X-Git-Url: http://andersk.mit.edu/gitweb/openssh.git/blobdiff_plain/f2107e976cca78fb5e84777027c8ae235967a279..b1f0c61269f2e5121405d51d9554777735c8f2bd:/session.c diff --git a/session.c b/session.c index 7c8fe5fa..8eb1a03e 100644 --- a/session.c +++ b/session.c @@ -33,7 +33,19 @@ */ #include "includes.h" -RCSID("$OpenBSD: session.c,v 1.178 2004/07/11 17:48:47 deraadt Exp $"); +RCSID("$OpenBSD: session.c,v 1.197 2006/02/28 01:10:21 djm Exp $"); + +#include +#ifdef HAVE_SYS_STAT_H +# include +#endif +#include +#include + +#ifdef HAVE_PATHS_H +#include +#endif +#include #include "ssh.h" #include "ssh1.h" @@ -56,6 +68,7 @@ RCSID("$OpenBSD: session.c,v 1.178 2004/07/11 17:48:47 deraadt Exp $"); #include "serverloop.h" #include "canohost.h" #include "session.h" +#include "kex.h" #include "monitor_wrap.h" #if defined(KRB5) && defined(USE_AFS) @@ -198,10 +211,9 @@ display_loginmsg(void) { if (buffer_len(&loginmsg) > 0) { buffer_append(&loginmsg, "\0", 1); - printf("%s\n", (char *)buffer_ptr(&loginmsg)); + printf("%s", (char *)buffer_ptr(&loginmsg)); buffer_clear(&loginmsg); } - fflush(stdout); } void @@ -209,15 +221,6 @@ 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(); @@ -246,6 +249,10 @@ do_authenticated1(Authctxt *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; @@ -265,11 +272,11 @@ do_authenticated1(Authctxt *authctxt) 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; } @@ -662,11 +669,15 @@ do_exec(Session *s, const char *command) 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 @@ -676,14 +687,19 @@ do_exec(Session *s, const char *command) 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; @@ -728,19 +744,6 @@ do_login(Session *s, const char *command) display_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 */ - do_motd(); } @@ -947,7 +950,8 @@ read_etc_default_login(char ***env, u_int *envsize, uid_t uid) } #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; @@ -988,7 +992,13 @@ do_setup_env(Session *s, const char *shell) * 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 @@ -1089,14 +1099,24 @@ do_setup_env(Session *s, const char *shell) 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 @@ -1256,6 +1276,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(); @@ -1286,6 +1313,13 @@ 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. @@ -1303,6 +1337,11 @@ do_setusercontext(struct passwd *pw) # 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 @@ -1318,11 +1357,17 @@ do_setusercontext(struct passwd *pw) 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, @@ -1377,7 +1422,7 @@ child_close_fds(void) endpwent(); /* - * Close any extra open file descriptors so that we don\'t have them + * 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. @@ -1429,18 +1474,28 @@ do_child(Session *s, const char *command) 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 (command == NULL) + 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. @@ -1486,7 +1541,7 @@ do_child(Session *s, const char *command) */ if (options.kerberos_get_afs_token && k_hasafs() && - (s->authctxt->krb5_ctx != NULL)) { + (s->authctxt->krb5_ctx != NULL)) { char cell[64]; debug("Getting AFS token"); @@ -1502,7 +1557,7 @@ do_child(Session *s, const char *command) } #endif - /* Change current directory to the user\'s home directory. */ + /* 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)); @@ -1590,6 +1645,7 @@ session_new(void) s->ttyfd = -1; s->used = 1; s->self = i; + s->x11_chanids = NULL; debug("session_new: session %d", i); return s; } @@ -1662,6 +1718,29 @@ session_by_channel(int id) 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) { @@ -1703,12 +1782,6 @@ session_pty_req(Session *s) 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); @@ -1763,7 +1836,7 @@ session_subsystem_req(Session *s) u_int len; int success = 0; char *cmd, *subsys = packet_get_string(&len); - int i; + u_int i; packet_check_eom(); logit("subsystem request for %.100s", subsys); @@ -1797,6 +1870,11 @@ session_x11_req(Session *s) { 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); @@ -2021,6 +2099,62 @@ sig2name(int sig) 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) { @@ -2054,7 +2188,14 @@ 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. @@ -2063,13 +2204,12 @@ session_exit_message(Session *s, int status) */ if (c->ostate != CHAN_OUTPUT_CLOSED) chan_write_failed(c); - s->chanid = -1; } void session_close(Session *s) { - int i; + u_int i; debug("session_close: session %d pid %ld", s->self, (long)s->pid); if (s->ttyfd != -1) @@ -2078,6 +2218,8 @@ session_close(Session *s) 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) @@ -2105,7 +2247,9 @@ session_close_by_pid(pid_t pid, int status) } if (s->chanid != -1) session_exit_message(s, status); - session_close(s); + if (s->ttyfd != -1) + session_pty_cleanup(s); + s->pid = 0; } /* @@ -2116,6 +2260,8 @@ void 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; @@ -2134,6 +2280,15 @@ session_close_by_channel(int id, void *arg) } /* 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); } @@ -2196,6 +2351,7 @@ session_setup_x11fwd(Session *s) 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."); @@ -2221,10 +2377,14 @@ session_setup_x11fwd(Session *s) } 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)