From: Greg Hudson <ghudson@mit.edu>
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

CVE: CVE-2025-3576
Upstream-Status: Backport [https://github.com/krb5/krb5/commit/1b57a4d134bbd0e7c52d5885a92eccc815726463]
Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
---
 src/include/k5-int.h               |  4 ++++
 src/kdc/kdc_util.c                 | 10 +++++++++
 src/lib/krb5/krb/get_in_tkt.c      | 33 ++++++++++++++++++++----------
 src/lib/krb5/krb/init_ctx.c        | 10 +++++++++
 src/tests/gssapi/t_enctypes.py     |  2 +-
 src/tests/t_etype_info.py          |  2 +-
 src/tests/t_sesskeynego.py         | 28 +++++++++++++++++++++++--
 src/util/k5test.py                 |  4 ++--
 8 files changed, 74 insertions(+), 19 deletions(-)

diff --git a/src/include/k5-int.h b/src/include/k5-int.h
index 6522422..fffc6f8 100644
--- a/src/include/k5-int.h
+++ b/src/include/k5-int.h
@@ -180,6 +180,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_AP_REQ_CHECKSUM_TYPE         "ap_req_checksum_type"
 #define KRB5_CONF_AUTH_TO_LOCAL                "auth_to_local"
@@ -1220,6 +1222,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 dns_canonicalize_hostname;
 
diff --git a/src/kdc/kdc_util.c b/src/kdc/kdc_util.c
index 0155c28..d62aca6 100644
--- a/src/kdc/kdc_util.c
+++ b/src/kdc/kdc_util.c
@@ -1012,6 +1012,16 @@ select_session_keytype(kdc_realm_t *kdc_active_realm, krb5_db_entry *server,
         if (!krb5_is_permitted_enctype(kdc_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 && !kdc_context->allow_des3)
+            continue;
+        if (ktype[i] == ENCTYPE_ARCFOUR_HMAC && !kdc_context->allow_rc4)
+            continue;
+
         if (dbentry_supports_enctype(kdc_active_realm, 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 51580c9..80f4d93 100644
--- a/src/lib/krb5/krb/get_in_tkt.c
+++ b/src/lib/krb5/krb/get_in_tkt.c
@@ -1509,22 +1509,32 @@ accept_method_data(krb5_context context, krb5_init_creds_context ctx)
                                      ctx->method_padata);
 }
 
-/* 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);
 }
@@ -1728,7 +1738,8 @@ init_creds_step_reply(krb5_context context,
 
     /* success */
     ctx->complete = TRUE;
-    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 947e504..077c428 100644
--- a/src/lib/krb5/krb/init_ctx.c
+++ b/src/lib/krb5/krb/init_ctx.c
@@ -208,6 +208,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 ee43ff0..92abb1b 100755
--- a/src/tests/gssapi/t_enctypes.py
+++ b/src/tests/gssapi/t_enctypes.py
@@ -14,7 +14,7 @@ rc4 = 'arcfour-hmac'
 
 # These tests make assumptions about the default enctype lists, so set
 # them explicitly rather than relying on the library defaults.
-enctypes='aes des3 rc4'
+enctypes='aes des3 rc4 allow_des3 allow_rc4'
 supp='aes256-cts:normal aes128-cts:normal des3-cbc-sha1:normal rc4-hmac:normal'
 conf = {'libdefaults': {
         'default_tgs_enctypes': enctypes,
diff --git a/src/tests/t_etype_info.py b/src/tests/t_etype_info.py
index 2026e78..f6e7804 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 des-cbc-crc:afs3'
-conf = {'libdefaults': {'allow_weak_crypto': 'true'},
+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)
 
diff --git a/src/tests/t_sesskeynego.py b/src/tests/t_sesskeynego.py
index 4480923..5143b0b 100755
--- a/src/tests/t_sesskeynego.py
+++ b/src/tests/t_sesskeynego.py
@@ -30,6 +30,8 @@ conf4 = {'libdefaults': {
         'default_tgs_enctypes': 'des-cbc-crc,rc4-hmac,aes256-cts'},
          'realms': {'$realm': {'des_crc_session_supported': 'false'}}}
 
+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)
@@ -59,10 +61,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, 'arcfour-hmac', 'aes256-cts-hmac-sha1-96')
+test_kvno(realm, 'aes128-cts-hmac-sha1-96', 'aes256-cts-hmac-sha1-96')
 
 # 3c: Test des-cbc-crc default assumption.
 realm.run([kadminl, 'delstr', 'server', 'session_enctypes'])
@@ -75,4 +79,24 @@ realm.run([kadminl, 'addprinc', '-randkey', '-e', 'aes256-cts', 'server'])
 test_kvno(realm, 'aes256-cts-hmac-sha1-96', 'aes256-cts-hmac-sha1-96')
 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, 'aes128-cts-hmac-sha1-96', '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 3aec1ef..ad47228 100644
--- a/src/util/k5test.py
+++ b/src/util/k5test.py
@@ -1262,7 +1262,7 @@ _passes = [
      {'libdefaults': {
                 'default_tgs_enctypes': 'des3',
                 'default_tkt_enctypes': 'des3',
-                'permitted_enctypes': 'des3'}},
+                'permitted_enctypes': 'des3 aes256-sha1'}},
      {'realms': {'$realm': {
                     'supported_enctypes': 'des3-cbc-sha1:normal',
                     'master_key_type': 'des3-cbc-sha1'}}}),
@@ -1272,7 +1272,7 @@ _passes = [
      {'libdefaults': {
                 'default_tgs_enctypes': 'rc4',
                 'default_tkt_enctypes': 'rc4',
-                'permitted_enctypes': 'rc4'}},
+                'permitted_enctypes': 'rc4 aes256-sha1'}},
      {'realms': {'$realm': {
                     'supported_enctypes': 'arcfour-hmac:normal',
                     'master_key_type': 'arcfour-hmac'}}}),
-- 
2.50.1

