=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/ssh/session.c,v retrieving revision 1.154.2.2 retrieving revision 1.155 diff -u -r1.154.2.2 -r1.155 --- src/usr.bin/ssh/session.c 2004/03/04 18:18:16 1.154.2.2 +++ src/usr.bin/ssh/session.c 2003/04/08 20:21:29 1.155 @@ -33,7 +33,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: session.c,v 1.154.2.2 2004/03/04 18:18:16 brad Exp $"); +RCSID("$OpenBSD: session.c,v 1.155 2003/04/08 20:21:29 itojun Exp $"); #include "ssh.h" #include "ssh1.h" @@ -58,19 +58,11 @@ #include "session.h" #include "monitor_wrap.h" -#ifdef KRB5 -#include -#endif - -#ifdef GSSAPI -#include "ssh-gss.h" -#endif - /* func */ Session *session_new(void); void session_set_fds(Session *, int, int, int); -void session_pty_cleanup(Session *); +void session_pty_cleanup(void *); void session_proctitle(Session *); int session_setup_x11fwd(Session *); void do_exec_pty(Session *, const char *); @@ -106,8 +98,6 @@ login_cap_t *lc; #endif -static int is_child = 0; - /* Name and directory of socket for authentication agent forwarding. */ static char *auth_sock_name = NULL; static char *auth_sock_dir = NULL; @@ -115,8 +105,10 @@ /* removes the agent forwarding socket */ static void -auth_sock_cleanup_proc(struct passwd *pw) +auth_sock_cleanup_proc(void *_pw) { + struct passwd *pw = _pw; + if (auth_sock_name != NULL) { temporarily_use_uid(pw); unlink(auth_sock_name); @@ -144,7 +136,7 @@ /* 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-XXXXXXXXXX", MAXPATHLEN); + strlcpy(auth_sock_dir, "/tmp/ssh-XXXXXXXX", MAXPATHLEN); /* Create private directory for socket */ if (mkdtemp(auth_sock_dir) == NULL) { @@ -160,6 +152,9 @@ 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) @@ -177,14 +172,14 @@ restore_uid(); /* Start listening on the socket. */ - if (listen(sock, SSH_LISTEN_BACKLOG) < 0) + 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, "auth socket", 1); + 0, xstrdup("auth socket"), 1); strlcpy(nc->path, auth_sock_name, sizeof(nc->path)); return 1; } @@ -213,7 +208,17 @@ else do_authenticated1(authctxt); - do_cleanup(authctxt); + /* remove agent socket */ + if (auth_sock_name != NULL) + 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 } /* @@ -322,6 +327,58 @@ 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) { @@ -386,7 +443,7 @@ /* Fork the child. */ if ((pid = fork()) == 0) { - is_child = 1; + fatal_remove_all_cleanups(); /* Child. Reinitialize the log since the pid has changed. */ log_init(__progname, options.log_level, options.log_facility, log_stderr); @@ -494,7 +551,7 @@ /* Fork the child. */ if ((pid = fork()) == 0) { - is_child = 1; + fatal_remove_all_cleanups(); /* Child. Reinitialize the log because the pid has changed. */ log_init(__progname, options.log_level, options.log_facility, log_stderr); @@ -568,14 +625,6 @@ debug("Forced command '%.900s'", command); } -#ifdef GSSAPI - if (options.gss_authentication) { - temporarily_use_uid(s->pw); - ssh_gssapi_storecreds(); - restore_uid(); - } -#endif - if (s->ttyfd != -1) do_exec_pty(s, command); else @@ -605,7 +654,7 @@ if (getpeername(packet_get_connection_in(), (struct sockaddr *) & from, &fromlen) < 0) { debug("getpeername: %.100s", strerror(errno)); - cleanup_exit(255); + fatal_cleanup(); } } @@ -613,7 +662,7 @@ if (!use_privsep) record_login(pid, s->tty, pw->pw_name, pw->pw_uid, get_remote_name_or_ip(utmp_len, - options.use_dns), + options.verify_reverse_mapping), (struct sockaddr *)&from, fromlen); if (check_quietlogin(s, command)) @@ -686,13 +735,12 @@ * Sets the value of the given variable in the environment. If the variable * already exists, its value is overriden. */ -void +static void child_set_env(char ***envp, u_int *envsizep, const char *name, const char *value) { - char **env; - u_int envsize; u_int i, namelen; + char **env; /* * Find the slot where the value should be stored. If the variable @@ -709,13 +757,12 @@ xfree(env[i]); } else { /* New variable. Expand if necessary. */ - envsize = *envsizep; - if (i >= envsize - 1) { - if (envsize >= 1000) - fatal("child_set_env: too many env vars"); - envsize += 50; - env = (*envp) = xrealloc(env, envsize * sizeof(char *)); - *envsizep = envsize; + 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 *)); } /* Need to set the NULL pointer at end of array beyond the new slot. */ env[i + 1] = NULL; @@ -784,13 +831,6 @@ env = xmalloc(envsize * sizeof(char *)); env[0] = NULL; -#ifdef GSSAPI - /* Allow any GSSAPI methods that we've used to alter - * the childs environment as they see fit - */ - ssh_gssapi_do_child(&env, &envsize); -#endif - if (!options.use_login) { /* Set basic environment. */ child_set_env(&env, &envsize, "USER", pw->pw_name); @@ -853,6 +893,11 @@ if (original_command) child_set_env(&env, &envsize, "SSH_ORIGINAL_COMMAND", original_command); +#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", @@ -925,7 +970,7 @@ if (debug_flag) { fprintf(stderr, "Running %.500s remove %.100s\n", - options.xauth_location, s->auth_display); + options.xauth_location, s->auth_display); fprintf(stderr, "%.500s add %.100s %.100s %.100s\n", options.xauth_location, s->auth_display, @@ -1007,22 +1052,6 @@ } static void -do_pwchange(Session *s) -{ - fprintf(stderr, "WARNING: Your password has expired.\n"); - if (s->ttyfd != -1) { - fprintf(stderr, - "You must change your password now and login again!\n"); - execl(_PATH_PASSWD_PROG, "passwd", (char *)NULL); - perror("passwd"); - } else { - fprintf(stderr, - "Password change required but no TTY available.\n"); - } - exit(1); -} - -static void launch_login(struct passwd *pw, const char *hostname) { /* Launch login(1). */ @@ -1036,40 +1065,6 @@ exit(1); } -static void -child_close_fds(void) -{ - int i; - - if (packet_get_connection_in() == packet_get_connection_out()) - close(packet_get_connection_in()); - else { - close(packet_get_connection_in()); - close(packet_get_connection_out()); - } - /* - * Close all descriptors related to channels. They will still remain - * open in the parent. - */ - /* XXX better use close-on-exec? -markus */ - channel_close_all(); - - /* - * Close any extra file descriptors. Note that there may still be - * descriptors left by system functions. They will be closed later. - */ - endpwent(); - - /* - * Close any extra open file descriptors so that we don\'t have them - * hanging around in clients. Note that we want to do this after - * initgroups, because at least on Solaris 2.3 it leaves file - * descriptors open. - */ - for (i = 3; i < 64; i++) - close(i); -} - /* * Performs common processing for the child, such as setting up the * environment, closing extra file descriptors, setting the user and group @@ -1083,18 +1078,11 @@ 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(); - /* Force a password change */ - if (s->authctxt->force_pwchange) { - do_setusercontext(pw); - child_close_fds(); - do_pwchange(s); - exit(1); - } - /* login(1) is only called if we execute the login shell */ if (options.use_login && command != NULL) options.use_login = 0; @@ -1127,7 +1115,7 @@ /* we have to stash the hostname before we close our socket. */ if (options.use_login) hostname = get_remote_name_or_ip(utmp_len, - options.use_dns); + 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 @@ -1135,39 +1123,51 @@ * closed before building the environment, as we call * get_remote_ipaddr there. */ - child_close_fds(); + if (packet_get_connection_in() == packet_get_connection_out()) + close(packet_get_connection_in()); + else { + close(packet_get_connection_in()); + close(packet_get_connection_out()); + } + /* + * Close all descriptors related to channels. They will still remain + * open in the parent. + */ + /* XXX better use close-on-exec? -markus */ + channel_close_all(); /* - * Must take new environment into use so that .ssh/rc, - * /etc/ssh/sshrc and xauth are run in the proper environment. + * Close any extra file descriptors. Note that there may still be + * descriptors left by system functions. They will be closed later. */ - environ = env; + endpwent(); -#ifdef KRB5 /* - * At this point, we check to see if AFS is active and if we have - * a valid Kerberos 5 TGT. If so, it seems like a good idea to see - * if we can (and need to) extend the ticket into an AFS token. If - * we don't do this, we run into potential problems if the user's - * home directory is in AFS and it's not world-readable. + * Close any extra open file descriptors so that we don\'t have them + * hanging around in clients. Note that we want to do this after + * initgroups, because at least on Solaris 2.3 it leaves file + * descriptors open. */ + for (i = 3; i < 64; i++) + close(i); - if (options.kerberos_get_afs_token && k_hasafs() && - (s->authctxt->krb5_ctx != NULL)) { + /* + * 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]; - debug("Getting AFS token"); - - k_setpag(); - if (k_afs_cell_of_file(pw->pw_dir, cell, sizeof(cell)) == 0) - krb5_afslog(s->authctxt->krb5_ctx, - s->authctxt->krb5_fwd_ccache, cell, NULL); + krb_afslog(cell, 0); - krb5_afslog_home(s->authctxt->krb5_ctx, - s->authctxt->krb5_fwd_ccache, NULL, NULL, pw->pw_dir); + krb_afslog(0, 0); } -#endif +#endif /* AFS */ /* Change current directory to the user\'s home directory. */ if (chdir(pw->pw_dir) < 0) { @@ -1290,7 +1290,7 @@ } s->authctxt = authctxt; s->pw = authctxt->pw; - if (s->pw == NULL || !authctxt->valid) + if (s->pw == NULL) fatal("no user for session %d", s->self); debug("session_open: session %d: link with channel %d", s->self, chanid); s->chanid = chanid; @@ -1412,6 +1412,11 @@ n_bytes = packet_remaining(); tty_parse_modes(s->ttyfd, &n_bytes); + /* + * Add a cleanup function to clear the utmp entry and record logout + * 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); @@ -1500,20 +1505,6 @@ } static int -session_break_req(Session *s) -{ - u_int break_length; - - break_length = packet_get_int(); /* ignored */ - packet_check_eom(); - - if (s->ttyfd == -1 || - tcsendbreak(s->ttyfd, 0) < 0) - return 0; - return 1; -} - -static int session_auth_agent_req(Session *s) { static int called = 0; @@ -1560,8 +1551,6 @@ success = session_auth_agent_req(s); } else if (strcmp(rtype, "subsystem") == 0) { success = session_subsystem_req(s); - } else if (strcmp(rtype, "break") == 0) { - success = session_break_req(s); } } if (strcmp(rtype, "window-change") == 0) { @@ -1593,8 +1582,10 @@ * (e.g., due to a dropped connection). */ void -session_pty_cleanup2(Session *s) +session_pty_cleanup2(void *session) { + Session *s = session; + if (s == NULL) { error("session_pty_cleanup: no session"); return; @@ -1625,9 +1616,9 @@ } void -session_pty_cleanup(Session *s) +session_pty_cleanup(void *session) { - PRIVSEP(session_pty_cleanup2(s)); + PRIVSEP(session_pty_cleanup2(session)); } static char * @@ -1696,8 +1687,10 @@ session_close(Session *s) { debug("session_close: session %d pid %ld", s->self, (long)s->pid); - if (s->ttyfd != -1) + if (s->ttyfd != -1) { + fatal_remove_cleanup(session_pty_cleanup, (void *)s); session_pty_cleanup(s); + } if (s->term) xfree(s->term); if (s->display) @@ -1746,8 +1739,10 @@ * delay detach of session, but release pty, since * the fd's to the child are already closed */ - if (s->ttyfd != -1) + if (s->ttyfd != -1) { + fatal_remove_cleanup(session_pty_cleanup, (void *)s); session_pty_cleanup(s); + } return; } /* detach by removing callback */ @@ -1864,44 +1859,4 @@ do_authenticated2(Authctxt *authctxt) { server_loop2(authctxt); -} - -void -do_cleanup(Authctxt *authctxt) -{ - static int called = 0; - - debug("do_cleanup"); - - /* no cleanup if we're in the child for login shell */ - if (is_child) - return; - - /* avoid double cleanup */ - if (called) - return; - called = 1; - - if (authctxt == NULL) - return; -#ifdef KRB5 - if (options.kerberos_ticket_cleanup && - authctxt->krb5_ctx) - krb5_cleanup_proc(authctxt); -#endif - -#ifdef GSSAPI - if (compat20 && options.gss_cleanup_creds) - ssh_gssapi_cleanup_creds(); -#endif - - /* remove agent socket */ - auth_sock_cleanup_proc(authctxt->pw); - - /* - * Cleanup ptys/utmp only if privsep is disabled, - * or if running in monitor. - */ - if (!use_privsep || mm_is_monitor()) - session_destroy_all(session_pty_cleanup2); }