22051483 Dynamically enabling FIPS mode in OpenSSH
authorTomas Kuthan <tomas.kuthan@oracle.com>
Fri, 22 Jan 2016 07:17:30 -0800
changeset 5310 a06a01eef195
parent 5309 aa644d83002b
child 5311 92a194d827f0
22051483 Dynamically enabling FIPS mode in OpenSSH
components/openssh/Makefile
components/openssh/patches/035-fips.patch
components/openssh/patches/036-fipsrandom.patch
components/openssh/patches/037-missing_or_misleading_error_messages.patch
components/openssh/patches/038-print_libcrypto_errors.patch
--- a/components/openssh/Makefile	Tue Jan 05 07:12:20 2016 -0800
+++ b/components/openssh/Makefile	Fri Jan 22 07:17:30 2016 -0800
@@ -61,6 +61,7 @@
 CFLAGS += -DWITHOUT_ED25519
 CFLAGS += -DPER_SESSION_XAUTHFILE
 CFLAGS += -DWITHOUT_CAST128
+CFLAGS += -DENABLE_OPENSSL_FIPS
 
 CONFIGURE_OPTIONS += CFLAGS="$(CFLAGS)" 
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openssh/patches/035-fips.patch	Fri Jan 22 07:17:30 2016 -0800
@@ -0,0 +1,726 @@
+#
+# Dynamically set FIPS mode, when underlying libcrypto is FIPS capable.
+# Limit ciphers and MACs in algorithm negotiation proposal.
+#
+# This patch is unlikely to be accepted upstream.
+#
+diff -pur old/cipher.c new/cipher.c
+--- old/cipher.c
++++ new/cipher.c
[email protected]@ -77,7 +77,34 @@ struct sshcipher {
+ #endif
+ };
+ 
++#ifdef ENABLE_OPENSSL_FIPS
++/* in FIPS mode limit ciphers to FIPS compliant only */
++#define	ciphers (ssh_FIPS_mode() ? ciphers_fips : ciphers_dflt)
++
++static const struct sshcipher ciphers_fips[] = {
++	{ "none",	SSH_CIPHER_NONE, 8, 0, 0, 0, 0, 0, EVP_enc_null },
++	{ "3des-cbc",	SSH_CIPHER_SSH2, 8, 24, 0, 0, 0, 1, EVP_des_ede3_cbc },
++	{ "aes128-cbc",	SSH_CIPHER_SSH2, 16, 16, 0, 0, 0, 1, EVP_aes_128_cbc },
++	{ "aes192-cbc",	SSH_CIPHER_SSH2, 16, 24, 0, 0, 0, 1, EVP_aes_192_cbc },
++	{ "aes256-cbc",	SSH_CIPHER_SSH2, 16, 32, 0, 0, 0, 1, EVP_aes_256_cbc },
++	{ "[email protected]",
++			SSH_CIPHER_SSH2, 16, 32, 0, 0, 0, 1, EVP_aes_256_cbc },
++	{ "aes128-ctr",	SSH_CIPHER_SSH2, 16, 16, 0, 0, 0, 0, EVP_aes_128_ctr },
++	{ "aes192-ctr",	SSH_CIPHER_SSH2, 16, 24, 0, 0, 0, 0, EVP_aes_192_ctr },
++	{ "aes256-ctr",	SSH_CIPHER_SSH2, 16, 32, 0, 0, 0, 0, EVP_aes_256_ctr },
++# ifdef OPENSSL_HAVE_EVPGCM
++	{ "[email protected]",
++			SSH_CIPHER_SSH2, 16, 16, 12, 16, 0, 0, EVP_aes_128_gcm },
++	{ "[email protected]",
++			SSH_CIPHER_SSH2, 16, 32, 12, 16, 0, 0, EVP_aes_256_gcm },
++# endif /* OPENSSL_HAVE_EVPGCM */
++	{ NULL,		SSH_CIPHER_INVALID, 0, 0, 0, 0, 0, 0, NULL }
++};
++
++static const struct sshcipher ciphers_dflt[] = {
++#else /* ENABLE_OPENSSL_FIPS */
+ static const struct sshcipher ciphers[] = {
++#endif /* ENABLE_OPENSSL_FIPS */
+ #ifdef WITH_SSH1
+ 	{ "des",	SSH_CIPHER_DES, 8, 8, 0, 0, 0, 1, EVP_des_cbc },
+ 	{ "3des",	SSH_CIPHER_3DES, 8, 16, 0, 0, 0, 1, evp_ssh1_3des },
+diff -pur old/digest-openssl.c new/digest-openssl.c
+--- old/digest-openssl.c
++++ new/digest-openssl.c
[email protected]@ -53,8 +53,22 @@ struct ssh_digest {
+ 	const EVP_MD *(*mdfunc)(void);
+ };
+ 
++#ifdef ENABLE_OPENSSL_FIPS
+ /* NB. Indexed directly by algorithm number */
++const struct ssh_digest digests_fips[] = {
++	{ SSH_DIGEST_MD5,	"",	 	16,	NULL },
++	{ SSH_DIGEST_RIPEMD160,	"",		20,	NULL },
++	{ SSH_DIGEST_SHA1,	"SHA1",	 	20,	EVP_sha1 },
++	{ SSH_DIGEST_SHA256,	"SHA256", 	32,	EVP_sha256 },
++	{ SSH_DIGEST_SHA384,	"SHA384",	48,	EVP_sha384 },
++	{ SSH_DIGEST_SHA512,	"SHA512", 	64,	EVP_sha512 },
++	{ -1,			NULL,		0,	NULL },
++};
++/* NB. Indexed directly by algorithm number */
++const struct ssh_digest digests_dflt[] = {
++#else /* ENABLE_OPENSSL_FIPS */
+ const struct ssh_digest digests[] = {
++#endif /* ENABLE_OPENSSL_FIPS */
+ 	{ SSH_DIGEST_MD5,	"MD5",	 	16,	EVP_md5 },
+ 	{ SSH_DIGEST_RIPEMD160,	"RIPEMD160",	20,	EVP_ripemd160 },
+ 	{ SSH_DIGEST_SHA1,	"SHA1",	 	20,	EVP_sha1 },
[email protected]@ -67,6 +81,9 @@ const struct ssh_digest digests[] = {
+ static const struct ssh_digest *
+ ssh_digest_by_alg(int alg)
+ {
++#ifdef ENABLE_OPENSSL_FIPS
++	struct ssh_digest *digests = ssh_FIPS_mode() ? digests_fips : digests_dflt;
++#endif
+ 	if (alg < 0 || alg >= SSH_DIGEST_MAX)
+ 		return NULL;
+ 	if (digests[alg].id != alg) /* sanity */
[email protected]@ -79,6 +96,9 @@ ssh_digest_by_alg(int alg)
+ int
+ ssh_digest_alg_by_name(const char *name)
+ {
++#ifdef ENABLE_OPENSSL_FIPS
++	struct ssh_digest *digests = ssh_FIPS_mode() ? digests_fips : digests_dflt;
++#endif
+ 	int alg;
+ 
+ 	for (alg = 0; digests[alg].id != -1; alg++) {
+diff -pur old/gss-genr.c new/gss-genr.c
+--- old/gss-genr.c
++++ new/gss-genr.c
[email protected]@ -100,6 +100,7 @@ ssh_gssapi_kex_mechs(gss_OID_set gss_sup
+ 	char deroid[2];
+ 	const EVP_MD *evp_md = EVP_md5();
+ 	EVP_MD_CTX md;
++	int fips_mode;
+ 
+ 	if (gss_enc2oid != NULL) {
+ 		for (i = 0; gss_enc2oid[i].encoded != NULL; i++)
[email protected]@ -112,6 +113,14 @@ ssh_gssapi_kex_mechs(gss_OID_set gss_sup
+ 
+ 	buffer_init(&buf);
+ 
++#ifdef ENABLE_OPENSSL_FIPS
++	fips_mode = ssh_FIPS_mode();
++	if (fips_mode) {
++		debug3("Temporarily unsetting FIPS mode to compute MD5 for "
++		    "GSS-API key exchange method names");
++		FIPS_mode_set(0);
++	}
++#endif
+ 	oidpos = 0;
+ 	for (i = 0; i < gss_supported->count; i++) {
+ 		if (gss_supported->elements[i].length < 128 &&
[email protected]@ -119,7 +128,6 @@ ssh_gssapi_kex_mechs(gss_OID_set gss_sup
+ 
+ 			deroid[0] = SSH_GSS_OIDTYPE;
+ 			deroid[1] = gss_supported->elements[i].length;
+-
+ 			EVP_DigestInit(&md, evp_md);
+ 			EVP_DigestUpdate(&md, deroid, 2);
+ 			EVP_DigestUpdate(&md,
[email protected]@ -151,6 +159,12 @@ ssh_gssapi_kex_mechs(gss_OID_set gss_sup
+ 			oidpos++;
+ 		}
+ 	}
++#ifdef ENABLE_OPENSSL_FIPS
++	if (fips_mode) {
++		ssh_FIPS_mode_set_if_capable();
++		ssh_FIPS_check_status();
++	}
++#endif
+ 	gss_enc2oid[oidpos].oid = NULL;
+ 	gss_enc2oid[oidpos].encoded = NULL;
+ 
+diff -pur old/mac.c new/mac.c
+--- old/mac.c
++++ new/mac.c
[email protected]@ -53,8 +53,33 @@ struct macalg {
+ 	int		len;		/* just for UMAC */
+ 	int		etm;		/* Encrypt-then-MAC */
+ };
++#ifdef ENABLE_OPENSSL_FIPS
++/* in FIPS mode limit macs to FIPS compliant only */
++#define	macs (ssh_FIPS_mode() ? macs_fips : macs_dflt)
+ 
++static const struct macalg macs_fips[] = {
++	/* Encrypt-and-MAC (encrypt-and-authenticate) variants */
++	{ "hmac-sha1",				SSH_DIGEST, SSH_DIGEST_SHA1, 0, 0, 0, 0 },
++	{ "hmac-sha1-96",			SSH_DIGEST, SSH_DIGEST_SHA1, 96, 0, 0, 0 },
++#ifdef HAVE_EVP_SHA256
++	{ "hmac-sha2-256",			SSH_DIGEST, SSH_DIGEST_SHA256, 0, 0, 0, 0 },
++	{ "hmac-sha2-512",			SSH_DIGEST, SSH_DIGEST_SHA512, 0, 0, 0, 0 },
++#endif
++	/* Encrypt-then-MAC variants */
++	{ "[email protected]",		SSH_DIGEST, SSH_DIGEST_SHA1, 0, 0, 0, 1 },
++	{ "[email protected]",	SSH_DIGEST, SSH_DIGEST_SHA1, 96, 0, 0, 1 },
++#ifdef HAVE_EVP_SHA256
++	{ "[email protected]",	SSH_DIGEST, SSH_DIGEST_SHA256, 0, 0, 0, 1 },
++	{ "[email protected]",	SSH_DIGEST, SSH_DIGEST_SHA512, 0, 0, 0, 1 },
++#endif
++
++	{ NULL,					0, 0, 0, 0, 0, 0 }
++};
++
++static const struct macalg macs_dflt[] = {
++#else /* ENABLE_OPENSSL_FIPS */
+ static const struct macalg macs[] = {
++#endif /* ENABLE_OPENSSL_FIPS */
+ 	/* Encrypt-and-MAC (encrypt-and-authenticate) variants */
+ 	{ "hmac-sha1",				SSH_DIGEST, SSH_DIGEST_SHA1, 0, 0, 0, 0 },
+ 	{ "hmac-sha1-96",			SSH_DIGEST, SSH_DIGEST_SHA1, 96, 0, 0, 0 },
+diff -pur old/misc.c new/misc.c
+--- old/misc.c
++++ new/misc.c
[email protected]@ -38,12 +38,15 @@
+ #include <string.h>
+ #include <time.h>
+ #include <unistd.h>
++#include <dlfcn.h>
+ 
+ #include <netinet/in.h>
+ #include <netinet/in_systm.h>
+ #include <netinet/ip.h>
+ #include <netinet/tcp.h>
+ 
++#include <openssl/crypto.h>
++
+ #include <ctype.h>
+ #include <errno.h>
+ #include <fcntl.h>
[email protected]@ -77,6 +80,60 @@ chop(char *s)
+ 
+ }
+ 
++#ifdef ENABLE_OPENSSL_FIPS
++/* is OpenSSL FIPS mode set? */
++int
++ssh_FIPS_mode()
++{
++	return FIPS_mode();
++}
++
++/* store FIPS_mode_set() err code */
++static unsigned long ssh_FIPS_err_code = 0;
++
++#define	MSGBUFSIZ	1024 /* equals log.c:MSGBUFSIZ */
++
++/*
++ * Check and display FIPS mode status. 
++ * Called after ssh_FIPS_mode_set_if_capable() and when logging facility is
++ * available.
++ * If FIPS_mode_failed for FIPS capable libcrypto, exits with 255 code.
++ */
++void 
++ssh_FIPS_check_status()
++{
++	char ebuf[MSGBUFSIZ];
++
++	if (dlsym(RTLD_DEFAULT, "FIPS_module_mode_set") != NULL) {
++		if (ssh_FIPS_mode()) {
++			debug("Running in FIPS mode.");
++		} else {
++			ERR_error_string_n(ssh_FIPS_err_code, ebuf,
++			    sizeof (ebuf));
++			fatal("Setting FIPS mode failed! %s", ebuf);
++		}
++	} else {
++		debug3("Loaded libcrypto is not FIPS capable.");
++	}
++	
++}
++
++/* if underlying libcrypto is FIPS capable, set FIPS_mode to 1 */
++int
++ssh_FIPS_mode_set_if_capable()
++{
++	/* presence of FIPS_module_mode_set indicates FIPS capable OpenSSL */
++	if (dlsym(RTLD_DEFAULT, "FIPS_module_mode_set") != NULL) {
++		/* call the API function FIPS_mode_set*/
++		if (!FIPS_mode_set(1)) {
++			ssh_FIPS_err_code = ERR_get_error();
++			return 1;
++		}
++	}
++	return 0;
++}
++#endif
++
+ /* set/unset filedescriptor to non-blocking */
+ int
+ set_nonblock(int fd)
+diff -pur old/misc.h new/misc.h
+--- old/misc.h
++++ new/misc.h
[email protected]@ -38,6 +38,11 @@ struct ForwardOptions {
+ 
+ char	*chop(char *);
+ char	*strdelim(char **);
++#ifdef ENABLE_OPENSSL_FIPS
++int	 ssh_FIPS_mode();
++int	 ssh_FIPS_mode_set_if_capable();
++void     ssh_FIPS_check_status();
++#endif
+ int	 set_nonblock(int);
+ int	 unset_nonblock(int);
+ void	 set_nodelay(int);
+diff -pur old/myproposal.h new/myproposal.h
+--- old/myproposal.h
++++ new/myproposal.h
[email protected]@ -131,6 +131,15 @@
+ 	CAST128 \
+ 	"aes192-cbc,aes256-cbc,arcfour,[email protected]"
+ 
++#ifdef ENABLE_OPENSSL_FIPS
++#define KEX_FIPS_SERVER_ENCRYPT \
++	"aes128-ctr,aes192-ctr,aes256-ctr" \
++	AESGCM_CIPHER_MODES
++
++#define KEX_FIPS_CLIENT_ENCRYPT KEX_FIPS_SERVER_ENCRYPT "," \
++	"aes128-cbc,3des-cbc,aes192-cbc,aes256-cbc,[email protected]"
++#endif /* ENABLE_OPENSSL_FIPS */
++
+ #define KEX_SERVER_MAC \
+ 	"[email protected]," \
+ 	"[email protected]," \
[email protected]@ -154,6 +163,20 @@
+ 	"hmac-sha1-96," \
+ 	"hmac-md5-96"
+ 
++#ifdef ENABLE_OPENSSL_FIPS
++#define KEX_FIPS_SERVER_MAC \
++	"[email protected]," \
++	"[email protected]," \
++	"[email protected]," \
++	"hmac-sha2-256," \
++	"hmac-sha2-512," \
++	"hmac-sha1"
++
++#define KEX_FIPS_CLIENT_MAC KEX_FIPS_SERVER_MAC "," \
++	"[email protected]," \
++	"hmac-sha1-96"
++#endif /* ENABLE_OPENSSL_FIPS */
++
+ #else
+ 
+ #define KEX_SERVER_KEX		\
+diff -pur old/readconf.c new/readconf.c
+--- old/readconf.c
++++ new/readconf.c
[email protected]@ -1760,6 +1760,11 @@ fill_default_options_for_canonicalizatio
+ void
+ fill_default_options(Options * options)
+ {
++#ifdef ENABLE_OPENSSL_FIPS
++	char *encs;
++	char *macs;
++#endif /* ENABLE_OPENSSL_FIPS */
++
+ 	if (options->forward_agent == -1)
+ 		options->forward_agent = 0;
+ 	if (options->forward_x11 == -1)
[email protected]@ -1934,8 +1939,15 @@ fill_default_options(Options * options)
+ 		options->fingerprint_hash = SSH_FP_HASH_DEFAULT;
+ 	if (options->update_hostkeys == -1)
+ 		options->update_hostkeys = 0;
++#ifndef ENABLE_OPENSSL_FIPS
+ 	if (kex_assemble_names(KEX_CLIENT_ENCRYPT, &options->ciphers) != 0 ||
+ 	    kex_assemble_names(KEX_CLIENT_MAC, &options->macs) != 0 ||
++#else
++	encs = ssh_FIPS_mode() ? KEX_FIPS_CLIENT_ENCRYPT : KEX_CLIENT_ENCRYPT;
++	macs = ssh_FIPS_mode() ? KEX_FIPS_CLIENT_MAC : KEX_CLIENT_MAC;
++	if (kex_assemble_names(encs, &options->ciphers) != 0 ||
++	    kex_assemble_names(macs, &options->macs) != 0 ||
++#endif /* ENABLE_OPENSSL_FIPS */
+ 	    kex_assemble_names(KEX_CLIENT_KEX, &options->kex_algorithms) != 0 ||
+ 	    kex_assemble_names(KEX_DEFAULT_PK_ALG,
+ 	    &options->hostbased_key_types) != 0 ||
+diff -pur old/servconf.c new/servconf.c
+--- old/servconf.c
++++ new/servconf.c
[email protected]@ -195,6 +195,10 @@ void
+ fill_default_server_options(ServerOptions *options)
+ {
+ 	int i;
++#ifdef ENABLE_OPENSSL_FIPS
++	char *encs;
++	char *macs;
++#endif /* ENABLE_OPENSSL_FIPS */
+ 
+ 	/* Portable-specific options */
+ 	if (options->use_pam == -1)
[email protected]@ -382,8 +386,15 @@ fill_default_server_options(ServerOption
+ 	if (options->fingerprint_hash == -1)
+ 		options->fingerprint_hash = SSH_FP_HASH_DEFAULT;
+ 
++#ifndef ENABLE_OPENSSL_FIPS
+ 	if (kex_assemble_names(KEX_SERVER_ENCRYPT, &options->ciphers) != 0 ||
+ 	    kex_assemble_names(KEX_SERVER_MAC, &options->macs) != 0 ||
++#else
++	encs = ssh_FIPS_mode() ? KEX_FIPS_SERVER_ENCRYPT : KEX_SERVER_ENCRYPT;
++	macs = ssh_FIPS_mode() ? KEX_FIPS_SERVER_MAC : KEX_SERVER_MAC;
++	if (kex_assemble_names(encs, &options->ciphers) != 0 ||
++	    kex_assemble_names(macs, &options->macs) != 0 ||
++#endif /* ENABLE_OPENSSL_FIPS */
+ 	    kex_assemble_names(KEX_SERVER_KEX, &options->kex_algorithms) != 0 ||
+ 	    kex_assemble_names(KEX_DEFAULT_PK_ALG,
+ 	    &options->hostbased_key_types) != 0 ||
+diff -pur old/ssh-add.1 new/ssh-add.1
+--- old/ssh-add.1
++++ new/ssh-add.1
[email protected]@ -114,6 +114,8 @@ and
+ .Dq sha256 .
+ The default is
+ .Dq sha256 .
++If OpenSSL is running in FIPS-140 mode, the only supported option is
++.Dq sha256 .
+ .It Fl e Ar pkcs11
+ Remove keys provided by the PKCS#11 shared library
+ .Ar pkcs11 .
+diff -pur old/ssh-add.c new/ssh-add.c
+--- old/ssh-add.c
++++ new/ssh-add.c
[email protected]@ -493,6 +493,12 @@ main(int argc, char **argv)
+ 	__progname = ssh_get_progname(argv[0]);
+ 	seed_rng();
+ 
++#ifdef ENABLE_OPENSSL_FIPS
++	if (ssh_FIPS_mode_set_if_capable()) {
++		fprintf(stderr, "Setting FIPS mode failed!");
++		exit(1);
++	}
++#endif
+ #ifdef WITH_OPENSSL
+ 	OpenSSL_add_all_algorithms();
+ #endif
+diff -pur old/ssh-agent.1 new/ssh-agent.1
+--- old/ssh-agent.1
++++ new/ssh-agent.1
[email protected]@ -110,6 +110,8 @@ and
+ .Dq sha256 .
+ The default is
+ .Dq sha256 .
++If OpenSSL is running in FIPS-140 mode, the only supported option is
++.Dq sha256 .
+ .It Fl k
+ Kill the current agent (given by the
+ .Ev SSH_AGENT_PID
+diff -pur old/ssh-agent.c new/ssh-agent.c
+--- old/ssh-agent.c
++++ new/ssh-agent.c
[email protected]@ -1187,6 +1187,7 @@ main(int ac, char **av)
+ 	struct timeval *tvp = NULL;
+ 	size_t len;
+ 	mode_t prev_mask;
++	int fips_err;
+ 
+ 	/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
+ 	sanitise_stdfd();
[email protected]@ -1200,6 +1201,9 @@ main(int ac, char **av)
+ 	prctl(PR_SET_DUMPABLE, 0);
+ #endif
+ 
++#ifdef ENABLE_OPENSSL_FIPS
++	fips_err = ssh_FIPS_mode_set_if_capable();
++#endif
+ #ifdef WITH_OPENSSL
+ 	OpenSSL_add_all_algorithms();
+ #endif
[email protected]@ -1330,7 +1334,18 @@ main(int ac, char **av)
+ 		printf(format, SSH_AUTHSOCKET_ENV_NAME, socket_name,
+ 		    SSH_AUTHSOCKET_ENV_NAME);
+ 		printf("echo Agent pid %ld;\n", (long)parent_pid);
++#ifdef ENABLE_OPENSSL_FIPS
++		ssh_FIPS_check_status();
++#endif
+ 		goto skip;
++#ifdef ENABLE_OPENSSL_FIPS
++	} else {
++		/* we still need to error out on FIPS_mode_set failure */
++		if (fips_err) {
++			fprintf(stderr, "Setting FIPS mode failed!");
++			cleanup_exit(1);
++		}
++#endif
+ 	}
+ 	pid = fork();
+ 	if (pid == -1) {
+diff -pur old/ssh-keygen.1 new/ssh-keygen.1
+--- old/ssh-keygen.1
++++ new/ssh-keygen.1
[email protected]@ -268,6 +268,8 @@ and
+ .Dq sha256 .
+ The default is
+ .Dq sha256 .
++If OpenSSL is running in FIPS-140 mode, the only supported option is
++.Dq sha256 .
+ .It Fl e
+ This option will read a private or public OpenSSH key file and
+ print to stdout the key in one of the formats specified by the
+diff -pur old/ssh-keygen.c new/ssh-keygen.c
+--- old/ssh-keygen.c
++++ new/ssh-keygen.c
[email protected]@ -2224,11 +2224,18 @@ main(int argc, char **argv)
+ 
+ 	__progname = ssh_get_progname(argv[0]);
+ 
++#ifdef ENABLE_OPENSSL_FIPS
++	ssh_FIPS_mode_set_if_capable();
++#endif
+ #ifdef WITH_OPENSSL
+ 	OpenSSL_add_all_algorithms();
+ #endif
+ 	log_init(argv[0], SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_USER, 1);
+ 
++#ifdef ENABLE_OPENSSL_FIPS
++	ssh_FIPS_check_status();
++#endif
++
+ 	seed_rng();
+ 
+ 	/* we need this for the home * directory.  */
+diff -pur old/ssh-keysign.c new/ssh-keysign.c
+--- old/ssh-keysign.c
++++ new/ssh-keysign.c
[email protected]@ -175,6 +175,7 @@ main(int argc, char **argv)
+ 	u_char *signature, *data, rver;
+ 	char *host, *fp;
+ 	size_t slen, dlen;
++	int fips_err;
+ #ifdef WITH_OPENSSL
+ 	u_int32_t rnd[256];
+ #endif
[email protected]@ -223,6 +224,16 @@ main(int argc, char **argv)
+ 	if (found == 0)
+ 		fatal("could not open any host key");
+ 
++#ifdef ENABLE_OPENSSL_FIPS
++	fips_err = ssh_FIPS_mode_set_if_capable();
++#ifdef DEBUG_SSH_KEYSIGN
++	ssh_FIPS_check_status();
++#else
++	/* we still need to error out on FIPS_mode_set failure */
++	if (fips_err)
++		fatal("Setting FIPS mode failed!");
++#endif
++#endif
+ #ifdef WITH_OPENSSL
+ 	OpenSSL_add_all_algorithms();
+ 	arc4random_buf(rnd, sizeof(rnd));
+diff -pur old/ssh.1 new/ssh.1
+--- old/ssh.1
++++ new/ssh.1
[email protected]@ -92,6 +92,9 @@ If
+ is specified,
+ it is executed on the remote host instead of a login shell.
+ .Pp
++If ssh links with FIPS-capable OpenSSL, ssh runs in FIPS-140 mode.
++In FIPS-140 mode non-FIPS approved ciphers, MACs and digests are disabled.
++.Pp
+ The options are as follows:
+ .Pp
+ .Bl -tag -width Ds -compact
+diff -pur old/ssh.c new/ssh.c
+--- old/ssh.c
++++ new/ssh.c
[email protected]@ -588,6 +588,11 @@ main(int ac, char **av)
+ 	 */
+ 	initialize_options(&options);
+ 
++#ifdef ENABLE_OPENSSL_FIPS
++	/* determine FIPS mode early to limit ciphers and macs */
++	ssh_FIPS_mode_set_if_capable();
++#endif
++
+ 	/* Parse command-line arguments. */
+ 	host = NULL;
+ 	use_syslog = 0;
[email protected]@ -997,6 +1002,10 @@ main(int ac, char **av)
+ #endif
+ 		);
+ 
++#ifdef ENABLE_OPENSSL_FIPS
++	ssh_FIPS_check_status();
++#endif
++
+ 	/* Parse the configuration files */
+ 	process_config_files(host_arg, pw, 0);
+ 
+diff -pur old/ssh_api.c new/ssh_api.c
+--- old/ssh_api.c
++++ new/ssh_api.c
[email protected]@ -81,6 +81,10 @@ ssh_init(struct ssh **sshp, int is_serve
+ 	int r;
+ 
+ 	if (!called) {
++#ifdef ENABLE_OPENSSL_FIPS
++		ssh_FIPS_mode_set_if_capable();
++		ssh_FIPS_check_status();
++#endif
+ #ifdef WITH_OPENSSL
+ 		OpenSSL_add_all_algorithms();
+ #endif /* WITH_OPENSSL */
+diff -pur old/ssh_config.5 new/ssh_config.5
+--- old/ssh_config.5
++++ new/ssh_config.5
[email protected]@ -423,6 +423,13 @@ aes128-cbc,3des-cbc,blowfish-cbc,cast128
+ aes192-cbc,aes256-cbc,arcfour
+ .Ed
+ .Pp
++The following ciphers are FIPS-140 approved and are supported in FIPS-140 mode:
++.Bd -literal -offset indent
++aes128-ctr,aes192-ctr,aes256-ctr,
[email protected],[email protected],
++aes128-cbc,3des-cbc,aes192-cbc,aes256-cbc
++.Ed
++.Pp
+ The list of available ciphers may also be obtained using the
+ .Fl Q
+ option of
[email protected]@ -662,6 +669,8 @@ and
+ .Dq sha256 .
+ The default is
+ .Dq sha256 .
++In FIPS-140 mode the only supported option is
++.Dq sha256 .
+ .It Cm ForwardAgent
+ Specifies whether the connection to the authentication agent (if any)
+ will be forwarded to the remote machine.
[email protected]@ -1110,6 +1119,16 @@ hmac-md5,hmac-sha1,hmac-ripemd160,
+ hmac-sha1-96,hmac-md5-96
+ .Ed
+ .Pp
++The following MACs are FIPS-140 approved and are supported in FIPS-140 mode:
++.Bd -literal -offset indent
[email protected],
[email protected],
++hmac-sha2-256,hmac-sha2-512,
[email protected],
[email protected]
++hmac-sha1,hmac-sha1-96
++.Ed
++.Pp
+ The list of available MAC algorithms may also be obtained using the
+ .Fl Q
+ option of
+diff -pur old/sshconnect.c new/sshconnect.c
+--- old/sshconnect.c
++++ new/sshconnect.c
[email protected]@ -523,8 +523,14 @@ send_client_banner(int connection_out, i
+ {
+ 	/* Send our own protocol version identification. */
+ 	if (compat20) {
++#ifdef ENABLE_OPENSSL_FIPS
++		xasprintf(&client_version_string, "SSH-%d.%d-%.100s%s\r\n",
++		    PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2, SSH_VERSION,
++		    ssh_FIPS_mode() ? " FIPS" : "");
++#else
+ 		xasprintf(&client_version_string, "SSH-%d.%d-%.100s\r\n",
+ 		    PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2, SSH_VERSION);
++#endif
+ 	} else {
+ 		xasprintf(&client_version_string, "SSH-%d.%d-%.100s\n",
+ 		    PROTOCOL_MAJOR_1, minor1, SSH_VERSION);
+diff -pur old/sshd.8 new/sshd.8
+--- old/sshd.8
++++ new/sshd.8
[email protected]@ -86,6 +86,9 @@ rereads its configuration file when it r
+ by executing itself with the name and options it was started with, e.g.\&
+ .Pa /usr/sbin/sshd .
+ .Pp
++If sshd links with FIPS-capable OpenSSL, sshd runs in FIPS-140 mode.
++In FIPS-140 mode non-FIPS approved ciphers, MACs and digests are disabled.
++.Pp
+ The options are as follows:
+ .Bl -tag -width Ds
+ .It Fl 4
+diff -pur old/sshd.c new/sshd.c
+--- old/sshd.c
++++ new/sshd.c
[email protected]@ -431,10 +431,18 @@ sshd_exchange_identification(int sock_in
+ 		minor = PROTOCOL_MINOR_1;
+ 	}
+ 
++#ifdef ENABLE_OPENSSL_FIPS
++	xasprintf(&server_version_string, "SSH-%d.%d-%.100s%s%s%s%s",
++	    major, minor, SSH_VERSION,
++	    ssh_FIPS_mode() ? " FIPS" : " ",
++	    *options.version_addendum == '\0' ? "" : " ",
++	    options.version_addendum, newline);
++#else
+ 	xasprintf(&server_version_string, "SSH-%d.%d-%.100s%s%s%s",
+ 	    major, minor, SSH_VERSION,
+ 	    *options.version_addendum == '\0' ? "" : " ",
+ 	    options.version_addendum, newline);
++#endif
+ 
+ 	/* Send our protocol version identification. */
+ 	if (roaming_atomicio(vwrite, sock_out, server_version_string,
[email protected]@ -1501,6 +1509,10 @@ main(int ac, char **av)
+ 	/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
+ 	sanitise_stdfd();
+ 
++#ifdef ENABLE_OPENSSL_FIPS
++	ssh_FIPS_mode_set_if_capable();
++#endif
++
+ 	/* Initialize configuration options to their default values. */
+ 	initialize_server_options(&options);
+ 
[email protected]@ -1653,6 +1665,10 @@ main(int ac, char **av)
+ 	    SYSLOG_FACILITY_AUTH : options.log_facility,
+ 	    log_stderr || !inetd_flag);
+ 
++#ifdef ENABLE_OPENSSL_FIPS
++	ssh_FIPS_check_status();
++#endif
++
+ 	/*
+ 	 * Unset KRB5CCNAME, otherwise the user's session may inherit it from
+ 	 * root's environment
+diff -pur old/sshd_config.5 new/sshd_config.5
+--- old/sshd_config.5
++++ new/sshd_config.5
[email protected]@ -481,6 +481,13 @@ aes128-ctr,aes192-ctr,aes256-ctr,
+ [email protected],[email protected]
+ .Ed
+ .Pp
++The following ciphers are FIPS-140 approved and are supported in FIPS-140 mode:
++.Bd -literal -offset indent
++aes128-ctr,aes192-ctr,aes256-ctr,
[email protected],[email protected],
++aes128-cbc,3des-cbc,aes192-cbc,aes256-cbc
++.Ed
++.Pp
+ The list of available ciphers may also be obtained using the
+ .Fl Q
+ option of
[email protected]@ -577,6 +584,8 @@ and
+ .Dq sha256 .
+ The default is
+ .Dq sha256 .
++In FIPS-140 mode the only supported option is
++.Dq sha256 .
+ .It Cm ForceCommand
+ Forces the execution of the command specified by
+ .Cm ForceCommand ,
[email protected]@ -1023,6 +1032,16 @@ [email protected],[email protected]
+ hmac-sha2-256,hmac-sha2-512
+ .Ed
+ .Pp
++The following MACs are FIPS-140 approved and are supported in FIPS-140 mode:
++.Bd -literal -offset indent
[email protected],
[email protected],
++hmac-sha2-256,hmac-sha2-512,
[email protected],
[email protected]
++hmac-sha1,hmac-sha1-96
++.Ed
++.Pp
+ The list of available MAC algorithms may also be obtained using the
+ .Fl Q
+ option of
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openssh/patches/036-fipsrandom.patch	Fri Jan 22 07:17:30 2016 -0800
@@ -0,0 +1,119 @@
+#
+# Replace arc4random* calls with FIPS compliant implementation in FIPS mode.
+#
+# Once libc:arc4random* are FIPS compliant (20816957), this patch will be
+# dropped.
+#
+# This is a temporary patch and is not intented for upstream contribution.
+#
+diff -pur old/misc.c new/misc.c
+--- old/misc.c
++++ new/misc.c
[email protected]@ -1164,3 +1164,87 @@ sock_set_v6only(int s)
+ 		error("setsockopt IPV6_V6ONLY: %s", strerror(errno));
+ #endif
+ }
++
++#ifdef ENABLE_OPENSSL_FIPS
++/* cancel arc4random* -> fips_arc4random* defines from misc.h */
++#undef	arc4random
++#undef	arc4random_buf
++#undef	arc4random_stir
++#undef	arc4random_uniform
++
++/* FIPS compliant alternative for arc4random */
++static uint32_t
++fips_arc4random_impl()
++{
++	unsigned int r = 0;
++
++	if (RAND_bytes((unsigned char *)&r, sizeof (r)) <= 0) {
++		fatal("RAND_bytes() failed. Aborting the process");
++	}
++
++	return (r);
++}
++
++uint32_t
++fips_arc4random()
++{
++	if (!ssh_FIPS_mode())
++		return arc4random();
++	else
++		return fips_arc4random_impl();
++}
++
++/* implementation taken from openbsd-compat/arc4random.c */
++void
++fips_arc4random_buf(void *_buf, size_t n)
++{
++	size_t i;
++	uint32_t r = 0;
++	char *buf = (char *)_buf;
++
++	if (!ssh_FIPS_mode())
++		return arc4random_buf(_buf, n);
++
++	for (i = 0; i < n; i++) {
++		if (i % 4 == 0)
++			r = fips_arc4random_impl();
++		buf[i] = r & 0xff;
++		r >>= 8;
++	}
++	explicit_bzero(&r, sizeof(r));
++}
++
++void
++fips_arc4random_stir(void)
++{
++	if (!ssh_FIPS_mode())
++		return arc4random_stir();
++}
++
++/* implementation taken from openbsd-compat/arc4random.c */
++uint32_t
++fips_arc4random_uniform(uint32_t upper_bound)
++{
++	uint32_t r, min;
++
++	if (upper_bound < 2)
++		return 0;
++
++	/* 2**32 % x == (2**32 - x) % x */
++	min = -upper_bound % upper_bound;
++
++	/*
++	 * This could theoretically loop forever but each retry has
++	 * p > 0.5 (worst case, usually far better) of selecting a
++	 * number inside the range we need, so it should rarely need
++	 * to re-roll.
++	 */
++	for (;;) {
++		r = fips_arc4random_impl();
++		if (r >= min)
++			break;
++	}
++
++	return r % upper_bound;
++}
++#endif /* ENABLE_OPENSSL_FIPS */
+diff -pur old/misc.h new/misc.h
+--- old/misc.h
++++ new/misc.h
[email protected]@ -140,4 +140,16 @@ char	*read_passphrase(const char *, int)
+ int	 ask_permission(const char *, ...) __attribute__((format(printf, 1, 2)));
+ int	 read_keyfile_line(FILE *, const char *, char *, size_t, u_long *);
+ 
++#ifdef ENABLE_OPENSSL_FIPS
++/* arc4random* FIPS alternatives */
++uint32_t fips_arc4random(void);
++void	 fips_arc4random_buf(void *, size_t);
++void	 fips_arc4random_stir(void);
++uint32_t fips_arc4random_uniform(uint32_t upper_bound);
++#define	arc4random fips_arc4random
++#define	arc4random_buf fips_arc4random_buf
++#define	arc4random_stir fips_arc4random_stir
++#define	arc4random_uniform fips_arc4random_uniform
++#endif /* ENABLE_OPENSSL_FIPS */
++
+ #endif /* _MISC_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openssh/patches/037-missing_or_misleading_error_messages.patch	Fri Jan 22 07:17:30 2016 -0800
@@ -0,0 +1,51 @@
+#
+# Add missing error() upon identity_sign() failure, fix typos in ssherr() calls.
+#
+# After OpenSSH code was refactored recently, DSA_do_sign() and RSA_sign()
+# failures no longer result in error messages printed out. This is particularly
+# inconvenient for FIPS mode, were these calls can fail due to non-compliant
+# crypto algorithm options.
+#
+# Investigating other missing error messages spotted two calls to ssherr()
+# with wrong error codes.
+#
+# Patch has been submitted upstream:
+# https://bugzilla.mindrot.org/show_bug.cgi?id=2507
+#
+# Update: patch has been accepted upstream, will be part of 7.2:
+# https://github.com/openssh/openssh-portable/commit/39736be
+#
+# On uprev to 7.2+ we will drop this patch.
+#
+diff -pur old/ssh-agent.c new/ssh-agent.c
+--- old/ssh-agent.c
++++ new/ssh-agent.c
[email protected]@ -389,7 +389,7 @@ process_sign_request2(SocketEntry *e)
+ 	if (flags & SSH_AGENT_OLD_SIGNATURE)
+ 		compat = SSH_BUG_SIGBLOB;
+ 	if ((r = sshkey_from_blob(blob, blen, &key)) != 0) {
+-		error("%s: cannot parse key blob: %s", __func__, ssh_err(ok));
++		error("%s: cannot parse key blob: %s", __func__, ssh_err(r));
+ 		goto send;
+ 	}
+ 	if ((id = lookup_identity(key, 2)) == NULL) {
[email protected]@ -402,7 +402,7 @@ process_sign_request2(SocketEntry *e)
+ 	}
+ 	if ((r = sshkey_sign(id->key, &signature, &slen,
+ 	    data, dlen, compat)) != 0) {
+-		error("%s: sshkey_sign: %s", __func__, ssh_err(ok));
++		error("%s: sshkey_sign: %s", __func__, ssh_err(r));
+ 		goto send;
+ 	}
+ 	/* Success */
+diff -pur old/sshconnect2.c new/sshconnect2.c
+--- old/sshconnect2.c
++++ new/sshconnect2.c
[email protected]@ -1167,6 +1167,7 @@ sign_and_send_pubkey(Authctxt *authctxt,
+ 	ret = identity_sign(id, &signature, &slen,
+ 	    buffer_ptr(&b), buffer_len(&b), datafellows);
+ 	if (ret != 0) {
++		error("%s: signing failed: %s", __func__, ssh_err(ret));
+ 		free(blob);
+ 		buffer_free(&b);
+ 		return 0;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openssh/patches/038-print_libcrypto_errors.patch	Fri Jan 22 07:17:30 2016 -0800
@@ -0,0 +1,38 @@
+#
+# Return OpenSSL error messages in ssherr() for SSH_ERR_LIBCRYPTO_ERROR.
+#
+# After code refactoring for library-like interfaces,OpenSSL error string
+# were replaced by generic and vague "error in libcrypto" message.
+#
+# This patch returns OpenSSL error strings for SSH_ERR_LIBCRYPTO_ERROR errors.
+#
+# Patch submitted upstream:
+# https://bugzilla.mindrot.org/show_bug.cgi?id=2508
+#
+diff -pur old/ssherr.c new/ssherr.c
+--- old/ssherr.c
++++ new/ssherr.c
[email protected]@ -17,11 +17,13 @@
+ 
+ #include <errno.h>
+ #include <string.h>
++#include <openssl/err.h>
+ #include "ssherr.h"
+ 
+ const char *
+ ssh_err(int n)
+ {
++	static char err_str[256];
+ 	switch (n) {
+ 	case SSH_ERR_SUCCESS:
+ 		return "success";
[email protected]@ -68,7 +70,8 @@ ssh_err(int n)
+ 	case SSH_ERR_SIGNATURE_INVALID:
+ 		return "incorrect signature";
+ 	case SSH_ERR_LIBCRYPTO_ERROR:
+-		return "error in libcrypto";  /* XXX fetch and return */
++		ERR_error_string_n(ERR_get_error(), err_str, sizeof (err_str));
++		return err_str;
+ 	case SSH_ERR_UNEXPECTED_TRAILING_DATA:
+ 		return "unexpected bytes remain after decoding";
+ 	case SSH_ERR_SYSTEM_ERROR: