pylibsmb: clang-format for the calls to Py_BuildValue()
[Samba.git] / source4 / kdc / pac-glue.c
blob58f089f4562829b05e1c732d32d6aa31fb8ee1ea
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 MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT |
1563 MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT,
1564 kdc_entry->realm_dn, kdc_entry->msg,
1565 workstation, client_name,
1566 true, password_change);
1568 kdc_entry->reject_status = nt_status;
1569 talloc_free(tmp_ctx);
1570 return nt_status;
1573 static krb5_error_code samba_get_requester_sid(TALLOC_CTX *mem_ctx,
1574 krb5_const_pac pac,
1575 krb5_context context,
1576 struct dom_sid *sid)
1578 NTSTATUS nt_status;
1579 enum ndr_err_code ndr_err;
1580 krb5_error_code ret = 0;
1582 DATA_BLOB pac_requester_sid_in;
1583 krb5_data k5pac_requester_sid_in;
1585 union PAC_INFO info;
1587 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
1588 if (tmp_ctx == NULL) {
1589 ret = ENOMEM;
1590 goto out;
1593 ret = krb5_pac_get_buffer(context, pac, PAC_TYPE_REQUESTER_SID,
1594 &k5pac_requester_sid_in);
1595 if (ret != 0) {
1596 goto out;
1599 pac_requester_sid_in = data_blob_const(k5pac_requester_sid_in.data,
1600 k5pac_requester_sid_in.length);
1602 ndr_err = ndr_pull_union_blob(&pac_requester_sid_in, tmp_ctx, &info,
1603 PAC_TYPE_REQUESTER_SID,
1604 (ndr_pull_flags_fn_t)ndr_pull_PAC_INFO);
1605 smb_krb5_free_data_contents(context, &k5pac_requester_sid_in);
1606 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1607 nt_status = ndr_map_error2ntstatus(ndr_err);
1608 DBG_ERR("can't parse the PAC REQUESTER_SID: %s\n", nt_errstr(nt_status));
1609 ret = map_errno_from_nt_status(nt_status);
1610 goto out;
1613 *sid = info.requester_sid.sid;
1615 out:
1616 talloc_free(tmp_ctx);
1617 return ret;
1620 /* Does a parse and SID check, but no crypto. */
1621 static krb5_error_code samba_kdc_validate_pac_blob(
1622 krb5_context context,
1623 const struct samba_kdc_entry_pac client)
1625 TALLOC_CTX *frame = talloc_stackframe();
1626 struct auth_user_info_dc *pac_user_info = NULL;
1627 struct dom_sid client_sid;
1628 struct dom_sid pac_sid;
1629 krb5_error_code code;
1630 bool ok;
1633 * First, try to get the SID from the requester SID buffer in the PAC.
1635 code = samba_get_requester_sid(frame, client.pac, context, &pac_sid);
1637 if (code == ENOENT) {
1639 * If the requester SID buffer isn't present, fall back to the
1640 * SID in the LOGON_INFO PAC buffer.
1642 code = kerberos_pac_to_user_info_dc(frame,
1643 client.pac,
1644 context,
1645 &pac_user_info,
1646 AUTH_EXCLUDE_RESOURCE_GROUPS,
1647 NULL,
1648 NULL,
1649 NULL);
1650 if (code != 0) {
1651 goto out;
1654 if (pac_user_info->num_sids == 0) {
1655 code = EINVAL;
1656 goto out;
1659 pac_sid = pac_user_info->sids[PRIMARY_USER_SID_INDEX].sid;
1660 } else if (code != 0) {
1661 goto out;
1664 code = samdb_result_dom_sid_buf(client.entry->msg,
1665 "objectSid",
1666 &client_sid);
1667 if (code) {
1668 goto out;
1671 ok = dom_sid_equal(&pac_sid, &client_sid);
1672 if (!ok) {
1673 struct dom_sid_buf buf1;
1674 struct dom_sid_buf buf2;
1676 DBG_ERR("SID mismatch between PAC and looked up client: "
1677 "PAC[%s] != CLI[%s]\n",
1678 dom_sid_str_buf(&pac_sid, &buf1),
1679 dom_sid_str_buf(&client_sid, &buf2));
1680 code = KRB5KDC_ERR_TGT_REVOKED;
1681 goto out;
1684 code = 0;
1685 out:
1686 TALLOC_FREE(frame);
1687 return code;
1692 * In the RODC case, to confirm that the returned user is permitted to
1693 * be replicated to the KDC (krbgtgt_xxx user) represented by *rodc
1695 static WERROR samba_rodc_confirm_user_is_allowed(uint32_t num_object_sids,
1696 const struct dom_sid *object_sids,
1697 const struct samba_kdc_entry *rodc,
1698 const struct samba_kdc_entry *object)
1700 int ret;
1701 WERROR werr;
1702 TALLOC_CTX *frame = talloc_stackframe();
1703 const char *rodc_attrs[] = { "msDS-KrbTgtLink",
1704 "msDS-NeverRevealGroup",
1705 "msDS-RevealOnDemandGroup",
1706 "userAccountControl",
1707 "objectSid",
1708 NULL };
1709 struct ldb_result *rodc_machine_account = NULL;
1710 struct ldb_dn *rodc_machine_account_dn = samdb_result_dn(rodc->kdc_db_ctx->samdb,
1711 frame,
1712 rodc->msg,
1713 "msDS-KrbTgtLinkBL",
1714 NULL);
1715 const struct dom_sid *rodc_machine_account_sid = NULL;
1717 if (rodc_machine_account_dn == NULL) {
1718 DBG_ERR("krbtgt account %s has no msDS-KrbTgtLinkBL to find RODC machine account for allow/deny list\n",
1719 ldb_dn_get_linearized(rodc->msg->dn));
1720 TALLOC_FREE(frame);
1721 return WERR_DOMAIN_CONTROLLER_NOT_FOUND;
1725 * Follow the link and get the RODC account (the krbtgt
1726 * account is the krbtgt_XXX account, but the
1727 * msDS-NeverRevealGroup and msDS-RevealOnDemandGroup is on
1728 * the RODC$ account)
1730 * We need DSDB_SEARCH_SHOW_EXTENDED_DN as we get a SID lists
1731 * out of the extended DNs
1734 ret = dsdb_search_dn(rodc->kdc_db_ctx->samdb,
1735 frame,
1736 &rodc_machine_account,
1737 rodc_machine_account_dn,
1738 rodc_attrs,
1739 DSDB_SEARCH_SHOW_EXTENDED_DN);
1740 if (ret != LDB_SUCCESS) {
1741 DBG_ERR("Failed to fetch RODC machine account %s pointed to by %s to check allow/deny list: %s\n",
1742 ldb_dn_get_linearized(rodc_machine_account_dn),
1743 ldb_dn_get_linearized(rodc->msg->dn),
1744 ldb_errstring(rodc->kdc_db_ctx->samdb));
1745 TALLOC_FREE(frame);
1746 return WERR_DOMAIN_CONTROLLER_NOT_FOUND;
1749 if (rodc_machine_account->count != 1) {
1750 DBG_ERR("Failed to fetch RODC machine account %s pointed to by %s to check allow/deny list: (%d)\n",
1751 ldb_dn_get_linearized(rodc_machine_account_dn),
1752 ldb_dn_get_linearized(rodc->msg->dn),
1753 rodc_machine_account->count);
1754 TALLOC_FREE(frame);
1755 return WERR_DS_DRA_BAD_DN;
1758 /* if the object SID is equal to the user_sid, allow */
1759 rodc_machine_account_sid = samdb_result_dom_sid(frame,
1760 rodc_machine_account->msgs[0],
1761 "objectSid");
1762 if (rodc_machine_account_sid == NULL) {
1763 TALLOC_FREE(frame);
1764 return WERR_DS_DRA_BAD_DN;
1767 werr = samdb_confirm_rodc_allowed_to_repl_to_sid_list(rodc->kdc_db_ctx->samdb,
1768 rodc_machine_account_sid,
1769 rodc_machine_account->msgs[0],
1770 object->msg,
1771 num_object_sids,
1772 object_sids);
1774 TALLOC_FREE(frame);
1775 return werr;
1779 * Perform an access check for the client attempting to authenticate to the
1780 * server. ‘client_info’ must be talloc-allocated so that we can make a
1781 * reference to it.
1783 krb5_error_code samba_kdc_allowed_to_authenticate_to(TALLOC_CTX *mem_ctx,
1784 struct ldb_context *samdb,
1785 struct loadparm_context *lp_ctx,
1786 const struct samba_kdc_entry *client,
1787 const struct auth_user_info_dc *client_info,
1788 const struct auth_user_info_dc *device_info,
1789 const struct auth_claims auth_claims,
1790 const struct samba_kdc_entry *server,
1791 struct authn_audit_info **server_audit_info_out,
1792 NTSTATUS *status_out)
1794 krb5_error_code ret = 0;
1795 NTSTATUS status;
1796 _UNUSED_ NTSTATUS _status;
1797 struct dom_sid server_sid = {};
1798 const struct authn_server_policy *server_policy = server->server_policy;
1800 if (status_out != NULL) {
1801 *status_out = NT_STATUS_OK;
1804 ret = samdb_result_dom_sid_buf(server->msg, "objectSid", &server_sid);
1805 if (ret) {
1807 * Ignore the return status — we are already in an error path,
1808 * and overwriting the real error code with the audit info
1809 * status is unhelpful.
1811 _status = authn_server_policy_audit_info(mem_ctx,
1812 server_policy,
1813 client_info,
1814 AUTHN_AUDIT_EVENT_OTHER_ERROR,
1815 AUTHN_AUDIT_REASON_NONE,
1816 dsdb_ldb_err_to_ntstatus(ret),
1817 server_audit_info_out);
1818 goto out;
1821 if (dom_sid_equal(&client_info->sids[PRIMARY_USER_SID_INDEX].sid, &server_sid)) {
1822 /* Authenticating to ourselves is always allowed. */
1823 status = authn_server_policy_audit_info(mem_ctx,
1824 server_policy,
1825 client_info,
1826 AUTHN_AUDIT_EVENT_OK,
1827 AUTHN_AUDIT_REASON_NONE,
1828 NT_STATUS_OK,
1829 server_audit_info_out);
1830 if (!NT_STATUS_IS_OK(status)) {
1831 ret = KRB5KRB_ERR_GENERIC;
1833 goto out;
1836 status = authn_policy_authenticate_to_service(mem_ctx,
1837 samdb,
1838 lp_ctx,
1839 AUTHN_POLICY_AUTH_TYPE_KERBEROS,
1840 client_info,
1841 device_info,
1842 auth_claims,
1843 server_policy,
1844 (struct authn_policy_flags) { .force_compounded_authentication = true },
1845 server_audit_info_out);
1846 if (!NT_STATUS_IS_OK(status)) {
1847 if (status_out != NULL) {
1848 *status_out = status;
1850 if (NT_STATUS_EQUAL(status, NT_STATUS_AUTHENTICATION_FIREWALL_FAILED)) {
1851 ret = KRB5KDC_ERR_POLICY;
1852 } else if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
1853 ret = KRB5KDC_ERR_POLICY;
1854 } else {
1855 ret = KRB5KRB_ERR_GENERIC;
1859 out:
1860 return ret;
1863 static krb5_error_code samba_kdc_add_domain_group_sid(struct PAC_DEVICE_INFO *info,
1864 const struct netr_SidAttr *sid)
1866 uint32_t i;
1867 uint32_t rid;
1868 NTSTATUS status;
1870 uint32_t domain_group_count = info->domain_group_count;
1871 struct PAC_DOMAIN_GROUP_MEMBERSHIP *domain_group = NULL;
1872 struct samr_RidWithAttribute *rids = NULL;
1874 for (i = 0; i < domain_group_count; ++i) {
1875 struct PAC_DOMAIN_GROUP_MEMBERSHIP *this_domain_group
1876 = &info->domain_groups[i];
1878 if (dom_sid_in_domain(this_domain_group->domain_sid, sid->sid)) {
1879 domain_group = this_domain_group;
1880 break;
1884 if (domain_group == NULL) {
1885 struct PAC_DOMAIN_GROUP_MEMBERSHIP *domain_groups = NULL;
1887 if (domain_group_count == UINT32_MAX) {
1888 return EINVAL;
1891 domain_groups = talloc_realloc(
1892 info,
1893 info->domain_groups,
1894 struct PAC_DOMAIN_GROUP_MEMBERSHIP,
1895 domain_group_count + 1);
1896 if (domain_groups == NULL) {
1897 return ENOMEM;
1900 info->domain_groups = domain_groups;
1902 domain_group = &info->domain_groups[domain_group_count++];
1903 *domain_group = (struct PAC_DOMAIN_GROUP_MEMBERSHIP) {};
1905 status = dom_sid_split_rid(info->domain_groups,
1906 sid->sid,
1907 &domain_group->domain_sid,
1908 &rid);
1909 if (!NT_STATUS_IS_OK(status)) {
1910 return map_errno_from_nt_status(status);
1912 } else {
1913 status = dom_sid_split_rid(NULL,
1914 sid->sid,
1915 NULL,
1916 &rid);
1917 if (!NT_STATUS_IS_OK(status)) {
1918 return map_errno_from_nt_status(status);
1922 if (domain_group->groups.count == UINT32_MAX) {
1923 return EINVAL;
1926 rids = talloc_realloc(info->domain_groups,
1927 domain_group->groups.rids,
1928 struct samr_RidWithAttribute,
1929 domain_group->groups.count + 1);
1930 if (rids == NULL) {
1931 return ENOMEM;
1934 domain_group->groups.rids = rids;
1936 domain_group->groups.rids[domain_group->groups.count] = (struct samr_RidWithAttribute) {
1937 .rid = rid,
1938 .attributes = sid->attributes,
1941 ++domain_group->groups.count;
1943 info->domain_group_count = domain_group_count;
1945 return 0;
1948 static krb5_error_code samba_kdc_make_device_info(TALLOC_CTX *mem_ctx,
1949 const struct netr_SamInfo3 *info3,
1950 struct PAC_DOMAIN_GROUP_MEMBERSHIP *resource_groups,
1951 union PAC_INFO *info)
1953 TALLOC_CTX *tmp_ctx = NULL;
1954 struct PAC_DEVICE_INFO *device_info = NULL;
1955 uint32_t i;
1956 krb5_error_code ret = 0;
1958 *info = (union PAC_INFO) {};
1960 info->device_info.info = NULL;
1962 tmp_ctx = talloc_new(mem_ctx);
1963 if (tmp_ctx == NULL) {
1964 return ENOMEM;
1967 device_info = talloc(tmp_ctx, struct PAC_DEVICE_INFO);
1968 if (device_info == NULL) {
1969 ret = ENOMEM;
1970 goto out;
1973 device_info->rid = info3->base.rid;
1974 device_info->primary_gid = info3->base.primary_gid;
1975 device_info->domain_sid = info3->base.domain_sid;
1976 device_info->groups = info3->base.groups;
1978 device_info->sid_count = 0;
1979 device_info->sids = NULL;
1981 if (resource_groups != NULL) {
1983 * The account's resource groups all belong to the same domain,
1984 * so we can add them all in one go.
1986 device_info->domain_group_count = 1;
1987 device_info->domain_groups = talloc_move(device_info, &resource_groups);
1988 } else {
1989 device_info->domain_group_count = 0;
1990 device_info->domain_groups = NULL;
1993 for (i = 0; i < info3->sidcount; ++i) {
1994 const struct netr_SidAttr *device_sid = &info3->sids[i];
1996 if (dom_sid_has_account_domain(device_sid->sid)) {
1997 ret = samba_kdc_add_domain_group_sid(device_info, device_sid);
1998 if (ret != 0) {
1999 goto out;
2001 } else {
2002 device_info->sids = talloc_realloc(device_info, device_info->sids,
2003 struct netr_SidAttr,
2004 device_info->sid_count + 1);
2005 if (device_info->sids == NULL) {
2006 ret = ENOMEM;
2007 goto out;
2010 device_info->sids[device_info->sid_count].sid = dom_sid_dup(device_info->sids, device_sid->sid);
2011 if (device_info->sids[device_info->sid_count].sid == NULL) {
2012 ret = ENOMEM;
2013 goto out;
2016 device_info->sids[device_info->sid_count].attributes = device_sid->attributes;
2018 ++device_info->sid_count;
2022 info->device_info.info = talloc_steal(mem_ctx, device_info);
2024 out:
2025 talloc_free(tmp_ctx);
2026 return ret;
2029 static krb5_error_code samba_kdc_update_device_info(TALLOC_CTX *mem_ctx,
2030 struct ldb_context *samdb,
2031 const union PAC_INFO *logon_info,
2032 struct PAC_DEVICE_INFO *device_info)
2034 NTSTATUS nt_status;
2035 struct auth_user_info_dc *device_info_dc = NULL;
2036 union netr_Validation validation;
2037 uint32_t i;
2038 uint32_t num_existing_sids;
2041 * This does a bit of unnecessary work, setting up fields we don't care
2042 * about -- we only want the SIDs.
2044 validation.sam3 = &logon_info->logon_info.info->info3;
2045 nt_status = make_user_info_dc_netlogon_validation(mem_ctx, "", 3, &validation,
2046 true, /* This user was authenticated */
2047 &device_info_dc);
2048 if (!NT_STATUS_IS_OK(nt_status)) {
2049 return map_errno_from_nt_status(nt_status);
2052 num_existing_sids = device_info_dc->num_sids;
2055 * We need to expand group memberships within our local domain,
2056 * as the token might be generated by a trusted domain.
2058 nt_status = authsam_update_user_info_dc(mem_ctx,
2059 samdb,
2060 device_info_dc);
2061 if (!NT_STATUS_IS_OK(nt_status)) {
2062 return map_errno_from_nt_status(nt_status);
2065 for (i = num_existing_sids; i < device_info_dc->num_sids; ++i) {
2066 struct auth_SidAttr *device_sid = &device_info_dc->sids[i];
2067 const struct netr_SidAttr sid = (struct netr_SidAttr) {
2068 .sid = &device_sid->sid,
2069 .attributes = device_sid->attrs,
2072 krb5_error_code ret = samba_kdc_add_domain_group_sid(device_info, &sid);
2073 if (ret != 0) {
2074 return ret;
2078 return 0;
2081 static krb5_error_code samba_kdc_get_device_info_pac_blob(TALLOC_CTX *mem_ctx,
2082 union PAC_INFO *info,
2083 DATA_BLOB **_device_info_blob)
2085 DATA_BLOB *device_info_blob = NULL;
2086 enum ndr_err_code ndr_err;
2088 *_device_info_blob = NULL;
2090 device_info_blob = talloc_zero(mem_ctx, DATA_BLOB);
2091 if (device_info_blob == NULL) {
2092 DBG_ERR("Out of memory\n");
2093 return ENOMEM;
2096 ndr_err = ndr_push_union_blob(device_info_blob, device_info_blob,
2097 info, PAC_TYPE_DEVICE_INFO,
2098 (ndr_push_flags_fn_t)ndr_push_PAC_INFO);
2099 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2100 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
2101 DBG_WARNING("PAC_DEVICE_INFO (presig) push failed: %s\n",
2102 nt_errstr(nt_status));
2103 talloc_free(device_info_blob);
2104 return map_errno_from_nt_status(nt_status);
2107 *_device_info_blob = device_info_blob;
2109 return 0;
2112 static krb5_error_code samba_kdc_create_device_info_blob(TALLOC_CTX *mem_ctx,
2113 krb5_context context,
2114 struct ldb_context *samdb,
2115 const krb5_const_pac device_pac,
2116 DATA_BLOB **device_info_blob)
2118 TALLOC_CTX *frame = NULL;
2119 krb5_data device_logon_info;
2120 krb5_error_code code = EINVAL;
2121 NTSTATUS nt_status;
2123 union PAC_INFO info;
2124 enum ndr_err_code ndr_err;
2125 DATA_BLOB device_logon_info_blob;
2127 union PAC_INFO logon_info;
2129 code = krb5_pac_get_buffer(context, device_pac,
2130 PAC_TYPE_LOGON_INFO,
2131 &device_logon_info);
2132 if (code != 0) {
2133 if (code == ENOENT) {
2134 DBG_ERR("Device PAC is missing LOGON_INFO\n");
2135 } else {
2136 DBG_ERR("Error getting LOGON_INFO from device PAC\n");
2138 return code;
2141 frame = talloc_stackframe();
2143 device_logon_info_blob = data_blob_const(device_logon_info.data,
2144 device_logon_info.length);
2146 ndr_err = ndr_pull_union_blob(&device_logon_info_blob, frame, &logon_info,
2147 PAC_TYPE_LOGON_INFO,
2148 (ndr_pull_flags_fn_t)ndr_pull_PAC_INFO);
2149 smb_krb5_free_data_contents(context, &device_logon_info);
2150 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2151 nt_status = ndr_map_error2ntstatus(ndr_err);
2152 DBG_ERR("can't parse device PAC LOGON_INFO: %s\n",
2153 nt_errstr(nt_status));
2154 talloc_free(frame);
2155 return map_errno_from_nt_status(nt_status);
2159 * When creating the device info structure, existing resource groups are
2160 * discarded.
2162 code = samba_kdc_make_device_info(frame,
2163 &logon_info.logon_info.info->info3,
2164 NULL, /* resource_groups */
2165 &info);
2166 if (code != 0) {
2167 talloc_free(frame);
2168 return code;
2171 code = samba_kdc_update_device_info(frame,
2172 samdb,
2173 &logon_info,
2174 info.device_info.info);
2175 if (code != 0) {
2176 talloc_free(frame);
2177 return code;
2180 code = samba_kdc_get_device_info_pac_blob(mem_ctx,
2181 &info,
2182 device_info_blob);
2184 talloc_free(frame);
2185 return code;
2188 static krb5_error_code samba_kdc_get_device_info_blob(TALLOC_CTX *mem_ctx,
2189 krb5_context context,
2190 struct ldb_context *samdb,
2191 const struct samba_kdc_entry_pac device,
2192 DATA_BLOB **device_info_blob)
2194 TALLOC_CTX *frame = NULL;
2195 krb5_error_code code = EINVAL;
2196 NTSTATUS nt_status;
2198 const struct auth_user_info_dc *device_info = NULL;
2199 struct netr_SamInfo3 *info3 = NULL;
2200 struct PAC_DOMAIN_GROUP_MEMBERSHIP *resource_groups = NULL;
2202 union PAC_INFO info;
2204 frame = talloc_stackframe();
2206 code = samba_kdc_get_user_info_dc(frame,
2207 context,
2208 samdb,
2209 device,
2210 &device_info,
2211 NULL /* resource_groups_out */);
2212 if (code) {
2213 const char *krb5_err = krb5_get_error_message(context, code);
2214 DBG_ERR("samba_kdc_get_user_info_dc failed: %s\n",
2215 krb5_err != NULL ? krb5_err : "<unknown>");
2216 krb5_free_error_message(context, krb5_err);
2218 talloc_free(frame);
2219 return KRB5KDC_ERR_TGT_REVOKED;
2222 nt_status = auth_convert_user_info_dc_saminfo3(frame, device_info,
2223 AUTH_INCLUDE_RESOURCE_GROUPS_COMPRESSED,
2224 &info3,
2225 &resource_groups);
2226 if (!NT_STATUS_IS_OK(nt_status)) {
2227 DBG_WARNING("Getting Samba info failed: %s\n",
2228 nt_errstr(nt_status));
2229 talloc_free(frame);
2230 return nt_status_to_krb5(nt_status);
2233 code = samba_kdc_make_device_info(frame,
2234 info3,
2235 resource_groups,
2236 &info);
2237 if (code != 0) {
2238 talloc_free(frame);
2239 return code;
2242 code = samba_kdc_get_device_info_pac_blob(mem_ctx,
2243 &info,
2244 device_info_blob);
2246 talloc_free(frame);
2247 return code;
2251 * @brief Verify a PAC
2253 * @param mem_ctx A talloc memory context
2255 * @param context A krb5 context
2257 * @param samdb An open samdb connection.
2259 * @param flags Bitwise OR'ed flags
2261 * @param client The client samba kdc PAC entry.
2263 * @param krbtgt The krbtgt samba kdc entry.
2265 * @return A Kerberos error code.
2267 krb5_error_code samba_kdc_verify_pac(TALLOC_CTX *mem_ctx,
2268 krb5_context context,
2269 struct ldb_context *samdb,
2270 uint32_t flags,
2271 const struct samba_kdc_entry_pac client,
2272 const struct samba_kdc_entry *krbtgt)
2274 TALLOC_CTX *tmp_ctx = NULL;
2275 struct pac_blobs *pac_blobs = NULL;
2276 krb5_error_code code = EINVAL;
2278 tmp_ctx = talloc_new(mem_ctx);
2279 if (tmp_ctx == NULL) {
2280 code = ENOMEM;
2281 goto done;
2284 if (client.entry != NULL) {
2286 * Check the objectSID of the client and pac data are the same.
2287 * Does a parse and SID check, but no crypto.
2289 code = samba_kdc_validate_pac_blob(context, client);
2290 if (code != 0) {
2291 goto done;
2295 if (!samba_krb5_pac_is_trusted(client)) {
2296 const struct auth_user_info_dc *user_info_dc = NULL;
2297 WERROR werr;
2299 struct dom_sid *object_sids = NULL;
2300 uint32_t j;
2302 if (client.entry == NULL) {
2303 code = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
2304 goto done;
2307 code = samba_kdc_get_user_info_from_db(tmp_ctx,
2308 samdb,
2309 client.entry,
2310 client.entry->msg,
2311 &user_info_dc);
2312 if (code) {
2313 const char *krb5_err = krb5_get_error_message(context, code);
2314 DBG_ERR("Getting user info for PAC failed: %s\n",
2315 krb5_err != NULL ? krb5_err : "<unknown>");
2316 krb5_free_error_message(context, krb5_err);
2318 code = KRB5KDC_ERR_TGT_REVOKED;
2319 goto done;
2323 * Check if the SID list in the user_info_dc intersects
2324 * correctly with the RODC allow/deny lists.
2326 object_sids = talloc_array(tmp_ctx, struct dom_sid, user_info_dc->num_sids);
2327 if (object_sids == NULL) {
2328 code = ENOMEM;
2329 goto done;
2332 for (j = 0; j < user_info_dc->num_sids; ++j) {
2333 object_sids[j] = user_info_dc->sids[j].sid;
2336 werr = samba_rodc_confirm_user_is_allowed(user_info_dc->num_sids,
2337 object_sids,
2338 krbtgt,
2339 client.entry);
2340 if (!W_ERROR_IS_OK(werr)) {
2341 code = KRB5KDC_ERR_TGT_REVOKED;
2342 if (W_ERROR_EQUAL(werr,
2343 WERR_DOMAIN_CONTROLLER_NOT_FOUND)) {
2344 code = KRB5KDC_ERR_POLICY;
2346 goto done;
2350 * The RODC PAC data isn't trusted for authorization as it may
2351 * be stale. The only thing meaningful we can do with an RODC
2352 * account on a full DC is exchange the RODC TGT for a 'real'
2353 * TGT.
2355 * So we match Windows (at least server 2022) and
2356 * don't allow S4U2Self.
2358 * https://lists.samba.org/archive/cifs-protocol/2022-April/003673.html
2360 if (flags & SAMBA_KDC_FLAG_PROTOCOL_TRANSITION) {
2361 code = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
2362 goto done;
2366 /* Check the types of the given PAC */
2368 code = pac_blobs_from_krb5_pac(tmp_ctx,
2369 context,
2370 client.pac,
2371 &pac_blobs);
2372 if (code != 0) {
2373 goto done;
2376 code = pac_blobs_ensure_exists(pac_blobs,
2377 PAC_TYPE_LOGON_INFO);
2378 if (code != 0) {
2379 goto done;
2382 code = pac_blobs_ensure_exists(pac_blobs,
2383 PAC_TYPE_LOGON_NAME);
2384 if (code != 0) {
2385 goto done;
2388 code = pac_blobs_ensure_exists(pac_blobs,
2389 PAC_TYPE_SRV_CHECKSUM);
2390 if (code != 0) {
2391 goto done;
2394 code = pac_blobs_ensure_exists(pac_blobs,
2395 PAC_TYPE_KDC_CHECKSUM);
2396 if (code != 0) {
2397 goto done;
2400 if (!(flags & SAMBA_KDC_FLAG_CONSTRAINED_DELEGATION)) {
2401 code = pac_blobs_ensure_exists(pac_blobs,
2402 PAC_TYPE_REQUESTER_SID);
2403 if (code != 0) {
2404 code = KRB5KDC_ERR_TGT_REVOKED;
2405 goto done;
2409 code = 0;
2411 done:
2412 talloc_free(tmp_ctx);
2414 return code;
2418 * @brief Update a PAC
2420 * @param mem_ctx A talloc memory context
2422 * @param context A krb5 context
2424 * @param samdb An open samdb connection.
2426 * @param lp_ctx A loadparm context.
2428 * @param flags Bitwise OR'ed flags
2430 * @param device_pac_is_trusted Whether the device's PAC was issued by a trusted server,
2431 * as opposed to an RODC.
2433 * @param client The client samba kdc PAC entry.
2435 * @param server_principal The server principal
2437 * @param server The server samba kdc entry.
2439 * @param delegated_proxy_principal The delegated proxy principal used for
2440 * updating the constrained delegation PAC
2441 * buffer.
2443 * @param delegated_proxy The delegated proxy kdc PAC entry.
2445 * @param device The computer's samba kdc PAC entry; used for compound
2446 * authentication.
2448 * @param new_pac The new already allocated PAC
2450 * @return A Kerberos error code. If no PAC should be returned, the code will be
2451 * ENOATTR!
2453 krb5_error_code samba_kdc_update_pac(TALLOC_CTX *mem_ctx,
2454 krb5_context context,
2455 struct ldb_context *samdb,
2456 struct loadparm_context *lp_ctx,
2457 uint32_t flags,
2458 const struct samba_kdc_entry_pac client,
2459 const krb5_const_principal server_principal,
2460 const struct samba_kdc_entry *server,
2461 const krb5_const_principal delegated_proxy_principal,
2462 const struct samba_kdc_entry_pac delegated_proxy,
2463 const struct samba_kdc_entry_pac device,
2464 krb5_pac new_pac,
2465 struct authn_audit_info **server_audit_info_out,
2466 NTSTATUS *status_out)
2468 TALLOC_CTX *tmp_ctx = NULL;
2469 krb5_error_code code = EINVAL;
2470 NTSTATUS nt_status;
2471 DATA_BLOB *pac_blob = NULL;
2472 DATA_BLOB *upn_blob = NULL;
2473 DATA_BLOB *deleg_blob = NULL;
2474 DATA_BLOB *requester_sid_blob = NULL;
2475 const DATA_BLOB *client_claims_blob = NULL;
2476 DATA_BLOB device_claims_blob = {};
2477 const DATA_BLOB *device_claims_blob_ptr = NULL;
2478 struct auth_claims auth_claims = {};
2479 DATA_BLOB *device_info_blob = NULL;
2480 bool is_tgs = false;
2481 bool server_restrictions_present = false;
2482 struct pac_blobs *pac_blobs = NULL;
2483 const struct auth_user_info_dc *user_info_dc_const = NULL;
2484 struct auth_user_info_dc *user_info_dc_shallow_copy = NULL;
2485 const struct PAC_DOMAIN_GROUP_MEMBERSHIP *_resource_groups = NULL;
2486 enum auth_group_inclusion group_inclusion;
2487 bool compounded_auth;
2488 size_t i = 0;
2490 if (server_audit_info_out != NULL) {
2491 *server_audit_info_out = NULL;
2494 if (status_out != NULL) {
2495 *status_out = NT_STATUS_OK;
2498 tmp_ctx = talloc_new(mem_ctx);
2499 if (tmp_ctx == NULL) {
2500 code = ENOMEM;
2501 goto done;
2505 int result = smb_krb5_principal_is_tgs(context, server_principal);
2506 if (result == -1) {
2507 code = ENOMEM;
2508 goto done;
2511 is_tgs = result;
2514 server_restrictions_present = !is_tgs && authn_policy_restrictions_present(server->server_policy);
2516 /* Only include resource groups in a service ticket. */
2517 if (is_tgs) {
2518 group_inclusion = AUTH_EXCLUDE_RESOURCE_GROUPS;
2519 } else if (server->supported_enctypes & KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED) {
2520 group_inclusion = AUTH_INCLUDE_RESOURCE_GROUPS;
2521 } else {
2522 group_inclusion = AUTH_INCLUDE_RESOURCE_GROUPS_COMPRESSED;
2525 compounded_auth = device.entry != NULL && !is_tgs
2526 && server->supported_enctypes & KERB_ENCTYPE_COMPOUND_IDENTITY_SUPPORTED;
2528 if (compounded_auth || (server_restrictions_present && device.entry != NULL)) {
2530 * [MS-KILE] 3.3.5.7.4 Compound Identity: the client claims from
2531 * the device PAC become the device claims in the new PAC.
2533 code = samba_kdc_get_claims_data(tmp_ctx,
2534 context,
2535 samdb,
2536 device,
2537 &auth_claims.device_claims);
2538 if (code) {
2539 goto done;
2542 if (compounded_auth) {
2543 nt_status = claims_data_encoded_claims_set(tmp_ctx,
2544 auth_claims.device_claims,
2545 &device_claims_blob);
2546 if (!NT_STATUS_IS_OK(nt_status)) {
2547 DBG_ERR("claims_data_encoded_claims_set failed: %s\n",
2548 nt_errstr(nt_status));
2549 code = map_errno_from_nt_status(nt_status);
2550 goto done;
2553 device_claims_blob_ptr = &device_claims_blob;
2555 if (samba_krb5_pac_is_trusted(device)) {
2556 code = samba_kdc_create_device_info_blob(tmp_ctx,
2557 context,
2558 samdb,
2559 device.pac,
2560 &device_info_blob);
2561 if (code != 0) {
2562 goto done;
2564 } else {
2565 /* Don't trust an RODC‐issued PAC; regenerate the device info. */
2566 code = samba_kdc_get_device_info_blob(tmp_ctx,
2567 context,
2568 samdb,
2569 device,
2570 &device_info_blob);
2571 if (code != 0) {
2572 goto done;
2578 if (delegated_proxy_principal != NULL) {
2579 deleg_blob = talloc_zero(tmp_ctx, DATA_BLOB);
2580 if (deleg_blob == NULL) {
2581 code = ENOMEM;
2582 goto done;
2585 nt_status = samba_kdc_update_delegation_info_blob(
2586 deleg_blob,
2587 context,
2588 client.pac,
2589 server_principal,
2590 delegated_proxy_principal,
2591 deleg_blob);
2592 if (!NT_STATUS_IS_OK(nt_status)) {
2593 DBG_ERR("update delegation info blob failed: %s\n",
2594 nt_errstr(nt_status));
2595 code = map_errno_from_nt_status(nt_status);
2596 goto done;
2601 * If we are creating a TGT, resource groups from our domain are not to
2602 * be put into the PAC. Instead, we take the resource groups directly
2603 * from the original PAC and copy them unmodified into the new one.
2605 code = samba_kdc_get_user_info_dc(tmp_ctx,
2606 context,
2607 samdb,
2608 client,
2609 &user_info_dc_const,
2610 is_tgs ? &_resource_groups : NULL);
2611 if (code != 0) {
2612 const char *err_str = krb5_get_error_message(context, code);
2613 DBG_ERR("samba_kdc_get_user_info_dc failed: %s\n",
2614 err_str != NULL ? err_str : "<unknown>");
2615 krb5_free_error_message(context, err_str);
2617 goto done;
2621 * Enforce the AllowedToAuthenticateTo part of an authentication policy,
2622 * if one is present.
2624 if (server_restrictions_present) {
2625 struct samba_kdc_entry_pac auth_entry;
2626 const struct auth_user_info_dc *auth_user_info_dc = NULL;
2627 const struct auth_user_info_dc *device_info = NULL;
2629 if (delegated_proxy.entry != NULL) {
2630 auth_entry = delegated_proxy;
2632 code = samba_kdc_get_user_info_dc(tmp_ctx,
2633 context,
2634 samdb,
2635 delegated_proxy,
2636 &auth_user_info_dc,
2637 NULL /* resource_groups_out */);
2638 if (code) {
2639 goto done;
2641 } else {
2642 auth_entry = client;
2643 auth_user_info_dc = user_info_dc_const;
2646 /* Fetch the user’s claims. */
2647 code = samba_kdc_get_claims_data(tmp_ctx,
2648 context,
2649 samdb,
2650 auth_entry,
2651 &auth_claims.user_claims);
2652 if (code) {
2653 goto done;
2656 if (device.entry != NULL) {
2657 code = samba_kdc_get_user_info_dc(tmp_ctx,
2658 context,
2659 samdb,
2660 device,
2661 &device_info,
2662 NULL /* resource_groups_out */);
2663 if (code) {
2664 goto done;
2669 * Allocate the audit info and output status on to the parent
2670 * mem_ctx, not the temporary context.
2672 code = samba_kdc_allowed_to_authenticate_to(mem_ctx,
2673 samdb,
2674 lp_ctx,
2675 auth_entry.entry,
2676 auth_user_info_dc,
2677 device_info,
2678 auth_claims,
2679 server,
2680 server_audit_info_out,
2681 status_out);
2682 if (code) {
2683 goto done;
2687 if (compounded_auth) {
2688 /* Make a shallow copy of the user_info_dc structure. */
2689 nt_status = authsam_shallow_copy_user_info_dc(tmp_ctx,
2690 user_info_dc_const,
2691 &user_info_dc_shallow_copy);
2692 user_info_dc_const = NULL;
2694 if (!NT_STATUS_IS_OK(nt_status)) {
2695 DBG_ERR("Failed to copy user_info_dc: %s\n",
2696 nt_errstr(nt_status));
2698 code = KRB5KDC_ERR_TGT_REVOKED;
2699 goto done;
2702 nt_status = samba_kdc_add_compounded_auth(user_info_dc_shallow_copy);
2703 if (!NT_STATUS_IS_OK(nt_status)) {
2704 DBG_ERR("Failed to add Compounded Authentication: %s\n",
2705 nt_errstr(nt_status));
2707 code = KRB5KDC_ERR_TGT_REVOKED;
2708 goto done;
2711 /* We can now set back to the const, it will not be modified */
2712 user_info_dc_const = user_info_dc_shallow_copy;
2715 if (samba_krb5_pac_is_trusted(client)) {
2716 pac_blob = talloc_zero(tmp_ctx, DATA_BLOB);
2717 if (pac_blob == NULL) {
2718 code = ENOMEM;
2719 goto done;
2722 nt_status = samba_get_logon_info_pac_blob(tmp_ctx,
2723 user_info_dc_const,
2724 _resource_groups,
2725 group_inclusion,
2726 pac_blob);
2727 if (!NT_STATUS_IS_OK(nt_status)) {
2728 DBG_ERR("samba_get_logon_info_pac_blob failed: %s\n",
2729 nt_errstr(nt_status));
2731 code = map_errno_from_nt_status(nt_status);
2732 goto done;
2736 * TODO: we need claim translation over trusts,
2737 * for now we just clear them...
2739 if (samba_kdc_entry_pac_issued_by_trust(client)) {
2740 client_claims_blob = &data_blob_null;
2742 } else {
2743 nt_status = samba_kdc_get_logon_info_blob(tmp_ctx,
2744 user_info_dc_const,
2745 group_inclusion,
2746 &pac_blob);
2747 if (!NT_STATUS_IS_OK(nt_status)) {
2748 DBG_ERR("samba_kdc_get_logon_info_blob failed: %s\n",
2749 nt_errstr(nt_status));
2750 code = KRB5KDC_ERR_TGT_REVOKED;
2751 goto done;
2754 nt_status = samba_kdc_get_upn_info_blob(tmp_ctx,
2755 user_info_dc_const,
2756 &upn_blob);
2757 if (!NT_STATUS_IS_OK(nt_status)) {
2758 DBG_ERR("samba_kdc_get_upn_info_blob failed: %s\n",
2759 nt_errstr(nt_status));
2760 code = KRB5KDC_ERR_TGT_REVOKED;
2761 goto done;
2764 if (is_tgs) {
2765 nt_status = samba_kdc_get_requester_sid_blob(tmp_ctx,
2766 user_info_dc_const,
2767 &requester_sid_blob);
2768 if (!NT_STATUS_IS_OK(nt_status)) {
2769 DBG_ERR("samba_kdc_get_requester_sid_blob failed: %s\n",
2770 nt_errstr(nt_status));
2771 code = KRB5KDC_ERR_TGT_REVOKED;
2772 goto done;
2776 /* Don't trust RODC-issued claims. Regenerate them. */
2777 nt_status = samba_kdc_get_claims_blob(tmp_ctx,
2778 client.entry,
2779 &client_claims_blob);
2780 if (!NT_STATUS_IS_OK(nt_status)) {
2781 DBG_ERR("samba_kdc_get_claims_blob failed: %s\n",
2782 nt_errstr(nt_status));
2783 code = map_errno_from_nt_status(nt_status);
2784 goto done;
2788 /* Check the types of the given PAC */
2789 code = pac_blobs_from_krb5_pac(tmp_ctx,
2790 context,
2791 client.pac,
2792 &pac_blobs);
2793 if (code != 0) {
2794 goto done;
2797 code = pac_blobs_replace_existing(pac_blobs,
2798 PAC_TYPE_LOGON_INFO,
2799 pac_blob);
2800 if (code != 0) {
2801 goto done;
2804 #ifdef SAMBA4_USES_HEIMDAL
2805 /* Not needed with MIT Kerberos */
2806 code = pac_blobs_replace_existing(pac_blobs,
2807 PAC_TYPE_LOGON_NAME,
2808 &data_blob_null);
2809 if (code != 0) {
2810 goto done;
2813 code = pac_blobs_replace_existing(pac_blobs,
2814 PAC_TYPE_SRV_CHECKSUM,
2815 &data_blob_null);
2816 if (code != 0) {
2817 goto done;
2820 code = pac_blobs_replace_existing(pac_blobs,
2821 PAC_TYPE_KDC_CHECKSUM,
2822 &data_blob_null);
2823 if (code != 0) {
2824 goto done;
2826 #endif
2828 code = pac_blobs_add_blob(pac_blobs,
2829 PAC_TYPE_CONSTRAINED_DELEGATION,
2830 deleg_blob);
2831 if (code != 0) {
2832 goto done;
2835 code = pac_blobs_add_blob(pac_blobs,
2836 PAC_TYPE_UPN_DNS_INFO,
2837 upn_blob);
2838 if (code != 0) {
2839 goto done;
2842 code = pac_blobs_add_blob(pac_blobs,
2843 PAC_TYPE_CLIENT_CLAIMS_INFO,
2844 client_claims_blob);
2845 if (code != 0) {
2846 goto done;
2849 code = pac_blobs_add_blob(pac_blobs,
2850 PAC_TYPE_DEVICE_INFO,
2851 device_info_blob);
2852 if (code != 0) {
2853 goto done;
2856 code = pac_blobs_add_blob(pac_blobs,
2857 PAC_TYPE_DEVICE_CLAIMS_INFO,
2858 device_claims_blob_ptr);
2859 if (code != 0) {
2860 goto done;
2863 if (!samba_krb5_pac_is_trusted(client) || !is_tgs) {
2864 pac_blobs_remove_blob(pac_blobs,
2865 PAC_TYPE_ATTRIBUTES_INFO);
2868 if (!is_tgs) {
2869 pac_blobs_remove_blob(pac_blobs,
2870 PAC_TYPE_REQUESTER_SID);
2873 code = pac_blobs_add_blob(pac_blobs,
2874 PAC_TYPE_REQUESTER_SID,
2875 requester_sid_blob);
2876 if (code != 0) {
2877 goto done;
2881 * The server account may be set not to want the PAC.
2883 * While this is wasteful if the above calculations were done
2884 * and now thrown away, this is cleaner as we do any ticket
2885 * signature checking etc always.
2887 * UF_NO_AUTH_DATA_REQUIRED is the rare case and most of the
2888 * time (eg not accepting a ticket from the RODC) we do not
2889 * need to re-generate anything anyway.
2891 if (!samba_princ_needs_pac(server)) {
2892 code = ENOATTR;
2893 goto done;
2896 if (samba_krb5_pac_is_trusted(client) && !is_tgs) {
2898 * The client may have requested no PAC when obtaining the
2899 * TGT.
2901 bool requested_pac = false;
2903 code = samba_client_requested_pac(context,
2904 client.pac,
2905 tmp_ctx,
2906 &requested_pac);
2907 if (code != 0 || !requested_pac) {
2908 if (!requested_pac) {
2909 code = ENOATTR;
2911 goto done;
2915 for (i = 0; i < pac_blobs->num_types; ++i) {
2916 krb5_data type_data;
2917 const DATA_BLOB *type_blob = pac_blobs->type_blobs[i].data;
2918 uint32_t type = pac_blobs->type_blobs[i].type;
2920 static char null_byte = '\0';
2921 const krb5_data null_data = smb_krb5_make_data(&null_byte, 0);
2923 #ifndef SAMBA4_USES_HEIMDAL
2924 /* Not needed with MIT Kerberos */
2925 switch(type) {
2926 case PAC_TYPE_LOGON_NAME:
2927 case PAC_TYPE_SRV_CHECKSUM:
2928 case PAC_TYPE_KDC_CHECKSUM:
2929 case PAC_TYPE_FULL_CHECKSUM:
2930 continue;
2931 default:
2932 break;
2934 #endif
2936 if (type_blob != NULL) {
2937 type_data = smb_krb5_data_from_blob(*type_blob);
2939 * Passing a NULL pointer into krb5_pac_add_buffer() is
2940 * not allowed, so pass null_data instead if needed.
2942 code = krb5_pac_add_buffer(context,
2943 new_pac,
2944 type,
2945 (type_data.data != NULL) ? &type_data : &null_data);
2946 if (code != 0) {
2947 goto done;
2949 } else if (samba_krb5_pac_is_trusted(client)) {
2951 * Convey the buffer from the original PAC if we can
2952 * trust it.
2955 code = krb5_pac_get_buffer(context,
2956 client.pac,
2957 type,
2958 &type_data);
2959 if (code != 0) {
2960 goto done;
2963 * Passing a NULL pointer into krb5_pac_add_buffer() is
2964 * not allowed, so pass null_data instead if needed.
2966 code = krb5_pac_add_buffer(context,
2967 new_pac,
2968 type,
2969 (type_data.data != NULL) ? &type_data : &null_data);
2970 smb_krb5_free_data_contents(context, &type_data);
2971 if (code != 0) {
2972 goto done;
2977 code = 0;
2978 done:
2979 TALLOC_FREE(tmp_ctx);
2980 return code;
2983 krb5_error_code samba_kdc_get_claims_data(TALLOC_CTX *mem_ctx,
2984 krb5_context context,
2985 struct ldb_context *samdb,
2986 struct samba_kdc_entry_pac entry,
2987 struct claims_data **claims_data_out)
2989 if (samba_kdc_entry_pac_issued_by_trust(entry)) {
2990 NTSTATUS status;
2993 * TODO: we need claim translation over trusts; for now we just
2994 * clear them…
2996 status = claims_data_from_encoded_claims_set(mem_ctx,
2997 NULL,
2998 claims_data_out);
2999 if (!NT_STATUS_IS_OK(status)) {
3000 return map_errno_from_nt_status(status);
3003 return 0;
3006 if (samba_krb5_pac_is_trusted(entry)) {
3007 return samba_kdc_get_claims_data_from_pac(mem_ctx,
3008 context,
3009 entry,
3010 claims_data_out);
3013 return samba_kdc_get_claims_data_from_db(samdb,
3014 entry.entry,
3015 claims_data_out);
3018 krb5_error_code samba_kdc_get_claims_data_from_pac(TALLOC_CTX *mem_ctx,
3019 krb5_context context,
3020 struct samba_kdc_entry_pac entry,
3021 struct claims_data **claims_data_out)
3023 TALLOC_CTX *frame = NULL;
3024 krb5_data claims_info = {};
3025 struct claims_data *claims_data = NULL;
3026 NTSTATUS status = NT_STATUS_OK;
3027 krb5_error_code code;
3029 if (!samba_krb5_pac_is_trusted(entry)) {
3030 code = EINVAL;
3031 goto out;
3034 if (samba_kdc_entry_pac_issued_by_trust(entry)) {
3035 code = EINVAL;
3036 goto out;
3039 if (claims_data_out == NULL) {
3040 code = EINVAL;
3041 goto out;
3044 *claims_data_out = NULL;
3046 if (entry.entry != NULL && entry.entry->claims_from_pac_are_initialized) {
3047 /* Note: the caller does not own this! */
3048 *claims_data_out = entry.entry->claims_from_pac;
3049 return 0;
3052 frame = talloc_stackframe();
3054 /* Fetch the claims from the PAC. */
3055 code = krb5_pac_get_buffer(context, entry.pac,
3056 PAC_TYPE_CLIENT_CLAIMS_INFO,
3057 &claims_info);
3058 if (code == ENOENT) {
3059 /* OK. */
3060 } else if (code != 0) {
3061 DBG_ERR("Error getting CLIENT_CLAIMS_INFO from PAC\n");
3062 goto out;
3063 } else if (claims_info.length) {
3064 DATA_BLOB claims_blob = data_blob_const(claims_info.data,
3065 claims_info.length);
3067 status = claims_data_from_encoded_claims_set(frame,
3068 &claims_blob,
3069 &claims_data);
3070 if (!NT_STATUS_IS_OK(status)) {
3071 code = map_errno_from_nt_status(status);
3072 goto out;
3076 if (entry.entry != NULL) {
3077 /* Note: the caller does not own this! */
3078 entry.entry->claims_from_pac = talloc_steal(entry.entry,
3079 claims_data);
3080 entry.entry->claims_from_pac_are_initialized = true;
3081 } else {
3082 talloc_steal(mem_ctx, claims_data);
3085 *claims_data_out = claims_data;
3087 out:
3088 smb_krb5_free_data_contents(context, &claims_info);
3089 talloc_free(frame);
3090 return code;
3093 krb5_error_code samba_kdc_get_claims_data_from_db(struct ldb_context *samdb,
3094 struct samba_kdc_entry *entry,
3095 struct claims_data **claims_data_out)
3097 TALLOC_CTX *frame = NULL;
3099 struct claims_data *claims_data = NULL;
3100 struct CLAIMS_SET *claims_set = NULL;
3101 NTSTATUS status = NT_STATUS_OK;
3102 krb5_error_code code;
3104 if (samdb == NULL) {
3105 code = EINVAL;
3106 goto out;
3109 if (claims_data_out == NULL) {
3110 code = EINVAL;
3111 goto out;
3114 if (entry == NULL) {
3115 code = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
3116 goto out;
3119 *claims_data_out = NULL;
3121 if (entry->claims_from_db_are_initialized) {
3122 /* Note: the caller does not own this! */
3123 *claims_data_out = entry->claims_from_db;
3124 return 0;
3127 frame = talloc_stackframe();
3129 code = get_claims_set_for_principal(samdb,
3130 frame,
3131 entry->msg,
3132 &claims_set);
3133 if (code) {
3134 DBG_ERR("Failed to fetch claims\n");
3135 goto out;
3138 if (claims_set != NULL) {
3139 status = claims_data_from_claims_set(claims_data,
3140 claims_set,
3141 &claims_data);
3142 if (!NT_STATUS_IS_OK(status)) {
3143 code = map_errno_from_nt_status(status);
3144 goto out;
3148 entry->claims_from_db = talloc_steal(entry,
3149 claims_data);
3150 entry->claims_from_db_are_initialized = true;
3152 /* Note: the caller does not own this! */
3153 *claims_data_out = entry->claims_from_db;
3155 out:
3156 talloc_free(frame);
3157 return code;
3160 krb5_error_code samba_kdc_check_device(TALLOC_CTX *mem_ctx,
3161 krb5_context context,
3162 struct ldb_context *samdb,
3163 struct loadparm_context *lp_ctx,
3164 const struct samba_kdc_entry_pac device,
3165 const struct authn_kerberos_client_policy *client_policy,
3166 struct authn_audit_info **client_audit_info_out,
3167 NTSTATUS *status_out)
3169 TALLOC_CTX *frame = NULL;
3170 krb5_error_code code = 0;
3171 NTSTATUS nt_status;
3172 const struct auth_user_info_dc *device_info = NULL;
3173 struct authn_audit_info *client_audit_info = NULL;
3174 struct auth_claims auth_claims = {};
3176 if (status_out != NULL) {
3177 *status_out = NT_STATUS_OK;
3180 if (!authn_policy_device_restrictions_present(client_policy)) {
3181 return 0;
3184 if (device.entry == NULL || device.pac == NULL) {
3185 NTSTATUS out_status = NT_STATUS_INVALID_WORKSTATION;
3187 nt_status = authn_kerberos_client_policy_audit_info(mem_ctx,
3188 client_policy,
3189 NULL /* client_info */,
3190 AUTHN_AUDIT_EVENT_KERBEROS_DEVICE_RESTRICTION,
3191 AUTHN_AUDIT_REASON_FAST_REQUIRED,
3192 out_status,
3193 client_audit_info_out);
3194 if (!NT_STATUS_IS_OK(nt_status)) {
3195 code = KRB5KRB_ERR_GENERIC;
3196 } else if (authn_kerberos_client_policy_is_enforced(client_policy)) {
3197 code = KRB5KDC_ERR_POLICY;
3199 if (status_out != NULL) {
3200 *status_out = out_status;
3202 } else {
3203 /* OK. */
3204 code = 0;
3207 goto out;
3210 frame = talloc_stackframe();
3212 code = samba_kdc_get_user_info_dc(frame,
3213 context,
3214 samdb,
3215 device,
3216 &device_info,
3217 NULL);
3218 if (code) {
3219 goto out;
3223 * The device claims become the *user* claims for the purpose of
3224 * evaluating a conditional ACE expression.
3226 code = samba_kdc_get_claims_data(frame,
3227 context,
3228 samdb,
3229 device,
3230 &auth_claims.user_claims);
3231 if (code) {
3232 goto out;
3235 nt_status = authn_policy_authenticate_from_device(frame,
3236 samdb,
3237 lp_ctx,
3238 device_info,
3239 auth_claims,
3240 client_policy,
3241 &client_audit_info);
3242 if (client_audit_info != NULL) {
3243 *client_audit_info_out = talloc_move(mem_ctx, &client_audit_info);
3245 if (!NT_STATUS_IS_OK(nt_status)) {
3246 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_AUTHENTICATION_FIREWALL_FAILED)) {
3247 code = KRB5KDC_ERR_POLICY;
3248 } else {
3249 code = KRB5KRB_ERR_GENERIC;
3252 goto out;
3255 out:
3256 talloc_free(frame);
3257 return code;