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