diff options
author | des <des@FreeBSD.org> | 2002-06-23 14:01:54 +0000 |
---|---|---|
committer | des <des@FreeBSD.org> | 2002-06-23 14:01:54 +0000 |
commit | 610201f50fdb0594e9885594b69e4ee69c71dd08 (patch) | |
tree | b7e89b45c0327694bc87ec94464fd0a685e1ef1d /crypto/openssh/sshd.c | |
parent | f58c4e7f5e1985bae7ed77fb0e8b2766eb4824e9 (diff) | |
download | FreeBSD-src-610201f50fdb0594e9885594b69e4ee69c71dd08.zip FreeBSD-src-610201f50fdb0594e9885594b69e4ee69c71dd08.tar.gz |
Vendor import of OpenSSH 3.3.
Diffstat (limited to 'crypto/openssh/sshd.c')
-rw-r--r-- | crypto/openssh/sshd.c | 358 |
1 files changed, 309 insertions, 49 deletions
diff --git a/crypto/openssh/sshd.c b/crypto/openssh/sshd.c index eca004f..0f04704 100644 --- a/crypto/openssh/sshd.c +++ b/crypto/openssh/sshd.c @@ -15,8 +15,10 @@ * called by a name other than "ssh" or "Secure Shell". * * SSH2 implementation: + * Privilege Separation: * - * Copyright (c) 2000 Markus Friedl. All rights reserved. + * Copyright (c) 2000, 2001, 2002 Markus Friedl. All rights reserved. + * Copyright (c) 2002 Niels Provos. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -40,11 +42,12 @@ */ #include "includes.h" -RCSID("$OpenBSD: sshd.c,v 1.228 2002/02/27 21:23:13 stevesk Exp $"); +RCSID("$OpenBSD: sshd.c,v 1.246 2002/06/20 23:05:56 markus Exp $"); #include <openssl/dh.h> #include <openssl/bn.h> #include <openssl/md5.h> +#include <openssl/rand.h> #include "ssh.h" #include "ssh1.h" @@ -72,6 +75,11 @@ RCSID("$OpenBSD: sshd.c,v 1.228 2002/02/27 21:23:13 stevesk Exp $"); #include "misc.h" #include "dispatch.h" #include "channels.h" +#include "session.h" +#include "monitor_mm.h" +#include "monitor.h" +#include "monitor_wrap.h" +#include "monitor_fdpass.h" #ifdef LIBWRAP #include <tcpd.h> @@ -180,8 +188,13 @@ u_int utmp_len = MAXHOSTNAMELEN; 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. */ void destroy_sensitive_data(void); +void demote_sensitive_data(void); static void do_ssh1_kex(void); static void do_ssh2_kex(void); @@ -254,10 +267,12 @@ sigterm_handler(int sig) static void main_sigchld_handler(int sig) { + pid_t pid; int save_errno = errno; int status; - while (waitpid(-1, &status, WNOHANG) > 0) + while ((pid = waitpid(-1, &status, WNOHANG)) > 0 || + (pid < 0 && errno == EINTR)) ; signal(SIGCHLD, main_sigchld_handler); @@ -350,7 +365,7 @@ sshd_exchange_identification(int sock_in, int sock_out) fatal_cleanup(); } - /* Read other side's version identification. */ + /* Read other sides version identification. */ memset(buf, 0, sizeof(buf)); for (i = 0; i < sizeof(buf) - 1; i++) { if (atomicio(read, sock_in, &buf[i], 1) != 1) { @@ -468,6 +483,160 @@ destroy_sensitive_data(void) 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 rand[256]; + int i; + struct passwd *pw; + + /* Enable challenge-response authentication for privilege separation */ + privsep_challenge_enable(); + + for (i = 0; i < 256; i++) + rand[i] = arc4random(); + RAND_seed(rand, sizeof(rand)); + + /* 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); + do_setusercontext(pw); +} + +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) { + 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; + 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 (authctxt->pw->pw_uid == 0 || options.use_login) { + /* 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) { + 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 * list_hostkey_types(void) { @@ -497,7 +666,7 @@ list_hostkey_types(void) return p; } -static Key * +Key * get_hostkey_by_type(int type) { int i; @@ -509,6 +678,25 @@ get_hostkey_by_type(int type) 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. * dropping starts at connection #max_startups_begin with a probability @@ -585,6 +773,7 @@ main(int ac, char **av) int listen_sock, maxfd; int startup_p[2]; int startups = 0; + Authctxt *authctxt; Key *key; int ret, key_used = 0; @@ -783,6 +972,19 @@ main(int ac, char **av) } } + 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); + } + /* Configuration looks good, so exit if in test mode. */ if (test_flag) exit(0); @@ -929,7 +1131,7 @@ main(int ac, char **av) */ f = fopen(options.pid_file, "w"); if (f) { - fprintf(f, "%u\n", (u_int) getpid()); + fprintf(f, "%ld\n", (long) getpid()); fclose(f); } } @@ -1076,7 +1278,7 @@ main(int ac, char **av) if (pid < 0) error("fork: %.100s", strerror(errno)); else - debug("Forked child %d.", pid); + debug("Forked child %ld.", (long)pid); close(startup_p[1]); @@ -1103,6 +1305,14 @@ main(int ac, char **av) /* 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 (setsid() < 0) + error("setsid: %.100s", strerror(errno)); + + /* * 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 * will not restart on SIGHUP since it no longer makes sense. @@ -1174,7 +1384,7 @@ main(int ac, char **av) sshd_exchange_identification(sock_in, sock_out); /* * Check that the connection comes from a privileged port. - * Rhosts-Authentication only makes sense from priviledged + * Rhosts-Authentication only makes sense from privileged * programs. Of course, if the intruder has root access on his local * machine, he can connect from any port. So do not use these * authentication methods from machines that you do not trust. @@ -1203,22 +1413,98 @@ main(int ac, char **av) packet_set_nonblocking(); + if (use_privsep) + if ((authctxt = privsep_preauth()) != NULL) + goto authenticated; + /* perform the key exchange */ /* authenticate user and start session */ if (compat20) { do_ssh2_kex(); - do_authentication2(); + authctxt = do_authentication2(); } else { do_ssh1_kex(); - do_authentication(); + authctxt = 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. */ verbose("Closing connection to %.100s", remote_ip); packet_close(); + + if (use_privsep) + mm_terminate(); + 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 */ static void @@ -1333,43 +1619,9 @@ do_ssh1_kex(void) packet_set_protocol_flags(protocol_flags); packet_check_eom(); - /* - * 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++; - } + /* Decrypt session_key_int using host/server keys */ + rsafail = PRIVSEP(ssh1_session_key(session_key_int)); + /* * Extract session key from the decrypted integer. The key is in the * least significant 256 bits of the integer; the first byte of the @@ -1420,9 +1672,12 @@ do_ssh1_kex(void) for (i = 0; i < 16; i++) session_id[i] = session_key[i] ^ session_key[i + 16]; } - /* Destroy the private and public keys. They will no longer be needed. */ + /* Destroy the private and public keys. No longer. */ destroy_sensitive_data(); + if (use_privsep) + mm_ssh1_session_id(session_id); + /* Destroy the decrypted integer. It is no longer needed. */ BN_clear_free(session_key_int); @@ -1434,7 +1689,7 @@ do_ssh1_kex(void) debug("Received session key; encryption turned on."); - /* Send an acknowledgement packet. Note that this packet is sent encrypted. */ + /* Send an acknowledgment packet. Note that this packet is sent encrypted. */ packet_start(SSH_SMSG_SUCCESS); packet_send(); packet_write_wait(); @@ -1461,6 +1716,10 @@ do_ssh2_kex(void) myproposal[PROPOSAL_MAC_ALGS_CTOS] = 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(); /* start key exchange */ @@ -1469,6 +1728,7 @@ do_ssh2_kex(void) kex->client_version_string=client_version_string; kex->server_version_string=server_version_string; kex->load_host_key=&get_hostkey_by_type; + kex->host_key_index=&get_hostkey_index; xxx_kex = kex; |