Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/krb5/src/kadmin/dbutil/tabdump.c
34889 views
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 alias;
73
static tdump_princ_fn keydata;
74
static tdump_princ_fn keyinfo;
75
static tdump_princ_fn princ_flags;
76
static tdump_princ_fn princ_lockout;
77
static tdump_princ_fn princ_meta;
78
static tdump_princ_fn princ_stringattrs;
79
static tdump_princ_fn princ_tktpolicy;
80
81
static char * const keydata_fields[] = {
82
"name", "keyindex", "kvno", "enctype", "key", "salttype", "salt", NULL
83
};
84
static char * const keyinfo_fields[] = {
85
"name", "keyindex", "kvno", "enctype", "salttype", "salt", NULL
86
};
87
static char * const princ_flags_fields[] = {
88
"name", "flag", "value", NULL
89
};
90
static char * const princ_lockout_fields[] = {
91
"name", "last_success", "last_failed", "fail_count", NULL
92
};
93
static char * const princ_meta_fields[] = {
94
"name", "modby", "modtime", "lastpwd", "policy", "mkvno", "hist_kvno", NULL
95
};
96
static char * const princ_stringattrs_fields[] = {
97
"name", "key", "value", NULL
98
};
99
static char * const princ_tktpolicy_fields[] = {
100
"name", "expiration", "pw_expiration", "max_life", "max_renew_life", NULL
101
};
102
static char * const alias_fields[] = {
103
"aliasname", "targetname", NULL
104
};
105
106
/* Lookup table for tabdump record types */
107
static struct tdtype tdtypes[] = {
108
{"alias", alias_fields, alias, NULL},
109
{"keydata", keydata_fields, keydata, NULL},
110
{"keyinfo", keyinfo_fields, keyinfo, NULL},
111
{"princ_flags", princ_flags_fields, princ_flags, NULL},
112
{"princ_lockout", princ_lockout_fields, princ_lockout, NULL},
113
{"princ_meta", princ_meta_fields, princ_meta, NULL},
114
{"princ_stringattrs", princ_stringattrs_fields, princ_stringattrs, NULL},
115
{"princ_tktpolicy", princ_tktpolicy_fields, princ_tktpolicy, NULL},
116
};
117
#define NTDTYPES (sizeof(tdtypes)/sizeof(tdtypes[0]))
118
119
/* State to pass to KDB iterator */
120
struct rec_args {
121
FILE *f;
122
struct tdtype *tdtype;
123
struct rechandle *rh;
124
struct tdopts *opts;
125
};
126
127
/* Decode the KADM_DATA from a DB entry.*/
128
static int
129
get_adb(krb5_db_entry *dbe, osa_princ_ent_rec *adb)
130
{
131
XDR xdrs;
132
int success;
133
krb5_tl_data tl_data;
134
krb5_error_code ret;
135
136
memset(adb, 0, sizeof(*adb));
137
tl_data.tl_data_type = KRB5_TL_KADM_DATA;
138
ret = krb5_dbe_lookup_tl_data(util_context, dbe, &tl_data);
139
if (ret != 0 || tl_data.tl_data_length == 0)
140
return 0;
141
xdrmem_create(&xdrs, (caddr_t)tl_data.tl_data_contents,
142
tl_data.tl_data_length, XDR_DECODE);
143
success = xdr_osa_princ_ent_rec(&xdrs, adb);
144
xdr_destroy(&xdrs);
145
return success;
146
}
147
148
/* Write a date field as an ISO 8601 UTC date/time representation. */
149
static int
150
write_date_iso(struct rec_args *args, krb5_timestamp when)
151
{
152
char buf[64];
153
time_t t;
154
struct tm *tm = NULL;
155
struct rechandle *h = args->rh;
156
157
t = ts2tt(when);
158
tm = gmtime(&t);
159
if (tm == NULL) {
160
errno = EINVAL;
161
return -1;
162
}
163
if (strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%SZ", tm) == 0) {
164
errno = EINVAL;
165
return -1;
166
}
167
if (writefield(h, "%s", buf) < 0)
168
return -1;
169
return 0;
170
}
171
172
/* Write a date field, optionally as a decimal POSIX timestamp. */
173
static int
174
write_date(struct rec_args *args, krb5_timestamp when)
175
{
176
struct tdopts *opts = args->opts;
177
struct rechandle *h = args->rh;
178
179
if (opts->numeric)
180
return writefield(h, "%d", when);
181
182
return write_date_iso(args, when);
183
}
184
185
/* Write an enctype field, optionally as decimal. */
186
static krb5_error_code
187
write_enctype(struct rec_args *args, krb5_int16 etype)
188
{
189
char buf[256];
190
krb5_error_code ret;
191
struct rechandle *h = args->rh;
192
struct tdopts *opts = args->opts;
193
194
if (!opts->numeric) {
195
ret = krb5_enctype_to_name(etype, 0, buf, sizeof(buf));
196
if (ret == 0) {
197
if (writefield(h, "%s", buf) < 0)
198
return errno;
199
return ret;
200
}
201
}
202
/* decimal if requested, or if conversion failed */
203
if (writefield(h, "%d", etype) < 0)
204
return errno;
205
return 0;
206
}
207
208
/* Write a salttype field, optionally as decimal. */
209
static krb5_error_code
210
write_salttype(struct rec_args *args, krb5_int16 salttype)
211
{
212
char buf[256];
213
krb5_error_code ret;
214
struct rechandle *h = args->rh;
215
struct tdopts *opts = args->opts;
216
217
if (!opts->numeric) {
218
ret = krb5_salttype_to_string(salttype, buf, sizeof(buf));
219
if (ret == 0) {
220
if (writefield(h, "%s", buf) < 0)
221
return errno;
222
return ret;
223
}
224
}
225
/* decimal if requested, or if conversion failed */
226
if (writefield(h, "%d", salttype) < 0)
227
return errno;
228
return 0;
229
}
230
231
/*
232
* Write a field of bytes from krb5_data as a hexadecimal string. Write empty
233
* strings as "-1" unless requested.
234
*/
235
static int
236
write_data(struct rec_args *args, krb5_data *data)
237
{
238
int ret;
239
char *hex;
240
struct rechandle *h = args->rh;
241
struct tdopts *opts = args->opts;
242
243
if (data->length == 0 && !opts->emptyhex_empty) {
244
if (writefield(h, "-1") < 0)
245
return -1;
246
return 0;
247
}
248
249
ret = k5_hex_encode(data->data, data->length, FALSE, &hex);
250
if (ret) {
251
errno = ret;
252
return -1;
253
}
254
255
ret = writefield(h, "%s", hex);
256
free(hex);
257
return ret;
258
}
259
260
static krb5_error_code
261
alias(struct rec_args *args, const char *name, krb5_db_entry *dbe)
262
{
263
krb5_error_code ret;
264
struct rechandle *h = args->rh;
265
krb5_principal target = NULL;
266
char *tname = NULL;
267
268
ret = krb5_dbe_read_alias(util_context, dbe, &target);
269
if (ret)
270
return ret;
271
if (target == NULL)
272
return 0;
273
274
ret = krb5_unparse_name(util_context, target, &tname);
275
if (ret)
276
goto cleanup;
277
278
if (startrec(h) < 0)
279
ret = errno;
280
if (!ret && writefield(h, "%s", name) < 0)
281
ret = errno;
282
if (!ret && writefield(h, "%s", tname) < 0)
283
ret = errno;
284
if (!ret && endrec(h) < 0)
285
ret = errno;
286
287
cleanup:
288
krb5_free_principal(util_context, target);
289
krb5_free_unparsed_name(util_context, tname);
290
return ret;
291
}
292
293
/* Write a single record of a keydata/keyinfo key set. */
294
static krb5_error_code
295
keyinfo_rec(struct rec_args *args, const char *name, int i, krb5_key_data *kd,
296
int dumpkeys)
297
{
298
int ret;
299
krb5_data data;
300
struct rechandle *h = args->rh;
301
302
if (startrec(h) < 0)
303
return errno;
304
if (writefield(h, "%s", name) < 0)
305
return errno;
306
if (writefield(h, "%d", i) < 0)
307
return errno;
308
if (writefield(h, "%d", kd->key_data_kvno) < 0)
309
return errno;
310
if (write_enctype(args, kd->key_data_type[0]) < 0)
311
return errno;
312
if (dumpkeys) {
313
data.length = kd->key_data_length[0];
314
data.data = (void *)kd->key_data_contents[0];
315
if (write_data(args, &data) < 0)
316
return errno;
317
}
318
ret = write_salttype(args, kd->key_data_type[1]);
319
if (ret)
320
return ret;
321
data.length = kd->key_data_length[1];
322
data.data = (void *)kd->key_data_contents[1];
323
if (write_data(args, &data) < 0)
324
return errno;
325
if (endrec(h) < 0)
326
return errno;
327
return 0;
328
}
329
330
/* Write out a principal's key set, optionally including actual key data. */
331
static krb5_error_code
332
keyinfo_common(struct rec_args *args, const char *name, krb5_db_entry *entry,
333
int dumpkeys)
334
{
335
krb5_error_code ret;
336
krb5_key_data kd;
337
int i;
338
339
for (i = 0; i < entry->n_key_data; i++) {
340
kd = entry->key_data[i];
341
/* missing salt data -> normal salt */
342
if (kd.key_data_ver == 1) {
343
kd.key_data_ver = 2;
344
kd.key_data_type[1] = KRB5_KDB_SALTTYPE_NORMAL;
345
kd.key_data_length[1] = 0;
346
kd.key_data_contents[1] = NULL;
347
}
348
ret = keyinfo_rec(args, name, i, &kd, dumpkeys);
349
if (ret)
350
return ret;
351
}
352
return 0;
353
}
354
355
/* Write a principal's key data. */
356
static krb5_error_code
357
keydata(struct rec_args *args, const char *name, krb5_db_entry *dbe)
358
{
359
return keyinfo_common(args, name, dbe, 1);
360
}
361
362
/* Write a principal's key info (suppressing actual key data). */
363
static krb5_error_code
364
keyinfo(struct rec_args *args, const char *name, krb5_db_entry *dbe)
365
{
366
return keyinfo_common(args, name, dbe, 0);
367
}
368
369
/* Write a record corresponding to a single principal flag setting. */
370
static krb5_error_code
371
princflag_rec(struct rechandle *h, const char *name, const char *flagname,
372
int set)
373
{
374
if (startrec(h) < 0)
375
return errno;
376
if (writefield(h, "%s", name) < 0)
377
return errno;
378
if (writefield(h, "%s", flagname) < 0)
379
return errno;
380
if (writefield(h, "%d", set) < 0)
381
return errno;
382
if (endrec(h) < 0)
383
return errno;
384
return 0;
385
}
386
387
/* Write a principal's flag settings. */
388
static krb5_error_code
389
princ_flags(struct rec_args *args, const char *name, krb5_db_entry *dbe)
390
{
391
int i;
392
char *s = NULL;
393
krb5_flags flags = dbe->attributes;
394
krb5_error_code ret;
395
struct tdopts *opts = args->opts;
396
struct rechandle *h = args->rh;
397
398
for (i = 0; i < 32; i++) {
399
if (opts->numeric) {
400
if (asprintf(&s, "0x%08lx", 1UL << i) == -1)
401
return ENOMEM;
402
} else {
403
ret = krb5_flagnum_to_string(i, &s);
404
if (ret)
405
return ret;
406
/* Don't print unknown flags if they're not set and numeric output
407
* isn't requested. */
408
if (!(flags & (1UL << i)) && strncmp(s, "0x", 2) == 0) {
409
free(s);
410
continue;
411
}
412
}
413
ret = princflag_rec(h, name, s, ((flags & (1UL << i)) != 0));
414
free(s);
415
if (ret)
416
return ret;
417
}
418
return 0;
419
}
420
421
/* Write a principal's lockout data. */
422
static krb5_error_code
423
princ_lockout(struct rec_args *args, const char *name, krb5_db_entry *dbe)
424
{
425
struct rechandle *h = args->rh;
426
427
if (startrec(h) < 0)
428
return errno;
429
if (writefield(h, "%s", name) < 0)
430
return errno;
431
if (write_date(args, dbe->last_success) < 0)
432
return errno;
433
if (write_date(args, dbe->last_failed) < 0)
434
return errno;
435
if (writefield(h, "%d", dbe->fail_auth_count) < 0)
436
return errno;
437
if (endrec(h) < 0)
438
return errno;
439
return 0;
440
}
441
442
/* Write a principal's metadata. */
443
static krb5_error_code
444
princ_meta(struct rec_args *args, const char *name, krb5_db_entry *dbe)
445
{
446
int got_adb = 0;
447
char *modby;
448
krb5_kvno mkvno;
449
const char *policy;
450
krb5_principal mod_princ = NULL;
451
krb5_timestamp mod_time, last_pwd;
452
krb5_error_code ret;
453
osa_princ_ent_rec adb;
454
struct rechandle *h = args->rh;
455
456
memset(&adb, 0, sizeof(adb));
457
if (startrec(h) < 0)
458
return errno;
459
if (writefield(h, "%s", name) < 0)
460
return errno;
461
462
ret = krb5_dbe_lookup_last_pwd_change(util_context, dbe, &last_pwd);
463
if (ret)
464
return ret;
465
ret = krb5_dbe_get_mkvno(util_context, dbe, &mkvno);
466
if (ret)
467
return ret;
468
469
ret = krb5_dbe_lookup_mod_princ_data(util_context, dbe, &mod_time,
470
&mod_princ);
471
if (ret)
472
return ret;
473
ret = krb5_unparse_name(util_context, mod_princ, &modby);
474
krb5_free_principal(util_context, mod_princ);
475
if (ret)
476
return ret;
477
ret = writefield(h, "%s", modby);
478
krb5_free_unparsed_name(util_context, modby);
479
if (ret < 0)
480
return errno;
481
482
if (write_date(args, mod_time) < 0)
483
return errno;
484
if (write_date(args, last_pwd) < 0)
485
return errno;
486
487
got_adb = get_adb(dbe, &adb);
488
if (got_adb && adb.policy != NULL)
489
policy = adb.policy;
490
else
491
policy = "";
492
ret = writefield(h, "%s", policy);
493
if (ret < 0) {
494
ret = errno;
495
goto cleanup;
496
}
497
if (writefield(h, "%d", mkvno) < 0) {
498
ret = errno;
499
goto cleanup;
500
}
501
if (writefield(h, "%d", adb.admin_history_kvno) < 0) {
502
ret = errno;
503
goto cleanup;
504
}
505
if (endrec(h) < 0)
506
ret = errno;
507
else
508
ret = 0;
509
510
cleanup:
511
kdb_free_entry(NULL, NULL, &adb);
512
return ret;
513
}
514
515
/* Write a principal's string attributes. */
516
static krb5_error_code
517
princ_stringattrs(struct rec_args *args, const char *name, krb5_db_entry *dbe)
518
{
519
int i, nattrs;
520
krb5_error_code ret;
521
krb5_string_attr *attrs;
522
struct rechandle *h = args->rh;
523
524
ret = krb5_dbe_get_strings(util_context, dbe, &attrs, &nattrs);
525
if (ret)
526
return ret;
527
for (i = 0; i < nattrs; i++) {
528
if (startrec(h) < 0) {
529
ret = errno;
530
goto cleanup;
531
}
532
if (writefield(h, "%s", name) < 0) {
533
ret = errno;
534
goto cleanup;
535
}
536
if (writefield(h, "%s", attrs[i].key) < 0) {
537
ret = errno;
538
goto cleanup;
539
}
540
if (writefield(h, "%s", attrs[i].value) < 0) {
541
ret = errno;
542
goto cleanup;
543
}
544
if (endrec(h) < 0) {
545
ret = errno;
546
goto cleanup;
547
}
548
}
549
cleanup:
550
krb5_dbe_free_strings(util_context, attrs, nattrs);
551
return ret;
552
}
553
554
/* Write a principal's ticket policy. */
555
static krb5_error_code
556
princ_tktpolicy(struct rec_args *args, const char *name, krb5_db_entry *dbe)
557
{
558
struct rechandle *h = args->rh;
559
560
if (startrec(h) < 0)
561
return errno;
562
if (writefield(h, "%s", name) < 0)
563
return errno;
564
if (write_date(args, dbe->expiration) < 0)
565
return errno;
566
if (write_date(args, dbe->pw_expiration) < 0)
567
return errno;
568
if (writefield(h, "%d", dbe->max_life) < 0)
569
return errno;
570
if (writefield(h, "%d", dbe->max_renewable_life) < 0)
571
return errno;
572
if (endrec(h) < 0)
573
return errno;
574
return 0;
575
}
576
577
/* Iterator function for krb5_db_iterate() */
578
static krb5_error_code
579
tditer(void *ptr, krb5_db_entry *entry)
580
{
581
krb5_error_code ret;
582
struct rec_args *args = ptr;
583
char *name;
584
585
ret = krb5_unparse_name(util_context, entry->princ, &name);
586
if (ret) {
587
com_err(progname, ret, _("while unparsing principal name"));
588
return ret;
589
}
590
ret = args->tdtype->princ_fn(args, name, entry);
591
krb5_free_unparsed_name(util_context, name);
592
if (ret)
593
return ret;
594
return 0;
595
}
596
597
/* Set up state structure for the iterator. */
598
static krb5_error_code
599
setup_args(struct rec_args *args, struct tdtype *tdtype,
600
struct tdopts *opts)
601
{
602
FILE *f = NULL;
603
const char *rectype = NULL;
604
struct rechandle *rh;
605
606
args->tdtype = tdtype;
607
args->opts = opts;
608
if (opts->fname != NULL && strcmp(opts->fname, "-") != 0) {
609
f = fopen(opts->fname, "w");
610
if (f == NULL) {
611
com_err(progname, errno, _("opening %s for writing"),
612
opts->fname);
613
return errno;
614
}
615
args->f = f;
616
} else {
617
f = stdout;
618
args->f = NULL;
619
}
620
if (opts->writerectype)
621
rectype = tdtype->rectype;
622
if (opts->csv)
623
rh = rechandle_csv(f, rectype);
624
else
625
rh = rechandle_tabsep(f, rectype);
626
if (rh == NULL)
627
return ENOMEM;
628
args->rh = rh;
629
if (!opts->omitheader && writeheader(rh, tdtype->fieldnames) < 0)
630
return errno;
631
return 0;
632
}
633
634
/* Clean up the state structure. */
635
static void
636
cleanup_args(struct rec_args *args)
637
{
638
rechandle_free(args->rh);
639
if (args->f != NULL)
640
fclose(args->f);
641
}
642
643
void
644
tabdump(int argc, char **argv)
645
{
646
int ch;
647
size_t i;
648
const char *rectype;
649
struct rec_args args;
650
struct tdopts opts;
651
krb5_error_code ret;
652
653
memset(&opts, 0, sizeof(opts));
654
memset(&args, 0, sizeof(args));
655
optind = 1;
656
while ((ch = getopt(argc, argv, "Hceno:")) != -1) {
657
switch (ch) {
658
case 'H':
659
opts.omitheader = 1;
660
break;
661
case 'c':
662
opts.csv = 1;
663
break;
664
case 'e':
665
opts.emptyhex_empty = 1;
666
break;
667
case 'n':
668
opts.numeric = 1;
669
break;
670
case 'o':
671
opts.fname = optarg;
672
break;
673
case '?':
674
default:
675
usage();
676
break;
677
}
678
}
679
if (argc - optind < 1)
680
usage();
681
rectype = argv[optind];
682
for (i = 0; i < NTDTYPES; i++) {
683
if (strcmp(rectype, tdtypes[i].rectype) == 0) {
684
setup_args(&args, &tdtypes[i], &opts);
685
break;
686
}
687
}
688
if (i >= NTDTYPES)
689
usage();
690
ret = krb5_db_iterate(util_context, NULL, tditer, &args, 0);
691
cleanup_args(&args);
692
if (ret) {
693
com_err(progname, ret, _("performing tabular dump"));
694
exit_status++;
695
}
696
}
697
698