[PATCH] sshd: Add ChangeDirectory configuration directive
Étienne Buira
etienne.buira at gmail.com
Sat Jun 13 22:08:51 AEST 2015
Directive described in sshd_config (5).
Also remove chdir warning skip code both because this can be handled
with more flexibility with this added directive and were broken since
a56086b9903b62c1c4fdedf01b68338fe4dc90e4.
---
regress/Makefile | 5 +++--
regress/sftp-chdir.sh | 30 ++++++++++++++++++++++++++++++
servconf.c | 15 ++++++++++++++-
servconf.h | 2 ++
session.c | 23 +++++++++++++++--------
sshd_config | 1 +
sshd_config.5 | 16 +++++++++++++++-
7 files changed, 80 insertions(+), 12 deletions(-)
create mode 100644 regress/sftp-chdir.sh
diff --git a/regress/Makefile b/regress/Makefile
index cba83f4..395241a 100644
--- a/regress/Makefile
+++ b/regress/Makefile
@@ -11,7 +11,7 @@ prep:
clean:
for F in $(CLEANFILES); do rm -f $(OBJ)$$F; done
- test -z "${SUDO}" || ${SUDO} rm -f ${SUDO_CLEAN}
+ test -z "${SUDO}" || ${SUDO} rm -df ${SUDO_CLEAN}
rm -rf $(OBJ).putty
distclean: clean
@@ -43,6 +43,7 @@ LTESTS= connect \
scp \
sftp \
sftp-chroot \
+ sftp-chdir \
sftp-cmds \
sftp-badcmds \
sftp-batch \
@@ -107,7 +108,7 @@ CLEANFILES= t2.out t3.out t6.out1 t6.out2 t7.out t7.out.pub copy.1 copy.2 \
key.ed25519-512.pub netcat host_krl_* host_revoked_* \
kh.* user_*key* agent-key.* known_hosts.* hkr.*
-SUDO_CLEAN+= /var/run/testdata_${USER} /var/run/keycommand_${USER}
+SUDO_CLEAN+= /var/run/testdata_${USER} /var/run/testlanddir_${USER}{/testdata_${USER},} /var/run/keycommand_${USER}
# Enable all malloc(3) randomisations and checks
TEST_ENV= "MALLOC_OPTIONS=AFGJPRX"
diff --git a/regress/sftp-chdir.sh b/regress/sftp-chdir.sh
new file mode 100644
index 0000000..46f5759
--- /dev/null
+++ b/regress/sftp-chdir.sh
@@ -0,0 +1,30 @@
+# $OpenBSD: sftp-chroot.sh,v 1.4 2014/01/20 00:00:30 dtucker Exp $
+# Placed in the Public Domain.
+
+tid="sftp in chroot with chdir"
+
+CHROOT=/var/run
+CHDIR=testlanddir_${USER}
+CHDIROUT=${CHROOT}/${CHDIR}
+FILENAME=testdata_${USER}
+PRIVDATA=${CHDIROUT}/${FILENAME}
+
+if [ -z "$SUDO" ]; then
+ echo "skipped: need SUDO to create file in /var/run, test won't work without"
+ exit 0
+fi
+
+$SUDO sh -c "mkdir -p $CHDIROUT && echo mekmitastdigoat > $PRIVDATA" || \
+ fatal "create $PRIVDATA failed"
+
+start_sshd -oChrootDirectory=$CHROOT -oForceCommand="internal-sftp" -oChangeDirectory=$CHDIR
+
+verbose "test $tid: get"
+${SFTP} -S "$SSH" -F $OBJ/ssh_config host:${FILENAME} $COPY \
+ >>$TEST_REGRESS_LOGFILE 2>&1 || \
+ fatal "Fetch ${FILENAME} failed"
+cmp $PRIVDATA $COPY || fail "$PRIVDATA $COPY differ"
+
+$SUDO rm $PRIVDATA
+$SUDO rmdir $CHDIROUT
+
diff --git a/servconf.c b/servconf.c
index eb32db0..6812c94 100644
--- a/servconf.c
+++ b/servconf.c
@@ -167,6 +167,7 @@ initialize_server_options(ServerOptions *options)
options->ip_qos_bulk = -1;
options->version_addendum = NULL;
options->fingerprint_hash = -1;
+ options->change_directory = NULL;
}
/* Returns 1 if a string option is unset or set to "none" or 0 otherwise. */
@@ -411,7 +412,7 @@ typedef enum {
sAuthorizedKeysCommand, sAuthorizedKeysCommandUser,
sAuthenticationMethods, sHostKeyAgent, sPermitUserRC,
sStreamLocalBindMask, sStreamLocalBindUnlink,
- sAllowStreamLocalForwarding, sFingerprintHash,
+ sAllowStreamLocalForwarding, sFingerprintHash, sChangeDirectory,
sDeprecated, sUnsupported
} ServerOpCodes;
@@ -549,6 +550,7 @@ static struct {
{ "streamlocalbindunlink", sStreamLocalBindUnlink, SSHCFG_ALL },
{ "allowstreamlocalforwarding", sAllowStreamLocalForwarding, SSHCFG_ALL },
{ "fingerprinthash", sFingerprintHash, SSHCFG_GLOBAL },
+ { "changedirectory", sChangeDirectory, SSHCFG_ALL },
{ NULL, sBadOption, 0 }
};
@@ -1826,6 +1828,15 @@ process_server_config_line(ServerOptions *options, char *line,
options->fingerprint_hash = value;
break;
+ case sChangeDirectory:
+ arg = strdelim(&cp);
+ if (!arg || *arg == '\0')
+ fatal("%s line %d: missing directory name.",
+ filename, linenum);
+ if (*activep && !options->change_directory)
+ options->change_directory = xstrdup(arg);
+ break;
+
case sDeprecated:
logit("%s line %d: Deprecated option %s",
filename, linenum, arg);
@@ -2007,6 +2018,7 @@ copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth)
M_CP_STROPT(adm_forced_command);
M_CP_STROPT(chroot_directory);
+ M_CP_STROPT(change_directory);
}
#undef M_CP_INTOPT
@@ -2281,6 +2293,7 @@ dump_config(ServerOptions *o)
o->hostbased_key_types : KEX_DEFAULT_PK_ALG);
dump_cfg_string(sPubkeyAcceptedKeyTypes, o->pubkey_key_types ?
o->pubkey_key_types : KEX_DEFAULT_PK_ALG);
+ dump_cfg_string(sChangeDirectory, o->change_directory);
/* string arguments requiring a lookup */
dump_cfg_string(sLogLevel, log_level_name(o->log_level));
diff --git a/servconf.h b/servconf.h
index 606d80c..02089e1 100644
--- a/servconf.h
+++ b/servconf.h
@@ -194,6 +194,8 @@ typedef struct {
char *auth_methods[MAX_AUTH_METHODS];
int fingerprint_hash;
+
+ char *change_directory;
} ServerOptions;
/* Information about the incoming connection as used by Match */
diff --git a/session.c b/session.c
index 5a64715..6955737 100644
--- a/session.c
+++ b/session.c
@@ -1670,6 +1670,7 @@ do_child(Session *s, const char *command)
char *argv[ARGV_MAX];
const char *shell, *shell0, *hostname = NULL;
struct passwd *pw = s->pw;
+ char *landingdir;
int r = 0;
/* remove hostkey from the child's memory */
@@ -1784,20 +1785,26 @@ do_child(Session *s, const char *command)
}
#endif
- /* Change current directory to the user's home directory. */
- if (chdir(pw->pw_dir) < 0) {
- /* Suppress missing homedir warning for chroot case */
+ /* Change current directory to landing directory (either home or
+ * set by config). */
+ if (!options.change_directory || !strcasecmp(options.change_directory, "none"))
+ landingdir = pw->pw_dir;
+ else
+ landingdir = percent_expand(options.change_directory,
+ "h", pw->pw_dir, "u", pw->pw_name, (char*)NULL);
+ if (chdir(landingdir) < 0) {
#ifdef HAVE_LOGIN_CAP
r = login_getcapbool(lc, "requirehome", 0);
#endif
- if (r || options.chroot_directory == NULL ||
- strcasecmp(options.chroot_directory, "none") == 0)
- fprintf(stderr, "Could not chdir to home "
- "directory %s: %s\n", pw->pw_dir,
- strerror(errno));
+ fprintf(stderr, "Could not chdir to landing "
+ "directory %s: %s\n", landingdir,
+ strerror(errno));
+
if (r)
exit(1);
}
+ if (options.change_directory && strcasecmp(options.change_directory, "none"))
+ free(landingdir);
closefrom(STDERR_FILENO + 1);
diff --git a/sshd_config b/sshd_config
index cf7d8e1..e850f0d 100644
--- a/sshd_config
+++ b/sshd_config
@@ -118,6 +118,7 @@ UsePrivilegeSeparation sandbox # Default for new installations.
#PermitTunnel no
#ChrootDirectory none
#VersionAddendum none
+#ChangeDirectory none
# no default banner path
#Banner none
diff --git a/sshd_config.5 b/sshd_config.5
index 5ab4318..539c229 100644
--- a/sshd_config.5
+++ b/sshd_config.5
@@ -378,6 +378,17 @@ PAM or through authentication styles supported in
.Xr login.conf 5 )
The default is
.Dq yes .
+.It Cm ChangeDirectory
+Specifies the pathname of a directory to
+.Xr chdir 2
+to after authentication, and after entering the chroot if
+.Cm ChrootDirectory
+were specified.
+.Pp
+The pathname 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.
.It Cm ChrootDirectory
Specifies the pathname of a directory to
.Xr chroot 2
@@ -388,7 +399,9 @@ checks that all components of the pathname are root-owned directories
which are not writable by any other user or group.
After the chroot,
.Xr sshd 8
-changes the working directory to the user's home directory.
+changes the working directory to the user's home directory (by default), or
+to directory specified with
+.Cm ChangeDirectory .
.Pp
The pathname may contain the following tokens that are expanded at runtime once
the connecting user has been authenticated: %% is replaced by a literal '%',
@@ -1041,6 +1054,7 @@ Available keywords are
.Cm AuthorizedKeysFile ,
.Cm AuthorizedPrincipalsFile ,
.Cm Banner ,
+.Cm ChangeDirectory ,
.Cm ChrootDirectory ,
.Cm DenyGroups ,
.Cm DenyUsers ,
--
2.0.5
More information about the openssh-unix-dev
mailing list