=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/ssh/session.c,v retrieving revision 1.108.2.7 retrieving revision 1.109 diff -u -r1.108.2.7 -r1.109 --- src/usr.bin/ssh/session.c 2002/10/11 14:53:07 1.108.2.7 +++ src/usr.bin/ssh/session.c 2001/11/29 21:10:51 1.109 @@ -33,7 +33,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: session.c,v 1.108.2.7 2002/10/11 14:53:07 miod Exp $"); +RCSID("$OpenBSD: session.c,v 1.109 2001/11/29 21:10:51 stevesk Exp $"); #include "ssh.h" #include "ssh1.h" @@ -56,13 +56,40 @@ #include "serverloop.h" #include "canohost.h" #include "session.h" -#include "monitor_wrap.h" +/* types */ + +#define TTYSZ 64 +typedef struct Session Session; +struct Session { + int used; + int self; + struct passwd *pw; + Authctxt *authctxt; + pid_t pid; + /* tty */ + char *term; + int ptyfd, ttyfd, ptymaster; + 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; + /* proto 2 */ + int chanid; + int is_subsystem; +}; + /* func */ Session *session_new(void); void session_set_fds(Session *, int, int, int); -void session_pty_cleanup(void *); +static void session_pty_cleanup(void *); void session_proctitle(Session *); int session_setup_x11fwd(Session *); void do_exec_pty(Session *, const char *); @@ -76,6 +103,7 @@ static void do_authenticated1(Authctxt *); static void do_authenticated2(Authctxt *); +static void session_close(Session *); static int session_pty_req(Session *); /* import */ @@ -95,96 +123,9 @@ Session sessions[MAX_SESSIONS]; #ifdef HAVE_LOGIN_CAP -login_cap_t *lc; +static 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) { @@ -197,6 +138,18 @@ close(startup_pipe); startup_pipe = -1; } +#ifdef HAVE_LOGIN_CAP + if ((lc = login_getclass(authctxt->pw->pw_class)) == NULL) { + error("unable to get login class"); + return; + } +#ifdef BSD_AUTH + if (auth_approval(NULL, lc, authctxt->pw->pw_name, "ssh") <= 0) { + packet_disconnect("Approval failure for %s", + authctxt->pw->pw_name); + } +#endif +#endif /* setup the channel layer */ if (!no_port_forwarding_flag && options.allow_tcp_forwarding) channel_permit_all_opens(); @@ -207,7 +160,7 @@ do_authenticated1(authctxt); /* remove agent socket */ - if (auth_sock_name != NULL) + if (auth_get_socket_name()) auth_sock_cleanup_proc(authctxt->pw); #ifdef KRB4 if (options.kerberos_ticket_cleanup) @@ -230,9 +183,9 @@ { Session *s; char *command; - int success, type, screen_flag; - int enable_compression_after_reply = 0; - u_int proto_len, data_len, dlen, compression_level = 0; + int success, type, plen, screen_flag; + int compression_level = 0, enable_compression_after_reply = 0; + u_int proto_len, data_len, dlen; s = session_new(); s->authctxt = authctxt; @@ -246,22 +199,18 @@ success = 0; /* Get a packet from the client. */ - type = packet_read(); + type = packet_read(&plen); /* 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. */ enable_compression_after_reply = 1; success = 1; @@ -287,7 +236,7 @@ } else { s->screen = 0; } - packet_check_eom(); + packet_done(); success = session_setup_x11fwd(s); if (!success) { xfree(s->auth_proto); @@ -324,22 +273,22 @@ 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_check_eom(); - + packet_integrity_check(plen, 4 + dlen, type); + /* 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 @@ -357,7 +306,7 @@ } break; #endif /* AFS || KRB5 */ - + #ifdef AFS case SSH_CMSG_HAVE_AFS_TOKEN: if (!options.afs_token_passing || !k_hasafs()) { @@ -365,8 +314,8 @@ } else { /* Accept AFS token. */ char *token = packet_get_string(&dlen); - packet_check_eom(); - + packet_integrity_check(plen, 4 + dlen, type); + if (auth_afs_token(s->authctxt, token)) success = 1; else @@ -387,7 +336,7 @@ } else { do_exec(s, NULL); } - packet_check_eom(); + packet_done(); session_close(s); return; @@ -418,7 +367,7 @@ void do_exec_no_pty(Session *s, const char *command) { - pid_t pid; + int pid; #ifdef USE_PIPES int pin[2], pout[2], perr[2]; @@ -441,8 +390,6 @@ /* 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); @@ -549,7 +496,6 @@ /* 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); @@ -637,8 +583,10 @@ 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(); @@ -647,34 +595,38 @@ * 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(); } } + /* 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. */ - 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); + 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 (check_quietlogin(s, command)) return; - if (options.print_lastlog && s->last_login_time != 0) { - time_string = ctime(&s->last_login_time); + if (options.print_lastlog && last_login_time != 0) { + time_string = ctime(&last_login_time); if (strchr(time_string, '\n')) *strchr(time_string, '\n') = 0; - if (strcmp(s->hostname, "") == 0) + if (strcmp(hostname, "") == 0) printf("Last login: %s\r\n", time_string); else - printf("Last login: %s from %s\r\n", time_string, - s->hostname); + printf("Last login: %s from %s\r\n", time_string, hostname); } do_motd(); @@ -735,7 +687,7 @@ */ 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; @@ -756,9 +708,6 @@ } 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 *)); } @@ -779,20 +728,17 @@ */ 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') @@ -801,8 +747,7 @@ *strchr(cp, '\n') = '\0'; value = strchr(cp, '='); if (value == NULL) { - fprintf(stderr, "Bad line %u in %.100s\n", lineno, - filename); + fprintf(stderr, "Bad line in %.100s: %.200s\n", filename, buf); continue; } /* @@ -816,14 +761,94 @@ fclose(f); } -static char ** -do_setup_env(Session *s, const char *shell) +/* + * Performs common processing for the child, such as setting up the + * environment, closing extra file descriptors, setting the user and group + * ids, and executing the command or shell. + */ +void +do_child(Session *s, const char *command) { + const char *shell, *hostname = NULL, *cp = NULL; + struct passwd *pw = s->pw; char buf[256]; - u_int i, envsize; + char cmd[1024]; + FILE *f = NULL; + u_int envsize, i; char **env; - struct passwd *pw = s->pw; + extern char **environ; + struct stat st; + char *argv[10]; + int do_xauth; + 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 (!options.use_login) { +#ifdef HAVE_LOGIN_CAP + if (!login_getcapbool(lc, "ignorenologin", 0) && pw->pw_uid) + f = fopen(login_getcapstr(lc, "nologin", _PATH_NOLOGIN, + _PATH_NOLOGIN), "r"); +#else + if (pw->pw_uid) + f = fopen(_PATH_NOLOGIN, "r"); +#endif + if (f) { + /* /etc/nologin exists. Print its contents and exit. */ + while (fgets(buf, sizeof(buf), f)) + fputs(buf, stderr); + fclose(f); + exit(254); + } + } + /* Set login name, uid, gid, and groups. */ + /* Login(1) does this as well, and it needs uid 0 for the "-h" + switch, so we let login(1) to this for us. */ + if (!options.use_login) { + if (getuid() == 0 || geteuid() == 0) { +#ifdef HAVE_LOGIN_CAP + if (setusercontext(lc, pw, pw->pw_uid, + (LOGIN_SETALL & ~LOGIN_SETPATH)) < 0) { + perror("unable to set user context"); + exit(1); + } +#else + if (setlogin(pw->pw_name) < 0) + error("setlogin failed: %s", strerror(errno)); + if (setgid(pw->pw_gid) < 0) { + perror("setgid"); + exit(1); + } + /* Initialize the group list. */ + if (initgroups(pw->pw_name, pw->pw_gid) < 0) { + perror("initgroups"); + exit(1); + } + endgrent(); + + /* Permanently switch to the desired uid. */ + permanently_set_uid(pw); +#endif + } + if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid) + fatal("Failed to set uids to %u.", (u_int) pw->pw_uid); + } + /* + * Get the shell from the password data. An empty shell field is + * legal, and means /bin/sh. + */ + shell = (pw->pw_shell[0] == '\0') ? _PATH_BSHELL : pw->pw_shell; +#ifdef HAVE_LOGIN_CAP + shell = login_getcapstr(lc, "shell", (char *)shell, (char *)shell); +#endif + /* Initialize the environment. */ envsize = 100; env = xmalloc(envsize * sizeof(char *)); @@ -835,10 +860,8 @@ child_set_env(&env, &envsize, "LOGNAME", pw->pw_name); child_set_env(&env, &envsize, "HOME", pw->pw_dir); #ifdef HAVE_LOGIN_CAP - 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")); + (void) setusercontext(lc, pw, pw->pw_uid, LOGIN_SETPATH); + child_set_env(&env, &envsize, "PATH", getenv("PATH")); #else child_set_env(&env, &envsize, "PATH", _PATH_STDPATH); #endif @@ -854,33 +877,24 @@ child_set_env(&env, &envsize, "TZ", getenv("TZ")); /* Set custom environment options from RSA authentication. */ - 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); + 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); } + 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) @@ -893,19 +907,19 @@ #ifdef KRB4 if (s->authctxt->krb4_ticket_file) child_set_env(&env, &envsize, "KRBTKFILE", - s->authctxt->krb4_ticket_file); + 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); + s->authctxt->krb5_ticket_file); #endif - if (auth_sock_name != NULL) + if (auth_get_socket_name() != NULL) child_set_env(&env, &envsize, SSH_AUTHSOCKET_ENV_NAME, - auth_sock_name); + auth_get_socket_name()); /* read $HOME/.ssh/environment. */ - if (options.permit_user_env && !options.use_login) { + if (!options.use_login) { snprintf(buf, sizeof buf, "%.200s/.ssh/environment", pw->pw_dir); read_environment_file(&env, &envsize, buf); @@ -916,194 +930,10 @@ for (i = 0; env[i]; i++) fprintf(stderr, " %.200s\n", env[i]); } - return env; -} - -/* - * Run $HOME/.ssh/rc, /etc/sshrc, or xauth (whichever is found - * first in this order). - */ -static void -do_rc_files(Session *s, const char *shell) -{ - FILE *f = NULL; - char cmd[1024]; - int do_xauth; - struct stat st; - - do_xauth = - s->display != NULL && s->auth_proto != NULL && s->auth_data != NULL; - - /* ignore _PATH_SSH_USER_RC for subsystems */ - if (!s->is_subsystem && (stat(_PATH_SSH_USER_RC, &st) >= 0)) { - snprintf(cmd, sizeof cmd, "%s -c '%s %s'", - shell, _PATH_BSHELL, _PATH_SSH_USER_RC); - if (debug_flag) - fprintf(stderr, "Running %s\n", cmd); - f = popen(cmd, "w"); - if (f) { - if (do_xauth) - fprintf(f, "%s %s\n", s->auth_proto, - s->auth_data); - pclose(f); - } else - fprintf(stderr, "Could not run %s\n", - _PATH_SSH_USER_RC); - } else if (stat(_PATH_SSH_SYSTEM_RC, &st) >= 0) { - if (debug_flag) - fprintf(stderr, "Running %s %s\n", _PATH_BSHELL, - _PATH_SSH_SYSTEM_RC); - f = popen(_PATH_BSHELL " " _PATH_SSH_SYSTEM_RC, "w"); - if (f) { - if (do_xauth) - fprintf(f, "%s %s\n", s->auth_proto, - s->auth_data); - pclose(f); - } else - fprintf(stderr, "Could not run %s\n", - _PATH_SSH_SYSTEM_RC); - } else if (do_xauth && options.xauth_location != NULL) { - /* Add authority data to .Xauthority if appropriate. */ - if (debug_flag) { - fprintf(stderr, - "Running %.500s add " - "%.100s %.100s %.100s\n", - options.xauth_location, s->auth_display, - s->auth_proto, s->auth_data); - } - snprintf(cmd, sizeof cmd, "%s -q -", - options.xauth_location); - f = popen(cmd, "w"); - if (f) { - fprintf(f, "add %s %s %s\n", - s->auth_display, s->auth_proto, - s->auth_data); - pclose(f); - } else { - fprintf(stderr, "Could not run %s\n", - cmd); - } - } -} - -static void -do_nologin(struct passwd *pw) -{ - FILE *f = NULL; - char buf[1024]; - -#ifdef HAVE_LOGIN_CAP - if (!login_getcapbool(lc, "ignorenologin", 0) && pw->pw_uid) - f = fopen(login_getcapstr(lc, "nologin", _PATH_NOLOGIN, - _PATH_NOLOGIN), "r"); -#else - if (pw->pw_uid) - f = fopen(_PATH_NOLOGIN, "r"); -#endif - if (f) { - /* /etc/nologin exists. Print its contents and exit. */ - 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) -{ - if (getuid() == 0 || geteuid() == 0) { -#ifdef HAVE_LOGIN_CAP - if (setusercontext(lc, pw, pw->pw_uid, - (LOGIN_SETALL & ~LOGIN_SETPATH)) < 0) { - perror("unable to set user context"); - exit(1); - } -#else - if (setlogin(pw->pw_name) < 0) - error("setlogin failed: %s", strerror(errno)); - if (setgid(pw->pw_gid) < 0) { - perror("setgid"); - exit(1); - } - /* Initialize the group list. */ - if (initgroups(pw->pw_name, pw->pw_gid) < 0) { - perror("initgroups"); - exit(1); - } - endgrent(); - - /* Permanently switch to the desired uid. */ - permanently_set_uid(pw); -#endif - } - if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid) - fatal("Failed to set uids to %u.", (u_int) pw->pw_uid); -} - -static void -launch_login(struct passwd *pw, const char *hostname) -{ - /* Launch login(1). */ - - execl("/usr/bin/login", "login", "-h", hostname, - "-p", "-f", "--", pw->pw_name, (char *)NULL); - - /* Login couldn't be executed, die. */ - - perror("login"); - exit(1); -} - -/* - * Performs common processing for the child, such as setting up the - * environment, closing extra file descriptors, setting the user and group - * ids, and executing the command or shell. - */ -void -do_child(Session *s, const char *command) -{ - extern char **environ; - char **env; - char *argv[10]; - const char *shell, *shell0, *hostname = NULL; - struct passwd *pw = s->pw; - u_int i; - - /* remove hostkey from the child's memory */ - destroy_sensitive_data(); - - /* login(1) is only called if we execute the login shell */ - if (options.use_login && command != NULL) - options.use_login = 0; - - /* - * Login(1) does this as well, and it needs uid 0 for the "-h" - * switch, so we let login(1) to this for us. - */ - if (!options.use_login) { - do_nologin(pw); - do_setusercontext(pw); - } - - /* - * Get the shell from the password data. An empty shell field is - * legal, and means /bin/sh. - */ - shell = (pw->pw_shell[0] == '\0') ? _PATH_BSHELL : pw->pw_shell; -#ifdef HAVE_LOGIN_CAP - shell = login_getcapstr(lc, "shell", (char *)shell, (char *)shell); -#endif - - env = do_setup_env(s, shell); - /* we have to stash the hostname before we close our socket. */ if (options.use_login) hostname = get_remote_name_or_ip(utmp_len, - options.verify_reverse_mapping); + options.reverse_mapping_check); /* * Close the connection descriptors; note that this is the child, and * the server will still have the socket open, and it is important @@ -1140,8 +970,8 @@ close(i); /* - * Must take new environment into use so that .ssh/rc, - * /etc/sshrc and xauth are run in the proper environment. + * Must take new environment into use so that .ssh/rc, /etc/sshrc and + * xauth are run in the proper environment. */ environ = env; @@ -1149,10 +979,10 @@ /* 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 */ @@ -1167,55 +997,112 @@ #endif } - if (!options.use_login) - do_rc_files(s, shell); + /* + * 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. */ + 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); + } + } + /* Get the last component of the shell name. */ + cp = strrchr(shell, '/'); + if (cp) + cp++; + else + cp = 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) { - char argv0[256]; + if (!options.use_login) { + char buf[256]; - /* Start the shell. Set initial character to '-'. */ - argv0[0] = '-'; + /* Start the shell. Set initial character to '-'. */ + buf[0] = '-'; + strncpy(buf + 1, cp, sizeof(buf) - 1); + buf[sizeof(buf) - 1] = 0; - if (strlcpy(argv0 + 1, shell0, sizeof(argv0) - 1) - >= sizeof(argv0) - 1) { - errno = EINVAL; + /* Execute the shell. */ + argv[0] = buf; + argv[1] = NULL; + execve(shell, argv, env); + + /* Executing the shell failed. */ perror(shell); exit(1); - } - /* Execute the shell. */ - argv[0] = argv0; - argv[1] = NULL; - execve(shell, argv, env); + } else { + /* Launch login(1). */ - /* Executing the shell failed. */ - perror(shell); - exit(1); + execl("/usr/bin/login", "login", "-h", hostname, + "-p", "-f", "--", pw->pw_name, (char *)NULL); + + /* Login couldn't be executed, die. */ + + perror("login"); + exit(1); + } } /* * Execute the command using the user's shell. This uses the -c * option to execute the command. */ - argv[0] = (char *) shell0; + argv[0] = (char *) cp; argv[1] = "-c"; argv[2] = (char *) command; argv[3] = NULL; @@ -1231,12 +1118,12 @@ 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)); @@ -1256,14 +1143,14 @@ 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 %ld", + debug("dump: used %d session %d %p channel %d pid %d", s->used, s->self, s, s->chanid, - (long)s->pid); + s->pid); } } @@ -1285,27 +1172,11 @@ 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); @@ -1321,13 +1192,13 @@ session_by_pid(pid_t pid) { int i; - debug("session_by_pid: pid %ld", (long)pid); - for (i = 0; i < MAX_SESSIONS; i++) { + debug("session_by_pid: pid %d", 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 %ld", (long)pid); + error("session_by_pid: unknown pid %d", pid); session_dump(); return NULL; } @@ -1339,7 +1210,7 @@ s->row = packet_get_int(); s->xpixel = packet_get_int(); s->ypixel = packet_get_int(); - packet_check_eom(); + packet_done(); pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel); return 1; } @@ -1358,12 +1229,6 @@ 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); @@ -1384,7 +1249,7 @@ /* Allocate a pty and open it. */ debug("Allocating pty."); - if (!PRIVSEP(pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, sizeof(s->tty)))) { + if (!pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, sizeof(s->tty))) { if (s->term) xfree(s->term); s->term = NULL; @@ -1405,13 +1270,12 @@ * time in case we call fatal() (e.g., the connection gets closed). */ fatal_add_cleanup(session_pty_cleanup, (void *)s); - if (!use_privsep) - pty_setowner(s->pw, s->tty); + 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_check_eom(); + packet_done(); session_proctitle(s); return 1; } @@ -1425,8 +1289,8 @@ char *cmd, *subsys = packet_get_string(&len); int i; - packet_check_eom(); - log("subsystem request for %.100s", subsys); + packet_done(); + log("subsystem request for %s", subsys); for (i = 0; i < options.num_subsystems; i++) { if (strcmp(subsys, options.subsystem_name[i]) == 0) { @@ -1440,12 +1304,11 @@ s->is_subsystem = 1; do_exec(s, cmd); success = 1; - break; } } if (!success) - log("subsystem request for %.100s failed, subsystem not found", + log("subsystem request for %s failed, subsystem not found", subsys); xfree(subsys); @@ -1461,7 +1324,7 @@ s->auth_proto = packet_get_string(NULL); s->auth_data = packet_get_string(NULL); s->screen = packet_get_int(); - packet_check_eom(); + packet_done(); success = session_setup_x11fwd(s); if (!success) { @@ -1476,7 +1339,7 @@ static int session_shell_req(Session *s) { - packet_check_eom(); + packet_done(); do_exec(s, NULL); return 1; } @@ -1486,7 +1349,7 @@ { u_int len; char *command = packet_get_string(&len); - packet_check_eom(); + packet_done(); do_exec(s, command); xfree(command); return 1; @@ -1496,7 +1359,7 @@ session_auth_agent_req(Session *s) { static int called = 0; - packet_check_eom(); + packet_done(); if (no_agent_forwarding_flag) { debug("session_auth_agent_req: no_agent_forwarding_flag"); return 0; @@ -1509,19 +1372,29 @@ } } -int -session_input_channel_req(Channel *c, const char *rtype) +void +session_input_channel_req(int id, void *arg) { + u_int len; + int reply; int success = 0; + char *rtype; Session *s; + Channel *c; - 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); + 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); + /* * a session is in LARVAL state until a shell, a command * or a subsystem is executed @@ -1544,7 +1417,14 @@ if (strcmp(rtype, "window-change") == 0) { success = session_window_change_req(s); } - return success; + + if (reply) { + packet_start(success ? + SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE); + packet_put_int(c->remote_id); + packet_send(); + } + xfree(rtype); } void @@ -1561,16 +1441,15 @@ channel_set_fds(s->chanid, fdout, fdin, fderr, fderr == -1 ? CHAN_EXTENDED_IGNORE : CHAN_EXTENDED_READ, - 1, - CHAN_SES_WINDOW_DEFAULT); + 1); } /* * Function to perform pty cleanup. Also called if we get aborted abnormally * (e.g., due to a dropped connection). */ -void -session_pty_cleanup2(void *session) +static void +session_pty_cleanup(void *session) { Session *s = session; @@ -1588,8 +1467,7 @@ record_logout(s->pid, s->tty); /* Release the pseudo-tty. */ - if (getuid() == 0) - pty_release(s->tty); + pty_release(s->tty); /* * Close the server side of the socket pairs. We must do this after @@ -1597,57 +1475,34 @@ * while we're still cleaning up. */ if (close(s->ptymaster) < 0) - error("close(s->ptymaster/%d): %s", s->ptymaster, strerror(errno)); + error("close(s->ptymaster): %s", strerror(errno)); /* unlink pty from session */ s->ttyfd = -1; } -void -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 ((c = channel_lookup(s->chanid)) == NULL) + if (s == NULL) + fatal("session_close: no session"); + c = channel_lookup(s->chanid); + if (c == NULL) fatal("session_exit_message: session %d: no channel %d", s->self, s->chanid); - debug("session_exit_message: session %d channel %d pid %ld", - s->self, s->chanid, (long)s->pid); + 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); - packet_put_cstring(sig2name(WTERMSIG(status))); + channel_request_start(s->chanid, + "exit-signal", 0); + packet_put_int(WTERMSIG(status)); packet_put_char(WCOREDUMP(status)); packet_put_cstring(""); packet_put_cstring(""); @@ -1671,10 +1526,10 @@ s->chanid = -1; } -void +static void session_close(Session *s) { - debug("session_close: session %d pid %ld", s->self, (long)s->pid); + debug("session_close: session %d pid %d", s->self, s->pid); if (s->ttyfd != -1) { fatal_remove_cleanup(session_pty_cleanup, (void *)s); session_pty_cleanup(s); @@ -1698,8 +1553,7 @@ { Session *s = session_by_pid(pid); if (s == NULL) { - debug("session_close_by_pid: no session for pid %ld", - (long)pid); + debug("session_close_by_pid: no session for pid %d", pid); return; } if (s->chanid != -1) @@ -1719,8 +1573,7 @@ debug("session_close_by_channel: no session for id %d", id); return; } - debug("session_close_by_channel: channel %d child %ld", - id, (long)s->pid); + 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); /* @@ -1740,17 +1593,13 @@ } void -session_destroy_all(void (*closefunc)(Session *)) +session_destroy_all(void) { int i; - for (i = 0; i < MAX_SESSIONS; i++) { + for(i = 0; i < MAX_SESSIONS; i++) { Session *s = &sessions[i]; - if (s->used) { - if (closefunc != NULL) - closefunc(s); - else - session_close(s); - } + if (s->used) + session_close(s); } } @@ -1760,7 +1609,7 @@ 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') @@ -1811,9 +1660,9 @@ debug("X11 display already set."); return 0; } - if (x11_create_display_inet(options.x11_display_offset, - options.x11_use_localhost, s->single_connection, - &s->display_number) == -1) { + s->display_number = x11_create_display_inet(options.x11_display_offset, + options.gateway_ports); + if (s->display_number == -1) { debug("x11_create_display_inet failed."); return 0; } @@ -1826,15 +1675,15 @@ * 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", + if (!options.gateway_ports) { + snprintf(display, sizeof display, "localhost:%d.%d", s->display_number, s->screen); - snprintf(auth_display, sizeof auth_display, "unix:%u.%u", - s->display_number, s->screen); + snprintf(auth_display, sizeof auth_display, "%.400s/unix:%d.%d", + hostname, s->display_number, s->screen); s->display = xstrdup(display); s->auth_display = xstrdup(auth_display); } else { - snprintf(display, sizeof display, "%.400s:%u.%u", hostname, + snprintf(display, sizeof display, "%.400s:%d.%d", hostname, s->display_number, s->screen); s->display = xstrdup(display); s->auth_display = xstrdup(display);