diff options
author | Alistair Delva <adelva@google.com> | 2020-08-21 00:00:13 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2020-08-21 00:00:13 +0000 |
commit | ed358b3546c776c1c677fd88eb8f716cf6187510 (patch) | |
tree | 3c6134bcb2cda4b9dccc57b4a8b997a945aab62d /session.c | |
parent | 22246b08952d746a7cc5a292570636cf4277598f (diff) | |
parent | 44a1065de8a58c51a021243a28bfa01e87822e4f (diff) |
Merge changes I934c73d4,I28cdc9a0,I9e734da9,I3c079d86
* changes:
UPSTREAM: depend
UPSTREAM: upstream: avoid possible NULL deref; from Pedro Martelletto
Revert "upstream: fix compilation with DEBUG_KEXDH; bz#3160 ok dtucker@"
Merge upstream-master into master
Diffstat (limited to 'session.c')
-rw-r--r-- | session.c | 1393 |
1 files changed, 670 insertions, 723 deletions
@@ -1,4 +1,4 @@ -/* $OpenBSD: session.c,v 1.277 2015/01/16 06:40:12 deraadt Exp $ */ +/* $OpenBSD: session.c,v 1.319 2020/03/13 03:17:07 djm Exp $ */ /* * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * All rights reserved @@ -46,6 +46,7 @@ #include <arpa/inet.h> +#include <ctype.h> #include <errno.h> #include <fcntl.h> #include <grp.h> @@ -55,26 +56,26 @@ #endif #include <pwd.h> #include <signal.h> -#include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <stdarg.h> #include <unistd.h> #include <limits.h> #include "openbsd-compat/sys-queue.h" #include "xmalloc.h" #include "ssh.h" -#include "ssh1.h" #include "ssh2.h" #include "sshpty.h" #include "packet.h" -#include "buffer.h" +#include "sshbuf.h" +#include "ssherr.h" #include "match.h" #include "uidswap.h" #include "compat.h" #include "channels.h" -#include "key.h" +#include "sshkey.h" #include "cipher.h" #ifdef GSSAPI #include "ssh-gss.h" @@ -94,6 +95,7 @@ #include "kex.h" #include "monitor_wrap.h" #include "sftp.h" +#include "atomicio.h" #if defined(KRB5) && defined(USE_AFS) #include <kafs.h> @@ -112,35 +114,32 @@ /* func */ Session *session_new(void); -void session_set_fds(Session *, int, int, int, int, int); +void session_set_fds(struct ssh *, Session *, int, int, int, int, int); void session_pty_cleanup(Session *); void session_proctitle(Session *); -int session_setup_x11fwd(Session *); -int do_exec_pty(Session *, const char *); -int do_exec_no_pty(Session *, const char *); -int do_exec(Session *, const char *); -void do_login(Session *, const char *); -#ifdef LOGIN_NEEDS_UTMPX -static void do_pre_login(Session *s); -#endif -void do_child(Session *, const char *); +int session_setup_x11fwd(struct ssh *, Session *); +int do_exec_pty(struct ssh *, Session *, const char *); +int do_exec_no_pty(struct ssh *, Session *, const char *); +int do_exec(struct ssh *, Session *, const char *); +void do_login(struct ssh *, Session *, const char *); +void do_child(struct ssh *, Session *, const char *); void do_motd(void); int check_quietlogin(Session *, const char *); -static void do_authenticated1(Authctxt *); -static void do_authenticated2(Authctxt *); +static void do_authenticated2(struct ssh *, Authctxt *); -static int session_pty_req(Session *); +static int session_pty_req(struct ssh *, Session *); /* import */ extern ServerOptions options; extern char *__progname; -extern int log_stderr; extern int debug_flag; extern u_int utmp_len; extern int startup_pipe; extern void destroy_sensitive_data(void); -extern Buffer loginmsg; +extern struct sshbuf *loginmsg; +extern struct sshauthopt *auth_opts; +extern char *tun_fwd_ifnames; /* serverloop.c */ /* original command from peer. */ const char *original_command = NULL; @@ -160,6 +159,10 @@ login_cap_t *lc; #endif static int is_child = 0; +static int in_chroot = 0; + +/* File containing userauth info, if ExposeAuthInfo set */ +static char *auth_info_file = NULL; /* Name and directory of socket for authentication agent forwarding. */ static char *auth_sock_name = NULL; @@ -180,7 +183,7 @@ auth_sock_cleanup_proc(struct passwd *pw) } static int -auth_input_request_forwarding(struct passwd * pw) +auth_input_request_forwarding(struct ssh *ssh, struct passwd * pw) { Channel *nc; int sock = -1; @@ -198,7 +201,7 @@ auth_input_request_forwarding(struct passwd * pw) /* Create private directory for socket */ if (mkdtemp(auth_sock_dir) == NULL) { - packet_send_debug("Agent forwarding disabled: " + ssh_packet_send_debug(ssh, "Agent forwarding disabled: " "mkdtemp() failed: %.100s", strerror(errno)); restore_uid(); free(auth_sock_dir); @@ -220,7 +223,7 @@ auth_input_request_forwarding(struct passwd * pw) goto authsock_err; /* Allocate a channel for the authentication agent socket. */ - nc = channel_new("auth socket", + nc = channel_new(ssh, "auth socket", SSH_CHANNEL_AUTH_SOCKET, sock, sock, -1, CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 0, "auth socket", 1); @@ -230,7 +233,9 @@ auth_input_request_forwarding(struct passwd * pw) authsock_err: free(auth_sock_name); if (auth_sock_dir != NULL) { + temporarily_use_uid(pw); rmdir(auth_sock_dir); + restore_uid(); free(auth_sock_dir); } if (sock != -1) @@ -243,186 +248,138 @@ auth_input_request_forwarding(struct passwd * pw) static void display_loginmsg(void) { - if (buffer_len(&loginmsg) > 0) { - buffer_append(&loginmsg, "\0", 1); - printf("%s", (char *)buffer_ptr(&loginmsg)); - buffer_clear(&loginmsg); - } + int r; + + if (sshbuf_len(loginmsg) == 0) + return; + if ((r = sshbuf_put_u8(loginmsg, 0)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + printf("%s", (char *)sshbuf_ptr(loginmsg)); + sshbuf_reset(loginmsg); } -void -do_authenticated(Authctxt *authctxt) +static void +prepare_auth_info_file(struct passwd *pw, struct sshbuf *info) { - setproctitle("%s", authctxt->pw->pw_name); + int fd = -1, success = 0; - /* setup the channel layer */ - /* XXX - streamlocal? */ - if (no_port_forwarding_flag || - (options.allow_tcp_forwarding & FORWARD_LOCAL) == 0) - channel_disable_adm_local_opens(); - else - channel_permit_all_opens(); - - auth_debug_send(); - - if (compat20) - do_authenticated2(authctxt); - else - do_authenticated1(authctxt); + if (!options.expose_userauth_info || info == NULL) + return; - do_cleanup(authctxt); + temporarily_use_uid(pw); + auth_info_file = xstrdup("/tmp/sshauth.XXXXXXXXXXXXXXX"); + if ((fd = mkstemp(auth_info_file)) == -1) { + error("%s: mkstemp: %s", __func__, strerror(errno)); + goto out; + } + if (atomicio(vwrite, fd, sshbuf_mutable_ptr(info), + sshbuf_len(info)) != sshbuf_len(info)) { + error("%s: write: %s", __func__, strerror(errno)); + goto out; + } + if (close(fd) != 0) { + error("%s: close: %s", __func__, strerror(errno)); + goto out; + } + success = 1; + out: + if (!success) { + if (fd != -1) + close(fd); + free(auth_info_file); + auth_info_file = NULL; + } + restore_uid(); } -/* - * Prepares for an interactive session. This is called after the user has - * been successfully authenticated. During this message exchange, pseudo - * terminals are allocated, X11, TCP/IP, and authentication agent forwardings - * are requested, etc. - */ static void -do_authenticated1(Authctxt *authctxt) +set_fwdpermit_from_authopts(struct ssh *ssh, const struct sshauthopt *opts) { - 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; - - s = session_new(); - if (s == NULL) { - error("no more sessions"); - return; + char *tmp, *cp, *host; + int port; + size_t i; + + if ((options.allow_tcp_forwarding & FORWARD_LOCAL) != 0) { + channel_clear_permission(ssh, FORWARD_USER, FORWARD_LOCAL); + for (i = 0; i < auth_opts->npermitopen; i++) { + tmp = cp = xstrdup(auth_opts->permitopen[i]); + /* This shouldn't fail as it has already been checked */ + if ((host = hpdelim(&cp)) == NULL) + fatal("%s: internal error: hpdelim", __func__); + host = cleanhostname(host); + if (cp == NULL || (port = permitopen_port(cp)) < 0) + fatal("%s: internal error: permitopen port", + __func__); + channel_add_permission(ssh, + FORWARD_USER, FORWARD_LOCAL, host, port); + free(tmp); + } } - s->authctxt = authctxt; - s->pw = authctxt->pw; - - /* - * We stay in this loop until the client requests to execute a shell - * or a command. - */ - for (;;) { - success = 0; + if ((options.allow_tcp_forwarding & FORWARD_REMOTE) != 0) { + channel_clear_permission(ssh, FORWARD_USER, FORWARD_REMOTE); + for (i = 0; i < auth_opts->npermitlisten; i++) { + tmp = cp = xstrdup(auth_opts->permitlisten[i]); + /* This shouldn't fail as it has already been checked */ + if ((host = hpdelim(&cp)) == NULL) + fatal("%s: internal error: hpdelim", __func__); + host = cleanhostname(host); + if (cp == NULL || (port = permitopen_port(cp)) < 0) + fatal("%s: internal error: permitlisten port", + __func__); + channel_add_permission(ssh, + FORWARD_USER, FORWARD_REMOTE, host, port); + free(tmp); + } + } +} - /* Get a packet from the client. */ - type = packet_read(); - - /* Process the packet. */ - switch (type) { - case SSH_CMSG_REQUEST_COMPRESSION: - compression_level = packet_get_int(); - packet_check_eom(); - if (compression_level < 1 || compression_level > 9) { - packet_send_debug("Received invalid compression level %d.", - compression_level); - break; - } - if (options.compression == COMP_NONE) { - debug2("compression disabled"); - break; - } - /* Enable compression after we have responded with SUCCESS. */ - enable_compression_after_reply = 1; - success = 1; - break; +void +do_authenticated(struct ssh *ssh, Authctxt *authctxt) +{ + setproctitle("%s", authctxt->pw->pw_name); - case SSH_CMSG_REQUEST_PTY: - success = session_pty_req(s); - break; + auth_log_authopts("active", auth_opts, 0); - case SSH_CMSG_X11_REQUEST_FORWARDING: - s->auth_proto = packet_get_string(&proto_len); - s->auth_data = packet_get_string(&data_len); + /* setup the channel layer */ + /* XXX - streamlocal? */ + set_fwdpermit_from_authopts(ssh, auth_opts); - screen_flag = packet_get_protocol_flags() & - SSH_PROTOFLAG_SCREEN_NUMBER; - debug2("SSH_PROTOFLAG_SCREEN_NUMBER: %d", screen_flag); + if (!auth_opts->permit_port_forwarding_flag || + options.disable_forwarding) { + channel_disable_admin(ssh, FORWARD_LOCAL); + channel_disable_admin(ssh, FORWARD_REMOTE); + } else { + if ((options.allow_tcp_forwarding & FORWARD_LOCAL) == 0) + channel_disable_admin(ssh, FORWARD_LOCAL); + else + channel_permit_all(ssh, FORWARD_LOCAL); + if ((options.allow_tcp_forwarding & FORWARD_REMOTE) == 0) + channel_disable_admin(ssh, FORWARD_REMOTE); + else + channel_permit_all(ssh, FORWARD_REMOTE); + } + auth_debug_send(ssh); - if (packet_remaining() == 4) { - if (!screen_flag) - debug2("Buggy client: " - "X11 screen flag missing"); - s->screen = packet_get_int(); - } else { - s->screen = 0; - } - packet_check_eom(); - success = session_setup_x11fwd(s); - if (!success) { - free(s->auth_proto); - free(s->auth_data); - s->auth_proto = NULL; - s->auth_data = NULL; - } - break; + prepare_auth_info_file(authctxt->pw, authctxt->session_info); - case SSH_CMSG_AGENT_REQUEST_FORWARDING: - if (!options.allow_agent_forwarding || - no_agent_forwarding_flag || compat13) { - debug("Authentication agent forwarding not permitted for this authentication."); - break; - } - debug("Received authentication agent forwarding request."); - success = auth_input_request_forwarding(s->pw); - break; + do_authenticated2(ssh, authctxt); - case SSH_CMSG_PORT_FORWARD_REQUEST: - if (no_port_forwarding_flag) { - debug("Port forwarding not permitted for this authentication."); - break; - } - if (!(options.allow_tcp_forwarding & FORWARD_REMOTE)) { - debug("Port forwarding not permitted."); - break; - } - debug("Received TCP/IP port forwarding request."); - if (channel_input_port_forward_request(s->pw->pw_uid == 0, - &options.fwd_opts) < 0) { - debug("Port forwarding failed."); - break; - } - success = 1; - break; + do_cleanup(ssh, authctxt); +} - case SSH_CMSG_MAX_PACKET_SIZE: - if (packet_set_maxsize(packet_get_int()) > 0) - success = 1; - break; +/* Check untrusted xauth strings for metacharacters */ +static int +xauth_valid_string(const char *s) +{ + size_t i; - case SSH_CMSG_EXEC_SHELL: - case SSH_CMSG_EXEC_CMD: - if (type == SSH_CMSG_EXEC_CMD) { - command = packet_get_string(&dlen); - debug("Exec command '%.500s'", command); - if (do_exec(s, command) != 0) - packet_disconnect( - "command execution failed"); - free(command); - } else { - if (do_exec(s, NULL) != 0) - packet_disconnect( - "shell execution failed"); - } - packet_check_eom(); - session_close(s); - return; - - default: - /* - * Any unknown messages in this phase are ignored, - * and a failure message is returned. - */ - logit("Unknown packet type received after authentication: %d", type); - } - packet_start(success ? SSH_SMSG_SUCCESS : SSH_SMSG_FAILURE); - packet_send(); - packet_write_wait(); - - /* Enable compression now that we have replied if appropriate. */ - if (enable_compression_after_reply) { - enable_compression_after_reply = 0; - packet_start_compression(compression_level); - } + for (i = 0; s[i] != '\0'; i++) { + if (!isalnum((u_char)s[i]) && + s[i] != '.' && s[i] != ':' && s[i] != '/' && + s[i] != '-' && s[i] != '_') + return 0; } + return 1; } #define USE_PIPES 1 @@ -432,10 +389,9 @@ do_authenticated1(Authctxt *authctxt) * setting up file descriptors and such. */ int -do_exec_no_pty(Session *s, const char *command) +do_exec_no_pty(struct ssh *ssh, Session *s, const char *command) { pid_t pid; - #ifdef USE_PIPES int pin[2], pout[2], perr[2]; @@ -443,17 +399,17 @@ do_exec_no_pty(Session *s, const char *command) fatal("do_exec_no_pty: no session"); /* Allocate pipes for communicating with the program. */ - if (pipe(pin) < 0) { + if (pipe(pin) == -1) { error("%s: pipe in: %.100s", __func__, strerror(errno)); return -1; } - if (pipe(pout) < 0) { + if (pipe(pout) == -1) { error("%s: pipe out: %.100s", __func__, strerror(errno)); close(pin[0]); close(pin[1]); return -1; } - if (pipe(perr) < 0) { + if (pipe(perr) == -1) { error("%s: pipe err: %.100s", __func__, strerror(errno)); close(pin[0]); @@ -469,11 +425,11 @@ do_exec_no_pty(Session *s, const char *command) fatal("do_exec_no_pty: no session"); /* Uses socket pairs to communicate with the program. */ - if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) < 0) { + if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1) { error("%s: socketpair #1: %.100s", __func__, strerror(errno)); return -1; } - if (socketpair(AF_UNIX, SOCK_STREAM, 0, err) < 0) { + if (socketpair(AF_UNIX, SOCK_STREAM, 0, err) == -1) { error("%s: socketpair #2: %.100s", __func__, strerror(errno)); close(inout[0]); @@ -505,15 +461,11 @@ do_exec_no_pty(Session *s, const char *command) case 0: is_child = 1; - /* Child. Reinitialize the log since the pid has changed. */ - log_init(__progname, options.log_level, - options.log_facility, log_stderr); - /* * Create a new session and process group since the 4.4BSD * setlogin() affects the entire process group. */ - if (setsid() < 0) + if (setsid() == -1) error("setsid failed: %.100s", strerror(errno)); #ifdef USE_PIPES @@ -522,19 +474,19 @@ do_exec_no_pty(Session *s, const char *command) * pair, and make the child side the standard input. */ close(pin[1]); - if (dup2(pin[0], 0) < 0) + if (dup2(pin[0], 0) == -1) perror("dup2 stdin"); close(pin[0]); /* Redirect stdout. */ close(pout[0]); - if (dup2(pout[1], 1) < 0) + if (dup2(pout[1], 1) == -1) perror("dup2 stdout"); close(pout[1]); /* Redirect stderr. */ close(perr[0]); - if (dup2(perr[1], 2) < 0) + if (dup2(perr[1], 2) == -1) perror("dup2 stderr"); close(perr[1]); #else @@ -545,38 +497,30 @@ do_exec_no_pty(Session *s, const char *command) */ close(inout[1]); close(err[1]); - if (dup2(inout[0], 0) < 0) /* stdin */ + if (dup2(inout[0], 0) == -1) /* stdin */ perror("dup2 stdin"); - if (dup2(inout[0], 1) < 0) /* stdout (same as stdin) */ + if (dup2(inout[0], 1) == -1) /* stdout (same as stdin) */ perror("dup2 stdout"); close(inout[0]); - if (dup2(err[0], 2) < 0) /* stderr */ + if (dup2(err[0], 2) == -1) /* stderr */ perror("dup2 stderr"); close(err[0]); #endif - -#ifdef _UNICOS - cray_init_job(s->pw); /* set up cray jid and tmpdir */ -#endif - /* Do processing for the child (exec command etc). */ - do_child(s, command); + do_child(ssh, s, command); /* NOTREACHED */ default: break; } -#ifdef _UNICOS - signal(WJSIGNAL, cray_job_termination_handler); -#endif /* _UNICOS */ #ifdef HAVE_CYGWIN cygwin_set_impersonation_token(INVALID_HANDLE_VALUE); #endif s->pid = pid; /* Set interactive/non-interactive mode. */ - packet_set_interactive(s->display != NULL, + ssh_packet_set_interactive(ssh, s->display != NULL, options.ip_qos_interactive, options.ip_qos_bulk); /* @@ -584,7 +528,7 @@ do_exec_no_pty(Session *s, const char *command) * it to the user, otherwise multiple sessions may accumulate * multiple copies of the login messages. */ - buffer_clear(&loginmsg); + sshbuf_reset(loginmsg); #ifdef USE_PIPES /* We are the parent. Close the child sides of the pipes. */ @@ -592,14 +536,8 @@ do_exec_no_pty(Session *s, const char *command) close(pout[1]); close(perr[1]); - if (compat20) { - session_set_fds(s, pin[1], pout[0], perr[0], - s->is_subsystem, 0); - } 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]. */ - } + session_set_fds(ssh, s, pin[1], pout[0], perr[0], + s->is_subsystem, 0); #else /* We are the parent. Close the child sides of the socket pairs. */ close(inout[0]); @@ -609,13 +547,8 @@ do_exec_no_pty(Session *s, const char *command) * Enter the interactive session. Note: server_loop must be able to * handle the case that fdin and fdout are the same. */ - if (compat20) { - session_set_fds(s, inout[1], inout[1], err[1], - s->is_subsystem, 0); - } else { - server_loop(pid, inout[1], inout[1], err[1]); - /* server_loop has closed inout[1] and err[1]. */ - } + session_set_fds(ssh, s, inout[1], inout[1], err[1], + s->is_subsystem, 0); #endif return 0; } @@ -627,7 +560,7 @@ do_exec_no_pty(Session *s, const char *command) * lastlog, and other such operations. */ int -do_exec_pty(Session *s, const char *command) +do_exec_pty(struct ssh *ssh, Session *s, const char *command) { int fdout, ptyfd, ttyfd, ptymaster; pid_t pid; @@ -644,14 +577,14 @@ do_exec_pty(Session *s, const char *command) * Do this before forking (and cleanup in the child) so as to * detect and gracefully fail out-of-fd conditions. */ - if ((fdout = dup(ptyfd)) < 0) { + if ((fdout = dup(ptyfd)) == -1) { error("%s: dup #1: %s", __func__, strerror(errno)); close(ttyfd); close(ptyfd); return -1; } /* we keep a reference to the pty master */ - if ((ptymaster = dup(ptyfd)) < 0) { + if ((ptymaster = dup(ptyfd)) == -1) { error("%s: dup #2: %s", __func__, strerror(errno)); close(ttyfd); close(ptyfd); @@ -674,9 +607,6 @@ do_exec_pty(Session *s, const char *command) close(fdout); close(ptymaster); - /* 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); @@ -684,11 +614,11 @@ do_exec_pty(Session *s, const char *command) pty_make_controlling_tty(&ttyfd, s->tty); /* Redirect stdin/stdout/stderr from the pseudo tty. */ - if (dup2(ttyfd, 0) < 0) + if (dup2(ttyfd, 0) == -1) error("dup2 stdin: %s", strerror(errno)); - if (dup2(ttyfd, 1) < 0) + if (dup2(ttyfd, 1) == -1) error("dup2 stdout: %s", strerror(errno)); - if (dup2(ttyfd, 2) < 0) + if (dup2(ttyfd, 2) == -1) error("dup2 stderr: %s", strerror(errno)); /* Close the extra descriptor for the pseudo tty. */ @@ -696,30 +626,18 @@ do_exec_pty(Session *s, const char *command) /* record login, etc. similar to login(1) */ #ifndef HAVE_OSF_SIA - if (!(options.use_login && command == NULL)) { -#ifdef _UNICOS - cray_init_job(s->pw); /* set up cray jid and tmpdir */ -#endif /* _UNICOS */ - do_login(s, command); - } -# ifdef LOGIN_NEEDS_UTMPX - else - do_pre_login(s); -# endif + do_login(ssh, s, command); #endif /* * Do common processing for the child, such as execing * the command. */ - do_child(s, command); + do_child(ssh, s, command); /* NOTREACHED */ default: break; } -#ifdef _UNICOS - signal(WJSIGNAL, cray_job_termination_handler); -#endif /* _UNICOS */ #ifdef HAVE_CYGWIN cygwin_set_impersonation_token(INVALID_HANDLE_VALUE); #endif @@ -731,66 +649,35 @@ do_exec_pty(Session *s, const char *command) /* Enter interactive session. */ s->ptymaster = ptymaster; - packet_set_interactive(1, + ssh_packet_set_interactive(ssh, 1, options.ip_qos_interactive, options.ip_qos_bulk); - if (compat20) { - session_set_fds(s, ptyfd, fdout, -1, 1, 1); - } else { - server_loop(pid, ptyfd, fdout, -1); - /* server_loop _has_ closed ptyfd and fdout. */ - } + session_set_fds(ssh, s, ptyfd, fdout, -1, 1, 1); return 0; } -#ifdef LOGIN_NEEDS_UTMPX -static void -do_pre_login(Session *s) -{ - socklen_t fromlen; - struct sockaddr_storage from; - pid_t pid = getpid(); - - /* - * Get IP address of client. If the connection is not a socket, let - * the address be 0.0.0.0. - */ - memset(&from, 0, sizeof(from)); - fromlen = sizeof(from); - if (packet_connection_is_on_socket()) { - if (getpeername(packet_get_connection_in(), - (struct sockaddr *)&from, &fromlen) < 0) { - debug("getpeername: %.100s", strerror(errno)); - cleanup_exit(255); - } - } - - record_utmp_only(pid, s->tty, s->pw->pw_name, - get_remote_name_or_ip(utmp_len, options.use_dns), - (struct sockaddr *)&from, fromlen); -} -#endif - /* * This is called to fork and execute a command. If another command is * to be forced, execute that instead. */ int -do_exec(Session *s, const char *command) +do_exec(struct ssh *ssh, Session *s, const char *command) { int ret; - const char *forced = NULL; - char session_type[1024], *tty = NULL; + const char *forced = NULL, *tty = NULL; + char session_type[1024]; if (options.adm_forced_command) { original_command = command; command = options.adm_forced_command; forced = "(config)"; - } else if (forced_command) { + } else if (auth_opts->force_command != NULL) { original_command = command; - command = forced_command; + command = auth_opts->force_command; forced = "(key-option)"; } + s->forced = 0; if (forced != NULL) { + s->forced = 1; if (IS_INTERNAL_SFTP(command)) { s->is_subsystem = s->is_subsystem ? SUBSYSTEM_INT_SFTP : SUBSYSTEM_INT_SFTP_ERROR; @@ -814,13 +701,14 @@ do_exec(Session *s, const char *command) tty += 5; } - verbose("Starting session: %s%s%s for %s from %.200s port %d", + verbose("Starting session: %s%s%s for %s from %.200s port %d id %d", session_type, tty == NULL ? "" : " on ", tty == NULL ? "" : tty, s->pw->pw_name, - get_remote_ipaddr(), - get_remote_port()); + ssh_remote_ipaddr(ssh), + ssh_remote_port(ssh), + s->self); #ifdef SSH_AUDIT_EVENTS if (command != NULL) @@ -834,9 +722,9 @@ do_exec(Session *s, const char *command) } #endif if (s->ttyfd != -1) - ret = do_exec_pty(s, command); + ret = do_exec_pty(ssh, s, command); else - ret = do_exec_no_pty(s, command); + ret = do_exec_no_pty(ssh, s, command); original_command = NULL; @@ -845,14 +733,14 @@ do_exec(Session *s, const char *command) * it to the user, otherwise multiple sessions may accumulate * multiple copies of the login messages. */ - buffer_clear(&loginmsg); + sshbuf_reset(loginmsg); return ret; } /* administrative, login(1)-like work */ void -do_login(Session *s, const char *command) +do_login(struct ssh *ssh, Session *s, const char *command) { socklen_t fromlen; struct sockaddr_storage from; @@ -865,9 +753,9 @@ do_login(Session *s, const char *command) */ memset(&from, 0, sizeof(from)); fromlen = sizeof(from); - if (packet_connection_is_on_socket()) { - if (getpeername(packet_get_connection_in(), - (struct sockaddr *)&from, &fromlen) < 0) { + if (ssh_packet_connection_is_on_socket(ssh)) { + if (getpeername(ssh_packet_get_connection_in(ssh), + (struct sockaddr *)&from, &fromlen) == -1) { debug("getpeername: %.100s", strerror(errno)); cleanup_exit(255); } @@ -876,7 +764,7 @@ do_login(Session *s, const char *command) /* 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, + session_get_remote_name_or_ip(ssh, utmp_len, options.use_dns), (struct sockaddr *)&from, fromlen); @@ -951,87 +839,30 @@ check_quietlogin(Session *s, const char *command) } /* - * Sets the value of the given variable in the environment. If the variable - * already exists, its value is overridden. - */ -void -child_set_env(char ***envp, u_int *envsizep, const char *name, - const char *value) -{ - char **env; - u_int envsize; - u_int i, namelen; - - if (strchr(name, '=') != NULL) { - error("Invalid environment variable \"%.100s\"", name); - return; - } - - /* - * If we're passed an uninitialized list, allocate a single null - * entry before continuing. - */ - if (*envp == NULL && *envsizep == 0) { - *envp = xmalloc(sizeof(char *)); - *envp[0] = NULL; - *envsizep = 1; - } - - /* - * Find the slot where the value should be stored. If the variable - * already exists, we reuse the slot; otherwise we append a new slot - * at the end of the array, expanding if necessary. - */ - env = *envp; - namelen = strlen(name); - for (i = 0; env[i]; i++) - if (strncmp(env[i], name, namelen) == 0 && env[i][namelen] == '=') - break; - if (env[i]) { - /* Reuse the slot. */ - free(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; - } - /* Need to set the NULL pointer at end of array beyond the new slot. */ - env[i + 1] = NULL; - } - - /* Allocate space and format the variable in the appropriate slot. */ - env[i] = xmalloc(strlen(name) + 1 + strlen(value) + 1); - snprintf(env[i], strlen(name) + 1 + strlen(value) + 1, "%s=%s", name, value); -} - -/* * Reads environment variables from the given file and adds/overrides them * into the environment. If the file does not exist, this does nothing. * Otherwise, it must consist of empty lines, comments (line starts with '#') * and assignments of the form name=value. No other forms are allowed. + * If whitelist is not NULL, then it is interpreted as a pattern list and + * only variable names that match it will be accepted. */ static void read_environment_file(char ***env, u_int *envsize, - const char *filename) + const char *filename, const char *whitelist) { FILE *f; - char buf[4096]; - char *cp, *value; + char *line = NULL, *cp, *value; + size_t linesize = 0; u_int lineno = 0; f = fopen(filename, "r"); if (!f) return; - while (fgets(buf, sizeof(buf), f)) { + while (getline(&line, &linesize, f) != -1) { if (++lineno > 1000) fatal("Too many lines in environment file %s", filename); - for (cp = buf; *cp == ' ' || *cp == '\t'; cp++) + for (cp = line; *cp == ' ' || *cp == '\t'; cp++) ; if (!*cp || *cp == '#' || *cp == '\n') continue; @@ -1050,8 +881,12 @@ read_environment_file(char ***env, u_int *envsize, */ *value = '\0'; value++; + if (whitelist != NULL && + match_pattern_list(cp, whitelist, 0) != 1) + continue; child_set_env(env, envsize, cp, value); } + free(line); fclose(f); } @@ -1088,7 +923,8 @@ read_etc_default_login(char ***env, u_int *envsize, uid_t uid) * so we use a temporary environment and copy the variables we're * interested in. */ - read_environment_file(&tmpenv, &tmpenvsize, "/etc/default/login"); + read_environment_file(&tmpenv, &tmpenvsize, "/etc/default/login", + options.permit_user_env_whitelist); if (tmpenv == NULL) return; @@ -1110,8 +946,10 @@ read_etc_default_login(char ***env, u_int *envsize, uid_t uid) } #endif /* HAVE_ETC_DEFAULT_LOGIN */ -void -copy_environment(char **source, char ***env, u_int *envsize) +#if defined(USE_PAM) || defined(HAVE_CYGWIN) +static void +copy_environment_blacklist(char **source, char ***env, u_int *envsize, + const char *blacklist) { char *var_name, *var_val; int i; @@ -1127,19 +965,32 @@ copy_environment(char **source, char ***env, u_int *envsize) } *var_val++ = '\0'; - debug3("Copy environment: %s=%s", var_name, var_val); - child_set_env(env, envsize, var_name, var_val); + if (blacklist == NULL || + match_pattern_list(var_name, blacklist, 0) != 1) { + debug3("Copy environment: %s=%s", var_name, var_val); + child_set_env(env, envsize, var_name, var_val); + } free(var_name); } } +#endif /* defined(USE_PAM) || defined(HAVE_CYGWIN) */ + +#ifdef HAVE_CYGWIN +static void +copy_environment(char **source, char ***env, u_int *envsize) +{ + copy_environment_blacklist(source, env, envsize, NULL); +} +#endif static char ** -do_setup_env(Session *s, const char *shell) +do_setup_env(struct ssh *ssh, Session *s, const char *shell) { char buf[256]; + size_t n; u_int i, envsize; - char **env, *laddr; + char *ocp, *cp, *value, **env, *laddr; struct passwd *pw = s->pw; #if !defined (HAVE_LOGIN_CAP) && !defined (HAVE_CYGWIN) char *path = NULL; @@ -1166,103 +1017,62 @@ do_setup_env(Session *s, const char *shell) #ifdef GSSAPI /* Allow any GSSAPI methods that we've used to alter - * the childs environment as they see fit + * the child's environment as they see fit */ ssh_gssapi_do_child(&env, &envsize); #endif - if (!options.use_login) { - /* Set basic environment. */ - for (i = 0; i < s->num_env; i++) - child_set_env(&env, &envsize, s->env[i].name, - s->env[i].val); + /* Set basic environment. */ + for (i = 0; i < s->num_env; i++) + child_set_env(&env, &envsize, s->env[i].name, s->env[i].val); - child_set_env(&env, &envsize, "USER", pw->pw_name); - child_set_env(&env, &envsize, "LOGNAME", pw->pw_name); + child_set_env(&env, &envsize, "USER", pw->pw_name); + child_set_env(&env, &envsize, "LOGNAME", pw->pw_name); #ifdef _AIX - child_set_env(&env, &envsize, "LOGIN", pw->pw_name); + child_set_env(&env, &envsize, "LOGIN", pw->pw_name); #endif - child_set_env(&env, &envsize, "HOME", pw->pw_dir); + 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")); + if (setusercontext(lc, pw, pw->pw_uid, LOGIN_SETPATH) < 0) + child_set_env(&env, &envsize, "PATH", _PATH_STDPATH); + else + child_set_env(&env, &envsize, "PATH", getenv("PATH")); #else /* HAVE_LOGIN_CAP */ # ifndef HAVE_CYGWIN - /* - * There's no standard path on Windows. The path contains - * important components pointing to the system directories, - * needed for loading shared libraries. So the path better - * remains intact here. - */ + /* + * There's no standard path on Windows. The path contains + * important components pointing to the system directories, + * needed for loading shared libraries. So the path better + * remains intact here. + */ # ifdef HAVE_ETC_DEFAULT_LOGIN - read_etc_default_login(&env, &envsize, pw->pw_uid); - path = child_get_env(env, "PATH"); + read_etc_default_login(&env, &envsize, pw->pw_uid); + path = child_get_env(env, "PATH"); # endif /* HAVE_ETC_DEFAULT_LOGIN */ - if (path == NULL || *path == '\0') { - child_set_env(&env, &envsize, "PATH", - s->pw->pw_uid == 0 ? - SUPERUSER_PATH : _PATH_STDPATH); - } + if (path == NULL || *path == '\0') { + child_set_env(&env, &envsize, "PATH", + s->pw->pw_uid == 0 ? SUPERUSER_PATH : _PATH_STDPATH); + } # endif /* HAVE_CYGWIN */ #endif /* HAVE_LOGIN_CAP */ -#ifndef ANDROID +#if !defined(ANDROID) + if (!options.use_pam) { snprintf(buf, sizeof buf, "%.200s/%.50s", - _PATH_MAILDIR, pw->pw_name); + _PATH_MAILDIR, pw->pw_name); child_set_env(&env, &envsize, "MAIL", buf); + } #endif - /* Normal systems set SHELL by default. */ - child_set_env(&env, &envsize, "SHELL", shell); - } + /* Normal systems set SHELL by default. */ + child_set_env(&env, &envsize, "SHELL", shell); + if (getenv("TZ")) 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; - free(ce->s); - free(ce); - } - } - - /* SSH_CLIENT deprecated */ - snprintf(buf, sizeof buf, "%.50s %d %d", - get_remote_ipaddr(), get_remote_port(), get_local_port()); - child_set_env(&env, &envsize, "SSH_CLIENT", buf); - - laddr = get_local_ipaddr(packet_get_connection_in()); - snprintf(buf, sizeof buf, "%.50s %d %.50s %d", - get_remote_ipaddr(), get_remote_port(), laddr, get_local_port()); - free(laddr); - child_set_env(&env, &envsize, "SSH_CONNECTION", 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 (original_command) - child_set_env(&env, &envsize, "SSH_ORIGINAL_COMMAND", - original_command); - -#ifdef _UNICOS - if (cray_tmpdir[0] != '\0') - child_set_env(&env, &envsize, "TMPDIR", cray_tmpdir); -#endif /* _UNICOS */ /* * Since we clear KRB5CCNAME at startup, if it's set now then it @@ -1282,7 +1092,8 @@ do_setup_env(Session *s, const char *shell) if ((cp = getenv("AUTHSTATE")) != NULL) child_set_env(&env, &envsize, "AUTHSTATE", cp); - read_environment_file(&env, &envsize, "/etc/environment"); + read_environment_file(&env, &envsize, "/etc/environment", + options.permit_user_env_whitelist); } #endif #ifdef KRB5 @@ -1290,6 +1101,37 @@ do_setup_env(Session *s, const char *shell) child_set_env(&env, &envsize, "KRB5CCNAME", s->authctxt->krb5_ccname); #endif + if (auth_sock_name != NULL) + child_set_env(&env, &envsize, SSH_AUTHSOCKET_ENV_NAME, + auth_sock_name); + + + /* Set custom environment options from pubkey authentication. */ + if (options.permit_user_env) { + for (n = 0 ; n < auth_opts->nenv; n++) { + ocp = xstrdup(auth_opts->env[n]); + cp = strchr(ocp, '='); + if (*cp == '=') { + *cp = '\0'; + /* Apply PermitUserEnvironment whitelist */ + if (options.permit_user_env_whitelist == NULL || + match_pattern_list(ocp, + options.permit_user_env_whitelist, 0) == 1) + child_set_env(&env, &envsize, + ocp, cp + 1); + } + free(ocp); + } + } + + /* read $HOME/.ssh/environment. */ + if (options.permit_user_env) { + snprintf(buf, sizeof buf, "%.200s/.ssh/environment", + pw->pw_dir); + read_environment_file(&env, &envsize, buf, + options.permit_user_env_whitelist); + } + #ifdef USE_PAM /* * Pull in any environment variables that may have @@ -1298,26 +1140,57 @@ do_setup_env(Session *s, const char *shell) if (options.use_pam) { char **p; + /* + * Don't allow PAM-internal env vars to leak + * back into the session environment. + */ +#define PAM_ENV_BLACKLIST "SSH_AUTH_INFO*,SSH_CONNECTION*" p = fetch_pam_child_environment(); - copy_environment(p, &env, &envsize); + copy_environment_blacklist(p, &env, &envsize, + PAM_ENV_BLACKLIST); free_pam_environment(p); p = fetch_pam_environment(); - copy_environment(p, &env, &envsize); + copy_environment_blacklist(p, &env, &envsize, + PAM_ENV_BLACKLIST); free_pam_environment(p); } #endif /* USE_PAM */ - if (auth_sock_name != NULL) - child_set_env(&env, &envsize, SSH_AUTHSOCKET_ENV_NAME, - auth_sock_name); - - /* read $HOME/.ssh/environment. */ - if (options.permit_user_env && !options.use_login) { - snprintf(buf, sizeof buf, "%.200s/.ssh/environment", - strcmp(pw->pw_dir, "/") ? pw->pw_dir : ""); - read_environment_file(&env, &envsize, buf); + /* Environment specified by admin */ + for (i = 0; i < options.num_setenv; i++) { + cp = xstrdup(options.setenv[i]); + if ((value = strchr(cp, '=')) == NULL) { + /* shouldn't happen; vars are checked in servconf.c */ + fatal("Invalid config SetEnv: %s", options.setenv[i]); + } + *value++ = '\0'; + child_set_env(&env, &envsize, cp, value); } + + /* SSH_CLIENT deprecated */ + snprintf(buf, sizeof buf, "%.50s %d %d", + ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), + ssh_local_port(ssh)); + child_set_env(&env, &envsize, "SSH_CLIENT", buf); + + laddr = get_local_ipaddr(ssh_packet_get_connection_in(ssh)); + snprintf(buf, sizeof buf, "%.50s %d %.50s %d", + ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), + laddr, ssh_local_port(ssh)); + free(laddr); + child_set_env(&env, &envsize, "SSH_CONNECTION", buf); + + if (tun_fwd_ifnames != NULL) + child_set_env(&env, &envsize, "SSH_TUNNEL", tun_fwd_ifnames); + if (auth_info_file != NULL) + child_set_env(&env, &envsize, "SSH_USER_AUTH", auth_info_file); + if (s->ttyfd != -1) + child_set_env(&env, &envsize, "SSH_TTY", s->tty); + if (original_command) + child_set_env(&env, &envsize, "SSH_ORIGINAL_COMMAND", + original_command); + if (debug_flag) { /* dump the environment */ fprintf(stderr, "Environment:\n"); @@ -1332,7 +1205,7 @@ do_setup_env(Session *s, const char *shell) * first in this order). */ static void -do_rc_files(Session *s, const char *shell) +do_rc_files(struct ssh *ssh, Session *s, const char *shell) { FILE *f = NULL; char cmd[1024]; @@ -1344,7 +1217,7 @@ do_rc_files(Session *s, const char *shell) /* ignore _PATH_SSH_USER_RC for subsystems and admin forced commands */ if (!s->is_subsystem && options.adm_forced_command == NULL && - !no_user_rc && options.permit_user_rc && + auth_opts->permit_user_rc && options.permit_user_rc && stat(_PATH_SSH_USER_RC, &st) >= 0) { snprintf(cmd, sizeof cmd, "%s -c '%s %s'", shell, _PATH_BSHELL, _PATH_SSH_USER_RC); @@ -1425,10 +1298,10 @@ do_nologin(struct passwd *pw) /* /etc/nologin exists. Print its contents if we can and exit. */ logit("User %.100s not allowed because %s exists", pw->pw_name, nl); if ((f = fopen(nl, "r")) != NULL) { - while (fgets(buf, sizeof(buf), f)) - fputs(buf, stderr); - fclose(f); - } + while (fgets(buf, sizeof(buf), f)) + fputs(buf, stderr); + fclose(f); + } exit(254); } @@ -1443,7 +1316,7 @@ safely_chroot(const char *path, uid_t uid) char component[PATH_MAX]; struct stat st; - if (*path != '/') + if (!path_absolute(path)) fatal("chroot path does not begin at root"); if (strlen(path) >= sizeof(component)) fatal("chroot path too long"); @@ -1468,7 +1341,7 @@ safely_chroot(const char *path, uid_t uid) component, strerror(errno)); if (st.st_uid != 0 || (st.st_mode & 022) != 0) fatal("bad ownership or modes for chroot " - "directory %s\"%s\"", + "directory %s\"%s\"", cp == NULL ? "" : "component ", component); if (!S_ISDIR(st.st_mode)) fatal("chroot path %s\"%s\" is not a directory", @@ -1491,10 +1364,7 @@ safely_chroot(const char *path, uid_t uid) void do_setusercontext(struct passwd *pw) { - char *chroot_path, *tmp; -#ifdef USE_LIBIAF - int doing_chroot = 0; -#endif + char uidstr[32], *chroot_path, *tmp; platform_setusercontext(pw); @@ -1517,28 +1387,26 @@ do_setusercontext(struct passwd *pw) perror("initgroups"); exit(1); } -#if !defined(ANDROID) endgrent(); #endif -#endif platform_setusercontext_post_groups(pw); - if (options.chroot_directory != NULL && + if (!in_chroot && options.chroot_directory != NULL && strcasecmp(options.chroot_directory, "none") != 0) { tmp = tilde_expand_filename(options.chroot_directory, pw->pw_uid); + snprintf(uidstr, sizeof(uidstr), "%llu", + (unsigned long long)pw->pw_uid); chroot_path = percent_expand(tmp, "h", pw->pw_dir, - "u", pw->pw_name, (char *)NULL); + "u", pw->pw_name, "U", uidstr, (char *)NULL); safely_chroot(chroot_path, pw->pw_uid); free(tmp); free(chroot_path); /* Make sure we don't attempt to chroot again */ free(options.chroot_directory); options.chroot_directory = NULL; -#ifdef USE_LIBIAF - doing_chroot = 1; -#endif + in_chroot = 1; } #ifdef HAVE_LOGIN_CAP @@ -1553,16 +1421,16 @@ do_setusercontext(struct passwd *pw) (void) setusercontext(lc, pw, pw->pw_uid, LOGIN_SETUMASK); #else # ifdef USE_LIBIAF -/* In a chroot environment, the set_id() will always fail; typically - * because of the lack of necessary authentication services and runtime - * such as ./usr/lib/libiaf.so, ./usr/lib/libpam.so.1, and ./etc/passwd - * We skip it in the internal sftp chroot case. - * We'll lose auditing and ACLs but permanently_set_uid will - * take care of the rest. - */ - if ((doing_chroot == 0) && set_id(pw->pw_name) != 0) { - fatal("set_id(%s) Failed", pw->pw_name); - } + /* + * In a chroot environment, the set_id() will always fail; + * typically because of the lack of necessary authentication + * services and runtime such as ./usr/lib/libiaf.so, + * ./usr/lib/libpam.so.1, and ./etc/passwd We skip it in the + * internal sftp chroot case. We'll lose auditing and ACLs but + * permanently_set_uid will take care of the rest. + */ + if (!in_chroot && set_id(pw->pw_name) != 0) + fatal("set_id(%s) Failed", pw->pw_name); # endif /* USE_LIBIAF */ /* Permanently switch to the desired uid. */ permanently_set_uid(pw); @@ -1602,28 +1470,7 @@ do_pwchange(Session *s) } static void -launch_login(struct passwd *pw, const char *hostname) -{ - /* Launch login(1). */ - - execl(LOGIN_PROGRAM, "login", "-h", hostname, -#ifdef xxxLOGIN_NEEDS_TERM - (s->term ? s->term : "unknown"), -#endif /* LOGIN_NEEDS_TERM */ -#ifdef LOGIN_NO_ENDOPT - "-p", "-f", pw->pw_name, (char *)NULL); -#else - "-p", "-f", "--", pw->pw_name, (char *)NULL); -#endif - - /* Login couldn't be executed, die. */ - - perror("login"); - exit(1); -} - -static void -child_close_fds(void) +child_close_fds(struct ssh *ssh) { extern int auth_sock; @@ -1632,26 +1479,25 @@ child_close_fds(void) auth_sock = -1; } - if (packet_get_connection_in() == packet_get_connection_out()) - close(packet_get_connection_in()); + if (ssh_packet_get_connection_in(ssh) == + ssh_packet_get_connection_out(ssh)) + close(ssh_packet_get_connection_in(ssh)); else { - close(packet_get_connection_in()); - close(packet_get_connection_out()); + close(ssh_packet_get_connection_in(ssh)); + close(ssh_packet_get_connection_out(ssh)); } /* * Close all descriptors related to channels. They will still remain * open in the parent. */ /* XXX better use close-on-exec? -markus */ - channel_close_all(); + channel_close_all(ssh); -#if !defined(ANDROID) /* * Close any extra file descriptors. Note that there may still be * descriptors left by system functions. They will be closed later. */ endpwent(); -#endif /* * Close any extra open file descriptors so that we don't have them @@ -1669,60 +1515,52 @@ child_close_fds(void) */ #define ARGV_MAX 10 void -do_child(Session *s, const char *command) +do_child(struct ssh *ssh, Session *s, const char *command) { extern char **environ; - char **env; - char *argv[ARGV_MAX]; - const char *shell, *shell0, *hostname = NULL; + char **env, *argv[ARGV_MAX], remote_id[512]; + const char *shell, *shell0; struct passwd *pw = s->pw; int r = 0; + sshpkt_fmt_connection_id(ssh, remote_id, sizeof(remote_id)); + /* remove hostkey from the child's memory */ destroy_sensitive_data(); + ssh_packet_clear_keys(ssh); /* Force a password change */ if (s->authctxt->force_pwchange) { do_setusercontext(pw); - child_close_fds(); + child_close_fds(ssh); 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; - -#ifdef _UNICOS - cray_setup(pw->pw_uid, pw->pw_name, command); -#endif /* _UNICOS */ - /* * Login(1) does this as well, and it needs uid 0 for the "-h" * switch, so we let login(1) to this for us. */ - if (!options.use_login) { #ifdef HAVE_OSF_SIA - session_setup_sia(pw, s->ttyfd == -1 ? NULL : s->tty); - if (!check_quietlogin(s, command)) - do_motd(); + session_setup_sia(pw, s->ttyfd == -1 ? NULL : s->tty); + if (!check_quietlogin(s, command)) + do_motd(); #else /* HAVE_OSF_SIA */ - /* When PAM is enabled we rely on it to do the nologin check */ - if (!options.use_pam) - do_nologin(pw); - do_setusercontext(pw); - /* - * PAM session modules in do_setusercontext may have - * generated messages, so if this in an interactive - * login then display them too. - */ - if (!check_quietlogin(s, command)) - display_loginmsg(); + /* When PAM is enabled we rely on it to do the nologin check */ + if (!options.use_pam) + do_nologin(pw); + do_setusercontext(pw); + /* + * PAM session modules in do_setusercontext may have + * generated messages, so if this in an interactive + * login then display them too. + */ + if (!check_quietlogin(s, command)) + display_loginmsg(); #endif /* HAVE_OSF_SIA */ - } #ifdef USE_PAM - if (options.use_pam && !options.use_login && !is_pam_session_open()) { + if (options.use_pam && !is_pam_session_open()) { debug3("PAM session not opened, exiting"); display_loginmsg(); exit(254); @@ -1739,24 +1577,20 @@ do_child(Session *s, const char *command) * Make sure $SHELL points to the shell from the password file, * even if shell is overridden from login.conf */ - env = do_setup_env(s, shell); + env = do_setup_env(ssh, s, shell); #ifdef HAVE_LOGIN_CAP shell = login_getcapstr(lc, "shell", (char *)shell, (char *)shell); #endif - /* 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); /* * Close the connection descriptors; note that this is the child, and * the server will still have the socket open, and it is important * that we do not shutdown it. Note that the descriptors cannot be * closed before building the environment, as we call - * get_remote_ipaddr there. + * ssh_remote_ipaddr there. */ - child_close_fds(); + child_close_fds(ssh); /* * Must take new environment into use so that .ssh/rc, @@ -1791,29 +1625,30 @@ do_child(Session *s, const char *command) #endif /* Change current directory to the user's home directory. */ - if (chdir(pw->pw_dir) < 0) { + if (chdir(pw->pw_dir) == -1) { /* Suppress missing homedir warning for chroot case */ #ifdef HAVE_LOGIN_CAP r = login_getcapbool(lc, "requirehome", 0); #endif - if (r || options.chroot_directory == NULL || - strcasecmp(options.chroot_directory, "none") == 0) + if (r || !in_chroot) { fprintf(stderr, "Could not chdir to home " "directory %s: %s\n", pw->pw_dir, strerror(errno)); + } if (r) exit(1); } closefrom(STDERR_FILENO + 1); - if (!options.use_login) - do_rc_files(s, shell); + do_rc_files(ssh, s, shell); /* restore SIGPIPE for child */ - signal(SIGPIPE, SIG_DFL); + ssh_signal(SIGPIPE, SIG_DFL); if (s->is_subsystem == SUBSYSTEM_INT_SFTP_ERROR) { + error("Connection from %s: refusing non-sftp session", + remote_id); printf("This service allows sftp connections only.\n"); fflush(NULL); exit(1); @@ -1838,11 +1673,6 @@ do_child(Session *s, const char *command) fflush(NULL); - if (options.use_login) { - launch_login(pw, hostname); - /* NEVERREACHED */ - } - /* Get the last component of the shell name. */ if ((shell0 = strrchr(shell, '/')) != NULL) shell0++; @@ -1920,8 +1750,8 @@ session_new(void) return NULL; debug2("%s: allocate (allocated %d max %d)", __func__, sessions_nalloc, options.max_sessions); - tmp = xrealloc(sessions, sessions_nalloc + 1, - sizeof(*sessions)); + tmp = xrecallocarray(sessions, sessions_nalloc, + sessions_nalloc + 1, sizeof(*sessions)); if (tmp == NULL) { error("%s: cannot allocate %d sessions", __func__, sessions_nalloc + 1); @@ -2059,43 +1889,40 @@ session_by_pid(pid_t pid) } static int -session_window_change_req(Session *s) +session_window_change_req(struct ssh *ssh, 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(); + int r; + + if ((r = sshpkt_get_u32(ssh, &s->col)) != 0 || + (r = sshpkt_get_u32(ssh, &s->row)) != 0 || + (r = sshpkt_get_u32(ssh, &s->xpixel)) != 0 || + (r = sshpkt_get_u32(ssh, &s->ypixel)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) + sshpkt_fatal(ssh, r, "%s: parse packet", __func__); pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel); return 1; } static int -session_pty_req(Session *s) +session_pty_req(struct ssh *ssh, Session *s) { - u_int len; - int n_bytes; + int r; - if (no_pty_flag || !options.permit_tty) { - debug("Allocating a pty not permitted for this authentication."); + if (!auth_opts->permit_pty_flag || !options.permit_tty) { + debug("Allocating a pty not permitted for this connection."); return 0; } if (s->ttyfd != -1) { - packet_disconnect("Protocol error: you already have a pty."); + ssh_packet_disconnect(ssh, "Protocol error: you already have a pty."); 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->xpixel = packet_get_int(); - s->ypixel = packet_get_int(); + if ((r = sshpkt_get_cstring(ssh, &s->term, NULL)) != 0 || + (r = sshpkt_get_u32(ssh, &s->col)) != 0 || + (r = sshpkt_get_u32(ssh, &s->row)) != 0 || + (r = sshpkt_get_u32(ssh, &s->xpixel)) != 0 || + (r = sshpkt_get_u32(ssh, &s->ypixel)) != 0) + sshpkt_fatal(ssh, r, "%s: parse packet", __func__); if (strcmp(s->term, "") == 0) { free(s->term); @@ -2115,10 +1942,10 @@ session_pty_req(Session *s) } 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); + ssh_tty_parse_modes(ssh, s->ttyfd); + + if ((r = sshpkt_get_end(ssh)) != 0) + sshpkt_fatal(ssh, r, "%s: parse packet", __func__); if (!use_privsep) pty_setowner(s->pw, s->tty); @@ -2126,22 +1953,21 @@ session_pty_req(Session *s) /* Set window size from the packet. */ pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel); - packet_check_eom(); session_proctitle(s); return 1; } static int -session_subsystem_req(Session *s) +session_subsystem_req(struct ssh *ssh, Session *s) { struct stat st; - u_int len; - int success = 0; + int r, success = 0; char *prog, *cmd; u_int i; - s->subsys = packet_get_string(&len); - packet_check_eom(); + if ((r = sshpkt_get_cstring(ssh, &s->subsys, NULL)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) + sshpkt_fatal(ssh, r, "%s: parse packet", __func__); debug2("subsystem request for %.100s by user %s", s->subsys, s->pw->pw_name); @@ -2153,13 +1979,13 @@ session_subsystem_req(Session *s) s->is_subsystem = SUBSYSTEM_INT_SFTP; debug("subsystem: %s", prog); } else { - if (stat(prog, &st) < 0) + if (stat(prog, &st) == -1) debug("subsystem: cannot stat %s: %s", prog, strerror(errno)); s->is_subsystem = SUBSYSTEM_EXT; debug("subsystem: exec() %s", cmd); } - success = do_exec(s, cmd) == 0; + success = do_exec(ssh, s, cmd) == 0; break; } } @@ -2172,22 +1998,32 @@ session_subsystem_req(Session *s) } static int -session_x11_req(Session *s) +session_x11_req(struct ssh *ssh, Session *s) { - int success; + int r, success; + u_char single_connection = 0; if (s->auth_proto != NULL || s->auth_data != NULL) { error("session_x11_req: session %d: " "x11 forwarding already active", s->self); return 0; } - 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(); + if ((r = sshpkt_get_u8(ssh, &single_connection)) != 0 || + (r = sshpkt_get_cstring(ssh, &s->auth_proto, NULL)) != 0 || + (r = sshpkt_get_cstring(ssh, &s->auth_data, NULL)) != 0 || + (r = sshpkt_get_u32(ssh, &s->screen)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) + sshpkt_fatal(ssh, r, "%s: parse packet", __func__); + + s->single_connection = single_connection; - success = session_setup_x11fwd(s); + if (xauth_valid_string(s->auth_proto) && + xauth_valid_string(s->auth_data)) + success = session_setup_x11fwd(ssh, s); + else { + success = 0; + error("Invalid X11 forwarding data"); + } if (!success) { free(s->auth_proto); free(s->auth_data); @@ -2198,45 +2034,56 @@ session_x11_req(Session *s) } static int -session_shell_req(Session *s) +session_shell_req(struct ssh *ssh, Session *s) { - packet_check_eom(); - return do_exec(s, NULL) == 0; + int r; + + if ((r = sshpkt_get_end(ssh)) != 0) + sshpkt_fatal(ssh, r, "%s: parse packet", __func__); + return do_exec(ssh, s, NULL) == 0; } static int -session_exec_req(Session *s) +session_exec_req(struct ssh *ssh, Session *s) { - u_int len, success; + u_int success; + int r; + char *command = NULL; + + if ((r = sshpkt_get_cstring(ssh, &command, NULL)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) + sshpkt_fatal(ssh, r, "%s: parse packet", __func__); - char *command = packet_get_string(&len); - packet_check_eom(); - success = do_exec(s, command) == 0; + success = do_exec(ssh, s, command) == 0; free(command); return success; } static int -session_break_req(Session *s) +session_break_req(struct ssh *ssh, Session *s) { + int r; - packet_get_int(); /* ignored */ - packet_check_eom(); + if ((r = sshpkt_get_u32(ssh, NULL)) != 0 || /* ignore */ + (r = sshpkt_get_end(ssh)) != 0) + sshpkt_fatal(ssh, r, "%s: parse packet", __func__); - if (s->ptymaster == -1 || tcsendbreak(s->ptymaster, 0) < 0) + if (s->ptymaster == -1 || tcsendbreak(s->ptymaster, 0) == -1) return 0; return 1; } static int -session_env_req(Session *s) +session_env_req(struct ssh *ssh, Session *s) { char *name, *val; - u_int name_len, val_len, i; + u_int i; + int r; - name = packet_get_cstring(&name_len); - val = packet_get_cstring(&val_len); - packet_check_eom(); + if ((r = sshpkt_get_cstring(ssh, &name, NULL)) != 0 || + (r = sshpkt_get_cstring(ssh, &val, NULL)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) + sshpkt_fatal(ssh, r, "%s: parse packet", __func__); /* Don't set too many environment variables */ if (s->num_env > 128) { @@ -2247,8 +2094,8 @@ session_env_req(Session *s) for (i = 0; i < options.num_accept_env; i++) { if (match_pattern(name, options.accept_env[i])) { debug2("Setting env %d: %s=%s", s->num_env, name, val); - s->env = xrealloc(s->env, s->num_env + 1, - sizeof(*s->env)); + s->env = xrecallocarray(s->env, s->num_env, + s->num_env + 1, sizeof(*s->env)); s->env[s->num_env].name = name; s->env[s->num_env].val = val; s->num_env++; @@ -2263,35 +2110,110 @@ session_env_req(Session *s) return (0); } +/* + * Conversion of signals from ssh channel request names. + * Subset of signals from RFC 4254 section 6.10C, with SIGINFO as + * local extension. + */ static int -session_auth_agent_req(Session *s) +name2sig(char *name) +{ +#define SSH_SIG(x) if (strcmp(name, #x) == 0) return SIG ## x + SSH_SIG(HUP); + SSH_SIG(INT); + SSH_SIG(KILL); + SSH_SIG(QUIT); + SSH_SIG(TERM); + SSH_SIG(USR1); + SSH_SIG(USR2); +#undef SSH_SIG +#ifdef SIGINFO + if (strcmp(name, "INFO@openssh.com") == 0) + return SIGINFO; +#endif + return -1; +} + +static int +session_signal_req(struct ssh *ssh, Session *s) +{ + char *signame = NULL; + int r, sig, success = 0; + + if ((r = sshpkt_get_cstring(ssh, &signame, NULL)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) { + error("%s: parse packet: %s", __func__, ssh_err(r)); + goto out; + } + if ((sig = name2sig(signame)) == -1) { + error("%s: unsupported signal \"%s\"", __func__, signame); + goto out; + } + if (s->pid <= 0) { + error("%s: no pid for session %d", __func__, s->self); + goto out; + } + if (s->forced || s->is_subsystem) { + error("%s: refusing to send signal %s to %s session", __func__, + signame, s->forced ? "forced-command" : "subsystem"); + goto out; + } + if (!use_privsep || mm_is_monitor()) { + error("%s: session signalling requires privilege separation", + __func__); + goto out; + } + + debug("%s: signal %s, killpg(%ld, %d)", __func__, signame, + (long)s->pid, sig); + temporarily_use_uid(s->pw); + r = killpg(s->pid, sig); + restore_uid(); + if (r != 0) { + error("%s: killpg(%ld, %d): %s", __func__, (long)s->pid, + sig, strerror(errno)); + goto out; + } + + /* success */ + success = 1; + out: + free(signame); + return success; +} + +static int +session_auth_agent_req(struct ssh *ssh, Session *s) { static int called = 0; - packet_check_eom(); - if (no_agent_forwarding_flag || !options.allow_agent_forwarding) { - debug("session_auth_agent_req: no_agent_forwarding_flag"); + int r; + + if ((r = sshpkt_get_end(ssh)) != 0) + sshpkt_fatal(ssh, r, "%s: parse packet", __func__); + if (!auth_opts->permit_agent_forwarding_flag || + !options.allow_agent_forwarding) { + debug("%s: agent forwarding disabled", __func__); return 0; } if (called) { return 0; } else { called = 1; - return auth_input_request_forwarding(s->pw); + return auth_input_request_forwarding(ssh, s->pw); } } int -session_input_channel_req(Channel *c, const char *rtype) +session_input_channel_req(struct ssh *ssh, Channel *c, const char *rtype) { int success = 0; Session *s; if ((s = session_by_channel(c->self)) == NULL) { - logit("session_input_channel_req: no session %d req %.100s", - c->self, rtype); + logit("%s: no session %d req %.100s", __func__, c->self, rtype); return 0; } - debug("session_input_channel_req: session %d req %s", s->self, rtype); + debug("%s: session %d req %s", __func__, s->self, rtype); /* * a session is in LARVAL state until a shell, a command @@ -2299,43 +2221,43 @@ session_input_channel_req(Channel *c, const char *rtype) */ if (c->type == SSH_CHANNEL_LARVAL) { if (strcmp(rtype, "shell") == 0) { - success = session_shell_req(s); + success = session_shell_req(ssh, s); } else if (strcmp(rtype, "exec") == 0) { - success = session_exec_req(s); + success = session_exec_req(ssh, s); } else if (strcmp(rtype, "pty-req") == 0) { - success = session_pty_req(s); + success = session_pty_req(ssh, s); } else if (strcmp(rtype, "x11-req") == 0) { - success = session_x11_req(s); + success = session_x11_req(ssh, s); } else if (strcmp(rtype, "auth-agent-req@openssh.com") == 0) { - success = session_auth_agent_req(s); + success = session_auth_agent_req(ssh, s); } else if (strcmp(rtype, "subsystem") == 0) { - success = session_subsystem_req(s); + success = session_subsystem_req(ssh, s); } else if (strcmp(rtype, "env") == 0) { - success = session_env_req(s); + success = session_env_req(ssh, s); } } if (strcmp(rtype, "window-change") == 0) { - success = session_window_change_req(s); + success = session_window_change_req(ssh, s); } else if (strcmp(rtype, "break") == 0) { - success = session_break_req(s); + success = session_break_req(ssh, s); + } else if (strcmp(rtype, "signal") == 0) { + success = session_signal_req(ssh, s); } return success; } void -session_set_fds(Session *s, int fdin, int fdout, int fderr, int ignore_fderr, - int is_tty) +session_set_fds(struct ssh *ssh, Session *s, + int fdin, int fdout, int fderr, int ignore_fderr, int is_tty) { - if (!compat20) - fatal("session_set_fds: called for proto != 2.0"); /* * now that have a child and a pipe to the child, * we can activate our channel and register the fd's */ if (s->chanid == -1) fatal("no channel for session %d", s->self); - channel_set_fds(s->chanid, + channel_set_fds(ssh, s->chanid, fdout, fdin, fderr, ignore_fderr ? CHAN_EXTENDED_IGNORE : CHAN_EXTENDED_READ, 1, is_tty, CHAN_SES_WINDOW_DEFAULT); @@ -2349,13 +2271,13 @@ void session_pty_cleanup2(Session *s) { if (s == NULL) { - error("session_pty_cleanup: no session"); + error("%s: no session", __func__); return; } if (s->ttyfd == -1) return; - debug("session_pty_cleanup: session %d release %s", s->self, s->tty); + debug("%s: session %d release %s", __func__, s->self, s->tty); /* Record that the user has logged out. */ if (s->pid != 0) @@ -2370,7 +2292,7 @@ session_pty_cleanup2(Session *s) * the pty cleanup, so that another process doesn't get this pty * while we're still cleaning up. */ - if (s->ptymaster != -1 && close(s->ptymaster) < 0) + if (s->ptymaster != -1 && close(s->ptymaster) == -1) error("close(s->ptymaster/%d): %s", s->ptymaster, strerror(errno)); @@ -2406,40 +2328,40 @@ sig2name(int sig) } static void -session_close_x11(int id) +session_close_x11(struct ssh *ssh, int id) { Channel *c; - if ((c = channel_by_id(id)) == NULL) { - debug("session_close_x11: x11 channel %d missing", id); + if ((c = channel_by_id(ssh, id)) == NULL) { + debug("%s: x11 channel %d missing", __func__, id); } else { /* Detach X11 listener */ - debug("session_close_x11: detach x11 channel %d", id); - channel_cancel_cleanup(id); + debug("%s: detach x11 channel %d", __func__, id); + channel_cancel_cleanup(ssh, id); if (c->ostate != CHAN_OUTPUT_CLOSED) - chan_mark_dead(c); + chan_mark_dead(ssh, c); } } static void -session_close_single_x11(int id, void *arg) +session_close_single_x11(struct ssh *ssh, int id, void *arg) { Session *s; u_int i; - debug3("session_close_single_x11: channel %d", id); - channel_cancel_cleanup(id); + debug3("%s: channel %d", __func__, id); + channel_cancel_cleanup(ssh, id); if ((s = session_by_x11_channel(id)) == NULL) - fatal("session_close_single_x11: no x11 channel %d", id); + fatal("%s: no x11 channel %d", __func__, id); for (i = 0; s->x11_chanids[i] != -1; i++) { - debug("session_close_single_x11: session %d: " - "closing channel %d", s->self, s->x11_chanids[i]); + debug("%s: session %d: closing channel %d", + __func__, s->self, s->x11_chanids[i]); /* * The channel "id" is already closing, but make sure we * close all of its siblings. */ if (s->x11_chanids[i] != id) - session_close_x11(s->x11_chanids[i]); + session_close_x11(ssh, s->x11_chanids[i]); } free(s->x11_chanids); s->x11_chanids = NULL; @@ -2454,45 +2376,47 @@ session_close_single_x11(int id, void *arg) } static void -session_exit_message(Session *s, int status) +session_exit_message(struct ssh *ssh, Session *s, int status) { Channel *c; + int r; - if ((c = channel_lookup(s->chanid)) == NULL) - fatal("session_exit_message: session %d: no channel %d", - s->self, s->chanid); - debug("session_exit_message: session %d channel %d pid %ld", - s->self, s->chanid, (long)s->pid); + if ((c = channel_lookup(ssh, s->chanid)) == NULL) + fatal("%s: session %d: no channel %d", + __func__, s->self, s->chanid); + debug("%s: session %d channel %d pid %ld", + __func__, s->self, s->chanid, (long)s->pid); if (WIFEXITED(status)) { - channel_request_start(s->chanid, "exit-status", 0); - packet_put_int(WEXITSTATUS(status)); - packet_send(); + channel_request_start(ssh, s->chanid, "exit-status", 0); + if ((r = sshpkt_put_u32(ssh, WEXITSTATUS(status))) != 0 || + (r = sshpkt_send(ssh)) != 0) + sshpkt_fatal(ssh, r, "%s: exit reply", __func__); } else if (WIFSIGNALED(status)) { - channel_request_start(s->chanid, "exit-signal", 0); - packet_put_cstring(sig2name(WTERMSIG(status))); -#ifdef WCOREDUMP - packet_put_char(WCOREDUMP(status)? 1 : 0); -#else /* WCOREDUMP */ - packet_put_char(0); -#endif /* WCOREDUMP */ - packet_put_cstring(""); - packet_put_cstring(""); - packet_send(); + channel_request_start(ssh, s->chanid, "exit-signal", 0); +#ifndef WCOREDUMP +# define WCOREDUMP(x) (0) +#endif + if ((r = sshpkt_put_cstring(ssh, sig2name(WTERMSIG(status)))) != 0 || + (r = sshpkt_put_u8(ssh, WCOREDUMP(status)? 1 : 0)) != 0 || + (r = sshpkt_put_cstring(ssh, "")) != 0 || + (r = sshpkt_put_cstring(ssh, "")) != 0 || + (r = sshpkt_send(ssh)) != 0) + sshpkt_fatal(ssh, r, "%s: exit reply", __func__); } else { /* Some weird exit cause. Just exit. */ - packet_disconnect("wait returned status %04x.", status); + ssh_packet_disconnect(ssh, "wait returned status %04x.", status); } /* disconnect channel */ - debug("session_exit_message: release channel %d", s->chanid); + debug("%s: release channel %d", __func__, s->chanid); /* * Adjust cleanup callback attachment to send close messages when * the channel gets EOF. The session will be then be closed - * by session_close_by_channel when the childs close their fds. + * by session_close_by_channel when the child sessions close their fds. */ - channel_register_cleanup(c->self, session_close_by_channel, 1); + channel_register_cleanup(ssh, c->self, session_close_by_channel, 1); /* * emulate a write failure with 'chan_write_failed', nobody will be @@ -2501,15 +2425,20 @@ session_exit_message(Session *s, int status) * be some more data waiting in the pipe. */ if (c->ostate != CHAN_OUTPUT_CLOSED) - chan_write_failed(c); + chan_write_failed(ssh, c); } void -session_close(Session *s) +session_close(struct ssh *ssh, Session *s) { u_int i; - debug("session_close: session %d pid %ld", s->self, (long)s->pid); + verbose("Close session: user %s from %.200s port %d id %d", + s->pw->pw_name, + ssh_remote_ipaddr(ssh), + ssh_remote_port(ssh), + s->self); + if (s->ttyfd != -1) session_pty_cleanup(s); free(s->term); @@ -2531,16 +2460,15 @@ session_close(Session *s) } void -session_close_by_pid(pid_t pid, int status) +session_close_by_pid(struct ssh *ssh, pid_t pid, int status) { Session *s = session_by_pid(pid); if (s == NULL) { - debug("session_close_by_pid: no session for pid %ld", - (long)pid); + debug("%s: no session for pid %ld", __func__, (long)pid); return; } if (s->chanid != -1) - session_exit_message(s, status); + session_exit_message(ssh, s, status); if (s->ttyfd != -1) session_pty_cleanup(s); s->pid = 0; @@ -2551,19 +2479,19 @@ session_close_by_pid(pid_t pid, int status) * the session 'child' itself dies */ void -session_close_by_channel(int id, void *arg) +session_close_by_channel(struct ssh *ssh, int id, void *arg) { Session *s = session_by_channel(id); u_int i; if (s == NULL) { - debug("session_close_by_channel: no session for id %d", id); + debug("%s: no session for id %d", __func__, id); return; } - debug("session_close_by_channel: channel %d child %ld", - id, (long)s->pid); + debug("%s: channel %d child %ld", __func__, id, (long)s->pid); if (s->pid != 0) { - debug("session_close_by_channel: channel %d: has child", id); + debug("%s: channel %d: has child, ttyfd %d", + __func__, id, s->ttyfd); /* * delay detach of session, but release pty, since * the fd's to the child are already closed @@ -2573,22 +2501,22 @@ session_close_by_channel(int id, void *arg) return; } /* detach by removing callback */ - channel_cancel_cleanup(s->chanid); + channel_cancel_cleanup(ssh, s->chanid); /* Close any X11 listeners associated with this session */ if (s->x11_chanids != NULL) { for (i = 0; s->x11_chanids[i] != -1; i++) { - session_close_x11(s->x11_chanids[i]); + session_close_x11(ssh, s->x11_chanids[i]); s->x11_chanids[i] = -1; } } s->chanid = -1; - session_close(s); + session_close(ssh, s); } void -session_destroy_all(void (*closefunc)(Session *)) +session_destroy_all(struct ssh *ssh, void (*closefunc)(Session *)) { int i; for (i = 0; i < sessions_nalloc; i++) { @@ -2597,7 +2525,7 @@ session_destroy_all(void (*closefunc)(Session *)) if (closefunc != NULL) closefunc(s); else - session_close(s); + session_close(ssh, s); } } } @@ -2640,15 +2568,15 @@ session_proctitle(Session *s) } int -session_setup_x11fwd(Session *s) +session_setup_x11fwd(struct ssh *ssh, Session *s) { struct stat st; char display[512], auth_display[512]; char hostname[NI_MAXHOST]; u_int i; - if (no_x11_forwarding_flag) { - packet_send_debug("X11 forwarding disabled in user configuration file."); + if (!auth_opts->permit_x11_forwarding_flag) { + ssh_packet_send_debug(ssh, "X11 forwarding disabled by key options."); return 0; } if (!options.x11_forwarding) { @@ -2657,31 +2585,26 @@ session_setup_x11fwd(Session *s) } if (options.xauth_location == NULL || (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."); + ssh_packet_send_debug(ssh, "No xauth program; cannot forward X11."); return 0; } if (s->display != NULL) { debug("X11 display already set."); return 0; } - if (x11_create_display_inet(options.x11_display_offset, + if (x11_create_display_inet(ssh, options.x11_display_offset, options.x11_use_localhost, s->single_connection, &s->display_number, &s->x11_chanids) == -1) { debug("x11_create_display_inet failed."); return 0; } for (i = 0; s->x11_chanids[i] != -1; i++) { - channel_register_cleanup(s->x11_chanids[i], + channel_register_cleanup(ssh, s->x11_chanids[i], session_close_single_x11, 0); } /* Set up a suitable value for the DISPLAY variable. */ - if (gethostname(hostname, sizeof(hostname)) < 0) + if (gethostname(hostname, sizeof(hostname)) == -1) fatal("gethostname: %.100s", strerror(errno)); /* * auth_display must be used as the displayname when the @@ -2703,7 +2626,7 @@ session_setup_x11fwd(Session *s) he = gethostbyname(hostname); if (he == NULL) { error("Can't get IP address for X11 DISPLAY."); - packet_send_debug("Can't get IP address for X11 DISPLAY."); + ssh_packet_send_debug(ssh, "Can't get IP address for X11 DISPLAY."); return 0; } memcpy(&my_addr, he->h_addr_list[0], sizeof(struct in_addr)); @@ -2721,13 +2644,13 @@ session_setup_x11fwd(Session *s) } static void -do_authenticated2(Authctxt *authctxt) +do_authenticated2(struct ssh *ssh, Authctxt *authctxt) { - server_loop2(authctxt); + server_loop2(ssh, authctxt); } void -do_cleanup(Authctxt *authctxt) +do_cleanup(struct ssh *ssh, Authctxt *authctxt) { static int called = 0; @@ -2762,17 +2685,41 @@ do_cleanup(Authctxt *authctxt) #endif #ifdef GSSAPI - if (compat20 && options.gss_cleanup_creds) + if (options.gss_cleanup_creds) ssh_gssapi_cleanup_creds(); #endif /* remove agent socket */ auth_sock_cleanup_proc(authctxt->pw); + /* remove userauth info */ + if (auth_info_file != NULL) { + temporarily_use_uid(authctxt->pw); + unlink(auth_info_file); + restore_uid(); + free(auth_info_file); + auth_info_file = NULL; + } + /* * 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); + session_destroy_all(ssh, session_pty_cleanup2); } + +/* Return a name for the remote host that fits inside utmp_size */ + +const char * +session_get_remote_name_or_ip(struct ssh *ssh, u_int utmp_size, int use_dns) +{ + const char *remote = ""; + + if (utmp_size > 0) + remote = auth_get_canonical_hostname(ssh, use_dns); + if (utmp_size == 0 || strlen(remote) > utmp_size) + remote = ssh_remote_ipaddr(ssh); + return remote; +} + |