1 /* 2 * Copyright (c) 2006 Kungliga Tekniska Högskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * 3. Neither the name of KTH nor the names of its contributors may be 18 * used to endorse or promote products derived from this software without 19 * specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY 22 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE 25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 28 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 30 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 31 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 #include "common.h" 35 RCSID("$Id$"); 36 37 /* 38 * 39 */ 40 41 enum handle_type { handle_context, handle_cred }; 42 43 struct handle { 44 int32_t idx; 45 enum handle_type type; 46 void *ptr; 47 struct handle *next; 48 }; 49 50 struct client { 51 krb5_storage *sock; 52 krb5_storage *logging; 53 char *moniker; 54 int32_t nHandle; 55 struct handle *handles; 56 struct sockaddr_storage sa; 57 socklen_t salen; 58 char servername[MAXHOSTNAMELEN]; 59 }; 60 61 FILE *logfile; 62 static char *targetname; 63 krb5_context context; 64 65 /* 66 * 67 */ 68 69 static void 70 logmessage(struct client *c, const char *file, unsigned int lineno, 71 int level, const char *fmt, ...) 72 { 73 char *message; 74 va_list ap; 75 int32_t ackid; 76 77 va_start(ap, fmt); 78 vasprintf(&message, fmt, ap); 79 va_end(ap); 80 81 if (logfile) 82 fprintf(logfile, "%s:%u: %d %s\n", file, lineno, level, message); 83 84 if (c->logging) { 85 if (krb5_store_int32(c->logging, eLogInfo) != 0) 86 errx(1, "krb5_store_int32: log level"); 87 if (krb5_store_string(c->logging, file) != 0) 88 errx(1, "krb5_store_string: filename"); 89 if (krb5_store_int32(c->logging, lineno) != 0) 90 errx(1, "krb5_store_string: filename"); 91 if (krb5_store_string(c->logging, message) != 0) 92 errx(1, "krb5_store_string: message"); 93 if (krb5_ret_int32(c->logging, &ackid) != 0) 94 errx(1, "krb5_ret_int32: ackid"); 95 } 96 free(message); 97 } 98 99 /* 100 * 101 */ 102 103 static int32_t 104 add_handle(struct client *c, enum handle_type type, void *data) 105 { 106 struct handle *h; 107 108 h = ecalloc(1, sizeof(*h)); 109 110 h->idx = ++c->nHandle; 111 h->type = type; 112 h->ptr = data; 113 h->next = c->handles; 114 c->handles = h; 115 116 return h->idx; 117 } 118 119 static void 120 del_handle(struct handle **h, int32_t idx) 121 { 122 OM_uint32 min_stat; 123 124 if (idx == 0) 125 return; 126 127 while (*h) { 128 if ((*h)->idx == idx) { 129 struct handle *p = *h; 130 *h = (*h)->next; 131 switch(p->type) { 132 case handle_context: { 133 gss_ctx_id_t c = p->ptr; 134 gss_delete_sec_context(&min_stat, &c, NULL); 135 break; } 136 case handle_cred: { 137 gss_cred_id_t c = p->ptr; 138 gss_release_cred(&min_stat, &c); 139 break; } 140 } 141 free(p); 142 return; 143 } 144 h = &((*h)->next); 145 } 146 errx(1, "tried to delete an unexisting handle"); 147 } 148 149 static void * 150 find_handle(struct handle *h, int32_t idx, enum handle_type type) 151 { 152 if (idx == 0) 153 return NULL; 154 155 while (h) { 156 if (h->idx == idx) { 157 if (type == h->type) 158 return h->ptr; 159 errx(1, "monger switched type on handle!"); 160 } 161 h = h->next; 162 } 163 return NULL; 164 } 165 166 167 static int32_t 168 convert_gss_to_gsm(OM_uint32 maj_stat) 169 { 170 switch(maj_stat) { 171 case 0: 172 return GSMERR_OK; 173 case GSS_S_CONTINUE_NEEDED: 174 return GSMERR_CONTINUE_NEEDED; 175 case GSS_S_DEFECTIVE_TOKEN: 176 return GSMERR_INVALID_TOKEN; 177 case GSS_S_BAD_MIC: 178 return GSMERR_AP_MODIFIED; 179 default: 180 return GSMERR_ERROR; 181 } 182 } 183 184 static int32_t 185 convert_krb5_to_gsm(krb5_error_code ret) 186 { 187 switch(ret) { 188 case 0: 189 return GSMERR_OK; 190 default: 191 return GSMERR_ERROR; 192 } 193 } 194 195 /* 196 * 197 */ 198 199 static int32_t 200 acquire_cred(struct client *c, 201 krb5_principal principal, 202 krb5_get_init_creds_opt *opt, 203 int32_t *handle) 204 { 205 krb5_error_code ret; 206 krb5_creds cred; 207 krb5_ccache id; 208 gss_cred_id_t gcred; 209 OM_uint32 maj_stat, min_stat; 210 211 *handle = 0; 212 213 krb5_get_init_creds_opt_set_forwardable (opt, 1); 214 krb5_get_init_creds_opt_set_renew_life (opt, 3600 * 24 * 30); 215 216 memset(&cred, 0, sizeof(cred)); 217 218 ret = krb5_get_init_creds_password (context, 219 &cred, 220 principal, 221 NULL, 222 NULL, 223 NULL, 224 0, 225 NULL, 226 opt); 227 if (ret) { 228 logmessage(c, __FILE__, __LINE__, 0, 229 "krb5_get_init_creds failed: %d", ret); 230 return convert_krb5_to_gsm(ret); 231 } 232 233 ret = krb5_cc_new_unique(context, "MEMORY", NULL, &id); 234 if (ret) 235 krb5_err (context, 1, ret, "krb5_cc_initialize"); 236 237 ret = krb5_cc_initialize (context, id, cred.client); 238 if (ret) 239 krb5_err (context, 1, ret, "krb5_cc_initialize"); 240 241 ret = krb5_cc_store_cred (context, id, &cred); 242 if (ret) 243 krb5_err (context, 1, ret, "krb5_cc_store_cred"); 244 245 krb5_free_cred_contents (context, &cred); 246 247 maj_stat = gss_krb5_import_cred(&min_stat, 248 id, 249 NULL, 250 NULL, 251 &gcred); 252 krb5_cc_close(context, id); 253 if (maj_stat) { 254 logmessage(c, __FILE__, __LINE__, 0, 255 "krb5 import creds failed with: %d", maj_stat); 256 return convert_gss_to_gsm(maj_stat); 257 } 258 259 *handle = add_handle(c, handle_cred, gcred); 260 261 return 0; 262 } 263 264 265 /* 266 * 267 */ 268 269 #define HandleOP(h) \ 270 handle##h(enum gssMaggotOp op, struct client *c) 271 272 /* 273 * 274 */ 275 276 static int 277 HandleOP(GetVersionInfo) 278 { 279 put32(c, GSSMAGGOTPROTOCOL); 280 errx(1, "GetVersionInfo"); 281 } 282 283 static int 284 HandleOP(GoodBye) 285 { 286 struct handle *h = c->handles; 287 unsigned int i = 0; 288 289 while (h) { 290 h = h->next; 291 i++; 292 } 293 294 if (i) 295 logmessage(c, __FILE__, __LINE__, 0, 296 "Did not toast all resources: %d", i); 297 return 1; 298 } 299 300 static int 301 HandleOP(InitContext) 302 { 303 OM_uint32 maj_stat, min_stat, ret_flags; 304 int32_t hContext, hCred, flags; 305 krb5_data target_name, in_token; 306 int32_t new_context_id = 0, gsm_error = 0; 307 krb5_data out_token = { 0 , NULL }; 308 309 gss_ctx_id_t ctx; 310 gss_cred_id_t creds; 311 gss_name_t gss_target_name; 312 gss_buffer_desc input_token, output_token; 313 gss_OID oid = GSS_C_NO_OID; 314 gss_buffer_t input_token_ptr = GSS_C_NO_BUFFER; 315 316 ret32(c, hContext); 317 ret32(c, hCred); 318 ret32(c, flags); 319 retdata(c, target_name); 320 retdata(c, in_token); 321 322 logmessage(c, __FILE__, __LINE__, 0, 323 "targetname: <%.*s>", (int)target_name.length, 324 (char *)target_name.data); 325 326 ctx = find_handle(c->handles, hContext, handle_context); 327 if (ctx == NULL) 328 hContext = 0; 329 creds = find_handle(c->handles, hCred, handle_cred); 330 if (creds == NULL) 331 abort(); 332 333 input_token.length = target_name.length; 334 input_token.value = target_name.data; 335 336 maj_stat = gss_import_name(&min_stat, 337 &input_token, 338 GSS_KRB5_NT_PRINCIPAL_NAME, 339 &gss_target_name); 340 if (GSS_ERROR(maj_stat)) { 341 logmessage(c, __FILE__, __LINE__, 0, 342 "import name creds failed with: %d", maj_stat); 343 gsm_error = convert_gss_to_gsm(maj_stat); 344 goto out; 345 } 346 347 /* oid from flags */ 348 349 if (in_token.length) { 350 input_token.length = in_token.length; 351 input_token.value = in_token.data; 352 input_token_ptr = &input_token; 353 if (ctx == NULL) 354 krb5_errx(context, 1, "initcreds, context NULL, but not first req"); 355 } else { 356 input_token.length = 0; 357 input_token.value = NULL; 358 if (ctx) 359 krb5_errx(context, 1, "initcreds, context not NULL, but first req"); 360 } 361 362 if ((flags & GSS_C_DELEG_FLAG) != 0) 363 logmessage(c, __FILE__, __LINE__, 0, "init_sec_context delegating"); 364 if ((flags & GSS_C_DCE_STYLE) != 0) 365 logmessage(c, __FILE__, __LINE__, 0, "init_sec_context dce-style"); 366 367 maj_stat = gss_init_sec_context(&min_stat, 368 creds, 369 &ctx, 370 gss_target_name, 371 oid, 372 flags & 0x7f, 373 0, 374 NULL, 375 input_token_ptr, 376 NULL, 377 &output_token, 378 &ret_flags, 379 NULL); 380 if (GSS_ERROR(maj_stat)) { 381 if (hContext != 0) 382 del_handle(&c->handles, hContext); 383 new_context_id = 0; 384 logmessage(c, __FILE__, __LINE__, 0, 385 "gss_init_sec_context returns code: %d/%d", 386 maj_stat, min_stat); 387 } else { 388 if (input_token.length == 0) 389 new_context_id = add_handle(c, handle_context, ctx); 390 else 391 new_context_id = hContext; 392 } 393 394 gsm_error = convert_gss_to_gsm(maj_stat); 395 396 if (output_token.length) { 397 out_token.data = output_token.value; 398 out_token.length = output_token.length; 399 } 400 401 out: 402 logmessage(c, __FILE__, __LINE__, 0, 403 "InitContext return code: %d", gsm_error); 404 405 put32(c, new_context_id); 406 put32(c, gsm_error); 407 putdata(c, out_token); 408 409 gss_release_name(&min_stat, &gss_target_name); 410 if (output_token.length) 411 gss_release_buffer(&min_stat, &output_token); 412 krb5_data_free(&in_token); 413 krb5_data_free(&target_name); 414 415 return 0; 416 } 417 418 static int 419 HandleOP(AcceptContext) 420 { 421 OM_uint32 maj_stat, min_stat, ret_flags; 422 int32_t hContext, deleg_hcred, flags; 423 krb5_data in_token; 424 int32_t new_context_id = 0, gsm_error = 0; 425 krb5_data out_token = { 0 , NULL }; 426 427 gss_ctx_id_t ctx; 428 gss_cred_id_t deleg_cred = GSS_C_NO_CREDENTIAL; 429 gss_buffer_desc input_token, output_token; 430 gss_buffer_t input_token_ptr = GSS_C_NO_BUFFER; 431 432 ret32(c, hContext); 433 ret32(c, flags); 434 retdata(c, in_token); 435 436 ctx = find_handle(c->handles, hContext, handle_context); 437 if (ctx == NULL) 438 hContext = 0; 439 440 if (in_token.length) { 441 input_token.length = in_token.length; 442 input_token.value = in_token.data; 443 input_token_ptr = &input_token; 444 } else { 445 input_token.length = 0; 446 input_token.value = NULL; 447 } 448 449 maj_stat = gss_accept_sec_context(&min_stat, 450 &ctx, 451 GSS_C_NO_CREDENTIAL, 452 &input_token, 453 GSS_C_NO_CHANNEL_BINDINGS, 454 NULL, 455 NULL, 456 &output_token, 457 &ret_flags, 458 NULL, 459 &deleg_cred); 460 if (GSS_ERROR(maj_stat)) { 461 if (hContext != 0) 462 del_handle(&c->handles, hContext); 463 logmessage(c, __FILE__, __LINE__, 0, 464 "gss_accept_sec_context returns code: %d/%d", 465 maj_stat, min_stat); 466 new_context_id = 0; 467 } else { 468 if (hContext == 0) 469 new_context_id = add_handle(c, handle_context, ctx); 470 else 471 new_context_id = hContext; 472 } 473 if (output_token.length) { 474 out_token.data = output_token.value; 475 out_token.length = output_token.length; 476 } 477 if ((ret_flags & GSS_C_DCE_STYLE) != 0) 478 logmessage(c, __FILE__, __LINE__, 0, "accept_sec_context dce-style"); 479 if ((ret_flags & GSS_C_DELEG_FLAG) != 0) { 480 deleg_hcred = add_handle(c, handle_cred, deleg_cred); 481 logmessage(c, __FILE__, __LINE__, 0, 482 "accept_context delegated handle: %d", deleg_hcred); 483 } else { 484 gss_release_cred(&min_stat, &deleg_cred); 485 deleg_hcred = 0; 486 } 487 488 489 gsm_error = convert_gss_to_gsm(maj_stat); 490 491 put32(c, new_context_id); 492 put32(c, gsm_error); 493 putdata(c, out_token); 494 put32(c, deleg_hcred); 495 496 if (output_token.length) 497 gss_release_buffer(&min_stat, &output_token); 498 krb5_data_free(&in_token); 499 500 return 0; 501 } 502 503 static int 504 HandleOP(ToastResource) 505 { 506 int32_t handle; 507 508 ret32(c, handle); 509 logmessage(c, __FILE__, __LINE__, 0, "toasting %d", handle); 510 del_handle(&c->handles, handle); 511 put32(c, GSMERR_OK); 512 513 return 0; 514 } 515 516 static int 517 HandleOP(AcquireCreds) 518 { 519 char *name, *password; 520 int32_t gsm_error, flags, handle = 0; 521 krb5_principal principal = NULL; 522 krb5_get_init_creds_opt *opt = NULL; 523 krb5_error_code ret; 524 525 retstring(c, name); 526 retstring(c, password); 527 ret32(c, flags); 528 529 logmessage(c, __FILE__, __LINE__, 0, 530 "username: %s password: %s", name, password); 531 532 ret = krb5_parse_name(context, name, &principal); 533 if (ret) { 534 gsm_error = convert_krb5_to_gsm(ret); 535 goto out; 536 } 537 538 ret = krb5_get_init_creds_opt_alloc (context, &opt); 539 if (ret) 540 krb5_err(context, 1, ret, "krb5_get_init_creds_opt_alloc"); 541 542 krb5_get_init_creds_opt_set_pa_password(context, opt, password, NULL); 543 544 gsm_error = acquire_cred(c, principal, opt, &handle); 545 546 out: 547 logmessage(c, __FILE__, __LINE__, 0, 548 "AcquireCreds handle: %d return code: %d", handle, gsm_error); 549 550 if (opt) 551 krb5_get_init_creds_opt_free (context, opt); 552 if (principal) 553 krb5_free_principal(context, principal); 554 free(name); 555 free(password); 556 557 put32(c, gsm_error); 558 put32(c, handle); 559 560 return 0; 561 } 562 563 static int 564 HandleOP(Sign) 565 { 566 OM_uint32 maj_stat, min_stat; 567 int32_t hContext, flags, seqno; 568 krb5_data token; 569 gss_ctx_id_t ctx; 570 gss_buffer_desc input_token, output_token; 571 572 ret32(c, hContext); 573 ret32(c, flags); 574 ret32(c, seqno); 575 retdata(c, token); 576 577 ctx = find_handle(c->handles, hContext, handle_context); 578 if (ctx == NULL) 579 errx(1, "sign: reference to unknown context"); 580 581 input_token.length = token.length; 582 input_token.value = token.data; 583 584 maj_stat = gss_get_mic(&min_stat, ctx, 0, &input_token, 585 &output_token); 586 if (maj_stat != GSS_S_COMPLETE) 587 errx(1, "gss_get_mic failed"); 588 589 krb5_data_free(&token); 590 591 token.data = output_token.value; 592 token.length = output_token.length; 593 594 put32(c, 0); /* XXX fix gsm_error */ 595 putdata(c, token); 596 597 gss_release_buffer(&min_stat, &output_token); 598 599 return 0; 600 } 601 602 static int 603 HandleOP(Verify) 604 { 605 OM_uint32 maj_stat, min_stat; 606 int32_t hContext, flags, seqno; 607 krb5_data msg, mic; 608 gss_ctx_id_t ctx; 609 gss_buffer_desc msg_token, mic_token; 610 gss_qop_t qop; 611 612 ret32(c, hContext); 613 614 ctx = find_handle(c->handles, hContext, handle_context); 615 if (ctx == NULL) 616 errx(1, "verify: reference to unknown context"); 617 618 ret32(c, flags); 619 ret32(c, seqno); 620 retdata(c, msg); 621 622 msg_token.length = msg.length; 623 msg_token.value = msg.data; 624 625 retdata(c, mic); 626 627 mic_token.length = mic.length; 628 mic_token.value = mic.data; 629 630 maj_stat = gss_verify_mic(&min_stat, ctx, &msg_token, 631 &mic_token, &qop); 632 if (maj_stat != GSS_S_COMPLETE) 633 errx(1, "gss_verify_mic failed"); 634 635 krb5_data_free(&mic); 636 krb5_data_free(&msg); 637 638 put32(c, 0); /* XXX fix gsm_error */ 639 640 return 0; 641 } 642 643 static int 644 HandleOP(GetVersionAndCapabilities) 645 { 646 int32_t cap = HAS_MONIKER; 647 char name[256] = "unknown", *str; 648 649 if (targetname) 650 cap |= ISSERVER; /* is server */ 651 652 #ifdef HAVE_UNAME 653 { 654 struct utsname ut; 655 if (uname(&ut) == 0) { 656 snprintf(name, sizeof(name), "%s-%s-%s", 657 ut.sysname, ut.version, ut.machine); 658 } 659 } 660 #endif 661 662 asprintf(&str, "gssmask %s %s", PACKAGE_STRING, name); 663 664 put32(c, GSSMAGGOTPROTOCOL); 665 put32(c, cap); 666 putstring(c, str); 667 free(str); 668 669 return 0; 670 } 671 672 static int 673 HandleOP(GetTargetName) 674 { 675 if (targetname) 676 putstring(c, targetname); 677 else 678 putstring(c, ""); 679 return 0; 680 } 681 682 static int 683 HandleOP(SetLoggingSocket) 684 { 685 int32_t portnum; 686 int fd, ret; 687 688 ret32(c, portnum); 689 690 logmessage(c, __FILE__, __LINE__, 0, 691 "logging port on peer is: %d", (int)portnum); 692 693 socket_set_port((struct sockaddr *)(&c->sa), htons(portnum)); 694 695 fd = socket(((struct sockaddr *)&c->sa)->sa_family, SOCK_STREAM, 0); 696 if (fd < 0) 697 return 0; 698 699 ret = connect(fd, (struct sockaddr *)&c->sa, c->salen); 700 if (ret < 0) { 701 logmessage(c, __FILE__, __LINE__, 0, "failed connect to log port: %s", 702 strerror(errno)); 703 close(fd); 704 return 0; 705 } 706 707 if (c->logging) 708 krb5_storage_free(c->logging); 709 c->logging = krb5_storage_from_fd(fd); 710 close(fd); 711 712 krb5_store_int32(c->logging, eLogSetMoniker); 713 store_string(c->logging, c->moniker); 714 715 logmessage(c, __FILE__, __LINE__, 0, "logging turned on"); 716 717 return 0; 718 } 719 720 721 static int 722 HandleOP(ChangePassword) 723 { 724 errx(1, "ChangePassword"); 725 } 726 727 static int 728 HandleOP(SetPasswordSelf) 729 { 730 errx(1, "SetPasswordSelf"); 731 } 732 733 static int 734 HandleOP(Wrap) 735 { 736 OM_uint32 maj_stat, min_stat; 737 int32_t hContext, flags, seqno; 738 krb5_data token; 739 gss_ctx_id_t ctx; 740 gss_buffer_desc input_token, output_token; 741 int conf_state; 742 743 ret32(c, hContext); 744 ret32(c, flags); 745 ret32(c, seqno); 746 retdata(c, token); 747 748 ctx = find_handle(c->handles, hContext, handle_context); 749 if (ctx == NULL) 750 errx(1, "wrap: reference to unknown context"); 751 752 input_token.length = token.length; 753 input_token.value = token.data; 754 755 maj_stat = gss_wrap(&min_stat, ctx, flags, 0, &input_token, 756 &conf_state, &output_token); 757 if (maj_stat != GSS_S_COMPLETE) 758 errx(1, "gss_wrap failed"); 759 760 krb5_data_free(&token); 761 762 token.data = output_token.value; 763 token.length = output_token.length; 764 765 put32(c, 0); /* XXX fix gsm_error */ 766 putdata(c, token); 767 768 gss_release_buffer(&min_stat, &output_token); 769 770 return 0; 771 } 772 773 774 static int 775 HandleOP(Unwrap) 776 { 777 OM_uint32 maj_stat, min_stat; 778 int32_t hContext, flags, seqno; 779 krb5_data token; 780 gss_ctx_id_t ctx; 781 gss_buffer_desc input_token, output_token; 782 int conf_state; 783 gss_qop_t qop_state; 784 785 ret32(c, hContext); 786 ret32(c, flags); 787 ret32(c, seqno); 788 retdata(c, token); 789 790 ctx = find_handle(c->handles, hContext, handle_context); 791 if (ctx == NULL) 792 errx(1, "unwrap: reference to unknown context"); 793 794 input_token.length = token.length; 795 input_token.value = token.data; 796 797 maj_stat = gss_unwrap(&min_stat, ctx, &input_token, 798 &output_token, &conf_state, &qop_state); 799 800 if (maj_stat != GSS_S_COMPLETE) 801 errx(1, "gss_unwrap failed: %d/%d", maj_stat, min_stat); 802 803 krb5_data_free(&token); 804 if (maj_stat == GSS_S_COMPLETE) { 805 token.data = output_token.value; 806 token.length = output_token.length; 807 } else { 808 token.data = NULL; 809 token.length = 0; 810 } 811 put32(c, 0); /* XXX fix gsm_error */ 812 putdata(c, token); 813 814 if (maj_stat == GSS_S_COMPLETE) 815 gss_release_buffer(&min_stat, &output_token); 816 817 return 0; 818 } 819 820 static int 821 HandleOP(Encrypt) 822 { 823 return handleWrap(op, c); 824 } 825 826 static int 827 HandleOP(Decrypt) 828 { 829 return handleUnwrap(op, c); 830 } 831 832 static int 833 HandleOP(ConnectLoggingService2) 834 { 835 errx(1, "ConnectLoggingService2"); 836 } 837 838 static int 839 HandleOP(GetMoniker) 840 { 841 putstring(c, c->moniker); 842 return 0; 843 } 844 845 static int 846 HandleOP(CallExtension) 847 { 848 errx(1, "CallExtension"); 849 } 850 851 static int 852 HandleOP(AcquirePKInitCreds) 853 { 854 int32_t flags; 855 krb5_data pfxdata; 856 char fn[] = "FILE:/tmp/pkcs12-creds-XXXXXXX"; 857 krb5_principal principal = NULL; 858 int fd; 859 860 ret32(c, flags); 861 retdata(c, pfxdata); 862 863 fd = mkstemp(fn + 5); 864 if (fd < 0) 865 errx(1, "mkstemp"); 866 867 net_write(fd, pfxdata.data, pfxdata.length); 868 krb5_data_free(&pfxdata); 869 close(fd); 870 871 if (principal) 872 krb5_free_principal(context, principal); 873 874 put32(c, -1); /* hResource */ 875 put32(c, GSMERR_NOT_SUPPORTED); 876 return 0; 877 } 878 879 static int 880 HandleOP(WrapExt) 881 { 882 OM_uint32 maj_stat, min_stat; 883 int32_t hContext, flags, bflags; 884 krb5_data token, header, trailer; 885 gss_ctx_id_t ctx; 886 unsigned char *p; 887 int conf_state, iov_len; 888 gss_iov_buffer_desc iov[6]; 889 890 ret32(c, hContext); 891 ret32(c, flags); 892 ret32(c, bflags); 893 retdata(c, header); 894 retdata(c, token); 895 retdata(c, trailer); 896 897 ctx = find_handle(c->handles, hContext, handle_context); 898 if (ctx == NULL) 899 errx(1, "wrap: reference to unknown context"); 900 901 memset(&iov, 0, sizeof(iov)); 902 903 iov_len = sizeof(iov)/sizeof(iov[0]); 904 905 if (bflags & WRAP_EXP_ONLY_HEADER) 906 iov_len -= 2; /* skip trailer and padding, aka dce-style */ 907 908 iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER | GSS_IOV_BUFFER_TYPE_FLAG_ALLOCATE; 909 if (header.length != 0) { 910 iov[1].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY; 911 iov[1].buffer.length = header.length; 912 iov[1].buffer.value = header.data; 913 } else { 914 iov[1].type = GSS_IOV_BUFFER_TYPE_EMPTY; 915 } 916 iov[2].type = GSS_IOV_BUFFER_TYPE_DATA; 917 iov[2].buffer.length = token.length; 918 iov[2].buffer.value = token.data; 919 if (trailer.length != 0) { 920 iov[3].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY; 921 iov[3].buffer.length = trailer.length; 922 iov[3].buffer.value = trailer.data; 923 } else { 924 iov[3].type = GSS_IOV_BUFFER_TYPE_EMPTY; 925 } 926 iov[4].type = GSS_IOV_BUFFER_TYPE_PADDING | GSS_IOV_BUFFER_TYPE_FLAG_ALLOCATE; 927 iov[5].type = GSS_IOV_BUFFER_TYPE_TRAILER | GSS_IOV_BUFFER_TYPE_FLAG_ALLOCATE; 928 929 maj_stat = gss_wrap_iov_length(&min_stat, ctx, flags, 0, &conf_state, 930 iov, iov_len); 931 if (maj_stat != GSS_S_COMPLETE) 932 errx(1, "gss_wrap_iov_length failed"); 933 934 maj_stat = gss_wrap_iov(&min_stat, ctx, flags, 0, &conf_state, 935 iov, iov_len); 936 if (maj_stat != GSS_S_COMPLETE) 937 errx(1, "gss_wrap_iov failed"); 938 939 krb5_data_free(&token); 940 941 token.length = iov[0].buffer.length + iov[2].buffer.length + iov[4].buffer.length + iov[5].buffer.length; 942 token.data = malloc(token.length); 943 944 p = token.data; 945 memcpy(p, iov[0].buffer.value, iov[0].buffer.length); 946 p += iov[0].buffer.length; 947 memcpy(p, iov[2].buffer.value, iov[2].buffer.length); 948 p += iov[2].buffer.length; 949 memcpy(p, iov[4].buffer.value, iov[4].buffer.length); 950 p += iov[4].buffer.length; 951 memcpy(p, iov[5].buffer.value, iov[5].buffer.length); 952 #ifndef __clang_analyzer__ 953 p += iov[5].buffer.length; 954 #endif 955 956 gss_release_iov_buffer(NULL, iov, iov_len); 957 958 put32(c, 0); /* XXX fix gsm_error */ 959 putdata(c, token); 960 961 free(token.data); 962 963 return 0; 964 } 965 966 967 static int 968 HandleOP(UnwrapExt) 969 { 970 OM_uint32 maj_stat, min_stat; 971 int32_t hContext, flags, bflags; 972 krb5_data token, header, trailer; 973 gss_ctx_id_t ctx; 974 gss_iov_buffer_desc iov[3]; 975 int conf_state, iov_len; 976 gss_qop_t qop_state; 977 978 ret32(c, hContext); 979 ret32(c, flags); 980 ret32(c, bflags); 981 retdata(c, header); 982 retdata(c, token); 983 retdata(c, trailer); 984 985 iov_len = sizeof(iov)/sizeof(iov[0]); 986 987 if (bflags & WRAP_EXP_ONLY_HEADER) 988 iov_len -= 1; /* skip trailer and padding, aka dce-style */ 989 990 ctx = find_handle(c->handles, hContext, handle_context); 991 if (ctx == NULL) 992 errx(1, "unwrap: reference to unknown context"); 993 994 if (header.length != 0) { 995 iov[0].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY; 996 iov[0].buffer.length = header.length; 997 iov[0].buffer.value = header.data; 998 } else { 999 iov[0].type = GSS_IOV_BUFFER_TYPE_EMPTY; 1000 } 1001 iov[1].type = GSS_IOV_BUFFER_TYPE_DATA; 1002 iov[1].buffer.length = token.length; 1003 iov[1].buffer.value = token.data; 1004 1005 if (trailer.length != 0) { 1006 iov[2].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY; 1007 iov[2].buffer.length = trailer.length; 1008 iov[2].buffer.value = trailer.data; 1009 } else { 1010 iov[2].type = GSS_IOV_BUFFER_TYPE_EMPTY; 1011 } 1012 1013 maj_stat = gss_unwrap_iov(&min_stat, ctx, &conf_state, &qop_state, 1014 iov, iov_len); 1015 1016 if (maj_stat != GSS_S_COMPLETE) 1017 errx(1, "gss_unwrap failed: %d/%d", maj_stat, min_stat); 1018 1019 if (maj_stat == GSS_S_COMPLETE) { 1020 token.data = iov[1].buffer.value; 1021 token.length = iov[1].buffer.length; 1022 } else { 1023 token.data = NULL; 1024 token.length = 0; 1025 } 1026 put32(c, 0); /* XXX fix gsm_error */ 1027 putdata(c, token); 1028 1029 return 0; 1030 } 1031 1032 /* 1033 * 1034 */ 1035 1036 struct handler { 1037 enum gssMaggotOp op; 1038 const char *name; 1039 int (*func)(enum gssMaggotOp, struct client *); 1040 }; 1041 1042 #define S(a) { e##a, #a, handle##a } 1043 1044 struct handler handlers[] = { 1045 S(GetVersionInfo), 1046 S(GoodBye), 1047 S(InitContext), 1048 S(AcceptContext), 1049 S(ToastResource), 1050 S(AcquireCreds), 1051 S(Encrypt), 1052 S(Decrypt), 1053 S(Sign), 1054 S(Verify), 1055 S(GetVersionAndCapabilities), 1056 S(GetTargetName), 1057 S(SetLoggingSocket), 1058 S(ChangePassword), 1059 S(SetPasswordSelf), 1060 S(Wrap), 1061 S(Unwrap), 1062 S(ConnectLoggingService2), 1063 S(GetMoniker), 1064 S(CallExtension), 1065 S(AcquirePKInitCreds), 1066 S(WrapExt), 1067 S(UnwrapExt), 1068 }; 1069 1070 #undef S 1071 1072 /* 1073 * 1074 */ 1075 1076 static struct handler * 1077 find_op(int32_t op) 1078 { 1079 int i; 1080 1081 for (i = 0; i < sizeof(handlers)/sizeof(handlers[0]); i++) 1082 if (handlers[i].op == op) 1083 return &handlers[i]; 1084 return NULL; 1085 } 1086 1087 static struct client * 1088 create_client(int fd, int port, const char *moniker) 1089 { 1090 struct client *c; 1091 1092 c = ecalloc(1, sizeof(*c)); 1093 1094 if (moniker) { 1095 c->moniker = estrdup(moniker); 1096 } else { 1097 char hostname[MAXHOSTNAMELEN]; 1098 gethostname(hostname, sizeof(hostname)); 1099 asprintf(&c->moniker, "gssmask: %s:%d", hostname, port); 1100 } 1101 1102 { 1103 c->salen = sizeof(c->sa); 1104 getpeername(fd, (struct sockaddr *)&c->sa, &c->salen); 1105 1106 getnameinfo((struct sockaddr *)&c->sa, c->salen, 1107 c->servername, sizeof(c->servername), 1108 NULL, 0, NI_NUMERICHOST); 1109 } 1110 1111 c->sock = krb5_storage_from_fd(fd); 1112 if (c->sock == NULL) 1113 errx(1, "krb5_storage_from_fd"); 1114 1115 close(fd); 1116 1117 return c; 1118 } 1119 1120 static void 1121 free_client(struct client *c) 1122 { 1123 while(c->handles) 1124 del_handle(&c->handles, c->handles->idx); 1125 1126 free(c->moniker); 1127 krb5_storage_free(c->sock); 1128 if (c->logging) 1129 krb5_storage_free(c->logging); 1130 free(c); 1131 } 1132 1133 1134 static void * 1135 handleServer(void *ptr) 1136 { 1137 struct handler *handler; 1138 struct client *c; 1139 int32_t op; 1140 1141 c = (struct client *)ptr; 1142 1143 1144 while(1) { 1145 ret32(c, op); 1146 1147 handler = find_op(op); 1148 if (handler == NULL) { 1149 logmessage(c, __FILE__, __LINE__, 0, 1150 "op %d not supported", (int)op); 1151 exit(1); 1152 } 1153 1154 logmessage(c, __FILE__, __LINE__, 0, 1155 "---> Got op %s from server %s", 1156 handler->name, c->servername); 1157 1158 if ((handler->func)(handler->op, c)) 1159 break; 1160 } 1161 1162 return NULL; 1163 } 1164 1165 1166 static char *port_str; 1167 static int version_flag; 1168 static int help_flag; 1169 static char *logfile_str; 1170 static char *moniker_str; 1171 1172 static int port = 4711; 1173 1174 struct getargs args[] = { 1175 { "spn", 0, arg_string, &targetname, "This host's SPN", 1176 "service/host@REALM" }, 1177 { "port", 'p', arg_string, &port_str, "Use this port", 1178 "number-of-service" }, 1179 { "logfile", 0, arg_string, &logfile_str, "logfile", 1180 "number-of-service" }, 1181 { "moniker", 0, arg_string, &moniker_str, "nickname", 1182 "name" }, 1183 { "version", 0, arg_flag, &version_flag, "Print version", 1184 NULL }, 1185 { "help", 0, arg_flag, &help_flag, NULL, 1186 NULL } 1187 }; 1188 1189 static void 1190 usage(int ret) 1191 { 1192 arg_printusage (args, 1193 sizeof(args) / sizeof(args[0]), 1194 NULL, 1195 ""); 1196 exit (ret); 1197 } 1198 1199 int 1200 main(int argc, char **argv) 1201 { 1202 int optidx = 0; 1203 1204 setprogname (argv[0]); 1205 1206 if (getarg (args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx)) 1207 usage (1); 1208 1209 if (help_flag) 1210 usage (0); 1211 1212 if (version_flag) { 1213 print_version (NULL); 1214 return 0; 1215 } 1216 1217 if (optidx != argc) 1218 usage (1); 1219 1220 if (port_str) { 1221 char *ptr; 1222 1223 port = strtol (port_str, &ptr, 10); 1224 if (port == 0 && ptr == port_str) 1225 errx (1, "Bad port `%s'", port_str); 1226 } 1227 1228 krb5_init_context(&context); 1229 1230 { 1231 const char *lf = logfile_str; 1232 if (lf == NULL) 1233 lf = "/dev/tty"; 1234 1235 logfile = fopen(lf, "w"); 1236 if (logfile == NULL) 1237 err(1, "error opening %s", lf); 1238 } 1239 1240 mini_inetd(htons(port), NULL); 1241 fprintf(logfile, "connected\n"); 1242 1243 { 1244 struct client *c; 1245 1246 c = create_client(0, port, moniker_str); 1247 /* close(0); */ 1248 1249 handleServer(c); 1250 1251 free_client(c); 1252 } 1253 1254 krb5_free_context(context); 1255 1256 return 0; 1257 } 1258