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