X-Git-Url: http://andersk.mit.edu/gitweb/openssh.git/blobdiff_plain/022487ce01e98352740bbf211fc36604a7743c71..HEAD:/sshd.c diff --git a/sshd.c b/sshd.c index 7f268526..bf2e76cc 100644 --- a/sshd.c +++ b/sshd.c @@ -1,3 +1,4 @@ +/* $OpenBSD: sshd.c,v 1.372 2010/01/29 00:20:41 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -42,39 +43,68 @@ */ #include "includes.h" -RCSID("$OpenBSD: sshd.c,v 1.307 2005/01/21 08:32:02 otto Exp $"); + +#include +#include +#include +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef HAVE_SYS_TIME_H +# include +#endif +#include "openbsd-compat/sys-tree.h" +#include "openbsd-compat/sys-queue.h" +#include + +#include +#include +#include +#ifdef HAVE_PATHS_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include #include +#include "openbsd-compat/openssl-compat.h" + #ifdef HAVE_SECUREWARE #include #include #endif +#include "xmalloc.h" #include "ssh.h" #include "ssh1.h" #include "ssh2.h" -#include "xmalloc.h" #include "rsa.h" #include "sshpty.h" #include "packet.h" #include "log.h" +#include "buffer.h" #include "servconf.h" #include "uidswap.h" #include "compat.h" -#include "buffer.h" -#include "bufaux.h" #include "cipher.h" -#include "kex.h" #include "key.h" +#include "kex.h" #include "dh.h" #include "myproposal.h" #include "authfile.h" #include "pathnames.h" #include "atomicio.h" #include "canohost.h" +#include "hostfile.h" #include "auth.h" #include "misc.h" #include "msg.h" @@ -83,14 +113,18 @@ RCSID("$OpenBSD: sshd.c,v 1.307 2005/01/21 08:32:02 otto Exp $"); #include "session.h" #include "monitor_mm.h" #include "monitor.h" +#ifdef GSSAPI +#include "ssh-gss.h" +#endif #include "monitor_wrap.h" -#include "monitor_fdpass.h" +#include "roaming.h" +#include "version.h" #ifdef LIBWRAP #include #include -int allow_severity = LOG_INFO; -int deny_severity = LOG_WARNING; +int allow_severity; +int deny_severity; #endif /* LIBWRAP */ #ifndef O_NOCTTY @@ -201,15 +235,21 @@ int *startup_pipes = NULL; int startup_pipe; /* in child */ /* variables used for privilege separation */ -int use_privsep; +int use_privsep = -1; struct monitor *pmonitor = NULL; /* global authentication context */ Authctxt *the_authctxt = NULL; +/* sshd_config buffer */ +Buffer cfg; + /* message to be displayed after login */ Buffer loginmsg; +/* Unprivileged user */ +struct passwd *privsep_pw = NULL; + /* Prototypes for various functions defined later in this file. */ void destroy_sensitive_data(void); void demote_sensitive_data(void); @@ -246,6 +286,8 @@ close_startup_pipes(void) * the effect is to reread the configuration file (and to regenerate * the server key). */ + +/*ARGSUSED*/ static void sighup_handler(int sig) { @@ -266,6 +308,8 @@ sighup_restart(void) logit("Received SIGHUP; restarting."); close_listen_socks(); close_startup_pipes(); + alarm(0); /* alarm timer persists across exec */ + signal(SIGHUP, SIG_IGN); /* will be restored after exec */ execv(saved_argv[0], saved_argv); logit("RESTART FAILED: av[0]='%.100s', error: %.100s.", saved_argv[0], strerror(errno)); @@ -275,6 +319,7 @@ sighup_restart(void) /* * Generic signal handler for terminating signals in the master daemon. */ +/*ARGSUSED*/ static void sigterm_handler(int sig) { @@ -285,6 +330,7 @@ sigterm_handler(int sig) * SIGCHLD handler. This is called whenever a child dies. This will then * reap any zombies left by exited children. */ +/*ARGSUSED*/ static void main_sigchld_handler(int sig) { @@ -303,16 +349,15 @@ main_sigchld_handler(int sig) /* * Signal handler for the alarm after the login grace period has expired. */ +/*ARGSUSED*/ static void grace_alarm_handler(int sig) { - /* XXX no idea how fix this signal handler */ - if (use_privsep && pmonitor != NULL && pmonitor->m_pid > 0) kill(pmonitor->m_pid, SIGALRM); /* Log error and exit. */ - fatal("Timeout before authentication for %s", get_remote_ipaddr()); + sigdie("Timeout before authentication for %s", get_remote_ipaddr()); } /* @@ -325,9 +370,6 @@ grace_alarm_handler(int sig) static void generate_ephemeral_server_key(void) { - u_int32_t rnd = 0; - int i; - verbose("Generating %s%d bit RSA key.", sensitive_data.server_key ? "new " : "", options.server_key_bits); if (sensitive_data.server_key != NULL) @@ -336,15 +378,11 @@ generate_ephemeral_server_key(void) options.server_key_bits); verbose("RSA key generation complete."); - for (i = 0; i < SSH_SESSION_KEY_LENGTH; i++) { - if (i % 4 == 0) - rnd = arc4random(); - sensitive_data.ssh1_cookie[i] = rnd & 0xff; - rnd >>= 8; - } + arc4random_buf(sensitive_data.ssh1_cookie, SSH_SESSION_KEY_LENGTH); arc4random_stir(); } +/*ARGSUSED*/ static void key_regeneration_alarm(int sig) { @@ -358,10 +396,11 @@ key_regeneration_alarm(int sig) static void sshd_exchange_identification(int sock_in, int sock_out) { - int i, mismatch; + u_int i; + int mismatch; int remote_major, remote_minor; int major, minor; - char *s; + char *s, *newline = "\n"; char buf[256]; /* Must not be larger than remote_version. */ char remote_version[256]; /* Must be at least as big as buf. */ @@ -372,15 +411,17 @@ sshd_exchange_identification(int sock_in, int sock_out) } else if (options.protocol & SSH_PROTO_2) { major = PROTOCOL_MAJOR_2; minor = PROTOCOL_MINOR_2; + newline = "\r\n"; } else { major = PROTOCOL_MAJOR_1; minor = PROTOCOL_MINOR_1; } - snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", major, minor, SSH_VERSION); + snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s%s", major, minor, + SSH_VERSION, newline); server_version_string = xstrdup(buf); /* Send our protocol version identification. */ - if (atomicio(vwrite, sock_out, server_version_string, + if (roaming_atomicio(vwrite, sock_out, server_version_string, strlen(server_version_string)) != strlen(server_version_string)) { logit("Could not write ident string to %s", get_remote_ipaddr()); @@ -390,7 +431,7 @@ sshd_exchange_identification(int sock_in, int sock_out) /* Read other sides version identification. */ memset(buf, 0, sizeof(buf)); for (i = 0; i < sizeof(buf) - 1; i++) { - if (atomicio(read, sock_in, &buf[i], 1) != 1) { + if (roaming_atomicio(read, sock_in, &buf[i], 1) != 1) { logit("Did not receive identification string from %s", get_remote_ipaddr()); cleanup_exit(255); @@ -540,25 +581,17 @@ privsep_preauth_child(void) { u_int32_t rnd[256]; gid_t gidset[1]; - struct passwd *pw; - int i; /* Enable challenge-response authentication for privilege separation */ privsep_challenge_enable(); - for (i = 0; i < 256; i++) - rnd[i] = arc4random(); + arc4random_stir(); + arc4random_buf(rnd, sizeof(rnd)); RAND_seed(rnd, sizeof(rnd)); /* Demote the private keys to public keys. */ demote_sensitive_data(); - if ((pw = getpwnam(SSH_PRIVSEP_USER)) == NULL) - fatal("Privilege separation user %s does not exist", - SSH_PRIVSEP_USER); - memset(pw->pw_passwd, 0, strlen(pw->pw_passwd)); - endpwent(); - /* Change our root directory */ if (chroot(_PATH_PRIVSEP_CHROOT_DIR) == -1) fatal("chroot(\"%s\"): %s", _PATH_PRIVSEP_CHROOT_DIR, @@ -567,16 +600,16 @@ privsep_preauth_child(void) fatal("chdir(\"/\"): %s", strerror(errno)); /* Drop our privileges */ - debug3("privsep user:group %u:%u", (u_int)pw->pw_uid, - (u_int)pw->pw_gid); + debug3("privsep user:group %u:%u", (u_int)privsep_pw->pw_uid, + (u_int)privsep_pw->pw_gid); #if 0 /* XXX not ready, too heavy after chroot */ - do_setusercontext(pw); + do_setusercontext(privsep_pw); #else - gidset[0] = pw->pw_gid; + gidset[0] = privsep_pw->pw_gid; if (setgroups(1, gidset) < 0) fatal("setgroups: %.100s", strerror(errno)); - permanently_set_uid(pw); + permanently_set_uid(privsep_pw); #endif } @@ -626,22 +659,16 @@ privsep_preauth(Authctxt *authctxt) static void privsep_postauth(Authctxt *authctxt) { + u_int32_t rnd[256]; + #ifdef DISABLE_FD_PASSING if (1) { #else if (authctxt->pw->pw_uid == 0 || options.use_login) { #endif /* File descriptor passing is broken or root login */ - monitor_apply_keystate(pmonitor); use_privsep = 0; - return; - } - - /* Authentication complete */ - alarm(0); - if (startup_pipe != -1) { - close(startup_pipe); - startup_pipe = -1; + goto skip; } /* New socket pair */ @@ -651,7 +678,7 @@ privsep_postauth(Authctxt *authctxt) if (pmonitor->m_pid == -1) fatal("fork of unprivileged child failed"); else if (pmonitor->m_pid != 0) { - debug2("User child is on pid %ld", (long)pmonitor->m_pid); + verbose("User child is on pid %ld", (long)pmonitor->m_pid); close(pmonitor->m_recvfd); buffer_clear(&loginmsg); monitor_child_postauth(pmonitor); @@ -665,11 +692,22 @@ privsep_postauth(Authctxt *authctxt) /* Demote the private keys to public keys. */ demote_sensitive_data(); + arc4random_stir(); + arc4random_buf(rnd, sizeof(rnd)); + RAND_seed(rnd, sizeof(rnd)); + /* Drop privileges */ do_setusercontext(authctxt->pw); + skip: /* It is safe now to apply the key state */ monitor_apply_keystate(pmonitor); + + /* + * Tell the packet layer that authentication was successful, since + * this information is not part of the key state. + */ + packet_set_authenticated(); } static char * @@ -757,7 +795,7 @@ drop_connection(int startups) p *= startups - options.max_startups_begin; p /= options.max_startups - options.max_startups_begin; p += options.max_startups_rate; - r = arc4random() % 100; + r = arc4random_uniform(100); debug("drop_connection: p %d, r %d", p, r); return (r < p) ? 1 : 0; @@ -769,8 +807,9 @@ usage(void) fprintf(stderr, "%s, %s\n", SSH_RELEASE, SSLeay_version(SSLEAY_VERSION)); fprintf(stderr, -"usage: sshd [-46Ddeiqt] [-b bits] [-f config_file] [-g login_grace_time]\n" -" [-h host_key_file] [-k key_gen_time] [-o option] [-p port] [-u len]\n" +"usage: sshd [-46DdeiqTt] [-b bits] [-C connection_spec] [-f config_file]\n" +" [-g login_grace_time] [-h host_key_file] [-k key_gen_time]\n" +" [-o option] [-p port] [-u len]\n" ); exit(1); } @@ -793,6 +832,7 @@ send_rexec_state(int fd, Buffer *conf) * bignum iqmp " * bignum p " * bignum q " + * string rngseed (only if OpenSSL is not self-seeded) */ buffer_init(&m); buffer_put_cstring(&m, buffer_ptr(conf)); @@ -809,6 +849,10 @@ send_rexec_state(int fd, Buffer *conf) } else buffer_put_int(&m, 0); +#ifndef OPENSSL_PRNG_ONLY + rexec_send_rng_seed(&m); +#endif + if (ssh_msg_send(fd, 0, &m) == -1) fatal("%s: ssh_msg_send failed", __func__); @@ -851,11 +895,339 @@ recv_rexec_state(int fd, Buffer *conf) rsa_generate_additional_parameters( sensitive_data.server_key->rsa); } + +#ifndef OPENSSL_PRNG_ONLY + rexec_recv_rng_seed(&m); +#endif + buffer_free(&m); debug3("%s: done", __func__); } +/* Accept a connection from inetd */ +static void +server_accept_inetd(int *sock_in, int *sock_out) +{ + int fd; + + startup_pipe = -1; + if (rexeced_flag) { + close(REEXEC_CONFIG_PASS_FD); + *sock_in = *sock_out = dup(STDIN_FILENO); + if (!debug_flag) { + startup_pipe = dup(REEXEC_STARTUP_PIPE_FD); + close(REEXEC_STARTUP_PIPE_FD); + } + } else { + *sock_in = dup(STDIN_FILENO); + *sock_out = dup(STDOUT_FILENO); + } + /* + * We intentionally do not close the descriptors 0, 1, and 2 + * as our code for setting the descriptors won't work if + * ttyfd happens to be one of those. + */ + if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) { + dup2(fd, STDIN_FILENO); + dup2(fd, STDOUT_FILENO); + if (fd > STDOUT_FILENO) + close(fd); + } + debug("inetd sockets after dupping: %d, %d", *sock_in, *sock_out); +} + +/* + * Listen for TCP connections + */ +static void +server_listen(void) +{ + int ret, listen_sock, on = 1; + struct addrinfo *ai; + char ntop[NI_MAXHOST], strport[NI_MAXSERV]; + + for (ai = options.listen_addrs; ai; ai = ai->ai_next) { + if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) + continue; + if (num_listen_socks >= MAX_LISTEN_SOCKS) + fatal("Too many listen sockets. " + "Enlarge MAX_LISTEN_SOCKS"); + if ((ret = getnameinfo(ai->ai_addr, ai->ai_addrlen, + ntop, sizeof(ntop), strport, sizeof(strport), + NI_NUMERICHOST|NI_NUMERICSERV)) != 0) { + error("getnameinfo failed: %.100s", + ssh_gai_strerror(ret)); + continue; + } + /* Create socket for listening. */ + listen_sock = socket(ai->ai_family, ai->ai_socktype, + ai->ai_protocol); + if (listen_sock < 0) { + /* kernel may not support ipv6 */ + verbose("socket: %.100s", strerror(errno)); + continue; + } + if (set_nonblock(listen_sock) == -1) { + close(listen_sock); + continue; + } + /* + * Set socket options. + * Allow local port reuse in TIME_WAIT. + */ + if (setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, + &on, sizeof(on)) == -1) + error("setsockopt SO_REUSEADDR: %s", strerror(errno)); + + /* Only communicate in IPv6 over AF_INET6 sockets. */ + if (ai->ai_family == AF_INET6) + sock_set_v6only(listen_sock); + + debug("Bind to port %s on %s.", strport, ntop); + + /* Bind the socket to the desired port. */ + if (bind(listen_sock, ai->ai_addr, ai->ai_addrlen) < 0) { + error("Bind to port %s on %s failed: %.200s.", + strport, ntop, strerror(errno)); + close(listen_sock); + continue; + } + listen_socks[num_listen_socks] = listen_sock; + num_listen_socks++; + + /* Start listening on the port. */ + if (listen(listen_sock, SSH_LISTEN_BACKLOG) < 0) + fatal("listen on [%s]:%s: %.100s", + ntop, strport, strerror(errno)); + logit("Server listening on %s port %s.", ntop, strport); + } + freeaddrinfo(options.listen_addrs); + + if (!num_listen_socks) + fatal("Cannot bind any address."); +} + +/* + * The main TCP accept loop. Note that, for the non-debug case, returns + * from this function are in a forked subprocess. + */ +static void +server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s) +{ + fd_set *fdset; + int i, j, ret, maxfd; + int key_used = 0, startups = 0; + int startup_p[2] = { -1 , -1 }; + struct sockaddr_storage from; + socklen_t fromlen; + pid_t pid; + + /* setup fd set for accept */ + fdset = NULL; + maxfd = 0; + for (i = 0; i < num_listen_socks; i++) + if (listen_socks[i] > maxfd) + maxfd = listen_socks[i]; + /* pipes connected to unauthenticated childs */ + startup_pipes = xcalloc(options.max_startups, sizeof(int)); + for (i = 0; i < options.max_startups; i++) + startup_pipes[i] = -1; + + /* + * Stay listening for connections until the system crashes or + * the daemon is killed with a signal. + */ + for (;;) { + if (received_sighup) + sighup_restart(); + if (fdset != NULL) + xfree(fdset); + fdset = (fd_set *)xcalloc(howmany(maxfd + 1, NFDBITS), + sizeof(fd_mask)); + + for (i = 0; i < num_listen_socks; i++) + FD_SET(listen_socks[i], fdset); + for (i = 0; i < options.max_startups; i++) + if (startup_pipes[i] != -1) + FD_SET(startup_pipes[i], fdset); + + /* Wait in select until there is a connection. */ + ret = select(maxfd+1, fdset, NULL, NULL, NULL); + if (ret < 0 && errno != EINTR) + error("select: %.100s", strerror(errno)); + if (received_sigterm) { + logit("Received signal %d; terminating.", + (int) received_sigterm); + close_listen_socks(); + unlink(options.pid_file); + exit(255); + } + if (key_used && key_do_regen) { + generate_ephemeral_server_key(); + key_used = 0; + key_do_regen = 0; + } + if (ret < 0) + continue; + + for (i = 0; i < options.max_startups; i++) + if (startup_pipes[i] != -1 && + FD_ISSET(startup_pipes[i], fdset)) { + /* + * the read end of the pipe is ready + * if the child has closed the pipe + * after successful authentication + * or if the child has died + */ + close(startup_pipes[i]); + startup_pipes[i] = -1; + startups--; + } + for (i = 0; i < num_listen_socks; i++) { + if (!FD_ISSET(listen_socks[i], fdset)) + continue; + fromlen = sizeof(from); + *newsock = accept(listen_socks[i], + (struct sockaddr *)&from, &fromlen); + if (*newsock < 0) { + if (errno != EINTR && errno != EAGAIN && + errno != EWOULDBLOCK) + error("accept: %.100s", strerror(errno)); + continue; + } + if (unset_nonblock(*newsock) == -1) { + close(*newsock); + continue; + } + if (drop_connection(startups) == 1) { + debug("drop connection #%d", startups); + close(*newsock); + continue; + } + if (pipe(startup_p) == -1) { + close(*newsock); + continue; + } + + if (rexec_flag && socketpair(AF_UNIX, + SOCK_STREAM, 0, config_s) == -1) { + error("reexec socketpair: %s", + strerror(errno)); + close(*newsock); + close(startup_p[0]); + close(startup_p[1]); + continue; + } + + for (j = 0; j < options.max_startups; j++) + if (startup_pipes[j] == -1) { + startup_pipes[j] = startup_p[0]; + if (maxfd < startup_p[0]) + maxfd = startup_p[0]; + startups++; + break; + } + + /* + * Got connection. Fork a child to handle it, unless + * we are in debugging mode. + */ + if (debug_flag) { + /* + * In debugging mode. Close the listening + * socket, and start processing the + * connection without forking. + */ + debug("Server will not fork when running in debugging mode."); + close_listen_socks(); + *sock_in = *newsock; + *sock_out = *newsock; + close(startup_p[0]); + close(startup_p[1]); + startup_pipe = -1; + pid = getpid(); + if (rexec_flag) { + send_rexec_state(config_s[0], + &cfg); + close(config_s[0]); + } + break; + } + + /* + * Normal production daemon. Fork, and have + * the child process the connection. The + * parent continues listening. + */ + platform_pre_fork(); + if ((pid = fork()) == 0) { + /* + * Child. Close the listening and + * max_startup sockets. Start using + * the accepted socket. Reinitialize + * logging (since our pid has changed). + * We break out of the loop to handle + * the connection. + */ + platform_post_fork_child(); + startup_pipe = startup_p[1]; + close_startup_pipes(); + close_listen_socks(); + *sock_in = *newsock; + *sock_out = *newsock; + log_init(__progname, + options.log_level, + options.log_facility, + log_stderr); + if (rexec_flag) + close(config_s[0]); + break; + } + + /* Parent. Stay in the loop. */ + platform_post_fork_parent(pid); + if (pid < 0) + error("fork: %.100s", strerror(errno)); + else + debug("Forked child %ld.", (long)pid); + + close(startup_p[1]); + + if (rexec_flag) { + send_rexec_state(config_s[0], &cfg); + close(config_s[0]); + close(config_s[1]); + } + + /* + * Mark that the key has been used (it + * was "given" to the child). + */ + if ((options.protocol & SSH_PROTO_1) && + key_used == 0) { + /* Schedule server key regeneration alarm. */ + signal(SIGALRM, key_regeneration_alarm); + alarm(options.key_regeneration_time); + key_used = 1; + } + + close(*newsock); + + /* + * Ensure that our random state differs + * from that of the child + */ + arc4random_stir(); + } + + /* child process check (or debug mode) */ + if (num_listen_socks < 0) + break; + } +} + + /* * Main program for the daemon. */ @@ -864,25 +1236,17 @@ main(int ac, char **av) { extern char *optarg; extern int optind; - int opt, j, i, fdsetsz, on = 1; + int opt, i, on = 1; int sock_in = -1, sock_out = -1, newsock = -1; - pid_t pid; - socklen_t fromlen; - fd_set *fdset; - struct sockaddr_storage from; const char *remote_ip; + char *test_user = NULL, *test_host = NULL, *test_addr = NULL; int remote_port; - FILE *f; - struct addrinfo *ai; - char ntop[NI_MAXHOST], strport[NI_MAXSERV]; - char *line; - int listen_sock, maxfd; - int startup_p[2] = { -1 , -1 }, config_s[2] = { -1 , -1 }; - int startups = 0; + char *line, *p, *cp; + int config_s[2] = { -1 , -1 }; + u_int64_t ibytes, obytes; + mode_t new_umask; Key *key; Authctxt *authctxt; - int ret, key_used = 0; - Buffer cfg; #ifdef HAVE_SECUREWARE (void)set_auth_parameters(ac, av); @@ -893,7 +1257,7 @@ main(int ac, char **av) /* Save argv. Duplicate so setproctitle emulation doesn't clobber it */ saved_argc = ac; rexec_argc = ac; - saved_argv = xmalloc(sizeof(*saved_argv) * (ac + 1)); + saved_argv = xcalloc(ac + 1, sizeof(*saved_argv)); for (i = 0; i < ac; i++) saved_argv[i] = xstrdup(av[i]); saved_argv[i] = NULL; @@ -907,11 +1271,14 @@ main(int ac, char **av) if (geteuid() == 0 && setgroups(0, NULL) == -1) debug("setgroups(): %.200s", strerror(errno)); + /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ + sanitise_stdfd(); + /* Initialize configuration options to their default values. */ initialize_server_options(&options); /* Parse command-line arguments. */ - while ((opt = getopt(ac, av, "f:p:b:k:h:g:u:o:dDeiqrtQR46")) != -1) { + while ((opt = getopt(ac, av, "f:p:b:k:h:g:u:o:C:dDeiqrtQRT46")) != -1) { switch (opt) { case '4': options.address_family = AF_INET; @@ -952,7 +1319,8 @@ main(int ac, char **av) options.log_level = SYSLOG_LEVEL_QUIET; break; case 'b': - options.server_key_bits = atoi(optarg); + options.server_key_bits = (int)strtonum(optarg, 256, + 32768, NULL); break; case 'p': options.ports_from_cmdline = 1; @@ -961,7 +1329,7 @@ main(int ac, char **av) exit(1); } options.ports[options.num_ports++] = a2port(optarg); - if (options.ports[options.num_ports-1] == 0) { + if (options.ports[options.num_ports-1] <= 0) { fprintf(stderr, "Bad port number.\n"); exit(1); } @@ -983,13 +1351,33 @@ main(int ac, char **av) fprintf(stderr, "too many host keys.\n"); exit(1); } - options.host_key_files[options.num_host_key_files++] = optarg; + options.host_key_files[options.num_host_key_files++] = + derelativise_path(optarg); break; case 't': test_flag = 1; break; + case 'T': + test_flag = 2; + break; + case 'C': + cp = optarg; + while ((p = strsep(&cp, ",")) && *p != '\0') { + if (strncmp(p, "addr=", 5) == 0) + test_addr = xstrdup(p + 5); + else if (strncmp(p, "host=", 5) == 0) + test_host = xstrdup(p + 5); + else if (strncmp(p, "user=", 5) == 0) + test_user = xstrdup(p + 5); + else { + fprintf(stderr, "Invalid test " + "mode specification %s\n", p); + exit(1); + } + } + break; case 'u': - utmp_len = atoi(optarg); + utmp_len = (u_int)strtonum(optarg, 0, MAXHOSTNAMELEN+1, NULL); if (utmp_len > MAXHOSTNAMELEN) { fprintf(stderr, "Invalid utmp length.\n"); exit(1); @@ -998,7 +1386,7 @@ main(int ac, char **av) case 'o': line = xstrdup(optarg); if (process_server_config_line(&options, line, - "command-line", 0) != 0) + "command-line", 0, NULL, NULL, NULL, NULL) != 0) exit(1); xfree(line); break; @@ -1010,7 +1398,7 @@ main(int ac, char **av) } if (rexeced_flag || inetd_flag) rexec_flag = 0; - if (rexec_flag && (av[0] == NULL || *av[0] != '/')) + if (!test_flag && (rexec_flag && (av[0] == NULL || *av[0] != '/'))) fatal("sshd re-exec requires execution with an absolute path"); if (rexeced_flag) closefrom(REEXEC_MIN_FREE_FD); @@ -1030,13 +1418,13 @@ main(int ac, char **av) SYSLOG_FACILITY_AUTH : options.log_facility, log_stderr || !inetd_flag); -#ifdef _AIX /* * Unset KRB5CCNAME, otherwise the user's session may inherit it from * root's environment - */ - unsetenv("KRB5CCNAME"); -#endif /* _AIX */ + */ + if (getenv("KRB5CCNAME") != NULL) + unsetenv("KRB5CCNAME"); + #ifdef _UNICOS /* Cray can define user privs drop all privs now! * Not needed on PRIV_SU systems! @@ -1044,13 +1432,26 @@ main(int ac, char **av) drop_cray_privs(); #endif - seed_rng(); - sensitive_data.server_key = NULL; sensitive_data.ssh1_host_key = NULL; sensitive_data.have_ssh1_key = 0; sensitive_data.have_ssh2_key = 0; + /* + * If we're doing an extended config test, make sure we have all of + * the parameters we need. If we're not doing an extended test, + * do not silently ignore connection test params. + */ + if (test_flag >= 2 && + (test_user != NULL || test_host != NULL || test_addr != NULL) + && (test_user == NULL || test_host == NULL || test_addr == NULL)) + fatal("user, host and addr are all required when testing " + "Match configs"); + if (test_flag < 2 && (test_user != NULL || test_host != NULL || + test_addr != NULL)) + fatal("Config test connection parameter (-C) provided without " + "test mode (-T)"); + /* Fetch our configuration */ buffer_init(&cfg); if (rexeced_flag) @@ -1058,15 +1459,18 @@ main(int ac, char **av) else load_server_config(config_file_name, &cfg); - parse_server_config(&options, - rexeced_flag ? "rexec" : config_file_name, &cfg); + parse_server_config(&options, rexeced_flag ? "rexec" : config_file_name, + &cfg, NULL, NULL, NULL); - if (!rexec_flag) - buffer_free(&cfg); + seed_rng(); /* Fill in default values for those options not explicitly set. */ fill_default_server_options(&options); + /* challenge-response is implemented via keyboard interactive */ + if (options.challenge_response_authentication) + options.kbd_interactive_authentication = 1; + /* set default channel AF */ channel_set_af(options.address_family); @@ -1078,8 +1482,21 @@ main(int ac, char **av) debug("sshd version %.100s", SSH_RELEASE); + /* Store privilege separation user for later use if required. */ + if ((privsep_pw = getpwnam(SSH_PRIVSEP_USER)) == NULL) { + if (use_privsep || options.kerberos_authentication) + fatal("Privilege separation user %s does not exist", + SSH_PRIVSEP_USER); + } else { + memset(privsep_pw->pw_passwd, 0, strlen(privsep_pw->pw_passwd)); + privsep_pw = pwcopy(privsep_pw); + xfree(privsep_pw->pw_passwd); + privsep_pw->pw_passwd = xstrdup("*"); + } + endpwent(); + /* load private host keys */ - sensitive_data.host_keys = xmalloc(options.num_host_key_files * + sensitive_data.host_keys = xcalloc(options.num_host_key_files, sizeof(Key *)); for (i = 0; i < options.num_host_key_files; i++) sensitive_data.host_keys[i] = NULL; @@ -1145,12 +1562,8 @@ main(int ac, char **av) } if (use_privsep) { - struct passwd *pw; struct stat st; - if ((pw = getpwnam(SSH_PRIVSEP_USER)) == NULL) - fatal("Privilege separation user %s does not exist", - SSH_PRIVSEP_USER); if ((stat(_PATH_PRIVSEP_CHROOT_DIR, &st) == -1) || (S_ISDIR(st.st_mode) == 0)) fatal("Missing privilege separation directory: %s", @@ -1167,6 +1580,13 @@ main(int ac, char **av) "world-writable.", _PATH_PRIVSEP_CHROOT_DIR); } + if (test_flag > 1) { + if (test_user != NULL && test_addr != NULL && test_host != NULL) + parse_server_match_config(&options, test_user, + test_host, test_addr); + dump_config(&options); + } + /* Configuration looks good, so exit if in test mode. */ if (test_flag) exit(0); @@ -1182,7 +1602,7 @@ main(int ac, char **av) debug("setgroups() failed: %.200s", strerror(errno)); if (rexec_flag) { - rexec_argv = xmalloc(sizeof(char *) * (rexec_argc + 2)); + rexec_argv = xcalloc(rexec_argc + 2, sizeof(char *)); for (i = 0; i < rexec_argc; i++) { debug("rexec_argv[%d]='%s'", i, saved_argv[i]); rexec_argv[i] = saved_argv[i]; @@ -1191,6 +1611,10 @@ main(int ac, char **av) rexec_argv[rexec_argc + 1] = NULL; } + /* Ensure that umask disallows at least group and world write */ + new_umask = umask(0077) | 0022; + (void) umask(new_umask); + /* Initialize the log (it is reinitialized below in case we forked). */ if (debug_flag && (!inetd_flag || rexeced_flag)) log_stderr = 1; @@ -1230,119 +1654,28 @@ main(int ac, char **av) /* ignore SIGPIPE */ signal(SIGPIPE, SIG_IGN); - /* Start listening for a socket, unless started from inetd. */ + /* Get a connection, either from inetd or a listening TCP socket */ if (inetd_flag) { - int fd; - - startup_pipe = -1; - if (rexeced_flag) { - close(REEXEC_CONFIG_PASS_FD); - sock_in = sock_out = dup(STDIN_FILENO); - if (!debug_flag) { - startup_pipe = dup(REEXEC_STARTUP_PIPE_FD); - close(REEXEC_STARTUP_PIPE_FD); - } - } else { - sock_in = dup(STDIN_FILENO); - sock_out = dup(STDOUT_FILENO); - } - /* - * We intentionally do not close the descriptors 0, 1, and 2 - * as our code for setting the descriptors won't work if - * ttyfd happens to be one of those. - */ - if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) { - dup2(fd, STDIN_FILENO); - dup2(fd, STDOUT_FILENO); - if (fd > STDOUT_FILENO) - close(fd); - } - debug("inetd sockets after dupping: %d, %d", sock_in, sock_out); - if ((options.protocol & SSH_PROTO_1) && - sensitive_data.server_key == NULL) - generate_ephemeral_server_key(); + server_accept_inetd(&sock_in, &sock_out); } else { - for (ai = options.listen_addrs; ai; ai = ai->ai_next) { - if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) - continue; - if (num_listen_socks >= MAX_LISTEN_SOCKS) - fatal("Too many listen sockets. " - "Enlarge MAX_LISTEN_SOCKS"); - if (getnameinfo(ai->ai_addr, ai->ai_addrlen, - ntop, sizeof(ntop), strport, sizeof(strport), - NI_NUMERICHOST|NI_NUMERICSERV) != 0) { - error("getnameinfo failed"); - continue; - } - /* Create socket for listening. */ - listen_sock = socket(ai->ai_family, ai->ai_socktype, - ai->ai_protocol); - if (listen_sock < 0) { - /* kernel may not support ipv6 */ - verbose("socket: %.100s", strerror(errno)); - continue; - } - if (set_nonblock(listen_sock) == -1) { - close(listen_sock); - continue; - } - /* - * Set socket options. - * Allow local port reuse in TIME_WAIT. - */ - if (setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, - &on, sizeof(on)) == -1) - error("setsockopt SO_REUSEADDR: %s", strerror(errno)); - - debug("Bind to port %s on %s.", strport, ntop); - - /* Bind the socket to the desired port. */ - if (bind(listen_sock, ai->ai_addr, ai->ai_addrlen) < 0) { - if (!ai->ai_next) - error("Bind to port %s on %s failed: %.200s.", - strport, ntop, strerror(errno)); - close(listen_sock); - continue; - } - listen_socks[num_listen_socks] = listen_sock; - num_listen_socks++; - - /* Start listening on the port. */ - logit("Server listening on %s port %s.", ntop, strport); - if (listen(listen_sock, SSH_LISTEN_BACKLOG) < 0) - fatal("listen: %.100s", strerror(errno)); - - } - freeaddrinfo(options.listen_addrs); - - if (!num_listen_socks) - fatal("Cannot bind any address."); + platform_pre_listen(); + server_listen(); if (options.protocol & SSH_PROTO_1) generate_ephemeral_server_key(); - /* - * Arrange to restart on SIGHUP. The handler needs - * listen_sock. - */ signal(SIGHUP, sighup_handler); - + signal(SIGCHLD, main_sigchld_handler); signal(SIGTERM, sigterm_handler); signal(SIGQUIT, sigterm_handler); - /* Arrange SIGCHLD to be caught. */ - signal(SIGCHLD, main_sigchld_handler); - - /* Write out the pid file after the sigterm handler is setup */ + /* + * Write out the pid file after the sigterm handler + * is setup and the listen sockets are bound + */ if (!debug_flag) { - /* - * Record our pid in /var/run/sshd.pid to make it - * easier to kill the correct sshd. We don't want to - * do this before the bind above because the bind will - * fail if there already is a daemon, and this will - * overwrite any old pid in the file. - */ - f = fopen(options.pid_file, "wb"); + FILE *f = fopen(options.pid_file, "w"); + if (f == NULL) { error("Couldn't create pid file \"%s\": %s", options.pid_file, strerror(errno)); @@ -1352,194 +1685,9 @@ main(int ac, char **av) } } - /* setup fd set for listen */ - fdset = NULL; - maxfd = 0; - for (i = 0; i < num_listen_socks; i++) - if (listen_socks[i] > maxfd) - maxfd = listen_socks[i]; - /* pipes connected to unauthenticated childs */ - startup_pipes = xmalloc(options.max_startups * sizeof(int)); - for (i = 0; i < options.max_startups; i++) - startup_pipes[i] = -1; - - /* - * Stay listening for connections until the system crashes or - * the daemon is killed with a signal. - */ - for (;;) { - if (received_sighup) - sighup_restart(); - if (fdset != NULL) - xfree(fdset); - fdsetsz = howmany(maxfd+1, NFDBITS) * sizeof(fd_mask); - fdset = (fd_set *)xmalloc(fdsetsz); - memset(fdset, 0, fdsetsz); - - for (i = 0; i < num_listen_socks; i++) - FD_SET(listen_socks[i], fdset); - for (i = 0; i < options.max_startups; i++) - if (startup_pipes[i] != -1) - FD_SET(startup_pipes[i], fdset); - - /* Wait in select until there is a connection. */ - ret = select(maxfd+1, fdset, NULL, NULL, NULL); - if (ret < 0 && errno != EINTR) - error("select: %.100s", strerror(errno)); - if (received_sigterm) { - logit("Received signal %d; terminating.", - (int) received_sigterm); - close_listen_socks(); - unlink(options.pid_file); - exit(255); - } - if (key_used && key_do_regen) { - generate_ephemeral_server_key(); - key_used = 0; - key_do_regen = 0; - } - if (ret < 0) - continue; - - for (i = 0; i < options.max_startups; i++) - if (startup_pipes[i] != -1 && - FD_ISSET(startup_pipes[i], fdset)) { - /* - * the read end of the pipe is ready - * if the child has closed the pipe - * after successful authentication - * or if the child has died - */ - close(startup_pipes[i]); - startup_pipes[i] = -1; - startups--; - } - for (i = 0; i < num_listen_socks; i++) { - if (!FD_ISSET(listen_socks[i], fdset)) - continue; - fromlen = sizeof(from); - newsock = accept(listen_socks[i], (struct sockaddr *)&from, - &fromlen); - if (newsock < 0) { - if (errno != EINTR && errno != EWOULDBLOCK) - error("accept: %.100s", strerror(errno)); - continue; - } - if (unset_nonblock(newsock) == -1) { - close(newsock); - continue; - } - if (drop_connection(startups) == 1) { - debug("drop connection #%d", startups); - close(newsock); - continue; - } - if (pipe(startup_p) == -1) { - close(newsock); - continue; - } - - if (rexec_flag && socketpair(AF_UNIX, - SOCK_STREAM, 0, config_s) == -1) { - error("reexec socketpair: %s", - strerror(errno)); - close(newsock); - close(startup_p[0]); - close(startup_p[1]); - continue; - } - - for (j = 0; j < options.max_startups; j++) - if (startup_pipes[j] == -1) { - startup_pipes[j] = startup_p[0]; - if (maxfd < startup_p[0]) - maxfd = startup_p[0]; - startups++; - break; - } - - /* - * Got connection. Fork a child to handle it, unless - * we are in debugging mode. - */ - if (debug_flag) { - /* - * In debugging mode. Close the listening - * socket, and start processing the - * connection without forking. - */ - debug("Server will not fork when running in debugging mode."); - close_listen_socks(); - sock_in = newsock; - sock_out = newsock; - close(startup_p[0]); - close(startup_p[1]); - startup_pipe = -1; - pid = getpid(); - if (rexec_flag) { - send_rexec_state(config_s[0], - &cfg); - close(config_s[0]); - } - break; - } else { - /* - * Normal production daemon. Fork, and have - * the child process the connection. The - * parent continues listening. - */ - if ((pid = fork()) == 0) { - /* - * Child. Close the listening and max_startup - * sockets. Start using the accepted socket. - * Reinitialize logging (since our pid has - * changed). We break out of the loop to handle - * the connection. - */ - startup_pipe = startup_p[1]; - close_startup_pipes(); - close_listen_socks(); - sock_in = newsock; - sock_out = newsock; - log_init(__progname, options.log_level, options.log_facility, log_stderr); - if (rexec_flag) - close(config_s[0]); - break; - } - } - - /* Parent. Stay in the loop. */ - if (pid < 0) - error("fork: %.100s", strerror(errno)); - else - debug("Forked child %ld.", (long)pid); - - close(startup_p[1]); - - if (rexec_flag) { - send_rexec_state(config_s[0], &cfg); - close(config_s[0]); - close(config_s[1]); - } - - /* Mark that the key has been used (it was "given" to the child). */ - if ((options.protocol & SSH_PROTO_1) && - key_used == 0) { - /* Schedule server key regeneration alarm. */ - signal(SIGALRM, key_regeneration_alarm); - alarm(options.key_regeneration_time); - key_used = 1; - } - - arc4random_stir(); - - /* Close the new socket (the child is now taking care of it). */ - close(newsock); - } - /* child process check (or debug mode) */ - if (num_listen_socks < 0) - break; - } + /* Accept a connection and return in a forked child */ + server_accept_loop(&sock_in, &sock_out, + &newsock, config_s); } /* This is the child processing a new connection. */ @@ -1600,6 +1748,10 @@ main(int ac, char **av) sock_in, sock_out, newsock, startup_pipe, config_s[0]); } + /* Executed child processes don't need these. */ + fcntl(sock_out, F_SETFD, FD_CLOEXEC); + fcntl(sock_in, F_SETFD, FD_CLOEXEC); + /* * Disable the key regeneration alarm. We will not regenerate the * key since we are no longer in a position to give it to anyone. We @@ -1613,22 +1765,41 @@ main(int ac, char **av) signal(SIGCHLD, SIG_DFL); signal(SIGINT, SIG_DFL); - /* Set SO_KEEPALIVE if requested. */ - if (options.tcp_keep_alive && - setsockopt(sock_in, SOL_SOCKET, SO_KEEPALIVE, &on, - sizeof(on)) < 0) - error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno)); - /* * Register our connection. This turns encryption off because we do * not have a key. */ packet_set_connection(sock_in, sock_out); + packet_set_server(); - remote_port = get_remote_port(); + /* Set SO_KEEPALIVE if requested. */ + if (options.tcp_keep_alive && packet_connection_is_on_socket() && + setsockopt(sock_in, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)) < 0) + error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno)); + + if ((remote_port = get_remote_port()) < 0) { + debug("get_remote_port failed"); + cleanup_exit(255); + } + + /* + * We use get_canonical_hostname with usedns = 0 instead of + * get_remote_ipaddr here so IP options will be checked. + */ + (void) get_canonical_hostname(0); + /* + * The rest of the code depends on the fact that + * get_remote_ipaddr() caches the remote ip, even if + * the socket goes away. + */ remote_ip = get_remote_ipaddr(); +#ifdef SSH_AUDIT_EVENTS + audit_connection_from(remote_ip, remote_port); +#endif #ifdef LIBWRAP + allow_severity = options.log_facility|LOG_INFO; + deny_severity = options.log_facility|LOG_WARNING; /* Check whether logins are denied from this host. */ if (packet_connection_is_on_socket()) { struct request_info req; @@ -1649,10 +1820,10 @@ main(int ac, char **av) verbose("Connection from %.500s port %d", remote_ip, remote_port); /* - * We don\'t want to listen forever unless the other side + * We don't want to listen forever unless the other side * successfully authenticates itself. So we set up an alarm which is * cleared after successful authentication. A limit of zero - * indicates no limit. Note that we don\'t set the alarm in debugging + * indicates no limit. Note that we don't set the alarm in debugging * mode; it is just annoying to have the server exit just when you * are about to discover the bug. */ @@ -1662,11 +1833,16 @@ main(int ac, char **av) sshd_exchange_identification(sock_in, sock_out); + /* In inetd mode, generate ephemeral key only for proto 1 connections */ + if (!compat20 && inetd_flag && sensitive_data.server_key == NULL) + generate_ephemeral_server_key(); + packet_set_nonblocking(); /* allocate authentication context */ - authctxt = xmalloc(sizeof(*authctxt)); - memset(authctxt, 0, sizeof(*authctxt)); + authctxt = xcalloc(1, sizeof(*authctxt)); + + authctxt->loginmsg = &loginmsg; /* XXX global for cleanup, access from other modules */ the_authctxt = authctxt; @@ -1697,6 +1873,36 @@ main(int ac, char **av) } authenticated: + /* + * Cancel the alarm we set to limit the time taken for + * authentication. + */ + alarm(0); + signal(SIGALRM, SIG_DFL); + authctxt->authenticated = 1; + if (startup_pipe != -1) { + close(startup_pipe); + startup_pipe = -1; + } + +#ifdef SSH_AUDIT_EVENTS + audit_event(SSH_AUTH_SUCCESS); +#endif + +#ifdef GSSAPI + if (options.gss_authentication) { + temporarily_use_uid(authctxt->pw); + ssh_gssapi_storecreds(); + restore_uid(); + } +#endif +#ifdef USE_PAM + if (options.use_pam) { + do_pam_setcred(1); + do_pam_session(); + } +#endif + /* * In privilege separation, we fork another child and prepare * file descriptor passing. @@ -1708,17 +1914,28 @@ main(int ac, char **av) destroy_sensitive_data(); } + packet_set_timeout(options.client_alive_interval, + options.client_alive_count_max); + /* Start session. */ do_authenticated(authctxt); /* The connection has been terminated. */ - verbose("Closing connection to %.100s", remote_ip); + packet_get_state(MODE_IN, NULL, NULL, NULL, &ibytes); + packet_get_state(MODE_OUT, NULL, NULL, NULL, &obytes); + verbose("Transferred: sent %llu, received %llu bytes", obytes, ibytes); + + verbose("Closing connection to %.500s port %d", remote_ip, remote_port); #ifdef USE_PAM if (options.use_pam) finish_pam(); #endif /* USE_PAM */ +#ifdef SSH_AUDIT_EVENTS + PRIVSEP(audit_event(SSH_CONNECTION_CLOSE)); +#endif + packet_close(); if (use_privsep) @@ -1736,11 +1953,14 @@ ssh1_session_key(BIGNUM *session_key_int) { int rsafail = 0; - if (BN_cmp(sensitive_data.server_key->rsa->n, sensitive_data.ssh1_host_key->rsa->n) > 0) { + if (BN_cmp(sensitive_data.server_key->rsa->n, + sensitive_data.ssh1_host_key->rsa->n) > 0) { /* Server key has bigger modulus. */ if (BN_num_bits(sensitive_data.server_key->rsa->n) < - BN_num_bits(sensitive_data.ssh1_host_key->rsa->n) + SSH_KEY_BITS_RESERVED) { - fatal("do_connection: %s: server_key %d < host_key %d + SSH_KEY_BITS_RESERVED %d", + BN_num_bits(sensitive_data.ssh1_host_key->rsa->n) + + SSH_KEY_BITS_RESERVED) { + fatal("do_connection: %s: " + "server_key %d < host_key %d + SSH_KEY_BITS_RESERVED %d", get_remote_ipaddr(), BN_num_bits(sensitive_data.server_key->rsa->n), BN_num_bits(sensitive_data.ssh1_host_key->rsa->n), @@ -1755,8 +1975,10 @@ ssh1_session_key(BIGNUM *session_key_int) } else { /* Host key has bigger modulus (or they are equal). */ if (BN_num_bits(sensitive_data.ssh1_host_key->rsa->n) < - BN_num_bits(sensitive_data.server_key->rsa->n) + SSH_KEY_BITS_RESERVED) { - fatal("do_connection: %s: host_key %d < server_key %d + SSH_KEY_BITS_RESERVED %d", + BN_num_bits(sensitive_data.server_key->rsa->n) + + SSH_KEY_BITS_RESERVED) { + fatal("do_connection: %s: " + "host_key %d < server_key %d + SSH_KEY_BITS_RESERVED %d", get_remote_ipaddr(), BN_num_bits(sensitive_data.ssh1_host_key->rsa->n), BN_num_bits(sensitive_data.server_key->rsa->n), @@ -1783,7 +2005,6 @@ do_ssh1_kex(void) u_char session_key[SSH_SESSION_KEY_LENGTH]; u_char cookie[8]; u_int cipher_type, auth_mask, protocol_flags; - u_int32_t rnd = 0; /* * Generate check bytes that the client must send back in the user @@ -1794,12 +2015,7 @@ do_ssh1_kex(void) * cookie. This only affects rhosts authentication, and this is one * of the reasons why it is inherently insecure. */ - for (i = 0; i < 8; i++) { - if (i % 4 == 0) - rnd = arc4random(); - cookie[i] = rnd & 0xff; - rnd >>= 8; - } + arc4random_buf(cookie, sizeof(cookie)); /* * Send our public key. We include in the packet 64 bits of random @@ -1881,10 +2097,10 @@ do_ssh1_kex(void) * key is in the highest bits. */ if (!rsafail) { - BN_mask_bits(session_key_int, sizeof(session_key) * 8); + (void) BN_mask_bits(session_key_int, sizeof(session_key) * 8); len = BN_num_bytes(session_key_int); - if (len < 0 || len > sizeof(session_key)) { - error("do_connection: bad session key len from %s: " + if (len < 0 || (u_int)len > sizeof(session_key)) { + error("do_ssh1_kex: bad session key len from %s: " "session_key_int %d > sizeof(session_key) %lu", get_remote_ipaddr(), len, (u_long)sizeof(session_key)); rsafail++; @@ -1970,10 +2186,14 @@ do_ssh2_kex(void) myproposal[PROPOSAL_MAC_ALGS_CTOS] = myproposal[PROPOSAL_MAC_ALGS_STOC] = options.macs; } - if (!options.compression) { + if (options.compression == COMP_NONE) { myproposal[PROPOSAL_COMP_ALGS_CTOS] = myproposal[PROPOSAL_COMP_ALGS_STOC] = "none"; + } else if (options.compression == COMP_DELAYED) { + myproposal[PROPOSAL_COMP_ALGS_CTOS] = + myproposal[PROPOSAL_COMP_ALGS_STOC] = "none,zlib@openssh.com"; } + myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = list_hostkey_types(); /* start key exchange */ @@ -1981,6 +2201,7 @@ do_ssh2_kex(void) kex->kex[KEX_DH_GRP1_SHA1] = kexdh_server; kex->kex[KEX_DH_GRP14_SHA1] = kexdh_server; kex->kex[KEX_DH_GEX_SHA1] = kexgex_server; + kex->kex[KEX_DH_GEX_SHA256] = kexgex_server; kex->server = 1; kex->client_version_string=client_version_string; kex->server_version_string=server_version_string; @@ -2010,5 +2231,10 @@ cleanup_exit(int i) { if (the_authctxt) do_cleanup(the_authctxt); +#ifdef SSH_AUDIT_EVENTS + /* done after do_cleanup so it can cancel the PAM auth 'thread' */ + if (!use_privsep || mm_is_monitor()) + audit_event(SSH_CONNECTION_ABANDON); +#endif _exit(i); }