From: Andreas Schneider Date: Mon, 7 Mar 2022 09:24:14 +0000 (+0100) Subject: s4:kdc: Implement common samba_kdc_update_pac() X-Git-Tag: tevent-0.12.0~429 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0828cbd4bfe0340dd05a8e47eca647d134863d2e;p=thirdparty%2Fsamba.git s4:kdc: Implement common samba_kdc_update_pac() Signed-off-by: Andreas Schneider Reviewed-by: Andrew Bartlett Reviewed-by: Joseph Sutton --- diff --git a/source4/kdc/pac-glue.c b/source4/kdc/pac-glue.c index 5198adc7dec..efeda9a998c 100644 --- a/source4/kdc/pac-glue.c +++ b/source4/kdc/pac-glue.c @@ -1351,3 +1351,537 @@ WERROR samba_rodc_confirm_user_is_allowed(uint32_t num_object_sids, TALLOC_FREE(frame); return werr; } + +/** + * @brief Update a PAC + * + * @param mem_ctx A talloc memory context + * + * @param context A krb5 context + * + * @param samdb An open samdb connection. + * + * @param flags Bitwise OR'ed flags + * + * @param client The client samba kdc entry. + + * @param server_principal The server principal + + * @param server The server samba kdc entry. + + * @param krbtgt The krbtgt samba kdc entry. + * + * @param delegated_proxy_principal The delegated proxy principal used for + * updating the constrained delegation PAC + * buffer. + + * @param old_pac The old PAC + + * @param new_pac The new already allocated PAC + + * @return A Kerberos error code. If no PAC should be returned, the code will be + * ENODATA! + */ +krb5_error_code samba_kdc_update_pac(TALLOC_CTX *mem_ctx, + krb5_context context, + struct ldb_context *samdb, + uint32_t flags, + struct samba_kdc_entry *client, + const krb5_principal server_principal, + struct samba_kdc_entry *server, + struct samba_kdc_entry *krbtgt, + const krb5_principal delegated_proxy_principal, + const krb5_pac old_pac, + krb5_pac new_pac) +{ + krb5_error_code code = EINVAL; + NTSTATUS nt_status; + DATA_BLOB *pac_blob = NULL; + DATA_BLOB *upn_blob = NULL; + DATA_BLOB *deleg_blob = NULL; + DATA_BLOB *requester_sid_blob = NULL; + bool is_untrusted = flags & SAMBA_KDC_FLAG_KRBTGT_IS_UNTRUSTED; + int is_tgs = false; + size_t num_types = 0; + uint32_t *types = NULL; + /* + * FIXME: Do we really still need forced_next_type? With MIT Kerberos + * the PAC buffers do not get ordered and it works just fine. We are + * not aware of any issues in this regard. This might be just ancient + * code. + */ + uint32_t forced_next_type = 0; + size_t i = 0; + ssize_t logon_info_idx = -1; + ssize_t delegation_idx = -1; + ssize_t logon_name_idx = -1; + ssize_t upn_dns_info_idx = -1; + ssize_t srv_checksum_idx = -1; + ssize_t kdc_checksum_idx = -1; + ssize_t tkt_checksum_idx = -1; + ssize_t attrs_info_idx = -1; + ssize_t requester_sid_idx = -1; + + if (client != NULL) { + /* + * Check the objectSID of the client and pac data are the same. + * Does a parse and SID check, but no crypto. + */ + code = samba_kdc_validate_pac_blob(context, + client, + old_pac); + if (code != 0) { + goto done; + } + } + + if (delegated_proxy_principal != NULL) { + deleg_blob = talloc_zero(mem_ctx, DATA_BLOB); + if (deleg_blob == NULL) { + code = ENOMEM; + goto done; + } + + nt_status = samba_kdc_update_delegation_info_blob( + mem_ctx, + context, + old_pac, + server_principal, + delegated_proxy_principal, + deleg_blob); + if (!NT_STATUS_IS_OK(nt_status)) { + DBG_ERR("update delegation info blob failed: %s\n", + nt_errstr(nt_status)); + code = EINVAL; + goto done; + } + } + + if (is_untrusted) { + struct auth_user_info_dc *user_info_dc = NULL; + WERROR werr; + + if (client == NULL) { + code = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; + goto done; + } + + nt_status = samba_kdc_get_pac_blobs(mem_ctx, + client, + &pac_blob, + NULL, + &upn_blob, + NULL, + PAC_ATTRIBUTE_FLAG_PAC_WAS_GIVEN_IMPLICITLY, + &requester_sid_blob, + &user_info_dc); + if (!NT_STATUS_IS_OK(nt_status)) { + DBG_ERR("samba_kdc_get_pac_blobs failed: %s\n", + nt_errstr(nt_status)); + code = KRB5KDC_ERR_TGT_REVOKED; + goto done; + } + + /* + * Check if the SID list in the user_info_dc intersects + * correctly with the RODC allow/deny lists. + */ + werr = samba_rodc_confirm_user_is_allowed(user_info_dc->num_sids, + user_info_dc->sids, + krbtgt, + client); + TALLOC_FREE(user_info_dc); + if (!W_ERROR_IS_OK(werr)) { + code = KRB5KDC_ERR_TGT_REVOKED; + if (W_ERROR_EQUAL(werr, + WERR_DOMAIN_CONTROLLER_NOT_FOUND)) { + code = KRB5KDC_ERR_POLICY; + } + goto done; + } + } else { + pac_blob = talloc_zero(mem_ctx, DATA_BLOB); + if (pac_blob == NULL) { + code = ENOMEM; + goto done; + } + + nt_status = samba_kdc_update_pac_blob(mem_ctx, + context, + samdb, + old_pac, + pac_blob, + NULL, + NULL); + if (!NT_STATUS_IS_OK(nt_status)) { + DBG_ERR("samba_kdc_update_pac_blob failed: %s\n", + nt_errstr(nt_status)); + code = EINVAL; + goto done; + } + } + + /* Check the types of the given PAC */ + code = krb5_pac_get_types(context, old_pac, &num_types, &types); + if (code != 0) { + DBG_ERR("krb5_pac_get_types failed\n"); + goto done; + } + + for (i = 0; i < num_types; i++) { + switch (types[i]) { + case PAC_TYPE_LOGON_INFO: + if (logon_info_idx != -1) { + DBG_WARNING("logon info type[%u] twice [%zd] " + "and [%zu]: \n", + types[i], + logon_info_idx, + i); + code = EINVAL; + goto done; + } + logon_info_idx = i; + break; + case PAC_TYPE_CONSTRAINED_DELEGATION: + if (delegation_idx != -1) { + DBG_WARNING("constrained delegation type[%u] " + "twice [%zd] and [%zu]: \n", + types[i], + delegation_idx, + i); + code = EINVAL; + goto done; + } + delegation_idx = i; + break; + case PAC_TYPE_LOGON_NAME: + if (logon_name_idx != -1) { + DBG_WARNING("logon name type[%u] twice [%zd] " + "and [%zu]: \n", + types[i], + logon_name_idx, + i); + code = EINVAL; + goto done; + } + logon_name_idx = i; + break; + case PAC_TYPE_UPN_DNS_INFO: + if (upn_dns_info_idx != -1) { + DBG_WARNING("upn dns info type[%u] twice [%zd] " + "and [%zu]: \n", + types[i], + upn_dns_info_idx, + i); + code = EINVAL; + goto done; + } + upn_dns_info_idx = i; + break; + case PAC_TYPE_SRV_CHECKSUM: + if (srv_checksum_idx != -1) { + DBG_WARNING("srv checksum type[%u] twice [%zd] " + "and [%zu]: \n", + types[i], + srv_checksum_idx, + i); + code = EINVAL; + goto done; + } + srv_checksum_idx = i; + break; + case PAC_TYPE_KDC_CHECKSUM: + if (kdc_checksum_idx != -1) { + DBG_WARNING("kdc checksum type[%u] twice [%zd] " + "and [%zu]: \n", + types[i], + kdc_checksum_idx, + i); + code = EINVAL; + goto done; + } + kdc_checksum_idx = i; + break; + case PAC_TYPE_TICKET_CHECKSUM: + if (tkt_checksum_idx != -1) { + DBG_WARNING("ticket checksum type[%u] twice " + "[%zd] and [%zu]: \n", + types[i], + tkt_checksum_idx, + i); + code = EINVAL; + goto done; + } + tkt_checksum_idx = i; + break; + case PAC_TYPE_ATTRIBUTES_INFO: + if (attrs_info_idx != -1) { + DBG_WARNING("attributes info type[%u] twice " + "[%zd] and [%zu]: \n", + types[i], + attrs_info_idx, + i); + code = EINVAL; + goto done; + } + attrs_info_idx = i; + break; + case PAC_TYPE_REQUESTER_SID: + if (requester_sid_idx != -1) { + DBG_WARNING("requester sid type[%u] twice" + "[%zd] and [%zu]: \n", + types[i], + requester_sid_idx, + i); + code = EINVAL; + goto done; + } + requester_sid_idx = i; + break; + default: + continue; + } + } + + if (logon_info_idx == -1) { + DBG_WARNING("PAC_TYPE_LOGON_INFO missing\n"); + code = EINVAL; + goto done; + } + if (logon_name_idx == -1) { + DBG_WARNING("PAC_TYPE_LOGON_NAME missing\n"); + code = EINVAL; + goto done; + } + if (srv_checksum_idx == -1) { + DBG_WARNING("PAC_TYPE_SRV_CHECKSUM missing\n"); + code = EINVAL; + goto done; + } + if (kdc_checksum_idx == -1) { + DBG_WARNING("PAC_TYPE_KDC_CHECKSUM missing\n"); + code = EINVAL; + goto done; + } + if (!(flags & SAMBA_KDC_FLAG_CONSTRAINED_DELEGATION) && + requester_sid_idx == -1) { + DBG_WARNING("PAC_TYPE_REQUESTER_SID missing\n"); + code = KRB5KDC_ERR_TGT_REVOKED; + goto done; + } + + /* + * The server account may be set not to want the PAC. + * + * While this is wasteful if the above cacluations were done + * and now thrown away, this is cleaner as we do any ticket + * signature checking etc always. + * + * UF_NO_AUTH_DATA_REQUIRED is the rare case and most of the + * time (eg not accepting a ticket from the RODC) we do not + * need to re-generate anything anyway. + */ + if (!samba_princ_needs_pac(server)) { + code = ENODATA; + goto done; + } + + is_tgs = smb_krb5_principal_is_tgs(context, server_principal); + if (is_tgs == -1) { + code = ENOMEM; + goto done; + } + + if (!is_untrusted && !is_tgs) { + /* + * The client may have requested no PAC when obtaining the + * TGT. + */ + bool requested_pac = false; + + code = samba_client_requested_pac(context, + &old_pac, + mem_ctx, + &requested_pac); + if (code != 0 || !requested_pac) { + if (!requested_pac) { + code = ENODATA; + } + goto done; + } + } + +#define MAX_PAC_BUFFERS 128 /* Avoid infinite loops */ + + for (i = 0; i < MAX_PAC_BUFFERS;) { + const uint8_t zero_byte = 0; + krb5_data type_data; + DATA_BLOB type_blob = data_blob_null; + uint32_t type; + + if (forced_next_type != 0) { + /* + * We need to inject possible missing types + */ + type = forced_next_type; + forced_next_type = 0; + } else if (i < num_types) { + type = types[i]; + i++; + } else { + break; + } + + switch (type) { + case PAC_TYPE_LOGON_INFO: + type_blob = *pac_blob; + + if (delegation_idx == -1 && deleg_blob != NULL) { + /* inject CONSTRAINED_DELEGATION behind */ + forced_next_type = + PAC_TYPE_CONSTRAINED_DELEGATION; + } + break; + case PAC_TYPE_CONSTRAINED_DELEGATION: + /* + * This is generated in the main KDC code + */ + if (flags & SAMBA_KDC_FLAG_SKIP_PAC_BUFFER) { + continue; + } + + if (deleg_blob != NULL) { + type_blob = *deleg_blob; + } + break; + case PAC_TYPE_CREDENTIAL_INFO: + /* + * Note that we copy the credential blob, + * as it's only usable with the PKINIT based + * AS-REP reply key, it's only available on the + * host which did the AS-REQ/AS-REP exchange. + * + * This matches Windows 2008R2... + */ + break; + case PAC_TYPE_LOGON_NAME: + /* + * This is generated in the main KDC code + */ + if (flags & SAMBA_KDC_FLAG_SKIP_PAC_BUFFER) { + continue; + } + + type_blob = data_blob_const(&zero_byte, 1); + + if (upn_dns_info_idx == -1 && upn_blob != NULL) { + /* inject UPN_DNS_INFO behind */ + forced_next_type = PAC_TYPE_UPN_DNS_INFO; + } + break; + case PAC_TYPE_UPN_DNS_INFO: + /* + * Replace in the RODC case, otherwise + * upn_blob is NULL and we just copy. + */ + if (upn_blob != NULL) { + type_blob = *upn_blob; + } + break; + case PAC_TYPE_SRV_CHECKSUM: + /* + * This is generated in the main KDC code + */ + if (flags & SAMBA_KDC_FLAG_SKIP_PAC_BUFFER) { + continue; + } + + type_blob = data_blob_const(&zero_byte, 1); + + if (requester_sid_idx == -1 && requester_sid_blob != NULL) { + /* inject REQUESTER_SID behind */ + forced_next_type = PAC_TYPE_REQUESTER_SID; + } + break; + case PAC_TYPE_KDC_CHECKSUM: + /* + * This is generated in the main KDC code + */ + if (flags & SAMBA_KDC_FLAG_SKIP_PAC_BUFFER) { + continue; + } + + type_blob = data_blob_const(&zero_byte, 1); + + break; + case PAC_TYPE_TICKET_CHECKSUM: + /* + * This is generated in the main KDC code + */ + if (flags & SAMBA_KDC_FLAG_SKIP_PAC_BUFFER) { + continue; + } + + type_blob = data_blob_const(&zero_byte, 1); + + break; + case PAC_TYPE_ATTRIBUTES_INFO: + if (!is_untrusted && is_tgs) { + /* just copy... */ + break; + } + + continue; + case PAC_TYPE_REQUESTER_SID: + if (!is_tgs) { + continue; + } + + /* + * Replace in the RODC case, otherwise + * requester_sid_blob is NULL and we just copy. + */ + if (requester_sid_blob != NULL) { + type_blob = *requester_sid_blob; + } + break; + default: + /* just copy... */ + break; + } + + if (type_blob.length != 0) { + code = smb_krb5_copy_data_contents(&type_data, + type_blob.data, + type_blob.length); + if (code != 0) { + goto done; + } + } else { + code = krb5_pac_get_buffer(context, + old_pac, + type, + &type_data); + if (code != 0) { + goto done; + } + } + + code = krb5_pac_add_buffer(context, + new_pac, + type, + &type_data); + smb_krb5_free_data_contents(context, &type_data); + if (code != 0) { + goto done; + } + } + + code = 0; +done: + TALLOC_FREE(pac_blob); + TALLOC_FREE(upn_blob); + TALLOC_FREE(deleg_blob); + SAFE_FREE(types); + return code; +} diff --git a/source4/kdc/pac-glue.h b/source4/kdc/pac-glue.h index 95911a793d7..8c68a0455fd 100644 --- a/source4/kdc/pac-glue.h +++ b/source4/kdc/pac-glue.h @@ -21,6 +21,14 @@ along with this program. If not, see . */ +enum { + SAMBA_KDC_FLAG_PROTOCOL_TRANSITION = 0x00000001, + SAMBA_KDC_FLAG_CONSTRAINED_DELEGATION = 0x00000002, + SAMBA_KDC_FLAG_KRBTGT_IN_DB = 0x00000004, + SAMBA_KDC_FLAG_KRBTGT_IS_UNTRUSTED = 0x00000008, + SAMBA_KDC_FLAG_SKIP_PAC_BUFFER = 0x00000010, +}; + krb5_error_code samba_kdc_encrypt_pac_credentials(krb5_context context, const krb5_keyblock *pkreplykey, const DATA_BLOB *cred_ndr_blob, @@ -90,3 +98,15 @@ WERROR samba_rodc_confirm_user_is_allowed(uint32_t num_sids, struct dom_sid *sids, struct samba_kdc_entry *rodc, struct samba_kdc_entry *object); + +krb5_error_code samba_kdc_update_pac(TALLOC_CTX *mem_ctx, + krb5_context context, + struct ldb_context *samdb, + uint32_t flags, + struct samba_kdc_entry *client, + const krb5_principal server_principal, + struct samba_kdc_entry *server, + struct samba_kdc_entry *krbtgt, + const krb5_principal delegated_proxy_principal, + const krb5_pac old_pac, + krb5_pac new_pac);