xref: /freebsd/crypto/krb5/src/kadmin/dbutil/tabdump.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* kdc/tabdump.c - reporting-friendly tabular KDB dumps */
3 /*
4  * Copyright (C) 2015 by the Massachusetts Institute of Technology.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  *   notice, this list of conditions and the following disclaimer.
13  *
14  * * Redistributions in binary form must reproduce the above copyright
15  *   notice, this list of conditions and the following disclaimer in
16  *   the documentation and/or other materials provided with the
17  *   distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
24  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
30  * OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include <k5-int.h>
34 #include "k5-platform.h"        /* for asprintf */
35 #include "k5-hex.h"
36 
37 #include <limits.h>
38 #include <stdio.h>
39 #include <string.h>
40 #include <unistd.h>
41 
42 #include <kadm5/admin.h>
43 #include <kadm5/server_internal.h>
44 
45 #include "adm_proto.h"
46 #include "kdb5_util.h"
47 #include "tdumputil.h"
48 
49 struct tdopts {
50     int csv;                    /* 1 for CSV, 0 for tab-separated */
51     int emptyhex_empty;         /* print empty hex strings as "" not "-1" */
52     int numeric;                /* numeric instead of symbolic output */
53     int omitheader;             /* omit field headers */
54     int writerectype;           /* write record type prefix */
55     char *fname;                /* output file name */
56 };
57 
58 struct rec_args;
59 
60 typedef int (tdump_princ_fn)(struct rec_args *, const char *, krb5_db_entry *);
61 typedef int (tdump_policy_fn)(struct rec_args *, const char *,
62                               kadm5_policy_ent_t);
63 
64 /* Descriptor for a tabdump record type */
65 struct tdtype {
66     const char *rectype;
67     char * const *fieldnames;
68     tdump_princ_fn *princ_fn;
69     tdump_policy_fn *policy_fn;
70 };
71 
72 static tdump_princ_fn keydata;
73 static tdump_princ_fn keyinfo;
74 static tdump_princ_fn princ_flags;
75 static tdump_princ_fn princ_lockout;
76 static tdump_princ_fn princ_meta;
77 static tdump_princ_fn princ_stringattrs;
78 static tdump_princ_fn princ_tktpolicy;
79 
80 static char * const keydata_fields[] = {
81     "name", "keyindex", "kvno", "enctype", "key", "salttype", "salt", NULL
82 };
83 static char * const keyinfo_fields[] = {
84     "name", "keyindex", "kvno", "enctype", "salttype", "salt", NULL
85 };
86 static char * const princ_flags_fields[] = {
87     "name", "flag", "value", NULL
88 };
89 static char * const princ_lockout_fields[] = {
90     "name", "last_success", "last_failed", "fail_count", NULL
91 };
92 static char * const princ_meta_fields[] = {
93     "name", "modby", "modtime", "lastpwd", "policy", "mkvno", "hist_kvno", NULL
94 };
95 static char * const princ_stringattrs_fields[] = {
96     "name", "key", "value", NULL
97 };
98 static char * const princ_tktpolicy_fields[] = {
99     "name", "expiration", "pw_expiration", "max_life", "max_renew_life", NULL
100 };
101 
102 /* Lookup table for tabdump record types */
103 static struct tdtype tdtypes[] = {
104     {"keydata", keydata_fields, keydata, NULL},
105     {"keyinfo", keyinfo_fields, keyinfo, NULL},
106     {"princ_flags", princ_flags_fields, princ_flags, NULL},
107     {"princ_lockout", princ_lockout_fields, princ_lockout, NULL},
108     {"princ_meta", princ_meta_fields, princ_meta, NULL},
109     {"princ_stringattrs", princ_stringattrs_fields, princ_stringattrs, NULL},
110     {"princ_tktpolicy", princ_tktpolicy_fields, princ_tktpolicy, NULL},
111 };
112 #define NTDTYPES (sizeof(tdtypes)/sizeof(tdtypes[0]))
113 
114 /* State to pass to KDB iterator */
115 struct rec_args {
116     FILE *f;
117     struct tdtype *tdtype;
118     struct rechandle *rh;
119     struct tdopts *opts;
120 };
121 
122 /* Decode the KADM_DATA from a DB entry.*/
123 static int
get_adb(krb5_db_entry * dbe,osa_princ_ent_rec * adb)124 get_adb(krb5_db_entry *dbe, osa_princ_ent_rec *adb)
125 {
126     XDR xdrs;
127     int success;
128     krb5_tl_data tl_data;
129     krb5_error_code ret;
130 
131     memset(adb, 0, sizeof(*adb));
132     tl_data.tl_data_type = KRB5_TL_KADM_DATA;
133     ret = krb5_dbe_lookup_tl_data(util_context, dbe, &tl_data);
134     if (ret != 0 || tl_data.tl_data_length == 0)
135         return 0;
136     xdrmem_create(&xdrs, (caddr_t)tl_data.tl_data_contents,
137                   tl_data.tl_data_length, XDR_DECODE);
138     success = xdr_osa_princ_ent_rec(&xdrs, adb);
139     xdr_destroy(&xdrs);
140     return success;
141 }
142 
143 /* Write a date field as an ISO 8601 UTC date/time representation. */
144 static int
write_date_iso(struct rec_args * args,krb5_timestamp when)145 write_date_iso(struct rec_args *args, krb5_timestamp when)
146 {
147     char buf[64];
148     time_t t;
149     struct tm *tm = NULL;
150     struct rechandle *h = args->rh;
151 
152     t = ts2tt(when);
153     tm = gmtime(&t);
154     if (tm == NULL) {
155         errno = EINVAL;
156         return -1;
157     }
158     if (strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%SZ", tm) == 0) {
159         errno = EINVAL;
160         return -1;
161     }
162     if (writefield(h, "%s", buf) < 0)
163         return -1;
164     return 0;
165 }
166 
167 /* Write a date field, optionally as a decimal POSIX timestamp. */
168 static int
write_date(struct rec_args * args,krb5_timestamp when)169 write_date(struct rec_args *args, krb5_timestamp when)
170 {
171     struct tdopts *opts = args->opts;
172     struct rechandle *h = args->rh;
173 
174     if (opts->numeric)
175         return writefield(h, "%d", when);
176 
177     return write_date_iso(args, when);
178 }
179 
180 /* Write an enctype field, optionally as decimal. */
181 static krb5_error_code
write_enctype(struct rec_args * args,krb5_int16 etype)182 write_enctype(struct rec_args *args, krb5_int16 etype)
183 {
184     char buf[256];
185     krb5_error_code ret;
186     struct rechandle *h = args->rh;
187     struct tdopts *opts = args->opts;
188 
189     if (!opts->numeric) {
190         ret = krb5_enctype_to_name(etype, 0, buf, sizeof(buf));
191         if (ret == 0) {
192             if (writefield(h, "%s", buf) < 0)
193                 return errno;
194             return ret;
195         }
196     }
197     /* decimal if requested, or if conversion failed */
198     if (writefield(h, "%d", etype) < 0)
199         return errno;
200     return 0;
201 }
202 
203 /* Write a salttype field, optionally as decimal. */
204 static krb5_error_code
write_salttype(struct rec_args * args,krb5_int16 salttype)205 write_salttype(struct rec_args *args, krb5_int16 salttype)
206 {
207     char buf[256];
208     krb5_error_code ret;
209     struct rechandle *h = args->rh;
210     struct tdopts *opts = args->opts;
211 
212     if (!opts->numeric) {
213         ret = krb5_salttype_to_string(salttype, buf, sizeof(buf));
214         if (ret == 0) {
215             if (writefield(h, "%s", buf) < 0)
216                 return errno;
217             return ret;
218         }
219     }
220     /* decimal if requested, or if conversion failed */
221     if (writefield(h, "%d", salttype) < 0)
222         return errno;
223     return 0;
224 }
225 
226 /*
227  * Write a field of bytes from krb5_data as a hexadecimal string.  Write empty
228  * strings as "-1" unless requested.
229  */
230 static int
write_data(struct rec_args * args,krb5_data * data)231 write_data(struct rec_args *args, krb5_data *data)
232 {
233     int ret;
234     char *hex;
235     struct rechandle *h = args->rh;
236     struct tdopts *opts = args->opts;
237 
238     if (data->length == 0 && !opts->emptyhex_empty) {
239         if (writefield(h, "-1") < 0)
240             return -1;
241         return 0;
242     }
243 
244     ret = k5_hex_encode(data->data, data->length, FALSE, &hex);
245     if (ret) {
246         errno = ret;
247         return -1;
248     }
249 
250     ret = writefield(h, "%s", hex);
251     free(hex);
252     return ret;
253 }
254 
255 /* Write a single record of a keydata/keyinfo key set. */
256 static krb5_error_code
keyinfo_rec(struct rec_args * args,const char * name,int i,krb5_key_data * kd,int dumpkeys)257 keyinfo_rec(struct rec_args *args, const char *name, int i, krb5_key_data *kd,
258             int dumpkeys)
259 {
260     int ret;
261     krb5_data data;
262     struct rechandle *h = args->rh;
263 
264     if (startrec(h) < 0)
265         return errno;
266     if (writefield(h, "%s", name) < 0)
267         return errno;
268     if (writefield(h, "%d", i) < 0)
269         return errno;
270     if (writefield(h, "%d", kd->key_data_kvno) < 0)
271         return errno;
272     if (write_enctype(args, kd->key_data_type[0]) < 0)
273         return errno;
274     if (dumpkeys) {
275         data.length = kd->key_data_length[0];
276         data.data = (void *)kd->key_data_contents[0];
277         if (write_data(args, &data) < 0)
278             return errno;
279     }
280     ret = write_salttype(args, kd->key_data_type[1]);
281     if (ret)
282         return ret;
283     data.length = kd->key_data_length[1];
284     data.data = (void *)kd->key_data_contents[1];
285     if (write_data(args, &data) < 0)
286         return errno;
287     if (endrec(h) < 0)
288         return errno;
289     return 0;
290 }
291 
292 /* Write out a principal's key set, optionally including actual key data. */
293 static krb5_error_code
keyinfo_common(struct rec_args * args,const char * name,krb5_db_entry * entry,int dumpkeys)294 keyinfo_common(struct rec_args *args, const char *name, krb5_db_entry *entry,
295                int dumpkeys)
296 {
297     krb5_error_code ret;
298     krb5_key_data kd;
299     int i;
300 
301     for (i = 0; i < entry->n_key_data; i++) {
302         kd = entry->key_data[i];
303         /* missing salt data -> normal salt */
304         if (kd.key_data_ver == 1) {
305             kd.key_data_ver = 2;
306             kd.key_data_type[1] = KRB5_KDB_SALTTYPE_NORMAL;
307             kd.key_data_length[1] = 0;
308             kd.key_data_contents[1] = NULL;
309         }
310         ret = keyinfo_rec(args, name, i, &kd, dumpkeys);
311         if (ret)
312             return ret;
313     }
314     return 0;
315 }
316 
317 /* Write a principal's key data. */
318 static krb5_error_code
keydata(struct rec_args * args,const char * name,krb5_db_entry * dbe)319 keydata(struct rec_args *args, const char *name, krb5_db_entry *dbe)
320 {
321     return keyinfo_common(args, name, dbe, 1);
322 }
323 
324 /* Write a principal's key info (suppressing actual key data). */
325 static krb5_error_code
keyinfo(struct rec_args * args,const char * name,krb5_db_entry * dbe)326 keyinfo(struct rec_args *args, const char *name, krb5_db_entry *dbe)
327 {
328     return keyinfo_common(args, name, dbe, 0);
329 }
330 
331 /* Write a record corresponding to a single principal flag setting. */
332 static krb5_error_code
princflag_rec(struct rechandle * h,const char * name,const char * flagname,int set)333 princflag_rec(struct rechandle *h, const char *name, const char *flagname,
334               int set)
335 {
336     if (startrec(h) < 0)
337         return errno;
338     if (writefield(h, "%s", name) < 0)
339         return errno;
340     if (writefield(h, "%s", flagname) < 0)
341         return errno;
342     if (writefield(h, "%d", set) < 0)
343         return errno;
344     if (endrec(h) < 0)
345         return errno;
346     return 0;
347 }
348 
349 /* Write a principal's flag settings. */
350 static krb5_error_code
princ_flags(struct rec_args * args,const char * name,krb5_db_entry * dbe)351 princ_flags(struct rec_args *args, const char *name, krb5_db_entry *dbe)
352 {
353     int i;
354     char *s = NULL;
355     krb5_flags flags = dbe->attributes;
356     krb5_error_code ret;
357     struct tdopts *opts = args->opts;
358     struct rechandle *h = args->rh;
359 
360     for (i = 0; i < 32; i++) {
361         if (opts->numeric) {
362             if (asprintf(&s, "0x%08lx", 1UL << i) == -1)
363                 return ENOMEM;
364         } else {
365             ret = krb5_flagnum_to_string(i, &s);
366             if (ret)
367                 return ret;
368             /* Don't print unknown flags if they're not set and numeric output
369              * isn't requested. */
370             if (!(flags & (1UL << i)) && strncmp(s, "0x", 2) == 0) {
371                 free(s);
372                 continue;
373             }
374         }
375         ret = princflag_rec(h, name, s, ((flags & (1UL << i)) != 0));
376         free(s);
377         if (ret)
378             return ret;
379     }
380     return 0;
381 }
382 
383 /* Write a principal's lockout data. */
384 static krb5_error_code
princ_lockout(struct rec_args * args,const char * name,krb5_db_entry * dbe)385 princ_lockout(struct rec_args *args, const char *name, krb5_db_entry *dbe)
386 {
387     struct rechandle *h = args->rh;
388 
389     if (startrec(h) < 0)
390         return errno;
391     if (writefield(h, "%s", name) < 0)
392         return errno;
393     if (write_date(args, dbe->last_success) < 0)
394         return errno;
395     if (write_date(args, dbe->last_failed) < 0)
396         return errno;
397     if (writefield(h, "%d", dbe->fail_auth_count) < 0)
398         return errno;
399     if (endrec(h) < 0)
400         return errno;
401     return 0;
402 }
403 
404 /* Write a principal's metadata. */
405 static krb5_error_code
princ_meta(struct rec_args * args,const char * name,krb5_db_entry * dbe)406 princ_meta(struct rec_args *args, const char *name, krb5_db_entry *dbe)
407 {
408     int got_adb = 0;
409     char *modby;
410     krb5_kvno mkvno;
411     const char *policy;
412     krb5_principal mod_princ = NULL;
413     krb5_timestamp mod_time, last_pwd;
414     krb5_error_code ret;
415     osa_princ_ent_rec adb;
416     struct rechandle *h = args->rh;
417 
418     memset(&adb, 0, sizeof(adb));
419     if (startrec(h) < 0)
420         return errno;
421     if (writefield(h, "%s", name) < 0)
422         return errno;
423 
424     ret = krb5_dbe_lookup_last_pwd_change(util_context, dbe, &last_pwd);
425     if (ret)
426         return ret;
427     ret = krb5_dbe_get_mkvno(util_context, dbe, &mkvno);
428     if (ret)
429         return ret;
430 
431     ret = krb5_dbe_lookup_mod_princ_data(util_context, dbe, &mod_time,
432                                          &mod_princ);
433     if (ret)
434         return ret;
435     ret = krb5_unparse_name(util_context, mod_princ, &modby);
436     krb5_free_principal(util_context, mod_princ);
437     if (ret)
438         return ret;
439     ret = writefield(h, "%s", modby);
440     krb5_free_unparsed_name(util_context, modby);
441     if (ret < 0)
442         return errno;
443 
444     if (write_date(args, mod_time) < 0)
445         return errno;
446     if (write_date(args, last_pwd) < 0)
447         return errno;
448 
449     got_adb = get_adb(dbe, &adb);
450     if (got_adb && adb.policy != NULL)
451         policy = adb.policy;
452     else
453         policy = "";
454     ret = writefield(h, "%s", policy);
455     if (ret < 0) {
456         ret = errno;
457         goto cleanup;
458     }
459     if (writefield(h, "%d", mkvno) < 0) {
460         ret = errno;
461         goto cleanup;
462     }
463     if (writefield(h, "%d", adb.admin_history_kvno) < 0) {
464         ret = errno;
465         goto cleanup;
466     }
467     if (endrec(h) < 0)
468         ret = errno;
469     else
470         ret = 0;
471 
472 cleanup:
473     kdb_free_entry(NULL, NULL, &adb);
474     return ret;
475 }
476 
477 /* Write a principal's string attributes. */
478 static krb5_error_code
princ_stringattrs(struct rec_args * args,const char * name,krb5_db_entry * dbe)479 princ_stringattrs(struct rec_args *args, const char *name, krb5_db_entry *dbe)
480 {
481     int i, nattrs;
482     krb5_error_code ret;
483     krb5_string_attr *attrs;
484     struct rechandle *h = args->rh;
485 
486     ret = krb5_dbe_get_strings(util_context, dbe, &attrs, &nattrs);
487     if (ret)
488         return ret;
489     for (i = 0; i < nattrs; i++) {
490         if (startrec(h) < 0) {
491             ret = errno;
492             goto cleanup;
493         }
494         if (writefield(h, "%s", name) < 0) {
495             ret = errno;
496             goto cleanup;
497         }
498         if (writefield(h, "%s", attrs[i].key) < 0) {
499             ret = errno;
500             goto cleanup;
501         }
502         if (writefield(h, "%s", attrs[i].value) < 0) {
503             ret = errno;
504             goto cleanup;
505         }
506         if (endrec(h) < 0) {
507             ret = errno;
508             goto cleanup;
509         }
510     }
511 cleanup:
512     krb5_dbe_free_strings(util_context, attrs, nattrs);
513     return ret;
514 }
515 
516 /* Write a principal's ticket policy. */
517 static krb5_error_code
princ_tktpolicy(struct rec_args * args,const char * name,krb5_db_entry * dbe)518 princ_tktpolicy(struct rec_args *args, const char *name, krb5_db_entry *dbe)
519 {
520     struct rechandle *h = args->rh;
521 
522     if (startrec(h) < 0)
523         return errno;
524     if (writefield(h, "%s", name) < 0)
525         return errno;
526     if (write_date(args, dbe->expiration) < 0)
527         return errno;
528     if (write_date(args, dbe->pw_expiration) < 0)
529         return errno;
530     if (writefield(h, "%d", dbe->max_life) < 0)
531         return errno;
532     if (writefield(h, "%d", dbe->max_renewable_life) < 0)
533         return errno;
534     if (endrec(h) < 0)
535         return errno;
536     return 0;
537 }
538 
539 /* Iterator function for krb5_db_iterate() */
540 static krb5_error_code
tditer(void * ptr,krb5_db_entry * entry)541 tditer(void *ptr, krb5_db_entry *entry)
542 {
543     krb5_error_code ret;
544     struct rec_args *args = ptr;
545     char *name;
546 
547     ret = krb5_unparse_name(util_context, entry->princ, &name);
548     if (ret) {
549         com_err(progname, ret, _("while unparsing principal name"));
550         return ret;
551     }
552     ret = args->tdtype->princ_fn(args, name, entry);
553     krb5_free_unparsed_name(util_context, name);
554     if (ret)
555         return ret;
556     return 0;
557 }
558 
559 /* Set up state structure for the iterator. */
560 static krb5_error_code
setup_args(struct rec_args * args,struct tdtype * tdtype,struct tdopts * opts)561 setup_args(struct rec_args *args, struct tdtype *tdtype,
562              struct tdopts *opts)
563 {
564     FILE *f = NULL;
565     const char *rectype = NULL;
566     struct rechandle *rh;
567 
568     args->tdtype = tdtype;
569     args->opts = opts;
570     if (opts->fname != NULL && strcmp(opts->fname, "-") != 0) {
571         f = fopen(opts->fname, "w");
572         if (f == NULL) {
573             com_err(progname, errno, _("opening %s for writing"),
574                     opts->fname);
575             return errno;
576         }
577         args->f = f;
578     } else {
579         f = stdout;
580         args->f = NULL;
581     }
582     if (opts->writerectype)
583         rectype = tdtype->rectype;
584     if (opts->csv)
585         rh = rechandle_csv(f, rectype);
586     else
587         rh = rechandle_tabsep(f, rectype);
588     if (rh == NULL)
589         return ENOMEM;
590     args->rh = rh;
591     if (!opts->omitheader && writeheader(rh, tdtype->fieldnames) < 0)
592         return errno;
593     return 0;
594 }
595 
596 /* Clean up the state structure. */
597 static void
cleanup_args(struct rec_args * args)598 cleanup_args(struct rec_args *args)
599 {
600     rechandle_free(args->rh);
601     if (args->f != NULL)
602         fclose(args->f);
603 }
604 
605 void
tabdump(int argc,char ** argv)606 tabdump(int argc, char **argv)
607 {
608     int ch;
609     size_t i;
610     const char *rectype;
611     struct rec_args args;
612     struct tdopts opts;
613     krb5_error_code ret;
614 
615     memset(&opts, 0, sizeof(opts));
616     memset(&args, 0, sizeof(args));
617     optind = 1;
618     while ((ch = getopt(argc, argv, "Hceno:")) != -1) {
619         switch (ch) {
620         case 'H':
621             opts.omitheader = 1;
622             break;
623         case 'c':
624             opts.csv = 1;
625             break;
626         case 'e':
627             opts.emptyhex_empty = 1;
628             break;
629         case 'n':
630             opts.numeric = 1;
631             break;
632         case 'o':
633             opts.fname = optarg;
634             break;
635         case '?':
636         default:
637             usage();
638             break;
639         }
640     }
641     if (argc - optind < 1)
642         usage();
643     rectype = argv[optind];
644     for (i = 0; i < NTDTYPES; i++) {
645         if (strcmp(rectype, tdtypes[i].rectype) == 0) {
646             setup_args(&args, &tdtypes[i], &opts);
647             break;
648         }
649     }
650     if (i >= NTDTYPES)
651         usage();
652     ret = krb5_db_iterate(util_context, NULL, tditer, &args, 0);
653     cleanup_args(&args);
654     if (ret) {
655         com_err(progname, ret, _("performing tabular dump"));
656         exit_status++;
657     }
658 }
659