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 limit_string(sname); 411 krb5_klog_syslog(LOG_ERR,"TGS_REQ: UNKNOWN SERVER: server='%s'", 412 sname); 413 free(sname); 414 } 415 return(KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN); 416 } 417 retval = krb5_dbe_find_enctype(kdc_context, &server, 418 ticket->enc_part.enctype, -1, 419 ticket->enc_part.kvno, &server_key); 420 if (retval) 421 goto errout; 422 if (!server_key) { 423 retval = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; 424 goto errout; 425 } 426 *kvno = server_key->key_data_kvno; 427 if ((*key = (krb5_keyblock *)malloc(sizeof **key))) { 428 retval = krb5_dbekd_decrypt_key_data(kdc_context, &master_keyblock, 429 server_key, 430 *key, NULL); 431 } else 432 retval = ENOMEM; 433 errout: 434 krb5_db_free_principal(kdc_context, &server, nprincs); 435 return retval; 436 } 437 438 /* This probably wants to be updated if you support last_req stuff */ 439 440 static krb5_last_req_entry nolrentry = { KV5M_LAST_REQ_ENTRY, KRB5_LRQ_NONE, 0 }; 441 static krb5_last_req_entry *nolrarray[] = { &nolrentry, 0 }; 442 443 krb5_error_code 444 fetch_last_req_info(krb5_db_entry *dbentry, krb5_last_req_entry ***lrentry) 445 { 446 *lrentry = nolrarray; 447 return 0; 448 } 449 450 451 /* XXX! This is a temporary place-holder */ 452 453 krb5_error_code 454 check_hot_list(krb5_ticket *ticket) 455 { 456 return 0; 457 } 458 459 460 #define MAX_REALM_LN 500 461 462 463 /* 464 * subrealm - determine if r2 is a subrealm of r1 465 * 466 * SUBREALM takes two realms, r1 and r2, and 467 * determines if r2 is a subrealm of r1. 468 * r2 is a subrealm of r1 if (r1 is a prefix 469 * of r2 AND r1 and r2 begin with a /) or if 470 * (r1 is a suffix of r2 and neither r1 nor r2 471 * begin with a /). 472 * 473 * RETURNS: If r2 is a subrealm, and r1 is a prefix, the number 474 * of characters in the suffix of r2 is returned as a 475 * negative number. 476 * 477 * If r2 is a subrealm, and r1 is a suffix, the number 478 * of characters in the prefix of r2 is returned as a 479 * positive number. 480 * 481 * If r2 is not a subrealm, SUBREALM returns 0. 482 */ 483 static int 484 subrealm(char *r1, char *r2) 485 { 486 size_t l1,l2; 487 l1 = strlen(r1); 488 l2 = strlen(r2); 489 if(l2 <= l1) return(0); 490 if((*r1 == '/') && (*r2 == '/') && (strncmp(r1,r2,l1) == 0)) return(l1-l2); 491 if((*r1 != '/') && (*r2 != '/') && (strncmp(r1,r2+l2-l1,l1) == 0)) 492 return(l2-l1); 493 return(0); 494 } 495 496 /* 497 * add_to_transited Adds the name of the realm which issued the 498 * ticket granting ticket on which the new ticket to 499 * be issued is based (note that this is the same as 500 * the realm of the server listed in the ticket 501 * granting ticket. 502 * 503 * ASSUMPTIONS: This procedure assumes that the transited field from 504 * the existing ticket granting ticket already appears 505 * in compressed form. It will add the new realm while 506 * maintaining that form. As long as each successive 507 * realm is added using this (or a similar) routine, the 508 * transited field will be in compressed form. The 509 * basis step is an empty transited field which is, by 510 * its nature, in its most compressed form. 511 * 512 * ARGUMENTS: krb5_data *tgt_trans Transited field from TGT 513 * krb5_data *new_trans The transited field for the new ticket 514 * krb5_principal tgs Name of ticket granting server 515 * This includes the realm of the KDC 516 * that issued the ticket granting 517 * ticket. This is the realm that is 518 * to be added to the transited field. 519 * krb5_principal client Name of the client 520 * krb5_principal server The name of the requested server. 521 * This may be the an intermediate 522 * ticket granting server. 523 * 524 * The last two argument are needed since they are 525 * implicitly part of the transited field of the new ticket 526 * even though they are not explicitly listed. 527 * 528 * RETURNS: krb5_error_code - Success, or out of memory 529 * 530 * MODIFIES: new_trans: ->length will contain the length of the new 531 * transited field. 532 * 533 * If ->data was not null when this procedure 534 * is called, the memory referenced by ->data 535 * will be deallocated. 536 * 537 * Memory will be allocated for the new transited field 538 * ->data will be updated to point to the newly 539 * allocated memory. 540 * 541 * BUGS: The space allocated for the new transited field is the 542 * maximum that might be needed given the old transited field, 543 * and the realm to be added. This length is calculated 544 * assuming that no compression of the new realm is possible. 545 * This has no adverse consequences other than the allocation 546 * of more space than required. 547 * 548 * This procedure will not yet use the null subfield notation, 549 * and it will get confused if it sees it. 550 * 551 * This procedure does not check for quoted commas in realm 552 * names. 553 */ 554 555 krb5_error_code 556 add_to_transited(krb5_data *tgt_trans, krb5_data *new_trans, 557 krb5_principal tgs, krb5_principal client, 558 krb5_principal server) 559 { 560 krb5_error_code retval; 561 char *realm; 562 char *trans; 563 char *otrans, *otrans_ptr; 564 565 /* The following are for stepping through the transited field */ 566 567 char prev[MAX_REALM_LN]; 568 char next[MAX_REALM_LN]; 569 char current[MAX_REALM_LN]; 570 char exp[MAX_REALM_LN]; /* Expanded current realm name */ 571 572 int i; 573 int clst, nlst; /* count of last character in current and next */ 574 int pl, pl1; /* prefix length */ 575 int added; /* TRUE = new realm has been added */ 576 577 if (!(realm = (char *) malloc(krb5_princ_realm(kdc_context, tgs)->length+1))) { 578 return(ENOMEM); 579 } 580 memcpy(realm, krb5_princ_realm(kdc_context, tgs)->data, 581 krb5_princ_realm(kdc_context, tgs)->length); 582 realm[krb5_princ_realm(kdc_context, tgs)->length] = '\0'; 583 584 if (!(otrans = (char *) malloc(tgt_trans->length+1))) { 585 free(realm); 586 return(ENOMEM); 587 } 588 memcpy(otrans, tgt_trans->data, tgt_trans->length); 589 otrans[tgt_trans->length] = '\0'; 590 /* Keep track of start so we can free */ 591 otrans_ptr = otrans; 592 593 /* +1 for null, 594 +1 for extra comma which may be added between 595 +1 for potential space when leading slash in realm */ 596 if (!(trans = (char *) malloc(strlen(realm) + strlen(otrans) + 3))) { 597 retval = ENOMEM; 598 goto fail; 599 } 600 601 if (new_trans->data) free(new_trans->data); 602 new_trans->data = trans; 603 new_trans->length = 0; 604 605 trans[0] = '\0'; 606 607 /* For the purpose of appending, the realm preceding the first */ 608 /* realm in the transited field is considered the null realm */ 609 610 prev[0] = '\0'; 611 612 /* read field into current */ 613 for (i = 0; *otrans != '\0';) { 614 if (*otrans == '\\') { 615 if (*(++otrans) == '\0') 616 break; 617 else 618 continue; 619 } 620 if (*otrans == ',') { 621 otrans++; 622 break; 623 } 624 current[i++] = *otrans++; 625 if (i >= MAX_REALM_LN) { 626 retval = KRB5KRB_AP_ERR_ILL_CR_TKT; 627 goto fail; 628 } 629 } 630 current[i] = '\0'; 631 632 added = (krb5_princ_realm(kdc_context, client)->length == strlen(realm) && 633 !strncmp(krb5_princ_realm(kdc_context, client)->data, realm, strlen(realm))) || 634 (krb5_princ_realm(kdc_context, server)->length == strlen(realm) && 635 !strncmp(krb5_princ_realm(kdc_context, server)->data, realm, strlen(realm))); 636 637 while (current[0]) { 638 639 /* figure out expanded form of current name */ 640 641 clst = strlen(current) - 1; 642 if (current[0] == ' ') { 643 strncpy(exp, current+1, sizeof(exp) - 1); 644 exp[sizeof(exp) - 1] = '\0'; 645 } 646 else if ((current[0] == '/') && (prev[0] == '/')) { 647 strncpy(exp, prev, sizeof(exp) - 1); 648 exp[sizeof(exp) - 1] = '\0'; 649 if (strlen(exp) + strlen(current) + 1 >= MAX_REALM_LN) { 650 retval = KRB5KRB_AP_ERR_ILL_CR_TKT; 651 goto fail; 652 } 653 strncat(exp, current, sizeof(exp) - 1 - strlen(exp)); 654 } 655 else if (current[clst] == '.') { 656 strncpy(exp, current, sizeof(exp) - 1); 657 exp[sizeof(exp) - 1] = '\0'; 658 if (strlen(exp) + strlen(prev) + 1 >= MAX_REALM_LN) { 659 retval = KRB5KRB_AP_ERR_ILL_CR_TKT; 660 goto fail; 661 } 662 strncat(exp, prev, sizeof(exp) - 1 - strlen(exp)); 663 } 664 else { 665 strncpy(exp, current, sizeof(exp) - 1); 666 exp[sizeof(exp) - 1] = '\0'; 667 } 668 669 /* read field into next */ 670 for (i = 0; *otrans != '\0';) { 671 if (*otrans == '\\') { 672 if (*(++otrans) == '\0') 673 break; 674 else 675 continue; 676 } 677 if (*otrans == ',') { 678 otrans++; 679 break; 680 } 681 next[i++] = *otrans++; 682 if (i >= MAX_REALM_LN) { 683 retval = KRB5KRB_AP_ERR_ILL_CR_TKT; 684 goto fail; 685 } 686 } 687 next[i] = '\0'; 688 nlst = i - 1; 689 690 if (!strcmp(exp, realm)) added = TRUE; 691 692 /* If we still have to insert the new realm */ 693 694 if (!added) { 695 696 /* Is the next field compressed? If not, and if the new */ 697 /* realm is a subrealm of the current realm, compress */ 698 /* the new realm, and insert immediately following the */ 699 /* current one. Note that we can not do this if the next*/ 700 /* field is already compressed since it would mess up */ 701 /* what has already been done. In most cases, this is */ 702 /* not a problem because the realm to be added will be a */ 703 /* subrealm of the next field too, and we will catch */ 704 /* it in a future iteration. */ 705 706 if ((next[nlst] != '.') && (next[0] != '/') && 707 (pl = subrealm(exp, realm))) { 708 added = TRUE; 709 current[sizeof(current) - 1] = '\0'; 710 if (strlen(current) + (pl>0?pl:-pl) + 2 >= MAX_REALM_LN) { 711 retval = KRB5KRB_AP_ERR_ILL_CR_TKT; 712 goto fail; 713 } 714 strncat(current, ",", sizeof(current) - 1 - strlen(current)); 715 if (pl > 0) { 716 strncat(current, realm, (unsigned) pl); 717 } 718 else { 719 strncat(current, realm+strlen(realm)+pl, (unsigned) (-pl)); 720 } 721 } 722 723 /* Whether or not the next field is compressed, if the */ 724 /* realm to be added is a superrealm of the current realm,*/ 725 /* then the current realm can be compressed. First the */ 726 /* realm to be added must be compressed relative to the */ 727 /* previous realm (if possible), and then the current */ 728 /* realm compressed relative to the new realm. Note that */ 729 /* if the realm to be added is also a superrealm of the */ 730 /* previous realm, it would have been added earlier, and */ 731 /* we would not reach this step this time around. */ 732 733 else if ((pl = subrealm(realm, exp))) { 734 added = TRUE; 735 current[0] = '\0'; 736 if ((pl1 = subrealm(prev,realm))) { 737 if (strlen(current) + (pl1>0?pl1:-pl1) + 1 >= MAX_REALM_LN) { 738 retval = KRB5KRB_AP_ERR_ILL_CR_TKT; 739 goto fail; 740 } 741 if (pl1 > 0) { 742 strncat(current, realm, (unsigned) pl1); 743 } 744 else { 745 strncat(current, realm+strlen(realm)+pl1, (unsigned) (-pl1)); 746 } 747 } 748 else { /* If not a subrealm */ 749 if ((realm[0] == '/') && prev[0]) { 750 if (strlen(current) + 2 >= MAX_REALM_LN) { 751 retval = KRB5KRB_AP_ERR_ILL_CR_TKT; 752 goto fail; 753 } 754 strncat(current, " ", sizeof(current) - 1 - strlen(current)); 755 current[sizeof(current) - 1] = '\0'; 756 } 757 if (strlen(current) + strlen(realm) + 1 >= MAX_REALM_LN) { 758 retval = KRB5KRB_AP_ERR_ILL_CR_TKT; 759 goto fail; 760 } 761 strncat(current, realm, sizeof(current) - 1 - strlen(current)); 762 current[sizeof(current) - 1] = '\0'; 763 } 764 if (strlen(current) + (pl>0?pl:-pl) + 2 >= MAX_REALM_LN) { 765 retval = KRB5KRB_AP_ERR_ILL_CR_TKT; 766 goto fail; 767 } 768 strncat(current,",", sizeof(current) - 1 - strlen(current)); 769 current[sizeof(current) - 1] = '\0'; 770 if (pl > 0) { 771 strncat(current, exp, (unsigned) pl); 772 } 773 else { 774 strncat(current, exp+strlen(exp)+pl, (unsigned)(-pl)); 775 } 776 } 777 } 778 779 if (new_trans->length != 0) { 780 if (strlen(trans) + 2 >= MAX_REALM_LN) { 781 retval = KRB5KRB_AP_ERR_ILL_CR_TKT; 782 goto fail; 783 } 784 strcat(trans, ","); 785 } 786 if (strlen(trans) + strlen(current) + 1 >= MAX_REALM_LN) { 787 retval = KRB5KRB_AP_ERR_ILL_CR_TKT; 788 goto fail; 789 } 790 strcat(trans, current); 791 new_trans->length = strlen(trans); 792 793 strncpy(prev, exp, sizeof(prev) - 1); 794 prev[sizeof(prev) - 1] = '\0'; 795 strncpy(current, next, sizeof(current) - 1); 796 current[sizeof(current) - 1] = '\0'; 797 } 798 799 if (!added) { 800 if (new_trans->length != 0) { 801 if (strlen(trans) + 2 >= MAX_REALM_LN) { 802 retval = KRB5KRB_AP_ERR_ILL_CR_TKT; 803 goto fail; 804 } 805 strcat(trans, ","); 806 } 807 if((realm[0] == '/') && trans[0]) { 808 if (strlen(trans) + 2 >= MAX_REALM_LN) { 809 retval = KRB5KRB_AP_ERR_ILL_CR_TKT; 810 goto fail; 811 } 812 strcat(trans, " "); 813 } 814 if (strlen(trans) + strlen(realm) + 1 >= MAX_REALM_LN) { 815 retval = KRB5KRB_AP_ERR_ILL_CR_TKT; 816 goto fail; 817 } 818 strcat(trans, realm); 819 new_trans->length = strlen(trans); 820 } 821 822 retval = 0; 823 fail: 824 free(realm); 825 free(otrans_ptr); 826 return (retval); 827 } 828 829 /* 830 * Routines that validate a AS request; checks a lot of things. :-) 831 * 832 * Returns a Kerberos protocol error number, which is _not_ the same 833 * as a com_err error number! 834 */ 835 #define AS_INVALID_OPTIONS (KDC_OPT_FORWARDED | KDC_OPT_PROXY |\ 836 KDC_OPT_VALIDATE | KDC_OPT_RENEW | KDC_OPT_ENC_TKT_IN_SKEY) 837 int 838 validate_as_request(register krb5_kdc_req *request, krb5_db_entry client, 839 krb5_db_entry server, krb5_timestamp kdc_time, 840 const char **status) 841 { 842 int errcode; 843 844 /* 845 * If an option is set that is only allowed in TGS requests, complain. 846 */ 847 if (request->kdc_options & AS_INVALID_OPTIONS) { 848 *status = "INVALID AS OPTIONS"; 849 return KDC_ERR_BADOPTION; 850 } 851 852 /* The client's password must not be expired, unless the server is 853 a KRB5_KDC_PWCHANGE_SERVICE. */ 854 if (client.pw_expiration && client.pw_expiration < kdc_time && 855 !isflagset(server.attributes, KRB5_KDB_PWCHANGE_SERVICE)) { 856 *status = "CLIENT KEY EXPIRED"; 857 #ifdef KRBCONF_VAGUE_ERRORS 858 return(KRB_ERR_GENERIC); 859 #else 860 return(KDC_ERR_KEY_EXP); 861 #endif 862 } 863 864 /* The client must not be expired */ 865 if (client.expiration && client.expiration < kdc_time) { 866 *status = "CLIENT EXPIRED"; 867 #ifdef KRBCONF_VAGUE_ERRORS 868 return(KRB_ERR_GENERIC); 869 #else 870 return(KDC_ERR_NAME_EXP); 871 #endif 872 } 873 874 /* The server must not be expired */ 875 if (server.expiration && server.expiration < kdc_time) { 876 *status = "SERVICE EXPIRED"; 877 return(KDC_ERR_SERVICE_EXP); 878 } 879 880 /* 881 * If the client requires password changing, then only allow the 882 * pwchange service. 883 */ 884 if (isflagset(client.attributes, KRB5_KDB_REQUIRES_PWCHANGE) && 885 !isflagset(server.attributes, KRB5_KDB_PWCHANGE_SERVICE)) { 886 *status = "REQUIRED PWCHANGE"; 887 return(KDC_ERR_KEY_EXP); 888 } 889 890 /* Client and server must allow postdating tickets */ 891 if ((isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE) || 892 isflagset(request->kdc_options, KDC_OPT_POSTDATED)) && 893 (isflagset(client.attributes, KRB5_KDB_DISALLOW_POSTDATED) || 894 isflagset(server.attributes, KRB5_KDB_DISALLOW_POSTDATED))) { 895 *status = "POSTDATE NOT ALLOWED"; 896 return(KDC_ERR_CANNOT_POSTDATE); 897 } 898 899 /* Client and server must allow forwardable tickets */ 900 if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE) && 901 (isflagset(client.attributes, KRB5_KDB_DISALLOW_FORWARDABLE) || 902 isflagset(server.attributes, KRB5_KDB_DISALLOW_FORWARDABLE))) { 903 *status = "FORWARDABLE NOT ALLOWED"; 904 return(KDC_ERR_POLICY); 905 } 906 907 /* Client and server must allow renewable tickets */ 908 if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE) && 909 (isflagset(client.attributes, KRB5_KDB_DISALLOW_RENEWABLE) || 910 isflagset(server.attributes, KRB5_KDB_DISALLOW_RENEWABLE))) { 911 *status = "RENEWABLE NOT ALLOWED"; 912 return(KDC_ERR_POLICY); 913 } 914 915 /* Client and server must allow proxiable tickets */ 916 if (isflagset(request->kdc_options, KDC_OPT_PROXIABLE) && 917 (isflagset(client.attributes, KRB5_KDB_DISALLOW_PROXIABLE) || 918 isflagset(server.attributes, KRB5_KDB_DISALLOW_PROXIABLE))) { 919 *status = "PROXIABLE NOT ALLOWED"; 920 return(KDC_ERR_POLICY); 921 } 922 923 /* Check to see if client is locked out */ 924 if (isflagset(client.attributes, KRB5_KDB_DISALLOW_ALL_TIX)) { 925 *status = "CLIENT LOCKED OUT"; 926 return(KDC_ERR_C_PRINCIPAL_UNKNOWN); 927 } 928 929 /* Check to see if server is locked out */ 930 if (isflagset(server.attributes, KRB5_KDB_DISALLOW_ALL_TIX)) { 931 *status = "SERVICE LOCKED OUT"; 932 return(KDC_ERR_S_PRINCIPAL_UNKNOWN); 933 } 934 935 /* Check to see if server is allowed to be a service */ 936 if (isflagset(server.attributes, KRB5_KDB_DISALLOW_SVR)) { 937 *status = "SERVICE NOT ALLOWED"; 938 return(KDC_ERR_S_PRINCIPAL_UNKNOWN); 939 } 940 941 /* 942 * Check against local policy 943 */ 944 errcode = against_local_policy_as(request, server, client, 945 kdc_time, status); 946 if (errcode) 947 return errcode; 948 949 return 0; 950 } 951 952 #define ASN1_ID_CLASS (0xc0) 953 #define ASN1_ID_TYPE (0x20) 954 #define ASN1_ID_TAG (0x1f) 955 #define ASN1_CLASS_UNIV (0) 956 #define ASN1_CLASS_APP (1) 957 #define ASN1_CLASS_CTX (2) 958 #define ASN1_CLASS_PRIV (3) 959 #define asn1_id_constructed(x) (x & ASN1_ID_TYPE) 960 #define asn1_id_primitive(x) (!asn1_id_constructed(x)) 961 #define asn1_id_class(x) ((x & ASN1_ID_CLASS) >> 6) 962 #define asn1_id_tag(x) (x & ASN1_ID_TAG) 963 964 /* 965 * asn1length - return encoded length of value. 966 * 967 * passed a pointer into the asn.1 stream, which is updated 968 * to point right after the length bits. 969 * 970 * returns -1 on failure. 971 */ 972 static int 973 asn1length(unsigned char **astream) 974 { 975 int length; /* resulting length */ 976 int sublen; /* sublengths */ 977 int blen; /* bytes of length */ 978 unsigned char *p; /* substring searching */ 979 980 if (**astream & 0x80) { 981 blen = **astream & 0x7f; 982 if (blen > 3) { 983 return(-1); 984 } 985 for (++*astream, length = 0; blen; ++*astream, blen--) { 986 length = (length << 8) | **astream; 987 } 988 if (length == 0) { 989 /* indefinite length, figure out by hand */ 990 p = *astream; 991 p++; 992 while (1) { 993 /* compute value length. */ 994 if ((sublen = asn1length(&p)) < 0) { 995 return(-1); 996 } 997 p += sublen; 998 /* check for termination */ 999 if ((!*p++) && (!*p)) { 1000 p++; 1001 break; 1002 } 1003 } 1004 length = p - *astream; 1005 } 1006 } else { 1007 length = **astream; 1008 ++*astream; 1009 } 1010 return(length); 1011 } 1012 1013 /* 1014 * fetch_asn1_field - return raw asn.1 stream of subfield. 1015 * 1016 * this routine is passed a context-dependent tag number and "level" and returns 1017 * the size and length of the corresponding level subfield. 1018 * 1019 * levels and are numbered starting from 1. 1020 * 1021 * returns 0 on success, -1 otherwise. 1022 */ 1023 int 1024 fetch_asn1_field(unsigned char *astream, unsigned int level, 1025 unsigned int field, krb5_data *data) 1026 { 1027 unsigned char *estream; /* end of stream */ 1028 int classes; /* # classes seen so far this level */ 1029 unsigned int levels = 0; /* levels seen so far */ 1030 int lastlevel = 1000; /* last level seen */ 1031 int length; /* various lengths */ 1032 int tag; /* tag number */ 1033 unsigned char savelen; /* saved length of our field */ 1034 1035 classes = -1; 1036 /* we assume that the first identifier/length will tell us 1037 how long the entire stream is. */ 1038 astream++; 1039 estream = astream; 1040 if ((length = asn1length(&astream)) < 0) { 1041 return(-1); 1042 } 1043 estream += length; 1044 /* search down the stream, checking identifiers. we process identifiers 1045 until we hit the "level" we want, and then process that level for our 1046 subfield, always making sure we don't go off the end of the stream. */ 1047 while (astream < estream) { 1048 if (!asn1_id_constructed(*astream)) { 1049 return(-1); 1050 } 1051 if (asn1_id_class(*astream) == ASN1_CLASS_CTX) { 1052 if ((tag = (int)asn1_id_tag(*astream)) <= lastlevel) { 1053 levels++; 1054 classes = -1; 1055 } 1056 lastlevel = tag; 1057 if (levels == level) { 1058 /* in our context-dependent class, is this the one we're looking for ? */ 1059 if (tag == field) { 1060 /* return length and data */ 1061 astream++; 1062 savelen = *astream; 1063 if ((data->length = asn1length(&astream)) < 0) { 1064 return(-1); 1065 } 1066 /* if the field length is indefinite, we will have to subtract two 1067 (terminating octets) from the length returned since we don't want 1068 to pass any info from the "wrapper" back. asn1length will always return 1069 the *total* length of the field, not just what's contained in it */ 1070 if ((savelen & 0xff) == 0x80) { 1071 data->length -=2 ; 1072 } 1073 data->data = (char *)astream; 1074 return(0); 1075 } else if (tag <= classes) { 1076 /* we've seen this class before, something must be wrong */ 1077 return(-1); 1078 } else { 1079 classes = tag; 1080 } 1081 } 1082 } 1083 /* if we're not on our level yet, process this value. otherwise skip over it */ 1084 astream++; 1085 if ((length = asn1length(&astream)) < 0) { 1086 return(-1); 1087 } 1088 if (levels == level) { 1089 astream += length; 1090 } 1091 } 1092 return(-1); 1093 } 1094 1095 /* 1096 * Routines that validate a TGS request; checks a lot of things. :-) 1097 * 1098 * Returns a Kerberos protocol error number, which is _not_ the same 1099 * as a com_err error number! 1100 */ 1101 #define TGS_OPTIONS_HANDLED (KDC_OPT_FORWARDABLE | KDC_OPT_FORWARDED | \ 1102 KDC_OPT_PROXIABLE | KDC_OPT_PROXY | \ 1103 KDC_OPT_ALLOW_POSTDATE | KDC_OPT_POSTDATED | \ 1104 KDC_OPT_RENEWABLE | KDC_OPT_RENEWABLE_OK | \ 1105 KDC_OPT_ENC_TKT_IN_SKEY | KDC_OPT_RENEW | \ 1106 KDC_OPT_VALIDATE) 1107 1108 #define NO_TGT_OPTION (KDC_OPT_FORWARDED | KDC_OPT_PROXY | KDC_OPT_RENEW | \ 1109 KDC_OPT_VALIDATE) 1110 1111 int 1112 validate_tgs_request(register krb5_kdc_req *request, krb5_db_entry server, 1113 krb5_ticket *ticket, krb5_timestamp kdc_time, 1114 const char **status) 1115 { 1116 int errcode; 1117 int st_idx = 0; 1118 1119 /* 1120 * If an illegal option is set, ignore it. 1121 */ 1122 request->kdc_options &= TGS_OPTIONS_HANDLED; 1123 1124 /* Check to see if server has expired */ 1125 if (server.expiration && server.expiration < kdc_time) { 1126 *status = "SERVICE EXPIRED"; 1127 return(KDC_ERR_SERVICE_EXP); 1128 } 1129 1130 /* 1131 * Verify that the server principal in authdat->ticket is correct 1132 * (either the ticket granting service or the service that was 1133 * originally requested) 1134 */ 1135 if (request->kdc_options & NO_TGT_OPTION) { 1136 if (!krb5_principal_compare(kdc_context, ticket->server, request->server)) { 1137 *status = "SERVER DIDN'T MATCH TICKET FOR RENEW/FORWARD/ETC"; 1138 return(KDC_ERR_SERVER_NOMATCH); 1139 } 1140 } else { 1141 /* 1142 * OK, we need to validate the krbtgt service in the ticket. 1143 * 1144 * The krbtgt service is of the form: 1145 * krbtgt/realm-A@realm-B 1146 * 1147 * Realm A is the "server realm"; the realm of the 1148 * server of the requested ticket must match this realm. 1149 * Of course, it should be a realm serviced by this KDC. 1150 * 1151 * Realm B is the "client realm"; this is what should be 1152 * added to the transited field. (which is done elsewhere) 1153 */ 1154 1155 /* Make sure there are two components... */ 1156 if (krb5_princ_size(kdc_context, ticket->server) != 2) { 1157 *status = "BAD TGS SERVER LENGTH"; 1158 return KRB_AP_ERR_NOT_US; 1159 } 1160 /* ...that the first component is krbtgt... */ 1161 if (!krb5_is_tgs_principal(ticket->server)) { 1162 *status = "BAD TGS SERVER NAME"; 1163 return KRB_AP_ERR_NOT_US; 1164 } 1165 /* ...and that the second component matches the server realm... */ 1166 if ((krb5_princ_size(kdc_context, ticket->server) <= 1) || 1167 (krb5_princ_component(kdc_context, ticket->server, 1)->length != 1168 krb5_princ_realm(kdc_context, request->server)->length) || 1169 memcmp(krb5_princ_component(kdc_context, ticket->server, 1)->data, 1170 krb5_princ_realm(kdc_context, request->server)->data, 1171 krb5_princ_realm(kdc_context, request->server)->length)) { 1172 *status = "BAD TGS SERVER INSTANCE"; 1173 return KRB_AP_ERR_NOT_US; 1174 } 1175 /* XXX add check that second component must match locally 1176 * supported realm? 1177 */ 1178 1179 /* Server must allow TGS based issuances */ 1180 if (isflagset(server.attributes, KRB5_KDB_DISALLOW_TGT_BASED)) { 1181 *status = "TGT BASED NOT ALLOWED"; 1182 return(KDC_ERR_POLICY); 1183 } 1184 } 1185 1186 /* TGS must be forwardable to get forwarded or forwardable ticket */ 1187 if ((isflagset(request->kdc_options, KDC_OPT_FORWARDED) || 1188 isflagset(request->kdc_options, KDC_OPT_FORWARDABLE)) && 1189 !isflagset(ticket->enc_part2->flags, TKT_FLG_FORWARDABLE)) { 1190 *status = "TGT NOT FORWARDABLE"; 1191 1192 return KDC_ERR_BADOPTION; 1193 } 1194 1195 /* TGS must be proxiable to get proxiable ticket */ 1196 if ((isflagset(request->kdc_options, KDC_OPT_PROXY) || 1197 isflagset(request->kdc_options, KDC_OPT_PROXIABLE)) && 1198 !isflagset(ticket->enc_part2->flags, TKT_FLG_PROXIABLE)) { 1199 *status = "TGT NOT PROXIABLE"; 1200 return KDC_ERR_BADOPTION; 1201 } 1202 1203 /* TGS must allow postdating to get postdated ticket */ 1204 if ((isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE) || 1205 isflagset(request->kdc_options, KDC_OPT_POSTDATED)) && 1206 !isflagset(ticket->enc_part2->flags, TKT_FLG_MAY_POSTDATE)) { 1207 *status = "TGT NOT POSTDATABLE"; 1208 return KDC_ERR_BADOPTION; 1209 } 1210 1211 /* can only validate invalid tix */ 1212 if (isflagset(request->kdc_options, KDC_OPT_VALIDATE) && 1213 !isflagset(ticket->enc_part2->flags, TKT_FLG_INVALID)) { 1214 *status = "VALIDATE VALID TICKET"; 1215 return KDC_ERR_BADOPTION; 1216 } 1217 1218 /* can only renew renewable tix */ 1219 if ((isflagset(request->kdc_options, KDC_OPT_RENEW) || 1220 isflagset(request->kdc_options, KDC_OPT_RENEWABLE)) && 1221 !isflagset(ticket->enc_part2->flags, TKT_FLG_RENEWABLE)) { 1222 *status = "TICKET NOT RENEWABLE"; 1223 return KDC_ERR_BADOPTION; 1224 } 1225 1226 /* can not proxy ticket granting tickets */ 1227 if (isflagset(request->kdc_options, KDC_OPT_PROXY) && 1228 (!request->server->data || 1229 request->server->data[0].length != KRB5_TGS_NAME_SIZE || 1230 memcmp(request->server->data[0].data, KRB5_TGS_NAME, 1231 KRB5_TGS_NAME_SIZE))) { 1232 *status = "CAN'T PROXY TGT"; 1233 return KDC_ERR_BADOPTION; 1234 } 1235 1236 /* Server must allow forwardable tickets */ 1237 if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE) && 1238 isflagset(server.attributes, KRB5_KDB_DISALLOW_FORWARDABLE)) { 1239 *status = "NON-FORWARDABLE TICKET"; 1240 return(KDC_ERR_POLICY); 1241 } 1242 1243 /* Server must allow renewable tickets */ 1244 if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE) && 1245 isflagset(server.attributes, KRB5_KDB_DISALLOW_RENEWABLE)) { 1246 *status = "NON-RENEWABLE TICKET"; 1247 return(KDC_ERR_POLICY); 1248 } 1249 1250 /* Server must allow proxiable tickets */ 1251 if (isflagset(request->kdc_options, KDC_OPT_PROXIABLE) && 1252 isflagset(server.attributes, KRB5_KDB_DISALLOW_PROXIABLE)) { 1253 *status = "NON-PROXIABLE TICKET"; 1254 return(KDC_ERR_POLICY); 1255 } 1256 1257 /* Server must allow postdated tickets */ 1258 if (isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE) && 1259 isflagset(server.attributes, KRB5_KDB_DISALLOW_POSTDATED)) { 1260 *status = "NON-POSTDATABLE TICKET"; 1261 return(KDC_ERR_CANNOT_POSTDATE); 1262 } 1263 1264 /* Server must allow DUP SKEY requests */ 1265 if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY) && 1266 isflagset(server.attributes, KRB5_KDB_DISALLOW_DUP_SKEY)) { 1267 *status = "DUP_SKEY DISALLOWED"; 1268 return(KDC_ERR_POLICY); 1269 } 1270 1271 /* Server must not be locked out */ 1272 if (isflagset(server.attributes, KRB5_KDB_DISALLOW_ALL_TIX)) { 1273 *status = "SERVER LOCKED OUT"; 1274 return(KDC_ERR_S_PRINCIPAL_UNKNOWN); 1275 } 1276 1277 /* Server must be allowed to be a service */ 1278 if (isflagset(server.attributes, KRB5_KDB_DISALLOW_SVR)) { 1279 *status = "SERVER NOT ALLOWED"; 1280 return(KDC_ERR_S_PRINCIPAL_UNKNOWN); 1281 } 1282 1283 /* Check the hot list */ 1284 if (check_hot_list(ticket)) { 1285 *status = "HOT_LIST"; 1286 return(KRB_AP_ERR_REPEAT); 1287 } 1288 1289 /* Check the start time vs. the KDC time */ 1290 if (isflagset(request->kdc_options, KDC_OPT_VALIDATE)) { 1291 if (ticket->enc_part2->times.starttime > kdc_time) { 1292 *status = "NOT_YET_VALID"; 1293 return(KRB_AP_ERR_TKT_NYV); 1294 } 1295 } 1296 1297 /* 1298 * Check the renew_till time. The endtime was already 1299 * been checked in the initial authentication check. 1300 */ 1301 if (isflagset(request->kdc_options, KDC_OPT_RENEW) && 1302 (ticket->enc_part2->times.renew_till < kdc_time)) { 1303 *status = "TKT_EXPIRED"; 1304 return(KRB_AP_ERR_TKT_EXPIRED); 1305 } 1306 1307 /* 1308 * Checks for ENC_TKT_IN_SKEY: 1309 * 1310 * (1) Make sure the second ticket exists 1311 * (2) Make sure it is a ticket granting ticket 1312 */ 1313 if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY)) { 1314 if (!request->second_ticket || 1315 !request->second_ticket[st_idx]) { 1316 *status = "NO_2ND_TKT"; 1317 return(KDC_ERR_BADOPTION); 1318 } 1319 if (!krb5_principal_compare(kdc_context, request->second_ticket[st_idx]->server, 1320 tgs_server)) { 1321 *status = "2ND_TKT_NOT_TGS"; 1322 return(KDC_ERR_POLICY); 1323 } 1324 st_idx++; 1325 } 1326 1327 /* Check for hardware preauthentication */ 1328 if (isflagset(server.attributes, KRB5_KDB_REQUIRES_HW_AUTH) && 1329 !isflagset(ticket->enc_part2->flags,TKT_FLG_HW_AUTH)) { 1330 *status = "NO HW PREAUTH"; 1331 return KRB_ERR_GENERIC; 1332 } 1333 1334 /* Check for any kind of preauthentication */ 1335 if (isflagset(server.attributes, KRB5_KDB_REQUIRES_PRE_AUTH) && 1336 !isflagset(ticket->enc_part2->flags, TKT_FLG_PRE_AUTH)) { 1337 *status = "NO PREAUTH"; 1338 return KRB_ERR_GENERIC; 1339 } 1340 1341 /* 1342 * Check local policy 1343 */ 1344 errcode = against_local_policy_tgs(request, server, ticket, status); 1345 if (errcode) 1346 return errcode; 1347 1348 1349 return 0; 1350 } 1351 1352 /* 1353 * This function returns 1 if the dbentry has a key for a specified 1354 * keytype, and 0 if not. 1355 */ 1356 int 1357 dbentry_has_key_for_enctype(krb5_context context, krb5_db_entry *client, 1358 krb5_enctype enctype) 1359 { 1360 krb5_error_code retval; 1361 krb5_key_data *datap; 1362 1363 retval = krb5_dbe_find_enctype(context, client, enctype, 1364 -1, 0, &datap); 1365 if (retval) 1366 return 0; 1367 else 1368 return 1; 1369 } 1370 1371 /* 1372 * This function returns 1 if the entity referenced by this 1373 * structure can support the a particular encryption system, and 0 if 1374 * not. 1375 * 1376 * XXX eventually this information should be looked up in the 1377 * database. Since it isn't, we use some hueristics and attribute 1378 * options bits for now. 1379 */ 1380 int 1381 dbentry_supports_enctype(krb5_context context, krb5_db_entry *client, 1382 krb5_enctype enctype) 1383 { 1384 /* 1385 * If it's DES_CBC_MD5, there's a bit in the attribute mask which 1386 * checks to see if we support it. 1387 * 1388 * In theory everything's supposed to support DES_CBC_MD5, but 1389 * that's not the reality.... 1390 */ 1391 1392 /* 1393 * We are assuming that all entries can support MD5; this information 1394 * need not be kept in the database. 1395 */ 1396 1397 1398 if (enctype == ENCTYPE_DES_CBC_MD5) 1399 return 1; 1400 1401 /* 1402 * XXX we assume everything can understand DES_CBC_CRC 1403 */ 1404 if (enctype == ENCTYPE_DES_CBC_CRC) 1405 return 1; 1406 1407 /* 1408 * If we have a key for the encryption system, we assume it's 1409 * supported. 1410 */ 1411 return dbentry_has_key_for_enctype(context, client, enctype); 1412 } 1413 1414 /* 1415 * This function returns the keytype which should be selected for the 1416 * session key. It is based on the ordered list which the user 1417 * requested, and what the KDC and the application server can support. 1418 */ 1419 krb5_enctype 1420 select_session_keytype(krb5_context context, krb5_db_entry *server, 1421 int nktypes, krb5_enctype *ktype) 1422 { 1423 int i; 1424 1425 for (i = 0; i < nktypes; i++) { 1426 if (!krb5_c_valid_enctype(ktype[i])) 1427 continue; 1428 1429 if (!krb5_is_permitted_enctype(context, ktype[i])) 1430 continue; 1431 1432 if (dbentry_supports_enctype(context, server, ktype[i])) 1433 return ktype[i]; 1434 } 1435 return 0; 1436 } 1437 1438 /* 1439 * This function returns salt information for a particular client_key 1440 */ 1441 krb5_error_code 1442 get_salt_from_key(krb5_context context, krb5_principal client, 1443 krb5_key_data *client_key, krb5_data *salt) 1444 { 1445 krb5_error_code retval; 1446 krb5_data * realm; 1447 1448 salt->data = 0; 1449 salt->length = SALT_TYPE_NO_LENGTH; 1450 1451 if (client_key->key_data_ver == 1) 1452 return 0; 1453 1454 switch (client_key->key_data_type[1]) { 1455 case KRB5_KDB_SALTTYPE_NORMAL: 1456 break; 1457 case KRB5_KDB_SALTTYPE_V4: 1458 /* send an empty (V4) salt */ 1459 salt->data = 0; 1460 salt->length = 0; 1461 break; 1462 case KRB5_KDB_SALTTYPE_NOREALM: 1463 if ((retval = krb5_principal2salt_norealm(context, client, salt))) 1464 return retval; 1465 break; 1466 case KRB5_KDB_SALTTYPE_AFS3: 1467 /* send the same salt as with onlyrealm - but with no type info, 1468 we just hope they figure it out on the other end. */ 1469 /* fall through to onlyrealm: */ 1470 case KRB5_KDB_SALTTYPE_ONLYREALM: 1471 realm = krb5_princ_realm(context, client); 1472 salt->length = realm->length; 1473 if ((salt->data = malloc(realm->length)) == NULL) 1474 return ENOMEM; 1475 memcpy(salt->data, realm->data, realm->length); 1476 break; 1477 case KRB5_KDB_SALTTYPE_SPECIAL: 1478 salt->length = client_key->key_data_length[1]; 1479 if ((salt->data = malloc(salt->length)) == NULL) 1480 return ENOMEM; 1481 memcpy(salt->data, client_key->key_data_contents[1], salt->length); 1482 break; 1483 } 1484 return 0; 1485 } 1486 1487 /* 1488 * Limit strings to a "reasonable" length to prevent crowding out of 1489 * other useful information in the log entry 1490 */ 1491 #define NAME_LENGTH_LIMIT 128 1492 1493 void limit_string(char *name) 1494 { 1495 int i; 1496 1497 if (!name) 1498 return; 1499 1500 if (strlen(name) < NAME_LENGTH_LIMIT) 1501 return; 1502 1503 i = NAME_LENGTH_LIMIT-4; 1504 name[i++] = '.'; 1505 name[i++] = '.'; 1506 name[i++] = '.'; 1507 name[i] = '\0'; 1508 return; 1509 } 1510 1511 /* 1512 * L10_2 = log10(2**x), rounded up; log10(2) ~= 0.301. 1513 */ 1514 #define L10_2(x) ((int)(((x * 301) + 999) / 1000)) 1515 1516 /* 1517 * Max length of sprintf("%ld") for an int of type T; includes leading 1518 * minus sign and terminating NUL. 1519 */ 1520 #define D_LEN(t) (L10_2(sizeof(t) * CHAR_BIT) + 2) 1521 1522 void 1523 ktypes2str(char *s, size_t len, int nktypes, krb5_enctype *ktype) 1524 { 1525 int i; 1526 char stmp[D_LEN(krb5_enctype) + 1]; 1527 char *p; 1528 1529 if (nktypes < 0 1530 || len < (sizeof(" etypes {...}") + D_LEN(int))) { 1531 *s = '\0'; 1532 return; 1533 } 1534 1535 sprintf(s, "%d etypes {", nktypes); 1536 for (i = 0; i < nktypes; i++) { 1537 sprintf(stmp, "%s%ld", i ? " " : "", (long)ktype[i]); 1538 if (strlen(s) + strlen(stmp) + sizeof("}") > len) 1539 break; 1540 strcat(s, stmp); 1541 } 1542 if (i < nktypes) { 1543 /* 1544 * We broke out of the loop. Try to truncate the list. 1545 */ 1546 p = s + strlen(s); 1547 while (p - s + sizeof("...}") > len) { 1548 while (p > s && *p != ' ' && *p != '{') 1549 *p-- = '\0'; 1550 if (p > s && *p == ' ') { 1551 *p-- = '\0'; 1552 continue; 1553 } 1554 } 1555 strcat(s, "..."); 1556 } 1557 strcat(s, "}"); 1558 return; 1559 } 1560 1561 void 1562 rep_etypes2str(char *s, size_t len, krb5_kdc_rep *rep) 1563 { 1564 char stmp[sizeof("ses=") + D_LEN(krb5_enctype)]; 1565 1566 if (len < (3 * D_LEN(krb5_enctype) 1567 + sizeof("etypes {rep= tkt= ses=}"))) { 1568 *s = '\0'; 1569 return; 1570 } 1571 1572 sprintf(s, "etypes {rep=%ld", (long)rep->enc_part.enctype); 1573 1574 if (rep->ticket != NULL) { 1575 sprintf(stmp, " tkt=%ld", (long)rep->ticket->enc_part.enctype); 1576 strcat(s, stmp); 1577 } 1578 1579 if (rep->ticket != NULL 1580 && rep->ticket->enc_part2 != NULL 1581 && rep->ticket->enc_part2->session != NULL) { 1582 sprintf(stmp, " ses=%ld", 1583 (long)rep->ticket->enc_part2->session->enctype); 1584 strcat(s, stmp); 1585 } 1586 strcat(s, "}"); 1587 return; 1588 } 1589