From: Andrew Bartlett Date: Mon, 31 Oct 2022 01:33:09 +0000 (+1300) Subject: third_party/heimdal: import lorikeet-heimdal-202210310104 (commit 0fc20ff4144973047e6... X-Git-Tag: talloc-2.4.0~594 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ef28247f3bbbd7cf9daed7a4dba28855496ce38e;p=thirdparty%2Fsamba.git third_party/heimdal: import lorikeet-heimdal-202210310104 (commit 0fc20ff4144973047e6aaaeb2fc8708bd75be222) This commit won't compile on it's own, as we need to fix the build system to cope in the next commit. The purpose of this commit is to update to a new lorikeet-heimdal tree that includes the previous two patches and is rebased on a current Heimdal master snapshot. Signed-off-by: Andrew Bartlett Reviewed-by: Joseph Sutton --- diff --git a/third_party/heimdal/.github/workflows/osx.yml b/third_party/heimdal/.github/workflows/osx.yml index 342f850f1c7..3463e99b6e9 100644 --- a/third_party/heimdal/.github/workflows/osx.yml +++ b/third_party/heimdal/.github/workflows/osx.yml @@ -66,7 +66,7 @@ jobs: echo "bison, flex, ncurses, texinfo, and unzip are in the base OS." echo "berkeley-db, perl, python, curl, and jq are installed in the" echo "base image already." - brew install autoconf automake libtool cpanm + brew install autoconf automake libtool cpanm texinfo texi2html sudo cpanm install JSON - name: Clone repository uses: actions/checkout@v1 @@ -79,8 +79,10 @@ jobs: /bin/sh ./autogen.sh mkdir build cd build - ../configure --srcdir=`dirname "$PWD"` --disable-afs-support --enable-maintainer-mode --enable-developer $CONFIGURE_OPTS --prefix=$HOME/inst CFLAGS="-Wno-error=shadow -Wno-error=bad-function-cast -Wno-error=unused-function -Wno-error=unused-result -Wno-error=deprecated-declarations" CFLAGS="-O0 -g -ggdb3" + ../configure --srcdir=`dirname "$PWD"` --disable-heimdal-documentation --disable-afs-support --enable-maintainer-mode --enable-developer $CONFIGURE_OPTS --prefix=$HOME/inst CFLAGS="-Wno-error=shadow -Wno-error=bad-function-cast -Wno-error=unused-function -Wno-error=unused-result -Wno-error=deprecated-declarations" CFLAGS="-O0 -g -ggdb3" ulimit -c unlimited + PATH=/usr/local/opt/texinfo/bin:$PATH + export PATH make -j4 #- name: Setup upterm session # uses: lhotari/action-upterm@v1 diff --git a/third_party/heimdal/.github/workflows/windows.yml b/third_party/heimdal/.github/workflows/windows.yml index f1c187c397a..0d3bad83b21 100644 --- a/third_party/heimdal/.github/workflows/windows.yml +++ b/third_party/heimdal/.github/workflows/windows.yml @@ -4,6 +4,7 @@ on: push: branches: - 'master' + - 'windows-build' - 'heimdal-7-1-branch' paths: - '!docs/**' @@ -76,6 +77,7 @@ jobs: pacman --noconfirm -S bison pacman --noconfirm -S perl pacman --noconfirm -S perl-JSON + pacman --noconfirm -S texinfo set PATH=%PATH%;%wix%bin title Heimdal Build %CPU% %dbg__type% set "PATH=%PATH%;C:\Perl64\bin;C:\tools\cygwin\bin;C:\Program Files (x86)\HTML Help Workshop" diff --git a/third_party/heimdal/admin/Makefile.am b/third_party/heimdal/admin/Makefile.am index a4a7bb4c0f9..1821d4b2e4b 100644 --- a/third_party/heimdal/admin/Makefile.am +++ b/third_party/heimdal/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/third_party/heimdal/admin/add.c b/third_party/heimdal/admin/add.c index 13580b9bb57..5f1920ff8be 100644 --- a/third_party/heimdal/admin/add.c +++ b/third_party/heimdal/admin/add.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2005 Kungliga Tekniska Högskolan + * Copyright (c) 1997-2022 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. * @@ -32,6 +32,8 @@ */ #include "ktutil_locl.h" +#include +#include RCSID("$Id$"); @@ -153,6 +155,178 @@ kt_add(struct add_options *opt, int argc, char **argv) krb5_warn(context, ret, "add"); out: krb5_kt_free_entry(context, &entry); - krb5_kt_close(context, keytab); + if (ret == 0) { + ret = krb5_kt_close(context, keytab); + if (ret) + krb5_warn(context, ret, "Could not write the keytab"); + } else { + krb5_kt_close(context, keytab); + } + return ret != 0; +} + +/* We might be reading from a pipe, so we can't use rk_undumpdata() */ +static char * +read_file(FILE *f) +{ + size_t alloced; + size_t len = 0; + size_t bytes; + char *res, *end, *p; + + if ((res = malloc(1024)) == NULL) + err(1, "Out of memory"); + alloced = 1024; + + end = res + alloced; + p = res; + do { + if (p == end) { + char *tmp; + + if ((tmp = realloc(res, alloced + (alloced > 1))) == NULL) + err(1, "Out of memory"); + alloced += alloced > 1; + p = tmp + (p - res); + res = tmp; + end = res + alloced; + } + bytes = fread(p, 1, end - p, f); + len += bytes; + p += bytes; + } while (bytes && !feof(f) && !ferror(f)); + + if (ferror(f)) + errx(1, "Could not read all input"); + if (p == end) { + char *tmp; + + if ((tmp = strndup(res, len)) == NULL) + err(1, "Out of memory"); + free(res); + res = tmp; + } + if (strlen(res) != len) + err(1, "Embedded NULs in input!"); + return res; +} + +static void +json2keytab_entry(heim_dict_t d, krb5_keytab kt, size_t idx) +{ + krb5_keytab_entry e; + krb5_error_code ret; + heim_object_t v; + uint64_t u; + int64_t i; + char *buf = NULL; + + memset(&e, 0, sizeof(e)); + + v = heim_dict_get_value(d, HSTR("timestamp")); + if (heim_get_tid(v) != HEIM_TID_NUMBER) + goto bad; + u = heim_number_get_long(v); + e.timestamp = u; + if (u != (uint64_t)e.timestamp) + goto bad; + + v = heim_dict_get_value(d, HSTR("kvno")); + if (heim_get_tid(v) != HEIM_TID_NUMBER) + goto bad; + i = heim_number_get_long(v); + e.vno = i; + if (i != (int64_t)e.vno) + goto bad; + + v = heim_dict_get_value(d, HSTR("enctype_number")); + if (heim_get_tid(v) != HEIM_TID_NUMBER) + goto bad; + i = heim_number_get_long(v); + e.keyblock.keytype = i; + if (i != (int64_t)e.keyblock.keytype) + goto bad; + + v = heim_dict_get_value(d, HSTR("key")); + if (heim_get_tid(v) != HEIM_TID_STRING) + goto bad; + { + const char *s = heim_string_get_utf8(v); + int declen; + + if ((buf = malloc(strlen(s))) == NULL) + err(1, "Out of memory"); + declen = rk_base64_decode(s, buf); + if (declen < 0) + goto bad; + e.keyblock.keyvalue.data = buf; + e.keyblock.keyvalue.length = declen; + } + + v = heim_dict_get_value(d, HSTR("principal")); + if (heim_get_tid(v) != HEIM_TID_STRING) + goto bad; + ret = krb5_parse_name(context, heim_string_get_utf8(v), &e.principal); + if (ret == 0) + ret = krb5_kt_add_entry(context, kt, &e); + + /* For now, ignore aliases; besides, they're never set anywhere in-tree */ + + if (ret) + krb5_warn(context, ret, + "Could not parse or write keytab entry %lu", + (unsigned long)idx); +bad: + krb5_free_principal(context, e.principal); +} + +int +kt_import(void *opt, int argc, char **argv) +{ + krb5_error_code ret; + krb5_keytab kt; + heim_object_t o; + heim_error_t json_err = NULL; + heim_json_flags_t flags = HEIM_JSON_F_STRICT; + FILE *f = argc == 0 ? stdin : fopen(argv[0], "r"); + size_t alen, i; + char *json; + + if (f == NULL) + err(1, "Could not open file %s", argv[0]); + + json = read_file(f); + o = heim_json_create(json, 10, flags, &json_err); + free(json); + if (o == NULL) { + if (json_err != NULL) { + o = heim_error_copy_string(json_err); + if (o) + errx(1, "Could not parse JSON: %s", heim_string_get_utf8(o)); + } + errx(1, "Could not parse JSON"); + } + + if (heim_get_tid(o) != HEIM_TID_ARRAY) + errx(1, "JSON text must be an array"); + + alen = heim_array_get_length(o); + if (alen == 0) + errx(1, "Empty JSON array; not overwriting keytab"); + + if ((kt = ktutil_open_keytab()) == NULL) + err(1, "Could not open keytab"); + + for (i = 0; i < alen; i++) { + heim_object_t e = heim_array_get_value(o, i); + + if (heim_get_tid(e) != HEIM_TID_DICT) + warnx("Element %ld of JSON text array is not an object", (long)i); + else + json2keytab_entry(heim_array_get_value(o, i), kt, i); + } + ret = krb5_kt_close(context, kt); + if (ret) + krb5_warn(context, ret, "Could not write the keytab"); return ret != 0; } diff --git a/third_party/heimdal/admin/copy.c b/third_party/heimdal/admin/copy.c index 7b50de1c3cb..8acd6e48ed0 100644 --- a/third_party/heimdal/admin/copy.c +++ b/third_party/heimdal/admin/copy.c @@ -47,7 +47,7 @@ compare_keyblock(const krb5_keyblock *a, const krb5_keyblock *b) } int -kt_copy (void *opt, int argc, char **argv) +kt_copy (struct copy_options *opt, int argc, char **argv) { krb5_error_code ret; krb5_keytab src_keytab, dst_keytab; @@ -106,11 +106,18 @@ kt_copy (void *opt, int argc, char **argv) "already exists for %s, keytype %s, kvno %d", name_str, etype_str, entry.vno); } - krb5_kt_free_entry(context, &dummy); - krb5_kt_free_entry (context, &entry); - free(name_str); - free(etype_str); - continue; + if (!opt->copy_duplicates_flag) { + krb5_kt_free_entry(context, &dummy); + krb5_kt_free_entry (context, &entry); + free(name_str); + free(etype_str); + continue; + } + /* + * Because we can end up trying all keys that match the enctype, + * copying entries with duplicate principal, vno, and enctype, but + * different keys, can be useful. + */ } else if(ret != KRB5_KT_NOTFOUND) { krb5_warn (context, ret, "%s: fetching %s/%s/%u", to, name_str, etype_str, entry.vno); diff --git a/third_party/heimdal/admin/get.c b/third_party/heimdal/admin/get.c index f56e50f4359..ecd6f6a160e 100644 --- a/third_party/heimdal/admin/get.c +++ b/third_party/heimdal/admin/get.c @@ -197,23 +197,27 @@ kt_get(struct get_options *opt, int argc, char **argv) break; } - ret = kadm5_create_principal(kadm_handle, &princ, mask, "thisIs_aUseless.password123"); - if(ret == 0) - created = 1; - else if(ret != KADM5_DUP) { - krb5_warn(context, ret, "kadm5_create_principal(%s)", argv[a]); - krb5_free_principal(context, princ_ent); - failed++; - continue; - } - ret = kadm5_randkey_principal_3(kadm_handle, princ_ent, keep, nks, ks, - &keys, &n_keys); - if (ret) { - krb5_warn(context, ret, "kadm5_randkey_principal(%s)", argv[a]); - krb5_free_principal(context, princ_ent); - failed++; - continue; - } + if (opt->create_flag) { + ret = kadm5_create_principal(kadm_handle, &princ, mask, "thisIs_aUseless.password123"); + if(ret == 0) + created = 1; + else if(ret != KADM5_DUP) { + krb5_warn(context, ret, "kadm5_create_principal(%s)", argv[a]); + krb5_free_principal(context, princ_ent); + failed++; + continue; + } + } + if (opt->change_keys_flag) { + ret = kadm5_randkey_principal_3(kadm_handle, princ_ent, keep, nks, ks, + &keys, &n_keys); + if (ret) { + krb5_warn(context, ret, "kadm5_randkey_principal(%s)", argv[a]); + krb5_free_principal(context, princ_ent); + failed++; + continue; + } + } ret = kadm5_get_principal(kadm_handle, princ_ent, &princ, KADM5_PRINCIPAL | KADM5_KVNO | KADM5_ATTRIBUTES); diff --git a/third_party/heimdal/admin/ktutil-commands.in b/third_party/heimdal/admin/ktutil-commands.in index 2b771e931a1..a85eb5c5715 100644 --- a/third_party/heimdal/admin/ktutil-commands.in +++ b/third_party/heimdal/admin/ktutil-commands.in @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004 Kungliga Tekniska Högskolan + * Copyright (c) 2004-2022 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. * @@ -151,11 +151,17 @@ command = { } command = { name = "copy" + name = "merge" function = "kt_copy" + option = { + long = "copy-duplicates" + type = "flag" + help = "copy entries for the same principal and kvno, but different keys" + } argument = "source destination" min_args = "2" max_args = "2" - help = "Copies one keytab to another." + help = "Merges one keytab into another." } command = { name = "get" @@ -166,6 +172,16 @@ command = { help = "admin principal" argument = "principal" } + option = { + long = "create" + type = "-flag" + help = "do not create the principal" + } + option = { + long = "change-keys" + type = "-flag" + help = "do not change the principal's keys" + } option = { long = "enctypes" short = "e" @@ -214,6 +230,14 @@ command = { argument = "principal..." help = "Change keys for specified principals, and add them to the keytab." } +command = { + name = "import" + function = "kt_import" + help = "Imports a keytab from JSON output of ktutil list --json --keys." + min_args = "0" + max_args = "1" + argument = "JSON-FILE" +} command = { name = "list" option = { @@ -226,6 +250,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/third_party/heimdal/admin/ktutil.1 b/third_party/heimdal/admin/ktutil.1 index 125b5e8f0d5..0036edcbd9b 100644 --- a/third_party/heimdal/admin/ktutil.1 +++ b/third_party/heimdal/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,30 +82,68 @@ 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 Oo Fl Fl copy-duplicates Oc 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 \ +Because entries already in +.Ar keytab-dest +are kept, this command functions to merge keytabs. +Entries for the same principal, key version number, and +encryption type in the +.Ar keytab-src +that are also in the +.Ar keytab-dest +will not be copied to the +.Ar keytab-dest +unless the +.Fl Fl copy-duplicates +option is given. +.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 no-create Oc \ +Oo Fl Fl no-change-keys 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 \ Oo Fl Fl realm= Ns Ar realm Oc Oo Fl a Ar admin server Oc \ Oo Fl Fl admin-server= Ns Ar admin server Oc Oo Fl s Ar server port Oc \ Oo Fl Fl server-port= Ns Ar server port Oc Ar principal ... +.Pp For each .Ar principal , -generate a new key for it (creating it if it doesn't already exist), -and put that key in the keytab. +get a the principal's keys from the KDC via the kadmin protocol, +creating the principal if it doesn't exist (unless +.Fl Fl no-create +is given), and changing its keys to new random keys (unless +.Fl Fl no-change-keys +is given). .Pp 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 import Oo JSON-FILE Oc +Read an array of keytab entries in a JSON file and copy them to +the keytab. +Use the +.Nm list +command with its +.Fl Fl json +option +and +.Fl Fl keys +option to export a keytab. +.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 \ +Use the +.Fl Fl json +and +.Fl Fl keys +options to export a keytab as JSON for importing with the +.Nm import +command. +.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,16 +151,28 @@ 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 -Renames all entries in the keytab that match the +.It Nm merge Oo Fl Fl copy-duplicates Oc Ar keytab-src Ar keytab-dest +An alias for the +.Nm copy +command. +.It Nm rename Ar from-principal Ar to-principal +Renames all entries for the +.Ar from-principal +in the keytab .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 (default one week) old. +Note that this does not update the KDC database. +The +.Xr kadmin 1 +command has a +.Nm prune +command that can do this on the KDC side. .El .Sh SEE ALSO .Xr kadmin 1 diff --git a/third_party/heimdal/admin/list.c b/third_party/heimdal/admin/list.c index 31be5461115..9d1e9d5d483 100644 --- a/third_party/heimdal/admin/list.c +++ b/third_party/heimdal/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; } diff --git a/third_party/heimdal/apply_heimdal.sh b/third_party/heimdal/apply_heimdal.sh index fc9aa292e5a..a720766d3c8 100755 --- a/third_party/heimdal/apply_heimdal.sh +++ b/third_party/heimdal/apply_heimdal.sh @@ -48,8 +48,8 @@ apply () { try_patch() { commit="$1" - git format-patch --stdout $commit -1 source4/heimdal > "$commit".patch - sed -i 's|/source4/heimdal/|/|g' "$commit".patch + git format-patch --stdout $commit -1 third_party/heimdal > "$commit".patch + sed -i 's|/third_party/heimdal/|/|g' "$commit".patch sed -i "s|^---$|(cherry picked from Samba commit $commit)\n---|g" "$commit".patch pushd $LORIKEET_PATH || exit 1 git reset --hard @@ -68,7 +68,7 @@ try_patch() { popd || exit 1 } -commits="$(git log --pretty=oneline --reverse $IMPORT_HASH..HEAD -- source4/heimdal | cut -d' ' -f1)" +commits="$(git log --pretty=oneline --reverse $IMPORT_HASH..HEAD -- third_party/heimdal | cut -d' ' -f1)" for c in $commits; do git log $c -1 echo -n "Try apply? [Y/n] " diff --git a/third_party/heimdal/configure.ac b/third_party/heimdal/configure.ac index 8c0b746ba5c..b946dfff4c1 100644 --- a/third_party/heimdal/configure.ac +++ b/third_party/heimdal/configure.ac @@ -505,16 +505,20 @@ rk_WIN32_EXPORT(BUILD_ROKEN_LIB, ROKEN_LIB) rk_WIN32_EXPORT(BUILD_GSSAPI_LIB, GSSAPI_LIB) rk_WIN32_EXPORT(BUILD_KDC_LIB, KDC_LIB) -dnl Deal with switch FALLTHROUGH +dnl Deal with switch fallthrough warnings AH_TOP([ -#if defined(__GNUC__) -#if __GNUC__ >= 7 -# define fallthrough __attribute__((fallthrough)) +#if defined(DISPATCH_FALLTHROUGH) +# define HEIM_FALLTHROUGH DISPATCH_FALLTHROUGH #else -# define fallthrough do {} while (0) /* fallthrough */ -#endif -#else -# define fallthrough do {} while (0) /* fallthrough */ +# if defined(__GNUC__) +# if __GNUC__ >= 7 +# define HEIM_FALLTHROUGH __attribute__((fallthrough)) +# else +# define HEIM_FALLTHROUGH do {} while (0) /* fallthrough */ +# endif +# else +# define HEIM_FALLTHROUGH do {} while (0) /* fallthrough */ +# endif #endif ]) diff --git a/third_party/heimdal/doc/Makefile.am b/third_party/heimdal/doc/Makefile.am index ed95c305fe3..aa7f8130f49 100644 --- a/third_party/heimdal/doc/Makefile.am +++ b/third_party/heimdal/doc/Makefile.am @@ -10,6 +10,8 @@ TEXI2DVI = true # ARGH, make distcheck can't be disabled to not build dvifiles info_TEXINFOS = heimdal.texi hx509.texi +BUILT_SOURCES = vars.texi + dxy_subst = sed -e 's,[@]srcdir[@],$(srcdir),g' \ -e 's,[@]objdir[@],.,g' \ -e 's,[@]PACKAGE_VERSION[@],$(PACKAGE_VERSION),g' @@ -119,7 +121,6 @@ heimdal_TEXINFOS = \ heimdal.texi \ install.texi \ intro.texi \ - kerberos4.texi \ migration.texi \ misc.texi \ programming.texi \ diff --git a/third_party/heimdal/doc/NTMakefile b/third_party/heimdal/doc/NTMakefile index 0299620ac11..4769c9126bc 100644 --- a/third_party/heimdal/doc/NTMakefile +++ b/third_party/heimdal/doc/NTMakefile @@ -40,7 +40,6 @@ heimdal_TEXINFOS = \ $(OBJ)\heimdal.texi \ $(OBJ)\install.texi \ $(OBJ)\intro.texi \ - $(OBJ)\kerberos4.texi \ $(OBJ)\migration.texi \ $(OBJ)\misc.texi \ $(OBJ)\programming.texi \ diff --git a/third_party/heimdal/doc/apps.texi b/third_party/heimdal/doc/apps.texi index 98585c4d0a7..2b48edaafaf 100644 --- a/third_party/heimdal/doc/apps.texi +++ b/third_party/heimdal/doc/apps.texi @@ -5,164 +5,10 @@ @chapter Applications @menu -* Authentication modules:: * AFS:: @end menu -@node Authentication modules, AFS, Applications, Applications -@section Authentication modules - -The problem of having different authentication mechanisms has been -recognised by several vendors, and several solutions have appeared. In -most cases these solutions involve some kind of shared modules that are -loaded at run-time. Modules for some of these systems can be found in -@file{lib/auth}. Presently there are modules for Digital's SIA, -and IRIX' @code{login} and @code{xdm} (in -@file{lib/auth/afskauthlib}). - -@menu -* Digital SIA:: -* IRIX:: -@end menu - -@node Digital SIA, IRIX, Authentication modules, Authentication modules -@subsection Digital SIA - -How to install the SIA module depends on which OS version you're -running. Tru64 5.0 has a new command, @file{siacfg}, which makes this -process quite simple. If you have this program, you should just be able -to run: -@example -siacfg -a KRB5 /usr/athena/lib/libsia_krb5.so -@end example - -On older versions, or if you want to do it by hand, you have to do the -following (not tested by us on Tru64 5.0): - -@itemize @bullet - -@item -Make sure @file{libsia_krb5.so} is available in -@file{/usr/athena/lib}. If @file{/usr/athena} is not on local disk, you -might want to put it in @file{/usr/shlib} or someplace else. If you do, -you'll have to edit @file{krb5_matrix.conf} to reflect the new location -(you will also have to do this if you installed in some other directory -than @file{/usr/athena}). If you built with shared libraries, you will -have to copy the shared @file{libkrb.so}, @file{libdes.so}, -@file{libkadm.so}, and @file{libkafs.so} to a place where the loader can -find them (such as @file{/usr/shlib}). -@item -Copy (your possibly edited) @file{krb5_matrix.conf} to @file{/etc/sia}. -@item -Apply @file{security.patch} to @file{/sbin/init.d/security}. -@item -Turn on KRB5 security by issuing @kbd{rcmgr set SECURITY KRB5} and -@kbd{rcmgr set KRB5_MATRIX_CONF krb5_matrix.conf}. -@item -Digital thinks you should reboot your machine, but that really shouldn't -be necessary. It's usually sufficient just to run -@kbd{/sbin/init.d/security start} (and restart any applications that use -SIA, like @code{xdm}.) -@end itemize - -Users with local passwords (like @samp{root}) should be able to login -safely. - -When using Digital's xdm the @samp{KRB5CCNAME} environment variable isn't -passed along as it should (since xdm zaps the environment). Instead you -have to set @samp{KRB5CCNAME} to the correct value in -@file{/usr/lib/X11/xdm/Xsession}. Add a line similar to -@example -KRB5CCNAME=FILE:/tmp/krb5cc`id -u`_`ps -o ppid= -p $$`; export KRB5CCNAME -@end example -If you use CDE, @code{dtlogin} allows you to specify which additional -environment variables it should export. To add @samp{KRB5CCNAME} to this -list, edit @file{/usr/dt/config/Xconfig}, and look for the definition of -@samp{exportList}. You want to add something like: -@example -Dtlogin.exportList: KRB5CCNAME -@end example - -@subsubheading Notes to users with Enhanced security - -Digital's @samp{ENHANCED} (C2) security, and Kerberos solve two -different problems. C2 deals with local security, adds better control of -who can do what, auditing, and similar things. Kerberos deals with -network security. - -To make C2 security work with Kerberos you will have to do the -following. - -@itemize @bullet -@item -Replace all occurrences of @file{krb5_matrix.conf} with -@file{krb5+c2_matrix.conf} in the directions above. -@item -You must enable ``vouching'' in the @samp{default} database. This will -make the OSFC2 module trust other SIA modules, so you can login without -giving your C2 password. To do this use @samp{edauth} to edit the -default entry @kbd{/usr/tcb/bin/edauth -dd default}, and add a -@samp{d_accept_alternate_vouching} capability, if not already present. -@item -For each user who does @emph{not} have a local C2 password, you should -set the password expiration field to zero. You can do this for each -user, or in the @samp{default} table. To do this use @samp{edauth} to -set (or change) the @samp{u_exp} capability to @samp{u_exp#0}. -@item -You also need to be aware that the shipped @file{login}, @file{rcp}, and -@file{rshd}, don't do any particular C2 magic (such as checking for -various forms of disabled accounts), so if you rely on those features, -you shouldn't use those programs. If you configure with -@samp{--enable-osfc2}, these programs will, however, set the login -UID. Still: use at your own risk. -@end itemize - -At present @samp{su} does not accept the vouching flag, so it will not -work as expected. - -Also, kerberised ftp will not work with C2 passwords. You can solve this -by using both Digital's ftpd and our on different ports. - -@strong{Remember}, if you do these changes you will get a system that -most certainly does @emph{not} fulfil the requirements of a C2 -system. If C2 is what you want, for instance if someone else is forcing -you to use it, you're out of luck. If you use enhanced security because -you want a system that is more secure than it would otherwise be, you -probably got an even more secure system. Passwords will not be sent in -the clear, for instance. - -@node IRIX, , Digital SIA, Authentication modules -@subsection IRIX - -The IRIX support is a module that is compatible with Transarc's -@file{afskauthlib.so}. It should work with all programs that use this -library. This should include @command{login} and @command{xdm}. - -The interface is not very documented but it seems that you have to copy -@file{libkafs.so}, @file{libkrb.so}, and @file{libdes.so} to -@file{/usr/lib}, or build your @file{afskauthlib.so} statically. - -The @file{afskauthlib.so} itself is able to reside in -@file{/usr/vice/etc}, @file{/usr/afsws/lib}, or the current directory -(wherever that is). - -IRIX 6.4 and newer seem to have all programs (including @command{xdm} and -@command{login}) in the N32 object format, whereas in older versions they -were O32. For it to work, the @file{afskauthlib.so} library has to be in -the same object format as the program that tries to load it. This might -require that you have to configure and build for O32 in addition to the -default N32. - -Apart from this it should ``just work''; there are no configuration -files. - -Note that recent Irix 6.5 versions (at least 6.5.22) have PAM, -including a @file{pam_krb5.so} module. Not all relevant programs use -PAM, though, e.g.@: @command{ssh}. In particular, for console -graphical login you need to turn off @samp{visuallogin} and turn on -@samp{xdm} with @command{chkconfig}. - -@node AFS, , Authentication modules, Applications +@node AFS, , Applications, Applications @section AFS @cindex AFS @@ -223,48 +69,3 @@ AFS-cell. If keyfile already exists, this will add the new key in afs-srvtab to KeyFile. -@section Using 2b tokens with AFS - -@subsection What is 2b ? - -2b is the name of the proposal that was implemented to give basic -Kerberos 5 support to AFS in rxkad. It's not real Kerberos 5 support -since it still uses fcrypt for data encryption and not Kerberos -encryption types. - -Its only possible (in all cases) to do this for DES encryption types -because only then the token (the AFS equivalent of a ticket) will be -smaller than the maximum size that can fit in the token cache in the -OpenAFS/Transarc client. It is a so tight fit that some extra wrapping -on the ASN1/DER encoding is removed from the Kerberos ticket. - -2b uses a Kerberos 5 EncTicketPart instead of a Kerberos 4 ditto for -the part of the ticket that is encrypted with the service's key. The -client doesn't know what's inside the encrypted data so to the client -it doesn't matter. - -To differentiate between Kerberos 4 tickets and Kerberos 5 tickets, 2b -uses a special kvno, 213 for 2b tokens and 255 for Kerberos 5 tokens. - -Its a requirement that all AFS servers that support 2b also support -native Kerberos 5 in rxkad. - -@subsection Configuring a Heimdal kdc to use 2b tokens - -Support for 2b tokens in the kdc are turned on for specific principals -by adding them to the string list option @code{[kdc]use_2b} in the -kdc's @file{krb5.conf} file. - -@example -[kdc] - use_2b = @{ - afs@@SU.SE = yes - afs/it.su.se@@SU.SE = yes - @} -@end example - -@subsection Configuring AFS clients for 2b support - -There is no need to configure AFS clients for 2b support. The only -software that needs to be installed/upgrade is a Kerberos 5 enabled -@file{afslog}. diff --git a/third_party/heimdal/doc/copyright.texi b/third_party/heimdal/doc/copyright.texi index d9f1a8c2e19..886bf2cdaa0 100644 --- a/third_party/heimdal/doc/copyright.texi +++ b/third_party/heimdal/doc/copyright.texi @@ -10,9 +10,7 @@ @end macro -@node Copyrights and Licenses, , Acknowledgments, Top @comment node-name, next, previous, up -@appendix Copyrights and Licenses @heading Kungliga Tekniska Högskolan diff --git a/third_party/heimdal/doc/heimdal.texi b/third_party/heimdal/doc/heimdal.texi index c8ef24969fb..00041ca76c2 100644 --- a/third_party/heimdal/doc/heimdal.texi +++ b/third_party/heimdal/doc/heimdal.texi @@ -1,4 +1,5 @@ \input texinfo @c -*- texinfo -*- + @c %**start of header @c $Id$ @setfilename heimdal.info @@ -7,9 +8,7 @@ @afourpaper @end iftex @c some sensible characters, please? -@tex -\input latin1.tex -@end tex +@documentencoding UTF-8 @setchapternewpage on @syncodeindex pg cp @c %**end of header @@ -73,7 +72,6 @@ This manual for version @value{VERSION} of Heimdal. * Setting up a realm:: * Applications:: * Things in search for a better place:: -* Kerberos 4 issues:: * Windows compatibility:: * Programming with Kerberos:: * Migration:: @@ -106,19 +104,8 @@ Setting up a realm Applications -* Authentication modules:: * AFS:: -Authentication modules - -* Digital SIA:: -* IRIX:: - -Kerberos 4 issues - -* Principal conversion issues:: -* Converting a version 4 database:: - Windows compatibility * Configuring Windows to use a Heimdal KDC:: @@ -140,14 +127,14 @@ Programming with Kerberos @include setup.texi @include apps.texi @include misc.texi -@include kerberos4.texi @include win2k.texi @include programming.texi @include migration.texi @include ack.texi +@node Copyrights and Licenses, , Acknowledgments, Top + @comment node-name, next, previous, up @include copyright.texi @c @shortcontents -@contents @bye diff --git a/third_party/heimdal/doc/hx509.texi b/third_party/heimdal/doc/hx509.texi index 0a90cb73528..4d0f05682aa 100644 --- a/third_party/heimdal/doc/hx509.texi +++ b/third_party/heimdal/doc/hx509.texi @@ -7,9 +7,7 @@ @afourpaper @end iftex @c some sensible characters, please? -@tex -\input latin1.tex -@end tex +@documentencoding UTF-8 @setchapternewpage on @syncodeindex pg cp @c %**end of header @@ -39,12 +37,14 @@ @def@copyrightstart{} @def@copyrightend{} @end iftex +@ifnottex @macro copynext @end macro @macro copyrightstart @end macro @macro copyrightend @end macro +@end ifnottex @page @copyrightstart diff --git a/third_party/heimdal/doc/kerberos4.texi b/third_party/heimdal/doc/kerberos4.texi deleted file mode 100644 index 41a6508aac9..00000000000 --- a/third_party/heimdal/doc/kerberos4.texi +++ /dev/null @@ -1,173 +0,0 @@ -@c $Id$ - -@node Kerberos 4 issues, Windows compatibility, Things in search for a better place, Top -@comment node-name, next, previous, up -@chapter Kerberos 4 issues - -Kerberos 4 KDC and KA server have been moved. - -For more about AFS, see the section @xref{AFS}. - -@menu -* Principal conversion issues:: -* Converting a version 4 database:: -@end menu - -@node Principal conversion issues, Converting a version 4 database, Kerberos 4 issues, Kerberos 4 issues -@section Principal conversion issues - -First, Kerberos 4 and Kerberos 5 principals are different. A version 4 -principal consists of a name, an instance, and a realm. A version 5 -principal has one or more components, and a realm (the terms ``name'' -and ``instance'' are still used, for the first and second component, -respectively). Also, in some cases the name of a version 4 principal -differs from the first component of the corresponding version 5 -principal. One notable example is the ``host'' type principals, where -the version 4 name is @samp{rcmd} (for ``remote command''), and the -version 5 name is @samp{host}. For the class of principals that has a -hostname as instance, there is an other major difference, Kerberos 4 -uses only the first component of the hostname, whereas Kerberos 5 uses -the fully qualified hostname. - -Because of this it can be hard or impossible to correctly convert a -version 4 principal to a version 5 principal @footnote{the other way is -not always trivial either, but usually easier}. The biggest problem is -to know if the conversion resulted in a valid principal. To give an -example, suppose you want to convert the principal @samp{rcmd.foo}. - -The @samp{rcmd} name suggests that the instance is a hostname (even if -there are exceptions to this rule). To correctly convert the instance -@samp{foo} to a hostname, you have to know which host it is referring -to. You can to this by either guessing (from the realm) which domain -name to append, or you have to have a list of possible hostnames. In the -simplest cases you can cover most principals with the first rule. If you -have several domains sharing a single realm this will not usually -work. If the exceptions are few you can probably come by with a lookup -table for the exceptions. - -In a complex scenario you will need some kind of host lookup mechanism. -Using DNS for this is tempting, but DNS is error prone, slow and unsafe -@footnote{at least until secure DNS is commonly available}. - -Fortunately, the KDC has a trump on hand: it can easily tell if a -principal exists in the database. The KDC will use -@code{krb5_425_conv_principal_ext} to convert principals when handling -to version 4 requests. - -@node Converting a version 4 database, , Principal conversion issues, Kerberos 4 issues -@section Converting a version 4 database - -If you want to convert an existing version 4 database, the principal -conversion issue arises too. - -If you decide to convert your database once and for all, you will only -have to do this conversion once. It is also possible to run a version 5 -KDC as a slave to a version 4 KDC. In this case this conversion will -happen every time the database is propagated. When doing this -conversion, there are a few things to look out for. If you have stale -entries in the database, these entries will not be converted. This might -be because these principals are not used anymore, or it might be just -because the principal couldn't be converted. - -You might also see problems with a many-to-one mapping of -principals. For instance, if you are using DNS lookups and you have two -principals @samp{rcmd.foo} and @samp{rcmd.bar}, where `foo' is a CNAME -for `bar', the resulting principals will be the same. Since the -conversion function can't tell which is correct, these conflicts will -have to be resolved manually. - -@subsection Conversion example - -Given the following set of hosts and services: - -@example -foo.se rcmd -mail.foo.se rcmd, pop -ftp.bar.se rcmd, ftp -@end example - -you have a database that consists of the following principals: - -@samp{rcmd.foo}, @samp{rcmd.mail}, @samp{pop.mail}, @samp{rcmd.ftp}, and -@samp{ftp.ftp}. - -lets say you also got these extra principals: @samp{rcmd.gone}, -@samp{rcmd.old-mail}, where @samp{gone.foo.se} was a machine that has -now passed away, and @samp{old-mail.foo.se} was an old mail machine that -is now a CNAME for @samp{mail.foo.se}. - -When you convert this database you want the following conversions to be -done: -@example -rcmd.foo host/foo.se -rcmd.mail host/mail.foo.se -pop.mail pop/mail.foo.se -rcmd.ftp host/ftp.bar.se -ftp.ftp ftp/ftp.bar.se -rcmd.gone @i{removed} -rcmd.old-mail @i{removed} -@end example - -A @file{krb5.conf} that does this looks like: - -@example -[realms] - FOO.SE = @{ - v4_name_convert = @{ - host = @{ - ftp = ftp - pop = pop - rcmd = host - @} - @} - v4_instance_convert = @{ - foo = foo.se - ftp = ftp.bar.se - @} - default_domain = foo.se - @} -@end example - -The @samp{v4_name_convert} section says which names should be considered -having an instance consisting of a hostname, and it also says how the -names should be converted (for instance @samp{rcmd} should be converted -to @samp{host}). The @samp{v4_instance_convert} section says how a -hostname should be qualified (this is just a hosts-file in -disguise). Host-instances that aren't covered by -@samp{v4_instance_convert} are qualified by appending the contents of -the @samp{default_domain}. - -Actually, this example doesn't work. Or rather, it works to well. Since -it has no way of knowing which hostnames are valid and which are not, it -will happily convert @samp{rcmd.gone} to @samp{host/gone.foo.se}. This -isn't a big problem, but if you have run your kerberos realm for a few -years, chances are big that you have quite a few `junk' principals. - -If you don't want this you can remove the @samp{default_domain} -statement, but then you will have to add entries for @emph{all} your hosts -in the @samp{v4_instance_convert} section. - -Instead of doing this you can use DNS to convert instances. This is not -a solution without problems, but it is probably easier than adding lots -of static host entries. - -To enable DNS lookup you should turn on @samp{v4_instance_resolve} in -the @samp{[libdefaults]} section. - -@subsection Converting a database - -The database conversion is done with @samp{hprop}. You can run this -command to propagate the database to the machine called -@samp{slave-server} (which should be running a @samp{hpropd}). - -@example -hprop --source=krb4-db --master-key=/.m slave-server -@end example - -This command can also be to use for converting the v4 database on the -server: - -@example -hprop -n --source=krb4-db -d /var/kerberos/principal --master-key=/.m | hpropd -n -@end example - diff --git a/third_party/heimdal/doc/migration.texi b/third_party/heimdal/doc/migration.texi index 2fa7ede597a..7c3e1e70ef4 100644 --- a/third_party/heimdal/doc/migration.texi +++ b/third_party/heimdal/doc/migration.texi @@ -16,6 +16,10 @@ To load the MIT Kerberos dump file, use the following command: kadmin can dump in MIT Kerberos format. Simply run: @samp{kadmin -l dump -f MIT}. +There are some limitations in this functionality. Users should check +the input dump and a native dump after loading to check for +differences. + The Heimdal KDC and kadmind, as well as kadmin -l and the libkadm5srv library can read and write MIT KDBs, and can read MIT stash files. To build with KDB support requires having a standalone libdb from MIT @@ -35,8 +39,6 @@ and hpropd. @section General issues -When migrating from a Kerberos 4 KDC. - @section Order in what to do things: @itemize @bullet @@ -63,11 +65,5 @@ you can also check the kdc-log to check what ticket are checked out. @item Burn the bridge and change the master. @item Let all users use the Kerberos 5 tools by default. -@item Turn off services that do not need Kerberos 4 authentication. - -Things that might be hard to get away is old programs with support for -Kerberos 4. Example applications are old Eudora installations using -KPOP, and Zephyr. Eudora can use the Kerberos 4 kerberos in the Heimdal -kdc. @end itemize diff --git a/third_party/heimdal/doc/misc.texi b/third_party/heimdal/doc/misc.texi index 1ad6aaab054..2d976f45d76 100644 --- a/third_party/heimdal/doc/misc.texi +++ b/third_party/heimdal/doc/misc.texi @@ -1,6 +1,6 @@ @c $Id$ -@node Things in search for a better place, Kerberos 4 issues, Applications, Top +@node Things in search for a better place, Windows compatibility, Applications, Top @chapter Things in search for a better place @section Making things work on Ciscos diff --git a/third_party/heimdal/doc/setup.texi b/third_party/heimdal/doc/setup.texi index 0b3a860edb1..962541359ee 100644 --- a/third_party/heimdal/doc/setup.texi +++ b/third_party/heimdal/doc/setup.texi @@ -6,15 +6,19 @@ A @cindex realm -realm is an administrative domain. The name of a Kerberos realm is -usually the Internet domain name in uppercase. Call your realm the same -as your Internet domain name if you do not have strong reasons for not +realm is an administrative domain containing any number of Kerberos +principals and namespaces. The name of a Kerberos realm is +usually a domain name in uppercase. Call your realm the same +as your site's domain name if you do not have strong reasons for not doing so. It will make life easier for you and everyone else. @menu * Configuration file:: * Creating the database:: * Modifying the database:: +* Using namespaces and synthetic principals to keep the database small:: +* Using hard aliases for realm migration:: +* Using soft aliases for configuring referrals:: * Checking the setup:: * keytabs:: * Remote administration:: @@ -40,7 +44,8 @@ To setup a realm you will first have to create a configuration file: @file{/etc/krb5.conf}. The @file{krb5.conf} file can contain many configuration options, some of which are described here. -There is a sample @file{krb5.conf} supplied with the distribution. +There is a sample @file{krb5.conf} supplied with the distribution, and +a page for it in section 5 of the system manual. The configuration file is a hierarchical structure consisting of sections, each containing a list of bindings (either variable @@ -84,11 +89,9 @@ are briefly described here. The @samp{libdefaults} section contains a list of library configuration parameters, such as the default realm and the timeout for KDC responses. The @samp{realms} section contains information about specific -realms, such as where they hide their KDC@. This section serves the same -purpose as the Kerberos 4 @file{krb.conf} file, but can contain more -information. Finally the @samp{domain_realm} section contains a list of -mappings from domains to realms, equivalent to the Kerberos 4 -@file{krb.realms} file. +realms, such as where they hide their KDC@. +Finally the @samp{domain_realm} section contains a list of +mappings from domains to realms. To continue with the realm setup, you will have to create a configuration file, with contents similar to the following. @@ -108,14 +111,17 @@ with contents similar to the following. @end example -If you use a realm name equal to your domain name, you can omit the -@samp{libdefaults}, and @samp{domain_realm}, sections. If you have a DNS -SRV-record for your realm, or your Kerberos server has DNS CNAME -@samp{kerberos.my.realm}, you can omit the @samp{realms} section too. +When realm names correspond to domain names, one can avoid having to +configure @samp{domain_realm} mappings, and one can avoid having to +configure a @samp{default_realm} in the @samp{libdefaults} section. +DNS SRV resource records can be used for KDC discovery, obviating the +need list KDCs in the @samp{realms} section of the @samp{krb5.conf} +file. @cindex KRB5_CONFIG -If you want to use a different configuration file then the default you -can point a file with the environment variable @samp{KRB5_CONFIG}. +The Heimdal libraries and commands (and the MIT ones too), support the +use of the environment variable @samp{KRB5_CONFIG} for using an +alternative configuration. @example env KRB5_CONFIG=$HOME/etc/krb5.conf kinit user@@REALM @@ -124,15 +130,16 @@ env KRB5_CONFIG=$HOME/etc/krb5.conf kinit user@@REALM @cindex GSS_MECH_CONFIG The GSS-API mechanism configuration file can also be changed from the default with the enviornment variable @samp{GSS_MECH_CONFIG}. Note that -this file only configures additional plugin mechanisms: Kerberos, NTLM -and SPNEGO are built in to the Heimdal GSS-API library. +this file can only configure additional plugin mechanisms: Kerberos, +NTLM and SPNEGO are built in to the Heimdal GSS-API library. @node Creating the database, Modifying the database, Configuration file, Setting up a realm @section Creating the database -The database library will look for the database in the directory -@file{@value{dbdir}}, so you should probably create that directory. -Make sure the directory has restrictive permissions. +The Heimdal database library, @code{libhdb}, will look for the +database in the directory @file{@value{dbdir}}, so you should probably +create that directory. Make sure the directory has restrictive +permissions. @example # mkdir /var/heimdal @@ -141,8 +148,8 @@ Make sure the directory has restrictive permissions. Heimdal supports various database backends: lmdb (LMDB), db3 (Berkeley DB 3.x, 4.x, or 5.x), db1 (Berkeley DB 2.x), sqlite (SQLite3), and ldap -(LDAP). The default is @value{dbtype}, and is selected at build time -from one of lmdb, db3, or db1. +(LDAP). The default is @value{dbtype}, and is selected at configure +time from one of lmdb, db3, or db1. These defaults can be overriden in the 'database' key in the @samp{kdc} section of the configuration. @@ -179,6 +186,11 @@ on which attackers can't do a dictionary attack. If you have a master key, make sure you make a backup of your master key file; without it backups of the database are of no use. +Note that encryption of the keys in the database is only useful when +the database is stored on external storage media that is easy to +steal. Thus for the most part there is no need to encrypt the keys in +the database. + To initialise the database use the @command{kadmin} program, with the @kbd{-l} option (to enable local database mode). First issue a @kbd{init MY.REALM} command. This will create the database and insert @@ -233,7 +245,7 @@ krbtgt/MY.REALM@@MY.REALM 1:0:1:52b53b61c875ce16:-:0:7:c8943be ... kadmin/changepw@@MY.REALM 1:0:1:f48c8af2b340e9fb:-:0:7:e3e6088 ... @end smallexample -@node Modifying the database, Checking the setup, Creating the database, Setting up a realm +@node Modifying the database, Using namespaces and synthetic principals to keep the database small, Creating the database, Setting up a realm @section Modifying the database All modifications of principals are done with with kadmin. @@ -295,7 +307,101 @@ R second @c Describe more of kadmin commands here... -@node Checking the setup, keytabs, Modifying the database, Setting up a realm +@node Using namespaces and synthetic principals to keep the database small, Checking the setup, Modifying the database, Setting up a realm +@section Using namespaces and synthetic principals to keep the database small + +Keeping a Kerberos database small is useful for several reasons: + +@itemize @bullet +@item to avoid low write transaction rates +@item to avoid replication latency +@item to keep re-keying costs down +@end itemize + +To avoid needing database entries for client principals, configure and +enable PKINIT and synthetic principals. Alternatively, configure and +enable the use of GSS-API pre-authentication, though this is currently +experimental. + +With synthetic client principals enabled, client principals will be +deemed to exist if they can pre-authenticate using a method that +yields an authenticated principal name, and if the client principal +does not already exist. + +To lock out or disable a specific synthetic client principal, create +it in the database with the desired attributes. + +To avoid needing database entries for host-based service principals, +create virtual host-based service principal namespaces using the +@command{add_ns} sub-command of the @command{kadmin} command. Virtual +host-based service principals will exist for every possible hostname +under a containing namespace, with keys derived from the namespace's +based keys and the current key rotation period. The long-term keys of +virtual host-based service principals rotate on a hard schedule as +configured for their namespaces, so hosts and applications using them +must keep re-fetching their @samp{keytabs}. See the manual pages for +@file{krb5.conf}, @command{kadmin}, and @command{httpkadmind} for more +details. + +Using these features one can end up with a database that contains just +@code{krbtgt} principals, principals for locked users, and principals +that are neither @code{krbtgt}, user, nor host-based services. + +@node Using hard aliases for realm migration, Using soft aliases for configuring referrals, Using namespaces and synthetic principals to keep the database small, Setting up a realm +@section Using hard aliases for realm migration + +The Heimdal @command{kadmin} command can be used to add aliases to +principal entries in the Heimdal database. Aliases of principals of +the form @samp{WELLKNOWN/REFERRALS/TARGET} or +@samp{WELLKNOWN/REFERRALS/TARGET/anything} are "soft" aliases. +Aliases of principals of other forms are "hard" aliases. + +When a client makes a request for a principal's alias, and it does not +use the KDC request "canonicalize" option flag, the Heimdal KDC will +treat the alias as a distinct principal that happens to share +attributes and long-term symmetric keys and salts with the principal +it is an alias of. + +This is useful for, for example, ensuring that host-based principals +can be referred to by any aliases. + +This can also be very useful for renaming realms: add new +@code{krbtgt} principals for the new realms, then add aliases to +existing principals in their new realms. For example, a user with a +principal @code{joe@@A} can be given an alias of +@code{joes@@B}, and +then they can @code{kinit joes@@B} and get Kerberos tickets for +@code{joes@@B}. Similarly, a service principal such as +@code{HTTP/foo.bar.baz.example@@BAZ.EXAMPLE} can be given an alias such as +@code{HTTP/foo.bar.baz.example@@BAR.BAZ.EXAMPLE}, or even +@code{HTTP/foobar.new-domain.example@@NEW-DOMAIN.EXAMPLE}, and +requesting tickets with those aliases as the service names will work. + +@node Using soft aliases for configuring referrals, Checking the setup, Using hard aliases for realm migration, Setting up a realm +@section Using soft aliases for configuring referrals + +Soft aliases, which are aliases of principals of the form +@code{WELLKNOWN/REFERRALS/TARGET} or +@code{WELLKNOWN/REFERRALS/TARGET/anything}, are used to generate +referrals to other realms. Specifically, the realm of a soft alias' +canonical name is the realm to issue referrals to. + +Soft aliases can be used to configure individual referrals, but also +of entire namespaces of hostnames. To configure the issuance of +referrals for entire namespaces, make a soft alias of the form +@code{WELLKNOWN/HOSTBASED-NAMESPACE/service/namespace-fqdn@@REALM} to +have the TGS for that @samp{REALM} issue referrals for all principals +of the form @code{service/hostname@@REALM} where the hostname component +is a sub-domain of the namespace component of the alias name. + +For example, a soft alias name +@code{WELLKNOWN/HOSTBASED-NAMESPACE/host/cloud.bar.example@@BAR.EXAMPLE} +to a realm @samp{B} will cause the KDC to issue referrals to @samp{B} +for any principals such as +@samp{host/foo.cloud.bar.example@@BAR.EXAMPLE}, and +@samp{host/baz.cloud.bar.example@@BAR.EXAMPLE}, and so on. + +@node Checking the setup, keytabs, Using namespaces and synthetic principals to keep the database small, Setting up a realm @section Checking the setup There are two tools that can check the consistency of the Kerberos @@ -674,10 +780,6 @@ fixed size encryption key. In Kerberos 5 the salt is determined by the encryption type, except in some special cases. -In @code{des} there is the Kerberos 4 salt -(none at all) or the afs-salt (using the cell (realm in -AFS lingo)). - In @code{arcfour} (the encryption type that Microsoft Windows 2000 uses) there is no salt. This is to be compatible with NTLM keys in Windows NT 4. @@ -696,12 +798,6 @@ no salt at all). Common types of salting include @itemize @bullet -@item @code{v4} (or @code{des:pw-salt:}) - -The Kerberos 4 salting is using no salt at all. Reason there is colon -at the end of the salt string is that it makes the salt the empty -string (same as no salt). - @item @code{v5} (or @code{pw-salt}) @code{pw-salt} uses the default salt for each encryption type is diff --git a/third_party/heimdal/doc/whatis.texi b/third_party/heimdal/doc/whatis.texi index 902344b0352..2b0e98de552 100644 --- a/third_party/heimdal/doc/whatis.texi +++ b/third_party/heimdal/doc/whatis.texi @@ -133,8 +133,10 @@ It would be possible to add a @dfn{replay cache} to the server side. The idea is to save the authenticators sent during the last few minutes, so that @var{B} can detect when someone is trying to retransmit an already used message. This is somewhat impractical -(mostly regarding efficiency), and is not part of Kerberos 4; MIT -Kerberos 5 contains it. +(mostly regarding performance); MIT Kerberos 5 has a replay cache, +while Heimdal does not. + +However, most GSS-API applicatons do not need a replay cache at all. To authenticate @var{B}, @var{A} might request that @var{B} sends something back that proves that @var{B} has access to the session diff --git a/third_party/heimdal/doc/win2k.texi b/third_party/heimdal/doc/win2k.texi index 0fefeee3fdc..d4ab2fecda9 100644 --- a/third_party/heimdal/doc/win2k.texi +++ b/third_party/heimdal/doc/win2k.texi @@ -1,7 +1,7 @@ @c $Id$ -@node Windows compatibility, Programming with Kerberos, Kerberos 4 issues, Top +@node Windows compatibility, Programming with Kerberos, Things in search for a better place, Top @comment node-name, next, previous, up @chapter Windows compatibility @@ -126,8 +126,7 @@ You also need to add the inter-realm keys to the Heimdal KDC. But take care to the encryption types and salting used for those keys. There should be no encryption type stronger than the one configured on Windows side for this relationship, itself limited to the ones supported by this specific version of -Windows, nor any Kerberos 4 salted hashes, as Windows does not seem to -understand them. Otherwise, the trust will not works. +Windows. Otherwise, the trust will not works. Here are the version-specific needed information: @enumerate @@ -235,11 +234,11 @@ analysing the data. @comment node-name, next, previous, up @section Quirks of Windows 2000 KDC -There are some issues with salts and Windows 2000. Using an empty salt---which is the only one that Kerberos 4 supported, and is therefore known -as a Kerberos 4 compatible salt---does not work, as far as we can tell -from out experiments and users' reports. Therefore, you have to make -sure you keep around keys with all the different types of salts that are -required. Microsoft have fixed this issue post Windows 2003. +There are some issues with salts and Windows 2000. Using an empty salt does +not work, as far as we can tell from out experiments and users' reports. +Therefore, you have to make sure you keep around keys with all the different +types of salts that are required. Microsoft have fixed this issue post Windows +2003. Microsoft seems also to have forgotten to implement the checksum algorithms @samp{rsa-md4-des} and @samp{rsa-md5-des}. This can make Name diff --git a/third_party/heimdal/include/config.h.w32 b/third_party/heimdal/include/config.h.w32 index 5521181d27c..6e0f6bcf147 100644 --- a/third_party/heimdal/include/config.h.w32 +++ b/third_party/heimdal/include/config.h.w32 @@ -26,13 +26,13 @@ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. - * + * **********************************************************************/ #ifndef __CONFIG_H__ #define __CONFIG_H__ -#define fallthrough do {} while(0) /* fallthrough */ +#define HEIM_FALLTHROUGH do {} while(0) /* fallthrough */ #ifndef RCSID #define RCSID(msg) \ diff --git a/third_party/heimdal/kadmin/NTMakefile b/third_party/heimdal/kadmin/NTMakefile index 80d05ec7adb..c7f2b7f6b5c 100644 --- a/third_party/heimdal/kadmin/NTMakefile +++ b/third_party/heimdal/kadmin/NTMakefile @@ -119,17 +119,17 @@ $(OBJ)\add_random_users.exe: $(OBJ)\add_random_users.obj $(LIBKADM5SRV) $(LIBKAD $(EXECONLINK) Secur32.lib Shell32.lib $(EXEPREP_NODIST) -TEST_BINARIES=$(OBJ)\test_util.exe - -$(OBJ)\test_util.exe: $(OBJ)\test_util.obj $(OBJ)\util.obj $(KADMIN_LIBS) - $(EXECONLINK) Secur32.lib Shell32.lib - $(EXEPREP_NODIST) - -test-binaries: $(TEST_BINARIES) - -test-run: - cd $(OBJ) - test_util.exe - cd $(SRCDIR) - -test:: test-binaries test-run +#TEST_BINARIES=$(OBJ)\test_util.exe +# +#$(OBJ)\test_util.exe: $(OBJ)\test_util.obj $(OBJ)\util.obj $(KADMIN_LIBS) +# $(EXECONLINK) Secur32.lib Shell32.lib +# $(EXEPREP_NODIST) +# +#test-binaries: $(TEST_BINARIES) +# +#test-run: +# cd $(OBJ) +# test_util.exe +# cd $(SRCDIR) +# +test:: #test-binaries test-run diff --git a/third_party/heimdal/kadmin/check.c b/third_party/heimdal/kadmin/check.c index ad14ed224c4..a8782ca43bf 100644 --- a/third_party/heimdal/kadmin/check.c +++ b/third_party/heimdal/kadmin/check.c @@ -73,7 +73,7 @@ do_check_entry(krb5_principal principal, void *data) return 1; memset (&princ, 0, sizeof(princ)); - ret = kadm5_get_principal(kadm_handle, principal, &princ, + ret = kadm5_get_principal(data, principal, &princ, KADM5_PRINCIPAL | KADM5_KEY_DATA); if(ret) { krb5_warn(context, ret, "Failed to get principal: %s", name); @@ -95,7 +95,7 @@ do_check_entry(krb5_principal principal, void *data) } free(name); - kadm5_free_principal_ent(kadm_handle, &princ); + kadm5_free_principal_ent(data, &princ); return 0; } @@ -106,6 +106,7 @@ check(void *opt, int argc, char **argv) kadm5_principal_ent_rec ent; krb5_error_code ret; char *realm = NULL, *p, *p2; + void *inner_kadm_handle = NULL; int found; if (argc == 0) { @@ -254,7 +255,15 @@ check(void *opt, int argc, char **argv) } } - foreach_principal("*", do_check_entry, "check", NULL); + ret = kadm5_dup_context(kadm_handle, &inner_kadm_handle); + if (ret == 0) + ret = foreach_principal("*", do_check_entry, "check", inner_kadm_handle); + if (inner_kadm_handle) + kadm5_destroy(inner_kadm_handle); + if (ret) { + krb5_warn(context, ret, "Could not iterate principals in realm"); + goto fail; + } free(realm); return 0; diff --git a/third_party/heimdal/kadmin/cpw.c b/third_party/heimdal/kadmin/cpw.c index 2f3c1c1bcd7..7ffc828cf30 100644 --- a/third_party/heimdal/kadmin/cpw.c +++ b/third_party/heimdal/kadmin/cpw.c @@ -40,18 +40,19 @@ struct cpw_entry_data { int random_password; char *password; krb5_key_data *key_data; + void *kadm_handle; }; static int -set_random_key (krb5_principal principal, int keepold) +set_random_key(void *dup_kadm_handle, krb5_principal principal, int keepold) { krb5_error_code ret; int i; krb5_keyblock *keys; int num_keys; - ret = kadm5_randkey_principal_3(kadm_handle, principal, keepold, 0, NULL, - &keys, &num_keys); + ret = kadm5_randkey_principal_3(dup_kadm_handle, principal, keepold, 0, + NULL, &keys, &num_keys); if(ret) return ret; for(i = 0; i < num_keys; i++) @@ -61,7 +62,9 @@ set_random_key (krb5_principal principal, int keepold) } static int -set_random_password (krb5_principal principal, int keepold) +set_random_password(void *dup_kadm_handle, + krb5_principal principal, + int keepold) { krb5_error_code ret; char pw[128]; @@ -72,7 +75,8 @@ set_random_password (krb5_principal principal, int keepold) return ret; random_password(pw, sizeof(pw)); - ret = kadm5_chpass_principal_3(kadm_handle, principal, keepold, 0, NULL, pw); + ret = kadm5_chpass_principal_3(dup_kadm_handle, principal, keepold, 0, + NULL, pw); if (ret == 0) printf ("%s's password set to \"%s\"\n", princ_name, pw); free(princ_name); @@ -81,7 +85,10 @@ set_random_password (krb5_principal principal, int keepold) } static int -set_password (krb5_principal principal, char *password, int keepold) +set_password(void *dup_kadm_handle, + krb5_principal principal, + char *password, + int keepold) { krb5_error_code ret = 0; char pwbuf[128]; @@ -108,18 +115,21 @@ set_password (krb5_principal principal, char *password, int keepold) password = pwbuf; } if(ret == 0) - ret = kadm5_chpass_principal_3(kadm_handle, principal, keepold, 0, NULL, - password); + ret = kadm5_chpass_principal_3(dup_kadm_handle, principal, keepold, 0, + NULL, password); memset_s(pwbuf, sizeof(pwbuf), 0, sizeof(pwbuf)); return ret; } static int -set_key_data (krb5_principal principal, krb5_key_data *key_data, int keepold) +set_key_data(void *dup_kadm_handle, + krb5_principal principal, + krb5_key_data *key_data, + int keepold) { krb5_error_code ret; - ret = kadm5_chpass_principal_with_key_3(kadm_handle, principal, keepold, + ret = kadm5_chpass_principal_with_key_3(dup_kadm_handle, principal, keepold, 3, key_data); return ret; } @@ -130,13 +140,13 @@ do_cpw_entry(krb5_principal principal, void *data) struct cpw_entry_data *e = data; if (e->random_key) - return set_random_key (principal, e->keepold); + return set_random_key(e->kadm_handle, principal, e->keepold); else if (e->random_password) - return set_random_password (principal, e->keepold); + return set_random_password(e->kadm_handle, principal, e->keepold); else if (e->key_data) - return set_key_data (principal, e->key_data, e->keepold); + return set_key_data(e->kadm_handle, principal, e->key_data, e->keepold); else - return set_password (principal, e->password, e->keepold); + return set_password(e->kadm_handle, principal, e->password, e->keepold); } int @@ -148,6 +158,10 @@ cpw_entry(struct passwd_options *opt, int argc, char **argv) int num; krb5_key_data key_data[3]; + data.kadm_handle = NULL; + ret = kadm5_dup_context(kadm_handle, &data.kadm_handle); + if (ret) + krb5_err(context, 1, ret, "Could not duplicate kadmin connection"); data.random_key = opt->random_key_flag; data.random_password = opt->random_password_flag; data.password = opt->password_string; @@ -206,6 +220,8 @@ cpw_entry(struct passwd_options *opt, int argc, char **argv) for(i = 0; i < argc; i++) ret = foreach_principal(argv[i], do_cpw_entry, "cpw", &data); + kadm5_destroy(data.kadm_handle); + if (data.key_data) { int16_t dummy; kadm5_free_key_data (kadm_handle, &dummy, key_data); diff --git a/third_party/heimdal/kadmin/del.c b/third_party/heimdal/kadmin/del.c index a066f56ea38..320fe6e8eab 100644 --- a/third_party/heimdal/kadmin/del.c +++ b/third_party/heimdal/kadmin/del.c @@ -37,7 +37,7 @@ static int do_del_entry(krb5_principal principal, void *data) { - return kadm5_delete_principal(kadm_handle, principal); + return kadm5_delete_principal(data, principal); } int @@ -45,12 +45,15 @@ del_entry(void *opt, int argc, char **argv) { int i; krb5_error_code ret = 0; + void *dup_kadm_handle = NULL; - for(i = 0; i < argc; i++) { - ret = foreach_principal(argv[i], do_del_entry, "del", NULL); - if (ret) - break; - } + ret = kadm5_dup_context(kadm_handle, &dup_kadm_handle); + + for (i = 0; ret == 0 && i < argc; i++) + ret = foreach_principal(argv[i], do_del_entry, "del", dup_kadm_handle); + + if (dup_kadm_handle) + kadm5_destroy(dup_kadm_handle); return ret != 0; } @@ -91,12 +94,14 @@ del_namespace(void *opt, int argc, char **argv) { int i; krb5_error_code ret = 0; - - for(i = 0; i < argc; i++) { - ret = foreach_principal(argv[i], do_del_ns_entry, "del_ns", NULL); - if (ret) - break; - } + void *dup_kadm_handle = NULL; + + ret = kadm5_dup_context(kadm_handle, &dup_kadm_handle); + for (i = 0; ret == 0 && i < argc; i++) + ret = foreach_principal(argv[i], do_del_ns_entry, "del_ns", + dup_kadm_handle); + if (dup_kadm_handle) + kadm5_destroy(dup_kadm_handle); return ret != 0; } diff --git a/third_party/heimdal/kadmin/ext.c b/third_party/heimdal/kadmin/ext.c index adb2e28518a..04d4d79a17b 100644 --- a/third_party/heimdal/kadmin/ext.c +++ b/third_party/heimdal/kadmin/ext.c @@ -40,6 +40,7 @@ struct ext_keytab_data { int random_key_flag; size_t nkstuple; krb5_key_salt_tuple *kstuple; + void *kadm_handle; }; static int @@ -59,7 +60,7 @@ do_ext_keytab(krb5_principal principal, void *data) if (!e->random_key_flag) mask |= KADM5_KVNO | KADM5_KEY_DATA; - ret = kadm5_get_principal(kadm_handle, principal, &princ, mask); + ret = kadm5_get_principal(e->kadm_handle, principal, &princ, mask); if (ret) return ret; @@ -112,7 +113,7 @@ do_ext_keytab(krb5_principal principal, void *data) n_k++; } } else if (e->random_key_flag) { - ret = kadm5_randkey_principal_3(kadm_handle, principal, e->keep, + ret = kadm5_randkey_principal_3(e->kadm_handle, principal, e->keep, e->nkstuple, e->kstuple, &k, &n_k); if (ret) goto out; @@ -140,7 +141,7 @@ do_ext_keytab(krb5_principal principal, void *data) } out: - kadm5_free_principal_ent(kadm_handle, &princ); + kadm5_free_principal_ent(e->kadm_handle, &princ); if (k) { for (i = 0; i < n_k; i++) memset(k[i].keyvalue.data, 0, k[i].keyvalue.length); @@ -159,6 +160,10 @@ ext_keytab(struct ext_keytab_options *opt, int argc, char **argv) const char *enctypes; size_t i; + data.kadm_handle = NULL; + ret = kadm5_dup_context(kadm_handle, &data.kadm_handle); + if (ret) + krb5_err(context, 1, ret, "Could not duplicate kadmin connection"); data.random_key_flag = opt->random_key_flag; data.keep = 1; i = 0; @@ -209,6 +214,7 @@ ext_keytab(struct ext_keytab_options *opt, int argc, char **argv) break; } + kadm5_destroy(data.kadm_handle); krb5_kt_close(context, data.keytab); free(data.kstuple); return ret != 0; diff --git a/third_party/heimdal/kadmin/get.c b/third_party/heimdal/kadmin/get.c index a884e11e96b..6e8ada01ea4 100644 --- a/third_party/heimdal/kadmin/get.c +++ b/third_party/heimdal/kadmin/get.c @@ -83,7 +83,9 @@ struct get_entry_data { uint32_t extra_mask; struct field_info *chead, **ctail; const char *krb5_config_fname; + void *kadm_handle; uint32_t n; + int upto; }; static int @@ -478,13 +480,18 @@ do_get_entry(krb5_principal principal, void *data) krb5_error_code ret; struct get_entry_data *e = data; + if (e->upto == 0) + return EINTR; + if (e->upto > 0) + e->upto--; + memset(&princ, 0, sizeof(princ)); - ret = kadm5_get_principal(kadm_handle, principal, + ret = kadm5_get_principal(e->kadm_handle, principal, &princ, e->mask | e->extra_mask); if (ret == 0) { (e->format)(e, &princ); - kadm5_free_principal_ent(kadm_handle, &princ); + kadm5_free_principal_ent(e->kadm_handle, &princ); } e->n++; @@ -534,8 +541,14 @@ static int do_list_entry(krb5_principal principal, void *data) { char buf[1024]; + int *upto = data; krb5_error_code ret; + if (*upto == 0) + return EINTR; + if (*upto > 0) + (*upto)--; + ret = krb5_unparse_name_fixed_short(context, principal, buf, sizeof(buf)); if (ret != 0) return ret; @@ -544,13 +557,13 @@ do_list_entry(krb5_principal principal, void *data) } static int -listit(const char *funcname, int argc, char **argv) +listit(const char *funcname, int upto, int argc, char **argv) { int i; krb5_error_code ret, saved_ret = 0; for (i = 0; i < argc; i++) { - ret = foreach_principal(argv[i], do_list_entry, funcname, NULL); + ret = foreach_principal(argv[i], do_list_entry, funcname, &upto); if (saved_ret == 0 && ret != 0) saved_ret = ret; } @@ -577,14 +590,19 @@ getit(struct get_options *opt, const char *name, int argc, char **argv) opt->short_flag = 1; if (opt->terse_flag) - return listit(name, argc, argv); + return listit(name, opt->upto_integer, argc, argv); + data.kadm_handle = NULL; + ret = kadm5_dup_context(kadm_handle, &data.kadm_handle); + if (ret) + krb5_err(context, 1, ret, "Could not duplicate kadmin connection"); data.table = NULL; data.chead = NULL; data.ctail = &data.chead; data.mask = 0; data.extra_mask = 0; data.krb5_config_fname = opt->krb5_config_file_string; + data.upto = opt->upto_integer; data.n = 0; if(opt->short_flag) { @@ -610,6 +628,8 @@ getit(struct get_options *opt, const char *name, int argc, char **argv) for(i = 0; i < argc; i++) ret = foreach_principal(argv[i], do_get_entry, name, &data); + kadm5_destroy(data.kadm_handle); + if(data.table != NULL) { rtbl_format(data.table, stdout); rtbl_destroy(data.table); @@ -638,5 +658,6 @@ list_princs(struct list_options *opt, int argc, char **argv) get_opt.short_flag = opt->short_flag; get_opt.terse_flag = opt->terse_flag; get_opt.column_info_string = opt->column_info_string; + get_opt.upto_integer = opt->upto_integer; return getit(&get_opt, "list", argc, argv); } diff --git a/third_party/heimdal/kadmin/kadmin-commands.in b/third_party/heimdal/kadmin/kadmin-commands.in index e8a1e8a08f2..db9c4415e6c 100644 --- a/third_party/heimdal/kadmin/kadmin-commands.in +++ b/third_party/heimdal/kadmin/kadmin-commands.in @@ -500,6 +500,12 @@ command = { type = "string" help = "filename to save the principal's krb5.confg in" } + option = { + long = "upto" + type = "integer" + default = "-1" + help = "maximum number of principals to get/list" + } argument = "principal..." min_args = "1" help = "Shows information about principals matching the expressions." @@ -674,6 +680,13 @@ command = { option = { long = "krb5-config-file" type = "string" + help = "only use this option with the get command" + } + option = { + long = "upto" + type = "integer" + default = "-1" + help = "maximum number of principals to get/list" } argument = "principal..." min_args = "1" diff --git a/third_party/heimdal/kadmin/kadmin.1 b/third_party/heimdal/kadmin/kadmin.1 index b0e852931c6..ded59979461 100644 --- a/third_party/heimdal/kadmin/kadmin.1 +++ b/third_party/heimdal/kadmin/kadmin.1 @@ -150,14 +150,34 @@ This command has the following aliases: .Bd -ragged -offset indent Adds one or more aliases to the given principal. .Pp -When a client requests a service ticket for a service principal -name that is an alias of a principal in a different realm, the -TGS will return a referral to that realm. -This compares favorably to using +There are two types of aliases: hard, and soft. +A soft alias is an alias of a principal of the form +.Ar WELLKNOWN/REFERRALS/TARGET@target_realm +or +.Ar WELLKNOWN/REFERRALS/TARGET/arbitrary-component@target_realm . +A hard alias is an alias of any normal principal, even if in a +different realm. +.Pp +Hard aliases are treated as distinct principals sharing +attributes and keys with their canonical principals. +If a client requests canonicalization of a hard alias name, the +KDC will use the canonical name in the ticket issued as long as +the alias and canonical names are in the same realm. +Conversely, if a client does not request canonicalization, or if +the hard alias and the canonical name have different realms, then +the KDC will issue a ticket for the alias name. +.Pp +Soft aliases can only be used to configure the production of +referrals by the KDC. +When a client requests a ticket for a principal that turns out to +be a soft alias, the KDC will respond with a referral to the +alias' canonical name's realm. +.Pp +Soft aliasing compares favorably to using .Ar [domain_realm] entries in the KDC's -.Ar krb5.conf , -but may be managed via the +.Ar krb5.conf : +soft aliases may be managed via the .Nm kadmin command and its .Nm add_alias @@ -166,9 +186,9 @@ and sub-commands rather than having to edit the KDC's configuration file and having to restart the KDC. .Pp -There are two methods for issuing referrals for entire namespaces -of hostnames. -An alias of the form +There are two methods for configuring the issuance of referrals +for entire namespaces of hostnames. +A soft alias of the form .Ar WELLKNOWN/HOSTBASED-NAMESPACE/service/namespace-fqdn@REALM (see .Nm add_namespace @@ -402,11 +422,15 @@ only change the ones specified. .Pp The .Fl Fl alias= Ns Ar alias-name -option may be given multiple times, which will set the complete -list of aliases for the principal. +option may be given multiple times. +If this option is used at all, the complete list of aliases must +be given, with one option per-alias. +If the list given has fewer aliases than the principal had prior +to the modification, then the missing aliases will be deleted. +.Pp Use the .Nm add_alias -command instead to add an alias without having to list all +command instead to add an alias to avoid having to list all existing aliases to keep. .Pp The diff --git a/third_party/heimdal/kadmin/kadmin_locl.h b/third_party/heimdal/kadmin/kadmin_locl.h index d76265b6cf7..6ad36b9094c 100644 --- a/third_party/heimdal/kadmin/kadmin_locl.h +++ b/third_party/heimdal/kadmin/kadmin_locl.h @@ -98,6 +98,7 @@ extern krb5_context context; extern void * kadm_handle; +extern int list_chunk_size; #undef ALLOC #define ALLOC(X) ((X) = malloc(sizeof(*(X)))) diff --git a/third_party/heimdal/kadmin/kadmind.c b/third_party/heimdal/kadmin/kadmind.c index 444950623f0..cf335d6dc01 100644 --- a/third_party/heimdal/kadmin/kadmind.c +++ b/third_party/heimdal/kadmin/kadmind.c @@ -45,6 +45,7 @@ static int debug_flag; static int readonly_flag; static char *port_str; char *realm; +int list_chunk_size = -1; static int detach_from_console = -1; int daemon_child = -1; @@ -72,6 +73,9 @@ static struct getargs args[] = { { "debug", 'd', arg_flag, &debug_flag, "enable debugging", NULL }, + { "list-chunk-size", 0, arg_integer,&list_chunk_size, + "set the LIST streaming count of names per chunk", "NUMBER" + }, { "detach", 0 , arg_flag, &detach_from_console, "detach from console", NULL diff --git a/third_party/heimdal/kadmin/mod.c b/third_party/heimdal/kadmin/mod.c index 7c7b2dd7ce4..3bcd9ac31d5 100644 --- a/third_party/heimdal/kadmin/mod.c +++ b/third_party/heimdal/kadmin/mod.c @@ -308,16 +308,24 @@ add_krb5_config(kadm5_principal_ent_rec *princ, const char *fname) add_tl(princ, KRB5_TL_EXTENSION, &buf); } +struct mod_data { + struct modify_namespace_key_rotation_options *opt_ns_kr; + struct modify_namespace_options *opt_ns; + struct modify_options *opt; + void *kadm_handle; +}; + static int do_mod_entry(krb5_principal principal, void *data) { krb5_error_code ret; kadm5_principal_ent_rec princ; int mask = 0; - struct modify_options *e = data; + struct mod_data *m = data; + struct modify_options *e = m->opt; memset (&princ, 0, sizeof(princ)); - ret = kadm5_get_principal(kadm_handle, principal, &princ, + ret = kadm5_get_principal(m->kadm_handle, principal, &princ, KADM5_PRINCIPAL | KADM5_ATTRIBUTES | KADM5_MAX_LIFE | KADM5_MAX_RLIFE | KADM5_PRINC_EXPIRE_TIME | @@ -382,12 +390,12 @@ do_mod_entry(krb5_principal principal, void *data) } else ret = edit_entry(&princ, &mask, NULL, 0); if(ret == 0) { - ret = kadm5_modify_principal(kadm_handle, &princ, mask); + ret = kadm5_modify_principal(m->kadm_handle, &princ, mask); if(ret) krb5_warn(context, ret, "kadm5_modify_principal"); } - kadm5_free_principal_ent(kadm_handle, &princ); + kadm5_free_principal_ent(m->kadm_handle, &princ); return ret; } @@ -395,13 +403,19 @@ int mod_entry(struct modify_options *opt, int argc, char **argv) { krb5_error_code ret = 0; + struct mod_data data; int i; - for(i = 0; i < argc; i++) { - ret = foreach_principal(argv[i], do_mod_entry, "mod", opt); - if (ret) - break; - } + data.kadm_handle = NULL; + data.opt_ns_kr = NULL; + data.opt_ns = NULL; + data.opt = opt; + + ret = kadm5_dup_context(kadm_handle, &data.kadm_handle); + for (i = 0; ret == 0 && i < argc; i++) + ret = foreach_principal(argv[i], do_mod_entry, "mod", &data); + if (data.kadm_handle) + kadm5_destroy(data.kadm_handle); return ret != 0; } @@ -411,10 +425,11 @@ do_mod_ns_entry(krb5_principal principal, void *data) krb5_error_code ret; kadm5_principal_ent_rec princ; int mask = 0; - struct modify_namespace_options *e = data; + struct mod_data *m = data; + struct modify_namespace_options *e = m->opt_ns; memset (&princ, 0, sizeof(princ)); - ret = kadm5_get_principal(kadm_handle, principal, &princ, + ret = kadm5_get_principal(m->kadm_handle, principal, &princ, KADM5_PRINCIPAL | KADM5_ATTRIBUTES | KADM5_MAX_LIFE | KADM5_MAX_RLIFE | KADM5_PRINC_EXPIRE_TIME | @@ -441,12 +456,12 @@ do_mod_ns_entry(krb5_principal principal, void *data) } else ret = edit_entry(&princ, &mask, NULL, 0); if(ret == 0) { - ret = kadm5_modify_principal(kadm_handle, &princ, mask); + ret = kadm5_modify_principal(m->kadm_handle, &princ, mask); if(ret) krb5_warn(context, ret, "kadm5_modify_principal"); } - kadm5_free_principal_ent(kadm_handle, &princ); + kadm5_free_principal_ent(m->kadm_handle, &princ); return ret; } @@ -454,13 +469,19 @@ int modify_namespace(struct modify_namespace_options *opt, int argc, char **argv) { krb5_error_code ret = 0; + struct mod_data data; int i; - for(i = 0; i < argc; i++) { - ret = foreach_principal(argv[i], do_mod_ns_entry, "mod_ns", opt); - if (ret) - break; - } + data.kadm_handle = NULL; + data.opt_ns_kr = NULL; + data.opt_ns = opt; + data.opt = NULL; + + ret = kadm5_dup_context(kadm_handle, &data.kadm_handle); + for (i = 0; ret == 0 && i < argc; i++) + ret = foreach_principal(argv[i], do_mod_ns_entry, "mod_ns", &data); + if (data.kadm_handle) + kadm5_destroy(data.kadm_handle); return ret != 0; } @@ -672,15 +693,20 @@ modify_ns_kr(struct modify_namespace_key_rotation_options *opt, char **argv) { krb5_error_code ret = 0; + struct mod_data data; int i; - for(i = 0; i < argc; i++) { + data.kadm_handle = NULL; + data.opt_ns_kr = opt; + data.opt_ns = NULL; + data.opt = NULL; + + ret = kadm5_dup_context(kadm_handle, &data.kadm_handle); + for (i = 0; ret == 0 && i < argc; i++) ret = foreach_principal(argv[i], do_mod_ns_kr, "mod_ns", opt); - if (ret) - break; - } + if (data.kadm_handle) + kadm5_destroy(data.kadm_handle); return ret != 0; - return 0; } #define princ_realm(P) ((P)->realm) diff --git a/third_party/heimdal/kadmin/rpc.c b/third_party/heimdal/kadmin/rpc.c index 1ae10f1af7c..5cae3d2c239 100644 --- a/third_party/heimdal/kadmin/rpc.c +++ b/third_party/heimdal/kadmin/rpc.c @@ -972,7 +972,7 @@ process_stream(krb5_context contextp, INSIST(gctx.ctx == NULL); gctx.inprogress = 1; - fallthrough; + HEIM_FALLTHROUGH; case RPG_CONTINUE_INIT: { gss_name_t src_name = GSS_C_NO_NAME; krb5_data in; diff --git a/third_party/heimdal/kadmin/server.c b/third_party/heimdal/kadmin/server.c index 52f20202e7f..281822a30fc 100644 --- a/third_party/heimdal/kadmin/server.c +++ b/third_party/heimdal/kadmin/server.c @@ -38,9 +38,148 @@ static kadm5_ret_t check_aliases(kadm5_server_context *, kadm5_principal_ent_rec *, kadm5_principal_ent_rec *); +/* + * All the iter_cb stuff is about online listing of principals via + * kadm5_iter_principals(). Search for "LIST" to see more commentary. + */ +struct iter_cb_data { + krb5_context context; + krb5_auth_context ac; + krb5_storage *rsp; + kadm5_ret_t ret; + size_t n; + size_t i; + int fd; + unsigned int initial:1; + unsigned int stop:1; +}; + +/* + * This function sends the current chunk of principal listing and checks if the + * client requested that the listing stop. + */ +static int +iter_cb_send_now(struct iter_cb_data *d) +{ + struct timeval tv; + krb5_data out; + + krb5_data_zero(&out); + + if (!d->stop) { + fd_set fds; + int nfds; + + /* + * The client can send us one message to interrupt the iteration. + * + * TODO: Maybe we should have the client send a message every N chunks + * so we can clock the listing and have a chance to receive any + * interrupt message from the client? + */ + FD_ZERO(&fds); + FD_SET(d->fd, &fds); + tv.tv_sec = 0; + tv.tv_usec = 0; + nfds = select(d->fd + 1, &fds, NULL, NULL, &tv); + if (nfds == -1) { + d->ret = errno; + } else if (nfds > 0) { + /* + * And it did. We'll throw this message away. It should be a NOP + * call, which we'd throw away anyways. If the client's stop + * message arrives after we're done anyways, well, it will be + * processed as a NOP and thrown away. + */ + d->stop = 1; + d->ret = krb5_read_priv_message(d->context, d->ac, &d->fd, &out); + krb5_data_free(&out); + if (d->ret == HEIM_ERR_EOF) + exit(0); + } + } + d->i = 0; + d->ret = krb5_storage_to_data(d->rsp, &out); + if (d->ret == 0) + d->ret = krb5_write_priv_message(d->context, d->ac, &d->fd, &out); + krb5_data_free(&out); + krb5_storage_free(d->rsp); + if ((d->rsp = krb5_storage_emem()) == NULL) + return krb5_enomem(d->context); + return d->ret; +} + +static int +iter_cb(void *cbdata, const char *p) +{ + struct iter_cb_data *d = cbdata; + krb5_error_code ret = 0; + size_t n = d->n; + + /* Convince the compiler that `-(int)d->n' is defined */ + if (n == 0 || n > INT_MAX) + return ERANGE; + if (d->rsp == NULL && (d->rsp = krb5_storage_emem()) == NULL) + return krb5_enomem(d->context); + if (d->i == 0) { + /* Every chunk starts with a result code */ + ret = krb5_store_int32(d->rsp, d->ret); + if (ret) + return ret; + if (d->ret) + return ret; + } + if (d->initial) { + /* + * We'll send up to `d->n' entries per-write. We send a negative + * number to indicate we accepted the client's proposal that we speak + * the online LIST protocol. + * + * Note that if we're here then we've already placed a result code in + * this reply (see above). + */ + d->initial = 0; + ret = krb5_store_int32(d->rsp, -(int)n); /* Princs per-chunk */ + if (ret == 0) + ret = iter_cb_send_now(d); + if (ret) + return ret; + /* + * Now that we've sent the acceptance reply, put a result code as the + * first thing in the next reply, which will have the first chunk of + * the listing. + */ + ret = krb5_store_int32(d->rsp, d->ret); + if (ret) + return ret; + if (d->ret) + return ret; + } + + if (p) { + ret = krb5_store_string(d->rsp, p); + d->i++; + } else { + /* + * We get called with `p == NULL' when the listing is done. This + * forces us to iter_cb_send_now(d) below, but also forces us to have a + * properly formed reply (i.e., that we have a result code as the first + * item), even if the chunk is otherwise empty (`d->i == 0'). + */ + d->i = n; + } + + if (ret == 0 && d->i == n) + ret = iter_cb_send_now(d); /* Chunk finished; send it */ + if (d->stop) + return EINTR; + return ret; +} + static kadm5_ret_t kadmind_dispatch(void *kadm_handlep, krb5_boolean initial, - krb5_data *in, krb5_data *out, int readonly) + krb5_data *in, krb5_auth_context ac, int fd, + krb5_data *out, int readonly) { kadm5_ret_t ret = 0; kadm5_ret_t ret_sp = 0; @@ -81,18 +220,48 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial, client, sizeof(client)); if (ret == 0) ret = krb5_ret_int32(sp, &cmd); - if (ret) + if (ret) { + ret_sp = krb5_store_int32(rsp, KADM5_FAILURE); goto fail; + } switch(cmd){ + case kadm_nop:{ + /* + * In the future we could use this for versioning. + * + * We used to respond to NOPs with KADM5_FAILURE. Now we respond with + * zero. In the future we could send back a protocol version number + * and use NOPs for protocol version negotiation. + * + * In the meantime, this gets called only if a client wants to + * interrupt a long-running LIST operation. + */ + op = "NOP"; + ret = krb5_ret_int32(sp, &tmp); + if (ret == 0 && tmp == 0) { + /* + * Reply not wanted. This would be a LIST interrupt request. + */ + krb5_storage_free(rsp); + krb5_storage_free(sp); + return 0; + } + ret_sp = krb5_store_int32(rsp, ret = 0); + break; + } case kadm_get:{ op = "GET"; ret = krb5_ret_principal(sp, &princ); - if(ret) + if (ret) { + ret_sp = krb5_store_int32(rsp, KADM5_UNK_PRINC); goto fail; + } ret = krb5_ret_int32(sp, &mask); - if (ret) + if (ret) { + ret_sp = krb5_store_int32(rsp, KADM5_FAILURE); goto fail; + } mask |= KADM5_PRINCIPAL; krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name)); @@ -100,8 +269,10 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial, /* If the caller doesn't have KADM5_PRIV_GET, we're done. */ ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_GET, princ); - if (ret) + if (ret) { + ret_sp = krb5_store_int32(rsp, ret); goto fail; + } /* Then check to see if it is ok to return keys */ if ((mask & KADM5_KEY_DATA) != 0) { @@ -129,6 +300,7 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial, * modes request other things too, so in all likelihood this * heuristic will not hurt any kadmin get uses. */ + ret_sp = krb5_store_int32(rsp, ret); goto fail; } } @@ -147,7 +319,7 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial, case kadm_delete:{ op = "DELETE"; if (readonly) { - ret = KADM5_READ_ONLY; + ret_sp = krb5_store_int32(rsp, ret = KADM5_READ_ONLY); goto fail; } ret = krb5_ret_principal(sp, &princ); @@ -171,19 +343,23 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial, case kadm_create:{ op = "CREATE"; if (readonly) { - ret = KADM5_READ_ONLY; + ret_sp = krb5_store_int32(rsp, ret = KADM5_READ_ONLY); goto fail; } ret = kadm5_ret_principal_ent(sp, &ent); - if(ret) + if(ret) { + ret_sp = krb5_store_int32(rsp, ret); goto fail; + } ret = krb5_ret_int32(sp, &mask); if(ret){ + ret_sp = krb5_store_int32(rsp, KADM5_FAILURE); kadm5_free_principal_ent(kadm_handlep, &ent); goto fail; } ret = krb5_ret_string(sp, &password); if(ret){ + ret_sp = krb5_store_int32(rsp, KADM5_FAILURE); kadm5_free_principal_ent(kadm_handlep, &ent); goto fail; } @@ -193,6 +369,7 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial, ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_ADD, ent.principal); if(ret){ + ret_sp = krb5_store_int32(rsp, ret); kadm5_free_principal_ent(kadm_handlep, &ent); goto fail; } @@ -203,6 +380,7 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial, */ ret = check_aliases(contextp, &ent, NULL); if (ret) { + ret_sp = krb5_store_int32(rsp, KADM5_BAD_PRINCIPAL); kadm5_free_principal_ent(kadm_handlep, &ent); goto fail; } @@ -216,14 +394,17 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial, case kadm_modify:{ op = "MODIFY"; if (readonly) { - ret = KADM5_READ_ONLY; + ret_sp = krb5_store_int32(rsp, ret = KADM5_READ_ONLY); goto fail; } ret = kadm5_ret_principal_ent(sp, &ent); - if(ret) + if(ret) { + ret_sp = krb5_store_int32(rsp, KADM5_FAILURE); goto fail; + } ret = krb5_ret_int32(sp, &mask); if(ret){ + ret_sp = krb5_store_int32(rsp, KADM5_FAILURE); kadm5_free_principal_ent(contextp, &ent); goto fail; } @@ -233,6 +414,7 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial, ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_MODIFY, ent.principal); if(ret){ + ret_sp = krb5_store_int32(rsp, ret); kadm5_free_principal_ent(contextp, &ent); goto fail; } @@ -245,12 +427,14 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial, */ ret = kadm5_get_principal(kadm_handlep, ent.principal, &ent_prev, mask); if (ret) { + ret_sp = krb5_store_int32(rsp, ret); kadm5_free_principal_ent(contextp, &ent); goto fail; } ret = check_aliases(contextp, &ent, &ent_prev); kadm5_free_principal_ent(contextp, &ent_prev); if (ret) { + ret_sp = krb5_store_int32(rsp, KADM5_BAD_PRINCIPAL); kadm5_free_principal_ent(contextp, &ent); goto fail; } @@ -263,23 +447,25 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial, case kadm_prune:{ op = "PRUNE"; if (readonly) { - ret = KADM5_READ_ONLY; + ret_sp = krb5_store_int32(rsp, ret = KADM5_READ_ONLY); goto fail; } ret = krb5_ret_principal(sp, &princ); - if (ret) - goto fail; - ret = krb5_ret_int32(sp, &kvno); + if (ret == 0) + ret = krb5_ret_int32(sp, &kvno); if (ret == HEIM_ERR_EOF) { kvno = 0; } else if (ret) { + ret_sp = krb5_store_int32(rsp, KADM5_FAILURE); goto fail; } krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name)); krb5_warnx(contextp->context, "%s: %s %s", client, op, name); ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_CPW, princ); - if (ret) + if (ret) { + ret_sp = krb5_store_int32(rsp, ret); goto fail; + } ret = kadm5_prune_principal(kadm_handlep, princ, kvno); ret_sp = krb5_store_int32(rsp, ret); @@ -288,15 +474,16 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial, case kadm_rename:{ op = "RENAME"; if (readonly) { - ret = KADM5_READ_ONLY; + ret_sp = krb5_store_int32(rsp, ret = KADM5_READ_ONLY); goto fail; } ret = krb5_ret_principal(sp, &princ); - if(ret) - goto fail; - ret = krb5_ret_principal(sp, &princ2); - if (ret) + if (ret == 0) + ret = krb5_ret_principal(sp, &princ2); + if (ret) { + ret_sp = krb5_store_int32(rsp, KADM5_FAILURE); goto fail; + } krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name)); krb5_unparse_name_fixed(contextp->context, princ2, @@ -321,11 +508,13 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial, princ); } } - if (ret) + if (ret) { + ret_sp = krb5_store_int32(rsp, ret); goto fail; + } ret = kadm5_rename_principal(kadm_handlep, princ, princ2); - ret_sp = krb5_store_int32(sp, ret); + ret_sp = krb5_store_int32(rsp, ret); break; } case kadm_chpass:{ @@ -333,7 +522,7 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial, op = "CHPASS"; if (readonly) { - ret = KADM5_READ_ONLY; + ret_sp = krb5_store_int32(rsp, ret = KADM5_READ_ONLY); goto fail; } ret = krb5_ret_principal(sp, &princ); @@ -348,8 +537,10 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial, if (ret == 0) krb5_warnx(contextp->context, "%s: %s %s", client, op, name); } - if (ret) + if (ret) { + ret_sp = krb5_store_int32(rsp, KADM5_FAILURE); goto fail; + } /* * Change password requests are subject to ACLs unless the principal is @@ -363,8 +554,10 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial, "kadmin", "allow_self_change_password", NULL); if (!(is_self_cpw && initial && allow_self_cpw)) { ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_CPW, princ); - if (ret) + if (ret) { + ret_sp = krb5_store_int32(rsp, ret); goto fail; + } } ret = kadm5_chpass_principal_3(kadm_handlep, princ, keepold, 0, NULL, @@ -379,7 +572,7 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial, op = "CHPASS_WITH_KEY"; if (readonly) { - ret = KADM5_READ_ONLY; + ret_sp = krb5_store_int32(rsp, ret = KADM5_READ_ONLY); goto fail; } ret = krb5_ret_principal(sp, &princ); @@ -390,18 +583,22 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial, if (ret == HEIM_ERR_EOF) ret = 0; } - if (ret) + if (ret) { + ret_sp = krb5_store_int32(rsp, KADM5_FAILURE); goto fail; + } /* n_key_data will be squeezed into an int16_t below. */ if (n_key_data < 0 || n_key_data >= 1 << 16 || (size_t)n_key_data > UINT_MAX/sizeof(*key_data)) { + ret_sp = krb5_store_int32(rsp, KADM5_FAILURE); ret = ERANGE; goto fail; } key_data = malloc (n_key_data * sizeof(*key_data)); if (key_data == NULL && n_key_data != 0) { + ret_sp = krb5_store_int32(rsp, KADM5_FAILURE); ret = krb5_enomem(contextp->context); goto fail; } @@ -413,6 +610,7 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial, kadm5_free_key_data (contextp, &dummy, key_data); free (key_data); + ret_sp = krb5_store_int32(rsp, KADM5_FAILURE); goto fail; } } @@ -432,6 +630,7 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial, kadm5_free_key_data (contextp, &dummy, key_data); free (key_data); + ret_sp = krb5_store_int32(rsp, KADM5_FAILURE); goto fail; } ret = kadm5_chpass_principal_with_key_3(kadm_handlep, princ, keepold, @@ -449,12 +648,14 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial, op = "RANDKEY"; if (readonly) { - ret = KADM5_READ_ONLY; + ret_sp = krb5_store_int32(rsp, ret = KADM5_READ_ONLY); goto fail; } ret = krb5_ret_principal(sp, &princ); - if (ret) + if (ret) { + ret_sp = krb5_store_int32(rsp, KADM5_FAILURE); goto fail; + } krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name)); krb5_warnx(contextp->context, "%s: %s %s", client, op, name); /* @@ -470,16 +671,20 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial, else ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_CPW, princ); - if (ret) + if (ret) { + ret_sp = krb5_store_int32(rsp, ret); goto fail; + } /* * See comments in kadm5_c_randkey_principal() regarding the * protocol. */ ret = krb5_ret_int32(sp, &keepold); - if (ret != 0 && ret != HEIM_ERR_EOF) + if (ret != 0 && ret != HEIM_ERR_EOF) { + ret_sp = krb5_store_int32(rsp, KADM5_FAILURE); goto fail; + } ret = krb5_ret_int32(sp, &n_ks_tuple); if (ret == HEIM_ERR_EOF) { @@ -497,15 +702,19 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial, &n, &ks_tuple); n_ks_tuple = n; } - if (ret != 0) + if (ret != 0) { + ret_sp = krb5_store_int32(rsp, KADM5_FAILURE); /* XXX */ goto fail; + } if (n_ks_tuple < 0) { + ret_sp = krb5_store_int32(rsp, KADM5_FAILURE); /* XXX */ ret = EOVERFLOW; goto fail; } free(ks_tuple); if ((ks_tuple = calloc(n_ks_tuple, sizeof (*ks_tuple))) == NULL) { + ret_sp = krb5_store_int32(rsp, KADM5_FAILURE); ret = errno; goto fail; } @@ -513,11 +722,13 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial, for (i = 0; i < n_ks_tuple; i++) { ret = krb5_ret_int32(sp, &ks_tuple[i].ks_enctype); if (ret != 0) { + ret_sp = krb5_store_int32(rsp, KADM5_FAILURE); free(ks_tuple); goto fail; } ret = krb5_ret_int32(sp, &ks_tuple[i].ks_salttype); if (ret != 0) { + ret_sp = krb5_store_int32(rsp, KADM5_FAILURE); free(ks_tuple); goto fail; } @@ -543,43 +754,119 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial, uint32_t privs; ret = kadm5_get_privs(kadm_handlep, &privs); if (ret == 0) - ret_sp = krb5_store_uint32(sp, privs); + ret_sp = krb5_store_uint32(rsp, privs); break; } case kadm_get_princs:{ op = "LIST"; ret = krb5_ret_int32(sp, &tmp); - if(ret) + if (ret) { + ret_sp = krb5_store_int32(rsp, KADM5_FAILURE); goto fail; - if(tmp){ + } + /* See kadm5_c_iter_principals() */ + if (tmp == 0x55555555) { + /* Want online iteration */ + ret = krb5_ret_string(sp, &expression); + if (ret) { + ret_sp = krb5_store_int32(rsp, KADM5_FAILURE); + goto fail; + } + if (expression[0] == '\0') { + free(expression); + expression = NULL; + } + } else if (tmp) { ret = krb5_ret_string(sp, &expression); - if(ret) + if (ret) { + ret_sp = krb5_store_int32(rsp, KADM5_FAILURE); goto fail; + } }else expression = NULL; krb5_warnx(contextp->context, "%s: %s %s", client, op, expression ? expression : "*"); ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_LIST, NULL); if(ret){ + ret_sp = krb5_store_int32(rsp, ret); free(expression); goto fail; } - ret = kadm5_get_principals(kadm_handlep, expression, &princs, &n_princs); - free(expression); - ret_sp = krb5_store_int32(rsp, ret); - if (ret == 0) { - int i; + if (fd > -1 && tmp == 0x55555555) { + struct iter_cb_data iter_cbdata; + int n; - ret_sp = krb5_store_int32(sp, n_princs); - for (i = 0; ret_sp == 0 && i < n_princs; i++) - ret_sp = krb5_store_string(sp, princs[i]); - kadm5_free_name_list(kadm_handlep, princs, &n_princs); - } + /* + * The client proposes that we speak the online variation of LIST + * by sending a magic value in the int32 that is meant to be a + * boolean for "an expression follows". The client must send an + * expression in this case because the server might be an old one, + * so even if the caller to kadm5_get/iter_principals() passed NULL + * for the expression, the client must send something ("*"). + * + * The list of principals will be streamed in multiple replies. + * + * The first reply will have just a return code and a negative + * count of maximum number of names per-subsequent reply. See + * `iter_cb()'. + * + * The second reply, third, .., nth replies will have a return code + * followed by 50 names, except the last reply must have fewer than + * 50 names -zero if need be- so the client can deterministically + * notice the end of the stream. + */ + + n = list_chunk_size; + if (n < 0) + n = krb5_config_get_int_default(contextp->context, NULL, -1, + "kadmin", "list_chunk_size", NULL); + if (n < 0) + n = 50; + if (n > 500) + n = 500; + if ((iter_cbdata.rsp = krb5_storage_emem()) == NULL) { + ret_sp = krb5_store_int32(rsp, KADM5_FAILURE); + ret = krb5_enomem(contextp->context); + goto fail; + } + iter_cbdata.context = contextp->context; + iter_cbdata.initial = 1; + iter_cbdata.stop = 0; + iter_cbdata.ret = 0; + iter_cbdata.ac = ac; + iter_cbdata.fd = fd; + iter_cbdata.n = n; + iter_cbdata.i = 0; + + /* + * All sending of replies will happen in iter_cb, except for the + * final chunk with the final result code. + */ + iter_cbdata.ret = kadm5_iter_principals(kadm_handlep, expression, + iter_cb, &iter_cbdata); + /* Send terminating chunk */ + iter_cb(&iter_cbdata, NULL); + /* Final result */ + ret = krb5_store_int32(rsp, iter_cbdata.ret); + krb5_storage_free(iter_cbdata.rsp); + } else { + ret = kadm5_get_principals(kadm_handlep, expression, &princs, &n_princs); + ret_sp = krb5_store_int32(rsp, ret); + if (ret == 0 && ret_sp == 0) { + int i; + + ret_sp = krb5_store_int32(rsp, n_princs); + for (i = 0; ret_sp == 0 && i < n_princs; i++) + ret_sp = krb5_store_string(rsp, princs[i]); + kadm5_free_name_list(kadm_handlep, princs, &n_princs); + } + } + free(expression); break; } default: krb5_warnx(contextp->context, "%s: UNKNOWN OP %d", client, cmd); - ret_sp = krb5_store_int32(sp, KADM5_FAILURE); + ret_sp = krb5_store_int32(rsp, KADM5_FAILURE); break; } @@ -744,11 +1031,13 @@ v5_loop (krb5_context contextp, if(ret) krb5_err(contextp, 1, ret, "krb5_read_priv_message"); doing_useful_work = 1; - ret = kadmind_dispatch(kadm_handlep, initial, &in, &out, readonly); + ret = kadmind_dispatch(kadm_handlep, initial, &in, ac, fd, &out, + readonly); if (ret) krb5_err(contextp, 1, ret, "kadmind_dispatch"); krb5_data_free(&in); - ret = krb5_write_priv_message(contextp, ac, &fd, &out); + if (out.length) + ret = krb5_write_priv_message(contextp, ac, &fd, &out); krb5_data_free(&out); if(ret) krb5_err(contextp, 1, ret, "krb5_write_priv_message"); diff --git a/third_party/heimdal/kadmin/util.c b/third_party/heimdal/kadmin/util.c index 720d9d3b759..fda1e982a9b 100644 --- a/third_party/heimdal/kadmin/util.c +++ b/third_party/heimdal/kadmin/util.c @@ -47,6 +47,7 @@ get_response(const char *prompt, const char *def, char *buf, size_t len); */ struct units kdb_attrs[] = { + { "no-auth-data-reqd", KRB5_KDB_NO_AUTH_DATA_REQUIRED }, { "disallow-client", KRB5_KDB_DISALLOW_CLIENT }, { "virtual", KRB5_KDB_VIRTUAL }, { "virtual-keys", KRB5_KDB_VIRTUAL_KEYS }, @@ -69,7 +70,6 @@ struct units kdb_attrs[] = { { "disallow-tgt-based", KRB5_KDB_DISALLOW_TGT_BASED }, { "disallow-forwardable", KRB5_KDB_DISALLOW_FORWARDABLE }, { "disallow-postdated", KRB5_KDB_DISALLOW_POSTDATED }, - { "no-auth-data-reqd", KRB5_KDB_NO_AUTH_DATA_REQUIRED }, { NULL, 0 } }; @@ -353,7 +353,14 @@ edit_timet (const char *prompt, krb5_timestamp *value, int *mask, int bit) void deltat2str(unsigned t, char *str, size_t len) { - if(t == 0 || t == INT_MAX) + /* + * A time delta in kadmin context is a positive number, and there's no + * point to it being possibly as large as 2^64 -1, so we use unsigned + * instead of a more generally appropriate type for time deltas (which + * conceptually can be negative, which in kadmin context there's no need + * for). + */ + if (t == 0 || t > INT_MAX) snprintf(str, len, "unlimited"); else unparse_time(t, str, len); @@ -370,6 +377,15 @@ str2deltat(const char *str, krb5_deltat *delta) int res; if(strcasecmp(str, "unlimited") == 0) { + /* + * Using zero to mean "unlimited" is unfortunate. We should use + * `UINT_MAX'. However, we've had this assumption that zero means + * unlimited, so there are HDB entries with present-but-zero max-life + * and max-renew-life. + * + * We could switch to using `UINT_MAX' or `UINT64_MAX' for "unlimited", + * but we'd have to continue to treat `0' as special for some time. + */ *delta = 0; return 0; } @@ -589,6 +605,32 @@ is_expression(const char *string) return 0; } +struct foreach_principal_data { + const char *funcname; + int (*func)(krb5_principal, void *); + void *data; +}; + +static int +foreach_principal_cb(void *data, const char *p) +{ + struct foreach_principal_data *d = data; + krb5_principal princ; + krb5_error_code ret; + + ret = krb5_parse_name(context, p, &princ); + if (ret) + return ret; + + ret = d->func(princ, d->data); + krb5_free_principal(context, princ); + if (ret) { + krb5_warn(context, ret, "%s %s", d->funcname, p); + krb5_clear_error_message(context); + } + return ret; +} + /* * Loop over all principals matching exp. If any of calls to `func' * failes, the first error is returned when all principals are @@ -600,52 +642,66 @@ foreach_principal(const char *exp_str, const char *funcname, void *data) { - char **princs = NULL; - int num_princs = 0; - int i; - krb5_error_code saved_ret = 0, ret = 0; - krb5_principal princ_ent; + struct foreach_principal_data d; + krb5_error_code ret; + krb5_principal p; int is_expr; + int go_slow = + secure_getenv("KADMIN_USE_GET_PRINCIPALS") != NULL && + *secure_getenv("KADMIN_USE_GET_PRINCIPALS") != '\0'; /* if this isn't an expression, there is no point in wading through the whole database looking for matches */ is_expr = is_expression(exp_str); - if(is_expr) - ret = kadm5_get_principals(kadm_handle, exp_str, &princs, &num_princs); - if(!is_expr || ret == KADM5_AUTH_LIST) { - /* we might be able to perform the requested opreration even - if we're not allowed to list principals */ - num_princs = 1; - princs = malloc(sizeof(*princs)); - if(princs == NULL) - return ENOMEM; - princs[0] = strdup(exp_str); - if(princs[0] == NULL){ - free(princs); - return ENOMEM; - } - } else if(ret) { - krb5_warn(context, ret, "kadm5_get_principals"); - return ret; - } - for(i = 0; i < num_princs; i++) { - ret = krb5_parse_name(context, princs[i], &princ_ent); - if(ret){ - krb5_warn(context, ret, "krb5_parse_name(%s)", princs[i]); - continue; - } - ret = (*func)(princ_ent, data); - if(ret) { - krb5_warn(context, ret, "%s %s", funcname, princs[i]); - krb5_clear_error_message(context); - if (saved_ret == 0) - saved_ret = ret; - } - krb5_free_principal(context, princ_ent); + + d.funcname = funcname; + d.func = func; + d.data = data; + + if (is_expr && !go_slow) { + ret = kadm5_iter_principals(kadm_handle, exp_str, + foreach_principal_cb, &d); + if (ret == 0) + return 0; + if (ret != KADM5_AUTH_LIST) { + krb5_warn(context, ret, "kadm5_iter_principals"); + return ret; + } + } else if (is_expr) { + char **princs = NULL; + int count = 0; + + /* + * This is just for testing, and maybe in case there are HDB backends + * that are not re-entrant (LDAP?). + */ + ret = kadm5_get_principals(kadm_handle, exp_str, &princs, &count); + if (ret == 0 && count > 0) { + int i; + + for (i = 0; ret == 0 && i < count; i++) + ret = foreach_principal_cb(&d, princs[i]); + kadm5_free_name_list(kadm_handle, princs, &count); + return ret; + } + if (ret != KADM5_AUTH_LIST) { + krb5_warn(context, ret, "kadm5_iter_principals"); + return ret; + } + } + /* we might be able to perform the requested opreration even + if we're not allowed to list principals */ + ret = krb5_parse_name(context, exp_str, &p); + if (ret) { + krb5_warn(context, ret, "krb5_parse_name(%s)", exp_str); + return ret; + } + ret = (*func)(p, data); + if (ret) { + krb5_warn(context, ret, "%s %s", funcname, exp_str); + krb5_clear_error_message(context); } - if (ret == 0 && saved_ret != 0) - ret = saved_ret; - kadm5_free_name_list(kadm_handle, princs, &num_princs); + krb5_free_principal(context, p); return ret; } diff --git a/third_party/heimdal/kcm/config.c b/third_party/heimdal/kcm/config.c index be4bce6af92..1a2b81ceb33 100644 --- a/third_party/heimdal/kcm/config.c +++ b/third_party/heimdal/kcm/config.c @@ -45,6 +45,7 @@ static char *max_request_str; /* `max_request' as a string */ int detach_from_console = -1; int daemon_child = -1; +int automatic_renewal = -1; static const char *system_cache_name = NULL; static const char *system_keytab = NULL; @@ -93,6 +94,10 @@ static struct getargs args[] = { "daemon-child", 0 , arg_integer, &daemon_child, "private argument, do not use", NULL }, + { + "automatic-renewal", 0 , arg_negative_flag, &automatic_renewal, + "disable automatic TGT renewal", NULL + }, { "help", 'h', arg_flag, &help_flag, NULL, NULL }, { "system-principal", 'k', arg_string, &system_principal, @@ -390,6 +395,13 @@ kcm_configure(int argc, char **argv) krb5_err(kcm_context, 1, ret, "initializing system ccache"); } + if(automatic_renewal == -1) + automatic_renewal = krb5_config_get_bool_default(kcm_context, NULL, + TRUE, + "kcm", + "automatic_renewal", + NULL); + if(detach_from_console == -1) detach_from_console = krb5_config_get_bool_default(kcm_context, NULL, FALSE, diff --git a/third_party/heimdal/kcm/events.c b/third_party/heimdal/kcm/events.c index cbbe58ac29d..8b78c10f07d 100644 --- a/third_party/heimdal/kcm/events.c +++ b/third_party/heimdal/kcm/events.c @@ -220,7 +220,7 @@ kcm_ccache_make_default_event(krb5_context context, event->fire_time = time(NULL); /* right away */ event->action = KCM_EVENT_ACQUIRE_CREDS; } else if (is_primary_credential_p(context, ccache, newcred)) { - if (newcred->flags.b.renewable) { + if (automatic_renewal && newcred->flags.b.renewable) { event->action = KCM_EVENT_RENEW_CREDS; ccache->flags |= KCM_FLAGS_RENEWABLE; } else { diff --git a/third_party/heimdal/kcm/kcm_locl.h b/third_party/heimdal/kcm/kcm_locl.h index c337384972d..dc0110448ac 100644 --- a/third_party/heimdal/kcm/kcm_locl.h +++ b/third_party/heimdal/kcm/kcm_locl.h @@ -169,6 +169,7 @@ extern sig_atomic_t exit_flag; extern int name_constraints; extern int detach_from_console; extern int daemon_child; +extern int automatic_renewal; extern int launchd_flag; extern int disallow_getting_krbtgt; diff --git a/third_party/heimdal/kdc/Makefile.am b/third_party/heimdal/kdc/Makefile.am index c7f57251f7c..48248d8248b 100644 --- a/third_party/heimdal/kdc/Makefile.am +++ b/third_party/heimdal/kdc/Makefile.am @@ -44,6 +44,7 @@ bx509d_LDADD = -ldl \ $(MICROHTTPD_LIBS) \ $(LIB_roken) \ $(LIB_heimbase) \ + $(LIB_hcrypto) \ $(top_builddir)/lib/sl/libsl.la \ $(top_builddir)/lib/asn1/libasn1.la \ $(top_builddir)/lib/krb5/libkrb5.la \ diff --git a/third_party/heimdal/kdc/bx509d.8 b/third_party/heimdal/kdc/bx509d.8 index 512d0545ed6..f94015568b7 100644 --- a/third_party/heimdal/kdc/bx509d.8 +++ b/third_party/heimdal/kdc/bx509d.8 @@ -40,7 +40,11 @@ .Op Fl Fl version .Op Fl H Ar HOSTNAME .Op Fl d | Fl Fl daemon -.Op Fl Fl daemon-child +.Op Fl Fl allow-GET +.Op Fl Fl no-allow-GET +.Op Fl Fl csrf-protection-type= Ns Ar CSRF-PROTECTION-TYPE +.Op Fl Fl csrf-header= Ns Ar HEADER-NAME +.Op Fl Fl csrf-key-file= Ns Ar FILE .Op Fl Fl reverse-proxied .Op Fl p Ar port number (default: 443) .Op Fl Fl cache-dir= Ns Ar DIRECTORY @@ -53,11 +57,24 @@ .Oc .Sh DESCRIPTION Serves RESTful (HTTPS) GETs of -.Ar /bx509 and -.Ar /bnegotiate , -end-points -performing corresponding kx509 and, possibly, PKINIT requests -to the KDCs of the requested realms (or just the given REALM). +.Ar /get-cert , +.Ar /get-tgt , +.Ar /get-tgts , +and +.Ar /get-negotiate-token , +end-points that implement various experimental Heimdal features: +.Bl -bullet -compact -offset indent +.It +.Li an online CA service over HTTPS, +.It +.Li a kinit-over-HTTPS service, and +.It +.Li a Negotiate token over HTTPS service. +.El +.Pp +As well, a +.Ar /health +service can be used for checking service status. .Pp Supported options: .Bl -tag -width Ds @@ -75,6 +92,64 @@ Print version. .Xc Expected audience(s) of bearer tokens (i.e., acceptor name). .It Xo +.Fl Fl allow-GET +.Xc +If given, then HTTP GET will be allowed for the various +end-points other than +.Ar /health . +Otherwise only HEAD and POST will be allowed. +By default GETs are allowed, but this will change soon. +.It Xo +.Fl Fl no-allow-GET +.Xc +If given then HTTP GETs will be rejected for the various +end-points other than +.Ar /health . +.It Xo +.Fl Fl csrf-protection-type= Ns Ar CSRF-PROTECTION-TYPE +.Xc +Possible values of +.Ar CSRF-PROTECTION-TYPE +are +.Bl -bullet -compact -offset indent +.It +.Li GET-with-header +.It +.Li GET-with-token +.It +.Li POST-with-header +.It +.Li POST-with-token +.El +This may be given multiple times. +The default is to require CSRF tokens for POST requests, and to +require neither a non-simple header nor a CSRF token for GET +requests. +.Pp +See +.Sx CROSS-SITE REQUEST FORGERY PROTECTION . +.It Xo +.Fl Fl csrf-header= Ns Ar HEADER-NAME +.Xc +If given, then all requests other than to the +.Ar /health +service must have the given request +.Ar HEADER-NAME +set (the value is irrelevant). +The +.Ar HEADER-NAME +must be a request header name such that a request having it makes +it not a +.Dq simple +request (see the Cross-Origin Resource Sharing specification). +Defaults to +.Ar X-CSRF . +.It Xo +.Fl Fl csrf-key-file= Ns Ar FILE +.Xc +If given, this file must contain a 16 byte binary key for keying +the HMAC used in CSRF token construction. +.It Xo .Fl d , .Fl Fl daemon .Xc @@ -82,7 +157,8 @@ Detach from TTY and run in the background. .It Xo .Fl Fl reverse-proxied .Xc -Serves HTTP instead of HTTPS, accepting only looped-back connections. +Serves HTTP instead of HTTPS, accepting only looped-back +connections. .It Xo .Fl p Ar port number (default: 443) .Xc @@ -90,29 +166,106 @@ PORT .It Xo .Fl Fl cache-dir= Ns Ar DIRECTORY .Xc -Directory for various caches. If not specified then a temporary directory will -be made. +Directory for various caches. +If not specified then a temporary directory will be made. .It Xo .Fl Fl cert= Ns Ar HX509-STORE .Xc -Certificate file path (PEM) for HTTPS service. May contain private key as -well. +Certificate file path (PEM) for HTTPS service. +May contain private key as well. .It Xo .Fl Fl private-key= Ns Ar HX509-STORE .Xc -Private key file path (PEM), if the private key is not stored along with the -certificiate. +Private key file path (PEM), if the private key is not stored +along with the certificiate. .It Xo .Fl t , .Fl Fl thread-per-client .Xc -Uses a thread per-client instead of as many threads as there are CPUs. +Uses a thread per-client instead of as many threads as there are +CPUs. .It Xo .Fl v , .Fl Fl verbose= Ns Ar run verbosely .Xc verbose .El +.Sh HTTP APIS +All HTTP APIs served by this program accept POSTs, with all +request parameters given as URI query parameters and/or as +form data in the POST request body, in either +.Ar application/x-www-form-urlencoded +or +.Ar multipart/formdata . +If request parameters are given both as URI query parameters +and as POST forms, then they are merged into a set. +.Pp +If GETs are enabled, then request parameters must be supplied +only as URI query parameters, as GET requests do not have request +bodies. +.Pp +URI query parameters must be of the form +.Ar param0=value¶m1=value... +.Pp +Some request parameters can only have one value. +If multiple values are given for such parameters, then either an +error will be produced, or only the first URI query parameter +value will be used, or the first POST form data parameter will be +used. +Other request parameters can have multiple values. +See below. +.Sh CROSS-SITE REQUEST FORGERY PROTECTION +.Em None +of the resources service by this service are intended to be +executed by web pages. +.Pp +All the resources provided by this service are +.Dq safe +in the sense that they do not change server-side state besides +logging, and in that they are idempotent, but they are +only safe to execute +.Em if and only if +the requesting party is trusted to see the response. +Since none of these resources are intended to be used from web +pages, it is important that web pages not be able to execute them +.Em and +observe the responses. +.Pp +In a web browser context, pages from other origins will be able +to attempt requests to this service, but should never be able to +see the responses because browsers normally wouldn't allow that. +Nonetheless, anti cross site request forgery (CSRF) protection +may be desirable. +.Pp +This service provides the following CSRF protection features: +.Bl -tag -width Ds -offset indent +.It requests are rejected if they have a +.Dq Referer +(except the experimental /get-negotiate-token end-point) +.It the service can be configured to require a header that would make the +request not Dq simple +.It GETs can be disabled (see options), thus requiring POSTs +.It GETs can be required to have a CSRF token (see below) +.It POSTs can be required to have a CSRF token +.El +.Pp +The experimental +.Ar /get-negotiate-token +end-point, however, always accepts +.Dq Referer +requests. +.Pp +To obtain a CSRF token, first execute the request without the +CSRF token, and the resulting error +response will include a +.Ar X-CSRF-Token +response header. +.Pp +To execute a request with a CSRF token, first obtain a CSRF token +as described above, then copy the token to the request as the +value of the request's +.Ar X-CSRF-Token +header. .Sh ONLINE CERTIFICATION AUTHORITY HTTP API This service provides an HTTP-based Certification Authority (CA). CA credentials and configuration are specified in the @@ -128,8 +281,8 @@ with the base-63 encoding of a DER encoding of a PKCS#10 .Ar CertificationRequest (Certificate Signing Request, or CSR) in a .Ar csr -required query parameter. -In a successful query, the response body will contain a PEM +required request parameter. +In a successful request, the response body will contain a PEM encoded end entity certificate and certification chain. .Pp Or @@ -146,9 +299,9 @@ Unauthorized requests will elicit a 403 response. .Pp Subject Alternative Names (SANs) and Extended Key Usage values may be requested, both in-band in the CSR as a requested -extensions attribute, and/or via optional query parameters. +extensions attribute, and/or via optional request parameters. .Pp -Supported query parameters (separated by ampersands) +Supported request parameters: .Bl -tag -width Ds -offset indent .It Li csr = Va base64-encoded-DER-encoded-CSR .It Li dNSName = Va hostname @@ -178,20 +331,20 @@ of .Ar /get-negotiate-token with a .Ar target = Ar service@host -query parameter. +request parameter. .Pp -In a successful query, the response body will contain a Negotiate -token for the authenticated client principal to the requested -target. +In a successful request, the response body will contain a +Negotiate token for the authenticated client principal to the +requested target. .Pp Authentication is required. Unauthenticated requests will elicit a 401 response. .Pp Subject Alternative Names (SANs) and Extended Key Usage values may be requested, both in-band in the CSR as a requested -extensions attribute, and/or via optional query parameters. +extensions attribute, and/or via optional request parameters. .Pp -Supported query parameters (separated by ampersands) +Supported request parameters: .Bl -tag -width Ds -offset indent .It Li target = Va service@hostname .It Li redirect = Va URI @@ -221,13 +374,14 @@ The protocol consists of a of .Ar /get-tgt . .Pp -Supported query parameters (separated by ampersands) +Supported request parameters: .Bl -tag -width Ds -offset indent .It Li cname = Va principal-name .It Li address = Va IP-address +.It Li lifetime = Va relative-time .El .Pp -In a successful query, the response body will contain a TGT and +In a successful request, the response body will contain a TGT and its session key encoded as a "ccache" file contents. .Pp Authentication is required. @@ -239,13 +393,14 @@ same as for by the authenticated client principal to get a certificate with a PKINIT SAN for itself or the requested principal if a .Va cname -query parameter was included. +request parameter was included. .Pp Unauthorized requests will elicit a 403 response. .Pp -Requested IP addresses will be added to the issued TGT if allowed. -The IP address of the client will be included if address-less TGTs -are not allowed. +Requested IP addresses will be added to the issued TGT if +allowed. +The IP address of the client will be included if address-less +TGTs are not allowed. See the .Va [get-tgt] section of @@ -257,6 +412,48 @@ end-point, but as configured in the .Va [get-tgt] section of .Xr krb5.conf 5 . +.Sh BATCH TGT HTTP API +Some sites may have special users that operate batch jobs systems +and that can impersonate many others by obtaining TGTs for them, +and which +.Dq prestash +credentials for those users in their credentials caches. +To support these sytems, a +.Ar GET +of +.Ar /get-tgts +with multiple +.Ar cname +request parameters will return those principals' TGTs (if the +caller is authorized). +.Pp +This is similar to the +.Ar /get-tgt +end-point, but a) multiple +.Ar cname +request parameter values may be given, and b) the caller's +principal name is not used as a default for the +.Ar cname +request parameter. +The +.Ar address +and +.Ar lifetime +request parameters are honored. +.Pp +For successful +.Ar GETs +the response body is a sequence of JSON texts each of which is a +JSON object with two keys: +.Bl -tag -width Ds -offset indent +.It Ar ccache +with a base64-encoded FILE-type ccache; +.It Ar name +the name of the principal whose credentials are in that ccache. +.El +.Sh NOTES +A future release may split all these end-points into separate +services. .Sh ENVIRONMENT .Bl -tag -width Ds .It Ev KRB5_CONFIG diff --git a/third_party/heimdal/kdc/bx509d.c b/third_party/heimdal/kdc/bx509d.c index 064c424b7c2..4d1b694a914 100644 --- a/third_party/heimdal/kdc/bx509d.c +++ b/third_party/heimdal/kdc/bx509d.c @@ -33,32 +33,19 @@ /* * This file implements a RESTful HTTPS API to an online CA, as well as an - * HTTP/Negotiate token issuer. + * HTTP/Negotiate token issuer, as well as a way to get TGTs. * - * Users are authenticated with bearer tokens. + * Users are authenticated with Negotiate and/or Bearer. * - * This is essentially a RESTful online CA sharing code with the KDC's kx509 - * online CA, and also a proxy for PKINIT and GSS-API (Negotiate). + * This is essentially a RESTful online CA sharing some code with the KDC's + * kx509 online CA, and also a proxy for PKINIT and GSS-API (Negotiate). * - * To get a key certified: - * - * GET /bx509?csr= - * - * To get an HTTP/Negotiate token: - * - * GET /bnegotiate?target= - * - * which, if authorized, produces a Negotiate token (base64-encoded, as - * expected, with the "Negotiate " prefix, ready to be put in an Authorization: - * header). + * See the manual page for HTTP API details. * * TBD: * - rewrite to not use libmicrohttpd but an alternative more appropriate to * Heimdal's license (though libmicrohttpd will do) - * - /bx509 should include the certificate chain - * - /bx509 should support HTTP/Negotiate * - there should be an end-point for fetching an issuer's chain - * - maybe add /bkrb5 which returns a KRB-CRED with the user's TGT * * NOTES: * - We use krb5_error_code values as much as possible. Where we need to use @@ -69,6 +56,49 @@ * (MHD_NO is an ENOMEM-cannot-even-make-a-static-503-response level event.) */ +/* + * Theory of operation: + * + * - We use libmicrohttpd (MHD) for the HTTP(S) implementation. + * + * - MHD has an online request processing model: + * + * - all requests are handled via the `dh' and `dh_cls' closure arguments + * of `MHD_start_daemon()'; ours is called `route()' + * + * - `dh' is called N+1 times: + * - once to allocate a request context + * - once for every N chunks of request body + * - once to process the request and produce a response + * + * - the response cannot begin to be produced before consuming the whole + * request body (for requests that have a body) + * (this seems like a bug in MHD) + * + * - the response body can be produced over multiple calls (i.e., in an + * online manner) + * + * - Our `route()' processes any POST request body form data / multipart by + * treating all the key/value pairs as if they had been additional URI query + * parameters. + * + * - Then `route()' calls a handler appropriate to the URI local-part with the + * request context, and the handler produces a response in one call. + * + * I.e., we turn the online MHD request processing into not-online. Our + * handlers are presented with complete requests and must produce complete + * responses in one call. + * + * - `route()' also does any authentication and CSRF protection so that the + * request handlers don't have to. + * + * This non-online request handling approach works for most everything we want + * to do. However, for /get-tgts with very large numbers of principals, we + * might have to revisit this, using MHD_create_response_from_callback() or + * MHD_create_response_from_pipe() (and a thread to do the actual work of + * producing the body) instead of MHD_create_response_from_buffer(). + */ + #define _XOPEN_SOURCE_EXTENDED 1 #define _DEFAULT_SOURCE 1 #define _BSD_SOURCE 1 @@ -128,20 +158,40 @@ typedef enum MHD_Result heim_mhd_result; enum k5_creds_kind { K5_CREDS_EPHEMERAL, K5_CREDS_CACHED }; +/* + * This is to keep track of memory we need to free, mainly because we had to + * duplicate data from the MHD POST form data processor. + */ +struct free_tend_list { + void *freeme1; + void *freeme2; + struct free_tend_list *next; +}; + +/* Per-request context data structure */ typedef struct bx509_request_desc { + /* Common elements for Heimdal request/response services */ HEIM_SVC_REQUEST_DESC_COMMON_ELEMENTS; struct MHD_Connection *connection; + struct MHD_PostProcessor *pp; + struct MHD_Response *response; krb5_times token_times; time_t req_life; hx509_request req; + struct free_tend_list *free_list; const char *for_cname; const char *target; const char *redir; + const char *method; + size_t post_data_size; enum k5_creds_kind cckind; char *pkix_store; + char *tgts_filename; + FILE *tgts; char *ccname; char *freeme1; + char *csrf_token; krb5_addresses tgt_addresses; /* For /get-tgt */ char frombuf[128]; } *bx509_request_desc; @@ -214,7 +264,17 @@ get_krb5_context(krb5_context *contextp) return *contextp ? 0 : ENOMEM; } +typedef enum { + CSRF_PROT_UNSPEC = 0, + CSRF_PROT_GET_WITH_HEADER = 1, + CSRF_PROT_GET_WITH_TOKEN = 2, + CSRF_PROT_POST_WITH_HEADER = 8, + CSRF_PROT_POST_WITH_TOKEN = 16, +} csrf_protection_type; + +static csrf_protection_type csrf_prot_type = CSRF_PROT_UNSPEC; static int port = -1; +static int allow_GET_flag = -1; static int help_flag; static int daemonize; static int daemon_child_fd = -1; @@ -223,11 +283,16 @@ static int version_flag; static int reverse_proxied_flag; static int thread_per_client_flag; struct getarg_strings audiences; +static getarg_strings csrf_prot_type_strs; +static const char *csrf_header = "X-CSRF"; static const char *cert_file; static const char *priv_key_file; static const char *cache_dir; +static const char *csrf_key_file; static char *impersonation_key_fn; +static char csrf_key[16]; + static krb5_error_code resp(struct bx509_request_desc *, int, enum MHD_ResponseMemoryMode, const char *, const void *, size_t, const char *); @@ -243,6 +308,7 @@ static krb5_error_code bad_404(struct bx509_request_desc *, const char *); static krb5_error_code bad_405(struct bx509_request_desc *, const char *); static krb5_error_code bad_500(struct bx509_request_desc *, krb5_error_code, const char *); static krb5_error_code bad_503(struct bx509_request_desc *, krb5_error_code, const char *); +static heim_mhd_result validate_csrf_token(struct bx509_request_desc *r); static int validate_token(struct bx509_request_desc *r) @@ -409,16 +475,20 @@ mk_pkix_store(char **pkix_store) int ret = ENOMEM; int fd; + if (*pkix_store) { + const char *fn = strchr(*pkix_store, ':'); + + fn = fn ? fn + 1 : *pkix_store; + (void) unlink(fn); + } + + free(*pkix_store); *pkix_store = NULL; if (asprintf(&s, "PEM-FILE:%s/pkix-XXXXXX", cache_dir) == -1 || s == NULL) { free(s); return ret; } - /* - * This way of using mkstemp() isn't safer than mktemp(), but we want to - * quiet the warning that we'd get if we used mktemp(). - */ if ((fd = mkstemp(s + sizeof("PEM-FILE:") - 1)) == -1) { free(s); return errno; @@ -428,11 +498,6 @@ mk_pkix_store(char **pkix_store) return 0; } -/* - * XXX Shouldn't be a body, but a status message. The body should be - * configurable to be from a file. MHD doesn't give us a way to set the - * response status message though, just the body. - */ static krb5_error_code resp(struct bx509_request_desc *r, int http_status_code, @@ -442,26 +507,31 @@ resp(struct bx509_request_desc *r, size_t bodylen, const char *token) { - struct MHD_Response *response; int mret = MHD_YES; + if (r->response) + return MHD_YES; + (void) gettimeofday(&r->tv_end, NULL); if (http_status_code == MHD_HTTP_OK || http_status_code == MHD_HTTP_TEMPORARY_REDIRECT) audit_trail(r, 0); - response = MHD_create_response_from_buffer(bodylen, rk_UNCONST(body), - rmmode); - if (response == NULL) + r->response = MHD_create_response_from_buffer(bodylen, rk_UNCONST(body), + rmmode); + if (r->response == NULL) return -1; - mret = MHD_add_response_header(response, MHD_HTTP_HEADER_CACHE_CONTROL, - "no-store, max-age=0"); + if (r->csrf_token) + mret = MHD_add_response_header(r->response, "X-CSRF-Token", r->csrf_token); + if (mret == MHD_YES) + mret = MHD_add_response_header(r->response, MHD_HTTP_HEADER_CACHE_CONTROL, + "no-store, max-age=0"); if (mret == MHD_YES && http_status_code == MHD_HTTP_UNAUTHORIZED) { - mret = MHD_add_response_header(response, + mret = MHD_add_response_header(r->response, MHD_HTTP_HEADER_WWW_AUTHENTICATE, "Bearer"); if (mret == MHD_YES) - mret = MHD_add_response_header(response, + mret = MHD_add_response_header(r->response, MHD_HTTP_HEADER_WWW_AUTHENTICATE, "Negotiate"); } else if (mret == MHD_YES && http_status_code == MHD_HTTP_TEMPORARY_REDIRECT) { @@ -470,21 +540,21 @@ resp(struct bx509_request_desc *r, /* XXX Move this */ redir = MHD_lookup_connection_value(r->connection, MHD_GET_ARGUMENT_KIND, "redirect"); - mret = MHD_add_response_header(response, MHD_HTTP_HEADER_LOCATION, + mret = MHD_add_response_header(r->response, MHD_HTTP_HEADER_LOCATION, redir); if (mret != MHD_NO && token) - mret = MHD_add_response_header(response, + mret = MHD_add_response_header(r->response, MHD_HTTP_HEADER_AUTHORIZATION, token); } if (mret == MHD_YES && content_type) { - mret = MHD_add_response_header(response, + mret = MHD_add_response_header(r->response, MHD_HTTP_HEADER_CONTENT_TYPE, content_type); } if (mret == MHD_YES) - mret = MHD_queue_response(r->connection, http_status_code, response); - MHD_destroy_response(response); + mret = MHD_queue_response(r->connection, http_status_code, r->response); + MHD_destroy_response(r->response); return mret == MHD_NO ? -1 : 0; } @@ -520,7 +590,7 @@ bad_reqv(struct bx509_request_desc *r, emsg = strerror(code); } - ret = vasprintf(&formatted, fmt, ap) == -1; + ret = vasprintf(&formatted, fmt, ap); if (code) { if (ret > -1 && formatted) ret = asprintf(&msg, "%s: %s (%d)", formatted, emsg, (int)code); @@ -601,6 +671,13 @@ bad_405(struct bx509_request_desc *r, const char *method) "Method not supported: %s", method); } +static krb5_error_code +bad_413(struct bx509_request_desc *r) +{ + return bad_req(r, E2BIG, MHD_HTTP_METHOD_NOT_ALLOWED, + "POST request body too large"); +} + static krb5_error_code bad_500(struct bx509_request_desc *r, krb5_error_code ret, @@ -871,20 +948,28 @@ addr_to_string(krb5_context context, snprintf(str, len, "", addr->sa_family); } +static void clean_req_desc(struct bx509_request_desc *); + static krb5_error_code set_req_desc(struct MHD_Connection *connection, + const char *method, const char *url, - struct bx509_request_desc *r) + struct bx509_request_desc **rp) { + struct bx509_request_desc *r; const union MHD_ConnectionInfo *ci; const char *token; krb5_error_code ret; - memset(r, 0, sizeof(*r)); + *rp = NULL; + if ((r = calloc(1, sizeof(*r))) == NULL) + return ENOMEM; (void) gettimeofday(&r->tv_start, NULL); ret = get_krb5_context(&r->context); r->connection = connection; + r->response = NULL; + r->pp = NULL; r->request.data = ""; r->request.length = sizeof(""); r->from = r->frombuf; @@ -893,12 +978,17 @@ set_req_desc(struct MHD_Connection *connection, r->hcontext = r->context ? r->context->hcontext : NULL; r->config = NULL; r->logf = logfac; + r->csrf_token = NULL; + r->free_list = NULL; + r->method = method; r->reqtype = url; r->target = r->redir = NULL; r->pkix_store = NULL; r->for_cname = NULL; r->freeme1 = NULL; r->reason = NULL; + r->tgts_filename = NULL; + r->tgts = NULL; r->ccname = NULL; r->reply = NULL; r->sname = NULL; @@ -934,6 +1024,10 @@ set_req_desc(struct MHD_Connection *connection, } + if (ret == 0) + *rp = r; + else + clean_req_desc(r); return ret; } @@ -942,6 +1036,13 @@ clean_req_desc(struct bx509_request_desc *r) { if (!r) return; + while (r->free_list) { + struct free_tend_list *ftl = r->free_list; + r->free_list = r->free_list->next; + free(ftl->freeme1); + free(ftl->freeme2); + free(ftl); + } if (r->pkix_store) { const char *fn = strchr(r->pkix_store, ':'); @@ -955,6 +1056,7 @@ clean_req_desc(struct bx509_request_desc *r) } krb5_free_addresses(r->context, &r->tgt_addresses); hx509_request_free(&r->req); + heim_release(r->attributes); heim_release(r->reason); heim_release(r->kv); if (r->ccname && r->cckind == K5_CREDS_EPHEMERAL) { @@ -964,11 +1066,22 @@ clean_req_desc(struct bx509_request_desc *r) fn += sizeof("FILE:") - 1; (void) unlink(fn); } + if (r->tgts) + (void) fclose(r->tgts); + if (r->tgts_filename) { + (void) unlink(r->tgts_filename); + free(r->tgts_filename); + } + /* No need to destroy r->response */ + if (r->pp) + MHD_destroy_post_processor(r->pp); + free(r->csrf_token); free(r->pkix_store); free(r->freeme1); free(r->ccname); free(r->cname); free(r->sname); + free(r); } /* Implements GETs of /bx509 */ @@ -984,9 +1097,6 @@ bx509(struct bx509_request_desc *r) if (csr == NULL) return bad_400(r, EINVAL, "CSR is missing"); - if ((ret = validate_token(r))) - return ret; /* validate_token() calls bad_req() */ - if (r->cname == NULL) return bad_403(r, EINVAL, "Could not extract principal name from token"); @@ -1424,9 +1534,8 @@ k5_get_creds(struct bx509_request_desc *r, enum k5_creds_kind kind) if ((ret = k5_do_CA(r))) return ret; /* k5_do_CA() calls bad_req() */ - if (ret == 0 && (ret = do_pkinit(r, kind))) - ret = bad_403(r, ret, - "Could not acquire Kerberos credentials using PKINIT"); + if (ret == 0) + ret = do_pkinit(r, kind); return ret; } @@ -1682,15 +1791,11 @@ bnegotiate(struct bx509_request_desc *r) char *nego_tok = NULL; ret = bnegotiate_get_target(r); - if (ret == 0) { - heim_audit_addkv((heim_svc_req_desc)r, KDC_AUDIT_VIS, "target", "%s", - r->target ? r->target : ""); - heim_audit_setkv_bool((heim_svc_req_desc)r, "redir", !!r->redir); - ret = validate_token(r); - } - /* bnegotiate_get_target() and validate_token() call bad_req() */ if (ret) - return ret; + return ret; /* bnegotiate_get_target() calls bad_req() */ + heim_audit_addkv((heim_svc_req_desc)r, KDC_AUDIT_VIS, "target", "%s", + r->target ? r->target : ""); + heim_audit_setkv_bool((heim_svc_req_desc)r, "redir", !!r->redir); /* * Make sure we have Kerberos credentials for cprinc. If we have them @@ -1702,7 +1807,8 @@ bnegotiate(struct bx509_request_desc *r) */ ret = k5_get_creds(r, K5_CREDS_CACHED); if (ret) - return ret; + return bad_403(r, ret, + "Could not acquire Kerberos credentials using PKINIT"); /* Acquire the Negotiate token and output it */ if (ret == 0 && r->ccname != NULL) @@ -1795,10 +1901,17 @@ get_tgt_param_cb(void *d, /* * Implements /get-tgt end-point. * - * Query parameters (mutually exclusive): + * Query parameters: * * - cname= (client principal name, if not the same as the authenticated - * name, then this will be impersonated if allowed) + * name, then this will be impersonated if allowed; may be + * given only once) + * + * - address= (IP address to add as a ticket address; may be given + * multiple times) + * + * - lifetime=