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 "rtbl.h" 38 #include "parse_units.h" 39 #include "kcc-commands.h" 40 41 static char* 42 printable_time_internal(time_t t, int x) 43 { 44 static char s[128]; 45 char *p; 46 47 if ((p = ctime(&t)) == NULL) 48 strlcpy(s, "?", sizeof(s)); 49 else 50 strlcpy(s, p + 4, sizeof(s)); 51 s[x] = 0; 52 return s; 53 } 54 55 static char* 56 printable_time(time_t t) 57 { 58 return printable_time_internal(t, 20); 59 } 60 61 static char* 62 printable_time_long(time_t t) 63 { 64 return printable_time_internal(t, 20); 65 } 66 67 #define COL_ISSUED NP_(" Issued","") 68 #define COL_EXPIRES NP_(" Expires", "") 69 #define COL_FLAGS NP_("Flags", "") 70 #define COL_NAME NP_(" Name", "") 71 #define COL_PRINCIPAL NP_(" Principal", "in klist output") 72 #define COL_PRINCIPAL_KVNO NP_(" Principal (kvno)", "in klist output") 73 #define COL_CACHENAME NP_(" Cache name", "name in klist output") 74 #define COL_DEFCACHE NP_("", "") 75 76 static void 77 print_cred(krb5_context context, krb5_creds *cred, rtbl_t ct, int do_flags) 78 { 79 char *str; 80 krb5_error_code ret; 81 krb5_timestamp sec; 82 83 krb5_timeofday (context, &sec); 84 85 86 if(cred->times.starttime) 87 rtbl_add_column_entry(ct, COL_ISSUED, 88 printable_time(cred->times.starttime)); 89 else 90 rtbl_add_column_entry(ct, COL_ISSUED, 91 printable_time(cred->times.authtime)); 92 93 if(cred->times.endtime > sec) 94 rtbl_add_column_entry(ct, COL_EXPIRES, 95 printable_time(cred->times.endtime)); 96 else 97 rtbl_add_column_entry(ct, COL_EXPIRES, N_(">>>Expired<<<", "")); 98 ret = krb5_unparse_name (context, cred->server, &str); 99 if (ret) 100 krb5_err(context, 1, ret, "krb5_unparse_name"); 101 rtbl_add_column_entry(ct, COL_PRINCIPAL, str); 102 if(do_flags) { 103 char s[16], *sp = s; 104 if(cred->flags.b.forwardable) 105 *sp++ = 'F'; 106 if(cred->flags.b.forwarded) 107 *sp++ = 'f'; 108 if(cred->flags.b.proxiable) 109 *sp++ = 'P'; 110 if(cred->flags.b.proxy) 111 *sp++ = 'p'; 112 if(cred->flags.b.may_postdate) 113 *sp++ = 'D'; 114 if(cred->flags.b.postdated) 115 *sp++ = 'd'; 116 if(cred->flags.b.renewable) 117 *sp++ = 'R'; 118 if(cred->flags.b.initial) 119 *sp++ = 'I'; 120 if(cred->flags.b.invalid) 121 *sp++ = 'i'; 122 if(cred->flags.b.pre_authent) 123 *sp++ = 'A'; 124 if(cred->flags.b.hw_authent) 125 *sp++ = 'H'; 126 *sp = '\0'; 127 rtbl_add_column_entry(ct, COL_FLAGS, s); 128 } 129 free(str); 130 } 131 132 static void 133 print_cred_verbose(krb5_context context, krb5_creds *cred) 134 { 135 size_t j; 136 char *str; 137 krb5_error_code ret; 138 krb5_timestamp sec; 139 140 krb5_timeofday (context, &sec); 141 142 ret = krb5_unparse_name(context, cred->server, &str); 143 if(ret) 144 exit(1); 145 printf(N_("Server: %s\n", ""), str); 146 free (str); 147 148 ret = krb5_unparse_name(context, cred->client, &str); 149 if(ret) 150 exit(1); 151 printf(N_("Client: %s\n", ""), str); 152 free (str); 153 154 { 155 Ticket t; 156 size_t len; 157 char *s; 158 159 decode_Ticket(cred->ticket.data, cred->ticket.length, &t, &len); 160 ret = krb5_enctype_to_string(context, t.enc_part.etype, &s); 161 printf(N_("Ticket etype: ", "")); 162 if (ret == 0) { 163 printf("%s", s); 164 free(s); 165 } else { 166 printf(N_("unknown-enctype(%d)", ""), t.enc_part.etype); 167 } 168 if(t.enc_part.kvno) 169 printf(N_(", kvno %d", ""), *t.enc_part.kvno); 170 printf("\n"); 171 if(cred->session.keytype != t.enc_part.etype) { 172 ret = krb5_enctype_to_string(context, cred->session.keytype, &str); 173 if(ret) 174 krb5_warn(context, ret, "session keytype"); 175 else { 176 printf(N_("Session key: %s\n", "enctype"), str); 177 free(str); 178 } 179 } 180 free_Ticket(&t); 181 printf(N_("Ticket length: %lu\n", ""), 182 (unsigned long)cred->ticket.length); 183 } 184 printf(N_("Auth time: %s\n", ""), 185 printable_time_long(cred->times.authtime)); 186 if(cred->times.authtime != cred->times.starttime) 187 printf(N_("Start time: %s\n", ""), 188 printable_time_long(cred->times.starttime)); 189 printf(N_("End time: %s", ""), 190 printable_time_long(cred->times.endtime)); 191 if(sec > cred->times.endtime) 192 printf(N_(" (expired)", "")); 193 printf("\n"); 194 if(cred->flags.b.renewable) 195 printf(N_("Renew till: %s\n", ""), 196 printable_time_long(cred->times.renew_till)); 197 { 198 char flags[1024]; 199 unparse_flags(TicketFlags2int(cred->flags.b), 200 asn1_TicketFlags_units(), 201 flags, sizeof(flags)); 202 printf(N_("Ticket flags: %s\n", ""), flags); 203 } 204 printf(N_("Addresses: ", "")); 205 if (cred->addresses.len != 0) { 206 for(j = 0; j < cred->addresses.len; j++){ 207 char buf[128]; 208 size_t len; 209 if(j) printf(", "); 210 ret = krb5_print_address(&cred->addresses.val[j], 211 buf, sizeof(buf), &len); 212 213 if(ret == 0) 214 printf("%s", buf); 215 } 216 } else { 217 printf(N_("addressless", "")); 218 } 219 printf("\n\n"); 220 } 221 222 /* 223 * Print all tickets in `ccache' on stdout, verbosily iff do_verbose. 224 */ 225 226 static void 227 print_tickets (krb5_context context, 228 krb5_ccache ccache, 229 krb5_principal principal, 230 int do_verbose, 231 int do_flags, 232 int do_hidden) 233 { 234 krb5_error_code ret; 235 char *str, *name; 236 krb5_cc_cursor cursor; 237 krb5_creds creds; 238 krb5_deltat sec; 239 240 rtbl_t ct = NULL; 241 242 ret = krb5_unparse_name (context, principal, &str); 243 if (ret) 244 krb5_err (context, 1, ret, "krb5_unparse_name"); 245 246 printf ("%17s: %s:%s\n", 247 N_("Credentials cache", ""), 248 krb5_cc_get_type(context, ccache), 249 krb5_cc_get_name(context, ccache)); 250 printf ("%17s: %s\n", N_("Principal", ""), str); 251 252 ret = krb5_cc_get_friendly_name(context, ccache, &name); 253 if (ret == 0) { 254 if (strcmp(name, str) != 0) 255 printf ("%17s: %s\n", N_("Friendly name", ""), name); 256 free(name); 257 } 258 free (str); 259 260 if(do_verbose) { 261 printf ("%17s: %d\n", N_("Cache version", ""), 262 krb5_cc_get_version(context, ccache)); 263 } else { 264 krb5_cc_set_flags(context, ccache, KRB5_TC_NOTICKET); 265 } 266 267 ret = krb5_cc_get_kdc_offset(context, ccache, &sec); 268 269 if (ret == 0 && do_verbose && sec != 0) { 270 char buf[BUFSIZ]; 271 int val; 272 int sig; 273 274 val = sec; 275 sig = 1; 276 if (val < 0) { 277 sig = -1; 278 val = -val; 279 } 280 281 unparse_time (val, buf, sizeof(buf)); 282 283 printf ("%17s: %s%s\n", N_("KDC time offset", ""), 284 sig == -1 ? "-" : "", buf); 285 } 286 287 printf("\n"); 288 289 ret = krb5_cc_start_seq_get (context, ccache, &cursor); 290 if (ret) 291 krb5_err(context, 1, ret, "krb5_cc_start_seq_get"); 292 293 if(!do_verbose) { 294 ct = rtbl_create(); 295 rtbl_add_column(ct, COL_ISSUED, 0); 296 rtbl_add_column(ct, COL_EXPIRES, 0); 297 if(do_flags) 298 rtbl_add_column(ct, COL_FLAGS, 0); 299 rtbl_add_column(ct, COL_PRINCIPAL, 0); 300 rtbl_set_separator(ct, " "); 301 } 302 while ((ret = krb5_cc_next_cred (context, 303 ccache, 304 &cursor, 305 &creds)) == 0) { 306 if (!do_hidden && krb5_is_config_principal(context, creds.server)) { 307 ; 308 }else if(do_verbose){ 309 print_cred_verbose(context, &creds); 310 }else{ 311 print_cred(context, &creds, ct, do_flags); 312 } 313 krb5_free_cred_contents (context, &creds); 314 } 315 if(ret != KRB5_CC_END) 316 krb5_err(context, 1, ret, "krb5_cc_get_next"); 317 ret = krb5_cc_end_seq_get (context, ccache, &cursor); 318 if (ret) 319 krb5_err (context, 1, ret, "krb5_cc_end_seq_get"); 320 if(!do_verbose) { 321 rtbl_format(ct, stdout); 322 rtbl_destroy(ct); 323 } 324 } 325 326 /* 327 * Check if there's a tgt for the realm of `principal' and ccache and 328 * if so return 0, else 1 329 */ 330 331 static int 332 check_for_tgt (krb5_context context, 333 krb5_ccache ccache, 334 krb5_principal principal, 335 time_t *expiration) 336 { 337 krb5_error_code ret; 338 krb5_creds pattern; 339 krb5_creds creds; 340 krb5_const_realm client_realm; 341 int expired; 342 343 krb5_cc_clear_mcred(&pattern); 344 345 client_realm = krb5_principal_get_realm(context, principal); 346 347 ret = krb5_make_principal (context, &pattern.server, 348 client_realm, KRB5_TGS_NAME, client_realm, NULL); 349 if (ret) 350 krb5_err (context, 1, ret, "krb5_make_principal"); 351 pattern.client = principal; 352 353 ret = krb5_cc_retrieve_cred (context, ccache, 0, &pattern, &creds); 354 krb5_free_principal (context, pattern.server); 355 if (ret) { 356 if (ret == KRB5_CC_END) 357 return 1; 358 krb5_err (context, 1, ret, "krb5_cc_retrieve_cred"); 359 } 360 361 expired = time(NULL) > creds.times.endtime; 362 363 if (expiration) 364 *expiration = creds.times.endtime; 365 366 krb5_free_cred_contents (context, &creds); 367 368 return expired; 369 } 370 371 /* 372 * Print a list of all AFS tokens 373 */ 374 375 #ifndef NO_AFS 376 377 static void 378 display_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 448 static int 449 display_v5_ccache (krb5_context context, krb5_ccache ccache, 450 int do_test, int do_verbose, 451 int do_flags, int do_hidden) 452 { 453 krb5_error_code ret; 454 krb5_principal principal; 455 int exit_status = 0; 456 457 458 ret = krb5_cc_get_principal (context, ccache, &principal); 459 if (ret) { 460 if(ret == ENOENT) { 461 if (!do_test) 462 krb5_warnx(context, N_("No ticket file: %s", ""), 463 krb5_cc_get_name(context, ccache)); 464 return 1; 465 } else 466 krb5_err (context, 1, ret, "krb5_cc_get_principal"); 467 } 468 if (do_test) 469 exit_status = check_for_tgt (context, ccache, principal, NULL); 470 else 471 print_tickets (context, ccache, principal, do_verbose, 472 do_flags, do_hidden); 473 474 ret = krb5_cc_close (context, ccache); 475 if (ret) 476 krb5_err (context, 1, ret, "krb5_cc_close"); 477 478 krb5_free_principal (context, principal); 479 480 return exit_status; 481 } 482 483 /* 484 * 485 */ 486 487 static int 488 list_caches(krb5_context context) 489 { 490 krb5_cc_cache_cursor cursor; 491 const char *cdef_name; 492 char *def_name; 493 krb5_error_code ret; 494 krb5_ccache id; 495 rtbl_t ct; 496 497 cdef_name = krb5_cc_default_name(context); 498 if (cdef_name == NULL) 499 krb5_errx(context, 1, "krb5_cc_default_name"); 500 def_name = strdup(cdef_name); 501 502 ret = krb5_cc_cache_get_first (context, NULL, &cursor); 503 if (ret == KRB5_CC_NOSUPP) 504 return 0; 505 else if (ret) 506 krb5_err (context, 1, ret, "krb5_cc_cache_get_first"); 507 508 ct = rtbl_create(); 509 rtbl_add_column(ct, COL_NAME, 0); 510 rtbl_add_column(ct, COL_CACHENAME, 0); 511 rtbl_add_column(ct, COL_EXPIRES, 0); 512 rtbl_add_column(ct, COL_DEFCACHE, 0); 513 rtbl_set_prefix(ct, " "); 514 rtbl_set_column_prefix(ct, COL_NAME, ""); 515 516 while (krb5_cc_cache_next (context, cursor, &id) == 0) { 517 krb5_principal principal = NULL; 518 int expired = 0; 519 char *name; 520 time_t t; 521 522 ret = krb5_cc_get_principal(context, id, &principal); 523 if (ret) 524 continue; 525 526 expired = check_for_tgt (context, id, principal, &t); 527 528 ret = krb5_cc_get_friendly_name(context, id, &name); 529 if (ret == 0) { 530 const char *str; 531 char *fname; 532 rtbl_add_column_entry(ct, COL_NAME, name); 533 rtbl_add_column_entry(ct, COL_CACHENAME, 534 krb5_cc_get_name(context, id)); 535 if (expired) 536 str = N_(">>> Expired <<<", ""); 537 else 538 str = printable_time(t); 539 rtbl_add_column_entry(ct, COL_EXPIRES, str); 540 free(name); 541 542 ret = krb5_cc_get_full_name(context, id, &fname); 543 if (ret) 544 krb5_err (context, 1, ret, "krb5_cc_get_full_name"); 545 546 if (strcmp(fname, def_name) == 0) 547 rtbl_add_column_entry(ct, COL_DEFCACHE, "*"); 548 else 549 rtbl_add_column_entry(ct, COL_DEFCACHE, ""); 550 551 krb5_xfree(fname); 552 } 553 krb5_cc_close(context, id); 554 555 krb5_free_principal(context, principal); 556 } 557 558 krb5_cc_cache_end_seq_get(context, cursor); 559 560 free(def_name); 561 rtbl_format(ct, stdout); 562 rtbl_destroy(ct); 563 564 return 0; 565 } 566 567 /* 568 * 569 */ 570 571 int 572 klist(struct klist_options *opt, int argc, char **argv) 573 { 574 krb5_error_code ret; 575 int exit_status = 0; 576 577 int do_verbose = 578 opt->verbose_flag || 579 opt->a_flag || 580 opt->n_flag; 581 int do_test = 582 opt->test_flag || 583 opt->s_flag; 584 585 if (opt->list_all_flag) { 586 exit_status = list_caches(kcc_context); 587 return exit_status; 588 } 589 590 if (opt->v5_flag) { 591 krb5_ccache id; 592 593 if (opt->all_content_flag) { 594 krb5_cc_cache_cursor cursor; 595 596 ret = krb5_cc_cache_get_first(kcc_context, NULL, &cursor); 597 if (ret) 598 krb5_err(kcc_context, 1, ret, "krb5_cc_cache_get_first"); 599 600 601 while (krb5_cc_cache_next(kcc_context, cursor, &id) == 0) { 602 exit_status |= display_v5_ccache(kcc_context, id, do_test, 603 do_verbose, opt->flags_flag, 604 opt->hidden_flag); 605 printf("\n\n"); 606 } 607 krb5_cc_cache_end_seq_get(kcc_context, cursor); 608 609 } else { 610 if(opt->cache_string) { 611 ret = krb5_cc_resolve(kcc_context, opt->cache_string, &id); 612 if (ret) 613 krb5_err(kcc_context, 1, ret, "%s", opt->cache_string); 614 } else { 615 ret = krb5_cc_default(kcc_context, &id); 616 if (ret) 617 krb5_err(kcc_context, 1, ret, "krb5_cc_resolve"); 618 } 619 exit_status = display_v5_ccache(kcc_context, id, do_test, 620 do_verbose, opt->flags_flag, 621 opt->hidden_flag); 622 } 623 } 624 625 if (!do_test) { 626 #ifndef NO_AFS 627 if (opt->tokens_flag && k_hasafs()) { 628 if (opt->v5_flag) 629 printf("\n"); 630 display_tokens(opt->verbose_flag); 631 } 632 #endif 633 } 634 635 return exit_status; 636 } 637