X-Git-Url: http://andersk.mit.edu/gitweb/openssh.git/blobdiff_plain/ced49be2dc7d39a30309a74acc3c95cbd1d930a5..fe661d8f9f98e269ff5efb3a79918ed25cbe6ee7:/session.c diff --git a/session.c b/session.c index 5a6afa7e..ac156175 100644 --- a/session.c +++ b/session.c @@ -33,7 +33,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: session.c,v 1.96 2001/06/26 16:15:24 dugsong Exp $"); +RCSID("$OpenBSD: session.c,v 1.151 2002/12/04 04:36:47 stevesk Exp $"); #include "ssh.h" #include "ssh1.h" @@ -56,20 +56,7 @@ RCSID("$OpenBSD: session.c,v 1.96 2001/06/26 16:15:24 dugsong 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,65 +64,27 @@ RCSID("$OpenBSD: session.c,v 1.96 2001/06/26 16:15:24 dugsong 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_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 *); -void do_child(Session *, const char *); +void session_set_fds(Session *, int, int, int); +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 *); static void do_authenticated1(Authctxt *); static void do_authenticated2(Authctxt *); - -static void session_close(Session *); + static int session_pty_req(Session *); /* import */ @@ -155,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) { @@ -175,18 +210,7 @@ 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 + /* setup the channel layer */ if (!no_port_forwarding_flag && options.allow_tcp_forwarding) channel_permit_all_opens(); @@ -197,7 +221,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) @@ -220,9 +244,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; @@ -236,16 +260,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. */ @@ -273,7 +301,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); @@ -310,22 +338,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 @@ -343,7 +371,7 @@ do_authenticated1(Authctxt *authctxt) } break; #endif /* AFS || KRB5 */ - + #ifdef AFS case SSH_CMSG_HAVE_AFS_TOKEN: if (!options.afs_token_passing || !k_hasafs()) { @@ -351,8 +379,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 @@ -373,7 +401,7 @@ do_authenticated1(Authctxt *authctxt) } else { do_exec(s, NULL); } - packet_done(); + packet_check_eom(); session_close(s); return; @@ -404,7 +432,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]; @@ -428,10 +456,15 @@ 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. */ if ((pid = fork()) == 0) { + fatal_remove_all_cleanups(); + /* Child. Reinitialize the log since the pid has changed. */ log_init(__progname, options.log_level, options.log_facility, log_stderr); @@ -479,10 +512,17 @@ do_exec_no_pty(Session *s, const char *command) perror("dup2 stderr"); #endif /* USE_PIPES */ +#ifdef _UNICOS + cray_init_job(s->pw); /* set up cray jid and tmpdir */ +#endif + /* Do processing for the child (exec command etc). */ do_child(s, command); /* NOTREACHED */ } +#ifdef _UNICOS + signal(WJSIGNAL, cray_job_termination_handler); +#endif /* _UNICOS */ #ifdef HAVE_CYGWIN if (is_winnt) cygwin_set_impersonation_token(INVALID_HANDLE_VALUE); @@ -547,34 +587,35 @@ do_exec_pty(Session *s, const char *command) /* Fork the child. */ if ((pid = fork()) == 0) { + fatal_remove_all_cleanups(); + /* Child. Reinitialize the log because the pid has changed. */ log_init(__progname, options.log_level, options.log_facility, log_stderr); - /* Close the master side of the pseudo tty. */ close(ptyfd); /* 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); /* record login, etc. similar to login(1) */ #ifndef HAVE_OSF_SIA - if (!(options.use_login && command == NULL)) + if (!(options.use_login && command == NULL)) { +#ifdef _UNICOS + cray_init_job(s->pw); /* set up cray jid and tmpdir */ +#endif /* _UNICOS */ do_login(s, command); + } # ifdef LOGIN_NEEDS_UTMPX else do_pre_login(s); @@ -585,6 +626,9 @@ do_exec_pty(Session *s, const char *command) do_child(s, command); /* NOTREACHED */ } +#ifdef _UNICOS + signal(WJSIGNAL, cray_job_termination_handler); +#endif /* _UNICOS */ #ifdef HAVE_CYGWIN if (is_winnt) cygwin_set_impersonation_token(INVALID_HANDLE_VALUE); @@ -622,7 +666,7 @@ do_exec_pty(Session *s, const char *command) } #ifdef LOGIN_NEEDS_UTMPX -void +static void do_pre_login(Session *s) { socklen_t fromlen; @@ -634,17 +678,17 @@ do_pre_login(Session *s) * the address be 0.0.0.0. */ memset(&from, 0, sizeof(from)); + fromlen = sizeof(from); 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 @@ -670,39 +714,14 @@ do_exec(Session *s, const char *command) original_command = NULL; } -/* - * Check for quiet login, either .hushlogin or command given. - */ -static int -check_quietlogin(Session *s, const char *command) -{ - char buf[256]; - struct passwd *pw = s->pw; - struct stat st; - - /* Return 1 if .hushlogin exists or a command given. */ - if (command != NULL) - return 1; - snprintf(buf, sizeof(buf), "%.200s/.hushlogin", pw->pw_dir); -#ifdef HAVE_LOGIN_CAP - if (login_getcapbool(lc, "hushlogin", 0) || stat(buf, &st) >= 0) - return 1; -#else - if (stat(buf, &st) >= 0) - return 1; -#endif - return 0; -} /* 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(); @@ -714,23 +733,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, fromlen); #ifdef USE_PAM /* @@ -755,15 +769,18 @@ 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); +#ifndef NO_SSH_LASTLOG + if (options.print_lastlog && s->last_login_time != 0) { + time_string = ctime(&s->last_login_time); if (strchr(time_string, '\n')) *strchr(time_string, '\n') = 0; - if (strcmp(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); } +#endif /* NO_SSH_LASTLOG */ do_motd(); } @@ -792,13 +809,38 @@ do_motd(void) } } + +/* + * Check for quiet login, either .hushlogin or command given. + */ +int +check_quietlogin(Session *s, const char *command) +{ + char buf[256]; + struct passwd *pw = s->pw; + struct stat st; + + /* Return 1 if .hushlogin exists or a command given. */ + if (command != NULL) + return 1; + snprintf(buf, sizeof(buf), "%.200s/.hushlogin", pw->pw_dir); +#ifdef HAVE_LOGIN_CAP + if (login_getcapbool(lc, "hushlogin", 0) || stat(buf, &st) >= 0) + return 1; +#else + if (stat(buf, &st) >= 0) + return 1; +#endif + return 0; +} + /* * Sets the value of the given variable in the environment. If the variable * already exists, its value is overriden. */ 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; @@ -819,6 +861,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 *)); } @@ -839,17 +884,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') @@ -858,7 +906,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; } /* @@ -872,337 +921,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 */ - -#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)); + *var_val++ = '\0'; - 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(USE_PAM) && !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 /* USE_PAM || 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 - -#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 */ + struct passwd *pw = s->pw; /* Initialize the environment. */ envsize = 100; @@ -1214,7 +962,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,8 +971,10 @@ do_child(Session *s, const char *command) child_set_env(&env, &envsize, "LOGNAME", pw->pw_name); child_set_env(&env, &envsize, "HOME", pw->pw_dir); #ifdef HAVE_LOGIN_CAP - (void) setusercontext(lc, pw, pw->pw_uid, LOGIN_SETPATH); - child_set_env(&env, &envsize, "PATH", getenv("PATH")); + if (setusercontext(lc, pw, pw->pw_uid, LOGIN_SETPATH) < 0) + child_set_env(&env, &envsize, "PATH", _PATH_STDPATH); + else + child_set_env(&env, &envsize, "PATH", getenv("PATH")); #else /* HAVE_LOGIN_CAP */ # ifndef HAVE_CYGWIN /* @@ -1233,7 +983,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 */ @@ -1248,24 +1003,33 @@ 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 *str = ce->s; + + for (i = 0; str[i] != '=' && str[i]; i++) + ; + if (str[i] == '=') { + str[i] = 0; + child_set_env(&env, &envsize, str, str + i + 1); + } + custom_environment = ce->next; + xfree(ce->s); + xfree(ce); } - custom_environment = ce->next; - xfree(ce->s); - xfree(ce); } + /* SSH_CLIENT deprecated */ 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); + snprintf(buf, sizeof buf, "%.50s %d %.50s %d", + get_remote_ipaddr(), get_remote_port(), + get_local_ipaddr(packet_get_connection_in()), get_local_port()); + child_set_env(&env, &envsize, "SSH_CONNECTION", buf); + if (s->ttyfd != -1) child_set_env(&env, &envsize, "SSH_TTY", s->tty); if (s->term) @@ -1276,12 +1040,21 @@ do_child(Session *s, const char *command) child_set_env(&env, &envsize, "SSH_ORIGINAL_COMMAND", original_command); +#ifdef _UNICOS + if (cray_tmpdir[0] != '\0') + child_set_env(&env, &envsize, "TMPDIR", cray_tmpdir); +#endif /* _UNICOS */ + #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) @@ -1294,18 +1067,27 @@ do_child(Session *s, const char *command) s->authctxt->krb5_ticket_file); #endif #ifdef USE_PAM - /* Pull in any environment variables that may have been set by PAM. */ - do_pam_environment(&env, &envsize); + /* + * Pull in any environment variables that may have + * been set by PAM. + */ + { + char **p; + + p = fetch_pam_environment(); + copy_environment(p, &env, &envsize); + free_pam_environment(p); + } #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) { + if (options.permit_user_env && !options.use_login) { snprintf(buf, sizeof buf, "%.200s/.ssh/environment", - pw->pw_dir); + strcmp(pw->pw_dir, "/") ? pw->pw_dir : ""); read_environment_file(&env, &envsize, buf); } if (debug_flag) { @@ -1314,10 +1096,244 @@ 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 remove %.100s\n", + options.xauth_location, s->auth_display); + fprintf(stderr, + "%.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, "remove %s\n", + s->auth_display); + 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. */ + log("User %.100s not allowed because %s exists", + pw->pw_name, _PATH_NOLOGIN); + 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) +{ +#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 + aix_usrinfo(pw); +# 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; + +#ifdef _UNICOS + cray_setup(pw->pw_uid, pw->pw_name, command); +#endif /* _UNICOS */ + + /* + * 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 @@ -1353,163 +1369,83 @@ do_child(Session *s, const char *command) for (i = 3; i < 64; i++) close(i); + /* + * Must take new environment into use so that .ssh/rc, + * /etc/ssh/sshrc and xauth are run in the proper environment. + */ + environ = env; + +#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. */ if (chdir(pw->pw_dir) < 0) { 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 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. - */ - environ = env; - - /* - * 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; - } + 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]; - - /* - * 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; - - mailbox = getenv("MAIL"); - if (mailbox != NULL) { - 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; + 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, 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; @@ -1525,12 +1461,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)); @@ -1550,19 +1486,19 @@ 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); } } int -session_open(int chanid) +session_open(Authctxt *authctxt, int chanid) { Session *s = session_new(); debug("session_open: channel %d", chanid); @@ -1570,7 +1506,8 @@ session_open(int chanid) error("no more sessions"); return 0; } - s->pw = auth_get_user(); + s->authctxt = authctxt; + s->pw = authctxt->pw; if (s->pw == NULL) fatal("no user for session %d", s->self); debug("session_open: session %d: link with channel %d", s->self, chanid); @@ -1578,11 +1515,27 @@ session_open(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); @@ -1598,13 +1551,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; } @@ -1616,7 +1569,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; } @@ -1635,6 +1588,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); @@ -1655,7 +1614,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; @@ -1676,12 +1635,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; } @@ -1689,25 +1649,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_no_pty(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; @@ -1722,7 +1691,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) { @@ -1737,7 +1706,7 @@ session_x11_req(Session *s) static int session_shell_req(Session *s) { - packet_done(); + packet_check_eom(); do_exec(s, NULL); return 1; } @@ -1747,7 +1716,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; @@ -1757,7 +1726,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; @@ -1770,28 +1739,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 @@ -1815,14 +1774,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 @@ -1839,15 +1791,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; @@ -1862,10 +1815,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 @@ -1873,31 +1827,57 @@ 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 char * +sig2name(int sig) +{ +#define SSH_SIG(x) if (sig == SIG ## x) return #x + SSH_SIG(ABRT); + SSH_SIG(ALRM); + SSH_SIG(FPE); + SSH_SIG(HUP); + SSH_SIG(ILL); + SSH_SIG(INT); + SSH_SIG(KILL); + SSH_SIG(PIPE); + SSH_SIG(QUIT); + SSH_SIG(SEGV); + SSH_SIG(TERM); + SSH_SIG(USR1); + SSH_SIG(USR2); +#undef SSH_SIG + return "SIG@openssh.com"; } 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); - packet_put_int(WTERMSIG(status)); + channel_request_start(s->chanid, "exit-signal", 0); + packet_put_cstring(sig2name(WTERMSIG(status))); #ifdef WCOREDUMP packet_put_char(WCOREDUMP(status)); #else /* WCOREDUMP */ @@ -1925,10 +1905,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); @@ -1937,6 +1917,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) @@ -1950,7 +1932,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) @@ -1967,22 +1950,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 +1994,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 +2020,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,16 +2045,55 @@ 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; } static void do_authenticated2(Authctxt *authctxt) { - server_loop2(); + server_loop2(authctxt); }