1 /* 2 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 #pragma ident "%Z%%M% %I% %E% SMI" 7 8 /* 9 * kdc/do_tgs_req.c 10 * 11 * Copyright 1990,1991,2001 by the Massachusetts Institute of Technology. 12 * All Rights Reserved. 13 * 14 * Export of this software from the United States of America may 15 * require a specific license from the United States Government. 16 * It is the responsibility of any person or organization contemplating 17 * export to obtain such a license before exporting. 18 * 19 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 20 * distribute this software and its documentation for any purpose and 21 * without fee is hereby granted, provided that the above copyright 22 * notice appear in all copies and that both that copyright notice and 23 * this permission notice appear in supporting documentation, and that 24 * the name of M.I.T. not be used in advertising or publicity pertaining 25 * to distribution of the software without specific, written prior 26 * permission. Furthermore if you modify this software you must label 27 * your software as modified software and not distribute it in such a 28 * fashion that it might be confused with the original M.I.T. software. 29 * M.I.T. makes no representations about the suitability of 30 * this software for any purpose. It is provided "as is" without express 31 * or implied warranty. 32 * 33 * 34 * KDC Routines to deal with TGS_REQ's 35 */ 36 37 #define NEED_SOCKETS 38 #include "k5-int.h" 39 #include "com_err.h" 40 41 #include <syslog.h> 42 #ifdef HAVE_NETINET_IN_H 43 #include <sys/types.h> 44 #include <netinet/in.h> 45 #ifndef hpux 46 #include <arpa/inet.h> 47 #endif 48 #endif 49 50 #include "kdc_util.h" 51 #include "policy.h" 52 #include "extern.h" 53 #include "adm_proto.h" 54 55 extern krb5_error_code setup_server_realm(krb5_principal); 56 57 static void find_alternate_tgs (krb5_kdc_req *, krb5_db_entry *, 58 krb5_boolean *, int *, 59 const krb5_fulladdr *from, char *cname); 60 61 static krb5_error_code prepare_error_tgs (krb5_kdc_req *, krb5_ticket *, 62 int, const char *, krb5_data **, 63 const char *); 64 65 /*ARGSUSED*/ 66 krb5_error_code 67 process_tgs_req(krb5_data *pkt, const krb5_fulladdr *from, 68 krb5_data **response) 69 { 70 krb5_keyblock * subkey; 71 krb5_kdc_req *request = 0; 72 krb5_db_entry server; 73 krb5_kdc_rep reply; 74 krb5_enc_kdc_rep_part reply_encpart; 75 krb5_ticket ticket_reply, *header_ticket = 0; 76 int st_idx = 0; 77 krb5_enc_tkt_part enc_tkt_reply; 78 krb5_transited enc_tkt_transited; 79 int newtransited = 0; 80 krb5_error_code retval = 0; 81 int nprincs = 0; 82 krb5_boolean more; 83 krb5_timestamp kdc_time, authtime=0; 84 krb5_keyblock session_key; 85 krb5_timestamp until, rtime; 86 krb5_keyblock encrypting_key; 87 krb5_key_data *server_key; 88 char *cname = 0, *sname = 0, *tmp = 0; 89 const char *fromstring = 0; 90 krb5_last_req_entry *nolrarray[2], nolrentry; 91 /* krb5_address *noaddrarray[1]; */ 92 krb5_enctype useenctype; 93 int errcode, errcode2; 94 register int i; 95 int firstpass = 1; 96 const char *status = 0; 97 char ktypestr[128]; 98 char rep_etypestr[128]; 99 char fromstringbuf[70]; 100 long long tmp_server_times, tmp_realm_times; 101 102 (void) memset(&encrypting_key, 0, sizeof(krb5_keyblock)); 103 (void) memset(&session_key, 0, sizeof(krb5_keyblock)); 104 105 retval = decode_krb5_tgs_req(pkt, &request); 106 if (retval) 107 return retval; 108 109 ktypes2str(ktypestr, sizeof(ktypestr), 110 request->nktypes, request->ktype); 111 /* 112 * setup_server_realm() sets up the global realm-specific data pointer. 113 */ 114 if ((retval = setup_server_realm(request->server))) 115 return retval; 116 117 fromstring = inet_ntop(ADDRTYPE2FAMILY(from->address->addrtype), 118 from->address->contents, 119 fromstringbuf, sizeof(fromstringbuf)); 120 if (!fromstring) 121 fromstring = "<unknown>"; 122 123 if ((errcode = krb5_unparse_name(kdc_context, request->server, &sname))) { 124 status = "UNPARSING SERVER"; 125 goto cleanup; 126 } 127 limit_string(sname); 128 129 /* errcode = kdc_process_tgs_req(request, from, pkt, &req_authdat); */ 130 errcode = kdc_process_tgs_req(request, from, pkt, &header_ticket, &subkey); 131 132 if (header_ticket && header_ticket->enc_part2 && 133 (errcode2 = krb5_unparse_name(kdc_context, 134 header_ticket->enc_part2->client, 135 &cname))) { 136 status = "UNPARSING CLIENT"; 137 errcode = errcode2; 138 goto cleanup; 139 } 140 limit_string(cname); 141 142 if (errcode) { 143 status = "PROCESS_TGS"; 144 goto cleanup; 145 } 146 147 if (!header_ticket) { 148 errcode = KRB5_NO_TKT_SUPPLIED; /* XXX? */ 149 status="UNEXPECTED NULL in header_ticket"; 150 goto cleanup; 151 } 152 153 /* 154 * We've already dealt with the AP_REQ authentication, so we can 155 * use header_ticket freely. The encrypted part (if any) has been 156 * decrypted with the session key. 157 */ 158 159 authtime = header_ticket->enc_part2->times.authtime; 160 161 /* XXX make sure server here has the proper realm...taken from AP_REQ 162 header? */ 163 164 nprincs = 1; 165 if ((errcode = krb5_db_get_principal(kdc_context, request->server, &server, 166 &nprincs, &more))) { 167 status = "LOOKING_UP_SERVER"; 168 nprincs = 0; 169 goto cleanup; 170 } 171 tgt_again: 172 if (more) { 173 status = "NON_UNIQUE_PRINCIPAL"; 174 errcode = KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE; 175 goto cleanup; 176 } else if (nprincs != 1) { 177 /* 178 * might be a request for a TGT for some other realm; we 179 * should do our best to find such a TGS in this db 180 */ 181 if (firstpass && krb5_is_tgs_principal(request->server) == TRUE) { 182 if (krb5_princ_size(kdc_context, request->server) == 2) { 183 krb5_data *server_1 = 184 krb5_princ_component(kdc_context, request->server, 1); 185 krb5_data *tgs_1 = 186 krb5_princ_component(kdc_context, tgs_server, 1); 187 188 if (!tgs_1 || server_1->length != tgs_1->length || 189 memcmp(server_1->data, tgs_1->data, tgs_1->length)) { 190 krb5_db_free_principal(kdc_context, &server, nprincs); 191 find_alternate_tgs(request, &server, &more, &nprincs, 192 from, cname); 193 firstpass = 0; 194 goto tgt_again; 195 } 196 } 197 } 198 krb5_db_free_principal(kdc_context, &server, nprincs); 199 status = "UNKNOWN_SERVER"; 200 errcode = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; 201 goto cleanup; 202 } 203 204 if ((errcode = krb5_timeofday(kdc_context, &kdc_time))) { 205 status = "TIME_OF_DAY"; 206 goto cleanup; 207 } 208 209 if ((retval = validate_tgs_request(request, server, header_ticket, 210 kdc_time, &status))) { 211 if (!status) 212 status = "UNKNOWN_REASON"; 213 errcode = retval + ERROR_TABLE_BASE_krb5; 214 goto cleanup; 215 } 216 217 /* 218 * We pick the session keytype here.... 219 * 220 * Some special care needs to be taken in the user-to-user 221 * case, since we don't know what keytypes the application server 222 * which is doing user-to-user authentication can support. We 223 * know that it at least must be able to support the encryption 224 * type of the session key in the TGT, since otherwise it won't be 225 * able to decrypt the U2U ticket! So we use that in preference 226 * to anything else. 227 */ 228 useenctype = 0; 229 if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY)) { 230 krb5_keyblock * st_sealing_key; 231 krb5_kvno st_srv_kvno; 232 krb5_enctype etype; 233 234 /* 235 * Get the key for the second ticket, and decrypt it. 236 */ 237 if ((errcode = kdc_get_server_key(request->second_ticket[st_idx], 238 &st_sealing_key, 239 &st_srv_kvno))) { 240 status = "2ND_TKT_SERVER"; 241 goto cleanup; 242 } 243 errcode = krb5_decrypt_tkt_part(kdc_context, st_sealing_key, 244 request->second_ticket[st_idx]); 245 krb5_free_keyblock(kdc_context, st_sealing_key); 246 if (errcode) { 247 status = "2ND_TKT_DECRYPT"; 248 goto cleanup; 249 } 250 251 etype = request->second_ticket[st_idx]->enc_part2->session->enctype; 252 if (!krb5_c_valid_enctype(etype)) { 253 status = "BAD_ETYPE_IN_2ND_TKT"; 254 errcode = KRB5KDC_ERR_ETYPE_NOSUPP; 255 goto cleanup; 256 } 257 258 for (i = 0; i < request->nktypes; i++) { 259 if (request->ktype[i] == etype) { 260 useenctype = etype; 261 break; 262 } 263 } 264 } 265 266 /* 267 * Select the keytype for the ticket session key. 268 */ 269 if ((useenctype == 0) && 270 (useenctype = select_session_keytype(kdc_context, &server, 271 request->nktypes, 272 request->ktype)) == 0) { 273 /* unsupported ktype */ 274 status = "BAD_ENCRYPTION_TYPE"; 275 errcode = KRB5KDC_ERR_ETYPE_NOSUPP; 276 goto cleanup; 277 } 278 279 errcode = krb5_c_make_random_key(kdc_context, useenctype, &session_key); 280 281 if (errcode) { 282 /* random key failed */ 283 status = "RANDOM_KEY_FAILED"; 284 goto cleanup; 285 } 286 287 ticket_reply.server = request->server; /* XXX careful for realm... */ 288 289 enc_tkt_reply.flags = 0; 290 enc_tkt_reply.times.starttime = 0; 291 292 /* 293 * Fix header_ticket's starttime; if it's zero, fill in the 294 * authtime's value. 295 */ 296 if (!(header_ticket->enc_part2->times.starttime)) 297 header_ticket->enc_part2->times.starttime = 298 header_ticket->enc_part2->times.authtime; 299 300 /* don't use new addresses unless forwarded, see below */ 301 302 enc_tkt_reply.caddrs = header_ticket->enc_part2->caddrs; 303 /* noaddrarray[0] = 0; */ 304 reply_encpart.caddrs = 0; /* optional...don't put it in */ 305 306 /* It should be noted that local policy may affect the */ 307 /* processing of any of these flags. For example, some */ 308 /* realms may refuse to issue renewable tickets */ 309 310 if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE)) 311 setflag(enc_tkt_reply.flags, TKT_FLG_FORWARDABLE); 312 313 if (isflagset(request->kdc_options, KDC_OPT_FORWARDED)) { 314 setflag(enc_tkt_reply.flags, TKT_FLG_FORWARDED); 315 316 /* include new addresses in ticket & reply */ 317 318 enc_tkt_reply.caddrs = request->addresses; 319 reply_encpart.caddrs = request->addresses; 320 } 321 if (isflagset(header_ticket->enc_part2->flags, TKT_FLG_FORWARDED)) 322 setflag(enc_tkt_reply.flags, TKT_FLG_FORWARDED); 323 324 if (isflagset(request->kdc_options, KDC_OPT_PROXIABLE)) 325 setflag(enc_tkt_reply.flags, TKT_FLG_PROXIABLE); 326 327 if (isflagset(request->kdc_options, KDC_OPT_PROXY)) { 328 setflag(enc_tkt_reply.flags, TKT_FLG_PROXY); 329 330 /* include new addresses in ticket & reply */ 331 332 enc_tkt_reply.caddrs = request->addresses; 333 reply_encpart.caddrs = request->addresses; 334 } 335 336 if (isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE)) 337 setflag(enc_tkt_reply.flags, TKT_FLG_MAY_POSTDATE); 338 339 if (isflagset(request->kdc_options, KDC_OPT_POSTDATED)) { 340 setflag(enc_tkt_reply.flags, TKT_FLG_POSTDATED); 341 setflag(enc_tkt_reply.flags, TKT_FLG_INVALID); 342 enc_tkt_reply.times.starttime = request->from; 343 } else 344 enc_tkt_reply.times.starttime = kdc_time; 345 346 if (isflagset(request->kdc_options, KDC_OPT_VALIDATE)) { 347 /* BEWARE of allocation hanging off of ticket & enc_part2, it belongs 348 to the caller */ 349 ticket_reply = *(header_ticket); 350 enc_tkt_reply = *(header_ticket->enc_part2); 351 clear(enc_tkt_reply.flags, TKT_FLG_INVALID); 352 } 353 354 if (isflagset(request->kdc_options, KDC_OPT_RENEW)) { 355 krb5_deltat old_life; 356 357 /* BEWARE of allocation hanging off of ticket & enc_part2, it belongs 358 to the caller */ 359 ticket_reply = *(header_ticket); 360 enc_tkt_reply = *(header_ticket->enc_part2); 361 362 old_life = enc_tkt_reply.times.endtime - enc_tkt_reply.times.starttime; 363 364 enc_tkt_reply.times.starttime = kdc_time; 365 enc_tkt_reply.times.endtime = 366 min(header_ticket->enc_part2->times.renew_till, 367 kdc_time + old_life); 368 } else { 369 /* not a renew request */ 370 enc_tkt_reply.times.starttime = kdc_time; 371 until = (request->till == 0) ? kdc_infinity : request->till; 372 373 /* SUNW */ 374 tmp_server_times = (long long) enc_tkt_reply.times.starttime 375 + server.max_life; 376 377 tmp_realm_times = (long long) enc_tkt_reply.times.starttime 378 + max_life_for_realm; 379 380 enc_tkt_reply.times.endtime = 381 min(until, 382 min(tmp_server_times, 383 min(tmp_realm_times, 384 min(header_ticket->enc_part2->times.endtime, 385 KRB5_KDB_EXPIRATION)))); /* SUNW */ 386 /* 387 enc_tkt_reply.times.endtime = 388 min(until, min(enc_tkt_reply.times.starttime + server.max_life, 389 min(enc_tkt_reply.times.starttime + max_life_for_realm, 390 min(header_ticket->enc_part2->times.endtime))); 391 */ 392 if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE_OK) && 393 (enc_tkt_reply.times.endtime < request->till) && 394 isflagset(header_ticket->enc_part2->flags, 395 TKT_FLG_RENEWABLE)) { 396 setflag(request->kdc_options, KDC_OPT_RENEWABLE); 397 request->rtime = 398 min(request->till, 399 min(KRB5_KDB_EXPIRATION, 400 header_ticket->enc_part2->times.renew_till)); 401 } 402 } 403 rtime = (request->rtime == 0) ? kdc_infinity : request->rtime; 404 405 if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE)) { 406 /* already checked above in policy check to reject request for a 407 renewable ticket using a non-renewable ticket */ 408 setflag(enc_tkt_reply.flags, TKT_FLG_RENEWABLE); 409 tmp_realm_times = (long long) enc_tkt_reply.times.starttime + 410 min(server.max_renewable_life,max_renewable_life_for_realm); 411 enc_tkt_reply.times.renew_till = 412 min(rtime, 413 min(header_ticket->enc_part2->times.renew_till, 414 min (tmp_realm_times, KRB5_KDB_EXPIRATION))); 415 } else { 416 enc_tkt_reply.times.renew_till = 0; 417 } 418 419 /* 420 * Set authtime to be the same as header_ticket's 421 */ 422 enc_tkt_reply.times.authtime = header_ticket->enc_part2->times.authtime; 423 424 /* 425 * Propagate the preauthentication flags through to the returned ticket. 426 */ 427 if (isflagset(header_ticket->enc_part2->flags, TKT_FLG_PRE_AUTH)) 428 setflag(enc_tkt_reply.flags, TKT_FLG_PRE_AUTH); 429 430 if (isflagset(header_ticket->enc_part2->flags, TKT_FLG_HW_AUTH)) 431 setflag(enc_tkt_reply.flags, TKT_FLG_HW_AUTH); 432 433 /* starttime is optional, and treated as authtime if not present. 434 so we can nuke it if it matches */ 435 if (enc_tkt_reply.times.starttime == enc_tkt_reply.times.authtime) 436 enc_tkt_reply.times.starttime = 0; 437 438 /* assemble any authorization data */ 439 if (request->authorization_data.ciphertext.data) { 440 krb5_data scratch; 441 442 scratch.length = request->authorization_data.ciphertext.length; 443 if (!(scratch.data = 444 malloc(request->authorization_data.ciphertext.length))) { 445 status = "AUTH_NOMEM"; 446 errcode = ENOMEM; 447 goto cleanup; 448 } 449 450 if ((errcode = krb5_c_decrypt(kdc_context, 451 header_ticket->enc_part2->session, 452 KRB5_KEYUSAGE_TGS_REQ_AD_SESSKEY, 453 0, &request->authorization_data, 454 &scratch))) { 455 status = "AUTH_ENCRYPT_FAIL"; 456 free(scratch.data); 457 goto cleanup; 458 } 459 460 /* scratch now has the authorization data, so we decode it */ 461 errcode = decode_krb5_authdata(&scratch, &(request->unenc_authdata)); 462 free(scratch.data); 463 if (errcode) { 464 status = "AUTH_DECODE"; 465 goto cleanup; 466 } 467 468 if ((errcode = 469 concat_authorization_data(request->unenc_authdata, 470 header_ticket->enc_part2->authorization_data, 471 &enc_tkt_reply.authorization_data))) { 472 status = "CONCAT_AUTH"; 473 goto cleanup; 474 } 475 } else 476 enc_tkt_reply.authorization_data = 477 header_ticket->enc_part2->authorization_data; 478 479 enc_tkt_reply.session = &session_key; 480 enc_tkt_reply.client = header_ticket->enc_part2->client; 481 enc_tkt_reply.transited.tr_type = KRB5_DOMAIN_X500_COMPRESS; 482 enc_tkt_reply.transited.tr_contents = empty_string; /* equivalent of "" */ 483 484 /* 485 * Only add the realm of the presented tgt to the transited list if 486 * it is different than the local realm (cross-realm) and it is different 487 * than the realm of the client (since the realm of the client is already 488 * implicitly part of the transited list and should not be explicitly 489 * listed). 490 */ 491 492 /* realm compare is like strcmp, but knows how to deal with these args */ 493 if (realm_compare(header_ticket->server, tgs_server) || 494 realm_compare(header_ticket->server, enc_tkt_reply.client)) { 495 /* tgt issued by local realm or issued by realm of client */ 496 enc_tkt_reply.transited = header_ticket->enc_part2->transited; 497 } else { 498 /* tgt issued by some other realm and not the realm of the client */ 499 /* assemble new transited field into allocated storage */ 500 if (header_ticket->enc_part2->transited.tr_type != 501 KRB5_DOMAIN_X500_COMPRESS) { 502 status = "BAD_TRTYPE"; 503 errcode = KRB5KDC_ERR_TRTYPE_NOSUPP; 504 goto cleanup; 505 } 506 enc_tkt_transited.tr_type = KRB5_DOMAIN_X500_COMPRESS; 507 enc_tkt_transited.tr_contents.data = 0; 508 enc_tkt_transited.tr_contents.length = 0; 509 enc_tkt_reply.transited = enc_tkt_transited; 510 if ((errcode = 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, 515 request->server))) { 516 status = "ADD_TR_FAIL"; 517 goto cleanup; 518 } 519 newtransited = 1; 520 } 521 if (!isflagset (request->kdc_options, KDC_OPT_DISABLE_TRANSITED_CHECK)) { 522 errcode = krb5_check_transited_list (kdc_context, 523 &enc_tkt_reply.transited.tr_contents, 524 krb5_princ_realm (kdc_context, header_ticket->enc_part2->client), 525 krb5_princ_realm (kdc_context, request->server)); 526 if (errcode == 0) { 527 setflag (enc_tkt_reply.flags, TKT_FLG_TRANSIT_POLICY_CHECKED); 528 } else if (errcode == KRB5KRB_AP_ERR_ILL_CR_TKT) 529 krb5_klog_syslog (LOG_INFO, 530 "bad realm transit path from '%s' to '%s' via '%.*s'", 531 cname ? cname : "<unknown client>", 532 sname ? sname : "<unknown server>", 533 enc_tkt_reply.transited.tr_contents.length, 534 enc_tkt_reply.transited.tr_contents.data); 535 else 536 krb5_klog_syslog (LOG_ERR, 537 "unexpected error checking transit from '%s' to '%s' via '%.*s': %s", 538 cname ? cname : "<unknown client>", 539 sname ? sname : "<unknown server>", 540 enc_tkt_reply.transited.tr_contents.length, 541 enc_tkt_reply.transited.tr_contents.data, 542 error_message (errcode)); 543 } else 544 krb5_klog_syslog (LOG_INFO, "not checking transit path"); 545 if (reject_bad_transit 546 && !isflagset (enc_tkt_reply.flags, TKT_FLG_TRANSIT_POLICY_CHECKED)) { 547 errcode = KRB5KDC_ERR_POLICY; 548 status = "BAD_TRANSIT"; 549 goto cleanup; 550 } 551 552 ticket_reply.enc_part2 = &enc_tkt_reply; 553 554 /* 555 * If we are doing user-to-user authentication, then make sure 556 * that the client for the second ticket matches the request 557 * server, and then encrypt the ticket using the session key of 558 * the second ticket. 559 */ 560 if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY)) { 561 /* 562 * Make sure the client for the second ticket matches 563 * requested server. 564 */ 565 krb5_enc_tkt_part *t2enc = request->second_ticket[st_idx]->enc_part2; 566 krb5_principal client2 = t2enc->client; 567 if (!krb5_principal_compare(kdc_context, request->server, client2)) { 568 if ((errcode = krb5_unparse_name(kdc_context, client2, &tmp))) 569 tmp = 0; 570 audit_krb5kdc_tgs_req_2ndtktmm( 571 (struct in_addr *)from->address->contents, 572 (in_port_t)from->port, 573 0, cname, sname); 574 krb5_klog_syslog(LOG_INFO, 575 "TGS_REQ %s: 2ND_TKT_MISMATCH: " 576 "authtime %d, %s for %s, 2nd tkt client %s", 577 fromstring, authtime, 578 cname ? cname : "<unknown client>", 579 sname ? sname : "<unknown server>", 580 tmp ? tmp : "<unknown>"); 581 errcode = KRB5KDC_ERR_SERVER_NOMATCH; 582 goto cleanup; 583 } 584 585 ticket_reply.enc_part.kvno = 0; 586 ticket_reply.enc_part.enctype = t2enc->session->enctype; 587 if ((errcode = krb5_encrypt_tkt_part(kdc_context, t2enc->session, 588 &ticket_reply))) { 589 status = "2ND_TKT_ENCRYPT"; 590 goto cleanup; 591 } 592 st_idx++; 593 } else { 594 /* 595 * Find the server key 596 */ 597 if ((errcode = krb5_dbe_find_enctype(kdc_context, &server, 598 -1, /* ignore keytype */ 599 -1, /* Ignore salttype */ 600 0, /* Get highest kvno */ 601 &server_key))) { 602 status = "FINDING_SERVER_KEY"; 603 goto cleanup; 604 } 605 /* convert server.key into a real key (it may be encrypted 606 * in the database) */ 607 if ((errcode = krb5_dbekd_decrypt_key_data(kdc_context, 608 &master_keyblock, 609 server_key, &encrypting_key, 610 NULL))) { 611 status = "DECRYPT_SERVER_KEY"; 612 goto cleanup; 613 } 614 errcode = krb5_encrypt_tkt_part(kdc_context, &encrypting_key, 615 &ticket_reply); 616 krb5_free_keyblock_contents(kdc_context, &encrypting_key); 617 if (errcode) { 618 status = "TKT_ENCRYPT"; 619 goto cleanup; 620 } 621 ticket_reply.enc_part.kvno = server_key->key_data_kvno; 622 } 623 624 /* Start assembling the response */ 625 reply.msg_type = KRB5_TGS_REP; 626 reply.padata = 0; /* always */ 627 reply.client = header_ticket->enc_part2->client; 628 reply.enc_part.kvno = 0; /* We are using the session key */ 629 reply.ticket = &ticket_reply; 630 631 reply_encpart.session = &session_key; 632 reply_encpart.nonce = request->nonce; 633 634 /* copy the time fields EXCEPT for authtime; its location 635 is used for ktime */ 636 reply_encpart.times = enc_tkt_reply.times; 637 reply_encpart.times.authtime = header_ticket->enc_part2->times.authtime; 638 639 /* starttime is optional, and treated as authtime if not present. 640 so we can nuke it if it matches */ 641 if (enc_tkt_reply.times.starttime == enc_tkt_reply.times.authtime) 642 enc_tkt_reply.times.starttime = 0; 643 644 nolrentry.lr_type = KRB5_LRQ_NONE; 645 nolrentry.value = 0; 646 nolrarray[0] = &nolrentry; 647 nolrarray[1] = 0; 648 reply_encpart.last_req = nolrarray; /* not available for TGS reqs */ 649 reply_encpart.key_exp = 0; /* ditto */ 650 reply_encpart.flags = enc_tkt_reply.flags; 651 reply_encpart.server = ticket_reply.server; 652 653 /* use the session key in the ticket, unless there's a subsession key 654 in the AP_REQ */ 655 656 reply.enc_part.enctype = subkey ? subkey->enctype : 657 header_ticket->enc_part2->session->enctype; 658 errcode = krb5_encode_kdc_rep(kdc_context, KRB5_TGS_REP, &reply_encpart, 659 subkey ? 1 : 0, 660 subkey ? subkey : 661 header_ticket->enc_part2->session, 662 &reply, response); 663 if (errcode) { 664 status = "ENCODE_KDC_REP"; 665 } else { 666 status = "ISSUE"; 667 } 668 669 if (ticket_reply.enc_part.ciphertext.data) { 670 memset(ticket_reply.enc_part.ciphertext.data, 0, 671 ticket_reply.enc_part.ciphertext.length); 672 free(ticket_reply.enc_part.ciphertext.data); 673 ticket_reply.enc_part.ciphertext.data = NULL; 674 } 675 /* these parts are left on as a courtesy from krb5_encode_kdc_rep so we 676 can use them in raw form if needed. But, we don't... */ 677 if (reply.enc_part.ciphertext.data) { 678 memset(reply.enc_part.ciphertext.data, 0, 679 reply.enc_part.ciphertext.length); 680 free(reply.enc_part.ciphertext.data); 681 reply.enc_part.ciphertext.data = NULL; 682 } 683 684 cleanup: 685 if (status) { 686 audit_krb5kdc_tgs_req((struct in_addr *)from->address->contents, 687 (in_port_t)from->port, 0, 688 cname ? cname : "<unknown client>", 689 sname ? sname : "<unknown client>", 690 errcode); 691 if (!errcode) 692 rep_etypes2str(rep_etypestr, sizeof(rep_etypestr), &reply); 693 krb5_klog_syslog(LOG_INFO, 694 "TGS_REQ (%s) %s: %s: authtime %d, " 695 "%s%s %s for %s%s%s", 696 ktypestr, 697 fromstring, status, authtime, 698 !errcode ? rep_etypestr : "", 699 !errcode ? "," : "", 700 cname ? cname : "<unknown client>", 701 sname ? sname : "<unknown server>", 702 errcode ? ", " : "", 703 errcode ? error_message(errcode) : ""); 704 } 705 706 if (errcode) { 707 if (status == 0) 708 status = error_message (errcode); 709 errcode -= ERROR_TABLE_BASE_krb5; 710 if (errcode < 0 || errcode > 128) 711 errcode = KRB_ERR_GENERIC; 712 713 retval = prepare_error_tgs(request, header_ticket, errcode, 714 fromstring, response, status); 715 } 716 717 if (header_ticket) 718 krb5_free_ticket(kdc_context, header_ticket); 719 if (request) 720 krb5_free_kdc_req(kdc_context, request); 721 if (cname) 722 free(cname); 723 if (sname) 724 free(sname); 725 if (nprincs) 726 krb5_db_free_principal(kdc_context, &server, 1); 727 if (session_key.contents) 728 krb5_free_keyblock_contents(kdc_context, &session_key); 729 if (newtransited) 730 free(enc_tkt_reply.transited.tr_contents.data); 731 732 return retval; 733 } 734 735 static krb5_error_code 736 prepare_error_tgs (krb5_kdc_req *request, krb5_ticket *ticket, int error, 737 const char *ident, krb5_data **response, const char *status) 738 { 739 krb5_error errpkt; 740 krb5_error_code retval; 741 krb5_data *scratch; 742 743 errpkt.ctime = request->nonce; 744 errpkt.cusec = 0; 745 746 if ((retval = krb5_us_timeofday(kdc_context, &errpkt.stime, 747 &errpkt.susec))) 748 return(retval); 749 errpkt.error = error; 750 errpkt.server = request->server; 751 if (ticket && ticket->enc_part2) 752 errpkt.client = ticket->enc_part2->client; 753 else 754 errpkt.client = 0; 755 errpkt.text.length = strlen(status) + 1; 756 if (!(errpkt.text.data = malloc(errpkt.text.length))) 757 return ENOMEM; 758 (void) strcpy(errpkt.text.data, status); 759 760 if (!(scratch = (krb5_data *)malloc(sizeof(*scratch)))) { 761 free(errpkt.text.data); 762 return ENOMEM; 763 } 764 errpkt.e_data.length = 0; 765 errpkt.e_data.data = 0; 766 767 retval = krb5_mk_error(kdc_context, &errpkt, scratch); 768 free(errpkt.text.data); 769 if (retval) 770 free(scratch); 771 else 772 *response = scratch; 773 774 return retval; 775 } 776 777 /* 778 * The request seems to be for a ticket-granting service somewhere else, 779 * but we don't have a ticket for the final TGS. Try to give the requestor 780 * some intermediate realm. 781 */ 782 static void 783 find_alternate_tgs(krb5_kdc_req *request, krb5_db_entry *server, 784 krb5_boolean *more, int *nprincs, 785 const krb5_fulladdr *from, char *cname) 786 { 787 krb5_error_code retval; 788 krb5_principal *plist, *pl2; 789 krb5_data tmp; 790 791 *nprincs = 0; 792 *more = FALSE; 793 794 /* 795 * Call to krb5_princ_component is normally not safe but is so 796 * here only because find_alternate_tgs() is only called from 797 * somewhere that has already checked the number of components in 798 * the principal. 799 */ 800 if ((retval = krb5_walk_realm_tree(kdc_context, 801 krb5_princ_realm(kdc_context, request->server), 802 krb5_princ_component(kdc_context, request->server, 1), 803 &plist, KRB5_REALM_BRANCH_CHAR))) 804 return; 805 806 /* move to the end */ 807 for (pl2 = plist; *pl2; pl2++); 808 809 /* the first entry in this array is for krbtgt/local@local, so we 810 ignore it */ 811 while (--pl2 > plist) { 812 *nprincs = 1; 813 tmp = *krb5_princ_realm(kdc_context, *pl2); 814 krb5_princ_set_realm(kdc_context, *pl2, 815 krb5_princ_realm(kdc_context, tgs_server)); 816 retval = krb5_db_get_principal(kdc_context, *pl2, server, nprincs, more); 817 krb5_princ_set_realm(kdc_context, *pl2, &tmp); 818 if (retval) { 819 *nprincs = 0; 820 *more = FALSE; 821 krb5_free_realm_tree(kdc_context, plist); 822 return; 823 } 824 if (*more) { 825 krb5_db_free_principal(kdc_context, server, *nprincs); 826 continue; 827 } else if (*nprincs == 1) { 828 /* Found it! */ 829 krb5_principal tmpprinc; 830 char *sname; 831 832 tmp = *krb5_princ_realm(kdc_context, *pl2); 833 krb5_princ_set_realm(kdc_context, *pl2, 834 krb5_princ_realm(kdc_context, tgs_server)); 835 if ((retval = krb5_copy_principal(kdc_context, *pl2, &tmpprinc))) { 836 krb5_db_free_principal(kdc_context, server, *nprincs); 837 krb5_princ_set_realm(kdc_context, *pl2, &tmp); 838 continue; 839 } 840 krb5_princ_set_realm(kdc_context, *pl2, &tmp); 841 842 krb5_free_principal(kdc_context, request->server); 843 request->server = tmpprinc; 844 if (krb5_unparse_name(kdc_context, request->server, &sname)) { 845 846 audit_krb5kdc_tgs_req_alt_tgt( 847 (struct in_addr *)from->address->contents, 848 (in_port_t)from->port, 849 0, cname, "<unparseable>", 0); 850 krb5_klog_syslog(LOG_INFO, 851 "TGS_REQ: issuing alternate <un-unparseable> TGT"); 852 } else { 853 audit_krb5kdc_tgs_req_alt_tgt( 854 (struct in_addr *)from->address->contents, 855 (in_port_t)from->port, 856 0, cname, sname, 0); 857 krb5_klog_syslog(LOG_INFO, 858 "TGS_REQ: issuing TGT %s", sname); 859 free(sname); 860 } 861 return; 862 } 863 krb5_db_free_principal(kdc_context, server, *nprincs); 864 continue; 865 } 866 867 *nprincs = 0; 868 *more = FALSE; 869 krb5_free_realm_tree(kdc_context, plist); 870 return; 871 } 872