1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* kdc/do_tgs_req.c - KDC Routines to deal with TGS_REQ's */
3 /*
4 * Copyright 1990, 1991, 2001, 2007, 2008, 2009, 2013, 2014 by the
5 * Massachusetts Institute of Technology. All Rights Reserved.
6 *
7 * Export of this software from the United States of America may
8 * require a specific license from the United States Government.
9 * It is the responsibility of any person or organization contemplating
10 * export to obtain such a license before exporting.
11 *
12 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13 * distribute this software and its documentation for any purpose and
14 * without fee is hereby granted, provided that the above copyright
15 * notice appear in all copies and that both that copyright notice and
16 * this permission notice appear in supporting documentation, and that
17 * the name of M.I.T. not be used in advertising or publicity pertaining
18 * to distribution of the software without specific, written prior
19 * permission. Furthermore if you modify this software you must label
20 * your software as modified software and not distribute it in such a
21 * fashion that it might be confused with the original M.I.T. software.
22 * M.I.T. makes no representations about the suitability of
23 * this software for any purpose. It is provided "as is" without express
24 * or implied warranty.
25 */
26 /*
27 * Copyright (c) 2006-2008, Novell, Inc.
28 * All rights reserved.
29 *
30 * Redistribution and use in source and binary forms, with or without
31 * modification, are permitted provided that the following conditions are met:
32 *
33 * * Redistributions of source code must retain the above copyright notice,
34 * this list of conditions and the following disclaimer.
35 * * Redistributions in binary form must reproduce the above copyright
36 * notice, this list of conditions and the following disclaimer in the
37 * documentation and/or other materials provided with the distribution.
38 * * The copyright holder's name is not used to endorse or promote products
39 * derived from this software without specific prior written permission.
40 *
41 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
42 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
43 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
44 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
45 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
46 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
47 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
48 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
49 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
50 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
51 * POSSIBILITY OF SUCH DAMAGE.
52 */
53
54 #include "k5-int.h"
55
56 #include <syslog.h>
57 #ifdef HAVE_NETINET_IN_H
58 #include <sys/types.h>
59 #include <netinet/in.h>
60 #ifndef hpux
61 #include <arpa/inet.h>
62 #endif
63 #endif
64
65 #include "kdc_util.h"
66 #include "kdc_audit.h"
67 #include "policy.h"
68 #include "extern.h"
69 #include "adm_proto.h"
70 #include <ctype.h>
71
72 struct tgs_req_info {
73 /* The decoded request. Ownership is transferred to this structure. This
74 * will be replaced with the inner FAST body if present. */
75 krb5_kdc_req *req;
76
77 /*
78 * The decrypted authentication header ticket from the request's
79 * PA-TGS-REQ, the KDB entry for its server, its encryption key, the
80 * PA-TGS-REQ subkey if present, and the decoded and verified header ticket
81 * PAC if present.
82 */
83 krb5_ticket *header_tkt;
84 krb5_db_entry *header_server;
85 krb5_keyblock *header_key;
86 krb5_keyblock *subkey;
87 krb5_pac header_pac;
88
89 /*
90 * If a second ticket is present and this is a U2U or S4U2Proxy request,
91 * the decoded and verified PAC if present, the KDB entry for the second
92 * ticket server server, and the key used to decrypt the second ticket.
93 */
94 krb5_pac stkt_pac;
95 krb5_db_entry *stkt_server;
96 krb5_keyblock *stkt_server_key;
97 /* For cross-realm S4U2Proxy requests, the client principal retrieved from
98 * stkt_pac. */
99 krb5_principal stkt_pac_client;
100
101 /* Storage for the local TGT KDB entry for the service realm if that isn't
102 * the header server. */
103 krb5_db_entry *local_tgt_storage;
104 /* The decrypted first key of the local TGT entry. */
105 krb5_keyblock local_tgt_key;
106
107 /* The server KDB entry. Normally the requested server, but for referral
108 * and alternate TGS replies this will be a cross-realm TGT entry. */
109 krb5_db_entry *server;
110
111 /*
112 * The subject client KDB entry for an S4U2Self request, or the header
113 * ticket client KDB entry for other requests. NULL if
114 * NO_AUTH_DATA_REQUIRED is set on the server KDB entry and this isn't an
115 * S4U2Self request, or if the client is in another realm and the KDB
116 * cannot map its principal name.
117 */
118 krb5_db_entry *client;
119
120 /* The decoded S4U2Self padata from the request, if present. */
121 krb5_pa_s4u_x509_user *s4u2self;
122
123 /* Authentication indicators retrieved from the header ticket, for
124 * non-S4U2Self requests. */
125 krb5_data **auth_indicators;
126
127 /* Storage for a transited list with the header TGT realm added, if that
128 * realm is different from the client and server realm. */
129 krb5_data new_transited;
130
131 /* The KDB flags applicable to this request (a subset of {CROSS_REALM,
132 * ISSUING_REFERRAL, PROTOCOL_TRANSITION, CONSTRAINED_DELEGATION}). */
133 unsigned int flags;
134
135 /* Booleans for two of the above flags, for convenience. */
136 krb5_boolean is_referral;
137 krb5_boolean is_crossrealm;
138
139 /* The authtime of subject_tkt. On early failures this may be 0. */
140 krb5_timestamp authtime;
141
142 /* The following fields are (or contain) alias pointers and should not be
143 * freed. */
144
145 /* The transited list implied by the request, aliasing new_transited or the
146 * header ticket transited field. */
147 krb5_transited transited;
148
149 /* Alias to the decrypted second ticket within req, if one applies to this
150 * request. */
151 const krb5_ticket *stkt;
152
153 /* Alias to stkt for S4U2Proxy requests, header_tkt otherwise. */
154 krb5_enc_tkt_part *subject_tkt;
155
156 /* Alias to local_tgt_storage or header_server. */
157 krb5_db_entry *local_tgt;
158
159 /* For either kind of S4U request, an alias to the requested client
160 * principal name. */
161 krb5_principal s4u_cprinc;
162
163 /* An alias to the client principal name we should issue the ticket for
164 * (either header_tkt->enc_part2->client or s4u_cprinc). */
165 krb5_principal tkt_client;
166
167 /* The client principal of the PA-TGS-REQ header ticket. On early failures
168 * this may be NULL. */
169 krb5_principal cprinc;
170
171 /* The canonicalized request server principal or referral/alternate TGT.
172 * On early failures this may be the requested server instead. */
173 krb5_principal sprinc;
174
175 };
176
177 static krb5_error_code
178 db_get_svc_princ(krb5_context, krb5_principal, krb5_flags,
179 krb5_db_entry **, const char **);
180
181 static krb5_error_code
prepare_error_tgs(struct kdc_request_state * state,krb5_kdc_req * request,krb5_ticket * ticket,krb5_error_code code,krb5_principal canon_server,krb5_data ** response,const char * status,krb5_pa_data ** e_data)182 prepare_error_tgs(struct kdc_request_state *state, krb5_kdc_req *request,
183 krb5_ticket *ticket, krb5_error_code code,
184 krb5_principal canon_server, krb5_data **response,
185 const char *status, krb5_pa_data **e_data)
186 {
187 krb5_context context = state->realm_data->realm_context;
188 krb5_error errpkt;
189 krb5_error_code retval = 0;
190 krb5_data *scratch, *e_data_asn1 = NULL, *fast_edata = NULL;
191
192 errpkt.magic = KV5M_ERROR;
193 errpkt.ctime = 0;
194 errpkt.cusec = 0;
195
196 retval = krb5_us_timeofday(context, &errpkt.stime, &errpkt.susec);
197 if (retval)
198 return(retval);
199 errpkt.error = errcode_to_protocol(code);
200 errpkt.server = request->server;
201 if (ticket && ticket->enc_part2)
202 errpkt.client = ticket->enc_part2->client;
203 else
204 errpkt.client = NULL;
205 errpkt.text.length = strlen(status);
206 if (!(errpkt.text.data = strdup(status)))
207 return ENOMEM;
208
209 if (!(scratch = (krb5_data *)malloc(sizeof(*scratch)))) {
210 free(errpkt.text.data);
211 return ENOMEM;
212 }
213
214 if (e_data != NULL) {
215 retval = encode_krb5_padata_sequence(e_data, &e_data_asn1);
216 if (retval) {
217 free(scratch);
218 free(errpkt.text.data);
219 return retval;
220 }
221 errpkt.e_data = *e_data_asn1;
222 } else
223 errpkt.e_data = empty_data();
224
225 retval = kdc_fast_handle_error(context, state, request, e_data,
226 &errpkt, &fast_edata);
227 if (retval) {
228 free(scratch);
229 free(errpkt.text.data);
230 krb5_free_data(context, e_data_asn1);
231 return retval;
232 }
233 if (fast_edata)
234 errpkt.e_data = *fast_edata;
235 if (kdc_fast_hide_client(state) && errpkt.client != NULL)
236 errpkt.client = (krb5_principal)krb5_anonymous_principal();
237 retval = krb5_mk_error(context, &errpkt, scratch);
238 free(errpkt.text.data);
239 krb5_free_data(context, e_data_asn1);
240 krb5_free_data(context, fast_edata);
241 if (retval)
242 free(scratch);
243 else
244 *response = scratch;
245
246 return retval;
247 }
248
249 /* KDC options that require a second ticket */
250 #define STKT_OPTIONS (KDC_OPT_CNAME_IN_ADDL_TKT | KDC_OPT_ENC_TKT_IN_SKEY)
251 /*
252 * If req is a second-ticket request and a second ticket is present, decrypt
253 * it. Set *stkt_out to an alias to the ticket with populated enc_part2. Set
254 * *server_out to the server DB entry and *key_out to the ticket decryption
255 * key.
256 */
257 static krb5_error_code
decrypt_2ndtkt(krb5_context context,krb5_kdc_req * req,krb5_flags flags,krb5_db_entry * local_tgt,krb5_keyblock * local_tgt_key,const krb5_ticket ** stkt_out,krb5_pac * pac_out,krb5_db_entry ** server_out,krb5_keyblock ** key_out,const char ** status)258 decrypt_2ndtkt(krb5_context context, krb5_kdc_req *req, krb5_flags flags,
259 krb5_db_entry *local_tgt, krb5_keyblock *local_tgt_key,
260 const krb5_ticket **stkt_out, krb5_pac *pac_out,
261 krb5_db_entry **server_out, krb5_keyblock **key_out,
262 const char **status)
263 {
264 krb5_error_code retval;
265 krb5_db_entry *server = NULL;
266 krb5_keyblock *key = NULL;
267 krb5_kvno kvno;
268 krb5_ticket *stkt;
269
270 *stkt_out = NULL;
271 *pac_out = NULL;
272 *server_out = NULL;
273 *key_out = NULL;
274
275 if (!(req->kdc_options & STKT_OPTIONS) || req->second_ticket == NULL ||
276 req->second_ticket[0] == NULL)
277 return 0;
278
279 stkt = req->second_ticket[0];
280 retval = kdc_get_server_key(context, stkt, flags, TRUE, &server, &key,
281 &kvno);
282 if (retval != 0) {
283 *status = "2ND_TKT_SERVER";
284 goto cleanup;
285 }
286 retval = krb5_decrypt_tkt_part(context, key, stkt);
287 if (retval != 0) {
288 *status = "2ND_TKT_DECRYPT";
289 goto cleanup;
290 }
291 retval = get_verified_pac(context, stkt->enc_part2, server, key, local_tgt,
292 local_tgt_key, pac_out);
293 if (retval != 0) {
294 *status = "2ND_TKT_PAC";
295 goto cleanup;
296 }
297 *stkt_out = stkt;
298 *server_out = server;
299 *key_out = key;
300 server = NULL;
301 key = NULL;
302
303 cleanup:
304 krb5_db_free_principal(context, server);
305 krb5_free_keyblock(context, key);
306 return retval;
307 }
308
309 static krb5_error_code
get_2ndtkt_enctype(krb5_kdc_req * req,krb5_enctype * useenctype,const char ** status)310 get_2ndtkt_enctype(krb5_kdc_req *req, krb5_enctype *useenctype,
311 const char **status)
312 {
313 krb5_enctype etype;
314 krb5_ticket *stkt = req->second_ticket[0];
315 int i;
316
317 etype = stkt->enc_part2->session->enctype;
318 if (!krb5_c_valid_enctype(etype)) {
319 *status = "BAD_ETYPE_IN_2ND_TKT";
320 return KRB5KDC_ERR_ETYPE_NOSUPP;
321 }
322 for (i = 0; i < req->nktypes; i++) {
323 if (req->ktype[i] == etype) {
324 *useenctype = etype;
325 break;
326 }
327 }
328 return 0;
329 }
330
331 static krb5_error_code
gen_session_key(krb5_context context,krb5_kdc_req * req,krb5_db_entry * server,krb5_keyblock * skey,const char ** status)332 gen_session_key(krb5_context context, krb5_kdc_req *req, krb5_db_entry *server,
333 krb5_keyblock *skey, const char **status)
334 {
335 krb5_error_code retval;
336 krb5_enctype useenctype = 0;
337
338 /*
339 * Some special care needs to be taken in the user-to-user
340 * case, since we don't know what keytypes the application server
341 * which is doing user-to-user authentication can support. We
342 * know that it at least must be able to support the encryption
343 * type of the session key in the TGT, since otherwise it won't be
344 * able to decrypt the U2U ticket! So we use that in preference
345 * to anything else.
346 */
347 if (req->kdc_options & KDC_OPT_ENC_TKT_IN_SKEY) {
348 retval = get_2ndtkt_enctype(req, &useenctype, status);
349 if (retval != 0)
350 return retval;
351 }
352 if (useenctype == 0) {
353 useenctype = select_session_keytype(context, server,
354 req->nktypes, req->ktype);
355 }
356 if (useenctype == 0) {
357 /* unsupported ktype */
358 *status = "BAD_ENCRYPTION_TYPE";
359 return KRB5KDC_ERR_ETYPE_NOSUPP;
360 }
361
362 return krb5_c_make_random_key(context, useenctype, skey);
363 }
364
365 /*
366 * The request seems to be for a ticket-granting service somewhere else,
367 * but we don't have a ticket for the final TGS. Try to give the requestor
368 * some intermediate realm.
369 */
370 static krb5_error_code
find_alternate_tgs(krb5_context context,krb5_principal princ,krb5_db_entry ** server_ptr,const char ** status)371 find_alternate_tgs(krb5_context context, krb5_principal princ,
372 krb5_db_entry **server_ptr, const char **status)
373 {
374 krb5_error_code retval;
375 krb5_principal *plist = NULL, *pl2;
376 krb5_data tmp;
377 krb5_db_entry *server = NULL;
378
379 *server_ptr = NULL;
380 assert(is_cross_tgs_principal(princ));
381 retval = krb5_walk_realm_tree(context, &princ->realm, &princ->data[1],
382 &plist, KRB5_REALM_BRANCH_CHAR);
383 if (retval)
384 goto cleanup;
385 /* move to the end */
386 for (pl2 = plist; *pl2; pl2++);
387
388 /* the first entry in this array is for krbtgt/local@local, so we
389 ignore it */
390 while (--pl2 > plist) {
391 tmp = *krb5_princ_realm(context, *pl2);
392 krb5_princ_set_realm(context, *pl2, &princ->realm);
393 retval = db_get_svc_princ(context, *pl2, 0, &server, status);
394 krb5_princ_set_realm(context, *pl2, &tmp);
395 if (retval == KRB5_KDB_NOENTRY)
396 continue;
397 else if (retval)
398 goto cleanup;
399
400 log_tgs_alt_tgt(context, server->princ);
401 *server_ptr = server;
402 server = NULL;
403 goto cleanup;
404 }
405 cleanup:
406 if (retval == 0 && *server_ptr == NULL)
407 retval = KRB5_KDB_NOENTRY;
408 if (retval != 0)
409 *status = "UNKNOWN_SERVER";
410
411 krb5_free_realm_tree(context, plist);
412 krb5_db_free_principal(context, server);
413 return retval;
414 }
415
416 /* Return true if item is an element of the space/comma-separated list. */
417 static krb5_boolean
in_list(const char * list,const char * item)418 in_list(const char *list, const char *item)
419 {
420 const char *p;
421 int len = strlen(item);
422
423 if (list == NULL)
424 return FALSE;
425 for (p = strstr(list, item); p != NULL; p = strstr(p + 1, item)) {
426 if ((p == list || isspace((unsigned char)p[-1]) || p[-1] == ',') &&
427 (p[len] == '\0' || isspace((unsigned char)p[len]) ||
428 p[len] == ','))
429 return TRUE;
430 }
431 return FALSE;
432 }
433
434 /*
435 * Check whether the request satisfies the conditions for generating a referral
436 * TGT. The caller checks whether the hostname component looks like a FQDN.
437 */
438 static krb5_boolean
is_referral_req(kdc_realm_t * realm,krb5_kdc_req * request)439 is_referral_req(kdc_realm_t *realm, krb5_kdc_req *request)
440 {
441 krb5_boolean ret = FALSE;
442 char *stype = NULL;
443 char *hostbased = realm->realm_hostbased;
444 char *no_referral = realm->realm_no_referral;
445
446 if (!(request->kdc_options & KDC_OPT_CANONICALIZE))
447 return FALSE;
448
449 if (request->kdc_options & KDC_OPT_ENC_TKT_IN_SKEY)
450 return FALSE;
451
452 if (request->server->length != 2)
453 return FALSE;
454
455 stype = data2string(&request->server->data[0]);
456 if (stype == NULL)
457 return FALSE;
458 switch (request->server->type) {
459 case KRB5_NT_UNKNOWN:
460 /* Allow referrals for NT-UNKNOWN principals, if configured. */
461 if (!in_list(hostbased, stype) && !in_list(hostbased, "*"))
462 goto cleanup;
463 /* FALLTHROUGH */
464 case KRB5_NT_SRV_HST:
465 case KRB5_NT_SRV_INST:
466 /* Deny referrals for specific service types, if configured. */
467 if (in_list(no_referral, stype) || in_list(no_referral, "*"))
468 goto cleanup;
469 ret = TRUE;
470 break;
471 default:
472 goto cleanup;
473 }
474 cleanup:
475 free(stype);
476 return ret;
477 }
478
479 /*
480 * Find a remote realm TGS principal for an unknown host-based service
481 * principal.
482 */
483 static krb5_int32
find_referral_tgs(kdc_realm_t * realm,krb5_kdc_req * request,krb5_principal * krbtgt_princ)484 find_referral_tgs(kdc_realm_t *realm, krb5_kdc_req *request,
485 krb5_principal *krbtgt_princ)
486 {
487 krb5_context context = realm->realm_context;
488 krb5_error_code retval = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
489 char **realms = NULL, *hostname = NULL;
490 krb5_data srealm = request->server->realm;
491
492 if (!is_referral_req(realm, request))
493 goto cleanup;
494
495 hostname = data2string(&request->server->data[1]);
496 if (hostname == NULL) {
497 retval = ENOMEM;
498 goto cleanup;
499 }
500 /* If the hostname doesn't contain a '.', it's not a FQDN. */
501 if (strchr(hostname, '.') == NULL)
502 goto cleanup;
503 retval = krb5_get_host_realm(context, hostname, &realms);
504 if (retval) {
505 /* no match found */
506 kdc_err(context, retval, "unable to find realm of host");
507 goto cleanup;
508 }
509 /* Don't return a referral to the empty realm or the service realm. */
510 if (realms == NULL || realms[0] == NULL || *realms[0] == '\0' ||
511 data_eq_string(srealm, realms[0])) {
512 retval = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
513 goto cleanup;
514 }
515 retval = krb5_build_principal(context, krbtgt_princ,
516 srealm.length, srealm.data,
517 "krbtgt", realms[0], (char *)0);
518 cleanup:
519 krb5_free_host_realm(context, realms);
520 free(hostname);
521
522 return retval;
523 }
524
525 static krb5_error_code
db_get_svc_princ(krb5_context ctx,krb5_principal princ,krb5_flags flags,krb5_db_entry ** server,const char ** status)526 db_get_svc_princ(krb5_context ctx, krb5_principal princ,
527 krb5_flags flags, krb5_db_entry **server,
528 const char **status)
529 {
530 krb5_error_code ret;
531
532 ret = krb5_db_get_principal(ctx, princ, flags, server);
533 if (ret == KRB5_KDB_CANTLOCK_DB)
534 ret = KRB5KDC_ERR_SVC_UNAVAILABLE;
535 if (ret != 0) {
536 *status = "LOOKING_UP_SERVER";
537 }
538 return ret;
539 }
540
541 static krb5_error_code
search_sprinc(kdc_realm_t * realm,krb5_kdc_req * req,krb5_flags flags,krb5_db_entry ** server,const char ** status)542 search_sprinc(kdc_realm_t *realm, krb5_kdc_req *req,
543 krb5_flags flags, krb5_db_entry **server, const char **status)
544 {
545 krb5_context context = realm->realm_context;
546 krb5_error_code ret;
547 krb5_principal princ = req->server;
548 krb5_principal reftgs = NULL;
549 krb5_boolean allow_referral;
550
551 /* Do not allow referrals for u2u or ticket modification requests, because
552 * the server is supposed to match an already-issued ticket. */
553 allow_referral = !(req->kdc_options & NO_REFERRAL_OPTION);
554 if (!allow_referral)
555 flags &= ~KRB5_KDB_FLAG_REFERRAL_OK;
556
557 ret = db_get_svc_princ(context, princ, flags, server, status);
558 if (ret == 0 || ret != KRB5_KDB_NOENTRY || !allow_referral)
559 goto cleanup;
560
561 if (!is_cross_tgs_principal(req->server)) {
562 ret = find_referral_tgs(realm, req, &reftgs);
563 if (ret != 0)
564 goto cleanup;
565 ret = db_get_svc_princ(context, reftgs, flags, server, status);
566 if (ret == 0 || ret != KRB5_KDB_NOENTRY)
567 goto cleanup;
568
569 princ = reftgs;
570 }
571 ret = find_alternate_tgs(context, princ, server, status);
572
573 cleanup:
574 if (ret != 0 && ret != KRB5KDC_ERR_SVC_UNAVAILABLE) {
575 ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
576 if (*status == NULL)
577 *status = "LOOKING_UP_SERVER";
578 }
579 krb5_free_principal(context, reftgs);
580 return ret;
581 }
582
583 /*
584 * Transfer ownership of *reqptr to *t and fill *t with information about the
585 * request. Decode the PA-TGS-REQ header ticket and the second ticket if
586 * applicable, and decode and verify their PACs if present. Decode and verify
587 * the S4U2Self request pa-data if present. Extract authentication indicators
588 * from the subject ticket. Construct the transited list implied by the
589 * request.
590 */
591 static krb5_error_code
gather_tgs_req_info(kdc_realm_t * realm,krb5_kdc_req ** reqptr,krb5_data * pkt,const krb5_fulladdr * from,struct kdc_request_state * fast_state,krb5_audit_state * au_state,struct tgs_req_info * t,const char ** status)592 gather_tgs_req_info(kdc_realm_t *realm, krb5_kdc_req **reqptr, krb5_data *pkt,
593 const krb5_fulladdr *from,
594 struct kdc_request_state *fast_state,
595 krb5_audit_state *au_state, struct tgs_req_info *t,
596 const char **status)
597 {
598 krb5_context context = realm->realm_context;
599 krb5_error_code ret;
600 krb5_pa_data *pa_tgs_req;
601 unsigned int s_flags;
602 krb5_enc_tkt_part *header_enc;
603 krb5_data d;
604
605 /* Transfer ownership of *reqptr to *t. */
606 t->req = *reqptr;
607 *reqptr = NULL;
608
609 if (t->req->msg_type != KRB5_TGS_REQ)
610 return KRB5_BADMSGTYPE;
611
612 /* Initially set t->sprinc to the outer request server, for logging of
613 * early failures. */
614 t->sprinc = t->req->server;
615
616 /* Read the PA-TGS-REQ authenticator and decrypt the header ticket. */
617 ret = kdc_process_tgs_req(realm, t->req, from, pkt, &t->header_tkt,
618 &t->header_server, &t->header_key, &t->subkey,
619 &pa_tgs_req);
620 if (t->header_tkt != NULL && t->header_tkt->enc_part2 != NULL)
621 t->cprinc = t->header_tkt->enc_part2->client;
622 if (ret) {
623 *status = "PROCESS_TGS";
624 return ret;
625 }
626 ret = kau_make_tkt_id(context, t->header_tkt, &au_state->tkt_in_id);
627 if (ret)
628 return ret;
629 header_enc = t->header_tkt->enc_part2;
630
631 /* If PA-FX-FAST-REQUEST padata is present, replace t->req with the inner
632 * request body. */
633 d = make_data(pa_tgs_req->contents, pa_tgs_req->length);
634 ret = kdc_find_fast(&t->req, &d, t->subkey, header_enc->session,
635 fast_state, NULL);
636 if (ret) {
637 *status = "FIND_FAST";
638 return ret;
639 }
640 /* Reset t->sprinc for the inner body and check it. */
641 t->sprinc = t->req->server;
642 if (t->sprinc == NULL) {
643 *status = "NULL_SERVER";
644 return KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
645 }
646
647 /* The header ticket server is usually a TGT, but if it is not, fetch the
648 * local TGT for the realm. Get the decrypted first local TGT key. */
649 ret = get_local_tgt(context, &t->sprinc->realm, t->header_server,
650 &t->local_tgt, &t->local_tgt_storage,
651 &t->local_tgt_key);
652 if (ret) {
653 *status = "GET_LOCAL_TGT";
654 return ret;
655 }
656
657 /* Decode and verify the header ticket PAC. */
658 ret = get_verified_pac(context, header_enc, t->header_server,
659 t->header_key, t->local_tgt, &t->local_tgt_key,
660 &t->header_pac);
661 if (ret) {
662 *status = "HEADER_PAC";
663 return ret;
664 }
665
666 au_state->request = t->req;
667 au_state->stage = SRVC_PRINC;
668
669 /* Look up the server principal entry, or a referral/alternate TGT. Reset
670 * t->sprinc to the canonical server name (its final value). */
671 s_flags = (t->req->kdc_options & KDC_OPT_CANONICALIZE) ?
672 KRB5_KDB_FLAG_REFERRAL_OK : 0;
673 ret = search_sprinc(realm, t->req, s_flags, &t->server, status);
674 if (ret)
675 return ret;
676 t->sprinc = t->server->princ;
677
678 /* If we got a cross-realm TGS which is not the requested server, we are
679 * issuing a referral (or alternate TGT, which we treat similarly). */
680 if (is_cross_tgs_principal(t->server->princ) &&
681 !krb5_principal_compare(context, t->req->server, t->server->princ))
682 t->flags |= KRB5_KDB_FLAG_ISSUING_REFERRAL;
683
684 /* Mark the request as cross-realm if the header ticket server is not from
685 * this realm. */
686 if (!data_eq(t->header_server->princ->realm, t->sprinc->realm))
687 t->flags |= KRB5_KDB_FLAG_CROSS_REALM;
688
689 t->is_referral = (t->flags & KRB5_KDB_FLAG_ISSUING_REFERRAL);
690 t->is_crossrealm = (t->flags & KRB5_KDB_FLAG_CROSS_REALM);
691
692 /* If S4U2Self padata is present, read it to get the requested principal
693 * name. Look up the requested client if it is in this realm. */
694 ret = kdc_process_s4u2self_req(context, t->req, t->server, t->subkey,
695 header_enc->session, &t->s4u2self,
696 &t->client, status);
697 if (t->s4u2self != NULL || ret) {
698 if (t->s4u2self != NULL)
699 au_state->s4u2self_user = t->s4u2self->user_id.user;
700 au_state->status = *status;
701 kau_s4u2self(context, !ret, au_state);
702 au_state->s4u2self_user = NULL;
703 }
704 if (ret)
705 return ret;
706 if (t->s4u2self != NULL) {
707 t->flags |= KRB5_KDB_FLAG_PROTOCOL_TRANSITION;
708 t->s4u_cprinc = t->s4u2self->user_id.user;
709
710 /*
711 * For consistency with Active Directory, don't allow authorization
712 * data to be disabled if S4U2Self is requested. The requesting
713 * service likely needs a PAC for an S4U2Proxy operation, even if it
714 * doesn't need authorization data in tickets received from clients.
715 */
716 t->server->attributes &= ~KRB5_KDB_NO_AUTH_DATA_REQUIRED;
717 }
718
719 /* For U2U or S4U2Proxy requests, decrypt the second ticket and read its
720 * PAC. */
721 ret = decrypt_2ndtkt(context, t->req, t->flags, t->local_tgt,
722 &t->local_tgt_key, &t->stkt, &t->stkt_pac,
723 &t->stkt_server, &t->stkt_server_key, status);
724 if (ret)
725 return ret;
726
727 /* Determine the subject ticket and set the authtime for logging. For
728 * S4U2Proxy requests determine the requested client principal. */
729 if (t->req->kdc_options & KDC_OPT_CNAME_IN_ADDL_TKT) {
730 t->flags |= KRB5_KDB_FLAG_CONSTRAINED_DELEGATION;
731 ret = kau_make_tkt_id(context, t->stkt, &au_state->evid_tkt_id);
732 if (ret)
733 return ret;
734 if (t->is_crossrealm) {
735 /* For cross-realm S4U2PROXY requests, the second ticket is a
736 * cross TGT with the requested client principal in its PAC. */
737 if (t->stkt_pac == NULL ||
738 get_pac_princ_with_realm(context, t->stkt_pac,
739 &t->stkt_pac_client, NULL) != 0) {
740 au_state->status = *status = "RBCD_PAC_PRINC";
741 au_state->violation = PROT_CONSTRAINT;
742 kau_s4u2proxy(context, FALSE, au_state);
743 return KRB5KDC_ERR_BADOPTION;
744 }
745 t->s4u_cprinc = t->stkt_pac_client;
746 } else {
747 /* Otherwise the requested client is the evidence ticket client. */
748 t->s4u_cprinc = t->stkt->enc_part2->client;
749 }
750 t->subject_tkt = t->stkt->enc_part2;
751 } else {
752 t->subject_tkt = header_enc;
753 }
754 t->authtime = t->subject_tkt->times.authtime;
755
756 /* For final S4U requests (either type) the issued ticket will be for the
757 * requested name; otherwise it will be for the header ticket client. */
758 t->tkt_client = ((t->flags & KRB5_KDB_FLAGS_S4U) && !t->is_referral) ?
759 t->s4u_cprinc : header_enc->client;
760
761 if (t->s4u2self == NULL) {
762 /* Extract auth indicators from the subject ticket. Skip this for
763 * S4U2Self requests as the subject didn't authenticate. */
764 ret = get_auth_indicators(context, t->subject_tkt, t->local_tgt,
765 &t->local_tgt_key, &t->auth_indicators);
766 if (ret) {
767 *status = "GET_AUTH_INDICATORS";
768 return ret;
769 }
770
771 if (!(t->server->attributes & KRB5_KDB_NO_AUTH_DATA_REQUIRED)) {
772 /* Try to look up the subject principal so that KDB modules can add
773 * additional authdata. Ask the KDB to map foreign principals. */
774 assert(t->client == NULL);
775 (void)krb5_db_get_principal(context, t->subject_tkt->client,
776 t->flags | KRB5_KDB_FLAG_CLIENT |
777 KRB5_KDB_FLAG_MAP_PRINCIPALS,
778 &t->client);
779 }
780 }
781
782 /*
783 * Compute the transited list implied by the request. Use the existing
784 * transited list if the realm of the header ticket server is the same as
785 * the subject or server realm.
786 */
787 if (!t->is_crossrealm ||
788 data_eq(t->header_tkt->server->realm, t->tkt_client->realm)) {
789 t->transited = header_enc->transited;
790 } else {
791 if (header_enc->transited.tr_type != KRB5_DOMAIN_X500_COMPRESS) {
792 *status = "VALIDATE_TRANSIT_TYPE";
793 return KRB5KDC_ERR_TRTYPE_NOSUPP;
794 }
795 ret = add_to_transited(&header_enc->transited.tr_contents,
796 &t->new_transited, t->header_tkt->server,
797 t->tkt_client, t->req->server);
798 if (ret) {
799 *status = "ADD_TO_TRANSITED_LIST";
800 return ret;
801 }
802 t->transited.tr_type = KRB5_DOMAIN_X500_COMPRESS;
803 t->transited.tr_contents = t->new_transited;
804 }
805
806 return 0;
807 }
808
809 /* Fill in *times_out with the times of the ticket to be issued. Set the
810 * TKT_FLG_RENEWABLE bit in *tktflags if the ticket will be renewable. */
811 static void
compute_ticket_times(kdc_realm_t * realm,struct tgs_req_info * t,krb5_timestamp kdc_time,krb5_flags * tktflags,krb5_ticket_times * times)812 compute_ticket_times(kdc_realm_t *realm, struct tgs_req_info *t,
813 krb5_timestamp kdc_time, krb5_flags *tktflags,
814 krb5_ticket_times *times)
815 {
816 krb5_timestamp hstarttime;
817 krb5_deltat hlife;
818 krb5_ticket_times *htimes = &t->header_tkt->enc_part2->times;
819
820 if (t->req->kdc_options & KDC_OPT_VALIDATE) {
821 /* Validation requests preserve the header ticket times. */
822 *times = *htimes;
823 return;
824 }
825
826 /* Preserve the authtime from the subject ticket. */
827 times->authtime = t->authtime;
828
829 times->starttime = (t->req->kdc_options & KDC_OPT_POSTDATED) ?
830 t->req->from : kdc_time;
831
832 if (t->req->kdc_options & KDC_OPT_RENEW) {
833 /* Give the new ticket the same lifetime as the header ticket, but no
834 * later than the renewable end time. */
835 hstarttime = htimes->starttime ? htimes->starttime : htimes->authtime;
836 hlife = ts_delta(htimes->endtime, hstarttime);
837 times->endtime = ts_min(htimes->renew_till,
838 ts_incr(times->starttime, hlife));
839 } else {
840 kdc_get_ticket_endtime(realm, times->starttime, htimes->endtime,
841 t->req->till, t->client, t->server,
842 ×->endtime);
843 }
844
845 kdc_get_ticket_renewtime(realm, t->req, t->header_tkt->enc_part2,
846 t->client, t->server, tktflags, times);
847
848 /* starttime is optional, and treated as authtime if not present.
849 * so we can omit it if it matches. */
850 if (times->starttime == times->authtime)
851 times->starttime = 0;
852 }
853
854 /* Check the request in *t against semantic protocol constraints and local
855 * policy. Determine flags and times for the ticket to be issued. */
856 static krb5_error_code
check_tgs_req(kdc_realm_t * realm,struct tgs_req_info * t,krb5_audit_state * au_state,krb5_flags * tktflags,krb5_ticket_times * times,const char ** status,krb5_pa_data *** e_data)857 check_tgs_req(kdc_realm_t *realm, struct tgs_req_info *t,
858 krb5_audit_state *au_state, krb5_flags *tktflags,
859 krb5_ticket_times *times, const char **status,
860 krb5_pa_data ***e_data)
861 {
862 krb5_context context = realm->realm_context;
863 krb5_error_code ret;
864 krb5_timestamp kdc_time;
865
866 au_state->stage = VALIDATE_POL;
867
868 ret = krb5_timeofday(context, &kdc_time);
869 if (ret)
870 return ret;
871
872 ret = check_tgs_constraints(realm, t->req, t->server, t->header_tkt,
873 t->header_pac, t->stkt, t->stkt_pac,
874 t->stkt_server, kdc_time, t->s4u2self,
875 t->client, t->is_crossrealm, t->is_referral,
876 status, e_data);
877 if (ret) {
878 au_state->violation = PROT_CONSTRAINT;
879 return ret;
880 }
881
882 ret = check_tgs_policy(realm, t->req, t->server, t->header_tkt,
883 t->header_pac, t->stkt, t->stkt_pac,
884 t->stkt_pac_client, t->stkt_server, kdc_time,
885 t->is_crossrealm, t->is_referral, status, e_data);
886 if (ret) {
887 au_state->violation = LOCAL_POLICY;
888 if (t->flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION) {
889 au_state->status = *status;
890 kau_s4u2proxy(context, FALSE, au_state);
891 }
892 return ret;
893 }
894
895 /* Check auth indicators from the subject ticket, except for S4U2Self
896 * requests (where the client didn't authenticate). */
897 if (t->s4u2self == NULL) {
898 ret = check_indicators(context, t->server, t->auth_indicators);
899 if (ret) {
900 *status = "HIGHER_AUTHENTICATION_REQUIRED";
901 return ret;
902 }
903 }
904
905 *tktflags = get_ticket_flags(t->req->kdc_options, t->client, t->server,
906 t->header_tkt->enc_part2);
907 compute_ticket_times(realm, t, kdc_time, tktflags, times);
908
909 /* For S4U2Self requests, check if we need to suppress the forwardable
910 * ticket flag. */
911 if (t->s4u2self != NULL && !t->is_referral) {
912 ret = s4u2self_forwardable(context, t->server, tktflags);
913 if (ret)
914 return ret;
915 }
916
917 /* Consult kdcpolicy modules, giving them a chance to modify the times of
918 * the issued ticket. */
919 ret = check_kdcpolicy_tgs(context, t->req, t->server, t->header_tkt,
920 t->auth_indicators, kdc_time, times, status);
921 if (ret)
922 return ret;
923
924 if (!(t->req->kdc_options & KDC_OPT_DISABLE_TRANSITED_CHECK)) {
925 /* Check the transited path for the issued ticket and set the
926 * transited-policy-checked flag if successful. */
927 ret = kdc_check_transited_list(context, &t->transited.tr_contents,
928 &t->subject_tkt->client->realm,
929 &t->req->server->realm);
930 if (ret) {
931 /* Log the transited-check failure and continue. */
932 log_tgs_badtrans(context, t->cprinc, t->sprinc,
933 &t->transited.tr_contents, ret);
934 } else {
935 *tktflags |= TKT_FLG_TRANSIT_POLICY_CHECKED;
936 }
937 } else {
938 krb5_klog_syslog(LOG_INFO, _("not checking transit path"));
939 }
940
941 /* By default, reject the request if the transited path was not checked
942 * successfully. */
943 if (realm->realm_reject_bad_transit &&
944 !(*tktflags & TKT_FLG_TRANSIT_POLICY_CHECKED)) {
945 *status = "BAD_TRANSIT";
946 au_state->violation = LOCAL_POLICY;
947 return KRB5KDC_ERR_POLICY;
948 }
949
950 return 0;
951 }
952
953 /* Construct a response issuing a ticket for the request in *t, using tktflags
954 * and *times for the ticket flags and times. */
955 static krb5_error_code
tgs_issue_ticket(kdc_realm_t * realm,struct tgs_req_info * t,krb5_flags tktflags,krb5_ticket_times * times,krb5_data * pkt,const krb5_fulladdr * from,struct kdc_request_state * fast_state,krb5_audit_state * au_state,const char ** status,krb5_data ** response)956 tgs_issue_ticket(kdc_realm_t *realm, struct tgs_req_info *t,
957 krb5_flags tktflags, krb5_ticket_times *times, krb5_data *pkt,
958 const krb5_fulladdr *from,
959 struct kdc_request_state *fast_state,
960 krb5_audit_state *au_state, const char **status,
961 krb5_data **response)
962 {
963 krb5_context context = realm->realm_context;
964 krb5_error_code ret;
965 krb5_keyblock session_key = { 0 }, server_key = { 0 };
966 krb5_keyblock *ticket_encrypting_key, *subject_key;
967 krb5_keyblock *initial_reply_key, *fast_reply_key = NULL;
968 krb5_enc_tkt_part enc_tkt_reply = { 0 };
969 krb5_ticket ticket_reply = { 0 };
970 krb5_enc_kdc_rep_part reply_encpart = { 0 };
971 krb5_kdc_rep reply = { 0 };
972 krb5_pac subject_pac;
973 krb5_db_entry *subject_server;
974 krb5_enc_tkt_part *header_enc_tkt = t->header_tkt->enc_part2;
975 krb5_last_req_entry nolrentry = { KV5M_LAST_REQ_ENTRY, KRB5_LRQ_NONE, 0 };
976 krb5_last_req_entry *nolrarray[2] = { &nolrentry, NULL };
977
978 au_state->stage = ISSUE_TKT;
979
980 ret = gen_session_key(context, t->req, t->server, &session_key, status);
981 if (ret)
982 goto cleanup;
983
984 if (t->flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION) {
985 subject_pac = t->stkt_pac;
986 subject_server = t->stkt_server;
987 subject_key = t->stkt_server_key;
988 } else {
989 subject_pac = t->header_pac;
990 subject_server = t->header_server;
991 subject_key = t->header_key;
992 }
993
994 initial_reply_key = (t->subkey != NULL) ? t->subkey :
995 t->header_tkt->enc_part2->session;
996
997 if (t->req->kdc_options & KDC_OPT_ENC_TKT_IN_SKEY) {
998 /* For user-to-user, encrypt the ticket with the second ticket's
999 * session key. */
1000 ticket_encrypting_key = t->stkt->enc_part2->session;
1001 } else {
1002 /* Otherwise encrypt the ticket with the server entry's first long-term
1003 * key. */
1004 ret = get_first_current_key(context, t->server, &server_key);
1005 if (ret) {
1006 *status = "FINDING_SERVER_KEY";
1007 goto cleanup;
1008 }
1009 ticket_encrypting_key = &server_key;
1010 }
1011
1012 if (t->req->kdc_options & (KDC_OPT_VALIDATE | KDC_OPT_RENEW)) {
1013 /* Copy the header ticket server and all enc-part fields except for
1014 * authorization data. */
1015 ticket_reply.server = t->header_tkt->server;
1016 enc_tkt_reply = *t->header_tkt->enc_part2;
1017 enc_tkt_reply.authorization_data = NULL;
1018 } else {
1019 if (t->req->kdc_options & (KDC_OPT_FORWARDED | KDC_OPT_PROXY)) {
1020 /* Include the requested addresses in the ticket and reply. */
1021 enc_tkt_reply.caddrs = t->req->addresses;
1022 reply_encpart.caddrs = t->req->addresses;
1023 } else {
1024 /* Use the header ticket addresses and omit them from the reply. */
1025 enc_tkt_reply.caddrs = header_enc_tkt->caddrs;
1026 reply_encpart.caddrs = NULL;
1027 }
1028
1029 ticket_reply.server = t->is_referral ? t->sprinc : t->req->server;
1030 }
1031
1032 enc_tkt_reply.flags = tktflags;
1033 enc_tkt_reply.times = *times;
1034 enc_tkt_reply.client = t->tkt_client;
1035 enc_tkt_reply.session = &session_key;
1036 enc_tkt_reply.transited = t->transited;
1037
1038 ret = handle_authdata(realm, t->flags, t->client, t->server,
1039 subject_server, t->local_tgt, &t->local_tgt_key,
1040 initial_reply_key, ticket_encrypting_key,
1041 subject_key, NULL, pkt, t->req, t->s4u_cprinc,
1042 subject_pac, t->subject_tkt, &t->auth_indicators,
1043 &enc_tkt_reply);
1044 if (ret) {
1045 krb5_klog_syslog(LOG_INFO, _("TGS_REQ : handle_authdata (%d)"), ret);
1046 *status = "HANDLE_AUTHDATA";
1047 goto cleanup;
1048 }
1049
1050 ticket_reply.enc_part2 = &enc_tkt_reply;
1051
1052 ret = krb5_encrypt_tkt_part(context, ticket_encrypting_key, &ticket_reply);
1053 if (ret)
1054 goto cleanup;
1055
1056 if (t->req->kdc_options & KDC_OPT_ENC_TKT_IN_SKEY) {
1057 ticket_reply.enc_part.kvno = 0;
1058 kau_u2u(context, TRUE, au_state);
1059 } else {
1060 ticket_reply.enc_part.kvno = current_kvno(t->server);
1061 }
1062
1063 au_state->stage = ENCR_REP;
1064
1065 if (t->s4u2self != NULL &&
1066 krb5int_find_pa_data(context, t->req->padata,
1067 KRB5_PADATA_S4U_X509_USER) != NULL) {
1068 /* Add an S4U2Self response to the encrypted padata (skipped if the
1069 * request only included PA-FOR-USER padata). */
1070 ret = kdc_make_s4u2self_rep(context, t->subkey,
1071 t->header_tkt->enc_part2->session,
1072 t->s4u2self, &reply, &reply_encpart);
1073 if (ret)
1074 goto cleanup;
1075 }
1076
1077 reply_encpart.session = &session_key;
1078 reply_encpart.nonce = t->req->nonce;
1079 reply_encpart.times = enc_tkt_reply.times;
1080 reply_encpart.last_req = nolrarray;
1081 reply_encpart.key_exp = 0;
1082 reply_encpart.flags = enc_tkt_reply.flags;
1083 reply_encpart.server = ticket_reply.server;
1084
1085 reply.msg_type = KRB5_TGS_REP;
1086 reply.client = enc_tkt_reply.client;
1087 reply.ticket = &ticket_reply;
1088 reply.enc_part.kvno = 0;
1089 reply.enc_part.enctype = initial_reply_key->enctype;
1090 ret = kdc_fast_response_handle_padata(fast_state, t->req, &reply,
1091 initial_reply_key->enctype);
1092 if (ret)
1093 goto cleanup;
1094 ret = kdc_fast_handle_reply_key(fast_state, initial_reply_key,
1095 &fast_reply_key);
1096 if (ret)
1097 goto cleanup;
1098 ret = return_enc_padata(context, pkt, t->req, fast_reply_key, t->server,
1099 &reply_encpart,
1100 t->is_referral &&
1101 (t->req->kdc_options & KDC_OPT_CANONICALIZE));
1102 if (ret) {
1103 *status = "KDC_RETURN_ENC_PADATA";
1104 goto cleanup;
1105 }
1106
1107 ret = kau_make_tkt_id(context, &ticket_reply, &au_state->tkt_out_id);
1108 if (ret)
1109 goto cleanup;
1110
1111 if (kdc_fast_hide_client(fast_state))
1112 reply.client = (krb5_principal)krb5_anonymous_principal();
1113 ret = krb5_encode_kdc_rep(context, KRB5_TGS_REP, &reply_encpart,
1114 t->subkey != NULL, fast_reply_key, &reply,
1115 response);
1116 if (ret)
1117 goto cleanup;
1118
1119 log_tgs_req(context, from, t->req, &reply, t->cprinc, t->sprinc,
1120 t->s4u_cprinc, t->authtime, t->flags, "ISSUE", 0, NULL);
1121 au_state->status = "ISSUE";
1122 au_state->reply = &reply;
1123 if (t->flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION)
1124 kau_s4u2proxy(context, TRUE, au_state);
1125 kau_tgs_req(context, TRUE, au_state);
1126 au_state->reply = NULL;
1127
1128 cleanup:
1129 zapfree(ticket_reply.enc_part.ciphertext.data,
1130 ticket_reply.enc_part.ciphertext.length);
1131 zapfree(reply.enc_part.ciphertext.data, reply.enc_part.ciphertext.length);
1132 krb5_free_pa_data(context, reply.padata);
1133 krb5_free_pa_data(context, reply_encpart.enc_padata);
1134 krb5_free_authdata(context, enc_tkt_reply.authorization_data);
1135 krb5_free_keyblock_contents(context, &session_key);
1136 krb5_free_keyblock_contents(context, &server_key);
1137 krb5_free_keyblock(context, fast_reply_key);
1138 return ret;
1139 }
1140
1141 static void
free_req_info(krb5_context context,struct tgs_req_info * t)1142 free_req_info(krb5_context context, struct tgs_req_info *t)
1143 {
1144 krb5_free_kdc_req(context, t->req);
1145 krb5_free_ticket(context, t->header_tkt);
1146 krb5_db_free_principal(context, t->header_server);
1147 krb5_free_keyblock(context, t->header_key);
1148 krb5_free_keyblock(context, t->subkey);
1149 krb5_pac_free(context, t->header_pac);
1150 krb5_pac_free(context, t->stkt_pac);
1151 krb5_db_free_principal(context, t->stkt_server);
1152 krb5_free_keyblock(context, t->stkt_server_key);
1153 krb5_db_free_principal(context, t->local_tgt_storage);
1154 krb5_free_keyblock_contents(context, &t->local_tgt_key);
1155 krb5_db_free_principal(context, t->server);
1156 krb5_db_free_principal(context, t->client);
1157 krb5_free_pa_s4u_x509_user(context, t->s4u2self);
1158 krb5_free_principal(context, t->stkt_pac_client);
1159 k5_free_data_ptr_list(t->auth_indicators);
1160 krb5_free_data_contents(context, &t->new_transited);
1161 }
1162
1163 krb5_error_code
process_tgs_req(krb5_kdc_req * request,krb5_data * pkt,const krb5_fulladdr * from,kdc_realm_t * realm,krb5_data ** response)1164 process_tgs_req(krb5_kdc_req *request, krb5_data *pkt,
1165 const krb5_fulladdr *from, kdc_realm_t *realm,
1166 krb5_data **response)
1167 {
1168 krb5_context context = realm->realm_context;
1169 krb5_error_code ret;
1170 struct tgs_req_info t = { 0 };
1171 struct kdc_request_state *fast_state = NULL;
1172 krb5_audit_state *au_state = NULL;
1173 krb5_pa_data **e_data = NULL;
1174 krb5_flags tktflags;
1175 krb5_ticket_times times = { 0 };
1176 const char *emsg = NULL, *status = NULL;
1177
1178 ret = kdc_make_rstate(realm, &fast_state);
1179 if (ret)
1180 goto cleanup;
1181 ret = kau_init_kdc_req(context, request, from, &au_state);
1182 if (ret)
1183 goto cleanup;
1184 kau_tgs_req(context, TRUE, au_state);
1185
1186 ret = gather_tgs_req_info(realm, &request, pkt, from, fast_state, au_state,
1187 &t, &status);
1188 if (ret)
1189 goto cleanup;
1190
1191 ret = check_tgs_req(realm, &t, au_state, &tktflags, ×, &status,
1192 &e_data);
1193 if (ret)
1194 goto cleanup;
1195
1196 ret = tgs_issue_ticket(realm, &t, tktflags, ×, pkt, from, fast_state,
1197 au_state, &status, response);
1198 if (ret)
1199 goto cleanup;
1200
1201 cleanup:
1202 if (status == NULL)
1203 status = "UNKNOWN_REASON";
1204
1205 if (ret) {
1206 emsg = krb5_get_error_message(context, ret);
1207 log_tgs_req(context, from, t.req, NULL, t.cprinc, t.sprinc,
1208 t.s4u_cprinc, t.authtime, t.flags, status, ret, emsg);
1209 krb5_free_error_message(context, emsg);
1210
1211 if (au_state != NULL) {
1212 au_state->status = status;
1213 kau_tgs_req(context, FALSE, au_state);
1214 }
1215 }
1216
1217 if (ret && fast_state != NULL) {
1218 ret = prepare_error_tgs(fast_state, t.req, t.header_tkt, ret,
1219 (t.server != NULL) ? t.server->princ : NULL,
1220 response, status, e_data);
1221 }
1222
1223 krb5_free_kdc_req(context, request);
1224 kdc_free_rstate(fast_state);
1225 kau_free_kdc_req(au_state);
1226 free_req_info(context, &t);
1227 krb5_free_pa_data(context, e_data);
1228 return ret;
1229 }
1230