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