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