1 /* 2 * Copyright 2007 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 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 krb5_klog_syslog (LOG_ERR, 546 "unexpected error checking transit from " 547 "'%s' to '%s' via '%.*s%s': %s", 548 cname ? cname : "<unknown client>", 549 sname ? sname : "<unknown server>", 550 tlen, 551 enc_tkt_reply.transited.tr_contents.data, 552 tdots, error_message (errcode)); 553 } else 554 krb5_klog_syslog (LOG_INFO, "not checking transit path"); 555 if (reject_bad_transit 556 && !isflagset (enc_tkt_reply.flags, TKT_FLG_TRANSIT_POLICY_CHECKED)) { 557 errcode = KRB5KDC_ERR_POLICY; 558 status = "BAD_TRANSIT"; 559 goto cleanup; 560 } 561 562 ticket_reply.enc_part2 = &enc_tkt_reply; 563 564 /* 565 * If we are doing user-to-user authentication, then make sure 566 * that the client for the second ticket matches the request 567 * server, and then encrypt the ticket using the session key of 568 * the second ticket. 569 */ 570 if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY)) { 571 /* 572 * Make sure the client for the second ticket matches 573 * requested server. 574 */ 575 krb5_enc_tkt_part *t2enc = request->second_ticket[st_idx]->enc_part2; 576 krb5_principal client2 = t2enc->client; 577 if (!krb5_principal_compare(kdc_context, request->server, client2)) { 578 if ((errcode = krb5_unparse_name(kdc_context, client2, &tmp))) 579 tmp = 0; 580 if (tmp != NULL) 581 limit_string(tmp); 582 audit_krb5kdc_tgs_req_2ndtktmm( 583 (struct in_addr *)from->address->contents, 584 (in_port_t)from->port, 585 0, cname, sname); 586 krb5_klog_syslog(LOG_INFO, 587 "TGS_REQ %s: 2ND_TKT_MISMATCH: " 588 "authtime %d, %s for %s, 2nd tkt client %s", 589 fromstring, authtime, 590 cname ? cname : "<unknown client>", 591 sname ? sname : "<unknown server>", 592 tmp ? tmp : "<unknown>"); 593 errcode = KRB5KDC_ERR_SERVER_NOMATCH; 594 goto cleanup; 595 } 596 597 ticket_reply.enc_part.kvno = 0; 598 ticket_reply.enc_part.enctype = t2enc->session->enctype; 599 if ((errcode = krb5_encrypt_tkt_part(kdc_context, t2enc->session, 600 &ticket_reply))) { 601 status = "2ND_TKT_ENCRYPT"; 602 goto cleanup; 603 } 604 st_idx++; 605 } else { 606 /* 607 * Find the server key 608 */ 609 if ((errcode = krb5_dbe_find_enctype(kdc_context, &server, 610 -1, /* ignore keytype */ 611 -1, /* Ignore salttype */ 612 0, /* Get highest kvno */ 613 &server_key))) { 614 status = "FINDING_SERVER_KEY"; 615 goto cleanup; 616 } 617 /* convert server.key into a real key (it may be encrypted 618 * in the database) */ 619 if ((errcode = krb5_dbekd_decrypt_key_data(kdc_context, 620 &master_keyblock, 621 server_key, &encrypting_key, 622 NULL))) { 623 status = "DECRYPT_SERVER_KEY"; 624 goto cleanup; 625 } 626 errcode = krb5_encrypt_tkt_part(kdc_context, &encrypting_key, 627 &ticket_reply); 628 krb5_free_keyblock_contents(kdc_context, &encrypting_key); 629 if (errcode) { 630 status = "TKT_ENCRYPT"; 631 goto cleanup; 632 } 633 ticket_reply.enc_part.kvno = server_key->key_data_kvno; 634 } 635 636 /* Start assembling the response */ 637 reply.msg_type = KRB5_TGS_REP; 638 reply.padata = 0; /* always */ 639 reply.client = header_ticket->enc_part2->client; 640 reply.enc_part.kvno = 0; /* We are using the session key */ 641 reply.ticket = &ticket_reply; 642 643 reply_encpart.session = &session_key; 644 reply_encpart.nonce = request->nonce; 645 646 /* copy the time fields EXCEPT for authtime; its location 647 is used for ktime */ 648 reply_encpart.times = enc_tkt_reply.times; 649 reply_encpart.times.authtime = header_ticket->enc_part2->times.authtime; 650 651 /* starttime is optional, and treated as authtime if not present. 652 so we can nuke it if it matches */ 653 if (enc_tkt_reply.times.starttime == enc_tkt_reply.times.authtime) 654 enc_tkt_reply.times.starttime = 0; 655 656 nolrentry.lr_type = KRB5_LRQ_NONE; 657 nolrentry.value = 0; 658 nolrarray[0] = &nolrentry; 659 nolrarray[1] = 0; 660 reply_encpart.last_req = nolrarray; /* not available for TGS reqs */ 661 reply_encpart.key_exp = 0; /* ditto */ 662 reply_encpart.flags = enc_tkt_reply.flags; 663 reply_encpart.server = ticket_reply.server; 664 665 /* use the session key in the ticket, unless there's a subsession key 666 in the AP_REQ */ 667 668 reply.enc_part.enctype = subkey ? subkey->enctype : 669 header_ticket->enc_part2->session->enctype; 670 errcode = krb5_encode_kdc_rep(kdc_context, KRB5_TGS_REP, &reply_encpart, 671 subkey ? 1 : 0, 672 subkey ? subkey : 673 header_ticket->enc_part2->session, 674 &reply, response); 675 if (errcode) { 676 status = "ENCODE_KDC_REP"; 677 } else { 678 status = "ISSUE"; 679 } 680 681 if (ticket_reply.enc_part.ciphertext.data) { 682 memset(ticket_reply.enc_part.ciphertext.data, 0, 683 ticket_reply.enc_part.ciphertext.length); 684 free(ticket_reply.enc_part.ciphertext.data); 685 ticket_reply.enc_part.ciphertext.data = NULL; 686 } 687 /* these parts are left on as a courtesy from krb5_encode_kdc_rep so we 688 can use them in raw form if needed. But, we don't... */ 689 if (reply.enc_part.ciphertext.data) { 690 memset(reply.enc_part.ciphertext.data, 0, 691 reply.enc_part.ciphertext.length); 692 free(reply.enc_part.ciphertext.data); 693 reply.enc_part.ciphertext.data = NULL; 694 } 695 696 cleanup: 697 if (status) { 698 audit_krb5kdc_tgs_req((struct in_addr *)from->address->contents, 699 (in_port_t)from->port, 0, 700 cname ? cname : "<unknown client>", 701 sname ? sname : "<unknown client>", 702 errcode); 703 if (!errcode) 704 rep_etypes2str(rep_etypestr, sizeof(rep_etypestr), &reply); 705 krb5_klog_syslog(LOG_INFO, 706 "TGS_REQ (%s) %s: %s: authtime %d, " 707 "%s%s %s for %s%s%s", 708 ktypestr, 709 fromstring, status, authtime, 710 !errcode ? rep_etypestr : "", 711 !errcode ? "," : "", 712 cname ? cname : "<unknown client>", 713 sname ? sname : "<unknown server>", 714 errcode ? ", " : "", 715 errcode ? error_message(errcode) : ""); 716 } 717 718 if (errcode) { 719 if (status == 0) 720 status = error_message (errcode); 721 errcode -= ERROR_TABLE_BASE_krb5; 722 if (errcode < 0 || errcode > 128) 723 errcode = KRB_ERR_GENERIC; 724 725 retval = prepare_error_tgs(request, header_ticket, errcode, 726 fromstring, response, status); 727 } 728 729 if (header_ticket) 730 krb5_free_ticket(kdc_context, header_ticket); 731 if (request) 732 krb5_free_kdc_req(kdc_context, request); 733 if (cname) 734 free(cname); 735 if (sname) 736 free(sname); 737 if (nprincs) 738 krb5_db_free_principal(kdc_context, &server, 1); 739 if (session_key.contents) 740 krb5_free_keyblock_contents(kdc_context, &session_key); 741 if (newtransited) 742 free(enc_tkt_reply.transited.tr_contents.data); 743 744 return retval; 745 } 746 747 static krb5_error_code 748 prepare_error_tgs (krb5_kdc_req *request, krb5_ticket *ticket, int error, 749 const char *ident, krb5_data **response, const char *status) 750 { 751 krb5_error errpkt; 752 krb5_error_code retval; 753 krb5_data *scratch; 754 755 errpkt.ctime = request->nonce; 756 errpkt.cusec = 0; 757 758 if ((retval = krb5_us_timeofday(kdc_context, &errpkt.stime, 759 &errpkt.susec))) 760 return(retval); 761 errpkt.error = error; 762 errpkt.server = request->server; 763 if (ticket && ticket->enc_part2) 764 errpkt.client = ticket->enc_part2->client; 765 else 766 errpkt.client = 0; 767 errpkt.text.length = strlen(status) + 1; 768 if (!(errpkt.text.data = malloc(errpkt.text.length))) 769 return ENOMEM; 770 (void) strcpy(errpkt.text.data, status); 771 772 if (!(scratch = (krb5_data *)malloc(sizeof(*scratch)))) { 773 free(errpkt.text.data); 774 return ENOMEM; 775 } 776 errpkt.e_data.length = 0; 777 errpkt.e_data.data = 0; 778 779 retval = krb5_mk_error(kdc_context, &errpkt, scratch); 780 free(errpkt.text.data); 781 if (retval) 782 free(scratch); 783 else 784 *response = scratch; 785 786 return retval; 787 } 788 789 /* 790 * The request seems to be for a ticket-granting service somewhere else, 791 * but we don't have a ticket for the final TGS. Try to give the requestor 792 * some intermediate realm. 793 */ 794 static void 795 find_alternate_tgs(krb5_kdc_req *request, krb5_db_entry *server, 796 krb5_boolean *more, int *nprincs, 797 const krb5_fulladdr *from, char *cname) 798 { 799 krb5_error_code retval; 800 krb5_principal *plist, *pl2; 801 krb5_data tmp; 802 803 *nprincs = 0; 804 *more = FALSE; 805 806 /* 807 * Call to krb5_princ_component is normally not safe but is so 808 * here only because find_alternate_tgs() is only called from 809 * somewhere that has already checked the number of components in 810 * the principal. 811 */ 812 if ((retval = krb5_walk_realm_tree(kdc_context, 813 krb5_princ_realm(kdc_context, request->server), 814 krb5_princ_component(kdc_context, request->server, 1), 815 &plist, KRB5_REALM_BRANCH_CHAR))) 816 return; 817 818 /* move to the end */ 819 for (pl2 = plist; *pl2; pl2++); 820 821 /* the first entry in this array is for krbtgt/local@local, so we 822 ignore it */ 823 while (--pl2 > plist) { 824 *nprincs = 1; 825 tmp = *krb5_princ_realm(kdc_context, *pl2); 826 krb5_princ_set_realm(kdc_context, *pl2, 827 krb5_princ_realm(kdc_context, tgs_server)); 828 retval = krb5_db_get_principal(kdc_context, *pl2, server, nprincs, more); 829 krb5_princ_set_realm(kdc_context, *pl2, &tmp); 830 if (retval) { 831 *nprincs = 0; 832 *more = FALSE; 833 krb5_free_realm_tree(kdc_context, plist); 834 return; 835 } 836 if (*more) { 837 krb5_db_free_principal(kdc_context, server, *nprincs); 838 continue; 839 } else if (*nprincs == 1) { 840 /* Found it! */ 841 krb5_principal tmpprinc; 842 char *sname; 843 844 tmp = *krb5_princ_realm(kdc_context, *pl2); 845 krb5_princ_set_realm(kdc_context, *pl2, 846 krb5_princ_realm(kdc_context, tgs_server)); 847 if ((retval = krb5_copy_principal(kdc_context, *pl2, &tmpprinc))) { 848 krb5_db_free_principal(kdc_context, server, *nprincs); 849 krb5_princ_set_realm(kdc_context, *pl2, &tmp); 850 continue; 851 } 852 krb5_princ_set_realm(kdc_context, *pl2, &tmp); 853 854 krb5_free_principal(kdc_context, request->server); 855 request->server = tmpprinc; 856 if (krb5_unparse_name(kdc_context, request->server, &sname)) { 857 858 audit_krb5kdc_tgs_req_alt_tgt( 859 (struct in_addr *)from->address->contents, 860 (in_port_t)from->port, 861 0, cname, "<unparseable>", 0); 862 krb5_klog_syslog(LOG_INFO, 863 "TGS_REQ: issuing alternate <un-unparseable> TGT"); 864 } else { 865 limit_string(sname); 866 audit_krb5kdc_tgs_req_alt_tgt( 867 (struct in_addr *)from->address->contents, 868 (in_port_t)from->port, 869 0, cname, sname, 0); 870 krb5_klog_syslog(LOG_INFO, 871 "TGS_REQ: issuing TGT %s", sname); 872 free(sname); 873 } 874 return; 875 } 876 krb5_db_free_principal(kdc_context, server, *nprincs); 877 continue; 878 } 879 880 *nprincs = 0; 881 *more = FALSE; 882 krb5_free_realm_tree(kdc_context, plist); 883 return; 884 } 885