1 /* 2 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 #pragma ident "%Z%%M% %I% %E% SMI" 7 8 #include <k5-int.h> 9 #include <auth_con.h> 10 11 static krb5_boolean chk_heimdal_seqnum(krb5_ui_4, krb5_ui_4); 12 13 /*ARGSUSED*/ 14 static krb5_error_code 15 actx_copy_addr(krb5_context context, const krb5_address *inad, 16 krb5_address **outad) 17 { 18 krb5_address *tmpad; 19 20 if (!(tmpad = (krb5_address *)MALLOC(sizeof(*tmpad)))) 21 return ENOMEM; 22 #ifdef HAVE_C_STRUCTURE_ASSIGNMENT 23 *tmpad = *inad; 24 #else 25 (void) memcpy(tmpad, inad, sizeof(krb5_address)); 26 #endif 27 if (!(tmpad->contents = (krb5_octet *)MALLOC(inad->length))) { 28 krb5_xfree(tmpad); 29 return ENOMEM; 30 } 31 (void) memcpy((char *)tmpad->contents, (char *)inad->contents, inad->length); 32 *outad = tmpad; 33 return 0; 34 } 35 36 krb5_error_code KRB5_CALLCONV 37 krb5_auth_con_init(krb5_context context, krb5_auth_context *auth_context) 38 { 39 *auth_context = 40 (krb5_auth_context)MALLOC(sizeof(struct _krb5_auth_context)); 41 if (!*auth_context) 42 return ENOMEM; 43 44 (void) memset(*auth_context, 0, sizeof(struct _krb5_auth_context)); 45 46 /* Default flags, do time not seq */ 47 (*auth_context)->auth_context_flags = 48 KRB5_AUTH_CONTEXT_DO_TIME | KRB5_AUTH_CONN_INITIALIZED; 49 50 (*auth_context)->req_cksumtype = context->default_ap_req_sumtype; 51 (*auth_context)->safe_cksumtype = context->default_safe_sumtype; 52 (*auth_context) -> checksum_func = NULL; 53 (*auth_context)->checksum_func_data = NULL; 54 (*auth_context)->magic = KV5M_AUTH_CONTEXT; 55 return 0; 56 } 57 58 krb5_error_code KRB5_CALLCONV 59 krb5_auth_con_free(krb5_context context, krb5_auth_context auth_context) 60 { 61 if (auth_context->local_addr) 62 krb5_free_address(context, auth_context->local_addr); 63 if (auth_context->remote_addr) 64 krb5_free_address(context, auth_context->remote_addr); 65 if (auth_context->local_port) 66 krb5_free_address(context, auth_context->local_port); 67 if (auth_context->remote_port) 68 krb5_free_address(context, auth_context->remote_port); 69 if (auth_context->authentp) 70 krb5_free_authenticator(context, auth_context->authentp); 71 if (auth_context->keyblock) 72 krb5_free_keyblock(context, auth_context->keyblock); 73 if (auth_context->send_subkey) 74 krb5_free_keyblock(context, auth_context->send_subkey); 75 if (auth_context->recv_subkey) 76 krb5_free_keyblock(context, auth_context->recv_subkey); 77 if (auth_context->rcache) 78 (void) krb5_rc_close(context, auth_context->rcache); 79 if (auth_context->permitted_etypes) 80 krb5_xfree(auth_context->permitted_etypes); 81 FREE(auth_context, sizeof(krb5_auth_context)); 82 return 0; 83 } 84 85 krb5_error_code KRB5_CALLCONV 86 krb5_auth_con_setaddrs(krb5_context context, 87 krb5_auth_context auth_context, 88 krb5_address *local_addr, 89 krb5_address *remote_addr) 90 { 91 krb5_error_code retval = 0; 92 93 /* Free old addresses */ 94 if (auth_context->local_addr) 95 (void) krb5_free_address(context, auth_context->local_addr); 96 if (auth_context->remote_addr) 97 (void) krb5_free_address(context, auth_context->remote_addr); 98 99 if (local_addr) 100 retval = actx_copy_addr(context, 101 local_addr, 102 &auth_context->local_addr); 103 else 104 auth_context->local_addr = NULL; 105 106 if (!retval && remote_addr) 107 retval = actx_copy_addr(context, 108 remote_addr, 109 &auth_context->remote_addr); 110 else 111 auth_context->remote_addr = NULL; 112 113 return retval; 114 } 115 116 krb5_error_code 117 krb5_auth_con_getaddrs(krb5_context context, 118 krb5_auth_context auth_context, 119 krb5_address **local_addr, 120 krb5_address **remote_addr) 121 { 122 krb5_error_code retval; 123 124 retval = 0; 125 if (local_addr && auth_context->local_addr) { 126 retval = actx_copy_addr(context, 127 auth_context->local_addr, 128 local_addr); 129 } 130 if (!retval && (remote_addr) && auth_context->remote_addr) { 131 retval = actx_copy_addr(context, 132 auth_context->remote_addr, 133 remote_addr); 134 } 135 return retval; 136 } 137 138 krb5_error_code 139 krb5_auth_con_setports( 140 krb5_context context, 141 krb5_auth_context auth_context, 142 krb5_address * local_port, 143 krb5_address * remote_port) 144 { 145 krb5_error_code retval; 146 147 /* Free old addresses */ 148 if (auth_context->local_port) 149 (void) krb5_free_address(context, auth_context->local_port); 150 if (auth_context->remote_port) 151 (void) krb5_free_address(context, auth_context->remote_port); 152 153 retval = 0; 154 if (local_port) 155 retval = actx_copy_addr(context, 156 local_port, 157 &auth_context->local_port); 158 else 159 auth_context->local_port = NULL; 160 161 if (!retval && remote_port) 162 retval = actx_copy_addr(context, 163 remote_port, 164 &auth_context->remote_port); 165 else 166 auth_context->remote_port = NULL; 167 168 return retval; 169 } 170 171 172 /* 173 * This function overloads the keyblock field. It is only useful prior to 174 * a krb5_rd_req_decode() call for user to user authentication where the 175 * server has the key and needs to use it to decrypt the incoming request. 176 * Once decrypted this key is no longer necessary and is then overwritten 177 * with the session key sent by the client. 178 */ 179 krb5_error_code KRB5_CALLCONV 180 krb5_auth_con_setuseruserkey( 181 krb5_context context, 182 krb5_auth_context auth_context, 183 krb5_keyblock * keyblock) 184 { 185 if (auth_context->keyblock) 186 krb5_free_keyblock(context, auth_context->keyblock); 187 return(krb5_copy_keyblock(context, keyblock, &(auth_context->keyblock))); 188 } 189 190 krb5_error_code KRB5_CALLCONV 191 krb5_auth_con_getkey( 192 krb5_context context, 193 krb5_auth_context auth_context, 194 krb5_keyblock ** keyblock) 195 { 196 if (auth_context->keyblock) 197 return krb5_copy_keyblock(context, auth_context->keyblock, keyblock); 198 *keyblock = NULL; 199 return 0; 200 } 201 202 krb5_error_code KRB5_CALLCONV 203 krb5_auth_con_getlocalsubkey( 204 krb5_context context, 205 krb5_auth_context auth_context, 206 krb5_keyblock * * keyblock) 207 { 208 return krb5_auth_con_getsendsubkey(context, auth_context, keyblock); 209 } 210 211 krb5_error_code KRB5_CALLCONV 212 krb5_auth_con_getremotesubkey( 213 krb5_context context, 214 krb5_auth_context auth_context, 215 krb5_keyblock **keyblock) 216 { 217 return krb5_auth_con_getrecvsubkey(context, auth_context, keyblock); 218 } 219 220 krb5_error_code KRB5_CALLCONV 221 krb5_auth_con_setsendsubkey(krb5_context ctx, krb5_auth_context ac, krb5_keyblock *keyblock) 222 { 223 if (ac->send_subkey != NULL) 224 krb5_free_keyblock(ctx, ac->send_subkey); 225 ac->send_subkey = NULL; 226 if (keyblock !=NULL) 227 return krb5_copy_keyblock(ctx, keyblock, &ac->send_subkey); 228 else 229 return 0; 230 } 231 232 krb5_error_code KRB5_CALLCONV 233 krb5_auth_con_setrecvsubkey(krb5_context ctx, krb5_auth_context ac, krb5_keyblock *keyblock) 234 { 235 if (ac->recv_subkey != NULL) 236 krb5_free_keyblock(ctx, ac->recv_subkey); 237 ac->recv_subkey = NULL; 238 if (keyblock != NULL) 239 return krb5_copy_keyblock(ctx, keyblock, &ac->recv_subkey); 240 else 241 return 0; 242 } 243 244 krb5_error_code KRB5_CALLCONV 245 krb5_auth_con_getsendsubkey(krb5_context ctx, krb5_auth_context ac, krb5_keyblock **keyblock) 246 { 247 if (ac->send_subkey != NULL) 248 return krb5_copy_keyblock(ctx, ac->send_subkey, keyblock); 249 *keyblock = NULL; 250 return 0; 251 } 252 253 krb5_error_code KRB5_CALLCONV 254 krb5_auth_con_getrecvsubkey(krb5_context ctx, krb5_auth_context ac, krb5_keyblock **keyblock) 255 { 256 if (ac->recv_subkey != NULL) 257 return krb5_copy_keyblock(ctx, ac->recv_subkey, keyblock); 258 *keyblock = NULL; 259 return 0; 260 } 261 262 /*ARGSUSED*/ 263 krb5_error_code KRB5_CALLCONV 264 krb5_auth_con_set_req_cksumtype( 265 krb5_context context, 266 krb5_auth_context auth_context, 267 krb5_cksumtype cksumtype) 268 { 269 auth_context->req_cksumtype = cksumtype; 270 return 0; 271 } 272 273 /*ARGSUSED*/ 274 krb5_error_code 275 krb5_auth_con_set_safe_cksumtype( 276 krb5_context context, 277 krb5_auth_context auth_context, 278 krb5_cksumtype cksumtype) 279 { 280 auth_context->safe_cksumtype = cksumtype; 281 return 0; 282 } 283 284 /*ARGSUSED*/ 285 krb5_error_code KRB5_CALLCONV 286 krb5_auth_con_getlocalseqnumber( 287 krb5_context context, 288 krb5_auth_context auth_context, 289 krb5_int32 * seqnumber) 290 { 291 *seqnumber = auth_context->local_seq_number; 292 return 0; 293 } 294 295 krb5_error_code KRB5_CALLCONV 296 krb5_auth_con_getauthenticator( 297 krb5_context context, 298 krb5_auth_context auth_context, 299 krb5_authenticator **authenticator) 300 { 301 return (krb5_copy_authenticator(context, auth_context->authentp, 302 authenticator)); 303 } 304 305 /*ARGSUSED*/ 306 krb5_error_code KRB5_CALLCONV 307 krb5_auth_con_getremoteseqnumber( 308 krb5_context context, 309 krb5_auth_context auth_context, 310 krb5_int32 * seqnumber) 311 { 312 *seqnumber = auth_context->remote_seq_number; 313 return 0; 314 } 315 316 krb5_error_code 317 krb5_auth_con_initivector( 318 krb5_context context, 319 krb5_auth_context auth_context) 320 { 321 krb5_error_code ret; 322 323 if (auth_context->keyblock) { 324 size_t blocksize; 325 326 if ((ret = krb5_c_block_size(context, auth_context->keyblock->enctype, 327 &blocksize))) 328 return(ret); 329 if ((auth_context->i_vector = (krb5_pointer)MALLOC(blocksize))) { 330 memset(auth_context->i_vector, 0, blocksize); 331 return 0; 332 } 333 return ENOMEM; 334 } 335 return EINVAL; /* XXX need an error for no keyblock */ 336 } 337 338 /*ARGSUSED*/ 339 krb5_error_code 340 krb5_auth_con_setivector( 341 krb5_context context, 342 krb5_auth_context auth_context, 343 krb5_pointer ivector) 344 { 345 auth_context->i_vector = ivector; 346 return 0; 347 } 348 349 /*ARGSUSED*/ 350 krb5_error_code 351 krb5_auth_con_getivector( 352 krb5_context context, 353 krb5_auth_context auth_context, 354 krb5_pointer * ivector) 355 { 356 *ivector = auth_context->i_vector; 357 return 0; 358 } 359 360 /*ARGSUSED*/ 361 krb5_error_code KRB5_CALLCONV 362 krb5_auth_con_setflags( 363 krb5_context context, 364 krb5_auth_context auth_context, 365 krb5_int32 flags) 366 { 367 auth_context->auth_context_flags = flags; 368 return 0; 369 } 370 371 /*ARGSUSED*/ 372 krb5_error_code KRB5_CALLCONV 373 krb5_auth_con_getflags( 374 krb5_context context, 375 krb5_auth_context auth_context, 376 krb5_int32 * flags) 377 { 378 *flags = auth_context->auth_context_flags; 379 return 0; 380 } 381 382 /*ARGSUSED*/ 383 krb5_error_code KRB5_CALLCONV 384 krb5_auth_con_setrcache( 385 krb5_context context, 386 krb5_auth_context auth_context, 387 krb5_rcache rcache) 388 { 389 auth_context->rcache = rcache; 390 return 0; 391 } 392 393 /*ARGSUSED*/ 394 krb5_error_code 395 krb5_auth_con_getrcache( 396 krb5_context context, 397 krb5_auth_context auth_context, 398 krb5_rcache * rcache) 399 { 400 *rcache = auth_context->rcache; 401 return 0; 402 } 403 404 /*ARGSUSED*/ 405 krb5_error_code 406 krb5_auth_con_setpermetypes( 407 krb5_context context, 408 krb5_auth_context auth_context, 409 const krb5_enctype * permetypes) 410 { 411 krb5_enctype * newpe; 412 int i; 413 414 for (i=0; permetypes[i]; i++) 415 ; 416 i++; /* include the zero */ 417 418 if ((newpe = (krb5_enctype *) MALLOC(i*sizeof(krb5_enctype))) 419 == NULL) 420 return(ENOMEM); 421 422 if (auth_context->permitted_etypes) 423 krb5_xfree(auth_context->permitted_etypes); 424 425 auth_context->permitted_etypes = newpe; 426 427 (void) memcpy(newpe, permetypes, i*sizeof(krb5_enctype)); 428 429 return 0; 430 } 431 432 /*ARGSUSED*/ 433 krb5_error_code 434 krb5_auth_con_getpermetypes( 435 krb5_context context, 436 krb5_auth_context auth_context, 437 krb5_enctype ** permetypes) 438 { 439 krb5_enctype * newpe; 440 int i; 441 442 if (! auth_context->permitted_etypes) { 443 *permetypes = NULL; 444 return(0); 445 } 446 447 for (i=0; auth_context->permitted_etypes[i]; i++) 448 ; 449 i++; /* include the zero */ 450 451 if ((newpe = (krb5_enctype *) MALLOC(i*sizeof(krb5_enctype))) 452 == NULL) 453 return(ENOMEM); 454 455 *permetypes = newpe; 456 457 memcpy(newpe, auth_context->permitted_etypes, i*sizeof(krb5_enctype)); 458 459 return(0); 460 } 461 462 krb5_error_code KRB5_CALLCONV 463 krb5_auth_con_set_checksum_func( krb5_context context, 464 krb5_auth_context auth_context, 465 krb5_mk_req_checksum_func func, 466 void *data) 467 { 468 auth_context->checksum_func = func; 469 auth_context->checksum_func_data = data; 470 return 0; 471 } 472 473 static krb5_boolean 474 chk_heimdal_seqnum(krb5_ui_4 exp_seq, krb5_ui_4 in_seq) 475 { 476 if (( exp_seq & 0xFF800000) == 0x00800000 477 && (in_seq & 0xFF800000) == 0xFF800000 478 && (in_seq & 0x00FFFFFF) == exp_seq) 479 return 1; 480 else if (( exp_seq & 0xFFFF8000) == 0x00008000 481 && (in_seq & 0xFFFF8000) == 0xFFFF8000 482 && (in_seq & 0x0000FFFF) == exp_seq) 483 return 1; 484 else if (( exp_seq & 0xFFFFFF80) == 0x00000080 485 && (in_seq & 0xFFFFFF80) == 0xFFFFFF80 486 && (in_seq & 0x000000FF) == exp_seq) 487 return 1; 488 else 489 return 0; 490 } 491 492 493 krb5_error_code KRB5_CALLCONV 494 krb5_auth_con_get_checksum_func( krb5_context context, 495 krb5_auth_context auth_context, 496 krb5_mk_req_checksum_func *func, 497 void **data) 498 { 499 *func = auth_context->checksum_func; 500 *data = auth_context->checksum_func_data; 501 return 0; 502 } 503 504 505 /* 506 * krb5int_auth_con_chkseqnum 507 * 508 * We use a somewhat complex heuristic for validating received 509 * sequence numbers. We must accommodate both our older 510 * implementation, which sends negative sequence numbers, and the 511 * broken Heimdal implementation (at least as of 0.5.2), which 512 * violates X.690 BER for integer encodings. The requirement of 513 * handling negative sequence numbers removes one of easier means of 514 * detecting a Heimdal implementation, so we resort to this mess 515 * here. 516 * 517 * X.690 BER (and consequently DER, which are the required encoding 518 * rules in RFC1510) encode all integer types as signed integers. 519 * This means that the MSB being set on the first octet of the 520 * contents of the encoding indicates a negative value. Heimdal does 521 * not prepend the required zero octet to unsigned integer encodings 522 * which would otherwise have the MSB of the first octet of their 523 * encodings set. 524 * 525 * Our ASN.1 library implements a special decoder for sequence 526 * numbers, accepting both negative and positive 32-bit numbers but 527 * mapping them both into the space of positive unsigned 32-bit 528 * numbers in the obvious bit-pattern-preserving way. This maintains 529 * compatibility with our older implementations. This also means that 530 * encodings emitted by Heimdal are ambiguous. 531 * 532 * Heimdal counter value received uint32 value 533 * 534 * 0x00000080 0xFFFFFF80 535 * 0x000000FF 0xFFFFFFFF 536 * 0x00008000 0xFFFF8000 537 * 0x0000FFFF 0xFFFFFFFF 538 * 0x00800000 0xFF800000 539 * 0x00FFFFFF 0xFFFFFFFF 540 * 0xFF800000 0xFF800000 541 * 0xFFFFFFFF 0xFFFFFFFF 542 * 543 * We use two auth_context flags, SANE_SEQ and HEIMDAL_SEQ, which are 544 * only set after we can unambiguously determine the sanity of the 545 * sending implementation. Once one of these flags is set, we accept 546 * only the sequence numbers appropriate to the remote implementation 547 * type. We can make the determination in two different ways. The 548 * first is to note the receipt of a "negative" sequence number when a 549 * "positive" one was expected. The second is to note the receipt of 550 * a sequence number that wraps through "zero" in a weird way. The 551 * latter corresponds to the receipt of an initial sequence number in 552 * the ambiguous range. 553 * 554 * There are 2^7 + 2^15 + 2^23 + 2^23 = 16810112 total ambiguous 555 * initial Heimdal counter values, but we receive them as one of 2^23 556 * possible values. There is a ~1/256 chance of a Heimdal 557 * implementation sending an intial sequence number in the ambiguous 558 * range. 559 * 560 * We have to do special treatment when receiving sequence numbers 561 * between 0xFF800000..0xFFFFFFFF, or when wrapping through zero 562 * weirdly (due to ambiguous initial sequence number). If we are 563 * expecting a value corresponding to an ambiguous Heimdal counter 564 * value, and we receive an exact match, we can mark the remote end as 565 * sane. 566 */ 567 krb5_boolean 568 krb5int_auth_con_chkseqnum( 569 krb5_context ctx, 570 krb5_auth_context ac, 571 krb5_ui_4 in_seq) 572 { 573 krb5_ui_4 exp_seq; 574 575 exp_seq = ac->remote_seq_number; 576 577 /* 578 * If sender is known to be sane, accept _only_ exact matches. 579 */ 580 if (ac->auth_context_flags & KRB5_AUTH_CONN_SANE_SEQ) 581 return in_seq == exp_seq; 582 /* 583 * If sender is not known to be sane, first check the ambiguous 584 * range of received values, 0xFF800000..0xFFFFFFFF. 585 */ 586 if ((in_seq & 0xFF800000) == 0xFF800000) { 587 /* 588 * If expected sequence number is in the range 589 * 0xFF800000..0xFFFFFFFF, then we can't make any 590 * determinations about the sanity of the sending 591 * implementation. 592 */ 593 if ((exp_seq & 0xFF800000) == 0xFF800000 && in_seq == exp_seq) 594 return 1; 595 /* 596 * If sender is not known for certain to be a broken Heimdal 597 * implementation, check for exact match. 598 */ 599 if (!(ac->auth_context_flags & KRB5_AUTH_CONN_HEIMDAL_SEQ) 600 && in_seq == exp_seq) 601 return 1; 602 /* 603 * Now apply hairy algorithm for matching sequence numbers 604 * sent by broken Heimdal implementations. If it matches, we 605 * know for certain it's a broken Heimdal sender. 606 */ 607 if (chk_heimdal_seqnum(exp_seq, in_seq)) { 608 ac->auth_context_flags |= KRB5_AUTH_CONN_HEIMDAL_SEQ; 609 return 1; 610 } 611 return 0; 612 } 613 614 /* 615 * Received value not in the ambiguous range? If the _expected_ 616 * value is in the range of ambiguous Hemidal counter values, and 617 * it matches the received value, sender is known to be sane. 618 */ 619 if (in_seq == exp_seq) { 620 if (( exp_seq & 0xFFFFFF80) == 0x00000080 621 || (exp_seq & 0xFFFF8000) == 0x00008000 622 || (exp_seq & 0xFF800000) == 0x00800000) 623 ac->auth_context_flags |= KRB5_AUTH_CONN_SANE_SEQ; 624 return 1; 625 } 626 627 /* 628 * Magic wraparound for the case where the intial sequence number 629 * is in the ambiguous range. This means that the sender's 630 * counter is at a different count than ours, so we correct ours, 631 * and mark the sender as being a broken Heimdal implementation. 632 */ 633 if (exp_seq == 0 634 && !(ac->auth_context_flags & KRB5_AUTH_CONN_HEIMDAL_SEQ)) { 635 switch (in_seq) { 636 case 0x100: 637 case 0x10000: 638 case 0x1000000: 639 ac->auth_context_flags |= KRB5_AUTH_CONN_HEIMDAL_SEQ; 640 exp_seq = in_seq; 641 return 1; 642 default: 643 return 0; 644 } 645 } 646 return 0; 647 } 648 649