sbrsh-7.6.1/0000755000232200023220000000000011155150156013034 5ustar pbuilderpbuildersbrsh-7.6.1/.gitignore0000644000232200023220000000014011013052103015001 0ustar pbuilderpbuilderoptimize debug autoconf.mk autoconf TAGS tags autom4te.cache config.log config.status configure sbrsh-7.6.1/COPYING0000644000232200023220000004311011013052103014050 0ustar pbuilderpbuilder GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. sbrsh-7.6.1/Makefile0000644000232200023220000000232511013052103014460 0ustar pbuilderpbuilderO := $(if $(wildcard autoconf.mk),autoconf,optimize) CFLAGS += -Wall -Wmissing-prototypes -Wformat=2 -Wshadow BINARIES := sbrsh sbrshd LIBRARIES := common NODIST := $(LIBRARIES) build: $(BINARIES) $(BINARIES): common-static include build/project.mk GIT := git AUTOCONF := autoconf .PHONY: source-tarball source-tarball: @(VERSION=`$(GIT) describe --tags | sed s/^v//` && \ TMP=$$(mktemp -d tmp.XXXXXX) && \ $(GIT) archive --format=tar --prefix="sbrsh-$$VERSION/" HEAD \ > "$$TMP/sbrsh-$$VERSION.tar" && \ cd "$$TMP" && \ tar xf "sbrsh-$$VERSION.tar" && \ cd "sbrsh-$$VERSION" && \ $(AUTOCONF) && \ cd .. && \ tar rf "sbrsh-$$VERSION.tar" "sbrsh-$$VERSION/configure" && \ gzip -c "sbrsh-$$VERSION.tar" > "../sbrsh-$$VERSION.tar.gz" && \ cd .. && \ rm -r "$$TMP") .PHONY: ipk ipk: daemon-$(PROFILE) rm -rf ipkg/build install -d ipkg/build/CONTROL sed s/''/`head -n1 debian/changelog | cut -d'(' -f2 | cut -d')' -f1`/ ipkg/control.in > ipkg/build/CONTROL/control install -m 755 ipkg/postinst ipkg/build/CONTROL/ install -m 755 ipkg/postrm ipkg/build/CONTROL/ install -d ipkg/build/usr/sbin install -m 755 $(daemon_BINARY_$(PROFILE)) ipkg/build/usr/sbin/ ipkg-build ipkg/build rm -rf ipkg/build sbrsh-7.6.1/README0000644000232200023220000001202211013052103013673 0ustar pbuilderpbuilder General Information =================== Scratchbox Remote Shell is an rsh/ssh-like utility for Linux that supports terminal emulation, automated mounting of network shares, chroot, etc. Please note that sbrsh is extremely insecure--do not use it on an untrusted network or on multi-user systems! sbrsh is distributed under the General Public License version 2. See COPYING for the full license. Client Usage ============ Execute a remote command: sbrsh [-t|--target ] [-c|--config ] [-d|--directory ] [-r|--remote ,,] [ []] Mount or unmount all filesystems of a target: sbrsh [-t|--target ] [-c|--config ] --mount|--umount --target symbolic name of the target configuration; the first target in the config file will be used by default --config specifies the absolute path to the user's configuration file (default is "$HOME/.sbrsh") --directory the user's current directory at the target device (default is "/") --remote specify the identity of the user on the target side (usually pair this with, e.g., sshfs 'idmap=user,uid=, gid=' mountopts); this allows you to avoid making matching accounts on both the host and target command name of the binary to be executed (may be looked up from PATH); if command is not specified, the SHELL environment variable is used; if SHELL is not set, "/bin/sh" is used arguments zero or more arguments for the command Daemon Usage ============ sbrshd [-p|--port ] [-l|--local-only] [-n|--no-sandbox] [-r|--allow-root] [-d|--debug ] [-V|--debug-verbose] [-e|--mount-expiration |none] [-m|--mount-bin ] [-u|--umount-bin ] [-t|--mount-tab ] [-b|--bind-opt ] --port sets a custom port number (default is 1202) --local-only makes sbrshd only accept connections from the local host --no-sandbox does not create a sandbox directory, create mounts or chroot --allow-root allow commands to be run with user and group id 0 --debug enables debugging to a log file --debug-verbose enables debugging of stdin/stdout/select polling --mount-expiration specifies the number of minutes to wait before expiring unused mount points (default is 15); 0 means that filesystems are unmounted immediately after commands exit; "none" means that filesystems are unmounted only when sbrshd exits --mount-bin specifies the mount binary path (default is "/bin/mount") --umount-bin specifies the umount binary path (default is "/bin/umount") --mount-tab specifies the mount table path (default is "/etc/mtab") --bind-opt specifies the option used when binding a path to a mount point (default is "--bind", or "-obind" if mount binary is Busybox) The pid of the daemon process is printed to stdout. The command exits immediately. The debug log can be opened and closed at run-time by sending the daemon process the SIGUSR1 (open log) and SIGUSR2 (close log) signals. If the debug log was not opened at startup with the --debug option, the debug log will be written to "/tmp/sbrshd-.log" (where is the value of the --port option). Client Config ============= The client configuration file lists all known TARGETs (see Client Usage). The first line of a TARGET block must not contain whitespaces before the name of the TARGET. The subsequent lines must be indented. "#" is a line end comment character. The layout of the first line: [:] The subsequent lines define the mounts needed by the TARGET (TYPE is either "nfs" or "bind"): [] See "sbrsh.conf" for examples. Daemon Config ============= The daemon configuration file is /etc/sbrshd.conf. It lists all client IP addresses that are granted access. The "*" wildcard can be used at the end of an IP address. "#" is a line end comment character. Here's an example configuration: 172.16.6.10 172.16.6.* 192.168.* Environment Variables ===================== The command execution environment at the target device can be controlled via a few environment variables. The resource limits can be set using variables with the "SBOX_RLIMIT_" prefix. The value can be either an integer or "unlimited". The supported setting are: SBOX_RLIMIT_CPU CPU time in seconds SBOX_RLIMIT_FSIZE max filesize SBOX_RLIMIT_DATA max data size SBOX_RLIMIT_STACK max stack size SBOX_RLIMIT_CORE max core file size SBOX_RLIMIT_RSS max resident set size SBOX_RLIMIT_NPROC max number of processes SBOX_RLIMIT_NOFILE max number of open files SBOX_RLIMIT_MEMLOCK max locked-in-memory address space SBOX_RLIMIT_AS address space (virtual memory) limit sbrsh-7.6.1/autoconf.mk.in0000644000232200023220000000033111013052103015567 0ustar pbuilderpbuilderCC := @CC@ CPPFLAGS += @CPPFLAGS@ CFLAGS += @CFLAGS@ LDFLAGS += @LDFLAGS@ prefix := @prefix@ exec_prefix := @exec_prefix@ PREFIX := @prefix@ BINDIR := @bindir@ SBINDIR := @sbindir@ SYSCONFDIR := @sysconfdir@ sbrsh-7.6.1/buffer.c0000644000232200023220000000560411013052103014440 0ustar pbuilderpbuilder/* * Copyright (c) 2003, 2004 Nokia * Author: tsavola@movial.fi * * This program is licensed under GPL (see COPYING for details) */ #include "types.h" #include "buffer.h" #include "protocol.h" #include "common.h" #include #include #include #include #include #include /** * Allocates and initializes the buffer_t structure and allocates the buffer. */ buffer_t *buf_alloc() { buffer_t *buf = calloc(1, sizeof (buffer_t)); if (buf) { buf->mem_size = BUFFER_SIZE; buf->mem = malloc(BUFFER_SIZE); if (!buf->mem) { free(buf); buf = 0; } } return buf; } /** * Frees memory allocated by buffer and the buffer. * @param buf the buffer */ void buf_free(buffer_t *buf) { if (buf->mem) free(buf->mem); free(buf); } /** * Sets the EOF flag. When the buffer is written completely, this causes the * @param buf the buffer * target file to be closed. */ void buf_set_eof(buffer_t *buf) { buf->eof = TRUE; } /** * Calculates the amount of data currently in the buffer. * @param buf the buffer * @return the amount of data in the buffer */ size_t buf_size(buffer_t *buf) { return buf->end - buf->start; } /** * Checks if the buffer is empty and doesn't have the EOF flag set. * @param buf the buffer * @returns TRUE or FALSE */ bool_t buf_is_empty(buffer_t *buf) { return buf->end == 0 && buf->eof == FALSE; } /** * Appends data to a buffer from a file. If buffer is full, a larger block of * memory is allocated for it. * @param buf the buffer * @param fd the file descriptor * @param len the amount of data to be copied * @return the number of bytes actually read, -1 on error */ ssize_t buf_read_in(buffer_t *buf, int fd, size_t len) { size_t size; assert (!buf->eof); size = buf->end + len; if (size > buf->mem_size) { /* TODO: use relloac() */ uint8_t *mem; mem = malloc(size); if (!mem) { return -1; } memcpy(mem, buf->mem, buf->mem_size); free(buf->mem); buf->mem_size = size; buf->mem = mem; } if (read_buf(fd, buf->mem + buf->end, len) < 0) { return -1; } buf->end += len; return len; } /** * Writes as much data from a buffer to a file as possible. * @param buf the buffer * @param fd the file descriptor * @return the amount of data actually written or -1 if fd doesn't want data */ ssize_t buf_write_out(buffer_t *buf, int *fd) { size_t size; ssize_t len = 0; size = buf_size(buf); if (size) { assert(buf->start < buf->mem_size); len = write_ni(*fd, &buf->mem[buf->start], size); if (len < 0) { if (errno == EAGAIN) { len = 0; } else { close(*fd); *fd = -1; return -1; } } buf->start += len; size = buf_size(buf); if (size == 0) { buf->start = 0; buf->end = 0; } } if (size == 0 && buf->eof) { close(*fd); *fd = -1; errno = 0; /* Make buffer empty. */ buf->eof = FALSE; } return len; } sbrsh-7.6.1/buffer.h0000644000232200023220000000121011013052103014432 0ustar pbuilderpbuilder/* * Copyright (c) 2003, 2004 Nokia * Author: tsavola@movial.fi * * This program is licensed under GPL (see COPYING for details) */ #ifndef BUFFER_H #define BUFFER_H #include "types.h" /** Default capacity for buffer_t. */ #define BUFFER_SIZE 4096 /** Dynamic buffer for I/O data. */ typedef struct { size_t start; size_t end; bool_t eof; size_t mem_size; uint8_t *mem; } buffer_t; buffer_t *buf_alloc(void); void buf_free(buffer_t *); void buf_set_eof(buffer_t *); size_t buf_size(buffer_t *); bool_t buf_is_empty(buffer_t *); ssize_t buf_read_in(buffer_t *, int fd, size_t len); ssize_t buf_write_out(buffer_t *, int *fd); #endif sbrsh-7.6.1/build/0000755000232200023220000000000011013052103014115 5ustar pbuilderpbuildersbrsh-7.6.1/build/README0000644000232200023220000000067411013052103015004 0ustar pbuilderpbuilderThis is a simple C/C++ build system based on GNU Make. Supports (at least) the GNU toolchain and ELF platforms. Meant to be used in place of Automake and Libtool. Can be used with Autoconf. URL: http://iki.fi/timo.savola/git/?p=maker.git Original version is copyright (c) 2008 Timo Savola. Distributable under the terms of the Boost Software License 1.0: http://boost.org/LICENSE_1_0.txt http://opensource.org/licenses/bsl1.0.html sbrsh-7.6.1/build/binary.mk0000644000232200023220000000047411013052103015737 0ustar pbuilderpbuilderinclude build/common.mk include build/target.mk O_NAME := $(O)/bin/$(NAME) build: $(O_NAME) $(O_NAME): $(OBJECTS) $(DEPENDS) $(call echo,Link,$@) $(QUIET) mkdir -p $(dir $@) $(QUIET) $(LINKCOMMAND) -o $@ $(OBJECTS) $(LIBS) install:: mkdir -p $(DEST_BINDIR) install $(O_NAME) $(DEST_BINDIR)/ .PHONY: build sbrsh-7.6.1/build/c.mk0000644000232200023220000000076611013052103014701 0ustar pbuilderpbuilderCCCOMMAND := $(CC) $(GENERICFLAGS) $(CCFLAGS) LINKCOMMAND := $(CCLINKER) $(CFLAGS) $(CCFLAGS) $(LDFLAGS) $(O)/obj/%.o: %.c $(call echo,Compile,$@) $(QUIET) mkdir -p $(dir $@) $(QUIET) $(CCCOMMAND) \ -MF $(O)/obj/$(patsubst %,%.d,$*.c) \ -MT $(O)/obj/$*.o -MT $(O)/obj/$*.os \ -o $@ $*.c $(O)/obj/%.os: %.c $(call echo,Compile,$@) $(QUIET) mkdir -p $(dir $@) $(QUIET) $(CCCOMMAND) $(PICFLAGS) \ -MF $(O)/obj/$(patsubst %,%.d,$*.c) \ -MT $(O)/obj/$*.o -MT $(O)/obj/$*.os \ -o $@ $*.c sbrsh-7.6.1/build/common.mk0000644000232200023220000000060611013052103015740 0ustar pbuilderpbuilderdefine echo @ printf " %-8s %s\n" "$(1)" "$(2)" endef QUIET := $(if $(V),,@) lastword = $(if $(1),$(word $(words $(1)),$(1)),) DEST_PREFIX = $(DESTDIR)$(PREFIX) DEST_BINDIR = $(DESTDIR)$(BINDIR) DEST_SBINDIR = $(DESTDIR)$(SBINDIR) DEST_LIBDIR = $(DESTDIR)$(LIBDIR) DEST_PLUGINDIR = $(DESTDIR)$(PLUGINDIR) DEST_DATADIR = $(DESTDIR)$(DATADIR) DEST_SYSCONFDIR = $(DESTDIR)$(SYSCONFDIR) sbrsh-7.6.1/build/library.mk0000644000232200023220000000175511013052103016122 0ustar pbuilderpbuilderinclude build/common.mk include build/target.mk LIBRARY := lib$(NAME).so.$(VERSION) LIBRARY_LINK := lib$(NAME).so ARCHIVE := lib$(NAME).a O_LIBRARY := $(O)/lib/$(LIBRARY) O_LIBRARY_LINK := $(O)/lib/$(LIBRARY_LINK) O_ARCHIVE := $(O)/lib/$(ARCHIVE) build: build-shared build-static build-shared: $(O_LIBRARY) build-static: $(O_ARCHIVE) $(O_LIBRARY): $(PIC_OBJECTS) $(DEPENDS) $(call echo,Link,$@) $(QUIET) mkdir -p $(dir $@) $(QUIET) $(LINKCOMMAND) -fPIC -shared -Wl,-soname,$(LIBRARY) \ -o $@ $(PIC_OBJECTS) $(LIBS) $(QUIET) ln -sf $(LIBRARY) $(O_LIBRARY_LINK) $(O_ARCHIVE): $(OBJECTS) $(call echo,Archive,$@) $(QUIET) mkdir -p $(dir $@) $(QUIET) $(AR) crs $@ $(OBJECTS) install:: ifneq ($(wildcard $(O_LIBRARY)),) mkdir -p $(DEST_LIBDIR) install $(O_LIBRARY) $(DEST_LIBDIR)/ ln -sf $(LIBRARY) $(DEST_LIBDIR)/$(LIBRARY_LINK) endif install:: ifneq ($(wildcard $(O_ARCHIVE)),) mkdir -p $(DEST_LIBDIR) install $(O_ARCHIVE) $(DEST_LIBDIR)/ endif .PHONY: build build-shared build-static sbrsh-7.6.1/build/project.mk0000644000232200023220000000450611013052103016121 0ustar pbuilderpbuilderO ?= debug # V # CONFIG # CROSS_COMPILE CCACHE ?= $(if $(shell which ccache),ccache,) ifeq ($(origin CC),default) CC := $(CCACHE) $(CROSS_COMPILE)gcc endif ifeq ($(origin CXX),default) CXX := $(CCACHE) $(CROSS_COMPILE)g++ endif ifeq ($(origin AR),default) AR := $(CCACHE) $(CROSS_COMPILE)ar endif CCLINKER ?= $(CC) CXXLINKER ?= $(CXX) PKG_CONFIG ?= pkg-config # PKG_CONFIG_PATH # CPPFLAGS CFLAGS ?= -g -Wall # CCFLAGS # CXXFLAGS # LDFLAGS # LIBS CXXPATTERNS ?= %.cc %.cp %.cxx %.cpp %.CPP %.c++ %.C PREFIX ?= /usr/local BINDIR ?= $(PREFIX)/bin SBINDIR ?= $(PREFIX)/sbin LIBDIR ?= $(PREFIX)/lib PLUGINDIR ?= $(LIBDIR) DATADIR ?= $(PREFIX)/share SYSCONFDIR ?= /etc # DESTDIR include build/common.mk ifneq ($(wildcard $(O)/config.mk),) include $(O)/config.mk else -include $(O).mk endif export O V export CONFIG export CC CXX CCLINKER CXXLINKER AR export PKG_CONFIG PKG_CONFIG_PATH export CPPFLAGS CFLAGS CCFLAGS CXXFLAGS LDFLAGS LIBS export CXXPATTERNS export PREFIX BINDIR SBINDIR LIBDIR PLUGINDIR DATADIR SYSCONFDIR export DESTDIR DIST ?= $(BINARIES) $(LIBRARIES) $(PLUGINS) DO_DIST := $(filter-out $(NODIST),$(DIST)) LIBRARY_TARGETS := $(foreach L,$(LIBRARIES),$(L)-shared $(L)-static) TARGETS := $(BINARIES) $(TESTS) $(LIBRARY_TARGETS) $(PLUGINS) CHECK_TARGETS := $(foreach T,$(TESTS),check-$(T)) INSTALL_TARGETS := $(foreach I,$(DO_DIST),install-$(I)) build: check: $(CHECK_TARGETS) all: build check install: $(INSTALL_TARGETS) clean: $(call echo,Remove,$(O)) $(QUIET) rm -rf "$(O)" makefile = $(firstword $(wildcard $(1).mk) $(1)/build.mk) librarymakefile = $(call makefile,$(patsubst %-static,%,$(patsubst %-shared,%,$(1)))) librarytarget = build-$(call lastword,$(subst -, ,$(1))) testmakefile = $(call makefile,$(patsubst check-%,%,$(1))) distmakefile = $(call makefile,$(patsubst install-%,%,$(1))) $(BINARIES) $(TESTS) $(PLUGINS): $(QUIET) $(MAKE) --no-print-directory -f $(call makefile,$@) build $(LIBRARY_TARGETS): $(QUIET) $(MAKE) --no-print-directory -f $(call librarymakefile,$@) $(call librarytarget,$@) $(CHECK_TARGETS): $(TESTS) $(QUIET) $(MAKE) --no-print-directory -f $(call testmakefile,$@) check $(INSTALL_TARGETS): $(DO_DIST) $(QUIET) $(MAKE) --no-print-directory -f $(call distmakefile,$@) install .PHONY: build install all check clean .PHONY: $(TARGETS) $(CHECK_TARGETS) $(INSTALL_TARGETS) $(LIBRARIES) sbrsh-7.6.1/build/target.mk0000644000232200023220000000063211013052103015735 0ustar pbuilderpbuilder-include build/pkgconfig.mk CONFIGFLAGS := $(if $(CONFIG),-include $(CONFIG),) GENERICFLAGS := $(CPPFLAGS) $(CONFIGFLAGS) $(CFLAGS) -c -MD PICFLAGS := -DPIC -fPIC -include build/c++.mk -include build/c.mk OBJECTS := $(patsubst %,$(O)/obj/%.o,$(basename $(SOURCES))) PIC_OBJECTS := $(patsubst %,$(O)/obj/%.os,$(basename $(SOURCES))) -include $(SOURCES:%=$(O)/obj/%.d) $(OBJECTS) $(PIC_OBJECTS): $(CONFIG) sbrsh-7.6.1/client.c0000644000232200023220000005364611155150155014473 0ustar pbuilderpbuilder/* * Copyright (c) 2003, 2004, 2005 Nokia * Author: Timo Savola * * This program is licensed under GPL (see COPYING for details) */ #include "client.h" #include "common.h" #include "protocol.h" #include "buffer.h" #include "config.h" #include "mount.h" #include "version.h" #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define REQUIRED_VERSION 6 extern char **environ; /** The name of this program. */ static char *progname; /** Path of the config file (command line option). */ static char *configpath = NULL; /** The command line arguments. */ static char *target = NULL; static char *remote_user = NULL; static char *cwd = NULL; static char **args = NULL; static int action = PTYPE_COMMAND; /** The socket fd. */ static int sd = -1; /** Buffers used to copy data around. */ static buffer_t *buf_out; static buffer_t *buf_err; static char *tmp_buf; /** Is the tty in raw mode? */ static bool_t rawmode = FALSE; /** The original mode of the tty. */ static struct termios oldtio; /** How much IN DATA has the server requested? */ static size_t inreq = 0; /** Have we requested some data? */ static bool_t outwait = FALSE; static bool_t errwait = FALSE; /** The protocol version of the daemon. */ static int daemon_version = 0; #ifdef DEBUG static void debug(const char *msg, ...) { va_list arg; fprintf(stderr, "%s (%d) debug: ", progname, getpid()); va_start(arg, msg); vfprintf(stderr, msg, arg); va_end(arg); fprintf(stderr, "\n"); fflush(stderr); } #else # define debug(msg, ...) #endif /* * Prints progname, message and errno description to stderr. */ void error(const char *msg, ...) { char *desc = NULL; va_list arg; if (errno > 0) desc = strerror(errno); #ifdef DEBUG fprintf(stderr, "%s (%d): ", progname, getpid()); #else fprintf(stderr, "%s: ", progname); #endif va_start(arg, msg); vfprintf(stderr, msg, arg); va_end(arg); if (desc) fprintf(stderr, " (%s)", desc); fprintf(stderr, "\n"); fflush(stderr); } /** * Writes (some of) buffer to a file. * @param buf the buffer * @param fd the target file descriptor * @param wait is set to false when the buffer is empty * @return 0 on success, -1 on error */ static int write_buffer(buffer_t *buf, int fd, bool_t *wait) { /* buf is never at EOF so fd never becomes -1. */ if (buf_write_out(buf, &fd) < 0) { error(fd == STDOUT_FILENO ? "Can't write buffer to stdout" : "Can't write buffer to stderr"); return -1; } if (buf_is_empty(buf)) *wait = FALSE; return 0; } /** * Sends an IN DATA packet. * @return 0 usually, 1 if stdin reached EOF, -1 on error */ static int send_data(void) { bool_t ok = FALSE; ssize_t len = BUFFER_SIZE; /* * Get the amount of data we can read from a terminal. */ if (isatty(STDIN_FILENO)) { if (ioctl(STDIN_FILENO, FIONREAD, &len) < 0) { error("Can't check tty for available data"); return -1; } if (len < 0) { error("ioctl(tty) gave invalid read length"); return -1; } if (len == 0) return 0; if (len > BUFFER_SIZE) len = BUFFER_SIZE; } len = read(STDIN_FILENO, tmp_buf, len); if (len < 0) { error("Can't read from stdin"); return -1; } if (len == 0) { debug("Stdin hit EOF"); ok = 1; inreq = 0; } if (write_buf_packet(sd, PTYPE_IN_DATA, len, tmp_buf) < 0) { error("Can't write IN DATA packet to socket"); return -1; } /* At EOF: 0-0=0 */ inreq -= len; return ok; } /** * Reads bytes from socket to a buffer. * @param buf the target buffer * @return 0 on success, -1 on error */ static int receive_stream(buffer_t *buf) { uint32_t len; if (read_uint32(sd, &len) < 0) { error("Unable to read data packet length"); return -1; } if (len == 0) { errno = 0; error("Received empty data packet (EOF)"); return -1; } if (buf_read_in(buf, sd, len) < 0) { error("Can't append data packet to buffer"); return -1; } return 0; } /** * Reads message from socket and prints it. * @param type "error" or "warning" * @return 0 on success, -1 on error */ static int receive_message(ptype_t type) { uint32_t len; if (read_uint32(sd, &len) < 0) { error("Unable to read message packet length"); return -1; } if (read_buf(sd, tmp_buf, len) < 0) { error("Can't read message packet"); return -1; } if (len == BUFFER_SIZE) len = BUFFER_SIZE - 1; tmp_buf[len] = '\0'; #ifdef DEBUG if (type == PTYPE_ERROR) fprintf(stderr, "%s (%d) server: %s\n", progname, getpid(), tmp_buf); else fprintf(stderr, "%s (%d): %s\n", progname, getpid(), tmp_buf); #else if (type == PTYPE_ERROR) fprintf(stderr, "%s server: %s\n", progname, tmp_buf); else fprintf(stderr, "%s: %s\n", progname, tmp_buf); #endif return 0; } /** * Reads a packet from the socket (sd) and does something about it. * The value of an RC packet is stored in the global rc variable. * @return 1 on RC, 0 on other valid packet type, -1 on error */ static int receive_packet(uint16_t *rc) { ptype_t type = read_enum(sd); switch (type) { case -1: error("Can't read packet type from socket"); return -1; case PTYPE_IN_REQ: inreq = BUFFER_SIZE; return 0; case PTYPE_OUT_DATA: return receive_stream(buf_out); case PTYPE_ERR_DATA: return receive_stream(buf_err); case PTYPE_RC: debug("Receiving RC packet"); if (read_uint16(sd, rc) < 0) { error("Can't read RC packet from socket"); return -1; } return 1; case PTYPE_MESSAGE: return receive_message(PTYPE_MESSAGE); case PTYPE_ERROR: return receive_message(PTYPE_ERROR); default: errno = 0; error("Received packet has unexpected type (0x%02x)", type); return -1; } } /** * Sends a request for more data. * @param ptype PTYPE_OUT_REQ or PTYPE_ERR_REQ * @param wait is set to true * @return 0 on success, -1 on error */ static int send_request(ptype_t ptype, bool_t *wait) { if (write_enum(sd, ptype) < 0) { error("Can't write packet to socket"); return -1; } *wait = TRUE; return 0; } /** * Manages stdin/stdout/stderr and waits for the return code. * @return -1 on error, return code on success */ static int manage(bool_t is_tty) { fd_set readfds, writefds; bool_t inopen; int ok; /* Check if stdin is really available */ if (is_tty || read(STDIN_FILENO,NULL,0)!=-1) { inopen = TRUE; } else { inopen = FALSE; } while (1) { FD_ZERO(&readfds); FD_ZERO(&writefds); FD_SET(sd, &readfds); if (inopen && inreq) FD_SET(STDIN_FILENO, &readfds); if (!buf_is_empty(buf_out) || !outwait) FD_SET(STDOUT_FILENO, &writefds); if (!is_tty && (!buf_is_empty(buf_err) || !errwait)) FD_SET(STDERR_FILENO, &writefds); if (select(sd + 1, &readfds, &writefds, NULL, NULL) <= 0) { error("Can't select"); return -1; } /* read packet from socket */ if (FD_ISSET(sd, &readfds)) { uint16_t rc; ok = receive_packet(&rc); if (ok < 0) return -1; if (ok > 0) return rc; } /* send data from stdin if requested */ if (inopen && inreq && FD_ISSET(STDIN_FILENO, &readfds)) { ok = send_data(); if (ok < 0) return -1; if (ok > 0) inopen = FALSE; } /* flush buf_out to stdout or request more data */ if (FD_ISSET(STDOUT_FILENO, &writefds)) { if (buf_is_empty(buf_out)) ok = outwait || send_request(PTYPE_OUT_REQ, &outwait) >= 0; else ok = write_buffer(buf_out, STDOUT_FILENO, &outwait) >= 0; if (!ok) return -1; } /* flush buf_err to stderr or request more data */ if (FD_ISSET(STDERR_FILENO, &writefds)) { if (buf_is_empty(buf_err)) ok = errwait || send_request(PTYPE_ERR_REQ, &errwait) >= 0; else ok = write_buffer(buf_err, STDERR_FILENO, &errwait) >= 0; if (!ok) return -1; } } /* Not reached. */ return -1; } /** * Sets stdin to raw mode. */ static int set_raw_mode(void) { struct termios tio; if (tcgetattr(STDIN_FILENO, &tio) < 0) { error("Can't get termios"); return -1; } oldtio = tio; tio.c_iflag |= IGNPAR; tio.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY); tio.c_lflag &= ~(IXOFF | ISIG | ICANON | ECHO | ECHOE | ECHOK); tio.c_lflag &= ~(ECHONL | IEXTEN | OPOST); tio.c_cc[VMIN] = 1; tio.c_cc[VTIME] = 0; debug("Entering raw mode"); if (tcsetattr(STDIN_FILENO, TCSADRAIN, &tio) < 0) { error("Can't change termios"); return -1; } rawmode = TRUE; return 0; } /** * Sets stdin in its original mode. */ static void set_old_mode(void) { if (!rawmode) { debug("Not in raw mode"); return; } debug("Leaving raw mode"); if (tcsetattr(STDIN_FILENO, TCSADRAIN, &oldtio) < 0) error("Can't restore original termios"); } static void usage(void) { fprintf(stderr, "Usage: %s [-t|--target ]" " [-c|--config ]" " [-d|--directory ]" " [-r|--remote ,,]" " []" " []\n" " %s [-t|--target ]" " [-c|--config ]" " --mount\n" " %s [-t|--target ]" " [-c|--config ]" " --umount\n" " %s -v|--version\n" " %s -h|--help\n", progname, progname, progname, progname, progname); } static bool_t should_skip_parameter(char *arg, struct option *longopts) { bool_t is_long; struct option *opt; is_long = strlen(arg) > 2; /* check if arg also has its value */ if (is_long && strchr(arg, '=') != NULL) return FALSE; for (opt = longopts; opt->name; ++opt) { if (is_long) { if (strcmp(&arg[2], opt->name) != 0) continue; } else { if (arg[1] != opt->val) continue; } return opt->has_arg == required_argument; } return FALSE; } static char **modify_args(int old_argc, char **old_argv, int *new_argc, struct option *longopts) { char **new_argv; int i = 0; new_argv = calloc(old_argc + 2, sizeof (char *)); if (!new_argv) { oom_error(); exit(1); } /* sbrsh's progname */ new_argv[i++] = *old_argv++; if (old_argc > 1) { /* sbrsh's options and -- */ while (i < old_argc) { char *arg = *old_argv++; if (arg[0] != '-') { new_argv[i++] = "--"; new_argv[i++] = arg; break; } new_argv[i++] = arg; if (should_skip_parameter(arg, longopts) && i < old_argc) new_argv[i++] = *old_argv++; } /* command and its arguments */ while (*old_argv) new_argv[i++] = *old_argv++; } *new_argc = i; return new_argv; } /** * Fills in the options from argv. Prints usage and exits on error. * @return TRUE if we should --umount-all and not run a command */ static void read_args(int orig_argc, char **orig_argv) { char **argv; int argc; int skip_args=0; struct option longopts[] = { { "help", no_argument, 0, 'h' }, { "version", no_argument, 0, 'v' }, { "sbox-call", no_argument, &skip_args, 1 }, { "target", required_argument, 0, 't' }, { "config", required_argument, 0, 'c' }, { "directory", required_argument, 0, 'd' }, { "remote", required_argument, 0, 'r' }, { "mount", no_argument, &action, PTYPE_MOUNT }, { "umount", no_argument, &action, PTYPE_UMOUNT }, { 0 } }; argv = modify_args(orig_argc, orig_argv, &argc, longopts); while (1) { int c; c = getopt_long(argc, argv, "hvt:c:d:r:", longopts, NULL); if (c < 0) { break; } switch (c) { case 't': target = optarg; break; case 'c': configpath = optarg; break; case 'd': cwd = optarg; break; case 'r': remote_user = optarg; break; case 0: /* --mount or --umount */ break; case 'v': fprintf(stderr, "Scratchbox Remote Shell client %d%s\n", PROTOCOL_VERSION, REVISION); exit(0); case 'h': usage(); exit(0); default: usage(); exit(1); } } if (argv[optind] && argv[optind+skip_args]) args = &argv[optind+skip_args]; } /** * Called when the process exits. */ static void cleanup(void) { /* see that stdout and stderr buffers are flushed */ if (!buf_is_empty(buf_out)) { set_nonblocking(STDOUT_FILENO, FALSE); write_buffer(buf_out, STDOUT_FILENO, &outwait); } if (!buf_is_empty(buf_err)) { set_nonblocking(STDERR_FILENO, FALSE); write_buffer(buf_err, STDERR_FILENO, &errwait); } set_old_mode(); debug("sbrsh exiting"); } static int determine_user_info(uid_t *uidp, gid_t *gidp, char *username) { uid_t uid; gid_t gid; if (remote_user != NULL) { /* * Parse the list for uid, gid, and username (string placed at * end for simplicity) */ if (sscanf(remote_user, "%d,%d,%s", &uid, &gid, username) != 3) { error("Invalid format for -r argument: %s", remote_user); return 1; } } else { char *str; uid = geteuid(); gid = getegid(); struct passwd *userstruct = getpwuid(uid); if (!userstruct) { error("Can't get user information about uid %d", uid); return 1; } strcpy(username, userstruct->pw_name); /* * Check for environment overrides */ str = getenv("_SBOX_NONFAKE_USER"); if (str) { strcpy(username, str); } str = getenv("_SBOX_NONFAKE_UID"); if (str && (uid = atoi(str)) <= 0) { error("Invalid _SBOX_NONFAKE_UID: %d", uid); return 1; } str = getenv("_SBOX_NONFAKE_GID"); if (str && (gid = atoi(str)) <= 0) { error("Invalid _SBOX_NONFAKE_GID: %d", gid); return 1; } } *uidp = uid; *gidp = gid; return 0; } /* * Reads options, resolves and connects to host, talks with it, returns rc. */ int main(int argc, char **argv) { config_t *cfg; mount_info_t **mounts; int16_t rc; uid_t uid = 0; gid_t gid = 0; char user[NAME_MAX]; progname = get_progname(argv[0]); debug("sbrsh version %d%s", PROTOCOL_VERSION, REVISION); /* * Allocate buffers */ if (!(cfg = config_alloc()) || !(buf_out = buf_alloc()) || !(buf_err = buf_alloc()) || !(tmp_buf = malloc(BUFFER_SIZE))) { oom_error(); return 1; } /* * Clean exit */ { struct sigaction act; act.sa_handler = exit; sigemptyset(&act.sa_mask); act.sa_flags = SA_ONESHOT; sigaction(SIGINT, &act, NULL); sigaction(SIGHUP, &act, NULL); sigaction(SIGTERM, &act, NULL); atexit(cleanup); } /* * Parse arguments */ read_args(argc, argv); /* * Read configuration file */ { char *home, *path = NULL; if (!configpath) { home = getenv("HOME"); path = malloc(strlen(home) + strlen("/" CONFIG_NAME) + 1); if (!path) { oom_error(); return 1; } strcpy(path, home); strcat(path, "/" CONFIG_NAME); configpath = path; } if (!config_read(cfg, configpath, target)) { if (target) error("Target %s not found in %s", target, configpath); else error("No targets found in %s", configpath); return 1; } if (path) { free(path); configpath = NULL; } } /* * Parse and do stuff with mount entries */ { size_t len, i; len = calc_vec_len((void **) cfg->opts); mounts = calloc(len + 1, sizeof (mount_info_t *)); if (!mounts) { oom_error(); return 1; } for (i = 0; i < len; ++i) { mount_info_t *mi = mntinfo_parse(cfg->opts[i]); if (!mi) return 1; debug("type=%d device=%s point=%s opts=%s", mi->type, mi->device, mi->point, mi->opts); if (action == PTYPE_COMMAND && (MTYPE_NFS == mi->type || MTYPE_SSH == mi->type)) { if (mntinfo_stat_device(mi) < 0) return 1; debug("device_dev=%lld", mi->device_dev); } mounts[i] = mi; } } /* * Connect to server */ { struct addrinfo *ai, *i, hints = { 0 }; char *port = DEFAULT_PORT; int ret; hints.ai_flags = AI_ADDRCONFIG; hints.ai_socktype = SOCK_STREAM; if (cfg->port) port = cfg->port; ret = getaddrinfo(cfg->host, port, &hints, &ai); if (ret < 0) { error("Can't resolve host: %s", gai_strerror(ret)); return 1; } for (i = ai; i; i = i->ai_next) { sd = socket(i->ai_family, i->ai_socktype, i->ai_protocol); if (sd < 0) continue; if (connect(sd, i->ai_addr, i->ai_addrlen) == 0) break; close(sd); sd = -1; } if (sd < 0) { error("Can't connect"); return 1; } freeaddrinfo(ai); } /* * Version */ if (send_version(sd) < 0) { error("Can't write protocol version packet to socket"); return 1; } daemon_version = get_version(sd); if (daemon_version < 0) { error("Can't read protocol version packet from socket"); return 1; } if (daemon_version < REQUIRED_VERSION) { errno = 0; error("Server version %d is too old (version %d required)", daemon_version, REQUIRED_VERSION); return 1; } /* * Find out remote user ID, group ID, and username */ if (determine_user_info(&uid, &gid, user)) { return 1; } /* * Send user name */ { debug("Sending USER packet: %s", user); if (write_str_packet(sd, PTYPE_USER, user) < 0) { error("Can't send USER packet"); return 1; } } /* * Read authentication reply or error messages */ while (1) { uint16_t val; ptype_t type = read_enum(sd); switch (type) { uint16_t auth; case -1: error("Can't read packet type from socket"); return 1; case PTYPE_AUTH: debug("Receiving AUTH packet"); if (read_uint16(sd, &auth) < 0) { error("Can't read AUTH packet from socket"); return 1; } if (auth) { debug("Authentication ok"); break; } else { errno = 0; error("Authentication failed"); return 1; } case PTYPE_RC: debug("Receiving RC packet"); if (read_uint16(sd, &val) < 0) { error("Can't read RC packet from socket"); return 1; } rc = val; break; case PTYPE_MESSAGE: receive_message(PTYPE_MESSAGE); continue; case PTYPE_ERROR: receive_message(PTYPE_ERROR); continue; default: errno = 0; error("Received packet has unexpected type (0x%02x)", type); return 1; } break; } /* * Send target name */ debug("Sending TARGET packet: %s", cfg->target); if (write_str_packet(sd, PTYPE_TARGET, cfg->target) < 0) { error("Can't send TARGET packet"); return 1; } config_free(cfg); /* * Send mounts */ debug("Sending MOUNTS packet"); if (write_enum(sd, PTYPE_MOUNTS) < 0 || write_mountv(sd, mounts) < 0) { error("Can't send MOUNTS packet"); return 1; } /* * Action paths */ if (action == PTYPE_COMMAND) { bool_t is_tty; /* * Send arguments */ if (args) { debug("Sending ARGS packet"); if (write_enum(sd, PTYPE_ARGS) < 0 || write_strv(sd, args) < 0) { error("Can't send ARGS packet"); return 1; } } /* * Send current working directory */ if (cwd) { debug("Sending CWD packet"); if (write_str_packet(sd, PTYPE_CWD, cwd) < 0) { error("Can't send CWD packet"); return 1; } } /* * Send environment */ { debug("Sending ENVIRON packet"); if (write_enum(sd, PTYPE_ENVIRON) < 0 || write_strv(sd, environ) < 0) { error("Can't send ENVIRON packet"); return 1; } } /* * Send effective user and group ID. If the user was not * faked, then send supplementary group IDs; otherwise * discover these on the remote side. */ { gid_t gids[NGROUPS_MAX]; int num, i; uint16v_t *ids; if (!remote_user) { num = getgroups(NGROUPS_MAX, gids); if (num < 0) { error("Can't get supplementary group IDs"); return 1; } } else { num = 0; } ids = uint16v_alloc(2 + num); if (!ids) { oom_error(); return 1; } ids->vec[0] = uid; ids->vec[1] = gid; for (i = 0; i < num; ++i) ids->vec[i + 2] = gids[i]; debug("Sending IDS packet"); if (write_enum(sd, PTYPE_IDS) < 0 || write_uint16v(sd, ids) < 0) { error("Can't send IDS packet"); return 1; } uint16v_free(ids); } /* * Send umask */ { uint16_t mask = umask(0); umask(mask); debug("Sending UMASK packet"); if (write_uint16_packet(sd, PTYPE_UMASK, mask) < 0) { error("Can't send UMASK packet"); return 1; } } /* * Send terminal size */ is_tty = isatty(STDIN_FILENO) && isatty(STDOUT_FILENO) && isatty(STDERR_FILENO); debug(is_tty ? "Terminal emulation enabled" : "Terminal emulation disabled"); if (is_tty) { struct winsize ws; ioctl(STDIN_FILENO, TIOCGWINSZ, &ws); debug("Sending WINSIZE packet"); if (write_enum(sd, PTYPE_WINSIZE) < 0 || write_winsize(sd, &ws) < 0) { error("Can't send WINSIZE packet"); return 1; } } /* * Non-blocking I/O */ if (set_nonblocking(STDOUT_FILENO, TRUE) < 0) { error("Can't make stdout non-blocking"); return 1; } if (is_tty) { if (set_nonblocking(STDERR_FILENO, TRUE) < 0) { error("Can't make stderr non-blocking"); return 1; } if (set_raw_mode() < 0) return 1; } /* * Send command action */ debug("Sending COMMAND packet"); if (write_enum(sd, PTYPE_COMMAND) < 0) { error("Can't send COMMAND packet"); return 1; } /* * Main loop */ rc = manage(is_tty); if (rc < 0) return 1; } else { /* * Send (un)mount action */ if (action == PTYPE_MOUNT) { debug("Sending MOUNT packet"); if (write_enum(sd, PTYPE_MOUNT) < 0) { error("Can't send MOUNT packet"); return 1; } } else { debug("Sending UMOUNT packet"); if (write_enum(sd, PTYPE_UMOUNT) < 0) { error("Can't send UMOUNT packet"); return 1; } } /* * Wait until the server has (un)mounted */ while (1) { uint16_t val; ptype_t type = read_enum(sd); switch (type) { case -1: error("Can't read packet type from socket"); return 1; case PTYPE_RC: debug("Receiving RC packet"); if (read_uint16(sd, &val) < 0) { error("Can't read RC packet from socket"); return 1; } rc = val; break; case PTYPE_MESSAGE: receive_message(PTYPE_MESSAGE); continue; case PTYPE_ERROR: receive_message(PTYPE_ERROR); continue; default: errno = 0; error("Received packet has unexpected type (0x%02x)", type); return 1; } break; } } /* * Exit with correct code */ if (rc == INTERNAL_ERROR_CODE) { debug("Internal server error"); return 1; } else { debug("Return code: %d", rc); return rc; } } sbrsh-7.6.1/client.h0000644000232200023220000000051111013052103014442 0ustar pbuilderpbuilder/* * Copyright (c) 2003, 2004 Nokia * Author: tsavola@movial.fi * * This program is licensed under GPL (see COPYING for details) */ #ifndef CLIENT_H #define CLIENT_H #include /** Name of the config file in the user's home directory. */ #define CONFIG_NAME ".sbrsh" void error(const char *msg, ...); #endif sbrsh-7.6.1/common.c0000644000232200023220000001111611013052103014452 0ustar pbuilderpbuilder/* * Copyright (c) 2003, 2004 Nokia * Author: tsavola@movial.fi * * This program is licensed under GPL (see COPYING for details) */ #include "types.h" #include "common.h" #include #include #include #include #include #include #include #include #include #include const char *oom = "Out of memory"; void oom_error(void) { errno = 0; error(oom); } /** * Sets a file descriptor (non-)blocking. * @param fd the behaviour of this file descriptor will be changed * @param nonblock if TRUE, fd is set nonblocking; if FALSE, fd is set blocking */ int set_nonblocking(int fd, bool_t nonblock) { int val; val = fcntl(fd, F_GETFL, 0); if (val < 0) return -1; if (nonblock) { if (val & O_NONBLOCK) return 0; val |= O_NONBLOCK; } else { if (!(val & O_NONBLOCK)) return 0; val &= ~O_NONBLOCK; } return fcntl(fd, F_SETFL, val); } /** * Sets a file descriptor to close-on-exec. */ int set_closeonexec(int fd) { int val; val = fcntl(fd, F_GETFD, 0); if (val < 0) return -1; if (val & FD_CLOEXEC) return 0; return fcntl(fd, F_SETFD, val | FD_CLOEXEC); } int setsockopt_bool(int s, int level, int sockopt, bool_t value) { return setsockopt(s, level, sockopt, &value, sizeof (value)); } /** * Gets a neat progname. */ char *get_progname(char *name) { char *p; p = strrchr(name, '/'); if (p && strlen(p) >= 2) return &p[1]; else return name; } uint32_t resolve(const char *hostname) { struct hostent *host; uint32_t addr; host = gethostbyname(hostname); if (!host) goto _err; addr = *(uint32_t *) host->h_addr; if (!addr) goto _err; return ntohl(addr); _err: error("Can't resolve host: %s", hostname); return 0; } /** * Frees all components of a null-terminated vector with a given function and * the vector itself. If the function is NULL, free(3) will be used. If vec is * NULL, nothing happens. * @param vec tor * @param func tion */ void free_vec(void **vec, free_func_t *func) { void **p; if (!vec) return; if (!func) func = (free_func_t *) free; for (p = vec; *p; ++p) func(*p); free(vec); } size_t calc_vec_len(void **vec) { size_t len = 0; if (vec) while (*vec++) ++len; return len; } static ssize_t do_read_line(FILE *file, char *buf, size_t size, char comment_char, bool_t *blankp) { bool_t blank = TRUE; bool_t comm = FALSE; size_t i = 0; int c; if (feof(file)) return -1; while (1) { c = fgetc(file); if (c == EOF || c == '\n') break; if (!isspace(c)) blank = FALSE; if (c == comment_char) comm = TRUE; if (!comm && i < size - 1) { *buf++ = c; i++; } } *buf = '\0'; if (blankp) *blankp = blank; return i; } ssize_t read_line(FILE *file, char *buf, size_t size) { return do_read_line(file, buf, size, '#', NULL); } ssize_t read_line_blank(FILE *file, char *buf, size_t size, bool_t *blankp) { return do_read_line(file, buf, size, '#', blankp); } /** * Reads a line, not stopping on comments. */ ssize_t read_line_nocomments(FILE *file, char *buf, size_t size) { return do_read_line(file, buf, size, '\0', NULL); } char *skip_spaces(const char *buf) { while (*buf && isspace(*buf)) ++buf; return (char *) buf; } char *find_space(const char *buf) { while (*buf && !isspace(*buf)) ++buf; return (char *) buf; } char *find_line(char *buf) { while (*buf && *buf++ != '\n') ; return buf; } char *trim_string(char *buf) { char *next; buf = skip_spaces(buf); for (next = buf; 1; ) { char *end = find_space(next); if (*end == '\0') break; next = skip_spaces(end); if (*next == '\0') { *end = '\0'; break; } } return buf; } int split_string(char *line, ...) { va_list arg; char **strp, *str; int n = 0; va_start(arg, line); while (1) { strp = va_arg(arg, char **); if (!strp) break; if (strlen(line) == 0) { *strp = NULL; continue; } str = line; line = find_space(line); if (*line) { *line++ = '\0'; line = skip_spaces(line); } if (strlen(str) > 0) { *strp = str; n++; } else { *strp = NULL; } } va_end(arg); return n; } size_t string_cat(char *buf, size_t bufsize, const char *piece, ...) { char *str; ssize_t avail; va_list arg; va_start(arg, piece); str = buf; *str = '\0'; while (piece) { size_t len; avail = (bufsize - 1) - strlen(str); if (avail <= 0) { break; } len = strlen(piece); if (len > avail) { strncat(str, piece, avail); break; } strcat(str, piece); piece = va_arg(arg, char *); } va_end(arg); return strlen(buf); } sbrsh-7.6.1/common.h0000644000232200023220000000303011013052103014453 0ustar pbuilderpbuilder/* * Copyright (c) 2003, 2004 Nokia * Author: tsavola@movial.fi * * This program is licensed under GPL (see COPYING for details) */ #ifndef COMMON_H #define COMMON_H #include "types.h" #include /** Default port. */ #define DEFAULT_PORT "1202" /** Maximum username length including the nul-terminator. */ #define USER_SIZE 16 /** Maximum password length including the nul-terminator. */ #define PWD_SIZE 16 /** Maximum hostname length including the nul-terminator. */ #define HOST_SIZE 256 #define MTYPE_NFS_S "nfs" #define MTYPE_BIND_S "bind" #define MTYPE_SSH_S "ssh" typedef void (free_func_t)(void *); int set_nonblocking(int fd, bool_t nonblock); int set_closeonexec(int fd); int setsockopt_bool(int s, int level, int optname, bool_t value); char *get_progname(char *name); uint32_t resolve(const char *); void free_vec(void **vec, free_func_t *); size_t calc_vec_len(void **vec); ssize_t read_line(FILE *file, char *buf, size_t size); ssize_t read_line_blank(FILE *file, char *buf, size_t size, bool_t *blankp); ssize_t read_line_nocomments(FILE *file, char *buf, size_t size); char *skip_spaces(const char *str); char *find_space(const char *str); char *find_line(char *str); char *trim_string(char *str); int split_string(char *line, ...); size_t string_cat(char *buf, size_t bufsize, const char *piece, ...); /** "Out of memory" */ extern const char *oom; /** errno = 0; error(oom); */ void oom_error(void); /* This is defined in client.c and daemon.c */ void error(const char *msg, ...); #endif sbrsh-7.6.1/common.mk0000644000232200023220000000013311013052103014634 0ustar pbuilderpbuilderNAME := common SOURCES := buffer.c common.c mount.c protocol.c include build/library.mk sbrsh-7.6.1/config.c0000644000232200023220000000735111013052103014435 0ustar pbuilderpbuilder/* * Copyright (c) 2003, 2004 Nokia * Author: Timo Savola * * This program is licensed under GPL (see COPYING for details) */ #define _GNU_SOURCE #include "types.h" #include "config.h" #include "common.h" #include "client.h" #include #include #include #include /** * Compares the beginning of BUF to TGT. Comparison ends at first whitespace. */ static char *is_target(char *tgt, char *buf) { int a, b; while (1) { a = *tgt++; b = *buf++; if (!a || !b || isspace(b)) return (!a && isspace(b)) ? buf : NULL; if (a != b) return NULL; } } /** * Parses BUF into CFG. */ static bool_t read_params(char *buf, config_t *cfg) { char *host, *port; host = skip_spaces(buf); if (!host) { errno = 0; error("Invalid parameter string"); return FALSE; } port = strchr(host, ':'); if (port) { *port++ = '\0'; buf = port; } else { port = NULL; buf = host; } buf = find_space(buf); if (!buf) { errno = 0; error("Invalid parameter string"); return FALSE; } *buf++ = '\0'; cfg->host = strdup(host); if (!cfg->host) { oom_error(); return FALSE; } if (port) { cfg->port = strdup(port); if (!cfg->port) { oom_error(); return FALSE; } } return TRUE; } static bool_t read_opts(FILE *file, char *buf, size_t size, config_t *cfg) { long pos; size_t cnt = 0; char **p; pos = ftell(file); while (1) { bool_t blank; ssize_t len; len = read_line_blank(file, buf, size, &blank); if (len < 0) break; if (blank) break; if (len > 0 && !isspace(buf[0])) break; buf = skip_spaces(buf); if (strlen(buf) == 0) continue; cnt++; } fseek(file, pos, SEEK_SET); cfg->opts = calloc(cnt + 1, sizeof (char *)); if (!cfg->opts) { oom_error(); return FALSE; } for (p = cfg->opts; cnt > 0; ) { if (read_line(file, buf, size) < 0) break; buf = skip_spaces(buf); if (strlen(buf) == 0) continue; *p++ = strdup(buf); cnt--; } return TRUE; } bool_t config_read(config_t *cfg, const char *filename, const char *target) { FILE *file; char buf[1024], *bufp; ssize_t len; bool_t ok = FALSE; file = fopen(filename, "r"); if (!file) { error("Can't open %s", filename); return FALSE; } while (1) { len = read_line(file, buf, sizeof (buf)); if (len < 0) { errno = 0; error("Target %s is not listed in %s", target, filename); break; } if (len == 0 || isspace(buf[0])) continue; if (target) { /* Compare target name */ bufp = is_target((char *) target, buf); if (!bufp) continue; cfg->target = strdup(target); } else { /* Take the first one (default) */ bufp = find_space(buf); cfg->target = strndup(buf, bufp - buf); } ok = (read_params(bufp, cfg) && read_opts(file, buf, sizeof (buf), cfg)); break; } fclose(file); return ok; } config_t *config_alloc(void) { return calloc(1, sizeof (config_t)); } void config_free(config_t *cfg) { if (cfg->target) free(cfg->target); if (cfg->host) free(cfg->host); if (cfg->port) free(cfg->port); if (cfg->opts) free_vec((void **) cfg->opts, NULL); free(cfg); } char **get_targets(const char *filename) { FILE *file; char buf[1024]; ssize_t len; size_t cnt; char **targets, **p; file = fopen(filename, "r"); if (!file) return NULL; for (cnt = 0; TRUE; ) { len = read_line(file, buf, sizeof (buf)); if (len < 0) break; if (len > 0 && !isspace(*buf)) ++cnt; } rewind(file); targets = calloc(cnt + 1, sizeof (char *)); if (!targets) { oom_error(); return NULL; } for (p = targets; TRUE; ) { len = read_line(file, buf, sizeof (buf)); if (len < 0) break; if (len > 0 && !isspace(*buf)) { *find_space(buf) = '\0'; *p++ = strdup(buf); } } return targets; } sbrsh-7.6.1/config.h0000644000232200023220000000103011013052103014426 0ustar pbuilderpbuilder/* * Copyright (c) 2003, 2004 Nokia * Author: Timo Savola * * This program is licensed under GPL (see COPYING for details) */ #ifndef CONFIG_H #define CONFIG_H #include "types.h" /** Holds the info parsed from the client's config file. */ typedef struct { char *target; char *host; char *port; char **opts; } config_t; config_t *config_alloc(void); void config_free(config_t *); bool_t config_read(config_t *cfg, const char *file, const char *target); char **get_targets(const char *filename); #endif sbrsh-7.6.1/configure.ac0000644000232200023220000000161411013052103015306 0ustar pbuilderpbuilderAC_PREREQ(2.61) AC_INIT(sbrsh, 7) AC_CONFIG_SRCDIR([protocol.h]) AC_PROG_CC AC_CHECK_LIB([util], [openpty]) AC_HEADER_DIRENT AC_HEADER_STDC AC_HEADER_SYS_WAIT AC_CHECK_HEADERS([fcntl.h inttypes.h limits.h netdb.h netinet/in.h stdint.h stdlib.h string.h sys/ioctl.h sys/param.h sys/socket.h sys/time.h syslog.h termios.h unistd.h utmp.h]) AC_C_CONST AC_TYPE_UID_T AC_TYPE_INT16_T AC_TYPE_PID_T AC_TYPE_SIZE_T AC_TYPE_SSIZE_T AC_HEADER_TIME AC_STRUCT_TM AC_TYPE_UINT16_T AC_TYPE_UINT32_T AC_TYPE_UINT64_T AC_TYPE_UINT8_T AC_C_VOLATILE AC_FUNC_ALLOCA AC_FUNC_CLOSEDIR_VOID AC_FUNC_ERROR_AT_LINE AC_FUNC_FORK AC_FUNC_GETGROUPS AC_PROG_GCC_TRADITIONAL AC_FUNC_MALLOC AC_FUNC_SELECT_ARGTYPES AC_FUNC_STAT AC_FUNC_VPRINTF AC_CHECK_FUNCS([atexit dup2 gethostbyname gettimeofday memset mkdir putenv realpath select socket strchr strdup strerror strndup strrchr strstr]) AC_CONFIG_FILES([autoconf.mk]) AC_OUTPUT sbrsh-7.6.1/daemon.c0000644000232200023220000016317111013052103014436 0ustar pbuilderpbuilder/* * Copyright (c) 2003, 2004, 2005 Nokia * Author: Timo Savola * * This program is licensed under GPL (see COPYING for details) */ #define _GNU_SOURCE #include "types.h" #include "daemon.h" #include "common.h" #include "protocol.h" #include "buffer.h" #include "mount.h" #include "fakeroot.h" #include "version.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define ISSET(fd, set) ((fd) >= 0 && FD_ISSET((fd), (set))) #define debug_vector if (debug_file) print_debug_vector #define debug_verbose if (debug_verbose_flag) debug #define PROC_MOUNTS_SSHFS "sshfs#" extern char **environ; FILE *debug_file = NULL; static char *debug_filename = NULL; static int debug_verbose_flag = 0; static int daemon_pid = -1; static char *port = DEFAULT_PORT; static bool_t local_only = FALSE; static bool_t sandbox = TRUE; static bool_t allow_root = FALSE; static int mount_expiration = DEFAULT_MOUNT_EXPIRATION; static char *mount_cmd = DEFAULT_MOUNT_CMD; static char *umount_cmd = DEFAULT_UMOUNT_CMD; static char *bind_opt = DEFAULT_BIND_OPT; static char *fusermount_cmd = DEFAULT_FUSERMOUNT_CMD; static char *sshfs_cmd = DEFAULT_SSHFS_CMD; /** Doubly linked list of mount entries. */ static struct { mount_t *head; mount_t *tail; } all_mounts = { NULL, NULL }; /** The mounts of the handler processes. */ static pid_mounts_t *pid_mounts = NULL; static struct { pid_t pid; const char *name; } debug_info = { -1, NULL }; void set_debug_name(const char *name) { debug_info.pid = getpid(); debug_info.name = name; } /** * Prints only timestamp and pid to debug log. */ static void print_debug_prefix(void) { struct timeval tv; struct tm *t; const char *domain = ""; gettimeofday(&tv, NULL); t = gmtime(&tv.tv_sec); if (debug_info.pid == getpid()) domain = debug_info.name; fprintf(debug_file, "%02d-%02d-%04d %02d:%02d:%02d.%03ld %5d %7s ", t->tm_mday, t->tm_mon + 1, 1900 + t->tm_year, t->tm_hour, t->tm_min, t->tm_sec, tv.tv_usec / 1000, getpid(), domain); } static void open_debug_log(void) { if (debug_file) { debug("Debugging is already enabled"); return; } if (!debug_filename) { debug_filename = malloc(strlen(DEFAULT_DEBUG_FILENAME_FMT) + 5); if (!debug_filename) { oom_error(); return; } sprintf(debug_filename, DEFAULT_DEBUG_FILENAME_FMT, port); } debug_file = fopen(debug_filename, "a"); if (!debug_file) { error("Can't append to %s", debug_filename); return; } debug("Debugging enabled"); debug("sbrshd version %d%s", PROTOCOL_VERSION, REVISION); debug("Port: %s", port); debug("Local only: %s", local_only ? "yes" : "no"); debug("Sandbox: %s", sandbox ? "yes" : "no"); debug("Allow root: %s", allow_root ? "yes" : "no"); if (mount_expiration > 0) { debug("Mount expiration: %d seconds", mount_expiration); } else if (mount_expiration == 0) { debug("Mount expiration: immediate"); } else { debug("Mount expiration: never"); } } static void close_debug_log(void) { if (debug_file) { debug("Debugging disabled"); fclose(debug_file); debug_file = NULL; } } /** * Prints message to debug log. */ void print_debug(const char *msg, ...) { va_list arg; print_debug_prefix(); va_start(arg, msg); vfprintf(debug_file, msg, arg); va_end(arg); fprintf(debug_file, "\n"); fflush(debug_file); } /** * Prints string vector to debug log. */ static void print_debug_vector(const char *msg, char **vec) { print_debug_prefix(); fprintf(debug_file, "%s", msg); while (*vec) fprintf(debug_file, " %s", *vec++); fprintf(debug_file, "\n"); fflush(debug_file); } /** * Prints message and errno description (if non-zero) to syslog and sends it to * the client (if data is non-null). */ static void print_error(handler_t *data, const char *progname, const char *msg, va_list arg, int priority) { char str[1024] = { '\0' }, *err; size_t len; vsnprintf(str, sizeof (str) - 1, msg, arg); len = strlen(str); err = strerror(errno); if (errno && err && (len + strlen(err) + 3) < sizeof (str)) { strcat(str, " ("); strcat(str, err); strcat(str, ")"); len = strlen(str); } if (progname) { fprintf(stderr, "%s: %s\n", progname, str); } else { syslog(priority, "%s", str); debug(priority == LOG_WARNING ? "Warning: %s" : "Error: %s", str); } if (data) write_str_packet(data->sd, PTYPE_ERROR, str); } void send_error(handler_t *data, const char *msg, ...) { va_list arg; if (!msg) { msg = strerror(errno); errno = 0; } va_start(arg, msg); print_error(data, NULL, msg, arg, LOG_ERR); va_end(arg); } /** * Prints message and errno description to syslog and stderr. */ static void error_err(const char *progname, const char *msg, ...) { va_list arg; va_start(arg, msg); print_error(NULL, progname, msg, arg, LOG_ERR); va_end(arg); } /* * Prints message and errno description to syslog. */ void error(const char *msg, ...) { va_list arg; va_start(arg, msg); print_error(NULL, NULL, msg, arg, LOG_ERR); va_end(arg); } /** * Prints message to syslog. */ static void warn(const char *msg, ...) { va_list arg; errno = 0; va_start(arg, msg); print_error(NULL, NULL, msg, arg, LOG_WARNING); va_end(arg); } /** * Passes the contents of BUF to send_error(). */ static void flush(handler_t *data, char *buf, size_t *lenp) { if (*lenp > 0) { buf[*lenp] = '\0'; *lenp = 0; errno = 0; send_error(data, buf); } } /** * Executes argv[0] in a child process. * @return -1 on error, 0 otherwise */ static int execute(handler_t *data, char **argv, int uid, int gid) { pid_t pid; int status, err[2]; size_t pos = 0; char line[1024]; if (pipe(err) < 0) { send_error(data, "Can't create pipe"); return -1; } pid = fork(); if (pid < 0) { send_error(data, "Can't fork"); return -1; } if (pid == 0) { /* child */ set_debug_name("EXECUTE"); if (gid >= 0) { debug("Changing to group ID %d", gid); if (setgid(gid) < 0) { send_error(data, "Can't change group ID to %d", gid); exit(1); } } if (uid >= 0) { debug("Changing to user ID %d", uid); if (setuid(uid) < 0) { send_error(data, "Can't change user ID to %d", uid); exit(1); } } if (dup2(err[1], STDOUT_FILENO) != STDOUT_FILENO) { send_error(data, "Can't duplicate pipe as stdout"); exit(1); } if (dup2(err[1], STDERR_FILENO) != STDERR_FILENO) { send_error(data, "Can't duplicate pipe as stderr"); exit(1); } close(err[0]); execv(argv[0], argv); send_error(data, "Can't execute command: %s", argv[0]); exit(1); } /* parent */ close(err[1]); while (1) { ssize_t len; char c = '\0'; len = read(err[0], &c, 1); if (c == '\0' && pos == 0) break; if (len < 0 && errno == EINTR) continue; if (len <= 0) { flush(data, line, &pos); break; } if (c == '\n' || c == '\0') { flush(data, line, &pos); continue; } if (pos == sizeof (line)) flush(data, line, &pos); line[pos++] = c; } close(err[0]); if (waitpid(pid, &status, 0) != pid) return -1; if (status < 0) return -1; errno = 0; return (WEXITSTATUS(status) != 0) ? -1 : 0; } static void check_for_busybox(const char *progname) { char mount_buf[PATH_MAX], *real_mount; real_mount = realpath(mount_cmd, mount_buf); if (!real_mount) { error_err(progname, "Can't get real path of %s", mount_cmd); exit(1); } if (strstr(basename(real_mount), "busybox") != NULL) { debug("%s is Busybox", mount_cmd); bind_opt = DEFAULT_BIND_OPT_BUSYBOX; } } /** * Read the lines of the config file into a string vector. */ static char **read_config(const char *filename) { int count, i, slot; char **lines; FILE *file; file = fopen(filename, "r"); if (!file) { if (errno == ENOENT) return calloc(1, sizeof (char *)); error_err(filename, "cannot open for reading"); return NULL; } count = 1; /* count EOF as a newline */ while (1) { int c = fgetc(file); if (c == EOF) break; if (c == '\n') ++count; } rewind(file); lines = calloc(count + 1, sizeof (char *)); if (!lines) { oom_error(); goto _out; } for (i = 0, slot = 0; i < count; ++i) { int pos, len; char *line, *ptr; pos = ftell(file); if (pos < 0) { error_err(filename, "tell"); goto _err; } len = 0; while (1) { int c = fgetc(file); if (c == EOF) break; ++len; if (c == '\n') break; } if (fseek(file, pos, SEEK_SET) < 0) { error_err(filename, "seek"); goto _err; } line = calloc(len + 1, sizeof (char)); if (!line) { oom_error(); goto _err; } if (fread(line, sizeof (char), len, file) != len) { error_err(filename, "read"); goto _err; } if (len == 0) continue; if (line[len - 1] == '\n') line[len - 1] = '\0'; ptr = strchr(line, '#'); if (ptr) *ptr = '\0'; if (strlen(line) > 0) lines[slot++] = line; } _out: fclose(file); return lines; _err: free_vec((void **) lines, NULL); goto _out; } /** * Write a string vector into the config file. */ static int write_config(const char *filename, char **lines) { FILE *file; int i, retval = -1; file = fopen(filename, "w"); if (!file) { error_err(filename, "cannot open for writing"); return -1; } for (i = 0; lines[i]; ++i) { char *line; int len; line = lines[i]; len = strlen(line); if (fwrite(line, sizeof (char), len, file) != len) { error_err(filename, "write"); goto _out; } if (len == 0 || line[len - 1] != '\n') if (fputc('\n', file) == EOF) { error_err(filename, "write"); goto _out; } } retval = 0; _out: fclose(file); return retval; } /** * Add an entry to the config file. */ static int add_to_config(char *host) { char **lines; int i, retval = 0; bool_t found = FALSE; lines = read_config(CONFIG_NAME); if (lines == NULL) return -1; for (i = 0; lines[i]; ++i) { char *line = lines[i]; char *ptr = find_space(line); if (strlen(host) == ptr - line && strncmp(line, host, ptr - line) == 0) { found = TRUE; break; } } if (!found) { int oldlineslen; char **vec, *line; oldlineslen = calc_vec_len((void **) lines); vec = calloc(sizeof (char *), oldlineslen + 2); if (!vec) { oom_error(); retval = -1; goto _out; } memcpy(vec, lines, sizeof (char *) * oldlineslen); free(lines); lines = vec; line = strdup(host); if (!line) { oom_error(); retval = -1; goto _out; } lines[oldlineslen] = line; retval = write_config(CONFIG_NAME, lines); } _out: free_vec((void **) lines, NULL); return retval; } /** * Finds an IP address from a config file. * @param data handler state * @param filename the config file path * @return TRUE if found */ static bool_t find_host(handler_t *data, const char *filename) { bool_t found = FALSE; char **lines; int i; lines = read_config(CONFIG_NAME); if (lines == NULL) return FALSE; for (i = 0; lines[i]; ++i) { char *line, *ptr, *p, *star; int len; line = lines[i]; ptr = find_space(line); for (p = ptr; *p; p++) if (!isspace(*p)) { errno = 0; error("Malformed line in %s: \"%s\" (old config file format?)", CONFIG_NAME, line); return FALSE; } *ptr = '\0'; len = ptr - line; star = strchr(line, '*'); if (star) { int pos = star - line; if (pos != len - 1) { errno = 0; error("Malformed line in %s: %s", CONFIG_NAME, line); continue; } --len; } if (!star && strlen(data->host) != len) continue; if (strncmp(line, data->host, len) == 0) { debug("Found matching line: %s", line); found = TRUE; break; } } free_vec((void **) lines, NULL); return found; } /** * Recursively creates all needed directories (with MKDIR_PERMS). */ static int mkdirs(const char *path) { char *parent, *p; int rc = -1; if (mkdir(path, MKDIR_PERMS) == 0 || errno == EEXIST) return 0; if (errno != ENOENT) return -1; parent = strdup(path); if (!parent) return -1; p = strrchr(parent, '/'); if (p && parent != p) { *p = '\0'; rc = 0; } if (rc == 0) rc = mkdirs(parent); free(parent); if (rc == 0 && mkdir(path, MKDIR_PERMS) < 0 && errno != EEXIST) rc = -1; return rc; } /** * Frees all resources associated with a mount entry. */ static void mnt_free(mount_t *m) { if (m->info.opts) free(m->info.opts); if (m->info.device) free(m->info.device); if (m->info.point) free(m->info.point); free(m); } /** * Finds an entry from the MOUNTS list. * @param point the mount point of the entry */ static mount_t *mnt_list_find(const char *point) { mount_t *mnt; for (mnt = all_mounts.head; mnt; mnt = mnt->next) if (strcmp(mnt->info.point, point) == 0) break; return mnt; } /** * Adds an entry to the MOUNTS list. The entries are kept in ascending order * based on their mount point strings. */ static void mnt_list_add(mount_t *mnt) { mount_t *prev, *next; prev = NULL; next = all_mounts.head; while (next) { if (strcmp(next->info.point, mnt->info.point) > 0) break; prev = next; next = next->next; } mnt->prev = prev; mnt->next = next; if (prev) prev->next = mnt; else all_mounts.head = mnt; if (next) next->prev = mnt; else all_mounts.tail = mnt; } /** * Removes and frees an entry from the MOUNTS list. */ static void mnt_list_del(mount_t *mnt) { if (mnt->prev) mnt->prev->next = mnt->next; else all_mounts.head = mnt->next; if (mnt->next) mnt->next->prev = mnt->prev; else all_mounts.tail = mnt->prev; mnt_free(mnt); } /** * Executes mount_cmd. */ static int do_mount(handler_t *data, const mount_info_t *mi) { char *argv[8]; int uid = -1; int gid = -1; memset(argv, 0, sizeof (argv)); argv[0] = mount_cmd; argv[1] = mi->device; argv[2] = mi->point; switch (mi->type) { case MTYPE_NFS: argv[3] = "-t"; argv[4] = "nfs"; argv[5] = mi->opts ? "-o" : NULL; argv[6] = mi->opts; if (mi->opts) { debug("Executing: %s %s %s -t nfs -o %s", argv[0], argv[1], argv[2], argv[6]); } else { debug("Executing: %s %s %s -t nfs", argv[0], argv[1], argv[2]); } break; case MTYPE_BIND: split_string(bind_opt, &argv[3], &argv[4], &argv[5], &argv[6], NULL); debug("Executing: %s %s %s %s %s %s %s", argv[0], argv[1], argv[2], argv[3] ? argv[3] : "", argv[4] ? argv[4] : "", argv[5] ? argv[5] : "", argv[6] ? argv[6] : ""); break; case MTYPE_SSH: argv[0] = sshfs_cmd; argv[3] = mi->opts ? "-o" : NULL; argv[4] = mi->opts; if (mi->opts) { debug("Executing: %s %s %s -o %s", argv[0], argv[1], argv[2], argv[4]); } else { debug("Executing: %s %s %s", argv[0], argv[1], argv[2]); } if (!data->param.ids || data->param.ids->len < 2) { errno = 0; send_error(data, "Valid IDS parameter required"); return -1; } uid = data->param.ids->vec[0]; gid = data->param.ids->vec[1]; break; default: errno = EINVAL; return -1; } return execute(data, argv, uid, gid); } static int do_unmount(handler_t *data, const mount_info_t *mi) { char *argv[] = { umount_cmd, mi->point, NULL, NULL }; /* Handle SSHFS as special case, needs to be unmounted using fusermount */ if (mi->type == MTYPE_SSH) { argv[0] = fusermount_cmd; argv[1] = "-u"; argv[2] = mi->point; } debug("Executing: %s %s %s", argv[0], argv[1], argv[2]); return execute(data, argv, -1, -1); } static mount_t *mnt_create(handler_t *data, mount_info_t *mi) { mount_t *mnt; mnt = mnt_list_find(mi->point); if (mnt) return mnt; mnt = calloc(1, sizeof (mount_t)); if (!mnt) { errno = 0; send_error(data, oom); return NULL; } mntinfo_copy(&mnt->info, mi); mnt_list_add(mnt); return mnt; } /** * Checks /proc/mounts if mount point is already mounted. * @param data handler state * @param point mount point to compare against * @return -1 on error, 0 otherwise */ static int is_mounted(handler_t *data, const char *point) { int mounted = FALSE; FILE *file; char buf[1024], buf1[PATH_MAX], buf2[PATH_MAX], *point1; point1 = realpath(point, buf1); if (!point1) return FALSE; file = fopen(MOUNTS_FILE, "r"); if (!file) { send_error(data, "Can't open " MOUNTS_FILE); return -1; } while (1) { char *device, *point2tmp, *point2; if (read_line_nocomments(file, buf, sizeof (buf)) < 0) break; split_string(buf, &device, &point2tmp, NULL); if (!device || !point2tmp) continue; point2 = realpath(point2tmp, buf2); if (!point2) { error("Can't get real path of %s", point2tmp); continue; } if (strcmp(point1, point2) == 0) { mounted = TRUE; break; } } fclose(file); debug(mounted ? "%s is mounted" : "%s is not mounted", point); return mounted; } /** * Mounts a filesystem if not already mounted. */ static mount_t *add_mount(handler_t *data, mount_info_t *mi) { int mounted; mount_t *mnt; mounted = is_mounted(data, mi->point); if (mounted < 0) return NULL; mnt = mnt_create(data, mi); if (!mnt) return NULL; if (!mounted) { debug("Creating directory %s", mnt->info.point); if (mkdirs(mnt->info.point) < 0) { send_error(data, "Can't create directory: %s", mnt->info.point); return NULL; } if (do_mount(data, &mnt->info) < 0) return NULL; } ++mnt->usage; return mnt; } /** * Unmounts a filesystem. * @return -1 on error, 0 otherwise */ static int remove_mount(handler_t *data, const mount_info_t *mi) { int mounted; mount_t *mnt; mounted = is_mounted(data, mi->point); if (mounted < 0) return -1; mnt = mnt_list_find(mi->point); if (mnt) { if (mnt->usage == 0) mnt_list_del(mnt); else send_error(data, "Warning: %s usage is %d", mnt->info.point, mnt->usage); } if (mounted && do_unmount(data, mi) < 0) return -1; return 0; } /** * Decrements the usage count of a mount and possibly sets its expiration time. */ static void release_mount(mount_t *mnt) { --mnt->usage; debug("Releasing mount %s (%d users remain)", mnt->info.point, mnt->usage); if (mnt->usage <= 0 && mount_expiration >= 0) { /* Force bind mounts to expire immediately */ if (mnt->info.type == MTYPE_BIND) mnt->expiration = 0; else mnt->expiration = time(NULL) + mount_expiration; } } /** * Steps through the MOUNTS list and unmounts all unused and expired entries. */ static void expire_mounts(void) { mount_t *prev, *curr; time_t now; debug("Checking for expired mounts"); now = time(NULL); prev = NULL; curr = all_mounts.tail; while (curr) { prev = curr->prev; if (curr->usage <= 0 && curr->expiration <= now) { debug("Unmounting %s", curr->info.point); if (curr->next) curr->next->prev = curr->prev; else all_mounts.tail = curr->prev; if (curr->prev) curr->prev->next = curr->next; else all_mounts.head = curr->next; // TODO Check if unmount was succesful do_unmount(NULL, &curr->info); mnt_free(curr); } curr = prev; } } /** * Parse remote filesystem path from /proc/mounts for mount point * @param data handler state * @param point mount point to compare against * @param cpy remote filesystem path * @return string length on success, -1 otherwise */ static int get_remote(handler_t *data, const char *point, char *cpy) { FILE *file; char buf[1024], buf1[PATH_MAX], buf2[PATH_MAX], *point1; point1 = realpath(point, buf1); if (!point1) return -1; file = fopen(MOUNTS_FILE, "r"); if (!file) { send_error(data, "Can't open " MOUNTS_FILE); return -1; } while (1) { char *device, *point2tmp, *point2; if (read_line_nocomments(file, buf, sizeof (buf)) < 0) break; split_string(buf, &device, &point2tmp, NULL); if (!device || !point2tmp) continue; point2 = realpath(point2tmp, buf2); if (!point2) { error("Can't get real path of %s", point2tmp); continue; } if (strcmp(point1, point2) != 0) continue; mount_t *mnt = mnt_list_find(point); /* Sample sshfs entry: * sshfs#you@host:/path / fuse nosuid,nodev,max_read=65536 0 0 */ if (mnt && (mnt->info.type == MTYPE_SSH && strncmp(device, PROC_MOUNTS_SSHFS, strlen(PROC_MOUNTS_SSHFS)) == 0)) { strcat(cpy, device + strlen(PROC_MOUNTS_SSHFS)); debug("Remote sshfs mount point is '%s'", cpy); fclose(file); return strlen(cpy); } /* Sample nfs entry: * host:/path / nfs rw,addr=host 0 0 */ if (mnt && (mnt->info.type == MTYPE_NFS)) { strcat(cpy, device); debug("Remote nfs mount point is '%s'", cpy); fclose(file); return strlen(cpy); } } fclose(file); return -1; } /** * Expire mount points if remote mount point has changed. * @param data handler state * @param mi mount point to compare against * @return -1 on error, 0 otherwise */ static int expire_changed(handler_t *data, const mount_info_t *mi) { mount_t *mnt; char remote_path[PATH_MAX] = { 0 }; /* On Linux path max is 4096 */ int len; mnt = mnt_list_find(mi->point); if (mnt == NULL) return 0; /* The remote path for a bind mount can be aliased to the original * device for mount points (not the directory originally bound), so * get_remote() will fail for those; and, we don't even check binds * below, so bail early. */ if (mi->type == MTYPE_BIND) return 0; len = get_remote(data, mi->point, remote_path); if (len <= 0) return len; /* TODO: Add check also for NFS mounts */ if ((mi->type == MTYPE_NFS || mi->type == MTYPE_SSH) && strcmp(remote_path, mi->device) != 0) { mount_t *curr; int exprcnt = 0; warn("Remote mount point for '%s' has changed", mi->point); /* Match target root and mount points under target root. * Tries to set expires also for bind mounts under the matching * mount point. */ for (curr = all_mounts.tail; curr != NULL; curr = curr->prev) { if (strncmp(mi->point, curr->info.point, strlen(mi->point) - 1) != 0) continue; if (curr->usage <= 0) { debug("Setting %s as expired", curr->info.point); curr->expiration = 0; ++exprcnt; } else { send_error(data, "Warning: %s remote mount point has changed, has usage %d", mnt->info.point, mnt->usage); return -1; } } if (exprcnt > 0) expire_mounts(); } return 0; } /** * Unmounts all entries in the MOUNTS list. */ static void unmount_all(void) { mount_t *prev, *mnt; mnt = all_mounts.tail; while (mnt) { prev = mnt->prev; debug("Unmounting %s", mnt->info.point); // TODO Check if unmount was succesful do_unmount(NULL, &mnt->info); mnt_free(mnt); mnt = prev; } all_mounts.head = NULL; all_mounts.tail = NULL; } /** * Unmounts all filesystems listed in the command info's mount info vector. * @return 0 or -1 on error */ static int unmount_infos(handler_t *data) { mount_info_t **mip; int cnt, rc = 0; debug("Unmounting filesystems"); /* count the options and go to end of the vector */ for (cnt = 0, mip = data->param.mounts; *mip; ++cnt, ++mip) ; /* try to unmount filesystems in reverse order */ for (--mip; cnt-- > 0; --mip) if (remove_mount(data, *mip) < 0) rc = -1; return rc; } /** * Gets the amount of data we can read from a file (which may be a tty). */ static ssize_t get_data_length(handler_t *data, int fd, size_t max) { ssize_t len; len = max; if (isatty(fd)) { if (ioctl(fd, FIONREAD, &len) < 0) { send_error(data, "Can't check pty for available data"); return -1; } if (len < 0) { send_error(data, "ioctl() gave invalid read length"); return -1; } if (len > max) len = max; } return len; } /** * Reads some data from a fd and writes it into the socket (sd) in a packet. * The fd will be set to -1 at EOF. * @param data handler state * @param out describes the output */ static void send_data(handler_t *data, output_desc_t *out) { ssize_t len; len = get_data_length(data, out->fd, out->req); if (len < 0) goto _error; /* nothing to do? */ if (len == 0) return; len = read_ni(out->fd, data->tmp_buf, len); if (len < 0) { send_error(data, "Can't read output of the process"); goto _error; } if (len) { debug_verbose("Sending %s DATA packet (%d bytes)", output_desc_is_stdout(out) ? "OUT" : "ERR", len); if (write_buf_packet(data->sd, out->data_type, len, data->tmp_buf) < 0) { error("Can't write packet to socket"); goto _error; } out->req -= len; } else { debug(output_desc_is_stdout(out) ? "Stdout hit EOF" : "Stderr hit EOF"); out->fd = -1; out->req = 0; } return; _error: data->error = TRUE; } /** * Sends a REQuest for more DATA. */ static void send_request(handler_t *data, input_desc_t *in) { /* Already waiting? */ if (in->wait) return; debug_verbose("Sending IN REQ packet"); if (write_enum(data->sd, in->req_type) < 0) { error("Can't write packet to socket"); goto _error; } in->wait = TRUE; return; _error: data->error = TRUE; } /** * Writes (some of) in->buf to in->fd. */ static void write_buffer(handler_t *data, input_desc_t *in) { debug_verbose("Writing buffer to stdin"); if (buf_write_out(in->buf, &in->fd) < 0) debug("Stdin is closed (%s)", strerror(errno)); if (buf_is_empty(in->buf)) in->wait = FALSE; } /** * Reads bytes from socket to buf_in. * @param data handler state * @param len the maximum number of bytes to be read */ static void receive_stream(handler_t *data) { uint32_t len; if (read_uint32(data->sd, &len) < 0) { send_error(data, "Can't read data packet length"); goto _error; } if (len > 0) { debug_verbose("Receiving IN DATA packet (%d bytes)", len); if (buf_read_in(data->in.buf, data->sd, len) < 0) { send_error(data, "Can't read IN DATA packet to buffer"); goto _error; } } else { debug("Receiving IN DATA packet: EOF"); buf_set_eof(data->in.buf); } return; _error: data->error = TRUE; } /** * Read an IN DATA or OUT/ERR REQ packet from socket. * @return 1 if EOF from client, 0 otherwise */ static int receive_packet(handler_t *data) { ptype_t type = read_enum(data->sd); switch (type) { case -1: if (errno == 0) { debug("EOF from client"); return 1; } error("Can't read packet header from socket"); goto _error; case PTYPE_IN_DATA: receive_stream(data); return 0; case PTYPE_OUT_REQ: debug_verbose("Receiving OUT REQ packet"); data->out.req = BUFFER_SIZE; return 0; case PTYPE_ERR_REQ: debug_verbose("Receiving ERR REQ packet"); data->err.req = BUFFER_SIZE; return 0; default: errno = 0; send_error(data, "Received packet has unexpected type (0x%02x)", type); /* goto _error */ } _error: data->error = TRUE; return 0; } /** * Prints information on an exited process (pid) to debug log. */ static void print_status(int pid, int status) { int sig; if (WIFEXITED(status)) { debug("Process %d returned: %d", pid, WEXITSTATUS(status)); } else if (WIFSIGNALED(status)) { sig = WTERMSIG(status); debug("Process %d terminated by signal: %s (%d)", pid, strsignal(sig), sig); } else if (WIFSTOPPED(status)) { sig = WSTOPSIG(status); debug("Process %d stopped by signal: %s (%d)", pid, strsignal(sig), sig); } else { error("Invalid status %d for pid %d", status, pid); } } #define print_debug_bool(m, b) print_debug(m, (b) ? "yes" : "no") #define debug_bool(m, b) if (debug_file) print_debug_bool(m, b) #define debug_status() \ if (debug_verbose_flag && debug_file) { \ print_debug_bool("Process alive = %s", alive); \ print_debug_bool("Stdin open = %s", data->in.fd >= 0); \ print_debug_bool("Stdin waiting = %s", data->in.wait); \ print_debug ("Stdin buffered = %d bytes%s", \ buf_size(data->in.buf), \ data->in.buf->eof ? " (EOF set)" : ""); \ print_debug_bool("Stdout open = %s", data->out.fd >= 0); \ print_debug ("Stdout request = %d bytes", data->out.req); \ print_debug_bool("Stderr open = %s", data->err.fd >= 0); \ print_debug ("Stderr request = %d bytes", data->err.req); \ print_debug_bool("Error = %s", data->error); \ } #define debug_select(msg) \ if (debug_verbose_flag && debug_file) { \ print_debug(msg " [%3s %2s %3s %3s][%2s]", \ ISSET(data->sd, &readfds) ? "NET" : "", \ ISSET(data->in.fd, &readfds) ? "IN" : "", \ ISSET(data->out.fd, &readfds) ? "OUT" : "", \ ISSET(data->err.fd, &readfds) ? "ERR" : "", \ ISSET(data->in.fd, &writefds) ? "IN" : ""); \ } /** * Manages the streams of the command executor (= the child-child process). * @param data handler state * @param pid of the child process * @return the return code (-1 on error) */ static int handler_manage(handler_t *data, pid_t pid) { fd_set readfds, writefds; int maxfd1, status; bool_t alive = TRUE; debug("Managing process %d", pid); maxfd1 = MAX(data->sd, data->in.fd); maxfd1 = MAX(maxfd1, data->out.fd); maxfd1 = MAX(maxfd1, data->err.fd); ++maxfd1; data->in.buf = buf_alloc(); if (!data->in.buf) { errno = 0; send_error(data, oom); goto _kill; } while (1) { int count, val; debug_status(); if (data->error) { debug("Ending loop due to error"); goto _kill; } /* non-TTY mode: check if we should exit */ if (!data->param.term && data->in.fd < 0 && data->out.fd < 0 && data->err.fd < 0) { debug("Ending loop due to closed stdin, stdout and stderr descriptors"); break; } FD_ZERO(&readfds); FD_ZERO(&writefds); FD_SET(data->sd, &readfds); if (data->out.fd >= 0 && data->out.req > 0) FD_SET(data->out.fd, &readfds); if (!data->param.term && data->err.fd >= 0 && data->err.req > 0) FD_SET(data->err.fd, &readfds); if (data->in.fd >= 0) { /* we want select to wake up if stdin hits EOF * (child exited but we haven't noticed yet) */ if (!data->param.term) FD_SET(data->in.fd, &readfds); if (!buf_is_empty(data->in.buf) || !data->in.wait) FD_SET(data->in.fd, &writefds); } debug_select("Selecting"); count = select(maxfd1, &readfds, &writefds, NULL, NULL); if (count < 0) { if (errno == EINTR) { debug("Select interrupted"); } else { error("Select failed"); debug("Ending loop due to failed select"); goto _kill; } } else if (count > 0) { debug_select("Selected "); /* TTY mode: check if we should exit */ if (data->param.term && !alive && ISSET(data->out.fd, &readfds) && get_data_length(data, data->out.fd, 1) <= 0) { debug("Ending loop due to dead process and empty stdout buffer"); break; } /* EOF from client? */ if (ISSET(data->sd, &readfds) && receive_packet(data) == 1) { close(data->sd); data->sd = -1; debug("Ending loop due to closed socket"); goto _kill; } if (ISSET(data->out.fd, &readfds)) send_data(data, &data->out); if (ISSET(data->err.fd, &readfds)) send_data(data, &data->err); if (ISSET(data->in.fd, &writefds)) { if (buf_is_empty(data->in.buf)) send_request(data, &data->in); else write_buffer(data, &data->in); } /* stdin at EOF? */ if (!data->param.term && ISSET(data->in.fd, &readfds)) data->in.fd = -1; } /* collect late children */ val = waitpid(-1, &status, WNOHANG); if (val < 0 && errno != ECHILD) { send_error(data, "Can't wait for children"); return -1; } if (val > 0) { if (val == pid) alive = FALSE; print_status(val, status); } } if (alive) { if (waitpid(pid, &status, 0) < 0) { send_error(data, "Can't wait for child %d", pid); return -1; } print_status(pid, status); } if (WIFEXITED(status)) return WEXITSTATUS(status); return -1; _kill: debug("Sending SIGTERM to command process %d", pid); kill(pid, SIGTERM); return -1; } /** * @return TRUE/FALSE or -1 on error */ static int authenticate(handler_t *data) { bool_t ok; debug("Searching for host %s in %s", data->host, CONFIG_NAME); ok = find_host(data, CONFIG_NAME); if (!ok) warn("Unauthorized connection from %s", data->host); return ok; } /** * More or less like forkpty. * @return pid of the child process (0 for child) or -1 on error */ static pid_t fork_pty(handler_t *data, int *ptyfd) { int master, slave; pid_t pid; if (openpty(&master, &slave, NULL, NULL, data->param.term) < 0) { send_error(data, "Can't open a pseudo-tty"); return -1; } pid = fork(); if (pid < 0) { send_error(data, "Can't fork"); return -1; } if (pid == 0) { /* child */ set_debug_name("COMMAND"); close(master); if (login_tty(slave)) { send_error(data, "Can't login to a pseudo-tty"); exit(1); } } else { /* parent */ close(slave); *ptyfd = master; } return pid; } /** * More or less like fork, but the stdin/stdout/stderr of the child process * are set to point to sockets. * @param data handler state * @param infd a place to write the parent-end of the child's stdin socketpair * @param outfd a place to write the parent-end of the child's stdin socketpair * @param errfd a place to write the parent-end of the child's stdin socketpair * @return pid of the child process (0 for child) or -1 on error */ static pid_t fork_sockets(handler_t *data, int *infd, int *outfd, int *errfd) { int insd[2], outsd[2], errsd[2]; pid_t pid; if (socketpair(AF_UNIX, SOCK_STREAM, 0, insd) < 0 || socketpair(AF_UNIX, SOCK_STREAM, 0, outsd) < 0 || socketpair(AF_UNIX, SOCK_STREAM, 0, errsd) < 0) { send_error(data, "Can't create socket pairs"); return -1; } pid = fork(); if (pid < 0) { send_error(data, "Can't fork"); return -1; } if (pid == 0) { /* child */ set_debug_name("COMMAND"); close(insd[0]); close(outsd[0]); close(errsd[0]); if (dup2(insd[1], STDIN_FILENO) != STDIN_FILENO) { send_error(data, "Can't duplicate socket as stdin"); exit(1); } if (dup2(outsd[1], STDOUT_FILENO) != STDOUT_FILENO) { send_error(data, "Can't duplicate socket as stdout"); exit(1); } if (dup2(errsd[1], STDERR_FILENO) != STDERR_FILENO) { send_error(data, "Can't duplicate socket as stderr"); exit(1); } } else { /* parent */ if (set_nonblocking(insd[0], TRUE)) { send_error(data, "Can't make socket non-blocking"); return -1; } *infd = insd[0]; *outfd = outsd[0]; *errfd = errsd[0]; } close(insd[1]); close(outsd[1]); close(errsd[1]); return pid; } /** * Read "SBOX_RLIMIT"-fields from ENVIRONment and set them in place. * @return -1 on error, 0 otherwise */ static int set_rlimits(handler_t *data) { static struct { const char *key; int resource; } info[] = { { ENV_RLIMIT_PREFIX "CPU", RLIMIT_CPU }, { ENV_RLIMIT_PREFIX "FSIZE", RLIMIT_FSIZE }, { ENV_RLIMIT_PREFIX "DATA", RLIMIT_DATA }, { ENV_RLIMIT_PREFIX "STACK", RLIMIT_STACK }, { ENV_RLIMIT_PREFIX "CORE", RLIMIT_CORE }, { ENV_RLIMIT_PREFIX "RSS", RLIMIT_RSS }, { ENV_RLIMIT_PREFIX "NPROC", RLIMIT_NPROC }, { ENV_RLIMIT_PREFIX "NOFILE", RLIMIT_NOFILE }, { ENV_RLIMIT_PREFIX "MEMLOCK", RLIMIT_MEMLOCK }, { ENV_RLIMIT_PREFIX "AS", RLIMIT_AS }, { NULL, 0 } }; const int prefixlen = strlen(ENV_RLIMIT_PREFIX); char *resname; int i; char *str; struct rlimit lim; for (i = 0; info[i].key; ++i) { resname = (char *) info[i].key + prefixlen; str = getenv(info[i].key); if (!str) continue; str = skip_spaces(str); if (strlen(str) == 0) continue; debug("Setting %s resource to %s", resname, str); if (getrlimit(info[i].resource, &lim) < 0) { send_error(data, "Can't get %s resource limit", resname); return -1; } lim.rlim_cur = RLIM_INFINITY; if (strncmp(str, ENV_RLIMIT_UNLIMITED, strlen(ENV_RLIMIT_UNLIMITED)) != 0) { lim.rlim_cur = atol(str); if (lim.rlim_cur == 0 && strcmp(str, "0") != 0) { send_error(data, "Invalid %s resource limit value: %s", resname, str); return -1; } } if (setrlimit(info[i].resource, &lim) < 0) { send_error(data, "Can't set %s resource limit to %d while maximum is %d", resname, lim.rlim_cur, lim.rlim_max); return -1; } } return 0; } /** * Copies a string and adds a "/" prefix if it doesn't have one. * abort()s if ENOMEM. This function is evil! */ static char *add_root_prefix(handler_t *data, char *orig) { char *dir; if (orig[0] == '/') return orig; dir = malloc(1 + strlen(orig) + 1); if (!dir) { errno = 0; send_error(data, oom); exit(1); } strcpy(dir, "/"); strcat(dir, orig); return dir; } /** * Executes the command. Never returns. */ static void execute_command(handler_t *data) { char **argv; /* * Change the root directory */ if (data->root) { char *dir = add_root_prefix(data, data->root); debug("Changing root directory to %s", dir); if (chroot(dir) < 0) { send_error(data, "Can't change root directory to: %s", dir); exit(1); } } chdir("/"); /* * Change gids and uid (in that order because we need to be root) */ { uid_t uid; gid_t gid; int gcount, i; gid_t groups[NGROUPS_MAX]; if (!data->param.ids || data->param.ids->len < 2) { errno = 0; send_error(data, "Valid IDS parameter required"); exit(1); } uid = data->param.ids->vec[0]; gid = data->param.ids->vec[1]; if (!allow_root) for (i = 0; i < data->param.ids->len; i++) if (data->param.ids->vec[i] == 0) { send_error(data, "root access denied"); exit(1); } gcount = data->param.ids->len - 2; /* * If no groups are passed in, then use the local * group mapping. */ if (gcount > 0) { if (gcount > NGROUPS_MAX) gcount = NGROUPS_MAX; for (i = 0; i < gcount; ++i) groups[i] = data->param.ids->vec[i + 2]; } debug("Changing gid to %d", gid); if (setgid(gid) < 0) { send_error(data, "Can't change group ID to %d", gid); exit(1); } if (gcount > 0) { debug("Setting %d supplementary gids", gcount); if (setgroups(gcount, groups) < 0) { send_error(data, "Can't set supplementary group IDs"); #ifndef DEBUG exit(1); #endif } } else { debug("Using groups of user"); } debug("Changing uid to %d", uid); if (setuid(uid) < 0) { send_error(data, "Can't change user ID to %d", uid); exit(1); } } /* * Change environment */ { char **env = data->param.environ; if (!env) { env = calloc(1, sizeof (char *)); if (!env) { errno = 0; send_error(data, oom); exit(1); } } environ=env; } if (data->fakerootkey && putenv(data->fakerootkey) < 0) { send_error(data, "Can't put %s to environment", data->fakerootkey); exit(1); } /* * Read resource limits from environment */ if (set_rlimits(data) < 0) exit(1); /* * Set umask */ umask(data->param.umask); /* * Build command and arguments */ argv = data->param.args; if (!argv) { argv = calloc(2, sizeof (char *)); if (!argv) { errno = 0; send_error(data, oom); exit(1); } argv[0] = getenv("SHELL"); if (!argv[0]) argv[0] = DEFAULT_SHELL; } /* * Change current directory */ if (data->param.cwd) { char *dir = add_root_prefix(data, data->param.cwd); debug("Changing current directory to %s", dir); if (chdir(dir) < 0) { send_error(data, "Can't change current directory to %s inside sandbox", dir); exit(1); } } /* hit it! */ debug_vector("Executing command:", argv); set_closeonexec(data->sd); execvp(argv[0], argv); send_error(data, "Can't execute command: %s", argv[0]); exit(1); } /** * Does the handshake & stuff for a client. * @return action or -1 on error */ static int handler_startup(handler_t *data, mount_t ***mounts_ptr) { size_t i, mount_count = 0; ptype_t action = 0; debug("Client IP address is %s", data->host); /* * Version */ debug("Sending VERSION packet"); if (send_version(data->sd) < 0) { error("Can't send protocol version packet"); return -1; } debug("Reading client's VERSION packet"); data->client_version = get_version(data->sd); if (data->client_version < 0) { error("Can't read protocol version packet"); return -1; } if (data->client_version < PROTOCOL_VERSION) { errno = 0; send_error(data, "Client version %d is too old (version %d required)", data->client_version, PROTOCOL_VERSION); return -1; } /* * Username */ debug("Reading USER packet"); if (read_enum(data->sd) != PTYPE_USER) { error("Received packet has unexpected type"); return -1; } data->user = read_str(data->sd); if (!data->user) { error("Can't read USER packet"); return -1; } /* * Authenticate */ { uint16_t auth; int ok = authenticate(data); if (ok < 0) return -1; auth = ok; debug("Sending AUTH packet"); if (write_uint16_packet(data->sd, PTYPE_AUTH, auth) < 0) { error("Can't write AUTH packet to socket"); return -1; } if (!auth) return -1; } /* * Parameter packets */ while (!action) { ptype_t type = read_enum(data->sd); switch (type) { case -1: error("Can't read packet type from socket"); return -1; case PTYPE_TARGET: debug("Receiving TARGET packet"); data->param.target = read_str(data->sd); if (!data->param.target) { error("Can't read TARGET packet"); return -1; } break; case PTYPE_MOUNTS: debug("Receiving MOUNTS packet"); data->param.mounts = read_mountv(data->sd); if (!data->param.mounts) { error("Can't read MOUNTS packet"); return -1; } break; case PTYPE_ARGS: debug("Receiving ARGS packet"); data->param.args = read_strv(data->sd); if (!data->param.args) { error("Can't read ARGS packet"); return -1; } break; case PTYPE_CWD: debug("Receiving CWD packet"); data->param.cwd = read_str(data->sd); if (!data->param.cwd) { error("Can't read CWD packet"); return -1; } break; case PTYPE_ENVIRON: debug("Receiving ENVIRON packet"); data->param.environ = read_strv(data->sd); if (!data->param.environ) { error("Can't read ENVIRON packet"); return -1; } break; case PTYPE_IDS: debug("Receiving IDS packet"); data->param.ids = read_uint16v(data->sd); if (!data->param.ids) { error("Can't read IDS packet"); return -1; } break; case PTYPE_UMASK: debug("Receiving UMASK packet"); if (read_uint16(data->sd, &data->param.umask) < 0) { error("Can't read UMASK packet"); return -1; } break; case PTYPE_WINSIZE: debug("Receiving WINSIZE packet"); data->param.term = read_winsize(data->sd); if (!data->param.term) { error("Can't read WINSIZE packet"); return -1; } break; case PTYPE_COMMAND: debug("Received COMMAND packet"); action = type; break; case PTYPE_MOUNT: debug("Received MOUNT packet"); action = type; break; case PTYPE_UMOUNT: debug("Received UMOUNT packet"); action = type; break; default: errno = 0; send_error(data, "Received packet has unexpected type (0x%02x)", type); return -1; } } if (!data->param.target) { errno = 0; send_error(data, "TARGET parameter required"); return -1; } if (action != PTYPE_COMMAND && !data->param.mounts) { errno = 0; send_error(data, "MOUNTS parameter required"); return -1; } /* * Build path to new root */ if (sandbox) { char root[PATH_MAX]; snprintf(root, sizeof (root) - 1, SANDBOX_ROOT "/%s@%s/%s", data->user, data->host, data->param.target); debug("Sandbox root is %s", root); data->root = strdup(root); if (!data->root) { errno = 0; send_error(data, oom); return -1; } } /* * Count mounts */ if (data->param.mounts) mount_count = calc_vec_len((void **) data->param.mounts); /* * Prepend root path to mount points */ if (mount_count > 0 && data->root) for (i = 0; i < mount_count; ++i) { char tmp[PATH_MAX], *str; mount_info_t *mi; mi = data->param.mounts[i]; snprintf(tmp, sizeof (tmp) - 1, "%s%s", data->root, mi->point); str = strdup(tmp); if (!str) { send_error(data, NULL); return -1; } free(mi->point); mi->point = str; } if (action == PTYPE_UMOUNT) { /* * Unmount target's filesystems */ debug("--umount requested"); if (unmount_infos(data) < 0) return -1; } else if (mount_count > 0) { /* * Mount filesystems */ mount_t **mounts; mntinfo_sort_vec(data->param.mounts); mounts = calloc(mount_count + 1, sizeof (mount_t *)); if (!mounts) { errno = 0; send_error(data, oom); return -1; } for (i = 0; i < mount_count; ++i) if (expire_changed(data, data->param.mounts[i]) < 0) { errno = 0; send_error(data, "Can't expire remote mount point: %s", data->param.mounts[i]->point); return -1; } for (i = 0; i < mount_count; ++i) { mounts[i] = add_mount(data, data->param.mounts[i]); if (!mounts[i]) { errno = 0; send_error(data, "Can't mount to point: %s", data->param.mounts[i]->point); free_vec((void **) mounts, (free_func_t *) release_mount); return -1; } } *mounts_ptr = mounts; } return action; } static void send_rc(handler_t *data, uint16_t rc) { if (data->sd < 0) { debug("Not sending RC packet"); return; } debug("Sending RC packet: %d", rc); /* send the return code to the client */ if (write_uint16_packet(data->sd, PTYPE_RC, rc) >= 0) { fd_set fds; /* do something until the client goes away */ while (1) { FD_ZERO(&fds); FD_SET(data->sd, &fds); if (select(data->sd + 1, &fds, NULL, NULL, NULL) < 0) { if (errno == EINTR) continue; else break; } if (read(data->sd, data->tmp_buf, BUFFER_SIZE) <= 0) break; } } else { error("Can't write RC packet to socket"); } close(data->sd); data->sd = -1; } static void handler_handle(handler_t *data) { int rc = -1; pid_t relay_pid, pid; relay_pid = fakeroot_relay(data); if (relay_pid < 0) goto _rc; if (data->param.term) { int fd; debug("Creating command process in a pty"); pid = fork_pty(data, &fd); data->in.fd = fd; data->out.fd = fd; data->err.fd = -1; } else { debug("Creating command process without a pty"); pid = fork_sockets(data, &data->in.fd, &data->out.fd, &data->err.fd); } /* child? */ if (pid == 0) execute_command(data); /* never returns */ if (pid > 0) rc = handler_manage(data, pid); if (relay_pid > 0) { debug("Sending SIGTERM to relay process %d", relay_pid); kill(relay_pid, SIGTERM); } _rc: if (rc < 0) rc = INTERNAL_ERROR_CODE; send_rc(data, rc); } static void release_mounts(mount_t **vec) { while (*vec) release_mount(*vec++); } static handler_t *alloc_handler(void) { handler_t *h = calloc(1, sizeof (handler_t)); if (h) { h->sd = -1; h->in.req_type = PTYPE_IN_REQ; h->out.data_type = PTYPE_OUT_DATA; h->err.data_type = PTYPE_ERR_DATA; h->error = 0; } return h; } /** * Frees all resources used by a handler. */ static void free_handler(handler_t *data) { if (data->sd >= 0) close(data->sd); if (data->user) free((char *) data->user); if (data->param.target) free(data->param.target); if (data->param.mounts) free_vec((void **) data->param.mounts, (free_func_t *) mntinfo_free); if (data->param.args) free_vec((void **) data->param.args, NULL); if (data->param.cwd) free(data->param.cwd); if (data->param.environ) free_vec((void **) data->param.environ, NULL); if (data->param.ids) uint16v_free(data->param.ids); if (data->root) free(data->root); if (data->in.buf) buf_free(data->in.buf); if (data->fakerootkey) free(data->fakerootkey); free(data); } static void pid_mounts_add(pid_t pid, mount_t **vec) { pid_mounts_t *pm, *node; /* allocate */ pm = calloc(1, sizeof (pid_mounts_t)); if (!pm) { oom_error(); return; } pm->pid = pid; pm->mounts = vec; /* append */ if (pid_mounts == NULL) { pid_mounts = pm; } else { for (node = pid_mounts; node->next; node = node->next) ; node->next = pm; } } static void pid_mounts_del(pid_t pid) { pid_mounts_t *pm = NULL, *node; if (!pid_mounts) return; /* find and remove */ if (pid_mounts->pid == pid) { pm = pid_mounts; pid_mounts = pm->next; } else { for (node = pid_mounts; node->next; node = node->next) if (node->next->pid == pid) { pm = node->next; node->next = pm->next; break; } } if (!pm) return; debug("Found mounts for pid %d", pid); /* deallocate */ if (pm->mounts) { release_mounts(pm->mounts); free(pm->mounts); } free(pm); } static void accept_conn(int srvsd) { struct sockaddr_storage addr; socklen_t len = sizeof (addr); int clisd; handler_t *data; mount_t **mounts = NULL; int action; pid_t pid; clisd = accept(srvsd, (struct sockaddr *) &addr, &len); if (clisd < 0) { error("Can't accept connection"); return; } debug("New connection"); data = alloc_handler(); if (!data) { oom_error(); close(clisd); return; } data->sd = clisd; /* get the IP address */ if (getnameinfo((struct sockaddr *) &addr, len, data->host, sizeof (data->host), NULL, 0, NI_NUMERICHOST) < 0) { error("Can't get client's IP address"); goto _error; } action = handler_startup(data, &mounts); if (action < 0) goto _error; if (action == PTYPE_COMMAND) { /* start handler process */ pid = fork(); if (pid < 0) { error("Can't fork"); goto _error; } /* child? */ if (pid == 0) { set_debug_name("HANDLER"); close(srvsd); handler_handle(data); debug("Handler process exiting"); exit(0); /* not reached */ } /* map pid to mounts vector */ if (mounts) pid_mounts_add(pid, mounts); free_handler(data); } else { send_rc(data, 0); free_handler(data); free_vec((void **) mounts, (free_func_t *) release_mount); } return; _error: send_rc(data, INTERNAL_ERROR_CODE); free_handler(data); if (mounts) free(mounts); } /** * Unmounts all filesystems that we've mounted and exits. */ static void clean_exit(int rc) { unmount_all(); debug("sbrshd exiting"); exit(rc); } static void sig_dummy(int sig) { #ifdef DEBUG int stored_errno = errno; debug(strsignal(sig)); errno = stored_errno; #endif } /* * Exits. If invoked on the DAEMON process, then it also unmounts everything. */ static void sig_exit(int sig) { debug(strsignal(sig)); if (getpid() == daemon_pid) clean_exit(0); exit(0); } static void sig_debug(int sig) { int stored_errno = errno; debug(strsignal(sig)); if (getpid() != daemon_pid) goto _ret; if (sig == SIGUSR1) open_debug_log(); else if (sig == SIGUSR2) close_debug_log(); _ret: errno = stored_errno; } static char *get_absolute_path(const char *progname, char *relpath) { char *tmp1, *tmp2, *dir, *file, *abspath; tmp1 = relpath; tmp2 = strdup(relpath); if (!tmp2) { errno = 0; error_err(progname, oom); exit(1); } dir = dirname(tmp1); file = basename(tmp2); abspath = malloc(PATH_MAX + 1); if (!abspath) { errno = 0; error_err(progname, oom); exit(1); } abspath = realpath(dir, abspath); if (!abspath) { error_err(progname, "Can't get real path of %s", dir); exit(1); } if (strlen(abspath) + 1 + strlen(file) > PATH_MAX) { error_err(progname, "Path is too long: %s/%s", abspath, file); exit(1); } strcat(abspath, "/"); strcat(abspath, file); free(tmp2); return abspath; } static void usage(char *progname) { fprintf(stderr, "Usage: %s [-p|--port ]" " [-l|--local-only]" " [-n|--no-sandbox]" " [-r|--allow-root]" " [-d|--debug ]" " [-e|--mount-expiration |none]" " [-m|--mount-bin ]" " [-u|--umount-bin ]" " [-S|--sshfs-bin ]" " [-F|--fusermount-bin ]" " [-t|--mount-tab ]" " [-b|--bind-opt ]\n" " %s add
\n" " %s -v|--version\n" " %s -h|--help\n", progname, progname, progname, progname); exit(0); } /** * Reads options. Prints usage and exits when necessary. */ static void read_args(char *progname, int argc, char **argv) { const char *const optstring = "hvp:lnird:Ve:m:u:b:"; struct option longopts[] = { { "help", no_argument, 0, 'h' }, { "version", no_argument, 0, 'v' }, { "port", required_argument, 0, 'p' }, { "local-only", no_argument, 0, 'l' }, { "no-sandbox", no_argument, 0, 'n' }, { "allow-root", no_argument, 0, 'r' }, { "debug", required_argument, 0, 'd' }, { "debug-verbose", no_argument, 0, 'V' }, { "mount-expiration", required_argument, 0, 'e' }, { "mount-bin", required_argument, 0, 'm' }, { "umount-bin", required_argument, 0, 'u' }, { "sshfs-bin", required_argument, 0, 'S' }, { "fusermount-bin", required_argument, 0, 'F' }, { "bind-opt", required_argument, 0, 'b' }, { 0 } }; char *debugname = NULL; char *exp_str = NULL; bool_t default_bind_opt = TRUE; while (1) { int c = getopt_long(argc, argv, optstring, longopts, NULL); if (c < 0) break; switch (c) { case 'p': port = optarg; break; case 'l': local_only = TRUE; break; case 'n': sandbox = FALSE; break; case 'r': allow_root = TRUE; break; case 'd': debugname = optarg; break; case 'V': debug_verbose_flag = TRUE; break; case 'e': exp_str = optarg; break; case 'm': mount_cmd = optarg; break; case 'u': umount_cmd = optarg; break; case 'S': sshfs_cmd = optarg; break; case 'F': fusermount_cmd = optarg; break; case 'b': bind_opt = optarg; default_bind_opt = FALSE; break; case 'v': fprintf(stderr, "Scratchbox Remote Shell daemon %d%s\n", PROTOCOL_VERSION, REVISION); exit(0); case 'h': case '?': default: usage(progname); } } if (exp_str) { if (strcmp(exp_str, MOUNT_EXPIRATION_NONE) == 0) { mount_expiration = -1; } else if (strcmp(exp_str, "0") == 0) { mount_expiration = 0; } else { int i = atoi(exp_str); if (i <= 0) { error_err(progname, "Invalid expiration time: %s minutes", exp_str); exit(1); } mount_expiration = i * 60; } } /* we need the absolute path since we chdir to root */ if (debugname) debug_filename = get_absolute_path(progname, debugname); if (default_bind_opt) check_for_busybox(progname); } /** * Do stuff, close fds and direct in/out/err to /dev/null or debug log. * To make this function sufficiently odd, setsid() will not be called. * @param listenfd this descriptor won't be closed */ int daemonize(int listenfd) { int debugfd, fd; chdir("/"); umask(0); /* Don't close debug file */ debugfd = debug_file ? fileno(debug_file) : -1; for (fd = getdtablesize(); fd-- > 0; ) if (fd != listenfd && fd != debugfd) close(fd); fd = open(NULL_FILE, O_RDWR); if (fd < 0) { error("Can't open " NULL_FILE); return -1; } assert(fd == STDIN_FILENO); if (debugfd >= 0) fd = debugfd; if (dup2(fd, STDOUT_FILENO) != STDOUT_FILENO) { error("Can't duplicate descriptor %d as stdout", fd); return -1; } if (dup2(fd, STDERR_FILENO) != STDERR_FILENO) { error("Can't duplicate descriptor %d as stderr", fd); return -1; } return 0; } /* * Startup and main loop. */ int main(int argc, char **argv) { char *progname; int srvsd; struct sigaction act_dummy, act_exit, act_debug; pid_t pid; unsigned int timeout = 0; struct timeval tv; progname = get_progname(argv[0]); /* add lines to config file */ if (argc >= 2 && strcmp(argv[1], "add") == 0) { char *host; if (argc != 3) { usage(progname); return 1; } host = argv[2]; if (strlen(host) == 0 || strchr(host, '@') != NULL) { usage(progname); return 1; } return -add_to_config(host); } /* read config */ openlog(progname, LOG_PID, LOG_DAEMON); read_args(progname, argc, argv); /* create a socket, bind it and listen to it */ { struct addrinfo *ai, hints = { 0 }; hints.ai_flags = AI_ADDRCONFIG; hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; if (!local_only) hints.ai_flags |= AI_PASSIVE; if (getaddrinfo(NULL, port, &hints, &ai) < 0) { error_err(progname, "Can't get address info"); return 1; } srvsd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (srvsd < 0) { error_err(progname, "Can't create socket"); return 1; } if (setsockopt_bool(srvsd, SOL_SOCKET, SO_REUSEADDR, TRUE) < 0) { error_err(progname, "Can't set socket option (SO_REUSEADDR)"); return 1; } if (bind(srvsd, ai->ai_addr, ai->ai_addrlen) < 0) { error_err(progname, "Can't bind socket"); return 1; } freeaddrinfo(ai); if (listen(srvsd, SOMAXCONN) < 0) { error_err(progname, "Can't listen with socket"); return 1; } } /* daemonize */ #ifndef DEBUG pid = fork(); if (pid < 0) { error_err(progname, "Can't fork"); return 1; } if (pid > 0) { printf("%d\n", pid); return 0; } #endif set_debug_name("DAEMON"); if (debug_filename) open_debug_log(); setsid(); if (daemonize(srvsd) < 0) return 1; /* signal handlers */ daemon_pid = getpid(); act_dummy.sa_handler = sig_dummy; sigemptyset(&act_dummy.sa_mask); act_dummy.sa_flags = 0; act_exit.sa_handler = sig_exit; sigemptyset(&act_exit.sa_mask); act_exit.sa_flags = SA_ONESHOT; act_debug.sa_handler = sig_debug; sigemptyset(&act_debug.sa_mask); act_debug.sa_flags = 0; #ifdef DEBUG sigaction(SIGINT, &act_exit, NULL); #else sigaction(SIGHUP, &act_dummy, NULL); #endif sigaction(SIGTERM, &act_exit, NULL); sigaction(SIGCHLD, &act_dummy, NULL); sigaction(SIGPIPE, &act_dummy, NULL); sigaction(SIGUSR1, &act_debug, NULL); sigaction(SIGUSR2, &act_debug, NULL); /* mount expiration */ if (mount_expiration >= 0) { timeout = mount_expiration / MOUNT_EXPIRATION_FREQUENCY; tv.tv_sec = timeout; tv.tv_usec = 0; } while (1) { fd_set fds; int count; debug("Waiting for connection"); FD_ZERO(&fds); FD_SET(srvsd, &fds); count = select(srvsd + 1, &fds, NULL, NULL, mount_expiration > 0 ? &tv : NULL); if (count < 0) { /* failed? */ if (errno != EINTR) { error("Select failed"); clean_exit(1); } /* we received a signal */ while (1) { int status; pid = waitpid(-1, &status, WNOHANG); if (pid <= 0) break; /* release mounts if found */ print_status(pid, status); pid_mounts_del(pid); } } else if (count == 1) { /* we received a connection */ accept_conn(srvsd); } /* expire mounts */ if (mount_expiration >= 0) { expire_mounts(); tv.tv_sec = timeout; tv.tv_usec = 0; } } return 0; /* not reached */ } sbrsh-7.6.1/daemon.h0000644000232200023220000000741711013052103014443 0ustar pbuilderpbuilder/* * Copyright (c) 2003, 2004, 2005 Nokia * Author: Timo Savola * * This program is licensed under GPL (see COPYING for details) */ #ifndef DAEMON_H #define DAEMON_H #include "types.h" #include "protocol.h" #include "buffer.h" #include "mount.h" #include #include #include /** Name of the system-wide config file. */ #define CONFIG_NAME "/etc/sbrshd.conf" /** Location of the sandbox directories. */ #define SANDBOX_ROOT "/var/sbrshd" /** The permissions used when creating directories for mount points. */ #define MKDIR_PERMS 0750 /** Default path to the mount binary. */ #define DEFAULT_MOUNT_CMD "/bin/mount" /** Default path to the umount binary. */ #define DEFAULT_UMOUNT_CMD "/bin/umount" /** Default path to FUSE fusermount binary. */ #define DEFAULT_FUSERMOUNT_CMD "/usr/bin/fusermount" /** Default path to SSHFS binary. */ #define DEFAULT_SSHFS_CMD "/usr/bin/sshfs" /** Default option used with MTYPE_BIND. */ #define DEFAULT_BIND_OPT "--bind" /** Default option used with MTYPE_BIND when using Busybox. */ #define DEFAULT_BIND_OPT_BUSYBOX "-obind" /** Default for how many seconds until a mount expires? */ #define DEFAULT_MOUNT_EXPIRATION (15 * 60) /** The string which means that mounts should never expire. */ #define MOUNT_EXPIRATION_NONE "none" /** How many times per expiration interval should the mounts be checked? */ #define MOUNT_EXPIRATION_FREQUENCY 6 /** SBOX_RLIMIT-variable string value that maps to RLIM_INFINITY. */ #define ENV_RLIMIT_UNLIMITED "unlimited" /** Prefix for variables that set a resource limit. */ #define ENV_RLIMIT_PREFIX "SBOX_RLIMIT_" /** Where stdin/out/err can be safely redirected. */ #define NULL_FILE "/dev/null" /* %d will be replaced by port number */ #define DEFAULT_DEBUG_FILENAME_FMT "/tmp/sbrshd-%s.log" /* Used when no command specified and no $SHELL set. */ #define DEFAULT_SHELL "/bin/sh" struct pid_mounts_s; #define output_desc_is_stdout(od_p) ((od_p)->data_type == PTYPE_OUT_DATA) typedef struct { /** File descriptor */ int fd; /** Are we waiting for DATA? */ bool_t wait; /** Used for buffering DATA received from the client. */ buffer_t *buf; /** Type of the REQ packet */ ptype_t req_type; } input_desc_t; typedef struct { /** File descriptor */ int fd; /** How much DATA has the client requested? */ size_t req; /** Type of the DATA packet */ ptype_t data_type; } output_desc_t; /** Handler process data. */ typedef struct { /** Socket connection to the client. */ int sd; /** The protocol version of the client. */ int client_version; /** Host name of the client. */ char host[NI_MAXHOST]; /** Username at the client end. */ const char *user; /** Parameters from the client. */ struct { char *target; mount_info_t **mounts; char **args; char *cwd; char **environ; uint16v_t *ids; uint16_t umask; struct winsize *term; } param; /** Root directory. */ char *root; /** The mount points used by this client. */ mount_t **mounts; input_desc_t in; output_desc_t out; output_desc_t err; /** Used for many things... */ char tmp_buf[BUFFER_SIZE]; /** FAKEROOTKEY environment string. */ char *fakerootkey; /** An error occured and handling should be aborted. */ bool_t error; } handler_t; /** An entry in a list of pid to mount vector mappings. */ typedef struct pid_mounts_s { struct pid_mounts_s *next; pid_t pid; mount_t **mounts; } pid_mounts_t; #define debug if (debug_file) print_debug extern FILE *debug_file; void set_debug_name(const char *name); void print_debug(const char *msg, ...); void send_error(handler_t *, const char *msg, ...); int daemonize(int listenfd); #endif sbrsh-7.6.1/debian/0000755000232200023220000000000011155152734014262 5ustar pbuilderpbuildersbrsh-7.6.1/debian/changelog0000644000232200023220000001544311155152636016144 0ustar pbuilderpbuildersbrsh (7.6.1) unstable; urgency=low * Add limits.h include, closes: #518849 * -- Riku Voipio Mon, 09 Mar 2009 10:35:40 +0200 sbrsh (7.6) unstable; urgency=low [ Riku Voipio ] * Upload to Debian [ Timo Savola ] * Revision 6 * simpler build scripts * Fix mount point config parsing * Fix mount/umount binary configuration check * Hack to make sshfs mounting work * Add mount/umount binary configuration options to scripts * Change uid and gid separately * Mount sshfs as normal user * Initial sshfs work [ Ed Swartz ] * Force bind mounts to expire immediately * added -r|--remote option to alter user identity on target * added --debug-verbose option to optionally enable stdin/stdout/poll logging [ Janne Kataja ] * Fix the bug Timo added to 179b3ed1dbea29c24969b6c7cc1c10a4f943b78c * Check if the configuration of existing mount points has changed and try to * Fix sshfs unmounting * Changed do_umount(...) function arguments to detect SSHFS mounts. -- Riku Voipio Thu, 15 May 2008 18:32:19 +0300 sbrsh (7.5.2) unstable; urgency=low * replace ENODATA with EIO to make sbrsh compile on freebsd -- Riku Voipio Tue, 20 Nov 2007 15:26:11 +0200 sbrsh (7.5.1) unstable; urgency=low * Add url to copyright and format it more like other packages. -- Riku Voipio Sun, 18 Nov 2007 20:16:03 +0200 sbrsh (7.5) unstable; urgency=low * Initial upload to debian Closes: #451560 * Include defines from fakeroot * Split packages for client and server Timo Savola: * Fix interrupted read/write handling in fakeroot relay * Fix EINTR check in mount command output processing * Remove the pointless username matching * Remove the pointless ident support * Remove obsolete SBOX_ENV_ documentation * Do not build sbrsh statically * Add ENABLE and DEBUG_FILE options to init script * Evaluate /etc/default/sbrshd in init script * Install example config files -- Riku Voipio Sun, 18 Nov 2007 13:18:35 +0200 sbrsh (7.4) unstable; urgency=low * debian: fix bashism in postrm script * daemon: check supplementary groups for root gid * client: do not drop gids from supplementary groups list -- Timo Savola Fri, 27 Oct 2006 21:05:33 +0300 sbrsh (7.3) unstable; urgency=low * daemon: Fix supplementary groups setting (#284). * Environment translation removed. Toni Timonen: * Support the new CPU-transparency calling convention. * Build sbrsh statically. * Check the existence of stdin before using it. This way it is possible to run binaries through sbrsh from scripts. -- Timo Savola Fri, 27 Oct 2006 10:58:22 +0300 sbrsh (7.0) unstable; urgency=low * Moved environment handling to client side, which now uses libsb_env. -- Toni Timonen Thu, 1 Sep 2005 13:22:31 +0300 sbrsh (6.9) unstable; urgency=low * mount.c: Fixed nfs mount recognition to not fail with sfs. * Daemon: Enchanced busybox regognition (eg. with familiar). -- Toni Timonen Thu, 25 Aug 2005 10:19:16 +0300 sbrsh (6.8) unstable; urgency=low * Fixes interrupt handling in network I/O functions (bug #190) * Client: Expect RC packet from server during authentication Thanks to Rabeeh Khoury for help. -- Timo Savola Wed, 11 May 2005 22:52:54 +0300 sbrsh (6.7) unstable; urgency=low * daemon.c (execute): Close pipe to command process when finished. * protocol.c (read_buf): Return error when buffer could not be filled due to closed socket. * client.c (main): Print more informative error message when passwd entry for uid could not be found. -- Timo Savola Thu, 17 Feb 2005 20:08:21 +0200 sbrsh (6.6) unstable; urgency=low * sbrshd.init: Print "done." after enabling/disabling debug log. -- Timo Savola Fri, 4 Feb 2005 15:13:44 +0200 sbrsh (6.5) unstable; urgency=low * debian/control: Do not depend on nfs-common. * debian/rules: Use LDFLAGS=-s instead of dh_strip to make the package cross-compile without Scratchbox. -- Timo Savola Wed, 2 Feb 2005 08:06:39 +0200 sbrsh (6.4) unstable; urgency=low * client.c: Use _SBOX_NONFAKE_UID and _SBOX_NONFAKE_GID variables when run in a fakeroot session. Print error message when authentication failed (the server no longer sends us an error message in that case). * daemon.c: Partial support for wildcards in the IP addresses in sbrshd.conf. * README: Documented new wildcard support in sbrshd.conf. * sbrshd.init, debian/rules, Makefile (ipk): sbrshd binary is installed to /usr/sbin instead of /sbin. * debian/sbrshd.postinst: Creates empty /etc/sbrshd.conf. * debian/sbrshd.postrm: Deletes /etc/sbrshd.conf when purging. * debian/copyright: Contains proper copyright information and a pointer to the GPL text. * debian/rules: Uses dh_clean. * debian/changelog: Recreated release history from CVS and Scratchbox's ChangeLog for documentation purposes. -- Timo Savola Tue, 1 Feb 2005 15:21:52 +0200 sbrsh (6.3) unstable; urgency=low * Fixes the mount problem correctly. -- Timo Savola Mon, 31 Jan 2005 18:14:04 +0200 sbrsh (6.2) unstable; urgency=low * Fixes a segmentation fault in the fakeroot relay. * Uses real path of mount points so that mounting and unmounting works with paths that have symlinks. * 'sbrshd add' does not fail if /etc/sbrshd.conf does not exist. * Minor sbrshd.conf parsing fix. -- Timo Savola Wed, 29 Dec 2004 23:06:47 +0200 sbrsh (6.1) unstable; urgency=low * Users can be granted access using syntax "sbrshd add @
". -- Timo Savola Wed, 22 Dec 2004 16:00:17 +0200 sbrsh (6.0) unstable; urgency=low * User accounts no longer needed at the daemon end. * The daemon uses a single configuration file: /etc/sbrshd.conf. * The sandbox directories are created under /var/sbrshd. * Supports a "sandboxless" mode where the commands are executed in the system environment. -- Timo Savola Mon, 20 Dec 2004 18:02:17 +0200 sbrsh (5.0) unstable; urgency=low * New version numbering scheme: the major version number corresponds to the protocol version (which was also incremented). * The plain-text password is no longer used. * The '--ident' option causes the daemon to do identd lookup to validate incoming connection. * umask is set for the executed command according to the client environment. * Daemon longer passes its version to the executed command via environment. * "SBRSH_RLIMIT_" prefix changed to "SBOX_RLIMIT_" in environment variables. -- Timo Savola Mon, 18 Oct 2004 18:23:09 +0300 sbrsh-7.6.1/debian/compat0000644000232200023220000000000211013052103015436 0ustar pbuilderpbuilder5 sbrsh-7.6.1/debian/control0000644000232200023220000000132411155152704015662 0ustar pbuilderpbuilderSource: sbrsh Section: net Priority: extra Maintainer: Riku Voipio Build-Depends: debhelper (>= 5) Standards-Version: 3.8.0 Package: sbrshd Architecture: any Depends: ${shlibs:Depends} Description: Scratchbox Remote Shell daemon sbrshd mounts nfs partition from the host and executes a binary on it. This used to provide cpu transparency for cross-compiling with scratchbox. . http://scratchbox.org/ Package: sbrsh Architecture: any Depends: ${shlibs:Depends} Description: Scratchbox Remote Shell client sbrsh requests a sbrshd host to mount a nfs partition, and executes a binary on it. This used to provide cpu transparency for cross-compiling with scratchbox. . http://scratchbox.org/ sbrsh-7.6.1/debian/copyright0000644000232200023220000000221111013052103016167 0ustar pbuilderpbuilderThis package was debianized by Riku Voipio on su 18.11.2007 20:03:35 +0200. The current Debian maintainer is Riku Voipio It was downloaded from: http://timo.stc.cx/git/?p=sbrsh.git Upstream Authors: Timo Savola Copyright: 2003 - 2006 Nokia This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. On Debian systems, the complete text of the GNU General Public License, version 2, can be found in /usr/share/common-licenses/GPL-2. The Debian packaging is part of the upstream and has the same copyright and license. sbrsh-7.6.1/debian/rules0000755000232200023220000000200611155152734015340 0ustar pbuilderpbuilder#!/usr/bin/make -f export LDFLAGS += -s export PREFIX := /usr build: sbrshd sbrsh sbrshd: $(MAKE) sbrshd sbrsh: $(MAKE) sbrsh binary: binary-arch binary-arch: build dh_testdir dh_testroot $(MAKE) install DESTDIR=debian/tmp cp sbrshd.init debian/ mkdir -p debian/sbrshd/usr/sbin mv debian/tmp/usr/sbin/sbrshd debian/sbrshd/usr/sbin/ mkdir -p debian/sbrshd/etc cp -a sbrshd.conf debian/sbrshd/etc/ mkdir -p debian/sbrshd/usr/share/doc/sbrshd cp -a README debian/sbrshd/usr/share/doc/sbrshd/ mkdir -p debian/sbrsh/usr/bin cp -a debian/tmp/usr/bin/sbrsh debian/sbrsh/usr/bin/ mkdir -p debian/sbrsh/usr/share/doc/sbrsh cp -a README debian/sbrsh/usr/share/doc/sbrsh/ dh_installexamples dh_installchangelogs dh_installdocs dh_installinit dh_shlibdeps dh_compress dh_fixperms dh_installdeb dh_gencontrol dh_md5sums dh_builddeb binary-indep: clean: dh_testdir dh_testroot dh_clean $(MAKE) clean rm -f debian/sbrshd.init rm -rf debian/tmp .PHONY: build binary binary-arch binary-indep clean sbrsh-7.6.1/debian/sbrsh.examples0000644000232200023220000000001311013052103017113 0ustar pbuilderpbuildersbrsh.conf sbrsh-7.6.1/debian/sbrshd.default0000644000232200023220000000201311013052103017067 0ustar pbuilderpbuilder# Set this to true to enable sbrshd. Please note that sbrshd should NOT be # enabled on a host which is connected to a public network as it is insecure! # ENABLE=false # TCP port to listen to. # #PORT=1202 # Accept only local connections. (For example when using an SSH tunnel.) # #LOCAL_ONLY=false # Do not chroot: mount filesystems and run programs in the host environment. # #NO_SANDBOX=false # Allow programs to be run with root privileges. # #ALLOW_ROOT=false # Enable debug logging. # #DEBUG=false # Specify the debug log filename. # DEBUG_FILE=/var/log/sbrshd.log # Timeout in minutes for unmounting filesystems. "none" disables the # unmounting. (Filesystems will always be unmounted at sbrshd shutdown.) # #EXPIRATION=15 # Specifies the mount and umount binaries paths. For example when busybox # mount doesn't have support for external mount helpers. # #MOUNT_BIN=/bin/mount #UMOUNT_BIN=/bin/umount # Specifies FUSE fusermount and sshfs binaries. # #SSHFS_BIN=/usr/bin/sshfs #FUSERMOUNT_BIN=/usr/bin/fusermount sbrsh-7.6.1/debian/sbrshd.postinst0000644000232200023220000000004711013052103017333 0ustar pbuilderpbuilder#!/bin/sh set -e #DEBHELPER# exit 0 sbrsh-7.6.1/debian/sbrshd.postrm0000644000232200023220000000013311013052103016770 0ustar pbuilderpbuilder#!/bin/sh set -e #DEBHELPER# if [ $1 = purge ]; then rm -f /etc/sbrshd.conf fi exit 0 sbrsh-7.6.1/debian/sbrshd.prerm0000644000232200023220000000004711013052103016575 0ustar pbuilderpbuilder#!/bin/sh set -e #DEBHELPER# exit 0 sbrsh-7.6.1/debug.mk0000644000232200023220000000004211013052103014431 0ustar pbuilderpbuilderCPPFLAGS += -DDEBUG CFLAGS += -g sbrsh-7.6.1/doc/0000755000232200023220000000000011013052103013563 5ustar pbuilderpbuildersbrsh-7.6.1/doc/fakeroot.dia0000644000232200023220000000337711013052103016066 0ustar pbuilderpbuilder‹í\Koã6¾çWÞK xe‘%j³Éb·@ÑC‹íölÐc«+K†D'ñ¥¿½$¥ÄÖË‘õ`í…ÄHdj†¤f¾o†êã§çM =Ò8ñ£ðntc¢Ñpy~¸º›üýõç÷xòéþæ£ç“üw“ÆïñßÝdÍØöÃlöôô¤û„°(Ö§'tö/ 2ãf“ûM;àFĵì*a,ö;FµlèÝdA–ßVq´ ½IÚ*k·Œ‚(ÖIp7y÷ &³LÌ,'ç„ì-YÑELÉ·zÑKÛƒl#zKã¢ØÍ6J|Þ„í·¥&5rÄçQ›¬UÂ…«ûwŸ­wi—² YU­UÂ6$^ùaYŸ› ¨c_çá|‹áU눇Wá'óm³˜ø¬¬fE%aª‰Å;Ú^O²$7™SCí¥?øŒEoôÿI“¤—_½ç\O\žwÚs-j¤<ù[ÏŸOM—¡£ö–Êß&ÿÑOüE@«Fà‡¬óó~¿ïG|ñ ID>ÜÞÑ"Riµï¹Qt>ä¯v¾G“7L-ߦFÒ:k6{kÖ‹íšNLz©@ÀRE@ö4ÎÄ9P¯–=à uŽfA4ØæÆ-þ¡K– ø/FBÄžö^û=O±,|ïnò»ñÖdpq“²am£ƒiá©ÕÞx…†Å¢Êõ–¼÷«€fJÝES“ÜšHGh lþÙ^- èf¾Œâ0ô=8©E¢ËI¨wô®#YSµf'•À.J’uô4¯ˆ‡ Çô©Ìcâù»¤+E¦—S¯hä(_é3«ðЇ§@n³S4¸¯@KGM-Ý„ØA·ë–kÃ)ÒMvÐÎÄÌ”tA¶ÔªÎ¢dÐ\hYˆ°¿ò€2í‡ÏþöcM¬}rõR²jÕâílü`ÏûHÂd¢%l/°—[Bzó/4x¤Ì_’ã©l× ·­Œ=pwUEÛlfŸítå9¾`.®7äOwu<–^…Z÷Hi¸ÛTƺ*Ùü, I¯Qt/” û"kjŽB‚«§P6§jƒ¶£„°; ­!_ÃáÙÚÙº[›½Äµ†‚¸Kª63ª ¥jëê¨ú—(áDýÇO#O+àé¢eŽ,}i,mõ’.8SgødÁpHźž%˜Ú’5’5PÂÔ†Ö0£6çi{äé–<íôâíÁ=DhA‚¥,m,=²t–.çHÒ—FÒ¸—=W,h š†éÂ7RBÓÝ×0¡œ¦Ý [õî‹DÝ~ìWd¡Ã[°Pc¿ì[ºÉï•-I'‹8Yk¡›(YTÁªtÉ:Gí›FëPçWž5TU­ùЏ·A§b«€b[¹ûЦ k„ÍCæ´†øÓÌqå9ê–QÎièÉΟ1¤êf»NpcYn‹º«Š0ÏZ™Ö'„çˆæó5'q=ÕK6;Jž4\åŠS¥µíT—¶„ qûä>””Šb_¾ÑÖÜë2Hg‘\?ÒßjîÙ½åoårÒB*¢ 0€ÎÀ`MqÛ×8¸†!VÝ]×Aà–Ó‹áòÈš#†-wh( ¯2Þv/$Fb6/ÉΛ•ôåÏ#ÜõwÀÍC–¥ ²`WÈ‚ŽX2³x²„1¶„h9Î-tx8ƒðT.GBs`Ì*¯¢‰Ü5pÐÝŒîÞ0Bqþ'w7;»»==ÞWêßÙ‘ŽM¦ eöÀv±Ìf\íëùÁU4=Ac„2F(×Y=ˆÕDŒiþäÔyŽ„®Æ2]ëºa¶¨Ã³àHq|•©°Æ€aô¾Î›x —’?;·:7X%‹„gˆ…IKîâÙJvñº ®Ù.|Ï¼Ž³+—Wkz)FÇøÀðgWìtÁÞ”çV\AXWZn3îªÚ#,[æ¸Gxi¥6 —‚?Xº:RQ« ÷,t Ó¢XEÇW:¯S|Eá9Óï®0à~%ÝWð^¡ÉÕñK]všñ•ö¼òvL²i[Á1–*™ûâ˜Ûí'¿vÔ¤×öQvm(Ê®1¹¾ÔYhô•ûb%©¯<Ê \ßqд4vøãeiïx ô²ß«ÔÏÞ°’¬:¯Y¯iÊ—6˜j²^¬$ëUðÚóâÞ¯ÔÂ~ YYVвRèèJ³R.{ßÍH§ ÒQ<¦£CRjú¯|ñýMîuÅ÷7ÿ»P}צ`sbrsh-7.6.1/doc/interaction-command.c0000644000232200023220000000043611013052103017665 0ustar pbuilderpbuilder#include #include #include int main(void) { struct stat buf; /* Read interactive typing */ while (fgetc(stdin) != '\n'); /* Do messaging with fakeroot daemon */ stat("/tmp", &buf); /* Output something */ printf("moikkamoi\n"); return 0; } sbrsh-7.6.1/doc/interaction.txt0000644000232200023220000000433111013052103016644 0ustar pbuilderpbuilderClient Daemon Daemon/handler Daemon/relay Daemon/command ------ ------ -------------- ------------ -------------- Read config file Stat NFS share device numbers Connect Accept connection Allocate handler data Send VERSION Send VERSION Check version Check version Send AUTH INFO Receive AUTH INFO Get user information Read user config file Check password Send AUTH OK Receive AUTH OK Send CMD INFO Set terminal to raw mode Receive CMD INFO Set stdout/stderr non-blocking Create directories Send OUT REQ Mount filesystems Fork handler process Store handler's mounts Fork fakeroot relay process Fork command with PTY Stat NFS mount device numbers Set command stdin non-blocking Change root directory Receive OUT REQ Get user information Send IN REQ Change user and group id Receive IN REQ Validate shell Send IN DATA from stdin Change environment Receive IN DATA to buffer Change current directory Execute command Write buffer to stdin/PTY Send OUT DATA from stdout/PTY Read from stdin Receive OUT DATA to buffer Send IN REQ Write buffer to stdout Receive IN REQ Send OUT REQ Send IN DATA from stdin Receive OUT REQ Receive IN DATA to buffer Write buffer to stdin/PTY Send OUT DATA from stdout/PTY Read from stdin Receive OUT DATA to buffer Send IN REQ Connect to fakeroot relay Write buffer to stdout Accept connection Receive IN REQ Connect to fakeroot daemon Send fakeroot message Send OUT REQ Receive message from command Translate device number Send message to daemon Receive message from daemon Restore device number Send message to command Receive fakeroot message Write to stdout Send OUT DATA from stdout/PTY Exit Receive OUT DATA to buffer Get command return code Command closed connection Write buffer to stdout Terminate relay Close connection to daemon Send RC Terminated Receive RC Exit Restore terminal mode Release handler's mounts Exit with return code sbrsh-7.6.1/fakeroot.c0000644000232200023220000002121311013052103014773 0ustar pbuilderpbuilder/* * Copyright (c) 2003, 2004, 2005 Nokia * Author: Timo Savola * * This program is licensed under GPL (see COPYING for details) */ #include "fakeroot.h" #include "types.h" #include "common.h" #include "daemon.h" #include "mount.h" #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include extern char **environ; typedef enum { DAEMON_TO_CLIENT, CLIENT_TO_DAEMON } direction_t; struct node_s; typedef struct node_s { struct node_s *next; int client; int daemon; fake_dev_t stored_dev; } node_t; static volatile bool_t parent_alive = TRUE; static volatile int listen_sd = -1; static uint32_t relay_id; static void append_node(handler_t *data, node_t **head, int client_sd, int daemon_sd) { node_t *node, *n; node = calloc(1, sizeof (node_t)); if (!node) { errno = 0; send_error(data, oom); exit(1); } node->client = client_sd; node->daemon = daemon_sd; if (*head) { for (n = *head; n->next; n = n->next); n->next = node; } else { *head = node; } } static void remove_node(node_t **head, node_t *node) { node_t *n; if (*head == node) { *head = node->next; } else { for (n = *head; n->next != node; n = n->next); n->next = node->next; } debug("Removing node: client=%d, daemon=%d", node->client, node->daemon); close(node->client); close(node->daemon); free(node); } static void get_device_numbers(handler_t *data) { mount_t **ptr; if (!data->mounts) { debug("No mounts"); return; } for (ptr = data->mounts; *ptr; ptr++) { mount_t *m = *ptr; if (m->info.type != MTYPE_BIND && m->point_dev == 0LL) { struct stat buf; if (stat(m->info.point, &buf) < 0) { send_error(data, "Can't stat %s", m->info.point); exit(1); } m->point_dev = buf.st_dev; debug("Device number of %s is %lld", m->info.device, m->info.device_dev); debug("Device number of %s is %lld", m->info.point, m->point_dev); } } } static bool_t translate_dev(handler_t *data, node_t *node, struct fakestat *st) { mount_t **ptr; const fake_dev_t old = ntohll(st->dev); node->stored_dev = st->dev; if (!data->mounts) { debug("No mounts"); return FALSE; } for (ptr = data->mounts; *ptr; ptr++) { const mount_t *m = *ptr; if (old == m->point_dev) { debug("Translating device number: %lld -> %lld", old, m->info.device_dev); st->dev = htonll(m->info.device_dev); return TRUE; } } debug("No match for device number %lld", old); return FALSE; } static void restore_dev(node_t *node, struct fakestat *st) { debug("Restoring device number"); st->dev = node->stored_dev; } static int copy_msg(handler_t *data, node_t *node, direction_t direction) { int source, destination; struct fake_msg buf; ssize_t len; if (direction == CLIENT_TO_DAEMON) { source = node->client; destination = node->daemon; debug("Processing message from client=%d", source); } else { source = node->daemon; destination = node->client; debug("Processing message from daemon=%d", source); } while (1) { len = read(source, &buf, sizeof (buf)); if (len >= 0) break; if (errno != EINTR) { send_error(data, "Can't read from socket"); exit(1); } } if (len == 0) { debug("No message"); return -1; } if (direction == CLIENT_TO_DAEMON) { if (translate_dev(data, node, &buf.st)) { buf.remote = htonl(0); } else if (ntohl(buf.remote) == 0) { debug("Setting remote ID"); buf.remote = htonl(relay_id); } } else { restore_dev(node, &buf.st); } if (direction == CLIENT_TO_DAEMON) { debug("Forwarding message to daemon=%d", destination); } else { debug("Forwarding message to client=%d", destination); } while (1) { if (write(destination, &buf, sizeof (buf)) >= 0) break; if (errno != EINTR) { send_error(data, "Can't write to socket"); exit(1); } } return 0; } static int get_daemon(handler_t *data, struct sockaddr_in *addr) { int sd; sd = socket(PF_INET, SOCK_STREAM, 0); if (sd < 0) { send_error(data, "Can't create socket"); exit(1); } if (connect(sd, (struct sockaddr *) addr, sizeof (struct sockaddr_in)) < 0) { if (errno == EINTR) { debug("Connect interrupted"); exit(0); } send_error(data, "Connect failed"); exit(1); } debug("Connected to daemon=%d", sd); return sd; } static int get_client(handler_t *data) { struct sockaddr_in addr; socklen_t len = sizeof (addr); int sd; sd = accept(listen_sd, (struct sockaddr *) &addr, &len); if (sd < 0) { if (errno == EINTR) { debug("Accept interrupted"); exit(0); } send_error(data, "Accept failed"); exit(1); } debug("Connection from client=%d", sd); return sd; } static void do_relay(handler_t *data, int port) { struct hostent *host; struct sockaddr_in addr = { 0 }; node_t *sd_head = NULL; fd_set fds; host = gethostbyname(data->host); if (!host) { send_error(data, "Can't resolve host: %s", data->host); exit(1); } addr.sin_family = AF_INET; addr.sin_addr.s_addr = *(unsigned int *) host->h_addr; addr.sin_port = htons(port); debug("Fakeroot daemon assumed to be at %s:%d", data->host, port); while (parent_alive) { int maxfd, count; node_t *n, *next; FD_ZERO(&fds); FD_SET(listen_sd, &fds); maxfd = listen_sd; for (n = sd_head, count = 0; n; n = n->next, count++) { FD_SET(n->daemon, &fds); FD_SET(n->client, &fds); maxfd = MAX(maxfd, n->daemon); maxfd = MAX(maxfd, n->client); } debug("Selecting (%d nodes)", count); if (select(maxfd + 1, &fds, NULL, NULL, NULL) < 0) { if (!parent_alive) { debug("SIGEXIT caught during select call"); break; } if (errno == EINTR) { debug("Select interrupted"); continue; } send_error(data, "Select failed"); exit(1); } for (n = sd_head; n; n = next) { next = n->next; if (FD_ISSET(n->daemon, &fds) && copy_msg(data, n, DAEMON_TO_CLIENT) < 0) { remove_node(&sd_head, n); continue; } if (FD_ISSET(n->client, &fds) && copy_msg(data, n, CLIENT_TO_DAEMON) < 0) { remove_node(&sd_head, n); } } if (FD_ISSET(listen_sd, &fds)) { int cli, dae; cli = get_client(data); dae = get_daemon(data, &addr); append_node(data, &sd_head, cli, dae); } } debug("Exiting"); } static void sig_exit(int sig) { int stored_errno = errno; parent_alive = FALSE; if (listen_sd >= 0) close(listen_sd); errno = stored_errno; } static pid_t fork_relay(handler_t *data, uint16_t *portp) { int faked_port, sd; struct sockaddr_in addr; socklen_t len; uint32_t id; pid_t pid; struct sigaction act_exit; faked_port = *portp; sd = socket(PF_INET, SOCK_STREAM, 0); if (sd < 0) { send_error(data, "Can't create socket"); return -1; } if (setsockopt_bool(sd, SOL_SOCKET, SO_REUSEADDR, TRUE)) { send_error(data, "Can't set socket option: SO_REUSEADDR"); goto _error; } if (listen(sd, SOMAXCONN) < 0) { send_error(data, "Can't listen to socket"); goto _error; } len = sizeof (addr); if (getsockname(sd, (struct sockaddr *) &addr, &len) < 0) { send_error(data, "Can't get name of relay listening socket"); goto _error; } *portp = ntohs(addr.sin_port); len = sizeof (addr); if (getsockname(data->sd, (struct sockaddr *) &addr, &len) < 0) { send_error(data, "Can't get name of client connection socket"); goto _error; } id = ntohl(addr.sin_addr.s_addr); pid = fork(); if (pid < 0) { send_error(data, "Can't fork"); goto _error; } if (pid > 0) { /* Parent: */ close(sd); return pid; } /* Child: */ set_debug_name("RELAY"); debug("Relaying fakeroot messages at port %d", *portp); debug("Remote ID of this relay is 0x%lx", id); daemonize(sd); act_exit.sa_handler = sig_exit; sigemptyset(&act_exit.sa_mask); act_exit.sa_flags = 0; sigaction(SIGHUP, &act_exit, NULL); sigaction(SIGTERM, &act_exit, NULL); get_device_numbers(data); listen_sd = sd; relay_id = id; do_relay(data, faked_port); exit(0); _error: close(sd); return -1; } pid_t fakeroot_relay(handler_t *data) { char **old_environ, *str; uint16_t port; pid_t pid; /* TODO: better. */ old_environ = environ; environ = data->param.environ; str = getenv(FAKEROOTKEY_ENV); environ = old_environ; if (!str || !*str) return 0; debug("Creating relay process"); port = atoi(str); if (port == 0) { send_error(data, "Invalid " FAKEROOTKEY_ENV ": %s", str); return -1; } pid = fork_relay(data, &port); if (pid < 0) return -1; data->fakerootkey = malloc(strlen(FAKEROOTKEY_ENV) + 1 + 5 + 1); if (!data->fakerootkey) { errno = 0; send_error(data, oom); goto _kill; } sprintf(data->fakerootkey, FAKEROOTKEY_ENV "=%d", port); return pid; _kill: kill(pid, SIGTERM); return -1; } sbrsh-7.6.1/fakeroot.h0000644000232200023220000000045311013052103015003 0ustar pbuilderpbuilder/* * Copyright (c) 2003, 2004 Nokia * Author: tsavola@movial.fi * * This program is licensed under GPL (see COPYING for details) */ #ifndef FAKEROOT_H #define FAKEROOT_H #include "daemon.h" #include #include int fakeroot_relay(handler_t *); #endif sbrsh-7.6.1/fakeroot/0000755000232200023220000000000011013052103014630 5ustar pbuilderpbuildersbrsh-7.6.1/fakeroot/config.h0000644000232200023220000000017211013052103016246 0ustar pbuilderpbuilder#define FAKEROOT_ATTR(attr) __attribute__ ((attr)) #define FAKEROOT_FAKENET #define HAVE_INTTYPES_H #define HAVE_STDINT_H sbrsh-7.6.1/fakeroot/message.h0000644000232200023220000000224111013052103016424 0ustar pbuilderpbuilder#ifndef FAKEROOT_MESSAGE_H #define FAKEROOT_MESSAGE_H #ifdef HAVE_STDINT_H #include #else # ifdef HAVE_INTTYPES_H # include # else # error Problem # endif #endif #if __BYTE_ORDER == __BIG_ENDIAN # define htonll(n) (n) # define ntohll(n) (n) #elif __BYTE_ORDER == __LITTLE_ENDIAN # define htonll(n) ((((uint64_t) htonl(n)) << 32LL) | htonl((n) >> 32LL)) # define ntohll(n) ((((uint64_t) ntohl(n)) << 32LL) | ntohl((n) >> 32LL)) #endif #define FAKEROOTKEY_ENV "FAKEROOTKEY" typedef uint32_t func_id_t; typedef uint64_t fake_ino_t; typedef uint64_t fake_dev_t; typedef uint32_t fake_uid_t; typedef uint32_t fake_gid_t; typedef uint32_t fake_mode_t; typedef uint32_t fake_nlink_t; struct fakestat { fake_uid_t uid; fake_gid_t gid; fake_ino_t ino; fake_dev_t dev; fake_dev_t rdev; fake_mode_t mode; fake_nlink_t nlink; } FAKEROOT_ATTR(packed); struct fake_msg { #ifndef FAKEROOT_FAKENET long mtype; /* message type in SYSV message sending */ #endif func_id_t id; /* the requested function */ #ifndef FAKEROOT_FAKENET pid_t pid; int serial; #endif struct fakestat st; uint32_t remote; } FAKEROOT_ATTR(packed); #endif sbrsh-7.6.1/ipkg/0000755000232200023220000000000011013052103013750 5ustar pbuilderpbuildersbrsh-7.6.1/ipkg/control.in0000644000232200023220000000062611013052103015764 0ustar pbuilderpbuilderPackage: sbrshd Section: devel Priority: optional Version: Architecture: arm Maintainer: scratchbox-devel@lists.scratchbox.org Depends: update-rc.d Description: Scratchbox Remote Shell is an rsh/ssh-like utility for Linux that supports terminal emulation, automated mounting of network shares, chroot, etc. See http://www.scratchbox.org/?id=401 for more info. Source: http://www.scratchbox.org . sbrsh-7.6.1/ipkg/postinst0000644000232200023220000000005211013052103015553 0ustar pbuilderpbuilder#!/bin/sh update-rc.d sbrshd defaults 99 sbrsh-7.6.1/ipkg/postrm0000644000232200023220000000004511013052103015216 0ustar pbuilderpbuilder#!/bin/sh update-rc.d sbrshd remove sbrsh-7.6.1/mount.c0000644000232200023220000001350411013052103014327 0ustar pbuilderpbuilder/* * Copyright (c) 2003, 2004, 2005 Nokia * Author: Timo Savola * * This program is licensed under GPL (see COPYING for details) */ #include "mount.h" #include "common.h" #include #include #include #include #include #include mount_info_t *mntinfo_alloc(void) { return calloc(1, sizeof (mount_info_t)); } void mntinfo_free(mount_info_t *mi) { if (mi->opts) free(mi->opts); if (mi->device) free(mi->device); if (mi->point) free(mi->point); free(mi); } int mntinfo_copy(mount_info_t *dest, mount_info_t *src) { memset(dest, 0, sizeof (mount_info_t)); dest->type = src->type; dest->point = strdup(src->point); if (!dest->point) goto _err; dest->device = strdup(src->device); if (!dest->device) goto _err; if (src->opts) { dest->opts = strdup(src->opts); if (!dest->opts) goto _err; } dest->device_dev = src->device_dev; return 0; _err: if (dest->device) free(dest->device); if (dest->point) free(dest->point); oom_error(); return -1; } /** * Returns a newly allocated mount_info_t and sets its fields according * to the input string. */ mount_info_t *mntinfo_parse(const char *input) { mount_info_t *mi; char *buf = NULL, *type, *device, *point, *opts; mi = mntinfo_alloc(); if (!mi) { oom_error(); return NULL; } buf = strdup(input); if (!buf) { oom_error(); goto _err_buf; } split_string(buf, &type, &device, &point, &opts, NULL); if (!type || !device || !point) { error("Invalid mount entry: %s", input); goto _err; } if (strcmp(type, MTYPE_NFS_S) == 0) { mi->type = MTYPE_NFS; } else if (strcmp(type, MTYPE_BIND_S) == 0) { mi->type = MTYPE_BIND; } else if (strcmp(type, MTYPE_SSH_S) == 0) { mi->type = MTYPE_SSH; } else { error("Unknown mount type: %s", type); goto _err; } mi->device = strdup(device); if (!mi->device) { oom_error(); goto _err; } mi->point = strdup(point); if (!mi->point) { oom_error(); goto _err; } if (opts) { mi->opts = strdup(opts); if (!mi->opts) { oom_error(); goto _err; } } free(buf); return mi; _err: free(buf); _err_buf: mntinfo_free(mi); return NULL; } /* * Parse IP address (addr_p) and path (path_p) from NFS mount string (fs). * NFS mount string should be of the form ":". * Returns -1 on error; 0 otherwise. */ static int resolve_nfs(const char *fs, uint32_t *addr_p, char **path_p) { char host[MAXHOSTNAMELEN], *path; uint32_t addr; path = strchr(fs, ':'); if (!path || path[1] == '\0') { error("Invalid NFS filesystem: %s", fs); return -1; } memset(host, '\0', sizeof (host)); strncpy(host, fs, MIN(path - fs, sizeof (host) - 1)); addr = resolve(host); if (!addr) return -1; *addr_p = addr; *path_p = path + 1; return 0; } /* * Check if NFS filesystem is not originating from this host, and try to * find the local mount point if it's originating from a remote host. */ static char *get_local_path(uint32_t r_addr, char *r_path) { FILE *file; char buf[1024]; struct { char nfs_path[PATH_MAX], *path; } sel; sel.path = NULL; file = fopen(MOUNTS_FILE, "r"); if (!file) { error("Can't open " MOUNTS_FILE); return NULL; } while (1) { char *device, *point, *type, *nfs_path; uint32_t nfs_addr; if (read_line(file, buf, sizeof (buf)) < 0) break; split_string(buf, &device, &point, &type, NULL); if (!device || !point || !type || !strchr(device,':') || strcmp(type, "nfs") != 0) continue; if (resolve_nfs(device, &nfs_addr, &nfs_path) < 0) goto _err; if (r_addr != nfs_addr) continue; if (strncmp(r_path, nfs_path, strlen(nfs_path)) != 0) continue; /* addr and nfs_path match */ if (sel.path) { if (strlen(sel.nfs_path) < strlen(nfs_path)) continue; free(sel.path); } sel.path = strdup(point); if (!sel.path) { oom_error(); goto _err; } memset(sel.nfs_path, '\0', sizeof (sel.nfs_path)); strncpy(sel.nfs_path, nfs_path, sizeof (sel.nfs_path) - 1); } fclose(file); if (sel.path) return sel.path; else return r_path; _err: if (sel.path) free(sel.path); fclose(file); return NULL; } static char *scratchbox_path_alias(const char *const path) { const char *const scratchbox = "/scratchbox/"; const size_t scratchbox_len = strlen(scratchbox); char *alias, *renamed; size_t alias_len, renamed_len; alias = getenv("_SBOX_DIR"); if (!alias) return NULL; alias_len = strlen(alias); if (alias_len <= 1 || strncmp(path, alias, alias_len) != 0) return NULL; renamed_len = strlen(path) - alias_len + scratchbox_len + 1; renamed = malloc(renamed_len); if (!renamed) { oom_error(); return NULL; } strcpy(renamed, scratchbox); strcat(renamed, path + alias_len); return renamed; } /** * Fills in the device_dev field of mount_info_t. */ int mntinfo_stat_device(mount_info_t *mi) { char *nfs_path = NULL, *path, *renamed_path; uint32_t nfs_addr; struct stat buf; int rc; if (mi->type == MTYPE_NFS) { if (resolve_nfs(mi->device, &nfs_addr, &nfs_path) < 0) return -1; path = get_local_path(nfs_addr, nfs_path); if (!path) return -1; renamed_path = scratchbox_path_alias(path); if (renamed_path) { if (path != nfs_path) free(path); path = renamed_path; } } else { path = strchr(mi->device, ':'); if (!path) return -1; path++; } rc = stat(path, &buf); if (rc >= 0) mi->device_dev = buf.st_dev; else error("Can't stat %s", path); if (nfs_path && path != nfs_path) free(path); return rc; } /** * Bubble sort based on mount point string. */ void mntinfo_sort_vec(mount_info_t **vec) { mount_info_t **p, *tmp; bool_t touched; if (!vec[0] || !vec[1]) return; do { touched = FALSE; for (p = vec; p[1]; ++p) if (strcmp(p[0]->point, p[1]->point) > 0) { tmp = p[0]; p[0] = p[1]; p[1] = tmp; touched = TRUE; } } while (touched); } sbrsh-7.6.1/mount.h0000644000232200023220000000304411013052103014332 0ustar pbuilderpbuilder/* * Copyright (c) 2003, 2004, 2005 Nokia * Author: Timo Savola * * This program is licensed under GPL (see COPYING for details) */ #ifndef MOUNT_H #define MOUNT_H #include "types.h" #define MOUNTS_FILE "/proc/mounts" typedef enum { /** mount info type: NFS */ MTYPE_NFS = 1, /** mount info type: path should be bound to a mount point */ MTYPE_BIND = 2, /** mount info type: SSH */ MTYPE_SSH = 3, } mtype_t; struct mount_info_s; struct mount_s; /** Describes how stuff should be mounted. */ typedef struct mount_info_s { /** Mount type. */ mtype_t type; /** Mount point path. */ char *point; /** Path to special file or whatever should be mounted... */ char *device; /** Options for the mount command (-o). */ char *opts; /** Device number of DEVICE (not applicable for MTYPE_BIND). */ dev_t device_dev; } mount_info_t; /** An entry in a doubly linked list of mount points. */ typedef struct mount_s { struct mount_s *prev; struct mount_s *next; /** How many processes are using this mount? */ unsigned int usage; /** When will the mount expire? Relevant when usage is 0. */ time_t expiration; /** Properties of the mount entry. */ struct mount_info_s info; /** Device number of POINT (not applicable for MTYPE_BIND). */ dev_t point_dev; } mount_t; mount_info_t *mntinfo_alloc(void); void mntinfo_free(mount_info_t *); int mntinfo_copy(mount_info_t *, mount_info_t *); mount_info_t *mntinfo_parse(const char *); int mntinfo_stat_device(mount_info_t *); void mntinfo_sort_vec(mount_info_t **); #endif sbrsh-7.6.1/optimize.mk0000644000232200023220000000007111013052103015205 0ustar pbuilderpbuilderCPPFLAGS += -DNDEBUG CFLAGS += -O2 -fomit-frame-pointer sbrsh-7.6.1/protocol.c0000644000232200023220000002242711013052103015032 0ustar pbuilderpbuilder/* * Copyright (c) 2003, 2004, 2005 Nokia * Author: tsavola@movial.fi * * This program is licensed under GPL (see COPYING for details) */ #include "protocol.h" #include "common.h" #include "mount.h" #include #include #include #include #include #include #if __BYTE_ORDER == __BIG_ENDIAN # define htonll(n) (n) # define ntohll(n) (n) #elif __BYTE_ORDER == __LITTLE_ENDIAN # define htonll(n) ((((uint64_t) htonl(n)) << 32LL) | htonl((n) >> 32LL)) # define ntohll(n) ((((uint64_t) ntohl(n)) << 32LL) | ntohl((n) >> 32LL)) #endif #if 0 #include static FILE *get_log(void) { static FILE *file = NULL; if (!file) { static char path[PATH_MAX]; snprintf(path, sizeof (path), "/tmp/protocol.%d", getpid()); file = fopen(path, "w"); if (!file) abort(); } return file; } static ssize_t my_write(int fd, void *buf, size_t len) { static size_t total = 0; ssize_t retval; size_t i; FILE *file; retval = write(fd, buf, len); if (retval > 0) { total += retval; file = get_log(); fprintf(file, "write fd=%2d total=%6d len=%5d buf=", fd, total, retval); for (i = 0; i < retval; ++i) fprintf(file, "%02x", (unsigned int) ((unsigned char *) buf)[i]); fprintf(file, "\n"); fflush(file); } return retval; } static ssize_t my_read(int fd, void *buf, size_t len) { static size_t total = 0; ssize_t retval; size_t i; FILE *file; retval = read(fd, buf, len); if (retval > 0) { total += retval; file = get_log(); fprintf(file, "read fd=%2d total=%6d len=%5d buf=", fd, total, retval); for (i = 0; i < retval; ++i) fprintf(file, "%02x", (unsigned int) ((unsigned char *) buf)[i]); fprintf(file, "\n"); fflush(file); } return retval; } #define write my_write #define read my_read #endif uint16v_t *uint16v_alloc(uint32_t len) { char *mem = malloc(sizeof (uint16v_t) + len * sizeof (uint16_t)); uint16v_t *ints = (uint16v_t *) mem; if (ints) { ints->len = len; ints->vec = (uint16_t *) (mem + sizeof (uint16v_t)); } return ints; } void uint16v_free(uint16v_t *ints) { free(ints); } /** * Same as read(2) but retries on EINTR. */ ssize_t read_ni(int fd, void *buf, size_t len) { ssize_t retval; do { retval = read(fd, buf, len); } while (retval < 0 && errno == EINTR); return retval; } /** * Same as write(2) but retries on EINTR. */ ssize_t write_ni(int fd, void *buf, size_t len) { ssize_t retval; do { retval = write(fd, buf, len); } while (retval < 0 && errno == EINTR); return retval; } int write_uint16(int fd, uint16_t i) { uint16_t data = htons(i); return write_buf(fd, &data, sizeof (data)); } int write_uint32(int fd, uint32_t i) { uint32_t data = htonl(i); return write_buf(fd, &data, sizeof (data)); } int write_uint64(int fd, uint64_t i) { uint64_t data = htonll(i); return write_buf(fd, &data, sizeof (data)); } int write_uint16v(int fd, uint16v_t *ints) { if (write_uint32(fd, ints->len) < 0) return -1; if (ints) { int i; for (i = 0; i < ints->len; ++i) if (write_uint16(fd, ints->vec[i]) < 0) return -1; } return 0; } /** * Writes a string into a file. * @param fd the file descriptor * @param str the null-terminated string * @return 0 on success, -1 on error */ int write_str(int fd, const char *str) { uint32_t len; if (!str) str = ""; len = strlen(str); if (write_uint32(fd, len) < 0 || write_buf(fd, (void *) str, len) < 0) return -1; return 0; } /** * Writes a string vector into a file. * @param fd the file descriptor * @param strv the string vector * @return 0 on success, -1 on error */ int write_strv(int fd, char **strv) { if (write_uint32(fd, calc_vec_len((void **) strv)) < 0) return -1; if (strv) { char **s; for (s = strv; *s; ++s) if (write_str(fd, *s) < 0) return -1; } return 0; } int write_mount(int fd, const mount_info_t *mi) { if (write_enum (fd, mi->type ) < 0 || write_str(fd, mi->point) < 0 || write_str (fd, mi->device ) < 0 || write_str(fd, mi->opts ) < 0 || write_uint64(fd, mi->device_dev)) return -1; return 0; } int write_mountv(int fd, mount_info_t **mounts) { if (write_uint32(fd, calc_vec_len((void **) mounts)) < 0) return -1; if (mounts) { mount_info_t **p; for (p = mounts; *p; ++p) if (write_mount(fd, *p) < 0) return -1; } return 0; } int write_buf(int fd, void *buf, size_t len) { ssize_t i, cnt; for (i = 0; i < len; i += cnt) { cnt = write(fd, buf + i, len - i); if (cnt < 0) { if (errno == EAGAIN || errno == EINTR) { cnt = 0; continue; } return -1; } } return 0; } /** * Reads a complete buffer from a file. errno will be set to EIO at EOF. * @param fd a file descriptor * @param buf pointer to a buffer large enough * @param len the length of the buffer * @return 0 on success, -1 on i/o error */ int read_buf(int fd, void *buf, size_t len) { ssize_t i, cnt; for (i = 0; i < len; i += cnt) { cnt = read(fd, buf + i, len - i); if (cnt < 0) { if (errno == EAGAIN || errno == EINTR) { cnt = 0; continue; } return -1; } if (cnt == 0) { errno = EIO; return -1; } } return 0; } int read_uint16(int fd, uint16_t *iptr) { uint16_t data; if (read_buf(fd, &data, sizeof (data)) < 0) return -1; *iptr = ntohs(data); return 0; } int read_uint32(int fd, uint32_t *iptr) { uint32_t data; if (read_buf(fd, &data, sizeof (data)) < 0) return -1; *iptr = ntohl(data); return 0; } int read_uint64(int fd, uint64_t *iptr) { uint64_t data; if (read_buf(fd, &data, sizeof (data)) < 0) return -1; *iptr = ntohll(data); return 0; } uint16v_t *read_uint16v(int fd) { uint32_t len, i; uint16v_t *ints; if (read_uint32(fd, &len) < 0) return NULL; ints = uint16v_alloc(len); if (!ints) return NULL; for (i = 0; i < len; ++i) if (read_uint16(fd, &ints->vec[i]) < 0) { uint16v_free(ints); return NULL; } return ints; } /** * Reads a string from a file. The returned string should be free()'d. * @param fd a file descriptor * @return NULL on error */ char *read_str(int fd) { uint32_t len; char *str; if (read_uint32(fd, &len) < 0) return NULL; str = calloc(len + 1, 1); if (!str) return NULL; if (read_buf(fd, str, len) < 0) { free(str); return NULL; } return str; } /** * Reads a string vector from a file. The returned strings and the vector * should be set free(). The vector is null-terminated. * @param fd a file descriptor * @return NULL on error */ char **read_strv(int fd) { uint32_t len, i; char **strv; if (read_uint32(fd, &len) < 0) return NULL; strv = calloc(len + 1, sizeof (char *)); if (!strv) return NULL; for (i = 0; i < len; ++i) { strv[i] = read_str(fd); if (!strv[i]) { free_vec((void **) strv, NULL); return NULL; } } return strv; } mount_info_t *read_mount(int fd) { mount_info_t *mi; mi = mntinfo_alloc(); if (!mi) return NULL; if ((mi->type = read_enum(fd)) >= 0 && (mi->point = read_str(fd)) && (mi->device = read_str(fd)) && (mi->opts = read_str(fd)) && read_uint64(fd, &mi->device_dev) >= 0) return mi; mntinfo_free(mi); return NULL; } mount_info_t **read_mountv(int fd) { uint32_t len, i; mount_info_t **mounts; if (read_uint32(fd, &len) < 0) return NULL; mounts = calloc(len + 1, sizeof (mount_info_t *)); if (!mounts) return NULL; for (i = 0; i < len; ++i) { mounts[i] = read_mount(fd); if (!mounts[i]) { free_vec((void **) mounts, (free_func_t *) mntinfo_free); return NULL; } } return mounts; } int write_winsize(int fd, const struct winsize *ws) { if (write_uint16(fd, ws->ws_row ) < 0 || write_uint16(fd, ws->ws_col ) < 0 || write_uint16(fd, ws->ws_xpixel) < 0 || write_uint16(fd, ws->ws_ypixel) < 0) return -1; return 0; } struct winsize *read_winsize(int fd) { uint16_t row, col, xpixel, ypixel; struct winsize *ws; if (read_uint16(fd, &row ) < 0 || read_uint16(fd, &col ) < 0 || read_uint16(fd, &xpixel) < 0 || read_uint16(fd, &ypixel) < 0) return NULL; ws = malloc(sizeof (struct winsize)); if (!ws) return NULL; ws->ws_row = row; ws->ws_col = col; ws->ws_xpixel = xpixel; ws->ws_ypixel = ypixel; return ws; } int write_enum(int fd, int type) { return write_uint16(fd, type); } int read_enum(int fd) { uint16_t type; if (read_uint16(fd, &type) < 0) return -1; return type; } int write_buf_packet(int fd, ptype_t type, size_t size, void *buf) { if (write_enum(fd, type) < 0 || write_uint32(fd, size) < 0 || write_buf(fd, buf, size) < 0) return -1; return 0; } int write_str_packet(int fd, ptype_t type, const char *str) { if (write_enum(fd, type) < 0 || write_str(fd, (char *) str) < 0) return -1; return 0; } int write_uint16_packet(int fd, ptype_t type, uint16_t val) { if (write_enum(fd, type) < 0 || write_uint16(fd, val) < 0) return -1; return 0; } /** * Writes the VERSION packet to a file. * @param fd a file descriptor * @return 0 on success, -1 on i/o error */ int send_version(int fd) { if (write_enum(fd, PTYPE_VERSION) < 0 || write_uint16(fd, PROTOCOL_VERSION) < 0) return -1; return 0; } /** * Reads the VERSION packet from a file. * @param fd a file descriptor * @return version on success, -1 on i/o error */ int get_version(int fd) { uint16_t ver; if (read_enum(fd) != PTYPE_VERSION) { errno = 0; return -1; } if (read_uint16(fd, &ver) < 0) return -1; return ver; } sbrsh-7.6.1/protocol.h0000644000232200023220000000472011013052103015033 0ustar pbuilderpbuilder/* * Copyright (c) 2003, 2004 Nokia * Author: tsavola@movial.fi * * This program is licensed under GPL (see COPYING for details) */ #ifndef PROTOCOL_H #define PROTOCOL_H #include "common.h" #include "mount.h" #include #include #define PROTOCOL_VERSION 7 #define INTERNAL_ERROR_CODE 32767 typedef struct { uint32_t len; uint16_t *vec; } uint16v_t; uint16v_t *uint16v_alloc(uint32_t len); void uint16v_free(uint16v_t *); ssize_t write_ni(int fd, void *buf, size_t len); ssize_t read_ni(int fd, void *buf, size_t len); int write_buf(int fd, void *buf, size_t len); int read_buf(int fd, void *buf, size_t len); int write_uint16(int fd, uint16_t); int read_uint16(int fd, uint16_t *); int write_uint32(int fd, uint32_t); int read_uint32(int fd, uint32_t *); int write_uint64(int fd, uint64_t); int read_uint64(int fd, uint64_t *); int write_uint16v(int fd, uint16v_t *); uint16v_t *read_uint16v(int fd); int write_str(int fd, const char *); char *read_str(int fd); int write_strv(int fd, char **); char **read_strv(int fd); int write_mount(int fd, const mount_info_t *); mount_info_t *read_mount(int fd); int write_mountv(int fd, mount_info_t **); mount_info_t **read_mountv(int fd); int write_winsize(int fd, const struct winsize *); struct winsize *read_winsize(int fd); typedef enum { PTYPE_VERSION = 0x0b, PTYPE_MESSAGE = 0x10, /* Daemon */ PTYPE_ERROR = 0x11, /* Daemon */ PTYPE_USER = 0x22, /* Client */ PTYPE_AUTH = 0x23, /* Daemon */ PTYPE_RC = 0x24, /* Daemon */ PTYPE_COMMAND = 0x30, /* Client */ PTYPE_MOUNT = 0x31, /* Client */ PTYPE_UMOUNT = 0x32, /* Client */ PTYPE_TARGET = 0x41, /* Client */ PTYPE_MOUNTS = 0x42, /* Client */ PTYPE_ARGS = 0x50, /* Client */ PTYPE_CWD = 0x51, /* Client */ PTYPE_ENVIRON = 0x52, /* Client */ PTYPE_IDS = 0x53, /* Client */ PTYPE_UMASK = 0x54, /* Client */ PTYPE_WINSIZE = 0x55, /* Client */ PTYPE_IN_REQ = 0x60, /* Daemon */ PTYPE_IN_DATA = 0x61, /* Client */ PTYPE_OUT_REQ = 0x62, /* Client */ PTYPE_OUT_DATA = 0x63, /* Daemon */ PTYPE_ERR_REQ = 0x64, /* Client */ PTYPE_ERR_DATA = 0x65, /* Daemon */ } ptype_t; int write_enum(int fd, int); int read_enum(int fd); int write_buf_packet(int fd, ptype_t, size_t size, void *); int write_str_packet(int fd, ptype_t, const char *); int write_uint16_packet(int fd, ptype_t, uint16_t); int send_version(int fd); int get_version(int fd); #endif sbrsh-7.6.1/sb-exportfs.c0000644000232200023220000001133211013052103015436 0ustar pbuilderpbuilder/* * sb-exportfs version 0.1 * * Copyright (c) 2003, 2004 Nokia * * sb-exportfs is licensed under GPL. * You can read the full license at http://www.gnu.org/licenses/gpl.txt * */ #define _GNU_SOURCE #include "types.h" #include "common.h" #include "config.h" #include #include #include #include #include #include #include #include #include #define SB_USERS_DIR "/scratchbox/users" #define SB_HOME_DIR "/scratchbox/users/%s/home" #define SB_TARGET_DIR "/scratchbox/users/%s/targets/%s" #define SB_TARGETS_DIR "/scratchbox/users/%s/targets" #define MOUNT_OPTS "rw,all_squash,anonuid=%i,anongid=%i" /* * We only allow export that are SB_HOME_DIR, SB_TARGET_DIR or SB_TARGETS_DIR. * In the last two we also check that those are not links to anywhere but * they are really directories. */ static int check_path(const char *path, const char *user, const char *target) { char *homedir; char *targetdir; char *targetsdir; char *mypath; char *tmp; struct stat filestat; homedir = alloca(strlen(SB_HOME_DIR) + strlen(user) + 2); targetdir = alloca(strlen(SB_TARGET_DIR) + strlen(user) + strlen(target) + 2); targetsdir = alloca(strlen(SB_TARGETS_DIR) + strlen(user) + 2); sprintf(homedir, SB_HOME_DIR, user); sprintf(targetdir, SB_TARGET_DIR, user, target); sprintf(targetsdir, SB_TARGETS_DIR, user); mypath = strdupa(path); while (mypath[strlen(mypath)] == '/') mypath[strlen(mypath)] = '\0'; /* if sb is istalled correctly then this should always be directory */ if (strcmp(mypath, homedir) == 0) return 0; /* we want to test this a bit */ if (strcmp(mypath, targetdir) == 0) { stat(mypath, &filestat); if (!S_ISDIR(filestat.st_mode)) goto _not_dir; tmp = strrchr(mypath, '/'); if (tmp == NULL) { fprintf(stderr, "Error: Bad path: %s\n", path); return -1337; } *tmp = '\0'; stat(tmp, &filestat); if (!S_ISDIR(filestat.st_mode)) goto _not_dir; return 0; } if (strcmp(mypath, targetsdir) == 0) { stat(mypath, &filestat); if (!S_ISDIR(filestat.st_mode)) goto _not_dir; return 0; } fprintf(stderr, "Error: Not allowed to export %s\n", path); printf("homedir: %s\n", homedir); printf("targetdir: %s\n", targetdir); printf("targetsdir: %s\n", targetsdir); return -2; _not_dir: fprintf(stderr, "Error: Not allowed to export %s (not a directory)\n", path); return -1; } int main(int argc, char **argv) { DIR *dir; struct dirent *file; config_t *cfg; pid_t pid; int status; dir = opendir(SB_USERS_DIR); if (dir == NULL) { perror("Error: Can't open directory"); exit(1); } pid = fork(); if (pid == 0) { execlp("exportfs", "exportfs", "-r", NULL); perror("Error: Can't exec exportfs"); } wait(&status); while ((file = readdir(dir)) != NULL) { char **targets; char **p, **q; char *sbrsh_config; struct passwd *pwds = NULL; if (strchr(file->d_name, '.') == file->d_name) continue; pwds = getpwnam(file->d_name); if (pwds == NULL) continue; /* check that .sbrsh file exists */ sbrsh_config = malloc(strlen(SB_USERS_DIR) + strlen("/home/") + strlen(file->d_name) * 2 + strlen("/.sbrsh") + 4); sprintf(sbrsh_config, "%s/%s/home/%s/.sbrsh", SB_USERS_DIR, file->d_name, file->d_name); targets = get_targets(sbrsh_config); if (!targets) { fprintf(stderr, "Warning: No targets found from %s\n", sbrsh_config); free(sbrsh_config); continue; } p = targets; while (*p != NULL) { cfg = config_alloc(); config_read(cfg, sbrsh_config, *p); q = cfg->opts; while (*q != NULL) { if (strncmp(*q, "nfs", 3) == 0) { char *path, *tmp; char *cp, *opts; path = strchr(*q, ':'); if (path == NULL) { fprintf(stderr, "Error: Bad config file: %s\n", sbrsh_config); ++q; continue; } ++path; tmp = find_space(path); if (tmp == NULL) { fprintf(stderr, "Error: Bad config file: %s\n", sbrsh_config); ++q; continue; } *tmp++ = '\0'; /* check the path */ if (check_path(path, file->d_name, *p) < 0) { ++q; continue; } cp = malloc(strlen(cfg->host) + strlen(path) + 4); sprintf(cp, "%s:/%s", cfg->host, path); opts = malloc(strlen(MOUNT_OPTS) + 11); sprintf(opts, MOUNT_OPTS, pwds->pw_uid, pwds->pw_gid); pid = fork(); if (pid == 0) { execlp("exportfs", "exportfs", "-i", "-o", opts, cp, NULL); perror("Error: Can't exec exportfs"); } wait(&status); free(cp); free(opts); } ++q; } config_free(cfg); ++p; } free_vec((void **) targets, NULL); free(sbrsh_config); } closedir(dir); return 0; } sbrsh-7.6.1/sbrsh.conf0000644000232200023220000000143011013052103015004 0ustar pbuilderpbuilder# Example target environment configuration for sbrsh. Multiple targets can # be specified in a single file. The configuration is read from ~/.sbrsh by # default. # # Format: # # [:] # nfs|bind [] # ... # # Usual scenario default-target device01 nfs host01:/home/juser/buildroot / rw,nolock,noac,tcp nfs host01:/home /home rw,nolock,noac,tcp bind /tmp /tmp bind /dev /dev bind /dev/pts /dev/pts bind /proc /proc bind /sys /sys # Unusual scenario other-target 192.168.1.5:1212 nfs host01:/work/buildroot / rw,nolock,noac nfs host02:/home /home rw,nolock,noac bind /var/tmp /tmp bind /dev /dev bind /proc /proc # When running sbrshd with the "--no-sandbox" option bare-target device02 sbrsh-7.6.1/sbrsh.mk0000644000232200023220000000017311013052103014471 0ustar pbuilderpbuilderNAME := sbrsh SOURCES := client.c config.c DEPENDS := $(O)/lib/libcommon.a LIBS := $(DEPENDS) include build/binary.mk sbrsh-7.6.1/sbrshd.conf0000644000232200023220000000033711013052103015155 0ustar pbuilderpbuilder# List of authorized IPv4 addresses for incoming sbrsh connections. # WARNING: No other authentication is performed! # # Trivial example: # 192.168.1.1 # 192.168.1.2 # # Wildcard example: # 192.168.1.* # 10.0.* # 127.0.0.1 sbrsh-7.6.1/sbrshd.init0000644000232200023220000000716411013052103015200 0ustar pbuilderpbuilder#!/bin/sh ### BEGIN INIT INFO # Provides: sbrshd # Required-Start: $remote_fs # Required-Stop: $remote_fs # Should-Start: $remote_fs # Should-Stop: $remote_fs # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Start sbrshd daemon # Description: Sbrshd deamon is started if defined to # start in /etc/default/sbrshd. ### END INIT INFO ENABLE=true # true | false PORT=1202 # LOCAL_ONLY=false # true | false NO_SANDBOX=false # true | false ALLOW_ROOT=false # true | false DEBUG=false # true | false DEBUG_VERBOSE=false # true | false EXPIRATION=15 # 0 | | none SBRSHD=/usr/sbin/sbrshd PIDFILE=/var/run/sbrshd.pid DEBUG_FILE=/tmp/sbrshd-$PORT.log if [ -f /etc/default/sbrshd ]; then . /etc/default/sbrshd fi if ! [ -x $SBRSHD ]; then exit 0 fi signal_sbrshd() { SIGNAL=$1 if [ -f $PIDFILE ]; then PID=`cat $PIDFILE` if [ x"$PID" != x ] && [ -d /proc/$PID ]; then if ! kill -$SIGNAL $PID; then echo "failed!" return fi if [ $SIGNAL = TERM ]; then for i in 1 2 3 4 5; do if [ ! -d /proc/$PID ]; then rm $PIDFILE echo "done." return fi sleep 1 done echo "process did not die!" else echo "done." fi else echo "process not found!" if [ $SIGNAL = TERM ]; then rm $PIDFILE fi fi else echo "pid file not found!" fi } case "$1" in start) if ! $ENABLE; then echo "Not starting sbrshd, disabled via /etc/default/sbrshd" exit 0 fi echo -n "Starting sbrshd: " if [ -f $PIDFILE ]; then PID=`cat $PIDFILE` if [ "$PID" ] && [ -d /proc/$PID ]; then echo "already running!" exit 1 else rm $PIDFILE fi fi ARGS="$SBRSHD --port=$PORT --mount-expiration=$EXPIRATION" if $LOCAL_ONLY; then ARGS="$ARGS --local-only" fi if $NO_SANDBOX; then ARGS="$ARGS --no-sandbox" fi if $ALLOW_ROOT; then ARGS="$ARGS --allow-root" fi if $DEBUG; then ARGS="$ARGS --debug=$DEBUG_FILE" fi if $DEBUG_VERBOSE; then ARGS="$ARGS --debug-verbose" fi if [ -n "$MOUNT_BIN" ] ; then ARGS="$ARGS --mount-bin=$MOUNT_BIN" fi if [ -n "$UMOUNT_BIN" ] ; then ARGS="$ARGS --umount-bin=$UMOUNT_BIN" fi if [ -n "$SSHFS_BIN" ] ; then ARGS="$ARGS --sshfs-bin=$SSHFS_BIN" fi if [ -n "$FUSERMOUNT_BIN" ] ; then ARGS="$ARGS --fusermount-bin=$FUSERMOUNT_BIN" fi PID=`$ARGS` if [ x"$PID" != x ]; then echo "done." echo $PID > $PIDFILE else exit 1 fi ;; stop) echo -n "Stopping sbrshd: " signal_sbrshd TERM ;; restart|reload|force-reload) $0 stop sleep 1 $0 start ;; enable-debug) echo -n "Opening sbrshd debug log: " signal_sbrshd USR1 ;; disable-debug) echo -n "Closing sbrshd debug log: " signal_sbrshd USR2 ;; *) echo "Usage: $0 {start|stop|restart|enable-debug|disable-debug}" exit 1 ;; esac exit 0 sbrsh-7.6.1/sbrshd.mk0000644000232200023220000000043011013052103014631 0ustar pbuilderpbuilderNAME := sbrshd SOURCES := daemon.c fakeroot.c CPPFLAGS += -I. DEPENDS := $(O)/lib/libcommon.a LIBS := $(DEPENDS) -lutil BINDIR := $(SBINDIR) include build/binary.mk install:: mkdir -p $(DEST_SYSCONFDIR)/init.d install -m 644 sbrshd.init $(DEST_SYSCONFDIR)/init.d/sbrshd sbrsh-7.6.1/types.h0000644000232200023220000000121411013052103014331 0ustar pbuilderpbuilder/* * Copyright (c) 2003, 2004 Nokia * Author: tsavola@movial.fi * * This program is licensed under GPL (see COPYING for details) */ #ifndef TYPES_H #define TYPES_H #include #include #include typedef unsigned int bool_t; #ifndef TRUE # define TRUE ((bool_t) 1) #endif #ifndef FALSE # define FALSE ((bool_t) 0) #endif #if __BYTE_ORDER == __BIG_ENDIAN # define htonll(n) (n) # define ntohll(n) (n) #elif __BYTE_ORDER == __LITTLE_ENDIAN # define htonll(n) ((((uint64_t) htonl(n)) << 32LL) | htonl((n) >> 32LL)) # define ntohll(n) ((((uint64_t) ntohl(n)) << 32LL) | ntohl((n) >> 32LL)) #endif #endif sbrsh-7.6.1/version.h0000644000232200023220000000002611013052103014652 0ustar pbuilderpbuilder#define REVISION ".6"