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