X-Git-Url: http://andersk.mit.edu/gitweb/openssh.git/blobdiff_plain/6fcf67f70da74e2957b9519aad296cd93db07fc9..1431a9004eca672916d485091f3db0a20954a9a0:/session.c diff --git a/session.c b/session.c index 4873e4d7..747a00af 100644 --- a/session.c +++ b/session.c @@ -33,7 +33,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: session.c,v 1.102 2001/09/16 14:46:54 markus Exp $"); +RCSID("$OpenBSD: session.c,v 1.142 2002/06/26 13:49:26 deraadt Exp $"); #include "ssh.h" #include "ssh1.h" @@ -56,20 +56,7 @@ RCSID("$OpenBSD: session.c,v 1.102 2001/09/16 14:46:54 markus Exp $"); #include "serverloop.h" #include "canohost.h" #include "session.h" - -#ifdef WITH_IRIX_PROJECT -#include -#endif /* WITH_IRIX_PROJECT */ -#ifdef WITH_IRIX_JOBS -#include -#endif -#ifdef WITH_IRIX_AUDIT -#include -#endif /* WITH_IRIX_AUDIT */ - -#if defined(HAVE_USERSEC_H) -#include -#endif +#include "monitor_wrap.h" #ifdef HAVE_CYGWIN #include @@ -77,58 +64,20 @@ RCSID("$OpenBSD: session.c,v 1.102 2001/09/16 14:46:54 markus Exp $"); #define is_winnt (GetVersion() < 0x80000000) #endif -/* AIX limits */ -#if defined(HAVE_GETUSERATTR) && !defined(S_UFSIZE_HARD) && defined(S_UFSIZE) -# define S_UFSIZE_HARD S_UFSIZE "_hard" -# define S_UCPU_HARD S_UCPU "_hard" -# define S_UDATA_HARD S_UDATA "_hard" -# define S_USTACK_HARD S_USTACK "_hard" -# define S_URSS_HARD S_URSS "_hard" -# define S_UCORE_HARD S_UCORE "_hard" -# define S_UNOFILE_HARD S_UNOFILE "_hard" -#endif - -#ifdef _AIX -# include -#endif - -/* types */ - -#define TTYSZ 64 -typedef struct Session Session; -struct Session { - int used; - int self; - struct passwd *pw; - Authctxt *authctxt; - 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 */ Session *session_new(void); void session_set_fds(Session *, int, int, int); -static void session_pty_cleanup(void *); +void session_pty_cleanup(void *); 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 *); void do_login(Session *, const char *); +#ifdef LOGIN_NEEDS_UTMPX +static void do_pre_login(Session *s); +#endif void do_child(Session *, const char *); void do_motd(void); int check_quietlogin(Session *, const char *); @@ -136,7 +85,6 @@ int check_quietlogin(Session *, const char *); static void do_authenticated1(Authctxt *); static void do_authenticated2(Authctxt *); -static void session_close(Session *); static int session_pty_req(Session *); /* import */ @@ -156,14 +104,100 @@ const char *original_command = NULL; Session sessions[MAX_SESSIONS]; #ifdef WITH_AIXAUTHENTICATE -/* AIX's lastlogin message, set in auth1.c */ char *aixloginmsg; #endif /* WITH_AIXAUTHENTICATE */ #ifdef HAVE_LOGIN_CAP -static login_cap_t *lc; +login_cap_t *lc; #endif +/* Name and directory of socket for authentication agent forwarding. */ +static char *auth_sock_name = NULL; +static char *auth_sock_dir = NULL; + +/* removes the agent forwarding socket */ + +static void +auth_sock_cleanup_proc(void *_pw) +{ + struct passwd *pw = _pw; + + if (auth_sock_name != NULL) { + temporarily_use_uid(pw); + unlink(auth_sock_name); + rmdir(auth_sock_dir); + auth_sock_name = NULL; + restore_uid(); + } +} + +static int +auth_input_request_forwarding(struct passwd * pw) +{ + Channel *nc; + int sock; + struct sockaddr_un sunaddr; + + if (auth_sock_name != NULL) { + error("authentication forwarding requested twice."); + return 0; + } + + /* Temporarily drop privileged uid for mkdir/bind. */ + 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-XXXXXXXX", MAXPATHLEN); + + /* 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; + } + snprintf(auth_sock_name, MAXPATHLEN, "%s/agent.%ld", + auth_sock_dir, (long) getpid()); + + /* delete agent socket on fatal() */ + fatal_add_cleanup(auth_sock_cleanup_proc, pw); + + /* Create the socket. */ + sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock < 0) + packet_disconnect("socket: %.100s", strerror(errno)); + + /* 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)); + + /* Restore the privileged uid. */ + restore_uid(); + + /* Start listening on the socket. */ + if (listen(sock, 5) < 0) + packet_disconnect("listen: %.100s", strerror(errno)); + + /* Allocate a channel for the authentication agent socket. */ + nc = channel_new("auth socket", + SSH_CHANNEL_AUTH_SOCKET, sock, sock, -1, + CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, + 0, xstrdup("auth socket"), 1); + strlcpy(nc->path, auth_sock_name, sizeof(nc->path)); + return 1; +} + + void do_authenticated(Authctxt *authctxt) { @@ -176,18 +210,14 @@ do_authenticated(Authctxt *authctxt) close(startup_pipe); startup_pipe = -1; } -#if defined(HAVE_LOGIN_CAP) && defined(HAVE_PW_CLASS_IN_PASSWD) - 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 +#ifdef WITH_AIXAUTHENTICATE + /* We don't have a pty yet, so just label the line as "ssh" */ + if (loginsuccess(authctxt->user, + get_canonical_hostname(options.verify_reverse_mapping), + "ssh", &aixloginmsg) < 0) + aixloginmsg = NULL; +#endif /* WITH_AIXAUTHENTICATE */ + /* setup the channel layer */ if (!no_port_forwarding_flag && options.allow_tcp_forwarding) channel_permit_all_opens(); @@ -198,7 +228,7 @@ do_authenticated(Authctxt *authctxt) do_authenticated1(authctxt); /* remove agent socket */ - if (auth_get_socket_name()) + if (auth_sock_name != NULL) auth_sock_cleanup_proc(authctxt->pw); #ifdef KRB4 if (options.kerberos_ticket_cleanup) @@ -221,9 +251,9 @@ do_authenticated1(Authctxt *authctxt) { Session *s; char *command; - int success, type, plen, screen_flag; - int compression_level = 0, enable_compression_after_reply = 0; - u_int proto_len, data_len, dlen; + int success, type, screen_flag; + int enable_compression_after_reply = 0; + u_int proto_len, data_len, dlen, compression_level = 0; s = session_new(); s->authctxt = authctxt; @@ -237,16 +267,20 @@ do_authenticated1(Authctxt *authctxt) success = 0; /* Get a packet from the client. */ - type = packet_read(&plen); + type = packet_read(); /* Process the packet. */ switch (type) { case SSH_CMSG_REQUEST_COMPRESSION: - packet_integrity_check(plen, 4, type); compression_level = packet_get_int(); + packet_check_eom(); if (compression_level < 1 || compression_level > 9) { packet_send_debug("Received illegal compression level %d.", - compression_level); + compression_level); + break; + } + if (!options.compression) { + debug2("compression disabled"); break; } /* Enable compression after we have responded with SUCCESS. */ @@ -274,7 +308,7 @@ do_authenticated1(Authctxt *authctxt) } else { s->screen = 0; } - packet_done(); + packet_check_eom(); success = session_setup_x11fwd(s); if (!success) { xfree(s->auth_proto); @@ -311,22 +345,22 @@ do_authenticated1(Authctxt *authctxt) if (packet_set_maxsize(packet_get_int()) > 0) success = 1; 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_integrity_check(plen, 4 + dlen, type); - + 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 @@ -344,7 +378,7 @@ do_authenticated1(Authctxt *authctxt) } break; #endif /* AFS || KRB5 */ - + #ifdef AFS case SSH_CMSG_HAVE_AFS_TOKEN: if (!options.afs_token_passing || !k_hasafs()) { @@ -352,8 +386,8 @@ do_authenticated1(Authctxt *authctxt) } else { /* Accept AFS token. */ char *token = packet_get_string(&dlen); - packet_integrity_check(plen, 4 + dlen, type); - + packet_check_eom(); + if (auth_afs_token(s->authctxt, token)) success = 1; else @@ -374,7 +408,7 @@ do_authenticated1(Authctxt *authctxt) } else { do_exec(s, NULL); } - packet_done(); + packet_check_eom(); session_close(s); return; @@ -405,7 +439,7 @@ do_authenticated1(Authctxt *authctxt) void do_exec_no_pty(Session *s, const char *command) { - int pid; + pid_t pid; #ifdef USE_PIPES int pin[2], pout[2], perr[2]; @@ -429,6 +463,9 @@ do_exec_no_pty(Session *s, const char *command) #if defined(USE_PAM) do_pam_session(s->pw->pw_name, NULL); do_pam_setcred(1); + if (is_pam_password_change_required()) + packet_disconnect("Password change required but no " + "TTY available"); #endif /* USE_PAM */ /* Fork the child. */ @@ -557,17 +594,13 @@ do_exec_pty(Session *s, const char *command) /* Make the pseudo tty our controlling tty. */ pty_make_controlling_tty(&ttyfd, s->tty); - /* Redirect stdin from the pseudo tty. */ - if (dup2(ttyfd, fileno(stdin)) < 0) - error("dup2 stdin failed: %.100s", 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)); + /* Redirect stdin/stdout/stderr from the pseudo tty. */ + if (dup2(ttyfd, 0) < 0) + error("dup2 stdin: %s", strerror(errno)); + if (dup2(ttyfd, 1) < 0) + error("dup2 stdout: %s", strerror(errno)); + if (dup2(ttyfd, 2) < 0) + error("dup2 stderr: %s", strerror(errno)); /* Close the extra descriptor for the pseudo tty. */ close(ttyfd); @@ -638,14 +671,14 @@ do_pre_login(Session *s) if (packet_connection_is_on_socket()) { fromlen = sizeof(from); if (getpeername(packet_get_connection_in(), - (struct sockaddr *) & from, &fromlen) < 0) { + (struct sockaddr *) & from, &fromlen) < 0) { debug("getpeername: %.100s", strerror(errno)); fatal_cleanup(); } } record_utmp_only(pid, s->tty, s->pw->pw_name, - get_remote_name_or_ip(utmp_len, options.reverse_mapping_check), + get_remote_name_or_ip(utmp_len, options.verify_reverse_mapping), (struct sockaddr *)&from); } #endif @@ -671,15 +704,14 @@ do_exec(Session *s, const char *command) original_command = NULL; } + /* administrative, login(1)-like work */ void do_login(Session *s, const char *command) { char *time_string; - char hostname[MAXHOSTNAMELEN]; socklen_t fromlen; struct sockaddr_storage from; - time_t last_login_time; struct passwd * pw = s->pw; pid_t pid = getpid(); @@ -691,23 +723,18 @@ do_login(Session *s, const char *command) if (packet_connection_is_on_socket()) { fromlen = sizeof(from); if (getpeername(packet_get_connection_in(), - (struct sockaddr *) & from, &fromlen) < 0) { + (struct sockaddr *) & from, &fromlen) < 0) { debug("getpeername: %.100s", strerror(errno)); 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_login(pid, s->tty, pw->pw_name, pw->pw_uid, - get_remote_name_or_ip(utmp_len, options.reverse_mapping_check), - (struct sockaddr *)&from); + if (!use_privsep) + record_login(pid, s->tty, pw->pw_name, pw->pw_uid, + get_remote_name_or_ip(utmp_len, + options.verify_reverse_mapping), + (struct sockaddr *)&from); #ifdef USE_PAM /* @@ -732,14 +759,15 @@ do_login(Session *s, const char *command) printf("%s\n", aixloginmsg); #endif /* WITH_AIXAUTHENTICATE */ - if (options.print_lastlog && last_login_time != 0) { - time_string = ctime(&last_login_time); + if (options.print_lastlog && s->last_login_time != 0) { + time_string = ctime(&s->last_login_time); if (strchr(time_string, '\n')) *strchr(time_string, '\n') = 0; - if (strcmp(hostname, "") == 0) + if (strcmp(s->hostname, "") == 0) printf("Last login: %s\r\n", time_string); else - printf("Last login: %s from %s\r\n", time_string, hostname); + printf("Last login: %s from %s\r\n", time_string, + s->hostname); } do_motd(); @@ -800,7 +828,7 @@ check_quietlogin(Session *s, const char *command) */ static void child_set_env(char ***envp, u_int *envsizep, const char *name, - const char *value) + const char *value) { u_int i, namelen; char **env; @@ -821,6 +849,9 @@ child_set_env(char ***envp, u_int *envsizep, const char *name, } else { /* New variable. Expand if necessary. */ if (i >= (*envsizep) - 1) { + if (*envsizep >= 1000) + fatal("child_set_env: too many env vars," + " skipping: %.100s", name); (*envsizep) += 50; env = (*envp) = xrealloc(env, (*envsizep) * sizeof(char *)); } @@ -841,17 +872,20 @@ child_set_env(char ***envp, u_int *envsizep, const char *name, */ static void read_environment_file(char ***env, u_int *envsize, - const char *filename) + const char *filename) { FILE *f; char buf[4096]; char *cp, *value; + u_int lineno = 0; f = fopen(filename, "r"); if (!f) return; while (fgets(buf, sizeof(buf), f)) { + if (++lineno > 1000) + fatal("Too many lines in environment file %s", filename); for (cp = buf; *cp == ' ' || *cp == '\t'; cp++) ; if (!*cp || *cp == '#' || *cp == '\n') @@ -860,7 +894,8 @@ read_environment_file(char ***env, u_int *envsize, *strchr(cp, '\n') = '\0'; value = strchr(cp, '='); if (value == NULL) { - fprintf(stderr, "Bad line in %.100s: %.200s\n", filename, buf); + fprintf(stderr, "Bad line %u in %.100s\n", lineno, + filename); continue; } /* @@ -874,325 +909,36 @@ read_environment_file(char ***env, u_int *envsize, fclose(f); } -#ifdef USE_PAM -/* - * Sets any environment variables which have been specified by PAM - */ -void do_pam_environment(char ***env, u_int *envsize) +void copy_environment(char **source, char ***env, u_int *envsize) { - char *equals, var_name[512], var_val[512]; - char **pam_env; + char *var_name, *var_val; int i; - if ((pam_env = fetch_pam_environment()) == NULL) + if (source == NULL) return; - for(i = 0; pam_env[i] != NULL; i++) { - if ((equals = strstr(pam_env[i], "=")) == NULL) + for(i = 0; source[i] != NULL; i++) { + var_name = xstrdup(source[i]); + if ((var_val = strstr(var_name, "=")) == NULL) { + xfree(var_name); continue; - - if (strlen(pam_env[i]) < (sizeof(var_name) - 1)) { - memset(var_name, '\0', sizeof(var_name)); - memset(var_val, '\0', sizeof(var_val)); - - strncpy(var_name, pam_env[i], equals - pam_env[i]); - strcpy(var_val, equals + 1); - - debug3("PAM environment: %s=%s", var_name, var_val); - - child_set_env(env, envsize, var_name, var_val); } - } -} -#endif /* USE_PAM */ + *var_val++ = '\0'; -#ifdef HAVE_CYGWIN -void copy_environment(char ***env, u_int *envsize) -{ - char *equals, var_name[512], var_val[512]; - int i; - - for(i = 0; environ[i] != NULL; i++) { - if ((equals = strstr(environ[i], "=")) == NULL) - continue; - - if (strlen(environ[i]) < (sizeof(var_name) - 1)) { - memset(var_name, '\0', sizeof(var_name)); - memset(var_val, '\0', sizeof(var_val)); - - strncpy(var_name, environ[i], equals - environ[i]); - strcpy(var_val, equals + 1); - - debug3("Copy environment: %s=%s", var_name, var_val); - - child_set_env(env, envsize, var_name, var_val); - } - } -} -#endif - -#if defined(HAVE_GETUSERATTR) -/* - * AIX-specific login initialisation - */ -void set_limit(char *user, char *soft, char *hard, int resource, int mult) -{ - struct rlimit rlim; - int slim, hlim; - - getrlimit(resource, &rlim); - - slim = 0; - if (getuserattr(user, soft, &slim, SEC_INT) != -1) { - if (slim < 0) { - rlim.rlim_cur = RLIM_INFINITY; - } else if (slim != 0) { - /* See the wackiness below */ - if (rlim.rlim_cur == slim * mult) - slim = 0; - else - rlim.rlim_cur = slim * mult; - } - } - - hlim = 0; - if (getuserattr(user, hard, &hlim, SEC_INT) != -1) { - if (hlim < 0) { - rlim.rlim_max = RLIM_INFINITY; - } else if (hlim != 0) { - rlim.rlim_max = hlim * mult; - } - } - - /* - * XXX For cpu and fsize the soft limit is set to the hard limit - * if the hard limit is left at its default value and the soft limit - * is changed from its default value, either by requesting it - * (slim == 0) or by setting it to the current default. At least - * that's how rlogind does it. If you're confused you're not alone. - * Bug or feature? AIX 4.3.1.2 - */ - if ((!strcmp(soft, "fsize") || !strcmp(soft, "cpu")) - && hlim == 0 && slim != 0) - rlim.rlim_max = rlim.rlim_cur; - /* A specified hard limit limits the soft limit */ - else if (hlim > 0 && rlim.rlim_cur > rlim.rlim_max) - rlim.rlim_cur = rlim.rlim_max; - /* A soft limit can increase a hard limit */ - else if (rlim.rlim_cur > rlim.rlim_max) - rlim.rlim_max = rlim.rlim_cur; - - if (setrlimit(resource, &rlim) != 0) - error("setrlimit(%.10s) failed: %.100s", soft, strerror(errno)); -} - -void set_limits_from_userattr(char *user) -{ - int mask; - char buf[16]; - - set_limit(user, S_UFSIZE, S_UFSIZE_HARD, RLIMIT_FSIZE, 512); - set_limit(user, S_UCPU, S_UCPU_HARD, RLIMIT_CPU, 1); - set_limit(user, S_UDATA, S_UDATA_HARD, RLIMIT_DATA, 512); - set_limit(user, S_USTACK, S_USTACK_HARD, RLIMIT_STACK, 512); - set_limit(user, S_URSS, S_URSS_HARD, RLIMIT_RSS, 512); - set_limit(user, S_UCORE, S_UCORE_HARD, RLIMIT_CORE, 512); -#if defined(S_UNOFILE) - set_limit(user, S_UNOFILE, S_UNOFILE_HARD, RLIMIT_NOFILE, 1); -#endif - - if (getuserattr(user, S_UMASK, &mask, SEC_INT) != -1) { - /* Convert decimal to octal */ - (void) snprintf(buf, sizeof(buf), "%d", mask); - if (sscanf(buf, "%o", &mask) == 1) - umask(mask); + debug3("Copy environment: %s=%s", var_name, var_val); + child_set_env(env, envsize, var_name, var_val); + + xfree(var_name); } } -#endif /* defined(HAVE_GETUSERATTR) */ -/* - * 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) +static char ** +do_setup_env(Session *s, const char *shell) { - const char *shell, *hostname = NULL, *cp = NULL; - struct passwd *pw = s->pw; char buf[256]; - char cmd[1024]; - FILE *f = NULL; - u_int envsize, i; + u_int i, envsize; char **env; - extern char **environ; - struct stat st; - char *argv[10]; - int do_xauth; -#ifdef WITH_IRIX_PROJECT - prid_t projid; -#endif /* WITH_IRIX_PROJECT */ -#ifdef WITH_IRIX_JOBS - jid_t jid = 0; -#else -#ifdef WITH_IRIX_ARRAY - int jid = 0; -#endif /* WITH_IRIX_ARRAY */ -#endif /* WITH_IRIX_JOBS */ - - do_xauth = - s->display != NULL && 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 !defined(HAVE_OSF_SIA) - 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 /* HAVE_LOGIN_CAP */ - if (pw->pw_uid) - f = fopen(_PATH_NOLOGIN, "r"); -# endif /* HAVE_LOGIN_CAP */ - if (f) { - /* /etc/nologin exists. Print its contents and exit. */ - while (fgets(buf, sizeof(buf), f)) - fputs(buf, stderr); - fclose(f); - exit(254); - } - } -#endif /* HAVE_OSF_SIA */ - - /* 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) { -#ifdef HAVE_OSF_SIA - session_setup_sia(pw->pw_name, s->ttyfd == -1 ? NULL : s->tty); - if (!check_quietlogin(s, command)) - do_motd(); -#else /* HAVE_OSF_SIA */ -#ifdef HAVE_CYGWIN - if (is_winnt) { -#else - if (getuid() == 0 || geteuid() == 0) { -#endif -# ifdef HAVE_GETUSERATTR - set_limits_from_userattr(pw->pw_name); -# endif /* HAVE_GETUSERATTR */ -# 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 /* HAVE_LOGIN_CAP */ -#if defined(HAVE_GETLUID) && defined(HAVE_SETLUID) - /* Sets login uid for accounting */ - if (getluid() == -1 && setluid(pw->pw_uid) == -1) - error("setluid: %s", strerror(errno)); -#endif /* defined(HAVE_GETLUID) && defined(HAVE_SETLUID) */ - - 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(); -# ifdef USE_PAM - /* - * PAM credentials may take the form of - * supplementary groups. These will have been - * wiped by the above initgroups() call. - * Reestablish them here. - */ - do_pam_setcred(0); -# endif /* USE_PAM */ -# ifdef WITH_IRIX_JOBS - jid = jlimit_startjob(pw->pw_name, pw->pw_uid, "interactive"); - if (jid == -1) { - fatal("Failed to create job container: %.100s", - strerror(errno)); - } -# endif /* WITH_IRIX_JOBS */ -# ifdef WITH_IRIX_ARRAY - /* initialize array session */ - if (jid == 0) { - if (newarraysess() != 0) - fatal("Failed to set up new array session: %.100s", - strerror(errno)); - } -# endif /* WITH_IRIX_ARRAY */ -# ifdef WITH_IRIX_PROJECT - /* initialize irix project info */ - if ((projid = getdfltprojuser(pw->pw_name)) == -1) { - debug("Failed to get project id, using projid 0"); - projid = 0; - } - if (setprid(projid)) - fatal("Failed to initialize project %d for %s: %.100s", - (int)projid, pw->pw_name, strerror(errno)); -# endif /* WITH_IRIX_PROJECT */ -#ifdef WITH_IRIX_AUDIT - if (sysconf(_SC_AUDIT)) { - debug("Setting sat id to %d", (int) pw->pw_uid); - if (satsetid(pw->pw_uid)) - debug("error setting satid: %.100s", strerror(errno)); - } -#endif /* WITH_IRIX_AUDIT */ - -#ifdef _AIX - /* - * AIX has a "usrinfo" area where logname and - * other stuff is stored - a few applications - * actually use this and die if it's not set - */ - if (s->ttyfd == -1) - s->tty[0] = '\0'; - cp = xmalloc(22 + strlen(s->tty) + - 2 * strlen(pw->pw_name)); - i = sprintf(cp, "LOGNAME=%s%cNAME=%s%cTTY=%s%c%c", - pw->pw_name, 0, pw->pw_name, 0, s->tty, 0, 0); - if (usrinfo(SETUINFO, cp, i) == -1) - fatal("Couldn't set usrinfo: %s", - strerror(errno)); - debug3("AIX/UsrInfo: set len %d", i); - xfree(cp); -#endif - - /* Permanently switch to the desired uid. */ - permanently_set_uid(pw); -# endif /* HAVE_LOGIN_CAP */ - } -#endif /* HAVE_OSF_SIA */ - -#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); - } - /* - * 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 + struct passwd *pw = s->pw; /* Initialize the environment. */ envsize = 100; @@ -1204,7 +950,7 @@ do_child(Session *s, const char *command) * The Windows environment contains some setting which are * important for a running system. They must not be dropped. */ - copy_environment(&env, &envsize); + copy_environment(environ, &env, &envsize); #endif if (!options.use_login) { @@ -1223,7 +969,12 @@ do_child(Session *s, const char *command) * needed for loading shared libraries. So the path better * remains intact here. */ +# ifdef SUPERUSER_PATH + child_set_env(&env, &envsize, "PATH", + s->pw->pw_uid == 0 ? SUPERUSER_PATH : _PATH_STDPATH); +# else child_set_env(&env, &envsize, "PATH", _PATH_STDPATH); +# endif /* SUPERUSER_PATH */ # endif /* HAVE_CYGWIN */ #endif /* HAVE_LOGIN_CAP */ @@ -1238,22 +989,25 @@ do_child(Session *s, const char *command) child_set_env(&env, &envsize, "TZ", getenv("TZ")); /* Set custom environment options from RSA authentication. */ - while (custom_environment) { - struct envstring *ce = custom_environment; - char *s = ce->s; - int i; - for (i = 0; s[i] != '=' && s[i]; i++); - if (s[i] == '=') { - s[i] = 0; - child_set_env(&env, &envsize, s, s + i + 1); + if (!options.use_login) { + while (custom_environment) { + struct envstring *ce = custom_environment; + char *s = ce->s; + + for (i = 0; s[i] != '=' && s[i]; i++) + ; + if (s[i] == '=') { + 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", - 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); if (s->ttyfd != -1) @@ -1267,11 +1021,15 @@ do_child(Session *s, const char *command) original_command); #ifdef _AIX - if ((cp = getenv("AUTHSTATE")) != NULL) - child_set_env(&env, &envsize, "AUTHSTATE", cp); - if ((cp = getenv("KRB5CCNAME")) != NULL) - child_set_env(&env, &envsize, "KRB5CCNAME", cp); - read_environment_file(&env, &envsize, "/etc/environment"); + { + char *cp; + + if ((cp = getenv("AUTHSTATE")) != NULL) + child_set_env(&env, &envsize, "AUTHSTATE", cp); + if ((cp = getenv("KRB5CCNAME")) != NULL) + child_set_env(&env, &envsize, "KRB5CCNAME", cp); + read_environment_file(&env, &envsize, "/etc/environment"); + } #endif #ifdef KRB4 if (s->authctxt->krb4_ticket_file) @@ -1285,12 +1043,12 @@ do_child(Session *s, const char *command) #endif #ifdef USE_PAM /* Pull in any environment variables that may have been set by PAM. */ - do_pam_environment(&env, &envsize); + copy_environment(fetch_pam_environment(), &env, &envsize); #endif /* USE_PAM */ - if (auth_get_socket_name() != NULL) + if (auth_sock_name != NULL) child_set_env(&env, &envsize, SSH_AUTHSOCKET_ENV_NAME, - auth_get_socket_name()); + auth_sock_name); /* read $HOME/.ssh/environment. */ if (!options.use_login) { @@ -1304,10 +1062,237 @@ do_child(Session *s, const char *command) for (i = 0; env[i]; i++) fprintf(stderr, " %.200s\n", env[i]); } + return env; +} + +/* + * Run $HOME/.ssh/rc, /etc/ssh/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 %.500s 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) +{ + char tty='\0'; + +#ifdef HAVE_CYGWIN + if (is_winnt) { +#else /* HAVE_CYGWIN */ + if (getuid() == 0 || geteuid() == 0) { +#endif /* HAVE_CYGWIN */ +#ifdef HAVE_SETPCRED + setpcred(pw->pw_name); +#endif /* HAVE_SETPCRED */ +#ifdef HAVE_LOGIN_CAP +#ifdef __bsdi__ + setpgid(0, 0); +#endif + if (setusercontext(lc, pw, pw->pw_uid, + (LOGIN_SETALL & ~LOGIN_SETPATH)) < 0) { + perror("unable to set user context"); + exit(1); + } +#else +# if defined(HAVE_GETLUID) && defined(HAVE_SETLUID) + /* Sets login uid for accounting */ + if (getluid() == -1 && setluid(pw->pw_uid) == -1) + error("setluid: %s", strerror(errno)); +# endif /* defined(HAVE_GETLUID) && defined(HAVE_SETLUID) */ + + 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(); +# ifdef USE_PAM + /* + * PAM credentials may take the form of supplementary groups. + * These will have been wiped by the above initgroups() call. + * Reestablish them here. + */ + do_pam_setcred(0); +# endif /* USE_PAM */ +# if defined(WITH_IRIX_PROJECT) || defined(WITH_IRIX_JOBS) || defined(WITH_IRIX_ARRAY) + irix_setusercontext(pw); +# endif /* defined(WITH_IRIX_PROJECT) || defined(WITH_IRIX_JOBS) || defined(WITH_IRIX_ARRAY) */ +# ifdef _AIX + /* XXX: Disable tty setting. Enabled if required later */ + aix_usrinfo(pw, &tty, -1); +# endif /* _AIX */ + /* 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(LOGIN_PROGRAM, "login", "-h", hostname, +#ifdef xxxLOGIN_NEEDS_TERM + (s->term ? s->term : "unknown"), +#endif /* LOGIN_NEEDS_TERM */ +#ifdef LOGIN_NO_ENDOPT + "-p", "-f", pw->pw_name, (char *)NULL); +#else + "-p", "-f", "--", pw->pw_name, (char *)NULL); +#endif + + /* 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) { +#ifdef HAVE_OSF_SIA + session_setup_sia(pw->pw_name, s->ttyfd == -1 ? NULL : s->tty); + if (!check_quietlogin(s, command)) + do_motd(); +#else /* HAVE_OSF_SIA */ + do_nologin(pw); + do_setusercontext(pw); +#endif /* HAVE_OSF_SIA */ + } + + /* + * 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. */ if (options.use_login) hostname = get_remote_name_or_ip(utmp_len, - options.reverse_mapping_check); + options.verify_reverse_mapping); /* * Close the connection descriptors; note that this is the child, and * the server will still have the socket open, and it is important @@ -1343,19 +1328,9 @@ do_child(Session *s, const char *command) for (i = 3; i < 64; i++) close(i); - /* 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)); -#ifdef HAVE_LOGIN_CAP - if (login_getcapbool(lc, "requirehome", 0)) - exit(1); -#endif - } - /* - * Must take new environment into use so that .ssh/rc, /etc/sshrc and - * xauth are run in the proper environment. + * Must take new environment into use so that .ssh/rc, + * /etc/ssh/sshrc and xauth are run in the proper environment. */ environ = env; @@ -1363,136 +1338,73 @@ do_child(Session *s, const char *command) /* 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 */ - /* - * 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 (debug_flag) { - fprintf(stderr, - "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; + /* 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)); +#ifdef HAVE_LOGIN_CAP + if (login_getcapbool(lc, "requirehome", 0)) + exit(1); +#endif } + if (!options.use_login) + do_rc_files(s, shell); + /* restore SIGPIPE for child */ signal(SIGPIPE, SIG_DFL); + if (options.use_login) { + launch_login(pw, hostname); + /* NEVERREACHED */ + } + + /* Get the last component of the shell name. */ + if ((shell0 = strrchr(shell, '/')) != NULL) + shell0++; + else + shell0 = shell; + /* * If we have no command, execute the shell. In this case, the shell * name to be passed in argv[0] is preceded by '-' to indicate that * this is a login shell. */ if (!command) { - if (!options.use_login) { - char buf[256]; - - /* Start the shell. Set initial character to '-'. */ - buf[0] = '-'; - strncpy(buf + 1, cp, sizeof(buf) - 1); - buf[sizeof(buf) - 1] = 0; + char argv0[256]; - /* Execute the shell. */ - argv[0] = buf; - argv[1] = NULL; - execve(shell, argv, env); + /* Start the shell. Set initial character to '-'. */ + argv0[0] = '-'; - /* Executing the shell failed. */ + if (strlcpy(argv0 + 1, shell0, sizeof(argv0) - 1) + >= sizeof(argv0) - 1) { + errno = EINVAL; perror(shell); exit(1); + } - } else { - /* Launch login(1). */ - - execl(LOGIN_PROGRAM, "login", "-h", hostname, -#ifdef LOGIN_NEEDS_TERM - s->term? s->term : "unknown", -#endif - "-p", "-f", "--", pw->pw_name, (char *)NULL); - - /* Login couldn't be executed, die. */ + /* Execute the shell. */ + argv[0] = argv0; + argv[1] = NULL; + execve(shell, argv, env); - perror("login"); - exit(1); - } + /* Executing the shell failed. */ + perror(shell); + exit(1); } /* * Execute the command using the user's shell. This uses the -c * option to execute the command. */ - argv[0] = (char *) cp; + argv[0] = (char *) shell0; argv[1] = "-c"; argv[2] = (char *) command; argv[3] = NULL; @@ -1508,12 +1420,12 @@ session_new(void) static int did_init = 0; if (!did_init) { debug("session_new: init"); - for(i = 0; i < MAX_SESSIONS; i++) { + for (i = 0; i < MAX_SESSIONS; i++) { sessions[i].used = 0; } did_init = 1; } - for(i = 0; i < MAX_SESSIONS; i++) { + for (i = 0; i < MAX_SESSIONS; i++) { Session *s = &sessions[i]; if (! s->used) { memset(s, 0, sizeof(*s)); @@ -1533,14 +1445,14 @@ static void session_dump(void) { int i; - for(i = 0; i < MAX_SESSIONS; i++) { + for (i = 0; i < MAX_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 %ld", s->used, s->self, s, s->chanid, - s->pid); + (long)s->pid); } } @@ -1562,11 +1474,27 @@ session_open(Authctxt *authctxt, int chanid) return 1; } +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) { int i; - for(i = 0; i < MAX_SESSIONS; i++) { + for (i = 0; i < MAX_SESSIONS; i++) { Session *s = &sessions[i]; if (s->used && s->chanid == id) { debug("session_by_channel: session %d channel %d", i, id); @@ -1582,13 +1510,13 @@ static Session * session_by_pid(pid_t pid) { int i; - debug("session_by_pid: pid %d", pid); - for(i = 0; i < MAX_SESSIONS; i++) { + debug("session_by_pid: pid %ld", (long)pid); + for (i = 0; i < MAX_SESSIONS; i++) { Session *s = &sessions[i]; if (s->used && s->pid == pid) return s; } - error("session_by_pid: unknown pid %d", pid); + error("session_by_pid: unknown pid %ld", (long)pid); session_dump(); return NULL; } @@ -1600,7 +1528,7 @@ session_window_change_req(Session *s) s->row = packet_get_int(); s->xpixel = packet_get_int(); s->ypixel = packet_get_int(); - packet_done(); + packet_check_eom(); pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel); return 1; } @@ -1619,6 +1547,12 @@ session_pty_req(Session *s) packet_disconnect("Protocol error: you already have a pty."); return 0; } + /* Get the time and hostname when the user last logged in. */ + if (options.print_lastlog) { + s->hostname[0] = '\0'; + s->last_login_time = get_last_login_time(s->pw->pw_uid, + s->pw->pw_name, s->hostname, sizeof(s->hostname)); + } s->term = packet_get_string(&len); @@ -1639,7 +1573,7 @@ session_pty_req(Session *s) /* 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)))) { if (s->term) xfree(s->term); s->term = NULL; @@ -1660,12 +1594,13 @@ session_pty_req(Session *s) * time in case we call fatal() (e.g., the connection gets closed). */ fatal_add_cleanup(session_pty_cleanup, (void *)s); - pty_setowner(s->pw, s->tty); + if (!use_privsep) + pty_setowner(s->pw, s->tty); /* Set window size from the packet. */ pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel); - packet_done(); + packet_check_eom(); session_proctitle(s); return 1; } @@ -1673,25 +1608,34 @@ session_pty_req(Session *s) static int session_subsystem_req(Session *s) { + struct stat st; u_int len; int success = 0; - char *subsys = packet_get_string(&len); + char *cmd, *subsys = packet_get_string(&len); int i; - packet_done(); - log("subsystem request for %s", subsys); + packet_check_eom(); + log("subsystem request for %.100s", subsys); for (i = 0; i < options.num_subsystems; i++) { - if(strcmp(subsys, options.subsystem_name[i]) == 0) { - debug("subsystem: exec() %s", options.subsystem_command[i]); + if (strcmp(subsys, options.subsystem_name[i]) == 0) { + cmd = 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; - do_exec(s, options.subsystem_command[i]); + do_exec(s, cmd); success = 1; + break; } } if (!success) - log("subsystem request for %s failed, subsystem not found", subsys); + log("subsystem request for %.100s failed, subsystem not found", + subsys); xfree(subsys); return success; @@ -1706,7 +1650,7 @@ session_x11_req(Session *s) s->auth_proto = packet_get_string(NULL); s->auth_data = packet_get_string(NULL); s->screen = packet_get_int(); - packet_done(); + packet_check_eom(); success = session_setup_x11fwd(s); if (!success) { @@ -1721,7 +1665,7 @@ session_x11_req(Session *s) static int session_shell_req(Session *s) { - packet_done(); + packet_check_eom(); do_exec(s, NULL); return 1; } @@ -1731,7 +1675,7 @@ session_exec_req(Session *s) { u_int len; char *command = packet_get_string(&len); - packet_done(); + packet_check_eom(); do_exec(s, command); xfree(command); return 1; @@ -1741,7 +1685,7 @@ static int session_auth_agent_req(Session *s) { static int called = 0; - packet_done(); + packet_check_eom(); if (no_agent_forwarding_flag) { debug("session_auth_agent_req: no_agent_forwarding_flag"); return 0; @@ -1754,28 +1698,18 @@ session_auth_agent_req(Session *s) } } -void -session_input_channel_req(int id, void *arg) +int +session_input_channel_req(Channel *c, const char *rtype) { - u_int len; - int reply; int success = 0; - char *rtype; Session *s; - Channel *c; - rtype = packet_get_string(&len); - reply = packet_get_char(); - - 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); + if ((s = session_by_channel(c->self)) == NULL) { + log("session_input_channel_req: no session %d req %.100s", + c->self, rtype); + return 0; + } + debug("session_input_channel_req: session %d req %s", s->self, rtype); /* * a session is in LARVAL state until a shell, a command @@ -1799,14 +1733,7 @@ session_input_channel_req(int id, void *arg) if (strcmp(rtype, "window-change") == 0) { success = session_window_change_req(s); } - - if (reply) { - packet_start(success ? - SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE); - packet_put_int(c->remote_id); - packet_send(); - } - xfree(rtype); + return success; } void @@ -1823,15 +1750,16 @@ session_set_fds(Session *s, int fdin, int fdout, int fderr) channel_set_fds(s->chanid, fdout, fdin, fderr, 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). */ -static void -session_pty_cleanup(void *session) +void +session_pty_cleanup2(void *session) { Session *s = session; @@ -1846,10 +1774,11 @@ session_pty_cleanup(void *session) /* Record that the user has logged out. */ if (s->pid != 0) - record_logout(s->pid, s->tty); + record_logout(s->pid, s->tty, s->pw->pw_name); /* Release the pseudo-tty. */ - pty_release(s->tty); + if (getuid() == 0) + pty_release(s->tty); /* * Close the server side of the socket pairs. We must do this after @@ -1857,30 +1786,35 @@ session_pty_cleanup(void *session) * while we're still cleaning up. */ if (close(s->ptymaster) < 0) - error("close(s->ptymaster): %s", strerror(errno)); + error("close(s->ptymaster/%d): %s", s->ptymaster, strerror(errno)); + + /* unlink pty from session */ + s->ttyfd = -1; +} + +void +session_pty_cleanup(void *session) +{ + PRIVSEP(session_pty_cleanup2(session)); } static void session_exit_message(Session *s, int status) { Channel *c; - if (s == NULL) - fatal("session_close: no session"); - c = channel_lookup(s->chanid); - if (c == NULL) + + if ((c = channel_lookup(s->chanid)) == NULL) fatal("session_exit_message: session %d: no channel %d", s->self, s->chanid); - debug("session_exit_message: session %d channel %d pid %d", - s->self, s->chanid, s->pid); + debug("session_exit_message: session %d channel %d pid %ld", + s->self, s->chanid, (long)s->pid); 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_send(); } 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)); #ifdef WCOREDUMP packet_put_char(WCOREDUMP(status)); @@ -1909,10 +1843,10 @@ session_exit_message(Session *s, int status) s->chanid = -1; } -static void +void session_close(Session *s) { - debug("session_close: session %d pid %d", s->self, s->pid); + debug("session_close: session %d pid %ld", s->self, (long)s->pid); if (s->ttyfd != -1) { fatal_remove_cleanup(session_pty_cleanup, (void *)s); session_pty_cleanup(s); @@ -1921,6 +1855,8 @@ session_close(Session *s) xfree(s->term); if (s->display) xfree(s->display); + if (s->auth_display) + xfree(s->auth_display); if (s->auth_data) xfree(s->auth_data); if (s->auth_proto) @@ -1934,7 +1870,8 @@ session_close_by_pid(pid_t pid, int status) { Session *s = session_by_pid(pid); if (s == NULL) { - debug("session_close_by_pid: no session for pid %d", pid); + debug("session_close_by_pid: no session for pid %ld", + (long)pid); return; } if (s->chanid != -1) @@ -1942,22 +1879,6 @@ session_close_by_pid(pid_t pid, int status) session_close(s); } -int -session_have_children(void) -{ - int i; - - for(i = 0; i < MAX_SESSIONS; i++) { - Session *s = &sessions[i]; - if (s->used && s->pid != -1) { - debug("session_have_children: id %d pid %d", i, s->pid); - return 1; - } - } - debug("session_have_children: no more children"); - return 0; -} - /* * this is called when a channel dies before * the session 'child' itself dies @@ -1967,22 +1888,41 @@ session_close_by_channel(int id, void *arg) { Session *s = session_by_channel(id); if (s == NULL) { - debug("session_close_by_channel: no session for channel %d", id); + debug("session_close_by_channel: no session for id %d", id); return; } - /* disconnect channel */ + debug("session_close_by_channel: channel %d child %ld", + id, (long)s->pid); + 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); s->chanid = -1; + session_close(s); +} - debug("session_close_by_channel: channel %d kill %d", id, s->pid); - if (s->pid == 0) { - /* close session immediately */ - session_close(s); - } else { - /* notify child, delay session cleanup */ - if (kill(s->pid, (s->ttyfd == -1) ? SIGTERM : SIGHUP) < 0) - error("session_close_by_channel: kill %d: %s", - s->pid, strerror(errno)); +void +session_destroy_all(void (*closefunc)(Session *)) +{ + int i; + for (i = 0; i < MAX_SESSIONS; i++) { + Session *s = &sessions[i]; + if (s->used) { + if (closefunc != NULL) + closefunc(s); + else + session_close(s); + } } } @@ -1992,7 +1932,7 @@ session_tty_list(void) static char buf[1024]; int i; buf[0] = '\0'; - for(i = 0; i < MAX_SESSIONS; i++) { + for (i = 0; i < MAX_SESSIONS; i++) { Session *s = &sessions[i]; if (s->used && s->ttyfd != -1) { if (buf[0] != '\0') @@ -2018,6 +1958,8 @@ int 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."); @@ -2041,11 +1983,50 @@ session_setup_x11fwd(Session *s) debug("X11 display already set."); return 0; } - s->display = x11_create_display_inet(s->screen, options.x11_display_offset); - if (s->display == NULL) { + if (x11_create_display_inet(options.x11_display_offset, + options.x11_use_localhost, s->single_connection, + &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:%u.%u", + s->display_number, s->screen); + snprintf(auth_display, sizeof auth_display, "unix:%u.%u", + s->display_number, s->screen); + s->display = xstrdup(display); + s->auth_display = xstrdup(auth_display); + } else { +#ifdef IPADDR_IN_DISPLAY + struct hostent *he; + struct in_addr my_addr; + + he = gethostbyname(hostname); + if (he == NULL) { + error("Can't get IP address for X11 DISPLAY."); + packet_send_debug("Can't get IP address for X11 DISPLAY."); + return 0; + } + memcpy(&my_addr, he->h_addr_list[0], sizeof(struct in_addr)); + snprintf(display, sizeof display, "%.50s:%u.%u", inet_ntoa(my_addr), + s->display_number, s->screen); +#else + snprintf(display, sizeof display, "%.400s:%u.%u", hostname, + s->display_number, s->screen); +#endif + s->display = xstrdup(display); + s->auth_display = xstrdup(display); + } + return 1; }