Describe implication of upstream ICU-22610
[samba.git] / source4 / kdc / pac-glue.c
blobca07fd377444fdbf7ae66ea43da1a124dedc7ed0
1 /*
2 Unix SMB/CIFS implementation.
4 PAC Glue between Samba and the KDC
6 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2009
7 Copyright (C) Simo Sorce <idra@samba.org> 2010
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "lib/replace/replace.h"
25 #include "lib/replace/system/kerberos.h"
26 #include "lib/replace/system/filesys.h"
27 #include "lib/util/debug.h"
28 #include "lib/util/samba_util.h"
29 #include "lib/util/talloc_stack.h"
31 #include "auth/auth_sam_reply.h"
32 #include "auth/kerberos/kerberos.h"
33 #include "auth/kerberos/pac_utils.h"
34 #include "auth/authn_policy.h"
35 #include "libcli/security/security.h"
36 #include "libds/common/flags.h"
37 #include "librpc/gen_ndr/ndr_krb5pac.h"
38 #include "param/param.h"
39 #include "source4/auth/auth.h"
40 #include "source4/dsdb/common/util.h"
41 #include "source4/dsdb/samdb/samdb.h"
42 #include "source4/kdc/authn_policy_util.h"
43 #include "source4/kdc/samba_kdc.h"
44 #include "source4/kdc/pac-glue.h"
45 #include "source4/kdc/ad_claims.h"
46 #include "source4/kdc/pac-blobs.h"
48 #include <ldb.h>
50 #undef DBGC_CLASS
51 #define DBGC_CLASS DBGC_KERBEROS
53 static
54 NTSTATUS samba_get_logon_info_pac_blob(TALLOC_CTX *mem_ctx,
55 const struct auth_user_info_dc *info,
56 const struct PAC_DOMAIN_GROUP_MEMBERSHIP *override_resource_groups,
57 const enum auth_group_inclusion group_inclusion,
58 DATA_BLOB *pac_data)
60 TALLOC_CTX *tmp_ctx = NULL;
61 struct netr_SamInfo3 *info3 = NULL;
62 struct PAC_DOMAIN_GROUP_MEMBERSHIP *_resource_groups = NULL;
63 struct PAC_DOMAIN_GROUP_MEMBERSHIP **resource_groups = NULL;
64 union PAC_INFO pac_info = {};
65 enum ndr_err_code ndr_err;
66 NTSTATUS nt_status = NT_STATUS_OK;
68 *pac_data = data_blob_null;
70 tmp_ctx = talloc_new(mem_ctx);
71 if (tmp_ctx == NULL) {
72 return NT_STATUS_NO_MEMORY;
75 if (override_resource_groups == NULL) {
76 resource_groups = &_resource_groups;
77 } else if (group_inclusion != AUTH_EXCLUDE_RESOURCE_GROUPS) {
79 * It doesn't make sense to override resource groups if we claim
80 * to want resource groups from user_info_dc.
82 DBG_ERR("supplied resource groups with invalid group inclusion parameter: %u\n",
83 group_inclusion);
84 nt_status = NT_STATUS_INVALID_PARAMETER;
85 goto out;
88 nt_status = auth_convert_user_info_dc_saminfo3(tmp_ctx, info,
89 group_inclusion,
90 &info3,
91 resource_groups);
92 if (!NT_STATUS_IS_OK(nt_status)) {
93 DBG_WARNING("Getting Samba info failed: %s\n",
94 nt_errstr(nt_status));
95 goto out;
98 pac_info.logon_info.info = talloc_zero(tmp_ctx, struct PAC_LOGON_INFO);
99 if (!pac_info.logon_info.info) {
100 nt_status = NT_STATUS_NO_MEMORY;
101 goto out;
104 pac_info.logon_info.info->info3 = *info3;
105 if (_resource_groups != NULL) {
106 pac_info.logon_info.info->resource_groups = *_resource_groups;
109 if (override_resource_groups != NULL) {
110 pac_info.logon_info.info->resource_groups = *override_resource_groups;
113 if (group_inclusion != AUTH_EXCLUDE_RESOURCE_GROUPS) {
115 * Set the resource groups flag based on whether any groups are
116 * present. Otherwise, the flag is propagated from the
117 * originating PAC.
119 if (pac_info.logon_info.info->resource_groups.groups.count > 0) {
120 pac_info.logon_info.info->info3.base.user_flags |= NETLOGON_RESOURCE_GROUPS;
121 } else {
122 pac_info.logon_info.info->info3.base.user_flags &= ~NETLOGON_RESOURCE_GROUPS;
126 ndr_err = ndr_push_union_blob(pac_data, mem_ctx, &pac_info,
127 PAC_TYPE_LOGON_INFO,
128 (ndr_push_flags_fn_t)ndr_push_PAC_INFO);
129 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
130 nt_status = ndr_map_error2ntstatus(ndr_err);
131 DBG_WARNING("PAC_LOGON_INFO (presig) push failed: %s\n",
132 nt_errstr(nt_status));
133 goto out;
136 out:
137 talloc_free(tmp_ctx);
138 return nt_status;
141 static
142 NTSTATUS samba_get_upn_info_pac_blob(TALLOC_CTX *mem_ctx,
143 const struct auth_user_info_dc *info,
144 DATA_BLOB *upn_data)
146 TALLOC_CTX *tmp_ctx = NULL;
147 union PAC_INFO pac_upn = {};
148 enum ndr_err_code ndr_err;
149 NTSTATUS nt_status = NT_STATUS_OK;
150 bool ok;
152 *upn_data = data_blob_null;
154 tmp_ctx = talloc_new(mem_ctx);
155 if (tmp_ctx == NULL) {
156 return NT_STATUS_NO_MEMORY;
159 pac_upn.upn_dns_info.upn_name = info->info->user_principal_name;
160 pac_upn.upn_dns_info.dns_domain_name = strupper_talloc(tmp_ctx,
161 info->info->dns_domain_name);
162 if (pac_upn.upn_dns_info.dns_domain_name == NULL) {
163 nt_status = NT_STATUS_NO_MEMORY;
164 goto out;
166 if (info->info->user_principal_constructed) {
167 pac_upn.upn_dns_info.flags |= PAC_UPN_DNS_FLAG_CONSTRUCTED;
170 pac_upn.upn_dns_info.flags |= PAC_UPN_DNS_FLAG_HAS_SAM_NAME_AND_SID;
172 pac_upn.upn_dns_info.ex.sam_name_and_sid.samaccountname
173 = info->info->account_name;
175 pac_upn.upn_dns_info.ex.sam_name_and_sid.objectsid
176 = &info->sids[PRIMARY_USER_SID_INDEX].sid;
178 ndr_err = ndr_push_union_blob(upn_data, mem_ctx, &pac_upn,
179 PAC_TYPE_UPN_DNS_INFO,
180 (ndr_push_flags_fn_t)ndr_push_PAC_INFO);
181 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
182 nt_status = ndr_map_error2ntstatus(ndr_err);
183 DBG_WARNING("PAC UPN_DNS_INFO (presig) push failed: %s\n",
184 nt_errstr(nt_status));
185 goto out;
188 ok = data_blob_pad(mem_ctx, upn_data, 8);
189 if (!ok) {
190 talloc_free(upn_data);
191 nt_status = NT_STATUS_NO_MEMORY;
192 goto out;
195 out:
196 talloc_free(tmp_ctx);
197 return nt_status;
200 static
201 NTSTATUS samba_get_cred_info_ndr_blob(TALLOC_CTX *mem_ctx,
202 const struct ldb_message *msg,
203 DATA_BLOB *cred_blob)
205 enum ndr_err_code ndr_err;
206 NTSTATUS nt_status;
207 struct samr_Password *lm_hash = NULL;
208 struct samr_Password *nt_hash = NULL;
209 struct PAC_CREDENTIAL_NTLM_SECPKG ntlm_secpkg = {
210 .version = 0,
212 DATA_BLOB ntlm_blob = data_blob_null;
213 struct PAC_CREDENTIAL_SUPPLEMENTAL_SECPKG secpkgs[1] = {{
214 .credential_size = 0,
216 struct PAC_CREDENTIAL_DATA cred_data = {
217 .credential_count = 0,
219 struct PAC_CREDENTIAL_DATA_NDR cred_ndr = {};
221 *cred_blob = data_blob_null;
223 lm_hash = samdb_result_hash(mem_ctx, msg, "dBCSPwd");
224 if (lm_hash != NULL) {
225 bool zero = all_zero(lm_hash->hash, 16);
226 if (zero) {
227 lm_hash = NULL;
230 if (lm_hash != NULL) {
231 DBG_INFO("Passing LM password hash through credentials set\n");
232 ntlm_secpkg.flags |= PAC_CREDENTIAL_NTLM_HAS_LM_HASH;
233 ntlm_secpkg.lm_password = *lm_hash;
234 ZERO_STRUCTP(lm_hash);
235 TALLOC_FREE(lm_hash);
238 nt_hash = samdb_result_hash(mem_ctx, msg, "unicodePwd");
239 if (nt_hash != NULL) {
240 bool zero = all_zero(nt_hash->hash, 16);
241 if (zero) {
242 nt_hash = NULL;
245 if (nt_hash != NULL) {
246 DBG_INFO("Passing NT password hash through credentials set\n");
247 ntlm_secpkg.flags |= PAC_CREDENTIAL_NTLM_HAS_NT_HASH;
248 ntlm_secpkg.nt_password = *nt_hash;
249 ZERO_STRUCTP(nt_hash);
250 TALLOC_FREE(nt_hash);
253 if (ntlm_secpkg.flags == 0) {
254 return NT_STATUS_OK;
257 #ifdef DEBUG_PASSWORD
258 if (DEBUGLVL(11)) {
259 NDR_PRINT_DEBUG(PAC_CREDENTIAL_NTLM_SECPKG, &ntlm_secpkg);
261 #endif
263 ndr_err = ndr_push_struct_blob(&ntlm_blob, mem_ctx, &ntlm_secpkg,
264 (ndr_push_flags_fn_t)ndr_push_PAC_CREDENTIAL_NTLM_SECPKG);
265 ZERO_STRUCT(ntlm_secpkg);
266 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
267 nt_status = ndr_map_error2ntstatus(ndr_err);
268 DBG_WARNING("PAC_CREDENTIAL_NTLM_SECPKG (presig) push failed: %s\n",
269 nt_errstr(nt_status));
270 return nt_status;
273 DBG_DEBUG("NTLM credential BLOB (len %zu) for user\n",
274 ntlm_blob.length);
275 dump_data_pw("PAC_CREDENTIAL_NTLM_SECPKG",
276 ntlm_blob.data, ntlm_blob.length);
278 secpkgs[0].package_name.string = discard_const_p(char, "NTLM");
279 secpkgs[0].credential_size = ntlm_blob.length;
280 secpkgs[0].credential = ntlm_blob.data;
282 cred_data.credential_count = ARRAY_SIZE(secpkgs);
283 cred_data.credentials = secpkgs;
285 #ifdef DEBUG_PASSWORD
286 if (DEBUGLVL(11)) {
287 NDR_PRINT_DEBUG(PAC_CREDENTIAL_DATA, &cred_data);
289 #endif
291 cred_ndr.ctr.data = &cred_data;
293 #ifdef DEBUG_PASSWORD
294 if (DEBUGLVL(11)) {
295 NDR_PRINT_DEBUG(PAC_CREDENTIAL_DATA_NDR, &cred_ndr);
297 #endif
299 ndr_err = ndr_push_struct_blob(cred_blob, mem_ctx, &cred_ndr,
300 (ndr_push_flags_fn_t)ndr_push_PAC_CREDENTIAL_DATA_NDR);
301 data_blob_clear(&ntlm_blob);
302 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
303 nt_status = ndr_map_error2ntstatus(ndr_err);
304 DBG_WARNING("PAC_CREDENTIAL_DATA_NDR (presig) push failed: %s\n",
305 nt_errstr(nt_status));
306 return nt_status;
309 DBG_DEBUG("Created credential BLOB (len %zu) for user\n",
310 cred_blob->length);
311 dump_data_pw("PAC_CREDENTIAL_DATA_NDR",
312 cred_blob->data, cred_blob->length);
314 return NT_STATUS_OK;
317 krb5_error_code samba_kdc_encrypt_pac_credentials(krb5_context context,
318 const krb5_keyblock *pkreplykey,
319 const DATA_BLOB *cred_ndr_blob,
320 TALLOC_CTX *mem_ctx,
321 DATA_BLOB *cred_info_blob)
323 #ifdef SAMBA4_USES_HEIMDAL
324 krb5_crypto cred_crypto;
325 krb5_enctype cred_enctype;
326 krb5_data cred_ndr_crypt;
327 struct PAC_CREDENTIAL_INFO pac_cred_info = { .version = 0, };
328 krb5_error_code ret;
329 const char *krb5err;
330 enum ndr_err_code ndr_err;
331 NTSTATUS nt_status;
333 *cred_info_blob = data_blob_null;
335 ret = krb5_crypto_init(context, pkreplykey, ETYPE_NULL,
336 &cred_crypto);
337 if (ret != 0) {
338 krb5err = krb5_get_error_message(context, ret);
339 DBG_WARNING("Failed initializing cred data crypto: %s\n", krb5err);
340 krb5_free_error_message(context, krb5err);
341 return ret;
344 ret = krb5_crypto_getenctype(context, cred_crypto, &cred_enctype);
345 if (ret != 0) {
346 DBG_WARNING("Failed getting crypto type for key\n");
347 krb5_crypto_destroy(context, cred_crypto);
348 return ret;
351 DBG_DEBUG("Plain cred_ndr_blob (len %zu)\n",
352 cred_ndr_blob->length);
353 dump_data_pw("PAC_CREDENTIAL_DATA_NDR",
354 cred_ndr_blob->data, cred_ndr_blob->length);
356 ret = krb5_encrypt(context, cred_crypto,
357 KRB5_KU_OTHER_ENCRYPTED,
358 cred_ndr_blob->data, cred_ndr_blob->length,
359 &cred_ndr_crypt);
360 krb5_crypto_destroy(context, cred_crypto);
361 if (ret != 0) {
362 krb5err = krb5_get_error_message(context, ret);
363 DBG_WARNING("Failed crypt of cred data: %s\n", krb5err);
364 krb5_free_error_message(context, krb5err);
365 return ret;
368 pac_cred_info.encryption_type = cred_enctype;
369 pac_cred_info.encrypted_data.length = cred_ndr_crypt.length;
370 pac_cred_info.encrypted_data.data = (uint8_t *)cred_ndr_crypt.data;
372 if (DEBUGLVL(10)) {
373 NDR_PRINT_DEBUG(PAC_CREDENTIAL_INFO, &pac_cred_info);
376 ndr_err = ndr_push_struct_blob(cred_info_blob, mem_ctx, &pac_cred_info,
377 (ndr_push_flags_fn_t)ndr_push_PAC_CREDENTIAL_INFO);
378 krb5_data_free(&cred_ndr_crypt);
379 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
380 nt_status = ndr_map_error2ntstatus(ndr_err);
381 DBG_WARNING("PAC_CREDENTIAL_INFO (presig) push failed: %s\n",
382 nt_errstr(nt_status));
383 return KRB5KDC_ERR_SVC_UNAVAILABLE;
386 DBG_DEBUG("Encrypted credential BLOB (len %zu) with alg %"PRId32"\n",
387 cred_info_blob->length, pac_cred_info.encryption_type);
388 dump_data_pw("PAC_CREDENTIAL_INFO",
389 cred_info_blob->data, cred_info_blob->length);
391 return 0;
392 #else /* SAMBA4_USES_HEIMDAL */
393 TALLOC_CTX *tmp_ctx = NULL;
394 krb5_key cred_key;
395 krb5_enctype cred_enctype;
396 struct PAC_CREDENTIAL_INFO pac_cred_info = { .version = 0, };
397 krb5_error_code code = 0;
398 const char *krb5err;
399 enum ndr_err_code ndr_err;
400 NTSTATUS nt_status;
401 krb5_data cred_ndr_data;
402 krb5_enc_data cred_ndr_crypt;
403 size_t enc_len = 0;
405 *cred_info_blob = data_blob_null;
407 tmp_ctx = talloc_new(mem_ctx);
408 if (tmp_ctx == NULL) {
409 return ENOMEM;
412 code = krb5_k_create_key(context,
413 pkreplykey,
414 &cred_key);
415 if (code != 0) {
416 krb5err = krb5_get_error_message(context, code);
417 DBG_WARNING("Failed initializing cred data crypto: %s\n", krb5err);
418 krb5_free_error_message(context, krb5err);
419 goto out;
422 cred_enctype = krb5_k_key_enctype(context, cred_key);
424 DBG_DEBUG("Plain cred_ndr_blob (len %zu)\n",
425 cred_ndr_blob->length);
426 dump_data_pw("PAC_CREDENTIAL_DATA_NDR",
427 cred_ndr_blob->data, cred_ndr_blob->length);
429 pac_cred_info.encryption_type = cred_enctype;
431 cred_ndr_data = smb_krb5_data_from_blob(*cred_ndr_blob);
433 code = krb5_c_encrypt_length(context,
434 cred_enctype,
435 cred_ndr_data.length,
436 &enc_len);
437 if (code != 0) {
438 krb5err = krb5_get_error_message(context, code);
439 DBG_WARNING("Failed initializing cred data crypto: %s\n", krb5err);
440 krb5_free_error_message(context, krb5err);
441 goto out;
444 pac_cred_info.encrypted_data = data_blob_talloc_zero(tmp_ctx, enc_len);
445 if (pac_cred_info.encrypted_data.data == NULL) {
446 DBG_ERR("Out of memory\n");
447 code = ENOMEM;
448 goto out;
451 cred_ndr_crypt.ciphertext = smb_krb5_data_from_blob(pac_cred_info.encrypted_data);
453 code = krb5_k_encrypt(context,
454 cred_key,
455 KRB5_KU_OTHER_ENCRYPTED,
456 NULL,
457 &cred_ndr_data,
458 &cred_ndr_crypt);
459 krb5_k_free_key(context, cred_key);
460 if (code != 0) {
461 krb5err = krb5_get_error_message(context, code);
462 DBG_WARNING("Failed crypt of cred data: %s\n", krb5err);
463 krb5_free_error_message(context, krb5err);
464 goto out;
467 if (DEBUGLVL(10)) {
468 NDR_PRINT_DEBUG(PAC_CREDENTIAL_INFO, &pac_cred_info);
471 ndr_err = ndr_push_struct_blob(cred_info_blob, mem_ctx, &pac_cred_info,
472 (ndr_push_flags_fn_t)ndr_push_PAC_CREDENTIAL_INFO);
473 TALLOC_FREE(pac_cred_info.encrypted_data.data);
474 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
475 nt_status = ndr_map_error2ntstatus(ndr_err);
476 DBG_WARNING("PAC_CREDENTIAL_INFO (presig) push failed: %s\n",
477 nt_errstr(nt_status));
478 code = KRB5KDC_ERR_SVC_UNAVAILABLE;
479 goto out;
482 DBG_DEBUG("Encrypted credential BLOB (len %zu) with alg %"PRId32"\n",
483 cred_info_blob->length, pac_cred_info.encryption_type);
484 dump_data_pw("PAC_CREDENTIAL_INFO",
485 cred_info_blob->data, cred_info_blob->length);
487 out:
488 talloc_free(tmp_ctx);
489 return code;
490 #endif /* SAMBA4_USES_HEIMDAL */
495 * @brief Create a PAC with the given blobs (logon, credentials, upn and
496 * delegation).
498 * @param[in] context The KRB5 context to use.
500 * @param[in] logon_blob Fill the logon info PAC buffer with the given blob,
501 * use NULL to ignore it.
503 * @param[in] cred_blob Fill the credentials info PAC buffer with the given
504 * blob, use NULL to ignore it.
506 * @param[in] upn_blob Fill the UPN info PAC buffer with the given blob, use
507 * NULL to ignore it.
509 * @param[in] deleg_blob Fill the delegation info PAC buffer with the given
510 * blob, use NULL to ignore it.
512 * @param[in] client_claims_blob Fill the client claims info PAC buffer with the
513 * given blob, use NULL to ignore it.
515 * @param[in] device_info_blob Fill the device info PAC buffer with the given
516 * blob, use NULL to ignore it.
518 * @param[in] device_claims_blob Fill the device claims info PAC buffer with the given
519 * blob, use NULL to ignore it.
521 * @param[in] pac The pac buffer to fill. This should be allocated with
522 * krb5_pac_init() already.
524 * @returns 0 on success or a corresponding KRB5 error.
526 krb5_error_code samba_make_krb5_pac(krb5_context context,
527 const DATA_BLOB *logon_blob,
528 const DATA_BLOB *cred_blob,
529 const DATA_BLOB *upn_blob,
530 const DATA_BLOB *pac_attrs_blob,
531 const DATA_BLOB *requester_sid_blob,
532 const DATA_BLOB *deleg_blob,
533 const DATA_BLOB *client_claims_blob,
534 const DATA_BLOB *device_info_blob,
535 const DATA_BLOB *device_claims_blob,
536 krb5_pac pac)
538 krb5_data logon_data;
539 krb5_error_code ret;
540 char null_byte = '\0';
541 krb5_data null_data = smb_krb5_make_data(&null_byte, 0);
543 /* The user account may be set not to want the PAC */
544 if (logon_blob == NULL) {
545 return 0;
548 logon_data = smb_krb5_data_from_blob(*logon_blob);
549 ret = krb5_pac_add_buffer(context, pac, PAC_TYPE_LOGON_INFO, &logon_data);
550 if (ret != 0) {
551 return ret;
554 if (device_info_blob != NULL) {
555 krb5_data device_info_data = smb_krb5_data_from_blob(*device_info_blob);
556 ret = krb5_pac_add_buffer(context, pac,
557 PAC_TYPE_DEVICE_INFO,
558 &device_info_data);
559 if (ret != 0) {
560 return ret;
564 if (client_claims_blob != NULL) {
565 krb5_data client_claims_data;
566 krb5_data *data = NULL;
568 if (client_claims_blob->length != 0) {
569 client_claims_data = smb_krb5_data_from_blob(*client_claims_blob);
570 data = &client_claims_data;
571 } else {
572 data = &null_data;
575 ret = krb5_pac_add_buffer(context, pac,
576 PAC_TYPE_CLIENT_CLAIMS_INFO,
577 data);
578 if (ret != 0) {
579 return ret;
583 if (device_claims_blob != NULL) {
584 krb5_data device_claims_data = smb_krb5_data_from_blob(*device_claims_blob);
585 ret = krb5_pac_add_buffer(context, pac,
586 PAC_TYPE_DEVICE_CLAIMS_INFO,
587 &device_claims_data);
588 if (ret != 0) {
589 return ret;
593 if (cred_blob != NULL) {
594 krb5_data cred_data = smb_krb5_data_from_blob(*cred_blob);
595 ret = krb5_pac_add_buffer(context, pac,
596 PAC_TYPE_CREDENTIAL_INFO,
597 &cred_data);
598 if (ret != 0) {
599 return ret;
603 #ifdef SAMBA4_USES_HEIMDAL
605 * null_data will be filled by the generic KDC code in the caller
606 * here we just add it in order to have it before
607 * PAC_TYPE_UPN_DNS_INFO
609 * Not needed with MIT Kerberos - asn
611 ret = krb5_pac_add_buffer(context, pac,
612 PAC_TYPE_LOGON_NAME,
613 &null_data);
614 if (ret != 0) {
615 return ret;
617 #endif
619 if (upn_blob != NULL) {
620 krb5_data upn_data = smb_krb5_data_from_blob(*upn_blob);
621 ret = krb5_pac_add_buffer(context, pac,
622 PAC_TYPE_UPN_DNS_INFO,
623 &upn_data);
624 if (ret != 0) {
625 return ret;
629 if (pac_attrs_blob != NULL) {
630 krb5_data pac_attrs_data = smb_krb5_data_from_blob(*pac_attrs_blob);
631 ret = krb5_pac_add_buffer(context, pac,
632 PAC_TYPE_ATTRIBUTES_INFO,
633 &pac_attrs_data);
634 if (ret != 0) {
635 return ret;
639 if (requester_sid_blob != NULL) {
640 krb5_data requester_sid_data = smb_krb5_data_from_blob(*requester_sid_blob);
641 ret = krb5_pac_add_buffer(context, pac,
642 PAC_TYPE_REQUESTER_SID,
643 &requester_sid_data);
644 if (ret != 0) {
645 return ret;
649 if (deleg_blob != NULL) {
650 krb5_data deleg_data = smb_krb5_data_from_blob(*deleg_blob);
651 ret = krb5_pac_add_buffer(context, pac,
652 PAC_TYPE_CONSTRAINED_DELEGATION,
653 &deleg_data);
654 if (ret != 0) {
655 return ret;
659 return ret;
662 bool samba_princ_needs_pac(const struct samba_kdc_entry *skdc_entry)
665 uint32_t userAccountControl;
667 /* The service account may be set not to want the PAC */
668 userAccountControl = ldb_msg_find_attr_as_uint(skdc_entry->msg, "userAccountControl", 0);
669 if (userAccountControl & UF_NO_AUTH_DATA_REQUIRED) {
670 return false;
673 return true;
676 static krb5_error_code samba_client_requested_pac(krb5_context context,
677 const krb5_const_pac pac,
678 TALLOC_CTX *mem_ctx,
679 bool *requested_pac)
681 enum ndr_err_code ndr_err;
682 krb5_data k5pac_attrs_in;
683 DATA_BLOB pac_attrs_in;
684 union PAC_INFO pac_attrs;
685 krb5_error_code ret;
687 *requested_pac = true;
689 ret = krb5_pac_get_buffer(context, pac, PAC_TYPE_ATTRIBUTES_INFO,
690 &k5pac_attrs_in);
691 if (ret != 0) {
692 return ret == ENOENT ? 0 : ret;
695 pac_attrs_in = data_blob_const(k5pac_attrs_in.data,
696 k5pac_attrs_in.length);
698 ndr_err = ndr_pull_union_blob(&pac_attrs_in, mem_ctx, &pac_attrs,
699 PAC_TYPE_ATTRIBUTES_INFO,
700 (ndr_pull_flags_fn_t)ndr_pull_PAC_INFO);
701 smb_krb5_free_data_contents(context, &k5pac_attrs_in);
702 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
703 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
704 DBG_ERR("can't parse the PAC ATTRIBUTES_INFO: %s\n", nt_errstr(nt_status));
705 return map_errno_from_nt_status(nt_status);
708 if (pac_attrs.attributes_info.flags & (PAC_ATTRIBUTE_FLAG_PAC_WAS_GIVEN_IMPLICITLY
709 | PAC_ATTRIBUTE_FLAG_PAC_WAS_REQUESTED)) {
710 *requested_pac = true;
711 } else {
712 *requested_pac = false;
715 return 0;
718 /* Was the krbtgt in this DB (ie, should we check the incoming signature) and was it an RODC */
719 krb5_error_code samba_krbtgt_is_in_db(const struct samba_kdc_entry *p,
720 bool *is_in_db,
721 bool *is_trusted)
723 NTSTATUS status;
724 krb5_error_code ret;
725 int rodc_krbtgt_number, trust_direction;
726 struct dom_sid sid;
727 uint32_t rid;
729 trust_direction = ldb_msg_find_attr_as_int(p->msg, "trustDirection", 0);
731 if (trust_direction != 0) {
732 /* Domain trust - we cannot check the sig, but we trust it for a correct PAC
734 This is exactly where we should flag for SID
735 validation when we do inter-forest trusts
737 *is_trusted = true;
738 *is_in_db = false;
739 return 0;
742 /* The lack of password controls etc applies to krbtgt by
743 * virtue of being that particular RID */
744 ret = samdb_result_dom_sid_buf(p->msg, "objectSid", &sid);
745 if (ret) {
746 return ret;
749 status = dom_sid_split_rid(NULL, &sid, NULL, &rid);
750 if (!NT_STATUS_IS_OK(status)) {
751 return map_errno_from_nt_status(status);
754 rodc_krbtgt_number = ldb_msg_find_attr_as_int(p->msg, "msDS-SecondaryKrbTgtNumber", -1);
756 if (p->kdc_db_ctx->my_krbtgt_number == 0) {
757 if (rid == DOMAIN_RID_KRBTGT) {
758 *is_trusted = true;
759 *is_in_db = true;
760 return 0;
761 } else if (rodc_krbtgt_number != -1) {
762 *is_in_db = true;
763 *is_trusted = false;
764 return 0;
766 } else if ((rid != DOMAIN_RID_KRBTGT) && (rodc_krbtgt_number == p->kdc_db_ctx->my_krbtgt_number)) {
767 *is_trusted = true;
768 *is_in_db = true;
769 return 0;
770 } else if (rid == DOMAIN_RID_KRBTGT) {
771 /* krbtgt viewed from an RODC */
772 *is_trusted = true;
773 *is_in_db = false;
774 return 0;
777 /* Another RODC */
778 *is_trusted = false;
779 *is_in_db = false;
780 return 0;
784 * Because the KDC does not limit protocol transition, two new well-known SIDs
785 * were introduced to give this control to the resource administrator. These
786 * SIDs identify whether protocol transition has occurred, and can be used with
787 * standard access control lists to grant or limit access as needed.
789 * https://docs.microsoft.com/en-us/windows-server/security/kerberos/kerberos-constrained-delegation-overview
791 NTSTATUS samba_kdc_add_asserted_identity(enum samba_asserted_identity ai,
792 struct auth_user_info_dc *user_info_dc)
794 const struct dom_sid *ai_sid = NULL;
796 switch (ai) {
797 case SAMBA_ASSERTED_IDENTITY_SERVICE:
798 ai_sid = &global_sid_Asserted_Identity_Service;
799 break;
800 case SAMBA_ASSERTED_IDENTITY_AUTHENTICATION_AUTHORITY:
801 ai_sid = &global_sid_Asserted_Identity_Authentication_Authority;
802 break;
803 case SAMBA_ASSERTED_IDENTITY_IGNORE:
804 return NT_STATUS_OK;
805 default:
806 return NT_STATUS_INVALID_PARAMETER;
809 return add_sid_to_array_attrs_unique(
810 user_info_dc,
811 ai_sid,
812 SE_GROUP_DEFAULT_FLAGS,
813 &user_info_dc->sids,
814 &user_info_dc->num_sids);
817 NTSTATUS samba_kdc_add_claims_valid(struct auth_user_info_dc *user_info_dc)
819 return add_sid_to_array_attrs_unique(
820 user_info_dc,
821 &global_sid_Claims_Valid,
822 SE_GROUP_DEFAULT_FLAGS,
823 &user_info_dc->sids,
824 &user_info_dc->num_sids);
827 NTSTATUS samba_kdc_add_fresh_public_key_identity(struct auth_user_info_dc *user_info_dc)
829 return add_sid_to_array_attrs_unique(
830 user_info_dc,
831 &global_sid_Fresh_Public_Key_Identity,
832 SE_GROUP_DEFAULT_FLAGS,
833 &user_info_dc->sids,
834 &user_info_dc->num_sids);
837 static NTSTATUS samba_kdc_add_compounded_auth(struct auth_user_info_dc *user_info_dc)
839 return add_sid_to_array_attrs_unique(
840 user_info_dc,
841 &global_sid_Compounded_Authentication,
842 SE_GROUP_DEFAULT_FLAGS,
843 &user_info_dc->sids,
844 &user_info_dc->num_sids);
847 bool samba_kdc_entry_is_trust(const struct samba_kdc_entry *entry)
849 return entry != NULL && entry->is_trust;
853 * Return true if this entry has an associated PAC issued or signed by a KDC
854 * that our KDC trusts. We trust the main krbtgt account, but we don’t trust any
855 * RODC krbtgt besides ourselves.
857 bool samba_krb5_pac_is_trusted(const struct samba_kdc_entry_pac pac)
859 if (pac.pac == NULL) {
860 return false;
863 #ifdef HAVE_KRB5_PAC_IS_TRUSTED /* Heimdal */
864 return krb5_pac_is_trusted(pac.pac);
865 #else /* MIT */
866 return pac.pac_is_trusted;
867 #endif /* HAVE_KRB5_PAC_IS_TRUSTED */
870 #ifdef HAVE_KRB5_PAC_IS_TRUSTED /* Heimdal */
871 struct samba_kdc_entry_pac samba_kdc_entry_pac(krb5_const_pac pac,
872 struct samba_kdc_entry *entry,
873 bool is_from_trust)
875 return (struct samba_kdc_entry_pac) {
876 .entry = entry,
877 .pac = pac,
878 .is_from_trust = is_from_trust,
881 #else /* MIT */
882 struct samba_kdc_entry_pac samba_kdc_entry_pac_from_trusted(krb5_const_pac pac,
883 struct samba_kdc_entry *entry,
884 bool is_from_trust,
885 bool is_trusted)
887 return (struct samba_kdc_entry_pac) {
888 .entry = entry,
889 .pac = pac,
890 .is_from_trust = is_from_trust,
891 .pac_is_trusted = is_trusted,
894 #endif /* HAVE_KRB5_PAC_IS_TRUSTED */
896 static bool samba_kdc_entry_pac_issued_by_trust(const struct samba_kdc_entry_pac entry)
898 return entry.pac != NULL && entry.is_from_trust;
901 NTSTATUS samba_kdc_get_logon_info_blob(TALLOC_CTX *mem_ctx,
902 const struct auth_user_info_dc *user_info_dc,
903 const enum auth_group_inclusion group_inclusion,
904 DATA_BLOB **_logon_info_blob)
906 DATA_BLOB *logon_blob = NULL;
907 NTSTATUS nt_status;
909 *_logon_info_blob = NULL;
911 logon_blob = talloc_zero(mem_ctx, DATA_BLOB);
912 if (logon_blob == NULL) {
913 return NT_STATUS_NO_MEMORY;
916 nt_status = samba_get_logon_info_pac_blob(logon_blob,
917 user_info_dc,
918 NULL,
919 group_inclusion,
920 logon_blob);
921 if (!NT_STATUS_IS_OK(nt_status)) {
922 DBG_ERR("Building PAC LOGON INFO failed: %s\n",
923 nt_errstr(nt_status));
924 talloc_free(logon_blob);
925 return nt_status;
928 *_logon_info_blob = logon_blob;
930 return NT_STATUS_OK;
933 NTSTATUS samba_kdc_get_cred_ndr_blob(TALLOC_CTX *mem_ctx,
934 const struct samba_kdc_entry *p,
935 DATA_BLOB **_cred_ndr_blob)
937 DATA_BLOB *cred_blob = NULL;
938 NTSTATUS nt_status;
940 SMB_ASSERT(_cred_ndr_blob != NULL);
942 *_cred_ndr_blob = NULL;
944 cred_blob = talloc_zero(mem_ctx, DATA_BLOB);
945 if (cred_blob == NULL) {
946 return NT_STATUS_NO_MEMORY;
949 nt_status = samba_get_cred_info_ndr_blob(cred_blob,
950 p->msg,
951 cred_blob);
952 if (!NT_STATUS_IS_OK(nt_status)) {
953 DBG_ERR("Building PAC CRED INFO failed: %s\n",
954 nt_errstr(nt_status));
955 talloc_free(cred_blob);
956 return nt_status;
959 *_cred_ndr_blob = cred_blob;
961 return NT_STATUS_OK;
964 NTSTATUS samba_kdc_get_upn_info_blob(TALLOC_CTX *mem_ctx,
965 const struct auth_user_info_dc *user_info_dc,
966 DATA_BLOB **_upn_info_blob)
968 DATA_BLOB *upn_blob = NULL;
969 NTSTATUS nt_status;
971 *_upn_info_blob = NULL;
973 upn_blob = talloc_zero(mem_ctx, DATA_BLOB);
974 if (upn_blob == NULL) {
975 return NT_STATUS_NO_MEMORY;
978 nt_status = samba_get_upn_info_pac_blob(upn_blob,
979 user_info_dc,
980 upn_blob);
981 if (!NT_STATUS_IS_OK(nt_status)) {
982 DBG_ERR("Building PAC UPN INFO failed: %s\n",
983 nt_errstr(nt_status));
984 talloc_free(upn_blob);
985 return nt_status;
988 *_upn_info_blob = upn_blob;
990 return NT_STATUS_OK;
993 NTSTATUS samba_kdc_get_pac_attrs_blob(TALLOC_CTX *mem_ctx,
994 uint64_t pac_attributes,
995 DATA_BLOB **_pac_attrs_blob)
997 DATA_BLOB *pac_attrs_blob = NULL;
998 union PAC_INFO pac_attrs = {};
999 enum ndr_err_code ndr_err;
1000 NTSTATUS nt_status;
1002 SMB_ASSERT(_pac_attrs_blob != NULL);
1004 *_pac_attrs_blob = NULL;
1006 pac_attrs_blob = talloc_zero(mem_ctx, DATA_BLOB);
1007 if (pac_attrs_blob == NULL) {
1008 return NT_STATUS_NO_MEMORY;
1011 /* Set the length of the flags in bits. */
1012 pac_attrs.attributes_info.flags_length = 2;
1013 pac_attrs.attributes_info.flags = pac_attributes;
1015 ndr_err = ndr_push_union_blob(pac_attrs_blob, pac_attrs_blob, &pac_attrs,
1016 PAC_TYPE_ATTRIBUTES_INFO,
1017 (ndr_push_flags_fn_t)ndr_push_PAC_INFO);
1018 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1019 nt_status = ndr_map_error2ntstatus(ndr_err);
1020 DBG_WARNING("PAC ATTRIBUTES_INFO (presig) push failed: %s\n",
1021 nt_errstr(nt_status));
1022 DBG_ERR("Building PAC ATTRIBUTES failed: %s\n",
1023 nt_errstr(nt_status));
1025 talloc_free(pac_attrs_blob);
1026 return nt_status;
1029 *_pac_attrs_blob = pac_attrs_blob;
1031 return NT_STATUS_OK;
1034 NTSTATUS samba_kdc_get_requester_sid_blob(TALLOC_CTX *mem_ctx,
1035 const struct auth_user_info_dc *user_info_dc,
1036 DATA_BLOB **_requester_sid_blob)
1038 DATA_BLOB *requester_sid_blob = NULL;
1039 NTSTATUS nt_status;
1041 SMB_ASSERT(_requester_sid_blob != NULL);
1043 *_requester_sid_blob = NULL;
1045 requester_sid_blob = talloc_zero(mem_ctx, DATA_BLOB);
1046 if (requester_sid_blob == NULL) {
1047 return NT_STATUS_NO_MEMORY;
1050 if (user_info_dc->num_sids > 0) {
1051 union PAC_INFO pac_requester_sid = {};
1052 enum ndr_err_code ndr_err;
1054 pac_requester_sid.requester_sid.sid = user_info_dc->sids[PRIMARY_USER_SID_INDEX].sid;
1056 ndr_err = ndr_push_union_blob(requester_sid_blob, requester_sid_blob,
1057 &pac_requester_sid,
1058 PAC_TYPE_REQUESTER_SID,
1059 (ndr_push_flags_fn_t)ndr_push_PAC_INFO);
1060 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1061 nt_status = ndr_map_error2ntstatus(ndr_err);
1062 DBG_WARNING("PAC_REQUESTER_SID (presig) push failed: %s\n",
1063 nt_errstr(nt_status));
1064 DBG_ERR("Building PAC REQUESTER SID failed: %s\n",
1065 nt_errstr(nt_status));
1067 talloc_free(requester_sid_blob);
1068 return nt_status;
1072 *_requester_sid_blob = requester_sid_blob;
1074 return NT_STATUS_OK;
1077 NTSTATUS samba_kdc_get_claims_blob(TALLOC_CTX *mem_ctx,
1078 struct samba_kdc_entry *p,
1079 const DATA_BLOB **_claims_blob)
1081 DATA_BLOB *claims_blob = NULL;
1082 struct claims_data *claims_data = NULL;
1083 NTSTATUS nt_status;
1084 int ret;
1086 SMB_ASSERT(_claims_blob != NULL);
1088 *_claims_blob = NULL;
1090 claims_blob = talloc_zero(mem_ctx, DATA_BLOB);
1091 if (claims_blob == NULL) {
1092 return NT_STATUS_NO_MEMORY;
1095 ret = samba_kdc_get_claims_data_from_db(p->kdc_db_ctx->samdb,
1097 &claims_data);
1098 if (ret != LDB_SUCCESS) {
1099 nt_status = dsdb_ldb_err_to_ntstatus(ret);
1100 DBG_ERR("Building claims failed: %s\n",
1101 nt_errstr(nt_status));
1102 talloc_free(claims_blob);
1103 return nt_status;
1106 nt_status = claims_data_encoded_claims_set(claims_blob,
1107 claims_data,
1108 claims_blob);
1109 if (!NT_STATUS_IS_OK(nt_status)) {
1110 talloc_free(claims_blob);
1111 return nt_status;
1114 *_claims_blob = claims_blob;
1116 return NT_STATUS_OK;
1119 krb5_error_code samba_kdc_get_user_info_from_db(TALLOC_CTX *mem_ctx,
1120 struct ldb_context *samdb,
1121 struct samba_kdc_entry *entry,
1122 const struct ldb_message *msg,
1123 const struct auth_user_info_dc **info_out)
1125 NTSTATUS nt_status;
1127 if (samdb == NULL) {
1128 return EINVAL;
1131 if (msg == NULL) {
1132 return EINVAL;
1135 if (info_out == NULL) {
1136 return EINVAL;
1139 if (entry == NULL) {
1140 return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
1143 *info_out = NULL;
1145 if (entry->info_from_db == NULL) {
1146 struct auth_user_info_dc *info_from_db = NULL;
1147 struct loadparm_context *lp_ctx = entry->kdc_db_ctx->lp_ctx;
1149 nt_status = authsam_make_user_info_dc(entry,
1150 samdb,
1151 lpcfg_netbios_name(lp_ctx),
1152 lpcfg_sam_name(lp_ctx),
1153 lpcfg_sam_dnsname(lp_ctx),
1154 entry->realm_dn,
1155 msg,
1156 data_blob_null,
1157 data_blob_null,
1158 &info_from_db);
1159 if (!NT_STATUS_IS_OK(nt_status)) {
1160 DBG_ERR("Getting user info for PAC failed: %s\n",
1161 nt_errstr(nt_status));
1162 /* NT_STATUS_OBJECT_NAME_NOT_FOUND is mapped to ENOENT. */
1163 return map_errno_from_nt_status(nt_status);
1166 entry->info_from_db = info_from_db;
1169 *info_out = entry->info_from_db;
1171 return 0;
1175 * Check whether a PAC contains the Authentication Authority Asserted Identity
1176 * SID.
1178 static krb5_error_code samba_kdc_pac_contains_asserted_identity(
1179 krb5_context context,
1180 const struct samba_kdc_entry_pac entry,
1181 bool *contains_out)
1183 TALLOC_CTX *frame = NULL;
1184 struct auth_user_info_dc *info = NULL;
1185 krb5_error_code ret = 0;
1187 if (contains_out == NULL) {
1188 ret = EINVAL;
1189 goto out;
1191 *contains_out = false;
1193 frame = talloc_stackframe();
1196 * Extract our info from the PAC. This does a bit of unnecessary work,
1197 * setting up fields we don’t care about — we only want the SIDs.
1199 ret = kerberos_pac_to_user_info_dc(frame,
1200 entry.pac,
1201 context,
1202 &info,
1203 AUTH_EXCLUDE_RESOURCE_GROUPS,
1204 NULL /* pac_srv_sig */,
1205 NULL /* pac_kdc_sig */,
1206 /* Ignore the resource groups. */
1207 NULL /* resource_groups */);
1208 if (ret) {
1209 const char *krb5err = krb5_get_error_message(context, ret);
1210 DBG_ERR("kerberos_pac_to_user_info_dc failed: %s\n",
1211 krb5err != NULL ? krb5err : "?");
1212 krb5_free_error_message(context, krb5err);
1214 goto out;
1217 /* Determine whether the PAC contains the Asserted Identity SID. */
1218 *contains_out = sid_attrs_contains_sid(
1219 info->sids,
1220 info->num_sids,
1221 &global_sid_Asserted_Identity_Authentication_Authority);
1223 out:
1224 talloc_free(frame);
1225 return ret;
1228 static krb5_error_code samba_kdc_get_user_info_from_pac(TALLOC_CTX *mem_ctx,
1229 krb5_context context,
1230 struct ldb_context *samdb,
1231 const struct samba_kdc_entry_pac entry,
1232 const struct auth_user_info_dc **info_out,
1233 const struct PAC_DOMAIN_GROUP_MEMBERSHIP **resource_groups_out)
1235 TALLOC_CTX *frame = NULL;
1236 struct auth_user_info_dc *info = NULL;
1237 struct PAC_DOMAIN_GROUP_MEMBERSHIP *resource_groups = NULL;
1238 krb5_error_code ret = 0;
1239 NTSTATUS nt_status;
1241 if (samdb == NULL) {
1242 ret = EINVAL;
1243 goto out;
1246 if (!samba_krb5_pac_is_trusted(entry)) {
1247 ret = EINVAL;
1248 goto out;
1251 if (info_out == NULL) {
1252 ret = EINVAL;
1253 goto out;
1256 *info_out = NULL;
1257 if (resource_groups_out != NULL) {
1258 *resource_groups_out = NULL;
1261 if (entry.entry == NULL || entry.entry->info_from_pac == NULL) {
1262 frame = talloc_stackframe();
1264 ret = kerberos_pac_to_user_info_dc(frame,
1265 entry.pac,
1266 context,
1267 &info,
1268 AUTH_EXCLUDE_RESOURCE_GROUPS,
1269 NULL,
1270 NULL,
1271 &resource_groups);
1272 if (ret) {
1273 const char *krb5err = krb5_get_error_message(context, ret);
1274 DBG_ERR("kerberos_pac_to_user_info_dc failed: %s\n",
1275 krb5err != NULL ? krb5err : "?");
1276 krb5_free_error_message(context, krb5err);
1278 goto out;
1282 * We need to expand group memberships within our local domain,
1283 * as the token might be generated by a trusted domain.
1285 nt_status = authsam_update_user_info_dc(frame,
1286 samdb,
1287 info);
1288 if (!NT_STATUS_IS_OK(nt_status)) {
1289 DBG_ERR("authsam_update_user_info_dc failed: %s\n",
1290 nt_errstr(nt_status));
1292 ret = map_errno_from_nt_status(nt_status);
1293 goto out;
1296 if (entry.entry != NULL) {
1297 entry.entry->info_from_pac = talloc_steal(entry.entry, info);
1298 entry.entry->resource_groups_from_pac = talloc_steal(entry.entry, resource_groups);
1303 if (entry.entry != NULL) {
1304 /* Note: the caller does not own this! */
1305 *info_out = entry.entry->info_from_pac;
1307 if (resource_groups_out != NULL) {
1308 /* Note: the caller does not own this! */
1309 *resource_groups_out = entry.entry->resource_groups_from_pac;
1311 } else {
1312 *info_out = talloc_steal(mem_ctx, info);
1314 if (resource_groups_out != NULL) {
1315 *resource_groups_out = talloc_steal(mem_ctx, resource_groups);
1319 out:
1320 talloc_free(frame);
1321 return ret;
1324 krb5_error_code samba_kdc_get_user_info_dc(TALLOC_CTX *mem_ctx,
1325 krb5_context context,
1326 struct ldb_context *samdb,
1327 const struct samba_kdc_entry_pac entry,
1328 const struct auth_user_info_dc **info_out,
1329 const struct PAC_DOMAIN_GROUP_MEMBERSHIP **resource_groups_out)
1331 const struct auth_user_info_dc *info = NULL;
1332 struct auth_user_info_dc *info_shallow_copy = NULL;
1333 bool pac_contains_asserted_identity = false;
1334 krb5_error_code ret = 0;
1335 NTSTATUS nt_status;
1337 *info_out = NULL;
1338 if (resource_groups_out != NULL) {
1339 *resource_groups_out = NULL;
1342 if (samba_krb5_pac_is_trusted(entry)) {
1343 return samba_kdc_get_user_info_from_pac(mem_ctx,
1344 context,
1345 samdb,
1346 entry,
1347 info_out,
1348 resource_groups_out);
1351 if (entry.entry == NULL) {
1352 return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
1356 * In this case the RWDC discards the PAC an RODC generated.
1357 * Windows adds the asserted_identity in this case too.
1359 * Note that SAMBA_KDC_FLAG_CONSTRAINED_DELEGATION
1360 * generates KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN.
1361 * So we can always use
1362 * SAMBA_ASSERTED_IDENTITY_AUTHENTICATION_AUTHORITY
1363 * here.
1365 ret = samba_kdc_get_user_info_from_db(mem_ctx,
1366 samdb,
1367 entry.entry,
1368 entry.entry->msg,
1369 &info);
1370 if (ret) {
1371 const char *krb5err = krb5_get_error_message(context, ret);
1372 DBG_ERR("samba_kdc_get_user_info_from_db: %s\n",
1373 krb5err != NULL ? krb5err : "?");
1374 krb5_free_error_message(context, krb5err);
1376 return KRB5KDC_ERR_TGT_REVOKED;
1379 /* Make a shallow copy of the user_info_dc structure. */
1380 nt_status = authsam_shallow_copy_user_info_dc(mem_ctx,
1381 info,
1382 &info_shallow_copy);
1383 info = NULL;
1385 if (!NT_STATUS_IS_OK(nt_status)) {
1386 DBG_ERR("Failed to allocate user_info_dc SIDs: %s\n",
1387 nt_errstr(nt_status));
1388 return map_errno_from_nt_status(nt_status);
1391 /* Determine whether the PAC contains the Asserted Identity SID. */
1392 ret = samba_kdc_pac_contains_asserted_identity(
1393 context, entry, &pac_contains_asserted_identity);
1394 if (ret) {
1395 return ret;
1398 if (pac_contains_asserted_identity) {
1399 nt_status = samba_kdc_add_asserted_identity(
1400 SAMBA_ASSERTED_IDENTITY_AUTHENTICATION_AUTHORITY,
1401 info_shallow_copy);
1402 if (!NT_STATUS_IS_OK(nt_status)) {
1403 DBG_ERR("Failed to add asserted identity: %s\n",
1404 nt_errstr(nt_status));
1405 TALLOC_FREE(info_shallow_copy);
1406 return KRB5KDC_ERR_TGT_REVOKED;
1410 nt_status = samba_kdc_add_claims_valid(info_shallow_copy);
1411 if (!NT_STATUS_IS_OK(nt_status)) {
1412 DBG_ERR("Failed to add Claims Valid: %s\n",
1413 nt_errstr(nt_status));
1414 TALLOC_FREE(info_shallow_copy);
1415 return KRB5KDC_ERR_TGT_REVOKED;
1418 *info_out = info_shallow_copy;
1420 return 0;
1423 static NTSTATUS samba_kdc_update_delegation_info_blob(TALLOC_CTX *mem_ctx,
1424 krb5_context context,
1425 const krb5_const_pac pac,
1426 const krb5_const_principal server_principal,
1427 const krb5_const_principal proxy_principal,
1428 DATA_BLOB *new_blob)
1430 krb5_data old_data = {};
1431 DATA_BLOB old_blob;
1432 krb5_error_code ret;
1433 NTSTATUS nt_status = NT_STATUS_OK;
1434 enum ndr_err_code ndr_err;
1435 union PAC_INFO info = {};
1436 struct PAC_CONSTRAINED_DELEGATION _d = {};
1437 struct PAC_CONSTRAINED_DELEGATION *d = NULL;
1438 char *server = NULL;
1439 char *proxy = NULL;
1440 uint32_t i;
1441 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
1443 if (tmp_ctx == NULL) {
1444 nt_status = NT_STATUS_NO_MEMORY;
1445 goto out;
1448 ret = krb5_pac_get_buffer(context, pac, PAC_TYPE_CONSTRAINED_DELEGATION, &old_data);
1449 if (ret == ENOENT) {
1450 /* OK. */
1451 } else if (ret) {
1452 nt_status = NT_STATUS_UNSUCCESSFUL;
1453 goto out;
1456 old_blob.length = old_data.length;
1457 old_blob.data = (uint8_t *)old_data.data;
1459 if (old_blob.length > 0) {
1460 ndr_err = ndr_pull_union_blob(&old_blob, tmp_ctx,
1461 &info, PAC_TYPE_CONSTRAINED_DELEGATION,
1462 (ndr_pull_flags_fn_t)ndr_pull_PAC_INFO);
1463 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1464 smb_krb5_free_data_contents(context, &old_data);
1465 nt_status = ndr_map_error2ntstatus(ndr_err);
1466 DBG_ERR("can't parse the PAC LOGON_INFO: %s\n", nt_errstr(nt_status));
1467 goto out;
1469 } else {
1470 info.constrained_delegation.info = &_d;
1472 smb_krb5_free_data_contents(context, &old_data);
1474 ret = krb5_unparse_name_flags(context, server_principal,
1475 KRB5_PRINCIPAL_UNPARSE_NO_REALM, &server);
1476 if (ret) {
1477 nt_status = NT_STATUS_INTERNAL_ERROR;
1478 goto out;
1481 ret = krb5_unparse_name(context, proxy_principal, &proxy);
1482 if (ret) {
1483 SAFE_FREE(server);
1484 nt_status = NT_STATUS_INTERNAL_ERROR;
1485 goto out;
1488 d = info.constrained_delegation.info;
1489 i = d->num_transited_services;
1490 d->proxy_target.string = server;
1491 d->transited_services = talloc_realloc(mem_ctx, d->transited_services,
1492 struct lsa_String, i + 1);
1493 if (d->transited_services == NULL) {
1494 SAFE_FREE(server);
1495 SAFE_FREE(proxy);
1496 nt_status = NT_STATUS_INTERNAL_ERROR;
1497 goto out;
1499 d->transited_services[i].string = proxy;
1500 d->num_transited_services = i + 1;
1502 ndr_err = ndr_push_union_blob(new_blob, mem_ctx,
1503 &info, PAC_TYPE_CONSTRAINED_DELEGATION,
1504 (ndr_push_flags_fn_t)ndr_push_PAC_INFO);
1505 SAFE_FREE(server);
1506 SAFE_FREE(proxy);
1507 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1508 smb_krb5_free_data_contents(context, &old_data);
1509 nt_status = ndr_map_error2ntstatus(ndr_err);
1510 DBG_ERR("can't parse the PAC LOGON_INFO: %s\n", nt_errstr(nt_status));
1511 goto out;
1514 out:
1515 talloc_free(tmp_ctx);
1516 return nt_status;
1519 /* function to map policy errors */
1520 krb5_error_code samba_kdc_map_policy_err(NTSTATUS nt_status)
1522 krb5_error_code ret;
1524 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_PASSWORD_MUST_CHANGE))
1525 ret = KRB5KDC_ERR_KEY_EXP;
1526 else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_PASSWORD_EXPIRED))
1527 ret = KRB5KDC_ERR_KEY_EXP;
1528 else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCOUNT_EXPIRED))
1529 ret = KRB5KDC_ERR_CLIENT_REVOKED;
1530 else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCOUNT_DISABLED))
1531 ret = KRB5KDC_ERR_CLIENT_REVOKED;
1532 else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_LOGON_HOURS))
1533 ret = KRB5KDC_ERR_CLIENT_REVOKED;
1534 else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCOUNT_LOCKED_OUT))
1535 ret = KRB5KDC_ERR_CLIENT_REVOKED;
1536 else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_WORKSTATION))
1537 ret = KRB5KDC_ERR_POLICY;
1538 else
1539 ret = KRB5KDC_ERR_POLICY;
1541 return ret;
1544 /* Given a kdc entry, consult the account_ok routine in auth/auth_sam.c
1545 * for consistency */
1546 NTSTATUS samba_kdc_check_client_access(struct samba_kdc_entry *kdc_entry,
1547 const char *client_name,
1548 const char *workstation,
1549 bool password_change)
1551 TALLOC_CTX *tmp_ctx;
1552 NTSTATUS nt_status;
1554 tmp_ctx = talloc_named(NULL, 0, "samba_kdc_check_client_access");
1555 if (!tmp_ctx) {
1556 return NT_STATUS_NO_MEMORY;
1559 /* we allow all kinds of trusts here */
1560 nt_status = authsam_account_ok(tmp_ctx,
1561 kdc_entry->kdc_db_ctx->samdb,
1562 kdc_entry->current_nttime,
1563 MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT |
1564 MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT,
1565 kdc_entry->realm_dn, kdc_entry->msg,
1566 workstation, client_name,
1567 true, password_change);
1569 kdc_entry->reject_status = nt_status;
1570 talloc_free(tmp_ctx);
1571 return nt_status;
1574 static krb5_error_code samba_get_requester_sid(TALLOC_CTX *mem_ctx,
1575 krb5_const_pac pac,
1576 krb5_context context,
1577 struct dom_sid *sid)
1579 NTSTATUS nt_status;
1580 enum ndr_err_code ndr_err;
1581 krb5_error_code ret = 0;
1583 DATA_BLOB pac_requester_sid_in;
1584 krb5_data k5pac_requester_sid_in;
1586 union PAC_INFO info;
1588 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
1589 if (tmp_ctx == NULL) {
1590 ret = ENOMEM;
1591 goto out;
1594 ret = krb5_pac_get_buffer(context, pac, PAC_TYPE_REQUESTER_SID,
1595 &k5pac_requester_sid_in);
1596 if (ret != 0) {
1597 goto out;
1600 pac_requester_sid_in = data_blob_const(k5pac_requester_sid_in.data,
1601 k5pac_requester_sid_in.length);
1603 ndr_err = ndr_pull_union_blob(&pac_requester_sid_in, tmp_ctx, &info,
1604 PAC_TYPE_REQUESTER_SID,
1605 (ndr_pull_flags_fn_t)ndr_pull_PAC_INFO);
1606 smb_krb5_free_data_contents(context, &k5pac_requester_sid_in);
1607 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1608 nt_status = ndr_map_error2ntstatus(ndr_err);
1609 DBG_ERR("can't parse the PAC REQUESTER_SID: %s\n", nt_errstr(nt_status));
1610 ret = map_errno_from_nt_status(nt_status);
1611 goto out;
1614 *sid = info.requester_sid.sid;
1616 out:
1617 talloc_free(tmp_ctx);
1618 return ret;
1621 /* Does a parse and SID check, but no crypto. */
1622 static krb5_error_code samba_kdc_validate_pac_blob(
1623 krb5_context context,
1624 const struct samba_kdc_entry_pac client)
1626 TALLOC_CTX *frame = talloc_stackframe();
1627 struct auth_user_info_dc *pac_user_info = NULL;
1628 struct dom_sid client_sid;
1629 struct dom_sid pac_sid;
1630 krb5_error_code code;
1631 bool ok;
1634 * First, try to get the SID from the requester SID buffer in the PAC.
1636 code = samba_get_requester_sid(frame, client.pac, context, &pac_sid);
1638 if (code == ENOENT) {
1640 * If the requester SID buffer isn't present, fall back to the
1641 * SID in the LOGON_INFO PAC buffer.
1643 code = kerberos_pac_to_user_info_dc(frame,
1644 client.pac,
1645 context,
1646 &pac_user_info,
1647 AUTH_EXCLUDE_RESOURCE_GROUPS,
1648 NULL,
1649 NULL,
1650 NULL);
1651 if (code != 0) {
1652 goto out;
1655 if (pac_user_info->num_sids == 0) {
1656 code = EINVAL;
1657 goto out;
1660 pac_sid = pac_user_info->sids[PRIMARY_USER_SID_INDEX].sid;
1661 } else if (code != 0) {
1662 goto out;
1665 code = samdb_result_dom_sid_buf(client.entry->msg,
1666 "objectSid",
1667 &client_sid);
1668 if (code) {
1669 goto out;
1672 ok = dom_sid_equal(&pac_sid, &client_sid);
1673 if (!ok) {
1674 struct dom_sid_buf buf1;
1675 struct dom_sid_buf buf2;
1677 DBG_ERR("SID mismatch between PAC and looked up client: "
1678 "PAC[%s] != CLI[%s]\n",
1679 dom_sid_str_buf(&pac_sid, &buf1),
1680 dom_sid_str_buf(&client_sid, &buf2));
1681 code = KRB5KDC_ERR_TGT_REVOKED;
1682 goto out;
1685 code = 0;
1686 out:
1687 TALLOC_FREE(frame);
1688 return code;
1693 * In the RODC case, to confirm that the returned user is permitted to
1694 * be replicated to the KDC (krbgtgt_xxx user) represented by *rodc
1696 static WERROR samba_rodc_confirm_user_is_allowed(uint32_t num_object_sids,
1697 const struct dom_sid *object_sids,
1698 const struct samba_kdc_entry *rodc,
1699 const struct samba_kdc_entry *object)
1701 int ret;
1702 WERROR werr;
1703 TALLOC_CTX *frame = talloc_stackframe();
1704 const char *rodc_attrs[] = { "msDS-KrbTgtLink",
1705 "msDS-NeverRevealGroup",
1706 "msDS-RevealOnDemandGroup",
1707 "userAccountControl",
1708 "objectSid",
1709 NULL };
1710 struct ldb_result *rodc_machine_account = NULL;
1711 struct ldb_dn *rodc_machine_account_dn = samdb_result_dn(rodc->kdc_db_ctx->samdb,
1712 frame,
1713 rodc->msg,
1714 "msDS-KrbTgtLinkBL",
1715 NULL);
1716 const struct dom_sid *rodc_machine_account_sid = NULL;
1718 if (rodc_machine_account_dn == NULL) {
1719 DBG_ERR("krbtgt account %s has no msDS-KrbTgtLinkBL to find RODC machine account for allow/deny list\n",
1720 ldb_dn_get_linearized(rodc->msg->dn));
1721 TALLOC_FREE(frame);
1722 return WERR_DOMAIN_CONTROLLER_NOT_FOUND;
1726 * Follow the link and get the RODC account (the krbtgt
1727 * account is the krbtgt_XXX account, but the
1728 * msDS-NeverRevealGroup and msDS-RevealOnDemandGroup is on
1729 * the RODC$ account)
1731 * We need DSDB_SEARCH_SHOW_EXTENDED_DN as we get a SID lists
1732 * out of the extended DNs
1735 ret = dsdb_search_dn(rodc->kdc_db_ctx->samdb,
1736 frame,
1737 &rodc_machine_account,
1738 rodc_machine_account_dn,
1739 rodc_attrs,
1740 DSDB_SEARCH_SHOW_EXTENDED_DN);
1741 if (ret != LDB_SUCCESS) {
1742 DBG_ERR("Failed to fetch RODC machine account %s pointed to by %s to check allow/deny list: %s\n",
1743 ldb_dn_get_linearized(rodc_machine_account_dn),
1744 ldb_dn_get_linearized(rodc->msg->dn),
1745 ldb_errstring(rodc->kdc_db_ctx->samdb));
1746 TALLOC_FREE(frame);
1747 return WERR_DOMAIN_CONTROLLER_NOT_FOUND;
1750 if (rodc_machine_account->count != 1) {
1751 DBG_ERR("Failed to fetch RODC machine account %s pointed to by %s to check allow/deny list: (%d)\n",
1752 ldb_dn_get_linearized(rodc_machine_account_dn),
1753 ldb_dn_get_linearized(rodc->msg->dn),
1754 rodc_machine_account->count);
1755 TALLOC_FREE(frame);
1756 return WERR_DS_DRA_BAD_DN;
1759 /* if the object SID is equal to the user_sid, allow */
1760 rodc_machine_account_sid = samdb_result_dom_sid(frame,
1761 rodc_machine_account->msgs[0],
1762 "objectSid");
1763 if (rodc_machine_account_sid == NULL) {
1764 TALLOC_FREE(frame);
1765 return WERR_DS_DRA_BAD_DN;
1768 werr = samdb_confirm_rodc_allowed_to_repl_to_sid_list(rodc->kdc_db_ctx->samdb,
1769 rodc_machine_account_sid,
1770 rodc_machine_account->msgs[0],
1771 object->msg,
1772 num_object_sids,
1773 object_sids);
1775 TALLOC_FREE(frame);
1776 return werr;
1780 * Perform an access check for the client attempting to authenticate to the
1781 * server. ‘client_info’ must be talloc-allocated so that we can make a
1782 * reference to it.
1784 krb5_error_code samba_kdc_allowed_to_authenticate_to(TALLOC_CTX *mem_ctx,
1785 struct ldb_context *samdb,
1786 struct loadparm_context *lp_ctx,
1787 const struct samba_kdc_entry *client,
1788 const struct auth_user_info_dc *client_info,
1789 const struct auth_user_info_dc *device_info,
1790 const struct auth_claims auth_claims,
1791 const struct samba_kdc_entry *server,
1792 struct authn_audit_info **server_audit_info_out,
1793 NTSTATUS *status_out)
1795 krb5_error_code ret = 0;
1796 NTSTATUS status;
1797 _UNUSED_ NTSTATUS _status;
1798 struct dom_sid server_sid = {};
1799 const struct authn_server_policy *server_policy = server->server_policy;
1801 if (status_out != NULL) {
1802 *status_out = NT_STATUS_OK;
1805 ret = samdb_result_dom_sid_buf(server->msg, "objectSid", &server_sid);
1806 if (ret) {
1808 * Ignore the return status — we are already in an error path,
1809 * and overwriting the real error code with the audit info
1810 * status is unhelpful.
1812 _status = authn_server_policy_audit_info(mem_ctx,
1813 server_policy,
1814 client_info,
1815 AUTHN_AUDIT_EVENT_OTHER_ERROR,
1816 AUTHN_AUDIT_REASON_NONE,
1817 dsdb_ldb_err_to_ntstatus(ret),
1818 server_audit_info_out);
1819 goto out;
1822 if (dom_sid_equal(&client_info->sids[PRIMARY_USER_SID_INDEX].sid, &server_sid)) {
1823 /* Authenticating to ourselves is always allowed. */
1824 status = authn_server_policy_audit_info(mem_ctx,
1825 server_policy,
1826 client_info,
1827 AUTHN_AUDIT_EVENT_OK,
1828 AUTHN_AUDIT_REASON_NONE,
1829 NT_STATUS_OK,
1830 server_audit_info_out);
1831 if (!NT_STATUS_IS_OK(status)) {
1832 ret = KRB5KRB_ERR_GENERIC;
1834 goto out;
1837 status = authn_policy_authenticate_to_service(mem_ctx,
1838 samdb,
1839 lp_ctx,
1840 AUTHN_POLICY_AUTH_TYPE_KERBEROS,
1841 client_info,
1842 device_info,
1843 auth_claims,
1844 server_policy,
1845 (struct authn_policy_flags) { .force_compounded_authentication = true },
1846 server_audit_info_out);
1847 if (!NT_STATUS_IS_OK(status)) {
1848 if (status_out != NULL) {
1849 *status_out = status;
1851 if (NT_STATUS_EQUAL(status, NT_STATUS_AUTHENTICATION_FIREWALL_FAILED)) {
1852 ret = KRB5KDC_ERR_POLICY;
1853 } else if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
1854 ret = KRB5KDC_ERR_POLICY;
1855 } else {
1856 ret = KRB5KRB_ERR_GENERIC;
1860 out:
1861 return ret;
1864 static krb5_error_code samba_kdc_add_domain_group_sid(struct PAC_DEVICE_INFO *info,
1865 const struct netr_SidAttr *sid)
1867 uint32_t i;
1868 uint32_t rid;
1869 NTSTATUS status;
1871 uint32_t domain_group_count = info->domain_group_count;
1872 struct PAC_DOMAIN_GROUP_MEMBERSHIP *domain_group = NULL;
1873 struct samr_RidWithAttribute *rids = NULL;
1875 for (i = 0; i < domain_group_count; ++i) {
1876 struct PAC_DOMAIN_GROUP_MEMBERSHIP *this_domain_group
1877 = &info->domain_groups[i];
1879 if (dom_sid_in_domain(this_domain_group->domain_sid, sid->sid)) {
1880 domain_group = this_domain_group;
1881 break;
1885 if (domain_group == NULL) {
1886 struct PAC_DOMAIN_GROUP_MEMBERSHIP *domain_groups = NULL;
1888 if (domain_group_count == UINT32_MAX) {
1889 return EINVAL;
1892 domain_groups = talloc_realloc(
1893 info,
1894 info->domain_groups,
1895 struct PAC_DOMAIN_GROUP_MEMBERSHIP,
1896 domain_group_count + 1);
1897 if (domain_groups == NULL) {
1898 return ENOMEM;
1901 info->domain_groups = domain_groups;
1903 domain_group = &info->domain_groups[domain_group_count++];
1904 *domain_group = (struct PAC_DOMAIN_GROUP_MEMBERSHIP) {};
1906 status = dom_sid_split_rid(info->domain_groups,
1907 sid->sid,
1908 &domain_group->domain_sid,
1909 &rid);
1910 if (!NT_STATUS_IS_OK(status)) {
1911 return map_errno_from_nt_status(status);
1913 } else {
1914 status = dom_sid_split_rid(NULL,
1915 sid->sid,
1916 NULL,
1917 &rid);
1918 if (!NT_STATUS_IS_OK(status)) {
1919 return map_errno_from_nt_status(status);
1923 if (domain_group->groups.count == UINT32_MAX) {
1924 return EINVAL;
1927 rids = talloc_realloc(info->domain_groups,
1928 domain_group->groups.rids,
1929 struct samr_RidWithAttribute,
1930 domain_group->groups.count + 1);
1931 if (rids == NULL) {
1932 return ENOMEM;
1935 domain_group->groups.rids = rids;
1937 domain_group->groups.rids[domain_group->groups.count] = (struct samr_RidWithAttribute) {
1938 .rid = rid,
1939 .attributes = sid->attributes,
1942 ++domain_group->groups.count;
1944 info->domain_group_count = domain_group_count;
1946 return 0;
1949 static krb5_error_code samba_kdc_make_device_info(TALLOC_CTX *mem_ctx,
1950 const struct netr_SamInfo3 *info3,
1951 struct PAC_DOMAIN_GROUP_MEMBERSHIP *resource_groups,
1952 union PAC_INFO *info)
1954 TALLOC_CTX *tmp_ctx = NULL;
1955 struct PAC_DEVICE_INFO *device_info = NULL;
1956 uint32_t i;
1957 krb5_error_code ret = 0;
1959 *info = (union PAC_INFO) {};
1961 info->device_info.info = NULL;
1963 tmp_ctx = talloc_new(mem_ctx);
1964 if (tmp_ctx == NULL) {
1965 return ENOMEM;
1968 device_info = talloc(tmp_ctx, struct PAC_DEVICE_INFO);
1969 if (device_info == NULL) {
1970 ret = ENOMEM;
1971 goto out;
1974 device_info->rid = info3->base.rid;
1975 device_info->primary_gid = info3->base.primary_gid;
1976 device_info->domain_sid = info3->base.domain_sid;
1977 device_info->groups = info3->base.groups;
1979 device_info->sid_count = 0;
1980 device_info->sids = NULL;
1982 if (resource_groups != NULL) {
1984 * The account's resource groups all belong to the same domain,
1985 * so we can add them all in one go.
1987 device_info->domain_group_count = 1;
1988 device_info->domain_groups = talloc_move(device_info, &resource_groups);
1989 } else {
1990 device_info->domain_group_count = 0;
1991 device_info->domain_groups = NULL;
1994 for (i = 0; i < info3->sidcount; ++i) {
1995 const struct netr_SidAttr *device_sid = &info3->sids[i];
1997 if (dom_sid_has_account_domain(device_sid->sid)) {
1998 ret = samba_kdc_add_domain_group_sid(device_info, device_sid);
1999 if (ret != 0) {
2000 goto out;
2002 } else {
2003 device_info->sids = talloc_realloc(device_info, device_info->sids,
2004 struct netr_SidAttr,
2005 device_info->sid_count + 1);
2006 if (device_info->sids == NULL) {
2007 ret = ENOMEM;
2008 goto out;
2011 device_info->sids[device_info->sid_count].sid = dom_sid_dup(device_info->sids, device_sid->sid);
2012 if (device_info->sids[device_info->sid_count].sid == NULL) {
2013 ret = ENOMEM;
2014 goto out;
2017 device_info->sids[device_info->sid_count].attributes = device_sid->attributes;
2019 ++device_info->sid_count;
2023 info->device_info.info = talloc_steal(mem_ctx, device_info);
2025 out:
2026 talloc_free(tmp_ctx);
2027 return ret;
2030 static krb5_error_code samba_kdc_update_device_info(TALLOC_CTX *mem_ctx,
2031 struct ldb_context *samdb,
2032 const union PAC_INFO *logon_info,
2033 struct PAC_DEVICE_INFO *device_info)
2035 NTSTATUS nt_status;
2036 struct auth_user_info_dc *device_info_dc = NULL;
2037 union netr_Validation validation;
2038 uint32_t i;
2039 uint32_t num_existing_sids;
2042 * This does a bit of unnecessary work, setting up fields we don't care
2043 * about -- we only want the SIDs.
2045 validation.sam3 = &logon_info->logon_info.info->info3;
2046 nt_status = make_user_info_dc_netlogon_validation(mem_ctx, "", 3, &validation,
2047 true, /* This user was authenticated */
2048 &device_info_dc);
2049 if (!NT_STATUS_IS_OK(nt_status)) {
2050 return map_errno_from_nt_status(nt_status);
2053 num_existing_sids = device_info_dc->num_sids;
2056 * We need to expand group memberships within our local domain,
2057 * as the token might be generated by a trusted domain.
2059 nt_status = authsam_update_user_info_dc(mem_ctx,
2060 samdb,
2061 device_info_dc);
2062 if (!NT_STATUS_IS_OK(nt_status)) {
2063 return map_errno_from_nt_status(nt_status);
2066 for (i = num_existing_sids; i < device_info_dc->num_sids; ++i) {
2067 struct auth_SidAttr *device_sid = &device_info_dc->sids[i];
2068 const struct netr_SidAttr sid = (struct netr_SidAttr) {
2069 .sid = &device_sid->sid,
2070 .attributes = device_sid->attrs,
2073 krb5_error_code ret = samba_kdc_add_domain_group_sid(device_info, &sid);
2074 if (ret != 0) {
2075 return ret;
2079 return 0;
2082 static krb5_error_code samba_kdc_get_device_info_pac_blob(TALLOC_CTX *mem_ctx,
2083 union PAC_INFO *info,
2084 DATA_BLOB **_device_info_blob)
2086 DATA_BLOB *device_info_blob = NULL;
2087 enum ndr_err_code ndr_err;
2089 *_device_info_blob = NULL;
2091 device_info_blob = talloc_zero(mem_ctx, DATA_BLOB);
2092 if (device_info_blob == NULL) {
2093 DBG_ERR("Out of memory\n");
2094 return ENOMEM;
2097 ndr_err = ndr_push_union_blob(device_info_blob, device_info_blob,
2098 info, PAC_TYPE_DEVICE_INFO,
2099 (ndr_push_flags_fn_t)ndr_push_PAC_INFO);
2100 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2101 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
2102 DBG_WARNING("PAC_DEVICE_INFO (presig) push failed: %s\n",
2103 nt_errstr(nt_status));
2104 talloc_free(device_info_blob);
2105 return map_errno_from_nt_status(nt_status);
2108 *_device_info_blob = device_info_blob;
2110 return 0;
2113 static krb5_error_code samba_kdc_create_device_info_blob(TALLOC_CTX *mem_ctx,
2114 krb5_context context,
2115 struct ldb_context *samdb,
2116 const krb5_const_pac device_pac,
2117 DATA_BLOB **device_info_blob)
2119 TALLOC_CTX *frame = NULL;
2120 krb5_data device_logon_info;
2121 krb5_error_code code = EINVAL;
2122 NTSTATUS nt_status;
2124 union PAC_INFO info;
2125 enum ndr_err_code ndr_err;
2126 DATA_BLOB device_logon_info_blob;
2128 union PAC_INFO logon_info;
2130 code = krb5_pac_get_buffer(context, device_pac,
2131 PAC_TYPE_LOGON_INFO,
2132 &device_logon_info);
2133 if (code != 0) {
2134 if (code == ENOENT) {
2135 DBG_ERR("Device PAC is missing LOGON_INFO\n");
2136 } else {
2137 DBG_ERR("Error getting LOGON_INFO from device PAC\n");
2139 return code;
2142 frame = talloc_stackframe();
2144 device_logon_info_blob = data_blob_const(device_logon_info.data,
2145 device_logon_info.length);
2147 ndr_err = ndr_pull_union_blob(&device_logon_info_blob, frame, &logon_info,
2148 PAC_TYPE_LOGON_INFO,
2149 (ndr_pull_flags_fn_t)ndr_pull_PAC_INFO);
2150 smb_krb5_free_data_contents(context, &device_logon_info);
2151 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2152 nt_status = ndr_map_error2ntstatus(ndr_err);
2153 DBG_ERR("can't parse device PAC LOGON_INFO: %s\n",
2154 nt_errstr(nt_status));
2155 talloc_free(frame);
2156 return map_errno_from_nt_status(nt_status);
2160 * When creating the device info structure, existing resource groups are
2161 * discarded.
2163 code = samba_kdc_make_device_info(frame,
2164 &logon_info.logon_info.info->info3,
2165 NULL, /* resource_groups */
2166 &info);
2167 if (code != 0) {
2168 talloc_free(frame);
2169 return code;
2172 code = samba_kdc_update_device_info(frame,
2173 samdb,
2174 &logon_info,
2175 info.device_info.info);
2176 if (code != 0) {
2177 talloc_free(frame);
2178 return code;
2181 code = samba_kdc_get_device_info_pac_blob(mem_ctx,
2182 &info,
2183 device_info_blob);
2185 talloc_free(frame);
2186 return code;
2189 static krb5_error_code samba_kdc_get_device_info_blob(TALLOC_CTX *mem_ctx,
2190 krb5_context context,
2191 struct ldb_context *samdb,
2192 const struct samba_kdc_entry_pac device,
2193 DATA_BLOB **device_info_blob)
2195 TALLOC_CTX *frame = NULL;
2196 krb5_error_code code = EINVAL;
2197 NTSTATUS nt_status;
2199 const struct auth_user_info_dc *device_info = NULL;
2200 struct netr_SamInfo3 *info3 = NULL;
2201 struct PAC_DOMAIN_GROUP_MEMBERSHIP *resource_groups = NULL;
2203 union PAC_INFO info;
2205 frame = talloc_stackframe();
2207 code = samba_kdc_get_user_info_dc(frame,
2208 context,
2209 samdb,
2210 device,
2211 &device_info,
2212 NULL /* resource_groups_out */);
2213 if (code) {
2214 const char *krb5_err = krb5_get_error_message(context, code);
2215 DBG_ERR("samba_kdc_get_user_info_dc failed: %s\n",
2216 krb5_err != NULL ? krb5_err : "<unknown>");
2217 krb5_free_error_message(context, krb5_err);
2219 talloc_free(frame);
2220 return KRB5KDC_ERR_TGT_REVOKED;
2223 nt_status = auth_convert_user_info_dc_saminfo3(frame, device_info,
2224 AUTH_INCLUDE_RESOURCE_GROUPS_COMPRESSED,
2225 &info3,
2226 &resource_groups);
2227 if (!NT_STATUS_IS_OK(nt_status)) {
2228 DBG_WARNING("Getting Samba info failed: %s\n",
2229 nt_errstr(nt_status));
2230 talloc_free(frame);
2231 return nt_status_to_krb5(nt_status);
2234 code = samba_kdc_make_device_info(frame,
2235 info3,
2236 resource_groups,
2237 &info);
2238 if (code != 0) {
2239 talloc_free(frame);
2240 return code;
2243 code = samba_kdc_get_device_info_pac_blob(mem_ctx,
2244 &info,
2245 device_info_blob);
2247 talloc_free(frame);
2248 return code;
2252 * @brief Verify a PAC
2254 * @param mem_ctx A talloc memory context
2256 * @param context A krb5 context
2258 * @param samdb An open samdb connection.
2260 * @param flags Bitwise OR'ed flags
2262 * @param client The client samba kdc PAC entry.
2264 * @param krbtgt The krbtgt samba kdc entry.
2266 * @return A Kerberos error code.
2268 krb5_error_code samba_kdc_verify_pac(TALLOC_CTX *mem_ctx,
2269 krb5_context context,
2270 struct ldb_context *samdb,
2271 uint32_t flags,
2272 const struct samba_kdc_entry_pac client,
2273 const struct samba_kdc_entry *krbtgt)
2275 TALLOC_CTX *tmp_ctx = NULL;
2276 struct pac_blobs *pac_blobs = NULL;
2277 krb5_error_code code = EINVAL;
2279 tmp_ctx = talloc_new(mem_ctx);
2280 if (tmp_ctx == NULL) {
2281 code = ENOMEM;
2282 goto done;
2285 if (client.entry != NULL) {
2287 * Check the objectSID of the client and pac data are the same.
2288 * Does a parse and SID check, but no crypto.
2290 code = samba_kdc_validate_pac_blob(context, client);
2291 if (code != 0) {
2292 goto done;
2296 if (!samba_krb5_pac_is_trusted(client)) {
2297 const struct auth_user_info_dc *user_info_dc = NULL;
2298 WERROR werr;
2300 struct dom_sid *object_sids = NULL;
2301 uint32_t j;
2303 if (client.entry == NULL) {
2304 code = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
2305 goto done;
2308 code = samba_kdc_get_user_info_from_db(tmp_ctx,
2309 samdb,
2310 client.entry,
2311 client.entry->msg,
2312 &user_info_dc);
2313 if (code) {
2314 const char *krb5_err = krb5_get_error_message(context, code);
2315 DBG_ERR("Getting user info for PAC failed: %s\n",
2316 krb5_err != NULL ? krb5_err : "<unknown>");
2317 krb5_free_error_message(context, krb5_err);
2319 code = KRB5KDC_ERR_TGT_REVOKED;
2320 goto done;
2324 * Check if the SID list in the user_info_dc intersects
2325 * correctly with the RODC allow/deny lists.
2327 object_sids = talloc_array(tmp_ctx, struct dom_sid, user_info_dc->num_sids);
2328 if (object_sids == NULL) {
2329 code = ENOMEM;
2330 goto done;
2333 for (j = 0; j < user_info_dc->num_sids; ++j) {
2334 object_sids[j] = user_info_dc->sids[j].sid;
2337 werr = samba_rodc_confirm_user_is_allowed(user_info_dc->num_sids,
2338 object_sids,
2339 krbtgt,
2340 client.entry);
2341 if (!W_ERROR_IS_OK(werr)) {
2342 code = KRB5KDC_ERR_TGT_REVOKED;
2343 if (W_ERROR_EQUAL(werr,
2344 WERR_DOMAIN_CONTROLLER_NOT_FOUND)) {
2345 code = KRB5KDC_ERR_POLICY;
2347 goto done;
2351 * The RODC PAC data isn't trusted for authorization as it may
2352 * be stale. The only thing meaningful we can do with an RODC
2353 * account on a full DC is exchange the RODC TGT for a 'real'
2354 * TGT.
2356 * So we match Windows (at least server 2022) and
2357 * don't allow S4U2Self.
2359 * https://lists.samba.org/archive/cifs-protocol/2022-April/003673.html
2361 if (flags & SAMBA_KDC_FLAG_PROTOCOL_TRANSITION) {
2362 code = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
2363 goto done;
2367 /* Check the types of the given PAC */
2369 code = pac_blobs_from_krb5_pac(tmp_ctx,
2370 context,
2371 client.pac,
2372 &pac_blobs);
2373 if (code != 0) {
2374 goto done;
2377 code = pac_blobs_ensure_exists(pac_blobs,
2378 PAC_TYPE_LOGON_INFO);
2379 if (code != 0) {
2380 goto done;
2383 code = pac_blobs_ensure_exists(pac_blobs,
2384 PAC_TYPE_LOGON_NAME);
2385 if (code != 0) {
2386 goto done;
2389 code = pac_blobs_ensure_exists(pac_blobs,
2390 PAC_TYPE_SRV_CHECKSUM);
2391 if (code != 0) {
2392 goto done;
2395 code = pac_blobs_ensure_exists(pac_blobs,
2396 PAC_TYPE_KDC_CHECKSUM);
2397 if (code != 0) {
2398 goto done;
2401 if (!(flags & SAMBA_KDC_FLAG_CONSTRAINED_DELEGATION)) {
2402 code = pac_blobs_ensure_exists(pac_blobs,
2403 PAC_TYPE_REQUESTER_SID);
2404 if (code != 0) {
2405 code = KRB5KDC_ERR_TGT_REVOKED;
2406 goto done;
2410 code = 0;
2412 done:
2413 talloc_free(tmp_ctx);
2415 return code;
2419 * @brief Update a PAC
2421 * @param mem_ctx A talloc memory context
2423 * @param context A krb5 context
2425 * @param samdb An open samdb connection.
2427 * @param lp_ctx A loadparm context.
2429 * @param flags Bitwise OR'ed flags
2431 * @param device_pac_is_trusted Whether the device's PAC was issued by a trusted server,
2432 * as opposed to an RODC.
2434 * @param client The client samba kdc PAC entry.
2436 * @param server_principal The server principal
2438 * @param server The server samba kdc entry.
2440 * @param delegated_proxy_principal The delegated proxy principal used for
2441 * updating the constrained delegation PAC
2442 * buffer.
2444 * @param delegated_proxy The delegated proxy kdc PAC entry.
2446 * @param device The computer's samba kdc PAC entry; used for compound
2447 * authentication.
2449 * @param new_pac The new already allocated PAC
2451 * @return A Kerberos error code. If no PAC should be returned, the code will be
2452 * ENOATTR!
2454 krb5_error_code samba_kdc_update_pac(TALLOC_CTX *mem_ctx,
2455 krb5_context context,
2456 struct ldb_context *samdb,
2457 struct loadparm_context *lp_ctx,
2458 uint32_t flags,
2459 const struct samba_kdc_entry_pac client,
2460 const krb5_const_principal server_principal,
2461 const struct samba_kdc_entry *server,
2462 const krb5_const_principal delegated_proxy_principal,
2463 const struct samba_kdc_entry_pac delegated_proxy,
2464 const struct samba_kdc_entry_pac device,
2465 krb5_pac new_pac,
2466 struct authn_audit_info **server_audit_info_out,
2467 NTSTATUS *status_out)
2469 TALLOC_CTX *tmp_ctx = NULL;
2470 krb5_error_code code = EINVAL;
2471 NTSTATUS nt_status;
2472 DATA_BLOB *pac_blob = NULL;
2473 DATA_BLOB *upn_blob = NULL;
2474 DATA_BLOB *deleg_blob = NULL;
2475 DATA_BLOB *requester_sid_blob = NULL;
2476 const DATA_BLOB *client_claims_blob = NULL;
2477 DATA_BLOB device_claims_blob = {};
2478 const DATA_BLOB *device_claims_blob_ptr = NULL;
2479 struct auth_claims auth_claims = {};
2480 DATA_BLOB *device_info_blob = NULL;
2481 bool is_tgs = false;
2482 bool server_restrictions_present = false;
2483 struct pac_blobs *pac_blobs = NULL;
2484 const struct auth_user_info_dc *user_info_dc_const = NULL;
2485 struct auth_user_info_dc *user_info_dc_shallow_copy = NULL;
2486 const struct PAC_DOMAIN_GROUP_MEMBERSHIP *_resource_groups = NULL;
2487 enum auth_group_inclusion group_inclusion;
2488 bool compounded_auth;
2489 size_t i = 0;
2491 if (server_audit_info_out != NULL) {
2492 *server_audit_info_out = NULL;
2495 if (status_out != NULL) {
2496 *status_out = NT_STATUS_OK;
2499 tmp_ctx = talloc_new(mem_ctx);
2500 if (tmp_ctx == NULL) {
2501 code = ENOMEM;
2502 goto done;
2506 int result = smb_krb5_principal_is_tgs(context, server_principal);
2507 if (result == -1) {
2508 code = ENOMEM;
2509 goto done;
2512 is_tgs = result;
2515 server_restrictions_present = !is_tgs && authn_policy_restrictions_present(server->server_policy);
2517 /* Only include resource groups in a service ticket. */
2518 if (is_tgs) {
2519 group_inclusion = AUTH_EXCLUDE_RESOURCE_GROUPS;
2520 } else if (server->supported_enctypes & KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED) {
2521 group_inclusion = AUTH_INCLUDE_RESOURCE_GROUPS;
2522 } else {
2523 group_inclusion = AUTH_INCLUDE_RESOURCE_GROUPS_COMPRESSED;
2526 compounded_auth = device.entry != NULL && !is_tgs
2527 && server->supported_enctypes & KERB_ENCTYPE_COMPOUND_IDENTITY_SUPPORTED;
2529 if (compounded_auth || (server_restrictions_present && device.entry != NULL)) {
2531 * [MS-KILE] 3.3.5.7.4 Compound Identity: the client claims from
2532 * the device PAC become the device claims in the new PAC.
2534 code = samba_kdc_get_claims_data(tmp_ctx,
2535 context,
2536 samdb,
2537 device,
2538 &auth_claims.device_claims);
2539 if (code) {
2540 goto done;
2543 if (compounded_auth) {
2544 nt_status = claims_data_encoded_claims_set(tmp_ctx,
2545 auth_claims.device_claims,
2546 &device_claims_blob);
2547 if (!NT_STATUS_IS_OK(nt_status)) {
2548 DBG_ERR("claims_data_encoded_claims_set failed: %s\n",
2549 nt_errstr(nt_status));
2550 code = map_errno_from_nt_status(nt_status);
2551 goto done;
2554 device_claims_blob_ptr = &device_claims_blob;
2556 if (samba_krb5_pac_is_trusted(device)) {
2557 code = samba_kdc_create_device_info_blob(tmp_ctx,
2558 context,
2559 samdb,
2560 device.pac,
2561 &device_info_blob);
2562 if (code != 0) {
2563 goto done;
2565 } else {
2566 /* Don't trust an RODC‐issued PAC; regenerate the device info. */
2567 code = samba_kdc_get_device_info_blob(tmp_ctx,
2568 context,
2569 samdb,
2570 device,
2571 &device_info_blob);
2572 if (code != 0) {
2573 goto done;
2579 if (delegated_proxy_principal != NULL) {
2580 deleg_blob = talloc_zero(tmp_ctx, DATA_BLOB);
2581 if (deleg_blob == NULL) {
2582 code = ENOMEM;
2583 goto done;
2586 nt_status = samba_kdc_update_delegation_info_blob(
2587 deleg_blob,
2588 context,
2589 client.pac,
2590 server_principal,
2591 delegated_proxy_principal,
2592 deleg_blob);
2593 if (!NT_STATUS_IS_OK(nt_status)) {
2594 DBG_ERR("update delegation info blob failed: %s\n",
2595 nt_errstr(nt_status));
2596 code = map_errno_from_nt_status(nt_status);
2597 goto done;
2602 * If we are creating a TGT, resource groups from our domain are not to
2603 * be put into the PAC. Instead, we take the resource groups directly
2604 * from the original PAC and copy them unmodified into the new one.
2606 code = samba_kdc_get_user_info_dc(tmp_ctx,
2607 context,
2608 samdb,
2609 client,
2610 &user_info_dc_const,
2611 is_tgs ? &_resource_groups : NULL);
2612 if (code != 0) {
2613 const char *err_str = krb5_get_error_message(context, code);
2614 DBG_ERR("samba_kdc_get_user_info_dc failed: %s\n",
2615 err_str != NULL ? err_str : "<unknown>");
2616 krb5_free_error_message(context, err_str);
2618 goto done;
2622 * Enforce the AllowedToAuthenticateTo part of an authentication policy,
2623 * if one is present.
2625 if (server_restrictions_present) {
2626 struct samba_kdc_entry_pac auth_entry;
2627 const struct auth_user_info_dc *auth_user_info_dc = NULL;
2628 const struct auth_user_info_dc *device_info = NULL;
2630 if (delegated_proxy.entry != NULL) {
2631 auth_entry = delegated_proxy;
2633 code = samba_kdc_get_user_info_dc(tmp_ctx,
2634 context,
2635 samdb,
2636 delegated_proxy,
2637 &auth_user_info_dc,
2638 NULL /* resource_groups_out */);
2639 if (code) {
2640 goto done;
2642 } else {
2643 auth_entry = client;
2644 auth_user_info_dc = user_info_dc_const;
2647 /* Fetch the user’s claims. */
2648 code = samba_kdc_get_claims_data(tmp_ctx,
2649 context,
2650 samdb,
2651 auth_entry,
2652 &auth_claims.user_claims);
2653 if (code) {
2654 goto done;
2657 if (device.entry != NULL) {
2658 code = samba_kdc_get_user_info_dc(tmp_ctx,
2659 context,
2660 samdb,
2661 device,
2662 &device_info,
2663 NULL /* resource_groups_out */);
2664 if (code) {
2665 goto done;
2670 * Allocate the audit info and output status on to the parent
2671 * mem_ctx, not the temporary context.
2673 code = samba_kdc_allowed_to_authenticate_to(mem_ctx,
2674 samdb,
2675 lp_ctx,
2676 auth_entry.entry,
2677 auth_user_info_dc,
2678 device_info,
2679 auth_claims,
2680 server,
2681 server_audit_info_out,
2682 status_out);
2683 if (code) {
2684 goto done;
2688 if (compounded_auth) {
2689 /* Make a shallow copy of the user_info_dc structure. */
2690 nt_status = authsam_shallow_copy_user_info_dc(tmp_ctx,
2691 user_info_dc_const,
2692 &user_info_dc_shallow_copy);
2693 user_info_dc_const = NULL;
2695 if (!NT_STATUS_IS_OK(nt_status)) {
2696 DBG_ERR("Failed to copy user_info_dc: %s\n",
2697 nt_errstr(nt_status));
2699 code = KRB5KDC_ERR_TGT_REVOKED;
2700 goto done;
2703 nt_status = samba_kdc_add_compounded_auth(user_info_dc_shallow_copy);
2704 if (!NT_STATUS_IS_OK(nt_status)) {
2705 DBG_ERR("Failed to add Compounded Authentication: %s\n",
2706 nt_errstr(nt_status));
2708 code = KRB5KDC_ERR_TGT_REVOKED;
2709 goto done;
2712 /* We can now set back to the const, it will not be modified */
2713 user_info_dc_const = user_info_dc_shallow_copy;
2716 if (samba_krb5_pac_is_trusted(client)) {
2717 pac_blob = talloc_zero(tmp_ctx, DATA_BLOB);
2718 if (pac_blob == NULL) {
2719 code = ENOMEM;
2720 goto done;
2723 nt_status = samba_get_logon_info_pac_blob(tmp_ctx,
2724 user_info_dc_const,
2725 _resource_groups,
2726 group_inclusion,
2727 pac_blob);
2728 if (!NT_STATUS_IS_OK(nt_status)) {
2729 DBG_ERR("samba_get_logon_info_pac_blob failed: %s\n",
2730 nt_errstr(nt_status));
2732 code = map_errno_from_nt_status(nt_status);
2733 goto done;
2737 * TODO: we need claim translation over trusts,
2738 * for now we just clear them...
2740 if (samba_kdc_entry_pac_issued_by_trust(client)) {
2741 client_claims_blob = &data_blob_null;
2743 } else {
2744 nt_status = samba_kdc_get_logon_info_blob(tmp_ctx,
2745 user_info_dc_const,
2746 group_inclusion,
2747 &pac_blob);
2748 if (!NT_STATUS_IS_OK(nt_status)) {
2749 DBG_ERR("samba_kdc_get_logon_info_blob failed: %s\n",
2750 nt_errstr(nt_status));
2751 code = KRB5KDC_ERR_TGT_REVOKED;
2752 goto done;
2755 nt_status = samba_kdc_get_upn_info_blob(tmp_ctx,
2756 user_info_dc_const,
2757 &upn_blob);
2758 if (!NT_STATUS_IS_OK(nt_status)) {
2759 DBG_ERR("samba_kdc_get_upn_info_blob failed: %s\n",
2760 nt_errstr(nt_status));
2761 code = KRB5KDC_ERR_TGT_REVOKED;
2762 goto done;
2765 if (is_tgs) {
2766 nt_status = samba_kdc_get_requester_sid_blob(tmp_ctx,
2767 user_info_dc_const,
2768 &requester_sid_blob);
2769 if (!NT_STATUS_IS_OK(nt_status)) {
2770 DBG_ERR("samba_kdc_get_requester_sid_blob failed: %s\n",
2771 nt_errstr(nt_status));
2772 code = KRB5KDC_ERR_TGT_REVOKED;
2773 goto done;
2777 /* Don't trust RODC-issued claims. Regenerate them. */
2778 nt_status = samba_kdc_get_claims_blob(tmp_ctx,
2779 client.entry,
2780 &client_claims_blob);
2781 if (!NT_STATUS_IS_OK(nt_status)) {
2782 DBG_ERR("samba_kdc_get_claims_blob failed: %s\n",
2783 nt_errstr(nt_status));
2784 code = map_errno_from_nt_status(nt_status);
2785 goto done;
2789 /* Check the types of the given PAC */
2790 code = pac_blobs_from_krb5_pac(tmp_ctx,
2791 context,
2792 client.pac,
2793 &pac_blobs);
2794 if (code != 0) {
2795 goto done;
2798 code = pac_blobs_replace_existing(pac_blobs,
2799 PAC_TYPE_LOGON_INFO,
2800 pac_blob);
2801 if (code != 0) {
2802 goto done;
2805 #ifdef SAMBA4_USES_HEIMDAL
2806 /* Not needed with MIT Kerberos */
2807 code = pac_blobs_replace_existing(pac_blobs,
2808 PAC_TYPE_LOGON_NAME,
2809 &data_blob_null);
2810 if (code != 0) {
2811 goto done;
2814 code = pac_blobs_replace_existing(pac_blobs,
2815 PAC_TYPE_SRV_CHECKSUM,
2816 &data_blob_null);
2817 if (code != 0) {
2818 goto done;
2821 code = pac_blobs_replace_existing(pac_blobs,
2822 PAC_TYPE_KDC_CHECKSUM,
2823 &data_blob_null);
2824 if (code != 0) {
2825 goto done;
2827 #endif
2829 code = pac_blobs_add_blob(pac_blobs,
2830 PAC_TYPE_CONSTRAINED_DELEGATION,
2831 deleg_blob);
2832 if (code != 0) {
2833 goto done;
2836 code = pac_blobs_add_blob(pac_blobs,
2837 PAC_TYPE_UPN_DNS_INFO,
2838 upn_blob);
2839 if (code != 0) {
2840 goto done;
2843 code = pac_blobs_add_blob(pac_blobs,
2844 PAC_TYPE_CLIENT_CLAIMS_INFO,
2845 client_claims_blob);
2846 if (code != 0) {
2847 goto done;
2850 code = pac_blobs_add_blob(pac_blobs,
2851 PAC_TYPE_DEVICE_INFO,
2852 device_info_blob);
2853 if (code != 0) {
2854 goto done;
2857 code = pac_blobs_add_blob(pac_blobs,
2858 PAC_TYPE_DEVICE_CLAIMS_INFO,
2859 device_claims_blob_ptr);
2860 if (code != 0) {
2861 goto done;
2864 if (!samba_krb5_pac_is_trusted(client) || !is_tgs) {
2865 pac_blobs_remove_blob(pac_blobs,
2866 PAC_TYPE_ATTRIBUTES_INFO);
2869 if (!is_tgs) {
2870 pac_blobs_remove_blob(pac_blobs,
2871 PAC_TYPE_REQUESTER_SID);
2874 code = pac_blobs_add_blob(pac_blobs,
2875 PAC_TYPE_REQUESTER_SID,
2876 requester_sid_blob);
2877 if (code != 0) {
2878 goto done;
2882 * The server account may be set not to want the PAC.
2884 * While this is wasteful if the above calculations were done
2885 * and now thrown away, this is cleaner as we do any ticket
2886 * signature checking etc always.
2888 * UF_NO_AUTH_DATA_REQUIRED is the rare case and most of the
2889 * time (eg not accepting a ticket from the RODC) we do not
2890 * need to re-generate anything anyway.
2892 if (!samba_princ_needs_pac(server)) {
2893 code = ENOATTR;
2894 goto done;
2897 if (samba_krb5_pac_is_trusted(client) && !is_tgs) {
2899 * The client may have requested no PAC when obtaining the
2900 * TGT.
2902 bool requested_pac = false;
2904 code = samba_client_requested_pac(context,
2905 client.pac,
2906 tmp_ctx,
2907 &requested_pac);
2908 if (code != 0 || !requested_pac) {
2909 if (!requested_pac) {
2910 code = ENOATTR;
2912 goto done;
2916 for (i = 0; i < pac_blobs->num_types; ++i) {
2917 krb5_data type_data;
2918 const DATA_BLOB *type_blob = pac_blobs->type_blobs[i].data;
2919 uint32_t type = pac_blobs->type_blobs[i].type;
2921 static char null_byte = '\0';
2922 const krb5_data null_data = smb_krb5_make_data(&null_byte, 0);
2924 #ifndef SAMBA4_USES_HEIMDAL
2925 /* Not needed with MIT Kerberos */
2926 switch(type) {
2927 case PAC_TYPE_LOGON_NAME:
2928 case PAC_TYPE_SRV_CHECKSUM:
2929 case PAC_TYPE_KDC_CHECKSUM:
2930 case PAC_TYPE_FULL_CHECKSUM:
2931 continue;
2932 default:
2933 break;
2935 #endif
2937 if (type_blob != NULL) {
2938 type_data = smb_krb5_data_from_blob(*type_blob);
2940 * Passing a NULL pointer into krb5_pac_add_buffer() is
2941 * not allowed, so pass null_data instead if needed.
2943 code = krb5_pac_add_buffer(context,
2944 new_pac,
2945 type,
2946 (type_data.data != NULL) ? &type_data : &null_data);
2947 if (code != 0) {
2948 goto done;
2950 } else if (samba_krb5_pac_is_trusted(client)) {
2952 * Convey the buffer from the original PAC if we can
2953 * trust it.
2956 code = krb5_pac_get_buffer(context,
2957 client.pac,
2958 type,
2959 &type_data);
2960 if (code != 0) {
2961 goto done;
2964 * Passing a NULL pointer into krb5_pac_add_buffer() is
2965 * not allowed, so pass null_data instead if needed.
2967 code = krb5_pac_add_buffer(context,
2968 new_pac,
2969 type,
2970 (type_data.data != NULL) ? &type_data : &null_data);
2971 smb_krb5_free_data_contents(context, &type_data);
2972 if (code != 0) {
2973 goto done;
2978 code = 0;
2979 done:
2980 TALLOC_FREE(tmp_ctx);
2981 return code;
2984 krb5_error_code samba_kdc_get_claims_data(TALLOC_CTX *mem_ctx,
2985 krb5_context context,
2986 struct ldb_context *samdb,
2987 struct samba_kdc_entry_pac entry,
2988 struct claims_data **claims_data_out)
2990 if (samba_kdc_entry_pac_issued_by_trust(entry)) {
2991 NTSTATUS status;
2994 * TODO: we need claim translation over trusts; for now we just
2995 * clear them…
2997 status = claims_data_from_encoded_claims_set(mem_ctx,
2998 NULL,
2999 claims_data_out);
3000 if (!NT_STATUS_IS_OK(status)) {
3001 return map_errno_from_nt_status(status);
3004 return 0;
3007 if (samba_krb5_pac_is_trusted(entry)) {
3008 return samba_kdc_get_claims_data_from_pac(mem_ctx,
3009 context,
3010 entry,
3011 claims_data_out);
3014 return samba_kdc_get_claims_data_from_db(samdb,
3015 entry.entry,
3016 claims_data_out);
3019 krb5_error_code samba_kdc_get_claims_data_from_pac(TALLOC_CTX *mem_ctx,
3020 krb5_context context,
3021 struct samba_kdc_entry_pac entry,
3022 struct claims_data **claims_data_out)
3024 TALLOC_CTX *frame = NULL;
3025 krb5_data claims_info = {};
3026 struct claims_data *claims_data = NULL;
3027 NTSTATUS status = NT_STATUS_OK;
3028 krb5_error_code code;
3030 if (!samba_krb5_pac_is_trusted(entry)) {
3031 code = EINVAL;
3032 goto out;
3035 if (samba_kdc_entry_pac_issued_by_trust(entry)) {
3036 code = EINVAL;
3037 goto out;
3040 if (claims_data_out == NULL) {
3041 code = EINVAL;
3042 goto out;
3045 *claims_data_out = NULL;
3047 if (entry.entry != NULL && entry.entry->claims_from_pac_are_initialized) {
3048 /* Note: the caller does not own this! */
3049 *claims_data_out = entry.entry->claims_from_pac;
3050 return 0;
3053 frame = talloc_stackframe();
3055 /* Fetch the claims from the PAC. */
3056 code = krb5_pac_get_buffer(context, entry.pac,
3057 PAC_TYPE_CLIENT_CLAIMS_INFO,
3058 &claims_info);
3059 if (code == ENOENT) {
3060 /* OK. */
3061 } else if (code != 0) {
3062 DBG_ERR("Error getting CLIENT_CLAIMS_INFO from PAC\n");
3063 goto out;
3064 } else if (claims_info.length) {
3065 DATA_BLOB claims_blob = data_blob_const(claims_info.data,
3066 claims_info.length);
3068 status = claims_data_from_encoded_claims_set(frame,
3069 &claims_blob,
3070 &claims_data);
3071 if (!NT_STATUS_IS_OK(status)) {
3072 code = map_errno_from_nt_status(status);
3073 goto out;
3077 if (entry.entry != NULL) {
3078 /* Note: the caller does not own this! */
3079 entry.entry->claims_from_pac = talloc_steal(entry.entry,
3080 claims_data);
3081 entry.entry->claims_from_pac_are_initialized = true;
3082 } else {
3083 talloc_steal(mem_ctx, claims_data);
3086 *claims_data_out = claims_data;
3088 out:
3089 smb_krb5_free_data_contents(context, &claims_info);
3090 talloc_free(frame);
3091 return code;
3094 krb5_error_code samba_kdc_get_claims_data_from_db(struct ldb_context *samdb,
3095 struct samba_kdc_entry *entry,
3096 struct claims_data **claims_data_out)
3098 TALLOC_CTX *frame = NULL;
3100 struct claims_data *claims_data = NULL;
3101 struct CLAIMS_SET *claims_set = NULL;
3102 NTSTATUS status = NT_STATUS_OK;
3103 krb5_error_code code;
3105 if (samdb == NULL) {
3106 code = EINVAL;
3107 goto out;
3110 if (claims_data_out == NULL) {
3111 code = EINVAL;
3112 goto out;
3115 if (entry == NULL) {
3116 code = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
3117 goto out;
3120 *claims_data_out = NULL;
3122 if (entry->claims_from_db_are_initialized) {
3123 /* Note: the caller does not own this! */
3124 *claims_data_out = entry->claims_from_db;
3125 return 0;
3128 frame = talloc_stackframe();
3130 code = get_claims_set_for_principal(samdb,
3131 frame,
3132 entry->msg,
3133 &claims_set);
3134 if (code) {
3135 DBG_ERR("Failed to fetch claims\n");
3136 goto out;
3139 if (claims_set != NULL) {
3140 status = claims_data_from_claims_set(claims_data,
3141 claims_set,
3142 &claims_data);
3143 if (!NT_STATUS_IS_OK(status)) {
3144 code = map_errno_from_nt_status(status);
3145 goto out;
3149 entry->claims_from_db = talloc_steal(entry,
3150 claims_data);
3151 entry->claims_from_db_are_initialized = true;
3153 /* Note: the caller does not own this! */
3154 *claims_data_out = entry->claims_from_db;
3156 out:
3157 talloc_free(frame);
3158 return code;
3161 krb5_error_code samba_kdc_check_device(TALLOC_CTX *mem_ctx,
3162 krb5_context context,
3163 struct ldb_context *samdb,
3164 struct loadparm_context *lp_ctx,
3165 const struct samba_kdc_entry_pac device,
3166 const struct authn_kerberos_client_policy *client_policy,
3167 struct authn_audit_info **client_audit_info_out,
3168 NTSTATUS *status_out)
3170 TALLOC_CTX *frame = NULL;
3171 krb5_error_code code = 0;
3172 NTSTATUS nt_status;
3173 const struct auth_user_info_dc *device_info = NULL;
3174 struct authn_audit_info *client_audit_info = NULL;
3175 struct auth_claims auth_claims = {};
3177 if (status_out != NULL) {
3178 *status_out = NT_STATUS_OK;
3181 if (!authn_policy_device_restrictions_present(client_policy)) {
3182 return 0;
3185 if (device.entry == NULL || device.pac == NULL) {
3186 NTSTATUS out_status = NT_STATUS_INVALID_WORKSTATION;
3188 nt_status = authn_kerberos_client_policy_audit_info(mem_ctx,
3189 client_policy,
3190 NULL /* client_info */,
3191 AUTHN_AUDIT_EVENT_KERBEROS_DEVICE_RESTRICTION,
3192 AUTHN_AUDIT_REASON_FAST_REQUIRED,
3193 out_status,
3194 client_audit_info_out);
3195 if (!NT_STATUS_IS_OK(nt_status)) {
3196 code = KRB5KRB_ERR_GENERIC;
3197 } else if (authn_kerberos_client_policy_is_enforced(client_policy)) {
3198 code = KRB5KDC_ERR_POLICY;
3200 if (status_out != NULL) {
3201 *status_out = out_status;
3203 } else {
3204 /* OK. */
3205 code = 0;
3208 goto out;
3211 frame = talloc_stackframe();
3213 code = samba_kdc_get_user_info_dc(frame,
3214 context,
3215 samdb,
3216 device,
3217 &device_info,
3218 NULL);
3219 if (code) {
3220 goto out;
3224 * The device claims become the *user* claims for the purpose of
3225 * evaluating a conditional ACE expression.
3227 code = samba_kdc_get_claims_data(frame,
3228 context,
3229 samdb,
3230 device,
3231 &auth_claims.user_claims);
3232 if (code) {
3233 goto out;
3236 nt_status = authn_policy_authenticate_from_device(frame,
3237 samdb,
3238 lp_ctx,
3239 device_info,
3240 auth_claims,
3241 client_policy,
3242 &client_audit_info);
3243 if (client_audit_info != NULL) {
3244 *client_audit_info_out = talloc_move(mem_ctx, &client_audit_info);
3246 if (!NT_STATUS_IS_OK(nt_status)) {
3247 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_AUTHENTICATION_FIREWALL_FAILED)) {
3248 code = KRB5KDC_ERR_POLICY;
3249 } else {
3250 code = KRB5KRB_ERR_GENERIC;
3253 goto out;
3256 out:
3257 talloc_free(frame);
3258 return code;