=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/ssh/session.c,v retrieving revision 1.42.2.8 retrieving revision 1.43 diff -u -r1.42.2.8 -r1.43 --- src/usr.bin/ssh/session.c 2002/03/08 17:04:43 1.42.2.8 +++ src/usr.bin/ssh/session.c 2000/11/06 23:04:56 1.43 @@ -9,7 +9,7 @@ * called by a name other than "ssh" or "Secure Shell". * * SSH2 support by Markus Friedl. - * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. + * Copyright (c) 2000 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,30 +33,29 @@ */ #include "includes.h" -RCSID("$OpenBSD: session.c,v 1.42.2.8 2002/03/08 17:04:43 brad Exp $"); +RCSID("$OpenBSD: session.c,v 1.43 2000/11/06 23:04:56 markus Exp $"); -#include "ssh.h" -#include "ssh1.h" -#include "ssh2.h" #include "xmalloc.h" -#include "sshpty.h" +#include "ssh.h" +#include "pty.h" #include "packet.h" #include "buffer.h" #include "mpaux.h" +#include "servconf.h" #include "uidswap.h" #include "compat.h" #include "channels.h" +#include "nchan.h" + #include "bufaux.h" +#include "ssh2.h" #include "auth.h" #include "auth-options.h" -#include "pathnames.h" -#include "log.h" -#include "servconf.h" -#include "sshlogin.h" -#include "serverloop.h" -#include "canohost.h" -#include "session.h" +#ifdef HAVE_LOGIN_CAP +#include +#endif + /* types */ #define TTYSZ 64 @@ -64,8 +63,8 @@ struct Session { int used; int self; - struct passwd *pw; - Authctxt *authctxt; + int extended; + struct passwd *pw; pid_t pid; /* tty */ char *term; @@ -73,50 +72,44 @@ 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); -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 do_motd(void); -int check_quietlogin(Session *, const char *); +void session_set_fds(Session *s, int fdin, int fdout, int fderr); +void session_pty_cleanup(Session *s); +void session_proctitle(Session *s); +void do_exec_pty(Session *s, const char *command, struct passwd * pw); +void do_exec_no_pty(Session *s, const char *command, struct passwd * pw); +void do_login(Session *s, const char *command); -static void do_authenticated1(Authctxt *); -static void do_authenticated2(Authctxt *); +void +do_child(const char *command, struct passwd * pw, const char *term, + const char *display, const char *auth_proto, + const char *auth_data, const char *ttyname); -static void session_close(Session *); -static int session_pty_req(Session *); - /* import */ extern ServerOptions options; extern char *__progname; extern int log_stderr; extern int debug_flag; -extern u_int utmp_len; +extern unsigned int utmp_len; + extern int startup_pipe; -extern void destroy_sensitive_data(void); +/* Local Xauthority file. */ +static char *xauthfile; + /* original command from peer. */ -const char *original_command = NULL; +char *original_command = NULL; /* data */ #define MAX_SESSIONS 10 @@ -126,50 +119,46 @@ static login_cap_t *lc; #endif +/* + * Remove local Xauthority file. + */ void -do_authenticated(Authctxt *authctxt) +xauthfile_cleanup_proc(void *ignore) { - /* - * Cancel the alarm we set to limit the time taken for - * authentication. - */ - alarm(0); - if (startup_pipe != -1) { - close(startup_pipe); - startup_pipe = -1; + debug("xauthfile_cleanup_proc called"); + + if (xauthfile != NULL) { + char *p; + unlink(xauthfile); + p = strrchr(xauthfile, '/'); + if (p != NULL) { + *p = '\0'; + rmdir(xauthfile); + } + xfree(xauthfile); + xauthfile = NULL; } -#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(); +} - if (compat20) - do_authenticated2(authctxt); - else - do_authenticated1(authctxt); +/* + * Function to perform cleanup if we get aborted abnormally (e.g., due to a + * dropped connection). + */ +void +pty_cleanup_proc(void *session) +{ + Session *s=session; + if (s == NULL) + fatal("pty_cleanup_proc: no session"); + debug("pty_cleanup_proc: %s", s->tty); - /* 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 + if (s->pid != 0) { + /* Record that the user has logged out. */ + record_logout(s->pid, s->tty); + } + + /* Release the pseudo-tty. */ + pty_release(s->tty); } /* @@ -178,37 +167,66 @@ * terminals are allocated, X11, TCP/IP, and authentication agent forwardings * are requested, etc. */ -static void -do_authenticated1(Authctxt *authctxt) +void +do_authenticated(struct passwd * pw) { Session *s; - char *command; - int success, type, screen_flag; + int type, fd; int compression_level = 0, enable_compression_after_reply = 0; - u_int proto_len, data_len, dlen; + int have_pty = 0; + char *command; + int n_bytes; + int plen; + unsigned int proto_len, data_len, dlen; + /* + * Cancel the alarm we set to limit the time taken for + * authentication. + */ + alarm(0); + if (startup_pipe != -1) { + close(startup_pipe); + startup_pipe = -1; + } + + /* + * Inform the channel mechanism that we are the server side and that + * the client may request to connect to any port at all. (The user + * could do it anyway, and we wouldn\'t know what is permitted except + * by the client telling us, so we can equally well trust the client + * not to request anything bogus.) + */ + if (!no_port_forwarding_flag && options.allow_tcp_forwarding) + channel_permit_all_opens(); + s = session_new(); - s->authctxt = authctxt; - s->pw = authctxt->pw; + s->pw = pw; +#ifdef HAVE_LOGIN_CAP + if ((lc = login_getclass(pw->pw_class)) == NULL) { + error("unable to get login class"); + return; + } +#endif + /* * We stay in this loop until the client requests to execute a shell * or a command. */ for (;;) { - success = 0; + int 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; } /* Enable compression after we have responded with SUCCESS. */ @@ -217,33 +235,103 @@ break; case SSH_CMSG_REQUEST_PTY: - success = session_pty_req(s); + if (no_pty_flag) { + debug("Allocating a pty not permitted for this authentication."); + break; + } + if (have_pty) + packet_disconnect("Protocol error: you already have a pty."); + + debug("Allocating pty."); + + /* Allocate a pty and open it. */ + if (!pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, + sizeof(s->tty))) { + error("Failed to allocate pty."); + break; + } + fatal_add_cleanup(pty_cleanup_proc, (void *)s); + pty_setowner(pw, s->tty); + + /* Get TERM from the packet. Note that the value may be of arbitrary length. */ + s->term = packet_get_string(&dlen); + packet_integrity_check(dlen, strlen(s->term), type); + /* packet_integrity_check(plen, 4 + dlen + 4*4 + n_bytes, type); */ + /* Remaining bytes */ + n_bytes = plen - (4 + dlen + 4 * 4); + + if (strcmp(s->term, "") == 0) { + xfree(s->term); + s->term = NULL; + } + /* Get window size from the packet. */ + s->row = packet_get_int(); + s->col = packet_get_int(); + s->xpixel = packet_get_int(); + s->ypixel = packet_get_int(); + pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel); + + /* Get tty modes from the packet. */ + tty_parse_modes(s->ttyfd, &n_bytes); + packet_integrity_check(plen, 4 + dlen + 4 * 4 + n_bytes, type); + + session_proctitle(s); + + /* Indicate that we now have a pty. */ + success = 1; + have_pty = 1; break; case SSH_CMSG_X11_REQUEST_FORWARDING: + if (!options.x11_forwarding) { + packet_send_debug("X11 forwarding disabled in server configuration file."); + break; + } + if (!options.xauth_location) { + packet_send_debug("No xauth program; cannot forward with spoofing."); + break; + } + if (no_x11_forwarding_flag) { + packet_send_debug("X11 forwarding not permitted for this authentication."); + break; + } + debug("Received request for X11 forwarding with auth spoofing."); + if (s->display != NULL) + packet_disconnect("Protocol error: X11 display already set."); + s->auth_proto = packet_get_string(&proto_len); s->auth_data = packet_get_string(&data_len); + packet_integrity_check(plen, 4 + proto_len + 4 + data_len + 4, type); - screen_flag = packet_get_protocol_flags() & - SSH_PROTOFLAG_SCREEN_NUMBER; - debug2("SSH_PROTOFLAG_SCREEN_NUMBER: %d", screen_flag); - - if (packet_remaining() == 4) { - if (!screen_flag) - debug2("Buggy client: " - "X11 screen flag missing"); + if (packet_get_protocol_flags() & SSH_PROTOFLAG_SCREEN_NUMBER) s->screen = packet_get_int(); - } else { + else s->screen = 0; + s->display = x11_create_display_inet(s->screen, options.x11_display_offset); + + if (s->display == NULL) + break; + + /* Setup to always have a local .Xauthority. */ + xauthfile = xmalloc(MAXPATHLEN); + strlcpy(xauthfile, "/tmp/ssh-XXXXXXXX", MAXPATHLEN); + temporarily_use_uid(pw->pw_uid); + if (mkdtemp(xauthfile) == NULL) { + restore_uid(); + error("private X11 dir: mkdtemp %s failed: %s", + xauthfile, strerror(errno)); + xfree(xauthfile); + xauthfile = NULL; + /* XXXX remove listening channels */ + break; } - packet_check_eom(); - success = session_setup_x11fwd(s); - if (!success) { - xfree(s->auth_proto); - xfree(s->auth_data); - s->auth_proto = NULL; - s->auth_data = NULL; - } + strlcat(xauthfile, "/cookies", MAXPATHLEN); + fd = open(xauthfile, O_RDWR|O_CREAT|O_EXCL, 0600); + if (fd >= 0) + close(fd); + restore_uid(); + fatal_add_cleanup(xauthfile_cleanup_proc, NULL); + success = 1; break; case SSH_CMSG_AGENT_REQUEST_FORWARDING: @@ -252,7 +340,7 @@ break; } debug("Received authentication agent forwarding request."); - success = auth_input_request_forwarding(s->pw); + success = auth_input_request_forwarding(pw); break; case SSH_CMSG_PORT_FORWARD_REQUEST: @@ -265,7 +353,7 @@ break; } debug("Received TCP/IP port forwarding request."); - channel_input_port_forward_request(s->pw->pw_uid == 0, options.gateway_ports); + channel_input_port_forward_request(pw->pw_uid == 0, options.gateway_ports); success = 1; break; @@ -274,70 +362,35 @@ 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: + /* Set interactive/non-interactive mode. */ + packet_set_interactive(have_pty || s->display != NULL, + options.keepalives); + if (type == SSH_CMSG_EXEC_CMD) { command = packet_get_string(&dlen); debug("Exec command '%.500s'", command); - do_exec(s, command); - xfree(command); + packet_integrity_check(plen, 4 + dlen, type); } else { - do_exec(s, NULL); + command = NULL; + packet_integrity_check(plen, 0, type); } - packet_check_eom(); - session_close(s); + if (forced_command != NULL) { + original_command = command; + command = forced_command; + debug("Forced command '%.500s'", forced_command); + } + if (have_pty) + do_exec_pty(s, command, pw); + else + do_exec_no_pty(s, command, pw); + + if (command != NULL) + xfree(command); + /* Cleanup user's local Xauthority file. */ + if (xauthfile) + xauthfile_cleanup_proc(NULL); return; default: @@ -365,7 +418,7 @@ * setting up file descriptors and such. */ void -do_exec_no_pty(Session *s, const char *command) +do_exec_no_pty(Session *s, const char *command, struct passwd * pw) { int pid; @@ -438,14 +491,12 @@ #endif /* USE_PIPES */ /* Do processing for the child (exec command etc). */ - do_child(s, command); + do_child(command, pw, NULL, s->display, s->auth_proto, s->auth_data, NULL); /* NOTREACHED */ } if (pid < 0) packet_disconnect("fork failed: %.100s", strerror(errno)); s->pid = pid; - /* Set interactive/non-interactive mode. */ - packet_set_interactive(s->display != NULL); #ifdef USE_PIPES /* We are the parent. Close the child sides of the pipes. */ close(pin[0]); @@ -453,11 +504,11 @@ close(perr[1]); if (compat20) { - session_set_fds(s, pin[1], pout[0], s->is_subsystem ? -1 : perr[0]); + session_set_fds(s, pin[1], pout[0], s->extended ? perr[0] : -1); } else { /* Enter the interactive session. */ server_loop(pid, pin[1], pout[0], perr[0]); - /* server_loop has closed pin[1], pout[0], and perr[0]. */ + /* server_loop has closed pin[1], pout[1], and perr[1]. */ } #else /* USE_PIPES */ /* We are the parent. Close the child sides of the socket pairs. */ @@ -469,7 +520,7 @@ * handle the case that fdin and fdout are the same. */ if (compat20) { - session_set_fds(s, inout[1], inout[1], s->is_subsystem ? -1 : err[1]); + session_set_fds(s, inout[1], inout[1], s->extended ? err[1] : -1); } else { server_loop(pid, inout[1], inout[1], err[1]); /* server_loop has closed inout[1] and err[1]. */ @@ -484,7 +535,7 @@ * lastlog, and other such operations. */ void -do_exec_pty(Session *s, const char *command) +do_exec_pty(Session *s, const char *command, struct passwd * pw) { int fdout, ptyfd, ttyfd, ptymaster; pid_t pid; @@ -496,23 +547,27 @@ /* 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/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)); + /* 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)); + /* Close the extra descriptor for the pseudo tty. */ close(ttyfd); @@ -521,7 +576,8 @@ do_login(s, command); /* Do common processing for the child, such as execing the command. */ - do_child(s, command); + do_child(command, pw, s->term, s->display, s->auth_proto, + s->auth_data, s->tty); /* NOTREACHED */ } if (pid < 0) @@ -547,45 +603,37 @@ s->ptymaster = ptymaster; /* Enter interactive session. */ - packet_set_interactive(1); if (compat20) { session_set_fds(s, ptyfd, fdout, -1); } else { server_loop(pid, ptyfd, fdout, -1); /* server_loop _has_ closed ptyfd and fdout. */ + session_pty_cleanup(s); } } -/* - * This is called to fork and execute a command. If another command is - * to be forced, execute that instead. - */ -void -do_exec(Session *s, const char *command) +const char * +get_remote_name_or_ip(void) { - if (forced_command) { - original_command = command; - command = forced_command; - debug("Forced command '%.900s'", command); - } - - if (s->ttyfd != -1) - do_exec_pty(s, command); - else - do_exec_no_pty(s, command); - - original_command = NULL; + static const char *remote = ""; + if (utmp_len > 0) + remote = get_canonical_hostname(); + if (utmp_len == 0 || strlen(remote) > utmp_len) + remote = get_remote_ipaddr(); + return remote; } - /* administrative, login(1)-like work */ void do_login(Session *s, const char *command) { + FILE *f; char *time_string; + char buf[256]; char hostname[MAXHOSTNAMELEN]; socklen_t fromlen; struct sockaddr_storage from; + struct stat st; time_t last_login_time; struct passwd * pw = s->pw; pid_t pid = getpid(); @@ -598,28 +646,32 @@ 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)); - } + hostname[0] = '\0'; + last_login_time = get_last_login_time(pw->pw_uid, pw->pw_name, + hostname, sizeof(hostname)); /* Record that there was a login on that tty from the remote host. */ record_login(pid, s->tty, pw->pw_name, pw->pw_uid, - get_remote_name_or_ip(utmp_len, options.verify_reverse_mapping), - (struct sockaddr *)&from); + get_remote_name_or_ip(), (struct sockaddr *)&from); - if (check_quietlogin(s, command)) + /* Done if .hushlogin exists or a command given. */ + if (command != NULL) return; - - if (options.print_lastlog && last_login_time != 0) { + snprintf(buf, sizeof(buf), "%.200s/.hushlogin", pw->pw_dir); +#ifdef HAVE_LOGIN_CAP + if (login_getcapbool(lc, "hushlogin", 0) || stat(buf, &st) >= 0) +#else + if (stat(buf, &st) >= 0) +#endif + return; + if (last_login_time != 0) { time_string = ctime(&last_login_time); if (strchr(time_string, '\n')) *strchr(time_string, '\n') = 0; @@ -628,19 +680,6 @@ else printf("Last login: %s from %s\r\n", time_string, hostname); } - - do_motd(); -} - -/* - * Display the message of the day. - */ -void -do_motd(void) -{ - FILE *f; - char buf[256]; - if (options.print_motd) { #ifdef HAVE_LOGIN_CAP f = fopen(login_getcapstr(lc, "welcome", "/etc/motd", @@ -656,40 +695,15 @@ } } - /* - * 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) +void +child_set_env(char ***envp, unsigned int *envsizep, const char *name, + const char *value) { - u_int i, namelen; + unsigned int i, namelen; char **env; /* @@ -726,9 +740,9 @@ * Otherwise, it must consist of empty lines, comments (line starts with '#') * and assignments of the form name=value. No other forms are allowed. */ -static void -read_environment_file(char ***env, u_int *envsize, - const char *filename) +void +read_environment_file(char ***env, unsigned int *envsize, + const char *filename) { FILE *f; char buf[4096]; @@ -761,14 +775,101 @@ 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(const char *command, struct passwd * pw, const char *term, + const char *display, const char *auth_proto, + const char *auth_data, const char *ttyname) { + const char *shell, *hostname = NULL, *cp = NULL; char buf[256]; - u_int i, envsize; + char cmd[1024]; + FILE *f = NULL; + unsigned int envsize, i; char **env; - struct passwd *pw = s->pw; + extern char **environ; + struct stat st; + char *argv[10]; + /* 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->pw_uid); +#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 */ + /* Initialize the environment. */ envsize = 100; env = xmalloc(envsize * sizeof(char *)); @@ -797,49 +898,48 @@ 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 *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); + 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); } 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) - child_set_env(&env, &envsize, "SSH_TTY", s->tty); - if (s->term) - child_set_env(&env, &envsize, "TERM", s->term); - if (s->display) - child_set_env(&env, &envsize, "DISPLAY", s->display); + if (ttyname) + child_set_env(&env, &envsize, "SSH_TTY", ttyname); + if (term) + child_set_env(&env, &envsize, "TERM", term); + if (display) + child_set_env(&env, &envsize, "DISPLAY", display); 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", - s->authctxt->krb5_ticket_file); -#endif + { + extern char *ticket; + + if (ticket) + child_set_env(&env, &envsize, "KRBTKFILE", ticket); + } +#endif /* KRB4 */ + + if (xauthfile) + child_set_env(&env, &envsize, "XAUTHORITY", xauthfile); 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) { @@ -853,178 +953,9 @@ 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 %.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) -{ - 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); -} - -/* - * 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); + hostname = get_remote_name_or_ip(); /* * Close the connection descriptors; note that this is the child, and * the server will still have the socket open, and it is important @@ -1060,90 +991,146 @@ for (i = 3; i < 64; i++) close(i); - /* - * Must take new environment into use so that .ssh/rc, - * /etc/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 } - if (!options.use_login) - do_rc_files(s, shell); + /* + * Must take new environment into use so that .ssh/rc, /etc/sshrc and + * xauth are run in the proper environment. + */ + environ = env; - /* restore SIGPIPE for child */ - signal(SIGPIPE, SIG_DFL); + /* + * Run $HOME/.ssh/rc, /etc/sshrc, or xauth (whichever is found first + * in this order). + */ + if (!options.use_login) { + if (stat(SSH_USER_RC, &st) >= 0) { + if (debug_flag) + fprintf(stderr, "Running /bin/sh %s\n", SSH_USER_RC); - if (options.use_login) { - /* Launch login(1). */ + f = popen("/bin/sh " SSH_USER_RC, "w"); + if (f) { + if (auth_proto != NULL && auth_data != NULL) + fprintf(f, "%s %s\n", auth_proto, auth_data); + pclose(f); + } else + fprintf(stderr, "Could not run %s\n", SSH_USER_RC); + } else if (stat(SSH_SYSTEM_RC, &st) >= 0) { + if (debug_flag) + fprintf(stderr, "Running /bin/sh %s\n", SSH_SYSTEM_RC); - execl("/usr/bin/login", "login", "-h", hostname, - "-p", "-f", "--", pw->pw_name, (char *)NULL); - - /* Login couldn't be executed, die. */ - - perror("login"); - exit(1); + f = popen("/bin/sh " SSH_SYSTEM_RC, "w"); + if (f) { + if (auth_proto != NULL && auth_data != NULL) + fprintf(f, "%s %s\n", auth_proto, auth_data); + pclose(f); + } else + fprintf(stderr, "Could not run %s\n", SSH_SYSTEM_RC); + } else if (options.xauth_location != NULL) { + /* Add authority data to .Xauthority if appropriate. */ + if (auth_proto != NULL && auth_data != NULL) { + char *screen = strchr(display, ':'); + if (debug_flag) { + fprintf(stderr, + "Running %.100s add %.100s %.100s %.100s\n", + options.xauth_location, display, + auth_proto, auth_data); + if (screen != NULL) + fprintf(stderr, + "Adding %.*s/unix%s %s %s\n", + (int)(screen-display), display, + screen, auth_proto, auth_data); + } + snprintf(cmd, sizeof cmd, "%s -q -", + options.xauth_location); + f = popen(cmd, "w"); + if (f) { + fprintf(f, "add %s %s %s\n", display, + auth_proto, auth_data); + if (screen != NULL) + fprintf(f, "add %.*s/unix%s %s %s\n", + (int)(screen-display), display, + screen, auth_proto, 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; } - - /* 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] = '-'; + /* + * Check for mail if we have a tty and it was enabled + * in server options. + */ + if (ttyname && 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; - 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, 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; @@ -1159,20 +1146,28 @@ 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; + sessions[i].self = i; } 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)); + s->pid = 0; + s->extended = 0; s->chanid = -1; s->ptyfd = -1; s->ttyfd = -1; + s->term = NULL; + s->pw = NULL; + s->display = NULL; + s->screen = 0; + s->auth_data = NULL; + s->auth_proto = NULL; s->used = 1; - s->self = i; + s->pw = NULL; debug("session_new: session %d", i); return s; } @@ -1180,11 +1175,11 @@ return NULL; } -static void +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, @@ -1196,7 +1191,7 @@ } int -session_open(Authctxt *authctxt, int chanid) +session_open(int chanid) { Session *s = session_new(); debug("session_open: channel %d", chanid); @@ -1204,20 +1199,19 @@ error("no more sessions"); return 0; } - s->authctxt = authctxt; - s->pw = authctxt->pw; + s->pw = auth_get_user(); if (s->pw == NULL) - fatal("no user for session %d", s->self); + fatal("no user for session %i", s->self); debug("session_open: session %d: link with channel %d", s->self, chanid); s->chanid = chanid; return 1; } -static Session * +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); @@ -1229,12 +1223,12 @@ return NULL; } -static Session * +Session * 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; @@ -1244,168 +1238,189 @@ return NULL; } -static int +int session_window_change_req(Session *s) { s->col = packet_get_int(); 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; } -static int +int session_pty_req(Session *s) { - u_int len; - int n_bytes; + unsigned int len; + char *term_modes; /* encoded terminal modes */ - if (no_pty_flag) { - debug("Allocating a pty not permitted for this authentication."); + if (no_pty_flag) return 0; - } - if (s->ttyfd != -1) { - packet_disconnect("Protocol error: you already have a pty."); + if (s->ttyfd != -1) return 0; - } - s->term = packet_get_string(&len); - - if (compat20) { - s->col = packet_get_int(); - s->row = packet_get_int(); - } else { - s->row = packet_get_int(); - s->col = packet_get_int(); - } + s->col = packet_get_int(); + s->row = packet_get_int(); s->xpixel = packet_get_int(); s->ypixel = packet_get_int(); + term_modes = packet_get_string(&len); + packet_done(); if (strcmp(s->term, "") == 0) { xfree(s->term); s->term = NULL; } - /* Allocate a pty and open it. */ - debug("Allocating pty."); if (!pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, sizeof(s->tty))) { - if (s->term) - xfree(s->term); + xfree(s->term); s->term = NULL; s->ptyfd = -1; s->ttyfd = -1; error("session_pty_req: session %d alloc failed", s->self); + xfree(term_modes); return 0; } debug("session_pty_req: session %d alloc %s", s->self, s->tty); - - /* for SSH1 the tty modes length is not given */ - if (!compat20) - 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); + fatal_add_cleanup(pty_cleanup_proc, (void *)s); pty_setowner(s->pw, s->tty); - - /* Set window size from the packet. */ + /* Get window size from the packet. */ pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel); - packet_check_eom(); session_proctitle(s); + + /* XXX parse and set terminal modes */ + xfree(term_modes); return 1; } -static int +int session_subsystem_req(Session *s) { - struct stat st; - u_int len; + unsigned int len; int success = 0; - char *cmd, *subsys = packet_get_string(&len); + char *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) { - cmd = options.subsystem_command[i]; - if (stat(cmd, &st) < 0) { - error("subsystem: cannot stat %s: %s", cmd, - strerror(errno)); - break; - } - debug("subsystem: exec() %s", cmd); - s->is_subsystem = 1; - do_exec(s, cmd); + if(strcmp(subsys, options.subsystem_name[i]) == 0) { + debug("subsystem: exec() %s", options.subsystem_command[i]); + do_exec_no_pty(s, options.subsystem_command[i], s->pw); success = 1; - break; } } if (!success) - log("subsystem request for %.100s failed, subsystem not found", - subsys); + log("subsystem request for %s failed, subsystem not found", subsys); xfree(subsys); return success; } -static int +int session_x11_req(Session *s) { - int success; + int fd; + if (no_x11_forwarding_flag) { + debug("X11 forwarding disabled in user configuration file."); + return 0; + } + if (!options.x11_forwarding) { + debug("X11 forwarding disabled in server configuration file."); + return 0; + } + if (xauthfile != NULL) { + debug("X11 fwd already started."); + return 0; + } + debug("Received request for X11 forwarding with auth spoofing."); + if (s->display != NULL) + packet_disconnect("Protocol error: X11 display already set."); + s->single_connection = packet_get_char(); 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) { + s->display = x11_create_display_inet(s->screen, options.x11_display_offset); + if (s->display == NULL) { xfree(s->auth_proto); xfree(s->auth_data); - s->auth_proto = NULL; - s->auth_data = NULL; + return 0; } - return success; + xauthfile = xmalloc(MAXPATHLEN); + strlcpy(xauthfile, "/tmp/ssh-XXXXXXXX", MAXPATHLEN); + temporarily_use_uid(s->pw->pw_uid); + if (mkdtemp(xauthfile) == NULL) { + restore_uid(); + error("private X11 dir: mkdtemp %s failed: %s", + xauthfile, strerror(errno)); + xfree(xauthfile); + xauthfile = NULL; + xfree(s->auth_proto); + xfree(s->auth_data); + /* XXXX remove listening channels */ + return 0; + } + strlcat(xauthfile, "/cookies", MAXPATHLEN); + fd = open(xauthfile, O_RDWR|O_CREAT|O_EXCL, 0600); + if (fd >= 0) + close(fd); + restore_uid(); + fatal_add_cleanup(xauthfile_cleanup_proc, s); + return 1; } -static int +int session_shell_req(Session *s) { - packet_check_eom(); - do_exec(s, NULL); + /* if forced_command == NULL, the shell is execed */ + char *shell = forced_command; + packet_done(); + s->extended = 1; + if (s->ttyfd == -1) + do_exec_no_pty(s, shell, s->pw); + else + do_exec_pty(s, shell, s->pw); return 1; } -static int +int session_exec_req(Session *s) { - u_int len; + unsigned int len; char *command = packet_get_string(&len); - packet_check_eom(); - do_exec(s, command); - xfree(command); + packet_done(); + if (forced_command) { + original_command = command; + command = forced_command; + debug("Forced command '%.500s'", forced_command); + } + s->extended = 1; + if (s->ttyfd == -1) + do_exec_no_pty(s, command, s->pw); + else + do_exec_pty(s, command, s->pw); + if (forced_command == NULL) + xfree(command); return 1; } -static int +int session_auth_agent_req(Session *s) { static int called = 0; - packet_check_eom(); - if (no_agent_forwarding_flag) { - debug("session_auth_agent_req: no_agent_forwarding_flag"); - return 0; - } + packet_done(); if (called) { return 0; } else { @@ -1414,22 +1429,32 @@ } } -int -session_input_channel_req(Channel *c, const char *rtype) +void +session_input_channel_req(int id, void *arg) { + unsigned 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 + * a session is in LARVAL state until a shell + * or programm is executed */ if (c->type == SSH_CHANNEL_LARVAL) { if (strcmp(rtype, "shell") == 0) { @@ -1449,7 +1474,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 @@ -1466,31 +1498,22 @@ 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). - */ -static void -session_pty_cleanup(void *session) +void +session_pty_cleanup(Session *s) { - Session *s = session; - - if (s == NULL) { - error("session_pty_cleanup: no session"); + if (s == NULL || s->ttyfd == -1) return; - } - if (s->ttyfd == -1) - return; - debug("session_pty_cleanup: session %d release %s", s->self, s->tty); + debug("session_pty_cleanup: session %i release %s", s->self, s->tty); + /* Cancel the cleanup function. */ + fatal_remove_cleanup(pty_cleanup_proc, (void *)s); + /* Record that the user has logged out. */ - if (s->pid != 0) - record_logout(s->pid, s->tty); + record_logout(s->pid, s->tty); /* Release the pseudo-tty. */ pty_release(s->tty); @@ -1502,28 +1525,29 @@ */ if (close(s->ptymaster) < 0) error("close(s->ptymaster): %s", strerror(errno)); - - /* unlink pty from session */ - s->ttyfd = -1; } -static void +void session_exit_message(Session *s, int status) { Channel *c; - - if ((c = channel_lookup(s->chanid)) == NULL) - fatal("session_exit_message: session %d: no channel %d", + if (s == NULL) + fatal("session_close: no session"); + c = channel_lookup(s->chanid); + if (c == NULL) + fatal("session_close: 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)); packet_put_char(WCOREDUMP(status)); packet_put_cstring(""); @@ -1548,25 +1572,26 @@ s->chanid = -1; } -static void -session_close(Session *s) +void +session_free(Session *s) { - 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); - } + debug("session_free: session %d pid %d", s->self, s->pid); if (s->term) 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) xfree(s->auth_proto); s->used = 0; +} + +void +session_close(Session *s) +{ + session_pty_cleanup(s); + session_free(s); session_proctitle(s); } @@ -1575,7 +1600,7 @@ { Session *s = session_by_pid(pid); if (s == NULL) { - debug("session_close_by_pid: no session for pid %d", pid); + debug("session_close_by_pid: no session for pid %d", s->pid); return; } if (s->chanid != -1) @@ -1592,46 +1617,32 @@ { Session *s = session_by_channel(id); if (s == NULL) { - debug("session_close_by_channel: no session for id %d", id); + debug("session_close_by_channel: no session for channel %d", id); return; } - 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 */ + /* disconnect channel */ channel_cancel_cleanup(s->chanid); s->chanid = -1; - session_close(s); -} -void -session_destroy_all(void) -{ - int i; - for (i = 0; i < MAX_SESSIONS; i++) { - Session *s = &sessions[i]; - if (s->used) - 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)); } } -static char * +char * 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') @@ -1653,69 +1664,28 @@ setproctitle("%s@%s", s->pw->pw_name, session_tty_list()); } -int -session_setup_x11fwd(Session *s) +void +do_authenticated2(void) { - struct stat st; - char display[512], auth_display[512]; - char hostname[MAXHOSTNAMELEN]; + struct passwd *pw; - if (no_x11_forwarding_flag) { - packet_send_debug("X11 forwarding disabled in user configuration file."); - return 0; - } - if (!options.x11_forwarding) { - debug("X11 forwarding disabled in server configuration file."); - return 0; - } - if (!options.xauth_location || - (stat(options.xauth_location, &st) == -1)) { - packet_send_debug("No xauth program; cannot forward with spoofing."); - return 0; - } - if (options.use_login) { - packet_send_debug("X11 forwarding disabled; " - "not compatible with UseLogin=yes."); - return 0; - } - if (s->display != NULL) { - debug("X11 display already set."); - return 0; - } - 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. + * Cancel the alarm we set to limit the time taken for + * authentication. */ - 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 { - snprintf(display, sizeof display, "%.400s:%d.%d", hostname, - s->display_number, s->screen); - s->display = xstrdup(display); - s->auth_display = xstrdup(display); + alarm(0); + if (startup_pipe != -1) { + close(startup_pipe); + startup_pipe = -1; } - - return 1; -} - -static void -do_authenticated2(Authctxt *authctxt) -{ - server_loop2(authctxt); +#ifdef HAVE_LOGIN_CAP + pw = auth_get_user(); + if ((lc = login_getclass(pw->pw_class)) == NULL) { + error("unable to get login class"); + return; + } +#endif + server_loop2(); + if (xauthfile) + xauthfile_cleanup_proc(NULL); }