1 /* $NetBSD: klist.c,v 1.6 2023/06/19 21:41:42 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
42 static char*
printable_time_internal(time_t t,int x)43 printable_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
56 static char*
printable_time(time_t t)57 printable_time(time_t t)
58 {
59 return printable_time_internal(t, 20);
60 }
61
62 static char*
printable_time_long(time_t t)63 printable_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
77 static void
print_cred(krb5_context context,krb5_creds * cred,rtbl_t ct,int do_flags)78 print_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
139 static void
print_cred_verbose(krb5_context context,krb5_creds * cred,int do_json)140 print_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
238 static void
print_tickets(krb5_context context,krb5_ccache ccache,krb5_principal principal,int do_verbose,int do_flags,int do_hidden,int do_json)239 print_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
359 static int
check_expiration(krb5_context context,krb5_ccache ccache,time_t * expiration)360 check_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
383 static void
display_tokens(int do_verbose)384 display_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
454 static int
display_v5_ccache(krb5_context context,krb5_ccache ccache,int do_test,int do_verbose,int do_flags,int do_hidden,int do_json)455 display_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
498 static int
list_caches(krb5_context context,struct klist_options * opt)499 list_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
587 int
klist(struct klist_options * opt,int argc,char ** argv)588 klist(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