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