version 1.206.2.6, 2002/10/11 14:53:07 |
version 1.207, 2001/10/24 08:41:41 |
|
|
* called by a name other than "ssh" or "Secure Shell". |
* called by a name other than "ssh" or "Secure Shell". |
* |
* |
* SSH2 implementation: |
* SSH2 implementation: |
* Privilege Separation: |
|
* |
* |
* Copyright (c) 2000, 2001, 2002 Markus Friedl. All rights reserved. |
* Copyright (c) 2000 Markus Friedl. All rights reserved. |
* Copyright (c) 2002 Niels Provos. All rights reserved. |
|
* |
* |
* Redistribution and use in source and binary forms, with or without |
* Redistribution and use in source and binary forms, with or without |
* modification, are permitted provided that the following conditions |
* modification, are permitted provided that the following conditions |
|
|
|
|
#include <openssl/dh.h> |
#include <openssl/dh.h> |
#include <openssl/bn.h> |
#include <openssl/bn.h> |
#include <openssl/md5.h> |
#include <openssl/hmac.h> |
#include <openssl/rand.h> |
|
|
|
#include "ssh.h" |
#include "ssh.h" |
#include "ssh1.h" |
#include "ssh1.h" |
|
|
#include "misc.h" |
#include "misc.h" |
#include "dispatch.h" |
#include "dispatch.h" |
#include "channels.h" |
#include "channels.h" |
#include "session.h" |
|
#include "monitor_mm.h" |
|
#include "monitor.h" |
|
#include "monitor_wrap.h" |
|
#include "monitor_fdpass.h" |
|
|
|
#ifdef LIBWRAP |
#ifdef LIBWRAP |
#include <tcpd.h> |
#include <tcpd.h> |
|
|
* Flag indicating whether the RSA server key needs to be 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. |
* Is set in the SIGALRM handler and cleared when the key is regenerated. |
*/ |
*/ |
static volatile sig_atomic_t key_do_regen = 0; |
int key_do_regen = 0; |
|
|
/* This is set to true when a signal is received. */ |
/* This is set to true when a signal is received. */ |
static volatile sig_atomic_t received_sighup = 0; |
int received_sighup = 0; |
static volatile sig_atomic_t received_sigterm = 0; |
int received_sigterm = 0; |
|
|
/* session identifier, used by RSA-auth */ |
/* session identifier, used by RSA-auth */ |
u_char session_id[16]; |
u_char session_id[16]; |
|
|
/* record remote hostname or ip */ |
/* record remote hostname or ip */ |
u_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 */ |
|
|
|
/* variables used for privilege separation */ |
|
extern struct monitor *pmonitor; |
|
extern int use_privsep; |
|
|
|
/* Prototypes for various functions defined later in this file. */ |
/* Prototypes for various functions defined later in this file. */ |
void destroy_sensitive_data(void); |
void destroy_sensitive_data(void); |
void demote_sensitive_data(void); |
|
|
|
static void do_ssh1_kex(void); |
static void do_ssh1_kex(void); |
static void do_ssh2_kex(void); |
static void do_ssh2_kex(void); |
|
|
close_listen_socks(void) |
close_listen_socks(void) |
{ |
{ |
int i; |
int i; |
|
|
for (i = 0; i < num_listen_socks; i++) |
for (i = 0; i < num_listen_socks; i++) |
close(listen_socks[i]); |
close(listen_socks[i]); |
num_listen_socks = -1; |
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; |
* Signal handler for SIGHUP. Sshd execs itself when it receives SIGHUP; |
* the effect is to reread the configuration file (and to regenerate |
* the effect is to reread the configuration file (and to regenerate |
|
|
static void |
static void |
sighup_handler(int sig) |
sighup_handler(int sig) |
{ |
{ |
int save_errno = errno; |
|
|
|
received_sighup = 1; |
received_sighup = 1; |
signal(SIGHUP, sighup_handler); |
signal(SIGHUP, sighup_handler); |
errno = save_errno; |
|
} |
} |
|
|
/* |
/* |
|
|
{ |
{ |
log("Received SIGHUP; restarting."); |
log("Received SIGHUP; restarting."); |
close_listen_socks(); |
close_listen_socks(); |
close_startup_pipes(); |
|
execv(saved_argv[0], saved_argv); |
execv(saved_argv[0], saved_argv); |
log("RESTART FAILED: av[0]='%.100s', error: %.100s.", saved_argv[0], |
log("RESTART FAILED: av[0]='%.100s', error: %.100s.", saved_argv[0], strerror(errno)); |
strerror(errno)); |
|
exit(1); |
exit(1); |
} |
} |
|
|
|
|
main_sigchld_handler(int sig) |
main_sigchld_handler(int sig) |
{ |
{ |
int save_errno = errno; |
int save_errno = errno; |
pid_t pid; |
|
int status; |
int status; |
|
|
while ((pid = waitpid(-1, &status, WNOHANG)) > 0 || |
while (waitpid(-1, &status, WNOHANG) > 0) |
(pid < 0 && errno == EINTR)) |
|
; |
; |
|
|
signal(SIGCHLD, main_sigchld_handler); |
signal(SIGCHLD, main_sigchld_handler); |
|
|
{ |
{ |
/* XXX no idea how fix this signal handler */ |
/* XXX no idea how fix this signal handler */ |
|
|
|
/* Close the connection. */ |
|
packet_close(); |
|
|
/* Log error and exit. */ |
/* Log error and exit. */ |
fatal("Timeout before authentication for %s", get_remote_ipaddr()); |
fatal("Timeout before authentication for %s.", get_remote_ipaddr()); |
} |
} |
|
|
/* |
/* |
|
|
static void |
static void |
generate_ephemeral_server_key(void) |
generate_ephemeral_server_key(void) |
{ |
{ |
u_int32_t rnd = 0; |
u_int32_t rand = 0; |
int i; |
int i; |
|
|
verbose("Generating %s%d bit RSA key.", |
verbose("Generating %s%d bit RSA key.", |
|
|
|
|
for (i = 0; i < SSH_SESSION_KEY_LENGTH; i++) { |
for (i = 0; i < SSH_SESSION_KEY_LENGTH; i++) { |
if (i % 4 == 0) |
if (i % 4 == 0) |
rnd = arc4random(); |
rand = arc4random(); |
sensitive_data.ssh1_cookie[i] = rnd & 0xff; |
sensitive_data.ssh1_cookie[i] = rand & 0xff; |
rnd >>= 8; |
rand >>= 8; |
} |
} |
arc4random_stir(); |
arc4random_stir(); |
} |
} |
|
|
key_regeneration_alarm(int sig) |
key_regeneration_alarm(int sig) |
{ |
{ |
int save_errno = errno; |
int save_errno = errno; |
|
|
signal(SIGALRM, SIG_DFL); |
signal(SIGALRM, SIG_DFL); |
errno = save_errno; |
errno = save_errno; |
key_do_regen = 1; |
key_do_regen = 1; |
|
|
|
|
if (client_version_string == NULL) { |
if (client_version_string == NULL) { |
/* Send our protocol version identification. */ |
/* Send our protocol version identification. */ |
if (atomicio(write, sock_out, server_version_string, |
if (atomicio(write, sock_out, server_version_string, strlen(server_version_string)) |
strlen(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(); |
fatal_cleanup(); |
} |
} |
|
|
/* Read other sides version identification. */ |
/* Read other side's version identification. */ |
memset(buf, 0, sizeof(buf)); |
memset(buf, 0, sizeof(buf)); |
for (i = 0; i < sizeof(buf) - 1; i++) { |
for (i = 0; i < sizeof(buf) - 1; i++) { |
if (atomicio(read, sock_in, &buf[i], 1) != 1) { |
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()); |
get_remote_ipaddr()); |
fatal_cleanup(); |
fatal_cleanup(); |
} |
} |
|
|
fatal_cleanup(); |
fatal_cleanup(); |
} |
} |
debug("Client protocol version %d.%d; client software version %.100s", |
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); |
compat_datafellows(remote_version); |
|
|
if (datafellows & SSH_BUG_PROBE) { |
|
log("probed from %s with %s. Don't panic.", |
|
get_remote_ipaddr(), client_version_string); |
|
fatal_cleanup(); |
|
} |
|
|
|
if (datafellows & SSH_BUG_SCANNER) { |
if (datafellows & SSH_BUG_SCANNER) { |
log("scanned from %s with %s. Don't panic.", |
log("scanned from %s with %s. Don't panic.", |
get_remote_ipaddr(), client_version_string); |
get_remote_ipaddr(), client_version_string); |
|
|
} |
} |
|
|
mismatch = 0; |
mismatch = 0; |
switch (remote_major) { |
switch(remote_major) { |
case 1: |
case 1: |
if (remote_minor == 99) { |
if (remote_minor == 99) { |
if (options.protocol & SSH_PROTO_2) |
if (options.protocol & SSH_PROTO_2) |
|
|
} |
} |
} |
} |
|
|
|
|
/* Destroy the host and server keys. They will no longer be needed. */ |
/* Destroy the host and server keys. They will no longer be needed. */ |
void |
void |
destroy_sensitive_data(void) |
destroy_sensitive_data(void) |
|
|
key_free(sensitive_data.server_key); |
key_free(sensitive_data.server_key); |
sensitive_data.server_key = NULL; |
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]) { |
if (sensitive_data.host_keys[i]) { |
key_free(sensitive_data.host_keys[i]); |
key_free(sensitive_data.host_keys[i]); |
sensitive_data.host_keys[i] = NULL; |
sensitive_data.host_keys[i] = NULL; |
|
|
memset(sensitive_data.ssh1_cookie, 0, SSH_SESSION_KEY_LENGTH); |
memset(sensitive_data.ssh1_cookie, 0, SSH_SESSION_KEY_LENGTH); |
} |
} |
|
|
/* Demote private to public keys for network child */ |
|
void |
|
demote_sensitive_data(void) |
|
{ |
|
Key *tmp; |
|
int i; |
|
|
|
if (sensitive_data.server_key) { |
|
tmp = key_demote(sensitive_data.server_key); |
|
key_free(sensitive_data.server_key); |
|
sensitive_data.server_key = tmp; |
|
} |
|
|
|
for (i = 0; i < options.num_host_key_files; i++) { |
|
if (sensitive_data.host_keys[i]) { |
|
tmp = key_demote(sensitive_data.host_keys[i]); |
|
key_free(sensitive_data.host_keys[i]); |
|
sensitive_data.host_keys[i] = tmp; |
|
if (tmp->type == KEY_RSA1) |
|
sensitive_data.ssh1_host_key = tmp; |
|
} |
|
} |
|
|
|
/* We do not clear ssh1_host key and cookie. XXX - Okay Niels? */ |
|
} |
|
|
|
static void |
|
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(); |
|
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, |
|
strerror(errno)); |
|
if (chdir("/") == -1) |
|
fatal("chdir(\"/\"): %s", strerror(errno)); |
|
|
|
/* Drop our privileges */ |
|
debug3("privsep user:group %u:%u", (u_int)pw->pw_uid, |
|
(u_int)pw->pw_gid); |
|
#if 0 |
|
/* XXX not ready, to heavy after chroot */ |
|
do_setusercontext(pw); |
|
#else |
|
gidset[0] = pw->pw_gid; |
|
if (setgid(pw->pw_gid) < 0) |
|
fatal("setgid failed for %u", pw->pw_gid ); |
|
if (setgroups(1, gidset) < 0) |
|
fatal("setgroups: %.100s", strerror(errno)); |
|
permanently_set_uid(pw); |
|
#endif |
|
} |
|
|
|
static Authctxt * |
|
privsep_preauth(void) |
|
{ |
|
Authctxt *authctxt = NULL; |
|
int status; |
|
pid_t pid; |
|
|
|
/* Set up unprivileged child process to deal with network data */ |
|
pmonitor = monitor_init(); |
|
/* Store a pointer to the kex for later rekeying */ |
|
pmonitor->m_pkex = &xxx_kex; |
|
|
|
pid = fork(); |
|
if (pid == -1) { |
|
fatal("fork of unprivileged child failed"); |
|
} else if (pid != 0) { |
|
fatal_remove_cleanup((void (*) (void *)) packet_close, NULL); |
|
|
|
debug2("Network child is on pid %ld", (long)pid); |
|
|
|
close(pmonitor->m_recvfd); |
|
authctxt = monitor_child_preauth(pmonitor); |
|
close(pmonitor->m_sendfd); |
|
|
|
/* Sync memory */ |
|
monitor_sync(pmonitor); |
|
|
|
/* Wait for the child's exit status */ |
|
while (waitpid(pid, &status, 0) < 0) |
|
if (errno != EINTR) |
|
break; |
|
|
|
/* Reinstall, since the child has finished */ |
|
fatal_add_cleanup((void (*) (void *)) packet_close, NULL); |
|
|
|
return (authctxt); |
|
} else { |
|
/* child */ |
|
|
|
close(pmonitor->m_sendfd); |
|
|
|
/* Demote the child */ |
|
if (getuid() == 0 || geteuid() == 0) |
|
privsep_preauth_child(); |
|
setproctitle("%s", "[net]"); |
|
} |
|
return (NULL); |
|
} |
|
|
|
static void |
|
privsep_postauth(Authctxt *authctxt) |
|
{ |
|
extern Authctxt *x_authctxt; |
|
|
|
/* XXX - Remote port forwarding */ |
|
x_authctxt = authctxt; |
|
|
|
#if BYTE_ORDER != LITTLE_ENDIAN |
|
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; |
|
} |
|
|
|
/* New socket pair */ |
|
monitor_reinit(pmonitor); |
|
|
|
pmonitor->m_pid = fork(); |
|
if (pmonitor->m_pid == -1) |
|
fatal("fork of unprivileged child failed"); |
|
else if (pmonitor->m_pid != 0) { |
|
fatal_remove_cleanup((void (*) (void *)) packet_close, NULL); |
|
|
|
debug2("User child is on pid %ld", (long)pmonitor->m_pid); |
|
close(pmonitor->m_recvfd); |
|
monitor_child_postauth(pmonitor); |
|
|
|
/* NEVERREACHED */ |
|
exit(0); |
|
} |
|
|
|
close(pmonitor->m_sendfd); |
|
|
|
/* Demote the private keys to public keys. */ |
|
demote_sensitive_data(); |
|
|
|
/* Drop privileges */ |
|
do_setusercontext(authctxt->pw); |
|
|
|
/* It is safe now to apply the key state */ |
|
monitor_apply_keystate(pmonitor); |
|
} |
|
|
|
static char * |
static char * |
list_hostkey_types(void) |
list_hostkey_types(void) |
{ |
{ |
Buffer b; |
static char buf[1024]; |
char *p; |
|
int i; |
int i; |
|
buf[0] = '\0'; |
buffer_init(&b); |
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]; |
Key *key = sensitive_data.host_keys[i]; |
if (key == NULL) |
if (key == NULL) |
continue; |
continue; |
switch (key->type) { |
switch(key->type) { |
case KEY_RSA: |
case KEY_RSA: |
case KEY_DSA: |
case KEY_DSA: |
if (buffer_len(&b) > 0) |
strlcat(buf, key_ssh_name(key), sizeof buf); |
buffer_append(&b, ",", 1); |
strlcat(buf, ",", sizeof buf); |
p = key_ssh_name(key); |
|
buffer_append(&b, p, strlen(p)); |
|
break; |
break; |
} |
} |
} |
} |
buffer_append(&b, "\0", 1); |
i = strlen(buf); |
p = xstrdup(buffer_ptr(&b)); |
if (i > 0 && buf[i-1] == ',') |
buffer_free(&b); |
buf[i-1] = '\0'; |
debug("list_hostkey_types: %s", p); |
debug("list_hostkey_types: %s", buf); |
return p; |
return buf; |
} |
} |
|
|
Key * |
static Key * |
get_hostkey_by_type(int type) |
get_hostkey_by_type(int type) |
{ |
{ |
int i; |
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]; |
Key *key = sensitive_data.host_keys[i]; |
if (key != NULL && key->type == type) |
if (key != NULL && key->type == type) |
return key; |
return key; |
|
|
return NULL; |
return NULL; |
} |
} |
|
|
Key * |
|
get_hostkey_by_index(int ind) |
|
{ |
|
if (ind < 0 || ind >= options.num_host_key_files) |
|
return (NULL); |
|
return (sensitive_data.host_keys[ind]); |
|
} |
|
|
|
int |
|
get_hostkey_index(Key *key) |
|
{ |
|
int i; |
|
|
|
for (i = 0; i < options.num_host_key_files; i++) { |
|
if (key == sensitive_data.host_keys[i]) |
|
return (i); |
|
} |
|
return (-1); |
|
} |
|
|
|
/* |
/* |
* returns 1 if connection should be dropped, 0 otherwise. |
* returns 1 if connection should be dropped, 0 otherwise. |
* dropping starts at connection #max_startups_begin with a probability |
* dropping starts at connection #max_startups_begin with a probability |
|
|
return (r < p) ? 1 : 0; |
return (r < p) ? 1 : 0; |
} |
} |
|
|
static void |
int *startup_pipes = NULL; /* options.max_startup sized array of fd ints */ |
usage(void) |
int startup_pipe; /* in child */ |
{ |
|
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. |
* Main program for the daemon. |
|
|
const char *remote_ip; |
const char *remote_ip; |
int remote_port; |
int remote_port; |
FILE *f; |
FILE *f; |
|
struct linger linger; |
struct addrinfo *ai; |
struct addrinfo *ai; |
char ntop[NI_MAXHOST], strport[NI_MAXSERV]; |
char ntop[NI_MAXHOST], strport[NI_MAXSERV]; |
int listen_sock, maxfd; |
int listen_sock, maxfd; |
int startup_p[2]; |
int startup_p[2]; |
int startups = 0; |
int startups = 0; |
Authctxt *authctxt; |
|
Key *key; |
Key *key; |
int ret, key_used = 0; |
int ret, key_used = 0; |
|
|
|
|
initialize_server_options(&options); |
initialize_server_options(&options); |
|
|
/* Parse command-line arguments. */ |
/* Parse command-line arguments. */ |
while ((opt = getopt(ac, av, "f:p:b:k:h:g:V:u:o:dDeiqtQ46")) != -1) { |
while ((opt = getopt(ac, av, "f:p:b:k:h:g:V:u:dDeiqtQ46")) != -1) { |
switch (opt) { |
switch (opt) { |
case '4': |
case '4': |
IPv4or6 = AF_INET; |
IPv4or6 = AF_INET; |
|
|
break; |
break; |
case 'u': |
case 'u': |
utmp_len = atoi(optarg); |
utmp_len = atoi(optarg); |
if (utmp_len > MAXHOSTNAMELEN) { |
|
fprintf(stderr, "Invalid utmp length.\n"); |
|
exit(1); |
|
} |
|
break; |
break; |
case 'o': |
|
if (process_server_config_line(&options, optarg, |
|
"command-line", 0) != 0) |
|
exit(1); |
|
break; |
|
case '?': |
case '?': |
default: |
default: |
usage(); |
fprintf(stderr, "sshd version %s\n", SSH_VERSION); |
break; |
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"); |
|
exit(1); |
} |
} |
} |
} |
SSLeay_add_all_algorithms(); |
SSLeay_add_all_algorithms(); |
|
|
* key (unless started from inetd) |
* key (unless started from inetd) |
*/ |
*/ |
log_init(__progname, |
log_init(__progname, |
options.log_level == SYSLOG_LEVEL_NOT_SET ? |
options.log_level == -1 ? SYSLOG_LEVEL_INFO : options.log_level, |
SYSLOG_LEVEL_INFO : options.log_level, |
options.log_facility == -1 ? SYSLOG_FACILITY_AUTH : options.log_facility, |
options.log_facility == SYSLOG_FACILITY_NOT_SET ? |
|
SYSLOG_FACILITY_AUTH : options.log_facility, |
|
!inetd_flag); |
!inetd_flag); |
|
|
/* Read server configuration options from the configuration file. */ |
/* Read server configuration options from the configuration file. */ |
|
|
debug("sshd version %.100s", SSH_VERSION); |
debug("sshd version %.100s", SSH_VERSION); |
|
|
/* load private host keys */ |
/* load private host keys */ |
sensitive_data.host_keys = xmalloc(options.num_host_key_files * |
sensitive_data.host_keys = xmalloc(options.num_host_key_files*sizeof(Key*)); |
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.host_keys[i] = NULL; |
sensitive_data.server_key = NULL; |
sensitive_data.server_key = NULL; |
sensitive_data.ssh1_host_key = NULL; |
sensitive_data.ssh1_host_key = NULL; |
sensitive_data.have_ssh1_key = 0; |
sensitive_data.have_ssh1_key = 0; |
sensitive_data.have_ssh2_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); |
key = key_load_private(options.host_key_files[i], "", NULL); |
sensitive_data.host_keys[i] = key; |
sensitive_data.host_keys[i] = key; |
if (key == NULL) { |
if (key == NULL) { |
|
|
sensitive_data.host_keys[i] = NULL; |
sensitive_data.host_keys[i] = NULL; |
continue; |
continue; |
} |
} |
switch (key->type) { |
switch(key->type){ |
case KEY_RSA1: |
case KEY_RSA1: |
sensitive_data.ssh1_host_key = key; |
sensitive_data.ssh1_host_key = key; |
sensitive_data.have_ssh1_key = 1; |
sensitive_data.have_ssh1_key = 1; |
|
|
* hate software patents. I dont know if this can go? Niels |
* hate software patents. I dont know if this can go? Niels |
*/ |
*/ |
if (options.server_key_bits > |
if (options.server_key_bits > |
BN_num_bits(sensitive_data.ssh1_host_key->rsa->n) - |
BN_num_bits(sensitive_data.ssh1_host_key->rsa->n) - SSH_KEY_BITS_RESERVED && |
SSH_KEY_BITS_RESERVED && options.server_key_bits < |
options.server_key_bits < |
BN_num_bits(sensitive_data.ssh1_host_key->rsa->n) + |
BN_num_bits(sensitive_data.ssh1_host_key->rsa->n) + SSH_KEY_BITS_RESERVED) { |
SSH_KEY_BITS_RESERVED) { |
|
options.server_key_bits = |
options.server_key_bits = |
BN_num_bits(sensitive_data.ssh1_host_key->rsa->n) + |
BN_num_bits(sensitive_data.ssh1_host_key->rsa->n) + SSH_KEY_BITS_RESERVED; |
SSH_KEY_BITS_RESERVED; |
|
debug("Forcing server key to %d bits to make it differ from host key.", |
debug("Forcing server key to %d bits to make it differ from host key.", |
options.server_key_bits); |
options.server_key_bits); |
} |
} |
} |
} |
|
|
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", |
|
_PATH_PRIVSEP_CHROOT_DIR); |
|
if (st.st_uid != 0 || (st.st_mode & (S_IWGRP|S_IWOTH)) != 0) |
|
fatal("Bad owner or mode for %s", |
|
_PATH_PRIVSEP_CHROOT_DIR); |
|
} |
|
|
|
/* Configuration looks good, so exit if in test mode. */ |
/* Configuration looks good, so exit if in test mode. */ |
if (test_flag) |
if (test_flag) |
exit(0); |
exit(0); |
|
|
/* Chdir to the root directory so that the current disk can be |
/* Chdir to the root directory so that the current disk can be |
unmounted if desired. */ |
unmounted if desired. */ |
chdir("/"); |
chdir("/"); |
|
|
/* ignore SIGPIPE */ |
/* ignore SIGPIPE */ |
signal(SIGPIPE, SIG_IGN); |
signal(SIGPIPE, SIG_IGN); |
|
|
|
|
continue; |
continue; |
} |
} |
/* |
/* |
* Set socket options. |
* Set socket options. We try to make the port |
* Allow local port reuse in TIME_WAIT. |
* reusable and have it close as fast as possible |
|
* without waiting in unnecessary wait states on |
|
* close. |
*/ |
*/ |
if (setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, |
setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, |
&on, sizeof(on)) == -1) |
(void *) &on, sizeof(on)); |
error("setsockopt SO_REUSEADDR: %s", strerror(errno)); |
linger.l_onoff = 1; |
|
linger.l_linger = 5; |
|
setsockopt(listen_sock, SOL_SOCKET, SO_LINGER, |
|
(void *) &linger, sizeof(linger)); |
|
|
debug("Bind to port %s on %s.", strport, ntop); |
debug("Bind to port %s on %s.", strport, ntop); |
|
|
|
|
*/ |
*/ |
f = fopen(options.pid_file, "w"); |
f = fopen(options.pid_file, "w"); |
if (f) { |
if (f) { |
fprintf(f, "%ld\n", (long) getpid()); |
fprintf(f, "%u\n", (u_int) getpid()); |
fclose(f); |
fclose(f); |
} |
} |
} |
} |
|
|
error("select: %.100s", strerror(errno)); |
error("select: %.100s", strerror(errno)); |
if (received_sigterm) { |
if (received_sigterm) { |
log("Received signal %d; terminating.", |
log("Received signal %d; terminating.", |
(int) received_sigterm); |
received_sigterm); |
close_listen_socks(); |
close_listen_socks(); |
unlink(options.pid_file); |
unlink(options.pid_file); |
exit(255); |
exit(255); |
|
|
} |
} |
if (fcntl(newsock, F_SETFL, 0) < 0) { |
if (fcntl(newsock, F_SETFL, 0) < 0) { |
error("newsock del O_NONBLOCK: %s", strerror(errno)); |
error("newsock del O_NONBLOCK: %s", strerror(errno)); |
close(newsock); |
|
continue; |
continue; |
} |
} |
if (drop_connection(startups) == 1) { |
if (drop_connection(startups) == 1) { |
|
|
* the connection. |
* the connection. |
*/ |
*/ |
startup_pipe = startup_p[1]; |
startup_pipe = startup_p[1]; |
close_startup_pipes(); |
for (j = 0; j < options.max_startups; j++) |
|
if (startup_pipes[j] != -1) |
|
close(startup_pipes[j]); |
close_listen_socks(); |
close_listen_socks(); |
sock_in = newsock; |
sock_in = newsock; |
sock_out = newsock; |
sock_out = newsock; |
|
|
if (pid < 0) |
if (pid < 0) |
error("fork: %.100s", strerror(errno)); |
error("fork: %.100s", strerror(errno)); |
else |
else |
debug("Forked child %ld.", (long)pid); |
debug("Forked child %d.", pid); |
|
|
close(startup_p[1]); |
close(startup_p[1]); |
|
|
|
|
/* This is the child processing a new connection. */ |
/* This is the child processing a new connection. */ |
|
|
/* |
/* |
* Create a new session and process group since the 4.4BSD |
|
* setlogin() affects the entire process group. We don't |
|
* want the child to be able to affect the parent. |
|
*/ |
|
if (!debug_flag && !inetd_flag && setsid() < 0) |
|
error("setsid: %.100s", strerror(errno)); |
|
|
|
/* |
|
* Disable the key regeneration alarm. We will not regenerate the |
* 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 |
* key since we are no longer in a position to give it to anyone. We |
* will not restart on SIGHUP since it no longer makes sense. |
* will not restart on SIGHUP since it no longer makes sense. |
|
|
signal(SIGQUIT, SIG_DFL); |
signal(SIGQUIT, SIG_DFL); |
signal(SIGCHLD, SIG_DFL); |
signal(SIGCHLD, SIG_DFL); |
|
|
|
/* |
|
* Set socket options for the connection. We want the socket to |
|
* close as fast as possible without waiting for anything. If the |
|
* connection is not a socket, these will do nothing. |
|
*/ |
|
/* 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)); |
|
|
/* Set keepalives if requested. */ |
/* Set keepalives if requested. */ |
if (options.keepalives && |
if (options.keepalives && |
setsockopt(sock_in, SOL_SOCKET, SO_KEEPALIVE, &on, |
setsockopt(sock_in, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, |
sizeof(on)) < 0) |
sizeof(on)) < 0) |
error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno)); |
error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno)); |
|
|
|
|
remote_port = get_remote_port(); |
remote_port = get_remote_port(); |
remote_ip = get_remote_ipaddr(); |
remote_ip = get_remote_ipaddr(); |
|
|
#ifdef LIBWRAP |
|
/* Check whether logins are denied from this host. */ |
/* Check whether logins are denied from this host. */ |
|
#ifdef LIBWRAP |
|
/* XXX LIBWRAP noes not know about IPv6 */ |
{ |
{ |
struct request_info req; |
struct request_info req; |
|
|
|
|
fromhost(&req); |
fromhost(&req); |
|
|
if (!hosts_access(&req)) { |
if (!hosts_access(&req)) { |
debug("Connection refused by tcp wrapper"); |
|
refuse(&req); |
refuse(&req); |
/* NOTREACHED */ |
close(sock_in); |
fatal("libwrap refuse returns"); |
close(sock_out); |
} |
} |
|
/*XXX IPv6 verbose("Connection from %.500s port %d", eval_client(&req), remote_port); */ |
} |
} |
#endif /* LIBWRAP */ |
#endif /* LIBWRAP */ |
|
|
/* Log the connection. */ |
/* Log the connection. */ |
verbose("Connection from %.500s port %d", remote_ip, remote_port); |
verbose("Connection from %.500s port %d", remote_ip, remote_port); |
|
|
|
|
sshd_exchange_identification(sock_in, sock_out); |
sshd_exchange_identification(sock_in, sock_out); |
/* |
/* |
* Check that the connection comes from a privileged port. |
* Check that the connection comes from a privileged port. |
* Rhosts-Authentication only makes sense from privileged |
* Rhosts-Authentication only makes sense from priviledged |
* programs. Of course, if the intruder has root access on his local |
* programs. Of course, if the intruder has root access on his local |
* machine, he can connect from any port. So do not use these |
* machine, he can connect from any port. So do not use these |
* authentication methods from machines that you do not trust. |
* authentication methods from machines that you do not trust. |
*/ |
*/ |
if (options.rhosts_authentication && |
if (remote_port >= IPPORT_RESERVED || |
(remote_port >= IPPORT_RESERVED || |
remote_port < IPPORT_RESERVED / 2) { |
remote_port < IPPORT_RESERVED / 2)) { |
|
debug("Rhosts Authentication disabled, " |
debug("Rhosts Authentication disabled, " |
"originating port %d not trusted.", remote_port); |
"originating port %d not trusted.", remote_port); |
options.rhosts_authentication = 0; |
options.rhosts_authentication = 0; |
|
|
|
|
packet_set_nonblocking(); |
packet_set_nonblocking(); |
|
|
if (use_privsep) |
|
if ((authctxt = privsep_preauth()) != NULL) |
|
goto authenticated; |
|
|
|
/* perform the key exchange */ |
/* perform the key exchange */ |
/* authenticate user and start session */ |
/* authenticate user and start session */ |
if (compat20) { |
if (compat20) { |
do_ssh2_kex(); |
do_ssh2_kex(); |
authctxt = do_authentication2(); |
do_authentication2(); |
} else { |
} else { |
do_ssh1_kex(); |
do_ssh1_kex(); |
authctxt = do_authentication(); |
do_authentication(); |
} |
} |
/* |
|
* If we use privilege separation, the unprivileged child transfers |
|
* the current keystate and exits |
|
*/ |
|
if (use_privsep) { |
|
mm_send_keystate(pmonitor); |
|
exit(0); |
|
} |
|
|
|
authenticated: |
|
/* |
|
* In privilege separation, we fork another child and prepare |
|
* file descriptor passing. |
|
*/ |
|
if (use_privsep) { |
|
privsep_postauth(authctxt); |
|
/* the monitor process [priv] will not return */ |
|
if (!compat20) |
|
destroy_sensitive_data(); |
|
} |
|
|
|
/* Perform session preparation. */ |
|
do_authenticated(authctxt); |
|
|
|
/* The connection has been terminated. */ |
/* The connection has been terminated. */ |
verbose("Closing connection to %.100s", remote_ip); |
verbose("Closing connection to %.100s", remote_ip); |
packet_close(); |
packet_close(); |
|
|
if (use_privsep) |
|
mm_terminate(); |
|
|
|
exit(0); |
exit(0); |
} |
} |
|
|
/* |
/* |
* Decrypt session_key_int using our private server key and private host key |
|
* (key with larger modulus first). |
|
*/ |
|
int |
|
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) { |
|
/* 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); |
|
} |
|
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.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); |
|
} |
|
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++; |
|
} |
|
return (rsafail); |
|
} |
|
/* |
|
* SSH1 key exchange |
* SSH1 key exchange |
*/ |
*/ |
static void |
static void |
do_ssh1_kex(void) |
do_ssh1_kex(void) |
{ |
{ |
int i, len; |
int i, len; |
|
int plen, slen; |
int rsafail = 0; |
int rsafail = 0; |
BIGNUM *session_key_int; |
BIGNUM *session_key_int; |
u_char session_key[SSH_SESSION_KEY_LENGTH]; |
u_char session_key[SSH_SESSION_KEY_LENGTH]; |
u_char cookie[8]; |
u_char cookie[8]; |
u_int cipher_type, auth_mask, protocol_flags; |
u_int cipher_type, auth_mask, protocol_flags; |
u_int32_t rnd = 0; |
u_int32_t rand = 0; |
|
|
/* |
/* |
* Generate check bytes that the client must send back in the user |
* Generate check bytes that the client must send back in the user |
|
|
*/ |
*/ |
for (i = 0; i < 8; i++) { |
for (i = 0; i < 8; i++) { |
if (i % 4 == 0) |
if (i % 4 == 0) |
rnd = arc4random(); |
rand = arc4random(); |
cookie[i] = rnd & 0xff; |
cookie[i] = rand & 0xff; |
rnd >>= 8; |
rand >>= 8; |
} |
} |
|
|
/* |
/* |
|
|
BN_num_bits(sensitive_data.ssh1_host_key->rsa->n)); |
BN_num_bits(sensitive_data.ssh1_host_key->rsa->n)); |
|
|
/* Read clients reply (cipher type and session key). */ |
/* Read clients reply (cipher type and session key). */ |
packet_read_expect(SSH_CMSG_SESSION_KEY); |
packet_read_expect(&plen, SSH_CMSG_SESSION_KEY); |
|
|
/* Get cipher type and check whether we accept this. */ |
/* Get cipher type and check whether we accept this. */ |
cipher_type = packet_get_char(); |
cipher_type = packet_get_char(); |
|
|
debug("Encryption type: %.200s", cipher_name(cipher_type)); |
debug("Encryption type: %.200s", cipher_name(cipher_type)); |
|
|
/* Get the encrypted integer. */ |
/* Get the encrypted integer. */ |
if ((session_key_int = BN_new()) == NULL) |
session_key_int = BN_new(); |
fatal("do_ssh1_kex: BN_new failed"); |
packet_get_bignum(session_key_int, &slen); |
packet_get_bignum(session_key_int); |
|
|
|
protocol_flags = packet_get_int(); |
protocol_flags = packet_get_int(); |
packet_set_protocol_flags(protocol_flags); |
packet_set_protocol_flags(protocol_flags); |
packet_check_eom(); |
|
|
|
/* Decrypt session_key_int using host/server keys */ |
packet_integrity_check(plen, 1 + 8 + slen + 4, SSH_CMSG_SESSION_KEY); |
rsafail = PRIVSEP(ssh1_session_key(session_key_int)); |
|
|
|
/* |
/* |
|
* Decrypt it using our private server key and private host key (key |
|
* with larger modulus first). |
|
*/ |
|
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); |
|
} |
|
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.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); |
|
} |
|
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++; |
|
} |
|
/* |
* Extract session key from the decrypted integer. The key is in the |
* Extract session key from the decrypted integer. The key is in the |
* least significant 256 bits of the integer; the first byte of the |
* least significant 256 bits of the integer; the first byte of the |
* key is in the highest bits. |
* key is in the highest bits. |
|
|
} |
} |
if (rsafail) { |
if (rsafail) { |
int bytes = BN_num_bytes(session_key_int); |
int bytes = BN_num_bytes(session_key_int); |
u_char *buf = xmalloc(bytes); |
char *buf = xmalloc(bytes); |
MD5_CTX md; |
MD5_CTX md; |
|
|
log("do_connection: generating a fake encryption key"); |
log("do_connection: generating a fake encryption key"); |
|
|
for (i = 0; i < 16; i++) |
for (i = 0; i < 16; i++) |
session_id[i] = session_key[i] ^ session_key[i + 16]; |
session_id[i] = session_key[i] ^ session_key[i + 16]; |
} |
} |
/* Destroy the private and public keys. No longer. */ |
/* Destroy the private and public keys. They will no longer be needed. */ |
destroy_sensitive_data(); |
destroy_sensitive_data(); |
|
|
if (use_privsep) |
|
mm_ssh1_session_id(session_id); |
|
|
|
/* Destroy the decrypted integer. It is no longer needed. */ |
/* Destroy the decrypted integer. It is no longer needed. */ |
BN_clear_free(session_key_int); |
BN_clear_free(session_key_int); |
|
|
|
|
|
|
debug("Received session key; encryption turned on."); |
debug("Received session key; encryption turned on."); |
|
|
/* Send an acknowledgment packet. Note that this packet is sent encrypted. */ |
/* Send an acknowledgement packet. Note that this packet is sent encrypted. */ |
packet_start(SSH_SMSG_SUCCESS); |
packet_start(SSH_SMSG_SUCCESS); |
packet_send(); |
packet_send(); |
packet_write_wait(); |
packet_write_wait(); |
|
|
myproposal[PROPOSAL_MAC_ALGS_CTOS] = |
myproposal[PROPOSAL_MAC_ALGS_CTOS] = |
myproposal[PROPOSAL_MAC_ALGS_STOC] = options.macs; |
myproposal[PROPOSAL_MAC_ALGS_STOC] = options.macs; |
} |
} |
if (!options.compression) { |
|
myproposal[PROPOSAL_COMP_ALGS_CTOS] = |
|
myproposal[PROPOSAL_COMP_ALGS_STOC] = "none"; |
|
} |
|
myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = list_hostkey_types(); |
myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = list_hostkey_types(); |
|
|
/* start key exchange */ |
/* start key exchange */ |
|
|
kex->client_version_string=client_version_string; |
kex->client_version_string=client_version_string; |
kex->server_version_string=server_version_string; |
kex->server_version_string=server_version_string; |
kex->load_host_key=&get_hostkey_by_type; |
kex->load_host_key=&get_hostkey_by_type; |
kex->host_key_index=&get_hostkey_index; |
|
|
|
xxx_kex = kex; |
xxx_kex = kex; |
|
|