sftp-server (secure) chroot patch?

Magnus F magnus at mandarin.nu
Sun Aug 17 01:45:10 EST 2003


Hello,

I know this chroot issue has been brought up many times before on this list. I saw that the contribibuted chroot-patch was removed from the contrib directory because it always was out of date. The main reason was of course was that sftp-server has to be run as root to be able to do the chroot() call? Most of you are against chroot (since it isnt in the src) but I believe a lot of users have use for it. I dont think the solution is to use SSH Corps version.

There are several chroot-patches available, like the chrootssh project and rssh (restricted shell sftp) and scponly. To make a long story short, none of these provide the ability to chroot sftp users in their homedir. That is, in these projects you are able to wonder around the chroot-tree /dev /bin /usr etc. 

I have found rssh to be usable because it ables you to restrict the user to sftp or scp, and shutdown ssh-access, however the user still can wonder around the chroot-tree.

I did a bit of research and came to the conclusion that the chroot-call needed to be done in the src of sftp-server.c
I found three patches that does it:
http://www.alt219.com/software/sftp-server-chroot/
http://www.coding-zone.com/chroot+sftp-server.patch
http://groups.google.com/groups?hl=sv&lr=&ie=UTF-8&oe=UTF-8&frame=right&th=45c783aa0a25801a&seekm=arc304%241v5l%241%40FreeBSD.csie.NCTU.edu.tw#link1

Problem one seems to be that setuid(getuid()); is reversable. Also better sanity checking before chroot is required. 

I have written a patch that (probably) is more secure than the ones I found. It uses uidswap functions to change uid & gid. 

Someone said the point behind subsystems was all the configuration for that subsystem are contained in the subsystem proper and not in sshd_config. Thats why I didnt add any config-changes to this patch.

So if someone feels like it, maybe /etc/ssh/sftp-server.conf is a good place to config chroot. Since I don't have much C-knowledge, there might be errors in this patch (it works for me though). Please reply with the correct code. 

Thanks to Ben Lindstrom for helping me with this...

I use this patch together with rssh, and I put the sftp-binary in the rssh chroot /ftproot/usr/local/libexec and its called by rssh-chroot-helper. When a user logins with sftp he will get chrooted to /ftproot/home/user. I shutdown access to scp and ssh, only sftp is allowed, so the user will never see the files in /ftproot

To apply this patch on OpenSSH 3.6.1p2:
- patch -p0 < sftp-server.patch
- edit Makefile and include uidswap.o in sftp-server
- make sftp-server
- copy sftp-server into your chroot and set +s

Regards
Magnus


"sftp-server.patch" 103 lines, 2521 characters 
--- openssh-3.6.1p2/sftp-server.c.org   2003-08-11 22:07:47.098650000 +0200
+++ openssh-3.6.1p2/sftp-server.c       2003-08-16 16:43:11.884356000 +0200
@@ -24,15 +24,24 @@
 #include "includes.h"
 RCSID("$OpenBSD: sftp-server.c,v 1.41 2003/03/26 04:02:51 deraadt Exp $");

+#define CHROOT
 #include "buffer.h"
 #include "bufaux.h"
 #include "getput.h"
 #include "log.h"
 #include "xmalloc.h"
-
 #include "sftp.h"
 #include "sftp-common.h"

+#ifdef CHROOT
+#include "uidswap.h"
+#include <pwd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <unistd.h>
+#endif /* CHROOT */
+
 /* helper */
 #define get_int64()                    buffer_get_int64(&iqueue);
 #define get_int()                      buffer_get_int(&iqueue);
@@ -62,6 +71,51 @@
        Attrib attrib;
 };

+#ifdef CHROOT
+static void
+chroot_init(void)
+{
+       gid_t gidset[1];
+       struct passwd *pw;
+       struct stat st;
+
+       /* Sanity checking before chroot */
+       if ((pw = getpwuid(getuid())) == NULL)
+               fatal("getpwuid failed for %u", (u_int)pw->pw_uid );
+
+       /* Sets passwd pointer to null */
+        memset(pw->pw_passwd, 0, strlen(pw->pw_passwd));
+       endpwent();
+
+       if (geteuid() != 0)
+               fatal("must be SUID root to use chroot feature");
+
+       if ((stat(pw->pw_dir, &st)) == -1)
+               fatal("cannot stat chroot directory %s: %s", pw->pw_dir, strerror(errno));
+
+       if (!S_ISDIR(st.st_mode))
+               fatal("%s is not a directory: %s", pw->pw_dir, strerror(errno));
+
+       /* Drop our privileges */
+       debug3("chroot user:group %u:%u", (u_int)pw->pw_uid, (u_int)pw->pw_gid);
+
+       /* Change our root directory */
+       if (chroot(pw->pw_dir) == -1)
+               fatal("chroot(\"%s\"): %s", pw->pw_dir, strerror(errno));
+
+       /* Change dir to prevent chroot break */
+       if (chdir("/") == -1)
+               fatal("chdir(\"/\"): %s", strerror(errno));
+
+       gidset[0] = pw->pw_gid;
+       if (setgid(pw->pw_gid) < 0)
+               fatal("setgid failed for %u", (u_int)pw->pw_gid );
+       if (setgroups(1, gidset) < 0)
+               fatal("setgroups: %.100s", strerror(errno));
+       permanently_set_uid(pw);
+}
+#endif /* CHROOT */
+
 static int
 errno_to_portable(int unixerrno)
 {
@@ -1028,15 +1082,17 @@
        int in, out, max;
        ssize_t len, olen, set_size;

-       /* XXX should use getopt */
+#ifdef DEBUG_SFTP-SERVER
+       log_init("sftp-server", SYSLOG_LEVEL_DEBUG3, SYSLOG_FACILITY_AUTH, 0);
+#endif
+
+#ifdef CHROOT
+       chroot_init();
+#endif

        __progname = get_progname(av[0]);
        handle_init();

-#ifdef DEBUG_SFTP_SERVER
-       log_init("sftp-server", SYSLOG_LEVEL_DEBUG1, SYSLOG_FACILITY_AUTH, 0);
-#endif
-
        in = dup(STDIN_FILENO);
        out = dup(STDOUT_FILENO);






More information about the openssh-unix-dev mailing list