sshd.c revision 62144
1217309Snwhitehorn/* 2251843Sbapt * Author: Tatu Ylonen <ylo@cs.hut.fi> 3217309Snwhitehorn * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4217309Snwhitehorn * All rights reserved 5217309Snwhitehorn * Created: Fri Mar 17 17:09:28 1995 ylo 6251843Sbapt * This program is the ssh daemon. It listens for connections from clients, and 7217309Snwhitehorn * performs authentication, executes use commands or shell, and forwards 8217309Snwhitehorn * information to/from the application to the user client over an encrypted 9217309Snwhitehorn * connection. This can also handle forwarding of X11, TCP/IP, and authentication 10217309Snwhitehorn * agent connections. 11217309Snwhitehorn * 12217309Snwhitehorn * SSH2 implementation, 13217309Snwhitehorn * Copyright (c) 2000 Markus Friedl. All rights reserved. 14217309Snwhitehorn * 15217309Snwhitehorn * $FreeBSD: head/crypto/openssh/sshd.c 62144 2000-06-26 23:39:26Z green $ 16217309Snwhitehorn */ 17217309Snwhitehorn 18217309Snwhitehorn#include "includes.h" 19217309SnwhitehornRCSID("$OpenBSD: sshd.c,v 1.118 2000/05/25 20:45:20 markus Exp $"); 20217309Snwhitehorn 21217309Snwhitehorn#include "xmalloc.h" 22217309Snwhitehorn#include "rsa.h" 23217309Snwhitehorn#include "ssh.h" 24217309Snwhitehorn#include "pty.h" 25217309Snwhitehorn#include "packet.h" 26217309Snwhitehorn#include "cipher.h" 27217309Snwhitehorn#include "mpaux.h" 28217309Snwhitehorn#include "servconf.h" 29217309Snwhitehorn#include "uidswap.h" 30217309Snwhitehorn#include "compat.h" 31217309Snwhitehorn#include "buffer.h" 32217309Snwhitehorn#include <poll.h> 33217309Snwhitehorn#include <time.h> 34217309Snwhitehorn 35217309Snwhitehorn#include "ssh2.h" 36217309Snwhitehorn#include <openssl/dh.h> 37217309Snwhitehorn#include <openssl/bn.h> 38217309Snwhitehorn#include <openssl/hmac.h> 39217309Snwhitehorn#include "kex.h" 40217309Snwhitehorn#include <openssl/dsa.h> 41217309Snwhitehorn#include <openssl/rsa.h> 42217309Snwhitehorn#include "key.h" 43217309Snwhitehorn#include "dsa.h" 44217309Snwhitehorn 45217309Snwhitehorn#include "auth.h" 46217309Snwhitehorn#include "myproposal.h" 47217309Snwhitehorn#include "authfile.h" 48217309Snwhitehorn 49217309Snwhitehorn#ifdef LIBWRAP 50217309Snwhitehorn#include <tcpd.h> 51217309Snwhitehorn#include <syslog.h> 52217309Snwhitehornint allow_severity = LOG_INFO; 53217309Snwhitehornint deny_severity = LOG_WARNING; 54217309Snwhitehorn#endif /* LIBWRAP */ 55217309Snwhitehorn 56217309Snwhitehorn#ifndef O_NOCTTY 57217309Snwhitehorn#define O_NOCTTY 0 58217309Snwhitehorn#endif 59251843Sbapt 60217309Snwhitehorn#ifdef KRB5 61217309Snwhitehorn#include <krb5.h> 62217309Snwhitehorn#endif /* KRB5 */ 63217309Snwhitehorn 64217309Snwhitehorn/* Server configuration options. */ 65217309SnwhitehornServerOptions options; 66217309Snwhitehorn 67217309Snwhitehorn/* Name of the server configuration file. */ 68217309Snwhitehornchar *config_file_name = SERVER_CONFIG_FILE; 69217309Snwhitehorn 70224014Snwhitehorn/* 71217309Snwhitehorn * Flag indicating whether IPv4 or IPv6. This can be set on the command line. 72224014Snwhitehorn * Default value is AF_UNSPEC means both IPv4 and IPv6. 73217309Snwhitehorn */ 74217309Snwhitehornint IPv4or6 = AF_UNSPEC; 75217309Snwhitehorn 76217309Snwhitehorn/* 77217309Snwhitehorn * Debug mode flag. This can be set on the command line. If debug 78217309Snwhitehorn * mode is enabled, extra debugging output will be sent to the system 79217309Snwhitehorn * log, the daemon will not go to background, and will exit after processing 80217309Snwhitehorn * the first connection. 81217309Snwhitehorn */ 82217309Snwhitehornint debug_flag = 0; 83217309Snwhitehorn 84217309Snwhitehorn/* Flag indicating that the daemon is being started from inetd. */ 85251843Sbaptint inetd_flag = 0; 86217309Snwhitehorn 87217309Snwhitehorn/* debug goes to stderr unless inetd_flag is set */ 88217309Snwhitehornint log_stderr = 0; 89217309Snwhitehorn 90217309Snwhitehorn/* argv[0] without path. */ 91217309Snwhitehornchar *av0; 92217309Snwhitehorn 93217309Snwhitehorn/* Saved arguments to main(). */ 94217309Snwhitehornchar **saved_argv; 95217309Snwhitehorn 96217309Snwhitehorn/* 97220749Snwhitehorn * The sockets that the server is listening; this is used in the SIGHUP 98224014Snwhitehorn * signal handler. 99217309Snwhitehorn */ 100217309Snwhitehorn#define MAX_LISTEN_SOCKS 16 101217309Snwhitehornint listen_socks[MAX_LISTEN_SOCKS]; 102217309Snwhitehornint num_listen_socks = 0; 103217309Snwhitehorn 104217309Snwhitehorn/* 105217309Snwhitehorn * the client's version string, passed by sshd2 in compat mode. if != NULL, 106217309Snwhitehorn * sshd will skip the version-number exchange 107217309Snwhitehorn */ 108220749Snwhitehornchar *client_version_string = NULL; 109217309Snwhitehornchar *server_version_string = NULL; 110217309Snwhitehorn 111217309Snwhitehorn/* 112220749Snwhitehorn * Any really sensitive data in the application is contained in this 113217309Snwhitehorn * structure. The idea is that this structure could be locked into memory so 114217309Snwhitehorn * that the pages do not get written into swap. However, there are some 115217309Snwhitehorn * problems. The private key contains BIGNUMs, and we do not (in principle) 116217309Snwhitehorn * have access to the internals of them, and locking just the structure is 117217309Snwhitehorn * not very useful. Currently, memory locking is not implemented. 118217309Snwhitehorn */ 119217309Snwhitehornstruct { 120217309Snwhitehorn RSA *private_key; /* Private part of empheral server key. */ 121217309Snwhitehorn RSA *host_key; /* Private part of host key. */ 122217309Snwhitehorn Key *dsa_host_key; /* Private DSA host key. */ 123217309Snwhitehorn} sensitive_data; 124217309Snwhitehorn 125217309Snwhitehorn/* 126217309Snwhitehorn * Flag indicating whether the current session key has been used. This flag 127217309Snwhitehorn * is set whenever the key is used, and cleared when the key is regenerated. 128217309Snwhitehorn */ 129217309Snwhitehornint key_used = 0; 130217309Snwhitehorn 131217309Snwhitehorn/* This is set to true when SIGHUP is received. */ 132217309Snwhitehornint received_sighup = 0; 133217309Snwhitehorn 134217309Snwhitehorn/* Public side of the server key. This value is regenerated regularly with 135217309Snwhitehorn the private key. */ 136217309SnwhitehornRSA *public_key; 137217309Snwhitehorn 138224014Snwhitehorn/* session identifier, used by RSA-auth */ 139217309Snwhitehornunsigned char session_id[16]; 140217309Snwhitehorn 141217309Snwhitehorn/* same for ssh2 */ 142217309Snwhitehornunsigned char *session_id2 = NULL; 143251843Sbaptint session_id2_len = 0; 144251843Sbapt 145251843Sbapt/* These are used to implement connections_per_period. */ 146251843Sbaptstruct ratelim_connection { 147251843Sbapt struct timeval connections_begin; 148251843Sbapt unsigned int connections_this_period; 149251843Sbapt} *ratelim_connections; 150251843Sbapt 151251843Sbaptstatic void 152251843Sbaptratelim_init(void) { 153251843Sbapt ratelim_connections = calloc(num_listen_socks, 154251843Sbapt sizeof(struct ratelim_connection)); 155251843Sbapt if (ratelim_connections == NULL) 156251843Sbapt fatal("calloc: %s", strerror(errno)); 157251843Sbapt} 158251843Sbapt 159251843Sbaptstatic __inline struct timeval 160251843Sbapttimevaldiff(struct timeval *tv1, struct timeval *tv2) { 161251843Sbapt struct timeval diff; 162251843Sbapt int carry; 163217309Snwhitehorn 164217309Snwhitehorn carry = tv1->tv_usec > tv2->tv_usec; 165217309Snwhitehorn diff.tv_sec = tv2->tv_sec - tv1->tv_sec - (carry ? 0 : 1); 166217309Snwhitehorn diff.tv_usec = tv2->tv_usec - tv1->tv_usec + (carry ? 1000000 : 0); 167217309Snwhitehorn 168217309Snwhitehorn return diff; 169217309Snwhitehorn} 170217309Snwhitehorn 171217309Snwhitehorn/* Prototypes for various functions defined later in this file. */ 172217309Snwhitehornvoid do_ssh1_kex(); 173217309Snwhitehornvoid do_ssh2_kex(); 174217309Snwhitehorn 175217309Snwhitehorn/* 176217309Snwhitehorn * Close all listening sockets 177217309Snwhitehorn */ 178217309Snwhitehornvoid 179217309Snwhitehornclose_listen_socks(void) 180217309Snwhitehorn{ 181217309Snwhitehorn int i; 182217309Snwhitehorn for (i = 0; i < num_listen_socks; i++) 183217309Snwhitehorn close(listen_socks[i]); 184217309Snwhitehorn num_listen_socks = -1; 185217309Snwhitehorn} 186217309Snwhitehorn 187217309Snwhitehorn/* 188217309Snwhitehorn * Signal handler for SIGHUP. Sshd execs itself when it receives SIGHUP; 189217309Snwhitehorn * the effect is to reread the configuration file (and to regenerate 190217309Snwhitehorn * the server key). 191217309Snwhitehorn */ 192217309Snwhitehornvoid 193217309Snwhitehornsighup_handler(int sig) 194217309Snwhitehorn{ 195217309Snwhitehorn received_sighup = 1; 196217309Snwhitehorn signal(SIGHUP, sighup_handler); 197217309Snwhitehorn} 198217309Snwhitehorn 199217309Snwhitehorn/* 200217309Snwhitehorn * Called from the main program after receiving SIGHUP. 201217309Snwhitehorn * Restarts the server. 202217309Snwhitehorn */ 203217309Snwhitehornvoid 204217309Snwhitehornsighup_restart() 205217309Snwhitehorn{ 206217309Snwhitehorn log("Received SIGHUP; restarting."); 207251843Sbapt close_listen_socks(); 208251843Sbapt execv(saved_argv[0], saved_argv); 209217309Snwhitehorn log("RESTART FAILED: av0='%s', error: %s.", av0, strerror(errno)); 210217309Snwhitehorn exit(1); 211217309Snwhitehorn} 212217309Snwhitehorn 213217309Snwhitehorn/* 214217309Snwhitehorn * Generic signal handler for terminating signals in the master daemon. 215217309Snwhitehorn * These close the listen socket; not closing it seems to cause "Address 216217309Snwhitehorn * already in use" problems on some machines, which is inconvenient. 217217309Snwhitehorn */ 218251843Sbaptvoid 219217309Snwhitehornsigterm_handler(int sig) 220217309Snwhitehorn{ 221217309Snwhitehorn log("Received signal %d; terminating.", sig); 222217309Snwhitehorn close_listen_socks(); 223217309Snwhitehorn unlink(options.pid_file); 224217309Snwhitehorn exit(255); 225217309Snwhitehorn} 226217309Snwhitehorn 227217309Snwhitehorn/* 228217309Snwhitehorn * SIGCHLD handler. This is called whenever a child dies. This will then 229217309Snwhitehorn * reap any zombies left by exited c. 230217309Snwhitehorn */ 231217309Snwhitehornvoid 232224014Snwhitehornmain_sigchld_handler(int sig) 233224014Snwhitehorn{ 234217309Snwhitehorn int save_errno = errno; 235217309Snwhitehorn int status; 236217309Snwhitehorn 237217309Snwhitehorn while (waitpid(-1, &status, WNOHANG) > 0) 238217309Snwhitehorn ; 239217309Snwhitehorn 240217309Snwhitehorn signal(SIGCHLD, main_sigchld_handler); 241217309Snwhitehorn errno = save_errno; 242217309Snwhitehorn} 243217309Snwhitehorn 244217309Snwhitehorn/* 245251843Sbapt * Signal handler for the alarm after the login grace period has expired. 246217309Snwhitehorn */ 247217309Snwhitehornvoid 248217309Snwhitehorngrace_alarm_handler(int sig) 249217309Snwhitehorn{ 250217309Snwhitehorn /* Close the connection. */ 251217309Snwhitehorn packet_close(); 252217309Snwhitehorn 253217309Snwhitehorn /* Log error and exit. */ 254251843Sbapt fatal("Timeout before authentication for %s.", get_remote_ipaddr()); 255217309Snwhitehorn} 256217309Snwhitehorn 257217309Snwhitehorn/* 258220749Snwhitehorn * Signal handler for the key regeneration alarm. Note that this 259224014Snwhitehorn * alarm only occurs in the daemon waiting for connections, and it does not 260217309Snwhitehorn * do anything with the private key or random state before forking. 261217309Snwhitehorn * Thus there should be no concurrency control/asynchronous execution 262217309Snwhitehorn * problems. 263217309Snwhitehorn */ 264217309Snwhitehorn/* XXX do we really want this work to be done in a signal handler ? -m */ 265217309Snwhitehornvoid 266217309Snwhitehornkey_regeneration_alarm(int sig) 267217309Snwhitehorn{ 268217309Snwhitehorn int save_errno = errno; 269217309Snwhitehorn 270220749Snwhitehorn /* Check if we should generate a new key. */ 271217309Snwhitehorn if (key_used) { 272217309Snwhitehorn /* This should really be done in the background. */ 273217309Snwhitehorn log("Generating new %d bit RSA key.", options.server_key_bits); 274220749Snwhitehorn 275220749Snwhitehorn if (sensitive_data.private_key != NULL) 276217309Snwhitehorn RSA_free(sensitive_data.private_key); 277217309Snwhitehorn sensitive_data.private_key = RSA_new(); 278217309Snwhitehorn 279217309Snwhitehorn if (public_key != NULL) 280217309Snwhitehorn RSA_free(public_key); 281217309Snwhitehorn public_key = RSA_new(); 282217309Snwhitehorn 283217309Snwhitehorn rsa_generate_key(sensitive_data.private_key, public_key, 284217309Snwhitehorn options.server_key_bits); 285217309Snwhitehorn arc4random_stir(); 286217309Snwhitehorn key_used = 0; 287217309Snwhitehorn log("RSA key generation complete."); 288217309Snwhitehorn } 289217309Snwhitehorn /* Reschedule the alarm. */ 290217309Snwhitehorn signal(SIGALRM, key_regeneration_alarm); 291217309Snwhitehorn alarm(options.key_regeneration_time); 292217309Snwhitehorn errno = save_errno; 293217309Snwhitehorn} 294217309Snwhitehorn 295217309Snwhitehornvoid 296217309Snwhitehornsshd_exchange_identification(int sock_in, int sock_out) 297217309Snwhitehorn{ 298217309Snwhitehorn int i, mismatch; 299217309Snwhitehorn int remote_major, remote_minor; 300224014Snwhitehorn int major, minor; 301217309Snwhitehorn char *s; 302217309Snwhitehorn char buf[256]; /* Must not be larger than remote_version. */ 303217309Snwhitehorn char remote_version[256]; /* Must be at least as big as buf. */ 304217309Snwhitehorn 305251843Sbapt if ((options.protocol & SSH_PROTO_1) && 306251843Sbapt (options.protocol & SSH_PROTO_2)) { 307251843Sbapt major = PROTOCOL_MAJOR_1; 308251843Sbapt minor = 99; 309251843Sbapt } else if (options.protocol & SSH_PROTO_2) { 310251843Sbapt major = PROTOCOL_MAJOR_2; 311251843Sbapt minor = PROTOCOL_MINOR_2; 312251843Sbapt } else { 313251843Sbapt major = PROTOCOL_MAJOR_1; 314251843Sbapt minor = PROTOCOL_MINOR_1; 315251843Sbapt } 316251843Sbapt snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", major, minor, SSH_VERSION); 317251843Sbapt server_version_string = xstrdup(buf); 318251843Sbapt 319251843Sbapt if (client_version_string == NULL) { 320251843Sbapt /* Send our protocol version identification. */ 321251843Sbapt if (atomicio(write, sock_out, server_version_string, strlen(server_version_string)) 322251843Sbapt != strlen(server_version_string)) { 323251843Sbapt log("Could not write ident string to %s.", get_remote_ipaddr()); 324251843Sbapt fatal_cleanup(); 325251843Sbapt } 326251843Sbapt 327251843Sbapt /* Read other side\'s version identification. */ 328251843Sbapt for (i = 0; i < sizeof(buf) - 1; i++) { 329251843Sbapt if (read(sock_in, &buf[i], 1) != 1) { 330251843Sbapt log("Did not receive ident string from %s.", get_remote_ipaddr()); 331251843Sbapt fatal_cleanup(); 332251843Sbapt } 333217309Snwhitehorn if (buf[i] == '\r') { 334217309Snwhitehorn buf[i] = '\n'; 335217309Snwhitehorn buf[i + 1] = 0; 336217309Snwhitehorn continue; 337217309Snwhitehorn } 338217309Snwhitehorn if (buf[i] == '\n') { 339217309Snwhitehorn /* buf[i] == '\n' */ 340217309Snwhitehorn buf[i + 1] = 0; 341217309Snwhitehorn break; 342217309Snwhitehorn } 343217309Snwhitehorn } 344217309Snwhitehorn buf[sizeof(buf) - 1] = 0; 345217309Snwhitehorn client_version_string = xstrdup(buf); 346217309Snwhitehorn } 347217309Snwhitehorn 348217309Snwhitehorn /* 349217309Snwhitehorn * Check that the versions match. In future this might accept 350217309Snwhitehorn * several versions and set appropriate flags to handle them. 351217309Snwhitehorn */ 352217309Snwhitehorn if (sscanf(client_version_string, "SSH-%d.%d-%[^\n]\n", 353217309Snwhitehorn &remote_major, &remote_minor, remote_version) != 3) { 354217309Snwhitehorn s = "Protocol mismatch.\n"; 355217309Snwhitehorn (void) atomicio(write, sock_out, s, strlen(s)); 356217309Snwhitehorn close(sock_in); 357217309Snwhitehorn close(sock_out); 358217309Snwhitehorn log("Bad protocol version identification '%.100s' from %s", 359217309Snwhitehorn client_version_string, get_remote_ipaddr()); 360217309Snwhitehorn fatal_cleanup(); 361217309Snwhitehorn } 362217309Snwhitehorn debug("Client protocol version %d.%d; client software version %.100s", 363217309Snwhitehorn remote_major, remote_minor, remote_version); 364217309Snwhitehorn 365217309Snwhitehorn compat_datafellows(remote_version); 366217309Snwhitehorn 367217309Snwhitehorn mismatch = 0; 368217309Snwhitehorn switch(remote_major) { 369220749Snwhitehorn case 1: 370217309Snwhitehorn if (remote_minor == 99) { 371217309Snwhitehorn if (options.protocol & SSH_PROTO_2) 372217309Snwhitehorn enable_compat20(); 373217309Snwhitehorn else 374217309Snwhitehorn mismatch = 1; 375220749Snwhitehorn break; 376217309Snwhitehorn } 377217309Snwhitehorn if (!(options.protocol & SSH_PROTO_1)) { 378217309Snwhitehorn mismatch = 1; 379217309Snwhitehorn break; 380217309Snwhitehorn } 381217309Snwhitehorn if (remote_minor < 3) { 382217309Snwhitehorn packet_disconnect("Your ssh version is too old and" 383217309Snwhitehorn "is no longer supported. Please install a newer version."); 384217309Snwhitehorn } else if (remote_minor == 3) { 385217309Snwhitehorn /* note that this disables agent-forwarding */ 386217309Snwhitehorn enable_compat13(); 387217309Snwhitehorn } 388217309Snwhitehorn break; 389217309Snwhitehorn case 2: 390217309Snwhitehorn if (options.protocol & SSH_PROTO_2) { 391217309Snwhitehorn enable_compat20(); 392217309Snwhitehorn break; 393217309Snwhitehorn } 394217309Snwhitehorn /* FALLTHROUGH */ 395217309Snwhitehorn default: 396217309Snwhitehorn mismatch = 1; 397217309Snwhitehorn break; 398217309Snwhitehorn } 399217309Snwhitehorn chop(server_version_string); 400217309Snwhitehorn chop(client_version_string); 401217309Snwhitehorn debug("Local version string %.200s", server_version_string); 402217309Snwhitehorn 403220749Snwhitehorn if (mismatch) { 404217309Snwhitehorn s = "Protocol major versions differ.\n"; 405217309Snwhitehorn (void) atomicio(write, sock_out, s, strlen(s)); 406217309Snwhitehorn close(sock_in); 407217309Snwhitehorn close(sock_out); 408217309Snwhitehorn log("Protocol major versions differ for %s: %.200s vs. %.200s", 409217309Snwhitehorn get_remote_ipaddr(), 410217309Snwhitehorn server_version_string, client_version_string); 411217309Snwhitehorn fatal_cleanup(); 412217309Snwhitehorn } 413220749Snwhitehorn if (compat20) 414220749Snwhitehorn packet_set_ssh2_format(); 415217309Snwhitehorn} 416217309Snwhitehorn 417217309Snwhitehorn 418217309Snwhitehornvoid 419217309Snwhitehorndestroy_sensitive_data(void) 420217309Snwhitehorn{ 421217309Snwhitehorn /* Destroy the private and public keys. They will no longer be needed. */ 422217309Snwhitehorn if (public_key) 423217309Snwhitehorn RSA_free(public_key); 424217309Snwhitehorn if (sensitive_data.private_key) 425217309Snwhitehorn RSA_free(sensitive_data.private_key); 426217309Snwhitehorn if (sensitive_data.host_key) 427217309Snwhitehorn RSA_free(sensitive_data.host_key); 428217309Snwhitehorn if (sensitive_data.dsa_host_key != NULL) 429217309Snwhitehorn key_free(sensitive_data.dsa_host_key); 430217309Snwhitehorn} 431217309Snwhitehorn 432217309Snwhitehorn/* 433217309Snwhitehorn * Main program for the daemon. 434217309Snwhitehorn */ 435217309Snwhitehornint 436217309Snwhitehornmain(int ac, char **av) 437217309Snwhitehorn{ 438217309Snwhitehorn extern char *optarg; 439217309Snwhitehorn extern int optind; 440217309Snwhitehorn int opt, sock_in = 0, sock_out = 0, newsock, i, fdsetsz, on = 1; 441217309Snwhitehorn pid_t pid; 442217309Snwhitehorn socklen_t fromlen; 443217309Snwhitehorn int ratelim_exceeded = 0; 444217309Snwhitehorn int silent = 0; 445217309Snwhitehorn fd_set *fdset; 446217309Snwhitehorn struct sockaddr_storage from; 447217309Snwhitehorn const char *remote_ip; 448217309Snwhitehorn int remote_port; 449217309Snwhitehorn FILE *f; 450217309Snwhitehorn struct linger linger; 451217309Snwhitehorn struct addrinfo *ai; 452217309Snwhitehorn char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 453217309Snwhitehorn int listen_sock, maxfd; 454217309Snwhitehorn 455220749Snwhitehorn /* Save argv[0]. */ 456217309Snwhitehorn saved_argv = av; 457217309Snwhitehorn if (strchr(av[0], '/')) 458217309Snwhitehorn av0 = strrchr(av[0], '/') + 1; 459217309Snwhitehorn else 460217309Snwhitehorn av0 = av[0]; 461217309Snwhitehorn 462217309Snwhitehorn /* Initialize configuration options to their default values. */ 463217309Snwhitehorn initialize_server_options(&options); 464217309Snwhitehorn 465217309Snwhitehorn /* Parse command-line arguments. */ 466217309Snwhitehorn while ((opt = getopt(ac, av, "f:p:b:k:h:g:V:diqQ46")) != EOF) { 467217309Snwhitehorn switch (opt) { 468217309Snwhitehorn case '4': 469217309Snwhitehorn IPv4or6 = AF_INET; 470217309Snwhitehorn break; 471251843Sbapt case '6': 472251843Sbapt IPv4or6 = AF_INET6; 473251843Sbapt break; 474251843Sbapt case 'f': 475251843Sbapt config_file_name = optarg; 476251843Sbapt break; 477217309Snwhitehorn case 'd': 478217309Snwhitehorn debug_flag = 1; 479217309Snwhitehorn options.log_level = SYSLOG_LEVEL_DEBUG; 480217309Snwhitehorn break; 481217309Snwhitehorn case 'i': 482217309Snwhitehorn inetd_flag = 1; 483217309Snwhitehorn break; 484217309Snwhitehorn case 'Q': 485217309Snwhitehorn silent = 1; 486217309Snwhitehorn break; 487217309Snwhitehorn case 'q': 488217309Snwhitehorn options.log_level = SYSLOG_LEVEL_QUIET; 489217309Snwhitehorn break; 490217309Snwhitehorn case 'b': 491217309Snwhitehorn options.server_key_bits = atoi(optarg); 492217309Snwhitehorn break; 493217309Snwhitehorn case 'p': 494220749Snwhitehorn options.ports_from_cmdline = 1; 495251843Sbapt if (options.num_ports >= MAX_PORTS) 496251843Sbapt fatal("too many ports.\n"); 497251843Sbapt options.ports[options.num_ports++] = atoi(optarg); 498251843Sbapt break; 499251843Sbapt case 'g': 500217309Snwhitehorn options.login_grace_time = atoi(optarg); 501217309Snwhitehorn break; 502217309Snwhitehorn case 'k': 503217309Snwhitehorn options.key_regeneration_time = atoi(optarg); 504217309Snwhitehorn break; 505217309Snwhitehorn case 'h': 506217309Snwhitehorn options.host_key_file = optarg; 507217309Snwhitehorn break; 508217309Snwhitehorn case 'V': 509251843Sbapt client_version_string = optarg; 510217309Snwhitehorn /* only makes sense with inetd_flag, i.e. no listen() */ 511217309Snwhitehorn inetd_flag = 1; 512217309Snwhitehorn break; 513217309Snwhitehorn case '?': 514217309Snwhitehorn default: 515217309Snwhitehorn fprintf(stderr, "sshd version %s\n", SSH_VERSION); 516251843Sbapt fprintf(stderr, "Usage: %s [options]\n", av0); 517251843Sbapt fprintf(stderr, "Options:\n"); 518217309Snwhitehorn fprintf(stderr, " -f file Configuration file (default %s)\n", SERVER_CONFIG_FILE); 519217309Snwhitehorn fprintf(stderr, " -d Debugging mode\n"); 520217309Snwhitehorn fprintf(stderr, " -i Started from inetd\n"); 521251843Sbapt fprintf(stderr, " -q Quiet (no logging)\n"); 522217309Snwhitehorn fprintf(stderr, " -p port Listen on the specified port (default: 22)\n"); 523217309Snwhitehorn fprintf(stderr, " -k seconds Regenerate server key every this many seconds (default: 3600)\n"); 524217309Snwhitehorn fprintf(stderr, " -g seconds Grace period for authentication (default: 300)\n"); 525217309Snwhitehorn fprintf(stderr, " -b bits Size of server RSA key (default: 768 bits)\n"); 526217309Snwhitehorn fprintf(stderr, " -h file File from which to read host key (default: %s)\n", 527217309Snwhitehorn HOST_KEY_FILE); 528217309Snwhitehorn fprintf(stderr, " -4 Use IPv4 only\n"); 529217309Snwhitehorn fprintf(stderr, " -6 Use IPv6 only\n"); 530217309Snwhitehorn exit(1); 531217309Snwhitehorn } 532217309Snwhitehorn } 533217309Snwhitehorn 534217309Snwhitehorn /* 535217309Snwhitehorn * Force logging to stderr until we have loaded the private host 536217309Snwhitehorn * key (unless started from inetd) 537217309Snwhitehorn */ 538217309Snwhitehorn log_init(av0, 539217309Snwhitehorn options.log_level == -1 ? SYSLOG_LEVEL_INFO : options.log_level, 540217309Snwhitehorn options.log_facility == -1 ? SYSLOG_FACILITY_AUTH : options.log_facility, 541217309Snwhitehorn !silent && !inetd_flag); 542217309Snwhitehorn 543217309Snwhitehorn /* Read server configuration options from the configuration file. */ 544217309Snwhitehorn read_server_config(&options, config_file_name); 545217309Snwhitehorn 546217309Snwhitehorn /* Fill in default values for those options not explicitly set. */ 547217309Snwhitehorn fill_default_server_options(&options); 548217309Snwhitehorn 549217309Snwhitehorn /* Check that there are no remaining arguments. */ 550217309Snwhitehorn if (optind < ac) { 551217309Snwhitehorn fprintf(stderr, "Extra argument %s.\n", av[optind]); 552217309Snwhitehorn exit(1); 553217309Snwhitehorn } 554217309Snwhitehorn 555217309Snwhitehorn debug("sshd version %.100s", SSH_VERSION); 556217309Snwhitehorn 557217309Snwhitehorn sensitive_data.dsa_host_key = NULL; 558217309Snwhitehorn sensitive_data.host_key = NULL; 559217309Snwhitehorn 560217309Snwhitehorn /* check if RSA support exists */ 561217309Snwhitehorn if ((options.protocol & SSH_PROTO_1) && 562217309Snwhitehorn rsa_alive() == 0) { 563217309Snwhitehorn log("no RSA support in libssl and libcrypto. See ssl(8)"); 564217309Snwhitehorn log("Disabling protocol version 1"); 565217309Snwhitehorn options.protocol &= ~SSH_PROTO_1; 566217309Snwhitehorn } 567217309Snwhitehorn /* Load the RSA/DSA host key. It must have empty passphrase. */ 568217309Snwhitehorn if (options.protocol & SSH_PROTO_1) { 569217309Snwhitehorn Key k; 570217309Snwhitehorn sensitive_data.host_key = RSA_new(); 571217309Snwhitehorn k.type = KEY_RSA; 572217309Snwhitehorn k.rsa = sensitive_data.host_key; 573217309Snwhitehorn errno = 0; 574217309Snwhitehorn if (!load_private_key(options.host_key_file, "", &k, NULL)) { 575251843Sbapt error("Could not load host key: %.200s: %.100s", 576217309Snwhitehorn options.host_key_file, strerror(errno)); 577251843Sbapt log("Disabling protocol version 1"); 578251843Sbapt options.protocol &= ~SSH_PROTO_1; 579251843Sbapt } 580251843Sbapt k.rsa = NULL; 581251843Sbapt } 582251843Sbapt if (options.protocol & SSH_PROTO_2) { 583251843Sbapt sensitive_data.dsa_host_key = key_new(KEY_DSA); 584251843Sbapt if (!load_private_key(options.host_dsa_key_file, "", sensitive_data.dsa_host_key, NULL)) { 585251843Sbapt 586217309Snwhitehorn error("Could not load DSA host key: %.200s", options.host_dsa_key_file); 587217309Snwhitehorn log("Disabling protocol version 2"); 588217309Snwhitehorn options.protocol &= ~SSH_PROTO_2; 589217309Snwhitehorn } 590217309Snwhitehorn } 591217309Snwhitehorn if (! options.protocol & (SSH_PROTO_1|SSH_PROTO_2)) { 592217309Snwhitehorn if (silent == 0) 593217309Snwhitehorn fprintf(stderr, "sshd: no hostkeys available -- exiting.\n"); 594217309Snwhitehorn log("sshd: no hostkeys available -- exiting.\n"); 595217309Snwhitehorn exit(1); 596217309Snwhitehorn } 597217309Snwhitehorn 598217309Snwhitehorn /* Check certain values for sanity. */ 599217309Snwhitehorn if (options.protocol & SSH_PROTO_1) { 600217309Snwhitehorn if (options.server_key_bits < 512 || 601217309Snwhitehorn options.server_key_bits > 32768) { 602217309Snwhitehorn fprintf(stderr, "Bad server key size.\n"); 603217309Snwhitehorn exit(1); 604217309Snwhitehorn } 605217309Snwhitehorn /* 606217309Snwhitehorn * Check that server and host key lengths differ sufficiently. This 607217309Snwhitehorn * is necessary to make double encryption work with rsaref. Oh, I 608217309Snwhitehorn * hate software patents. I dont know if this can go? Niels 609217309Snwhitehorn */ 610217309Snwhitehorn if (options.server_key_bits > 611217309Snwhitehorn BN_num_bits(sensitive_data.host_key->n) - SSH_KEY_BITS_RESERVED && 612217309Snwhitehorn options.server_key_bits < 613217309Snwhitehorn BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED) { 614217309Snwhitehorn options.server_key_bits = 615217309Snwhitehorn BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED; 616217309Snwhitehorn debug("Forcing server key to %d bits to make it differ from host key.", 617217309Snwhitehorn options.server_key_bits); 618217309Snwhitehorn } 619217309Snwhitehorn } 620217309Snwhitehorn 621217309Snwhitehorn /* Initialize the log (it is reinitialized below in case we forked). */ 622217309Snwhitehorn if (debug_flag && !inetd_flag) 623217309Snwhitehorn log_stderr = 1; 624217309Snwhitehorn log_init(av0, options.log_level, options.log_facility, log_stderr); 625217309Snwhitehorn 626217309Snwhitehorn /* 627217309Snwhitehorn * If not in debugging mode, and not started from inetd, disconnect 628217309Snwhitehorn * from the controlling terminal, and fork. The original process 629217309Snwhitehorn * exits. 630217309Snwhitehorn */ 631217309Snwhitehorn if (!debug_flag && !inetd_flag) { 632220749Snwhitehorn#ifdef TIOCNOTTY 633220749Snwhitehorn int fd; 634217309Snwhitehorn#endif /* TIOCNOTTY */ 635217309Snwhitehorn if (daemon(0, 0) < 0) 636217309Snwhitehorn fatal("daemon() failed: %.200s", strerror(errno)); 637217309Snwhitehorn 638217309Snwhitehorn /* Disconnect from the controlling tty. */ 639217309Snwhitehorn#ifdef TIOCNOTTY 640217309Snwhitehorn fd = open("/dev/tty", O_RDWR | O_NOCTTY); 641217309Snwhitehorn if (fd >= 0) { 642217309Snwhitehorn (void) ioctl(fd, TIOCNOTTY, NULL); 643217309Snwhitehorn close(fd); 644217309Snwhitehorn } 645217309Snwhitehorn#endif /* TIOCNOTTY */ 646217309Snwhitehorn } 647217309Snwhitehorn /* Reinitialize the log (because of the fork above). */ 648217309Snwhitehorn log_init(av0, options.log_level, options.log_facility, log_stderr); 649217309Snwhitehorn 650217309Snwhitehorn /* Do not display messages to stdout in RSA code. */ 651217309Snwhitehorn rsa_set_verbose(0); 652217309Snwhitehorn 653217309Snwhitehorn /* Initialize the random number generator. */ 654217309Snwhitehorn arc4random_stir(); 655217309Snwhitehorn 656217309Snwhitehorn /* Chdir to the root directory so that the current disk can be 657217309Snwhitehorn unmounted if desired. */ 658217309Snwhitehorn chdir("/"); 659217309Snwhitehorn 660217309Snwhitehorn /* Start listening for a socket, unless started from inetd. */ 661217309Snwhitehorn if (inetd_flag) { 662217309Snwhitehorn int s1, s2; 663217309Snwhitehorn s1 = dup(0); /* Make sure descriptors 0, 1, and 2 are in use. */ 664217309Snwhitehorn s2 = dup(s1); 665217309Snwhitehorn sock_in = dup(0); 666217309Snwhitehorn sock_out = dup(1); 667217309Snwhitehorn /* 668217309Snwhitehorn * We intentionally do not close the descriptors 0, 1, and 2 669217309Snwhitehorn * as our code for setting the descriptors won\'t work if 670217309Snwhitehorn * ttyfd happens to be one of those. 671217309Snwhitehorn */ 672217309Snwhitehorn debug("inetd sockets after dupping: %d, %d", sock_in, sock_out); 673217309Snwhitehorn 674217309Snwhitehorn if (options.protocol & SSH_PROTO_1) { 675217309Snwhitehorn public_key = RSA_new(); 676217309Snwhitehorn sensitive_data.private_key = RSA_new(); 677217309Snwhitehorn log("Generating %d bit RSA key.", options.server_key_bits); 678217309Snwhitehorn rsa_generate_key(sensitive_data.private_key, public_key, 679217309Snwhitehorn options.server_key_bits); 680217309Snwhitehorn arc4random_stir(); 681217309Snwhitehorn log("RSA key generation complete."); 682217309Snwhitehorn } 683217309Snwhitehorn } else { 684217309Snwhitehorn for (ai = options.listen_addrs; ai; ai = ai->ai_next) { 685217309Snwhitehorn if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) 686217309Snwhitehorn continue; 687217309Snwhitehorn if (num_listen_socks >= MAX_LISTEN_SOCKS) 688217309Snwhitehorn fatal("Too many listen sockets. " 689217309Snwhitehorn "Enlarge MAX_LISTEN_SOCKS"); 690217309Snwhitehorn if (getnameinfo(ai->ai_addr, ai->ai_addrlen, 691217309Snwhitehorn ntop, sizeof(ntop), strport, sizeof(strport), 692217309Snwhitehorn NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 693217309Snwhitehorn error("getnameinfo failed"); 694217309Snwhitehorn continue; 695217309Snwhitehorn } 696217309Snwhitehorn /* Create socket for listening. */ 697217309Snwhitehorn listen_sock = socket(ai->ai_family, SOCK_STREAM, 0); 698217309Snwhitehorn if (listen_sock < 0) { 699217309Snwhitehorn /* kernel may not support ipv6 */ 700217309Snwhitehorn verbose("socket: %.100s", strerror(errno)); 701217309Snwhitehorn continue; 702217309Snwhitehorn } 703217309Snwhitehorn if (fcntl(listen_sock, F_SETFL, O_NONBLOCK) < 0) { 704217309Snwhitehorn error("listen_sock O_NONBLOCK: %s", strerror(errno)); 705217309Snwhitehorn close(listen_sock); 706217309Snwhitehorn continue; 707220749Snwhitehorn } 708220749Snwhitehorn /* 709217309Snwhitehorn * Set socket options. We try to make the port 710217309Snwhitehorn * reusable and have it close as fast as possible 711217309Snwhitehorn * without waiting in unnecessary wait states on 712217309Snwhitehorn * close. 713217309Snwhitehorn */ 714217309Snwhitehorn setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, 715220749Snwhitehorn (void *) &on, sizeof(on)); 716220749Snwhitehorn linger.l_onoff = 1; 717220749Snwhitehorn linger.l_linger = 5; 718220749Snwhitehorn setsockopt(listen_sock, SOL_SOCKET, SO_LINGER, 719217309Snwhitehorn (void *) &linger, sizeof(linger)); 720217309Snwhitehorn 721217309Snwhitehorn debug("Bind to port %s on %s.", strport, ntop); 722217309Snwhitehorn 723220749Snwhitehorn /* Bind the socket to the desired port. */ 724220749Snwhitehorn if (bind(listen_sock, ai->ai_addr, ai->ai_addrlen) < 0) { 725220749Snwhitehorn error("Bind to port %s on %s failed: %.200s.", 726220749Snwhitehorn strport, ntop, strerror(errno)); 727220749Snwhitehorn close(listen_sock); 728220749Snwhitehorn continue; 729220749Snwhitehorn } 730220749Snwhitehorn listen_socks[num_listen_socks] = listen_sock; 731220749Snwhitehorn num_listen_socks++; 732220749Snwhitehorn 733220749Snwhitehorn /* Start listening on the port. */ 734217309Snwhitehorn log("Server listening on %s port %s.", ntop, strport); 735217309Snwhitehorn if (listen(listen_sock, 5) < 0) 736217309Snwhitehorn fatal("listen: %.100s", strerror(errno)); 737217309Snwhitehorn 738217309Snwhitehorn } 739217309Snwhitehorn freeaddrinfo(options.listen_addrs); 740217309Snwhitehorn 741217309Snwhitehorn if (!num_listen_socks) 742217309Snwhitehorn fatal("Cannot bind any address."); 743217309Snwhitehorn 744217309Snwhitehorn if (!debug_flag) { 745217309Snwhitehorn /* 746217309Snwhitehorn * Record our pid in /etc/sshd_pid to make it easier 747217309Snwhitehorn * to kill the correct sshd. We don\'t want to do 748217309Snwhitehorn * this before the bind above because the bind will 749217309Snwhitehorn * fail if there already is a daemon, and this will 750217309Snwhitehorn * overwrite any old pid in the file. 751217309Snwhitehorn */ 752217309Snwhitehorn f = fopen(options.pid_file, "w"); 753217309Snwhitehorn if (f) { 754217309Snwhitehorn fprintf(f, "%u\n", (unsigned int) getpid()); 755217309Snwhitehorn fclose(f); 756217309Snwhitehorn } 757217309Snwhitehorn } 758217309Snwhitehorn if (options.protocol & SSH_PROTO_1) { 759217309Snwhitehorn public_key = RSA_new(); 760217309Snwhitehorn sensitive_data.private_key = RSA_new(); 761217309Snwhitehorn 762217309Snwhitehorn log("Generating %d bit RSA key.", options.server_key_bits); 763217309Snwhitehorn rsa_generate_key(sensitive_data.private_key, public_key, 764217309Snwhitehorn options.server_key_bits); 765217309Snwhitehorn arc4random_stir(); 766217309Snwhitehorn log("RSA key generation complete."); 767217309Snwhitehorn 768217309Snwhitehorn /* Schedule server key regeneration alarm. */ 769217309Snwhitehorn signal(SIGALRM, key_regeneration_alarm); 770217309Snwhitehorn alarm(options.key_regeneration_time); 771217309Snwhitehorn } 772217309Snwhitehorn 773217309Snwhitehorn /* Arrange to restart on SIGHUP. The handler needs listen_sock. */ 774217309Snwhitehorn signal(SIGHUP, sighup_handler); 775217309Snwhitehorn signal(SIGTERM, sigterm_handler); 776217309Snwhitehorn signal(SIGQUIT, sigterm_handler); 777217309Snwhitehorn 778217309Snwhitehorn /* Arrange SIGCHLD to be caught. */ 779217309Snwhitehorn signal(SIGCHLD, main_sigchld_handler); 780217309Snwhitehorn 781217309Snwhitehorn /* setup fd set for listen */ 782217309Snwhitehorn maxfd = 0; 783217309Snwhitehorn for (i = 0; i < num_listen_socks; i++) 784217309Snwhitehorn if (listen_socks[i] > maxfd) 785217309Snwhitehorn maxfd = listen_socks[i]; 786217309Snwhitehorn fdsetsz = howmany(maxfd, NFDBITS) * sizeof(fd_mask); 787217309Snwhitehorn fdset = (fd_set *)xmalloc(fdsetsz); 788217309Snwhitehorn 789217309Snwhitehorn ratelim_init(); 790217309Snwhitehorn 791217309Snwhitehorn /* 792217309Snwhitehorn * Stay listening for connections until the system crashes or 793217309Snwhitehorn * the daemon is killed with a signal. 794217309Snwhitehorn */ 795217309Snwhitehorn for (;;) { 796217309Snwhitehorn if (received_sighup) 797217309Snwhitehorn sighup_restart(); 798217309Snwhitehorn /* Wait in select until there is a connection. */ 799217309Snwhitehorn memset(fdset, 0, fdsetsz); 800217309Snwhitehorn for (i = 0; i < num_listen_socks; i++) 801217309Snwhitehorn FD_SET(listen_socks[i], fdset); 802217309Snwhitehorn if (select(maxfd + 1, fdset, NULL, NULL, NULL) < 0) { 803217309Snwhitehorn if (errno != EINTR) 804217309Snwhitehorn error("select: %.100s", strerror(errno)); 805217309Snwhitehorn continue; 806217309Snwhitehorn } 807217309Snwhitehorn for (i = 0; i < num_listen_socks; i++) { 808217309Snwhitehorn if (!FD_ISSET(listen_socks[i], fdset)) 809217309Snwhitehorn continue; 810217309Snwhitehorn fromlen = sizeof(from); 811217309Snwhitehorn newsock = accept(listen_socks[i], (struct sockaddr *)&from, 812217309Snwhitehorn &fromlen); 813217309Snwhitehorn if (newsock < 0) { 814217309Snwhitehorn if (errno != EINTR && errno != EWOULDBLOCK) 815217309Snwhitehorn error("accept: %.100s", strerror(errno)); 816217309Snwhitehorn continue; 817217309Snwhitehorn } 818217309Snwhitehorn if (fcntl(newsock, F_SETFL, 0) < 0) { 819217309Snwhitehorn error("newsock del O_NONBLOCK: %s", strerror(errno)); 820217309Snwhitehorn continue; 821217309Snwhitehorn } 822217309Snwhitehorn if (options.connections_per_period != 0) { 823217309Snwhitehorn struct timeval diff, connections_end; 824217309Snwhitehorn struct ratelim_connection *rc; 825217309Snwhitehorn 826217309Snwhitehorn (void)gettimeofday(&connections_end, NULL); 827217309Snwhitehorn rc = &ratelim_connections[i]; 828217309Snwhitehorn diff = timevaldiff(&rc->connections_begin, 829217309Snwhitehorn &connections_end); 830217309Snwhitehorn if (diff.tv_sec >= options.connections_period) { 831217309Snwhitehorn /* 832217309Snwhitehorn * Slide the window forward only after 833217309Snwhitehorn * completely leaving it. 834217309Snwhitehorn */ 835217309Snwhitehorn rc->connections_begin = connections_end; 836217309Snwhitehorn rc->connections_this_period = 1; 837217309Snwhitehorn } else { 838217309Snwhitehorn if (++rc->connections_this_period > 839217309Snwhitehorn options.connections_per_period) 840217309Snwhitehorn ratelim_exceeded = 1; 841217309Snwhitehorn } 842217309Snwhitehorn } 843217309Snwhitehorn 844217309Snwhitehorn /* 845251843Sbapt * Got connection. Fork a child to handle it unless 846251843Sbapt * we are in debugging mode or the maximum number of 847251843Sbapt * connections per period has been exceeded. 848251843Sbapt */ 849251843Sbapt if (debug_flag) { 850251843Sbapt /* 851251843Sbapt * In debugging mode. Close the listening 852251843Sbapt * socket, and start processing the 853251843Sbapt * connection without forking. 854251843Sbapt */ 855251843Sbapt debug("Server will not fork when running in debugging mode."); 856251843Sbapt close_listen_socks(); 857251843Sbapt sock_in = newsock; 858251843Sbapt sock_out = newsock; 859251843Sbapt pid = getpid(); 860251843Sbapt break; 861251843Sbapt } else if (ratelim_exceeded) { 862251843Sbapt const char *myaddr; 863251843Sbapt 864251843Sbapt myaddr = get_ipaddr(newsock); 865251843Sbapt log("rate limit (%u/%u) on %s port %d " 866251843Sbapt "exceeded by %s", 867251843Sbapt options.connections_per_period, 868251843Sbapt options.connections_period, myaddr, 869251843Sbapt get_sock_port(newsock, 1), ntop); 870251843Sbapt free((void *)myaddr); 871251843Sbapt close(newsock); 872251843Sbapt ratelim_exceeded = 0; 873251843Sbapt continue; 874251843Sbapt } else { 875251843Sbapt /* 876251843Sbapt * Normal production daemon. Fork, and have 877251843Sbapt * the child process the connection. The 878251843Sbapt * parent continues listening. 879251843Sbapt */ 880251843Sbapt if ((pid = fork()) == 0) { 881251843Sbapt /* 882251843Sbapt * Child. Close the listening socket, and start using the 883251843Sbapt * accepted socket. Reinitialize logging (since our pid has 884251843Sbapt * changed). We break out of the loop to handle the connection. 885251843Sbapt */ 886251843Sbapt close_listen_socks(); 887251843Sbapt sock_in = newsock; 888251843Sbapt sock_out = newsock; 889251843Sbapt log_init(av0, options.log_level, options.log_facility, log_stderr); 890251843Sbapt break; 891251843Sbapt } 892251843Sbapt } 893251843Sbapt 894251843Sbapt /* Parent. Stay in the loop. */ 895251843Sbapt if (pid < 0) 896251843Sbapt error("fork: %.100s", strerror(errno)); 897251843Sbapt else 898251843Sbapt debug("Forked child %d.", pid); 899251843Sbapt 900251843Sbapt /* Mark that the key has been used (it was "given" to the child). */ 901251843Sbapt key_used = 1; 902251843Sbapt 903251843Sbapt arc4random_stir(); 904251843Sbapt 905251843Sbapt /* Close the new socket (the child is now taking care of it). */ 906251843Sbapt close(newsock); 907251843Sbapt } /* for (i = 0; i < num_listen_socks; i++) */ 908251843Sbapt /* child process check (or debug mode) */ 909251843Sbapt if (num_listen_socks < 0) 910251843Sbapt break; 911251843Sbapt } 912251843Sbapt } 913251843Sbapt 914217309Snwhitehorn /* This is the child processing a new connection. */ 915217309Snwhitehorn 916217309Snwhitehorn /* 917217309Snwhitehorn * Disable the key regeneration alarm. We will not regenerate the 918217309Snwhitehorn * key since we are no longer in a position to give it to anyone. We 919217309Snwhitehorn * will not restart on SIGHUP since it no longer makes sense. 920217309Snwhitehorn */ 921217309Snwhitehorn alarm(0); 922217309Snwhitehorn signal(SIGALRM, SIG_DFL); 923217309Snwhitehorn signal(SIGHUP, SIG_DFL); 924217309Snwhitehorn signal(SIGTERM, SIG_DFL); 925217309Snwhitehorn signal(SIGQUIT, SIG_DFL); 926217309Snwhitehorn signal(SIGCHLD, SIG_DFL); 927217309Snwhitehorn 928217309Snwhitehorn /* 929217309Snwhitehorn * Set socket options for the connection. We want the socket to 930217309Snwhitehorn * close as fast as possible without waiting for anything. If the 931217309Snwhitehorn * connection is not a socket, these will do nothing. 932217309Snwhitehorn */ 933217309Snwhitehorn /* setsockopt(sock_in, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); */ 934217309Snwhitehorn linger.l_onoff = 1; 935217309Snwhitehorn linger.l_linger = 5; 936217309Snwhitehorn setsockopt(sock_in, SOL_SOCKET, SO_LINGER, (void *) &linger, sizeof(linger)); 937217309Snwhitehorn 938217309Snwhitehorn /* 939217309Snwhitehorn * Register our connection. This turns encryption off because we do 940217309Snwhitehorn * not have a key. 941217309Snwhitehorn */ 942217309Snwhitehorn packet_set_connection(sock_in, sock_out); 943217309Snwhitehorn 944217309Snwhitehorn remote_port = get_remote_port(); 945217309Snwhitehorn remote_ip = get_remote_ipaddr(); 946217309Snwhitehorn 947217309Snwhitehorn /* Check whether logins are denied from this host. */ 948217309Snwhitehorn#ifdef LIBWRAP 949217309Snwhitehorn { 950217309Snwhitehorn struct request_info req; 951217309Snwhitehorn 952217309Snwhitehorn request_init(&req, RQ_DAEMON, av0, RQ_FILE, sock_in, NULL); 953217309Snwhitehorn fromhost(&req); 954217309Snwhitehorn 955217309Snwhitehorn if (!hosts_access(&req)) { 956217309Snwhitehorn close(sock_in); 957217309Snwhitehorn close(sock_out); 958217309Snwhitehorn refuse(&req); 959217309Snwhitehorn } 960217309Snwhitehorn verbose("Connection from %.500s port %d", eval_client(&req), remote_port); 961217309Snwhitehorn } 962217309Snwhitehorn#endif /* LIBWRAP */ 963217309Snwhitehorn /* Log the connection. */ 964217309Snwhitehorn verbose("Connection from %.500s port %d", remote_ip, remote_port); 965217309Snwhitehorn 966217309Snwhitehorn /* 967217309Snwhitehorn * We don\'t want to listen forever unless the other side 968217309Snwhitehorn * successfully authenticates itself. So we set up an alarm which is 969217309Snwhitehorn * cleared after successful authentication. A limit of zero 970217309Snwhitehorn * indicates no limit. Note that we don\'t set the alarm in debugging 971217309Snwhitehorn * mode; it is just annoying to have the server exit just when you 972217309Snwhitehorn * are about to discover the bug. 973217309Snwhitehorn */ 974217309Snwhitehorn signal(SIGALRM, grace_alarm_handler); 975217309Snwhitehorn if (!debug_flag) 976217309Snwhitehorn alarm(options.login_grace_time); 977217309Snwhitehorn 978217309Snwhitehorn sshd_exchange_identification(sock_in, sock_out); 979217309Snwhitehorn /* 980217309Snwhitehorn * Check that the connection comes from a privileged port. Rhosts- 981217309Snwhitehorn * and Rhosts-RSA-Authentication only make sense from priviledged 982217309Snwhitehorn * programs. Of course, if the intruder has root access on his local 983217309Snwhitehorn * machine, he can connect from any port. So do not use these 984217309Snwhitehorn * authentication methods from machines that you do not trust. 985217309Snwhitehorn */ 986217309Snwhitehorn if (remote_port >= IPPORT_RESERVED || 987217309Snwhitehorn remote_port < IPPORT_RESERVED / 2) { 988217309Snwhitehorn options.rhosts_authentication = 0; 989217309Snwhitehorn options.rhosts_rsa_authentication = 0; 990217309Snwhitehorn } 991217309Snwhitehorn#ifdef KRB4 992217309Snwhitehorn if (!packet_connection_is_ipv4() && 993217309Snwhitehorn options.krb4_authentication) { 994217309Snwhitehorn debug("Kerberos Authentication disabled, only available for IPv4."); 995217309Snwhitehorn options.krb4_authentication = 0; 996217309Snwhitehorn } 997217309Snwhitehorn#endif /* KRB4 */ 998217309Snwhitehorn 999217309Snwhitehorn packet_set_nonblocking(); 1000217309Snwhitehorn 1001224014Snwhitehorn /* perform the key exchange */ 1002220749Snwhitehorn /* authenticate user and start session */ 1003220749Snwhitehorn if (compat20) { 1004220749Snwhitehorn do_ssh2_kex(); 1005220749Snwhitehorn do_authentication2(); 1006220749Snwhitehorn } else { 1007220749Snwhitehorn do_ssh1_kex(); 1008220749Snwhitehorn do_authentication(); 1009220749Snwhitehorn } 1010220749Snwhitehorn 1011220749Snwhitehorn#ifdef KRB4 1012220749Snwhitehorn /* Cleanup user's ticket cache file. */ 1013220749Snwhitehorn if (options.krb4_ticket_cleanup) 1014220749Snwhitehorn (void) dest_tkt(); 1015220749Snwhitehorn#endif /* KRB4 */ 1016220749Snwhitehorn 1017220749Snwhitehorn /* The connection has been terminated. */ 1018220749Snwhitehorn verbose("Closing connection to %.100s", remote_ip); 1019220749Snwhitehorn packet_close(); 1020220749Snwhitehorn exit(0); 1021224014Snwhitehorn} 1022220749Snwhitehorn 1023217309Snwhitehorn/* 1024217309Snwhitehorn * SSH1 key exchange 1025220749Snwhitehorn */ 1026220749Snwhitehornvoid 1027220749Snwhitehorndo_ssh1_kex() 1028220749Snwhitehorn{ 1029220749Snwhitehorn int i, len; 1030220749Snwhitehorn int plen, slen; 1031220749Snwhitehorn BIGNUM *session_key_int; 1032220749Snwhitehorn unsigned char session_key[SSH_SESSION_KEY_LENGTH]; 1033220749Snwhitehorn unsigned char cookie[8]; 1034220749Snwhitehorn unsigned int cipher_type, auth_mask, protocol_flags; 1035220749Snwhitehorn u_int32_t rand = 0; 1036220749Snwhitehorn 1037220749Snwhitehorn /* 1038220749Snwhitehorn * Generate check bytes that the client must send back in the user 1039220749Snwhitehorn * packet in order for it to be accepted; this is used to defy ip 1040220749Snwhitehorn * spoofing attacks. Note that this only works against somebody 1041220749Snwhitehorn * doing IP spoofing from a remote machine; any machine on the local 1042220749Snwhitehorn * network can still see outgoing packets and catch the random 1043220749Snwhitehorn * cookie. This only affects rhosts authentication, and this is one 1044220749Snwhitehorn * of the reasons why it is inherently insecure. 1045220749Snwhitehorn */ 1046220749Snwhitehorn for (i = 0; i < 8; i++) { 1047220749Snwhitehorn if (i % 4 == 0) 1048220749Snwhitehorn rand = arc4random(); 1049220749Snwhitehorn cookie[i] = rand & 0xff; 1050220749Snwhitehorn rand >>= 8; 1051220749Snwhitehorn } 1052217309Snwhitehorn 1053217309Snwhitehorn /* 1054217309Snwhitehorn * Send our public key. We include in the packet 64 bits of random 1055217309Snwhitehorn * data that must be matched in the reply in order to prevent IP 1056217309Snwhitehorn * spoofing. 1057217309Snwhitehorn */ 1058217309Snwhitehorn packet_start(SSH_SMSG_PUBLIC_KEY); 1059217309Snwhitehorn for (i = 0; i < 8; i++) 1060217309Snwhitehorn packet_put_char(cookie[i]); 1061217309Snwhitehorn 1062217309Snwhitehorn /* Store our public server RSA key. */ 1063217309Snwhitehorn packet_put_int(BN_num_bits(public_key->n)); 1064217309Snwhitehorn packet_put_bignum(public_key->e); 1065217309Snwhitehorn packet_put_bignum(public_key->n); 1066217309Snwhitehorn 1067217309Snwhitehorn /* Store our public host RSA key. */ 1068217309Snwhitehorn packet_put_int(BN_num_bits(sensitive_data.host_key->n)); 1069217309Snwhitehorn packet_put_bignum(sensitive_data.host_key->e); 1070217309Snwhitehorn packet_put_bignum(sensitive_data.host_key->n); 1071217309Snwhitehorn 1072217309Snwhitehorn /* Put protocol flags. */ 1073217309Snwhitehorn packet_put_int(SSH_PROTOFLAG_HOST_IN_FWD_OPEN); 1074217309Snwhitehorn 1075217309Snwhitehorn /* Declare which ciphers we support. */ 1076217309Snwhitehorn packet_put_int(cipher_mask1()); 1077217309Snwhitehorn 1078217309Snwhitehorn /* Declare supported authentication types. */ 1079217309Snwhitehorn auth_mask = 0; 1080217309Snwhitehorn if (options.rhosts_authentication) 1081217309Snwhitehorn auth_mask |= 1 << SSH_AUTH_RHOSTS; 1082217309Snwhitehorn if (options.rhosts_rsa_authentication) 1083217309Snwhitehorn auth_mask |= 1 << SSH_AUTH_RHOSTS_RSA; 1084217309Snwhitehorn if (options.rsa_authentication) 1085217309Snwhitehorn auth_mask |= 1 << SSH_AUTH_RSA; 1086217309Snwhitehorn#ifdef KRB4 1087217309Snwhitehorn if (options.krb4_authentication) 1088217309Snwhitehorn auth_mask |= 1 << SSH_AUTH_KRB4; 1089217309Snwhitehorn#endif 1090217309Snwhitehorn#ifdef KRB5 1091217309Snwhitehorn if (options.krb5_authentication) { 1092217309Snwhitehorn auth_mask |= 1 << SSH_AUTH_KRB5; 1093217309Snwhitehorn /* compatibility with MetaCentre ssh */ 1094217309Snwhitehorn auth_mask |= 1 << SSH_AUTH_KRB4; 1095217309Snwhitehorn } 1096217309Snwhitehorn if (options.krb5_tgt_passing) 1097217309Snwhitehorn auth_mask |= 1 << SSH_PASS_KRB5_TGT; 1098217309Snwhitehorn#endif /* KRB5 */ 1099217309Snwhitehorn 1100217309Snwhitehorn#ifdef AFS 1101217309Snwhitehorn if (options.krb4_tgt_passing) 1102217309Snwhitehorn auth_mask |= 1 << SSH_PASS_KRB4_TGT; 1103217309Snwhitehorn if (options.afs_token_passing) 1104217309Snwhitehorn auth_mask |= 1 << SSH_PASS_AFS_TOKEN; 1105217309Snwhitehorn#endif 1106217309Snwhitehorn#ifdef SKEY 1107217309Snwhitehorn if (options.skey_authentication == 1) 1108217309Snwhitehorn auth_mask |= 1 << SSH_AUTH_TIS; 1109220749Snwhitehorn#endif 1110220749Snwhitehorn if (options.password_authentication) 1111217309Snwhitehorn auth_mask |= 1 << SSH_AUTH_PASSWORD; 1112217309Snwhitehorn packet_put_int(auth_mask); 1113217309Snwhitehorn 1114217309Snwhitehorn /* Send the packet and wait for it to be sent. */ 1115217309Snwhitehorn packet_send(); 1116217309Snwhitehorn packet_write_wait(); 1117217309Snwhitehorn 1118217309Snwhitehorn debug("Sent %d bit public key and %d bit host key.", 1119217309Snwhitehorn BN_num_bits(public_key->n), BN_num_bits(sensitive_data.host_key->n)); 1120217309Snwhitehorn 1121217309Snwhitehorn /* Read clients reply (cipher type and session key). */ 1122217309Snwhitehorn packet_read_expect(&plen, SSH_CMSG_SESSION_KEY); 1123217309Snwhitehorn 1124217309Snwhitehorn /* Get cipher type and check whether we accept this. */ 1125217309Snwhitehorn cipher_type = packet_get_char(); 1126217309Snwhitehorn 1127217309Snwhitehorn if (!(cipher_mask() & (1 << cipher_type))) 1128251843Sbapt packet_disconnect("Warning: client selects unsupported cipher."); 1129217309Snwhitehorn 1130217309Snwhitehorn /* Get check bytes from the packet. These must match those we 1131217309Snwhitehorn sent earlier with the public key packet. */ 1132217309Snwhitehorn for (i = 0; i < 8; i++) 1133251843Sbapt if (cookie[i] != packet_get_char()) 1134217309Snwhitehorn packet_disconnect("IP Spoofing check bytes do not match."); 1135251843Sbapt 1136217309Snwhitehorn debug("Encryption type: %.200s", cipher_name(cipher_type)); 1137217309Snwhitehorn 1138217309Snwhitehorn /* Get the encrypted integer. */ 1139217309Snwhitehorn session_key_int = BN_new(); 1140217309Snwhitehorn packet_get_bignum(session_key_int, &slen); 1141217309Snwhitehorn 1142217309Snwhitehorn protocol_flags = packet_get_int(); 1143217309Snwhitehorn packet_set_protocol_flags(protocol_flags); 1144217309Snwhitehorn 1145217309Snwhitehorn packet_integrity_check(plen, 1 + 8 + slen + 4, SSH_CMSG_SESSION_KEY); 1146217309Snwhitehorn 1147217309Snwhitehorn /* 1148217309Snwhitehorn * Decrypt it using our private server key and private host key (key 1149217309Snwhitehorn * with larger modulus first). 1150217309Snwhitehorn */ 1151217309Snwhitehorn if (BN_cmp(sensitive_data.private_key->n, sensitive_data.host_key->n) > 0) { 1152217309Snwhitehorn /* Private key has bigger modulus. */ 1153217309Snwhitehorn if (BN_num_bits(sensitive_data.private_key->n) < 1154217309Snwhitehorn BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED) { 1155217309Snwhitehorn fatal("do_connection: %s: private_key %d < host_key %d + SSH_KEY_BITS_RESERVED %d", 1156217309Snwhitehorn get_remote_ipaddr(), 1157217309Snwhitehorn BN_num_bits(sensitive_data.private_key->n), 1158217309Snwhitehorn BN_num_bits(sensitive_data.host_key->n), 1159217309Snwhitehorn SSH_KEY_BITS_RESERVED); 1160217309Snwhitehorn } 1161217309Snwhitehorn rsa_private_decrypt(session_key_int, session_key_int, 1162217309Snwhitehorn sensitive_data.private_key); 1163220749Snwhitehorn rsa_private_decrypt(session_key_int, session_key_int, 1164217309Snwhitehorn sensitive_data.host_key); 1165217309Snwhitehorn } else { 1166217309Snwhitehorn /* Host key has bigger modulus (or they are equal). */ 1167217309Snwhitehorn if (BN_num_bits(sensitive_data.host_key->n) < 1168217309Snwhitehorn BN_num_bits(sensitive_data.private_key->n) + SSH_KEY_BITS_RESERVED) { 1169217309Snwhitehorn fatal("do_connection: %s: host_key %d < private_key %d + SSH_KEY_BITS_RESERVED %d", 1170217309Snwhitehorn get_remote_ipaddr(), 1171217309Snwhitehorn BN_num_bits(sensitive_data.host_key->n), 1172217309Snwhitehorn BN_num_bits(sensitive_data.private_key->n), 1173217309Snwhitehorn SSH_KEY_BITS_RESERVED); 1174217309Snwhitehorn } 1175217309Snwhitehorn rsa_private_decrypt(session_key_int, session_key_int, 1176217309Snwhitehorn sensitive_data.host_key); 1177251843Sbapt rsa_private_decrypt(session_key_int, session_key_int, 1178251843Sbapt sensitive_data.private_key); 1179251843Sbapt } 1180251843Sbapt 1181251843Sbapt compute_session_id(session_id, cookie, 1182251843Sbapt sensitive_data.host_key->n, 1183251843Sbapt sensitive_data.private_key->n); 1184251843Sbapt 1185251843Sbapt /* Destroy the private and public keys. They will no longer be needed. */ 1186251843Sbapt destroy_sensitive_data(); 1187251843Sbapt 1188251843Sbapt /* 1189251843Sbapt * Extract session key from the decrypted integer. The key is in the 1190251843Sbapt * least significant 256 bits of the integer; the first byte of the 1191251843Sbapt * key is in the highest bits. 1192251843Sbapt */ 1193251843Sbapt BN_mask_bits(session_key_int, sizeof(session_key) * 8); 1194251843Sbapt len = BN_num_bytes(session_key_int); 1195251843Sbapt if (len < 0 || len > sizeof(session_key)) 1196251843Sbapt fatal("do_connection: bad len from %s: session_key_int %d > sizeof(session_key) %d", 1197251843Sbapt get_remote_ipaddr(), 1198251843Sbapt len, sizeof(session_key)); 1199251843Sbapt memset(session_key, 0, sizeof(session_key)); 1200251843Sbapt BN_bn2bin(session_key_int, session_key + sizeof(session_key) - len); 1201251843Sbapt 1202251843Sbapt /* Destroy the decrypted integer. It is no longer needed. */ 1203251843Sbapt BN_clear_free(session_key_int); 1204251843Sbapt 1205251843Sbapt /* Xor the first 16 bytes of the session key with the session id. */ 1206251843Sbapt for (i = 0; i < 16; i++) 1207251843Sbapt session_key[i] ^= session_id[i]; 1208251843Sbapt 1209251843Sbapt /* Set the session key. From this on all communications will be encrypted. */ 1210251843Sbapt packet_set_encryption_key(session_key, SSH_SESSION_KEY_LENGTH, cipher_type); 1211251843Sbapt 1212251843Sbapt /* Destroy our copy of the session key. It is no longer needed. */ 1213251843Sbapt memset(session_key, 0, sizeof(session_key)); 1214217309Snwhitehorn 1215217309Snwhitehorn debug("Received session key; encryption turned on."); 1216217309Snwhitehorn 1217217309Snwhitehorn /* Send an acknowledgement packet. Note that this packet is sent encrypted. */ 1218217309Snwhitehorn packet_start(SSH_SMSG_SUCCESS); 1219217309Snwhitehorn packet_send(); 1220217309Snwhitehorn packet_write_wait(); 1221217309Snwhitehorn} 1222217309Snwhitehorn 1223217309Snwhitehorn/* 1224217309Snwhitehorn * SSH2 key exchange: diffie-hellman-group1-sha1 1225217309Snwhitehorn */ 1226217309Snwhitehornvoid 1227217309Snwhitehorndo_ssh2_kex() 1228217309Snwhitehorn{ 1229217309Snwhitehorn Buffer *server_kexinit; 1230217309Snwhitehorn Buffer *client_kexinit; 1231217309Snwhitehorn int payload_len, dlen; 1232217309Snwhitehorn int slen; 1233217309Snwhitehorn unsigned int klen, kout; 1234217309Snwhitehorn unsigned char *signature = NULL; 1235217309Snwhitehorn unsigned char *server_host_key_blob = NULL; 1236217309Snwhitehorn unsigned int sbloblen; 1237217309Snwhitehorn DH *dh; 1238217309Snwhitehorn BIGNUM *dh_client_pub = 0; 1239217309Snwhitehorn BIGNUM *shared_secret = 0; 1240217309Snwhitehorn int i; 1241217309Snwhitehorn unsigned char *kbuf; 1242217309Snwhitehorn unsigned char *hash; 1243217309Snwhitehorn Kex *kex; 1244217309Snwhitehorn char *cprop[PROPOSAL_MAX]; 1245217309Snwhitehorn 1246217309Snwhitehorn/* KEXINIT */ 1247217309Snwhitehorn 1248217309Snwhitehorn if (options.ciphers != NULL) { 1249217309Snwhitehorn myproposal[PROPOSAL_ENC_ALGS_CTOS] = 1250217309Snwhitehorn myproposal[PROPOSAL_ENC_ALGS_STOC] = options.ciphers; 1251217309Snwhitehorn } 1252217309Snwhitehorn server_kexinit = kex_init(myproposal); 1253217309Snwhitehorn client_kexinit = xmalloc(sizeof(*client_kexinit)); 1254217309Snwhitehorn buffer_init(client_kexinit); 1255217309Snwhitehorn 1256217309Snwhitehorn /* algorithm negotiation */ 1257217309Snwhitehorn kex_exchange_kexinit(server_kexinit, client_kexinit, cprop); 1258217309Snwhitehorn kex = kex_choose_conf(cprop, myproposal, 1); 1259217309Snwhitehorn for (i = 0; i < PROPOSAL_MAX; i++) 1260224014Snwhitehorn xfree(cprop[i]); 1261224014Snwhitehorn 1262224014Snwhitehorn/* KEXDH */ 1263224014Snwhitehorn 1264224014Snwhitehorn debug("Wait SSH2_MSG_KEXDH_INIT."); 1265224014Snwhitehorn packet_read_expect(&payload_len, SSH2_MSG_KEXDH_INIT); 1266224014Snwhitehorn 1267224014Snwhitehorn /* key, cert */ 1268224014Snwhitehorn dh_client_pub = BN_new(); 1269217309Snwhitehorn if (dh_client_pub == NULL) 1270217309Snwhitehorn fatal("dh_client_pub == NULL"); 1271217309Snwhitehorn packet_get_bignum2(dh_client_pub, &dlen); 1272217309Snwhitehorn 1273217309Snwhitehorn#ifdef DEBUG_KEXDH 1274217309Snwhitehorn fprintf(stderr, "\ndh_client_pub= "); 1275217309Snwhitehorn bignum_print(dh_client_pub); 1276217309Snwhitehorn fprintf(stderr, "\n"); 1277251843Sbapt debug("bits %d", BN_num_bits(dh_client_pub)); 1278217309Snwhitehorn#endif 1279217309Snwhitehorn 1280217309Snwhitehorn /* generate DH key */ 1281217309Snwhitehorn dh = dh_new_group1(); /* XXX depends on 'kex' */ 1282217309Snwhitehorn 1283217309Snwhitehorn#ifdef DEBUG_KEXDH 1284217309Snwhitehorn fprintf(stderr, "\np= "); 1285217309Snwhitehorn bignum_print(dh->p); 1286217309Snwhitehorn fprintf(stderr, "\ng= "); 1287217309Snwhitehorn bignum_print(dh->g); 1288217309Snwhitehorn fprintf(stderr, "\npub= "); 1289217309Snwhitehorn bignum_print(dh->pub_key); 1290217309Snwhitehorn fprintf(stderr, "\n"); 1291217309Snwhitehorn#endif 1292217309Snwhitehorn if (!dh_pub_is_valid(dh, dh_client_pub)) 1293217309Snwhitehorn packet_disconnect("bad client public DH value"); 1294217309Snwhitehorn 1295217309Snwhitehorn klen = DH_size(dh); 1296217309Snwhitehorn kbuf = xmalloc(klen); 1297217309Snwhitehorn kout = DH_compute_key(kbuf, dh_client_pub, dh); 1298220749Snwhitehorn 1299220749Snwhitehorn#ifdef DEBUG_KEXDH 1300217309Snwhitehorn debug("shared secret: len %d/%d", klen, kout); 1301217309Snwhitehorn fprintf(stderr, "shared secret == "); 1302224014Snwhitehorn for (i = 0; i< kout; i++) 1303224014Snwhitehorn fprintf(stderr, "%02x", (kbuf[i])&0xff); 1304224014Snwhitehorn fprintf(stderr, "\n"); 1305217309Snwhitehorn#endif 1306217309Snwhitehorn shared_secret = BN_new(); 1307217309Snwhitehorn 1308217309Snwhitehorn BN_bin2bn(kbuf, kout, shared_secret); 1309217309Snwhitehorn memset(kbuf, 0, klen); 1310217309Snwhitehorn xfree(kbuf); 1311217309Snwhitehorn 1312217309Snwhitehorn /* XXX precompute? */ 1313217309Snwhitehorn dsa_make_key_blob(sensitive_data.dsa_host_key, &server_host_key_blob, &sbloblen); 1314217309Snwhitehorn 1315217309Snwhitehorn /* calc H */ /* XXX depends on 'kex' */ 1316217309Snwhitehorn hash = kex_hash( 1317220749Snwhitehorn client_version_string, 1318217309Snwhitehorn server_version_string, 1319217309Snwhitehorn buffer_ptr(client_kexinit), buffer_len(client_kexinit), 1320217309Snwhitehorn buffer_ptr(server_kexinit), buffer_len(server_kexinit), 1321217309Snwhitehorn (char *)server_host_key_blob, sbloblen, 1322217309Snwhitehorn dh_client_pub, 1323217309Snwhitehorn dh->pub_key, 1324217309Snwhitehorn shared_secret 1325217309Snwhitehorn ); 1326217309Snwhitehorn buffer_free(client_kexinit); 1327217309Snwhitehorn buffer_free(server_kexinit); 1328217309Snwhitehorn xfree(client_kexinit); 1329217309Snwhitehorn xfree(server_kexinit); 1330217309Snwhitehorn#ifdef DEBUG_KEXDH 1331217309Snwhitehorn fprintf(stderr, "hash == "); 1332217309Snwhitehorn for (i = 0; i< 20; i++) 1333217309Snwhitehorn fprintf(stderr, "%02x", (hash[i])&0xff); 1334217309Snwhitehorn fprintf(stderr, "\n"); 1335217309Snwhitehorn#endif 1336217309Snwhitehorn /* save session id := H */ 1337217309Snwhitehorn /* XXX hashlen depends on KEX */ 1338217309Snwhitehorn session_id2_len = 20; 1339217309Snwhitehorn session_id2 = xmalloc(session_id2_len); 1340217309Snwhitehorn memcpy(session_id2, hash, session_id2_len); 1341251843Sbapt 1342217309Snwhitehorn /* sign H */ 1343251843Sbapt /* XXX hashlen depends on KEX */ 1344251843Sbapt dsa_sign(sensitive_data.dsa_host_key, &signature, &slen, hash, 20); 1345251843Sbapt 1346251843Sbapt destroy_sensitive_data(); 1347251843Sbapt 1348251843Sbapt /* send server hostkey, DH pubkey 'f' and singed H */ 1349251843Sbapt packet_start(SSH2_MSG_KEXDH_REPLY); 1350251843Sbapt packet_put_string((char *)server_host_key_blob, sbloblen); 1351251843Sbapt packet_put_bignum2(dh->pub_key); /* f */ 1352251843Sbapt packet_put_string((char *)signature, slen); 1353251843Sbapt packet_send(); 1354251843Sbapt xfree(signature); 1355251843Sbapt xfree(server_host_key_blob); 1356251843Sbapt packet_write_wait(); 1357251843Sbapt 1358251843Sbapt kex_derive_keys(kex, hash, shared_secret); 1359251843Sbapt packet_set_kex(kex); 1360251843Sbapt 1361251843Sbapt /* have keys, free DH */ 1362251843Sbapt DH_free(dh); 1363251843Sbapt 1364251843Sbapt debug("send SSH2_MSG_NEWKEYS."); 1365251843Sbapt packet_start(SSH2_MSG_NEWKEYS); 1366251843Sbapt packet_send(); 1367251843Sbapt packet_write_wait(); 1368217309Snwhitehorn debug("done: send SSH2_MSG_NEWKEYS."); 1369217309Snwhitehorn 1370217309Snwhitehorn debug("Wait SSH2_MSG_NEWKEYS."); 1371217309Snwhitehorn packet_read_expect(&payload_len, SSH2_MSG_NEWKEYS); 1372217309Snwhitehorn debug("GOT SSH2_MSG_NEWKEYS."); 1373217309Snwhitehorn 1374217309Snwhitehorn#ifdef DEBUG_KEXDH 1375217309Snwhitehorn /* send 1st encrypted/maced/compressed message */ 1376217309Snwhitehorn packet_start(SSH2_MSG_IGNORE); 1377251843Sbapt packet_put_cstring("markus"); 1378251843Sbapt packet_send(); 1379217309Snwhitehorn packet_write_wait(); 1380251843Sbapt#endif 1381217309Snwhitehorn debug("done: KEX2."); 1382217309Snwhitehorn} 1383217309Snwhitehorn