klist.c revision 178825
1/*
2 * Copyright (c) 1997-2004 Kungliga Tekniska H�gskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of the Institute nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#include "kuser_locl.h"
35#include "rtbl.h"
36
37RCSID("$Id: klist.c 20516 2007-04-22 10:40:41Z lha $");
38
39static char*
40printable_time(time_t t)
41{
42    static char s[128];
43    strlcpy(s, ctime(&t)+ 4, sizeof(s));
44    s[15] = 0;
45    return s;
46}
47
48static char*
49printable_time_long(time_t t)
50{
51    static char s[128];
52    strlcpy(s, ctime(&t)+ 4, sizeof(s));
53    s[20] = 0;
54    return s;
55}
56
57#define COL_ISSUED		"  Issued"
58#define COL_EXPIRES		"  Expires"
59#define COL_FLAGS		"Flags"
60#define COL_PRINCIPAL		"  Principal"
61#define COL_PRINCIPAL_KVNO	"  Principal (kvno)"
62#define COL_CACHENAME		"  Cache name"
63
64static void
65print_cred(krb5_context context, krb5_creds *cred, rtbl_t ct, int do_flags)
66{
67    char *str;
68    krb5_error_code ret;
69    krb5_timestamp sec;
70
71    krb5_timeofday (context, &sec);
72
73
74    if(cred->times.starttime)
75	rtbl_add_column_entry(ct, COL_ISSUED,
76			      printable_time(cred->times.starttime));
77    else
78	rtbl_add_column_entry(ct, COL_ISSUED,
79			      printable_time(cred->times.authtime));
80
81    if(cred->times.endtime > sec)
82	rtbl_add_column_entry(ct, COL_EXPIRES,
83			      printable_time(cred->times.endtime));
84    else
85	rtbl_add_column_entry(ct, COL_EXPIRES, ">>>Expired<<<");
86    ret = krb5_unparse_name (context, cred->server, &str);
87    if (ret)
88	krb5_err(context, 1, ret, "krb5_unparse_name");
89    rtbl_add_column_entry(ct, COL_PRINCIPAL, str);
90    if(do_flags) {
91	char s[16], *sp = s;
92	if(cred->flags.b.forwardable)
93	    *sp++ = 'F';
94	if(cred->flags.b.forwarded)
95	    *sp++ = 'f';
96	if(cred->flags.b.proxiable)
97	    *sp++ = 'P';
98	if(cred->flags.b.proxy)
99	    *sp++ = 'p';
100	if(cred->flags.b.may_postdate)
101	    *sp++ = 'D';
102	if(cred->flags.b.postdated)
103	    *sp++ = 'd';
104	if(cred->flags.b.renewable)
105	    *sp++ = 'R';
106	if(cred->flags.b.initial)
107	    *sp++ = 'I';
108	if(cred->flags.b.invalid)
109	    *sp++ = 'i';
110	if(cred->flags.b.pre_authent)
111	    *sp++ = 'A';
112	if(cred->flags.b.hw_authent)
113	    *sp++ = 'H';
114	*sp++ = '\0';
115	rtbl_add_column_entry(ct, COL_FLAGS, s);
116    }
117    free(str);
118}
119
120static void
121print_cred_verbose(krb5_context context, krb5_creds *cred)
122{
123    int j;
124    char *str;
125    krb5_error_code ret;
126    int first_flag;
127    krb5_timestamp sec;
128
129    krb5_timeofday (context, &sec);
130
131    ret = krb5_unparse_name(context, cred->server, &str);
132    if(ret)
133	exit(1);
134    printf("Server: %s\n", str);
135    free (str);
136
137    ret = krb5_unparse_name(context, cred->client, &str);
138    if(ret)
139	exit(1);
140    printf("Client: %s\n", str);
141    free (str);
142
143    {
144	Ticket t;
145	size_t len;
146	char *s;
147
148	decode_Ticket(cred->ticket.data, cred->ticket.length, &t, &len);
149	ret = krb5_enctype_to_string(context, t.enc_part.etype, &s);
150	printf("Ticket etype: ");
151	if (ret == 0) {
152	    printf("%s", s);
153	    free(s);
154	} else {
155	    printf("unknown(%d)", t.enc_part.etype);
156	}
157	if(t.enc_part.kvno)
158	    printf(", kvno %d", *t.enc_part.kvno);
159	printf("\n");
160	if(cred->session.keytype != t.enc_part.etype) {
161	    ret = krb5_enctype_to_string(context, cred->session.keytype, &str);
162	    if(ret)
163		krb5_warn(context, ret, "session keytype");
164	    else {
165		printf("Session key: %s\n", str);
166		free(str);
167	    }
168	}
169	free_Ticket(&t);
170	printf("Ticket length: %lu\n", (unsigned long)cred->ticket.length);
171    }
172    printf("Auth time:  %s\n", printable_time_long(cred->times.authtime));
173    if(cred->times.authtime != cred->times.starttime)
174	printf("Start time: %s\n", printable_time_long(cred->times.starttime));
175    printf("End time:   %s", printable_time_long(cred->times.endtime));
176    if(sec > cred->times.endtime)
177	printf(" (expired)");
178    printf("\n");
179    if(cred->flags.b.renewable)
180	printf("Renew till: %s\n",
181	       printable_time_long(cred->times.renew_till));
182    printf("Ticket flags: ");
183#define PRINT_FLAG2(f, s) if(cred->flags.b.f) { if(!first_flag) printf(", "); printf("%s", #s); first_flag = 0; }
184#define PRINT_FLAG(f) PRINT_FLAG2(f, f)
185    first_flag = 1;
186    PRINT_FLAG(forwardable);
187    PRINT_FLAG(forwarded);
188    PRINT_FLAG(proxiable);
189    PRINT_FLAG(proxy);
190    PRINT_FLAG2(may_postdate, may-postdate);
191    PRINT_FLAG(postdated);
192    PRINT_FLAG(invalid);
193    PRINT_FLAG(renewable);
194    PRINT_FLAG(initial);
195    PRINT_FLAG2(pre_authent, pre-authenticated);
196    PRINT_FLAG2(hw_authent, hw-authenticated);
197    PRINT_FLAG2(transited_policy_checked, transited-policy-checked);
198    PRINT_FLAG2(ok_as_delegate, ok-as-delegate);
199    PRINT_FLAG(anonymous);
200    printf("\n");
201    printf("Addresses: ");
202    if (cred->addresses.len != 0) {
203	for(j = 0; j < cred->addresses.len; j++){
204	    char buf[128];
205	    size_t len;
206	    if(j) printf(", ");
207	    ret = krb5_print_address(&cred->addresses.val[j],
208				     buf, sizeof(buf), &len);
209
210	    if(ret == 0)
211		printf("%s", buf);
212	}
213    } else {
214	printf("addressless");
215    }
216    printf("\n\n");
217}
218
219/*
220 * Print all tickets in `ccache' on stdout, verbosily iff do_verbose.
221 */
222
223static void
224print_tickets (krb5_context context,
225	       krb5_ccache ccache,
226	       krb5_principal principal,
227	       int do_verbose,
228	       int do_flags,
229	       int do_hidden)
230{
231    krb5_error_code ret;
232    char *str;
233    krb5_cc_cursor cursor;
234    krb5_creds creds;
235    int32_t sec, usec;
236
237    rtbl_t ct = NULL;
238
239    ret = krb5_unparse_name (context, principal, &str);
240    if (ret)
241	krb5_err (context, 1, ret, "krb5_unparse_name");
242
243    printf ("%17s: %s:%s\n",
244	    "Credentials cache",
245	    krb5_cc_get_type(context, ccache),
246	    krb5_cc_get_name(context, ccache));
247    printf ("%17s: %s\n", "Principal", str);
248    free (str);
249
250    if(do_verbose)
251	printf ("%17s: %d\n", "Cache version",
252		krb5_cc_get_version(context, ccache));
253
254    krb5_get_kdc_sec_offset(context, &sec, &usec);
255
256    if (do_verbose && sec != 0) {
257	char buf[BUFSIZ];
258	int val;
259	int sig;
260
261	val = sec;
262	sig = 1;
263	if (val < 0) {
264	    sig = -1;
265	    val = -val;
266	}
267
268	unparse_time (val, buf, sizeof(buf));
269
270	printf ("%17s: %s%s\n", "KDC time offset",
271		sig == -1 ? "-" : "", buf);
272    }
273
274    printf("\n");
275
276    ret = krb5_cc_start_seq_get (context, ccache, &cursor);
277    if (ret)
278	krb5_err(context, 1, ret, "krb5_cc_start_seq_get");
279
280    if(!do_verbose) {
281	ct = rtbl_create();
282	rtbl_add_column(ct, COL_ISSUED, 0);
283	rtbl_add_column(ct, COL_EXPIRES, 0);
284	if(do_flags)
285	    rtbl_add_column(ct, COL_FLAGS, 0);
286	rtbl_add_column(ct, COL_PRINCIPAL, 0);
287	rtbl_set_separator(ct, "  ");
288    }
289    while ((ret = krb5_cc_next_cred (context,
290				     ccache,
291				     &cursor,
292				     &creds)) == 0) {
293	const char *str;
294	str = krb5_principal_get_comp_string(context, creds.server, 0);
295	if (!do_hidden && str && str[0] == '@') {
296	    ;
297	}else if(do_verbose){
298	    print_cred_verbose(context, &creds);
299	}else{
300	    print_cred(context, &creds, ct, do_flags);
301	}
302	krb5_free_cred_contents (context, &creds);
303    }
304    if(ret != KRB5_CC_END)
305	krb5_err(context, 1, ret, "krb5_cc_get_next");
306    ret = krb5_cc_end_seq_get (context, ccache, &cursor);
307    if (ret)
308	krb5_err (context, 1, ret, "krb5_cc_end_seq_get");
309    if(!do_verbose) {
310	rtbl_format(ct, stdout);
311	rtbl_destroy(ct);
312    }
313}
314
315/*
316 * Check if there's a tgt for the realm of `principal' and ccache and
317 * if so return 0, else 1
318 */
319
320static int
321check_for_tgt (krb5_context context,
322	       krb5_ccache ccache,
323	       krb5_principal principal,
324	       time_t *expiration)
325{
326    krb5_error_code ret;
327    krb5_creds pattern;
328    krb5_creds creds;
329    krb5_realm *client_realm;
330    int expired;
331
332    krb5_cc_clear_mcred(&pattern);
333
334    client_realm = krb5_princ_realm (context, principal);
335
336    ret = krb5_make_principal (context, &pattern.server,
337			       *client_realm, KRB5_TGS_NAME, *client_realm,
338			       NULL);
339    if (ret)
340	krb5_err (context, 1, ret, "krb5_make_principal");
341    pattern.client = principal;
342
343    ret = krb5_cc_retrieve_cred (context, ccache, 0, &pattern, &creds);
344    krb5_free_principal (context, pattern.server);
345    if (ret) {
346	if (ret == KRB5_CC_END)
347	    return 1;
348	krb5_err (context, 1, ret, "krb5_cc_retrieve_cred");
349    }
350
351    expired = time(NULL) > creds.times.endtime;
352
353    if (expiration)
354	*expiration = creds.times.endtime;
355
356    krb5_free_cred_contents (context, &creds);
357
358    return expired;
359}
360
361/*
362 * Print a list of all AFS tokens
363 */
364
365static void
366display_tokens(int do_verbose)
367{
368    uint32_t i;
369    unsigned char t[4096];
370    struct ViceIoctl parms;
371
372    parms.in = (void *)&i;
373    parms.in_size = sizeof(i);
374    parms.out = (void *)t;
375    parms.out_size = sizeof(t);
376
377    for (i = 0;; i++) {
378        int32_t size_secret_tok, size_public_tok;
379        unsigned char *cell;
380	struct ClearToken ct;
381	unsigned char *r = t;
382	struct timeval tv;
383	char buf1[20], buf2[20];
384
385	if(k_pioctl(NULL, VIOCGETTOK, &parms, 0) < 0) {
386	    if(errno == EDOM)
387		break;
388	    continue;
389	}
390	if(parms.out_size > sizeof(t))
391	    continue;
392	if(parms.out_size < sizeof(size_secret_tok))
393	    continue;
394	t[min(parms.out_size,sizeof(t)-1)] = 0;
395	memcpy(&size_secret_tok, r, sizeof(size_secret_tok));
396	/* dont bother about the secret token */
397	r += size_secret_tok + sizeof(size_secret_tok);
398	if (parms.out_size < (r - t) + sizeof(size_public_tok))
399	    continue;
400	memcpy(&size_public_tok, r, sizeof(size_public_tok));
401	r += sizeof(size_public_tok);
402	if (parms.out_size < (r - t) + size_public_tok + sizeof(int32_t))
403	    continue;
404	memcpy(&ct, r, size_public_tok);
405	r += size_public_tok;
406	/* there is a int32_t with length of cellname, but we dont read it */
407	r += sizeof(int32_t);
408	cell = r;
409
410	gettimeofday (&tv, NULL);
411	strlcpy (buf1, printable_time(ct.BeginTimestamp),
412		 sizeof(buf1));
413	if (do_verbose || tv.tv_sec < ct.EndTimestamp)
414	    strlcpy (buf2, printable_time(ct.EndTimestamp),
415		     sizeof(buf2));
416	else
417	    strlcpy (buf2, ">>> Expired <<<", sizeof(buf2));
418
419	printf("%s  %s  ", buf1, buf2);
420
421	if ((ct.EndTimestamp - ct.BeginTimestamp) & 1)
422	    printf("User's (AFS ID %d) tokens for %s", ct.ViceId, cell);
423	else
424	    printf("Tokens for %s", cell);
425	if (do_verbose)
426	    printf(" (%d)", ct.AuthHandle);
427	putchar('\n');
428    }
429}
430
431/*
432 * display the ccache in `cred_cache'
433 */
434
435static int
436display_v5_ccache (const char *cred_cache, int do_test, int do_verbose,
437		   int do_flags, int do_hidden)
438{
439    krb5_error_code ret;
440    krb5_context context;
441    krb5_ccache ccache;
442    krb5_principal principal;
443    int exit_status = 0;
444
445    ret = krb5_init_context (&context);
446    if (ret)
447	errx (1, "krb5_init_context failed: %d", ret);
448
449    if(cred_cache) {
450	ret = krb5_cc_resolve(context, cred_cache, &ccache);
451	if (ret)
452	    krb5_err (context, 1, ret, "%s", cred_cache);
453    } else {
454	ret = krb5_cc_default (context, &ccache);
455	if (ret)
456	    krb5_err (context, 1, ret, "krb5_cc_resolve");
457    }
458
459    ret = krb5_cc_get_principal (context, ccache, &principal);
460    if (ret) {
461	if(ret == ENOENT) {
462	    if (!do_test)
463		krb5_warnx(context, "No ticket file: %s",
464			   krb5_cc_get_name(context, ccache));
465	    return 1;
466	} else
467	    krb5_err (context, 1, ret, "krb5_cc_get_principal");
468    }
469    if (do_test)
470	exit_status = check_for_tgt (context, ccache, principal, NULL);
471    else
472	print_tickets (context, ccache, principal, do_verbose,
473		       do_flags, do_hidden);
474
475    ret = krb5_cc_close (context, ccache);
476    if (ret)
477	krb5_err (context, 1, ret, "krb5_cc_close");
478
479    krb5_free_principal (context, principal);
480    krb5_free_context (context);
481    return exit_status;
482}
483
484/*
485 *
486 */
487
488static int
489list_caches(void)
490{
491    krb5_cc_cache_cursor cursor;
492    krb5_context context;
493    krb5_error_code ret;
494    krb5_ccache id;
495    rtbl_t ct;
496
497    ret = krb5_init_context (&context);
498    if (ret)
499	errx (1, "krb5_init_context failed: %d", ret);
500
501    ret = krb5_cc_cache_get_first (context, NULL, &cursor);
502    if (ret == KRB5_CC_NOSUPP)
503	return 0;
504    else if (ret)
505	krb5_err (context, 1, ret, "krb5_cc_cache_get_first");
506
507    ct = rtbl_create();
508    rtbl_add_column(ct, COL_PRINCIPAL, 0);
509    rtbl_add_column(ct, COL_CACHENAME, 0);
510    rtbl_add_column(ct, COL_EXPIRES, 0);
511    rtbl_set_prefix(ct, "   ");
512    rtbl_set_column_prefix(ct, COL_PRINCIPAL, "");
513
514    while ((ret = krb5_cc_cache_next (context, cursor, &id)) == 0) {
515	krb5_principal principal;
516	char *name;
517
518	ret = krb5_cc_get_principal(context, id, &principal);
519	if (ret == 0) {
520	    time_t t;
521	    int expired = check_for_tgt (context, id, principal, &t);
522
523	    ret = krb5_unparse_name(context, principal, &name);
524	    if (ret == 0) {
525		rtbl_add_column_entry(ct, COL_PRINCIPAL, name);
526		rtbl_add_column_entry(ct, COL_CACHENAME,
527				      krb5_cc_get_name(context, id));
528		rtbl_add_column_entry(ct, COL_EXPIRES,
529				      expired ? ">>> Expired <<<" :
530				      printable_time(t));
531		free(name);
532		krb5_free_principal(context, principal);
533	    }
534	}
535	krb5_cc_close(context, id);
536    }
537
538    krb5_cc_cache_end_seq_get(context, cursor);
539
540    rtbl_format(ct, stdout);
541    rtbl_destroy(ct);
542
543    return 0;
544}
545
546/*
547 *
548 */
549
550static int version_flag		= 0;
551static int help_flag		= 0;
552static int do_verbose		= 0;
553static int do_list_caches	= 0;
554static int do_test		= 0;
555static int do_tokens		= 0;
556static int do_v5		= 1;
557static char *cred_cache;
558static int do_flags	 	= 0;
559static int do_hidden	 	= 0;
560
561static struct getargs args[] = {
562    { NULL, 'f', arg_flag, &do_flags },
563    { "cache",			'c', arg_string, &cred_cache,
564      "credentials cache to list", "cache" },
565    { "test",			't', arg_flag, &do_test,
566      "test for having tickets", NULL },
567    { NULL,			's', arg_flag, &do_test },
568    { "tokens",			'T',   arg_flag, &do_tokens,
569      "display AFS tokens", NULL },
570    { "v5",			'5',	arg_flag, &do_v5,
571      "display v5 cred cache", NULL},
572    { "list-caches",		'l', arg_flag, &do_list_caches,
573      "verbose output", NULL },
574    { "verbose",		'v', arg_flag, &do_verbose,
575      "verbose output", NULL },
576    { "hidden",			0,   arg_flag, &do_hidden,
577      "display hidden credentials", NULL },
578    { NULL,			'a', arg_flag, &do_verbose },
579    { NULL,			'n', arg_flag, &do_verbose },
580    { "version", 		0,   arg_flag, &version_flag,
581      "print version", NULL },
582    { "help",			0,   arg_flag, &help_flag,
583      NULL, NULL}
584};
585
586static void
587usage (int ret)
588{
589    arg_printusage (args,
590		    sizeof(args)/sizeof(*args),
591		    NULL,
592		    "");
593    exit (ret);
594}
595
596int
597main (int argc, char **argv)
598{
599    int optidx = 0;
600    int exit_status = 0;
601
602    setprogname (argv[0]);
603
604    if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
605	usage(1);
606
607    if (help_flag)
608	usage (0);
609
610    if(version_flag){
611	print_version(NULL);
612	exit(0);
613    }
614
615    argc -= optidx;
616    argv += optidx;
617
618    if (argc != 0)
619	usage (1);
620
621    if (do_list_caches) {
622	exit_status = list_caches();
623	return exit_status;
624    }
625
626    if (do_v5)
627	exit_status = display_v5_ccache (cred_cache, do_test,
628					 do_verbose, do_flags, do_hidden);
629
630    if (!do_test) {
631	if (do_tokens && k_hasafs ()) {
632	    if (do_v5)
633		printf ("\n");
634	    display_tokens (do_verbose);
635	}
636    }
637
638    return exit_status;
639}
640