Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
openSUSE:Leap:42.1:Staging:C
openssh
openssh-6.6p1-fips.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File openssh-6.6p1-fips.patch of Package openssh
# HG changeset patch # Parent ce230d46b57837ef96b1a2e46ded204c7bec2c8a # # comply with FIPS 140-2 by using only approved crypto algorithms # when OpenSSL is detected to be running in FIPS mode 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 @@ -40,16 +40,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 1024 +#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,180 @@ +/* + * 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> + +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,16 +41,18 @@ #include "cipher.h" #include "kex.h" #include "log.h" #include "packet.h" #include "dh.h" #include "ssh2.h" #include "compat.h" +#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; u_int klen, slen, sbloblen, hashlen; @@ -59,23 +61,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,16 +45,18 @@ #include "dh.h" #include "ssh2.h" #include "compat.h" #ifdef GSSAPI #include "ssh-gss.h" #endif #include "monitor_wrap.h" +#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; u_int sbloblen, klen, slen, hashlen; @@ -71,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/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/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);
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor