s3: tests: Add new test_stream_dir_rename.sh test.
[Samba.git] / source4 / kdc / wdc-samba4.c
blob6e3cbe7b312e0adb0ee9195d48b0a14042f71286
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 "includes.h"
25 #include "kdc/kdc-glue.h"
26 #include "kdc/db-glue.h"
27 #include "kdc/pac-glue.h"
28 #include "sdb.h"
29 #include "sdb_hdb.h"
30 #include "librpc/gen_ndr/auth.h"
31 #include <krb5_locl.h>
32 #include "lib/replace/system/filesys.h"
34 #undef DBGC_CLASS
35 #define DBGC_CLASS DBGC_KERBEROS
37 static int samba_wdc_pac_options(astgs_request_t r, PAC_OPTIONS_FLAGS *flags)
39 const KDC_REQ *req = kdc_request_get_req(r);
40 const PA_DATA *padata_pac_options = NULL;
42 ZERO_STRUCTP(flags);
44 if (req->padata != NULL) {
45 int idx = 0;
47 padata_pac_options = krb5_find_padata(req->padata->val,
48 req->padata->len,
49 KRB5_PADATA_PAC_OPTIONS,
50 &idx);
53 if (padata_pac_options != NULL) {
54 PA_PAC_OPTIONS pa_pac_options = {};
55 int ret;
57 ret = decode_PA_PAC_OPTIONS(padata_pac_options->padata_value.data,
58 padata_pac_options->padata_value.length,
59 &pa_pac_options, NULL);
60 if (ret) {
61 return ret;
63 *flags = pa_pac_options.flags;
66 return 0;
69 static bool samba_wdc_is_s4u2self_req(astgs_request_t r)
71 krb5_kdc_configuration *config = kdc_request_get_config((kdc_request_t)r);
72 const KDC_REQ *req = kdc_request_get_req(r);
73 const PA_DATA *pa_for_user = NULL;
75 if (req->msg_type != krb_tgs_req) {
76 return false;
79 if (config->enable_fast && req->padata != NULL) {
80 const PA_DATA *pa_fx_fast = NULL;
81 int idx = 0;
83 pa_fx_fast = krb5_find_padata(req->padata->val,
84 req->padata->len,
85 KRB5_PADATA_FX_FAST,
86 &idx);
87 if (pa_fx_fast != NULL) {
89 * We're in the outer request
90 * with KRB5_PADATA_FX_FAST
91 * if fast is enabled we'll
92 * process the s4u2self
93 * request only in the
94 * inner request.
96 return false;
100 if (req->padata != NULL) {
101 int idx = 0;
103 pa_for_user = krb5_find_padata(req->padata->val,
104 req->padata->len,
105 KRB5_PADATA_FOR_USER,
106 &idx);
109 if (pa_for_user != NULL) {
110 return true;
113 return false;
117 * Given the right private pointer from hdb_samba4,
118 * get a PAC from the attached ldb messages.
120 * For PKINIT we also get pk_reply_key and can add PAC_CREDENTIAL_INFO.
122 static krb5_error_code samba_wdc_get_pac(void *priv,
123 astgs_request_t r,
124 hdb_entry *client,
125 hdb_entry *server,
126 const krb5_keyblock *pk_reply_key,
127 uint64_t pac_attributes,
128 krb5_pac *pac)
130 krb5_context context = kdc_request_get_context((kdc_request_t)r);
131 TALLOC_CTX *mem_ctx;
132 DATA_BLOB *logon_blob = NULL;
133 DATA_BLOB *cred_ndr = NULL;
134 DATA_BLOB **cred_ndr_ptr = NULL;
135 DATA_BLOB _cred_blob = data_blob_null;
136 DATA_BLOB *cred_blob = NULL;
137 DATA_BLOB *upn_blob = NULL;
138 DATA_BLOB *pac_attrs_blob = NULL;
139 DATA_BLOB *requester_sid_blob = NULL;
140 DATA_BLOB *claims_blob = NULL;
141 krb5_error_code ret;
142 NTSTATUS nt_status;
143 struct samba_kdc_entry *skdc_entry =
144 talloc_get_type_abort(client->context,
145 struct samba_kdc_entry);
146 const struct samba_kdc_entry *server_entry =
147 talloc_get_type_abort(server->context,
148 struct samba_kdc_entry);
149 bool is_krbtgt = krb5_principal_is_krbtgt(context, server->principal);
150 /* Only include resource groups in a service ticket. */
151 enum auth_group_inclusion group_inclusion;
152 bool is_s4u2self = samba_wdc_is_s4u2self_req(r);
153 enum samba_asserted_identity asserted_identity =
154 (is_s4u2self) ?
155 SAMBA_ASSERTED_IDENTITY_SERVICE :
156 SAMBA_ASSERTED_IDENTITY_AUTHENTICATION_AUTHORITY;
157 PAC_OPTIONS_FLAGS pac_options = {};
159 /* Only include resource groups in a service ticket. */
160 if (is_krbtgt) {
161 group_inclusion = AUTH_EXCLUDE_RESOURCE_GROUPS;
162 } else if (server_entry->supported_enctypes & KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED) {
163 group_inclusion = AUTH_INCLUDE_RESOURCE_GROUPS;
164 } else {
165 group_inclusion = AUTH_INCLUDE_RESOURCE_GROUPS_COMPRESSED;
168 ret = samba_wdc_pac_options(r, &pac_options);
169 if (ret != 0) {
170 return ret;
173 mem_ctx = talloc_named(client->context, 0, "samba_get_pac context");
174 if (!mem_ctx) {
175 return ENOMEM;
178 if (pk_reply_key != NULL) {
179 cred_ndr_ptr = &cred_ndr;
182 nt_status = samba_kdc_get_pac_blobs(mem_ctx, skdc_entry,
183 asserted_identity,
184 group_inclusion,
185 &logon_blob,
186 cred_ndr_ptr,
187 &upn_blob,
188 is_krbtgt ? &pac_attrs_blob : NULL,
189 pac_attributes,
190 is_krbtgt ? &requester_sid_blob : NULL,
191 pac_options.claims ?
192 &claims_blob : NULL);
193 if (!NT_STATUS_IS_OK(nt_status)) {
194 talloc_free(mem_ctx);
195 return EINVAL;
198 if (pk_reply_key != NULL && cred_ndr != NULL) {
199 ret = samba_kdc_encrypt_pac_credentials(context,
200 pk_reply_key,
201 cred_ndr,
202 mem_ctx,
203 &_cred_blob);
204 if (ret != 0) {
205 talloc_free(mem_ctx);
206 return ret;
208 cred_blob = &_cred_blob;
211 ret = krb5_pac_init(context, pac);
212 if (ret != 0) {
213 talloc_free(mem_ctx);
214 return ret;
217 ret = samba_make_krb5_pac(context, logon_blob, cred_blob,
218 upn_blob, pac_attrs_blob,
219 requester_sid_blob, NULL,
220 NULL, NULL, NULL,
221 *pac);
223 talloc_free(mem_ctx);
224 return ret;
227 static krb5_error_code samba_wdc_reget_pac2(astgs_request_t r,
228 const krb5_principal delegated_proxy_principal,
229 hdb_entry *client,
230 hdb_entry *server,
231 hdb_entry *krbtgt,
232 krb5_pac *pac,
233 krb5_cksumtype ctype,
234 const hdb_entry *device,
235 krb5_const_pac *device_pac)
237 krb5_context context = kdc_request_get_context((kdc_request_t)r);
238 struct samba_kdc_entry *client_skdc_entry = NULL;
239 struct samba_kdc_entry *server_skdc_entry =
240 talloc_get_type_abort(server->context, struct samba_kdc_entry);
241 struct samba_kdc_entry *krbtgt_skdc_entry =
242 talloc_get_type_abort(krbtgt->context, struct samba_kdc_entry);
243 TALLOC_CTX *mem_ctx = NULL;
244 krb5_pac new_pac = NULL;
245 krb5_error_code ret;
246 bool is_s4u2self = samba_wdc_is_s4u2self_req(r);
247 bool is_in_db = false;
248 bool is_untrusted = false;
249 uint32_t flags = 0;
250 PAC_OPTIONS_FLAGS pac_options = {};
252 ret = samba_wdc_pac_options(r, &pac_options);
253 if (ret != 0) {
254 return ret;
257 mem_ctx = talloc_named(NULL, 0, "samba_kdc_reget_pac2 context");
258 if (mem_ctx == NULL) {
259 return ENOMEM;
262 if (client != NULL) {
263 client_skdc_entry = talloc_get_type_abort(client->context,
264 struct samba_kdc_entry);
267 if (device != NULL) {
268 struct samba_kdc_entry *device_skdc_entry = NULL;
270 device_skdc_entry = talloc_get_type_abort(device->context,
271 struct samba_kdc_entry);
274 * Check the objectSID of the device and pac data are the same.
275 * Does a parse and SID check, but no crypto.
277 ret = samba_kdc_validate_pac_blob(context,
278 device_skdc_entry,
279 *device_pac);
280 if (ret != 0) {
281 talloc_free(mem_ctx);
282 return ret;
287 * If the krbtgt was generated by an RODC, and we are not that
288 * RODC, then we need to regenerate the PAC - we can't trust
289 * it, and confirm that the RODC was permitted to print this ticket
291 * Becasue of the samba_kdc_validate_pac_blob() step we can be
292 * sure that the record in 'client' matches the SID in the
293 * original PAC.
295 ret = samba_krbtgt_is_in_db(krbtgt_skdc_entry, &is_in_db, &is_untrusted);
296 if (ret != 0) {
297 goto out;
300 if (is_s4u2self) {
301 flags |= SAMBA_KDC_FLAG_PROTOCOL_TRANSITION;
304 if (delegated_proxy_principal != NULL) {
305 krb5_enctype etype;
306 Key *key = NULL;
308 if (!is_in_db) {
310 * The RODC-issued PAC was signed by a KDC entry that we
311 * don't have a key for. The server signature is not
312 * trustworthy, since it could have been created by the
313 * server we got the ticket from. We must not proceed as
314 * otherwise the ticket signature is unchecked.
316 ret = HDB_ERR_NOT_FOUND_HERE;
317 goto out;
320 /* Fetch the correct key depending on the checksum type. */
321 if (ctype == CKSUMTYPE_HMAC_MD5) {
322 etype = ENCTYPE_ARCFOUR_HMAC;
323 } else {
324 ret = krb5_cksumtype_to_enctype(context,
325 ctype,
326 &etype);
327 if (ret != 0) {
328 goto out;
331 ret = hdb_enctype2key(context, krbtgt, NULL, etype, &key);
332 if (ret != 0) {
333 goto out;
336 /* Check the KDC, whole-PAC and ticket signatures. */
337 ret = krb5_pac_verify(context,
338 *pac,
340 NULL,
341 NULL,
342 &key->key);
343 if (ret != 0) {
344 DEBUG(1, ("PAC KDC signature failed to verify\n"));
345 goto out;
348 flags |= SAMBA_KDC_FLAG_CONSTRAINED_DELEGATION;
351 if (is_untrusted) {
352 flags |= SAMBA_KDC_FLAG_KRBTGT_IS_UNTRUSTED;
355 if (is_in_db) {
356 flags |= SAMBA_KDC_FLAG_KRBTGT_IN_DB;
359 ret = krb5_pac_init(context, &new_pac);
360 if (ret != 0) {
361 new_pac = NULL;
362 goto out;
365 ret = samba_kdc_update_pac(mem_ctx,
366 context,
367 krbtgt_skdc_entry->kdc_db_ctx->samdb,
368 flags,
369 client_skdc_entry,
370 server->principal,
371 server_skdc_entry,
372 krbtgt_skdc_entry,
373 delegated_proxy_principal,
374 *pac,
375 new_pac);
376 if (ret != 0) {
377 krb5_pac_free(context, new_pac);
378 if (ret == ENOATTR) {
379 krb5_pac_free(context, *pac);
380 *pac = NULL;
381 ret = 0;
383 goto out;
386 /* Replace the pac */
387 krb5_pac_free(context, *pac);
388 *pac = new_pac;
390 out:
391 talloc_free(mem_ctx);
392 return ret;
395 /* Resign (and reform, including possibly new groups) a PAC */
397 static krb5_error_code samba_wdc_reget_pac(void *priv, astgs_request_t r,
398 const krb5_principal client_principal,
399 const krb5_principal delegated_proxy_principal,
400 hdb_entry *client,
401 hdb_entry *server,
402 hdb_entry *krbtgt,
403 krb5_pac *pac)
405 krb5_context context = kdc_request_get_context((kdc_request_t)r);
406 krb5_kdc_configuration *config = kdc_request_get_config((kdc_request_t)r);
407 struct samba_kdc_entry *krbtgt_skdc_entry =
408 talloc_get_type_abort(krbtgt->context,
409 struct samba_kdc_entry);
410 krb5_error_code ret;
411 krb5_cksumtype ctype = CKSUMTYPE_NONE;
412 hdb_entry signing_krbtgt_hdb;
413 const hdb_entry *explicit_armor_client =
414 kdc_request_get_explicit_armor_client(r);
415 krb5_const_pac explicit_armor_pac =
416 kdc_request_get_explicit_armor_pac(r);
418 if (delegated_proxy_principal) {
419 uint16_t rodc_id;
420 unsigned int my_krbtgt_number;
423 * We're using delegated_proxy_principal for the moment to
424 * indicate cases where the ticket was encrypted with the server
425 * key, and not a krbtgt key. This cannot be trusted, so we need
426 * to find a krbtgt key that signs the PAC in order to trust the
427 * ticket.
429 * The krbtgt passed in to this function refers to the krbtgt
430 * used to decrypt the ticket of the server requesting
431 * S4U2Proxy.
433 * When we implement service ticket renewal, we need to check
434 * the PAC, and this will need to be updated.
436 ret = krb5_pac_get_kdc_checksum_info(context,
437 *pac,
438 &ctype,
439 &rodc_id);
440 if (ret != 0) {
441 DEBUG(1, ("Failed to get PAC checksum info\n"));
442 return ret;
446 * We need to check the KDC and ticket signatures, fetching the
447 * correct key based on the enctype.
450 my_krbtgt_number = krbtgt_skdc_entry->kdc_db_ctx->my_krbtgt_number;
452 if (my_krbtgt_number != 0) {
454 * If we are an RODC, and we are not the KDC that signed
455 * the evidence ticket, then we need to proxy the
456 * request.
458 if (rodc_id != my_krbtgt_number) {
459 return HDB_ERR_NOT_FOUND_HERE;
461 } else {
463 * If we are a DC, the ticket may have been signed by a
464 * different KDC than the one that issued the header
465 * ticket.
467 if (rodc_id != krbtgt->kvno >> 16) {
468 struct sdb_entry signing_krbtgt_sdb;
471 * If we didn't sign the ticket, then return an
472 * error.
474 if (rodc_id != 0) {
475 return KRB5KRB_AP_ERR_MODIFIED;
479 * Fetch our key from the database. To support
480 * key rollover, we're going to need to try
481 * multiple keys by trial and error. For now,
482 * krbtgt keys aren't assumed to change.
484 ret = samba_kdc_fetch(context,
485 krbtgt_skdc_entry->kdc_db_ctx,
486 krbtgt->principal,
487 SDB_F_GET_KRBTGT | SDB_F_CANON,
489 &signing_krbtgt_sdb);
490 if (ret != 0) {
491 return ret;
494 ret = sdb_entry_to_hdb_entry(context,
495 &signing_krbtgt_sdb,
496 &signing_krbtgt_hdb);
497 sdb_entry_free(&signing_krbtgt_sdb);
498 if (ret != 0) {
499 return ret;
503 * Replace the krbtgt entry with our own entry
504 * for further processing.
506 krbtgt = &signing_krbtgt_hdb;
509 } else if (!krbtgt_skdc_entry->is_trust) {
511 * We expect to have received a TGT, so check that we haven't
512 * been given a kpasswd ticket instead. We don't need to do this
513 * check for an incoming trust, as they use a different secret
514 * and can't be confused with a normal TGT.
516 krb5_ticket *tgt = kdc_request_get_ticket(r);
518 struct timeval now = krb5_kdc_get_time();
521 * Check if the ticket is in the last two minutes of its
522 * life.
524 KerberosTime lifetime = rk_time_sub(tgt->ticket.endtime, now.tv_sec);
525 if (lifetime <= CHANGEPW_LIFETIME) {
527 * This ticket has at most two minutes left to live. It
528 * may be a kpasswd ticket rather than a TGT, so don't
529 * accept it.
531 kdc_audit_addreason((kdc_request_t)r,
532 "Ticket is not a ticket-granting ticket");
533 return KRB5KRB_AP_ERR_TKT_EXPIRED;
537 ret = samba_wdc_reget_pac2(r,
538 delegated_proxy_principal,
539 client,
540 server,
541 krbtgt,
542 pac,
543 ctype,
544 explicit_armor_client,
545 &explicit_armor_pac);
547 if (krbtgt == &signing_krbtgt_hdb) {
548 hdb_free_entry(context, config->db[0], &signing_krbtgt_hdb);
551 return ret;
554 static char *get_netbios_name(TALLOC_CTX *mem_ctx, HostAddresses *addrs)
556 char *nb_name = NULL;
557 size_t len;
558 unsigned int i;
560 for (i = 0; addrs && i < addrs->len; i++) {
561 if (addrs->val[i].addr_type != KRB5_ADDRESS_NETBIOS) {
562 continue;
564 len = MIN(addrs->val[i].address.length, 15);
565 nb_name = talloc_strndup(mem_ctx,
566 addrs->val[i].address.data, len);
567 if (nb_name) {
568 break;
572 if ((nb_name == NULL) || (nb_name[0] == '\0')) {
573 return NULL;
576 /* Strip space padding */
577 for (len = strlen(nb_name) - 1;
578 (len > 0) && (nb_name[len] == ' ');
579 --len) {
580 nb_name[len] = '\0';
583 return nb_name;
586 /* this function allocates 'data' using malloc.
587 * The caller is responsible for freeing it */
588 static void samba_kdc_build_edata_reply(NTSTATUS nt_status, krb5_data *e_data)
590 e_data->data = malloc(12);
591 if (e_data->data == NULL) {
592 e_data->length = 0;
593 e_data->data = NULL;
594 return;
596 e_data->length = 12;
598 SIVAL(e_data->data, 0, NT_STATUS_V(nt_status));
599 SIVAL(e_data->data, 4, 0);
600 SIVAL(e_data->data, 8, 1);
602 return;
605 static krb5_error_code samba_wdc_check_client_access(void *priv,
606 astgs_request_t r)
608 struct samba_kdc_entry *kdc_entry;
609 bool password_change;
610 char *workstation;
611 NTSTATUS nt_status;
614 kdc_entry = talloc_get_type(kdc_request_get_client(r)->context, struct samba_kdc_entry);
615 password_change = (kdc_request_get_server(r) && kdc_request_get_server(r)->flags.change_pw);
616 workstation = get_netbios_name((TALLOC_CTX *)kdc_request_get_client(r)->context,
617 kdc_request_get_req(r)->req_body.addresses);
619 nt_status = samba_kdc_check_client_access(kdc_entry,
620 kdc_request_get_cname((kdc_request_t)r),
621 workstation,
622 password_change);
624 if (!NT_STATUS_IS_OK(nt_status)) {
625 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_MEMORY)) {
626 return ENOMEM;
629 if (kdc_request_get_rep(r)->padata) {
630 int ret;
631 krb5_data kd;
633 samba_kdc_build_edata_reply(nt_status, &kd);
634 ret = krb5_padata_add(kdc_request_get_context((kdc_request_t)r), kdc_request_get_rep(r)->padata,
635 KRB5_PADATA_PW_SALT,
636 kd.data, kd.length);
637 if (ret != 0) {
639 * So we do not leak the allocated
640 * memory on kd in the error case
642 krb5_data_free(&kd);
646 return samba_kdc_map_policy_err(nt_status);
649 /* Now do the standard Heimdal check */
650 return KRB5_PLUGIN_NO_HANDLE;
653 /* this function allocates 'data' using malloc.
654 * The caller is responsible for freeing it */
655 static krb5_error_code samba_kdc_build_supported_etypes(uint32_t supported_etypes,
656 krb5_data *e_data)
658 e_data->data = malloc(4);
659 if (e_data->data == NULL) {
660 return ENOMEM;
662 e_data->length = 4;
664 PUSH_LE_U32(e_data->data, 0, supported_etypes);
666 return 0;
669 static krb5_error_code samba_wdc_finalize_reply(void *priv,
670 astgs_request_t r)
672 struct samba_kdc_entry *server_kdc_entry;
673 uint32_t supported_enctypes;
675 server_kdc_entry = talloc_get_type(kdc_request_get_server(r)->context, struct samba_kdc_entry);
678 * If the canonicalize flag is set, add PA-SUPPORTED-ENCTYPES padata
679 * type to indicate what encryption types the server supports.
681 supported_enctypes = server_kdc_entry->supported_enctypes;
682 if (kdc_request_get_req(r)->req_body.kdc_options.canonicalize && supported_enctypes != 0) {
683 krb5_error_code ret;
685 PA_DATA md;
687 ret = samba_kdc_build_supported_etypes(supported_enctypes, &md.padata_value);
688 if (ret != 0) {
689 return ret;
692 md.padata_type = KRB5_PADATA_SUPPORTED_ETYPES;
694 ret = kdc_request_add_encrypted_padata(r, &md);
695 if (ret != 0) {
697 * So we do not leak the allocated
698 * memory on kd in the error case
700 krb5_data_free(&md.padata_value);
704 return 0;
707 static krb5_error_code samba_wdc_plugin_init(krb5_context context, void **ptr)
709 *ptr = NULL;
710 return 0;
713 static void samba_wdc_plugin_fini(void *ptr)
715 return;
718 static krb5_error_code samba_wdc_referral_policy(void *priv,
719 astgs_request_t r)
721 return kdc_request_get_error_code((kdc_request_t)r);
724 struct krb5plugin_kdc_ftable kdc_plugin_table = {
725 .minor_version = KRB5_PLUGIN_KDC_VERSION_10,
726 .init = samba_wdc_plugin_init,
727 .fini = samba_wdc_plugin_fini,
728 .pac_verify = samba_wdc_reget_pac,
729 .client_access = samba_wdc_check_client_access,
730 .finalize_reply = samba_wdc_finalize_reply,
731 .pac_generate = samba_wdc_get_pac,
732 .referral_policy = samba_wdc_referral_policy,