1 /* $NetBSD: protocol.c,v 1.1.1.1 2011/04/13 18:14:36 elric 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(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 1076 KCM_LOG_REQUEST(context, client, opcode); 1077 1078 for (c = default_caches; c != NULL; c = c->next) { 1079 if (kcm_is_same_session(client, c->uid, c->session)) { 1080 name = c->name; 1081 break; 1082 } 1083 } 1084 if (name == NULL) 1085 name = n = kcm_ccache_first_name(client); 1086 1087 if (name == NULL) { 1088 asprintf(&n, "%d", (int)client->uid); 1089 name = n; 1090 } 1091 if (name == NULL) 1092 return ENOMEM; 1093 ret = krb5_store_stringz(response, name); 1094 if (n) 1095 free(n); 1096 return ret; 1097 } 1098 1099 static void 1100 kcm_drop_default_cache(krb5_context context, kcm_client *client, char *name) 1101 { 1102 struct kcm_default_cache **c; 1103 1104 for (c = &default_caches; *c != NULL; c = &(*c)->next) { 1105 if (!kcm_is_same_session(client, (*c)->uid, (*c)->session)) 1106 continue; 1107 if (strcmp((*c)->name, name) == 0) { 1108 struct kcm_default_cache *h = *c; 1109 *c = (*c)->next; 1110 free(h->name); 1111 free(h); 1112 break; 1113 } 1114 } 1115 } 1116 1117 static krb5_error_code 1118 kcm_op_set_default_cache(krb5_context context, 1119 kcm_client *client, 1120 kcm_operation opcode, 1121 krb5_storage *request, 1122 krb5_storage *response) 1123 { 1124 struct kcm_default_cache *c; 1125 krb5_error_code ret; 1126 char *name; 1127 1128 ret = krb5_ret_stringz(request, &name); 1129 if (ret) 1130 return ret; 1131 1132 KCM_LOG_REQUEST_NAME(context, client, opcode, name); 1133 1134 for (c = default_caches; c != NULL; c = c->next) { 1135 if (kcm_is_same_session(client, c->uid, c->session)) 1136 break; 1137 } 1138 if (c == NULL) { 1139 c = malloc(sizeof(*c)); 1140 if (c == NULL) 1141 return ENOMEM; 1142 c->session = client->session; 1143 c->uid = client->uid; 1144 c->name = strdup(name); 1145 1146 c->next = default_caches; 1147 default_caches = c; 1148 } else { 1149 free(c->name); 1150 c->name = strdup(name); 1151 } 1152 1153 return 0; 1154 } 1155 1156 static krb5_error_code 1157 kcm_op_get_kdc_offset(krb5_context context, 1158 kcm_client *client, 1159 kcm_operation opcode, 1160 krb5_storage *request, 1161 krb5_storage *response) 1162 { 1163 krb5_error_code ret; 1164 kcm_ccache ccache; 1165 char *name; 1166 1167 ret = krb5_ret_stringz(request, &name); 1168 if (ret) 1169 return ret; 1170 1171 KCM_LOG_REQUEST_NAME(context, client, opcode, name); 1172 1173 ret = kcm_ccache_resolve_client(context, client, opcode, name, &ccache); 1174 free(name); 1175 if (ret) 1176 return ret; 1177 1178 HEIMDAL_MUTEX_lock(&ccache->mutex); 1179 ret = krb5_store_int32(response, ccache->kdc_offset); 1180 HEIMDAL_MUTEX_unlock(&ccache->mutex); 1181 1182 kcm_release_ccache(context, ccache); 1183 1184 return ret; 1185 } 1186 1187 static krb5_error_code 1188 kcm_op_set_kdc_offset(krb5_context context, 1189 kcm_client *client, 1190 kcm_operation opcode, 1191 krb5_storage *request, 1192 krb5_storage *response) 1193 { 1194 krb5_error_code ret; 1195 kcm_ccache ccache; 1196 int32_t offset; 1197 char *name; 1198 1199 ret = krb5_ret_stringz(request, &name); 1200 if (ret) 1201 return ret; 1202 1203 KCM_LOG_REQUEST_NAME(context, client, opcode, name); 1204 1205 ret = krb5_ret_int32(request, &offset); 1206 if (ret) { 1207 free(name); 1208 return ret; 1209 } 1210 1211 ret = kcm_ccache_resolve_client(context, client, opcode, name, &ccache); 1212 free(name); 1213 if (ret) 1214 return ret; 1215 1216 HEIMDAL_MUTEX_lock(&ccache->mutex); 1217 ccache->kdc_offset = offset; 1218 HEIMDAL_MUTEX_unlock(&ccache->mutex); 1219 1220 kcm_release_ccache(context, ccache); 1221 1222 return ret; 1223 } 1224 1225 struct kcm_ntlm_cred { 1226 kcmuuid_t uuid; 1227 char *user; 1228 char *domain; 1229 krb5_data nthash; 1230 uid_t uid; 1231 pid_t session; 1232 struct kcm_ntlm_cred *next; 1233 }; 1234 1235 static struct kcm_ntlm_cred *ntlm_head; 1236 1237 static void 1238 free_cred(struct kcm_ntlm_cred *cred) 1239 { 1240 free(cred->user); 1241 free(cred->domain); 1242 krb5_data_free(&cred->nthash); 1243 free(cred); 1244 } 1245 1246 1247 /* 1248 * name 1249 * domain 1250 * ntlm hash 1251 * 1252 * Reply: 1253 * uuid 1254 */ 1255 1256 static struct kcm_ntlm_cred * 1257 find_ntlm_cred(const char *user, const char *domain, kcm_client *client) 1258 { 1259 struct kcm_ntlm_cred *c; 1260 1261 for (c = ntlm_head; c != NULL; c = c->next) 1262 if ((user[0] == '\0' || strcmp(user, c->user) == 0) && 1263 (domain == NULL || strcmp(domain, c->domain) == 0) && 1264 kcm_is_same_session(client, c->uid, c->session)) 1265 return c; 1266 1267 return NULL; 1268 } 1269 1270 static krb5_error_code 1271 kcm_op_add_ntlm_cred(krb5_context context, 1272 kcm_client *client, 1273 kcm_operation opcode, 1274 krb5_storage *request, 1275 krb5_storage *response) 1276 { 1277 struct kcm_ntlm_cred *cred, *c; 1278 krb5_error_code ret; 1279 1280 cred = calloc(1, sizeof(*cred)); 1281 if (cred == NULL) 1282 return ENOMEM; 1283 1284 RAND_bytes(cred->uuid, sizeof(cred->uuid)); 1285 1286 ret = krb5_ret_stringz(request, &cred->user); 1287 if (ret) 1288 goto error; 1289 1290 ret = krb5_ret_stringz(request, &cred->domain); 1291 if (ret) 1292 goto error; 1293 1294 ret = krb5_ret_data(request, &cred->nthash); 1295 if (ret) 1296 goto error; 1297 1298 /* search for dups */ 1299 c = find_ntlm_cred(cred->user, cred->domain, client); 1300 if (c) { 1301 krb5_data hash = c->nthash; 1302 c->nthash = cred->nthash; 1303 cred->nthash = hash; 1304 free_cred(cred); 1305 cred = c; 1306 } else { 1307 cred->next = ntlm_head; 1308 ntlm_head = cred; 1309 } 1310 1311 cred->uid = client->uid; 1312 cred->session = client->session; 1313 1314 /* write response */ 1315 (void)krb5_storage_write(response, &cred->uuid, sizeof(cred->uuid)); 1316 1317 return 0; 1318 1319 error: 1320 free_cred(cred); 1321 1322 return ret; 1323 } 1324 1325 /* 1326 * { "HAVE_NTLM_CRED", NULL }, 1327 * 1328 * input: 1329 * name 1330 * domain 1331 */ 1332 1333 static krb5_error_code 1334 kcm_op_have_ntlm_cred(krb5_context context, 1335 kcm_client *client, 1336 kcm_operation opcode, 1337 krb5_storage *request, 1338 krb5_storage *response) 1339 { 1340 struct kcm_ntlm_cred *c; 1341 char *user = NULL, *domain = NULL; 1342 krb5_error_code ret; 1343 1344 ret = krb5_ret_stringz(request, &user); 1345 if (ret) 1346 goto error; 1347 1348 ret = krb5_ret_stringz(request, &domain); 1349 if (ret) 1350 goto error; 1351 1352 if (domain[0] == '\0') { 1353 free(domain); 1354 domain = NULL; 1355 } 1356 1357 c = find_ntlm_cred(user, domain, client); 1358 if (c == NULL) 1359 ret = ENOENT; 1360 1361 error: 1362 free(user); 1363 if (domain) 1364 free(domain); 1365 1366 return ret; 1367 } 1368 1369 /* 1370 * { "DEL_NTLM_CRED", NULL }, 1371 * 1372 * input: 1373 * name 1374 * domain 1375 */ 1376 1377 static krb5_error_code 1378 kcm_op_del_ntlm_cred(krb5_context context, 1379 kcm_client *client, 1380 kcm_operation opcode, 1381 krb5_storage *request, 1382 krb5_storage *response) 1383 { 1384 struct kcm_ntlm_cred **cp, *c; 1385 char *user = NULL, *domain = NULL; 1386 krb5_error_code ret; 1387 1388 ret = krb5_ret_stringz(request, &user); 1389 if (ret) 1390 goto error; 1391 1392 ret = krb5_ret_stringz(request, &domain); 1393 if (ret) 1394 goto error; 1395 1396 for (cp = &ntlm_head; *cp != NULL; cp = &(*cp)->next) { 1397 if (strcmp(user, (*cp)->user) == 0 && strcmp(domain, (*cp)->domain) == 0 && 1398 kcm_is_same_session(client, (*cp)->uid, (*cp)->session)) 1399 { 1400 c = *cp; 1401 *cp = c->next; 1402 1403 free_cred(c); 1404 break; 1405 } 1406 } 1407 1408 error: 1409 free(user); 1410 free(domain); 1411 1412 return ret; 1413 } 1414 1415 /* 1416 * { "DO_NTLM_AUTH", NULL }, 1417 * 1418 * input: 1419 * name:string 1420 * domain:string 1421 * type2:data 1422 * 1423 * reply: 1424 * type3:data 1425 * flags:int32 1426 * session-key:data 1427 */ 1428 1429 #define NTLM_FLAG_SESSIONKEY 1 1430 #define NTLM_FLAG_NTLM2_SESSION 2 1431 #define NTLM_FLAG_KEYEX 4 1432 1433 static krb5_error_code 1434 kcm_op_do_ntlm(krb5_context context, 1435 kcm_client *client, 1436 kcm_operation opcode, 1437 krb5_storage *request, 1438 krb5_storage *response) 1439 { 1440 struct kcm_ntlm_cred *c; 1441 struct ntlm_type2 type2; 1442 struct ntlm_type3 type3; 1443 char *user = NULL, *domain = NULL; 1444 struct ntlm_buf ndata, sessionkey; 1445 krb5_data data; 1446 krb5_error_code ret; 1447 uint32_t flags = 0; 1448 1449 memset(&type2, 0, sizeof(type2)); 1450 memset(&type3, 0, sizeof(type3)); 1451 sessionkey.data = NULL; 1452 sessionkey.length = 0; 1453 1454 ret = krb5_ret_stringz(request, &user); 1455 if (ret) 1456 goto error; 1457 1458 ret = krb5_ret_stringz(request, &domain); 1459 if (ret) 1460 goto error; 1461 1462 if (domain[0] == '\0') { 1463 free(domain); 1464 domain = NULL; 1465 } 1466 1467 c = find_ntlm_cred(user, domain, client); 1468 if (c == NULL) { 1469 ret = EINVAL; 1470 goto error; 1471 } 1472 1473 ret = krb5_ret_data(request, &data); 1474 if (ret) 1475 goto error; 1476 1477 ndata.data = data.data; 1478 ndata.length = data.length; 1479 1480 ret = heim_ntlm_decode_type2(&ndata, &type2); 1481 krb5_data_free(&data); 1482 if (ret) 1483 goto error; 1484 1485 if (domain && strcmp(domain, type2.targetname) == 0) { 1486 ret = EINVAL; 1487 goto error; 1488 } 1489 1490 type3.username = c->user; 1491 type3.flags = type2.flags; 1492 type3.targetname = type2.targetname; 1493 type3.ws = rk_UNCONST("workstation"); 1494 1495 /* 1496 * NTLM Version 1 if no targetinfo buffer. 1497 */ 1498 1499 if (1 || type2.targetinfo.length == 0) { 1500 struct ntlm_buf sessionkey; 1501 1502 if (type2.flags & NTLM_NEG_NTLM2_SESSION) { 1503 unsigned char nonce[8]; 1504 1505 if (RAND_bytes(nonce, sizeof(nonce)) != 1) { 1506 ret = EINVAL; 1507 goto error; 1508 } 1509 1510 ret = heim_ntlm_calculate_ntlm2_sess(nonce, 1511 type2.challenge, 1512 c->nthash.data, 1513 &type3.lm, 1514 &type3.ntlm); 1515 } else { 1516 ret = heim_ntlm_calculate_ntlm1(c->nthash.data, 1517 c->nthash.length, 1518 type2.challenge, 1519 &type3.ntlm); 1520 1521 } 1522 if (ret) 1523 goto error; 1524 1525 ret = heim_ntlm_build_ntlm1_master(c->nthash.data, 1526 c->nthash.length, 1527 &sessionkey, 1528 &type3.sessionkey); 1529 if (ret) { 1530 if (type3.lm.data) 1531 free(type3.lm.data); 1532 if (type3.ntlm.data) 1533 free(type3.ntlm.data); 1534 goto error; 1535 } 1536 1537 free(sessionkey.data); 1538 if (ret) { 1539 if (type3.lm.data) 1540 free(type3.lm.data); 1541 if (type3.ntlm.data) 1542 free(type3.ntlm.data); 1543 goto error; 1544 } 1545 flags |= NTLM_FLAG_SESSIONKEY; 1546 #if 0 1547 } else { 1548 struct ntlm_buf sessionkey; 1549 unsigned char ntlmv2[16]; 1550 struct ntlm_targetinfo ti; 1551 1552 /* verify infotarget */ 1553 1554 ret = heim_ntlm_decode_targetinfo(&type2.targetinfo, 1, &ti); 1555 if(ret) { 1556 _gss_ntlm_delete_sec_context(minor_status, 1557 context_handle, NULL); 1558 *minor_status = ret; 1559 return GSS_S_FAILURE; 1560 } 1561 1562 if (ti.domainname && strcmp(ti.domainname, name->domain) != 0) { 1563 _gss_ntlm_delete_sec_context(minor_status, 1564 context_handle, NULL); 1565 *minor_status = EINVAL; 1566 return GSS_S_FAILURE; 1567 } 1568 1569 ret = heim_ntlm_calculate_ntlm2(ctx->client->key.data, 1570 ctx->client->key.length, 1571 type3.username, 1572 name->domain, 1573 type2.challenge, 1574 &type2.targetinfo, 1575 ntlmv2, 1576 &type3.ntlm); 1577 if (ret) { 1578 _gss_ntlm_delete_sec_context(minor_status, 1579 context_handle, NULL); 1580 *minor_status = ret; 1581 return GSS_S_FAILURE; 1582 } 1583 1584 ret = heim_ntlm_build_ntlm1_master(ntlmv2, sizeof(ntlmv2), 1585 &sessionkey, 1586 &type3.sessionkey); 1587 memset(ntlmv2, 0, sizeof(ntlmv2)); 1588 if (ret) { 1589 _gss_ntlm_delete_sec_context(minor_status, 1590 context_handle, NULL); 1591 *minor_status = ret; 1592 return GSS_S_FAILURE; 1593 } 1594 1595 flags |= NTLM_FLAG_NTLM2_SESSION | 1596 NTLM_FLAG_SESSION; 1597 1598 if (type3.flags & NTLM_NEG_KEYEX) 1599 flags |= NTLM_FLAG_KEYEX; 1600 1601 ret = krb5_data_copy(&ctx->sessionkey, 1602 sessionkey.data, sessionkey.length); 1603 free(sessionkey.data); 1604 if (ret) { 1605 _gss_ntlm_delete_sec_context(minor_status, 1606 context_handle, NULL); 1607 *minor_status = ret; 1608 return GSS_S_FAILURE; 1609 } 1610 #endif 1611 } 1612 1613 #if 0 1614 if (flags & NTLM_FLAG_NTLM2_SESSION) { 1615 _gss_ntlm_set_key(&ctx->u.v2.send, 0, (ctx->flags & NTLM_NEG_KEYEX), 1616 ctx->sessionkey.data, 1617 ctx->sessionkey.length); 1618 _gss_ntlm_set_key(&ctx->u.v2.recv, 1, (ctx->flags & NTLM_NEG_KEYEX), 1619 ctx->sessionkey.data, 1620 ctx->sessionkey.length); 1621 } else { 1622 flags |= NTLM_FLAG_SESSION; 1623 RC4_set_key(&ctx->u.v1.crypto_recv.key, 1624 ctx->sessionkey.length, 1625 ctx->sessionkey.data); 1626 RC4_set_key(&ctx->u.v1.crypto_send.key, 1627 ctx->sessionkey.length, 1628 ctx->sessionkey.data); 1629 } 1630 #endif 1631 1632 ret = heim_ntlm_encode_type3(&type3, &ndata); 1633 if (ret) 1634 goto error; 1635 1636 data.data = ndata.data; 1637 data.length = ndata.length; 1638 ret = krb5_store_data(response, data); 1639 heim_ntlm_free_buf(&ndata); 1640 if (ret) goto error; 1641 1642 ret = krb5_store_int32(response, flags); 1643 if (ret) goto error; 1644 1645 data.data = sessionkey.data; 1646 data.length = sessionkey.length; 1647 1648 ret = krb5_store_data(response, data); 1649 if (ret) goto error; 1650 1651 error: 1652 free(type3.username); 1653 heim_ntlm_free_type2(&type2); 1654 free(user); 1655 if (domain) 1656 free(domain); 1657 1658 return ret; 1659 } 1660 1661 1662 /* 1663 * { "GET_NTLM_UUID_LIST", NULL } 1664 * 1665 * reply: 1666 * 1 user domain 1667 * 0 [ end of list ] 1668 */ 1669 1670 static krb5_error_code 1671 kcm_op_get_ntlm_user_list(krb5_context context, 1672 kcm_client *client, 1673 kcm_operation opcode, 1674 krb5_storage *request, 1675 krb5_storage *response) 1676 { 1677 struct kcm_ntlm_cred *c; 1678 krb5_error_code ret; 1679 1680 for (c = ntlm_head; c != NULL; c = c->next) { 1681 if (!kcm_is_same_session(client, c->uid, c->session)) 1682 continue; 1683 1684 ret = krb5_store_uint32(response, 1); 1685 if (ret) 1686 return ret; 1687 ret = krb5_store_stringz(response, c->user); 1688 if (ret) 1689 return ret; 1690 ret = krb5_store_stringz(response, c->domain); 1691 if (ret) 1692 return ret; 1693 } 1694 return krb5_store_uint32(response, 0); 1695 } 1696 1697 /* 1698 * 1699 */ 1700 1701 static struct kcm_op kcm_ops[] = { 1702 { "NOOP", kcm_op_noop }, 1703 { "GET_NAME", kcm_op_get_name }, 1704 { "RESOLVE", kcm_op_noop }, 1705 { "GEN_NEW", kcm_op_gen_new }, 1706 { "INITIALIZE", kcm_op_initialize }, 1707 { "DESTROY", kcm_op_destroy }, 1708 { "STORE", kcm_op_store }, 1709 { "RETRIEVE", kcm_op_retrieve }, 1710 { "GET_PRINCIPAL", kcm_op_get_principal }, 1711 { "GET_CRED_UUID_LIST", kcm_op_get_cred_uuid_list }, 1712 { "GET_CRED_BY_UUID", kcm_op_get_cred_by_uuid }, 1713 { "REMOVE_CRED", kcm_op_remove_cred }, 1714 { "SET_FLAGS", kcm_op_set_flags }, 1715 { "CHOWN", kcm_op_chown }, 1716 { "CHMOD", kcm_op_chmod }, 1717 { "GET_INITIAL_TICKET", kcm_op_get_initial_ticket }, 1718 { "GET_TICKET", kcm_op_get_ticket }, 1719 { "MOVE_CACHE", kcm_op_move_cache }, 1720 { "GET_CACHE_UUID_LIST", kcm_op_get_cache_uuid_list }, 1721 { "GET_CACHE_BY_UUID", kcm_op_get_cache_by_uuid }, 1722 { "GET_DEFAULT_CACHE", kcm_op_get_default_cache }, 1723 { "SET_DEFAULT_CACHE", kcm_op_set_default_cache }, 1724 { "GET_KDC_OFFSET", kcm_op_get_kdc_offset }, 1725 { "SET_KDC_OFFSET", kcm_op_set_kdc_offset }, 1726 { "ADD_NTLM_CRED", kcm_op_add_ntlm_cred }, 1727 { "HAVE_USER_CRED", kcm_op_have_ntlm_cred }, 1728 { "DEL_NTLM_CRED", kcm_op_del_ntlm_cred }, 1729 { "DO_NTLM_AUTH", kcm_op_do_ntlm }, 1730 { "GET_NTLM_USER_LIST", kcm_op_get_ntlm_user_list } 1731 }; 1732 1733 1734 const char * 1735 kcm_op2string(kcm_operation opcode) 1736 { 1737 if (opcode >= sizeof(kcm_ops)/sizeof(kcm_ops[0])) 1738 return "Unknown operation"; 1739 1740 return kcm_ops[opcode].name; 1741 } 1742 1743 krb5_error_code 1744 kcm_dispatch(krb5_context context, 1745 kcm_client *client, 1746 krb5_data *req_data, 1747 krb5_data *resp_data) 1748 { 1749 krb5_error_code ret; 1750 kcm_method method; 1751 krb5_storage *req_sp = NULL; 1752 krb5_storage *resp_sp = NULL; 1753 uint16_t opcode; 1754 1755 resp_sp = krb5_storage_emem(); 1756 if (resp_sp == NULL) { 1757 return ENOMEM; 1758 } 1759 1760 if (client->pid == -1) { 1761 kcm_log(0, "Client had invalid process number"); 1762 ret = KRB5_FCC_INTERNAL; 1763 goto out; 1764 } 1765 1766 req_sp = krb5_storage_from_data(req_data); 1767 if (req_sp == NULL) { 1768 kcm_log(0, "Process %d: failed to initialize storage from data", 1769 client->pid); 1770 ret = KRB5_CC_IO; 1771 goto out; 1772 } 1773 1774 ret = krb5_ret_uint16(req_sp, &opcode); 1775 if (ret) { 1776 kcm_log(0, "Process %d: didn't send a message", client->pid); 1777 goto out; 1778 } 1779 1780 if (opcode >= sizeof(kcm_ops)/sizeof(kcm_ops[0])) { 1781 kcm_log(0, "Process %d: invalid operation code %d", 1782 client->pid, opcode); 1783 ret = KRB5_FCC_INTERNAL; 1784 goto out; 1785 } 1786 method = kcm_ops[opcode].method; 1787 if (method == NULL) { 1788 kcm_log(0, "Process %d: operation code %s not implemented", 1789 client->pid, kcm_op2string(opcode)); 1790 ret = KRB5_FCC_INTERNAL; 1791 goto out; 1792 } 1793 1794 /* seek past place for status code */ 1795 krb5_storage_seek(resp_sp, 4, SEEK_SET); 1796 1797 ret = (*method)(context, client, opcode, req_sp, resp_sp); 1798 1799 out: 1800 if (req_sp != NULL) { 1801 krb5_storage_free(req_sp); 1802 } 1803 1804 krb5_storage_seek(resp_sp, 0, SEEK_SET); 1805 krb5_store_int32(resp_sp, ret); 1806 1807 ret = krb5_storage_to_data(resp_sp, resp_data); 1808 krb5_storage_free(resp_sp); 1809 1810 return ret; 1811 } 1812 1813