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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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