diff options
Diffstat (limited to 'third_party/heimdal/kuser')
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; +} |