klist.c revision 1.1.1.2
1/*	$NetBSD: klist.c,v 1.1.1.2 2014/04/24 12:45:28 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
43static char*
44printable_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
57static char*
58printable_time(time_t t)
59{
60    return printable_time_internal(t, 20);
61}
62
63static char*
64printable_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
78static void
79print_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
134static void
135print_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
228static void
229print_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
333static int
334check_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
379static void
380display_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
450static int
451display_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
489static int
490list_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
573int
574klist(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