# comply with FIPS 140-2 by using only approved crypto algorithms
# when OpenSSL is detected to be running in FIPS mode
#
# HG changeset patch
# Parent 38b7075a354e9a37d329069f0520917471d20789

diff --git a/openssh-6.2p2/Makefile.in b/openssh-6.2p2/Makefile.in
--- a/openssh-6.2p2/Makefile.in
+++ b/openssh-6.2p2/Makefile.in
@@ -72,17 +72,17 @@ LIBSSH_OBJS=authfd.o authfile.o bufaux.o
 	cipher-bf1.o cipher-ctr.o cipher-3des1.o cleanup.o \
 	compat.o compress.o crc32.o deattack.o fatal.o hostfile.o \
 	log.o match.o md-sha256.o moduli.o nchan.o packet.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 \
-	jpake.o schnorr.o ssh-pkcs11.o krl.o auditstub.o
+	jpake.o schnorr.o ssh-pkcs11.o krl.o auditstub.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.2p2/auth-rsa.c b/openssh-6.2p2/auth-rsa.c
--- a/openssh-6.2p2/auth-rsa.c
+++ b/openssh-6.2p2/auth-rsa.c
@@ -15,17 +15,17 @@
  */
 
 #include "includes.h"
 
 #include <sys/types.h>
 #include <sys/stat.h>
 
 #include <openssl/rsa.h>
-#include <openssl/md5.h>
+#include <openssl/evp.h>
 
 #include <pwd.h>
 #include <stdio.h>
 #include <stdarg.h>
 #include <string.h>
 
 #include "xmalloc.h"
 #include "rsa.h"
@@ -42,16 +42,17 @@
 #include "hostfile.h"
 #include "auth.h"
 #ifdef GSSAPI
 #include "ssh-gss.h"
 #endif
 #include "monitor_wrap.h"
 #include "ssh.h"
 #include "misc.h"
+#include "fips.h"
 
 /* import */
 extern ServerOptions options;
 
 /*
  * Session identifier that is used to bind key exchange and authentication
  * responses to a particular session.
  */
@@ -83,45 +84,54 @@ 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[MAX_HASH_LEN])
 {
-	u_char buf[32], mdbuf[16];
-	MD5_CTX md;
+	u_char buf[2 * MAX_HASH_LEN], mdbuf[MAX_HASH_LEN];
+	const EVP_MD *evp_md;
+	EVP_MD_CTX md;
 	int len, rv;
 #ifdef SSH_AUDIT_EVENTS
 	char *fp;
 #endif
+	int hash_len;
 
 	/* don't allow short keys */
 	if (BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) {
 		error("auth_rsa_verify_response: RSA modulus too small: %d < minimum %d bits",
 		    BN_num_bits(key->rsa->n), SSH_RSA_MINIMUM_MODULUS_SIZE);
 		return (0);
 	}
 
-	/* The response is MD5 of decrypted challenge plus session id. */
+	hash_len = fips_hash_len(fips_hash_min());
+
+	/* 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 || len > (2 * hash_len))
 		fatal("auth_rsa_verify_response: bad challenge length %d", len);
-	memset(buf, 0, 32);
-	BN_bn2bin(challenge, buf + 32 - len);
-	MD5_Init(&md);
-	MD5_Update(&md, buf, 32);
-	MD5_Update(&md, session_id, 16);
-	MD5_Final(mdbuf, &md);
+	memset(buf, 0, sizeof(buf));
+	BN_bn2bin(challenge, buf + 2 * hash_len - len);
+
+	if ((evp_md = fips_EVP_get_digest_min()) == NULL) {
+		fatal("auth_rsa_verify_response: fips_EVP_get_digest_min failed");
+	}
+	EVP_DigestInit(&md, evp_md);
+	EVP_DigestUpdate(&md, buf, 2 * hash_len);
+	EVP_DigestUpdate(&md, session_id, hash_len);
+	EVP_DigestFinal(&md, mdbuf, NULL);
 
 	/* Verify that the response is the original challenge. */
-	rv = timingsafe_bcmp(response, mdbuf, 16) == 0;
+	rv = timingsafe_bcmp(response, mdbuf, hash_len) == 0;
 
 #ifdef SSH_AUDIT_EVENTS
 	fp = key_fingerprint(key, key_fp_type_select(), SSH_FP_HEX);
 	if (audit_keyusage(1, "ssh-rsa1", RSA_size(key->rsa) * 8, fp, rv) == 0) {
 		debug("unsuccessful audit");
 		rv = 0;
 	}
 	xfree(fp);
@@ -135,17 +145,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[MAX_HASH_LEN];
 	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. */
@@ -155,17 +165,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 < fips_hash_len(fips_hash_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.2p2/cipher-ctr.c b/openssh-6.2p2/cipher-ctr.c
--- a/openssh-6.2p2/cipher-ctr.c
+++ b/openssh-6.2p2/cipher-ctr.c
@@ -21,16 +21,17 @@
 
 #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
 
@@ -134,13 +135,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.2p2/cipher.c b/openssh-6.2p2/cipher.c
--- a/openssh-6.2p2/cipher.c
+++ b/openssh-6.2p2/cipher.c
@@ -42,16 +42,17 @@
 #include <openssl/md5.h>
 
 #include <string.h>
 #include <stdarg.h>
 
 #include "xmalloc.h"
 #include "log.h"
 #include "cipher.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);
 
@@ -81,18 +82,57 @@ struct Cipher ciphers[] = {
 	{ "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 }
 };
 
+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 }
+};
+
 /*--*/
 
+static struct Cipher *
+fips_select_ciphers(void)
+{
+	int fips = fips_mode();
+	switch (fips) {
+		case 0:
+			return ciphers;
+		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;
+	}
+}
+
 u_int
 cipher_blocksize(const Cipher *c)
 {
 	return (c->block_size);
 }
 
 u_int
 cipher_keylen(const Cipher *c)
@@ -135,27 +175,27 @@ cipher_mask_ssh1(int client)
 	}
 	return mask;
 }
 
 Cipher *
 cipher_by_name(const char *name)
 {
 	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;
 }
 
 Cipher *
 cipher_by_number(int id)
 {
 	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)
@@ -189,17 +229,17 @@ ciphers_valid(const char *names)
  */
 
 int
 cipher_number(const char *name)
 {
 	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)
 {
@@ -352,24 +392,29 @@ 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, Cipher *cipher,
     const char *passphrase, int do_encrypt)
 {
-	MD5_CTX md;
-	u_char digest[16];
+	const EVP_MD *evp_md;
+	EVP_MD_CTX md;
+	u_char digest[MAX_HASH_LEN];
+	int dlen;
 
-	MD5_Init(&md);
-	MD5_Update(&md, (const u_char *)passphrase, strlen(passphrase));
-	MD5_Final(digest, &md);
-
-	cipher_init(cc, cipher, digest, 16, NULL, 0, do_encrypt);
+	if ((evp_md = fips_EVP_get_digest_min()) == NULL) {
+		fatal("auth_rsa_verify_response: fips_EVP_get_digest_min failed");
+	}
+	EVP_DigestInit(&md, evp_md);
+	EVP_DigestUpdate(&md, (const u_char *)passphrase, strlen(passphrase));
+	EVP_DigestFinal(&md, digest, &dlen);
+	
+	cipher_init(cc, cipher, digest, dlen, NULL, 0, do_encrypt);
 
 	memset(digest, 0, sizeof(digest));
 	memset(&md, 0, sizeof(md));
 }
 
 /*
  * Exports an IV from the CipherContext required to export the key
  * state back from the unprivileged child to the privileged parent
diff --git a/openssh-6.2p2/fips.c b/openssh-6.2p2/fips.c
new file mode 100644
--- /dev/null
+++ b/openssh-6.2p2/fips.c
@@ -0,0 +1,176 @@
+/*
+ * 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 "fips.h"
+
+#include "log.h"
+
+enum hgp {
+	HGP_LEN,
+	HGP_NID
+};
+
+static int fips_state = -1;
+
+static const struct Hashes {
+	enum hash_type ht;
+	int byte_length;
+	int nid;
+} hashes[] = {
+	{ HASH_MD5,    HASH_LEN_MD5,    NID_md5 },
+	{ HASH_SHA1,   HASH_LEN_SHA1,   NID_sha1 },
+	{ HASH_SHA256, HASH_LEN_SHA256, NID_sha256 },
+	{ HASH_err,    -1,              -1 }
+};
+
+int
+fips_mode()
+{
+	if (-1 == fips_state) {
+		fips_state = FIPS_mode();
+		if (fips_state)
+			debug("FIPS mode initialized");
+	}
+	return fips_state;
+}
+
+static int
+fips_hash_get_param(enum hash_type ht, int param)
+{
+	int i;
+	for (i = 0; i < HASH_err; i++) {
+		if (hashes[i].ht == ht) {
+			switch (param) {
+				case HGP_LEN:
+					return hashes[i].byte_length;
+				case HGP_NID:
+					return hashes[i].nid;
+				default:
+					fatal("Fatal error: internal error at %s:%u",
+					    __FILE__, __LINE__);
+			}
+		}
+	}
+	/* should not be reached */
+	fatal("Fatal error: incorrect hash type '%i' at %s:%u",
+	    ht, __FILE__, __LINE__);
+//    return -1;
+}
+
+int
+fips_hash2nid(enum hash_type ht)
+{
+	return fips_hash_get_param(ht, HGP_NID);
+}
+
+int
+fips_hash_len(enum hash_type ht)
+{
+	return fips_hash_get_param(ht, HGP_LEN);
+}
+
+void
+fips_correct_fp_type(enum fp_type *fp)
+{
+	int fips;
+
+	fips = fips_mode();
+	switch (fips) {
+		case 0:
+			break;
+		case 1:
+			if (SSH_FP_MD5 == *fp) {
+				*fp = SSH_FP_SHA1;
+				logit("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;
+}
+
+void
+fips_correct_nid(int *nid)
+{
+	int fips;
+
+	fips = fips_mode();
+	switch (fips) {
+		case 0:
+			break;
+		case 1:
+			if (NID_md5 == *nid) {
+				*nid = NID_sha1;
+				logit("MD5 not allowed in FIPS 140-2 mode, "
+				    "using SHA-1 for hashing instead.");
+			}
+			break;
+		default:
+			/* should not be reached */
+			fatal("Fatal error: incorrect FIPS mode '%i' at %s:%u",
+			    fips, __FILE__, __LINE__);
+	}
+
+	return;
+}
+
+enum hash_type
+fips_hash_min(void)
+{
+	int fips;
+	enum hash_type ht;
+
+	fips = fips_mode();
+	switch (fips) {
+		case 0:
+			ht = HASH_MD5;
+			break;
+		case 1:
+			ht = HASH_SHA1;
+			break;
+		default:
+			/* should not be reached */
+			fatal("Fatal error: incorrect FIPS mode '%i' at %s:%u",
+			    fips, __FILE__, __LINE__);
+	}
+	return ht;
+}
+
+enum hash_type
+fips_hash_nid_min()
+{
+	return fips_hash2nid(fips_hash_min());
+}
+
+const EVP_MD *
+fips_EVP_get_digest_min(void)
+{
+	return EVP_get_digestbynid(fips_hash_nid_min());
+}
+
diff --git a/openssh-6.2p2/fips.h b/openssh-6.2p2/fips.h
new file mode 100644
--- /dev/null
+++ b/openssh-6.2p2/fips.h
@@ -0,0 +1,54 @@
+/*
+ * 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 <openssl/fips.h>
+#include <openssl/evp.h>
+
+#include "key.h"
+
+#define HASH_LEN_MD5	16
+#define HASH_LEN_SHA1	20
+#define HASH_LEN_SHA256	32
+#define MAX_HASH_LEN	HASH_LEN_SHA256
+
+enum hash_type {
+	HASH_MD5 = 1,
+	HASH_SHA1,
+	HASH_SHA256,
+	HASH_err
+};
+
+int	 fips_mode(void);
+void	 fips_correct_fp_type(enum fp_type *);
+void	 fips_correct_nid(int *);
+
+enum hash_type	 fips_hash_min(void);
+const EVP_MD	*fips_EVP_get_digest_min(void);
+int		 fips_hash2nid(enum hash_type);
+int		 fips_hash_len(enum hash_type);
+
+#endif
+
diff --git a/openssh-6.2p2/key.c b/openssh-6.2p2/key.c
--- a/openssh-6.2p2/key.c
+++ b/openssh-6.2p2/key.c
@@ -49,16 +49,17 @@
 #include "xmalloc.h"
 #include "key.h"
 #include "rsa.h"
 #include "uuencode.h"
 #include "buffer.h"
 #include "log.h"
 #include "misc.h"
 #include "ssh2.h"
+#include "fips.h"
 
 static int to_blob(const Key *, u_char **, u_int *, int);
 
 static struct KeyCert *
 cert_new(void)
 {
 	struct KeyCert *cert;
 
@@ -634,16 +635,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())
+			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.2p2/mac.c b/openssh-6.2p2/mac.c
--- a/openssh-6.2p2/mac.c
+++ b/openssh-6.2p2/mac.c
@@ -36,34 +36,35 @@
 #include "xmalloc.h"
 #include "log.h"
 #include "cipher.h"
 #include "buffer.h"
 #include "key.h"
 #include "kex.h"
 #include "mac.h"
 #include "misc.h"
+#include "fips.h"
 
 #include "umac.h"
 
 #include "openbsd-compat/openssl-compat.h"
 
 #define SSH_EVP		1	/* OpenSSL EVP-based MAC */
 #define SSH_UMAC	2	/* UMAC (not integrated with OpenSSL) */
 #define SSH_UMAC128	3
 
-struct {
+struct Macs {
 	char		*name;
 	int		type;
 	const EVP_MD *	(*mdfunc)(void);
 	int		truncatebits;	/* truncate digest if != 0 */
 	int		key_len;	/* just for UMAC */
 	int		len;		/* just for UMAC */
 	int		etm;		/* Encrypt-then-MAC */
-} macs[] = {
+} macs_all[] = {
 	/* Encrypt-and-MAC (encrypt-and-authenticate) variants */
 	{ "hmac-sha1",				SSH_EVP, EVP_sha1, 0, 0, 0, 0 },
 	{ "hmac-sha1-96",			SSH_EVP, EVP_sha1, 96, 0, 0, 0 },
 #ifdef HAVE_EVP_SHA256
 	{ "hmac-sha2-256",			SSH_EVP, EVP_sha256, 0, 0, 0, 0 },
 	{ "hmac-sha2-512",			SSH_EVP, EVP_sha512, 0, 0, 0, 0 },
 #endif
 	{ "hmac-md5",				SSH_EVP, EVP_md5, 0, 0, 0, 0 },
@@ -84,19 +85,46 @@ struct {
 	{ "hmac-md5-96-etm@openssh.com",	SSH_EVP, EVP_md5, 96, 0, 0, 1 },
 	{ "hmac-ripemd160-etm@openssh.com",	SSH_EVP, EVP_ripemd160, 0, 0, 0, 1 },
 	{ "umac-64-etm@openssh.com",		SSH_UMAC, NULL, 0, 128, 64, 1 },
 	{ "umac-128-etm@openssh.com",		SSH_UMAC128, NULL, 0, 128, 128, 1 },
 
 	{ NULL,					0, NULL, 0, 0, 0, 0 }
 };
 
+struct Macs macs_fips140_2[] = {
+	{ "hmac-sha1",				SSH_EVP, EVP_sha1, 0, 0, 0, 0 },
+#ifdef HAVE_EVP_SHA256
+	{ "hmac-sha2-256",			SSH_EVP, EVP_sha256, 0, 0, 0, 0 },
+	{ "hmac-sha2-512",			SSH_EVP, EVP_sha512, 0, 0, 0, 0 },
+#endif
+	{ NULL,					0, NULL, 0, 0, 0, 0 }
+};
+
+static struct Macs *
+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__);
+//			return NULL;
+	}
+}
+
 static void
 mac_setup_by_id(Mac *mac, int which)
 {
+	struct Macs *macs = fips_select_macs();
 	int evp_len;
 	mac->type = macs[which].type;
 	if (mac->type == SSH_EVP) {
 		mac->evp_md = (*macs[which].mdfunc)();
 		if ((evp_len = EVP_MD_size(mac->evp_md)) <= 0)
 			fatal("mac %s len %d", mac->name, evp_len);
 		mac->key_len = mac->mac_len = (u_int)evp_len;
 	} else {
@@ -107,16 +135,17 @@ mac_setup_by_id(Mac *mac, int which)
 	if (macs[which].truncatebits != 0)
 		mac->mac_len = macs[which].truncatebits / 8;
 	mac->etm = macs[which].etm;
 }
 
 int
 mac_setup(Mac *mac, char *name)
 {
+	struct Macs *macs = fips_select_macs();
 	int i;
 
 	for (i = 0; macs[i].name; i++) {
 		if (strcmp(name, macs[i].name) == 0) {
 			if (mac != NULL)
 				mac_setup_by_id(mac, i);
 			debug2("mac_setup: found %s", name);
 			return (0);
diff --git a/openssh-6.2p2/myproposal.h b/openssh-6.2p2/myproposal.h
--- a/openssh-6.2p2/myproposal.h
+++ b/openssh-6.2p2/myproposal.h
@@ -71,16 +71,20 @@
 	"ssh-dss"
 
 #define	KEX_DEFAULT_ENCRYPT \
 	"aes128-ctr,aes192-ctr,aes256-ctr," \
 	"arcfour256,arcfour128," \
 	"aes128-gcm@openssh.com,aes256-gcm@openssh.com," \
 	"aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc," \
 	"aes192-cbc,aes256-cbc,arcfour,rijndael-cbc@lysator.liu.se"
+#define	KEX_FIPS_140_2_ENCRYPT \
+	"aes128-ctr,aes192-ctr,aes256-ctr," \
+	"aes128-cbc,3des-cbc," \
+	"aes192-cbc,aes256-cbc,rijndael-cbc@lysator.liu.se"
 #ifdef HAVE_EVP_SHA256
 #define	SHA2_HMAC_MODES \
 	"hmac-sha2-256," \
 	"hmac-sha2-512,"
 #else
 # define SHA2_HMAC_MODES
 #endif
 #define	KEX_DEFAULT_MAC \
@@ -97,16 +101,19 @@
 	"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_FIPS_140_2_MAC \
+	"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.2p2/openbsd-compat/bsd-arc4random.c b/openssh-6.2p2/openbsd-compat/bsd-arc4random.c
--- a/openssh-6.2p2/openbsd-compat/bsd-arc4random.c
+++ b/openssh-6.2p2/openbsd-compat/bsd-arc4random.c
@@ -18,34 +18,35 @@
 
 #include <sys/types.h>
 
 #include <string.h>
 #include <stdlib.h>
 #include <stdarg.h>
 
 #include "log.h"
+#include "fips.h"
 
 #ifndef HAVE_ARC4RANDOM
 
 #include <openssl/rand.h>
 #include <openssl/rc4.h>
 #include <openssl/err.h>
 
 /* Size of key to use */
 #define SEED_SIZE 20
 
 /* Number of bytes to reseed after */
 #define REKEY_BYTES	(1 << 24)
 
 static int rc4_ready = 0;
 static RC4_KEY rc4;
 
-unsigned int
-arc4random(void)
+static unsigned int
+arc4random_bsd(void)
 {
 	unsigned int r = 0;
 	static int first_time = 1;
 
 	if (rc4_ready <= 0) {
 		if (first_time)
 			seed_rng();
 		first_time = 0;
@@ -54,18 +55,18 @@ arc4random(void)
 
 	RC4(&rc4, sizeof(r), (unsigned char *)&r, (unsigned char *)&r);
 
 	rc4_ready -= sizeof(r);
 	
 	return(r);
 }
 
-void
-arc4random_stir(void)
+static void
+arc4random_stir_bsd(void)
 {
 	unsigned char rand_buf[SEED_SIZE];
 	int i;
 
 	memset(&rc4, 0, sizeof(rc4));
 	if (RAND_bytes(rand_buf, sizeof(rand_buf)) <= 0)
 		fatal("Couldn't obtain random bytes (error %ld)",
 		    ERR_get_error());
@@ -77,16 +78,63 @@ arc4random_stir(void)
 	 */
 	for(i = 0; i <= 256; i += sizeof(rand_buf))
 		RC4(&rc4, sizeof(rand_buf), rand_buf, rand_buf);
 
 	memset(rand_buf, 0, sizeof(rand_buf));
 
 	rc4_ready = REKEY_BYTES;
 }
+
+static unsigned int
+arc4random_fips(void)
+{
+	unsigned int r = 0;
+	void *rp = &r;
+	static int first_time = 1;
+
+	if (first_time) {
+		seed_rng();
+		first_time = 0;
+	}
+	if (!rc4_ready)
+		arc4random_stir();
+	RAND_bytes(rp, sizeof(r));
+
+	return(r);
+}
+
+static void
+arc4random_stir_fips(void)
+{
+	unsigned char rand_buf[SEED_SIZE];
+
+	if (RAND_bytes(rand_buf, sizeof(rand_buf)) <= 0)
+		fatal("Couldn't obtain random bytes (error %ld)",
+		    ERR_get_error());
+	rc4_ready = 1;
+}
+
+unsigned int
+arc4random(void)
+{
+	if (fips_mode())
+		return arc4random_fips();
+	else
+		return arc4random_bsd();
+}
+
+void
+arc4random_stir(void)
+{
+	if (fips_mode())
+		return arc4random_stir_fips();
+	else
+		return arc4random_stir_bsd();
+}
 #endif /* !HAVE_ARC4RANDOM */
 
 #ifndef HAVE_ARC4RANDOM_BUF
 void
 arc4random_buf(void *_buf, size_t n)
 {
 	size_t i;
 	u_int32_t r = 0;
diff --git a/openssh-6.2p2/ssh-rsa.c b/openssh-6.2p2/ssh-rsa.c
--- a/openssh-6.2p2/ssh-rsa.c
+++ b/openssh-6.2p2/ssh-rsa.c
@@ -27,16 +27,17 @@
 
 #include "xmalloc.h"
 #include "log.h"
 #include "buffer.h"
 #include "key.h"
 #include "compat.h"
 #include "misc.h"
 #include "ssh.h"
+#include "fips.h"
 
 static int openssh_RSA_verify(int, u_char *, u_int, u_char *, u_int, RSA *);
 
 /* RSASSA-PKCS1-v1_5 (PKCS #1 v2.0 signature) with SHA1 */
 int
 ssh_rsa_sign(const Key *key, u_char **sigp, u_int *lenp,
     const u_char *data, u_int datalen)
 {
@@ -48,16 +49,17 @@ ssh_rsa_sign(const Key *key, u_char **si
 	Buffer b;
 
 	if (key == NULL || key->rsa == NULL || (key->type != KEY_RSA &&
 	    key->type != KEY_RSA_CERT && key->type != KEY_RSA_CERT_V00)) {
 		error("ssh_rsa_sign: no RSA key");
 		return -1;
 	}
 	nid = (datafellows & SSH_BUG_RSASIGMD5) ? NID_md5 : NID_sha1;
+	fips_correct_nid(&nid);
 	if ((evp_md = EVP_get_digestbynid(nid)) == NULL) {
 		error("ssh_rsa_sign: EVP_get_digestbynid %d failed", nid);
 		return -1;
 	}
 	EVP_DigestInit(&md, evp_md);
 	EVP_DigestUpdate(&md, data, datalen);
 	EVP_DigestFinal(&md, digest, &dlen);
 
@@ -154,16 +156,17 @@ ssh_rsa_verify(const Key *key, const u_c
 		debug("ssh_rsa_verify: add padding: modlen %u > len %u",
 		    modlen, len);
 		sigblob = xrealloc(sigblob, 1, modlen);
 		memmove(sigblob + diff, sigblob, len);
 		memset(sigblob, 0, diff);
 		len = modlen;
 	}
 	nid = (datafellows & SSH_BUG_RSASIGMD5) ? NID_md5 : NID_sha1;
+	fips_correct_nid(&nid);
 	if ((evp_md = EVP_get_digestbynid(nid)) == NULL) {
 		error("ssh_rsa_verify: EVP_get_digestbynid %d failed", nid);
 		xfree(sigblob);
 		return -1;
 	}
 	EVP_DigestInit(&md, evp_md);
 	EVP_DigestUpdate(&md, data, datalen);
 	EVP_DigestFinal(&md, digest, &dlen);
diff --git a/openssh-6.2p2/ssh.c b/openssh-6.2p2/ssh.c
--- a/openssh-6.2p2/ssh.c
+++ b/openssh-6.2p2/ssh.c
@@ -99,16 +99,17 @@
 #include "kex.h"
 #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 */
@@ -324,16 +325,18 @@ main(int ac, char **av)
 	use_syslog = 0;
 	argv0 = av[0];
 
  again:
 	while ((opt = getopt(ac, av, "1246ab:c:e:fgi:kl:m:no:p:qstvx"
 	    "ACD:F:I:KL:MNO:PR: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;
@@ -781,17 +784,22 @@ main(int ac, char **av)
 		xfree(cp);
 	}
 	if (muxclient_command != 0 && options.control_path == NULL)
 		fatal("No ControlPath specified for \"-O\" command");
 	if (options.control_path != NULL)
 		muxclient(options.control_path);
 
 	timeout_ms = options.connection_timeout * 1000;
-
+	if (FIPS_mode()) {
+		options.protocol &= SSH_PROTO_2;
+		if (options.protocol == 0)
+			fatal("Protocol 2 disabled by configuration but required in the FIPS mode");
+	}
+ 
 	/* Open a connection to the remote host. */
 	if (ssh_connect(host, &hostaddr, options.port,
 	    options.address_family, options.connection_attempts, &timeout_ms,
 	    options.tcp_keep_alive, 
 #ifdef HAVE_CYGWIN
 	    options.use_privileged_port,
 #else
 	    original_effective_uid == 0 && options.use_privileged_port,
diff --git a/openssh-6.2p2/sshconnect2.c b/openssh-6.2p2/sshconnect2.c
--- a/openssh-6.2p2/sshconnect2.c
+++ b/openssh-6.2p2/sshconnect2.c
@@ -67,16 +67,17 @@
 #include "dispatch.h"
 #include "canohost.h"
 #include "msg.h"
 #include "pathnames.h"
 #include "uidswap.h"
 #include "hostfile.h"
 #include "schnorr.h"
 #include "jpake.h"
+#include "fips.h"
 
 #ifdef GSSAPI
 #include "ssh-gss.h"
 #endif
 
 /* import */
 extern char *client_version_string;
 extern char *server_version_string;
@@ -165,31 +166,37 @@ 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()) {
+		myproposal[PROPOSAL_ENC_ALGS_CTOS] =
+		myproposal[PROPOSAL_ENC_ALGS_STOC] = KEX_FIPS_140_2_ENCRYPT;
 	}
 	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()) {
+		myproposal[PROPOSAL_MAC_ALGS_CTOS] =
+		myproposal[PROPOSAL_MAC_ALGS_STOC] = KEX_FIPS_140_2_MAC;
 	}
 	if (options.hostkeyalgorithms != NULL)
 		myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] =
 		    options.hostkeyalgorithms;
 	else {
 		/* Prefer algorithms that we already have keys for */
 		myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] =
 		    order_hostkeyalgs(host, hostaddr, port);
diff --git a/openssh-6.2p2/sshd.c b/openssh-6.2p2/sshd.c
--- a/openssh-6.2p2/sshd.c
+++ b/openssh-6.2p2/sshd.c
@@ -118,16 +118,17 @@
 #ifdef GSSAPI
 #include "ssh-gss.h"
 #endif
 #include "monitor_wrap.h"
 #include "roaming.h"
 #include "audit.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 */
 
@@ -1708,16 +1709,20 @@ main(int ac, char **av)
 		case KEY_DSA:
 		case KEY_ECDSA:
 			sensitive_data.have_ssh2_key = 1;
 			break;
 		}
 		debug("private host key: #%d type %d %s", i, key->type,
 		    key_type(key));
 	}
+	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;
 	}
@@ -2411,25 +2416,31 @@ do_ssh1_kex(void)
 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_FIPS_140_2_ENCRYPT;
 	}
 	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_FIPS_140_2_MAC;
 	}
 	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";
 	}
