+/* $OpenBSD: session.c,v 1.209 2006/07/11 20:07:25 stevesk Exp $ */
/*
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
* All rights reserved
*/
#include "includes.h"
-RCSID("$OpenBSD: session.c,v 1.163 2003/08/31 13:29:05 markus Exp $");
+
+#include <sys/types.h>
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/un.h>
+
+#include <errno.h>
+#include <grp.h>
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+#endif
+#include <pwd.h>
+#include <signal.h>
+#include <arpa/inet.h>
#include "ssh.h"
#include "ssh1.h"
#include "sshpty.h"
#include "packet.h"
#include "buffer.h"
-#include "mpaux.h"
+#include "match.h"
#include "uidswap.h"
#include "compat.h"
#include "channels.h"
#include "serverloop.h"
#include "canohost.h"
#include "session.h"
+#include "kex.h"
#include "monitor_wrap.h"
+#if defined(KRB5) && defined(USE_AFS)
+#include <kafs.h>
+#endif
+
#ifdef GSSAPI
#include "ssh-gss.h"
#endif
Session *session_new(void);
void session_set_fds(Session *, int, int, int);
-void session_pty_cleanup(void *);
+void session_pty_cleanup(Session *);
void session_proctitle(Session *);
int session_setup_x11fwd(Session *);
void do_exec_pty(Session *, const char *);
login_cap_t *lc;
#endif
+static int is_child = 0;
+
/* Name and directory of socket for authentication agent forwarding. */
static char *auth_sock_name = NULL;
static char *auth_sock_dir = NULL;
/* removes the agent forwarding socket */
static void
-auth_sock_cleanup_proc(void *_pw)
+auth_sock_cleanup_proc(struct passwd *pw)
{
- struct passwd *pw = _pw;
-
if (auth_sock_name != NULL) {
temporarily_use_uid(pw);
unlink(auth_sock_name);
/* 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-XXXXXXXX", MAXPATHLEN);
+ strlcpy(auth_sock_dir, "/tmp/ssh-XXXXXXXXXX", MAXPATHLEN);
/* Create private directory for socket */
if (mkdtemp(auth_sock_dir) == NULL) {
snprintf(auth_sock_name, MAXPATHLEN, "%s/agent.%ld",
auth_sock_dir, (long) getpid());
- /* delete agent socket on fatal() */
- fatal_add_cleanup(auth_sock_cleanup_proc, pw);
-
/* Create the socket. */
sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock < 0)
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)
+ if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0)
packet_disconnect("bind: %.100s", strerror(errno));
/* Restore the privileged uid. */
restore_uid();
/* Start listening on the socket. */
- if (listen(sock, 5) < 0)
+ if (listen(sock, SSH_LISTEN_BACKLOG) < 0)
packet_disconnect("listen: %.100s", strerror(errno));
/* Allocate a channel for the authentication agent socket. */
return 1;
}
+static void
+display_loginmsg(void)
+{
+ if (buffer_len(&loginmsg) > 0) {
+ buffer_append(&loginmsg, "\0", 1);
+ printf("%s", (char *)buffer_ptr(&loginmsg));
+ buffer_clear(&loginmsg);
+ }
+}
void
do_authenticated(Authctxt *authctxt)
{
setproctitle("%s", authctxt->pw->pw_name);
- /*
- * Cancel the alarm we set to limit the time taken for
- * authentication.
- */
- alarm(0);
- if (startup_pipe != -1) {
- close(startup_pipe);
- startup_pipe = -1;
- }
-
/* setup the channel layer */
if (!no_port_forwarding_flag && options.allow_tcp_forwarding)
channel_permit_all_opens();
else
do_authenticated1(authctxt);
- /* remove agent socket */
- if (auth_sock_name != NULL)
- auth_sock_cleanup_proc(authctxt->pw);
-#ifdef KRB5
- if (options.kerberos_ticket_cleanup)
- krb5_cleanup_proc(authctxt);
-#endif
+ do_cleanup(authctxt);
}
/*
u_int proto_len, data_len, dlen, compression_level = 0;
s = session_new();
+ if (s == NULL) {
+ error("no more sessions");
+ return;
+ }
s->authctxt = authctxt;
s->pw = authctxt->pw;
compression_level = packet_get_int();
packet_check_eom();
if (compression_level < 1 || compression_level > 9) {
- packet_send_debug("Received illegal compression level %d.",
+ packet_send_debug("Received invalid compression level %d.",
compression_level);
break;
}
- if (!options.compression) {
+ if (options.compression == COMP_NONE) {
debug2("compression disabled");
break;
}
break;
}
debug("Received TCP/IP port forwarding request.");
- channel_input_port_forward_request(s->pw->pw_uid == 0, options.gateway_ports);
+ if (channel_input_port_forward_request(s->pw->pw_uid == 0,
+ options.gateway_ports) < 0) {
+ debug("Port forwarding failed.");
+ break;
+ }
success = 1;
break;
session_proctitle(s);
#if defined(USE_PAM)
- if (options.use_pam) {
+ if (options.use_pam && !use_privsep)
do_pam_setcred(1);
- if (is_pam_password_change_required())
- packet_disconnect("Password change required but no "
- "TTY available");
- }
#endif /* USE_PAM */
/* Fork the child. */
if ((pid = fork()) == 0) {
- fatal_remove_all_cleanups();
+ is_child = 1;
/* Child. Reinitialize the log since the pid has changed. */
log_init(__progname, options.log_level, options.log_facility, log_stderr);
close(perr[1]);
if (compat20) {
- session_set_fds(s, pin[1], pout[0], s->is_subsystem ? -1 : perr[0]);
+ if (s->is_subsystem) {
+ close(perr[0]);
+ perr[0] = -1;
+ }
+ session_set_fds(s, pin[1], pout[0], perr[0]);
} else {
/* Enter the interactive session. */
server_loop(pid, pin[1], pout[0], perr[0]);
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 defined(USE_PAM)
if (options.use_pam) {
do_pam_set_tty(s->tty);
- do_pam_setcred(1);
+ if (!use_privsep)
+ do_pam_setcred(1);
}
#endif
/* Fork the child. */
if ((pid = fork()) == 0) {
- fatal_remove_all_cleanups();
+ is_child = 1;
/* Child. Reinitialize the log because the pid has changed. */
log_init(__progname, options.log_level, options.log_facility, log_stderr);
fromlen = sizeof(from);
if (packet_connection_is_on_socket()) {
if (getpeername(packet_get_connection_in(),
- (struct sockaddr *) & from, &fromlen) < 0) {
+ (struct sockaddr *)&from, &fromlen) < 0) {
debug("getpeername: %.100s", strerror(errno));
- fatal_cleanup();
+ cleanup_exit(255);
}
}
debug("Forced command '%.900s'", command);
}
-#ifdef GSSAPI
- if (options.gss_authentication) {
- temporarily_use_uid(s->pw);
- ssh_gssapi_storecreds();
- restore_uid();
+#ifdef SSH_AUDIT_EVENTS
+ if (command != NULL)
+ PRIVSEP(audit_run_command(command));
+ else if (s->ttyfd == -1) {
+ char *shell = s->pw->pw_shell;
+
+ if (shell[0] == '\0') /* empty shell means /bin/sh */
+ shell =_PATH_BSHELL;
+ PRIVSEP(audit_run_command(shell));
}
#endif
do_exec_no_pty(s, command);
original_command = NULL;
-}
+ /*
+ * Clear loginmsg: 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);
+}
/* administrative, login(1)-like work */
void
do_login(Session *s, const char *command)
{
- char *time_string;
socklen_t fromlen;
struct sockaddr_storage from;
struct passwd * pw = s->pw;
if (getpeername(packet_get_connection_in(),
(struct sockaddr *) & from, &fromlen) < 0) {
debug("getpeername: %.100s", strerror(errno));
- fatal_cleanup();
+ cleanup_exit(255);
}
}
* If password change is needed, do it now.
* This needs to occur before the ~/.hushlogin check.
*/
- if (options.use_pam && is_pam_password_change_required()) {
- print_pam_messages();
+ if (options.use_pam && !use_privsep && s->authctxt->force_pwchange) {
+ display_loginmsg();
do_pam_chauthtok();
+ s->authctxt->force_pwchange = 0;
/* XXX - signal [net] parent to enable forwardings */
}
#endif
if (check_quietlogin(s, command))
return;
-#ifdef USE_PAM
- if (options.use_pam && !is_pam_password_change_required())
- print_pam_messages();
-#endif /* USE_PAM */
-
- /* display post-login message */
- if (buffer_len(&loginmsg) > 0) {
- buffer_append(&loginmsg, "\0", 1);
- printf("%s\n", (char *)buffer_ptr(&loginmsg));
- }
- buffer_free(&loginmsg);
-
-#ifndef NO_SSH_LASTLOG
- if (options.print_lastlog && s->last_login_time != 0) {
- time_string = ctime(&s->last_login_time);
- if (strchr(time_string, '\n'))
- *strchr(time_string, '\n') = 0;
- if (strcmp(s->hostname, "") == 0)
- printf("Last login: %s\r\n", time_string);
- else
- printf("Last login: %s from %s\r\n", time_string,
- s->hostname);
- }
-#endif /* NO_SSH_LASTLOG */
+ display_loginmsg();
do_motd();
}
child_set_env(char ***envp, u_int *envsizep, const char *name,
const char *value)
{
- u_int i, namelen;
char **env;
+ u_int envsize;
+ u_int i, namelen;
/*
* If we're passed an uninitialized list, allocate a single null
xfree(env[i]);
} else {
/* New variable. Expand if necessary. */
- if (i >= (*envsizep) - 1) {
- if (*envsizep >= 1000)
- fatal("child_set_env: too many env vars,"
- " skipping: %.100s", name);
- (*envsizep) += 50;
- env = (*envp) = xrealloc(env, (*envsizep) * sizeof(char *));
+ envsize = *envsizep;
+ if (i >= envsize - 1) {
+ if (envsize >= 1000)
+ fatal("child_set_env: too many env vars");
+ envsize += 50;
+ env = (*envp) = xrealloc(env, envsize, sizeof(char *));
+ *envsizep = envsize;
}
/* Need to set the NULL pointer at end of array beyond the new slot. */
env[i + 1] = NULL;
read_etc_default_login(char ***env, u_int *envsize, uid_t uid)
{
char **tmpenv = NULL, *var;
- u_int i;
- size_t tmpenvsize = 0;
- mode_t mask;
+ u_int i, tmpenvsize = 0;
+ u_long mask;
/*
* We don't want to copy the whole file to the child's environment,
*/
read_environment_file(&tmpenv, &tmpenvsize, "/etc/default/login");
+ if (tmpenv == NULL)
+ return;
+
if (uid == 0)
var = child_get_env(tmpenv, "SUPATH");
else
var = child_get_env(tmpenv, "PATH");
if (var != NULL)
child_set_env(env, envsize, "PATH", var);
-
+
if ((var = child_get_env(tmpenv, "UMASK")) != NULL)
if (sscanf(var, "%5lo", &mask) == 1)
- umask(mask);
-
+ umask((mode_t)mask);
+
for (i = 0; tmpenv[i] != NULL; i++)
xfree(tmpenv[i]);
xfree(tmpenv);
}
#endif /* HAVE_ETC_DEFAULT_LOGIN */
-void copy_environment(char **source, char ***env, u_int *envsize)
+void
+copy_environment(char **source, char ***env, u_int *envsize)
{
char *var_name, *var_val;
int i;
debug3("Copy environment: %s=%s", var_name, var_val);
child_set_env(env, envsize, var_name, var_val);
-
+
xfree(var_name);
}
}
/* Initialize the environment. */
envsize = 100;
- env = xmalloc(envsize * sizeof(char *));
+ env = xcalloc(envsize, sizeof(char *));
env[0] = NULL;
#ifdef HAVE_CYGWIN
* The Windows environment contains some setting which are
* important for a running system. They must not be dropped.
*/
- copy_environment(environ, &env, &envsize);
+ {
+ char **p;
+
+ p = fetch_windows_environment();
+ copy_environment(p, &env, &envsize);
+ free_windows_environment(p);
+ }
#endif
#ifdef GSSAPI
- /* Allow any GSSAPI methods that we've used to alter
+ /* Allow any GSSAPI methods that we've used to alter
* the childs environment as they see fit
*/
ssh_gssapi_do_child(&env, &envsize);
if (!options.use_login) {
/* Set basic environment. */
+ for (i = 0; i < s->num_env; i++)
+ child_set_env(&env, &envsize, s->env[i].name,
+ s->env[i].val);
+
child_set_env(&env, &envsize, "USER", pw->pw_name);
child_set_env(&env, &envsize, "LOGNAME", pw->pw_name);
#ifdef _AIX
path = child_get_env(env, "PATH");
# endif /* HAVE_ETC_DEFAULT_LOGIN */
if (path == NULL || *path == '\0') {
- child_set_env(&env, &envsize, "PATH",
+ child_set_env(&env, &envsize, "PATH",
s->pw->pw_uid == 0 ?
SUPERUSER_PATH : _PATH_STDPATH);
}
child_set_env(&env, &envsize, "TMPDIR", cray_tmpdir);
#endif /* _UNICOS */
+ /*
+ * Since we clear KRB5CCNAME at startup, if it's set now then it
+ * must have been set by a native authentication method (eg AIX or
+ * SIA), so copy it to the child.
+ */
+ {
+ char *cp;
+
+ if ((cp = getenv("KRB5CCNAME")) != NULL)
+ child_set_env(&env, &envsize, "KRB5CCNAME", cp);
+ }
+
#ifdef _AIX
{
char *cp;
if ((cp = getenv("AUTHSTATE")) != NULL)
child_set_env(&env, &envsize, "AUTHSTATE", cp);
- if ((cp = getenv("KRB5CCNAME")) != NULL)
- child_set_env(&env, &envsize, "KRB5CCNAME", cp);
read_environment_file(&env, &envsize, "/etc/environment");
}
#endif
#ifdef KRB5
- if (s->authctxt->krb5_ticket_file)
+ if (s->authctxt->krb5_ccname)
child_set_env(&env, &envsize, "KRB5CCNAME",
- s->authctxt->krb5_ticket_file);
+ s->authctxt->krb5_ccname);
#endif
#ifdef USE_PAM
/*
* been set by PAM.
*/
if (options.use_pam) {
- char **p = fetch_pam_environment();
+ char **p;
+
+ p = fetch_pam_child_environment();
+ copy_environment(p, &env, &envsize);
+ free_pam_environment(p);
+ p = fetch_pam_environment();
copy_environment(p, &env, &envsize);
free_pam_environment(p);
}
if (debug_flag) {
fprintf(stderr,
"Running %.500s remove %.100s\n",
- options.xauth_location, s->auth_display);
+ options.xauth_location, s->auth_display);
fprintf(stderr,
"%.500s add %.100s %.100s %.100s\n",
options.xauth_location, s->auth_display,
# 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);
+ }
+# endif /* USE_PAM */
if (setusercontext(lc, pw, pw->pw_uid,
(LOGIN_SETALL & ~LOGIN_SETPATH)) < 0) {
perror("unable to set user context");
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.
+ * PAM credentials may take the form of supplementary groups.
* These will have been wiped by the above initgroups() call.
* Reestablish them here.
*/
# ifdef _AIX
aix_usrinfo(pw);
# endif /* _AIX */
+#if defined(HAVE_LIBIAF) && !defined(BROKEN_LIBIAF)
+ if (set_id(pw->pw_name) != 0) {
+ exit(1);
+ }
+#endif /* HAVE_LIBIAF && !BROKEN_LIBIAF */
/* Permanently switch to the desired uid. */
permanently_set_uid(pw);
#endif
#endif
if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid)
fatal("Failed to set uids to %u.", (u_int) pw->pw_uid);
+
+#ifdef WITH_SELINUX
+ ssh_selinux_setup_exec_context(pw->pw_name);
+#endif
+}
+
+static void
+do_pwchange(Session *s)
+{
+ fflush(NULL);
+ fprintf(stderr, "WARNING: Your password has expired.\n");
+ if (s->ttyfd != -1) {
+ fprintf(stderr,
+ "You must change your password now and login again!\n");
+#ifdef PASSWD_NEEDS_USERNAME
+ execl(_PATH_PASSWD_PROG, "passwd", s->pw->pw_name,
+ (char *)NULL);
+#else
+ execl(_PATH_PASSWD_PROG, "passwd", (char *)NULL);
+#endif
+ perror("passwd");
+ } else {
+ fprintf(stderr,
+ "Password change required but no TTY available.\n");
+ }
+ exit(1);
}
static void
exit(1);
}
+static void
+child_close_fds(void)
+{
+ int i;
+
+ if (packet_get_connection_in() == packet_get_connection_out())
+ close(packet_get_connection_in());
+ else {
+ close(packet_get_connection_in());
+ close(packet_get_connection_out());
+ }
+ /*
+ * Close all descriptors related to channels. They will still remain
+ * open in the parent.
+ */
+ /* XXX better use close-on-exec? -markus */
+ channel_close_all();
+
+ /*
+ * Close any extra file descriptors. Note that there may still be
+ * descriptors left by system functions. They will be closed later.
+ */
+ endpwent();
+
+ /*
+ * Close any extra open file descriptors so that we don't have them
+ * hanging around in clients. Note that we want to do this after
+ * initgroups, because at least on Solaris 2.3 it leaves file
+ * descriptors open.
+ */
+ for (i = 3; i < 64; i++)
+ close(i);
+}
+
/*
* Performs common processing for the child, such as setting up the
* environment, closing extra file descriptors, setting the user and group
char *argv[10];
const char *shell, *shell0, *hostname = NULL;
struct passwd *pw = s->pw;
- u_int i;
/* remove hostkey from the child's memory */
destroy_sensitive_data();
+ /* Force a password change */
+ if (s->authctxt->force_pwchange) {
+ do_setusercontext(pw);
+ child_close_fds();
+ do_pwchange(s);
+ exit(1);
+ }
+
/* login(1) is only called if we execute the login shell */
if (options.use_login && command != NULL)
options.use_login = 0;
if (!check_quietlogin(s, command))
do_motd();
#else /* HAVE_OSF_SIA */
- do_nologin(pw);
+ /* When PAM is enabled we rely on it to do the nologin check */
+ if (!options.use_pam)
+ do_nologin(pw);
do_setusercontext(pw);
+ /*
+ * PAM session modules in do_setusercontext may have
+ * generated messages, so if this in an interactive
+ * login then display them too.
+ */
+ if (!check_quietlogin(s, command))
+ display_loginmsg();
#endif /* HAVE_OSF_SIA */
}
+#ifdef USE_PAM
+ if (options.use_pam && !options.use_login && !is_pam_session_open()) {
+ debug3("PAM session not opened, exiting");
+ display_loginmsg();
+ exit(254);
+ }
+#endif
+
/*
* Get the shell from the password data. An empty shell field is
* legal, and means /bin/sh.
* closed before building the environment, as we call
* get_remote_ipaddr there.
*/
- if (packet_get_connection_in() == packet_get_connection_out())
- close(packet_get_connection_in());
- else {
- close(packet_get_connection_in());
- close(packet_get_connection_out());
- }
- /*
- * Close all descriptors related to channels. They will still remain
- * open in the parent.
- */
- /* XXX better use close-on-exec? -markus */
- channel_close_all();
+ child_close_fds();
/*
- * Close any extra file descriptors. Note that there may still be
- * descriptors left by system functions. They will be closed later.
+ * Must take new environment into use so that .ssh/rc,
+ * /etc/ssh/sshrc and xauth are run in the proper environment.
*/
- endpwent();
+ environ = env;
+#if defined(KRB5) && defined(USE_AFS)
/*
- * Close any extra open file descriptors so that we don\'t have them
- * hanging around in clients. Note that we want to do this after
- * initgroups, because at least on Solaris 2.3 it leaves file
- * descriptors open.
+ * At this point, we check to see if AFS is active and if we have
+ * a valid Kerberos 5 TGT. If so, it seems like a good idea to see
+ * if we can (and need to) extend the ticket into an AFS token. If
+ * we don't do this, we run into potential problems if the user's
+ * home directory is in AFS and it's not world-readable.
*/
- for (i = 3; i < 64; i++)
- close(i);
- /*
- * Must take new environment into use so that .ssh/rc,
- * /etc/ssh/sshrc and xauth are run in the proper environment.
- */
- environ = env;
+ if (options.kerberos_get_afs_token && k_hasafs() &&
+ (s->authctxt->krb5_ctx != NULL)) {
+ char cell[64];
+
+ debug("Getting AFS token");
+
+ k_setpag();
- /* Change current directory to the user\'s home directory. */
+ if (k_afs_cell_of_file(pw->pw_dir, cell, sizeof(cell)) == 0)
+ krb5_afslog(s->authctxt->krb5_ctx,
+ s->authctxt->krb5_fwd_ccache, cell, NULL);
+
+ krb5_afslog_home(s->authctxt->krb5_ctx,
+ s->authctxt->krb5_fwd_ccache, NULL, NULL, pw->pw_dir);
+ }
+#endif
+
+ /* 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));
s->ttyfd = -1;
s->used = 1;
s->self = i;
+ s->x11_chanids = NULL;
debug("session_new: session %d", i);
return s;
}
}
s->authctxt = authctxt;
s->pw = authctxt->pw;
- if (s->pw == NULL)
+ if (s->pw == NULL || !authctxt->valid)
fatal("no user for session %d", s->self);
debug("session_open: session %d: link with channel %d", s->self, chanid);
s->chanid = chanid;
return NULL;
}
+static Session *
+session_by_x11_channel(int id)
+{
+ int i, j;
+
+ for (i = 0; i < MAX_SESSIONS; i++) {
+ Session *s = &sessions[i];
+
+ if (s->x11_chanids == NULL || !s->used)
+ continue;
+ for (j = 0; s->x11_chanids[j] != -1; j++) {
+ if (s->x11_chanids[j] == id) {
+ debug("session_by_x11_channel: session %d "
+ "channel %d", s->self, id);
+ return s;
+ }
+ }
+ }
+ debug("session_by_x11_channel: unknown channel %d", id);
+ session_dump();
+ return NULL;
+}
+
static Session *
session_by_pid(pid_t pid)
{
packet_disconnect("Protocol error: you already have a pty.");
return 0;
}
- /* Get the time and hostname when the user last logged in. */
- if (options.print_lastlog) {
- s->hostname[0] = '\0';
- s->last_login_time = get_last_login_time(s->pw->pw_uid,
- s->pw->pw_name, s->hostname, sizeof(s->hostname));
- }
s->term = packet_get_string(&len);
n_bytes = packet_remaining();
tty_parse_modes(s->ttyfd, &n_bytes);
- /*
- * Add a cleanup function to clear the utmp entry and record logout
- * time in case we call fatal() (e.g., the connection gets closed).
- */
- fatal_add_cleanup(session_pty_cleanup, (void *)s);
if (!use_privsep)
pty_setowner(s->pw, s->tty);
struct stat st;
u_int len;
int success = 0;
- char *cmd, *subsys = packet_get_string(&len);
- int i;
+ char *prog, *cmd, *subsys = packet_get_string(&len);
+ u_int i;
packet_check_eom();
logit("subsystem request for %.100s", subsys);
for (i = 0; i < options.num_subsystems; i++) {
if (strcmp(subsys, options.subsystem_name[i]) == 0) {
- cmd = options.subsystem_command[i];
- if (stat(cmd, &st) < 0) {
- error("subsystem: cannot stat %s: %s", cmd,
+ prog = options.subsystem_command[i];
+ cmd = options.subsystem_args[i];
+ if (stat(prog, &st) < 0) {
+ error("subsystem: cannot stat %s: %s", prog,
strerror(errno));
break;
}
{
int success;
+ if (s->auth_proto != NULL || s->auth_data != NULL) {
+ error("session_x11_req: session %d: "
+ "x11 forwarding already active", s->self);
+ return 0;
+ }
s->single_connection = packet_get_char();
s->auth_proto = packet_get_string(NULL);
s->auth_data = packet_get_string(NULL);
static int
session_break_req(Session *s)
{
- u_int break_length;
- break_length = packet_get_int(); /* ignored */
+ packet_get_int(); /* ignored */
packet_check_eom();
if (s->ttyfd == -1 ||
return 1;
}
+static int
+session_env_req(Session *s)
+{
+ char *name, *val;
+ u_int name_len, val_len, i;
+
+ name = packet_get_string(&name_len);
+ val = packet_get_string(&val_len);
+ packet_check_eom();
+
+ /* Don't set too many environment variables */
+ if (s->num_env > 128) {
+ debug2("Ignoring env request %s: too many env vars", name);
+ goto fail;
+ }
+
+ for (i = 0; i < options.num_accept_env; i++) {
+ if (match_pattern(name, options.accept_env[i])) {
+ debug2("Setting env %d: %s=%s", s->num_env, name, val);
+ s->env = xrealloc(s->env, s->num_env + 1,
+ sizeof(*s->env));
+ s->env[s->num_env].name = name;
+ s->env[s->num_env].val = val;
+ s->num_env++;
+ return (1);
+ }
+ }
+ debug2("Ignoring env request %s: disallowed name", name);
+
+ fail:
+ xfree(name);
+ xfree(val);
+ return (0);
+}
+
static int
session_auth_agent_req(Session *s)
{
success = session_auth_agent_req(s);
} else if (strcmp(rtype, "subsystem") == 0) {
success = session_subsystem_req(s);
- } else if (strcmp(rtype, "break") == 0) {
- success = session_break_req(s);
+ } else if (strcmp(rtype, "env") == 0) {
+ success = session_env_req(s);
}
}
if (strcmp(rtype, "window-change") == 0) {
success = session_window_change_req(s);
+ } else if (strcmp(rtype, "break") == 0) {
+ success = session_break_req(s);
}
+
return success;
}
* (e.g., due to a dropped connection).
*/
void
-session_pty_cleanup2(void *session)
+session_pty_cleanup2(Session *s)
{
- Session *s = session;
-
if (s == NULL) {
error("session_pty_cleanup: no session");
return;
}
void
-session_pty_cleanup(void *session)
+session_pty_cleanup(Session *s)
{
- PRIVSEP(session_pty_cleanup2(session));
+ PRIVSEP(session_pty_cleanup2(s));
}
static char *
return "SIG@openssh.com";
}
+static void
+session_close_x11(int id)
+{
+ Channel *c;
+
+ if ((c = channel_by_id(id)) == NULL) {
+ debug("session_close_x11: x11 channel %d missing", id);
+ } else {
+ /* Detach X11 listener */
+ debug("session_close_x11: detach x11 channel %d", id);
+ channel_cancel_cleanup(id);
+ if (c->ostate != CHAN_OUTPUT_CLOSED)
+ chan_mark_dead(c);
+ }
+}
+
+static void
+session_close_single_x11(int id, void *arg)
+{
+ Session *s;
+ u_int i;
+
+ debug3("session_close_single_x11: channel %d", id);
+ channel_cancel_cleanup(id);
+ 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: "
+ "closing channel %d", s->self, s->x11_chanids[i]);
+ /*
+ * The channel "id" is already closing, but make sure we
+ * close all of its siblings.
+ */
+ if (s->x11_chanids[i] != id)
+ session_close_x11(s->x11_chanids[i]);
+ }
+ xfree(s->x11_chanids);
+ s->x11_chanids = NULL;
+ if (s->display) {
+ xfree(s->display);
+ s->display = NULL;
+ }
+ if (s->auth_proto) {
+ xfree(s->auth_proto);
+ s->auth_proto = NULL;
+ }
+ if (s->auth_data) {
+ xfree(s->auth_data);
+ s->auth_data = NULL;
+ }
+ if (s->auth_display) {
+ xfree(s->auth_display);
+ s->auth_display = NULL;
+ }
+}
+
static void
session_exit_message(Session *s, int status)
{
/* disconnect channel */
debug("session_exit_message: release channel %d", s->chanid);
- channel_cancel_cleanup(s->chanid);
+
+ /*
+ * Adjust cleanup callback attachment to send close messages when
+ * the channel gets EOF. The session will be then be closed
+ * by session_close_by_channel when the childs close their fds.
+ */
+ channel_register_cleanup(c->self, session_close_by_channel, 1);
+
/*
* emulate a write failure with 'chan_write_failed', nobody will be
* interested in data we write.
*/
if (c->ostate != CHAN_OUTPUT_CLOSED)
chan_write_failed(c);
- s->chanid = -1;
}
void
session_close(Session *s)
{
+ u_int i;
+
debug("session_close: session %d pid %ld", s->self, (long)s->pid);
- if (s->ttyfd != -1) {
- fatal_remove_cleanup(session_pty_cleanup, (void *)s);
+ if (s->ttyfd != -1)
session_pty_cleanup(s);
- }
if (s->term)
xfree(s->term);
if (s->display)
xfree(s->display);
+ if (s->x11_chanids)
+ xfree(s->x11_chanids);
if (s->auth_display)
xfree(s->auth_display);
if (s->auth_data)
if (s->auth_proto)
xfree(s->auth_proto);
s->used = 0;
+ for (i = 0; i < s->num_env; i++) {
+ xfree(s->env[i].name);
+ xfree(s->env[i].val);
+ }
+ if (s->env != NULL)
+ xfree(s->env);
session_proctitle(s);
}
}
if (s->chanid != -1)
session_exit_message(s, status);
- session_close(s);
+ if (s->ttyfd != -1)
+ session_pty_cleanup(s);
+ s->pid = 0;
}
/*
session_close_by_channel(int id, void *arg)
{
Session *s = session_by_channel(id);
+ u_int i;
+
if (s == NULL) {
debug("session_close_by_channel: no session for id %d", id);
return;
* delay detach of session, but release pty, since
* the fd's to the child are already closed
*/
- if (s->ttyfd != -1) {
- fatal_remove_cleanup(session_pty_cleanup, (void *)s);
+ if (s->ttyfd != -1)
session_pty_cleanup(s);
- }
return;
}
/* detach by removing callback */
channel_cancel_cleanup(s->chanid);
+
+ /* Close any X11 listeners associated with this session */
+ if (s->x11_chanids != NULL) {
+ for (i = 0; s->x11_chanids[i] != -1; i++) {
+ session_close_x11(s->x11_chanids[i]);
+ s->x11_chanids[i] = -1;
+ }
+ }
+
s->chanid = -1;
session_close(s);
}
for (i = 0; i < MAX_SESSIONS; i++) {
Session *s = &sessions[i];
if (s->used && s->ttyfd != -1) {
-
+
if (strncmp(s->tty, "/dev/", 5) != 0) {
cp = strrchr(s->tty, '/');
cp = (cp == NULL) ? s->tty : cp + 1;
} else
cp = s->tty + 5;
-
+
if (buf[0] != '\0')
strlcat(buf, ",", sizeof buf);
strlcat(buf, cp, sizeof buf);
struct stat st;
char display[512], auth_display[512];
char hostname[MAXHOSTNAMELEN];
+ u_int i;
if (no_x11_forwarding_flag) {
packet_send_debug("X11 forwarding disabled in user configuration file.");
}
if (x11_create_display_inet(options.x11_display_offset,
options.x11_use_localhost, s->single_connection,
- &s->display_number) == -1) {
+ &s->display_number, &s->x11_chanids) == -1) {
debug("x11_create_display_inet failed.");
return 0;
}
+ for (i = 0; s->x11_chanids[i] != -1; i++) {
+ channel_register_cleanup(s->x11_chanids[i],
+ session_close_single_x11, 0);
+ }
/* Set up a suitable value for the DISPLAY variable. */
if (gethostname(hostname, sizeof(hostname)) < 0)
do_authenticated2(Authctxt *authctxt)
{
server_loop2(authctxt);
-#if defined(GSSAPI)
- if (options.gss_cleanup_creds)
- ssh_gssapi_cleanup_creds(NULL);
+}
+
+void
+do_cleanup(Authctxt *authctxt)
+{
+ static int called = 0;
+
+ debug("do_cleanup");
+
+ /* no cleanup if we're in the child for login shell */
+ if (is_child)
+ return;
+
+ /* avoid double cleanup */
+ if (called)
+ return;
+ called = 1;
+
+ if (authctxt == NULL)
+ return;
+#ifdef KRB5
+ if (options.kerberos_ticket_cleanup &&
+ authctxt->krb5_ctx)
+ krb5_cleanup_proc(authctxt);
+#endif
+
+#ifdef GSSAPI
+ if (compat20 && options.gss_cleanup_creds)
+ 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);
+
+ /*
+ * Cleanup ptys/utmp only if privsep is disabled,
+ * or if running in monitor.
+ */
+ if (!use_privsep || mm_is_monitor())
+ session_destroy_all(session_pty_cleanup2);
}