=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/ssh/session.c,v retrieving revision 1.163.2.2 retrieving revision 1.164 diff -u -r1.163.2.2 -r1.164 --- src/usr.bin/ssh/session.c 2004/08/19 22:37:32 1.163.2.2 +++ src/usr.bin/ssh/session.c 2003/09/18 08:49:45 1.164 @@ -33,7 +33,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: session.c,v 1.163.2.2 2004/08/19 22:37:32 brad Exp $"); +RCSID("$OpenBSD: session.c,v 1.164 2003/09/18 08:49:45 markus Exp $"); #include "ssh.h" #include "ssh1.h" @@ -42,7 +42,7 @@ #include "sshpty.h" #include "packet.h" #include "buffer.h" -#include "match.h" +#include "mpaux.h" #include "uidswap.h" #include "compat.h" #include "channels.h" @@ -58,10 +58,6 @@ #include "session.h" #include "monitor_wrap.h" -#ifdef KRB5 -#include -#endif - #ifdef GSSAPI #include "ssh-gss.h" #endif @@ -70,7 +66,7 @@ Session *session_new(void); void session_set_fds(Session *, int, int, int); -void session_pty_cleanup(Session *); +void session_pty_cleanup(void *); void session_proctitle(Session *); int session_setup_x11fwd(Session *); void do_exec_pty(Session *, const char *); @@ -94,7 +90,6 @@ extern u_int utmp_len; extern int startup_pipe; extern void destroy_sensitive_data(void); -extern Buffer loginmsg; /* original command from peer. */ const char *original_command = NULL; @@ -107,8 +102,6 @@ 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; @@ -116,8 +109,10 @@ /* removes the agent forwarding socket */ static void -auth_sock_cleanup_proc(struct passwd *pw) +auth_sock_cleanup_proc(void *_pw) { + struct passwd *pw = _pw; + if (auth_sock_name != NULL) { temporarily_use_uid(pw); unlink(auth_sock_name); @@ -145,7 +140,7 @@ /* 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); + strlcpy(auth_sock_dir, "/tmp/ssh-XXXXXXXX", MAXPATHLEN); /* Create private directory for socket */ if (mkdtemp(auth_sock_dir) == NULL) { @@ -161,6 +156,9 @@ 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) @@ -178,7 +176,7 @@ restore_uid(); /* Start listening on the socket. */ - if (listen(sock, SSH_LISTEN_BACKLOG) < 0) + if (listen(sock, 5) < 0) packet_disconnect("listen: %.100s", strerror(errno)); /* Allocate a channel for the authentication agent socket. */ @@ -190,15 +188,6 @@ 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) @@ -223,7 +212,13 @@ else do_authenticated1(authctxt); - do_cleanup(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 } /* @@ -261,7 +256,7 @@ compression_level = packet_get_int(); packet_check_eom(); if (compression_level < 1 || compression_level > 9) { - packet_send_debug("Received invalid compression level %d.", + packet_send_debug("Received illegal compression level %d.", compression_level); break; } @@ -396,7 +391,7 @@ /* Fork the child. */ if ((pid = fork()) == 0) { - is_child = 1; + fatal_remove_all_cleanups(); /* Child. Reinitialize the log since the pid has changed. */ log_init(__progname, options.log_level, options.log_facility, log_stderr); @@ -461,11 +456,7 @@ close(perr[1]); if (compat20) { - if (s->is_subsystem) { - close(perr[0]); - perr[0] = -1; - } - session_set_fds(s, pin[1], pout[0], perr[0]); + session_set_fds(s, pin[1], pout[0], s->is_subsystem ? -1 : perr[0]); } else { /* Enter the interactive session. */ server_loop(pid, pin[1], pout[0], perr[0]); @@ -508,7 +499,7 @@ /* Fork the child. */ if ((pid = fork()) == 0) { - is_child = 1; + fatal_remove_all_cleanups(); /* Child. Reinitialize the log because the pid has changed. */ log_init(__progname, options.log_level, options.log_facility, log_stderr); @@ -596,13 +587,6 @@ 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); } @@ -610,6 +594,7 @@ void do_login(Session *s, const char *command) { + char *time_string; socklen_t fromlen; struct sockaddr_storage from; struct passwd * pw = s->pw; @@ -625,7 +610,7 @@ if (getpeername(packet_get_connection_in(), (struct sockaddr *) & from, &fromlen) < 0) { debug("getpeername: %.100s", strerror(errno)); - cleanup_exit(255); + fatal_cleanup(); } } @@ -639,7 +624,16 @@ if (check_quietlogin(s, command)) return; - display_loginmsg(); + 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); + } do_motd(); } @@ -796,7 +790,7 @@ env[0] = NULL; #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); @@ -804,10 +798,6 @@ 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); child_set_env(&env, &envsize, "HOME", pw->pw_dir); @@ -940,7 +930,7 @@ 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, @@ -1022,23 +1012,6 @@ } 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"); - execl(_PATH_PASSWD_PROG, "passwd", (char *)NULL); - perror("passwd"); - } else { - fprintf(stderr, - "Password change required but no TTY available.\n"); - } - exit(1); -} - -static void launch_login(struct passwd *pw, const char *hostname) { /* Launch login(1). */ @@ -1052,40 +1025,6 @@ 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 @@ -1099,18 +1038,11 @@ 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; @@ -1151,40 +1083,40 @@ * closed before building the environment, as we call * get_remote_ipaddr there. */ - child_close_fds(); + 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(); /* - * Must take new environment into use so that .ssh/rc, - * /etc/ssh/sshrc and xauth are run in the proper environment. + * Close any extra file descriptors. Note that there may still be + * descriptors left by system functions. They will be closed later. */ - environ = env; + endpwent(); -#ifdef KRB5 /* - * 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. + * 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); - if (options.kerberos_get_afs_token && k_hasafs() && - (s->authctxt->krb5_ctx != NULL)) { - char cell[64]; + /* + * Must take new environment into use so that .ssh/rc, + * /etc/ssh/sshrc and xauth are run in the proper environment. + */ + environ = env; - debug("Getting AFS token"); - - k_setpag(); - - 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", @@ -1306,7 +1238,7 @@ } s->authctxt = authctxt; s->pw = authctxt->pw; - if (s->pw == NULL || !authctxt->valid) + if (s->pw == NULL) fatal("no user for session %d", s->self); debug("session_open: session %d: link with channel %d", s->self, chanid); s->chanid = chanid; @@ -1386,6 +1318,12 @@ 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); @@ -1422,6 +1360,11 @@ 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); @@ -1512,8 +1455,9 @@ static int session_break_req(Session *s) { + u_int break_length; - packet_get_int(); /* ignored */ + break_length = packet_get_int(); /* ignored */ packet_check_eom(); if (s->ttyfd == -1 || @@ -1523,41 +1467,6 @@ } 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, sizeof(*s->env) * - (s->num_env + 1)); - 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) { static int called = 0; @@ -1604,16 +1513,13 @@ success = session_auth_agent_req(s); } else if (strcmp(rtype, "subsystem") == 0) { success = session_subsystem_req(s); - } else if (strcmp(rtype, "env") == 0) { - success = session_env_req(s); + } else if (strcmp(rtype, "break") == 0) { + success = session_break_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; } @@ -1640,8 +1546,10 @@ * (e.g., due to a dropped connection). */ void -session_pty_cleanup2(Session *s) +session_pty_cleanup2(void *session) { + Session *s = session; + if (s == NULL) { error("session_pty_cleanup: no session"); return; @@ -1672,9 +1580,9 @@ } void -session_pty_cleanup(Session *s) +session_pty_cleanup(void *session) { - PRIVSEP(session_pty_cleanup2(s)); + PRIVSEP(session_pty_cleanup2(session)); } static char * @@ -1742,11 +1650,11 @@ void session_close(Session *s) { - int i; - debug("session_close: session %d pid %ld", s->self, (long)s->pid); - if (s->ttyfd != -1) + if (s->ttyfd != -1) { + fatal_remove_cleanup(session_pty_cleanup, (void *)s); session_pty_cleanup(s); + } if (s->term) xfree(s->term); if (s->display) @@ -1758,12 +1666,6 @@ 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); } @@ -1801,8 +1703,10 @@ * delay detach of session, but release pty, since * the fd's to the child are already closed */ - if (s->ttyfd != -1) + if (s->ttyfd != -1) { + fatal_remove_cleanup(session_pty_cleanup, (void *)s); session_pty_cleanup(s); + } return; } /* detach by removing callback */ @@ -1919,44 +1823,8 @@ do_authenticated2(Authctxt *authctxt) { server_loop2(authctxt); -} - -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); +#if defined(GSSAPI) + if (options.gss_cleanup_creds) + ssh_gssapi_cleanup_creds(NULL); #endif - -#ifdef GSSAPI - if (compat20 && options.gss_cleanup_creds) - ssh_gssapi_cleanup_creds(); -#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); }