]> andersk Git - gssapi-openssh.git/blobdiff - openssh/session.c
merged OpenSSH 5.3p1 to trunk
[gssapi-openssh.git] / openssh / session.c
index a77dde38f761ff9e4b35f432dfc359c5efd7332b..52a8a0be53c7e2cd5f298650555350e5f5c0118b 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: session.c,v 1.233 2008/03/26 21:28:14 djm Exp $ */
+/* $OpenBSD: session.c,v 1.246 2009/04/17 19:23:06 stevesk Exp $ */
 /*
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
  *                    All rights reserved
@@ -59,6 +59,7 @@
 #include <string.h>
 #include <unistd.h>
 
+#include "openbsd-compat/sys-queue.h"
 #include "xmalloc.h"
 #include "ssh.h"
 #include "ssh1.h"
 #include <kafs.h>
 #endif
 
+#define IS_INTERNAL_SFTP(c) \
+       (!strncmp(c, INTERNAL_SFTP_NAME, sizeof(INTERNAL_SFTP_NAME) - 1) && \
+        (c[sizeof(INTERNAL_SFTP_NAME) - 1] == '\0' || \
+         c[sizeof(INTERNAL_SFTP_NAME) - 1] == ' ' || \
+         c[sizeof(INTERNAL_SFTP_NAME) - 1] == '\t'))
+
 /* func */
 
 Session *session_new(void);
-void   session_set_fds(Session *, int, int, int);
+void   session_set_fds(Session *, int, int, int, int);
 void   session_pty_cleanup(Session *);
 void   session_proctitle(Session *);
 int    session_setup_x11fwd(Session *);
-void   do_exec_pty(Session *, const char *);
-void   do_exec_no_pty(Session *, const char *);
-void   do_exec(Session *, const char *);
+int    do_exec_pty(Session *, const char *);
+int    do_exec_no_pty(Session *, const char *);
+int    do_exec(Session *, const char *);
 void   do_login(Session *, const char *);
 #ifdef LOGIN_NEEDS_UTMPX
 static void    do_pre_login(Session *s);
@@ -117,6 +124,11 @@ static void do_authenticated2(Authctxt *);
 
 static int session_pty_req(Session *);
 
+#ifdef SESSION_HOOKS
+static void execute_session_hook(char* prog, Authctxt *authctxt,
+                                 int startup, int save);
+#endif
+
 /* import */
 extern ServerOptions options;
 extern char *__progname;
@@ -131,8 +143,9 @@ extern Buffer loginmsg;
 const char *original_command = NULL;
 
 /* data */
-#define MAX_SESSIONS 20
-Session        sessions[MAX_SESSIONS];
+static int sessions_first_unused = -1;
+static int sessions_nalloc = 0;
+static Session *sessions = NULL;
 
 #define SUBSYSTEM_NONE         0
 #define SUBSYSTEM_EXT          1
@@ -166,7 +179,7 @@ static int
 auth_input_request_forwarding(struct passwd * pw)
 {
        Channel *nc;
-       int sock;
+       int sock = -1;
        struct sockaddr_un sunaddr;
 
        if (auth_sock_name != NULL) {
@@ -178,51 +191,70 @@ auth_input_request_forwarding(struct passwd * pw)
        temporarily_use_uid(pw);
 
        /* 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);
+       auth_sock_dir = xstrdup("/tmp/ssh-XXXXXXXXXX");
 
        /* Create private directory for socket */
        if (mkdtemp(auth_sock_dir) == NULL) {
                packet_send_debug("Agent forwarding disabled: "
                    "mkdtemp() failed: %.100s", strerror(errno));
                restore_uid();
-               xfree(auth_sock_name);
                xfree(auth_sock_dir);
-               auth_sock_name = NULL;
                auth_sock_dir = NULL;
-               return 0;
+               goto authsock_err;
        }
-       snprintf(auth_sock_name, MAXPATHLEN, "%s/agent.%ld",
-                auth_sock_dir, (long) getpid());
+
+       xasprintf(&auth_sock_name, "%s/agent.%ld",
+           auth_sock_dir, (long) getpid());
 
        /* Create the socket. */
        sock = socket(AF_UNIX, SOCK_STREAM, 0);
-       if (sock < 0)
-               packet_disconnect("socket: %.100s", strerror(errno));
+       if (sock < 0) {
+               error("socket: %.100s", strerror(errno));
+               restore_uid();
+               goto authsock_err;
+       }
 
        /* Bind it to the name. */
        memset(&sunaddr, 0, sizeof(sunaddr));
        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)
-               packet_disconnect("bind: %.100s", strerror(errno));
+       if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0) {
+               error("bind: %.100s", strerror(errno));
+               restore_uid();
+               goto authsock_err;
+       }
 
        /* Restore the privileged uid. */
        restore_uid();
 
        /* Start listening on the socket. */
-       if (listen(sock, SSH_LISTEN_BACKLOG) < 0)
-               packet_disconnect("listen: %.100s", strerror(errno));
+       if (listen(sock, SSH_LISTEN_BACKLOG) < 0) {
+               error("listen: %.100s", strerror(errno));
+               goto authsock_err;
+       }
 
        /* Allocate a channel for the authentication agent socket. */
+       /* this shouldn't matter if its hpn or not - cjr */
        nc = channel_new("auth socket",
            SSH_CHANNEL_AUTH_SOCKET, sock, sock, -1,
            CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT,
            0, "auth socket", 1);
-       strlcpy(nc->path, auth_sock_name, sizeof(nc->path));
+       nc->path = xstrdup(auth_sock_name);
        return 1;
+
+ authsock_err:
+       if (auth_sock_name != NULL)
+               xfree(auth_sock_name);
+       if (auth_sock_dir != NULL) {
+               rmdir(auth_sock_dir);
+               xfree(auth_sock_dir);
+       }
+       if (sock != -1)
+               close(sock);
+       auth_sock_name = NULL;
+       auth_sock_dir = NULL;
+       return 0;
 }
 
 static void
@@ -249,6 +281,21 @@ do_authenticated(Authctxt *authctxt)
        else
                do_authenticated1(authctxt);
 
+#ifdef SESSION_HOOKS
+        if (options.session_hooks_allow &&
+            options.session_hooks_shutdown_cmd)
+        {
+            execute_session_hook(options.session_hooks_shutdown_cmd,
+                                 authctxt,
+                                 /* startup = */ 0, /* save = */ 0);
+
+            if (authctxt->session_env_file)
+            {
+                free(authctxt->session_env_file);
+            }
+        }
+#endif
+
        do_cleanup(authctxt);
 }
 
@@ -335,7 +382,8 @@ do_authenticated1(Authctxt *authctxt)
                        break;
 
                case SSH_CMSG_AGENT_REQUEST_FORWARDING:
-                       if (no_agent_forwarding_flag || compat13) {
+                       if (!options.allow_agent_forwarding ||
+                           no_agent_forwarding_flag || compat13) {
                                debug("Authentication agent forwarding not permitted for this authentication.");
                                break;
                        }
@@ -354,7 +402,7 @@ do_authenticated1(Authctxt *authctxt)
                        }
                        debug("Received TCP/IP port forwarding request.");
                        if (channel_input_port_forward_request(s->pw->pw_uid == 0,
-                           options.gateway_ports) < 0) {
+                             options.gateway_ports) < 0) {
                                debug("Port forwarding failed.");
                                break;
                        }
@@ -371,10 +419,14 @@ do_authenticated1(Authctxt *authctxt)
                        if (type == SSH_CMSG_EXEC_CMD) {
                                command = packet_get_string(&dlen);
                                debug("Exec command '%.500s'", command);
-                               do_exec(s, command);
+                               if (do_exec(s, command) != 0)
+                                       packet_disconnect(
+                                           "command execution failed");
                                xfree(command);
                        } else {
-                               do_exec(s, NULL);
+                               if (do_exec(s, NULL) != 0)
+                                       packet_disconnect(
+                                           "shell execution failed");
                        }
                        packet_check_eom();
                        session_close(s);
@@ -399,41 +451,84 @@ do_authenticated1(Authctxt *authctxt)
        }
 }
 
+#define USE_PIPES
 /*
  * This is called to fork and execute a command when we have no tty.  This
  * will call do_child from the child, and server_loop from the parent after
  * setting up file descriptors and such.
  */
-void
+int
 do_exec_no_pty(Session *s, const char *command)
 {
        pid_t pid;
 
 #ifdef USE_PIPES
        int pin[2], pout[2], perr[2];
+
        /* Allocate pipes for communicating with the program. */
-       if (pipe(pin) < 0 || pipe(pout) < 0 || pipe(perr) < 0)
-               packet_disconnect("Could not create pipes: %.100s",
-                                 strerror(errno));
-#else /* USE_PIPES */
+       if (pipe(pin) < 0) {
+               error("%s: pipe in: %.100s", __func__, strerror(errno));
+               return -1;
+       }
+       if (pipe(pout) < 0) {
+               error("%s: pipe out: %.100s", __func__, strerror(errno));
+               close(pin[0]);
+               close(pin[1]);
+               return -1;
+       }
+       if (pipe(perr) < 0) {
+               error("%s: pipe err: %.100s", __func__, strerror(errno));
+               close(pin[0]);
+               close(pin[1]);
+               close(pout[0]);
+               close(pout[1]);
+               return -1;
+       }
+#else
        int inout[2], err[2];
+
        /* Uses socket pairs to communicate with the program. */
-       if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) < 0 ||
-           socketpair(AF_UNIX, SOCK_STREAM, 0, err) < 0)
-               packet_disconnect("Could not create socket pairs: %.100s",
-                                 strerror(errno));
-#endif /* USE_PIPES */
+       if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) < 0) {
+               error("%s: socketpair #1: %.100s", __func__, strerror(errno));
+               return -1;
+       }
+       if (socketpair(AF_UNIX, SOCK_STREAM, 0, err) < 0) {
+               error("%s: socketpair #2: %.100s", __func__, strerror(errno));
+               close(inout[0]);
+               close(inout[1]);
+               return -1;
+       }
+#endif
+
        if (s == NULL)
                fatal("do_exec_no_pty: no session");
 
        session_proctitle(s);
 
        /* Fork the child. */
-       if ((pid = fork()) == 0) {
+       switch ((pid = fork())) {
+       case -1:
+               error("%s: fork: %.100s", __func__, strerror(errno));
+#ifdef USE_PIPES
+               close(pin[0]);
+               close(pin[1]);
+               close(pout[0]);
+               close(pout[1]);
+               close(perr[0]);
+               close(perr[1]);
+#else
+               close(inout[0]);
+               close(inout[1]);
+               close(err[0]);
+               close(err[1]);
+#endif
+               return -1;
+       case 0:
                is_child = 1;
 
                /* Child.  Reinitialize the log since the pid has changed. */
-               log_init(__progname, options.log_level, options.log_facility, log_stderr);
+               log_init(__progname, options.log_level,
+                   options.log_facility, log_stderr);
 
                /*
                 * Create a new session and process group since the 4.4BSD
@@ -463,7 +558,7 @@ do_exec_no_pty(Session *s, const char *command)
                if (dup2(perr[1], 2) < 0)
                        perror("dup2 stderr");
                close(perr[1]);
-#else /* USE_PIPES */
+#else
                /*
                 * Redirect stdin, stdout, and stderr.  Stdin and stdout will
                 * use the same socket, as some programs (particularly rdist)
@@ -473,11 +568,14 @@ do_exec_no_pty(Session *s, const char *command)
                close(err[1]);
                if (dup2(inout[0], 0) < 0)      /* stdin */
                        perror("dup2 stdin");
-               if (dup2(inout[0], 1) < 0)      /* stdout.  Note: same socket as stdin. */
+               if (dup2(inout[0], 1) < 0)      /* stdout (same as stdin) */
                        perror("dup2 stdout");
+               close(inout[0]);
                if (dup2(err[0], 2) < 0)        /* stderr */
                        perror("dup2 stderr");
-#endif /* USE_PIPES */
+               close(err[0]);
+#endif
+
 
 #ifdef _UNICOS
                cray_init_job(s->pw); /* set up cray jid and tmpdir */
@@ -486,19 +584,28 @@ do_exec_no_pty(Session *s, const char *command)
                /* Do processing for the child (exec command etc). */
                do_child(s, command);
                /* NOTREACHED */
+       default:
+               break;
        }
+
 #ifdef _UNICOS
        signal(WJSIGNAL, cray_job_termination_handler);
 #endif /* _UNICOS */
 #ifdef HAVE_CYGWIN
-       if (is_winnt)
-               cygwin_set_impersonation_token(INVALID_HANDLE_VALUE);
+       cygwin_set_impersonation_token(INVALID_HANDLE_VALUE);
 #endif
-       if (pid < 0)
-               packet_disconnect("fork failed: %.100s", strerror(errno));
+
        s->pid = pid;
        /* Set interactive/non-interactive mode. */
        packet_set_interactive(s->display != NULL);
+
+       /*
+        * 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);
+
 #ifdef USE_PIPES
        /* We are the parent.  Close the child sides of the pipes. */
        close(pin[0]);
@@ -510,35 +617,32 @@ do_exec_no_pty(Session *s, const char *command)
                        close(perr[0]);
                        perr[0] = -1;
                }
-               session_set_fds(s, pin[1], pout[0], perr[0]);
+               session_set_fds(s, pin[1], pout[0], perr[0], 0);
        } else {
                /* Enter the interactive session. */
                server_loop(pid, pin[1], pout[0], perr[0]);
                /* server_loop has closed pin[1], pout[0], and perr[0]. */
        }
-#else /* USE_PIPES */
+#else
        /* We are the parent.  Close the child sides of the socket pairs. */
        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 (compat20) {
-               session_set_fds(s, inout[1], inout[1], s->is_subsystem ? -1 : err[1]);
+               session_set_fds(s, inout[1], inout[1],
+                   s->is_subsystem ? -1 : err[1], 0);
+               if (s->is_subsystem)
+                       close(err[1]);
        } else {
                server_loop(pid, inout[1], inout[1], err[1]);
                /* server_loop has closed inout[1] and err[1]. */
        }
-#endif /* USE_PIPES */
+#endif
+       return 0;
 }
 
 /*
@@ -547,7 +651,7 @@ do_exec_no_pty(Session *s, const char *command)
  * setting up file descriptors, controlling tty, updating wtmp, utmp,
  * lastlog, and other such operations.
  */
-void
+int
 do_exec_pty(Session *s, const char *command)
 {
        int fdout, ptyfd, ttyfd, ptymaster;
@@ -558,12 +662,46 @@ do_exec_pty(Session *s, const char *command)
        ptyfd = s->ptyfd;
        ttyfd = s->ttyfd;
 
+       /*
+        * Create another descriptor of the pty master side for use as the
+        * standard input.  We could use the original descriptor, but this
+        * simplifies code in server_loop.  The descriptor is bidirectional.
+        * Do this before forking (and cleanup in the child) so as to
+        * detect and gracefully fail out-of-fd conditions.
+        */
+       if ((fdout = dup(ptyfd)) < 0) {
+               error("%s: dup #1: %s", __func__, strerror(errno));
+               close(ttyfd);
+               close(ptyfd);
+               return -1;
+       }
+       /* we keep a reference to the pty master */
+       if ((ptymaster = dup(ptyfd)) < 0) {
+               error("%s: dup #2: %s", __func__, strerror(errno));
+               close(ttyfd);
+               close(ptyfd);
+               close(fdout);
+               return -1;
+       }
+
        /* Fork the child. */
-       if ((pid = fork()) == 0) {
+       switch ((pid = fork())) {
+       case -1:
+               error("%s: fork: %.100s", __func__, strerror(errno));
+               close(fdout);
+               close(ptymaster);
+               close(ttyfd);
+               close(ptyfd);
+               return -1;
+       case 0:
                is_child = 1;
 
+               close(fdout);
+               close(ptymaster);
+
                /* Child.  Reinitialize the log because the pid has changed. */
-               log_init(__progname, options.log_level, options.log_facility, log_stderr);
+               log_init(__progname, options.log_level,
+                   options.log_facility, log_stderr);
                /* Close the master side of the pseudo tty. */
                close(ptyfd);
 
@@ -594,48 +732,38 @@ do_exec_pty(Session *s, const char *command)
                        do_pre_login(s);
 # endif
 #endif
-
-               /* Do common processing for the child, such as execing the command. */
+               /*
+                * Do common processing for the child, such as execing
+                * the command.
+                */
                do_child(s, command);
                /* NOTREACHED */
+       default:
+               break;
        }
+
 #ifdef _UNICOS
        signal(WJSIGNAL, cray_job_termination_handler);
 #endif /* _UNICOS */
 #ifdef HAVE_CYGWIN
-       if (is_winnt)
-               cygwin_set_impersonation_token(INVALID_HANDLE_VALUE);
+       cygwin_set_impersonation_token(INVALID_HANDLE_VALUE);
 #endif
-       if (pid < 0)
-               packet_disconnect("fork failed: %.100s", strerror(errno));
+
        s->pid = pid;
 
        /* Parent.  Close the slave side of the pseudo tty. */
        close(ttyfd);
 
-       /*
-        * Create another descriptor of the pty master side for use as the
-        * standard input.  We could use the original descriptor, but this
-        * simplifies code in server_loop.  The descriptor is bidirectional.
-        */
-       fdout = dup(ptyfd);
-       if (fdout < 0)
-               packet_disconnect("dup #1 failed: %.100s", strerror(errno));
-
-       /* we keep a reference to the pty master */
-       ptymaster = dup(ptyfd);
-       if (ptymaster < 0)
-               packet_disconnect("dup #2 failed: %.100s", strerror(errno));
-       s->ptymaster = ptymaster;
-
        /* Enter interactive session. */
+       s->ptymaster = ptymaster;
        packet_set_interactive(1);
        if (compat20) {
-               session_set_fds(s, ptyfd, fdout, -1);
+               session_set_fds(s, ptyfd, fdout, -1, 1);
        } else {
                server_loop(pid, ptyfd, fdout, -1);
                /* server_loop _has_ closed ptyfd and fdout. */
        }
+       return 0;
 }
 
 #ifdef LOGIN_NEEDS_UTMPX
@@ -670,13 +798,15 @@ do_pre_login(Session *s)
  * This is called to fork and execute a command.  If another command is
  * to be forced, execute that instead.
  */
-void
+int
 do_exec(Session *s, const char *command)
 {
+       int ret;
+
        if (options.adm_forced_command) {
                original_command = command;
                command = options.adm_forced_command;
-               if (strcmp(INTERNAL_SFTP_NAME, command) == 0)
+               if (IS_INTERNAL_SFTP(command))
                        s->is_subsystem = SUBSYSTEM_INT_SFTP;
                else if (s->is_subsystem)
                        s->is_subsystem = SUBSYSTEM_EXT;
@@ -684,13 +814,33 @@ do_exec(Session *s, const char *command)
        } else if (forced_command) {
                original_command = command;
                command = forced_command;
-               if (strcmp(INTERNAL_SFTP_NAME, command) == 0)
+               if (IS_INTERNAL_SFTP(command))
                        s->is_subsystem = SUBSYSTEM_INT_SFTP;
                else if (s->is_subsystem)
                        s->is_subsystem = SUBSYSTEM_EXT;
                debug("Forced command (key option) '%.900s'", command);
        }
 
+#if defined(SESSION_HOOKS)
+       if (options.session_hooks_allow &&
+           (options.session_hooks_startup_cmd ||
+            options.session_hooks_shutdown_cmd))
+       {
+               char env_file[1000];
+               struct stat st;
+               do
+               {
+                       snprintf(env_file,
+                                sizeof(env_file),
+                                "/tmp/ssh_env_%d%d%d",
+                                getuid(),
+                                getpid(),
+                                rand());
+               } while (stat(env_file, &st)==0);
+               s->authctxt->session_env_file = strdup(env_file);
+       }
+#endif
+
 #ifdef SSH_AUDIT_EVENTS
        if (command != NULL)
                PRIVSEP(audit_run_command(command));
@@ -703,9 +853,9 @@ do_exec(Session *s, const char *command)
        }
 #endif
        if (s->ttyfd != -1)
-               do_exec_pty(s, command);
+               ret = do_exec_pty(s, command);
        else
-               do_exec_no_pty(s, command);
+               ret = do_exec_no_pty(s, command);
 
        original_command = NULL;
 
@@ -715,6 +865,8 @@ do_exec(Session *s, const char *command)
         * multiple copies of the login messages.
         */
        buffer_clear(&loginmsg);
+
+       return ret;
 }
 
 /* administrative, login(1)-like work */
@@ -734,7 +886,7 @@ do_login(Session *s, const char *command)
        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));
                        cleanup_exit(255);
                }
@@ -819,7 +971,7 @@ check_quietlogin(Session *s, const char *command)
 
 /*
  * Sets the value of the given variable in the environment.  If the variable
- * already exists, its value is overriden.
+ * already exists, its value is overridden.
  */
 void
 child_set_env(char ***envp, u_int *envsizep, const char *name,
@@ -917,6 +1069,117 @@ read_environment_file(char ***env, u_int *envsize,
        fclose(f);
 }
 
+#ifdef SESSION_HOOKS
+#define SSH_SESSION_ENV_FILE "SSH_SESSION_ENV_FILE"
+
+typedef enum { no_op, execute, clear_env, restore_env,
+               read_env, save_or_rm_env } session_action_t;
+
+static session_action_t action_order[2][5] = {
+    { clear_env, read_env, execute, save_or_rm_env, restore_env }, /*shutdown*/
+    { execute, read_env, save_or_rm_env, no_op, no_op }            /*startup */
+};
+
+static
+void execute_session_hook(char* prog, Authctxt *authctxt,
+                          int startup, int save)
+{
+    extern char **environ;
+
+    struct stat  st;
+    char         **saved_env, **tmpenv;
+    char         *env_file = authctxt->session_env_file;
+    int          i, status = 0;
+
+    for (i=0; i<5; i++)
+    {
+        switch (action_order[startup][i])
+        {
+          case no_op:
+            break;
+
+          case execute:
+            {
+                FILE* fp;
+                char  buf[1000];
+
+                snprintf(buf,
+                         sizeof(buf),
+                         "%s -c '%s'",
+                         authctxt->pw->pw_shell,
+                         prog);
+
+                debug("executing session hook: [%s]", buf);
+                setenv(SSH_SESSION_ENV_FILE, env_file, /* overwrite = */ 1);
+
+                /* flusing is recommended in the popen(3) man page, to avoid
+                   intermingling of output */
+                fflush(stdout);
+                fflush(stderr);
+                if ((fp=popen(buf, "w")) == NULL)
+                {
+                    perror("Unable to run session hook");
+                    return;
+                }
+                status = pclose(fp);
+                debug2("session hook executed, status=%d", status);
+                unsetenv(SSH_SESSION_ENV_FILE);
+            }
+            break;
+
+          case clear_env:
+            saved_env = environ;
+            tmpenv = (char**) malloc(sizeof(char*));
+            tmpenv[0] = NULL;
+            environ = tmpenv;
+            break;
+
+          case restore_env:
+            environ = saved_env;
+            free(tmpenv);
+            break;
+
+          case read_env:
+            if (status==0 && stat(env_file, &st)==0)
+            {
+                int envsize = 0;
+
+                debug("reading environment from %s", env_file);
+                while (environ[envsize++]) ;
+                read_environment_file(&environ, &envsize, env_file);
+            }
+            break;
+
+          case save_or_rm_env:
+            if (status==0 && save)
+            {
+                FILE* fp;
+                int    envcount=0;
+
+                debug2("saving environment to %s", env_file);
+                if ((fp = fopen(env_file, "w")) == NULL) /* hmm: file perms? */
+                {
+                    perror("Unable to save session hook info");
+                }
+                while (environ[envcount])
+                {
+                    fprintf(fp, "%s\n", environ[envcount++]);
+                }
+                fflush(fp);
+                fclose(fp);
+            }
+            else if (stat(env_file, &st)==0)
+            {
+                debug2("removing environment file %s", env_file);
+                remove(env_file);
+            }
+            break;
+        }
+    }
+
+}
+#endif
+
 #ifdef HAVE_ETC_DEFAULT_LOGIN
 /*
  * Return named variable from specified environment, or NULL if not present.
@@ -1003,7 +1266,7 @@ do_setup_env(Session *s, const char *shell)
        u_int i, envsize;
        char **env, *laddr;
        struct passwd *pw = s->pw;
-#ifndef HAVE_LOGIN_CAP
+#if !defined (HAVE_LOGIN_CAP) && !defined (HAVE_CYGWIN)
        char *path = NULL;
 #endif
 
@@ -1080,6 +1343,23 @@ do_setup_env(Session *s, const char *shell)
        if (getenv("TZ"))
                child_set_env(&env, &envsize, "TZ", getenv("TZ"));
 
+#ifdef GSI /* GSI shared libs typically installed in non-system locations. */
+       {
+               char *cp;
+
+               if ((cp = getenv("LD_LIBRARY_PATH")) != NULL)
+                       child_set_env(&env, &envsize, "LD_LIBRARY_PATH", cp);
+               if ((cp = getenv("LIBPATH")) != NULL)
+                       child_set_env(&env, &envsize, "LIBPATH", cp);
+               if ((cp = getenv("SHLIB_PATH")) != NULL)
+                       child_set_env(&env, &envsize, "SHLIB_PATH", cp);
+               if ((cp = getenv("LD_LIBRARYN32_PATH")) != NULL)
+                       child_set_env(&env, &envsize, "LD_LIBRARYN32_PATH",cp);
+               if ((cp = getenv("LD_LIBRARY64_PATH")) != NULL)
+                       child_set_env(&env, &envsize, "LD_LIBRARY64_PATH",cp);
+       }
+#endif
+
        /* Set custom environment options from RSA authentication. */
        if (!options.use_login) {
                while (custom_environment) {
@@ -1204,7 +1484,7 @@ do_rc_files(Session *s, const char *shell)
 
        /* ignore _PATH_SSH_USER_RC for subsystems and admin forced commands */
        if (!s->is_subsystem && options.adm_forced_command == NULL &&
-           !no_user_rc &&  (stat(_PATH_SSH_USER_RC, &st) >= 0)) {
+           !no_user_rc && stat(_PATH_SSH_USER_RC, &st) >= 0) {
                snprintf(cmd, sizeof cmd, "%s -c '%s %s'",
                    shell, _PATH_BSHELL, _PATH_SSH_USER_RC);
                if (debug_flag)
@@ -1355,11 +1635,6 @@ do_setusercontext(struct passwd *pw)
        if (getuid() == 0 || geteuid() == 0)
 #endif /* HAVE_CYGWIN */
        {
-
-#ifdef HAVE_SETPCRED
-               if (setpcred(pw->pw_name, (char **)NULL) == -1)
-                       fatal("Failed to set process credentials");
-#endif /* HAVE_SETPCRED */
 #ifdef HAVE_LOGIN_CAP
 # ifdef __bsdi__
                setpgid(0, 0);
@@ -1427,6 +1702,10 @@ do_setusercontext(struct passwd *pw)
                        free(chroot_path);
                }
 
+#ifdef HAVE_SETPCRED
+               if (setpcred(pw->pw_name, (char **)NULL) == -1)
+                       fatal("Failed to set process credentials");
+#endif /* HAVE_SETPCRED */
 #ifdef HAVE_LOGIN_CAP
                if (setusercontext(lc, pw, pw->pw_uid, LOGIN_SETUSER) < 0) {
                        perror("unable to set user context (setuser)");
@@ -1438,9 +1717,6 @@ do_setusercontext(struct passwd *pw)
 #endif
        }
 
-#ifdef HAVE_CYGWIN
-       if (is_winnt)
-#endif
        if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid)
                fatal("Failed to set uids to %u.", (u_int) pw->pw_uid);
 
@@ -1540,6 +1816,19 @@ do_child(Session *s, const char *command)
        char *argv[ARGV_MAX];
        const char *shell, *shell0, *hostname = NULL;
        struct passwd *pw = s->pw;
+       int r = 0;
+
+#ifdef AFS_KRB5
+/* Default place to look for aklog. */
+#ifdef AKLOG_PATH
+#define KPROGDIR AKLOG_PATH
+#else
+#define KPROGDIR "/usr/bin/aklog"
+#endif /* AKLOG_PATH */
+
+       struct stat st;
+       char *aklog_path;
+#endif /* AFS_KRB5 */
 
        /* remove hostkey from the child's memory */
        destroy_sensitive_data();
@@ -1653,14 +1942,53 @@ do_child(Session *s, const char *command)
        }
 #endif
 
+#ifdef AFS_KRB5
+
+       /* User has authenticated, and if a ticket was going to be
+        * passed we would have it.  KRB5CCNAME should already be set.
+        * Now try to get an AFS token using aklog.
+        */
+       if (k_hasafs()) {  /* Do we have AFS? */
+
+               aklog_path = xstrdup(KPROGDIR);
+
+               /*
+                * Make sure it exists before we try to run it
+                */
+               if (stat(aklog_path, &st) == 0) {
+                       debug("Running %s to get afs token.",aklog_path);
+                       system(aklog_path);
+               } else {
+                       debug("%s does not exist.",aklog_path);
+               }
+
+               xfree(aklog_path);
+       }
+#endif /* AFS_KRB5 */
+
+#ifdef SESSION_HOOKS
+        if (options.session_hooks_allow &&
+            options.session_hooks_startup_cmd)
+        {
+            execute_session_hook(options.session_hooks_startup_cmd,
+                                 s->authctxt,
+                                 /* startup = */ 1,
+                                 options.session_hooks_shutdown_cmd != NULL);
+        }
+#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));
+               /* Suppress missing homedir warning for chroot case */
 #ifdef HAVE_LOGIN_CAP
-               if (login_getcapbool(lc, "requirehome", 0))
-                       exit(1);
+               r = login_getcapbool(lc, "requirehome", 0);
 #endif
+               if (r || options.chroot_directory == NULL)
+                       fprintf(stderr, "Could not chdir to home "
+                           "directory %s: %s\n", pw->pw_dir,
+                           strerror(errno));
+               if (r)
+                       exit(1);
        }
 
        closefrom(STDERR_FILENO + 1);
@@ -1676,8 +2004,8 @@ do_child(Session *s, const char *command)
                int i;
                char *p, *args;
 
-               setproctitle("%s@internal-sftp-server", s->pw->pw_name);
-               args = strdup(command ? command : "sftp-server");
+               setproctitle("%s@%s", s->pw->pw_name, INTERNAL_SFTP_NAME);
+               args = xstrdup(command ? command : "sftp-server");
                for (i = 0, (p = strtok(args, " ")); p; (p = strtok(NULL, " ")))
                        if (i < ARGV_MAX - 1)
                                argv[i++] = p;
@@ -1719,7 +2047,7 @@ do_child(Session *s, const char *command)
                /* Execute the shell. */
                argv[0] = argv0;
                argv[1] = NULL;
-               execve(shell, argv, env);
+               execve(shell, argv, environ);
 
                /* Executing the shell failed. */
                perror(shell);
@@ -1733,48 +2061,84 @@ do_child(Session *s, const char *command)
        argv[1] = "-c";
        argv[2] = (char *) command;
        argv[3] = NULL;
-       execve(shell, argv, env);
+       execve(shell, argv, environ);
        perror(shell);
        exit(1);
 }
 
+void
+session_unused(int id)
+{
+       debug3("%s: session id %d unused", __func__, id);
+       if (id >= options.max_sessions ||
+           id >= sessions_nalloc) {
+               fatal("%s: insane session id %d (max %d nalloc %d)",
+                   __func__, id, options.max_sessions, sessions_nalloc);
+       }
+       bzero(&sessions[id], sizeof(*sessions));
+       sessions[id].self = id;
+       sessions[id].used = 0;
+       sessions[id].chanid = -1;
+       sessions[id].ptyfd = -1;
+       sessions[id].ttyfd = -1;
+       sessions[id].ptymaster = -1;
+       sessions[id].x11_chanids = NULL;
+       sessions[id].next_unused = sessions_first_unused;
+       sessions_first_unused = id;
+}
+
 Session *
 session_new(void)
 {
-       int i;
-       static int did_init = 0;
-       if (!did_init) {
-               debug("session_new: init");
-               for (i = 0; i < MAX_SESSIONS; i++) {
-                       sessions[i].used = 0;
+       Session *s, *tmp;
+
+       if (sessions_first_unused == -1) {
+               if (sessions_nalloc >= options.max_sessions)
+                       return NULL;
+               debug2("%s: allocate (allocated %d max %d)",
+                   __func__, sessions_nalloc, options.max_sessions);
+               tmp = xrealloc(sessions, sessions_nalloc + 1,
+                   sizeof(*sessions));
+               if (tmp == NULL) {
+                       error("%s: cannot allocate %d sessions",
+                           __func__, sessions_nalloc + 1);
+                       return NULL;
                }
-               did_init = 1;
+               sessions = tmp;
+               session_unused(sessions_nalloc++);
        }
-       for (i = 0; i < MAX_SESSIONS; i++) {
-               Session *s = &sessions[i];
-               if (! s->used) {
-                       memset(s, 0, sizeof(*s));
-                       s->chanid = -1;
-                       s->ptyfd = -1;
-                       s->ttyfd = -1;
-                       s->used = 1;
-                       s->self = i;
-                       s->x11_chanids = NULL;
-                       debug("session_new: session %d", i);
-                       return s;
-               }
+
+       if (sessions_first_unused >= sessions_nalloc ||
+           sessions_first_unused < 0) {
+               fatal("%s: insane first_unused %d max %d nalloc %d",
+                   __func__, sessions_first_unused, options.max_sessions,
+                   sessions_nalloc);
        }
-       return NULL;
+
+       s = &sessions[sessions_first_unused];
+       if (s->used) {
+               fatal("%s: session %d already used",
+                   __func__, sessions_first_unused);
+       }
+       sessions_first_unused = s->next_unused;
+       s->used = 1;
+       s->next_unused = -1;
+       debug("session_new: session %d", s->self);
+
+       return s;
 }
 
 static void
 session_dump(void)
 {
        int i;
-       for (i = 0; i < MAX_SESSIONS; i++) {
+       for (i = 0; i < sessions_nalloc; i++) {
                Session *s = &sessions[i];
-               debug("dump: used %d session %d %p channel %d pid %ld",
+
+               debug("dump: used %d next_unused %d session %d %p "
+                   "channel %d pid %ld",
                    s->used,
+                   s->next_unused,
                    s->self,
                    s,
                    s->chanid,
@@ -1804,7 +2168,7 @@ Session *
 session_by_tty(char *tty)
 {
        int i;
-       for (i = 0; i < MAX_SESSIONS; i++) {
+       for (i = 0; i < sessions_nalloc; i++) {
                Session *s = &sessions[i];
                if (s->used && s->ttyfd != -1 && strcmp(s->tty, tty) == 0) {
                        debug("session_by_tty: session %d tty %s", i, tty);
@@ -1820,10 +2184,11 @@ static Session *
 session_by_channel(int id)
 {
        int i;
-       for (i = 0; i < MAX_SESSIONS; i++) {
+       for (i = 0; i < sessions_nalloc; i++) {
                Session *s = &sessions[i];
                if (s->used && s->chanid == id) {
-                       debug("session_by_channel: session %d channel %d", i, id);
+                       debug("session_by_channel: session %d channel %d",
+                           i, id);
                        return s;
                }
        }
@@ -1837,7 +2202,7 @@ session_by_x11_channel(int id)
 {
        int i, j;
 
-       for (i = 0; i < MAX_SESSIONS; i++) {
+       for (i = 0; i < sessions_nalloc; i++) {
                Session *s = &sessions[i];
 
                if (s->x11_chanids == NULL || !s->used)
@@ -1860,7 +2225,7 @@ session_by_pid(pid_t pid)
 {
        int i;
        debug("session_by_pid: pid %ld", (long)pid);
-       for (i = 0; i < MAX_SESSIONS; i++) {
+       for (i = 0; i < sessions_nalloc; i++) {
                Session *s = &sessions[i];
                if (s->used && s->pid == pid)
                        return s;
@@ -1916,7 +2281,8 @@ session_pty_req(Session *s)
 
        /* Allocate a pty and open it. */
        debug("Allocating pty.");
-       if (!PRIVSEP(pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, sizeof(s->tty)))) {
+       if (!PRIVSEP(pty_allocate(&s->ptyfd, &s->ttyfd, s->tty,
+           sizeof(s->tty)))) {
                if (s->term)
                        xfree(s->term);
                s->term = NULL;
@@ -1969,8 +2335,7 @@ session_subsystem_req(Session *s)
                                s->is_subsystem = SUBSYSTEM_EXT;
                        }
                        debug("subsystem: exec() %s", cmd);
-                       do_exec(s, cmd);
-                       success = 1;
+                       success = do_exec(s, cmd) == 0;
                        break;
                }
        }
@@ -2013,19 +2378,19 @@ static int
 session_shell_req(Session *s)
 {
        packet_check_eom();
-       do_exec(s, NULL);
-       return 1;
+       return do_exec(s, NULL) == 0;
 }
 
 static int
 session_exec_req(Session *s)
 {
-       u_int len;
+       u_int len, success;
+
        char *command = packet_get_string(&len);
        packet_check_eom();
-       do_exec(s, command);
+       success = do_exec(s, command) == 0;
        xfree(command);
-       return 1;
+       return success;
 }
 
 static int
@@ -2035,8 +2400,7 @@ session_break_req(Session *s)
        packet_get_int();       /* ignored */
        packet_check_eom();
 
-       if (s->ttyfd == -1 ||
-           tcsendbreak(s->ttyfd, 0) < 0)
+       if (s->ttyfd == -1 || tcsendbreak(s->ttyfd, 0) < 0)
                return 0;
        return 1;
 }
@@ -2081,7 +2445,7 @@ session_auth_agent_req(Session *s)
 {
        static int called = 0;
        packet_check_eom();
-       if (no_agent_forwarding_flag) {
+       if (no_agent_forwarding_flag || !options.allow_agent_forwarding) {
                debug("session_auth_agent_req: no_agent_forwarding_flag");
                return 0;
        }
@@ -2137,7 +2501,7 @@ session_input_channel_req(Channel *c, const char *rtype)
 }
 
 void
-session_set_fds(Session *s, int fdin, int fdout, int fderr)
+session_set_fds(Session *s, int fdin, int fdout, int fderr, int is_tty)
 {
        if (!compat20)
                fatal("session_set_fds: called for proto != 2.0");
@@ -2147,11 +2511,16 @@ session_set_fds(Session *s, int fdin, int fdout, int fderr)
         */
        if (s->chanid == -1)
                fatal("no channel for session %d", s->self);
+       if (options.hpn_disabled)
        channel_set_fds(s->chanid,
            fdout, fdin, fderr,
            fderr == -1 ? CHAN_EXTENDED_IGNORE : CHAN_EXTENDED_READ,
-           1,
-           CHAN_SES_WINDOW_DEFAULT);
+           1, is_tty, CHAN_SES_WINDOW_DEFAULT);
+       else 
+               channel_set_fds(s->chanid,
+                   fdout, fdin, fderr,
+                   fderr == -1 ? CHAN_EXTENDED_IGNORE : CHAN_EXTENDED_READ,
+                   1, is_tty, options.hpn_buffer_size);
 }
 
 /*
@@ -2183,8 +2552,9 @@ session_pty_cleanup2(Session *s)
         * the pty cleanup, so that another process doesn't get this pty
         * while we're still cleaning up.
         */
-       if (close(s->ptymaster) < 0)
-               error("close(s->ptymaster/%d): %s", s->ptymaster, strerror(errno));
+       if (s->ptymaster != -1 && close(s->ptymaster) < 0)
+               error("close(s->ptymaster/%d): %s",
+                   s->ptymaster, strerror(errno));
 
        /* unlink pty from session */
        s->ttyfd = -1;
@@ -2344,7 +2714,6 @@ session_close(Session *s)
                xfree(s->auth_data);
        if (s->auth_proto)
                xfree(s->auth_proto);
-       s->used = 0;
        if (s->env != NULL) {
                for (i = 0; i < s->num_env; i++) {
                        xfree(s->env[i].name);
@@ -2353,6 +2722,7 @@ session_close(Session *s)
                xfree(s->env);
        }
        session_proctitle(s);
+       session_unused(s->self);
 }
 
 void
@@ -2416,7 +2786,7 @@ void
 session_destroy_all(void (*closefunc)(Session *))
 {
        int i;
-       for (i = 0; i < MAX_SESSIONS; i++) {
+       for (i = 0; i < sessions_nalloc; i++) {
                Session *s = &sessions[i];
                if (s->used) {
                        if (closefunc != NULL)
@@ -2435,7 +2805,7 @@ session_tty_list(void)
        char *cp;
 
        buf[0] = '\0';
-       for (i = 0; i < MAX_SESSIONS; i++) {
+       for (i = 0; i < sessions_nalloc; i++) {
                Session *s = &sessions[i];
                if (s->used && s->ttyfd != -1) {
 
This page took 0.30483 seconds and 4 git commands to generate.