Re: Chrooted sftp-server and /dev/null
On Friday 23 June 2006 22:24, Joachim Schipper wrote: You could set up a named pipe (mkfifo(1)), and have a process continually drain it (cat /home/john/dev/null /dev/null ); however, while this would work for the most likely use (writing to /dev/null), it wouldn't allow for reading. I'm not sure if sftp-server ever reads from /dev/null, but it is not impossible. Strange errors will occur if this is the case. Im thinking it might just be easier to make a copy of the /dev/null device, but i need to investigate and test this... Yes, make sure you also set real uid. A small part of /usr/src/usr.sbin/tcpdump/privsep.c: /* Child - drop suid privileges */ gid = getgid(); uid = getuid(); if (setresgid(gid, gid, gid) == -1) err(1, setresgid() failed); if (setresuid(uid, uid, uid) == -1) err(1, setresuid() failed); Do note that this is only necessary if the shell is suid and/or sgid; however, normal users don't have the rights to call chroot(2), so these additional priviliges are necessary. Also, you are aware that you perform chroot(), setresuid() and setresgid(), and only then execve()? This means that you'll need some binaries in the home directories... So, be aware that deleting a file or directory requires write priviliges on the parent directory; i.e., john can replace /home/john/bin/sftp-server by an arbitrary binary if john has write priviliges on his home directory, hence my suggestion to use /home (which is typically only writable by root) above. (An alternate solution is to make /home/john owned by root, group john, and with priviliges 0750; this would break too many things to be feasible if shells are allowed, but just might work if only considering sftp.) Finally, be aware of the many other options sshd allows, like various ways of tunneling. For the same reason as above, those cannot be disabled in /home/john/.ssh/authorized_keys only (disabling them there works iff the user cannot mess with this file, which is clearly not the case if the user has access to sftp). Either disable them sshd-wide or set AuthorizedKeysFile (see sshd_config(5)) to something like /home/.keys/%u/authorized_keys. Note that running any number of ssh daemons in parallel works just fine, subject to some caveats (they can, of course, not listen on the same ports on the same interfaces; they are quite CPU intensive; and random number quality may degrade if the pool is drained sufficiently fast). Joachim I am going to write another program which is used to setup, check, and update the chroot environments with the right files and permissions. Im going to have it chown the home dirs to root/wheel, and there will only be a single writeable dir owned by the user ( which will contain their website files for example ). Here is a copy of the code ive got so far... its by no means finished, or formatted in the proper way, or even checked over properly again: #include stdio.h #include stdlib.h #include errno.h #include string.h #include syslog.h #include stdarg.h #include unistd.h #include fcntl.h #include sys/types.h #include sys/wait.h #include pwd.h char home_dir[1024]; int argc; char **argv; void print_arguments(void); void check_arguments(void); void check_user(void); char * find_end_part(char *buff); void setup_env(void); int main(int _argc, char **_argv) { char *exec_args[2]; argc = _argc; argv = _argv; openlog(jshell, LOG_PID | LOG_NDELAY, LOG_AUTH); check_arguments(); check_user(); if (chroot(home_dir) != 0 || chdir(/) != 0) { syslog(LOG_ERR, chroot(%s) failed: %s, home_dir, strerror(errno)); return 1; } /* drop privledges */ if (seteuid(getuid()) != 0 || setuid(getuid()) != 0) { syslog(LOG_ERR, setuid(%d) failed: %s, getuid(), strerror(errno)); return 1; } exec_args[1] = NULL; exec_args[0] = find_end_part(argv[2]); execve(argv[2], exec_args, NULL); syslog(LOG_ERR, execve failed); return 1; } /* print arguments to syslog */ void print_arguments(void) { int x; for (x = 0; x argc; x++) { syslog(LOG_ERR, %d arg is '%s', x, argv[x]); } } /* * for now we only allow -c /usr/libexec/sftp-server as an argument */ void check_arguments(void) { /* compare second argument ( should be -c ) */ if (argc != 3 || strcmp(-c, argv[1]) != 0) { syslog(LOG_ERR, invalid arguments\n); print_arguments(); exit(1); } /* compare third argument */ if (strcmp(/usr/libexec/sftp-server, argv[2]) != 0) { syslog(LOG_ERR, invalid arguments\n); print_arguments(); exit(1); } } /* * check the user has some sane permissions and settings * and what not on their home dir. */ void check_user(void) { struct passwd *pw = NULL; /* * do we bother checking for a root login? * why would root be
Re: Chrooted sftp-server and /dev/null
On Fri, Jun 23, 2006 at 12:00:17PM +1200, Joshua Sandbrook wrote: On Thursday 22 June 2006 22:26, Joachim Schipper wrote: A quick grep through /usr/src/usr.bin/ssh suggests that ssh (and, most likely, sftp) interacts with /dev/null quite a bit. It might be possible to change the code to work without, but that would take quite a bit of work I fear. An alternative hack would be to change sftp directly; in this case, it can safely open /dev/null and then call chroot() at the appropriate time. OTOH, you have a custom patch that you should apply at the appropriate time and place, which, too, has its disadvantages. Also, be *very* careful in writing the shell, as it must be suid root for what you want it to do... this, in fact, suggests that the best solution might be to write a trivial shell and just have /home/*/dev/null. Or, for that matter, /home/dev/null and chroot into /home. Thanks for the reply... It is sftp-server that tries to open /dev/null. As I dont want to modify sftp-server or anything like that, I think im going to just populate each chroot environment with a /dev/null. However, as I dont want /home to have any devices on it, is there a way to have some sort of file type that simply throws the data away like /dev/null does? You could set up a named pipe (mkfifo(1)), and have a process continually drain it (cat /home/john/dev/null /dev/null ); however, while this would work for the most likely use (writing to /dev/null), it wouldn't allow for reading. I'm not sure if sftp-server ever reads from /dev/null, but it is not impossible. Strange errors will occur if this is the case. As for the shell, Yep, I am being carefull with it.. all the shell actaully does, is checks if the arguments given to it is '-c /usr/libexec/sftp-server', and if it is, it then chroots to the users home dir, and then it sets effective uid to the uid that called the shell, and then it executes /usr/libexec/sftp-server. Not much code at all. I want to research more into possible security hassles ( like /home/foo/.ssh ) and stuff like that later on. Any suggestions in this area? ( security ) Yes, make sure you also set real uid. A small part of /usr/src/usr.sbin/tcpdump/privsep.c: /* Child - drop suid privileges */ gid = getgid(); uid = getuid(); if (setresgid(gid, gid, gid) == -1) err(1, setresgid() failed); if (setresuid(uid, uid, uid) == -1) err(1, setresuid() failed); Do note that this is only necessary if the shell is suid and/or sgid; however, normal users don't have the rights to call chroot(2), so these additional priviliges are necessary. Also, you are aware that you perform chroot(), setresuid() and setresgid(), and only then execve()? This means that you'll need some binaries in the home directories... So, be aware that deleting a file or directory requires write priviliges on the parent directory; i.e., john can replace /home/john/bin/sftp-server by an arbitrary binary if john has write priviliges on his home directory, hence my suggestion to use /home (which is typically only writable by root) above. (An alternate solution is to make /home/john owned by root, group john, and with priviliges 0750; this would break too many things to be feasible if shells are allowed, but just might work if only considering sftp.) Finally, be aware of the many other options sshd allows, like various ways of tunneling. For the same reason as above, those cannot be disabled in /home/john/.ssh/authorized_keys only (disabling them there works iff the user cannot mess with this file, which is clearly not the case if the user has access to sftp). Either disable them sshd-wide or set AuthorizedKeysFile (see sshd_config(5)) to something like /home/.keys/%u/authorized_keys. Note that running any number of ssh daemons in parallel works just fine, subject to some caveats (they can, of course, not listen on the same ports on the same interfaces; they are quite CPU intensive; and random number quality may degrade if the pool is drained sufficiently fast). Joachim
[Fwd: Re: Chrooted sftp-server and /dev/null]
Joshua Sandbrook wrote: Gidday Im writing a shell at the moment that chroots into a users home dir and then runs only the sftp-server program ( which is in the uses home dir ). Anyway, it wont work unless /dev/null is present in the chroot... I am using execve to run sftp-server, and I am wondering if it has something to do with stdout / stdin / stderr fd's being closed on execve? Can anyone help me here? Thanks, Josh /de-lurk Alternatively, might something like rssh be useful? http://pizzashack.org/rssh/ This is a shell, designed to only allow scp/sftp/et al, but not allow any sort of command execution in the traditional sense. It also supports chroot. The only small fly in the ointment, is that according to http://pizzashack.org/rssh/platforms.shtml it doesn't work on *BSD, as it requires the missing wordexp() function. However, the page dates back to 2003, and I haven't the skills to check if this is still the case... Don't know if this helps or not, but I thought I'd throw it into the mix :-) Si1entDave
Re: [Fwd: Re: Chrooted sftp-server and /dev/null]
On Friday 23 June 2006 16:42, Richard Wilson wrote: The only small fly in the ointment, is that according to http://pizzashack.org/rssh/platforms.shtml it doesn't work on *BSD, as it requires the missing wordexp() function. However, the page dates back to 2003, and I haven't the skills to check if this is still the case... At least FreeBSD now has this call, but OpenBSD does not. However, scponly [1] compiles on OpenBSD out of the box. Bastiaan [1] http://www.sublimation.org/scponly/
Re: Chrooted sftp-server and /dev/null
On Wed, Jun 21, 2006 at 09:41:42AM +1200, Joshua Sandbrook wrote: Gidday Im writing a shell at the moment that chroots into a users home dir and then runs only the sftp-server program ( which is in the uses home dir ). Anyway, it wont work unless /dev/null is present in the chroot... I am using execve to run sftp-server, and I am wondering if it has something to do with stdout / stdin / stderr fd's being closed on execve? Can anyone help me here? Well, since nobody else seems to respond... If you can set it up in a controlled testing environment, you could ktrace(1) it. This would tell you, at the very least, what program actually opens /dev/null. A quick grep through /usr/src/usr.bin/ssh suggests that ssh (and, most likely, sftp) interacts with /dev/null quite a bit. It might be possible to change the code to work without, but that would take quite a bit of work I fear. An alternative hack would be to change sftp directly; in this case, it can safely open /dev/null and then call chroot() at the appropriate time. OTOH, you have a custom patch that you should apply at the appropriate time and place, which, too, has its disadvantages. Also, be *very* careful in writing the shell, as it must be suid root for what you want it to do... this, in fact, suggests that the best solution might be to write a trivial shell and just have /home/*/dev/null. Or, for that matter, /home/dev/null and chroot into /home. Joachim
Re: Chrooted sftp-server and /dev/null
Thanks for the reply... It is sftp-server that tries to open /dev/null. As I dont want to modify sftp-server or anything like that, I think im going to just populate each chroot environment with a /dev/null. However, as I dont want /home to have any devices on it, is there a way to have some sort of file type that simply throws the data away like /dev/null does? As for the shell, Yep, I am being carefull with it.. all the shell actaully does, is checks if the arguments given to it is '-c /usr/libexec/sftp-server', and if it is, it then chroots to the users home dir, and then it sets effective uid to the uid that called the shell, and then it executes /usr/libexec/sftp-server. Not much code at all. I want to research more into possible security hassles ( like /home/foo/.ssh ) and stuff like that later on. Any suggestions in this area? ( security ) Cheers, Josh On Thursday 22 June 2006 22:26, Joachim Schipper wrote: Well, since nobody else seems to respond... If you can set it up in a controlled testing environment, you could ktrace(1) it. This would tell you, at the very least, what program actually opens /dev/null. A quick grep through /usr/src/usr.bin/ssh suggests that ssh (and, most likely, sftp) interacts with /dev/null quite a bit. It might be possible to change the code to work without, but that would take quite a bit of work I fear. An alternative hack would be to change sftp directly; in this case, it can safely open /dev/null and then call chroot() at the appropriate time. OTOH, you have a custom patch that you should apply at the appropriate time and place, which, too, has its disadvantages. Also, be *very* careful in writing the shell, as it must be suid root for what you want it to do... this, in fact, suggests that the best solution might be to write a trivial shell and just have /home/*/dev/null. Or, for that matter, /home/dev/null and chroot into /home. Joachim
Re: Chrooted sftp-server and /dev/null
Can anyone help here? Ive played wih fcntl's FD_CLOEXEC and what not.. it was set to 0, and yeah... If someone can help solve this mystery then there is one less file required in the chroot environment. A cleaner scponly shell :) On Wednesday 21 June 2006 09:41, Joshua Sandbrook wrote: Gidday Im writing a shell at the moment that chroots into a users home dir and then runs only the sftp-server program ( which is in the uses home dir ). Anyway, it wont work unless /dev/null is present in the chroot... I am using execve to run sftp-server, and I am wondering if it has something to do with stdout / stdin / stderr fd's being closed on execve? Can anyone help me here? Thanks, Josh