diff --git a/0035-Don-t-issue-session-keys-with-deprecated-enctypes.patch b/0035-Don-t-issue-session-keys-with-deprecated-enctypes.patch new file mode 100644 index 0000000..ff9f9dc --- /dev/null +++ b/0035-Don-t-issue-session-keys-with-deprecated-enctypes.patch @@ -0,0 +1,327 @@ +From f557bbdf974d38aa250bb7f9d79d8c62603a9a6d Mon Sep 17 00:00:00 2001 +From: Greg Hudson +Date: Fri, 16 Dec 2022 18:31:07 -0500 +Subject: [PATCH] Don't issue session keys with deprecated enctypes + +A paper by Tom Tervoort noted that rc4-hmac pre-hashes the input for +its checksum and GSS operations before applying HMAC, and is therefore +potentially vulnerable to hash collision attacks if a protocol +contains a restricted signing oracle. + +In light of these potential attacks, begin the functional deprecation +of DES3 and RC4 by disallowing their use as session key enctypes by +default. Add the variables allow_des3 and allow_rc4 in case +negotiability of these enctypes for session keys needs to be turned +back on, with the expectation that in future releases the enctypes +will be more comprehensively deprecated. + +ticket: 9081 +(cherry picked from commit 1b57a4d134bbd0e7c52d5885a92eccc815726463) +--- + doc/admin/conf_files/krb5_conf.rst | 12 ++++++++++++ + doc/admin/enctypes.rst | 23 +++++++++++++++++++--- + src/include/k5-int.h | 4 ++++ + src/kdc/kdc_util.c | 10 ++++++++++ + src/lib/krb5/krb/get_in_tkt.c | 31 +++++++++++++++++++----------- + src/lib/krb5/krb/init_ctx.c | 10 ++++++++++ + src/tests/gssapi/t_enctypes.py | 5 +++-- + src/tests/t_etype_info.py | 5 +++-- + src/tests/t_sesskeynego.py | 28 +++++++++++++++++++++++++-- + src/util/k5test.py | 9 ++++++++- + 10 files changed, 116 insertions(+), 21 deletions(-) + +diff --git a/doc/admin/conf_files/krb5_conf.rst b/doc/admin/conf_files/krb5_conf.rst +index dca52e1426..d51fd3ce7e 100644 +--- a/doc/admin/conf_files/krb5_conf.rst ++++ b/doc/admin/conf_files/krb5_conf.rst +@@ -95,6 +95,18 @@ Additionally, krb5.conf may include any of the relations described in + + The libdefaults section may contain any of the following relations: + ++**allow_des3** ++ Permit the KDC to issue tickets with des3-cbc-sha1 session keys. ++ In future releases, this flag will allow des3-cbc-sha1 to be used ++ at all. The default value for this tag is false. (Added in ++ release 1.21.) ++ ++**allow_rc4** ++ Permit the KDC to issue tickets with arcfour-hmac session keys. ++ In future releases, this flag will allow arcfour-hmac to be used ++ at all. The default value for this tag is false. (Added in ++ release 1.21.) ++ + **allow_weak_crypto** + If this flag is set to false, then weak encryption types (as noted + in :ref:`Encryption_types` in :ref:`kdc.conf(5)`) will be filtered +diff --git a/doc/admin/enctypes.rst b/doc/admin/enctypes.rst +index c4d5499d3b..2b4ed7da0b 100644 +--- a/doc/admin/enctypes.rst ++++ b/doc/admin/enctypes.rst +@@ -48,12 +48,15 @@ Session key selection + The KDC chooses the session key enctype by taking the intersection of + its **permitted_enctypes** list, the list of long-term keys for the + most recent kvno of the service, and the client's requested list of +-enctypes. ++enctypes. Starting in krb5-1.21, all services are assumed to support ++aes256-cts-hmac-sha1-96; also, des3-cbc-sha1 and arcfour-hmac session ++keys will not be issued by default. + + Starting in krb5-1.11, it is possible to set a string attribute on a + service principal to control what session key enctypes the KDC may +-issue for service tickets for that principal. See :ref:`set_string` +-in :ref:`kadmin(1)` for details. ++issue for service tickets for that principal, overriding the service's ++long-term keys and the assumption of aes256-cts-hmac-sha1-96 support. ++See :ref:`set_string` in :ref:`kadmin(1)` for details. + + + Choosing enctypes for a service +@@ -87,6 +90,20 @@ affect how enctypes are chosen. + acceptable risk for your environment and the weak enctypes are + required for backward compatibility. + ++**allow_des3** ++ was added in release 1.21 and defaults to *false*. Unless this ++ flag is set to *true*, the KDC will not issue tickets with ++ des3-cbc-sha1 session keys. In a future release, this flag will ++ control whether des3-cbc-sha1 is permitted in similar fashion to ++ weak enctypes. ++ ++**allow_rc4** ++ was added in release 1.21 and defaults to *false*. Unless this ++ flag is set to *true*, the KDC will not issue tickets with ++ arcfour-hmac session keys. In a future release, this flag will ++ control whether arcfour-hmac is permitted in similar fashion to ++ weak enctypes. ++ + **permitted_enctypes** + controls the set of enctypes that a service will permit for + session keys and for ticket and authenticator encryption. The KDC +diff --git a/src/include/k5-int.h b/src/include/k5-int.h +index b7789a2dd8..d0a263aa7d 100644 +--- a/src/include/k5-int.h ++++ b/src/include/k5-int.h +@@ -181,6 +181,8 @@ typedef unsigned char u_char; + * matches the variable name. Keep these alphabetized. */ + #define KRB5_CONF_ACL_FILE "acl_file" + #define KRB5_CONF_ADMIN_SERVER "admin_server" ++#define KRB5_CONF_ALLOW_DES3 "allow_des3" ++#define KRB5_CONF_ALLOW_RC4 "allow_rc4" + #define KRB5_CONF_ALLOW_WEAK_CRYPTO "allow_weak_crypto" + #define KRB5_CONF_AUTH_TO_LOCAL "auth_to_local" + #define KRB5_CONF_AUTH_TO_LOCAL_NAMES "auth_to_local_names" +@@ -1241,6 +1243,8 @@ struct _krb5_context { + struct _kdb_log_context *kdblog_context; + + krb5_boolean allow_weak_crypto; ++ krb5_boolean allow_des3; ++ krb5_boolean allow_rc4; + krb5_boolean ignore_acceptor_hostname; + krb5_boolean enforce_ok_as_delegate; + enum dns_canonhost dns_canonicalize_hostname; +diff --git a/src/kdc/kdc_util.c b/src/kdc/kdc_util.c +index 93415ba862..c7b6e4090d 100644 +--- a/src/kdc/kdc_util.c ++++ b/src/kdc/kdc_util.c +@@ -1108,6 +1108,16 @@ select_session_keytype(krb5_context context, krb5_db_entry *server, + if (!krb5_is_permitted_enctype(context, ktype[i])) + continue; + ++ /* ++ * Prevent these deprecated enctypes from being used as session keys ++ * unless they are explicitly allowed. In the future they will be more ++ * comprehensively disabled and eventually removed. ++ */ ++ if (ktype[i] == ENCTYPE_DES3_CBC_SHA1 && !context->allow_des3) ++ continue; ++ if (ktype[i] == ENCTYPE_ARCFOUR_HMAC && !context->allow_rc4) ++ continue; ++ + if (dbentry_supports_enctype(context, server, ktype[i])) + return ktype[i]; + } +diff --git a/src/lib/krb5/krb/get_in_tkt.c b/src/lib/krb5/krb/get_in_tkt.c +index 1b420a3ac2..ea089f0fcc 100644 +--- a/src/lib/krb5/krb/get_in_tkt.c ++++ b/src/lib/krb5/krb/get_in_tkt.c +@@ -1582,22 +1582,31 @@ warn_pw_expiry(krb5_context context, krb5_get_init_creds_opt *options, + (*prompter)(context, data, 0, banner, 0, 0); + } + +-/* Display a warning via the prompter if des3-cbc-sha1 was used for either the +- * reply key or the session key. */ ++/* Display a warning via the prompter if a deprecated enctype was used for ++ * either the reply key or the session key. */ + static void +-warn_des3(krb5_context context, krb5_init_creds_context ctx, +- krb5_enctype as_key_enctype) ++warn_deprecated(krb5_context context, krb5_init_creds_context ctx, ++ krb5_enctype as_key_enctype) + { +- const char *banner; ++ krb5_enctype etype; ++ char encbuf[128], banner[256]; + +- if (as_key_enctype != ENCTYPE_DES3_CBC_SHA1 && +- ctx->cred.keyblock.enctype != ENCTYPE_DES3_CBC_SHA1) +- return; + if (ctx->prompter == NULL) + return; + +- banner = _("Warning: encryption type des3-cbc-sha1 used for " +- "authentication is weak and will be disabled"); ++ if (krb5int_c_deprecated_enctype(as_key_enctype)) ++ etype = as_key_enctype; ++ else if (krb5int_c_deprecated_enctype(ctx->cred.keyblock.enctype)) ++ etype = ctx->cred.keyblock.enctype; ++ else ++ return; ++ ++ if (krb5_enctype_to_name(etype, FALSE, encbuf, sizeof(encbuf)) != 0) ++ return; ++ snprintf(banner, sizeof(banner), ++ _("Warning: encryption type %s used for authentication is " ++ "deprecated and will be disabled"), encbuf); ++ + /* PROMPTER_INVOCATION */ + (*ctx->prompter)(context, ctx->prompter_data, NULL, banner, 0, NULL); + } +@@ -1848,7 +1857,7 @@ init_creds_step_reply(krb5_context context, + ctx->complete = TRUE; + warn_pw_expiry(context, ctx->opt, ctx->prompter, ctx->prompter_data, + ctx->in_tkt_service, ctx->reply); +- warn_des3(context, ctx, encrypting_key.enctype); ++ warn_deprecated(context, ctx, encrypting_key.enctype); + + cleanup: + krb5_free_pa_data(context, kdc_padata); +diff --git a/src/lib/krb5/krb/init_ctx.c b/src/lib/krb5/krb/init_ctx.c +index 582a2945ff..a32f8dbf03 100644 +--- a/src/lib/krb5/krb/init_ctx.c ++++ b/src/lib/krb5/krb/init_ctx.c +@@ -220,6 +220,16 @@ krb5_init_context_profile(profile_t profile, krb5_flags flags, + goto cleanup; + ctx->allow_weak_crypto = tmp; + ++ retval = get_boolean(ctx, KRB5_CONF_ALLOW_DES3, 0, &tmp); ++ if (retval) ++ goto cleanup; ++ ctx->allow_des3 = tmp; ++ ++ retval = get_boolean(ctx, KRB5_CONF_ALLOW_RC4, 0, &tmp); ++ if (retval) ++ goto cleanup; ++ ctx->allow_rc4 = tmp; ++ + retval = get_boolean(ctx, KRB5_CONF_IGNORE_ACCEPTOR_HOSTNAME, 0, &tmp); + if (retval) + goto cleanup; +diff --git a/src/tests/gssapi/t_enctypes.py b/src/tests/gssapi/t_enctypes.py +index 2f95d89967..e6bde47afc 100755 +--- a/src/tests/gssapi/t_enctypes.py ++++ b/src/tests/gssapi/t_enctypes.py +@@ -10,8 +10,9 @@ d_rc4 = 'DEPRECATED:arcfour-hmac' + + # These tests make assumptions about the default enctype lists, so set + # them explicitly rather than relying on the library defaults. +-supp='aes256-cts:normal aes128-cts:normal rc4-hmac:normal' +-conf = {'libdefaults': {'permitted_enctypes': 'aes rc4'}, ++supp='aes256-cts:normal aes128-cts:normal des3-cbc-sha1:normal rc4-hmac:normal' ++conf = {'libdefaults': {'permitted_enctypes': 'aes des3 rc4', ++ 'allow_des3': 'true', 'allow_rc4': 'true'}, + 'realms': {'$realm': {'supported_enctypes': supp}}} + realm = K5Realm(krb5_conf=conf) + shutil.copyfile(realm.ccache, os.path.join(realm.testdir, 'save')) +diff --git a/src/tests/t_etype_info.py b/src/tests/t_etype_info.py +index a6f538b66d..75d9621dd6 100644 +--- a/src/tests/t_etype_info.py ++++ b/src/tests/t_etype_info.py +@@ -1,7 +1,8 @@ + from k5test import * + +-supported_enctypes = 'aes128-cts rc4-hmac' +-conf = {'realms': {'$realm': {'supported_enctypes': supported_enctypes}}} ++supported_enctypes = 'aes128-cts des3-cbc-sha1 rc4-hmac' ++conf = {'libdefaults': {'allow_des3': 'true', 'allow_rc4': 'true'}, ++ 'realms': {'$realm': {'supported_enctypes': supported_enctypes}}} + realm = K5Realm(create_host=False, get_creds=False, krb5_conf=conf) + + realm.run([kadminl, 'addprinc', '-pw', 'pw', '+requires_preauth', +diff --git a/src/tests/t_sesskeynego.py b/src/tests/t_sesskeynego.py +index 9024aee838..5a213617b5 100755 +--- a/src/tests/t_sesskeynego.py ++++ b/src/tests/t_sesskeynego.py +@@ -25,6 +25,8 @@ conf3 = {'libdefaults': { + 'default_tkt_enctypes': 'aes128-cts', + 'default_tgs_enctypes': 'rc4-hmac,aes128-cts'}} + conf4 = {'libdefaults': {'permitted_enctypes': 'aes256-cts'}} ++conf5 = {'libdefaults': {'allow_rc4': 'true'}} ++conf6 = {'libdefaults': {'allow_des3': 'true'}} + # Test with client request and session_enctypes preferring aes128, but + # aes256 long-term key. + realm = K5Realm(krb5_conf=conf1, create_host=False, get_creds=False) +@@ -54,10 +56,12 @@ realm.run([kadminl, 'setstr', 'server', 'session_enctypes', + 'aes128-cts,aes256-cts']) + test_kvno(realm, 'aes128-cts-hmac-sha1-96', 'aes256-cts-hmac-sha1-96') + +-# 3b: Negotiate rc4-hmac session key when principal only has aes256 long-term. ++# 3b: Skip RC4 (as the KDC does not allow it for session keys by ++# default) and negotiate aes128-cts session key, with only an aes256 ++# long-term service key. + realm.run([kadminl, 'setstr', 'server', 'session_enctypes', + 'rc4-hmac,aes128-cts,aes256-cts']) +-test_kvno(realm, 'DEPRECATED:arcfour-hmac', 'aes256-cts-hmac-sha1-96') ++test_kvno(realm, 'aes128-cts-hmac-sha1-96', 'aes256-cts-hmac-sha1-96') + realm.stop() + + # 4: Check that permitted_enctypes is a default for session key enctypes. +@@ -67,4 +71,24 @@ realm.run([kvno, 'user'], + expected_trace=('etypes requested in TGS request: aes256-cts',)) + realm.stop() + ++# 5: allow_rc4 permits negotiation of rc4-hmac session key. ++realm = K5Realm(krb5_conf=conf5, create_host=False, get_creds=False) ++realm.run([kadminl, 'addprinc', '-randkey', '-e', 'aes256-cts', 'server']) ++realm.run([kadminl, 'setstr', 'server', 'session_enctypes', 'rc4-hmac']) ++test_kvno(realm, 'DEPRECATED:arcfour-hmac', 'aes256-cts-hmac-sha1-96') ++realm.stop() ++ ++# 6: allow_des3 permits negotiation of des3-cbc-sha1 session key. ++realm = K5Realm(krb5_conf=conf6, create_host=False, get_creds=False) ++realm.run([kadminl, 'addprinc', '-randkey', '-e', 'aes256-cts', 'server']) ++realm.run([kadminl, 'setstr', 'server', 'session_enctypes', 'des3-cbc-sha1']) ++test_kvno(realm, 'DEPRECATED:des3-cbc-sha1', 'aes256-cts-hmac-sha1-96') ++realm.stop() ++ ++# 7: default config negotiates aes256-sha1 session key for RC4-only service. ++realm = K5Realm(create_host=False, get_creds=False) ++realm.run([kadminl, 'addprinc', '-randkey', '-e', 'rc4-hmac', 'server']) ++test_kvno(realm, 'aes256-cts-hmac-sha1-96', 'DEPRECATED:arcfour-hmac') ++realm.stop() ++ + success('sesskeynego') +diff --git a/src/util/k5test.py b/src/util/k5test.py +index d823653aa0..8e5f5ba8e9 100644 +--- a/src/util/k5test.py ++++ b/src/util/k5test.py +@@ -1338,9 +1338,16 @@ _passes = [ + # No special settings; exercises AES256. + ('default', None, None, None), + ++ # Exercise the DES3 enctype. ++ ('des3', None, ++ {'libdefaults': {'permitted_enctypes': 'des3 aes256-sha1'}}, ++ {'realms': {'$realm': { ++ 'supported_enctypes': 'des3-cbc-sha1:normal', ++ 'master_key_type': 'des3-cbc-sha1'}}}), ++ + # Exercise the arcfour enctype. + ('arcfour', None, +- {'libdefaults': {'permitted_enctypes': 'rc4'}}, ++ {'libdefaults': {'permitted_enctypes': 'rc4 aes256-sha1'}}, + {'realms': {'$realm': { + 'supported_enctypes': 'arcfour-hmac:normal', + 'master_key_type': 'arcfour-hmac'}}}), +-- +2.49.0 + diff --git a/0036-downstream-Remove-3des-support-cumulative-1.patch b/0036-downstream-Remove-3des-support-cumulative-1.patch new file mode 100644 index 0000000..9d116a2 --- /dev/null +++ b/0036-downstream-Remove-3des-support-cumulative-1.patch @@ -0,0 +1,260 @@ +From b575f7fa544c407ada7339359dcbc904d4faa6ae Mon Sep 17 00:00:00 2001 +From: Julien Rische +Date: Fri, 4 Apr 2025 15:08:36 +0200 +Subject: [PATCH] [downstream] Remove 3des support (cumulative 1) + +Remove mentions for the triple-DES encryption type which were added +since the previous downstream patch. +--- + README | 15 +++++++-------- + doc/admin/conf_files/krb5_conf.rst | 6 ------ + doc/admin/enctypes.rst | 11 ++--------- + doc/mitK5features.rst | 5 ++--- + src/include/k5-int.h | 2 -- + src/kdc/kdc_util.c | 2 -- + src/lib/krb5/krb/init_ctx.c | 5 ----- + src/man/krb5.conf.man | 6 ------ + src/tests/gssapi/t_enctypes.py | 5 ++--- + src/tests/t_etype_info.py | 4 ++-- + src/tests/t_sesskeynego.py | 8 -------- + src/util/k5test.py | 7 ------- + 12 files changed, 15 insertions(+), 61 deletions(-) + +diff --git a/README b/README +index 6d6f7f16e3..9341bd3dd8 100644 +--- a/README ++++ b/README +@@ -81,11 +81,11 @@ Triple-DES and RC4 transitions + ------------------------------ + + Beginning with the krb5-1.21 release, the KDC will not issue tickets +-with triple-DES or RC4 session keys unless explicitly configured using +-the new allow_des3 and allow_rc4 variables in [libdefaults]. To +-facilitate the negotiation of session keys, the KDC will assume that +-all services can handle aes256-sha1 session keys unless the service +-principal has a session_enctypes string attribute. ++with RC4 session keys unless explicitly configured using the new ++allow_rc4 variable in [libdefaults]. To facilitate the negotiation of ++session keys, the KDC will assume that all services can handle ++aes256-sha1 session keys unless the service principal has a ++session_enctypes string attribute. + + Beginning with the krb5-1.19 release, a warning will be issued if + initial credentials are acquired using the des3-cbc-sha1 encryption +@@ -164,9 +164,8 @@ Developer experience: + + Protocol evolution: + +-* The KDC will no longer issue tickets with RC4 or triple-DES session +- keys unless explicitly configured with the new allow_rc4 or +- allow_des3 variables respectively. ++* The KDC will no longer issue tickets with RC4 session keys unless ++ explicitly configured with the new allow_rc4 variable. + + * The KDC will assume that all services can handle aes256-sha1 session + keys unless the service principal has a session_enctypes string +diff --git a/doc/admin/conf_files/krb5_conf.rst b/doc/admin/conf_files/krb5_conf.rst +index d51fd3ce7e..d20dcf18e3 100644 +--- a/doc/admin/conf_files/krb5_conf.rst ++++ b/doc/admin/conf_files/krb5_conf.rst +@@ -95,12 +95,6 @@ Additionally, krb5.conf may include any of the relations described in + + The libdefaults section may contain any of the following relations: + +-**allow_des3** +- Permit the KDC to issue tickets with des3-cbc-sha1 session keys. +- In future releases, this flag will allow des3-cbc-sha1 to be used +- at all. The default value for this tag is false. (Added in +- release 1.21.) +- + **allow_rc4** + Permit the KDC to issue tickets with arcfour-hmac session keys. + In future releases, this flag will allow arcfour-hmac to be used +diff --git a/doc/admin/enctypes.rst b/doc/admin/enctypes.rst +index 2b4ed7da0b..6ce4638d5e 100644 +--- a/doc/admin/enctypes.rst ++++ b/doc/admin/enctypes.rst +@@ -49,8 +49,8 @@ The KDC chooses the session key enctype by taking the intersection of + its **permitted_enctypes** list, the list of long-term keys for the + most recent kvno of the service, and the client's requested list of + enctypes. Starting in krb5-1.21, all services are assumed to support +-aes256-cts-hmac-sha1-96; also, des3-cbc-sha1 and arcfour-hmac session +-keys will not be issued by default. ++aes256-cts-hmac-sha1-96; also, arcfour-hmac session keys will not be ++issued by default. + + Starting in krb5-1.11, it is possible to set a string attribute on a + service principal to control what session key enctypes the KDC may +@@ -90,13 +90,6 @@ affect how enctypes are chosen. + acceptable risk for your environment and the weak enctypes are + required for backward compatibility. + +-**allow_des3** +- was added in release 1.21 and defaults to *false*. Unless this +- flag is set to *true*, the KDC will not issue tickets with +- des3-cbc-sha1 session keys. In a future release, this flag will +- control whether des3-cbc-sha1 is permitted in similar fashion to +- weak enctypes. +- + **allow_rc4** + was added in release 1.21 and defaults to *false*. Unless this + flag is set to *true*, the KDC will not issue tickets with +diff --git a/doc/mitK5features.rst b/doc/mitK5features.rst +index cad0855724..64d746b0af 100644 +--- a/doc/mitK5features.rst ++++ b/doc/mitK5features.rst +@@ -659,9 +659,8 @@ Release 1.21 + + * Protocol evolution: + +- - The KDC will no longer issue tickets with RC4 or triple-DES +- session keys unless explicitly configured with the new allow_rc4 +- or allow_des3 variables respectively. ++ - The KDC will no longer issue tickets with RC4 session keys unless ++ explicitly configured with the new allow_rc4 variable. + + - The KDC will assume that all services can handle aes256-sha1 + session keys unless the service principal has a session_enctypes +diff --git a/src/include/k5-int.h b/src/include/k5-int.h +index d0a263aa7d..82a763298d 100644 +--- a/src/include/k5-int.h ++++ b/src/include/k5-int.h +@@ -181,7 +181,6 @@ typedef unsigned char u_char; + * matches the variable name. Keep these alphabetized. */ + #define KRB5_CONF_ACL_FILE "acl_file" + #define KRB5_CONF_ADMIN_SERVER "admin_server" +-#define KRB5_CONF_ALLOW_DES3 "allow_des3" + #define KRB5_CONF_ALLOW_RC4 "allow_rc4" + #define KRB5_CONF_ALLOW_WEAK_CRYPTO "allow_weak_crypto" + #define KRB5_CONF_AUTH_TO_LOCAL "auth_to_local" +@@ -1243,7 +1242,6 @@ struct _krb5_context { + struct _kdb_log_context *kdblog_context; + + krb5_boolean allow_weak_crypto; +- krb5_boolean allow_des3; + krb5_boolean allow_rc4; + krb5_boolean ignore_acceptor_hostname; + krb5_boolean enforce_ok_as_delegate; +diff --git a/src/kdc/kdc_util.c b/src/kdc/kdc_util.c +index c7b6e4090d..bafcf5f728 100644 +--- a/src/kdc/kdc_util.c ++++ b/src/kdc/kdc_util.c +@@ -1113,8 +1113,6 @@ select_session_keytype(krb5_context context, krb5_db_entry *server, + * unless they are explicitly allowed. In the future they will be more + * comprehensively disabled and eventually removed. + */ +- if (ktype[i] == ENCTYPE_DES3_CBC_SHA1 && !context->allow_des3) +- continue; + if (ktype[i] == ENCTYPE_ARCFOUR_HMAC && !context->allow_rc4) + continue; + +diff --git a/src/lib/krb5/krb/init_ctx.c b/src/lib/krb5/krb/init_ctx.c +index a32f8dbf03..82aba64c5e 100644 +--- a/src/lib/krb5/krb/init_ctx.c ++++ b/src/lib/krb5/krb/init_ctx.c +@@ -220,11 +220,6 @@ krb5_init_context_profile(profile_t profile, krb5_flags flags, + goto cleanup; + ctx->allow_weak_crypto = tmp; + +- retval = get_boolean(ctx, KRB5_CONF_ALLOW_DES3, 0, &tmp); +- if (retval) +- goto cleanup; +- ctx->allow_des3 = tmp; +- + retval = get_boolean(ctx, KRB5_CONF_ALLOW_RC4, 0, &tmp); + if (retval) + goto cleanup; +diff --git a/src/man/krb5.conf.man b/src/man/krb5.conf.man +index 6c0e9aff8c..4b53988712 100644 +--- a/src/man/krb5.conf.man ++++ b/src/man/krb5.conf.man +@@ -178,12 +178,6 @@ kdc.conf(5), but it is not a recommended practice. + The libdefaults section may contain any of the following relations: + .INDENT 0.0 + .TP +-\fBallow_des3\fP +-Permit the KDC to issue tickets with des3\-cbc\-sha1 session keys. +-In future releases, this flag will allow des3\-cbc\-sha1 to be used +-at all. The default value for this tag is false. (Added in +-release 1.21.) +-.TP + \fBallow_rc4\fP + Permit the KDC to issue tickets with arcfour\-hmac session keys. + In future releases, this flag will allow arcfour\-hmac to be used +diff --git a/src/tests/gssapi/t_enctypes.py b/src/tests/gssapi/t_enctypes.py +index e6bde47afc..1bb8c40b6b 100755 +--- a/src/tests/gssapi/t_enctypes.py ++++ b/src/tests/gssapi/t_enctypes.py +@@ -10,9 +10,8 @@ d_rc4 = 'DEPRECATED:arcfour-hmac' + + # These tests make assumptions about the default enctype lists, so set + # them explicitly rather than relying on the library defaults. +-supp='aes256-cts:normal aes128-cts:normal des3-cbc-sha1:normal rc4-hmac:normal' +-conf = {'libdefaults': {'permitted_enctypes': 'aes des3 rc4', +- 'allow_des3': 'true', 'allow_rc4': 'true'}, ++supp='aes256-cts:normal aes128-cts:normal rc4-hmac:normal' ++conf = {'libdefaults': {'permitted_enctypes': 'aes rc4', 'allow_rc4': 'true'}, + 'realms': {'$realm': {'supported_enctypes': supp}}} + realm = K5Realm(krb5_conf=conf) + shutil.copyfile(realm.ccache, os.path.join(realm.testdir, 'save')) +diff --git a/src/tests/t_etype_info.py b/src/tests/t_etype_info.py +index 75d9621dd6..e82ff7ff07 100644 +--- a/src/tests/t_etype_info.py ++++ b/src/tests/t_etype_info.py +@@ -1,7 +1,7 @@ + from k5test import * + +-supported_enctypes = 'aes128-cts des3-cbc-sha1 rc4-hmac' +-conf = {'libdefaults': {'allow_des3': 'true', 'allow_rc4': 'true'}, ++supported_enctypes = 'aes128-cts rc4-hmac' ++conf = {'libdefaults': {'allow_rc4': 'true'}, + 'realms': {'$realm': {'supported_enctypes': supported_enctypes}}} + realm = K5Realm(create_host=False, get_creds=False, krb5_conf=conf) + +diff --git a/src/tests/t_sesskeynego.py b/src/tests/t_sesskeynego.py +index 5a213617b5..c7dba0ff5b 100755 +--- a/src/tests/t_sesskeynego.py ++++ b/src/tests/t_sesskeynego.py +@@ -26,7 +26,6 @@ conf3 = {'libdefaults': { + 'default_tgs_enctypes': 'rc4-hmac,aes128-cts'}} + conf4 = {'libdefaults': {'permitted_enctypes': 'aes256-cts'}} + conf5 = {'libdefaults': {'allow_rc4': 'true'}} +-conf6 = {'libdefaults': {'allow_des3': 'true'}} + # Test with client request and session_enctypes preferring aes128, but + # aes256 long-term key. + realm = K5Realm(krb5_conf=conf1, create_host=False, get_creds=False) +@@ -78,13 +77,6 @@ realm.run([kadminl, 'setstr', 'server', 'session_enctypes', 'rc4-hmac']) + test_kvno(realm, 'DEPRECATED:arcfour-hmac', 'aes256-cts-hmac-sha1-96') + realm.stop() + +-# 6: allow_des3 permits negotiation of des3-cbc-sha1 session key. +-realm = K5Realm(krb5_conf=conf6, create_host=False, get_creds=False) +-realm.run([kadminl, 'addprinc', '-randkey', '-e', 'aes256-cts', 'server']) +-realm.run([kadminl, 'setstr', 'server', 'session_enctypes', 'des3-cbc-sha1']) +-test_kvno(realm, 'DEPRECATED:des3-cbc-sha1', 'aes256-cts-hmac-sha1-96') +-realm.stop() +- + # 7: default config negotiates aes256-sha1 session key for RC4-only service. + realm = K5Realm(create_host=False, get_creds=False) + realm.run([kadminl, 'addprinc', '-randkey', '-e', 'rc4-hmac', 'server']) +diff --git a/src/util/k5test.py b/src/util/k5test.py +index 8e5f5ba8e9..b953827018 100644 +--- a/src/util/k5test.py ++++ b/src/util/k5test.py +@@ -1338,13 +1338,6 @@ _passes = [ + # No special settings; exercises AES256. + ('default', None, None, None), + +- # Exercise the DES3 enctype. +- ('des3', None, +- {'libdefaults': {'permitted_enctypes': 'des3 aes256-sha1'}}, +- {'realms': {'$realm': { +- 'supported_enctypes': 'des3-cbc-sha1:normal', +- 'master_key_type': 'des3-cbc-sha1'}}}), +- + # Exercise the arcfour enctype. + ('arcfour', None, + {'libdefaults': {'permitted_enctypes': 'rc4 aes256-sha1'}}, +-- +2.49.0 + diff --git a/0037-Add-PKINIT-paChecksum2-from-MS-PKCA-v20230920.patch b/0037-Add-PKINIT-paChecksum2-from-MS-PKCA-v20230920.patch new file mode 100644 index 0000000..362faf7 --- /dev/null +++ b/0037-Add-PKINIT-paChecksum2-from-MS-PKCA-v20230920.patch @@ -0,0 +1,692 @@ +From a4a965b646fbaabe142c39904c50ad6b9b23f98b Mon Sep 17 00:00:00 2001 +From: Julien Rische +Date: Tue, 14 Jan 2025 13:31:11 +0100 +Subject: [PATCH] Add PKINIT paChecksum2 from MS-PKCA v20230920 + +In 2023, Microsoft updated MS-PKCA to add the optional paChecksum2 +element in the PKAuthenticator sequence. This checksum accepts SHA-1, +SHA-256, SHA-384, and SHA-512 digests. + +In Windows Server 2025, this checksum becomes mandatory when using +PKINIT with FFDH (but strangely not with ECDH if SHA-1 is configured as +allowed). + +[ghudson@mit.edu: refactored crypto interfaces to reduce complexity of +calling code] + +ticket: 9166 (new) +(cherry picked from commit 310793ba63782af5ffa3a95d20e41f8f03ca7e00) +--- + src/include/k5-int-pkinit.h | 25 ++-- + src/lib/krb5/asn.1/asn1_k_encode.c | 18 ++- + src/plugins/preauth/pkinit/pkinit.h | 1 + + src/plugins/preauth/pkinit/pkinit_clnt.c | 41 +++---- + src/plugins/preauth/pkinit/pkinit_constants.c | 42 +++++-- + src/plugins/preauth/pkinit/pkinit_crypto.h | 24 +++- + .../preauth/pkinit/pkinit_crypto_openssl.c | 116 +++++++++++++++++- + src/plugins/preauth/pkinit/pkinit_kdf_test.c | 4 +- + src/plugins/preauth/pkinit/pkinit_lib.c | 16 ++- + src/plugins/preauth/pkinit/pkinit_srv.c | 38 ++---- + src/plugins/preauth/pkinit/pkinit_trace.h | 5 +- + src/tests/asn.1/krb5_decode_test.c | 2 +- + src/tests/asn.1/ktest.c | 7 +- + src/tests/asn.1/ktest_equal.c | 2 +- + src/tests/asn.1/pkinit_encode.out | 2 +- + src/tests/asn.1/pkinit_trval.out | 2 +- + 16 files changed, 250 insertions(+), 95 deletions(-) + +diff --git a/src/include/k5-int-pkinit.h b/src/include/k5-int-pkinit.h +index 915904e518..cf6b1f99c5 100644 +--- a/src/include/k5-int-pkinit.h ++++ b/src/include/k5-int-pkinit.h +@@ -36,21 +36,28 @@ + * pkinit structures + */ + +-/* PKAuthenticator */ +-typedef struct _krb5_pk_authenticator { +- krb5_int32 cusec; /* (0..999999) */ +- krb5_timestamp ctime; +- krb5_int32 nonce; /* (0..4294967295) */ +- krb5_checksum paChecksum; +- krb5_data *freshnessToken; +-} krb5_pk_authenticator; +- + /* AlgorithmIdentifier */ + typedef struct _krb5_algorithm_identifier { + krb5_data algorithm; /* OID */ + krb5_data parameters; /* Optional */ + } krb5_algorithm_identifier; + ++/* PAChecksum2 */ ++typedef struct _krb5_pachecksum2 { ++ krb5_data checksum; ++ krb5_algorithm_identifier algorithmIdentifier; ++} krb5_pachecksum2; ++ ++/* PKAuthenticator */ ++typedef struct _krb5_pk_authenticator { ++ krb5_int32 cusec; /* (0..999999) */ ++ krb5_timestamp ctime; ++ krb5_int32 nonce; /* (0..4294967295) */ ++ krb5_data paChecksum; ++ krb5_data *freshnessToken; /* Optional */ ++ krb5_pachecksum2 *paChecksum2; /* Optional */ ++} krb5_pk_authenticator; ++ + /** AuthPack from RFC 4556*/ + typedef struct _krb5_auth_pack { + krb5_pk_authenticator pkAuthenticator; +diff --git a/src/lib/krb5/asn.1/asn1_k_encode.c b/src/lib/krb5/asn.1/asn1_k_encode.c +index 5378b5c23b..cf7b500837 100644 +--- a/src/lib/krb5/asn.1/asn1_k_encode.c ++++ b/src/lib/krb5/asn.1/asn1_k_encode.c +@@ -1394,20 +1394,30 @@ DEFSEQTYPE(pkinit_supp_pub_info, krb5_pkinit_supp_pub_info, + MAKE_ENCODER(encode_krb5_pkinit_supp_pub_info, pkinit_supp_pub_info); + MAKE_ENCODER(encode_krb5_sp80056a_other_info, sp80056a_other_info); + +-/* A krb5_checksum encoded as an OCTET STRING, for PKAuthenticator. */ +-DEFCOUNTEDTYPE(ostring_checksum, krb5_checksum, contents, length, octetstring); ++DEFFIELD(pachecksum2_0, krb5_pachecksum2, checksum, 0, ostring_data); ++DEFFIELD(pachecksum2_1, krb5_pachecksum2, algorithmIdentifier, 1, ++ algorithm_identifier); ++static const struct atype_info *pachecksum2_fields[] = { ++ &k5_atype_pachecksum2_0, &k5_atype_pachecksum2_1 ++}; ++DEFSEQTYPE(pachecksum2, krb5_pachecksum2, pachecksum2_fields); ++ ++DEFPTRTYPE(pachecksum2_ptr, pachecksum2); ++DEFOPTIONALZEROTYPE(opt_pachecksum2_ptr, pachecksum2_ptr); + + DEFFIELD(pk_authenticator_0, krb5_pk_authenticator, cusec, 0, int32); + DEFFIELD(pk_authenticator_1, krb5_pk_authenticator, ctime, 1, kerberos_time); + DEFFIELD(pk_authenticator_2, krb5_pk_authenticator, nonce, 2, int32); + DEFFIELD(pk_authenticator_3, krb5_pk_authenticator, paChecksum, 3, +- ostring_checksum); ++ ostring_data); + DEFFIELD(pk_authenticator_4, krb5_pk_authenticator, freshnessToken, 4, + opt_ostring_data_ptr); ++DEFFIELD(pk_authenticator_5, krb5_pk_authenticator, paChecksum2, 5, ++ opt_pachecksum2_ptr); + static const struct atype_info *pk_authenticator_fields[] = { + &k5_atype_pk_authenticator_0, &k5_atype_pk_authenticator_1, + &k5_atype_pk_authenticator_2, &k5_atype_pk_authenticator_3, +- &k5_atype_pk_authenticator_4 ++ &k5_atype_pk_authenticator_4, &k5_atype_pk_authenticator_5 + }; + DEFSEQTYPE(pk_authenticator, krb5_pk_authenticator, pk_authenticator_fields); + +diff --git a/src/plugins/preauth/pkinit/pkinit.h b/src/plugins/preauth/pkinit/pkinit.h +index 7ba7155bb4..a1564b6df2 100644 +--- a/src/plugins/preauth/pkinit/pkinit.h ++++ b/src/plugins/preauth/pkinit/pkinit.h +@@ -338,6 +338,7 @@ void free_krb5_external_principal_identifier(krb5_external_principal_identifier + void free_krb5_algorithm_identifiers(krb5_algorithm_identifier ***in); + void free_krb5_algorithm_identifier(krb5_algorithm_identifier *in); + void free_krb5_kdc_dh_key_info(krb5_kdc_dh_key_info **in); ++void free_pachecksum2(krb5_context context, krb5_pachecksum2 **in); + krb5_error_code pkinit_copy_krb5_data(krb5_data *dst, const krb5_data *src); + + +diff --git a/src/plugins/preauth/pkinit/pkinit_clnt.c b/src/plugins/preauth/pkinit/pkinit_clnt.c +index b08022a214..433f477538 100644 +--- a/src/plugins/preauth/pkinit/pkinit_clnt.c ++++ b/src/plugins/preauth/pkinit/pkinit_clnt.c +@@ -56,10 +56,9 @@ use_content_info(krb5_context context, pkinit_req_context req, + static krb5_error_code + pkinit_as_req_create(krb5_context context, pkinit_context plgctx, + pkinit_req_context reqctx, krb5_timestamp ctsec, +- krb5_int32 cusec, krb5_ui_4 nonce, +- const krb5_checksum *cksum, +- krb5_principal client, krb5_principal server, +- krb5_data **as_req); ++ krb5_int32 cusec, krb5_ui_4 nonce, const krb5_data *cksum, ++ const krb5_pachecksum2 *cksum2, krb5_principal client, ++ krb5_principal server, krb5_data **as_req); + + static krb5_error_code + pkinit_as_rep_parse(krb5_context context, pkinit_context plgctx, +@@ -89,7 +88,8 @@ pa_pkinit_gen_req(krb5_context context, + krb5_timestamp ctsec = 0; + krb5_int32 cusec = 0; + krb5_ui_4 nonce = 0; +- krb5_checksum cksum; ++ krb5_data cksum = empty_data(); ++ krb5_pachecksum2 *cksum2 = NULL; + krb5_data *der_req = NULL; + krb5_pa_data **return_pa_data = NULL; + +@@ -118,15 +118,10 @@ pa_pkinit_gen_req(krb5_context context, + goto cleanup; + } + +- retval = krb5_c_make_checksum(context, CKSUMTYPE_SHA1, NULL, 0, der_req, +- &cksum); ++ retval = crypto_generate_checksums(context, der_req, &cksum, &cksum2); + if (retval) + goto cleanup; +- TRACE_PKINIT_CLIENT_REQ_CHECKSUM(context, &cksum); +-#ifdef DEBUG_CKSUM +- pkiDebug("calculating checksum on buf size (%d)\n", der_req->length); +- print_buffer(der_req->data, der_req->length); +-#endif ++ TRACE_PKINIT_CLIENT_REQ_CHECKSUMS(context, &cksum, cksum2); + + retval = cb->get_preauth_time(context, rock, TRUE, &ctsec, &cusec); + if (retval) +@@ -140,7 +135,8 @@ pa_pkinit_gen_req(krb5_context context, + nonce = request->nonce; + + retval = pkinit_as_req_create(context, plgctx, reqctx, ctsec, cusec, +- nonce, &cksum, request->client, request->server, &out_data); ++ nonce, &cksum, cksum2, request->client, ++ request->server, &out_data); + if (retval) { + pkiDebug("error %d on pkinit_as_req_create; aborting PKINIT\n", + (int) retval); +@@ -168,23 +164,19 @@ pa_pkinit_gen_req(krb5_context context, + + cleanup: + krb5_free_data(context, der_req); +- krb5_free_checksum_contents(context, &cksum); ++ krb5_free_data_contents(context, &cksum); ++ free_pachecksum2(context, &cksum2); + krb5_free_data(context, out_data); + krb5_free_pa_data(context, return_pa_data); + return retval; + } + + static krb5_error_code +-pkinit_as_req_create(krb5_context context, +- pkinit_context plgctx, +- pkinit_req_context reqctx, +- krb5_timestamp ctsec, +- krb5_int32 cusec, +- krb5_ui_4 nonce, +- const krb5_checksum * cksum, +- krb5_principal client, +- krb5_principal server, +- krb5_data ** as_req) ++pkinit_as_req_create(krb5_context context, pkinit_context plgctx, ++ pkinit_req_context reqctx, krb5_timestamp ctsec, ++ krb5_int32 cusec, krb5_ui_4 nonce, const krb5_data *cksum, ++ const krb5_pachecksum2 *cksum2, krb5_principal client, ++ krb5_principal server, krb5_data **as_req) + { + krb5_error_code retval = ENOMEM; + krb5_data spki = empty_data(), *coded_auth_pack = NULL; +@@ -202,6 +194,7 @@ pkinit_as_req_create(krb5_context context, + auth_pack.pkAuthenticator.paChecksum = *cksum; + if (!reqctx->opts->disable_freshness) + auth_pack.pkAuthenticator.freshnessToken = reqctx->freshness_token; ++ auth_pack.pkAuthenticator.paChecksum2 = (krb5_pachecksum2 *)cksum2; + auth_pack.clientDHNonce.length = 0; + auth_pack.supportedKDFs = (krb5_data **)supported_kdf_alg_ids; + +diff --git a/src/plugins/preauth/pkinit/pkinit_constants.c b/src/plugins/preauth/pkinit/pkinit_constants.c +index 905e90d29c..a32b373c32 100644 +--- a/src/plugins/preauth/pkinit/pkinit_constants.c ++++ b/src/plugins/preauth/pkinit/pkinit_constants.c +@@ -34,25 +34,49 @@ + + /* RFC 8636 id-pkinit-kdf-ah-sha1: iso(1) identified-organization(3) dod(6) + * internet(1) security(5) kerberosv5(2) pkinit(3) kdf(6) sha1(1) */ +-static char sha1_oid[8] = { 0x2B, 0x06, 0x01, 0x05, 0x02, 0x03, 0x06, 0x01 }; ++static char kdf_sha1[8] = { 0x2B, 0x06, 0x01, 0x05, 0x02, 0x03, 0x06, 0x01 }; + /* RFC 8636 id-pkinit-kdf-ah-sha256: iso(1) identified-organization(3) dod(6) + * internet(1) security(5) kerberosv5(2) pkinit(3) kdf(6) sha256(2) */ +-static char sha256_oid[8] = { 0x2B, 0x06, 0x01, 0x05, 0x02, 0x03, 0x06, 0x02 }; ++static char kdf_sha256[8] = { 0x2B, 0x06, 0x01, 0x05, 0x02, 0x03, 0x06, 0x02 }; + /* RFC 8636 id-pkinit-kdf-ah-sha512: iso(1) identified-organization(3) dod(6) + * internet(1) security(5) kerberosv5(2) pkinit(3) kdf(6) sha512(3) */ +-static char sha512_oid[8] = { 0x2B, 0x06, 0x01, 0x05, 0x02, 0x03, 0x06, 0x03 }; ++static char kdf_sha512[8] = { 0x2B, 0x06, 0x01, 0x05, 0x02, 0x03, 0x06, 0x03 }; + +-const krb5_data sha1_id = { KV5M_DATA, sizeof(sha1_oid), sha1_oid }; +-const krb5_data sha256_id = { KV5M_DATA, sizeof(sha256_oid), sha256_oid }; +-const krb5_data sha512_id = { KV5M_DATA, sizeof(sha512_oid), sha512_oid }; ++const krb5_data kdf_sha1_id = { KV5M_DATA, sizeof(kdf_sha1), kdf_sha1 }; ++const krb5_data kdf_sha256_id = { KV5M_DATA, sizeof(kdf_sha256), kdf_sha256 }; ++const krb5_data kdf_sha512_id = { KV5M_DATA, sizeof(kdf_sha512), kdf_sha512 }; + + krb5_data const * const supported_kdf_alg_ids[] = { +- &sha256_id, +- &sha1_id, +- &sha512_id, ++ &kdf_sha256_id, ++ &kdf_sha1_id, ++ &kdf_sha512_id, + NULL + }; + ++/* RFC 3370 sha-1: iso(1) identified-organization(3) oiw(14) secsig(3) ++ * algorithm(2) 26 */ ++static char cms_sha1[] = { 0x2b, 0x0e, 0x03, 0x02, 0x1a }; ++/* RFC 5754 id-sha256: joint-iso-itu-t(2) country(16) us(840) organization(1) ++ * gov(101) csor(3) nistalgorithm(4) hashalgs(2) 1 */ ++static char cms_sha256[] = { ++ 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01 ++}; ++/* RFC 5754 id-sha384: joint-iso-itu-t(2) country(16) us(840) organization(1) ++ * gov(101) csor(3) nistalgorithm(4) hashalgs(2) 2 */ ++static char cms_sha384[] = { ++ 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02 ++}; ++/* RFC 5754 id-sha512: joint-iso-itu-t(2) country(16) us(840) organization(1) ++ * gov(101) csor(3) nistalgorithm(4) hashalgs(2) 3 */ ++static char cms_sha512[] = { ++ 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03 ++}; ++ ++const krb5_data cms_sha1_id = { KV5M_DATA, sizeof(cms_sha1), cms_sha1 }; ++const krb5_data cms_sha256_id = { KV5M_DATA, sizeof(cms_sha256), cms_sha256 }; ++const krb5_data cms_sha384_id = { KV5M_DATA, sizeof(cms_sha384), cms_sha384 }; ++const krb5_data cms_sha512_id = { KV5M_DATA, sizeof(cms_sha512), cms_sha512 }; ++ + /* RFC 4055 sha256WithRSAEncryption: iso(1) member-body(2) us(840) + * rsadsi(113549) pkcs(1) 1 11 */ + static char sha256WithRSAEncr_oid[9] = { +diff --git a/src/plugins/preauth/pkinit/pkinit_crypto.h b/src/plugins/preauth/pkinit/pkinit_crypto.h +index fd876e4850..3b12e904b1 100644 +--- a/src/plugins/preauth/pkinit/pkinit_crypto.h ++++ b/src/plugins/preauth/pkinit/pkinit_crypto.h +@@ -562,9 +562,13 @@ pkinit_alg_agility_kdf(krb5_context context, + krb5_data *pk_as_rep, + krb5_keyblock *key_block); + +-extern const krb5_data sha1_id; +-extern const krb5_data sha256_id; +-extern const krb5_data sha512_id; ++extern const krb5_data kdf_sha1_id; ++extern const krb5_data kdf_sha256_id; ++extern const krb5_data kdf_sha512_id; ++extern const krb5_data cms_sha1_id; ++extern const krb5_data cms_sha256_id; ++extern const krb5_data cms_sha384_id; ++extern const krb5_data cms_sha512_id; + extern const krb5_data oakley_1024; + extern const krb5_data oakley_2048; + extern const krb5_data oakley_4096; +@@ -597,4 +601,18 @@ crypto_req_cert_matching_data(krb5_context context, + + int parse_dh_min_bits(krb5_context context, const char *str); + ++/* Generate a SHA-1 checksum over body in *cksum1_out and a SHA-256 checksum ++ * over body in *cksum2_out with appropriate metadata. */ ++krb5_error_code ++crypto_generate_checksums(krb5_context context, const krb5_data *body, ++ krb5_data *cksum1_out, ++ krb5_pachecksum2 **cksum2_out); ++ ++/* Verify the SHA-1 checksum in cksum1 and the tagged checksum in cksum2. ++ * cksum2 may be NULL, in which case only cksum1 is verified. */ ++krb5_error_code ++crypto_verify_checksums(krb5_context context, krb5_data *body, ++ const krb5_data *cksum1, ++ const krb5_pachecksum2 *cksum2); ++ + #endif /* _PKINIT_CRYPTO_H */ +diff --git a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c +index 402bf1b9b3..429b7d202c 100644 +--- a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c ++++ b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c +@@ -2616,11 +2616,11 @@ cleanup: + static const EVP_MD * + algid_to_md(const krb5_data *alg_id) + { +- if (data_eq(*alg_id, sha1_id)) ++ if (data_eq(*alg_id, kdf_sha1_id)) + return EVP_sha1(); +- if (data_eq(*alg_id, sha256_id)) ++ if (data_eq(*alg_id, kdf_sha256_id)) + return EVP_sha256(); +- if (data_eq(*alg_id, sha512_id)) ++ if (data_eq(*alg_id, kdf_sha512_id)) + return EVP_sha512(); + return NULL; + } +@@ -5663,3 +5663,113 @@ parse_dh_min_bits(krb5_context context, const char *str) + TRACE_PKINIT_DH_INVALID_MIN_BITS(context, str); + return PKINIT_DEFAULT_DH_MIN_BITS; + } ++ ++/* Return the OpenSSL message digest type matching the given CMS OID, or NULL ++ * if it doesn't match any of the CMS OIDs we know about. */ ++static const EVP_MD * ++md_from_cms_oid(const krb5_data *alg_id) ++{ ++ if (data_eq(*alg_id, cms_sha1_id)) ++ return EVP_sha1(); ++ if (data_eq(*alg_id, cms_sha256_id)) ++ return EVP_sha256(); ++ if (data_eq(*alg_id, cms_sha384_id)) ++ return EVP_sha384(); ++ if (data_eq(*alg_id, cms_sha512_id)) ++ return EVP_sha512(); ++ return NULL; ++} ++ ++/* Compute a message digest of the given type over body, placing the result in ++ * *digest_out in allocated storage. Return true on success. */ ++static krb5_boolean ++make_digest(const krb5_data *body, const EVP_MD *md, krb5_data *digest_out) ++{ ++ krb5_error_code ret; ++ krb5_data d; ++ ++ if (md == NULL) ++ return FALSE; ++ ret = alloc_data(&d, EVP_MD_size(md)); ++ if (ret) ++ return FALSE; ++ if (!EVP_Digest(body->data, body->length, (uint8_t *)d.data, &d.length, md, ++ NULL)) { ++ free(d.data); ++ return FALSE; ++ } ++ *digest_out = d; ++ return TRUE; ++} ++ ++/* Return true if digest verifies for the given body and message digest ++ * type. */ ++static krb5_boolean ++check_digest(const krb5_data *body, const EVP_MD *md, const krb5_data *digest) ++{ ++ unsigned int digest_len; ++ uint8_t buf[EVP_MAX_MD_SIZE]; ++ ++ if (md == NULL) ++ return FALSE; ++ if (!EVP_Digest(body->data, body->length, buf, &digest_len, md, NULL)) ++ return FALSE; ++ return (digest->length == digest_len && ++ CRYPTO_memcmp(digest->data, buf, digest_len) == 0); ++} ++ ++krb5_error_code ++crypto_generate_checksums(krb5_context context, const krb5_data *body, ++ krb5_data *cksum1_out, krb5_pachecksum2 **cksum2_out) ++{ ++ krb5_data cksum1 = empty_data(); ++ krb5_pachecksum2 *cksum2 = NULL; ++ krb5_error_code ret; ++ ++ if (!make_digest(body, EVP_sha1(), &cksum1)) ++ goto fail; ++ ++ cksum2 = k5alloc(sizeof(*cksum2), &ret); ++ if (cksum2 == NULL) ++ goto fail; ++ ++ if (!make_digest(body, EVP_sha256(), &cksum2->checksum)) ++ goto fail; ++ ++ if (krb5int_copy_data_contents(context, &cms_sha256_id, ++ &cksum2->algorithmIdentifier.algorithm)) ++ goto fail; ++ ++ cksum2->algorithmIdentifier.parameters = empty_data(); ++ ++ *cksum1_out = cksum1; ++ *cksum2_out = cksum2; ++ return 0; ++ ++fail: ++ krb5_free_data_contents(context, &cksum1); ++ free_pachecksum2(context, &cksum2); ++ return KRB5_CRYPTO_INTERNAL; ++} ++ ++krb5_error_code ++crypto_verify_checksums(krb5_context context, krb5_data *body, ++ const krb5_data *cksum1, ++ const krb5_pachecksum2 *cksum2) ++{ ++ const EVP_MD *md; ++ ++ /* RFC 4556 doesn't say what error to return if the checksum doesn't match. ++ * Windows returns this one. */ ++ if (!check_digest(body, EVP_sha1(), cksum1)) ++ return KRB5KRB_AP_ERR_MODIFIED; ++ ++ if (cksum2 == NULL) ++ return 0; ++ ++ md = md_from_cms_oid(&cksum2->algorithmIdentifier.algorithm); ++ if (!check_digest(body, md, &cksum2->checksum)) ++ return KRB5KRB_AP_ERR_MODIFIED; ++ ++ return 0; ++} +diff --git a/src/plugins/preauth/pkinit/pkinit_kdf_test.c b/src/plugins/preauth/pkinit/pkinit_kdf_test.c +index 99c93ac128..dd6e8d7503 100644 +--- a/src/plugins/preauth/pkinit/pkinit_kdf_test.c ++++ b/src/plugins/preauth/pkinit/pkinit_kdf_test.c +@@ -126,7 +126,7 @@ main(int argc, char **argv) + + /* TEST 1: SHA-1/AES */ + /* set up algorithm id */ +- alg_id.algorithm = sha1_id; ++ alg_id.algorithm = kdf_sha1_id; + + enctype = enctype_aes; + +@@ -157,7 +157,7 @@ main(int argc, char **argv) + + /* TEST 2: SHA-256/AES */ + /* set up algorithm id */ +- alg_id.algorithm = sha256_id; ++ alg_id.algorithm = kdf_sha256_id; + + enctype = enctype_aes; + +diff --git a/src/plugins/preauth/pkinit/pkinit_lib.c b/src/plugins/preauth/pkinit/pkinit_lib.c +index 25965eb5d2..891f47fd26 100644 +--- a/src/plugins/preauth/pkinit/pkinit_lib.c ++++ b/src/plugins/preauth/pkinit/pkinit_lib.c +@@ -29,6 +29,7 @@ + * SUCH DAMAGES. + */ + ++#include "k5-int.h" + #include "pkinit.h" + + #define FAKECERT +@@ -119,8 +120,9 @@ free_krb5_auth_pack(krb5_auth_pack **in) + { + if ((*in) == NULL) return; + krb5_free_data_contents(NULL, &(*in)->clientPublicValue); +- free((*in)->pkAuthenticator.paChecksum.contents); ++ free((*in)->pkAuthenticator.paChecksum.data); + krb5_free_data(NULL, (*in)->pkAuthenticator.freshnessToken); ++ free_pachecksum2(NULL, &(*in)->pkAuthenticator.paChecksum2); + if ((*in)->supportedCMSTypes != NULL) + free_krb5_algorithm_identifiers(&((*in)->supportedCMSTypes)); + if ((*in)->supportedKDFs) { +@@ -196,6 +198,18 @@ free_krb5_kdc_dh_key_info(krb5_kdc_dh_key_info **in) + free(*in); + } + ++void ++free_pachecksum2(krb5_context context, krb5_pachecksum2 **in) ++{ ++ if (*in == NULL) ++ return; ++ krb5_free_data_contents(context, &(*in)->checksum); ++ krb5_free_data_contents(context, &(*in)->algorithmIdentifier.algorithm); ++ krb5_free_data_contents(context, &(*in)->algorithmIdentifier.parameters); ++ free(*in); ++ *in = NULL; ++} ++ + void + init_krb5_pa_pk_as_req(krb5_pa_pk_as_req **in) + { +diff --git a/src/plugins/preauth/pkinit/pkinit_srv.c b/src/plugins/preauth/pkinit/pkinit_srv.c +index e22bcb195b..f558308483 100644 +--- a/src/plugins/preauth/pkinit/pkinit_srv.c ++++ b/src/plugins/preauth/pkinit/pkinit_srv.c +@@ -428,11 +428,12 @@ pkinit_server_verify_padata(krb5_context context, + krb5_data authp_data = {0, 0, NULL}, krb5_authz = {0, 0, NULL}; + krb5_pa_pk_as_req *reqp = NULL; + krb5_auth_pack *auth_pack = NULL; ++ krb5_pk_authenticator *pka; + pkinit_kdc_context plgctx = NULL; + pkinit_kdc_req_context reqctx = NULL; + krb5_checksum cksum = {0, 0, 0, NULL}; + krb5_data *der_req = NULL; +- krb5_data k5data, *ftoken; ++ krb5_data k5data; + int is_signed = 1; + krb5_pa_data **e_data = NULL; + krb5_kdcpreauth_modreq modreq = NULL; +@@ -524,8 +525,9 @@ pkinit_server_verify_padata(krb5_context context, + pkiDebug("failed to decode krb5_auth_pack\n"); + goto cleanup; + } ++ pka = &auth_pack->pkAuthenticator; + +- retval = krb5_check_clockskew(context, auth_pack->pkAuthenticator.ctime); ++ retval = krb5_check_clockskew(context, pka->ctime); + if (retval) + goto cleanup; + +@@ -548,36 +550,14 @@ pkinit_server_verify_padata(krb5_context context, + goto cleanup; + } + der_req = cb->request_body(context, rock); +- retval = krb5_c_make_checksum(context, CKSUMTYPE_SHA1, NULL, 0, der_req, +- &cksum); +- if (retval) { +- pkiDebug("unable to calculate AS REQ checksum\n"); +- goto cleanup; +- } +- if (cksum.length != auth_pack->pkAuthenticator.paChecksum.length || +- k5_bcmp(cksum.contents, auth_pack->pkAuthenticator.paChecksum.contents, +- cksum.length) != 0) { +- pkiDebug("failed to match the checksum\n"); +-#ifdef DEBUG_CKSUM +- pkiDebug("calculating checksum on buf size (%d)\n", req_pkt->length); +- print_buffer(req_pkt->data, req_pkt->length); +- pkiDebug("received checksum type=%d size=%d ", +- auth_pack->pkAuthenticator.paChecksum.checksum_type, +- auth_pack->pkAuthenticator.paChecksum.length); +- print_buffer(auth_pack->pkAuthenticator.paChecksum.contents, +- auth_pack->pkAuthenticator.paChecksum.length); +- pkiDebug("expected checksum type=%d size=%d ", +- cksum.checksum_type, cksum.length); +- print_buffer(cksum.contents, cksum.length); +-#endif + +- retval = KRB5KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED; ++ retval = crypto_verify_checksums(context, der_req, &pka->paChecksum, ++ pka->paChecksum2); ++ if (retval) + goto cleanup; +- } + +- ftoken = auth_pack->pkAuthenticator.freshnessToken; +- if (ftoken != NULL) { +- retval = cb->check_freshness_token(context, rock, ftoken); ++ if (pka->freshnessToken != NULL) { ++ retval = cb->check_freshness_token(context, rock, pka->freshnessToken); + if (retval) + goto cleanup; + valid_freshness_token = TRUE; +diff --git a/src/plugins/preauth/pkinit/pkinit_trace.h b/src/plugins/preauth/pkinit/pkinit_trace.h +index 1faa6816d7..7b68d4b3b1 100644 +--- a/src/plugins/preauth/pkinit/pkinit_trace.h ++++ b/src/plugins/preauth/pkinit/pkinit_trace.h +@@ -58,8 +58,9 @@ + TRACE(c, "PKINIT client verified DH reply") + #define TRACE_PKINIT_CLIENT_REP_DH_FAIL(c) \ + TRACE(c, "PKINIT client could not verify DH reply") +-#define TRACE_PKINIT_CLIENT_REQ_CHECKSUM(c, cksum) \ +- TRACE(c, "PKINIT client computed kdc-req-body checksum {cksum}", cksum) ++#define TRACE_PKINIT_CLIENT_REQ_CHECKSUMS(c, ck1, ck2) \ ++ TRACE(c, "PKINIT client computed checksums: {hexdata} {hexdata}", \ ++ ck1, &(ck2)->checksum) + #define TRACE_PKINIT_CLIENT_REQ_DH(c) \ + TRACE(c, "PKINIT client making DH request") + #define TRACE_PKINIT_CLIENT_SAN_CONFIG_DNSNAME(c, host) \ +diff --git a/src/tests/asn.1/krb5_decode_test.c b/src/tests/asn.1/krb5_decode_test.c +index 2fa6dce8eb..f47849abad 100644 +--- a/src/tests/asn.1/krb5_decode_test.c ++++ b/src/tests/asn.1/krb5_decode_test.c +@@ -1174,7 +1174,7 @@ main(int argc, char **argv) + /* decode_krb5_auth_pack */ + { + setup(krb5_auth_pack,ktest_make_sample_auth_pack); +- decode_run("krb5_auth_pack","","30 81 85 A0 35 30 33 A0 05 02 03 01 E2 40 A1 11 18 0F 31 39 39 34 30 36 31 30 30 36 30 33 31 37 5A A2 03 02 01 2A A3 06 04 04 31 32 33 34 A4 0A 04 08 6B 72 62 35 64 61 74 61 A1 08 04 06 70 76 61 6C 75 65 A2 24 30 22 30 13 06 09 2A 86 48 86 F7 12 01 02 02 04 06 70 61 72 61 6D 73 30 0B 06 09 2A 86 48 86 F7 12 01 02 02 A3 0A 04 08 6B 72 62 35 64 61 74 61 A4 10 30 0E 30 0C A0 0A 06 08 6B 72 62 35 64 61 74 61", ++ decode_run("krb5_auth_pack","","30 81 89 A0 39 30 37 A0 05 02 03 01 E2 40 A1 11 18 0F 31 39 39 34 30 36 31 30 30 36 30 33 31 37 5A A2 03 02 01 2A A3 0A 04 08 6B 72 62 35 64 61 74 61 A4 0A 04 08 6B 72 62 35 64 61 74 61 A1 08 04 06 70 76 61 6C 75 65 A2 24 30 22 30 13 06 09 2A 86 48 86 F7 12 01 02 02 04 06 70 61 72 61 6D 73 30 0B 06 09 2A 86 48 86 F7 12 01 02 02 A3 0A 04 08 6B 72 62 35 64 61 74 61 A4 10 30 0E 30 0C A0 0A 06 08 6B 72 62 35 64 61 74 61", + acc.decode_krb5_auth_pack, + ktest_equal_auth_pack,ktest_free_auth_pack); + ktest_empty_auth_pack(&ref); +diff --git a/src/tests/asn.1/ktest.c b/src/tests/asn.1/ktest.c +index d37e4fa7e6..7f54aa3184 100644 +--- a/src/tests/asn.1/ktest.c ++++ b/src/tests/asn.1/ktest.c +@@ -700,9 +700,7 @@ ktest_make_sample_pk_authenticator(krb5_pk_authenticator *p) + p->cusec = SAMPLE_USEC; + p->ctime = SAMPLE_TIME; + p->nonce = SAMPLE_NONCE; +- ktest_make_sample_checksum(&p->paChecksum); +- /* We don't encode the checksum type, only the contents. */ +- p->paChecksum.checksum_type = 0; ++ ktest_make_sample_data(&p->paChecksum); + p->freshnessToken = ealloc(sizeof(krb5_data)); + ktest_make_sample_data(p->freshnessToken); + } +@@ -1604,8 +1602,7 @@ ktest_empty_pa_otp_req(krb5_pa_otp_req *p) + static void + ktest_empty_pk_authenticator(krb5_pk_authenticator *p) + { +- ktest_empty_checksum(&p->paChecksum); +- p->paChecksum.contents = NULL; ++ ktest_empty_data(&p->paChecksum); + krb5_free_data(NULL, p->freshnessToken); + p->freshnessToken = NULL; + } +diff --git a/src/tests/asn.1/ktest_equal.c b/src/tests/asn.1/ktest_equal.c +index b48a0285d2..13786dd1e5 100644 +--- a/src/tests/asn.1/ktest_equal.c ++++ b/src/tests/asn.1/ktest_equal.c +@@ -844,7 +844,7 @@ ktest_equal_pk_authenticator(krb5_pk_authenticator *ref, + p = p && scalar_equal(cusec); + p = p && scalar_equal(ctime); + p = p && scalar_equal(nonce); +- p = p && struct_equal(paChecksum, ktest_equal_checksum); ++ p = p && data_eq(ref->paChecksum, var->paChecksum); + return p; + } + +diff --git a/src/tests/asn.1/pkinit_encode.out b/src/tests/asn.1/pkinit_encode.out +index 6ec7aaa36a..a764182e15 100644 +--- a/src/tests/asn.1/pkinit_encode.out ++++ b/src/tests/asn.1/pkinit_encode.out +@@ -1,7 +1,7 @@ + encode_krb5_pa_pk_as_req: 30 38 80 08 6B 72 62 35 64 61 74 61 A1 22 30 20 30 1E 80 08 6B 72 62 35 64 61 74 61 81 08 6B 72 62 35 64 61 74 61 82 08 6B 72 62 35 64 61 74 61 82 08 6B 72 62 35 64 61 74 61 + encode_krb5_pa_pk_as_rep(dhInfo): A0 28 30 26 80 08 6B 72 62 35 64 61 74 61 A1 0A 04 08 6B 72 62 35 64 61 74 61 A2 0E 30 0C A0 0A 06 08 6B 72 62 35 64 61 74 61 + encode_krb5_pa_pk_as_rep(encKeyPack): 81 08 6B 72 62 35 64 61 74 61 +-encode_krb5_auth_pack: 30 81 85 A0 35 30 33 A0 05 02 03 01 E2 40 A1 11 18 0F 31 39 39 34 30 36 31 30 30 36 30 33 31 37 5A A2 03 02 01 2A A3 06 04 04 31 32 33 34 A4 0A 04 08 6B 72 62 35 64 61 74 61 A1 08 04 06 70 76 61 6C 75 65 A2 24 30 22 30 13 06 09 2A 86 48 86 F7 12 01 02 02 04 06 70 61 72 61 6D 73 30 0B 06 09 2A 86 48 86 F7 12 01 02 02 A3 0A 04 08 6B 72 62 35 64 61 74 61 A4 10 30 0E 30 0C A0 0A 06 08 6B 72 62 35 64 61 74 61 ++encode_krb5_auth_pack: 30 81 89 A0 39 30 37 A0 05 02 03 01 E2 40 A1 11 18 0F 31 39 39 34 30 36 31 30 30 36 30 33 31 37 5A A2 03 02 01 2A A3 0A 04 08 6B 72 62 35 64 61 74 61 A4 0A 04 08 6B 72 62 35 64 61 74 61 A1 08 04 06 70 76 61 6C 75 65 A2 24 30 22 30 13 06 09 2A 86 48 86 F7 12 01 02 02 04 06 70 61 72 61 6D 73 30 0B 06 09 2A 86 48 86 F7 12 01 02 02 A3 0A 04 08 6B 72 62 35 64 61 74 61 A4 10 30 0E 30 0C A0 0A 06 08 6B 72 62 35 64 61 74 61 + encode_krb5_kdc_dh_key_info: 30 25 A0 0B 03 09 00 6B 72 62 35 64 61 74 61 A1 03 02 01 2A A2 11 18 0F 31 39 39 34 30 36 31 30 30 36 30 33 31 37 5A + encode_krb5_reply_key_pack: 30 26 A0 13 30 11 A0 03 02 01 01 A1 0A 04 08 31 32 33 34 35 36 37 38 A1 0F 30 0D A0 03 02 01 01 A1 06 04 04 31 32 33 34 + encode_krb5_sp80056a_other_info: 30 81 81 30 0B 06 09 2A 86 48 86 F7 12 01 02 02 A0 32 04 30 30 2E A0 10 1B 0E 41 54 48 45 4E 41 2E 4D 49 54 2E 45 44 55 A1 1A 30 18 A0 03 02 01 01 A1 11 30 0F 1B 06 68 66 74 73 61 69 1B 05 65 78 74 72 61 A1 32 04 30 30 2E A0 10 1B 0E 41 54 48 45 4E 41 2E 4D 49 54 2E 45 44 55 A1 1A 30 18 A0 03 02 01 01 A1 11 30 0F 1B 06 68 66 74 73 61 69 1B 05 65 78 74 72 61 A2 0A 04 08 6B 72 62 35 64 61 74 61 +diff --git a/src/tests/asn.1/pkinit_trval.out b/src/tests/asn.1/pkinit_trval.out +index 46f4a34108..c47bd71f67 100644 +--- a/src/tests/asn.1/pkinit_trval.out ++++ b/src/tests/asn.1/pkinit_trval.out +@@ -38,7 +38,7 @@ encode_krb5_auth_pack: + . . [0] [Integer] 123456 + . . [1] [Generalized Time] "19940610060317Z" + . . [2] [Integer] 42 +-. . [3] [Octet String] "1234" ++. . [3] [Octet String] "krb5data" + . . [4] [Octet String] "krb5data" + . [1] [Octet String] "pvalue" + . [2] [Sequence/Sequence Of] +-- +2.49.0 + diff --git a/0038-downstream-Do-not-block-HMAC-MD4-5-in-FIPS-mode.patch b/0038-downstream-Do-not-block-HMAC-MD4-5-in-FIPS-mode.patch new file mode 100644 index 0000000..9e3f32d --- /dev/null +++ b/0038-downstream-Do-not-block-HMAC-MD4-5-in-FIPS-mode.patch @@ -0,0 +1,381 @@ +From bef72b26b7380d8f5537c0c4f5bc62b8ce4a4ce3 Mon Sep 17 00:00:00 2001 +From: Julien Rische +Date: Thu, 10 Apr 2025 10:04:22 +0200 +Subject: [PATCH] [downstream] Do not block HMAC-MD4/5 in FIPS mode + +To ensure RC4 HMAC-MD5 was not used in FIPS mode, access to HMAC-MD4/5 +was not allowed in this mode. However, since we provide the +"radius_md5_fips_override" configuration parameter to allow using RADIUS +regardless to the FIPS restrictions, we should allow HMAC-MD5 to be used +too in this case, because it is required for the newly supported +Message-Authenticator attribute. + +A FIPS mode check is added in calculate_mac() which will fail if +"radius_md5_fips_override" is not true. It will not affect interactions +between krb5kdc and ipa-otpd, because the Message-Authenticator +attribute is not generated in this case. +--- + src/lib/crypto/krb/crypto_int.h | 9 +++ + src/lib/crypto/openssl/Makefile.in | 9 ++- + src/lib/crypto/openssl/common.c | 80 +++++++++++++++++++ + .../crypto/openssl/hash_provider/hash_evp.c | 62 ++------------ + src/lib/crypto/openssl/hmac.c | 15 ++-- + src/lib/krad/packet.c | 19 +++-- + 6 files changed, 120 insertions(+), 74 deletions(-) + create mode 100644 src/lib/crypto/openssl/common.c + +diff --git a/src/lib/crypto/krb/crypto_int.h b/src/lib/crypto/krb/crypto_int.h +index 1ee4b30e02..ff67b6bd35 100644 +--- a/src/lib/crypto/krb/crypto_int.h ++++ b/src/lib/crypto/krb/crypto_int.h +@@ -36,6 +36,9 @@ + + #include + #if OPENSSL_VERSION_NUMBER >= 0x30000000L ++ ++#include ++ + /* + * OpenSSL 3.0 relegates MD4 and RC4 to the legacy provider, which must be + * explicitly loaded into a library context. Performing this loading within a +@@ -660,4 +663,10 @@ iov_cursor_advance(struct iov_cursor *c, size_t nblocks) + c->out_pos += nblocks * c->block_size; + } + ++#if OPENSSL_VERSION_NUMBER >= 0x30000000L ++ ++krb5_error_code k5_get_ossl_legacy_libctx(OSSL_LIB_CTX **libctx); ++ ++#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ ++ + #endif /* CRYPTO_INT_H */ +diff --git a/src/lib/crypto/openssl/Makefile.in b/src/lib/crypto/openssl/Makefile.in +index 8e4cdb8bbf..cc131000bd 100644 +--- a/src/lib/crypto/openssl/Makefile.in ++++ b/src/lib/crypto/openssl/Makefile.in +@@ -8,21 +8,24 @@ STLIBOBJS=\ + hmac.o \ + kdf.o \ + pbkdf2.o \ +- sha256.o ++ sha256.o \ ++ common.o + + OBJS=\ + $(OUTPRE)cmac.$(OBJEXT) \ + $(OUTPRE)hmac.$(OBJEXT) \ + $(OUTPRE)kdf.$(OBJEXT) \ + $(OUTPRE)pbkdf2.$(OBJEXT) \ +- $(OUTPRE)sha256.$(OBJEXT) ++ $(OUTPRE)sha256.$(OBJEXT) \ ++ $(OUTPRE)common.$(OBJEXT) + + SRCS=\ + $(srcdir)/cmac.c \ + $(srcdir)/hmac.c \ + $(srcdir)/kdf.c \ + $(srcdir)/pbkdf2.c \ +- $(srcdir)/sha256.c ++ $(srcdir)/sha256.c \ ++ $(srcdir)/common.c + + SUBDIROBJLISTS= md4/OBJS.ST \ + md5/OBJS.ST sha1/OBJS.ST sha2/OBJS.ST \ +diff --git a/src/lib/crypto/openssl/common.c b/src/lib/crypto/openssl/common.c +new file mode 100644 +index 0000000000..ced43fd54c +--- /dev/null ++++ b/src/lib/crypto/openssl/common.c +@@ -0,0 +1,80 @@ ++#include "crypto_int.h" ++ ++#if OPENSSL_VERSION_NUMBER >= 0x30000000L ++ ++#include ++#include ++#include ++#include ++ ++typedef struct ossl_legacy_context { ++ bool initialized; ++ OSSL_LIB_CTX *libctx; ++ OSSL_PROVIDER *default_provider; ++ OSSL_PROVIDER *legacy_provider; ++} ossl_legacy_context_t; ++ ++static thread_local ossl_legacy_context_t g_ossl_legacy_ctx; ++ ++static krb5_error_code ++init_ossl_legacy_ctx(ossl_legacy_context_t *ctx) ++{ ++ ctx->libctx = OSSL_LIB_CTX_new(); ++ if (!ctx->libctx) ++ return KRB5_CRYPTO_INTERNAL; ++ ++ /* Load both legacy and default provider as both may be needed. */ ++ ctx->default_provider = OSSL_PROVIDER_load(ctx->libctx, "default"); ++ ctx->legacy_provider = OSSL_PROVIDER_load(ctx->libctx, "legacy"); ++ ++ if (!(ctx->default_provider && ctx->legacy_provider)) ++ return KRB5_CRYPTO_INTERNAL; ++ ++ ctx->initialized = true; ++ return 0; ++} ++ ++static void ++deinit_ossl_legacy_ctx(ossl_legacy_context_t *ctx) ++{ ++ if (ctx->legacy_provider) ++ OSSL_PROVIDER_unload(ctx->legacy_provider); ++ ++ if (ctx->default_provider) ++ OSSL_PROVIDER_unload(ctx->default_provider); ++ ++ if (ctx->libctx) ++ OSSL_LIB_CTX_free(ctx->libctx); ++ ++ ctx->initialized = false; ++} ++ ++krb5_error_code ++k5_get_ossl_legacy_libctx(OSSL_LIB_CTX **libctx) ++{ ++ krb5_error_code err; ++ ++ if (!FIPS_mode()) { ++ if (libctx) ++ *libctx = NULL; ++ err = 0; ++ goto end; ++ } ++ ++ if (!g_ossl_legacy_ctx.initialized) { ++ err = init_ossl_legacy_ctx(&g_ossl_legacy_ctx); ++ if (err) { ++ deinit_ossl_legacy_ctx(&g_ossl_legacy_ctx); ++ goto end; ++ } ++ } ++ ++ if (libctx) ++ *libctx = g_ossl_legacy_ctx.libctx; ++ err = 0; ++ ++end: ++ return err; ++} ++ ++#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ +diff --git a/src/lib/crypto/openssl/hash_provider/hash_evp.c b/src/lib/crypto/openssl/hash_provider/hash_evp.c +index eb2e693e9f..2fd5d383d6 100644 +--- a/src/lib/crypto/openssl/hash_provider/hash_evp.c ++++ b/src/lib/crypto/openssl/hash_provider/hash_evp.c +@@ -44,48 +44,7 @@ + #define EVP_MD_CTX_free EVP_MD_CTX_destroy + #endif + +-#include + #include +-#include +- +-typedef struct ossl_lib_md_context { +- OSSL_LIB_CTX *libctx; +- OSSL_PROVIDER *default_provider; +- OSSL_PROVIDER *legacy_provider; +-} ossl_md_context_t; +- +-static thread_local ossl_md_context_t *ossl_md_ctx = NULL; +- +-static krb5_error_code +-init_ossl_md_ctx(ossl_md_context_t *ctx, const char *algo) +-{ +- ctx->libctx = OSSL_LIB_CTX_new(); +- if (!ctx->libctx) +- return KRB5_CRYPTO_INTERNAL; +- +- /* Load both legacy and default provider as both may be needed. */ +- ctx->default_provider = OSSL_PROVIDER_load(ctx->libctx, "default"); +- ctx->legacy_provider = OSSL_PROVIDER_load(ctx->libctx, "legacy"); +- +- if (!(ctx->default_provider && ctx->legacy_provider)) +- return KRB5_CRYPTO_INTERNAL; +- +- return 0; +-} +- +-static void +-deinit_ossl_ctx(ossl_md_context_t *ctx) +-{ +- if (ctx->legacy_provider) +- OSSL_PROVIDER_unload(ctx->legacy_provider); +- +- if (ctx->default_provider) +- OSSL_PROVIDER_unload(ctx->default_provider); +- +- if (ctx->libctx) +- OSSL_LIB_CTX_free(ctx->libctx); +-} +- + + static krb5_error_code + hash_evp(const EVP_MD *type, const krb5_crypto_iov *data, size_t num_data, +@@ -120,25 +79,14 @@ hash_legacy_evp(const char *algo, const krb5_crypto_iov *data, size_t num_data, + krb5_data *output) + { + krb5_error_code err; ++ OSSL_LIB_CTX *ossl_libctx; + EVP_MD *md = NULL; + +- if (!ossl_md_ctx) { +- ossl_md_ctx = malloc(sizeof(ossl_md_context_t)); +- if (!ossl_md_ctx) { +- err = ENOMEM; +- goto end; +- } +- +- err = init_ossl_md_ctx(ossl_md_ctx, algo); +- if (err) { +- deinit_ossl_ctx(ossl_md_ctx); +- free(ossl_md_ctx); +- ossl_md_ctx = NULL; +- goto end; +- } +- } ++ err = k5_get_ossl_legacy_libctx(&ossl_libctx); ++ if (err) ++ goto end; + +- md = EVP_MD_fetch(ossl_md_ctx->libctx, algo, NULL); ++ md = EVP_MD_fetch(ossl_libctx, algo, NULL); + if (!md) { + err = KRB5_CRYPTO_INTERNAL; + goto end; +diff --git a/src/lib/crypto/openssl/hmac.c b/src/lib/crypto/openssl/hmac.c +index 25a419d73a..8f9e88fec9 100644 +--- a/src/lib/crypto/openssl/hmac.c ++++ b/src/lib/crypto/openssl/hmac.c +@@ -59,7 +59,6 @@ + #if OPENSSL_VERSION_NUMBER >= 0x30000000L + #include + #include +-#include + #else + #include + #endif +@@ -112,11 +111,7 @@ map_digest(const struct krb5_hash_provider *hash) + return EVP_sha256(); + else if (hash == &krb5int_hash_sha384) + return EVP_sha384(); +- +- if (FIPS_mode()) +- return NULL; +- +- if (hash == &krb5int_hash_md5) ++ else if (hash == &krb5int_hash_md5) + return EVP_md5(); + else if (hash == &krb5int_hash_md4) + return EVP_md4(); +@@ -138,13 +133,19 @@ krb5int_hmac_keyblock(const struct krb5_hash_provider *hash, + EVP_MAC_CTX *ctx = NULL; + OSSL_PARAM params[2], *p = params; + size_t i = 0, md_len; ++ OSSL_LIB_CTX *ossl_libctx; ++ krb5_error_code err; + + if (md == NULL || keyblock->length > hash->blocksize) + return KRB5_CRYPTO_INTERNAL; + if (output->length < hash->hashsize) + return KRB5_BAD_MSIZE; + +- mac = EVP_MAC_fetch(NULL, "HMAC", NULL); ++ err = k5_get_ossl_legacy_libctx(&ossl_libctx); ++ if (err) ++ return err; ++ ++ mac = EVP_MAC_fetch(ossl_libctx, "HMAC", NULL); + if (mac == NULL) + return KRB5_CRYPTO_INTERNAL; + +diff --git a/src/lib/krad/packet.c b/src/lib/krad/packet.c +index 3c1a4d507e..b95c99df65 100644 +--- a/src/lib/krad/packet.c ++++ b/src/lib/krad/packet.c +@@ -278,7 +278,7 @@ lookup_msgauth_addr(const krad_packet *pkt) + * auth, which may be from pkt or from a corresponding request. + */ + static krb5_error_code +-calculate_mac(const char *secret, const krad_packet *pkt, ++calculate_mac(krb5_context ctx, const char *secret, const krad_packet *pkt, + const uint8_t auth[AUTH_FIELD_SIZE], + uint8_t mac_out[MD5_DIGEST_SIZE]) + { +@@ -288,6 +288,10 @@ calculate_mac(const char *secret, const krad_packet *pkt, + krb5_crypto_iov input[5]; + krb5_data ksecr, mac; + ++ /* Do not use HMAC-MD5 if not explicitly allowed */ ++ if (kr_use_fips(ctx)) ++ return KRB5_CRYPTO_INTERNAL; ++ + msgauth_attr = lookup_msgauth_addr(pkt); + if (msgauth_attr == NULL) + return EINVAL; +@@ -393,7 +397,8 @@ krad_packet_new_request(krb5_context ctx, const char *secret, krad_code code, + + if (msgauth_required) { + /* Calculate and set the Message-Authenticator MAC. */ +- retval = calculate_mac(secret, pkt, pkt_auth(pkt), pkt_attr(pkt) + 2); ++ retval = calculate_mac(ctx, secret, pkt, pkt_auth(pkt), ++ pkt_attr(pkt) + 2); + if (retval != 0) + goto error; + } +@@ -454,7 +459,7 @@ krad_packet_new_response(krb5_context ctx, const char *secret, krad_code code, + * section 5.14, use the authenticator from the request, not from the + * response. + */ +- retval = calculate_mac(secret, pkt, pkt_auth(request), ++ retval = calculate_mac(ctx, secret, pkt, pkt_auth(request), + pkt_attr(pkt) + 2); + if (retval != 0) + goto error; +@@ -476,7 +481,7 @@ error: + /* Verify the Message-Authenticator value in pkt, using the provided + * authenticator (which may be from pkt or from a corresponding request). */ + static krb5_error_code +-verify_msgauth(const char *secret, const krad_packet *pkt, ++verify_msgauth(krb5_context ctx, const char *secret, const krad_packet *pkt, + const uint8_t auth[AUTH_FIELD_SIZE]) + { + uint8_t mac[MD5_DIGEST_SIZE]; +@@ -488,7 +493,7 @@ verify_msgauth(const char *secret, const krad_packet *pkt, + if (msgauth == NULL) + return ENODATA; + +- retval = calculate_mac(secret, pkt, auth, mac); ++ retval = calculate_mac(ctx, secret, pkt, auth, mac); + if (retval) + return retval; + +@@ -561,7 +566,7 @@ krad_packet_decode_request(krb5_context ctx, const char *secret, + + /* Verify Message-Authenticator if present. */ + if (has_pkt_msgauth(req)) { +- retval = verify_msgauth(secret, req, pkt_auth(req)); ++ retval = verify_msgauth(ctx, secret, req, pkt_auth(req)); + if (retval) { + krad_packet_free(req); + return retval; +@@ -613,7 +618,7 @@ krad_packet_decode_response(krb5_context ctx, const char *secret, + + /* Verify Message-Authenticator if present. */ + if (has_pkt_msgauth(*rsppkt)) { +- if (verify_msgauth(secret, *rsppkt, pkt_auth(tmp)) != 0) ++ if (verify_msgauth(ctx, secret, *rsppkt, pkt_auth(tmp)) != 0) + continue; + } + +-- +2.49.0 + diff --git a/kdc.conf b/kdc.conf index c504e58..7b788e5 100644 --- a/kdc.conf +++ b/kdc.conf @@ -1,3 +1,7 @@ +[libdefaults] +# Allow RC4 HMAC-MD5 for session keys (see CVE-2022-37966) +#allow_rc4 = true + [kdcdefaults] kdc_ports = 88 kdc_tcp_ports = 88 diff --git a/krb5.spec b/krb5.spec index 08579be..abc71db 100644 --- a/krb5.spec +++ b/krb5.spec @@ -10,7 +10,7 @@ # # baserelease is what we have standardized across Fedora and what # rpmdev-bumpspec knows how to handle. -%global baserelease 7 +%global baserelease 8 # This should be e.g. beta1 or %%nil %global pre_release %nil @@ -93,6 +93,10 @@ Patch0031: 0031-Support-PKCS11-EC-client-certs-in-PKINIT.patch Patch0032: 0032-Improve-PKCS11-error-reporting-in-PKINIT.patch Patch0033: 0033-Set-missing-mask-flags-for-kdb5_util-operations.patch Patch0034: 0034-Prevent-overflow-when-calculating-ulog-block-size.patch +Patch0035: 0035-Don-t-issue-session-keys-with-deprecated-enctypes.patch +Patch0036: 0036-downstream-Remove-3des-support-cumulative-1.patch +Patch0037: 0037-Add-PKINIT-paChecksum2-from-MS-PKCA-v20230920.patch +Patch0038: 0038-downstream-Do-not-block-HMAC-MD4-5-in-FIPS-mode.patch License: Brian-Gladman-2-Clause AND BSD-2-Clause AND (BSD-2-Clause OR GPL-2.0-or-later) AND BSD-2-Clause-first-lines AND BSD-3-Clause AND BSD-4-Clause AND CMU-Mach-nodoc AND FSFULLRWD AND HPND AND HPND-export2-US AND HPND-export-US AND HPND-export-US-acknowledgement AND HPND-export-US-modify AND ISC AND MIT AND MIT-CMU AND OLDAP-2.8 AND OpenVision URL: https://web.mit.edu/kerberos/www/ @@ -734,6 +738,14 @@ exit 0 %{_datarootdir}/%{name}-tests/%{_arch} %changelog +* Mon Apr 28 2025 Julien Rische - 1.21.3-8 +- Do not block HMAC-MD4/5 in FIPS mode + Resolves: RHEL-88705 +- Don't issue RC4 session keys by default (CVE-2025-3576) + Resolves: RHEL-88047 +- Add PKINIT paChecksum2 from MS-PKCA v20230920 + Resolves: RHEL-74295 + * Wed Jan 29 2025 Julien Rische - 1.21.3-7 - Prevent overflow when calculating ulog block size (CVE-2025-24528) Resolves: RHEL-76758