#include "includes.h"
RCSID("$OpenBSD: sshd.c,v 1.209 2001/11/10 13:19:45 markus Exp $");
#include <openssl/dh.h>
#include <openssl/bn.h>
#include <openssl/hmac.h>
#include "ssh.h"
#include "ssh1.h"
#include "ssh2.h"
#include "xmalloc.h"
#include "rsa.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 "cipher.h"
#include "kex.h"
#include "key.h"
#include "dh.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 <tcpd.h>
#include <syslog.h>
int allow_severity = LOG_INFO;
int deny_severity = LOG_WARNING;
#endif
#ifndef O_NOCTTY
#define O_NOCTTY 0
#endif
#ifdef HAVE___PROGNAME
extern char *__progname;
#else
char *__progname;
#endif
ServerOptions options;
char *config_file_name = _PATH_SERVER_CONFIG_FILE;
#ifdef IPV4_DEFAULT
int IPv4or6 = AF_INET;
#else
int IPv4or6 = AF_UNSPEC;
#endif
int debug_flag = 0;
int test_flag = 0;
int inetd_flag = 0;
int no_daemon_flag = 0;
int log_stderr = 0;
char **saved_argv;
int saved_argc;
#define MAX_LISTEN_SOCKS 16
int listen_socks[MAX_LISTEN_SOCKS];
int num_listen_socks = 0;
char *client_version_string = NULL;
char *server_version_string = NULL;
Kex *xxx_kex;
struct {
Key *server_key;
Key *ssh1_host_key;
Key **host_keys;
int have_ssh1_key;
int have_ssh2_key;
u_char ssh1_cookie[SSH_SESSION_KEY_LENGTH];
} sensitive_data;
int key_do_regen = 0;
int received_sighup = 0;
int received_sigterm = 0;
u_char session_id[16];
u_char *session_id2 = NULL;
int session_id2_len = 0;
u_int utmp_len = MAXHOSTNAMELEN;
void destroy_sensitive_data(void);
static void do_ssh1_kex(void);
static void do_ssh2_kex(void);
static void
close_listen_socks(void)
{
int i;
for (i = 0; i < num_listen_socks; i++)
close(listen_socks[i]);
num_listen_socks = -1;
}
static void
sighup_handler(int sig)
{
received_sighup = 1;
signal(SIGHUP, sighup_handler);
}
static void
sighup_restart(void)
{
log("Received SIGHUP; restarting.");
close_listen_socks();
execv(saved_argv[0], saved_argv);
log("RESTART FAILED: av[0]='%.100s', error: %.100s.", saved_argv[0], strerror(errno));
exit(1);
}
static void
sigterm_handler(int sig)
{
received_sigterm = sig;
}
static void
main_sigchld_handler(int sig)
{
int save_errno = errno;
int status;
while (waitpid(-1, &status, WNOHANG) > 0)
;
signal(SIGCHLD, main_sigchld_handler);
errno = save_errno;
}
static void
grace_alarm_handler(int sig)
{
packet_close();
fatal("Timeout before authentication for %s.", get_remote_ipaddr());
}
static void
generate_ephemeral_server_key(void)
{
u_int32_t rand = 0;
int i;
verbose("Generating %s%d bit RSA key.",
sensitive_data.server_key ? "new " : "", options.server_key_bits);
if (sensitive_data.server_key != NULL)
key_free(sensitive_data.server_key);
sensitive_data.server_key = key_generate(KEY_RSA1,
options.server_key_bits);
verbose("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;
}
arc4random_stir();
}
static void
key_regeneration_alarm(int sig)
{
int save_errno = errno;
signal(SIGALRM, SIG_DFL);
errno = save_errno;
key_do_regen = 1;
}
static void
sshd_exchange_identification(int sock_in, int sock_out)
{
int i, mismatch;
int remote_major, remote_minor;
int major, minor;
char *s;
char buf[256];
char remote_version[256];
if ((options.protocol & SSH_PROTO_1) &&
(options.protocol & SSH_PROTO_2)) {
major = PROTOCOL_MAJOR_1;
minor = 99;
} else if (options.protocol & SSH_PROTO_2) {
major = PROTOCOL_MAJOR_2;
minor = PROTOCOL_MINOR_2;
} else {
major = PROTOCOL_MAJOR_1;
minor = PROTOCOL_MINOR_1;
}
snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", major, minor, SSH_VERSION);
server_version_string = xstrdup(buf);
if (client_version_string == NULL) {
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());
fatal_cleanup();
}
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",
get_remote_ipaddr());
fatal_cleanup();
}
if (buf[i] == '\r') {
buf[i] = 0;
if (i == 12 &&
strncmp(buf, "SSH-1.5-W1.0", 12) == 0)
break;
continue;
}
if (buf[i] == '\n') {
buf[i] = 0;
break;
}
}
buf[sizeof(buf) - 1] = 0;
client_version_string = xstrdup(buf);
}
if (sscanf(client_version_string, "SSH-%d.%d-%[^\n]\n",
&remote_major, &remote_minor, remote_version) != 3) {
s = "Protocol mismatch.\n";
(void) atomicio(write, sock_out, s, strlen(s));
close(sock_in);
close(sock_out);
log("Bad protocol version identification '%.100s' from %s",
client_version_string, get_remote_ipaddr());
fatal_cleanup();
}
debug("Client protocol version %d.%d; client software version %.100s",
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) {
case 1:
if (remote_minor == 99) {
if (options.protocol & SSH_PROTO_2)
enable_compat20();
else
mismatch = 1;
break;
}
if (!(options.protocol & SSH_PROTO_1)) {
mismatch = 1;
break;
}
if (remote_minor < 3) {
packet_disconnect("Your ssh version is too old and "
"is no longer supported. Please install a newer version.");
} else if (remote_minor == 3) {
enable_compat13();
}
break;
case 2:
if (options.protocol & SSH_PROTO_2) {
enable_compat20();
break;
}
default:
mismatch = 1;
break;
}
chop(server_version_string);
debug("Local version string %.200s", server_version_string);
if (mismatch) {
s = "Protocol major versions differ.\n";
(void) atomicio(write, sock_out, s, strlen(s));
close(sock_in);
close(sock_out);
log("Protocol major versions differ for %s: %.200s vs. %.200s",
get_remote_ipaddr(),
server_version_string, client_version_string);
fatal_cleanup();
}
}
void
destroy_sensitive_data(void)
{
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)
{
static char buf[1024];
int i;
buf[0] = '\0';
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:
strlcat(buf, key_ssh_name(key), sizeof buf);
strlcat(buf, ",", sizeof buf);
break;
}
}
i = strlen(buf);
if (i > 0 && buf[i-1] == ',')
buf[i-1] = '\0';
debug("list_hostkey_types: %s", buf);
return buf;
}
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;
}
static int
drop_connection(int startups)
{
double p, r;
if (startups < options.max_startups_begin)
return 0;
if (startups >= options.max_startups)
return 1;
if (options.max_startups_rate == 100)
return 1;
p = 100 - options.max_startups_rate;
p *= startups - options.max_startups_begin;
p /= (double) (options.max_startups - options.max_startups_begin);
p += options.max_startups_rate;
p /= 100.0;
r = arc4random() / (double) UINT_MAX;
debug("drop_connection: p %g, r %g", p, r);
return (r < p) ? 1 : 0;
}
int *startup_pipes = NULL;
int startup_pipe;
int
main(int ac, char **av)
{
extern char *optarg;
extern int optind;
int opt, sock_in = 0, sock_out = 0, newsock, j, i, fdsetsz, on = 1;
pid_t pid;
socklen_t fromlen;
fd_set *fdset;
struct sockaddr_storage from;
const char *remote_ip;
int remote_port;
FILE *f;
struct linger linger;
struct addrinfo *ai;
char ntop[NI_MAXHOST], strport[NI_MAXSERV];
int listen_sock, maxfd;
int startup_p[2];
int startups = 0;
Key *key;
int ret, key_used = 0;
__progname = get_progname(av[0]);
init_rng();
saved_argc = ac;
saved_argv = av;
initialize_server_options(&options);
while ((opt = getopt(ac, av, "f:p:b:k:h:g:V:u:dDeiqtQ46")) != -1) {
switch (opt) {
case '4':
IPv4or6 = AF_INET;
break;
case '6':
IPv4or6 = AF_INET6;
break;
case 'f':
config_file_name = optarg;
break;
case 'd':
if (0 == debug_flag) {
debug_flag = 1;
options.log_level = SYSLOG_LEVEL_DEBUG1;
} else if (options.log_level < SYSLOG_LEVEL_DEBUG3) {
options.log_level++;
} else {
fprintf(stderr, "Too high debugging level.\n");
exit(1);
}
break;
case 'D':
no_daemon_flag = 1;
break;
case 'e':
log_stderr = 1;
break;
case 'i':
inetd_flag = 1;
break;
case 'Q':
break;
case 'q':
options.log_level = SYSLOG_LEVEL_QUIET;
break;
case 'b':
options.server_key_bits = atoi(optarg);
break;
case 'p':
options.ports_from_cmdline = 1;
if (options.num_ports >= MAX_PORTS) {
fprintf(stderr, "too many ports.\n");
exit(1);
}
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':
if ((options.login_grace_time = convtime(optarg)) == -1) {
fprintf(stderr, "Invalid login grace time.\n");
exit(1);
}
break;
case 'k':
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) {
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;
inetd_flag = 1;
break;
case 't':
test_flag = 1;
break;
case 'u':
utmp_len = atoi(optarg);
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, " -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();
channel_set_af(IPv4or6);
log_init(__progname,
options.log_level == -1 ? SYSLOG_LEVEL_INFO : options.log_level,
options.log_facility == -1 ? SYSLOG_FACILITY_AUTH : options.log_facility,
!inetd_flag);
#ifdef _CRAY
drop_cray_privs();
#endif
seed_rng();
read_server_config(&options, config_file_name);
fill_default_server_options(&options);
if (optind < ac) {
fprintf(stderr, "Extra argument %s.\n", av[optind]);
exit(1);
}
debug("sshd version %.100s", SSH_VERSION);
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;
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;
}
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) && !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);
}
if (options.protocol & SSH_PROTO_1) {
if (options.server_key_bits < 512 ||
options.server_key_bits > 32768) {
fprintf(stderr, "Bad server key size.\n");
exit(1);
}
if (options.server_key_bits >
BN_num_bits(sensitive_data.ssh1_host_key->rsa->n) - SSH_KEY_BITS_RESERVED &&
options.server_key_bits <
BN_num_bits(sensitive_data.ssh1_host_key->rsa->n) + SSH_KEY_BITS_RESERVED) {
options.server_key_bits =
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);
}
}
if (test_flag)
exit(0);
#ifdef HAVE_SCO_PROTECTED_PW
(void) set_auth_parameters(ac, av);
#endif
if (debug_flag && !inetd_flag)
log_stderr = 1;
log_init(__progname, options.log_level, options.log_facility, log_stderr);
if (!(debug_flag || inetd_flag || no_daemon_flag)) {
#ifdef TIOCNOTTY
int fd;
#endif
if (daemon(0, 0) < 0)
fatal("daemon() failed: %.200s", strerror(errno));
#ifdef TIOCNOTTY
fd = open(_PATH_TTY, O_RDWR | O_NOCTTY);
if (fd >= 0) {
(void) ioctl(fd, TIOCNOTTY, NULL);
close(fd);
}
#endif
}
log_init(__progname, options.log_level, options.log_facility, log_stderr);
arc4random_stir();
chdir("/");
signal(SIGPIPE, SIG_IGN);
if (inetd_flag) {
int s1;
s1 = dup(0);
dup(s1);
sock_in = dup(0);
sock_out = dup(1);
startup_pipe = -1;
debug("inetd sockets after dupping: %d, %d", sock_in, sock_out);
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)
continue;
if (num_listen_socks >= MAX_LISTEN_SOCKS)
fatal("Too many listen sockets. "
"Enlarge MAX_LISTEN_SOCKS");
if (getnameinfo(ai->ai_addr, ai->ai_addrlen,
ntop, sizeof(ntop), strport, sizeof(strport),
NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
error("getnameinfo failed");
continue;
}
listen_sock = socket(ai->ai_family, SOCK_STREAM, 0);
if (listen_sock < 0) {
verbose("socket: %.100s", strerror(errno));
continue;
}
if (fcntl(listen_sock, F_SETFL, O_NONBLOCK) < 0) {
error("listen_sock O_NONBLOCK: %s", strerror(errno));
close(listen_sock);
continue;
}
setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR,
(void *) &on, sizeof(on));
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);
if (bind(listen_sock, ai->ai_addr, ai->ai_addrlen) < 0) {
if (!ai->ai_next)
error("Bind to port %s on %s failed: %.200s.",
strport, ntop, strerror(errno));
close(listen_sock);
continue;
}
listen_socks[num_listen_socks] = listen_sock;
num_listen_socks++;
log("Server listening on %s port %s.", ntop, strport);
if (listen(listen_sock, 5) < 0)
fatal("listen: %.100s", strerror(errno));
}
freeaddrinfo(options.listen_addrs);
if (!num_listen_socks)
fatal("Cannot bind any address.");
if (options.protocol & SSH_PROTO_1)
generate_ephemeral_server_key();
signal(SIGHUP, sighup_handler);
signal(SIGTERM, sigterm_handler);
signal(SIGQUIT, sigterm_handler);
signal(SIGCHLD, main_sigchld_handler);
if (!debug_flag) {
f = fopen(options.pid_file, "wb");
if (f) {
fprintf(f, "%u\n", (u_int) getpid());
fclose(f);
}
}
fdset = NULL;
maxfd = 0;
for (i = 0; i < num_listen_socks; i++)
if (listen_socks[i] > maxfd)
maxfd = listen_socks[i];
startup_pipes = xmalloc(options.max_startups * sizeof(int));
for (i = 0; i < options.max_startups; i++)
startup_pipes[i] = -1;
for (;;) {
if (received_sighup)
sighup_restart();
if (fdset != NULL)
xfree(fdset);
fdsetsz = howmany(maxfd+1, NFDBITS) * sizeof(fd_mask);
fdset = (fd_set *)xmalloc(fdsetsz);
memset(fdset, 0, fdsetsz);
for (i = 0; i < num_listen_socks; i++)
FD_SET(listen_socks[i], fdset);
for (i = 0; i < options.max_startups; i++)
if (startup_pipes[i] != -1)
FD_SET(startup_pipes[i], fdset);
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.",
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)) {
close(startup_pipes[i]);
startup_pipes[i] = -1;
startups--;
}
for (i = 0; i < num_listen_socks; i++) {
if (!FD_ISSET(listen_socks[i], fdset))
continue;
fromlen = sizeof(from);
newsock = accept(listen_socks[i], (struct sockaddr *)&from,
&fromlen);
if (newsock < 0) {
if (errno != EINTR && errno != EWOULDBLOCK)
error("accept: %.100s", strerror(errno));
continue;
}
if (fcntl(newsock, F_SETFL, 0) < 0) {
error("newsock del O_NONBLOCK: %s", strerror(errno));
continue;
}
if (drop_connection(startups) == 1) {
debug("drop connection #%d", startups);
close(newsock);
continue;
}
if (pipe(startup_p) == -1) {
close(newsock);
continue;
}
for (j = 0; j < options.max_startups; j++)
if (startup_pipes[j] == -1) {
startup_pipes[j] = startup_p[0];
if (maxfd < startup_p[0])
maxfd = startup_p[0];
startups++;
break;
}
if (debug_flag) {
debug("Server will not fork when running in debugging mode.");
close_listen_socks();
sock_in = newsock;
sock_out = newsock;
startup_pipe = -1;
pid = getpid();
break;
} else {
if ((pid = fork()) == 0) {
startup_pipe = startup_p[1];
for (j = 0; j < options.max_startups; j++)
if (startup_pipes[j] != -1)
close(startup_pipes[j]);
close_listen_socks();
sock_in = newsock;
sock_out = newsock;
log_init(__progname, options.log_level, options.log_facility, log_stderr);
break;
}
}
if (pid < 0)
error("fork: %.100s", strerror(errno));
else
debug("Forked child %d.", pid);
close(startup_p[1]);
if ((options.protocol & SSH_PROTO_1) &&
key_used == 0) {
signal(SIGALRM, key_regeneration_alarm);
alarm(options.key_regeneration_time);
key_used = 1;
}
arc4random_stir();
close(newsock);
}
if (num_listen_socks < 0)
break;
}
}
alarm(0);
signal(SIGALRM, SIG_DFL);
signal(SIGHUP, SIG_DFL);
signal(SIGTERM, SIG_DFL);
signal(SIGQUIT, SIG_DFL);
signal(SIGCHLD, SIG_DFL);
signal(SIGINT, SIG_DFL);
linger.l_onoff = 1;
linger.l_linger = 5;
setsockopt(sock_in, SOL_SOCKET, SO_LINGER, (void *) &linger, sizeof(linger));
if (options.keepalives &&
setsockopt(sock_in, SOL_SOCKET, SO_KEEPALIVE, (void *)&on,
sizeof(on)) < 0)
error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno));
packet_set_connection(sock_in, sock_out);
remote_port = get_remote_port();
remote_ip = get_remote_ipaddr();
#ifdef LIBWRAP
{
struct request_info req;
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);
fatal("libwrap refuse returns");
}
}
#endif
verbose("Connection from %.500s port %d", remote_ip, remote_port);
signal(SIGALRM, grace_alarm_handler);
if (!debug_flag)
alarm(options.login_grace_time);
sshd_exchange_identification(sock_in, sock_out);
if (remote_port >= IPPORT_RESERVED ||
remote_port < IPPORT_RESERVED / 2) {
debug("Rhosts Authentication disabled, "
"originating port %d not trusted.", remote_port);
options.rhosts_authentication = 0;
}
#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
#ifdef AFS
if (k_hasafs()) {
k_setpag();
k_unlog();
}
#endif
packet_set_nonblocking();
if (compat20) {
do_ssh2_kex();
do_authentication2();
} else {
do_ssh1_kex();
do_authentication();
}
verbose("Closing connection to %.100s", remote_ip);
#ifdef USE_PAM
finish_pam();
#endif
packet_close();
exit(0);
}
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];
u_char cookie[8];
u_int cipher_type, auth_mask, protocol_flags;
u_int32_t rand = 0;
for (i = 0; i < 8; i++) {
if (i % 4 == 0)
rand = arc4random();
cookie[i] = rand & 0xff;
rand >>= 8;
}
packet_start(SSH_SMSG_PUBLIC_KEY);
for (i = 0; i < 8; i++)
packet_put_char(cookie[i]);
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);
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);
packet_put_int(SSH_PROTOFLAG_HOST_IN_FWD_OPEN);
packet_put_int(cipher_mask_ssh1(0));
auth_mask = 0;
if (options.rhosts_authentication)
auth_mask |= 1 << SSH_AUTH_RHOSTS;
if (options.rhosts_rsa_authentication)
auth_mask |= 1 << SSH_AUTH_RHOSTS_RSA;
if (options.rsa_authentication)
auth_mask |= 1 << SSH_AUTH_RSA;
#if defined(KRB4) || defined(KRB5)
if (options.kerberos_authentication)
auth_mask |= 1 << SSH_AUTH_KERBEROS;
#endif
#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_response_authentication == 1)
auth_mask |= 1 << SSH_AUTH_TIS;
if (options.password_authentication)
auth_mask |= 1 << SSH_AUTH_PASSWORD;
packet_put_int(auth_mask);
packet_send();
packet_write_wait();
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));
packet_read_expect(&plen, SSH_CMSG_SESSION_KEY);
cipher_type = packet_get_char();
if (!(cipher_mask_ssh1(0) & (1 << cipher_type)))
packet_disconnect("Warning: client selects unsupported cipher.");
for (i = 0; i < 8; i++)
if (cookie[i] != packet_get_char())
packet_disconnect("IP Spoofing check bytes do not match.");
debug("Encryption type: %.200s", cipher_name(cipher_type));
session_key_int = BN_new();
packet_get_bignum(session_key_int, &slen);
protocol_flags = packet_get_int();
packet_set_protocol_flags(protocol_flags);
packet_integrity_check(plen, 1 + 8 + slen + 4, SSH_CMSG_SESSION_KEY);
if (BN_cmp(sensitive_data.server_key->rsa->n, sensitive_data.ssh1_host_key->rsa->n) > 0) {
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 {
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++;
}
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);
for (i = 0; i < 16; i++)
session_key[i] ^= session_id[i];
}
}
if (rsafail) {
int bytes = BN_num_bytes(session_key_int);
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_sensitive_data();
BN_clear_free(session_key_int);
packet_set_encryption_key(session_key, SSH_SESSION_KEY_LENGTH, cipher_type);
memset(session_key, 0, sizeof(session_key));
debug("Received session key; encryption turned on.");
packet_start(SSH_SMSG_SUCCESS);
packet_send();
packet_write_wait();
}
static void
do_ssh2_kex(void)
{
Kex *kex;
if (options.ciphers != NULL) {
myproposal[PROPOSAL_ENC_ALGS_CTOS] =
myproposal[PROPOSAL_ENC_ALGS_STOC] = options.ciphers;
}
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]);
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();
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;
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
packet_start(SSH2_MSG_IGNORE);
packet_put_cstring("markus");
packet_send();
packet_write_wait();
#endif
debug("KEX done");
}