1/* 2 * Copyright (c) 2005, PADL Software Pty Ltd. 3 * All rights reserved. 4 * 5 * Portions Copyright (c) 2009 Apple Inc. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * 3. Neither the name of PADL Software nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35#include "kcm_locl.h" 36#include <heimntlm.h> 37#include <heimscram.h> 38 39static void 40kcm_drop_default_cache(krb5_context context, kcm_client *client, char *name); 41 42int 43kcm_is_same_session(kcm_client *client, uid_t uid, pid_t session) 44{ 45 /* 46 * Only same session 47 * Let user access any credential regardless of session. 48 * Deny otherwise. 49 */ 50 51 if (use_uid_matching && client->uid != 0 && client->uid == uid) { 52 kcm_log(1, "allowed (uid matching)"); 53 return 1; 54 } else if (client->session == session) { 55 kcm_log(1, "allowed (session matching)"); 56 return 1; 57 } 58 59 60 kcm_log(1, "denied"); 61 return 0; 62} 63 64static krb5_error_code 65kcm_op_noop(krb5_context context, 66 kcm_client *client, 67 kcm_operation opcode, 68 krb5_storage *request, 69 krb5_storage *response) 70{ 71 KCM_LOG_REQUEST(context, client, opcode); 72 73 return 0; 74} 75 76/* 77 * Request: 78 * NameZ 79 * Response: 80 * NameZ 81 * 82 */ 83static krb5_error_code 84kcm_op_get_name(krb5_context context, 85 kcm_client *client, 86 kcm_operation opcode, 87 krb5_storage *request, 88 krb5_storage *response) 89 90{ 91 krb5_error_code ret; 92 char *name = NULL; 93 kcm_ccache ccache; 94 95 ret = krb5_ret_stringz(request, &name); 96 if (ret) 97 return ret; 98 99 KCM_LOG_REQUEST_NAME(context, client, opcode, name); 100 101 ret = kcm_ccache_resolve_client(context, client, opcode, 102 name, &ccache); 103 if (ret) { 104 free(name); 105 return ret; 106 } 107 108 ret = krb5_store_stringz(response, ccache->name); 109 if (ret) { 110 kcm_release_ccache(context, ccache); 111 free(name); 112 return ret; 113 } 114 115 free(name); 116 kcm_release_ccache(context, ccache); 117 return 0; 118} 119 120/* 121 * Request: 122 * 123 * Response: 124 * NameZ 125 */ 126static krb5_error_code 127kcm_op_gen_new(krb5_context context, 128 kcm_client *client, 129 kcm_operation opcode, 130 krb5_storage *request, 131 krb5_storage *response) 132{ 133 krb5_error_code ret; 134 char *name; 135 136 KCM_LOG_REQUEST(context, client, opcode); 137 138 /* deprecated */ 139 140 name = kcm_ccache_nextid(client->pid, client->uid); 141 if (name == NULL) { 142 return KRB5_CC_NOMEM; 143 } 144 145 ret = krb5_store_stringz(response, name); 146 free(name); 147 148 return ret; 149} 150 151/* 152 * Request: 153 * NameZ 154 * Principal 155 * 156 * Response: 157 * 158 */ 159static krb5_error_code 160kcm_op_initialize(krb5_context context, 161 kcm_client *client, 162 kcm_operation opcode, 163 krb5_storage *request, 164 krb5_storage *response) 165{ 166 kcm_ccache ccache; 167 krb5_principal principal; 168 krb5_error_code ret; 169 char *name; 170#if 0 171 kcm_event event; 172#endif 173 174 KCM_LOG_REQUEST(context, client, opcode); 175 176 ret = krb5_ret_stringz(request, &name); 177 if (ret) 178 return ret; 179 180 ret = krb5_ret_principal(request, &principal); 181 if (ret) { 182 free(name); 183 return ret; 184 } 185 186 ret = kcm_ccache_new_client(context, client, name, &ccache); 187 if (ret) { 188 free(name); 189 krb5_free_principal(context, principal); 190 return ret; 191 } 192 193 ccache->client = principal; 194 195 free(name); 196 197 kcm_release_ccache(context, ccache); 198 199 kcm_data_changed = 1; 200 201 return ret; 202} 203 204/* 205 * Request: 206 * NameZ 207 * 208 * Response: 209 * 210 */ 211static krb5_error_code 212kcm_op_destroy(krb5_context context, 213 kcm_client *client, 214 kcm_operation opcode, 215 krb5_storage *request, 216 krb5_storage *response) 217{ 218 krb5_error_code ret; 219 char *name; 220 221 ret = krb5_ret_stringz(request, &name); 222 if (ret) 223 return ret; 224 225 KCM_LOG_REQUEST_NAME(context, client, opcode, name); 226 227 ret = kcm_ccache_destroy_client(context, client, name); 228 if (ret == 0) 229 kcm_drop_default_cache(context, client, name); 230 231 free(name); 232 233 kcm_data_changed = 1; 234 235 return ret; 236} 237 238/* 239 * Request: 240 * NameZ 241 * Creds 242 * 243 * Response: 244 * 245 */ 246static krb5_error_code 247kcm_op_store(krb5_context context, 248 kcm_client *client, 249 kcm_operation opcode, 250 krb5_storage *request, 251 krb5_storage *response) 252{ 253 krb5_creds creds; 254 krb5_error_code ret; 255 kcm_ccache ccache; 256 char *name; 257 258 ret = krb5_ret_stringz(request, &name); 259 if (ret) 260 return ret; 261 262 KCM_LOG_REQUEST_NAME(context, client, opcode, name); 263 264 ret = krb5_ret_creds(request, &creds); 265 if (ret) { 266 free(name); 267 return ret; 268 } 269 270 ret = kcm_ccache_resolve_client(context, client, opcode, 271 name, &ccache); 272 if (ret) { 273 free(name); 274 krb5_free_cred_contents(context, &creds); 275 return ret; 276 } 277 278 ret = kcm_ccache_store_cred(context, ccache, &creds, 0); 279 if (ret) { 280 free(name); 281 krb5_free_cred_contents(context, &creds); 282 kcm_release_ccache(context, ccache); 283 return ret; 284 } 285 286 if (creds.client && krb5_principal_is_root_krbtgt(context, creds.server)) 287 kcm_ccache_enqueue_default(context, ccache, &creds); 288 289 free(name); 290 kcm_release_ccache(context, ccache); 291 292 kcm_data_changed = 1; 293 294 return 0; 295} 296 297/* 298 * Request: 299 * NameZ 300 * WhichFields 301 * MatchCreds 302 * 303 * Response: 304 * Creds 305 * 306 */ 307static krb5_error_code 308kcm_op_retrieve(krb5_context context, 309 kcm_client *client, 310 kcm_operation opcode, 311 krb5_storage *request, 312 krb5_storage *response) 313{ 314 uint32_t flags; 315 krb5_creds mcreds; 316 krb5_error_code ret; 317 kcm_ccache ccache; 318 char *name; 319 krb5_creds *credp; 320 321 ret = krb5_ret_stringz(request, &name); 322 if (ret) 323 return ret; 324 325 KCM_LOG_REQUEST_NAME(context, client, opcode, name); 326 327 ret = krb5_ret_uint32(request, &flags); 328 if (ret) { 329 free(name); 330 goto out; 331 } 332 333 if (flags & KRB5_TC_MATCH_REFERRAL) 334 flags |= KRB5_TC_DONT_MATCH_REALM; 335 336 ret = krb5_ret_creds_tag(request, &mcreds); 337 if (ret) { 338 free(name); 339 goto out; 340 } 341 342 if (disallow_getting_krbtgt && 343 mcreds.server->name.name_string.len == 2 && 344 strcmp(mcreds.server->name.name_string.val[0], KRB5_TGS_NAME) == 0) 345 { 346 krb5_free_cred_contents(context, &mcreds); 347 ret = KRB5_FCC_PERM; 348 free(name); 349 goto out; 350 } 351 352 ret = kcm_ccache_resolve_client(context, client, opcode, 353 name, &ccache); 354 if (ret) { 355 krb5_free_cred_contents(context, &mcreds); 356 goto out; 357 } 358 359 ret = kcm_ccache_retrieve_cred(context, ccache, flags, 360 &mcreds, &credp); 361 if (ret == 0) 362 ret = krb5_store_creds(response, credp); 363 364 kcm_release_ccache(context, ccache); 365 krb5_free_cred_contents(context, &mcreds); 366 367 out: 368 free(name); 369 return ret; 370} 371 372/* 373 * Request: 374 * NameZ 375 * 376 * Response: 377 * Principal 378 */ 379static krb5_error_code 380kcm_op_get_principal(krb5_context context, 381 kcm_client *client, 382 kcm_operation opcode, 383 krb5_storage *request, 384 krb5_storage *response) 385{ 386 krb5_error_code ret; 387 kcm_ccache ccache; 388 char *name; 389 390 ret = krb5_ret_stringz(request, &name); 391 if (ret) 392 return ret; 393 394 KCM_LOG_REQUEST_NAME(context, client, opcode, name); 395 396 ret = kcm_ccache_resolve_client(context, client, opcode, 397 name, &ccache); 398 if (ret) { 399 free(name); 400 return ret; 401 } 402 403 if (ccache->client == NULL) 404 ret = KRB5_CC_NOTFOUND; 405 else 406 ret = krb5_store_principal(response, ccache->client); 407 408 free(name); 409 kcm_release_ccache(context, ccache); 410 411 return ret; 412} 413 414/* 415 * Request: 416 * NameZ 417 * 418 * Response: 419 * UUIDs 420 * 421 */ 422static krb5_error_code 423kcm_op_get_cred_uuid_list(krb5_context context, 424 kcm_client *client, 425 kcm_operation opcode, 426 krb5_storage *request, 427 krb5_storage *response) 428{ 429 struct kcm_creds *creds; 430 krb5_error_code ret; 431 kcm_ccache ccache; 432 char *name; 433 434 ret = krb5_ret_stringz(request, &name); 435 if (ret) 436 return ret; 437 438 KCM_LOG_REQUEST_NAME(context, client, opcode, name); 439 440 ret = kcm_ccache_resolve_client(context, client, opcode, 441 name, &ccache); 442 free(name); 443 if (ret) 444 return ret; 445 446 for (creds = ccache->creds ; creds ; creds = creds->next) { 447 ssize_t sret; 448 sret = krb5_storage_write(response, &creds->uuid, sizeof(creds->uuid)); 449 if (sret != sizeof(creds->uuid)) { 450 ret = ENOMEM; 451 break; 452 } 453 } 454 455 kcm_release_ccache(context, ccache); 456 457 return ret; 458} 459 460/* 461 * Request: 462 * NameZ 463 * Cursor 464 * 465 * Response: 466 * Creds 467 */ 468static krb5_error_code 469kcm_op_get_cred_by_uuid(krb5_context context, 470 kcm_client *client, 471 kcm_operation opcode, 472 krb5_storage *request, 473 krb5_storage *response) 474{ 475 krb5_error_code ret; 476 kcm_ccache ccache; 477 char *name; 478 struct kcm_creds *c; 479 kcmuuid_t uuid; 480 ssize_t sret; 481 482 ret = krb5_ret_stringz(request, &name); 483 if (ret) 484 return ret; 485 486 KCM_LOG_REQUEST_NAME(context, client, opcode, name); 487 488 ret = kcm_ccache_resolve_client(context, client, opcode, 489 name, &ccache); 490 free(name); 491 if (ret) 492 return ret; 493 494 sret = krb5_storage_read(request, &uuid, sizeof(uuid)); 495 if (sret != sizeof(uuid)) { 496 kcm_release_ccache(context, ccache); 497 krb5_clear_error_message(context); 498 return KRB5_CC_IO; 499 } 500 501 c = kcm_ccache_find_cred_uuid(context, ccache, uuid); 502 if (c == NULL) { 503 kcm_release_ccache(context, ccache); 504 return KRB5_CC_END; 505 } 506 507 HEIMDAL_MUTEX_lock(&ccache->mutex); 508 ret = krb5_store_creds(response, &c->cred); 509 HEIMDAL_MUTEX_unlock(&ccache->mutex); 510 511 kcm_release_ccache(context, ccache); 512 513 return ret; 514} 515 516/* 517 * Request: 518 * NameZ 519 * WhichFields 520 * MatchCreds 521 * 522 * Response: 523 * 524 */ 525static krb5_error_code 526kcm_op_remove_cred(krb5_context context, 527 kcm_client *client, 528 kcm_operation opcode, 529 krb5_storage *request, 530 krb5_storage *response) 531{ 532 uint32_t whichfields; 533 krb5_creds mcreds; 534 krb5_error_code ret; 535 kcm_ccache ccache; 536 char *name; 537 538 ret = krb5_ret_stringz(request, &name); 539 if (ret) 540 return ret; 541 542 KCM_LOG_REQUEST_NAME(context, client, opcode, name); 543 544 ret = krb5_ret_uint32(request, &whichfields); 545 if (ret) { 546 free(name); 547 return ret; 548 } 549 550 ret = krb5_ret_creds_tag(request, &mcreds); 551 if (ret) { 552 free(name); 553 return ret; 554 } 555 556 ret = kcm_ccache_resolve_client(context, client, opcode, 557 name, &ccache); 558 if (ret) { 559 free(name); 560 krb5_free_cred_contents(context, &mcreds); 561 return ret; 562 } 563 564 ret = kcm_ccache_remove_cred(context, ccache, whichfields, &mcreds); 565 566 /* XXX need to remove any events that match */ 567 568 free(name); 569 krb5_free_cred_contents(context, &mcreds); 570 kcm_release_ccache(context, ccache); 571 572 kcm_data_changed = 1; 573 574 return ret; 575} 576 577/* 578 * Request: 579 * NameZ 580 * Flags 581 * 582 * Response: 583 * 584 */ 585static krb5_error_code 586kcm_op_set_flags(krb5_context context, 587 kcm_client *client, 588 kcm_operation opcode, 589 krb5_storage *request, 590 krb5_storage *response) 591{ 592 uint32_t flags; 593 krb5_error_code ret; 594 kcm_ccache ccache; 595 char *name; 596 597 ret = krb5_ret_stringz(request, &name); 598 if (ret) 599 return ret; 600 601 KCM_LOG_REQUEST_NAME(context, client, opcode, name); 602 603 ret = krb5_ret_uint32(request, &flags); 604 if (ret) { 605 free(name); 606 return ret; 607 } 608 609 ret = kcm_ccache_resolve_client(context, client, opcode, 610 name, &ccache); 611 if (ret) { 612 free(name); 613 return ret; 614 } 615 616 /* we don't really support any flags yet */ 617 free(name); 618 kcm_release_ccache(context, ccache); 619 620 return 0; 621} 622 623/* 624 * Request: 625 * NameZ 626 * UID 627 * GID 628 * 629 * Response: 630 * 631 */ 632static krb5_error_code 633kcm_op_chown(krb5_context context, 634 kcm_client *client, 635 kcm_operation opcode, 636 krb5_storage *request, 637 krb5_storage *response) 638{ 639 uint32_t uid; 640 uint32_t gid; 641 krb5_error_code ret; 642 kcm_ccache ccache; 643 char *name; 644 645 ret = krb5_ret_stringz(request, &name); 646 if (ret) 647 return ret; 648 649 KCM_LOG_REQUEST_NAME(context, client, opcode, name); 650 651 ret = krb5_ret_uint32(request, &uid); 652 if (ret) { 653 free(name); 654 return ret; 655 } 656 657 ret = krb5_ret_uint32(request, &gid); 658 if (ret) { 659 free(name); 660 return ret; 661 } 662 663 ret = kcm_ccache_resolve_client(context, client, opcode, 664 name, &ccache); 665 if (ret) { 666 free(name); 667 return ret; 668 } 669 670 free(name); 671 kcm_release_ccache(context, ccache); 672 673 kcm_data_changed = 1; 674 675 return ret; 676} 677 678/* 679 * Request: 680 * NameZ 681 * Mode 682 * 683 * Response: 684 * 685 */ 686static krb5_error_code 687kcm_op_chmod(krb5_context context, 688 kcm_client *client, 689 kcm_operation opcode, 690 krb5_storage *request, 691 krb5_storage *response) 692{ 693 uint16_t mode; 694 krb5_error_code ret; 695 kcm_ccache ccache; 696 char *name; 697 698 ret = krb5_ret_stringz(request, &name); 699 if (ret) 700 return ret; 701 702 KCM_LOG_REQUEST_NAME(context, client, opcode, name); 703 704 ret = krb5_ret_uint16(request, &mode); 705 if (ret) { 706 free(name); 707 return ret; 708 } 709 710 ret = kcm_ccache_resolve_client(context, client, opcode, 711 name, &ccache); 712 if (ret) { 713 free(name); 714 return ret; 715 } 716 717 ret = kcm_chmod(context, client, ccache, mode); 718 719 free(name); 720 kcm_release_ccache(context, ccache); 721 722 kcm_data_changed = 1; 723 724 return ret; 725} 726 727/* 728 * Protocol extensions for moving ticket acquisition responsibility 729 * from client to KCM follow. 730 */ 731 732/* 733 * Request: 734 * NameZ 735 * clientPrincipal 736 * ServerPrincipalPresent 737 * ServerPrincipal OPTIONAL 738 * password 739 * 740 * Repsonse: 741 * 742 */ 743static krb5_error_code 744kcm_op_get_initial_ticket(krb5_context context, 745 kcm_client *client, 746 kcm_operation opcode, 747 krb5_storage *request, 748 krb5_storage *response) 749{ 750 char *name, *password; 751 krb5_error_code ret; 752 kcm_ccache ccache; 753 int8_t not_tgt = 0; 754 krb5_principal cprincipal = NULL; 755 krb5_principal server = NULL; 756 757 ret = krb5_ret_stringz(request, &name); 758 if (ret) 759 return ret; 760 761 KCM_LOG_REQUEST_NAME(context, client, opcode, name); 762 763 764 ret = krb5_ret_principal(request, &cprincipal); 765 if (ret) { 766 free(name); 767 return ret; 768 } 769 770 ret = krb5_ret_int8(request, ¬_tgt); 771 if (ret) { 772 free(name); 773 return ret; 774 } 775 776 if (not_tgt) { 777 ret = krb5_ret_principal(request, &server); 778 } else { 779 ret = krb5_make_principal(context,&server, cprincipal->realm, 780 KRB5_TGS_NAME, cprincipal->realm, 781 NULL); 782 } 783 if (ret) { 784 krb5_free_principal(context, cprincipal); 785 free(name); 786 return ret; 787 } 788 789 ret = krb5_ret_stringz(request, &password); 790 if (ret) { 791 free(name); 792 krb5_free_principal(context, cprincipal); 793 if (server != NULL) 794 krb5_free_principal(context, server); 795 return ret; 796 } 797 798 ret = kcm_ccache_resolve_client(context, client, opcode, 799 name, &ccache); 800 if (ret == 0) { 801 HEIMDAL_MUTEX_lock(&ccache->mutex); 802 803 if (ccache->client) 804 krb5_free_principal(context, ccache->client); 805 if (ccache->server) 806 krb5_free_principal(context, ccache->server); 807 if (ccache->password) { 808 memset(ccache->password, 0, strlen(ccache->password)); 809 free(ccache->password); 810 } 811 812 ccache->client = cprincipal; 813 ccache->server = server; 814 ccache->password = password; 815 ccache->flags |= KCM_FLAGS_USE_PASSWORD; 816 ccache->renew_life = 3600 * 24 * 7; /* 1 week */ 817 818 kcm_ccache_update_acquire_status(kcm_context, ccache, KCM_STATUS_ACQUIRE_START, 0); 819 820 HEIMDAL_MUTEX_unlock(&ccache->mutex); 821 822 kcm_release_ccache(context, ccache); 823 824 kcm_data_changed = 1; 825 } else { 826 krb5_free_principal(context, cprincipal); 827 if (server) 828 krb5_free_principal(context, server); 829 memset(password, 0, strlen(password)); 830 free(password); 831 } 832 833 834 free(name); 835 836 return ret; 837} 838 839/* 840 * Request: 841 * NameZ 842 * ServerPrincipal 843 * KDCFlags 844 * EncryptionType 845 * 846 * Repsonse: 847 * 848 */ 849static krb5_error_code 850kcm_op_get_ticket(krb5_context context, 851 kcm_client *client, 852 kcm_operation opcode, 853 krb5_storage *request, 854 krb5_storage *response) 855{ 856 krb5_error_code ret; 857 kcm_ccache ccache; 858 char *name; 859 krb5_principal server = NULL; 860 krb5_ccache_data ccdata; 861 krb5_creds in, *out; 862 krb5_kdc_flags flags; 863 864 memset(&in, 0, sizeof(in)); 865 866 ret = krb5_ret_stringz(request, &name); 867 if (ret) 868 return ret; 869 870 KCM_LOG_REQUEST_NAME(context, client, opcode, name); 871 872 ret = krb5_ret_uint32(request, &flags.i); 873 if (ret) { 874 free(name); 875 return ret; 876 } 877 878 ret = krb5_ret_int32(request, &in.session.keytype); 879 if (ret) { 880 free(name); 881 return ret; 882 } 883 884 ret = krb5_ret_principal(request, &server); 885 if (ret) { 886 free(name); 887 return ret; 888 } 889 890 ret = kcm_ccache_resolve_client(context, client, opcode, 891 name, &ccache); 892 if (ret) { 893 krb5_free_principal(context, server); 894 free(name); 895 return ret; 896 } 897 898 HEIMDAL_MUTEX_lock(&ccache->mutex); 899 900 /* Fake up an internal ccache */ 901 kcm_internal_ccache(context, ccache, &ccdata); 902 903 in.client = ccache->client; 904 in.server = server; 905 in.times.endtime = 0; 906 907 /* glue cc layer will store creds */ 908 ret = krb5_get_credentials_with_flags(context, 0, flags, 909 &ccdata, &in, &out); 910 911 HEIMDAL_MUTEX_unlock(&ccache->mutex); 912 913 krb5_free_principal(context, server); 914 915 if (ret == 0) 916 krb5_free_cred_contents(context, out); 917 918 kcm_release_ccache(context, ccache); 919 free(name); 920 921 kcm_data_changed = 1; 922 923 return ret; 924} 925 926/* 927 * Request: 928 * OldNameZ 929 * NewNameZ 930 * 931 * Repsonse: 932 * 933 */ 934static krb5_error_code 935kcm_op_move_cache(krb5_context context, 936 kcm_client *client, 937 kcm_operation opcode, 938 krb5_storage *request, 939 krb5_storage *response) 940{ 941 krb5_error_code ret; 942 kcm_ccache oldid, newid; 943 char *oldname, *newname; 944 945 ret = krb5_ret_stringz(request, &oldname); 946 if (ret) 947 return ret; 948 949 KCM_LOG_REQUEST_NAME(context, client, opcode, oldname); 950 951 ret = krb5_ret_stringz(request, &newname); 952 if (ret) { 953 free(oldname); 954 return ret; 955 } 956 957 /* if we are renaming to ourself, done! */ 958 if (strcmp(newname, oldname) == 0) { 959 free(oldname); 960 free(newname); 961 return 0; 962 } 963 964 ret = kcm_ccache_resolve_client(context, client, opcode, oldname, &oldid); 965 if (ret) { 966 free(oldname); 967 free(newname); 968 return ret; 969 } 970 971 /* Check if new credential cache exists, if not create one. */ 972 ret = kcm_ccache_resolve_client(context, client, opcode, newname, &newid); 973 if (ret == KRB5_FCC_NOFILE) 974 ret = kcm_ccache_new_client(context, client, newname, &newid); 975 free(newname); 976 977 if (ret) { 978 free(oldname); 979 kcm_release_ccache(context, oldid); 980 return ret; 981 } 982 983 HEIMDAL_MUTEX_lock(&oldid->mutex); 984 HEIMDAL_MUTEX_lock(&newid->mutex); 985 986 /* move content */ 987 { 988 struct kcm_ccache_data tmp; 989 990#define MOVE(n,o,f) { tmp.f = n->f ; n->f = o->f; o->f = tmp.f; } 991 992 MOVE(newid, oldid, flags); 993 MOVE(newid, oldid, client); 994 MOVE(newid, oldid, server); 995 MOVE(newid, oldid, creds); 996 MOVE(newid, oldid, tkt_life); 997 MOVE(newid, oldid, renew_life); 998 MOVE(newid, oldid, password); 999 MOVE(newid, oldid, keytab); 1000 MOVE(newid, oldid, kdc_offset); 1001 MOVE(newid, oldid, expire); 1002#undef MOVE 1003 } 1004 1005 kcm_update_renew_time(newid); 1006 1007 if (newid->expire && (newid->flags & KCM_MASK_KEY_PRESENT) == 0 && time(NULL) < newid->expire) 1008 kcm_update_expire_time(newid, newid->expire); 1009 1010 HEIMDAL_MUTEX_unlock(&oldid->mutex); 1011 HEIMDAL_MUTEX_unlock(&newid->mutex); 1012 1013 kcm_release_ccache(context, oldid); 1014 kcm_release_ccache(context, newid); 1015 1016 ret = kcm_ccache_destroy_client(context, client, oldname); 1017 if (ret == 0) 1018 kcm_drop_default_cache(context, client, oldname); 1019 1020 free(oldname); 1021 1022 kcm_data_changed = 1; 1023 1024 return ret; 1025} 1026 1027static krb5_error_code 1028kcm_op_get_cache_uuid_list(krb5_context context, 1029 kcm_client *client, 1030 kcm_operation opcode, 1031 krb5_storage *request, 1032 krb5_storage *response) 1033{ 1034 KCM_LOG_REQUEST(context, client, opcode); 1035 1036 return kcm_ccache_get_uuids(context, client, opcode, response); 1037} 1038 1039static krb5_error_code 1040kcm_op_get_cache_by_uuid(krb5_context context, 1041 kcm_client *client, 1042 kcm_operation opcode, 1043 krb5_storage *request, 1044 krb5_storage *response) 1045{ 1046 krb5_error_code ret; 1047 kcmuuid_t uuid; 1048 ssize_t sret; 1049 kcm_ccache cache; 1050 1051 KCM_LOG_REQUEST(context, client, opcode); 1052 1053 sret = krb5_storage_read(request, &uuid, sizeof(uuid)); 1054 if (sret != sizeof(uuid)) { 1055 krb5_clear_error_message(context); 1056 return KRB5_CC_IO; 1057 } 1058 1059 ret = kcm_ccache_resolve_by_uuid(context, uuid, &cache); 1060 if (ret) 1061 return ret; 1062 1063 ret = kcm_access(context, client, opcode, cache); 1064 if (ret) 1065 ret = KRB5_FCC_NOFILE; 1066 1067 if (ret == 0) 1068 ret = krb5_store_stringz(response, cache->name); 1069 1070 kcm_release_ccache(context, cache); 1071 1072 return ret; 1073} 1074 1075struct kcm_default_cache *default_caches; 1076 1077static krb5_error_code 1078kcm_op_get_default_cache(krb5_context context, 1079 kcm_client *client, 1080 kcm_operation opcode, 1081 krb5_storage *request, 1082 krb5_storage *response) 1083{ 1084 struct kcm_default_cache *c; 1085 krb5_error_code ret; 1086 const char *name = NULL; 1087 char *n = NULL; 1088 1089 KCM_LOG_REQUEST(context, client, opcode); 1090 1091 for (c = default_caches; c != NULL; c = c->next) { 1092 if (kcm_is_same_session(client, c->uid, c->session)) { 1093 name = c->name; 1094 break; 1095 } 1096 } 1097 if (name == NULL) 1098 name = n = kcm_ccache_first_name(client); 1099 1100 if (name == NULL) { 1101 asprintf(&n, "%d", (int)client->uid); 1102 name = n; 1103 } 1104 if (name == NULL) 1105 return ENOMEM; 1106 ret = krb5_store_stringz(response, name); 1107 if (n) 1108 free(n); 1109 return ret; 1110} 1111 1112static void 1113kcm_drop_default_cache(krb5_context context, kcm_client *client, char *name) 1114{ 1115 struct kcm_default_cache **c; 1116 1117 for (c = &default_caches; *c != NULL; c = &(*c)->next) { 1118 if (!kcm_is_same_session(client, (*c)->uid, (*c)->session)) 1119 continue; 1120 if (strcmp((*c)->name, name) == 0) { 1121 struct kcm_default_cache *h = *c; 1122 *c = (*c)->next; 1123 free(h->name); 1124 free(h); 1125 break; 1126 } 1127 } 1128} 1129 1130static krb5_error_code 1131kcm_op_set_default_cache(krb5_context context, 1132 kcm_client *client, 1133 kcm_operation opcode, 1134 krb5_storage *request, 1135 krb5_storage *response) 1136{ 1137 struct kcm_default_cache *c; 1138 krb5_error_code ret; 1139 char *name; 1140 1141 ret = krb5_ret_stringz(request, &name); 1142 if (ret) 1143 return ret; 1144 1145 KCM_LOG_REQUEST_NAME(context, client, opcode, name); 1146 1147 for (c = default_caches; c != NULL; c = c->next) { 1148 if (kcm_is_same_session(client, c->uid, c->session)) 1149 break; 1150 } 1151 if (c == NULL) { 1152 c = calloc(1, sizeof(*c)); 1153 if (c == NULL) { 1154 free(name); 1155 return ENOMEM; 1156 } 1157 c->session = client->session; 1158 c->uid = client->uid; 1159 c->name = name; 1160 1161 c->next = default_caches; 1162 default_caches = c; 1163 } else { 1164 free(c->name); 1165 c->name = name; 1166 } 1167 1168 return 0; 1169} 1170 1171static krb5_error_code 1172kcm_op_get_kdc_offset(krb5_context context, 1173 kcm_client *client, 1174 kcm_operation opcode, 1175 krb5_storage *request, 1176 krb5_storage *response) 1177{ 1178 krb5_error_code ret; 1179 kcm_ccache ccache; 1180 char *name; 1181 1182 ret = krb5_ret_stringz(request, &name); 1183 if (ret) 1184 return ret; 1185 1186 KCM_LOG_REQUEST_NAME(context, client, opcode, name); 1187 1188 ret = kcm_ccache_resolve_client(context, client, opcode, name, &ccache); 1189 free(name); 1190 if (ret) 1191 return ret; 1192 1193 HEIMDAL_MUTEX_lock(&ccache->mutex); 1194 ret = krb5_store_int32(response, ccache->kdc_offset); 1195 HEIMDAL_MUTEX_unlock(&ccache->mutex); 1196 1197 kcm_release_ccache(context, ccache); 1198 1199 return ret; 1200} 1201 1202static krb5_error_code 1203kcm_op_set_kdc_offset(krb5_context context, 1204 kcm_client *client, 1205 kcm_operation opcode, 1206 krb5_storage *request, 1207 krb5_storage *response) 1208{ 1209 krb5_error_code ret; 1210 kcm_ccache ccache; 1211 int32_t offset; 1212 char *name; 1213 1214 ret = krb5_ret_stringz(request, &name); 1215 if (ret) 1216 return ret; 1217 1218 KCM_LOG_REQUEST_NAME(context, client, opcode, name); 1219 1220 ret = krb5_ret_int32(request, &offset); 1221 if (ret) { 1222 free(name); 1223 return ret; 1224 } 1225 1226 ret = kcm_ccache_resolve_client(context, client, opcode, name, &ccache); 1227 free(name); 1228 if (ret) 1229 return ret; 1230 1231 HEIMDAL_MUTEX_lock(&ccache->mutex); 1232 ccache->kdc_offset = offset; 1233 HEIMDAL_MUTEX_unlock(&ccache->mutex); 1234 1235 kcm_release_ccache(context, ccache); 1236 1237 return ret; 1238} 1239 1240static krb5_error_code 1241kcm_op_retain_kcred(krb5_context context, 1242 kcm_client *client, 1243 kcm_operation opcode, 1244 krb5_storage *request, 1245 krb5_storage *response) 1246{ 1247 krb5_error_code ret; 1248 kcm_ccache ccache; 1249 char *name; 1250 1251 ret = krb5_ret_stringz(request, &name); 1252 if (ret) 1253 return ret; 1254 1255 KCM_LOG_REQUEST_NAME(context, client, opcode, name); 1256 1257 ret = kcm_ccache_resolve_client(context, client, opcode, name, &ccache); 1258 if (ret) { 1259 free(name); 1260 return ret; 1261 } 1262 1263 HEIMDAL_MUTEX_lock(&ccache->mutex); 1264 ccache->holdcount++; 1265 kcm_log(1, "retain_kcred: holdcount for %s is %ld", name, ccache->holdcount); 1266 HEIMDAL_MUTEX_unlock(&ccache->mutex); 1267 1268 kcm_release_ccache(context, ccache); 1269 free(name); 1270 1271 kcm_data_changed = 1; 1272 1273 return 0; 1274} 1275 1276static krb5_error_code 1277kcm_op_release_kcred(krb5_context context, 1278 kcm_client *client, 1279 kcm_operation opcode, 1280 krb5_storage *request, 1281 krb5_storage *response) 1282{ 1283 krb5_error_code ret; 1284 kcm_ccache ccache; 1285 char *name; 1286 int destroy = 0; 1287 1288 ret = krb5_ret_stringz(request, &name); 1289 if (ret) 1290 return ret; 1291 1292 KCM_LOG_REQUEST_NAME(context, client, opcode, name); 1293 1294 ret = kcm_ccache_resolve_client(context, client, opcode, name, &ccache); 1295 if (ret) { 1296 free(name); 1297 return ret; 1298 } 1299 1300 HEIMDAL_MUTEX_lock(&ccache->mutex); 1301 ccache->holdcount--; 1302 if (ccache->holdcount < 1) 1303 destroy = 1; 1304 kcm_log(1, "release_kcred: holdcount for %s is %ld", name, ccache->holdcount); 1305 HEIMDAL_MUTEX_unlock(&ccache->mutex); 1306 1307 kcm_release_ccache(context, ccache); 1308 1309 if (destroy) { 1310 kcm_log(1, "holdcount for %s is zero, removing", name); 1311 1312 ret = kcm_ccache_destroy_client(context, client, name); 1313 if (ret == 0) 1314 kcm_drop_default_cache(context, client, name); 1315 } 1316 free(name); 1317 1318 kcm_data_changed = 1; 1319 1320 return 0; 1321} 1322 1323static krb5_error_code 1324kcm_op_get_uuid(krb5_context context, 1325 kcm_client *client, 1326 kcm_operation opcode, 1327 krb5_storage *request, 1328 krb5_storage *response) 1329{ 1330 krb5_error_code ret; 1331 kcm_ccache ccache; 1332 char *name; 1333 krb5_uuid uuid; 1334 1335 ret = krb5_ret_stringz(request, &name); 1336 if (ret) 1337 return ret; 1338 1339 KCM_LOG_REQUEST_NAME(context, client, opcode, name); 1340 1341 ret = kcm_ccache_resolve_client(context, client, opcode, name, &ccache); 1342 free(name); 1343 if (ret) { 1344 return ret; 1345 } 1346 1347 HEIMDAL_MUTEX_lock(&ccache->mutex); 1348 memcpy(uuid, ccache->uuid, sizeof(uuid)); 1349 HEIMDAL_MUTEX_unlock(&ccache->mutex); 1350 1351 kcm_release_ccache(context, ccache); 1352 1353 (void)krb5_storage_write(response, uuid, sizeof(uuid)); 1354 1355 return 0; 1356} 1357 1358 1359/* 1360 * 1361 */ 1362 1363enum kcm_cred_type { KCM_NTLM_CRED, KCM_SCRAM_CRED }; 1364 1365struct kcm_ntlm_cred { 1366 enum kcm_cred_type type; 1367 kcmuuid_t uuid; 1368 char *user; 1369 char *domain; 1370#define nthash u.ntlm 1371 union { 1372 krb5_data ntlm; 1373 char *password; 1374 } u; 1375 uid_t uid; 1376 pid_t session; 1377 long refcount; 1378 heim_dict_t labels; 1379 struct kcm_ntlm_cred *next; 1380}; 1381 1382static struct kcm_ntlm_cred *ntlm_head; 1383static HEIMDAL_MUTEX cred_mutex = HEIMDAL_MUTEX_INITIALIZER; 1384 1385#define CHECK(s) do { if ((s)) { goto out; } } while(0) 1386 1387static krb5_error_code 1388kcm_unparse_digest_one(krb5_storage *inner, struct kcm_ntlm_cred *c) 1389{ 1390 __block krb5_error_code ret; 1391 1392 if (c->type == KCM_NTLM_CRED) 1393 CHECK(ret = krb5_store_stringz(inner, "ntlm-cache")); 1394 else if (c->type == KCM_SCRAM_CRED) 1395 CHECK(ret = krb5_store_stringz(inner, "scram-cache")); 1396 else 1397 heim_assert(false, "unknown cred type"); 1398 1399 CHECK(ret = krb5_store_uuid(inner, c->uuid)); 1400 CHECK(ret = krb5_store_stringz(inner, c->user)); 1401 if (c->domain) { 1402 CHECK(ret = krb5_store_uint8(inner, 1)); 1403 CHECK(ret = krb5_store_stringz(inner, c->domain)); 1404 } else { 1405 CHECK(ret = krb5_store_uint8(inner, 0)); 1406 } 1407 1408 if (c->type == KCM_NTLM_CRED) 1409 CHECK(ret = krb5_store_data(inner, c->u.ntlm)); 1410 else if (c->type == KCM_SCRAM_CRED) 1411 CHECK(ret = krb5_store_stringz(inner, c->u.password)); 1412 1413 CHECK(ret = krb5_store_int32(inner, c->uid)); 1414 CHECK(ret = krb5_store_int32(inner, c->session)); 1415 CHECK(ret = krb5_store_uint32(inner, (uint32_t)c->refcount)); 1416 1417 heim_dict_iterate(c->labels, ^(heim_object_t key, heim_object_t value) { 1418 heim_data_t d = value; 1419 krb5_data data; 1420 data.data = (void *)heim_data_get_bytes(d); 1421 data.length = heim_data_get_length(d); 1422 if (ret) return; 1423 ret = krb5_store_uint8(inner, 1); 1424 if (ret) return; 1425 char *k = heim_string_copy_utf8(key); 1426 ret = krb5_store_stringz(inner, k); 1427 free(k); 1428 if (ret) return; 1429 ret = krb5_store_data(inner, data); 1430 if (ret) return; 1431 }); 1432 CHECK(ret); 1433 CHECK(ret = krb5_store_uint8(inner, 0)); 1434 out: 1435 return ret; 1436} 1437 1438krb5_error_code 1439kcm_unparse_digest_all(krb5_context context, krb5_storage *sp) 1440{ 1441 struct kcm_ntlm_cred *c; 1442 krb5_error_code r = 0; 1443 1444 HEIMDAL_MUTEX_lock(&cred_mutex); 1445 1446 for (c = ntlm_head; r == 0 && c != NULL; c = c->next) { 1447 1448 r = kcm_unparse_wrap(sp, "digest-cache", c->session, ^(krb5_storage *inner) { 1449 return kcm_unparse_digest_one(inner, c); 1450 }); 1451 } 1452 if (r) 1453 kcm_log(10, "failed to write digest-cred: %d", r); 1454 1455 HEIMDAL_MUTEX_unlock(&cred_mutex); 1456 1457 return r; 1458} 1459 1460krb5_error_code 1461kcm_parse_digest_one(krb5_context context, krb5_storage *sp) 1462{ 1463 struct kcm_ntlm_cred *c; 1464 krb5_error_code ret; 1465 char *type = NULL; 1466 uint32_t u32; 1467 int32_t s32; 1468 uint8_t u8; 1469 1470 c = calloc(1, sizeof(*c)); 1471 1472 CHECK(ret = krb5_ret_stringz(sp, &type)); 1473 1474 if (strcmp(type, "ntlm-cache") == 0) { 1475 c->type = KCM_NTLM_CRED; 1476 } else if (strcmp(type, "scram-cache") == 0) { 1477 c->type = KCM_SCRAM_CRED; 1478 } else { 1479 free(type); 1480 return EINVAL; 1481 } 1482 1483 CHECK(ret = krb5_ret_uuid(sp, c->uuid)); 1484 CHECK(ret = krb5_ret_stringz(sp, &c->user)); 1485 CHECK(ret = krb5_ret_uint8(sp, &u8)); 1486 if (u8) { 1487 CHECK(ret = krb5_ret_stringz(sp, &c->domain)); 1488 } 1489 1490 if (c->type == KCM_NTLM_CRED) 1491 CHECK(ret = krb5_ret_data(sp, &c->u.ntlm)); 1492 else if (c->type == KCM_SCRAM_CRED) 1493 CHECK(ret = krb5_ret_stringz(sp, &c->u.password)); 1494 1495 CHECK(ret = krb5_ret_int32(sp, &s32)); 1496 c->uid = s32; 1497 CHECK(ret = krb5_ret_uint32(sp, &u32)); 1498 c->session = u32; 1499 CHECK(ret = krb5_ret_uint32(sp, &u32)); 1500 c->refcount = u32; 1501 1502 c->labels = heim_dict_create(0); 1503 1504 while (1) { 1505 krb5_data data; 1506 char *str; 1507 CHECK(ret = krb5_ret_uint8(sp, &u8)); 1508 if (u8 == 0) 1509 break; 1510 1511 CHECK(ret = krb5_ret_stringz(sp, &str)); 1512 heim_string_t s = heim_string_create(str); 1513 free(str); 1514 CHECK(ret = krb5_ret_data(sp, &data)); 1515 heim_data_t d = heim_data_create(data.data, data.length); 1516 krb5_data_free(&data); 1517 heim_dict_add_value(c->labels, s, d); 1518 heim_release(s); 1519 heim_release(d); 1520 } 1521 1522 c->next = ntlm_head; 1523 ntlm_head = c; 1524 1525 out: 1526 free(type); 1527 if (ret) { 1528 kcm_log(10, "failed to read %s: %d", type, ret); 1529 /* free_cred(c); */ 1530 } 1531 return ret; 1532} 1533 1534static void 1535free_cred(struct kcm_ntlm_cred *cred) 1536{ 1537 free(cred->user); 1538 free(cred->domain); 1539 1540 if (cred->type == KCM_NTLM_CRED) { 1541 krb5_data_free(&cred->nthash); 1542 } else if (cred->type == KCM_SCRAM_CRED) { 1543 free(cred->u.password); 1544 } else { 1545 abort(); 1546 } 1547 heim_release(cred->labels); 1548 free(cred); 1549} 1550 1551 1552static struct kcm_ntlm_cred * 1553find_ntlm_cred(enum kcm_cred_type type, const char *user, const char *domain, kcm_client *client) 1554{ 1555 struct kcm_ntlm_cred *c; 1556 1557 for (c = ntlm_head; c != NULL; c = c->next) 1558 if (c->type == type && (user[0] == '\0' || strcasecmp(user, c->user) == 0) && 1559 (domain == NULL || domain[0] == '\0' || strcasecmp(domain, c->domain) == 0) && 1560 kcm_is_same_session(client, c->uid, c->session)) 1561 return c; 1562 1563 return NULL; 1564} 1565 1566static struct kcm_ntlm_cred * 1567create_cred(enum kcm_cred_type type) 1568{ 1569 struct kcm_ntlm_cred *cred; 1570 1571 cred = calloc(1, sizeof(*cred)); 1572 if (cred == NULL) 1573 return NULL; 1574 1575 cred->type = type; 1576 cred->labels = heim_dict_create(0); 1577 cred->refcount = 1; 1578 1579 CCRandomCopyBytes(kCCRandomDefault, cred->uuid, sizeof(cred->uuid)); 1580 1581 return cred; 1582} 1583 1584/* 1585 * name 1586 * domain 1587 * ntlm hash 1588 * 1589 * Reply: 1590 * uuid 1591 */ 1592 1593static krb5_error_code 1594kcm_op_add_ntlm_cred(krb5_context context, 1595 kcm_client *client, 1596 kcm_operation opcode, 1597 krb5_storage *request, 1598 krb5_storage *response) 1599{ 1600 struct kcm_ntlm_cred *cred, *c; 1601 krb5_error_code ret; 1602 1603 cred = create_cred(KCM_NTLM_CRED); 1604 if (cred == NULL) 1605 return ENOMEM; 1606 1607 ret = krb5_ret_stringz(request, &cred->user); 1608 if (ret) 1609 goto error; 1610 1611 ret = krb5_ret_stringz(request, &cred->domain); 1612 if (ret) 1613 goto error; 1614 1615 ret = krb5_ret_data(request, &cred->nthash); 1616 if (ret) 1617 goto error; 1618 1619 HEIMDAL_MUTEX_lock(&cred_mutex); 1620 1621 /* search for dups */ 1622 c = find_ntlm_cred(KCM_NTLM_CRED, cred->user, cred->domain, client); 1623 if (c) { 1624 krb5_data hash = c->nthash; 1625 c->nthash = cred->nthash; 1626 cred->nthash = hash; 1627 free_cred(cred); 1628 cred = c; 1629 } else { 1630 cred->next = ntlm_head; 1631 ntlm_head = cred; 1632 } 1633 1634 cred->uid = client->uid; 1635 cred->session = client->session; 1636 1637 HEIMDAL_MUTEX_unlock(&cred_mutex); 1638 1639 /* write response */ 1640 (void)krb5_storage_write(response, &cred->uuid, sizeof(cred->uuid)); 1641 1642 kcm_data_changed = 1; 1643 1644 return 0; 1645 1646 error: 1647 free_cred(cred); 1648 1649 return ret; 1650} 1651 1652/* 1653 * { "HAVE_NTLM_CRED", NULL }, 1654 * 1655 * input: 1656 * name 1657 * domain 1658 */ 1659 1660static krb5_error_code 1661kcm_op_have_ntlm_cred(krb5_context context, 1662 kcm_client *client, 1663 kcm_operation opcode, 1664 krb5_storage *request, 1665 krb5_storage *response) 1666{ 1667 struct kcm_ntlm_cred *c; 1668 char *user = NULL, *domain = NULL; 1669 krb5_error_code ret; 1670 1671 ret = krb5_ret_stringz(request, &user); 1672 if (ret) 1673 goto error; 1674 1675 ret = krb5_ret_stringz(request, &domain); 1676 if (ret) 1677 goto error; 1678 1679 HEIMDAL_MUTEX_lock(&cred_mutex); 1680 1681 c = find_ntlm_cred(KCM_NTLM_CRED, user, domain, client); 1682 if (c == NULL) 1683 ret = ENOENT; 1684 1685 kcm_log(10, "ntlm checking for ntlm cred for %s@%s, -> %s", 1686 user, domain, (c == NULL ? "no" : "yes")); 1687 1688 if (c) 1689 (void)krb5_storage_write(response, &c->uuid, sizeof(c->uuid)); 1690 1691 HEIMDAL_MUTEX_unlock(&cred_mutex); 1692 1693 error: 1694 free(user); 1695 if (domain) 1696 free(domain); 1697 1698 return ret; 1699} 1700 1701/* 1702 * { "DEL_CRED", NULL }, 1703 * 1704 * input: 1705 * uuid 1706 */ 1707 1708static krb5_error_code 1709kcm_op_del_cred(krb5_context context, 1710 kcm_client *client, 1711 kcm_operation opcode, 1712 krb5_storage *request, 1713 krb5_storage *response) 1714{ 1715 struct kcm_ntlm_cred **cp, *c; 1716 kcmuuid_t uuid; 1717 ssize_t sret; 1718 1719 KCM_LOG_REQUEST(context, client, opcode); 1720 1721 sret = krb5_storage_read(request, &uuid, sizeof(uuid)); 1722 if (sret != sizeof(uuid)) { 1723 krb5_clear_error_message(context); 1724 return KRB5_CC_IO; 1725 } 1726 1727 HEIMDAL_MUTEX_lock(&cred_mutex); 1728 1729 for (cp = &ntlm_head; *cp != NULL; cp = &(*cp)->next) { 1730 if ((*cp)->type == KCM_NTLM_CRED && 1731 memcmp((*cp)->uuid, uuid, sizeof(uuid)) == 0 && 1732 kcm_is_same_session(client, (*cp)->uid, (*cp)->session)) 1733 { 1734 c = *cp; 1735 *cp = c->next; 1736 1737 free_cred(c); 1738 kcm_data_changed = 1; 1739 break; 1740 } 1741 } 1742 1743 HEIMDAL_MUTEX_unlock(&cred_mutex); 1744 1745 return 0; 1746} 1747 1748static struct ntlm_challenge { 1749 struct ntlm_challenge *next; 1750 uint8_t challenge[8]; 1751 time_t ts; 1752} *ntlm_challenges = NULL; 1753 1754static void 1755ntlm_delete_chain(struct ntlm_challenge *c) 1756{ 1757 while (c) { 1758 struct ntlm_challenge *next = c->next; 1759 free(c); 1760 c = next; 1761 } 1762} 1763 1764static int 1765ntlm_expiredp(struct ntlm_challenge *c, time_t now) 1766{ 1767 return c->ts > now - heim_ntlm_time_skew; 1768} 1769 1770static int 1771check_ntlm_challage(uint8_t chal[8]) 1772{ 1773 struct ntlm_challenge **q = &ntlm_challenges; 1774 time_t t = time(NULL); 1775 while (*q) { 1776 if (ntlm_expiredp(*q, t)) { 1777 struct ntlm_challenge *c = *q; 1778 *q = NULL; 1779 ntlm_delete_chain(c); 1780 return 0; 1781 } 1782 if (memcmp((*q)->challenge, chal, sizeof((*q)->challenge)) == 0) 1783 return EAUTH; 1784 1785 q = &(*q)->next; 1786 } 1787 return 0; 1788} 1789 1790/* 1791 * { "SET_NTLM_CHALLAGE", NULL } 1792 * 1793 * request: 1794 * challage 8 byte 1795 */ 1796 1797static krb5_error_code 1798kcm_op_add_ntlm_challenge(krb5_context context, 1799 kcm_client *client, 1800 kcm_operation opcode, 1801 krb5_storage *request, 1802 krb5_storage *response) 1803{ 1804 uint8_t chal[8]; 1805 struct ntlm_challenge *c; 1806 ssize_t sret; 1807 1808 KCM_LOG_REQUEST(context, client, opcode); 1809 1810 if (client->uid != 0) 1811 return EAUTH; 1812 1813 c = malloc(sizeof(*c)); 1814 if (c == NULL) 1815 return ENOMEM; 1816 1817 sret = krb5_storage_read(request, c->challenge, sizeof(c->challenge)); 1818 if (sret != sizeof(chal)) { 1819 free(c); 1820 return KRB5_CC_IO; 1821 } 1822 1823 c->ts = time(NULL); 1824 c->next = ntlm_challenges; 1825 ntlm_challenges = c; 1826 1827 kcm_data_changed = 1; 1828 1829 return 0; 1830} 1831 1832krb5_error_code 1833kcm_parse_ntlm_challenge_one(krb5_context context, krb5_storage *sp) 1834{ 1835 struct ntlm_challenge *c, **q; 1836 krb5_error_code ret; 1837 int32_t ts; 1838 ssize_t sret; 1839 1840 c = malloc(sizeof(*c)); 1841 if (c == NULL) 1842 return ENOMEM; 1843 1844 sret = krb5_storage_read(sp, c->challenge, sizeof(c->challenge)); 1845 if (sret != sizeof(c->challenge)) { 1846 free(c); 1847 return KRB5_CC_IO; 1848 } 1849 1850 ret = krb5_ret_int32(sp, &ts); 1851 if (ret) { 1852 free(c); 1853 return ret; 1854 } 1855 c->ts = ts; 1856 c->next = NULL; 1857 1858 if (ntlm_expiredp(c, time(NULL))) { 1859 free(c); 1860 } else { 1861 /* find end and add c, XXX performance */ 1862 for (q = &ntlm_challenges; *q != NULL; q = &(*q)->next) 1863 ; 1864 *q = c; 1865 } 1866 1867 return 0; 1868} 1869 1870krb5_error_code 1871kcm_unparse_challenge_all(krb5_context context, krb5_storage *sp) 1872{ 1873 struct ntlm_challenge *c; 1874 krb5_error_code r = 0; 1875 time_t now = time(NULL); 1876 1877 for (c = ntlm_challenges; r == 0 && c != NULL; c = c->next) { 1878 1879 if (ntlm_expiredp(c, now)) /* stop when they have expired */ 1880 break; 1881 1882 r = kcm_unparse_wrap(sp, "ntlm-chal", 0, ^(krb5_storage *inner) { 1883 ssize_t sret; 1884 sret = krb5_storage_write(inner, c->challenge, 1885 sizeof(c->challenge)); 1886 if (sret != sizeof(c->challenge)) 1887 return EINVAL; 1888 return krb5_store_int32(inner, (int32_t)c->ts); 1889 }); 1890 } 1891 if (r) 1892 kcm_log(10, "failed to write ntlm-chal: %d", r); 1893 return r; 1894} 1895 1896/* 1897 * 1898 */ 1899 1900static int 1901ntlm_domain_is_hostname(const char *name) 1902{ 1903 return (name[0] == '\\'); 1904} 1905 1906/* 1907 * { "DO_NTLM_AUTH", NULL }, 1908 * 1909 * input: 1910 * name:string 1911 * domain:string 1912 * type2:data 1913 * 1914 * reply: 1915 * type3:data 1916 * flags:int32 1917 * session-key:data 1918 */ 1919 1920static krb5_error_code 1921kcm_op_do_ntlm(krb5_context context, 1922 kcm_client *client, 1923 kcm_operation opcode, 1924 krb5_storage *request, 1925 krb5_storage *response) 1926{ 1927#ifdef ENABLE_NTLM 1928 struct kcm_ntlm_cred *c; 1929 struct ntlm_type2 type2; 1930 struct ntlm_type3 type3; 1931 char *user = NULL, *domain = NULL, *targetname = NULL; 1932 struct ntlm_buf ndata, sessionkey, tidata; 1933 krb5_data type2data, cb, type1data, tempdata; 1934 krb5_error_code ret; 1935 uint32_t type1flags, flags = 0; 1936 const char *type = "unknown"; 1937 char flagname[256]; 1938 size_t mic_offset = 0; 1939 1940 KCM_LOG_REQUEST(context, client, opcode); 1941 1942 krb5_data_zero(&cb); 1943 krb5_data_zero(&type1data); 1944 krb5_data_zero(&type2data); 1945 memset(&tidata, 0, sizeof(tidata)); 1946 memset(&type2, 0, sizeof(type2)); 1947 memset(&type3, 0, sizeof(type3)); 1948 sessionkey.data = NULL; 1949 sessionkey.length = 0; 1950 1951 HEIMDAL_MUTEX_lock(&cred_mutex); 1952 1953 ret = krb5_ret_stringz(request, &user); 1954 if (ret) 1955 goto error; 1956 1957 ret = krb5_ret_stringz(request, &domain); 1958 if (ret) 1959 goto error; 1960 1961 kcm_log(10, "NTLM AUTH with cred %s\\%s", domain, user); 1962 1963 c = find_ntlm_cred(KCM_NTLM_CRED, user, domain, client); 1964 if (c == NULL) { 1965 ret = EINVAL; 1966 goto error; 1967 } 1968 1969 ret = krb5_ret_data(request, &type2data); 1970 if (ret) 1971 goto error; 1972 1973 ret = krb5_ret_data(request, &cb); 1974 if (ret) 1975 goto error; 1976 1977 ret = krb5_ret_data(request, &type1data); 1978 if (ret) 1979 goto error; 1980 1981 ret = krb5_ret_stringz(request, &targetname); 1982 if (ret) 1983 goto error; 1984 1985 ret = krb5_ret_uint32(request, &type1flags); 1986 if (ret) 1987 goto error; 1988 1989 ndata.data = type2data.data; 1990 ndata.length = type2data.length; 1991 1992 ret = heim_ntlm_decode_type2(&ndata, &type2); 1993 if (ret) 1994 goto error; 1995 1996 kcm_log(10, "checking for ntlm mirror attack"); 1997 ret = check_ntlm_challage(type2.challenge); 1998 if (ret) { 1999 kcm_log(0, "ntlm mirror attack detected"); 2000 goto error; 2001 } 2002 2003 /* if service name or case matching with domain, let pick the domain */ 2004 if (ntlm_domain_is_hostname(c->domain) || strcasecmp(domain, type2.targetname) == 0) { 2005 free(domain); 2006 domain = type2.targetname; 2007 if (domain == NULL) { 2008 ret = ENOMEM; 2009 goto error; 2010 } 2011 } else { 2012 free(domain); 2013 domain = c->domain; 2014 } 2015 2016 type3.username = c->user; 2017 type3.flags = type2.flags; 2018 /* only allow what we negotiated ourself */ 2019 type3.flags &= type1flags; 2020 type3.targetname = domain; 2021 type3.ws = rk_UNCONST("workstation"); 2022 2023 /* 2024 * Only do NTLM Version 1 if they force us 2025 */ 2026 2027 if (gss_mo_get(GSS_NTLM_MECHANISM, GSS_C_NTLM_FORCE_V1, NULL)) { 2028 2029 type = "v1"; 2030 2031 if (type2.flags & NTLM_NEG_NTLM2_SESSION) { 2032 unsigned char nonce[8]; 2033 2034 if (CCRandomCopyBytes(kCCRandomDefault, nonce, sizeof(nonce))) { 2035 ret = EINVAL; 2036 goto error; 2037 } 2038 2039 ret = heim_ntlm_calculate_ntlm2_sess(nonce, 2040 type2.challenge, 2041 c->nthash.data, 2042 &type3.lm, 2043 &type3.ntlm); 2044 } else { 2045 ret = heim_ntlm_calculate_ntlm1(c->nthash.data, 2046 c->nthash.length, 2047 type2.challenge, 2048 &type3.ntlm); 2049 2050 } 2051 if (ret) 2052 goto error; 2053 2054 if (type3.flags & NTLM_NEG_KEYEX) { 2055 ret = heim_ntlm_build_ntlm1_master(c->nthash.data, 2056 c->nthash.length, 2057 &sessionkey, 2058 &type3.sessionkey); 2059 } else { 2060 ret = heim_ntlm_v1_base_session(c->nthash.data, 2061 c->nthash.length, 2062 &sessionkey); 2063 } 2064 if (ret) 2065 goto error; 2066 2067 } else { 2068 unsigned char ntlmv2[16]; 2069 struct ntlm_targetinfo ti; 2070 static uint8_t zeros[16] = { 0 }; 2071 2072 type = "v2"; 2073 2074 /* verify infotarget */ 2075 2076 ret = heim_ntlm_decode_targetinfo(&type2.targetinfo, 1, &ti); 2077 if (ret) 2078 goto error; 2079 2080 if (ti.avflags & NTLM_TI_AV_FLAG_GUEST) 2081 flags |= KCM_NTLM_FLAG_AV_GUEST; 2082 2083 if (ti.channel_bindings.data) 2084 free(ti.channel_bindings.data); 2085 if (ti.targetname) 2086 free(ti.targetname); 2087 2088 /* 2089 * We are going to use MIC, tell server so it can reject the 2090 * authenticate if the mic is missing. 2091 */ 2092 ti.avflags |= NTLM_TI_AV_FLAG_MIC; 2093 ti.targetname = targetname; 2094 2095 if (cb.length == 0) { 2096 ti.channel_bindings.data = zeros; 2097 ti.channel_bindings.length = sizeof(zeros); 2098 } else { 2099 kcm_log(10, "using channelbindings of size %lu", (unsigned long)cb.length); 2100 ti.channel_bindings.data = cb.data; 2101 ti.channel_bindings.length = cb.length; 2102 } 2103 2104 ret = heim_ntlm_encode_targetinfo(&ti, TRUE, &tidata); 2105 2106 ti.targetname = NULL; 2107 ti.channel_bindings.data = NULL; 2108 ti.channel_bindings.length = 0; 2109 2110 heim_ntlm_free_targetinfo(&ti); 2111 if (ret) 2112 goto error; 2113 2114 /* 2115 * Prefer NTLM_NEG_EXTENDED_SESSION over NTLM_NEG_LM_KEY as 2116 * decribed in 2.2.2.5. 2117 */ 2118 2119 if (type3.flags & NTLM_NEG_NTLM2_SESSION) 2120 type3.flags &= ~NTLM_NEG_LM_KEY; 2121 2122 if ((type3.flags & NTLM_NEG_LM_KEY) && 2123 gss_mo_get(GSS_NTLM_MECHANISM, GSS_C_NTLM_SUPPORT_LM2, NULL)) { 2124 ret = heim_ntlm_calculate_lm2(c->nthash.data, 2125 c->nthash.length, 2126 type3.username, 2127 domain, 2128 type2.challenge, 2129 ntlmv2, 2130 &type3.lm); 2131 } else { 2132 type3.lm.data = malloc(24); 2133 if (type3.lm.data == NULL) { 2134 ret = ENOMEM; 2135 } else { 2136 type3.lm.length = 24; 2137 memset(type3.lm.data, 0, type3.lm.length); 2138 } 2139 } 2140 if (ret) 2141 goto error; 2142 2143 ret = heim_ntlm_calculate_ntlm2(c->nthash.data, 2144 c->nthash.length, 2145 type3.username, 2146 domain, 2147 type2.challenge, 2148 &tidata, 2149 ntlmv2, 2150 &type3.ntlm); 2151 if (ret) 2152 goto error; 2153 2154 if (type3.flags & NTLM_NEG_KEYEX) { 2155 ret = heim_ntlm_build_ntlm2_master(ntlmv2, sizeof(ntlmv2), 2156 &type3.ntlm, 2157 &sessionkey, 2158 &type3.sessionkey); 2159 } else { 2160 ret = heim_ntlm_v2_base_session(ntlmv2, sizeof(ntlmv2), &type3.ntlm, &sessionkey); 2161 } 2162 2163 memset(ntlmv2, 0, sizeof(ntlmv2)); 2164 if (ret) 2165 goto error; 2166 } 2167 2168 ret = heim_ntlm_encode_type3(&type3, &ndata, &mic_offset); 2169 if (ret) 2170 goto error; 2171 if (ndata.length < CC_MD5_DIGEST_LENGTH) { 2172 ret = EINVAL; 2173 goto error; 2174 } 2175 2176 if (mic_offset && mic_offset < ndata.length - CC_MD5_DIGEST_LENGTH) { 2177 CCHmacContext mic; 2178 uint8_t *p = (uint8_t *)ndata.data + mic_offset; 2179 CCHmacInit(&mic, kCCHmacAlgMD5, sessionkey.data, sessionkey.length); 2180 CCHmacUpdate(&mic, type1data.data, type1data.length); 2181 CCHmacUpdate(&mic, type2data.data, type2data.length); 2182 CCHmacUpdate(&mic, ndata.data, ndata.length); 2183 CCHmacFinal(&mic, p); 2184 } 2185 2186 tempdata.data = ndata.data; 2187 tempdata.length = ndata.length; 2188 ret = krb5_store_data(response, tempdata); 2189 heim_ntlm_free_buf(&ndata); 2190 2191 if (ret) goto error; 2192 2193 ret = krb5_store_int32(response, flags); 2194 if (ret) goto error; 2195 2196 tempdata.data = sessionkey.data; 2197 tempdata.length = sessionkey.length; 2198 2199 ret = krb5_store_data(response, tempdata); 2200 if (ret) goto error; 2201 ret = krb5_store_string(response, c->user); 2202 if (ret) goto error; 2203 ret = krb5_store_string(response, domain); 2204 if (ret) goto error; 2205 ret = krb5_store_uint32(response, type3.flags); 2206 if (ret) goto error; 2207 2208 heim_ntlm_unparse_flags(type3.flags, flagname, sizeof(flagname)); 2209 2210 kcm_log(0, "ntlm %s request processed for %s\\%s flags: %s", 2211 type, domain, c->user, flagname); 2212 2213 error: 2214 HEIMDAL_MUTEX_unlock(&cred_mutex); 2215 2216 krb5_data_free(&cb); 2217 krb5_data_free(&type1data); 2218 krb5_data_free(&type2data); 2219 if (type3.lm.data) 2220 free(type3.lm.data); 2221 if (type3.ntlm.data) 2222 free(type3.ntlm.data); 2223 if (type3.sessionkey.data) 2224 free(type3.sessionkey.data); 2225 if (targetname) 2226 free(targetname); 2227 heim_ntlm_free_type2(&type2); 2228 heim_ntlm_free_buf(&sessionkey); 2229 heim_ntlm_free_buf(&tidata); 2230 free(user); 2231 2232 return ret; 2233#else 2234 return EINVAL; 2235#endif 2236} 2237 2238 2239/* 2240 * { "GET_NTLM_UUID_LIST", NULL } 2241 * 2242 * reply: 2243 * 1 user domain uuid 2244 * 0 [ end of list ] 2245 */ 2246 2247static krb5_error_code 2248kcm_op_get_ntlm_user_list(krb5_context context, 2249 kcm_client *client, 2250 kcm_operation opcode, 2251 krb5_storage *request, 2252 krb5_storage *response) 2253{ 2254 struct kcm_ntlm_cred *c; 2255 krb5_error_code ret; 2256 ssize_t sret; 2257 2258 KCM_LOG_REQUEST(context, client, opcode); 2259 2260 HEIMDAL_MUTEX_lock(&cred_mutex); 2261 2262 for (c = ntlm_head; c != NULL; c = c->next) { 2263 if (c->type != KCM_NTLM_CRED || !kcm_is_same_session(client, c->uid, c->session)) 2264 continue; 2265 2266 ret = krb5_store_uint32(response, 1); 2267 if (ret) 2268 goto out; 2269 ret = krb5_store_stringz(response, c->user); 2270 if (ret) 2271 goto out; 2272 ret = krb5_store_stringz(response, c->domain); 2273 if (ret) 2274 goto out; 2275 sret = krb5_storage_write(response, c->uuid, sizeof(c->uuid)); 2276 if (sret != sizeof(c->uuid)) { 2277 ret = ENOMEM; 2278 goto out; 2279 } 2280 } 2281 ret = krb5_store_uint32(response, 0); 2282 out: 2283 HEIMDAL_MUTEX_unlock(&cred_mutex); 2284 return ret; 2285} 2286 2287static krb5_error_code 2288kcm_op_add_scram_cred(krb5_context context, 2289 kcm_client *client, 2290 kcm_operation opcode, 2291 krb5_storage *request, 2292 krb5_storage *response) 2293{ 2294 struct kcm_ntlm_cred *cred, *c; 2295 krb5_error_code ret; 2296 2297 KCM_LOG_REQUEST(context, client, opcode); 2298 2299 cred = create_cred(KCM_SCRAM_CRED); 2300 if (cred == NULL) 2301 return ENOMEM; 2302 2303 ret = krb5_ret_stringz(request, &cred->user); 2304 if (ret) 2305 goto error; 2306 2307 ret = krb5_ret_stringz(request, &cred->u.password); 2308 if (ret) 2309 goto error; 2310 2311 HEIMDAL_MUTEX_lock(&cred_mutex); 2312 2313 /* search for dups */ 2314 c = find_ntlm_cred(KCM_SCRAM_CRED, cred->user, NULL, client); 2315 if (c) { 2316 char *pw = c->u.password; 2317 c->u.password = cred->u.password; 2318 cred->u.password = pw; 2319 free_cred(cred); 2320 cred = c; 2321 } else { 2322 cred->next = ntlm_head; 2323 ntlm_head = cred; 2324 } 2325 2326 cred->uid = client->uid; 2327 cred->session = client->session; 2328 2329 /* write response */ 2330 (void)krb5_storage_write(response, cred->uuid, sizeof(cred->uuid)); 2331 2332 2333 HEIMDAL_MUTEX_unlock(&cred_mutex); 2334 kcm_data_changed = 1; 2335 2336 return 0; 2337 2338 error: 2339 free_cred(cred); 2340 2341 return ret; 2342} 2343 2344static krb5_error_code 2345kcm_op_have_scram_cred(krb5_context context, 2346 kcm_client *client, 2347 kcm_operation opcode, 2348 krb5_storage *request, 2349 krb5_storage *response) 2350{ 2351 struct kcm_ntlm_cred *c; 2352 char *user = NULL; 2353 krb5_error_code ret; 2354 2355 KCM_LOG_REQUEST(context, client, opcode); 2356 2357 ret = krb5_ret_stringz(request, &user); 2358 if (ret) 2359 return ret; 2360 2361 HEIMDAL_MUTEX_lock(&cred_mutex); 2362 2363 c = find_ntlm_cred(KCM_SCRAM_CRED, user, NULL, client); 2364 if (c == NULL) 2365 ret = ENOENT; 2366 2367 if (c) 2368 (void)krb5_storage_write(response, c->uuid, sizeof(c->uuid)); 2369 2370 HEIMDAL_MUTEX_unlock(&cred_mutex); 2371 2372 free(user); 2373 2374 return ret; 2375} 2376 2377static krb5_error_code 2378kcm_op_del_scram_cred(krb5_context context, 2379 kcm_client *client, 2380 kcm_operation opcode, 2381 krb5_storage *request, 2382 krb5_storage *response) 2383{ 2384 struct kcm_ntlm_cred **cp, *c; 2385 char *user = NULL; 2386 krb5_error_code ret; 2387 2388 KCM_LOG_REQUEST(context, client, opcode); 2389 2390 ret = krb5_ret_stringz(request, &user); 2391 if (ret) 2392 return ret; 2393 2394 HEIMDAL_MUTEX_lock(&cred_mutex); 2395 2396 for (cp = &ntlm_head; *cp != NULL; cp = &(*cp)->next) { 2397 if ((*cp)->type == KCM_SCRAM_CRED && strcasecmp(user, (*cp)->user) == 0 && 2398 kcm_is_same_session(client, (*cp)->uid, (*cp)->session)) 2399 { 2400 c = *cp; 2401 *cp = c->next; 2402 2403 free_cred(c); 2404 kcm_data_changed = 1; 2405 break; 2406 } 2407 } 2408 2409 HEIMDAL_MUTEX_unlock(&cred_mutex); 2410 2411 free(user); 2412 2413 return ret; 2414} 2415 2416/* 2417 * IN: 2418 * clientname: stringz 2419 * iterations: uint32_t 2420 * salt: krb5_data 2421 * c1: krb5_data 2422 * s1: krb5_data 2423 * c2noproof: krb5_data 2424 */ 2425 2426static krb5_error_code 2427kcm_op_do_scram(krb5_context context, 2428 kcm_client *client, 2429 kcm_operation opcode, 2430 krb5_storage *request, 2431 krb5_storage *response) 2432{ 2433#ifdef ENABLE_SCRAM 2434 heim_scram_data proof, server, client_key, stored, server_key, session_key; 2435 heim_scram_method method = HEIM_SCRAM_DIGEST_SHA1; 2436 krb5_data salt, c1, s1, c2noproof; 2437 struct kcm_ntlm_cred *c; 2438 krb5_error_code ret; 2439 uint32_t iterations; 2440 unsigned char *p, *q; 2441 char *user = NULL; 2442 size_t n; 2443 2444 KCM_LOG_REQUEST(context, client, opcode); 2445 2446 memset(&proof, 0, sizeof(proof)); 2447 memset(&server, 0, sizeof(server)); 2448 memset(&client_key, 0, sizeof(client_key)); 2449 memset(&stored, 0, sizeof(stored)); 2450 memset(&server_key, 0, sizeof(server_key)); 2451 memset(&session_key, 0, sizeof(session_key)); 2452 krb5_data_zero(&salt); 2453 krb5_data_zero(&c1); 2454 krb5_data_zero(&s1); 2455 krb5_data_zero(&c2noproof); 2456 2457 HEIMDAL_MUTEX_lock(&cred_mutex); 2458 2459 ret = krb5_ret_stringz(request, &user); 2460 if (ret) 2461 goto out; 2462 2463 c = find_ntlm_cred(KCM_SCRAM_CRED, user, NULL, client); 2464 if (c == NULL) { 2465 ret = ENOENT; 2466 goto out; 2467 } 2468 2469 ret = krb5_ret_uint32(request, &iterations); 2470 if (ret) 2471 goto out; 2472 2473 ret = krb5_ret_data(request, &salt); 2474 if (ret) 2475 goto out; 2476 2477 ret = krb5_ret_data(request, &c1); 2478 if (ret) 2479 goto out; 2480 2481 ret = krb5_ret_data(request, &s1); 2482 if (ret) 2483 goto out; 2484 2485 ret = krb5_ret_data(request, &c2noproof); 2486 if (ret) 2487 goto out; 2488 2489 ret = heim_scram_stored_key(method, c->u.password, iterations, &salt, 2490 &client_key, &stored, &server_key); 2491 if (ret) 2492 goto out; 2493 2494 ret = heim_scram_generate(method, &stored, &server_key, 2495 &c1, &s1, &c2noproof, &proof, &server); 2496 if (ret) 2497 goto out; 2498 2499 2500 ret = heim_scram_session_key(method, &stored, &client_key, 2501 &c1, &s1, &c2noproof, 2502 &session_key); 2503 if (ret) 2504 goto out; 2505 2506 /* 2507 * Now client_key XOR proof 2508 */ 2509 p = proof.data; 2510 q = client_key.data; 2511 2512 for (n = 0 ; n < client_key.length; n++) 2513 p[n] = p[n] ^ q[n]; 2514 2515 ret = krb5_store_data(response, proof); 2516 if (ret) 2517 goto out; 2518 ret = krb5_store_data(response, server); 2519 if (ret) 2520 goto out; 2521 ret = krb5_store_data(response, session_key); 2522 if (ret) 2523 goto out; 2524 2525out: 2526 HEIMDAL_MUTEX_unlock(&cred_mutex); 2527 if (user) 2528 free(user); 2529 2530 krb5_data_free(&salt); 2531 krb5_data_free(&c1); 2532 krb5_data_free(&s1); 2533 krb5_data_free(&c2noproof); 2534 2535 heim_scram_data_free(&proof); 2536 heim_scram_data_free(&server); 2537 heim_scram_data_free(&client_key); 2538 heim_scram_data_free(&stored); 2539 heim_scram_data_free(&server_key); 2540 heim_scram_data_free(&session_key); 2541 2542 return ret; 2543#else 2544 return EINVAL; 2545#endif 2546} 2547 2548static krb5_error_code 2549kcm_op_get_scram_user_list(krb5_context context, 2550 kcm_client *client, 2551 kcm_operation opcode, 2552 krb5_storage *request, 2553 krb5_storage *response) 2554{ 2555 struct kcm_ntlm_cred *c; 2556 krb5_error_code ret; 2557 ssize_t sret; 2558 2559 KCM_LOG_REQUEST(context, client, opcode); 2560 2561 for (c = ntlm_head; c != NULL; c = c->next) { 2562 if (c->type != KCM_SCRAM_CRED || !kcm_is_same_session(client, c->uid, c->session)) 2563 continue; 2564 2565 ret = krb5_store_uint32(response, 1); 2566 if (ret) 2567 return ret; 2568 ret = krb5_store_stringz(response, c->user); 2569 if (ret) 2570 return ret; 2571 2572 sret = krb5_storage_write(response, c->uuid, sizeof(c->uuid)); 2573 if (sret != sizeof(c->uuid)) { 2574 ret = ENOMEM; 2575 return ret; 2576 } 2577 } 2578 return krb5_store_uint32(response, 0); 2579} 2580 2581static krb5_error_code 2582kcm_op_retain_cred(krb5_context context, 2583 kcm_client *client, 2584 kcm_operation opcode, 2585 krb5_storage *request, 2586 krb5_storage *response) 2587{ 2588 struct kcm_ntlm_cred *c; 2589 kcmuuid_t uuid; 2590 ssize_t sret; 2591 2592 KCM_LOG_REQUEST(context, client, opcode); 2593 2594 sret = krb5_storage_read(request, &uuid, sizeof(uuid)); 2595 if (sret != sizeof(uuid)) { 2596 krb5_clear_error_message(context); 2597 return KRB5_CC_IO; 2598 } 2599 2600 for (c = ntlm_head; c != NULL; c = c->next) { 2601 if (!kcm_is_same_session(client, c->uid, c->session)) 2602 continue; 2603 2604 if (memcmp(uuid, c->uuid, sizeof(c->uuid)) == 0) { 2605 c->refcount++; 2606 kcm_data_changed = 1; 2607 return 0; 2608 } 2609 } 2610 2611 return 0; 2612} 2613 2614static krb5_error_code 2615kcm_op_release_cred(krb5_context context, 2616 kcm_client *client, 2617 kcm_operation opcode, 2618 krb5_storage *request, 2619 krb5_storage *response) 2620{ 2621 struct kcm_ntlm_cred **cp; 2622 kcmuuid_t uuid; 2623 ssize_t sret; 2624 2625 KCM_LOG_REQUEST(context, client, opcode); 2626 2627 sret = krb5_storage_read(request, &uuid, sizeof(uuid)); 2628 if (sret != sizeof(uuid)) { 2629 krb5_clear_error_message(context); 2630 return KRB5_CC_IO; 2631 } 2632 2633 for (cp = &ntlm_head; *cp != NULL; cp = &(*cp)->next) { 2634 struct kcm_ntlm_cred *c = *cp; 2635 2636 if (!kcm_is_same_session(client, c->uid, c->session)) 2637 continue; 2638 2639 if (memcmp(uuid, c->uuid, sizeof(uuid)) == 0) { 2640 c->refcount--; 2641 if (c->refcount < 1) { 2642 *cp = c->next; 2643 free_cred(c); 2644 } 2645 kcm_data_changed = 1; 2646 return 0; 2647 } 2648 } 2649 return 0; 2650} 2651 2652static krb5_error_code 2653kcm_op_cred_label_get(krb5_context context, 2654 kcm_client *client, 2655 kcm_operation opcode, 2656 krb5_storage *request, 2657 krb5_storage *response) 2658{ 2659 struct kcm_ntlm_cred *c; 2660 krb5_error_code ret; 2661 heim_string_t s; 2662 char *label; 2663 kcmuuid_t uuid; 2664 ssize_t sret; 2665 2666 KCM_LOG_REQUEST(context, client, opcode); 2667 2668 sret = krb5_storage_read(request, &uuid, sizeof(uuid)); 2669 if (sret != sizeof(uuid)) { 2670 krb5_clear_error_message(context); 2671 return KRB5_CC_IO; 2672 } 2673 2674 ret = krb5_ret_stringz(request, &label); 2675 if (ret) 2676 return ret; 2677 2678 s = heim_string_create(label); 2679 free(label); 2680 2681 for (c = ntlm_head; c != NULL; c = c->next) { 2682 if (!kcm_is_same_session(client, c->uid, c->session)) 2683 continue; 2684 2685 if (memcmp(uuid, c->uuid, sizeof(c->uuid)) == 0) { 2686 heim_data_t d; 2687 2688 d = heim_dict_copy_value(c->labels, s); 2689 if (d) { 2690 krb5_data data; 2691 data.length = heim_data_get_length(d); 2692 data.data = (void *)heim_data_get_bytes(d); 2693 2694 krb5_store_data(response, data); 2695 heim_release(d); 2696 break; 2697 } 2698 } 2699 } 2700 heim_release(s); 2701 2702 if (c == NULL) 2703 return ENOENT; 2704 2705 return 0; 2706} 2707 2708static krb5_error_code 2709kcm_op_cred_label_set(krb5_context context, 2710 kcm_client *client, 2711 kcm_operation opcode, 2712 krb5_storage *request, 2713 krb5_storage *response) 2714{ 2715 struct kcm_ntlm_cred *c; 2716 kcmuuid_t uuid; 2717 krb5_data data; 2718 char *label = NULL; 2719 ssize_t sret; 2720 2721 KCM_LOG_REQUEST(context, client, opcode); 2722 2723 sret = krb5_storage_read(request, &uuid, sizeof(uuid)); 2724 if (sret != sizeof(uuid)) { 2725 krb5_clear_error_message(context); 2726 return KRB5_CC_IO; 2727 } 2728 2729 krb5_ret_stringz(request, &label); 2730 krb5_ret_data(request, &data); 2731 2732 HEIMDAL_MUTEX_lock(&cred_mutex); 2733 2734 for (c = ntlm_head; c != NULL; c = c->next) { 2735 2736 if (!kcm_is_same_session(client, c->uid, c->session)) 2737 continue; 2738 2739 if (memcmp(uuid, c->uuid, sizeof(uuid)) == 0) { 2740 heim_string_t s; 2741 2742 s = heim_string_create(label); 2743 2744 if (data.length) { 2745 heim_data_t d; 2746 2747 d = heim_data_create(data.data, data.length); 2748 2749 heim_dict_add_value(c->labels, s, d); 2750 heim_release(d); 2751 } else { 2752 heim_dict_delete_key(c->labels, s); 2753 } 2754 kcm_data_changed = 1; 2755 heim_release(s); 2756 break; 2757 } 2758 } 2759 2760 HEIMDAL_MUTEX_unlock(&cred_mutex); 2761 2762 krb5_data_free(&data); 2763 free(label); 2764 2765 if (c == NULL) 2766 return ENOENT; 2767 2768 return 0; 2769} 2770 2771 2772 2773/* 2774 * 2775 */ 2776 2777static struct kcm_op kcm_ops[] = { 2778 { "NOOP", kcm_op_noop }, 2779 { "GET_NAME", kcm_op_get_name }, 2780 { "RESOLVE", kcm_op_noop }, 2781 { "GEN_NEW", kcm_op_gen_new }, 2782 { "INITIALIZE", kcm_op_initialize }, 2783 { "DESTROY", kcm_op_destroy }, 2784 { "STORE", kcm_op_store }, 2785 { "RETRIEVE", kcm_op_retrieve }, 2786 { "GET_PRINCIPAL", kcm_op_get_principal }, 2787 { "GET_CRED_UUID_LIST", kcm_op_get_cred_uuid_list }, 2788 { "GET_CRED_BY_UUID", kcm_op_get_cred_by_uuid }, 2789 { "REMOVE_CRED", kcm_op_remove_cred }, 2790 { "SET_FLAGS", kcm_op_set_flags }, 2791 { "CHOWN", kcm_op_chown }, 2792 { "CHMOD", kcm_op_chmod }, 2793 { "GET_INITIAL_TICKET", kcm_op_get_initial_ticket }, 2794 { "GET_TICKET", kcm_op_get_ticket }, 2795 { "MOVE_CACHE", kcm_op_move_cache }, 2796 { "GET_CACHE_UUID_LIST", kcm_op_get_cache_uuid_list }, 2797 { "GET_CACHE_BY_UUID", kcm_op_get_cache_by_uuid }, 2798 { "GET_DEFAULT_CACHE", kcm_op_get_default_cache }, 2799 { "SET_DEFAULT_CACHE", kcm_op_set_default_cache }, 2800 { "GET_KDC_OFFSET", kcm_op_get_kdc_offset }, 2801 { "SET_KDC_OFFSET", kcm_op_set_kdc_offset }, 2802 { "RETAIN_KCRED", kcm_op_retain_kcred }, 2803 { "RELEASE_KCRED", kcm_op_release_kcred }, 2804 { "GET_UUID", kcm_op_get_uuid }, 2805 { "ADD_NTLM_CRED", kcm_op_add_ntlm_cred }, 2806 { "HAVE_NTLM_CRED", kcm_op_have_ntlm_cred }, 2807 { "SET_NTLM_CHALLEGE", kcm_op_add_ntlm_challenge }, 2808 { "DO_NTLM_AUTH", kcm_op_do_ntlm }, 2809 { "SET_NTLM_USER_LIST", kcm_op_get_ntlm_user_list }, 2810 { "ADD_SCRAM_CRED", kcm_op_add_scram_cred }, 2811 { "HAVE_SCRAM_CRED", kcm_op_have_scram_cred }, 2812 { "DEL_SCRAM_CRED", kcm_op_del_scram_cred }, 2813 { "DO_SCRAM_AUTH", kcm_op_do_scram }, 2814 { "GET_SCRAM_USER_LIST", kcm_op_get_scram_user_list }, 2815 { "DEL_CRED", kcm_op_del_cred }, 2816 { "RETAIN_CRED", kcm_op_retain_cred }, 2817 { "RELEASE_CRED", kcm_op_release_cred }, 2818 { "CRED_LABEL_GET", kcm_op_cred_label_get }, 2819 { "CRED_LABEL_SET", kcm_op_cred_label_set } 2820}; 2821 2822 2823const char * 2824kcm_op2string(kcm_operation opcode) 2825{ 2826 if (opcode >= sizeof(kcm_ops)/sizeof(kcm_ops[0])) 2827 return "Unknown operation"; 2828 2829 return kcm_ops[opcode].name; 2830} 2831 2832krb5_error_code 2833kcm_dispatch(krb5_context context, 2834 kcm_client *client, 2835 krb5_data *req_data, 2836 krb5_data *resp_data) 2837{ 2838 krb5_error_code ret; 2839 kcm_method method; 2840 krb5_storage *req_sp = NULL; 2841 krb5_storage *resp_sp = NULL; 2842 uint16_t opcode; 2843 2844 resp_sp = krb5_storage_emem(); 2845 if (resp_sp == NULL) { 2846 return ENOMEM; 2847 } 2848 2849 if (client->pid == -1) { 2850 kcm_log(0, "Client had invalid process number"); 2851 ret = KRB5_FCC_INTERNAL; 2852 goto out; 2853 } 2854 2855 req_sp = krb5_storage_from_data(req_data); 2856 if (req_sp == NULL) { 2857 kcm_log(0, "Process %d: failed to initialize storage from data", 2858 client->pid); 2859 ret = KRB5_CC_IO; 2860 goto out; 2861 } 2862 2863 ret = krb5_ret_uint16(req_sp, &opcode); 2864 if (ret) { 2865 kcm_log(0, "Process %d: didn't send a message", client->pid); 2866 goto out; 2867 } 2868 2869 if (opcode >= sizeof(kcm_ops)/sizeof(kcm_ops[0])) { 2870 kcm_log(0, "Process %d: invalid operation code %d", 2871 client->pid, opcode); 2872 ret = KRB5_FCC_INTERNAL; 2873 goto out; 2874 } 2875 method = kcm_ops[opcode].method; 2876 if (method == NULL) { 2877 kcm_log(0, "Process %d: operation code %s not implemented", 2878 client->pid, kcm_op2string(opcode)); 2879 ret = KRB5_FCC_INTERNAL; 2880 goto out; 2881 } 2882 2883 /* seek past place for status code */ 2884 krb5_storage_seek(resp_sp, 4, SEEK_SET); 2885 2886 ret = (*method)(context, client, opcode, req_sp, resp_sp); 2887 2888out: 2889 if (req_sp != NULL) { 2890 krb5_storage_free(req_sp); 2891 } 2892 2893 krb5_storage_seek(resp_sp, 0, SEEK_SET); 2894 krb5_store_int32(resp_sp, ret); 2895 2896 ret = krb5_storage_to_data(resp_sp, resp_data); 2897 krb5_storage_free(resp_sp); 2898 2899 return ret; 2900} 2901 2902