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