X-Git-Url: http://andersk.mit.edu/gitweb/openssh.git/blobdiff_plain/538f0477658880566d85b8882388986f8719ad2d..be2ca0c95a7bca195f3c7626365f4994af30d34b:/session.c diff --git a/session.c b/session.c index 63b7da1f..99b84394 100644 --- a/session.c +++ b/session.c @@ -33,7 +33,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: session.c,v 1.168 2003/11/21 11:57:03 djm Exp $"); +RCSID("$OpenBSD: session.c,v 1.179 2004/07/17 05:31:41 dtucker Exp $"); #include "ssh.h" #include "ssh1.h" @@ -42,7 +42,7 @@ RCSID("$OpenBSD: session.c,v 1.168 2003/11/21 11:57:03 djm Exp $"); #include "sshpty.h" #include "packet.h" #include "buffer.h" -#include "mpaux.h" +#include "match.h" #include "uidswap.h" #include "compat.h" #include "channels.h" @@ -58,6 +58,10 @@ RCSID("$OpenBSD: session.c,v 1.168 2003/11/21 11:57:03 djm Exp $"); #include "session.h" #include "monitor_wrap.h" +#if defined(KRB5) && defined(USE_AFS) +#include +#endif + #ifdef GSSAPI #include "ssh-gss.h" #endif @@ -177,7 +181,7 @@ auth_input_request_forwarding(struct passwd * pw) 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. */ @@ -189,6 +193,15 @@ auth_input_request_forwarding(struct passwd * pw) 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) @@ -385,12 +398,8 @@ do_exec_no_pty(Session *s, const char *command) 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. */ @@ -471,7 +480,11 @@ do_exec_no_pty(Session *s, const char *command) 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]); @@ -482,6 +495,13 @@ do_exec_no_pty(Session *s, const char *command) 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. @@ -515,7 +535,8 @@ do_exec_pty(Session *s, const char *command) #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 @@ -654,14 +675,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; @@ -693,9 +719,10 @@ do_login(Session *s, const char *command) * 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 @@ -703,30 +730,7 @@ do_login(Session *s, const char *command) 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(); } @@ -987,6 +991,10 @@ do_setup_env(Session *s, const char *shell) 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 @@ -1084,9 +1092,9 @@ do_setup_env(Session *s, const char *shell) } #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 /* @@ -1239,6 +1247,12 @@ do_setusercontext(struct passwd *pw) # ifdef __bsdi__ setpgid(0, 0); # 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"); @@ -1292,6 +1306,23 @@ do_setusercontext(struct passwd *pw) fatal("Failed to set uids to %u.", (u_int) pw->pw_uid); } +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) { @@ -1313,6 +1344,40 @@ launch_login(struct passwd *pw, const char *hostname) 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 @@ -1326,11 +1391,18 @@ do_child(Session *s, const char *command) 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; @@ -1351,6 +1423,13 @@ do_child(Session *s, const char *command) #else /* HAVE_OSF_SIA */ 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) + display_loginmsg(); #endif /* HAVE_OSF_SIA */ } @@ -1381,39 +1460,39 @@ do_child(Session *s, const char *command) * 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(); + + 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) { @@ -1616,12 +1695,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); @@ -1748,9 +1821,8 @@ session_exec_req(Session *s) 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 || @@ -1759,6 +1831,41 @@ session_break_req(Session *s) 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, 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) { @@ -1806,13 +1913,16 @@ session_input_channel_req(Channel *c, const char *rtype) 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; } @@ -1945,6 +2055,8 @@ session_exit_message(Session *s, int status) void session_close(Session *s) { + int i; + debug("session_close: session %d pid %ld", s->self, (long)s->pid); if (s->ttyfd != -1) session_pty_cleanup(s); @@ -1959,6 +2071,12 @@ session_close(Session *s) 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); }