File openssh-6.6p1-fips.patch of Package openssh.10219

# HG changeset patch
# Parent  bfbd15d9410b79ca29a27873bec7ac202a720194
FIPS 140-2 compliance. Perform selftests on start and use only FIPS approved
algorithms.

diff --git a/openssh-6.6p1/Makefile.in b/openssh-6.6p1/Makefile.in
--- a/openssh-6.6p1/Makefile.in
+++ b/openssh-6.6p1/Makefile.in
@@ -71,17 +71,18 @@ LIBSSH_OBJS=authfd.o authfile.o bufaux.o
 	readpass.o rsa.o ttymodes.o xmalloc.o addrmatch.o \
 	atomicio.o key.o dispatch.o kex.o mac.o uidswap.o uuencode.o misc.o \
 	monitor_fdpass.o rijndael.o ssh-dss.o ssh-ecdsa.o ssh-rsa.o dh.o \
 	kexdh.o kexgex.o kexdhc.o kexgexc.o bufec.o kexecdh.o kexecdhc.o \
 	msg.o progressmeter.o dns.o entropy.o gss-genr.o umac.o umac128.o \
 	ssh-pkcs11.o krl.o smult_curve25519_ref.o \
 	kexc25519.o kexc25519c.o poly1305.o chacha.o cipher-chachapoly.o \
 	ssh-ed25519.o digest-openssl.o hmac.o \
-	sc25519.o ge25519.o fe25519.o ed25519.o verify.o hash.o blocks.o
+	sc25519.o ge25519.o fe25519.o ed25519.o verify.o hash.o blocks.o \
+	fips.o
 
 SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \
 	sshconnect.o sshconnect1.o sshconnect2.o mux.o \
 	roaming_common.o roaming_client.o
 
 SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.o auth-rsa.o auth-rh-rsa.o \
 	audit.o audit-bsm.o audit-linux.o platform.o \
 	sshpty.o sshlogin.o servconf.o serverloop.o \
diff --git a/openssh-6.6p1/auth-rsa.c b/openssh-6.6p1/auth-rsa.c
--- a/openssh-6.6p1/auth-rsa.c
+++ b/openssh-6.6p1/auth-rsa.c
@@ -44,16 +44,18 @@
 #include "ssh-gss.h"
 #endif
 #include "monitor_wrap.h"
 #include "ssh.h"
 #include "misc.h"
 
 #include "digest.h"
 
+#include "fips.h"
+
 /* import */
 extern ServerOptions options;
 
 /*
  * Session identifier that is used to bind key exchange and authentication
  * responses to a particular session.
  */
 extern u_char session_id[16];
@@ -84,45 +86,52 @@ auth_rsa_generate_challenge(Key *key)
 	if (BN_mod(challenge, challenge, key->rsa->n, ctx) == 0)
 		fatal("auth_rsa_generate_challenge: BN_mod failed");
 	BN_CTX_free(ctx);
 
 	return challenge;
 }
 
 int
-auth_rsa_verify_response(Key *key, BIGNUM *challenge, u_char response[16])
+auth_rsa_verify_response(Key *key, BIGNUM *challenge,
+    u_char response[SSH_DIGEST_MAX_LENGTH])
 {
-	u_char buf[32], mdbuf[16];
+	u_char buf[2 * SSH_DIGEST_MAX_LENGTH], mdbuf[SSH_DIGEST_MAX_LENGTH];
 	struct ssh_digest_ctx *md;
 	int len;
+	int dgst;
+	size_t dgst_len;
 
 	/* don't allow short keys */
 	if (BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) {
 		error("%s: RSA modulus too small: %d < minimum %d bits",
 		    __func__,
 		    BN_num_bits(key->rsa->n), SSH_RSA_MINIMUM_MODULUS_SIZE);
 		return (0);
 	}
 
-	/* The response is MD5 of decrypted challenge plus session id. */
+	dgst = fips_correct_dgst(SSH_DIGEST_MD5);
+	dgst_len = ssh_digest_bytes(dgst);
+
+	/* The response is a hash of decrypted challenge plus session id.
+	 * Normally this is MD5, in FIPS mode a stronger function is used. */
 	len = BN_num_bytes(challenge);
-	if (len <= 0 || len > 32)
+	if (len <= 0 || (unsigned int)len > (2 * dgst_len))
 		fatal("%s: bad challenge length %d", __func__, len);
-	memset(buf, 0, 32);
-	BN_bn2bin(challenge, buf + 32 - len);
-	if ((md = ssh_digest_start(SSH_DIGEST_MD5)) == NULL ||
-	    ssh_digest_update(md, buf, 32) < 0 ||
-	    ssh_digest_update(md, session_id, 16) < 0 ||
+	memset(buf, 0, sizeof(buf));
+	BN_bn2bin(challenge, buf + 2 * dgst_len - len);
+	if ((md = ssh_digest_start(dgst)) == NULL ||
+	    ssh_digest_update(md, buf, 2 * dgst_len) < 0 ||
+	    ssh_digest_update(md, session_id, dgst_len) < 0 ||
 	    ssh_digest_final(md, mdbuf, sizeof(mdbuf)) < 0)
 		fatal("%s: md5 failed", __func__);
 	ssh_digest_free(md);
 
 	/* Verify that the response is the original challenge. */
-	if (timingsafe_bcmp(response, mdbuf, 16) != 0) {
+	if (timingsafe_bcmp(response, mdbuf, dgst_len) != 0) {
 		/* Wrong answer. */
 		return (0);
 	}
 	/* Correct answer. */
 	return (1);
 }
 
 /*
@@ -130,17 +139,17 @@ auth_rsa_verify_response(Key *key, BIGNU
  * and returns true (non-zero) if the client gave the correct answer to
  * our challenge; returns zero if the client gives a wrong answer.
  */
 
 int
 auth_rsa_challenge_dialog(Key *key)
 {
 	BIGNUM *challenge, *encrypted_challenge;
-	u_char response[16];
+	u_char response[SSH_DIGEST_MAX_LENGTH];
 	int i, success;
 
 	if ((encrypted_challenge = BN_new()) == NULL)
 		fatal("auth_rsa_challenge_dialog: BN_new() failed");
 
 	challenge = PRIVSEP(auth_rsa_generate_challenge(key));
 
 	/* Encrypt the challenge with the public key. */
@@ -150,17 +159,17 @@ auth_rsa_challenge_dialog(Key *key)
 	packet_start(SSH_SMSG_AUTH_RSA_CHALLENGE);
 	packet_put_bignum(encrypted_challenge);
 	packet_send();
 	BN_clear_free(encrypted_challenge);
 	packet_write_wait();
 
 	/* Wait for a response. */
 	packet_read_expect(SSH_CMSG_AUTH_RSA_RESPONSE);
-	for (i = 0; i < 16; i++)
+	for (i = 0; i < ssh_digest_bytes(fips_dgst_min()); i++)
 		response[i] = (u_char)packet_get_char();
 	packet_check_eom();
 
 	success = PRIVSEP(auth_rsa_verify_response(key, challenge, response));
 	BN_clear_free(challenge);
 	return (success);
 }
 
diff --git a/openssh-6.6p1/cipher-ctr.c b/openssh-6.6p1/cipher-ctr.c
--- a/openssh-6.6p1/cipher-ctr.c
+++ b/openssh-6.6p1/cipher-ctr.c
@@ -22,16 +22,18 @@
 #include <stdarg.h>
 #include <string.h>
 
 #include <openssl/evp.h>
 
 #include "xmalloc.h"
 #include "log.h"
 
+#include "fips.h"
+
 /* compatibility with old or broken OpenSSL versions */
 #include "openbsd-compat/openssl-compat.h"
 
 #ifndef USE_BUILTIN_RIJNDAEL
 #include <openssl/aes.h>
 #endif
 
 struct ssh_aes_ctr_ctx
@@ -134,13 +136,15 @@ evp_aes_128_ctr(void)
 	aes_ctr.iv_len = AES_BLOCK_SIZE;
 	aes_ctr.key_len = 16;
 	aes_ctr.init = ssh_aes_ctr_init;
 	aes_ctr.cleanup = ssh_aes_ctr_cleanup;
 	aes_ctr.do_cipher = ssh_aes_ctr;
 #ifndef SSH_OLD_EVP
 	aes_ctr.flags = EVP_CIPH_CBC_MODE | EVP_CIPH_VARIABLE_LENGTH |
 	    EVP_CIPH_ALWAYS_CALL_INIT | EVP_CIPH_CUSTOM_IV;
+	if (fips_mode())
+		aes_ctr.flags |= EVP_CIPH_FLAG_FIPS;
 #endif
 	return (&aes_ctr);
 }
 
 #endif /* OPENSSL_HAVE_EVPCTR */
diff --git a/openssh-6.6p1/cipher.c b/openssh-6.6p1/cipher.c
--- a/openssh-6.6p1/cipher.c
+++ b/openssh-6.6p1/cipher.c
@@ -45,16 +45,18 @@
 
 #include "xmalloc.h"
 #include "log.h"
 #include "misc.h"
 #include "cipher.h"
 #include "buffer.h"
 #include "digest.h"
 
+#include "fips.h"
+
 /* compatibility with old or broken OpenSSL versions */
 #include "openbsd-compat/openssl-compat.h"
 
 extern const EVP_CIPHER *evp_ssh1_bf(void);
 extern const EVP_CIPHER *evp_ssh1_3des(void);
 extern void ssh1_3des_iv(EVP_CIPHER_CTX *, int, u_char *, int);
 
 struct Cipher {
@@ -66,17 +68,17 @@ struct Cipher {
 	u_int	auth_len;
 	u_int	discard_len;
 	u_int	flags;
 #define CFLAG_CBC		(1<<0)
 #define CFLAG_CHACHAPOLY	(1<<1)
 	const EVP_CIPHER	*(*evptype)(void);
 };
 
-static const struct Cipher ciphers[] = {
+static const struct Cipher ciphers_all[] = {
 	{ "none",	SSH_CIPHER_NONE, 8, 0, 0, 0, 0, 0, EVP_enc_null },
 	{ "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 },
 	{ "blowfish",	SSH_CIPHER_BLOWFISH, 8, 32, 0, 0, 0, 1, evp_ssh1_bf },
 
 	{ "3des-cbc",	SSH_CIPHER_SSH2, 8, 24, 0, 0, 0, 1, EVP_des_ede3_cbc },
 	{ "blowfish-cbc",
 			SSH_CIPHER_SSH2, 8, 16, 0, 0, 0, 1, EVP_bf_cbc },
@@ -99,27 +101,67 @@ static const struct Cipher ciphers[] = {
 	{ "aes256-gcm@openssh.com",
 			SSH_CIPHER_SSH2, 16, 32, 12, 16, 0, 0, EVP_aes_256_gcm },
 #endif
 	{ "chacha20-poly1305@openssh.com",
 			SSH_CIPHER_SSH2, 8, 64, 0, 16, 0, CFLAG_CHACHAPOLY, NULL },
 	{ NULL,		SSH_CIPHER_INVALID, 0, 0, 0, 0, 0, 0, NULL }
 };
 
+static const struct Cipher ciphers_fips140_2[] = {
+	{ "none",	SSH_CIPHER_NONE, 8, 0, 0, 0, 0, 0, EVP_enc_null },
+	{ "3des",	SSH_CIPHER_3DES, 8, 16, 0, 0, 0, 1, evp_ssh1_3des },
+
+	{ "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 },
+	{ "rijndael-cbc@lysator.liu.se",
+			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
+	{ "aes128-gcm@openssh.com",
+			SSH_CIPHER_SSH2, 16, 16, 12, 16, 0, 0, EVP_aes_128_gcm },
+	{ "aes256-gcm@openssh.com",
+			SSH_CIPHER_SSH2, 16, 32, 12, 16, 0, 0, EVP_aes_256_gcm },
+#endif
+	{ NULL,		SSH_CIPHER_INVALID, 0, 0, 0, 0, 0, 0, NULL }
+};
+
 /*--*/
 
+/* Returns array of ciphers available depending on selected FIPS mode */
+static const struct Cipher *
+fips_select_ciphers(void)
+{
+	int fips = fips_mode();
+	switch (fips) {
+		case 0:
+			return ciphers_all;
+		case 1:
+			return ciphers_fips140_2;
+		default:
+			/* should not be reached */
+			fatal("Fatal error: incorrect FIPS mode '%i' at %s:%u",
+			    fips, __FILE__, __LINE__);
+			return NULL;
+	}
+}
+
 /* Returns a list of supported ciphers separated by the specified char. */
 char *
 cipher_alg_list(char sep, int auth_only)
 {
 	char *ret = NULL;
 	size_t nlen, rlen = 0;
 	const Cipher *c;
 
-	for (c = ciphers; c->name != NULL; c++) {
+	for (c = fips_select_ciphers(); c->name != NULL; c++) {
 		if (c->number != SSH_CIPHER_SSH2)
 			continue;
 		if (auth_only && c->auth_len == 0)
 			continue;
 		if (ret != NULL)
 			ret[rlen++] = sep;
 		nlen = strlen(c->name);
 		ret = xrealloc(ret, 1, rlen + nlen + 2);
@@ -189,27 +231,27 @@ cipher_mask_ssh1(int client)
 	}
 	return mask;
 }
 
 const Cipher *
 cipher_by_name(const char *name)
 {
 	const Cipher *c;
-	for (c = ciphers; c->name != NULL; c++)
+	for (c = fips_select_ciphers(); c->name != NULL; c++)
 		if (strcmp(c->name, name) == 0)
 			return c;
 	return NULL;
 }
 
 const Cipher *
 cipher_by_number(int id)
 {
 	const Cipher *c;
-	for (c = ciphers; c->name != NULL; c++)
+	for (c = fips_select_ciphers(); c->name != NULL; c++)
 		if (c->number == id)
 			return c;
 	return NULL;
 }
 
 #define	CIPHER_SEP	","
 int
 ciphers_valid(const char *names)
@@ -241,17 +283,17 @@ ciphers_valid(const char *names)
  */
 
 int
 cipher_number(const char *name)
 {
 	const Cipher *c;
 	if (name == NULL)
 		return -1;
-	for (c = ciphers; c->name != NULL; c++)
+	for (c = fips_select_ciphers(); c->name != NULL; c++)
 		if (strcasecmp(c->name, name) == 0)
 			return c->number;
 	return -1;
 }
 
 char *
 cipher_name(int id)
 {
@@ -429,23 +471,24 @@ cipher_cleanup(CipherContext *cc)
  * Selects the cipher, and keys if by computing the MD5 checksum of the
  * passphrase and using the resulting 16 bytes as the key.
  */
 
 void
 cipher_set_key_string(CipherContext *cc, const Cipher *cipher,
     const char *passphrase, int do_encrypt)
 {
-	u_char digest[16];
+	u_char digest[SSH_DIGEST_MAX_LENGTH];
+	int dgst = fips_correct_dgst(SSH_DIGEST_MD5);
 
-	if (ssh_digest_memory(SSH_DIGEST_MD5, passphrase, strlen(passphrase),
+	if (ssh_digest_memory(dgst, passphrase, strlen(passphrase),
 	    digest, sizeof(digest)) < 0)
 		fatal("%s: md5 failed", __func__);
 
-	cipher_init(cc, cipher, digest, 16, NULL, 0, do_encrypt);
+	cipher_init(cc, cipher, digest, ssh_digest_bytes(dgst), NULL, 0, do_encrypt);
 
 	explicit_bzero(digest, sizeof(digest));
 }
 
 /*
  * Exports an IV from the CipherContext required to export the key
  * state back from the unprivileged child to the privileged parent
  * process.
diff --git a/openssh-6.6p1/dh.h b/openssh-6.6p1/dh.h
--- a/openssh-6.6p1/dh.h
+++ b/openssh-6.6p1/dh.h
@@ -41,16 +41,17 @@ DH	*dh_new_group14(void);
 void	 dh_gen_key(DH *, int);
 int	 dh_pub_is_valid(DH *, BIGNUM *);
 
 int	 dh_estimate(int);
 
 /* Min and max values from RFC4419. */
 #define DH_GRP_MIN_RFC	1024
 #define DH_GRP_MIN	2048
+#define DH_GRP_MIN_FIPS	2048
 #define DH_GRP_MAX	8192
 
 /*
  * Values for "type" field of moduli(5)
  * Specifies the internal structure of the prime modulus.
  */
 #define MODULI_TYPE_UNKNOWN		(0)
 #define MODULI_TYPE_UNSTRUCTURED	(1)
diff --git a/openssh-6.6p1/fips.c b/openssh-6.6p1/fips.c
new file mode 100644
--- /dev/null
+++ b/openssh-6.6p1/fips.c
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2012 Petr Cerny.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "includes.h"
+
+#include "fips.h"
+
+#include "dh.h"
+#include "digest.h"
+#include "key.h"
+#include "log.h"
+
+#include <openssl/crypto.h>
+
+/* import from dh.c */
+extern int dh_grp_min;
+
+static int fips_state = -1;
+
+static int
+fips_check_required_env(void)
+{
+	int fips_required = 0;
+	char *env = getenv(SSH_FORCE_FIPS_ENV);
+
+	if (env) {
+		errno = 0;
+		fips_required = strtol(env, NULL, 10);
+		if (errno) {
+			debug("bogus value in the %s environment variable, ignoring\n"
+			    , SSH_FORCE_FIPS_ENV);
+			fips_required = 0;
+		} else
+			fips_required = 1;
+	}
+	return fips_required;
+}
+
+int
+fips_mode(void)
+{
+	if (-1 == fips_state) {
+		fips_state = FIPS_mode();
+		if (fips_state)
+			debug("FIPS mode initialized");
+		else {
+			if (fips_check_required_env()) {
+				debug("FIPS mode requested through the environment variable '%s'"
+				    , SSH_FORCE_FIPS_ENV);
+				if (!FIPS_mode_set(1))
+					fatal("Unable to enter FIPS mode as requested through the environment variable '%s'"
+					    , SSH_FORCE_FIPS_ENV);
+				fips_state = 1;
+			}
+		}
+	}
+	return fips_state;
+}
+
+enum fp_type
+fips_correct_fp_type(enum fp_type fp)
+{
+	int fips;
+	enum fp_type fp_fix = fp;
+
+	fips = fips_mode();
+	switch (fips) {
+		case 0:
+			break;
+		case 1:
+			if (SSH_FP_MD5 == fp) {
+				fp_fix = SSH_FP_SHA1;
+				debug("MD5 not allowed in FIPS 140-2 mode, "
+				    "using SHA-1 for key fingerprints instead.");
+			}
+			break;
+		default:
+			/* should not be reached */
+			fatal("Fatal error: incorrect FIPS mode '%i' at %s:%u",
+			    fips, __FILE__, __LINE__);
+	}
+
+	return fp_fix;
+}
+
+int
+fips_correct_dgst(int digest)
+{
+	int fips;
+	int rv = -1;
+
+	fips = fips_mode();
+	switch (fips) {
+		case 0:
+			rv = digest;
+			break;
+		case 1:
+			switch (digest) {
+				case SSH_DIGEST_MD5:
+				case SSH_DIGEST_RIPEMD160:
+					debug("MD5/RIPEMD160 digests not allowed in FIPS 140-2 mode"
+					    "using SHA-1 instead.");
+					rv = SSH_DIGEST_SHA1;
+					break;
+				default:
+					rv = digest;
+					break;
+			}
+			break;
+		default:
+			/* should not be reached */
+			fatal("Fatal error: incorrect FIPS mode '%i' at %s:%u",
+			    fips, __FILE__, __LINE__);
+	}
+
+	return rv;
+}
+
+int
+fips_dgst_min(void)
+{
+	int fips;
+	int dgst;
+
+	fips = fips_mode();
+	switch (fips) {
+		case 0:
+			dgst = SSH_DIGEST_MD5;
+			break;
+		case 1:
+			dgst = SSH_DIGEST_SHA1;
+			break;
+		default:
+			/* should not be reached */
+			fatal("Fatal error: incorrect FIPS mode '%i' at %s:%u",
+			    fips, __FILE__, __LINE__);
+	}
+	return dgst;
+}
+
+int
+fips_dh_grp_min(void)
+{
+	int fips;
+	int dh;
+
+	fips = fips_mode();
+	switch (fips) {
+		case 0:
+			dh = dh_grp_min;
+			break;
+		case 1:
+			dh = DH_GRP_MIN_FIPS;
+			break;
+		default:
+			/* should not be reached */
+			fatal("Fatal error: incorrect FIPS mode '%i' at %s:%u",
+			    fips, __FILE__, __LINE__);
+	}
+	return dh;
+}
+
diff --git a/openssh-6.6p1/fips.h b/openssh-6.6p1/fips.h
new file mode 100644
--- /dev/null
+++ b/openssh-6.6p1/fips.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2012 Petr Cerny.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef FIPS_H
+#define FIPS_H
+
+#include "key.h"
+
+#define SSH_FORCE_FIPS_ENV	"SSH_FORCE_FIPS"
+
+int	 fips_mode(void);
+int	 fips_correct_dgst(int);
+int	 fips_dgst_min(void);
+int	 fips_dh_grp_min(void);
+enum fp_type	 fips_correct_fp_type(enum fp_type);
+
+#endif
+
diff --git a/openssh-6.6p1/hmac.c b/openssh-6.6p1/hmac.c
--- a/openssh-6.6p1/hmac.c
+++ b/openssh-6.6p1/hmac.c
@@ -139,17 +139,17 @@ ssh_hmac_free(struct ssh_hmac_ctx *ctx)
 /* cc -DTEST hmac.c digest.c buffer.c cleanup.c fatal.c log.c xmalloc.c -lcrypto */
 static void
 hmac_test(void *key, size_t klen, void *m, size_t mlen, u_char *e, size_t elen)
 {
 	struct ssh_hmac_ctx	*ctx;
 	size_t			 i;
 	u_char			 digest[16];
 
-	if ((ctx = ssh_hmac_start(SSH_DIGEST_MD5)) == NULL)
+	if ((ctx = ssh_hmac_start(fips_correct_dgst(SSH_DIGEST_MD5))) == NULL)
 		printf("ssh_hmac_start failed");
 	if (ssh_hmac_init(ctx, key, klen) < 0 ||
 	    ssh_hmac_update(ctx, m, mlen) < 0 ||
 	    ssh_hmac_final(ctx, digest, sizeof(digest)) < 0)
 		printf("ssh_hmac_xxx failed");
 	ssh_hmac_free(ctx);
 
 	if (memcmp(e, digest, elen)) {
diff --git a/openssh-6.6p1/kex.c b/openssh-6.6p1/kex.c
--- a/openssh-6.6p1/kex.c
+++ b/openssh-6.6p1/kex.c
@@ -46,16 +46,18 @@
 #include "log.h"
 #include "mac.h"
 #include "match.h"
 #include "dispatch.h"
 #include "monitor.h"
 #include "roaming.h"
 #include "digest.h"
 
+#include "fips.h"
+
 #if OPENSSL_VERSION_NUMBER >= 0x00907000L
 # if defined(HAVE_EVP_SHA256)
 # define evp_ssh_sha256 EVP_sha256
 # else
 extern const EVP_MD *evp_ssh_sha256(void);
 # endif
 #endif
 
@@ -64,17 +66,17 @@ static void kex_kexinit_finish(Kex *);
 static void kex_choose_conf(Kex *);
 
 struct kexalg {
 	char *name;
 	int type;
 	int ec_nid;
 	int hash_alg;
 };
-static const struct kexalg kexalgs[] = {
+static const struct kexalg kexalgs_all[] = {
 	{ KEX_DH1, KEX_DH_GRP1_SHA1, 0, SSH_DIGEST_SHA1 },
 	{ KEX_DH14, KEX_DH_GRP14_SHA1, 0, SSH_DIGEST_SHA1 },
 	{ KEX_DHGEX_SHA1, KEX_DH_GEX_SHA1, 0, SSH_DIGEST_SHA1 },
 #ifdef HAVE_EVP_SHA256
 	{ KEX_DHGEX_SHA256, KEX_DH_GEX_SHA256, 0, SSH_DIGEST_SHA256 },
 #endif
 #ifdef OPENSSL_HAS_ECC
 	{ KEX_ECDH_SHA2_NISTP256, KEX_ECDH_SHA2,
@@ -88,40 +90,76 @@ static const struct kexalg kexalgs[] = {
 #endif
 	{ KEX_DH1, KEX_DH_GRP1_SHA1, 0, SSH_DIGEST_SHA1 },
 #ifdef HAVE_EVP_SHA256
 	{ KEX_CURVE25519_SHA256, KEX_C25519_SHA256, 0, SSH_DIGEST_SHA256 },
 #endif
 	{ NULL, -1, -1, -1},
 };
 
+static const struct kexalg kexalgs_fips140_2[] = {
+	{ KEX_DH14, KEX_DH_GRP14_SHA1, 0, SSH_DIGEST_SHA1 },
+	{ KEX_DHGEX_SHA1, KEX_DH_GEX_SHA1, 0, SSH_DIGEST_SHA1 },
+#ifdef HAVE_EVP_SHA256
+	{ KEX_DHGEX_SHA256, KEX_DH_GEX_SHA256, 0, SSH_DIGEST_SHA256 },
+#endif
+#ifdef OPENSSL_HAS_ECC
+	{ KEX_ECDH_SHA2_NISTP256, KEX_ECDH_SHA2,
+	    NID_X9_62_prime256v1, SSH_DIGEST_SHA256 },
+	{ KEX_ECDH_SHA2_NISTP384, KEX_ECDH_SHA2, NID_secp384r1,
+	    SSH_DIGEST_SHA384 },
+# ifdef OPENSSL_HAS_NISTP521
+	{ KEX_ECDH_SHA2_NISTP521, KEX_ECDH_SHA2, NID_secp521r1,
+	    SSH_DIGEST_SHA512 },
+# endif
+#endif
+	{ NULL, -1, -1, -1},
+};
+
+/* Returns array of macs available depending on selected FIPS mode */
+static const struct kexalg *
+fips_select_kexalgs(void)
+{
+	int fips = fips_mode();
+	switch (fips) {
+		case 0:
+			return kexalgs_all;
+		case 1:
+			return kexalgs_fips140_2;
+		default:
+			/* should not be reached */
+			fatal("Fatal error: incorrect FIPS mode '%i' at %s:%u",
+			    fips, __FILE__, __LINE__);
+	}
+}
+
 char *
 kex_alg_list(char sep)
 {
 	char *ret = NULL;
 	size_t nlen, rlen = 0;
 	const struct kexalg *k;
 
-	for (k = kexalgs; k->name != NULL; k++) {
+	for (k = fips_select_kexalgs(); k->name != NULL; k++) {
 		if (ret != NULL)
 			ret[rlen++] = sep;
 		nlen = strlen(k->name);
 		ret = xrealloc(ret, 1, rlen + nlen + 2);
 		memcpy(ret + rlen, k->name, nlen + 1);
 		rlen += nlen;
 	}
 	return ret;
 }
 
 static const struct kexalg *
 kex_alg_by_name(const char *name)
 {
 	const struct kexalg *k;
 
-	for (k = kexalgs; k->name != NULL; k++) {
+	for (k = fips_select_kexalgs(); k->name != NULL; k++) {
 		if (strcmp(k->name, name) == 0)
 			return k;
 	}
 	return NULL;
 }
 
 /* Validate KEX method name list */
 int
@@ -638,19 +676,21 @@ kex_get_newkeys(int mode)
 }
 
 void
 derive_ssh1_session_id(BIGNUM *host_modulus, BIGNUM *server_modulus,
     u_int8_t cookie[8], u_int8_t id[16])
 {
 	u_int8_t nbuf[2048], obuf[SSH_DIGEST_MAX_LENGTH];
 	int len;
+	int digest;
 	struct ssh_digest_ctx *hashctx;
 
-	if ((hashctx = ssh_digest_start(SSH_DIGEST_MD5)) == NULL)
+	digest = fips_correct_dgst(SSH_DIGEST_MD5);
+	if ((hashctx = ssh_digest_start(digest)) == NULL)
 		fatal("%s: ssh_digest_start", __func__);
 
 	len = BN_num_bytes(host_modulus);
 	if (len < (512 / 8) || (u_int)len > sizeof(nbuf))
 		fatal("%s: bad host modulus (len %d)", __func__, len);
 	BN_bn2bin(host_modulus, nbuf);
 	if (ssh_digest_update(hashctx, nbuf, len) != 0)
 		fatal("%s: ssh_digest_update failed", __func__);
@@ -659,17 +699,17 @@ derive_ssh1_session_id(BIGNUM *host_modu
 	if (len < (512 / 8) || (u_int)len > sizeof(nbuf))
 		fatal("%s: bad server modulus (len %d)", __func__, len);
 	BN_bn2bin(server_modulus, nbuf);
 	if (ssh_digest_update(hashctx, nbuf, len) != 0 ||
 	    ssh_digest_update(hashctx, cookie, 8) != 0)
 		fatal("%s: ssh_digest_update failed", __func__);
 	if (ssh_digest_final(hashctx, obuf, sizeof(obuf)) != 0)
 		fatal("%s: ssh_digest_final failed", __func__);
-	memcpy(id, obuf, ssh_digest_bytes(SSH_DIGEST_MD5));
+	memcpy(id, obuf, ssh_digest_bytes(digest));
 
 	explicit_bzero(nbuf, sizeof(nbuf));
 	explicit_bzero(obuf, sizeof(obuf));
 }
 
 #if defined(DEBUG_KEX) || defined(DEBUG_KEXDH) || defined(DEBUG_KEXECDH)
 void
 dump_digest(char *msg, u_char *digest, int len)
diff --git a/openssh-6.6p1/kexgexc.c b/openssh-6.6p1/kexgexc.c
--- a/openssh-6.6p1/kexgexc.c
+++ b/openssh-6.6p1/kexgexc.c
@@ -41,18 +41,17 @@
 #include "cipher.h"
 #include "kex.h"
 #include "log.h"
 #include "packet.h"
 #include "dh.h"
 #include "ssh2.h"
 #include "compat.h"
 
-/* import from dh.c */
-extern int dh_grp_min;
+#include "fips.h"
 
 void
 kexgex_client(Kex *kex)
 {
 	BIGNUM *dh_server_pub = NULL, *shared_secret = NULL;
 	BIGNUM *p = NULL, *g = NULL;
 	Key *server_host_key;
 	u_char *kbuf, *hash, *signature = NULL, *server_host_key_blob = NULL;
@@ -63,23 +62,23 @@ kexgex_client(Kex *kex)
 	DH *dh;
 
 	nbits = dh_estimate(kex->dh_need * 8);
 
 	if (datafellows & SSH_OLD_DHGEX) {
 		/* Old GEX request */
 		packet_start(SSH2_MSG_KEX_DH_GEX_REQUEST_OLD);
 		packet_put_int(nbits);
-		min = dh_grp_min;
+		min = fips_dh_grp_min();
 		max = DH_GRP_MAX;
 
 		debug("SSH2_MSG_KEX_DH_GEX_REQUEST_OLD(%u) sent", nbits);
 	} else {
 		/* New GEX request */
-		min = dh_grp_min;
+		min = fips_dh_grp_min();
 		max = DH_GRP_MAX;
 		packet_start(SSH2_MSG_KEX_DH_GEX_REQUEST);
 		packet_put_int(min);
 		packet_put_int(nbits);
 		packet_put_int(max);
 
 		debug("SSH2_MSG_KEX_DH_GEX_REQUEST(%u<%u<%u) sent",
 		    min, nbits, max);
diff --git a/openssh-6.6p1/kexgexs.c b/openssh-6.6p1/kexgexs.c
--- a/openssh-6.6p1/kexgexs.c
+++ b/openssh-6.6p1/kexgexs.c
@@ -45,18 +45,17 @@
 #include "dh.h"
 #include "ssh2.h"
 #include "compat.h"
 #ifdef GSSAPI
 #include "ssh-gss.h"
 #endif
 #include "monitor_wrap.h"
 
-/* import from dh.c */
-extern int dh_grp_min;
+#include "fips.h"
 
 void
 kexgex_server(Kex *kex)
 {
 	BIGNUM *shared_secret = NULL, *dh_client_pub = NULL;
 	Key *server_host_public, *server_host_private;
 	DH *dh;
 	u_char *kbuf, *hash, *signature = NULL, *server_host_key_blob = NULL;
@@ -74,26 +73,26 @@ kexgex_server(Kex *kex)
 
 	type = packet_read();
 	switch (type) {
 	case SSH2_MSG_KEX_DH_GEX_REQUEST:
 		debug("SSH2_MSG_KEX_DH_GEX_REQUEST received");
 		omin = min = packet_get_int();
 		onbits = nbits = packet_get_int();
 		omax = max = packet_get_int();
-		min = MAX(dh_grp_min, min);
+		min = MAX(fips_dh_grp_min(), min);
 		max = MIN(DH_GRP_MAX, max);
-		nbits = MAX(dh_grp_min, nbits);
+		nbits = MAX(fips_dh_grp_min(), nbits);
 		nbits = MIN(DH_GRP_MAX, nbits);
 		break;
 	case SSH2_MSG_KEX_DH_GEX_REQUEST_OLD:
 		debug("SSH2_MSG_KEX_DH_GEX_REQUEST_OLD received");
 		onbits = nbits = packet_get_int();
 		/* unused for old GEX */
-		omin = min = dh_grp_min;
+		omin = min = fips_dh_grp_min();
 		omax = max = DH_GRP_MAX;
 		break;
 	default:
 		fatal("protocol error during kex, no DH_GEX_REQUEST: %d", type);
 	}
 	packet_check_eom();
 
 	if (omax < omin || onbits < omin || omax < onbits) {
diff --git a/openssh-6.6p1/key.c b/openssh-6.6p1/key.c
--- a/openssh-6.6p1/key.c
+++ b/openssh-6.6p1/key.c
@@ -53,16 +53,18 @@
 #include "rsa.h"
 #include "uuencode.h"
 #include "buffer.h"
 #include "log.h"
 #include "misc.h"
 #include "ssh2.h"
 #include "digest.h"
 
+#include "fips.h"
+
 static int to_blob(const Key *, u_char **, u_int *, int);
 static Key *key_from_blob2(const u_char *, u_int, int);
 
 static struct KeyCert *
 cert_new(void)
 {
 	struct KeyCert *cert;
 
@@ -664,16 +666,19 @@ key_fp_type_select(void)
 				error("invalid key type in environment variable "
 				    SSH_FP_TYPE_ENVVAR ": '%s' - falling back to MD5.",
 				    env);
 				fp = SSH_FP_MD5;
 			}
 		} else
 			fp = SSH_FP_MD5;
 
+		if (fips_mode())
+			fp = fips_correct_fp_type(fp);
+
 		fp_defined = 1;
    }
    return fp;
 }
 
 /* 
  * string lengths must be less or equal to SSH_FP_TYPE_STRLEN (defined in
  * key.h) as to fit into the fingerprint string buffer
diff --git a/openssh-6.6p1/mac.c b/openssh-6.6p1/mac.c
--- a/openssh-6.6p1/mac.c
+++ b/openssh-6.6p1/mac.c
@@ -39,33 +39,35 @@
 #include "kex.h"
 #include "mac.h"
 #include "misc.h"
 
 #include "digest.h"
 #include "hmac.h"
 #include "umac.h"
 
+#include "fips.h"
+
 #include "openbsd-compat/openssl-compat.h"
 
 #define SSH_DIGEST	1	/* SSH_DIGEST_XXX */
 #define SSH_UMAC	2	/* UMAC (not integrated with OpenSSL) */
 #define SSH_UMAC128	3
 
 struct macalg {
 	char		*name;
 	int		type;
 	int		alg;
 	int		truncatebits;	/* truncate digest if != 0 */
 	int		key_len;	/* just for UMAC */
 	int		len;		/* just for UMAC */
 	int		etm;		/* Encrypt-then-MAC */
 };
 
-static const struct macalg macs[] = {
+static const struct macalg macs_all[] = {
 	/* 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
 	{ "hmac-md5",				SSH_DIGEST, SSH_DIGEST_MD5, 0, 0, 0, 0 },
@@ -86,25 +88,59 @@ static const struct macalg macs[] = {
 	{ "hmac-md5-96-etm@openssh.com",	SSH_DIGEST, SSH_DIGEST_MD5, 96, 0, 0, 1 },
 	{ "hmac-ripemd160-etm@openssh.com",	SSH_DIGEST, SSH_DIGEST_RIPEMD160, 0, 0, 0, 1 },
 	{ "umac-64-etm@openssh.com",		SSH_UMAC, 0, 0, 128, 64, 1 },
 	{ "umac-128-etm@openssh.com",		SSH_UMAC128, 0, 0, 128, 128, 1 },
 
 	{ NULL,					0, 0, 0, 0, 0, 0 }
 };
 
+static const struct macalg macs_fips140_2[] = {
+	/* Encrypt-and-MAC (encrypt-and-authenticate) variants */
+	{ "hmac-sha1",				SSH_DIGEST, SSH_DIGEST_SHA1, 0, 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 */
+	{ "hmac-sha1-etm@openssh.com",		SSH_DIGEST, SSH_DIGEST_SHA1, 0, 0, 0, 1 },
+#ifdef HAVE_EVP_SHA256
+	{ "hmac-sha2-256-etm@openssh.com",	SSH_DIGEST, SSH_DIGEST_SHA256, 0, 0, 0, 1 },
+	{ "hmac-sha2-512-etm@openssh.com",	SSH_DIGEST, SSH_DIGEST_SHA512, 0, 0, 0, 1 },
+#endif
+	{ NULL,					0, 0, 0, 0, 0, 0 }
+};
+
+/* Returns array of macs available depending on selected FIPS mode */
+static const struct macalg *
+fips_select_macs(void)
+{
+	int fips = fips_mode();
+	switch (fips) {
+		case 0:
+			return macs_all;
+		case 1:
+			return macs_fips140_2;
+		default:
+			/* should not be reached */
+			fatal("Fatal error: incorrect FIPS mode '%i' at %s:%u",
+			    fips, __FILE__, __LINE__);
+	}
+}
+
 /* Returns a list of supported MACs separated by the specified char. */
 char *
 mac_alg_list(char sep)
 {
 	char *ret = NULL;
 	size_t nlen, rlen = 0;
 	const struct macalg *m;
 
-	for (m = macs; m->name != NULL; m++) {
+	for (m = fips_select_macs(); m->name != NULL; m++) {
 		if (ret != NULL)
 			ret[rlen++] = sep;
 		nlen = strlen(m->name);
 		ret = xrealloc(ret, 1, rlen + nlen + 2);
 		memcpy(ret + rlen, m->name, nlen + 1);
 		rlen += nlen;
 	}
 	return ret;
@@ -128,17 +164,17 @@ mac_setup_by_alg(Mac *mac, const struct 
 	mac->etm = macalg->etm;
 }
 
 int
 mac_setup(Mac *mac, char *name)
 {
 	const struct macalg *m;
 
-	for (m = macs; m->name != NULL; m++) {
+	for (m = fips_select_macs(); m->name != NULL; m++) {
 		if (strcmp(name, m->name) != 0)
 			continue;
 		if (mac != NULL) {
 			mac_setup_by_alg(mac, m);
 			debug2("mac_setup: setup %s", name);
 		}
 		return (0);
 	}
diff --git a/openssh-6.6p1/myproposal.h b/openssh-6.6p1/myproposal.h
--- a/openssh-6.6p1/myproposal.h
+++ b/openssh-6.6p1/myproposal.h
@@ -83,16 +83,22 @@
 # define KEX_DEFAULT_KEX \
 	KEX_CURVE25519_METHODS \
 	KEX_ECDH_METHODS \
 	KEX_SHA256_METHODS \
 	"diffie-hellman-group-exchange-sha1," \
 	"diffie-hellman-group14-sha1," \
 	"diffie-hellman-group1-sha1"
 
+# define KEX_DEFAULT_KEX_FIPS140_2 \
+	KEX_ECDH_METHODS \
+	KEX_SHA256_METHODS \
+	"diffie-hellman-group-exchange-sha1," \
+	"diffie-hellman-group14-sha1"
+
 #define	KEX_DEFAULT_PK_ALG	\
 	HOSTKEY_ECDSA_CERT_METHODS \
 	"ssh-ed25519-cert-v01@openssh.com," \
 	"ssh-rsa-cert-v01@openssh.com," \
 	"ssh-dss-cert-v01@openssh.com," \
 	"ssh-rsa-cert-v00@openssh.com," \
 	"ssh-dss-cert-v00@openssh.com," \
 	HOSTKEY_ECDSA_METHODS \
@@ -104,16 +110,21 @@
 
 #define	KEX_DEFAULT_ENCRYPT \
 	"aes128-ctr,aes192-ctr,aes256-ctr," \
 	"arcfour256,arcfour128," \
 	AESGCM_CIPHER_MODES \
 	"chacha20-poly1305@openssh.com," \
 	"aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc," \
 	"aes192-cbc,aes256-cbc,arcfour,rijndael-cbc@lysator.liu.se"
+#define	KEX_DEFAULT_ENCRYPT_FIPS140_2 \
+	"aes128-ctr,aes192-ctr,aes256-ctr," \
+	AESGCM_CIPHER_MODES \
+	"aes128-cbc,3des-cbc," \
+	"aes192-cbc,aes256-cbc,rijndael-cbc@lysator.liu.se"
 
 #define	KEX_DEFAULT_MAC \
 	"hmac-md5-etm@openssh.com," \
 	"hmac-sha1-etm@openssh.com," \
 	"umac-64-etm@openssh.com," \
 	"umac-128-etm@openssh.com," \
 	"hmac-sha2-256-etm@openssh.com," \
 	"hmac-sha2-512-etm@openssh.com," \
@@ -124,16 +135,22 @@
 	"hmac-sha1," \
 	"umac-64@openssh.com," \
 	"umac-128@openssh.com," \
 	SHA2_HMAC_MODES \
 	"hmac-ripemd160," \
 	"hmac-ripemd160@openssh.com," \
 	"hmac-sha1-96," \
 	"hmac-md5-96"
+#define	KEX_DEFAULT_MAC_FIPS140_2 \
+	"hmac-sha1-etm@openssh.com," \
+	"hmac-sha2-256-etm@openssh.com," \
+	"hmac-sha2-512-etm@openssh.com," \
+	"hmac-sha1," \
+	SHA2_HMAC_MODES \
 
 #define	KEX_DEFAULT_COMP	"none,zlib@openssh.com,zlib"
 #define	KEX_DEFAULT_LANG	""
 
 
 static char *myproposal[PROPOSAL_MAX] = {
 	KEX_DEFAULT_KEX,
 	KEX_DEFAULT_PK_ALG,
diff --git a/openssh-6.6p1/readconf.c b/openssh-6.6p1/readconf.c
--- a/openssh-6.6p1/readconf.c
+++ b/openssh-6.6p1/readconf.c
@@ -51,16 +51,17 @@
 #include "readconf.h"
 #include "match.h"
 #include "misc.h"
 #include "buffer.h"
 #include "kex.h"
 #include "mac.h"
 #include "uidswap.h"
 #include "dh.h"
+#include "fips.h"
 
 /* Format of the configuration file:
 
    # Configuration data is parsed as follows:
    #  1. command line options
    #  2. user-specific file
    #  3. system-wide file
    # Any configuration value is only changed the first time it is set.
@@ -1668,19 +1669,20 @@ fill_default_options(Options * options)
 		options->number_of_password_prompts = 3;
 	/* Selected in ssh_login(). */
 	if (options->cipher == -1)
 		options->cipher = SSH_CIPHER_NOT_SET;
 	/* options->ciphers, default set in myproposals.h */
 	/* options->macs, default set in myproposals.h */
 	/* options->kex_algorithms, default set in myproposals.h */
 	if (options->kex_dhmin == -1)
-		options->kex_dhmin = DH_GRP_MIN_RFC;
+		options->kex_dhmin = fips_dh_grp_min();
 	else {
-		options->kex_dhmin = MAX(options->kex_dhmin, DH_GRP_MIN_RFC);
+		options->kex_dhmin = MAX(options->kex_dhmin,
+		    fips_mode() ? DH_GRP_MIN_FIPS : DH_GRP_MIN_RFC);
 		options->kex_dhmin = MIN(options->kex_dhmin, DH_GRP_MAX);
 	}
 	dh_grp_min = options->kex_dhmin;
 	/* options->hostkeyalgorithms, default set in myproposals.h */
 	if (options->protocol == SSH_PROTO_UNKNOWN)
 		options->protocol = SSH_PROTO_2;
 	if (options->num_identity_files == 0) {
 		if (options->protocol & SSH_PROTO_1) {
diff --git a/openssh-6.6p1/servconf.c b/openssh-6.6p1/servconf.c
--- a/openssh-6.6p1/servconf.c
+++ b/openssh-6.6p1/servconf.c
@@ -50,16 +50,17 @@
 #include "match.h"
 #include "channels.h"
 #include "groupaccess.h"
 #include "canohost.h"
 #include "packet.h"
 #include "hostfile.h"
 #include "auth.h"
 #include "dh.h"
+#include "fips.h"
 
 /* import from dh.c */
 extern int dh_grp_min;
 
 static void add_listen_addr(ServerOptions *, char *, int);
 static void add_one_listen_addr(ServerOptions *, char *, int);
 
 /* Use of privilege separation or not */
@@ -167,19 +168,20 @@ fill_default_server_options(ServerOption
 {
 	/* Portable-specific options */
 	if (options->use_pam == -1)
 		options->use_pam = 0;
 	if (options->use_pam_check_locks == -1)
 		options->use_pam_check_locks = 0;
 
 	if (options->kex_dhmin == -1)
-		options->kex_dhmin = DH_GRP_MIN_RFC;
+		options->kex_dhmin = fips_dh_grp_min();
 	else {
-		options->kex_dhmin = MAX(options->kex_dhmin, DH_GRP_MIN_RFC);
+		options->kex_dhmin = MAX(options->kex_dhmin,
+		    fips_mode() ? DH_GRP_MIN_FIPS : DH_GRP_MIN_RFC);
 		options->kex_dhmin = MIN(options->kex_dhmin, DH_GRP_MAX);
 	}
 	dh_grp_min = options->kex_dhmin;
 	/* Standard Options */
 	if (options->protocol == SSH_PROTO_UNKNOWN)
 		options->protocol = SSH_PROTO_2;
 	if (options->num_host_key_files == 0) {
 		/* fill default hostkeys for protocols */
diff --git a/openssh-6.6p1/ssh-keygen.c b/openssh-6.6p1/ssh-keygen.c
--- a/openssh-6.6p1/ssh-keygen.c
+++ b/openssh-6.6p1/ssh-keygen.c
@@ -49,16 +49,18 @@
 #include "hostfile.h"
 #include "dns.h"
 #include "ssh.h"
 #include "ssh2.h"
 #include "ssh-pkcs11.h"
 #include "atomicio.h"
 #include "krl.h"
 
+#include "fips.h"
+
 /* Number of bits in the RSA/DSA key.  This value can be set on the command line. */
 #define DEFAULT_BITS		2048
 #define DEFAULT_BITS_DSA	1024
 #define DEFAULT_BITS_ECDSA	256
 u_int32_t bits = 0;
 
 /*
  * Flag indicating that we just want to change the passphrase.  This can be
@@ -884,38 +886,53 @@ do_fingerprint(struct passwd *pw)
 		exit(1);
 	}
 	exit(0);
 }
 
 static void
 do_gen_all_hostkeys(struct passwd *pw)
 {
-	struct {
+	struct Key_types {
 		char *key_type;
 		char *key_type_display;
 		char *path;
-	} key_types[] = {
+	};
+	struct Key_types key_types_all[] = {
 		{ "rsa1", "RSA1", _PATH_HOST_KEY_FILE },
 		{ "rsa", "RSA" ,_PATH_HOST_RSA_KEY_FILE },
 		{ "dsa", "DSA", _PATH_HOST_DSA_KEY_FILE },
 #ifdef OPENSSL_HAS_ECC
 		{ "ecdsa", "ECDSA",_PATH_HOST_ECDSA_KEY_FILE },
 #endif
 		{ "ed25519", "ED25519",_PATH_HOST_ED25519_KEY_FILE },
 		{ NULL, NULL, NULL }
 	};
+	struct Key_types key_types_fips140_2[] = {
+		{ "rsa", "RSA" ,_PATH_HOST_RSA_KEY_FILE },
+#ifdef OPENSSL_HAS_ECC
+		{ "ecdsa", "ECDSA",_PATH_HOST_ECDSA_KEY_FILE },
+#endif
+		{ NULL, NULL, NULL }
+	};
+	struct Key_types *key_types;
 
 	int first = 0;
 	struct stat st;
 	Key *private, *public;
 	char comment[1024];
 	int i, type, fd;
 	FILE *f;
 
+	if (fips_mode()) {
+		key_types = key_types_fips140_2;
+	} else {
+		key_types = key_types_all;
+	}
+
 	for (i = 0; key_types[i].key_type; i++) {
 		if (stat(key_types[i].path, &st) == 0)
 			continue;
 		if (errno != ENOENT) {
 			printf("Could not stat %s: %s", key_types[i].path,
 			    strerror(errno));
 			first = 0;
 			continue;
@@ -2570,16 +2587,23 @@ main(int argc, char **argv)
 		do_gen_all_hostkeys(pw);
 		return (0);
 	}
 
 	if (key_type_name == NULL)
 		key_type_name = "rsa";
 
 	type = key_type_from_name(key_type_name);
+
+	/* protocol v1 is not allowed in FIPS mode, DSA is not acceptable because
+	 * it has to be 1024 bit due to RFC 4253 using SHA-1 which implies 1024 bit
+	 * keys due to FIPS-186 specification for DSS */
+	if (fips_mode() && (type == KEY_RSA1 || type == KEY_DSA))
+		fatal("Key type %s not alowed in FIPS mode", key_type_name);
+	
 	type_bits_valid(type, &bits);
 
 	if (!quiet)
 		printf("Generating public/private %s key pair.\n", key_type_name);
 	private = key_generate(type, bits);
 	if (private == NULL) {
 		fprintf(stderr, "key_generate failed\n");
 		exit(1);
diff --git a/openssh-6.6p1/ssh.c b/openssh-6.6p1/ssh.c
--- a/openssh-6.6p1/ssh.c
+++ b/openssh-6.6p1/ssh.c
@@ -100,16 +100,18 @@
 #include "mac.h"
 #include "sshpty.h"
 #include "match.h"
 #include "msg.h"
 #include "uidswap.h"
 #include "roaming.h"
 #include "version.h"
 
+#include "fips.h"
+
 #ifdef ENABLE_PKCS11
 #include "ssh-pkcs11.h"
 #endif
 
 extern char *__progname;
 
 /* Saves a copy of argv for setproctitle emulation */
 #ifndef HAVE_SETPROCTITLE
@@ -499,16 +501,18 @@ main(int ac, char **av)
 	logfile = NULL;
 	argv0 = av[0];
 
  again:
 	while ((opt = getopt(ac, av, "1246ab:c:e:fgi:kl:m:no:p:qstvx"
 	    "ACD:E:F:I:KL:MNO:PQ:R:S:TVw:W:XYy")) != -1) {
 		switch (opt) {
 		case '1':
+			if (fips_mode())
+				fatal("Protocol 1 not allowed in the FIPS mode.");
 			options.protocol = SSH_PROTO_1;
 			break;
 		case '2':
 			options.protocol = SSH_PROTO_2;
 			break;
 		case '4':
 			options.address_family = AF_INET;
 			break;
@@ -927,16 +931,22 @@ main(int ac, char **av)
 	if (strcasecmp(host_arg, host) != 0) {
 		debug("Hostname has changed; re-reading configuration");
 		process_config_files(pw);
 	}
 
 	/* Fill configuration defaults. */
 	fill_default_options(&options);
 
+	if (fips_mode()) {
+		options.protocol &= SSH_PROTO_2;
+		if (options.protocol == 0)
+			fatal("Protocol 2 disabled by configuration but required in the FIPS mode");
+	}
+
 	if (options.port == 0)
 		options.port = default_ssh_port();
 	channel_set_af(options.address_family);
 
 	/* Tidy and check options */
 	if (options.host_key_alias != NULL)
 		lowercase(options.host_key_alias);
 	if (options.proxy_command != NULL &&
diff --git a/openssh-6.6p1/ssh_config.0 b/openssh-6.6p1/ssh_config.0
--- a/openssh-6.6p1/ssh_config.0
+++ b/openssh-6.6p1/ssh_config.0
@@ -533,16 +533,19 @@ DESCRIPTION
              only know short DH group parameters.
 
              Note, that while by default this option is set to 1024 to maintain
              maximum backward compatibility, using it can severly impact
              security and thus should be viewed as a temporary fix of last
              resort and all efforts should be made to fix the (broken)
              counterparty.
 
+             In the FIPS mode the FIPS standard takes precedence over RFC and
+             forces the minimum to a higher value, currently 2048 bits.
+
      LocalCommand
              Specifies a command to execute on the local machine after
              successfully connecting to the server.  The command string
              extends to the end of the line, and is executed with the user's
              shell.  The following escape character substitutions will be
              performed: `%d' (local user's home directory), `%h' (remote host
              name), `%l' (local host name), `%n' (host name as provided on the
              command line), `%p' (remote port), `%r' (remote user name) or
diff --git a/openssh-6.6p1/ssh_config.5 b/openssh-6.6p1/ssh_config.5
--- a/openssh-6.6p1/ssh_config.5
+++ b/openssh-6.6p1/ssh_config.5
@@ -908,16 +908,19 @@ than the current minimum, down to the RF
 Using this option may be needed when connecting to servers that
 only know short DH group parameters.
 .Pp
 Note, that while by default this option is set to 1024 to maintain
 maximum backward compatibility, using it can severly impact
 security and thus should be viewed as a temporary fix of last
 resort and all efforts should be made to fix the (broken)
 counterparty.
+.Pp
+In the FIPS mode the FIPS standard takes precedence over RFC and
+forces the minimum to a higher value, currently 2048 bits.
 .It Cm LocalCommand
 Specifies a command to execute on the local machine after successfully
 connecting to the server.
 The command string extends to the end of the line, and is executed with
 the user's shell.
 The following escape character substitutions will be performed:
 .Ql %d
 (local user's home directory),
diff --git a/openssh-6.6p1/sshconnect2.c b/openssh-6.6p1/sshconnect2.c
--- a/openssh-6.6p1/sshconnect2.c
+++ b/openssh-6.6p1/sshconnect2.c
@@ -66,16 +66,18 @@
 #include "match.h"
 #include "dispatch.h"
 #include "canohost.h"
 #include "msg.h"
 #include "pathnames.h"
 #include "uidswap.h"
 #include "hostfile.h"
 
+#include "fips.h"
+
 #ifdef GSSAPI
 #include "ssh-gss.h"
 #endif
 
 /* import */
 extern char *client_version_string;
 extern char *server_version_string;
 extern Options options;
@@ -163,43 +165,55 @@ ssh_kex2(char *host, struct sockaddr *ho
 
 	if (options.ciphers == (char *)-1) {
 		logit("No valid ciphers for protocol version 2 given, using defaults.");
 		options.ciphers = NULL;
 	}
 	if (options.ciphers != NULL) {
 		myproposal[PROPOSAL_ENC_ALGS_CTOS] =
 		myproposal[PROPOSAL_ENC_ALGS_STOC] = options.ciphers;
+	} else if (fips_mode()) {
+        /* TODO: use intersection of FIPS ciphers and those requested in
+         * configuration */
+		myproposal[PROPOSAL_ENC_ALGS_CTOS] =
+		myproposal[PROPOSAL_ENC_ALGS_STOC] = KEX_DEFAULT_ENCRYPT_FIPS140_2;
 	}
 	myproposal[PROPOSAL_ENC_ALGS_CTOS] =
 	    compat_cipher_proposal(myproposal[PROPOSAL_ENC_ALGS_CTOS]);
 	myproposal[PROPOSAL_ENC_ALGS_STOC] =
 	    compat_cipher_proposal(myproposal[PROPOSAL_ENC_ALGS_STOC]);
 	if (options.compression) {
 		myproposal[PROPOSAL_COMP_ALGS_CTOS] =
 		myproposal[PROPOSAL_COMP_ALGS_STOC] = "zlib@openssh.com,zlib,none";
 	} else {
 		myproposal[PROPOSAL_COMP_ALGS_CTOS] =
 		myproposal[PROPOSAL_COMP_ALGS_STOC] = "none,zlib@openssh.com,zlib";
 	}
 	if (options.macs != NULL) {
 		myproposal[PROPOSAL_MAC_ALGS_CTOS] =
 		myproposal[PROPOSAL_MAC_ALGS_STOC] = options.macs;
+	} else if (fips_mode()) {
+        /* TODO: use intersection of FIPS macs and those requested in
+         * configuration */
+		myproposal[PROPOSAL_MAC_ALGS_CTOS] =
+		myproposal[PROPOSAL_MAC_ALGS_STOC] = KEX_DEFAULT_MAC_FIPS140_2;
 	}
 	if (options.hostkeyalgorithms != NULL)
 		myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] =
 		    compat_pkalg_proposal(options.hostkeyalgorithms);
 	else {
 		/* Prefer algorithms that we already have keys for */
 		myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] =
 		    compat_pkalg_proposal(
 		    order_hostkeyalgs(host, hostaddr, port));
 	}
 	if (options.kex_algorithms != NULL)
 		myproposal[PROPOSAL_KEX_ALGS] = options.kex_algorithms;
+	else if (fips_mode())
+		myproposal[PROPOSAL_KEX_ALGS] = KEX_DEFAULT_KEX_FIPS140_2;
 	myproposal[PROPOSAL_KEX_ALGS] = compat_kex_proposal(
 	    myproposal[PROPOSAL_KEX_ALGS]);
 
 	if (options.rekey_limit || options.rekey_interval)
 		packet_set_rekey_limits((u_int32_t)options.rekey_limit,
 		    (time_t)options.rekey_interval);
 
 	/* start key exchange */
diff --git a/openssh-6.6p1/sshd.c b/openssh-6.6p1/sshd.c
--- a/openssh-6.6p1/sshd.c
+++ b/openssh-6.6p1/sshd.c
@@ -117,16 +117,18 @@
 #ifdef GSSAPI
 #include "ssh-gss.h"
 #endif
 #include "monitor_wrap.h"
 #include "roaming.h"
 #include "ssh-sandbox.h"
 #include "version.h"
 
+#include "fips.h"
+
 #ifdef LIBWRAP
 #include <tcpd.h>
 #include <syslog.h>
 int allow_severity;
 int deny_severity;
 #endif /* LIBWRAP */
 
 #ifndef O_NOCTTY
@@ -1723,16 +1725,20 @@ main(int ac, char **av)
 		case KEY_ECDSA:
 		case KEY_ED25519:
 			sensitive_data.have_ssh2_key = 1;
 			break;
 		}
 		debug("private host key: #%d type %d %s", i, keytype,
 		    key_type(key ? key : pubkey));
 	}
+	if ((options.protocol & SSH_PROTO_1) && fips_mode()) {
+		logit("Disabling protocol version 1. Not allowed in the FIPS mode.");
+		options.protocol &= ~SSH_PROTO_1;
+	}
 	if ((options.protocol & SSH_PROTO_1) && !sensitive_data.have_ssh1_key) {
 		logit("Disabling protocol version 1. Could not load host key");
 		options.protocol &= ~SSH_PROTO_1;
 	}
 	if ((options.protocol & SSH_PROTO_2) && !sensitive_data.have_ssh2_key) {
 		logit("Disabling protocol version 2. Could not load host key");
 		options.protocol &= ~SSH_PROTO_2;
 	}
@@ -2370,30 +2376,30 @@ do_ssh1_kex(void)
 	}
 	if (rsafail) {
 		int bytes = BN_num_bytes(session_key_int);
 		u_char *buf = xmalloc(bytes);
 		struct ssh_digest_ctx *md;
 
 		logit("do_connection: generating a fake encryption key");
 		BN_bn2bin(session_key_int, buf);
-		if ((md = ssh_digest_start(SSH_DIGEST_MD5)) == NULL ||
+		if ((md = ssh_digest_start(fips_correct_dgst(SSH_DIGEST_MD5))) == NULL ||
 		    ssh_digest_update(md, buf, bytes) < 0 ||
 		    ssh_digest_update(md, sensitive_data.ssh1_cookie,
 		    SSH_SESSION_KEY_LENGTH) < 0 ||
 		    ssh_digest_final(md, session_key, sizeof(session_key)) < 0)
-			fatal("%s: md5 failed", __func__);
+			fatal("%s: hash failed", __func__);
 		ssh_digest_free(md);
-		if ((md = ssh_digest_start(SSH_DIGEST_MD5)) == NULL ||
+		if ((md = ssh_digest_start(fips_correct_dgst(SSH_DIGEST_MD5))) == NULL ||
 		    ssh_digest_update(md, session_key, 16) < 0 ||
 		    ssh_digest_update(md, sensitive_data.ssh1_cookie,
 		    SSH_SESSION_KEY_LENGTH) < 0 ||
 		    ssh_digest_final(md, session_key + 16,
 		    sizeof(session_key) - 16) < 0)
-			fatal("%s: md5 failed", __func__);
+			fatal("%s: hash failed", __func__);
 		ssh_digest_free(md);
 		explicit_bzero(buf, bytes);
 		free(buf);
 		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();
@@ -2441,35 +2447,43 @@ sshd_hostkey_sign(Key *privkey, Key *pub
 static void
 do_ssh2_kex(void)
 {
 	Kex *kex;
 
 	if (options.ciphers != NULL) {
 		myproposal[PROPOSAL_ENC_ALGS_CTOS] =
 		myproposal[PROPOSAL_ENC_ALGS_STOC] = options.ciphers;
+	} else if (fips_mode()) {
+		myproposal[PROPOSAL_ENC_ALGS_CTOS] =
+		myproposal[PROPOSAL_ENC_ALGS_STOC] = KEX_DEFAULT_ENCRYPT_FIPS140_2;
 	}
 	myproposal[PROPOSAL_ENC_ALGS_CTOS] =
 	    compat_cipher_proposal(myproposal[PROPOSAL_ENC_ALGS_CTOS]);
 	myproposal[PROPOSAL_ENC_ALGS_STOC] =
 	    compat_cipher_proposal(myproposal[PROPOSAL_ENC_ALGS_STOC]);
 
 	if (options.macs != NULL) {
 		myproposal[PROPOSAL_MAC_ALGS_CTOS] =
 		myproposal[PROPOSAL_MAC_ALGS_STOC] = options.macs;
+	} else if (fips_mode()) {
+		myproposal[PROPOSAL_MAC_ALGS_CTOS] =
+		myproposal[PROPOSAL_MAC_ALGS_STOC] = KEX_DEFAULT_MAC_FIPS140_2;
 	}
 	if (options.compression == COMP_NONE) {
 		myproposal[PROPOSAL_COMP_ALGS_CTOS] =
 		myproposal[PROPOSAL_COMP_ALGS_STOC] = "none";
 	} else if (options.compression == COMP_DELAYED) {
 		myproposal[PROPOSAL_COMP_ALGS_CTOS] =
 		myproposal[PROPOSAL_COMP_ALGS_STOC] = "none,zlib@openssh.com";
 	}
 	if (options.kex_algorithms != NULL)
 		myproposal[PROPOSAL_KEX_ALGS] = options.kex_algorithms;
+	else if (fips_mode())
+		myproposal[PROPOSAL_KEX_ALGS] = KEX_DEFAULT_KEX_FIPS140_2;
 
 	myproposal[PROPOSAL_KEX_ALGS] = compat_kex_proposal(
 	    myproposal[PROPOSAL_KEX_ALGS]);
 
 	if (options.rekey_limit || options.rekey_interval)
 		packet_set_rekey_limits((u_int32_t)options.rekey_limit,
 		    (time_t)options.rekey_interval);
 
diff --git a/openssh-6.6p1/sshd_config.0 b/openssh-6.6p1/sshd_config.0
--- a/openssh-6.6p1/sshd_config.0
+++ b/openssh-6.6p1/sshd_config.0
@@ -424,16 +424,19 @@ DESCRIPTION
              clients only know short DH group parameters.
 
              Note, that while by default this option is set to 1024 to maintain
              maximum backward compatibility, using it can severly impact
              security and thus should be viewed as a temporary fix of last
              resort and all efforts should be made to fix the (broken)
              counterparty.
 
+             In the FIPS mode the FIPS standard takes precedence over RFC and
+             forces the minimum to a higher value, currently 2048 bits.
+
      KeyRegenerationInterval
              In protocol version 1, the ephemeral server key is automatically
              regenerated after this many seconds (if it has been used).  The
              purpose of regeneration is to prevent decrypting captured
              sessions by later breaking into the machine and stealing the
              keys.  The key is never stored anywhere.  If the value is 0, the
              key is never regenerated.  The default is 3600 (seconds).
 
diff --git a/openssh-6.6p1/sshd_config.5 b/openssh-6.6p1/sshd_config.5
--- a/openssh-6.6p1/sshd_config.5
+++ b/openssh-6.6p1/sshd_config.5
@@ -691,16 +691,19 @@ than the current minimum, down to the RF
 Using this option may be needed when some of the connectiong
 clients only know short DH group parameters.
 .Pp
 Note, that while by default this option is set to 1024 to maintain
 maximum backward compatibility, using it can severly impact
 security and thus should be viewed as a temporary fix of last
 resort and all efforts should be made to fix the (broken)
 counterparty.
+.Pp
+In the FIPS mode the FIPS standard takes precedence over RFC and
+forces the minimum to a higher value, currently 2048 bits.
 .It Cm KeyRegenerationInterval
 In protocol version 1, the ephemeral server key is automatically regenerated
 after this many seconds (if it has been used).
 The purpose of regeneration is to prevent
 decrypting captured sessions by later breaking into the machine and
 stealing the keys.
 The key is never stored anywhere.
 If the value is 0, the key is never regenerated.
openSUSE Build Service is sponsored by