CVE-2022-2031 s4:kpasswd: Do not accept TGTs as kpasswd tickets
authorJoseph Sutton <josephsutton@catalyst.net.nz>
Fri, 10 Jun 2022 07:18:53 +0000 (19:18 +1200)
committerJule Anger <janger@samba.org>
Sun, 24 Jul 2022 09:42:02 +0000 (11:42 +0200)
If TGTs can be used as kpasswd tickets, the two-minute lifetime of a
authentic kpasswd ticket may be bypassed. Furthermore, kpasswd tickets
are not supposed to be cached, but using this flaw, a stolen credentials
cache containing a TGT may be used to change that account's password,
and thus is made more valuable to an attacker.

Since all TGTs should be issued with a REQUESTER_SID PAC buffer, and
service tickets without it, we assert the absence of this buffer to
ensure we're not accepting a TGT.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=15047
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15049

Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
Reviewed-by: Andreas Schneider <asn@samba.org>
[jsutton@samba.org Fixed knownfail conflicts]

[jsutton@samba.org Fixed knownfail conflicts]

selftest/knownfail_heimdal_kdc
selftest/knownfail_mit_kdc
source4/kdc/kpasswd-helper.c
source4/kdc/kpasswd-helper.h
source4/kdc/kpasswd-service-heimdal.c
source4/kdc/kpasswd-service-mit.c

index 42beccaed5830056b962be3a33949c86815ec27a..424a8b81c38b00f11668a431624ce785328c8980 100644 (file)
 ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_service_ticket
 ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_sid_mismatch_existing
 ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_sid_mismatch_nonexisting
-#
-# Kpasswd tests
-#
-^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_tgt.ad_dc
index 9fc34e5d8db89f79b558cc468f28c2a8ae3240e4..0d2f5bab6d2e531f65207e505c5a0e9186ae75b8 100644 (file)
@@ -581,7 +581,3 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_
 ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_canonicalize_realm_case.ad_dc
 ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_no_canonicalize_realm_case.ad_dc
 ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_ticket_requester_sid_tgs.ad_dc
-#
-# Kpasswd tests
-#
-samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_tgt.ad_dc
index 55a2f5b3bf69245c5010b938fdd1b8c7c39a4b3d..2ffdb79aea5cbd571e38a5c48e965fd04bc9f5f4 100644 (file)
@@ -241,3 +241,23 @@ NTSTATUS kpasswd_samdb_set_password(TALLOC_CTX *mem_ctx,
 
        return status;
 }
+
+krb5_error_code kpasswd_check_non_tgt(struct auth_session_info *session_info,
+                                     const char **error_string)
+{
+       switch(session_info->ticket_type) {
+       case TICKET_TYPE_TGT:
+               /* TGTs are disallowed here. */
+               *error_string = "A TGT may not be used as a ticket to kpasswd";
+               return KRB5_KPASSWD_AUTHERROR;
+       case TICKET_TYPE_NON_TGT:
+               /* Non-TGTs are permitted, and expected. */
+               break;
+       default:
+               /* In case we forgot to set the type. */
+               *error_string = "Failed to ascertain that ticket to kpasswd is not a TGT";
+               return KRB5_KPASSWD_HARDERROR;
+       }
+
+       return 0;
+}
index 8fad81e0a5d499d59949873f2243bb8b1d8e4397..94a6e2acfddd85e7f9f74a62cceaadc7bf59427f 100644 (file)
@@ -43,4 +43,6 @@ NTSTATUS kpasswd_samdb_set_password(TALLOC_CTX *mem_ctx,
                                    enum samPwdChangeReason *reject_reason,
                                    struct samr_DomInfo1 **dominfo);
 
+krb5_error_code kpasswd_check_non_tgt(struct auth_session_info *session_info,
+                                     const char **error_string);
 #endif /* _KPASSWD_HELPER_H */
index a0352d1ad35fb22966e6503fbf1e8d044b4c2bf7..4d009b9eb24a023ba904405e8b9795427fabf205 100644 (file)
@@ -253,6 +253,7 @@ krb5_error_code kpasswd_handle_request(struct kdc_server *kdc,
 {
        struct auth_session_info *session_info;
        NTSTATUS status;
+       krb5_error_code code;
 
        status = gensec_session_info(gensec_security,
                                     mem_ctx,
@@ -264,6 +265,18 @@ krb5_error_code kpasswd_handle_request(struct kdc_server *kdc,
                return KRB5_KPASSWD_HARDERROR;
        }
 
+       /*
+        * Since the kpasswd service shares its keys with the krbtgt, we might
+        * have received a TGT rather than a kpasswd ticket. We need to check
+        * the ticket type to ensure that TGTs cannot be misused in this manner.
+        */
+       code = kpasswd_check_non_tgt(session_info,
+                                    error_string);
+       if (code != 0) {
+               DBG_WARNING("%s\n", *error_string);
+               return code;
+       }
+
        switch(verno) {
        case KRB5_KPASSWD_VERS_CHANGEPW: {
                DATA_BLOB password = data_blob_null;
index de4c6f3f622af3b523f5fef60f8a52b443a10b1a..6b051567b6e1f50a924804973cd2ffd4c796f145 100644 (file)
@@ -332,6 +332,7 @@ krb5_error_code kpasswd_handle_request(struct kdc_server *kdc,
 {
        struct auth_session_info *session_info;
        NTSTATUS status;
+       krb5_error_code code;
 
        status = gensec_session_info(gensec_security,
                                     mem_ctx,
@@ -344,6 +345,18 @@ krb5_error_code kpasswd_handle_request(struct kdc_server *kdc,
                return KRB5_KPASSWD_HARDERROR;
        }
 
+       /*
+        * Since the kpasswd service shares its keys with the krbtgt, we might
+        * have received a TGT rather than a kpasswd ticket. We need to check
+        * the ticket type to ensure that TGTs cannot be misused in this manner.
+        */
+       code = kpasswd_check_non_tgt(session_info,
+                                    error_string);
+       if (code != 0) {
+               DBG_WARNING("%s\n", *error_string);
+               return code;
+       }
+
        switch(verno) {
        case 1: {
                DATA_BLOB password;