diff options
author | Damien Miller <djm@cvs.openbsd.org> | 2008-02-08 23:24:09 +0000 |
---|---|---|
committer | Damien Miller <djm@cvs.openbsd.org> | 2008-02-08 23:24:09 +0000 |
commit | b61be46adafe7676e6b3c1ca4903b6c64b007db1 (patch) | |
tree | 667e7da24f8446b750b5b3162f7732b4968a72d0 | |
parent | b425483dcf67a41ad5d6c58871e3e43a9b582db9 (diff) |
add sshd_config ChrootDirectory option to chroot(2) users to a directory and
tweak internal sftp server to work with it (no special files in chroot
required). ok markus@
-rw-r--r-- | usr.bin/ssh/servconf.c | 12 | ||||
-rw-r--r-- | usr.bin/ssh/servconf.h | 4 | ||||
-rw-r--r-- | usr.bin/ssh/session.c | 93 | ||||
-rw-r--r-- | usr.bin/ssh/sftp-server-main.c | 16 | ||||
-rw-r--r-- | usr.bin/ssh/sftp-server.c | 13 | ||||
-rw-r--r-- | usr.bin/ssh/sftp.h | 6 | ||||
-rw-r--r-- | usr.bin/ssh/sshd_config | 3 | ||||
-rw-r--r-- | usr.bin/ssh/sshd_config.5 | 54 |
8 files changed, 175 insertions, 26 deletions
diff --git a/usr.bin/ssh/servconf.c b/usr.bin/ssh/servconf.c index 81bf8ef3650..9282d43ebd9 100644 --- a/usr.bin/ssh/servconf.c +++ b/usr.bin/ssh/servconf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: servconf.c,v 1.175 2008/01/01 09:27:33 dtucker Exp $ */ +/* $OpenBSD: servconf.c,v 1.176 2008/02/08 23:24:08 djm Exp $ */ /* * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * All rights reserved @@ -115,6 +115,7 @@ initialize_server_options(ServerOptions *options) options->permit_tun = -1; options->num_permitted_opens = -1; options->adm_forced_command = NULL; + options->chroot_directory = NULL; } void @@ -266,7 +267,7 @@ typedef enum { sHostbasedUsesNameFromPacketOnly, sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, sAuthorizedKeysFile2, sGssAuthentication, sGssCleanupCreds, sAcceptEnv, sPermitTunnel, - sMatch, sPermitOpen, sForceCommand, + sMatch, sPermitOpen, sForceCommand, sChrootDirectory, sUsePrivilegeSeparation, sDeprecated, sUnsupported } ServerOpCodes; @@ -366,6 +367,7 @@ static struct { { "match", sMatch, SSHCFG_ALL }, { "permitopen", sPermitOpen, SSHCFG_ALL }, { "forcecommand", sForceCommand, SSHCFG_ALL }, + { "chrootdirectory", sChrootDirectory, SSHCFG_ALL }, { NULL, sBadOption, 0 } }; @@ -1104,6 +1106,7 @@ parse_flag: case sBanner: charptr = &options->banner; goto parse_filename; + /* * These options can contain %X options expanded at * connect time, so that you can specify paths like: @@ -1212,6 +1215,10 @@ parse_flag: options->adm_forced_command = xstrdup(cp + len); return 0; + case sChrootDirectory: + charptr = &options->chroot_directory; + goto parse_filename; + case sDeprecated: logit("%s line %d: Deprecated option %s", filename, linenum, arg); @@ -1320,6 +1327,7 @@ copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth) if (preauth) return; M_CP_STROPT(adm_forced_command); + M_CP_STROPT(chroot_directory); } #undef M_CP_INTOPT diff --git a/usr.bin/ssh/servconf.h b/usr.bin/ssh/servconf.h index dd5bc832a9a..46e732d2588 100644 --- a/usr.bin/ssh/servconf.h +++ b/usr.bin/ssh/servconf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: servconf.h,v 1.80 2007/02/19 10:45:58 dtucker Exp $ */ +/* $OpenBSD: servconf.h,v 1.81 2008/02/08 23:24:08 djm Exp $ */ /* * Author: Tatu Ylonen <ylo@cs.hut.fi> @@ -139,6 +139,8 @@ typedef struct { int permit_tun; int num_permitted_opens; + + char *chroot_directory; } ServerOptions; void initialize_server_options(ServerOptions *); diff --git a/usr.bin/ssh/session.c b/usr.bin/ssh/session.c index 2592b425073..6c4451c179f 100644 --- a/usr.bin/ssh/session.c +++ b/usr.bin/ssh/session.c @@ -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 <ylo@cs.hut.fi>, 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 <kafs.h> #endif +/* Magic name for internal sftp-server */ +#define INTERNAL_SFTP_NAME "internal-sftp" + /* func */ Session *session_new(void); @@ -550,13 +554,17 @@ do_exec(Session *s, const char *command) 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 @@ do_exec(Session *s, const char *command) restore_uid(); } #endif - if (s->ttyfd != -1) do_exec_pty(s, command); else @@ -950,16 +957,88 @@ do_nologin(struct passwd *pw) } } +/* + * 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 @@ do_child(Session *s, const char *command) 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 @@ session_subsystem_req(Session *s) 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, diff --git a/usr.bin/ssh/sftp-server-main.c b/usr.bin/ssh/sftp-server-main.c index 993455b6c0a..2a4c4f95093 100644 --- a/usr.bin/ssh/sftp-server-main.c +++ b/usr.bin/ssh/sftp-server-main.c @@ -16,10 +16,14 @@ */ #include <sys/types.h> +#include <pwd.h> #include <stdarg.h> +#include <stdio.h> +#include <unistd.h> #include "log.h" #include "sftp.h" +#include "misc.h" void cleanup_exit(int i) @@ -30,5 +34,15 @@ cleanup_exit(int i) int main(int argc, char **argv) { - return (sftp_server_main(argc, argv)); + struct passwd *user_pw; + + /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ + sanitise_stdfd(); + + if ((user_pw = getpwuid(getuid())) == NULL) { + fprintf(stderr, "No user found for uid %lu", (u_long)getuid()); + return 1; + } + + return (sftp_server_main(argc, argv, user_pw)); } diff --git a/usr.bin/ssh/sftp-server.c b/usr.bin/ssh/sftp-server.c index dfc85aec454..8141f91011a 100644 --- a/usr.bin/ssh/sftp-server.c +++ b/usr.bin/ssh/sftp-server.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sftp-server.c,v 1.76 2008/02/04 21:53:00 markus Exp $ */ +/* $OpenBSD: sftp-server.c,v 1.77 2008/02/08 23:24:07 djm Exp $ */ /* * Copyright (c) 2000-2004 Markus Friedl. All rights reserved. * @@ -1198,7 +1198,7 @@ sftp_server_usage(void) } int -sftp_server_main(int argc, char **argv) +sftp_server_main(int argc, char **argv, struct passwd *user_pw) { fd_set *rset, *wset; int in, out, max, ch, skipargs = 0, log_stderr = 0; @@ -1209,9 +1209,6 @@ sftp_server_main(int argc, char **argv) extern char *optarg; extern char *__progname; - /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ - sanitise_stdfd(); - log_init(__progname, log_level, log_facility, log_stderr); while (!skipargs && (ch = getopt(argc, argv, "C:f:l:che")) != -1) { @@ -1255,11 +1252,7 @@ sftp_server_main(int argc, char **argv) } else client_addr = xstrdup("UNKNOWN"); - if ((pw = getpwuid(getuid())) == NULL) { - error("No user found for uid %lu", (u_long)getuid()); - sftp_server_cleanup_exit(255); - } - pw = pwcopy(pw); + pw = pwcopy(user_pw); logit("session opened for local user %s from [%s]", pw->pw_name, client_addr); diff --git a/usr.bin/ssh/sftp.h b/usr.bin/ssh/sftp.h index 12b9cc05680..0835da6ed41 100644 --- a/usr.bin/ssh/sftp.h +++ b/usr.bin/ssh/sftp.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sftp.h,v 1.6 2008/02/04 21:53:00 markus Exp $ */ +/* $OpenBSD: sftp.h,v 1.7 2008/02/08 23:24:07 djm Exp $ */ /* * Copyright (c) 2001 Markus Friedl. All rights reserved. @@ -91,5 +91,7 @@ #define SSH2_FX_OP_UNSUPPORTED 8 #define SSH2_FX_MAX 8 -int sftp_server_main(int, char **); +struct passwd; + +int sftp_server_main(int, char **, struct passwd *); void sftp_server_cleanup_exit(int) __dead; diff --git a/usr.bin/ssh/sshd_config b/usr.bin/ssh/sshd_config index 1bf22c6b6f4..c24645e6d5f 100644 --- a/usr.bin/ssh/sshd_config +++ b/usr.bin/ssh/sshd_config @@ -1,4 +1,4 @@ -# $OpenBSD: sshd_config,v 1.76 2007/08/23 03:22:16 djm Exp $ +# $OpenBSD: sshd_config,v 1.77 2008/02/08 23:24:07 djm Exp $ # This is the sshd server system-wide configuration file. See # sshd_config(5) for more information. @@ -89,6 +89,7 @@ Protocol 2 #PidFile /var/run/sshd.pid #MaxStartups 10 #PermitTunnel no +#ChrootDirectory none # no default banner path #Banner none diff --git a/usr.bin/ssh/sshd_config.5 b/usr.bin/ssh/sshd_config.5 index 22204d7066f..07029c123e4 100644 --- a/usr.bin/ssh/sshd_config.5 +++ b/usr.bin/ssh/sshd_config.5 @@ -34,8 +34,8 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: sshd_config.5,v 1.79 2008/01/01 09:27:33 dtucker Exp $ -.Dd $Mdocdate: January 1 2008 $ +.\" $OpenBSD: sshd_config.5,v 1.80 2008/02/08 23:24:07 djm Exp $ +.Dd $Mdocdate: February 8 2008 $ .Dt SSHD_CONFIG 5 .Os .Sh NAME @@ -173,6 +173,45 @@ All authentication styles from are supported. The default is .Dq yes . +.It Cm ChrootDirectory +Specifies a path to +.Xr chroot 2 +to after authentication. +This path, and all its components, must be root-owned directories that are +not writable by any other user or group. +.Pp +The path may contain the following tokens that are expanded at runtime once +the connecting user has been authenticated: %% is replaced by a literal '%', +%h is replaced by the home directory of the user being authenticated, and +%u is replaced by the username of that user. +.Pp +The +.Cm ChrootDirectory +must contain the necessary files and directories to support the +users' session. +For an interactive session this requires at least a shell, typically +.Xr sh 1 , +and basic +.Pa /dev +nodes such as +.Xr null 4 , +.Xr zero 4 , +.Xr stdin 4 , +.Xr stdout 4 , +.Xr stderr 4 , +.Xr arandom 4 +and +.Xr tty 4 +devices. +For file transfer sessions using +.Dq sftp , +no additional configuration of the environment is necessary if the +in-process sftp server is used (see +.Cm Subsystem +for details. +.Pp +The default is not to +.Xr chroot 2 . .It Cm Ciphers Specifies the ciphers allowed for protocol version 2. Multiple ciphers must be comma-separated. @@ -740,11 +779,22 @@ The default is Configures an external subsystem (e.g. file transfer daemon). Arguments should be a subsystem name and a command (with optional arguments) to execute upon subsystem request. +.Pp The command .Xr sftp-server 8 implements the .Dq sftp file transfer subsystem. +.Pp +Alternately the name +.Dq internal-sftp +implements an in-process +.Dq sftp +server. +This may simplify configurations using +.Cm ChrootDirectory +to force a different filesystem root on clients. +.Pp By default no subsystems are defined. Note that this option applies to protocol version 2 only. .It Cm SyslogFacility |