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