1/* 2 * Copyright (c) 1997-2008 Kungliga Tekniska Högskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Portions Copyright (c) 2009 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 the Institute nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36#include "kuser_locl.h" 37#include "parse_units.h" 38#include "kcc-commands.h" 39 40static char* 41printable_time_internal(time_t t, int x) 42{ 43 static char s[128]; 44 char *p; 45 46 if ((p = ctime(&t)) == NULL) 47 strlcpy(s, "?", sizeof(s)); 48 else 49 strlcpy(s, p + 4, sizeof(s)); 50 s[x] = 0; 51 return s; 52} 53 54static char* 55printable_time(time_t t) 56{ 57 return printable_time_internal(t, 20); 58} 59 60static char* 61printable_time_long(time_t t) 62{ 63 return printable_time_internal(t, 20); 64} 65 66#define COL_ISSUED NP_(" Issued","") 67#define COL_EXPIRES NP_(" Expires", "") 68#define COL_FLAGS NP_("Flags", "") 69#define COL_NAME NP_(" Name", "") 70#define COL_PRINCIPAL NP_(" Principal", "in klist output") 71#define COL_PRINCIPAL_KVNO NP_(" Principal (kvno)", "in klist output") 72#define COL_CACHENAME NP_(" Cache name", "name in klist output") 73#define COL_DEFCACHE NP_("", "") 74 75static void 76print_cred(krb5_context context, krb5_creds *cred, rtbl_t ct, int do_flags) 77{ 78 char *str; 79 krb5_error_code ret; 80 krb5_timestamp sec; 81 82 krb5_timeofday (context, &sec); 83 84 85 if(cred->times.starttime) 86 rtbl_add_column_entry(ct, COL_ISSUED, 87 printable_time(cred->times.starttime)); 88 else 89 rtbl_add_column_entry(ct, COL_ISSUED, 90 printable_time(cred->times.authtime)); 91 92 if(cred->times.endtime > sec) 93 rtbl_add_column_entry(ct, COL_EXPIRES, 94 printable_time(cred->times.endtime)); 95 else 96 rtbl_add_column_entry(ct, COL_EXPIRES, N_(">>>Expired<<<", "")); 97 ret = krb5_unparse_name (context, cred->server, &str); 98 if (ret) 99 krb5_err(context, 1, ret, "krb5_unparse_name"); 100 rtbl_add_column_entry(ct, COL_PRINCIPAL, str); 101 if(do_flags) { 102 char s[16], *sp = s; 103 if(cred->flags.b.forwardable) 104 *sp++ = 'F'; 105 if(cred->flags.b.forwarded) 106 *sp++ = 'f'; 107 if(cred->flags.b.proxiable) 108 *sp++ = 'P'; 109 if(cred->flags.b.proxy) 110 *sp++ = 'p'; 111 if(cred->flags.b.may_postdate) 112 *sp++ = 'D'; 113 if(cred->flags.b.postdated) 114 *sp++ = 'd'; 115 if(cred->flags.b.renewable) 116 *sp++ = 'R'; 117 if(cred->flags.b.initial) 118 *sp++ = 'I'; 119 if(cred->flags.b.invalid) 120 *sp++ = 'i'; 121 if(cred->flags.b.pre_authent) 122 *sp++ = 'A'; 123 if(cred->flags.b.hw_authent) 124 *sp++ = 'H'; 125 *sp = '\0'; 126 rtbl_add_column_entry(ct, COL_FLAGS, s); 127 } 128 free(str); 129} 130 131static void 132print_cred_verbose(krb5_context context, krb5_creds *cred, int do_json) 133{ 134 size_t j; 135 char *str; 136 krb5_error_code ret; 137 krb5_timestamp sec; 138 139 if (do_json) { /* XXX support more json formating later */ 140 printf("{ \"verbose-supported\" : false }"); 141 return; 142 } 143 144 krb5_timeofday (context, &sec); 145 146 ret = krb5_unparse_name(context, cred->server, &str); 147 if(ret) 148 exit(1); 149 printf(N_("Server: %s\n", ""), str); 150 free (str); 151 152 ret = krb5_unparse_name(context, cred->client, &str); 153 if(ret) 154 exit(1); 155 printf(N_("Client: %s\n", ""), str); 156 free (str); 157 158 if (!krb5_is_config_principal(context, cred->client)) { 159 Ticket t; 160 size_t len; 161 char *s; 162 163 decode_Ticket(cred->ticket.data, cred->ticket.length, &t, &len); 164 ret = krb5_enctype_to_string(context, t.enc_part.etype, &s); 165 printf(N_("Ticket etype: ", "")); 166 if (ret == 0) { 167 printf("%s", s); 168 free(s); 169 } else { 170 printf(N_("unknown-enctype(%d)", ""), t.enc_part.etype); 171 } 172 if(t.enc_part.kvno) 173 printf(N_(", kvno %d", ""), *t.enc_part.kvno); 174 printf("\n"); 175 if(cred->session.keytype != t.enc_part.etype) { 176 ret = krb5_enctype_to_string(context, cred->session.keytype, &str); 177 if(ret) 178 krb5_warn(context, ret, "session keytype"); 179 else { 180 printf(N_("Session key: %s\n", "enctype"), str); 181 free(str); 182 } 183 } 184 free_Ticket(&t); 185 printf(N_("Ticket length: %lu\n", ""), 186 (unsigned long)cred->ticket.length); 187 } 188 printf(N_("Auth time: %s\n", ""), 189 printable_time_long(cred->times.authtime)); 190 if(cred->times.authtime != cred->times.starttime) 191 printf(N_("Start time: %s\n", ""), 192 printable_time_long(cred->times.starttime)); 193 printf(N_("End time: %s", ""), 194 printable_time_long(cred->times.endtime)); 195 if(sec > cred->times.endtime) 196 printf(N_(" (expired)", "")); 197 printf("\n"); 198 if(cred->flags.b.renewable) 199 printf(N_("Renew till: %s\n", ""), 200 printable_time_long(cred->times.renew_till)); 201 { 202 char flags[1024]; 203 unparse_flags(TicketFlags2int(cred->flags.b), 204 asn1_TicketFlags_units(), 205 flags, sizeof(flags)); 206 printf(N_("Ticket flags: %s\n", ""), flags); 207 } 208 printf(N_("Addresses: ", "")); 209 if (cred->addresses.len != 0) { 210 for(j = 0; j < cred->addresses.len; j++){ 211 char buf[128]; 212 size_t len; 213 if(j) printf(", "); 214 ret = krb5_print_address(&cred->addresses.val[j], 215 buf, sizeof(buf), &len); 216 217 if(ret == 0) 218 printf("%s", buf); 219 } 220 } else { 221 printf(N_("addressless", "")); 222 } 223 printf("\n\n"); 224} 225 226/* 227 * Print all tickets in `ccache' on stdout, verbosily iff do_verbose. 228 */ 229 230static void 231print_tickets (krb5_context context, 232 krb5_ccache ccache, 233 krb5_principal principal, 234 int do_verbose, 235 int do_flags, 236 int do_hidden, 237 int do_json) 238{ 239 char *str, *name, *fullname; 240 krb5_error_code ret; 241 krb5_cc_cursor cursor; 242 krb5_creds creds; 243 krb5_deltat sec; 244 245 rtbl_t ct = NULL; 246 247 ret = krb5_unparse_name (context, principal, &str); 248 if (ret) 249 krb5_err (context, 1, ret, "krb5_unparse_name"); 250 251 ret = krb5_cc_get_full_name(context, ccache, &fullname); 252 if (ret) 253 krb5_err (context, 1, ret, "krb5_cc_get_full_name"); 254 255 if (!do_json) { 256 printf ("%17s: %s\n", N_("Credentials cache", ""), fullname); 257 printf ("%17s: %s\n", N_("Principal", ""), str); 258 259 ret = krb5_cc_get_friendly_name(context, ccache, &name); 260 if (ret == 0) { 261 if (strcmp(name, str) != 0) 262 printf ("%17s: %s\n", N_("Friendly name", ""), name); 263 free(name); 264 } 265 free (str); 266 267 if(do_verbose) { 268 printf ("%17s: %d\n", N_("Cache version", ""), 269 krb5_cc_get_version(context, ccache)); 270 } else { 271 krb5_cc_set_flags(context, ccache, KRB5_TC_NOTICKET); 272 } 273 274 ret = krb5_cc_get_kdc_offset(context, ccache, &sec); 275 276 if (ret == 0 && do_verbose && sec != 0) { 277 char buf[BUFSIZ]; 278 int val; 279 int sig; 280 281 val = (int)sec; 282 sig = 1; 283 if (val < 0) { 284 sig = -1; 285 val = -val; 286 } 287 288 unparse_time (val, buf, sizeof(buf)); 289 290 printf ("%17s: %s%s\n", N_("KDC time offset", ""), 291 sig == -1 ? "-" : "", buf); 292 } 293 printf("\n"); 294 } else { 295 printf ("{ \"cache\" : \"%s\", \"principal\" : \"%s\", ", fullname, str); 296 } 297 298 ret = krb5_cc_start_seq_get (context, ccache, &cursor); 299 if (ret) 300 krb5_err(context, 1, ret, "krb5_cc_start_seq_get"); 301 302 if(!do_verbose) { 303 ct = rtbl_create(); 304 rtbl_add_column(ct, COL_ISSUED, 0); 305 rtbl_add_column(ct, COL_EXPIRES, 0); 306 if(do_flags) 307 rtbl_add_column(ct, COL_FLAGS, 0); 308 rtbl_add_column(ct, COL_PRINCIPAL, 0); 309 rtbl_set_separator(ct, " "); 310 if (do_json) { 311 rtbl_set_flags(ct, RTBL_JSON); 312 printf("\"tickets\" : "); 313 } 314 } 315 if (do_verbose && do_json) 316 printf("\"tickets\" : ["); 317 while ((ret = krb5_cc_next_cred (context, 318 ccache, 319 &cursor, 320 &creds)) == 0) { 321 if (!do_hidden && krb5_is_config_principal(context, creds.server)) { 322 ; 323 }else if(do_verbose){ 324 print_cred_verbose(context, &creds, do_json); 325 }else{ 326 print_cred(context, &creds, ct, do_flags); 327 } 328 krb5_free_cred_contents (context, &creds); 329 } 330 if(ret != KRB5_CC_END) 331 krb5_err(context, 1, ret, "krb5_cc_get_next"); 332 ret = krb5_cc_end_seq_get (context, ccache, &cursor); 333 if (ret) 334 krb5_err (context, 1, ret, "krb5_cc_end_seq_get"); 335 if(!do_verbose) { 336 rtbl_format(ct, stdout); 337 rtbl_destroy(ct); 338 } 339 if (do_json) { 340 if (do_verbose) 341 printf("]"); 342 printf("}"); 343 } 344} 345 346/* 347 * Check if there's a tgt for the realm of `principal' and ccache and 348 * if so return 0, else 1 349 */ 350 351static int 352check_for_tgt (krb5_context context, 353 krb5_ccache ccache, 354 krb5_principal principal, 355 time_t *expiration) 356{ 357 krb5_error_code ret; 358 krb5_creds pattern; 359 krb5_creds creds; 360 krb5_const_realm client_realm; 361 int expired; 362 363 krb5_cc_clear_mcred(&pattern); 364 365 client_realm = krb5_principal_get_realm(context, principal); 366 367 ret = krb5_make_principal (context, &pattern.server, 368 client_realm, KRB5_TGS_NAME, client_realm, NULL); 369 if (ret) 370 krb5_err (context, 1, ret, "krb5_make_principal"); 371 pattern.client = principal; 372 373 ret = krb5_cc_retrieve_cred (context, ccache, 0, &pattern, &creds); 374 krb5_free_principal (context, pattern.server); 375 if (ret) { 376 if (ret == KRB5_CC_NOTFOUND) 377 return 1; 378 krb5_err (context, 1, ret, "krb5_cc_retrieve_cred"); 379 } 380 381 expired = time(NULL) > creds.times.endtime; 382 383 if (expiration) 384 *expiration = creds.times.endtime; 385 386 krb5_free_cred_contents (context, &creds); 387 388 return expired; 389} 390 391/* 392 * Print a list of all AFS tokens 393 */ 394 395#ifndef NO_AFS 396 397static void 398display_tokens(int do_verbose) 399{ 400 uint32_t i; 401 unsigned char t[4096]; 402 struct ViceIoctl parms; 403 404 parms.in = (void *)&i; 405 parms.in_size = sizeof(i); 406 parms.out = (void *)t; 407 parms.out_size = sizeof(t); 408 409 for (i = 0;; i++) { 410 int32_t size_secret_tok, size_public_tok; 411 unsigned char *cell; 412 struct ClearToken ct; 413 unsigned char *r = t; 414 struct timeval tv; 415 char buf1[20], buf2[20]; 416 417 if(k_pioctl(NULL, VIOCGETTOK, &parms, 0) < 0) { 418 if(errno == EDOM) 419 break; 420 continue; 421 } 422 if(parms.out_size > sizeof(t)) 423 continue; 424 if(parms.out_size < sizeof(size_secret_tok)) 425 continue; 426 t[min(parms.out_size,sizeof(t)-1)] = 0; 427 memcpy(&size_secret_tok, r, sizeof(size_secret_tok)); 428 /* dont bother about the secret token */ 429 r += size_secret_tok + sizeof(size_secret_tok); 430 if (parms.out_size < (r - t) + sizeof(size_public_tok)) 431 continue; 432 memcpy(&size_public_tok, r, sizeof(size_public_tok)); 433 r += sizeof(size_public_tok); 434 if (parms.out_size < (r - t) + size_public_tok + sizeof(int32_t)) 435 continue; 436 memcpy(&ct, r, size_public_tok); 437 r += size_public_tok; 438 /* there is a int32_t with length of cellname, but we dont read it */ 439 r += sizeof(int32_t); 440 cell = r; 441 442 gettimeofday (&tv, NULL); 443 strlcpy (buf1, printable_time(ct.BeginTimestamp), 444 sizeof(buf1)); 445 if (do_verbose || tv.tv_sec < ct.EndTimestamp) 446 strlcpy (buf2, printable_time(ct.EndTimestamp), 447 sizeof(buf2)); 448 else 449 strlcpy (buf2, N_(">>> Expired <<<", ""), sizeof(buf2)); 450 451 printf("%s %s ", buf1, buf2); 452 453 if ((ct.EndTimestamp - ct.BeginTimestamp) & 1) 454 printf(N_("User's (AFS ID %d) tokens for %s", ""), ct.ViceId, cell); 455 else 456 printf(N_("Tokens for %s", ""), cell); 457 if (do_verbose) 458 printf(" (%d)", ct.AuthHandle); 459 putchar('\n'); 460 } 461} 462#endif 463 464/* 465 * display the ccache in `cred_cache' 466 */ 467 468static int 469display_v5_ccache (krb5_context context, krb5_ccache ccache, 470 int do_test, int do_verbose, 471 int do_flags, int do_hidden, 472 int do_json) 473{ 474 krb5_error_code ret; 475 krb5_principal principal; 476 int exit_status = 0; 477 478 479 ret = krb5_cc_get_principal (context, ccache, &principal); 480 if (ret) { 481 if (do_json) { 482 printf("{}"); 483 return 0; 484 } 485 if(ret == ENOENT) { 486 if (!do_test) 487 krb5_warnx(context, N_("No ticket file: %s", ""), 488 krb5_cc_get_name(context, ccache)); 489 return 1; 490 } else 491 krb5_err (context, 1, ret, "krb5_cc_get_principal"); 492 } 493 if (do_test) 494 exit_status = check_for_tgt (context, ccache, principal, NULL); 495 else 496 print_tickets (context, ccache, principal, do_verbose, 497 do_flags, do_hidden, do_json); 498 499 ret = krb5_cc_close (context, ccache); 500 if (ret) 501 krb5_err (context, 1, ret, "krb5_cc_close"); 502 503 krb5_free_principal (context, principal); 504 505 return exit_status; 506} 507 508/* 509 * 510 */ 511 512static int 513list_caches(krb5_context context, struct klist_options *opt) 514{ 515 krb5_cccol_cursor cursor; 516 const char *cdef_name; 517 char *def_name; 518 krb5_error_code ret; 519 krb5_ccache id; 520 rtbl_t ct; 521 522 cdef_name = krb5_cc_default_name(context); 523 if (cdef_name == NULL) 524 krb5_errx(context, 1, "krb5_cc_default_name"); 525 def_name = strdup(cdef_name); 526 527 ret = krb5_cccol_cursor_new(context, &cursor); 528 if (ret == KRB5_CC_NOSUPP) { 529 free(def_name); 530 return 0; 531 } else if (ret) 532 krb5_err (context, 1, ret, "krb5_cc_cache_get_first"); 533 534 ct = rtbl_create(); 535 rtbl_add_column(ct, COL_DEFCACHE, 0); 536 rtbl_add_column(ct, COL_NAME, 0); 537 rtbl_add_column(ct, COL_CACHENAME, 0); 538 rtbl_add_column(ct, COL_EXPIRES, 0); 539 rtbl_add_column(ct, COL_DEFCACHE, 0); 540 rtbl_set_prefix(ct, " "); 541 rtbl_set_column_prefix(ct, COL_DEFCACHE, ""); 542 rtbl_set_column_prefix(ct, COL_NAME, " "); 543 if (opt->json_flag) 544 rtbl_set_flags(ct, RTBL_JSON); 545 546 while (krb5_cccol_cursor_next(context, cursor, &id) == 0 && id != NULL) { 547 krb5_principal principal = NULL; 548 int expired = 0; 549 char *name; 550 time_t t; 551 552 ret = krb5_cc_get_principal(context, id, &principal); 553 if (ret) 554 continue; 555 556 expired = check_for_tgt (context, id, principal, &t); 557 558 ret = krb5_cc_get_friendly_name(context, id, &name); 559 if (ret == 0) { 560 const char *str; 561 char *fname; 562 563 rtbl_add_column_entry(ct, COL_NAME, name); 564 free(name); 565 566 if (expired) 567 str = N_(">>> Expired <<<", ""); 568 else 569 str = printable_time(t); 570 rtbl_add_column_entry(ct, COL_EXPIRES, str); 571 572 ret = krb5_cc_get_full_name(context, id, &fname); 573 if (ret) 574 krb5_err (context, 1, ret, "krb5_cc_get_full_name"); 575 576 rtbl_add_column_entry(ct, COL_CACHENAME, fname); 577 if (opt->json_flag) 578 ; 579 else if (strcmp(fname, def_name) == 0) 580 rtbl_add_column_entry(ct, COL_DEFCACHE, "*"); 581 else 582 rtbl_add_column_entry(ct, COL_DEFCACHE, ""); 583 584 krb5_xfree(fname); 585 } 586 krb5_cc_close(context, id); 587 588 krb5_free_principal(context, principal); 589 } 590 591 krb5_cccol_cursor_free(context, &cursor); 592 593 free(def_name); 594 rtbl_format(ct, stdout); 595 rtbl_destroy(ct); 596 597 if (opt->json_flag) 598 printf("\n"); 599 600 return 0; 601} 602 603/* 604 * 605 */ 606 607int 608klist(struct klist_options *opt, int argc, char **argv) 609{ 610 krb5_error_code ret; 611 int exit_status = 0; 612 613 int do_verbose = 614 opt->verbose_flag || 615 opt->a_flag || 616 opt->n_flag; 617 int do_test = 618 opt->test_flag || 619 opt->s_flag; 620 621 if(opt->version_flag) { 622 print_version(NULL); 623 exit(0); 624 } 625 626 if (opt->list_all_flag) { 627 exit_status = list_caches(kcc_context, opt); 628 return exit_status; 629 } 630 631 if (opt->v5_flag) { 632 krb5_ccache id; 633 634 if (opt->all_content_flag) { 635 krb5_cc_cache_cursor cursor; 636 int first = 1; 637 638 ret = krb5_cc_cache_get_first(kcc_context, NULL, &cursor); 639 if (ret) 640 krb5_err(kcc_context, 1, ret, "krb5_cc_cache_get_first"); 641 642 if (opt->json_flag) 643 printf("["); 644 while (krb5_cc_cache_next(kcc_context, cursor, &id) == 0) { 645 if (opt->json_flag && !first) 646 printf(","); 647 648 exit_status |= display_v5_ccache(kcc_context, id, do_test, 649 do_verbose, opt->flags_flag, 650 opt->hidden_flag, opt->json_flag); 651 if (!opt->json_flag) 652 printf("\n\n"); 653 654 first = 0; 655 } 656 krb5_cc_cache_end_seq_get(kcc_context, cursor); 657 if (opt->json_flag) 658 printf("]"); 659 } else { 660 if(opt->cache_string) { 661 ret = krb5_cc_resolve(kcc_context, opt->cache_string, &id); 662 if (ret) 663 krb5_err(kcc_context, 1, ret, "%s", opt->cache_string); 664 } else { 665 ret = krb5_cc_default(kcc_context, &id); 666 if (ret) 667 krb5_err(kcc_context, 1, ret, "krb5_cc_resolve"); 668 } 669 exit_status = display_v5_ccache(kcc_context, id, do_test, 670 do_verbose, opt->flags_flag, 671 opt->hidden_flag, opt->json_flag); 672 } 673 } 674 675 if (!do_test) { 676#ifndef NO_AFS 677 if (opt->tokens_flag && k_hasafs()) { 678 if (opt->v5_flag) 679 printf("\n"); 680 display_tokens(opt->verbose_flag); 681 } 682#endif 683 } 684 685 return exit_status; 686} 687