]> andersk Git - openssh.git/blobdiff - session.c
- djm@cvs.openbsd.org 2005/06/17 02:44:33
[openssh.git] / session.c
index 00f8785f5d41331e997e70aa921f1290a90f75e0..a1dc6835a2a609fb61a099feb7dd52c297f973ae 100644 (file)
--- a/session.c
+++ b/session.c
@@ -33,7 +33,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: session.c,v 1.169 2003/12/02 17:01:15 markus Exp $");
+RCSID("$OpenBSD: session.c,v 1.182 2005/06/17 02:44:33 djm Exp $");
 
 #include "ssh.h"
 #include "ssh1.h"
@@ -42,7 +42,7 @@ RCSID("$OpenBSD: session.c,v 1.169 2003/12/02 17:01:15 markus 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.169 2003/12/02 17:01:15 markus Exp $");
 #include "session.h"
 #include "monitor_wrap.h"
 
+#if defined(KRB5) && defined(USE_AFS)
+#include <kafs.h>
+#endif
+
 #ifdef GSSAPI
 #include "ssh-gss.h"
 #endif
@@ -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)
@@ -232,6 +245,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;
 
@@ -251,7 +268,7 @@ 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;
                        }
@@ -385,12 +402,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 +484,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 +499,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 +539,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
 
@@ -640,11 +665,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
 
@@ -654,14 +683,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 +727,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 +738,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();
 }
@@ -934,7 +946,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;
@@ -975,7 +988,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
@@ -987,6 +1006,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
@@ -1072,21 +1095,31 @@ 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
 #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 +1272,19 @@ 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);
+               }
+# endif /* USE_PAM */
                if (setusercontext(lc, pw, pw->pw_uid,
                    (LOGIN_SETALL & ~LOGIN_SETPATH)) < 0) {
                        perror("unable to set user context");
@@ -1263,6 +1309,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.
@@ -1292,6 +1345,28 @@ 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");
+#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
 launch_login(struct passwd *pw, const char *hostname)
 {
@@ -1313,6 +1388,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 +1435,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,9 +1467,24 @@ 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 (!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.
@@ -1381,39 +1512,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 +1747,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);
 
@@ -1676,7 +1801,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);
@@ -1748,9 +1873,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 +1883,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 +1965,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 +2107,8 @@ session_exit_message(Session *s, int status)
 void
 session_close(Session *s)
 {
+       u_int i;
+
        debug("session_close: session %d pid %ld", s->self, (long)s->pid);
        if (s->ttyfd != -1)
                session_pty_cleanup(s);
@@ -1959,6 +2123,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);
 }
 
This page took 0.068731 seconds and 4 git commands to generate.