X-Git-Url: http://andersk.mit.edu/gitweb/openssh.git/blobdiff_plain/25c31d49c3870e027eb7ea5bcc1a303d0108565f..7ca4010c51e5d143ce6a4684a8f4b7c4e74905a3:/sshd.c diff --git a/sshd.c b/sshd.c index 76aec80b..a206db24 100644 --- a/sshd.c +++ b/sshd.c @@ -1,3 +1,4 @@ +/* $OpenBSD: sshd.c,v 1.330 2006/03/25 13:17:02 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -42,7 +43,18 @@ */ #include "includes.h" -RCSID("$OpenBSD: sshd.c,v 1.306 2005/01/17 22:48:39 dtucker Exp $"); + +#include +#ifdef HAVE_SYS_STAT_H +# include +#endif +#include +#include + +#ifdef HAVE_PATHS_H +#include +#endif +#include #include #include @@ -246,6 +258,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) { @@ -275,6 +289,7 @@ sighup_restart(void) /* * Generic signal handler for terminating signals in the master daemon. */ +/*ARGSUSED*/ static void sigterm_handler(int sig) { @@ -285,6 +300,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,6 +319,7 @@ 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) { @@ -345,6 +362,7 @@ generate_ephemeral_server_key(void) arc4random_stir(); } +/*ARGSUSED*/ static void key_regeneration_alarm(int sig) { @@ -358,7 +376,8 @@ 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; @@ -632,16 +651,8 @@ privsep_postauth(Authctxt *authctxt) 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 */ @@ -668,8 +679,15 @@ privsep_postauth(Authctxt *authctxt) /* 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 * @@ -793,6 +811,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 +828,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,6 +874,11 @@ 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__); @@ -864,7 +892,7 @@ main(int ac, char **av) { extern char *optarg; extern int optind; - int opt, j, i, fdsetsz, on = 1; + int opt, j, i, on = 1; int sock_in = -1, sock_out = -1, newsock = -1; pid_t pid; socklen_t fromlen; @@ -907,6 +935,9 @@ 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); @@ -952,7 +983,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; @@ -989,7 +1021,7 @@ main(int ac, char **av) test_flag = 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); @@ -1030,13 +1062,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,8 +1076,6 @@ 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; @@ -1064,6 +1094,8 @@ main(int ac, char **av) if (!rexec_flag) buffer_free(&cfg); + seed_rng(); + /* Fill in default values for those options not explicitly set. */ fill_default_server_options(&options); @@ -1079,7 +1111,7 @@ main(int ac, char **av) debug("sshd version %.100s", SSH_RELEASE); /* 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,10 +1177,9 @@ main(int ac, char **av) } if (use_privsep) { - struct passwd *pw; struct stat st; - if ((pw = getpwnam(SSH_PRIVSEP_USER)) == NULL) + if (getpwnam(SSH_PRIVSEP_USER) == NULL) fatal("Privilege separation user %s does not exist", SSH_PRIVSEP_USER); if ((stat(_PATH_PRIVSEP_CHROOT_DIR, &st) == -1) || @@ -1182,7 +1213,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]; @@ -1268,10 +1299,12 @@ main(int ac, char **av) if (num_listen_socks >= MAX_LISTEN_SOCKS) fatal("Too many listen sockets. " "Enlarge MAX_LISTEN_SOCKS"); - if (getnameinfo(ai->ai_addr, ai->ai_addrlen, + if ((ret = getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), strport, sizeof(strport), - NI_NUMERICHOST|NI_NUMERICSERV) != 0) { - error("getnameinfo failed"); + NI_NUMERICHOST|NI_NUMERICSERV)) != 0) { + error("getnameinfo failed: %.100s", + (ret != EAI_SYSTEM) ? gai_strerror(ret) : + strerror(errno)); continue; } /* Create socket for listening. */ @@ -1308,10 +1341,10 @@ main(int ac, char **av) 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)); - + fatal("listen on [%s]:%s: %.100s", + ntop, strport, strerror(errno)); + logit("Server listening on %s port %s.", ntop, strport); } freeaddrinfo(options.listen_addrs); @@ -1359,7 +1392,7 @@ main(int ac, char **av) if (listen_socks[i] > maxfd) maxfd = listen_socks[i]; /* pipes connected to unauthenticated childs */ - startup_pipes = xmalloc(options.max_startups * sizeof(int)); + startup_pipes = xcalloc(options.max_startups, sizeof(int)); for (i = 0; i < options.max_startups; i++) startup_pipes[i] = -1; @@ -1372,9 +1405,8 @@ main(int ac, char **av) sighup_restart(); if (fdset != NULL) xfree(fdset); - fdsetsz = howmany(maxfd+1, NFDBITS) * sizeof(fd_mask); - fdset = (fd_set *)xmalloc(fdsetsz); - memset(fdset, 0, fdsetsz); + 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); @@ -1418,8 +1450,8 @@ main(int ac, char **av) if (!FD_ISSET(listen_socks[i], fdset)) continue; fromlen = sizeof(from); - newsock = accept(listen_socks[i], (struct sockaddr *)&from, - &fromlen); + newsock = accept(listen_socks[i], + (struct sockaddr *)&from, &fromlen); if (newsock < 0) { if (errno != EINTR && errno != EWOULDBLOCK) error("accept: %.100s", strerror(errno)); @@ -1490,10 +1522,11 @@ main(int ac, char **av) */ 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 + * 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]; @@ -1501,7 +1534,10 @@ main(int ac, char **av) close_listen_socks(); sock_in = newsock; sock_out = newsock; - log_init(__progname, options.log_level, options.log_facility, log_stderr); + log_init(__progname, + options.log_level, + options.log_facility, + log_stderr); if (rexec_flag) close(config_s[0]); break; @@ -1522,7 +1558,10 @@ main(int ac, char **av) close(config_s[1]); } - /* Mark that the key has been used (it was "given" to the child). */ + /* + * 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. */ @@ -1532,8 +1571,6 @@ main(int ac, char **av) } arc4random_stir(); - - /* Close the new socket (the child is now taking care of it). */ close(newsock); } /* child process check (or debug mode) */ @@ -1613,21 +1650,32 @@ 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(); + + /* 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)); - remote_port = get_remote_port(); - remote_ip = get_remote_ipaddr(); + 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. + */ + remote_ip = get_canonical_hostname(0); + +#ifdef SSH_AUDIT_EVENTS + audit_connection_from(remote_ip, remote_port); +#endif #ifdef LIBWRAP /* Check whether logins are denied from this host. */ if (packet_connection_is_on_socket()) { @@ -1649,10 +1697,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. */ @@ -1664,23 +1712,21 @@ main(int ac, char **av) packet_set_nonblocking(); - /* prepare buffers to collect authentication messages */ - buffer_init(&loginmsg); - /* 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; + /* prepare buffer to collect messages to display to user after login */ + buffer_init(&loginmsg); + if (use_privsep) if (privsep_preauth(authctxt) == 1) goto authenticated; - /* prepare buffer to collect messages to display to user after login */ - buffer_init(&loginmsg); - /* perform the key exchange */ /* authenticate user and start session */ if (compat20) { @@ -1700,6 +1746,21 @@ main(int ac, char **av) } authenticated: + /* + * Cancel the alarm we set to limit the time taken for + * authentication. + */ + alarm(0); + signal(SIGALRM, SIG_DFL); + if (startup_pipe != -1) { + close(startup_pipe); + startup_pipe = -1; + } + +#ifdef SSH_AUDIT_EVENTS + audit_event(SSH_AUTH_SUCCESS); +#endif + /* * In privilege separation, we fork another child and prepare * file descriptor passing. @@ -1722,6 +1783,10 @@ main(int ac, char **av) finish_pam(); #endif /* USE_PAM */ +#ifdef SSH_AUDIT_EVENTS + PRIVSEP(audit_event(SSH_CONNECTION_CLOSE)); +#endif + packet_close(); if (use_privsep) @@ -1739,11 +1804,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), @@ -1758,8 +1826,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), @@ -1886,7 +1956,7 @@ do_ssh1_kex(void) if (!rsafail) { BN_mask_bits(session_key_int, sizeof(session_key) * 8); len = BN_num_bytes(session_key_int); - if (len < 0 || len > sizeof(session_key)) { + if (len < 0 || (u_int)len > sizeof(session_key)) { error("do_connection: bad session key len from %s: " "session_key_int %d > sizeof(session_key) %lu", get_remote_ipaddr(), len, (u_long)sizeof(session_key)); @@ -1973,10 +2043,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 */ @@ -1984,6 +2058,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; @@ -2013,5 +2088,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); }