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