summaryrefslogtreecommitdiffstats
path: root/crypto/openssh/session.c
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/openssh/session.c')
-rw-r--r--crypto/openssh/session.c616
1 files changed, 425 insertions, 191 deletions
diff --git a/crypto/openssh/session.c b/crypto/openssh/session.c
index a752e72..4dd390b 100644
--- a/crypto/openssh/session.c
+++ b/crypto/openssh/session.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: session.c,v 1.220 2006/10/09 23:36:11 djm Exp $ */
+/* $OpenBSD: session.c,v 1.241 2008/06/16 13:22:53 dtucker Exp $ */
/*
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
* All rights reserved
@@ -60,6 +60,7 @@ __RCSID("$FreeBSD$");
#include <string.h>
#include <unistd.h>
+#include "openbsd-compat/sys-queue.h"
#include "xmalloc.h"
#include "ssh.h"
#include "ssh1.h"
@@ -85,9 +86,11 @@ __RCSID("$FreeBSD$");
#include "sshlogin.h"
#include "serverloop.h"
#include "canohost.h"
+#include "misc.h"
#include "session.h"
#include "kex.h"
#include "monitor_wrap.h"
+#include "sftp.h"
#if defined(KRB5) && defined(USE_AFS)
#include <kafs.h>
@@ -96,13 +99,13 @@ __RCSID("$FreeBSD$");
/* func */
Session *session_new(void);
-void session_set_fds(Session *, int, int, int);
+void session_set_fds(Session *, int, int, int, int);
void session_pty_cleanup(Session *);
void session_proctitle(Session *);
int session_setup_x11fwd(Session *);
-void do_exec_pty(Session *, const char *);
-void do_exec_no_pty(Session *, const char *);
-void do_exec(Session *, const char *);
+int do_exec_pty(Session *, const char *);
+int do_exec_no_pty(Session *, const char *);
+int do_exec(Session *, const char *);
void do_login(Session *, const char *);
#ifdef LOGIN_NEEDS_UTMPX
static void do_pre_login(Session *s);
@@ -130,8 +133,13 @@ extern Buffer loginmsg;
const char *original_command = NULL;
/* data */
-#define MAX_SESSIONS 10
-Session sessions[MAX_SESSIONS];
+static int sessions_first_unused = -1;
+static int sessions_nalloc = 0;
+static Session *sessions = NULL;
+
+#define SUBSYSTEM_NONE 0
+#define SUBSYSTEM_EXT 1
+#define SUBSYSTEM_INT_SFTP 2
#ifdef HAVE_LOGIN_CAP
login_cap_t *lc;
@@ -161,7 +169,7 @@ static int
auth_input_request_forwarding(struct passwd * pw)
{
Channel *nc;
- int sock;
+ int sock = -1;
struct sockaddr_un sunaddr;
if (auth_sock_name != NULL) {
@@ -173,43 +181,48 @@ auth_input_request_forwarding(struct passwd * pw)
temporarily_use_uid(pw);
/* Allocate a buffer for the socket name, and format the name. */
- auth_sock_name = xmalloc(MAXPATHLEN);
- auth_sock_dir = xmalloc(MAXPATHLEN);
- strlcpy(auth_sock_dir, "/tmp/ssh-XXXXXXXXXX", MAXPATHLEN);
+ auth_sock_dir = xstrdup("/tmp/ssh-XXXXXXXXXX");
/* Create private directory for socket */
if (mkdtemp(auth_sock_dir) == NULL) {
packet_send_debug("Agent forwarding disabled: "
"mkdtemp() failed: %.100s", strerror(errno));
restore_uid();
- xfree(auth_sock_name);
xfree(auth_sock_dir);
- auth_sock_name = NULL;
auth_sock_dir = NULL;
- return 0;
+ goto authsock_err;
}
- snprintf(auth_sock_name, MAXPATHLEN, "%s/agent.%ld",
- auth_sock_dir, (long) getpid());
+
+ xasprintf(&auth_sock_name, "%s/agent.%ld",
+ auth_sock_dir, (long) getpid());
/* Create the socket. */
sock = socket(AF_UNIX, SOCK_STREAM, 0);
- if (sock < 0)
- packet_disconnect("socket: %.100s", strerror(errno));
+ if (sock < 0) {
+ error("socket: %.100s", strerror(errno));
+ restore_uid();
+ goto authsock_err;
+ }
/* Bind it to the name. */
memset(&sunaddr, 0, sizeof(sunaddr));
sunaddr.sun_family = AF_UNIX;
strlcpy(sunaddr.sun_path, auth_sock_name, sizeof(sunaddr.sun_path));
- if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0)
- packet_disconnect("bind: %.100s", strerror(errno));
+ if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0) {
+ error("bind: %.100s", strerror(errno));
+ restore_uid();
+ goto authsock_err;
+ }
/* Restore the privileged uid. */
restore_uid();
/* Start listening on the socket. */
- if (listen(sock, SSH_LISTEN_BACKLOG) < 0)
- packet_disconnect("listen: %.100s", strerror(errno));
+ if (listen(sock, SSH_LISTEN_BACKLOG) < 0) {
+ error("listen: %.100s", strerror(errno));
+ goto authsock_err;
+ }
/* Allocate a channel for the authentication agent socket. */
nc = channel_new("auth socket",
@@ -218,6 +231,19 @@ auth_input_request_forwarding(struct passwd * pw)
0, "auth socket", 1);
strlcpy(nc->path, auth_sock_name, sizeof(nc->path));
return 1;
+
+ authsock_err:
+ if (auth_sock_name != NULL)
+ xfree(auth_sock_name);
+ if (auth_sock_dir != NULL) {
+ rmdir(auth_sock_dir);
+ xfree(auth_sock_dir);
+ }
+ if (sock != -1)
+ close(sock);
+ auth_sock_name = NULL;
+ auth_sock_dir = NULL;
+ return 0;
}
static void
@@ -330,7 +356,8 @@ do_authenticated1(Authctxt *authctxt)
break;
case SSH_CMSG_AGENT_REQUEST_FORWARDING:
- if (no_agent_forwarding_flag || compat13) {
+ if (!options.allow_agent_forwarding ||
+ no_agent_forwarding_flag || compat13) {
debug("Authentication agent forwarding not permitted for this authentication.");
break;
}
@@ -366,10 +393,14 @@ do_authenticated1(Authctxt *authctxt)
if (type == SSH_CMSG_EXEC_CMD) {
command = packet_get_string(&dlen);
debug("Exec command '%.500s'", command);
- do_exec(s, command);
+ if (do_exec(s, command) != 0)
+ packet_disconnect(
+ "command execution failed");
xfree(command);
} else {
- do_exec(s, NULL);
+ if (do_exec(s, NULL) != 0)
+ packet_disconnect(
+ "shell execution failed");
}
packet_check_eom();
session_close(s);
@@ -394,46 +425,84 @@ do_authenticated1(Authctxt *authctxt)
}
}
+#define USE_PIPES
/*
* This is called to fork and execute a command when we have no tty. This
* will call do_child from the child, and server_loop from the parent after
* setting up file descriptors and such.
*/
-void
+int
do_exec_no_pty(Session *s, const char *command)
{
pid_t pid;
#ifdef USE_PIPES
int pin[2], pout[2], perr[2];
+
/* Allocate pipes for communicating with the program. */
- if (pipe(pin) < 0 || pipe(pout) < 0 || pipe(perr) < 0)
- packet_disconnect("Could not create pipes: %.100s",
- strerror(errno));
-#else /* USE_PIPES */
+ if (pipe(pin) < 0) {
+ error("%s: pipe in: %.100s", __func__, strerror(errno));
+ return -1;
+ }
+ if (pipe(pout) < 0) {
+ error("%s: pipe out: %.100s", __func__, strerror(errno));
+ close(pin[0]);
+ close(pin[1]);
+ return -1;
+ }
+ if (pipe(perr) < 0) {
+ error("%s: pipe err: %.100s", __func__, strerror(errno));
+ close(pin[0]);
+ close(pin[1]);
+ close(pout[0]);
+ close(pout[1]);
+ return -1;
+ }
+#else
int inout[2], err[2];
+
/* Uses socket pairs to communicate with the program. */
- if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) < 0 ||
- socketpair(AF_UNIX, SOCK_STREAM, 0, err) < 0)
- packet_disconnect("Could not create socket pairs: %.100s",
- strerror(errno));
-#endif /* USE_PIPES */
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) < 0) {
+ error("%s: socketpair #1: %.100s", __func__, strerror(errno));
+ return -1;
+ }
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, err) < 0) {
+ error("%s: socketpair #2: %.100s", __func__, strerror(errno));
+ close(inout[0]);
+ close(inout[1]);
+ return -1;
+ }
+#endif
+
if (s == NULL)
fatal("do_exec_no_pty: no session");
session_proctitle(s);
-#if defined(USE_PAM)
- if (options.use_pam && !use_privsep)
- do_pam_setcred(1);
-#endif /* USE_PAM */
-
/* Fork the child. */
- if ((pid = fork()) == 0) {
+ switch ((pid = fork())) {
+ case -1:
+ error("%s: fork: %.100s", __func__, strerror(errno));
+#ifdef USE_PIPES
+ close(pin[0]);
+ close(pin[1]);
+ close(pout[0]);
+ close(pout[1]);
+ close(perr[0]);
+ close(perr[1]);
+#else
+ close(inout[0]);
+ close(inout[1]);
+ close(err[0]);
+ close(err[1]);
+#endif
+ return -1;
+ case 0:
is_child = 1;
/* Child. Reinitialize the log since the pid has changed. */
- log_init(__progname, options.log_level, options.log_facility, log_stderr);
+ log_init(__progname, options.log_level,
+ options.log_facility, log_stderr);
/*
* Create a new session and process group since the 4.4BSD
@@ -463,7 +532,7 @@ do_exec_no_pty(Session *s, const char *command)
if (dup2(perr[1], 2) < 0)
perror("dup2 stderr");
close(perr[1]);
-#else /* USE_PIPES */
+#else
/*
* Redirect stdin, stdout, and stderr. Stdin and stdout will
* use the same socket, as some programs (particularly rdist)
@@ -473,11 +542,14 @@ do_exec_no_pty(Session *s, const char *command)
close(err[1]);
if (dup2(inout[0], 0) < 0) /* stdin */
perror("dup2 stdin");
- if (dup2(inout[0], 1) < 0) /* stdout. Note: same socket as stdin. */
+ if (dup2(inout[0], 1) < 0) /* stdout (same as stdin) */
perror("dup2 stdout");
+ close(inout[0]);
if (dup2(err[0], 2) < 0) /* stderr */
perror("dup2 stderr");
-#endif /* USE_PIPES */
+ close(err[0]);
+#endif
+
#ifdef _UNICOS
cray_init_job(s->pw); /* set up cray jid and tmpdir */
@@ -486,7 +558,10 @@ do_exec_no_pty(Session *s, const char *command)
/* Do processing for the child (exec command etc). */
do_child(s, command);
/* NOTREACHED */
+ default:
+ break;
}
+
#ifdef _UNICOS
signal(WJSIGNAL, cray_job_termination_handler);
#endif /* _UNICOS */
@@ -494,11 +569,18 @@ do_exec_no_pty(Session *s, const char *command)
if (is_winnt)
cygwin_set_impersonation_token(INVALID_HANDLE_VALUE);
#endif
- if (pid < 0)
- packet_disconnect("fork failed: %.100s", strerror(errno));
+
s->pid = pid;
/* Set interactive/non-interactive mode. */
packet_set_interactive(s->display != NULL);
+
+ /*
+ * Clear loginmsg, since it's the child's responsibility to display
+ * it to the user, otherwise multiple sessions may accumulate
+ * multiple copies of the login messages.
+ */
+ buffer_clear(&loginmsg);
+
#ifdef USE_PIPES
/* We are the parent. Close the child sides of the pipes. */
close(pin[0]);
@@ -510,35 +592,32 @@ do_exec_no_pty(Session *s, const char *command)
close(perr[0]);
perr[0] = -1;
}
- session_set_fds(s, pin[1], pout[0], perr[0]);
+ session_set_fds(s, pin[1], pout[0], perr[0], 0);
} else {
/* Enter the interactive session. */
server_loop(pid, pin[1], pout[0], perr[0]);
/* server_loop has closed pin[1], pout[0], and perr[0]. */
}
-#else /* USE_PIPES */
+#else
/* We are the parent. Close the child sides of the socket pairs. */
close(inout[0]);
close(err[0]);
/*
- * Clear loginmsg, since it's the child's responsibility to display
- * it to the user, otherwise multiple sessions may accumulate
- * multiple copies of the login messages.
- */
- buffer_clear(&loginmsg);
-
- /*
* Enter the interactive session. Note: server_loop must be able to
* handle the case that fdin and fdout are the same.
*/
if (compat20) {
- session_set_fds(s, inout[1], inout[1], s->is_subsystem ? -1 : err[1]);
+ session_set_fds(s, inout[1], inout[1],
+ s->is_subsystem ? -1 : err[1], 0);
+ if (s->is_subsystem)
+ close(err[1]);
} else {
server_loop(pid, inout[1], inout[1], err[1]);
/* server_loop has closed inout[1] and err[1]. */
}
-#endif /* USE_PIPES */
+#endif
+ return 0;
}
/*
@@ -547,7 +626,7 @@ do_exec_no_pty(Session *s, const char *command)
* setting up file descriptors, controlling tty, updating wtmp, utmp,
* lastlog, and other such operations.
*/
-void
+int
do_exec_pty(Session *s, const char *command)
{
int fdout, ptyfd, ttyfd, ptymaster;
@@ -558,20 +637,46 @@ do_exec_pty(Session *s, const char *command)
ptyfd = s->ptyfd;
ttyfd = s->ttyfd;
-#if defined(USE_PAM)
- if (options.use_pam) {
- do_pam_set_tty(s->tty);
- if (!use_privsep)
- do_pam_setcred(1);
+ /*
+ * Create another descriptor of the pty master side for use as the
+ * standard input. We could use the original descriptor, but this
+ * simplifies code in server_loop. The descriptor is bidirectional.
+ * Do this before forking (and cleanup in the child) so as to
+ * detect and gracefully fail out-of-fd conditions.
+ */
+ if ((fdout = dup(ptyfd)) < 0) {
+ error("%s: dup #1: %s", __func__, strerror(errno));
+ close(ttyfd);
+ close(ptyfd);
+ return -1;
+ }
+ /* we keep a reference to the pty master */
+ if ((ptymaster = dup(ptyfd)) < 0) {
+ error("%s: dup #2: %s", __func__, strerror(errno));
+ close(ttyfd);
+ close(ptyfd);
+ close(fdout);
+ return -1;
}
-#endif
/* Fork the child. */
- if ((pid = fork()) == 0) {
+ switch ((pid = fork())) {
+ case -1:
+ error("%s: fork: %.100s", __func__, strerror(errno));
+ close(fdout);
+ close(ptymaster);
+ close(ttyfd);
+ close(ptyfd);
+ return -1;
+ case 0:
is_child = 1;
+ close(fdout);
+ close(ptymaster);
+
/* Child. Reinitialize the log because the pid has changed. */
- log_init(__progname, options.log_level, options.log_facility, log_stderr);
+ log_init(__progname, options.log_level,
+ options.log_facility, log_stderr);
/* Close the master side of the pseudo tty. */
close(ptyfd);
@@ -602,11 +707,16 @@ do_exec_pty(Session *s, const char *command)
do_pre_login(s);
# endif
#endif
-
- /* Do common processing for the child, such as execing the command. */
- do_child(s, command);
- /* NOTREACHED */
+ /*
+ * Do common processing for the child, such as execing
+ * the command.
+ */
+ do_child(s, command);
+ /* NOTREACHED */
+ default:
+ break;
}
+
#ifdef _UNICOS
signal(WJSIGNAL, cray_job_termination_handler);
#endif /* _UNICOS */
@@ -614,36 +724,22 @@ do_exec_pty(Session *s, const char *command)
if (is_winnt)
cygwin_set_impersonation_token(INVALID_HANDLE_VALUE);
#endif
- if (pid < 0)
- packet_disconnect("fork failed: %.100s", strerror(errno));
+
s->pid = pid;
/* Parent. Close the slave side of the pseudo tty. */
close(ttyfd);
- /*
- * Create another descriptor of the pty master side for use as the
- * standard input. We could use the original descriptor, but this
- * simplifies code in server_loop. The descriptor is bidirectional.
- */
- fdout = dup(ptyfd);
- if (fdout < 0)
- packet_disconnect("dup #1 failed: %.100s", strerror(errno));
-
- /* we keep a reference to the pty master */
- ptymaster = dup(ptyfd);
- if (ptymaster < 0)
- packet_disconnect("dup #2 failed: %.100s", strerror(errno));
- s->ptymaster = ptymaster;
-
/* Enter interactive session. */
+ s->ptymaster = ptymaster;
packet_set_interactive(1);
if (compat20) {
- session_set_fds(s, ptyfd, fdout, -1);
+ session_set_fds(s, ptyfd, fdout, -1, 1);
} else {
server_loop(pid, ptyfd, fdout, -1);
/* server_loop _has_ closed ptyfd and fdout. */
}
+ return 0;
}
#ifdef LOGIN_NEEDS_UTMPX
@@ -678,16 +774,26 @@ do_pre_login(Session *s)
* This is called to fork and execute a command. If another command is
* to be forced, execute that instead.
*/
-void
+int
do_exec(Session *s, const char *command)
{
+ int ret;
+
if (options.adm_forced_command) {
original_command = command;
command = options.adm_forced_command;
+ 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 (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);
}
@@ -702,11 +808,10 @@ do_exec(Session *s, const char *command)
PRIVSEP(audit_run_command(shell));
}
#endif
-
if (s->ttyfd != -1)
- do_exec_pty(s, command);
+ ret = do_exec_pty(s, command);
else
- do_exec_no_pty(s, command);
+ ret = do_exec_no_pty(s, command);
original_command = NULL;
@@ -716,6 +821,8 @@ do_exec(Session *s, const char *command)
* multiple copies of the login messages.
*/
buffer_clear(&loginmsg);
+
+ return ret;
}
/* administrative, login(1)-like work */
@@ -916,8 +1023,9 @@ read_environment_file(char ***env, u_int *envsize,
;
if (!*cp || *cp == '#' || *cp == '\n')
continue;
- if (strchr(cp, '\n'))
- *strchr(cp, '\n') = '\0';
+
+ cp[strcspn(cp, "\n")] = '\0';
+
value = strchr(cp, '=');
if (value == NULL) {
fprintf(stderr, "Bad line %u in %.100s\n", lineno,
@@ -1231,8 +1339,9 @@ do_rc_files(Session *s, const char *shell)
do_xauth =
s->display != NULL && s->auth_proto != NULL && s->auth_data != NULL;
- /* ignore _PATH_SSH_USER_RC for subsystems */
- if (!s->is_subsystem && (stat(_PATH_SSH_USER_RC, &st) >= 0)) {
+ /* ignore _PATH_SSH_USER_RC for subsystems and admin forced commands */
+ if (!s->is_subsystem && options.adm_forced_command == NULL &&
+ !no_user_rc && stat(_PATH_SSH_USER_RC, &st) >= 0) {
snprintf(cmd, sizeof cmd, "%s -c '%s %s'",
shell, _PATH_BSHELL, _PATH_SSH_USER_RC);
if (debug_flag)
@@ -1313,10 +1422,72 @@ 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)
{
+ char *chroot_path, *tmp;
+
+#ifdef WITH_SELINUX
+ /* Cache selinux status for later use */
+ (void)ssh_selinux_enabled();
+#endif
+
#ifndef HAVE_CYGWIN
if (getuid() == 0 || geteuid() == 0)
#endif /* HAVE_CYGWIN */
@@ -1330,21 +1501,13 @@ do_setusercontext(struct passwd *pw)
# ifdef __bsdi__
setpgid(0, 0);
# endif
-#ifdef GSSAPI
- if (options.gss_authentication) {
- temporarily_use_uid(pw);
- ssh_gssapi_storecreds();
- restore_uid();
- }
-#endif
# ifdef USE_PAM
if (options.use_pam) {
- do_pam_session();
- do_pam_setcred(0);
+ do_pam_setcred(use_privsep);
}
# endif /* USE_PAM */
if (setusercontext(lc, pw, pw->pw_uid,
- (LOGIN_SETALL & ~(LOGIN_SETENV|LOGIN_SETPATH))) < 0) {
+ (LOGIN_SETALL & ~(LOGIN_SETENV|LOGIN_SETPATH|LOGIN_SETUSER))) < 0) {
perror("unable to set user context");
exit(1);
}
@@ -1367,13 +1530,6 @@ do_setusercontext(struct passwd *pw)
exit(1);
}
endgrent();
-#ifdef GSSAPI
- if (options.gss_authentication) {
- temporarily_use_uid(pw);
- ssh_gssapi_storecreds();
- restore_uid();
- }
-#endif
# ifdef USE_PAM
/*
* PAM credentials may take the form of supplementary groups.
@@ -1381,21 +1537,39 @@ do_setusercontext(struct passwd *pw)
* Reestablish them here.
*/
if (options.use_pam) {
- do_pam_session();
- do_pam_setcred(0);
+ do_pam_setcred(use_privsep);
}
# endif /* USE_PAM */
# if defined(WITH_IRIX_PROJECT) || defined(WITH_IRIX_JOBS) || defined(WITH_IRIX_ARRAY)
irix_setusercontext(pw);
-# endif /* defined(WITH_IRIX_PROJECT) || defined(WITH_IRIX_JOBS) || defined(WITH_IRIX_ARRAY) */
+# endif /* defined(WITH_IRIX_PROJECT) || defined(WITH_IRIX_JOBS) || defined(WITH_IRIX_ARRAY) */
# ifdef _AIX
aix_usrinfo(pw);
# endif /* _AIX */
-#if defined(HAVE_LIBIAF) && !defined(BROKEN_LIBIAF)
+# ifdef USE_LIBIAF
if (set_id(pw->pw_name) != 0) {
exit(1);
}
-#endif /* HAVE_LIBIAF && !BROKEN_LIBIAF */
+# endif /* USE_LIBIAF */
+#endif
+
+ if (options.chroot_directory != NULL &&
+ strcasecmp(options.chroot_directory, "none") != 0) {
+ tmp = tilde_expand_filename(options.chroot_directory,
+ pw->pw_uid);
+ chroot_path = percent_expand(tmp, "h", pw->pw_dir,
+ "u", pw->pw_name, (char *)NULL);
+ safely_chroot(chroot_path, pw->pw_uid);
+ free(tmp);
+ 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
@@ -1494,17 +1668,16 @@ child_close_fds(void)
* environment, closing extra file descriptors, setting the user and group
* ids, and executing the command or shell.
*/
+#define ARGV_MAX 10
void
do_child(Session *s, const char *command)
{
extern char **environ;
char **env;
- char *argv[10];
+ char *argv[ARGV_MAX];
const char *shell, *shell0, *hostname = NULL;
struct passwd *pw = s->pw;
-#ifdef HAVE_LOGIN_CAP
- int lc_requirehome;
-#endif
+ int r = 0;
/* remove hostkey from the child's memory */
destroy_sensitive_data();
@@ -1592,10 +1765,6 @@ do_child(Session *s, const char *command)
*/
environ = env;
-#ifdef HAVE_LOGIN_CAP
- lc_requirehome = login_getcapbool(lc, "requirehome", 0);
- login_close(lc);
-#endif
#if defined(KRB5) && defined(USE_AFS)
/*
* At this point, we check to see if AFS is active and if we have
@@ -1624,20 +1793,42 @@ do_child(Session *s, const char *command)
/* Change current directory to the user's home directory. */
if (chdir(pw->pw_dir) < 0) {
- fprintf(stderr, "Could not chdir to home directory %s: %s\n",
- pw->pw_dir, strerror(errno));
+ /* Suppress missing homedir warning for chroot case */
#ifdef HAVE_LOGIN_CAP
- if (lc_requirehome)
- exit(1);
+ r = login_getcapbool(lc, "requirehome", 0);
#endif
+ if (r || options.chroot_directory == NULL)
+ fprintf(stderr, "Could not chdir to home "
+ "directory %s: %s\n", pw->pw_dir,
+ strerror(errno));
+ if (r)
+ exit(1);
}
+ closefrom(STDERR_FILENO + 1);
+
if (!options.use_login)
do_rc_files(s, shell);
/* restore SIGPIPE for child */
signal(SIGPIPE, SIG_DFL);
+ if (s->is_subsystem == SUBSYSTEM_INT_SFTP) {
+ extern int optind, optreset;
+ int i;
+ char *p, *args;
+
+ setproctitle("%s@internal-sftp-server", s->pw->pw_name);
+ args = strdup(command ? command : "sftp-server");
+ for (i = 0, (p = strtok(args, " ")); p; (p = strtok(NULL, " ")))
+ if (i < ARGV_MAX - 1)
+ argv[i++] = p;
+ argv[i] = NULL;
+ optind = optreset = 1;
+ __progname = argv[0];
+ exit(sftp_server_main(i, argv, s->pw));
+ }
+
if (options.use_login) {
launch_login(pw, hostname);
/* NEVERREACHED */
@@ -1689,43 +1880,79 @@ do_child(Session *s, const char *command)
exit(1);
}
+void
+session_unused(int id)
+{
+ debug3("%s: session id %d unused", __func__, id);
+ if (id >= options.max_sessions ||
+ id >= sessions_nalloc) {
+ fatal("%s: insane session id %d (max %d nalloc %d)",
+ __func__, id, options.max_sessions, sessions_nalloc);
+ }
+ bzero(&sessions[id], sizeof(*sessions));
+ sessions[id].self = id;
+ sessions[id].used = 0;
+ sessions[id].chanid = -1;
+ sessions[id].ptyfd = -1;
+ sessions[id].ttyfd = -1;
+ sessions[id].ptymaster = -1;
+ sessions[id].x11_chanids = NULL;
+ sessions[id].next_unused = sessions_first_unused;
+ sessions_first_unused = id;
+}
+
Session *
session_new(void)
{
- int i;
- static int did_init = 0;
- if (!did_init) {
- debug("session_new: init");
- for (i = 0; i < MAX_SESSIONS; i++) {
- sessions[i].used = 0;
+ Session *s, *tmp;
+
+ if (sessions_first_unused == -1) {
+ if (sessions_nalloc >= options.max_sessions)
+ return NULL;
+ debug2("%s: allocate (allocated %d max %d)",
+ __func__, sessions_nalloc, options.max_sessions);
+ tmp = xrealloc(sessions, sessions_nalloc + 1,
+ sizeof(*sessions));
+ if (tmp == NULL) {
+ error("%s: cannot allocate %d sessions",
+ __func__, sessions_nalloc + 1);
+ return NULL;
}
- did_init = 1;
+ sessions = tmp;
+ session_unused(sessions_nalloc++);
}
- for (i = 0; i < MAX_SESSIONS; i++) {
- Session *s = &sessions[i];
- if (! s->used) {
- memset(s, 0, sizeof(*s));
- s->chanid = -1;
- s->ptyfd = -1;
- s->ttyfd = -1;
- s->used = 1;
- s->self = i;
- s->x11_chanids = NULL;
- debug("session_new: session %d", i);
- return s;
- }
+
+ if (sessions_first_unused >= sessions_nalloc ||
+ sessions_first_unused < 0) {
+ fatal("%s: insane first_unused %d max %d nalloc %d",
+ __func__, sessions_first_unused, options.max_sessions,
+ sessions_nalloc);
}
- return NULL;
+
+ s = &sessions[sessions_first_unused];
+ if (s->used) {
+ fatal("%s: session %d already used",
+ __func__, sessions_first_unused);
+ }
+ sessions_first_unused = s->next_unused;
+ s->used = 1;
+ s->next_unused = -1;
+ debug("session_new: session %d", s->self);
+
+ return s;
}
static void
session_dump(void)
{
int i;
- for (i = 0; i < MAX_SESSIONS; i++) {
+ for (i = 0; i < sessions_nalloc; i++) {
Session *s = &sessions[i];
- debug("dump: used %d session %d %p channel %d pid %ld",
+
+ debug("dump: used %d next_unused %d session %d %p "
+ "channel %d pid %ld",
s->used,
+ s->next_unused,
s->self,
s,
s->chanid,
@@ -1755,7 +1982,7 @@ Session *
session_by_tty(char *tty)
{
int i;
- for (i = 0; i < MAX_SESSIONS; i++) {
+ for (i = 0; i < sessions_nalloc; i++) {
Session *s = &sessions[i];
if (s->used && s->ttyfd != -1 && strcmp(s->tty, tty) == 0) {
debug("session_by_tty: session %d tty %s", i, tty);
@@ -1771,10 +1998,11 @@ static Session *
session_by_channel(int id)
{
int i;
- for (i = 0; i < MAX_SESSIONS; i++) {
+ for (i = 0; i < sessions_nalloc; i++) {
Session *s = &sessions[i];
if (s->used && s->chanid == id) {
- debug("session_by_channel: session %d channel %d", i, id);
+ debug("session_by_channel: session %d channel %d",
+ i, id);
return s;
}
}
@@ -1788,7 +2016,7 @@ session_by_x11_channel(int id)
{
int i, j;
- for (i = 0; i < MAX_SESSIONS; i++) {
+ for (i = 0; i < sessions_nalloc; i++) {
Session *s = &sessions[i];
if (s->x11_chanids == NULL || !s->used)
@@ -1811,7 +2039,7 @@ session_by_pid(pid_t pid)
{
int i;
debug("session_by_pid: pid %ld", (long)pid);
- for (i = 0; i < MAX_SESSIONS; i++) {
+ for (i = 0; i < sessions_nalloc; i++) {
Session *s = &sessions[i];
if (s->used && s->pid == pid)
return s;
@@ -1867,7 +2095,8 @@ session_pty_req(Session *s)
/* Allocate a pty and open it. */
debug("Allocating pty.");
- if (!PRIVSEP(pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, sizeof(s->tty)))) {
+ if (!PRIVSEP(pty_allocate(&s->ptyfd, &s->ttyfd, s->tty,
+ sizeof(s->tty)))) {
if (s->term)
xfree(s->term);
s->term = NULL;
@@ -1910,15 +2139,17 @@ session_subsystem_req(Session *s)
if (strcmp(subsys, options.subsystem_name[i]) == 0) {
prog = options.subsystem_command[i];
cmd = options.subsystem_args[i];
- if (stat(prog, &st) < 0) {
+ 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,
strerror(errno));
break;
+ } else {
+ s->is_subsystem = SUBSYSTEM_EXT;
}
debug("subsystem: exec() %s", cmd);
- s->is_subsystem = 1;
- do_exec(s, cmd);
- success = 1;
+ success = do_exec(s, cmd) == 0;
break;
}
}
@@ -1961,19 +2192,19 @@ static int
session_shell_req(Session *s)
{
packet_check_eom();
- do_exec(s, NULL);
- return 1;
+ return do_exec(s, NULL) == 0;
}
static int
session_exec_req(Session *s)
{
- u_int len;
+ u_int len, success;
+
char *command = packet_get_string(&len);
packet_check_eom();
- do_exec(s, command);
+ success = do_exec(s, command) == 0;
xfree(command);
- return 1;
+ return success;
}
static int
@@ -1983,8 +2214,7 @@ session_break_req(Session *s)
packet_get_int(); /* ignored */
packet_check_eom();
- if (s->ttyfd == -1 ||
- tcsendbreak(s->ttyfd, 0) < 0)
+ if (s->ttyfd == -1 || tcsendbreak(s->ttyfd, 0) < 0)
return 0;
return 1;
}
@@ -2029,7 +2259,7 @@ session_auth_agent_req(Session *s)
{
static int called = 0;
packet_check_eom();
- if (no_agent_forwarding_flag) {
+ if (no_agent_forwarding_flag || !options.allow_agent_forwarding) {
debug("session_auth_agent_req: no_agent_forwarding_flag");
return 0;
}
@@ -2064,7 +2294,7 @@ session_input_channel_req(Channel *c, const char *rtype)
} else if (strcmp(rtype, "exec") == 0) {
success = session_exec_req(s);
} else if (strcmp(rtype, "pty-req") == 0) {
- success = session_pty_req(s);
+ success = session_pty_req(s);
} else if (strcmp(rtype, "x11-req") == 0) {
success = session_x11_req(s);
} else if (strcmp(rtype, "auth-agent-req@openssh.com") == 0) {
@@ -2085,7 +2315,7 @@ session_input_channel_req(Channel *c, const char *rtype)
}
void
-session_set_fds(Session *s, int fdin, int fdout, int fderr)
+session_set_fds(Session *s, int fdin, int fdout, int fderr, int is_tty)
{
if (!compat20)
fatal("session_set_fds: called for proto != 2.0");
@@ -2098,8 +2328,7 @@ session_set_fds(Session *s, int fdin, int fdout, int fderr)
channel_set_fds(s->chanid,
fdout, fdin, fderr,
fderr == -1 ? CHAN_EXTENDED_IGNORE : CHAN_EXTENDED_READ,
- 1,
- CHAN_SES_WINDOW_DEFAULT);
+ 1, is_tty, CHAN_SES_WINDOW_DEFAULT);
}
/*
@@ -2131,8 +2360,9 @@ session_pty_cleanup2(Session *s)
* the pty cleanup, so that another process doesn't get this pty
* while we're still cleaning up.
*/
- if (close(s->ptymaster) < 0)
- error("close(s->ptymaster/%d): %s", s->ptymaster, strerror(errno));
+ if (s->ptymaster != -1 && close(s->ptymaster) < 0)
+ error("close(s->ptymaster/%d): %s",
+ s->ptymaster, strerror(errno));
/* unlink pty from session */
s->ttyfd = -1;
@@ -2189,7 +2419,7 @@ session_close_single_x11(int id, void *arg)
debug3("session_close_single_x11: channel %d", id);
channel_cancel_cleanup(id);
- if ((s = session_by_x11_channel(id)) == NULL)
+ if ((s = session_by_x11_channel(id)) == NULL)
fatal("session_close_single_x11: no x11 channel %d", id);
for (i = 0; s->x11_chanids[i] != -1; i++) {
debug("session_close_single_x11: session %d: "
@@ -2240,7 +2470,7 @@ session_exit_message(Session *s, int status)
channel_request_start(s->chanid, "exit-signal", 0);
packet_put_cstring(sig2name(WTERMSIG(status)));
#ifdef WCOREDUMP
- packet_put_char(WCOREDUMP(status));
+ packet_put_char(WCOREDUMP(status)? 1 : 0);
#else /* WCOREDUMP */
packet_put_char(0);
#endif /* WCOREDUMP */
@@ -2292,7 +2522,6 @@ session_close(Session *s)
xfree(s->auth_data);
if (s->auth_proto)
xfree(s->auth_proto);
- s->used = 0;
if (s->env != NULL) {
for (i = 0; i < s->num_env; i++) {
xfree(s->env[i].name);
@@ -2301,6 +2530,7 @@ session_close(Session *s)
xfree(s->env);
}
session_proctitle(s);
+ session_unused(s->self);
}
void
@@ -2364,7 +2594,7 @@ void
session_destroy_all(void (*closefunc)(Session *))
{
int i;
- for (i = 0; i < MAX_SESSIONS; i++) {
+ for (i = 0; i < sessions_nalloc; i++) {
Session *s = &sessions[i];
if (s->used) {
if (closefunc != NULL)
@@ -2383,7 +2613,7 @@ session_tty_list(void)
char *cp;
buf[0] = '\0';
- for (i = 0; i < MAX_SESSIONS; i++) {
+ for (i = 0; i < sessions_nalloc; i++) {
Session *s = &sessions[i];
if (s->used && s->ttyfd != -1) {
@@ -2515,8 +2745,19 @@ do_cleanup(Authctxt *authctxt)
return;
called = 1;
- if (authctxt == NULL || !authctxt->authenticated)
+ if (authctxt == NULL)
+ return;
+
+#ifdef USE_PAM
+ if (options.use_pam) {
+ sshpam_cleanup();
+ sshpam_thread_cleanup();
+ }
+#endif
+
+ if (!authctxt->authenticated)
return;
+
#ifdef KRB5
if (options.kerberos_ticket_cleanup &&
authctxt->krb5_ctx)
@@ -2528,13 +2769,6 @@ do_cleanup(Authctxt *authctxt)
ssh_gssapi_cleanup_creds();
#endif
-#ifdef USE_PAM
- if (options.use_pam) {
- sshpam_cleanup();
- sshpam_thread_cleanup();
- }
-#endif
-
/* remove agent socket */
auth_sock_cleanup_proc(authctxt->pw);
OpenPOWER on IntegriCloud