2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
10 * Copyright 1990,1991,2001 by the Massachusetts Institute of Technology.
11 * All Rights Reserved.
13 * Export of this software from the United States of America may
14 * require a specific license from the United States Government.
15 * It is the responsibility of any person or organization contemplating
16 * export to obtain such a license before exporting.
18 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
19 * distribute this software and its documentation for any purpose and
20 * without fee is hereby granted, provided that the above copyright
21 * notice appear in all copies and that both that copyright notice and
22 * this permission notice appear in supporting documentation, and that
23 * the name of M.I.T. not be used in advertising or publicity pertaining
24 * to distribution of the software without specific, written prior
25 * permission. Furthermore if you modify this software you must label
26 * your software as modified software and not distribute it in such a
27 * fashion that it might be confused with the original M.I.T. software.
28 * M.I.T. makes no representations about the suitability of
29 * this software for any purpose. It is provided "as is" without express
30 * or implied warranty.
33 * KDC Routines to deal with TGS_REQ's
40 #ifdef HAVE_NETINET_IN_H
41 #include <sys/types.h>
42 #include <netinet/in.h>
44 #include <arpa/inet.h>
51 #include "adm_proto.h"
53 extern krb5_error_code
setup_server_realm(krb5_principal
);
55 static void find_alternate_tgs (krb5_kdc_req
*, krb5_db_entry
*,
56 krb5_boolean
*, int *,
57 const krb5_fulladdr
*from
, char *cname
);
59 static krb5_error_code
prepare_error_tgs (krb5_kdc_req
*, krb5_ticket
*,
60 int, const char *, krb5_data
**,
65 process_tgs_req(krb5_data
*pkt
, const krb5_fulladdr
*from
,
68 krb5_keyblock
* subkey
;
69 krb5_kdc_req
*request
= 0;
72 krb5_enc_kdc_rep_part reply_encpart
;
73 krb5_ticket ticket_reply
, *header_ticket
= 0;
75 krb5_enc_tkt_part enc_tkt_reply
;
76 krb5_transited enc_tkt_transited
;
78 krb5_error_code retval
= 0;
81 krb5_timestamp kdc_time
, authtime
=0;
82 krb5_keyblock session_key
;
83 krb5_timestamp until
, rtime
;
84 krb5_keyblock encrypting_key
;
85 krb5_key_data
*server_key
;
86 char *cname
= 0, *sname
= 0, *tmp
= 0;
87 const char *fromstring
= 0;
88 krb5_last_req_entry
*nolrarray
[2], nolrentry
;
89 /* krb5_address *noaddrarray[1]; */
90 krb5_enctype useenctype
;
91 int errcode
, errcode2
;
94 const char *status
= 0;
96 char rep_etypestr
[128];
97 char fromstringbuf
[70];
98 long long tmp_server_times
, tmp_realm_times
;
100 (void) memset(&encrypting_key
, 0, sizeof(krb5_keyblock
));
101 (void) memset(&session_key
, 0, sizeof(krb5_keyblock
));
103 retval
= decode_krb5_tgs_req(pkt
, &request
);
107 ktypes2str(ktypestr
, sizeof(ktypestr
),
108 request
->nktypes
, request
->ktype
);
110 * setup_server_realm() sets up the global realm-specific data pointer.
112 if ((retval
= setup_server_realm(request
->server
)))
115 fromstring
= inet_ntop(ADDRTYPE2FAMILY(from
->address
->addrtype
),
116 from
->address
->contents
,
117 fromstringbuf
, sizeof(fromstringbuf
));
119 fromstring
= "<unknown>";
121 if ((errcode
= krb5_unparse_name(kdc_context
, request
->server
, &sname
))) {
122 status
= "UNPARSING SERVER";
127 /* errcode = kdc_process_tgs_req(request, from, pkt, &req_authdat); */
128 errcode
= kdc_process_tgs_req(request
, from
, pkt
, &header_ticket
, &subkey
);
130 if (header_ticket
&& header_ticket
->enc_part2
&&
131 (errcode2
= krb5_unparse_name(kdc_context
,
132 header_ticket
->enc_part2
->client
,
134 status
= "UNPARSING CLIENT";
141 status
= "PROCESS_TGS";
145 if (!header_ticket
) {
146 errcode
= KRB5_NO_TKT_SUPPLIED
; /* XXX? */
147 status
="UNEXPECTED NULL in header_ticket";
152 * We've already dealt with the AP_REQ authentication, so we can
153 * use header_ticket freely. The encrypted part (if any) has been
154 * decrypted with the session key.
157 authtime
= header_ticket
->enc_part2
->times
.authtime
;
159 /* XXX make sure server here has the proper realm...taken from AP_REQ
163 if ((errcode
= krb5_db_get_principal(kdc_context
, request
->server
, &server
,
165 status
= "LOOKING_UP_SERVER";
171 status
= "NON_UNIQUE_PRINCIPAL";
172 errcode
= KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE
;
174 } else if (nprincs
!= 1) {
176 * might be a request for a TGT for some other realm; we
177 * should do our best to find such a TGS in this db
179 if (firstpass
&& krb5_is_tgs_principal(request
->server
) == TRUE
) {
180 if (krb5_princ_size(kdc_context
, request
->server
) == 2) {
181 krb5_data
*server_1
=
182 krb5_princ_component(kdc_context
, request
->server
, 1);
184 krb5_princ_component(kdc_context
, tgs_server
, 1);
186 if (!tgs_1
|| server_1
->length
!= tgs_1
->length
||
187 memcmp(server_1
->data
, tgs_1
->data
, tgs_1
->length
)) {
188 krb5_db_free_principal(kdc_context
, &server
, nprincs
);
189 find_alternate_tgs(request
, &server
, &more
, &nprincs
,
196 krb5_db_free_principal(kdc_context
, &server
, nprincs
);
197 status
= "UNKNOWN_SERVER";
198 errcode
= KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN
;
202 if ((errcode
= krb5_timeofday(kdc_context
, &kdc_time
))) {
203 status
= "TIME_OF_DAY";
207 if ((retval
= validate_tgs_request(request
, server
, header_ticket
,
208 kdc_time
, &status
))) {
210 status
= "UNKNOWN_REASON";
211 errcode
= retval
+ ERROR_TABLE_BASE_krb5
;
216 * We pick the session keytype here....
218 * Some special care needs to be taken in the user-to-user
219 * case, since we don't know what keytypes the application server
220 * which is doing user-to-user authentication can support. We
221 * know that it at least must be able to support the encryption
222 * type of the session key in the TGT, since otherwise it won't be
223 * able to decrypt the U2U ticket! So we use that in preference
227 if (isflagset(request
->kdc_options
, KDC_OPT_ENC_TKT_IN_SKEY
)) {
228 krb5_keyblock
* st_sealing_key
;
229 krb5_kvno st_srv_kvno
;
233 * Get the key for the second ticket, and decrypt it.
235 if ((errcode
= kdc_get_server_key(request
->second_ticket
[st_idx
],
238 status
= "2ND_TKT_SERVER";
241 errcode
= krb5_decrypt_tkt_part(kdc_context
, st_sealing_key
,
242 request
->second_ticket
[st_idx
]);
243 krb5_free_keyblock(kdc_context
, st_sealing_key
);
245 status
= "2ND_TKT_DECRYPT";
249 etype
= request
->second_ticket
[st_idx
]->enc_part2
->session
->enctype
;
250 if (!krb5_c_valid_enctype(etype
)) {
251 status
= "BAD_ETYPE_IN_2ND_TKT";
252 errcode
= KRB5KDC_ERR_ETYPE_NOSUPP
;
256 for (i
= 0; i
< request
->nktypes
; i
++) {
257 if (request
->ktype
[i
] == etype
) {
265 * Select the keytype for the ticket session key.
267 if ((useenctype
== 0) &&
268 (useenctype
= select_session_keytype(kdc_context
, &server
,
270 request
->ktype
)) == 0) {
271 /* unsupported ktype */
272 status
= "BAD_ENCRYPTION_TYPE";
273 errcode
= KRB5KDC_ERR_ETYPE_NOSUPP
;
277 errcode
= krb5_c_make_random_key(kdc_context
, useenctype
, &session_key
);
280 /* random key failed */
281 status
= "RANDOM_KEY_FAILED";
285 ticket_reply
.server
= request
->server
; /* XXX careful for realm... */
287 enc_tkt_reply
.flags
= 0;
288 enc_tkt_reply
.times
.starttime
= 0;
291 * Fix header_ticket's starttime; if it's zero, fill in the
294 if (!(header_ticket
->enc_part2
->times
.starttime
))
295 header_ticket
->enc_part2
->times
.starttime
=
296 header_ticket
->enc_part2
->times
.authtime
;
298 /* don't use new addresses unless forwarded, see below */
300 enc_tkt_reply
.caddrs
= header_ticket
->enc_part2
->caddrs
;
301 /* noaddrarray[0] = 0; */
302 reply_encpart
.caddrs
= 0; /* optional...don't put it in */
304 /* It should be noted that local policy may affect the */
305 /* processing of any of these flags. For example, some */
306 /* realms may refuse to issue renewable tickets */
308 if (isflagset(request
->kdc_options
, KDC_OPT_FORWARDABLE
))
309 setflag(enc_tkt_reply
.flags
, TKT_FLG_FORWARDABLE
);
311 if (isflagset(request
->kdc_options
, KDC_OPT_FORWARDED
)) {
312 setflag(enc_tkt_reply
.flags
, TKT_FLG_FORWARDED
);
314 /* include new addresses in ticket & reply */
316 enc_tkt_reply
.caddrs
= request
->addresses
;
317 reply_encpart
.caddrs
= request
->addresses
;
319 if (isflagset(header_ticket
->enc_part2
->flags
, TKT_FLG_FORWARDED
))
320 setflag(enc_tkt_reply
.flags
, TKT_FLG_FORWARDED
);
322 if (isflagset(request
->kdc_options
, KDC_OPT_PROXIABLE
))
323 setflag(enc_tkt_reply
.flags
, TKT_FLG_PROXIABLE
);
325 if (isflagset(request
->kdc_options
, KDC_OPT_PROXY
)) {
326 setflag(enc_tkt_reply
.flags
, TKT_FLG_PROXY
);
328 /* include new addresses in ticket & reply */
330 enc_tkt_reply
.caddrs
= request
->addresses
;
331 reply_encpart
.caddrs
= request
->addresses
;
334 if (isflagset(request
->kdc_options
, KDC_OPT_ALLOW_POSTDATE
))
335 setflag(enc_tkt_reply
.flags
, TKT_FLG_MAY_POSTDATE
);
337 if (isflagset(request
->kdc_options
, KDC_OPT_POSTDATED
)) {
338 setflag(enc_tkt_reply
.flags
, TKT_FLG_POSTDATED
);
339 setflag(enc_tkt_reply
.flags
, TKT_FLG_INVALID
);
340 enc_tkt_reply
.times
.starttime
= request
->from
;
342 enc_tkt_reply
.times
.starttime
= kdc_time
;
344 if (isflagset(request
->kdc_options
, KDC_OPT_VALIDATE
)) {
345 /* BEWARE of allocation hanging off of ticket & enc_part2, it belongs
347 ticket_reply
= *(header_ticket
);
348 enc_tkt_reply
= *(header_ticket
->enc_part2
);
349 clear(enc_tkt_reply
.flags
, TKT_FLG_INVALID
);
352 if (isflagset(request
->kdc_options
, KDC_OPT_RENEW
)) {
353 krb5_deltat old_life
;
355 /* BEWARE of allocation hanging off of ticket & enc_part2, it belongs
357 ticket_reply
= *(header_ticket
);
358 enc_tkt_reply
= *(header_ticket
->enc_part2
);
360 old_life
= enc_tkt_reply
.times
.endtime
- enc_tkt_reply
.times
.starttime
;
362 enc_tkt_reply
.times
.starttime
= kdc_time
;
363 enc_tkt_reply
.times
.endtime
=
364 min(header_ticket
->enc_part2
->times
.renew_till
,
365 kdc_time
+ old_life
);
367 /* not a renew request */
368 enc_tkt_reply
.times
.starttime
= kdc_time
;
369 until
= (request
->till
== 0) ? kdc_infinity
: request
->till
;
372 tmp_server_times
= (long long) enc_tkt_reply
.times
.starttime
375 tmp_realm_times
= (long long) enc_tkt_reply
.times
.starttime
376 + max_life_for_realm
;
378 enc_tkt_reply
.times
.endtime
=
380 min(tmp_server_times
,
382 min(header_ticket
->enc_part2
->times
.endtime
,
383 KRB5_KDB_EXPIRATION
)))); /* SUNW */
385 enc_tkt_reply.times.endtime =
386 min(until, min(enc_tkt_reply.times.starttime + server.max_life,
387 min(enc_tkt_reply.times.starttime + max_life_for_realm,
388 min(header_ticket->enc_part2->times.endtime)));
390 if (isflagset(request
->kdc_options
, KDC_OPT_RENEWABLE_OK
) &&
391 (enc_tkt_reply
.times
.endtime
< request
->till
) &&
392 isflagset(header_ticket
->enc_part2
->flags
,
393 TKT_FLG_RENEWABLE
)) {
394 setflag(request
->kdc_options
, KDC_OPT_RENEWABLE
);
397 min(KRB5_KDB_EXPIRATION
,
398 header_ticket
->enc_part2
->times
.renew_till
));
401 rtime
= (request
->rtime
== 0) ? kdc_infinity
: request
->rtime
;
403 if (isflagset(request
->kdc_options
, KDC_OPT_RENEWABLE
)) {
404 /* already checked above in policy check to reject request for a
405 renewable ticket using a non-renewable ticket */
406 setflag(enc_tkt_reply
.flags
, TKT_FLG_RENEWABLE
);
407 tmp_realm_times
= (long long) enc_tkt_reply
.times
.starttime
+
408 min(server
.max_renewable_life
,max_renewable_life_for_realm
);
409 enc_tkt_reply
.times
.renew_till
=
411 min(header_ticket
->enc_part2
->times
.renew_till
,
412 min (tmp_realm_times
, KRB5_KDB_EXPIRATION
)));
414 enc_tkt_reply
.times
.renew_till
= 0;
418 * Set authtime to be the same as header_ticket's
420 enc_tkt_reply
.times
.authtime
= header_ticket
->enc_part2
->times
.authtime
;
423 * Propagate the preauthentication flags through to the returned ticket.
425 if (isflagset(header_ticket
->enc_part2
->flags
, TKT_FLG_PRE_AUTH
))
426 setflag(enc_tkt_reply
.flags
, TKT_FLG_PRE_AUTH
);
428 if (isflagset(header_ticket
->enc_part2
->flags
, TKT_FLG_HW_AUTH
))
429 setflag(enc_tkt_reply
.flags
, TKT_FLG_HW_AUTH
);
431 /* starttime is optional, and treated as authtime if not present.
432 so we can nuke it if it matches */
433 if (enc_tkt_reply
.times
.starttime
== enc_tkt_reply
.times
.authtime
)
434 enc_tkt_reply
.times
.starttime
= 0;
436 /* assemble any authorization data */
437 if (request
->authorization_data
.ciphertext
.data
) {
440 scratch
.length
= request
->authorization_data
.ciphertext
.length
;
442 malloc(request
->authorization_data
.ciphertext
.length
))) {
443 status
= "AUTH_NOMEM";
448 if ((errcode
= krb5_c_decrypt(kdc_context
,
449 header_ticket
->enc_part2
->session
,
450 KRB5_KEYUSAGE_TGS_REQ_AD_SESSKEY
,
451 0, &request
->authorization_data
,
453 status
= "AUTH_ENCRYPT_FAIL";
458 /* scratch now has the authorization data, so we decode it */
459 errcode
= decode_krb5_authdata(&scratch
, &(request
->unenc_authdata
));
462 status
= "AUTH_DECODE";
467 concat_authorization_data(request
->unenc_authdata
,
468 header_ticket
->enc_part2
->authorization_data
,
469 &enc_tkt_reply
.authorization_data
))) {
470 status
= "CONCAT_AUTH";
474 enc_tkt_reply
.authorization_data
=
475 header_ticket
->enc_part2
->authorization_data
;
477 enc_tkt_reply
.session
= &session_key
;
478 enc_tkt_reply
.client
= header_ticket
->enc_part2
->client
;
479 enc_tkt_reply
.transited
.tr_type
= KRB5_DOMAIN_X500_COMPRESS
;
480 enc_tkt_reply
.transited
.tr_contents
= empty_string
; /* equivalent of "" */
483 * Only add the realm of the presented tgt to the transited list if
484 * it is different than the local realm (cross-realm) and it is different
485 * than the realm of the client (since the realm of the client is already
486 * implicitly part of the transited list and should not be explicitly
490 /* realm compare is like strcmp, but knows how to deal with these args */
491 if (realm_compare(header_ticket
->server
, tgs_server
) ||
492 realm_compare(header_ticket
->server
, enc_tkt_reply
.client
)) {
493 /* tgt issued by local realm or issued by realm of client */
494 enc_tkt_reply
.transited
= header_ticket
->enc_part2
->transited
;
496 /* tgt issued by some other realm and not the realm of the client */
497 /* assemble new transited field into allocated storage */
498 if (header_ticket
->enc_part2
->transited
.tr_type
!=
499 KRB5_DOMAIN_X500_COMPRESS
) {
500 status
= "BAD_TRTYPE";
501 errcode
= KRB5KDC_ERR_TRTYPE_NOSUPP
;
504 enc_tkt_transited
.tr_type
= KRB5_DOMAIN_X500_COMPRESS
;
505 enc_tkt_transited
.magic
= 0;
506 enc_tkt_transited
.tr_contents
.magic
= 0;
507 enc_tkt_transited
.tr_contents
.data
= 0;
508 enc_tkt_transited
.tr_contents
.length
= 0;
509 enc_tkt_reply
.transited
= enc_tkt_transited
;
511 add_to_transited(&header_ticket
->enc_part2
->transited
.tr_contents
,
512 &enc_tkt_reply
.transited
.tr_contents
,
513 header_ticket
->server
,
514 enc_tkt_reply
.client
,
516 status
= "ADD_TR_FAIL";
521 if (!isflagset (request
->kdc_options
, KDC_OPT_DISABLE_TRANSITED_CHECK
)) {
525 errcode
= krb5_check_transited_list (kdc_context
,
526 &enc_tkt_reply
.transited
.tr_contents
,
527 krb5_princ_realm (kdc_context
, header_ticket
->enc_part2
->client
),
528 krb5_princ_realm (kdc_context
, request
->server
));
529 tlen
= enc_tkt_reply
.transited
.tr_contents
.length
;
530 tdots
= tlen
> 125 ? "..." : "";
531 tlen
= tlen
> 125 ? 125 : tlen
;
534 setflag (enc_tkt_reply
.flags
, TKT_FLG_TRANSIT_POLICY_CHECKED
);
535 } else if (errcode
== KRB5KRB_AP_ERR_ILL_CR_TKT
)
536 krb5_klog_syslog (LOG_INFO
,
537 "bad realm transit path from '%s' to '%s' "
539 cname
? cname
: "<unknown client>",
540 sname
? sname
: "<unknown server>",
542 enc_tkt_reply
.transited
.tr_contents
.data
,
545 const char *emsg
= krb5_get_error_message(kdc_context
, errcode
);
546 krb5_klog_syslog (LOG_ERR
,
547 "unexpected error checking transit from "
548 "'%s' to '%s' via '%.*s%s': %s",
549 cname
? cname
: "<unknown client>",
550 sname
? sname
: "<unknown server>",
552 enc_tkt_reply
.transited
.tr_contents
.data
,
554 krb5_free_error_message(kdc_context
, emsg
);
557 krb5_klog_syslog (LOG_INFO
, "not checking transit path");
558 if (reject_bad_transit
559 && !isflagset (enc_tkt_reply
.flags
, TKT_FLG_TRANSIT_POLICY_CHECKED
)) {
560 errcode
= KRB5KDC_ERR_POLICY
;
561 status
= "BAD_TRANSIT";
565 ticket_reply
.enc_part2
= &enc_tkt_reply
;
568 * If we are doing user-to-user authentication, then make sure
569 * that the client for the second ticket matches the request
570 * server, and then encrypt the ticket using the session key of
573 if (isflagset(request
->kdc_options
, KDC_OPT_ENC_TKT_IN_SKEY
)) {
575 * Make sure the client for the second ticket matches
578 krb5_enc_tkt_part
*t2enc
= request
->second_ticket
[st_idx
]->enc_part2
;
579 krb5_principal client2
= t2enc
->client
;
580 if (!krb5_principal_compare(kdc_context
, request
->server
, client2
)) {
581 if ((errcode
= krb5_unparse_name(kdc_context
, client2
, &tmp
)))
585 audit_krb5kdc_tgs_req_2ndtktmm(
586 (struct in_addr
*)from
->address
->contents
,
587 (in_port_t
)from
->port
,
589 krb5_klog_syslog(LOG_INFO
,
590 "TGS_REQ %s: 2ND_TKT_MISMATCH: "
591 "authtime %d, %s for %s, 2nd tkt client %s",
592 fromstring
, authtime
,
593 cname
? cname
: "<unknown client>",
594 sname
? sname
: "<unknown server>",
595 tmp
? tmp
: "<unknown>");
596 errcode
= KRB5KDC_ERR_SERVER_NOMATCH
;
600 ticket_reply
.enc_part
.kvno
= 0;
601 ticket_reply
.enc_part
.enctype
= t2enc
->session
->enctype
;
602 if ((errcode
= krb5_encrypt_tkt_part(kdc_context
, t2enc
->session
,
604 status
= "2ND_TKT_ENCRYPT";
610 * Find the server key
612 if ((errcode
= krb5_dbe_find_enctype(kdc_context
, &server
,
613 -1, /* ignore keytype */
614 -1, /* Ignore salttype */
615 0, /* Get highest kvno */
617 status
= "FINDING_SERVER_KEY";
620 /* convert server.key into a real key (it may be encrypted
621 * in the database) */
622 if ((errcode
= krb5_dbekd_decrypt_key_data(kdc_context
,
624 server_key
, &encrypting_key
,
626 status
= "DECRYPT_SERVER_KEY";
629 errcode
= krb5_encrypt_tkt_part(kdc_context
, &encrypting_key
,
631 krb5_free_keyblock_contents(kdc_context
, &encrypting_key
);
633 status
= "TKT_ENCRYPT";
636 ticket_reply
.enc_part
.kvno
= server_key
->key_data_kvno
;
639 /* Start assembling the response */
640 reply
.msg_type
= KRB5_TGS_REP
;
641 reply
.padata
= 0; /* always */
642 reply
.client
= header_ticket
->enc_part2
->client
;
643 reply
.enc_part
.kvno
= 0; /* We are using the session key */
644 reply
.ticket
= &ticket_reply
;
646 reply_encpart
.session
= &session_key
;
647 reply_encpart
.nonce
= request
->nonce
;
649 /* copy the time fields EXCEPT for authtime; its location
651 reply_encpart
.times
= enc_tkt_reply
.times
;
652 reply_encpart
.times
.authtime
= header_ticket
->enc_part2
->times
.authtime
;
654 /* starttime is optional, and treated as authtime if not present.
655 so we can nuke it if it matches */
656 if (enc_tkt_reply
.times
.starttime
== enc_tkt_reply
.times
.authtime
)
657 enc_tkt_reply
.times
.starttime
= 0;
659 nolrentry
.lr_type
= KRB5_LRQ_NONE
;
661 nolrarray
[0] = &nolrentry
;
663 reply_encpart
.last_req
= nolrarray
; /* not available for TGS reqs */
664 reply_encpart
.key_exp
= 0; /* ditto */
665 reply_encpart
.flags
= enc_tkt_reply
.flags
;
666 reply_encpart
.server
= ticket_reply
.server
;
668 /* use the session key in the ticket, unless there's a subsession key
671 reply
.enc_part
.enctype
= subkey
? subkey
->enctype
:
672 header_ticket
->enc_part2
->session
->enctype
;
673 errcode
= krb5_encode_kdc_rep(kdc_context
, KRB5_TGS_REP
, &reply_encpart
,
676 header_ticket
->enc_part2
->session
,
679 status
= "ENCODE_KDC_REP";
684 if (ticket_reply
.enc_part
.ciphertext
.data
) {
685 memset(ticket_reply
.enc_part
.ciphertext
.data
, 0,
686 ticket_reply
.enc_part
.ciphertext
.length
);
687 free(ticket_reply
.enc_part
.ciphertext
.data
);
688 ticket_reply
.enc_part
.ciphertext
.data
= NULL
;
690 /* these parts are left on as a courtesy from krb5_encode_kdc_rep so we
691 can use them in raw form if needed. But, we don't... */
692 if (reply
.enc_part
.ciphertext
.data
) {
693 memset(reply
.enc_part
.ciphertext
.data
, 0,
694 reply
.enc_part
.ciphertext
.length
);
695 free(reply
.enc_part
.ciphertext
.data
);
696 reply
.enc_part
.ciphertext
.data
= NULL
;
701 const char * emsg
= NULL
;
702 audit_krb5kdc_tgs_req((struct in_addr
*)from
->address
->contents
,
703 (in_port_t
)from
->port
, 0,
704 cname
? cname
: "<unknown client>",
705 sname
? sname
: "<unknown client>",
708 rep_etypes2str(rep_etypestr
, sizeof(rep_etypestr
), &reply
);
710 emsg
= krb5_get_error_message (kdc_context
, errcode
);
711 krb5_klog_syslog(LOG_INFO
,
712 "TGS_REQ (%s) %s: %s: authtime %d, "
713 "%s%s %s for %s%s%s",
715 fromstring
, status
, authtime
,
716 !errcode
? rep_etypestr
: "",
718 cname
? cname
: "<unknown client>",
719 sname
? sname
: "<unknown server>",
721 errcode
? emsg
: "");
723 krb5_free_error_message (kdc_context
, emsg
);
729 status
= krb5_get_error_message (kdc_context
, errcode
);
732 errcode
-= ERROR_TABLE_BASE_krb5
;
733 if (errcode
< 0 || errcode
> 128)
734 errcode
= KRB_ERR_GENERIC
;
736 retval
= prepare_error_tgs(request
, header_ticket
, errcode
,
737 fromstring
, response
, status
);
739 krb5_free_error_message (kdc_context
, status
);
745 krb5_free_ticket(kdc_context
, header_ticket
);
747 krb5_free_kdc_req(kdc_context
, request
);
753 krb5_db_free_principal(kdc_context
, &server
, 1);
754 if (session_key
.contents
)
755 krb5_free_keyblock_contents(kdc_context
, &session_key
);
757 free(enc_tkt_reply
.transited
.tr_contents
.data
);
762 static krb5_error_code
763 prepare_error_tgs (krb5_kdc_req
*request
, krb5_ticket
*ticket
, int error
,
764 const char *ident
, krb5_data
**response
, const char *status
)
767 krb5_error_code retval
;
770 errpkt
.ctime
= request
->nonce
;
773 if ((retval
= krb5_us_timeofday(kdc_context
, &errpkt
.stime
,
776 errpkt
.error
= error
;
777 errpkt
.server
= request
->server
;
778 if (ticket
&& ticket
->enc_part2
)
779 errpkt
.client
= ticket
->enc_part2
->client
;
782 errpkt
.text
.length
= strlen(status
) + 1;
783 if (!(errpkt
.text
.data
= malloc(errpkt
.text
.length
)))
785 (void) strcpy(errpkt
.text
.data
, status
);
787 if (!(scratch
= (krb5_data
*)malloc(sizeof(*scratch
)))) {
788 free(errpkt
.text
.data
);
791 errpkt
.e_data
.length
= 0;
792 errpkt
.e_data
.data
= 0;
794 retval
= krb5_mk_error(kdc_context
, &errpkt
, scratch
);
795 free(errpkt
.text
.data
);
805 * The request seems to be for a ticket-granting service somewhere else,
806 * but we don't have a ticket for the final TGS. Try to give the requestor
807 * some intermediate realm.
810 find_alternate_tgs(krb5_kdc_req
*request
, krb5_db_entry
*server
,
811 krb5_boolean
*more
, int *nprincs
,
812 const krb5_fulladdr
*from
, char *cname
)
814 krb5_error_code retval
;
815 krb5_principal
*plist
, *pl2
;
822 * Call to krb5_princ_component is normally not safe but is so
823 * here only because find_alternate_tgs() is only called from
824 * somewhere that has already checked the number of components in
827 if ((retval
= krb5_walk_realm_tree(kdc_context
,
828 krb5_princ_realm(kdc_context
, request
->server
),
829 krb5_princ_component(kdc_context
, request
->server
, 1),
830 &plist
, KRB5_REALM_BRANCH_CHAR
)))
833 /* move to the end */
834 for (pl2
= plist
; *pl2
; pl2
++);
836 /* the first entry in this array is for krbtgt/local@local, so we
838 while (--pl2
> plist
) {
840 tmp
= *krb5_princ_realm(kdc_context
, *pl2
);
841 krb5_princ_set_realm(kdc_context
, *pl2
,
842 krb5_princ_realm(kdc_context
, tgs_server
));
843 retval
= krb5_db_get_principal(kdc_context
, *pl2
, server
, nprincs
, more
);
844 krb5_princ_set_realm(kdc_context
, *pl2
, &tmp
);
848 krb5_free_realm_tree(kdc_context
, plist
);
852 krb5_db_free_principal(kdc_context
, server
, *nprincs
);
854 } else if (*nprincs
== 1) {
856 krb5_principal tmpprinc
;
859 tmp
= *krb5_princ_realm(kdc_context
, *pl2
);
860 krb5_princ_set_realm(kdc_context
, *pl2
,
861 krb5_princ_realm(kdc_context
, tgs_server
));
862 if ((retval
= krb5_copy_principal(kdc_context
, *pl2
, &tmpprinc
))) {
863 krb5_db_free_principal(kdc_context
, server
, *nprincs
);
864 krb5_princ_set_realm(kdc_context
, *pl2
, &tmp
);
867 krb5_princ_set_realm(kdc_context
, *pl2
, &tmp
);
869 krb5_free_principal(kdc_context
, request
->server
);
870 request
->server
= tmpprinc
;
871 if (krb5_unparse_name(kdc_context
, request
->server
, &sname
)) {
873 audit_krb5kdc_tgs_req_alt_tgt(
874 (struct in_addr
*)from
->address
->contents
,
875 (in_port_t
)from
->port
,
876 0, cname
, "<unparseable>", 0);
877 krb5_klog_syslog(LOG_INFO
,
878 "TGS_REQ: issuing alternate <un-unparseable> TGT");
881 audit_krb5kdc_tgs_req_alt_tgt(
882 (struct in_addr
*)from
->address
->contents
,
883 (in_port_t
)from
->port
,
885 krb5_klog_syslog(LOG_INFO
,
886 "TGS_REQ: issuing TGT %s", sname
);
891 krb5_db_free_principal(kdc_context
, server
, *nprincs
);
897 krb5_free_realm_tree(kdc_context
, plist
);