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