Path: blob/main/crypto/krb5/src/kadmin/dbutil/tabdump.c
34889 views
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */1/* kdc/tabdump.c - reporting-friendly tabular KDB dumps */2/*3* Copyright (C) 2015 by the Massachusetts Institute of Technology.4* All rights reserved.5*6* Redistribution and use in source and binary forms, with or without7* modification, are permitted provided that the following conditions8* are met:9*10* * Redistributions of source code must retain the above copyright11* notice, this list of conditions and the following disclaimer.12*13* * Redistributions in binary form must reproduce the above copyright14* notice, this list of conditions and the following disclaimer in15* the documentation and/or other materials provided with the16* distribution.17*18* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS19* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT20* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS21* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE22* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,23* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES24* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR25* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)26* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,27* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)28* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED29* OF THE POSSIBILITY OF SUCH DAMAGE.30*/3132#include <k5-int.h>33#include "k5-platform.h" /* for asprintf */34#include "k5-hex.h"3536#include <limits.h>37#include <stdio.h>38#include <string.h>39#include <unistd.h>4041#include <kadm5/admin.h>42#include <kadm5/server_internal.h>4344#include "adm_proto.h"45#include "kdb5_util.h"46#include "tdumputil.h"4748struct tdopts {49int csv; /* 1 for CSV, 0 for tab-separated */50int emptyhex_empty; /* print empty hex strings as "" not "-1" */51int numeric; /* numeric instead of symbolic output */52int omitheader; /* omit field headers */53int writerectype; /* write record type prefix */54char *fname; /* output file name */55};5657struct rec_args;5859typedef int (tdump_princ_fn)(struct rec_args *, const char *, krb5_db_entry *);60typedef int (tdump_policy_fn)(struct rec_args *, const char *,61kadm5_policy_ent_t);6263/* Descriptor for a tabdump record type */64struct tdtype {65const char *rectype;66char * const *fieldnames;67tdump_princ_fn *princ_fn;68tdump_policy_fn *policy_fn;69};7071static tdump_princ_fn alias;72static tdump_princ_fn keydata;73static tdump_princ_fn keyinfo;74static tdump_princ_fn princ_flags;75static tdump_princ_fn princ_lockout;76static tdump_princ_fn princ_meta;77static tdump_princ_fn princ_stringattrs;78static tdump_princ_fn princ_tktpolicy;7980static char * const keydata_fields[] = {81"name", "keyindex", "kvno", "enctype", "key", "salttype", "salt", NULL82};83static char * const keyinfo_fields[] = {84"name", "keyindex", "kvno", "enctype", "salttype", "salt", NULL85};86static char * const princ_flags_fields[] = {87"name", "flag", "value", NULL88};89static char * const princ_lockout_fields[] = {90"name", "last_success", "last_failed", "fail_count", NULL91};92static char * const princ_meta_fields[] = {93"name", "modby", "modtime", "lastpwd", "policy", "mkvno", "hist_kvno", NULL94};95static char * const princ_stringattrs_fields[] = {96"name", "key", "value", NULL97};98static char * const princ_tktpolicy_fields[] = {99"name", "expiration", "pw_expiration", "max_life", "max_renew_life", NULL100};101static char * const alias_fields[] = {102"aliasname", "targetname", NULL103};104105/* Lookup table for tabdump record types */106static struct tdtype tdtypes[] = {107{"alias", alias_fields, alias, NULL},108{"keydata", keydata_fields, keydata, NULL},109{"keyinfo", keyinfo_fields, keyinfo, NULL},110{"princ_flags", princ_flags_fields, princ_flags, NULL},111{"princ_lockout", princ_lockout_fields, princ_lockout, NULL},112{"princ_meta", princ_meta_fields, princ_meta, NULL},113{"princ_stringattrs", princ_stringattrs_fields, princ_stringattrs, NULL},114{"princ_tktpolicy", princ_tktpolicy_fields, princ_tktpolicy, NULL},115};116#define NTDTYPES (sizeof(tdtypes)/sizeof(tdtypes[0]))117118/* State to pass to KDB iterator */119struct rec_args {120FILE *f;121struct tdtype *tdtype;122struct rechandle *rh;123struct tdopts *opts;124};125126/* Decode the KADM_DATA from a DB entry.*/127static int128get_adb(krb5_db_entry *dbe, osa_princ_ent_rec *adb)129{130XDR xdrs;131int success;132krb5_tl_data tl_data;133krb5_error_code ret;134135memset(adb, 0, sizeof(*adb));136tl_data.tl_data_type = KRB5_TL_KADM_DATA;137ret = krb5_dbe_lookup_tl_data(util_context, dbe, &tl_data);138if (ret != 0 || tl_data.tl_data_length == 0)139return 0;140xdrmem_create(&xdrs, (caddr_t)tl_data.tl_data_contents,141tl_data.tl_data_length, XDR_DECODE);142success = xdr_osa_princ_ent_rec(&xdrs, adb);143xdr_destroy(&xdrs);144return success;145}146147/* Write a date field as an ISO 8601 UTC date/time representation. */148static int149write_date_iso(struct rec_args *args, krb5_timestamp when)150{151char buf[64];152time_t t;153struct tm *tm = NULL;154struct rechandle *h = args->rh;155156t = ts2tt(when);157tm = gmtime(&t);158if (tm == NULL) {159errno = EINVAL;160return -1;161}162if (strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%SZ", tm) == 0) {163errno = EINVAL;164return -1;165}166if (writefield(h, "%s", buf) < 0)167return -1;168return 0;169}170171/* Write a date field, optionally as a decimal POSIX timestamp. */172static int173write_date(struct rec_args *args, krb5_timestamp when)174{175struct tdopts *opts = args->opts;176struct rechandle *h = args->rh;177178if (opts->numeric)179return writefield(h, "%d", when);180181return write_date_iso(args, when);182}183184/* Write an enctype field, optionally as decimal. */185static krb5_error_code186write_enctype(struct rec_args *args, krb5_int16 etype)187{188char buf[256];189krb5_error_code ret;190struct rechandle *h = args->rh;191struct tdopts *opts = args->opts;192193if (!opts->numeric) {194ret = krb5_enctype_to_name(etype, 0, buf, sizeof(buf));195if (ret == 0) {196if (writefield(h, "%s", buf) < 0)197return errno;198return ret;199}200}201/* decimal if requested, or if conversion failed */202if (writefield(h, "%d", etype) < 0)203return errno;204return 0;205}206207/* Write a salttype field, optionally as decimal. */208static krb5_error_code209write_salttype(struct rec_args *args, krb5_int16 salttype)210{211char buf[256];212krb5_error_code ret;213struct rechandle *h = args->rh;214struct tdopts *opts = args->opts;215216if (!opts->numeric) {217ret = krb5_salttype_to_string(salttype, buf, sizeof(buf));218if (ret == 0) {219if (writefield(h, "%s", buf) < 0)220return errno;221return ret;222}223}224/* decimal if requested, or if conversion failed */225if (writefield(h, "%d", salttype) < 0)226return errno;227return 0;228}229230/*231* Write a field of bytes from krb5_data as a hexadecimal string. Write empty232* strings as "-1" unless requested.233*/234static int235write_data(struct rec_args *args, krb5_data *data)236{237int ret;238char *hex;239struct rechandle *h = args->rh;240struct tdopts *opts = args->opts;241242if (data->length == 0 && !opts->emptyhex_empty) {243if (writefield(h, "-1") < 0)244return -1;245return 0;246}247248ret = k5_hex_encode(data->data, data->length, FALSE, &hex);249if (ret) {250errno = ret;251return -1;252}253254ret = writefield(h, "%s", hex);255free(hex);256return ret;257}258259static krb5_error_code260alias(struct rec_args *args, const char *name, krb5_db_entry *dbe)261{262krb5_error_code ret;263struct rechandle *h = args->rh;264krb5_principal target = NULL;265char *tname = NULL;266267ret = krb5_dbe_read_alias(util_context, dbe, &target);268if (ret)269return ret;270if (target == NULL)271return 0;272273ret = krb5_unparse_name(util_context, target, &tname);274if (ret)275goto cleanup;276277if (startrec(h) < 0)278ret = errno;279if (!ret && writefield(h, "%s", name) < 0)280ret = errno;281if (!ret && writefield(h, "%s", tname) < 0)282ret = errno;283if (!ret && endrec(h) < 0)284ret = errno;285286cleanup:287krb5_free_principal(util_context, target);288krb5_free_unparsed_name(util_context, tname);289return ret;290}291292/* Write a single record of a keydata/keyinfo key set. */293static krb5_error_code294keyinfo_rec(struct rec_args *args, const char *name, int i, krb5_key_data *kd,295int dumpkeys)296{297int ret;298krb5_data data;299struct rechandle *h = args->rh;300301if (startrec(h) < 0)302return errno;303if (writefield(h, "%s", name) < 0)304return errno;305if (writefield(h, "%d", i) < 0)306return errno;307if (writefield(h, "%d", kd->key_data_kvno) < 0)308return errno;309if (write_enctype(args, kd->key_data_type[0]) < 0)310return errno;311if (dumpkeys) {312data.length = kd->key_data_length[0];313data.data = (void *)kd->key_data_contents[0];314if (write_data(args, &data) < 0)315return errno;316}317ret = write_salttype(args, kd->key_data_type[1]);318if (ret)319return ret;320data.length = kd->key_data_length[1];321data.data = (void *)kd->key_data_contents[1];322if (write_data(args, &data) < 0)323return errno;324if (endrec(h) < 0)325return errno;326return 0;327}328329/* Write out a principal's key set, optionally including actual key data. */330static krb5_error_code331keyinfo_common(struct rec_args *args, const char *name, krb5_db_entry *entry,332int dumpkeys)333{334krb5_error_code ret;335krb5_key_data kd;336int i;337338for (i = 0; i < entry->n_key_data; i++) {339kd = entry->key_data[i];340/* missing salt data -> normal salt */341if (kd.key_data_ver == 1) {342kd.key_data_ver = 2;343kd.key_data_type[1] = KRB5_KDB_SALTTYPE_NORMAL;344kd.key_data_length[1] = 0;345kd.key_data_contents[1] = NULL;346}347ret = keyinfo_rec(args, name, i, &kd, dumpkeys);348if (ret)349return ret;350}351return 0;352}353354/* Write a principal's key data. */355static krb5_error_code356keydata(struct rec_args *args, const char *name, krb5_db_entry *dbe)357{358return keyinfo_common(args, name, dbe, 1);359}360361/* Write a principal's key info (suppressing actual key data). */362static krb5_error_code363keyinfo(struct rec_args *args, const char *name, krb5_db_entry *dbe)364{365return keyinfo_common(args, name, dbe, 0);366}367368/* Write a record corresponding to a single principal flag setting. */369static krb5_error_code370princflag_rec(struct rechandle *h, const char *name, const char *flagname,371int set)372{373if (startrec(h) < 0)374return errno;375if (writefield(h, "%s", name) < 0)376return errno;377if (writefield(h, "%s", flagname) < 0)378return errno;379if (writefield(h, "%d", set) < 0)380return errno;381if (endrec(h) < 0)382return errno;383return 0;384}385386/* Write a principal's flag settings. */387static krb5_error_code388princ_flags(struct rec_args *args, const char *name, krb5_db_entry *dbe)389{390int i;391char *s = NULL;392krb5_flags flags = dbe->attributes;393krb5_error_code ret;394struct tdopts *opts = args->opts;395struct rechandle *h = args->rh;396397for (i = 0; i < 32; i++) {398if (opts->numeric) {399if (asprintf(&s, "0x%08lx", 1UL << i) == -1)400return ENOMEM;401} else {402ret = krb5_flagnum_to_string(i, &s);403if (ret)404return ret;405/* Don't print unknown flags if they're not set and numeric output406* isn't requested. */407if (!(flags & (1UL << i)) && strncmp(s, "0x", 2) == 0) {408free(s);409continue;410}411}412ret = princflag_rec(h, name, s, ((flags & (1UL << i)) != 0));413free(s);414if (ret)415return ret;416}417return 0;418}419420/* Write a principal's lockout data. */421static krb5_error_code422princ_lockout(struct rec_args *args, const char *name, krb5_db_entry *dbe)423{424struct rechandle *h = args->rh;425426if (startrec(h) < 0)427return errno;428if (writefield(h, "%s", name) < 0)429return errno;430if (write_date(args, dbe->last_success) < 0)431return errno;432if (write_date(args, dbe->last_failed) < 0)433return errno;434if (writefield(h, "%d", dbe->fail_auth_count) < 0)435return errno;436if (endrec(h) < 0)437return errno;438return 0;439}440441/* Write a principal's metadata. */442static krb5_error_code443princ_meta(struct rec_args *args, const char *name, krb5_db_entry *dbe)444{445int got_adb = 0;446char *modby;447krb5_kvno mkvno;448const char *policy;449krb5_principal mod_princ = NULL;450krb5_timestamp mod_time, last_pwd;451krb5_error_code ret;452osa_princ_ent_rec adb;453struct rechandle *h = args->rh;454455memset(&adb, 0, sizeof(adb));456if (startrec(h) < 0)457return errno;458if (writefield(h, "%s", name) < 0)459return errno;460461ret = krb5_dbe_lookup_last_pwd_change(util_context, dbe, &last_pwd);462if (ret)463return ret;464ret = krb5_dbe_get_mkvno(util_context, dbe, &mkvno);465if (ret)466return ret;467468ret = krb5_dbe_lookup_mod_princ_data(util_context, dbe, &mod_time,469&mod_princ);470if (ret)471return ret;472ret = krb5_unparse_name(util_context, mod_princ, &modby);473krb5_free_principal(util_context, mod_princ);474if (ret)475return ret;476ret = writefield(h, "%s", modby);477krb5_free_unparsed_name(util_context, modby);478if (ret < 0)479return errno;480481if (write_date(args, mod_time) < 0)482return errno;483if (write_date(args, last_pwd) < 0)484return errno;485486got_adb = get_adb(dbe, &adb);487if (got_adb && adb.policy != NULL)488policy = adb.policy;489else490policy = "";491ret = writefield(h, "%s", policy);492if (ret < 0) {493ret = errno;494goto cleanup;495}496if (writefield(h, "%d", mkvno) < 0) {497ret = errno;498goto cleanup;499}500if (writefield(h, "%d", adb.admin_history_kvno) < 0) {501ret = errno;502goto cleanup;503}504if (endrec(h) < 0)505ret = errno;506else507ret = 0;508509cleanup:510kdb_free_entry(NULL, NULL, &adb);511return ret;512}513514/* Write a principal's string attributes. */515static krb5_error_code516princ_stringattrs(struct rec_args *args, const char *name, krb5_db_entry *dbe)517{518int i, nattrs;519krb5_error_code ret;520krb5_string_attr *attrs;521struct rechandle *h = args->rh;522523ret = krb5_dbe_get_strings(util_context, dbe, &attrs, &nattrs);524if (ret)525return ret;526for (i = 0; i < nattrs; i++) {527if (startrec(h) < 0) {528ret = errno;529goto cleanup;530}531if (writefield(h, "%s", name) < 0) {532ret = errno;533goto cleanup;534}535if (writefield(h, "%s", attrs[i].key) < 0) {536ret = errno;537goto cleanup;538}539if (writefield(h, "%s", attrs[i].value) < 0) {540ret = errno;541goto cleanup;542}543if (endrec(h) < 0) {544ret = errno;545goto cleanup;546}547}548cleanup:549krb5_dbe_free_strings(util_context, attrs, nattrs);550return ret;551}552553/* Write a principal's ticket policy. */554static krb5_error_code555princ_tktpolicy(struct rec_args *args, const char *name, krb5_db_entry *dbe)556{557struct rechandle *h = args->rh;558559if (startrec(h) < 0)560return errno;561if (writefield(h, "%s", name) < 0)562return errno;563if (write_date(args, dbe->expiration) < 0)564return errno;565if (write_date(args, dbe->pw_expiration) < 0)566return errno;567if (writefield(h, "%d", dbe->max_life) < 0)568return errno;569if (writefield(h, "%d", dbe->max_renewable_life) < 0)570return errno;571if (endrec(h) < 0)572return errno;573return 0;574}575576/* Iterator function for krb5_db_iterate() */577static krb5_error_code578tditer(void *ptr, krb5_db_entry *entry)579{580krb5_error_code ret;581struct rec_args *args = ptr;582char *name;583584ret = krb5_unparse_name(util_context, entry->princ, &name);585if (ret) {586com_err(progname, ret, _("while unparsing principal name"));587return ret;588}589ret = args->tdtype->princ_fn(args, name, entry);590krb5_free_unparsed_name(util_context, name);591if (ret)592return ret;593return 0;594}595596/* Set up state structure for the iterator. */597static krb5_error_code598setup_args(struct rec_args *args, struct tdtype *tdtype,599struct tdopts *opts)600{601FILE *f = NULL;602const char *rectype = NULL;603struct rechandle *rh;604605args->tdtype = tdtype;606args->opts = opts;607if (opts->fname != NULL && strcmp(opts->fname, "-") != 0) {608f = fopen(opts->fname, "w");609if (f == NULL) {610com_err(progname, errno, _("opening %s for writing"),611opts->fname);612return errno;613}614args->f = f;615} else {616f = stdout;617args->f = NULL;618}619if (opts->writerectype)620rectype = tdtype->rectype;621if (opts->csv)622rh = rechandle_csv(f, rectype);623else624rh = rechandle_tabsep(f, rectype);625if (rh == NULL)626return ENOMEM;627args->rh = rh;628if (!opts->omitheader && writeheader(rh, tdtype->fieldnames) < 0)629return errno;630return 0;631}632633/* Clean up the state structure. */634static void635cleanup_args(struct rec_args *args)636{637rechandle_free(args->rh);638if (args->f != NULL)639fclose(args->f);640}641642void643tabdump(int argc, char **argv)644{645int ch;646size_t i;647const char *rectype;648struct rec_args args;649struct tdopts opts;650krb5_error_code ret;651652memset(&opts, 0, sizeof(opts));653memset(&args, 0, sizeof(args));654optind = 1;655while ((ch = getopt(argc, argv, "Hceno:")) != -1) {656switch (ch) {657case 'H':658opts.omitheader = 1;659break;660case 'c':661opts.csv = 1;662break;663case 'e':664opts.emptyhex_empty = 1;665break;666case 'n':667opts.numeric = 1;668break;669case 'o':670opts.fname = optarg;671break;672case '?':673default:674usage();675break;676}677}678if (argc - optind < 1)679usage();680rectype = argv[optind];681for (i = 0; i < NTDTYPES; i++) {682if (strcmp(rectype, tdtypes[i].rectype) == 0) {683setup_args(&args, &tdtypes[i], &opts);684break;685}686}687if (i >= NTDTYPES)688usage();689ret = krb5_db_iterate(util_context, NULL, tditer, &args, 0);690cleanup_args(&args);691if (ret) {692com_err(progname, ret, _("performing tabular dump"));693exit_status++;694}695}696697698