=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/ssh/session.c,v retrieving revision 1.225 retrieving revision 1.226 diff -u -r1.225 -r1.226 --- src/usr.bin/ssh/session.c 2008/02/04 21:53:00 1.225 +++ src/usr.bin/ssh/session.c 2008/02/08 23:24:07 1.226 @@ -1,4 +1,4 @@ -/* $OpenBSD: session.c,v 1.225 2008/02/04 21:53:00 markus Exp $ */ +/* $OpenBSD: session.c,v 1.226 2008/02/08 23:24:07 djm Exp $ */ /* * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved @@ -74,6 +74,7 @@ #include "sshlogin.h" #include "serverloop.h" #include "canohost.h" +#include "misc.h" #include "session.h" #ifdef GSSAPI #include "ssh-gss.h" @@ -85,6 +86,9 @@ #include #endif +/* Magic name for internal sftp-server */ +#define INTERNAL_SFTP_NAME "internal-sftp" + /* func */ Session *session_new(void); @@ -550,13 +554,17 @@ if (options.adm_forced_command) { original_command = command; command = options.adm_forced_command; - if (s->is_subsystem) + if (strcmp(INTERNAL_SFTP_NAME, command) == 0) + s->is_subsystem = SUBSYSTEM_INT_SFTP; + else if (s->is_subsystem) s->is_subsystem = SUBSYSTEM_EXT; debug("Forced command (config) '%.900s'", command); } else if (forced_command) { original_command = command; command = forced_command; - if (s->is_subsystem) + if (strcmp(INTERNAL_SFTP_NAME, command) == 0) + s->is_subsystem = SUBSYSTEM_INT_SFTP; + else if (s->is_subsystem) s->is_subsystem = SUBSYSTEM_EXT; debug("Forced command (key option) '%.900s'", command); } @@ -568,7 +576,6 @@ restore_uid(); } #endif - if (s->ttyfd != -1) do_exec_pty(s, command); else @@ -950,16 +957,88 @@ } } +/* + * Chroot into a directory after checking it for safety: all path components + * must be root-owned directories with strict permissions. + */ +static void +safely_chroot(const char *path, uid_t uid) +{ + const char *cp; + char component[MAXPATHLEN]; + struct stat st; + + if (*path != '/') + fatal("chroot path does not begin at root"); + if (strlen(path) >= sizeof(component)) + fatal("chroot path too long"); + + /* + * Descend the path, checking that each component is a + * root-owned directory with strict permissions. + */ + for (cp = path; cp != NULL;) { + if ((cp = strchr(cp, '/')) == NULL) + strlcpy(component, path, sizeof(component)); + else { + cp++; + memcpy(component, path, cp - path); + component[cp - path] = '\0'; + } + + debug3("%s: checking '%s'", __func__, component); + + if (stat(component, &st) != 0) + fatal("%s: stat(\"%s\"): %s", __func__, + component, strerror(errno)); + if (st.st_uid != 0 || (st.st_mode & 022) != 0) + fatal("bad ownership or modes for chroot " + "directory %s\"%s\"", + cp == NULL ? "" : "component ", component); + if (!S_ISDIR(st.st_mode)) + fatal("chroot path %s\"%s\" is not a directory", + cp == NULL ? "" : "component ", component); + + } + + if (chdir(path) == -1) + fatal("Unable to chdir to chroot path \"%s\": " + "%s", path, strerror(errno)); + if (chroot(path) == -1) + fatal("chroot(\"%s\"): %s", path, strerror(errno)); + if (chdir("/") == -1) + fatal("%s: chdir(/) after chroot: %s", + __func__, strerror(errno)); + verbose("Changed root directory to \"%s\"", path); +} + /* Set login name, uid, gid, and groups. */ void do_setusercontext(struct passwd *pw) { if (getuid() == 0 || geteuid() == 0) { + /* Prepare groups */ if (setusercontext(lc, pw, pw->pw_uid, - (LOGIN_SETALL & ~LOGIN_SETPATH)) < 0) { + (LOGIN_SETALL & ~(LOGIN_SETPATH|LOGIN_SETUSER))) < 0) { perror("unable to set user context"); exit(1); } + + if (options.chroot_directory != NULL && + strcasecmp(options.chroot_directory, "none") != 0) { + char *chroot_path; + + chroot_path = percent_expand(options.chroot_directory, + "h", pw->pw_dir, "u", pw->pw_name, (char *)NULL); + safely_chroot(chroot_path, pw->pw_uid); + free(chroot_path); + } + + /* Set UID */ + if (setusercontext(lc, pw, pw->pw_uid, LOGIN_SETUSER) < 0) { + perror("unable to set user context (setuser)"); + exit(1); + } } if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid) fatal("Failed to set uids to %u.", (u_int) pw->pw_uid); @@ -1155,7 +1234,7 @@ argv[i] = NULL; optind = optreset = 1; __progname = argv[0]; - exit(sftp_server_main(i, argv)); + exit(sftp_server_main(i, argv, s->pw)); } if (options.use_login) { @@ -1430,7 +1509,7 @@ if (strcmp(subsys, options.subsystem_name[i]) == 0) { prog = options.subsystem_command[i]; cmd = options.subsystem_args[i]; - if (!strcmp("internal-sftp", prog)) { + if (!strcmp(INTERNAL_SFTP_NAME, prog)) { s->is_subsystem = SUBSYSTEM_INT_SFTP; } else if (stat(prog, &st) < 0) { error("subsystem: cannot stat %s: %s", prog,