[BACK]Return to session.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / ssh

Diff for /src/usr.bin/ssh/session.c between version 1.74.2.6 and 1.75

version 1.74.2.6, 2002/06/02 22:56:11 version 1.75, 2001/05/03 15:45:15
Line 9 
Line 9 
  * called by a name other than "ssh" or "Secure Shell".   * called by a name other than "ssh" or "Secure Shell".
  *   *
  * SSH2 support by Markus Friedl.   * SSH2 support by Markus Friedl.
  * Copyright (c) 2000, 2001 Markus Friedl.  All rights reserved.   * Copyright (c) 2000 Markus Friedl.  All rights reserved.
  *   *
  * Redistribution and use in source and binary forms, with or without   * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions   * modification, are permitted provided that the following conditions
Line 46 
Line 46 
 #include "uidswap.h"  #include "uidswap.h"
 #include "compat.h"  #include "compat.h"
 #include "channels.h"  #include "channels.h"
   #include "nchan.h"
 #include "bufaux.h"  #include "bufaux.h"
 #include "auth.h"  #include "auth.h"
 #include "auth-options.h"  #include "auth-options.h"
Line 56 
Line 57 
 #include "serverloop.h"  #include "serverloop.h"
 #include "canohost.h"  #include "canohost.h"
 #include "session.h"  #include "session.h"
 #include "monitor_wrap.h"  
   
   /* types */
   
   #define TTYSZ 64
   typedef struct Session Session;
   struct Session {
           int     used;
           int     self;
           struct  passwd *pw;
           pid_t   pid;
           /* tty */
           char    *term;
           int     ptyfd, ttyfd, ptymaster;
           int     row, col, xpixel, ypixel;
           char    tty[TTYSZ];
           /* X11 */
           char    *display;
           int     screen;
           char    *auth_proto;
           char    *auth_data;
           int     single_connection;
           /* proto 2 */
           int     chanid;
           int     is_subsystem;
   };
   
 /* func */  /* func */
   
 Session *session_new(void);  Session *session_new(void);
 void    session_set_fds(Session *, int, int, int);  void    session_set_fds(Session *s, int fdin, int fdout, int fderr);
 void    session_pty_cleanup(void *);  void    session_pty_cleanup(Session *s);
 void    session_proctitle(Session *);  void    session_proctitle(Session *s);
 int     session_setup_x11fwd(Session *);  void    do_exec_pty(Session *s, const char *command);
 void    do_exec_pty(Session *, const char *);  void    do_exec_no_pty(Session *s, const char *command);
 void    do_exec_no_pty(Session *, const char *);  void    do_login(Session *s, const char *command);
 void    do_exec(Session *, const char *);  void    do_child(Session *s, const char *command);
 void    do_login(Session *, const char *);  
 void    do_child(Session *, const char *);  
 void    do_motd(void);  void    do_motd(void);
 int     check_quietlogin(Session *, const char *);  int     check_quietlogin(Session *s, const char *command);
   
 static void do_authenticated1(Authctxt *);  void    do_authenticated1(Authctxt *authctxt);
 static void do_authenticated2(Authctxt *);  void    do_authenticated2(Authctxt *authctxt);
   
 static int session_pty_req(Session *);  
   
 /* import */  /* import */
 extern ServerOptions options;  extern ServerOptions options;
 extern char *__progname;  extern char *__progname;
Line 87 
Line 108 
 extern int startup_pipe;  extern int startup_pipe;
 extern void destroy_sensitive_data(void);  extern void destroy_sensitive_data(void);
   
   /* Local Xauthority file. */
   static char *xauthfile;
   
 /* original command from peer. */  /* original command from peer. */
 const char *original_command = NULL;  char *original_command = NULL;
   
 /* data */  /* data */
 #define MAX_SESSIONS 10  #define MAX_SESSIONS 10
 Session sessions[MAX_SESSIONS];  Session sessions[MAX_SESSIONS];
   
 #ifdef HAVE_LOGIN_CAP  #ifdef HAVE_LOGIN_CAP
 login_cap_t *lc;  static login_cap_t *lc;
 #endif  #endif
   
 void  void
Line 110 
Line 134 
                 close(startup_pipe);                  close(startup_pipe);
                 startup_pipe = -1;                  startup_pipe = -1;
         }          }
   #ifdef HAVE_LOGIN_CAP
           if ((lc = login_getclass(authctxt->pw->pw_class)) == NULL) {
                   error("unable to get login class");
                   return;
           }
   #ifdef BSD_AUTH
           if (auth_approval(NULL, lc, authctxt->pw->pw_name, "ssh") <= 0) {
                   packet_disconnect("Approval failure for %s",
                       authctxt->pw->pw_name);
           }
   #endif
   #endif
         /* setup the channel layer */          /* setup the channel layer */
         if (!no_port_forwarding_flag && options.allow_tcp_forwarding)          if (!no_port_forwarding_flag && options.allow_tcp_forwarding)
                 channel_permit_all_opens();                  channel_permit_all_opens();
Line 118 
Line 154 
                 do_authenticated2(authctxt);                  do_authenticated2(authctxt);
         else          else
                 do_authenticated1(authctxt);                  do_authenticated1(authctxt);
   }
   
         /* remove agent socket */  /*
         if (auth_get_socket_name())   * Remove local Xauthority file.
                 auth_sock_cleanup_proc(authctxt->pw);   */
 #ifdef KRB4  void
         if (options.kerberos_ticket_cleanup)  xauthfile_cleanup_proc(void *ignore)
                 krb4_cleanup_proc(authctxt);  {
 #endif          debug("xauthfile_cleanup_proc called");
 #ifdef KRB5  
         if (options.kerberos_ticket_cleanup)          if (xauthfile != NULL) {
                 krb5_cleanup_proc(authctxt);                  char *p;
 #endif                  unlink(xauthfile);
                   p = strrchr(xauthfile, '/');
                   if (p != NULL) {
                           *p = '\0';
                           rmdir(xauthfile);
                   }
                   xfree(xauthfile);
                   xauthfile = NULL;
           }
 }  }
   
 /*  /*
    * Function to perform cleanup if we get aborted abnormally (e.g., due to a
    * dropped connection).
    */
   void
   pty_cleanup_proc(void *session)
   {
           Session *s=session;
           if (s == NULL)
                   fatal("pty_cleanup_proc: no session");
           debug("pty_cleanup_proc: %s", s->tty);
   
           if (s->pid != 0) {
                   /* Record that the user has logged out. */
                   record_logout(s->pid, s->tty);
           }
   
           /* Release the pseudo-tty. */
           pty_release(s->tty);
   }
   
   /*
  * Prepares for an interactive session.  This is called after the user has   * Prepares for an interactive session.  This is called after the user has
  * been successfully authenticated.  During this message exchange, pseudo   * been successfully authenticated.  During this message exchange, pseudo
  * terminals are allocated, X11, TCP/IP, and authentication agent forwardings   * terminals are allocated, X11, TCP/IP, and authentication agent forwardings
  * are requested, etc.   * are requested, etc.
  */   */
 static void  void
 do_authenticated1(Authctxt *authctxt)  do_authenticated1(Authctxt *authctxt)
 {  {
         Session *s;          Session *s;
         char *command;          char *command;
         int success, type, screen_flag;          int success, type, fd, n_bytes, plen, screen_flag, have_pty = 0;
         int compression_level = 0, enable_compression_after_reply = 0;          int compression_level = 0, enable_compression_after_reply = 0;
         u_int proto_len, data_len, dlen;          u_int proto_len, data_len, dlen;
   
         s = session_new();          s = session_new();
         s->authctxt = authctxt;  
         s->pw = authctxt->pw;          s->pw = authctxt->pw;
   
         /*          /*
Line 159 
Line 224 
                 success = 0;                  success = 0;
   
                 /* Get a packet from the client. */                  /* Get a packet from the client. */
                 type = packet_read();                  type = packet_read(&plen);
   
                 /* Process the packet. */                  /* Process the packet. */
                 switch (type) {                  switch (type) {
                 case SSH_CMSG_REQUEST_COMPRESSION:                  case SSH_CMSG_REQUEST_COMPRESSION:
                           packet_integrity_check(plen, 4, type);
                         compression_level = packet_get_int();                          compression_level = packet_get_int();
                         packet_check_eom();  
                         if (compression_level < 1 || compression_level > 9) {                          if (compression_level < 1 || compression_level > 9) {
                                 packet_send_debug("Received illegal compression level %d.",                                  packet_send_debug("Received illegal compression level %d.",
                                     compression_level);                                       compression_level);
                                 break;                                  break;
                         }                          }
                         /* Enable compression after we have responded with SUCCESS. */                          /* Enable compression after we have responded with SUCCESS. */
Line 177 
Line 242 
                         break;                          break;
   
                 case SSH_CMSG_REQUEST_PTY:                  case SSH_CMSG_REQUEST_PTY:
                         success = session_pty_req(s);                          if (no_pty_flag) {
                                   debug("Allocating a pty not permitted for this authentication.");
                                   break;
                           }
                           if (have_pty)
                                   packet_disconnect("Protocol error: you already have a pty.");
   
                           debug("Allocating pty.");
   
                           /* Allocate a pty and open it. */
                           if (!pty_allocate(&s->ptyfd, &s->ttyfd, s->tty,
                               sizeof(s->tty))) {
                                   error("Failed to allocate pty.");
                                   break;
                           }
                           fatal_add_cleanup(pty_cleanup_proc, (void *)s);
                           pty_setowner(s->pw, s->tty);
   
                           /* Get TERM from the packet.  Note that the value may be of arbitrary length. */
                           s->term = packet_get_string(&dlen);
                           packet_integrity_check(dlen, strlen(s->term), type);
                           /* packet_integrity_check(plen, 4 + dlen + 4*4 + n_bytes, type); */
                           /* Remaining bytes */
                           n_bytes = plen - (4 + dlen + 4 * 4);
   
                           if (strcmp(s->term, "") == 0) {
                                   xfree(s->term);
                                   s->term = NULL;
                           }
                           /* Get window size from the packet. */
                           s->row = packet_get_int();
                           s->col = packet_get_int();
                           s->xpixel = packet_get_int();
                           s->ypixel = packet_get_int();
                           pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel);
   
                           /* Get tty modes from the packet. */
                           tty_parse_modes(s->ttyfd, &n_bytes);
                           packet_integrity_check(plen, 4 + dlen + 4 * 4 + n_bytes, type);
   
                           session_proctitle(s);
   
                           /* Indicate that we now have a pty. */
                           success = 1;
                           have_pty = 1;
                         break;                          break;
   
                 case SSH_CMSG_X11_REQUEST_FORWARDING:                  case SSH_CMSG_X11_REQUEST_FORWARDING:
                           if (!options.x11_forwarding) {
                                   packet_send_debug("X11 forwarding disabled in server configuration file.");
                                   break;
                           }
                           if (!options.xauth_location) {
                                   packet_send_debug("No xauth program; cannot forward with spoofing.");
                                   break;
                           }
                           if (no_x11_forwarding_flag) {
                                   packet_send_debug("X11 forwarding not permitted for this authentication.");
                                   break;
                           }
                           debug("Received request for X11 forwarding with auth spoofing.");
                           if (s->display != NULL)
                                   packet_disconnect("Protocol error: X11 display already set.");
   
                         s->auth_proto = packet_get_string(&proto_len);                          s->auth_proto = packet_get_string(&proto_len);
                         s->auth_data = packet_get_string(&data_len);                          s->auth_data = packet_get_string(&data_len);
   
Line 192 
Line 317 
                                 if (!screen_flag)                                  if (!screen_flag)
                                         debug2("Buggy client: "                                          debug2("Buggy client: "
                                             "X11 screen flag missing");                                              "X11 screen flag missing");
                                   packet_integrity_check(plen,
                                       4 + proto_len + 4 + data_len + 4, type);
                                 s->screen = packet_get_int();                                  s->screen = packet_get_int();
                         } else {                          } else {
                                   packet_integrity_check(plen,
                                       4 + proto_len + 4 + data_len, type);
                                 s->screen = 0;                                  s->screen = 0;
                         }                          }
                         packet_check_eom();                          s->display = x11_create_display_inet(s->screen, options.x11_display_offset);
                         success = session_setup_x11fwd(s);  
                         if (!success) {                          if (s->display == NULL)
                                 xfree(s->auth_proto);                                  break;
                                 xfree(s->auth_data);  
                                 s->auth_proto = NULL;                          /* Setup to always have a local .Xauthority. */
                                 s->auth_data = NULL;                          xauthfile = xmalloc(MAXPATHLEN);
                           strlcpy(xauthfile, "/tmp/ssh-XXXXXXXX", MAXPATHLEN);
                           temporarily_use_uid(s->pw);
                           if (mkdtemp(xauthfile) == NULL) {
                                   restore_uid();
                                   error("private X11 dir: mkdtemp %s failed: %s",
                                       xauthfile, strerror(errno));
                                   xfree(xauthfile);
                                   xauthfile = NULL;
                                   /* XXXX remove listening channels */
                                   break;
                         }                          }
                           strlcat(xauthfile, "/cookies", MAXPATHLEN);
                           fd = open(xauthfile, O_RDWR|O_CREAT|O_EXCL, 0600);
                           if (fd >= 0)
                                   close(fd);
                           restore_uid();
                           fatal_add_cleanup(xauthfile_cleanup_proc, NULL);
                           success = 1;
                         break;                          break;
   
                 case SSH_CMSG_AGENT_REQUEST_FORWARDING:                  case SSH_CMSG_AGENT_REQUEST_FORWARDING:
Line 234 
Line 380 
                                 success = 1;                                  success = 1;
                         break;                          break;
   
 #if defined(AFS) || defined(KRB5)  
                 case SSH_CMSG_HAVE_KERBEROS_TGT:  
                         if (!options.kerberos_tgt_passing) {  
                                 verbose("Kerberos TGT passing disabled.");  
                         } else {  
                                 char *kdata = packet_get_string(&dlen);  
                                 packet_check_eom();  
   
                                 /* XXX - 0x41, see creds_to_radix version */  
                                 if (kdata[0] != 0x41) {  
 #ifdef KRB5  
                                         krb5_data tgt;  
                                         tgt.data = kdata;  
                                         tgt.length = dlen;  
   
                                         if (auth_krb5_tgt(s->authctxt, &tgt))  
                                                 success = 1;  
                                         else  
                                                 verbose("Kerberos v5 TGT refused for %.100s", s->authctxt->user);  
 #endif /* KRB5 */  
                                 } else {  
 #ifdef AFS  
                                         if (auth_krb4_tgt(s->authctxt, kdata))  
                                                 success = 1;  
                                         else  
                                                 verbose("Kerberos v4 TGT refused for %.100s", s->authctxt->user);  
 #endif /* AFS */  
                                 }  
                                 xfree(kdata);  
                         }  
                         break;  
 #endif /* AFS || KRB5 */  
   
 #ifdef AFS  
                 case SSH_CMSG_HAVE_AFS_TOKEN:  
                         if (!options.afs_token_passing || !k_hasafs()) {  
                                 verbose("AFS token passing disabled.");  
                         } else {  
                                 /* Accept AFS token. */  
                                 char *token = packet_get_string(&dlen);  
                                 packet_check_eom();  
   
                                 if (auth_afs_token(s->authctxt, token))  
                                         success = 1;  
                                 else  
                                         verbose("AFS token refused for %.100s",  
                                             s->authctxt->user);  
                                 xfree(token);  
                         }  
                         break;  
 #endif /* AFS */  
   
                 case SSH_CMSG_EXEC_SHELL:                  case SSH_CMSG_EXEC_SHELL:
                 case SSH_CMSG_EXEC_CMD:                  case SSH_CMSG_EXEC_CMD:
                         if (type == SSH_CMSG_EXEC_CMD) {                          if (type == SSH_CMSG_EXEC_CMD) {
                                 command = packet_get_string(&dlen);                                  command = packet_get_string(&dlen);
                                 debug("Exec command '%.500s'", command);                                  debug("Exec command '%.500s'", command);
                                 do_exec(s, command);                                  packet_integrity_check(plen, 4 + dlen, type);
                                 xfree(command);  
                         } else {                          } else {
                                 do_exec(s, NULL);                                  command = NULL;
                                   packet_integrity_check(plen, 0, type);
                         }                          }
                         packet_check_eom();                          if (forced_command != NULL) {
                         session_close(s);                                  original_command = command;
                                   command = forced_command;
                                   debug("Forced command '%.500s'", forced_command);
                           }
                           if (have_pty)
                                   do_exec_pty(s, command);
                           else
                                   do_exec_no_pty(s, command);
   
                           if (command != NULL)
                                   xfree(command);
                           /* Cleanup user's local Xauthority file. */
                           if (xauthfile)
                                   xauthfile_cleanup_proc(NULL);
                         return;                          return;
   
                 default:                  default:
Line 456 
Line 563 
   
         /* Fork the child. */          /* Fork the child. */
         if ((pid = fork()) == 0) {          if ((pid = fork()) == 0) {
   
                 /* Child.  Reinitialize the log because the pid has changed. */                  /* 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 the master side of the pseudo tty. */
                 close(ptyfd);                  close(ptyfd);
   
                 /* Make the pseudo tty our controlling tty. */                  /* Make the pseudo tty our controlling tty. */
                 pty_make_controlling_tty(&ttyfd, s->tty);                  pty_make_controlling_tty(&ttyfd, s->tty);
   
                 /* Redirect stdin/stdout/stderr from the pseudo tty. */                  /* Redirect stdin from the pseudo tty. */
                 if (dup2(ttyfd, 0) < 0)                  if (dup2(ttyfd, fileno(stdin)) < 0)
                         error("dup2 stdin: %s", strerror(errno));                          error("dup2 stdin failed: %.100s", strerror(errno));
                 if (dup2(ttyfd, 1) < 0)  
                         error("dup2 stdout: %s", strerror(errno));  
                 if (dup2(ttyfd, 2) < 0)  
                         error("dup2 stderr: %s", strerror(errno));  
   
                   /* Redirect stdout to the pseudo tty. */
                   if (dup2(ttyfd, fileno(stdout)) < 0)
                           error("dup2 stdin failed: %.100s", strerror(errno));
   
                   /* Redirect stderr to the pseudo tty. */
                   if (dup2(ttyfd, fileno(stderr)) < 0)
                           error("dup2 stdin failed: %.100s", strerror(errno));
   
                 /* Close the extra descriptor for the pseudo tty. */                  /* Close the extra descriptor for the pseudo tty. */
                 close(ttyfd);                  close(ttyfd);
   
Line 513 
Line 624 
         } else {          } else {
                 server_loop(pid, ptyfd, fdout, -1);                  server_loop(pid, ptyfd, fdout, -1);
                 /* server_loop _has_ closed ptyfd and fdout. */                  /* server_loop _has_ closed ptyfd and fdout. */
                   session_pty_cleanup(s);
         }          }
 }  }
   
 /*  
  * This is called to fork and execute a command.  If another command is  
  * to be forced, execute that instead.  
  */  
 void  
 do_exec(Session *s, const char *command)  
 {  
         if (forced_command) {  
                 original_command = command;  
                 command = forced_command;  
                 debug("Forced command '%.900s'", command);  
         }  
   
         if (s->ttyfd != -1)  
                 do_exec_pty(s, command);  
         else  
                 do_exec_no_pty(s, command);  
   
         original_command = NULL;  
 }  
   
   
 /* administrative, login(1)-like work */  /* administrative, login(1)-like work */
 void  void
 do_login(Session *s, const char *command)  do_login(Session *s, const char *command)
 {  {
         char *time_string;          char *time_string;
           char hostname[MAXHOSTNAMELEN];
         socklen_t fromlen;          socklen_t fromlen;
         struct sockaddr_storage from;          struct sockaddr_storage from;
           time_t last_login_time;
         struct passwd * pw = s->pw;          struct passwd * pw = s->pw;
         pid_t pid = getpid();          pid_t pid = getpid();
   
Line 556 
Line 648 
         if (packet_connection_is_on_socket()) {          if (packet_connection_is_on_socket()) {
                 fromlen = sizeof(from);                  fromlen = sizeof(from);
                 if (getpeername(packet_get_connection_in(),                  if (getpeername(packet_get_connection_in(),
                     (struct sockaddr *) & from, &fromlen) < 0) {                       (struct sockaddr *) & from, &fromlen) < 0) {
                         debug("getpeername: %.100s", strerror(errno));                          debug("getpeername: %.100s", strerror(errno));
                         fatal_cleanup();                          fatal_cleanup();
                 }                  }
         }          }
   
           /* Get the time and hostname when the user last logged in. */
           if (options.print_lastlog) {
                   hostname[0] = '\0';
                   last_login_time = get_last_login_time(pw->pw_uid, pw->pw_name,
                       hostname, sizeof(hostname));
           }
   
         /* Record that there was a login on that tty from the remote host. */          /* Record that there was a login on that tty from the remote host. */
         if (!use_privsep)          record_login(pid, s->tty, pw->pw_name, pw->pw_uid,
                 record_login(pid, s->tty, pw->pw_name, pw->pw_uid,              get_remote_name_or_ip(utmp_len, options.reverse_mapping_check),
                     get_remote_name_or_ip(utmp_len,              (struct sockaddr *)&from);
                     options.verify_reverse_mapping),  
                     (struct sockaddr *)&from);  
   
         if (check_quietlogin(s, command))          if (check_quietlogin(s, command))
                 return;                  return;
   
         if (options.print_lastlog && s->last_login_time != 0) {          if (options.print_lastlog && last_login_time != 0) {
                 time_string = ctime(&s->last_login_time);                  time_string = ctime(&last_login_time);
                 if (strchr(time_string, '\n'))                  if (strchr(time_string, '\n'))
                         *strchr(time_string, '\n') = 0;                          *strchr(time_string, '\n') = 0;
                 if (strcmp(s->hostname, "") == 0)                  if (strcmp(hostname, "") == 0)
                         printf("Last login: %s\r\n", time_string);                          printf("Last login: %s\r\n", time_string);
                 else                  else
                         printf("Last login: %s from %s\r\n", time_string,                          printf("Last login: %s from %s\r\n", time_string, hostname);
                             s->hostname);  
         }          }
   
         do_motd();          do_motd();
Line 618 
Line 714 
 check_quietlogin(Session *s, const char *command)  check_quietlogin(Session *s, const char *command)
 {  {
         char buf[256];          char buf[256];
         struct passwd *pw = s->pw;          struct passwd * pw = s->pw;
         struct stat st;          struct stat st;
   
         /* Return 1 if .hushlogin exists or a command given. */          /* Return 1 if .hushlogin exists or a command given. */
Line 639 
Line 735 
  * Sets the value of the given variable in the environment.  If the variable   * Sets the value of the given variable in the environment.  If the variable
  * already exists, its value is overriden.   * already exists, its value is overriden.
  */   */
 static void  void
 child_set_env(char ***envp, u_int *envsizep, const char *name,  child_set_env(char ***envp, u_int *envsizep, const char *name,
         const char *value)                const char *value)
 {  {
         u_int i, namelen;          u_int i, namelen;
         char **env;          char **env;
Line 680 
Line 776 
  * Otherwise, it must consist of empty lines, comments (line starts with '#')   * Otherwise, it must consist of empty lines, comments (line starts with '#')
  * and assignments of the form name=value.  No other forms are allowed.   * and assignments of the form name=value.  No other forms are allowed.
  */   */
 static void  void
 read_environment_file(char ***env, u_int *envsize,  read_environment_file(char ***env, u_int *envsize,
         const char *filename)                        const char *filename)
 {  {
         FILE *f;          FILE *f;
         char buf[4096];          char buf[4096];
Line 715 
Line 811 
         fclose(f);          fclose(f);
 }  }
   
 static char **  /*
 do_setup_env(Session *s, const char *shell)   * Performs common processing for the child, such as setting up the
    * environment, closing extra file descriptors, setting the user and group
    * ids, and executing the command or shell.
    */
   void
   do_child(Session *s, const char *command)
 {  {
           const char *shell, *hostname = NULL, *cp = NULL;
           struct passwd * pw = s->pw;
         char buf[256];          char buf[256];
         u_int i, envsize;          char cmd[1024];
           FILE *f = NULL;
           u_int envsize, i;
         char **env;          char **env;
         struct passwd *pw = s->pw;          extern char **environ;
           struct stat st;
           char *argv[10];
           int do_xauth = s->auth_proto != NULL && s->auth_data != NULL;
   
           /* remove hostkey from the child's memory */
           destroy_sensitive_data();
   
           /* login(1) is only called if we execute the login shell */
           if (options.use_login && command != NULL)
                   options.use_login = 0;
   
           if (!options.use_login) {
   #ifdef HAVE_LOGIN_CAP
                   if (!login_getcapbool(lc, "ignorenologin", 0) && pw->pw_uid)
                           f = fopen(login_getcapstr(lc, "nologin", _PATH_NOLOGIN,
                               _PATH_NOLOGIN), "r");
   #else
                   if (pw->pw_uid)
                           f = fopen(_PATH_NOLOGIN, "r");
   #endif
                   if (f) {
                           /* /etc/nologin exists.  Print its contents and exit. */
                           while (fgets(buf, sizeof(buf), f))
                                   fputs(buf, stderr);
                           fclose(f);
                           exit(254);
                   }
           }
           /* Set login name, uid, gid, and groups. */
           /* Login(1) does this as well, and it needs uid 0 for the "-h"
              switch, so we let login(1) to this for us. */
           if (!options.use_login) {
                   if (getuid() == 0 || geteuid() == 0) {
   #ifdef HAVE_LOGIN_CAP
                           if (setusercontext(lc, pw, pw->pw_uid,
                               (LOGIN_SETALL & ~LOGIN_SETPATH)) < 0) {
                                   perror("unable to set user context");
                                   exit(1);
                           }
   #else
                           if (setlogin(pw->pw_name) < 0)
                                   error("setlogin failed: %s", strerror(errno));
                           if (setgid(pw->pw_gid) < 0) {
                                   perror("setgid");
                                   exit(1);
                           }
                           /* Initialize the group list. */
                           if (initgroups(pw->pw_name, pw->pw_gid) < 0) {
                                   perror("initgroups");
                                   exit(1);
                           }
                           endgrent();
   
                           /* Permanently switch to the desired uid. */
                           permanently_set_uid(pw);
   #endif
                   }
                   if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid)
                           fatal("Failed to set uids to %u.", (u_int) pw->pw_uid);
           }
           /*
            * Get the shell from the password data.  An empty shell field is
            * legal, and means /bin/sh.
            */
           shell = (pw->pw_shell[0] == '\0') ? _PATH_BSHELL : pw->pw_shell;
   #ifdef HAVE_LOGIN_CAP
           shell = login_getcapstr(lc, "shell", (char *)shell, (char *)shell);
   #endif
   
   #ifdef AFS
           /* Try to get AFS tokens for the local cell. */
           if (k_hasafs()) {
                   char cell[64];
   
                   if (k_afs_cell_of_file(pw->pw_dir, cell, sizeof(cell)) == 0)
                           krb_afslog(cell, 0);
   
                   krb_afslog(0, 0);
           }
   #endif /* AFS */
   
         /* Initialize the environment. */          /* Initialize the environment. */
         envsize = 100;          envsize = 100;
         env = xmalloc(envsize * sizeof(char *));          env = xmalloc(envsize * sizeof(char *));
Line 751 
Line 936 
                 child_set_env(&env, &envsize, "TZ", getenv("TZ"));                  child_set_env(&env, &envsize, "TZ", getenv("TZ"));
   
         /* Set custom environment options from RSA authentication. */          /* Set custom environment options from RSA authentication. */
         if (!options.use_login) {          while (custom_environment) {
                 while (custom_environment) {                  struct envstring *ce = custom_environment;
                         struct envstring *ce = custom_environment;                  char *s = ce->s;
                         char *s = ce->s;                  int i;
                   for (i = 0; s[i] != '=' && s[i]; i++);
                         for (i = 0; s[i] != '=' && s[i]; i++)                  if (s[i] == '=') {
                                 ;                          s[i] = 0;
                         if (s[i] == '=') {                          child_set_env(&env, &envsize, s, s + i + 1);
                                 s[i] = 0;  
                                 child_set_env(&env, &envsize, s, s + i + 1);  
                         }  
                         custom_environment = ce->next;  
                         xfree(ce->s);  
                         xfree(ce);  
                 }                  }
                   custom_environment = ce->next;
                   xfree(ce->s);
                   xfree(ce);
         }          }
   
         snprintf(buf, sizeof buf, "%.50s %d %d",          snprintf(buf, sizeof buf, "%.50s %d %d",
             get_remote_ipaddr(), get_remote_port(), get_local_port());                   get_remote_ipaddr(), get_remote_port(), get_local_port());
         child_set_env(&env, &envsize, "SSH_CLIENT", buf);          child_set_env(&env, &envsize, "SSH_CLIENT", buf);
   
         if (s->ttyfd != -1)          if (s->ttyfd != -1)
Line 781 
Line 963 
         if (original_command)          if (original_command)
                 child_set_env(&env, &envsize, "SSH_ORIGINAL_COMMAND",                  child_set_env(&env, &envsize, "SSH_ORIGINAL_COMMAND",
                     original_command);                      original_command);
   
 #ifdef KRB4  #ifdef KRB4
         if (s->authctxt->krb4_ticket_file)          {
                 child_set_env(&env, &envsize, "KRBTKFILE",                  extern char *ticket;
                     s->authctxt->krb4_ticket_file);  
 #endif                  if (ticket)
 #ifdef KRB5                          child_set_env(&env, &envsize, "KRBTKFILE", ticket);
         if (s->authctxt->krb5_ticket_file)          }
                 child_set_env(&env, &envsize, "KRB5CCNAME",  #endif /* KRB4 */
                     s->authctxt->krb5_ticket_file);  
 #endif          if (xauthfile)
                   child_set_env(&env, &envsize, "XAUTHORITY", xauthfile);
         if (auth_get_socket_name() != NULL)          if (auth_get_socket_name() != NULL)
                 child_set_env(&env, &envsize, SSH_AUTHSOCKET_ENV_NAME,                  child_set_env(&env, &envsize, SSH_AUTHSOCKET_ENV_NAME,
                     auth_get_socket_name());                                auth_get_socket_name());
   
         /* read $HOME/.ssh/environment. */          /* read $HOME/.ssh/environment. */
         if (!options.use_login) {          if (!options.use_login) {
Line 807 
Line 991 
                 for (i = 0; env[i]; i++)                  for (i = 0; env[i]; i++)
                         fprintf(stderr, "  %.200s\n", env[i]);                          fprintf(stderr, "  %.200s\n", env[i]);
         }          }
         return env;  
 }  
   
 /*  
  * Run $HOME/.ssh/rc, /etc/sshrc, or xauth (whichever is found  
  * first in this order).  
  */  
 static void  
 do_rc_files(Session *s, const char *shell)  
 {  
         FILE *f = NULL;  
         char cmd[1024];  
         int do_xauth;  
         struct stat st;  
   
         do_xauth =  
             s->display != NULL && s->auth_proto != NULL && s->auth_data != NULL;  
   
         /* ignore _PATH_SSH_USER_RC for subsystems */  
         if (!s->is_subsystem && (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)  
                         fprintf(stderr, "Running %s\n", cmd);  
                 f = popen(cmd, "w");  
                 if (f) {  
                         if (do_xauth)  
                                 fprintf(f, "%s %s\n", s->auth_proto,  
                                     s->auth_data);  
                         pclose(f);  
                 } else  
                         fprintf(stderr, "Could not run %s\n",  
                             _PATH_SSH_USER_RC);  
         } else if (stat(_PATH_SSH_SYSTEM_RC, &st) >= 0) {  
                 if (debug_flag)  
                         fprintf(stderr, "Running %s %s\n", _PATH_BSHELL,  
                             _PATH_SSH_SYSTEM_RC);  
                 f = popen(_PATH_BSHELL " " _PATH_SSH_SYSTEM_RC, "w");  
                 if (f) {  
                         if (do_xauth)  
                                 fprintf(f, "%s %s\n", s->auth_proto,  
                                     s->auth_data);  
                         pclose(f);  
                 } else  
                         fprintf(stderr, "Could not run %s\n",  
                             _PATH_SSH_SYSTEM_RC);  
         } else if (do_xauth && options.xauth_location != NULL) {  
                 /* Add authority data to .Xauthority if appropriate. */  
                 if (debug_flag) {  
                         fprintf(stderr,  
                             "Running %.100s add "  
                             "%.100s %.100s %.100s\n",  
                             options.xauth_location, s->auth_display,  
                             s->auth_proto, s->auth_data);  
                 }  
                 snprintf(cmd, sizeof cmd, "%s -q -",  
                     options.xauth_location);  
                 f = popen(cmd, "w");  
                 if (f) {  
                         fprintf(f, "add %s %s %s\n",  
                             s->auth_display, s->auth_proto,  
                             s->auth_data);  
                         pclose(f);  
                 } else {  
                         fprintf(stderr, "Could not run %s\n",  
                             cmd);  
                 }  
         }  
 }  
   
 static void  
 do_nologin(struct passwd *pw)  
 {  
         FILE *f = NULL;  
         char buf[1024];  
   
 #ifdef HAVE_LOGIN_CAP  
         if (!login_getcapbool(lc, "ignorenologin", 0) && pw->pw_uid)  
                 f = fopen(login_getcapstr(lc, "nologin", _PATH_NOLOGIN,  
                     _PATH_NOLOGIN), "r");  
 #else  
         if (pw->pw_uid)  
                 f = fopen(_PATH_NOLOGIN, "r");  
 #endif  
         if (f) {  
                 /* /etc/nologin exists.  Print its contents and exit. */  
                 while (fgets(buf, sizeof(buf), f))  
                         fputs(buf, stderr);  
                 fclose(f);  
                 exit(254);  
         }  
 }  
   
 /* Set login name, uid, gid, and groups. */  
 void  
 do_setusercontext(struct passwd *pw)  
 {  
         if (getuid() == 0 || geteuid() == 0) {  
 #ifdef HAVE_LOGIN_CAP  
                 if (setusercontext(lc, pw, pw->pw_uid,  
                     (LOGIN_SETALL & ~LOGIN_SETPATH)) < 0) {  
                         perror("unable to set user context");  
                         exit(1);  
                 }  
 #else  
                 if (setlogin(pw->pw_name) < 0)  
                         error("setlogin failed: %s", strerror(errno));  
                 if (setgid(pw->pw_gid) < 0) {  
                         perror("setgid");  
                         exit(1);  
                 }  
                 /* Initialize the group list. */  
                 if (initgroups(pw->pw_name, pw->pw_gid) < 0) {  
                         perror("initgroups");  
                         exit(1);  
                 }  
                 endgrent();  
   
                 /* Permanently switch to the desired uid. */  
                 permanently_set_uid(pw);  
 #endif  
         }  
         if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid)  
                 fatal("Failed to set uids to %u.", (u_int) pw->pw_uid);  
 }  
   
 static void  
 launch_login(struct passwd *pw, const char *hostname)  
 {  
         /* Launch login(1). */  
   
         execl("/usr/bin/login", "login", "-h", hostname,  
             "-p", "-f", "--", pw->pw_name, (char *)NULL);  
   
         /* Login couldn't be executed, die. */  
   
         perror("login");  
         exit(1);  
 }  
   
 /*  
  * Performs common processing for the child, such as setting up the  
  * environment, closing extra file descriptors, setting the user and group  
  * ids, and executing the command or shell.  
  */  
 void  
 do_child(Session *s, const char *command)  
 {  
         extern char **environ;  
         char **env;  
         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();  
   
         /* login(1) is only called if we execute the login shell */  
         if (options.use_login && command != NULL)  
                 options.use_login = 0;  
   
         /*  
          * Login(1) does this as well, and it needs uid 0 for the "-h"  
          * switch, so we let login(1) to this for us.  
          */  
         if (!options.use_login) {  
                 do_nologin(pw);  
                 do_setusercontext(pw);  
         }  
   
         /*  
          * Get the shell from the password data.  An empty shell field is  
          * legal, and means /bin/sh.  
          */  
         shell = (pw->pw_shell[0] == '\0') ? _PATH_BSHELL : pw->pw_shell;  
 #ifdef HAVE_LOGIN_CAP  
         shell = login_getcapstr(lc, "shell", (char *)shell, (char *)shell);  
 #endif  
   
         env = do_setup_env(s, shell);  
   
         /* we have to stash the hostname before we close our socket. */          /* we have to stash the hostname before we close our socket. */
         if (options.use_login)          if (options.use_login)
                 hostname = get_remote_name_or_ip(utmp_len,                  hostname = get_remote_name_or_ip(utmp_len,
                     options.verify_reverse_mapping);                      options.reverse_mapping_check);
         /*          /*
          * Close the connection descriptors; note that this is the child, and           * Close the connection descriptors; note that this is the child, and
          * the server will still have the socket open, and it is important           * the server will still have the socket open, and it is important
Line 1028 
Line 1030 
         for (i = 3; i < 64; i++)          for (i = 3; i < 64; i++)
                 close(i);                  close(i);
   
         /*  
          * Must take new environment into use so that .ssh/rc,  
          * /etc/sshrc and xauth are run in the proper environment.  
          */  
         environ = env;  
   
 #ifdef AFS  
         /* Try to get AFS tokens for the local cell. */  
         if (k_hasafs()) {  
                 char cell[64];  
   
                 if (k_afs_cell_of_file(pw->pw_dir, cell, sizeof(cell)) == 0)  
                         krb_afslog(cell, 0);  
   
                 krb_afslog(0, 0);  
         }  
 #endif /* AFS */  
   
         /* Change current directory to the user\'s home directory. */          /* Change current directory to the user\'s home directory. */
         if (chdir(pw->pw_dir) < 0) {          if (chdir(pw->pw_dir) < 0) {
                 fprintf(stderr, "Could not chdir to home directory %s: %s\n",                  fprintf(stderr, "Could not chdir to home directory %s: %s\n",
                     pw->pw_dir, strerror(errno));                          pw->pw_dir, strerror(errno));
 #ifdef HAVE_LOGIN_CAP  #ifdef HAVE_LOGIN_CAP
                 if (login_getcapbool(lc, "requirehome", 0))                  if (login_getcapbool(lc, "requirehome", 0))
                         exit(1);                          exit(1);
 #endif  #endif
         }          }
   
         if (!options.use_login)          /*
                 do_rc_files(s, shell);           * Must take new environment into use so that .ssh/rc, /etc/sshrc and
            * xauth are run in the proper environment.
            */
           environ = env;
   
         /* restore SIGPIPE for child */          /*
         signal(SIGPIPE,  SIG_DFL);           * Run $HOME/.ssh/rc, /etc/sshrc, or xauth (whichever is found first
            * in this order).
            */
           if (!options.use_login) {
                   /* ignore _PATH_SSH_USER_RC for subsystems */
                   if (!s->is_subsystem && (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)
                                   fprintf(stderr, "Running %s\n", cmd);
                           f = popen(cmd, "w");
                           if (f) {
                                   if (do_xauth)
                                           fprintf(f, "%s %s\n", s->auth_proto,
                                               s->auth_data);
                                   pclose(f);
                           } else
                                   fprintf(stderr, "Could not run %s\n",
                                       _PATH_SSH_USER_RC);
                   } else if (stat(_PATH_SSH_SYSTEM_RC, &st) >= 0) {
                           if (debug_flag)
                                   fprintf(stderr, "Running %s %s\n", _PATH_BSHELL,
                                       _PATH_SSH_SYSTEM_RC);
                           f = popen(_PATH_BSHELL " " _PATH_SSH_SYSTEM_RC, "w");
                           if (f) {
                                   if (do_xauth)
                                           fprintf(f, "%s %s\n", s->auth_proto,
                                               s->auth_data);
                                   pclose(f);
                           } else
                                   fprintf(stderr, "Could not run %s\n",
                                       _PATH_SSH_SYSTEM_RC);
                   } else if (do_xauth && options.xauth_location != NULL) {
                           /* Add authority data to .Xauthority if appropriate. */
                           char *screen = strchr(s->display, ':');
   
         if (options.use_login) {                          if (debug_flag) {
                 launch_login(pw, hostname);                                  fprintf(stderr,
                 /* NEVERREACHED */                                      "Running %.100s add "
                                       "%.100s %.100s %.100s\n",
                                       options.xauth_location, s->display,
                                       s->auth_proto, s->auth_data);
                                   if (screen != NULL)
                                           fprintf(stderr,
                                               "Adding %.*s/unix%s %s %s\n",
                                               (int)(screen - s->display),
                                               s->display, screen,
                                               s->auth_proto, s->auth_data);
                           }
                           snprintf(cmd, sizeof cmd, "%s -q -",
                               options.xauth_location);
                           f = popen(cmd, "w");
                           if (f) {
                                   fprintf(f, "add %s %s %s\n", s->display,
                                       s->auth_proto, s->auth_data);
                                   if (screen != NULL)
                                           fprintf(f, "add %.*s/unix%s %s %s\n",
                                               (int)(screen - s->display),
                                               s->display, screen,
                                               s->auth_proto,
                                               s->auth_data);
                                   pclose(f);
                           } else {
                                   fprintf(stderr, "Could not run %s\n",
                                       cmd);
                           }
                   }
                   /* Get the last component of the shell name. */
                   cp = strrchr(shell, '/');
                   if (cp)
                           cp++;
                   else
                           cp = shell;
         }          }
   
         /* Get the last component of the shell name. */          /* restore SIGPIPE for child */
         if ((shell0 = strrchr(shell, '/')) != NULL)          signal(SIGPIPE,  SIG_DFL);
                 shell0++;  
         else  
                 shell0 = shell;  
   
         /*          /*
          * If we have no command, execute the shell.  In this case, the shell           * If we have no command, execute the shell.  In this case, the shell
Line 1079 
Line 1131 
          * this is a login shell.           * this is a login shell.
          */           */
         if (!command) {          if (!command) {
                 char argv0[256];                  if (!options.use_login) {
                           char buf[256];
   
                 /* Start the shell.  Set initial character to '-'. */                          /*
                 argv0[0] = '-';                           * Check for mail if we have a tty and it was enabled
                            * in server options.
                            */
                           if (s->ttyfd != -1 && options.check_mail) {
                                   char *mailbox;
                                   struct stat mailstat;
   
                 if (strlcpy(argv0 + 1, shell0, sizeof(argv0) - 1)                                  mailbox = getenv("MAIL");
                     >= sizeof(argv0) - 1) {                                  if (mailbox != NULL) {
                         errno = EINVAL;                                          if (stat(mailbox, &mailstat) != 0 ||
                                               mailstat.st_size == 0)
                                                   printf("No mail.\n");
                                           else if (mailstat.st_mtime < mailstat.st_atime)
                                                   printf("You have mail.\n");
                                           else
                                                   printf("You have new mail.\n");
                                   }
                           }
                           /* Start the shell.  Set initial character to '-'. */
                           buf[0] = '-';
                           strncpy(buf + 1, cp, sizeof(buf) - 1);
                           buf[sizeof(buf) - 1] = 0;
   
                           /* Execute the shell. */
                           argv[0] = buf;
                           argv[1] = NULL;
                           execve(shell, argv, env);
   
                           /* Executing the shell failed. */
                         perror(shell);                          perror(shell);
                         exit(1);                          exit(1);
                 }  
   
                 /* Execute the shell. */                  } else {
                 argv[0] = argv0;                          /* Launch login(1). */
                 argv[1] = NULL;  
                 execve(shell, argv, env);  
   
                 /* Executing the shell failed. */                          execl("/usr/bin/login", "login", "-h", hostname,
                 perror(shell);                               "-p", "-f", "--", pw->pw_name, NULL);
                 exit(1);  
                           /* Login couldn't be executed, die. */
   
                           perror("login");
                           exit(1);
                   }
         }          }
         /*          /*
          * Execute the command using the user's shell.  This uses the -c           * Execute the command using the user's shell.  This uses the -c
          * option to execute the command.           * option to execute the command.
          */           */
         argv[0] = (char *) shell0;          argv[0] = (char *) cp;
         argv[1] = "-c";          argv[1] = "-c";
         argv[2] = (char *) command;          argv[2] = (char *) command;
         argv[3] = NULL;          argv[3] = NULL;
Line 1120 
Line 1199 
         static int did_init = 0;          static int did_init = 0;
         if (!did_init) {          if (!did_init) {
                 debug("session_new: init");                  debug("session_new: init");
                 for (i = 0; i < MAX_SESSIONS; i++) {                  for(i = 0; i < MAX_SESSIONS; i++) {
                         sessions[i].used = 0;                          sessions[i].used = 0;
                           sessions[i].self = i;
                 }                  }
                 did_init = 1;                  did_init = 1;
         }          }
         for (i = 0; i < MAX_SESSIONS; i++) {          for(i = 0; i < MAX_SESSIONS; i++) {
                 Session *s = &sessions[i];                  Session *s = &sessions[i];
                 if (! s->used) {                  if (! s->used) {
                         memset(s, 0, sizeof(*s));                          memset(s, 0, sizeof(*s));
Line 1133 
Line 1213 
                         s->ptyfd = -1;                          s->ptyfd = -1;
                         s->ttyfd = -1;                          s->ttyfd = -1;
                         s->used = 1;                          s->used = 1;
                         s->self = i;  
                         debug("session_new: session %d", i);                          debug("session_new: session %d", i);
                         return s;                          return s;
                 }                  }
Line 1141 
Line 1220 
         return NULL;          return NULL;
 }  }
   
 static void  void
 session_dump(void)  session_dump(void)
 {  {
         int i;          int i;
         for (i = 0; i < MAX_SESSIONS; i++) {          for(i = 0; i < MAX_SESSIONS; i++) {
                 Session *s = &sessions[i];                  Session *s = &sessions[i];
                 debug("dump: used %d session %d %p channel %d pid %d",                  debug("dump: used %d session %d %p channel %d pid %d",
                     s->used,                      s->used,
Line 1157 
Line 1236 
 }  }
   
 int  int
 session_open(Authctxt *authctxt, int chanid)  session_open(int chanid)
 {  {
         Session *s = session_new();          Session *s = session_new();
         debug("session_open: channel %d", chanid);          debug("session_open: channel %d", chanid);
Line 1165 
Line 1244 
                 error("no more sessions");                  error("no more sessions");
                 return 0;                  return 0;
         }          }
         s->authctxt = authctxt;          s->pw = auth_get_user();
         s->pw = authctxt->pw;  
         if (s->pw == NULL)          if (s->pw == NULL)
                 fatal("no user for session %d", s->self);                  fatal("no user for session %d", s->self);
         debug("session_open: session %d: link with channel %d", s->self, chanid);          debug("session_open: session %d: link with channel %d", s->self, chanid);
Line 1175 
Line 1253 
 }  }
   
 Session *  Session *
 session_by_tty(char *tty)  
 {  
         int i;  
         for (i = 0; i < MAX_SESSIONS; 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);  
                         return s;  
                 }  
         }  
         debug("session_by_tty: unknown tty %.100s", tty);  
         session_dump();  
         return NULL;  
 }  
   
 static Session *  
 session_by_channel(int id)  session_by_channel(int id)
 {  {
         int i;          int i;
         for (i = 0; i < MAX_SESSIONS; i++) {          for(i = 0; i < MAX_SESSIONS; i++) {
                 Session *s = &sessions[i];                  Session *s = &sessions[i];
                 if (s->used && s->chanid == id) {                  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);
Line 1206 
Line 1268 
         return NULL;          return NULL;
 }  }
   
 static Session *  Session *
 session_by_pid(pid_t pid)  session_by_pid(pid_t pid)
 {  {
         int i;          int i;
         debug("session_by_pid: pid %d", pid);          debug("session_by_pid: pid %d", pid);
         for (i = 0; i < MAX_SESSIONS; i++) {          for(i = 0; i < MAX_SESSIONS; i++) {
                 Session *s = &sessions[i];                  Session *s = &sessions[i];
                 if (s->used && s->pid == pid)                  if (s->used && s->pid == pid)
                         return s;                          return s;
Line 1221 
Line 1283 
         return NULL;          return NULL;
 }  }
   
 static int  int
 session_window_change_req(Session *s)  session_window_change_req(Session *s)
 {  {
         s->col = packet_get_int();          s->col = packet_get_int();
         s->row = packet_get_int();          s->row = packet_get_int();
         s->xpixel = packet_get_int();          s->xpixel = packet_get_int();
         s->ypixel = packet_get_int();          s->ypixel = packet_get_int();
         packet_check_eom();          packet_done();
         pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel);          pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel);
         return 1;          return 1;
 }  }
   
 static int  int
 session_pty_req(Session *s)  session_pty_req(Session *s)
 {  {
         u_int len;          u_int len;
         int n_bytes;          int n_bytes;
   
         if (no_pty_flag) {          if (no_pty_flag)
                 debug("Allocating a pty not permitted for this authentication.");  
                 return 0;                  return 0;
         }          if (s->ttyfd != -1)
         if (s->ttyfd != -1) {  
                 packet_disconnect("Protocol error: you already have a pty.");  
                 return 0;                  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);          s->term = packet_get_string(&len);
           s->col = packet_get_int();
         if (compat20) {          s->row = packet_get_int();
                 s->col = packet_get_int();  
                 s->row = packet_get_int();  
         } else {  
                 s->row = packet_get_int();  
                 s->col = packet_get_int();  
         }  
         s->xpixel = packet_get_int();          s->xpixel = packet_get_int();
         s->ypixel = packet_get_int();          s->ypixel = packet_get_int();
   
Line 1270 
Line 1315 
                 xfree(s->term);                  xfree(s->term);
                 s->term = NULL;                  s->term = NULL;
         }          }
   
         /* Allocate a pty and open it. */          /* Allocate a pty and open it. */
         debug("Allocating pty.");          if (!pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, sizeof(s->tty))) {
         if (!PRIVSEP(pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, sizeof(s->tty)))) {                  xfree(s->term);
                 if (s->term)  
                         xfree(s->term);  
                 s->term = NULL;                  s->term = NULL;
                 s->ptyfd = -1;                  s->ptyfd = -1;
                 s->ttyfd = -1;                  s->ttyfd = -1;
Line 1283 
Line 1325 
                 return 0;                  return 0;
         }          }
         debug("session_pty_req: session %d alloc %s", s->self, s->tty);          debug("session_pty_req: session %d alloc %s", s->self, s->tty);
   
         /* for SSH1 the tty modes length is not given */  
         if (!compat20)  
                 n_bytes = packet_remaining();  
         tty_parse_modes(s->ttyfd, &n_bytes);  
   
         /*          /*
          * Add a cleanup function to clear the utmp entry and record logout           * Add a cleanup function to clear the utmp entry and record logout
          * time in case we call fatal() (e.g., the connection gets closed).           * time in case we call fatal() (e.g., the connection gets closed).
          */           */
         fatal_add_cleanup(session_pty_cleanup, (void *)s);          fatal_add_cleanup(pty_cleanup_proc, (void *)s);
         if (!use_privsep)          pty_setowner(s->pw, s->tty);
                 pty_setowner(s->pw, s->tty);          /* Get window size from the packet. */
   
         /* Set window size from the packet. */  
         pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel);          pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel);
   
         packet_check_eom();          /* Get tty modes from the packet. */
           tty_parse_modes(s->ttyfd, &n_bytes);
           packet_done();
   
         session_proctitle(s);          session_proctitle(s);
   
         return 1;          return 1;
 }  }
   
 static int  int
 session_subsystem_req(Session *s)  session_subsystem_req(Session *s)
 {  {
         struct stat st;  
         u_int len;          u_int len;
         int success = 0;          int success = 0;
         char *cmd, *subsys = packet_get_string(&len);          char *subsys = packet_get_string(&len);
         int i;          int i;
   
         packet_check_eom();          packet_done();
         log("subsystem request for %.100s", subsys);          log("subsystem request for %s", subsys);
   
         for (i = 0; i < options.num_subsystems; i++) {          for (i = 0; i < options.num_subsystems; i++) {
                 if (strcmp(subsys, options.subsystem_name[i]) == 0) {                  if(strcmp(subsys, options.subsystem_name[i]) == 0) {
                         cmd = options.subsystem_command[i];                          debug("subsystem: exec() %s", options.subsystem_command[i]);
                         if (stat(cmd, &st) < 0) {  
                                 error("subsystem: cannot stat %s: %s", cmd,  
                                     strerror(errno));  
                                 break;  
                         }  
                         debug("subsystem: exec() %s", cmd);  
                         s->is_subsystem = 1;                          s->is_subsystem = 1;
                         do_exec(s, cmd);                          do_exec_no_pty(s, options.subsystem_command[i]);
                         success = 1;                          success = 1;
                         break;  
                 }                  }
         }          }
   
         if (!success)          if (!success)
                 log("subsystem request for %.100s failed, subsystem not found",                  log("subsystem request for %s failed, subsystem not found", subsys);
                     subsys);  
   
         xfree(subsys);          xfree(subsys);
         return success;          return success;
 }  }
   
 static int  int
 session_x11_req(Session *s)  session_x11_req(Session *s)
 {  {
         int success;          int fd;
           if (no_x11_forwarding_flag) {
                   debug("X11 forwarding disabled in user configuration file.");
                   return 0;
           }
           if (!options.x11_forwarding) {
                   debug("X11 forwarding disabled in server configuration file.");
                   return 0;
           }
           if (xauthfile != NULL) {
                   debug("X11 fwd already started.");
                   return 0;
           }
   
           debug("Received request for X11 forwarding with auth spoofing.");
           if (s->display != NULL)
                   packet_disconnect("Protocol error: X11 display already set.");
   
         s->single_connection = packet_get_char();          s->single_connection = packet_get_char();
         s->auth_proto = packet_get_string(NULL);          s->auth_proto = packet_get_string(NULL);
         s->auth_data = packet_get_string(NULL);          s->auth_data = packet_get_string(NULL);
         s->screen = packet_get_int();          s->screen = packet_get_int();
         packet_check_eom();          packet_done();
   
         success = session_setup_x11fwd(s);          s->display = x11_create_display_inet(s->screen, options.x11_display_offset);
         if (!success) {          if (s->display == NULL) {
                 xfree(s->auth_proto);                  xfree(s->auth_proto);
                 xfree(s->auth_data);                  xfree(s->auth_data);
                 s->auth_proto = NULL;                  return 0;
                 s->auth_data = NULL;  
         }          }
         return success;          xauthfile = xmalloc(MAXPATHLEN);
           strlcpy(xauthfile, "/tmp/ssh-XXXXXXXX", MAXPATHLEN);
           temporarily_use_uid(s->pw);
           if (mkdtemp(xauthfile) == NULL) {
                   restore_uid();
                   error("private X11 dir: mkdtemp %s failed: %s",
                       xauthfile, strerror(errno));
                   xfree(xauthfile);
                   xauthfile = NULL;
                   xfree(s->auth_proto);
                   xfree(s->auth_data);
                   /* XXXX remove listening channels */
                   return 0;
           }
           strlcat(xauthfile, "/cookies", MAXPATHLEN);
           fd = open(xauthfile, O_RDWR|O_CREAT|O_EXCL, 0600);
           if (fd >= 0)
                   close(fd);
           restore_uid();
           fatal_add_cleanup(xauthfile_cleanup_proc, s);
           return 1;
 }  }
   
 static int  int
 session_shell_req(Session *s)  session_shell_req(Session *s)
 {  {
         packet_check_eom();          /* if forced_command == NULL, the shell is execed */
         do_exec(s, NULL);          char *shell = forced_command;
           packet_done();
           if (s->ttyfd == -1)
                   do_exec_no_pty(s, shell);
           else
                   do_exec_pty(s, shell);
         return 1;          return 1;
 }  }
   
 static int  int
 session_exec_req(Session *s)  session_exec_req(Session *s)
 {  {
         u_int len;          u_int len;
         char *command = packet_get_string(&len);          char *command = packet_get_string(&len);
         packet_check_eom();          packet_done();
         do_exec(s, command);          if (forced_command) {
         xfree(command);                  original_command = command;
                   command = forced_command;
                   debug("Forced command '%.500s'", forced_command);
           }
           if (s->ttyfd == -1)
                   do_exec_no_pty(s, command);
           else
                   do_exec_pty(s, command);
           if (forced_command == NULL)
                   xfree(command);
         return 1;          return 1;
 }  }
   
 static int  int
 session_auth_agent_req(Session *s)  session_auth_agent_req(Session *s)
 {  {
         static int called = 0;          static int called = 0;
         packet_check_eom();          packet_done();
         if (no_agent_forwarding_flag) {          if (no_agent_forwarding_flag) {
                 debug("session_auth_agent_req: no_agent_forwarding_flag");                  debug("session_auth_agent_req: no_agent_forwarding_flag");
                 return 0;                  return 0;
Line 1398 
Line 1476 
         }          }
 }  }
   
 int  void
 session_input_channel_req(Channel *c, const char *rtype)  session_input_channel_req(int id, void *arg)
 {  {
           u_int len;
           int reply;
         int success = 0;          int success = 0;
           char *rtype;
         Session *s;          Session *s;
           Channel *c;
   
         if ((s = session_by_channel(c->self)) == NULL) {          rtype = packet_get_string(&len);
                 log("session_input_channel_req: no session %d req %.100s",          reply = packet_get_char();
                     c->self, rtype);  
                 return 0;  
         }  
         debug("session_input_channel_req: session %d req %s", s->self, rtype);  
   
           s = session_by_channel(id);
           if (s == NULL)
                   fatal("session_input_channel_req: channel %d: no session", id);
           c = channel_lookup(id);
           if (c == NULL)
                   fatal("session_input_channel_req: channel %d: bad channel", id);
   
           debug("session_input_channel_req: session %d channel %d request %s reply %d",
               s->self, id, rtype, reply);
   
         /*          /*
          * a session is in LARVAL state until a shell, a command           * a session is in LARVAL state until a shell, a command
          * or a subsystem is executed           * or a subsystem is executed
Line 1433 
Line 1521 
         if (strcmp(rtype, "window-change") == 0) {          if (strcmp(rtype, "window-change") == 0) {
                 success = session_window_change_req(s);                  success = session_window_change_req(s);
         }          }
         return success;  
           if (reply) {
                   packet_start(success ?
                       SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE);
                   packet_put_int(c->remote_id);
                   packet_send();
           }
           xfree(rtype);
 }  }
   
 void  void
Line 1450 
Line 1545 
         channel_set_fds(s->chanid,          channel_set_fds(s->chanid,
             fdout, fdin, fderr,              fdout, fdin, fderr,
             fderr == -1 ? CHAN_EXTENDED_IGNORE : CHAN_EXTENDED_READ,              fderr == -1 ? CHAN_EXTENDED_IGNORE : CHAN_EXTENDED_READ,
             1,              1);
             CHAN_SES_WINDOW_DEFAULT);  
 }  }
   
 /*  
  * Function to perform pty cleanup. Also called if we get aborted abnormally  
  * (e.g., due to a dropped connection).  
  */  
 void  void
 session_pty_cleanup2(void *session)  session_pty_cleanup(Session *s)
 {  {
         Session *s = session;          if (s == NULL || s->ttyfd == -1)
   
         if (s == NULL) {  
                 error("session_pty_cleanup: no session");  
                 return;                  return;
         }  
         if (s->ttyfd == -1)  
                 return;  
   
         debug("session_pty_cleanup: session %d release %s", s->self, s->tty);          debug("session_pty_cleanup: session %d release %s", s->self, s->tty);
   
           /* Cancel the cleanup function. */
           fatal_remove_cleanup(pty_cleanup_proc, (void *)s);
   
         /* Record that the user has logged out. */          /* Record that the user has logged out. */
         if (s->pid != 0)          record_logout(s->pid, s->tty);
                 record_logout(s->pid, s->tty);  
   
         /* Release the pseudo-tty. */          /* Release the pseudo-tty. */
         if (getuid() == 0)          pty_release(s->tty);
                 pty_release(s->tty);  
   
         /*          /*
          * Close the server side of the socket pairs.  We must do this after           * Close the server side of the socket pairs.  We must do this after
Line 1486 
Line 1571 
          * while we're still cleaning up.           * while we're still cleaning up.
          */           */
         if (close(s->ptymaster) < 0)          if (close(s->ptymaster) < 0)
                 error("close(s->ptymaster/%d): %s", s->ptymaster, strerror(errno));                  error("close(s->ptymaster): %s", strerror(errno));
   
         /* unlink pty from session */  
         s->ttyfd = -1;  
 }  }
   
 void  void
 session_pty_cleanup(void *session)  
 {  
         PRIVSEP(session_pty_cleanup2(session));  
 }  
   
 static void  
 session_exit_message(Session *s, int status)  session_exit_message(Session *s, int status)
 {  {
         Channel *c;          Channel *c;
           if (s == NULL)
         if ((c = channel_lookup(s->chanid)) == NULL)                  fatal("session_close: no session");
                 fatal("session_exit_message: session %d: no channel %d",          c = channel_lookup(s->chanid);
           if (c == NULL)
                   fatal("session_close: session %d: no channel %d",
                     s->self, s->chanid);                      s->self, s->chanid);
         debug("session_exit_message: session %d channel %d pid %d",          debug("session_exit_message: session %d channel %d pid %d",
             s->self, s->chanid, s->pid);              s->self, s->chanid, s->pid);
   
         if (WIFEXITED(status)) {          if (WIFEXITED(status)) {
                 channel_request_start(s->chanid, "exit-status", 0);                  channel_request_start(s->chanid,
                       "exit-status", 0);
                 packet_put_int(WEXITSTATUS(status));                  packet_put_int(WEXITSTATUS(status));
                 packet_send();                  packet_send();
         } else if (WIFSIGNALED(status)) {          } else if (WIFSIGNALED(status)) {
                 channel_request_start(s->chanid, "exit-signal", 0);                  channel_request_start(s->chanid,
                       "exit-signal", 0);
                 packet_put_int(WTERMSIG(status));                  packet_put_int(WTERMSIG(status));
                 packet_put_char(WCOREDUMP(status));                  packet_put_char(WCOREDUMP(status));
                 packet_put_cstring("");                  packet_put_cstring("");
Line 1540 
Line 1620 
 }  }
   
 void  void
 session_close(Session *s)  session_free(Session *s)
 {  {
         debug("session_close: session %d pid %d", s->self, s->pid);          debug("session_free: session %d pid %d", s->self, s->pid);
         if (s->ttyfd != -1) {  
                 fatal_remove_cleanup(session_pty_cleanup, (void *)s);  
                 session_pty_cleanup(s);  
         }  
         if (s->term)          if (s->term)
                 xfree(s->term);                  xfree(s->term);
         if (s->display)          if (s->display)
                 xfree(s->display);                  xfree(s->display);
         if (s->auth_display)  
                 xfree(s->auth_display);  
         if (s->auth_data)          if (s->auth_data)
                 xfree(s->auth_data);                  xfree(s->auth_data);
         if (s->auth_proto)          if (s->auth_proto)
                 xfree(s->auth_proto);                  xfree(s->auth_proto);
         s->used = 0;          s->used = 0;
   }
   
   void
   session_close(Session *s)
   {
           session_pty_cleanup(s);
           session_free(s);
         session_proctitle(s);          session_proctitle(s);
 }  }
   
Line 1566 
Line 1647 
 {  {
         Session *s = session_by_pid(pid);          Session *s = session_by_pid(pid);
         if (s == NULL) {          if (s == NULL) {
                 debug("session_close_by_pid: no session for pid %d", pid);                  debug("session_close_by_pid: no session for pid %d", s->pid);
                 return;                  return;
         }          }
         if (s->chanid != -1)          if (s->chanid != -1)
Line 1583 
Line 1664 
 {  {
         Session *s = session_by_channel(id);          Session *s = session_by_channel(id);
         if (s == NULL) {          if (s == NULL) {
                 debug("session_close_by_channel: no session for id %d", id);                  debug("session_close_by_channel: no session for channel %d", id);
                 return;                  return;
         }          }
         debug("session_close_by_channel: channel %d child %d", id, s->pid);          /* disconnect channel */
         if (s->pid != 0) {  
                 debug("session_close_by_channel: channel %d: has child", id);  
                 /*  
                  * delay detach of session, but release pty, since  
                  * the fd's to the child are already closed  
                  */  
                 if (s->ttyfd != -1) {  
                         fatal_remove_cleanup(session_pty_cleanup, (void *)s);  
                         session_pty_cleanup(s);  
                 }  
                 return;  
         }  
         /* detach by removing callback */  
         channel_cancel_cleanup(s->chanid);          channel_cancel_cleanup(s->chanid);
         s->chanid = -1;          s->chanid = -1;
         session_close(s);  
 }  
   
 void          debug("session_close_by_channel: channel %d kill %d", id, s->pid);
 session_destroy_all(void (*closefunc)(Session *))          if (s->pid == 0) {
 {                  /* close session immediately */
         int i;                  session_close(s);
         for (i = 0; i < MAX_SESSIONS; i++) {          } else {
                 Session *s = &sessions[i];                  /* notify child, delay session cleanup */
                 if (s->used) {                  if (kill(s->pid, (s->ttyfd == -1) ? SIGTERM : SIGHUP) < 0)
                         if (closefunc != NULL)                          error("session_close_by_channel: kill %d: %s",
                                 closefunc(s);                              s->pid, strerror(errno));
                         else  
                                 session_close(s);  
                 }  
         }          }
 }  }
   
 static char *  char *
 session_tty_list(void)  session_tty_list(void)
 {  {
         static char buf[1024];          static char buf[1024];
         int i;          int i;
         buf[0] = '\0';          buf[0] = '\0';
         for (i = 0; i < MAX_SESSIONS; i++) {          for(i = 0; i < MAX_SESSIONS; i++) {
                 Session *s = &sessions[i];                  Session *s = &sessions[i];
                 if (s->used && s->ttyfd != -1) {                  if (s->used && s->ttyfd != -1) {
                         if (buf[0] != '\0')                          if (buf[0] != '\0')
Line 1648 
Line 1711 
                 setproctitle("%s@%s", s->pw->pw_name, session_tty_list());                  setproctitle("%s@%s", s->pw->pw_name, session_tty_list());
 }  }
   
 int  void
 session_setup_x11fwd(Session *s)  
 {  
         struct stat st;  
         char display[512], auth_display[512];  
         char hostname[MAXHOSTNAMELEN];  
   
         if (no_x11_forwarding_flag) {  
                 packet_send_debug("X11 forwarding disabled in user configuration file.");  
                 return 0;  
         }  
         if (!options.x11_forwarding) {  
                 debug("X11 forwarding disabled in server configuration file.");  
                 return 0;  
         }  
         if (!options.xauth_location ||  
             (stat(options.xauth_location, &st) == -1)) {  
                 packet_send_debug("No xauth program; cannot forward with spoofing.");  
                 return 0;  
         }  
         if (options.use_login) {  
                 packet_send_debug("X11 forwarding disabled; "  
                     "not compatible with UseLogin=yes.");  
                 return 0;  
         }  
         if (s->display != NULL) {  
                 debug("X11 display already set.");  
                 return 0;  
         }  
         s->display_number = x11_create_display_inet(options.x11_display_offset,  
             options.x11_use_localhost, s->single_connection);  
         if (s->display_number == -1) {  
                 debug("x11_create_display_inet failed.");  
                 return 0;  
         }  
   
         /* Set up a suitable value for the DISPLAY variable. */  
         if (gethostname(hostname, sizeof(hostname)) < 0)  
                 fatal("gethostname: %.100s", strerror(errno));  
         /*  
          * auth_display must be used as the displayname when the  
          * authorization entry is added with xauth(1).  This will be  
          * different than the DISPLAY string for localhost displays.  
          */  
         if (options.x11_use_localhost) {  
                 snprintf(display, sizeof display, "localhost:%d.%d",  
                     s->display_number, s->screen);  
                 snprintf(auth_display, sizeof auth_display, "unix:%d.%d",  
                     s->display_number, s->screen);  
                 s->display = xstrdup(display);  
                 s->auth_display = xstrdup(auth_display);  
         } else {  
                 snprintf(display, sizeof display, "%.400s:%d.%d", hostname,  
                     s->display_number, s->screen);  
                 s->display = xstrdup(display);  
                 s->auth_display = xstrdup(display);  
         }  
   
         return 1;  
 }  
   
 static void  
 do_authenticated2(Authctxt *authctxt)  do_authenticated2(Authctxt *authctxt)
 {  {
         server_loop2(authctxt);  
           server_loop2();
           if (xauthfile)
                   xauthfile_cleanup_proc(NULL);
 }  }

Legend:
Removed from v.1.74.2.6  
changed lines
  Added in v.1.75