s4:kdc: Add NTSTATUS e-data to KDC reply
authorJoseph Sutton <josephsutton@catalyst.net.nz>
Wed, 17 May 2023 03:47:18 +0000 (15:47 +1200)
committerAndrew Bartlett <abartlet@samba.org>
Thu, 18 May 2023 04:53:30 +0000 (04:53 +0000)
If an NTSTATUS code has been set in the KDC request structure, encode it
as KERB-ERROR-DATA and add it to the KDC reply.

hdb_samba4_set_ntstatus() adds the NTSTATUS code to the request
structure.

hdb_samba4_get_ntstatus() gets that status code back from the request
structure.

hdb_samba4_set_edata_from_ntstatus() encodes the status code and adds it
to the reply.

Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
selftest/knownfail_heimdal_kdc
source4/kdc/hdb-samba4.c
source4/kdc/wdc-samba4.c
source4/selftest/tests.py

index c39c0053b88ff0baa3d145a53071c70867e14e60..c00fc68ac12873672eb091977999441295fd8fd0 100644 (file)
 #
 ^samba.tests.krb5.protected_users_tests.samba.tests.krb5.protected_users_tests.ProtectedUsersTests.test_samr_change_password_protected.ad_dc
 #
-# Lockout tests
-#
-^samba.tests.krb5.lockout_tests.samba.tests.krb5.lockout_tests.LockoutTests.test_lockout_race_kdc_ntstatus.ad_dc:local
-^samba.tests.krb5.lockout_tests.samba.tests.krb5.lockout_tests.LockoutTests.test_lockout_transaction_bad_pwd_kdc_ntstatus.ad_dc:local
-^samba.tests.krb5.lockout_tests.samba.tests.krb5.lockout_tests.LockoutTests.test_lockout_transaction_kdc_ntstatus.ad_dc:local
-^samba.tests.krb5.lockout_tests.samba.tests.krb5.lockout_tests.LockoutTests.test_lockout_transaction_rename_kdc_ntstatus.ad_dc:local
-#
 # Group tests
 #
 ^samba.tests.krb5.group_tests.samba.tests.krb5.group_tests.GroupTests.test_group_nested_group_addition_compression_tgs_req_to_service.ad_dc
 ^samba.tests.krb5.authn_policy_tests.samba.tests.krb5.authn_policy_tests.AuthnPolicyTests.test_authn_policy_samr_pwd_change_deny_service_allowed_from.ad_dc
 ^samba.tests.krb5.authn_policy_tests.samba.tests.krb5.authn_policy_tests.AuthnPolicyTests.test_authn_policy_samr_pwd_change_deny_service_not_allowed_from.ad_dc
 ^samba.tests.krb5.authn_policy_tests.samba.tests.krb5.authn_policy_tests.AuthnPolicyTests.test_authn_policy_simple_bind_deny_user.ad_dc
-^samba.tests.krb5.authn_policy_tests.samba.tests.krb5.authn_policy_tests.AuthnPolicyTests.test_authn_policy_tgt_lifetime_min.ad_dc
 ^samba.tests.krb5.authn_policy_tests.samba.tests.krb5.authn_policy_tests.AuthnPolicyTests.test_samlogon_allowed_to_computer_allow_asserted_identity.ad_dc
 ^samba.tests.krb5.authn_policy_tests.samba.tests.krb5.authn_policy_tests.AuthnPolicyTests.test_samlogon_allowed_to_computer_allow_claims_valid.ad_dc
 ^samba.tests.krb5.authn_policy_tests.samba.tests.krb5.authn_policy_tests.AuthnPolicyTests.test_samlogon_allowed_to_computer_allow_compounded_auth.ad_dc
index 12dd746ad1841bf0af924150e53fef29811b5193..f16b9361db90fe5202bd3619849b4b19992ffc31 100644 (file)
@@ -570,6 +570,96 @@ krb5_error_code hdb_samba4_set_ntstatus(astgs_request_t r,
        return 0;
 }
 
+static krb5_error_code hdb_samba4_make_nt_status_edata(const NTSTATUS status,
+                                                      const uint32_t flags,
+                                                      krb5_data *edata_out)
+{
+    const uint32_t status_code = NT_STATUS_V(status);
+    const uint32_t zero = 0;
+    KERB_ERROR_DATA error_data;
+    krb5_data e_data;
+
+    krb5_error_code ret;
+    size_t size;
+
+    /* The raw KERB-ERR-TYPE-EXTENDED structure. */
+    uint8_t data[12];
+
+    PUSH_LE_U32(data, 0, status_code);
+    PUSH_LE_U32(data, 4, zero);
+    PUSH_LE_U32(data, 8, flags);
+
+    e_data = (krb5_data) {
+           .data = &data,
+           .length = sizeof(data),
+    };
+
+    error_data = (KERB_ERROR_DATA) {
+           .data_type = kERB_ERR_TYPE_EXTENDED,
+           .data_value = &e_data,
+    };
+
+    ASN1_MALLOC_ENCODE(KERB_ERROR_DATA,
+                      edata_out->data, edata_out->length,
+                      &error_data,
+                      &size, ret);
+    if (ret) {
+           return ret;
+    }
+    if (size != edata_out->length) {
+           /* Internal ASN.1 encoder error */
+           krb5_data_free(edata_out);
+           return KRB5KRB_ERR_GENERIC;
+    }
+
+    return 0;
+}
+
+static krb5_error_code hdb_samba4_set_edata_from_ntstatus(hdb_request_t r, const NTSTATUS status)
+{
+       const KDC_REQ *req = kdc_request_get_req((astgs_request_t)r);
+       krb5_error_code ret = 0;
+       krb5_data e_data;
+       uint32_t flags = 1;
+
+       if (req->msg_type == krb_tgs_req) {
+               /* This flag is used to indicate a TGS-REQ. */
+               flags |= 2;
+       }
+
+       ret = hdb_samba4_make_nt_status_edata(status, flags, &e_data);
+       if (ret) {
+               return ret;
+       }
+
+       ret = kdc_set_e_data((astgs_request_t)r, e_data);
+       if (ret) {
+               krb5_data_free(&e_data);
+       }
+
+       return ret;
+}
+
+static NTSTATUS hdb_samba4_get_ntstatus(hdb_request_t r)
+{
+       struct hdb_ntstatus_obj *status_obj = NULL;
+
+       status_obj = heim_audit_getkv((heim_svc_req_desc)r, SAMBA_HDB_NT_STATUS);
+       if (status_obj == NULL) {
+               return NT_STATUS_OK;
+       }
+
+       if (r->error_code != status_obj->current_error) {
+               /*
+                * The error code has changed from what we expect. Consider the
+                * NTSTATUS to be invalidated.
+                */
+               return NT_STATUS_OK;
+       }
+
+       return status_obj->status;
+}
+
 static krb5_error_code hdb_samba4_audit(krb5_context context,
                                        HDB *db,
                                        hdb_entry *entry,
@@ -589,6 +679,9 @@ static krb5_error_code hdb_samba4_audit(krb5_context context,
        struct auth_usersupplied_info ui;
        size_t sa_socklen = 0;
        krb5_error_code final_ret = 0;
+       NTSTATUS edata_status;
+
+       edata_status = hdb_samba4_get_ntstatus(r);
 
        hdb_auth_status_obj = heim_audit_getkv((heim_svc_req_desc)r, KDC_REQUEST_KV_AUTH_EVENT);
        if (hdb_auth_status_obj == NULL) {
@@ -705,6 +798,8 @@ static krb5_error_code hdb_samba4_audit(krb5_context context,
                        status = authsam_logon_success_accounting(kdc_db_ctx->samdb, p->msg,
                                                                  domain_dn, true, frame, &send_to_sam);
                        if (NT_STATUS_EQUAL(status, NT_STATUS_ACCOUNT_LOCKED_OUT)) {
+                               edata_status = status;
+
                                final_ret = KRB5KDC_ERR_CLIENT_REVOKED;
                                r->error_code = final_ret;
                                rwdc_fallback = kdc_db_ctx->rodc;
@@ -712,8 +807,14 @@ static krb5_error_code hdb_samba4_audit(krb5_context context,
                                final_ret = KRB5KRB_ERR_GENERIC;
                                r->error_code = final_ret;
                                rwdc_fallback = kdc_db_ctx->rodc;
-                       } else if (kdc_db_ctx->rodc && send_to_sam != NULL) {
-                               reset_bad_password_netlogon(frame, kdc_db_ctx, send_to_sam);
+                       } else {
+                               if (r->error_code == KRB5KDC_ERR_NEVER_VALID) {
+                                       edata_status = NT_STATUS_TIME_DIFFERENCE_AT_DC;
+                               }
+
+                               if (kdc_db_ctx->rodc && send_to_sam != NULL) {
+                                       reset_bad_password_netlogon(frame, kdc_db_ctx, send_to_sam);
+                               }
                        }
 
                        /* This is the final sucess */
@@ -758,6 +859,8 @@ static krb5_error_code hdb_samba4_audit(krb5_context context,
                } else if (hdb_auth_status == KDC_AUTH_EVENT_WRONG_LONG_TERM_KEY) {
                        status = authsam_update_bad_pwd_count(kdc_db_ctx->samdb, p->msg, domain_dn);
                        if (NT_STATUS_EQUAL(status, NT_STATUS_ACCOUNT_LOCKED_OUT)) {
+                               edata_status = status;
+
                                final_ret = KRB5KDC_ERR_CLIENT_REVOKED;
                                r->error_code = final_ret;
                        } else {
@@ -765,7 +868,7 @@ static krb5_error_code hdb_samba4_audit(krb5_context context,
                        }
                        rwdc_fallback = kdc_db_ctx->rodc;
                } else if (hdb_auth_status == KDC_AUTH_EVENT_CLIENT_LOCKED_OUT) {
-                       status = NT_STATUS_ACCOUNT_LOCKED_OUT;
+                       edata_status = status = NT_STATUS_ACCOUNT_LOCKED_OUT;
                        rwdc_fallback = kdc_db_ctx->rodc;
                } else if (hdb_auth_status == KDC_AUTH_EVENT_CLIENT_NAME_UNAUTHORIZED) {
                        if (pa_type != NULL && strncmp(pa_type, "PK-INIT", strlen("PK-INIT")) == 0) {
@@ -789,6 +892,15 @@ static krb5_error_code hdb_samba4_audit(krb5_context context,
                        r->error_code = final_ret;
                }
 
+               if (!NT_STATUS_IS_OK(edata_status)) {
+                       krb5_error_code code;
+
+                       code = hdb_samba4_set_edata_from_ntstatus(r, edata_status);
+                       if (code) {
+                               r->error_code = final_ret = code;
+                       }
+               }
+
                if (rwdc_fallback) {
                        /*
                         * Forward the request to an RWDC in order
index d2eb49c7cb67365924ad621ebedcdc7b442b33a9..872f7e0dc9dc810800363d8a9c1e13455189e018 100644 (file)
@@ -685,28 +685,43 @@ static krb5_error_code samba_wdc_check_client_access(void *priv,
                                                  password_change);
 
        if (!NT_STATUS_IS_OK(nt_status)) {
+               krb5_error_code ret;
+               krb5_error_code ret2;
+
                if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_MEMORY)) {
                        return ENOMEM;
                }
 
+               ret = samba_kdc_map_policy_err(nt_status);
+
+               /*
+                * Add the NTSTATUS to the request so we can return it in the
+                * ‘e-data’ field later.
+                */
+               ret2 = hdb_samba4_set_ntstatus(r, nt_status, ret);
+               if (ret2) {
+                       ret = ret2;
+               }
+
                if (kdc_request_get_rep(r)->padata) {
-                       int ret;
                        krb5_data kd;
 
                        samba_kdc_build_edata_reply(nt_status, &kd);
-                       ret = krb5_padata_add(kdc_request_get_context((kdc_request_t)r), kdc_request_get_rep(r)->padata,
-                                             KRB5_PADATA_PW_SALT,
-                                             kd.data, kd.length);
-                       if (ret != 0) {
+                       ret2 = krb5_padata_add(kdc_request_get_context((kdc_request_t)r), kdc_request_get_rep(r)->padata,
+                                              KRB5_PADATA_PW_SALT,
+                                              kd.data, kd.length);
+                       if (ret2) {
                                /*
                                 * So we do not leak the allocated
                                 * memory on kd in the error case
                                 */
                                krb5_data_free(&kd);
+
+                               ret = ret2;
                        }
                }
 
-               return samba_kdc_map_policy_err(nt_status);
+               return ret;
        }
 
        /* Now do the standard Heimdal check */
index 525ea4a6ce46e6ce3da16e819e1906b789b0be97..93d161d89f197d7a7f37bfdd55a033afb3edd888 100755 (executable)
@@ -1205,7 +1205,7 @@ expect_pac = int('SAMBA4_USES_HEIMDAL' in config_hash)
 extra_pac_buffers = int('SAMBA4_USES_HEIMDAL' in config_hash)
 check_cname = int('SAMBA4_USES_HEIMDAL' in config_hash)
 check_padata = int('SAMBA4_USES_HEIMDAL' in config_hash)
-expect_nt_status = 0
+expect_nt_status = int('SAMBA4_USES_HEIMDAL' in config_hash)
 krb5_environ = {
     'SERVICE_USERNAME': '$SERVER',
     'ADMIN_USERNAME': '$DC_USERNAME',