From: Joseph Sutton Date: Wed, 17 May 2023 03:47:18 +0000 (+1200) Subject: s4:kdc: Add NTSTATUS e-data to KDC reply X-Git-Tag: talloc-2.4.1~619 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=637fd961bd359c3ca30e21ebae731ead5cfbc673;p=thirdparty%2Fsamba.git s4:kdc: Add NTSTATUS e-data to KDC reply 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 Reviewed-by: Andrew Bartlett --- diff --git a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc index c39c0053b88..c00fc68ac12 100644 --- a/selftest/knownfail_heimdal_kdc +++ b/selftest/knownfail_heimdal_kdc @@ -57,13 +57,6 @@ # ^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 @@ -132,7 +125,6 @@ ^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 diff --git a/source4/kdc/hdb-samba4.c b/source4/kdc/hdb-samba4.c index 12dd746ad18..f16b9361db9 100644 --- a/source4/kdc/hdb-samba4.c +++ b/source4/kdc/hdb-samba4.c @@ -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 diff --git a/source4/kdc/wdc-samba4.c b/source4/kdc/wdc-samba4.c index d2eb49c7cb6..872f7e0dc9d 100644 --- a/source4/kdc/wdc-samba4.c +++ b/source4/kdc/wdc-samba4.c @@ -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 */ diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py index 525ea4a6ce4..93d161d89f1 100755 --- a/source4/selftest/tests.py +++ b/source4/selftest/tests.py @@ -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',