CVE-2018-20217: Reachable Assertion (CWE-617)
value: A Reachable Assertion issue was discovered in the KDC in MIT Kerberos 5 (aka krb5) before 1.17. If an attacker can obtain a krbtgt ticket using an older encryption type (single-DES, triple-DES, or RC4), the attacker can crash the KDC by making an S4U2Self request.
CWE description: The product contains an assert() or similar statement that can be triggered by an attacker, which leads to an application exit or other behavior that is more severe than necessary.
CVSS v3 base score: 5.3
Published: 2018-12-26T21:29Z
Repository: https://github.com/krb5/krb5
Language: C
Files changed: src/kdc/kdc_util.c, src/lib/krb5/krb/s4u_creds.c, src/tests/gssapi/t_s4u.py
Commit date: 2018-12-08 15:21:14 -0500
Commit message: Ignore password attributes for S4U2Self requests

For consistency with Windows KDCs, allow protocol transition to work
even if the password has expired or needs changing.

Also, when looking up an enterprise principal with an AS request,
treat ERR_KEY_EXP as confirmation that the client is present in the
realm.

[ghudson@mit.edu: added comment in kdc_process_s4u2self_req(); edited
commit message]

ticket: 8763 (new)
tags: pullup
target_version: 1.17

Diff: diff --git a/src/kdc/kdc_util.c b/src/kdc/kdc_util.c
index 6d53173fb01..6517a213cd2 100644
--- a/src/kdc/kdc_util.c
+++ b/src/kdc/kdc_util.c
@@ -1605,10 +1605,15 @@ kdc_process_s4u2self_req(kdc_realm_t *kdc_active_realm,
             return code; /* caller can free for_user */
         }
 
         memset(&no_server, 0, sizeof(no_server));
 
+        /* Ignore password expiration and needchange attributes (as Windows
+         * does), since S4U2Self is not password authentication. */
+        princ->pw_expiration = 0;
+        clear(princ->attributes, KRB5_KDB_REQUIRES_PWCHANGE);
+
         code = validate_as_request(kdc_active_realm, request, *princ,
                                    no_server, kdc_time, status, &e_data);
         if (code) {
             krb5_db_free_principal(kdc_context, princ);
             krb5_free_pa_data(kdc_context, e_data);
diff --git a/src/lib/krb5/krb/s4u_creds.c b/src/lib/krb5/krb/s4u_creds.c
index d2fdcb3f16a..614ed41908f 100644
--- a/src/lib/krb5/krb/s4u_creds.c
+++ b/src/lib/krb5/krb/s4u_creds.c
@@ -114,11 +114,11 @@ s4u_identify_user(krb5_context context,
     }
 
     code = k5_get_init_creds(context, &creds, &client, NULL, NULL, 0, NULL,
                              opts, krb5_get_as_key_noop, &userid, &use_master,
                              NULL);
-    if (code == 0 || code == KRB5_PREAUTH_FAILED) {
+    if (!code || code == KRB5_PREAUTH_FAILED || code == KRB5KDC_ERR_KEY_EXP) {
         *canon_user = userid.user;
         userid.user = NULL;
         code = 0;
     }
 
diff --git a/src/tests/gssapi/t_s4u.py b/src/tests/gssapi/t_s4u.py
index fd29e1a270a..84f3fbd752b 100755
--- a/src/tests/gssapi/t_s4u.py
+++ b/src/tests/gssapi/t_s4u.py
@@ -17,10 +17,18 @@ pservice1 = 'p:' + service1
 pservice2 = 'p:' + service2
 
 # Get forwardable creds for service1 in the default cache.
 realm.kinit(service1, None, ['-f', '-k'])
 
+# Try S4U2Self for user with a restricted password.
+realm.run([kadminl, 'modprinc', '+needchange', realm.user_princ])
+realm.run(['./t_s4u', 'e:user', '-'])
+realm.run([kadminl, 'modprinc', '-needchange',
+          '-pwexpire', '1/1/2000', realm.user_princ])
+realm.run(['./t_s4u', 'e:user', '-'])
+realm.run([kadminl, 'modprinc', '-pwexpire', 'never', realm.user_princ])
+
 # Try krb5 -> S4U2Proxy with forwardable user creds.  This should fail
 # at the S4U2Proxy step since the DB2 back end currently has no
 # support for allowing it.
 realm.kinit(realm.user_princ, password('user'), ['-f', '-c', usercache])
 output = realm.run(['./t_s4u2proxy_krb5', usercache, storagecache, '-',

Vulnerable code:     if (code == 0 || code == KRB5_PREAUTH_FAILED) {
Fixed code:         /* Ignore password expiration and needchange attributes (as Windows
         * does), since S4U2Self is not password authentication. */
        princ->pw_expiration = 0;
        clear(princ->attributes, KRB5_KDB_REQUIRES_PWCHANGE);

    if (!code || code == KRB5_PREAUTH_FAILED || code == KRB5KDC_ERR_KEY_EXP) {
# Try S4U2Self for user with a restricted password.
realm.run([kadminl, 'modprinc', '+needchange', realm.user_princ])
realm.run(['./t_s4u', 'e:user', '-'])
realm.run([kadminl, 'modprinc', '-needchange',
          '-pwexpire', '1/1/2000', realm.user_princ])
realm.run(['./t_s4u', 'e:user', '-'])
realm.run([kadminl, 'modprinc', '-pwexpire', 'never', realm.user_princ])