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