From: Dave Hart Date: Mon, 4 Mar 2024 13:16:37 +0000 (+0000) Subject: Build MD5 support even with OpenSSL to accomodate FIPS OpenSSL which lacks it. X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=38035adeaf7e57954119ee54526eea289e388c2d;p=thirdparty%2Fntp.git Build MD5 support even with OpenSSL to accomodate FIPS OpenSSL which lacks it. Cache EVP_MD_CTX to avoid many alloc/frees around digest ops. bk: 65e5c9b533PYzhgaJVkZLrtViqzfmA --- diff --git a/include/ntp_md5.h b/include/ntp_md5.h index c3d63a8da..22caff350 100644 --- a/include/ntp_md5.h +++ b/include/ntp_md5.h @@ -6,6 +6,18 @@ #ifndef NTP_MD5_H #define NTP_MD5_H +/* Use the system MD5 or fall back on libisc's */ +# if defined HAVE_MD5_H && defined HAVE_MD5INIT +# include +# else +# include "isc/md5.h" + typedef isc_md5_t MD5_CTX; +# define MD5_DIGEST_LENGTH ISC_MD5_DIGESTLENGTH +# define MD5Init(c) isc_md5_init(c) +# define MD5Update(c, p, s) isc_md5_update(c, (const void *)p, s) +# define MD5Final(d, c) isc_md5_final((c), (d)) /* swapped */ +# endif + # define KEY_TYPE_MD5 NID_md5 #ifdef OPENSSL @@ -20,21 +32,11 @@ /* * Provide OpenSSL-alike MD5 API if we're not using OpenSSL */ -# if defined HAVE_MD5_H && defined HAVE_MD5INIT -# include -# else -# include "isc/md5.h" - typedef isc_md5_t MD5_CTX; -# define MD5Init(c) isc_md5_init(c) -# define MD5Update(c, p, s) isc_md5_update(c, p, s) -# define MD5Final(d, c) isc_md5_final((c), (d)) /* swapped */ -# endif typedef MD5_CTX EVP_MD_CTX; # define NID_md5 4 /* from openssl/objects.h */ -# define MD5_LENGTH 16 -# define EVP_MAX_MD_SIZE MD5_LENGTH +# define EVP_MAX_MD_SIZE MD5_DIGEST_LENGTH # define EVP_MD_CTX_free(c) free(c) # define EVP_MD_CTX_new() calloc(1, sizeof(MD5_CTX)) # define EVP_get_digestbynid(t) NULL diff --git a/include/ntp_stdlib.h b/include/ntp_stdlib.h index b3b6d5f11..6f667fbab 100644 --- a/include/ntp_stdlib.h +++ b/include/ntp_stdlib.h @@ -243,12 +243,13 @@ extern pset_tod_using set_tod_using; #ifdef OPENSSL extern void ssl_init (void); extern void ssl_check_version (void); -extern int ssl_init_done; +extern EVP_MD_CTX* digest_ctx; /* also ssl_init_done */ #define INIT_SSL() \ do { \ - if (!ssl_init_done) \ + if (NULL == digest_ctx) { \ ssl_init(); \ - } while (0) + } \ + } while (FALSE) #else /* !OPENSSL follows */ #define ssl_check_version() do {} while (0) #define INIT_SSL() do {} while (0) diff --git a/libntp/a_md5encrypt.c b/libntp/a_md5encrypt.c index 191d3dd05..1c60a2949 100644 --- a/libntp/a_md5encrypt.c +++ b/libntp/a_md5encrypt.c @@ -21,7 +21,6 @@ typedef struct { size_t len; } rwbuffT; - #if defined(OPENSSL) && defined(ENABLE_CMAC) static size_t cmac_ctx_size( @@ -40,56 +39,33 @@ cmac_ctx_size( #endif /* OPENSSL && ENABLE_CMAC */ +/* + * Allocate and initialize a digest context. As a speed optimization, + * take an idea from ntpsec and cache the context to avoid malloc/free + * overhead in time-critical paths. ntpsec also caches the algorithms + * with each key. + * This is not thread-safe, but that is + * not a problem at present. + */ static EVP_MD_CTX * get_md_ctx( - int type, - int/*BOOL*/ for_auth + int nid ) { #ifndef OPENSSL - return emalloc(sizeof(MD5_CTX)); -#else - EVP_MD_CTX * ctx; -# if OPENSSL_VERSION_NUMBER >= 0x30000000L - EVP_MD * md; -# endif - const char * md_name; + static MD5_CTX md5_ctx; - ctx = EVP_MD_CTX_new(); - md_name = OBJ_nid2sn(type); + DEBUG_INSIST(NID_md5 == nid); + MD5Init(&md5_ctx); -# if OPENSSL_VERSION_NUMBER >= 0x30000000L - /* - * See section FIPS Provider: - * https://www.openssl.org/docs/man3.0/man7/crypto.html - * for property query strings - */ - if (!for_auth && NID_md5 == type) { - md = EVP_MD_fetch(NULL, md_name, "fips=no"); - } else { - md = EVP_MD_fetch(NULL, md_name, ""); - } - if (NULL == md) { - msyslog(LOG_ERR, "Could not load digest %s", md_name); - exit(1); - } - if (!EVP_DigestInit_ex(ctx, md, NULL)) { - msyslog(LOG_ERR, "%s init failed", md_name); - exit(1); - } - EVP_MD_free(md); -# else /* OPENSSL_VERSION_NUMBER < 0x30000000L follows */ -# ifdef EVP_MD_CTX_FLAG_NON_FIPS_ALLOW - if (!for_auth && NID_md5 == type) { - EVP_MD_CTX_set_flags(ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW); - } -# endif - if (!EVP_DigestInit_ex(ctx, EVP_get_digestbynid(type), NULL)) { - msyslog(LOG_ERR, "%s init failed", md_name); + return &md5_ctx; +#else + if (!EVP_DigestInit(digest_ctx, EVP_get_digestbynid(nid))) { + msyslog(LOG_ERR, "%s init failed", OBJ_nid2sn(nid)); exit(1); } -# endif /* OPENSSL_VERSION_NUMBER */ - return ctx; + + return digest_ctx; #endif /* OPENSSL */ } @@ -158,7 +134,7 @@ make_mac( EVP_MD_CTX * ctx; u_int uilen = 0; - ctx = get_md_ctx(ktype, TRUE); + ctx = get_md_ctx(ktype); if (NULL == ctx) { goto mac_fail; } @@ -184,17 +160,14 @@ make_mac( } mac_fail: retlen = (size_t)uilen; - - if (ctx) - EVP_MD_CTX_free(ctx); } #else /* !OPENSSL follows */ if (NID_md5 == ktype) { - EVP_MD_CTX * ctx = emalloc(sizeof(MD5_CTX)); - size_t len = 0; + EVP_MD_CTX * ctx; + ctx = get_md_ctx(ktype); if (digest->len < MD5_LENGTH) { msyslog(LOG_ERR, "%s", "MAC encrypt: MAC md5 buf too small."); } else { @@ -202,10 +175,8 @@ make_mac( MD5Update(ctx, (const void *)key->buf, key->len); MD5Update(ctx, (const void *)msg->buf, msg->len); MD5Final(digest->buf, ctx); - len = MD5_LENGTH; + retlen = MD5_LENGTH; } - free(ctx); - retlen = len; } else { msyslog(LOG_ERR, "MAC encrypt: invalid key type %d", ktype); } @@ -296,28 +267,26 @@ MD5authdecrypt( * match the little-endian hash. This is ugly but it seems better * than changing the IPv6 refid calculation on the more-common * systems. + * This is not thread safe, not a problem so far. */ u_int32 addr2refid(sockaddr_u *addr) { - u_char digest[EVP_MAX_MD_SIZE]; - u_int32 addr_refid; - EVP_MD_CTX * ctx; - u_int len; + static MD5_CTX md5_ctx; + union u_tag { + u_char digest[MD5_DIGEST_LENGTH]; + u_int32 addr_refid; + } u; if (IS_IPV4(addr)) { return (NSRCADR(addr)); } - INIT_SSL(); /* MD5 is not used for authentication here. */ - ctx = get_md_ctx(NID_md5, FALSE); - EVP_DigestUpdate(ctx, (u_char *)&SOCK_ADDR6(addr), - sizeof(SOCK_ADDR6(addr))); - EVP_DigestFinal(ctx, digest, &len); - EVP_MD_CTX_free(ctx); - memcpy(&addr_refid, digest, sizeof(addr_refid)); + MD5Init(&md5_ctx); + MD5Update(&md5_ctx, (void *)&SOCK_ADDR6(addr), sizeof(SOCK_ADDR6(addr))); + MD5Final(u.digest, &md5_ctx); #ifdef WORDS_BIGENDIAN - addr_refid = BYTESWAP32(addr_refid); + u.addr_refid = BYTESWAP32(u.addr_refid); #endif - return addr_refid; + return u.addr_refid; } diff --git a/libntp/ssl_init.c b/libntp/ssl_init.c index 152fad073..0ffd8528d 100644 --- a/libntp/ssl_init.c +++ b/libntp/ssl_init.c @@ -23,34 +23,38 @@ # define CMAC_LENGTH 16 # define CMAC "AES128CMAC" # endif /*HAVE_OPENSSL_CMAC_H*/ -int ssl_init_done; -#if OPENSSL_VERSION_NUMBER < 0x10100000L +EVP_MD_CTX *digest_ctx; + + static void atexit_ssl_cleanup(void) { - if (!ssl_init_done) { + if (NULL == digest_ctx) { return; } - - ssl_init_done = FALSE; + EVP_MD_CTX_free(digest_ctx); + digest_ctx = NULL; +#if OPENSSL_VERSION_NUMBER < 0x10100000L EVP_cleanup(); ERR_free_strings(); -} #endif /* OpenSSL < 1.1 */ +} + void ssl_init(void) { init_lib(); - if ( ! ssl_init_done) { + if (NULL == digest_ctx) { #if OPENSSL_VERSION_NUMBER < 0x10100000L ERR_load_crypto_strings(); OpenSSL_add_all_algorithms(); - atexit(&atexit_ssl_cleanup); #endif /* OpenSSL < 1.1 */ - ssl_init_done = TRUE; + digest_ctx = EVP_MD_CTX_new(); + INSIST(digest_ctx != NULL); + atexit(&atexit_ssl_cleanup); } } diff --git a/ntpd/ntp_control.c b/ntpd/ntp_control.c index 75e1c72c9..898b5a4d5 100644 --- a/ntpd/ntp_control.c +++ b/ntpd/ntp_control.c @@ -3633,8 +3633,13 @@ static void configure( /* - * derive_nonce - generate client-address-specific nonce value - * associated with a given timestamp. + * derive_nonce - generate 32-bit nonce value derived from the client + * address and a request-specific timestamp. + * + * This uses MD5 for a non-authentication purpose -- the nonce is used + * analogous to the TCP 3-way handshake to confirm the UDP client can + * receive traffic from which it claims to originate, that is, to + * prevent spoofed requests leading to reflected amplification. */ static u_int32 derive_nonce( sockaddr_u * addr, @@ -3644,13 +3649,11 @@ static u_int32 derive_nonce( { static u_int32 salt[4]; static u_long last_salt_update; + MD5_CTX ctx; union d_tag { - u_char digest[EVP_MAX_MD_SIZE]; + u_char digest[MD5_DIGEST_LENGTH]; u_int32 extract; } d; - EVP_MD_CTX *ctx; - u_int len; - int rc; while (!salt[0] || current_time - last_salt_update >= 3600) { salt[0] = ntp_random(); @@ -3660,32 +3663,18 @@ static u_int32 derive_nonce( last_salt_update = current_time; } - ctx = EVP_MD_CTX_new(); -# if defined(OPENSSL) && defined(EVP_MD_CTX_FLAG_NON_FIPS_ALLOW) - /* [Bug 3457] set flags and don't kill them again */ - EVP_MD_CTX_set_flags(ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW); - rc = EVP_DigestInit_ex(ctx, EVP_get_digestbynid(NID_md5), NULL); -# else - rc = EVP_DigestInit(ctx, EVP_get_digestbynid(NID_md5)); -# endif - if (!rc) { - msyslog(LOG_ERR, "EVP_DigestInit failed in '%s'", __func__); - return (0); + MD5Init(&ctx); + MD5Update(&ctx, salt, sizeof(salt)); + MD5Update(&ctx, &ts_i, sizeof(ts_i)); + MD5Update(&ctx, &ts_f, sizeof(ts_f)); + if (IS_IPV4(addr)) { + MD5Update(&ctx, &SOCK_ADDR4(addr), sizeof(SOCK_ADDR4(addr))); + } else { + MD5Update(&ctx, &SOCK_ADDR6(addr), sizeof(SOCK_ADDR6(addr))); } - - EVP_DigestUpdate(ctx, salt, sizeof(salt)); - EVP_DigestUpdate(ctx, &ts_i, sizeof(ts_i)); - EVP_DigestUpdate(ctx, &ts_f, sizeof(ts_f)); - if (IS_IPV4(addr)) - EVP_DigestUpdate(ctx, &SOCK_ADDR4(addr), - sizeof(SOCK_ADDR4(addr))); - else - EVP_DigestUpdate(ctx, &SOCK_ADDR6(addr), - sizeof(SOCK_ADDR6(addr))); - EVP_DigestUpdate(ctx, &NSRCPORT(addr), sizeof(NSRCPORT(addr))); - EVP_DigestUpdate(ctx, salt, sizeof(salt)); - EVP_DigestFinal(ctx, d.digest, &len); - EVP_MD_CTX_free(ctx); + MD5Update(&ctx, &NSRCPORT(addr), sizeof(NSRCPORT(addr))); + MD5Update(&ctx, salt, sizeof(salt)); + MD5Final(d.digest, &ctx); return d.extract; } diff --git a/ntpd/ntp_crypto.c b/ntpd/ntp_crypto.c index 8eaebd04f..ad5aa95cf 100644 --- a/ntpd/ntp_crypto.c +++ b/ntpd/ntp_crypto.c @@ -267,17 +267,10 @@ session_key( hdlen = 10 * sizeof(u_int32); break; } - ctx = EVP_MD_CTX_new(); -# if defined(OPENSSL) && defined(EVP_MD_CTX_FLAG_NON_FIPS_ALLOW) - /* [Bug 3457] set flags and don't kill them again */ - EVP_MD_CTX_set_flags(ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW); - EVP_DigestInit_ex(ctx, EVP_get_digestbynid(crypto_nid), NULL); -# else + ctx = digest_ctx; EVP_DigestInit(ctx, EVP_get_digestbynid(crypto_nid)); -# endif EVP_DigestUpdate(ctx, (u_char *)header, hdlen); EVP_DigestFinal(ctx, dgst, &len); - EVP_MD_CTX_free(ctx); memcpy(&keyid, dgst, 4); keyid = ntohl(keyid); if (lifetime != 0) { @@ -389,7 +382,7 @@ make_keylist( if (tstamp != 0) { if (vp->sig == NULL) vp->sig = emalloc(sign_siglen); - ctx = EVP_MD_CTX_new(); + ctx = digest_ctx; EVP_SignInit(ctx, sign_digest); EVP_SignUpdate(ctx, (u_char *)vp, 12); EVP_SignUpdate(ctx, vp->ptr, sizeof(struct autokey)); @@ -398,7 +391,6 @@ make_keylist( vp->siglen = htonl(len); peer->flags |= FLAG_ASSOC; } - EVP_MD_CTX_free(ctx); } DPRINTF(1, ("make_keys: %d %08x %08x ts %u fs %u poll %d\n", peer->keynumber, keyid, cookie, ntohl(vp->tstamp), @@ -1549,16 +1541,15 @@ crypto_verify( * signature. If the identity exchange is verified, light the * proventic bit. What a relief. */ - ctx = EVP_MD_CTX_new(); + ctx = digest_ctx; EVP_VerifyInit(ctx, peer->digest); - /* XXX: the "+ 12" needs to be at least documented... */ - EVP_VerifyUpdate(ctx, (u_char *)&ep->tstamp, vallen + 12); + EVP_VerifyUpdate(ctx, (u_char *)&ep->tstamp, vallen + + sizeof(ep->tstamp) + sizeof(ep->fstamp) + + sizeof(ep->vallen)); if (EVP_VerifyFinal(ctx, (u_char *)&ep->pkt[i], siglen, pkey) <= 0) { - EVP_MD_CTX_free(ctx); return (XEVNT_SIG); } - EVP_MD_CTX_free(ctx); if (peer->crypto & CRYPTO_FLAG_VRFY) peer->crypto |= CRYPTO_FLAG_PROV; @@ -1629,7 +1620,7 @@ crypto_encrypt( return (XEVNT_OK); vp->sig = emalloc(sign_siglen); - ctx = EVP_MD_CTX_new(); + ctx = digest_ctx; EVP_SignInit(ctx, sign_digest); EVP_SignUpdate(ctx, (u_char *)&vp->tstamp, 12); EVP_SignUpdate(ctx, vp->ptr, vallen); @@ -1637,7 +1628,6 @@ crypto_encrypt( INSIST(vallen <= sign_siglen); vp->siglen = htonl(vallen); } - EVP_MD_CTX_free(ctx); return (XEVNT_OK); } @@ -1855,7 +1845,7 @@ crypto_update(void) if (hostval.tstamp == 0) return; - ctx = EVP_MD_CTX_new(); + ctx = digest_ctx; /* * Sign public key and timestamps. The filestamp is derived from @@ -1951,7 +1941,6 @@ crypto_update(void) ntohl(hostval.tstamp)); record_crypto_stats(NULL, statstr); DPRINTF(1, ("crypto_update: %s\n", statstr)); - EVP_MD_CTX_free(ctx); } /* @@ -2099,17 +2088,10 @@ bighash( len = BN_num_bytes(bn); ptr = emalloc(len); BN_bn2bin(bn, ptr); - ctx = EVP_MD_CTX_new(); -# if defined(OPENSSL) && defined(EVP_MD_CTX_FLAG_NON_FIPS_ALLOW) - /* [Bug 3457] set flags and don't kill them again */ - EVP_MD_CTX_set_flags(ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW); - EVP_DigestInit_ex(ctx, EVP_md5(), NULL); -# else + ctx = digest_ctx; EVP_DigestInit(ctx, EVP_md5()); -# endif EVP_DigestUpdate(ctx, ptr, len); EVP_DigestFinal(ctx, dgst, &len); - EVP_MD_CTX_free(ctx); BN_bin2bn(dgst, len, bk); free(ptr); } @@ -2222,7 +2204,7 @@ crypto_alice( return (XEVNT_OK); vp->sig = emalloc(sign_siglen); - ctx = EVP_MD_CTX_new(); + ctx = digest_ctx; EVP_SignInit(ctx, sign_digest); EVP_SignUpdate(ctx, (u_char *)&vp->tstamp, 12); EVP_SignUpdate(ctx, vp->ptr, len); @@ -2230,7 +2212,6 @@ crypto_alice( INSIST(len <= sign_siglen); vp->siglen = htonl(len); } - EVP_MD_CTX_free(ctx); return (XEVNT_OK); } @@ -2341,7 +2322,7 @@ crypto_bob( /* XXX: more validation to make sure the sign fits... */ vp->sig = emalloc(sign_siglen); - ctx = EVP_MD_CTX_new(); + ctx = digest_ctx; EVP_SignInit(ctx, sign_digest); EVP_SignUpdate(ctx, (u_char *)&vp->tstamp, 12); EVP_SignUpdate(ctx, vp->ptr, len); @@ -2349,7 +2330,6 @@ crypto_bob( INSIST(len <= sign_siglen); vp->siglen = htonl(len); } - EVP_MD_CTX_free(ctx); retv = XEVNT_OK; cleanup: @@ -2562,7 +2542,7 @@ crypto_alice2( return (XEVNT_OK); vp->sig = emalloc(sign_siglen); - ctx = EVP_MD_CTX_new(); + ctx = digest_ctx; EVP_SignInit(ctx, sign_digest); EVP_SignUpdate(ctx, (u_char *)&vp->tstamp, 12); EVP_SignUpdate(ctx, vp->ptr, len); @@ -2570,7 +2550,6 @@ crypto_alice2( INSIST(len <= sign_siglen); vp->siglen = htonl(len); } - EVP_MD_CTX_free(ctx); return (XEVNT_OK); } @@ -2667,7 +2646,7 @@ crypto_bob2( return (XEVNT_OK); vp->sig = emalloc(sign_siglen); - ctx = EVP_MD_CTX_new(); + ctx = digest_ctx; EVP_SignInit(ctx, sign_digest); EVP_SignUpdate(ctx, (u_char *)&vp->tstamp, 12); EVP_SignUpdate(ctx, vp->ptr, len); @@ -2675,7 +2654,6 @@ crypto_bob2( INSIST(len <= sign_siglen); vp->siglen = htonl(len); } - EVP_MD_CTX_free(ctx); return (XEVNT_OK); } @@ -2906,7 +2884,7 @@ crypto_alice3( return (XEVNT_OK); vp->sig = emalloc(sign_siglen); - ctx = EVP_MD_CTX_new(); + ctx = digest_ctx; EVP_SignInit(ctx, sign_digest); EVP_SignUpdate(ctx, (u_char *)&vp->tstamp, 12); EVP_SignUpdate(ctx, vp->ptr, len); @@ -2914,7 +2892,6 @@ crypto_alice3( INSIST(len <= sign_siglen); vp->siglen = htonl(len); } - EVP_MD_CTX_free(ctx); return (XEVNT_OK); } @@ -3019,7 +2996,7 @@ crypto_bob3( return (XEVNT_OK); vp->sig = emalloc(sign_siglen); - ctx = EVP_MD_CTX_new(); + ctx = digest_ctx; EVP_SignInit(ctx, sign_digest); EVP_SignUpdate(ctx, (u_char *)&vp->tstamp, 12); EVP_SignUpdate(ctx, vp->ptr, len); @@ -3027,7 +3004,6 @@ crypto_bob3( INSIST(len <= sign_siglen); vp->siglen = htonl(len); } - EVP_MD_CTX_free(ctx); return (XEVNT_OK); } @@ -3269,7 +3245,7 @@ cert_sign( vp->siglen = 0; if (tstamp != 0) { vp->sig = emalloc(sign_siglen); - ctx = EVP_MD_CTX_new(); + ctx = digest_ctx; EVP_SignInit(ctx, sign_digest); EVP_SignUpdate(ctx, (u_char *)vp, 12); EVP_SignUpdate(ctx, vp->ptr, len); @@ -3277,7 +3253,6 @@ cert_sign( INSIST(len <= sign_siglen); vp->siglen = htonl(len); } - EVP_MD_CTX_free(ctx); } #ifdef DEBUG if (debug > 1) diff --git a/ntpd/ntp_proto.c b/ntpd/ntp_proto.c index 35c9a825f..0c1c75a11 100644 --- a/ntpd/ntp_proto.c +++ b/ntpd/ntp_proto.c @@ -733,10 +733,10 @@ receive( hisleap = PKT_LEAP(pkt->li_vn_mode); hisstratum = PKT_TO_STRATUM(pkt->stratum); - INSIST(0 != hisstratum); /* paranoia check PKT_TO_STRATUM result */ - + DEBUG_INSIST(0 != hisstratum); /* paranoia check PKT_TO_STRATUM result */ + /* TODO: this should be in a unit test */ DPRINTF(1, ("receive: at %ld %s<-%s ippeerlimit %d mode %d iflags %s " - "restrict %s org 0x%8x.%08x xmt 0x%8x.%08x\n", + "restrict %s org 0x%x.%08x xmt 0x%x.%08x\n", current_time, stoa(&rbufp->dstadr->sin), stoa(&rbufp->recv_srcadr), r4a.ippeerlimit, hismode, iflags_str(rbufp->dstadr->flags), diff --git a/ports/winnt/include/msvc_ssl_autolib.h b/ports/winnt/include/msvc_ssl_autolib.h index 9a8ae6126..34a870abf 100644 --- a/ports/winnt/include/msvc_ssl_autolib.h +++ b/ports/winnt/include/msvc_ssl_autolib.h @@ -19,7 +19,7 @@ * https://slproweb.com/products/Win32OpenSSL.html * * If 'OPENSSL_AUTOLINK_STRICT' is defined, then target bit width, - * runtime model and debug/release info are incoded into the library + * runtime model and debug/release info are encoded into the library * file name, according to this scheme: * * basename.lib