kdc: remove KRB5SignedPath, to be replaced with PAC
[Samba.git] / source4 / heimdal / kdc / krb5tgs.c
blobc6bab82f5173be3157441940fb121125bfd17ddb
1 /*
2 * Copyright (c) 1997-2008 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the Institute nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
34 #include "kdc_locl.h"
37 * return the realm of a krbtgt-ticket or NULL
40 static Realm
41 get_krbtgt_realm(const PrincipalName *p)
43 if(p->name_string.len == 2
44 && strcmp(p->name_string.val[0], KRB5_TGS_NAME) == 0)
45 return p->name_string.val[1];
46 else
47 return NULL;
54 static krb5_error_code
55 check_PAC(krb5_context context,
56 krb5_kdc_configuration *config,
57 const krb5_principal client_principal,
58 const krb5_principal delegated_proxy_principal,
59 hdb_entry_ex *client,
60 hdb_entry_ex *server,
61 hdb_entry_ex *krbtgt,
62 const EncryptionKey *server_check_key,
63 const EncryptionKey *server_sign_key,
64 const EncryptionKey *krbtgt_sign_key,
65 EncTicketPart *tkt,
66 krb5_data *rspac,
67 int *signedpath)
69 AuthorizationData *ad = tkt->authorization_data;
70 unsigned i, j;
71 krb5_error_code ret;
73 if (ad == NULL || ad->len == 0)
74 return 0;
76 for (i = 0; i < ad->len; i++) {
77 AuthorizationData child;
79 if (ad->val[i].ad_type != KRB5_AUTHDATA_IF_RELEVANT)
80 continue;
82 ret = decode_AuthorizationData(ad->val[i].ad_data.data,
83 ad->val[i].ad_data.length,
84 &child,
85 NULL);
86 if (ret) {
87 krb5_set_error_message(context, ret, "Failed to decode "
88 "IF_RELEVANT with %d", ret);
89 return ret;
91 for (j = 0; j < child.len; j++) {
93 if (child.val[j].ad_type == KRB5_AUTHDATA_WIN2K_PAC) {
94 int signed_pac = 0;
95 krb5_pac pac;
97 /* Found PAC */
98 ret = krb5_pac_parse(context,
99 child.val[j].ad_data.data,
100 child.val[j].ad_data.length,
101 &pac);
102 free_AuthorizationData(&child);
103 if (ret)
104 return ret;
106 ret = krb5_pac_verify(context, pac, tkt->authtime,
107 client_principal,
108 server_check_key, NULL);
109 if (ret) {
110 krb5_pac_free(context, pac);
111 return ret;
114 ret = _kdc_pac_verify(context, client_principal,
115 delegated_proxy_principal,
116 client, server, krbtgt, &pac, &signed_pac);
117 if (ret) {
118 krb5_pac_free(context, pac);
119 return ret;
123 * Only re-sign PAC if we could verify it with the PAC
124 * function. The no-verify case happens when we get in
125 * a PAC from cross realm from a Windows domain and
126 * that there is no PAC verification function.
128 if (signed_pac) {
129 *signedpath = 1;
130 ret = _krb5_pac_sign(context, pac, tkt->authtime,
131 client_principal,
132 server_sign_key, krbtgt_sign_key, rspac);
134 krb5_pac_free(context, pac);
136 return ret;
139 free_AuthorizationData(&child);
141 return 0;
148 static krb5_error_code
149 check_tgs_flags(krb5_context context,
150 krb5_kdc_configuration *config,
151 KDC_REQ_BODY *b, const EncTicketPart *tgt, EncTicketPart *et)
153 KDCOptions f = b->kdc_options;
155 if(f.validate){
156 if(!tgt->flags.invalid || tgt->starttime == NULL){
157 kdc_log(context, config, 0,
158 "Bad request to validate ticket");
159 return KRB5KDC_ERR_BADOPTION;
161 if(*tgt->starttime > kdc_time){
162 kdc_log(context, config, 0,
163 "Early request to validate ticket");
164 return KRB5KRB_AP_ERR_TKT_NYV;
166 /* XXX tkt = tgt */
167 et->flags.invalid = 0;
168 }else if(tgt->flags.invalid){
169 kdc_log(context, config, 0,
170 "Ticket-granting ticket has INVALID flag set");
171 return KRB5KRB_AP_ERR_TKT_INVALID;
174 if(f.forwardable){
175 if(!tgt->flags.forwardable){
176 kdc_log(context, config, 0,
177 "Bad request for forwardable ticket");
178 return KRB5KDC_ERR_BADOPTION;
180 et->flags.forwardable = 1;
182 if(f.forwarded){
183 if(!tgt->flags.forwardable){
184 kdc_log(context, config, 0,
185 "Request to forward non-forwardable ticket");
186 return KRB5KDC_ERR_BADOPTION;
188 et->flags.forwarded = 1;
189 et->caddr = b->addresses;
191 if(tgt->flags.forwarded)
192 et->flags.forwarded = 1;
194 if(f.proxiable){
195 if(!tgt->flags.proxiable){
196 kdc_log(context, config, 0,
197 "Bad request for proxiable ticket");
198 return KRB5KDC_ERR_BADOPTION;
200 et->flags.proxiable = 1;
202 if(f.proxy){
203 if(!tgt->flags.proxiable){
204 kdc_log(context, config, 0,
205 "Request to proxy non-proxiable ticket");
206 return KRB5KDC_ERR_BADOPTION;
208 et->flags.proxy = 1;
209 et->caddr = b->addresses;
211 if(tgt->flags.proxy)
212 et->flags.proxy = 1;
214 if(f.allow_postdate){
215 if(!tgt->flags.may_postdate){
216 kdc_log(context, config, 0,
217 "Bad request for post-datable ticket");
218 return KRB5KDC_ERR_BADOPTION;
220 et->flags.may_postdate = 1;
222 if(f.postdated){
223 if(!tgt->flags.may_postdate){
224 kdc_log(context, config, 0,
225 "Bad request for postdated ticket");
226 return KRB5KDC_ERR_BADOPTION;
228 if(b->from)
229 *et->starttime = *b->from;
230 et->flags.postdated = 1;
231 et->flags.invalid = 1;
232 }else if(b->from && *b->from > kdc_time + context->max_skew){
233 kdc_log(context, config, 0, "Ticket cannot be postdated");
234 return KRB5KDC_ERR_CANNOT_POSTDATE;
237 if(f.renewable){
238 if(!tgt->flags.renewable || tgt->renew_till == NULL){
239 kdc_log(context, config, 0,
240 "Bad request for renewable ticket");
241 return KRB5KDC_ERR_BADOPTION;
243 et->flags.renewable = 1;
244 ALLOC(et->renew_till);
245 _kdc_fix_time(&b->rtime);
246 *et->renew_till = *b->rtime;
248 if(f.renew){
249 time_t old_life;
250 if(!tgt->flags.renewable || tgt->renew_till == NULL){
251 kdc_log(context, config, 0,
252 "Request to renew non-renewable ticket");
253 return KRB5KDC_ERR_BADOPTION;
255 old_life = tgt->endtime;
256 if(tgt->starttime)
257 old_life -= *tgt->starttime;
258 else
259 old_life -= tgt->authtime;
260 et->endtime = *et->starttime + old_life;
261 if (et->renew_till != NULL)
262 et->endtime = min(*et->renew_till, et->endtime);
265 #if 0
266 /* checks for excess flags */
267 if(f.request_anonymous && !config->allow_anonymous){
268 kdc_log(context, config, 0,
269 "Request for anonymous ticket");
270 return KRB5KDC_ERR_BADOPTION;
272 #endif
273 return 0;
277 * Determine if constrained delegation is allowed from this client to this server
280 static krb5_error_code
281 check_constrained_delegation(krb5_context context,
282 krb5_kdc_configuration *config,
283 HDB *clientdb,
284 hdb_entry_ex *client,
285 hdb_entry_ex *server,
286 krb5_const_principal target)
288 const HDB_Ext_Constrained_delegation_acl *acl;
289 krb5_error_code ret;
290 size_t i;
293 * constrained_delegation (S4U2Proxy) only works within
294 * the same realm. We use the already canonicalized version
295 * of the principals here, while "target" is the principal
296 * provided by the client.
298 if(!krb5_realm_compare(context, client->entry.principal, server->entry.principal)) {
299 ret = KRB5KDC_ERR_BADOPTION;
300 kdc_log(context, config, 0,
301 "Bad request for constrained delegation");
302 return ret;
305 if (clientdb->hdb_check_constrained_delegation) {
306 ret = clientdb->hdb_check_constrained_delegation(context, clientdb, client, target);
307 if (ret == 0)
308 return 0;
309 } else {
310 /* if client delegates to itself, that ok */
311 if (krb5_principal_compare(context, client->entry.principal, server->entry.principal) == TRUE)
312 return 0;
314 ret = hdb_entry_get_ConstrainedDelegACL(&client->entry, &acl);
315 if (ret) {
316 krb5_clear_error_message(context);
317 return ret;
320 if (acl) {
321 for (i = 0; i < acl->len; i++) {
322 if (krb5_principal_compare(context, target, &acl->val[i]) == TRUE)
323 return 0;
326 ret = KRB5KDC_ERR_BADOPTION;
328 kdc_log(context, config, 0,
329 "Bad request for constrained delegation");
330 return ret;
334 * Determine if s4u2self is allowed from this client to this server
336 * For example, regardless of the principal being impersonated, if the
337 * 'client' and 'server' are the same, then it's safe.
340 static krb5_error_code
341 check_s4u2self(krb5_context context,
342 krb5_kdc_configuration *config,
343 HDB *clientdb,
344 hdb_entry_ex *client,
345 krb5_const_principal server)
347 krb5_error_code ret;
349 /* if client does a s4u2self to itself, that ok */
350 if (krb5_principal_compare(context, client->entry.principal, server) == TRUE)
351 return 0;
353 if (clientdb->hdb_check_s4u2self) {
354 ret = clientdb->hdb_check_s4u2self(context, clientdb, client, server);
355 if (ret == 0)
356 return 0;
357 } else {
358 ret = KRB5KDC_ERR_BADOPTION;
360 return ret;
367 static krb5_error_code
368 verify_flags (krb5_context context,
369 krb5_kdc_configuration *config,
370 const EncTicketPart *et,
371 const char *pstr)
373 if(et->endtime < kdc_time){
374 kdc_log(context, config, 0, "Ticket expired (%s)", pstr);
375 return KRB5KRB_AP_ERR_TKT_EXPIRED;
377 if(et->flags.invalid){
378 kdc_log(context, config, 0, "Ticket not valid (%s)", pstr);
379 return KRB5KRB_AP_ERR_TKT_NYV;
381 return 0;
388 static krb5_error_code
389 fix_transited_encoding(krb5_context context,
390 krb5_kdc_configuration *config,
391 krb5_boolean check_policy,
392 const TransitedEncoding *tr,
393 EncTicketPart *et,
394 const char *client_realm,
395 const char *server_realm,
396 const char *tgt_realm)
398 krb5_error_code ret = 0;
399 char **realms, **tmp;
400 unsigned int num_realms;
401 size_t i;
403 switch (tr->tr_type) {
404 case DOMAIN_X500_COMPRESS:
405 break;
406 case 0:
408 * Allow empty content of type 0 because that is was Microsoft
409 * generates in their TGT.
411 if (tr->contents.length == 0)
412 break;
413 kdc_log(context, config, 0,
414 "Transited type 0 with non empty content");
415 return KRB5KDC_ERR_TRTYPE_NOSUPP;
416 default:
417 kdc_log(context, config, 0,
418 "Unknown transited type: %u", tr->tr_type);
419 return KRB5KDC_ERR_TRTYPE_NOSUPP;
422 ret = krb5_domain_x500_decode(context,
423 tr->contents,
424 &realms,
425 &num_realms,
426 client_realm,
427 server_realm);
428 if(ret){
429 krb5_warn(context, ret,
430 "Decoding transited encoding");
431 return ret;
433 if(strcmp(client_realm, tgt_realm) && strcmp(server_realm, tgt_realm)) {
434 /* not us, so add the previous realm to transited set */
435 if (num_realms + 1 > UINT_MAX/sizeof(*realms)) {
436 ret = ERANGE;
437 goto free_realms;
439 tmp = realloc(realms, (num_realms + 1) * sizeof(*realms));
440 if(tmp == NULL){
441 ret = ENOMEM;
442 goto free_realms;
444 realms = tmp;
445 realms[num_realms] = strdup(tgt_realm);
446 if(realms[num_realms] == NULL){
447 ret = ENOMEM;
448 goto free_realms;
450 num_realms++;
452 if(num_realms == 0) {
453 if(strcmp(client_realm, server_realm))
454 kdc_log(context, config, 0,
455 "cross-realm %s -> %s", client_realm, server_realm);
456 } else {
457 size_t l = 0;
458 char *rs;
459 for(i = 0; i < num_realms; i++)
460 l += strlen(realms[i]) + 2;
461 rs = malloc(l);
462 if(rs != NULL) {
463 *rs = '\0';
464 for(i = 0; i < num_realms; i++) {
465 if(i > 0)
466 strlcat(rs, ", ", l);
467 strlcat(rs, realms[i], l);
469 kdc_log(context, config, 0,
470 "cross-realm %s -> %s via [%s]",
471 client_realm, server_realm, rs);
472 free(rs);
475 if(check_policy) {
476 ret = krb5_check_transited(context, client_realm,
477 server_realm,
478 realms, num_realms, NULL);
479 if(ret) {
480 krb5_warn(context, ret, "cross-realm %s -> %s",
481 client_realm, server_realm);
482 goto free_realms;
484 et->flags.transited_policy_checked = 1;
486 et->transited.tr_type = DOMAIN_X500_COMPRESS;
487 ret = krb5_domain_x500_encode(realms, num_realms, &et->transited.contents);
488 if(ret)
489 krb5_warn(context, ret, "Encoding transited encoding");
490 free_realms:
491 for(i = 0; i < num_realms; i++)
492 free(realms[i]);
493 free(realms);
494 return ret;
498 static krb5_error_code
499 tgs_make_reply(krb5_context context,
500 krb5_kdc_configuration *config,
501 KDC_REQ_BODY *b,
502 krb5_const_principal tgt_name,
503 const EncTicketPart *tgt,
504 const krb5_keyblock *replykey,
505 int rk_is_subkey,
506 const EncryptionKey *serverkey,
507 const krb5_keyblock *sessionkey,
508 krb5_kvno kvno,
509 AuthorizationData *auth_data,
510 hdb_entry_ex *server,
511 krb5_principal server_principal,
512 const char *server_name,
513 hdb_entry_ex *client,
514 krb5_principal client_principal,
515 hdb_entry_ex *krbtgt,
516 krb5_enctype krbtgt_etype,
517 const krb5_data *rspac,
518 const METHOD_DATA *enc_pa_data,
519 const char **e_text,
520 krb5_data *reply)
522 KDC_REP rep;
523 EncKDCRepPart ek;
524 EncTicketPart et;
525 KDCOptions f = b->kdc_options;
526 krb5_error_code ret;
527 int is_weak = 0;
529 memset(&rep, 0, sizeof(rep));
530 memset(&et, 0, sizeof(et));
531 memset(&ek, 0, sizeof(ek));
533 rep.pvno = 5;
534 rep.msg_type = krb_tgs_rep;
536 et.authtime = tgt->authtime;
537 _kdc_fix_time(&b->till);
538 et.endtime = min(tgt->endtime, *b->till);
539 ALLOC(et.starttime);
540 *et.starttime = kdc_time;
542 ret = check_tgs_flags(context, config, b, tgt, &et);
543 if(ret)
544 goto out;
546 /* We should check the transited encoding if:
547 1) the request doesn't ask not to be checked
548 2) globally enforcing a check
549 3) principal requires checking
550 4) we allow non-check per-principal, but principal isn't marked as allowing this
551 5) we don't globally allow this
554 #define GLOBAL_FORCE_TRANSITED_CHECK \
555 (config->trpolicy == TRPOLICY_ALWAYS_CHECK)
556 #define GLOBAL_ALLOW_PER_PRINCIPAL \
557 (config->trpolicy == TRPOLICY_ALLOW_PER_PRINCIPAL)
558 #define GLOBAL_ALLOW_DISABLE_TRANSITED_CHECK \
559 (config->trpolicy == TRPOLICY_ALWAYS_HONOUR_REQUEST)
561 /* these will consult the database in future release */
562 #define PRINCIPAL_FORCE_TRANSITED_CHECK(P) 0
563 #define PRINCIPAL_ALLOW_DISABLE_TRANSITED_CHECK(P) 0
565 ret = fix_transited_encoding(context, config,
566 !f.disable_transited_check ||
567 GLOBAL_FORCE_TRANSITED_CHECK ||
568 PRINCIPAL_FORCE_TRANSITED_CHECK(server) ||
569 !((GLOBAL_ALLOW_PER_PRINCIPAL &&
570 PRINCIPAL_ALLOW_DISABLE_TRANSITED_CHECK(server)) ||
571 GLOBAL_ALLOW_DISABLE_TRANSITED_CHECK),
572 &tgt->transited, &et,
573 krb5_principal_get_realm(context, client_principal),
574 krb5_principal_get_realm(context, server->entry.principal),
575 krb5_principal_get_realm(context, krbtgt->entry.principal));
576 if(ret)
577 goto out;
579 copy_Realm(&server_principal->realm, &rep.ticket.realm);
580 _krb5_principal2principalname(&rep.ticket.sname, server_principal);
581 copy_Realm(&tgt_name->realm, &rep.crealm);
583 if (f.request_anonymous)
584 _kdc_make_anonymous_principalname (&rep.cname);
585 else */
587 copy_PrincipalName(&tgt_name->name, &rep.cname);
588 rep.ticket.tkt_vno = 5;
590 ek.caddr = et.caddr;
591 if(et.caddr == NULL)
592 et.caddr = tgt->caddr;
595 time_t life;
596 life = et.endtime - *et.starttime;
597 if(client && client->entry.max_life)
598 life = min(life, *client->entry.max_life);
599 if(server->entry.max_life)
600 life = min(life, *server->entry.max_life);
601 et.endtime = *et.starttime + life;
603 if(f.renewable_ok && tgt->flags.renewable &&
604 et.renew_till == NULL && et.endtime < *b->till &&
605 tgt->renew_till != NULL)
607 et.flags.renewable = 1;
608 ALLOC(et.renew_till);
609 *et.renew_till = *b->till;
611 if(et.renew_till){
612 time_t renew;
613 renew = *et.renew_till - et.authtime;
614 if(client && client->entry.max_renew)
615 renew = min(renew, *client->entry.max_renew);
616 if(server->entry.max_renew)
617 renew = min(renew, *server->entry.max_renew);
618 *et.renew_till = et.authtime + renew;
621 if(et.renew_till){
622 *et.renew_till = min(*et.renew_till, *tgt->renew_till);
623 *et.starttime = min(*et.starttime, *et.renew_till);
624 et.endtime = min(et.endtime, *et.renew_till);
627 *et.starttime = min(*et.starttime, et.endtime);
629 if(*et.starttime == et.endtime){
630 ret = KRB5KDC_ERR_NEVER_VALID;
631 goto out;
633 if(et.renew_till && et.endtime == *et.renew_till){
634 free(et.renew_till);
635 et.renew_till = NULL;
636 et.flags.renewable = 0;
639 et.flags.pre_authent = tgt->flags.pre_authent;
640 et.flags.hw_authent = tgt->flags.hw_authent;
641 et.flags.anonymous = tgt->flags.anonymous;
642 et.flags.ok_as_delegate = server->entry.flags.ok_as_delegate;
644 /* See MS-KILE 3.3.5.1 */
645 if (!server->entry.flags.forwardable)
646 et.flags.forwardable = 0;
647 if (!server->entry.flags.proxiable)
648 et.flags.proxiable = 0;
650 if(rspac->length) {
652 * No not need to filter out the any PAC from the
653 * auth_data since it's signed by the KDC.
655 ret = _kdc_tkt_add_if_relevant_ad(context, &et,
656 KRB5_AUTHDATA_WIN2K_PAC, rspac);
657 if (ret)
658 goto out;
661 if (auth_data) {
662 unsigned int i = 0;
664 /* XXX check authdata */
666 if (et.authorization_data == NULL) {
667 et.authorization_data = calloc(1, sizeof(*et.authorization_data));
668 if (et.authorization_data == NULL) {
669 ret = ENOMEM;
670 krb5_set_error_message(context, ret, "malloc: out of memory");
671 goto out;
674 for(i = 0; i < auth_data->len ; i++) {
675 ret = add_AuthorizationData(et.authorization_data, &auth_data->val[i]);
676 if (ret) {
677 krb5_set_error_message(context, ret, "malloc: out of memory");
678 goto out;
683 ret = krb5_copy_keyblock_contents(context, sessionkey, &et.key);
684 if (ret)
685 goto out;
686 et.crealm = tgt_name->realm;
687 et.cname = tgt_name->name;
689 ek.key = et.key;
690 /* MIT must have at least one last_req */
691 ek.last_req.len = 1;
692 ek.last_req.val = calloc(1, sizeof(*ek.last_req.val));
693 if (ek.last_req.val == NULL) {
694 ret = ENOMEM;
695 goto out;
697 ek.nonce = b->nonce;
698 ek.flags = et.flags;
699 ek.authtime = et.authtime;
700 ek.starttime = et.starttime;
701 ek.endtime = et.endtime;
702 ek.renew_till = et.renew_till;
703 ek.srealm = rep.ticket.realm;
704 ek.sname = rep.ticket.sname;
706 _kdc_log_timestamp(context, config, "TGS-REQ", et.authtime, et.starttime,
707 et.endtime, et.renew_till);
709 if (enc_pa_data->len) {
710 rep.padata = calloc(1, sizeof(*rep.padata));
711 if (rep.padata == NULL) {
712 ret = ENOMEM;
713 goto out;
715 ret = copy_METHOD_DATA(enc_pa_data, rep.padata);
716 if (ret)
717 goto out;
720 if (krb5_enctype_valid(context, et.key.keytype) != 0
721 && _kdc_is_weak_exception(server->entry.principal, et.key.keytype))
723 krb5_enctype_enable(context, et.key.keytype);
724 is_weak = 1;
728 /* It is somewhat unclear where the etype in the following
729 encryption should come from. What we have is a session
730 key in the passed tgt, and a list of preferred etypes
731 *for the new ticket*. Should we pick the best possible
732 etype, given the keytype in the tgt, or should we look
733 at the etype list here as well? What if the tgt
734 session key is DES3 and we want a ticket with a (say)
735 CAST session key. Should the DES3 etype be added to the
736 etype list, even if we don't want a session key with
737 DES3? */
738 ret = _kdc_encode_reply(context, config,
739 &rep, &et, &ek, et.key.keytype,
740 kvno,
741 serverkey, 0, replykey, rk_is_subkey,
742 e_text, reply);
743 if (is_weak)
744 krb5_enctype_disable(context, et.key.keytype);
746 out:
747 free_TGS_REP(&rep);
748 free_TransitedEncoding(&et.transited);
749 if(et.starttime)
750 free(et.starttime);
751 if(et.renew_till)
752 free(et.renew_till);
753 if(et.authorization_data) {
754 free_AuthorizationData(et.authorization_data);
755 free(et.authorization_data);
757 free_LastReq(&ek.last_req);
758 memset(et.key.keyvalue.data, 0, et.key.keyvalue.length);
759 free_EncryptionKey(&et.key);
760 return ret;
763 static krb5_error_code
764 tgs_check_authenticator(krb5_context context,
765 krb5_kdc_configuration *config,
766 krb5_auth_context ac,
767 KDC_REQ_BODY *b,
768 const char **e_text,
769 krb5_keyblock *key)
771 krb5_authenticator auth;
772 size_t len = 0;
773 unsigned char *buf;
774 size_t buf_size;
775 krb5_error_code ret;
776 krb5_crypto crypto;
778 krb5_auth_con_getauthenticator(context, ac, &auth);
779 if(auth->cksum == NULL){
780 kdc_log(context, config, 0, "No authenticator in request");
781 ret = KRB5KRB_AP_ERR_INAPP_CKSUM;
782 goto out;
785 * according to RFC1510 it doesn't need to be keyed,
786 * but according to the latest draft it needs to.
788 if (
789 #if 0
790 !krb5_checksum_is_keyed(context, auth->cksum->cksumtype)
792 #endif
793 !krb5_checksum_is_collision_proof(context, auth->cksum->cksumtype)) {
794 kdc_log(context, config, 0, "Bad checksum type in authenticator: %d",
795 auth->cksum->cksumtype);
796 ret = KRB5KRB_AP_ERR_INAPP_CKSUM;
797 goto out;
800 /* XXX should not re-encode this */
801 ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, b, &len, ret);
802 if(ret){
803 const char *msg = krb5_get_error_message(context, ret);
804 kdc_log(context, config, 0, "Failed to encode KDC-REQ-BODY: %s", msg);
805 krb5_free_error_message(context, msg);
806 goto out;
808 if(buf_size != len) {
809 free(buf);
810 kdc_log(context, config, 0, "Internal error in ASN.1 encoder");
811 *e_text = "KDC internal error";
812 ret = KRB5KRB_ERR_GENERIC;
813 goto out;
815 ret = krb5_crypto_init(context, key, 0, &crypto);
816 if (ret) {
817 const char *msg = krb5_get_error_message(context, ret);
818 free(buf);
819 kdc_log(context, config, 0, "krb5_crypto_init failed: %s", msg);
820 krb5_free_error_message(context, msg);
821 goto out;
823 ret = krb5_verify_checksum(context,
824 crypto,
825 KRB5_KU_TGS_REQ_AUTH_CKSUM,
826 buf,
827 len,
828 auth->cksum);
829 free(buf);
830 krb5_crypto_destroy(context, crypto);
831 if(ret){
832 const char *msg = krb5_get_error_message(context, ret);
833 kdc_log(context, config, 0,
834 "Failed to verify authenticator checksum: %s", msg);
835 krb5_free_error_message(context, msg);
837 out:
838 free_Authenticator(auth);
839 free(auth);
840 return ret;
847 static const char *
848 find_rpath(krb5_context context, Realm crealm, Realm srealm)
850 const char *new_realm = krb5_config_get_string(context,
851 NULL,
852 "capaths",
853 crealm,
854 srealm,
855 NULL);
856 return new_realm;
860 static krb5_boolean
861 need_referral(krb5_context context, krb5_kdc_configuration *config,
862 const KDCOptions * const options, krb5_principal server,
863 krb5_realm **realms)
865 const char *name;
867 if(!options->canonicalize && server->name.name_type != KRB5_NT_SRV_INST)
868 return FALSE;
870 if (server->name.name_string.len == 1)
871 name = server->name.name_string.val[0];
872 else if (server->name.name_string.len == 3) {
874 This is used to give referrals for the
875 E3514235-4B06-11D1-AB04-00C04FC2DCD2/NTDSGUID/DNSDOMAIN
876 SPN form, which is used for inter-domain communication in AD
878 name = server->name.name_string.val[2];
879 kdc_log(context, config, 0, "Giving 3 part referral for %s", name);
880 *realms = malloc(sizeof(char *)*2);
881 if (*realms == NULL) {
882 krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
883 return FALSE;
885 (*realms)[0] = strdup(name);
886 (*realms)[1] = NULL;
887 return TRUE;
888 } else if (server->name.name_string.len > 1)
889 name = server->name.name_string.val[1];
890 else
891 return FALSE;
893 kdc_log(context, config, 0, "Searching referral for %s", name);
895 return _krb5_get_host_realm_int(context, name, FALSE, realms) == 0;
898 static krb5_error_code
899 tgs_parse_request(krb5_context context,
900 krb5_kdc_configuration *config,
901 KDC_REQ_BODY *b,
902 const PA_DATA *tgs_req,
903 hdb_entry_ex **krbtgt,
904 krb5_enctype *krbtgt_etype,
905 krb5_ticket **ticket,
906 const char **e_text,
907 const char *from,
908 const struct sockaddr *from_addr,
909 time_t **csec,
910 int **cusec,
911 AuthorizationData **auth_data,
912 krb5_keyblock **replykey,
913 int *rk_is_subkey)
915 static char failed[] = "<unparse_name failed>";
916 krb5_ap_req ap_req;
917 krb5_error_code ret;
918 krb5_principal princ;
919 krb5_auth_context ac = NULL;
920 krb5_flags ap_req_options;
921 krb5_flags verify_ap_req_flags;
922 krb5_crypto crypto;
923 Key *tkey;
924 krb5_keyblock *subkey = NULL;
925 unsigned usage;
926 krb5uint32 kvno = 0;
927 krb5uint32 *kvno_ptr = NULL;
929 *auth_data = NULL;
930 *csec = NULL;
931 *cusec = NULL;
932 *replykey = NULL;
934 memset(&ap_req, 0, sizeof(ap_req));
935 ret = krb5_decode_ap_req(context, &tgs_req->padata_value, &ap_req);
936 if(ret){
937 const char *msg = krb5_get_error_message(context, ret);
938 kdc_log(context, config, 0, "Failed to decode AP-REQ: %s", msg);
939 krb5_free_error_message(context, msg);
940 goto out;
943 if(!get_krbtgt_realm(&ap_req.ticket.sname)){
944 /* XXX check for ticket.sname == req.sname */
945 kdc_log(context, config, 0, "PA-DATA is not a ticket-granting ticket");
946 ret = KRB5KDC_ERR_POLICY; /* ? */
947 goto out;
950 _krb5_principalname2krb5_principal(context,
951 &princ,
952 ap_req.ticket.sname,
953 ap_req.ticket.realm);
955 if (ap_req.ticket.enc_part.kvno) {
956 kvno = *ap_req.ticket.enc_part.kvno;
957 kvno_ptr = &kvno;
959 ret = _kdc_db_fetch(context, config, princ, HDB_F_GET_KRBTGT, kvno_ptr,
960 NULL, krbtgt);
962 if(ret == HDB_ERR_NOT_FOUND_HERE) {
963 char *p;
964 ret = krb5_unparse_name(context, princ, &p);
965 if (ret != 0)
966 p = failed;
967 krb5_free_principal(context, princ);
968 kdc_log(context, config, 5, "Ticket-granting ticket account %s does not have secrets at this KDC, need to proxy", p);
969 if (ret == 0)
970 free(p);
971 ret = HDB_ERR_NOT_FOUND_HERE;
972 goto out;
973 } else if(ret){
974 const char *msg = krb5_get_error_message(context, ret);
975 char *p;
976 ret = krb5_unparse_name(context, princ, &p);
977 if (ret != 0)
978 p = failed;
979 krb5_free_principal(context, princ);
980 kdc_log(context, config, 0,
981 "Ticket-granting ticket not found in database: %s", msg);
982 krb5_free_error_message(context, msg);
983 if (ret == 0)
984 free(p);
985 ret = KRB5KRB_AP_ERR_NOT_US;
986 goto out;
989 if(ap_req.ticket.enc_part.kvno &&
990 *ap_req.ticket.enc_part.kvno != (*krbtgt)->entry.kvno){
991 char *p;
993 ret = krb5_unparse_name (context, princ, &p);
994 krb5_free_principal(context, princ);
995 if (ret != 0)
996 p = failed;
997 kdc_log(context, config, 0,
998 "Ticket kvno = %d, DB kvno = %d (%s)",
999 *ap_req.ticket.enc_part.kvno,
1000 (*krbtgt)->entry.kvno,
1002 if (ret == 0)
1003 free (p);
1004 ret = KRB5KRB_AP_ERR_BADKEYVER;
1005 goto out;
1008 *krbtgt_etype = ap_req.ticket.enc_part.etype;
1010 ret = hdb_enctype2key(context, &(*krbtgt)->entry,
1011 ap_req.ticket.enc_part.etype, &tkey);
1012 if(ret){
1013 char *str = NULL, *p = NULL;
1015 krb5_enctype_to_string(context, ap_req.ticket.enc_part.etype, &str);
1016 krb5_unparse_name(context, princ, &p);
1017 kdc_log(context, config, 0,
1018 "No server key with enctype %s found for %s",
1019 str ? str : "<unknown enctype>",
1020 p ? p : "<unparse_name failed>");
1021 free(str);
1022 free(p);
1023 ret = KRB5KRB_AP_ERR_BADKEYVER;
1024 goto out;
1027 if (b->kdc_options.validate)
1028 verify_ap_req_flags = KRB5_VERIFY_AP_REQ_IGNORE_INVALID;
1029 else
1030 verify_ap_req_flags = 0;
1032 ret = krb5_verify_ap_req2(context,
1033 &ac,
1034 &ap_req,
1035 princ,
1036 &tkey->key,
1037 verify_ap_req_flags,
1038 &ap_req_options,
1039 ticket,
1040 KRB5_KU_TGS_REQ_AUTH);
1042 krb5_free_principal(context, princ);
1043 if(ret) {
1044 const char *msg = krb5_get_error_message(context, ret);
1045 kdc_log(context, config, 0, "Failed to verify AP-REQ: %s", msg);
1046 krb5_free_error_message(context, msg);
1047 goto out;
1051 krb5_authenticator auth;
1053 ret = krb5_auth_con_getauthenticator(context, ac, &auth);
1054 if (ret == 0) {
1055 *csec = malloc(sizeof(**csec));
1056 if (*csec == NULL) {
1057 krb5_free_authenticator(context, &auth);
1058 kdc_log(context, config, 0, "malloc failed");
1059 goto out;
1061 **csec = auth->ctime;
1062 *cusec = malloc(sizeof(**cusec));
1063 if (*cusec == NULL) {
1064 krb5_free_authenticator(context, &auth);
1065 kdc_log(context, config, 0, "malloc failed");
1066 goto out;
1068 **cusec = auth->cusec;
1069 krb5_free_authenticator(context, &auth);
1073 ret = tgs_check_authenticator(context, config,
1074 ac, b, e_text, &(*ticket)->ticket.key);
1075 if (ret) {
1076 krb5_auth_con_free(context, ac);
1077 goto out;
1080 usage = KRB5_KU_TGS_REQ_AUTH_DAT_SUBKEY;
1081 *rk_is_subkey = 1;
1083 ret = krb5_auth_con_getremotesubkey(context, ac, &subkey);
1084 if(ret){
1085 const char *msg = krb5_get_error_message(context, ret);
1086 krb5_auth_con_free(context, ac);
1087 kdc_log(context, config, 0, "Failed to get remote subkey: %s", msg);
1088 krb5_free_error_message(context, msg);
1089 goto out;
1091 if(subkey == NULL){
1092 usage = KRB5_KU_TGS_REQ_AUTH_DAT_SESSION;
1093 *rk_is_subkey = 0;
1095 ret = krb5_auth_con_getkey(context, ac, &subkey);
1096 if(ret) {
1097 const char *msg = krb5_get_error_message(context, ret);
1098 krb5_auth_con_free(context, ac);
1099 kdc_log(context, config, 0, "Failed to get session key: %s", msg);
1100 krb5_free_error_message(context, msg);
1101 goto out;
1104 if(subkey == NULL){
1105 krb5_auth_con_free(context, ac);
1106 kdc_log(context, config, 0,
1107 "Failed to get key for enc-authorization-data");
1108 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
1109 goto out;
1112 *replykey = subkey;
1114 if (b->enc_authorization_data) {
1115 krb5_data ad;
1117 ret = krb5_crypto_init(context, subkey, 0, &crypto);
1118 if (ret) {
1119 const char *msg = krb5_get_error_message(context, ret);
1120 krb5_auth_con_free(context, ac);
1121 kdc_log(context, config, 0, "krb5_crypto_init failed: %s", msg);
1122 krb5_free_error_message(context, msg);
1123 goto out;
1125 ret = krb5_decrypt_EncryptedData (context,
1126 crypto,
1127 usage,
1128 b->enc_authorization_data,
1129 &ad);
1130 krb5_crypto_destroy(context, crypto);
1131 if(ret){
1132 krb5_auth_con_free(context, ac);
1133 kdc_log(context, config, 0,
1134 "Failed to decrypt enc-authorization-data");
1135 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
1136 goto out;
1138 ALLOC(*auth_data);
1139 if (*auth_data == NULL) {
1140 krb5_auth_con_free(context, ac);
1141 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
1142 goto out;
1144 ret = decode_AuthorizationData(ad.data, ad.length, *auth_data, NULL);
1145 if(ret){
1146 krb5_auth_con_free(context, ac);
1147 free(*auth_data);
1148 *auth_data = NULL;
1149 kdc_log(context, config, 0, "Failed to decode authorization data");
1150 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
1151 goto out;
1155 krb5_auth_con_free(context, ac);
1157 out:
1158 free_AP_REQ(&ap_req);
1160 return ret;
1163 static krb5_error_code
1164 build_server_referral(krb5_context context,
1165 krb5_kdc_configuration *config,
1166 krb5_crypto session,
1167 krb5_const_realm referred_realm,
1168 const PrincipalName *true_principal_name,
1169 const PrincipalName *requested_principal,
1170 krb5_data *outdata)
1172 PA_ServerReferralData ref;
1173 krb5_error_code ret;
1174 EncryptedData ed;
1175 krb5_data data;
1176 size_t size = 0;
1178 memset(&ref, 0, sizeof(ref));
1180 if (referred_realm) {
1181 ALLOC(ref.referred_realm);
1182 if (ref.referred_realm == NULL)
1183 goto eout;
1184 *ref.referred_realm = strdup(referred_realm);
1185 if (*ref.referred_realm == NULL)
1186 goto eout;
1188 if (true_principal_name) {
1189 ALLOC(ref.true_principal_name);
1190 if (ref.true_principal_name == NULL)
1191 goto eout;
1192 ret = copy_PrincipalName(true_principal_name, ref.true_principal_name);
1193 if (ret)
1194 goto eout;
1196 if (requested_principal) {
1197 ALLOC(ref.requested_principal_name);
1198 if (ref.requested_principal_name == NULL)
1199 goto eout;
1200 ret = copy_PrincipalName(requested_principal,
1201 ref.requested_principal_name);
1202 if (ret)
1203 goto eout;
1206 ASN1_MALLOC_ENCODE(PA_ServerReferralData,
1207 data.data, data.length,
1208 &ref, &size, ret);
1209 free_PA_ServerReferralData(&ref);
1210 if (ret)
1211 return ret;
1212 if (data.length != size)
1213 krb5_abortx(context, "internal asn.1 encoder error");
1215 ret = krb5_encrypt_EncryptedData(context, session,
1216 KRB5_KU_PA_SERVER_REFERRAL,
1217 data.data, data.length,
1218 0 /* kvno */, &ed);
1219 free(data.data);
1220 if (ret)
1221 return ret;
1223 ASN1_MALLOC_ENCODE(EncryptedData,
1224 outdata->data, outdata->length,
1225 &ed, &size, ret);
1226 free_EncryptedData(&ed);
1227 if (ret)
1228 return ret;
1229 if (outdata->length != size)
1230 krb5_abortx(context, "internal asn.1 encoder error");
1232 return 0;
1233 eout:
1234 free_PA_ServerReferralData(&ref);
1235 krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
1236 return ENOMEM;
1239 static krb5_error_code
1240 tgs_build_reply(krb5_context context,
1241 krb5_kdc_configuration *config,
1242 KDC_REQ *req,
1243 KDC_REQ_BODY *b,
1244 hdb_entry_ex *krbtgt,
1245 krb5_enctype krbtgt_etype,
1246 const krb5_keyblock *replykey,
1247 int rk_is_subkey,
1248 krb5_ticket *ticket,
1249 krb5_data *reply,
1250 const char *from,
1251 const char **e_text,
1252 AuthorizationData **auth_data,
1253 const struct sockaddr *from_addr)
1255 krb5_error_code ret;
1256 krb5_principal cp = NULL, sp = NULL, tp = NULL, dp = NULL;
1257 krb5_principal krbtgt_principal = NULL;
1258 char *spn = NULL, *cpn = NULL, *tpn = NULL, *dpn = NULL;
1259 hdb_entry_ex *server = NULL, *client = NULL, *s4u2self_impersonated_client = NULL;
1260 HDB *clientdb, *s4u2self_impersonated_clientdb;
1261 krb5_realm ref_realm = NULL;
1262 EncTicketPart *tgt = &ticket->ticket;
1263 const EncryptionKey *ekey;
1264 krb5_keyblock sessionkey;
1265 krb5_kvno kvno;
1266 krb5_data rspac;
1268 hdb_entry_ex *krbtgt_out = NULL;
1270 METHOD_DATA enc_pa_data;
1272 PrincipalName *s;
1273 Realm r;
1274 int nloop = 0;
1275 EncTicketPart adtkt;
1276 char opt_str[128];
1277 int signedpath = 0;
1279 Key *tkey_check;
1280 Key *tkey_sign;
1281 int flags = HDB_F_FOR_TGS_REQ;
1283 memset(&sessionkey, 0, sizeof(sessionkey));
1284 memset(&adtkt, 0, sizeof(adtkt));
1285 krb5_data_zero(&rspac);
1286 memset(&enc_pa_data, 0, sizeof(enc_pa_data));
1288 s = b->sname;
1289 r = b->realm;
1291 if (b->kdc_options.canonicalize)
1292 flags |= HDB_F_CANON;
1294 if(b->kdc_options.enc_tkt_in_skey){
1295 Ticket *t;
1296 hdb_entry_ex *uu;
1297 krb5_principal p;
1298 Key *uukey;
1299 krb5uint32 second_kvno = 0;
1300 krb5uint32 *kvno_ptr = NULL;
1302 if(b->additional_tickets == NULL ||
1303 b->additional_tickets->len == 0){
1304 ret = KRB5KDC_ERR_BADOPTION; /* ? */
1305 kdc_log(context, config, 0,
1306 "No second ticket present in request");
1307 goto out;
1309 t = &b->additional_tickets->val[0];
1310 if(!get_krbtgt_realm(&t->sname)){
1311 kdc_log(context, config, 0,
1312 "Additional ticket is not a ticket-granting ticket");
1313 ret = KRB5KDC_ERR_POLICY;
1314 goto out;
1316 _krb5_principalname2krb5_principal(context, &p, t->sname, t->realm);
1317 if(t->enc_part.kvno){
1318 second_kvno = *t->enc_part.kvno;
1319 kvno_ptr = &second_kvno;
1321 ret = _kdc_db_fetch(context, config, p,
1322 HDB_F_GET_KRBTGT, kvno_ptr,
1323 NULL, &uu);
1324 krb5_free_principal(context, p);
1325 if(ret){
1326 if (ret == HDB_ERR_NOENTRY)
1327 ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
1328 goto out;
1330 ret = hdb_enctype2key(context, &uu->entry,
1331 t->enc_part.etype, &uukey);
1332 if(ret){
1333 _kdc_free_ent(context, uu);
1334 ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */
1335 goto out;
1337 ret = krb5_decrypt_ticket(context, t, &uukey->key, &adtkt, 0);
1338 _kdc_free_ent(context, uu);
1339 if(ret)
1340 goto out;
1342 ret = verify_flags(context, config, &adtkt, spn);
1343 if (ret)
1344 goto out;
1346 s = &adtkt.cname;
1347 r = adtkt.crealm;
1348 } else if (s == NULL) {
1349 ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
1350 krb5_set_error_message(context, ret, "No server in request");
1351 goto out;
1354 _krb5_principalname2krb5_principal(context, &sp, *s, r);
1355 ret = krb5_unparse_name(context, sp, &spn);
1356 if (ret)
1357 goto out;
1358 _krb5_principalname2krb5_principal(context, &cp, tgt->cname, tgt->crealm);
1359 ret = krb5_unparse_name(context, cp, &cpn);
1360 if (ret)
1361 goto out;
1362 unparse_flags (KDCOptions2int(b->kdc_options),
1363 asn1_KDCOptions_units(),
1364 opt_str, sizeof(opt_str));
1365 if(*opt_str)
1366 kdc_log(context, config, 0,
1367 "TGS-REQ %s from %s for %s [%s]",
1368 cpn, from, spn, opt_str);
1369 else
1370 kdc_log(context, config, 0,
1371 "TGS-REQ %s from %s for %s", cpn, from, spn);
1374 * Fetch server
1377 server_lookup:
1378 ret = _kdc_db_fetch(context, config, sp, HDB_F_GET_SERVER | flags,
1379 NULL, NULL, &server);
1381 if(ret == HDB_ERR_NOT_FOUND_HERE) {
1382 kdc_log(context, config, 5, "target %s does not have secrets at this KDC, need to proxy", sp);
1383 goto out;
1384 } else if (ret == HDB_ERR_WRONG_REALM) {
1385 if (ref_realm)
1386 free(ref_realm);
1387 ref_realm = strdup(server->entry.principal->realm);
1388 if (ref_realm == NULL) {
1389 ret = ENOMEM;
1390 goto out;
1393 kdc_log(context, config, 5,
1394 "Returning a referral to realm %s for "
1395 "server %s.",
1396 ref_realm, spn);
1397 krb5_free_principal(context, sp);
1398 sp = NULL;
1399 free(spn);
1400 spn = NULL;
1401 ret = krb5_make_principal(context, &sp, r, KRB5_TGS_NAME,
1402 ref_realm, NULL);
1403 if (ret)
1404 goto out;
1405 ret = krb5_unparse_name(context, sp, &spn);
1406 if (ret)
1407 goto out;
1409 goto server_lookup;
1410 } else if(ret){
1411 const char *new_rlm, *msg;
1412 Realm req_rlm;
1413 krb5_realm *realms;
1415 if (!config->autodetect_referrals) {
1416 /* noop */
1417 } else if ((req_rlm = get_krbtgt_realm(&sp->name)) != NULL) {
1418 if(nloop++ < 2) {
1419 new_rlm = find_rpath(context, tgt->crealm, req_rlm);
1420 if(new_rlm) {
1421 kdc_log(context, config, 5, "krbtgt for realm %s "
1422 "not found, trying %s",
1423 req_rlm, new_rlm);
1424 krb5_free_principal(context, sp);
1425 free(spn);
1426 krb5_make_principal(context, &sp, r,
1427 KRB5_TGS_NAME, new_rlm, NULL);
1428 ret = krb5_unparse_name(context, sp, &spn);
1429 if (ret)
1430 goto out;
1432 if (ref_realm)
1433 free(ref_realm);
1434 ref_realm = strdup(new_rlm);
1435 goto server_lookup;
1438 } else if(need_referral(context, config, &b->kdc_options, sp, &realms)) {
1439 if (strcmp(realms[0], sp->realm) != 0) {
1440 kdc_log(context, config, 5,
1441 "Returning a referral to realm %s for "
1442 "server %s that was not found",
1443 realms[0], spn);
1444 krb5_free_principal(context, sp);
1445 free(spn);
1446 krb5_make_principal(context, &sp, r, KRB5_TGS_NAME,
1447 realms[0], NULL);
1448 ret = krb5_unparse_name(context, sp, &spn);
1449 if (ret)
1450 goto out;
1452 if (ref_realm)
1453 free(ref_realm);
1454 ref_realm = strdup(realms[0]);
1456 krb5_free_host_realm(context, realms);
1457 goto server_lookup;
1459 krb5_free_host_realm(context, realms);
1461 msg = krb5_get_error_message(context, ret);
1462 kdc_log(context, config, 0,
1463 "Server not found in database: %s: %s", spn, msg);
1464 krb5_free_error_message(context, msg);
1465 if (ret == HDB_ERR_NOENTRY)
1466 ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
1467 goto out;
1471 * Select enctype, return key and kvno.
1475 krb5_enctype etype;
1477 if(b->kdc_options.enc_tkt_in_skey) {
1478 size_t i;
1479 ekey = &adtkt.key;
1480 for(i = 0; i < b->etype.len; i++)
1481 if (b->etype.val[i] == adtkt.key.keytype)
1482 break;
1483 if(i == b->etype.len) {
1484 kdc_log(context, config, 0,
1485 "Addition ticket have not matching etypes");
1486 krb5_clear_error_message(context);
1487 ret = KRB5KDC_ERR_ETYPE_NOSUPP;
1488 goto out;
1490 etype = b->etype.val[i];
1491 kvno = 0;
1492 } else {
1493 Key *skey;
1495 ret = _kdc_find_etype(context,
1496 config->tgs_use_strongest_session_key, FALSE,
1497 server, b->etype.val, b->etype.len, NULL,
1498 &skey);
1499 if(ret) {
1500 kdc_log(context, config, 0,
1501 "Server (%s) has no support for etypes", spn);
1502 goto out;
1504 ekey = &skey->key;
1505 etype = skey->key.keytype;
1506 kvno = server->entry.kvno;
1509 ret = krb5_generate_random_keyblock(context, etype, &sessionkey);
1510 if (ret)
1511 goto out;
1515 * Check that service is in the same realm as the krbtgt. If it's
1516 * not the same, it's someone that is using a uni-directional trust
1517 * backward.
1521 * Validate authoriation data
1524 ret = hdb_enctype2key(context, &krbtgt->entry,
1525 krbtgt_etype, &tkey_check);
1526 if(ret) {
1527 kdc_log(context, config, 0,
1528 "Failed to find key for krbtgt PAC check");
1529 goto out;
1532 /* Now refetch the primary krbtgt, and get the current kvno (the
1533 * sign check may have been on an old kvno, and the server may
1534 * have been an incoming trust) */
1535 ret = krb5_make_principal(context, &krbtgt_principal,
1536 krb5_principal_get_comp_string(context,
1537 krbtgt->entry.principal,
1539 KRB5_TGS_NAME,
1540 krb5_principal_get_comp_string(context,
1541 krbtgt->entry.principal,
1542 1), NULL);
1543 if(ret) {
1544 kdc_log(context, config, 0,
1545 "Failed to generate krbtgt principal");
1546 goto out;
1549 ret = _kdc_db_fetch(context, config, krbtgt_principal, HDB_F_GET_KRBTGT, NULL, NULL, &krbtgt_out);
1550 krb5_free_principal(context, krbtgt_principal);
1551 if (ret) {
1552 krb5_error_code ret2;
1553 char *ktpn, *ktpn2;
1554 ret = krb5_unparse_name(context, krbtgt->entry.principal, &ktpn);
1555 ret2 = krb5_unparse_name(context, krbtgt_principal, &ktpn2);
1556 kdc_log(context, config, 0,
1557 "Request with wrong krbtgt: %s, %s not found in our database",
1558 (ret == 0) ? ktpn : "<unknown>", (ret2 == 0) ? ktpn2 : "<unknown>");
1559 if(ret == 0)
1560 free(ktpn);
1561 if(ret2 == 0)
1562 free(ktpn2);
1563 ret = KRB5KRB_AP_ERR_NOT_US;
1564 goto out;
1567 /* The first realm is the realm of the service, the second is
1568 * krbtgt/<this>/@REALM component of the krbtgt DN the request was
1569 * encrypted to. The redirection via the krbtgt_out entry allows
1570 * the DB to possibly correct the case of the realm (Samba4 does
1571 * this) before the strcmp() */
1572 if (strcmp(krb5_principal_get_realm(context, server->entry.principal),
1573 krb5_principal_get_realm(context, krbtgt_out->entry.principal)) != 0) {
1574 char *ktpn;
1575 ret = krb5_unparse_name(context, krbtgt_out->entry.principal, &ktpn);
1576 kdc_log(context, config, 0,
1577 "Request with wrong krbtgt: %s",
1578 (ret == 0) ? ktpn : "<unknown>");
1579 if(ret == 0)
1580 free(ktpn);
1581 ret = KRB5KRB_AP_ERR_NOT_US;
1584 ret = hdb_enctype2key(context, &krbtgt_out->entry,
1585 krbtgt_etype, &tkey_sign);
1586 if(ret) {
1587 kdc_log(context, config, 0,
1588 "Failed to find key for krbtgt PAC signature");
1589 goto out;
1592 ret = _kdc_db_fetch(context, config, cp, HDB_F_GET_CLIENT | flags,
1593 NULL, &clientdb, &client);
1594 if(ret == HDB_ERR_NOT_FOUND_HERE) {
1595 /* This is OK, we are just trying to find out if they have
1596 * been disabled or deleted in the meantime, missing secrets
1597 * is OK */
1598 } else if(ret){
1599 const char *krbtgt_realm, *msg;
1602 * If the client belongs to the same realm as our krbtgt, it
1603 * should exist in the local database.
1607 krbtgt_realm = krb5_principal_get_realm(context, krbtgt_out->entry.principal);
1609 if(strcmp(krb5_principal_get_realm(context, cp), krbtgt_realm) == 0) {
1610 if (ret == HDB_ERR_NOENTRY)
1611 ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
1612 kdc_log(context, config, 1, "Client no longer in database: %s",
1613 cpn);
1614 goto out;
1617 msg = krb5_get_error_message(context, ret);
1618 kdc_log(context, config, 1, "Client not found in database: %s", msg);
1619 krb5_free_error_message(context, msg);
1622 ret = check_PAC(context, config, cp, NULL,
1623 client, server, krbtgt,
1624 &tkey_check->key,
1625 ekey, &tkey_sign->key,
1626 tgt, &rspac, &signedpath);
1627 if (ret) {
1628 const char *msg = krb5_get_error_message(context, ret);
1629 kdc_log(context, config, 0,
1630 "Verify PAC failed for %s (%s) from %s with %s",
1631 spn, cpn, from, msg);
1632 krb5_free_error_message(context, msg);
1633 goto out;
1637 * Process request
1640 /* by default the tgt principal matches the client principal */
1641 tp = cp;
1642 tpn = cpn;
1644 if (client) {
1645 const PA_DATA *sdata;
1646 int i = 0;
1648 sdata = _kdc_find_padata(req, &i, KRB5_PADATA_FOR_USER);
1649 if (sdata) {
1650 krb5_crypto crypto;
1651 krb5_data datack;
1652 PA_S4U2Self self;
1653 const char *str;
1655 ret = decode_PA_S4U2Self(sdata->padata_value.data,
1656 sdata->padata_value.length,
1657 &self, NULL);
1658 if (ret) {
1659 kdc_log(context, config, 0, "Failed to decode PA-S4U2Self");
1660 goto out;
1663 if (!krb5_checksum_is_keyed(context, self.cksum.cksumtype)) {
1664 free_PA_S4U2Self(&self);
1665 kdc_log(context, config, 0, "Reject PA-S4U2Self with unkeyed checksum");
1666 ret = KRB5KRB_AP_ERR_INAPP_CKSUM;
1667 goto out;
1670 ret = _krb5_s4u2self_to_checksumdata(context, &self, &datack);
1671 if (ret)
1672 goto out;
1674 ret = krb5_crypto_init(context, &tgt->key, 0, &crypto);
1675 if (ret) {
1676 const char *msg = krb5_get_error_message(context, ret);
1677 free_PA_S4U2Self(&self);
1678 krb5_data_free(&datack);
1679 kdc_log(context, config, 0, "krb5_crypto_init failed: %s", msg);
1680 krb5_free_error_message(context, msg);
1681 goto out;
1684 /* Allow HMAC_MD5 checksum with any key type */
1685 if (self.cksum.cksumtype == CKSUMTYPE_HMAC_MD5) {
1686 unsigned char csdata[16];
1687 Checksum cs;
1689 cs.checksum.length = sizeof(csdata);
1690 cs.checksum.data = &csdata;
1692 ret = _krb5_HMAC_MD5_checksum(context, &crypto->key,
1693 datack.data, datack.length,
1694 KRB5_KU_OTHER_CKSUM, &cs);
1695 if (ret == 0 &&
1696 krb5_data_ct_cmp(&cs.checksum, &self.cksum.checksum) != 0)
1697 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
1699 else {
1700 ret = krb5_verify_checksum(context,
1701 crypto,
1702 KRB5_KU_OTHER_CKSUM,
1703 datack.data,
1704 datack.length,
1705 &self.cksum);
1707 krb5_data_free(&datack);
1708 krb5_crypto_destroy(context, crypto);
1709 if (ret) {
1710 const char *msg = krb5_get_error_message(context, ret);
1711 free_PA_S4U2Self(&self);
1712 kdc_log(context, config, 0,
1713 "krb5_verify_checksum failed for S4U2Self: %s", msg);
1714 krb5_free_error_message(context, msg);
1715 goto out;
1718 ret = _krb5_principalname2krb5_principal(context,
1719 &tp,
1720 self.name,
1721 self.realm);
1722 free_PA_S4U2Self(&self);
1723 if (ret)
1724 goto out;
1726 ret = krb5_unparse_name(context, tp, &tpn);
1727 if (ret)
1728 goto out;
1730 ret = _kdc_db_fetch(context, config, tp, HDB_F_GET_CLIENT | flags,
1731 NULL, &s4u2self_impersonated_clientdb,
1732 &s4u2self_impersonated_client);
1733 if (ret) {
1734 const char *msg;
1737 * If the client belongs to the same realm as our krbtgt, it
1738 * should exist in the local database.
1742 if (ret == HDB_ERR_NOENTRY)
1743 ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
1744 msg = krb5_get_error_message(context, ret);
1745 kdc_log(context, config, 1,
1746 "S2U4Self principal to impersonate %s not found in database: %s",
1747 tpn, msg);
1748 krb5_free_error_message(context, msg);
1749 goto out;
1752 /* Ignore pw_end attributes (as Windows does),
1753 * since S4U2Self is not password authentication. */
1754 free(s4u2self_impersonated_client->entry.pw_end);
1755 s4u2self_impersonated_client->entry.pw_end = NULL;
1757 ret = kdc_check_flags(context, config, s4u2self_impersonated_client, tpn,
1758 NULL, NULL, FALSE);
1759 if (ret)
1760 goto out;
1762 /* If we were about to put a PAC into the ticket, we better fix it to be the right PAC */
1763 if(rspac.data) {
1764 krb5_pac p = NULL;
1765 krb5_data_free(&rspac);
1766 ret = _kdc_pac_generate(context, s4u2self_impersonated_client, NULL, &p);
1767 if (ret) {
1768 kdc_log(context, config, 0, "PAC generation failed for -- %s",
1769 tpn);
1770 goto out;
1772 if (p != NULL) {
1773 ret = _krb5_pac_sign(context, p, ticket->ticket.authtime,
1774 s4u2self_impersonated_client->entry.principal,
1775 ekey, &tkey_sign->key,
1776 &rspac);
1777 krb5_pac_free(context, p);
1778 if (ret) {
1779 kdc_log(context, config, 0, "PAC signing failed for -- %s",
1780 tpn);
1781 goto out;
1787 * Check that service doing the impersonating is
1788 * requesting a ticket to it-self.
1790 ret = check_s4u2self(context, config, clientdb, client, sp);
1791 if (ret) {
1792 kdc_log(context, config, 0, "S4U2Self: %s is not allowed "
1793 "to impersonate to service "
1794 "(tried for user %s to service %s)",
1795 cpn, tpn, spn);
1796 goto out;
1800 * If the service isn't trusted for authentication to
1801 * delegation or if the impersonate client is disallowed
1802 * forwardable, remove the forwardable flag.
1805 if (client->entry.flags.trusted_for_delegation &&
1806 s4u2self_impersonated_client->entry.flags.forwardable) {
1807 str = "[forwardable]";
1808 } else {
1809 b->kdc_options.forwardable = 0;
1810 str = "";
1812 kdc_log(context, config, 0, "s4u2self %s impersonating %s to "
1813 "service %s %s", cpn, tpn, spn, str);
1818 * Constrained delegation
1821 if (client != NULL
1822 && b->additional_tickets != NULL
1823 && b->additional_tickets->len != 0
1824 && b->kdc_options.enc_tkt_in_skey == 0)
1826 int ad_signedpath = 0;
1827 Key *clientkey;
1828 Ticket *t;
1831 * Require that the KDC have issued the service's krbtgt (not
1832 * self-issued ticket with kimpersonate(1).
1834 if (!signedpath) {
1835 ret = KRB5KDC_ERR_BADOPTION;
1836 kdc_log(context, config, 0,
1837 "Constrained delegation done on service ticket %s/%s",
1838 cpn, spn);
1839 goto out;
1842 t = &b->additional_tickets->val[0];
1844 ret = hdb_enctype2key(context, &client->entry,
1845 t->enc_part.etype, &clientkey);
1846 if(ret){
1847 ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */
1848 goto out;
1851 ret = krb5_decrypt_ticket(context, t, &clientkey->key, &adtkt, 0);
1852 if (ret) {
1853 kdc_log(context, config, 0,
1854 "failed to decrypt ticket for "
1855 "constrained delegation from %s to %s ", cpn, spn);
1856 goto out;
1859 ret = _krb5_principalname2krb5_principal(context,
1860 &tp,
1861 adtkt.cname,
1862 adtkt.crealm);
1863 if (ret)
1864 goto out;
1866 ret = krb5_unparse_name(context, tp, &tpn);
1867 if (ret)
1868 goto out;
1870 ret = _krb5_principalname2krb5_principal(context,
1871 &dp,
1872 t->sname,
1873 t->realm);
1874 if (ret)
1875 goto out;
1877 ret = krb5_unparse_name(context, dp, &dpn);
1878 if (ret)
1879 goto out;
1881 /* check that ticket is valid */
1882 if (adtkt.flags.forwardable == 0) {
1883 kdc_log(context, config, 0,
1884 "Missing forwardable flag on ticket for "
1885 "constrained delegation from %s (%s) as %s to %s ",
1886 cpn, dpn, tpn, spn);
1887 ret = KRB5KDC_ERR_BADOPTION;
1888 goto out;
1891 ret = check_constrained_delegation(context, config, clientdb,
1892 client, server, sp);
1893 if (ret) {
1894 kdc_log(context, config, 0,
1895 "constrained delegation from %s (%s) as %s to %s not allowed",
1896 cpn, dpn, tpn, spn);
1897 goto out;
1900 ret = verify_flags(context, config, &adtkt, tpn);
1901 if (ret) {
1902 goto out;
1905 krb5_data_free(&rspac);
1908 * generate the PAC for the user.
1910 * TODO: pass in t->sname and t->realm and build
1911 * a S4U_DELEGATION_INFO blob to the PAC.
1913 ret = check_PAC(context, config, tp, dp,
1914 client, server, krbtgt,
1915 &clientkey->key,
1916 ekey, &tkey_sign->key,
1917 &adtkt, &rspac, &ad_signedpath);
1918 if (ret) {
1919 const char *msg = krb5_get_error_message(context, ret);
1920 kdc_log(context, config, 0,
1921 "Verify delegated PAC failed to %s for client"
1922 "%s (%s) as %s from %s with %s",
1923 spn, cpn, dpn, tpn, from, msg);
1924 krb5_free_error_message(context, msg);
1925 goto out;
1928 if (!ad_signedpath) {
1929 ret = KRB5KDC_ERR_BADOPTION;
1930 kdc_log(context, config, 0,
1931 "Ticket not signed with PAC nor SignedPath service %s failed "
1932 "for delegation to %s for client %s (%s)"
1933 "from %s",
1934 spn, tpn, dpn, cpn, from);
1935 goto out;
1938 kdc_log(context, config, 0, "constrained delegation for %s "
1939 "from %s (%s) to %s", tpn, cpn, dpn, spn);
1943 * Check flags
1946 ret = kdc_check_flags(context, config,
1947 client, cpn,
1948 server, spn,
1949 FALSE);
1950 if(ret)
1951 goto out;
1953 if((b->kdc_options.validate || b->kdc_options.renew) &&
1954 !krb5_principal_compare(context,
1955 krbtgt->entry.principal,
1956 server->entry.principal)){
1957 kdc_log(context, config, 0, "Inconsistent request.");
1958 ret = KRB5KDC_ERR_SERVER_NOMATCH;
1959 goto out;
1962 /* check for valid set of addresses */
1963 if(!_kdc_check_addresses(context, config, tgt->caddr, from_addr)) {
1964 ret = KRB5KRB_AP_ERR_BADADDR;
1965 kdc_log(context, config, 0, "Request from wrong address");
1966 goto out;
1970 * If this is an referral, add server referral data to the
1971 * auth_data reply .
1973 if (ref_realm) {
1974 PA_DATA pa;
1975 krb5_crypto crypto;
1977 kdc_log(context, config, 0,
1978 "Adding server referral to %s", ref_realm);
1980 ret = krb5_crypto_init(context, &sessionkey, 0, &crypto);
1981 if (ret)
1982 goto out;
1984 ret = build_server_referral(context, config, crypto, ref_realm,
1985 NULL, s, &pa.padata_value);
1986 krb5_crypto_destroy(context, crypto);
1987 if (ret) {
1988 kdc_log(context, config, 0,
1989 "Failed building server referral");
1990 goto out;
1992 pa.padata_type = KRB5_PADATA_SERVER_REFERRAL;
1994 ret = add_METHOD_DATA(&enc_pa_data, &pa);
1995 krb5_data_free(&pa.padata_value);
1996 if (ret) {
1997 kdc_log(context, config, 0,
1998 "Add server referral METHOD-DATA failed");
1999 goto out;
2007 ret = tgs_make_reply(context,
2008 config,
2011 tgt,
2012 replykey,
2013 rk_is_subkey,
2014 ekey,
2015 &sessionkey,
2016 kvno,
2017 *auth_data,
2018 server,
2019 server->entry.principal,
2020 spn,
2021 client,
2023 krbtgt_out,
2024 krbtgt_etype,
2025 &rspac,
2026 &enc_pa_data,
2027 e_text,
2028 reply);
2030 out:
2031 if (tpn != cpn)
2032 free(tpn);
2033 free(spn);
2034 free(cpn);
2035 if (dpn)
2036 free(dpn);
2038 krb5_data_free(&rspac);
2039 krb5_free_keyblock_contents(context, &sessionkey);
2040 if(krbtgt_out)
2041 _kdc_free_ent(context, krbtgt_out);
2042 if(server)
2043 _kdc_free_ent(context, server);
2044 if(client)
2045 _kdc_free_ent(context, client);
2046 if(s4u2self_impersonated_client)
2047 _kdc_free_ent(context, s4u2self_impersonated_client);
2049 if (tp && tp != cp)
2050 krb5_free_principal(context, tp);
2051 if (cp)
2052 krb5_free_principal(context, cp);
2053 if (dp)
2054 krb5_free_principal(context, dp);
2055 if (sp)
2056 krb5_free_principal(context, sp);
2057 if (ref_realm)
2058 free(ref_realm);
2059 free_METHOD_DATA(&enc_pa_data);
2061 free_EncTicketPart(&adtkt);
2063 return ret;
2070 krb5_error_code
2071 _kdc_tgs_rep(krb5_context context,
2072 krb5_kdc_configuration *config,
2073 KDC_REQ *req,
2074 krb5_data *data,
2075 const char *from,
2076 struct sockaddr *from_addr,
2077 int datagram_reply)
2079 AuthorizationData *auth_data = NULL;
2080 krb5_error_code ret;
2081 int i = 0;
2082 const PA_DATA *tgs_req;
2084 hdb_entry_ex *krbtgt = NULL;
2085 krb5_ticket *ticket = NULL;
2086 const char *e_text = NULL;
2087 krb5_enctype krbtgt_etype = ETYPE_NULL;
2089 krb5_keyblock *replykey = NULL;
2090 int rk_is_subkey = 0;
2091 time_t *csec = NULL;
2092 int *cusec = NULL;
2094 if(req->padata == NULL){
2095 ret = KRB5KDC_ERR_PREAUTH_REQUIRED; /* XXX ??? */
2096 kdc_log(context, config, 0,
2097 "TGS-REQ from %s without PA-DATA", from);
2098 goto out;
2101 tgs_req = _kdc_find_padata(req, &i, KRB5_PADATA_TGS_REQ);
2103 if(tgs_req == NULL){
2104 ret = KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
2106 kdc_log(context, config, 0,
2107 "TGS-REQ from %s without PA-TGS-REQ", from);
2108 goto out;
2110 ret = tgs_parse_request(context, config,
2111 &req->req_body, tgs_req,
2112 &krbtgt,
2113 &krbtgt_etype,
2114 &ticket,
2115 &e_text,
2116 from, from_addr,
2117 &csec, &cusec,
2118 &auth_data,
2119 &replykey,
2120 &rk_is_subkey);
2121 if (ret == HDB_ERR_NOT_FOUND_HERE) {
2122 /* kdc_log() is called in tgs_parse_request() */
2123 goto out;
2125 if (ret) {
2126 kdc_log(context, config, 0,
2127 "Failed parsing TGS-REQ from %s", from);
2128 goto out;
2131 ret = tgs_build_reply(context,
2132 config,
2133 req,
2134 &req->req_body,
2135 krbtgt,
2136 krbtgt_etype,
2137 replykey,
2138 rk_is_subkey,
2139 ticket,
2140 data,
2141 from,
2142 &e_text,
2143 &auth_data,
2144 from_addr);
2145 if (ret) {
2146 kdc_log(context, config, 0,
2147 "Failed building TGS-REP to %s", from);
2148 goto out;
2151 /* */
2152 if (datagram_reply && data->length > config->max_datagram_reply_length) {
2153 krb5_data_free(data);
2154 ret = KRB5KRB_ERR_RESPONSE_TOO_BIG;
2155 e_text = "Reply packet too large";
2158 out:
2159 if (replykey)
2160 krb5_free_keyblock(context, replykey);
2161 if(ret && ret != HDB_ERR_NOT_FOUND_HERE && data->data == NULL){
2162 krb5_mk_error(context,
2163 ret,
2164 NULL,
2165 NULL,
2166 NULL,
2167 NULL,
2168 csec,
2169 cusec,
2170 data);
2171 ret = 0;
2173 free(csec);
2174 free(cusec);
2175 if (ticket)
2176 krb5_free_ticket(context, ticket);
2177 if(krbtgt)
2178 _kdc_free_ent(context, krbtgt);
2180 if (auth_data) {
2181 free_AuthorizationData(auth_data);
2182 free(auth_data);
2185 return ret;