diff options
Diffstat (limited to 'source4/kdc')
-rw-r--r-- | source4/kdc/mit_samba.c | 383 | ||||
-rw-r--r-- | source4/kdc/mit_samba.h | 10 |
2 files changed, 393 insertions, 0 deletions
diff --git a/source4/kdc/mit_samba.c b/source4/kdc/mit_samba.c index 82a6dfddbe1..333714e0109 100644 --- a/source4/kdc/mit_samba.c +++ b/source4/kdc/mit_samba.c @@ -30,6 +30,7 @@ #include "kdc/sdb.h" #include "kdc/sdb_kdb.h" #include "auth/kerberos/kerberos.h" +#include "auth/kerberos/pac_utils.h" #include "kdc/samba_kdc.h" #include "kdc/pac-glue.h" #include "kdc/db-glue.h" @@ -468,6 +469,388 @@ done: return ret; } +krb5_error_code mit_samba_reget_pac(struct mit_samba_context *ctx, + krb5_context context, + int flags, + krb5_const_principal client_principal, + krb5_db_entry *client, + krb5_db_entry *server, + krb5_db_entry *krbtgt, + krb5_keyblock *krbtgt_keyblock, + krb5_pac *pac) +{ + TALLOC_CTX *tmp_ctx; + krb5_error_code code; + NTSTATUS nt_status; + DATA_BLOB *pac_blob = NULL; + DATA_BLOB *upn_blob = NULL; + DATA_BLOB *deleg_blob = NULL; + struct samba_kdc_entry *client_skdc_entry = NULL; + struct samba_kdc_entry *krbtgt_skdc_entry; + bool is_in_db = false; + bool is_untrusted = false; + size_t num_types = 0; + uint32_t *types = NULL; + 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; + krb5_pac new_pac = NULL; + bool ok; + + if (client != NULL) { + client_skdc_entry = + talloc_get_type_abort(client->e_data, + struct samba_kdc_entry); + + /* The user account may be set not to want the PAC */ + ok = samba_princ_needs_pac(client_skdc_entry); + if (!ok) { + return EINVAL; + } + } + + if (krbtgt == NULL) { + return EINVAL; + } + krbtgt_skdc_entry = + talloc_get_type_abort(krbtgt->e_data, + struct samba_kdc_entry); + + tmp_ctx = talloc_named(ctx, 0, "mit_samba_reget_pac context"); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + code = samba_krbtgt_is_in_db(krbtgt_skdc_entry, + &is_in_db, + &is_untrusted); + if (code != 0) { + goto done; + } + + if (is_untrusted) { + if (client == NULL) { + code = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; + goto done; + } + + nt_status = samba_kdc_get_pac_blobs(tmp_ctx, + client_skdc_entry, + &pac_blob, + NULL, + &upn_blob); + if (!NT_STATUS_IS_OK(nt_status)) { + code = EINVAL; + goto done; + } + } else { + struct PAC_SIGNATURE_DATA *pac_srv_sig; + struct PAC_SIGNATURE_DATA *pac_kdc_sig; + + pac_blob = talloc_zero(tmp_ctx, DATA_BLOB); + if (pac_blob == NULL) { + code = ENOMEM; + goto done; + } + + pac_srv_sig = talloc_zero(tmp_ctx, struct PAC_SIGNATURE_DATA); + if (pac_srv_sig == NULL) { + code = ENOMEM; + goto done; + } + + pac_kdc_sig = talloc_zero(tmp_ctx, struct PAC_SIGNATURE_DATA); + if (pac_kdc_sig == NULL) { + code = ENOMEM; + goto done; + } + + nt_status = samba_kdc_update_pac_blob(tmp_ctx, + context, + *pac, + pac_blob, + pac_srv_sig, + pac_kdc_sig); + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(0, ("Update PAC blob failed: %s\n", + nt_errstr(nt_status))); + code = EINVAL; + goto done; + } + + if (is_in_db) { + /* + * Now check the KDC signature, fetching the correct + * key based on the enc type. + */ + code = check_pac_checksum(pac_srv_sig->signature, + pac_kdc_sig, + context, + krbtgt_keyblock); + if (code != 0) { + DBG_INFO("PAC KDC signature failed to verify\n"); + goto done; + } + } + } + + if (flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION) { + deleg_blob = talloc_zero(tmp_ctx, DATA_BLOB); + if (deleg_blob == NULL) { + code = ENOMEM; + goto done; + } + + nt_status = samba_kdc_update_delegation_info_blob(tmp_ctx, + context, + *pac, + server->princ, + discard_const(client_principal), + deleg_blob); + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(0, ("Update delegation info failed: %s\n", + nt_errstr(nt_status))); + code = EINVAL; + goto done; + } + } + + /* Check the types of the given PAC */ + code = krb5_pac_get_types(context, *pac, &num_types, &types); + if (code != 0) { + 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 type[%u] twice [%zd] and [%zu]: \n", + types[i], + logon_info_idx, + i); + SAFE_FREE(types); + code = EINVAL; + goto done; + } + logon_info_idx = i; + break; + case PAC_TYPE_CONSTRAINED_DELEGATION: + if (delegation_idx != -1) { + DBG_WARNING("logon type[%u] twice [%zd] and [%zu]: \n", + types[i], + delegation_idx, + i); + SAFE_FREE(types); + code = EINVAL; + goto done; + } + delegation_idx = i; + break; + case PAC_TYPE_LOGON_NAME: + if (logon_name_idx != -1) { + DBG_WARNING("logon type[%u] twice [%zd] and [%zu]: \n", + types[i], + logon_name_idx, + i); + SAFE_FREE(types); + code = EINVAL; + goto done; + } + logon_name_idx = i; + break; + case PAC_TYPE_UPN_DNS_INFO: + if (upn_dns_info_idx != -1) { + DBG_WARNING("logon type[%u] twice [%zd] and [%zu]: \n", + types[i], + upn_dns_info_idx, + i); + SAFE_FREE(types); + code = EINVAL; + goto done; + } + upn_dns_info_idx = i; + break; + case PAC_TYPE_SRV_CHECKSUM: + if (srv_checksum_idx != -1) { + DBG_WARNING("logon type[%u] twice [%zd] and [%zu]: \n", + types[i], + srv_checksum_idx, + i); + SAFE_FREE(types); + code = EINVAL; + goto done; + } + srv_checksum_idx = i; + break; + case PAC_TYPE_KDC_CHECKSUM: + if (kdc_checksum_idx != -1) { + DBG_WARNING("logon type[%u] twice [%zd] and [%zu]: \n", + types[i], + kdc_checksum_idx, + i); + SAFE_FREE(types); + code = EINVAL; + goto done; + } + kdc_checksum_idx = i; + break; + default: + continue; + } + } + + if (logon_info_idx == -1) { + DEBUG(1, ("PAC_TYPE_LOGON_INFO missing\n")); + SAFE_FREE(types); + code = EINVAL; + goto done; + } + if (logon_name_idx == -1) { + DEBUG(1, ("PAC_TYPE_LOGON_NAME missing\n")); + SAFE_FREE(types); + code = EINVAL; + goto done; + } + if (srv_checksum_idx == -1) { + DEBUG(1, ("PAC_TYPE_SRV_CHECKSUM missing\n")); + SAFE_FREE(types); + code = EINVAL; + goto done; + } + if (kdc_checksum_idx == -1) { + DEBUG(1, ("PAC_TYPE_KDC_CHECKSUM missing\n")); + SAFE_FREE(types); + code = EINVAL; + goto done; + } + + /* Build an updated PAC */ + code = krb5_pac_init(context, &new_pac); + if (code != 0) { + SAFE_FREE(types); + goto done; + } + + for (i = 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: + 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 + */ + continue; + 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 + */ + continue; + case PAC_TYPE_KDC_CHECKSUM: + /* + * This is generated in the main KDC code + */ + continue; + 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) { + SAFE_FREE(types); + krb5_pac_free(context, new_pac); + goto done; + } + } else { + code = krb5_pac_get_buffer(context, + *pac, + type, + &type_data); + if (code != 0) { + SAFE_FREE(types); + krb5_pac_free(context, new_pac); + goto done; + } + } + + code = krb5_pac_add_buffer(context, + new_pac, + type, + &type_data); + smb_krb5_free_data_contents(context, &type_data); + if (code != 0) { + SAFE_FREE(types); + krb5_pac_free(context, new_pac); + goto done; + } + } + + SAFE_FREE(types); + + /* We now replace the pac */ + krb5_pac_free(context, *pac); + *pac = new_pac; +done: + talloc_free(tmp_ctx); + return code; +} + /* provide header, function is exported but there are no public headers */ krb5_error_code encode_krb5_padata_sequence(krb5_pa_data *const *rep, krb5_data **code); diff --git a/source4/kdc/mit_samba.h b/source4/kdc/mit_samba.h index 37e7d8d5126..036e77a5299 100644 --- a/source4/kdc/mit_samba.h +++ b/source4/kdc/mit_samba.h @@ -53,6 +53,16 @@ int mit_samba_get_pac(struct mit_samba_context *smb_ctx, krb5_keyblock *client_key, krb5_pac *pac); +krb5_error_code mit_samba_reget_pac(struct mit_samba_context *ctx, + krb5_context context, + int flags, + krb5_const_principal client_principal, + krb5_db_entry *client, + krb5_db_entry *server, + krb5_db_entry *krbtgt, + krb5_keyblock *krbtgt_keyblock, + krb5_pac *pac); + int mit_samba_update_pac_data(struct mit_samba_context *ctx, krb5_db_entry *client, DATA_BLOB *pac_data, |