X-Git-Url: http://andersk.mit.edu/gitweb/openssh.git/blobdiff_plain/782209449ae4c917630b865d750d495d3c448f59..1836f69f0538fd8f9ecdad854e2fd6ed9990546f:/session.c diff --git a/session.c b/session.c index 35e49c74..29467029 100644 --- a/session.c +++ b/session.c @@ -9,7 +9,7 @@ * called by a name other than "ssh" or "Secure Shell". * * SSH2 support by Markus Friedl. - * Copyright (c) 2000 Markus Friedl. All rights reserved. + * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,7 +33,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: session.c,v 1.94 2001/06/23 15:12:20 itojun Exp $"); +RCSID("$OpenBSD: session.c,v 1.129 2002/03/18 03:41:08 provos Exp $"); #include "ssh.h" #include "ssh1.h" @@ -57,41 +57,12 @@ RCSID("$OpenBSD: session.c,v 1.94 2001/06/23 15:12:20 itojun Exp $"); #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 - #ifdef HAVE_CYGWIN #include #include #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 @@ -99,7 +70,8 @@ typedef struct Session Session; struct Session { int used; int self; - struct passwd *pw; + struct passwd *pw; + Authctxt *authctxt; pid_t pid; /* tty */ char *term; @@ -107,8 +79,10 @@ struct Session { int row, col, xpixel, ypixel; char tty[TTYSZ]; /* X11 */ + int display_number; char *display; int screen; + char *auth_display; char *auth_proto; char *auth_data; int single_connection; @@ -120,20 +94,24 @@ struct Session { /* 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); +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 *); +#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 *); @@ -154,12 +132,11 @@ 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 void @@ -174,18 +151,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,6 +171,14 @@ do_authenticated(Authctxt *authctxt) /* remove agent socket */ if (auth_get_socket_name()) auth_sock_cleanup_proc(authctxt->pw); +#ifdef KRB4 + if (options.kerberos_ticket_cleanup) + krb4_cleanup_proc(authctxt); +#endif +#ifdef KRB5 + if (options.kerberos_ticket_cleanup) + krb5_cleanup_proc(authctxt); +#endif } /* @@ -211,11 +192,12 @@ do_authenticated1(Authctxt *authctxt) { Session *s; char *command; - int success, type, plen, screen_flag; + int success, type, screen_flag; int compression_level = 0, enable_compression_after_reply = 0; u_int proto_len, data_len, dlen; s = session_new(); + s->authctxt = authctxt; s->pw = authctxt->pw; /* @@ -226,16 +208,16 @@ 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; } /* Enable compression after we have responded with SUCCESS. */ @@ -263,7 +245,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); @@ -301,6 +283,58 @@ do_authenticated1(Authctxt *authctxt) 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_check_eom(); + + /* XXX - 0x41, see creds_to_radix version */ + if (kdata[0] != 0x41) { +#ifdef KRB5 + krb5_data tgt; + tgt.data = kdata; + tgt.length = dlen; + + if (auth_krb5_tgt(s->authctxt, &tgt)) + success = 1; + else + verbose("Kerberos v5 TGT refused for %.100s", s->authctxt->user); +#endif /* KRB5 */ + } else { +#ifdef AFS + if (auth_krb4_tgt(s->authctxt, kdata)) + success = 1; + else + verbose("Kerberos v4 TGT refused for %.100s", s->authctxt->user); +#endif /* AFS */ + } + xfree(kdata); + } + break; +#endif /* AFS || KRB5 */ + +#ifdef AFS + case SSH_CMSG_HAVE_AFS_TOKEN: + if (!options.afs_token_passing || !k_hasafs()) { + verbose("AFS token passing disabled."); + } else { + /* Accept AFS token. */ + char *token = packet_get_string(&dlen); + packet_check_eom(); + + if (auth_afs_token(s->authctxt, token)) + success = 1; + else + verbose("AFS token refused for %.100s", + s->authctxt->user); + xfree(token); + } + break; +#endif /* AFS */ + case SSH_CMSG_EXEC_SHELL: case SSH_CMSG_EXEC_CMD: if (type == SSH_CMSG_EXEC_CMD) { @@ -311,7 +345,7 @@ do_authenticated1(Authctxt *authctxt) } else { do_exec(s, NULL); } - packet_done(); + packet_check_eom(); session_close(s); return; @@ -366,6 +400,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. */ @@ -485,26 +522,22 @@ do_exec_pty(Session *s, const char *command) /* Fork the child. */ if ((pid = fork()) == 0) { + /* 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); @@ -560,7 +593,7 @@ do_exec_pty(Session *s, const char *command) } #ifdef LOGIN_NEEDS_UTMPX -void +static void do_pre_login(Session *s) { socklen_t fromlen; @@ -575,14 +608,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 @@ -608,29 +641,6 @@ 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 @@ -652,7 +662,7 @@ 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(); } @@ -667,7 +677,7 @@ do_login(Session *s, const char *command) /* 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), + get_remote_name_or_ip(utmp_len, options.verify_reverse_mapping), (struct sockaddr *)&from); #ifdef USE_PAM @@ -730,13 +740,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; @@ -777,7 +812,7 @@ 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]; @@ -810,337 +845,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, 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, 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(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; @@ -1152,7 +886,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) { @@ -1186,22 +920,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) @@ -1215,30 +952,34 @@ 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"); -#endif - -#ifdef KRB4 { - extern char *ticket; + char *cp; - if (ticket) - child_set_env(&env, &envsize, "KRBTKFILE", ticket); + 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 /* KRB4 */ - +#endif +#ifdef KRB4 + if (s->authctxt->krb4_ticket_file) + child_set_env(&env, &envsize, "KRBTKFILE", + s->authctxt->krb4_ticket_file); +#endif +#ifdef KRB5 + if (s->authctxt->krb5_ticket_file) + child_set_env(&env, &envsize, "KRB5CCNAME", + 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); + copy_environment(fetch_pam_environment(), &env, &envsize); #endif /* USE_PAM */ if (auth_get_socket_name() != NULL) child_set_env(&env, &envsize, SSH_AUTHSOCKET_ENV_NAME, - auth_get_socket_name()); + auth_get_socket_name()); /* read $HOME/.ssh/environment. */ if (!options.use_login) { @@ -1252,10 +993,210 @@ 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 %.100s add " + "%.100s %.100s %.100s\n", + options.xauth_location, s->auth_display, + s->auth_proto, s->auth_data); + } + snprintf(cmd, sizeof cmd, "%s -q -", + options.xauth_location); + f = popen(cmd, "w"); + if (f) { + fprintf(f, "add %s %s %s\n", + s->auth_display, s->auth_proto, + s->auth_data); + pclose(f); + } else { + fprintf(stderr, "Could not run %s\n", + cmd); + } + } +} + +static void +do_nologin(struct passwd *pw) +{ + FILE *f = NULL; + char buf[1024]; + +#ifdef HAVE_LOGIN_CAP + if (!login_getcapbool(lc, "ignorenologin", 0) && pw->pw_uid) + f = fopen(login_getcapstr(lc, "nologin", _PATH_NOLOGIN, + _PATH_NOLOGIN), "r"); +#else + if (pw->pw_uid) + f = fopen(_PATH_NOLOGIN, "r"); +#endif + if (f) { + /* /etc/nologin exists. Print its contents and exit. */ + while (fgets(buf, sizeof(buf), f)) + fputs(buf, stderr); + fclose(f); + exit(254); + } +} + +/* Set login name, uid, gid, and groups. */ +static 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_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 +# 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) */ + /* 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); +} + +/* + * 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); +# ifdef _AIX + aix_usrinfo(pw, s->tty, s->ttyfd); +# endif /* _AIX */ + 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 @@ -1291,163 +1232,93 @@ 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(1). */ + + execl(LOGIN_PROGRAM, "login", "-h", hostname, +#ifdef LOGIN_NEEDS_TERM + (s->term ? s->term : "unknown"), +#endif /* LOGIN_NEEDS_TERM */ + "-p", "-f", "--", pw->pw_name, (char *)NULL); + + /* Login couldn't be executed, die. */ + + perror("login"); + exit(1); + } + + /* 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]; + char argv0[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; - - /* 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; @@ -1463,12 +1334,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)); @@ -1488,7 +1359,7 @@ 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", s->used, @@ -1500,7 +1371,7 @@ session_dump(void) } int -session_open(int chanid) +session_open(Authctxt *authctxt, int chanid) { Session *s = session_new(); debug("session_open: channel %d", chanid); @@ -1508,7 +1379,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); @@ -1520,7 +1392,7 @@ 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); @@ -1537,7 +1409,7 @@ session_by_pid(pid_t pid) { int i; debug("session_by_pid: pid %d", pid); - for(i = 0; i < MAX_SESSIONS; i++) { + for (i = 0; i < MAX_SESSIONS; i++) { Session *s = &sessions[i]; if (s->used && s->pid == pid) return s; @@ -1554,7 +1426,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,7 +1491,7 @@ session_pty_req(Session *s) /* 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; } @@ -1627,25 +1499,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; @@ -1660,7 +1541,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) { @@ -1675,7 +1556,7 @@ session_x11_req(Session *s) static int session_shell_req(Session *s) { - packet_done(); + packet_check_eom(); do_exec(s, NULL); return 1; } @@ -1685,7 +1566,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; @@ -1695,7 +1576,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; @@ -1708,28 +1589,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 @@ -1753,14 +1624,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 @@ -1777,7 +1641,8 @@ 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); } /* @@ -1800,7 +1665,7 @@ 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); @@ -1812,29 +1677,28 @@ session_pty_cleanup(void *session) */ if (close(s->ptymaster) < 0) error("close(s->ptymaster): %s", strerror(errno)); + + /* unlink pty from session */ + s->ttyfd = -1; } 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); 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)); @@ -1875,6 +1739,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) @@ -1905,22 +1771,36 @@ 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 %d", id, 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) +{ + int i; + for (i = 0; i < MAX_SESSIONS; i++) { + Session *s = &sessions[i]; + if (s->used) + session_close(s); } } @@ -1930,7 +1810,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') @@ -1956,6 +1836,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."); @@ -1979,16 +1861,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) { + s->display_number = x11_create_display_inet(options.x11_display_offset, + options.x11_use_localhost, s->single_connection); + if (s->display_number == -1) { debug("x11_create_display_inet failed."); return 0; } + + /* Set up a suitable value for the DISPLAY variable. */ + if (gethostname(hostname, sizeof(hostname)) < 0) + fatal("gethostname: %.100s", strerror(errno)); + /* + * auth_display must be used as the displayname when the + * authorization entry is added with xauth(1). This will be + * different than the DISPLAY string for localhost displays. + */ + if (options.x11_use_localhost) { + snprintf(display, sizeof display, "localhost:%d.%d", + s->display_number, s->screen); + snprintf(auth_display, sizeof auth_display, "unix:%d.%d", + s->display_number, s->screen); + s->display = xstrdup(display); + s->auth_display = xstrdup(auth_display); + } else { +#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:%d.%d", inet_ntoa(my_addr), + s->display_number, s->screen); +#else + snprintf(display, sizeof display, "%.400s:%d.%d", 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); }