xref: /illumos-gate/usr/src/cmd/krb5/klist/klist.c (revision 2aeafac3612e19716bf8164f89c3c9196342979c)
1 /*
2  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*
7  * clients/klist/klist.c
8  *
9  * Copyright 1990 by the Massachusetts Institute of Technology.
10  * All Rights Reserved.
11  *
12  * Export of this software from the United States of America may
13  *   require a specific license from the United States Government.
14  *   It is the responsibility of any person or organization contemplating
15  *   export to obtain such a license before exporting.
16  *
17  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
18  * distribute this software and its documentation for any purpose and
19  * without fee is hereby granted, provided that the above copyright
20  * notice appear in all copies and that both that copyright notice and
21  * this permission notice appear in supporting documentation, and that
22  * the name of M.I.T. not be used in advertising or publicity pertaining
23  * to distribution of the software without specific, written prior
24  * permission.  Furthermore if you modify this software you must label
25  * your software as modified software and not distribute it in such a
26  * fashion that it might be confused with the original M.I.T. software.
27  * M.I.T. makes no representations about the suitability of
28  * this software for any purpose.  It is provided "as is" without express
29  * or implied warranty.
30  *
31  *
32  * List out the contents of your credential cache or keytab.
33  */
34 
35 #include <k5-int.h>
36 #include "com_err.h"
37 #include <krb5.h>
38 #ifdef KRB5_KRB4_COMPAT
39 #include <kerberosIV/krb.h>
40 #endif /* KRB5_KRB4_COMPAT */
41 
42 #include <stdlib.h>
43 #include <string.h>
44 #include <stdio.h>
45 #include <time.h>
46 #include <libintl.h>
47 #include <locale.h>
48 #include <netinet/in.h>
49 #if defined(HAVE_ARPA_INET_H)
50 #include <arpa/inet.h>
51 #endif
52 #include <inet/ip.h>
53 #include <inet/ip6.h>
54 
55 #ifndef _WIN32
56 #define GET_PROGNAME(x) (strrchr((x), '/') ? strrchr((x), '/')+1 : (x))
57 #else
58 #define GET_PROGNAME(x) max(max(strrchr((x), '/'), strrchr((x), '\\')) + 1,(x))
59 #endif /* _WIN32 */
60 
61 #ifndef _WIN32
62 #include <sys/socket.h>
63 #include <netdb.h>
64 #endif
65 
66 extern int optind;
67 
68 int show_flags = 0, show_time = 0, status_only = 0, show_keys = 0;
69 int show_etype = 0, show_addresses = 0, no_resolve = 0;
70 char *defname;
71 char *progname;
72 krb5_int32 now;
73 size_t timestamp_width;
74 
75 krb5_context kcontext;
76 
77 char * etype_string (krb5_enctype );
78 void show_credential (krb5_creds *);
79 
80 void do_ccache (char *);
81 void do_keytab (char *);
82 void printtime (time_t);
83 void one_addr (krb5_address *);
84 void fillit (FILE *, unsigned int, int);
85 void show_addr(krb5_address *a);
86 
87 #ifdef KRB5_KRB4_COMPAT
88 void do_v4_ccache (char *);
89 #endif /* KRB5_KRB4_COMPAT */
90 
91 #define DEFAULT 0
92 #define CCACHE 1
93 #define KEYTAB 2
94 
95 /*
96  * The reason we start out with got_k4 and got_k5 as zero (false) is
97  * so that we can easily add dynamic loading support for determining
98  * whether Kerberos 4 and Keberos 5 libraries are available
99  */
100 
101 static int got_k5 = 0;
102 static int got_k4 = 0;
103 
104 static int default_k5 = 1;
105 #ifdef KRB5_KRB4_COMPAT
106 static int default_k4 = 1;
107 #else  /* KRB5_KRB4_COMPAT */
108 static int default_k4 = 0;
109 #endif /* KRB5_KRB4_COMPAT */
110 
111 static void usage()
112 {
113 #define KRB_AVAIL_STRING(x) ((x)?gettext("available"):gettext("not available"))
114 
115     fprintf(stderr, gettext("Usage: %s [-5] [-4] [-e]"
116                     " [[-c] [-f] [-s] [-a [-n]]] "
117 	            "[-k [-t] [-K]] [name]\n"), progname);
118     fprintf(stderr, "\t-5 Kerberos 5 (%s)\n", KRB_AVAIL_STRING(got_k5));
119     fprintf(stderr, "\t-4 Kerberos 4 (%s)\n", KRB_AVAIL_STRING(got_k4));
120     fprintf(stderr, gettext("\t   (Default is %s%s%s%s)\n"),
121 	    default_k5?"Kerberos 5":"",
122 	    (default_k5 && default_k4)?gettext(" and "):"",
123 	    default_k4?"Kerberos 4":"",
124 	    (!default_k5 && !default_k4)?gettext("neither"):"");
125     fprintf(stderr, gettext("\t-c specifies credentials cache\n"));
126     fprintf(stderr, gettext("\t-k specifies keytab\n"));
127     fprintf(stderr, gettext("\t   (Default is credentials cache)\n"));
128     fprintf(stderr, gettext("\t-e shows the encryption type\n"));
129     fprintf(stderr, gettext("\toptions for credential caches:\n"));
130     fprintf(stderr, gettext("\t\t-f shows credentials flags\n"));
131     fprintf(stderr, gettext("\t\t-s sets exit status based on valid tgt existence\n"));
132     fprintf(stderr, gettext("\t\t-a displays the address list\n"));
133     fprintf(stderr, gettext("\t\t-n do not reverse-resolve\n"));
134     fprintf(stderr, gettext("\toptions for keytabs:\n"));
135     fprintf(stderr, gettext("\t\t-t shows keytab entry timestamps\n"));
136     fprintf(stderr, gettext("\t\t-K shows keytab entry DES keys\n"));
137     exit(1);
138 }
139 
140 
141 int
142 main(argc, argv)
143     int argc;
144     char **argv;
145 {
146     int c;
147     char *name;
148     int mode;
149     int use_k5 = 0, use_k4 = 0;
150 
151     got_k5 = 1;
152 #ifdef KRB5_KRB4_COMPAT
153     got_k4 = 1;
154 #endif /* KRB5_KRB4_COMPAT */
155 
156     (void) setlocale(LC_ALL, "");
157 
158 #if !defined(TEXT_DOMAIN)
159 #define	TEXT_DOMAIN "SYS_TEST"
160 #endif /* !TEXT_DOMAIN */
161 
162     (void) textdomain(TEXT_DOMAIN);
163 
164     progname = GET_PROGNAME(argv[0]);
165 
166     name = NULL;
167     mode = DEFAULT;
168     while ((c = getopt(argc, argv, "fetKsnack45")) != -1) {
169 	switch (c) {
170 	case 'f':
171 	    show_flags = 1;
172 	    break;
173 	case 'e':
174 	    show_etype = 1;
175 	    break;
176 	case 't':
177 	    show_time = 1;
178 	    break;
179 	case 'K':
180 	    show_keys = 1;
181 	    break;
182 	case 's':
183 	    status_only = 1;
184 	    break;
185 	case 'n':
186 	    no_resolve = 1;
187 	    break;
188 	case 'a':
189 	    show_addresses = 1;
190 	    break;
191 	case 'c':
192 	    if (mode != DEFAULT) usage();
193 	    mode = CCACHE;
194 	    break;
195 	case 'k':
196 	    if (mode != DEFAULT) usage();
197 	    mode = KEYTAB;
198 	    break;
199 	case '4':
200 	    if (!got_k4)
201 	    {
202 #ifdef KRB5_KRB4_COMPAT
203 		fprintf(stderr, "Kerberos 4 support could not be loaded\n");
204 #else  /* KRB5_KRB4_COMPAT */
205 		fprintf(stderr, gettext("This was not built with Kerberos 4 support\n"));
206 #endif /* KRB5_KRB4_COMPAT */
207 		exit(3);
208 	    }
209 	    use_k4 = 1;
210 	    break;
211 	case '5':
212 	    if (!got_k5)
213 	    {
214 		fprintf(stderr, gettext("Kerberos 5 support could not be loaded\n"));
215 		exit(3);
216 	    }
217 	    use_k5 = 1;
218 	    break;
219 	default:
220 	    usage();
221 	    break;
222 	}
223     }
224 
225     if (no_resolve && !show_addresses) {
226 	usage();
227     }
228 
229     if (mode == DEFAULT || mode == CCACHE) {
230 	if (show_time || show_keys)
231 	    usage();
232     } else {
233 	if (show_flags || status_only || show_addresses)
234 	    usage();
235     }
236 
237     if (argc - optind > 1) {
238 		fprintf(stderr,
239 			gettext("Extra arguments (starting with \"%s\").\n"),
240 		argv[optind+1]);
241 	usage();
242     }
243 
244     name = (optind == argc-1) ? argv[optind] : 0;
245 
246     if (!use_k5 && !use_k4)
247     {
248 	use_k5 = default_k5;
249 	use_k4 = default_k4;
250     }
251 
252     if (!use_k5)
253 	got_k5 = 0;
254     if (!use_k4)
255 	got_k4 = 0;
256 
257     now = time(0);
258     {
259 	char tmp[BUFSIZ];
260 
261 	if (!krb5_timestamp_to_sfstring(now, tmp, 20, (char *) NULL) ||
262 	    !krb5_timestamp_to_sfstring(now, tmp, sizeof(tmp),
263 					(char *) NULL))
264 	    timestamp_width = (int) strlen(tmp);
265 	else
266 	    timestamp_width = 15;
267     }
268 
269     if (got_k5)
270     {
271 	krb5_error_code retval;
272 	retval = krb5_init_context(&kcontext);
273 	if (retval) {
274 	    com_err(progname, retval, gettext("while initializing krb5"));
275 	    exit(1);
276 	}
277 
278 	if (mode == DEFAULT || mode == CCACHE)
279 	    do_ccache(name);
280 	else
281 	    do_keytab(name);
282     } else {
283 #ifdef KRB5_KRB4_COMPAT
284 	if (mode == DEFAULT || mode == CCACHE)
285 	    do_v4_ccache(name);
286 	else {
287 	    /* We may want to add v4 srvtab support */
288 	    fprintf(stderr,
289 		    "%s: srvtab option not supported for Kerberos 4\n",
290 		    progname);
291 	    exit(1);
292 	}
293 #endif /* KRB4_KRB5_COMPAT */
294     }
295 
296     return 0;
297 }
298 
299 void do_keytab(name)
300    char *name;
301 {
302      krb5_keytab kt;
303      krb5_keytab_entry entry;
304      krb5_kt_cursor cursor;
305      char buf[BUFSIZ]; /* hopefully large enough for any type */
306      char *pname;
307      int code;
308 
309      if (name == NULL) {
310 	  if ((code = krb5_kt_default(kcontext, &kt))) {
311 			com_err(progname, code,
312 				gettext("while getting default keytab"));
313 	       exit(1);
314 	  }
315      } else {
316 	  if ((code = krb5_kt_resolve(kcontext, name, &kt))) {
317 			com_err(progname, code,
318 				gettext("while resolving keytab %s"),
319 		       name);
320 	       exit(1);
321 	  }
322      }
323 
324      if ((code = krb5_kt_get_name(kcontext, kt, buf, BUFSIZ))) {
325 	  com_err(progname, code,
326 			gettext("while getting keytab name"));
327 	  exit(1);
328      }
329 
330      printf(gettext("Keytab name: %s\n"), buf);
331 
332      if ((code = krb5_kt_start_seq_get(kcontext, kt, &cursor))) {
333 	  com_err(progname, code,
334 			gettext("while starting keytab scan"));
335 	  exit(1);
336      }
337 
338      if (show_time) {
339 	  printf(gettext("KVNO Timestamp"));
340 	  fillit(stdout, timestamp_width -
341 	    sizeof (gettext("Timestamp")) + 2, (int)' ');
342 	  printf(gettext("Principal\n"));
343 	  printf("---- ");
344 	  fillit(stdout, timestamp_width, (int) '-');
345 	  printf(" ");
346 	  fillit(stdout, 78 - timestamp_width -
347 		    sizeof (gettext("KVNO")), (int)'-');
348 	  printf("\n");
349      } else {
350 	  printf(gettext("KVNO Principal\n"));
351 	  printf("---- ------------------------------"
352 			    "--------------------------------------"
353 			    "------\n");
354      }
355 
356      while ((code = krb5_kt_next_entry(kcontext, kt, &entry, &cursor)) == 0) {
357 	  if ((code = krb5_unparse_name(kcontext, entry.principal, &pname))) {
358 	       com_err(progname, code,
359 				gettext("while unparsing principal name"));
360 	       exit(1);
361 	  }
362 	  printf("%4d ", entry.vno);
363 	  if (show_time) {
364 	       printtime(entry.timestamp);
365 	       printf(" ");
366 	  }
367 	  printf("%s", pname);
368 	  if (show_etype)
369 	      printf(" (%s) " , etype_string(entry.key.enctype));
370 	  if (show_keys) {
371 	       printf(" (0x");
372 	       {
373 		    int i;
374 		    for (i = 0; i < entry.key.length; i++)
375 			 printf("%02x", entry.key.contents[i]);
376 	       }
377 	       printf(")");
378 	  }
379 	  printf("\n");
380 	  krb5_free_unparsed_name(kcontext, pname);
381      }
382      if (code && code != KRB5_KT_END) {
383 		com_err(progname, code,
384 			gettext("while scanning keytab"));
385 	  exit(1);
386      }
387      if ((code = krb5_kt_end_seq_get(kcontext, kt, &cursor))) {
388 		com_err(progname, code,
389 			gettext("while ending keytab scan"));
390 	  exit(1);
391      }
392      exit(0);
393 }
394 void do_ccache(name)
395    char *name;
396 {
397     krb5_ccache cache = NULL;
398     krb5_cc_cursor cur;
399     krb5_creds creds;
400     krb5_principal princ;
401     krb5_flags flags;
402     krb5_error_code code;
403     int	exit_status = 0;
404 
405     if (status_only)
406 	/* exit_status is set back to 0 if a valid tgt is found */
407 	exit_status = 1;
408 
409     if (name == NULL) {
410 	if ((code = krb5_cc_default(kcontext, &cache))) {
411 	    if (!status_only)
412 				com_err(progname, code,
413 					gettext("while getting default "
414 						"ccache"));
415 	    exit(1);
416 	    }
417     } else {
418 	if ((code = krb5_cc_resolve(kcontext, name, &cache))) {
419 	    if (!status_only)
420 				com_err(progname, code,
421 					gettext("while resolving ccache %s"),
422 			name);
423 	    exit(1);
424 	}
425     }
426 
427     flags = 0;				/* turns off OPENCLOSE mode */
428     if ((code = krb5_cc_set_flags(kcontext, cache, flags))) {
429 	if (code == KRB5_FCC_NOFILE) {
430 	    if (!status_only) {
431 		com_err(progname, code, gettext("(ticket cache %s:%s)"),
432 			krb5_cc_get_type(kcontext, cache),
433 			krb5_cc_get_name(kcontext, cache));
434 #ifdef KRB5_KRB4_COMPAT
435 		if (name == NULL)
436 		    do_v4_ccache(0);
437 #endif /* KRB5_KRB4_COMPAT */
438 	    }
439 	} else {
440 	    if (!status_only)
441 		com_err(progname, code,
442 			gettext("while setting cache "
443 				"flags(ticket cache %s:%s)"),
444 			krb5_cc_get_type(kcontext, cache),
445 			krb5_cc_get_name(kcontext, cache));
446 	}
447 	exit(1);
448     }
449     if ((code = krb5_cc_get_principal(kcontext, cache, &princ))) {
450 	if (!status_only)
451 			com_err(progname, code,
452 				gettext("while retrieving principal name"));
453 	exit(1);
454     }
455     if ((code = krb5_unparse_name(kcontext, princ, &defname))) {
456 	if (!status_only)
457 			com_err(progname, code,
458 				gettext("while unparsing principal name"));
459 	exit(1);
460     }
461     if (!status_only) {
462 		printf(gettext("Ticket cache: %s:%s\nDefault principal: "
463 			    "%s\n\n"),
464 	       krb5_cc_get_type(kcontext, cache),
465 	       krb5_cc_get_name(kcontext, cache), defname);
466 		fputs(gettext("Valid starting"), stdout);
467 		fillit(stdout, timestamp_width -
468 		    sizeof (gettext("Valid starting")) + 3, (int)' ');
469 		fputs(gettext("Expires"), stdout);
470 		fillit(stdout, timestamp_width -
471 		    sizeof (gettext("Expires")) + 3, (int)' ');
472 		fputs(gettext("Service principal\n"), stdout);
473     }
474     if ((code = krb5_cc_start_seq_get(kcontext, cache, &cur))) {
475 	if (!status_only)
476 			com_err(progname, code,
477 				gettext("while starting to retrieve tickets"));
478 	exit(1);
479     }
480     while (!(code = krb5_cc_next_cred(kcontext, cache, &cur, &creds))) {
481 	if (status_only) {
482 	    if (exit_status && creds.server->length == 2 &&
483 			    strcmp(creds.server->realm.data,
484 				princ->realm.data) == 0 &&
485 			    strcmp((char *)creds.server->data[0].data,
486 				"krbtgt") == 0 &&
487 		strcmp((char *)creds.server->data[1].data,
488 		       princ->realm.data) == 0 &&
489 		creds.times.endtime > now)
490 		exit_status = 0;
491 	} else {
492 	    show_credential(&creds);
493 	}
494 	krb5_free_cred_contents(kcontext, &creds);
495     }
496     if (code == KRB5_CC_END) {
497 	if ((code = krb5_cc_end_seq_get(kcontext, cache, &cur))) {
498 	    if (!status_only)
499 				com_err(progname, code,
500 					gettext("while finishing ticket "
501 						"retrieval"));
502 	    exit(1);
503 	}
504 	flags = KRB5_TC_OPENCLOSE;	/* turns on OPENCLOSE mode */
505 	if ((code = krb5_cc_set_flags(kcontext, cache, flags))) {
506 	    if (!status_only)
507 				com_err(progname, code,
508 					gettext("while closing ccache"));
509 	    exit(1);
510 	}
511 #ifdef KRB5_KRB4_COMPAT
512 	if (name == NULL && !status_only)
513 	    do_v4_ccache(0);
514 #endif /* KRB5_KRB4_COMPAT */
515 	exit(exit_status);
516     } else {
517 	if (!status_only)
518 			com_err(progname, code,
519 				gettext("while retrieving a ticket"));
520 	exit(1);
521     }
522 }
523 
524 char *
525 etype_string(enctype)
526     krb5_enctype enctype;
527 {
528     static char buf[256];
529     krb5_error_code retval;
530 
531     if ((retval = krb5_enctype_to_string(enctype, buf, sizeof(buf)))) {
532 	/* XXX if there's an error != EINVAL, I should probably report it */
533 	snprintf(buf, sizeof(buf), gettext("unsupported encryption type %d"), enctype);
534     }
535 
536     return buf;
537 }
538 
539 static char *
540 flags_string(cred)
541     register krb5_creds *cred;
542 {
543     static char buf[32];
544     int i = 0;
545 
546     if (cred->ticket_flags & TKT_FLG_FORWARDABLE)
547 	buf[i++] = 'F';
548     if (cred->ticket_flags & TKT_FLG_FORWARDED)
549 	buf[i++] = 'f';
550     if (cred->ticket_flags & TKT_FLG_PROXIABLE)
551 	buf[i++] = 'P';
552     if (cred->ticket_flags & TKT_FLG_PROXY)
553 	buf[i++] = 'p';
554     if (cred->ticket_flags & TKT_FLG_MAY_POSTDATE)
555 	buf[i++] = 'D';
556     if (cred->ticket_flags & TKT_FLG_POSTDATED)
557 	buf[i++] = 'd';
558     if (cred->ticket_flags & TKT_FLG_INVALID)
559 	buf[i++] = 'i';
560     if (cred->ticket_flags & TKT_FLG_RENEWABLE)
561 	buf[i++] = 'R';
562     if (cred->ticket_flags & TKT_FLG_INITIAL)
563 	buf[i++] = 'I';
564     if (cred->ticket_flags & TKT_FLG_HW_AUTH)
565 	buf[i++] = 'H';
566     if (cred->ticket_flags & TKT_FLG_PRE_AUTH)
567 	buf[i++] = 'A';
568     if (cred->ticket_flags & TKT_FLG_TRANSIT_POLICY_CHECKED)
569 	buf[i++] = 'T';
570     if (cred->ticket_flags & TKT_FLG_OK_AS_DELEGATE)
571 	buf[i++] = 'O';		/* D/d are taken.  Use short strings?  */
572     if (cred->ticket_flags & TKT_FLG_ANONYMOUS)
573 	buf[i++] = 'a';
574     buf[i] = '\0';
575     return(buf);
576 }
577 
578 void
579 printtime(tv)
580     time_t tv;
581 {
582     char timestring[BUFSIZ];
583     char fill;
584 
585     fill = ' ';
586 	if (!krb5_timestamp_to_sfstring((krb5_timestamp) tv, timestring,
587 					timestamp_width+1, &fill)) {
588 	printf(timestring);
589     }
590 }
591 
592 void
593 show_credential(cred)
594     register krb5_creds * cred;
595 {
596     krb5_error_code retval;
597     krb5_ticket *tkt;
598     char *name, *sname, *flags;
599     int	extra_field = 0;
600 
601     retval = krb5_unparse_name(kcontext, cred->client, &name);
602     if (retval) {
603 		com_err(progname, retval,
604 			gettext("while unparsing client name"));
605 	return;
606     }
607     retval = krb5_unparse_name(kcontext, cred->server, &sname);
608     if (retval) {
609 		com_err(progname, retval,
610 			gettext("while unparsing server name"));
611 		krb5_free_unparsed_name(kcontext, name);
612 	return;
613     }
614     if (!cred->times.starttime)
615 	cred->times.starttime = cred->times.authtime;
616 
617     printtime(cred->times.starttime);
618     putchar(' '); putchar(' ');
619     printtime(cred->times.endtime);
620     putchar(' '); putchar(' ');
621 
622     printf("%s\n", sname);
623 
624     if (strcmp(name, defname)) {
625 		printf(gettext("\tfor client %s"), name);
626 	    extra_field++;
627     }
628 
629     if (cred->times.renew_till) {
630 	if (!extra_field)
631 		fputs("\t",stdout);
632 	else
633 		fputs(", ",stdout);
634 	fputs(gettext("renew until "), stdout);
635 	printtime(cred->times.renew_till);
636 	extra_field += 2;
637     }
638 
639     if (extra_field > 3) {
640 	fputs("\n", stdout);
641 	extra_field = 0;
642     }
643 
644     if (show_flags) {
645 	flags = flags_string(cred);
646 	if (flags && *flags) {
647 	    if (!extra_field)
648 		fputs("\t",stdout);
649 	    else
650 		fputs(", ",stdout);
651 			printf(gettext("Flags: %s"), flags);
652 	    extra_field++;
653 	}
654     }
655 
656     if (extra_field > 2) {
657 	fputs("\n", stdout);
658 	extra_field = 0;
659     }
660 
661     if (show_etype) {
662 	retval = decode_krb5_ticket(&cred->ticket, &tkt);
663 	if (retval)
664 	    goto err_tkt;
665 
666 	if (!extra_field)
667 	    fputs("\t",stdout);
668 	else
669 	    fputs(", ",stdout);
670 	printf(gettext("Etype(skey, tkt): %s, "),
671 	       etype_string(cred->keyblock.enctype));
672 	printf("%s ",
673 	       etype_string(tkt->enc_part.enctype));
674 	extra_field++;
675 
676     err_tkt:
677 	if (tkt != NULL)
678 	    krb5_free_ticket(kcontext, tkt);
679     }
680 
681     /* if any additional info was printed, extra_field is non-zero */
682     if (extra_field)
683 	putchar('\n');
684 
685 
686     if (show_addresses) {
687 	if (!cred->addresses || !cred->addresses[0]) {
688 	    printf(gettext("\tAddresses: (none)\n"));
689 	} else {
690 	    int i;
691 
692 	    printf(gettext("\tAddresses: "));
693 	    one_addr(cred->addresses[0]);
694 
695 	    for (i=1; cred->addresses[i]; i++) {
696 		printf(", ");
697 		one_addr(cred->addresses[i]);
698 	    }
699 
700 	    printf("\n");
701 	}
702     }
703 
704     krb5_free_unparsed_name(kcontext, name);
705     krb5_free_unparsed_name(kcontext, sname);
706 }
707 
708 #include "port-sockets.h"
709 #include "socket-utils.h" /* for ss2sin etc */
710 #include <fake-addrinfo.h>
711 
712 void one_addr(a)
713     krb5_address *a;
714 {
715     struct sockaddr_storage ss;
716     int err;
717     char namebuf[NI_MAXHOST];
718 
719     memset (&ss, 0, sizeof (ss));
720 
721     switch (a->addrtype) {
722     case ADDRTYPE_INET:
723 	if (a->length != IPV4_ADDR_LEN) {
724 	broken:
725 	    printf ("broken address (type %d length %d)",
726 		    a->addrtype, a->length);
727 	    return;
728 	}
729 	{
730 	    struct sockaddr_in *sinp = ss2sin (&ss);
731 	    sinp->sin_family = AF_INET;
732 #ifdef HAVE_SA_LEN
733 	    sinp->sin_len = sizeof (struct sockaddr_in);
734 #endif
735 	    memcpy (&sinp->sin_addr, a->contents, IPV4_ADDR_LEN);
736 	}
737 	break;
738 #ifdef KRB5_USE_INET6
739     case ADDRTYPE_INET6:
740 	if (a->length != IPV6_ADDR_LEN)
741 	    goto broken;
742 	{
743 	    struct sockaddr_in6 *sin6p = ss2sin6 (&ss);
744 	    sin6p->sin6_family = AF_INET6;
745 #ifdef HAVE_SA_LEN
746 	    sin6p->sin6_len = sizeof (struct sockaddr_in6);
747 #endif
748 	    memcpy (&sin6p->sin6_addr, a->contents, IPV6_ADDR_LEN);
749 	}
750 	break;
751 #endif
752     default:
753 	printf(gettext("unknown addr type %d"), a->addrtype);
754 	return;
755     }
756 
757     namebuf[0] = 0;
758     err = getnameinfo (ss2sa (&ss), socklen (ss2sa (&ss)),
759 		       namebuf, sizeof (namebuf), 0, 0,
760 		       no_resolve ? NI_NUMERICHOST : 0U);
761     if (err) {
762 	printf (gettext("unprintable address (type %d, error %d %s)"), a->addrtype, err,
763 		gai_strerror (err));
764 	return;
765     }
766     printf ("%s", namebuf);
767 }
768 
769 void
770 fillit(f, num, c)
771     FILE		*f;
772     unsigned int	num;
773     int			c;
774 {
775     int i;
776 
777     for (i=0; i<num; i++)
778 	fputc(c, f);
779 }
780 
781 #ifdef KRB5_KRB4_COMPAT
782 void
783 do_v4_ccache(name)
784     char * name;
785 {
786     char    pname[ANAME_SZ];
787     char    pinst[INST_SZ];
788     char    prealm[REALM_SZ];
789     char    *file;
790     int     k_errno;
791     CREDENTIALS c;
792     int     header = 1;
793 
794     if (!got_k4)
795 	return;
796 
797     file = name?name:tkt_string();
798 
799     if (status_only) {
800 	fprintf(stderr,
801 		"%s: exit status option not supported for Kerberos 4\n",
802 		progname);
803 	exit(1);
804     }
805 
806     if (got_k5)
807 	printf("\n\n");
808 
809     printf("Kerberos 4 ticket cache: %s\n", file);
810 
811     /*
812      * Since krb_get_tf_realm will return a ticket_file error,
813      * we will call tf_init and tf_close first to filter out
814      * things like no ticket file.  Otherwise, the error that
815      * the user would see would be
816      * klist: can't find realm of ticket file: No ticket file (tf_util)
817      * instead of
818      * klist: No ticket file (tf_util)
819      */
820 
821     /* Open ticket file */
822     k_errno = tf_init(file, R_TKT_FIL);
823     if (k_errno) {
824 	fprintf(stderr, "%s: %s\n", progname, krb_get_err_text (k_errno));
825 	exit(1);
826     }
827     /* Close ticket file */
828     (void) tf_close();
829 
830     /*
831      * We must find the realm of the ticket file here before calling
832      * tf_init because since the realm of the ticket file is not
833      * really stored in the principal section of the file, the
834      * routine we use must itself call tf_init and tf_close.
835      */
836     if ((k_errno = krb_get_tf_realm(file, prealm)) != KSUCCESS) {
837 	fprintf(stderr, "%s: can't find realm of ticket file: %s\n",
838 		progname, krb_get_err_text (k_errno));
839 	exit(1);
840     }
841 
842     /* Open ticket file */
843     if ((k_errno = tf_init(file, R_TKT_FIL))) {
844 	fprintf(stderr, "%s: %s\n", progname, krb_get_err_text (k_errno));
845 	exit(1);
846     }
847     /* Get principal name and instance */
848     if ((k_errno = tf_get_pname(pname)) ||
849 	(k_errno = tf_get_pinst(pinst))) {
850 	fprintf(stderr, "%s: %s\n", progname, krb_get_err_text (k_errno));
851 	exit(1);
852     }
853 
854     /*
855      * You may think that this is the obvious place to get the
856      * realm of the ticket file, but it can't be done here as the
857      * routine to do this must open the ticket file.  This is why
858      * it was done before tf_init.
859      */
860 
861     printf("Principal: %s%s%s%s%s\n\n", pname,
862 	   (pinst[0] ? "." : ""), pinst,
863 	   (prealm[0] ? "@" : ""), prealm);
864     while ((k_errno = tf_get_cred(&c)) == KSUCCESS) {
865 	if (header) {
866 	    printf("%-18s  %-18s  %s\n",
867 		   "  Issued", "  Expires", "  Principal");
868 	    header = 0;
869 	}
870 	printtime(c.issue_date);
871 	fputs("  ", stdout);
872 	printtime(krb_life_to_time(c.issue_date, c.lifetime));
873 	printf("  %s%s%s%s%s\n",
874 	       c.service, (c.instance[0] ? "." : ""), c.instance,
875 	       (c.realm[0] ? "@" : ""), c.realm);
876     }
877     if (header && k_errno == EOF) {
878 	printf("No tickets in file.\n");
879     }
880 }
881 #endif /* KRB4_KRB5_COMPAT */
882