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