openssh/openssh-6.5p1-fips.patch
Petr Cerny 5d4cc441c8 Accepting request 226334 from home:pcerny:factory
- re-enabling the GSSAPI Key Exchange patch 
!!! currently breaks anythng else than Factory

OBS-URL: https://build.opensuse.org/request/show/226334
OBS-URL: https://build.opensuse.org/package/show/network/openssh?expand=0&rev=72
2014-03-17 02:46:40 +00:00

1041 lines
29 KiB
Diff

# 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 df8b01308484dd9227b64c8bb820e52b56b89b4d
diff --git a/openssh-6.5p1/Makefile.in b/openssh-6.5p1/Makefile.in
--- a/openssh-6.5p1/Makefile.in
+++ b/openssh-6.5p1/Makefile.in
@@ -76,17 +76,18 @@ LIBSSH_OBJS=authfd.o authfile.o bufaux.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 smult_curve25519_ref.o \
kexc25519.o kexc25519c.o poly1305.o chacha.o cipher-chachapoly.o \
ssh-ed25519.o digest.o \
sc25519.o ge25519.o fe25519.o ed25519.o verify.o hash.o blocks.o \
- auditstub.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.5p1/auth-rsa.c b/openssh-6.5p1/auth-rsa.c
--- a/openssh-6.5p1/auth-rsa.c
+++ b/openssh-6.5p1/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;
}
free(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.5p1/cipher-ctr.c b/openssh-6.5p1/cipher-ctr.c
--- a/openssh-6.5p1/cipher-ctr.c
+++ b/openssh-6.5p1/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.5p1/cipher.c b/openssh-6.5p1/cipher.c
--- a/openssh-6.5p1/cipher.c
+++ b/openssh-6.5p1/cipher.c
@@ -44,25 +44,26 @@
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include "xmalloc.h"
#include "log.h"
#include "misc.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);
-struct Cipher ciphers[] = {
+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 },
@@ -85,27 +86,67 @@ 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 }
};
+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 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);
@@ -175,27 +216,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)
@@ -229,17 +270,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)
{
@@ -417,24 +458,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, const 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.5p1/fips.c b/openssh-6.5p1/fips.c
new file mode 100644
--- /dev/null
+++ b/openssh-6.5p1/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.5p1/fips.h b/openssh-6.5p1/fips.h
new file mode 100644
--- /dev/null
+++ b/openssh-6.5p1/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.5p1/key.c b/openssh-6.5p1/key.c
--- a/openssh-6.5p1/key.c
+++ b/openssh-6.5p1/key.c
@@ -52,16 +52,17 @@
#include "key.h"
#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 +665,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.5p1/mac.c b/openssh-6.5p1/mac.c
--- a/openssh-6.5p1/mac.c
+++ b/openssh-6.5p1/mac.c
@@ -36,16 +36,17 @@
#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
@@ -55,17 +56,17 @@ struct macalg {
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 */
};
-static const struct macalg macs[] = {
+static const struct macalg 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 },
@@ -86,25 +87,57 @@ static const struct macalg macs[] = {
{ "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 }
};
+static const struct macalg macs_fips140_2[] = {
+ /* Encrypt-and-MAC (encrypt-and-authenticate) variants */
+ { "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
+#ifdef HAVE_EVP_SHA256
+ { "hmac-sha2-256-etm@openssh.com", SSH_EVP, EVP_sha256, 0, 0, 0, 1 },
+ { "hmac-sha2-512-etm@openssh.com", SSH_EVP, EVP_sha512, 0, 0, 0, 1 },
+#endif
+ { NULL, 0, NULL, 0, 0, 0, 0 }
+};
+
+/* Returns array of macs available depending on selected FIPS mode */
+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;
+ }
+}
+
/* 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;
@@ -131,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: found %s", name);
return (0);
}
debug2("mac_setup: unknown %s", name);
diff --git a/openssh-6.5p1/myproposal.h b/openssh-6.5p1/myproposal.h
--- a/openssh-6.5p1/myproposal.h
+++ b/openssh-6.5p1/myproposal.h
@@ -104,16 +104,20 @@
#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_FIPS_140_2_ENCRYPT \
+ "aes128-ctr,aes192-ctr,aes256-ctr," \
+ "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 +128,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.5p1/ssh.c b/openssh-6.5p1/ssh.c
--- a/openssh-6.5p1/ssh.c
+++ b/openssh-6.5p1/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 */
@@ -453,16 +454,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;
@@ -959,16 +962,22 @@ main(int ac, char **av)
}
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, addrs, &hostaddr, options.port,
options.address_family, options.connection_attempts,
&timeout_ms, options.tcp_keep_alive,
options.use_privileged_port) != 0)
exit(255);
if (addrs != NULL)
diff --git a/openssh-6.5p1/sshconnect2.c b/openssh-6.5p1/sshconnect2.c
--- a/openssh-6.5p1/sshconnect2.c
+++ b/openssh-6.5p1/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,41 @@ 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_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()) {
+ /* TODO: use intersection of FIPS macs and those requested in
+ * configuration */
+ 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] =
compat_pkalg_proposal(options.hostkeyalgorithms);
else {
/* Prefer algorithms that we already have keys for */
myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] =
compat_pkalg_proposal(
diff --git a/openssh-6.5p1/sshd.c b/openssh-6.5p1/sshd.c
--- a/openssh-6.5p1/sshd.c
+++ b/openssh-6.5p1/sshd.c
@@ -119,16 +119,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 */
@@ -1786,16 +1787,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;
}
@@ -2506,25 +2511,31 @@ 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_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";
}