diff options
author | Dag-Erling Smørgrav <des@FreeBSD.org> | 2002-03-18 09:55:03 +0000 |
---|---|---|
committer | Dag-Erling Smørgrav <des@FreeBSD.org> | 2002-03-18 09:55:03 +0000 |
commit | ae1f160d56b2d59d406f1af34cbdcc88a9e1b914 (patch) | |
tree | 7ac239d263df7247abaf6488a321dac17f2ebce9 /crypto/openssh/sshd.c | |
parent | 1e8db6e2f63ea90b361b3bbc9ebe9990660cb596 (diff) | |
download | src-test2-ae1f160d56b2d59d406f1af34cbdcc88a9e1b914.tar.gz src-test2-ae1f160d56b2d59d406f1af34cbdcc88a9e1b914.zip |
Notes
Diffstat (limited to 'crypto/openssh/sshd.c')
-rw-r--r-- | crypto/openssh/sshd.c | 302 |
1 files changed, 178 insertions, 124 deletions
diff --git a/crypto/openssh/sshd.c b/crypto/openssh/sshd.c index 2c7b201f0400..eca004fa0925 100644 --- a/crypto/openssh/sshd.c +++ b/crypto/openssh/sshd.c @@ -40,11 +40,11 @@ */ #include "includes.h" -RCSID("$OpenBSD: sshd.c,v 1.195 2001/04/15 16:58:03 markus Exp $"); +RCSID("$OpenBSD: sshd.c,v 1.228 2002/02/27 21:23:13 stevesk Exp $"); #include <openssl/dh.h> #include <openssl/bn.h> -#include <openssl/hmac.h> +#include <openssl/md5.h> #include "ssh.h" #include "ssh1.h" @@ -71,6 +71,7 @@ RCSID("$OpenBSD: sshd.c,v 1.195 2001/04/15 16:58:03 markus Exp $"); #include "auth.h" #include "misc.h" #include "dispatch.h" +#include "channels.h" #ifdef LIBWRAP #include <tcpd.h> @@ -105,6 +106,9 @@ int IPv4or6 = AF_UNSPEC; */ 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; @@ -156,10 +160,11 @@ struct { * 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_do_regen = 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; /* session identifier, used by RSA-auth */ u_char session_id[16]; @@ -171,17 +176,20 @@ int session_id2_len = 0; /* record remote hostname or ip */ 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); -void do_ssh2_kex(void); +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; @@ -190,27 +198,41 @@ close_listen_socks(void) 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 +static void sighup_restart(void) { log("Received SIGHUP; restarting."); close_listen_socks(); + close_startup_pipes(); execv(saved_argv[0], saved_argv); log("RESTART FAILED: av[0]='%.100s', error: %.100s.", saved_argv[0], strerror(errno)); exit(1); @@ -218,23 +240,18 @@ sighup_restart(void) /* * 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; @@ -250,9 +267,11 @@ main_sigchld_handler(int sig) /* * 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(); @@ -267,7 +286,7 @@ grace_alarm_handler(int sig) * Thus there should be no concurrency control/asynchronous execution * problems. */ -void +static void generate_ephemeral_server_key(void) { u_int32_t rand = 0; @@ -290,7 +309,7 @@ generate_ephemeral_server_key(void) arc4random_stir(); } -void +static void key_regeneration_alarm(int sig) { int save_errno = errno; @@ -299,7 +318,7 @@ key_regeneration_alarm(int sig) key_do_regen = 1; } -void +static void sshd_exchange_identification(int sock_in, int sock_out) { int i, mismatch; @@ -327,7 +346,7 @@ sshd_exchange_identification(int sock_in, int sock_out) /* 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(); } @@ -335,7 +354,7 @@ sshd_exchange_identification(int sock_in, int sock_out) 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 identification string from %s.", + log("Did not receive identification string from %s", get_remote_ipaddr()); fatal_cleanup(); } @@ -371,7 +390,7 @@ sshd_exchange_identification(int sock_in, int sock_out) 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); @@ -382,7 +401,7 @@ sshd_exchange_identification(int sock_in, int sock_out) } mismatch = 0; - switch(remote_major) { + switch (remote_major) { case 1: if (remote_minor == 99) { if (options.protocol & SSH_PROTO_2) @@ -426,8 +445,6 @@ sshd_exchange_identification(int sock_in, int sock_out) server_version_string, client_version_string); fatal_cleanup(); } - if (compat20) - packet_set_ssh2_format(); } @@ -441,7 +458,7 @@ destroy_sensitive_data(void) key_free(sensitive_data.server_key); sensitive_data.server_key = NULL; } - for(i = 0; i < options.num_host_key_files; i++) { + 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; @@ -451,36 +468,40 @@ destroy_sensitive_data(void) memset(sensitive_data.ssh1_cookie, 0, SSH_SESSION_KEY_LENGTH); } -char * +static char * list_hostkey_types(void) { - static char buf[1024]; + Buffer b; + char *p; int i; - buf[0] = '\0'; - for(i = 0; i < options.num_host_key_files; 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) { + switch (key->type) { case KEY_RSA: case KEY_DSA: - strlcat(buf, key_ssh_name(key), sizeof buf); - strlcat(buf, ",", sizeof buf); + if (buffer_len(&b) > 0) + buffer_append(&b, ",", 1); + p = key_ssh_name(key); + buffer_append(&b, p, strlen(p)); break; } } - i = strlen(buf); - if (i > 0 && buf[i-1] == ',') - buf[i-1] = '\0'; - debug("list_hostkey_types: %s", buf); - return buf; + buffer_append(&b, "\0", 1); + p = xstrdup(buffer_ptr(&b)); + buffer_free(&b); + debug("list_hostkey_types: %s", p); + return p; } -Key * +static Key * get_hostkey_by_type(int type) { int i; - for(i = 0; i < options.num_host_key_files; 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; @@ -494,7 +515,7 @@ get_hostkey_by_type(int type) * 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; @@ -517,8 +538,30 @@ drop_connection(int startups) 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. @@ -552,7 +595,7 @@ main(int ac, char **av) initialize_server_options(&options); /* Parse command-line arguments. */ - while ((opt = getopt(ac, av, "f:p:b:k:h:g:V:u:dDeiqQ46")) != -1) { + while ((opt = getopt(ac, av, "f:p:b:k:h:g:V:u:o:dDeiqtQ46")) != -1) { switch (opt) { case '4': IPv4or6 = AF_INET; @@ -605,10 +648,16 @@ main(int ac, char **av) } 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': if (options.num_host_key_files >= MAX_HOSTKEYS) { @@ -622,40 +671,35 @@ main(int ac, char **av) /* 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", __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, " -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"); - 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(__progname, - options.log_level == -1 ? SYSLOG_LEVEL_INFO : options.log_level, - options.log_facility == -1 ? SYSLOG_FACILITY_AUTH : options.log_facility, + 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. */ @@ -674,14 +718,14 @@ main(int ac, char **av) /* 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++) + 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; - for(i = 0; i < options.num_host_key_files; i++) { + 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) { @@ -690,7 +734,7 @@ main(int ac, char **av) sensitive_data.host_keys[i] = NULL; continue; } - switch(key->type){ + switch (key->type) { case KEY_RSA1: sensitive_data.ssh1_host_key = key; sensitive_data.have_ssh1_key = 1; @@ -739,6 +783,10 @@ main(int ac, char **av) } } + /* 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; @@ -774,7 +822,7 @@ main(int ac, char **av) /* Chdir to the root directory so that the current disk can be unmounted if desired. */ chdir("/"); - + /* ignore SIGPIPE */ signal(SIGPIPE, SIG_IGN); @@ -826,11 +874,11 @@ main(int ac, char **av) * 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); @@ -855,6 +903,22 @@ main(int ac, char **av) 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 /var/run/sshd.pid to make it @@ -869,17 +933,6 @@ main(int ac, char **av) fclose(f); } } - 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); /* setup fd set for listen */ fdset = NULL; @@ -915,6 +968,13 @@ main(int ac, char **av) 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; @@ -949,6 +1009,7 @@ main(int ac, char **av) } if (fcntl(newsock, F_SETFL, 0) < 0) { error("newsock del O_NONBLOCK: %s", strerror(errno)); + close(newsock); continue; } if (drop_connection(startups) == 1) { @@ -1002,9 +1063,7 @@ main(int ac, char **av) * 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; @@ -1063,11 +1122,11 @@ main(int ac, char **av) /* 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, (void *)&on, + setsockopt(sock_in, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)) < 0) error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno)); @@ -1080,23 +1139,23 @@ main(int ac, char **av) 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, __progname, RQ_FILE, sock_in, NULL); + request_init(&req, RQ_DAEMON, __progname, RQ_FILE, sock_in, 0); fromhost(&req); if (!hosts_access(&req)) { + debug("Connection refused by tcp wrapper"); refuse(&req); - close(sock_in); - close(sock_out); + /* 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); @@ -1120,19 +1179,20 @@ main(int ac, char **av) * 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 not trusted."); + "originating port %d not trusted.", remote_port); options.rhosts_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()) { @@ -1152,13 +1212,6 @@ main(int ac, char **av) 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(); @@ -1168,11 +1221,10 @@ main(int ac, char **av) /* * SSH1 key exchange */ -void +static void do_ssh1_kex(void) { int i, len; - int plen, slen; int rsafail = 0; BIGNUM *session_key_int; u_char session_key[SSH_SESSION_KEY_LENGTH]; @@ -1229,17 +1281,19 @@ do_ssh1_kex(void) 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 - if (options.challenge_reponse_authentication == 1) + if (options.challenge_response_authentication == 1) auth_mask |= 1 << SSH_AUTH_TIS; if (options.password_authentication) auth_mask |= 1 << SSH_AUTH_PASSWORD; @@ -1254,7 +1308,7 @@ do_ssh1_kex(void) 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(); @@ -1271,13 +1325,13 @@ do_ssh1_kex(void) 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_integrity_check(plen, 1 + 8 + slen + 4, SSH_CMSG_SESSION_KEY); + packet_check_eom(); /* * Decrypt it using our private server key and private host key (key @@ -1347,7 +1401,7 @@ do_ssh1_kex(void) } if (rsafail) { int bytes = BN_num_bytes(session_key_int); - char *buf = xmalloc(bytes); + u_char *buf = xmalloc(bytes); MD5_CTX md; log("do_connection: generating a fake encryption key"); @@ -1389,7 +1443,7 @@ do_ssh1_kex(void) /* * SSH2 key exchange: diffie-hellman-group1-sha1 */ -void +static void do_ssh2_kex(void) { Kex *kex; |