summaryrefslogtreecommitdiff
path: root/third_party
diff options
context:
space:
mode:
authorAndrew Bartlett <abartlet@samba.org>2022-10-31 14:33:09 +1300
committerAndrew Bartlett <abartlet@samba.org>2022-11-02 04:23:34 +0000
commitef28247f3bbbd7cf9daed7a4dba28855496ce38e (patch)
treef97d5b9b41ebbfb7e52269486b3c41d9a0fd9575 /third_party
parentab4c7bda8daccdb99adaf6ec7fddf8b5f84be09a (diff)
downloadsamba-ef28247f3bbbd7cf9daed7a4dba28855496ce38e.tar.gz
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 <abartlet@samba.org> Reviewed-by: Joseph Sutton <josephsutton@catalyst.net.nz>
Diffstat (limited to 'third_party')
-rw-r--r--third_party/heimdal/.github/workflows/osx.yml6
-rw-r--r--third_party/heimdal/.github/workflows/windows.yml2
-rw-r--r--third_party/heimdal/admin/Makefile.am1
-rw-r--r--third_party/heimdal/admin/add.c178
-rw-r--r--third_party/heimdal/admin/copy.c19
-rw-r--r--third_party/heimdal/admin/get.c38
-rw-r--r--third_party/heimdal/admin/ktutil-commands.in33
-rw-r--r--third_party/heimdal/admin/ktutil.172
-rw-r--r--third_party/heimdal/admin/list.c139
-rwxr-xr-xthird_party/heimdal/apply_heimdal.sh6
-rw-r--r--third_party/heimdal/configure.ac20
-rw-r--r--third_party/heimdal/doc/Makefile.am3
-rw-r--r--third_party/heimdal/doc/NTMakefile1
-rw-r--r--third_party/heimdal/doc/apps.texi201
-rw-r--r--third_party/heimdal/doc/copyright.texi2
-rw-r--r--third_party/heimdal/doc/heimdal.texi21
-rw-r--r--third_party/heimdal/doc/hx509.texi6
-rw-r--r--third_party/heimdal/doc/kerberos4.texi173
-rw-r--r--third_party/heimdal/doc/migration.texi12
-rw-r--r--third_party/heimdal/doc/misc.texi2
-rw-r--r--third_party/heimdal/doc/setup.texi164
-rw-r--r--third_party/heimdal/doc/whatis.texi6
-rw-r--r--third_party/heimdal/doc/win2k.texi15
-rw-r--r--third_party/heimdal/include/config.h.w324
-rw-r--r--third_party/heimdal/kadmin/NTMakefile28
-rw-r--r--third_party/heimdal/kadmin/check.c15
-rw-r--r--third_party/heimdal/kadmin/cpw.c44
-rw-r--r--third_party/heimdal/kadmin/del.c29
-rw-r--r--third_party/heimdal/kadmin/ext.c12
-rw-r--r--third_party/heimdal/kadmin/get.c31
-rw-r--r--third_party/heimdal/kadmin/kadmin-commands.in13
-rw-r--r--third_party/heimdal/kadmin/kadmin.148
-rw-r--r--third_party/heimdal/kadmin/kadmin_locl.h1
-rw-r--r--third_party/heimdal/kadmin/kadmind.c4
-rw-r--r--third_party/heimdal/kadmin/mod.c72
-rw-r--r--third_party/heimdal/kadmin/rpc.c2
-rw-r--r--third_party/heimdal/kadmin/server.c387
-rw-r--r--third_party/heimdal/kadmin/util.c140
-rw-r--r--third_party/heimdal/kcm/config.c12
-rw-r--r--third_party/heimdal/kcm/events.c2
-rw-r--r--third_party/heimdal/kcm/kcm_locl.h1
-rw-r--r--third_party/heimdal/kdc/Makefile.am1
-rw-r--r--third_party/heimdal/kdc/bx509d.8257
-rw-r--r--third_party/heimdal/kdc/bx509d.c1031
-rw-r--r--third_party/heimdal/kdc/digest-service.c2
-rw-r--r--third_party/heimdal/kdc/digest.c2
-rw-r--r--third_party/heimdal/kdc/httpkadmind.8243
-rw-r--r--third_party/heimdal/kdc/httpkadmind.c607
-rw-r--r--third_party/heimdal/kdc/process.c27
-rw-r--r--third_party/heimdal/kdc/simple_csr_authorizer.c8
-rw-r--r--third_party/heimdal/kuser/Makefile.am1
-rw-r--r--third_party/heimdal/kuser/kinit.c10
-rw-r--r--third_party/heimdal/kuser/klist.c464
-rw-r--r--third_party/heimdal/lib/asn1/asn1_compile.15
-rw-r--r--third_party/heimdal/lib/asn1/gen_copy.c2
-rw-r--r--third_party/heimdal/lib/asn1/gen_free.c2
-rw-r--r--third_party/heimdal/lib/asn1/main.c8
-rw-r--r--third_party/heimdal/lib/asn1/template.c4
-rw-r--r--third_party/heimdal/lib/base/heimbase.h30
-rw-r--r--third_party/heimdal/lib/base/heimbasepriv.h23
-rw-r--r--third_party/heimdal/lib/base/json.c864
-rw-r--r--third_party/heimdal/lib/base/log.c3
-rw-r--r--third_party/heimdal/lib/base/string.c5
-rw-r--r--third_party/heimdal/lib/base/test_base.c340
-rw-r--r--third_party/heimdal/lib/base/version-script.map2
-rw-r--r--third_party/heimdal/lib/gssapi/Makefile.am12
-rw-r--r--third_party/heimdal/lib/gssapi/gss-token.c6
-rw-r--r--third_party/heimdal/lib/gssapi/krb5/8003.c2
-rw-r--r--third_party/heimdal/lib/gssapi/krb5/init_sec_context.c2
-rw-r--r--third_party/heimdal/lib/gssapi/netlogon/crypto.c2
-rw-r--r--third_party/heimdal/lib/gssapi/ntlm/crypto.c2
-rw-r--r--third_party/heimdal/lib/hcrypto/des.c2
-rw-r--r--third_party/heimdal/lib/hcrypto/dh.c2
-rw-r--r--third_party/heimdal/lib/hcrypto/dsa.c2
-rw-r--r--third_party/heimdal/lib/hcrypto/engine.c2
-rw-r--r--third_party/heimdal/lib/hcrypto/evp-openssl.c4
-rw-r--r--third_party/heimdal/lib/hcrypto/evp.c10
-rw-r--r--third_party/heimdal/lib/hcrypto/hmac.c6
-rw-r--r--third_party/heimdal/lib/hcrypto/md2.c2
-rw-r--r--third_party/heimdal/lib/hcrypto/passwd_dlg.c4
-rw-r--r--third_party/heimdal/lib/hcrypto/rand-fortuna.c2
-rw-r--r--third_party/heimdal/lib/hcrypto/rc2.c2
-rw-r--r--third_party/heimdal/lib/hcrypto/rsa.c4
-rw-r--r--third_party/heimdal/lib/hdb/Makefile.am4
-rw-r--r--third_party/heimdal/lib/hdb/common.c195
-rw-r--r--third_party/heimdal/lib/hdb/hdb-mdb.c2
-rw-r--r--third_party/heimdal/lib/hdb/hdb.asn12
-rw-r--r--third_party/heimdal/lib/hdb/hdb.opt4
-rw-r--r--third_party/heimdal/lib/hdb/keytab.c5
-rw-r--r--third_party/heimdal/lib/hx509/cert.c2
-rw-r--r--third_party/heimdal/lib/hx509/cms.c2
-rw-r--r--third_party/heimdal/lib/hx509/file.c2
-rw-r--r--third_party/heimdal/lib/hx509/hxtool.1207
-rw-r--r--third_party/heimdal/lib/hx509/hxtool.c68
-rw-r--r--third_party/heimdal/lib/hx509/req.c8
-rw-r--r--third_party/heimdal/lib/ipc/server.c46
-rw-r--r--third_party/heimdal/lib/kadm5/ad.c38
-rw-r--r--third_party/heimdal/lib/kadm5/common_glue.c15
-rw-r--r--third_party/heimdal/lib/kadm5/context_s.c10
-rw-r--r--third_party/heimdal/lib/kadm5/create_s.c8
-rw-r--r--third_party/heimdal/lib/kadm5/destroy_s.c12
-rw-r--r--third_party/heimdal/lib/kadm5/get_c.c2
-rw-r--r--third_party/heimdal/lib/kadm5/get_princs_c.c186
-rw-r--r--third_party/heimdal/lib/kadm5/get_princs_s.c124
-rw-r--r--third_party/heimdal/lib/kadm5/init_c.c52
-rw-r--r--third_party/heimdal/lib/kadm5/init_s.c15
-rw-r--r--third_party/heimdal/lib/kadm5/iprop.846
-rw-r--r--third_party/heimdal/lib/kadm5/ipropd_slave.c46
-rw-r--r--third_party/heimdal/lib/kadm5/libkadm5srv-exports.def2
-rw-r--r--third_party/heimdal/lib/kadm5/private.h2
-rw-r--r--third_party/heimdal/lib/kadm5/version-script-client.map5
-rw-r--r--third_party/heimdal/lib/kadm5/version-script.map2
-rw-r--r--third_party/heimdal/lib/kafs/kafs_locl.h1
-rw-r--r--third_party/heimdal/lib/kafs/rxkad_kdf.c2
-rw-r--r--third_party/heimdal/lib/krb5/Makefile.am1
-rw-r--r--third_party/heimdal/lib/krb5/NTMakefile12
-rw-r--r--third_party/heimdal/lib/krb5/aname_to_localname.c1
-rw-r--r--third_party/heimdal/lib/krb5/changepw.c2
-rw-r--r--third_party/heimdal/lib/krb5/context.c115
-rw-r--r--third_party/heimdal/lib/krb5/convert_creds.c3
-rw-r--r--third_party/heimdal/lib/krb5/kcm.c3
-rw-r--r--third_party/heimdal/lib/krb5/keytab.c3
-rw-r--r--third_party/heimdal/lib/krb5/krb5-v4compat.h139
-rw-r--r--third_party/heimdal/lib/krb5/pac.c5
-rw-r--r--third_party/heimdal/lib/krb5/pkinit.c12
-rw-r--r--third_party/heimdal/lib/krb5/send_to_kdc.c4
-rw-r--r--third_party/heimdal/lib/krb5/store.c2
-rw-r--r--third_party/heimdal/lib/libedit/config.h.in100
-rw-r--r--third_party/heimdal/lib/ntlm/digest.c2
-rw-r--r--third_party/heimdal/lib/ntlm/ntlm.c8
-rw-r--r--third_party/heimdal/lib/otp/otp_verify.c2
-rw-r--r--third_party/heimdal/lib/roken/Makefile.am2
-rw-r--r--third_party/heimdal/lib/roken/base32.c8
-rw-r--r--third_party/heimdal/lib/roken/dirent-test.c4
-rw-r--r--third_party/heimdal/lib/roken/fnmatch.c2
-rw-r--r--third_party/heimdal/lib/roken/getaddrinfo.c4
-rw-r--r--third_party/heimdal/lib/roken/getuserinfo.c2
-rw-r--r--third_party/heimdal/lib/roken/parse_units.c3
-rw-r--r--third_party/heimdal/lib/roken/parse_units.h4
-rw-r--r--third_party/heimdal/lib/roken/snprintf.c2
-rw-r--r--third_party/heimdal/lib/roken/strftime.c2
-rw-r--r--third_party/heimdal/lib/roken/strptime.c2
-rw-r--r--third_party/heimdal/lib/sl/slc-gram.y2
-rw-r--r--third_party/heimdal/lib/wind/utf8.c14
-rw-r--r--third_party/heimdal/packages/windows/sdk/NTMakefile2
-rw-r--r--third_party/heimdal/tests/gss/krb5.conf.in1
-rw-r--r--third_party/heimdal/tests/kdc/check-bx509.in283
-rw-r--r--third_party/heimdal/tests/kdc/check-httpkadmind.in177
-rw-r--r--third_party/heimdal/tests/kdc/check-kadmin.in236
-rw-r--r--third_party/heimdal/tests/kdc/check-kdc.in6
-rw-r--r--third_party/heimdal/tests/kdc/check-referral.in94
-rw-r--r--third_party/heimdal/tests/kdc/krb5-httpkadmind.conf.in6
-rw-r--r--third_party/heimdal/tests/kdc/krb5.conf.in3
-rw-r--r--third_party/heimdal/windows/README.md61
154 files changed, 6672 insertions, 1751 deletions
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 <heimbase.h>
+#include <base64.h>
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"
@@ -167,6 +173,16 @@ command = {
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"
type = "strings"
@@ -215,6 +231,14 @@ command = {
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 = {
long = "keys"
@@ -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 <heimbase.h>
#include <rtbl.h>
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&param1=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=<base64-encoded-PKCS#10-CSR>
- *
- * To get an HTTP/Negotiate token:
- *
- * GET /bnegotiate?target=<acceptor-principal>
- *
- * 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);
@@ -602,6 +672,13 @@ bad_405(struct bx509_request_desc *r, const char *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,
const char *reason)
@@ -871,20 +948,28 @@ addr_to_string(krb5_context context,
snprintf(str, len, "<family=%d>", 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 = "<HTTP-REQUEST>";
r->request.length = sizeof("<HTTP-REQUEST>");
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 : "<unknown>");
- 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 : "<unknown>");
+ 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=<name> (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> (IP address to add as a ticket address; may be given
+ * multiple times)
+ *
+ * - lifetime=<time> (requested lifetime for the ticket; may be given only
+ * once)
*/
static krb5_error_code
get_tgt(struct bx509_request_desc *r)
@@ -1812,12 +1925,9 @@ get_tgt(struct bx509_request_desc *r)
MHD_GET_ARGUMENT_KIND, "cname");
if (r->for_cname && r->for_cname[0] == '\0')
r->for_cname = NULL;
- ret = validate_token(r);
- if (ret == 0)
- ret = authorize_TGT_REQ(r);
- /* validate_token() and authorize_TGT_REQ() call bad_req() */
+ ret = authorize_TGT_REQ(r);
if (ret)
- return ret;
+ return ret; /* authorize_TGT_REQ() calls bad_req() */
r->error_code = 0;
(void) MHD_get_connection_values(r->connection, MHD_GET_ARGUMENT_KIND,
@@ -1828,7 +1938,8 @@ get_tgt(struct bx509_request_desc *r)
if (ret == 0)
ret = k5_get_creds(r, K5_CREDS_EPHEMERAL);
if (ret)
- return ret;
+ return bad_403(r, ret,
+ "Could not acquire Kerberos credentials using PKINIT");
fn = strchr(r->ccname, ':');
if (fn == NULL)
@@ -1843,6 +1954,262 @@ get_tgt(struct bx509_request_desc *r)
return ret;
}
+static int
+get_tgts_accumulate_ccache_write_json(struct bx509_request_desc *r,
+ krb5_error_code code,
+ const char *data,
+ size_t datalen)
+{
+ heim_object_t k, v;
+ heim_string_t text;
+ heim_error_t e = NULL;
+ heim_dict_t o;
+ int ret;
+
+ o = heim_dict_create(9);
+ k = heim_string_create("name");
+ v = heim_string_create(r->for_cname);
+ if (o && k && v)
+ ret = heim_dict_set_value(o, k, v);
+ else
+ ret = errno;
+
+ if (ret == 0) {
+ heim_release(v);
+ heim_release(k);
+ k = heim_string_create("error_code");
+ v = heim_number_create(code);
+ if (k && v)
+ ret = heim_dict_set_value(o, k, v);
+ }
+ if (ret == 0 && data != NULL) {
+ heim_release(v);
+ heim_release(k);
+ k = heim_string_create("ccache");
+ v = heim_data_create(data, datalen);
+ if (k && v)
+ ret = heim_dict_set_value(o, k, v);
+ }
+ if (ret == 0 && code != 0) {
+ heim_release(v);
+ heim_release(k);
+ k = heim_string_create("error");
+ v = heim_string_create(krb5_get_error_message(r->context, code));
+ if (k && v)
+ ret = heim_dict_set_value(o, k, v);
+ }
+ heim_release(v);
+ heim_release(k);
+ if (ret) {
+ heim_release(o);
+ return bad_503(r, errno, "Out of memory");
+ }
+
+ text = heim_json_copy_serialize(o,
+ HEIM_JSON_F_NO_DATA_DICT |
+ HEIM_JSON_F_ONE_LINE,
+ &e);
+ if (text) {
+ const char *s = heim_string_get_utf8(text);
+
+ (void) fwrite(s, strlen(s), 1, r->tgts);
+ } else {
+ const char *s = NULL;
+ v = heim_error_copy_string(e);
+ if (v)
+ s = heim_string_get_utf8(v);
+ if (s == NULL)
+ s = "<unknown encoder error>";
+ krb5_log_msg(r->context, logfac, 1, NULL, "Failed to encode JSON text with ccache or error for %s: %s",
+ r->for_cname, s);
+ heim_release(v);
+ }
+ heim_release(text);
+ heim_release(o);
+ return MHD_YES;
+}
+
+/* Writes one ccache to a response file, as JSON */
+static int
+get_tgts_accumulate_ccache(struct bx509_request_desc *r, krb5_error_code ret)
+{
+ const char *fn;
+ size_t bodylen = 0;
+ void *body = NULL;
+ int res;
+
+ if (r->tgts == NULL) {
+ int fd = -1;
+
+ if (asprintf(&r->tgts_filename,
+ "%s/tgts-json-XXXXXX", cache_dir) == -1 ||
+ r->tgts_filename == NULL) {
+ free(r->tgts_filename);
+ r->tgts_filename = NULL;
+
+ return bad_enomem(r, r->error_code = ENOMEM);
+ }
+ if ((fd = mkstemp(r->tgts_filename)) == -1)
+ return bad_req(r, errno, MHD_HTTP_SERVICE_UNAVAILABLE,
+ "%s", strerror(r->error_code = errno));
+ if ((r->tgts = fdopen(fd, "w+")) == NULL) {
+ (void) close(fd);
+ return bad_req(r, errno, MHD_HTTP_SERVICE_UNAVAILABLE,
+ "%s", strerror(r->error_code = errno));
+ }
+ }
+
+ if (ret == 0) {
+ fn = strchr(r->ccname, ':');
+ if (fn == NULL)
+ return bad_req(r, errno, MHD_HTTP_SERVICE_UNAVAILABLE,
+ "Internal error (invalid credentials cache name)");
+ fn++;
+ if ((r->error_code = rk_undumpdata(fn, &body, &bodylen)))
+ return bad_req(r, errno, MHD_HTTP_SERVICE_UNAVAILABLE,
+ "%s", strerror(r->error_code));
+ (void) unlink(fn);
+ free(r->ccname);
+ r->ccname = NULL;
+ if (bodylen > INT_MAX >> 4) {
+ free(body);
+ return bad_req(r, errno, MHD_HTTP_SERVICE_UNAVAILABLE,
+ "Credentials cache too large!");
+ }
+ }
+
+ res = get_tgts_accumulate_ccache_write_json(r, ret, body, bodylen);
+ free(body);
+ return res;
+}
+
+static heim_mhd_result
+get_tgts_param_authorize_cb(void *d,
+ enum MHD_ValueKind kind,
+ const char *key,
+ const char *val)
+{
+ struct bx509_request_desc *r = d;
+ krb5_error_code ret = 0;
+
+ if (strcmp(key, "cname") != 0 || val == NULL)
+ return MHD_YES;
+
+ if (r->req == NULL) {
+ ret = hx509_request_init(r->context->hx509ctx, &r->req);
+ if (ret == 0)
+ ret = hx509_request_add_eku(r->context->hx509ctx, r->req,
+ ASN1_OID_ID_PKEKUOID);
+ if (ret)
+ return bad_500(r, ret, "Out of resources");
+ }
+ heim_audit_addkv((heim_svc_req_desc)r, KDC_AUDIT_VIS,
+ "requested_krb5PrincipalName", "%s", val);
+ ret = hx509_request_add_pkinit(r->context->hx509ctx, r->req,
+ val);
+ if (ret)
+ return bad_403(r, ret, "Not authorized to requested TGT");
+ return MHD_YES;
+}
+
+/* For each requested principal, produce a ccache */
+static heim_mhd_result
+get_tgts_param_execute_cb(void *d,
+ enum MHD_ValueKind kind,
+ const char *key,
+ const char *val)
+{
+ struct bx509_request_desc *r = d;
+ heim_mhd_result res = MHD_YES;
+ krb5_error_code ret;
+
+ if (strcmp(key, "cname") == 0 && val) {
+ /* Handled upstairs */
+ r->for_cname = val;
+ ret = k5_get_creds(r, K5_CREDS_EPHEMERAL);
+ res = get_tgts_accumulate_ccache(r, ret);
+ } else {
+ /* Handled upstairs */
+ }
+ return res;
+}
+
+/*
+ * Implements /get-tgts end-point.
+ *
+ * Query parameters:
+ *
+ * - cname=<name> (client principal name, if not the same as the authenticated
+ * name, then this will be impersonated if allowed; may be
+ * given multiple times)
+ */
+static krb5_error_code
+get_tgts(struct bx509_request_desc *r)
+{
+ krb5_error_code ret;
+ krb5_principal p = NULL;
+ size_t bodylen;
+ void *body;
+ int res = MHD_YES;
+
+ /* Prep to authorize */
+ ret = krb5_parse_name(r->context, r->cname, &p);
+ if (ret)
+ return bad_403(r, ret, "Could not parse caller principal name");
+ if (ret == 0) {
+ /* Extract q-params other than `cname' */
+ r->error_code = 0;
+ res = MHD_get_connection_values(r->connection, MHD_GET_ARGUMENT_KIND,
+ get_tgt_param_cb, r);
+ if (r->response || res == MHD_NO)
+ return res;
+
+ ret = r->error_code;
+ }
+ if (ret == 0) {
+ /* Authorize requested client principal names (calls bad_req()) */
+ r->error_code = 0;
+ res = MHD_get_connection_values(r->connection, MHD_GET_ARGUMENT_KIND,
+ get_tgts_param_authorize_cb, r);
+ if (r->response || res == MHD_NO)
+ return res;
+
+ ret = r->error_code;
+ if (ret == 0) {
+ ret = kdc_authorize_csr(r->context, "get-tgt", r->req, p);
+ if (ret) {
+ krb5_free_principal(r->context, p);
+ return bad_403(r, ret, "Permission denied");
+ }
+ }
+ hx509_request_free(&r->req);
+ }
+ if (ret == 0) {
+ /* get_tgts_param_execute_cb() calls bad_req() */
+ r->error_code = 0;
+ res = MHD_get_connection_values(r->connection, MHD_GET_ARGUMENT_KIND,
+ get_tgts_param_execute_cb, r);
+ if (r->response || res == MHD_NO)
+ return res;
+ ret = r->error_code;
+ }
+ krb5_free_principal(r->context, p);
+
+ /*
+ * get_tgts_param_execute_cb() will write its JSON response to the file
+ * named by r->ccname.
+ */
+ if (fflush(r->tgts) != 0)
+ return bad_503(r, ret, "Could not get TGT");
+ if ((errno = rk_undumpdata(r->tgts_filename, &body, &bodylen)))
+ return bad_503(r, ret, "Could not get TGT");
+
+ ret = resp(r, MHD_HTTP_OK, MHD_RESPMEM_MUST_COPY,
+ "application/x-krb5-ccaches-json", body, bodylen, NULL);
+ free(body);
+ return ret;
+}
+
static krb5_error_code
health(const char *method, struct bx509_request_desc *r)
{
@@ -1856,7 +2223,250 @@ health(const char *method, struct bx509_request_desc *r)
}
-/* Implements the entirety of this REST service */
+static krb5_error_code
+mac_csrf_token(struct bx509_request_desc *r, krb5_storage *sp)
+{
+ krb5_error_code ret;
+ krb5_data data;
+ char mac[EVP_MAX_MD_SIZE];
+ unsigned int maclen = sizeof(mac);
+ HMAC_CTX *ctx = NULL;
+
+ ret = krb5_storage_to_data(sp, &data);
+ if (ret == 0 && (ctx = HMAC_CTX_new()) == NULL)
+ ret = krb5_enomem(r->context);
+ /* HMAC the token body and the client principal name */
+ if (ret == 0) {
+ if (HMAC_Init_ex(ctx, csrf_key, sizeof(csrf_key),
+ EVP_sha256(),
+ NULL) == 0) {
+ HMAC_CTX_cleanup(ctx);
+ ret = krb5_enomem(r->context);
+ } else {
+ HMAC_Update(ctx, data.data, data.length);
+ if (r->cname)
+ HMAC_Update(ctx, r->cname, strlen(r->cname));
+ HMAC_Final(ctx, mac, &maclen);
+ HMAC_CTX_cleanup(ctx);
+ krb5_data_free(&data);
+ data.length = maclen;
+ data.data = mac;
+ if (krb5_storage_write(sp, mac, maclen) != maclen)
+ ret = krb5_enomem(r->context);
+ }
+ }
+ if (ctx)
+ HMAC_CTX_free(ctx);
+ return ret;
+}
+
+/*
+ * Make a CSRF token. If one is also given, make one with the same body
+ * content so we can check the HMAC.
+ *
+ * Outputs the token and its age. Do not use either if the token does not
+ * equal the given token.
+ */
+static krb5_error_code
+make_csrf_token(struct bx509_request_desc *r,
+ const char *given,
+ char **token,
+ int64_t *age)
+{
+ krb5_error_code ret = 0;
+ unsigned char given_decoded[128];
+ krb5_storage *sp = NULL;
+ krb5_data data;
+ ssize_t dlen = -1;
+ uint64_t nonce;
+ int64_t t = 0;
+
+
+ *age = 0;
+ data.data = NULL;
+ data.length = 0;
+ if (given) {
+ size_t len = strlen(given);
+
+ /* Extract issue time and nonce from token */
+ if (len >= sizeof(given_decoded))
+ ret = ERANGE;
+ if (ret == 0 && (dlen = rk_base64_decode(given, &given_decoded)) <= 0)
+ ret = errno;
+ if (ret == 0 &&
+ (sp = krb5_storage_from_mem(given_decoded, dlen)) == NULL)
+ ret = krb5_enomem(r->context);
+ if (ret == 0)
+ ret = krb5_ret_int64(sp, &t);
+ if (ret == 0)
+ ret = krb5_ret_uint64(sp, &nonce);
+ krb5_storage_free(sp);
+ sp = NULL;
+ if (ret == 0)
+ *age = time(NULL) - t;
+ } else {
+ t = time(NULL);
+ krb5_generate_random_block((void *)&nonce, sizeof(nonce));
+ }
+
+ if (ret == 0 && (sp = krb5_storage_emem()) == NULL)
+ ret = krb5_enomem(r->context);
+ if (ret == 0)
+ ret = krb5_store_int64(sp, t);
+ if (ret == 0)
+ ret = krb5_store_uint64(sp, nonce);
+ if (ret == 0)
+ ret = mac_csrf_token(r, sp);
+ if (ret == 0)
+ ret = krb5_storage_to_data(sp, &data);
+ if (ret == 0 && data.length > INT_MAX)
+ ret = ERANGE;
+ if (ret == 0 &&
+ (dlen = rk_base64_encode(data.data, data.length, token)) < 0)
+ ret = errno;
+ krb5_storage_free(sp);
+ krb5_data_free(&data);
+ return ret;
+}
+
+static heim_mhd_result
+validate_csrf_token(struct bx509_request_desc *r)
+{
+ const char *given;
+ int64_t age;
+ krb5_error_code ret;
+
+ if ((((csrf_prot_type & CSRF_PROT_GET_WITH_HEADER) &&
+ strcmp(r->method, "GET") == 0) ||
+ ((csrf_prot_type & CSRF_PROT_POST_WITH_HEADER) &&
+ strcmp(r->method, "POST") == 0)) &&
+ MHD_lookup_connection_value(r->connection, MHD_HEADER_KIND,
+ csrf_header) == NULL) {
+ ret = bad_req(r, EACCES, MHD_HTTP_FORBIDDEN,
+ "Request must have header \"%s\"", csrf_header);
+ return ret == -1 ? MHD_NO : MHD_YES;
+ }
+
+ if (strcmp(r->method, "GET") == 0 &&
+ !(csrf_prot_type & CSRF_PROT_GET_WITH_TOKEN))
+ return 0;
+ if (strcmp(r->method, "POST") == 0 &&
+ !(csrf_prot_type & CSRF_PROT_POST_WITH_TOKEN))
+ return 0;
+
+ given = MHD_lookup_connection_value(r->connection, MHD_HEADER_KIND,
+ "X-CSRF-Token");
+ ret = make_csrf_token(r, given, &r->csrf_token, &age);
+ if (ret)
+ return bad_503(r, ret, "Could not make or validate CSRF token");
+ if (given == NULL)
+ return bad_req(r, EACCES, MHD_HTTP_FORBIDDEN,
+ "CSRF token needed; copy the X-CSRF-Token: response "
+ "header to your next POST");
+ if (strlen(given) != strlen(r->csrf_token) ||
+ strcmp(given, r->csrf_token) != 0)
+ return bad_403(r, EACCES, "Invalid CSRF token");
+ if (age > 300)
+ return bad_403(r, EACCES, "CSRF token expired");
+ return 0;
+}
+
+/*
+ * MHD callback to free the request context when MHD is done sending the
+ * response.
+ */
+static void
+cleanup_req(void *cls,
+ struct MHD_Connection *connection,
+ void **con_cls,
+ enum MHD_RequestTerminationCode toe)
+{
+ struct bx509_request_desc *r = *con_cls;
+
+ (void)cls;
+ (void)connection;
+ (void)toe;
+ clean_req_desc(r);
+ *con_cls = NULL;
+}
+
+/* Callback for MHD POST form data processing */
+static heim_mhd_result
+ip(void *cls,
+ enum MHD_ValueKind kind,
+ const char *key,
+ const char *content_name,
+ const char *content_type,
+ const char *transfer_encoding,
+ const char *val,
+ uint64_t off,
+ size_t size)
+{
+ struct bx509_request_desc *r = cls;
+ struct free_tend_list *ftl = calloc(1, sizeof(*ftl));
+ char *keydup = strdup(key);
+ char *valdup = strndup(val, size);
+
+ (void)content_name; /* MIME attachment name */
+ (void)content_type; /* Don't care -- MHD liked it */
+ (void)transfer_encoding;
+ (void)off; /* Offset in POST data */
+
+ /*
+ * We're going to MHD_set_connection_value(), but we need copies because
+ * the MHD POST processor quite naturally keeps none of the chunks
+ * received.
+ */
+ if (ftl == NULL || keydup == NULL || valdup == NULL) {
+ free(ftl);
+ free(keydup);
+ return MHD_NO;
+ }
+ ftl->freeme1 = keydup;
+ ftl->freeme2 = valdup;
+ ftl->next = r->free_list;
+ r->free_list = ftl;
+
+ return MHD_set_connection_value(r->connection, MHD_GET_ARGUMENT_KIND,
+ keydup, valdup);
+}
+
+typedef krb5_error_code (*handler)(struct bx509_request_desc *);
+
+struct route {
+ const char *local_part;
+ handler h;
+ unsigned int referer_ok:1;
+} routes[] = {
+ { "/get-cert", bx509, 0 },
+ { "/get-negotiate-token", bnegotiate, 1 },
+ { "/get-tgt", get_tgt, 0 },
+ { "/get-tgts", get_tgts, 0 },
+ /* Lousy old names to be removed eventually */
+ { "/bnegotiate", bnegotiate, 1 },
+ { "/bx509", bx509, 0 },
+};
+
+/*
+ * We should commonalize all of:
+ *
+ * - route() and related infrastructure
+ * - including the CSRF functions
+ * - and Negotiate/Bearer authentication
+ *
+ * so that we end up with a simple framework that our daemons can invoke to
+ * serve simple functions that take a fully-consumed request and send a
+ * response.
+ *
+ * Then:
+ *
+ * - split out the CA and non-CA bits into separate daemons using that common
+ * code,
+ * - make httpkadmind use that common code,
+ * - abstract out all the MHD stuff.
+ */
+
+/* Routes requests */
static heim_mhd_result
route(void *cls,
struct MHD_Connection *connection,
@@ -1867,46 +2477,137 @@ route(void *cls,
size_t *upload_data_size,
void **ctx)
{
- static int aptr = 0;
- struct bx509_request_desc r;
+ struct bx509_request_desc *r = *ctx;
+ size_t i;
int ret;
- if (*ctx == NULL) {
+ if (r == NULL) {
/*
* This is the first call, right after headers were read.
*
* We must return quickly so that any 100-Continue might be sent with
- * celerity.
+ * celerity. We want to make sure to send any 401s early, so we check
+ * WWW-Authenticate now, not later.
*
- * We'll get called again to really do the processing. If we handled
- * POSTs then we'd also get called with upload_data != NULL between the
- * first and last calls. We need to keep no state between the first
- * and last calls, but we do need to distinguish first and last call,
- * so we use the ctx argument for this.
+ * We'll get called again to really do the processing. If we're
+ * handling a POST then we'll also get called with upload_data != NULL,
+ * possibly multiple times.
*/
- *ctx = &aptr;
- return MHD_YES;
+ if ((ret = set_req_desc(connection, method, url, &r)))
+ return bad_503(r, ret, "Could not initialize request state");
+ *ctx = r;
+
+ /* All requests other than /health require authentication */
+ if (strcmp(url, "/health") == 0)
+ return MHD_YES;
+
+ /*
+ * Authenticate and do CSRF protection.
+ *
+ * If the Referer: header is set in the request, we don't want CSRF
+ * protection as only /get-negotiate-token will accept a Referer:
+ * header (see routes[] and below), so we'll call validate_csrf_token()
+ * for the other routes or reject the request for having Referer: set.
+ */
+ ret = validate_token(r);
+ if (ret == 0 &&
+ MHD_lookup_connection_value(r->connection, MHD_HEADER_KIND, "Referer") == NULL)
+ ret = validate_csrf_token(r);
+
+ /*
+ * As this is the initial call to this handler, we must return now.
+ *
+ * If authentication or CSRF protection failed then we'll already have
+ * enqueued a 401, 403, or 5xx response and then we're done.
+ *
+ * If both authentication and CSRF protection succeeded then no
+ * response has been queued up and we'll get called again to finally
+ * process the request, then this entire if block will not be executed.
+ */
+ return ret == -1 ? MHD_NO : MHD_YES;
+ }
+
+ /* Validate HTTP method */
+ if (strcmp(method, "GET") != 0 &&
+ strcmp(method, "POST") != 0 &&
+ strcmp(method, "HEAD") != 0) {
+ return bad_405(r, method) == -1 ? MHD_NO : MHD_YES;
}
- if ((ret = set_req_desc(connection, url, &r)))
- return bad_503(&r, ret, "Could not initialize request state");
if ((strcmp(method, "HEAD") == 0 || strcmp(method, "GET") == 0) &&
- (strcmp(url, "/health") == 0 || strcmp(url, "/") == 0))
- ret = health(method, &r);
- else if (strcmp(method, "GET") != 0)
- ret = bad_405(&r, method);
- else if (strcmp(url, "/get-cert") == 0 ||
- strcmp(url, "/bx509") == 0) /* old name */
- ret = bx509(&r);
- else if (strcmp(url, "/get-negotiate-token") == 0 ||
- strcmp(url, "/bnegotiate") == 0) /* old name */
- ret = bnegotiate(&r);
- else if (strcmp(url, "/get-tgt") == 0)
- ret = get_tgt(&r);
- else
- ret = bad_404(&r, url);
+ (strcmp(url, "/health") == 0 || strcmp(url, "/") == 0)) {
+ /* /health end-point -- no authentication, no CSRF, no nothing */
+ return health(method, r) == -1 ? MHD_NO : MHD_YES;
+ }
+
+ if (r->cname == NULL)
+ return bad_401(r, "Authorization token is missing");
- clean_req_desc(&r);
+ if (strcmp(method, "POST") == 0 && *upload_data_size != 0) {
+ /*
+ * Consume all the POST body and set form data as MHD_GET_ARGUMENT_KIND
+ * (as if they had been URI query parameters).
+ *
+ * We have to do this before we can MHD_queue_response() as MHD will
+ * not consume the rest of the request body on its own, so it's an
+ * error to MHD_queue_response() before we've done this, and if we do
+ * then MHD just closes the connection.
+ *
+ * 4KB should be more than enough buffer space for all the keys we
+ * expect.
+ */
+ if (r->pp == NULL)
+ r->pp = MHD_create_post_processor(connection, 4096, ip, r);
+ if (r->pp == NULL) {
+ ret = bad_503(r, errno ? errno : ENOMEM,
+ "Could not consume POST data");
+ return ret == -1 ? MHD_NO : MHD_YES;
+ }
+ if (r->post_data_size + *upload_data_size > 1UL<<17) {
+ return bad_413(r) == -1 ? MHD_NO : MHD_YES;
+ }
+ r->post_data_size += *upload_data_size;
+ if (MHD_post_process(r->pp, upload_data,
+ *upload_data_size) == MHD_NO) {
+ ret = bad_503(r, errno ? errno : ENOMEM,
+ "Could not consume POST data");
+ return ret == -1 ? MHD_NO : MHD_YES;
+ }
+ *upload_data_size = 0;
+ return MHD_YES;
+ }
+
+ /*
+ * Either this is a HEAD, a GET, or a POST whose request body has now been
+ * received completely and processed.
+ */
+
+ /* Allow GET? */
+ if (strcmp(method, "GET") == 0 && !allow_GET_flag) {
+ /* No */
+ return bad_405(r, method) == -1 ? MHD_NO : MHD_YES;
+ }
+
+ for (i = 0; i < sizeof(routes)/sizeof(routes[0]); i++) {
+ if (strcmp(url, routes[i].local_part) != 0)
+ continue;
+ if (!routes[i].referer_ok &&
+ MHD_lookup_connection_value(r->connection,
+ MHD_HEADER_KIND,
+ "Referer") != NULL) {
+ ret = bad_req(r, EACCES, MHD_HTTP_FORBIDDEN,
+ "GET from browser not allowed");
+ return ret == -1 ? MHD_NO : MHD_YES;
+ }
+ if (strcmp(method, "HEAD") == 0)
+ ret = resp(r, MHD_HTTP_OK, MHD_RESPMEM_PERSISTENT, NULL, "", 0,
+ NULL);
+ else
+ ret = routes[i].h(r);
+ return ret == -1 ? MHD_NO : MHD_YES;
+ }
+
+ ret = bad_404(r, url);
return ret == -1 ? MHD_NO : MHD_YES;
}
@@ -1914,14 +2615,21 @@ static struct getargs args[] = {
{ "help", 'h', arg_flag, &help_flag, "Print usage message", NULL },
{ "version", '\0', arg_flag, &version_flag, "Print version", NULL },
{ NULL, 'H', arg_strings, &audiences,
- "expected token audience(s) of bx509 service", "HOSTNAME" },
+ "expected token audience(s)", "HOSTNAME" },
{ "daemon", 'd', arg_flag, &daemonize, "daemonize", "daemonize" },
{ "daemon-child", 0, arg_flag, &daemon_child_fd, NULL, NULL }, /* priv */
{ "reverse-proxied", 0, arg_flag, &reverse_proxied_flag,
"reverse proxied", "listen on 127.0.0.1 and do not use TLS" },
- { NULL, 'p', arg_integer, &port, "PORT", "port number (default: 443)" },
+ { "port", 'p', arg_integer, &port, "port number (default: 443)", "PORT" },
{ "cache-dir", 0, arg_string, &cache_dir,
"cache directory", "DIRECTORY" },
+ { "allow-GET", 0, arg_negative_flag, &allow_GET_flag, NULL, NULL },
+ { "csrf-header", 0, arg_flag,
+ &csrf_header, "required request header", "HEADER-NAME" },
+ { "csrf-protection-type", 0, arg_strings, &csrf_prot_type_strs,
+ "Anti-CSRF protection type", "TYPE" },
+ { "csrf-key-file", 0, arg_string, &csrf_key_file,
+ "CSRF MAC key", "FILE" },
{ "cert", 0, arg_string, &cert_file,
"certificate file path (PEM)", "HX509-STORE" },
{ "private-key", 0, arg_string, &priv_key_file,
@@ -1935,9 +2643,10 @@ static int
usage(int e)
{
arg_printusage(args, sizeof(args) / sizeof(args[0]), "bx509",
- "\nServes RESTful GETs of /bx509 and /bnegotiate,\n"
- "performing corresponding kx509 and, possibly, PKINIT requests\n"
- "to the KDCs of the requested realms (or just the given REALM).\n");
+ "\nServes RESTful GETs of /get-cert, /get-tgt, /get-tgts, and\n"
+ "/get-negotiate-toke, performing corresponding kx509 and, \n"
+ "possibly, PKINIT requests to the KDCs of the requested \n"
+ "realms (or just the given REALM).\n");
exit(e);
}
@@ -2009,6 +2718,67 @@ load_plugins(krb5_context context)
#endif
}
+static void
+get_csrf_prot_type(krb5_context context)
+{
+ char * const *strs = csrf_prot_type_strs.strings;
+ size_t n = csrf_prot_type_strs.num_strings;
+ size_t i;
+ char **freeme = NULL;
+
+ if (csrf_header == NULL)
+ csrf_header = krb5_config_get_string(context, NULL, "bx509d",
+ "csrf_protection_csrf_header",
+ NULL);
+
+ if (n == 0) {
+ char * const *p;
+
+ strs = freeme = krb5_config_get_strings(context, NULL, "bx509d",
+ "csrf_protection_type", NULL);
+ for (p = strs; p && p; p++)
+ n++;
+ }
+
+ for (i = 0; i < n; i++) {
+ if (strcmp(strs[i], "GET-with-header") == 0)
+ csrf_prot_type |= CSRF_PROT_GET_WITH_HEADER;
+ else if (strcmp(strs[i], "GET-with-token") == 0)
+ csrf_prot_type |= CSRF_PROT_GET_WITH_TOKEN;
+ else if (strcmp(strs[i], "POST-with-header") == 0)
+ csrf_prot_type |= CSRF_PROT_POST_WITH_HEADER;
+ else if (strcmp(strs[i], "POST-with-token") == 0)
+ csrf_prot_type |= CSRF_PROT_POST_WITH_TOKEN;
+ }
+ free(freeme);
+
+ /*
+ * For GETs we default to no CSRF protection as our GETable resources are
+ * safe and idempotent and we count on the browser not to make the
+ * responses available to cross-site requests.
+ *
+ * But, really, we don't want browsers even making these requests since, if
+ * the browsers behave correctly, then there's no point, and if they don't
+ * behave correctly then that could be catastrophic. Of course, there's no
+ * guarantee that a browser won't have other catastrophic bugs, but still,
+ * we should probably change this default in the future:
+ *
+ * if (!(csrf_prot_type & CSRF_PROT_GET_WITH_HEADER) &&
+ * !(csrf_prot_type & CSRF_PROT_GET_WITH_TOKEN))
+ * csrf_prot_type |= <whatever-the-new-default-should-be>;
+ */
+
+ /*
+ * For POSTs we default to CSRF protection with anti-CSRF tokens even
+ * though out POSTable resources are safe and idempotent when POSTed and we
+ * could count on the browser not to make the responses available to
+ * cross-site requests.
+ */
+ if (!(csrf_prot_type & CSRF_PROT_POST_WITH_HEADER) &&
+ !(csrf_prot_type & CSRF_PROT_POST_WITH_TOKEN))
+ csrf_prot_type |= CSRF_PROT_POST_WITH_TOKEN;
+}
+
int
main(int argc, char **argv)
{
@@ -2039,6 +2809,39 @@ main(int argc, char **argv)
if (port < 0)
errx(1, "Port number must be given");
+ if ((errno = pthread_key_create(&k5ctx, k5_free_context)))
+ err(1, "Could not create thread-specific storage");
+
+ if ((errno = get_krb5_context(&context)))
+ err(1, "Could not init krb5 context");
+
+ bx509_openlog(context, "bx509d", &logfac);
+ load_plugins(context);
+
+ if (allow_GET_flag == -1)
+ warnx("It is safer to use --no-allow-GET");
+
+ get_csrf_prot_type(context);
+
+ krb5_generate_random_block((void *)&csrf_key, sizeof(csrf_key));
+ if (csrf_key_file == NULL)
+ csrf_key_file = krb5_config_get_string(context, NULL, "bx509d",
+ "csrf_key_file", NULL);
+ if (csrf_key_file) {
+ ssize_t bytes;
+ int fd;
+
+ fd = open(csrf_key_file, O_RDONLY);
+ if (fd == -1)
+ err(1, "CSRF key file missing %s", csrf_key_file);
+ bytes = read(fd, csrf_key, sizeof(csrf_key));
+ if (bytes == -1)
+ err(1, "Could not read CSRF key file %s", csrf_key_file);
+ if (bytes != sizeof(csrf_key))
+ errx(1, "CSRF key file too small (should be %lu) %s",
+ (unsigned long)sizeof(csrf_key), csrf_key_file);
+ }
+
if (audiences.num_strings == 0) {
char localhost[MAXHOSTNAMELEN];
@@ -2062,15 +2865,6 @@ main(int argc, char **argv)
if (argc != 0)
usage(1);
- if ((errno = pthread_key_create(&k5ctx, k5_free_context)))
- err(1, "Could not create thread-specific storage");
-
- if ((errno = get_krb5_context(&context)))
- err(1, "Could not init krb5 context");
-
- bx509_openlog(context, "bx509d", &logfac);
- load_plugins(context);
-
if (cache_dir == NULL) {
char *s = NULL;
@@ -2191,16 +2985,23 @@ again:
sin.sin_family = AF_INET;
sin.sin_port = htons(port);
current = MHD_start_daemon(flags, port,
+ /*
+ * This is a connection access callback. We
+ * don't use it.
+ */
NULL, NULL,
+ /* This is our request handler */
route, (char *)NULL,
MHD_OPTION_SOCK_ADDR, &sin,
MHD_OPTION_CONNECTION_LIMIT, (unsigned int)200,
MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int)10,
+ /* This is our request cleanup handler */
+ MHD_OPTION_NOTIFY_COMPLETED, cleanup_req, NULL,
MHD_OPTION_END);
} else if (sock != MHD_INVALID_SOCKET) {
/*
- * Certificate/key rollover: reuse the listen socket returned by
- * MHD_quiesce_daemon().
+ * Restart following a possible certificate/key rollover, reusing the
+ * listen socket returned by MHD_quiesce_daemon().
*/
current = MHD_start_daemon(flags | MHD_USE_SSL, port,
NULL, NULL,
@@ -2209,10 +3010,17 @@ again:
MHD_OPTION_HTTPS_MEM_CERT, cert_pem,
MHD_OPTION_CONNECTION_LIMIT, (unsigned int)200,
MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int)10,
+ MHD_OPTION_NOTIFY_COMPLETED, cleanup_req, NULL,
MHD_OPTION_LISTEN_SOCKET, sock,
MHD_OPTION_END);
sock = MHD_INVALID_SOCKET;
} else {
+ /*
+ * Initial MHD_start_daemon(), with TLS.
+ *
+ * Subsequently we'll restart reusing the listen socket this creates.
+ * See above.
+ */
current = MHD_start_daemon(flags | MHD_USE_SSL, port,
NULL, NULL,
route, (char *)NULL,
@@ -2220,6 +3028,7 @@ again:
MHD_OPTION_HTTPS_MEM_CERT, cert_pem,
MHD_OPTION_CONNECTION_LIMIT, (unsigned int)200,
MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int)10,
+ MHD_OPTION_NOTIFY_COMPLETED, cleanup_req, NULL,
MHD_OPTION_END);
}
if (current == NULL)
diff --git a/third_party/heimdal/kdc/digest-service.c b/third_party/heimdal/kdc/digest-service.c
index 4ea76dbe7e2..275efaff501 100644
--- a/third_party/heimdal/kdc/digest-service.c
+++ b/third_party/heimdal/kdc/digest-service.c
@@ -179,7 +179,7 @@ ntlm_service(void *ctx, const heim_idata *req,
goto failed;
if (ntq.ntChallengeResponce.length != answer.length ||
- memcmp(ntq.ntChallengeResponce.data, answer.data, answer.length) != 0) {
+ ct_memcmp(ntq.ntChallengeResponce.data, answer.data, answer.length) != 0) {
free(answer.data);
ret = EINVAL;
goto failed;
diff --git a/third_party/heimdal/kdc/digest.c b/third_party/heimdal/kdc/digest.c
index 092b4a75a4b..3285aaa54d0 100644
--- a/third_party/heimdal/kdc/digest.c
+++ b/third_party/heimdal/kdc/digest.c
@@ -1314,7 +1314,7 @@ _kdc_do_digest(krb5_context context,
}
if (ireq.u.ntlmRequest.ntlm.length != answer.length ||
- memcmp(ireq.u.ntlmRequest.ntlm.data, answer.data, answer.length) != 0)
+ ct_memcmp(ireq.u.ntlmRequest.ntlm.data, answer.data, answer.length) != 0)
{
free(answer.data);
ret = EINVAL;
diff --git a/third_party/heimdal/kdc/httpkadmind.8 b/third_party/heimdal/kdc/httpkadmind.8
index 90b4f63aa69..345e9594a7d 100644
--- a/third_party/heimdal/kdc/httpkadmind.8
+++ b/third_party/heimdal/kdc/httpkadmind.8
@@ -43,6 +43,10 @@
.Op Fl Fl daemon-child
.Op Fl Fl reverse-proxied
.Op Fl p Ar port number (default: 443)
+.Op Fl Fl allow-GET
+.Op Fl Fl no-allow-GET
+.Op Fl Fl GET-with-csrf-token
+.Op Fl Fl csrf-header= Ns Ar HEADER-NAME
.Op Fl Fl temp-dir= Ns Ar DIRECTORY
.Op Fl Fl cert=HX509-STORE
.Op Fl Fl private-key=HX509-STORE
@@ -64,15 +68,23 @@
.Xc
.Oc
.Sh DESCRIPTION
-Serves the following resources:
+Serves the following resources over HTTP:
.Ar /get-keys and
.Ar /get-config .
.Pp
The
.Ar /get-keys
-end-point allows callers to get keytab content for named
-principals, possibly performing write operations such as creating
-a non-existent principal, or rotating its keys, if requested.
+end-point allows callers to get a principal's keys in
+.Dq keytab
+format for named principals, possibly performing write operations
+such as creating a non-existent principal, or rotating its keys,
+if requested.
+Note that this end-point can cause KDC HDB principal entries to
+be modified or created incidental to fetching the principal's
+keys.
+The use of the HTTP POST method is required when this end-point
+writes to the KDC's HDB.
+See below.
.Pp
The
.Ar /get-config
@@ -96,7 +108,87 @@ end-point accepts a single query parameter:
.Bl -tag -width Ds -offset indent
.It Ar princ=PRINCIPAL .
.El
+.Sh HTTP APIS
+All HTTP APIs served by this program accept POSTs, with all
+request parameters given as either 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 GETs are enabled, then request parameters must be supplied as
+URI query parameters.
+.Pp
+Note that requests that cause changes to the HDB must always be
+done via POST, never GET.
+.Pp
+URI query parameters must be of the form
+.Ar param0=value&param1=value...
+Some parameters can be given multiple values -- see the
+descriptions of the end-points.
+.Sh CROSS-SITE REQUEST FORGERY PROTECTION
+.Em None
+of the resources service by this service are intended to be
+executed by web pages.
+.Pp
+Most of 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
+Some of the resources provided by this service do change
+server-side state, specifically principal entries in the KDC's
+HDB.
+Those always require the use of POST, not GET.
+.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.
+.Pp
+The key for keying the CSRF token HMAC is that of the first
+current key for the
+.Sq WELLKNOWN/CSRFTOKEN
+principal for the realm being used.
+Every realm served by this service must have this principal.
+.Sh GETTING KEYTABS
The
.Ar /get-keys
end-point accepts various parameters:
@@ -140,6 +232,14 @@ If the named principal(s) is (are) virtual, this will cause it
.It Ar create=true
If the named principal(s) does not (do not) exist, this will
cause it (them) to be created.
+The default attributes for new principals created this way will
+be taken (except for the disabled attribute) from any containing
+virtual host-based service principal namespace that include a
+leading
+.Sq .
+in the hostname component, or from
+.Nm krb5.conf(5)
+(see the CONFIGURATION section).
.It Ar rotate=true
This will cause the keys of concrete principals to be rotated.
.It Ar revoke=true
@@ -150,6 +250,31 @@ the target will not be able to be decrypted by the caller as it
will not have the necessary keys.
.El
.Pp
+The HTTP
+.Nm Cache-Control
+header will be set on
+.Nm get-keys
+responses to
+.Dq Nm no-store ,
+and the
+.Nm max-age
+cache control parameter will be set to the least number of
+seconds until before any of the requested principal's keys could
+change.
+For virtual principals this will be either the time left until a
+quarter of the rotation period before the next rotation, or the
+time left until a
+quarter of the rotation period after the next rotation.
+For concrete principals this will be the time left to the first
+such principal's password expiration, or, if none of them have a
+configured password expiration time, then half of the
+.Nm new_service_key_delay
+configured in the
+.Nm [hdb]
+section of the
+.Nm krb5.conf(5)
+file.
+.Pp
Authorization is handled via the same mechanism as in
.Nm bx509d(8)
which was originally intended to authorize certification requests
@@ -160,9 +285,10 @@ but using
.Nm [ext_keytab]
as the
.Nm krb5.conf(5) section.
-Clients with host-based principals for the the host service can
-create and extract keys for their own service name, but otherwise
-a number of service names are not denied:
+Clients with host-based principals for the
+.Dq host
+service can create and extract keys for their own service name,
+but otherwise a number of service names are denied:
.Bl -tag -width Ds -offset indent
.It Dq host
.It Dq root
@@ -207,6 +333,51 @@ Serves HTTP instead of HTTPS, accepting only looped-back connections.
.Xc
PORT
.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).
+.It Xo
.Fl Fl temp-dir= Ns Ar DIRECTORY
.Xc
Directory for temp files.
@@ -361,11 +532,69 @@ Authorizer configuration goes in
in
.Nm krb5.conf(5). For example:
.Pp
+.Bd -literal -offset indent
[ext_keytab]
simple_csr_authorizer_directory = /etc/krb5/simple_csr_authz
ipc_csr_authorizer = {
service = UNIX:/var/heimdal/csr_authorizer_sock
}
+.Ed
+.Pp
+Configuration parameters specific to
+.Nm httpkadmind :
+.Bl -tag -width Ds -offset indent
+.It csr_authorizer_handles_svc_names = BOOL
+.It new_hostbased_service_principal_attributes = ...
+.El
+.Pp
+The
+.Nm [ext_keytab]
+.Nm get_keys_max_spns = NUMBER
+parameter can be used to specify a maximum number of principals whose
+keys can be retrieved in one
+.Nm GET
+of the
+.Nm /get-keys
+end-point.
+Defaults to 400.
+.Pp
+The
+.Nm [ext_keytab]
+.Nm new_hostbased_service_principal_attributes
+parameter may be used instead of virtual host-based service
+namespace principals to specify the attributes of new principals
+created by
+.Nm httpkadmind ,
+and its value is a hive with a service name then a hostname or
+namespace, and whose value is a set of attributes as given in the
+.Nm kadmin(1) modify
+command.
+For example:
+.Bd -literal -offset indent
+[ext_keytab]
+ new_hostbased_service_principal_attributes = {
+ host = {
+ a-particular-hostname.test.h5l.se = ok-as-delegate
+ .prod.test.h5l.se = ok-as-delegate
+ }
+ }
+.Ed
+.Pp
+which means that
+.Dq host/a-particular-hostname.test.h5l.se ,
+if created via
+.Nm httpkadmind ,
+will be allowed to get delegated credentials (ticket forwarding),
+and that hostnames matching the glob pattern
+.Dq host/*.prod.test.h5l.se ,
+if created via
+.Nm httpkadmind ,
+will also allowed to get delegated credentials.
+All host-based service principals created via
+.Nm httpkadmind
+not matchining any
+.Nm new_hostbased_service_principal_attributes
+service namespaces will have the empty attribute set.
.Sh EXAMPLES
To start
.Nm httpkadmind
diff --git a/third_party/heimdal/kdc/httpkadmind.c b/third_party/heimdal/kdc/httpkadmind.c
index 0e31b4044c3..068b5acbf90 100644
--- a/third_party/heimdal/kdc/httpkadmind.c
+++ b/third_party/heimdal/kdc/httpkadmind.c
@@ -128,6 +128,12 @@ typedef enum MHD_Result heim_mhd_result;
* here.
*/
+struct free_tend_list {
+ void *freeme1;
+ void *freeme2;
+ struct free_tend_list *next;
+};
+
/* Our request description structure */
typedef struct kadmin_request_desc {
HEIM_SVC_REQUEST_DESC_COMMON_ELEMENTS;
@@ -165,6 +171,8 @@ typedef struct kadmin_request_desc {
* be damned.
*/
hx509_request req; /* For authz only */
+ struct free_tend_list *free_list;
+ struct MHD_PostProcessor *pp;
heim_array_t service_names;
heim_array_t hostnames;
heim_array_t spns;
@@ -176,7 +184,11 @@ typedef struct kadmin_request_desc {
char *keytab_name;
char *freeme1;
char *enctypes;
+ char *cache_control;
+ char *csrf_token;
const char *method;
+ krb5_timestamp pw_end;
+ size_t post_data_size;
unsigned int response_set:1;
unsigned int materialize:1;
unsigned int rotate_now:1;
@@ -305,8 +317,18 @@ get_krb5_context(krb5_context *contextp)
return ret;
}
+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 help_flag;
+static int allow_GET_flag = -1;
static int daemonize;
static int daemon_child_fd = -1;
static int local_hdb;
@@ -317,6 +339,8 @@ 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;
@@ -425,7 +449,7 @@ out:
static krb5_error_code resp(kadmin_request_desc, int, krb5_error_code,
enum MHD_ResponseMemoryMode, const char *,
- const void *, size_t, const char *, const char *);
+ const void *, size_t, const char *);
static krb5_error_code bad_req(kadmin_request_desc, krb5_error_code, int,
const char *, ...)
HEIMDAL_PRINTF_ATTRIBUTE((__printf__, 4, 5));
@@ -434,7 +458,7 @@ static krb5_error_code bad_enomem(kadmin_request_desc, krb5_error_code);
static krb5_error_code bad_400(kadmin_request_desc, krb5_error_code, const char *);
static krb5_error_code bad_401(kadmin_request_desc, const char *);
static krb5_error_code bad_403(kadmin_request_desc, krb5_error_code, const char *);
-static krb5_error_code bad_404(kadmin_request_desc, const char *);
+static krb5_error_code bad_404(kadmin_request_desc, krb5_error_code, const char *);
static krb5_error_code bad_405(kadmin_request_desc, const char *);
/*static krb5_error_code bad_500(kadmin_request_desc, krb5_error_code, const char *);*/
static krb5_error_code bad_503(kadmin_request_desc, krb5_error_code, const char *);
@@ -635,8 +659,7 @@ resp(kadmin_request_desc r,
const char *content_type,
const void *body,
size_t bodylen,
- const char *token,
- const char *csrf)
+ const char *token)
{
struct MHD_Response *response;
int mret = MHD_YES;
@@ -657,8 +680,35 @@ resp(kadmin_request_desc r,
rmmode);
if (response == NULL)
return -1;
- mret = MHD_add_response_header(response, MHD_HTTP_HEADER_CACHE_CONTROL,
- "no-store, max-age=0");
+ mret = MHD_add_response_header(response, MHD_HTTP_HEADER_AGE, "0");
+ if (mret == MHD_YES && http_status_code == MHD_HTTP_OK) {
+ krb5_timestamp now;
+
+ free(r->cache_control);
+ r->cache_control = NULL;
+ krb5_timeofday(r->context, &now);
+ if (r->pw_end && r->pw_end > now) {
+ if (asprintf(&r->cache_control, "no-store, max-age=%lld",
+ (long long)r->pw_end - now) == -1 ||
+ r->cache_control == NULL)
+ /* Soft handling of ENOMEM here */
+ mret = MHD_add_response_header(response,
+ MHD_HTTP_HEADER_CACHE_CONTROL,
+ "no-store, max-age=3600");
+ else
+ mret = MHD_add_response_header(response,
+ MHD_HTTP_HEADER_CACHE_CONTROL,
+ r->cache_control);
+
+ } else
+ mret = MHD_add_response_header(response,
+ MHD_HTTP_HEADER_CACHE_CONTROL,
+ "no-store, max-age=0");
+ } else {
+ /* Shouldn't happen */
+ mret = MHD_add_response_header(response, MHD_HTTP_HEADER_CACHE_CONTROL,
+ "no-store, max-age=0");
+ }
if (mret == MHD_YES && http_status_code == MHD_HTTP_UNAUTHORIZED) {
size_t i;
@@ -680,10 +730,10 @@ resp(kadmin_request_desc r,
http_status_code = MHD_HTTP_SERVICE_UNAVAILABLE;
}
- if (mret == MHD_YES && csrf)
+ if (mret == MHD_YES && r->csrf_token)
mret = MHD_add_response_header(response,
"X-CSRF-Token",
- csrf);
+ r->csrf_token);
if (mret == MHD_YES && content_type) {
mret = MHD_add_response_header(response,
@@ -722,7 +772,7 @@ bad_reqv(kadmin_request_desc r,
if (context)
krb5_log_msg(context, logfac, 1, NULL, "Out of memory");
return resp(r, http_status_code, code, MHD_RESPMEM_PERSISTENT,
- NULL, fmt, BODYLEN_IS_STRLEN, NULL, NULL);
+ NULL, fmt, BODYLEN_IS_STRLEN, NULL);
}
if (code) {
@@ -749,11 +799,11 @@ bad_reqv(kadmin_request_desc r,
krb5_log_msg(context, logfac, 1, NULL, "Out of memory");
return resp(r, MHD_HTTP_SERVICE_UNAVAILABLE, ENOMEM,
MHD_RESPMEM_PERSISTENT, NULL,
- "Out of memory", BODYLEN_IS_STRLEN, NULL, NULL);
+ "Out of memory", BODYLEN_IS_STRLEN, NULL);
}
ret = resp(r, http_status_code, code, MHD_RESPMEM_MUST_COPY,
- NULL, msg, BODYLEN_IS_STRLEN, NULL, NULL);
+ NULL, msg, BODYLEN_IS_STRLEN, NULL);
free(formatted);
free(msg);
return ret == -1 ? -1 : code;
@@ -801,9 +851,9 @@ bad_403(kadmin_request_desc r, krb5_error_code ret, const char *reason)
}
static krb5_error_code
-bad_404(kadmin_request_desc r, const char *name)
+bad_404(kadmin_request_desc r, krb5_error_code ret, const char *name)
{
- return bad_req(r, ENOENT, MHD_HTTP_NOT_FOUND,
+ return bad_req(r, ret, MHD_HTTP_NOT_FOUND,
"Resource not found: %s", name);
}
@@ -815,6 +865,13 @@ bad_405(kadmin_request_desc r, const char *method)
}
static krb5_error_code
+bad_413(kadmin_request_desc r)
+{
+ return bad_req(r, E2BIG, MHD_HTTP_METHOD_NOT_ALLOWED,
+ "POST request body too large");
+}
+
+static krb5_error_code
bad_method_want_POST(kadmin_request_desc r)
{
return bad_req(r, EPERM, MHD_HTTP_METHOD_NOT_ALLOWED,
@@ -859,7 +916,7 @@ good_ext_keytab(kadmin_request_desc r)
return bad_503(r, ret, "Could not recover keytab from temp file");
ret = resp(r, MHD_HTTP_OK, 0, MHD_RESPMEM_MUST_COPY,
- "application/octet-stream", body, bodylen, NULL, NULL);
+ "application/octet-stream", body, bodylen, NULL);
free(body);
return ret;
}
@@ -1215,6 +1272,93 @@ make_kstuple(krb5_context context,
return *kstuple ? 0 :krb5_enomem(context);
}
+/* Copied from kadmin/util.c */
+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 },
+ { "allow-digest", KRB5_KDB_ALLOW_DIGEST },
+ { "allow-kerberos4", KRB5_KDB_ALLOW_KERBEROS4 },
+ { "trusted-for-delegation", KRB5_KDB_TRUSTED_FOR_DELEGATION },
+ { "ok-as-delegate", KRB5_KDB_OK_AS_DELEGATE },
+ { "new-princ", KRB5_KDB_NEW_PRINC },
+ { "support-desmd5", KRB5_KDB_SUPPORT_DESMD5 },
+ { "pwchange-service", KRB5_KDB_PWCHANGE_SERVICE },
+ { "disallow-svr", KRB5_KDB_DISALLOW_SVR },
+ { "requires-pw-change", KRB5_KDB_REQUIRES_PWCHANGE },
+ { "requires-hw-auth", KRB5_KDB_REQUIRES_HW_AUTH },
+ { "requires-pre-auth", KRB5_KDB_REQUIRES_PRE_AUTH },
+ { "disallow-all-tix", KRB5_KDB_DISALLOW_ALL_TIX },
+ { "disallow-dup-skey", KRB5_KDB_DISALLOW_DUP_SKEY },
+ { "disallow-proxiable", KRB5_KDB_DISALLOW_PROXIABLE },
+ { "disallow-renewable", KRB5_KDB_DISALLOW_RENEWABLE },
+ { "disallow-tgt-based", KRB5_KDB_DISALLOW_TGT_BASED },
+ { "disallow-forwardable", KRB5_KDB_DISALLOW_FORWARDABLE },
+ { "disallow-postdated", KRB5_KDB_DISALLOW_POSTDATED },
+ { NULL, 0 }
+};
+
+/*
+ * Determine the default/allowed attributes for some new principal.
+ */
+static krb5_flags
+create_attributes(kadmin_request_desc r, krb5_const_principal p)
+{
+ krb5_error_code ret;
+ const char *srealm = krb5_principal_get_realm(r->context, p);
+ const char *svc;
+ const char *hn;
+
+ /* Has to be a host-based service principal (for now) */
+ if (krb5_principal_get_num_comp(r->context, p) != 2)
+ return 0;
+
+ hn = krb5_principal_get_comp_string(r->context, p, 1);
+ svc = krb5_principal_get_comp_string(r->context, p, 0);
+
+ while (hn && strchr(hn, '.') != NULL) {
+ kadm5_principal_ent_rec nsprinc;
+ krb5_principal nsp;
+ uint64_t a = 0;
+ const char *as;
+
+ /* Try finding a virtual host-based service principal namespace */
+ memset(&nsprinc, 0, sizeof(nsprinc));
+ ret = krb5_make_principal(r->context, &nsp, srealm,
+ KRB5_WELLKNOWN_NAME, HDB_WK_NAMESPACE,
+ svc, hn, NULL);
+ if (ret == 0)
+ ret = kadm5_get_principal(r->kadm_handle, nsp, &nsprinc,
+ KADM5_PRINCIPAL | KADM5_ATTRIBUTES);
+ krb5_free_principal(r->context, nsp);
+ if (ret == 0) {
+ /* Found one; use it even if disabled, but drop that attribute */
+ a = nsprinc.attributes & ~KRB5_KDB_DISALLOW_ALL_TIX;
+ kadm5_free_principal_ent(r->kadm_handle, &nsprinc);
+ return a;
+ }
+
+ /* Fallback on krb5.conf */
+ as = krb5_config_get_string(r->context, NULL, "ext_keytab",
+ "new_hostbased_service_principal_attributes",
+ svc, hn, NULL);
+ if (as) {
+ a = parse_flags(as, kdb_attrs, 0);
+ if (a == (uint64_t)-1) {
+ krb5_warnx(r->context, "Invalid value for [ext_keytab] "
+ "new_hostbased_service_principal_attributes");
+ return 0;
+ }
+ return a;
+ }
+
+ hn = strchr(hn + 1, '.');
+ }
+
+ return 0;
+}
+
/*
* Get keys for one principal.
*
@@ -1229,7 +1373,8 @@ get_keys1(kadmin_request_desc r, const char *pname)
krb5_principal p = NULL;
uint32_t mask =
KADM5_PRINCIPAL | KADM5_KVNO | KADM5_MAX_LIFE | KADM5_MAX_RLIFE |
- KADM5_ATTRIBUTES | KADM5_KEY_DATA | KADM5_TL_DATA;
+ KADM5_PW_EXPIRATION | KADM5_ATTRIBUTES | KADM5_KEY_DATA |
+ KADM5_TL_DATA;
uint32_t create_mask = mask & ~(KADM5_KEY_DATA | KADM5_TL_DATA);
size_t nkstuple = 0;
int change = 0;
@@ -1270,6 +1415,9 @@ get_keys1(kadmin_request_desc r, const char *pname)
if (ret == KADM5_UNK_PRINC && r->create) {
char pw[128];
+ memset(&princ, 0, sizeof(princ));
+ princ.attributes = create_attributes(r, p);
+
if (read_only)
ret = KADM5_READ_ONLY;
else
@@ -1281,7 +1429,6 @@ get_keys1(kadmin_request_desc r, const char *pname)
ret = get_kadm_handle(r->context, r->realm, 1 /* want_write */,
&r->kadm_handle);
}
- memset(&princ, 0, sizeof(princ));
/*
* Some software is allergic to kvno 1, assuming that kvno 1 implies
* half-baked service principal. We've some vague recollection of
@@ -1304,9 +1451,6 @@ get_keys1(kadmin_request_desc r, const char *pname)
} else if (ret == 0 && r->materialize &&
(princ.attributes & KRB5_KDB_VIRTUAL)) {
-#ifndef MATERIALIZE_NOTYET
- ret = ENOTSUP;
-#else
if (read_only)
ret = KADM5_READ_ONLY;
else
@@ -1335,7 +1479,6 @@ get_keys1(kadmin_request_desc r, const char *pname)
if (ret == 0)
ret = kadm5_create_principal(r->kadm_handle, &princ, mask, "");
refetch = 1;
-#endif
} /* else create/materialize q-params are superfluous */
/* Handle rotate / revoke options */
@@ -1384,6 +1527,36 @@ get_keys1(kadmin_request_desc r, const char *pname)
if (ret == 0)
ret = write_keytab(r, &princ, pname);
+
+ if (ret == 0) {
+ /*
+ * We will use the principal's password expiration to work out the
+ * value for the max-age Cache-Control.
+ *
+ * Virtual service principals will have their `pw_expiration' set to a
+ * time when the client should refetch keys.
+ *
+ * Concrete service principals will generally not have a non-zero
+ * `pw_expiration', but if we have a new_service_key_delay, then we'll
+ * use half of it as the max-age Cache-Control.
+ */
+ if (princ.pw_expiration == 0) {
+ krb5_timestamp nskd =
+ krb5_config_get_time_default(r->context, NULL, 0, "hdb",
+ "new_service_key_delay", NULL);
+ if (nskd)
+ princ.pw_expiration = time(NULL) + (nskd >> 1);
+ }
+
+ /*
+ * This service can be used to fetch more than one principal's keys, so
+ * the max-age Cache-Control should be derived from the soonest-
+ * "expiring" principal.
+ */
+ if (r->pw_end == 0 ||
+ (princ.pw_expiration < r->pw_end && princ.pw_expiration > time(NULL)))
+ r->pw_end = princ.pw_expiration;
+ }
if (freeit)
kadm5_free_principal_ent(r->kadm_handle, &princ);
krb5_free_principal(r->context, p);
@@ -1398,7 +1571,7 @@ static krb5_error_code check_csrf(kadmin_request_desc);
* When this returns a response will have been set.
*/
static krb5_error_code
-get_keysN(kadmin_request_desc r, const char *method)
+get_keysN(kadmin_request_desc r)
{
krb5_error_code ret;
size_t nhosts;
@@ -1411,11 +1584,17 @@ get_keysN(kadmin_request_desc r, const char *method)
if (ret)
return ret; /* authorize_req() calls bad_req() on error */
+ /*
+ * If we have a r->kadm_handle already it's because we validated a CSRF
+ * token. It may not be a handle to a realm we wanted though.
+ */
+ if (r->kadm_handle)
+ kadm5_destroy(r->kadm_handle);
+ r->kadm_handle = NULL;
ret = get_kadm_handle(r->context, r->realm ? r->realm : realm,
0 /* want_write */, &r->kadm_handle);
-
- if (strcmp(method, "POST") == 0 && (ret = check_csrf(r)))
- return ret; /* check_csrf() calls bad_req() on error */
+ if (ret)
+ return bad_404(r, ret, "Could not connect to realm");
nhosts = heim_array_get_length(r->hostnames);
nsvcs = heim_array_get_length(r->service_names);
@@ -1436,8 +1615,9 @@ get_keysN(kadmin_request_desc r, const char *method)
return bad_503(r, ret, "Out of memory");
}
- /* FIXME: Make this configurable */
- if (nspns + nsvcs * nhosts > 40)
+ if (nspns + nsvcs * nhosts >
+ krb5_config_get_int_default(r->context, NULL, 400,
+ "ext_keytab", "get_keys_max_spns", NULL))
return bad_403(r, EINVAL, "Requested keys for too many principals");
ret = make_keytab(r);
@@ -1485,7 +1665,7 @@ get_keysN(kadmin_request_desc r, const char *method)
krb5_log_msg(r->context, logfac, 1, NULL,
"Redirect %s to primary server", r->cname);
return resp(r, MHD_HTTP_TEMPORARY_REDIRECT, KADM5_READ_ONLY,
- MHD_RESPMEM_PERSISTENT, NULL, "", 0, NULL, NULL);
+ MHD_RESPMEM_PERSISTENT, NULL, "", 0, NULL);
} else {
krb5_log_msg(r->context, logfac, 1, NULL, "HDB is read-only");
return bad_403(r, ret, "HDB is read-only");
@@ -1518,25 +1698,33 @@ addr_to_string(krb5_context context,
snprintf(str, len, "<family=%d>", addr->sa_family);
}
+static void clean_req_desc(kadmin_request_desc);
+
static krb5_error_code
set_req_desc(struct MHD_Connection *connection,
const char *method,
const char *url,
- kadmin_request_desc r)
+ kadmin_request_desc *rp)
{
const union MHD_ConnectionInfo *ci;
+ kadmin_request_desc r;
const char *token;
krb5_error_code ret;
- memset(r, 0, sizeof(*r));
- (void) gettimeofday(&r->tv_start, NULL);
+ *rp = NULL;
+ if ((r = calloc(1, sizeof(*r))) == NULL)
+ return ENOMEM;
- if ((ret = get_krb5_context(&r->context)))
+ (void) gettimeofday(&r->tv_start, NULL);
+ if ((ret = get_krb5_context(&r->context))) {
+ free(r);
return ret;
+ }
/* HEIM_SVC_REQUEST_DESC_COMMON_ELEMENTS fields */
r->request.data = "<HTTP-REQUEST>";
r->request.length = sizeof("<HTTP-REQUEST>");
r->from = r->frombuf;
+ r->free_list = NULL;
r->config = NULL;
r->logf = logfac;
r->reqtype = url;
@@ -1546,6 +1734,7 @@ set_req_desc(struct MHD_Connection *connection,
r->cname = NULL;
r->addr = NULL;
r->kv = heim_dict_create(10);
+ r->pp = NULL;
r->attributes = heim_dict_create(1);
/* Our fields */
r->connection = connection;
@@ -1556,6 +1745,7 @@ set_req_desc(struct MHD_Connection *connection,
r->spns = heim_array_create();
r->keytab_name = NULL;
r->enctypes = NULL;
+ r->cache_control = NULL;
r->freeme1 = NULL;
r->method = method;
r->cprinc = NULL;
@@ -1590,6 +1780,10 @@ set_req_desc(struct MHD_Connection *connection,
krb5_log_msg(r->context, logfac, 1, NULL, "Out of memory");
ret = r->error_code = ENOMEM;
}
+ if (ret == 0)
+ *rp = r;
+ else
+ clean_req_desc(r);
return ret;
}
@@ -1605,32 +1799,49 @@ clean_req_desc(kadmin_request_desc r)
(void) unlink(strchr(r->keytab_name, ':') + 1);
if (r->kadm_handle)
kadm5_destroy(r->kadm_handle);
+ if (r->pp)
+ MHD_destroy_post_processor(r->pp);
hx509_request_free(&r->req);
heim_release(r->service_names);
+ heim_release(r->attributes);
heim_release(r->hostnames);
heim_release(r->reason);
heim_release(r->spns);
heim_release(r->kv);
krb5_free_principal(r->context, r->cprinc);
+ free(r->cache_control);
free(r->keytab_name);
+ free(r->csrf_token);
free(r->enctypes);
free(r->freeme1);
free(r->cname);
free(r->sname);
+ free(r->realm);
+ free(r);
+}
+
+static void
+cleanup_req(void *cls,
+ struct MHD_Connection *connection,
+ void **con_cls,
+ enum MHD_RequestTerminationCode toe)
+{
+ kadmin_request_desc r = *con_cls;
+
+ (void)cls;
+ (void)connection;
+ (void)toe;
+ clean_req_desc(r);
+ *con_cls = NULL;
}
/* Implements GETs of /get-keys */
static krb5_error_code
-get_keys(kadmin_request_desc r, const char *method)
+get_keys(kadmin_request_desc r)
{
- krb5_error_code ret;
-
- if ((ret = validate_token(r)))
- return ret; /* validate_token() calls bad_req() */
if (r->cname == NULL || r->cprinc == NULL)
- return bad_403(r, EINVAL,
- "Could not extract principal name from token");
- return get_keysN(r, method); /* Sets an HTTP response */
+ return bad_401(r, "Could not extract principal name from token");
+ return get_keysN(r); /* Sets an HTTP response */
}
/* Implements GETs of /get-config */
@@ -1649,11 +1860,8 @@ get_config(kadmin_request_desc r)
void *body = "include /etc/krb5.conf\n";
int freeit = 0;
- if ((ret = validate_token(r)))
- return ret; /* validate_token() calls bad_req() */
if (r->cname == NULL || r->cprinc == NULL)
- return bad_403(r, EINVAL,
- "Could not extract principal name from token");
+ return bad_401(r, "Could not extract principal name from token");
/*
* No authorization needed -- configs are public. Though we do require
* authentication (above).
@@ -1686,7 +1894,7 @@ get_config(kadmin_request_desc r)
}
} else {
r->error_code = ret;
- return bad_404(r, "/get-config");
+ return bad_404(r, ret, "/get-config");
}
}
@@ -1694,7 +1902,7 @@ get_config(kadmin_request_desc r)
krb5_log_msg(r->context, logfac, 1, NULL,
"Returned krb5.conf contents to %s", r->cname);
ret = resp(r, MHD_HTTP_OK, 0, MHD_RESPMEM_MUST_COPY,
- "application/text", body, bodylen, NULL, NULL);
+ "application/text", body, bodylen, NULL);
} else {
ret = bad_503(r, ret, "Could not retrieve principal configuration");
}
@@ -1720,7 +1928,9 @@ mac_csrf_token(kadmin_request_desc r, krb5_storage *sp)
memset(&princ, 0, sizeof(princ));
ret = krb5_storage_to_data(sp, &data);
if (r->kadm_handle == NULL)
- ret = get_kadm_handle(r->context, r->realm, 0 /* want_write */,
+ ret = get_kadm_handle(r->context,
+ r->realm ? r->realm : realm,
+ 0 /* want_write */,
&r->kadm_handle);
if (ret == 0)
ret = krb5_make_principal(r->context, &p,
@@ -1776,7 +1986,6 @@ make_csrf_token(kadmin_request_desc r,
char **token,
int64_t *age)
{
- static HEIMDAL_THREAD_LOCAL char tokenbuf[128]; /* See below, be sad */
krb5_error_code ret = 0;
unsigned char given_decoded[128];
krb5_storage *sp = NULL;
@@ -1827,17 +2036,6 @@ make_csrf_token(kadmin_request_desc r,
if (ret == 0 &&
(dlen = rk_base64_encode(data.data, data.length, token)) < 0)
ret = errno;
- if (ret == 0 && dlen >= sizeof(tokenbuf))
- ret = ERANGE;
- if (ret == 0) {
- /*
- * Work around for older versions of libmicrohttpd do not strdup()ing
- * response header values.
- */
- memcpy(tokenbuf, *token, dlen);
- free(*token);
- *token = tokenbuf;
- }
krb5_storage_free(sp);
krb5_data_free(&data);
return ret;
@@ -1854,11 +2052,28 @@ check_csrf(kadmin_request_desc r)
const char *given;
int64_t age;
size_t givenlen, expectedlen;
- char *expected = NULL;
+
+ if ((((csrf_prot_type & CSRF_PROT_GET_WITH_HEADER) &&
+ strcmp(r->method, "GET") == 0) ||
+ ((csrf_prot_type & CSRF_PROT_POST_WITH_HEADER) &&
+ strcmp(r->method, "POST") == 0)) &&
+ MHD_lookup_connection_value(r->connection, MHD_HEADER_KIND,
+ csrf_header) == NULL) {
+ ret = bad_req(r, EACCES, MHD_HTTP_FORBIDDEN,
+ "Request must have header \"%s\"", csrf_header);
+ return ret == -1 ? MHD_NO : MHD_YES;
+ }
+
+ if (strcmp(r->method, "GET") == 0 &&
+ !(csrf_prot_type & CSRF_PROT_GET_WITH_TOKEN))
+ return 0;
+ if (strcmp(r->method, "POST") == 0 &&
+ !(csrf_prot_type & CSRF_PROT_POST_WITH_TOKEN))
+ return 0;
given = MHD_lookup_connection_value(r->connection, MHD_HEADER_KIND,
"X-CSRF-Token");
- ret = make_csrf_token(r, given, &expected, &age);
+ ret = make_csrf_token(r, given, &r->csrf_token, &age);
if (ret)
return bad_503(r, ret, "Could not create a CSRF token");
/*
@@ -1868,15 +2083,14 @@ check_csrf(kadmin_request_desc r)
if (given == NULL) {
(void) resp(r, MHD_HTTP_FORBIDDEN, ENOSYS, MHD_RESPMEM_PERSISTENT,
NULL, "CSRF token needed; copy the X-CSRF-Token: response "
- "header to your next POST", BODYLEN_IS_STRLEN, NULL,
- expected);
+ "header to your next POST", BODYLEN_IS_STRLEN, NULL);
return ENOSYS;
}
/* Validate the CSRF token for this request */
givenlen = strlen(given);
- expectedlen = strlen(expected);
- if (givenlen != expectedlen || ct_memcmp(given, expected, givenlen)) {
+ expectedlen = strlen(r->csrf_token);
+ if (givenlen != expectedlen || ct_memcmp(given, r->csrf_token, givenlen)) {
(void) bad_403(r, EACCES, "Invalid CSRF token");
return EACCES;
}
@@ -1892,15 +2106,60 @@ health(const char *method, kadmin_request_desc r)
{
if (strcmp(method, "HEAD") == 0) {
return resp(r, MHD_HTTP_OK, 0, MHD_RESPMEM_PERSISTENT, NULL, "", 0,
- NULL, NULL);
+ NULL);
}
return resp(r, MHD_HTTP_OK, 0, MHD_RESPMEM_PERSISTENT, NULL,
"To determine the health of the service, use the /get-config "
- "end-point.\n", BODYLEN_IS_STRLEN, NULL, NULL);
+ "end-point.\n", BODYLEN_IS_STRLEN, NULL);
}
-/* Implements the entirety of this REST service */
+static heim_mhd_result
+ip(void *cls,
+ enum MHD_ValueKind kind,
+ const char *key,
+ const char *content_name,
+ const char *content_type,
+ const char *transfer_encoding,
+ const char *val,
+ uint64_t off,
+ size_t size)
+{
+ kadmin_request_desc r = cls;
+ struct free_tend_list *ftl = calloc(1, sizeof(*ftl));
+ char *keydup = strdup(key);
+ char *valdup = strndup(val, size);
+
+ (void)content_name; /* MIME attachment name */
+ (void)content_type;
+ (void)transfer_encoding;
+ (void)off; /* Offset in POST data */
+
+ /* We're going to MHD_set_connection_value(), but we need copies */
+ if (ftl == NULL || keydup == NULL || valdup == NULL) {
+ free(ftl);
+ free(keydup);
+ return MHD_NO;
+ }
+ ftl->freeme1 = keydup;
+ ftl->freeme2 = valdup;
+ ftl->next = r->free_list;
+ r->free_list = ftl;
+
+ return MHD_set_connection_value(r->connection, MHD_GET_ARGUMENT_KIND,
+ keydup, valdup);
+}
+
+typedef krb5_error_code (*handler)(struct kadmin_request_desc *);
+
+struct route {
+ const char *local_part;
+ handler h;
+} routes[] = {
+ { "/get-keys", get_keys },
+ { "/get-config", get_config },
+};
+
static heim_mhd_result
route(void *cls,
struct MHD_Connection *connection,
@@ -1911,50 +2170,131 @@ route(void *cls,
size_t *upload_data_size,
void **ctx)
{
- static int aptr = 0;
- struct kadmin_request_desc r;
+ struct kadmin_request_desc *r = *ctx;
+ size_t i;
int ret;
- if (*ctx == NULL) {
+ if (r == NULL) {
/*
* This is the first call, right after headers were read.
*
* We must return quickly so that any 100-Continue might be sent with
- * celerity.
+ * celerity. We want to make sure to send any 401s early, so we check
+ * WWW-Authenticate now, not later.
*
- * We'll get called again to really do the processing. If we handled
- * POSTs then we'd also get called with upload_data != NULL between the
- * first and last calls. We need to keep no state between the first
- * and last calls, but we do need to distinguish first and last call,
- * so we use the ctx argument for this.
+ * We'll get called again to really do the processing. If we're
+ * handling a POST then we'll also get called with upload_data != NULL,
+ * possibly multiple times.
*/
- *ctx = &aptr;
+ if ((ret = set_req_desc(connection, method, url, &r))) {
+ return
+ bad_503(r, ret, "Could not initialize request state") == -1
+ ? MHD_NO : MHD_YES;
+ }
+ *ctx = r;
+
+ /*
+ * All requests other than /health require authentication and CSRF
+ * protection.
+ */
+ if (strcmp(url, "/health") == 0)
+ return MHD_YES;
+
+ /* Authenticate and do CSRF protection */
+ ret = validate_token(r);
+ if (ret == 0)
+ ret = check_csrf(r);
+
+ /*
+ * As this is the initial call to this handler, we must return now.
+ *
+ * If authentication or CSRF protection failed then we'll already have
+ * enqueued a 401, 403, or 5xx response and then we're done.
+ *
+ * If both authentication and CSRF protection succeeded then no
+ * response has been queued up and we'll get called again to finally
+ * process the request, then this entire if block will not be executed.
+ */
+ return ret == -1 ? MHD_NO : MHD_YES;
+ }
+
+ /* Validate HTTP method */
+ if (strcmp(method, "GET") != 0 &&
+ strcmp(method, "POST") != 0 &&
+ strcmp(method, "HEAD") != 0) {
+ return bad_405(r, method) == -1 ? MHD_NO : MHD_YES;
+ }
+
+ if ((strcmp(method, "HEAD") == 0 || strcmp(method, "GET") == 0) &&
+ (strcmp(url, "/health") == 0 || strcmp(url, "/") == 0)) {
+ /* /health end-point -- no authentication, no CSRF, no nothing */
+ return health(method, r) == -1 ? MHD_NO : MHD_YES;
+ }
+
+ if (strcmp(method, "POST") == 0 && *upload_data_size != 0) {
+ /*
+ * Consume all the POST body and set form data as MHD_GET_ARGUMENT_KIND
+ * (as if they had been URI query parameters).
+ *
+ * We have to do this before we can MHD_queue_response() as MHD will
+ * not consume the rest of the request body on its own, so it's an
+ * error to MHD_queue_response() before we've done this, and if we do
+ * then MHD just closes the connection.
+ *
+ * 4KB should be more than enough buffer space for all the keys we
+ * expect.
+ */
+ if (r->pp == NULL)
+ r->pp = MHD_create_post_processor(connection, 4096, ip, r);
+ if (r->pp == NULL) {
+ ret = bad_503(r, errno ? errno : ENOMEM,
+ "Could not consume POST data");
+ return ret == -1 ? MHD_NO : MHD_YES;
+ }
+ if (r->post_data_size + *upload_data_size > 1UL<<17) {
+ return bad_413(r) == -1 ? MHD_NO : MHD_YES;
+ }
+ r->post_data_size += *upload_data_size;
+ if (MHD_post_process(r->pp, upload_data,
+ *upload_data_size) == MHD_NO) {
+ ret = bad_503(r, errno ? errno : ENOMEM,
+ "Could not consume POST data");
+ return ret == -1 ? MHD_NO : MHD_YES;
+ }
+ *upload_data_size = 0;
return MHD_YES;
}
/*
- * Note that because we attempt to connect to the HDB in set_req_desc(),
- * this early 503 if we fail to serves to do all of what /health should do.
+ * Either this is a HEAD, a GET, or a POST whose request body has now been
+ * received completely and processed.
*/
- if ((ret = set_req_desc(connection, method, url, &r)))
- return bad_503(&r, ret, "Could not initialize request state");
- if ((strcmp(method, "HEAD") == 0 || strcmp(method, "GET") == 0) &&
- (strcmp(url, "/health") == 0 || strcmp(url, "/") == 0)) {
- ret = health(method, &r);
- } else if (strcmp(method, "GET") != 0 && strcmp(method, "POST") != 0) {
- ret = bad_405(&r, method);
- } else if (strcmp(url, "/get-keys") == 0) {
- ret = get_keys(&r, method);
- } else if (strcmp(url, "/get-config") == 0) {
- if (strcmp(method, "GET") != 0)
- ret = bad_405(&r, method);
+
+ /* Allow GET? */
+ if (strcmp(method, "GET") == 0 && !allow_GET_flag) {
+ /* No */
+ return bad_405(r, method) == -1 ? MHD_NO : MHD_YES;
+ }
+
+ for (i = 0; i < sizeof(routes)/sizeof(routes[0]); i++) {
+ if (strcmp(url, routes[i].local_part) != 0)
+ continue;
+ if (MHD_lookup_connection_value(r->connection,
+ MHD_HEADER_KIND,
+ "Referer") != NULL) {
+ ret = bad_req(r, EACCES, MHD_HTTP_FORBIDDEN,
+ "GET from browser not allowed");
+ return ret == -1 ? MHD_NO : MHD_YES;
+ }
+ if (strcmp(method, "HEAD") == 0)
+ ret = resp(r, MHD_HTTP_OK, 0, MHD_RESPMEM_PERSISTENT, NULL, "", 0,
+ NULL);
else
- ret = get_config(&r);
- } else {
- ret = bad_404(&r, url);
+ ret = routes[i].h(r);
+ return ret == -1 ? MHD_NO : MHD_YES;
}
- clean_req_desc(&r);
+ ret = bad_404(r, ENOENT, url);
return ret == -1 ? MHD_NO : MHD_YES;
}
@@ -1963,6 +2303,10 @@ static struct getargs args[] = {
{ "version", '\0', arg_flag, &version_flag, "Print version", NULL },
{ NULL, 'H', arg_strings, &audiences,
"expected token audience(s) of the service", "HOSTNAME" },
+ { "allow-GET", 0, arg_negative_flag,
+ &allow_GET_flag, NULL, NULL },
+ { "csrf-header", 0, arg_string, &csrf_header,
+ "required request header", "HEADER-NAME" },
{ "daemon", 'd', arg_flag, &daemonize, "daemonize", "daemonize" },
{ "daemon-child", 0, arg_flag, &daemon_child_fd, NULL, NULL }, /* priv */
{ "reverse-proxied", 0, arg_flag, &reverse_proxied_flag,
@@ -2076,6 +2420,67 @@ load_plugins(krb5_context context)
#endif
}
+static void
+get_csrf_prot_type(krb5_context context)
+{
+ char * const *strs = csrf_prot_type_strs.strings;
+ size_t n = csrf_prot_type_strs.num_strings;
+ size_t i;
+ char **freeme = NULL;
+
+ if (csrf_header == NULL)
+ csrf_header = krb5_config_get_string(context, NULL, "bx509d",
+ "csrf_protection_csrf_header",
+ NULL);
+
+ if (n == 0) {
+ char * const *p;
+
+ strs = freeme = krb5_config_get_strings(context, NULL, "bx509d",
+ "csrf_protection_type", NULL);
+ for (p = strs; p && p; p++)
+ n++;
+ }
+
+ for (i = 0; i < n; i++) {
+ if (strcmp(strs[i], "GET-with-header") == 0)
+ csrf_prot_type |= CSRF_PROT_GET_WITH_HEADER;
+ else if (strcmp(strs[i], "GET-with-token") == 0)
+ csrf_prot_type |= CSRF_PROT_GET_WITH_TOKEN;
+ else if (strcmp(strs[i], "POST-with-header") == 0)
+ csrf_prot_type |= CSRF_PROT_POST_WITH_HEADER;
+ else if (strcmp(strs[i], "POST-with-token") == 0)
+ csrf_prot_type |= CSRF_PROT_POST_WITH_TOKEN;
+ }
+ free(freeme);
+
+ /*
+ * For GETs we default to no CSRF protection as our GETable resources are
+ * safe and idempotent and we count on the browser not to make the
+ * responses available to cross-site requests.
+ *
+ * But, really, we don't want browsers even making these requests since, if
+ * the browsers behave correctly, then there's no point, and if they don't
+ * behave correctly then that could be catastrophic. Of course, there's no
+ * guarantee that a browser won't have other catastrophic bugs, but still,
+ * we should probably change this default in the future:
+ *
+ * if (!(csrf_prot_type & CSRF_PROT_GET_WITH_HEADER) &&
+ * !(csrf_prot_type & CSRF_PROT_GET_WITH_TOKEN))
+ * csrf_prot_type |= <whatever-the-new-default-should-be>;
+ */
+
+ /*
+ * For POSTs we default to CSRF protection with anti-CSRF tokens even
+ * though out POSTable resources are safe and idempotent when POSTed and we
+ * could count on the browser not to make the responses available to
+ * cross-site requests.
+ */
+ if (!(csrf_prot_type & CSRF_PROT_POST_WITH_HEADER) &&
+ !(csrf_prot_type & CSRF_PROT_POST_WITH_TOKEN))
+ csrf_prot_type |= CSRF_PROT_POST_WITH_TOKEN;
+}
+
int
main(int argc, char **argv)
{
@@ -2136,6 +2541,8 @@ main(int argc, char **argv)
if ((errno = get_krb5_context(&context)))
err(1, "Could not init krb5 context (config file issue?)");
+ get_csrf_prot_type(context);
+
if (!realm) {
char *s;
@@ -2149,6 +2556,7 @@ main(int argc, char **argv)
&kadm_handle)))
err(1, "Could not connect to HDB");
kadm5_destroy(kadm_handle);
+ kadm_handle = NULL;
my_openlog(context, "httpkadmind", &logfac);
load_plugins(context);
@@ -2273,11 +2681,18 @@ again:
sin.sin_family = AF_INET;
sin.sin_port = htons(port);
current = MHD_start_daemon(flags, port,
+ /*
+ * This is a connection access callback. We
+ * don't use it.
+ */
NULL, NULL,
+ /* This is our request handler */
route, (char *)NULL,
MHD_OPTION_SOCK_ADDR, &sin,
MHD_OPTION_CONNECTION_LIMIT, (unsigned int)200,
MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int)10,
+ /* This is our request cleanup handler */
+ MHD_OPTION_NOTIFY_COMPLETED, cleanup_req, NULL,
MHD_OPTION_END);
} else if (sock != MHD_INVALID_SOCKET) {
/*
@@ -2292,6 +2707,7 @@ again:
MHD_OPTION_CONNECTION_LIMIT, (unsigned int)200,
MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int)10,
MHD_OPTION_LISTEN_SOCKET, sock,
+ MHD_OPTION_NOTIFY_COMPLETED, cleanup_req, NULL,
MHD_OPTION_END);
sock = MHD_INVALID_SOCKET;
} else {
@@ -2302,6 +2718,7 @@ again:
MHD_OPTION_HTTPS_MEM_CERT, cert_pem,
MHD_OPTION_CONNECTION_LIMIT, (unsigned int)200,
MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int)10,
+ MHD_OPTION_NOTIFY_COMPLETED, cleanup_req, NULL,
MHD_OPTION_END);
}
if (current == NULL)
diff --git a/third_party/heimdal/kdc/process.c b/third_party/heimdal/kdc/process.c
index 98a405e17d9..9142d68b24b 100644
--- a/third_party/heimdal/kdc/process.c
+++ b/third_party/heimdal/kdc/process.c
@@ -189,7 +189,34 @@ _kdc_audit_trail(kdc_request_t r, krb5_error_code ret)
CASE(KRB5KDC_ERR_SERVICE_NOTYET);
CASE(KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN);
CASE(KRB5KDC_ERR_TRTYPE_NOSUPP);
+ CASE(KRB5KRB_AP_ERR_BADADDR);
+ CASE(KRB5KRB_AP_ERR_BADDIRECTION);
+ CASE(KRB5KRB_AP_ERR_BAD_INTEGRITY);
+ CASE(KRB5KRB_AP_ERR_BADKEYVER);
+ CASE(KRB5KRB_AP_ERR_BADMATCH);
+ CASE(KRB5KRB_AP_ERR_BADORDER);
+ CASE(KRB5KRB_AP_ERR_BADSEQ);
+ CASE(KRB5KRB_AP_ERR_BADVERSION);
+ CASE(KRB5KRB_AP_ERR_ILL_CR_TKT);
+ CASE(KRB5KRB_AP_ERR_INAPP_CKSUM);
+ CASE(KRB5KRB_AP_ERR_METHOD);
+ CASE(KRB5KRB_AP_ERR_MODIFIED);
+ CASE(KRB5KRB_AP_ERR_MSG_TYPE);
+ CASE(KRB5KRB_AP_ERR_MUT_FAIL);
+ CASE(KRB5KRB_AP_ERR_NOKEY);
+ CASE(KRB5KRB_AP_ERR_NOT_US);
+ CASE(KRB5KRB_AP_ERR_REPEAT);
+ CASE(KRB5KRB_AP_ERR_SKEW);
+ CASE(KRB5KRB_AP_ERR_TKT_EXPIRED);
+ CASE(KRB5KRB_AP_ERR_TKT_INVALID);
+ CASE(KRB5KRB_AP_ERR_TKT_NYV);
+ CASE(KRB5KRB_AP_ERR_V4_REPLY);
+ CASE(KRB5KRB_AP_PATH_NOT_ACCEPTED);
+ CASE(KRB5KRB_AP_WRONG_PRINC);
+ CASE(KRB5KRB_ERR_FIELD_TOOLONG);
+ CASE(KRB5KRB_ERR_GENERIC);
CASE(KRB5KRB_ERR_RESPONSE_TOO_BIG);
+
case 0:
retname = "SUCCESS";
break;
diff --git a/third_party/heimdal/kdc/simple_csr_authorizer.c b/third_party/heimdal/kdc/simple_csr_authorizer.c
index 2300eb53299..b46a8931ad3 100644
--- a/third_party/heimdal/kdc/simple_csr_authorizer.c
+++ b/third_party/heimdal/kdc/simple_csr_authorizer.c
@@ -48,10 +48,10 @@
* <ext> is one of:
*
* - pkinit (SAN)
- * - xmpt (SAN)
- * - emailt (SAN)
- * - ms-upt (SAN)
- * - dnsnamt (SAN)
+ * - xmpp (SAN)
+ * - email (SAN)
+ * - ms-upn (SAN)
+ * - dnsname (SAN)
* - eku (EKU OID)
*
* and <value> is a display form of the SAN or EKU OID, with SANs URL-encoded
diff --git a/third_party/heimdal/kuser/Makefile.am b/third_party/heimdal/kuser/Makefile.am
index 91db2edcafa..96ad36fd29e 100644
--- a/third_party/heimdal/kuser/Makefile.am
+++ b/third_party/heimdal/kuser/Makefile.am
@@ -47,6 +47,7 @@ heimtools_LDADD = \
$(top_builddir)/lib/sl/libsl.la \
$(kinit_LDADD) \
$(LIB_readline) \
+ $(LIB_heimbase) \
$(LIB_hx509)
dist_heimtools_SOURCES = heimtools.c klist.c kx509.c kswitch.c copy_cred_cache.c
diff --git a/third_party/heimdal/kuser/kinit.c b/third_party/heimdal/kuser/kinit.c
index 6ac4b45426a..01cb8ed1a41 100644
--- a/third_party/heimdal/kuser/kinit.c
+++ b/third_party/heimdal/kuser/kinit.c
@@ -1321,13 +1321,15 @@ renew_func(void *ptr)
ret = get_new_tickets(ctx->context, ctx->principal, ctx->ccache,
ctx->ticket_life, 0, ctx->anonymous_pkinit);
}
- expire = ticket_lifetime(ctx->context, ctx->ccache, ctx->principal,
- server_str, &renew_expire);
+ if (ret == 0) {
+ expire = ticket_lifetime(ctx->context, ctx->ccache, ctx->principal,
+ server_str, &renew_expire);
#ifndef NO_AFS
- if (ret == 0 && server_str == NULL && do_afslog && k_hasafs())
- krb5_afslog(ctx->context, ctx->ccache, NULL, NULL);
+ if (server_str == NULL && do_afslog && k_hasafs())
+ krb5_afslog(ctx->context, ctx->ccache, NULL, NULL);
#endif
+ }
update_siginfo_msg(expire, server_str);
diff --git a/third_party/heimdal/kuser/klist.c b/third_party/heimdal/kuser/klist.c
index b33c3c28a7a..f0a438218b7 100644
--- a/third_party/heimdal/kuser/klist.c
+++ b/third_party/heimdal/kuser/klist.c
@@ -38,7 +38,7 @@
#include "heimtools-commands.h"
#undef HC_DEPRECATED_CRYPTO
-static char*
+static const char *
printable_time_internal(time_t t, int x)
{
static char s[128];
@@ -52,13 +52,13 @@ printable_time_internal(time_t t, int x)
return s;
}
-static char*
+static const char *
printable_time(time_t t)
{
return printable_time_internal(t, 20);
}
-static char*
+static const char *
printable_time_long(time_t t)
{
return printable_time_internal(t, 20);
@@ -136,18 +136,173 @@ print_cred(krb5_context context, krb5_creds *cred, rtbl_t ct, int do_flags)
}
static void
-print_cred_verbose(krb5_context context, krb5_creds *cred, int do_json)
+cred2json(krb5_context context, krb5_creds *cred, heim_array_t tix)
{
- size_t j;
+ heim_dict_t t = heim_dict_create(10); /* ticket top-level */
+ heim_dict_t e = heim_dict_create(10); /* ticket times */
+ heim_dict_t f = heim_dict_create(20); /* flags */
+ heim_object_t o;
+ char buf[16], *sp = buf;
char *str;
krb5_error_code ret;
krb5_timestamp sec;
- if (do_json) { /* XXX support more json formating later */
- printf("{ \"verbose-supported\" : false }");
- return;
+ heim_array_append_value(tix, t);
+ krb5_timeofday(context, &sec);
+
+ /*
+ * JSON object names (keys) that start with capitals are for compatibility
+ * with the JSON we used to output. The others are new.
+ */
+ heim_dict_set_value(t, HSTR("times"), e);
+ heim_dict_set_value(t, HSTR("flags"), f);
+
+ heim_dict_set_value(e, HSTR("authtime"),
+ o = heim_number_create(cred->times.authtime));
+ heim_release(o);
+ heim_dict_set_value(t, HSTR("Issued"),
+ o = heim_string_create(printable_time(cred->times.authtime)));
+ heim_release(o);
+ heim_dict_set_value(e, HSTR("starttime"), heim_null_create());
+ if (cred->times.starttime) {
+ heim_dict_set_value(e, HSTR("starttime"),
+ o = heim_number_create(cred->times.starttime));
+ heim_release(o);
+ heim_dict_set_value(t, HSTR("Starttime"),
+ o = heim_string_create(printable_time(cred->times.starttime)));
+ heim_release(o);
+ }
+
+ if (cred->times.renew_till) {
+ heim_dict_set_value(e, HSTR("renew_till"),
+ o = heim_number_create(cred->times.starttime));
+ heim_release(o);
+ heim_dict_set_value(t, HSTR("Renew till"),
+ o = heim_string_create(printable_time(cred->times.starttime)));
+ heim_release(o);
}
+ heim_dict_set_value(e, HSTR("endtime"),
+ o = heim_number_create(cred->times.endtime));
+ heim_release(o);
+
+ if (cred->times.endtime > sec) {
+ heim_dict_set_value(t, HSTR("Expires"),
+ o = heim_string_create(printable_time(cred->times.endtime)));
+ heim_release(o);
+ heim_dict_set_value(t, HSTR("expired"), heim_bool_create(0));
+ } else {
+ heim_dict_set_value(t, HSTR("Expires"), HSTR(">>>Expired<<<"));
+ heim_dict_set_value(t, HSTR("expired"), heim_bool_create(1));
+ }
+
+ ret = krb5_unparse_name(context, cred->server, &str);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_unparse_name");
+ heim_dict_set_value(t, HSTR("Principal"), o = heim_string_create(str));
+ heim_release(o);
+
+ if (cred->flags.b.forwardable) {
+ heim_dict_set_value(f, HSTR("forwardable"), heim_bool_create(1));
+ *sp++ = 'F';
+ } else {
+ heim_dict_set_value(f, HSTR("forwardable"), heim_bool_create(0));
+ }
+ if (cred->flags.b.forwarded) {
+ heim_dict_set_value(f, HSTR("forwarded"), heim_bool_create(1));
+ *sp++ = 'f';
+ } else {
+ heim_dict_set_value(f, HSTR("forwarded"), heim_bool_create(0));
+ }
+ if (cred->flags.b.proxiable) {
+ heim_dict_set_value(f, HSTR("proxiable"), heim_bool_create(1));
+ *sp++ = 'P';
+ } else {
+ heim_dict_set_value(f, HSTR("proxiable"), heim_bool_create(0));
+ }
+ if (cred->flags.b.proxy) {
+ heim_dict_set_value(f, HSTR("proxy"), heim_bool_create(1));
+ *sp++ = 'p';
+ } else {
+ heim_dict_set_value(f, HSTR("proxy"), heim_bool_create(0));
+ }
+ if (cred->flags.b.may_postdate) {
+ heim_dict_set_value(f, HSTR("may_postdate"), heim_bool_create(1));
+ *sp++ = 'D';
+ } else {
+ heim_dict_set_value(f, HSTR("may_postdate"), heim_bool_create(0));
+ }
+ if (cred->flags.b.postdated) {
+ heim_dict_set_value(f, HSTR("postdated"), heim_bool_create(1));
+ *sp++ = 'd';
+ } else {
+ heim_dict_set_value(f, HSTR("postdated"), heim_bool_create(0));
+ }
+ if (cred->flags.b.renewable) {
+ heim_dict_set_value(f, HSTR("renewable"), heim_bool_create(1));
+ *sp++ = 'R';
+ } else {
+ heim_dict_set_value(f, HSTR("renewable"), heim_bool_create(0));
+ }
+ if (cred->flags.b.initial) {
+ heim_dict_set_value(f, HSTR("initial"), heim_bool_create(1));
+ *sp++ = 'I';
+ } else {
+ heim_dict_set_value(f, HSTR("initial"), heim_bool_create(0));
+ }
+ if (cred->flags.b.invalid) {
+ heim_dict_set_value(f, HSTR("invalid"), heim_bool_create(1));
+ *sp++ = 'i';
+ } else {
+ heim_dict_set_value(f, HSTR("invalid"), heim_bool_create(0));
+ }
+ if (cred->flags.b.pre_authent) {
+ heim_dict_set_value(f, HSTR("pre_authent"), heim_bool_create(1));
+ *sp++ = 'A';
+ } else {
+ heim_dict_set_value(f, HSTR("pre_authent"), heim_bool_create(0));
+ }
+ if (cred->flags.b.hw_authent) {
+ heim_dict_set_value(f, HSTR("hw_authent"), heim_bool_create(1));
+ *sp++ = 'H';
+ } else {
+ heim_dict_set_value(f, HSTR("hw_authent"), heim_bool_create(0));
+ }
+ if (cred->flags.b.transited_policy_checked) {
+ heim_dict_set_value(f, HSTR("transited_policy_checked"), heim_bool_create(1));
+ *sp++ = 'T';
+ } else {
+ heim_dict_set_value(f, HSTR("transited_policy_checked"), heim_bool_create(0));
+ }
+ if (cred->flags.b.ok_as_delegate) {
+ heim_dict_set_value(f, HSTR("ok_as_delegate"), heim_bool_create(1));
+ *sp++ = 'O';
+ } else {
+ heim_dict_set_value(f, HSTR("ok_as_delegate"), heim_bool_create(0));
+ }
+ if (cred->flags.b.anonymous) {
+ heim_dict_set_value(f, HSTR("anonymous"), heim_bool_create(1));
+ *sp++ = 'a';
+ } else {
+ heim_dict_set_value(f, HSTR("anonymous"), heim_bool_create(0));
+ }
+ *sp = '\0';
+ heim_dict_set_value(t, HSTR("Flags"), o = heim_string_create(sp));
+ heim_release(e);
+ heim_release(f);
+ heim_release(t);
+ heim_release(o);
+ free(str);
+}
+
+static void
+print_cred_verbose(krb5_context context, krb5_creds *cred)
+{
+ size_t j;
+ char *str;
+ krb5_error_code ret;
+ krb5_timestamp sec;
+
krb5_timeofday (context, &sec);
ret = krb5_unparse_name(context, cred->server, &str);
@@ -252,6 +407,74 @@ print_cred_verbose(krb5_context context, krb5_creds *cred, int do_json)
printf("\n\n");
}
+static void
+cache2json(krb5_context context,
+ krb5_ccache ccache,
+ krb5_principal principal,
+ heim_dict_t dict)
+{
+ heim_array_t tix = heim_array_create();
+ heim_object_t o;
+ char *str, *fullname;
+ char *name = NULL;
+ krb5_error_code ret;
+ krb5_cc_cursor cursor;
+ krb5_creds creds;
+ krb5_deltat sec;
+
+ ret = krb5_unparse_name(context, principal, &str);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_unparse_name");
+
+ ret = krb5_cc_get_full_name(context, ccache, &fullname);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_get_full_name");
+
+ heim_dict_set_value(dict, HSTR("cache"),
+ o = heim_string_create(fullname));
+ heim_release(o);
+ heim_dict_set_value(dict, HSTR("principal"),
+ o = heim_string_create(str));
+ heim_release(o);
+ heim_dict_set_value(dict, HSTR("cache_version"),
+ o = heim_number_create(krb5_cc_get_version(context,
+ ccache)));
+ heim_release(o);
+ free(str);
+
+ ret = krb5_cc_get_friendly_name(context, ccache, &name);
+ if (ret == 0) {
+ heim_dict_set_value(dict, HSTR("friendly_name"),
+ o = heim_string_create(name));
+ heim_release(o);
+ }
+ free(name);
+
+ ret = krb5_cc_get_kdc_offset(context, ccache, &sec);
+ if (ret == 0) {
+ heim_dict_set_value(dict, HSTR("kdc_offset"),
+ o = heim_number_create(sec));
+ heim_release(o);
+ }
+
+ heim_dict_set_value(dict, HSTR("tickets"), tix);
+ ret = krb5_cc_start_seq_get(context, ccache, &cursor);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_start_seq_get");
+
+ while ((ret = krb5_cc_next_cred(context, ccache, &cursor, &creds)) == 0) {
+ cred2json(context, &creds, tix);
+ krb5_free_cred_contents(context, &creds);
+ }
+ if (ret != KRB5_CC_END)
+ krb5_err(context, 1, ret, "krb5_cc_get_next");
+ ret = krb5_cc_end_seq_get(context, ccache, &cursor);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_end_seq_get");
+ heim_release(tix);
+ free(fullname);
+}
+
/*
* Print all tickets in `ccache' on stdout, verbosely if do_verbose.
*/
@@ -262,8 +485,7 @@ print_tickets(krb5_context context,
krb5_principal principal,
int do_verbose,
int do_flags,
- int do_hidden,
- int do_json)
+ int do_hidden)
{
char *str, *name, *fullname;
krb5_error_code ret;
@@ -271,7 +493,6 @@ print_tickets(krb5_context context,
krb5_creds creds;
krb5_deltat sec;
rtbl_t ct = NULL;
- int print_comma = 0;
ret = krb5_unparse_name (context, principal, &str);
if (ret)
@@ -281,27 +502,26 @@ print_tickets(krb5_context context,
if (ret)
krb5_err (context, 1, ret, "krb5_cc_get_full_name");
- if (!do_json) {
- printf ("%17s: %s\n", N_("Credentials cache", ""), fullname);
- printf ("%17s: %s\n", N_("Principal", ""), str);
-
- ret = krb5_cc_get_friendly_name(context, ccache, &name);
- if (ret == 0) {
- if (strcmp(name, str) != 0)
- printf ("%17s: %s\n", N_("Friendly name", ""), name);
- free(name);
- }
+ printf ("%17s: %s\n", N_("Credentials cache", ""), fullname);
+ printf ("%17s: %s\n", N_("Principal", ""), str);
- if(do_verbose) {
- printf ("%17s: %d\n", N_("Cache version", ""),
- krb5_cc_get_version(context, ccache));
- } else {
- krb5_cc_set_flags(context, ccache, KRB5_TC_NOTICKET);
- }
+ ret = krb5_cc_get_friendly_name(context, ccache, &name);
+ if (ret == 0) {
+ if (strcmp(name, str) != 0) {
+ printf ("%17s: %s\n", N_("Friendly name", ""), name);
+ }
+ free(name);
+ }
+
+ if(do_verbose) {
+ printf ("%17s: %d\n", N_("Cache version", ""),
+ krb5_cc_get_version(context, ccache));
+ }
- ret = krb5_cc_get_kdc_offset(context, ccache, &sec);
+ ret = krb5_cc_get_kdc_offset(context, ccache, &sec);
+ if (ret == 0) {
+ if (do_verbose && sec != 0) {
- if (ret == 0 && do_verbose && sec != 0) {
char buf[BUFSIZ];
int val;
int sig;
@@ -317,18 +537,16 @@ print_tickets(krb5_context context,
printf ("%17s: %s%s\n", N_("KDC time offset", ""),
sig == -1 ? "-" : "", buf);
- }
- printf("\n");
- } else {
- printf ("{ \"cache\" : \"%s\", \"principal\" : \"%s\", ", fullname, str);
+ }
}
+ printf("\n");
free(str);
ret = krb5_cc_start_seq_get (context, ccache, &cursor);
if (ret)
krb5_err(context, 1, ret, "krb5_cc_start_seq_get");
- if(!do_verbose) {
+ if (!do_verbose) {
ct = rtbl_create();
rtbl_add_column(ct, COL_ISSUED, 0);
rtbl_add_column(ct, COL_EXPIRES, 0);
@@ -336,21 +554,12 @@ print_tickets(krb5_context context,
rtbl_add_column(ct, COL_FLAGS, 0);
rtbl_add_column(ct, COL_PRINCIPAL, 0);
rtbl_set_separator(ct, " ");
- if (do_json) {
- rtbl_set_flags(ct, RTBL_JSON);
- printf("\"tickets\" : ");
- }
}
- if (do_verbose && do_json)
- printf("\"tickets\" : [");
while ((ret = krb5_cc_next_cred(context, ccache, &cursor, &creds)) == 0) {
if (!do_hidden && krb5_is_config_principal(context, creds.server)) {
;
} else if (do_verbose) {
- if (do_json && print_comma)
- printf(",");
- print_cred_verbose(context, &creds, do_json);
- print_comma = 1;
+ print_cred_verbose(context, &creds);
} else {
print_cred(context, &creds, ct, do_flags);
}
@@ -366,11 +575,6 @@ print_tickets(krb5_context context,
rtbl_format(ct, stdout);
rtbl_destroy(ct);
}
- if (do_json) {
- if (do_verbose)
- printf("]");
- printf("}");
- }
free(fullname);
}
@@ -475,10 +679,10 @@ display_tokens(int do_verbose)
*/
static int
-display_v5_ccache (krb5_context context, krb5_ccache ccache,
- int do_test, int do_verbose,
- int do_flags, int do_hidden,
- int do_json)
+display_v5_ccache(krb5_context context, krb5_ccache ccache,
+ int do_test, int do_verbose,
+ int do_flags, int do_hidden,
+ heim_dict_t dict)
{
krb5_error_code ret;
krb5_principal principal;
@@ -487,10 +691,8 @@ display_v5_ccache (krb5_context context, krb5_ccache ccache,
ret = krb5_cc_get_principal (context, ccache, &principal);
if (ret) {
- if (do_json) {
- printf("{}");
+ if (dict)
return 0;
- }
if(ret == ENOENT) {
if (!do_test)
krb5_warnx(context, N_("No ticket file: %s", ""),
@@ -499,11 +701,18 @@ display_v5_ccache (krb5_context context, krb5_ccache ccache,
} else
krb5_err (context, 1, ret, "krb5_cc_get_principal");
}
- if (do_test)
- exit_status = check_expiration(context, ccache, NULL);
- else
- print_tickets (context, ccache, principal, do_verbose,
- do_flags, do_hidden, do_json);
+ exit_status = check_expiration(context, ccache, NULL);
+ if (!do_test) {
+ if (dict) {
+ heim_dict_set_value(dict, HSTR("expired"),
+ heim_bool_create(!!exit_status));
+ cache2json(context, ccache, principal, dict);
+ } else {
+ print_tickets(context, ccache, principal, do_verbose,
+ do_flags, do_hidden);
+ }
+ exit_status = 0;
+ }
ret = krb5_cc_close (context, ccache);
if (ret)
@@ -514,6 +723,87 @@ display_v5_ccache (krb5_context context, krb5_ccache ccache,
return exit_status;
}
+static int
+caches2json(krb5_context context)
+{
+ krb5_cccol_cursor cursor;
+ const char *cdef_name = krb5_cc_default_name(context);
+ char *def_name;
+ heim_object_t o;
+ heim_array_t a = heim_array_create();
+ krb5_error_code ret;
+ krb5_ccache id;
+
+ if ((def_name = krb5_cccol_get_default_ccname(context)) == NULL)
+ cdef_name = krb5_cc_default_name(context);
+ if (!def_name && cdef_name && (def_name = strdup(cdef_name)) == NULL)
+ krb5_err(context, 1, ENOMEM, "Out of memory");
+
+ ret = krb5_cccol_cursor_new(context, &cursor);
+ if (ret == KRB5_CC_NOSUPP) {
+ free(def_name);
+ return 0;
+ }
+ else if (ret)
+ krb5_err (context, 1, ret, "krb5_cc_cache_get_first");
+
+ while (krb5_cccol_cursor_next(context, cursor, &id) == 0 && id != NULL) {
+ heim_dict_t dict = heim_dict_create(10);
+ int expired = 0;
+ char *name;
+ time_t t;
+
+ expired = check_expiration(context, id, &t);
+ ret = krb5_cc_get_friendly_name(context, id, &name);
+ if (ret == 0) {
+ char *fname;
+
+ heim_dict_set_value(dict, HSTR("Name"),
+ o = heim_string_create(name));
+ heim_release(o);
+ free(name);
+
+ if (expired)
+ o = heim_string_create(N_(">>> Expired <<<", ""));
+ else
+ o = heim_string_create(printable_time(t));
+ heim_dict_set_value(dict, HSTR("Expires"), o);
+ heim_release(o);
+
+ ret = krb5_cc_get_full_name(context, id, &fname);
+ if (ret)
+ krb5_err (context, 1, ret, "krb5_cc_get_full_name");
+
+ heim_dict_set_value(dict, HSTR("Cache Name"),
+ o = heim_string_create(fname));
+ heim_release(o);
+
+ if (def_name && strcmp(fname, def_name) == 0)
+ heim_dict_set_value(dict, HSTR("is_default_cache"),
+ heim_bool_create(1));
+ else
+ heim_dict_set_value(dict, HSTR("is_default_cache"),
+ heim_bool_create(0));
+ heim_array_append_value(a, dict);
+ heim_release(dict);
+
+ krb5_xfree(fname);
+ }
+ krb5_cc_close(context, id);
+ }
+
+ krb5_cccol_cursor_free(context, &cursor);
+ free(def_name);
+
+ o = heim_json_copy_serialize(a, HEIM_JSON_F_STRICT | HEIM_JSON_F_INDENT2,
+ NULL);
+ printf("%s", heim_string_get_utf8(o));
+ heim_release(a);
+ heim_release(o);
+
+ return 0;
+}
+
/*
*
*/
@@ -550,8 +840,6 @@ list_caches(krb5_context context, struct klist_options *opt)
rtbl_set_prefix(ct, " ");
rtbl_set_column_prefix(ct, COL_DEFCACHE, "");
rtbl_set_column_prefix(ct, COL_NAME, " ");
- if (opt->json_flag)
- rtbl_set_flags(ct, RTBL_JSON);
while (krb5_cccol_cursor_next(context, cursor, &id) == 0 && id != NULL) {
int expired = 0;
@@ -579,9 +867,7 @@ list_caches(krb5_context context, struct klist_options *opt)
krb5_err (context, 1, ret, "krb5_cc_get_full_name");
rtbl_add_column_entry(ct, COL_CACHENAME, fname);
- if (opt->json_flag)
- ;
- else if (def_name && strcmp(fname, def_name) == 0)
+ if (def_name && strcmp(fname, def_name) == 0)
rtbl_add_column_entry(ct, COL_DEFCACHE, "*");
else
rtbl_add_column_entry(ct, COL_DEFCACHE, "");
@@ -597,9 +883,6 @@ list_caches(krb5_context context, struct klist_options *opt)
rtbl_format(ct, stdout);
rtbl_destroy(ct);
- if (opt->json_flag)
- printf("\n");
-
return 0;
}
@@ -611,6 +894,7 @@ int
klist(struct klist_options *opt, int argc, char **argv)
{
krb5_error_code ret;
+ heim_object_t o = NULL;
int exit_status = 0;
int do_verbose =
@@ -627,7 +911,10 @@ klist(struct klist_options *opt, int argc, char **argv)
}
if (opt->list_all_flag) {
- exit_status = list_caches(heimtools_context, opt);
+ if (opt->json_flag)
+ exit_status = caches2json(heimtools_context);
+ else
+ exit_status = list_caches(heimtools_context, opt);
return exit_status;
}
@@ -635,32 +922,28 @@ klist(struct klist_options *opt, int argc, char **argv)
krb5_ccache id;
if (opt->all_content_flag) {
+ heim_array_t a = opt->json_flag ? heim_array_create() : NULL;
krb5_cc_cache_cursor cursor;
- int first = 1;
ret = krb5_cc_cache_get_first(heimtools_context, NULL, &cursor);
if (ret)
krb5_err(heimtools_context, 1, ret, "krb5_cc_cache_get_first");
- if (opt->json_flag)
- printf("[");
while (krb5_cc_cache_next(heimtools_context, cursor, &id) == 0) {
- if (opt->json_flag && !first)
- printf(",");
+ heim_dict_t dict = opt->json_flag ? heim_dict_create(10) : NULL;
exit_status |= display_v5_ccache(heimtools_context, id, do_test,
do_verbose, opt->flags_flag,
opt->hidden_flag,
- opt->json_flag);
- if (!opt->json_flag)
- printf("\n\n");
-
- first = 0;
+ dict);
+ if (a)
+ heim_array_append_value(a, dict);
+ heim_release(dict);
}
krb5_cc_cache_end_seq_get(heimtools_context, cursor);
- if (opt->json_flag)
- printf("]");
+ o = a;
} else {
+ heim_dict_t dict = opt->json_flag ? heim_dict_create(10) : NULL;
if(opt->cache_string) {
ret = krb5_cc_resolve(heimtools_context, opt->cache_string, &id);
if (ret)
@@ -672,10 +955,25 @@ klist(struct klist_options *opt, int argc, char **argv)
}
exit_status = display_v5_ccache(heimtools_context, id, do_test,
do_verbose, opt->flags_flag,
- opt->hidden_flag, opt->json_flag);
+ opt->hidden_flag, dict);
+ o = dict;
}
}
+ if (o) {
+ heim_string_t s = heim_json_copy_serialize(o,
+ HEIM_JSON_F_STRICT |
+ HEIM_JSON_F_INDENT2,
+ NULL);
+
+ if (s == NULL)
+ errx(1, "Could not format JSON text");
+
+ printf("%s", heim_string_get_utf8(s));
+ heim_release(o);
+ heim_release(s);
+ }
+
if (!do_test) {
#ifndef NO_AFS
if (opt->tokens_flag && k_hasafs()) {
diff --git a/third_party/heimdal/lib/asn1/asn1_compile.1 b/third_party/heimdal/lib/asn1/asn1_compile.1
index a7953df5fa1..9af27672c2a 100644
--- a/third_party/heimdal/lib/asn1/asn1_compile.1
+++ b/third_party/heimdal/lib/asn1/asn1_compile.1
@@ -330,6 +330,11 @@ to form the names of the files generated.
.It Fl Fl option-file=FILE
Take additional command-line options from
.Ar FILE .
+The options file must have one command-line option per-line, but
+leading whitespace is ignored, and lines that start with a hash
+symbol
+.Sq ( # )
+are comments and are ignored.
.It Fl Fl original-order
Attempt to preserve the original order of type definition in the
ASN.1 module.
diff --git a/third_party/heimdal/lib/asn1/gen_copy.c b/third_party/heimdal/lib/asn1/gen_copy.c
index bec6f8b059f..c6d420a2f16 100644
--- a/third_party/heimdal/lib/asn1/gen_copy.c
+++ b/third_party/heimdal/lib/asn1/gen_copy.c
@@ -62,7 +62,7 @@ copy_type (const char *from, const char *to, const Type *t, int preserve)
copy_primitive ("heim_integer", from, to);
break;
}
- fallthrough;
+ HEIM_FALLTHROUGH;
case TBoolean:
case TEnumerated :
fprintf(codefile, "*(%s) = *(%s);\n", to, from);
diff --git a/third_party/heimdal/lib/asn1/gen_free.c b/third_party/heimdal/lib/asn1/gen_free.c
index 0507d542180..53b7bfe7b90 100644
--- a/third_party/heimdal/lib/asn1/gen_free.c
+++ b/third_party/heimdal/lib/asn1/gen_free.c
@@ -56,7 +56,7 @@ free_type (const char *name, const Type *t, int preserve)
free_primitive ("heim_integer", name);
break;
}
- /* fallthrough; */
+ /* HEIM_FALLTHROUGH; */
case TBoolean:
case TEnumerated :
case TNull:
diff --git a/third_party/heimdal/lib/asn1/main.c b/third_party/heimdal/lib/asn1/main.c
index bcfdad62e2e..569b4782b4d 100644
--- a/third_party/heimdal/lib/asn1/main.c
+++ b/third_party/heimdal/lib/asn1/main.c
@@ -409,8 +409,16 @@ main(int argc, char **argv)
sz = 2;
while (fgets(buf, sizeof(buf), opt) != NULL) {
+ size_t buflen, ws;
+
buf[strcspn(buf, "\n\r")] = '\0';
+ buflen = strlen(buf);
+ if ((ws = strspn(buf, " \t")))
+ memmove(buf, buf + ws, buflen -= ws);
+ if (buf[0] == '\0' || buf[0] == '#')
+ continue;
+
if (len + 1 >= sz) {
arg = realloc(arg, (sz + (sz>>1) + 2) * sizeof(arg[0]));
if (arg == NULL) {
diff --git a/third_party/heimdal/lib/asn1/template.c b/third_party/heimdal/lib/asn1/template.c
index 7a19e7477e3..31eb66004ec 100644
--- a/third_party/heimdal/lib/asn1/template.c
+++ b/third_party/heimdal/lib/asn1/template.c
@@ -2549,7 +2549,9 @@ _asn1_print(const struct asn1_template *t,
}
default: break;
}
- if (nnames)
+ if (nnames &&
+ (t->tt & A1_OP_MASK) != A1_OP_TYPE_DECORATE_EXTERN &&
+ (t->tt & A1_OP_MASK) != A1_OP_TYPE_DECORATE)
r = rk_strpoolprintf(r, ",%s\"%s\":",
indents ? indents : "",
(const char *)(tnames++)->ptr);
diff --git a/third_party/heimdal/lib/base/heimbase.h b/third_party/heimdal/lib/base/heimbase.h
index 3706fc8710d..5d5c4e3b75e 100644
--- a/third_party/heimdal/lib/base/heimbase.h
+++ b/third_party/heimdal/lib/base/heimbase.h
@@ -134,6 +134,28 @@ typedef struct heim_config_binding heim_config_section;
* CF-like, JSON APIs
*/
+typedef enum heim_tid_enum {
+ HEIM_TID_NUMBER = 0,
+ HEIM_TID_NULL = 1,
+ HEIM_TID_BOOL = 2,
+ HEIM_TID_TAGGED_UNUSED2 = 3, /* reserved for tagged object types */
+ HEIM_TID_TAGGED_UNUSED3 = 4, /* reserved for tagged object types */
+ HEIM_TID_TAGGED_UNUSED4 = 5, /* reserved for tagged object types */
+ HEIM_TID_TAGGED_UNUSED5 = 6, /* reserved for tagged object types */
+ HEIM_TID_TAGGED_UNUSED6 = 7, /* reserved for tagged object types */
+ HEIM_TID_MEMORY = 128,
+ HEIM_TID_ARRAY = 129,
+ HEIM_TID_DICT = 130,
+ HEIM_TID_STRING = 131,
+ HEIM_TID_AUTORELEASE = 132,
+ HEIM_TID_ERROR = 133,
+ HEIM_TID_DATA = 134,
+ HEIM_TID_DB = 135,
+ HEIM_TID_PA_AUTH_MECH = 136,
+ HEIM_TID_PAC = 137,
+ HEIM_TID_USER = 255
+} heim_tid;
+
typedef void * heim_object_t;
typedef unsigned int heim_tid_t;
typedef heim_object_t heim_bool_t;
@@ -463,7 +485,13 @@ typedef enum heim_json_flags {
HEIM_JSON_F_STRICT = 31,
HEIM_JSON_F_CNULL2JSNULL = 32,
HEIM_JSON_F_TRY_DECODE_DATA = 64,
- HEIM_JSON_F_ONE_LINE = 128
+ HEIM_JSON_F_ONE_LINE = 128,
+ HEIM_JSON_F_ESCAPE_NON_ASCII = 256,
+ HEIM_JSON_F_NO_ESCAPE_NON_ASCII = 512,
+ /* The default is to indent with one tab */
+ HEIM_JSON_F_INDENT2 = 1024,
+ HEIM_JSON_F_INDENT4 = 2048,
+ HEIM_JSON_F_INDENT8 = 4096,
} heim_json_flags_t;
heim_object_t heim_json_create(const char *, size_t, heim_json_flags_t,
diff --git a/third_party/heimdal/lib/base/heimbasepriv.h b/third_party/heimdal/lib/base/heimbasepriv.h
index b9f63e56b6a..45ffb12d70e 100644
--- a/third_party/heimdal/lib/base/heimbasepriv.h
+++ b/third_party/heimdal/lib/base/heimbasepriv.h
@@ -47,29 +47,6 @@ typedef heim_string_t (*heim_type_description)(void *);
typedef struct heim_type_data *heim_type_t;
-enum {
- HEIM_TID_NUMBER = 0,
- HEIM_TID_NULL = 1,
- HEIM_TID_BOOL = 2,
- HEIM_TID_TAGGED_UNUSED2 = 3, /* reserved for tagged object types */
- HEIM_TID_TAGGED_UNUSED3 = 4, /* reserved for tagged object types */
- HEIM_TID_TAGGED_UNUSED4 = 5, /* reserved for tagged object types */
- HEIM_TID_TAGGED_UNUSED5 = 6, /* reserved for tagged object types */
- HEIM_TID_TAGGED_UNUSED6 = 7, /* reserved for tagged object types */
- HEIM_TID_MEMORY = 128,
- HEIM_TID_ARRAY = 129,
- HEIM_TID_DICT = 130,
- HEIM_TID_STRING = 131,
- HEIM_TID_AUTORELEASE = 132,
- HEIM_TID_ERROR = 133,
- HEIM_TID_DATA = 134,
- HEIM_TID_DB = 135,
- HEIM_TID_PA_AUTH_MECH = 136,
- HEIM_TID_PAC = 137,
- HEIM_TID_USER = 255
-
-};
-
struct heim_type_data {
heim_tid_t tid;
const char *name;
diff --git a/third_party/heimdal/lib/base/json.c b/third_party/heimdal/lib/base/json.c
index 2ef371b975e..c7ef0658850 100644
--- a/third_party/heimdal/lib/base/json.c
+++ b/third_party/heimdal/lib/base/json.c
@@ -37,10 +37,12 @@
#include <ctype.h>
#include <base64.h>
+#ifndef WIN32
+#include <langinfo.h>
+#endif
+
static heim_base_once_t heim_json_once = HEIM_BASE_ONCE_INIT;
static heim_string_t heim_tid_data_uuid_key = NULL;
-static const char base64_chars[] =
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static void
json_init_once(void *arg)
@@ -66,7 +68,7 @@ struct heim_strbuf {
};
static int
-base2json(heim_object_t, struct twojson *);
+base2json(heim_object_t, struct twojson *, int);
static void
indent(struct twojson *j)
@@ -74,8 +76,18 @@ indent(struct twojson *j)
size_t i = j->indent;
if (j->flags & HEIM_JSON_F_ONE_LINE)
return;
- while (i--)
- j->out(j->ctx, "\t");
+ if (j->flags & HEIM_JSON_F_INDENT2)
+ while (i--)
+ j->out(j->ctx, " ");
+ else if (j->flags & HEIM_JSON_F_INDENT4)
+ while (i--)
+ j->out(j->ctx, " ");
+ else if (j->flags & HEIM_JSON_F_INDENT8)
+ while (i--)
+ j->out(j->ctx, " ");
+ else
+ while (i--)
+ j->out(j->ctx, "\t");
}
static void
@@ -90,7 +102,7 @@ array2json(heim_object_t value, void *ctx, int *stop)
j->out(j->ctx, NULL); /* eat previous '\n' if possible */
j->out(j->ctx, ",\n");
}
- j->ret = base2json(value, j);
+ j->ret = base2json(value, j, 0);
}
static void
@@ -105,19 +117,77 @@ dict2json(heim_object_t key, heim_object_t value, void *ctx)
j->out(j->ctx, NULL); /* eat previous '\n' if possible */
j->out(j->ctx, ",\n");
}
- j->ret = base2json(key, j);
- if (j->ret)
- return;
- j->out(j->ctx, " : \n");
- j->indent++;
- j->ret = base2json(value, j);
+ j->ret = base2json(key, j, 0);
if (j->ret)
return;
- j->indent--;
+ switch (heim_get_tid(value)) {
+ case HEIM_TID_ARRAY:
+ case HEIM_TID_DICT:
+ case HEIM_TID_DATA:
+ j->out(j->ctx, ":\n");
+ j->indent++;
+ j->ret = base2json(value, j, 0);
+ if (j->ret)
+ return;
+ j->indent--;
+ break;
+ default:
+ j->out(j->ctx, ": ");
+ j->ret = base2json(value, j, 1);
+ break;
+ }
+}
+
+#ifndef WIN32
+static void
+init_is_utf8(void *ptr)
+{
+ *(int *)ptr = strcasecmp("utf-8", nl_langinfo(CODESET)) == 0;
+}
+#endif
+
+int
+heim_locale_is_utf8(void)
+{
+#ifdef WIN32
+ return 0; /* XXX Implement */
+#else
+ static int locale_is_utf8 = -1;
+ static heim_base_once_t once = HEIM_BASE_ONCE_INIT;
+
+ heim_base_once_f(&once, &locale_is_utf8, init_is_utf8);
+ return locale_is_utf8;
+#endif
+}
+
+static void
+out_escaped_bmp(struct twojson *j, const unsigned char *p, int nbytes)
+{
+ unsigned char e[sizeof("\\u0000")];
+ unsigned codepoint;
+
+ if (nbytes == 2)
+ codepoint = ((p[0] & 0x1f) << 6) | (p[1] & 0x3f);
+ else if (nbytes == 3)
+ codepoint = ((p[0] & 0x0f) << 12) | ((p[1] & 0x3f) << 6) | (p[2] & 0x3f);
+ else
+ abort();
+ e[0] = '\\';
+ e[1] = 'u';
+ e[2] = codepoint >> 12;
+ e[2] += (e[2] < 10) ? '0' : ('A' - 10);
+ e[3] = (codepoint >> 8) & 0x0f;
+ e[3] += (e[3] < 10) ? '0' : ('A' - 10);
+ e[4] = (codepoint >> 4) & 0x0f;
+ e[4] += (e[4] < 10) ? '0' : ('A' - 10);
+ e[5] = codepoint & 0x0f;
+ e[5] += (e[5] < 10) ? '0' : ('A' - 10);
+ e[6] = '\0';
+ j->out(j->ctx, (char *)e);
}
static int
-base2json(heim_object_t obj, struct twojson *j)
+base2json(heim_object_t obj, struct twojson *j, int skip_indent)
{
heim_tid_t type;
int first = 0;
@@ -166,12 +236,186 @@ base2json(heim_object_t obj, struct twojson *j)
j->first = first;
break;
- case HEIM_TID_STRING:
- indent(j);
+ case HEIM_TID_STRING: {
+ const unsigned char *s = (const unsigned char *)heim_string_get_utf8(obj);
+ const unsigned char *p;
+ unsigned int c, cp, ctop, cbot;
+ char e[sizeof("\\u0123\\u3210")];
+ int good;
+ size_t i;
+
+ if (!skip_indent)
+ indent(j);
j->out(j->ctx, "\"");
- j->out(j->ctx, heim_string_get_utf8(obj));
+ for (p = s; (c = *p); p++) {
+ switch (c) {
+ /* ASCII control characters w/ C-like escapes */
+ case '\b': j->out(j->ctx, "\\b"); continue;
+ case '\f': j->out(j->ctx, "\\f"); continue;
+ case '\n': j->out(j->ctx, "\\n"); continue;
+ case '\r': j->out(j->ctx, "\\r"); continue;
+ case '\t': j->out(j->ctx, "\\t"); continue;
+ /* Other must-escape non-control ASCII characters */
+ case '"': j->out(j->ctx, "\\\""); continue;
+ case '\\': j->out(j->ctx, "\\\\"); continue;
+ default: break;
+ }
+
+ /*
+ * JSON string encoding is... complex.
+ *
+ * Invalid UTF-8 w/ HEIM_JSON_F_STRICT_STRINGS set -> return 1
+ *
+ * Invalid UTF-8 w/o HEIM_JSON_F_STRICT_STRINGS set -> pass
+ * through, a sort of Heimdal WTF-8, but not _the_ WTF-8.
+ */
+ if (c < 0x20) {
+ /* ASCII control character w/o C-like escape */
+ e[0] = '\\';
+ e[1] = 'u';
+ e[2] = '0';
+ e[3] = '0';
+ e[4] = "0123456789ABCDEF"[c>>4];
+ e[5] = "0123456789ABCDEF"[c & 0x0f];
+ e[6] = '\0';
+ j->out(j->ctx, e);
+ continue;
+ }
+ if (c < 0x80) {
+ /* ASCII */
+ e[0] = c;
+ e[1] = '\0';
+ j->out(j->ctx, e);
+ continue;
+ }
+ if ((c & 0xc0) == 0x80) {
+ /* UTF-8 bare non-leading byte */
+ if (!(j->flags & HEIM_JSON_F_STRICT_STRINGS)) {
+ e[0] = c;
+ e[1] = '\0';
+ j->out(j->ctx, e);
+ continue;
+ }
+ return 1;
+ }
+ if ((c & 0xe0) == 0xc0) {
+ /* UTF-8 leading byte of two-byte sequence */
+ good = 1;
+ for (i = 1; i < 2 && good && p[i]; i++) {
+ if ((p[i] & 0xc0) != 0x80)
+ good = 0;
+ }
+ if (i != 2)
+ good = 0;
+ if (!good && !(j->flags & HEIM_JSON_F_STRICT_STRINGS)) {
+ e[0] = c;
+ e[1] = '\0';
+ j->out(j->ctx, e);
+ continue;
+ } else if (!good) {
+ return 1;
+ }
+ if (j->flags & HEIM_JSON_F_ESCAPE_NON_ASCII) {
+ out_escaped_bmp(j, p, 2);
+ p += 1;
+ continue;
+ }
+ e[0] = c;
+ e[1] = p[1];
+ e[2] = '\0';
+ j->out(j->ctx, e);
+ p += 1;
+ continue;
+ }
+ if ((c & 0xf0) == 0xe0) {
+ /* UTF-8 leading byte of three-byte sequence */
+ good = 1;
+ for (i = 1; i < 3 && good && p[i]; i++) {
+ if ((p[i] & 0xc0) != 0x80)
+ good = 0;
+ }
+ if (i != 3)
+ good = 0;
+ if (!good && !(j->flags & HEIM_JSON_F_STRICT_STRINGS)) {
+ e[0] = c;
+ e[1] = '\0';
+ j->out(j->ctx, e);
+ continue;
+ } else if (!good) {
+ return 1;
+ }
+ if (j->flags & HEIM_JSON_F_ESCAPE_NON_ASCII) {
+ out_escaped_bmp(j, p, 3);
+ p += 2;
+ continue;
+ }
+ e[0] = c;
+ e[1] = p[1];
+ e[2] = p[2];
+ e[3] = '\0';
+ j->out(j->ctx, e);
+ p += 2;
+ continue;
+ }
+
+ if (c > 0xf7) {
+ /* Invalid UTF-8 leading byte */
+ if (!(j->flags & HEIM_JSON_F_STRICT_STRINGS)) {
+ e[0] = c;
+ e[1] = '\0';
+ j->out(j->ctx, e);
+ continue;
+ }
+ return 1;
+ }
+
+ /*
+ * A codepoint > U+FFFF, needs encoding a la UTF-16 surrogate
+ * pair because JSON takes after JS which uses UTF-16. Ugly.
+ */
+ cp = c & 0x7;
+ good = 1;
+ for (i = 1; i < 4 && good && p[i]; i++) {
+ if ((p[i] & 0xc0) == 0x80)
+ cp = (cp << 6) | (p[i] & 0x3f);
+ else
+ good = 0;
+ }
+ if (i != 4)
+ good = 0;
+ if (!good && !(j->flags & HEIM_JSON_F_STRICT_STRINGS)) {
+ e[0] = c;
+ e[1] = '\0';
+ j->out(j->ctx, e);
+ continue;
+ } else if (!good) {
+ return 1;
+ }
+ p += 3;
+
+ cp -= 0x10000;
+ ctop = 0xD800 + (cp >> 10);
+ cbot = 0xDC00 + (cp & 0x3ff);
+
+ e[0 ] = '\\';
+ e[1 ] = 'u';
+ e[2 ] = "0123456789ABCDEF"[(ctop ) >> 12];
+ e[3 ] = "0123456789ABCDEF"[(ctop & 0x0f00) >> 8];
+ e[4 ] = "0123456789ABCDEF"[(ctop & 0x00f0) >> 4];
+ e[5 ] = "0123456789ABCDEF"[(ctop & 0x000f) ];
+ e[6 ] = '\\';
+ e[7 ] = 'u';
+ e[8 ] = "0123456789ABCDEF"[(cbot ) >> 12];
+ e[9 ] = "0123456789ABCDEF"[(cbot & 0x0f00) >> 8];
+ e[10] = "0123456789ABCDEF"[(cbot & 0x00f0) >> 4];
+ e[11] = "0123456789ABCDEF"[(cbot & 0x000f) ];
+ e[12] = '\0';
+ j->out(j->ctx, e);
+ continue;
+ }
j->out(j->ctx, "\"");
break;
+ }
case HEIM_TID_DATA: {
heim_dict_t d;
@@ -220,7 +464,7 @@ base2json(heim_object_t obj, struct twojson *j)
heim_release(d);
return ENOMEM;
}
- ret = base2json(d, j);
+ ret = base2json(d, j, 0);
heim_release(d);
if (ret)
return ret;
@@ -230,17 +474,20 @@ base2json(heim_object_t obj, struct twojson *j)
case HEIM_TID_NUMBER: {
char num[32];
- indent(j);
+ if (!skip_indent)
+ indent(j);
snprintf(num, sizeof (num), "%d", heim_number_get_int(obj));
j->out(j->ctx, num);
break;
}
case HEIM_TID_NULL:
- indent(j);
+ if (!skip_indent)
+ indent(j);
j->out(j->ctx, "null");
break;
case HEIM_TID_BOOL:
- indent(j);
+ if (!skip_indent)
+ indent(j);
j->out(j->ctx, heim_bool_val(obj) ? "true" : "false");
break;
default:
@@ -255,9 +502,6 @@ heim_base2json(heim_object_t obj, void *ctx, heim_json_flags_t flags,
{
struct twojson j;
- if (flags & HEIM_JSON_F_STRICT_STRINGS)
- return ENOTSUP; /* Sorry, not yet! */
-
heim_base_once_f(&heim_json_once, NULL, json_init_once);
j.indent = 0;
@@ -267,7 +511,11 @@ heim_base2json(heim_object_t obj, void *ctx, heim_json_flags_t flags,
j.ret = 0;
j.first = 1;
- return base2json(obj, &j);
+ if (!(flags & HEIM_JSON_F_NO_ESCAPE_NON_ASCII) &&
+ !heim_locale_is_utf8())
+ j.flags |= HEIM_JSON_F_ESCAPE_NON_ASCII;
+
+ return base2json(obj, &j, 0);
}
@@ -342,93 +590,425 @@ parse_number(struct parse_ctx *ctx)
return heim_number_create(number * neg);
}
+/*
+ * Read 4 hex digits from ctx->p.
+ *
+ * If we don't have enough, rewind ctx->p and return -1 .
+ */
+static int
+unescape_unicode(struct parse_ctx *ctx)
+{
+ int c = 0;
+ int i;
+
+ for (i = 0; i < 4 && ctx->p < ctx->pend; i++, ctx->p++) {
+ if (*ctx->p >= '0' && *ctx->p <= '9') {
+ c = (c << 4) + (*ctx->p - '0');
+ } else if (*ctx->p >= 'A' && *ctx->p <= 'F') {
+ c = (c << 4) + (10 + *ctx->p - 'A');
+ } else if (*ctx->p >= 'a' && *ctx->p <= 'f') {
+ c = (c << 4) + (10 + *ctx->p - 'a');
+ } else {
+ ctx->p -= i;
+ return -1;
+ }
+ }
+ return c;
+}
+
+static int
+encode_utf8(struct parse_ctx *ctx, char **pp, char *pend, int c)
+{
+ char *p = *pp;
+
+ if (c < 0x80) {
+ /* ASCII */
+ if (p >= pend) return 0;
+ *(p++) = c;
+ *pp = p;
+ return 1;
+ }
+ if (c < 0x800) {
+ /* 2 code unit UTF-8 sequence */
+ if (p >= pend) return 0;
+ *(p++) = 0xc0 | ((c >> 6) );
+ if (p == pend) return 0;
+ *(p++) = 0x80 | ((c ) & 0x3f);
+ *pp = p;
+ return 1;
+ }
+ if (c < 0x10000) {
+ /* 3 code unit UTF-8 sequence */
+ if (p >= pend) return 0;
+ *(p++) = 0xe0 | ((c >> 12) );
+ if (p == pend) return 0;
+ *(p++) = 0x80 | ((c >> 6) & 0x3f);
+ if (p == pend) return 0;
+ *(p++) = 0x80 | ((c) & 0x3f);
+ *pp = p;
+ return 1;
+ }
+ if (c < 0x110000) {
+ /* 4 code unit UTF-8 sequence */
+ if (p >= pend) return 0;
+ *(p++) = 0xf0 | ((c >> 18) );
+ if (p == pend) return 0;
+ *(p++) = 0x80 | ((c >> 12) & 0x3f);
+ if (p == pend) return 0;
+ *(p++) = 0x80 | ((c >> 6) & 0x3f);
+ if (p == pend) return 0;
+ *(p++) = 0x80 | ((c) & 0x3f);
+ *pp = p;
+ return 1;
+ }
+ return 0;
+}
+
+static heim_string_t
+parse_string_error(struct parse_ctx *ctx,
+ char *freeme,
+ const char *msg)
+{
+ free(freeme);
+ ctx->error = heim_error_create(EINVAL, "%s at %lu", msg, ctx->lineno);
+ return NULL;
+}
+
static heim_string_t
parse_string(struct parse_ctx *ctx)
{
const uint8_t *start;
- int quote = 0;
-
- if (ctx->flags & HEIM_JSON_F_STRICT_STRINGS) {
- ctx->error = heim_error_create(EINVAL, "Strict JSON string encoding "
- "not yet supported");
- return NULL;
+ heim_object_t o;
+ size_t alloc_len = 0;
+ size_t need = 0;
+ char *p0, *p, *pend;
+ int strict = ctx->flags & HEIM_JSON_F_STRICT_STRINGS;
+ int binary = 0;
+
+ if (*ctx->p != '"')
+ return parse_string_error(ctx, NULL,
+ "Expected a JSON string but found "
+ "something else");
+ start = ++(ctx->p);
+
+ /* Estimate how many bytes we need to allocate */
+ p0 = p = pend = NULL;
+ for (need = 1; ctx->p < ctx->pend; ctx->p++) {
+ need++;
+ if (*ctx->p == '\\')
+ ctx->p++;
+ else if (*ctx->p == '"')
+ break;
}
+ if (ctx->p == ctx->pend)
+ return parse_string_error(ctx, NULL, "Unterminated JSON string");
- if (*ctx->p != '"') {
- ctx->error = heim_error_create(EINVAL, "Expected a JSON string but "
- "found something else at line %lu",
- ctx->lineno);
- return NULL;
+ ctx->p = start;
+ while (ctx->p < ctx->pend) {
+ const unsigned char *p_save;
+ int32_t ctop, cbot;
+
+ if (*ctx->p == '"') {
+ ctx->p++;
+ break;
+ }
+
+ /* Allocate or resize our output buffer if need be */
+ if (need || p == pend) {
+ char *tmp;
+
+ /*
+ * Work out how far p is into p0 to re-esablish p after
+ * the realloc()
+ */
+ size_t p0_to_p_len = (p - p0);
+
+ tmp = realloc(p0, alloc_len + need + 5 /* slop? */);
+
+ if (tmp == NULL) {
+ ctx->error = heim_error_create_enomem();
+ free(p0);
+ return NULL;
+ }
+ alloc_len += need + 5;
+
+ /*
+ * We have two pointers, p and p0, we want to keep them
+ * pointing into the same memory after the realloc()
+ */
+ p = tmp + p0_to_p_len;
+ p0 = tmp;
+ pend = p0 + alloc_len;
+
+ need = 0;
+ }
+
+ if (*ctx->p != '\\') {
+ unsigned char c = *ctx->p;
+
+ /*
+ * Not backslashed -> consume now.
+ *
+ * NOTE: All cases in this block must continue or return w/ error.
+ */
+
+ /* Check for unescaped ASCII control characters */
+ if (c == '\n') {
+ if (strict)
+ return parse_string_error(ctx, p0,
+ "Unescaped newline in JSON string");
+ /* Count the newline but don't add it to the decoding */
+ ctx->lineno++;
+ } else if (strict && *ctx->p <= 0x1f) {
+ return parse_string_error(ctx, p0, "Unescaped ASCII control character");
+ } else if (c == 0) {
+ binary = 1;
+ }
+ if (!strict || c < 0x80) {
+ /* ASCII, or not strict -> no need to validate */
+ *(p++) = c;
+ ctx->p++;
+ continue;
+ }
+
+ /*
+ * Being strict for parsing means we want to detect malformed UTF-8
+ * sequences.
+ *
+ * If not strict then we just go on below and add to `p' whatever
+ * bytes we find in `ctx->p' as we find them.
+ *
+ * For each two-byte sequence we need one more byte in `p[]'. For
+ * each three-byte sequence we need two more bytes in `p[]'.
+ *
+ * Setting `need' and looping will cause `p0' to be grown.
+ *
+ * NOTE: All cases in this block must continue or return w/ error.
+ */
+ if ((c & 0xe0) == 0xc0) {
+ /* Two-byte UTF-8 encoding */
+ if (pend - p < 2) {
+ need = 2;
+ continue; /* realloc p0 */
+ }
+
+ *(p++) = c;
+ ctx->p++;
+ if (ctx->p == ctx->pend)
+ return parse_string_error(ctx, p0, "Truncated UTF-8");
+ c = *(ctx->p++);
+ if ((c & 0xc0) != 0x80)
+ return parse_string_error(ctx, p0, "Truncated UTF-8");
+ *(p++) = c;
+ continue;
+ }
+ if ((c & 0xf0) == 0xe0) {
+ /* Three-byte UTF-8 encoding */
+ if (pend - p < 3) {
+ need = 3;
+ continue; /* realloc p0 */
+ }
+
+ *(p++) = c;
+ ctx->p++;
+ if (ctx->p == ctx->pend)
+ return parse_string_error(ctx, p0, "Truncated UTF-8");
+ c = *(ctx->p++);
+ if ((c & 0xc0) != 0x80)
+ return parse_string_error(ctx, p0, "Truncated UTF-8");
+ *(p++) = c;
+ c = *(ctx->p++);
+ if ((c & 0xc0) != 0x80)
+ return parse_string_error(ctx, p0, "Truncated UTF-8");
+ *(p++) = c;
+ continue;
+ }
+ if ((c & 0xf8) == 0xf0)
+ return parse_string_error(ctx, p0, "UTF-8 sequence not "
+ "encoded as escaped UTF-16");
+ if ((c & 0xc0) == 0x80)
+ return parse_string_error(ctx, p0,
+ "Invalid UTF-8 "
+ "(bare continuation code unit)");
+
+ return parse_string_error(ctx, p0, "Not UTF-8");
+ }
+
+ /* Backslash-quoted character */
+ ctx->p++;
+ if (ctx->p == ctx->pend) {
+ ctx->error =
+ heim_error_create(EINVAL,
+ "Unterminated JSON string at line %lu",
+ ctx->lineno);
+ free(p0);
+ return NULL;
+ }
+ switch (*ctx->p) {
+ /* Simple escapes */
+ case 'b': *(p++) = '\b'; ctx->p++; continue;
+ case 'f': *(p++) = '\f'; ctx->p++; continue;
+ case 'n': *(p++) = '\n'; ctx->p++; continue;
+ case 'r': *(p++) = '\r'; ctx->p++; continue;
+ case 't': *(p++) = '\t'; ctx->p++; continue;
+ case '"': *(p++) = '"'; ctx->p++; continue;
+ case '\\': *(p++) = '\\'; ctx->p++; continue;
+ /* Escaped Unicode handled below */
+ case 'u':
+ /*
+ * Worst case for !strict we need 11 bytes for a truncated non-BMP
+ * codepoint escape. Call it 12.
+ */
+ if (strict)
+ need = 4;
+ else
+ need = 12;
+ if (pend - p < need) {
+ /* Go back to the backslash, realloc, try again */
+ ctx->p--;
+ continue;
+ }
+
+ need = 0;
+ ctx->p++;
+ break;
+ default:
+ if (!strict) {
+ *(p++) = *ctx->p;
+ ctx->p++;
+ continue;
+ }
+ ctx->error =
+ heim_error_create(EINVAL,
+ "Invalid backslash escape at line %lu",
+ ctx->lineno);
+ free(p0);
+ return NULL;
+ }
+
+ /* Unicode code point */
+ if (pend - p < 12) {
+ need = 12;
+ ctx->p -= 2; /* for "\\u" */
+ continue; /* This will cause p0 to be realloc'ed */
+ }
+ p_save = ctx->p;
+ ctop = cbot = -3;
+ ctop = unescape_unicode(ctx);
+ if (ctop == -1 && strict)
+ return parse_string_error(ctx, p0, "Invalid escaped Unicode");
+ if (ctop == -1) {
+ /*
+ * Not strict; tolerate bad input.
+ *
+ * Output "\\u" and then loop to treat what we expected to be four
+ * digits as if they were not part of an escaped Unicode codepoint.
+ */
+ ctx->p = p_save;
+ if (p < pend)
+ *(p++) = '\\';
+ if (p < pend)
+ *(p++) = 'u';
+ continue;
+ }
+ if (ctop == 0) {
+ *(p++) = '\0';
+ binary = 1;
+ continue;
+ }
+ if (ctop < 0xd800) {
+ if (!encode_utf8(ctx, &p, pend, ctop))
+ return parse_string_error(ctx, p0,
+ "Internal JSON string parse error");
+ continue;
+ }
+
+ /*
+ * We parsed the top escaped codepoint of a surrogate pair encoding
+ * of a non-BMP Unicode codepoint. What follows must be another
+ * escaped codepoint.
+ */
+ if (ctx->p < ctx->pend && ctx->p[0] == '\\')
+ ctx->p++;
+ else
+ ctop = -1;
+ if (ctop > -1 && ctx->p < ctx->pend && ctx->p[0] == 'u')
+ ctx->p++;
+ else
+ ctop = -1;
+ if (ctop > -1) {
+ /* Parse the hex digits of the bottom half of the surrogate pair */
+ cbot = unescape_unicode(ctx);
+ if (cbot == -1 || cbot < 0xdc00)
+ ctop = -1;
+ }
+ if (ctop == -1) {
+ if (strict)
+ return parse_string_error(ctx, p0,
+ "Invalid surrogate pair");
+
+ /*
+ * Output "\\u", rewind, output the digits of `ctop'.
+ *
+ * When we get to what should have been the bottom half of the
+ * pair we'll necessarily fail to parse it as a normal escaped
+ * Unicode codepoint, and once again, rewind and output its digits.
+ */
+ if (p < pend)
+ *(p++) = '\\';
+ if (p < pend)
+ *(p++) = 'u';
+ ctx->p = p_save;
+ continue;
+ }
+
+ /* Finally decode the surrogate pair then encode as UTF-8 */
+ ctop -= 0xd800;
+ cbot -= 0xdc00;
+ if (!encode_utf8(ctx, &p, pend, 0x10000 + ((ctop << 10) | (cbot & 0x3ff))))
+ return parse_string_error(ctx, p0,
+ "Internal JSON string parse error");
}
- start = ++ctx->p;
- while (ctx->p < ctx->pend) {
- if (*ctx->p == '\n') {
- ctx->lineno++;
- } else if (*ctx->p == '\\') {
- if (ctx->p + 1 == ctx->pend)
- goto out;
- ctx->p++;
- quote = 1;
- } else if (*ctx->p == '"') {
- heim_object_t o;
-
- if (quote) {
- char *p0, *p;
- p = p0 = malloc(ctx->p - start);
- if (p == NULL)
- goto out;
- while (start < ctx->p) {
- if (*start == '\\') {
- start++;
- /* XXX validate quoted char */
- }
- *p++ = *start++;
- }
- o = heim_string_create_with_bytes(p0, p - p0);
- free(p0);
- } else {
- o = heim_string_create_with_bytes(start, ctx->p - start);
- if (o == NULL) {
- ctx->error = heim_error_create_enomem();
- return NULL;
- }
-
- /* If we can decode as base64, then let's */
- if (ctx->flags & HEIM_JSON_F_TRY_DECODE_DATA) {
- void *buf;
- size_t len;
- const char *s;
-
- s = heim_string_get_utf8(o);
- len = strlen(s);
-
- if (len >= 4 && strspn(s, base64_chars) >= len - 2) {
- buf = malloc(len);
- if (buf == NULL) {
- heim_release(o);
- ctx->error = heim_error_create_enomem();
- return NULL;
- }
- len = rk_base64_decode(s, buf);
- if (len == -1) {
- free(buf);
- return o;
- }
- heim_release(o);
- o = heim_data_ref_create(buf, len, free);
- }
- }
- }
- ctx->p += 1;
+ if (p0 == NULL)
+ return heim_string_create("");
- return o;
- }
- ctx->p += 1;
+ /* NUL-terminate for rk_base64_decode() and plain paranoia */
+ if (p0 != NULL && p == pend) {
+ /*
+ * Work out how far p is into p0 to re-esablish p after
+ * the realloc()
+ */
+ size_t p0_to_pend_len = (pend - p0);
+ char *tmp = realloc(p0, 1 + p0_to_pend_len);
+
+ if (tmp == NULL) {
+ ctx->error = heim_error_create_enomem();
+ free(p0);
+ return NULL;
+ }
+ /*
+ * We have three pointers, p, pend (which are the same)
+ * and p0, we want to keep them pointing into the same
+ * memory after the realloc()
+ */
+ p = tmp + p0_to_pend_len;
+
+ pend = p + 1;
+ p0 = tmp;
}
- out:
- ctx->error = heim_error_create(EINVAL, "ran out of string");
- return NULL;
+ *(p++) = '\0';
+
+ /* If there's embedded NULs, it's not a C string */
+ if (binary) {
+ o = heim_data_ref_create(p0, (p - 1) - p0, free);
+ return o;
+ }
+
+ /* Sadly this will copy `p0' */
+ o = heim_string_create_with_bytes(p0, p - p0);
+ free(p0);
+ return o;
}
static int
@@ -809,3 +1389,83 @@ heim_json_copy_serialize(heim_object_t obj, heim_json_flags_t flags, heim_error_
}
return str;
}
+
+struct heim_eq_f_ctx {
+ heim_dict_t other;
+ int ret;
+};
+
+static void
+heim_eq_dict_iter_f(heim_object_t key, heim_object_t val, void *d)
+{
+ struct heim_eq_f_ctx *ctx = d;
+ heim_object_t other_val;
+
+ if (!ctx->ret)
+ return;
+
+ /*
+ * This doesn't work if the key is an array or a dict, which, anyways,
+ * isn't allowed in JSON, though we allow it.
+ */
+ other_val = heim_dict_get_value(ctx->other, key);
+ ctx->ret = heim_json_eq(val, other_val);
+}
+
+int
+heim_json_eq(heim_object_t a, heim_object_t b)
+{
+ heim_tid_t atid, btid;
+
+ if (a == b)
+ return 1;
+ if (a == NULL || b == NULL)
+ return 0;
+ atid = heim_get_tid(a);
+ btid = heim_get_tid(b);
+ if (atid != btid)
+ return 0;
+ switch (atid) {
+ case HEIM_TID_ARRAY: {
+ size_t len = heim_array_get_length(b);
+ size_t i;
+
+ if (heim_array_get_length(a) != len)
+ return 0;
+ for (i = 0; i < len; i++) {
+ if (!heim_json_eq(heim_array_get_value(a, i),
+ heim_array_get_value(b, i)))
+ return 0;
+ }
+ return 1;
+ }
+ case HEIM_TID_DICT: {
+ struct heim_eq_f_ctx ctx;
+
+ ctx.other = b;
+ ctx.ret = 1;
+ heim_dict_iterate_f(a, &ctx, heim_eq_dict_iter_f);
+
+ if (ctx.ret) {
+ ctx.other = a;
+ heim_dict_iterate_f(b, &ctx, heim_eq_dict_iter_f);
+ }
+ return ctx.ret;
+ }
+ case HEIM_TID_STRING:
+ return strcmp(heim_string_get_utf8(a), heim_string_get_utf8(b)) == 0;
+ case HEIM_TID_DATA: {
+ return heim_data_get_length(a) == heim_data_get_length(b) &&
+ memcmp(heim_data_get_ptr(a), heim_data_get_ptr(b),
+ heim_data_get_length(a)) == 0;
+ }
+ case HEIM_TID_NUMBER:
+ return heim_number_get_long(a) == heim_number_get_long(b);
+ case HEIM_TID_NULL:
+ case HEIM_TID_BOOL:
+ return heim_bool_val(a) == heim_bool_val(b);
+ default:
+ break;
+ }
+ return 0;
+}
diff --git a/third_party/heimdal/lib/base/log.c b/third_party/heimdal/lib/base/log.c
index 24295b5adbc..1d79c7e45b9 100644
--- a/third_party/heimdal/lib/base/log.c
+++ b/third_party/heimdal/lib/base/log.c
@@ -1051,7 +1051,8 @@ heim_audit_trail(heim_svc_req_desc r, heim_error_code ret, const char *retname)
break;
default:
/* Wish we had a com_err number->symbolic name function */
- (void) snprintf(retvalbuf, sizeof(retvalbuf), "UNKNOWN-%d", ret);
+ (void) snprintf(retvalbuf, sizeof(retvalbuf), "UNKNOWN-%d",
+ ret ? ret : r->error_code);
retval = retvalbuf;
break;
}
diff --git a/third_party/heimdal/lib/base/string.c b/third_party/heimdal/lib/base/string.c
index f942447163d..5e79e00b18c 100644
--- a/third_party/heimdal/lib/base/string.c
+++ b/third_party/heimdal/lib/base/string.c
@@ -153,7 +153,8 @@ heim_string_create_with_bytes(const void *data, size_t len)
s = _heim_alloc_object(&_heim_string_object, len + 1);
if (s) {
- memcpy(s, data, len);
+ if (len)
+ memcpy(s, data, len);
((char *)s)[len] = '\0';
}
return s;
@@ -238,7 +239,7 @@ heim_string_t
__heim_string_constant(const char *_str)
{
static HEIMDAL_MUTEX mutex = HEIMDAL_MUTEX_INITIALIZER;
- static heim_base_once_t once;
+ static heim_base_once_t once = HEIM_BASE_ONCE_INIT;
static heim_dict_t dict = NULL;
heim_string_t s, s2;
diff --git a/third_party/heimdal/lib/base/test_base.c b/third_party/heimdal/lib/base/test_base.c
index be6c860e26b..6d0668663af 100644
--- a/third_party/heimdal/lib/base/test_base.c
+++ b/third_party/heimdal/lib/base/test_base.c
@@ -53,6 +53,7 @@
#include <sys/stat.h>
#ifndef WIN32
#include <sys/file.h>
+#include <locale.h>
#endif
#ifdef HAVE_IO_H
#include <io.h>
@@ -244,26 +245,318 @@ test_json(void)
};
char *s;
size_t i, k;
- heim_object_t o, o2;
+ heim_object_t o, o2, o3;
heim_string_t k1 = heim_string_create("k1");
o = heim_json_create("\"string\"", 10, 0, NULL);
heim_assert(o != NULL, "string");
heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid");
heim_assert(strcmp("string", heim_string_get_utf8(o)) == 0, "wrong string");
+ o2 = heim_json_copy_serialize(o, 0, NULL);
+ o3 = heim_json_create(heim_string_get_utf8(o2), 10, 0, NULL);
+ heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
+ heim_release(o3);
+ heim_release(o2);
+ heim_release(o);
+
+ /*
+ * Test string escaping:
+ *
+ * - C-like must-escapes
+ * - ASCII control character must-escapes
+ * - surrogate pairs
+ *
+ * We test round-tripping. First we parse, then we serialize, then parse,
+ * then compare the second parse to the first for equality.
+ *
+ * We do compare serialized forms in spite of their not being canonical.
+ * That means that some changes to serialization can cause failures here.
+ */
+ o = heim_json_create("\""
+ "\\b\\f\\n\\r\\t" /* ASCII C-like escapes */
+ "\x1e" /* ASCII control character w/o C-like escape */
+ "\\u00e1" /* &aacute; */
+ "\\u07ff"
+ "\\u0801"
+ "\\u8001"
+ "\\uD834\\udd1e" /* U+1D11E, as shown in RFC 7159 */
+ "\"", 10, 0, NULL);
+ heim_assert(o != NULL, "string");
+ heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid");
+ heim_assert(strcmp(
+ "\b\f\n\r\t"
+ "\x1e"
+ "\xc3\xa1"
+ "\xdf\xbf"
+ "\xe0\xA0\x81"
+ "\xe8\x80\x81"
+ "\xf0\x9d\x84\x9e", heim_string_get_utf8(o)) == 0, "wrong string");
+ o2 = heim_json_copy_serialize(o,
+ HEIM_JSON_F_STRICT |
+ HEIM_JSON_F_NO_ESCAPE_NON_ASCII, NULL);
+ heim_assert(strcmp("\"\\b\\f\\n\\r\\t\\u001Eá߿ࠁ老\\uD834\\uDD1E\"",
+ heim_string_get_utf8(o2)) == 0,
+ "JSON encoding changed; please check that it is till valid");
+ o3 = heim_json_create(heim_string_get_utf8(o2), 10, HEIM_JSON_F_STRICT, NULL);
+ heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
+ heim_release(o3);
+ heim_release(o2);
+ heim_release(o);
+
+ o = heim_json_create("\""
+ "\\b\\f\\n\\r\\t" /* ASCII C-like escapes */
+ "\x1e" /* ASCII control character w/o C-like escape */
+ "\xc3\xa1"
+ "\xdf\xbf"
+ "\xe0\xa0\x81"
+ "\xE8\x80\x81"
+ "\\uD834\\udd1e" /* U+1D11E, as shown in RFC 7159 */
+ "\"", 10, 0, NULL);
+ heim_assert(o != NULL, "string");
+ heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid");
+ heim_assert(strcmp(
+ "\b\f\n\r\t"
+ "\x1e"
+ "\xc3\xa1"
+ "\xdf\xbf"
+ "\xe0\xA0\x81"
+ "\xe8\x80\x81"
+ "\xf0\x9d\x84\x9e", heim_string_get_utf8(o)) == 0, "wrong string");
+ o2 = heim_json_copy_serialize(o,
+ HEIM_JSON_F_STRICT |
+ HEIM_JSON_F_NO_ESCAPE_NON_ASCII, NULL);
+ heim_assert(strcmp("\"\\b\\f\\n\\r\\t\\u001Eá߿ࠁ老\\uD834\\uDD1E\"",
+ heim_string_get_utf8(o2)) == 0,
+ "JSON encoding changed; please check that it is till valid");
+ o3 = heim_json_create(heim_string_get_utf8(o2), 10, HEIM_JSON_F_STRICT, NULL);
+ heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
+ heim_release(o3);
+ heim_release(o2);
+ heim_release(o);
+
+ /*
+ * Test HEIM_JSON_F_ESCAPE_NON_ASCII.
+ *
+ * Also test that we get escaped non-ASCII because we're in a not-UTF-8
+ * locale, since we setlocale(LC_ALL, "C"), so we should escape non-ASCII
+ * by default.
+ */
+ o = heim_json_create("\""
+ "\\b\\f\\n\\r\\t" /* ASCII C-like escapes */
+ "\x1e" /* ASCII control character w/o C-like escape */
+ "\xc3\xa1"
+ "\xdf\xbf"
+ "\xe0\xa0\x81"
+ "\xE8\x80\x81"
+ "\\uD834\\udd1e" /* U+1D11E, as shown in RFC 7159 */
+ "\"", 10, 0, NULL);
+ heim_assert(o != NULL, "string");
+ heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid");
+ heim_assert(strcmp(
+ "\b\f\n\r\t"
+ "\x1e"
+ "\xc3\xa1"
+ "\xdf\xbf"
+ "\xe0\xA0\x81"
+ "\xe8\x80\x81"
+ "\xf0\x9d\x84\x9e", heim_string_get_utf8(o)) == 0, "wrong string");
+ o2 = heim_json_copy_serialize(o,
+ HEIM_JSON_F_STRICT |
+ HEIM_JSON_F_ESCAPE_NON_ASCII, NULL);
+ heim_assert(strcmp("\"\\b\\f\\n\\r\\t\\u001E\\u00E1\\u07FF\\u0801\\u8001"
+ "\\uD834\\uDD1E\"",
+ heim_string_get_utf8(o2)) == 0,
+ "JSON encoding changed; please check that it is till valid");
+ heim_release(o2);
+ o2 = heim_json_copy_serialize(o, HEIM_JSON_F_STRICT, NULL);
+ heim_assert(strcmp("\"\\b\\f\\n\\r\\t\\u001E\\u00E1\\u07FF\\u0801\\u8001"
+ "\\uD834\\uDD1E\"",
+ heim_string_get_utf8(o2)) == 0,
+ "JSON encoding changed; please check that it is till valid");
+ o3 = heim_json_create(heim_string_get_utf8(o2), 10, HEIM_JSON_F_STRICT, NULL);
+ heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
+ heim_release(o3);
+ heim_release(o2);
+ heim_release(o);
+
+ /* Test rejection of unescaped ASCII control characters */
+ o = heim_json_create("\"\b\\f\"", 10, HEIM_JSON_F_STRICT, NULL);
+ heim_assert(o == NULL, "strict parse accepted bad input");
+ o = heim_json_create("\"\b\x1e\"", 10, HEIM_JSON_F_STRICT, NULL);
+ heim_assert(o == NULL, "strict parse accepted bad input");
+
+ o = heim_json_create("\"\b\\f\"", 10, 0, NULL);
+ heim_assert(o != NULL, "string");
+ heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid");
+ heim_assert(strcmp("\b\f", heim_string_get_utf8(o)) == 0, "wrong string");
+ o2 = heim_json_copy_serialize(o,
+ HEIM_JSON_F_STRICT |
+ HEIM_JSON_F_NO_ESCAPE_NON_ASCII, NULL);
+ heim_assert(strcmp("\"\\b\\f\"", heim_string_get_utf8(o2)) == 0,
+ "JSON encoding changed; please check that it is till valid");
+ o3 = heim_json_create(heim_string_get_utf8(o2), 10, HEIM_JSON_F_STRICT, NULL);
+ heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
+ heim_release(o3);
+ heim_release(o2);
+ heim_release(o);
+
+ /* Test bogus backslash escape */
+ o = heim_json_create("\""
+ "\\ "
+ "\"", 10, HEIM_JSON_F_STRICT, NULL);
+ heim_assert(o == NULL, "malformed string accepted");
+ o = heim_json_create("\""
+ "\\ "
+ "\"", 10, 0, NULL);
+ heim_assert(o != NULL, "malformed string rejected (not strict)");
+ heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid");
+ heim_assert(strcmp(" ", heim_string_get_utf8(o)) == 0, "wrong string");
+ o2 = heim_json_copy_serialize(o,
+ HEIM_JSON_F_STRICT |
+ HEIM_JSON_F_NO_ESCAPE_NON_ASCII, NULL);
+ heim_assert(strcmp("\" \"", heim_string_get_utf8(o2)) == 0,
+ "JSON encoding changed; please check that it is till valid");
+ o3 = heim_json_create(heim_string_get_utf8(o2), 10, HEIM_JSON_F_STRICT, NULL);
+ heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
+ heim_release(o3);
+ heim_release(o2);
+ heim_release(o);
+
+ /* Test truncated surrogate encoding (bottom code unit) */
+ o = heim_json_create("\""
+ "\xE8\x80\x81"
+ "\\uD834\\udd"
+ "\"", 10, HEIM_JSON_F_STRICT, NULL);
+ heim_assert(o == NULL, "malformed string accepted");
+ o = heim_json_create("\""
+ "\xE8\x80\x81"
+ "\\uD834\\udd"
+ "\"", 10, 0, NULL);
+ heim_assert(o != NULL, "malformed string rejected (not strict)");
+ heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid");
+ heim_assert(strcmp(
+ "\xe8\x80\x81"
+ "\\uD834\\udd", heim_string_get_utf8(o)) == 0, "wrong string");
+ o2 = heim_json_copy_serialize(o,
+ HEIM_JSON_F_STRICT |
+ HEIM_JSON_F_NO_ESCAPE_NON_ASCII, NULL);
+ heim_assert(strcmp("\"老\\\\uD834\\\\udd\"",
+ heim_string_get_utf8(o2)) == 0,
+ "JSON encoding changed; please check that it is till valid");
+ o3 = heim_json_create(heim_string_get_utf8(o2), 10, HEIM_JSON_F_STRICT, NULL);
+ heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
+ heim_release(o3);
+ heim_release(o2);
+ heim_release(o);
+
+ /* Test truncated surrogate encodings (top code unit) */
+ o = heim_json_create("\""
+ "\xE8\x80\x81"
+ "\\uD83"
+ "\"", 10, HEIM_JSON_F_STRICT, NULL);
+ heim_assert(o == NULL, "malformed string accepted");
+ o = heim_json_create("\""
+ "\xE8\x80\x81"
+ "\\uD83"
+ "\"", 10, 0, NULL);
+ heim_assert(o != NULL, "malformed string rejected (not strict)");
+ heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid");
+ heim_assert(strcmp(
+ "\xe8\x80\x81"
+ "\\uD83", heim_string_get_utf8(o)) == 0, "wrong string");
+ o2 = heim_json_copy_serialize(o,
+ HEIM_JSON_F_STRICT |
+ HEIM_JSON_F_NO_ESCAPE_NON_ASCII, NULL);
+ heim_assert(strcmp("\"老\\\\uD83\"",
+ heim_string_get_utf8(o2)) == 0,
+ "JSON encoding changed; please check that it is till valid");
+ o3 = heim_json_create(heim_string_get_utf8(o2), 10, HEIM_JSON_F_STRICT, NULL);
+ heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
+ heim_release(o3);
+ heim_release(o2);
heim_release(o);
+ /*
+ * Test handling of truncated UTF-8 multi-byte sequences.
+ */
+ o = heim_json_create("\""
+ "\xE8\x80"
+ "\"", 10, 0, NULL);
+ heim_assert(o != NULL, "malformed string rejected (not strict)");
+ heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid");
+ heim_assert(strcmp("\xe8\x80",
+ heim_string_get_utf8(o)) == 0, "wrong string");
+ o2 = heim_json_copy_serialize(o,
+ HEIM_JSON_F_STRICT |
+ HEIM_JSON_F_NO_ESCAPE_NON_ASCII, NULL);
+ heim_assert(o2 == NULL, "malformed string serialized");
+ o2 = heim_json_copy_serialize(o, HEIM_JSON_F_NO_ESCAPE_NON_ASCII, NULL);
+ o3 = heim_json_create(heim_string_get_utf8(o2), 10, HEIM_JSON_F_STRICT, NULL);
+ heim_assert(o3 == NULL, "malformed string accepted (not strict)");
+ o3 = heim_json_create(heim_string_get_utf8(o2), 10, 0, NULL);
+ heim_assert(strcmp("\xe8\x80",
+ heim_string_get_utf8(o3)) == 0, "wrong string");
+ heim_release(o3);
+ heim_release(o2);
+ heim_release(o);
+
+ /* Test handling of unescaped / embedded newline */
+ o = heim_json_create("\"\n\"", 10, HEIM_JSON_F_STRICT, NULL);
+ heim_assert(o == NULL, "malformed string accepted (strict)");
+ o = heim_json_create("\"\n\"", 10, 0, NULL);
+ heim_assert(o != NULL, "malformed string rejected (not strict)");
+ heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid");
+ heim_assert(strcmp("\n", heim_string_get_utf8(o)) == 0, "wrong string");
+ o2 = heim_json_copy_serialize(o, HEIM_JSON_F_STRICT, NULL);
+ heim_assert(o2 != NULL, "string not serialized");
+ o3 = heim_json_create(heim_string_get_utf8(o2), 10, HEIM_JSON_F_STRICT, NULL);
+ heim_assert(o3 != NULL, "string not accepted");
+ heim_assert(strcmp("\n", heim_string_get_utf8(o3)) == 0, "wrong string");
+ heim_release(o3);
+ heim_release(o2);
+ heim_release(o);
+
+ /* Test handling of embedded NULs (must decode as data, not string) */
+ o = heim_json_create("\"\\u0000\"", 10, HEIM_JSON_F_STRICT, NULL);
+ heim_assert(o != NULL, "string with NULs rejected");
+ heim_assert(heim_get_tid(o) == heim_data_get_type_id(), "data-tid");
+ heim_assert(heim_data_get_length(o) == 1, "wrong data length");
+ heim_assert(((const char *)heim_data_get_ptr(o))[0] == '\0',
+ "wrong data NUL");
+ o2 = heim_json_copy_serialize(o, 0, NULL);
+ heim_assert(o2 != NULL, "data not serialized");
+ heim_release(o2);
+ heim_release(o);
+
+ /*
+ * Note that the trailing ']' is not part of the JSON text (which is just a
+ * string).
+ */
o = heim_json_create(" \"foo\\\"bar\" ]", 10, 0, NULL);
heim_assert(o != NULL, "string");
heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid");
heim_assert(strcmp("foo\"bar", heim_string_get_utf8(o)) == 0, "wrong string");
+ o2 = heim_json_copy_serialize(o, 0, NULL);
+ o3 = heim_json_create(heim_string_get_utf8(o2), 10, 0, NULL);
+ heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
+ heim_release(o3);
+ heim_release(o2);
heim_release(o);
o = heim_json_create(" { \"key\" : \"value\" }", 10, 0, NULL);
heim_assert(o != NULL, "dict");
heim_assert(heim_get_tid(o) == heim_dict_get_type_id(), "dict-tid");
+ o2 = heim_json_copy_serialize(o, 0, NULL);
+ o3 = heim_json_create(heim_string_get_utf8(o2), 10, 0, NULL);
+ heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
+ heim_release(o3);
+ heim_release(o2);
heim_release(o);
+ /*
+ * heim_json_eq() can't handle dicts with dicts as keys, so we don't check
+ * for round-tripping here
+ */
o = heim_json_create("{ { \"k1\" : \"s1\", \"k2\" : \"s2\" } : \"s3\", "
"{ \"k3\" : \"s4\" } : -1 }", 10, 0, NULL);
heim_assert(o != NULL, "dict");
@@ -281,6 +574,11 @@ test_json(void)
o2 = heim_dict_copy_value(o, k1);
heim_assert(heim_get_tid(o2) == heim_string_get_type_id(), "string-tid");
heim_release(o2);
+ o2 = heim_json_copy_serialize(o, 0, NULL);
+ o3 = heim_json_create(heim_string_get_utf8(o2), 10, 0, NULL);
+ heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
+ heim_release(o3);
+ heim_release(o2);
heim_release(o);
o = heim_json_create(" { \"k1\" : { \"k2\" : \"s2\" } }", 10, 0, NULL);
@@ -289,6 +587,11 @@ test_json(void)
o2 = heim_dict_copy_value(o, k1);
heim_assert(heim_get_tid(o2) == heim_dict_get_type_id(), "dict-tid");
heim_release(o2);
+ o2 = heim_json_copy_serialize(o, 0, NULL);
+ o3 = heim_json_create(heim_string_get_utf8(o2), 10, 0, NULL);
+ heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
+ heim_release(o3);
+ heim_release(o2);
heim_release(o);
o = heim_json_create("{ \"k1\" : 1 }", 10, 0, NULL);
@@ -297,26 +600,51 @@ test_json(void)
o2 = heim_dict_copy_value(o, k1);
heim_assert(heim_get_tid(o2) == heim_number_get_type_id(), "number-tid");
heim_release(o2);
+ o2 = heim_json_copy_serialize(o, 0, NULL);
+ o3 = heim_json_create(heim_string_get_utf8(o2), 10, 0, NULL);
+ heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
+ heim_release(o3);
+ heim_release(o2);
heim_release(o);
o = heim_json_create("-10", 10, 0, NULL);
heim_assert(o != NULL, "number");
heim_assert(heim_get_tid(o) == heim_number_get_type_id(), "number-tid");
+ o2 = heim_json_copy_serialize(o, 0, NULL);
+ o3 = heim_json_create(heim_string_get_utf8(o2), 10, 0, NULL);
+ heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
+ heim_release(o3);
+ heim_release(o2);
heim_release(o);
o = heim_json_create("99", 10, 0, NULL);
heim_assert(o != NULL, "number");
heim_assert(heim_get_tid(o) == heim_number_get_type_id(), "number-tid");
+ o2 = heim_json_copy_serialize(o, 0, NULL);
+ o3 = heim_json_create(heim_string_get_utf8(o2), 10, 0, NULL);
+ heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
+ heim_release(o3);
+ heim_release(o2);
heim_release(o);
o = heim_json_create(" [ 1 ]", 10, 0, NULL);
heim_assert(o != NULL, "array");
heim_assert(heim_get_tid(o) == heim_array_get_type_id(), "array-tid");
+ o2 = heim_json_copy_serialize(o, 0, NULL);
+ o3 = heim_json_create(heim_string_get_utf8(o2), 10, 0, NULL);
+ heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
+ heim_release(o3);
+ heim_release(o2);
heim_release(o);
o = heim_json_create(" [ -1 ]", 10, 0, NULL);
heim_assert(o != NULL, "array");
heim_assert(heim_get_tid(o) == heim_array_get_type_id(), "array-tid");
+ o2 = heim_json_copy_serialize(o, 0, NULL);
+ o3 = heim_json_create(heim_string_get_utf8(o2), 10, 0, NULL);
+ heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
+ heim_release(o3);
+ heim_release(o2);
heim_release(o);
for (i = 0; i < (sizeof (j) / sizeof (j[0])); i++) {
@@ -325,6 +653,11 @@ test_json(void)
fprintf(stderr, "Failed to parse this JSON: %s\n", j[i]);
return 1;
}
+ o2 = heim_json_copy_serialize(o, 0, NULL);
+ o3 = heim_json_create(heim_string_get_utf8(o2), 10, 0, NULL);
+ heim_assert(heim_json_eq(o, o3), "JSON text did not round-trip");
+ heim_release(o3);
+ heim_release(o2);
heim_release(o);
/* Simple fuzz test */
for (k = strlen(j[i]) - 1; k > 0; k--) {
@@ -945,6 +1278,11 @@ main(int argc, char **argv)
{
int res = 0;
+#ifndef WIN32
+ setlocale(LC_ALL, "C");
+ heim_assert(!heim_locale_is_utf8(), "setlocale(LC_ALL, \"C\") failed?");
+#endif
+
res |= test_memory();
res |= test_mutex();
res |= test_rwlock();
diff --git a/third_party/heimdal/lib/base/version-script.map b/third_party/heimdal/lib/base/version-script.map
index 928e8619995..9493ee69236 100644
--- a/third_party/heimdal/lib/base/version-script.map
+++ b/third_party/heimdal/lib/base/version-script.map
@@ -146,7 +146,9 @@ HEIMDAL_BASE_1.0 {
heim_json_copy_serialize;
heim_json_create;
heim_json_create_with_bytes;
+ heim_json_eq;
heim_load_plugins;
+ heim_locale_is_utf8;
heim_log;
heim_log_msg;
_heim_make_permanent;
diff --git a/third_party/heimdal/lib/gssapi/Makefile.am b/third_party/heimdal/lib/gssapi/Makefile.am
index a69ebffb04e..3cb8437db28 100644
--- a/third_party/heimdal/lib/gssapi/Makefile.am
+++ b/third_party/heimdal/lib/gssapi/Makefile.am
@@ -264,10 +264,6 @@ dist_libgssapi_la_SOURCES = \
$(sanonsrc)
nodist_libgssapi_la_SOURCES = \
- gkrb5_err.c \
- gkrb5_err.h \
- negoex_err.c \
- negoex_err.h \
$(BUILT_SOURCES)
libgssapi_la_DEPENDENCIES = version-script.map
@@ -333,7 +329,13 @@ $(test_context_OBJECTS): $(BUILTHEADERS)
$(libgssapi_la_OBJECTS): $(srcdir)/version-script.map
-BUILT_SOURCES = $(spnego_files) $(gssapi_files)
+BUILT_SOURCES = \
+ $(spnego_files) \
+ $(gssapi_files) \
+ gkrb5_err.c \
+ gkrb5_err.h \
+ negoex_err.c \
+ negoex_err.h
$(libgssapi_la_OBJECTS): gkrb5_err.h negoex_err.h
gkrb5_err.h: $(srcdir)/krb5/gkrb5_err.et
diff --git a/third_party/heimdal/lib/gssapi/gss-token.c b/third_party/heimdal/lib/gssapi/gss-token.c
index 844fa4d3820..f3f90521c3b 100644
--- a/third_party/heimdal/lib/gssapi/gss-token.c
+++ b/third_party/heimdal/lib/gssapi/gss-token.c
@@ -235,10 +235,9 @@ write_and_free_token(gss_buffer_t out, int negotiate)
printf("\n");
if (len < inc)
inc = len;
- ret = rk_base64_encode(p, inc, &outstr);
- if (ret < 0) {
+ if (rk_base64_encode(p, inc, &outstr) < 0) {
fprintf(stderr, "Out of memory.\n");
- ret = 1;
+ ret = errno;
goto bail;
}
ret = 0;
@@ -247,6 +246,7 @@ write_and_free_token(gss_buffer_t out, int negotiate)
p += inc;
len -= inc;
} while (len > 0);
+ ret = 0;
bail:
gss_release_buffer(&min, out);
diff --git a/third_party/heimdal/lib/gssapi/krb5/8003.c b/third_party/heimdal/lib/gssapi/krb5/8003.c
index 3e213363534..bf7da11c754 100644
--- a/third_party/heimdal/lib/gssapi/krb5/8003.c
+++ b/third_party/heimdal/lib/gssapi/krb5/8003.c
@@ -259,7 +259,7 @@ _gsskrb5_verify_8003_checksum(
}
if (input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS
- && (memcmp(p, zeros, sizeof(zeros)) != 0 || client_asserted_cb)) {
+ && (ct_memcmp(p, zeros, sizeof(zeros)) != 0 || client_asserted_cb)) {
if(hash_input_chan_bindings(input_chan_bindings, hash) != 0) {
*minor_status = 0;
return GSS_S_BAD_BINDINGS;
diff --git a/third_party/heimdal/lib/gssapi/krb5/init_sec_context.c b/third_party/heimdal/lib/gssapi/krb5/init_sec_context.c
index 62b26ed7eb9..a705d03a875 100644
--- a/third_party/heimdal/lib/gssapi/krb5/init_sec_context.c
+++ b/third_party/heimdal/lib/gssapi/krb5/init_sec_context.c
@@ -932,7 +932,7 @@ OM_uint32 GSSAPI_CALLCONV _gsskrb5_init_sec_context
time_rec);
if (ret != GSS_S_COMPLETE)
break;
- fallthrough;
+ HEIM_FALLTHROUGH;
case INITIATOR_RESTART:
ret = init_auth_restart(minor_status,
cred,
diff --git a/third_party/heimdal/lib/gssapi/netlogon/crypto.c b/third_party/heimdal/lib/gssapi/netlogon/crypto.c
index 0fc8f019d64..6147eec55e0 100644
--- a/third_party/heimdal/lib/gssapi/netlogon/crypto.c
+++ b/third_party/heimdal/lib/gssapi/netlogon/crypto.c
@@ -588,7 +588,7 @@ _netlogon_unwrap_iov(OM_uint32 *minor_status,
/* [MS-NRPC] 3.3.4.2.2.10: verify signature */
_netlogon_digest(ctx, sig, iov, iov_count, checksum);
- if (memcmp(sig->Checksum, checksum, _netlogon_checksum_length(sig)) != 0)
+ if (ct_memcmp(sig->Checksum, checksum, _netlogon_checksum_length(sig)) != 0)
return GSS_S_BAD_SIG;
HEIMDAL_MUTEX_lock(&ctx->Mutex);
diff --git a/third_party/heimdal/lib/gssapi/ntlm/crypto.c b/third_party/heimdal/lib/gssapi/ntlm/crypto.c
index efa71d911dc..d1a115ff8e3 100644
--- a/third_party/heimdal/lib/gssapi/ntlm/crypto.c
+++ b/third_party/heimdal/lib/gssapi/ntlm/crypto.c
@@ -230,7 +230,7 @@ v2_verify_message(gss_buffer_t in,
if (ret)
return ret;
- if (memcmp(checksum, out, 16) != 0)
+ if (ct_memcmp(checksum, out, 16) != 0)
return GSS_S_BAD_MIC;
return GSS_S_COMPLETE;
diff --git a/third_party/heimdal/lib/hcrypto/des.c b/third_party/heimdal/lib/hcrypto/des.c
index ac174180fb9..32f87b47137 100644
--- a/third_party/heimdal/lib/hcrypto/des.c
+++ b/third_party/heimdal/lib/hcrypto/des.c
@@ -851,7 +851,7 @@ DES_string_to_key(const char *str, DES_cblock *key)
k[7] ^= 0xF0;
DES_set_key(key, &ks);
DES_cbc_cksum(s, key, len, &ks, key);
- memset(&ks, 0, sizeof(ks));
+ memset_s(&ks, sizeof(ks), 0, sizeof(ks));
DES_set_odd_parity(key);
if (DES_is_weak_key(key))
k[7] ^= 0xF0;
diff --git a/third_party/heimdal/lib/hcrypto/dh.c b/third_party/heimdal/lib/hcrypto/dh.c
index 5d2d214f752..b3b25e5186d 100644
--- a/third_party/heimdal/lib/hcrypto/dh.c
+++ b/third_party/heimdal/lib/hcrypto/dh.c
@@ -145,7 +145,7 @@ DH_free(DH *dh)
free_if(dh->counter);
#undef free_if
- memset(dh, 0, sizeof(*dh));
+ memset_s(dh, sizeof(*dh), 0, sizeof(*dh));
free(dh);
}
diff --git a/third_party/heimdal/lib/hcrypto/dsa.c b/third_party/heimdal/lib/hcrypto/dsa.c
index bbb34601c31..eac0ae6498e 100644
--- a/third_party/heimdal/lib/hcrypto/dsa.c
+++ b/third_party/heimdal/lib/hcrypto/dsa.c
@@ -70,7 +70,7 @@ DSA_free(DSA *dsa)
free_if(dsa->r);
#undef free_if
- memset(dsa, 0, sizeof(*dsa));
+ memset_s(dsa, sizeof(*dsa), 0, sizeof(*dsa));
free(dsa);
}
diff --git a/third_party/heimdal/lib/hcrypto/engine.c b/third_party/heimdal/lib/hcrypto/engine.c
index 3dae960fd0c..6a79b7c9907 100644
--- a/third_party/heimdal/lib/hcrypto/engine.c
+++ b/third_party/heimdal/lib/hcrypto/engine.c
@@ -87,7 +87,7 @@ ENGINE_finish(ENGINE *engine)
if (engine->dso_handle)
dlclose(engine->dso_handle);
- memset(engine, 0, sizeof(*engine));
+ memset_s(engine, sizeof(*engine), 0, sizeof(*engine));
engine->references = -1;
diff --git a/third_party/heimdal/lib/hcrypto/evp-openssl.c b/third_party/heimdal/lib/hcrypto/evp-openssl.c
index a651184c6ea..ca02862bf68 100644
--- a/third_party/heimdal/lib/hcrypto/evp-openssl.c
+++ b/third_party/heimdal/lib/hcrypto/evp-openssl.c
@@ -204,7 +204,7 @@ get_EVP_CIPHER_once_cb(void *d)
*/
ossl_evp = EVP_get_cipherbynid(arg->nid);
if (ossl_evp == NULL) {
- (void) memset(hc_evp, 0, sizeof(*hc_evp));
+ (void) memset_s(hc_evp, sizeof(*hc_evp), 0, sizeof(*hc_evp));
#if HCRYPTO_FALLBACK
*arg->hc_memoizep = arg->fallback;
#endif
@@ -348,7 +348,7 @@ get_EVP_MD_once_cb(void *d)
*arg->ossl_memoizep = ossl_evp = EVP_get_digestbynid(arg->nid);
if (ossl_evp == NULL) {
- (void) memset(hc_evp, 0, sizeof(*hc_evp));
+ (void) memset_s(hc_evp, sizeof(*hc_evp), 0, sizeof(*hc_evp));
#if HCRYPTO_FALLBACK
*arg->hc_memoizep = arg->fallback;
#endif
diff --git a/third_party/heimdal/lib/hcrypto/evp.c b/third_party/heimdal/lib/hcrypto/evp.c
index 9cced4c536c..320e85283f7 100644
--- a/third_party/heimdal/lib/hcrypto/evp.c
+++ b/third_party/heimdal/lib/hcrypto/evp.c
@@ -189,12 +189,12 @@ EVP_MD_CTX_cleanup(EVP_MD_CTX *ctx) HC_DEPRECATED
if (!ret)
return ret;
} else if (ctx->md) {
- memset(ctx->ptr, 0, ctx->md->ctx_size);
+ memset_s(ctx->ptr, ctx->md->ctx_size, 0, ctx->md->ctx_size);
}
ctx->md = NULL;
ctx->engine = NULL;
free(ctx->ptr);
- memset(ctx, 0, sizeof(*ctx));
+ memset_s(ctx, sizeof(*ctx), 0, sizeof(*ctx));
return 1;
}
@@ -607,7 +607,7 @@ EVP_CIPHER_CTX_cleanup(EVP_CIPHER_CTX *c)
}
if (c->cipher_data) {
if (c->cipher)
- memset(c->cipher_data, 0, c->cipher->ctx_size);
+ memset_s(c->cipher_data, c->cipher->ctx_size, 0, c->cipher->ctx_size);
free(c->cipher_data);
c->cipher_data = NULL;
}
@@ -905,7 +905,7 @@ EVP_CipherUpdate(EVP_CIPHER_CTX *ctx, void *out, int *outlen,
/* fill in local buffer and encrypt */
memcpy(ctx->buf + ctx->buf_len, in, left);
ret = (*ctx->cipher->do_cipher)(ctx, out, ctx->buf, blocksize);
- memset(ctx->buf, 0, blocksize);
+ memset_s(ctx->buf, blocksize, 0, blocksize);
if (ret != 1)
return ret;
@@ -966,7 +966,7 @@ EVP_CipherFinal_ex(EVP_CIPHER_CTX *ctx, void *out, int *outlen)
/* zero fill local buffer */
memset(ctx->buf + ctx->buf_len, 0, left);
ret = (*ctx->cipher->do_cipher)(ctx, out, ctx->buf, blocksize);
- memset(ctx->buf, 0, blocksize);
+ memset_s(ctx->buf, blocksize, 0, blocksize);
if (ret != 1)
return ret;
diff --git a/third_party/heimdal/lib/hcrypto/hmac.c b/third_party/heimdal/lib/hcrypto/hmac.c
index 6b387ae90dc..adccee76b26 100644
--- a/third_party/heimdal/lib/hcrypto/hmac.c
+++ b/third_party/heimdal/lib/hcrypto/hmac.c
@@ -46,17 +46,17 @@ void
HMAC_CTX_cleanup(HMAC_CTX *ctx)
{
if (ctx->buf) {
- memset(ctx->buf, 0, ctx->key_length);
+ memset_s(ctx->buf, ctx->key_length, 0, ctx->key_length);
free(ctx->buf);
ctx->buf = NULL;
}
if (ctx->opad) {
- memset(ctx->opad, 0, EVP_MD_block_size(ctx->md));
+ memset_s(ctx->opad, EVP_MD_block_size(ctx->md), 0, EVP_MD_block_size(ctx->md));
free(ctx->opad);
ctx->opad = NULL;
}
if (ctx->ipad) {
- memset(ctx->ipad, 0, EVP_MD_block_size(ctx->md));
+ memset_s(ctx->ipad, EVP_MD_block_size(ctx->md), 0, EVP_MD_block_size(ctx->md));
free(ctx->ipad);
ctx->ipad = NULL;
}
diff --git a/third_party/heimdal/lib/hcrypto/md2.c b/third_party/heimdal/lib/hcrypto/md2.c
index da41e6d83ee..0170d416a1c 100644
--- a/third_party/heimdal/lib/hcrypto/md2.c
+++ b/third_party/heimdal/lib/hcrypto/md2.c
@@ -133,6 +133,6 @@ MD2_Final (void *res, struct md2 *m)
MD2_Update(m, pad, 16);
memcpy(res, m->state, MD2_DIGEST_LENGTH);
- memset(m, 0, sizeof(*m));
+ memset_s(m, sizeof(*m), 0, sizeof(*m));
return 1;
}
diff --git a/third_party/heimdal/lib/hcrypto/passwd_dlg.c b/third_party/heimdal/lib/hcrypto/passwd_dlg.c
index 037fa7436a4..30721160a2a 100644
--- a/third_party/heimdal/lib/hcrypto/passwd_dlg.c
+++ b/third_party/heimdal/lib/hcrypto/passwd_dlg.c
@@ -77,11 +77,11 @@ pwd_dialog(char *buf, int size)
{
case IDOK:
strlcpy(buf, passwd, size);
- memset (passwd, 0, sizeof(passwd));
+ memset_s (passwd, sizeof(passwd), 0, sizeof(passwd));
return 0;
case IDCANCEL:
default:
- memset (passwd, 0, sizeof(passwd));
+ memset_s (passwd, sizeof(passwd), 0, sizeof(passwd));
return 1;
}
}
diff --git a/third_party/heimdal/lib/hcrypto/rand-fortuna.c b/third_party/heimdal/lib/hcrypto/rand-fortuna.c
index 313b86f83cf..74ba12396fd 100644
--- a/third_party/heimdal/lib/hcrypto/rand-fortuna.c
+++ b/third_party/heimdal/lib/hcrypto/rand-fortuna.c
@@ -336,7 +336,7 @@ add_entropy(FState * st, const unsigned char *data, unsigned len)
st->pool0_bytes += len;
memset_s(hash, sizeof(hash), 0, sizeof(hash));
- memset_s(&md, sizeof(hash), 0, sizeof(md));
+ memset_s(&md, sizeof(md), 0, sizeof(md));
}
/*
diff --git a/third_party/heimdal/lib/hcrypto/rc2.c b/third_party/heimdal/lib/hcrypto/rc2.c
index 02b684cbd4b..53d32cf0417 100644
--- a/third_party/heimdal/lib/hcrypto/rc2.c
+++ b/third_party/heimdal/lib/hcrypto/rc2.c
@@ -105,7 +105,7 @@ RC2_set_key(RC2_KEY *key, int len, const unsigned char *data, int bits)
for (j = 0; j < 64; j++)
key->data[j] = k[(j * 2) + 0] | (k[(j * 2) + 1] << 8);
- memset(k, 0, sizeof(k));
+ memset_s(k, sizeof(k), 0, sizeof(k));
}
#define ROT16L(w,n) ((w<<n)|(w>>(16-n)))
diff --git a/third_party/heimdal/lib/hcrypto/rsa.c b/third_party/heimdal/lib/hcrypto/rsa.c
index 6172b25413f..31470d0069a 100644
--- a/third_party/heimdal/lib/hcrypto/rsa.c
+++ b/third_party/heimdal/lib/hcrypto/rsa.c
@@ -160,7 +160,7 @@ RSA_free(RSA *rsa)
free_if(rsa->iqmp);
#undef free_if
- memset(rsa, 0, sizeof(*rsa));
+ memset_s(rsa, sizeof(*rsa), 0, sizeof(*rsa));
free(rsa);
}
@@ -426,7 +426,7 @@ RSA_verify(int type, const unsigned char *from, unsigned int flen,
return -4;
}
- if (flen != di.digest.length || memcmp(di.digest.data, from, flen) != 0) {
+ if (flen != di.digest.length || ct_memcmp(di.digest.data, from, flen) != 0) {
free_DigestInfo(&di);
return -5;
}
diff --git a/third_party/heimdal/lib/hdb/Makefile.am b/third_party/heimdal/lib/hdb/Makefile.am
index 89ab15d9d3e..4a559953243 100644
--- a/third_party/heimdal/lib/hdb/Makefile.am
+++ b/third_party/heimdal/lib/hdb/Makefile.am
@@ -17,7 +17,9 @@ endif
BUILT_SOURCES = \
$(gen_files_hdb) \
hdb_err.c \
- hdb_err.h
+ hdb_err.h \
+ $(srcdir)/hdb-protos.h \
+ $(srcdir)/hdb-private.h
gen_files_hdb = \
asn1_Event.c \
diff --git a/third_party/heimdal/lib/hdb/common.c b/third_party/heimdal/lib/hdb/common.c
index a92cc1372db..56e582abaa8 100644
--- a/third_party/heimdal/lib/hdb/common.c
+++ b/third_party/heimdal/lib/hdb/common.c
@@ -181,6 +181,7 @@ fetch_entry_or_alias(krb5_context context,
ret = decode_HDB_EntryOrAlias(value.data, value.length, &eoa, NULL);
if (ret == 0 && eoa.element == choice_HDB_EntryOrAlias_entry) {
*entry = eoa.u.entry;
+ entry->aliased = 0;
} else if (ret == 0 && eoa.element == choice_HDB_EntryOrAlias_alias) {
krb5_data_free(&key);
ret = hdb_principal2key(context, eoa.u.alias.principal, &key);
@@ -192,6 +193,7 @@ fetch_entry_or_alias(krb5_context context,
/* No alias chaining */
ret = hdb_value2entry(context, &value, entry);
krb5_free_principal(context, eoa.u.alias.principal);
+ entry->aliased = 1;
} else if (ret == 0)
ret = ENOTSUP;
if (ret == 0 && enterprise_principal) {
@@ -203,6 +205,7 @@ fetch_entry_or_alias(krb5_context context,
entry->flags.force_canonicalize = 1;
}
+#if 0
/* HDB_F_GET_ANY indicates request originated from KDC (not kadmin) */
if (ret == 0 && eoa.element == choice_HDB_EntryOrAlias_alias &&
(flags & (HDB_F_CANON|HDB_F_GET_ANY)) == 0) {
@@ -211,6 +214,7 @@ fetch_entry_or_alias(krb5_context context,
free_HDB_entry(entry);
ret = HDB_ERR_NOENTRY;
}
+#endif
krb5_free_principal(context, enterprise_principal);
krb5_data_free(&value);
@@ -219,11 +223,58 @@ fetch_entry_or_alias(krb5_context context,
return ret;
}
+/*
+ * We have only one type of aliases in our HDB entries, but we really need two:
+ * hard and soft.
+ *
+ * Hard aliases should be treated as if they were distinct principals with the
+ * same keys.
+ *
+ * Soft aliases should be treated as configuration to issue referrals, and they
+ * can only result in referrals to other realms.
+ *
+ * Rather than add a type of aliases, we'll use a convention where the form of
+ * the target of the alias indicates whether the alias is hard or soft.
+ *
+ * TODO We could also use an attribute of the aliased entry.
+ */
+static int
+is_soft_alias_p(krb5_context context,
+ krb5_const_principal principal,
+ unsigned int flags,
+ hdb_entry *h)
+{
+ /* Target is a WELLKNOWN/REFERRALS/TARGET/... -> soft alias */
+ if (krb5_principal_get_num_comp(context, h->principal) >= 3 &&
+ strcmp(krb5_principal_get_comp_string(context, h->principal, 0),
+ KRB5_WELLKNOWN_NAME) == 0 &&
+ strcmp(krb5_principal_get_comp_string(context, h->principal, 1),
+ "REFERRALS") == 0 &&
+ strcmp(krb5_principal_get_comp_string(context, h->principal, 2),
+ "TARGET") == 0)
+ return 1;
+
+ /*
+ * Pre-8.0 we had only soft aliases for a while, and one site used aliases
+ * of referrals-targetNN@TARGET-REALM.
+ */
+ if (krb5_principal_get_num_comp(context, h->principal) == 1 &&
+ strncmp("referrals-target",
+ krb5_principal_get_comp_string(context, h->principal, 0),
+ sizeof("referrals-target") - 1) == 0)
+ return 1;
+
+ /* All other cases are hard aliases */
+ return 0;
+}
+
krb5_error_code
_hdb_fetch_kvno(krb5_context context, HDB *db, krb5_const_principal principal,
unsigned flags, krb5_kvno kvno, hdb_entry *entry)
{
krb5_error_code ret;
+ int soft_aliased = 0;
+ int same_realm;
ret = fetch_entry_or_alias(context, db, principal, flags, entry);
if (ret)
@@ -278,7 +329,54 @@ _hdb_fetch_kvno(krb5_context context, HDB *db, krb5_const_principal principal,
}
}
- return 0;
+ if (!entry->aliased)
+ return 0;
+
+ soft_aliased = is_soft_alias_p(context, principal, flags, entry);
+
+ /* Never return HDB_ERR_WRONG_REALM to kadm5 or other non-KDC callers */
+ if ((flags & HDB_F_ADMIN_DATA))
+ return 0;
+
+ same_realm = krb5_realm_compare(context, principal, entry->principal);
+
+ if (entry->aliased && !soft_aliased) {
+ /*
+ * This is a hard alias. We'll make the entry's name be the same as
+ * the alias.
+ *
+ * Except, we allow for disabling this for same-realm aliases, mainly
+ * for our tests.
+ */
+ if (same_realm &&
+ krb5_config_get_bool_default(context, NULL, FALSE, "hdb",
+ "same_realm_aliases_are_soft", NULL))
+ return 0;
+
+ /* EPNs are always soft */
+ if (principal->name.name_type != KRB5_NT_ENTERPRISE_PRINCIPAL) {
+ krb5_free_principal(context, entry->principal);
+ ret = krb5_copy_principal(context, principal, &entry->principal);
+ if (ret) {
+ hdb_free_entry(context, db, entry);
+ return ret;
+ }
+ }
+ return 0;
+ }
+
+ /* Same realm -> not a referral, therefore this is a hard alias */
+ if (same_realm) {
+ if (soft_aliased) {
+ /* Soft alias to the same realm?! No. */
+ hdb_free_entry(context, db, entry);
+ return HDB_ERR_NOENTRY;
+ }
+ return 0;
+ }
+
+ /* Not same realm && not hard alias */
+ return HDB_ERR_WRONG_REALM;
}
static krb5_error_code
@@ -313,6 +411,8 @@ hdb_remove_aliases(krb5_context context, HDB *db, krb5_data *key)
if (code == 0) {
code = db->hdb__del(context, db, akey);
krb5_data_free(&akey);
+ if (code == HDB_ERR_NOENTRY)
+ code = 0;
}
if (code) {
free_HDB_entry(&oldentry);
@@ -348,6 +448,12 @@ hdb_add_aliases(krb5_context context, HDB *db,
if (code == 0) {
code = db->hdb__put(context, db, flags, key, value);
krb5_data_free(&key);
+ if (code == HDB_ERR_EXISTS)
+ /*
+ * Assuming hdb_check_aliases() was called, this must be a
+ * duplicate in the alias list.
+ */
+ code = 0;
}
krb5_data_free(&value);
if (code)
@@ -750,6 +856,10 @@ derive_keys_for_kr(krb5_context context,
* (t - krp->epoch < 0) is better than (krp->epoch < t), making us more
* tolerant of signed 32-bit time_t here near 2038. Of course, we have
* signed 32-bit time_t dragons elsewhere.
+ *
+ * We don't need to check for n == 0 && rotation_period_offset < 0 because
+ * only derive_keys_for_current_kr() calls us with non-zero rotation period
+ * offsets, and it will never call us in that case.
*/
if (t - krp->epoch < 0)
return 0; /* This KR is not relevant yet */
@@ -758,6 +868,37 @@ derive_keys_for_kr(krb5_context context,
set_time = krp->epoch + krp->period * n;
kvno = krp->base_kvno + n;
+ /*
+ * Since this principal is virtual, or has virtual keys, we're going to
+ * derive a "password expiration time" for it in order to help httpkadmind
+ * and other tools figure out when to request keys again.
+ *
+ * The kadm5 representation of principals does not include the set_time of
+ * keys/keysets, so we can't have httpkadmind derive a Cache-Control from
+ * that without adding yet another "TL data". Since adding TL data is a
+ * huge pain, we'll just use the `pw_end' field of `HDB_entry' to
+ * communicate when this principal's keys will change next.
+ */
+ if (h->pw_end[0] == 0) {
+ KerberosTime used = (t - krp->epoch) % krp->period;
+ KerberosTime left = krp->period - used;
+
+ /*
+ * If `h->pw_end[0]' == 0 then this must be the current period of the
+ * current KR we're deriving keys for. See upstairs.
+ *
+ * If there's more than a quarter of this time period left, then we'll
+ * set `h->pw_end[0]' to one quarter before the end of this time
+ * period. Else we'll set it to 1/4 after (we'll be including the next
+ * set of derived keys, so there's no harm in waiting that long to
+ * refetch).
+ */
+ if (left > krp->period >> 2)
+ h->pw_end[0] = set_time + krp->period - (krp->period >> 2);
+ else
+ h->pw_end[0] = set_time + krp->period + (krp->period >> 2);
+ }
+
/*
* Do not waste cycles computing keys not wanted or needed.
@@ -1092,6 +1233,11 @@ derive_keys(krb5_context context,
"because last key rotation period "
"marks deletion", p);
+ /* See `derive_keys_for_kr()' */
+ if (h->pw_end == NULL &&
+ (h->pw_end = calloc(1, sizeof(h->pw_end[0]))) == NULL)
+ ret = krb5_enomem(context);
+
/*
* Derive and set in `h' its current kvno and current keys.
*
@@ -1140,6 +1286,12 @@ derive_keys(krb5_context context,
if (ret == 0 && *h->max_life > kr.val[current_kr].period >> 1)
*h->max_life = kr.val[current_kr].period >> 1;
+ if (ret == 0 && h->pw_end[0] == 0)
+ /* Shouldn't happen */
+ h->pw_end[0] = kr.val[current_kr].epoch +
+ kr.val[current_kr].period *
+ (1 + (t - kr.val[current_kr].epoch) / kr.val[current_kr].period);
+
free_HDB_Ext_KeyRotation(&kr);
free_HDB_Ext_KeySet(&base_keys);
free(p);
@@ -1506,6 +1658,10 @@ fetch_it(krb5_context context,
* of labels.
*/
ret = db->hdb_fetch_kvno(context, db, tmpprinc, flags, kvno, ent);
+ if (ret == 0 && nsprinc && ent->flags.invalid) {
+ free_HDB_entry(ent);
+ ret = HDB_ERR_NOENTRY;
+ }
if (ret != HDB_ERR_NOENTRY || hdots == 0 || hdots < mindots || !tmp ||
!do_search)
break;
@@ -1561,7 +1717,9 @@ fetch_it(krb5_context context,
* If unencrypted keys were requested, derive them. There may not be any
* key derivation to do, but that's decided in derive_keys().
*/
- if (ret == 0) {
+ if (ret == 0 || ret == HDB_ERR_WRONG_REALM) {
+ krb5_error_code save_ret = ret;
+
/* Fix the principal name if namespaced */
ret = fix_princ_name(context, princ, nsprinc, ent);
@@ -1572,6 +1730,8 @@ fetch_it(krb5_context context,
/* Pick the best kvno for this principal at the given time */
if (ret == 0)
ret = pick_kvno(context, db, flags, t, kvno, ent);
+ if (ret == 0)
+ ret = save_ret;
}
out:
@@ -1603,7 +1763,7 @@ out:
* @param kvno Key version number (use zero to mean "current")
* @param h Output HDB entry
*
- * @return Zero on success, an error code otherwise.
+ * @return Zero or HDB_ERR_WRONG_REALM on success, an error code otherwise.
*/
krb5_error_code
hdb_fetch_kvno(krb5_context context,
@@ -1615,24 +1775,25 @@ hdb_fetch_kvno(krb5_context context,
krb5uint32 kvno,
hdb_entry *h)
{
- krb5_error_code ret = HDB_ERR_NOENTRY;
+ krb5_error_code ret;
+ krb5_timestamp now;
+
+ krb5_timeofday(context, &now);
flags |= kvno ? HDB_F_KVNO_SPECIFIED : 0; /* XXX is this needed */
- if (t == 0)
- krb5_timeofday(context, &t);
- ret = fetch_it(context, db, principal, flags, t, etype, kvno, h);
+ ret = fetch_it(context, db, principal, flags, t ? t : now, etype, kvno, h);
+ if (ret == 0 && t == 0 && h->flags.virtual &&
+ h->pw_end && h->pw_end[0] < now) {
+ /*
+ * This shouldn't happen!
+ *
+ * Do not allow h->pw_end[0] to be in the past for virtual principals
+ * outside testing. This is just to prevent the AS/TGS from failing.
+ */
+ h->pw_end[0] = now + 3600;
+ }
if (ret == HDB_ERR_NOENTRY)
krb5_set_error_message(context, ret, "no such entry found in hdb");
-
- /*
- * This check is to support aliases in HDB; the force_canonicalize
- * check is to allow HDB backends to support realm name canon
- * independently of principal aliases (used by Samba).
- */
- if (ret == 0 && !(flags & HDB_F_ADMIN_DATA) &&
- !h->flags.force_canonicalize &&
- !krb5_realm_compare(context, principal, h->principal))
- ret = HDB_ERR_WRONG_REALM;
return ret;
}
diff --git a/third_party/heimdal/lib/hdb/hdb-mdb.c b/third_party/heimdal/lib/hdb/hdb-mdb.c
index 6aa5201eb8a..dd1f27453d5 100644
--- a/third_party/heimdal/lib/hdb/hdb-mdb.c
+++ b/third_party/heimdal/lib/hdb/hdb-mdb.c
@@ -146,7 +146,7 @@ my_mdb_env_create_and_open(krb5_context context,
{
struct keep_it_open *p, *n;
MDB_txn *txn = NULL;
- unsigned int flags = MDB_NOSUBDIR;
+ unsigned int flags = MDB_NOSUBDIR | MDB_NOTLS;
struct stat st;
size_t mapsize = 0;
int max_readers;
diff --git a/third_party/heimdal/lib/hdb/hdb.asn1 b/third_party/heimdal/lib/hdb/hdb.asn1
index 9eb96be73d0..abc75f742cc 100644
--- a/third_party/heimdal/lib/hdb/hdb.asn1
+++ b/third_party/heimdal/lib/hdb/hdb.asn1
@@ -59,6 +59,8 @@ HDBFlags ::= BIT STRING {
force-canonicalize(30), -- force the KDC to return the canonical
-- principal irrespective of the setting
-- of the canonicalize KDC option
+ -- (principals cannot have this flag
+ -- set when stored into the HDB)
do-not-store(31) -- Not to be modified and stored in HDB
}
diff --git a/third_party/heimdal/lib/hdb/hdb.opt b/third_party/heimdal/lib/hdb/hdb.opt
index 626f8c7b07a..a96eb632e52 100644
--- a/third_party/heimdal/lib/hdb/hdb.opt
+++ b/third_party/heimdal/lib/hdb/hdb.opt
@@ -3,3 +3,7 @@
--sequence=HDB-Ext-KeySet
--sequence=Keys
--decorate=HDB_entry:void:context?:::
+# The `aliased` field is for indicating whether a name used in an HDB
+# lookup was an alias. This could be useful to applications when hard
+# aliasing is implemented in an HDB backend, as it should be.
+--decorate=HDB_entry:int:aliased:::
diff --git a/third_party/heimdal/lib/hdb/keytab.c b/third_party/heimdal/lib/hdb/keytab.c
index b1aa0207c97..df16cb782fa 100644
--- a/third_party/heimdal/lib/hdb/keytab.c
+++ b/third_party/heimdal/lib/hdb/keytab.c
@@ -223,10 +223,9 @@ hdb_get_entry(krb5_context context,
HDB_F_GET_CLIENT|HDB_F_GET_SERVER|HDB_F_GET_KRBTGT,
0, 0, kvno, &ent);
- if(ret == HDB_ERR_NOENTRY) {
+ if (ret == HDB_ERR_WRONG_REALM || ret == HDB_ERR_NOENTRY)
ret = KRB5_KT_NOTFOUND;
- goto out;
- }else if(ret)
+ if (ret)
goto out;
if(kvno && (krb5_kvno)ent.kvno != kvno) {
diff --git a/third_party/heimdal/lib/hx509/cert.c b/third_party/heimdal/lib/hx509/cert.c
index 33805b8ed1a..3dda886edc5 100644
--- a/third_party/heimdal/lib/hx509/cert.c
+++ b/third_party/heimdal/lib/hx509/cert.c
@@ -2443,7 +2443,7 @@ hx509_verify_path(hx509_context context,
type = EE_CERT;
}
}
- fallthrough;
+ HEIM_FALLTHROUGH;
case EE_CERT:
/*
* If there where any proxy certificates in the chain
diff --git a/third_party/heimdal/lib/hx509/cms.c b/third_party/heimdal/lib/hx509/cms.c
index d2728a38c2f..c770b813262 100644
--- a/third_party/heimdal/lib/hx509/cms.c
+++ b/third_party/heimdal/lib/hx509/cms.c
@@ -182,7 +182,7 @@ fill_CMSIdentifier(const hx509_cert cert,
&id->u.subjectKeyIdentifier);
if (ret == 0)
break;
- fallthrough;
+ HEIM_FALLTHROUGH;
case CMS_ID_NAME: {
hx509_name name;
diff --git a/third_party/heimdal/lib/hx509/file.c b/third_party/heimdal/lib/hx509/file.c
index a22f6252cfa..e1fbe7c0688 100644
--- a/third_party/heimdal/lib/hx509/file.c
+++ b/third_party/heimdal/lib/hx509/file.c
@@ -230,7 +230,7 @@ hx509_pem_read(hx509_context context,
where = INDATA;
goto indata;
}
- fallthrough;
+ HEIM_FALLTHROUGH;
case INHEADER:
if (buf[0] == '\0') {
where = INDATA;
diff --git a/third_party/heimdal/lib/hx509/hxtool.1 b/third_party/heimdal/lib/hx509/hxtool.1
new file mode 100644
index 00000000000..377500784ec
--- /dev/null
+++ b/third_party/heimdal/lib/hx509/hxtool.1
@@ -0,0 +1,207 @@
+.\" Copyright (c) 2022 Kungliga Tekniska Högskolan
+.\" (Royal Institute of Technology, Stockholm, Sweden).
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" 3. Neither the name of the Institute nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 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.
+.\"
+.\" $Id$
+.\"
+.Dd February 22, 2022
+.Dt HXTOOL 1
+.Os HEIMDAL
+.Sh NAME
+.Nm hxtool
+.Nd PKIX command-line utility
+.Sh SYNOPSIS
+.Nm
+.Bk -words
+.Oo Fl Fl version Oc
+.Oo Fl Fl help Oc
+.Op Ar sub-command
+.Ek
+.Sh DESCRIPTION
+.Nm
+is a utility for making certificate sigining requests (CSRs),
+displaying CSRs, signing certificates, etc.
+are given, then the value will be parsed and displayed using just
+the self-describing nature of DER.
+.Pp
+All sub-commands have their own help message, shown when invoked
+with the
+.Fl Fl help
+or
+.Fl h
+option.
+.Pp
+Supported commands:
+.Bl -tag -width Ds -offset indent
+.It help
+.It list-oids
+.It verify
+.It print
+.It validate
+.It certificate-copy, cc
+.It ocsp-fetch
+.It ocsp-verify
+.It ocsp-print
+.It revoke-print
+.It generate-key
+.It request-create
+.It request-print
+.It query
+.It info
+.It random-data
+.It crypto-available
+.It crypto-select
+.It hex
+.It certificate-sign, cert-sign, issue-certificate, ca
+.It crl-sign
+.El
+Other sub-commands reported by the
+.Nm help
+sub-command are not stable or fully supported at this time.
+.Sh CERTIFICATE STORES
+Stores of certificates and/or keys have string names that can be
+used with
+.Nm 's
+commands.
+Sub-commands use these certificate store names to refer to files
+and tokens where keys and/or certificates are to be found or
+written.
+For example,
+.Sq FILE:/path/to/some/file .
+.Pp
+Use the
+.Nm certificate-copy
+command to copy certificates from one store to another.
+This is useful for, e.g., converting DER files to PEM or
+vice-versa.
+.Pp
+Heimdal supports a variety of certificate and key store types:
+.Bl -tag -width Ds -offset indent
+.It PEM-FILE:/path
+If writing, PEM will be written.
+If reading, PEM will be expected.
+.It DER-FILE:/path
+If writing, DER will be written.
+If reading, DER will be expected.
+.It FILE:/path
+If writing, PEM will be written.
+If reading, PEM or DER will be detected.
+.It PKCS12:/path
+Barely supported at this time.
+.It DIR:/path
+OpenSSL-style hashed directory of trust anchors.
+.It MEMORY:name
+An in-memory only store, usually never used in
+.NM 's
+commands.
+.It KEYCHAIN:system-anchors
+On OS X this refers to the system's trust anchors.
+.It KEYCHAIN:FILE:/path
+On OS X this refers to an OS X keychain at the given path.
+.It NULL:
+An empty store.
+.It PKCS11:/path/to/shared/object,slot=NUMBER
+Loads the given PKCS#11 provider object and uses the token at the
+given slot number.
+.El
+.Sh CERTIFICATES
+You can validate a certificate with the
+.Nm validate
+sub-command, or verify a certificate and its certification path
+with the
+.Nm verify
+sub-command.
+.Pp
+You can display a certificate using the
+.Nm print
+sub-command:
+.Pp
+.Nm print
+.Oo options Oc
+.Ar STORE
+.Pp
+Options:
+.Bl -tag -width Ds -offset indent
+.Op Fl Fl content
+.Op Fl Fl info
+.Op Fl Fl never-fail
+.Op Fl Fl pass=password
+.Op Fl Fl raw-json
+.El
+.Pp
+The
+.Fl Fl pass=password
+option is for PKCS#12 and PKCS#11 stores, and if needed and not
+given, will be prompted for.
+Note that it's not secure to pass passwords as command-line
+arguments on multi-tenant systems.
+.Pp
+The
+.Fl Fl raw-json
+option prints the certificate(s) in the given
+.Ar STORE
+as a JSON dump of their DER using an experimental (i.e.,
+unstable) schema.
+.Sh KEYS
+The
+.Nm generate-key
+sub-command will generate a key.
+.Sh CERTIFICATE SIGNING REQUESTS
+The
+.Nm request-create
+sub-command will create a CSR.
+The
+.Nm request-print
+sub-command will display a CSR.
+.Sh CERTIFICATE ISSUANCE / CERTIFICATION AUTHORITY
+The
+.Nm certificate-sign
+sub-command will issue a certificate.
+See its usage message.
+.Sh ONLINE CERTIFICATE STATUS PROTOCOL
+The
+.Nm ocsp-fetch
+sub-command will fetch OCSP Responses for the given
+certificates.
+.Pp
+The
+.Nm ocsp-verify
+sub-command will verify OCSP Responses.
+.Pp
+The
+.Nm ocsp-print
+sub-command will display OCSP Responses.
+.Sh CERTIFICATE REVOCATION LIST
+The
+.Nm crl-sign
+sub-command will add certificates to a certificate revocation
+list.
+.Sh SEE ALSO
+.Xr openssl 1
diff --git a/third_party/heimdal/lib/hx509/hxtool.c b/third_party/heimdal/lib/hx509/hxtool.c
index 1bcfdfa44e9..aa9e279c81c 100644
--- a/third_party/heimdal/lib/hx509/hxtool.c
+++ b/third_party/heimdal/lib/hx509/hxtool.c
@@ -2076,39 +2076,33 @@ hxtool_ca(struct certificate_sign_options *opt, int argc, char **argv)
}
if (opt->generate_key_string) {
- struct hx509_generate_private_context *keyctx;
+ /*
+ * Note that we used to set isCA in the key gen context. Now that we
+ * use get_key() we no longer set isCA in the key gen context. But
+ * nothing uses that field of the key gen context.
+ */
+ get_key(opt->certificate_private_key_string,
+ opt->generate_key_string,
+ opt->key_bits_integer,
+ &cert_key);
- ret = _hx509_generate_private_key_init(context,
- &asn1_oid_id_pkcs1_rsaEncryption,
- &keyctx);
+ ret = hx509_private_key2SPKI(context, cert_key, &spki);
if (ret)
- hx509_err(context, 1, ret, "generate private key");
-
- if (opt->issue_ca_flag)
- _hx509_generate_private_key_is_ca(context, keyctx);
-
- if (opt->key_bits_integer)
- _hx509_generate_private_key_bits(context, keyctx,
- opt->key_bits_integer);
+ errx(1, "hx509_private_key2SPKI: %d\n", ret);
- ret = _hx509_generate_private_key(context, keyctx,
- &cert_key);
- _hx509_generate_private_key_free(&keyctx);
+ if (opt->self_signed_flag)
+ private_key = cert_key;
+ } else if (opt->certificate_private_key_string) {
+ ret = read_private_key(opt->certificate_private_key_string, &cert_key);
if (ret)
- hx509_err(context, 1, ret, "generate private key");
+ err(1, "read_private_key for certificate");
ret = hx509_private_key2SPKI(context, cert_key, &spki);
if (ret)
errx(1, "hx509_private_key2SPKI: %d\n", ret);
- if (opt->self_signed_flag)
- private_key = cert_key;
- }
-
- if (opt->certificate_private_key_string) {
- ret = read_private_key(opt->certificate_private_key_string, &cert_key);
- if (ret)
- err(1, "read_private_key for certificate");
+ if (opt->self_signed_flag)
+ private_key = cert_key;
}
if (opt->subject_string) {
@@ -2319,7 +2313,31 @@ hxtool_ca(struct certificate_sign_options *opt, int argc, char **argv)
hx509_err(context, 1, ret, "hx509_ca_sign");
}
- if (cert_key) {
+ /* Copy the private key to the output store, maybe */
+ if (cert_key && opt->generate_key_string &&
+ !opt->certificate_private_key_string) {
+ /*
+ * Yes: because we're generating the key and --certificate-private-key
+ * was not given.
+ */
+ ret = _hx509_cert_assign_key(cert, cert_key);
+ if (ret)
+ hx509_err(context, 1, ret, "_hx509_cert_assign_key");
+ } else if (opt->certificate_private_key_string && opt->certificate_string &&
+ strcmp(opt->certificate_private_key_string,
+ opt->certificate_string) == 0) {
+ /*
+ * Yes: because we're re-writing the store whence the private key. We
+ * would lose the key otherwise.
+ */
+ ret = _hx509_cert_assign_key(cert, cert_key);
+ if (ret)
+ hx509_err(context, 1, ret, "_hx509_cert_assign_key");
+ } else if (opt->self_signed_flag && opt->ca_private_key_string &&
+ opt->certificate_string &&
+ strcmp(opt->ca_private_key_string,
+ opt->certificate_string) == 0) {
+ /* Yes: same as preceding */
ret = _hx509_cert_assign_key(cert, cert_key);
if (ret)
hx509_err(context, 1, ret, "_hx509_cert_assign_key");
diff --git a/third_party/heimdal/lib/hx509/req.c b/third_party/heimdal/lib/hx509/req.c
index 2b3f46d532a..c8fa42e5f99 100644
--- a/third_party/heimdal/lib/hx509/req.c
+++ b/third_party/heimdal/lib/hx509/req.c
@@ -1046,7 +1046,7 @@ authorize_feat(hx509_request req, abitstring a, size_t n, int idx)
switch (ret) {
case 0:
req->nauthorized++;
- fallthrough;
+ HEIM_FALLTHROUGH;
case -1:
return 0;
default:
@@ -1063,7 +1063,7 @@ reject_feat(hx509_request req, abitstring a, size_t n, int idx)
switch (ret) {
case 0:
req->nauthorized--;
- fallthrough;
+ HEIM_FALLTHROUGH;
case -1:
return 0;
default:
@@ -1245,7 +1245,7 @@ san_map_type(GeneralName *san)
if (der_heim_oid_cmp(&san->u.otherName.type_id, map[i].oid) == 0)
return map[i].type;
}
- fallthrough;
+ HEIM_FALLTHROUGH;
default: return HX509_SAN_TYPE_UNSUPPORTED;
}
}
@@ -1360,7 +1360,7 @@ hx509_request_get_san(hx509_request req,
case HX509_SAN_TYPE_REGISTERED_ID:
return der_print_heim_oid(&san->u.registeredID, '.', out);
case HX509_SAN_TYPE_XMPP:
- fallthrough;
+ HEIM_FALLTHROUGH;
case HX509_SAN_TYPE_MS_UPN: {
int ret;
diff --git a/third_party/heimdal/lib/ipc/server.c b/third_party/heimdal/lib/ipc/server.c
index 40601b9744f..b0b2fa1cb66 100644
--- a/third_party/heimdal/lib/ipc/server.c
+++ b/third_party/heimdal/lib/ipc/server.c
@@ -122,26 +122,19 @@ mach_complete_sync(heim_sipc_call ctx, int returnvalue, heim_idata *reply)
{
struct mach_call_ctx *s = (struct mach_call_ctx *)ctx;
heim_ipc_message_inband_t replyin;
- mach_msg_type_number_t replyinCnt;
- heim_ipc_message_outband_t replyout;
- mach_msg_type_number_t replyoutCnt;
- kern_return_t kr;
+ mach_msg_type_number_t replyinCnt = 0;
+ heim_ipc_message_outband_t replyout = 0;
+ mach_msg_type_number_t replyoutCnt = 0;
if (returnvalue) {
/* on error, no reply */
- replyinCnt = 0;
- replyout = 0; replyoutCnt = 0;
- kr = KERN_SUCCESS;
} else if (reply->length < 2048) {
replyinCnt = reply->length;
memcpy(replyin, reply->data, replyinCnt);
- replyout = 0; replyoutCnt = 0;
- kr = KERN_SUCCESS;
} else {
- replyinCnt = 0;
- kr = vm_read(mach_task_self(),
- (vm_address_t)reply->data, reply->length,
- (vm_address_t *)&replyout, &replyoutCnt);
+ vm_read(mach_task_self(),
+ (vm_address_t)reply->data, reply->length,
+ (vm_address_t *)&replyout, &replyoutCnt);
}
mheim_ripc_call_reply(s->reply_port, returnvalue,
@@ -159,31 +152,24 @@ mach_complete_async(heim_sipc_call ctx, int returnvalue, heim_idata *reply)
{
struct mach_call_ctx *s = (struct mach_call_ctx *)ctx;
heim_ipc_message_inband_t replyin;
- mach_msg_type_number_t replyinCnt;
- heim_ipc_message_outband_t replyout;
- mach_msg_type_number_t replyoutCnt;
- kern_return_t kr;
+ mach_msg_type_number_t replyinCnt = 0;
+ heim_ipc_message_outband_t replyout = 0;
+ mach_msg_type_number_t replyoutCnt = 0;
if (returnvalue) {
/* on error, no reply */
- replyinCnt = 0;
- replyout = 0; replyoutCnt = 0;
- kr = KERN_SUCCESS;
} else if (reply->length < 2048) {
replyinCnt = reply->length;
memcpy(replyin, reply->data, replyinCnt);
- replyout = 0; replyoutCnt = 0;
- kr = KERN_SUCCESS;
} else {
- replyinCnt = 0;
- kr = vm_read(mach_task_self(),
- (vm_address_t)reply->data, reply->length,
- (vm_address_t *)&replyout, &replyoutCnt);
+ vm_read(mach_task_self(),
+ (vm_address_t)reply->data, reply->length,
+ (vm_address_t *)&replyout, &replyoutCnt);
}
- kr = mheim_aipc_acall_reply(s->reply_port, returnvalue,
- replyin, replyinCnt,
- replyout, replyoutCnt);
+ mheim_aipc_acall_reply(s->reply_port, returnvalue,
+ replyin, replyinCnt,
+ replyout, replyoutCnt);
heim_ipc_free_cred(s->cred);
free(s->req.data);
free(s);
@@ -700,6 +686,7 @@ maybe_close(struct client *c)
dispatch_release(c->out);
#endif
close(c->fd); /* ref count fd close */
+ free(c->inmsg);
free(c);
return 1;
}
@@ -1382,4 +1369,3 @@ heim_ipc_main(void)
process_loop();
#endif
}
-
diff --git a/third_party/heimdal/lib/kadm5/ad.c b/third_party/heimdal/lib/kadm5/ad.c
index 58ccf32eacd..b9b9c9023b9 100644
--- a/third_party/heimdal/lib/kadm5/ad.c
+++ b/third_party/heimdal/lib/kadm5/ad.c
@@ -1066,6 +1066,33 @@ kadm5_ad_get_principals(void *server_handle,
}
static kadm5_ret_t
+kadm5_ad_iter_principals(void *server_handle,
+ const char *expression,
+ int (*cb)(void *, const char *),
+ void *cbdata)
+{
+ kadm5_ad_context *context = server_handle;
+
+#ifdef OPENLDAP
+ kadm5_ret_t ret;
+
+ ret = ad_get_cred(context, NULL);
+ if (ret)
+ return ret;
+
+ ret = _kadm5_ad_connect(server_handle);
+ if (ret)
+ return ret;
+
+ krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented");
+ return KADM5_RPC_ERROR;
+#else
+ krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented");
+ return KADM5_RPC_ERROR;
+#endif
+}
+
+static kadm5_ret_t
kadm5_ad_get_privs(void *server_handle, uint32_t*privs)
{
kadm5_ad_context *context = server_handle;
@@ -1169,7 +1196,7 @@ kadm5_ad_modify_principal(void *server_handle,
if (entry->attributes & KRB5_KDB_REQUIRES_HW_AUTH)
i |= UF_SMARTCARD_REQUIRED;
else
- i &= UF_SMARTCARD_REQUIRED;
+ i &= ~UF_SMARTCARD_REQUIRED;
if (entry->attributes & KRB5_KDB_DISALLOW_SVR)
i &= ~UF_WORKSTATION_TRUST_ACCOUNT;
else
@@ -1388,6 +1415,9 @@ set_funcs(kadm5_ad_context *c)
SET(c, lock);
SET(c, unlock);
SETNOTIMP(c, setkey_principal_3);
+ SETNOTIMP(c, prune_principal);
+ SET(c, iter_principals);
+ SET(c, dup_context);
}
kadm5_ret_t
@@ -1456,6 +1486,12 @@ kadm5_ad_init_with_password_ctx(krb5_context context,
}
kadm5_ret_t
+kadm5_ad_dup_context(void *in, void **out)
+{
+ return ENOTSUP;
+}
+
+kadm5_ret_t
kadm5_ad_init_with_password(const char *client_name,
const char *password,
const char *service_name,
diff --git a/third_party/heimdal/lib/kadm5/common_glue.c b/third_party/heimdal/lib/kadm5/common_glue.c
index 9993b0a349e..210bf93b91c 100644
--- a/third_party/heimdal/lib/kadm5/common_glue.c
+++ b/third_party/heimdal/lib/kadm5/common_glue.c
@@ -39,6 +39,12 @@ RCSID("$Id$");
#define __CALLABLE(F) (((kadm5_common_context*)server_handle)->funcs.F != 0)
kadm5_ret_t
+kadm5_dup_context(void *server_handle, void **dup_server_handle)
+{
+ return __CALL(dup_context, (server_handle, dup_server_handle));
+}
+
+kadm5_ret_t
kadm5_chpass_principal(void *server_handle,
krb5_principal princ,
const char *password)
@@ -223,6 +229,15 @@ kadm5_get_principals(void *server_handle,
}
kadm5_ret_t
+kadm5_iter_principals(void *server_handle,
+ const char *expression,
+ int (*cb)(void *, const char *),
+ void *cbdata)
+{
+ return __CALL(iter_principals, (server_handle, expression, cb, cbdata));
+}
+
+kadm5_ret_t
kadm5_get_privs(void *server_handle,
uint32_t *privs)
{
diff --git a/third_party/heimdal/lib/kadm5/context_s.c b/third_party/heimdal/lib/kadm5/context_s.c
index 0c154ecfef0..5c9b3e31c81 100644
--- a/third_party/heimdal/lib/kadm5/context_s.c
+++ b/third_party/heimdal/lib/kadm5/context_s.c
@@ -112,6 +112,8 @@ set_funcs(kadm5_server_context *c)
SET(c, lock);
SET(c, unlock);
SET(c, setkey_principal_3);
+ SET(c, iter_principals);
+ SET(c, dup_context);
}
#ifndef NO_UNIX_SOCKETS
@@ -250,6 +252,8 @@ _kadm5_s_init_context(kadm5_server_context **ctx,
krb5_add_et_list (context, initialize_kadm5_error_table_r);
#define is_set(M) (params && params->mask & KADM5_CONFIG_ ## M)
+ if (params)
+ (*ctx)->config.mask = params->mask;
if (is_set(REALM)) {
(*ctx)->config.realm = strdup(params->realm);
if ((*ctx)->config.realm == NULL)
@@ -275,9 +279,9 @@ _kadm5_s_init_context(kadm5_server_context **ctx,
return krb5_enomem(context);
}
- find_db_spec(*ctx);
-
- ret = _kadm5_s_init_hooks(*ctx);
+ ret = find_db_spec(*ctx);
+ if (ret == 0)
+ ret = _kadm5_s_init_hooks(*ctx);
if (ret != 0) {
kadm5_s_destroy(*ctx);
*ctx = NULL;
diff --git a/third_party/heimdal/lib/kadm5/create_s.c b/third_party/heimdal/lib/kadm5/create_s.c
index 1c2ab15f30d..e603497ace2 100644
--- a/third_party/heimdal/lib/kadm5/create_s.c
+++ b/third_party/heimdal/lib/kadm5/create_s.c
@@ -50,6 +50,14 @@ get_default(kadm5_server_context *context, krb5_principal princ,
ret = kadm5_s_get_principal(context, def_principal, def,
KADM5_PRINCIPAL_NORMAL_MASK);
krb5_free_principal (context->context, def_principal);
+
+ if (ret) {
+ /* Copy defaults from kadmin/init.c */
+ memset(def, 0, sizeof(*def));
+ def->max_life = 24 * 60 * 60;
+ def->max_renewable_life = 7 * def->max_life;
+ def->attributes = KRB5_KDB_DISALLOW_ALL_TIX;
+ }
return ret;
}
diff --git a/third_party/heimdal/lib/kadm5/destroy_s.c b/third_party/heimdal/lib/kadm5/destroy_s.c
index fc86e5960e6..0558b45c577 100644
--- a/third_party/heimdal/lib/kadm5/destroy_s.c
+++ b/third_party/heimdal/lib/kadm5/destroy_s.c
@@ -42,10 +42,12 @@ RCSID("$Id$");
static void
destroy_config (kadm5_config_params *c)
{
- free (c->realm);
- free (c->dbname);
- free (c->acl_file);
- free (c->stash_file);
+ if (!c)
+ return;
+ free(c->realm);
+ free(c->dbname);
+ free(c->acl_file);
+ free(c->stash_file);
}
/*
@@ -55,6 +57,8 @@ destroy_config (kadm5_config_params *c)
static void
destroy_kadm5_log_context (kadm5_log_context *c)
{
+ if (!c)
+ return;
free(c->log_file);
if (c->socket_fd != rk_INVALID_SOCKET)
rk_closesocket(c->socket_fd);
diff --git a/third_party/heimdal/lib/kadm5/get_c.c b/third_party/heimdal/lib/kadm5/get_c.c
index 5b2bcca762a..1c1fd9dce80 100644
--- a/third_party/heimdal/lib/kadm5/get_c.c
+++ b/third_party/heimdal/lib/kadm5/get_c.c
@@ -89,7 +89,7 @@ kadm5_c_get_principal(void *server_handle,
out_keep_error:
if (ret == 0)
- kadm5_ret_principal_ent(sp, ent);
+ ret = kadm5_ret_principal_ent(sp, ent);
krb5_storage_free(sp);
krb5_data_free(&reply);
return ret;
diff --git a/third_party/heimdal/lib/kadm5/get_princs_c.c b/third_party/heimdal/lib/kadm5/get_princs_c.c
index 4998223d260..93d7ce7ed1b 100644
--- a/third_party/heimdal/lib/kadm5/get_princs_c.c
+++ b/third_party/heimdal/lib/kadm5/get_princs_c.c
@@ -66,7 +66,7 @@ kadm5_c_get_principals(void *server_handle,
ret = krb5_store_int32(sp, kadm_get_princs);
if (ret)
goto out;
- ret = krb5_store_int32(sp, expression != NULL);
+ ret = krb5_store_int32(sp, expression != NULL ? 1 : 0);
if (ret)
goto out;
if (expression) {
@@ -117,3 +117,187 @@ kadm5_c_get_principals(void *server_handle,
krb5_data_free(&reply);
return ret;
}
+
+kadm5_ret_t
+kadm5_c_iter_principals(void *server_handle,
+ const char *expression,
+ int (*cb)(void *, const char *),
+ void *cbdata)
+{
+ kadm5_client_context *context = server_handle;
+ kadm5_ret_t ret;
+ krb5_storage *sp;
+ unsigned char buf[1024];
+ int32_t tmp;
+ krb5_data reply;
+ size_t i;
+
+ ret = _kadm5_connect(server_handle, 0 /* want_write */);
+ if (ret)
+ return ret;
+
+ krb5_data_zero(&reply);
+
+ sp = krb5_storage_from_mem(buf, sizeof(buf));
+ if (sp == NULL) {
+ ret = krb5_enomem(context->context);
+ goto out_keep_error;
+ }
+ ret = krb5_store_int32(sp, kadm_get_princs);
+ if (ret)
+ goto out;
+
+ /*
+ * Our protocol has an int boolean for this operation to indicate whether
+ * there's an expression. What we'll do here is that instead of sending
+ * just false or trueish, for online iteration we'll send a number other
+ * than 0 or 1 -- a magic value > 0 and < INT_MAX.
+ *
+ * In response we'll expect multiple replies, each with up to some small
+ * number of principal names. See kadmin/server.c.
+ */
+ ret = krb5_store_int32(sp, 0x55555555);
+ if (ret)
+ goto out;
+ ret = krb5_store_string(sp, expression ? expression : "");
+ if (ret)
+ goto out;
+ ret = _kadm5_client_send(context, sp);
+ if (ret)
+ goto out_keep_error;
+ ret = _kadm5_client_recv(context, &reply);
+ if (ret)
+ goto out_keep_error;
+ krb5_storage_free(sp);
+ sp = krb5_storage_from_data (&reply);
+ if (sp == NULL) {
+ ret = krb5_enomem(context->context);
+ goto out_keep_error;
+ }
+ ret = krb5_ret_int32(sp, &tmp);
+ if (ret == 0)
+ ret = tmp;
+
+ if (ret)
+ goto out;
+
+ ret = krb5_ret_int32(sp, &tmp);
+ if (ret)
+ goto out;
+
+ if (tmp < 0) {
+ size_t n = -tmp;
+ int more = 1;
+ int stop = 0;
+
+ /* The server supports online iteration, hooray! */
+
+ while (more) {
+ /*
+ * We expect any number of chunks, each having `n' names, except
+ * the last one would have fewer than `n' (possibly zero, even).
+ *
+ * After that we expect one more reply with just a final return
+ * code.
+ */
+ krb5_data_free(&reply);
+ krb5_storage_free(sp);
+ sp = NULL;
+ ret = _kadm5_client_recv(context, &reply);
+ if (ret == 0 && (sp = krb5_storage_from_data(&reply)) == NULL)
+ ret = krb5_enomem(context->context);
+ if (ret)
+ goto out;
+
+ /* Every chunk begins with a status code */
+ ret = krb5_ret_int32(sp, &tmp);
+ if (ret == 0)
+ ret = tmp;
+ if (ret)
+ goto out;
+
+ /* We expect up to -tmp principals per reply */
+ for (i = 0; i < n; i++) {
+ char *princ = NULL;
+
+ ret = krb5_ret_string(sp, &princ);
+ if (ret == HEIM_ERR_EOF) {
+ /* This was the last reply */
+ more = 0;
+ ret = 0;
+ break;
+ }
+ if (ret)
+ goto out;
+ if (!stop) {
+ stop = cb(cbdata, princ);
+ if (stop) {
+ /*
+ * Tell the server to stop.
+ *
+ * We use a NOP for this, but with a payload that says
+ * "don't reply to the NOP" just in case the NOP
+ * arrives and is processed _after_ the LISTing has
+ * finished.
+ */
+ krb5_storage_free(sp);
+ if ((sp = krb5_storage_emem()) &&
+ krb5_store_int32(sp, kadm_nop) == 0 &&
+ krb5_store_int32(sp, 0))
+ (void) _kadm5_client_send(context, sp);
+ }
+ }
+ free(princ);
+ }
+
+ if (!more) {
+ if (ret == 0)
+ ret = stop;
+ break;
+ }
+ }
+ /* Get the final result code */
+ krb5_data_free(&reply);
+ krb5_storage_free(sp);
+ sp = NULL;
+ ret = _kadm5_client_recv(context, &reply);
+ if (ret == 0 && (sp = krb5_storage_from_data(&reply)) == NULL)
+ ret = krb5_enomem(context->context);
+ if (ret)
+ goto out;
+ ret = krb5_ret_int32(sp, &tmp);
+ if (ret == 0)
+ ret = tmp;
+ if (!stop) {
+ /*
+ * Send our "interrupt" after the last chunk if we hand't
+ * interrupted already.
+ */
+ krb5_storage_free(sp);
+ if ((sp = krb5_storage_emem()) &&
+ krb5_store_int32(sp, kadm_nop) == 0)
+ (void) _kadm5_client_send(context, sp);
+ }
+ } else {
+ size_t n = tmp;
+
+ /* Old server -- listing not online */
+ for (i = 0; i < n; i++) {
+ char *princ = NULL;
+
+ ret = krb5_ret_string(sp, &princ);
+ if (ret)
+ goto out;
+ cb(cbdata, princ);
+ free(princ);
+ }
+ }
+
+ out:
+ krb5_clear_error_message(context->context);
+
+ out_keep_error:
+ krb5_storage_free(sp);
+ krb5_data_free(&reply);
+ return ret;
+}
diff --git a/third_party/heimdal/lib/kadm5/get_princs_s.c b/third_party/heimdal/lib/kadm5/get_princs_s.c
index 27fac2bbb0b..14d3907d759 100644
--- a/third_party/heimdal/lib/kadm5/get_princs_s.c
+++ b/third_party/heimdal/lib/kadm5/get_princs_s.c
@@ -39,17 +39,27 @@ struct foreach_data {
const char *exp;
char *exp2;
char **princs;
- int count;
+ size_t nalloced;
+ size_t count;
};
static krb5_error_code
add_princ(krb5_context context, struct foreach_data *d, char *princ)
{
- char **tmp;
- tmp = realloc(d->princs, (d->count + 1) * sizeof(*tmp));
- if (tmp == NULL)
- return krb5_enomem(context);
- d->princs = tmp;
+
+ if (d->count == INT_MAX)
+ return ERANGE;
+ if (d->nalloced == d->count) {
+ size_t n = d->nalloced + (d->nalloced >> 1) + 128; /* No O(N^2) pls */
+ char **tmp;
+
+ if (SIZE_MAX / sizeof(*tmp) <= n)
+ return ERANGE;
+ if ((tmp = realloc(d->princs, n * sizeof(*tmp))) == NULL)
+ return krb5_enomem(context);
+ d->princs = tmp;
+ d->nalloced = n;
+ }
d->princs[d->count++] = princ;
return 0;
}
@@ -84,7 +94,7 @@ kadm5_s_get_principals(void *server_handle,
{
struct foreach_data d;
kadm5_server_context *context = server_handle;
- kadm5_ret_t ret;
+ kadm5_ret_t ret = 0;
if (!context->keep_open) {
ret = context->db->hdb_open(context->context, context->db, O_RDONLY, 0);
@@ -94,33 +104,103 @@ kadm5_s_get_principals(void *server_handle,
}
}
d.exp = expression;
- {
+ d.exp2 = NULL;
+ if (expression) {
krb5_realm r;
int aret;
ret = krb5_get_default_realm(context->context, &r);
- if (ret)
- goto out;
- aret = asprintf(&d.exp2, "%s@%s", expression, r);
- free(r);
- if (aret == -1 || d.exp2 == NULL) {
- ret = krb5_enomem(context->context);
- goto out;
- }
+ if (ret == 0) {
+ aret = asprintf(&d.exp2, "%s@%s", expression, r);
+ free(r);
+ if (aret == -1 || d.exp2 == NULL)
+ ret = krb5_enomem(context->context);
+ }
}
d.princs = NULL;
+ d.nalloced = 0;
d.count = 0;
- ret = hdb_foreach(context->context, context->db, HDB_F_ADMIN_DATA, foreach, &d);
+ if (ret == 0)
+ ret = hdb_foreach(context->context, context->db, HDB_F_ADMIN_DATA,
+ foreach, &d);
if (ret == 0)
ret = add_princ(context->context, &d, NULL);
- if (ret == 0){
+ if (d.count >= INT_MAX)
+ *count = INT_MAX;
+ else
+ *count = d.count - 1;
+ if (ret == 0)
*princs = d.princs;
- *count = d.count - 1;
- } else
- kadm5_free_name_list(context, d.princs, &d.count);
+ else
+ kadm5_free_name_list(context, d.princs, count);
+ free(d.exp2);
+ if (!context->keep_open)
+ context->db->hdb_close(context->context, context->db);
+ return _kadm5_error_code(ret);
+}
+
+struct foreach_online_data {
+ const char *exp;
+ char *exp2;
+ int (*cb)(void *, const char *);
+ void *cbdata;
+};
+
+static krb5_error_code
+foreach_online(krb5_context context, HDB *db, hdb_entry *ent, void *data)
+{
+ struct foreach_online_data *d = data;
+ krb5_error_code ret;
+ char *princ = NULL;
+
+ ret = krb5_unparse_name(context, ent->principal, &princ);
+ if (ret == 0) {
+ if (!d->exp ||
+ fnmatch(d->exp, princ, 0) == 0 || fnmatch(d->exp2, princ, 0) == 0)
+ ret = d->cb(d->cbdata, princ);
+ free(princ);
+ }
+ return ret;
+}
+
+kadm5_ret_t
+kadm5_s_iter_principals(void *server_handle,
+ const char *expression,
+ int (*cb)(void *, const char *),
+ void *cbdata)
+{
+ struct foreach_online_data d;
+ kadm5_server_context *context = server_handle;
+ kadm5_ret_t ret = 0;
+
+ if (!context->keep_open) {
+ ret = context->db->hdb_open(context->context, context->db, O_RDONLY, 0);
+ if (ret) {
+ krb5_warn(context->context, ret, "opening database");
+ return ret;
+ }
+ }
+ d.exp = expression;
+ d.exp2 = NULL;
+ d.cb = cb;
+ d.cbdata = cbdata;
+ if (expression) {
+ krb5_realm r;
+ int aret;
+
+ ret = krb5_get_default_realm(context->context, &r);
+ if (ret == 0) {
+ aret = asprintf(&d.exp2, "%s@%s", expression, r);
+ free(r);
+ if (aret == -1 || d.exp2 == NULL)
+ ret = krb5_enomem(context->context);
+ }
+ }
+ if (ret == 0)
+ ret = hdb_foreach(context->context, context->db, HDB_F_ADMIN_DATA,
+ foreach_online, &d);
free(d.exp2);
- out:
if (!context->keep_open)
context->db->hdb_close(context->context, context->db);
return _kadm5_error_code(ret);
diff --git a/third_party/heimdal/lib/kadm5/init_c.c b/third_party/heimdal/lib/kadm5/init_c.c
index 5d585d1a295..ffbb639581c 100644
--- a/third_party/heimdal/lib/kadm5/init_c.c
+++ b/third_party/heimdal/lib/kadm5/init_c.c
@@ -78,6 +78,8 @@ set_funcs(kadm5_client_context *c)
SET(c, lock);
SET(c, unlock);
SETNOTIMP(c, setkey_principal_3);
+ SET(c, iter_principals);
+ SET(c, dup_context);
}
kadm5_ret_t
@@ -192,6 +194,56 @@ _kadm5_c_init_context(kadm5_client_context **ctx,
return 0;
}
+kadm5_ret_t
+kadm5_c_dup_context(void *vin, void **out)
+{
+ krb5_error_code ret;
+ kadm5_client_context *in = vin;
+ krb5_context context = in->context;
+ kadm5_client_context *ctx;
+
+ *out = NULL;
+ ctx = malloc(sizeof(*ctx));
+ if (ctx == NULL)
+ return krb5_enomem(in->context);
+
+
+ memset(ctx, 0, sizeof(*ctx));
+ set_funcs(ctx);
+ ctx->readonly_kadmind_port = in->readonly_kadmind_port;
+ ctx->kadmind_port = in->kadmind_port;
+
+ ret = krb5_copy_context(context, &(ctx->context));
+ if (ret == 0) {
+ ctx->my_context = TRUE;
+ ret = krb5_add_et_list(ctx->context, initialize_kadm5_error_table_r);
+ }
+ if (ret == 0 && (ctx->realm = strdup(in->realm)) == NULL)
+ ret = krb5_enomem(context);
+ if (ret == 0 &&
+ (ctx->admin_server = strdup(in->admin_server)) == NULL)
+ ret = krb5_enomem(context);
+ if (in->readonly_admin_server &&
+ (ctx->readonly_admin_server = strdup(in->readonly_admin_server)) == NULL)
+ ret = krb5_enomem(context);
+ if (in->keytab && (ctx->keytab = strdup(in->keytab)) == NULL)
+ ret = krb5_enomem(context);
+ if (in->ccache) {
+ char *fullname = NULL;
+
+ ret = krb5_cc_get_full_name(context, in->ccache, &fullname);
+ if (ret == 0)
+ ret = krb5_cc_resolve(context, fullname, &ctx->ccache);
+ free(fullname);
+ }
+ ctx->sock = -1;
+ if (ret == 0)
+ *out = ctx;
+ else
+ kadm5_c_destroy(ctx);
+ return ret;
+}
+
static krb5_error_code
get_kadm_ticket(krb5_context context,
krb5_ccache id,
diff --git a/third_party/heimdal/lib/kadm5/init_s.c b/third_party/heimdal/lib/kadm5/init_s.c
index 1b1d7f2ff58..35402d88a52 100644
--- a/third_party/heimdal/lib/kadm5/init_s.c
+++ b/third_party/heimdal/lib/kadm5/init_s.c
@@ -109,6 +109,21 @@ kadm5_s_init_with_context(krb5_context context,
}
kadm5_ret_t
+kadm5_s_dup_context(void *vin, void **out)
+{
+ kadm5_server_context *in = vin;
+ kadm5_ret_t ret;
+ char *p = NULL;
+
+ ret = krb5_unparse_name(in->context, in->caller, &p);
+ if (ret == 0)
+ ret = kadm5_s_init_with_context(in->context, p, NULL,
+ &in->config, 0, 0, out);
+ free(p);
+ return ret;
+}
+
+kadm5_ret_t
kadm5_s_init_with_password_ctx(krb5_context context,
const char *client_name,
const char *password,
diff --git a/third_party/heimdal/lib/kadm5/iprop.8 b/third_party/heimdal/lib/kadm5/iprop.8
index b881c441a1a..41aac061829 100644
--- a/third_party/heimdal/lib/kadm5/iprop.8
+++ b/third_party/heimdal/lib/kadm5/iprop.8
@@ -68,6 +68,8 @@
.Oo Fl r Ar string \*(Ba Xo Fl Fl realm= Ns Ar string Xc Oc
.Oo Fl d Ar file \*(Ba Xo Fl Fl database= Ns Ar file Xc Oc
.Oo Fl k Ar kspec \*(Ba Xo Fl Fl keytab= Ns Ar kspec Xc Oc
+.Oo Xo Fl Fl no-keytab Xc Oc
+.Oo Xo Fl Fl cache= Ns Ar cspec Xc Oc
.Op Fl Fl statusfile= Ns Ar file
.Op Fl Fl hostname= Ns Ar hostname
.Op Fl Fl port= Ns Ar port
@@ -125,10 +127,40 @@ This should normally be defined as
in
.Pa /etc/services
or another source of the services database.
-The master and slaves
-must each have access to a keytab with keys for the
-.Nm iprop
-service principal on the local host.
+.Pp
+The
+.Nm ipropd-master
+and
+.Nm ipropd-slave
+programs require acceptor and initiator credentials,
+respectively, for host-based principals for the
+.Ar iprop
+service and the fully-qualified hostnames of the hosts on which
+they run.
+.Pp
+The
+.Nm ipropd-master
+program uses, by default, the HDB-backed keytab
+.Ar HDBGET: ,
+though a file-based keytab can also be specified.
+.Pp
+The
+.Nm ipropd-slave
+program uses the default keytab, which is specified by the
+.Ev KRB5_KTNAME
+environment variable, or the value of the
+.Ar default_keytab_name
+configuration parameter in the
+.Ar [libdefaults]
+section.
+However, if the
+.Fl Fl no-keytab
+option is given, then
+.Nm ipropd-slave
+will use the given or default credentials cache, and it will
+expect that cache to be kept fresh externally (such as by the
+.Nm kinit
+program).
.Pp
There is a keep-alive feature logged in the master's
.Pa slave-stats
@@ -150,6 +182,11 @@ Supported options for
Keytab for authenticating
.Nm ipropd-slave
clients.
+.It Fl Fl cache= Ns Ar cspec
+If the keytab given is the empty string then credentials will be
+used from the default credentials cache, or from the
+.Ar cspec
+if given.
.It Fl d Ar file , Fl Fl database= Ns Ar file
Database (default per KDC)
.It Fl Fl slave-stats-file= Ns Ar file
@@ -201,6 +238,7 @@ in the database directory, or in the directory named by the
.Ev HEIM_PIDFILE_DIR
environment variable.
.Sh SEE ALSO
+.Xr kinit 1 ,
.Xr krb5.conf 5 ,
.Xr hprop 8 ,
.Xr hpropd 8 ,
diff --git a/third_party/heimdal/lib/kadm5/ipropd_slave.c b/third_party/heimdal/lib/kadm5/ipropd_slave.c
index 2b1be00ea60..f92b20c8569 100644
--- a/third_party/heimdal/lib/kadm5/ipropd_slave.c
+++ b/third_party/heimdal/lib/kadm5/ipropd_slave.c
@@ -39,6 +39,9 @@ static const char *config_name = "ipropd-slave";
static int verbose;
static int async_hdb = 0;
+static int no_keytab_flag;
+static char *ccache_str;
+static char *keytab_str;
static krb5_log_facility *log_facility;
static char five_min[] = "5 min";
@@ -115,8 +118,7 @@ connect_to_master (krb5_context context, const char *master,
}
static void
-get_creds(krb5_context context, const char *keytab_str,
- krb5_ccache *cache, const char *serverhost)
+get_creds(krb5_context context, krb5_ccache *cache, const char *serverhost)
{
krb5_keytab keytab;
krb5_principal client;
@@ -127,13 +129,33 @@ get_creds(krb5_context context, const char *keytab_str,
char keytab_buf[256];
int aret;
+ if (no_keytab_flag) {
+ /* We're using an externally refreshed ccache */
+ if (*cache == NULL) {
+ if (ccache_str == NULL)
+ ret = krb5_cc_default(context, cache);
+ else
+ ret = krb5_cc_resolve(context, ccache_str, cache);
+ if (ret)
+ krb5_err(context, 1, ret, "Could not resolve the default cache");
+ }
+ return;
+ }
+
if (keytab_str == NULL) {
ret = krb5_kt_default_name (context, keytab_buf, sizeof(keytab_buf));
- if (ret)
- krb5_err (context, 1, ret, "krb5_kt_default_name");
- keytab_str = keytab_buf;
+ if (ret == 0) {
+ keytab_str = keytab_buf;
+ } else {
+ krb5_warn(context, ret, "Using HDBGET: as the default keytab");
+ keytab_str = "HDBGET:";
+ }
}
+ if (*cache)
+ krb5_cc_destroy(context, *cache);
+ *cache = NULL;
+
ret = krb5_kt_resolve(context, keytab_str, &keytab);
if(ret)
krb5_err(context, 1, ret, "%s", keytab_str);
@@ -690,7 +712,6 @@ static char *status_file;
static char *config_file;
static int version_flag;
static int help_flag;
-static char *keytab_str;
static char *port_str;
static int detach_from_console;
static int daemon_child = -1;
@@ -699,8 +720,12 @@ static struct getargs args[] = {
{ "config-file", 'c', arg_string, &config_file, NULL, NULL },
{ "realm", 'r', arg_string, &realm, NULL, NULL },
{ "database", 'd', arg_string, &database, "database", "file"},
+ { "no-keytab", 0, arg_flag, &no_keytab_flag,
+ "use externally refreshed cache", NULL },
+ { "ccache", 0, arg_string, &ccache_str,
+ "client credentials", "CCACHE" },
{ "keytab", 'k', arg_string, &keytab_str,
- "keytab to get authentication from", "kspec" },
+ "client credentials keytab", "KEYTAB" },
{ "time-lost", 0, arg_string, &server_time_lost,
"time before server is considered lost", "time" },
{ "status-file", 0, arg_string, &status_file,
@@ -740,7 +765,7 @@ main(int argc, char **argv)
kadm5_server_context *server_context;
kadm5_config_params conf;
int master_fd;
- krb5_ccache ccache;
+ krb5_ccache ccache = NULL;
krb5_principal server;
char **files;
int optidx = 0;
@@ -858,7 +883,7 @@ main(int argc, char **argv)
if (ret)
krb5_err(context, 1, ret, "db->close");
- get_creds(context, keytab_str, &ccache, master);
+ get_creds(context, &ccache, master);
ret = krb5_sname_to_principal (context, master, IPROP_NAME,
KRB5_NT_SRV_HST, &server);
@@ -923,8 +948,7 @@ main(int argc, char **argv)
if (auth_context) {
krb5_auth_con_free(context, auth_context);
auth_context = NULL;
- krb5_cc_destroy(context, ccache);
- get_creds(context, keytab_str, &ccache, master);
+ get_creds(context, &ccache, master);
}
if (verbose)
krb5_warnx(context, "authenticating to master");
diff --git a/third_party/heimdal/lib/kadm5/libkadm5srv-exports.def b/third_party/heimdal/lib/kadm5/libkadm5srv-exports.def
index 72ba4174c1f..56366ae2046 100644
--- a/third_party/heimdal/lib/kadm5/libkadm5srv-exports.def
+++ b/third_party/heimdal/lib/kadm5/libkadm5srv-exports.def
@@ -15,6 +15,7 @@ EXPORTS
kadm5_delete_policy
kadm5_delete_principal
kadm5_destroy
+ kadm5_dup_context
kadm5_flush
kadm5_free_key_data
kadm5_free_name_list
@@ -32,6 +33,7 @@ EXPORTS
kadm5_init_with_password_ctx
kadm5_init_with_skey
kadm5_init_with_skey_ctx
+ kadm5_iter_principals
kadm5_lock
kadm5_modify_policy
kadm5_modify_principal
diff --git a/third_party/heimdal/lib/kadm5/private.h b/third_party/heimdal/lib/kadm5/private.h
index 88c2f2823d2..1cb8e3918f5 100644
--- a/third_party/heimdal/lib/kadm5/private.h
+++ b/third_party/heimdal/lib/kadm5/private.h
@@ -68,6 +68,8 @@ struct kadm_func {
int, krb5_key_salt_tuple *,
krb5_keyblock *, int);
kadm5_ret_t (*prune_principal) (void *, krb5_principal, int);
+ kadm5_ret_t (*iter_principals) (void*, const char*, int (*)(void *, const char *), void *);
+ kadm5_ret_t (*dup_context) (void*, void **);
};
typedef struct kadm5_hook_context {
diff --git a/third_party/heimdal/lib/kadm5/version-script-client.map b/third_party/heimdal/lib/kadm5/version-script-client.map
index 4d89fabf308..baae30b1173 100644
--- a/third_party/heimdal/lib/kadm5/version-script-client.map
+++ b/third_party/heimdal/lib/kadm5/version-script-client.map
@@ -11,6 +11,7 @@ HEIMDAL_KADM5_CLIENT_1.0 {
kadm5_c_create_principal;
kadm5_c_delete_principal;
kadm5_c_destroy;
+ kadm5_c_dup_context;
kadm5_c_flush;
kadm5_c_get_principal;
kadm5_c_get_principals;
@@ -21,6 +22,8 @@ HEIMDAL_KADM5_CLIENT_1.0 {
kadm5_c_init_with_password_ctx;
kadm5_c_init_with_skey;
kadm5_c_init_with_skey_ctx;
+ kadm5_c_iter_principals;
+ kadm5_c_get_privs;
kadm5_c_modify_principal;
kadm5_c_prune_principal;
kadm5_c_randkey_principal;
@@ -30,6 +33,7 @@ HEIMDAL_KADM5_CLIENT_1.0 {
kadm5_create_principal;
kadm5_delete_principal;
kadm5_destroy;
+ kadm5_dup_context;
kadm5_flush;
kadm5_free_key_data;
kadm5_free_name_list;
@@ -43,6 +47,7 @@ HEIMDAL_KADM5_CLIENT_1.0 {
kadm5_init_with_password_ctx;
kadm5_init_with_skey;
kadm5_init_with_skey_ctx;
+ kadm5_iter_principals;
kadm5_modify_principal;
kadm5_randkey_principal;
kadm5_randkey_principal_3;
diff --git a/third_party/heimdal/lib/kadm5/version-script.map b/third_party/heimdal/lib/kadm5/version-script.map
index e4b4100f334..6de88fc2217 100644
--- a/third_party/heimdal/lib/kadm5/version-script.map
+++ b/third_party/heimdal/lib/kadm5/version-script.map
@@ -16,6 +16,7 @@ HEIMDAL_KAMD5_SERVER_1.0 {
kadm5_create_principal_3;
kadm5_delete_principal;
kadm5_destroy;
+ kadm5_dup_context;
kadm5_decrypt_key;
kadm5_delete_policy;
kadm5_flush;
@@ -35,6 +36,7 @@ HEIMDAL_KAMD5_SERVER_1.0 {
kadm5_init_with_password_ctx;
kadm5_init_with_skey;
kadm5_init_with_skey_ctx;
+ kadm5_iter_principals;
kadm5_lock;
kadm5_modify_principal;
kadm5_modify_policy;
diff --git a/third_party/heimdal/lib/kafs/kafs_locl.h b/third_party/heimdal/lib/kafs/kafs_locl.h
index 6f34ca63170..f4e2f64b407 100644
--- a/third_party/heimdal/lib/kafs/kafs_locl.h
+++ b/third_party/heimdal/lib/kafs/kafs_locl.h
@@ -96,7 +96,6 @@
#endif
#ifdef KRB5
#include "crypto-headers.h"
-#include <krb5-v4compat.h>
typedef struct credentials CREDENTIALS;
#endif /* KRB5 */
#ifndef NO_AFS
diff --git a/third_party/heimdal/lib/kafs/rxkad_kdf.c b/third_party/heimdal/lib/kafs/rxkad_kdf.c
index 5af391ed99b..b542e8916b5 100644
--- a/third_party/heimdal/lib/kafs/rxkad_kdf.c
+++ b/third_party/heimdal/lib/kafs/rxkad_kdf.c
@@ -209,7 +209,7 @@ _kafs_derive_des_key(krb5_enctype enctype, void *keydata, size_t keylen,
ret = compress_parity_bits(keydata, &keylen);
if (ret)
return ret;
- fallthrough;
+ HEIM_FALLTHROUGH;
default:
if (enctype < 0)
return KRB5_PROG_ETYPE_NOSUPP;
diff --git a/third_party/heimdal/lib/krb5/Makefile.am b/third_party/heimdal/lib/krb5/Makefile.am
index c1345c28a5a..ecce461dd89 100644
--- a/third_party/heimdal/lib/krb5/Makefile.am
+++ b/third_party/heimdal/lib/krb5/Makefile.am
@@ -193,7 +193,6 @@ dist_libkrb5_la_SOURCES = \
keytab_keyfile.c \
keytab_memory.c \
krb5_locl.h \
- krb5-v4compat.h \
krcache.c \
krbhst.c \
kuserok.c \
diff --git a/third_party/heimdal/lib/krb5/NTMakefile b/third_party/heimdal/lib/krb5/NTMakefile
index 40ca0fb0bcc..993e76fcc23 100644
--- a/third_party/heimdal/lib/krb5/NTMakefile
+++ b/third_party/heimdal/lib/krb5/NTMakefile
@@ -2,19 +2,19 @@
#
# Copyright (c) 2009 - 2017, Secure Endpoints Inc.
# All rights reserved.
-#
+#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
-#
+#
# - Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
-#
+#
# - Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
-#
+#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
@@ -27,7 +27,7 @@
# 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.
-#
+#
RELDIR=lib\krb5
@@ -184,7 +184,6 @@ INCFILES= \
$(INCDIR)\krb5_locl.h \
$(INCDIR)\krb5-protos.h \
$(INCDIR)\krb5-private.h \
- $(INCDIR)\krb5-v4compat.h \
$(INCDIR)\crypto.h \
$(INCDIR)\an2ln_plugin.h \
$(INCDIR)\ccache_plugin.h \
@@ -267,7 +266,6 @@ dist_libkrb5_la_SOURCES = \
keytab_keyfile.c \
keytab_memory.c \
krb5_locl.h \
- krb5-v4compat.h \
krbhst.c \
kuserok.c \
kx509.c \
diff --git a/third_party/heimdal/lib/krb5/aname_to_localname.c b/third_party/heimdal/lib/krb5/aname_to_localname.c
index 11f270d4978..8515c306e69 100644
--- a/third_party/heimdal/lib/krb5/aname_to_localname.c
+++ b/third_party/heimdal/lib/krb5/aname_to_localname.c
@@ -420,6 +420,7 @@ an2ln_def_plug_an2ln(void *plug_ctx, krb5_context context,
heim_dict_set_value(db_options, HSTR("read-only"),
heim_number_create(1));
dbh = heim_db_create(NULL, an2ln_db_fname, db_options, &error);
+ heim_release(db_options);
if (dbh == NULL) {
krb5_set_error_message(context, heim_error_get_code(error),
N_("Couldn't open aname2lname-text-db", ""));
diff --git a/third_party/heimdal/lib/krb5/changepw.c b/third_party/heimdal/lib/krb5/changepw.c
index 12f0b154689..1982925bf06 100644
--- a/third_party/heimdal/lib/krb5/changepw.c
+++ b/third_party/heimdal/lib/krb5/changepw.c
@@ -384,7 +384,7 @@ process_reply (krb5_context context,
ap_rep_data.data = reply + 6;
ap_rep_data.length = (reply[4] << 8) | (reply[5]);
- if (reply + len < (u_char *)ap_rep_data.data + ap_rep_data.length) {
+ if (len - 6 < ap_rep_data.length) {
str2data (result_string, "client: wrong AP len in reply");
*result_code = KRB5_KPASSWD_MALFORMED;
return 0;
diff --git a/third_party/heimdal/lib/krb5/context.c b/third_party/heimdal/lib/krb5/context.c
index 4040a983518..d6ddd902d64 100644
--- a/third_party/heimdal/lib/krb5/context.c
+++ b/third_party/heimdal/lib/krb5/context.c
@@ -545,7 +545,7 @@ copy_etypes (krb5_context context,
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_copy_context(krb5_context context, krb5_context *out)
{
- krb5_error_code ret;
+ krb5_error_code ret = 0;
krb5_context p;
*out = NULL;
@@ -554,81 +554,80 @@ krb5_copy_context(krb5_context context, krb5_context *out)
if (p == NULL)
return krb5_enomem(context);
- if ((p->hcontext = heim_context_init()) == NULL) {
- ret = ENOMEM;
- goto out;
- }
+ p->cc_ops = NULL;
+ p->etypes = NULL;
+ p->kt_types = NULL;
+ p->cfg_etypes = NULL;
+ p->etypes_des = NULL;
+ p->default_realms = NULL;
+ p->extra_addresses = NULL;
+ p->ignore_addresses = NULL;
- heim_context_set_log_utc(p->hcontext, context->log_utc);
-
- if (context->default_cc_name &&
- (p->default_cc_name = strdup(context->default_cc_name)) == NULL) {
+ if ((p->hcontext = heim_context_init()) == NULL)
ret = ENOMEM;
- goto out;
+
+ if (ret == 0) {
+ heim_context_set_log_utc(p->hcontext, context->log_utc);
+ ret = _krb5_config_copy(context, context->cf, &p->cf);
}
- if (context->default_cc_name_env &&
- (p->default_cc_name_env =
- strdup(context->default_cc_name_env)) == NULL) {
- ret = ENOMEM;
- goto out;
+ if (ret == 0)
+ ret = init_context_from_config_file(p);
+ if (ret == 0 && context->default_cc_name) {
+ free(p->default_cc_name);
+ if ((p->default_cc_name = strdup(context->default_cc_name)) == NULL)
+ ret = ENOMEM;
}
- if (context->configured_default_cc_name &&
- (p->configured_default_cc_name =
- strdup(context->configured_default_cc_name)) == NULL) {
- ret = ENOMEM;
- goto out;
+ if (ret == 0 && context->default_cc_name_env) {
+ free(p->default_cc_name_env);
+ if ((p->default_cc_name_env =
+ strdup(context->default_cc_name_env)) == NULL)
+ ret = ENOMEM;
+ }
+ if (ret == 0 && context->configured_default_cc_name) {
+ free(context->configured_default_cc_name);
+ if ((p->configured_default_cc_name =
+ strdup(context->configured_default_cc_name)) == NULL)
+ ret = ENOMEM;
}
- if (context->etypes) {
+ if (ret == 0 && context->etypes) {
+ free(p->etypes);
ret = copy_etypes(context, context->etypes, &p->etypes);
- if (ret)
- goto out;
}
- if (context->cfg_etypes) {
+ if (ret == 0 && context->cfg_etypes) {
+ free(p->cfg_etypes);
ret = copy_etypes(context, context->cfg_etypes, &p->cfg_etypes);
- if (ret)
- goto out;
}
- if (context->etypes_des) {
+ if (ret == 0 && context->etypes_des) {
+ free(p->etypes_des);
ret = copy_etypes(context, context->etypes_des, &p->etypes_des);
- if (ret)
- goto out;
}
- if (context->default_realms) {
+ if (ret == 0 && context->default_realms) {
+ krb5_free_host_realm(context, p->default_realms);
ret = krb5_copy_host_realm(context,
context->default_realms, &p->default_realms);
- if (ret)
- goto out;
}
- ret = _krb5_config_copy(context, context->cf, &p->cf);
- if (ret)
- goto out;
-
/* XXX should copy */
- _krb5_init_ets(p);
-
- cc_ops_copy(p, context);
- kt_ops_copy(p, context);
-
- ret = krb5_set_extra_addresses(p, context->extra_addresses);
- if (ret)
- goto out;
- ret = krb5_set_extra_addresses(p, context->ignore_addresses);
- if (ret)
- goto out;
-
- ret = _krb5_copy_send_to_kdc_func(p, context);
- if (ret)
- goto out;
-
- *out = p;
-
- return 0;
-
- out:
- krb5_free_context(p);
+ if (ret == 0)
+ _krb5_init_ets(p);
+
+ if (ret == 0)
+ ret = cc_ops_copy(p, context);
+ if (ret == 0)
+ ret = kt_ops_copy(p, context);
+ if (ret == 0)
+ ret = krb5_set_extra_addresses(p, context->extra_addresses);
+ if (ret == 0)
+ ret = krb5_set_extra_addresses(p, context->ignore_addresses);
+ if (ret == 0)
+ ret = _krb5_copy_send_to_kdc_func(p, context);
+
+ if (ret == 0)
+ *out = p;
+ else
+ krb5_free_context(p);
return ret;
}
diff --git a/third_party/heimdal/lib/krb5/convert_creds.c b/third_party/heimdal/lib/krb5/convert_creds.c
index fc371c63776..56261b29fa6 100644
--- a/third_party/heimdal/lib/krb5/convert_creds.c
+++ b/third_party/heimdal/lib/krb5/convert_creds.c
@@ -32,7 +32,6 @@
*/
#include "krb5_locl.h"
-#include "krb5-v4compat.h"
#ifndef HEIMDAL_SMALLER
@@ -58,7 +57,6 @@ krb524_convert_creds_kdc(krb5_context context,
struct credentials *v4creds)
KRB5_DEPRECATED_FUNCTION("Use X instead")
{
- memset(v4creds, 0, sizeof(*v4creds));
krb5_set_error_message(context, EINVAL,
N_("krb524_convert_creds_kdc not supported", ""));
return EINVAL;
@@ -86,7 +84,6 @@ krb524_convert_creds_kdc_ccache(krb5_context context,
struct credentials *v4creds)
KRB5_DEPRECATED_FUNCTION("Use X instead")
{
- memset(v4creds, 0, sizeof(*v4creds));
krb5_set_error_message(context, EINVAL,
N_("krb524_convert_creds_kdc_ccache not supported", ""));
return EINVAL;
diff --git a/third_party/heimdal/lib/krb5/kcm.c b/third_party/heimdal/lib/krb5/kcm.c
index a75fc03f985..f20fc51936f 100644
--- a/third_party/heimdal/lib/krb5/kcm.c
+++ b/third_party/heimdal/lib/krb5/kcm.c
@@ -238,8 +238,7 @@ kcm_alloc(krb5_context context,
if (residual) {
/* KCM cache names must start with {UID} or {UID}: */
- if (residual[0] != '0')
- plen = strspn(residual, "0123456789");
+ plen = strspn(residual, "0123456789");
if (plen && residual[plen] != ':' && residual[plen] != '\0')
plen = 0;
/*
diff --git a/third_party/heimdal/lib/krb5/keytab.c b/third_party/heimdal/lib/krb5/keytab.c
index 559d640f002..bcb3ed83733 100644
--- a/third_party/heimdal/lib/krb5/keytab.c
+++ b/third_party/heimdal/lib/krb5/keytab.c
@@ -883,7 +883,8 @@ krb5_kt_add_entry(krb5_context context,
id->prefix);
return KRB5_KT_NOWRITE;
}
- entry->timestamp = time(NULL);
+ if (entry->timestamp == 0)
+ entry->timestamp = time(NULL);
return (*id->add)(context, id,entry);
}
diff --git a/third_party/heimdal/lib/krb5/krb5-v4compat.h b/third_party/heimdal/lib/krb5/krb5-v4compat.h
deleted file mode 100644
index 2992976e655..00000000000
--- a/third_party/heimdal/lib/krb5/krb5-v4compat.h
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright (c) 1997 - 2003 Kungliga Tekniska Högskolan
- * (Royal Institute of Technology, Stockholm, Sweden).
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * 3. Neither the name of the Institute nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 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.
- */
-
-/* $Id$ */
-
-#ifndef __KRB5_V4COMPAT_H__
-#define __KRB5_V4COMPAT_H__
-
-#include "krb_err.h"
-
-/*
- * This file must only be included with v4 compat glue stuff in
- * heimdal sources.
- *
- * It MUST NOT be installed.
- */
-
-#define KRB_PROT_VERSION 4
-
-#define AUTH_MSG_KDC_REQUEST (1<<1)
-#define AUTH_MSG_KDC_REPLY (2<<1)
-#define AUTH_MSG_APPL_REQUEST (3<<1)
-#define AUTH_MSG_APPL_REQUEST_MUTUAL (4<<1)
-#define AUTH_MSG_ERR_REPLY (5<<1)
-#define AUTH_MSG_PRIVATE (6<<1)
-#define AUTH_MSG_SAFE (7<<1)
-#define AUTH_MSG_APPL_ERR (8<<1)
-#define AUTH_MSG_KDC_FORWARD (9<<1)
-#define AUTH_MSG_KDC_RENEW (10<<1)
-#define AUTH_MSG_DIE (63<<1)
-
-/* General definitions */
-#define KSUCCESS 0
-#define KFAILURE 255
-
-/* */
-
-#define MAX_KTXT_LEN 1250
-
-#define ANAME_SZ 40
-#define REALM_SZ 40
-#define SNAME_SZ 40
-#define INST_SZ 40
-
-struct ktext {
- unsigned int length; /* Length of the text */
- unsigned char dat[MAX_KTXT_LEN]; /* The data itself */
- uint32_t mbz; /* zero to catch runaway strings */
-};
-
-struct credentials {
- char service[ANAME_SZ]; /* Service name */
- char instance[INST_SZ]; /* Instance */
- char realm[REALM_SZ]; /* Auth domain */
- char session[8]; /* Session key */
- int lifetime; /* Lifetime */
- int kvno; /* Key version number */
- struct ktext ticket_st; /* The ticket itself */
- int32_t issue_date; /* The issue time */
- char pname[ANAME_SZ]; /* Principal's name */
- char pinst[INST_SZ]; /* Principal's instance */
-};
-
-#define TKTLIFENUMFIXED 64
-#define TKTLIFEMINFIXED 0x80
-#define TKTLIFEMAXFIXED 0xBF
-#define TKTLIFENOEXPIRE 0xFF
-#define MAXTKTLIFETIME (30*24*3600) /* 30 days */
-#ifndef NEVERDATE
-#define NEVERDATE ((time_t)0x7fffffffL)
-#endif
-
-#define KERB_ERR_NULL_KEY 10
-
-#define CLOCK_SKEW 5*60
-
-#ifndef TKT_ROOT
-#define TKT_ROOT "%{TEMP}/tkt"
-#endif
-
-struct _krb5_krb_auth_data {
- int8_t k_flags; /* Flags from ticket */
- char *pname; /* Principal's name */
- char *pinst; /* His Instance */
- char *prealm; /* His Realm */
- uint32_t checksum; /* Data checksum (opt) */
- krb5_keyblock session; /* Session Key */
- unsigned char life; /* Life of ticket */
- uint32_t time_sec; /* Time ticket issued */
- uint32_t address; /* Address in ticket */
-};
-
-KRB5_LIB_FUNCTION time_t KRB5_LIB_CALL
-_krb5_krb_life_to_time (int, int);
-
-KRB5_LIB_FUNCTION int KRB5_LIB_CALL
-_krb5_krb_time_to_life (time_t, time_t);
-
-KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
-_krb5_krb_tf_setup (krb5_context, struct credentials *,
- const char *, int);
-
-KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
-_krb5_krb_dest_tkt(krb5_context, const char *);
-
-#define krb_time_to_life _krb5_krb_time_to_life
-#define krb_life_to_time _krb5_krb_life_to_time
-
-#endif /* __KRB5_V4COMPAT_H__ */
diff --git a/third_party/heimdal/lib/krb5/pac.c b/third_party/heimdal/lib/krb5/pac.c
index c8f355c8179..b923981908d 100644
--- a/third_party/heimdal/lib/krb5/pac.c
+++ b/third_party/heimdal/lib/krb5/pac.c
@@ -882,6 +882,7 @@ verify_logonname(krb5_context context,
}
ret = krb5_storage_read(sp, s, len);
if (ret != len) {
+ free(s);
krb5_storage_free(sp);
krb5_set_error_message(context, EINVAL, "Failed to read PAC logon name");
return EINVAL;
@@ -894,8 +895,10 @@ verify_logonname(krb5_context context,
unsigned int flags = WIND_RW_LE;
ucs2 = malloc(sizeof(ucs2[0]) * ucs2len);
- if (ucs2 == NULL)
+ if (ucs2 == NULL) {
+ free(s);
return krb5_enomem(context);
+ }
ret = wind_ucs2read(s, len, &flags, ucs2, &ucs2len);
free(s);
diff --git a/third_party/heimdal/lib/krb5/pkinit.c b/third_party/heimdal/lib/krb5/pkinit.c
index c9a6e3e8f8b..0501728d3e5 100644
--- a/third_party/heimdal/lib/krb5/pkinit.c
+++ b/third_party/heimdal/lib/krb5/pkinit.c
@@ -109,7 +109,7 @@ integer_to_BN(krb5_context context, const char *field, const heim_integer *f)
}
static krb5_error_code
-select_dh_group(krb5_context context, DH *dh, unsigned long bits,
+select_dh_group(krb5_context context, DH *dh, unsigned long min_bits,
struct krb5_dh_moduli **moduli)
{
const struct krb5_dh_moduli *m;
@@ -118,25 +118,25 @@ select_dh_group(krb5_context context, DH *dh, unsigned long bits,
krb5_set_error_message(context, EINVAL,
N_("Did not find a DH group parameter "
"matching requirement of %lu bits", ""),
- bits);
+ min_bits);
return EINVAL;
}
- if (bits == 0) {
+ if (min_bits == 0) {
m = moduli[1]; /* XXX */
if (m == NULL)
m = moduli[0]; /* XXX */
} else {
int i;
for (i = 0; moduli[i] != NULL; i++) {
- if (bits < moduli[i]->bits)
+ if (moduli[i]->bits >= min_bits)
break;
}
if (moduli[i] == NULL) {
krb5_set_error_message(context, EINVAL,
N_("Did not find a DH group parameter "
"matching requirement of %lu bits", ""),
- bits);
+ min_bits);
return EINVAL;
}
m = moduli[i];
@@ -2164,7 +2164,7 @@ static const char *default_moduli_rfc3526_MODP_group14 =
/* name */
"rfc3526-MODP-group14 "
/* bits */
- "1760 "
+ "2048 "
/* p */
"FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
"29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
diff --git a/third_party/heimdal/lib/krb5/send_to_kdc.c b/third_party/heimdal/lib/krb5/send_to_kdc.c
index 086f2edcd5d..a854e5c4ae9 100644
--- a/third_party/heimdal/lib/krb5/send_to_kdc.c
+++ b/third_party/heimdal/lib/krb5/send_to_kdc.c
@@ -1192,7 +1192,7 @@ krb5_sendto_context(krb5_context context,
break;
}
action = KRB5_SENDTO_KRBHST;
- fallthrough;
+ HEIM_FALLTHROUGH;
case KRB5_SENDTO_KRBHST:
if (ctx->krbhst == NULL) {
ret = krb5_krbhst_init_flags(context, realm, type,
@@ -1214,7 +1214,7 @@ krb5_sendto_context(krb5_context context,
handle = heim_retain(ctx->krbhst);
}
action = KRB5_SENDTO_TIMEOUT;
- fallthrough;
+ HEIM_FALLTHROUGH;
case KRB5_SENDTO_TIMEOUT:
/*
diff --git a/third_party/heimdal/lib/krb5/store.c b/third_party/heimdal/lib/krb5/store.c
index 79f8e3adbab..8b966f83e79 100644
--- a/third_party/heimdal/lib/krb5/store.c
+++ b/third_party/heimdal/lib/krb5/store.c
@@ -1002,6 +1002,8 @@ krb5_ret_string(krb5_storage *sp,
{
int ret;
krb5_data data;
+
+ *string = NULL;
ret = krb5_ret_data(sp, &data);
if(ret)
return ret;
diff --git a/third_party/heimdal/lib/libedit/config.h.in b/third_party/heimdal/lib/libedit/config.h.in
index 059e5abc6a4..a9dbc475598 100644
--- a/third_party/heimdal/lib/libedit/config.h.in
+++ b/third_party/heimdal/lib/libedit/config.h.in
@@ -48,8 +48,8 @@
/* Define to 1 if you have the <limits.h> header file. */
#undef HAVE_LIMITS_H
-/* Define to 1 if you have the <memory.h> header file. */
-#undef HAVE_MEMORY_H
+/* Define to 1 if you have the <minix/config.h> header file. */
+#undef HAVE_MINIX_CONFIG_H
/* Define to 1 if you have the <ncurses.h> header file. */
#undef HAVE_NCURSES_H
@@ -67,6 +67,9 @@
/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
+/* Define to 1 if you have the <stdio.h> header file. */
+#undef HAVE_STDIO_H
+
/* Define to 1 if you have the <stdlib.h> header file. */
#undef HAVE_STDLIB_H
@@ -120,6 +123,9 @@
/* Define to 1 if you have the <vfork.h> header file. */
#undef HAVE_VFORK_H
+/* Define to 1 if you have the <wchar.h> header file. */
+#undef HAVE_WCHAR_H
+
/* Define to 1 if you have the `wcsdup' function. */
#undef HAVE_WCSDUP
@@ -160,48 +166,106 @@
/* Define as the return type of signal handlers (`int' or `void'). */
#undef RETSIGTYPE
-/* Define to 1 if you have the ANSI C header files. */
+/* Define to 1 if all of the C90 standard headers exist (not just the ones
+ required in a freestanding environment). This macro is provided for
+ backward compatibility; new code need not use it. */
#undef STDC_HEADERS
/* Enable extensions on AIX 3, Interix. */
#ifndef _ALL_SOURCE
# undef _ALL_SOURCE
#endif
+/* Enable general extensions on macOS. */
+#ifndef _DARWIN_C_SOURCE
+# undef _DARWIN_C_SOURCE
+#endif
+/* Enable general extensions on Solaris. */
+#ifndef __EXTENSIONS__
+# undef __EXTENSIONS__
+#endif
/* Enable GNU extensions on systems that have them. */
#ifndef _GNU_SOURCE
# undef _GNU_SOURCE
#endif
-/* Enable threading extensions on Solaris. */
+/* Enable X/Open compliant socket functions that do not require linking
+ with -lxnet on HP-UX 11.11. */
+#ifndef _HPUX_ALT_XOPEN_SOCKET_API
+# undef _HPUX_ALT_XOPEN_SOCKET_API
+#endif
+/* Identify the host operating system as Minix.
+ This macro does not affect the system headers' behavior.
+ A future release of Autoconf may stop defining this macro. */
+#ifndef _MINIX
+# undef _MINIX
+#endif
+/* Enable general extensions on NetBSD.
+ Enable NetBSD compatibility extensions on Minix. */
+#ifndef _NETBSD_SOURCE
+# undef _NETBSD_SOURCE
+#endif
+/* Enable OpenBSD compatibility extensions on NetBSD.
+ Oddly enough, this does nothing on OpenBSD. */
+#ifndef _OPENBSD_SOURCE
+# undef _OPENBSD_SOURCE
+#endif
+/* Define to 1 if needed for POSIX-compatible behavior. */
+#ifndef _POSIX_SOURCE
+# undef _POSIX_SOURCE
+#endif
+/* Define to 2 if needed for POSIX-compatible behavior. */
+#ifndef _POSIX_1_SOURCE
+# undef _POSIX_1_SOURCE
+#endif
+/* Enable POSIX-compatible threading on Solaris. */
#ifndef _POSIX_PTHREAD_SEMANTICS
# undef _POSIX_PTHREAD_SEMANTICS
#endif
+/* Enable extensions specified by ISO/IEC TS 18661-5:2014. */
+#ifndef __STDC_WANT_IEC_60559_ATTRIBS_EXT__
+# undef __STDC_WANT_IEC_60559_ATTRIBS_EXT__
+#endif
+/* Enable extensions specified by ISO/IEC TS 18661-1:2014. */
+#ifndef __STDC_WANT_IEC_60559_BFP_EXT__
+# undef __STDC_WANT_IEC_60559_BFP_EXT__
+#endif
+/* Enable extensions specified by ISO/IEC TS 18661-2:2015. */
+#ifndef __STDC_WANT_IEC_60559_DFP_EXT__
+# undef __STDC_WANT_IEC_60559_DFP_EXT__
+#endif
+/* Enable extensions specified by ISO/IEC TS 18661-4:2015. */
+#ifndef __STDC_WANT_IEC_60559_FUNCS_EXT__
+# undef __STDC_WANT_IEC_60559_FUNCS_EXT__
+#endif
+/* Enable extensions specified by ISO/IEC TS 18661-3:2015. */
+#ifndef __STDC_WANT_IEC_60559_TYPES_EXT__
+# undef __STDC_WANT_IEC_60559_TYPES_EXT__
+#endif
+/* Enable extensions specified by ISO/IEC TR 24731-2:2010. */
+#ifndef __STDC_WANT_LIB_EXT2__
+# undef __STDC_WANT_LIB_EXT2__
+#endif
+/* Enable extensions specified by ISO/IEC 24747:2009. */
+#ifndef __STDC_WANT_MATH_SPEC_FUNCS__
+# undef __STDC_WANT_MATH_SPEC_FUNCS__
+#endif
/* Enable extensions on HP NonStop. */
#ifndef _TANDEM_SOURCE
# undef _TANDEM_SOURCE
#endif
-/* Enable general extensions on Solaris. */
-#ifndef __EXTENSIONS__
-# undef __EXTENSIONS__
+/* Enable X/Open extensions. Define to 500 only if necessary
+ to make mbstate_t available. */
+#ifndef _XOPEN_SOURCE
+# undef _XOPEN_SOURCE
#endif
/* Version number of package */
#undef VERSION
-/* Define to 1 if on MINIX. */
-#undef _MINIX
-
-/* Define to 2 if the system does not provide POSIX.1 features except with
- this defined. */
-#undef _POSIX_1_SOURCE
-
-/* Define to 1 if you need to in order for `stat' and other things to work. */
-#undef _POSIX_SOURCE
-
/* Define to empty if `const' does not conform to ANSI C. */
#undef const
-/* Define to `int' if <sys/types.h> does not define. */
+/* Define as a signed integer type capable of holding a process identifier. */
#undef pid_t
/* Define to `unsigned int' if <sys/types.h> does not define. */
diff --git a/third_party/heimdal/lib/ntlm/digest.c b/third_party/heimdal/lib/ntlm/digest.c
index 761e1f497fc..42c39ff23e1 100644
--- a/third_party/heimdal/lib/ntlm/digest.c
+++ b/third_party/heimdal/lib/ntlm/digest.c
@@ -471,7 +471,7 @@ heim_digest_generate_challenge(heim_digest_t context)
break;
case HEIM_DIGEST_TYPE_AUTO:
context->type = HEIM_DIGEST_TYPE_RFC2831;
- fallthrough;
+ HEIM_FALLTHROUGH;
case HEIM_DIGEST_TYPE_RFC2831:
asprintf(&challenge, "realm=\"%s\",nonce=\"%s\",qop=\"%s\",algorithm=md5-sess,charset=utf-8,maxbuf=%s",
context->serverRealm, context->serverNonce, context->serverQOP, context->serverMaxbuf);
diff --git a/third_party/heimdal/lib/ntlm/ntlm.c b/third_party/heimdal/lib/ntlm/ntlm.c
index d75752ea000..7384e18076f 100644
--- a/third_party/heimdal/lib/ntlm/ntlm.c
+++ b/third_party/heimdal/lib/ntlm/ntlm.c
@@ -682,7 +682,7 @@ heim_ntlm_decode_type1(const struct ntlm_buf *buf, struct ntlm_type1 *data)
krb5_storage_set_byteorder(in, KRB5_STORAGE_BYTEORDER_LE);
CHECK_SIZE(krb5_storage_read(in, sig, sizeof(sig)), sizeof(sig));
- CHECK(memcmp(ntlmsigature, sig, sizeof(ntlmsigature)), 0);
+ CHECK(ct_memcmp(ntlmsigature, sig, sizeof(ntlmsigature)), 0);
CHECK(krb5_ret_uint32(in, &type), 0);
CHECK(type, 1);
CHECK(krb5_ret_uint32(in, &data->flags), 0);
@@ -844,7 +844,7 @@ heim_ntlm_decode_type2(const struct ntlm_buf *buf, struct ntlm_type2 *type2)
krb5_storage_set_byteorder(in, KRB5_STORAGE_BYTEORDER_LE);
CHECK_SIZE(krb5_storage_read(in, sig, sizeof(sig)), sizeof(sig));
- CHECK(memcmp(ntlmsigature, sig, sizeof(ntlmsigature)), 0);
+ CHECK(ct_memcmp(ntlmsigature, sig, sizeof(ntlmsigature)), 0);
CHECK(krb5_ret_uint32(in, &type), 0);
CHECK(type, 2);
@@ -1001,7 +1001,7 @@ heim_ntlm_decode_type3(const struct ntlm_buf *buf,
krb5_storage_set_byteorder(in, KRB5_STORAGE_BYTEORDER_LE);
CHECK_SIZE(krb5_storage_read(in, sig, sizeof(sig)), sizeof(sig));
- CHECK(memcmp(ntlmsigature, sig, sizeof(ntlmsigature)), 0);
+ CHECK(ct_memcmp(ntlmsigature, sig, sizeof(ntlmsigature)), 0);
CHECK(krb5_ret_uint32(in, &type), 0);
CHECK(type, 3);
CHECK(ret_sec_buffer(in, &lm), 0);
@@ -1825,7 +1825,7 @@ verify_ntlm2(const void *key, size_t len,
if (ret)
goto out;
- if (memcmp(serveranswer, clientanswer, 16) != 0) {
+ if (ct_memcmp(serveranswer, clientanswer, 16) != 0) {
heim_ntlm_free_buf(infotarget);
return HNTLM_ERR_AUTH;
}
diff --git a/third_party/heimdal/lib/otp/otp_verify.c b/third_party/heimdal/lib/otp/otp_verify.c
index 8a1c743cc83..373f9295a5e 100644
--- a/third_party/heimdal/lib/otp/otp_verify.c
+++ b/third_party/heimdal/lib/otp/otp_verify.c
@@ -49,7 +49,7 @@ otp_verify_user_1 (OtpContext *ctx, const char *passwd)
}
memcpy (key2, key1, sizeof(key1));
ctx->alg->next (key2);
- if (memcmp (ctx->key, key2, sizeof(key2)) == 0) {
+ if (ct_memcmp (ctx->key, key2, sizeof(key2)) == 0) {
--ctx->n;
memcpy (ctx->key, key1, sizeof(key1));
return 0;
diff --git a/third_party/heimdal/lib/roken/Makefile.am b/third_party/heimdal/lib/roken/Makefile.am
index 35d753204e7..f6d038f3f6e 100644
--- a/third_party/heimdal/lib/roken/Makefile.am
+++ b/third_party/heimdal/lib/roken/Makefile.am
@@ -201,7 +201,7 @@ libroken_la_LIBADD = @LTLIBOBJS@ $(LIB_crypt) $(LIB_pidfile)
$(LTLIBOBJS) $(libroken_la_OBJECTS): roken.h $(XHEADERS)
-BUILT_SOURCES = roken.h $(err_h) $(fnmatch_h) $(ifadds_h) $(search_h) $(vis_h)
+BUILT_SOURCES = roken.h $(err_h) $(fnmatch_h) $(ifaddrs_h) $(search_h) $(vis_h)
## these are controlled by configure
XHEADERS = $(err_h) $(fnmatch_h) $(ifaddrs_h) $(search_h) $(vis_h)
diff --git a/third_party/heimdal/lib/roken/base32.c b/third_party/heimdal/lib/roken/base32.c
index ef74336d70a..4ec86ae90f1 100644
--- a/third_party/heimdal/lib/roken/base32.c
+++ b/third_party/heimdal/lib/roken/base32.c
@@ -100,10 +100,10 @@ rk_base32_encode(const void *data, int size, char **str, enum rk_base32_flags fl
p[6] = chars[(c & 0x0000000000000003e0ULL) >> 5];
p[7] = chars[(c & 0x00000000000000001fULL) >> 0];
switch (i - size) {
- case 4: p[2] = p[3] = '='; fallthrough;
- case 3: p[4] = '='; fallthrough;
- case 2: p[5] = p[6] = '='; fallthrough;
- case 1: p[7] = '='; fallthrough;
+ case 4: p[2] = p[3] = '='; HEIM_FALLTHROUGH;
+ case 3: p[4] = '='; HEIM_FALLTHROUGH;
+ case 2: p[5] = p[6] = '='; HEIM_FALLTHROUGH;
+ case 1: p[7] = '='; HEIM_FALLTHROUGH;
default: break;
}
p += 8;
diff --git a/third_party/heimdal/lib/roken/dirent-test.c b/third_party/heimdal/lib/roken/dirent-test.c
index dc4518ad5b0..2c6b5055be7 100644
--- a/third_party/heimdal/lib/roken/dirent-test.c
+++ b/third_party/heimdal/lib/roken/dirent-test.c
@@ -148,7 +148,7 @@ int teardown_test(void)
strcmp(dirname + len + 1 - sizeof(TESTDIR)/sizeof(char), TESTDIR) == 0) {
- fallthrough;
+ HEIM_FALLTHROUGH;
} else {
/* did we create the directory? */
@@ -162,7 +162,7 @@ int teardown_test(void)
fprintf(stderr, "Can't change to test directory. Aborting cleanup.\n");
return -1;
} else {
- fallthrough;
+ HEIM_FALLTHROUGH;
}
} else {
return -1;
diff --git a/third_party/heimdal/lib/roken/fnmatch.c b/third_party/heimdal/lib/roken/fnmatch.c
index 74f35283d35..34da19f2cb1 100644
--- a/third_party/heimdal/lib/roken/fnmatch.c
+++ b/third_party/heimdal/lib/roken/fnmatch.c
@@ -129,7 +129,7 @@ rk_fnmatch(const char *pattern, const char *string, int flags)
--pattern;
}
}
- fallthrough;
+ HEIM_FALLTHROUGH;
default:
if (c != *string++)
return (FNM_NOMATCH);
diff --git a/third_party/heimdal/lib/roken/getaddrinfo.c b/third_party/heimdal/lib/roken/getaddrinfo.c
index 12a26a71225..47affba59f5 100644
--- a/third_party/heimdal/lib/roken/getaddrinfo.c
+++ b/third_party/heimdal/lib/roken/getaddrinfo.c
@@ -188,7 +188,7 @@ get_null (const struct addrinfo *hints,
struct addrinfo *first = NULL;
struct addrinfo **current = &first;
int family = PF_UNSPEC;
- int ret;
+ int ret = EAI_FAMILY;
if (hints != NULL)
family = hints->ai_family;
@@ -216,7 +216,7 @@ get_null (const struct addrinfo *hints,
&current, const_v4, &v4_addr, NULL);
}
*res = first;
- return 0;
+ return ret;
}
static int
diff --git a/third_party/heimdal/lib/roken/getuserinfo.c b/third_party/heimdal/lib/roken/getuserinfo.c
index 7fd2ca9f151..09f4c73155a 100644
--- a/third_party/heimdal/lib/roken/getuserinfo.c
+++ b/third_party/heimdal/lib/roken/getuserinfo.c
@@ -136,7 +136,7 @@ roken_get_homedir(char *home, size_t homesz)
}
return home;
}
- fallthrough;
+ HEIM_FALLTHROUGH;
#else
#ifdef HAVE_GETPWNAM_R
size_t buflen = 2048;
diff --git a/third_party/heimdal/lib/roken/parse_units.c b/third_party/heimdal/lib/roken/parse_units.c
index d76578abfe0..017eedf376b 100644
--- a/third_party/heimdal/lib/roken/parse_units.c
+++ b/third_party/heimdal/lib/roken/parse_units.c
@@ -275,8 +275,7 @@ acc_flags(uint64_t res, int64_t val, uint64_t mult)
}
ROKEN_LIB_FUNCTION uint64_t ROKEN_LIB_CALL
-parse_flags (const char *s, const struct units *units,
- int orig)
+parse_flags(const char *s, const struct units *units, uint64_t orig)
{
return parse_something_unsigned (s, units, NULL, acc_flags, orig, 1);
}
diff --git a/third_party/heimdal/lib/roken/parse_units.h b/third_party/heimdal/lib/roken/parse_units.h
index c5b6f2030a8..d17897dda60 100644
--- a/third_party/heimdal/lib/roken/parse_units.h
+++ b/third_party/heimdal/lib/roken/parse_units.h
@@ -97,8 +97,8 @@ ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
print_units_table (const struct units *units, FILE *f);
ROKEN_LIB_FUNCTION uint64_t ROKEN_LIB_CALL
-parse_flags (const char *s, const struct units *units,
- int orig);
+parse_flags(const char *s, const struct units *units,
+ uint64_t orig);
ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
unparse_units(int64_t num, const struct units *units, char *s, size_t len);
diff --git a/third_party/heimdal/lib/roken/snprintf.c b/third_party/heimdal/lib/roken/snprintf.c
index 3da48962ee3..c8afedb2620 100644
--- a/third_party/heimdal/lib/roken/snprintf.c
+++ b/third_party/heimdal/lib/roken/snprintf.c
@@ -515,7 +515,7 @@ xyzprintf (struct snprintf_state *state, const char *char_format, va_list ap)
}
case '\0' :
--format;
- fallthrough;
+ HEIM_FALLTHROUGH;
case '%' :
(*state->append_char)(state, c);
++len;
diff --git a/third_party/heimdal/lib/roken/strftime.c b/third_party/heimdal/lib/roken/strftime.c
index 9a951dd3008..7be930755ca 100644
--- a/third_party/heimdal/lib/roken/strftime.c
+++ b/third_party/heimdal/lib/roken/strftime.c
@@ -377,7 +377,7 @@ strftime (char *buf, size_t maxsize, const char *format,
break;
case '\0' :
--format;
- fallthrough;
+ HEIM_FALLTHROUGH;
case '%' :
ret = snprintf (buf, maxsize - n,
"%%");
diff --git a/third_party/heimdal/lib/roken/strptime.c b/third_party/heimdal/lib/roken/strptime.c
index 86216d2d6dc..b9345c08323 100644
--- a/third_party/heimdal/lib/roken/strptime.c
+++ b/third_party/heimdal/lib/roken/strptime.c
@@ -424,7 +424,7 @@ strptime (const char *buf, const char *format, struct tm *timeptr)
abort ();
case '\0' :
--format;
- fallthrough;
+ HEIM_FALLTHROUGH;
case '%' :
if (*buf == '%')
++buf;
diff --git a/third_party/heimdal/lib/sl/slc-gram.y b/third_party/heimdal/lib/sl/slc-gram.y
index 38045c10048..eed7fc10f04 100644
--- a/third_party/heimdal/lib/sl/slc-gram.y
+++ b/third_party/heimdal/lib/sl/slc-gram.y
@@ -686,7 +686,7 @@ gen_wrapper(struct assignment *as)
(*th->free)(s);
free(s);
}
- cprint(1, "return 0;\n");
+ cprint(1, "return 1;\n");
cprint(0, "}\n");
cprint(0, "\n");
free(n);
diff --git a/third_party/heimdal/lib/wind/utf8.c b/third_party/heimdal/lib/wind/utf8.c
index 452b7b260d8..4559109f3fd 100644
--- a/third_party/heimdal/lib/wind/utf8.c
+++ b/third_party/heimdal/lib/wind/utf8.c
@@ -205,18 +205,18 @@ wind_ucs4utf8(const uint32_t *in, size_t in_len, char *out, size_t *out_len)
case 4:
out[3] = (ch | 0x80) & 0xbf;
ch = ch >> 6;
- fallthrough;
+ HEIM_FALLTHROUGH;
case 3:
out[2] = (ch | 0x80) & 0xbf;
ch = ch >> 6;
- fallthrough;
+ HEIM_FALLTHROUGH;
case 2:
out[1] = (ch | 0x80) & 0xbf;
ch = ch >> 6;
- fallthrough;
+ HEIM_FALLTHROUGH;
case 1:
out[0] = ch | first_char[len - 1];
- fallthrough;
+ HEIM_FALLTHROUGH;
default:
break;
}
@@ -486,14 +486,14 @@ wind_ucs2utf8(const uint16_t *in, size_t in_len, char *out, size_t *out_len)
case 3:
out[2] = (ch | 0x80) & 0xbf;
ch = ch >> 6;
- fallthrough;
+ HEIM_FALLTHROUGH;
case 2:
out[1] = (ch | 0x80) & 0xbf;
ch = ch >> 6;
- fallthrough;
+ HEIM_FALLTHROUGH;
case 1:
out[0] = ch | first_char[len - 1];
- fallthrough;
+ HEIM_FALLTHROUGH;
default:
break;
}
diff --git a/third_party/heimdal/packages/windows/sdk/NTMakefile b/third_party/heimdal/packages/windows/sdk/NTMakefile
index 934b0c17c72..4f2d355bf61 100644
--- a/third_party/heimdal/packages/windows/sdk/NTMakefile
+++ b/third_party/heimdal/packages/windows/sdk/NTMakefile
@@ -76,7 +76,6 @@ INCFILES=\
$(SDKINCDIR)\krb5\k5e1_err.h \
$(SDKINCDIR)\krb5\krb5-protos.h \
$(SDKINCDIR)\krb5\krb5-types.h \
- $(SDKINCDIR)\krb5\krb5-v4compat.h \
$(SDKINCDIR)\krb5\krb5.h \
$(SDKINCDIR)\krb5\krb5_asn1.h \
$(SDKINCDIR)\krb5\krb5_ccapi.h \
@@ -110,7 +109,6 @@ INCFILES=\
$(SDKINCDIR)\heimdal\k5e1_err.h \
$(SDKINCDIR)\heimdal\krb5-protos.h \
$(SDKINCDIR)\heimdal\krb5-types.h \
- $(SDKINCDIR)\heimdal\krb5-v4compat.h \
$(SDKINCDIR)\heimdal\krb5.h \
$(SDKINCDIR)\heimdal\krb5_asn1.h \
$(SDKINCDIR)\heimdal\krb5_ccapi.h \
diff --git a/third_party/heimdal/tests/gss/krb5.conf.in b/third_party/heimdal/tests/gss/krb5.conf.in
index aae031db645..01c4c2e7fa2 100644
--- a/third_party/heimdal/tests/gss/krb5.conf.in
+++ b/third_party/heimdal/tests/gss/krb5.conf.in
@@ -45,6 +45,7 @@ include @srcdirabs@/include-krb5.conf
enable_virtual_hostbased_princs = true
virtual_hostbased_princ_mindots = 1
virtual_hostbased_princ_maxdots = 3
+ same_realm_aliases_are_soft = true
[logging]
kdc = 0-/FILE:@objdir@/messages.log
diff --git a/third_party/heimdal/tests/kdc/check-bx509.in b/third_party/heimdal/tests/kdc/check-bx509.in
index b50239d8440..5109854fc26 100644
--- a/third_party/heimdal/tests/kdc/check-bx509.in
+++ b/third_party/heimdal/tests/kdc/check-bx509.in
@@ -42,18 +42,21 @@ testfailed="echo test failed; cat messages.log; exit 1"
# If there is no useful db support compiled in, disable test
${have_db} || exit 77
+umask 077
+
R=TEST.H5L.SE
DCs="DC=test,DC=h5l,DC=se"
port=@port@
bx509port=@bx509port@
+server=datan.test.h5l.se
+otherserver=other.test.h5l.se
+
kadmin="${kadmin} -l -r $R"
-bx509d="${bx509d} --reverse-proxied -p $bx509port"
+bx509d="${bx509d} --allow-GET --reverse-proxied -p $bx509port -H $server --cert=${objdir}/bx509.pem -t"
kdc="${kdc} --addresses=localhost -P $port"
-server=datan.test.h5l.se
-otherserver=other.test.h5l.se
cachefile="${objdir}/cache.krb5"
cache="FILE:${cachefile}"
cachefile2="${objdir}/cache2.krb5"
@@ -131,6 +134,55 @@ get_cert() {
"$@" "$url"
}
+get_with_token() {
+ if [ -n "$csr" ]; then
+ url="http://${server}:${bx509port}/${1}?csr=$csr${2}"
+ else
+ url="http://${server}:${bx509port}/${1}?${2}"
+ fi
+ shift 2
+
+ curl -fg --resolve ${server}:${bx509port}:127.0.0.1 \
+ -H "Authorization: Negotiate $token" \
+ -D response-headers \
+ "$@" "$url" &&
+ { echo "GET w/o CSRF token succeeded!"; exit 2; }
+ curl -g --resolve ${server}:${bx509port}:127.0.0.1 \
+ -H "Authorization: Negotiate $token" \
+ -D response-headers \
+ "$@" "$url"
+ grep ^X-CSRF-Token: response-headers >/dev/null ||
+ { echo "GET w/o CSRF token did not output a CSRF token!"; exit 2; }
+ curl -fg --resolve ${server}:${bx509port}:127.0.0.1 \
+ -H "Authorization: Negotiate $token" \
+ -H "$(sed -e 's/\r//' response-headers | grep ^X-CSRF-Token:)" \
+ "$@" "$url" ||
+ { echo "GET w/ CSRF failed"; exit 2; }
+}
+
+get_via_POST() {
+ endpoint=$1
+ shift
+
+ curl -fg --resolve ${server}:${bx509port}:127.0.0.1 \
+ -H "Authorization: Negotiate $token" \
+ -X POST -D response-headers \
+ "$@" "http://${server}:${bx509port}/${endpoint}" &&
+ { echo "POST w/o CSRF token succeeded!"; exit 2; }
+ curl -g --resolve ${server}:${bx509port}:127.0.0.1 \
+ -H "Authorization: Negotiate $token" \
+ -X POST -D response-headers \
+ "$@" "http://${server}:${bx509port}/${endpoint}"
+ grep ^X-CSRF-Token: response-headers >/dev/null ||
+ { echo "POST w/o CSRF token did not output a CSRF token!"; exit 2; }
+ curl -fg --resolve ${server}:${bx509port}:127.0.0.1 \
+ -H "Authorization: Negotiate $token" \
+ -H "$(sed -e 's/\r//' response-headers | grep ^X-CSRF-Token:)" \
+ -X POST \
+ "$@" "http://${server}:${bx509port}/${endpoint}" ||
+ { echo "POST w/ CSRF failed"; exit 2; }
+}
+
rm -f $kt $ukt
$ktutil -k $keytab add -r -V 1 -e aes128-cts-hmac-sha1-96 \
-p HTTP/datan.test.h5l.se@${R} ||
@@ -292,15 +344,15 @@ ${kadmin} init \
${R} || exit 1
${kadmin} add -r --use-defaults foo@${R} || exit 1
${kadmin} add -r --use-defaults bar@${R} || exit 1
+${kadmin} add -r --use-defaults baz@${R} || exit 1
${kadmin} modify --pkinit-acl="CN=foo,DC=test,DC=h5l,DC=se" foo@${R} || exit 1
echo "Starting bx509d"
-${bx509d} -H $server --cert=${objdir}/bx509.pem -t --daemon ||
- { echo "bx509 failed to start"; exit 2; }
+${bx509d} --daemon || { echo "bx509 failed to start"; exit 2; }
bx509pid=`getpid bx509d`
-trap "kill -9 ${bx509pid}; echo signal killing bx509d; exit 1;" EXIT
+trap 'kill -9 ${bx509pid}; echo signal killing bx509d; exit 1;' EXIT
ec=0
rm -f trivial.pem server.pem email.pem
@@ -310,12 +362,25 @@ csr_revoke
$hxtool request-create --subject='' --generate-key=rsa --key-bits=1024 \
--key=FILE:"${objdir}/k.der" "${objdir}/req" ||
{ echo "Failed to make a CSR"; exit 2; }
-csr=$($rkbase64 -- ${objdir}/req | $rkvis -h --stdin)
# XXX Add autoconf check for curl?
# Create a barebones bx509 HTTP/1.1 client test program?
+echo "Fetching a trivial user certificate (no authentication, must fail)"
+# Encode the CSR in base64, then URL-encode it
+csr=$($rkbase64 -- ${objdir}/req | $rkvis -h --stdin)
+if (set -vx;
+ curl -g --resolve ${server}:${bx509port}:127.0.0.1 \
+ -sf -o "${objdir}/trivial.pem" \
+ "http://${server}:${bx509port}/bx509?csr=$csr"); then
+ $hxtool print --content "FILE:${objdir}/trivial.pem"
+ echo 'Got a certificate without authenticating!'
+ exit 1
+fi
+
echo "Fetching a trivial user certificate"
+# Encode the CSR in base64, then URL-encode it
+csr=$($rkbase64 -- ${objdir}/req | $rkvis -h --stdin)
token=$(KRB5CCNAME=$cache $gsstoken HTTP@$server)
if (set -vx; get_cert '' -sf -o "${objdir}/trivial.pem"); then
$hxtool print --content "FILE:${objdir}/trivial.pem"
@@ -336,6 +401,43 @@ else
exit 1
fi
+echo "Fetching a trivial user certificate (with POST, no auth, must fail)"
+# Encode the CSR in base64; curl will URL-encode it for us
+csr=$($rkbase64 -- ${objdir}/req)
+if (set -vx;
+ curl -fg --resolve ${server}:${bx509port}:127.0.0.1 \
+ -X POST -D response-headers \
+ -F csr="$csr" -o "${objdir}/trivial.pem" \
+ "http://${server}:${bx509port}/bx509" ); then
+ $hxtool print --content "FILE:${objdir}/trivial.pem"
+ echo 'Got a certificate without authenticating!'
+ exit 1
+fi
+
+echo "Fetching a trivial user certificate (with POST)"
+# Encode the CSR in base64; curl will URL-encode it for us
+csr=$($rkbase64 -- ${objdir}/req)
+token=$(KRB5CCNAME=$cache $gsstoken HTTP@$server)
+if (set -vx;
+ get_via_POST bx509 -F csr="$csr" -o "${objdir}/trivial.pem"); then
+ $hxtool print --content "FILE:${objdir}/trivial.pem"
+ if $hxtool acert --end-entity \
+ --expr="%{certificate.subject} == \"CN=foo,$DCs\"" \
+ -P "foo@${R}" "FILE:${objdir}/trivial.pem"; then
+ echo 'Successfully obtained a trivial client certificate!'
+ else
+ echo 'FAIL: Obtained a trivial client certificate w/o expected PKINIT SAN)'
+ exit 1
+ fi
+ if $hxtool acert --expr="%{certificate.subject} == \"OU=Users,$DCs\"" \
+ --has-private-key "FILE:${objdir}/trivial.pem"; then
+ echo 'Successfully obtained a trivial client certificate!'
+ fi
+else
+ echo 'Failed to get a certificate!'
+ exit 1
+fi
+
echo "Checking that authorization is enforced"
csr_revoke
get_cert '&rfc822Name=foo@bar.example' -vvv -o "${objdir}/bad1.pem"
@@ -430,10 +532,10 @@ ${kadmin} ext_keytab -r -k $ukeytab foo@${R} || exit 1
echo "Starting kdc";
${kdc} --detach --testing || { echo "kdc failed to start"; cat messages.log; exit 1; }
kdcpid=`getpid kdc`
-trap "kill -9 ${kdcpid} ${bx509pid}; echo signal killing kdc and bx509d; exit 1;" EXIT
+trap 'kill -9 ${kdcpid} ${bx509pid}; echo signal killing kdc and bx509d; exit 1;' EXIT
${kinit} -kt $ukeytab foo@${R} || exit 1
-$klist || { echo "failed to setup kimpersonate credentials"; exit 2; }
+$klist || { echo "failed to kinit"; exit 2; }
echo "Fetch TGT (not granted for other)"
token=$(KRB5CCNAME=$cache $gsstoken HTTP@$server)
@@ -474,7 +576,7 @@ if ! (set -vx;
exit 2
fi
${kgetcred} -H HTTP/${server}@${R} ||
- { echo "Trivial offline CA test failed (TGS)"; exit 2; }
+ { echo "Fetched TGT didn't work"; exit 2; }
${klist} | grep Addresses:.IPv4:8.8.8.8 ||
{ echo "Failed to get a TGT with /get-tgt end-point with addresses"; exit 2; }
@@ -491,7 +593,7 @@ if ! (set -vx;
exit 2
fi
${kgetcred} -H HTTP/${server}@${R} ||
- { echo "Trivial offline CA test failed (TGS)"; exit 2; }
+ { echo "Fetched TGT didn't work"; exit 2; }
${klist} | grep Addresses:.IPv4:8.8.8.8 ||
{ echo "Failed to get a TGT with /get-tgt end-point with addresses"; exit 2; }
@@ -509,7 +611,7 @@ if ! (set -vx;
exit 2
fi
${kgetcred} -H HTTP/${server}@${R} ||
- { echo "Trivial offline CA test failed (TGS)"; exit 2; }
+ { echo "Fetched TGT didn't work"; exit 2; }
if which jq >/dev/null; then
if ! ${klistjson} | jq -e '
(reduce (.tickets[0]|(.Issued,.Expires)|
@@ -535,7 +637,7 @@ if ! (set -vx;
exit 2
fi
${kgetcred} -H HTTP/${server}@${R} ||
- { echo "Trivial offline CA test failed (TGS)"; exit 2; }
+ { echo "Fetched TGT didn't work"; exit 2; }
if which jq >/dev/null; then
if ! ${klistjson} | jq -e '
(reduce (.tickets[0]|(.Issued,.Expires)|
@@ -561,7 +663,7 @@ if ! (set -vx;
exit 2
fi
${kgetcred} -H HTTP/${server}@${R} ||
- { echo "Trivial offline CA test failed (TGS)"; exit 2; }
+ { echo "Fetched TGT didn't work"; exit 2; }
if which jq >/dev/null; then
if ! ${klistjson} | jq -e '
(reduce (.tickets[0]|(.Issued,.Expires)|
@@ -573,6 +675,153 @@ if which jq >/dev/null; then
fi
fi
+echo "Fetch TGTs (batch, authz fail)"
+${kadmin} modify --max-ticket-life=10d krbtgt/${R}@${R}
+(set -vx; csr_grant pkinit bar@${R} foo@${R})
+${kdestroy}
+token=$(KRB5CCNAME=$cache2 $gsstoken HTTP@$server)
+if (set -vx;
+ curl -o "${cachefile}.json" -Lgsf \
+ --resolve ${server}:${bx509port}:127.0.0.1 \
+ -H "Authorization: Negotiate $token" \
+ "http://${server}:${bx509port}/get-tgts?cname=bar@${R}&cname=baz@${R}"); then
+ echo "Got TGTs with /get-tgts end-point that should have been denied"
+ exit 2
+fi
+
+echo "Fetch TGTs (batch, authz pass)"
+${kadmin} modify --max-ticket-life=10d krbtgt/${R}@${R}
+(csr_grant pkinit bar@${R} foo@${R})
+(csr_grant pkinit baz@${R} foo@${R})
+${kdestroy}
+token=$(KRB5CCNAME=$cache2 $gsstoken HTTP@$server)
+if ! (set -vx;
+ curl -vvvo "${cachefile}.json" -Lgsf \
+ --resolve ${server}:${bx509port}:127.0.0.1 \
+ -H "Authorization: Negotiate $token" \
+ "http://${server}:${bx509port}/get-tgts?cname=bar@${R}&cname=baz@${R}"); then
+ echo "Failed to get TGTs batch"
+ exit 2
+fi
+if which jq >/dev/null; then
+ jq -e . "${cachefile}.json" > /dev/null ||
+ { echo "/get-tgts produced non-JSON"; exit 2; }
+
+ # Check bar@$R's tickets:
+ jq -r 'select(.name|startswith("bar@")).ccache' "${cachefile}.json" |
+ $rkbase64 -d -- - > "${cachefile}"
+ ${kgetcred} -H HTTP/${server}@${R} ||
+ { echo "Fetched TGT didn't work"; exit 2; }
+ ${klistjson} | jq -e --arg p bar@$R '.principal == $p' > /dev/null ||
+ { echo "/get-tgts produced wrong TGTs"; exit 2; }
+
+ # Check baz@$R's tickets:
+ jq -r 'select(.name|startswith("baz@")).ccache' "${cachefile}.json" |
+ $rkbase64 -d -- - > "${cachefile}"
+ ${kgetcred} -H HTTP/${server}@${R} ||
+ { echo "Fetched TGT didn't work"; exit 2; }
+ ${klistjson} | jq -e --arg p baz@$R '.principal == $p' > /dev/null ||
+ { echo "/get-tgts produced wrong TGTs"; exit 2; }
+fi
+
+echo "Fetch TGTs (batch, authz pass, one non-existent principal)"
+${kadmin} modify --max-ticket-life=10d krbtgt/${R}@${R}
+(csr_grant pkinit bar@${R} foo@${R})
+(csr_grant pkinit baz@${R} foo@${R})
+(csr_grant pkinit not@${R} foo@${R})
+${kdestroy}
+token=$(KRB5CCNAME=$cache2 $gsstoken HTTP@$server)
+if ! (set -vx;
+ curl -vvvo "${cachefile}.json" -Lgsf \
+ --resolve ${server}:${bx509port}:127.0.0.1 \
+ -H "Authorization: Negotiate $token" \
+ "http://${server}:${bx509port}/get-tgts?cname=not@${R}&cname=bar@${R}&cname=baz@${R}"); then
+ echo "Failed to get TGTs batch including non-existent principal"
+ exit 2
+fi
+if which jq >/dev/null; then
+ set -vx
+ jq -e . "${cachefile}.json" > /dev/null ||
+ { echo "/get-tgts produced non-JSON"; exit 2; }
+ jq -es '.[]|select(.name|startswith("not@"))|(.error_code//empty)' "${cachefile}.json" > /dev/null ||
+ { echo "No error was reported for not@${R}!"; exit 2; }
+
+ # Check bar@$R's tickets:
+ jq -r 'select(.name|startswith("bar@")).ccache' "${cachefile}.json" |
+ $rkbase64 -d -- - > "${cachefile}"
+ ${kgetcred} -H HTTP/${server}@${R} ||
+ { echo "Fetched TGT didn't work"; exit 2; }
+ ${klistjson} | jq -e --arg p bar@$R '.principal == $p' > /dev/null ||
+ { echo "/get-tgts produced wrong TGTs"; exit 2; }
+
+ # Check baz@$R's tickets:
+ jq -r 'select(.name|startswith("baz@")).ccache' "${cachefile}.json" |
+ $rkbase64 -d -- - > "${cachefile}"
+ ${kgetcred} -H HTTP/${server}@${R} ||
+ { echo "Fetched TGT didn't work"; exit 2; }
+ ${klistjson} | jq -e --arg p baz@$R '.principal == $p' > /dev/null ||
+ { echo "/get-tgts produced wrong TGTs"; exit 2; }
+fi
+
+echo "killing bx509d (${bx509pid})"
+sh ${leaks_kill} bx509d $bx509pid || ec=1
+
+echo "Starting bx509d (csrf-protection-type=GET-with-token, POST-with-header)"
+${bx509d} --csrf-protection-type=GET-with-token \
+ --csrf-protection-type=POST-with-header --daemon || {
+ echo "bx509 failed to start"
+ exit 2
+}
+bx509pid=`getpid bx509d`
+
+${kinit} -kt $ukeytab foo@${R} || exit 1
+$klist || { echo "failed to kinit"; exit 2; }
+
+echo "Fetching a trivial user certificate (GET with CSRF token)"
+csr=$($rkbase64 -- ${objdir}/req | $rkvis -h --stdin)
+token=$(KRB5CCNAME=$cache $gsstoken HTTP@$server)
+if (set -vx; get_with_token get-cert '' -o "${objdir}/trivial.pem"); then
+ $hxtool print --content "FILE:${objdir}/trivial.pem"
+ if $hxtool acert --end-entity \
+ --expr="%{certificate.subject} == \"CN=foo,$DCs\"" \
+ -P "foo@${R}" "FILE:${objdir}/trivial.pem"; then
+ echo 'Successfully obtained a trivial client certificate!'
+ else
+ echo 'FAIL: Obtained a trivial client certificate w/o expected PKINIT SAN)'
+ exit 1
+ fi
+ if $hxtool acert --expr="%{certificate.subject} == \"OU=Users,$DCs\"" \
+ --has-private-key "FILE:${objdir}/trivial.pem"; then
+ echo 'Successfully obtained a trivial client certificate!'
+ fi
+else
+ echo 'Failed to get a certificate!'
+ exit 1
+fi
+
+echo "Fetching a trivial user certificate (POST with X-CSRF header, no token)"
+# Encode the CSR in base64, then URL-encode it
+csr=$($rkbase64 -- ${objdir}/req | $rkvis -h --stdin)
+token=$(KRB5CCNAME=$cache $gsstoken HTTP@$server)
+if (set -vx; get_cert '' -H 'X-CSRF: junk' -X POST -sf -o "${objdir}/trivial.pem"); then
+ $hxtool print --content "FILE:${objdir}/trivial.pem"
+ if $hxtool acert --end-entity \
+ --expr="%{certificate.subject} == \"CN=foo,$DCs\"" \
+ -P "foo@${R}" "FILE:${objdir}/trivial.pem"; then
+ echo 'Successfully obtained a trivial client certificate!'
+ else
+ echo 'FAIL: Obtained a trivial client certificate w/o expected PKINIT SAN)'
+ exit 1
+ fi
+ if $hxtool acert --expr="%{certificate.subject} == \"OU=Users,$DCs\"" \
+ --has-private-key "FILE:${objdir}/trivial.pem"; then
+ echo 'Successfully obtained a trivial client certificate!'
+ fi
+else
+ echo 'Failed to get a certificate!'
+ exit 1
+fi
+
echo "Fetch negotiate token (pre-test)"
# Do what /bnegotiate does, roughly, prior to testing /bnegotiate
$hxtool request-create --subject='' --generate-key=rsa --key-bits=1024 \
@@ -596,11 +845,9 @@ grep 'REQ.*wrongaddr=true' ${objdir}/messages.log |
echo "Fetching a Negotiate token"
token=$(KRB5CCNAME=$cache $gsstoken HTTP@$server)
+csr=
if (set -vx;
- curl -o negotiate-token -Lgsf \
- --resolve ${server}:${bx509port}:127.0.0.1 \
- -H "Authorization: Negotiate $token" \
- "http://${server}:${bx509port}/bnegotiate?target=HTTP%40${server}"); then
+ get_with_token get-negotiate-token "target=HTTP%40${server}" -o "${objdir}/negotiate-token"); then
# bx509 sends us a token w/o a newline for now; we add one because
# gss-token expects it.
test -s negotiate-token && echo >> negotiate-token
diff --git a/third_party/heimdal/tests/kdc/check-httpkadmind.in b/third_party/heimdal/tests/kdc/check-httpkadmind.in
index f57f2af8592..816f753d079 100644
--- a/third_party/heimdal/tests/kdc/check-httpkadmind.in
+++ b/third_party/heimdal/tests/kdc/check-httpkadmind.in
@@ -133,9 +133,11 @@ fi
# HTTP curl-opts
HTTP() {
- curl -g --resolve ${server}:${restport2}:127.0.0.1 \
- --resolve ${server}:${restport}:127.0.0.1 \
- -u: --negotiate $verbose "$@"
+ curl -g --resolve ${server}:${restport2}:127.0.0.1 \
+ --resolve ${server}:${restport}:127.0.0.1 \
+ -u: --negotiate $verbose \
+ -D response-headers \
+ "$@"
}
# get_config QPARAMS curl-opts
@@ -145,6 +147,23 @@ get_config() {
HTTP $verbose "$@" "$url"
}
+check_age() {
+ set -- $(grep -i ^Cache-Control: response-headers)
+ if [ $# -eq 0 ]; then
+ return 1
+ fi
+ shift
+ for param in "$@"; do
+ case "$param" in
+ no-store) true;;
+ max-age=0) return 1;;
+ max-age=*) true;;
+ *) return 1;;
+ esac
+ done
+ return 0;
+}
+
# get_keytab QPARAMS curl-opts
get_keytab() {
url="http://${server}:${restport}/get-keys?$1"
@@ -163,9 +182,9 @@ get_keytab_POST() {
get_keytab "$q" -X POST --data-binary @/dev/null -f "$@" &&
{ echo "POST succeeded w/o CSRF token!"; return 1; }
- get_keytab "$q" -X POST --data-binary @/dev/null -D response-headers "$@"
+ get_keytab "$q" -X POST --data-binary @/dev/null "$@"
grep ^X-CSRF-Token: response-headers >/dev/null || return 1
- get_keytab "$q" -X POST --data-binary @/dev/null -D response-headers \
+ get_keytab "$q" -X POST --data-binary @/dev/null \
-H "$(sed -e 's/\r//' response-headers | grep ^X-CSRF-Token:)" "$@"
grep '^HTTP/1.1 200' response-headers >/dev/null || return $?
return 0
@@ -174,7 +193,7 @@ get_keytab_POST() {
get_keytab_POST_redir() {
url="http://${server}:${restport}/get-keys?$1"
shift
- HTTP -X POST --data-binary @/dev/null -D response-headers "$@" "$url"
+ HTTP -X POST --data-binary @/dev/null "$@" "$url"
grep ^X-CSRF-Token: response-headers >/dev/null ||
{ echo "POST w/o CSRF token had response w/o CSRF token!"; return 1; }
HTTP -X POST --data-binary @/dev/null -f \
@@ -218,6 +237,9 @@ ${kadmin} add -r --use-defaults HTTP/xyz.${domain}@${R} || exit 1
${kadmin} add_ns --key-rotation-epoch=-1d --key-rotation-period=5m \
--max-ticket-life=1d --max-renewable-life=5d \
--attributes= HTTP/ns.${domain}@${R} || exit 1
+${kadmin} add_ns --key-rotation-epoch=-1d --key-rotation-period=5m \
+ --max-ticket-life=1d --max-renewable-life=5d \
+ --attributes=ok-as-delegate host/.ns2.${domain}@${R} || exit 1
${kadmin} add -r --use-defaults HTTP/${server}@${R} || exit 1
${kadmin} ext_keytab -r -k $keytab kadmin/admin@${R} || exit 1
${kadmin} ext_keytab -r -k $keytab httpkadmind/admin@${R} || exit 1
@@ -292,6 +314,8 @@ ${ktutil} -k "${objdir}/extracted_keytab" list --keys > extracted_keytab.kadmin
{ echo "Failed to list keytab for $p"; exit 1; }
get_keytab "dNSName=${hn}" -sf -o "${objdir}/extracted_keytab" ||
{ echo "Failed to get a keytab for $p with curl"; exit 1; }
+check_age
+grep -i ^Cache-Control response-headers
${ktutil} -k "${objdir}/extracted_keytab" list --keys > extracted_keytab.rest ||
{ echo "Failed to list keytab for $p"; exit 1; }
cmp extracted_keytab.kadmin extracted_keytab.rest ||
@@ -416,7 +440,6 @@ ${ktutil} -k "${objdir}/extracted_keytab" list --keys > extracted_keytab.kadmin
cmp extracted_keytab.kadmin extracted_keytab.rest ||
{ echo "Keytabs for $p don't match!"; exit 1; }
-if false; then
hn=bar.ns.${domain}
p=HTTP/$hn
echo "Checking materialization of virtual principal ($p)"
@@ -435,7 +458,6 @@ ${ktutil} -k "${objdir}/extracted_keytab" list --keys > extracted_keytab.kadmin
{ echo "Failed to list keytab for $p"; exit 1; }
cmp extracted_keytab.kadmin extracted_keytab.rest ||
{ echo "Keytabs for $p don't match!"; exit 1; }
-fi
echo "Starting secondary httpkadmind to test HTTP redirection"
${httpkadmind2} --primary-server-uri=http://localhost:$restport \
@@ -622,22 +644,21 @@ cmp extracted_keytab.rest1 extracted_keytab.rest2 > /dev/null &&
test "$(grep $p extracted_keytab.rest2 | wc -l)" -eq 3 ||
{ echo "Wrong number of new keys!"; exit 1; }
-echo "Checking that host services as clients can self-serve"
+echo "Checking that host services as clients can self-create"
hn=synthesized.${domain}
p=host/$hn
-KRB5CCNAME=$admincache ${kadmin} get -s $p &&
+KRB5CCNAME=$admincache ${kadmin} get -s $p >/dev/null &&
{ echo "Internal error -- $p exists too soon"; exit 1; }
${kinit2} -C "FILE:${objdir}/pkinit-synthetic.crt,${keyfile2}" ${p}@${R} || \
{ echo "Failed to kinit with PKINIT client cert"; exit 1; }
${kgetcred2} HTTP/localhost@$R || echo WAT
-${klist2}
rm -f extracted_keytab*
KRB5CCNAME=$cache2 \
get_keytab_POST "spn=$p&create=true" -s -o "${objdir}/extracted_keytab" ||
{ echo "Failed to create and extract host keys for self"; exit 1; }
-${ktutil} -k "${objdir}/extracted_keytab" list ||
+${ktutil} -k "${objdir}/extracted_keytab" list > /dev/null ||
{ echo "Failed to create and extract host keys for self (bogus keytab)"; exit 1; }
-KRB5CCNAME=$admincache ${kadmin} get -s $p ||
+KRB5CCNAME=$admincache ${kadmin} get -s $p >/dev/null ||
{ echo "Failed to create and extract host keys for self"; exit 1; }
echo "Checking that host services can't get other host service principals"
@@ -646,8 +667,8 @@ p=host/$hn
KRB5CCNAME=$cache2 \
get_keytab_POST "spn=$p&create=true" -s -o "${objdir}/extracted_keytab2" &&
{ echo "Failed to fail to create and extract host keys for other!"; exit 1; }
-${ktutil} -k "${objdir}/extracted_keytab2" list || true
-KRB5CCNAME=$admincache ${kadmin} get -s $p &&
+${ktutil} -k "${objdir}/extracted_keytab2" list > /dev/null || true
+KRB5CCNAME=$admincache ${kadmin} get -s $p >/dev/null &&
{ echo "Failed to fail to create and extract host keys for other!"; exit 1; }
echo "Checking that host services can't get keys for themselves and others"
@@ -657,15 +678,135 @@ p2=host/nonexistent.${domain}
${kinit2} -C "FILE:${objdir}/pkinit-synthetic.crt,${keyfile2}" ${p}@${R} || \
{ echo "Failed to kinit with PKINIT client cert"; exit 1; }
${kgetcred2} HTTP/localhost@$R || echo WAT
-${klist2}
rm -f extracted_keytab*
KRB5CCNAME=$cache2 \
get_keytab_POST "spn=$p&spn=$p2&create=true" -s -o "${objdir}/extracted_keytab" &&
{ echo "Failed to fail to create and extract host keys for other!"; exit 1; }
-${ktutil} -k "${objdir}/extracted_keytab2" list || true
-KRB5CCNAME=$admincache ${kadmin} get -s $p2 &&
+${ktutil} -k "${objdir}/extracted_keytab2" list > /dev/null || true
+KRB5CCNAME=$admincache ${kadmin} get -s $p2 >/dev/null &&
{ echo "Failed to fail to create and extract host keys for other!"; exit 1; }
+echo "Checking that attributes for new principals can be configured"
+hn=a-particular-hostname.test.h5l.se
+p=host/$hn
+${hxtool} issue-certificate \
+ --ca-certificate=FILE:$objdir/ca.crt,${keyfile} \
+ --type="pkinit-client" \
+ --pk-init-principal="$p@$R" \
+ --req="PKCS10:req-pkinit.der" \
+ --lifetime=7d \
+ --certificate="FILE:pkinit-synthetic.crt" ||
+ { echo "Failed to make PKINIT client cert"; exit 1; }
+KRB5CCNAME=$admincache ${kadmin} get -s $p >/dev/null 2>&1 &&
+ { echo "Internal error -- $p exists too soon"; exit 1; }
+${kinit2} -C "FILE:${objdir}/pkinit-synthetic.crt,${keyfile2}" ${p}@${R} || \
+ { echo "Failed to kinit with PKINIT client cert"; exit 1; }
+${kgetcred2} HTTP/localhost@$R || echo WAT
+rm -f extracted_keytab*
+KRB5CCNAME=$cache2 \
+get_keytab_POST "spn=$p&create=true" -s -o "${objdir}/extracted_keytab" ||
+ { echo "Failed to create and extract host keys for self"; exit 1; }
+${ktutil} -k "${objdir}/extracted_keytab" list > /dev/null ||
+ { echo "Failed to create and extract host keys for self (bogus keytab)"; exit 1; }
+KRB5CCNAME=$admincache ${kadmin} get -s $p >/dev/null ||
+ { echo "Failed to create and extract host keys for self"; exit 1; }
+KRB5CCNAME=$admincache ${kadmin} get $p |
+ grep 'Attributes:.*ok-as-delegate' > /dev/null ||
+ { echo "Failed to create with configured attributes"; exit 1; }
+KRB5CCNAME=$admincache ${kadmin} get $p |
+ grep 'Attributes:.*no-auth-data-reqd' > /dev/null ||
+ { echo "Failed to create with configured attributes"; exit 1; }
+
+hn=other-hostname.test.h5l.se
+p=host/$hn
+${hxtool} issue-certificate \
+ --ca-certificate=FILE:$objdir/ca.crt,${keyfile} \
+ --type="pkinit-client" \
+ --pk-init-principal="$p@$R" \
+ --req="PKCS10:req-pkinit.der" \
+ --lifetime=7d \
+ --certificate="FILE:pkinit-synthetic.crt" ||
+ { echo "Failed to make PKINIT client cert"; exit 1; }
+KRB5CCNAME=$admincache ${kadmin} get -s $p >/dev/null 2>&1 &&
+ { echo "Internal error -- $p exists too soon"; exit 1; }
+${kinit2} -C "FILE:${objdir}/pkinit-synthetic.crt,${keyfile2}" ${p}@${R} || \
+ { echo "Failed to kinit with PKINIT client cert"; exit 1; }
+${kgetcred2} HTTP/localhost@$R || echo WAT
+rm -f extracted_keytab*
+KRB5CCNAME=$cache2 \
+get_keytab_POST "spn=$p&create=true" -s -o "${objdir}/extracted_keytab" ||
+ { echo "Failed to create and extract host keys for self"; exit 1; }
+${ktutil} -k "${objdir}/extracted_keytab" list > /dev/null ||
+ { echo "Failed to create and extract host keys for self (bogus keytab)"; exit 1; }
+KRB5CCNAME=$admincache ${kadmin} get -s $p >/dev/null ||
+ { echo "Failed to create and extract host keys for self"; exit 1; }
+KRB5CCNAME=$admincache ${kadmin} get $p |
+ grep 'Attributes:.*ok-as-delegate' > /dev/null &&
+ { echo "Create with unexpected attributes"; exit 1; }
+KRB5CCNAME=$admincache ${kadmin} get $p |
+ grep 'Attributes:.*no-auth-data-reqd' > /dev/null &&
+ { echo "Create with unexpected attributes"; exit 1; }
+
+hn=a-server.prod.test.h5l.se
+p=host/$hn
+${hxtool} issue-certificate \
+ --ca-certificate=FILE:$objdir/ca.crt,${keyfile} \
+ --type="pkinit-client" \
+ --pk-init-principal="$p@$R" \
+ --req="PKCS10:req-pkinit.der" \
+ --lifetime=7d \
+ --certificate="FILE:pkinit-synthetic.crt" ||
+ { echo "Failed to make PKINIT client cert"; exit 1; }
+KRB5CCNAME=$admincache ${kadmin} get -s $p >/dev/null 2>&1 &&
+ { echo "Internal error -- $p exists too soon"; exit 1; }
+${kinit2} -C "FILE:${objdir}/pkinit-synthetic.crt,${keyfile2}" ${p}@${R} || \
+ { echo "Failed to kinit with PKINIT client cert"; exit 1; }
+${kgetcred2} HTTP/localhost@$R || echo WAT
+rm -f extracted_keytab*
+KRB5CCNAME=$cache2 \
+get_keytab_POST "spn=$p&create=true" -s -o "${objdir}/extracted_keytab" ||
+ { echo "Failed to create and extract host keys for self"; exit 1; }
+${ktutil} -k "${objdir}/extracted_keytab" list > /dev/null ||
+ { echo "Failed to create and extract host keys for self (bogus keytab)"; exit 1; }
+KRB5CCNAME=$admincache ${kadmin} get -s $p >/dev/null ||
+ { echo "Failed to create and extract host keys for self"; exit 1; }
+KRB5CCNAME=$admincache ${kadmin} get $p |
+ grep 'Attributes:.*ok-as-delegate' > /dev/null ||
+ { echo "Failed to create with configured attributes"; exit 1; }
+KRB5CCNAME=$admincache ${kadmin} get $p |
+ grep 'Attributes:.*no-auth-data-reqd' > /dev/null ||
+ { echo "Failed to create with configured attributes"; exit 1; }
+
+hn=a-host.ns2.test.h5l.se
+p=host/$hn
+${hxtool} issue-certificate \
+ --ca-certificate=FILE:$objdir/ca.crt,${keyfile} \
+ --type="pkinit-client" \
+ --pk-init-principal="$p@$R" \
+ --req="PKCS10:req-pkinit.der" \
+ --lifetime=7d \
+ --certificate="FILE:pkinit-synthetic.crt" ||
+ { echo "Failed to make PKINIT client cert"; exit 1; }
+KRB5CCNAME=$admincache ${kadmin} get -s $p >/dev/null 2>&1 &&
+ { echo "Internal error -- $p exists too soon"; exit 1; }
+${kinit2} -C "FILE:${objdir}/pkinit-synthetic.crt,${keyfile2}" ${p}@${R} || \
+ { echo "Failed to kinit with PKINIT client cert"; exit 1; }
+${kgetcred2} HTTP/localhost@$R || echo WAT
+rm -f extracted_keytab*
+KRB5CCNAME=$cache2 \
+get_keytab_POST "spn=$p&create=true" -s -o "${objdir}/extracted_keytab" ||
+ { echo "Failed to create and extract host keys for self"; exit 1; }
+${ktutil} -k "${objdir}/extracted_keytab" list > /dev/null ||
+ { echo "Failed to create and extract host keys for self (bogus keytab)"; exit 1; }
+KRB5CCNAME=$admincache ${kadmin} get -s $p >/dev/null ||
+ { echo "Failed to create and extract host keys for self"; exit 1; }
+KRB5CCNAME=$admincache ${kadmin} get $p |
+ grep 'Attributes:.*ok-as-delegate' > /dev/null ||
+ { echo "Failed to create with namespace attributes"; exit 1; }
+KRB5CCNAME=$admincache ${kadmin} get $p |
+ grep 'Attributes:.*no-auth-data-reqd' > /dev/null &&
+ { echo "Create with unexpected attributes"; exit 1; }
+
grep 'Internal error' messages.log &&
{ echo "Internal errors in log"; exit 1; }
diff --git a/third_party/heimdal/tests/kdc/check-kadmin.in b/third_party/heimdal/tests/kdc/check-kadmin.in
index 45d679ceb4a..339868bfb8f 100644
--- a/third_party/heimdal/tests/kdc/check-kadmin.in
+++ b/third_party/heimdal/tests/kdc/check-kadmin.in
@@ -100,6 +100,11 @@ echo Starting kdc ; > messages.log
${kdc} --detach --testing || { echo "kdc failed to start"; cat messages.log; exit 1; }
kdcpid=`getpid kdc`
+echo Starting kadmind
+${kadmind} --detach --list-chunk-size=1 \
+ || { echo "kadmind failed to start"; cat messages.log; exit 1; }
+kadmpid=`getpid kadmind`
+
trap "kill -9 ${kdcpid} ${kadmpid}" EXIT
#----------------------------------
@@ -107,58 +112,34 @@ echo "kinit (no admin); test mod --alias authorization"
${kinit} --password-file=${objdir}/foopassword \
-S kadmin/admin@${R} hasalias@${R} || exit 1
-${kadmind} -d &
-kadmpid=$!
-sleep 1
-
# Check that one non-permitted alias -> failure
env KRB5CCNAME=${cache} \
${kadmin} -p hasalias@${R} modify --alias=goodalias1@${R} --alias=badalias@${R} hasalias@${R} &&
{ echo "kadmin failed $?"; cat messages.log ; exit 1; }
-wait $kadmpid || { echo "kadmind failed $?"; cat messages.log ; exit 1; }
-
-${kadmind} -d &
-kadmpid=$!
-sleep 1
# Check that all permitted aliases -> success
env KRB5CCNAME=${cache} \
${kadmin} -p hasalias@${R} modify --alias=goodalias1@${R} --alias=goodalias2@${R} hasalias@${R} ||
{ echo "kadmin failed $?"; cat messages.log ; exit 1; }
-wait $kadmpid || { echo "kadmind failed $?"; cat messages.log ; exit 1; }
-
-${kadmind} -d &
-kadmpid=$!
-sleep 1
# Check that we can drop aliases
env KRB5CCNAME=${cache} \
${kadmin} -p hasalias@${R} modify --alias=goodalias3@${R} hasalias@${R} ||
{ echo "kadmin failed $?"; cat messages.log ; exit 1; }
-wait $kadmpid || { echo "kadmind failed $?"; cat messages.log ; exit 1; }
${kadmin} -l get hasalias@${R} | grep Aliases: > kadmin.tmp
read junk aliases < kadmin.tmp
rm kadmin.tmp
[ "$aliases" != "goodalias3@${R}" ] && { echo "kadmind failed $?"; cat messages.log ; exit 1; }
-${kadmind} -d &
-kadmpid=$!
-sleep 1
-
env KRB5CCNAME=${cache} \
${kadmin} -p hasalias@${R} modify --alias=goodalias1@${R} --alias=goodalias2@${R} --alias=goodalias3@${R} hasalias@${R} ||
{ echo "kadmin failed $?"; cat messages.log ; exit 1; }
-wait $kadmpid || { echo "kadmind failed $?"; cat messages.log ; exit 1; }
${kadmin} -l get hasalias@${R} | grep Aliases: > kadmin.tmp
read junk aliases < kadmin.tmp
rm kadmin.tmp
[ "$aliases" != "goodalias1@${R} goodalias2@${R} goodalias3@${R}" ] && { echo "FOO failed $?"; cat messages.log ; exit 1; }
#----------------------------------
-${kadmind} -d &
-kadmpid=$!
-sleep 1
-
echo "kinit (no admin)"
${kinit} --password-file=${objdir}/foopassword \
-S kadmin/admin@${R} bar@${R} || exit 1
@@ -171,10 +152,6 @@ ${kadmin} -l get kaka2@${R} > /dev/null ||
{ echo "kadmin failed $?"; cat messages.log ; exit 1; }
#----------------------------------
-${kadmind} -d &
-kadmpid=$!
-sleep 1
-
echo "kinit (no admin)"
${kinit} --password-file=${objdir}/foopassword \
-S kadmin/admin@${R} baz@${R} || exit 1
@@ -184,10 +161,6 @@ ${kadmin} -p baz@${R} get bar@${R} > /dev/null ||
{ echo "kadmin failed $?"; cat messages.log ; exit 1; }
#----------------------------------
-${kadmind} -d &
-kadmpid=$!
-sleep 1
-
echo "kinit (no admin)"
${kinit} --password-file=${objdir}/foopassword \
-S kadmin/admin@${R} baz@${R} || exit 1
@@ -197,10 +170,6 @@ ${kadmin} -p baz@${R} passwd -p "$foopassword" bar@${R} > /dev/null 2>/dev/null
{ echo "kadmin succesded $?"; cat messages.log ; exit 1; }
#----------------------------------
-${kadmind} -d &
-kadmpid=$!
-sleep 1
-
echo "kinit (no admin)"
${kinit} --password-file=${objdir}/foopassword \
-S kadmin/admin@${R} baz@${R} || exit 1
@@ -210,10 +179,6 @@ ${kadmin} -p baz@${R} get bar@${R} > /dev/null ||
{ echo "kadmin failed $?"; cat messages.log ; exit 1; }
#----------------------------------
-${kadmind} -d &
-kadmpid=$!
-sleep 1
-
echo "kinit (no admin)"
${kinit} --password-file=${objdir}/foopassword \
-S kadmin/admin@${R} bez@${R} || exit 1
@@ -223,10 +188,6 @@ ${kadmin} -p bez@${R} passwd -p "$foopassword" bar@${R} > /dev/null 2>/dev/null
{ echo "kadmin succesded $?"; cat messages.log ; exit 1; }
#----------------------------------
-${kadmind} -d &
-kadmpid=$!
-sleep 1
-
echo "kinit (no admin)"
${kinit} --password-file=${objdir}/foopassword \
-S kadmin/admin@${R} fez@${R} || exit 1
@@ -236,10 +197,6 @@ ${kadmin} -p fez@${R} get bar@${R} > /dev/null ||
{ echo "kadmin failed $?"; cat messages.log ; exit 1; }
#----------------------------------
-${kadmind} -d &
-kadmpid=$!
-sleep 1
-
echo "kinit (no admin)"
${kinit} --password-file=${objdir}/foopassword \
-S kadmin/admin@${R} fez@${R} || exit 1
@@ -249,10 +206,6 @@ ${kadmin} -p fez@${R} passwd -p "$foopassword" bar@${R} > /dev/null 2>/dev/null
{ echo "kadmin succesded $?"; cat messages.log ; exit 1; }
#----------------------------------
-${kadmind} -d &
-kadmpid=$!
-sleep 1
-
echo "kinit (admin)"
${kinit} --password-file=${objdir}/foopassword \
-S kadmin/admin@${R} foo/admin@${R} || exit 1
@@ -268,10 +221,6 @@ ${kadmin} -p foo/admin@${R} add -p abc --use-defaults kaka@${R} &&
{ echo "kadmin succeeded $?"; cat messages.log ; exit 1; }
#----------------------------------
-${kadmind} -d &
-kadmpid=$!
-sleep 1
-
echo "kadmin get doesnotexists"
env KRB5CCNAME=${cache} \
${kadmin} -p foo/admin@${R} get -s doesnotexists@${R} \
@@ -286,14 +235,10 @@ mv kadmin2.tmp kadmin.tmp
grep -v ': connect' kadmin.tmp > kadmin2.tmp
mv kadmin2.tmp kadmin.tmp
-cmp kadmin.tmp ${srcdir}/donotexists.txt || \
+diff kadmin.tmp ${srcdir}/donotexists.txt || \
{ echo "wrong response"; exit 1;}
#----------------------------------
-${kadmind} -d &
-kadmpid=$!
-sleep 1
-
echo "kadmin get pkinit-acl"
env KRB5CCNAME=${cache} \
${kadmin} -p foo/admin@${R} get -o pkinit-acl pkinit@${R} \
@@ -301,10 +246,6 @@ ${kadmin} -p foo/admin@${R} get -o pkinit-acl pkinit@${R} \
{ echo "kadmin failed $?"; cat messages.log ; exit 1; }
#----------------------------------
-${kadmind} -d &
-kadmpid=$!
-sleep 1
-
echo "kadmin get -o principal"
env KRB5CCNAME=${cache} \
${kadmin} -p foo/admin@${R} get -o principal bar@${R} \
@@ -316,10 +257,6 @@ fi
#----------------------------------
-${kadmind} -d &
-kadmpid=$!
-sleep 1
-
echo "kadmin get -o kvno"
env KRB5CCNAME=${cache} \
${kadmin} -p foo/admin@${R} get -o kvno bar@${R} \
@@ -331,10 +268,6 @@ fi
#----------------------------------
-${kadmind} -d &
-kadmpid=$!
-sleep 1
-
echo "kadmin get -o princ_expire_time"
env KRB5CCNAME=${cache} \
${kadmin} -p foo/admin@${R} get -o princ_expire_time bar@${R} \
@@ -345,67 +278,178 @@ if test "`cat kadmin.tmp`" != "Principal expires: never" ; then
fi
#----------------------------------
-${kadmind} -d &
-kadmpid=$!
-sleep 1
-
echo "kadmin get -s -o attributes"
env KRB5CCNAME=${cache} \
${kadmin} -p foo/admin@${R} get -s -o attributes bar@${R} \
- > kadmin.tmp 2>&1 || \
+ > kadmin.tmp || \
{ echo "kadmin failed $?"; cat messages.log ; exit 1; }
if test "`cat kadmin.tmp`" != "Attributes" ; then
cat kadmin.tmp ; cat messages.log ; exit 1 ;
fi
#----------------------------------
-${kadmind} -d &
-kadmpid=$!
-sleep 1
-
echo "kadmin prune"
env KRB5CCNAME=${cache} \
${kadmin} prune --kvno=2 prune@${R} \
> kadmin.tmp 2>&1 || \
{ echo "kadmin failed $?"; cat messages.log ; exit 1; }
-wait $kadmpid || { echo "kadmind failed $?"; cat messages.log ; exit 1; }
-
-${kadmind} -d &
-kadmpid=$!
-sleep 1
-
env KRB5CCNAME=${cache} \
${kadmin} get prune@${R} \
> kadmin.tmp 2>&1 || \
{ echo "kadmin failed $?"; cat messages.log ; exit 1; }
-wait $kadmpid || { echo "kadmind failed $?"; cat messages.log ; exit 1; }
-
cat kadmin.tmp | ${EGREP} Keytypes: | cut -d: -f2 | tr ' ' '
' | sed 's/^.*[[]\(.*\)[]].*$/\1/' | grep '[0-9]' | sort -nu | tr -d '
' | ${EGREP} '^13$' > /dev/null || \
{ echo "kadmin prune failed $?"; cat messages.log ; exit 1; }
#----------------------------------
-${kadmind} -d &
-kadmpid=$!
-sleep 1
-
echo "kadmin pruneall"
env KRB5CCNAME=${cache} \
${kadmin} get pruneall@${R} \
> kadmin.tmp 2>&1 || \
{ echo "kadmin failed $?"; cat messages.log ; exit 1; }
-wait $kadmpid || { echo "kadmind failed $?"; cat messages.log ; exit 1; }
-
cat kadmin.tmp | ${EGREP} Keytypes: | cut -d: -f2 | tr ' ' '
' | sed 's/^.*[[]\(.*\)[]].*$/\1/' | grep '[0-9]' | sort -nu | tr -d '
' | ${EGREP} '^3$' > /dev/null || \
{ echo "kadmin pruneall failed $?"; cat messages.log ; exit 1; }
+env KRB5CCNAME=${cache} \
+ ${kadmin} -p foo/admin@${R} list --upto=3 '*' > kadmin.tmp
+[ `wc -l < kadmin.tmp` -eq 3 ] ||
+ { echo "kadmin list --upto 3 produced `wc -l < kadmin.tmp` results!"; exit 1; }
+
+#----------------------------------
+echo "kadmin get '*' (re-entrance)"; > messages.log
+${kadmin} -l get '*' > kadmin.tmp ||
+ { echo "failed to list principals"; cat messages.log ; exit 1; }
+> messages.log
+env KRB5CCNAME=${cache} \
+ ${kadmin} -p foo/admin@${R} get '*' > kadmin.tmp2 ||
+ { echo "failed to list principals"; cat messages.log ; exit 1; }
+diff -u kadmin.tmp kadmin.tmp2 ||
+ { echo "local and remote get all differ"; exit 1; }
+
+#----------------------------------
+# We have 20 principals in the DB. Test two chunks of 1 (since that's how we
+# started kadmind above.
+> messages.log
+echo "kadmin list all (chunk size 1)"
+# Check that list produces the same output locally and remote.
+env KRB5CCNAME=${cache} \
+ ${kadmin} -p foo/admin@${R} list '*' | sort > kadmin.tmp ||
+ { echo "failed to list principals"; cat messages.log ; exit 1; }
+${kadmin} -l list '*' | sort > kadmin.tmp2
+diff kadmin.tmp kadmin.tmp2 ||
+ { echo "failed to list all principals"; cat messages.log ; exit 1; }
+# kadmin dump does not use kadm5_iter_principals, so this is a good way to
+# double check the above results. This time we drop the realm part because
+# kadmin doesn't show us the realm for principals in the default realm.
+${kadmin} -l list '*' | cut -d'@' -f1 | sort > kadmin.tmp
+${kadmin} -l dump | cut -d'@' -f1 | sort > kadmin.tmp2
+diff kadmin.tmp kadmin.tmp2 ||
+ { echo "failed to list all principals (dump)"; cat messages.log ; exit 1; }
+${kadmin} -l > kadmin.tmp <<"EOF"
+list *
+get foo/admin
+EOF
+grep Attributes kadmin.tmp > /dev/null ||
+ { echo "failed to execute command after list"; cat messages.log ; exit 1; }
+env KRB5CCNAME=${cache} \
+${kadmin} -p foo/admin@${R} > kadmin.tmp <<"EOF"
+list *
+get foo/admin
+EOF
+grep Attributes kadmin.tmp > /dev/null ||
+ { echo "failed to execute command after list"; cat messages.log ; exit 1; }
+
+#----------------------------------
+# We have 20 principals in the DB. Test two chunks of 10.
+sh ${leaks_kill} kadmind $kadmpid || exit 1
+${kadmind} --list-chunk-size=10 --detach
+kadmpid=`getpid kadmind`
+
+> messages.log
+echo "kadmin list all (chunk size 10)"
+# Check that list produces the same output locally and remote.
+env KRB5CCNAME=${cache} \
+ ${kadmin} -p foo/admin@${R} list '*' | sort > kadmin.tmp ||
+ { echo "failed to list principals"; cat messages.log ; exit 1; }
+${kadmin} -l list '*' | sort > kadmin.tmp2
+diff kadmin.tmp kadmin.tmp2 ||
+ { echo "failed to list all principals"; cat messages.log ; exit 1; }
+# kadmin dump does not use kadm5_iter_principals, so this is a good way to
+# double check the above results. This time we drop the realm part because
+# kadmin doesn't show us the realm for principals in the default realm.
+${kadmin} -l list '*' | cut -d'@' -f1 | sort > kadmin.tmp
+${kadmin} -l dump | cut -d'@' -f1 | sort > kadmin.tmp2
+diff kadmin.tmp kadmin.tmp2 ||
+ { echo "failed to list all principals (dump)"; cat messages.log ; exit 1; }
+env KRB5CCNAME=${cache} \
+${kadmin} -p foo/admin@${R} > kadmin.tmp <<"EOF"
+list *
+get foo/admin
+EOF
+grep Attributes kadmin.tmp > /dev/null ||
+ { echo "failed to execute command after list"; cat messages.log ; exit 1; }
+
+#----------------------------------
+# We have 20 principals in the DB. Test one chunk of 50.
+sh ${leaks_kill} kadmind $kadmpid || exit 1
+${kadmind} --list-chunk-size=50 --detach
+kadmpid=`getpid kadmind`
+
+> messages.log
+echo "kadmin list all (chunk size 50)"
+# Check that list produces the same output locally and remote.
+env KRB5CCNAME=${cache} \
+ ${kadmin} -p foo/admin@${R} list '*' | sort > kadmin.tmp ||
+ { echo "failed to list principals"; cat messages.log ; exit 1; }
+${kadmin} -l list '*' | sort > kadmin.tmp2
+diff kadmin.tmp kadmin.tmp2 ||
+ { echo "failed to list all principals"; cat messages.log ; exit 1; }
+# kadmin dump does not use kadm5_iter_principals, so this is a good way to
+# double check the above results. This time we drop the realm part because
+# kadmin doesn't show us the realm for principals in the default realm.
+${kadmin} -l list '*' | cut -d'@' -f1 | sort > kadmin.tmp
+${kadmin} -l dump | cut -d'@' -f1 | sort > kadmin.tmp2
+diff kadmin.tmp kadmin.tmp2 ||
+ { echo "failed to list all principals (dump)"; cat messages.log ; exit 1; }
+env KRB5CCNAME=${cache} \
+${kadmin} -p foo/admin@${R} > kadmin.tmp <<"EOF"
+list *
+get foo/admin
+EOF
+grep Attributes kadmin.tmp > /dev/null ||
+ { echo "failed to execute command after list"; cat messages.log ; exit 1; }
+
+#----------------------------------
+# We have 20 principals in the DB. Test 3 chunks of up to 7.
+sh ${leaks_kill} kadmind $kadmpid || exit 1
+${kadmind} --list-chunk-size=7 --detach
+kadmpid=`getpid kadmind`
+
+> messages.log
+echo "kadmin list all (chunk size 7)"
+# Check that list produces the same output locally and remote.
+env KRB5CCNAME=${cache} \
+ ${kadmin} -p foo/admin@${R} list '*' | sort > kadmin.tmp ||
+ { echo "failed to list principals"; cat messages.log ; exit 1; }
+${kadmin} -l list '*' | sort > kadmin.tmp2
+diff kadmin.tmp kadmin.tmp2 ||
+ { echo "failed to list all principals"; cat messages.log ; exit 1; }
+# kadmin dump does not use kadm5_iter_principals, so this is a good way to
+# double check the above results. This time we drop the realm part because
+# kadmin doesn't show us the realm for principals in the default realm.
+${kadmin} -l list '*' | cut -d'@' -f1 | sort > kadmin.tmp
+${kadmin} -l dump | cut -d'@' -f1 | sort > kadmin.tmp2
+diff kadmin.tmp kadmin.tmp2 ||
+ { echo "failed to list all principals (dump)"; cat messages.log ; exit 1; }
+
#----------------------------------
echo "killing kdc (${kdcpid} ${kadmpid})"
sh ${leaks_kill} kdc $kdcpid || exit 1
+sh ${leaks_kill} kadmind $kadmpid || exit 1
trap "" EXIT
diff --git a/third_party/heimdal/tests/kdc/check-kdc.in b/third_party/heimdal/tests/kdc/check-kdc.in
index e53293b2427..7d2f4edc7f3 100644
--- a/third_party/heimdal/tests/kdc/check-kdc.in
+++ b/third_party/heimdal/tests/kdc/check-kdc.in
@@ -238,11 +238,11 @@ ${kadmin} ext -k ${keytab} ${rps} || exit 1
${kadmin} add -p kaka --use-defaults ${server2}@${R2} || exit 1
${kadmin} ext -k ${keytab} ${server2}@${R2} || exit 1
-${kadmin} add -p foo --use-defaults referral-placeholder@${R5} || exit 1
-${kadmin} add_alias referral-placeholder@${R5} ${server3}@${R} || exit 1
+${kadmin} add -p foo --use-defaults WELLKNOWN/REFERRALS/TARGET@${R5} || exit 1
+${kadmin} add_alias WELLKNOWN/REFERRALS/TARGET@${R5} ${server3}@${R} || exit 1
${kadmin5} add -p kaka --use-defaults ${server3}@${R5} || exit 1
${kadmin5} ext -k ${keytab} ${server3}@${R5} || exit 1
-${kadmin} add_alias referral-placeholder@${R5} ${namespace}@${R} || exit 1
+${kadmin} add_alias WELLKNOWN/REFERRALS/TARGET@${R5} ${namespace}@${R} || exit 1
${kadmin5} add -p kaka --use-defaults ${server5}@${R5} || exit 1
${kadmin5} ext -k ${keytab} ${server5}@${R5} || exit 1
${kadmin} add -p kaka --use-defaults ${serverip}@${R} || exit 1
diff --git a/third_party/heimdal/tests/kdc/check-referral.in b/third_party/heimdal/tests/kdc/check-referral.in
index 73c26c368ca..49f6a52e449 100644
--- a/third_party/heimdal/tests/kdc/check-referral.in
+++ b/third_party/heimdal/tests/kdc/check-referral.in
@@ -42,10 +42,24 @@ testfailed="echo test failed; cat messages.log; exit 1"
# If there is no useful db support compiled in, disable test
${have_db} || exit 77
+d=test.h5l.se
+d2=xtst.heim.example
R=TEST.H5L.SE
-R2=SUB.TEST.H5L.SE
-
-service=ldap/host.sub.test.h5l.se:389
+R2=XTST.HEIM.EXAMPLE
+
+# $service1 will be a hard alias of $service2
+service1=ldap/host.${d}:389
+service2=ldap/host.${d2}:389
+# $service3 and $service4 will have soft aliases referrals from each
+# other's realms
+service3=host/foohost.${d}
+service4=host/barhost.${d2}
+# $service5 and $service6 will be hardaliases
+service5=host/thing1.${d}
+service6=host/thing1.${d2}
+# $service7 and $service8 will be hardaliases in the opposite direction
+service7=host/thing2.${d}
+service8=host/thing2.${d2}
port=@port@
@@ -64,6 +78,9 @@ keytab="FILE:${keytabfile}"
KRB5_CONFIG="${objdir}/krb5.conf"
export KRB5_CONFIG
+KRB5CCNAME=$cache
+export KRB5CCNAME
+
rm -f ${keytabfile}
rm -f current-db*
rm -f out-*
@@ -84,11 +101,34 @@ ${kadmin} \
--realm-max-renewable-life=1month \
${R2} || exit 1
+${kadmin} add -r --use-defaults WELLKNOWN/REFERRALS/TARGET@${R} || exit 1
+${kadmin} add -r --use-defaults WELLKNOWN/REFERRALS/TARGET@${R2} || exit 1
+
+# User 'foo' gets two aliases in the same realm, and one in the other
${kadmin} add -p foo --use-defaults foo@${R} || exit 1
-${kadmin} modify --alias=alias1 --alias=alias2 foo@${R} || exit 1
+${kadmin} add_alias foo@${R} foo@${R2} alias1 alias2 || exit 1
${kadmin} get foo@${R} | grep alias1@${R} >/dev/null || exit 1
+${kadmin} get foo@${R} | grep alias2@${R} >/dev/null || exit 1
+${kadmin} get foo@${R} | grep foo@${R2} >/dev/null || exit 1
+
+# service1 is an alias of service2, in different realms
+${kadmin} add -p foo --use-defaults ${service2}@${R2} || exit 1
+${kadmin} add_alias ${service2}@${R2} ${service1}@${R} || exit 1
+${kadmin} get ${service2}@${R2} | grep ${service1}@${R} >/dev/null || exit 1
+
+# service3 and service4 get soft aliases in each other's realms
+${kadmin} add -p foo --use-defaults ${service3}@${R} || exit 1
+${kadmin} add -p foo --use-defaults ${service4}@${R2} || exit 1
+${kadmin} add_alias WELLKNOWN/REFERRALS/TARGET@${R2} ${service4}@${R} || exit 1
+${kadmin} add_alias WELLKNOWN/REFERRALS/TARGET@${R} ${service3}@${R2} || exit 1
-${kadmin} add -p foo --use-defaults ${service}@${R2} || exit 1
+# service6 is a hard alias of service5
+${kadmin} add -p foo --use-defaults ${service5}@${R} || exit 1
+${kadmin} add_alias ${service5}@${R} ${service6}@${R2} || exit 1
+
+# service8 is a hard alias of service7, but in the opposite direction
+${kadmin} add -p foo --use-defaults ${service7}@${R2} || exit 1
+${kadmin} add_alias ${service5}@${R} ${service8}@${R} || exit 1
${kadmin} add -p foo --use-defaults bar@${R} || exit 1
${kadmin} add -p foo --use-defaults 'baz\@realm.foo@'${R} || exit 1
@@ -187,6 +227,31 @@ ${klist} | grep "Principal: alias1@${R}" > /dev/null || \
echo "checking that we got back right principal inside the PAC"
${test_ap_req} krbtgt/${R}@${R} ${keytab} ${cache} || \
{ ec=1 ; eval "${testfailed}"; }
+${kgetcred} ${service2}@${R2} || { ec=1 ; eval "${testfailed}"; }
+${kgetcred} ${service1}@${R} || { ec=1 ; eval "${testfailed}"; }
+${kdestroy}
+
+echo "Getting client foo@${R2} tickets (non canon case)"; > messages.log
+${kinit} --password-file=${objdir}/foopassword foo@${R2} || \
+ { ec=1 ; eval "${testfailed}"; }
+echo "checking that we got back right principal"
+${klist} | grep "Principal: foo@${R2}" > /dev/null || \
+ { ec=1 ; eval "${testfailed}"; }
+echo "checking that we got back right principal inside the PAC"
+${test_ap_req} krbtgt/${R}@${R} ${keytab} ${cache} || \
+ { ec=1 ; eval "${testfailed}"; }
+echo "Getting various service tickets using foo@${R2} client"
+${kgetcred} ${service2}@${R2} || { ec=1 ; eval "${testfailed}"; }
+${kgetcred} ${service1}@${R} || { ec=1 ; eval "${testfailed}"; }
+${kgetcred} ${service1}@${R2} || { ec=1 ; eval "${testfailed}"; }
+${kgetcred} ${service2}@${R} || { ec=1 ; eval "${testfailed}"; }
+${kgetcred} ${service3}@ || { ec=1 ; eval "${testfailed}"; }
+${kgetcred} ${service4}@ || { ec=1 ; eval "${testfailed}"; }
+${kgetcred} ${service5}@ || { ec=1 ; eval "${testfailed}"; }
+${kgetcred} ${service6}@ || { ec=1 ; eval "${testfailed}"; }
+${kgetcred} ${service7}@ || { ec=1 ; eval "${testfailed}"; }
+${kgetcred} ${service8}@${R} || { ec=1 ; eval "${testfailed}"; }
+${kdestroy}
echo "Getting client alias2 tickets (removed)"; > messages.log
${kadmin} modify --alias=alias1 foo@${R} || { ec=1 ; eval "${testfailed}"; }
@@ -200,25 +265,30 @@ ${kadmin} modify --alias= foo@${R} || { ec=1 ; eval "${testfailed}"; }
echo "Test server referrals"
-echo "Getting client for ${service}@${R} (tgs kdc referral)"
+echo "Getting client for ${service2}@${R} (tgs kdc referral)"
> messages.log
${kinit} --password-file=${objdir}/foopassword foo@${R} || \
{ ec=1 ; eval "${testfailed}"; }
-${kgetcred} --canonicalize ${service}@${R} ||
- { ec=1 ; eval "${testfailed}"; }
+${kgetcred} --canonicalize ${service2}@${R} || { ec=1 ; eval "${testfailed}"; }
+${kgetcred} ${service3}@${R} || { ec=1 ; eval "${testfailed}"; }
+${kgetcred} ${service4}@ || { ec=1 ; eval "${testfailed}"; }
echo "checking that we got back right principal"
-${klist} | grep "${service}@${R2}" > /dev/null || \
+${klist} | grep "${service2}@${R2}" > /dev/null || \
+ { ec=1 ; eval "${testfailed}"; }
+${klist} | grep "${service4}@${R}" > /dev/null && \
+ { ec=1 ; eval "${testfailed}"; }
+${klist} | grep "${service4}@${R2}" > /dev/null || \
{ ec=1 ; eval "${testfailed}"; }
${kdestroy}
-echo "Getting client for ${service}@${R2} (tgs client side guessing)"
+echo "Getting client for ${service2}@${R2} (tgs client side guessing)"
> messages.log
${kinit} --password-file=${objdir}/foopassword foo@${R} || \
{ ec=1 ; eval "${testfailed}"; }
-${kgetcred} ${service}@${R2} ||
+${kgetcred} ${service2}@${R2} ||
{ ec=1 ; eval "${testfailed}"; }
echo "checking that we got back right principal"
-${klist} | grep "${service}@${R2}" > /dev/null || \
+${klist} | grep "${service2}@${R2}" > /dev/null || \
{ ec=1 ; eval "${testfailed}"; }
${kdestroy}
diff --git a/third_party/heimdal/tests/kdc/krb5-httpkadmind.conf.in b/third_party/heimdal/tests/kdc/krb5-httpkadmind.conf.in
index 4882d52f5e9..f887e82c4f1 100644
--- a/third_party/heimdal/tests/kdc/krb5-httpkadmind.conf.in
+++ b/third_party/heimdal/tests/kdc/krb5-httpkadmind.conf.in
@@ -85,6 +85,12 @@
[ext_keytab]
simple_csr_authorizer_directory = @objdir@/simple_csr_authz
+ new_hostbased_service_principal_attributes = {
+ host = {
+ a-particular-hostname.test.h5l.se = ok-as-delegate,no-auth-data-reqd
+ .prod.test.h5l.se = ok-as-delegate no-auth-data-reqd
+ }
+ }
[logging]
kdc = 0-/FILE:@objdir@/messages.log
diff --git a/third_party/heimdal/tests/kdc/krb5.conf.in b/third_party/heimdal/tests/kdc/krb5.conf.in
index a85836d76b2..5b9d644cd0a 100644
--- a/third_party/heimdal/tests/kdc/krb5.conf.in
+++ b/third_party/heimdal/tests/kdc/krb5.conf.in
@@ -31,6 +31,9 @@
TEST4.H5L.SE = {
kdc = localhost:@port@
}
+ XTST.HEIM.EXAMPLE = {
+ kdc = localhost:@port@
+ }
SOME-REALM5.FR = {
kdc = localhost:@port@
}
diff --git a/third_party/heimdal/windows/README.md b/third_party/heimdal/windows/README.md
index 0c8614176d8..f58dbf77793 100644
--- a/third_party/heimdal/windows/README.md
+++ b/third_party/heimdal/windows/README.md
@@ -1,14 +1,14 @@
Building Heimdal for Windows
===================
-1. Introduction
----------------
+# 1. Introduction
+
Heimdal can be built and run on Windows XP or later. Older OSs may
work, but have not been tested.
-2. Prerequisites
-----------------
+# 2. Prerequisites
+
* __Microsoft Visual C++ Compiler__: Heimdal has been tested with
Microsoft Visual C/C++ compiler version 15.x. This corresponds to
@@ -25,7 +25,8 @@ work, but have not been tested.
* __Perl__: A recent version of Perl. Tested with ActiveState
ActivePerl.
-* __Python__: Tested with Python 2.5 and 2.6.
+* __Python__: Tested with Python 2.5 and 2.6. Python 3.9 is known to not
+ work.
* __WiX__: The Windows [Installer XML toolkit (WiX)][1] Version 3.x is
used to build the installers.
@@ -37,6 +38,8 @@ work, but have not been tested.
However, a recent build of `makeinfo` is required for building the
documentation. Cygwin makeinfo 4.7 is known to work.
+ - Native `makeinfo.exe` is no longer available from cygwin.
+
* __Certificate for code-signing__: The Heimdal build produces a
number of Assemblies that should be signed if they are to be
installed via Windows Installer. In addition, all executable
@@ -48,14 +51,14 @@ work, but have not been tested.
[1]: http://wix.sourceforge.net/
-3. Setting up the build environment
------------------------------------
+# 3. Setting up the build environment
+
-* Start with a Windows SDK or Visual Studio build environment. The
+* Starting with a Windows SDK environment: The
target platform, OS and build type (debug / release) is determined
by the build environment.
- E.g.: If you are using the Windows SDK, you can use the `SetEnv.Cmd`
+ E.g.: You can use the `SetEnv.Cmd`
script to set up a build environment targetting 64-bit Windows XP or
later with:
@@ -67,6 +70,16 @@ work, but have not been tested.
the build will produce release binaries.
+* Starting with a Visual Studio build: The target platform and OS is determined
+ by the build environment.
+
+ E.g.: You can use the `vcvarsall.bat` script to set up an environ,ent
+ script to set up a build environment targetting 64-bit Windows 10 with:
+
+ vcvarsall.bat x64 10.0.19041.0 -vcvars_ver=14.29 -vcvars_spectre_libs=spectre
+
+ The choice of Debug or Release is made on the `nmake` command line.
+
* Add any directories to `PATH` as necessary for tools required by
the build to be found. The build scripts will check for build
tools at the start of the build and will indicate which ones are
@@ -114,6 +127,11 @@ work, but have not been tested.
set CODESIGN=c:\scripts\mycodesigner.cmd
set CODESIGN_SHA256=c:\scripts\mycodesigner256.cmd
+ - 'APPVER'. This environment variable controls the version passed to
+ the `-subsystem` qualifier for linker. Additionally it helps
+ locate the runtime library (or otherwise) associated with the
+ compiler (Not sure how to build for XP with VC2017)
+
* Define the code sign public key token. This is contained in the
environment variable `CODESIGN_PKT` and is needed to build the
Heimdal assemblies. If you are not using a code-sign certificate,
@@ -133,15 +151,21 @@ work, but have not been tested.
set CODESIGN_PKT=abcdef0123456789
-4. Running the build
---------------------
+# 4. Running the build
+
+_If you checkout from git, you should ensure that and `awk` input files
+retain unix (LF only) line endings._
Change the current directory to the root of the Heimdal source tree
and run:
nmake /f NTMakefile
-This should build the binaries, assemblies and the installers.
+This should build the binaries, assemblies and the installers. If you are
+building with the Visual Studio tools you can build the release versions
+by setting the NODEBUG variable
+
+ nmake /f NTMakefile NODEBUG=TRUE
The build can also be invoked from any subdirectory that contains an
`NTMakefile` using the same command. Keep in mind that there are
@@ -163,3 +187,16 @@ be built. First build for X86 and then build AMD64
nmake /f NTMakefile MULTIPLATFORM_INSTALLER=1
The build must be executed under cmd.exe.
+
+# 5. Makeinfo
+
+Makeinfo is no longer available from cygwin (see
+[this mail thread][2]).The following appears to work
+(added to NTMakefile.w32)
+
+ MAKEINFO=$(PERL) C:\cygwin64\bin\texi2any
+
+You should expect a certain amount of debugging to ensure that all the required
+Perl libraries are installed.
+
+[2]: https://sourceware.org/legacy-ml/cygwin/2015-03/msg00503.html \ No newline at end of file