krb5_pac *pac,
krb5_cksumtype ctype)
{
+ struct samba_kdc_entry *client_skdc_entry = NULL;
struct samba_kdc_entry *server_skdc_entry =
- talloc_get_type_abort(server->context,
- struct samba_kdc_entry);
+ talloc_get_type_abort(server->context, struct samba_kdc_entry);
struct samba_kdc_entry *krbtgt_skdc_entry =
- talloc_get_type_abort(krbtgt->context,
- struct samba_kdc_entry);
- TALLOC_CTX *mem_ctx = talloc_named(server_skdc_entry,
- 0,
- "samba_kdc_reget_pac2 context");
+ talloc_get_type_abort(krbtgt->context, struct samba_kdc_entry);
+ TALLOC_CTX *mem_ctx = NULL;
krb5_pac new_pac = NULL;
- DATA_BLOB *pac_blob = NULL;
- DATA_BLOB *upn_blob = NULL;
- DATA_BLOB *requester_sid_blob = NULL;
- DATA_BLOB *deleg_blob = NULL;
krb5_error_code ret;
- NTSTATUS nt_status;
- bool is_in_db, is_untrusted;
- bool is_krbtgt;
- 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;
- ssize_t tkt_checksum_idx = -1;
- ssize_t attrs_info_idx = -1;
- ssize_t requester_sid_idx = -1;
+ bool is_in_db = false;
+ bool is_untrusted = false;
+ uint32_t flags = 0;
- if (!mem_ctx) {
+ mem_ctx = talloc_named(NULL, 0, "samba_kdc_reget_pac2 context");
+ if (mem_ctx == NULL) {
return ENOMEM;
}
if (client != NULL) {
- struct samba_kdc_entry *client_skdc_entry = NULL;
-
client_skdc_entry = talloc_get_type_abort(client->context,
struct samba_kdc_entry);
-
- /*
- * Check the objectSID of the client and pac data are the same.
- * Does a parse and SID check, but no crypto.
- */
- ret = samba_kdc_validate_pac_blob(context, client_skdc_entry, *pac);
- if (ret != 0) {
- talloc_free(mem_ctx);
- return ret;
- }
}
/*
*/
ret = samba_krbtgt_is_in_db(krbtgt_skdc_entry, &is_in_db, &is_untrusted);
if (ret != 0) {
- talloc_free(mem_ctx);
- return ret;
+ goto out;
}
if (delegated_proxy_principal != NULL) {
* server we got the ticket from. We must not proceed as
* otherwise the ticket signature is unchecked.
*/
- talloc_free(mem_ctx);
- return HDB_ERR_NOT_FOUND_HERE;
+ ret = HDB_ERR_NOT_FOUND_HERE;
+ goto out;
}
/* Fetch the correct key depending on the checksum type. */
ctype,
&etype);
if (ret != 0) {
- talloc_free(mem_ctx);
- return ret;
+ goto out;
}
}
ret = hdb_enctype2key(context, krbtgt, NULL, etype, &key);
if (ret != 0) {
- talloc_free(mem_ctx);
- return ret;
+ goto out;
}
/* Check the KDC and ticket signatures. */
&key->key);
if (ret != 0) {
DEBUG(1, ("PAC KDC signature failed to verify\n"));
- talloc_free(mem_ctx);
- return ret;
- }
-
- deleg_blob = talloc_zero(mem_ctx, DATA_BLOB);
- if (!deleg_blob) {
- talloc_free(mem_ctx);
- return ENOMEM;
+ goto out;
}
- nt_status = samba_kdc_update_delegation_info_blob(mem_ctx,
- context, *pac,
- server->principal,
- delegated_proxy_principal,
- deleg_blob);
- if (!NT_STATUS_IS_OK(nt_status)) {
- DBG_ERR("samba_kdc_update_delegation_info_blob() failed: %s\n",
- nt_errstr(nt_status));
- talloc_free(mem_ctx);
- return EINVAL;
- }
+ flags |= SAMBA_KDC_FLAG_CONSTRAINED_DELEGATION;
}
if (is_untrusted) {
- struct samba_kdc_entry *client_skdc_entry = NULL;
- struct auth_user_info_dc *user_info_dc = NULL;
- WERROR werr;
-
- if (client == NULL) {
- return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
- }
-
- client_skdc_entry = talloc_get_type_abort(client->context,
- struct samba_kdc_entry);
-
- nt_status = samba_kdc_get_pac_blobs(mem_ctx, client_skdc_entry,
- &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)) {
- talloc_free(mem_ctx);
- DBG_ERR("samba_kdc_get_pac_blobs() failed: %s\n",
- nt_errstr(nt_status));
- return KRB5KDC_ERR_TGT_REVOKED;
- }
-
- /*
- * Now 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_skdc_entry,
- client_skdc_entry);
- if (!W_ERROR_IS_OK(werr)) {
- talloc_free(mem_ctx);
- if (W_ERROR_EQUAL(werr, WERR_DOMAIN_CONTROLLER_NOT_FOUND)) {
- return KRB5KDC_ERR_POLICY;
- } else {
- return KRB5KDC_ERR_TGT_REVOKED;
- }
- }
+ flags |= SAMBA_KDC_FLAG_KRBTGT_IS_UNTRUSTED;
}
- if (!is_untrusted) {
- pac_blob = talloc_zero(mem_ctx, DATA_BLOB);
- if (!pac_blob) {
- talloc_free(mem_ctx);
- return ENOMEM;
- }
-
- nt_status = samba_kdc_update_pac_blob(mem_ctx, context,
- krbtgt_skdc_entry->kdc_db_ctx->samdb,
- *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));
- talloc_free(mem_ctx);
- return EINVAL;
- }
- }
-
- /* Check the types of the given PAC */
- ret = krb5_pac_get_types(context, *pac, &num_types, &types);
- if (ret != 0) {
- talloc_free(mem_ctx);
- return ret;
- }
-
- for (i = 0; i < num_types; i++) {
- switch (types[i]) {
- case PAC_TYPE_LOGON_INFO:
- if (logon_info_idx != -1) {
- DEBUG(1, ("logon info type[%"PRIu32"] twice [%zd] and [%zu]: \n",
- types[i],
- logon_info_idx,
- i));
- SAFE_FREE(types);
- talloc_free(mem_ctx);
- return EINVAL;
- }
- logon_info_idx = i;
- break;
- case PAC_TYPE_CONSTRAINED_DELEGATION:
- if (delegation_idx != -1) {
- DEBUG(1, ("constrained delegation type[%"PRIu32"] twice [%zd] and [%zu]: \n",
- types[i],
- delegation_idx,
- i));
- SAFE_FREE(types);
- talloc_free(mem_ctx);
- return EINVAL;
- }
- delegation_idx = i;
- break;
- case PAC_TYPE_LOGON_NAME:
- if (logon_name_idx != -1) {
- DEBUG(1, ("logon name type[%"PRIu32"] twice [%zd] and [%zu]: \n",
- types[i],
- logon_name_idx,
- i));
- SAFE_FREE(types);
- talloc_free(mem_ctx);
- return EINVAL;
- }
- logon_name_idx = i;
- break;
- case PAC_TYPE_UPN_DNS_INFO:
- if (upn_dns_info_idx != -1) {
- DEBUG(1, ("upn dns info type[%"PRIu32"] twice [%zd] and [%zu]: \n",
- types[i],
- upn_dns_info_idx,
- i));
- SAFE_FREE(types);
- talloc_free(mem_ctx);
- return EINVAL;
- }
- upn_dns_info_idx = i;
- break;
- case PAC_TYPE_SRV_CHECKSUM:
- if (srv_checksum_idx != -1) {
- DEBUG(1, ("server checksum type[%"PRIu32"] twice [%zd] and [%zu]: \n",
- types[i],
- srv_checksum_idx,
- i));
- SAFE_FREE(types);
- talloc_free(mem_ctx);
- return EINVAL;
- }
- srv_checksum_idx = i;
- break;
- case PAC_TYPE_KDC_CHECKSUM:
- if (kdc_checksum_idx != -1) {
- DEBUG(1, ("kdc checksum type[%"PRIu32"] twice [%zd] and [%zu]: \n",
- types[i],
- kdc_checksum_idx,
- i));
- SAFE_FREE(types);
- talloc_free(mem_ctx);
- return EINVAL;
- }
- kdc_checksum_idx = i;
- break;
- case PAC_TYPE_TICKET_CHECKSUM:
- if (tkt_checksum_idx != -1) {
- DEBUG(1, ("ticket checksum type[%"PRIu32"] twice [%zd] and [%zu]: \n",
- types[i],
- tkt_checksum_idx,
- i));
- SAFE_FREE(types);
- talloc_free(mem_ctx);
- return EINVAL;
- }
- tkt_checksum_idx = i;
- break;
- case PAC_TYPE_ATTRIBUTES_INFO:
- if (attrs_info_idx != -1) {
- DEBUG(1, ("attributes info type[%"PRIu32"] twice [%zd] and [%zu]: \n",
- types[i],
- attrs_info_idx,
- i));
- SAFE_FREE(types);
- talloc_free(mem_ctx);
- return EINVAL;
- }
- attrs_info_idx = i;
- break;
- case PAC_TYPE_REQUESTER_SID:
- if (requester_sid_idx != -1) {
- DEBUG(1, ("requester sid type[%"PRIu32"] twice [%zd] and [%zu]: \n",
- types[i],
- requester_sid_idx,
- i));
- SAFE_FREE(types);
- talloc_free(mem_ctx);
- return EINVAL;
- }
- requester_sid_idx = i;
- break;
- default:
- continue;
- }
+ if (is_in_db) {
+ flags |= SAMBA_KDC_FLAG_KRBTGT_IN_DB;
}
- if (logon_info_idx == -1) {
- DEBUG(1, ("PAC_TYPE_LOGON_INFO missing\n"));
- SAFE_FREE(types);
- talloc_free(mem_ctx);
- return EINVAL;
- }
- if (logon_name_idx == -1) {
- DEBUG(1, ("PAC_TYPE_LOGON_NAME missing\n"));
- SAFE_FREE(types);
- talloc_free(mem_ctx);
- return EINVAL;
- }
- if (srv_checksum_idx == -1) {
- DEBUG(1, ("PAC_TYPE_SRV_CHECKSUM missing\n"));
- SAFE_FREE(types);
- talloc_free(mem_ctx);
- return EINVAL;
- }
- if (kdc_checksum_idx == -1) {
- DEBUG(1, ("PAC_TYPE_KDC_CHECKSUM missing\n"));
- SAFE_FREE(types);
- talloc_free(mem_ctx);
- return EINVAL;
- }
- if (delegated_proxy_principal == NULL && requester_sid_idx == -1) {
- DEBUG(1, ("PAC_TYPE_REQUESTER_SID missing\n"));
- SAFE_FREE(types);
- talloc_free(mem_ctx);
- return KRB5KDC_ERR_TGT_REVOKED;
- }
-
- /*
- * 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_skdc_entry)) {
- ret = 0;
- new_pac = NULL;
- goto out;
- }
-
- is_krbtgt = krb5_principal_is_krbtgt(context, server->principal);
-
- if (!is_untrusted && !is_krbtgt) {
- /*
- * The client may have requested no PAC when obtaining the
- * TGT.
- */
- bool requested_pac;
- ret = samba_client_requested_pac(context, pac, mem_ctx,
- &requested_pac);
- if (ret != 0 || !requested_pac) {
- new_pac = NULL;
- goto out;
- }
- }
-
- /* Otherwise build an updated PAC */
ret = krb5_pac_init(context, &new_pac);
if (ret != 0) {
new_pac = NULL;
goto out;
}
- for (i = 0;;) {
- 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:
- 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
- * we just add a place holder here.
- */
- 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 are generated in the main KDC code
- * we just add a place holder here.
- */
- 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 are generated in the main KDC code
- * we just add a place holders here.
- */
- type_blob = data_blob_const(&zero_byte, 1);
- break;
- case PAC_TYPE_ATTRIBUTES_INFO:
- if (!is_untrusted && is_krbtgt) {
- /* just copy... */
- break;
- } else {
- continue;
- }
- case PAC_TYPE_REQUESTER_SID:
- if (is_krbtgt) {
- /*
- * 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;
- } else {
- continue;
- }
- default:
- /* just copy... */
- break;
- }
-
- if (type_blob.length != 0) {
- ret = smb_krb5_copy_data_contents(&type_data,
- type_blob.data,
- type_blob.length);
- if (ret != 0) {
- SAFE_FREE(types);
- krb5_pac_free(context, new_pac);
- talloc_free(mem_ctx);
- return ret;
- }
- } else {
- ret = krb5_pac_get_buffer(context, *pac,
- type, &type_data);
- if (ret != 0) {
- SAFE_FREE(types);
- krb5_pac_free(context, new_pac);
- talloc_free(mem_ctx);
- return ret;
- }
- }
-
- ret = krb5_pac_add_buffer(context, new_pac,
- type, &type_data);
- smb_krb5_free_data_contents(context, &type_data);
- if (ret != 0) {
- SAFE_FREE(types);
- krb5_pac_free(context, new_pac);
- talloc_free(mem_ctx);
- return ret;
+ ret = samba_kdc_update_pac(mem_ctx,
+ context,
+ krbtgt_skdc_entry->kdc_db_ctx->samdb,
+ flags,
+ client_skdc_entry,
+ server->principal,
+ server_skdc_entry,
+ krbtgt_skdc_entry,
+ delegated_proxy_principal,
+ *pac,
+ new_pac);
+ if (ret != 0) {
+ krb5_pac_free(context, new_pac);
+ if (ret == ENODATA) {
+ krb5_pac_free(context, *pac);
+ *pac = NULL;
+ ret = 0;
}
+ goto out;
}
-out:
-
- SAFE_FREE(types);
-
- /* We now replace the pac */
+ /* Replace the pac */
krb5_pac_free(context, *pac);
*pac = new_pac;
+out:
talloc_free(mem_ctx);
return ret;
}