summaryrefslogtreecommitdiffstats
path: root/third_party/heimdal/kuser
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/heimdal/kuser')
-rw-r--r--third_party/heimdal/kuser/Makefile.am104
-rw-r--r--third_party/heimdal/kuser/NTMakefile140
-rw-r--r--third_party/heimdal/kuser/copy_cred_cache.196
-rw-r--r--third_party/heimdal/kuser/copy_cred_cache.c164
-rw-r--r--third_party/heimdal/kuser/generate-requests.c146
-rw-r--r--third_party/heimdal/kuser/heimtools-commands.in304
-rw-r--r--third_party/heimdal/kuser/heimtools-version.rc36
-rw-r--r--third_party/heimdal/kuser/heimtools.c165
-rw-r--r--third_party/heimdal/kuser/kcpytkt.c178
-rw-r--r--third_party/heimdal/kuser/kdecode_ticket.c161
-rw-r--r--third_party/heimdal/kuser/kdeltkt.c172
-rw-r--r--third_party/heimdal/kuser/kdestroy-version.rc36
-rw-r--r--third_party/heimdal/kuser/kdestroy.175
-rw-r--r--third_party/heimdal/kuser/kdestroy.c172
-rw-r--r--third_party/heimdal/kuser/kdigest-commands.in280
-rw-r--r--third_party/heimdal/kuser/kdigest-version.rc36
-rw-r--r--third_party/heimdal/kuser/kdigest.8257
-rw-r--r--third_party/heimdal/kuser/kdigest.c572
-rw-r--r--third_party/heimdal/kuser/kgetcred-version.rc36
-rw-r--r--third_party/heimdal/kuser/kgetcred.1188
-rw-r--r--third_party/heimdal/kuser/kgetcred.c371
-rw-r--r--third_party/heimdal/kuser/kimpersonate-version.rc36
-rw-r--r--third_party/heimdal/kuser/kimpersonate.8130
-rw-r--r--third_party/heimdal/kuser/kimpersonate.c395
-rw-r--r--third_party/heimdal/kuser/kinit-version.rc36
-rw-r--r--third_party/heimdal/kuser/kinit.1463
-rw-r--r--third_party/heimdal/kuser/kinit.c1993
-rw-r--r--third_party/heimdal/kuser/klist.1135
-rw-r--r--third_party/heimdal/kuser/klist.c988
-rw-r--r--third_party/heimdal/kuser/kswitch.185
-rw-r--r--third_party/heimdal/kuser/kswitch.c179
-rw-r--r--third_party/heimdal/kuser/kuser_locl.h117
-rw-r--r--third_party/heimdal/kuser/kverify.c130
-rw-r--r--third_party/heimdal/kuser/kvno.c278
-rw-r--r--third_party/heimdal/kuser/kx509.1133
-rw-r--r--third_party/heimdal/kuser/kx509.c303
36 files changed, 9090 insertions, 0 deletions
diff --git a/third_party/heimdal/kuser/Makefile.am b/third_party/heimdal/kuser/Makefile.am
new file mode 100644
index 0000000..96ad36f
--- /dev/null
+++ b/third_party/heimdal/kuser/Makefile.am
@@ -0,0 +1,104 @@
+# $Id$
+
+include $(top_srcdir)/Makefile.am.common
+
+if !NO_AFS
+afs_lib = $(LIB_kafs)
+endif
+
+AM_CPPFLAGS += -I$(srcdir)/../lib/krb5 \
+ -I$(srcdir)/../lib/gssapi \
+ $(INCLUDE_libintl) \
+ -DHEIMDAL_LOCALEDIR='"$(localedir)"'
+
+man_MANS = \
+ kinit.1 \
+ klist.1 \
+ kdestroy.1 \
+ kswitch.1 \
+ kdigest.8 \
+ kgetcred.1 \
+ kimpersonate.8 \
+ kx509.1
+
+bin_PROGRAMS = kinit kdestroy kgetcred heimtools
+libexec_PROGRAMS = kdigest kimpersonate
+
+noinst_PROGRAMS = kverify kdecode_ticket generate-requests
+
+kinit_LDADD = \
+ $(afs_lib) \
+ $(top_builddir)/lib/krb5/libkrb5.la \
+ $(top_builddir)/lib/gssapi/libgssapi.la \
+ $(top_builddir)/lib/gss_preauth/libgss_preauth.la \
+ $(top_builddir)/lib/ntlm/libheimntlm.la \
+ $(LIB_hcrypto) \
+ $(top_builddir)/lib/asn1/libasn1.la \
+ $(LIB_libintl) \
+ $(LIB_roken)
+
+kdestroy_LDADD = $(kinit_LDADD)
+
+kimpersonate_LDADD = $(kinit_LDADD)
+
+LIB_hx509 = ../lib/hx509/libhx509.la
+
+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
+nodist_heimtools_SOURCES = heimtools-commands.c
+
+$(heimtools_OBJECTS): heimtools-commands.h
+
+dist_kdigest_SOURCES = kdigest.c
+nodist_kdigest_SOURCES = kdigest-commands.c
+
+kdigest_LDADD = \
+ $(top_builddir)/lib/ntlm/libheimntlm.la \
+ $(top_builddir)/lib/krb5/libkrb5.la \
+ $(LIB_hcrypto) \
+ $(top_builddir)/lib/asn1/libasn1.la \
+ $(top_builddir)/lib/sl/libsl.la \
+ $(LIB_roken)
+
+$(kdigest_OBJECTS): kdigest-commands.h
+
+CLEANFILES = \
+ kdigest-commands.h kdigest-commands.c \
+ heimtools-commands.h heimtools-commands.c
+
+kdigest-commands.c kdigest-commands.h: kdigest-commands.in
+ $(SLC) $(srcdir)/kdigest-commands.in
+
+heimtools-commands.c heimtools-commands.h: heimtools-commands.in
+ $(SLC) $(srcdir)/heimtools-commands.in
+
+LDADD = \
+ $(top_builddir)/lib/krb5/libkrb5.la \
+ $(LIB_hcrypto) \
+ $(top_builddir)/lib/asn1/libasn1.la \
+ $(LIB_roken)
+
+EXTRA_DIST = NTMakefile $(man_MANS) \
+ heimtools-version.rc \
+ kcpytkt.c \
+ kdeltkt.c \
+ kvno.c \
+ kdestroy-version.rc \
+ kdigest-version.rc \
+ kgetcred-version.rc \
+ kimpersonate-version.rc \
+ kinit-version.rc \
+ kuser_locl.h heimtools-commands.in kdigest-commands.in copy_cred_cache.1
+
+# make sure install-exec-hook doesn't have any commands in Makefile.am.common
+install-exec-hook:
+ (cd $(DESTDIR)$(bindir) && rm -f klist && $(LN_S) heimtools klist)
+ (cd $(DESTDIR)$(bindir) && rm -f kx509 && $(LN_S) heimtools kx509)
+ (cd $(DESTDIR)$(bindir) && rm -f kswitch && $(LN_S) heimtools kswitch)
+
diff --git a/third_party/heimdal/kuser/NTMakefile b/third_party/heimdal/kuser/NTMakefile
new file mode 100644
index 0000000..4b97b70
--- /dev/null
+++ b/third_party/heimdal/kuser/NTMakefile
@@ -0,0 +1,140 @@
+########################################################################
+#
+# Copyright (c) 2009, 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
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT HOLDER 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.
+#
+
+RELDIR=kuser
+
+intcflags=-I$(OBJ) -I$(SRC)\lib\gssapi -I$(OBJDIR)\lib\gssapi -I$(OBJDIR)\lib\gss_preauth
+
+!include ../windows/NTMakefile.w32
+
+BINPROGRAMS=\
+ $(BINDIR)\kinit.exe \
+ $(BINDIR)\heimtools.exe \
+ $(BINDIR)\kdestroy.exe \
+ $(BINDIR)\kgetcred.exe \
+ $(BINDIR)\kvno.exe \
+ $(BINDIR)\kcpytkt.exe \
+ $(BINDIR)\kdeltkt.exe
+
+LIBEXECPROGRAMS=\
+ $(LIBEXECDIR)\kdigest.exe \
+ $(LIBEXECDIR)\kimpersonate.exe
+
+NOINSTPROGRAMS=\
+ $(OBJ)\kverify.exe \
+ $(OBJ)\kdecode_ticket.exe \
+ $(OBJ)\generate-requests.exe
+
+
+BINLIBS=\
+ $(LIBHEIMBASE) \
+ $(LIBGSS_PREAUTH) \
+ $(LIBGSSAPI) \
+ $(LIBHEIMDAL) \
+ $(LIBHEIMNTLM) \
+ $(LIBHX509) \
+!if !defined(NO_AFS)
+ $(LIBKAFS) \
+!endif
+ $(LIBROKEN) \
+ $(LIBVERS)
+
+all:: $(BINPROGRAMS) $(LIBEXECPROGRAMS)
+
+clean::
+ -$(RM) $(BINPROGRAMS:.exe=.*) $(LIBEXECPROGRAMS:.exe=.*)
+
+
+$(BINDIR)\kinit.exe: $(OBJ)\kinit.obj $(BINLIBS) $(OBJ)\kinit-version.res
+ $(EXECONLINK) Secur32.lib Shell32.lib
+ $(EXEPREP)
+
+HEIMTOOLS_OBJS = \
+ $(OBJ)\heimtools-commands.obj \
+ $(OBJ)\heimtools.obj \
+ $(OBJ)\kswitch.obj \
+ $(OBJ)\klist.obj \
+ $(OBJ)\kx509.obj \
+ $(OBJ)\copy_cred_cache.obj
+
+HEIMTOOLSLIBS=\
+ $(BINLIBS) \
+ $(LIBSL)
+
+$(BINDIR)\heimtools.exe: $(HEIMTOOLS_OBJS) $(HEIMTOOLSLIBS) $(OBJ)\heimtools-version.res
+ $(EXECONLINK) Secur32.lib Shell32.lib
+ $(EXEPREP)
+
+
+$(BINDIR)\kdestroy.exe: $(OBJ)\kdestroy.obj $(BINLIBS) $(OBJ)\kdestroy-version.res
+ $(EXECONLINK)
+ $(EXEPREP)
+
+
+$(BINDIR)\kgetcred.exe: $(OBJ)\kgetcred.obj $(BINLIBS) $(OBJ)\kgetcred-version.res
+ $(EXECONLINK)
+ $(EXEPREP)
+
+
+$(LIBEXECDIR)\kdigest.exe: $(OBJ)\kdigest-commands.obj $(OBJ)\kdigest.obj $(BINLIBS) $(LIBSL) $(OBJ)\kdigest-version.res
+ $(EXECONLINK)
+ $(EXEPREP)
+
+$(OBJ)\kdigest.obj: kdigest.c
+ $(C2OBJ) -I$(OBJ)
+
+$(OBJ)\kdigest-commands.c $(OBJ)\kdigest-commands.h: kdigest-commands.in
+ cd $(OBJ)
+ $(CP) $(SRCDIR)\kdigest-commands.in $(OBJ)
+ $(BINDIR)\slc.exe kdigest-commands.in
+ cd $(SRCDIR)
+
+$(OBJ)\heimtools-commands.c $(OBJ)\heimtools-commands.h: heimtools-commands.in
+ cd $(OBJ)
+ $(CP) $(SRCDIR)\heimtools-commands.in $(OBJ)
+ $(BINDIR)\slc.exe heimtools-commands.in
+ cd $(SRCDIR)
+
+$(LIBEXECDIR)\kimpersonate.exe: $(OBJ)\kimpersonate.obj $(BINLIBS) $(OBJ)\kimpersonate-version.res
+ $(EXECONLINK)
+ $(EXEPREP)
+
+$(BINDIR)\kvno.exe: $(OBJ)\kvno.obj $(BINLIBS)
+ $(EXECONLINK)
+ $(EXEPREP)
+
+$(BINDIR)\kcpytkt.exe: $(OBJ)\kcpytkt.obj $(BINLIBS)
+ $(EXECONLINK)
+ $(EXEPREP)
+
+$(BINDIR)\kdeltkt.exe: $(OBJ)\kdeltkt.obj $(BINLIBS)
+ $(EXECONLINK)
+ $(EXEPREP)
diff --git a/third_party/heimdal/kuser/copy_cred_cache.1 b/third_party/heimdal/kuser/copy_cred_cache.1
new file mode 100644
index 0000000..0a3f46f
--- /dev/null
+++ b/third_party/heimdal/kuser/copy_cred_cache.1
@@ -0,0 +1,96 @@
+.\" Copyright (c) 2004 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 April 24, 2004
+.Dt COPY_CRED_CACHE 1
+.Os HEIMDAL
+.Sh NAME
+.Nm copy_cred_cache
+.Nd copy credentials from one cache to another
+.Sh SYNOPSIS
+.Nm
+.Op Fl Fl krbtgt-only
+.Op Fl Fl service= Ns Ar principal
+.Op Fl Fl enctype= Ns Ar enctype
+.Op Fl Fl flags= Ns Ar ticketflags
+.Op Fl Fl valid-for= Ns Ar time
+.Op Fl Fl fcache-version= Ns Ar integer
+.Op Aq Ar from-cache
+.Aq Ar to-cache
+.Sh DESCRIPTION
+.Nm
+copies credentials from
+.Aq Ar from-cache
+(or the default cache) to
+.Aq Ar to-cache .
+.Pp
+Supported options:
+.Bl -tag -width Ds
+.It Fl Fl krbtgt-only
+Copies only krbtgt credentials for the client's realm. This is
+equivalent to
+.Fl Fl service= Ns Li krbtgt/ Ns Ao Ar CLIENTREALM Ac Ns Li @ Ns Ao Ar CLIENTREALM Ac .
+.It Fl Fl service= Ns Ar principal
+Copies only credentials matching this service principal.
+.It Fl Fl enctype= Ns Ar enctype
+Copies only credentials a matching enctype.
+.It Fl Fl flags= Ns Ar ticketflags
+Copies only credentials with these ticket flags set.
+.It Fl Fl valid-for= Ns Ar time
+Copies only credentials that are valid for at least this long. This
+does not take renewable creds into account.
+.It Fl Fl fcache-version= Ns Ar integer
+The created cache, If a standard
+.Li FILE
+cache is created, it will have this file format version.
+.El
+.\".Sh ENVIRONMENT
+.\".Sh FILES
+.Sh EXAMPLES
+To copy only credentials that are valid for at least one day and with
+the
+.Li initial
+flag set, try something like:
+.Bd -literal -offset indent
+$ copy_cred_cache --valid-for=1d --flags=initial FILE:/some/cache
+.Ed
+.Sh DIAGNOSTICS
+The
+.Nm
+utility exits 0 on success, and \*[Gt]0 if an error occurs, or if no
+credentials where actually copied.
+.\".Sh SEE ALSO
+.\".Sh STANDARDS
+.\".Sh HISTORY
+.\".Sh AUTHORS
+.\".Sh BUGS
diff --git a/third_party/heimdal/kuser/copy_cred_cache.c b/third_party/heimdal/kuser/copy_cred_cache.c
new file mode 100644
index 0000000..1067000
--- /dev/null
+++ b/third_party/heimdal/kuser/copy_cred_cache.c
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2004 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.
+ */
+
+#include "kuser_locl.h"
+#include <config.h>
+#include <parse_units.h>
+#include <parse_time.h>
+#include "heimtools-commands.h"
+
+static int32_t
+bitswap32(int32_t b)
+{
+ int32_t r = 0;
+ int i;
+ for (i = 0; i < 32; i++) {
+ r = r << 1 | (b & 1);
+ b = b >> 1;
+ }
+ return r;
+}
+
+static void
+parse_ticket_flags(krb5_context context,
+ const char *string, krb5_ticket_flags *ret_flags)
+{
+ TicketFlags ff;
+ int flags = parse_flags(string, asn1_TicketFlags_units(), 0);
+ if (flags == -1) /* XXX */
+ krb5_errx(context, 1, "bad flags specified: \"%s\"", string);
+
+ memset(&ff, 0, sizeof(ff));
+ ff.proxy = 1;
+ if ((size_t)parse_flags("proxy", asn1_TicketFlags_units(), 0) == TicketFlags2int(ff))
+ ret_flags->i = flags;
+ else
+ ret_flags->i = bitswap32(flags);
+}
+
+struct ctx {
+ krb5_flags whichfields;
+ krb5_creds mcreds;
+};
+
+static krb5_boolean
+matchfunc(krb5_context context, void *ptr, const krb5_creds *creds)
+{
+ struct ctx *ctx = ptr;
+ if (krb5_compare_creds(context, ctx->whichfields, &ctx->mcreds, creds))
+ return TRUE;
+ return FALSE;
+}
+
+int
+copy_cred_cache(struct copy_cred_cache_options *opt, int argc, char **argv)
+{
+ krb5_error_code ret;
+ const char *from_name, *to_name;
+ krb5_ccache from_ccache, to_ccache;
+ unsigned int matched;
+ struct ctx ctx;
+
+ memset(&ctx, 0, sizeof(ctx));
+
+ if (opt->service_string) {
+ ret = krb5_parse_name(heimtools_context, opt->service_string, &ctx.mcreds.server);
+ if (ret)
+ krb5_err(heimtools_context, 1, ret, "%s", opt->service_string);
+ }
+ if (opt->enctype_string) {
+ krb5_enctype enctype;
+ ret = krb5_string_to_enctype(heimtools_context, opt->enctype_string, &enctype);
+ if (ret)
+ krb5_err(heimtools_context, 1, ret, "%s", opt->enctype_string);
+ ctx.whichfields |= KRB5_TC_MATCH_KEYTYPE;
+ ctx.mcreds.session.keytype = enctype;
+ }
+ if (opt->flags_string) {
+ parse_ticket_flags(heimtools_context, opt->flags_string, &ctx.mcreds.flags);
+ ctx.whichfields |= KRB5_TC_MATCH_FLAGS;
+ }
+ if (opt->valid_for_string) {
+ time_t t = parse_time(opt->valid_for_string, "s");
+ if(t < 0)
+ errx(1, "unknown time \"%s\"", opt->valid_for_string);
+ krb5_timeofday(heimtools_context, &ctx.mcreds.times.endtime);
+ ctx.mcreds.times.endtime += t;
+ ctx.whichfields |= KRB5_TC_MATCH_TIMES;
+ }
+ if (opt->fcache_version_integer)
+ krb5_set_fcache_version(heimtools_context, opt->fcache_version_integer);
+
+ if (argc == 1) {
+ from_name = krb5_cc_default_name(heimtools_context);
+ to_name = argv[0];
+ } else {
+ from_name = argv[0];
+ to_name = argv[1];
+ }
+
+ ret = krb5_cc_resolve(heimtools_context, from_name, &from_ccache);
+ if (ret)
+ krb5_err(heimtools_context, 1, ret, "%s", from_name);
+
+ if (opt->krbtgt_only_flag) {
+ krb5_principal client;
+ ret = krb5_cc_get_principal(heimtools_context, from_ccache, &client);
+ if (ret)
+ krb5_err(heimtools_context, 1, ret, "getting default principal");
+ ret = krb5_make_principal(heimtools_context, &ctx.mcreds.server,
+ krb5_principal_get_realm(heimtools_context, client),
+ KRB5_TGS_NAME,
+ krb5_principal_get_realm(heimtools_context, client),
+ NULL);
+ if (ret)
+ krb5_err(heimtools_context, 1, ret, "constructing krbtgt principal");
+ krb5_free_principal(heimtools_context, client);
+ }
+ ret = krb5_cc_resolve(heimtools_context, to_name, &to_ccache);
+ if (ret)
+ krb5_err(heimtools_context, 1, ret, "%s", to_name);
+
+ ret = krb5_cc_copy_match_f(heimtools_context, from_ccache, to_ccache,
+ matchfunc, &ctx, &matched);
+ if (ret)
+ krb5_err(heimtools_context, 1, ret, "copying cred cache");
+
+ krb5_cc_close(heimtools_context, from_ccache);
+ if(matched == 0)
+ krb5_cc_destroy(heimtools_context, to_ccache);
+ else
+ krb5_cc_close(heimtools_context, to_ccache);
+
+ return matched == 0;
+}
diff --git a/third_party/heimdal/kuser/generate-requests.c b/third_party/heimdal/kuser/generate-requests.c
new file mode 100644
index 0000000..2dd71bf
--- /dev/null
+++ b/third_party/heimdal/kuser/generate-requests.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2000 - 2004 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.
+ */
+
+#include "kuser_locl.h"
+
+static unsigned
+read_words (const char *filename, char ***ret_w)
+{
+ unsigned n, alloc;
+ FILE *f;
+ char buf[256];
+ char **w = NULL;
+
+ f = fopen (filename, "r");
+ if (f == NULL)
+ err (1, "cannot open %s", filename);
+ alloc = n = 0;
+ while (fgets (buf, sizeof(buf), f) != NULL) {
+ buf[strcspn(buf, "\r\n")] = '\0';
+ if (n >= alloc) {
+ alloc += 16;
+ w = erealloc (w, alloc * sizeof(*w));
+ }
+ w[n++] = estrdup (buf);
+ }
+ *ret_w = w;
+ if (n == 0)
+ errx(1, "%s is an empty file, no words to try", filename);
+ fclose(f);
+ return n;
+}
+
+static void
+generate_requests (const char *filename, unsigned nreq)
+{
+ krb5_principal client;
+ krb5_context context;
+ krb5_error_code ret;
+ krb5_creds cred;
+ int i;
+ char **words;
+ unsigned nwords;
+
+ ret = krb5_init_context (&context);
+ if (ret)
+ errx (1, "krb5_init_context failed: %d", ret);
+
+ nwords = read_words (filename, &words);
+
+ for (i = 0; i < nreq; ++i) {
+ char *name = words[rand() % nwords];
+
+ memset(&cred, 0, sizeof(cred));
+
+ ret = krb5_parse_name (context, name, &client);
+ if (ret)
+ krb5_err (context, 1, ret, "krb5_parse_name %s", name);
+
+ ret = krb5_get_init_creds_password (context, &cred, client, "",
+ NULL, NULL, 0, NULL, NULL);
+ if (ret)
+ krb5_free_cred_contents (context, &cred);
+ krb5_free_principal(context, client);
+ }
+ free(words);
+}
+
+static int version_flag = 0;
+static int help_flag = 0;
+
+static struct getargs args[] = {
+ { "version", 0, arg_flag, &version_flag, NULL, NULL },
+ { "help", 0, arg_flag, &help_flag, NULL, NULL }
+};
+
+static void
+usage (int ret)
+{
+ arg_printusage (args,
+ sizeof(args)/sizeof(*args),
+ NULL,
+ "file number");
+ exit (ret);
+}
+
+int
+main(int argc, char **argv)
+{
+ int optidx = 0;
+ int nreq;
+ char *end;
+
+ setprogname(argv[0]);
+ if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
+ usage(1);
+
+ if (help_flag)
+ usage (0);
+
+ if(version_flag) {
+ print_version(NULL);
+ exit(0);
+ }
+
+ argc -= optidx;
+ argv += optidx;
+
+ if (argc != 2)
+ usage (1);
+ srand (0);
+ nreq = strtol (argv[1], &end, 0);
+ if (argv[1] == end || *end != '\0')
+ usage (1);
+ generate_requests (argv[0], nreq);
+ return 0;
+}
diff --git a/third_party/heimdal/kuser/heimtools-commands.in b/third_party/heimdal/kuser/heimtools-commands.in
new file mode 100644
index 0000000..ea8b073
--- /dev/null
+++ b/third_party/heimdal/kuser/heimtools-commands.in
@@ -0,0 +1,304 @@
+/*
+ * Copyright (c) 2010 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.
+ */
+
+command = {
+ name = "klist"
+ name = "list"
+ help = "List kerberos tickets"
+ option = {
+ long = "cache"
+ short = "c"
+ type = "string"
+ help = "credential cache to list"
+ }
+ option = {
+ name = "flags"
+ short = "f"
+ type = "flag"
+ help = "list flags"
+ }
+ option = {
+ long = "test"
+ short = "t"
+ type = "flag"
+ help = "test for having tickets"
+ }
+ option = {
+ name = "s"
+ short = "s"
+ type = "flag"
+ }
+ option = {
+ long = "tokens"
+ short = "T"
+ type = "flag"
+ help = "display AFS tokens"
+ }
+ option = {
+ long = "v5"
+ short = "5"
+ type = "flag"
+ default = "1"
+ help = "display v5 credential tokens"
+ }
+ option = {
+ long = "all-content"
+ short = "A"
+ type = "flag"
+ help = "List all caches with their content"
+ }
+ option = {
+ long = "list-all"
+ short = "l"
+ type = "flag"
+ help = "List all caches"
+ }
+ option = {
+ long = "json"
+ type = "flag"
+ help = "JSON formated output"
+ }
+ option = {
+ long = "verbose"
+ short = "v"
+ type = "flag"
+ help = "Verbose output"
+ }
+ option = {
+ long = "version"
+ type = "flag"
+ help = "version"
+ }
+ option = {
+ name = "a"
+ short = "a"
+ type = "flag"
+ }
+ option = {
+ name = "n"
+ short = "n"
+ type = "flag"
+ }
+ option = {
+ long = "hidden"
+ type = "flag"
+ help = "Verbose output"
+ }
+}
+command = {
+ name = "kgetcred"
+ help = "Acquire a Kerberos ticket"
+ option = {
+ long = "enctype"
+ short = "e"
+ type = "string"
+ argument = "enctype"
+ help = "Encryption type to use"
+ }
+ option = {
+ long = "cache"
+ short = "c"
+ type = "string"
+ argument = "cachename"
+ help = "Credentials cache"
+ }
+}
+command = {
+ name = "kswitch"
+ name = "switch"
+ help = "Switch default kerberos cache"
+ option = {
+ long = "type"
+ short = "t"
+ type = "string"
+ help = "type of credential cache"
+ }
+ option = {
+ long = "cache"
+ short = "c"
+ type = "string"
+ help = "name of credential cache"
+ }
+ option = {
+ long = "principal"
+ short = "p"
+ type = "string"
+ help = "name of principal"
+ }
+ option = {
+ long = "interactive"
+ short = "i"
+ type = "flag"
+ help = "interactive selection"
+ }
+};
+command = {
+ name = "kvno"
+ help = "Acquire a Kerberos ticket"
+ option = {
+ long = "enctype"
+ short = "e"
+ type = "string"
+ argument = "enctype"
+ help = "Encryption type to use"
+ }
+ option = {
+ long = "cache"
+ short = "c"
+ type = "string"
+ argument = "cachename"
+ help = "Credentials cache"
+ }
+ option = {
+ long = "keytab"
+ short = "k"
+ type = "string"
+ argument = "keytabname"
+ help = "Keytab to use"
+ }
+ option = {
+ long = "server"
+ short = "S"
+ type = "string"
+ argument = "principal"
+ help = "Server to get ticket for"
+ }
+ option = {
+ long = "quiet"
+ short = "q"
+ type = "flag"
+ help = "Quiet"
+ }
+}
+command = {
+ name = "copy_cred_cache"
+ option = {
+ long = "krbtgt-only"
+ type = "flag"
+ help = "only copy local krbtgt"
+ }
+ option = {
+ long = "service"
+ type = "string"
+ help = "limit to this service"
+ argument = "service"
+ }
+ option = {
+ long = "enctype"
+ type = "string"
+ help = "limit to this enctype"
+ argument = "enctype"
+ }
+ option = {
+ long = "flags"
+ type = "string"
+ help = "limit to these flags"
+ }
+ option = {
+ long = "valid-for"
+ type = "string"
+ help = "limit to creds valid for at least this long"
+ argument = "time"
+ }
+ option = {
+ long = "fcache-version"
+ type = "integer"
+ help = "file cache version to create"
+ }
+ min_args = "1"
+ max_args = "2"
+ help = "Copies credential caches"
+ argument = "[source] destination"
+}
+command = {
+ name = "kx509"
+ help = "Acquire or extract certificates"
+ option = {
+ long = "cache"
+ short = "c"
+ type = "string"
+ help = "Kerberos credential cache"
+ }
+ option = {
+ long = "save"
+ short = "s"
+ type = "flag"
+ help = "save the certificate and private key in the Kerberos credential cache"
+ }
+ option = {
+ long = "out"
+ short = "o"
+ type = "string"
+ help = "hx509 store for kx509 certificate and private key"
+ }
+ option = {
+ long = "extract"
+ short = "x"
+ type = "flag"
+ help = "extract certificate and private key from credential cache"
+ }
+ option = {
+ long = "test"
+ short = "t"
+ type = "integer"
+ help = "check for certificate with at least given time left"
+ }
+ option = {
+ name = "private-key"
+ short = "K"
+ type = "string"
+ help = "hx509 store containing private key"
+ }
+ option = {
+ name = "csr"
+ short = "C"
+ type = "string"
+ help = "file containing DER-encoded PKCS#10 certificate request"
+ }
+ option = {
+ name = "realm"
+ short = "r"
+ type = "string"
+ help = "realm from which to acquire certificate"
+ }
+ min_args = "0"
+ max_args = "0"
+}
+command = {
+ name = "help"
+ name = "?"
+ argument = "[command]"
+ min_args = "0"
+ max_args = "1"
+ help = "Help! I need somebody."
+}
diff --git a/third_party/heimdal/kuser/heimtools-version.rc b/third_party/heimdal/kuser/heimtools-version.rc
new file mode 100644
index 0000000..a57f9e2
--- /dev/null
+++ b/third_party/heimdal/kuser/heimtools-version.rc
@@ -0,0 +1,36 @@
+/***********************************************************************
+ * Copyright (c) 2010, 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
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER 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.
+ *
+ **********************************************************************/
+
+#define RC_FILE_TYPE VFT_APP
+#define RC_FILE_DESC_0409 "Ticket tool"
+#define RC_FILE_ORIG_0409 "heimtools.exe"
+
+#include "../windows/version.rc"
diff --git a/third_party/heimdal/kuser/heimtools.c b/third_party/heimdal/kuser/heimtools.c
new file mode 100644
index 0000000..70b23d6
--- /dev/null
+++ b/third_party/heimdal/kuser/heimtools.c
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2010 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.
+ */
+
+#include "kuser_locl.h"
+#include <sl.h>
+#include "heimtools-commands.h"
+
+krb5_context heimtools_context;
+static int version_flag;
+static int help_flag;
+
+static struct getargs args[] = {
+ { "version", 0, arg_flag, &version_flag, NULL, NULL },
+ { "help", 0, arg_flag, &help_flag, NULL, NULL }
+};
+
+static void
+usage(int ret)
+{
+ arg_printusage_i18n(args,
+ sizeof(args)/sizeof(*args),
+ N_("Usage: ", ""),
+ NULL,
+ "command ..",
+ getarg_i18n);
+ exit (ret);
+}
+
+int
+help(void *opt, int argc, char **argv)
+{
+ sl_slc_help(commands, argc, argv);
+ return 0;
+}
+
+int
+kgetcred(struct kgetcred_options *opt, int argc, char **argv)
+{
+ return 0;
+}
+
+/*
+ * Wrapper for command line compatiblity
+ */
+
+int
+kvno(struct kvno_options *opt, int argc, char **argv)
+{
+ struct kgetcred_options k;
+ memset(&k, 0, sizeof(k));
+
+ k.cache_string = opt->cache_string;
+ k.enctype_string = opt->enctype_string;
+
+ return kgetcred(&k, argc, argv);
+}
+
+static int
+command_alias(const char *name)
+{
+ const char *aliases[] = {
+ "kinit", "klist", "kswitch", "kgetcred", "kvno", "kdeltkt",
+ "kdestroy", "kcpytkt", NULL
+ }, **p = aliases;
+
+ while (*p && strcmp(name, *p) != 0)
+ p++;
+ return *p != NULL;
+}
+
+
+int
+main(int argc, char **argv)
+{
+ krb5_error_code ret;
+ int optidx = 0;
+ int exit_status = 0;
+
+ setprogname (argv[0]);
+
+ setlocale (LC_ALL, "");
+ bindtextdomain ("heimdal_kuser", HEIMDAL_LOCALEDIR);
+ textdomain("heimdal_kuser");
+
+ ret = krb5_init_context(&heimtools_context);
+ if (ret == KRB5_CONFIG_BADFORMAT)
+ errx (1, "krb5_init_context failed to parse configuration file");
+ else if (ret)
+ errx(1, "krb5_init_context failed: %d", ret);
+
+ /*
+ * Support linking of heimtools to commands
+ */
+
+ if (!command_alias(getprogname())) {
+
+ if (argc == 1) {
+ sl_slc_help(commands, 0, NULL);
+ return 1;
+ }
+
+ if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
+ usage(1);
+
+ if (help_flag)
+ usage (0);
+
+ if(version_flag) {
+ print_version(NULL);
+ exit(0);
+ }
+
+ } else {
+ argv[0] = rk_UNCONST(getprogname());
+ }
+
+ argc -= optidx;
+ argv += optidx;
+
+ if (argc != 0) {
+ ret = sl_command(commands, argc, argv);
+ if(ret == -1)
+ sl_did_you_mean(commands, argv[0]);
+ else if (ret == -2)
+ ret = 0;
+ if(ret != 0)
+ exit_status = 1;
+ } else {
+ sl_slc_help(commands, argc, argv);
+ exit_status = 1;
+ }
+
+ krb5_free_context(heimtools_context);
+ return exit_status;
+}
diff --git a/third_party/heimdal/kuser/kcpytkt.c b/third_party/heimdal/kuser/kcpytkt.c
new file mode 100644
index 0000000..591a6ee
--- /dev/null
+++ b/third_party/heimdal/kuser/kcpytkt.c
@@ -0,0 +1,178 @@
+
+#include "kuser_locl.h"
+
+static char *etypestr = 0;
+static char *fromccachestr = 0;
+static char *flagstr = 0;
+static int exp_ok = 0;
+static int quiet_flag = 0;
+static int version_flag = 0;
+static int help_flag = 0;
+
+struct getargs args[] = {
+ { "cache", 'c', arg_string, &fromccachestr,
+ "Credentials cache", "cachename" },
+ { "enctype", 'e', arg_string, &etypestr,
+ "Encryption type", "enctype" },
+ { "flags", 'f', arg_string, &flagstr,
+ "Flags", "flags" },
+ { "expired-ok", 'E', arg_flag, &exp_ok,
+ "Keep expired tickets" },
+ { "quiet", 'q', arg_flag, &quiet_flag, "Quiet" },
+ { "version", 0, arg_flag, &version_flag },
+ { "help", 0, arg_flag, &help_flag }
+};
+
+static void
+usage(int ret)
+{
+ arg_printusage(args, sizeof(args)/sizeof(args[0]),
+ "Usage: ", "dest_ccache service1 [service2 ...]");
+ exit (ret);
+}
+
+static void do_kcpytkt (int argc, char *argv[], char *fromccachestr, char *etypestr, int flags);
+
+int main(int argc, char *argv[])
+{
+ int optidx;
+ int flags = 0;
+
+ setprogname(argv[0]);
+
+ if (getarg(args, sizeof(args)/sizeof(args[0]), argc, argv, &optidx))
+ usage(1);
+
+ if (help_flag)
+ usage(0);
+
+ if (version_flag) {
+ print_version(NULL);
+ exit(0);
+ }
+
+ argc -= optidx;
+ argv += optidx;
+
+ if (argc < 2)
+ usage(1);
+
+ if (flagstr)
+ flags = atoi(flagstr);
+
+ do_kcpytkt(argc, argv, fromccachestr, etypestr, flags);
+
+ return 0;
+}
+
+static void do_kcpytkt (int count, char *names[],
+ char *fromccachestr, char *etypestr, int flags)
+{
+ krb5_context context;
+ krb5_error_code ret;
+ int i, errors;
+ krb5_enctype etype;
+ krb5_ccache fromccache;
+ krb5_ccache destccache;
+ krb5_principal me;
+ krb5_creds in_creds, out_creds;
+ int retflags;
+ char *princ;
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ errx(1, "krb5_init_context failed: %d", ret);
+
+ if (etypestr) {
+ ret = krb5_string_to_enctype(context, etypestr, &etype);
+ if (ret)
+ krb5_err(context, 1, ret, "Can't convert enctype %s", etypestr);
+ retflags = KRB5_TC_MATCH_SRV_NAMEONLY | KRB5_TC_MATCH_KEYTYPE;
+ } else {
+ etype = 0;
+ retflags = KRB5_TC_MATCH_SRV_NAMEONLY;
+ }
+
+ if (fromccachestr)
+ ret = krb5_cc_resolve(context, fromccachestr, &fromccache);
+ else
+ ret = krb5_cc_default(context, &fromccache);
+ if (ret)
+ krb5_err(context, 1, ret, "Can't resolve credentials cache");
+
+ ret = krb5_cc_get_principal(context, fromccache, &me);
+ if (ret)
+ krb5_err(context, 1, ret, "Can't query client principal name");
+
+ ret = krb5_cc_resolve(context, names[0], &destccache);
+ if (ret)
+ krb5_err(context, 1, ret, "Can't resolve destination cache");
+
+ errors = 0;
+
+ for (i = 1; i < count; i++) {
+ memset(&in_creds, 0, sizeof(in_creds));
+
+ in_creds.client = me;
+
+ ret = krb5_parse_name(context, names[i], &in_creds.server);
+ if (ret) {
+ if (!quiet_flag)
+ krb5_warn(context, ret, "Parse error for %s", names[i]);
+ errors++;
+ continue;
+ }
+
+ ret = krb5_unparse_name(context, in_creds.server, &princ);
+ if (ret) {
+ krb5_warn(context, ret, "Unparse error for %s", names[i]);
+ errors++;
+ continue;
+ }
+
+ in_creds.session.keytype = etype;
+
+ if (!exp_ok) {
+ krb5_timeofday(context, &in_creds.times.endtime);
+ retflags |= KRB5_TC_MATCH_TIMES;
+ }
+
+ ret = krb5_cc_retrieve_cred(context, fromccache, retflags,
+ &in_creds, &out_creds);
+ if (ret) {
+ krb5_warn(context, ret, "Can't retrieve credentials for %s", princ);
+
+ krb5_free_unparsed_name(context, princ);
+
+ errors++;
+ continue;
+ }
+
+ ret = krb5_cc_store_cred(context, destccache, &out_creds);
+
+ krb5_free_principal(context, in_creds.server);
+
+ if (ret) {
+ krb5_warn(context, ret, "Can't store credentials for %s", princ);
+
+ krb5_free_cred_contents(context, &out_creds);
+ krb5_free_unparsed_name(context, princ);
+
+ errors++;
+ continue;
+ }
+
+ krb5_free_unparsed_name(context, princ);
+ krb5_free_cred_contents(context, &out_creds);
+ }
+
+ krb5_free_principal(context, me);
+ krb5_cc_close(context, fromccache);
+ krb5_cc_close(context, destccache);
+ krb5_free_context(context);
+
+ if (errors)
+ exit(1);
+
+ exit(0);
+}
diff --git a/third_party/heimdal/kuser/kdecode_ticket.c b/third_party/heimdal/kuser/kdecode_ticket.c
new file mode 100644
index 0000000..edb0cc8
--- /dev/null
+++ b/third_party/heimdal/kuser/kdecode_ticket.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 1997 - 2004 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.
+ */
+
+#include "kuser_locl.h"
+
+static char *etype_str;
+static int version_flag;
+static int help_flag;
+
+static void
+print_and_decode_tkt (krb5_context context,
+ krb5_data *ticket,
+ krb5_principal server,
+ krb5_enctype enctype)
+{
+ krb5_error_code ret;
+ krb5_crypto crypto;
+ krb5_data dec_data;
+ size_t len;
+ EncTicketPart decr_part;
+ krb5_keyblock key;
+ Ticket tkt;
+
+ ret = decode_Ticket (ticket->data, ticket->length, &tkt, &len);
+ if (ret)
+ krb5_err (context, 1, ret, "decode_Ticket");
+
+ ret = krb5_string_to_key (context, enctype, "foo", server, &key);
+ if (ret)
+ krb5_err (context, 1, ret, "krb5_string_to_key");
+
+ ret = krb5_crypto_init(context, &key, 0, &crypto);
+ if (ret)
+ krb5_err (context, 1, ret, "krb5_crypto_init");
+
+ ret = krb5_decrypt_EncryptedData (context, crypto, KRB5_KU_TICKET,
+ &tkt.enc_part, &dec_data);
+ krb5_crypto_destroy (context, crypto);
+ if (ret)
+ krb5_err (context, 1, ret, "krb5_decrypt_EncryptedData");
+ ret = decode_EncTicketPart (dec_data.data, dec_data.length,
+ &decr_part, &len);
+ krb5_data_free (&dec_data);
+ if (ret)
+ krb5_err (context, 1, ret, "krb5_decode_EncTicketPart");
+ free_EncTicketPart(&decr_part);
+}
+
+struct getargs args[] = {
+ { "enctype", 'e', arg_string, &etype_str,
+ "encryption type to use", "enctype"},
+ { "version", 0, arg_flag, &version_flag, NULL, NULL },
+ { "help", 0, arg_flag, &help_flag, NULL, NULL }
+};
+
+static void
+usage (int ret)
+{
+ arg_printusage (args,
+ sizeof(args)/sizeof(*args),
+ NULL,
+ "service");
+ exit (ret);
+}
+
+int
+main(int argc, char **argv)
+{
+ krb5_error_code ret;
+ krb5_context context;
+ krb5_ccache cache;
+ krb5_creds in, *out;
+ int optidx = 0;
+
+ setprogname (argv[0]);
+
+ ret = krb5_init_context (&context);
+ if (ret)
+ errx(1, "krb5_init_context failed: %d", ret);
+
+ if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
+ usage(1);
+
+ if (help_flag)
+ usage (0);
+
+ if(version_flag) {
+ print_version(NULL);
+ exit(0);
+ }
+
+ argc -= optidx;
+ argv += optidx;
+
+ if (argc != 1)
+ usage (1);
+
+ ret = krb5_cc_default(context, &cache);
+ if (ret)
+ krb5_err (context, 1, ret, "krb5_cc_default");
+
+ memset(&in, 0, sizeof(in));
+
+ if (etype_str) {
+ krb5_enctype enctype;
+
+ ret = krb5_string_to_enctype(context, etype_str, &enctype);
+ if (ret)
+ krb5_errx (context, 1, "unrecognized enctype: %s", etype_str);
+ in.session.keytype = enctype;
+ }
+
+ ret = krb5_cc_get_principal(context, cache, &in.client);
+ if (ret)
+ krb5_err (context, 1, ret, "krb5_cc_get_principal");
+
+ ret = krb5_parse_name(context, argv[0], &in.server);
+ if (ret)
+ krb5_err (context, 1, ret, "krb5_parse_name %s", argv[0]);
+
+ in.times.endtime = 0;
+ ret = krb5_get_credentials(context, 0, cache, &in, &out);
+ if (ret)
+ krb5_err (context, 1, ret, "krb5_get_credentials");
+
+ print_and_decode_tkt (context, &out->ticket, out->server,
+ out->session.keytype);
+
+ krb5_free_cred_contents(context, out);
+ return 0;
+}
diff --git a/third_party/heimdal/kuser/kdeltkt.c b/third_party/heimdal/kuser/kdeltkt.c
new file mode 100644
index 0000000..aab7837
--- /dev/null
+++ b/third_party/heimdal/kuser/kdeltkt.c
@@ -0,0 +1,172 @@
+
+#include "kuser_locl.h"
+
+static char *etypestr = 0;
+static char *ccachestr = 0;
+static char *flagstr = 0;
+static int exp_only = 0;
+static int quiet_flag = 0;
+static int help_flag = 0;
+static int version_flag = 0;
+
+struct getargs args[] = {
+ { "cache", 'c', arg_string, &ccachestr,
+ "Credentials cache", "cachename" },
+ { "enctype", 'e', arg_string, &etypestr,
+ "Encryption type", "enctype" },
+ { "flags", 'f', arg_string, &flagstr,
+ "Flags", "flags" },
+ { "expired-only", 'E', arg_flag, &exp_only,
+ "Delete only expired tickets" },
+ { "quiet", 'q', arg_flag, &quiet_flag, "Quiet" },
+ { "version", 0, arg_flag, &version_flag },
+ { "help", 0, arg_flag, &help_flag }
+};
+
+static void
+usage(int ret)
+{
+ arg_printusage(args, sizeof(args)/sizeof(args[0]),
+ "Usage: ", "service1 [service2 ...]");
+ exit(ret);
+}
+
+static void do_kdeltkt (int argc, char *argv[], char *ccachestr, char *etypestr, int flags);
+
+int main(int argc, char *argv[])
+{
+ int optidx = 0;
+ int flags = 0;
+
+ setprogname(argv[0]);
+
+ if (getarg(args, sizeof(args)/sizeof(args[0]), argc, argv, &optidx))
+ usage (1);
+
+ if (help_flag)
+ usage(0);
+
+ if (version_flag) {
+ print_version(NULL);
+ exit(0);
+ }
+
+ argc -= optidx;
+ argv += optidx;
+
+ if (argc < 1)
+ usage (1);
+
+ if (flagstr)
+ flags = atoi(flagstr);
+
+ do_kdeltkt(argc, argv, ccachestr, etypestr, flags);
+
+ return 0;
+}
+
+static void do_kdeltkt (int count, char *names[],
+ char *ccachestr, char *etypestr, int flags)
+{
+ krb5_context context;
+ krb5_error_code ret;
+ int i, errors;
+ krb5_enctype etype;
+ krb5_ccache ccache;
+ krb5_principal me;
+ krb5_creds in_creds, out_creds;
+ int retflags;
+ char *princ;
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ errx(1, "krb5_init_context failed: %d", ret);
+
+ if (etypestr) {
+ ret = krb5_string_to_enctype(context, etypestr, &etype);
+ if (ret)
+ krb5_err(context, 1, ret, "Can't convert enctype %s", etypestr);
+ retflags = KRB5_TC_MATCH_SRV_NAMEONLY | KRB5_TC_MATCH_KEYTYPE;
+ } else {
+ etype = 0;
+ retflags = KRB5_TC_MATCH_SRV_NAMEONLY;
+ }
+
+ if (ccachestr)
+ ret = krb5_cc_resolve(context, ccachestr, &ccache);
+ else
+ ret = krb5_cc_default(context, &ccache);
+ if (ret)
+ krb5_err(context, 1, ret, "Can't open credentials cache");
+
+ ret = krb5_cc_get_principal(context, ccache, &me);
+ if (ret)
+ krb5_err(context, 1, ret, "Can't get client principal");
+
+ errors = 0;
+
+ for (i = 0; i < count; i++) {
+ memset(&in_creds, 0, sizeof(in_creds));
+
+ in_creds.client = me;
+
+ ret = krb5_parse_name(context, names[i], &in_creds.server);
+ if (ret) {
+ if (!quiet_flag)
+ krb5_warn(context, ret, "Can't parse principal name %s", names[i]);
+ errors++;
+ continue;
+ }
+
+ ret = krb5_unparse_name(context, in_creds.server, &princ);
+ if (ret) {
+ krb5_warn(context, ret, "Can't unparse principal name %s", names[i]);
+ errors++;
+ continue;
+ }
+
+ in_creds.session.keytype = etype;
+
+ if (exp_only) {
+ krb5_timeofday(context, &in_creds.times.endtime);
+ retflags |= KRB5_TC_MATCH_TIMES;
+ }
+
+ ret = krb5_cc_retrieve_cred(context, ccache, retflags,
+ &in_creds, &out_creds);
+ if (ret) {
+ krb5_warn(context, ret, "Can't retrieve credentials for %s", princ);
+
+ krb5_free_unparsed_name(context, princ);
+
+ errors++;
+ continue;
+ }
+
+ ret = krb5_cc_remove_cred(context, ccache, flags, &out_creds);
+
+ krb5_free_principal(context, in_creds.server);
+
+ if (ret) {
+ krb5_warn(context, ret, "Can't remove credentials for %s", princ);
+
+ krb5_free_cred_contents(context, &out_creds);
+ krb5_free_unparsed_name(context, princ);
+
+ errors++;
+ continue;
+ }
+
+ krb5_free_unparsed_name(context, princ);
+ krb5_free_cred_contents(context, &out_creds);
+ }
+
+ krb5_free_principal(context, me);
+ krb5_cc_close(context, ccache);
+ krb5_free_context(context);
+
+ if (errors)
+ exit(1);
+
+ exit(0);
+}
diff --git a/third_party/heimdal/kuser/kdestroy-version.rc b/third_party/heimdal/kuser/kdestroy-version.rc
new file mode 100644
index 0000000..9ccbdef
--- /dev/null
+++ b/third_party/heimdal/kuser/kdestroy-version.rc
@@ -0,0 +1,36 @@
+/***********************************************************************
+ * Copyright (c) 2010, 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
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER 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.
+ *
+ **********************************************************************/
+
+#define RC_FILE_TYPE VFT_APP
+#define RC_FILE_DESC_0409 "Destroy Kerberos Tickets"
+#define RC_FILE_ORIG_0409 "kdestroy.exe"
+
+#include "../windows/version.rc"
diff --git a/third_party/heimdal/kuser/kdestroy.1 b/third_party/heimdal/kuser/kdestroy.1
new file mode 100644
index 0000000..3c93665
--- /dev/null
+++ b/third_party/heimdal/kuser/kdestroy.1
@@ -0,0 +1,75 @@
+.\" Copyright (c) 1997, 1999, 2001, 2004, 2006 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 April 27, 2006
+.Dt KDESTROY 1
+.Os HEIMDAL
+.Sh NAME
+.Nm kdestroy
+.Nd remove one credential or destroy the current ticket file
+.Sh SYNOPSIS
+.Nm
+.Bk -words
+.Op Fl c Ar cachefile
+.Op Fl Fl credential= Ns Ar principal
+.Op Fl Fl cache= Ns Ar cachefile
+.Op Fl A | Fl Fl all
+.Op Fl Fl no-unlog
+.Op Fl Fl no-delete-v4
+.Op Fl Fl version
+.Op Fl Fl help
+.Ek
+.Sh DESCRIPTION
+.Nm
+removes one credential or the current set of tickets.
+.Pp
+Supported options:
+.Bl -tag -width Ds
+.It Fl credential= Ns Ar principal
+remove
+.Fa principal
+from the credential cache if it exists.
+.It Fl c Ar cachefile
+.It Fl cache= Ns Ar cachefile
+The cache file to remove.
+.It Fl A
+.It Fl Fl all
+remove all credential caches.
+.It Fl Fl no-unlog
+Do not remove AFS tokens.
+.It Fl Fl no-delete-v4
+Do not remove v4 tickets.
+.El
+.Sh SEE ALSO
+.Xr kinit 1 ,
+.Xr klist 1
diff --git a/third_party/heimdal/kuser/kdestroy.c b/third_party/heimdal/kuser/kdestroy.c
new file mode 100644
index 0000000..1823bf5
--- /dev/null
+++ b/third_party/heimdal/kuser/kdestroy.c
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 1997 - 2000, 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.
+ */
+
+#include "kuser_locl.h"
+
+static const char *cache;
+static const char *credential;
+static int help_flag;
+static int version_flag;
+#ifndef NO_AFS
+static int unlog_flag = 1;
+#endif
+static int dest_tkt_flag = 1;
+static int all_flag = 0;
+
+struct getargs args[] = {
+ { "credential", 0, arg_string, rk_UNCONST(&credential),
+ "remove one credential", "principal" },
+ { "cache", 'c', arg_string, rk_UNCONST(&cache), "cache to destroy", "cache" },
+ { "all", 'A', arg_flag, &all_flag, "destroy all caches", NULL },
+#ifndef NO_AFS
+ { "unlog", 0, arg_negative_flag, &unlog_flag,
+ "do not destroy tokens", NULL },
+#endif
+ { "delete-v4", 0, arg_negative_flag, &dest_tkt_flag,
+ "do not destroy v4 tickets", NULL },
+ { "version", 0, arg_flag, &version_flag, NULL, NULL },
+ { "help", 'h', arg_flag, &help_flag, NULL, NULL}
+};
+
+int num_args = sizeof(args) / sizeof(args[0]);
+
+static void
+usage (int status)
+{
+ arg_printusage (args, num_args, NULL, "");
+ exit (status);
+}
+
+int
+main (int argc, char **argv)
+{
+ krb5_error_code ret;
+ krb5_context context;
+ krb5_ccache ccache;
+ int optidx = 0;
+ int exit_val = 0;
+
+ setprogname (argv[0]);
+
+ if(getarg(args, num_args, argc, argv, &optidx))
+ usage(1);
+
+ if (help_flag)
+ usage (0);
+
+ if(version_flag){
+ print_version(NULL);
+ exit(0);
+ }
+
+ argc -= optidx;
+ argv += optidx;
+
+ if (argc != 0)
+ usage (1);
+
+ ret = krb5_init_context (&context);
+ if (ret)
+ errx (1, "krb5_init_context failed: %d", ret);
+
+ if (all_flag) {
+ krb5_cccol_cursor cursor;
+
+ ret = krb5_cccol_cursor_new (context, &cursor);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cccol_cursor_new");
+
+ while (krb5_cccol_cursor_next (context, cursor, &ccache) == 0 && ccache != NULL) {
+
+ ret = krb5_cc_destroy (context, ccache);
+ if (ret) {
+ krb5_warn(context, ret, "krb5_cc_destroy");
+ exit_val = 1;
+ }
+ }
+ krb5_cccol_cursor_free(context, &cursor);
+
+ } else {
+ if(cache == NULL) {
+ ret = krb5_cc_default(context, &ccache);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_default");
+ } else {
+ ret = krb5_cc_resolve(context,
+ cache,
+ &ccache);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_resolve");
+ }
+
+ if (ret == 0) {
+ if (credential) {
+ krb5_creds mcred;
+
+ krb5_cc_clear_mcred(&mcred);
+
+ ret = krb5_parse_name(context, credential, &mcred.server);
+ if (ret)
+ krb5_err(context, 1, ret,
+ "Can't parse principal %s", credential);
+
+ ret = krb5_cc_remove_cred(context, ccache, 0, &mcred);
+ if (ret)
+ krb5_err(context, 1, ret,
+ "Failed to remove principal %s", credential);
+
+ krb5_cc_close(context, ccache);
+ krb5_free_principal(context, mcred.server);
+ krb5_free_context(context);
+ return 0;
+ }
+
+ ret = krb5_cc_destroy (context, ccache);
+ if (ret) {
+ krb5_warn(context, ret, "krb5_cc_destroy");
+ exit_val = 1;
+ }
+ }
+ }
+
+ krb5_free_context (context);
+
+#ifndef NO_AFS
+ if (unlog_flag && k_hasafs ()) {
+ if (k_unlog ())
+ exit_val = 1;
+ }
+#endif
+
+ return exit_val;
+}
diff --git a/third_party/heimdal/kuser/kdigest-commands.in b/third_party/heimdal/kuser/kdigest-commands.in
new file mode 100644
index 0000000..3f73f5b
--- /dev/null
+++ b/third_party/heimdal/kuser/kdigest-commands.in
@@ -0,0 +1,280 @@
+/*
+ * Copyright (c) 2006 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$ */
+
+command = {
+ name = "digest-probe"
+ option = {
+ long = "realm"
+ type = "string"
+ help = "Kerberos realm to communicate with"
+ }
+ help = "probe what mech is allowed/supported for this server"
+}
+command = {
+ name = "digest-server-init"
+ option = {
+ long = "type"
+ type = "string"
+ help = "digest type"
+ default = "sasl"
+ }
+ option = {
+ long = "kerberos-realm"
+ type = "string"
+ argument = "realm"
+ help = ""
+ }
+ option = {
+ long = "digest"
+ type = "string"
+ argument = "digest-type"
+ help = "digest type to use in the algorithm"
+ }
+ option = {
+ long = "cb-type"
+ type = "string"
+ argument = "type"
+ help = "type of channel bindings"
+ }
+ option = {
+ long = "cb-value"
+ type = "string"
+ argument = "value"
+ help = "value of channel bindings"
+ }
+ option = {
+ long = "hostname"
+ type = "string"
+ argument = "hostname"
+ help = "hostname of the server"
+ }
+ option = {
+ long = "realm"
+ type = "string"
+ help = "Kerberos realm to communicate with"
+ }
+ help = "Sets up a digest context and return initial parameters"
+}
+command = {
+ name = "digest-server-request"
+ option = {
+ long = "type"
+ type = "string"
+ help = "digest type"
+ default = "sasl"
+ }
+ option = {
+ long = "kerberos-realm"
+ type = "string"
+ argument = "realm"
+ help = ""
+ }
+ option = {
+ long = "username"
+ type = "string"
+ argument = "name"
+ help = "digest type"
+ }
+ option = {
+ long = "server-nonce"
+ type = "string"
+ argument = "nonce"
+ help = ""
+ }
+ option = {
+ long = "server-identifier"
+ type = "string"
+ argument = "nonce"
+ help = ""
+ }
+ option = {
+ long = "client-nonce"
+ type = "string"
+ argument = "nonce"
+ help = ""
+ }
+ option = {
+ long = "client-response"
+ type = "string"
+ argument = "response"
+ help = ""
+ }
+ option = {
+ long = "opaque"
+ type = "string"
+ argument = "string"
+ help = ""
+ }
+ option = {
+ long = "authentication-name"
+ type = "string"
+ argument = "name"
+ help = ""
+ }
+ option = {
+ long = "realm"
+ type = "string"
+ argument = "realm"
+ help = ""
+ }
+ option = {
+ long = "method"
+ type = "string"
+ argument = "method"
+ help = ""
+ }
+ option = {
+ long = "uri"
+ type = "string"
+ argument = "uri"
+ help = ""
+ }
+ option = {
+ long = "nounce-count"
+ type = "string"
+ argument = "count"
+ help = ""
+ }
+ option = {
+ long = "qop"
+ type = "string"
+ argument = "qop"
+ help = ""
+ }
+ option = {
+ long = "ccache"
+ type = "string"
+ argument = "ccache"
+ help = "Where the the credential cache is created when the KDC returns tickets"
+ }
+ help = "Completes digest negotiation and return final parameters"
+}
+command = {
+ name = "digest-client-request"
+ option = {
+ long = "type"
+ type = "string"
+ help = "digest type"
+ default = "sasl"
+ }
+ option = {
+ long = "username"
+ type = "string"
+ argument = "name"
+ help = "digest type"
+ }
+ option = {
+ long = "password"
+ type = "string"
+ argument = "password"
+ }
+ option = {
+ long = "server-nonce"
+ type = "string"
+ argument = "nonce"
+ help = ""
+ }
+ option = {
+ long = "server-identifier"
+ type = "string"
+ argument = "nonce"
+ help = ""
+ }
+ option = {
+ long = "client-nonce"
+ type = "string"
+ argument = "nonce"
+ help = ""
+ }
+ option = {
+ long = "opaque"
+ type = "string"
+ argument = "string"
+ help = ""
+ }
+ option = {
+ long = "realm"
+ type = "string"
+ argument = "realm"
+ help = ""
+ }
+ option = {
+ long = "method"
+ type = "string"
+ argument = "method"
+ help = ""
+ }
+ option = {
+ long = "uri"
+ type = "string"
+ argument = "uri"
+ help = ""
+ }
+ option = {
+ long = "nounce-count"
+ type = "string"
+ argument = "count"
+ help = ""
+ }
+ option = {
+ long = "qop"
+ type = "string"
+ argument = "qop"
+ help = ""
+ }
+ help = "Client part of a digest exchange"
+}
+command = {
+ name = "ntlm-server-init"
+ option = {
+ long = "version"
+ type = "integer"
+ help = "ntlm version"
+ default = "1"
+ }
+ option = {
+ long = "kerberos-realm"
+ type = "string"
+ help = "Kerberos realm to communicate with"
+ }
+ help = "Sets up a digest context and return initial parameters"
+}
+command = {
+ name = "help"
+ name = "?"
+ argument = "[command]"
+ min_args = "0"
+ max_args = "1"
+ help = "Help! I need somebody."
+}
diff --git a/third_party/heimdal/kuser/kdigest-version.rc b/third_party/heimdal/kuser/kdigest-version.rc
new file mode 100644
index 0000000..8e5b16e
--- /dev/null
+++ b/third_party/heimdal/kuser/kdigest-version.rc
@@ -0,0 +1,36 @@
+/***********************************************************************
+ * Copyright (c) 2010, 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
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER 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.
+ *
+ **********************************************************************/
+
+#define RC_FILE_TYPE VFT_APP
+#define RC_FILE_DESC_0409 "KDC Digest Interface Tool"
+#define RC_FILE_ORIG_0409 "kdigest.exe"
+
+#include "../windows/version.rc"
diff --git a/third_party/heimdal/kuser/kdigest.8 b/third_party/heimdal/kuser/kdigest.8
new file mode 100644
index 0000000..6b633c3
--- /dev/null
+++ b/third_party/heimdal/kuser/kdigest.8
@@ -0,0 +1,257 @@
+.\" Copyright (c) 2008 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 September 25, 2008
+.Dt KDIGEST 8
+.Os HEIMDAL
+.Sh NAME
+.Nm kdigest
+.Nd userland tool to access digest interface in the KDC
+.Sh SYNOPSIS
+.Nm
+.Op Fl Fl ccache= Ns Ar string
+.Op Fl Fl version
+.Op Fl Fl help
+command
+.Op arguments
+.Sh DESCRIPTION
+Supported options:
+.Bl -tag -width Ds
+.It Xo
+.Fl Fl ccache= Ns Ar string
+.Xc
+credential cache
+.It Xo
+.Fl Fl version
+.Xc
+print version
+.It Xo
+.Fl Fl help
+.Xc
+.El
+.Pp
+Available commands are:
+.Bl -tag -width Ds
+.It Xo digest-probe
+.Op Fl Fl realm= Ns Ar string
+.Op Fl h | Fl Fl help
+.Xc
+.Bl -tag -width Ds
+.It Xo
+.Fl Fl realm= Ns Ar string
+.Xc
+Kerberos realm to communicate with
+.El
+.It Xo digest-server-init
+.Op Fl Fl type= Ns Ar string
+.Op Fl Fl kerberos-realm= Ns Ar realm
+.Op Fl Fl digest= Ns Ar digest-type
+.Op Fl Fl cb-type= Ns Ar type
+.Op Fl Fl cb-value= Ns Ar value
+.Op Fl Fl hostname= Ns Ar hostname
+.Op Fl Fl realm= Ns Ar string
+.Xc
+.Bl -tag -width Ds
+.It Xo
+.Fl Fl type= Ns Ar string
+.Xc
+digest type
+.It Xo
+.Fl Fl kerberos-realm= Ns Ar realm
+.Xc
+.It Xo
+.Fl Fl digest= Ns Ar digest-type
+.Xc
+digest type to use in the algorithm
+.It Xo
+.Fl Fl cb-type= Ns Ar type
+.Xc
+type of channel bindings
+.It Xo
+.Fl Fl cb-value= Ns Ar value
+.Xc
+value of channel bindings
+.It Xo
+.Fl Fl hostname= Ns Ar hostname
+.Xc
+hostname of the server
+.It Xo
+.Fl Fl realm= Ns Ar string
+.Xc
+Kerberos realm to communicate with
+.El
+.It Xo digest-server-request
+.Op Fl Fl type= Ns Ar string
+.Op Fl Fl kerberos-realm= Ns Ar realm
+.Op Fl Fl username= Ns Ar name
+.Op Fl Fl server-nonce= Ns Ar nonce
+.Op Fl Fl server-identifier= Ns Ar nonce
+.Op Fl Fl client-nonce= Ns Ar nonce
+.Op Fl Fl client-response= Ns Ar response
+.Op Fl Fl opaque= Ns Ar string
+.Op Fl Fl authentication-name= Ns Ar name
+.Op Fl Fl realm= Ns Ar realm
+.Op Fl Fl method= Ns Ar method
+.Op Fl Fl uri= Ns Ar uri
+.Op Fl Fl nounce-count= Ns Ar count
+.Op Fl Fl qop= Ns Ar qop
+.Op Fl Fl ccache= Ns Ar ccache
+.Xc
+.Bl -tag -width Ds
+.It Xo
+.Fl Fl type= Ns Ar string
+.Xc
+digest type
+.It Xo
+.Fl Fl kerberos-realm= Ns Ar realm
+.Xc
+.It Xo
+.Fl Fl username= Ns Ar name
+.Xc
+digest type
+.It Xo
+.Fl Fl server-nonce= Ns Ar nonce
+.Xc
+.It Xo
+.Fl Fl server-identifier= Ns Ar nonce
+.Xc
+.It Xo
+.Fl Fl client-nonce= Ns Ar nonce
+.Xc
+.It Xo
+.Fl Fl client-response= Ns Ar response
+.Xc
+.It Xo
+.Fl Fl opaque= Ns Ar string
+.Xc
+.It Xo
+.Fl Fl authentication-name= Ns Ar name
+.Xc
+.It Xo
+.Fl Fl realm= Ns Ar realm
+.Xc
+.It Xo
+.Fl Fl method= Ns Ar method
+.Xc
+.It Xo
+.Fl Fl uri= Ns Ar uri
+.Xc
+.It Xo
+.Fl Fl nounce-count= Ns Ar count
+.Xc
+.It Xo
+.Fl Fl qop= Ns Ar qop
+.Xc
+.It Xo
+.Fl Fl ccache= Ns Ar ccache
+.Xc
+Where the the credential cache is created when the KDC returns tickets
+.El
+.It Xo digest-client-request
+.Op Fl Fl type= Ns Ar string
+.Op Fl Fl username= Ns Ar name
+.Op Fl Fl password= Ns Ar password
+.Op Fl Fl server-nonce= Ns Ar nonce
+.Op Fl Fl server-identifier= Ns Ar nonce
+.Op Fl Fl client-nonce= Ns Ar nonce
+.Op Fl Fl opaque= Ns Ar string
+.Op Fl Fl realm= Ns Ar realm
+.Op Fl Fl method= Ns Ar method
+.Op Fl Fl uri= Ns Ar uri
+.Op Fl Fl nounce-count= Ns Ar count
+.Op Fl Fl qop= Ns Ar qop
+.Xc
+.Bl -tag -width Ds
+.It Xo
+.Fl Fl type= Ns Ar string
+.Xc
+digest type
+.It Xo
+.Fl Fl username= Ns Ar name
+.Xc
+digest type
+.It Xo
+.Fl Fl password= Ns Ar password
+.Xc
+.It Xo
+.Fl Fl server-nonce= Ns Ar nonce
+.Xc
+.It Xo
+.Fl Fl server-identifier= Ns Ar nonce
+.Xc
+.It Xo
+.Fl Fl client-nonce= Ns Ar nonce
+.Xc
+.It Xo
+.Fl Fl opaque= Ns Ar string
+.Xc
+.It Xo
+.Fl Fl realm= Ns Ar realm
+.Xc
+.It Xo
+.Fl Fl method= Ns Ar method
+.Xc
+.It Xo
+.Fl Fl uri= Ns Ar uri
+.Xc
+.It Xo
+.Fl Fl nounce-count= Ns Ar count
+.Xc
+.It Xo
+.Fl Fl qop= Ns Ar qop
+.Xc
+.El
+.It Xo ntlm-server-init
+.Op Fl Fl version= Ns Ar integer
+.Op Fl Fl kerberos-realm= Ns Ar string
+.Xc
+.Bl -tag -width Ds
+.It Xo
+.Fl Fl version= Ns Ar integer
+.Xc
+ntlm version
+.It Xo
+.Fl Fl kerberos-realm= Ns Ar string
+.Xc
+Kerberos realm to communicate with
+.El
+.El
+.\".Sh ENVIRONMENT
+.\".Sh FILES
+.\".Sh EXAMPLES
+.\".Sh DIAGNOSTICS
+.\".Sh SEE ALSO
+.\".Sh STANDARDS
+.\".Sh HISTORY
+.\".Sh AUTHORS
+.\".Sh BUGS
diff --git a/third_party/heimdal/kuser/kdigest.c b/third_party/heimdal/kuser/kdigest.c
new file mode 100644
index 0000000..c538151
--- /dev/null
+++ b/third_party/heimdal/kuser/kdigest.c
@@ -0,0 +1,572 @@
+/*
+ * Copyright (c) 2006 - 2007 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.
+ */
+
+#define HC_DEPRECATED_CRYPTO
+
+#include "kuser_locl.h"
+
+#include <kdigest-commands.h>
+#include <hex.h>
+#include <base64.h>
+#include <heimntlm.h>
+#include "crypto-headers.h"
+
+static int version_flag = 0;
+static int help_flag = 0;
+static char *ccache_string;
+static krb5_ccache id;
+
+static struct getargs args[] = {
+ {"ccache", 0, arg_string, &ccache_string, "credential cache", NULL },
+ {"version", 0, arg_flag, &version_flag, "print version", NULL },
+ {"help", 0, arg_flag, &help_flag, NULL, NULL }
+};
+
+static void
+usage (int ret)
+{
+ arg_printusage (args, sizeof(args)/sizeof(*args),
+ NULL, "");
+ exit (ret);
+}
+
+static krb5_context context;
+
+int
+digest_probe(struct digest_probe_options *opt,
+ int argc, char ** argv)
+{
+ krb5_error_code ret;
+ krb5_realm realm;
+ unsigned flags;
+
+ realm = opt->realm_string;
+
+ if (realm == NULL)
+ errx(1, "realm missing");
+
+ ret = krb5_digest_probe(context, realm, id, &flags);
+ if (ret)
+ krb5_err(context, 1, ret, "digest_probe");
+
+ printf("flags: %u\n", flags);
+
+ return 0;
+}
+
+int
+digest_server_init(struct digest_server_init_options *opt,
+ int argc, char ** argv)
+{
+ krb5_error_code ret;
+ krb5_digest digest;
+
+ ret = krb5_digest_alloc(context, &digest);
+ if (ret)
+ krb5_err(context, 1, ret, "digest_alloc");
+
+ ret = krb5_digest_set_type(context, digest, opt->type_string);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_digest_set_type");
+
+ if (opt->cb_type_string && opt->cb_value_string) {
+ ret = krb5_digest_set_server_cb(context, digest,
+ opt->cb_type_string,
+ opt->cb_value_string);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_digest_set_server_cb");
+ }
+ ret = krb5_digest_init_request(context,
+ digest,
+ opt->kerberos_realm_string,
+ id);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_digest_init_request");
+
+ printf("type=%s\n", opt->type_string);
+ printf("server-nonce=%s\n",
+ krb5_digest_get_server_nonce(context, digest));
+ {
+ const char *s = krb5_digest_get_identifier(context, digest);
+ if (s)
+ printf("identifier=%s\n", s);
+ }
+ printf("opaque=%s\n", krb5_digest_get_opaque(context, digest));
+
+ krb5_digest_free(digest);
+
+ return 0;
+}
+
+int
+digest_server_request(struct digest_server_request_options *opt,
+ int argc, char **argv)
+{
+ krb5_error_code ret;
+ krb5_digest digest;
+ const char *status, *rsp;
+ krb5_data session_key;
+
+ if (opt->server_nonce_string == NULL)
+ errx(1, "server nonce missing");
+ if (opt->type_string == NULL)
+ errx(1, "type missing");
+ if (opt->opaque_string == NULL)
+ errx(1, "opaque missing");
+ if (opt->client_response_string == NULL)
+ errx(1, "client response missing");
+
+ ret = krb5_digest_alloc(context, &digest);
+ if (ret)
+ krb5_err(context, 1, ret, "digest_alloc");
+
+ if (strcasecmp(opt->type_string, "CHAP") == 0) {
+ if (opt->server_identifier_string == NULL)
+ errx(1, "server identifier missing");
+
+ ret = krb5_digest_set_identifier(context, digest,
+ opt->server_identifier_string);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_digest_set_type");
+ }
+
+ ret = krb5_digest_set_type(context, digest, opt->type_string);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_digest_set_type");
+
+ ret = krb5_digest_set_username(context, digest, opt->username_string);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_digest_set_username");
+
+ ret = krb5_digest_set_server_nonce(context, digest,
+ opt->server_nonce_string);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_digest_set_server_nonce");
+
+ if(opt->client_nonce_string) {
+ ret = krb5_digest_set_client_nonce(context, digest,
+ opt->client_nonce_string);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_digest_set_client_nonce");
+ }
+
+
+ ret = krb5_digest_set_opaque(context, digest, opt->opaque_string);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_digest_set_opaque");
+
+ ret = krb5_digest_set_responseData(context, digest,
+ opt->client_response_string);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_digest_set_responseData");
+
+ ret = krb5_digest_request(context, digest,
+ opt->kerberos_realm_string, id);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_digest_request");
+
+ status = krb5_digest_rep_get_status(context, digest) ? "ok" : "failed";
+ rsp = krb5_digest_get_rsp(context, digest);
+
+ printf("status=%s\n", status);
+ if (rsp)
+ printf("rsp=%s\n", rsp);
+ printf("tickets=no\n");
+
+ ret = krb5_digest_get_session_key(context, digest, &session_key);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_digest_get_session_key");
+
+ if (session_key.length) {
+ char *key;
+ hex_encode(session_key.data, session_key.length, &key);
+ if (key == NULL)
+ krb5_errx(context, 1, "hex_encode");
+ krb5_data_free(&session_key);
+ printf("session-key=%s\n", key);
+ free(key);
+ }
+
+ krb5_digest_free(digest);
+
+ return 0;
+}
+
+static void
+client_chap(const void *server_nonce, size_t snoncelen,
+ unsigned char server_identifier,
+ const char *password)
+{
+ EVP_MD_CTX *ctx;
+ unsigned char md[MD5_DIGEST_LENGTH];
+ char *h;
+
+ ctx = EVP_MD_CTX_create();
+ EVP_DigestInit_ex(ctx, EVP_md5(), NULL);
+
+ EVP_DigestUpdate(ctx, &server_identifier, 1);
+ EVP_DigestUpdate(ctx, password, strlen(password));
+ EVP_DigestUpdate(ctx, server_nonce, snoncelen);
+ EVP_DigestFinal_ex(ctx, md, NULL);
+
+ EVP_MD_CTX_destroy(ctx);
+
+ hex_encode(md, 16, &h);
+
+ printf("responseData=%s\n", h);
+ free(h);
+}
+
+static const unsigned char ms_chap_v2_magic1[39] = {
+ 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
+ 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
+ 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
+ 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74
+};
+static const unsigned char ms_chap_v2_magic2[41] = {
+ 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
+ 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
+ 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
+ 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
+ 0x6E
+};
+static const unsigned char ms_rfc3079_magic1[27] = {
+ 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
+ 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79
+};
+
+static void
+client_mschapv2(const void *server_nonce, size_t snoncelen,
+ const void *client_nonce, size_t cnoncelen,
+ const char *username,
+ const char *password)
+{
+ EVP_MD_CTX *hctx, *ctx;
+ unsigned char md[SHA_DIGEST_LENGTH], challenge[SHA_DIGEST_LENGTH];
+ unsigned char hmd[MD4_DIGEST_LENGTH];
+ struct ntlm_buf answer;
+ int i, len, ret;
+ char *h;
+
+ ctx = EVP_MD_CTX_create();
+ EVP_DigestInit_ex(ctx, EVP_sha1(), NULL);
+
+ EVP_DigestUpdate(ctx, client_nonce, cnoncelen);
+ EVP_DigestUpdate(ctx, server_nonce, snoncelen);
+ EVP_DigestUpdate(ctx, username, strlen(username));
+ EVP_DigestFinal_ex(ctx, md, NULL);
+
+
+ hctx = EVP_MD_CTX_create();
+ EVP_DigestInit_ex(hctx, EVP_md4(), NULL);
+ len = strlen(password);
+ for (i = 0; i < len; i++) {
+ EVP_DigestUpdate(hctx, &password[i], 1);
+ EVP_DigestUpdate(hctx, &password[len], 1);
+ }
+ EVP_DigestFinal_ex(hctx, hmd, NULL);
+
+
+ /* ChallengeResponse */
+ ret = heim_ntlm_calculate_ntlm1(hmd, sizeof(hmd), md, &answer);
+ if (ret)
+ errx(1, "heim_ntlm_calculate_ntlm1");
+
+ hex_encode(answer.data, answer.length, &h);
+ printf("responseData=%s\n", h);
+ free(h);
+
+ /* PasswordHash */
+ EVP_DigestInit_ex(hctx, EVP_md4(), NULL);
+ EVP_DigestUpdate(hctx, hmd, sizeof(hmd));
+ EVP_DigestFinal_ex(hctx, hmd, NULL);
+
+
+ /* GenerateAuthenticatorResponse */
+ EVP_DigestInit_ex(ctx, EVP_sha1(), NULL);
+ EVP_DigestUpdate(ctx, hmd, sizeof(hmd));
+ EVP_DigestUpdate(ctx, answer.data, answer.length);
+ EVP_DigestUpdate(ctx, ms_chap_v2_magic1, sizeof(ms_chap_v2_magic1));
+ EVP_DigestFinal_ex(ctx, md, NULL);
+
+ /* ChallengeHash */
+ EVP_DigestInit_ex(ctx, EVP_sha1(), NULL);
+ EVP_DigestUpdate(ctx, client_nonce, cnoncelen);
+ EVP_DigestUpdate(ctx, server_nonce, snoncelen);
+ EVP_DigestUpdate(ctx, username, strlen(username));
+ EVP_DigestFinal_ex(ctx, challenge, NULL);
+
+ EVP_DigestInit_ex(ctx, EVP_sha1(), NULL);
+ EVP_DigestUpdate(ctx, md, sizeof(md));
+ EVP_DigestUpdate(ctx, challenge, 8);
+ EVP_DigestUpdate(ctx, ms_chap_v2_magic2, sizeof(ms_chap_v2_magic2));
+ EVP_DigestFinal_ex(ctx, md, NULL);
+
+ hex_encode(md, sizeof(md), &h);
+ printf("AuthenticatorResponse=%s\n", h);
+ free(h);
+
+ /* get_master, rfc 3079 3.4 */
+ EVP_DigestInit_ex(ctx, EVP_sha1(), NULL);
+ EVP_DigestUpdate(ctx, hmd, sizeof(hmd));
+ EVP_DigestUpdate(ctx, answer.data, answer.length);
+ EVP_DigestUpdate(ctx, ms_rfc3079_magic1, sizeof(ms_rfc3079_magic1));
+ EVP_DigestFinal_ex(ctx, md, NULL);
+
+ free(answer.data);
+
+ hex_encode(md, 16, &h);
+ printf("session-key=%s\n", h);
+ free(h);
+
+ EVP_MD_CTX_destroy(hctx);
+ EVP_MD_CTX_destroy(ctx);
+}
+
+
+int
+digest_client_request(struct digest_client_request_options *opt,
+ int argc, char **argv)
+{
+ char *server_nonce, *client_nonce = NULL, server_identifier;
+ ssize_t snoncelen, cnoncelen = 0;
+
+ if (opt->server_nonce_string == NULL)
+ errx(1, "server nonce missing");
+ if (opt->password_string == NULL)
+ errx(1, "password missing");
+
+ if (opt->opaque_string == NULL)
+ errx(1, "opaque missing");
+
+ snoncelen = strlen(opt->server_nonce_string);
+ server_nonce = malloc(snoncelen);
+ if (server_nonce == NULL)
+ errx(1, "server_nonce");
+
+ snoncelen = hex_decode(opt->server_nonce_string, server_nonce, snoncelen);
+ if (snoncelen <= 0)
+ errx(1, "server nonce wrong");
+
+ if (opt->client_nonce_string) {
+ cnoncelen = strlen(opt->client_nonce_string);
+ client_nonce = malloc(cnoncelen);
+ if (client_nonce == NULL)
+ errx(1, "client_nonce");
+
+ cnoncelen = hex_decode(opt->client_nonce_string,
+ client_nonce, cnoncelen);
+ if (cnoncelen <= 0)
+ errx(1, "client nonce wrong");
+ }
+
+ if (opt->server_identifier_string) {
+ int ret;
+
+ ret = hex_decode(opt->server_identifier_string, &server_identifier, 1);
+ if (ret != 1)
+ errx(1, "server identifier wrong length");
+ }
+
+ if (strcasecmp(opt->type_string, "CHAP") == 0) {
+ if (opt->server_identifier_string == NULL)
+ errx(1, "server identifier missing");
+
+ client_chap(server_nonce, snoncelen, server_identifier,
+ opt->password_string);
+
+ } else if (strcasecmp(opt->type_string, "MS-CHAP-V2") == 0) {
+ if (opt->client_nonce_string == NULL)
+ errx(1, "client nonce missing");
+ if (opt->username_string == NULL)
+ errx(1, "client nonce missing");
+
+ client_mschapv2(server_nonce, snoncelen,
+ client_nonce, cnoncelen,
+ opt->username_string,
+ opt->password_string);
+ }
+ if (client_nonce)
+ free(client_nonce);
+ free(server_nonce);
+
+ return 0;
+}
+
+#include <heimntlm.h>
+
+int
+ntlm_server_init(struct ntlm_server_init_options *opt,
+ int argc, char ** argv)
+{
+ krb5_error_code ret;
+ krb5_ntlm ntlm;
+ struct ntlm_type2 type2;
+ krb5_data challenge, opaque;
+ struct ntlm_buf data;
+ char *s;
+ static char zero2[] = "\x00\x00";
+
+ memset(&type2, 0, sizeof(type2));
+
+ ret = krb5_ntlm_alloc(context, &ntlm);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_ntlm_alloc");
+
+ ret = krb5_ntlm_init_request(context,
+ ntlm,
+ opt->kerberos_realm_string,
+ id,
+ NTLM_NEG_UNICODE|NTLM_NEG_NTLM,
+ "NUTCRACKER",
+ "L");
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_ntlm_init_request");
+
+ /*
+ *
+ */
+
+ ret = krb5_ntlm_init_get_challenge(context, ntlm, &challenge);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_ntlm_init_get_challenge");
+
+ if (challenge.length != sizeof(type2.challenge))
+ krb5_errx(context, 1, "ntlm challenge have wrong length");
+ memcpy(type2.challenge, challenge.data, sizeof(type2.challenge));
+ krb5_data_free(&challenge);
+
+ ret = krb5_ntlm_init_get_flags(context, ntlm, &type2.flags);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_ntlm_init_get_flags");
+
+ krb5_ntlm_init_get_targetname(context, ntlm, &type2.targetname);
+ type2.targetinfo.data = zero2;
+ type2.targetinfo.length = 2;
+
+ ret = heim_ntlm_encode_type2(&type2, &data);
+ if (ret)
+ krb5_errx(context, 1, "heim_ntlm_encode_type2");
+
+ free(type2.targetname);
+
+ /*
+ *
+ */
+
+ rk_base64_encode(data.data, data.length, &s);
+ free(data.data);
+ printf("type2=%s\n", s);
+ free(s);
+
+ /*
+ *
+ */
+
+ ret = krb5_ntlm_init_get_opaque(context, ntlm, &opaque);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_ntlm_init_get_opaque");
+
+ rk_base64_encode(opaque.data, opaque.length, &s);
+ krb5_data_free(&opaque);
+ printf("opaque=%s\n", s);
+ free(s);
+
+ /*
+ *
+ */
+
+ krb5_ntlm_free(context, ntlm);
+
+ return 0;
+}
+
+
+/*
+ *
+ */
+
+int
+help(void *opt, int argc, char **argv)
+{
+ sl_slc_help(commands, argc, argv);
+ return 0;
+}
+
+int
+main(int argc, char **argv)
+{
+ krb5_error_code ret;
+ int optidx = 0;
+
+ setprogname(argv[0]);
+
+ ret = krb5_init_context (&context);
+ if (ret == KRB5_CONFIG_BADFORMAT)
+ errx (1, "krb5_init_context failed to parse configuration file");
+ else if (ret)
+ errx(1, "krb5_init_context failed: %d", ret);
+
+ if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
+ usage(1);
+
+ if (help_flag)
+ usage (0);
+
+ if(version_flag){
+ print_version(NULL);
+ exit(0);
+ }
+
+ argc -= optidx;
+ argv += optidx;
+
+ if (argc == 0) {
+ help(NULL, argc, argv);
+ return 1;
+ }
+
+ if (ccache_string) {
+ ret = krb5_cc_resolve(context, ccache_string, &id);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_resolve");
+ }
+
+ ret = sl_command (commands, argc, argv);
+ if (ret == -1) {
+ help(NULL, argc, argv);
+ return 1;
+ }
+ return ret;
+}
diff --git a/third_party/heimdal/kuser/kgetcred-version.rc b/third_party/heimdal/kuser/kgetcred-version.rc
new file mode 100644
index 0000000..cd30649
--- /dev/null
+++ b/third_party/heimdal/kuser/kgetcred-version.rc
@@ -0,0 +1,36 @@
+/***********************************************************************
+ * Copyright (c) 2010, 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
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER 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.
+ *
+ **********************************************************************/
+
+#define RC_FILE_TYPE VFT_APP
+#define RC_FILE_DESC_0409 "Get Kerberos Ticket For Service"
+#define RC_FILE_ORIG_0409 "kgetcred.exe"
+
+#include "../windows/version.rc"
diff --git a/third_party/heimdal/kuser/kgetcred.1 b/third_party/heimdal/kuser/kgetcred.1
new file mode 100644
index 0000000..f6c8461
--- /dev/null
+++ b/third_party/heimdal/kuser/kgetcred.1
@@ -0,0 +1,188 @@
+.\" Copyright (c) 1999, 2001 - 2002 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 March 12, 2004
+.Dt KGETCRED 1
+.Os HEIMDAL
+.Sh NAME
+.Nm kgetcred
+.Nd "get a ticket for a particular service"
+.Sh SYNOPSIS
+.Nm
+.Op Fl Fl canonicalize
+.Op Fl Fl canonical
+.Oo Fl c cache \*(Ba Xo
+.Fl Fl cache= Ns Ar cache
+.Xc
+.Oc
+.Oo Fl e Ar enctype \*(Ba Xo
+.Fl Fl enctype= Ns Ar enctype
+.Xc
+.Oc
+.Op Fl Fl debug
+.Oo Fl H \*(Ba Xo
+.Fl Fl hostbased
+.Xc
+.Oc
+.Op Fl Fl name-type= Ns Ar name-type
+.Op Fl Fl no-transit-check
+.Op Fl Fl no-store
+.Op Fl Fl cached-only
+.Op Fl n \*(Ba Fl Fl anonymous
+.Op Fl Fl version
+.Op Fl Fl help
+.Ar principal
+.Nm
+.Op options
+.Fl Fl hostbased
+.Ar principal
+.Nm
+.Op options
+.Fl Fl hostbased
+.Ar service
+.Ar hostname
+.Ar [extra-components]
+.Sh DESCRIPTION
+.Nm
+obtains a ticket for the given service principal.
+Usually tickets for services are obtained automatically when needed
+but sometimes for some odd reason you want to obtain a particular
+ticket or of a special type.
+.Pp
+If
+.Fl Fl hostbased
+is given then the given service principal name will be canonicalized
+(see below).
+.Pp
+The third form constructs a host-based principal from the given service
+name and hostname. The service name "host" is used if the given
+.Ar service
+name in the third usage is the empty string.
+.Pp
+For host-based names, the local host's hostname is used if the given
+.Ar hostname
+is the empty string or if the
+.Ar principal
+has a single component.
+.Pp
+Any additional components will be included, even for host-based service
+principal names, but there are no defaults nor local canonicalization
+rules for additional components.
+.Pp
+Local name canonicalization rules are applied unless the
+.Fl Fl canonical
+option is given. Currently local name canonicalization rules are
+supported only for host-based principal names' hostname component.
+.Pp
+The principal's realm name may be canonicalized by following Kerberos
+referrals from the client principal's home realm if the
+.Fl Fl canonicalize
+option is given or if the local name canonicalization rules are
+configured to use referrals.
+.Pp
+Supported options:
+.Bl -tag -width Ds
+.It Fl Fl canonicalize
+requests that the KDC canonicalize the principal. Currently this only
+canonicalizes the realm by chasing referrals from the user's start
+realm, but in the future this may also enable the KDC to canonicalize
+the complete principal name.
+.It Fl Fl canonical
+turns off local canonicalization of the principal name.
+.It Fl Fl name-type= Ns Ar name-type
+the name-type to use when parsing the principal name.
+.It Fl Fl hostbased
+is short for
+.Fl Fl name-type=srv_hst .
+.It Fl c Ar cache , Fl Fl cache= Ns Ar cache
+the credential cache to use.
+.It Fl Fl delegation-credential-cache= Ns Ar cache
+the credential cache to use for delegation.
+.It Fl e Ar enctype , Fl Fl enctype= Ns Ar enctype
+encryption type to use.
+.It Fl Fl no-transit-check
+requests that the KDC doesn't do transit checking.
+.It Fl Fl no-store
+do not store tickets in the ccache.
+.It Fl Fl cached-only
+do not talk the TGS, search only the ccache.
+.It Fl Fl anonymous
+obtain an anonymous service ticket.
+.It Fl Fl forwardable
+.It Fl Fl debug
+enables debug output to stderr.
+.It Fl Fl version
+.It Fl Fl help
+.El
+.Pp
+If the
+.Fl Fl canonical
+option is used, then no further canonicalization should be done locally
+by the client (for example, DNS), but if
+.Fl Fl canonicalize
+is used, then the client will ask that the KDC canonicalize the name.
+.Pp
+If the
+.Fl Fl canonicalize
+option is used with
+.Fl Fl hostbased
+a host-based name-type, and
+.Fl Fl canonical
+is not used, then the hostname will be canonicalized according to the
+name canonicalization rules in
+.Va krb5.conf .
+.Pp
+GSS-API initiator applications with host-based services will get the
+same behavior as using the
+.Fl Fl canonicalize
+.Fl Fl hostbased
+options here.
+.Sh ENVIRONMENT
+.Bl -tag -width Ds
+.It Ev KRB5CCNAME
+Specifies the default credentials cache.
+.It Ev KRB5_CONFIG
+The file name of
+.Pa krb5.conf ,
+the default being
+.Pa /etc/krb5.conf .
+.It Ev KRB5_NO_TICKET_STORE
+If this variable is present in the environment, any service tickets obtained
+are not added to the credential cache. This affects all heimdal applications
+and library clients, not just kgetcred.
+.El
+.Sh SEE ALSO
+.Xr kinit 1 ,
+.Xr klist 1 ,
+.Xr krb5.conf 5 ,
+.Xr krb5_openlog 3
diff --git a/third_party/heimdal/kuser/kgetcred.c b/third_party/heimdal/kuser/kgetcred.c
new file mode 100644
index 0000000..4982f8a
--- /dev/null
+++ b/third_party/heimdal/kuser/kgetcred.c
@@ -0,0 +1,371 @@
+/*
+ * Copyright (c) 1997 - 2008 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.
+ */
+
+#include "kuser_locl.h"
+
+static char *cache_str;
+static char *out_cache_str;
+static char *delegation_cred_str;
+static char *etype_str;
+static int transit_flag = 1;
+static int forwardable_flag;
+static int canonicalize_flag;
+static int is_hostbased_flag;
+static int is_canonical_flag;
+static char *impersonate_str;
+static char *nametype_str;
+static int store_flag = 1;
+static int cached_only_flag;
+static int anonymous_flag;
+static int debug_flag;
+static int version_flag;
+static int help_flag;
+
+struct getargs args[] = {
+ { "cache", 'c', arg_string, &cache_str,
+ NP_("credential cache to use", ""), "cache"},
+ { "out-cache", 0, arg_string, &out_cache_str,
+ NP_("credential cache to store credential in", ""), "cache"},
+ { "delegation-credential-cache",0,arg_string, &delegation_cred_str,
+ NP_("where to find the ticket use for delegation", ""), "cache"},
+ { "canonicalize", 0, arg_flag, &canonicalize_flag,
+ NP_("canonicalize the principal (chase referrals)", ""), NULL },
+ { "canonical", 0, arg_flag, &is_canonical_flag,
+ NP_("the name components are canonical", ""), NULL },
+ { "forwardable", 0, arg_flag, &forwardable_flag,
+ NP_("forwardable ticket requested", ""), NULL},
+ { "transit-check", 0, arg_negative_flag, &transit_flag, NULL, NULL },
+ { "enctype", 'e', arg_string, &etype_str,
+ NP_("encryption type to use", ""), "enctype"},
+ { "impersonate", 0, arg_string, &impersonate_str,
+ NP_("client to impersonate", ""), "principal"},
+ { "name-type", 0, arg_string, &nametype_str,
+ NP_("Kerberos name type", ""), NULL },
+ { "hostbased", 'H', arg_flag, &is_hostbased_flag,
+ NP_("indicate that the name is a host-based service name", ""), NULL },
+ { "store", 0, arg_negative_flag, &store_flag,
+ NP_("don't store the tickets obtained in the cache", ""), NULL },
+ { "cached-only", 0, arg_flag, &cached_only_flag,
+ NP_("don't talk to the KDC, just search the cache", ""), NULL },
+ { "anonymous", 'n', arg_flag, &anonymous_flag,
+ NP_("request an anonymous ticket", ""), NULL },
+ { "debug", 0, arg_flag, &debug_flag, NULL, NULL },
+ { "version", 0, arg_flag, &version_flag, NULL, NULL },
+ { "help", 0, arg_flag, &help_flag, NULL, NULL }
+};
+
+static void
+usage(int ret)
+{
+ arg_printusage(args,
+ sizeof(args)/sizeof(*args),
+ NULL,
+ "service");
+ exit (ret);
+}
+
+int
+main(int argc, char **argv)
+{
+ krb5_error_code ret;
+ krb5_context context;
+ krb5_ccache cache;
+ krb5_creds *out;
+ int optidx = 0;
+ int32_t nametype = KRB5_NT_UNKNOWN;
+ krb5_get_creds_opt opt;
+ krb5_principal server = NULL;
+ krb5_principal impersonate;
+
+ setprogname(argv[0]);
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ errx(1, "krb5_init_context failed: %d", ret);
+
+ if (getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
+ usage(1);
+
+ if (help_flag)
+ usage (0);
+
+ if (version_flag) {
+ print_version(NULL);
+ exit(0);
+ }
+
+ argc -= optidx;
+ argv += optidx;
+
+ if (debug_flag) {
+ ret = krb5_set_debug_dest(context, getprogname(), "STDERR");
+ if (ret)
+ krb5_warn(context, ret, "krb5_set_debug_dest");
+ }
+
+ if (cache_str) {
+ ret = krb5_cc_resolve(context, cache_str, &cache);
+ if (ret)
+ krb5_err(context, 1, ret, "%s", cache_str);
+ } else {
+ ret = krb5_cc_default (context, &cache);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_resolve");
+ }
+
+ ret = krb5_get_creds_opt_alloc(context, &opt);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_get_creds_opt_alloc");
+
+ if (etype_str) {
+ krb5_enctype enctype;
+
+ ret = krb5_string_to_enctype(context, etype_str, &enctype);
+ if (ret)
+ krb5_errx(context, 1, N_("unrecognized enctype: %s", ""),
+ etype_str);
+ krb5_get_creds_opt_set_enctype(context, opt, enctype);
+ }
+
+ if (impersonate_str) {
+ ret = krb5_parse_name(context, impersonate_str, &impersonate);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_parse_name %s", impersonate_str);
+ krb5_get_creds_opt_set_impersonate(context, opt, impersonate);
+ krb5_get_creds_opt_add_options(context, opt, KRB5_GC_NO_STORE);
+ krb5_free_principal(context, impersonate);
+ }
+
+ if (out_cache_str)
+ krb5_get_creds_opt_add_options(context, opt, KRB5_GC_NO_STORE);
+
+ if (forwardable_flag)
+ krb5_get_creds_opt_add_options(context, opt, KRB5_GC_FORWARDABLE);
+ if (!transit_flag)
+ krb5_get_creds_opt_add_options(context, opt, KRB5_GC_NO_TRANSIT_CHECK);
+ if (canonicalize_flag)
+ krb5_get_creds_opt_add_options(context, opt, KRB5_GC_CANONICALIZE);
+ if (!store_flag)
+ krb5_get_creds_opt_add_options(context, opt, KRB5_GC_NO_STORE);
+ if (cached_only_flag)
+ krb5_get_creds_opt_add_options(context, opt, KRB5_GC_CACHED);
+ if (anonymous_flag)
+ krb5_get_creds_opt_add_options(context, opt, KRB5_GC_ANONYMOUS);
+
+ if (delegation_cred_str) {
+ krb5_ccache id;
+ krb5_creds c, mc;
+ Ticket ticket;
+
+ krb5_cc_clear_mcred(&mc);
+ ret = krb5_cc_get_principal(context, cache, &mc.server);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_get_principal");
+
+ ret = krb5_cc_resolve(context, delegation_cred_str, &id);
+ if(ret)
+ krb5_err(context, 1, ret, "krb5_cc_resolve");
+
+ ret = krb5_cc_retrieve_cred(context, id, 0, &mc, &c);
+ if(ret)
+ krb5_err(context, 1, ret, "krb5_cc_retrieve_cred");
+
+ ret = decode_Ticket(c.ticket.data, c.ticket.length, &ticket, NULL);
+ if (ret) {
+ krb5_clear_error_message(context);
+ krb5_err(context, 1, ret, "decode_Ticket");
+ }
+ krb5_free_cred_contents(context, &c);
+
+ ret = krb5_get_creds_opt_set_ticket(context, opt, &ticket);
+ if(ret)
+ krb5_err(context, 1, ret, "krb5_get_creds_opt_set_ticket");
+ free_Ticket(&ticket);
+
+ krb5_cc_close(context, id);
+ krb5_free_principal(context, mc.server);
+
+ krb5_get_creds_opt_add_options(context, opt,
+ KRB5_GC_CONSTRAINED_DELEGATION);
+ }
+
+ if (nametype_str != NULL) {
+ ret = krb5_parse_nametype(context, nametype_str, &nametype);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_parse_nametype");
+ }
+
+ if (nametype == KRB5_NT_SRV_HST ||
+ nametype == KRB5_NT_SRV_HST_NEEDS_CANON)
+ is_hostbased_flag = 1;
+
+ if (is_hostbased_flag) {
+ const char *sname = NULL;
+ const char *hname = NULL;
+
+ if (nametype_str != NULL &&
+ nametype != KRB5_NT_SRV_HST &&
+ nametype != KRB5_NT_SRV_HST_NEEDS_CANON)
+ krb5_errx(context, 1, "--hostbased not compatible with "
+ "non-hostbased --name-type");
+
+ if (is_canonical_flag)
+ nametype = KRB5_NT_SRV_HST;
+ else
+ nametype = KRB5_NT_SRV_HST_NEEDS_CANON;
+
+ /*
+ * Host-based service names can have more than one component.
+ *
+ * RFC5179 did not, but should have, assign a Kerberos name-type
+ * corresponding to GSS_C_NT_DOMAINBASED. But it's basically a
+ * host-based service name type with one additional component.
+ *
+ * So that's how we're treating host-based service names here:
+ * two or more components.
+ */
+
+ if (argc == 0) {
+ usage(1);
+ } else if (argc == 1) {
+ krb5_principal server2;
+
+ /*
+ * In this case the one argument is a principal name, not the
+ * service name.
+ *
+ * We parse the argument as a principal name, extract the service
+ * and hostname components, use krb5_sname_to_principal(), then
+ * extract the service and hostname components from that.
+ */
+
+ ret = krb5_parse_name(context, argv[0], &server);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_parse_name %s", argv[0]);
+ sname = krb5_principal_get_comp_string(context, server, 0);
+
+ /*
+ * If a single-component principal name is given, then we'll
+ * default the hostname, as krb5_principal_get_comp_string()
+ * returns NULL in this case.
+ */
+ hname = krb5_principal_get_comp_string(context, server, 1);
+
+ ret = krb5_sname_to_principal(context, hname, sname,
+ KRB5_NT_SRV_HST, &server2);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_sname_to_principal %s %s",
+ sname, hname);
+ sname = krb5_principal_get_comp_string(context, server2, 0);
+ hname = krb5_principal_get_comp_string(context, server2, 1);
+
+ /*
+ * Modify the original with the new sname/hname. This way we
+ * retain any additional principal name components from the given
+ * principal name.
+ *
+ * The name-type is set further below.
+ */
+ ret = krb5_principal_set_comp_string(context, server, 0, sname);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_principal_set_comp_string %s", argv[0]);
+ ret = krb5_principal_set_comp_string(context, server, 1, hname);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_principal_set_comp_string %s", argv[0]);
+ krb5_free_principal(context, server2);
+ } else {
+ size_t i;
+
+ /*
+ * In this case the arguments are principal name components.
+ *
+ * The service and hostname components can be defaulted by passing
+ * empty strings.
+ */
+ sname = argv[0];
+ if (*sname == '\0')
+ sname = NULL;
+ hname = argv[1];
+ if (hname == NULL || *hname == '\0')
+ hname = NULL;
+ ret = krb5_sname_to_principal(context, hname, sname,
+ KRB5_NT_SRV_HST, &server);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_sname_to_principal");
+
+ for (i = 2; i < argc; i++) {
+ ret = krb5_principal_set_comp_string(context, server, i, argv[i]);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_principal_set_comp_string");
+ }
+ }
+ } else if (argc == 1) {
+ ret = krb5_parse_name(context, argv[0], &server);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_parse_name %s", argv[0]);
+ } else {
+ usage(1);
+ }
+
+ if (nametype != KRB5_NT_UNKNOWN)
+ server->name.name_type = (NAME_TYPE)nametype;
+
+ ret = krb5_get_creds(context, opt, cache, server, &out);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_get_creds");
+
+ if (out_cache_str) {
+ krb5_ccache id;
+
+ ret = krb5_cc_resolve(context, out_cache_str, &id);
+ if(ret)
+ krb5_err(context, 1, ret, "krb5_cc_resolve");
+
+ ret = krb5_cc_initialize(context, id, out->client);
+ if(ret)
+ krb5_err(context, 1, ret, "krb5_cc_initialize");
+
+ ret = krb5_cc_store_cred(context, id, out);
+ if(ret)
+ krb5_err(context, 1, ret, "krb5_cc_store_cred");
+ krb5_cc_close(context, id);
+ }
+
+ krb5_free_creds(context, out);
+ krb5_free_principal(context, server);
+ krb5_get_creds_opt_free(context, opt);
+ krb5_cc_close (context, cache);
+ krb5_free_context (context);
+
+ return 0;
+}
diff --git a/third_party/heimdal/kuser/kimpersonate-version.rc b/third_party/heimdal/kuser/kimpersonate-version.rc
new file mode 100644
index 0000000..8552b05
--- /dev/null
+++ b/third_party/heimdal/kuser/kimpersonate-version.rc
@@ -0,0 +1,36 @@
+/***********************************************************************
+ * Copyright (c) 2010, 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
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER 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.
+ *
+ **********************************************************************/
+
+#define RC_FILE_TYPE VFT_APP
+#define RC_FILE_DESC_0409 "Impersonate a Kerberos Principal"
+#define RC_FILE_ORIG_0409 "kimpersonate.exe"
+
+#include "../windows/version.rc"
diff --git a/third_party/heimdal/kuser/kimpersonate.8 b/third_party/heimdal/kuser/kimpersonate.8
new file mode 100644
index 0000000..ca79ee3
--- /dev/null
+++ b/third_party/heimdal/kuser/kimpersonate.8
@@ -0,0 +1,130 @@
+.\" Copyright (c) 2002 - 2007 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 September 18, 2006
+.Dt KIMPERSONATE 8
+.Os
+.Sh NAME
+.Nm kimpersonate
+.Nd impersonate a user when there exist a keyfile or KeyFile
+.Sh SYNOPSIS
+.Nm
+.Op Fl s Ar string \*(Ba Fl Fl ccache= Ns Ar string
+.Op Fl s Ar string \*(Ba Fl Fl server= Ns Ar string
+.Op Fl c Ar string \*(Ba Fl Fl client= Ns Ar string
+.Op Fl k Ar string \*(Ba Fl Fl keytab= Ns Ar string
+.Op Fl 5 | Fl Fl krb5
+.Op Fl A | Fl Fl add
+.Op Fl R | Fl Fl referral
+.Op Fl e Ar integer \*(Ba Fl Fl expire-time= Ns Ar integer
+.Op Fl a Ar string \*(Ba Fl Fl client-address= Ns Ar string
+.Op Fl t Ar string \*(Ba Fl Fl enc-type= Ns Ar string
+.Op Fl Fl session-enc-type= Ns Ar string
+.Op Fl f Ar string \*(Ba Fl Fl ticket-flags= Ns Ar string
+.Op Fl Fl verbose
+.Op Fl Fl version
+.Op Fl Fl help
+.Sh DESCRIPTION
+The
+.Nm
+program creates a "fake" ticket using the service-key of the service and
+stores it in the given (or default) ccache. This is useful for testing.
+The service key can be read from a Kerberos 5 keytab or AFS KeyFile.
+Supported options:
+.Bl -tag -width Ds
+.It Fl Fl ccache= Ns Ar string
+ccache into which to store the ticket
+.It Fl s Ar string Ns , Fl Fl server= Ns Ar string
+name of server principal
+.It Fl c Ar string Ns , Fl Fl client= Ns Ar string
+name of client principal
+.It Fl k Ar string Ns , Fl Fl keytab= Ns Ar string
+name of keytab file
+.It Fl 5 Ns , Fl Fl krb5
+create a Kerberos 5 ticket
+.It Fl A Ns , Fl Fl add
+don't re-initialize the ccache, instead add the ticket to an existing
+ccache.
+.It Fl R Ns , Fl Fl referral
+simulate a referrals-based KDC client by storing two entries, one with
+the empty realm for the service principal name.
+.It Fl e Ar integer Ns , Fl Fl expire-time= Ns Ar integer
+lifetime of ticket in seconds
+.It Fl a Ar string Ns , Fl Fl client-address= Ns Ar string
+address of client
+.It Fl t Ar string Ns , Fl Fl enc-type= Ns Ar string
+encryption type (defaults to "aes256-cts-hmac-sha1-96")
+.It Fl Fl session-enc-type= Ns Ar string
+session encryption type (defaults to enc-type or "des-cbc-crc" for afs service tickets)
+.It Fl f Ar string Ns , Fl Fl ticket-flags= Ns Ar string
+ticket flags for krb5 ticket
+.It Fl Fl verbose
+Verbose output
+.It Fl Fl version
+Print version
+.It Fl Fl help
+.El
+.Sh FILES
+Uses
+.Pa /etc/krb5.keytab,
+and
+.Pa /usr/afs/etc/KeyFile
+when available and the
+.Fl k
+option is used with an appropriate prefix.
+.Sh EXAMPLES
+.Nm
+can be used in
+.Nm samba
+root preexec option
+or for debugging.
+.Nm
+-s host/hummel.e.kth.se@E.KTH.SE -c lha@E.KTH.SE -5
+will create a Kerberos 5 ticket for lha@E.KTH.SE for the host
+hummel.e.kth.se if there exists a keytab entry for it in
+.Pa /etc/krb5.keytab .
+.Pp
+In combination with the
+.Nm ktutil
+command, this is useful for testing. For example,
+.Pp
+.Nm ktutil
+-k tkt add -p host/foo.test@TEST -V2 -e aes256-cts-hmac-sha1-96 -r
+.Pp
+.Nm
+--cache=tcc -s host/foo.test@TEST -c jdoe@TEST -k tkt --referral
+.Sh SEE ALSO
+.Xr kinit 1 ,
+.Xr klist 1
+.Sh AUTHORS
+Love Hornquist Astrand <lha@kth.se>
diff --git a/third_party/heimdal/kuser/kimpersonate.c b/third_party/heimdal/kuser/kimpersonate.c
new file mode 100644
index 0000000..35b4295
--- /dev/null
+++ b/third_party/heimdal/kuser/kimpersonate.c
@@ -0,0 +1,395 @@
+/*
+ * Copyright (c) 2000 - 2007 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.
+ */
+
+#include "kuser_locl.h"
+#include <parse_units.h>
+
+static char *client_principal_str = NULL;
+static krb5_principal client_principal;
+static char *server_principal_str = NULL;
+static krb5_principal server_principal;
+
+static char *ccache_str = NULL;
+
+static char *ticket_flags_str = NULL;
+static TicketFlags ticket_flags;
+static char *keytab_file = NULL;
+static char *enctype_string = NULL;
+static char *session_enctype_string = NULL;
+static int expiration_time = 3600;
+static struct getarg_strings client_addresses;
+static int version_flag = 0;
+static int help_flag = 0;
+static int use_krb5 = 1;
+static int add_to_ccache = 0;
+static int use_referral_realm = 0;
+
+static const char *enc_type = "aes256-cts-hmac-sha1-96";
+static const char *session_enc_type = NULL;
+
+static void
+encode_ticket(krb5_context context,
+ EncryptionKey *skey,
+ krb5_enctype etype,
+ int skvno,
+ krb5_creds *cred)
+{
+ size_t len, size;
+ char *buf;
+ krb5_error_code ret;
+ krb5_crypto crypto;
+ EncryptedData enc_part;
+ EncTicketPart et;
+ Ticket ticket;
+
+ memset(&enc_part, 0, sizeof(enc_part));
+ memset(&ticket, 0, sizeof(ticket));
+
+ /*
+ * Set up `enc_part'
+ */
+
+ et.flags = cred->flags.b;
+ et.key = cred->session;
+ et.crealm = cred->client->realm;
+ et.cname = cred->client->name;
+ {
+ krb5_data empty_string;
+
+ krb5_data_zero(&empty_string);
+ et.transited.tr_type = domain_X500_Compress;
+ et.transited.contents = empty_string;
+ }
+ et.authtime = cred->times.authtime;
+ et.starttime = NULL;
+ et.endtime = cred->times.endtime;
+ et.renew_till = NULL;
+ et.caddr = &cred->addresses;
+ et.authorization_data = NULL; /* XXX allow random authorization_data */
+
+ /*
+ * Encrypt `enc_part' of ticket with service key
+ */
+
+ ASN1_MALLOC_ENCODE(EncTicketPart, buf, len, &et, &size, ret);
+ if (ret)
+ krb5_err(context, 1, ret, "EncTicketPart");
+
+ ret = krb5_crypto_init(context, skey, etype, &crypto);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_crypto_init");
+ ret = krb5_encrypt_EncryptedData(context,
+ crypto,
+ KRB5_KU_TICKET,
+ buf,
+ len,
+ skvno,
+ &ticket.enc_part);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_encrypt_EncryptedData");
+
+ free(buf);
+ krb5_crypto_destroy(context, crypto);
+
+ /*
+ * Encode ticket
+ */
+
+ ticket.tkt_vno = 5;
+ ticket.realm = cred->server->realm;
+ ticket.sname = cred->server->name;
+ ASN1_MALLOC_ENCODE(Ticket, cred->ticket.data, cred->ticket.length, &ticket, &size, ret);
+ free_EncryptedData(&ticket.enc_part);
+ if(ret)
+ krb5_err(context, 1, ret, "encode_Ticket");
+}
+
+/*
+ *
+ */
+
+static int
+create_krb5_tickets(krb5_context context, krb5_keytab kt)
+{
+ krb5_error_code ret;
+ krb5_keytab_entry entry;
+ krb5_creds cred;
+ krb5_enctype etype;
+ krb5_enctype session_etype;
+ krb5_ccache ccache;
+
+ memset(&cred, 0, sizeof(cred));
+
+ ret = krb5_string_to_enctype(context, enc_type, &etype);
+ if (ret)
+ krb5_err (context, 1, ret, "krb5_string_to_enctype (enc-type)");
+ ret = krb5_string_to_enctype(context, session_enc_type, &session_etype);
+ if (ret)
+ krb5_err (context, 1, ret, "krb5_string_to_enctype (session-enc-type)");
+ ret = krb5_kt_get_entry(context, kt, server_principal, 0, etype, &entry);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_kt_get_entry (perhaps use different --enc-type)");
+
+ /*
+ * setup cred
+ */
+
+
+ ret = krb5_copy_principal(context, client_principal, &cred.client);
+ if (ret == 0)
+ ret = krb5_copy_principal(context, server_principal, &cred.server);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_copy_principal");
+ ret = krb5_generate_random_keyblock(context, session_etype, &cred.session);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_generate_random_keyblock");
+
+ cred.times.authtime = time(NULL);
+ cred.times.starttime = time(NULL);
+ cred.times.endtime = time(NULL) + expiration_time;
+ cred.times.renew_till = 0;
+ krb5_data_zero(&cred.second_ticket);
+
+ ret = krb5_get_all_client_addrs(context, &cred.addresses);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_get_all_client_addrs");
+ cred.flags.b = ticket_flags;
+
+
+ /*
+ * Encode encrypted part of ticket
+ */
+
+ encode_ticket(context, &entry.keyblock, etype, entry.vno, &cred);
+ krb5_kt_free_entry(context, &entry);
+
+ /*
+ * Write to cc
+ */
+
+ if (ccache_str) {
+ ret = krb5_cc_resolve(context, ccache_str, &ccache);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_resolve");
+ } else {
+ ret = krb5_cc_default(context, &ccache);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_default");
+ }
+
+ if (add_to_ccache) {
+ krb5_principal def_princ = NULL;
+
+ /*
+ * Force fcache to read the ccache header, otherwise the store
+ * will fail.
+ */
+ ret = krb5_cc_get_principal(context, ccache, &def_princ);
+ if (ret) {
+ krb5_warn(context, ret,
+ "Given ccache appears not to exist; initializing it");
+ ret = krb5_cc_initialize(context, ccache, cred.client);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_initialize");
+ }
+ krb5_free_principal(context, def_princ);
+ } else {
+ ret = krb5_cc_initialize(context, ccache, cred.client);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_initialize");
+ }
+
+ if (use_referral_realm &&
+ strcmp(krb5_principal_get_realm(context, cred.server), "") != 0) {
+ krb5_free_principal(context, cred.server);
+ ret = krb5_copy_principal(context, server_principal, &cred.server);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_copy_principal");
+ ret = krb5_principal_set_realm(context, cred.server, "");
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_principal_set_realm");
+ ret = krb5_cc_store_cred(context, ccache, &cred);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_store_cred");
+
+ krb5_free_principal(context, cred.server);
+ ret = krb5_copy_principal(context, server_principal, &cred.server);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_copy_principal");
+ }
+ ret = krb5_cc_store_cred(context, ccache, &cred);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_store_cred");
+
+ krb5_free_cred_contents(context, &cred);
+ krb5_cc_close(context, ccache);
+
+ return 0;
+}
+
+/*
+ *
+ */
+
+static void
+setup_env(krb5_context context, krb5_keytab *kt)
+{
+ krb5_error_code ret;
+
+ if (keytab_file)
+ ret = krb5_kt_resolve(context, keytab_file, kt);
+ else
+ ret = krb5_kt_default(context, kt);
+ if (ret)
+ krb5_err(context, 1, ret, "resolving keytab");
+
+ if (client_principal_str == NULL)
+ krb5_errx(context, 1, "missing client principal");
+ ret = krb5_parse_name(context, client_principal_str, &client_principal);
+ if (ret)
+ krb5_err(context, 1, ret, "resolving client name");
+
+ if (server_principal_str == NULL)
+ krb5_errx(context, 1, "missing server principal");
+ ret = krb5_parse_name(context, server_principal_str, &server_principal);
+ if (ret)
+ krb5_err(context, 1, ret, "resolving server name");
+
+ /* If no session-enc-type specified on command line and this is an afs */
+ /* service ticket, change default of session_enc_type to DES. */
+ if (session_enctype_string == NULL
+ && strcmp("afs", *server_principal->name.name_string.val) == 0)
+ session_enc_type = "des-cbc-crc";
+
+ if (ticket_flags_str) {
+ int ticket_flags_int;
+
+ ticket_flags_int = parse_flags(ticket_flags_str,
+ asn1_TicketFlags_units(), 0);
+ if (ticket_flags_int <= 0) {
+ krb5_warnx(context, "bad ticket flags: `%s'", ticket_flags_str);
+ print_flags_table(asn1_TicketFlags_units(), stderr);
+ exit(1);
+ }
+ if (ticket_flags_int)
+ ticket_flags = int2TicketFlags(ticket_flags_int);
+ }
+}
+
+/*
+ *
+ */
+
+struct getargs args[] = {
+ { "ccache", 0, arg_string, &ccache_str,
+ "name of kerberos 5 credential cache", "cache-name"},
+ { "server", 's', arg_string, &server_principal_str,
+ "name of server principal", NULL },
+ { "client", 'c', arg_string, &client_principal_str,
+ "name of client principal", NULL },
+ { "keytab", 'k', arg_string, &keytab_file,
+ "name of keytab file", NULL },
+ { "krb5", '5', arg_flag, &use_krb5,
+ "create a kerberos 5 ticket", NULL },
+ { "add", 'A', arg_flag, &add_to_ccache,
+ "add to ccache without re-initializing it", NULL },
+ { "referral", 'R', arg_flag, &use_referral_realm,
+ "store an additional entry for the service with the empty realm", NULL },
+ { "expire-time", 'e', arg_integer, &expiration_time,
+ "lifetime of ticket in seconds", NULL },
+ { "client-addresses", 'a', arg_strings, &client_addresses,
+ "addresses of client", NULL },
+ { "enc-type", 't', arg_string, &enctype_string,
+ "encryption type", NULL },
+ { "session-enc-type", 0, arg_string,&session_enctype_string,
+ "encryption type", NULL },
+ { "ticket-flags", 'f', arg_string, &ticket_flags_str,
+ "ticket flags for krb5 ticket", NULL },
+ { "version", 0, arg_flag, &version_flag, "Print version",
+ NULL },
+ { "help", 0, arg_flag, &help_flag, NULL,
+ NULL }
+};
+
+static void
+usage(int ret)
+{
+ arg_printusage(args,
+ sizeof(args) / sizeof(args[0]),
+ NULL,
+ "");
+ exit(ret);
+}
+
+int
+main(int argc, char **argv)
+{
+ int optidx = 0;
+ krb5_error_code ret;
+ krb5_context context;
+ krb5_keytab kt;
+
+ setprogname(argv[0]);
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ errx(1, "krb5_init_context failed: %u", ret);
+
+ if (getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
+ usage(1);
+
+ if (help_flag)
+ usage(0);
+
+ if (version_flag) {
+ print_version(NULL);
+ return 0;
+ }
+
+ if (enctype_string)
+ enc_type = enctype_string;
+ if (session_enctype_string)
+ session_enc_type = session_enctype_string;
+ else
+ session_enc_type = enc_type;
+
+ setup_env(context, &kt);
+
+ if (use_krb5)
+ create_krb5_tickets(context, kt);
+
+ krb5_kt_close(context, kt);
+ krb5_free_context(context);
+
+ return 0;
+}
diff --git a/third_party/heimdal/kuser/kinit-version.rc b/third_party/heimdal/kuser/kinit-version.rc
new file mode 100644
index 0000000..3eb53e2
--- /dev/null
+++ b/third_party/heimdal/kuser/kinit-version.rc
@@ -0,0 +1,36 @@
+/***********************************************************************
+ * Copyright (c) 2010, 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
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER 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.
+ *
+ **********************************************************************/
+
+#define RC_FILE_TYPE VFT_APP
+#define RC_FILE_DESC_0409 "Acquire Initial Kerberos Tickets"
+#define RC_FILE_ORIG_0409 "kinit.exe"
+
+#include "../windows/version.rc"
diff --git a/third_party/heimdal/kuser/kinit.1 b/third_party/heimdal/kuser/kinit.1
new file mode 100644
index 0000000..f374a7c
--- /dev/null
+++ b/third_party/heimdal/kuser/kinit.1
@@ -0,0 +1,463 @@
+.\" Copyright (c) 1998 - 2003, 2006 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 April 25, 2006
+.Dt KINIT 1
+.Os HEIMDAL
+.Sh NAME
+.Nm kinit
+.Nd acquire initial tickets
+.Sh SYNOPSIS
+.Nm kinit
+.Op Fl Fl no-change-default
+.Op Fl Fl default-for-principal
+.Op Fl Fl afslog
+.Oo Fl c Ar cachename \*(Ba Xo
+.Fl Fl cache= Ns Ar cachename
+.Xc
+.Oc
+.Op Fl f | Fl Fl forwardable
+.Op Fl F | Fl Fl no-forwardable
+.Oo Fl t Ar keytabname \*(Ba Xo
+.Fl Fl keytab= Ns Ar keytabname
+.Xc
+.Oc
+.Oo Fl l Ar time \*(Ba Xo
+.Fl Fl lifetime= Ns Ar time
+.Xc
+.Oc
+.Op Fl p | Fl Fl proxiable
+.Op Fl R | Fl Fl renew
+.Op Fl Fl renewable
+.Oo Fl r Ar time \*(Ba Xo
+.Fl Fl renewable-life= Ns Ar time
+.Xc
+.Oc
+.Oo Fl S Ar principal \*(Ba Xo
+.Fl Fl server= Ns Ar principal
+.Xc
+.Oc
+.Oo Fl s Ar time \*(Ba Xo
+.Fl Fl start-time= Ns Ar time
+.Xc
+.Oc
+.Op Fl k | Fl Fl use-keytab
+.Op Fl v | Fl Fl validate
+.Oo Fl e Ar enctypes \*(Ba Xo
+.Fl Fl enctypes= Ns Ar enctypes
+.Xc
+.Oc
+.Oo Fl a Ar addresses \*(Ba Xo
+.Fl Fl extra-addresses= Ns Ar addresses
+.Xc
+.Oc
+.Op Fl Fl password-file= Ns Ar filename
+.Op Fl Fl fcache-version= Ns Ar version-number
+.Op Fl A | Fl Fl no-addresses
+.Op Fl n | Fl Fl anonymous
+.Op Fl Fl enterprise
+.Op Fl Fl version
+.Op Fl Fl help
+.Op Ar principal Op Ar command
+.Sh DESCRIPTION
+.Nm
+is used to authenticate to the Kerberos server as
+.Ar principal ,
+or if none is given, a system generated default (typically your login
+name at the default realm), and acquire a ticket granting ticket that
+can later be used to obtain tickets for other services.
+.Pp
+Supported options:
+.Bl -tag -width Ds
+.It Fl c Ar cachename | Fl Fl cache= Ns Ar cachename
+The credentials cache to put the acquired ticket in, if other than
+default.
+.It Fl Fl no-change-default
+By default the principal's credentials will be stored in the default
+credential cache. This option will cause them to instead be stored
+only in a cache whose name is derived from the principal's name. Note
+that
+.Xr klist 1
+with the
+.Fl l
+option will list all the credential caches the user has, along with
+the name of the principal whose credentials are stored therein. This
+option is ignored if the
+.Fl c Ar cachename | Fl Fl cache= Ns Ar cachename
+option is given.
+See also
+.Xr kswitch 1 .
+.It Fl Fl default-for-principal
+If this option is given and
+.Fl c Ar cachename | Fl Fl cache= Ns Ar cachename
+is not given, then the cache that will be used will be one that
+is appropriate for the client principal. For example, if the
+default cache type is
+.Ar FILE
+then the default cache may be either
+.Ar FILE:/tmp/krb5cc_%{uid}+%{principal_name}
+or
+.Ar FILE:/tmp/krb5cc_%{uid}
+if the principal is the default principal for the user, meaning
+that it is of the form
+.Ar ${USER}@${user_realm}
+or
+.Ar ${USER}@${default_realm} .
+This option implies
+.Fl Fl no-change-default
+unless
+.Fl Fl change-default
+is given. Caches for the user can be listed with the
+.Fl l
+option to
+.Xr klist 1 .
+.It Fl f Fl Fl forwardable
+Obtain a ticket than can be forwarded to another host.
+.It Fl F Fl Fl no-forwardable
+Do not obtain a forwardable ticket.
+.It Fl t Ar keytabname , Fl Fl keytab= Ns Ar keytabname
+Don't ask for a password, but instead get the key from the specified
+keytab.
+.It Fl l Ar time , Fl Fl lifetime= Ns Ar time
+Specifies the lifetime of the ticket.
+The argument can either be in seconds, or a more human readable string
+like
+.Sq 1h .
+.It Fl p , Fl Fl proxiable
+Request tickets with the proxiable flag set.
+.It Fl R , Fl Fl renew
+Try to renew a ticket.
+The ticket must have the
+.Sq renewable
+flag set, and must not be expired. If the
+.Oo Fl S Ar principal Oc
+option is specified, the ticket for the indicated service is renewed.
+If no service is explicitly specified, an attempt is made to renew the
+TGT for the client realm. If no TGT for the client realm is found in the
+credential cache, an attempt is made to renew the TGT for the defaualt
+realm (if that is found in the credential cache), or else the first
+TGT found. This makes it easier for users to renew forwarded tickets
+that are not issued by the origin realm.
+.It Fl Fl renewable
+The same as
+.Fl Fl renewable-life ,
+with an infinite time.
+.It Fl r Ar time , Fl Fl renewable-life= Ns Ar time
+The max renewable ticket life.
+.It Fl S Ar principal , Fl Fl server= Ns Ar principal
+Get a ticket for a service other than krbtgt/LOCAL.REALM.
+.It Fl s Ar time , Fl Fl start-time= Ns Ar time
+Obtain a ticket that starts to be valid
+.Ar time
+(which can really be a generic time specification, like
+.Sq 1h )
+seconds into the future.
+.It Fl k , Fl Fl use-keytab
+The same as
+.Fl Fl keytab ,
+but with the default keytab name (normally
+.Ar FILE:/etc/krb5.keytab ) .
+.It Fl v , Fl Fl validate
+Try to validate an invalid ticket.
+.It Fl e , Fl Fl enctypes= Ns Ar enctypes
+Request tickets with this particular enctype.
+.It Fl Fl password-file= Ns Ar filename
+read the password from the first line of
+.Ar filename .
+If the
+.Ar filename
+is
+.Ar STDIN ,
+the password will be read from the standard input.
+.It Fl Fl fcache-version= Ns Ar version-number
+Create a credentials cache of version
+.Ar version-number .
+.It Fl a , Fl Fl extra-addresses= Ns Ar enctypes
+Adds a set of addresses that will, in addition to the systems local
+addresses, be put in the ticket.
+This can be useful if all addresses a client can use can't be
+automatically figured out.
+One such example is if the client is behind a firewall.
+Also settable via
+.Li libdefaults/extra_addresses
+in
+.Xr krb5.conf 5 .
+.It Fl A , Fl Fl no-addresses
+Request a ticket with no addresses.
+.It Fl n , Fl Fl anonymous
+Request an anonymous ticket.
+With the default (false) setting of the
+.Ar historical_anon_pkinit
+configuration parameter, if the principal is specified as @REALM, then
+anonymous PKINIT will be used to acquire an unauthenticated anonymous ticket
+and both the client name and (with fully RFC-comformant KDCs) realm in the
+returned ticket will be anonymized.
+Otherwise, authentication proceeds as normal and the anonymous ticket will have
+only the client name anonymized.
+With
+.Ar historical_anon_pkinit
+set to
+.Li true ,
+the principal is interpreted as a realm even without an at-sign prefix, and it
+is not possible to obtain authenticated anonymized tickets.
+.It Fl Fl enterprise
+Parse principal as a enterprise (KRB5-NT-ENTERPRISE) name. Enterprise
+names are email like principals that are stored in the name part of
+the principal, and since there are two @ characters the parser needs
+to know that the first is not a realm.
+An example of an enterprise name is
+.Dq lha@e.kth.se@KTH.SE ,
+and this option is usually used with canonicalize so that the
+principal returned from the KDC will typically be the real principal
+name.
+.It Fl Fl gss-mech
+Enable GSS-API pre-authentication using the specified mechanism OID. Unless
+.Ar gss-name
+is also set, then the specified principal name will be used as the GSS-API
+initiator name. If the principal is specified as @REALM or left unspecified,
+then the default GSS-API credential will be used.
+.It Fl Fl gss-name
+Attempt GSS-API pre-authentication using an initiator name distinct from the
+Kerberos client principal,
+.It Fl Fl afslog
+Gets AFS tickets, converts them to version 4 format, and stores them
+in the kernel.
+Only useful if you have AFS.
+.El
+.Pp
+The
+.Ar forwardable ,
+.Ar proxiable ,
+.Ar ticket_life ,
+and
+.Ar renewable_life
+options can be set to a default value from the
+.Dv appdefaults
+section in krb5.conf, see
+.Xr krb5_appdefault 3 .
+.Pp
+If a
+.Ar command
+is given,
+.Nm
+will set up new credentials caches, and AFS PAG, and then run the given
+command.
+When it finishes the credentials will be removed.
+.Sh CREDENTIALS CACHE TYPES
+Heimdal supports a number of credentials cache types:
+.Bl -tag -width Ds
+.It FILE
+Uses a file per-cache with a binary format common to other Kerberos
+implementations.
+.It DIR
+Uses a directory with multiple files, one per-cache in a collection.
+.It SCC
+Uses a SQLite3 database with multiple caches in the database.
+.It KEYRING
+Uses a Linux keyring.
+.It KCM
+Uses a inter-process communications (IPC) to talk to a daemon typically named
+.Nm kcm .
+.It API
+Uses KCM or else a shared object that implements the "CCAPI".
+.It MEMORY
+Uses in-process memory (which disappears on process exit, so this if of little
+use in this program,
+.Nm
+).
+.El
+.Sh CREDENTIALS CACHE COLLECTIONS
+Every credentials cache's name consists of its cache type (e.g.,
+FILE), a possibly-optional collection name, and a possibly
+optional "subsidiary" name naming a single cache in the
+collection.
+.Pp
+The convention in Heimdal is that a cache's subsidiary cache name
+is the name of the client principal whose credentials are
+expected to be stored and found in that cache, with the following
+characters replaced with a hyphen: slash, backslash, colon, and
+plus.
+.Pp
+The caches in a credentials cache collection can be listed by the
+.Xr klist 1
+command.
+The
+.Sq FILE
+credentials cache type supports listing of caches in the
+collection only when the
+.Ql enable_file_cache_iteration
+is set to
+.Ql yes
+in the
+.Ql [libdefaults]
+section of
+.Xr krb5.conf 5 .
+.Sh CREDENTIALS CACHE NAMES
+The general syntax for credentials cache names is
+.Dl TYPE:[collection-name][:subsidiary]
+except that for the FILE type it is
+.Dl FILE:collection-name[+subsidiary]
+and for the KEYRING type it is:
+.Dl KEYRING:[anchor:][collection[:subsidiary]]
+where the collection name is free-form and the anchor is one of
+.Sq process ,
+.Sq thread ,
+or
+.Sq legacy .
+.Pp
+The collection name is always absent for the
+.Ql MEMORY
+credentials cache type.
+.Pp
+When the collection name is absent then the default collection
+for the given credentials cache type is used, which are:
+.Bl -tag -compact
+.It Ql /tmp/krb5cc_{UID}
+for FILE caches, where {UID} is a numeric user ID
+.It Ql /tmp/krb5cc_{UID}_dir
+for DIR caches, where {UID} is a numeric user ID
+.It Ql /tmp/krb5scc_{UID}
+for SCC caches, where {UID} is a numeric user ID, and where the
+named file is a SQLite3 database file
+.It Ql {UID}
+for KCM caches, where {UID} is the user's numeric user ID
+.It <implementation-specific>
+for API (CCAPI) credentials caches
+.El
+.Pp
+The collection name is only optional for:
+.Ql DIR ,
+.Ql SCC ,
+.Ql KCM ,
+.Ql KEYRING
+and
+.Ql API
+credentials cache types.
+.Sh EXAMPLE CREDENTIALS CACHE NAMES
+.Bl -tag -width Ds
+.It Ql FILE:/tmp/cc
+this is a FILE cache in a file named
+.Ql /tmp/cc
+(the default would be
+.Ql /tmp/krb5cc_{UID} )
+.It Ql FILE:/tmp/cc+jane@TEST.H5L.SE
+.It Ql DIR:/tmp/ccdir
+this is a FILE cache named by
+.Ql /tmp/krb5cc_{UID}_dir/primary
+which will be of the form
+.Ql /tmp/ccdir/tkt.XXXXXX
+.It Ql DIR:/tmp/ccdir:jane@TEST.H5L.SE
+this is a FILE ccache named
+.Ql /tmp/ccdir/tkt.jane@TEST.H5L.SE
+.It Ql DIR::jane@TEST.H5L.SE
+this is a FILE ccache named
+.Ql /tmp/krb5cc_{UID}_dir/tkt.jane@TEST.H5L.SE
+where {UID} is the user's numeric identifier
+.It Ql SCC:
+this is the current primary cache in the SQLite3 database named
+.Ql /tmp/krb5scc_{UID}
+.It Ql SCC:/tmp/ccdb
+this is the current primary cache in the SQLite3 database named
+.Ql /tmp/ccdb
+.It Ql SCC:/tmp/ccdb:jane@TEST.H5L.SE
+this is the cache
+.Dq named jane@TEST.H5L.SE
+in the SQLite3 database
+named
+.Ql /tmp/ccdb
+.It Ql SCC::jane@TEST.H5L.SE
+this is the cache named
+.Dq jane@TEST.H5L.SE
+in the SQLite3 database named
+.Ql /tmp/krb5scc_{UID}
+.It Ql KEYRING:
+this is the primary cache in the default KEYRING collection for
+the running user
+.It Ql KEYRING:foo
+this is the primary cache in the KEYRING collection named
+.Dq foo
+.It Ql KEYRING:foo:jane@TEST.H5L.SE
+this is the cache named
+.Dq jane@TEST.H5L.SE
+in the KEYRING collection named
+.Dq foo
+.It Ql KCM:
+this is the primary cache in the default KCM collection for the
+running user
+.It Ql KCM:12345
+this is the primary cache in the default KCM collection for the
+user whose numeric identifier is 12345
+.It Ql KCM:jane@TEST.H5L.SE
+this is the cache named
+.Dq jane@TEST.H5L.SE
+in the default KCM collection for the running user
+.It Ql KCM:12345:jane@TEST.H5L.SE
+this is the cache named
+.Dq jane@TEST.H5L.SE
+in the default KCM collection for the given user
+.It Ql API:
+this is the primary cache in the default API collection for the
+running user
+.It Ql API:foo
+this is the primary cache in the API collection named
+.Dq foo
+.It Ql API:foo:jane@TEST.H5L.SE
+this is the cache named
+.Dq jane@TEST.H5L.SE
+in the KEYRING collection named
+.Dq foo
+.El
+.Sh ENVIRONMENT
+.Bl -tag -width Ds
+.It Ev KRB5CCNAME
+Specifies the default credentials cache.
+.It Ev KRB5_CONFIG
+The file name of
+.Pa krb5.conf ,
+the default being
+.Pa /etc/krb5.conf .
+.El
+.\".Sh FILES
+.\".Sh EXAMPLES
+.\".Sh DIAGNOSTICS
+.Sh SEE ALSO
+.Xr kdestroy 1 ,
+.Xr klist 1 ,
+.Xr kswitch 1 ,
+.Xr kcm 8 ,
+.Xr krb5_appdefault 3 ,
+.Xr krb5.conf 5
+.\".Sh STANDARDS
+.\".Sh HISTORY
+.\".Sh AUTHORS
+.\".Sh BUGS
diff --git a/third_party/heimdal/kuser/kinit.c b/third_party/heimdal/kuser/kinit.c
new file mode 100644
index 0000000..9a2fac6
--- /dev/null
+++ b/third_party/heimdal/kuser/kinit.c
@@ -0,0 +1,1993 @@
+/*
+ * Copyright (c) 1997-2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2009 Apple 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:
+ *
+ * 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.
+ */
+
+#include "kuser_locl.h"
+#undef HC_DEPRECATED_CRYPTO
+#include <krb5_locl.h>
+
+#ifdef HAVE_FRAMEWORK_SECURITY
+#include <Security/Security.h>
+#endif
+
+#ifndef NO_NTLM
+#include "heimntlm.h"
+#endif
+
+#ifndef SIGINFO
+#define SIGINFO SIGUSR1
+#endif
+
+int forwardable_flag = -1;
+int proxiable_flag = -1;
+int renewable_flag = -1;
+int renew_flag = 0;
+int pac_flag = -1;
+int validate_flag = 0;
+int version_flag = 0;
+int help_flag = 0;
+int addrs_flag = -1;
+struct getarg_strings extra_addresses;
+int anonymous_flag = 0;
+char *lifetime = NULL;
+char *renew_life = NULL;
+char *server_str = NULL;
+static krb5_principal tgs_service;
+char *cred_cache = NULL;
+char *start_str = NULL;
+static int default_for = 0;
+static int switch_cache_flags = -1;
+struct getarg_strings etype_str;
+int use_keytab = 0;
+char *keytab_str = NULL;
+static krb5_keytab kt = NULL;
+int do_afslog = -1;
+int fcache_version;
+char *password_file = NULL;
+char *pk_user_id = NULL;
+int pk_enterprise_flag = 0;
+struct hx509_certs_data *ent_user_id = NULL;
+char *pk_x509_anchors = NULL;
+int pk_use_enckey = 0;
+int pk_anon_fast_armor = -1;
+char *gss_preauth_mech = NULL;
+char *gss_preauth_name = NULL;
+char *kdc_hostname = NULL;
+static int canonicalize_flag = 0;
+static int enterprise_flag = 0;
+static int ok_as_delegate_flag = 0;
+static char *fast_armor_cache_string = NULL;
+static int use_referrals_flag = 0;
+static int windows_flag = 0;
+#ifndef NO_NTLM
+static char *ntlm_domain;
+#endif
+
+
+static struct getargs args[] = {
+ /*
+ * used by MIT
+ * a: ~A
+ * V: verbose
+ * F: ~f
+ * P: ~p
+ * C: v4 cache name?
+ * 5:
+ *
+ * old flags
+ * 4:
+ * 9:
+ */
+ { "afslog", 0 , arg_flag, &do_afslog,
+ NP_("obtain afs tokens", ""), NULL },
+
+ { "cache", 'c', arg_string, &cred_cache,
+ NP_("credentials cache", ""), "cachename" },
+
+ { "forwardable", 'F', arg_negative_flag, &forwardable_flag,
+ NP_("get tickets not forwardable", ""), NULL },
+
+ { NULL, 'f', arg_flag, &forwardable_flag,
+ NP_("get forwardable tickets", ""), NULL },
+
+ { "keytab", 't', arg_string, &keytab_str,
+ NP_("keytab to use", ""), "keytabname" },
+
+ { "lifetime", 'l', arg_string, &lifetime,
+ NP_("lifetime of tickets", ""), "time" },
+
+ { "proxiable", 'p', arg_flag, &proxiable_flag,
+ NP_("get proxiable tickets", ""), NULL },
+
+ { "renew", 'R', arg_flag, &renew_flag,
+ NP_("renew TGT", ""), NULL },
+
+ { "renewable", 0, arg_flag, &renewable_flag,
+ NP_("get renewable tickets", ""), NULL },
+
+ { "renewable-life", 'r', arg_string, &renew_life,
+ NP_("renewable lifetime of tickets", ""), "time" },
+
+ { "server", 'S', arg_string, &server_str,
+ NP_("server to get ticket for", ""), "principal" },
+
+ { "start-time", 's', arg_string, &start_str,
+ NP_("when ticket gets valid", ""), "time" },
+
+ { "use-keytab", 'k', arg_flag, &use_keytab,
+ NP_("get key from keytab", ""), NULL },
+
+ { "validate", 'v', arg_flag, &validate_flag,
+ NP_("validate TGT", ""), NULL },
+
+ { "enctypes", 'e', arg_strings, &etype_str,
+ NP_("encryption types to use", ""), "enctypes" },
+
+ { "fcache-version", 0, arg_integer, &fcache_version,
+ NP_("file cache version to create", ""), NULL },
+
+ { "addresses", 'A', arg_negative_flag, &addrs_flag,
+ NP_("request a ticket with no addresses", ""), NULL },
+
+ { "extra-addresses",'a', arg_strings, &extra_addresses,
+ NP_("include these extra addresses", ""), "addresses" },
+
+ { "anonymous", 'n', arg_flag, &anonymous_flag,
+ NP_("request an anonymous ticket", ""), NULL },
+
+ { "request-pac", 0, arg_flag, &pac_flag,
+ NP_("request a Windows PAC", ""), NULL },
+
+ { "password-file", 0, arg_string, &password_file,
+ NP_("read the password from a file", ""), NULL },
+
+ { "canonicalize",0, arg_flag, &canonicalize_flag,
+ NP_("canonicalize client principal", ""), NULL },
+
+ { "enterprise",0, arg_flag, &enterprise_flag,
+ NP_("parse principal as a KRB5-NT-ENTERPRISE name", ""), NULL },
+#ifdef PKINIT
+ { "pk-enterprise", 0, arg_flag, &pk_enterprise_flag,
+ NP_("use enterprise name from certificate", ""), NULL },
+
+ { "pk-user", 'C', arg_string, &pk_user_id,
+ NP_("principal's public/private/certificate identifier", ""), "id" },
+
+ { "x509-anchors", 'D', arg_string, &pk_x509_anchors,
+ NP_("directory with CA certificates", ""), "directory" },
+
+ { "pk-use-enckey", 0, arg_flag, &pk_use_enckey,
+ NP_("Use RSA encrypted reply (instead of DH)", ""), NULL },
+
+ { "pk-anon-fast-armor", 0, arg_flag, &pk_anon_fast_armor,
+ NP_("use unauthenticated anonymous PKINIT as FAST armor", ""), NULL },
+#endif
+
+ { "gss-mech", 0, arg_string, &gss_preauth_mech,
+ NP_("use GSS mechanism for pre-authentication", ""), NULL },
+
+ { "gss-name", 0, arg_string, &gss_preauth_name,
+ NP_("use distinct GSS identity for pre-authentication", ""), NULL },
+
+ { "kdc-hostname", 0, arg_string, &kdc_hostname,
+ NP_("KDC host name", ""), "hostname" },
+
+#ifndef NO_NTLM
+ { "ntlm-domain", 0, arg_string, &ntlm_domain,
+ NP_("NTLM domain", ""), "domain" },
+#endif
+
+ { "change-default", 0, arg_negative_flag, &switch_cache_flags,
+ NP_("switch the default cache to the new credentials cache", ""), NULL },
+
+ { "default-for-principal", 0, arg_flag, &default_for,
+ NP_("Use a default cache appropriate for the client principal", ""), NULL },
+
+ { "ok-as-delegate", 0, arg_flag, &ok_as_delegate_flag,
+ NP_("honor ok-as-delegate on tickets", ""), NULL },
+
+ { "fast-armor-cache", 0, arg_string, &fast_armor_cache_string,
+ NP_("use this credential cache as FAST armor cache", ""), "cache" },
+
+ { "use-referrals", 0, arg_flag, &use_referrals_flag,
+ NP_("only use referrals, no dns canonicalisation", ""), NULL },
+
+ { "windows", 0, arg_flag, &windows_flag,
+ NP_("get windows behavior", ""), NULL },
+
+ { "version", 0, arg_flag, &version_flag, NULL, NULL },
+ { "help", 0, arg_flag, &help_flag, NULL, NULL }
+};
+
+static char *
+get_default_realm(krb5_context context);
+
+static void
+usage(int ret)
+{
+ arg_printusage_i18n(args, sizeof(args)/sizeof(*args), N_("Usage: ", ""),
+ NULL, "[principal [command]]", getarg_i18n);
+ exit(ret);
+}
+
+static krb5_error_code
+tgs_principal(krb5_context context,
+ krb5_ccache cache,
+ krb5_principal client,
+ krb5_const_realm tgs_realm,
+ krb5_principal *out_princ)
+{
+ krb5_error_code ret;
+ krb5_principal tgs_princ;
+ krb5_creds creds;
+ krb5_creds *tick;
+ krb5_flags options;
+
+ ret = krb5_make_principal(context, &tgs_princ, tgs_realm,
+ KRB5_TGS_NAME, tgs_realm, NULL);
+ if (ret)
+ return ret;
+
+ /*
+ * Don't fail-over to a different realm just because a TGT expired
+ */
+ options = KRB5_GC_CACHED | KRB5_GC_EXPIRED_OK;
+
+ memset(&creds, 0, sizeof(creds));
+ creds.client = client;
+ creds.server = tgs_princ;
+ ret = krb5_get_credentials(context, options, cache, &creds, &tick);
+ if (ret == 0) {
+ krb5_free_creds(context, tick);
+ *out_princ = tgs_princ;
+ } else {
+ krb5_free_principal(context, tgs_princ);
+ }
+
+ return ret;
+}
+
+
+/*
+ * Try TGS specified with '-S',
+ * then TGS of client realm,
+ * then if fallback is FALSE: fail,
+ * otherwise try TGS of default realm,
+ * and finally first TGT in ccache.
+ */
+static krb5_error_code
+get_server(krb5_context context,
+ krb5_ccache cache,
+ krb5_principal client,
+ const char *server,
+ krb5_boolean fallback,
+ krb5_principal *princ)
+{
+ krb5_error_code ret = 0;
+ krb5_const_realm realm;
+ krb5_realm def_realm;
+ krb5_cc_cursor cursor;
+ krb5_creds creds;
+ const char *pcomp;
+
+ if (tgs_service)
+ goto done;
+
+ if (server) {
+ ret = krb5_parse_name(context, server, &tgs_service);
+ goto done;
+ }
+
+ /* Try the client realm first */
+ realm = krb5_principal_get_realm(context, client);
+ ret = tgs_principal(context, cache, client, realm, &tgs_service);
+ if (ret == 0 || ret != KRB5_CC_NOTFOUND)
+ goto done;
+
+ if (!fallback)
+ return ret;
+
+ /* Next try the default realm */
+ ret = krb5_get_default_realm(context, &def_realm);
+ if (ret)
+ return ret;
+ ret = tgs_principal(context, cache, client, def_realm, &tgs_service);
+ free(def_realm);
+ if (ret == 0 || ret != KRB5_CC_NOTFOUND)
+ goto done;
+
+ /* Finally try the first TGT with instance == realm in the cache */
+ ret = krb5_cc_start_seq_get(context, cache, &cursor);
+ if (ret)
+ return ret;
+
+ for (/**/; ret == 0; krb5_free_cred_contents (context, &creds)) {
+
+ ret = krb5_cc_next_cred(context, cache, &cursor, &creds);
+ if (ret)
+ break;
+ if (creds.server->name.name_string.len != 2)
+ continue;
+ pcomp = krb5_principal_get_comp_string(context, creds.server, 0);
+ if (strcmp(pcomp, KRB5_TGS_NAME) != 0)
+ continue;
+ realm = krb5_principal_get_realm(context, creds.server);
+ pcomp = krb5_principal_get_comp_string(context, creds.server, 1);
+ if (strcmp(realm, pcomp) != 0)
+ continue;
+ ret = krb5_copy_principal(context, creds.server, &tgs_service);
+ break;
+ }
+ if (ret == KRB5_CC_END) {
+ ret = KRB5_CC_NOTFOUND;
+ krb5_set_error_message(context, ret,
+ N_("Credential cache contains no TGTs", ""));
+ }
+ krb5_cc_end_seq_get(context, cache, &cursor);
+
+done:
+ if (!ret)
+ ret = krb5_copy_principal(context, tgs_service, princ);
+ return ret;
+}
+
+static krb5_error_code
+copy_configs(krb5_context context,
+ krb5_ccache dst,
+ krb5_ccache src,
+ krb5_principal start_ticket_server)
+{
+ krb5_error_code ret;
+ const char *cfg_names[] = {"realm-config", "FriendlyName", "anon_pkinit_realm", NULL};
+ const char *cfg_names_w_pname[] = {"fast_avail", NULL};
+ krb5_data cfg_data;
+ size_t i;
+
+ for (i = 0; cfg_names[i]; i++) {
+ ret = krb5_cc_get_config(context, src, NULL, cfg_names[i], &cfg_data);
+ if (ret == KRB5_CC_NOTFOUND || ret == KRB5_CC_END) {
+ continue;
+ } else if (ret) {
+ krb5_warn(context, ret, "krb5_cc_get_config");
+ return ret;
+ }
+ ret = krb5_cc_set_config(context, dst, NULL, cfg_names[i], &cfg_data);
+ if (ret)
+ krb5_warn(context, ret, "krb5_cc_set_config");
+ }
+ for (i = 0; start_ticket_server && cfg_names_w_pname[i]; i++) {
+ ret = krb5_cc_get_config(context, src, start_ticket_server,
+ cfg_names_w_pname[i], &cfg_data);
+ if (ret == KRB5_CC_NOTFOUND || ret == KRB5_CC_END) {
+ continue;
+ } else if (ret) {
+ krb5_warn(context, ret, "krb5_cc_get_config");
+ return ret;
+ }
+ ret = krb5_cc_set_config(context, dst, start_ticket_server,
+ cfg_names_w_pname[i], &cfg_data);
+ if (ret && ret != KRB5_CC_NOTFOUND)
+ krb5_warn(context, ret, "krb5_cc_set_config");
+ }
+ /*
+ * We don't copy cc configs for any other principals though (mostly
+ * those are per-target time offsets and the like, so it's bad to
+ * lose them, but hardly the end of the world, and as they may not
+ * expire anyways, it's good to let them go).
+ */
+ return 0;
+}
+
+static krb5_error_code
+get_anon_pkinit_tgs_name(krb5_context context,
+ krb5_ccache ccache,
+ krb5_principal *tgs_name)
+{
+ krb5_error_code ret;
+ krb5_data data;
+ char *realm;
+
+ ret = krb5_cc_get_config(context, ccache, NULL, "anon_pkinit_realm", &data);
+ if (ret == 0)
+ realm = strndup(data.data, data.length);
+ else
+ realm = get_default_realm(context);
+
+ krb5_data_free(&data);
+
+ if (realm == NULL)
+ return krb5_enomem(context);
+
+ ret = krb5_make_principal(context, tgs_name, realm,
+ KRB5_TGS_NAME, realm, NULL);
+
+ free(realm);
+
+ return ret;
+}
+
+static krb5_error_code
+renew_validate(krb5_context context,
+ int renew,
+ int validate,
+ krb5_ccache *cachep,
+ krb5_const_principal principal,
+ krb5_boolean cache_is_default_for,
+ const char *server,
+ krb5_deltat life)
+{
+ krb5_error_code ret;
+ krb5_ccache tempccache = NULL;
+ krb5_ccache cache = *cachep;
+ krb5_creds in, *out = NULL;
+ krb5_kdc_flags flags;
+
+ memset(&in, 0, sizeof(in));
+
+ ret = krb5_cc_get_principal(context, cache, &in.client);
+ if (ret && cache_is_default_for && principal) {
+ krb5_error_code ret2;
+ krb5_ccache def_ccache = NULL;
+
+ ret2 = krb5_cc_default(context, &def_ccache);
+ if (ret2 == 0)
+ ret2 = krb5_cc_get_principal(context, def_ccache, &in.client);
+ if (ret2 == 0 &&
+ krb5_principal_compare(context, principal, in.client)) {
+ krb5_cc_close(context, *cachep);
+ cache = *cachep = def_ccache;
+ def_ccache = NULL;
+ ret = 0;
+ }
+ krb5_cc_close(context, def_ccache);
+ }
+ if (ret) {
+ krb5_warn(context, ret, "krb5_cc_get_principal");
+ return ret;
+ }
+
+ if (principal && !krb5_principal_compare(context, principal, in.client)) {
+ char *ccname = NULL;
+
+ (void) krb5_cc_get_full_name(context, cache, &ccname);
+ krb5_errx(context, 1, "Credentials in cache %s do not match requested "
+ "principal", ccname ? ccname : "requested");
+ free(ccname);
+ }
+
+ if (server == NULL &&
+ krb5_principal_is_anonymous(context, in.client,
+ KRB5_ANON_MATCH_UNAUTHENTICATED))
+ ret = get_anon_pkinit_tgs_name(context, cache, &in.server);
+ else
+ ret = get_server(context, cache, in.client, server, TRUE, &in.server);
+ if (ret) {
+ krb5_warn(context, ret, "get_server");
+ goto out;
+ }
+
+ if (renew) {
+ /*
+ * no need to check the error here, it's only to be
+ * friendly to the user
+ */
+ (void) krb5_get_credentials(context, KRB5_GC_CACHED, cache, &in, &out);
+ }
+
+ flags.i = 0;
+ flags.b.renewable = flags.b.renew = renew;
+ flags.b.validate = validate;
+
+ if (forwardable_flag != -1)
+ flags.b.forwardable = forwardable_flag;
+ else if (out)
+ flags.b.forwardable = out->flags.b.forwardable;
+
+ if (proxiable_flag != -1)
+ flags.b.proxiable = proxiable_flag;
+ else if (out)
+ flags.b.proxiable = out->flags.b.proxiable;
+
+ if (anonymous_flag)
+ flags.b.request_anonymous = anonymous_flag;
+ if (life)
+ in.times.endtime = time(NULL) + life;
+
+ if (out) {
+ krb5_free_creds(context, out);
+ out = NULL;
+ }
+
+
+ ret = krb5_get_kdc_cred(context,
+ cache,
+ flags,
+ NULL,
+ NULL,
+ &in,
+ &out);
+ if (ret) {
+ krb5_warn(context, ret, "krb5_get_kdc_cred");
+ goto out;
+ }
+
+ ret = krb5_cc_new_unique(context, krb5_cc_get_type(context, cache),
+ NULL, &tempccache);
+ if (ret) {
+ krb5_warn(context, ret, "krb5_cc_new_unique");
+ goto out;
+ }
+
+ ret = krb5_cc_initialize(context, tempccache, in.client);
+ if (ret) {
+ krb5_warn(context, ret, "krb5_cc_initialize");
+ goto out;
+ }
+
+ ret = krb5_cc_store_cred(context, tempccache, out);
+ if (ret) {
+ krb5_warn(context, ret, "krb5_cc_store_cred");
+ goto out;
+ }
+
+ /*
+ * We want to preserve cc configs as some are security-relevant, and
+ * anyways it's the friendly thing to do.
+ */
+ ret = copy_configs(context, tempccache, cache, out->server);
+ if (ret)
+ goto out;
+
+ ret = krb5_cc_move(context, tempccache, cache);
+ if (ret) {
+ krb5_warn(context, ret, "krb5_cc_move");
+ goto out;
+ }
+ tempccache = NULL;
+
+out:
+ if (tempccache)
+ krb5_cc_destroy(context, tempccache);
+ if (out)
+ krb5_free_creds(context, out);
+ krb5_free_cred_contents(context, &in);
+ return ret;
+}
+
+static krb5_error_code
+make_wellknown_name(krb5_context context,
+ krb5_const_realm realm,
+ const char *instance,
+ krb5_principal *principal)
+{
+ krb5_error_code ret;
+
+ ret = krb5_make_principal(context, principal, realm,
+ KRB5_WELLKNOWN_NAME, instance, NULL);
+ if (ret == 0)
+ krb5_principal_set_type(context, *principal, KRB5_NT_WELLKNOWN);
+
+ return ret;
+}
+
+static krb5_error_code
+acquire_gss_cred(krb5_context context,
+ krb5_const_principal client,
+ krb5_deltat life,
+ const char *passwd,
+ gss_cred_id_t *cred,
+ gss_OID *mech)
+{
+ krb5_error_code ret;
+ OM_uint32 major, minor;
+ gss_name_t name = GSS_C_NO_NAME;
+ gss_key_value_element_desc cred_element;
+ gss_key_value_set_desc cred_store;
+ gss_OID_set_desc mechs;
+
+ *cred = GSS_C_NO_CREDENTIAL;
+ *mech = GSS_C_NO_OID;
+
+ if (gss_preauth_mech) {
+ *mech = gss_name_to_oid(gss_preauth_mech);
+ if (*mech == GSS_C_NO_OID)
+ return EINVAL;
+ }
+
+ if (gss_preauth_name) {
+ gss_buffer_desc buf;
+
+ buf.value = gss_preauth_name;
+ buf.length = strlen(gss_preauth_name);
+
+ major = gss_import_name(&minor, &buf, GSS_C_NT_USER_NAME, &name);
+ ret = _krb5_gss_map_error(major, minor);
+ } else if (!krb5_principal_is_federated(context, client)) {
+ ret = _krb5_gss_pa_unparse_name(context, client, &name);
+ } else {
+ /*
+ * WELLKNOWN/FEDERATED is used a placeholder where the user
+ * did not specify either a Kerberos credential or a GSS-API
+ * initiator name. It avoids the expense of acquiring a default
+ * credential purely to interrogate the credential name.
+ */
+ name = GSS_C_NO_NAME;
+ ret = 0;
+ }
+ if (ret)
+ goto out;
+
+ cred_store.count = 1;
+ cred_store.elements = &cred_element;
+
+ if (passwd && passwd[0]) {
+ cred_element.key = "password";
+ cred_element.value = passwd;
+ } else if (keytab_str) {
+ cred_element.key = "client_keytab",
+ cred_element.value = keytab_str;
+ } else {
+ cred_store.count = 0;
+ }
+
+ if (*mech) {
+ mechs.count = 1;
+ mechs.elements = (gss_OID)*mech;
+ }
+
+ major = gss_acquire_cred_from(&minor,
+ name,
+ life ? life : GSS_C_INDEFINITE,
+ *mech ? &mechs : GSS_C_NO_OID_SET,
+ GSS_C_INITIATE,
+ &cred_store,
+ cred,
+ NULL,
+ NULL);
+ if (major != GSS_S_COMPLETE) {
+ ret = _krb5_gss_map_error(major, minor);
+ goto out;
+ }
+
+out:
+ gss_release_name(&minor, &name);
+ return ret;
+}
+
+#ifndef NO_NTLM
+
+static krb5_error_code
+store_ntlmkey(krb5_context context, krb5_ccache id,
+ const char *domain, struct ntlm_buf *buf)
+{
+ krb5_error_code ret;
+ krb5_data data;
+ char *name;
+ int aret;
+
+ ret = krb5_cc_get_config(context, id, NULL, "default-ntlm-domain", &data);
+ if (ret == 0) {
+ krb5_data_free(&data);
+ } else {
+ data.length = strlen(domain);
+ data.data = rk_UNCONST(domain);
+ ret = krb5_cc_set_config(context, id, NULL, "default-ntlm-domain", &data);
+ if (ret != 0)
+ return ret;
+ }
+
+ aret = asprintf(&name, "ntlm-key-%s", domain);
+ if (aret == -1 || name == NULL)
+ return krb5_enomem(context);
+
+ data.length = buf->length;
+ data.data = buf->data;
+
+ ret = krb5_cc_set_config(context, id, NULL, name, &data);
+ free(name);
+ return ret;
+}
+#endif
+
+static krb5_error_code
+get_new_tickets(krb5_context context,
+ krb5_principal principal,
+ krb5_ccache ccache,
+ krb5_deltat ticket_life,
+ int interactive,
+ int anonymous_pkinit)
+{
+ krb5_error_code ret;
+ krb5_creds cred;
+ char passwd[256];
+ krb5_deltat start_time = 0;
+ krb5_deltat renew = 0;
+ const char *renewstr = NULL;
+ krb5_enctype *enctype = NULL;
+ krb5_ccache tempccache = NULL;
+ krb5_init_creds_context ctx = NULL;
+ krb5_get_init_creds_opt *opt = NULL;
+ krb5_prompter_fct prompter = krb5_prompter_posix;
+ gss_cred_id_t gss_cred = GSS_C_NO_CREDENTIAL;
+ gss_OID gss_mech = GSS_C_NO_OID;
+ krb5_principal federated_name = NULL;
+ krb5_ccache fastid = NULL;
+
+#ifndef NO_NTLM
+ struct ntlm_buf ntlmkey;
+ memset(&ntlmkey, 0, sizeof(ntlmkey));
+#endif
+ passwd[0] = '\0';
+
+ if (!interactive)
+ prompter = NULL;
+
+ if (password_file) {
+ FILE *f;
+
+ if (strcasecmp("STDIN", password_file) == 0)
+ f = stdin;
+ else
+ f = fopen(password_file, "r");
+ if (f == NULL) {
+ krb5_warnx(context, N_("Failed to open the password file %s", ""),
+ password_file);
+ return errno;
+ }
+
+ if (fgets(passwd, sizeof(passwd), f) == NULL) {
+ krb5_warnx(context, N_("Failed to read password from file %s", ""),
+ password_file);
+ if (f != stdin)
+ fclose(f);
+ return EINVAL; /* XXX Need a better error */
+ }
+ if (f != stdin)
+ fclose(f);
+ passwd[strcspn(passwd, "\n")] = '\0';
+ }
+
+#ifdef HAVE_FRAMEWORK_SECURITY
+ if (passwd[0] == '\0') {
+ enum querykey {
+ qk_class, qk_matchlimit, qk_service, qk_account, qk_secreturndata,
+ };
+ const void *querykeys[] = {
+ [qk_class] = kSecClass,
+ [qk_matchlimit] = kSecMatchLimit,
+ [qk_service] = kSecAttrService,
+ [qk_account] = kSecAttrAccount,
+ [qk_secreturndata] = kSecReturnData,
+ };
+ const void *queryargs[] = {
+ [qk_class] = kSecClassGenericPassword,
+ [qk_matchlimit] = kSecMatchLimitOne,
+ [qk_service] = NULL, /* filled in later */
+ [qk_account] = NULL, /* filled in later */
+ [qk_secreturndata] = kCFBooleanTrue,
+ };
+ CFStringRef service_ref = NULL;
+ CFStringRef account_ref = NULL;
+ CFDictionaryRef query_ref = NULL;
+ const char *realm;
+ OSStatus osret;
+ char *name = NULL;
+ CFTypeRef item_ref = NULL;
+ CFDataRef item;
+ CFIndex length;
+
+ realm = krb5_principal_get_realm(context, principal);
+
+ ret = krb5_unparse_name_flags(context, principal,
+ KRB5_PRINCIPAL_UNPARSE_NO_REALM, &name);
+ if (ret)
+ goto fail;
+
+ service_ref = CFStringCreateWithCString(kCFAllocatorDefault, realm,
+ kCFStringEncodingUTF8);
+ if (service_ref == NULL)
+ goto fail;
+
+ account_ref = CFStringCreateWithCString(kCFAllocatorDefault, name,
+ kCFStringEncodingUTF8);
+ if (account_ref == NULL)
+ goto fail;
+
+ queryargs[qk_service] = service_ref;
+ queryargs[qk_account] = account_ref;
+ query_ref = CFDictionaryCreate(kCFAllocatorDefault,
+ querykeys, queryargs,
+ /*numValues*/sizeof(querykeys)/sizeof(querykeys[0]),
+ /*keyCallbacks*/NULL, /*valueCallbacks*/NULL);
+ if (query_ref == NULL)
+ goto fail;
+
+ osret = SecItemCopyMatching(query_ref, &item_ref);
+ if (osret != noErr)
+ goto fail;
+
+ item = item_ref;
+ length = CFDataGetLength(item);
+ if (length >= sizeof(passwd) - 1)
+ goto fail;
+
+ CFDataGetBytes(item, CFRangeMake(0, length), (UInt8 *)passwd);
+ passwd[length] = '\0';
+
+ fail:
+ if (item_ref)
+ CFRelease(item_ref);
+ if (query_ref)
+ CFRelease(query_ref);
+ if (account_ref)
+ CFRelease(account_ref);
+ if (service_ref)
+ CFRelease(service_ref);
+ free(name);
+ }
+#endif
+
+ memset(&cred, 0, sizeof(cred));
+
+ ret = krb5_get_init_creds_opt_alloc(context, &opt);
+ if (ret) {
+ krb5_warn(context, ret, "krb5_get_init_creds_opt_alloc");
+ goto out;
+ }
+
+ krb5_get_init_creds_opt_set_default_flags(context, "kinit",
+ krb5_principal_get_realm(context, principal), opt);
+
+ if (forwardable_flag != -1)
+ krb5_get_init_creds_opt_set_forwardable(opt, forwardable_flag);
+ if (proxiable_flag != -1)
+ krb5_get_init_creds_opt_set_proxiable(opt, proxiable_flag);
+ if (anonymous_flag)
+ krb5_get_init_creds_opt_set_anonymous(opt, anonymous_flag);
+ if (pac_flag != -1)
+ krb5_get_init_creds_opt_set_pac_request(context, opt,
+ pac_flag ? TRUE : FALSE);
+ if (canonicalize_flag)
+ krb5_get_init_creds_opt_set_canonicalize(context, opt, TRUE);
+ if (pk_enterprise_flag || enterprise_flag || canonicalize_flag || windows_flag)
+ krb5_get_init_creds_opt_set_win2k(context, opt, TRUE);
+ if (pk_user_id || ent_user_id || anonymous_pkinit) {
+ if (pk_anon_fast_armor == -1)
+ pk_anon_fast_armor = 0;
+ ret = krb5_get_init_creds_opt_set_pkinit(context, opt,
+ principal,
+ pk_user_id,
+ pk_x509_anchors,
+ NULL,
+ NULL,
+ pk_use_enckey ? KRB5_GIC_OPT_PKINIT_USE_ENCKEY : 0 |
+ anonymous_pkinit ? KRB5_GIC_OPT_PKINIT_ANONYMOUS : 0,
+ prompter,
+ NULL,
+ passwd);
+ if (ret) {
+ krb5_warn(context, ret, "krb5_get_init_creds_opt_set_pkinit");
+ goto out;
+ }
+ if (ent_user_id)
+ krb5_get_init_creds_opt_set_pkinit_user_certs(context, opt, ent_user_id);
+ }
+
+ if (addrs_flag != -1)
+ krb5_get_init_creds_opt_set_addressless(context, opt,
+ addrs_flag ? FALSE : TRUE);
+
+ if (renew_life == NULL && renewable_flag)
+ renewstr = "6 months";
+ if (renew_life)
+ renewstr = renew_life;
+ if (renewstr) {
+ renew = parse_time(renewstr, "s");
+ if (renew < 0)
+ errx(1, "unparsable time: %s", renewstr);
+
+ krb5_get_init_creds_opt_set_renew_life(opt, renew);
+ }
+
+ if (ticket_life != 0)
+ krb5_get_init_creds_opt_set_tkt_life(opt, ticket_life);
+
+ if (start_str) {
+ int tmp = parse_time(start_str, "s");
+ if (tmp < 0)
+ errx(1, N_("unparsable time: %s", ""), start_str);
+
+ start_time = tmp;
+ }
+
+ if (etype_str.num_strings) {
+ int i;
+
+ enctype = malloc(etype_str.num_strings * sizeof(*enctype));
+ if (enctype == NULL)
+ errx(1, "out of memory");
+ for(i = 0; i < etype_str.num_strings; i++) {
+ ret = krb5_string_to_enctype(context,
+ etype_str.strings[i],
+ &enctype[i]);
+ if (ret)
+ errx(1, "unrecognized enctype: %s", etype_str.strings[i]);
+ }
+ krb5_get_init_creds_opt_set_etype_list(opt, enctype,
+ etype_str.num_strings);
+ }
+
+ if (gss_preauth_mech || gss_preauth_name) {
+ ret = acquire_gss_cred(context, principal, ticket_life,
+ passwd, &gss_cred, &gss_mech);
+ if (ret)
+ goto out;
+
+ /*
+ * The principal specified on the command line is used as the GSS-API
+ * initiator name, unless the --gss-name option was present, in which
+ * case the initiator name is specified independently.
+ */
+ if (gss_preauth_name == NULL) {
+ krb5_const_realm realm = krb5_principal_get_realm(context, principal);
+
+ ret = make_wellknown_name(context, realm,
+ KRB5_FEDERATED_NAME, &federated_name);
+ if (ret)
+ goto out;
+
+ principal = federated_name;
+ }
+ }
+
+ if (fast_armor_cache_string) {
+ if (pk_anon_fast_armor > 0)
+ krb5_errx(context, 1,
+ N_("cannot specify FAST armor cache with FAST "
+ "anonymous PKINIT option", ""));
+ pk_anon_fast_armor = 0;
+
+ ret = krb5_cc_resolve(context, fast_armor_cache_string, &fastid);
+ if (ret) {
+ krb5_warn(context, ret, "krb5_cc_resolve(FAST cache)");
+ goto out;
+ }
+
+ ret = krb5_get_init_creds_opt_set_fast_ccache(context, opt, fastid);
+ if (ret) {
+ krb5_warn(context, ret, "krb5_init_creds_set_fast_ccache");
+ goto out;
+ }
+
+ ret = krb5_get_init_creds_opt_set_fast_flags(context, opt,
+ KRB5_FAST_REQUIRED);
+ if (ret) {
+ krb5_warn(context, ret, "krb5_init_creds_set_fast_flags");
+ goto out;
+ }
+ }
+
+ ret = krb5_init_creds_init(context, principal, prompter, NULL, start_time, opt, &ctx);
+ if (ret) {
+ krb5_warn(context, ret, "krb5_init_creds_init");
+ goto out;
+ }
+
+ if (server_str) {
+ ret = krb5_init_creds_set_service(context, ctx, server_str);
+ if (ret) {
+ krb5_warn(context, ret, "krb5_init_creds_set_service");
+ goto out;
+ }
+ }
+
+ if (kdc_hostname) {
+ ret = krb5_init_creds_set_kdc_hostname(context, ctx, kdc_hostname);
+ if (ret) {
+ krb5_warn(context, ret, "krb5_init_creds_set_kdc_hostname");
+ goto out;
+ }
+ }
+
+ if (anonymous_flag && pk_anon_fast_armor == -1)
+ pk_anon_fast_armor = 0;
+ if (!gss_preauth_mech && anonymous_flag && pk_anon_fast_armor) {
+ krb5_warnx(context, N_("Ignoring --pk-anon-fast-armor because "
+ "--anonymous given", ""));
+ pk_anon_fast_armor = 0;
+ }
+
+ if (fast_armor_cache_string) {
+ /* handled above */
+ } else if (pk_anon_fast_armor == -1) {
+ ret = _krb5_init_creds_set_fast_anon_pkinit_optimistic(context, ctx);
+ if (ret) {
+ krb5_warn(context, ret, "_krb5_init_creds_set_fast_anon_pkinit_optimistic");
+ goto out;
+ }
+ } else if (pk_anon_fast_armor) {
+ ret = krb5_init_creds_set_fast_anon_pkinit(context, ctx);
+ if (ret) {
+ krb5_warn(context, ret, "krb5_init_creds_set_fast_anon_pkinit");
+ goto out;
+ }
+ }
+
+ if (gss_mech != GSS_C_NO_OID) {
+ ret = krb5_gss_set_init_creds(context, ctx, gss_cred, gss_mech);
+ if (ret) {
+ krb5_warn(context, ret, "krb5_gss_set_init_creds");
+ goto out;
+ }
+ } else if (use_keytab || keytab_str) {
+ ret = krb5_init_creds_set_keytab(context, ctx, kt);
+ if (ret) {
+ krb5_warn(context, ret, "krb5_init_creds_set_keytab");
+ goto out;
+ }
+ } else if (pk_user_id || ent_user_id ||
+ krb5_principal_is_anonymous(context, principal, KRB5_ANON_MATCH_ANY)) {
+ /* nop */;
+ } else if (!interactive && passwd[0] == '\0') {
+ static int already_warned = 0;
+
+ if (!already_warned)
+ krb5_warnx(context, "Not interactive, failed to get "
+ "initial ticket");
+ krb5_get_init_creds_opt_free(context, opt);
+ already_warned = 1;
+ return 0;
+ } else {
+
+ if (passwd[0] == '\0') {
+ char *p, *prompt;
+ int aret = 0;
+
+ ret = krb5_unparse_name(context, principal, &p);
+ if (ret)
+ errx(1, "failed to generate passwd prompt: not enough memory");
+
+ aret = asprintf(&prompt, N_("%s's Password: ", ""), p);
+ free(p);
+ if (aret == -1)
+ errx(1, "failed to generate passwd prompt: not enough memory");
+
+ if (UI_UTIL_read_pw_string(passwd, sizeof(passwd)-1, prompt, 0)){
+ memset(passwd, 0, sizeof(passwd));
+ errx(1, "failed to read password");
+ }
+ free(prompt);
+ }
+
+ if (passwd[0]) {
+ ret = krb5_init_creds_set_password(context, ctx, passwd);
+ if (ret) {
+ krb5_warn(context, ret, "krb5_init_creds_set_password");
+ goto out;
+ }
+ }
+ }
+
+ ret = krb5_init_creds_get(context, ctx);
+
+#ifndef NO_NTLM
+ if (ntlm_domain && passwd[0])
+ heim_ntlm_nt_key(passwd, &ntlmkey);
+#endif
+ memset_s(passwd, sizeof(passwd), 0, sizeof(passwd));
+
+ switch(ret){
+ case 0:
+ break;
+ case KRB5_LIBOS_PWDINTR: /* don't print anything if it was just C-c:ed */
+ exit(1);
+ case KRB5KRB_AP_ERR_BAD_INTEGRITY:
+ case KRB5KRB_AP_ERR_MODIFIED:
+ case KRB5KDC_ERR_PREAUTH_FAILED:
+ case KRB5_GET_IN_TKT_LOOP:
+ krb5_warnx(context, N_("Password incorrect", ""));
+ goto out;
+ case KRB5KRB_AP_ERR_V4_REPLY:
+ krb5_warnx(context, N_("Looks like a Kerberos 4 reply", ""));
+ goto out;
+ case KRB5KDC_ERR_KEY_EXPIRED:
+ krb5_warnx(context, N_("Password expired", ""));
+ goto out;
+ default:
+ krb5_warn(context, ret, "krb5_get_init_creds");
+ goto out;
+ }
+
+ krb5_process_last_request(context, opt, ctx);
+
+ ret = krb5_init_creds_get_creds(context, ctx, &cred);
+ if (ret) {
+ krb5_warn(context, ret, "krb5_init_creds_get_creds");
+ goto out;
+ }
+
+ if (ticket_life != 0) {
+ if (labs(cred.times.endtime - cred.times.starttime - ticket_life) > 30) {
+ char life[64];
+ unparse_time_approx(cred.times.endtime - cred.times.starttime,
+ life, sizeof(life));
+ krb5_warnx(context, N_("NOTICE: ticket lifetime is %s", ""), life);
+ }
+ }
+ if (renew_life) {
+ if (labs(cred.times.renew_till - cred.times.starttime - renew) > 30) {
+ char life[64];
+ unparse_time_approx(cred.times.renew_till - cred.times.starttime,
+ life, sizeof(life));
+ krb5_warnx(context,
+ N_("NOTICE: ticket renewable lifetime is %s", ""),
+ life);
+ }
+ }
+ krb5_free_cred_contents(context, &cred);
+
+ ret = krb5_cc_new_unique(context, krb5_cc_get_type(context, ccache),
+ NULL, &tempccache);
+ if (ret) {
+ krb5_warn(context, ret, "krb5_cc_new_unique");
+ goto out;
+ }
+
+ ret = krb5_init_creds_store(context, ctx, tempccache);
+ if (ret) {
+ krb5_warn(context, ret, "krb5_init_creds_store");
+ goto out;
+ }
+
+ ret = krb5_init_creds_store_config(context, ctx, tempccache);
+ if (ret) {
+ krb5_warn(context, ret, "krb5_init_creds_store_config");
+ goto out;
+ }
+
+ ret = krb5_init_creds_warn_user(context, ctx);
+ if (ret) {
+ krb5_warn(context, ret, "krb5_init_creds_warn_user");
+ goto out;
+ }
+
+ krb5_init_creds_free(context, ctx);
+ ctx = NULL;
+
+ ret = krb5_cc_move(context, tempccache, ccache);
+ if (ret) {
+ krb5_warn(context, ret, "krb5_cc_move");
+ goto out;
+ }
+ tempccache = NULL;
+
+ if (switch_cache_flags)
+ krb5_cc_switch(context, ccache);
+
+#ifndef NO_NTLM
+ if (ntlm_domain && ntlmkey.data)
+ store_ntlmkey(context, ccache, ntlm_domain, &ntlmkey);
+#endif
+
+ if (ok_as_delegate_flag || windows_flag || use_referrals_flag) {
+ unsigned char d = 0;
+ krb5_data data;
+
+ if (ok_as_delegate_flag || windows_flag)
+ d |= 1;
+ if (use_referrals_flag || windows_flag)
+ d |= 2;
+
+ data.length = 1;
+ data.data = &d;
+
+ krb5_cc_set_config(context, ccache, NULL, "realm-config", &data);
+ }
+
+ if (anonymous_pkinit) {
+ krb5_data data;
+
+ data.length = strlen(principal->realm);
+ data.data = principal->realm;
+
+ krb5_cc_set_config(context, ccache, NULL, "anon_pkinit_realm", &data);
+ }
+
+out:
+ {
+ OM_uint32 minor;
+ gss_release_cred(&minor, &gss_cred);
+ }
+ krb5_free_principal(context, federated_name);
+ krb5_get_init_creds_opt_free(context, opt);
+ if (ctx)
+ krb5_init_creds_free(context, ctx);
+ if (tempccache)
+ krb5_cc_destroy(context, tempccache);
+ if (enctype)
+ free(enctype);
+ if (fastid)
+ krb5_cc_close(context, fastid);
+
+ return ret;
+}
+
+static time_t
+ticket_lifetime(krb5_context context, krb5_ccache cache, krb5_principal client,
+ const char *server, time_t *renew)
+{
+ krb5_creds in_cred, *cred;
+ krb5_error_code ret;
+ time_t timeout;
+ time_t curtime;
+
+ memset(&in_cred, 0, sizeof(in_cred));
+
+ if (renew != NULL)
+ *renew = 0;
+
+ ret = krb5_cc_get_principal(context, cache, &in_cred.client);
+ if (ret) {
+ krb5_warn(context, ret, "krb5_cc_get_principal");
+ return 0;
+ }
+
+ /* Determine TGS principal without fallback */
+ ret = get_server(context, cache, in_cred.client, server, FALSE,
+ &in_cred.server);
+ if (ret) {
+ krb5_free_principal(context, in_cred.client);
+ krb5_warn(context, ret, "get_server");
+ return 0;
+ }
+
+ ret = krb5_get_credentials(context, KRB5_GC_CACHED,
+ cache, &in_cred, &cred);
+ krb5_free_principal(context, in_cred.client);
+ krb5_free_principal(context, in_cred.server);
+ if (ret) {
+ krb5_warn(context, ret, "krb5_get_credentials");
+ return 0;
+ }
+ curtime = time(NULL);
+ timeout = cred->times.endtime - curtime;
+ if (timeout < 0)
+ timeout = 0;
+ if (renew) {
+ *renew = cred->times.renew_till - curtime;
+ if (*renew < 0)
+ *renew = 0;
+ }
+ krb5_free_creds(context, cred);
+ return timeout;
+}
+
+static time_t expire;
+
+static char siginfo_msg[1024] = "No credentials\n";
+
+static void
+update_siginfo_msg(time_t exp, const char *srv)
+{
+ /* Note that exp is relative time */
+ memset(siginfo_msg, 0, sizeof(siginfo_msg));
+ memcpy(&siginfo_msg, "Updating...\n", sizeof("Updating...\n"));
+ if (exp) {
+ if (srv == NULL) {
+ snprintf(siginfo_msg, sizeof(siginfo_msg),
+ N_("kinit: TGT expires in %llu seconds\n", ""),
+ (unsigned long long)expire);
+ } else {
+ snprintf(siginfo_msg, sizeof(siginfo_msg),
+ N_("kinit: Ticket for %s expired\n", ""), srv);
+ }
+ return;
+ }
+
+ /* Expired creds */
+ if (srv == NULL) {
+ snprintf(siginfo_msg, sizeof(siginfo_msg),
+ N_("kinit: TGT expired\n", ""));
+ } else {
+ snprintf(siginfo_msg, sizeof(siginfo_msg),
+ N_("kinit: Ticket for %s expired\n", ""), srv);
+ }
+}
+
+#ifdef HAVE_SIGACTION
+static void
+handler(int sig)
+{
+ if (sig == SIGINFO) {
+ struct iovec iov[2];
+
+ iov[0].iov_base = rk_UNCONST(siginfo_msg);
+ iov[0].iov_len = strlen(siginfo_msg);
+ iov[1].iov_base = "\n";
+ iov[1].iov_len = 1;
+
+ writev(STDERR_FILENO, iov, sizeof(iov)/sizeof(iov[0]));
+ } /* else ignore interrupts; our progeny will not ignore them */
+}
+#endif
+
+struct renew_ctx {
+ krb5_context context;
+ krb5_ccache ccache;
+ krb5_principal principal;
+ krb5_deltat ticket_life;
+ krb5_deltat timeout;
+ int anonymous_pkinit;
+};
+
+static time_t
+renew_func(void *ptr)
+{
+ krb5_error_code ret;
+ struct renew_ctx *ctx = ptr;
+ time_t renew_expire;
+ static time_t exp_delay = 1;
+
+ /*
+ * NOTE: We count on the ccache implementation to notice changes to the
+ * actual ccache filesystem/whatever objects. There should be no ccache
+ * types for which this is not the case, but it might not hurt to
+ * re-krb5_cc_resolve() after each successful renew_validate()/
+ * get_new_tickets() call.
+ */
+
+ expire = ticket_lifetime(ctx->context, ctx->ccache, ctx->principal,
+ server_str, &renew_expire);
+
+ /*
+ * When a keytab is available to obtain new tickets, if we are within
+ * half of the original ticket lifetime of the renew limit, get a new
+ * TGT instead of renewing the existing TGT. Note, ctx->ticket_life
+ * is zero by default (without a '-l' option) and cannot be used to
+ * set the time scale on which we decide whether we're "close to the
+ * renew limit".
+ */
+ if (use_keytab || keytab_str)
+ expire += ctx->timeout;
+ if (renew_expire > expire) {
+ ret = renew_validate(ctx->context, 1, validate_flag, &ctx->ccache,
+ NULL, FALSE, server_str, ctx->ticket_life);
+ } else {
+ ret = get_new_tickets(ctx->context, ctx->principal, ctx->ccache,
+ ctx->ticket_life, 0, ctx->anonymous_pkinit);
+ }
+ if (ret == 0) {
+ expire = ticket_lifetime(ctx->context, ctx->ccache, ctx->principal,
+ server_str, &renew_expire);
+
+#ifndef NO_AFS
+ if (server_str == NULL && do_afslog && k_hasafs())
+ krb5_afslog(ctx->context, ctx->ccache, NULL, NULL);
+#endif
+ }
+
+ update_siginfo_msg(expire, server_str);
+
+ /*
+ * If our tickets have expired and we been able to either renew them
+ * or obtain new tickets, then we still call this function but we use
+ * an exponential backoff. This should take care of the case where
+ * we are using stored credentials but the KDC has been unavailable
+ * for some reason...
+ */
+
+ if (expire < 1) {
+ /*
+ * We can't ask to keep spamming stderr but not syslog, so we warn
+ * only once.
+ */
+ if (exp_delay == 1) {
+ krb5_warnx(ctx->context, N_("NOTICE: Could not renew/refresh "
+ "tickets", ""));
+ }
+ if (exp_delay < 7200)
+ exp_delay += exp_delay / 2 + 1;
+ return exp_delay;
+ }
+ exp_delay = 1;
+
+ return expire / 2 + 1;
+}
+
+static void
+set_princ_realm(krb5_context context,
+ krb5_principal principal,
+ const char *realm)
+{
+ krb5_error_code ret;
+
+ if ((ret = krb5_principal_set_realm(context, principal, realm)) != 0)
+ krb5_err(context, 1, ret, "krb5_principal_set_realm");
+}
+
+static void
+parse_name_realm(krb5_context context,
+ const char *name,
+ int flags,
+ const char *realm,
+ krb5_principal *princ)
+{
+ krb5_error_code ret;
+
+ if (realm)
+ flags |= KRB5_PRINCIPAL_PARSE_NO_DEF_REALM;
+ if ((ret = krb5_parse_name_flags(context, name, flags, princ)) != 0)
+ krb5_err(context, 1, ret, "krb5_parse_name_flags");
+ if (realm && krb5_principal_get_realm(context, *princ) == NULL)
+ set_princ_realm(context, *princ, realm);
+}
+
+static char *
+get_default_realm(krb5_context context)
+{
+ char *realm;
+ krb5_error_code ret;
+
+ if ((ret = krb5_get_default_realm(context, &realm)) != 0)
+ krb5_err(context, 1, ret, "krb5_get_default_realm");
+ return realm;
+}
+
+static void
+get_default_principal(krb5_context context, krb5_principal *princ)
+{
+ krb5_error_code ret;
+
+ if ((ret = krb5_get_default_principal(context, princ)) != 0)
+ krb5_err(context, 1, ret, "krb5_get_default_principal");
+}
+
+static char *
+get_user_realm(krb5_context context)
+{
+ krb5_error_code ret;
+ char *user_realm = NULL;
+
+ /*
+ * If memory allocation fails, we don't try to use the wrong realm,
+ * that will trigger misleading error messages complicate support.
+ */
+ krb5_appdefault_string(context, "kinit", NULL, "user_realm", "",
+ &user_realm);
+ if (user_realm == NULL) {
+ ret = krb5_enomem(context);
+ krb5_err(context, 1, ret, "krb5_appdefault_string");
+ }
+
+ if (*user_realm == 0) {
+ free(user_realm);
+ user_realm = NULL;
+ }
+
+ return user_realm;
+}
+
+static void
+get_princ(krb5_context context,
+ krb5_principal *principal,
+ const char *ccname,
+ const char *name)
+{
+ krb5_error_code ret = 0;
+ krb5_principal tmp;
+ int parseflags = 0;
+ char *user_realm;
+
+ if (name == NULL) {
+ krb5_ccache ccache = NULL;
+
+ /* If credential cache provides a client principal, use that. */
+ if (ccname)
+ ret = krb5_cc_resolve(context, ccname, &ccache);
+ else
+ ret = krb5_cc_default(context, &ccache);
+ if (ret == 0)
+ ret = krb5_cc_get_principal(context, ccache, principal);
+ krb5_cc_close(context, ccache);
+ if (ret == 0)
+ return;
+ }
+
+ user_realm = get_user_realm(context);
+
+ if (name) {
+ if (enterprise_flag)
+ parseflags |= KRB5_PRINCIPAL_PARSE_ENTERPRISE;
+
+ parse_name_realm(context, name, parseflags, user_realm, &tmp);
+
+ if (user_realm && krb5_principal_get_num_comp(context, tmp) > 1) {
+ /* Principal is instance qualified, reparse with default realm. */
+ krb5_free_principal(context, tmp);
+ parse_name_realm(context, name, parseflags, NULL, principal);
+ } else {
+ *principal = tmp;
+ }
+ } else {
+ get_default_principal(context, principal);
+ if (user_realm)
+ set_princ_realm(context, *principal, user_realm);
+ }
+
+ if (user_realm)
+ free(user_realm);
+}
+
+static void
+get_princ_kt(krb5_context context,
+ krb5_principal *principal,
+ char *name)
+{
+ krb5_error_code ret;
+ krb5_principal tmp;
+ krb5_ccache ccache;
+ krb5_kt_cursor cursor;
+ krb5_keytab_entry entry;
+ char *def_realm;
+
+ if (name == NULL) {
+ /*
+ * If the credential cache exists and specifies a client principal,
+ * use that.
+ */
+ if (krb5_cc_default(context, &ccache) == 0) {
+ ret = krb5_cc_get_principal(context, ccache, principal);
+ krb5_cc_close(context, ccache);
+ if (ret == 0)
+ return;
+ }
+ }
+
+ if (name) {
+ /* If the principal specifies an explicit realm, just use that. */
+ int parseflags = KRB5_PRINCIPAL_PARSE_NO_DEF_REALM;
+
+ parse_name_realm(context, name, parseflags, NULL, &tmp);
+ if (krb5_principal_get_realm(context, tmp) != NULL) {
+ *principal = tmp;
+ return;
+ }
+ } else {
+ /* Otherwise, search keytab for bare name of the default principal. */
+ get_default_principal(context, &tmp);
+ set_princ_realm(context, tmp, NULL);
+ }
+
+ def_realm = get_default_realm(context);
+
+ ret = krb5_kt_start_seq_get(context, kt, &cursor);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_kt_start_seq_get");
+
+ while (ret == 0 &&
+ krb5_kt_next_entry(context, kt, &entry, &cursor) == 0) {
+ const char *realm;
+
+ if (!krb5_principal_compare_any_realm(context, tmp, entry.principal))
+ continue;
+ if (*principal &&
+ krb5_principal_compare(context, *principal, entry.principal))
+ continue;
+ /* The default realm takes precedence */
+ realm = krb5_principal_get_realm(context, entry.principal);
+ if (*principal && strcmp(def_realm, realm) == 0) {
+ krb5_free_principal(context, *principal);
+ ret = krb5_copy_principal(context, entry.principal, principal);
+ break;
+ }
+ if (!*principal)
+ ret = krb5_copy_principal(context, entry.principal, principal);
+ }
+ if (ret != 0 || (ret = krb5_kt_end_seq_get(context, kt, &cursor)) != 0)
+ krb5_err(context, 1, ret, "get_princ_kt");
+ if (!*principal) {
+ if (name)
+ parse_name_realm(context, name, 0, NULL, principal);
+ else
+ krb5_err(context, 1, KRB5_CC_NOTFOUND, "get_princ_kt");
+ }
+
+ krb5_free_principal(context, tmp);
+ free(def_realm);
+}
+
+static krb5_error_code
+get_switched_ccache(krb5_context context,
+ const char * type,
+ krb5_principal principal,
+ krb5_ccache *ccache)
+{
+ krb5_error_code ret;
+
+#ifdef _WIN32
+ if (strcmp(type, "API") == 0) {
+ /*
+ * Windows stores the default ccache name in the
+ * registry which is shared across multiple logon
+ * sessions for the same user. The API credential
+ * cache provides a unique name space per logon
+ * session. Therefore there is no need to generate
+ * a unique ccache name. Instead use the principal
+ * name. This provides a friendlier user experience.
+ */
+ char * unparsed_name;
+ char * cred_cache;
+
+ ret = krb5_unparse_name(context, principal,
+ &unparsed_name);
+ if (ret)
+ krb5_err(context, 1, ret,
+ N_("unparsing principal name", ""));
+
+ ret = asprintf(&cred_cache, "API:%s", unparsed_name);
+ krb5_free_unparsed_name(context, unparsed_name);
+ if (ret == -1 || cred_cache == NULL)
+ krb5_err(context, 1, ret,
+ N_("building credential cache name", ""));
+
+ ret = krb5_cc_resolve(context, cred_cache, ccache);
+ free(cred_cache);
+ } else if (strcmp(type, "MSLSA") == 0) {
+ /*
+ * The Windows MSLSA cache when it is writeable
+ * stores tickets for multiple client principals
+ * in a single credential cache.
+ */
+ ret = krb5_cc_resolve(context, "MSLSA:", ccache);
+ } else {
+ ret = krb5_cc_new_unique(context, type, NULL, ccache);
+ }
+#else /* !_WIN32 */
+ ret = krb5_cc_new_unique(context, type, NULL, ccache);
+#endif /* _WIN32 */
+
+ return ret;
+}
+
+int
+main(int argc, char **argv)
+{
+ krb5_error_code ret;
+ krb5_context context;
+ krb5_ccache ccache;
+ krb5_principal principal = NULL;
+ int optidx = 0;
+ krb5_deltat ticket_life = 0;
+#ifdef HAVE_SIGACTION
+ struct sigaction sa;
+#endif
+ krb5_boolean unique_ccache = FALSE;
+ krb5_boolean historical_anon_pkinit = FALSE;
+ int anonymous_pkinit = FALSE;
+
+ setprogname(argv[0]);
+
+ setlocale(LC_ALL, "");
+ bindtextdomain("heimdal_kuser", HEIMDAL_LOCALEDIR);
+ textdomain("heimdal_kuser");
+
+ ret = krb5_init_context(&context);
+ if (ret == KRB5_CONFIG_BADFORMAT)
+ krb5_err(context, 1, ret, "Failed to parse configuration file");
+ else if (ret == EISDIR)
+ /* We use EISDIR to mean "not a regular file" */
+ krb5_errx(context, 1,
+ "Failed to read configuration file: not a regular file");
+ else if (ret)
+ krb5_err(context, 1, ret, "Failed to read configuration file");
+
+ if (getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
+ usage(1);
+
+ if (help_flag)
+ usage(0);
+
+ if (version_flag) {
+ print_version(NULL);
+ exit(0);
+ }
+
+ argc -= optidx;
+ argv += optidx;
+
+ krb5_appdefault_boolean(context, "kinit", NULL, "historical_anon_pkinit",
+ FALSE, &historical_anon_pkinit);
+
+ /*
+ * Open the keytab now, we use the keytab to determine the principal's
+ * realm when the requested principal has no realm.
+ */
+ if (use_keytab || keytab_str) {
+ if (keytab_str)
+ ret = krb5_kt_resolve(context, keytab_str, &kt);
+ else
+ ret = krb5_kt_default(context, &kt);
+ if (ret)
+ krb5_err(context, 1, ret, "resolving keytab");
+ }
+
+ if (pk_enterprise_flag) {
+ ret = krb5_pk_enterprise_cert(context, pk_user_id,
+ argv[0], &principal,
+ &ent_user_id);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_pk_enterprise_certs");
+
+ pk_user_id = NULL;
+ if (pk_anon_fast_armor > 0)
+ krb5_warnx(context, N_("Ignoring --pk-anon-fast-armor "
+ "because --pk-user given", ""));
+ pk_anon_fast_armor = 0;
+ } else if (argc && argv[0][0] == '@' &&
+ (gss_preauth_mech || anonymous_flag)) {
+ const char *instance = NULL;
+
+ if (gss_preauth_mech) {
+ instance = KRB5_FEDERATED_NAME;
+ } else if (anonymous_flag) {
+ instance = KRB5_ANON_NAME;
+ anonymous_pkinit = TRUE;
+ }
+
+ ret = make_wellknown_name(context, &argv[0][1], instance, &principal);
+ if (ret)
+ krb5_err(context, 1, ret, "make_wellknown_name");
+ if (!gss_preauth_mech && pk_anon_fast_armor > 1) {
+ krb5_warnx(context, N_("Ignoring --pk-anon-fast-armor "
+ "because --anonymous given", ""));
+ pk_anon_fast_armor = 0;
+ }
+ } else if (anonymous_flag && historical_anon_pkinit) {
+ char *realm = argc == 0 ? get_default_realm(context) :
+ argv[0][0] == '@' ? &argv[0][1] : argv[0];
+
+ ret = krb5_make_principal(context, &principal, realm,
+ KRB5_WELLKNOWN_NAME, KRB5_ANON_NAME, NULL);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_make_principal");
+ krb5_principal_set_type(context, principal, KRB5_NT_WELLKNOWN);
+ anonymous_pkinit = TRUE;
+ } else if (use_keytab || keytab_str) {
+ get_princ_kt(context, &principal, argv[0]);
+ } else if (gss_preauth_mech && argc == 0 && gss_preauth_name == NULL) {
+ /*
+ * Use the federated name as a placeholder if we have neither a Kerberos
+ * nor a GSS-API client name, and we are performing GSS-API preauth.
+ */
+ ret = make_wellknown_name(context, get_default_realm(context),
+ KRB5_FEDERATED_NAME, &principal);
+ if (ret)
+ krb5_err(context, 1, ret, "make_wellknown_name");
+ } else {
+ get_princ(context, &principal, cred_cache, argv[0]);
+ }
+
+ if (fcache_version)
+ krb5_set_fcache_version(context, fcache_version);
+
+ if (renewable_flag == -1)
+ /* this seems somewhat pointless, but whatever */
+ krb5_appdefault_boolean(context, "kinit",
+ krb5_principal_get_realm(context, principal),
+ "renewable", FALSE, &renewable_flag);
+ if (do_afslog == -1)
+ krb5_appdefault_boolean(context, "kinit",
+ krb5_principal_get_realm(context, principal),
+ "afslog", TRUE, &do_afslog);
+
+ /*
+ * Cases:
+ *
+ * - use the given ccache
+ * - use a new unique ccache for running a command with (in this case we
+ * get to set KRB5CCNAME, so a new unique ccache makes sense)
+ * - use the default ccache for the given principal as requested and do
+ * not later switch the collection's default/primary to it
+ * - use the default cache, possibly a new unique one that later gets
+ * switched to it
+ *
+ * The important thing is that, except for the case where we're running a
+ * command, we _can't set KRB5CCNAME_, and we can't expect the user to read
+ * our output and figure out to set it (we could have an output-for-shell-
+ * eval mode, like ssh-agent and such, but we don't). Therefore, in all
+ * cases where we can't set KRB5CCNAME we must do something that makes
+ * sense to the user, and that is to either initialize a given ccache, use
+ * the default, or use a subsidiary ccache named after the principal whose
+ * creds we're initializing.
+ */
+ if (cred_cache) {
+ /* Use the given ccache */
+ ret = krb5_cc_resolve(context, cred_cache, &ccache);
+ } else if (argc > 1) {
+ char s[1024];
+
+ /*
+ * A command was given, so use a new unique ccache (and destroy it
+ * later).
+ */
+ ret = krb5_cc_new_unique(context, NULL, NULL, &ccache);
+ if (ret)
+ krb5_err(context, 1, ret, "creating cred cache");
+ snprintf(s, sizeof(s), "%s:%s",
+ krb5_cc_get_type(context, ccache),
+ krb5_cc_get_name(context, ccache));
+ setenv("KRB5CCNAME", s, 1);
+ unique_ccache = TRUE;
+ switch_cache_flags = 0;
+ } else if (default_for) {
+ ret = krb5_cc_default_for(context, principal, &ccache);
+ if (switch_cache_flags == -1)
+ switch_cache_flags = 0;
+ } else {
+ ret = krb5_cc_cache_match(context, principal, &ccache);
+ if (ret) {
+ const char *type;
+ ret = krb5_cc_default(context, &ccache);
+ if (ret)
+ krb5_err(context, 1, ret,
+ N_("resolving credentials cache", ""));
+
+ /*
+ * Check if the type support switching, and we do,
+ * then do that instead over overwriting the current
+ * default credential
+ */
+ type = krb5_cc_get_type(context, ccache);
+ if (krb5_cc_support_switch(context, type) &&
+ strcmp(type, "FILE")) {
+ krb5_cc_close(context, ccache);
+ ret = get_switched_ccache(context, type, principal,
+ &ccache);
+ if (ret == 0)
+ unique_ccache = TRUE;
+ }
+ }
+ }
+ if (ret)
+ krb5_err(context, 1, ret, N_("resolving credentials cache", ""));
+
+#ifndef NO_AFS
+ if (argc > 1 && k_hasafs())
+ k_setpag();
+#endif
+
+ if (lifetime) {
+ int tmp = parse_time(lifetime, "s");
+ if (tmp < 0)
+ errx(1, N_("unparsable time: %s", ""), lifetime);
+
+ ticket_life = tmp;
+ }
+
+ if (addrs_flag == 0 && extra_addresses.num_strings > 0)
+ krb5_errx(context, 1,
+ N_("specifying both extra addresses and "
+ "no addresses makes no sense", ""));
+ {
+ int i;
+ krb5_addresses addresses;
+ memset(&addresses, 0, sizeof(addresses));
+ for(i = 0; i < extra_addresses.num_strings; i++) {
+ ret = krb5_parse_address(context, extra_addresses.strings[i],
+ &addresses);
+ if (ret == 0) {
+ krb5_add_extra_addresses(context, &addresses);
+ krb5_free_addresses(context, &addresses);
+ }
+ }
+ free_getarg_strings(&extra_addresses);
+ }
+
+ if (renew_flag || validate_flag) {
+ ret = renew_validate(context, renew_flag, validate_flag,
+ &ccache, principal,
+ default_for ? TRUE : FALSE, server_str,
+ ticket_life);
+
+#ifndef NO_AFS
+ if (ret == 0 && server_str == NULL && do_afslog && k_hasafs())
+ krb5_afslog(context, ccache, NULL, NULL);
+#endif
+
+ if (unique_ccache)
+ krb5_cc_destroy(context, ccache);
+ exit(ret != 0);
+ }
+
+ ret = get_new_tickets(context, principal, ccache, ticket_life,
+ 1, anonymous_pkinit);
+ if (ret) {
+ if (unique_ccache)
+ krb5_cc_destroy(context, ccache);
+ exit(1);
+ }
+
+#ifndef NO_AFS
+ if (ret == 0 && server_str == NULL && do_afslog && k_hasafs())
+ krb5_afslog(context, ccache, NULL, NULL);
+#endif
+
+ if (argc > 1) {
+ struct renew_ctx ctx;
+ time_t timeout;
+
+ timeout = ticket_lifetime(context, ccache, principal,
+ server_str, NULL) / 2;
+
+ ctx.context = context;
+ ctx.ccache = ccache;
+ ctx.principal = principal;
+ ctx.ticket_life = ticket_life;
+ ctx.timeout = timeout;
+ ctx.anonymous_pkinit = anonymous_pkinit;
+
+#ifdef HAVE_SIGACTION
+ memset(&sa, 0, sizeof(sa));
+ sigemptyset(&sa.sa_mask);
+ sa.sa_handler = handler;
+
+ sigaction(SIGINFO, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+ sigaction(SIGQUIT, &sa, NULL);
+#endif
+
+ ret = simple_execvp_timed(argv[1], argv+1,
+ renew_func, &ctx, timeout);
+#define EX_NOEXEC 126
+#define EX_NOTFOUND 127
+ if (ret == EX_NOEXEC)
+ krb5_warnx(context, N_("permission denied: %s", ""), argv[1]);
+ else if (ret == EX_NOTFOUND)
+ krb5_warnx(context, N_("command not found: %s", ""), argv[1]);
+
+ krb5_cc_destroy(context, ccache);
+#ifndef NO_AFS
+ if (k_hasafs())
+ k_unlog();
+#endif
+ } else {
+ krb5_cc_close(context, ccache);
+ ret = 0;
+ }
+ krb5_free_principal(context, principal);
+ if (kt)
+ krb5_kt_close(context, kt);
+ krb5_free_context(context);
+ return ret;
+}
diff --git a/third_party/heimdal/kuser/klist.1 b/third_party/heimdal/kuser/klist.1
new file mode 100644
index 0000000..8ebad7d
--- /dev/null
+++ b/third_party/heimdal/kuser/klist.1
@@ -0,0 +1,135 @@
+.\" Copyright (c) 2000 - 2005 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 October 6, 2005
+.Dt KLIST 1
+.Os HEIMDAL
+.Sh NAME
+.Nm klist
+.Nd list Kerberos credentials
+.Sh SYNOPSIS
+.Nm
+.Bk -words
+.Oo Fl c Ar cache \*(Ba Xo
+.Fl Fl cache= Ns Ar cache
+.Xc
+.Oc
+.Op Fl s | Fl t | Fl Fl test
+.Op Fl T | Fl Fl tokens
+.Op Fl 5 | Fl Fl v5
+.Op Fl v | Fl Fl verbose
+.Op Fl l | Fl Fl list-caches
+.Op Fl f
+.Op Fl Fl version
+.Op Fl Fl help
+.Ek
+.Sh DESCRIPTION
+.Nm
+reads and displays the current tickets in the credential cache (also
+known as the ticket file).
+.Pp
+Options supported:
+.Bl -tag -width Ds
+.It Fl c Ar cache , Fl Fl cache= Ns Ar cache
+credential cache to list
+.It Fl s , Fl t , Fl Fl test
+Test for there being an active and valid TGT for the local realm of
+the user in the credential cache.
+.It Fl T , Fl Fl tokens
+display AFS tokens
+.It Fl 5 , Fl Fl v5
+display v5 cred cache (this is the default)
+.It Fl f
+Include ticket flags in short form, each character stands for a
+specific flag, as follows:
+.Bl -tag -width XXX -compact -offset indent
+.It F
+forwardable
+.It f
+forwarded
+.It P
+proxiable
+.It p
+proxied
+.It D
+postdate-able
+.It d
+postdated
+.It R
+renewable
+.It I
+initial
+.It i
+invalid
+.It A
+pre-authenticated
+.It H
+hardware authenticated
+.El
+.Pp
+This information is also output with the
+.Fl Fl verbose
+option, but in a more verbose way.
+.It Fl v , Fl Fl verbose
+Verbose output. Include all possible information:
+.Bl -tag -width XXXX -offset indent
+.It Server
+the principal the ticket is for
+.It Ticket etype
+the encryption type used in the ticket, followed by the key version of
+the ticket, if it is available
+.It Session key
+the encryption type of the session key, if it's different from the
+encryption type of the ticket
+.It Auth time
+the time the authentication exchange took place
+.It Start time
+the time that this ticket is valid from (only printed if it's
+different from the auth time)
+.It End time
+when the ticket expires, if it has already expired this is also noted
+.It Renew till
+the maximum possible end time of any ticket derived from this one
+.It Ticket flags
+the flags set on the ticket
+.It Addresses
+the set of addresses from which this ticket is valid
+.El
+.It Fl l , Fl Fl list-caches
+List the credential caches for the current users, not all cache types
+supports listing multiple caches.
+.Pp
+.El
+.Sh SEE ALSO
+.Xr kdestroy 1 ,
+.Xr kinit 1
diff --git a/third_party/heimdal/kuser/klist.c b/third_party/heimdal/kuser/klist.c
new file mode 100644
index 0000000..f0a4382
--- /dev/null
+++ b/third_party/heimdal/kuser/klist.c
@@ -0,0 +1,988 @@
+/*
+ * Copyright (c) 1997-2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2009 Apple 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:
+ *
+ * 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.
+ */
+
+#include "kuser_locl.h"
+#include "parse_units.h"
+#include "heimtools-commands.h"
+#undef HC_DEPRECATED_CRYPTO
+
+static const char *
+printable_time_internal(time_t t, int x)
+{
+ static char s[128];
+ char *p;
+
+ if ((p = ctime(&t)) == NULL)
+ strlcpy(s, "?", sizeof(s));
+ else
+ strlcpy(s, p + 4, sizeof(s));
+ s[x] = 0;
+ return s;
+}
+
+static const char *
+printable_time(time_t t)
+{
+ return printable_time_internal(t, 20);
+}
+
+static const char *
+printable_time_long(time_t t)
+{
+ return printable_time_internal(t, 20);
+}
+
+#define COL_ISSUED NP_(" Issued","")
+#define COL_EXPIRES NP_(" Expires", "")
+#define COL_FLAGS NP_("Flags", "")
+#define COL_NAME NP_(" Name", "")
+#define COL_PRINCIPAL NP_(" Principal", "in klist output")
+#define COL_PRINCIPAL_KVNO NP_(" Principal (kvno)", "in klist output")
+#define COL_CACHENAME NP_(" Cache name", "name in klist output")
+#define COL_DEFCACHE NP_("", "")
+
+static void
+print_cred(krb5_context context, krb5_creds *cred, rtbl_t ct, int do_flags)
+{
+ char *str;
+ krb5_error_code ret;
+ krb5_timestamp sec;
+
+ krb5_timeofday (context, &sec);
+
+
+ if(cred->times.starttime)
+ rtbl_add_column_entry(ct, COL_ISSUED,
+ printable_time(cred->times.starttime));
+ else
+ rtbl_add_column_entry(ct, COL_ISSUED,
+ printable_time(cred->times.authtime));
+
+ if(cred->times.endtime > sec)
+ rtbl_add_column_entry(ct, COL_EXPIRES,
+ printable_time(cred->times.endtime));
+ else
+ rtbl_add_column_entry(ct, COL_EXPIRES, N_(">>>Expired<<<", ""));
+ ret = krb5_unparse_name (context, cred->server, &str);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_unparse_name");
+ rtbl_add_column_entry(ct, COL_PRINCIPAL, str);
+ if(do_flags) {
+ char s[16], *sp = s;
+ if(cred->flags.b.forwardable)
+ *sp++ = 'F';
+ if(cred->flags.b.forwarded)
+ *sp++ = 'f';
+ if(cred->flags.b.proxiable)
+ *sp++ = 'P';
+ if(cred->flags.b.proxy)
+ *sp++ = 'p';
+ if(cred->flags.b.may_postdate)
+ *sp++ = 'D';
+ if(cred->flags.b.postdated)
+ *sp++ = 'd';
+ if(cred->flags.b.renewable)
+ *sp++ = 'R';
+ if(cred->flags.b.initial)
+ *sp++ = 'I';
+ if(cred->flags.b.invalid)
+ *sp++ = 'i';
+ if(cred->flags.b.pre_authent)
+ *sp++ = 'A';
+ if(cred->flags.b.hw_authent)
+ *sp++ = 'H';
+ if(cred->flags.b.transited_policy_checked)
+ *sp++ = 'T';
+ if(cred->flags.b.ok_as_delegate)
+ *sp++ = 'O';
+ if(cred->flags.b.anonymous)
+ *sp++ = 'a';
+ *sp = '\0';
+ rtbl_add_column_entry(ct, COL_FLAGS, s);
+ }
+ free(str);
+}
+
+static void
+cred2json(krb5_context context, krb5_creds *cred, heim_array_t tix)
+{
+ 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;
+
+ 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);
+ if(ret)
+ exit(1);
+ printf(N_("Server: %s\n", ""), str);
+ free (str);
+
+ ret = krb5_unparse_name(context, cred->client, &str);
+ if(ret)
+ exit(1);
+ printf(N_("Client: %s\n", ""), str);
+ free (str);
+
+ if (krb5_is_config_principal(context, cred->server)) {
+ if (krb5_principal_get_num_comp(context, cred->server) > 1) {
+ const char *s;
+
+ /* If the payload is text and not secret/sensitive, print it */
+ s = krb5_principal_get_comp_string(context, cred->server, 1);
+ if (strcmp(s, "start_realm") == 0 ||
+ strcmp(s, "anon_pkinit_realm") == 0 ||
+ strcmp(s, "default-ntlm-domain") == 0 ||
+ strcmp(s, "FriendlyName") == 0 ||
+ strcmp(s, "fast_avail") == 0 ||
+ strcmp(s, "kx509store") == 0 ||
+ strcmp(s, "kx509_service_realm") == 0 ||
+ strcmp(s, "kx509_service_status") == 0)
+ printf(N_("Configuration item payload: %.*s\n", ""),
+ (int)cred->ticket.length,
+ (const char *)cred->ticket.data);
+ else
+ printf(N_("Configuration item payload length: %lu\n", ""),
+ (unsigned long)cred->ticket.length);
+ } /* else... this is a meaningless entry; nothing would create it */
+ } else {
+ Ticket t;
+ size_t len;
+ char *s;
+
+ decode_Ticket(cred->ticket.data, cred->ticket.length, &t, &len);
+ ret = krb5_enctype_to_string(context, t.enc_part.etype, &s);
+ printf(N_("Ticket etype: ", ""));
+ if (ret == 0) {
+ printf("%s", s);
+ free(s);
+ } else {
+ printf(N_("unknown-enctype(%d)", ""), t.enc_part.etype);
+ }
+ if(t.enc_part.kvno)
+ printf(N_(", kvno %d", ""), *t.enc_part.kvno);
+ printf("\n");
+ if(cred->session.keytype != t.enc_part.etype) {
+ ret = krb5_enctype_to_string(context, cred->session.keytype, &str);
+ if(ret)
+ krb5_warn(context, ret, "session keytype");
+ else {
+ printf(N_("Session key: %s\n", "enctype"), str);
+ free(str);
+ }
+ }
+ free_Ticket(&t);
+ printf(N_("Ticket length: %lu\n", ""),
+ (unsigned long)cred->ticket.length);
+ printf(N_("Auth time: %s\n", ""),
+ printable_time_long(cred->times.authtime));
+ if(cred->times.authtime != cred->times.starttime)
+ printf(N_("Start time: %s\n", ""),
+ printable_time_long(cred->times.starttime));
+ printf(N_("End time: %s", ""),
+ printable_time_long(cred->times.endtime));
+ if(sec > cred->times.endtime)
+ printf(N_(" (expired)", ""));
+ printf("\n");
+ if(cred->flags.b.renewable)
+ printf(N_("Renew till: %s\n", ""),
+ printable_time_long(cred->times.renew_till));
+ {
+ char flags[1024];
+ int result = unparse_flags(TicketFlags2int(cred->flags.b),
+ asn1_TicketFlags_units(),
+ flags, sizeof(flags));
+ if (result > 0) {
+ printf(N_("Ticket flags: %s\n", ""), flags);
+ }
+ }
+ printf(N_("Addresses: ", ""));
+ if (cred->addresses.len != 0) {
+ for(j = 0; j < cred->addresses.len; j++){
+ char buf[128];
+ if(j) printf(", ");
+ ret = krb5_print_address(&cred->addresses.val[j],
+ buf, sizeof(buf), &len);
+
+ if(ret == 0)
+ printf("%s", buf);
+ }
+ } else {
+ printf(N_("addressless", ""));
+ }
+ }
+ 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.
+ */
+
+static void
+print_tickets(krb5_context context,
+ krb5_ccache ccache,
+ krb5_principal principal,
+ int do_verbose,
+ int do_flags,
+ int do_hidden)
+{
+ char *str, *name, *fullname;
+ krb5_error_code ret;
+ krb5_cc_cursor cursor;
+ krb5_creds creds;
+ krb5_deltat sec;
+ rtbl_t ct = NULL;
+
+ 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");
+
+ 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);
+ }
+
+ 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);
+ if (ret == 0) {
+ if (do_verbose && sec != 0) {
+
+ char buf[BUFSIZ];
+ int val;
+ int sig;
+
+ val = (int)sec;
+ sig = 1;
+ if (val < 0) {
+ sig = -1;
+ val = -val;
+ }
+
+ unparse_time (val, buf, sizeof(buf));
+
+ printf ("%17s: %s%s\n", N_("KDC time offset", ""),
+ sig == -1 ? "-" : "", buf);
+ }
+ }
+ 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) {
+ ct = rtbl_create();
+ rtbl_add_column(ct, COL_ISSUED, 0);
+ rtbl_add_column(ct, COL_EXPIRES, 0);
+ if(do_flags)
+ rtbl_add_column(ct, COL_FLAGS, 0);
+ rtbl_add_column(ct, COL_PRINCIPAL, 0);
+ rtbl_set_separator(ct, " ");
+ }
+ 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) {
+ print_cred_verbose(context, &creds);
+ } else {
+ print_cred(context, &creds, ct, do_flags);
+ }
+ 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");
+
+ if(!do_verbose) {
+ rtbl_format(ct, stdout);
+ rtbl_destroy(ct);
+ }
+ free(fullname);
+}
+
+/*
+ * Check if there's a tgt for the realm of `principal' and ccache and
+ * if so return 0, else 1
+ */
+
+static int
+check_expiration(krb5_context context,
+ krb5_ccache ccache,
+ time_t *expiration)
+{
+ krb5_error_code ret;
+ time_t t;
+
+ ret = krb5_cc_get_lifetime(context, ccache, &t);
+ if (ret || t == 0)
+ return 1;
+
+ if (expiration)
+ *expiration = time(NULL) + t;
+
+ return 0;
+}
+
+/*
+ * Print a list of all AFS tokens
+ */
+
+#ifndef NO_AFS
+
+static void
+display_tokens(int do_verbose)
+{
+ uint32_t i;
+ unsigned char t[4096];
+ struct ViceIoctl parms;
+
+ parms.in = (void *)&i;
+ parms.in_size = sizeof(i);
+ parms.out = (void *)t;
+ parms.out_size = sizeof(t);
+
+ for (i = 0;; i++) {
+ int32_t size_secret_tok, size_public_tok;
+ unsigned char *cell;
+ struct ClearToken ct;
+ unsigned char *r = t;
+ struct timeval tv;
+ char buf1[20], buf2[20];
+
+ if(k_pioctl(NULL, VIOCGETTOK, &parms, 0) < 0) {
+ if(errno == EDOM)
+ break;
+ continue;
+ }
+ if(parms.out_size > sizeof(t))
+ continue;
+ if(parms.out_size < sizeof(size_secret_tok))
+ continue;
+ t[min(parms.out_size,sizeof(t)-1)] = 0;
+ memcpy(&size_secret_tok, r, sizeof(size_secret_tok));
+ /* don't bother about the secret token */
+ r += size_secret_tok + sizeof(size_secret_tok);
+ if (parms.out_size < (r - t) + sizeof(size_public_tok))
+ continue;
+ memcpy(&size_public_tok, r, sizeof(size_public_tok));
+ r += sizeof(size_public_tok);
+ if (parms.out_size < (r - t) + size_public_tok + sizeof(int32_t))
+ continue;
+ memcpy(&ct, r, size_public_tok);
+ r += size_public_tok;
+ /* there is a int32_t with length of cellname, but we don't read it */
+ r += sizeof(int32_t);
+ cell = r;
+
+ gettimeofday (&tv, NULL);
+ strlcpy (buf1, printable_time(ct.BeginTimestamp),
+ sizeof(buf1));
+ if (do_verbose || tv.tv_sec < ct.EndTimestamp)
+ strlcpy (buf2, printable_time(ct.EndTimestamp),
+ sizeof(buf2));
+ else
+ strlcpy (buf2, N_(">>> Expired <<<", ""), sizeof(buf2));
+
+ printf("%s %s ", buf1, buf2);
+
+ if ((ct.EndTimestamp - ct.BeginTimestamp) & 1)
+ printf(N_("User's (AFS ID %d) tokens for %s", ""), ct.ViceId, cell);
+ else
+ printf(N_("Tokens for %s", ""), cell);
+ if (do_verbose)
+ printf(" (%d)", ct.AuthHandle);
+ putchar('\n');
+ }
+}
+#endif
+
+/*
+ * display the ccache in `cred_cache'
+ */
+
+static int
+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;
+ int exit_status = 0;
+
+
+ ret = krb5_cc_get_principal (context, ccache, &principal);
+ if (ret) {
+ if (dict)
+ return 0;
+ if(ret == ENOENT) {
+ if (!do_test)
+ krb5_warnx(context, N_("No ticket file: %s", ""),
+ krb5_cc_get_name(context, ccache));
+ return 1;
+ } else
+ krb5_err (context, 1, ret, "krb5_cc_get_principal");
+ }
+ 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)
+ krb5_err (context, 1, ret, "krb5_cc_close");
+
+ krb5_free_principal (context, principal);
+
+ 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;
+}
+
+/*
+ *
+ */
+
+static int
+list_caches(krb5_context context, struct klist_options *opt)
+{
+ krb5_cccol_cursor cursor;
+ const char *cdef_name = krb5_cc_default_name(context);
+ char *def_name;
+ krb5_error_code ret;
+ krb5_ccache id;
+ rtbl_t ct;
+
+ 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");
+
+ ct = rtbl_create();
+ rtbl_add_column(ct, COL_DEFCACHE, 0);
+ rtbl_add_column(ct, COL_NAME, 0);
+ rtbl_add_column(ct, COL_CACHENAME, 0);
+ rtbl_add_column(ct, COL_EXPIRES, 0);
+ rtbl_add_column(ct, COL_DEFCACHE, 0);
+ rtbl_set_prefix(ct, " ");
+ rtbl_set_column_prefix(ct, COL_DEFCACHE, "");
+ rtbl_set_column_prefix(ct, COL_NAME, " ");
+
+ while (krb5_cccol_cursor_next(context, cursor, &id) == 0 && id != NULL) {
+ 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) {
+ const char *str;
+ char *fname;
+
+ rtbl_add_column_entry(ct, COL_NAME, name);
+ free(name);
+
+ if (expired)
+ str = N_(">>> Expired <<<", "");
+ else
+ str = printable_time(t);
+ rtbl_add_column_entry(ct, COL_EXPIRES, str);
+
+ ret = krb5_cc_get_full_name(context, id, &fname);
+ if (ret)
+ krb5_err (context, 1, ret, "krb5_cc_get_full_name");
+
+ rtbl_add_column_entry(ct, COL_CACHENAME, fname);
+ if (def_name && strcmp(fname, def_name) == 0)
+ rtbl_add_column_entry(ct, COL_DEFCACHE, "*");
+ else
+ rtbl_add_column_entry(ct, COL_DEFCACHE, "");
+
+ krb5_xfree(fname);
+ }
+ krb5_cc_close(context, id);
+ }
+
+ krb5_cccol_cursor_free(context, &cursor);
+
+ free(def_name);
+ rtbl_format(ct, stdout);
+ rtbl_destroy(ct);
+
+ return 0;
+}
+
+/*
+ *
+ */
+
+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 =
+ opt->verbose_flag ||
+ opt->a_flag ||
+ opt->n_flag;
+ int do_test =
+ opt->test_flag ||
+ opt->s_flag;
+
+ if(opt->version_flag) {
+ print_version(NULL);
+ exit(0);
+ }
+
+ if (opt->list_all_flag) {
+ if (opt->json_flag)
+ exit_status = caches2json(heimtools_context);
+ else
+ exit_status = list_caches(heimtools_context, opt);
+ return exit_status;
+ }
+
+ if (opt->v5_flag) {
+ krb5_ccache id;
+
+ if (opt->all_content_flag) {
+ heim_array_t a = opt->json_flag ? heim_array_create() : NULL;
+ krb5_cc_cache_cursor cursor;
+
+ ret = krb5_cc_cache_get_first(heimtools_context, NULL, &cursor);
+ if (ret)
+ krb5_err(heimtools_context, 1, ret, "krb5_cc_cache_get_first");
+
+ while (krb5_cc_cache_next(heimtools_context, cursor, &id) == 0) {
+ 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,
+ dict);
+ if (a)
+ heim_array_append_value(a, dict);
+ heim_release(dict);
+ }
+ krb5_cc_cache_end_seq_get(heimtools_context, cursor);
+ 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)
+ krb5_err(heimtools_context, 1, ret, "%s", opt->cache_string);
+ } else {
+ ret = krb5_cc_default(heimtools_context, &id);
+ if (ret)
+ krb5_err(heimtools_context, 1, ret, "krb5_cc_resolve");
+ }
+ exit_status = display_v5_ccache(heimtools_context, id, do_test,
+ do_verbose, opt->flags_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()) {
+ if (opt->v5_flag)
+ printf("\n");
+ display_tokens(opt->verbose_flag);
+ }
+#endif
+ }
+
+ return exit_status;
+}
diff --git a/third_party/heimdal/kuser/kswitch.1 b/third_party/heimdal/kuser/kswitch.1
new file mode 100644
index 0000000..c41fe9a
--- /dev/null
+++ b/third_party/heimdal/kuser/kswitch.1
@@ -0,0 +1,85 @@
+.\" Copyright (c) 2009 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.
+.\"
+.Dd August 25, 2009
+.Dt KSWITCH 1
+.Os HEIMDAL
+.Sh NAME
+.Nm kswitch
+.Nd switch between default credential caches
+.Sh SYNOPSIS
+.Nm
+.Oo Fl t Ar type \*(Ba Xo
+.Fl Fl type= Ns Ar type
+.Xc
+.Oc
+.Oo Fl c Ar cache \*(Ba Xo
+.Fl Fl cache= Ns Ar cache
+.Xc
+.Oc
+.Oo Fl p Ar principal \*(Ba Xo
+.Fl Fl principal= Ns Ar principal
+.Xc
+.Oc
+.Op Fl i | Fl Fl interactive
+.Op Fl Fl version
+.Op Fl Fl help
+.Sh DESCRIPTION
+Supported options:
+.Bl -tag -width Ds
+.It Xo
+.Fl t Ar type ,
+.Fl Fl type= Ns Ar type
+.Xc
+type of credential cache
+.It Xo
+.Fl c Ar cache ,
+.Fl Fl cache= Ns Ar cache
+.Xc
+name of credential cache to switch to
+.It Xo
+.Fl p Ar principal ,
+.Fl Fl principal= Ns Ar principal
+.Xc
+name of principal to switch to
+.It Xo
+.Fl i ,
+.Fl Fl interactive
+.Xc
+interactive switching between credentials.
+.It Xo
+.Fl Fl version
+.Xc
+print version
+.It Xo
+.Fl Fl help
+.Xc
+.El
diff --git a/third_party/heimdal/kuser/kswitch.c b/third_party/heimdal/kuser/kswitch.c
new file mode 100644
index 0000000..3bb3b70
--- /dev/null
+++ b/third_party/heimdal/kuser/kswitch.c
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2008 - 2010 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.
+ */
+
+#include "kuser_locl.h"
+#include "heimtools-commands.h"
+
+#ifdef HAVE_READLINE
+char *readline(const char *prompt);
+#else
+
+static char *
+readline(const char *prompt)
+{
+ char buf[BUFSIZ];
+ printf ("%s", prompt);
+ fflush (stdout);
+ if(fgets(buf, sizeof(buf), stdin) == NULL)
+ return NULL;
+ buf[strcspn(buf, "\r\n")] = '\0';
+ return strdup(buf);
+}
+
+#endif
+
+/*
+ *
+ */
+
+int
+kswitch(struct kswitch_options *opt, int argc, char **argv)
+{
+ krb5_error_code ret;
+ krb5_ccache id = NULL;
+
+ if (opt->cache_string && opt->principal_string)
+ krb5_errx(heimtools_context, 1,
+ N_("Both --cache and --principal given, choose one", ""));
+
+ if (opt->interactive_flag) {
+ krb5_cc_cache_cursor cursor;
+ krb5_ccache *ids = NULL;
+ size_t i, len = 0;
+ char *name;
+ rtbl_t ct;
+
+ ct = rtbl_create();
+
+ rtbl_add_column_by_id(ct, 0, "#", 0);
+ rtbl_add_column_by_id(ct, 1, "Principal", 0);
+ rtbl_set_column_affix_by_id(ct, 1, " ", "");
+ rtbl_add_column_by_id(ct, 2, "Type", 0);
+ rtbl_set_column_affix_by_id(ct, 2, " ", "");
+
+ ret = krb5_cc_cache_get_first(heimtools_context, NULL, &cursor);
+ if (ret)
+ krb5_err(heimtools_context, 1, ret, "krb5_cc_cache_get_first");
+
+ while (krb5_cc_cache_next(heimtools_context, cursor, &id) == 0) {
+ krb5_principal p = NULL;
+ char num[10];
+
+ ret = krb5_cc_get_principal(heimtools_context, id, &p);
+ if (ret == 0)
+ ret = krb5_unparse_name(heimtools_context, p, &name);
+ if (ret) {
+ krb5_cc_close(heimtools_context, id);
+ continue;
+ }
+
+ krb5_free_principal(heimtools_context, p);
+
+ snprintf(num, sizeof(num), "%d", (int)(len + 1));
+ rtbl_add_column_entry_by_id(ct, 0, num);
+ rtbl_add_column_entry_by_id(ct, 1, name);
+ rtbl_add_column_entry_by_id(ct, 2, krb5_cc_get_type(heimtools_context, id));
+ free(name);
+
+ ids = erealloc(ids, (len + 1) * sizeof(ids[0]));
+ ids[len] = id;
+ len++;
+ }
+ krb5_cc_cache_end_seq_get(heimtools_context, cursor);
+
+ rtbl_format(ct, stdout);
+ rtbl_destroy(ct);
+
+ name = readline("Select number: ");
+ if (name) {
+ i = atoi(name);
+ if (i == 0)
+ krb5_errx(heimtools_context, 1, "Cache number '%s' is invalid", name);
+ if (i > len)
+ krb5_errx(heimtools_context, 1, "Cache number '%s' is too large", name);
+
+ id = ids[i - 1];
+ ids[i - 1] = NULL;
+ free(name);
+ } else
+ krb5_errx(heimtools_context, 1, "No cache selected");
+ for (i = 0; i < len; i++)
+ if (ids[i])
+ krb5_cc_close(heimtools_context, ids[i]);
+ free(ids);
+ } else if (opt->principal_string) {
+ krb5_principal p;
+
+ ret = krb5_parse_name(heimtools_context, opt->principal_string, &p);
+ if (ret)
+ krb5_err(heimtools_context, 1, ret, "krb5_parse_name: %s",
+ opt->principal_string);
+
+ ret = krb5_cc_cache_match(heimtools_context, p, &id);
+ if (ret)
+ krb5_err(heimtools_context, 1, ret,
+ N_("Did not find principal: %s", ""),
+ opt->principal_string);
+
+ krb5_free_principal(heimtools_context, p);
+
+ } else if (opt->cache_string) {
+ const krb5_cc_ops *ops;
+ char *str;
+ int aret;
+
+ ops = krb5_cc_get_prefix_ops(heimtools_context, opt->type_string);
+ if (ops == NULL)
+ krb5_err(heimtools_context, 1, 0, "krb5_cc_get_prefix_ops");
+
+ aret = asprintf(&str, "%s:%s", ops->prefix, opt->cache_string);
+ if (aret == -1)
+ krb5_errx(heimtools_context, 1, N_("out of memory", ""));
+
+ ret = krb5_cc_resolve(heimtools_context, str, &id);
+ if (ret)
+ krb5_err(heimtools_context, 1, ret, "krb5_cc_resolve: %s", str);
+
+ free(str);
+ } else {
+ krb5_errx(heimtools_context, 1, "missing option for kswitch");
+ }
+
+ ret = krb5_cc_switch(heimtools_context, id);
+ if (ret)
+ krb5_err(heimtools_context, 1, ret, "krb5_cc_switch");
+
+ krb5_cc_close(heimtools_context, id);
+
+ return 0;
+}
diff --git a/third_party/heimdal/kuser/kuser_locl.h b/third_party/heimdal/kuser/kuser_locl.h
new file mode 100644
index 0000000..b1a097a
--- /dev/null
+++ b/third_party/heimdal/kuser/kuser_locl.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 1997 - 1999 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 __KUSER_LOCL_H__
+#define __KUSER_LOCL_H__
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETINET_IN6_H
+#include <netinet/in6.h>
+#endif
+#ifdef HAVE_NETINET6_IN6_H
+#include <netinet6/in6.h>
+#endif
+
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#include <roken.h>
+#include <getarg.h>
+#include <parse_time.h>
+#include <err.h>
+#include <krb5.h>
+#include <heimbase.h>
+
+#include <gssapi_mech.h>
+#include <gss-preauth-protos.h>
+#include <gss-preauth-private.h>
+
+#if defined(HAVE_SYS_IOCTL_H) && SunOS != 40
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_SYS_IOCCOM_H
+#include <sys/ioccom.h>
+#endif
+#ifndef NO_AFS
+#include <kafs.h>
+#endif
+#include "crypto-headers.h" /* for UI_UTIL_read_pw_string */
+
+#include <rtbl.h>
+
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#endif
+
+#ifdef LIBINTL
+#include <libintl.h>
+#undef N_
+#define N_(x,y) gettext(x)
+#undef NP_
+#define NP_(x,y) (x)
+#define getarg_i18n gettext
+#else
+#undef N_
+#define N_(x,y) (x)
+#undef NP_
+#define NP_(x,y) (x)
+#define getarg_i18n NULL
+#define bindtextdomain(package, localedir)
+#define textdomain(package)
+#endif
+
+extern krb5_context heimtools_context;
+
+#endif /* __KUSER_LOCL_H__ */
diff --git a/third_party/heimdal/kuser/kverify.c b/third_party/heimdal/kuser/kverify.c
new file mode 100644
index 0000000..83b3b00
--- /dev/null
+++ b/third_party/heimdal/kuser/kverify.c
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 1997 - 2005, 2007 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.
+ */
+
+#include "kuser_locl.h"
+
+static int help_flag = 0;
+static int version_flag = 0;
+
+static struct getargs args[] = {
+ { "version", 0, arg_flag, &version_flag, NULL, NULL },
+ { "help", 0, arg_flag, &help_flag, NULL, NULL }
+};
+
+static void
+usage (int ret)
+{
+ arg_printusage (args,
+ sizeof(args)/sizeof(*args),
+ NULL,
+ "[principal]");
+ exit (ret);
+}
+
+int
+main(int argc, char **argv)
+{
+ krb5_context context;
+ krb5_error_code ret;
+ krb5_creds cred;
+ krb5_preauthtype pre_auth_types[] = {KRB5_PADATA_ENC_TIMESTAMP};
+ krb5_get_init_creds_opt *get_options;
+ krb5_verify_init_creds_opt verify_options;
+ krb5_principal principal = NULL;
+ int optidx = 0;
+
+ setprogname (argv[0]);
+
+ if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
+ usage(1);
+
+ if (help_flag)
+ usage (0);
+
+ if(version_flag) {
+ print_version(NULL);
+ exit(0);
+ }
+
+ argc -= optidx;
+ argv += optidx;
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ errx (1, "krb5_init_context failed: %d", ret);
+
+ ret = krb5_get_init_creds_opt_alloc (context, &get_options);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_get_init_creds_opt_alloc");
+
+ krb5_get_init_creds_opt_set_preauth_list (get_options,
+ pre_auth_types,
+ 1);
+
+ krb5_verify_init_creds_opt_init (&verify_options);
+
+ if (argc) {
+ ret = krb5_parse_name(context, argv[0], &principal);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_parse_name: %s", argv[0]);
+ } else {
+ ret = krb5_get_default_principal(context, &principal);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_get_default_principal");
+
+ }
+
+ ret = krb5_get_init_creds_password (context,
+ &cred,
+ principal,
+ NULL,
+ krb5_prompter_posix,
+ NULL,
+ 0,
+ NULL,
+ get_options);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_get_init_creds");
+
+ ret = krb5_verify_init_creds (context,
+ &cred,
+ NULL,
+ NULL,
+ NULL,
+ &verify_options);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_verify_init_creds");
+ krb5_free_cred_contents (context, &cred);
+ krb5_free_context (context);
+ return 0;
+}
diff --git a/third_party/heimdal/kuser/kvno.c b/third_party/heimdal/kuser/kvno.c
new file mode 100644
index 0000000..7ddf2a2
--- /dev/null
+++ b/third_party/heimdal/kuser/kvno.c
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 1998 by the FundsXpress, INC.
+ *
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may require
+ * a specific license from the United States Government. It is the
+ * responsibility of any person or organization contemplating export to
+ * obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of FundsXpress. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. FundsXpress makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include "kuser_locl.h"
+
+static char *etype_str = NULL;
+static char *ccache_name = NULL;
+static char *keytab_name = NULL;
+static char *sname = NULL;
+
+static int version_flag = 0;
+static int help_flag = 0;
+static int quiet_flag = 0;
+
+static void do_v5_kvno (int argc, char *argv[],
+ char *ccache_name, char *etype_str, char *keytab_name,
+ char *sname);
+
+struct getargs args[] = {
+ { "enctype", 'e', arg_string, &etype_str,
+ NP_("Encryption type to use", ""), "enctype" },
+ { "cache", 'c', arg_string, &ccache_name,
+ NP_("Credentials cache", ""), "cachename" },
+ { "keytab", 'k', arg_string, &keytab_name,
+ NP_("Keytab to use", ""), "keytabname" },
+ { "server", 'S', arg_string, &sname,
+ NP_("Server to get ticket for", ""), "principal" },
+ { "quiet", 'q', arg_flag, &quiet_flag,
+ NP_("Quiet", "") },
+ { "version", 0, arg_flag, &version_flag },
+ { "help", 0, arg_flag, &help_flag }
+};
+
+static void
+usage(int ret)
+{
+ arg_printusage_i18n (args, sizeof(args)/sizeof(*args),
+ N_("Usage: ", ""), NULL,
+ "principal1 [principal2 ...]",
+ getarg_i18n);
+ exit (ret);
+}
+
+int main(int argc, char *argv[])
+{
+ int optidx = 0;
+
+ setprogname (argv[0]);
+
+ setlocale(LC_ALL, "");
+ bindtextdomain ("heimdal_kuser", HEIMDAL_LOCALEDIR);
+ textdomain("heimdal_kuser");
+
+ if (getarg(args, sizeof(args)/sizeof(args[0]), argc, argv, &optidx))
+ usage(1);
+
+ if (help_flag)
+ usage (0);
+
+ if (version_flag) {
+ print_version(NULL);
+ exit (0);
+ }
+
+ argc -= optidx;
+ argv += optidx;
+
+ do_v5_kvno(argc, argv, ccache_name, etype_str, keytab_name, sname);
+
+ return 0;
+}
+
+static void do_v5_kvno (int count, char *names[],
+ char * ccache_name, char *etype_str, char *keytab_name,
+ char *sname)
+{
+ krb5_error_code ret;
+ krb5_context context = 0;
+ int i, errors;
+ krb5_enctype etype;
+ krb5_ccache ccache;
+ krb5_principal me;
+ krb5_creds in_creds, *out_creds = NULL;
+ Ticket ticket;
+ size_t len;
+ char *princ = NULL;
+ krb5_keytab keytab = NULL;
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ errx(1, "krb5_init_context failed: %d", ret);
+
+ if (etype_str) {
+ ret = krb5_string_to_enctype(context, etype_str, &etype);
+ if (ret)
+ krb5_err(context, 1, ret, "Failed to convert encryption type %s", etype_str);
+ } else {
+ etype = 0;
+ }
+
+ if (ccache_name)
+ ret = krb5_cc_resolve(context, ccache_name, &ccache);
+ else
+ ret = krb5_cc_default(context, &ccache);
+ if (ret)
+ krb5_err(context, 1, ret, "Failed to open credentials cache %s",
+ (ccache_name) ? ccache_name : "(Default)");
+
+ if (keytab_name) {
+ ret = krb5_kt_resolve(context, keytab_name, &keytab);
+ if (ret)
+ krb5_err(context, 1, ret, "Can't resolve keytab %s", keytab_name);
+ }
+
+ ret = krb5_cc_get_principal(context, ccache, &me);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_get_principal");
+
+ errors = 0;
+
+ for (i = 0; i < count; i++) {
+ memset(&in_creds, 0, sizeof(in_creds));
+ memset(&ticket, 0, sizeof(ticket));
+
+ in_creds.client = me;
+
+ if (sname != NULL) {
+ ret = krb5_sname_to_principal(context, names[i],
+ sname, KRB5_NT_SRV_HST,
+ &in_creds.server);
+ } else {
+ ret = krb5_parse_name(context, names[i], &in_creds.server);
+ }
+ if (ret) {
+ if (!quiet_flag)
+ krb5_warn(context, ret, "Couldn't parse principal name %s", names[i]);
+ errors++;
+ continue;
+ }
+
+ ret = krb5_unparse_name(context, in_creds.server, &princ);
+ if (ret) {
+ krb5_warn(context, ret, "Couldn't format parsed principal name for '%s'",
+ names[i]);
+ errors++;
+ goto next;
+ }
+
+ in_creds.session.keytype = etype;
+
+ ret = krb5_get_credentials(context, 0, ccache, &in_creds, &out_creds);
+
+ if (ret) {
+ krb5_warn(context, ret, "Couldn't get credentials for %s", princ);
+ errors++;
+ goto next;
+ }
+
+ ret = decode_Ticket(out_creds->ticket.data, out_creds->ticket.length,
+ &ticket, &len);
+ if (ret) {
+ krb5_err(context, 1, ret, "Can't decode ticket for %s", princ);
+ errors++;
+ goto next;
+ continue;
+ }
+
+ if (keytab) {
+ krb5_keytab_entry kte;
+ krb5_crypto crypto;
+ krb5_data dec_data;
+ EncTicketPart decr_part;
+
+ ret = krb5_kt_get_entry(context, keytab, in_creds.server,
+ (ticket.enc_part.kvno != NULL)?
+ *ticket.enc_part.kvno : 0,
+ ticket.enc_part.etype,
+ &kte);
+ if (ret) {
+ krb5_warn(context, ret, "Can't decrypt ticket for %s", princ);
+ if (!quiet_flag)
+ printf("%s: kvno = %d, keytab entry invalid", princ,
+ (ticket.enc_part.kvno != NULL)?
+ *ticket.enc_part.kvno : 0);
+ errors ++;
+ goto next;
+ }
+
+ ret = krb5_crypto_init(context, &kte.keyblock, 0, &crypto);
+ if (ret) {
+ krb5_warn(context, ret, "krb5_crypto_init");
+ errors ++;
+ krb5_kt_free_entry(context, &kte);
+ goto next;
+ }
+
+ ret = krb5_decrypt_EncryptedData (context, crypto, KRB5_KU_TICKET,
+ &ticket.enc_part, &dec_data);
+ krb5_crypto_destroy(context, crypto);
+ krb5_kt_free_entry(context, &kte);
+
+ if (ret) {
+ krb5_warn(context, ret, "krb5_decrypt_EncryptedData");
+ errors ++;
+ goto next;
+ }
+
+ ret = decode_EncTicketPart(dec_data.data, dec_data.length,
+ &decr_part, &len);
+ krb5_data_free(&dec_data);
+ if (ret) {
+ krb5_warn(context, ret, "decode_EncTicketPart");
+ errors ++;
+ goto next;
+ }
+
+ if (!quiet_flag)
+ printf("%s: kvno = %d, keytab entry valid\n", princ,
+ (ticket.enc_part.kvno != NULL)?
+ *ticket.enc_part.kvno : 0);
+
+ free_EncTicketPart(&decr_part);
+ } else {
+ if (!quiet_flag)
+ printf("%s: kvno = %d\n", princ,
+ (ticket.enc_part.kvno != NULL)? *ticket.enc_part.kvno : 0);
+ }
+
+ next:
+ if (out_creds) {
+ krb5_free_creds(context, out_creds);
+ out_creds = NULL;
+ }
+
+ if (princ) {
+ krb5_free_unparsed_name(context, princ);
+ princ = NULL;
+ }
+
+ krb5_free_principal(context, in_creds.server);
+
+ free_Ticket(&ticket);
+ }
+
+ if (keytab)
+ krb5_kt_close(context, keytab);
+ krb5_free_principal(context, me);
+ krb5_cc_close(context, ccache);
+ krb5_free_context(context);
+
+ if (errors)
+ exit(1);
+
+ exit(0);
+}
diff --git a/third_party/heimdal/kuser/kx509.1 b/third_party/heimdal/kuser/kx509.1
new file mode 100644
index 0000000..1fb3ce2
--- /dev/null
+++ b/third_party/heimdal/kuser/kx509.1
@@ -0,0 +1,133 @@
+.\" Copyright (c) 2019 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 October 6, 2005
+.Dt KLIST 1
+.Os HEIMDAL
+.Sh NAME
+.Nm kx509
+.Nd acquire or extract certificates using Kerberos credentials
+.Sh SYNOPSIS
+.Nm
+.Bk -words
+.Oo Fl c Ar cache \*(Ba Xo
+.Fl Fl cache= Ns Ar cache
+.Xc
+.Oc
+.Oo Fl s \*(Ba Xo
+.Fl Fl save
+.Xc
+.Oc
+.Oo Fl o Ar store \*(Ba Xo
+.Fl Fl out= Ns Ar store
+.Xc
+.Oc
+.Oo Fl x \*(Ba Xo
+.Fl Fl extract
+.Xc
+.Oc
+.Oo Fl t Ar time-left \*(Ba Xo
+.Fl Fl test= Ns Ar time-left
+.Xc
+.Oc
+.Oo Fl C Ar PKCS10:filename \*(Ba Xo
+.Fl Fl csr= Ns Ar PKCS10:filename
+.Xc
+.Oc
+.Oo Fl C Ar PKCS10:filename \*(Ba Xo
+.Fl Fl csr= Ns Ar PKCS10:filename
+.Xc
+.Oc
+.Oo Fl K Ar hx509-store \*(Ba Xo
+.Fl Fl private-key= Ns Ar hx509-store
+.Xc
+.Oc
+.Oo Fl r Ar realm \*(Ba Xo
+.Fl Fl realm= Ns Ar realm
+.Xc
+.Oc
+.Op Fl Fl help
+.Ek
+.Sh DESCRIPTION
+.Nm
+acquires PKIX credentials from a credential cache using the kx509
+protocol, or extracts PKIX credentials stored in a credential
+cache.
+.Pp
+Options supported:
+.Bl -tag -width Ds
+.It Fl c Ar cache , Fl Fl cache= Ns Ar cache
+credential cache to use (if not given, then the default will be
+used).
+.It Fl t Ar time-left , Fl Fl test= Ns Ar time-left
+Test for there being an active and valid certificate in the
+credential cache, with at least
+.Ar time-left
+seconds left of valid life. If given with the
+.Fl o
+then the certificates in the hx509 store are tested along with
+those in the credentials cache (if any).
+.It Fl x , Fl Fl extract
+Extract, rather than acquire credentials.
+.It Fl s , Fl Fl save
+save the acquired certificate and the private key used in the
+given credential cache.
+.It Fl o , Fl Fl out= Ns Ar hx509-store
+An hx509 store specification, such as
+.Va DER-FILE:/path/to/der/file ,
+.Va PEM-FILE:/path/to/PEM/file ,
+.Va FILE:/path/to/PEM/file ,
+or
+.Va PKCS12:/path/to/PKCS#12/file
+into which to store any PKIX certificate and private key
+(unencrypted) that may have been acquired with the kx509 protocol
+and stored in the
+.Ns Ar ccache.
+.It Fl r Ar realm, Fl Fl realm= Ns Ar realm
+specify the name of the realm whose kx509 service to use.
+.It Fl K Ar store, Fl Fl private-key= Ns Ar store
+use the private key from the given hx509 store for requesting a
+certificate.
+.It Fl C Ar csr, Fl Fl csr= Ns Ar certificate-request
+specify a CSR to use, which must be a string of the form
+PKCS10:filename and which must contain the DER encoding of a
+PKCS#10 certification request.
+.El
+.Pp
+The
+.Nm hxtool(1)
+command can be used to create private keys and CSRs.
+.Sh SEE ALSO
+.Xr kdestroy 1 ,
+.Xr kinit 1 ,
+.Xr hxtool 1
diff --git a/third_party/heimdal/kuser/kx509.c b/third_party/heimdal/kuser/kx509.c
new file mode 100644
index 0000000..1cd76fc
--- /dev/null
+++ b/third_party/heimdal/kuser/kx509.c
@@ -0,0 +1,303 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+#include "kuser_locl.h"
+#include "heimtools-commands.h"
+#include <kx509_asn1.h>
+#undef HC_DEPRECATED_CRYPTO
+#include "../lib/hx509/hx_locl.h"
+#include "../lib/krb5/krb5_locl.h"
+#include "hx509-private.h"
+
+struct validate_store {
+ size_t ncerts;
+ int grace;
+};
+
+static int KRB5_CALLCONV
+validate1(hx509_context hx509ctx, void *d, hx509_cert cert)
+{
+ struct validate_store *v = d;
+
+ if (hx509_cert_get_notAfter(cert) < time(NULL) + v->grace)
+ return HX509_CERT_USED_AFTER_TIME;
+ v->ncerts++;
+ return 0;
+}
+
+static void
+validate(krb5_context context,
+ int grace,
+ const char *hx509_store,
+ krb5_data *der_cert,
+ krb5_data *pkcs8_priv_key)
+{
+ hx509_context hx509ctx = NULL;
+ hx509_cert cert;
+ krb5_error_code ret;
+
+ ret = hx509_context_init(&hx509ctx);
+ if (ret)
+ krb5_err(context, 1, ret, "hx509 context init");
+
+ if (der_cert->data && pkcs8_priv_key->data) {
+ hx509_private_key key = NULL;
+
+ cert = hx509_cert_init_data(hx509ctx, der_cert->data,
+ der_cert->length, NULL);
+ if (cert == NULL)
+ krb5_err(context, 1, errno, "certificate could not be loaded");
+ ret = hx509_parse_private_key(hx509ctx, NULL, pkcs8_priv_key->data,
+ pkcs8_priv_key->length,
+ HX509_KEY_FORMAT_PKCS8, &key);
+ if (ret)
+ krb5_err(context, 1, ret, "certificate could not be loaded");
+ if (hx509_cert_get_notAfter(cert) < time(NULL) + grace)
+ krb5_errx(context, 1, "certificate is expired");
+ hx509_private_key_free(&key);
+ hx509_cert_free(cert);
+ }
+ if (hx509_store) {
+ struct validate_store v;
+ hx509_certs certs;
+
+ v.ncerts = 0;
+ v.grace = grace;
+
+ ret = hx509_certs_init(hx509ctx, hx509_store, 0, NULL, &certs);
+ if (ret)
+ krb5_err(context, 1, ret, "could not read hx509 store %s",
+ hx509_store);
+ ret = hx509_certs_iter_f(hx509ctx, certs, validate1, &v);
+ if (ret)
+ krb5_err(context, 1, ret, "at least one certificate in %s expired",
+ hx509_store);
+ if (!v.ncerts)
+ krb5_errx(context, 1, "no certificates in %s", hx509_store);
+
+ hx509_certs_free(&certs);
+ }
+
+ hx509_context_free(&hx509ctx);
+}
+
+static krb5_error_code KRB5_CALLCONV
+add1_2chain(hx509_context hx509ctx, void *d, hx509_cert cert)
+{
+ heim_octet_string os;
+ krb5_error_code ret;
+ Certificates *cs = d;
+ Certificate c;
+
+ ret = hx509_cert_binary(hx509ctx, cert, &os);
+ if (ret == 0)
+ ret = decode_Certificate(os.data, os.length, &c, NULL);
+ der_free_octet_string(&os);
+ if (ret == 0) {
+ add_Certificates(cs, &c);
+ free_Certificate(&c);
+ }
+ return ret;
+}
+
+static krb5_error_code
+add_chain(hx509_context hx509ctx, hx509_certs certs, krb5_data *chain)
+{
+ krb5_error_code ret;
+ Certificates cs;
+ size_t len;
+
+ ret = decode_Certificates(chain->data, chain->length, &cs, &len);
+ if (ret == 0) {
+ ret = hx509_certs_iter_f(hx509ctx, certs, add1_2chain, &cs);
+ free_Certificates(&cs);
+ }
+ return ret;
+}
+
+static void
+store(krb5_context context,
+ const char *hx509_store,
+ krb5_data *der_cert,
+ krb5_data *pkcs8_priv_key,
+ krb5_data *chain)
+{
+ hx509_context hx509ctx = NULL;
+ hx509_private_key key = NULL;
+ hx509_certs certs;
+ hx509_cert cert;
+ char *store_exp = NULL;
+ krb5_error_code ret;
+
+ if (hx509_store == NULL) {
+ hx509_store = krb5_config_get_string(context, NULL, "libdefaults",
+ "kx509_store", NULL);
+ if (hx509_store) {
+ ret = _krb5_expand_path_tokens(context, hx509_store, 1,
+ &store_exp);
+ if (ret)
+ krb5_err(context, 1, ret, "expanding tokens in default "
+ "hx509 store");
+ hx509_store = store_exp;
+ }
+ }
+ if (hx509_store == NULL)
+ krb5_errx(context, 1, "no hx509 store given and no default hx509 "
+ "store configured");
+
+ ret = hx509_context_init(&hx509ctx);
+ if (ret)
+ krb5_err(context, 1, ret, "hx509 context init");
+
+ cert = hx509_cert_init_data(hx509ctx, der_cert->data,
+ der_cert->length, NULL);
+ if (cert == NULL)
+ krb5_err(context, 1, errno, "certificate could not be loaded");
+ ret = hx509_parse_private_key(hx509ctx, NULL, pkcs8_priv_key->data,
+ pkcs8_priv_key->length,
+ HX509_KEY_FORMAT_PKCS8, &key);
+ if (ret)
+ krb5_err(context, 1, ret, "certificate could not be loaded");
+ (void) _hx509_cert_assign_key(cert, key);
+
+ ret = hx509_certs_init(hx509ctx, hx509_store, HX509_CERTS_CREATE, NULL,
+ &certs);
+ if (ret == 0)
+ ret = hx509_certs_add(hx509ctx, certs, cert);
+ if (ret == 0)
+ add_chain(hx509ctx, certs, chain);
+ if (ret == 0)
+ ret = hx509_certs_store(hx509ctx, certs, 0, NULL);
+ if (ret)
+ krb5_err(context, 1, ret, "certificate could not be stored");
+
+ hx509_private_key_free(&key);
+ hx509_certs_free(&certs);
+ hx509_cert_free(cert);
+ hx509_context_free(&hx509ctx);
+ free(store_exp);
+}
+
+static void
+set_csr(krb5_context context, krb5_kx509_req_ctx req, const char *csr_file)
+{
+ krb5_error_code ret;
+ krb5_data d;
+
+ if (strncmp(csr_file, "PKCS10:", sizeof("PKCS10:") - 1) != 0)
+ krb5_errx(context, 1, "CSR filename must start with \"PKCS10:\"");
+ ret = rk_undumpdata(csr_file + sizeof("PKCS10:") - 1, &d.data, &d.length);
+ if (ret)
+ krb5_err(context, 1, ret, "could not read CSR");
+ ret = krb5_kx509_ctx_set_csr_der(context, req, &d);
+ if (ret)
+ krb5_err(context, 1, ret, "hx509 context init");
+}
+
+int
+kx509(struct kx509_options *opt, int argc, char **argv)
+{
+ krb5_kx509_req_ctx req = NULL;
+ krb5_context context = heimtools_context;
+ krb5_error_code ret = 0;
+ krb5_ccache ccout = NULL;
+ krb5_ccache cc = NULL;
+
+ if (opt->cache_string)
+ ret = krb5_cc_resolve(context, opt->cache_string, &cc);
+ else if (opt->save_flag || opt->extract_flag)
+ ret = krb5_cc_default(context, &cc);
+ if (ret)
+ krb5_err(context, 1, ret, "no input credential cache");
+ if (opt->save_flag)
+ ccout = cc;
+
+ if (opt->test_integer &&
+ (opt->extract_flag || opt->csr_string || opt->private_key_string))
+ krb5_errx(context, 1, "--test is exclusive of --extract, --csr, and "
+ "--private-key");
+
+ if (opt->extract_flag && (opt->csr_string || opt->private_key_string))
+ krb5_errx(context, 1, "--extract is exclusive of --csr and "
+ "--private-key");
+
+ if (opt->test_integer || opt->extract_flag) {
+ krb5_data der_cert, pkcs8_key, chain;
+
+ der_cert.data = pkcs8_key.data = chain.data = NULL;
+ der_cert.length = pkcs8_key.length = chain.length = 0;
+ ret = krb5_cc_get_config(context, cc, NULL, "kx509cert", &der_cert);
+ if (ret == 0)
+ ret = krb5_cc_get_config(context, cc, NULL, "kx509key",
+ &pkcs8_key);
+ if (ret == 0)
+ ret = krb5_cc_get_config(context, cc, NULL, "kx509cert-chain",
+ &chain);
+ if (ret)
+ krb5_err(context, 1, ret, "no certificate in credential cache");
+ if (opt->test_integer)
+ validate(context, opt->test_integer, opt->out_string, &der_cert,
+ &pkcs8_key);
+ else
+ store(context, opt->out_string, &der_cert, &pkcs8_key, &chain);
+ krb5_data_free(&pkcs8_key);
+ krb5_data_free(&der_cert);
+ krb5_data_free(&chain);
+ } else {
+ /*
+ * XXX We should delete any cc configs that indicate that kx509 is
+ * disabled.
+ */
+ ret = krb5_kx509_ctx_init(context, &req);
+ if (ret == 0 && opt->realm_string)
+ ret = krb5_kx509_ctx_set_realm(context, req, opt->realm_string);
+ if (ret == 0 && opt->csr_string)
+ set_csr(context, req, opt->csr_string);
+ if (ret == 0 && opt->private_key_string)
+ ret = krb5_kx509_ctx_set_key(context, req,
+ opt->private_key_string);
+ if (ret)
+ krb5_err(context, 1, ret,
+ "could not set up kx509 request options");
+
+ ret = krb5_kx509_ext(context, req, cc, opt->out_string, ccout);
+ if (ret)
+ krb5_err(context, 1, ret,
+ "could not acquire certificate with kx509");
+ krb5_kx509_ctx_free(context, &req);
+ }
+
+ krb5_cc_close(context, cc);
+
+ return 0;
+}