xref: /freebsd/crypto/krb5/src/clients/klist/klist.c (revision f1c4c3daccbaf3820f0e2224de53df12fc952fcc)
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* clients/klist/klist.c - List contents of credential cache or keytab */
3 /*
4  * Copyright 1990 by the Massachusetts Institute of Technology.
5  * All Rights Reserved.
6  *
7  * Export of this software from the United States of America may
8  *   require a specific license from the United States Government.
9  *   It is the responsibility of any person or organization contemplating
10  *   export to obtain such a license before exporting.
11  *
12  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13  * distribute this software and its documentation for any purpose and
14  * without fee is hereby granted, provided that the above copyright
15  * notice appear in all copies and that both that copyright notice and
16  * this permission notice appear in supporting documentation, and that
17  * the name of M.I.T. not be used in advertising or publicity pertaining
18  * to distribution of the software without specific, written prior
19  * permission.  Furthermore if you modify this software you must label
20  * your software as modified software and not distribute it in such a
21  * fashion that it might be confused with the original M.I.T. software.
22  * M.I.T. makes no representations about the suitability of
23  * this software for any purpose.  It is provided "as is" without express
24  * or implied warranty.
25  */
26 
27 #include "k5-int.h"
28 #include <krb5.h>
29 #include <com_err.h>
30 #include <locale.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <stdio.h>
34 #include <time.h>
35 
36 /* Need definition of INET6 before network headers, for IRIX.  */
37 #if defined(HAVE_ARPA_INET_H)
38 #include <arpa/inet.h>
39 #endif
40 
41 #ifndef _WIN32
42 #define GET_PROGNAME(x) (strrchr((x), '/') ? strrchr((x), '/') + 1 : (x))
43 #else
44 #define GET_PROGNAME(x) max(max(strrchr((x), '/'), strrchr((x), '\\')) + 1,(x))
45 #endif
46 
47 #ifndef _WIN32
48 #include <sys/socket.h>
49 #include <netdb.h>
50 #endif
51 
52 int show_flags = 0, show_time = 0, status_only = 0, show_keys = 0;
53 int show_etype = 0, show_addresses = 0, no_resolve = 0, print_version = 0;
54 int show_adtype = 0, show_all = 0, list_all = 0, use_client_keytab = 0;
55 int show_config = 0;
56 char *progname;
57 krb5_timestamp now;
58 unsigned int timestamp_width;
59 
60 krb5_context context;
61 
62 static krb5_boolean is_local_tgt(krb5_principal princ, krb5_data *realm);
63 static char *etype_string(krb5_enctype );
64 static void show_credential(krb5_creds *, const char *);
65 
66 static void list_all_ccaches(void);
67 static int list_ccache(krb5_ccache);
68 static void show_all_ccaches(void);
69 static void do_ccache(void);
70 static int show_ccache(krb5_ccache);
71 static int check_ccache(krb5_ccache);
72 static void do_keytab(const char *);
73 static void printtime(krb5_timestamp);
74 static void one_addr(krb5_address *);
75 static void fillit(FILE *, unsigned int, int);
76 
77 #define DEFAULT 0
78 #define CCACHE 1
79 #define KEYTAB 2
80 
81 static void
usage(void)82 usage(void)
83 {
84     fprintf(stderr, _("Usage: %s [-e] [-V] [[-c] [-l] [-A] [-d] [-f] [-s] "
85                       "[-a [-n]]] [-k [-i] [-t] [-K]] [-C] [name]\n"),
86             progname);
87     fprintf(stderr, _("\t-c specifies credentials cache\n"));
88     fprintf(stderr, _("\t-k specifies keytab\n"));
89     fprintf(stderr, _("\t   (Default is credentials cache)\n"));
90     fprintf(stderr, _("\t-i uses default client keytab if no name given\n"));
91     fprintf(stderr, _("\t-l lists credential caches in collection\n"));
92     fprintf(stderr, _("\t-A shows content of all credential caches\n"));
93     fprintf(stderr, _("\t-e shows the encryption type\n"));
94     fprintf(stderr, _("\t-V shows the Kerberos version and exits\n"));
95     fprintf(stderr, _("\toptions for credential caches:\n"));
96     fprintf(stderr, _("\t\t-d shows the submitted authorization data "
97                       "types\n"));
98     fprintf(stderr, _("\t\t-f shows credentials flags\n"));
99     fprintf(stderr, _("\t\t-s sets exit status based on valid tgt "
100                       "existence\n"));
101     fprintf(stderr, _("\t\t-a displays the address list\n"));
102     fprintf(stderr, _("\t\t\t-n do not reverse-resolve\n"));
103     fprintf(stderr, _("\toptions for keytabs:\n"));
104     fprintf(stderr, _("\t\t-t shows keytab entry timestamps\n"));
105     fprintf(stderr, _("\t\t-K shows keytab entry keys\n"));
106     fprintf(stderr, _("\t\t-C includes configuration data entries\n"));
107     exit(1);
108 }
109 
110 static void
extended_com_err_fn(const char * prog,errcode_t code,const char * fmt,va_list args)111 extended_com_err_fn(const char *prog, errcode_t code, const char *fmt,
112                     va_list args)
113 {
114     const char *msg;
115 
116     msg = krb5_get_error_message(context, code);
117     fprintf(stderr, "%s: %s%s", prog, msg, (*fmt == '\0') ? "" : " ");
118     krb5_free_error_message(context, msg);
119     vfprintf(stderr, fmt, args);
120     fprintf(stderr, "\n");
121 }
122 
123 int
main(int argc,char * argv[])124 main(int argc, char *argv[])
125 {
126     krb5_error_code ret;
127     char *name, tmp[BUFSIZ];
128     int c, mode;
129 
130     setlocale(LC_ALL, "");
131     progname = GET_PROGNAME(argv[0]);
132     set_com_err_hook(extended_com_err_fn);
133 
134     name = NULL;
135     mode = DEFAULT;
136     /* V = version so v can be used for verbose later if desired. */
137     while ((c = getopt(argc, argv, "dfetKsnacki45lAVC")) != -1) {
138         switch (c) {
139         case 'd':
140             show_adtype = 1;
141             break;
142         case 'f':
143             show_flags = 1;
144             break;
145         case 'e':
146             show_etype = 1;
147             break;
148         case 't':
149             show_time = 1;
150             break;
151         case 'K':
152             show_keys = 1;
153             break;
154         case 's':
155             status_only = 1;
156             break;
157         case 'n':
158             no_resolve = 1;
159             break;
160         case 'a':
161             show_addresses = 1;
162             break;
163         case 'c':
164             if (mode != DEFAULT)
165                 usage();
166             mode = CCACHE;
167             break;
168         case 'k':
169             if (mode != DEFAULT)
170                 usage();
171             mode = KEYTAB;
172             break;
173         case 'i':
174             use_client_keytab = 1;
175             break;
176         case '4':
177             fprintf(stderr, _("Kerberos 4 is no longer supported\n"));
178             exit(3);
179             break;
180         case '5':
181             break;
182         case 'l':
183             list_all = 1;
184             break;
185         case 'A':
186             show_all = 1;
187             break;
188         case 'C':
189             show_config = 1;
190             break;
191         case 'V':
192             print_version = 1;
193             break;
194         default:
195             usage();
196             break;
197         }
198     }
199 
200     if (no_resolve && !show_addresses)
201         usage();
202 
203     if (mode == DEFAULT || mode == CCACHE) {
204         if (show_time || show_keys)
205             usage();
206         if ((show_all && list_all) || (status_only && list_all))
207             usage();
208     } else {
209         if (show_flags || status_only || show_addresses ||
210             show_all || list_all)
211             usage();
212     }
213 
214     if (argc - optind > 1) {
215         fprintf(stderr, _("Extra arguments (starting with \"%s\").\n"),
216                 argv[optind + 1]);
217         usage();
218     }
219 
220     if (print_version) {
221 #ifdef _WIN32                   /* No access to autoconf vars; fix somehow. */
222         printf("Kerberos for Windows\n");
223 #else
224         printf(_("%s version %s\n"), PACKAGE_NAME, PACKAGE_VERSION);
225 #endif
226         exit(0);
227     }
228 
229     name = (optind == argc - 1) ? argv[optind] : NULL;
230     now = time(0);
231 
232     if (!krb5_timestamp_to_sfstring(now, tmp, 20, NULL) ||
233         !krb5_timestamp_to_sfstring(now, tmp, sizeof(tmp), NULL))
234         timestamp_width = (int)strlen(tmp);
235     else
236         timestamp_width = 15;
237 
238     ret = krb5_init_context(&context);
239     if (ret) {
240         com_err(progname, ret, _("while initializing krb5"));
241         exit(1);
242     }
243 
244     if (name != NULL && mode != KEYTAB) {
245         ret = krb5_cc_set_default_name(context, name);
246         if (ret) {
247             com_err(progname, ret, _("while setting default cache name"));
248             exit(1);
249         }
250     }
251 
252     if (list_all)
253         list_all_ccaches();
254     else if (show_all)
255         show_all_ccaches();
256     else if (mode == DEFAULT || mode == CCACHE)
257         do_ccache();
258     else
259         do_keytab(name);
260     return 0;
261 }
262 
263 static void
do_keytab(const char * name)264 do_keytab(const char *name)
265 {
266     krb5_error_code ret;
267     krb5_keytab kt;
268     krb5_keytab_entry entry;
269     krb5_kt_cursor cursor;
270     unsigned int i;
271     char buf[BUFSIZ]; /* Hopefully large enough for any type */
272     char *pname;
273 
274     if (name == NULL && use_client_keytab) {
275         ret = krb5_kt_client_default(context, &kt);
276         if (ret) {
277             com_err(progname, ret, _("while getting default client keytab"));
278             exit(1);
279         }
280     } else if (name == NULL) {
281         ret = krb5_kt_default(context, &kt);
282         if (ret) {
283             com_err(progname, ret, _("while getting default keytab"));
284             exit(1);
285         }
286     } else {
287         ret = krb5_kt_resolve(context, name, &kt);
288         if (ret) {
289             com_err(progname, ret, _("while resolving keytab %s"), name);
290             exit(1);
291         }
292     }
293 
294     ret = krb5_kt_get_name(context, kt, buf, BUFSIZ);
295     if (ret) {
296         com_err(progname, ret, _("while getting keytab name"));
297         exit(1);
298     }
299 
300     printf("Keytab name: %s\n", buf);
301 
302     ret = krb5_kt_start_seq_get(context, kt, &cursor);
303     if (ret) {
304         com_err(progname, ret, _("while starting keytab scan"));
305         exit(1);
306     }
307 
308     /* XXX Translating would disturb table alignment; skip for now. */
309     if (show_time) {
310         printf("KVNO Timestamp");
311         fillit(stdout, timestamp_width - sizeof("Timestamp") + 2, (int) ' ');
312         printf("Principal\n");
313         printf("---- ");
314         fillit(stdout, timestamp_width, (int) '-');
315         printf(" ");
316         fillit(stdout, 78 - timestamp_width - sizeof("KVNO"), (int) '-');
317         printf("\n");
318     } else {
319         printf("KVNO Principal\n");
320         printf("---- ------------------------------------------------"
321                "--------------------------\n");
322     }
323 
324     while ((ret = krb5_kt_next_entry(context, kt, &entry, &cursor)) == 0) {
325         ret = krb5_unparse_name(context, entry.principal, &pname);
326         if (ret) {
327             com_err(progname, ret, _("while unparsing principal name"));
328             exit(1);
329         }
330         printf("%4d ", entry.vno);
331         if (show_time) {
332             printtime(entry.timestamp);
333             printf(" ");
334         }
335         printf("%s", pname);
336         if (show_etype)
337             printf(" (%s) " , etype_string(entry.key.enctype));
338         if (show_keys) {
339             printf(" (0x");
340             for (i = 0; i < entry.key.length; i++)
341                 printf("%02x", entry.key.contents[i]);
342             printf(")");
343         }
344         printf("\n");
345         krb5_free_unparsed_name(context, pname);
346         krb5_free_keytab_entry_contents(context, &entry);
347     }
348     if (ret && ret != KRB5_KT_END) {
349         com_err(progname, ret, _("while scanning keytab"));
350         exit(1);
351     }
352     ret = krb5_kt_end_seq_get(context, kt, &cursor);
353     if (ret) {
354         com_err(progname, ret, _("while ending keytab scan"));
355         exit(1);
356     }
357     exit(0);
358 }
359 
360 static void
list_all_ccaches(void)361 list_all_ccaches(void)
362 {
363     krb5_error_code ret;
364     krb5_ccache cache;
365     krb5_cccol_cursor cursor;
366     int exit_status;
367 
368     ret = krb5_cccol_cursor_new(context, &cursor);
369     if (ret) {
370         if (!status_only)
371             com_err(progname, ret, _("while listing ccache collection"));
372         exit(1);
373     }
374 
375     /* XXX Translating would disturb table alignment; skip for now. */
376     printf("%-30s %s\n", "Principal name", "Cache name");
377     printf("%-30s %s\n", "--------------", "----------");
378     exit_status = 1;
379     while ((ret = krb5_cccol_cursor_next(context, cursor, &cache)) == 0 &&
380            cache != NULL) {
381         exit_status = list_ccache(cache) && exit_status;
382         krb5_cc_close(context, cache);
383     }
384     krb5_cccol_cursor_free(context, &cursor);
385     exit(exit_status);
386 }
387 
388 static int
list_ccache(krb5_ccache cache)389 list_ccache(krb5_ccache cache)
390 {
391     krb5_error_code ret;
392     krb5_principal princ = NULL;
393     char *princname = NULL, *ccname = NULL;
394     int expired, status = 1;
395 
396     ret = krb5_cc_get_principal(context, cache, &princ);
397     if (ret)                    /* Uninitialized cache file, probably. */
398         goto cleanup;
399     ret = krb5_unparse_name(context, princ, &princname);
400     if (ret)
401         goto cleanup;
402     ret = krb5_cc_get_full_name(context, cache, &ccname);
403     if (ret)
404         goto cleanup;
405 
406     expired = check_ccache(cache);
407 
408     printf("%-30.30s %s", princname, ccname);
409     if (expired)
410         printf(" %s", _("(Expired)"));
411     printf("\n");
412 
413     status = 0;
414 
415 cleanup:
416     krb5_free_principal(context, princ);
417     krb5_free_unparsed_name(context, princname);
418     krb5_free_string(context, ccname);
419     return status;
420 }
421 
422 static void
show_all_ccaches(void)423 show_all_ccaches(void)
424 {
425     krb5_error_code ret;
426     krb5_ccache cache;
427     krb5_cccol_cursor cursor;
428     krb5_boolean first;
429     int exit_status, st;
430 
431     ret = krb5_cccol_cursor_new(context, &cursor);
432     if (ret) {
433         if (!status_only)
434             com_err(progname, ret, _("while listing ccache collection"));
435         exit(1);
436     }
437     exit_status = 1;
438     first = TRUE;
439     while ((ret = krb5_cccol_cursor_next(context, cursor, &cache)) == 0 &&
440            cache != NULL) {
441         if (!status_only && !first)
442             printf("\n");
443         first = FALSE;
444         st = status_only ? check_ccache(cache) : show_ccache(cache);
445         exit_status = st && exit_status;
446         krb5_cc_close(context, cache);
447     }
448     krb5_cccol_cursor_free(context, &cursor);
449     exit(exit_status);
450 }
451 
452 static void
do_ccache(void)453 do_ccache(void)
454 {
455     krb5_error_code ret;
456     krb5_ccache cache;
457 
458     ret = krb5_cc_default(context, &cache);
459     if (ret) {
460         if (!status_only)
461             com_err(progname, ret, _("while resolving ccache"));
462         exit(1);
463     }
464     exit(status_only ? check_ccache(cache) : show_ccache(cache));
465 }
466 
467 /* Display the contents of cache. */
468 static int
show_ccache(krb5_ccache cache)469 show_ccache(krb5_ccache cache)
470 {
471     krb5_cc_cursor cur = NULL;
472     krb5_creds creds;
473     krb5_principal princ = NULL;
474     krb5_error_code ret;
475     char *defname = NULL;
476     int status = 1;
477 
478     ret = krb5_cc_get_principal(context, cache, &princ);
479     if (ret) {
480         com_err(progname, ret, "");
481         goto cleanup;
482     }
483     ret = krb5_unparse_name(context, princ, &defname);
484     if (ret) {
485         com_err(progname, ret, _("while unparsing principal name"));
486         goto cleanup;
487     }
488 
489     printf(_("Ticket cache: %s:%s\nDefault principal: %s\n\n"),
490            krb5_cc_get_type(context, cache), krb5_cc_get_name(context, cache),
491            defname);
492     /* XXX Translating would disturb table alignment; skip for now. */
493     fputs("Valid starting", stdout);
494     fillit(stdout, timestamp_width - sizeof("Valid starting") + 3, (int) ' ');
495     fputs("Expires", stdout);
496     fillit(stdout, timestamp_width - sizeof("Expires") + 3, (int) ' ');
497     fputs("Service principal\n", stdout);
498 
499     ret = krb5_cc_start_seq_get(context, cache, &cur);
500     if (ret) {
501         com_err(progname, ret, _("while starting to retrieve tickets"));
502         goto cleanup;
503     }
504     while ((ret = krb5_cc_next_cred(context, cache, &cur, &creds)) == 0) {
505         if (show_config || !krb5_is_config_principal(context, creds.server))
506             show_credential(&creds, defname);
507         krb5_free_cred_contents(context, &creds);
508     }
509     if (ret == KRB5_CC_END) {
510         ret = krb5_cc_end_seq_get(context, cache, &cur);
511         cur = NULL;
512         if (ret) {
513             com_err(progname, ret, _("while finishing ticket retrieval"));
514             goto cleanup;
515         }
516     } else {
517         com_err(progname, ret, _("while retrieving a ticket"));
518         goto cleanup;
519     }
520 
521     status = 0;
522 
523 cleanup:
524     if (cur != NULL)
525         (void)krb5_cc_end_seq_get(context, cache, &cur);
526     krb5_free_principal(context, princ);
527     krb5_free_unparsed_name(context, defname);
528     return status;
529 }
530 
531 /* Return 0 if cache is accessible, present, and unexpired; return 1 if not. */
532 static int
check_ccache(krb5_ccache cache)533 check_ccache(krb5_ccache cache)
534 {
535     krb5_error_code ret;
536     krb5_cc_cursor cur = NULL;
537     krb5_creds creds;
538     krb5_principal princ = NULL;
539     krb5_boolean found_tgt = FALSE, found_current_tgt = FALSE;
540     krb5_boolean found_current_cred = FALSE;
541 
542     ret = krb5_cc_get_principal(context, cache, &princ);
543     if (ret)
544         goto cleanup;
545     ret = krb5_cc_start_seq_get(context, cache, &cur);
546     if (ret)
547         goto cleanup;
548     found_tgt = found_current_tgt = found_current_cred = FALSE;
549     while ((ret = krb5_cc_next_cred(context, cache, &cur, &creds)) == 0) {
550         if (is_local_tgt(creds.server, &princ->realm)) {
551             found_tgt = TRUE;
552             if (ts_after(creds.times.endtime, now))
553                 found_current_tgt = TRUE;
554         } else if (!krb5_is_config_principal(context, creds.server) &&
555                    ts_after(creds.times.endtime, now)) {
556             found_current_cred = TRUE;
557         }
558         krb5_free_cred_contents(context, &creds);
559     }
560     if (ret != KRB5_CC_END)
561         goto cleanup;
562     ret = krb5_cc_end_seq_get(context, cache, &cur);
563     cur = NULL;
564 
565 cleanup:
566     if (cur != NULL)
567         (void)krb5_cc_end_seq_get(context, cache, &cur);
568     krb5_free_principal(context, princ);
569     if (ret)
570         return 1;
571     /* If the cache contains at least one local TGT, require that it be
572      * current.  Otherwise accept any current cred. */
573     if (found_tgt)
574         return found_current_tgt ? 0 : 1;
575     return found_current_cred ? 0 : 1;
576 }
577 
578 /* Return true if princ is the local krbtgt principal for local_realm. */
579 static krb5_boolean
is_local_tgt(krb5_principal princ,krb5_data * realm)580 is_local_tgt(krb5_principal princ, krb5_data *realm)
581 {
582     return princ->length == 2 && data_eq(princ->realm, *realm) &&
583         data_eq_string(princ->data[0], KRB5_TGS_NAME) &&
584         data_eq(princ->data[1], *realm);
585 }
586 
587 static char *
etype_string(krb5_enctype enctype)588 etype_string(krb5_enctype enctype)
589 {
590     static char buf[100];
591     char *bp = buf;
592     size_t deplen, buflen = sizeof(buf);
593 
594     if (krb5int_c_deprecated_enctype(enctype)) {
595         deplen = strlcpy(bp, "DEPRECATED:", buflen);
596         buflen -= deplen;
597         bp += deplen;
598     }
599 
600     if (krb5_enctype_to_name(enctype, FALSE, bp, buflen))
601         snprintf(bp, buflen, "etype %d", enctype);
602     return buf;
603 }
604 
605 static char *
flags_string(krb5_creds * cred)606 flags_string(krb5_creds *cred)
607 {
608     static char buf[32];
609     int i = 0;
610 
611     if (cred->ticket_flags & TKT_FLG_FORWARDABLE)
612         buf[i++] = 'F';
613     if (cred->ticket_flags & TKT_FLG_FORWARDED)
614         buf[i++] = 'f';
615     if (cred->ticket_flags & TKT_FLG_PROXIABLE)
616         buf[i++] = 'P';
617     if (cred->ticket_flags & TKT_FLG_PROXY)
618         buf[i++] = 'p';
619     if (cred->ticket_flags & TKT_FLG_MAY_POSTDATE)
620         buf[i++] = 'D';
621     if (cred->ticket_flags & TKT_FLG_POSTDATED)
622         buf[i++] = 'd';
623     if (cred->ticket_flags & TKT_FLG_INVALID)
624         buf[i++] = 'i';
625     if (cred->ticket_flags & TKT_FLG_RENEWABLE)
626         buf[i++] = 'R';
627     if (cred->ticket_flags & TKT_FLG_INITIAL)
628         buf[i++] = 'I';
629     if (cred->ticket_flags & TKT_FLG_HW_AUTH)
630         buf[i++] = 'H';
631     if (cred->ticket_flags & TKT_FLG_PRE_AUTH)
632         buf[i++] = 'A';
633     if (cred->ticket_flags & TKT_FLG_TRANSIT_POLICY_CHECKED)
634         buf[i++] = 'T';
635     if (cred->ticket_flags & TKT_FLG_OK_AS_DELEGATE)
636         buf[i++] = 'O';         /* D/d are taken.  Use short strings? */
637     if (cred->ticket_flags & TKT_FLG_ANONYMOUS)
638         buf[i++] = 'a';
639     buf[i] = '\0';
640     return buf;
641 }
642 
643 static void
printtime(krb5_timestamp ts)644 printtime(krb5_timestamp ts)
645 {
646     char timestring[BUFSIZ], fill = ' ';
647 
648     if (!krb5_timestamp_to_sfstring(ts, timestring, timestamp_width + 1,
649                                     &fill))
650         printf("%s", timestring);
651 }
652 
653 static void
print_config_data(int col,krb5_data * data)654 print_config_data(int col, krb5_data *data)
655 {
656     unsigned int i;
657 
658     for (i = 0; i < data->length; i++) {
659         while (col < 8) {
660             putchar(' ');
661             col++;
662         }
663         if (data->data[i] > 0x20 && data->data[i] < 0x7f) {
664             putchar(data->data[i]);
665             col++;
666         } else {
667             col += printf("\\%03o", (unsigned char)data->data[i]);
668         }
669         if (col > 72) {
670             putchar('\n');
671             col = 0;
672         }
673     }
674     if (col > 0)
675         putchar('\n');
676 }
677 
678 static void
show_credential(krb5_creds * cred,const char * defname)679 show_credential(krb5_creds *cred, const char *defname)
680 {
681     krb5_error_code ret;
682     krb5_ticket *tkt = NULL;
683     char *name = NULL, *sname = NULL, *tktsname, *flags;
684     int extra_field = 0, ccol = 0, i, r;
685     krb5_boolean is_config = krb5_is_config_principal(context, cred->server);
686 
687     ret = krb5_unparse_name(context, cred->client, &name);
688     if (ret) {
689         com_err(progname, ret, _("while unparsing client name"));
690         goto cleanup;
691     }
692     ret = krb5_unparse_name(context, cred->server, &sname);
693     if (ret) {
694         com_err(progname, ret, _("while unparsing server name"));
695         goto cleanup;
696     }
697     if (!is_config)
698         (void)krb5_decode_ticket(&cred->ticket, &tkt);
699     if (!cred->times.starttime)
700         cred->times.starttime = cred->times.authtime;
701 
702     if (!is_config) {
703         printtime(cred->times.starttime);
704         putchar(' ');
705         putchar(' ');
706         printtime(cred->times.endtime);
707         putchar(' ');
708         putchar(' ');
709         printf("%s\n", sname);
710     } else {
711         fputs("config: ", stdout);
712         ccol = 8;
713         for (i = 1; i < cred->server->length; i++) {
714             r = printf("%s%.*s%s", i > 1 ? "(" : "",
715                        (int)cred->server->data[i].length,
716                        cred->server->data[i].data, i > 1 ? ")" : "");
717             if (r >= 0)
718                 ccol += r;
719         }
720         fputs(" = ", stdout);
721         ccol += 3;
722     }
723 
724     if (strcmp(name, defname)) {
725         printf(_("\tfor client %s"), name);
726         extra_field++;
727     }
728 
729     if (is_config)
730         print_config_data(ccol, &cred->ticket);
731 
732     if (cred->times.renew_till) {
733         if (!extra_field)
734             fputs("\t",stdout);
735         else
736             fputs(", ",stdout);
737         fputs(_("renew until "), stdout);
738         printtime(cred->times.renew_till);
739         extra_field += 2;
740     }
741 
742     if (show_flags) {
743         flags = flags_string(cred);
744         if (flags && *flags) {
745             if (!extra_field)
746                 fputs("\t",stdout);
747             else
748                 fputs(", ",stdout);
749             printf(_("Flags: %s"), flags);
750             extra_field++;
751         }
752     }
753 
754     if (extra_field > 2) {
755         fputs("\n", stdout);
756         extra_field = 0;
757     }
758 
759     if (show_etype && tkt != NULL) {
760         if (!extra_field)
761             fputs("\t",stdout);
762         else
763             fputs(", ",stdout);
764         printf(_("Etype (skey, tkt): %s, "),
765                etype_string(cred->keyblock.enctype));
766         printf("%s ", etype_string(tkt->enc_part.enctype));
767         extra_field++;
768     }
769 
770     if (show_adtype) {
771         if (cred->authdata != NULL) {
772             if (!extra_field)
773                 fputs("\t",stdout);
774             else
775                 fputs(", ",stdout);
776             printf(_("AD types: "));
777             for (i = 0; cred->authdata[i] != NULL; i++) {
778                 if (i)
779                     printf(", ");
780                 printf("%d", cred->authdata[i]->ad_type);
781             }
782             extra_field++;
783         }
784     }
785 
786     /* If any additional info was printed, extra_field is non-zero. */
787     if (extra_field)
788         putchar('\n');
789 
790     if (show_addresses) {
791         if (cred->addresses == NULL || cred->addresses[0] == NULL) {
792             printf(_("\tAddresses: (none)\n"));
793         } else {
794             printf(_("\tAddresses: "));
795             one_addr(cred->addresses[0]);
796 
797             for (i = 1; cred->addresses[i] != NULL; i++) {
798                 printf(", ");
799                 one_addr(cred->addresses[i]);
800             }
801 
802             printf("\n");
803         }
804     }
805 
806     /* Display the ticket server if it is different from the server name the
807      * entry was cached under (most commonly for referrals). */
808     if (tkt != NULL &&
809         !krb5_principal_compare(context, cred->server, tkt->server)) {
810         ret = krb5_unparse_name(context, tkt->server, &tktsname);
811         if (ret) {
812             com_err(progname, ret, _("while unparsing ticket server name"));
813             goto cleanup;
814         }
815         printf(_("\tTicket server: %s\n"), tktsname);
816         krb5_free_unparsed_name(context, tktsname);
817     }
818 
819 cleanup:
820     krb5_free_unparsed_name(context, name);
821     krb5_free_unparsed_name(context, sname);
822     krb5_free_ticket(context, tkt);
823 }
824 
825 #include "port-sockets.h"
826 #include "socket-utils.h" /* For ss2sin etc. */
827 #include "fake-addrinfo.h"
828 
829 static void
one_addr(krb5_address * a)830 one_addr(krb5_address *a)
831 {
832     struct sockaddr_storage ss;
833     struct sockaddr_in *sinp;
834     struct sockaddr_in6 *sin6p;
835     int err, i;
836     char namebuf[NI_MAXHOST];
837     const uint8_t *p;
838 
839     memset(&ss, 0, sizeof(ss));
840 
841     switch (a->addrtype) {
842     case ADDRTYPE_INET:
843         if (a->length != 4) {
844             printf(_("broken address (type %d length %d)"),
845                    a->addrtype, a->length);
846             return;
847         }
848         sinp = ss2sin(&ss);
849         sinp->sin_family = AF_INET;
850         memcpy(&sinp->sin_addr, a->contents, 4);
851         break;
852     case ADDRTYPE_INET6:
853         if (a->length != 16) {
854             printf(_("broken address (type %d length %d)"),
855                    a->addrtype, a->length);
856             return;
857         }
858         sin6p = ss2sin6(&ss);
859         sin6p->sin6_family = AF_INET6;
860         memcpy(&sin6p->sin6_addr, a->contents, 16);
861         break;
862     case ADDRTYPE_NETBIOS:
863         if (a->length != 16) {
864             printf(_("broken address (type %d length %d)"),
865                    a->addrtype, a->length);
866             return;
867         }
868         p = a->contents;
869         for (i = 0; i < 15 && p[i] != '\0' && p[i] != ' '; i++)
870             putchar(p[i]);
871         return;
872     default:
873         printf(_("unknown addrtype %d"), a->addrtype);
874         return;
875     }
876 
877     namebuf[0] = 0;
878     err = getnameinfo(ss2sa(&ss), sa_socklen(ss2sa(&ss)), namebuf,
879                       sizeof(namebuf), 0, 0,
880                       no_resolve ? NI_NUMERICHOST : 0U);
881     if (err) {
882         printf(_("unprintable address (type %d, error %d %s)"), a->addrtype,
883                err, gai_strerror(err));
884         return;
885     }
886     printf("%s", namebuf);
887 }
888 
889 static void
fillit(FILE * f,unsigned int num,int c)890 fillit(FILE *f, unsigned int num, int c)
891 {
892     unsigned int i;
893 
894     for (i = 0; i < num; i++)
895         fputc(c, f);
896 }
897