]> andersk Git - openssh.git/blobdiff - sshd.c
- millert@cvs.openbsd.org 2002/05/13 15:53:19
[openssh.git] / sshd.c
diff --git a/sshd.c b/sshd.c
index cbe31608728d626c3dc03ac9b912af18f7596176..0bd64477784e58fbba50c32db9fcc6cdabce52af 100644 (file)
--- a/sshd.c
+++ b/sshd.c
  * 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
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: sshd.c,v 1.228 2002/02/27 21:23:13 stevesk Exp $");
+RCSID("$OpenBSD: sshd.c,v 1.241 2002/05/13 15:53:19 millert Exp $");
 
 #include <openssl/dh.h>
 #include <openssl/bn.h>
 #include <openssl/md5.h>
+#include <openssl/rand.h>
+#ifdef HAVE_SECUREWARE
+#include <sys/security.h>
+#include <prot.h>
+#endif
 
 #include "ssh.h"
 #include "ssh1.h"
@@ -195,15 +202,8 @@ int *startup_pipes = NULL;
 int startup_pipe;              /* in child */
 
 /* variables used for privilege separation */
-#define MM_MEMSIZE     65536
-struct mm_master *mm_zback;
-struct mm_master *mm_zlib;
-
+extern struct monitor *monitor;
 extern int use_privsep;
-/* Socket for the child to receive a fd */
-extern int mm_recvfd;
-/* Socket for the parent to send a fd */
-int mm_sendfd;
 
 /* Prototypes for various functions defined later in this file. */
 void destroy_sensitive_data(void);
@@ -280,10 +280,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);
@@ -506,57 +508,148 @@ demote_sensitive_data(void)
                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? */
 }
 
-void
-privsep_postauth(Authctxt *authctxt)
+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;
 
-       if (0) {
-               /* File descriptor passing is broken */
-               mm_apply_keystate(mm_zlib);
+       /* Set up unprivileged child process to deal with network data */
+       monitor = monitor_init();
+       /* Store a pointer to the kex for later rekeying */
+       monitor->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 %d", pid);
+
+               close(monitor->m_recvfd);
+               authctxt = monitor_child_preauth(monitor);
+               close(monitor->m_sendfd);
+
+               /* Sync memory */
+               monitor_sync(monitor);
+
+               /* Wait for the child's exit status */
+               while (waitpid(pid, &status, 0) < 0)
+                       if (errno != EINTR)
+                               break;
+               return (authctxt);
+       } else {
+               /* child */
+
+               close(monitor->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(monitor);
                use_privsep = 0;
                return;
        }
 
-       pid = fork();
-       if (pid == -1)
+       /* Authentication complete */
+       alarm(0);
+       if (startup_pipe != -1) {
+               close(startup_pipe);
+               startup_pipe = -1;
+       }
+
+       /* New socket pair */
+       monitor_reinit(monitor);
+
+       monitor->m_pid = fork();
+       if (monitor->m_pid == -1)
                fatal("fork of unprivileged child failed");
-       else if (pid != 0) {
-               debug2("User child is on pid %d", pid);
-               close(mm_recvfd);
-               monitor_child_postauth(mm_sendfd);
+       else if (monitor->m_pid != 0) {
+               debug2("User child is on pid %d", monitor->m_pid);
+               close(monitor->m_recvfd);
+               monitor_child_postauth(monitor);
 
-               /* Teardown? */
+               /* NEVERREACHED */
                exit(0);
        }
 
-       close(mm_sendfd);
+       close(monitor->m_sendfd);
 
        /* Demote the private keys to public keys. */
        demote_sensitive_data();
 
        /* Drop privileges */
-       if (seteuid(authctxt->pw->pw_uid) == -1)
-               fatal("%s: seteuid", __FUNCTION__);
-       if (setuid(authctxt->pw->pw_uid) == -1)
-               fatal("%s: setuid", __FUNCTION__);
+       do_setusercontext(authctxt->pw);
 
        /* It is safe now to apply the key state */
-       mm_apply_keystate(mm_zlib);
+       monitor_apply_keystate(monitor);
 }
 
-
 static char *
 list_hostkey_types(void)
 {
@@ -586,7 +679,7 @@ list_hostkey_types(void)
        return p;
 }
 
-static Key *
+Key *
 get_hostkey_by_type(int type)
 {
        int i;
@@ -694,10 +787,12 @@ main(int ac, char **av)
        int startup_p[2];
        int startups = 0;
        Authctxt *authctxt;
-       int sp[2];
        Key *key;
        int ret, key_used = 0;
 
+#ifdef HAVE_SECUREWARE
+       (void)set_auth_parameters(ac, av);
+#endif
        __progname = get_progname(av[0]);
        init_rng();
 
@@ -910,9 +1005,15 @@ main(int ac, char **av)
        if (test_flag)
                exit(0);
 
-#ifdef HAVE_SCO_PROTECTED_PW
-       (void) set_auth_parameters(ac, av);
-#endif
+       /*
+        * Clear out any supplemental groups we may have inherited.  This
+        * prevents inadvertent creation of files with bad modes (in the
+        * portable version at least, it's certainly possible for PAM 
+        * to create a file, and we can't control the code in every 
+        * module which might be used).
+        */
+       if (setgroups(0, NULL) < 0)
+               debug("setgroups() failed: %.200s", strerror(errno));
 
        /* Initialize the log (it is reinitialized below in case we forked). */
        if (debug_flag && !inetd_flag)
@@ -1230,6 +1331,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
@@ -1332,75 +1441,46 @@ main(int ac, char **av)
 
        packet_set_nonblocking();
 
-       if (!use_privsep)
-               goto skip_privilegeseparation;
-               
-       /* Set up unprivileged child process to deal with network data */
-       monitor_socketpair(sp);
-       mm_recvfd = sp[0];
-       mm_sendfd = sp[1];
-
-       /* Used to share zlib space across processes */
-       mm_zback = mm_create(NULL, MM_MEMSIZE);
-       mm_zlib = mm_create(mm_zback, 20 * MM_MEMSIZE);
-
-       /* Compression needs to share state across borders */
-       mm_init_compression(mm_zlib);
-
-       pid = fork();
-       if (pid == -1)
-               fatal("fork of unprivileged child failed");
-       else if (pid != 0) {
-               debug2("Network child is on pid %d", pid);
-               authctxt = monitor_child_preauth(mm_sendfd);
-
-               /* The member allocation is not visible, so sync it */
-               mm_share_sync(&mm_zlib, &mm_zback);
-               goto authenticated;
-       } else {
-               /* Demote the private keys to public keys. */
-               demote_sensitive_data();
-
-               /* Change our root directory - /var/empty is standard*/
-               if (chroot("/var/empty") == -1)
-                       fatal("chroot(/var/empty)");
-               if (chdir("/") == -1)
-                       fatal("chdir(/)");
-               
-               /* Drop our privileges */
-               seteuid(32767); /* XXX - Niels */
-               setuid(32767);
-       }               
-
- skip_privilegeseparation:
+       if (use_privsep)
+               if ((authctxt = privsep_preauth()) != NULL)
+                       goto authenticated;
 
        /* perform the key exchange */
        /* authenticate user and start session */
        if (compat20) {
                do_ssh2_kex();
                authctxt = do_authentication2();
-               if (use_privsep)
-                       mm_send_keystate(mm_recvfd);
        } else {
                do_ssh1_kex();
                authctxt = do_authentication();
        }
-
-       /* If we use privilege separation, the unprivileged child exits */
-       if (use_privsep)
+       /*
+        * If we use privilege separation, the unprivileged child transfers
+        * the current keystate and exits
+        */
+       if (use_privsep) {
+               mm_send_keystate(monitor);
                exit(0);
+       }
 
  authenticated:
-       /* 
+       /*
         * In privilege separation, we fork another child and prepare
         * file descriptor passing.
         */
-       if (use_privsep)
+       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);
+
 #ifdef USE_PAM
        finish_pam();
 #endif /* USE_PAM */
@@ -1408,11 +1488,55 @@ main(int ac, char **av)
        packet_close();
 
        if (use_privsep)
-               mm_terminate(mm_recvfd);
+               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
  */
@@ -1528,43 +1652,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
@@ -1615,6 +1705,11 @@ 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. 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);
This page took 0.052397 seconds and 4 git commands to generate.