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