Copyright 2008 Sun Microsystems, Inc. All rights reserved. Use is subject to license terms. Sun's Alternative "Privilege Separation" for OpenSSH Table of Contents 1. Introduction 2. What is "Privilege?" 3. Analysis of the SSH Protocols 3.1. Privileged Resources, Operations, in the SSH Protocols 4. OpenSSH's Privilege Separation 5. SUNWssh's Alternative Privilege Separation 6. Comparison of the OpenSSH and SUNWssh PrivSep Models 7. Future Directions 8. Guide to the AltPrivSep Source Code A. References 1. Introduction Implementations of SSH servers require some degree of privilege in order to function properly. Often such implementations retain such privilege throughout normal operation even while users are logged in. This means that vulnerabilities in the implementation of the protocols can be exploited in such ways as to escalate the privilege that would normally be accorded to mer-mortal users. The OpenSSH team introduced support for "privilege separation" in the OpenSSH ssh server some years ago to minimize the extent of extant, undiscovered vulnerabilities in the OpenSSH server source code. The basic concept is to have a multi-process server implementation where one process, the "monitor" is privileged and implements a smaller protocol than the ssh protocols, and thus is, hopefully, less likely to sport exploitable security bugs. The ssh team at Sun agrees with the basic OpenSSH privilege separation concept, but disagrees with its design. Here we present our alternative to the OpenSSH design. We begin with the question of just what is "privilege" and follow on with an analysis of the SSH protocols vis-a-vis privilege. Then we briefly describe the OpenSSH model, followed by an exposition of our alternative model. 2. What is "Privilege?" Privilege, in a traditional Unix sense, is that which the "root" user can do that other users cannot directly do. In Solaris 10 there is a new approach to this sort of privilege with the aim of running much of the operating system with the Least Privilege required; root's privilege is broken down into many privileges and these are managed through privilege sets. We won't go into the details of Solaris 10's Least Privilege facility here. But privilege is also access to data and resources that can be used to escalate the privilege of those who have access to them. For example: secret, or private cryptographic keys used in authentication. Network security typically requires the use of cryptographic keys for authentication. 3. Analysis of the SSH Protocols There are two or, rather three SSH protocols: - version 1 - version 1.5 - version 2 Version 1 and 1.5 are much the same, from our point of view; version 2 is significantly different from the other two. Familiarity by the reader with the specifications for these protocols is not assumed, but would be beneficial to the reader. Quite roughly, these protocols consist of the following: a) initial version exchange (for protocol version negotiation) b) a binary encoding of message data c) message syntaxes for the protocols' messages d) specifications on use of cryptography for transport privacy (encryption) and integrity protection e) a key exchange protocol (which also authenticates servers to clients) f) a protocol for user authentication g) a session protocol h) a re-keying protocol (v2-only) Some of these parts of the ssh protocols are quite complex, some quite straightforward. Altogether implementation of the ssh protocols requires a source code base of significant size. The OpenSSH implementation relies on OpenSSL for cryptographic service, on libz for compression service and miscellaneous other libraries. Besides these OpenSSH consists of several tens of thousands of lines of source code in C. SUNWssh is based on OpenSSH, so it is comparable in size and complexity to OpenSSH. There is, then, plenty of space for security bugs in the OpenSSH, and, therefore, also in the SUNWssh source code bases. The OpenSSH team designed and implemented a "privilege separation" feature in their ssh server to reduce the risk that a security bug in OpenSSH could be successfully exploited and an attacker's privilege escalated. 3.1. Privileged Resources, Operations, in the SSH Protocols What privileges does an SSH server need then? Observation with Solaris 10's ppriv(1) and truss(1) commands as well as analysis of the ssh protocols leads to conclude as follows. No privilege or privileged resources are needed to implement the parts (a)-(d) mentioned in section 3. For key exchange and server authentication (e) an ssh server requires: - Access to the host's ssh private keys. - Access to the host's GSS-API acceptor credentials. [SSHv2-only] An ssh server requires practically all privileges for user authentication (f) (at least PAM does), particularly PRIV_PROC_SETID, for logging the user in. Post-authentication an ssh server requires the following privileges: - Those required for auditing a user's subsequent logout. That is, PRIV_PROC_AUDIT. - Those required for record keeping (i.e., utmpx/wtmpx logging). That is, either open file descriptor for those files or PRIV_FILE_DAC_WRITE or otherwise access to those files, perhaps through a special user id or group id which would be granted write access through the ACLs on those files. Since SSHv2 allows clients to open many channels with pseudo-terminals a server may need to open and close utmpx/wtmpx records multiple times in the lifetime of an SSHv2 connection. - Those required for accessing the host's ssh private keys for SSHv2 re-keying. [SSHv2-only] These keys can be (and are) loaded at server startup time, requiring PRIV_FILE_DAC_READ, or access through file ACLs, at that time, but not thence. - Those required for accessing the host's GSS-API acceptor credentials for SSHv2 re-keying. These credentials may require a large set of privileges. The Solaris 10 Kerberos V GSS-API mechanism, for example, requires PRIV_FILE_DAC_READ (for access to the system keytab) and PRIV_FILE_DAC_WRITE (for access to the Kerberos V replay cache). It is worth pointing out that because of a wrinkle in the specification of the SSHv2 protocol and various implementations, access to a host's ssh private keys can allow one not only to impersonate the host as a server (which is, in practice, difficult), but also to impersonate the host as a client (which is quite easy to do) using "hostbased" user authentication. It is entirely possible to have one-process server implementation that drops most privileges and access to privileged resources after user authentication succeeds. Such an implementation would make some privileges, such as PRIV_PROC_SETID, available to any attacker that successfully exploited a security bug in the ssh server. But such an implementation would also have to retain access to resources needed for authenticating the server, which, as described above, can be used to impersonate the server, in some cases with ease. 4. OpenSSH's Privilege Separation The OpenSSH privilege separation model is quite complex. It consists of a monitor, which retains all privileges and access to privileged resources, and two processes which run with much less privilege: one process running as a special user, "sshd," for hosting all phases of the SSH protocols up to and including authentication, and one process running as the actual user that logs in and which hosts all phases of the SSH protocols post-user- authentication. The monitor and its companion processes speak a private protocol over IPC. This protocol is intended to be smaller and simpler than the SSH wire protocols. In practice the OpenSSH monitor protocols relating to user authentication are neither smaller nor simpler than the SSH user authentication protocols; and though they are different they also transport much the same data, including RSA/DSA signatures, usernames, PAM conversations, and GSS-API context and MIC tokens. The key exchange protocols have been broken down into their essentials and the monitor serves only services such as signing server replies with private host keys. Note also that the OpenSSH monitor protocol uses the same encodings as the SSH protocols and uses the same implementation of those encodings. 5. SUNWssh's Alternative Privilege Separation The Sun Microsystems ssh team believes that the OpenSSH team has reached the point of diminishing returns in attempting to separate processing of the user authentication protocols and that the OpenSSH approach to privilege separation of the key exchange protocols has led to a situation in which the monitor acts as an oracle, willing to sign anything provided by the unprivileged processes that talk to it. The Sun ssh team proposes a somewhat different privilege separation implementation that shares with the OpenSSH model the goal of minimizing and simplifying the protocol spoken by the monitor, but little source code. We eschew any temptation to apply the privilege separation concept to the version negotiation, initial key exchange and user authentication phases of the ssh protocols (but see section 7). Instead we focus on separating processing of auditing, record keeping and re-keying from processing of the session protocols. We also wish to avoid creating any oracles in the monitor. This approach allows us to have a very simple monitor protocol. Our monitor protocol consists of the following operations: - record a new pseudo-terminal session - record the end of a pseudo-terminal session - process a re-key protocol messages - get keys negotiated during re-keying to the session process to it can use them Logout auditing is done when the session process dies and so does not require a monitor protocol message. By processing all re-key protocol messages in the monitor we prevent the creation of oracles in the monitor. This is so because the monitor signs only material which it has generated and over which an attacker would have little influence (through the attackers offered DH public key, for example). Odds and ends: - If the monitor receives SIGHUP, SIGTERM or SIGINT it will call fatal_cleanup(), and thence will forcibly shutdown(3SOCKET) the ssh connection socket, causing its child to exit, and audit a logout. - The monitor does not attempt to update utmpx/wtmpx independently of its child -- it depends on the child asking it to. - The child now is unable to chown() ptys back to root. That's Ok, other services on Solaris do the same and everything still works because of grantpt(3C). - The sshd server process (the one that will become a monitor) forks a child process before the key exchange starts. The reason for it is that if we forked after that we would end up using PKCS#11 sessions initialized in the monitor unless UseOpenSSLEngine was explicitly set to 'no'. Using any existing PKCS#11 sessions or object handles over fork is what the PKCS#11 standard explicitly prohibits. To solve that, we would have to rekey before fork and then newly initialize the engine in the child, together with the new crypto contexts initialized with the keys produced by the key re-exchange. And, that wouldn't help in situations where the client does not support rekeying which also includes the whole protocol version 1. The pre-fork solution is simpler and also much faster. So, the key exchange and authentication is fully done in the child server process while the monitor waits aside to read the authentication context that is needed for further operation. The child drops privileges after the authentication finishes. With the ssh client, the situation is slightly more complicated. Given the fact that the user can request to go to the background during the connection using the ~& sequence we must be prepared to rekey before forking, to reinitialize the engine in the child after that, and then set the new crypto contexts with the new keys. If the server we are communicating with does not support rekeying we will not use the engine at all. We expect this situation to be extremely rare and will not offer any workaround for that. This also includes the protocol version 1. However, this version is already considered obsolete and should not be used if possible. 6. Comparison of the OpenSSH and SUNWssh PrivSep Models The OpenSSH server involves three processes which we will term "pre-session," "session" and "monitor." The OpenSSH pre-session process implements: - the ssh version string exchange - the ssh message encoding/decoding - most of the initial key exchange protocols - transport protection - part of the user authentication protocols The OpenSSH session process implements: - the ssh message encoding/decoding - transport protection - most of the re-keying protocols - the session protocols The OpenSSH monitor process implements: - the ssh message encoding/decoding - parts of the key exchange and re-key protocols (primarily signing of server replies with host private keys) - most of the user authentication protocols, specifically: - evaluation of ~/.ssh/authorized_keys (for pubkey userauth) - evaluation of known hosts files (for hostbased userauth) - evaluation of .shosts/.rhosts files (for hostbased userauth) - verification of signatures w/ public keys (pubkey, hostbased) - PAM API calls, conversation function - GSS-API calls Note that any vulnerabilities in the parsing of authorized_keys, known hosts and .shosts/rhosts files are as exploitable in the monitor as in a server w/o privilege separation. Similarly for any vulnerabilities in PAM modules and GSS-API mechanisms. The SUNWssh server involves two processes which we will term "session" and "monitor." The SUNWssh monitor process implements: - the ssh version string exchange - the ssh message encoding/decoding - transport protection - all of the key exchange and re-key protocols - all of the user authentication protocols The SUNWssh session process implements: - the ssh message encoding/decoding - transport protection - the session protocols Obviously all of these processes also implement their side of the monitor protocols. The OpenSSH 3.5p1 monitor protocol, on Solaris, has approximately 20 monitor request and corresponding response messages. The SUNWssh monitor protocol has 5 monitor request and response messages; additionally, the monitor processes standard re-key messages (but note: the monitor and the session process IPC is completely unencrypted), which amounts to about 14 more messages altogether. Much of the OpenSSH monitor protocol is a variation of the on-the-wire ssh protocols, with some contents re-packaging. We believe this does not afford the monitor much additional, if any protection from attacks in the key exchange and user authentication protocols. The re-packaging that is done in the OpenSSH monitor protocol is risky business. By separating the act of signing some blob of data from computing that blob of data one can create an oracle; this is exactly what happened in the OpenSSH case. As you can see in the next section, the SUNWssh privilege separation could evolve somewhat in the OpenSSH direction by saving the monitor all transport protection work, but we cannot save the monitor much, if any work relating to authentication or key exchange. 7. Future Directions The SUNWssh server privilege separation implementation could stand several improvements. The first improvement would be to have a single system-wide monitor. This would reduce resource consumption. The work needed to implement such an enhancement is very similar to the work needed to produce an SSH API and library, and it is not trivial. If this is not done then at least dropping PRIV_PROC_SETID and instead setting the saved-set-user-id in the monitor to that of the logged in user would be nice. The second enhancement would be to add a "none" host key algorithm to SSHv2 and a corresponding option in SUNWssh to disallow re-keying with any other host key algorithm. This would allow customers to configure their server and monitor so that no re-key protocol messages need be processed by the monitor. A third enhancement would be to enhance the GSS-API mechanisms to require fewer privileges. In practice this means overhauling the Kerberos V mechanism's replay cache. This would allow the monitor to run with fewer privileges. Further, even without improving the Kerberos V mechanism's replay cache it should be possible to drop at least PRIV_PROC_FORK/EXEC/ SESSION. A fourth enhancement would to have the unprivileged process handle all transport protection and proxy to the monitor all key exchange and user authentication protocol messages. This is a variation on the OpenSSH model, but without the re-packaging of ssh message contents seen there. After authentication succeeds the monitor could either change the unprivileged process' credentials (as can be done with ppriv(1) or the unprivileged process would, as in OpenSSH, pass the session keys/IVs/keystate to the monitor which would then pass them to a new process, the session process, that would then run as the logged in user. 8. Guide to the AltPrivSep Source Code First, a brief introduction to the SUNWssh/OpenSSH source code. The source code is organized as follows: $SRC/cmd/ssh/etc/ | +-> config files $SRC/cmd/ssh/include/ | +-> header files (note: none are installed/shipped) $SRC/cmd/ssh/libopenbsd-compat/common/ | +-> misc. portability source code $SRC/cmd/ssh/libssh/common/ | +-> implementation of encoding, transport protection, various wrappers around cryptography, the key exchange and host authentication protocols, the session protocols, and misc. other code cipher.c mac.c compress.c packet.c | +-> transport protocol buffer.c bufaux.c | +-> encoding channels.c nchan.c | +-> session protocol kex.c kexdh.c kexgex.c | +-> key exchange/re-key code common to ssh and sshd kexdhs.c kexgexs.c kexgsss.c | +-> key exchange/re-key code (server only) kexdhc.c kexgexc.c kexgssc.c | +-> key exchange/re-key code (client only) dh.c rsa.c mpaux.c ssh-rsa.c ssh-dss.c ssh-gss.c | +-> crypto wrappers/utilities log.c | +-> logging, including debug logging, on stderr or syslog $SRC/cmd/ssh/ssh/ | +-> ssh(1) $SRC/cmd/ssh/sshd/ | +-> sshd(1M), including auditing, implementation of user authentication and the OpenSSH and SUNWssh monitors sshd.c | +-> main() auth*.c | +-> user authentication serverloop.c session.c | +-> session protocols bsmaudit.[ch] sshlogin.c loginrec.c | +-> auditing and record-keeping $SRC/cmd/ssh// | +-> scp, sftp, sftp-server, ssh-agent, ssh-add, ... The SUNWssh altprivsep adds two new source files: $SRC/cmd/ssh/include/altprivsep.h $SRC/cmd/ssh/sshd/altprivsep.c | +-> monitor start routine, altprivsep_packet_*() routines for communication with the monitor, routines to help with key exchanges, service procedures for the monitor, etc... and modifies the following: $SRC/cmd/ssh/include/config.h | +> adds cpp define "ALTPRIVSEP" $SRC/cmd/ssh/include/ssh2.h | +-> adds private message type "SSH2_PRIV_MSG_ALTPRIVSEP" (254) $SRC/cmd/ssh/include/packet.h | +-> adds prototypes for several simple utility functions, some of which are specifically meant to avoid having to link altprivsep.c into ssh(1) $SRC/cmd/ssh/libssh/common/kex.c $SRC/cmd/ssh/libssh/common/packet.c | +-> implements the hooks needed to proxy re-key messages to/from the monitor $SRC/cmd/ssh/sshd/Makefile | +-> adds altprivsep.o to list of objects linked into sshd(1M) $SRC/cmd/ssh/sshd/serverloop.c | +-> adds an event loop for the monitor modifies the usual event loops for SSHv2 $SRC/cmd/ssh/sshd/session.c | +-> modifies do_login() and session_pty_cleanup2() to call altprivsep_record_login/logout() instead of record_login/logout(). modifies do_exec_pty() so that the server waits for the call to altprivsep_record_login() in child process to complete before returning so that the server and the child processes do not compete for monitor IPC I/O. $SRC/cmd/ssh/include/log.h $SRC/cmd/ssh/libssh/common/log.c | +-> adds an internal interface, set_log_txt_prefix() so that the monitor's debug and log messages get prefixed with a string ("monitor ") that indicates they are from the monitor $SRC/cmd/ssh/sshd/sshd.c | +-> modifies the body of code that follows the user authentication phase of the ssh protocols so as to start the monitor and move the relevant code into the monitor or session processes as appropriate while dropping privileges and access to privileged resources in the session process The monitor uses the packet.h interfaces to communicate with the session process as though it were its ssh client peer, but always uses the "none" cipher, mac and compression algorithms and installs even handlers only for the relevant key exchange messages and the private monitor message used for the other monitor services. The monitor serves the following services: - APS_MSG_NEWKEYS_REQ -> used to obtain keys/IVs after re-keys - APS_MSG_RECORD_LOGIN -> used to update utmpx/wtmpx - APS_MSG_RECORD_LOGOUT -> used to update utmpx/wtmpx The session and monitor processes communicate over a pipe. All monitor IPC I/O from the session process is blocking (though the pipe is set to non-blocking I/O). The monitor protocol is entirely synchronous and relies on the re-key protocols being entirely synchronous also (which they are, unlike the session protocols). The kex.c and packet.c files are minimally modified, primarily to prevent the monitor from handling SSH_MSG_NEWKEYS messages as a normal ssh server should, instead letting the session process process SSH_MSG_NEWKEYS messages by requesting the new keys negotiated with client from the monitor. Note that for SSHv1 no on-the-wire messages are processed by the monitor after authentication. In fact, the monitor thinks it's running SSHv2, even if the on-the-wire protocol is v1. A. References The IETF SECSH Working Group: http://www.ietf.org/html.charters/secsh-charter.html The SSHv2 architecture, assigned numbers: http://www.ietf.org/internet-drafts/draft-ietf-secsh-architecture-16.txt http://www.ietf.org/internet-drafts/draft-ietf-secsh-assignednumbers-06.txt New cipher modes for SSHv2: http://www.ietf.org/internet-drafts/draft-ietf-secsh-newmodes-02.txt The SSHv2 "transport," including initial key exchange and re-key protocols, but excluding negotiable DH group size and GSS-API-based key exchange: http://www.ietf.org/internet-drafts/draft-ietf-secsh-transport-18.txt Additional key exchange protocols for SSHv2: http://www.ietf.org/internet-drafts/draft-ietf-secsh-gsskeyex-08.txt http://www.ietf.org/internet-drafts/draft-ietf-secsh-dh-group-exchange-04.txt Base user authentication spec for SSHv2 (includes none, password, pubkey and hostbased user authentication): http://www.ietf.org/internet-drafts/draft-ietf-secsh-userauth-21.txt SSHv2 user authentication using PAM-style prompting: http://www.ietf.org/internet-drafts/draft-ietf-secsh-auth-kbdinteract-06.txt SSHv2 user authentication using the GSS-API: http://www.ietf.org/internet-drafts/draft-ietf-secsh-gsskeyex-08.txt SSHv2 "session" protocol (i.e., the protocol used for pty sessions, port forwarding, agent forwarding, X display forwarding, etc...): http://www.ietf.org/internet-drafts/draft-ietf-secsh-connect-19.txt