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