1 /* 2 * $Source: /mit/krb5/.cvsroot/src/appl/bsd/forward.c,v $ 3 * $Id: forward.c,v 1.4 1993/10/15 16:41:29 tytso Exp $ 4 */ 5 6 #ifndef lint 7 static char *rcsid_forward_c = 8 "$Id: forward.c,v 1.4 1993/10/15 16:41:29 tytso Exp $"; 9 #endif /* lint */ 10 #define LIBC_SCCS 11 12 /*- 13 * Copyright (c) 1993 14 * The Regents of the University of California. All rights reserved. 15 * 16 * %sccs.include.redist.c% 17 */ 18 19 #ifndef lint 20 static char sccsid[] = "@(#)forward.c 8.1 (Berkeley) 03/24/95"; 21 #endif /* not lint */ 22 23 24 /* General-purpose forwarding routines. These routines may be put into */ 25 /* libkrb5.a to allow widespread use */ 26 27 #if defined(KRB5) && defined(FORWARD) 28 #include <stdio.h> 29 #include <pwd.h> 30 #include <netdb.h> 31 32 #include <krb5/krb5.h> 33 #include <krb5/asn1.h> 34 #include <krb5/crc-32.h> 35 #include <krb5/los-proto.h> 36 #include <krb5/ext-proto.h> 37 38 #define KRB5_DEFAULT_LIFE 60*60*8 /* 8 hours */ 39 /* helper function: convert flags to necessary KDC options */ 40 #define flags2options(flags) (flags & KDC_TKT_COMMON_MASK) 41 42 43 44 /* Get a TGT for use at the remote host */ 45 krb5_error_code 46 get_for_creds(etype, sumtype, rhost, client, enc_key, forwardable, outbuf) 47 const krb5_enctype etype; 48 const krb5_cksumtype sumtype; 49 char *rhost; 50 krb5_principal client; 51 krb5_keyblock *enc_key; 52 int forwardable; /* Should forwarded TGT also be forwardable? */ 53 krb5_data *outbuf; 54 { 55 struct hostent *hp; 56 krb5_address **addrs; 57 krb5_error_code retval; 58 krb5_data *scratch; 59 krb5_kdc_rep *dec_rep; 60 krb5_error *err_reply; 61 krb5_response tgsrep; 62 krb5_creds creds, tgt; 63 krb5_ccache cc; 64 krb5_flags kdcoptions; 65 krb5_timestamp now; 66 char *remote_host; 67 char **hrealms; 68 int i; 69 70 if (!rhost || !(hp = gethostbyname(rhost))) 71 return KRB5_ERR_BAD_HOSTNAME; 72 73 remote_host = (char *) malloc(strlen(hp->h_name)+1); 74 if (!remote_host) 75 return ENOMEM; 76 strcpy(remote_host, hp->h_name); 77 78 if (retval = krb5_get_host_realm(remote_host, &hrealms)) { 79 free(remote_host); 80 return retval; 81 } 82 if (!hrealms[0]) { 83 free(remote_host); 84 krb5_xfree(hrealms); 85 return KRB5_ERR_HOST_REALM_UNKNOWN; 86 } 87 88 /* Count elements */ 89 for(i=0; hp->h_addr_list[i]; i++); 90 91 addrs = (krb5_address **) malloc ((i+1)*sizeof(*addrs)); 92 if (!addrs) 93 return ENOMEM; 94 95 for(i=0; hp->h_addr_list[i]; i++) { 96 addrs[i] = (krb5_address *) malloc(sizeof(krb5_address)); 97 if (addrs[i]) { 98 addrs[i]->addrtype = hp->h_addrtype; 99 addrs[i]->length = hp->h_length; 100 addrs[i]->contents = (unsigned char *)malloc(addrs[i]->length); 101 if (!addrs[i]->contents) { 102 krb5_free_addresses(addrs); 103 return ENOMEM; 104 } 105 else 106 memcpy ((char *)addrs[i]->contents, hp->h_addr_list[i], 107 addrs[i]->length); 108 } 109 else { 110 return ENOMEM; 111 } 112 } 113 addrs[i] = 0; 114 115 memset((char *)&creds, 0, sizeof(creds)); 116 if (retval = krb5_copy_principal(client, &creds.client)) 117 return retval; 118 119 if (retval = krb5_build_principal_ext(&creds.server, 120 strlen(hrealms[0]), 121 hrealms[0], 122 KRB5_TGS_NAME_SIZE, 123 KRB5_TGS_NAME, 124 client->realm.length, 125 client->realm.data, 126 0)) 127 return retval; 128 129 creds.times.starttime = 0; 130 if (retval = krb5_timeofday(&now)) { 131 return retval; 132 } 133 creds.times.endtime = now + KRB5_DEFAULT_LIFE; 134 creds.times.renew_till = 0; 135 136 if (retval = krb5_cc_default(&cc)) { 137 return retval; 138 } 139 140 /* fetch tgt directly from cache */ 141 if (retval = krb5_cc_retrieve_cred (cc, 142 KRB5_TC_MATCH_SRV_NAMEONLY, 143 &creds, 144 &tgt)) { 145 return retval; 146 } 147 148 /* tgt->client must be equal to creds.client */ 149 if (!krb5_principal_compare(tgt.client, creds.client)) 150 return KRB5_PRINC_NOMATCH; 151 152 if (!tgt.ticket.length) 153 return(KRB5_NO_TKT_SUPPLIED); 154 155 kdcoptions = flags2options(tgt.ticket_flags)|KDC_OPT_FORWARDED; 156 157 if (!forwardable) /* Reset KDC_OPT_FORWARDABLE */ 158 kdcoptions &= ~(KDC_OPT_FORWARDABLE); 159 160 if (retval = krb5_send_tgs(kdcoptions, &creds.times, etype, sumtype, 161 creds.server, 162 addrs, 163 creds.authdata, 164 0, /* no padata */ 165 0, /* no second ticket */ 166 &tgt, &tgsrep)) 167 return retval; 168 169 #undef cleanup 170 #define cleanup() free(tgsrep.response.data) 171 172 switch (tgsrep.message_type) { 173 case KRB5_TGS_REP: 174 break; 175 case KRB5_ERROR: 176 default: 177 if (!krb5_is_krb_error(&tgsrep.response)) { 178 retval = KRB5KRB_AP_ERR_MSG_TYPE; 179 } else 180 retval = decode_krb5_error(&tgsrep.response, &err_reply); 181 if (retval) { 182 cleanup(); 183 return retval; /* neither proper reply nor error! */ 184 } 185 186 retval = err_reply->error + ERROR_TABLE_BASE_krb5; 187 188 krb5_free_error(err_reply); 189 cleanup(); 190 return retval; 191 } 192 retval = krb5_decode_kdc_rep(&tgsrep.response, 193 &tgt.keyblock, 194 etype, /* enctype */ 195 &dec_rep); 196 197 cleanup(); 198 if (retval) 199 return retval; 200 #undef cleanup 201 #define cleanup() {\ 202 memset((char *)dec_rep->enc_part2->session->contents, 0,\ 203 dec_rep->enc_part2->session->length);\ 204 krb5_free_kdc_rep(dec_rep); } 205 206 if (dec_rep->msg_type != KRB5_TGS_REP) { 207 retval = KRB5KRB_AP_ERR_MSG_TYPE; 208 cleanup(); 209 return retval; 210 } 211 212 /* now it's decrypted and ready for prime time */ 213 214 if (!krb5_principal_compare(dec_rep->client, tgt.client)) { 215 cleanup(); 216 return KRB5_KDCREP_MODIFIED; 217 } 218 219 if (retval = mk_cred(dec_rep, 220 etype, 221 enc_key, 222 0, 223 0, 224 outbuf)) 225 return retval; 226 227 krb5_free_kdc_rep(dec_rep); 228 229 return retval; 230 #undef cleanup 231 } 232 233 234 235 /* Create asn.1 encoded KRB-CRED message from the kdc reply. */ 236 krb5_error_code 237 mk_cred(dec_rep, etype, key, sender_addr, recv_addr, outbuf) 238 krb5_kdc_rep *dec_rep; 239 krb5_enctype etype; 240 krb5_keyblock *key; 241 krb5_address *sender_addr; 242 krb5_address *recv_addr; 243 krb5_data *outbuf; 244 { 245 krb5_error_code retval; 246 krb5_encrypt_block eblock; 247 krb5_cred ret_cred; 248 krb5_cred_enc_part cred_enc_part; 249 krb5_data *scratch; 250 251 if (!valid_etype(etype)) 252 return KRB5_PROG_ETYPE_NOSUPP; 253 254 ret_cred.tickets = (krb5_ticket **) calloc(2, sizeof(*ret_cred.tickets)); 255 if (!ret_cred.tickets) 256 return ENOMEM; 257 ret_cred.tickets[0] = dec_rep->ticket; 258 ret_cred.tickets[1] = 0; 259 260 ret_cred.enc_part.etype = etype; 261 ret_cred.enc_part.kvno = 0; 262 263 cred_enc_part.ticket_info = (krb5_cred_info **) 264 calloc(2, sizeof(*cred_enc_part.ticket_info)); 265 if (!cred_enc_part.ticket_info) { 266 krb5_free_tickets(ret_cred.tickets); 267 return ENOMEM; 268 } 269 cred_enc_part.ticket_info[0] = (krb5_cred_info *) 270 malloc(sizeof(*cred_enc_part.ticket_info[0])); 271 if (!cred_enc_part.ticket_info[0]) { 272 krb5_free_tickets(ret_cred.tickets); 273 krb5_free_cred_enc_part(cred_enc_part); 274 return ENOMEM; 275 } 276 cred_enc_part.nonce = 0; 277 278 if (retval = krb5_us_timeofday(&cred_enc_part.timestamp, 279 &cred_enc_part.usec)) 280 return retval; 281 282 cred_enc_part.s_address = (krb5_address *)sender_addr; 283 cred_enc_part.r_address = (krb5_address *)recv_addr; 284 285 cred_enc_part.ticket_info[0]->session = dec_rep->enc_part2->session; 286 cred_enc_part.ticket_info[0]->client = dec_rep->client; 287 cred_enc_part.ticket_info[0]->server = dec_rep->enc_part2->server; 288 cred_enc_part.ticket_info[0]->flags = dec_rep->enc_part2->flags; 289 cred_enc_part.ticket_info[0]->times = dec_rep->enc_part2->times; 290 cred_enc_part.ticket_info[0]->caddrs = dec_rep->enc_part2->caddrs; 291 292 cred_enc_part.ticket_info[1] = 0; 293 294 /* start by encoding to-be-encrypted part of the message */ 295 296 if (retval = encode_krb5_enc_cred_part(&cred_enc_part, &scratch)) 297 return retval; 298 299 #define cleanup_scratch() { (void) memset(scratch->data, 0, scratch->length); krb5_free_data(scratch); } 300 301 /* put together an eblock for this encryption */ 302 303 krb5_use_cstype(&eblock, etype); 304 ret_cred.enc_part.ciphertext.length = krb5_encrypt_size(scratch->length, 305 eblock.crypto_entry); 306 /* add padding area, and zero it */ 307 if (!(scratch->data = realloc(scratch->data, 308 ret_cred.enc_part.ciphertext.length))) { 309 /* may destroy scratch->data */ 310 krb5_xfree(scratch); 311 return ENOMEM; 312 } 313 memset(scratch->data + scratch->length, 0, 314 ret_cred.enc_part.ciphertext.length - scratch->length); 315 if (!(ret_cred.enc_part.ciphertext.data = 316 malloc(ret_cred.enc_part.ciphertext.length))) { 317 retval = ENOMEM; 318 goto clean_scratch; 319 } 320 321 #define cleanup_encpart() {\ 322 (void) memset(ret_cred.enc_part.ciphertext.data, 0, \ 323 ret_cred.enc_part.ciphertext.length); \ 324 free(ret_cred.enc_part.ciphertext.data); \ 325 ret_cred.enc_part.ciphertext.length = 0; \ 326 ret_cred.enc_part.ciphertext.data = 0;} 327 328 /* do any necessary key pre-processing */ 329 if (retval = krb5_process_key(&eblock, key)) { 330 goto clean_encpart; 331 } 332 333 #define cleanup_prockey() {(void) krb5_finish_key(&eblock);} 334 335 /* call the encryption routine */ 336 if (retval = krb5_encrypt((krb5_pointer) scratch->data, 337 (krb5_pointer) 338 ret_cred.enc_part.ciphertext.data, 339 scratch->length, &eblock, 340 0)) { 341 goto clean_prockey; 342 } 343 344 /* private message is now assembled-- do some cleanup */ 345 cleanup_scratch(); 346 347 if (retval = krb5_finish_key(&eblock)) { 348 cleanup_encpart(); 349 return retval; 350 } 351 /* encode private message */ 352 if (retval = encode_krb5_cred(&ret_cred, &scratch)) { 353 cleanup_encpart(); 354 return retval; 355 } 356 357 cleanup_encpart(); 358 359 *outbuf = *scratch; 360 krb5_xfree(scratch); 361 return 0; 362 363 clean_prockey: 364 cleanup_prockey(); 365 clean_encpart: 366 cleanup_encpart(); 367 clean_scratch: 368 cleanup_scratch(); 369 370 return retval; 371 #undef cleanup_prockey 372 #undef cleanup_encpart 373 #undef cleanup_scratch 374 } 375 376 377 378 /* Decode, decrypt and store the forwarded creds in the local ccache. */ 379 krb5_error_code 380 rd_and_store_for_creds(inbuf, ticket, lusername) 381 krb5_data *inbuf; 382 krb5_ticket *ticket; 383 char *lusername; 384 { 385 krb5_encrypt_block eblock; 386 krb5_creds creds; 387 krb5_error_code retval; 388 char ccname[35]; 389 krb5_ccache ccache = NULL; 390 struct passwd *pwd; 391 392 if (retval = rd_cred(inbuf, ticket->enc_part2->session, 393 &creds, 0, 0)) { 394 return(retval); 395 } 396 397 if (!(pwd = (struct passwd *) getpwnam(lusername))) { 398 return -1; 399 } 400 401 sprintf(ccname, "FILE:/tmp/krb5cc_%d", pwd->pw_uid); 402 403 if (retval = krb5_cc_resolve(ccname, &ccache)) { 404 return(retval); 405 } 406 407 if (retval = krb5_cc_initialize(ccache, 408 ticket->enc_part2->client)) { 409 return(retval); 410 } 411 412 if (retval = krb5_cc_store_cred(ccache, &creds)) { 413 return(retval); 414 } 415 416 if (retval = chown(ccname+5, pwd->pw_uid, -1)) { 417 return(retval); 418 } 419 420 return retval; 421 } 422 423 424 425 extern krb5_deltat krb5_clockskew; 426 #define in_clock_skew(date) (abs((date)-currenttime) < krb5_clockskew) 427 428 /* Decode the KRB-CRED message, and return creds */ 429 krb5_error_code 430 rd_cred(inbuf, key, creds, sender_addr, recv_addr) 431 const krb5_data *inbuf; 432 const krb5_keyblock *key; 433 krb5_creds *creds; /* Filled in */ 434 const krb5_address *sender_addr; /* optional */ 435 const krb5_address *recv_addr; /* optional */ 436 { 437 krb5_error_code retval; 438 krb5_encrypt_block eblock; 439 krb5_cred *credmsg; 440 krb5_cred_enc_part *credmsg_enc_part; 441 krb5_data *scratch; 442 krb5_timestamp currenttime; 443 444 if (!krb5_is_krb_cred(inbuf)) 445 return KRB5KRB_AP_ERR_MSG_TYPE; 446 447 /* decode private message */ 448 if (retval = decode_krb5_cred(inbuf, &credmsg)) { 449 return retval; 450 } 451 452 #define cleanup_credmsg() {(void)krb5_xfree(credmsg->enc_part.ciphertext.data); (void)krb5_xfree(credmsg);} 453 454 if (!(scratch = (krb5_data *) malloc(sizeof(*scratch)))) { 455 cleanup_credmsg(); 456 return ENOMEM; 457 } 458 459 #define cleanup_scratch() {(void)memset(scratch->data, 0, scratch->length); (void)krb5_xfree(scratch->data);} 460 461 if (retval = encode_krb5_ticket(credmsg->tickets[0], &scratch)) { 462 cleanup_credmsg(); 463 cleanup_scratch(); 464 return(retval); 465 } 466 467 creds->ticket = *scratch; 468 if (!(creds->ticket.data = malloc(scratch->length))) { 469 krb5_xfree(creds->ticket.data); 470 return ENOMEM; 471 } 472 memcpy((char *)creds->ticket.data, (char *) scratch->data, scratch->length); 473 474 cleanup_scratch(); 475 476 if (!valid_etype(credmsg->enc_part.etype)) { 477 cleanup_credmsg(); 478 return KRB5_PROG_ETYPE_NOSUPP; 479 } 480 481 /* put together an eblock for this decryption */ 482 483 krb5_use_cstype(&eblock, credmsg->enc_part.etype); 484 scratch->length = credmsg->enc_part.ciphertext.length; 485 486 if (!(scratch->data = malloc(scratch->length))) { 487 cleanup_credmsg(); 488 return ENOMEM; 489 } 490 491 /* do any necessary key pre-processing */ 492 if (retval = krb5_process_key(&eblock, key)) { 493 cleanup_credmsg(); 494 cleanup_scratch(); 495 return retval; 496 } 497 498 #define cleanup_prockey() {(void) krb5_finish_key(&eblock);} 499 500 /* call the decryption routine */ 501 if (retval = krb5_decrypt((krb5_pointer) credmsg->enc_part.ciphertext.data, 502 (krb5_pointer) scratch->data, 503 scratch->length, &eblock, 504 0)) { 505 cleanup_credmsg(); 506 cleanup_scratch(); 507 cleanup_prockey(); 508 return retval; 509 } 510 511 /* cred message is now decrypted -- do some cleanup */ 512 513 cleanup_credmsg(); 514 515 if (retval = krb5_finish_key(&eblock)) { 516 cleanup_scratch(); 517 return retval; 518 } 519 520 /* now decode the decrypted stuff */ 521 if (retval = decode_krb5_enc_cred_part(scratch, &credmsg_enc_part)) { 522 cleanup_scratch(); 523 return retval; 524 } 525 cleanup_scratch(); 526 527 #define cleanup_mesg() {(void)krb5_xfree(credmsg_enc_part);} 528 529 if (retval = krb5_timeofday(¤ttime)) { 530 cleanup_mesg(); 531 return retval; 532 } 533 if (!in_clock_skew(credmsg_enc_part->timestamp)) { 534 cleanup_mesg(); 535 return KRB5KRB_AP_ERR_SKEW; 536 } 537 538 if (sender_addr && credmsg_enc_part->s_address && 539 !krb5_address_compare(sender_addr, 540 credmsg_enc_part->s_address)) { 541 cleanup_mesg(); 542 return KRB5KRB_AP_ERR_BADADDR; 543 } 544 if (recv_addr && credmsg_enc_part->r_address && 545 !krb5_address_compare(recv_addr, 546 credmsg_enc_part->r_address)) { 547 cleanup_mesg(); 548 return KRB5KRB_AP_ERR_BADADDR; 549 } 550 551 if (credmsg_enc_part->r_address) { 552 krb5_address **our_addrs; 553 554 if (retval = krb5_os_localaddr(&our_addrs)) { 555 cleanup_mesg(); 556 return retval; 557 } 558 if (!krb5_address_search(credmsg_enc_part->r_address, 559 our_addrs)) { 560 krb5_free_addresses(our_addrs); 561 cleanup_mesg(); 562 return KRB5KRB_AP_ERR_BADADDR; 563 } 564 krb5_free_addresses(our_addrs); 565 } 566 567 if (retval = krb5_copy_principal(credmsg_enc_part->ticket_info[0]->client, 568 &creds->client)) { 569 return(retval); 570 } 571 572 if (retval = krb5_copy_principal(credmsg_enc_part->ticket_info[0]->server, 573 &creds->server)) { 574 return(retval); 575 } 576 577 if (retval = 578 krb5_copy_keyblock_contents(credmsg_enc_part->ticket_info[0]->session, 579 &creds->keyblock)) { 580 return(retval); 581 } 582 583 #undef clean 584 #define clean() {\ 585 memset((char *)creds->keyblock.contents, 0, creds->keyblock.length);} 586 587 creds->times = credmsg_enc_part->ticket_info[0]->times; 588 creds->is_skey = FALSE; 589 creds->ticket_flags = credmsg_enc_part->ticket_info[0]->flags; 590 591 if (retval = krb5_copy_addresses(credmsg_enc_part->ticket_info[0]->caddrs, 592 &creds->addresses)) { 593 clean(); 594 return(retval); 595 } 596 597 creds->second_ticket.length = 0; 598 599 creds->authdata = 0; 600 601 cleanup_mesg(); 602 return 0; 603 #undef clean 604 #undef cleanup_credmsg 605 #undef cleanup_scratch 606 #undef cleanup_prockey 607 #undef cleanup_mesg 608 } 609 610 #endif /* defined(KRB5) && defined(FORWARD) */ 611