diff --git a/admin/Makefile.am b/admin/Makefile.am index a4a7bb4c0..1821d4b2e 100644 --- a/admin/Makefile.am +++ b/admin/Makefile.am @@ -37,6 +37,7 @@ LDADD = \ $(LIB_hcrypto) \ $(top_builddir)/lib/asn1/libasn1.la \ $(top_builddir)/lib/sl/libsl.la \ + $(LIB_heimbase) \ $(LIB_readline) \ $(LIB_roken) diff --git a/admin/ktutil-commands.in b/admin/ktutil-commands.in index 2b771e931..51c8df0be 100644 --- a/admin/ktutil-commands.in +++ b/admin/ktutil-commands.in @@ -226,6 +226,11 @@ command = { type = "flag" help = "show timestamps" } + option = { + long = "json" + type = "flag" + help = "output JSON representation" + } max_args = "0" function = "kt_list" help = "Show contents of keytab." diff --git a/admin/ktutil.1 b/admin/ktutil.1 index 125b5e8f0..1a78cc868 100644 --- a/admin/ktutil.1 +++ b/admin/ktutil.1 @@ -60,7 +60,7 @@ Verbose output. .Ar command can be one of the following: .Bl -tag -width srvconvert -.It add Oo Fl p Ar principal Oc Oo Fl Fl principal= Ns Ar principal Oc \ +.It Nm add Oo Fl p Ar principal Oc Oo Fl Fl principal= Ns Ar principal Oc \ Oo Fl V Ar kvno Oc Oo Fl Fl kvno= Ns Ar kvno Oc Oo Fl e Ar enctype Oc \ Oo Fl Fl keepold | Fl Fl keepallold | Fl Fl pruneall Oc \ Oo Fl Fl enctype= Ns Ar enctype Oc Oo Fl w Ar password Oc \ @@ -72,7 +72,7 @@ principal to add; if what you really want is to add a new principal to the keytab, you should consider the .Ar get command, which talks to the kadmin server. -.It change Oo Fl r Ar realm Oc Oo Fl Fl realm= Ns Ar realm Oc \ +.It Nm change Oo Fl r Ar realm Oc Oo Fl Fl realm= Ns Ar realm Oc \ Oo Fl Fl keepold | Fl Fl keepallold | Fl Fl pruneall Oc \ Oo Fl Fl enctype= Ns Ar enctype Oc \ Oo Fl Fl a Ar host Oc Oo Fl Fl admin-server= Ns Ar host Oc \ @@ -82,12 +82,12 @@ server for the realm of a keytab entry. Otherwise it will use the values specified by the options. .Pp If no principals are given, all the ones in the keytab are updated. -.It copy Ar keytab-src Ar keytab-dest +.It Nm copy Ar keytab-src Ar keytab-dest Copies all the entries from .Ar keytab-src to .Ar keytab-dest . -.It get Oo Fl p Ar admin principal Oc \ +.It Nm get Oo Fl p Ar admin principal Oc \ Oo Fl Fl principal= Ns Ar admin principal Oc Oo Fl e Ar enctype Oc \ Oo Fl Fl keepold | Fl Fl keepallold | Fl Fl pruneall Oc \ Oo Fl Fl enctypes= Ns Ar enctype Oc Oo Fl r Ar realm Oc \ @@ -103,9 +103,9 @@ If no .Ar realm is specified, the realm to operate on is taken from the first principal. -.It list Oo Fl Fl keys Oc Op Fl Fl timestamp +.It Nm list Oo Fl Fl keys Oc Op Fl Fl timestamp Oo Op Fl Fl json Oc List the keys stored in the keytab. -.It remove Oo Fl p Ar principal Oc Oo Fl Fl principal= Ns Ar principal Oc \ +.It Nm remove Oo Fl p Ar principal Oc Oo Fl Fl principal= Ns Ar principal Oc \ Oo Fl V kvno Oc Oo Fl Fl kvno= Ns Ar kvno Oc Oo Fl e enctype Oc \ Oo Fl Fl enctype= Ns Ar enctype Oc Removes the specified key or keys. Not specifying a @@ -113,12 +113,12 @@ Removes the specified key or keys. Not specifying a removes keys with any version number. Not specifying an .Ar enctype removes keys of any type. -.It rename Ar from-principal Ar to-principal +.It Nm rename Ar from-principal Ar to-principal Renames all entries in the keytab that match the .Ar from-principal to .Ar to-principal . -.It purge Op Fl Fl age= Ns Ar age +.It Nm purge Op Fl Fl age= Ns Ar age Removes all old versions of a key for which there is a newer version that is at least .Ar age diff --git a/admin/list.c b/admin/list.c index 31be54611..9d1e9d5d4 100644 --- a/admin/list.c +++ b/admin/list.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Kungliga Tekniska Högskolan + * Copyright (c) 1997-2022 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. * @@ -32,6 +32,7 @@ */ #include "ktutil_locl.h" +#include #include RCSID("$Id$"); @@ -131,7 +132,8 @@ do_list(struct list_options *opt, const char *keytab_str) struct rk_strpool *p = NULL; for (i = 0; i< entry.aliases->len; i++) { - krb5_unparse_name_fixed(context, entry.principal, buf, sizeof(buf)); + krb5_unparse_name_fixed(context, &entry.aliases->val[i], + buf, sizeof(buf)); p = rk_strpoolprintf(p, "%s%s", buf, i + 1 < entry.aliases->len ? ", " : ""); @@ -152,6 +154,137 @@ out: return ret; } +static int +do_list1_json(struct list_options *opt, + const char *keytab_str, + heim_array_t a) +{ + krb5_error_code ret; + krb5_keytab keytab; + krb5_keytab_entry entry; + krb5_kt_cursor cursor; + + ret = krb5_kt_resolve(context, keytab_str, &keytab); + if (ret) { + krb5_warn(context, ret, "resolving keytab %s", keytab_str); + return ret; + } + + ret = krb5_kt_start_seq_get(context, keytab, &cursor); + if(ret) { + krb5_warn(context, ret, "krb5_kt_start_seq_get %s", keytab_str); + krb5_kt_close(context, keytab); + return ret; + } + + //if (opt->timestamp_flag) + //if (opt->keys_flag) + + while (krb5_kt_next_entry(context, keytab, &entry, &cursor) == 0) { + heim_dict_t d = heim_dict_create(5); + heim_object_t o; + char *s; + + heim_array_append_value(a, d); + heim_dict_set_value(d, HSTR("keytab"), + o = heim_string_create(keytab_str)); heim_release(o); + heim_dict_set_value(d, HSTR("kvno"), o = heim_number_create(entry.vno)); + heim_release(o); + heim_dict_set_value(d, HSTR("enctype_number"), + o = heim_number_create(entry.keyblock.keytype)); + heim_release(o); + heim_dict_set_value(d, HSTR("flags"), + o = heim_number_create(entry.flags)); + heim_release(o); + ret = krb5_enctype_to_string(context, entry.keyblock.keytype, &s); + if (ret == 0) { + heim_dict_set_value(d, HSTR("enctype"), o = heim_string_create(s)); + heim_release(o); + free(s); + } + heim_dict_set_value(d, HSTR("timestamp"), + o = heim_number_create(entry.timestamp)); + heim_release(o); + + ret = krb5_unparse_name(context, entry.principal, &s); + if (ret) + krb5_err(context, 1, ret, "Could not format principal"); + heim_dict_set_value(d, HSTR("principal"), o = heim_string_create(s)); + heim_release(o); + free(s); + + if (opt->keys_flag) { + o = heim_data_create(entry.keyblock.keyvalue.data, + entry.keyblock.keyvalue.length); + heim_dict_set_value(d, HSTR("key"), o); + heim_release(o); + } + if (entry.aliases) { + heim_array_t aliases = heim_array_create(); + unsigned int i; + + for (i = 0; i< entry.aliases->len; i++) { + ret = krb5_unparse_name(context, &entry.aliases->val[i], &s); + if (ret) + krb5_err(context, 1, ret, "Could not format principal"); + heim_array_append_value(aliases, o = heim_string_create(s)); + heim_release(o); + free(s); + } + heim_dict_set_value(d, HSTR("aliases"), aliases); + heim_release(aliases); + free(s); + } + + krb5_kt_free_entry(context, &entry); + heim_release(d); + } + + ret = krb5_kt_end_seq_get(context, keytab, &cursor); + krb5_kt_close(context, keytab); + return ret; +} + +static int +do_list_json(struct list_options *opt, const char *keytab_str) +{ + krb5_error_code ret = 0; + heim_json_flags_t flags = + (HEIM_JSON_F_STRICT | HEIM_JSON_F_INDENT2 | HEIM_JSON_F_NO_DATA_DICT) & + ~HEIM_JSON_F_NO_DATA; + heim_array_t a = heim_array_create(); + heim_string_t s; + + /* + * Special-case the ANY: keytab type. What do we get from this? We get to + * include the actual keytab name for each entry in its JSON + * representation. Otherwise there would be no point because the ANY: + * keytab type iterates all the keytabs it joins. + * + * Why strncasecmp() though? Because do_list() uses it, though it arguably + * never should have. + */ + if (strncasecmp(keytab_str, "ANY:", 4) == 0) { + char buf[1024]; + + keytab_str += 4; + ret = 0; + while (strsep_copy((const char**)&keytab_str, ",", + buf, sizeof(buf)) != -1) { + if (do_list1_json(opt, buf, a)) + ret = 1; + } + } else { + ret = do_list1_json(opt, keytab_str, a); + } + + s = heim_json_copy_serialize(a, flags, NULL); + printf("%s", heim_string_get_utf8(s)); + heim_release(a); + heim_release(s); + return ret; +} + int kt_list(struct list_options *opt, int argc, char **argv) { @@ -168,5 +301,7 @@ kt_list(struct list_options *opt, int argc, char **argv) } keytab_string = kt; } + if (opt->json_flag) + return do_list_json(opt, keytab_string) != 0; return do_list(opt, keytab_string) != 0; }