1 /* 2 * kdc/kdc_util.c 3 * 4 * Copyright 1990,1991 by the Massachusetts Institute of Technology. 5 * All Rights Reserved. 6 * 7 * Export of this software from the United States of America may 8 * require a specific license from the United States Government. 9 * It is the responsibility of any person or organization contemplating 10 * export to obtain such a license before exporting. 11 * 12 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 13 * distribute this software and its documentation for any purpose and 14 * without fee is hereby granted, provided that the above copyright 15 * notice appear in all copies and that both that copyright notice and 16 * this permission notice appear in supporting documentation, and that 17 * the name of M.I.T. not be used in advertising or publicity pertaining 18 * to distribution of the software without specific, written prior 19 * permission. Furthermore if you modify this software you must label 20 * your software as modified software and not distribute it in such a 21 * fashion that it might be confused with the original M.I.T. software. 22 * M.I.T. makes no representations about the suitability of 23 * this software for any purpose. It is provided "as is" without express 24 * or implied warranty. 25 * 26 * 27 * Utility functions for the KDC implementation. 28 */ 29 30 31 32 #include "k5-int.h" 33 #include "kdc_util.h" 34 #include "extern.h" 35 #include <stdio.h> 36 #include <ctype.h> 37 #include <syslog.h> 38 #include "adm.h" 39 #include "adm_proto.h" 40 #include <limits.h> 41 42 #ifdef USE_RCACHE 43 static char *kdc_current_rcname = (char *) NULL; 44 krb5_deltat rc_lifetime; /* See kdc_initialize_rcache() */ 45 #endif 46 47 #ifdef USE_RCACHE 48 /* 49 * initialize the replay cache. 50 */ 51 krb5_error_code 52 kdc_initialize_rcache(krb5_context kcontext, char *rcache_name) 53 { 54 krb5_error_code retval; 55 char *rcname; 56 char *sname; 57 58 rcname = (rcache_name) ? rcache_name : kdc_current_rcname; 59 60 /* rc_lifetime used elsewhere to verify we're not */ 61 /* replaying really old data */ 62 rc_lifetime = kcontext->clockskew; 63 64 if (!rcname) 65 rcname = KDCRCACHE; 66 if (!(retval = krb5_rc_resolve_full(kcontext, &kdc_rcache, rcname))) { 67 /* Recover or initialize the replay cache */ 68 if (!(retval = krb5_rc_recover(kcontext, kdc_rcache)) || 69 !(retval = krb5_rc_initialize(kcontext, 70 kdc_rcache, 71 kcontext->clockskew)) 72 ) { 73 /* Expunge the replay cache */ 74 if (!(retval = krb5_rc_expunge(kcontext, kdc_rcache))) { 75 sname = kdc_current_rcname; 76 kdc_current_rcname = strdup(rcname); 77 if (sname) 78 free(sname); 79 } 80 } 81 if (retval) 82 krb5_rc_close(kcontext, kdc_rcache); 83 } 84 return(retval); 85 } 86 #endif 87 88 /* 89 * concatenate first two authdata arrays, returning an allocated replacement. 90 * The replacement should be freed with krb5_free_authdata(). 91 */ 92 krb5_error_code 93 concat_authorization_data(krb5_authdata **first, krb5_authdata **second, 94 krb5_authdata ***output) 95 { 96 register int i, j; 97 register krb5_authdata **ptr, **retdata; 98 99 /* count up the entries */ 100 i = 0; 101 if (first) 102 for (ptr = first; *ptr; ptr++) 103 i++; 104 if (second) 105 for (ptr = second; *ptr; ptr++) 106 i++; 107 108 retdata = (krb5_authdata **)malloc((i+1)*sizeof(*retdata)); 109 if (!retdata) 110 return ENOMEM; 111 retdata[i] = 0; /* null-terminated array */ 112 for (i = 0, j = 0, ptr = first; j < 2 ; ptr = second, j++) 113 while (ptr && *ptr) { 114 /* now walk & copy */ 115 retdata[i] = (krb5_authdata *)malloc(sizeof(*retdata[i])); 116 if (!retdata[i]) { 117 krb5_free_authdata(kdc_context, retdata); 118 return ENOMEM; 119 } 120 *retdata[i] = **ptr; 121 if (!(retdata[i]->contents = 122 (krb5_octet *)malloc(retdata[i]->length))) { 123 free((char *)retdata[i]); 124 retdata[i] = 0; 125 krb5_free_authdata(kdc_context, retdata); 126 return ENOMEM; 127 } 128 memcpy((char *) retdata[i]->contents, 129 (char *)(*ptr)->contents, 130 retdata[i]->length); 131 132 ptr++; 133 i++; 134 } 135 *output = retdata; 136 return 0; 137 } 138 139 krb5_boolean 140 realm_compare(krb5_principal princ1, krb5_principal princ2) 141 { 142 krb5_data *realm1 = krb5_princ_realm(kdc_context, princ1); 143 krb5_data *realm2 = krb5_princ_realm(kdc_context, princ2); 144 145 return((realm1->length == realm2->length) && 146 !memcmp(realm1->data, realm2->data, realm1->length)); 147 } 148 149 /* 150 * Returns TRUE if the kerberos principal is the name of a Kerberos ticket 151 * service. 152 */ 153 krb5_boolean krb5_is_tgs_principal(krb5_principal principal) 154 { 155 if ((krb5_princ_size(kdc_context, principal) > 0) && 156 (krb5_princ_component(kdc_context, principal, 0)->length == 157 KRB5_TGS_NAME_SIZE) && 158 (!memcmp(krb5_princ_component(kdc_context, principal, 0)->data, 159 KRB5_TGS_NAME, KRB5_TGS_NAME_SIZE))) 160 return TRUE; 161 return FALSE; 162 } 163 164 /* 165 * given authentication data (provides seed for checksum), verify checksum 166 * for source data. 167 */ 168 static krb5_error_code 169 comp_cksum(krb5_context kcontext, krb5_data *source, krb5_ticket *ticket, 170 krb5_checksum *his_cksum) 171 { 172 krb5_error_code retval; 173 krb5_boolean valid; 174 175 if (!krb5_c_valid_cksumtype(his_cksum->checksum_type)) 176 return KRB5KDC_ERR_SUMTYPE_NOSUPP; 177 178 /* must be collision proof */ 179 if (!krb5_c_is_coll_proof_cksum(his_cksum->checksum_type)) 180 return KRB5KRB_AP_ERR_INAPP_CKSUM; 181 182 /* verify checksum */ 183 if ((retval = krb5_c_verify_checksum(kcontext, ticket->enc_part2->session, 184 KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM, 185 source, his_cksum, &valid))) 186 return(retval); 187 188 if (!valid) 189 return(KRB5KRB_AP_ERR_BAD_INTEGRITY); 190 191 return(0); 192 } 193 194 krb5_error_code 195 kdc_process_tgs_req(krb5_kdc_req *request, const krb5_fulladdr *from, 196 krb5_data *pkt, krb5_ticket **ticket, 197 krb5_keyblock **subkey) 198 { 199 krb5_pa_data ** tmppa; 200 krb5_ap_req * apreq; 201 krb5_error_code retval; 202 krb5_data scratch1; 203 krb5_data * scratch = NULL; 204 krb5_boolean foreign_server = FALSE; 205 krb5_auth_context auth_context = NULL; 206 krb5_authenticator * authenticator = NULL; 207 krb5_checksum * his_cksum = NULL; 208 /* krb5_keyblock * key = NULL;*/ 209 /* krb5_kvno kvno = 0;*/ 210 211 if (!request->padata) 212 return KRB5KDC_ERR_PADATA_TYPE_NOSUPP; 213 for (tmppa = request->padata; *tmppa; tmppa++) { 214 if ((*tmppa)->pa_type == KRB5_PADATA_AP_REQ) 215 break; 216 } 217 if (!*tmppa) /* cannot find any AP_REQ */ 218 return KRB5KDC_ERR_PADATA_TYPE_NOSUPP; 219 220 scratch1.length = (*tmppa)->length; 221 scratch1.data = (char *)(*tmppa)->contents; 222 if ((retval = decode_krb5_ap_req(&scratch1, &apreq))) 223 return retval; 224 225 if (isflagset(apreq->ap_options, AP_OPTS_USE_SESSION_KEY) || 226 isflagset(apreq->ap_options, AP_OPTS_MUTUAL_REQUIRED)) { 227 krb5_klog_syslog(LOG_INFO, "TGS_REQ: SESSION KEY or MUTUAL"); 228 retval = KRB5KDC_ERR_POLICY; 229 goto cleanup; 230 } 231 232 /* If the "server" principal in the ticket is not something 233 in the local realm, then we must refuse to service the request 234 if the client claims to be from the local realm. 235 236 If we don't do this, then some other realm's nasty KDC can 237 claim to be authenticating a client from our realm, and we'll 238 give out tickets concurring with it! 239 240 we set a flag here for checking below. 241 */ 242 if ((krb5_princ_realm(kdc_context, apreq->ticket->server)->length != 243 krb5_princ_realm(kdc_context, tgs_server)->length) || 244 memcmp(krb5_princ_realm(kdc_context, apreq->ticket->server)->data, 245 krb5_princ_realm(kdc_context, tgs_server)->data, 246 krb5_princ_realm(kdc_context, tgs_server)->length)) 247 foreign_server = TRUE; 248 249 if ((retval = krb5_auth_con_init(kdc_context, &auth_context))) 250 goto cleanup; 251 252 if ((retval = krb5_auth_con_setaddrs(kdc_context, auth_context, NULL, 253 from->address)) ) 254 goto cleanup_auth_context; 255 #ifdef USE_RCACHE 256 if ((retval = krb5_auth_con_setrcache(kdc_context, auth_context, 257 kdc_rcache))) 258 goto cleanup_auth_context; 259 #endif 260 261 /* 262 if ((retval = kdc_get_server_key(apreq->ticket, &key, &kvno))) 263 goto cleanup_auth_context; 264 */ 265 266 /* 267 * XXX This is currently wrong but to fix it will require making a 268 * new keytab for groveling over the kdb. 269 */ 270 /* 271 retval = krb5_auth_con_setuseruserkey(kdc_context, auth_context, key); 272 krb5_free_keyblock(kdc_context, key); 273 if (retval) 274 goto cleanup_auth_context; 275 */ 276 277 if ((retval = krb5_rd_req_decoded_anyflag(kdc_context, &auth_context, apreq, 278 apreq->ticket->server, 279 kdc_active_realm->realm_keytab, 280 NULL, ticket))) { 281 #ifdef USE_RCACHE 282 /* 283 * I'm not so sure that this is right, but it's better than nothing 284 * at all. 285 * 286 * If we choke in the rd_req because of the replay cache, then attempt 287 * to reinitialize the replay cache because somebody could have deleted 288 * it from underneath us (e.g. a cron job) 289 */ 290 if ((retval == KRB5_RC_IO_IO) || 291 (retval == KRB5_RC_IO_UNKNOWN)) { 292 (void) krb5_rc_close(kdc_context, kdc_rcache); 293 kdc_rcache = (krb5_rcache) NULL; 294 if (!(retval = kdc_initialize_rcache(kdc_context, (char *) NULL))) { 295 if ((retval = krb5_auth_con_setrcache(kdc_context, auth_context, 296 kdc_rcache)) || 297 (retval = krb5_rd_req_decoded_anyflag(kdc_context, &auth_context, 298 apreq, apreq->ticket->server, 299 kdc_active_realm->realm_keytab, 300 NULL, ticket)) 301 ) 302 goto cleanup_auth_context; 303 } 304 } else 305 goto cleanup_auth_context; 306 #else 307 goto cleanup_auth_context; 308 #endif 309 } 310 311 /* "invalid flag" tickets can must be used to validate */ 312 if (isflagset((*ticket)->enc_part2->flags, TKT_FLG_INVALID) 313 && !isflagset(request->kdc_options, KDC_OPT_VALIDATE)) { 314 retval = KRB5KRB_AP_ERR_TKT_INVALID; 315 goto cleanup_auth_context; 316 } 317 318 if ((retval = krb5_auth_con_getrecvsubkey(kdc_context, 319 auth_context, subkey))) 320 goto cleanup_auth_context; 321 322 if ((retval = krb5_auth_con_getauthenticator(kdc_context, auth_context, 323 &authenticator))) 324 goto cleanup_auth_context; 325 326 /* Check for a checksum */ 327 if (!(his_cksum = authenticator->checksum)) { 328 retval = KRB5KRB_AP_ERR_INAPP_CKSUM; 329 goto cleanup_authenticator; 330 } 331 332 /* make sure the client is of proper lineage (see above) */ 333 if (foreign_server) { 334 krb5_data *tkt_realm = krb5_princ_realm(kdc_context, 335 (*ticket)->enc_part2->client); 336 krb5_data *tgs_realm = krb5_princ_realm(kdc_context, tgs_server); 337 if (tkt_realm->length == tgs_realm->length && 338 !memcmp(tkt_realm->data, tgs_realm->data, tgs_realm->length)) { 339 /* someone in a foreign realm claiming to be local */ 340 krb5_klog_syslog(LOG_INFO, "PROCESS_TGS: failed lineage check"); 341 retval = KRB5KDC_ERR_POLICY; 342 goto cleanup_authenticator; 343 } 344 } 345 346 /* 347 * Check application checksum vs. tgs request 348 * 349 * We try checksumming the req-body two different ways: first we 350 * try reaching into the raw asn.1 stream (if available), and 351 * checksum that directly; if that fails, then we try encoding 352 * using our local asn.1 library. 353 */ 354 if (pkt && (fetch_asn1_field((unsigned char *) pkt->data, 355 1, 4, &scratch1) >= 0)) { 356 if (comp_cksum(kdc_context, &scratch1, *ticket, his_cksum)) { 357 if (!(retval = encode_krb5_kdc_req_body(request, &scratch))) 358 retval = comp_cksum(kdc_context, scratch, *ticket, his_cksum); 359 krb5_free_data(kdc_context, scratch); 360 } 361 } 362 363 cleanup_authenticator: 364 krb5_free_authenticator(kdc_context, authenticator); 365 366 cleanup_auth_context: 367 /* We do not want the free of the auth_context to close the rcache */ 368 #ifdef USE_RCACHE 369 (void) krb5_auth_con_setrcache(kdc_context, auth_context, 0); 370 #endif 371 krb5_auth_con_free(kdc_context, auth_context); 372 373 cleanup: 374 krb5_free_ap_req(kdc_context, apreq); 375 return retval; 376 } 377 378 /* XXX This function should no longer be necessary. 379 * The KDC should take the keytab associated with the realm and pass that to 380 * the krb5_rd_req_decode(). --proven 381 * 382 * It's actually still used by do_tgs_req() for u2u auth, and not too 383 * much else. -- tlyu 384 */ 385 krb5_error_code 386 kdc_get_server_key(krb5_ticket *ticket, krb5_keyblock **key, krb5_kvno *kvno) 387 { 388 krb5_error_code retval; 389 krb5_db_entry server; 390 krb5_boolean more; 391 int nprincs; 392 krb5_key_data * server_key; 393 394 nprincs = 1; 395 396 if ((retval = krb5_db_get_principal(kdc_context, ticket->server, 397 &server, &nprincs, 398 &more))) { 399 return(retval); 400 } 401 if (more) { 402 krb5_db_free_principal(kdc_context, &server, nprincs); 403 return(KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE); 404 } else if (nprincs != 1) { 405 char *sname; 406 407 krb5_db_free_principal(kdc_context, &server, nprincs); 408 if (!krb5_unparse_name(kdc_context, ticket->server, &sname)) { 409 limit_string(sname); 410 krb5_klog_syslog(LOG_ERR,"TGS_REQ: UNKNOWN SERVER: server='%s'", 411 sname); 412 free(sname); 413 } 414 return(KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN); 415 } 416 retval = krb5_dbe_find_enctype(kdc_context, &server, 417 ticket->enc_part.enctype, -1, 418 ticket->enc_part.kvno, &server_key); 419 if (retval) 420 goto errout; 421 if (!server_key) { 422 retval = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; 423 goto errout; 424 } 425 *kvno = server_key->key_data_kvno; 426 if ((*key = (krb5_keyblock *)malloc(sizeof **key))) { 427 retval = krb5_dbekd_decrypt_key_data(kdc_context, &master_keyblock, 428 server_key, 429 *key, NULL); 430 } else 431 retval = ENOMEM; 432 errout: 433 krb5_db_free_principal(kdc_context, &server, nprincs); 434 return retval; 435 } 436 437 /* This probably wants to be updated if you support last_req stuff */ 438 439 static krb5_last_req_entry nolrentry = { KV5M_LAST_REQ_ENTRY, KRB5_LRQ_NONE, 0 }; 440 static krb5_last_req_entry *nolrarray[] = { &nolrentry, 0 }; 441 442 krb5_error_code 443 fetch_last_req_info(krb5_db_entry *dbentry, krb5_last_req_entry ***lrentry) 444 { 445 *lrentry = nolrarray; 446 return 0; 447 } 448 449 450 /* XXX! This is a temporary place-holder */ 451 452 krb5_error_code 453 check_hot_list(krb5_ticket *ticket) 454 { 455 return 0; 456 } 457 458 459 #define MAX_REALM_LN 500 460 461 462 /* 463 * subrealm - determine if r2 is a subrealm of r1 464 * 465 * SUBREALM takes two realms, r1 and r2, and 466 * determines if r2 is a subrealm of r1. 467 * r2 is a subrealm of r1 if (r1 is a prefix 468 * of r2 AND r1 and r2 begin with a /) or if 469 * (r1 is a suffix of r2 and neither r1 nor r2 470 * begin with a /). 471 * 472 * RETURNS: If r2 is a subrealm, and r1 is a prefix, the number 473 * of characters in the suffix of r2 is returned as a 474 * negative number. 475 * 476 * If r2 is a subrealm, and r1 is a suffix, the number 477 * of characters in the prefix of r2 is returned as a 478 * positive number. 479 * 480 * If r2 is not a subrealm, SUBREALM returns 0. 481 */ 482 static int 483 subrealm(char *r1, char *r2) 484 { 485 size_t l1,l2; 486 l1 = strlen(r1); 487 l2 = strlen(r2); 488 if(l2 <= l1) return(0); 489 if((*r1 == '/') && (*r2 == '/') && (strncmp(r1,r2,l1) == 0)) return(l1-l2); 490 if((*r1 != '/') && (*r2 != '/') && (strncmp(r1,r2+l2-l1,l1) == 0)) 491 return(l2-l1); 492 return(0); 493 } 494 495 /* 496 * add_to_transited Adds the name of the realm which issued the 497 * ticket granting ticket on which the new ticket to 498 * be issued is based (note that this is the same as 499 * the realm of the server listed in the ticket 500 * granting ticket. 501 * 502 * ASSUMPTIONS: This procedure assumes that the transited field from 503 * the existing ticket granting ticket already appears 504 * in compressed form. It will add the new realm while 505 * maintaining that form. As long as each successive 506 * realm is added using this (or a similar) routine, the 507 * transited field will be in compressed form. The 508 * basis step is an empty transited field which is, by 509 * its nature, in its most compressed form. 510 * 511 * ARGUMENTS: krb5_data *tgt_trans Transited field from TGT 512 * krb5_data *new_trans The transited field for the new ticket 513 * krb5_principal tgs Name of ticket granting server 514 * This includes the realm of the KDC 515 * that issued the ticket granting 516 * ticket. This is the realm that is 517 * to be added to the transited field. 518 * krb5_principal client Name of the client 519 * krb5_principal server The name of the requested server. 520 * This may be the an intermediate 521 * ticket granting server. 522 * 523 * The last two argument are needed since they are 524 * implicitly part of the transited field of the new ticket 525 * even though they are not explicitly listed. 526 * 527 * RETURNS: krb5_error_code - Success, or out of memory 528 * 529 * MODIFIES: new_trans: ->length will contain the length of the new 530 * transited field. 531 * 532 * If ->data was not null when this procedure 533 * is called, the memory referenced by ->data 534 * will be deallocated. 535 * 536 * Memory will be allocated for the new transited field 537 * ->data will be updated to point to the newly 538 * allocated memory. 539 * 540 * BUGS: The space allocated for the new transited field is the 541 * maximum that might be needed given the old transited field, 542 * and the realm to be added. This length is calculated 543 * assuming that no compression of the new realm is possible. 544 * This has no adverse consequences other than the allocation 545 * of more space than required. 546 * 547 * This procedure will not yet use the null subfield notation, 548 * and it will get confused if it sees it. 549 * 550 * This procedure does not check for quoted commas in realm 551 * names. 552 */ 553 554 static char * 555 data2string (krb5_data *d) 556 { 557 char *s; 558 s = malloc(d->length + 1); 559 if (s) { 560 memcpy(s, d->data, d->length); 561 s[d->length] = 0; 562 } 563 return s; 564 } 565 566 krb5_error_code 567 add_to_transited(krb5_data *tgt_trans, krb5_data *new_trans, 568 krb5_principal tgs, krb5_principal client, 569 krb5_principal server) 570 { 571 krb5_error_code retval; 572 char *realm; 573 char *trans; 574 char *otrans, *otrans_ptr; 575 576 /* The following are for stepping through the transited field */ 577 578 char prev[MAX_REALM_LN]; 579 char next[MAX_REALM_LN]; 580 char current[MAX_REALM_LN]; 581 char exp[MAX_REALM_LN]; /* Expanded current realm name */ 582 583 int i; 584 int clst, nlst; /* count of last character in current and next */ 585 int pl, pl1; /* prefix length */ 586 int added; /* TRUE = new realm has been added */ 587 588 realm = data2string(krb5_princ_realm(kdc_context, tgs)); 589 if (realm == NULL) 590 return(ENOMEM); 591 592 otrans = data2string(tgt_trans); 593 if (otrans == NULL) { 594 free(realm); 595 return(ENOMEM); 596 } 597 /* Keep track of start so we can free */ 598 otrans_ptr = otrans; 599 600 /* +1 for null, 601 +1 for extra comma which may be added between 602 +1 for potential space when leading slash in realm */ 603 if (!(trans = (char *) malloc(strlen(realm) + strlen(otrans) + 3))) { 604 retval = ENOMEM; 605 goto fail; 606 } 607 608 if (new_trans->data) free(new_trans->data); 609 new_trans->data = trans; 610 new_trans->length = 0; 611 612 trans[0] = '\0'; 613 614 /* For the purpose of appending, the realm preceding the first */ 615 /* realm in the transited field is considered the null realm */ 616 617 prev[0] = '\0'; 618 619 /* read field into current */ 620 for (i = 0; *otrans != '\0';) { 621 if (*otrans == '\\') { 622 if (*(++otrans) == '\0') 623 break; 624 else 625 continue; 626 } 627 if (*otrans == ',') { 628 otrans++; 629 break; 630 } 631 current[i++] = *otrans++; 632 if (i >= MAX_REALM_LN) { 633 retval = KRB5KRB_AP_ERR_ILL_CR_TKT; 634 goto fail; 635 } 636 } 637 current[i] = '\0'; 638 639 added = (krb5_princ_realm(kdc_context, client)->length == strlen(realm) && 640 !strncmp(krb5_princ_realm(kdc_context, client)->data, realm, strlen(realm))) || 641 (krb5_princ_realm(kdc_context, server)->length == strlen(realm) && 642 !strncmp(krb5_princ_realm(kdc_context, server)->data, realm, strlen(realm))); 643 644 while (current[0]) { 645 646 /* figure out expanded form of current name */ 647 648 clst = strlen(current) - 1; 649 if (current[0] == ' ') { 650 strncpy(exp, current+1, sizeof(exp) - 1); 651 exp[sizeof(exp) - 1] = '\0'; 652 } 653 else if ((current[0] == '/') && (prev[0] == '/')) { 654 strncpy(exp, prev, sizeof(exp) - 1); 655 exp[sizeof(exp) - 1] = '\0'; 656 if (strlen(exp) + strlen(current) + 1 >= MAX_REALM_LN) { 657 retval = KRB5KRB_AP_ERR_ILL_CR_TKT; 658 goto fail; 659 } 660 strncat(exp, current, sizeof(exp) - 1 - strlen(exp)); 661 } 662 else if (current[clst] == '.') { 663 strncpy(exp, current, sizeof(exp) - 1); 664 exp[sizeof(exp) - 1] = '\0'; 665 if (strlen(exp) + strlen(prev) + 1 >= MAX_REALM_LN) { 666 retval = KRB5KRB_AP_ERR_ILL_CR_TKT; 667 goto fail; 668 } 669 strncat(exp, prev, sizeof(exp) - 1 - strlen(exp)); 670 } 671 else { 672 strncpy(exp, current, sizeof(exp) - 1); 673 exp[sizeof(exp) - 1] = '\0'; 674 } 675 676 /* read field into next */ 677 for (i = 0; *otrans != '\0';) { 678 if (*otrans == '\\') { 679 if (*(++otrans) == '\0') 680 break; 681 else 682 continue; 683 } 684 if (*otrans == ',') { 685 otrans++; 686 break; 687 } 688 next[i++] = *otrans++; 689 if (i >= MAX_REALM_LN) { 690 retval = KRB5KRB_AP_ERR_ILL_CR_TKT; 691 goto fail; 692 } 693 } 694 next[i] = '\0'; 695 nlst = i - 1; 696 697 if (!strcmp(exp, realm)) added = TRUE; 698 699 /* If we still have to insert the new realm */ 700 701 if (!added) { 702 703 /* Is the next field compressed? If not, and if the new */ 704 /* realm is a subrealm of the current realm, compress */ 705 /* the new realm, and insert immediately following the */ 706 /* current one. Note that we can not do this if the next*/ 707 /* field is already compressed since it would mess up */ 708 /* what has already been done. In most cases, this is */ 709 /* not a problem because the realm to be added will be a */ 710 /* subrealm of the next field too, and we will catch */ 711 /* it in a future iteration. */ 712 713 /* Note that the second test here is an unsigned comparison, 714 so the first half (or a cast) is also required. */ 715 assert(nlst < 0 || nlst < sizeof(next)); 716 if ((nlst < 0 || next[nlst] != '.') && 717 (next[0] != '/') && 718 (pl = subrealm(exp, realm))) { 719 added = TRUE; 720 current[sizeof(current) - 1] = '\0'; 721 if (strlen(current) + (pl>0?pl:-pl) + 2 >= MAX_REALM_LN) { 722 retval = KRB5KRB_AP_ERR_ILL_CR_TKT; 723 goto fail; 724 } 725 strncat(current, ",", sizeof(current) - 1 - strlen(current)); 726 if (pl > 0) { 727 strncat(current, realm, (unsigned) pl); 728 } 729 else { 730 strncat(current, realm+strlen(realm)+pl, (unsigned) (-pl)); 731 } 732 } 733 734 /* Whether or not the next field is compressed, if the */ 735 /* realm to be added is a superrealm of the current realm,*/ 736 /* then the current realm can be compressed. First the */ 737 /* realm to be added must be compressed relative to the */ 738 /* previous realm (if possible), and then the current */ 739 /* realm compressed relative to the new realm. Note that */ 740 /* if the realm to be added is also a superrealm of the */ 741 /* previous realm, it would have been added earlier, and */ 742 /* we would not reach this step this time around. */ 743 744 else if ((pl = subrealm(realm, exp))) { 745 added = TRUE; 746 current[0] = '\0'; 747 if ((pl1 = subrealm(prev,realm))) { 748 if (strlen(current) + (pl1>0?pl1:-pl1) + 1 >= MAX_REALM_LN) { 749 retval = KRB5KRB_AP_ERR_ILL_CR_TKT; 750 goto fail; 751 } 752 if (pl1 > 0) { 753 strncat(current, realm, (unsigned) pl1); 754 } 755 else { 756 strncat(current, realm+strlen(realm)+pl1, (unsigned) (-pl1)); 757 } 758 } 759 else { /* If not a subrealm */ 760 if ((realm[0] == '/') && prev[0]) { 761 if (strlen(current) + 2 >= MAX_REALM_LN) { 762 retval = KRB5KRB_AP_ERR_ILL_CR_TKT; 763 goto fail; 764 } 765 strncat(current, " ", sizeof(current) - 1 - strlen(current)); 766 current[sizeof(current) - 1] = '\0'; 767 } 768 if (strlen(current) + strlen(realm) + 1 >= MAX_REALM_LN) { 769 retval = KRB5KRB_AP_ERR_ILL_CR_TKT; 770 goto fail; 771 } 772 strncat(current, realm, sizeof(current) - 1 - strlen(current)); 773 current[sizeof(current) - 1] = '\0'; 774 } 775 if (strlen(current) + (pl>0?pl:-pl) + 2 >= MAX_REALM_LN) { 776 retval = KRB5KRB_AP_ERR_ILL_CR_TKT; 777 goto fail; 778 } 779 strncat(current,",", sizeof(current) - 1 - strlen(current)); 780 current[sizeof(current) - 1] = '\0'; 781 if (pl > 0) { 782 strncat(current, exp, (unsigned) pl); 783 } 784 else { 785 strncat(current, exp+strlen(exp)+pl, (unsigned)(-pl)); 786 } 787 } 788 } 789 790 if (new_trans->length != 0) { 791 if (strlen(trans) + 2 >= MAX_REALM_LN) { 792 retval = KRB5KRB_AP_ERR_ILL_CR_TKT; 793 goto fail; 794 } 795 strcat(trans, ","); 796 } 797 if (strlen(trans) + strlen(current) + 1 >= MAX_REALM_LN) { 798 retval = KRB5KRB_AP_ERR_ILL_CR_TKT; 799 goto fail; 800 } 801 strcat(trans, current); 802 new_trans->length = strlen(trans); 803 804 strncpy(prev, exp, sizeof(prev) - 1); 805 prev[sizeof(prev) - 1] = '\0'; 806 strncpy(current, next, sizeof(current) - 1); 807 current[sizeof(current) - 1] = '\0'; 808 } 809 810 if (!added) { 811 if (new_trans->length != 0) { 812 if (strlen(trans) + 2 >= MAX_REALM_LN) { 813 retval = KRB5KRB_AP_ERR_ILL_CR_TKT; 814 goto fail; 815 } 816 strcat(trans, ","); 817 } 818 if((realm[0] == '/') && trans[0]) { 819 if (strlen(trans) + 2 >= MAX_REALM_LN) { 820 retval = KRB5KRB_AP_ERR_ILL_CR_TKT; 821 goto fail; 822 } 823 strcat(trans, " "); 824 } 825 if (strlen(trans) + strlen(realm) + 1 >= MAX_REALM_LN) { 826 retval = KRB5KRB_AP_ERR_ILL_CR_TKT; 827 goto fail; 828 } 829 strcat(trans, realm); 830 new_trans->length = strlen(trans); 831 } 832 833 retval = 0; 834 fail: 835 free(realm); 836 free(otrans_ptr); 837 return (retval); 838 } 839 840 /* 841 * Routines that validate a AS request; checks a lot of things. :-) 842 * 843 * Returns a Kerberos protocol error number, which is _not_ the same 844 * as a com_err error number! 845 */ 846 #define AS_INVALID_OPTIONS (KDC_OPT_FORWARDED | KDC_OPT_PROXY |\ 847 KDC_OPT_VALIDATE | KDC_OPT_RENEW | KDC_OPT_ENC_TKT_IN_SKEY) 848 int 849 validate_as_request(register krb5_kdc_req *request, krb5_db_entry client, 850 krb5_db_entry server, krb5_timestamp kdc_time, 851 const char **status) 852 { 853 int errcode; 854 855 /* 856 * If an option is set that is only allowed in TGS requests, complain. 857 */ 858 if (request->kdc_options & AS_INVALID_OPTIONS) { 859 *status = "INVALID AS OPTIONS"; 860 return KDC_ERR_BADOPTION; 861 } 862 863 /* The client's password must not be expired, unless the server is 864 a KRB5_KDC_PWCHANGE_SERVICE. */ 865 if (client.pw_expiration && client.pw_expiration < kdc_time && 866 !isflagset(server.attributes, KRB5_KDB_PWCHANGE_SERVICE)) { 867 *status = "CLIENT KEY EXPIRED"; 868 #ifdef KRBCONF_VAGUE_ERRORS 869 return(KRB_ERR_GENERIC); 870 #else 871 return(KDC_ERR_KEY_EXP); 872 #endif 873 } 874 875 /* The client must not be expired */ 876 if (client.expiration && client.expiration < kdc_time) { 877 *status = "CLIENT EXPIRED"; 878 #ifdef KRBCONF_VAGUE_ERRORS 879 return(KRB_ERR_GENERIC); 880 #else 881 return(KDC_ERR_NAME_EXP); 882 #endif 883 } 884 885 /* The server must not be expired */ 886 if (server.expiration && server.expiration < kdc_time) { 887 *status = "SERVICE EXPIRED"; 888 return(KDC_ERR_SERVICE_EXP); 889 } 890 891 /* 892 * If the client requires password changing, then only allow the 893 * pwchange service. 894 */ 895 if (isflagset(client.attributes, KRB5_KDB_REQUIRES_PWCHANGE) && 896 !isflagset(server.attributes, KRB5_KDB_PWCHANGE_SERVICE)) { 897 *status = "REQUIRED PWCHANGE"; 898 return(KDC_ERR_KEY_EXP); 899 } 900 901 /* Client and server must allow postdating tickets */ 902 if ((isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE) || 903 isflagset(request->kdc_options, KDC_OPT_POSTDATED)) && 904 (isflagset(client.attributes, KRB5_KDB_DISALLOW_POSTDATED) || 905 isflagset(server.attributes, KRB5_KDB_DISALLOW_POSTDATED))) { 906 *status = "POSTDATE NOT ALLOWED"; 907 return(KDC_ERR_CANNOT_POSTDATE); 908 } 909 910 /* Client and server must allow forwardable tickets */ 911 if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE) && 912 (isflagset(client.attributes, KRB5_KDB_DISALLOW_FORWARDABLE) || 913 isflagset(server.attributes, KRB5_KDB_DISALLOW_FORWARDABLE))) { 914 *status = "FORWARDABLE NOT ALLOWED"; 915 return(KDC_ERR_POLICY); 916 } 917 918 /* Client and server must allow renewable tickets */ 919 if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE) && 920 (isflagset(client.attributes, KRB5_KDB_DISALLOW_RENEWABLE) || 921 isflagset(server.attributes, KRB5_KDB_DISALLOW_RENEWABLE))) { 922 *status = "RENEWABLE NOT ALLOWED"; 923 return(KDC_ERR_POLICY); 924 } 925 926 /* Client and server must allow proxiable tickets */ 927 if (isflagset(request->kdc_options, KDC_OPT_PROXIABLE) && 928 (isflagset(client.attributes, KRB5_KDB_DISALLOW_PROXIABLE) || 929 isflagset(server.attributes, KRB5_KDB_DISALLOW_PROXIABLE))) { 930 *status = "PROXIABLE NOT ALLOWED"; 931 return(KDC_ERR_POLICY); 932 } 933 934 /* Check to see if client is locked out */ 935 if (isflagset(client.attributes, KRB5_KDB_DISALLOW_ALL_TIX)) { 936 *status = "CLIENT LOCKED OUT"; 937 return(KDC_ERR_C_PRINCIPAL_UNKNOWN); 938 } 939 940 /* Check to see if server is locked out */ 941 if (isflagset(server.attributes, KRB5_KDB_DISALLOW_ALL_TIX)) { 942 *status = "SERVICE LOCKED OUT"; 943 return(KDC_ERR_S_PRINCIPAL_UNKNOWN); 944 } 945 946 /* Check to see if server is allowed to be a service */ 947 if (isflagset(server.attributes, KRB5_KDB_DISALLOW_SVR)) { 948 *status = "SERVICE NOT ALLOWED"; 949 return(KDC_ERR_S_PRINCIPAL_UNKNOWN); 950 } 951 952 /* 953 * Check against local policy 954 */ 955 errcode = against_local_policy_as(request, server, client, 956 kdc_time, status); 957 if (errcode) 958 return errcode; 959 960 return 0; 961 } 962 963 #define ASN1_ID_CLASS (0xc0) 964 #define ASN1_ID_TYPE (0x20) 965 #define ASN1_ID_TAG (0x1f) 966 #define ASN1_CLASS_UNIV (0) 967 #define ASN1_CLASS_APP (1) 968 #define ASN1_CLASS_CTX (2) 969 #define ASN1_CLASS_PRIV (3) 970 #define asn1_id_constructed(x) (x & ASN1_ID_TYPE) 971 #define asn1_id_primitive(x) (!asn1_id_constructed(x)) 972 #define asn1_id_class(x) ((x & ASN1_ID_CLASS) >> 6) 973 #define asn1_id_tag(x) (x & ASN1_ID_TAG) 974 975 /* 976 * asn1length - return encoded length of value. 977 * 978 * passed a pointer into the asn.1 stream, which is updated 979 * to point right after the length bits. 980 * 981 * returns -1 on failure. 982 */ 983 static int 984 asn1length(unsigned char **astream) 985 { 986 int length; /* resulting length */ 987 int sublen; /* sublengths */ 988 int blen; /* bytes of length */ 989 unsigned char *p; /* substring searching */ 990 991 if (**astream & 0x80) { 992 blen = **astream & 0x7f; 993 if (blen > 3) { 994 return(-1); 995 } 996 for (++*astream, length = 0; blen; ++*astream, blen--) { 997 length = (length << 8) | **astream; 998 } 999 if (length == 0) { 1000 /* indefinite length, figure out by hand */ 1001 p = *astream; 1002 p++; 1003 while (1) { 1004 /* compute value length. */ 1005 if ((sublen = asn1length(&p)) < 0) { 1006 return(-1); 1007 } 1008 p += sublen; 1009 /* check for termination */ 1010 if ((!*p++) && (!*p)) { 1011 p++; 1012 break; 1013 } 1014 } 1015 length = p - *astream; 1016 } 1017 } else { 1018 length = **astream; 1019 ++*astream; 1020 } 1021 return(length); 1022 } 1023 1024 /* 1025 * fetch_asn1_field - return raw asn.1 stream of subfield. 1026 * 1027 * this routine is passed a context-dependent tag number and "level" and returns 1028 * the size and length of the corresponding level subfield. 1029 * 1030 * levels and are numbered starting from 1. 1031 * 1032 * returns 0 on success, -1 otherwise. 1033 */ 1034 int 1035 fetch_asn1_field(unsigned char *astream, unsigned int level, 1036 unsigned int field, krb5_data *data) 1037 { 1038 unsigned char *estream; /* end of stream */ 1039 int classes; /* # classes seen so far this level */ 1040 unsigned int levels = 0; /* levels seen so far */ 1041 int lastlevel = 1000; /* last level seen */ 1042 int length; /* various lengths */ 1043 int tag; /* tag number */ 1044 unsigned char savelen; /* saved length of our field */ 1045 1046 classes = -1; 1047 /* we assume that the first identifier/length will tell us 1048 how long the entire stream is. */ 1049 astream++; 1050 estream = astream; 1051 if ((length = asn1length(&astream)) < 0) { 1052 return(-1); 1053 } 1054 estream += length; 1055 /* search down the stream, checking identifiers. we process identifiers 1056 until we hit the "level" we want, and then process that level for our 1057 subfield, always making sure we don't go off the end of the stream. */ 1058 while (astream < estream) { 1059 if (!asn1_id_constructed(*astream)) { 1060 return(-1); 1061 } 1062 if (asn1_id_class(*astream) == ASN1_CLASS_CTX) { 1063 if ((tag = (int)asn1_id_tag(*astream)) <= lastlevel) { 1064 levels++; 1065 classes = -1; 1066 } 1067 lastlevel = tag; 1068 if (levels == level) { 1069 /* in our context-dependent class, is this the one we're looking for ? */ 1070 if (tag == field) { 1071 /* return length and data */ 1072 astream++; 1073 savelen = *astream; 1074 if ((data->length = asn1length(&astream)) < 0) { 1075 return(-1); 1076 } 1077 /* if the field length is indefinite, we will have to subtract two 1078 (terminating octets) from the length returned since we don't want 1079 to pass any info from the "wrapper" back. asn1length will always return 1080 the *total* length of the field, not just what's contained in it */ 1081 if ((savelen & 0xff) == 0x80) { 1082 data->length -=2 ; 1083 } 1084 data->data = (char *)astream; 1085 return(0); 1086 } else if (tag <= classes) { 1087 /* we've seen this class before, something must be wrong */ 1088 return(-1); 1089 } else { 1090 classes = tag; 1091 } 1092 } 1093 } 1094 /* if we're not on our level yet, process this value. otherwise skip over it */ 1095 astream++; 1096 if ((length = asn1length(&astream)) < 0) { 1097 return(-1); 1098 } 1099 if (levels == level) { 1100 astream += length; 1101 } 1102 } 1103 return(-1); 1104 } 1105 1106 /* 1107 * Routines that validate a TGS request; checks a lot of things. :-) 1108 * 1109 * Returns a Kerberos protocol error number, which is _not_ the same 1110 * as a com_err error number! 1111 */ 1112 #define TGS_OPTIONS_HANDLED (KDC_OPT_FORWARDABLE | KDC_OPT_FORWARDED | \ 1113 KDC_OPT_PROXIABLE | KDC_OPT_PROXY | \ 1114 KDC_OPT_ALLOW_POSTDATE | KDC_OPT_POSTDATED | \ 1115 KDC_OPT_RENEWABLE | KDC_OPT_RENEWABLE_OK | \ 1116 KDC_OPT_ENC_TKT_IN_SKEY | KDC_OPT_RENEW | \ 1117 KDC_OPT_VALIDATE) 1118 1119 #define NO_TGT_OPTION (KDC_OPT_FORWARDED | KDC_OPT_PROXY | KDC_OPT_RENEW | \ 1120 KDC_OPT_VALIDATE) 1121 1122 int 1123 validate_tgs_request(register krb5_kdc_req *request, krb5_db_entry server, 1124 krb5_ticket *ticket, krb5_timestamp kdc_time, 1125 const char **status) 1126 { 1127 int errcode; 1128 int st_idx = 0; 1129 1130 /* 1131 * If an illegal option is set, ignore it. 1132 */ 1133 request->kdc_options &= TGS_OPTIONS_HANDLED; 1134 1135 /* Check to see if server has expired */ 1136 if (server.expiration && server.expiration < kdc_time) { 1137 *status = "SERVICE EXPIRED"; 1138 return(KDC_ERR_SERVICE_EXP); 1139 } 1140 1141 /* 1142 * Verify that the server principal in authdat->ticket is correct 1143 * (either the ticket granting service or the service that was 1144 * originally requested) 1145 */ 1146 if (request->kdc_options & NO_TGT_OPTION) { 1147 if (!krb5_principal_compare(kdc_context, ticket->server, request->server)) { 1148 *status = "SERVER DIDN'T MATCH TICKET FOR RENEW/FORWARD/ETC"; 1149 return(KDC_ERR_SERVER_NOMATCH); 1150 } 1151 } else { 1152 /* 1153 * OK, we need to validate the krbtgt service in the ticket. 1154 * 1155 * The krbtgt service is of the form: 1156 * krbtgt/realm-A@realm-B 1157 * 1158 * Realm A is the "server realm"; the realm of the 1159 * server of the requested ticket must match this realm. 1160 * Of course, it should be a realm serviced by this KDC. 1161 * 1162 * Realm B is the "client realm"; this is what should be 1163 * added to the transited field. (which is done elsewhere) 1164 */ 1165 1166 /* Make sure there are two components... */ 1167 if (krb5_princ_size(kdc_context, ticket->server) != 2) { 1168 *status = "BAD TGS SERVER LENGTH"; 1169 return KRB_AP_ERR_NOT_US; 1170 } 1171 /* ...that the first component is krbtgt... */ 1172 if (!krb5_is_tgs_principal(ticket->server)) { 1173 *status = "BAD TGS SERVER NAME"; 1174 return KRB_AP_ERR_NOT_US; 1175 } 1176 /* ...and that the second component matches the server realm... */ 1177 if ((krb5_princ_size(kdc_context, ticket->server) <= 1) || 1178 (krb5_princ_component(kdc_context, ticket->server, 1)->length != 1179 krb5_princ_realm(kdc_context, request->server)->length) || 1180 memcmp(krb5_princ_component(kdc_context, ticket->server, 1)->data, 1181 krb5_princ_realm(kdc_context, request->server)->data, 1182 krb5_princ_realm(kdc_context, request->server)->length)) { 1183 *status = "BAD TGS SERVER INSTANCE"; 1184 return KRB_AP_ERR_NOT_US; 1185 } 1186 /* XXX add check that second component must match locally 1187 * supported realm? 1188 */ 1189 1190 /* Server must allow TGS based issuances */ 1191 if (isflagset(server.attributes, KRB5_KDB_DISALLOW_TGT_BASED)) { 1192 *status = "TGT BASED NOT ALLOWED"; 1193 return(KDC_ERR_POLICY); 1194 } 1195 } 1196 1197 /* TGS must be forwardable to get forwarded or forwardable ticket */ 1198 if ((isflagset(request->kdc_options, KDC_OPT_FORWARDED) || 1199 isflagset(request->kdc_options, KDC_OPT_FORWARDABLE)) && 1200 !isflagset(ticket->enc_part2->flags, TKT_FLG_FORWARDABLE)) { 1201 *status = "TGT NOT FORWARDABLE"; 1202 1203 return KDC_ERR_BADOPTION; 1204 } 1205 1206 /* TGS must be proxiable to get proxiable ticket */ 1207 if ((isflagset(request->kdc_options, KDC_OPT_PROXY) || 1208 isflagset(request->kdc_options, KDC_OPT_PROXIABLE)) && 1209 !isflagset(ticket->enc_part2->flags, TKT_FLG_PROXIABLE)) { 1210 *status = "TGT NOT PROXIABLE"; 1211 return KDC_ERR_BADOPTION; 1212 } 1213 1214 /* TGS must allow postdating to get postdated ticket */ 1215 if ((isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE) || 1216 isflagset(request->kdc_options, KDC_OPT_POSTDATED)) && 1217 !isflagset(ticket->enc_part2->flags, TKT_FLG_MAY_POSTDATE)) { 1218 *status = "TGT NOT POSTDATABLE"; 1219 return KDC_ERR_BADOPTION; 1220 } 1221 1222 /* can only validate invalid tix */ 1223 if (isflagset(request->kdc_options, KDC_OPT_VALIDATE) && 1224 !isflagset(ticket->enc_part2->flags, TKT_FLG_INVALID)) { 1225 *status = "VALIDATE VALID TICKET"; 1226 return KDC_ERR_BADOPTION; 1227 } 1228 1229 /* can only renew renewable tix */ 1230 if ((isflagset(request->kdc_options, KDC_OPT_RENEW) || 1231 isflagset(request->kdc_options, KDC_OPT_RENEWABLE)) && 1232 !isflagset(ticket->enc_part2->flags, TKT_FLG_RENEWABLE)) { 1233 *status = "TICKET NOT RENEWABLE"; 1234 return KDC_ERR_BADOPTION; 1235 } 1236 1237 /* can not proxy ticket granting tickets */ 1238 if (isflagset(request->kdc_options, KDC_OPT_PROXY) && 1239 (!request->server->data || 1240 request->server->data[0].length != KRB5_TGS_NAME_SIZE || 1241 memcmp(request->server->data[0].data, KRB5_TGS_NAME, 1242 KRB5_TGS_NAME_SIZE))) { 1243 *status = "CAN'T PROXY TGT"; 1244 return KDC_ERR_BADOPTION; 1245 } 1246 1247 /* Server must allow forwardable tickets */ 1248 if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE) && 1249 isflagset(server.attributes, KRB5_KDB_DISALLOW_FORWARDABLE)) { 1250 *status = "NON-FORWARDABLE TICKET"; 1251 return(KDC_ERR_POLICY); 1252 } 1253 1254 /* Server must allow renewable tickets */ 1255 if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE) && 1256 isflagset(server.attributes, KRB5_KDB_DISALLOW_RENEWABLE)) { 1257 *status = "NON-RENEWABLE TICKET"; 1258 return(KDC_ERR_POLICY); 1259 } 1260 1261 /* Server must allow proxiable tickets */ 1262 if (isflagset(request->kdc_options, KDC_OPT_PROXIABLE) && 1263 isflagset(server.attributes, KRB5_KDB_DISALLOW_PROXIABLE)) { 1264 *status = "NON-PROXIABLE TICKET"; 1265 return(KDC_ERR_POLICY); 1266 } 1267 1268 /* Server must allow postdated tickets */ 1269 if (isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE) && 1270 isflagset(server.attributes, KRB5_KDB_DISALLOW_POSTDATED)) { 1271 *status = "NON-POSTDATABLE TICKET"; 1272 return(KDC_ERR_CANNOT_POSTDATE); 1273 } 1274 1275 /* Server must allow DUP SKEY requests */ 1276 if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY) && 1277 isflagset(server.attributes, KRB5_KDB_DISALLOW_DUP_SKEY)) { 1278 *status = "DUP_SKEY DISALLOWED"; 1279 return(KDC_ERR_POLICY); 1280 } 1281 1282 /* Server must not be locked out */ 1283 if (isflagset(server.attributes, KRB5_KDB_DISALLOW_ALL_TIX)) { 1284 *status = "SERVER LOCKED OUT"; 1285 return(KDC_ERR_S_PRINCIPAL_UNKNOWN); 1286 } 1287 1288 /* Server must be allowed to be a service */ 1289 if (isflagset(server.attributes, KRB5_KDB_DISALLOW_SVR)) { 1290 *status = "SERVER NOT ALLOWED"; 1291 return(KDC_ERR_S_PRINCIPAL_UNKNOWN); 1292 } 1293 1294 /* Check the hot list */ 1295 if (check_hot_list(ticket)) { 1296 *status = "HOT_LIST"; 1297 return(KRB_AP_ERR_REPEAT); 1298 } 1299 1300 /* Check the start time vs. the KDC time */ 1301 if (isflagset(request->kdc_options, KDC_OPT_VALIDATE)) { 1302 if (ticket->enc_part2->times.starttime > kdc_time) { 1303 *status = "NOT_YET_VALID"; 1304 return(KRB_AP_ERR_TKT_NYV); 1305 } 1306 } 1307 1308 /* 1309 * Check the renew_till time. The endtime was already 1310 * been checked in the initial authentication check. 1311 */ 1312 if (isflagset(request->kdc_options, KDC_OPT_RENEW) && 1313 (ticket->enc_part2->times.renew_till < kdc_time)) { 1314 *status = "TKT_EXPIRED"; 1315 return(KRB_AP_ERR_TKT_EXPIRED); 1316 } 1317 1318 /* 1319 * Checks for ENC_TKT_IN_SKEY: 1320 * 1321 * (1) Make sure the second ticket exists 1322 * (2) Make sure it is a ticket granting ticket 1323 */ 1324 if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY)) { 1325 if (!request->second_ticket || 1326 !request->second_ticket[st_idx]) { 1327 *status = "NO_2ND_TKT"; 1328 return(KDC_ERR_BADOPTION); 1329 } 1330 if (!krb5_principal_compare(kdc_context, request->second_ticket[st_idx]->server, 1331 tgs_server)) { 1332 *status = "2ND_TKT_NOT_TGS"; 1333 return(KDC_ERR_POLICY); 1334 } 1335 st_idx++; 1336 } 1337 1338 /* Check for hardware preauthentication */ 1339 if (isflagset(server.attributes, KRB5_KDB_REQUIRES_HW_AUTH) && 1340 !isflagset(ticket->enc_part2->flags,TKT_FLG_HW_AUTH)) { 1341 *status = "NO HW PREAUTH"; 1342 return KRB_ERR_GENERIC; 1343 } 1344 1345 /* Check for any kind of preauthentication */ 1346 if (isflagset(server.attributes, KRB5_KDB_REQUIRES_PRE_AUTH) && 1347 !isflagset(ticket->enc_part2->flags, TKT_FLG_PRE_AUTH)) { 1348 *status = "NO PREAUTH"; 1349 return KRB_ERR_GENERIC; 1350 } 1351 1352 /* 1353 * Check local policy 1354 */ 1355 errcode = against_local_policy_tgs(request, server, ticket, status); 1356 if (errcode) 1357 return errcode; 1358 1359 1360 return 0; 1361 } 1362 1363 /* 1364 * This function returns 1 if the dbentry has a key for a specified 1365 * keytype, and 0 if not. 1366 */ 1367 int 1368 dbentry_has_key_for_enctype(krb5_context context, krb5_db_entry *client, 1369 krb5_enctype enctype) 1370 { 1371 krb5_error_code retval; 1372 krb5_key_data *datap; 1373 1374 retval = krb5_dbe_find_enctype(context, client, enctype, 1375 -1, 0, &datap); 1376 if (retval) 1377 return 0; 1378 else 1379 return 1; 1380 } 1381 1382 /* 1383 * This function returns 1 if the entity referenced by this 1384 * structure can support the a particular encryption system, and 0 if 1385 * not. 1386 * 1387 * XXX eventually this information should be looked up in the 1388 * database. Since it isn't, we use some hueristics and attribute 1389 * options bits for now. 1390 */ 1391 int 1392 dbentry_supports_enctype(krb5_context context, krb5_db_entry *client, 1393 krb5_enctype enctype) 1394 { 1395 /* 1396 * If it's DES_CBC_MD5, there's a bit in the attribute mask which 1397 * checks to see if we support it. 1398 * 1399 * In theory everything's supposed to support DES_CBC_MD5, but 1400 * that's not the reality.... 1401 */ 1402 1403 /* 1404 * We are assuming that all entries can support MD5; this information 1405 * need not be kept in the database. 1406 */ 1407 1408 1409 if (enctype == ENCTYPE_DES_CBC_MD5) 1410 return 1; 1411 1412 /* 1413 * XXX we assume everything can understand DES_CBC_CRC 1414 */ 1415 if (enctype == ENCTYPE_DES_CBC_CRC) 1416 return 1; 1417 1418 /* 1419 * If we have a key for the encryption system, we assume it's 1420 * supported. 1421 */ 1422 return dbentry_has_key_for_enctype(context, client, enctype); 1423 } 1424 1425 /* 1426 * This function returns the keytype which should be selected for the 1427 * session key. It is based on the ordered list which the user 1428 * requested, and what the KDC and the application server can support. 1429 */ 1430 krb5_enctype 1431 select_session_keytype(krb5_context context, krb5_db_entry *server, 1432 int nktypes, krb5_enctype *ktype) 1433 { 1434 int i; 1435 1436 for (i = 0; i < nktypes; i++) { 1437 if (!krb5_c_valid_enctype(ktype[i])) 1438 continue; 1439 1440 if (!krb5_is_permitted_enctype(context, ktype[i])) 1441 continue; 1442 1443 if (dbentry_supports_enctype(context, server, ktype[i])) 1444 return ktype[i]; 1445 } 1446 return 0; 1447 } 1448 1449 /* 1450 * This function returns salt information for a particular client_key 1451 */ 1452 krb5_error_code 1453 get_salt_from_key(krb5_context context, krb5_principal client, 1454 krb5_key_data *client_key, krb5_data *salt) 1455 { 1456 krb5_error_code retval; 1457 krb5_data * realm; 1458 1459 salt->data = 0; 1460 salt->length = SALT_TYPE_NO_LENGTH; 1461 1462 if (client_key->key_data_ver == 1) 1463 return 0; 1464 1465 switch (client_key->key_data_type[1]) { 1466 case KRB5_KDB_SALTTYPE_NORMAL: 1467 break; 1468 case KRB5_KDB_SALTTYPE_V4: 1469 /* send an empty (V4) salt */ 1470 salt->data = 0; 1471 salt->length = 0; 1472 break; 1473 case KRB5_KDB_SALTTYPE_NOREALM: 1474 if ((retval = krb5_principal2salt_norealm(context, client, salt))) 1475 return retval; 1476 break; 1477 case KRB5_KDB_SALTTYPE_AFS3: 1478 /* send the same salt as with onlyrealm - but with no type info, 1479 we just hope they figure it out on the other end. */ 1480 /* fall through to onlyrealm: */ 1481 case KRB5_KDB_SALTTYPE_ONLYREALM: 1482 realm = krb5_princ_realm(context, client); 1483 salt->length = realm->length; 1484 if ((salt->data = malloc(realm->length)) == NULL) 1485 return ENOMEM; 1486 memcpy(salt->data, realm->data, realm->length); 1487 break; 1488 case KRB5_KDB_SALTTYPE_SPECIAL: 1489 salt->length = client_key->key_data_length[1]; 1490 if ((salt->data = malloc(salt->length)) == NULL) 1491 return ENOMEM; 1492 memcpy(salt->data, client_key->key_data_contents[1], salt->length); 1493 break; 1494 } 1495 return 0; 1496 } 1497 1498 /* 1499 * Limit strings to a "reasonable" length to prevent crowding out of 1500 * other useful information in the log entry 1501 */ 1502 #define NAME_LENGTH_LIMIT 128 1503 1504 void limit_string(char *name) 1505 { 1506 int i; 1507 1508 if (!name) 1509 return; 1510 1511 if (strlen(name) < NAME_LENGTH_LIMIT) 1512 return; 1513 1514 i = NAME_LENGTH_LIMIT-4; 1515 name[i++] = '.'; 1516 name[i++] = '.'; 1517 name[i++] = '.'; 1518 name[i] = '\0'; 1519 return; 1520 } 1521 1522 /* 1523 * L10_2 = log10(2**x), rounded up; log10(2) ~= 0.301. 1524 */ 1525 #define L10_2(x) ((int)(((x * 301) + 999) / 1000)) 1526 1527 /* 1528 * Max length of sprintf("%ld") for an int of type T; includes leading 1529 * minus sign and terminating NUL. 1530 */ 1531 #define D_LEN(t) (L10_2(sizeof(t) * CHAR_BIT) + 2) 1532 1533 void 1534 ktypes2str(char *s, size_t len, int nktypes, krb5_enctype *ktype) 1535 { 1536 int i; 1537 char stmp[D_LEN(krb5_enctype) + 1]; 1538 char *p; 1539 1540 if (nktypes < 0 1541 || len < (sizeof(" etypes {...}") + D_LEN(int))) { 1542 *s = '\0'; 1543 return; 1544 } 1545 1546 sprintf(s, "%d etypes {", nktypes); 1547 for (i = 0; i < nktypes; i++) { 1548 sprintf(stmp, "%s%ld", i ? " " : "", (long)ktype[i]); 1549 if (strlen(s) + strlen(stmp) + sizeof("}") > len) 1550 break; 1551 strcat(s, stmp); 1552 } 1553 if (i < nktypes) { 1554 /* 1555 * We broke out of the loop. Try to truncate the list. 1556 */ 1557 p = s + strlen(s); 1558 while (p - s + sizeof("...}") > len) { 1559 while (p > s && *p != ' ' && *p != '{') 1560 *p-- = '\0'; 1561 if (p > s && *p == ' ') { 1562 *p-- = '\0'; 1563 continue; 1564 } 1565 } 1566 strcat(s, "..."); 1567 } 1568 strcat(s, "}"); 1569 return; 1570 } 1571 1572 void 1573 rep_etypes2str(char *s, size_t len, krb5_kdc_rep *rep) 1574 { 1575 char stmp[sizeof("ses=") + D_LEN(krb5_enctype)]; 1576 1577 if (len < (3 * D_LEN(krb5_enctype) 1578 + sizeof("etypes {rep= tkt= ses=}"))) { 1579 *s = '\0'; 1580 return; 1581 } 1582 1583 sprintf(s, "etypes {rep=%ld", (long)rep->enc_part.enctype); 1584 1585 if (rep->ticket != NULL) { 1586 sprintf(stmp, " tkt=%ld", (long)rep->ticket->enc_part.enctype); 1587 strcat(s, stmp); 1588 } 1589 1590 if (rep->ticket != NULL 1591 && rep->ticket->enc_part2 != NULL 1592 && rep->ticket->enc_part2->session != NULL) { 1593 sprintf(stmp, " ses=%ld", 1594 (long)rep->ticket->enc_part2->session->enctype); 1595 strcat(s, stmp); 1596 } 1597 strcat(s, "}"); 1598 return; 1599 } 1600