=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/ssh/sshd.c,v retrieving revision 1.132 retrieving revision 1.132.2.8 diff -u -r1.132 -r1.132.2.8 --- src/usr.bin/ssh/sshd.c 2000/10/13 18:34:46 1.132 +++ src/usr.bin/ssh/sshd.c 2002/03/08 17:04:43 1.132.2.8 @@ -40,33 +40,38 @@ */ #include "includes.h" -RCSID("$OpenBSD: sshd.c,v 1.132 2000/10/13 18:34:46 markus Exp $"); +RCSID("$OpenBSD: sshd.c,v 1.132.2.8 2002/03/08 17:04:43 brad Exp $"); +#include +#include +#include + +#include "ssh.h" +#include "ssh1.h" +#include "ssh2.h" #include "xmalloc.h" #include "rsa.h" -#include "ssh.h" -#include "pty.h" +#include "sshpty.h" #include "packet.h" #include "mpaux.h" +#include "log.h" #include "servconf.h" #include "uidswap.h" #include "compat.h" #include "buffer.h" - -#include "ssh2.h" -#include -#include -#include +#include "cipher.h" #include "kex.h" -#include -#include #include "key.h" -#include "dsa.h" #include "dh.h" - -#include "auth.h" #include "myproposal.h" #include "authfile.h" +#include "pathnames.h" +#include "atomicio.h" +#include "canohost.h" +#include "auth.h" +#include "misc.h" +#include "dispatch.h" +#include "channels.h" #ifdef LIBWRAP #include @@ -79,11 +84,13 @@ #define O_NOCTTY 0 #endif +extern char *__progname; + /* Server configuration options. */ ServerOptions options; /* Name of the server configuration file. */ -char *config_file_name = SERVER_CONFIG_FILE; +char *config_file_name = _PATH_SERVER_CONFIG_FILE; /* * Flag indicating whether IPv4 or IPv6. This can be set on the command line. @@ -99,15 +106,18 @@ */ int debug_flag = 0; +/* Flag indicating that the daemon should only test the configuration and keys. */ +int test_flag = 0; + /* Flag indicating that the daemon is being started from inetd. */ int inetd_flag = 0; +/* Flag indicating that sshd should not detach and become a daemon. */ +int no_daemon_flag = 0; + /* debug goes to stderr unless inetd_flag is set */ int log_stderr = 0; -/* argv[0] without path. */ -char *av0; - /* Saved arguments to main(). */ char **saved_argv; @@ -126,6 +136,9 @@ char *client_version_string = NULL; char *server_version_string = NULL; +/* for rekeying XXX fixme */ +Kex *xxx_kex; + /* * Any really sensitive data in the application is contained in this * structure. The idea is that this structure could be locked into memory so @@ -135,45 +148,48 @@ * not very useful. Currently, memory locking is not implemented. */ struct { - RSA *private_key; /* Private part of empheral server key. */ - RSA *host_key; /* Private part of host key. */ - Key *dsa_host_key; /* Private DSA host key. */ + Key *server_key; /* ephemeral server key */ + Key *ssh1_host_key; /* ssh1 host key */ + Key **host_keys; /* all private host keys */ + int have_ssh1_key; + int have_ssh2_key; + u_char ssh1_cookie[SSH_SESSION_KEY_LENGTH]; } sensitive_data; /* - * Flag indicating whether the current session key has been used. This flag - * is set whenever the key is used, and cleared when the key is regenerated. + * Flag indicating whether the RSA server key needs to be regenerated. + * Is set in the SIGALRM handler and cleared when the key is regenerated. */ -int key_used = 0; +static volatile sig_atomic_t key_do_regen = 0; -/* This is set to true when SIGHUP is received. */ -int received_sighup = 0; +/* This is set to true when a signal is received. */ +static volatile sig_atomic_t received_sighup = 0; +static volatile sig_atomic_t received_sigterm = 0; -/* Public side of the server key. This value is regenerated regularly with - the private key. */ -RSA *public_key; - /* session identifier, used by RSA-auth */ -unsigned char session_id[16]; +u_char session_id[16]; /* same for ssh2 */ -unsigned char *session_id2 = NULL; +u_char *session_id2 = NULL; int session_id2_len = 0; /* record remote hostname or ip */ -unsigned int utmp_len = MAXHOSTNAMELEN; +u_int utmp_len = MAXHOSTNAMELEN; +/* options.max_startup sized array of fd ints */ +int *startup_pipes = NULL; +int startup_pipe; /* in child */ + /* Prototypes for various functions defined later in this file. */ -void do_ssh1_kex(); -void do_ssh2_kex(); +void destroy_sensitive_data(void); -void ssh_dh1_server(Kex *, Buffer *_kexinit, Buffer *); -void ssh_dhgex_server(Kex *, Buffer *_kexinit, Buffer *); +static void do_ssh1_kex(void); +static void do_ssh2_kex(void); /* * Close all listening sockets */ -void +static void close_listen_socks(void) { int i; @@ -182,51 +198,60 @@ num_listen_socks = -1; } +static void +close_startup_pipes(void) +{ + int i; + if (startup_pipes) + for (i = 0; i < options.max_startups; i++) + if (startup_pipes[i] != -1) + close(startup_pipes[i]); +} + /* * Signal handler for SIGHUP. Sshd execs itself when it receives SIGHUP; * the effect is to reread the configuration file (and to regenerate * the server key). */ -void +static void sighup_handler(int sig) { + int save_errno = errno; + received_sighup = 1; signal(SIGHUP, sighup_handler); + errno = save_errno; } /* * Called from the main program after receiving SIGHUP. * Restarts the server. */ -void -sighup_restart() +static void +sighup_restart(void) { log("Received SIGHUP; restarting."); close_listen_socks(); + close_startup_pipes(); execv(saved_argv[0], saved_argv); - log("RESTART FAILED: av0='%s', error: %s.", av0, strerror(errno)); + log("RESTART FAILED: av[0]='%.100s', error: %.100s.", saved_argv[0], strerror(errno)); exit(1); } /* * Generic signal handler for terminating signals in the master daemon. - * These close the listen socket; not closing it seems to cause "Address - * already in use" problems on some machines, which is inconvenient. */ -void +static void sigterm_handler(int sig) { - log("Received signal %d; terminating.", sig); - close_listen_socks(); - unlink(options.pid_file); - exit(255); + received_sigterm = sig; } /* * SIGCHLD handler. This is called whenever a child dies. This will then - * reap any zombies left by exited c. + * reap any zombies left by exited children. */ -void +static void main_sigchld_handler(int sig) { int save_errno = errno; @@ -242,9 +267,11 @@ /* * Signal handler for the alarm after the login grace period has expired. */ -void +static void grace_alarm_handler(int sig) { + /* XXX no idea how fix this signal handler */ + /* Close the connection. */ packet_close(); @@ -259,38 +286,39 @@ * Thus there should be no concurrency control/asynchronous execution * problems. */ -/* XXX do we really want this work to be done in a signal handler ? -m */ -void -key_regeneration_alarm(int sig) +static void +generate_ephemeral_server_key(void) { - int save_errno = errno; + u_int32_t rand = 0; + int i; - /* Check if we should generate a new key. */ - if (key_used) { - /* This should really be done in the background. */ - log("Generating new %d bit RSA key.", options.server_key_bits); + verbose("Generating %s%d bit RSA key.", + sensitive_data.server_key ? "new " : "", options.server_key_bits); + if (sensitive_data.server_key != NULL) + key_free(sensitive_data.server_key); + sensitive_data.server_key = key_generate(KEY_RSA1, + options.server_key_bits); + verbose("RSA key generation complete."); - if (sensitive_data.private_key != NULL) - RSA_free(sensitive_data.private_key); - sensitive_data.private_key = RSA_new(); - - if (public_key != NULL) - RSA_free(public_key); - public_key = RSA_new(); - - rsa_generate_key(sensitive_data.private_key, public_key, - options.server_key_bits); - arc4random_stir(); - key_used = 0; - log("RSA key generation complete."); + for (i = 0; i < SSH_SESSION_KEY_LENGTH; i++) { + if (i % 4 == 0) + rand = arc4random(); + sensitive_data.ssh1_cookie[i] = rand & 0xff; + rand >>= 8; } - /* Reschedule the alarm. */ - signal(SIGALRM, key_regeneration_alarm); - alarm(options.key_regeneration_time); + arc4random_stir(); +} + +static void +key_regeneration_alarm(int sig) +{ + int save_errno = errno; + signal(SIGALRM, SIG_DFL); errno = save_errno; + key_do_regen = 1; } -void +static void sshd_exchange_identification(int sock_in, int sock_out) { int i, mismatch; @@ -318,19 +346,20 @@ /* Send our protocol version identification. */ if (atomicio(write, sock_out, server_version_string, strlen(server_version_string)) != strlen(server_version_string)) { - log("Could not write ident string to %s.", get_remote_ipaddr()); + log("Could not write ident string to %s", get_remote_ipaddr()); fatal_cleanup(); } - /* Read other side\'s version identification. */ + /* Read other side's version identification. */ + memset(buf, 0, sizeof(buf)); for (i = 0; i < sizeof(buf) - 1; i++) { if (atomicio(read, sock_in, &buf[i], 1) != 1) { - log("Did not receive ident string from %s.", get_remote_ipaddr()); + log("Did not receive identification string from %s", + get_remote_ipaddr()); fatal_cleanup(); } if (buf[i] == '\r') { - buf[i] = '\n'; - buf[i + 1] = 0; + buf[i] = 0; /* Kludge for F-Secure Macintosh < 1.0.2 */ if (i == 12 && strncmp(buf, "SSH-1.5-W1.0", 12) == 0) @@ -338,8 +367,7 @@ continue; } if (buf[i] == '\n') { - /* buf[i] == '\n' */ - buf[i + 1] = 0; + buf[i] = 0; break; } } @@ -362,12 +390,18 @@ fatal_cleanup(); } debug("Client protocol version %d.%d; client software version %.100s", - remote_major, remote_minor, remote_version); + remote_major, remote_minor, remote_version); compat_datafellows(remote_version); + if (datafellows & SSH_BUG_SCANNER) { + log("scanned from %s with %s. Don't panic.", + get_remote_ipaddr(), client_version_string); + fatal_cleanup(); + } + mismatch = 0; - switch(remote_major) { + switch (remote_major) { case 1: if (remote_minor == 99) { if (options.protocol & SSH_PROTO_2) @@ -399,7 +433,6 @@ break; } chop(server_version_string); - chop(client_version_string); debug("Local version string %.200s", server_version_string); if (mismatch) { @@ -412,32 +445,77 @@ server_version_string, client_version_string); fatal_cleanup(); } - if (compat20) - packet_set_ssh2_format(); } +/* Destroy the host and server keys. They will no longer be needed. */ void destroy_sensitive_data(void) { - /* Destroy the private and public keys. They will no longer be needed. */ - if (public_key) - RSA_free(public_key); - if (sensitive_data.private_key) - RSA_free(sensitive_data.private_key); - if (sensitive_data.host_key) - RSA_free(sensitive_data.host_key); - if (sensitive_data.dsa_host_key != NULL) - key_free(sensitive_data.dsa_host_key); + int i; + + if (sensitive_data.server_key) { + key_free(sensitive_data.server_key); + sensitive_data.server_key = NULL; + } + for (i = 0; i < options.num_host_key_files; i++) { + if (sensitive_data.host_keys[i]) { + key_free(sensitive_data.host_keys[i]); + sensitive_data.host_keys[i] = NULL; + } + } + sensitive_data.ssh1_host_key = NULL; + memset(sensitive_data.ssh1_cookie, 0, SSH_SESSION_KEY_LENGTH); } +static char * +list_hostkey_types(void) +{ + Buffer b; + char *p; + int i; + + buffer_init(&b); + for (i = 0; i < options.num_host_key_files; i++) { + Key *key = sensitive_data.host_keys[i]; + if (key == NULL) + continue; + switch (key->type) { + case KEY_RSA: + case KEY_DSA: + if (buffer_len(&b) > 0) + buffer_append(&b, ",", 1); + p = key_ssh_name(key); + buffer_append(&b, p, strlen(p)); + break; + } + } + buffer_append(&b, "\0", 1); + p = xstrdup(buffer_ptr(&b)); + buffer_free(&b); + debug("list_hostkey_types: %s", p); + return p; +} + +static Key * +get_hostkey_by_type(int type) +{ + int i; + for (i = 0; i < options.num_host_key_files; i++) { + Key *key = sensitive_data.host_keys[i]; + if (key != NULL && key->type == type) + return key; + } + return NULL; +} + /* * returns 1 if connection should be dropped, 0 otherwise. * dropping starts at connection #max_startups_begin with a probability * of (max_startups_rate/100). the probability increases linearly until * all connections are dropped for startups > max_startups */ -int +static int drop_connection(int startups) { double p, r; @@ -460,8 +538,30 @@ return (r < p) ? 1 : 0; } -int *startup_pipes = NULL; /* options.max_startup sized array of fd ints */ -int startup_pipe; /* in child */ +static void +usage(void) +{ + fprintf(stderr, "sshd version %s\n", SSH_VERSION); + fprintf(stderr, "Usage: %s [options]\n", __progname); + fprintf(stderr, "Options:\n"); + fprintf(stderr, " -f file Configuration file (default %s)\n", _PATH_SERVER_CONFIG_FILE); + fprintf(stderr, " -d Debugging mode (multiple -d means more debugging)\n"); + fprintf(stderr, " -i Started from inetd\n"); + fprintf(stderr, " -D Do not fork into daemon mode\n"); + fprintf(stderr, " -t Only test configuration file and keys\n"); + fprintf(stderr, " -q Quiet (no logging)\n"); + fprintf(stderr, " -p port Listen on the specified port (default: 22)\n"); + fprintf(stderr, " -k seconds Regenerate server key every this many seconds (default: 3600)\n"); + fprintf(stderr, " -g seconds Grace period for authentication (default: 600)\n"); + fprintf(stderr, " -b bits Size of server RSA key (default: 768 bits)\n"); + fprintf(stderr, " -h file File from which to read host key (default: %s)\n", + _PATH_HOST_KEY_FILE); + fprintf(stderr, " -u len Maximum hostname length for utmp recording\n"); + fprintf(stderr, " -4 Use IPv4 only\n"); + fprintf(stderr, " -6 Use IPv6 only\n"); + fprintf(stderr, " -o option Process the option as if it was read from a configuration file.\n"); + exit(1); +} /* * Main program for the daemon. @@ -474,7 +574,6 @@ int opt, sock_in = 0, sock_out = 0, newsock, j, i, fdsetsz, on = 1; pid_t pid; socklen_t fromlen; - int silent = 0; fd_set *fdset; struct sockaddr_storage from; const char *remote_ip; @@ -486,19 +585,17 @@ int listen_sock, maxfd; int startup_p[2]; int startups = 0; + Key *key; + int ret, key_used = 0; - /* Save argv[0]. */ + /* Save argv. */ saved_argv = av; - if (strchr(av[0], '/')) - av0 = strrchr(av[0], '/') + 1; - else - av0 = av[0]; /* 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:V:u:diqQ46")) != EOF) { + while ((opt = getopt(ac, av, "f:p:b:k:h:g:V:u:o:dDeiqtQ46")) != -1) { switch (opt) { case '4': IPv4or6 = AF_INET; @@ -520,11 +617,17 @@ exit(1); } break; + case 'D': + no_daemon_flag = 1; + break; + case 'e': + log_stderr = 1; + break; case 'i': inetd_flag = 1; break; case 'Q': - silent = 1; + /* ignored */ break; case 'q': options.log_level = SYSLOG_LEVEL_QUIET; @@ -538,55 +641,66 @@ fprintf(stderr, "too many ports.\n"); exit(1); } - options.ports[options.num_ports++] = atoi(optarg); + options.ports[options.num_ports++] = a2port(optarg); + if (options.ports[options.num_ports-1] == 0) { + fprintf(stderr, "Bad port number.\n"); + exit(1); + } break; case 'g': - options.login_grace_time = atoi(optarg); + if ((options.login_grace_time = convtime(optarg)) == -1) { + fprintf(stderr, "Invalid login grace time.\n"); + exit(1); + } break; case 'k': - options.key_regeneration_time = atoi(optarg); + if ((options.key_regeneration_time = convtime(optarg)) == -1) { + fprintf(stderr, "Invalid key regeneration interval.\n"); + exit(1); + } break; case 'h': - options.host_key_file = optarg; + if (options.num_host_key_files >= MAX_HOSTKEYS) { + fprintf(stderr, "too many host keys.\n"); + exit(1); + } + options.host_key_files[options.num_host_key_files++] = optarg; break; case 'V': client_version_string = optarg; /* only makes sense with inetd_flag, i.e. no listen() */ inetd_flag = 1; break; + case 't': + test_flag = 1; + break; case 'u': utmp_len = atoi(optarg); break; + case 'o': + if (process_server_config_line(&options, optarg, + "command-line", 0) != 0) + exit(1); + break; case '?': default: - fprintf(stderr, "sshd version %s\n", SSH_VERSION); - fprintf(stderr, "Usage: %s [options]\n", av0); - fprintf(stderr, "Options:\n"); - fprintf(stderr, " -f file Configuration file (default %s)\n", SERVER_CONFIG_FILE); - fprintf(stderr, " -d Debugging mode (multiple -d means more debugging)\n"); - fprintf(stderr, " -i Started from inetd\n"); - fprintf(stderr, " -q Quiet (no logging)\n"); - fprintf(stderr, " -p port Listen on the specified port (default: 22)\n"); - fprintf(stderr, " -k seconds Regenerate server key every this many seconds (default: 3600)\n"); - fprintf(stderr, " -g seconds Grace period for authentication (default: 300)\n"); - fprintf(stderr, " -b bits Size of server RSA key (default: 768 bits)\n"); - fprintf(stderr, " -h file File from which to read host key (default: %s)\n", - HOST_KEY_FILE); - fprintf(stderr, " -u len Maximum hostname length for utmp recording\n"); - fprintf(stderr, " -4 Use IPv4 only\n"); - fprintf(stderr, " -6 Use IPv6 only\n"); - exit(1); + usage(); + break; } } + SSLeay_add_all_algorithms(); + channel_set_af(IPv4or6); /* * Force logging to stderr until we have loaded the private host * key (unless started from inetd) */ - log_init(av0, - options.log_level == -1 ? SYSLOG_LEVEL_INFO : options.log_level, - options.log_facility == -1 ? SYSLOG_FACILITY_AUTH : options.log_facility, - !silent && !inetd_flag); + log_init(__progname, + options.log_level == SYSLOG_LEVEL_NOT_SET ? + SYSLOG_LEVEL_INFO : options.log_level, + options.log_facility == SYSLOG_FACILITY_NOT_SET ? + SYSLOG_FACILITY_AUTH : options.log_facility, + !inetd_flag); /* Read server configuration options from the configuration file. */ read_server_config(&options, config_file_name); @@ -602,44 +716,47 @@ debug("sshd version %.100s", SSH_VERSION); - sensitive_data.dsa_host_key = NULL; - sensitive_data.host_key = NULL; + /* load private host keys */ + sensitive_data.host_keys = xmalloc(options.num_host_key_files*sizeof(Key*)); + for (i = 0; i < options.num_host_key_files; i++) + sensitive_data.host_keys[i] = NULL; + sensitive_data.server_key = NULL; + sensitive_data.ssh1_host_key = NULL; + sensitive_data.have_ssh1_key = 0; + sensitive_data.have_ssh2_key = 0; - /* check if RSA support exists */ - if ((options.protocol & SSH_PROTO_1) && - rsa_alive() == 0) { - log("no RSA support in libssl and libcrypto. See ssl(8)"); - log("Disabling protocol version 1"); - options.protocol &= ~SSH_PROTO_1; - } - /* Load the RSA/DSA host key. It must have empty passphrase. */ - if (options.protocol & SSH_PROTO_1) { - Key k; - sensitive_data.host_key = RSA_new(); - k.type = KEY_RSA; - k.rsa = sensitive_data.host_key; - errno = 0; - if (!load_private_key(options.host_key_file, "", &k, NULL)) { - error("Could not load host key: %.200s: %.100s", - options.host_key_file, strerror(errno)); - log("Disabling protocol version 1"); - options.protocol &= ~SSH_PROTO_1; + for (i = 0; i < options.num_host_key_files; i++) { + key = key_load_private(options.host_key_files[i], "", NULL); + sensitive_data.host_keys[i] = key; + if (key == NULL) { + error("Could not load host key: %s", + options.host_key_files[i]); + sensitive_data.host_keys[i] = NULL; + continue; } - k.rsa = NULL; - } - if (options.protocol & SSH_PROTO_2) { - sensitive_data.dsa_host_key = key_new(KEY_DSA); - if (!load_private_key(options.host_dsa_key_file, "", sensitive_data.dsa_host_key, NULL)) { - - error("Could not load DSA host key: %.200s", options.host_dsa_key_file); - log("Disabling protocol version 2"); - options.protocol &= ~SSH_PROTO_2; + switch (key->type) { + case KEY_RSA1: + sensitive_data.ssh1_host_key = key; + sensitive_data.have_ssh1_key = 1; + break; + case KEY_RSA: + case KEY_DSA: + sensitive_data.have_ssh2_key = 1; + break; } + debug("private host key: #%d type %d %s", i, key->type, + key_type(key)); } - if (! options.protocol & (SSH_PROTO_1|SSH_PROTO_2)) { - if (silent == 0) - fprintf(stderr, "sshd: no hostkeys available -- exiting.\n"); - log("sshd: no hostkeys available -- exiting.\n"); + if ((options.protocol & SSH_PROTO_1) && !sensitive_data.have_ssh1_key) { + log("Disabling protocol version 1. Could not load host key"); + options.protocol &= ~SSH_PROTO_1; + } + if ((options.protocol & SSH_PROTO_2) && !sensitive_data.have_ssh2_key) { + log("Disabling protocol version 2. Could not load host key"); + options.protocol &= ~SSH_PROTO_2; + } + if (!(options.protocol & (SSH_PROTO_1|SSH_PROTO_2))) { + log("sshd: no hostkeys available -- exiting."); exit(1); } @@ -656,27 +773,31 @@ * hate software patents. I dont know if this can go? Niels */ if (options.server_key_bits > - BN_num_bits(sensitive_data.host_key->n) - SSH_KEY_BITS_RESERVED && + BN_num_bits(sensitive_data.ssh1_host_key->rsa->n) - SSH_KEY_BITS_RESERVED && options.server_key_bits < - BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED) { + BN_num_bits(sensitive_data.ssh1_host_key->rsa->n) + SSH_KEY_BITS_RESERVED) { options.server_key_bits = - BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED; + BN_num_bits(sensitive_data.ssh1_host_key->rsa->n) + SSH_KEY_BITS_RESERVED; debug("Forcing server key to %d bits to make it differ from host key.", options.server_key_bits); } } + /* Configuration looks good, so exit if in test mode. */ + if (test_flag) + exit(0); + /* Initialize the log (it is reinitialized below in case we forked). */ if (debug_flag && !inetd_flag) log_stderr = 1; - log_init(av0, options.log_level, options.log_facility, log_stderr); + log_init(__progname, options.log_level, options.log_facility, log_stderr); /* * If not in debugging mode, and not started from inetd, disconnect * from the controlling terminal, and fork. The original process * exits. */ - if (!debug_flag && !inetd_flag) { + if (!(debug_flag || inetd_flag || no_daemon_flag)) { #ifdef TIOCNOTTY int fd; #endif /* TIOCNOTTY */ @@ -685,7 +806,7 @@ /* Disconnect from the controlling tty. */ #ifdef TIOCNOTTY - fd = open("/dev/tty", O_RDWR | O_NOCTTY); + fd = open(_PATH_TTY, O_RDWR | O_NOCTTY); if (fd >= 0) { (void) ioctl(fd, TIOCNOTTY, NULL); close(fd); @@ -693,11 +814,8 @@ #endif /* TIOCNOTTY */ } /* Reinitialize the log (because of the fork above). */ - log_init(av0, options.log_level, options.log_facility, log_stderr); + log_init(__progname, options.log_level, options.log_facility, log_stderr); - /* Do not display messages to stdout in RSA code. */ - rsa_set_verbose(0); - /* Initialize the random number generator. */ arc4random_stir(); @@ -705,11 +823,14 @@ unmounted if desired. */ chdir("/"); + /* ignore SIGPIPE */ + signal(SIGPIPE, SIG_IGN); + /* Start listening for a socket, unless started from inetd. */ if (inetd_flag) { - int s1, s2; + int s1; s1 = dup(0); /* Make sure descriptors 0, 1, and 2 are in use. */ - s2 = dup(s1); + dup(s1); sock_in = dup(0); sock_out = dup(1); startup_pipe = -1; @@ -719,16 +840,8 @@ * ttyfd happens to be one of those. */ debug("inetd sockets after dupping: %d, %d", sock_in, sock_out); - - if (options.protocol & SSH_PROTO_1) { - public_key = RSA_new(); - sensitive_data.private_key = RSA_new(); - log("Generating %d bit RSA key.", options.server_key_bits); - rsa_generate_key(sensitive_data.private_key, public_key, - options.server_key_bits); - arc4random_stir(); - log("RSA key generation complete."); - } + if (options.protocol & SSH_PROTO_1) + generate_ephemeral_server_key(); } else { for (ai = options.listen_addrs; ai; ai = ai->ai_next) { if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) @@ -761,11 +874,11 @@ * close. */ setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, - (void *) &on, sizeof(on)); + &on, sizeof(on)); linger.l_onoff = 1; linger.l_linger = 5; setsockopt(listen_sock, SOL_SOCKET, SO_LINGER, - (void *) &linger, sizeof(linger)); + &linger, sizeof(linger)); debug("Bind to port %s on %s.", strport, ntop); @@ -790,44 +903,37 @@ if (!num_listen_socks) fatal("Cannot bind any address."); + 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(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 */ if (!debug_flag) { /* - * Record our pid in /etc/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 + * 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, "w"); if (f) { - fprintf(f, "%u\n", (unsigned int) getpid()); + fprintf(f, "%u\n", (u_int) getpid()); fclose(f); } } - if (options.protocol & SSH_PROTO_1) { - public_key = RSA_new(); - sensitive_data.private_key = RSA_new(); - log("Generating %d bit RSA key.", options.server_key_bits); - rsa_generate_key(sensitive_data.private_key, public_key, - options.server_key_bits); - arc4random_stir(); - log("RSA key generation complete."); - - /* Schedule server key regeneration alarm. */ - signal(SIGALRM, key_regeneration_alarm); - alarm(options.key_regeneration_time); - } - - /* Arrange to restart on SIGHUP. The handler needs listen_sock. */ - signal(SIGHUP, sighup_handler); - - signal(SIGTERM, sigterm_handler); - signal(SIGQUIT, sigterm_handler); - - /* Arrange SIGCHLD to be caught. */ - signal(SIGCHLD, main_sigchld_handler); - /* setup fd set for listen */ fdset = NULL; maxfd = 0; @@ -848,7 +954,7 @@ sighup_restart(); if (fdset != NULL) xfree(fdset); - fdsetsz = howmany(maxfd, NFDBITS) * sizeof(fd_mask); + fdsetsz = howmany(maxfd+1, NFDBITS) * sizeof(fd_mask); fdset = (fd_set *)xmalloc(fdsetsz); memset(fdset, 0, fdsetsz); @@ -859,18 +965,31 @@ FD_SET(startup_pipes[i], fdset); /* Wait in select until there is a connection. */ - if (select(maxfd + 1, fdset, NULL, NULL, NULL) < 0) { - if (errno != EINTR) - error("select: %.100s", strerror(errno)); - continue; + ret = select(maxfd+1, fdset, NULL, NULL, NULL); + if (ret < 0 && errno != EINTR) + error("select: %.100s", strerror(errno)); + if (received_sigterm) { + log("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 successfull authentication + * after successful authentication * or if the child has died */ close(startup_pipes[i]); @@ -890,6 +1009,7 @@ } if (fcntl(newsock, F_SETFL, 0) < 0) { error("newsock del O_NONBLOCK: %s", strerror(errno)); + close(newsock); continue; } if (drop_connection(startups) == 1) { @@ -910,7 +1030,7 @@ startups++; break; } - + /* * Got connection. Fork a child to handle it, unless * we are in debugging mode. @@ -943,13 +1063,11 @@ * the connection. */ startup_pipe = startup_p[1]; - for (j = 0; j < options.max_startups; j++) - if (startup_pipes[j] != -1) - close(startup_pipes[j]); + close_startup_pipes(); close_listen_socks(); sock_in = newsock; sock_out = newsock; - log_init(av0, options.log_level, options.log_facility, log_stderr); + log_init(__progname, options.log_level, options.log_facility, log_stderr); break; } } @@ -963,7 +1081,13 @@ close(startup_p[1]); /* Mark that the key has been used (it was "given" to the child). */ - key_used = 1; + 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(); @@ -998,8 +1122,14 @@ /* setsockopt(sock_in, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); */ linger.l_onoff = 1; linger.l_linger = 5; - setsockopt(sock_in, SOL_SOCKET, SO_LINGER, (void *) &linger, sizeof(linger)); + setsockopt(sock_in, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger)); + /* Set keepalives if requested. */ + if (options.keepalives && + 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. @@ -1009,23 +1139,23 @@ remote_port = get_remote_port(); remote_ip = get_remote_ipaddr(); - /* Check whether logins are denied from this host. */ #ifdef LIBWRAP - /* XXX LIBWRAP noes not know about IPv6 */ + /* Check whether logins are denied from this host. */ { struct request_info req; - request_init(&req, RQ_DAEMON, av0, RQ_FILE, sock_in, NULL); + request_init(&req, RQ_DAEMON, __progname, RQ_FILE, sock_in, 0); fromhost(&req); if (!hosts_access(&req)) { - close(sock_in); - close(sock_out); + debug("Connection refused by tcp wrapper"); refuse(&req); + /* NOTREACHED */ + fatal("libwrap refuse returns"); } -/*XXX IPv6 verbose("Connection from %.500s port %d", eval_client(&req), remote_port); */ } #endif /* LIBWRAP */ + /* Log the connection. */ verbose("Connection from %.500s port %d", remote_ip, remote_port); @@ -1043,24 +1173,33 @@ sshd_exchange_identification(sock_in, sock_out); /* - * Check that the connection comes from a privileged port. Rhosts- - * and Rhosts-RSA-Authentication only make sense from priviledged + * Check that the connection comes from a privileged port. + * Rhosts-Authentication only makes sense from priviledged * programs. Of course, if the intruder has root access on his local * machine, he can connect from any port. So do not use these * authentication methods from machines that you do not trust. */ - if (remote_port >= IPPORT_RESERVED || - remote_port < IPPORT_RESERVED / 2) { + if (options.rhosts_authentication && + (remote_port >= IPPORT_RESERVED || + remote_port < IPPORT_RESERVED / 2)) { + debug("Rhosts Authentication disabled, " + "originating port %d not trusted.", remote_port); options.rhosts_authentication = 0; - options.rhosts_rsa_authentication = 0; } -#ifdef KRB4 +#if defined(KRB4) && !defined(KRB5) if (!packet_connection_is_ipv4() && options.kerberos_authentication) { debug("Kerberos Authentication disabled, only available for IPv4."); options.kerberos_authentication = 0; } -#endif /* KRB4 */ +#endif /* KRB4 && !KRB5 */ +#ifdef AFS + /* If machine has AFS, set process authentication group. */ + if (k_hasafs()) { + k_setpag(); + k_unlog(); + } +#endif /* AFS */ packet_set_nonblocking(); @@ -1073,13 +1212,6 @@ do_ssh1_kex(); do_authentication(); } - -#ifdef KRB4 - /* Cleanup user's ticket cache file. */ - if (options.kerberos_ticket_cleanup) - (void) dest_tkt(); -#endif /* KRB4 */ - /* The connection has been terminated. */ verbose("Closing connection to %.100s", remote_ip); packet_close(); @@ -1089,15 +1221,15 @@ /* * SSH1 key exchange */ -void -do_ssh1_kex() +static void +do_ssh1_kex(void) { int i, len; - int plen, slen; + int rsafail = 0; BIGNUM *session_key_int; - unsigned char session_key[SSH_SESSION_KEY_LENGTH]; - unsigned char cookie[8]; - unsigned int cipher_type, auth_mask, protocol_flags; + u_char session_key[SSH_SESSION_KEY_LENGTH]; + u_char cookie[8]; + u_int cipher_type, auth_mask, protocol_flags; u_int32_t rand = 0; /* @@ -1126,14 +1258,14 @@ packet_put_char(cookie[i]); /* Store our public server RSA key. */ - packet_put_int(BN_num_bits(public_key->n)); - packet_put_bignum(public_key->e); - packet_put_bignum(public_key->n); + packet_put_int(BN_num_bits(sensitive_data.server_key->rsa->n)); + packet_put_bignum(sensitive_data.server_key->rsa->e); + packet_put_bignum(sensitive_data.server_key->rsa->n); /* Store our public host RSA key. */ - packet_put_int(BN_num_bits(sensitive_data.host_key->n)); - packet_put_bignum(sensitive_data.host_key->e); - packet_put_bignum(sensitive_data.host_key->n); + packet_put_int(BN_num_bits(sensitive_data.ssh1_host_key->rsa->n)); + packet_put_bignum(sensitive_data.ssh1_host_key->rsa->e); + packet_put_bignum(sensitive_data.ssh1_host_key->rsa->n); /* Put protocol flags. */ packet_put_int(SSH_PROTOFLAG_HOST_IN_FWD_OPEN); @@ -1149,20 +1281,20 @@ auth_mask |= 1 << SSH_AUTH_RHOSTS_RSA; if (options.rsa_authentication) auth_mask |= 1 << SSH_AUTH_RSA; -#ifdef KRB4 +#if defined(KRB4) || defined(KRB5) if (options.kerberos_authentication) auth_mask |= 1 << SSH_AUTH_KERBEROS; #endif -#ifdef AFS +#if defined(AFS) || defined(KRB5) if (options.kerberos_tgt_passing) auth_mask |= 1 << SSH_PASS_KERBEROS_TGT; +#endif +#ifdef AFS if (options.afs_token_passing) auth_mask |= 1 << SSH_PASS_AFS_TOKEN; #endif -#ifdef SKEY - if (options.skey_authentication == 1) + if (options.challenge_response_authentication == 1) auth_mask |= 1 << SSH_AUTH_TIS; -#endif if (options.password_authentication) auth_mask |= 1 << SSH_AUTH_PASSWORD; packet_put_int(auth_mask); @@ -1171,11 +1303,12 @@ packet_send(); packet_write_wait(); - debug("Sent %d bit public key and %d bit host key.", - BN_num_bits(public_key->n), BN_num_bits(sensitive_data.host_key->n)); + debug("Sent %d bit server key and %d bit host key.", + BN_num_bits(sensitive_data.server_key->rsa->n), + BN_num_bits(sensitive_data.ssh1_host_key->rsa->n)); /* Read clients reply (cipher type and session key). */ - packet_read_expect(&plen, SSH_CMSG_SESSION_KEY); + packet_read_expect(SSH_CMSG_SESSION_KEY); /* Get cipher type and check whether we accept this. */ cipher_type = packet_get_char(); @@ -1192,76 +1325,107 @@ debug("Encryption type: %.200s", cipher_name(cipher_type)); /* Get the encrypted integer. */ - session_key_int = BN_new(); - packet_get_bignum(session_key_int, &slen); + if ((session_key_int = BN_new()) == NULL) + fatal("do_ssh1_kex: BN_new failed"); + packet_get_bignum(session_key_int); protocol_flags = packet_get_int(); packet_set_protocol_flags(protocol_flags); + packet_check_eom(); - packet_integrity_check(plen, 1 + 8 + slen + 4, SSH_CMSG_SESSION_KEY); - /* * Decrypt it using our private server key and private host key (key * with larger modulus first). */ - if (BN_cmp(sensitive_data.private_key->n, sensitive_data.host_key->n) > 0) { - /* Private key has bigger modulus. */ - if (BN_num_bits(sensitive_data.private_key->n) < - BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED) { - fatal("do_connection: %s: private_key %d < host_key %d + SSH_KEY_BITS_RESERVED %d", - get_remote_ipaddr(), - BN_num_bits(sensitive_data.private_key->n), - BN_num_bits(sensitive_data.host_key->n), - SSH_KEY_BITS_RESERVED); + 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", + get_remote_ipaddr(), + BN_num_bits(sensitive_data.server_key->rsa->n), + BN_num_bits(sensitive_data.ssh1_host_key->rsa->n), + SSH_KEY_BITS_RESERVED); } - rsa_private_decrypt(session_key_int, session_key_int, - sensitive_data.private_key); - rsa_private_decrypt(session_key_int, session_key_int, - sensitive_data.host_key); + if (rsa_private_decrypt(session_key_int, session_key_int, + sensitive_data.server_key->rsa) <= 0) + rsafail++; + if (rsa_private_decrypt(session_key_int, session_key_int, + sensitive_data.ssh1_host_key->rsa) <= 0) + rsafail++; } else { /* Host key has bigger modulus (or they are equal). */ - if (BN_num_bits(sensitive_data.host_key->n) < - BN_num_bits(sensitive_data.private_key->n) + SSH_KEY_BITS_RESERVED) { - fatal("do_connection: %s: host_key %d < private_key %d + SSH_KEY_BITS_RESERVED %d", - get_remote_ipaddr(), - BN_num_bits(sensitive_data.host_key->n), - BN_num_bits(sensitive_data.private_key->n), - SSH_KEY_BITS_RESERVED); + 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", + get_remote_ipaddr(), + BN_num_bits(sensitive_data.ssh1_host_key->rsa->n), + BN_num_bits(sensitive_data.server_key->rsa->n), + SSH_KEY_BITS_RESERVED); } - rsa_private_decrypt(session_key_int, session_key_int, - sensitive_data.host_key); - rsa_private_decrypt(session_key_int, session_key_int, - sensitive_data.private_key); + if (rsa_private_decrypt(session_key_int, session_key_int, + sensitive_data.ssh1_host_key->rsa) < 0) + rsafail++; + if (rsa_private_decrypt(session_key_int, session_key_int, + sensitive_data.server_key->rsa) < 0) + rsafail++; } - - compute_session_id(session_id, cookie, - sensitive_data.host_key->n, - sensitive_data.private_key->n); - - /* Destroy the private and public keys. They will no longer be needed. */ - destroy_sensitive_data(); - /* * Extract session key from the decrypted integer. The key is in the * least significant 256 bits of the integer; the first byte of the * key is in the highest bits. */ - BN_mask_bits(session_key_int, sizeof(session_key) * 8); - len = BN_num_bytes(session_key_int); - if (len < 0 || len > sizeof(session_key)) - fatal("do_connection: bad len from %s: session_key_int %d > sizeof(session_key) %d", - get_remote_ipaddr(), - len, sizeof(session_key)); - memset(session_key, 0, sizeof(session_key)); - BN_bn2bin(session_key_int, session_key + sizeof(session_key) - len); + 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)) { + 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)); + rsafail++; + } else { + memset(session_key, 0, sizeof(session_key)); + BN_bn2bin(session_key_int, + session_key + sizeof(session_key) - len); + compute_session_id(session_id, cookie, + sensitive_data.ssh1_host_key->rsa->n, + sensitive_data.server_key->rsa->n); + /* + * Xor the first 16 bytes of the session key with the + * session id. + */ + for (i = 0; i < 16; i++) + session_key[i] ^= session_id[i]; + } + } + if (rsafail) { + int bytes = BN_num_bytes(session_key_int); + u_char *buf = xmalloc(bytes); + MD5_CTX md; + + log("do_connection: generating a fake encryption key"); + BN_bn2bin(session_key_int, buf); + MD5_Init(&md); + MD5_Update(&md, buf, bytes); + MD5_Update(&md, sensitive_data.ssh1_cookie, SSH_SESSION_KEY_LENGTH); + MD5_Final(session_key, &md); + MD5_Init(&md); + MD5_Update(&md, session_key, 16); + MD5_Update(&md, buf, bytes); + MD5_Update(&md, sensitive_data.ssh1_cookie, SSH_SESSION_KEY_LENGTH); + MD5_Final(session_key + 16, &md); + memset(buf, 0, bytes); + xfree(buf); + for (i = 0; i < 16; i++) + session_id[i] = session_key[i] ^ session_key[i + 16]; + } + /* Destroy the private and public keys. They will no longer be needed. */ + destroy_sensitive_data(); + /* Destroy the decrypted integer. It is no longer needed. */ BN_clear_free(session_key_int); - /* Xor the first 16 bytes of the session key with the session id. */ - for (i = 0; i < 16; i++) - session_key[i] ^= session_id[i]; - /* Set the session key. From this on all communications will be encrypted. */ packet_set_encryption_key(session_key, SSH_SESSION_KEY_LENGTH, cipher_type); @@ -1279,53 +1443,40 @@ /* * SSH2 key exchange: diffie-hellman-group1-sha1 */ -void -do_ssh2_kex() +static void +do_ssh2_kex(void) { - Buffer *server_kexinit; - Buffer *client_kexinit; - int payload_len; - int i; Kex *kex; - char *cprop[PROPOSAL_MAX]; -/* KEXINIT */ - if (options.ciphers != NULL) { myproposal[PROPOSAL_ENC_ALGS_CTOS] = myproposal[PROPOSAL_ENC_ALGS_STOC] = options.ciphers; } - server_kexinit = kex_init(myproposal); - client_kexinit = xmalloc(sizeof(*client_kexinit)); - buffer_init(client_kexinit); + myproposal[PROPOSAL_ENC_ALGS_CTOS] = + compat_cipher_proposal(myproposal[PROPOSAL_ENC_ALGS_CTOS]); + myproposal[PROPOSAL_ENC_ALGS_STOC] = + compat_cipher_proposal(myproposal[PROPOSAL_ENC_ALGS_STOC]); - /* algorithm negotiation */ - kex_exchange_kexinit(server_kexinit, client_kexinit, cprop); - kex = kex_choose_conf(cprop, myproposal, 1); - for (i = 0; i < PROPOSAL_MAX; i++) - xfree(cprop[i]); - - switch (kex->kex_type) { - case DH_GRP1_SHA1: - ssh_dh1_server(kex, client_kexinit, server_kexinit); - break; - case DH_GEX_SHA1: - ssh_dhgex_server(kex, client_kexinit, server_kexinit); - break; - default: - fatal("Unsupported key exchange %d", kex->kex_type); + if (options.macs != NULL) { + myproposal[PROPOSAL_MAC_ALGS_CTOS] = + myproposal[PROPOSAL_MAC_ALGS_STOC] = options.macs; } + myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = list_hostkey_types(); - debug("send SSH2_MSG_NEWKEYS."); - packet_start(SSH2_MSG_NEWKEYS); - packet_send(); - packet_write_wait(); - debug("done: send SSH2_MSG_NEWKEYS."); + /* start key exchange */ + kex = kex_setup(myproposal); + kex->server = 1; + kex->client_version_string=client_version_string; + kex->server_version_string=server_version_string; + kex->load_host_key=&get_hostkey_by_type; - debug("Wait SSH2_MSG_NEWKEYS."); - packet_read_expect(&payload_len, SSH2_MSG_NEWKEYS); - debug("GOT SSH2_MSG_NEWKEYS."); + xxx_kex = kex; + dispatch_run(DISPATCH_BLOCK, &kex->done, kex); + + session_id2 = kex->session_id; + session_id2_len = kex->session_id_len; + #ifdef DEBUG_KEXDH /* send 1st encrypted/maced/compressed message */ packet_start(SSH2_MSG_IGNORE); @@ -1333,269 +1484,5 @@ packet_send(); packet_write_wait(); #endif - - debug("done: KEX2."); + debug("KEX done"); } - -/* - * SSH2 key exchange - */ - -/* diffie-hellman-group1-sha1 */ - -void -ssh_dh1_server(Kex *kex, Buffer *client_kexinit, Buffer *server_kexinit) -{ -#ifdef DEBUG_KEXDH - int i; -#endif - int payload_len, dlen; - int slen; - unsigned char *signature = NULL; - unsigned char *server_host_key_blob = NULL; - unsigned int sbloblen; - unsigned int klen, kout; - unsigned char *kbuf; - unsigned char *hash; - BIGNUM *shared_secret = 0; - DH *dh; - BIGNUM *dh_client_pub = 0; - -/* KEXDH */ - debug("Wait SSH2_MSG_KEXDH_INIT."); - packet_read_expect(&payload_len, SSH2_MSG_KEXDH_INIT); - - /* key, cert */ - dh_client_pub = BN_new(); - if (dh_client_pub == NULL) - fatal("dh_client_pub == NULL"); - packet_get_bignum2(dh_client_pub, &dlen); - -#ifdef DEBUG_KEXDH - fprintf(stderr, "\ndh_client_pub= "); - BN_print_fp(stderr, dh_client_pub); - fprintf(stderr, "\n"); - debug("bits %d", BN_num_bits(dh_client_pub)); -#endif - - /* generate DH key */ - dh = dh_new_group1(); /* XXX depends on 'kex' */ - -#ifdef DEBUG_KEXDH - fprintf(stderr, "\np= "); - BN_print_fp(stderr, dh->p); - fprintf(stderr, "\ng= "); - bn_print(dh->g); - fprintf(stderr, "\npub= "); - BN_print_fp(stderr, dh->pub_key); - fprintf(stderr, "\n"); - DHparams_print_fp(stderr, dh); -#endif - if (!dh_pub_is_valid(dh, dh_client_pub)) - packet_disconnect("bad client public DH value"); - - klen = DH_size(dh); - kbuf = xmalloc(klen); - kout = DH_compute_key(kbuf, dh_client_pub, dh); - -#ifdef DEBUG_KEXDH - debug("shared secret: len %d/%d", klen, kout); - fprintf(stderr, "shared secret == "); - for (i = 0; i< kout; i++) - fprintf(stderr, "%02x", (kbuf[i])&0xff); - fprintf(stderr, "\n"); -#endif - shared_secret = BN_new(); - - BN_bin2bn(kbuf, kout, shared_secret); - memset(kbuf, 0, klen); - xfree(kbuf); - - /* XXX precompute? */ - dsa_make_key_blob(sensitive_data.dsa_host_key, - &server_host_key_blob, &sbloblen); - - /* calc H */ /* XXX depends on 'kex' */ - hash = kex_hash( - client_version_string, - server_version_string, - buffer_ptr(client_kexinit), buffer_len(client_kexinit), - buffer_ptr(server_kexinit), buffer_len(server_kexinit), - (char *)server_host_key_blob, sbloblen, - dh_client_pub, - dh->pub_key, - shared_secret - ); - buffer_free(client_kexinit); - buffer_free(server_kexinit); - xfree(client_kexinit); - xfree(server_kexinit); -#ifdef DEBUG_KEXDH - fprintf(stderr, "hash == "); - for (i = 0; i< 20; i++) - fprintf(stderr, "%02x", (hash[i])&0xff); - fprintf(stderr, "\n"); -#endif - /* save session id := H */ - /* XXX hashlen depends on KEX */ - session_id2_len = 20; - session_id2 = xmalloc(session_id2_len); - memcpy(session_id2, hash, session_id2_len); - - /* sign H */ - /* XXX hashlen depends on KEX */ - dsa_sign(sensitive_data.dsa_host_key, &signature, &slen, hash, 20); - - destroy_sensitive_data(); - - /* send server hostkey, DH pubkey 'f' and singed H */ - packet_start(SSH2_MSG_KEXDH_REPLY); - packet_put_string((char *)server_host_key_blob, sbloblen); - packet_put_bignum2(dh->pub_key); /* f */ - packet_put_string((char *)signature, slen); - packet_send(); - xfree(signature); - xfree(server_host_key_blob); - packet_write_wait(); - - kex_derive_keys(kex, hash, shared_secret); - packet_set_kex(kex); - - /* have keys, free DH */ - DH_free(dh); -} - -/* diffie-hellman-group-exchange-sha1 */ - -void -ssh_dhgex_server(Kex *kex, Buffer *client_kexinit, Buffer *server_kexinit) -{ -#ifdef DEBUG_KEXDH - int i; -#endif - int payload_len, dlen; - int slen, nbits; - unsigned char *signature = NULL; - unsigned char *server_host_key_blob = NULL; - unsigned int sbloblen; - unsigned int klen, kout; - unsigned char *kbuf; - unsigned char *hash; - BIGNUM *shared_secret = 0; - DH *dh; - BIGNUM *dh_client_pub = 0; - -/* KEXDHGEX */ - debug("Wait SSH2_MSG_KEX_DH_GEX_REQUEST."); - packet_read_expect(&payload_len, SSH2_MSG_KEX_DH_GEX_REQUEST); - nbits = packet_get_int(); - dh = choose_dh(nbits); - - debug("Sending SSH2_MSG_KEX_DH_GEX_GROUP."); - packet_start(SSH2_MSG_KEX_DH_GEX_GROUP); - packet_put_bignum2(dh->p); - packet_put_bignum2(dh->g); - packet_send(); - packet_write_wait(); - - debug("Wait SSH2_MSG_KEX_DH_GEX_INIT."); - packet_read_expect(&payload_len, SSH2_MSG_KEX_DH_GEX_INIT); - - /* key, cert */ - dh_client_pub = BN_new(); - if (dh_client_pub == NULL) - fatal("dh_client_pub == NULL"); - packet_get_bignum2(dh_client_pub, &dlen); - -#ifdef DEBUG_KEXDH - fprintf(stderr, "\ndh_client_pub= "); - BN_print_fp(stderr, dh_client_pub); - fprintf(stderr, "\n"); - debug("bits %d", BN_num_bits(dh_client_pub)); -#endif - -#ifdef DEBUG_KEXDH - fprintf(stderr, "\np= "); - BN_print_fp(stderr, dh->p); - fprintf(stderr, "\ng= "); - bn_print(dh->g); - fprintf(stderr, "\npub= "); - BN_print_fp(stderr, dh->pub_key); - fprintf(stderr, "\n"); - DHparams_print_fp(stderr, dh); -#endif - if (!dh_pub_is_valid(dh, dh_client_pub)) - packet_disconnect("bad client public DH value"); - - klen = DH_size(dh); - kbuf = xmalloc(klen); - kout = DH_compute_key(kbuf, dh_client_pub, dh); - -#ifdef DEBUG_KEXDH - debug("shared secret: len %d/%d", klen, kout); - fprintf(stderr, "shared secret == "); - for (i = 0; i< kout; i++) - fprintf(stderr, "%02x", (kbuf[i])&0xff); - fprintf(stderr, "\n"); -#endif - shared_secret = BN_new(); - - BN_bin2bn(kbuf, kout, shared_secret); - memset(kbuf, 0, klen); - xfree(kbuf); - - /* XXX precompute? */ - dsa_make_key_blob(sensitive_data.dsa_host_key, - &server_host_key_blob, &sbloblen); - - /* calc H */ /* XXX depends on 'kex' */ - hash = kex_hash_gex( - client_version_string, - server_version_string, - buffer_ptr(client_kexinit), buffer_len(client_kexinit), - buffer_ptr(server_kexinit), buffer_len(server_kexinit), - (char *)server_host_key_blob, sbloblen, - nbits, dh->p, dh->g, - dh_client_pub, - dh->pub_key, - shared_secret - ); - buffer_free(client_kexinit); - buffer_free(server_kexinit); - xfree(client_kexinit); - xfree(server_kexinit); -#ifdef DEBUG_KEXDH - fprintf(stderr, "hash == "); - for (i = 0; i< 20; i++) - fprintf(stderr, "%02x", (hash[i])&0xff); - fprintf(stderr, "\n"); -#endif - /* save session id := H */ - /* XXX hashlen depends on KEX */ - session_id2_len = 20; - session_id2 = xmalloc(session_id2_len); - memcpy(session_id2, hash, session_id2_len); - - /* sign H */ - /* XXX hashlen depends on KEX */ - dsa_sign(sensitive_data.dsa_host_key, &signature, &slen, hash, 20); - - destroy_sensitive_data(); - - /* send server hostkey, DH pubkey 'f' and singed H */ - packet_start(SSH2_MSG_KEX_DH_GEX_REPLY); - packet_put_string((char *)server_host_key_blob, sbloblen); - packet_put_bignum2(dh->pub_key); /* f */ - packet_put_string((char *)signature, slen); - packet_send(); - xfree(signature); - xfree(server_host_key_blob); - packet_write_wait(); - - kex_derive_keys(kex, hash, shared_secret); - packet_set_kex(kex); - - /* have keys, free DH */ - DH_free(dh); -} -