diff --git a/sshd.c b/sshd.c index fab0d98..aa8a8bf 100644 --- a/sshd.c +++ b/sshd.c @@ -592,10 +592,79 @@ privsep_preauth(struct ssh *ssh) /* Arrange for logging to be sent to the monitor */ set_log_handler(mm_log_handler, pmonitor); +#ifdef __APPLE_SANDBOX_NAMED_EXTERNAL__ + /* + * ssh_sandbox_child() has the side-effect of disabling opening + * new files. This is a security precaution to prevent the + * child process from leaking data or opening new sockets, but + * clashes with newer OpenSSL implementations. + * + * Generally, OpenSSL wants to read new entropy from the system + * for each reseeding operation (and, by extension, through any + * operation that might trigger an internal reseeding, like + * requesting random bytes). + * + * The current OpenSSL port only enables the default set of + * system entropy - which means reading in data from crypto + * devices like /dev/{,u,s}random and /dev/hwrng. + * + * To speed things up, OpenSSL tries to open file descriptors + * to the listed devices and caches the result, i.e., the open + * file descriptor. Those are normally kept open UNLESS a + * reading error occurred OR no random bytes were returned. + * + * In a quite scary move, OpenSSL versions prior to 1.1.1 + * didn't fail when getting system entropy wasn't successful + * and also added some "pseudo-random" data like the PID, + * user id and current time to the entropy pool, which was + * often enough to seed the PRNG. + * + * More recent versions have a rewritten PRNG/DRBG core and, + * crucially, stricter rules when it comes to acquiring system + * entropy - this is now strictly required and no other data + * is mixed into the pool. + * + * OpenSSH generally tries (or intends) to leave crypto devices + * (which should one of the earliest open devices) alone and + * not close their FD on re-exec, but that doesn't seem to + * work. Although OpenSSL is initialized very early in the + * main() call chain, which SHOULD lead to open file + * descriptors to crypto devices, on a typical OS X/macOS + * system, /dev/urandom is opened as FD 6, which is above any + * FD that would be preserved after a re-exec operation. + * + * This leads to the child process having no open file + * descriptors to /dev/urandom, activating the sandbox, + * setting the number of open files to zero and subsequently + * effectively breaking OpenSSL 1.1.1+. + * + * We'll work around that by reseeding the PRNGs before + * enabling the sandbox, which has the side-effect of opening + * a file descriptor to /dev/urandom and keeping it open. + * + * There is a slight catch: errors in reading from the FD or a + * read count of zero (i.e., the device not returning any data) + * will lead to the FD being closed again without a way to be + * re-opened. + * + * We can take this risk, as this should realistically not + * happen. Even if it does, that only means that the child + * process will fail to read random data and hence terminate + * with an error - showing the same symptoms the workaround + * is intended to fix, but nothing worse. + */ + reseed_prngs(); + + /* We need to do this before we chroot() so we can read sshd.sb and libsandbox.dylib */ + if (box != NULL) + ssh_sandbox_child(box); +#endif privsep_preauth_child(); setproctitle("%s", "[net]"); +#ifndef __APPLE_SANDBOX_NAMED_EXTERNAL__ if (box != NULL) ssh_sandbox_child(box); +#endif return 0; }