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 38 static void 39 kcm_drop_default_cache(krb5_context context, kcm_client *client, char *name); 40 41 42 int 43 kcm_is_same_session(kcm_client *client, uid_t uid, pid_t session) 44 { 45 #if 0 /* XXX pppd is running in diffrent session the user */ 46 if (session != -1) 47 return (client->session == session); 48 else 49 #endif 50 return (client->uid == uid); 51 } 52 53 static krb5_error_code 54 kcm_op_noop(krb5_context context, 55 kcm_client *client, 56 kcm_operation opcode, 57 krb5_storage *request, 58 krb5_storage *response) 59 { 60 KCM_LOG_REQUEST(context, client, opcode); 61 62 return 0; 63 } 64 65 /* 66 * Request: 67 * NameZ 68 * Response: 69 * NameZ 70 * 71 */ 72 static krb5_error_code 73 kcm_op_get_name(krb5_context context, 74 kcm_client *client, 75 kcm_operation opcode, 76 krb5_storage *request, 77 krb5_storage *response) 78 79 { 80 krb5_error_code ret; 81 char *name = NULL; 82 kcm_ccache ccache; 83 84 ret = krb5_ret_stringz(request, &name); 85 if (ret) 86 return ret; 87 88 KCM_LOG_REQUEST_NAME(context, client, opcode, name); 89 90 ret = kcm_ccache_resolve_client(context, client, opcode, 91 name, &ccache); 92 if (ret) { 93 free(name); 94 return ret; 95 } 96 97 ret = krb5_store_stringz(response, ccache->name); 98 if (ret) { 99 kcm_release_ccache(context, ccache); 100 free(name); 101 return ret; 102 } 103 104 free(name); 105 kcm_release_ccache(context, ccache); 106 return 0; 107 } 108 109 /* 110 * Request: 111 * 112 * Response: 113 * NameZ 114 */ 115 static krb5_error_code 116 kcm_op_gen_new(krb5_context context, 117 kcm_client *client, 118 kcm_operation opcode, 119 krb5_storage *request, 120 krb5_storage *response) 121 { 122 krb5_error_code ret; 123 char *name; 124 125 KCM_LOG_REQUEST(context, client, opcode); 126 127 name = kcm_ccache_nextid(client->pid, client->uid, client->gid); 128 if (name == NULL) { 129 return KRB5_CC_NOMEM; 130 } 131 132 ret = krb5_store_stringz(response, name); 133 free(name); 134 135 return ret; 136 } 137 138 /* 139 * Request: 140 * NameZ 141 * Principal 142 * 143 * Response: 144 * 145 */ 146 static krb5_error_code 147 kcm_op_initialize(krb5_context context, 148 kcm_client *client, 149 kcm_operation opcode, 150 krb5_storage *request, 151 krb5_storage *response) 152 { 153 kcm_ccache ccache; 154 krb5_principal principal; 155 krb5_error_code ret; 156 char *name; 157 #if 0 158 kcm_event event; 159 #endif 160 161 KCM_LOG_REQUEST(context, client, opcode); 162 163 ret = krb5_ret_stringz(request, &name); 164 if (ret) 165 return ret; 166 167 ret = krb5_ret_principal(request, &principal); 168 if (ret) { 169 free(name); 170 return ret; 171 } 172 173 ret = kcm_ccache_new_client(context, client, name, &ccache); 174 if (ret) { 175 free(name); 176 krb5_free_principal(context, principal); 177 return ret; 178 } 179 180 ccache->client = principal; 181 182 free(name); 183 184 #if 0 185 /* 186 * Create a new credentials cache. To mitigate DoS attacks we will 187 * expire it in 30 minutes unless it has some credentials added 188 * to it 189 */ 190 191 event.fire_time = 30 * 60; 192 event.expire_time = 0; 193 event.backoff_time = 0; 194 event.action = KCM_EVENT_DESTROY_EMPTY_CACHE; 195 event.ccache = ccache; 196 197 ret = kcm_enqueue_event_relative(context, &event); 198 #endif 199 200 kcm_release_ccache(context, ccache); 201 202 return ret; 203 } 204 205 /* 206 * Request: 207 * NameZ 208 * 209 * Response: 210 * 211 */ 212 static krb5_error_code 213 kcm_op_destroy(krb5_context context, 214 kcm_client *client, 215 kcm_operation opcode, 216 krb5_storage *request, 217 krb5_storage *response) 218 { 219 krb5_error_code ret; 220 char *name; 221 222 ret = krb5_ret_stringz(request, &name); 223 if (ret) 224 return ret; 225 226 KCM_LOG_REQUEST_NAME(context, client, opcode, name); 227 228 ret = kcm_ccache_destroy_client(context, client, name); 229 if (ret == 0) 230 kcm_drop_default_cache(context, client, name); 231 232 free(name); 233 234 return ret; 235 } 236 237 /* 238 * Request: 239 * NameZ 240 * Creds 241 * 242 * Response: 243 * 244 */ 245 static krb5_error_code 246 kcm_op_store(krb5_context context, 247 kcm_client *client, 248 kcm_operation opcode, 249 krb5_storage *request, 250 krb5_storage *response) 251 { 252 krb5_creds creds; 253 krb5_error_code ret; 254 kcm_ccache ccache; 255 char *name; 256 257 ret = krb5_ret_stringz(request, &name); 258 if (ret) 259 return ret; 260 261 KCM_LOG_REQUEST_NAME(context, client, opcode, name); 262 263 ret = krb5_ret_creds(request, &creds); 264 if (ret) { 265 free(name); 266 return ret; 267 } 268 269 ret = kcm_ccache_resolve_client(context, client, opcode, 270 name, &ccache); 271 if (ret) { 272 free(name); 273 krb5_free_cred_contents(context, &creds); 274 return ret; 275 } 276 277 ret = kcm_ccache_store_cred(context, ccache, &creds, 0); 278 if (ret) { 279 free(name); 280 krb5_free_cred_contents(context, &creds); 281 kcm_release_ccache(context, ccache); 282 return ret; 283 } 284 285 kcm_ccache_enqueue_default(context, ccache, &creds); 286 287 free(name); 288 kcm_release_ccache(context, ccache); 289 290 return 0; 291 } 292 293 /* 294 * Request: 295 * NameZ 296 * WhichFields 297 * MatchCreds 298 * 299 * Response: 300 * Creds 301 * 302 */ 303 static krb5_error_code 304 kcm_op_retrieve(krb5_context context, 305 kcm_client *client, 306 kcm_operation opcode, 307 krb5_storage *request, 308 krb5_storage *response) 309 { 310 uint32_t flags; 311 krb5_creds mcreds; 312 krb5_error_code ret; 313 kcm_ccache ccache; 314 char *name; 315 krb5_creds *credp; 316 int free_creds = 0; 317 318 ret = krb5_ret_stringz(request, &name); 319 if (ret) 320 return ret; 321 322 KCM_LOG_REQUEST_NAME(context, client, opcode, name); 323 324 ret = krb5_ret_uint32(request, &flags); 325 if (ret) { 326 free(name); 327 return ret; 328 } 329 330 ret = krb5_ret_creds_tag(request, &mcreds); 331 if (ret) { 332 free(name); 333 return ret; 334 } 335 336 if (disallow_getting_krbtgt && 337 mcreds.server->name.name_string.len == 2 && 338 strcmp(mcreds.server->name.name_string.val[0], KRB5_TGS_NAME) == 0) 339 { 340 free(name); 341 krb5_free_cred_contents(context, &mcreds); 342 return KRB5_FCC_PERM; 343 } 344 345 ret = kcm_ccache_resolve_client(context, client, opcode, 346 name, &ccache); 347 if (ret) { 348 free(name); 349 krb5_free_cred_contents(context, &mcreds); 350 return ret; 351 } 352 353 ret = kcm_ccache_retrieve_cred(context, ccache, flags, 354 &mcreds, &credp); 355 if (ret && ((flags & KRB5_GC_CACHED) == 0) && 356 !krb5_is_config_principal(context, mcreds.server)) { 357 krb5_ccache_data ccdata; 358 359 /* try and acquire */ 360 HEIMDAL_MUTEX_lock(&ccache->mutex); 361 362 /* Fake up an internal ccache */ 363 kcm_internal_ccache(context, ccache, &ccdata); 364 365 /* glue cc layer will store creds */ 366 ret = krb5_get_credentials(context, 0, &ccdata, &mcreds, &credp); 367 if (ret == 0) 368 free_creds = 1; 369 370 HEIMDAL_MUTEX_unlock(&ccache->mutex); 371 } 372 373 if (ret == 0) { 374 ret = krb5_store_creds(response, credp); 375 } 376 377 free(name); 378 krb5_free_cred_contents(context, &mcreds); 379 kcm_release_ccache(context, ccache); 380 381 if (free_creds) 382 krb5_free_cred_contents(context, credp); 383 384 return ret; 385 } 386 387 /* 388 * Request: 389 * NameZ 390 * 391 * Response: 392 * Principal 393 */ 394 static krb5_error_code 395 kcm_op_get_principal(krb5_context context, 396 kcm_client *client, 397 kcm_operation opcode, 398 krb5_storage *request, 399 krb5_storage *response) 400 { 401 krb5_error_code ret; 402 kcm_ccache ccache; 403 char *name; 404 405 ret = krb5_ret_stringz(request, &name); 406 if (ret) 407 return ret; 408 409 KCM_LOG_REQUEST_NAME(context, client, opcode, name); 410 411 ret = kcm_ccache_resolve_client(context, client, opcode, 412 name, &ccache); 413 if (ret) { 414 free(name); 415 return ret; 416 } 417 418 if (ccache->client == NULL) 419 ret = KRB5_CC_NOTFOUND; 420 else 421 ret = krb5_store_principal(response, ccache->client); 422 423 free(name); 424 kcm_release_ccache(context, ccache); 425 426 return ret; 427 } 428 429 /* 430 * Request: 431 * NameZ 432 * 433 * Response: 434 * UUIDs 435 * 436 */ 437 static krb5_error_code 438 kcm_op_get_cred_uuid_list(krb5_context context, 439 kcm_client *client, 440 kcm_operation opcode, 441 krb5_storage *request, 442 krb5_storage *response) 443 { 444 struct kcm_creds *creds; 445 krb5_error_code ret; 446 kcm_ccache ccache; 447 char *name; 448 449 ret = krb5_ret_stringz(request, &name); 450 if (ret) 451 return ret; 452 453 KCM_LOG_REQUEST_NAME(context, client, opcode, name); 454 455 ret = kcm_ccache_resolve_client(context, client, opcode, 456 name, &ccache); 457 free(name); 458 if (ret) 459 return ret; 460 461 for (creds = ccache->creds ; creds ; creds = creds->next) { 462 ssize_t sret; 463 sret = krb5_storage_write(response, &creds->uuid, sizeof(creds->uuid)); 464 if (sret != sizeof(creds->uuid)) { 465 ret = ENOMEM; 466 break; 467 } 468 } 469 470 kcm_release_ccache(context, ccache); 471 472 return ret; 473 } 474 475 /* 476 * Request: 477 * NameZ 478 * Cursor 479 * 480 * Response: 481 * Creds 482 */ 483 static krb5_error_code 484 kcm_op_get_cred_by_uuid(krb5_context context, 485 kcm_client *client, 486 kcm_operation opcode, 487 krb5_storage *request, 488 krb5_storage *response) 489 { 490 krb5_error_code ret; 491 kcm_ccache ccache; 492 char *name; 493 struct kcm_creds *c; 494 kcmuuid_t uuid; 495 ssize_t sret; 496 497 ret = krb5_ret_stringz(request, &name); 498 if (ret) 499 return ret; 500 501 KCM_LOG_REQUEST_NAME(context, client, opcode, name); 502 503 ret = kcm_ccache_resolve_client(context, client, opcode, 504 name, &ccache); 505 free(name); 506 if (ret) 507 return ret; 508 509 sret = krb5_storage_read(request, &uuid, sizeof(uuid)); 510 if (sret != sizeof(uuid)) { 511 kcm_release_ccache(context, ccache); 512 krb5_clear_error_message(context); 513 return KRB5_CC_IO; 514 } 515 516 c = kcm_ccache_find_cred_uuid(context, ccache, uuid); 517 if (c == NULL) { 518 kcm_release_ccache(context, ccache); 519 return KRB5_CC_END; 520 } 521 522 HEIMDAL_MUTEX_lock(&ccache->mutex); 523 ret = krb5_store_creds(response, &c->cred); 524 HEIMDAL_MUTEX_unlock(&ccache->mutex); 525 526 kcm_release_ccache(context, ccache); 527 528 return ret; 529 } 530 531 /* 532 * Request: 533 * NameZ 534 * WhichFields 535 * MatchCreds 536 * 537 * Response: 538 * 539 */ 540 static krb5_error_code 541 kcm_op_remove_cred(krb5_context context, 542 kcm_client *client, 543 kcm_operation opcode, 544 krb5_storage *request, 545 krb5_storage *response) 546 { 547 uint32_t whichfields; 548 krb5_creds mcreds; 549 krb5_error_code ret; 550 kcm_ccache ccache; 551 char *name; 552 553 ret = krb5_ret_stringz(request, &name); 554 if (ret) 555 return ret; 556 557 KCM_LOG_REQUEST_NAME(context, client, opcode, name); 558 559 ret = krb5_ret_uint32(request, &whichfields); 560 if (ret) { 561 free(name); 562 return ret; 563 } 564 565 ret = krb5_ret_creds_tag(request, &mcreds); 566 if (ret) { 567 free(name); 568 return ret; 569 } 570 571 ret = kcm_ccache_resolve_client(context, client, opcode, 572 name, &ccache); 573 if (ret) { 574 free(name); 575 krb5_free_cred_contents(context, &mcreds); 576 return ret; 577 } 578 579 ret = kcm_ccache_remove_cred(context, ccache, whichfields, &mcreds); 580 581 /* XXX need to remove any events that match */ 582 583 free(name); 584 krb5_free_cred_contents(context, &mcreds); 585 kcm_release_ccache(context, ccache); 586 587 return ret; 588 } 589 590 /* 591 * Request: 592 * NameZ 593 * Flags 594 * 595 * Response: 596 * 597 */ 598 static krb5_error_code 599 kcm_op_set_flags(krb5_context context, 600 kcm_client *client, 601 kcm_operation opcode, 602 krb5_storage *request, 603 krb5_storage *response) 604 { 605 uint32_t flags; 606 krb5_error_code ret; 607 kcm_ccache ccache; 608 char *name; 609 610 ret = krb5_ret_stringz(request, &name); 611 if (ret) 612 return ret; 613 614 KCM_LOG_REQUEST_NAME(context, client, opcode, name); 615 616 ret = krb5_ret_uint32(request, &flags); 617 if (ret) { 618 free(name); 619 return ret; 620 } 621 622 ret = kcm_ccache_resolve_client(context, client, opcode, 623 name, &ccache); 624 if (ret) { 625 free(name); 626 return ret; 627 } 628 629 /* we don't really support any flags yet */ 630 free(name); 631 kcm_release_ccache(context, ccache); 632 633 return 0; 634 } 635 636 /* 637 * Request: 638 * NameZ 639 * UID 640 * GID 641 * 642 * Response: 643 * 644 */ 645 static krb5_error_code 646 kcm_op_chown(krb5_context context, 647 kcm_client *client, 648 kcm_operation opcode, 649 krb5_storage *request, 650 krb5_storage *response) 651 { 652 uint32_t uid; 653 uint32_t gid; 654 krb5_error_code ret; 655 kcm_ccache ccache; 656 char *name; 657 658 ret = krb5_ret_stringz(request, &name); 659 if (ret) 660 return ret; 661 662 KCM_LOG_REQUEST_NAME(context, client, opcode, name); 663 664 ret = krb5_ret_uint32(request, &uid); 665 if (ret) { 666 free(name); 667 return ret; 668 } 669 670 ret = krb5_ret_uint32(request, &gid); 671 if (ret) { 672 free(name); 673 return ret; 674 } 675 676 ret = kcm_ccache_resolve_client(context, client, opcode, 677 name, &ccache); 678 if (ret) { 679 free(name); 680 return ret; 681 } 682 683 ret = kcm_chown(context, client, ccache, uid, gid); 684 685 free(name); 686 kcm_release_ccache(context, ccache); 687 688 return ret; 689 } 690 691 /* 692 * Request: 693 * NameZ 694 * Mode 695 * 696 * Response: 697 * 698 */ 699 static krb5_error_code 700 kcm_op_chmod(krb5_context context, 701 kcm_client *client, 702 kcm_operation opcode, 703 krb5_storage *request, 704 krb5_storage *response) 705 { 706 uint16_t mode; 707 krb5_error_code ret; 708 kcm_ccache ccache; 709 char *name; 710 711 ret = krb5_ret_stringz(request, &name); 712 if (ret) 713 return ret; 714 715 KCM_LOG_REQUEST_NAME(context, client, opcode, name); 716 717 ret = krb5_ret_uint16(request, &mode); 718 if (ret) { 719 free(name); 720 return ret; 721 } 722 723 ret = kcm_ccache_resolve_client(context, client, opcode, 724 name, &ccache); 725 if (ret) { 726 free(name); 727 return ret; 728 } 729 730 ret = kcm_chmod(context, client, ccache, mode); 731 732 free(name); 733 kcm_release_ccache(context, ccache); 734 735 return ret; 736 } 737 738 /* 739 * Protocol extensions for moving ticket acquisition responsibility 740 * from client to KCM follow. 741 */ 742 743 /* 744 * Request: 745 * NameZ 746 * ServerPrincipalPresent 747 * ServerPrincipal OPTIONAL 748 * Key 749 * 750 * Repsonse: 751 * 752 */ 753 static krb5_error_code 754 kcm_op_get_initial_ticket(krb5_context context, 755 kcm_client *client, 756 kcm_operation opcode, 757 krb5_storage *request, 758 krb5_storage *response) 759 { 760 krb5_error_code ret; 761 kcm_ccache ccache; 762 char *name; 763 int8_t not_tgt = 0; 764 krb5_principal server = NULL; 765 krb5_keyblock key; 766 767 krb5_keyblock_zero(&key); 768 769 ret = krb5_ret_stringz(request, &name); 770 if (ret) 771 return ret; 772 773 KCM_LOG_REQUEST_NAME(context, client, opcode, name); 774 775 ret = krb5_ret_int8(request, ¬_tgt); 776 if (ret) { 777 free(name); 778 return ret; 779 } 780 781 if (not_tgt) { 782 ret = krb5_ret_principal(request, &server); 783 if (ret) { 784 free(name); 785 return ret; 786 } 787 } 788 789 ret = krb5_ret_keyblock(request, &key); 790 if (ret) { 791 free(name); 792 if (server != NULL) 793 krb5_free_principal(context, server); 794 return ret; 795 } 796 797 ret = kcm_ccache_resolve_client(context, client, opcode, 798 name, &ccache); 799 if (ret == 0) { 800 HEIMDAL_MUTEX_lock(&ccache->mutex); 801 802 if (ccache->server != NULL) { 803 krb5_free_principal(context, ccache->server); 804 ccache->server = NULL; 805 } 806 807 krb5_free_keyblock(context, &ccache->key.keyblock); 808 809 ccache->server = server; 810 ccache->key.keyblock = key; 811 ccache->flags |= KCM_FLAGS_USE_CACHED_KEY; 812 813 ret = kcm_ccache_enqueue_default(context, ccache, NULL); 814 if (ret) { 815 ccache->server = NULL; 816 krb5_keyblock_zero(&ccache->key.keyblock); 817 ccache->flags &= ~(KCM_FLAGS_USE_CACHED_KEY); 818 } 819 820 HEIMDAL_MUTEX_unlock(&ccache->mutex); 821 } 822 823 free(name); 824 825 if (ret != 0) { 826 krb5_free_principal(context, server); 827 krb5_free_keyblock(context, &key); 828 } 829 830 kcm_release_ccache(context, ccache); 831 832 return ret; 833 } 834 835 /* 836 * Request: 837 * NameZ 838 * ServerPrincipal 839 * KDCFlags 840 * EncryptionType 841 * 842 * Repsonse: 843 * 844 */ 845 static krb5_error_code 846 kcm_op_get_ticket(krb5_context context, 847 kcm_client *client, 848 kcm_operation opcode, 849 krb5_storage *request, 850 krb5_storage *response) 851 { 852 krb5_error_code ret; 853 kcm_ccache ccache; 854 char *name; 855 krb5_principal server = NULL; 856 krb5_ccache_data ccdata; 857 krb5_creds in, *out; 858 krb5_kdc_flags flags; 859 860 memset(&in, 0, sizeof(in)); 861 862 ret = krb5_ret_stringz(request, &name); 863 if (ret) 864 return ret; 865 866 KCM_LOG_REQUEST_NAME(context, client, opcode, name); 867 868 ret = krb5_ret_uint32(request, &flags.i); 869 if (ret) { 870 free(name); 871 return ret; 872 } 873 874 ret = krb5_ret_int32(request, &in.session.keytype); 875 if (ret) { 876 free(name); 877 return ret; 878 } 879 880 ret = krb5_ret_principal(request, &server); 881 if (ret) { 882 free(name); 883 return ret; 884 } 885 886 ret = kcm_ccache_resolve_client(context, client, opcode, 887 name, &ccache); 888 if (ret) { 889 krb5_free_principal(context, server); 890 free(name); 891 return ret; 892 } 893 894 HEIMDAL_MUTEX_lock(&ccache->mutex); 895 896 /* Fake up an internal ccache */ 897 kcm_internal_ccache(context, ccache, &ccdata); 898 899 in.client = ccache->client; 900 in.server = server; 901 in.times.endtime = 0; 902 903 /* glue cc layer will store creds */ 904 ret = krb5_get_credentials_with_flags(context, 0, flags, 905 &ccdata, &in, &out); 906 907 HEIMDAL_MUTEX_unlock(&ccache->mutex); 908 909 krb5_free_principal(context, server); 910 911 if (ret == 0) 912 krb5_free_cred_contents(context, out); 913 914 kcm_release_ccache(context, ccache); 915 free(name); 916 917 return ret; 918 } 919 920 /* 921 * Request: 922 * OldNameZ 923 * NewNameZ 924 * 925 * Repsonse: 926 * 927 */ 928 static krb5_error_code 929 kcm_op_move_cache(krb5_context context, 930 kcm_client *client, 931 kcm_operation opcode, 932 krb5_storage *request, 933 krb5_storage *response) 934 { 935 krb5_error_code ret; 936 kcm_ccache oldid, newid; 937 char *oldname, *newname; 938 939 ret = krb5_ret_stringz(request, &oldname); 940 if (ret) 941 return ret; 942 943 KCM_LOG_REQUEST_NAME(context, client, opcode, oldname); 944 945 ret = krb5_ret_stringz(request, &newname); 946 if (ret) { 947 free(oldname); 948 return ret; 949 } 950 951 /* move to ourself is simple, done! */ 952 if (strcmp(oldname, newname) == 0) { 953 free(oldname); 954 free(newname); 955 return 0; 956 } 957 958 ret = kcm_ccache_resolve_client(context, client, opcode, oldname, &oldid); 959 if (ret) { 960 free(oldname); 961 free(newname); 962 return ret; 963 } 964 965 /* Check if new credential cache exists, if not create one. */ 966 ret = kcm_ccache_resolve_client(context, client, opcode, newname, &newid); 967 if (ret == KRB5_FCC_NOFILE) 968 ret = kcm_ccache_new_client(context, client, newname, &newid); 969 free(newname); 970 971 if (ret) { 972 free(oldname); 973 kcm_release_ccache(context, oldid); 974 return ret; 975 } 976 977 HEIMDAL_MUTEX_lock(&oldid->mutex); 978 HEIMDAL_MUTEX_lock(&newid->mutex); 979 980 /* move content */ 981 { 982 kcm_ccache_data tmp; 983 984 #define MOVE(n,o,f) { tmp.f = n->f ; n->f = o->f; o->f = tmp.f; } 985 986 MOVE(newid, oldid, flags); 987 MOVE(newid, oldid, client); 988 MOVE(newid, oldid, server); 989 MOVE(newid, oldid, creds); 990 MOVE(newid, oldid, tkt_life); 991 MOVE(newid, oldid, renew_life); 992 MOVE(newid, oldid, key); 993 MOVE(newid, oldid, kdc_offset); 994 #undef MOVE 995 } 996 997 HEIMDAL_MUTEX_unlock(&oldid->mutex); 998 HEIMDAL_MUTEX_unlock(&newid->mutex); 999 1000 kcm_release_ccache(context, oldid); 1001 kcm_release_ccache(context, newid); 1002 1003 ret = kcm_ccache_destroy_client(context, client, oldname); 1004 if (ret == 0) 1005 kcm_drop_default_cache(context, client, oldname); 1006 1007 free(oldname); 1008 1009 return ret; 1010 } 1011 1012 static krb5_error_code 1013 kcm_op_get_cache_uuid_list(krb5_context context, 1014 kcm_client *client, 1015 kcm_operation opcode, 1016 krb5_storage *request, 1017 krb5_storage *response) 1018 { 1019 KCM_LOG_REQUEST(context, client, opcode); 1020 1021 return kcm_ccache_get_uuids(context, client, opcode, response); 1022 } 1023 1024 static krb5_error_code 1025 kcm_op_get_cache_by_uuid(krb5_context context, 1026 kcm_client *client, 1027 kcm_operation opcode, 1028 krb5_storage *request, 1029 krb5_storage *response) 1030 { 1031 krb5_error_code ret; 1032 kcmuuid_t uuid; 1033 ssize_t sret; 1034 kcm_ccache cache; 1035 1036 KCM_LOG_REQUEST(context, client, opcode); 1037 1038 sret = krb5_storage_read(request, &uuid, sizeof(uuid)); 1039 if (sret != sizeof(uuid)) { 1040 krb5_clear_error_message(context); 1041 return KRB5_CC_IO; 1042 } 1043 1044 ret = kcm_ccache_resolve_by_uuid(context, uuid, &cache); 1045 if (ret) 1046 return ret; 1047 1048 ret = kcm_access(context, client, opcode, cache); 1049 if (ret) 1050 ret = KRB5_FCC_NOFILE; 1051 1052 if (ret == 0) 1053 ret = krb5_store_stringz(response, cache->name); 1054 1055 kcm_release_ccache(context, cache); 1056 1057 return ret; 1058 } 1059 1060 struct kcm_default_cache *default_caches; 1061 1062 static krb5_error_code 1063 kcm_op_get_default_cache(krb5_context context, 1064 kcm_client *client, 1065 kcm_operation opcode, 1066 krb5_storage *request, 1067 krb5_storage *response) 1068 { 1069 struct kcm_default_cache *c; 1070 krb5_error_code ret; 1071 const char *name = NULL; 1072 char *n = NULL; 1073 1074 KCM_LOG_REQUEST(context, client, opcode); 1075 1076 for (c = default_caches; c != NULL; c = c->next) { 1077 if (kcm_is_same_session(client, c->uid, c->session)) { 1078 name = c->name; 1079 break; 1080 } 1081 } 1082 if (name == NULL) 1083 name = n = kcm_ccache_first_name(client); 1084 1085 if (name == NULL) { 1086 asprintf(&n, "%d", (int)client->uid); 1087 name = n; 1088 } 1089 if (name == NULL) 1090 return ENOMEM; 1091 ret = krb5_store_stringz(response, name); 1092 if (n) 1093 free(n); 1094 return ret; 1095 } 1096 1097 static void 1098 kcm_drop_default_cache(krb5_context context, kcm_client *client, char *name) 1099 { 1100 struct kcm_default_cache **c; 1101 1102 for (c = &default_caches; *c != NULL; c = &(*c)->next) { 1103 if (!kcm_is_same_session(client, (*c)->uid, (*c)->session)) 1104 continue; 1105 if (strcmp((*c)->name, name) == 0) { 1106 struct kcm_default_cache *h = *c; 1107 *c = (*c)->next; 1108 free(h->name); 1109 free(h); 1110 break; 1111 } 1112 } 1113 } 1114 1115 static krb5_error_code 1116 kcm_op_set_default_cache(krb5_context context, 1117 kcm_client *client, 1118 kcm_operation opcode, 1119 krb5_storage *request, 1120 krb5_storage *response) 1121 { 1122 struct kcm_default_cache *c; 1123 krb5_error_code ret; 1124 char *name; 1125 1126 ret = krb5_ret_stringz(request, &name); 1127 if (ret) 1128 return ret; 1129 1130 KCM_LOG_REQUEST_NAME(context, client, opcode, name); 1131 1132 for (c = default_caches; c != NULL; c = c->next) { 1133 if (kcm_is_same_session(client, c->uid, c->session)) 1134 break; 1135 } 1136 if (c == NULL) { 1137 c = malloc(sizeof(*c)); 1138 if (c == NULL) 1139 return ENOMEM; 1140 c->session = client->session; 1141 c->uid = client->uid; 1142 c->name = strdup(name); 1143 1144 c->next = default_caches; 1145 default_caches = c; 1146 } else { 1147 free(c->name); 1148 c->name = strdup(name); 1149 } 1150 1151 return 0; 1152 } 1153 1154 static krb5_error_code 1155 kcm_op_get_kdc_offset(krb5_context context, 1156 kcm_client *client, 1157 kcm_operation opcode, 1158 krb5_storage *request, 1159 krb5_storage *response) 1160 { 1161 krb5_error_code ret; 1162 kcm_ccache ccache; 1163 char *name; 1164 1165 ret = krb5_ret_stringz(request, &name); 1166 if (ret) 1167 return ret; 1168 1169 KCM_LOG_REQUEST_NAME(context, client, opcode, name); 1170 1171 ret = kcm_ccache_resolve_client(context, client, opcode, name, &ccache); 1172 free(name); 1173 if (ret) 1174 return ret; 1175 1176 HEIMDAL_MUTEX_lock(&ccache->mutex); 1177 ret = krb5_store_int32(response, ccache->kdc_offset); 1178 HEIMDAL_MUTEX_unlock(&ccache->mutex); 1179 1180 kcm_release_ccache(context, ccache); 1181 1182 return ret; 1183 } 1184 1185 static krb5_error_code 1186 kcm_op_set_kdc_offset(krb5_context context, 1187 kcm_client *client, 1188 kcm_operation opcode, 1189 krb5_storage *request, 1190 krb5_storage *response) 1191 { 1192 krb5_error_code ret; 1193 kcm_ccache ccache; 1194 int32_t offset; 1195 char *name; 1196 1197 ret = krb5_ret_stringz(request, &name); 1198 if (ret) 1199 return ret; 1200 1201 KCM_LOG_REQUEST_NAME(context, client, opcode, name); 1202 1203 ret = krb5_ret_int32(request, &offset); 1204 if (ret) { 1205 free(name); 1206 return ret; 1207 } 1208 1209 ret = kcm_ccache_resolve_client(context, client, opcode, name, &ccache); 1210 free(name); 1211 if (ret) 1212 return ret; 1213 1214 HEIMDAL_MUTEX_lock(&ccache->mutex); 1215 ccache->kdc_offset = offset; 1216 HEIMDAL_MUTEX_unlock(&ccache->mutex); 1217 1218 kcm_release_ccache(context, ccache); 1219 1220 return ret; 1221 } 1222 1223 struct kcm_ntlm_cred { 1224 kcmuuid_t uuid; 1225 char *user; 1226 char *domain; 1227 krb5_data nthash; 1228 uid_t uid; 1229 pid_t session; 1230 struct kcm_ntlm_cred *next; 1231 }; 1232 1233 static struct kcm_ntlm_cred *ntlm_head; 1234 1235 static void 1236 free_cred(struct kcm_ntlm_cred *cred) 1237 { 1238 free(cred->user); 1239 free(cred->domain); 1240 krb5_data_free(&cred->nthash); 1241 free(cred); 1242 } 1243 1244 1245 /* 1246 * name 1247 * domain 1248 * ntlm hash 1249 * 1250 * Reply: 1251 * uuid 1252 */ 1253 1254 static struct kcm_ntlm_cred * 1255 find_ntlm_cred(const char *user, const char *domain, kcm_client *client) 1256 { 1257 struct kcm_ntlm_cred *c; 1258 1259 for (c = ntlm_head; c != NULL; c = c->next) 1260 if ((user[0] == '\0' || strcmp(user, c->user) == 0) && 1261 (domain == NULL || strcmp(domain, c->domain) == 0) && 1262 kcm_is_same_session(client, c->uid, c->session)) 1263 return c; 1264 1265 return NULL; 1266 } 1267 1268 static krb5_error_code 1269 kcm_op_add_ntlm_cred(krb5_context context, 1270 kcm_client *client, 1271 kcm_operation opcode, 1272 krb5_storage *request, 1273 krb5_storage *response) 1274 { 1275 struct kcm_ntlm_cred *cred, *c; 1276 krb5_error_code ret; 1277 1278 cred = calloc(1, sizeof(*cred)); 1279 if (cred == NULL) 1280 return ENOMEM; 1281 1282 RAND_bytes(cred->uuid, sizeof(cred->uuid)); 1283 1284 ret = krb5_ret_stringz(request, &cred->user); 1285 if (ret) 1286 goto error; 1287 1288 ret = krb5_ret_stringz(request, &cred->domain); 1289 if (ret) 1290 goto error; 1291 1292 ret = krb5_ret_data(request, &cred->nthash); 1293 if (ret) 1294 goto error; 1295 1296 /* search for dups */ 1297 c = find_ntlm_cred(cred->user, cred->domain, client); 1298 if (c) { 1299 krb5_data hash = c->nthash; 1300 c->nthash = cred->nthash; 1301 cred->nthash = hash; 1302 free_cred(cred); 1303 cred = c; 1304 } else { 1305 cred->next = ntlm_head; 1306 ntlm_head = cred; 1307 } 1308 1309 cred->uid = client->uid; 1310 cred->session = client->session; 1311 1312 /* write response */ 1313 (void)krb5_storage_write(response, &cred->uuid, sizeof(cred->uuid)); 1314 1315 return 0; 1316 1317 error: 1318 free_cred(cred); 1319 1320 return ret; 1321 } 1322 1323 /* 1324 * { "HAVE_NTLM_CRED", NULL }, 1325 * 1326 * input: 1327 * name 1328 * domain 1329 */ 1330 1331 static krb5_error_code 1332 kcm_op_have_ntlm_cred(krb5_context context, 1333 kcm_client *client, 1334 kcm_operation opcode, 1335 krb5_storage *request, 1336 krb5_storage *response) 1337 { 1338 struct kcm_ntlm_cred *c; 1339 char *user = NULL, *domain = NULL; 1340 krb5_error_code ret; 1341 1342 ret = krb5_ret_stringz(request, &user); 1343 if (ret) 1344 goto error; 1345 1346 ret = krb5_ret_stringz(request, &domain); 1347 if (ret) 1348 goto error; 1349 1350 if (domain[0] == '\0') { 1351 free(domain); 1352 domain = NULL; 1353 } 1354 1355 c = find_ntlm_cred(user, domain, client); 1356 if (c == NULL) 1357 ret = ENOENT; 1358 1359 error: 1360 free(user); 1361 if (domain) 1362 free(domain); 1363 1364 return ret; 1365 } 1366 1367 /* 1368 * { "DEL_NTLM_CRED", NULL }, 1369 * 1370 * input: 1371 * name 1372 * domain 1373 */ 1374 1375 static krb5_error_code 1376 kcm_op_del_ntlm_cred(krb5_context context, 1377 kcm_client *client, 1378 kcm_operation opcode, 1379 krb5_storage *request, 1380 krb5_storage *response) 1381 { 1382 struct kcm_ntlm_cred **cp, *c; 1383 char *user = NULL, *domain = NULL; 1384 krb5_error_code ret; 1385 1386 ret = krb5_ret_stringz(request, &user); 1387 if (ret) 1388 goto error; 1389 1390 ret = krb5_ret_stringz(request, &domain); 1391 if (ret) 1392 goto error; 1393 1394 for (cp = &ntlm_head; *cp != NULL; cp = &(*cp)->next) { 1395 if (strcmp(user, (*cp)->user) == 0 && strcmp(domain, (*cp)->domain) == 0 && 1396 kcm_is_same_session(client, (*cp)->uid, (*cp)->session)) 1397 { 1398 c = *cp; 1399 *cp = c->next; 1400 1401 free_cred(c); 1402 break; 1403 } 1404 } 1405 1406 error: 1407 free(user); 1408 free(domain); 1409 1410 return ret; 1411 } 1412 1413 /* 1414 * { "DO_NTLM_AUTH", NULL }, 1415 * 1416 * input: 1417 * name:string 1418 * domain:string 1419 * type2:data 1420 * 1421 * reply: 1422 * type3:data 1423 * flags:int32 1424 * session-key:data 1425 */ 1426 1427 #define NTLM_FLAG_SESSIONKEY 1 1428 #define NTLM_FLAG_NTLM2_SESSION 2 1429 #define NTLM_FLAG_KEYEX 4 1430 1431 static krb5_error_code 1432 kcm_op_do_ntlm(krb5_context context, 1433 kcm_client *client, 1434 kcm_operation opcode, 1435 krb5_storage *request, 1436 krb5_storage *response) 1437 { 1438 struct kcm_ntlm_cred *c; 1439 struct ntlm_type2 type2; 1440 struct ntlm_type3 type3; 1441 char *user = NULL, *domain = NULL; 1442 struct ntlm_buf ndata, sessionkey; 1443 krb5_data data; 1444 krb5_error_code ret; 1445 uint32_t flags = 0; 1446 1447 memset(&type2, 0, sizeof(type2)); 1448 memset(&type3, 0, sizeof(type3)); 1449 sessionkey.data = NULL; 1450 sessionkey.length = 0; 1451 1452 ret = krb5_ret_stringz(request, &user); 1453 if (ret) 1454 goto error; 1455 1456 ret = krb5_ret_stringz(request, &domain); 1457 if (ret) 1458 goto error; 1459 1460 if (domain[0] == '\0') { 1461 free(domain); 1462 domain = NULL; 1463 } 1464 1465 c = find_ntlm_cred(user, domain, client); 1466 if (c == NULL) { 1467 ret = EINVAL; 1468 goto error; 1469 } 1470 1471 ret = krb5_ret_data(request, &data); 1472 if (ret) 1473 goto error; 1474 1475 ndata.data = data.data; 1476 ndata.length = data.length; 1477 1478 ret = heim_ntlm_decode_type2(&ndata, &type2); 1479 krb5_data_free(&data); 1480 if (ret) 1481 goto error; 1482 1483 if (domain && strcmp(domain, type2.targetname) == 0) { 1484 ret = EINVAL; 1485 goto error; 1486 } 1487 1488 type3.username = c->user; 1489 type3.flags = type2.flags; 1490 type3.targetname = type2.targetname; 1491 type3.ws = rk_UNCONST("workstation"); 1492 1493 /* 1494 * NTLM Version 1 if no targetinfo buffer. 1495 */ 1496 1497 if (1 || type2.targetinfo.length == 0) { 1498 struct ntlm_buf sessionkey; 1499 1500 if (type2.flags & NTLM_NEG_NTLM2_SESSION) { 1501 unsigned char nonce[8]; 1502 1503 if (RAND_bytes(nonce, sizeof(nonce)) != 1) { 1504 ret = EINVAL; 1505 goto error; 1506 } 1507 1508 ret = heim_ntlm_calculate_ntlm2_sess(nonce, 1509 type2.challenge, 1510 c->nthash.data, 1511 &type3.lm, 1512 &type3.ntlm); 1513 } else { 1514 ret = heim_ntlm_calculate_ntlm1(c->nthash.data, 1515 c->nthash.length, 1516 type2.challenge, 1517 &type3.ntlm); 1518 1519 } 1520 if (ret) 1521 goto error; 1522 1523 ret = heim_ntlm_build_ntlm1_master(c->nthash.data, 1524 c->nthash.length, 1525 &sessionkey, 1526 &type3.sessionkey); 1527 if (ret) { 1528 if (type3.lm.data) 1529 free(type3.lm.data); 1530 if (type3.ntlm.data) 1531 free(type3.ntlm.data); 1532 goto error; 1533 } 1534 1535 free(sessionkey.data); 1536 if (ret) { 1537 if (type3.lm.data) 1538 free(type3.lm.data); 1539 if (type3.ntlm.data) 1540 free(type3.ntlm.data); 1541 goto error; 1542 } 1543 flags |= NTLM_FLAG_SESSIONKEY; 1544 #if 0 1545 } else { 1546 struct ntlm_buf sessionkey; 1547 unsigned char ntlmv2[16]; 1548 struct ntlm_targetinfo ti; 1549 1550 /* verify infotarget */ 1551 1552 ret = heim_ntlm_decode_targetinfo(&type2.targetinfo, 1, &ti); 1553 if(ret) { 1554 _gss_ntlm_delete_sec_context(minor_status, 1555 context_handle, NULL); 1556 *minor_status = ret; 1557 return GSS_S_FAILURE; 1558 } 1559 1560 if (ti.domainname && strcmp(ti.domainname, name->domain) != 0) { 1561 _gss_ntlm_delete_sec_context(minor_status, 1562 context_handle, NULL); 1563 *minor_status = EINVAL; 1564 return GSS_S_FAILURE; 1565 } 1566 1567 ret = heim_ntlm_calculate_ntlm2(ctx->client->key.data, 1568 ctx->client->key.length, 1569 type3.username, 1570 name->domain, 1571 type2.challenge, 1572 &type2.targetinfo, 1573 ntlmv2, 1574 &type3.ntlm); 1575 if (ret) { 1576 _gss_ntlm_delete_sec_context(minor_status, 1577 context_handle, NULL); 1578 *minor_status = ret; 1579 return GSS_S_FAILURE; 1580 } 1581 1582 ret = heim_ntlm_build_ntlm1_master(ntlmv2, sizeof(ntlmv2), 1583 &sessionkey, 1584 &type3.sessionkey); 1585 memset(ntlmv2, 0, sizeof(ntlmv2)); 1586 if (ret) { 1587 _gss_ntlm_delete_sec_context(minor_status, 1588 context_handle, NULL); 1589 *minor_status = ret; 1590 return GSS_S_FAILURE; 1591 } 1592 1593 flags |= NTLM_FLAG_NTLM2_SESSION | 1594 NTLM_FLAG_SESSION; 1595 1596 if (type3.flags & NTLM_NEG_KEYEX) 1597 flags |= NTLM_FLAG_KEYEX; 1598 1599 ret = krb5_data_copy(&ctx->sessionkey, 1600 sessionkey.data, sessionkey.length); 1601 free(sessionkey.data); 1602 if (ret) { 1603 _gss_ntlm_delete_sec_context(minor_status, 1604 context_handle, NULL); 1605 *minor_status = ret; 1606 return GSS_S_FAILURE; 1607 } 1608 #endif 1609 } 1610 1611 #if 0 1612 if (flags & NTLM_FLAG_NTLM2_SESSION) { 1613 _gss_ntlm_set_key(&ctx->u.v2.send, 0, (ctx->flags & NTLM_NEG_KEYEX), 1614 ctx->sessionkey.data, 1615 ctx->sessionkey.length); 1616 _gss_ntlm_set_key(&ctx->u.v2.recv, 1, (ctx->flags & NTLM_NEG_KEYEX), 1617 ctx->sessionkey.data, 1618 ctx->sessionkey.length); 1619 } else { 1620 flags |= NTLM_FLAG_SESSION; 1621 RC4_set_key(&ctx->u.v1.crypto_recv.key, 1622 ctx->sessionkey.length, 1623 ctx->sessionkey.data); 1624 RC4_set_key(&ctx->u.v1.crypto_send.key, 1625 ctx->sessionkey.length, 1626 ctx->sessionkey.data); 1627 } 1628 #endif 1629 1630 ret = heim_ntlm_encode_type3(&type3, &ndata); 1631 if (ret) 1632 goto error; 1633 1634 data.data = ndata.data; 1635 data.length = ndata.length; 1636 ret = krb5_store_data(response, data); 1637 heim_ntlm_free_buf(&ndata); 1638 if (ret) goto error; 1639 1640 ret = krb5_store_int32(response, flags); 1641 if (ret) goto error; 1642 1643 data.data = sessionkey.data; 1644 data.length = sessionkey.length; 1645 1646 ret = krb5_store_data(response, data); 1647 if (ret) goto error; 1648 1649 error: 1650 free(type3.username); 1651 heim_ntlm_free_type2(&type2); 1652 free(user); 1653 if (domain) 1654 free(domain); 1655 1656 return ret; 1657 } 1658 1659 1660 /* 1661 * { "GET_NTLM_UUID_LIST", NULL } 1662 * 1663 * reply: 1664 * 1 user domain 1665 * 0 [ end of list ] 1666 */ 1667 1668 static krb5_error_code 1669 kcm_op_get_ntlm_user_list(krb5_context context, 1670 kcm_client *client, 1671 kcm_operation opcode, 1672 krb5_storage *request, 1673 krb5_storage *response) 1674 { 1675 struct kcm_ntlm_cred *c; 1676 krb5_error_code ret; 1677 1678 for (c = ntlm_head; c != NULL; c = c->next) { 1679 if (!kcm_is_same_session(client, c->uid, c->session)) 1680 continue; 1681 1682 ret = krb5_store_uint32(response, 1); 1683 if (ret) 1684 return ret; 1685 ret = krb5_store_stringz(response, c->user); 1686 if (ret) 1687 return ret; 1688 ret = krb5_store_stringz(response, c->domain); 1689 if (ret) 1690 return ret; 1691 } 1692 return krb5_store_uint32(response, 0); 1693 } 1694 1695 /* 1696 * 1697 */ 1698 1699 static struct kcm_op kcm_ops[] = { 1700 { "NOOP", kcm_op_noop }, 1701 { "GET_NAME", kcm_op_get_name }, 1702 { "RESOLVE", kcm_op_noop }, 1703 { "GEN_NEW", kcm_op_gen_new }, 1704 { "INITIALIZE", kcm_op_initialize }, 1705 { "DESTROY", kcm_op_destroy }, 1706 { "STORE", kcm_op_store }, 1707 { "RETRIEVE", kcm_op_retrieve }, 1708 { "GET_PRINCIPAL", kcm_op_get_principal }, 1709 { "GET_CRED_UUID_LIST", kcm_op_get_cred_uuid_list }, 1710 { "GET_CRED_BY_UUID", kcm_op_get_cred_by_uuid }, 1711 { "REMOVE_CRED", kcm_op_remove_cred }, 1712 { "SET_FLAGS", kcm_op_set_flags }, 1713 { "CHOWN", kcm_op_chown }, 1714 { "CHMOD", kcm_op_chmod }, 1715 { "GET_INITIAL_TICKET", kcm_op_get_initial_ticket }, 1716 { "GET_TICKET", kcm_op_get_ticket }, 1717 { "MOVE_CACHE", kcm_op_move_cache }, 1718 { "GET_CACHE_UUID_LIST", kcm_op_get_cache_uuid_list }, 1719 { "GET_CACHE_BY_UUID", kcm_op_get_cache_by_uuid }, 1720 { "GET_DEFAULT_CACHE", kcm_op_get_default_cache }, 1721 { "SET_DEFAULT_CACHE", kcm_op_set_default_cache }, 1722 { "GET_KDC_OFFSET", kcm_op_get_kdc_offset }, 1723 { "SET_KDC_OFFSET", kcm_op_set_kdc_offset }, 1724 { "ADD_NTLM_CRED", kcm_op_add_ntlm_cred }, 1725 { "HAVE_USER_CRED", kcm_op_have_ntlm_cred }, 1726 { "DEL_NTLM_CRED", kcm_op_del_ntlm_cred }, 1727 { "DO_NTLM_AUTH", kcm_op_do_ntlm }, 1728 { "GET_NTLM_USER_LIST", kcm_op_get_ntlm_user_list } 1729 }; 1730 1731 1732 const char * 1733 kcm_op2string(kcm_operation opcode) 1734 { 1735 if (opcode >= sizeof(kcm_ops)/sizeof(kcm_ops[0])) 1736 return "Unknown operation"; 1737 1738 return kcm_ops[opcode].name; 1739 } 1740 1741 krb5_error_code 1742 kcm_dispatch(krb5_context context, 1743 kcm_client *client, 1744 krb5_data *req_data, 1745 krb5_data *resp_data) 1746 { 1747 krb5_error_code ret; 1748 kcm_method method; 1749 krb5_storage *req_sp = NULL; 1750 krb5_storage *resp_sp = NULL; 1751 uint16_t opcode; 1752 1753 resp_sp = krb5_storage_emem(); 1754 if (resp_sp == NULL) { 1755 return ENOMEM; 1756 } 1757 1758 if (client->pid == -1) { 1759 kcm_log(0, "Client had invalid process number"); 1760 ret = KRB5_FCC_INTERNAL; 1761 goto out; 1762 } 1763 1764 req_sp = krb5_storage_from_data(req_data); 1765 if (req_sp == NULL) { 1766 kcm_log(0, "Process %d: failed to initialize storage from data", 1767 client->pid); 1768 ret = KRB5_CC_IO; 1769 goto out; 1770 } 1771 1772 ret = krb5_ret_uint16(req_sp, &opcode); 1773 if (ret) { 1774 kcm_log(0, "Process %d: didn't send a message", client->pid); 1775 goto out; 1776 } 1777 1778 if (opcode >= sizeof(kcm_ops)/sizeof(kcm_ops[0])) { 1779 kcm_log(0, "Process %d: invalid operation code %d", 1780 client->pid, opcode); 1781 ret = KRB5_FCC_INTERNAL; 1782 goto out; 1783 } 1784 method = kcm_ops[opcode].method; 1785 if (method == NULL) { 1786 kcm_log(0, "Process %d: operation code %s not implemented", 1787 client->pid, kcm_op2string(opcode)); 1788 ret = KRB5_FCC_INTERNAL; 1789 goto out; 1790 } 1791 1792 /* seek past place for status code */ 1793 krb5_storage_seek(resp_sp, 4, SEEK_SET); 1794 1795 ret = (*method)(context, client, opcode, req_sp, resp_sp); 1796 1797 out: 1798 if (req_sp != NULL) { 1799 krb5_storage_free(req_sp); 1800 } 1801 1802 krb5_storage_seek(resp_sp, 0, SEEK_SET); 1803 krb5_store_int32(resp_sp, ret); 1804 1805 ret = krb5_storage_to_data(resp_sp, resp_data); 1806 krb5_storage_free(resp_sp); 1807 1808 return ret; 1809 } 1810 1811