1 /* 2 * Copyright (c) 2006 Kungliga Tekniska H�gskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * 3. Neither the name of the Institute nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #include "krb5_locl.h" 35 RCSID("$Id: digest.c 22156 2007-12-04 20:02:49Z lha $"); 36 #include "digest_asn1.h" 37 38 struct krb5_digest_data { 39 char *cbtype; 40 char *cbbinding; 41 42 DigestInit init; 43 DigestInitReply initReply; 44 DigestRequest request; 45 DigestResponse response; 46 }; 47 48 krb5_error_code 49 krb5_digest_alloc(krb5_context context, krb5_digest *digest) 50 { 51 krb5_digest d; 52 53 d = calloc(1, sizeof(*d)); 54 if (d == NULL) { 55 *digest = NULL; 56 krb5_set_error_string(context, "out of memory"); 57 return ENOMEM; 58 } 59 *digest = d; 60 61 return 0; 62 } 63 64 void 65 krb5_digest_free(krb5_digest digest) 66 { 67 if (digest == NULL) 68 return; 69 free_DigestInit(&digest->init); 70 free_DigestInitReply(&digest->initReply); 71 free_DigestRequest(&digest->request); 72 free_DigestResponse(&digest->response); 73 memset(digest, 0, sizeof(*digest)); 74 free(digest); 75 return; 76 } 77 78 krb5_error_code 79 krb5_digest_set_server_cb(krb5_context context, 80 krb5_digest digest, 81 const char *type, 82 const char *binding) 83 { 84 if (digest->init.channel) { 85 krb5_set_error_string(context, "server channel binding already set"); 86 return EINVAL; 87 } 88 digest->init.channel = calloc(1, sizeof(*digest->init.channel)); 89 if (digest->init.channel == NULL) 90 goto error; 91 92 digest->init.channel->cb_type = strdup(type); 93 if (digest->init.channel->cb_type == NULL) 94 goto error; 95 96 digest->init.channel->cb_binding = strdup(binding); 97 if (digest->init.channel->cb_binding == NULL) 98 goto error; 99 return 0; 100 error: 101 if (digest->init.channel) { 102 free(digest->init.channel->cb_type); 103 free(digest->init.channel->cb_binding); 104 free(digest->init.channel); 105 digest->init.channel = NULL; 106 } 107 krb5_set_error_string(context, "out of memory"); 108 return ENOMEM; 109 } 110 111 krb5_error_code 112 krb5_digest_set_type(krb5_context context, 113 krb5_digest digest, 114 const char *type) 115 { 116 if (digest->init.type) { 117 krb5_set_error_string(context, "client type already set"); 118 return EINVAL; 119 } 120 digest->init.type = strdup(type); 121 if (digest->init.type == NULL) { 122 krb5_set_error_string(context, "out of memory"); 123 return ENOMEM; 124 } 125 return 0; 126 } 127 128 krb5_error_code 129 krb5_digest_set_hostname(krb5_context context, 130 krb5_digest digest, 131 const char *hostname) 132 { 133 if (digest->init.hostname) { 134 krb5_set_error_string(context, "server hostname already set"); 135 return EINVAL; 136 } 137 digest->init.hostname = malloc(sizeof(*digest->init.hostname)); 138 if (digest->init.hostname == NULL) { 139 krb5_set_error_string(context, "out of memory"); 140 return ENOMEM; 141 } 142 *digest->init.hostname = strdup(hostname); 143 if (*digest->init.hostname == NULL) { 144 krb5_set_error_string(context, "out of memory"); 145 free(digest->init.hostname); 146 digest->init.hostname = NULL; 147 return ENOMEM; 148 } 149 return 0; 150 } 151 152 const char * 153 krb5_digest_get_server_nonce(krb5_context context, 154 krb5_digest digest) 155 { 156 return digest->initReply.nonce; 157 } 158 159 krb5_error_code 160 krb5_digest_set_server_nonce(krb5_context context, 161 krb5_digest digest, 162 const char *nonce) 163 { 164 if (digest->request.serverNonce) { 165 krb5_set_error_string(context, "nonce already set"); 166 return EINVAL; 167 } 168 digest->request.serverNonce = strdup(nonce); 169 if (digest->request.serverNonce == NULL) { 170 krb5_set_error_string(context, "out of memory"); 171 return ENOMEM; 172 } 173 return 0; 174 } 175 176 const char * 177 krb5_digest_get_opaque(krb5_context context, 178 krb5_digest digest) 179 { 180 return digest->initReply.opaque; 181 } 182 183 krb5_error_code 184 krb5_digest_set_opaque(krb5_context context, 185 krb5_digest digest, 186 const char *opaque) 187 { 188 if (digest->request.opaque) { 189 krb5_set_error_string(context, "opaque already set"); 190 return EINVAL; 191 } 192 digest->request.opaque = strdup(opaque); 193 if (digest->request.opaque == NULL) { 194 krb5_set_error_string(context, "out of memory"); 195 return ENOMEM; 196 } 197 return 0; 198 } 199 200 const char * 201 krb5_digest_get_identifier(krb5_context context, 202 krb5_digest digest) 203 { 204 if (digest->initReply.identifier == NULL) 205 return NULL; 206 return *digest->initReply.identifier; 207 } 208 209 krb5_error_code 210 krb5_digest_set_identifier(krb5_context context, 211 krb5_digest digest, 212 const char *id) 213 { 214 if (digest->request.identifier) { 215 krb5_set_error_string(context, "identifier already set"); 216 return EINVAL; 217 } 218 digest->request.identifier = calloc(1, sizeof(*digest->request.identifier)); 219 if (digest->request.identifier == NULL) { 220 krb5_set_error_string(context, "out of memory"); 221 return ENOMEM; 222 } 223 *digest->request.identifier = strdup(id); 224 if (*digest->request.identifier == NULL) { 225 krb5_set_error_string(context, "out of memory"); 226 free(digest->request.identifier); 227 digest->request.identifier = NULL; 228 return ENOMEM; 229 } 230 return 0; 231 } 232 233 static krb5_error_code 234 digest_request(krb5_context context, 235 krb5_realm realm, 236 krb5_ccache ccache, 237 krb5_key_usage usage, 238 const DigestReqInner *ireq, 239 DigestRepInner *irep) 240 { 241 DigestREQ req; 242 DigestREP rep; 243 krb5_error_code ret; 244 krb5_data data, data2; 245 size_t size; 246 krb5_crypto crypto = NULL; 247 krb5_auth_context ac = NULL; 248 krb5_principal principal = NULL; 249 krb5_ccache id = NULL; 250 krb5_realm r = NULL; 251 252 krb5_data_zero(&data); 253 krb5_data_zero(&data2); 254 memset(&req, 0, sizeof(req)); 255 memset(&rep, 0, sizeof(rep)); 256 257 if (ccache == NULL) { 258 ret = krb5_cc_default(context, &id); 259 if (ret) 260 goto out; 261 } else 262 id = ccache; 263 264 if (realm == NULL) { 265 ret = krb5_get_default_realm(context, &r); 266 if (ret) 267 goto out; 268 } else 269 r = realm; 270 271 /* 272 * 273 */ 274 275 ret = krb5_make_principal(context, &principal, 276 r, KRB5_DIGEST_NAME, r, NULL); 277 if (ret) 278 goto out; 279 280 ASN1_MALLOC_ENCODE(DigestReqInner, data.data, data.length, 281 ireq, &size, ret); 282 if (ret) { 283 krb5_set_error_string(context, 284 "Failed to encode digest inner request"); 285 goto out; 286 } 287 if (size != data.length) 288 krb5_abortx(context, "ASN.1 internal encoder error"); 289 290 ret = krb5_mk_req_exact(context, &ac, 291 AP_OPTS_USE_SUBKEY|AP_OPTS_MUTUAL_REQUIRED, 292 principal, NULL, id, &req.apReq); 293 if (ret) 294 goto out; 295 296 { 297 krb5_keyblock *key; 298 299 ret = krb5_auth_con_getlocalsubkey(context, ac, &key); 300 if (ret) 301 goto out; 302 if (key == NULL) { 303 krb5_set_error_string(context, "Digest failed to get local subkey"); 304 ret = EINVAL; 305 goto out; 306 } 307 308 ret = krb5_crypto_init(context, key, 0, &crypto); 309 krb5_free_keyblock (context, key); 310 if (ret) 311 goto out; 312 } 313 314 ret = krb5_encrypt_EncryptedData(context, crypto, usage, 315 data.data, data.length, 0, 316 &req.innerReq); 317 if (ret) 318 goto out; 319 320 krb5_data_free(&data); 321 322 ASN1_MALLOC_ENCODE(DigestREQ, data.data, data.length, 323 &req, &size, ret); 324 if (ret) { 325 krb5_set_error_string(context, "Failed to encode DigestREQest"); 326 goto out; 327 } 328 if (size != data.length) 329 krb5_abortx(context, "ASN.1 internal encoder error"); 330 331 ret = krb5_sendto_kdc(context, &data, &r, &data2); 332 if (ret) 333 goto out; 334 335 ret = decode_DigestREP(data2.data, data2.length, &rep, NULL); 336 if (ret) { 337 krb5_set_error_string(context, "Failed to parse digest response"); 338 goto out; 339 } 340 341 { 342 krb5_ap_rep_enc_part *repl; 343 344 ret = krb5_rd_rep(context, ac, &rep.apRep, &repl); 345 if (ret) 346 goto out; 347 348 krb5_free_ap_rep_enc_part(context, repl); 349 } 350 { 351 krb5_keyblock *key; 352 353 ret = krb5_auth_con_getremotesubkey(context, ac, &key); 354 if (ret) 355 goto out; 356 if (key == NULL) { 357 ret = EINVAL; 358 krb5_set_error_string(context, 359 "Digest reply have no remote subkey"); 360 goto out; 361 } 362 363 krb5_crypto_destroy(context, crypto); 364 ret = krb5_crypto_init(context, key, 0, &crypto); 365 krb5_free_keyblock (context, key); 366 if (ret) 367 goto out; 368 } 369 370 krb5_data_free(&data); 371 ret = krb5_decrypt_EncryptedData(context, crypto, usage, 372 &rep.innerRep, &data); 373 if (ret) 374 goto out; 375 376 ret = decode_DigestRepInner(data.data, data.length, irep, NULL); 377 if (ret) { 378 krb5_set_error_string(context, "Failed to decode digest inner reply"); 379 goto out; 380 } 381 382 out: 383 if (ccache == NULL && id) 384 krb5_cc_close(context, id); 385 if (realm == NULL && r) 386 free(r); 387 if (crypto) 388 krb5_crypto_destroy(context, crypto); 389 if (ac) 390 krb5_auth_con_free(context, ac); 391 if (principal) 392 krb5_free_principal(context, principal); 393 394 krb5_data_free(&data); 395 krb5_data_free(&data2); 396 397 free_DigestREQ(&req); 398 free_DigestREP(&rep); 399 400 return ret; 401 } 402 403 krb5_error_code 404 krb5_digest_init_request(krb5_context context, 405 krb5_digest digest, 406 krb5_realm realm, 407 krb5_ccache ccache) 408 { 409 DigestReqInner ireq; 410 DigestRepInner irep; 411 krb5_error_code ret; 412 413 memset(&ireq, 0, sizeof(ireq)); 414 memset(&irep, 0, sizeof(irep)); 415 416 if (digest->init.type == NULL) { 417 krb5_set_error_string(context, "Type missing from init req"); 418 return EINVAL; 419 } 420 421 ireq.element = choice_DigestReqInner_init; 422 ireq.u.init = digest->init; 423 424 ret = digest_request(context, realm, ccache, 425 KRB5_KU_DIGEST_ENCRYPT, &ireq, &irep); 426 if (ret) 427 goto out; 428 429 if (irep.element == choice_DigestRepInner_error) { 430 krb5_set_error_string(context, "Digest init error: %s", 431 irep.u.error.reason); 432 ret = irep.u.error.code; 433 goto out; 434 } 435 436 if (irep.element != choice_DigestRepInner_initReply) { 437 krb5_set_error_string(context, "digest reply not an initReply"); 438 ret = EINVAL; 439 goto out; 440 } 441 442 ret = copy_DigestInitReply(&irep.u.initReply, &digest->initReply); 443 if (ret) { 444 krb5_set_error_string(context, "Failed to copy initReply"); 445 goto out; 446 } 447 448 out: 449 free_DigestRepInner(&irep); 450 451 return ret; 452 } 453 454 455 krb5_error_code 456 krb5_digest_set_client_nonce(krb5_context context, 457 krb5_digest digest, 458 const char *nonce) 459 { 460 if (digest->request.clientNonce) { 461 krb5_set_error_string(context, "clientNonce already set"); 462 return EINVAL; 463 } 464 digest->request.clientNonce = 465 calloc(1, sizeof(*digest->request.clientNonce)); 466 if (digest->request.clientNonce == NULL) { 467 krb5_set_error_string(context, "out of memory"); 468 return ENOMEM; 469 } 470 *digest->request.clientNonce = strdup(nonce); 471 if (*digest->request.clientNonce == NULL) { 472 krb5_set_error_string(context, "out of memory"); 473 free(digest->request.clientNonce); 474 digest->request.clientNonce = NULL; 475 return ENOMEM; 476 } 477 return 0; 478 } 479 480 krb5_error_code 481 krb5_digest_set_digest(krb5_context context, 482 krb5_digest digest, 483 const char *dgst) 484 { 485 if (digest->request.digest) { 486 krb5_set_error_string(context, "digest already set"); 487 return EINVAL; 488 } 489 digest->request.digest = strdup(dgst); 490 if (digest->request.digest == NULL) { 491 krb5_set_error_string(context, "out of memory"); 492 return ENOMEM; 493 } 494 return 0; 495 } 496 497 krb5_error_code 498 krb5_digest_set_username(krb5_context context, 499 krb5_digest digest, 500 const char *username) 501 { 502 if (digest->request.username) { 503 krb5_set_error_string(context, "username already set"); 504 return EINVAL; 505 } 506 digest->request.username = strdup(username); 507 if (digest->request.username == NULL) { 508 krb5_set_error_string(context, "out of memory"); 509 return ENOMEM; 510 } 511 return 0; 512 } 513 514 krb5_error_code 515 krb5_digest_set_authid(krb5_context context, 516 krb5_digest digest, 517 const char *authid) 518 { 519 if (digest->request.authid) { 520 krb5_set_error_string(context, "authid already set"); 521 return EINVAL; 522 } 523 digest->request.authid = malloc(sizeof(*digest->request.authid)); 524 if (digest->request.authid == NULL) { 525 krb5_set_error_string(context, "out of memory"); 526 return ENOMEM; 527 } 528 *digest->request.authid = strdup(authid); 529 if (*digest->request.authid == NULL) { 530 krb5_set_error_string(context, "out of memory"); 531 free(digest->request.authid); 532 digest->request.authid = NULL; 533 return ENOMEM; 534 } 535 return 0; 536 } 537 538 krb5_error_code 539 krb5_digest_set_authentication_user(krb5_context context, 540 krb5_digest digest, 541 krb5_principal authentication_user) 542 { 543 krb5_error_code ret; 544 545 if (digest->request.authentication_user) { 546 krb5_set_error_string(context, "authentication_user already set"); 547 return EINVAL; 548 } 549 ret = krb5_copy_principal(context, 550 authentication_user, 551 &digest->request.authentication_user); 552 if (digest->request.authentication_user == NULL) { 553 krb5_set_error_string(context, "out of memory"); 554 return ENOMEM; 555 } 556 return 0; 557 } 558 559 krb5_error_code 560 krb5_digest_set_realm(krb5_context context, 561 krb5_digest digest, 562 const char *realm) 563 { 564 if (digest->request.realm) { 565 krb5_set_error_string(context, "realm already set"); 566 return EINVAL; 567 } 568 digest->request.realm = malloc(sizeof(*digest->request.realm)); 569 if (digest->request.realm == NULL) { 570 krb5_set_error_string(context, "out of memory"); 571 return ENOMEM; 572 } 573 *digest->request.realm = strdup(realm); 574 if (*digest->request.realm == NULL) { 575 krb5_set_error_string(context, "out of memory"); 576 free(digest->request.realm); 577 digest->request.realm = NULL; 578 return ENOMEM; 579 } 580 return 0; 581 } 582 583 krb5_error_code 584 krb5_digest_set_method(krb5_context context, 585 krb5_digest digest, 586 const char *method) 587 { 588 if (digest->request.method) { 589 krb5_set_error_string(context, "method already set"); 590 return EINVAL; 591 } 592 digest->request.method = malloc(sizeof(*digest->request.method)); 593 if (digest->request.method == NULL) { 594 krb5_set_error_string(context, "out of memory"); 595 return ENOMEM; 596 } 597 *digest->request.method = strdup(method); 598 if (*digest->request.method == NULL) { 599 krb5_set_error_string(context, "out of memory"); 600 free(digest->request.method); 601 digest->request.method = NULL; 602 return ENOMEM; 603 } 604 return 0; 605 } 606 607 krb5_error_code 608 krb5_digest_set_uri(krb5_context context, 609 krb5_digest digest, 610 const char *uri) 611 { 612 if (digest->request.uri) { 613 krb5_set_error_string(context, "uri already set"); 614 return EINVAL; 615 } 616 digest->request.uri = malloc(sizeof(*digest->request.uri)); 617 if (digest->request.uri == NULL) { 618 krb5_set_error_string(context, "out of memory"); 619 return ENOMEM; 620 } 621 *digest->request.uri = strdup(uri); 622 if (*digest->request.uri == NULL) { 623 krb5_set_error_string(context, "out of memory"); 624 free(digest->request.uri); 625 digest->request.uri = NULL; 626 return ENOMEM; 627 } 628 return 0; 629 } 630 631 krb5_error_code 632 krb5_digest_set_nonceCount(krb5_context context, 633 krb5_digest digest, 634 const char *nonce_count) 635 { 636 if (digest->request.nonceCount) { 637 krb5_set_error_string(context, "nonceCount already set"); 638 return EINVAL; 639 } 640 digest->request.nonceCount = 641 malloc(sizeof(*digest->request.nonceCount)); 642 if (digest->request.nonceCount == NULL) { 643 krb5_set_error_string(context, "out of memory"); 644 return ENOMEM; 645 } 646 *digest->request.nonceCount = strdup(nonce_count); 647 if (*digest->request.nonceCount == NULL) { 648 krb5_set_error_string(context, "out of memory"); 649 free(digest->request.nonceCount); 650 digest->request.nonceCount = NULL; 651 return ENOMEM; 652 } 653 return 0; 654 } 655 656 krb5_error_code 657 krb5_digest_set_qop(krb5_context context, 658 krb5_digest digest, 659 const char *qop) 660 { 661 if (digest->request.qop) { 662 krb5_set_error_string(context, "qop already set"); 663 return EINVAL; 664 } 665 digest->request.qop = malloc(sizeof(*digest->request.qop)); 666 if (digest->request.qop == NULL) { 667 krb5_set_error_string(context, "out of memory"); 668 return ENOMEM; 669 } 670 *digest->request.qop = strdup(qop); 671 if (*digest->request.qop == NULL) { 672 krb5_set_error_string(context, "out of memory"); 673 free(digest->request.qop); 674 digest->request.qop = NULL; 675 return ENOMEM; 676 } 677 return 0; 678 } 679 680 int 681 krb5_digest_set_responseData(krb5_context context, 682 krb5_digest digest, 683 const char *response) 684 { 685 digest->request.responseData = strdup(response); 686 if (digest->request.responseData == NULL) { 687 krb5_set_error_string(context, "out of memory"); 688 return ENOMEM; 689 } 690 return 0; 691 } 692 693 krb5_error_code 694 krb5_digest_request(krb5_context context, 695 krb5_digest digest, 696 krb5_realm realm, 697 krb5_ccache ccache) 698 { 699 DigestReqInner ireq; 700 DigestRepInner irep; 701 krb5_error_code ret; 702 703 memset(&ireq, 0, sizeof(ireq)); 704 memset(&irep, 0, sizeof(irep)); 705 706 ireq.element = choice_DigestReqInner_digestRequest; 707 ireq.u.digestRequest = digest->request; 708 709 if (digest->request.type == NULL) { 710 if (digest->init.type == NULL) { 711 krb5_set_error_string(context, "Type missing from req"); 712 return EINVAL; 713 } 714 ireq.u.digestRequest.type = digest->init.type; 715 } 716 717 if (ireq.u.digestRequest.digest == NULL) 718 ireq.u.digestRequest.digest = "md5"; 719 720 ret = digest_request(context, realm, ccache, 721 KRB5_KU_DIGEST_ENCRYPT, &ireq, &irep); 722 if (ret) 723 return ret; 724 725 if (irep.element == choice_DigestRepInner_error) { 726 krb5_set_error_string(context, "Digest response error: %s", 727 irep.u.error.reason); 728 ret = irep.u.error.code; 729 goto out; 730 } 731 732 if (irep.element != choice_DigestRepInner_response) { 733 krb5_set_error_string(context, "digest reply not an DigestResponse"); 734 ret = EINVAL; 735 goto out; 736 } 737 738 ret = copy_DigestResponse(&irep.u.response, &digest->response); 739 if (ret) { 740 krb5_set_error_string(context, "Failed to copy initReply"); 741 goto out; 742 } 743 744 out: 745 free_DigestRepInner(&irep); 746 747 return ret; 748 } 749 750 krb5_boolean 751 krb5_digest_rep_get_status(krb5_context context, 752 krb5_digest digest) 753 { 754 return digest->response.success ? TRUE : FALSE; 755 } 756 757 const char * 758 krb5_digest_get_rsp(krb5_context context, 759 krb5_digest digest) 760 { 761 if (digest->response.rsp == NULL) 762 return NULL; 763 return *digest->response.rsp; 764 } 765 766 krb5_error_code 767 krb5_digest_get_tickets(krb5_context context, 768 krb5_digest digest, 769 Ticket **tickets) 770 { 771 *tickets = NULL; 772 return 0; 773 } 774 775 776 krb5_error_code 777 krb5_digest_get_client_binding(krb5_context context, 778 krb5_digest digest, 779 char **type, 780 char **binding) 781 { 782 if (digest->response.channel) { 783 *type = strdup(digest->response.channel->cb_type); 784 *binding = strdup(digest->response.channel->cb_binding); 785 if (*type == NULL || *binding == NULL) { 786 free(*type); 787 free(*binding); 788 krb5_set_error_string(context, "out of memory"); 789 return ENOMEM; 790 } 791 } else { 792 *type = NULL; 793 *binding = NULL; 794 } 795 return 0; 796 } 797 798 krb5_error_code 799 krb5_digest_get_session_key(krb5_context context, 800 krb5_digest digest, 801 krb5_data *data) 802 { 803 krb5_error_code ret; 804 805 krb5_data_zero(data); 806 if (digest->response.session_key == NULL) 807 return 0; 808 ret = der_copy_octet_string(digest->response.session_key, data); 809 if (ret) 810 krb5_clear_error_string(context); 811 812 return ret; 813 } 814 815 struct krb5_ntlm_data { 816 NTLMInit init; 817 NTLMInitReply initReply; 818 NTLMRequest request; 819 NTLMResponse response; 820 }; 821 822 krb5_error_code 823 krb5_ntlm_alloc(krb5_context context, 824 krb5_ntlm *ntlm) 825 { 826 *ntlm = calloc(1, sizeof(**ntlm)); 827 if (*ntlm == NULL) { 828 krb5_set_error_string(context, "out of memory"); 829 return ENOMEM; 830 } 831 return 0; 832 } 833 834 krb5_error_code 835 krb5_ntlm_free(krb5_context context, krb5_ntlm ntlm) 836 { 837 free_NTLMInit(&ntlm->init); 838 free_NTLMInitReply(&ntlm->initReply); 839 free_NTLMRequest(&ntlm->request); 840 free_NTLMResponse(&ntlm->response); 841 memset(ntlm, 0, sizeof(*ntlm)); 842 free(ntlm); 843 return 0; 844 } 845 846 847 krb5_error_code 848 krb5_ntlm_init_request(krb5_context context, 849 krb5_ntlm ntlm, 850 krb5_realm realm, 851 krb5_ccache ccache, 852 uint32_t flags, 853 const char *hostname, 854 const char *domainname) 855 { 856 DigestReqInner ireq; 857 DigestRepInner irep; 858 krb5_error_code ret; 859 860 memset(&ireq, 0, sizeof(ireq)); 861 memset(&irep, 0, sizeof(irep)); 862 863 ntlm->init.flags = flags; 864 if (hostname) { 865 ALLOC(ntlm->init.hostname, 1); 866 *ntlm->init.hostname = strdup(hostname); 867 } 868 if (domainname) { 869 ALLOC(ntlm->init.domain, 1); 870 *ntlm->init.domain = strdup(domainname); 871 } 872 873 ireq.element = choice_DigestReqInner_ntlmInit; 874 ireq.u.ntlmInit = ntlm->init; 875 876 ret = digest_request(context, realm, ccache, 877 KRB5_KU_DIGEST_ENCRYPT, &ireq, &irep); 878 if (ret) 879 goto out; 880 881 if (irep.element == choice_DigestRepInner_error) { 882 krb5_set_error_string(context, "Digest init error: %s", 883 irep.u.error.reason); 884 ret = irep.u.error.code; 885 goto out; 886 } 887 888 if (irep.element != choice_DigestRepInner_ntlmInitReply) { 889 krb5_set_error_string(context, "ntlm reply not an initReply"); 890 ret = EINVAL; 891 goto out; 892 } 893 894 ret = copy_NTLMInitReply(&irep.u.ntlmInitReply, &ntlm->initReply); 895 if (ret) { 896 krb5_set_error_string(context, "Failed to copy initReply"); 897 goto out; 898 } 899 900 out: 901 free_DigestRepInner(&irep); 902 903 return ret; 904 } 905 906 krb5_error_code 907 krb5_ntlm_init_get_flags(krb5_context context, 908 krb5_ntlm ntlm, 909 uint32_t *flags) 910 { 911 *flags = ntlm->initReply.flags; 912 return 0; 913 } 914 915 krb5_error_code 916 krb5_ntlm_init_get_challange(krb5_context context, 917 krb5_ntlm ntlm, 918 krb5_data *challange) 919 { 920 krb5_error_code ret; 921 922 ret = der_copy_octet_string(&ntlm->initReply.challange, challange); 923 if (ret) 924 krb5_clear_error_string(context); 925 926 return ret; 927 } 928 929 krb5_error_code 930 krb5_ntlm_init_get_opaque(krb5_context context, 931 krb5_ntlm ntlm, 932 krb5_data *opaque) 933 { 934 krb5_error_code ret; 935 936 ret = der_copy_octet_string(&ntlm->initReply.opaque, opaque); 937 if (ret) 938 krb5_clear_error_string(context); 939 940 return ret; 941 } 942 943 krb5_error_code 944 krb5_ntlm_init_get_targetname(krb5_context context, 945 krb5_ntlm ntlm, 946 char **name) 947 { 948 *name = strdup(ntlm->initReply.targetname); 949 if (*name == NULL) { 950 krb5_clear_error_string(context); 951 return ENOMEM; 952 } 953 return 0; 954 } 955 956 krb5_error_code 957 krb5_ntlm_init_get_targetinfo(krb5_context context, 958 krb5_ntlm ntlm, 959 krb5_data *data) 960 { 961 krb5_error_code ret; 962 963 if (ntlm->initReply.targetinfo == NULL) { 964 krb5_data_zero(data); 965 return 0; 966 } 967 968 ret = krb5_data_copy(data, 969 ntlm->initReply.targetinfo->data, 970 ntlm->initReply.targetinfo->length); 971 if (ret) { 972 krb5_clear_error_string(context); 973 return ret; 974 } 975 return 0; 976 } 977 978 979 krb5_error_code 980 krb5_ntlm_request(krb5_context context, 981 krb5_ntlm ntlm, 982 krb5_realm realm, 983 krb5_ccache ccache) 984 { 985 DigestReqInner ireq; 986 DigestRepInner irep; 987 krb5_error_code ret; 988 989 memset(&ireq, 0, sizeof(ireq)); 990 memset(&irep, 0, sizeof(irep)); 991 992 ireq.element = choice_DigestReqInner_ntlmRequest; 993 ireq.u.ntlmRequest = ntlm->request; 994 995 ret = digest_request(context, realm, ccache, 996 KRB5_KU_DIGEST_ENCRYPT, &ireq, &irep); 997 if (ret) 998 return ret; 999 1000 if (irep.element == choice_DigestRepInner_error) { 1001 krb5_set_error_string(context, "NTLM response error: %s", 1002 irep.u.error.reason); 1003 ret = irep.u.error.code; 1004 goto out; 1005 } 1006 1007 if (irep.element != choice_DigestRepInner_ntlmResponse) { 1008 krb5_set_error_string(context, "NTLM reply not an NTLMResponse"); 1009 ret = EINVAL; 1010 goto out; 1011 } 1012 1013 ret = copy_NTLMResponse(&irep.u.ntlmResponse, &ntlm->response); 1014 if (ret) { 1015 krb5_set_error_string(context, "Failed to copy NTLMResponse"); 1016 goto out; 1017 } 1018 1019 out: 1020 free_DigestRepInner(&irep); 1021 1022 return ret; 1023 } 1024 1025 krb5_error_code 1026 krb5_ntlm_req_set_flags(krb5_context context, 1027 krb5_ntlm ntlm, 1028 uint32_t flags) 1029 { 1030 ntlm->request.flags = flags; 1031 return 0; 1032 } 1033 1034 krb5_error_code 1035 krb5_ntlm_req_set_username(krb5_context context, 1036 krb5_ntlm ntlm, 1037 const char *username) 1038 { 1039 ntlm->request.username = strdup(username); 1040 if (ntlm->request.username == NULL) { 1041 krb5_set_error_string(context, "out of memory"); 1042 return ENOMEM; 1043 } 1044 return 0; 1045 } 1046 1047 krb5_error_code 1048 krb5_ntlm_req_set_targetname(krb5_context context, 1049 krb5_ntlm ntlm, 1050 const char *targetname) 1051 { 1052 ntlm->request.targetname = strdup(targetname); 1053 if (ntlm->request.targetname == NULL) { 1054 krb5_set_error_string(context, "out of memory"); 1055 return ENOMEM; 1056 } 1057 return 0; 1058 } 1059 1060 krb5_error_code 1061 krb5_ntlm_req_set_lm(krb5_context context, 1062 krb5_ntlm ntlm, 1063 void *hash, size_t len) 1064 { 1065 ntlm->request.lm.data = malloc(len); 1066 if (ntlm->request.lm.data == NULL) { 1067 krb5_set_error_string(context, "out of memory"); 1068 return ENOMEM; 1069 } 1070 ntlm->request.lm.length = len; 1071 memcpy(ntlm->request.lm.data, hash, len); 1072 return 0; 1073 } 1074 1075 krb5_error_code 1076 krb5_ntlm_req_set_ntlm(krb5_context context, 1077 krb5_ntlm ntlm, 1078 void *hash, size_t len) 1079 { 1080 ntlm->request.ntlm.data = malloc(len); 1081 if (ntlm->request.ntlm.data == NULL) { 1082 krb5_set_error_string(context, "out of memory"); 1083 return ENOMEM; 1084 } 1085 ntlm->request.ntlm.length = len; 1086 memcpy(ntlm->request.ntlm.data, hash, len); 1087 return 0; 1088 } 1089 1090 krb5_error_code 1091 krb5_ntlm_req_set_opaque(krb5_context context, 1092 krb5_ntlm ntlm, 1093 krb5_data *opaque) 1094 { 1095 ntlm->request.opaque.data = malloc(opaque->length); 1096 if (ntlm->request.opaque.data == NULL) { 1097 krb5_set_error_string(context, "out of memory"); 1098 return ENOMEM; 1099 } 1100 ntlm->request.opaque.length = opaque->length; 1101 memcpy(ntlm->request.opaque.data, opaque->data, opaque->length); 1102 return 0; 1103 } 1104 1105 krb5_error_code 1106 krb5_ntlm_req_set_session(krb5_context context, 1107 krb5_ntlm ntlm, 1108 void *sessionkey, size_t length) 1109 { 1110 ntlm->request.sessionkey = calloc(1, sizeof(*ntlm->request.sessionkey)); 1111 if (ntlm->request.sessionkey == NULL) { 1112 krb5_set_error_string(context, "out of memory"); 1113 return ENOMEM; 1114 } 1115 ntlm->request.sessionkey->data = malloc(length); 1116 if (ntlm->request.sessionkey->data == NULL) { 1117 krb5_set_error_string(context, "out of memory"); 1118 return ENOMEM; 1119 } 1120 memcpy(ntlm->request.sessionkey->data, sessionkey, length); 1121 ntlm->request.sessionkey->length = length; 1122 return 0; 1123 } 1124 1125 krb5_boolean 1126 krb5_ntlm_rep_get_status(krb5_context context, 1127 krb5_ntlm ntlm) 1128 { 1129 return ntlm->response.success ? TRUE : FALSE; 1130 } 1131 1132 krb5_error_code 1133 krb5_ntlm_rep_get_sessionkey(krb5_context context, 1134 krb5_ntlm ntlm, 1135 krb5_data *data) 1136 { 1137 if (ntlm->response.sessionkey == NULL) { 1138 krb5_set_error_string(context, "no ntlm session key"); 1139 return EINVAL; 1140 } 1141 krb5_clear_error_string(context); 1142 return krb5_data_copy(data, 1143 ntlm->response.sessionkey->data, 1144 ntlm->response.sessionkey->length); 1145 } 1146 1147 /** 1148 * Get the supported/allowed mechanism for this principal. 1149 * 1150 * @param context A Keberos context. 1151 * @param realm The realm of the KDC. 1152 * @param ccache The credential cache to use when talking to the KDC. 1153 * @param flags The supported mechanism. 1154 * 1155 * @return Return an error code or 0. 1156 * 1157 * @ingroup krb5_digest 1158 */ 1159 1160 krb5_error_code 1161 krb5_digest_probe(krb5_context context, 1162 krb5_realm realm, 1163 krb5_ccache ccache, 1164 unsigned *flags) 1165 { 1166 DigestReqInner ireq; 1167 DigestRepInner irep; 1168 krb5_error_code ret; 1169 1170 memset(&ireq, 0, sizeof(ireq)); 1171 memset(&irep, 0, sizeof(irep)); 1172 1173 ireq.element = choice_DigestReqInner_supportedMechs; 1174 1175 ret = digest_request(context, realm, ccache, 1176 KRB5_KU_DIGEST_ENCRYPT, &ireq, &irep); 1177 if (ret) 1178 goto out; 1179 1180 if (irep.element == choice_DigestRepInner_error) { 1181 krb5_set_error_string(context, "Digest probe error: %s", 1182 irep.u.error.reason); 1183 ret = irep.u.error.code; 1184 goto out; 1185 } 1186 1187 if (irep.element != choice_DigestRepInner_supportedMechs) { 1188 krb5_set_error_string(context, "Digest reply not an probe"); 1189 ret = EINVAL; 1190 goto out; 1191 } 1192 1193 *flags = DigestTypes2int(irep.u.supportedMechs); 1194 1195 out: 1196 free_DigestRepInner(&irep); 1197 1198 return ret; 1199 } 1200