- djm@cvs.openbsd.org 2008/02/08 23:24:07

[servconf.c servconf.h session.c sftp-server.c sftp.h sshd_config]
     [sshd_config.5]
     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@
This commit is contained in:
Damien Miller 2008-02-10 22:40:12 +11:00
parent dfc24258a7
commit d8cb1f184f
9 changed files with 220 additions and 31 deletions

View File

@ -1,4 +1,4 @@
# $Id: Makefile.in,v 1.285 2007/06/11 04:01:42 djm Exp $
# $Id: Makefile.in,v 1.286 2008/02/10 11:40:12 djm Exp $
# uncomment if you run a non bourne compatable shell. Ie. csh
#SHELL = @SH@
@ -156,8 +156,8 @@ ssh-keysign$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keysign.o
ssh-keyscan$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keyscan.o
$(LD) -o $@ ssh-keyscan.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh $(LIBS)
sftp-server$(EXEEXT): $(LIBCOMPAT) libssh.a sftp.o sftp-common.o sftp-server.o
$(LD) -o $@ sftp-server.o sftp-common.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS)
sftp-server$(EXEEXT): $(LIBCOMPAT) libssh.a sftp.o sftp-common.o sftp-server.o sftp-server-main.o
$(LD) -o $@ sftp-server.o sftp-common.o sftp-server-main.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS)
sftp$(EXEEXT): $(LIBCOMPAT) libssh.a sftp.o sftp-client.o sftp-common.o sftp-glob.o progressmeter.o
$(LD) -o $@ progressmeter.o sftp.o sftp-client.o sftp-common.o sftp-glob.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) $(LIBEDIT)

View File

@ -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
@ -122,6 +122,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
@ -291,7 +292,7 @@ typedef enum {
sHostbasedUsesNameFromPacketOnly, sClientAliveInterval,
sClientAliveCountMax, sAuthorizedKeysFile, sAuthorizedKeysFile2,
sGssAuthentication, sGssCleanupCreds, sAcceptEnv, sPermitTunnel,
sMatch, sPermitOpen, sForceCommand,
sMatch, sPermitOpen, sForceCommand, sChrootDirectory,
sUsePrivilegeSeparation,
sDeprecated, sUnsupported
} ServerOpCodes;
@ -403,6 +404,7 @@ static struct {
{ "match", sMatch, SSHCFG_ALL },
{ "permitopen", sPermitOpen, SSHCFG_ALL },
{ "forcecommand", sForceCommand, SSHCFG_ALL },
{ "chrootdirectory", sChrootDirectory, SSHCFG_ALL },
{ NULL, sBadOption, 0 }
};
@ -1147,6 +1149,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:
@ -1255,6 +1258,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);
@ -1363,6 +1370,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

View File

@ -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>
@ -141,6 +141,8 @@ typedef struct {
int permit_tun;
int num_permitted_opens;
char *chroot_directory;
} ServerOptions;
void initialize_server_options(ServerOptions *);

104
session.c
View File

@ -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
@ -84,6 +84,7 @@
#include "sshlogin.h"
#include "serverloop.h"
#include "canohost.h"
#include "misc.h"
#include "session.h"
#include "kex.h"
#include "monitor_wrap.h"
@ -93,6 +94,9 @@
#include <kafs.h>
#endif
/* Magic name for internal sftp-server */
#define INTERNAL_SFTP_NAME "internal-sftp"
/* func */
Session *session_new(void);
@ -130,7 +134,7 @@ extern Buffer loginmsg;
const char *original_command = NULL;
/* data */
#define MAX_SESSIONS 10
#define MAX_SESSIONS 20
Session sessions[MAX_SESSIONS];
#define SUBSYSTEM_NONE 0
@ -688,13 +692,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);
}
@ -710,7 +718,6 @@ do_exec(Session *s, const char *command)
PRIVSEP(audit_run_command(shell));
}
#endif
if (s->ttyfd != -1)
do_exec_pty(s, command);
else
@ -1293,6 +1300,61 @@ 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)
@ -1324,7 +1386,7 @@ do_setusercontext(struct passwd *pw)
}
# endif /* USE_PAM */
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);
}
@ -1347,13 +1409,13 @@ do_setusercontext(struct passwd *pw)
exit(1);
}
endgrent();
#ifdef GSSAPI
# ifdef GSSAPI
if (options.gss_authentication) {
temporarily_use_uid(pw);
ssh_gssapi_storecreds();
restore_uid();
}
#endif
# endif
# ifdef USE_PAM
/*
* PAM credentials may take the form of supplementary groups.
@ -1371,11 +1433,29 @@ do_setusercontext(struct passwd *pw)
# ifdef _AIX
aix_usrinfo(pw);
# endif /* _AIX */
#ifdef USE_LIBIAF
# ifdef USE_LIBIAF
if (set_id(pw->pw_name) != 0) {
exit(1);
}
#endif /* USE_LIBIAF */
# endif /* USE_LIBIAF */
#endif
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);
}
#ifdef HAVE_LOGIN_CAP
if (setusercontext(lc, pw, pw->pw_uid, LOGIN_SETUSER) < 0) {
perror("unable to set user context (setuser)");
exit(1);
}
#else
/* Permanently switch to the desired uid. */
permanently_set_uid(pw);
#endif
@ -1625,7 +1705,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) {
@ -1900,7 +1980,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,

50
sftp-server-main.c Normal file
View File

@ -0,0 +1,50 @@
/* $OpenBSD: */
/*
* Copyright (c) 2008 Markus Friedl. All rights reserved.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "includes.h"
#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)
{
sftp_server_cleanup_exit(i);
}
int
main(int argc, char **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));
}

View File

@ -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.
*
@ -1219,7 +1219,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;
@ -1277,11 +1277,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);

6
sftp.h
View File

@ -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;

View File

@ -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.
@ -102,6 +102,7 @@ Protocol 2
#PidFile /var/run/sshd.pid
#MaxStartups 10
#PermitTunnel no
#ChrootDirectory none
# no default banner path
#Banner none

View File

@ -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