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