1/* 2 * Copyright (c) 2006 Kungliga Tekniska Högskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Portions Copyright (c) 2009 - 2010 Apple Inc. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * 3. Neither the name of KTH nor the names of its contributors may be 20 * used to endorse or promote products derived from this software without 21 * specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY 24 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 26 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE 27 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 28 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 29 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 30 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 31 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 32 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 33 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 */ 35 36#include <config.h> 37 38#include <stdio.h> 39#include <gssapi.h> 40#include <gssapi_krb5.h> 41#include <gssapi_spnego.h> 42#include <gssapi_ntlm.h> 43#include <gssapi_oid.h> 44#include <gssapi_spi.h> 45#include <heim-ipc.h> 46#include <err.h> 47#include <roken.h> 48#include <getarg.h> 49#include <rtbl.h> 50#include <gss-commands.h> 51#include <krb5.h> 52#include <parse_time.h> 53 54#include "crypto-headers.h" 55 56static int version_flag = 0; 57static int help_flag = 0; 58 59static struct getargs args[] = { 60 {"version", 0, arg_flag, &version_flag, "print version", NULL }, 61 {"help", 0, arg_flag, &help_flag, NULL, NULL } 62}; 63 64static void 65usage (int ret) 66{ 67 arg_printusage (args, sizeof(args)/sizeof(*args), 68 NULL, "service@host"); 69 exit (ret); 70} 71 72#define COL_DESC "Description" 73#define COL_ENABLED "Enabled" 74#define COL_EXPIRE "Expire" 75#define COL_MECH "Mech" 76#define COL_NAME "Name" 77#define COL_OID "OID" 78#define COL_OPTION "Option" 79#define COL_SASL "SASL" 80#define COL_VALUE "Value" 81#define COL_UUID "UUID" 82 83int 84supported_mechanisms(struct supported_mechanisms_options *opt, int argc, char **argv) 85{ 86 OM_uint32 maj_stat, min_stat; 87 gss_OID_set mechs; 88 rtbl_t ct; 89 size_t i; 90 91 maj_stat = gss_indicate_mechs(&min_stat, &mechs); 92 if (maj_stat != GSS_S_COMPLETE) 93 errx(1, "gss_indicate_mechs failed"); 94 95 printf("Supported mechanisms:\n"); 96 97 ct = rtbl_create(); 98 if (ct == NULL) 99 errx(1, "rtbl_create"); 100 101 rtbl_set_separator(ct, " "); 102 rtbl_add_column(ct, COL_OID, 0); 103 rtbl_add_column(ct, COL_NAME, 0); 104 if (opt->options_flag) { 105 rtbl_add_column(ct, COL_OPTION, 0); 106 rtbl_add_column(ct, COL_ENABLED, 0); 107 } 108 109 for (i = 0; i < mechs->count; i++) { 110 gss_buffer_desc str; 111 const char *name = NULL; 112 113 maj_stat = gss_oid_to_str(&min_stat, &mechs->elements[i], &str); 114 if (maj_stat != GSS_S_COMPLETE) 115 errx(1, "gss_oid_to_str failed"); 116 117 rtbl_add_column_entryv(ct, COL_OID, "%.*s", 118 (int)str.length, (char *)str.value); 119 gss_release_buffer(&min_stat, &str); 120 121 name = gss_oid_to_name(&mechs->elements[i]); 122 if (name) 123 rtbl_add_column_entry(ct, COL_NAME, name); 124 else 125 rtbl_add_column_entry(ct, COL_NAME, ""); 126 127 if (opt->options_flag) { 128 gss_OID_set options = GSS_C_NO_OID_SET; 129 gss_buffer_desc oname; 130 size_t n; 131 int ena; 132 133 gss_mo_list(&mechs->elements[i], &options); 134 135 if (options == NULL || options->count == 0) { 136 rtbl_add_column_entry(ct, COL_OPTION, ""); 137 rtbl_add_column_entry(ct, COL_ENABLED, ""); 138 } 139 140 for (n = 0; options && n < options->count; n++) { 141 maj_stat = gss_mo_name(&mechs->elements[i], &options->elements[n], &oname); 142 if (maj_stat != GSS_S_COMPLETE) 143 continue; 144 145 if (n != 0) { 146 rtbl_add_column_entry(ct, COL_OID, ""); 147 rtbl_add_column_entry(ct, COL_NAME, ""); 148 } 149 ena = gss_mo_get(&mechs->elements[i], &options->elements[n], NULL); 150 151 rtbl_add_column_entryv(ct, COL_OPTION, "%.*s", (int)oname.length, (char *)oname.value); 152 rtbl_add_column_entry(ct, COL_ENABLED, ena ? "yes" : "no"); 153 gss_release_buffer(&min_stat, &oname); 154 } 155 } 156 } 157 gss_release_oid_set(&min_stat, &mechs); 158 159 rtbl_format(ct, stdout); 160 rtbl_destroy(ct); 161 162 return 0; 163} 164 165int 166acquire_credential(struct acquire_credential_options *opt, int argc, char **argv) 167{ 168 char password[512]; 169 OM_uint32 maj_stat, min_stat; 170 gss_const_OID mech = NULL; 171 gss_OID nametype = GSS_C_NT_USER_NAME; 172 gss_name_t name = GSS_C_NO_NAME; 173 gss_buffer_desc buffer; 174 gss_cred_id_t cred = GSS_C_NO_CREDENTIAL; 175 CFMutableDictionaryRef attributes; 176 CFStringRef pw; 177 CFErrorRef error = NULL; 178 179 /* 180 * mech 181 */ 182 183 attributes = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, 184 &kCFTypeDictionaryKeyCallBacks, 185 &kCFTypeDictionaryValueCallBacks); 186 if (attributes == NULL) 187 errx(1, "out of memory"); 188 189 if (opt->mech_string) { 190 mech = gss_name_to_oid(opt->mech_string); 191 if (mech == NULL) 192 errx(1, "No such mech: %s", opt->mech_string); 193 } else { 194 mech = GSS_KRB5_MECHANISM; 195 } 196 197 /* 198 * user 199 */ 200 201 if (opt->user_string == NULL && argc < 1) 202 errx(1, "no user string"); 203 204 if (opt->user_string) { 205 buffer.value = rk_UNCONST(opt->user_string); 206 buffer.length = strlen(opt->user_string); 207 } else { 208 buffer.value = argv[0]; 209 buffer.length = strlen(argv[0]); 210 } 211 212 maj_stat = gss_import_name(&min_stat, &buffer, nametype, &name); 213 if (maj_stat) 214 errx(1, "failed to import name"); 215 216 /* 217 * password 218 */ 219 220 if (UI_UTIL_read_pw_string(password, sizeof(password), 221 "Password: ", 0) != 0) 222 errx(1, "failed reading password"); 223 224 pw = CFStringCreateWithCString(kCFAllocatorDefault, password, kCFStringEncodingUTF8); 225 CFDictionarySetValue(attributes, kGSSICPassword, pw); 226 CFRelease(pw); 227 228 if (opt->validate_flag) 229 CFDictionarySetValue(attributes, kGSSICVerifyCredential, kCFBooleanTrue); 230 if (opt->kdc_hostname_string) { 231 CFStringRef hn = CFStringCreateWithCString(NULL, opt->kdc_hostname_string, kCFStringEncodingUTF8); 232 if (hn == NULL) 233 errx(1, "CFStringCreateWithCString"); 234 CFDictionarySetValue(attributes, kGSSICLKDCHostname, hn); 235 CFRelease(hn); 236 } 237 238 maj_stat = gss_aapl_initial_cred(name, 239 mech, 240 attributes, 241 &cred, 242 &error); 243 if (maj_stat != GSS_S_COMPLETE) { 244 char *msg = NULL; 245 if (error) { 246 CFStringRef m; 247 m = CFErrorCopyDescription(error); 248 if (m) { 249 msg = rk_cfstring2cstring(m); 250 CFRelease(m); 251 } 252 } 253 errx(1, "gss_aapl_initial_cred: %s: %d", 254 msg ? msg : "", (int)maj_stat); 255 free(msg); 256 } 257 gss_release_cred(&min_stat, &cred); 258 gss_release_name(&min_stat, &name); 259 260 CFRelease(attributes); 261 262 if (error) 263 CFRelease(error); 264 265 return 0; 266} 267 268 269struct print_cred { 270 rtbl_t t; 271}; 272 273static void 274print_cred(void *ctx, gss_const_OID oid, gss_cred_id_t cred) 275{ 276 struct print_cred *pc = ctx; 277 gss_buffer_set_t data_set; 278 OM_uint32 major, junk; 279 gss_buffer_desc buffer; 280 gss_name_t name; 281 const char *str; 282 OM_uint32 expire; 283 284 if (cred == NULL) 285 return; 286 287 major = gss_inquire_cred(&junk, cred, NULL, &expire, NULL, NULL); 288 if (major == GSS_S_CREDENTIALS_EXPIRED) 289 expire = 0; 290 else if (major) 291 goto out; 292 major = gss_inquire_cred_by_mech(&junk, cred, (gss_OID)oid, &name, NULL, NULL, NULL); 293 if (major) goto out; 294 major = gss_display_name(&junk, name, &buffer, NULL); 295 gss_release_name(&junk, &name); 296 if (major) goto out; 297 298 rtbl_add_column_entryv(pc->t, COL_NAME, "%.*s", 299 (int)buffer.length, (char *)buffer.value); 300 301 gss_release_buffer(&junk, &buffer); 302 303 str = gss_oid_to_name(oid); 304 if (str) 305 rtbl_add_column_entry(pc->t, COL_MECH, str); 306 307 if (expire == GSS_C_INDEFINITE) 308 rtbl_add_column_entryv(pc->t, COL_EXPIRE, "never"); 309 else if (expire == 0) 310 rtbl_add_column_entryv(pc->t, COL_EXPIRE, "expired"); 311 else { 312 char life[80]; 313 unparse_time_approx(expire, life, sizeof(life)); 314 rtbl_add_column_entry(pc->t, COL_EXPIRE, life); 315 } 316 317 major = gss_inquire_cred_by_oid(&junk, cred, GSS_C_NT_UUID, &data_set); 318 if (major == GSS_S_COMPLETE && data_set->count == 1) 319 rtbl_add_column_entryv(pc->t, COL_UUID, "%.*s\n", (int)data_set->elements[0].length, (const char *)data_set->elements[0].value); 320 else 321 rtbl_add_column_entry(pc->t, COL_UUID, ""); 322 323 gss_release_buffer_set(&junk, &data_set); 324 325 out: 326 gss_release_cred(&junk, &cred); 327} 328 329static void 330diag_cred(void *ctx, gss_const_OID oid, gss_cred_id_t cred) 331{ 332 const char *delim = "----------------"; 333 gss_buffer_set_t data_set; 334 gss_buffer_desc buffer; 335 OM_uint32 major, junk; 336 gss_name_t name; 337 const char *mech; 338 size_t n; 339 340 if (cred == NULL) 341 return; 342 343 major = gss_inquire_cred_by_mech(&junk, cred, (gss_OID)oid, &name, NULL, NULL, NULL); 344 if (major) 345 return; 346 347 major = gss_display_name(&junk, name, &buffer, NULL); 348 gss_release_name(&junk, &name); 349 if (major) 350 return; 351 352 mech = gss_oid_to_name(oid); 353 354 printf("@GSSCred\n%s\n%s: %.*s\n", 355 delim, 356 mech ? mech : "<unknown-mech>", 357 (int)buffer.length, buffer.value); 358 gss_release_buffer(&junk, &buffer); 359 360 major = gss_inquire_cred_by_oid(&junk, cred, GSS_C_CRED_DIAG, &data_set); 361 if (major) 362 return; 363 364 for (n = 0; n < data_set->count; n++) 365 printf("%s\n%.*s\n", delim, (int)data_set->elements[n].length, data_set->elements[n].value); 366 367 printf("%s\n", delim); 368 369 gss_release_buffer_set(&junk, &data_set); 370 371 major = gss_inquire_cred_by_oid(&junk, cred, GSS_C_NT_UUID, &data_set); 372 if (major == GSS_S_COMPLETE || data_set->count == 1) { 373 printf("UUID: %.*s\n", (int)data_set->elements[0].length, (const char *)data_set->elements[0].value); 374 printf("%s\n", delim); 375 } 376 gss_release_buffer_set(&junk, &data_set); 377 378 379} 380 381 382 383int 384list_credentials(struct list_credentials_options *opt, int argc, char **argv) 385{ 386 struct print_cred pc; 387 gss_const_OID mech = NULL; 388 389 if (opt->mech_string) { 390 mech = gss_name_to_oid(opt->mech_string); 391 if (mech == NULL) 392 errx(1, "No such mech: %s", opt->mech_string); 393 } 394 395 if (opt->verbose_flag) { 396 397 gss_iter_creds_f(NULL, 0, mech, NULL, diag_cred); 398 399 } else { 400 401 pc.t = rtbl_create(); 402 if (pc.t == NULL) 403 errx(1, "rtbl_create"); 404 405 rtbl_set_separator(pc.t, " "); 406 rtbl_add_column(pc.t, COL_NAME, 0); 407 rtbl_add_column(pc.t, COL_EXPIRE, 0); 408 rtbl_add_column(pc.t, COL_MECH, 0); 409 rtbl_add_column(pc.t, COL_UUID, 0); 410 411 gss_iter_creds_f(NULL, 0, mech, &pc, print_cred); 412 413 rtbl_format(pc.t, stdout); 414 rtbl_destroy(pc.t); 415 } 416 417 return 0; 418} 419 420static gss_cred_id_t 421acquire_cred(const char *name_string, gss_const_OID mech, gss_const_OID nametype) 422{ 423 OM_uint32 maj_stat, min_stat; 424 gss_OID_set mechset = NULL; 425 gss_cred_id_t cred = NULL; 426 gss_buffer_desc buffer; 427 gss_name_t name; 428 429 buffer.value = rk_UNCONST(name_string); 430 buffer.length = strlen(name_string); 431 432 maj_stat = gss_import_name(&min_stat, &buffer, nametype, &name); 433 if (maj_stat) 434 errx(1, "failed to import name"); 435 436 if (mech) { 437 gss_create_empty_oid_set(&min_stat, &mechset); 438 gss_add_oid_set_member(&min_stat, mech, &mechset); 439 } 440 441 maj_stat = gss_acquire_cred(&min_stat, name, GSS_C_INDEFINITE, 442 mechset, GSS_C_INITIATE, 443 &cred, NULL, NULL); 444 gss_release_name(&min_stat, &name); 445 gss_release_oid_set(&min_stat, &mechset); 446 if (maj_stat || cred == NULL) 447 errx(1, "acquire_cred failed"); 448 449 return cred; 450} 451 452static void 453destroy_cred(void *arg1, gss_const_OID oid, gss_cred_id_t cred) 454{ 455 gss_destroy_cred(NULL, &cred); 456} 457 458int 459destroy(struct destroy_options *opt, int argc, char **argv) 460{ 461 gss_const_OID mech = NULL; 462 463 if (opt->mech_string) { 464 mech = gss_name_to_oid(opt->mech_string); 465 if (mech == NULL) 466 errx(1, "No such mech: %s", opt->mech_string); 467 } 468 469 if (opt->all_flag) { 470 gss_iter_creds_f(NULL, 0, mech, NULL, destroy_cred); 471 } else { 472 gss_cred_id_t cred; 473 474 if (argc < 1) { 475 printf("%s: missing name\n", getprogname()); 476 return 1; 477 } 478 479 cred = acquire_cred(argv[0], mech, GSS_C_NT_USER_NAME); 480 481 gss_destroy_cred(NULL, &cred); 482 483 } 484 485 return 0; 486} 487 488/* 489 * 490 */ 491 492static int 493common_hold(OM_uint32 (*func)(OM_uint32 *, gss_cred_id_t), 494 const char *mech_string, int argc, char **argv) 495{ 496 OM_uint32 min_stat, maj_stat; 497 gss_const_OID mech = GSS_C_NO_OID; 498 gss_cred_id_t cred; 499 500 if (argc < 1) { 501 printf("missing username to (un)hold\n"); 502 return 1; 503 } 504 505 if (mech_string) { 506 mech = gss_name_to_oid(mech_string); 507 if (mech == NULL) 508 errx(1, "No such mech: %s", mech_string); 509 } 510 511 cred = acquire_cred(argv[0], mech, GSS_C_NT_USER_NAME); 512 513 maj_stat = func(&min_stat, cred); 514 if (maj_stat != GSS_S_COMPLETE) 515 errx(1, "(un)hold failed"); 516 517 gss_release_cred(&min_stat, &cred); 518 519 return 0; 520} 521 522int 523hold(struct hold_options *opt, int argc, char **argv) 524{ 525 return common_hold(gss_cred_hold, opt->mech_string, argc, argv); 526} 527 528int 529unhold(struct unhold_options *opt, int argc, char **argv) 530{ 531 return common_hold(gss_cred_unhold, opt->mech_string, argc, argv); 532} 533 534int 535get_label(struct get_label_options *opt, int argc, char **argv) 536{ 537 OM_uint32 min_stat, maj_stat; 538 gss_const_OID mech = GSS_C_NO_OID; 539 gss_cred_id_t cred; 540 gss_buffer_desc buf; 541 542 if (opt->mech_string) { 543 mech = gss_name_to_oid(opt->mech_string); 544 if (mech == NULL) 545 errx(1, "No such mech: %s", opt->mech_string); 546 } 547 548 cred = acquire_cred(argv[0], mech, GSS_C_NT_USER_NAME); 549 550 maj_stat = gss_cred_label_get(&min_stat, cred, argv[1], &buf); 551 if (maj_stat != GSS_S_COMPLETE) 552 errx(1, "label get failed"); 553 554 printf("value: %.*s\n", (int)buf.length, (char *)buf.value); 555 556 gss_release_buffer(&min_stat, &buf); 557 gss_release_cred(&min_stat, &cred); 558 559 return 0; 560} 561 562int 563set_label(struct set_label_options *opt, int argc, char **argv) 564{ 565 OM_uint32 min_stat, maj_stat; 566 gss_const_OID mech = GSS_C_NO_OID; 567 gss_cred_id_t cred; 568 gss_buffer_desc buf; 569 gss_buffer_t bufp = NULL; 570 571 if (opt->mech_string) { 572 mech = gss_name_to_oid(opt->mech_string); 573 if (mech == NULL) 574 errx(1, "No such mech: %s", opt->mech_string); 575 } 576 577 cred = acquire_cred(argv[0], mech, GSS_C_NT_USER_NAME); 578 579 if (argc > 2) { 580 buf.value = argv[2]; 581 buf.length = strlen(argv[2]); 582 bufp = &buf; 583 } 584 585 maj_stat = gss_cred_label_set(&min_stat, cred, argv[1], bufp); 586 if (maj_stat != GSS_S_COMPLETE) 587 errx(1, "label get failed"); 588 589 gss_release_cred(&min_stat, &cred); 590 591 return 0; 592} 593 594/* 595 * 596 */ 597 598static void 599print_mech_attr(const char *mechname, gss_const_OID mech, gss_OID_set set) 600{ 601 gss_buffer_desc name, desc; 602 OM_uint32 major, minor; 603 rtbl_t ct; 604 size_t n; 605 606 ct = rtbl_create(); 607 if (ct == NULL) 608 errx(1, "rtbl_create"); 609 610 rtbl_set_separator(ct, " "); 611 rtbl_add_column(ct, COL_OID, 0); 612 rtbl_add_column(ct, COL_DESC, 0); 613 if (mech) 614 rtbl_add_column(ct, COL_VALUE, 0); 615 616 for (n = 0; n < set->count; n++) { 617 major = gss_display_mech_attr(&minor, &set->elements[n], &name, &desc, NULL); 618 if (major) 619 continue; 620 621 rtbl_add_column_entryv(ct, COL_OID, "%.*s", 622 (int)name.length, (char *)name.value); 623 rtbl_add_column_entryv(ct, COL_DESC, "%.*s", 624 (int)desc.length, (char *)desc.value); 625 if (mech) { 626 gss_buffer_desc value; 627 628 if (gss_mo_get(mech, &set->elements[n], &value) != 0) 629 value.length = 0; 630 631 if (value.length) 632 rtbl_add_column_entryv(ct, COL_VALUE, "%.*s", 633 (int)value.length, (char *)value.value); 634 else 635 rtbl_add_column_entryv(ct, COL_VALUE, "<>"); 636 gss_release_buffer(&minor, &value); 637 } 638 639 gss_release_buffer(&minor, &name); 640 gss_release_buffer(&minor, &desc); 641 } 642 643 printf("attributes for: %s\n", mechname); 644 rtbl_format(ct, stdout); 645 rtbl_destroy(ct); 646} 647 648int 649attrs_for_mech(struct attrs_for_mech_options *opt, int argc, char **argv) 650{ 651 gss_OID_set mech_attr = NULL, known_mech_attrs = NULL; 652 gss_const_OID mech = GSS_C_NO_OID; 653 OM_uint32 major, minor; 654 655 if (opt->mech_string) { 656 mech = gss_name_to_oid(opt->mech_string); 657 if (mech == NULL) 658 errx(1, "mech %s is unknown", opt->mech_string); 659 } 660 661 major = gss_inquire_attrs_for_mech(&minor, 662 mech, 663 &mech_attr, 664 &known_mech_attrs); 665 if (major) 666 errx(1, "gss_inquire_attrs_for_mech"); 667 668 if (mech) 669 print_mech_attr(opt->mech_string, mech, mech_attr); 670 671 if (opt->all_flag) 672 print_mech_attr("all mechs", NULL, known_mech_attrs); 673 674 gss_release_oid_set(&minor, &mech_attr); 675 gss_release_oid_set(&minor, &known_mech_attrs); 676 677 return 0; 678} 679 680/* 681 * 682 */ 683 684int 685help(void *opt, int argc, char **argv) 686{ 687 sl_slc_help(commands, argc, argv); 688 return 0; 689} 690 691int 692main(int argc, char **argv) 693{ 694 int optidx = 0; 695 696 setprogname(argv[0]); 697 if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx)) 698 usage(1); 699 700 if (help_flag) 701 usage (0); 702 703 if(version_flag){ 704 print_version(NULL); 705 exit(0); 706 } 707 708 argc -= optidx; 709 argv += optidx; 710 711 if (argc == 0) { 712 help(NULL, argc, argv); 713 return 1; 714 } 715 716 return sl_command (commands, argc, argv); 717} 718