x
[heimdal.git] / kdc / krb5tgs.c
blob4ac3b400cb763faa1866ec095180c7477dfce35c
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"
36 RCSID("$Id$");
39 * return the realm of a krbtgt-ticket or NULL
42 static Realm
43 get_krbtgt_realm(const PrincipalName *p)
45 if(p->name_string.len == 2
46 && strcmp(p->name_string.val[0], KRB5_TGS_NAME) == 0)
47 return p->name_string.val[1];
48 else
49 return NULL;
53 * The KDC might add a signed path to the ticket authorization data
54 * field. This is to avoid server impersonating clients and the
55 * request constrained delegation.
57 * This is done by storing a KRB5_AUTHDATA_IF_RELEVANT with a single
58 * entry of type KRB5SignedPath.
61 static krb5_error_code
62 find_KRB5SignedPath(krb5_context context,
63 const AuthorizationData *ad,
64 krb5_data *data)
66 AuthorizationData child;
67 krb5_error_code ret;
68 int pos;
70 if (ad == NULL || ad->len == 0)
71 return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
73 pos = ad->len - 1;
75 if (ad->val[pos].ad_type != KRB5_AUTHDATA_IF_RELEVANT)
76 return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
78 ret = decode_AuthorizationData(ad->val[pos].ad_data.data,
79 ad->val[pos].ad_data.length,
80 &child,
81 NULL);
82 if (ret) {
83 krb5_set_error_string(context, "Failed to decode "
84 "IF_RELEVANT with %d", ret);
85 return ret;
88 if (child.len != 1) {
89 free_AuthorizationData(&child);
90 return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
93 if (child.val[0].ad_type != KRB5_AUTHDATA_SIGNTICKET) {
94 free_AuthorizationData(&child);
95 return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
98 if (data)
99 ret = der_copy_octet_string(&child.val[0].ad_data, data);
100 free_AuthorizationData(&child);
101 return ret;
104 krb5_error_code
105 _kdc_add_KRB5SignedPath(krb5_context context,
106 krb5_kdc_configuration *config,
107 hdb_entry_ex *krbtgt,
108 krb5_enctype enctype,
109 krb5_const_principal server,
110 KRB5SignedPathPrincipals *principals,
111 EncTicketPart *tkt)
113 krb5_error_code ret;
114 KRB5SignedPath sp;
115 krb5_data data;
116 krb5_crypto crypto = NULL;
117 size_t size;
119 if (server && principals) {
120 ret = add_KRB5SignedPathPrincipals(principals, server);
121 if (ret)
122 return ret;
126 KRB5SignedPathData spd;
128 spd.encticket = *tkt;
129 spd.delegated = principals;
131 ASN1_MALLOC_ENCODE(KRB5SignedPathData, data.data, data.length,
132 &spd, &size, ret);
133 if (ret)
134 return ret;
135 if (data.length != size)
136 krb5_abortx(context, "internal asn.1 encoder error");
140 Key *key;
141 ret = hdb_enctype2key(context, &krbtgt->entry, enctype, &key);
142 if (ret == 0)
143 ret = krb5_crypto_init(context, &key->key, 0, &crypto);
144 if (ret) {
145 free(data.data);
146 return ret;
151 * Fill in KRB5SignedPath
154 sp.etype = enctype;
155 sp.delegated = principals;
157 ret = krb5_create_checksum(context, crypto, KRB5_KU_KRB5SIGNEDPATH, 0,
158 data.data, data.length, &sp.cksum);
159 krb5_crypto_destroy(context, crypto);
160 free(data.data);
161 if (ret)
162 return ret;
164 ASN1_MALLOC_ENCODE(KRB5SignedPath, data.data, data.length, &sp, &size, ret);
165 free_Checksum(&sp.cksum);
166 if (ret)
167 return ret;
168 if (data.length != size)
169 krb5_abortx(context, "internal asn.1 encoder error");
173 * Add IF-RELEVANT(KRB5SignedPath) to the last slot in
174 * authorization data field.
177 ret = _kdc_tkt_add_if_relevant_ad(context, tkt,
178 KRB5_AUTHDATA_SIGNTICKET, &data);
179 krb5_data_free(&data);
181 return ret;
184 static krb5_error_code
185 check_KRB5SignedPath(krb5_context context,
186 krb5_kdc_configuration *config,
187 hdb_entry_ex *krbtgt,
188 EncTicketPart *tkt,
189 KRB5SignedPathPrincipals **delegated,
190 int *signedpath)
192 krb5_error_code ret;
193 krb5_data data;
194 krb5_crypto crypto = NULL;
196 if (delegated)
197 *delegated = NULL;
199 ret = find_KRB5SignedPath(context, tkt->authorization_data, &data);
200 if (ret == 0) {
201 KRB5SignedPathData spd;
202 KRB5SignedPath sp;
203 AuthorizationData *ad;
204 size_t size;
206 ret = decode_KRB5SignedPath(data.data, data.length, &sp, NULL);
207 krb5_data_free(&data);
208 if (ret)
209 return ret;
211 spd.encticket = *tkt;
212 /* the KRB5SignedPath is the last entry */
213 ad = spd.encticket.authorization_data;
214 if (--ad->len == 0)
215 spd.encticket.authorization_data = NULL;
216 spd.delegated = sp.delegated;
218 ASN1_MALLOC_ENCODE(KRB5SignedPathData, data.data, data.length,
219 &spd, &size, ret);
220 ad->len++;
221 spd.encticket.authorization_data = ad;
222 if (ret) {
223 free_KRB5SignedPath(&sp);
224 return ret;
226 if (data.length != size)
227 krb5_abortx(context, "internal asn.1 encoder error");
230 Key *key;
231 ret = hdb_enctype2key(context, &krbtgt->entry, sp.etype, &key);
232 if (ret == 0)
233 ret = krb5_crypto_init(context, &key->key, 0, &crypto);
234 if (ret) {
235 free(data.data);
236 free_KRB5SignedPath(&sp);
237 return ret;
240 ret = krb5_verify_checksum(context, crypto, KRB5_KU_KRB5SIGNEDPATH,
241 data.data, data.length,
242 &sp.cksum);
243 krb5_crypto_destroy(context, crypto);
244 free(data.data);
245 if (ret) {
246 free_KRB5SignedPath(&sp);
247 return ret;
250 if (delegated && sp.delegated) {
252 *delegated = malloc(sizeof(*sp.delegated));
253 if (*delegated == NULL) {
254 free_KRB5SignedPath(&sp);
255 return ENOMEM;
258 ret = copy_KRB5SignedPathPrincipals(*delegated, sp.delegated);
259 if (ret) {
260 free_KRB5SignedPath(&sp);
261 free(*delegated);
262 *delegated = NULL;
263 return ret;
266 free_KRB5SignedPath(&sp);
268 *signedpath = 1;
271 return 0;
278 static krb5_error_code
279 check_PAC(krb5_context context,
280 krb5_kdc_configuration *config,
281 const krb5_principal client_principal,
282 hdb_entry_ex *client,
283 hdb_entry_ex *server,
284 const EncryptionKey *server_key,
285 const EncryptionKey *krbtgt_key,
286 EncTicketPart *tkt,
287 krb5_data *rspac,
288 int *signedpath)
290 AuthorizationData *ad = tkt->authorization_data;
291 unsigned i, j;
292 krb5_error_code ret;
294 if (ad == NULL || ad->len == 0)
295 return 0;
297 for (i = 0; i < ad->len; i++) {
298 AuthorizationData child;
300 if (ad->val[i].ad_type != KRB5_AUTHDATA_IF_RELEVANT)
301 continue;
303 ret = decode_AuthorizationData(ad->val[i].ad_data.data,
304 ad->val[i].ad_data.length,
305 &child,
306 NULL);
307 if (ret) {
308 krb5_set_error_string(context, "Failed to decode "
309 "IF_RELEVANT with %d", ret);
310 return ret;
312 for (j = 0; j < child.len; j++) {
314 if (child.val[j].ad_type == KRB5_AUTHDATA_WIN2K_PAC) {
315 krb5_pac pac;
317 /* Found PAC */
318 ret = krb5_pac_parse(context,
319 child.val[j].ad_data.data,
320 child.val[j].ad_data.length,
321 &pac);
322 free_AuthorizationData(&child);
323 if (ret)
324 return ret;
326 ret = krb5_pac_verify(context, pac, tkt->authtime,
327 client_principal,
328 krbtgt_key, NULL);
329 if (ret) {
330 krb5_pac_free(context, pac);
331 return ret;
334 ret = _kdc_pac_verify(context, client_principal,
335 client, server, &pac);
336 if (ret) {
337 krb5_pac_free(context, pac);
338 return ret;
340 *signedpath = 1;
342 ret = _krb5_pac_sign(context, pac, tkt->authtime,
343 client_principal,
344 server_key, krbtgt_key, rspac);
346 krb5_pac_free(context, pac);
348 return ret;
351 free_AuthorizationData(&child);
353 return 0;
360 static krb5_error_code
361 check_tgs_flags(krb5_context context,
362 krb5_kdc_configuration *config,
363 KDC_REQ_BODY *b, const EncTicketPart *tgt, EncTicketPart *et)
365 KDCOptions f = b->kdc_options;
367 if(f.validate){
368 if(!tgt->flags.invalid || tgt->starttime == NULL){
369 kdc_log(context, config, 0,
370 "Bad request to validate ticket");
371 return KRB5KDC_ERR_BADOPTION;
373 if(*tgt->starttime > kdc_time){
374 kdc_log(context, config, 0,
375 "Early request to validate ticket");
376 return KRB5KRB_AP_ERR_TKT_NYV;
378 /* XXX tkt = tgt */
379 et->flags.invalid = 0;
380 }else if(tgt->flags.invalid){
381 kdc_log(context, config, 0,
382 "Ticket-granting ticket has INVALID flag set");
383 return KRB5KRB_AP_ERR_TKT_INVALID;
386 if(f.forwardable){
387 if(!tgt->flags.forwardable){
388 kdc_log(context, config, 0,
389 "Bad request for forwardable ticket");
390 return KRB5KDC_ERR_BADOPTION;
392 et->flags.forwardable = 1;
394 if(f.forwarded){
395 if(!tgt->flags.forwardable){
396 kdc_log(context, config, 0,
397 "Request to forward non-forwardable ticket");
398 return KRB5KDC_ERR_BADOPTION;
400 et->flags.forwarded = 1;
401 et->caddr = b->addresses;
403 if(tgt->flags.forwarded)
404 et->flags.forwarded = 1;
406 if(f.proxiable){
407 if(!tgt->flags.proxiable){
408 kdc_log(context, config, 0,
409 "Bad request for proxiable ticket");
410 return KRB5KDC_ERR_BADOPTION;
412 et->flags.proxiable = 1;
414 if(f.proxy){
415 if(!tgt->flags.proxiable){
416 kdc_log(context, config, 0,
417 "Request to proxy non-proxiable ticket");
418 return KRB5KDC_ERR_BADOPTION;
420 et->flags.proxy = 1;
421 et->caddr = b->addresses;
423 if(tgt->flags.proxy)
424 et->flags.proxy = 1;
426 if(f.allow_postdate){
427 if(!tgt->flags.may_postdate){
428 kdc_log(context, config, 0,
429 "Bad request for post-datable ticket");
430 return KRB5KDC_ERR_BADOPTION;
432 et->flags.may_postdate = 1;
434 if(f.postdated){
435 if(!tgt->flags.may_postdate){
436 kdc_log(context, config, 0,
437 "Bad request for postdated ticket");
438 return KRB5KDC_ERR_BADOPTION;
440 if(b->from)
441 *et->starttime = *b->from;
442 et->flags.postdated = 1;
443 et->flags.invalid = 1;
444 }else if(b->from && *b->from > kdc_time + context->max_skew){
445 kdc_log(context, config, 0, "Ticket cannot be postdated");
446 return KRB5KDC_ERR_CANNOT_POSTDATE;
449 if(f.renewable){
450 if(!tgt->flags.renewable){
451 kdc_log(context, config, 0,
452 "Bad request for renewable ticket");
453 return KRB5KDC_ERR_BADOPTION;
455 et->flags.renewable = 1;
456 ALLOC(et->renew_till);
457 _kdc_fix_time(&b->rtime);
458 *et->renew_till = *b->rtime;
460 if(f.renew){
461 time_t old_life;
462 if(!tgt->flags.renewable || tgt->renew_till == NULL){
463 kdc_log(context, config, 0,
464 "Request to renew non-renewable ticket");
465 return KRB5KDC_ERR_BADOPTION;
467 old_life = tgt->endtime;
468 if(tgt->starttime)
469 old_life -= *tgt->starttime;
470 else
471 old_life -= tgt->authtime;
472 et->endtime = *et->starttime + old_life;
473 if (et->renew_till != NULL)
474 et->endtime = min(*et->renew_till, et->endtime);
477 #if 0
478 /* checks for excess flags */
479 if(f.request_anonymous && !config->allow_anonymous){
480 kdc_log(context, config, 0,
481 "Request for anonymous ticket");
482 return KRB5KDC_ERR_BADOPTION;
484 #endif
485 return 0;
492 static krb5_error_code
493 check_constrained_delegation(krb5_context context,
494 krb5_kdc_configuration *config,
495 hdb_entry_ex *client,
496 krb5_const_principal server)
498 const HDB_Ext_Constrained_delegation_acl *acl;
499 krb5_error_code ret;
500 int i;
502 ret = hdb_entry_get_ConstrainedDelegACL(&client->entry, &acl);
503 if (ret) {
504 krb5_clear_error_string(context);
505 return ret;
508 if (acl) {
509 for (i = 0; i < acl->len; i++) {
510 if (krb5_principal_compare(context, server, &acl->val[i]) == TRUE)
511 return 0;
514 kdc_log(context, config, 0,
515 "Bad request for constrained delegation");
516 return KRB5KDC_ERR_BADOPTION;
523 static krb5_error_code
524 verify_flags (krb5_context context,
525 krb5_kdc_configuration *config,
526 const EncTicketPart *et,
527 const char *pstr)
529 if(et->endtime < kdc_time){
530 kdc_log(context, config, 0, "Ticket expired (%s)", pstr);
531 return KRB5KRB_AP_ERR_TKT_EXPIRED;
533 if(et->flags.invalid){
534 kdc_log(context, config, 0, "Ticket not valid (%s)", pstr);
535 return KRB5KRB_AP_ERR_TKT_NYV;
537 return 0;
544 static krb5_error_code
545 fix_transited_encoding(krb5_context context,
546 krb5_kdc_configuration *config,
547 krb5_boolean check_policy,
548 const TransitedEncoding *tr,
549 EncTicketPart *et,
550 const char *client_realm,
551 const char *server_realm,
552 const char *tgt_realm)
554 krb5_error_code ret = 0;
555 char **realms, **tmp;
556 unsigned int num_realms;
557 int i;
559 switch (tr->tr_type) {
560 case DOMAIN_X500_COMPRESS:
561 break;
562 case 0:
564 * Allow empty content of type 0 because that is was Microsoft
565 * generates in their TGT.
567 if (tr->contents.length == 0)
568 break;
569 kdc_log(context, config, 0,
570 "Transited type 0 with non empty content");
571 return KRB5KDC_ERR_TRTYPE_NOSUPP;
572 default:
573 kdc_log(context, config, 0,
574 "Unknown transited type: %u", tr->tr_type);
575 return KRB5KDC_ERR_TRTYPE_NOSUPP;
578 ret = krb5_domain_x500_decode(context,
579 tr->contents,
580 &realms,
581 &num_realms,
582 client_realm,
583 server_realm);
584 if(ret){
585 krb5_warn(context, ret,
586 "Decoding transited encoding");
587 return ret;
589 if(strcmp(client_realm, tgt_realm) && strcmp(server_realm, tgt_realm)) {
590 /* not us, so add the previous realm to transited set */
591 if (num_realms + 1 > UINT_MAX/sizeof(*realms)) {
592 ret = ERANGE;
593 goto free_realms;
595 tmp = realloc(realms, (num_realms + 1) * sizeof(*realms));
596 if(tmp == NULL){
597 ret = ENOMEM;
598 goto free_realms;
600 realms = tmp;
601 realms[num_realms] = strdup(tgt_realm);
602 if(realms[num_realms] == NULL){
603 ret = ENOMEM;
604 goto free_realms;
606 num_realms++;
608 if(num_realms == 0) {
609 if(strcmp(client_realm, server_realm))
610 kdc_log(context, config, 0,
611 "cross-realm %s -> %s", client_realm, server_realm);
612 } else {
613 size_t l = 0;
614 char *rs;
615 for(i = 0; i < num_realms; i++)
616 l += strlen(realms[i]) + 2;
617 rs = malloc(l);
618 if(rs != NULL) {
619 *rs = '\0';
620 for(i = 0; i < num_realms; i++) {
621 if(i > 0)
622 strlcat(rs, ", ", l);
623 strlcat(rs, realms[i], l);
625 kdc_log(context, config, 0,
626 "cross-realm %s -> %s via [%s]",
627 client_realm, server_realm, rs);
628 free(rs);
631 if(check_policy) {
632 ret = krb5_check_transited(context, client_realm,
633 server_realm,
634 realms, num_realms, NULL);
635 if(ret) {
636 krb5_warn(context, ret, "cross-realm %s -> %s",
637 client_realm, server_realm);
638 goto free_realms;
640 et->flags.transited_policy_checked = 1;
642 et->transited.tr_type = DOMAIN_X500_COMPRESS;
643 ret = krb5_domain_x500_encode(realms, num_realms, &et->transited.contents);
644 if(ret)
645 krb5_warn(context, ret, "Encoding transited encoding");
646 free_realms:
647 for(i = 0; i < num_realms; i++)
648 free(realms[i]);
649 free(realms);
650 return ret;
654 static krb5_error_code
655 tgs_make_reply(krb5_context context,
656 krb5_kdc_configuration *config,
657 KDC_REQ_BODY *b,
658 krb5_const_principal tgt_name,
659 const EncTicketPart *tgt,
660 const EncryptionKey *serverkey,
661 const krb5_keyblock *sessionkey,
662 krb5_kvno kvno,
663 AuthorizationData *auth_data,
664 hdb_entry_ex *server,
665 const char *server_name,
666 hdb_entry_ex *client,
667 krb5_principal client_principal,
668 hdb_entry_ex *krbtgt,
669 krb5_enctype krbtgt_etype,
670 KRB5SignedPathPrincipals *spp,
671 const krb5_data *rspac,
672 const METHOD_DATA *enc_pa_data,
673 const char **e_text,
674 krb5_data *reply)
676 KDC_REP rep;
677 EncKDCRepPart ek;
678 EncTicketPart et;
679 KDCOptions f = b->kdc_options;
680 krb5_error_code ret;
682 memset(&rep, 0, sizeof(rep));
683 memset(&et, 0, sizeof(et));
684 memset(&ek, 0, sizeof(ek));
686 rep.pvno = 5;
687 rep.msg_type = krb_tgs_rep;
689 et.authtime = tgt->authtime;
690 _kdc_fix_time(&b->till);
691 et.endtime = min(tgt->endtime, *b->till);
692 ALLOC(et.starttime);
693 *et.starttime = kdc_time;
695 ret = check_tgs_flags(context, config, b, tgt, &et);
696 if(ret)
697 goto out;
699 /* We should check the transited encoding if:
700 1) the request doesn't ask not to be checked
701 2) globally enforcing a check
702 3) principal requires checking
703 4) we allow non-check per-principal, but principal isn't marked as allowing this
704 5) we don't globally allow this
707 #define GLOBAL_FORCE_TRANSITED_CHECK \
708 (config->trpolicy == TRPOLICY_ALWAYS_CHECK)
709 #define GLOBAL_ALLOW_PER_PRINCIPAL \
710 (config->trpolicy == TRPOLICY_ALLOW_PER_PRINCIPAL)
711 #define GLOBAL_ALLOW_DISABLE_TRANSITED_CHECK \
712 (config->trpolicy == TRPOLICY_ALWAYS_HONOUR_REQUEST)
714 /* these will consult the database in future release */
715 #define PRINCIPAL_FORCE_TRANSITED_CHECK(P) 0
716 #define PRINCIPAL_ALLOW_DISABLE_TRANSITED_CHECK(P) 0
718 ret = fix_transited_encoding(context, config,
719 !f.disable_transited_check ||
720 GLOBAL_FORCE_TRANSITED_CHECK ||
721 PRINCIPAL_FORCE_TRANSITED_CHECK(server) ||
722 !((GLOBAL_ALLOW_PER_PRINCIPAL &&
723 PRINCIPAL_ALLOW_DISABLE_TRANSITED_CHECK(server)) ||
724 GLOBAL_ALLOW_DISABLE_TRANSITED_CHECK),
725 &tgt->transited, &et,
726 *krb5_princ_realm(context, client_principal),
727 *krb5_princ_realm(context, server->entry.principal),
728 *krb5_princ_realm(context, krbtgt->entry.principal));
729 if(ret)
730 goto out;
732 copy_Realm(krb5_princ_realm(context, server->entry.principal),
733 &rep.ticket.realm);
734 _krb5_principal2principalname(&rep.ticket.sname, server->entry.principal);
735 copy_Realm(&tgt_name->realm, &rep.crealm);
737 if (f.request_anonymous)
738 _kdc_make_anonymous_principalname (&rep.cname);
739 else */
741 copy_PrincipalName(&tgt_name->name, &rep.cname);
742 rep.ticket.tkt_vno = 5;
744 ek.caddr = et.caddr;
745 if(et.caddr == NULL)
746 et.caddr = tgt->caddr;
749 time_t life;
750 life = et.endtime - *et.starttime;
751 if(client && client->entry.max_life)
752 life = min(life, *client->entry.max_life);
753 if(server->entry.max_life)
754 life = min(life, *server->entry.max_life);
755 et.endtime = *et.starttime + life;
757 if(f.renewable_ok && tgt->flags.renewable &&
758 et.renew_till == NULL && et.endtime < *b->till){
759 et.flags.renewable = 1;
760 ALLOC(et.renew_till);
761 *et.renew_till = *b->till;
763 if(et.renew_till){
764 time_t renew;
765 renew = *et.renew_till - et.authtime;
766 if(client && client->entry.max_renew)
767 renew = min(renew, *client->entry.max_renew);
768 if(server->entry.max_renew)
769 renew = min(renew, *server->entry.max_renew);
770 *et.renew_till = et.authtime + renew;
773 if(et.renew_till){
774 *et.renew_till = min(*et.renew_till, *tgt->renew_till);
775 *et.starttime = min(*et.starttime, *et.renew_till);
776 et.endtime = min(et.endtime, *et.renew_till);
779 *et.starttime = min(*et.starttime, et.endtime);
781 if(*et.starttime == et.endtime){
782 ret = KRB5KDC_ERR_NEVER_VALID;
783 goto out;
785 if(et.renew_till && et.endtime == *et.renew_till){
786 free(et.renew_till);
787 et.renew_till = NULL;
788 et.flags.renewable = 0;
791 et.flags.pre_authent = tgt->flags.pre_authent;
792 et.flags.hw_authent = tgt->flags.hw_authent;
793 et.flags.anonymous = tgt->flags.anonymous;
794 et.flags.ok_as_delegate = server->entry.flags.ok_as_delegate;
796 if (auth_data) {
797 /* XXX Check enc-authorization-data */
798 et.authorization_data = calloc(1, sizeof(*et.authorization_data));
799 if (et.authorization_data == NULL) {
800 ret = ENOMEM;
801 goto out;
803 ret = copy_AuthorizationData(auth_data, et.authorization_data);
804 if (ret)
805 goto out;
807 /* Filter out type KRB5SignedPath */
808 ret = find_KRB5SignedPath(context, et.authorization_data, NULL);
809 if (ret == 0) {
810 if (et.authorization_data->len == 1) {
811 free_AuthorizationData(et.authorization_data);
812 free(et.authorization_data);
813 et.authorization_data = NULL;
814 } else {
815 AuthorizationData *ad = et.authorization_data;
816 free_AuthorizationDataElement(&ad->val[ad->len - 1]);
817 ad->len--;
822 if(rspac->length) {
824 * No not need to filter out the any PAC from the
825 * auth_data since it's signed by the KDC.
827 ret = _kdc_tkt_add_if_relevant_ad(context, &et,
828 KRB5_AUTHDATA_WIN2K_PAC,
829 rspac);
830 if (ret)
831 goto out;
834 ret = krb5_copy_keyblock_contents(context, sessionkey, &et.key);
835 if (ret)
836 goto out;
837 et.crealm = tgt->crealm;
838 et.cname = tgt_name->name;
840 ek.key = et.key;
841 /* MIT must have at least one last_req */
842 ek.last_req.len = 1;
843 ek.last_req.val = calloc(1, sizeof(*ek.last_req.val));
844 if (ek.last_req.val == NULL) {
845 ret = ENOMEM;
846 goto out;
848 ek.nonce = b->nonce;
849 ek.flags = et.flags;
850 ek.authtime = et.authtime;
851 ek.starttime = et.starttime;
852 ek.endtime = et.endtime;
853 ek.renew_till = et.renew_till;
854 ek.srealm = rep.ticket.realm;
855 ek.sname = rep.ticket.sname;
857 _kdc_log_timestamp(context, config, "TGS-REQ", et.authtime, et.starttime,
858 et.endtime, et.renew_till);
860 /* Don't sign cross realm tickets, they can't be checked anyway */
862 char *r = get_krbtgt_realm(&ek.sname);
864 if (r == NULL || strcmp(r, ek.srealm) == 0) {
865 ret = _kdc_add_KRB5SignedPath(context,
866 config,
867 krbtgt,
868 krbtgt_etype,
869 NULL,
870 spp,
871 &et);
872 if (ret)
873 goto out;
877 if (enc_pa_data->len) {
878 rep.padata = calloc(1, sizeof(*rep.padata));
879 if (rep.padata == NULL) {
880 ret = ENOMEM;
881 goto out;
883 ret = copy_METHOD_DATA(enc_pa_data, rep.padata);
884 if (ret)
885 goto out;
888 /* It is somewhat unclear where the etype in the following
889 encryption should come from. What we have is a session
890 key in the passed tgt, and a list of preferred etypes
891 *for the new ticket*. Should we pick the best possible
892 etype, given the keytype in the tgt, or should we look
893 at the etype list here as well? What if the tgt
894 session key is DES3 and we want a ticket with a (say)
895 CAST session key. Should the DES3 etype be added to the
896 etype list, even if we don't want a session key with
897 DES3? */
898 ret = _kdc_encode_reply(context, config,
899 &rep, &et, &ek, et.key.keytype,
900 kvno,
901 serverkey, 0, &tgt->key, e_text, reply);
902 out:
903 free_TGS_REP(&rep);
904 free_TransitedEncoding(&et.transited);
905 if(et.starttime)
906 free(et.starttime);
907 if(et.renew_till)
908 free(et.renew_till);
909 if(et.authorization_data) {
910 free_AuthorizationData(et.authorization_data);
911 free(et.authorization_data);
913 free_LastReq(&ek.last_req);
914 memset(et.key.keyvalue.data, 0, et.key.keyvalue.length);
915 free_EncryptionKey(&et.key);
916 return ret;
919 static krb5_error_code
920 tgs_check_authenticator(krb5_context context,
921 krb5_kdc_configuration *config,
922 krb5_auth_context ac,
923 KDC_REQ_BODY *b,
924 const char **e_text,
925 krb5_keyblock *key)
927 krb5_authenticator auth;
928 size_t len;
929 unsigned char *buf;
930 size_t buf_size;
931 krb5_error_code ret;
932 krb5_crypto crypto;
934 krb5_auth_con_getauthenticator(context, ac, &auth);
935 if(auth->cksum == NULL){
936 kdc_log(context, config, 0, "No authenticator in request");
937 ret = KRB5KRB_AP_ERR_INAPP_CKSUM;
938 goto out;
941 * according to RFC1510 it doesn't need to be keyed,
942 * but according to the latest draft it needs to.
944 if (
945 #if 0
946 !krb5_checksum_is_keyed(context, auth->cksum->cksumtype)
948 #endif
949 !krb5_checksum_is_collision_proof(context, auth->cksum->cksumtype)) {
950 kdc_log(context, config, 0, "Bad checksum type in authenticator: %d",
951 auth->cksum->cksumtype);
952 ret = KRB5KRB_AP_ERR_INAPP_CKSUM;
953 goto out;
956 /* XXX should not re-encode this */
957 ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, b, &len, ret);
958 if(ret){
959 kdc_log(context, config, 0, "Failed to encode KDC-REQ-BODY: %s",
960 krb5_get_err_text(context, ret));
961 goto out;
963 if(buf_size != len) {
964 free(buf);
965 kdc_log(context, config, 0, "Internal error in ASN.1 encoder");
966 *e_text = "KDC internal error";
967 ret = KRB5KRB_ERR_GENERIC;
968 goto out;
970 ret = krb5_crypto_init(context, key, 0, &crypto);
971 if (ret) {
972 free(buf);
973 kdc_log(context, config, 0, "krb5_crypto_init failed: %s",
974 krb5_get_err_text(context, ret));
975 goto out;
977 ret = krb5_verify_checksum(context,
978 crypto,
979 KRB5_KU_TGS_REQ_AUTH_CKSUM,
980 buf,
981 len,
982 auth->cksum);
983 free(buf);
984 krb5_crypto_destroy(context, crypto);
985 if(ret){
986 kdc_log(context, config, 0,
987 "Failed to verify authenticator checksum: %s",
988 krb5_get_err_text(context, ret));
990 out:
991 free_Authenticator(auth);
992 free(auth);
993 return ret;
1000 static const char *
1001 find_rpath(krb5_context context, Realm crealm, Realm srealm)
1003 const char *new_realm = krb5_config_get_string(context,
1004 NULL,
1005 "capaths",
1006 crealm,
1007 srealm,
1008 NULL);
1009 return new_realm;
1013 static krb5_boolean
1014 need_referral(krb5_context context, krb5_kdc_configuration *config,
1015 const KDCOptions * const options, krb5_principal server,
1016 krb5_realm **realms)
1018 const char *name;
1020 if(!options->canonicalize && server->name.name_type != KRB5_NT_SRV_INST)
1021 return FALSE;
1023 if (server->name.name_string.len == 1)
1024 name = server->name.name_string.val[0];
1025 if (server->name.name_string.len > 1)
1026 name = server->name.name_string.val[1];
1027 else
1028 return FALSE;
1030 kdc_log(context, config, 0, "Searching referral for %s", name);
1032 return _krb5_get_host_realm_int(context, name, FALSE, realms) == 0;
1035 static krb5_error_code
1036 tgs_parse_request(krb5_context context,
1037 krb5_kdc_configuration *config,
1038 KDC_REQ_BODY *b,
1039 const PA_DATA *tgs_req,
1040 hdb_entry_ex **krbtgt,
1041 krb5_enctype *krbtgt_etype,
1042 krb5_ticket **ticket,
1043 const char **e_text,
1044 const char *from,
1045 const struct sockaddr *from_addr,
1046 time_t **csec,
1047 int **cusec,
1048 AuthorizationData **auth_data)
1050 krb5_ap_req ap_req;
1051 krb5_error_code ret;
1052 krb5_principal princ;
1053 krb5_auth_context ac = NULL;
1054 krb5_flags ap_req_options;
1055 krb5_flags verify_ap_req_flags;
1056 krb5_crypto crypto;
1057 Key *tkey;
1059 *auth_data = NULL;
1060 *csec = NULL;
1061 *cusec = NULL;
1063 memset(&ap_req, 0, sizeof(ap_req));
1064 ret = krb5_decode_ap_req(context, &tgs_req->padata_value, &ap_req);
1065 if(ret){
1066 kdc_log(context, config, 0, "Failed to decode AP-REQ: %s",
1067 krb5_get_err_text(context, ret));
1068 goto out;
1071 if(!get_krbtgt_realm(&ap_req.ticket.sname)){
1072 /* XXX check for ticket.sname == req.sname */
1073 kdc_log(context, config, 0, "PA-DATA is not a ticket-granting ticket");
1074 ret = KRB5KDC_ERR_POLICY; /* ? */
1075 goto out;
1078 _krb5_principalname2krb5_principal(context,
1079 &princ,
1080 ap_req.ticket.sname,
1081 ap_req.ticket.realm);
1083 ret = _kdc_db_fetch(context, config, princ, HDB_F_GET_KRBTGT, NULL, krbtgt);
1085 if(ret) {
1086 char *p;
1087 ret = krb5_unparse_name(context, princ, &p);
1088 if (ret != 0)
1089 p = "<unparse_name failed>";
1090 krb5_free_principal(context, princ);
1091 kdc_log(context, config, 0,
1092 "Ticket-granting ticket not found in database: %s: %s",
1093 p, krb5_get_err_text(context, ret));
1094 if (ret == 0)
1095 free(p);
1096 ret = KRB5KRB_AP_ERR_NOT_US;
1097 goto out;
1100 if(ap_req.ticket.enc_part.kvno &&
1101 *ap_req.ticket.enc_part.kvno != (*krbtgt)->entry.kvno){
1102 char *p;
1104 ret = krb5_unparse_name (context, princ, &p);
1105 krb5_free_principal(context, princ);
1106 if (ret != 0)
1107 p = "<unparse_name failed>";
1108 kdc_log(context, config, 0,
1109 "Ticket kvno = %d, DB kvno = %d (%s)",
1110 *ap_req.ticket.enc_part.kvno,
1111 (*krbtgt)->entry.kvno,
1113 if (ret == 0)
1114 free (p);
1115 ret = KRB5KRB_AP_ERR_BADKEYVER;
1116 goto out;
1119 *krbtgt_etype = ap_req.ticket.enc_part.etype;
1121 ret = hdb_enctype2key(context, &(*krbtgt)->entry,
1122 ap_req.ticket.enc_part.etype, &tkey);
1123 if(ret){
1124 char *str = NULL, *p = NULL;
1126 krb5_enctype_to_string(context, ap_req.ticket.enc_part.etype, &str);
1127 krb5_unparse_name(context, princ, &p);
1128 kdc_log(context, config, 0,
1129 "No server key with enctype %s found for %s",
1130 str ? str : "<unknown enctype>",
1131 p ? p : "<unparse_name failed>");
1132 free(str);
1133 free(p);
1134 ret = KRB5KRB_AP_ERR_BADKEYVER;
1135 goto out;
1138 if (b->kdc_options.validate)
1139 verify_ap_req_flags = KRB5_VERIFY_AP_REQ_IGNORE_INVALID;
1140 else
1141 verify_ap_req_flags = 0;
1143 ret = krb5_verify_ap_req2(context,
1144 &ac,
1145 &ap_req,
1146 princ,
1147 &tkey->key,
1148 verify_ap_req_flags,
1149 &ap_req_options,
1150 ticket,
1151 KRB5_KU_TGS_REQ_AUTH);
1153 krb5_free_principal(context, princ);
1154 if(ret) {
1155 kdc_log(context, config, 0, "Failed to verify AP-REQ: %s",
1156 krb5_get_err_text(context, ret));
1157 goto out;
1161 krb5_authenticator auth;
1163 ret = krb5_auth_con_getauthenticator(context, ac, &auth);
1164 if (ret == 0) {
1165 *csec = malloc(sizeof(**csec));
1166 if (*csec == NULL) {
1167 krb5_free_authenticator(context, &auth);
1168 kdc_log(context, config, 0, "malloc failed");
1169 goto out;
1171 **csec = auth->ctime;
1172 *cusec = malloc(sizeof(**cusec));
1173 if (*cusec == NULL) {
1174 krb5_free_authenticator(context, &auth);
1175 kdc_log(context, config, 0, "malloc failed");
1176 goto out;
1178 **cusec = auth->cusec;
1179 krb5_free_authenticator(context, &auth);
1183 ret = tgs_check_authenticator(context, config,
1184 ac, b, e_text, &(*ticket)->ticket.key);
1185 if (ret) {
1186 krb5_auth_con_free(context, ac);
1187 goto out;
1190 if (b->enc_authorization_data) {
1191 unsigned usage = KRB5_KU_TGS_REQ_AUTH_DAT_SUBKEY;
1192 krb5_keyblock *subkey;
1193 krb5_data ad;
1195 ret = krb5_auth_con_getremotesubkey(context,
1197 &subkey);
1198 if(ret){
1199 krb5_auth_con_free(context, ac);
1200 kdc_log(context, config, 0, "Failed to get remote subkey: %s",
1201 krb5_get_err_text(context, ret));
1202 goto out;
1204 if(subkey == NULL){
1205 usage = KRB5_KU_TGS_REQ_AUTH_DAT_SESSION;
1206 ret = krb5_auth_con_getkey(context, ac, &subkey);
1207 if(ret) {
1208 krb5_auth_con_free(context, ac);
1209 kdc_log(context, config, 0, "Failed to get session key: %s",
1210 krb5_get_err_text(context, ret));
1211 goto out;
1214 if(subkey == NULL){
1215 krb5_auth_con_free(context, ac);
1216 kdc_log(context, config, 0,
1217 "Failed to get key for enc-authorization-data");
1218 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
1219 goto out;
1221 ret = krb5_crypto_init(context, subkey, 0, &crypto);
1222 if (ret) {
1223 krb5_auth_con_free(context, ac);
1224 kdc_log(context, config, 0, "krb5_crypto_init failed: %s",
1225 krb5_get_err_text(context, ret));
1226 goto out;
1228 ret = krb5_decrypt_EncryptedData (context,
1229 crypto,
1230 usage,
1231 b->enc_authorization_data,
1232 &ad);
1233 krb5_crypto_destroy(context, crypto);
1234 if(ret){
1235 krb5_auth_con_free(context, ac);
1236 kdc_log(context, config, 0,
1237 "Failed to decrypt enc-authorization-data");
1238 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
1239 goto out;
1241 krb5_free_keyblock(context, subkey);
1242 ALLOC(*auth_data);
1243 if (*auth_data == NULL) {
1244 krb5_auth_con_free(context, ac);
1245 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
1246 goto out;
1248 ret = decode_AuthorizationData(ad.data, ad.length, *auth_data, NULL);
1249 if(ret){
1250 krb5_auth_con_free(context, ac);
1251 free(*auth_data);
1252 *auth_data = NULL;
1253 kdc_log(context, config, 0, "Failed to decode authorization data");
1254 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
1255 goto out;
1259 krb5_auth_con_free(context, ac);
1261 out:
1262 free_AP_REQ(&ap_req);
1264 return ret;
1267 static krb5_error_code
1268 build_server_referral(krb5_context context,
1269 krb5_kdc_configuration *config,
1270 krb5_crypto session,
1271 krb5_const_realm referred_realm,
1272 const PrincipalName *true_principal_name,
1273 const PrincipalName *requested_principal,
1274 krb5_data *outdata)
1276 PA_ServerReferralData ref;
1277 krb5_error_code ret;
1278 EncryptedData ed;
1279 krb5_data data;
1280 size_t size;
1282 memset(&ref, 0, sizeof(ref));
1284 if (referred_realm) {
1285 ref.referred_realm = malloc(sizeof(ref.referred_realm));
1286 if (ref.referred_realm == NULL)
1287 goto eout;
1288 *ref.referred_realm = strdup(referred_realm);
1289 if (*ref.referred_realm == NULL)
1290 goto eout;
1292 if (true_principal_name) {
1293 ref.true_principal_name =
1294 malloc(sizeof(ref.true_principal_name));
1295 if (ref.true_principal_name == NULL)
1296 goto eout;
1297 ret = copy_PrincipalName(true_principal_name, ref.true_principal_name);
1298 if (ret)
1299 goto eout;
1301 if (requested_principal) {
1302 ref.requested_principal_name =
1303 malloc(sizeof(ref.requested_principal_name));
1304 if (ref.requested_principal_name == NULL)
1305 goto eout;
1306 ret = copy_PrincipalName(requested_principal,
1307 ref.requested_principal_name);
1308 if (ret)
1309 goto eout;
1312 ASN1_MALLOC_ENCODE(PA_ServerReferralData,
1313 data.data, data.length,
1314 &ref, &size, ret);
1315 free_PA_ServerReferralData(&ref);
1316 if (ret)
1317 return ret;
1318 if (data.length != size)
1319 krb5_abortx(context, "internal asn.1 encoder error");
1321 ret = krb5_encrypt_EncryptedData(context, session,
1322 KRB5_KU_PA_SERVER_REFERRAL,
1323 data.data, data.length,
1324 0 /* kvno */, &ed);
1325 free(data.data);
1326 if (ret)
1327 return ret;
1329 ASN1_MALLOC_ENCODE(EncryptedData,
1330 outdata->data, outdata->length,
1331 &ed, &size, ret);
1332 free_EncryptedData(&ed);
1333 if (ret)
1334 return ret;
1335 if (outdata->length != size)
1336 krb5_abortx(context, "internal asn.1 encoder error");
1338 return 0;
1339 eout:
1340 free_PA_ServerReferralData(&ref);
1341 krb5_set_error_string(context, "out of memory");
1342 return ENOMEM;
1345 static krb5_error_code
1346 tgs_build_reply(krb5_context context,
1347 krb5_kdc_configuration *config,
1348 KDC_REQ *req,
1349 KDC_REQ_BODY *b,
1350 hdb_entry_ex *krbtgt,
1351 krb5_enctype krbtgt_etype,
1352 krb5_ticket *ticket,
1353 krb5_data *reply,
1354 const char *from,
1355 const char **e_text,
1356 AuthorizationData **auth_data,
1357 const struct sockaddr *from_addr,
1358 int datagram_reply)
1360 krb5_error_code ret;
1361 krb5_principal cp = NULL, sp = NULL;
1362 krb5_principal client_principal = NULL;
1363 char *spn = NULL, *cpn = NULL;
1364 hdb_entry_ex *server = NULL, *client = NULL;
1365 krb5_realm ref_realm = NULL;
1366 EncTicketPart *tgt = &ticket->ticket;
1367 KRB5SignedPathPrincipals *spp = NULL;
1368 const EncryptionKey *ekey;
1369 krb5_keyblock sessionkey;
1370 krb5_kvno kvno;
1371 krb5_data rspac;
1372 int cross_realm = 0;
1374 METHOD_DATA enc_pa_data;
1376 PrincipalName *s;
1377 Realm r;
1378 int nloop = 0;
1379 EncTicketPart adtkt;
1380 char opt_str[128];
1381 int signedpath = 0;
1383 memset(&sessionkey, 0, sizeof(sessionkey));
1384 memset(&adtkt, 0, sizeof(adtkt));
1385 krb5_data_zero(&rspac);
1386 memset(&enc_pa_data, 0, sizeof(enc_pa_data));
1388 s = b->sname;
1389 r = b->realm;
1391 if(b->kdc_options.enc_tkt_in_skey){
1392 Ticket *t;
1393 hdb_entry_ex *uu;
1394 krb5_principal p;
1395 Key *uukey;
1397 if(b->additional_tickets == NULL ||
1398 b->additional_tickets->len == 0){
1399 ret = KRB5KDC_ERR_BADOPTION; /* ? */
1400 kdc_log(context, config, 0,
1401 "No second ticket present in request");
1402 goto out;
1404 t = &b->additional_tickets->val[0];
1405 if(!get_krbtgt_realm(&t->sname)){
1406 kdc_log(context, config, 0,
1407 "Additional ticket is not a ticket-granting ticket");
1408 ret = KRB5KDC_ERR_POLICY;
1409 goto out;
1411 _krb5_principalname2krb5_principal(context, &p, t->sname, t->realm);
1412 ret = _kdc_db_fetch(context, config, p,
1413 HDB_F_GET_CLIENT|HDB_F_GET_SERVER,
1414 NULL, &uu);
1415 krb5_free_principal(context, p);
1416 if(ret){
1417 if (ret == HDB_ERR_NOENTRY)
1418 ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
1419 goto out;
1421 ret = hdb_enctype2key(context, &uu->entry,
1422 t->enc_part.etype, &uukey);
1423 if(ret){
1424 _kdc_free_ent(context, uu);
1425 ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */
1426 goto out;
1428 ret = krb5_decrypt_ticket(context, t, &uukey->key, &adtkt, 0);
1429 _kdc_free_ent(context, uu);
1430 if(ret)
1431 goto out;
1433 ret = verify_flags(context, config, &adtkt, spn);
1434 if (ret)
1435 goto out;
1437 s = &adtkt.cname;
1438 r = adtkt.crealm;
1441 _krb5_principalname2krb5_principal(context, &sp, *s, r);
1442 ret = krb5_unparse_name(context, sp, &spn);
1443 if (ret)
1444 goto out;
1445 _krb5_principalname2krb5_principal(context, &cp, tgt->cname, tgt->crealm);
1446 ret = krb5_unparse_name(context, cp, &cpn);
1447 if (ret)
1448 goto out;
1449 unparse_flags (KDCOptions2int(b->kdc_options),
1450 asn1_KDCOptions_units(),
1451 opt_str, sizeof(opt_str));
1452 if(*opt_str)
1453 kdc_log(context, config, 0,
1454 "TGS-REQ %s from %s for %s [%s]",
1455 cpn, from, spn, opt_str);
1456 else
1457 kdc_log(context, config, 0,
1458 "TGS-REQ %s from %s for %s", cpn, from, spn);
1461 * Fetch server
1464 server_lookup:
1465 ret = _kdc_db_fetch(context, config, sp, HDB_F_GET_SERVER, NULL, &server);
1467 if(ret){
1468 const char *new_rlm;
1469 Realm req_rlm;
1470 krb5_realm *realms;
1472 if ((req_rlm = get_krbtgt_realm(&sp->name)) != NULL) {
1473 if(nloop++ < 2) {
1474 new_rlm = find_rpath(context, tgt->crealm, req_rlm);
1475 if(new_rlm) {
1476 kdc_log(context, config, 5, "krbtgt for realm %s "
1477 "not found, trying %s",
1478 req_rlm, new_rlm);
1479 krb5_free_principal(context, sp);
1480 free(spn);
1481 krb5_make_principal(context, &sp, r,
1482 KRB5_TGS_NAME, new_rlm, NULL);
1483 ret = krb5_unparse_name(context, sp, &spn);
1484 if (ret)
1485 goto out;
1487 if (ref_realm)
1488 free(ref_realm);
1489 ref_realm = strdup(new_rlm);
1490 goto server_lookup;
1493 } else if(need_referral(context, config, &b->kdc_options, sp, &realms)) {
1494 if (strcmp(realms[0], sp->realm) != 0) {
1495 kdc_log(context, config, 5,
1496 "Returning a referral to realm %s for "
1497 "server %s that was not found",
1498 realms[0], spn);
1499 krb5_free_principal(context, sp);
1500 free(spn);
1501 krb5_make_principal(context, &sp, r, KRB5_TGS_NAME,
1502 realms[0], NULL);
1503 ret = krb5_unparse_name(context, sp, &spn);
1504 if (ret)
1505 goto out;
1507 if (ref_realm)
1508 free(ref_realm);
1509 ref_realm = strdup(realms[0]);
1511 krb5_free_host_realm(context, realms);
1512 goto server_lookup;
1514 krb5_free_host_realm(context, realms);
1516 kdc_log(context, config, 0,
1517 "Server not found in database: %s: %s", spn,
1518 krb5_get_err_text(context, ret));
1519 if (ret == HDB_ERR_NOENTRY)
1520 ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
1521 goto out;
1524 ret = _kdc_db_fetch(context, config, cp, HDB_F_GET_CLIENT, NULL, &client);
1525 if(ret) {
1526 const char *krbtgt_realm;
1529 * If the client belongs to the same realm as our krbtgt, it
1530 * should exist in the local database.
1534 krbtgt_realm =
1535 krb5_principal_get_comp_string(context,
1536 krbtgt->entry.principal, 1);
1538 if(strcmp(krb5_principal_get_realm(context, cp), krbtgt_realm) == 0) {
1539 if (ret == HDB_ERR_NOENTRY)
1540 ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
1541 kdc_log(context, config, 1, "Client no longer in database: %s",
1542 cpn);
1543 goto out;
1546 kdc_log(context, config, 1, "Client not found in database: %s: %s",
1547 cpn, krb5_get_err_text(context, ret));
1549 cross_realm = 1;
1553 * Select enctype, return key and kvno.
1557 krb5_enctype etype;
1559 if(b->kdc_options.enc_tkt_in_skey) {
1560 int i;
1561 ekey = &adtkt.key;
1562 for(i = 0; i < b->etype.len; i++)
1563 if (b->etype.val[i] == adtkt.key.keytype)
1564 break;
1565 if(i == b->etype.len) {
1566 kdc_log(context, config, 0,
1567 "Addition ticket have not matching etypes", spp);
1568 krb5_clear_error_string(context);
1569 return KRB5KDC_ERR_ETYPE_NOSUPP;
1571 etype = b->etype.val[i];
1572 kvno = 0;
1573 } else {
1574 Key *skey;
1576 ret = _kdc_find_etype(context, server, b->etype.val, b->etype.len,
1577 &skey, &etype);
1578 if(ret) {
1579 kdc_log(context, config, 0,
1580 "Server (%s) has no support for etypes", spn);
1581 return ret;
1583 ekey = &skey->key;
1584 kvno = server->entry.kvno;
1587 ret = krb5_generate_random_keyblock(context, etype, &sessionkey);
1588 if (ret)
1589 goto out;
1593 * Validate authoriation data
1597 * Check that service is in the same realm as the krbtgt. If it's
1598 * not the same, it's someone that is using a uni-directional trust
1599 * backward.
1602 if (strcmp(krb5_principal_get_realm(context, sp),
1603 krb5_principal_get_comp_string(context,
1604 krbtgt->entry.principal,
1605 1)) != 0) {
1606 char *tpn;
1607 ret = krb5_unparse_name(context, krbtgt->entry.principal, &tpn);
1608 kdc_log(context, config, 0,
1609 "Request with wrong krbtgt: %s",
1610 (ret == 0) ? tpn : "<unknown>");
1611 if(ret == 0)
1612 free(tpn);
1613 ret = KRB5KRB_AP_ERR_NOT_US;
1614 goto out;
1617 /* check PAC if not cross realm and if there is one */
1618 if (!cross_realm) {
1619 Key *tkey;
1621 ret = hdb_enctype2key(context, &krbtgt->entry,
1622 krbtgt_etype, &tkey);
1623 if(ret) {
1624 kdc_log(context, config, 0,
1625 "Failed to find key for krbtgt PAC check");
1626 goto out;
1629 ret = check_PAC(context, config, cp,
1630 client, server, ekey, &tkey->key,
1631 tgt, &rspac, &signedpath);
1632 if (ret) {
1633 kdc_log(context, config, 0,
1634 "Verify PAC failed for %s (%s) from %s with %s",
1635 spn, cpn, from, krb5_get_err_text(context, ret));
1636 goto out;
1640 /* also check the krbtgt for signature */
1641 ret = check_KRB5SignedPath(context,
1642 config,
1643 krbtgt,
1644 tgt,
1645 &spp,
1646 &signedpath);
1647 if (ret) {
1648 kdc_log(context, config, 0,
1649 "KRB5SignedPath check failed for %s (%s) from %s with %s",
1650 spn, cpn, from, krb5_get_err_text(context, ret));
1651 goto out;
1655 * Process request
1658 client_principal = cp;
1660 if (client) {
1661 const PA_DATA *sdata;
1662 int i = 0;
1664 sdata = _kdc_find_padata(req, &i, KRB5_PADATA_S4U2SELF);
1665 if (sdata) {
1666 krb5_crypto crypto;
1667 krb5_data datack;
1668 PA_S4U2Self self;
1669 char *selfcpn = NULL;
1670 const char *str;
1672 ret = decode_PA_S4U2Self(sdata->padata_value.data,
1673 sdata->padata_value.length,
1674 &self, NULL);
1675 if (ret) {
1676 kdc_log(context, config, 0, "Failed to decode PA-S4U2Self");
1677 goto out;
1680 ret = _krb5_s4u2self_to_checksumdata(context, &self, &datack);
1681 if (ret)
1682 goto out;
1684 ret = krb5_crypto_init(context, &tgt->key, 0, &crypto);
1685 if (ret) {
1686 free_PA_S4U2Self(&self);
1687 krb5_data_free(&datack);
1688 kdc_log(context, config, 0, "krb5_crypto_init failed: %s",
1689 krb5_get_err_text(context, ret));
1690 goto out;
1693 ret = krb5_verify_checksum(context,
1694 crypto,
1695 KRB5_KU_OTHER_CKSUM,
1696 datack.data,
1697 datack.length,
1698 &self.cksum);
1699 krb5_data_free(&datack);
1700 krb5_crypto_destroy(context, crypto);
1701 if (ret) {
1702 free_PA_S4U2Self(&self);
1703 kdc_log(context, config, 0,
1704 "krb5_verify_checksum failed for S4U2Self: %s",
1705 krb5_get_err_text(context, ret));
1706 goto out;
1709 ret = _krb5_principalname2krb5_principal(context,
1710 &client_principal,
1711 self.name,
1712 self.realm);
1713 free_PA_S4U2Self(&self);
1714 if (ret)
1715 goto out;
1717 ret = krb5_unparse_name(context, client_principal, &selfcpn);
1718 if (ret)
1719 goto out;
1722 * Check that service doing the impersonating is
1723 * requesting a ticket to it-self.
1725 if (krb5_principal_compare(context, cp, sp) != TRUE) {
1726 kdc_log(context, config, 0, "S4U2Self: %s is not allowed "
1727 "to impersonate some other user "
1728 "(tried for user %s to service %s)",
1729 cpn, selfcpn, spn);
1730 free(selfcpn);
1731 ret = KRB5KDC_ERR_BADOPTION; /* ? */
1732 goto out;
1736 * If the service isn't trusted for authentication to
1737 * delegation, remove the forward flag.
1740 if (client->entry.flags.trusted_for_delegation) {
1741 str = "[forwardable]";
1742 } else {
1743 b->kdc_options.forwardable = 0;
1744 str = "";
1746 kdc_log(context, config, 0, "s4u2self %s impersonating %s to "
1747 "service %s %s", cpn, selfcpn, spn, str);
1748 free(selfcpn);
1753 * Constrained delegation
1756 if (client != NULL
1757 && b->additional_tickets != NULL
1758 && b->additional_tickets->len != 0
1759 && b->kdc_options.enc_tkt_in_skey == 0)
1761 int ad_signedpath = 0;
1762 Key *clientkey;
1763 Ticket *t;
1764 char *str;
1767 * Require that the KDC have issued the service's krbtgt (not
1768 * self-issued ticket with kimpersonate(1).
1770 if (!signedpath) {
1771 ret = KRB5KDC_ERR_BADOPTION;
1772 kdc_log(context, config, 0,
1773 "Constrained delegation done on service ticket %s/%s",
1774 cpn, spn);
1775 goto out;
1778 t = &b->additional_tickets->val[0];
1780 ret = hdb_enctype2key(context, &client->entry,
1781 t->enc_part.etype, &clientkey);
1782 if(ret){
1783 ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */
1784 goto out;
1787 ret = krb5_decrypt_ticket(context, t, &clientkey->key, &adtkt, 0);
1788 if (ret) {
1789 kdc_log(context, config, 0,
1790 "failed to decrypt ticket for "
1791 "constrained delegation from %s to %s ", spn, cpn);
1792 goto out;
1795 /* check that ticket is valid */
1796 if (adtkt.flags.forwardable == 0) {
1797 kdc_log(context, config, 0,
1798 "Missing forwardable flag on ticket for "
1799 "constrained delegation from %s to %s ", spn, cpn);
1800 ret = KRB5KDC_ERR_BADOPTION;
1801 goto out;
1804 ret = check_constrained_delegation(context, config, client, sp);
1805 if (ret) {
1806 kdc_log(context, config, 0,
1807 "constrained delegation from %s to %s not allowed",
1808 spn, cpn);
1809 goto out;
1812 ret = _krb5_principalname2krb5_principal(context,
1813 &client_principal,
1814 adtkt.cname,
1815 adtkt.crealm);
1816 if (ret)
1817 goto out;
1819 ret = krb5_unparse_name(context, client_principal, &str);
1820 if (ret)
1821 goto out;
1823 ret = verify_flags(context, config, &adtkt, str);
1824 if (ret) {
1825 free(str);
1826 goto out;
1830 * Check that the KDC issued the user's ticket.
1832 ret = check_KRB5SignedPath(context,
1833 config,
1834 krbtgt,
1835 &adtkt,
1836 NULL,
1837 &ad_signedpath);
1838 if (ret == 0 && !ad_signedpath)
1839 ret = KRB5KDC_ERR_BADOPTION;
1840 if (ret) {
1841 kdc_log(context, config, 0,
1842 "KRB5SignedPath check from service %s failed "
1843 "for delegation to %s for client %s "
1844 "from %s failed with %s",
1845 spn, str, cpn, from, krb5_get_err_text(context, ret));
1846 free(str);
1847 goto out;
1850 kdc_log(context, config, 0, "constrained delegation for %s "
1851 "from %s to %s", str, cpn, spn);
1852 free(str);
1856 * Check flags
1859 ret = _kdc_check_flags(context, config,
1860 client, cpn,
1861 server, spn,
1862 FALSE);
1863 if(ret)
1864 goto out;
1866 if((b->kdc_options.validate || b->kdc_options.renew) &&
1867 !krb5_principal_compare(context,
1868 krbtgt->entry.principal,
1869 server->entry.principal)){
1870 kdc_log(context, config, 0, "Inconsistent request.");
1871 ret = KRB5KDC_ERR_SERVER_NOMATCH;
1872 goto out;
1875 /* check for valid set of addresses */
1876 if(!_kdc_check_addresses(context, config, tgt->caddr, from_addr)) {
1877 ret = KRB5KRB_AP_ERR_BADADDR;
1878 kdc_log(context, config, 0, "Request from wrong address");
1879 goto out;
1883 * If this is an referral, add server referral data to the
1884 * auth_data reply .
1886 if (ref_realm) {
1887 PA_DATA pa;
1888 krb5_crypto crypto;
1890 kdc_log(context, config, 0,
1891 "Adding server referral to %s", ref_realm);
1893 ret = krb5_crypto_init(context, &sessionkey, 0, &crypto);
1894 if (ret)
1895 goto out;
1897 ret = build_server_referral(context, config, crypto, ref_realm,
1898 NULL, s, &pa.padata_value);
1899 krb5_crypto_destroy(context, crypto);
1900 if (ret) {
1901 kdc_log(context, config, 0,
1902 "Failed building server referral");
1903 goto out;
1905 pa.padata_type = KRB5_PADATA_SERVER_REFERRAL;
1907 ret = add_METHOD_DATA(&enc_pa_data, &pa);
1908 krb5_data_free(&pa.padata_value);
1909 if (ret) {
1910 kdc_log(context, config, 0,
1911 "Add server referral METHOD-DATA failed");
1912 goto out;
1920 ret = tgs_make_reply(context,
1921 config,
1923 client_principal,
1924 tgt,
1925 ekey,
1926 &sessionkey,
1927 kvno,
1928 *auth_data,
1929 server,
1930 spn,
1931 client,
1933 krbtgt,
1934 krbtgt_etype,
1935 spp,
1936 &rspac,
1937 &enc_pa_data,
1938 e_text,
1939 reply);
1941 out:
1942 free(spn);
1943 free(cpn);
1945 krb5_data_free(&rspac);
1946 krb5_free_keyblock_contents(context, &sessionkey);
1947 if(server)
1948 _kdc_free_ent(context, server);
1949 if(client)
1950 _kdc_free_ent(context, client);
1952 if (client_principal && client_principal != cp)
1953 krb5_free_principal(context, client_principal);
1954 if (cp)
1955 krb5_free_principal(context, cp);
1956 if (sp)
1957 krb5_free_principal(context, sp);
1958 if (ref_realm)
1959 free(ref_realm);
1960 free_METHOD_DATA(&enc_pa_data);
1962 free_EncTicketPart(&adtkt);
1964 return ret;
1971 krb5_error_code
1972 _kdc_tgs_rep(krb5_context context,
1973 krb5_kdc_configuration *config,
1974 KDC_REQ *req,
1975 krb5_data *data,
1976 const char *from,
1977 struct sockaddr *from_addr,
1978 int datagram_reply)
1980 AuthorizationData *auth_data = NULL;
1981 krb5_error_code ret;
1982 int i = 0;
1983 const PA_DATA *tgs_req;
1985 hdb_entry_ex *krbtgt = NULL;
1986 krb5_ticket *ticket = NULL;
1987 const char *e_text = NULL;
1988 krb5_enctype krbtgt_etype = ETYPE_NULL;
1990 time_t *csec = NULL;
1991 int *cusec = NULL;
1993 if(req->padata == NULL){
1994 ret = KRB5KDC_ERR_PREAUTH_REQUIRED; /* XXX ??? */
1995 kdc_log(context, config, 0,
1996 "TGS-REQ from %s without PA-DATA", from);
1997 goto out;
2000 tgs_req = _kdc_find_padata(req, &i, KRB5_PADATA_TGS_REQ);
2002 if(tgs_req == NULL){
2003 ret = KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
2005 kdc_log(context, config, 0,
2006 "TGS-REQ from %s without PA-TGS-REQ", from);
2007 goto out;
2009 ret = tgs_parse_request(context, config,
2010 &req->req_body, tgs_req,
2011 &krbtgt,
2012 &krbtgt_etype,
2013 &ticket,
2014 &e_text,
2015 from, from_addr,
2016 &csec, &cusec,
2017 &auth_data);
2018 if (ret) {
2019 kdc_log(context, config, 0,
2020 "Failed parsing TGS-REQ from %s", from);
2021 goto out;
2024 ret = tgs_build_reply(context,
2025 config,
2026 req,
2027 &req->req_body,
2028 krbtgt,
2029 krbtgt_etype,
2030 ticket,
2031 data,
2032 from,
2033 &e_text,
2034 &auth_data,
2035 from_addr,
2036 datagram_reply);
2037 if (ret) {
2038 kdc_log(context, config, 0,
2039 "Failed building TGS-REP to %s", from);
2040 goto out;
2043 /* */
2044 if (datagram_reply && data->length > config->max_datagram_reply_length) {
2045 krb5_data_free(data);
2046 ret = KRB5KRB_ERR_RESPONSE_TOO_BIG;
2047 e_text = "Reply packet too large";
2050 out:
2051 if(ret && data->data == NULL){
2052 krb5_mk_error(context,
2053 ret,
2054 NULL,
2055 NULL,
2056 NULL,
2057 NULL,
2058 csec,
2059 cusec,
2060 data);
2062 free(csec);
2063 free(cusec);
2064 if (ticket)
2065 krb5_free_ticket(context, ticket);
2066 if(krbtgt)
2067 _kdc_free_ent(context, krbtgt);
2069 if (auth_data) {
2070 free_AuthorizationData(auth_data);
2071 free(auth_data);
2074 return 0;