summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDamien Miller <djm@cvs.openbsd.org>2008-02-08 23:24:09 +0000
committerDamien Miller <djm@cvs.openbsd.org>2008-02-08 23:24:09 +0000
commitb61be46adafe7676e6b3c1ca4903b6c64b007db1 (patch)
tree667e7da24f8446b750b5b3162f7732b4968a72d0
parentb425483dcf67a41ad5d6c58871e3e43a9b582db9 (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.c12
-rw-r--r--usr.bin/ssh/servconf.h4
-rw-r--r--usr.bin/ssh/session.c93
-rw-r--r--usr.bin/ssh/sftp-server-main.c16
-rw-r--r--usr.bin/ssh/sftp-server.c13
-rw-r--r--usr.bin/ssh/sftp.h6
-rw-r--r--usr.bin/ssh/sshd_config3
-rw-r--r--usr.bin/ssh/sshd_config.554
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