summaryrefslogtreecommitdiff
path: root/src/tests/gssapi
diff options
context:
space:
mode:
authorCy Schubert <cy@FreeBSD.org>2017-07-07 17:03:42 +0000
committerCy Schubert <cy@FreeBSD.org>2017-07-07 17:03:42 +0000
commit33a9b234e7087f573ef08cd7318c6497ba08b439 (patch)
treed0ea40ad3bf5463a3c55795977c71bcb7d781b4b /src/tests/gssapi
Diffstat (limited to 'src/tests/gssapi')
-rw-r--r--src/tests/gssapi/Makefile.in115
-rw-r--r--src/tests/gssapi/ccinit.c72
-rw-r--r--src/tests/gssapi/ccrefresh.c80
-rw-r--r--src/tests/gssapi/common.c266
-rw-r--r--src/tests/gssapi/common.h81
-rw-r--r--src/tests/gssapi/deps173
-rw-r--r--src/tests/gssapi/t_accname.c93
-rw-r--r--src/tests/gssapi/t_authind.py38
-rw-r--r--src/tests/gssapi/t_ccselect.c90
-rwxr-xr-xsrc/tests/gssapi/t_ccselect.py124
-rw-r--r--src/tests/gssapi/t_ciflags.c120
-rwxr-xr-xsrc/tests/gssapi/t_client_keytab.py152
-rw-r--r--src/tests/gssapi/t_credstore.c138
-rw-r--r--src/tests/gssapi/t_enctypes.c191
-rwxr-xr-xsrc/tests/gssapi/t_enctypes.py149
-rw-r--r--src/tests/gssapi/t_err.c121
-rw-r--r--src/tests/gssapi/t_export_cred.c115
-rwxr-xr-xsrc/tests/gssapi/t_export_cred.py53
-rw-r--r--src/tests/gssapi/t_export_name.c119
-rwxr-xr-xsrc/tests/gssapi/t_gssapi.py223
-rw-r--r--src/tests/gssapi/t_gssexts.c247
-rw-r--r--src/tests/gssapi/t_imp_cred.c101
-rw-r--r--src/tests/gssapi/t_imp_name.c58
-rw-r--r--src/tests/gssapi/t_inq_cred.c116
-rw-r--r--src/tests/gssapi/t_inq_ctx.c241
-rw-r--r--src/tests/gssapi/t_inq_mechs_name.c64
-rw-r--r--src/tests/gssapi/t_invalid.c429
-rw-r--r--src/tests/gssapi/t_iov.c547
-rw-r--r--src/tests/gssapi/t_namingexts.c227
-rw-r--r--src/tests/gssapi/t_oid.c221
-rw-r--r--src/tests/gssapi/t_pcontok.c202
-rw-r--r--src/tests/gssapi/t_prf.c194
-rw-r--r--src/tests/gssapi/t_s4u.c314
-rwxr-xr-xsrc/tests/gssapi/t_s4u.py162
-rw-r--r--src/tests/gssapi/t_s4u2proxy_krb5.c164
-rw-r--r--src/tests/gssapi/t_saslname.c165
-rw-r--r--src/tests/gssapi/t_spnego.c314
-rw-r--r--src/tests/gssapi/t_srcattrs.c63
38 files changed, 6342 insertions, 0 deletions
diff --git a/src/tests/gssapi/Makefile.in b/src/tests/gssapi/Makefile.in
new file mode 100644
index 000000000000..6c146429793d
--- /dev/null
+++ b/src/tests/gssapi/Makefile.in
@@ -0,0 +1,115 @@
+mydir=tests$(S)gssapi
+BUILDTOP=$(REL)..$(S)..
+DEFINES = -DUSE_AUTOCONF_H
+
+# For t_prf.c
+LOCALINCLUDES = -I$(srcdir)/../../lib/gssapi/mechglue \
+ -I$(srcdir)/../../lib/gssapi/krb5 \
+ -I$(srcdir)/../../lib/gssapi/generic -I../../lib/gssapi/krb5 \
+ -I../../lib/gssapi/generic
+
+SRCS= $(srcdir)/ccinit.c $(srcdir)/ccrefresh.c $(srcdir)/common.c \
+ $(srcdir)/t_accname.c $(srcdir)/t_ccselect.c $(srcdir)/t_ciflags.c \
+ $(srcdir)/t_credstore.c $(srcdir)/t_enctypes.c $(srcdir)/t_err.c \
+ $(srcdir)/t_export_cred.c $(srcdir)/t_export_name.c \
+ $(srcdir)/t_gssexts.c $(srcdir)/t_imp_cred.c $(srcdir)/t_imp_name.c \
+ $(srcdir)/t_invalid.c $(srcdir)/t_inq_cred.c $(srcdir)/t_inq_ctx.c \
+ $(srcdir)/t_inq_mechs_name.c $(srcdir)/t_iov.c \
+ $(srcdir)/t_namingexts.c $(srcdir)/t_oid.c $(srcdir)/t_pcontok.c \
+ $(srcdir)/t_prf.c $(srcdir)/t_s4u.c $(srcdir)/t_s4u2proxy_krb5.c \
+ $(srcdir)/t_saslname.c $(srcdir)/t_spnego.c $(srcdir)/t_srcattrs.c
+
+OBJS= ccinit.o ccrefresh.o common.o t_accname.o t_ccselect.o t_ciflags.o \
+ t_credstore.o t_enctypes.o t_err.o t_export_cred.o t_export_name.o \
+ t_gssexts.o t_imp_cred.o t_imp_name.o t_invalid.o t_inq_cred.o \
+ t_inq_ctx.o t_inq_mechs_name.o t_iov.o t_namingexts.o t_oid.o \
+ t_pcontok.o t_prf.o t_s4u.o t_s4u2proxy_krb5.o t_saslname.o \
+ t_spnego.o t_srcattrs.o
+
+COMMON_DEPS= common.o $(GSS_DEPLIBS) $(KRB5_BASE_DEPLIBS)
+COMMON_LIBS= common.o $(GSS_LIBS) $(KRB5_BASE_LIBS)
+
+all: ccinit ccrefresh t_accname t_ccselect t_ciflags t_credstore t_enctypes \
+ t_err t_export_cred t_export_name t_gssexts t_imp_cred t_imp_name \
+ t_invalid t_inq_cred t_inq_ctx t_inq_mechs_name t_iov t_namingexts \
+ t_oid t_pcontok t_prf t_s4u t_s4u2proxy_krb5 t_saslname t_spnego \
+ t_srcattrs
+
+check-unix: t_oid
+ $(RUN_TEST) ./t_invalid
+ $(RUN_TEST) ./t_oid
+ $(RUN_TEST) ./t_prf
+
+check-pytests: ccinit ccrefresh t_accname t_ccselect t_ciflags t_credstore \
+ t_enctypes t_err t_export_cred t_export_name t_imp_cred t_inq_cred \
+ t_inq_ctx t_inq_mechs_name t_iov t_pcontok t_s4u t_s4u2proxy_krb5 \
+ t_spnego t_srcattrs
+ $(RUNPYTEST) $(srcdir)/t_gssapi.py $(PYTESTFLAGS)
+ $(RUNPYTEST) $(srcdir)/t_ccselect.py $(PYTESTFLAGS)
+ $(RUNPYTEST) $(srcdir)/t_client_keytab.py $(PYTESTFLAGS)
+ $(RUNPYTEST) $(srcdir)/t_enctypes.py $(PYTESTFLAGS)
+ $(RUNPYTEST) $(srcdir)/t_export_cred.py $(PYTESTFLAGS)
+ $(RUNPYTEST) $(srcdir)/t_s4u.py $(PYTESTFLAGS)
+ $(RUNPYTEST) $(srcdir)/t_authind.py $(PYTESTFLAGS)
+
+ccinit: ccinit.o $(KRB5_BASE_DEPLIBS)
+ $(CC_LINK) -o ccinit ccinit.o $(KRB5_BASE_LIBS)
+ccrefresh: ccrefresh.o $(KRB5_BASE_DEPLIBS)
+ $(CC_LINK) -o ccrefresh ccrefresh.o $(KRB5_BASE_LIBS)
+t_accname: t_accname.o $(COMMON_DEPS)
+ $(CC_LINK) -o $@ t_accname.o $(COMMON_LIBS)
+t_ccselect: t_ccselect.o $(COMMON_DEPS)
+ $(CC_LINK) -o $@ t_ccselect.o $(COMMON_LIBS)
+t_ciflags: t_ciflags.o $(COMMON_DEPS)
+ $(CC_LINK) -o $@ t_ciflags.o $(COMMON_LIBS)
+t_credstore: t_credstore.o $(COMMON_DEPS)
+ $(CC_LINK) -o $@ t_credstore.o $(COMMON_LIBS)
+t_enctypes: t_enctypes.o $(COMMON_DEPS)
+ $(CC_LINK) -o $@ t_enctypes.o $(COMMON_LIBS)
+t_err: t_err.o $(COMMON_DEPS)
+ $(CC_LINK) -o $@ t_err.o $(COMMON_LIBS)
+t_export_cred: t_export_cred.o $(COMMON_DEPS)
+ $(CC_LINK) -o $@ t_export_cred.o $(COMMON_LIBS)
+t_export_name: t_export_name.o $(COMMON_DEPS)
+ $(CC_LINK) -o $@ t_export_name.o $(COMMON_LIBS)
+t_gssexts: t_gssexts.o $(COMMON_DEPS)
+ $(CC_LINK) -o $@ t_gssexts.o $(COMMON_LIBS)
+t_imp_cred: t_imp_cred.o $(COMMON_DEPS)
+ $(CC_LINK) -o $@ t_imp_cred.o $(COMMON_LIBS)
+t_imp_name: t_imp_name.o $(COMMON_DEPS)
+ $(CC_LINK) -o $@ t_imp_name.o $(COMMON_LIBS)
+t_invalid: t_invalid.o $(COMMON_DEPS)
+ $(CC_LINK) -o $@ t_invalid.o $(COMMON_LIBS)
+t_inq_cred: t_inq_cred.o $(COMMON_DEPS)
+ $(CC_LINK) -o $@ t_inq_cred.o $(COMMON_LIBS)
+t_inq_ctx: t_inq_ctx.o $(COMMON_DEPS)
+ $(CC_LINK) -o $@ t_inq_ctx.o $(COMMON_LIBS)
+t_inq_mechs_name: t_inq_mechs_name.o $(COMMON_DEPS)
+ $(CC_LINK) -o $@ t_inq_mechs_name.o $(COMMON_LIBS)
+t_iov: t_iov.o $(COMMON_DEPS)
+ $(CC_LINK) -o $@ t_iov.o $(COMMON_LIBS)
+t_namingexts: t_namingexts.o $(COMMON_DEPS)
+ $(CC_LINK) -o $@ t_namingexts.o $(COMMON_LIBS)
+t_pcontok: t_pcontok.o $(COMMON_DEPS)
+ $(CC_LINK) -o $@ t_pcontok.o $(COMMON_LIBS)
+t_oid: t_oid.o $(COMMON_DEPS)
+ $(CC_LINK) -o $@ t_oid.o $(COMMON_LIBS)
+t_prf: t_prf.o $(COMMON_DEPS)
+ $(CC_LINK) -o $@ t_prf.o $(COMMON_LIBS)
+t_s4u: t_s4u.o $(COMMON_DEPS)
+ $(CC_LINK) -o $@ t_s4u.o $(COMMON_LIBS)
+t_s4u2proxy_krb5: t_s4u2proxy_krb5.o $(COMMON_DEPS)
+ $(CC_LINK) -o $@ t_s4u2proxy_krb5.o $(COMMON_LIBS)
+t_saslname: t_saslname.o $(COMMON_DEPS)
+ $(CC_LINK) -o $@ t_saslname.o $(COMMON_LIBS)
+t_spnego: t_spnego.o $(COMMON_DEPS)
+ $(CC_LINK) -o $@ t_spnego.o $(COMMON_LIBS)
+t_srcattrs: t_srcattrs.o $(COMMON_DEPS)
+ $(CC_LINK) -o $@ t_srcattrs.o $(COMMON_LIBS)
+
+clean:
+ $(RM) ccinit ccrefresh t_accname t_ccselect t_ciflags t_credstore
+ $(RM) t_enctypes t_err t_export_cred t_export_name t_gssexts t_imp_cred
+ $(RM) t_imp_name t_invalid t_inq_cred t_inq_ctx t_inq_mechs_name t_iov
+ $(RM) t_namingexts t_oid t_pcontok t_prf t_s4u t_s4u2proxy_krb5
+ $(RM) t_saslname t_spnego t_srcattrs
diff --git a/src/tests/gssapi/ccinit.c b/src/tests/gssapi/ccinit.c
new file mode 100644
index 000000000000..b06f04404788
--- /dev/null
+++ b/src/tests/gssapi/ccinit.c
@@ -0,0 +1,72 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* tests/gssapi/ccinit.c - Initialize an empty ccache */
+/*
+ * Copyright (C) 2012 by the Massachusetts Institute of Technology.
+ * 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.
+ */
+
+/*
+ * This program initializes a ccache without attempting to get credentials in
+ * it. It is used to test some finer points of gss_acquire_cred behavior.
+ */
+
+#include "k5-int.h"
+
+static void
+check(krb5_error_code code)
+{
+ if (code != 0) {
+ com_err("ccinit", code, NULL);
+ abort();
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ const char *ccname, *princname;
+ krb5_context context;
+ krb5_principal princ;
+ krb5_ccache ccache;
+
+ if (argc != 3) {
+ fprintf(stderr, "Usage: %s ccname princname\n", argv[0]);
+ return 1;
+ }
+ ccname = argv[1];
+ princname = argv[2];
+
+ check(krb5_init_context(&context));
+ check(krb5_parse_name(context, princname, &princ));
+ check(krb5_cc_resolve(context, ccname, &ccache));
+ check(krb5_cc_initialize(context, ccache, princ));
+ krb5_cc_close(context, ccache);
+ krb5_free_principal(context, princ);
+ krb5_free_context(context);
+ return 0;
+}
diff --git a/src/tests/gssapi/ccrefresh.c b/src/tests/gssapi/ccrefresh.c
new file mode 100644
index 000000000000..e1f04ed2ebf4
--- /dev/null
+++ b/src/tests/gssapi/ccrefresh.c
@@ -0,0 +1,80 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* tests/gssapi/ccrefresh.c - Get or set refresh time on a ccache */
+/*
+ * Copyright (C) 2012 by the Massachusetts Institute of Technology.
+ * 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.
+ */
+
+/*
+ * This program sets the refresh time of an existing ccache to 1, forcing a
+ * refresh.
+ */
+
+#include "k5-int.h"
+
+static void
+check(krb5_error_code code)
+{
+ if (code != 0) {
+ com_err("ccrefresh", code, NULL);
+ abort();
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ const char *ccname, *value = NULL;
+ krb5_context context;
+ krb5_ccache ccache;
+ krb5_data d;
+
+ if (argc != 2 && argc != 3) {
+ fprintf(stderr, "Usage: %s ccname [value]\n", argv[0]);
+ return 1;
+ }
+ ccname = argv[1];
+ if (argc == 3)
+ value = argv[2];
+
+ check(krb5_init_context(&context));
+ check(krb5_cc_resolve(context, ccname, &ccache));
+ if (value != NULL) {
+ d = string2data((char *)value);
+ check(krb5_cc_set_config(context, ccache, NULL,
+ KRB5_CC_CONF_REFRESH_TIME, &d));
+ } else {
+ check(krb5_cc_get_config(context, ccache, NULL,
+ KRB5_CC_CONF_REFRESH_TIME, &d));
+ printf("%.*s\n", (int)d.length, d.data);
+ krb5_free_data_contents(context, &d);
+ }
+ krb5_cc_close(context, ccache);
+ krb5_free_context(context);
+ return 0;
+}
diff --git a/src/tests/gssapi/common.c b/src/tests/gssapi/common.c
new file mode 100644
index 000000000000..0de36d3135ce
--- /dev/null
+++ b/src/tests/gssapi/common.c
@@ -0,0 +1,266 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* tests/gssapi/common.c - Common utility functions for GSSAPI test programs */
+/*
+ * Copyright (C) 2012 by the Massachusetts Institute of Technology.
+ * 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.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "common.h"
+
+gss_OID_desc mech_krb5 = { 9, "\052\206\110\206\367\022\001\002\002" };
+gss_OID_desc mech_spnego = { 6, "\053\006\001\005\005\002" };
+gss_OID_desc mech_iakerb = { 6, "\053\006\001\005\002\005" };
+gss_OID_set_desc mechset_krb5 = { 1, &mech_krb5 };
+gss_OID_set_desc mechset_spnego = { 1, &mech_spnego };
+gss_OID_set_desc mechset_iakerb = { 1, &mech_iakerb };
+
+static void
+display_status(const char *msg, OM_uint32 code, int type)
+{
+ OM_uint32 min_stat, msg_ctx = 0;
+ gss_buffer_desc buf;
+
+ do {
+ (void)gss_display_status(&min_stat, code, type, GSS_C_NULL_OID,
+ &msg_ctx, &buf);
+ fprintf(stderr, "%s: %.*s\n", msg, (int)buf.length, (char *)buf.value);
+ (void)gss_release_buffer(&min_stat, &buf);
+ } while (msg_ctx != 0);
+}
+
+void
+check_gsserr(const char *msg, OM_uint32 major, OM_uint32 minor)
+{
+ if (GSS_ERROR(major)) {
+ display_status(msg, major, GSS_C_GSS_CODE);
+ display_status(msg, minor, GSS_C_MECH_CODE);
+ exit(1);
+ }
+}
+
+void
+check_k5err(krb5_context context, const char *msg, krb5_error_code code)
+{
+ const char *errmsg;
+
+ if (code) {
+ errmsg = krb5_get_error_message(context, code);
+ printf("%s: %s\n", msg, errmsg);
+ krb5_free_error_message(context, errmsg);
+ exit(1);
+ }
+}
+
+void
+errout(const char *msg)
+{
+ fprintf(stderr, "%s\n", msg);
+ exit(1);
+}
+
+gss_name_t
+import_name(const char *str)
+{
+ OM_uint32 major, minor;
+ gss_name_t name;
+ gss_buffer_desc buf;
+ gss_OID nametype = NULL;
+
+ if (*str == 'u')
+ nametype = GSS_C_NT_USER_NAME;
+ else if (*str == 'p')
+ nametype = (gss_OID)GSS_KRB5_NT_PRINCIPAL_NAME;
+ else if (*str == 'h')
+ nametype = GSS_C_NT_HOSTBASED_SERVICE;
+ if (nametype == NULL || str[1] != ':')
+ errout("names must begin with u: or p: or h:");
+ buf.value = (char *)str + 2;
+ buf.length = strlen(str) - 2;
+ major = gss_import_name(&minor, &buf, nametype, &name);
+ check_gsserr("gss_import_name", major, minor);
+ return name;
+}
+
+void
+establish_contexts(gss_OID imech, gss_cred_id_t icred, gss_cred_id_t acred,
+ gss_name_t tname, OM_uint32 flags, gss_ctx_id_t *ictx,
+ gss_ctx_id_t *actx, gss_name_t *src_name, gss_OID *amech,
+ gss_cred_id_t *deleg_cred)
+{
+ OM_uint32 minor, imaj, amaj;
+ gss_buffer_desc itok, atok;
+
+ *ictx = *actx = GSS_C_NO_CONTEXT;
+ imaj = amaj = GSS_S_CONTINUE_NEEDED;
+ itok.value = atok.value = NULL;
+ itok.length = atok.length = 0;
+ for (;;) {
+ (void)gss_release_buffer(&minor, &itok);
+ imaj = gss_init_sec_context(&minor, icred, ictx, tname, imech, flags,
+ GSS_C_INDEFINITE,
+ GSS_C_NO_CHANNEL_BINDINGS, &atok, NULL,
+ &itok, NULL, NULL);
+ check_gsserr("gss_init_sec_context", imaj, minor);
+ if (amaj == GSS_S_COMPLETE)
+ break;
+
+ (void)gss_release_buffer(&minor, &atok);
+ amaj = gss_accept_sec_context(&minor, actx, acred, &itok,
+ GSS_C_NO_CHANNEL_BINDINGS, src_name,
+ amech, &atok, NULL, NULL, deleg_cred);
+ check_gsserr("gss_accept_sec_context", amaj, minor);
+ (void)gss_release_buffer(&minor, &itok);
+ if (imaj == GSS_S_COMPLETE)
+ break;
+ }
+
+ if (imaj != GSS_S_COMPLETE || amaj != GSS_S_COMPLETE)
+ errout("One side wants to continue after the other is done");
+
+ (void)gss_release_buffer(&minor, &itok);
+ (void)gss_release_buffer(&minor, &atok);
+}
+
+void
+export_import_cred(gss_cred_id_t *cred)
+{
+ OM_uint32 major, minor;
+ gss_buffer_desc buf;
+
+ major = gss_export_cred(&minor, *cred, &buf);
+ check_gsserr("gss_export_cred", major, minor);
+ (void)gss_release_cred(&minor, cred);
+ major = gss_import_cred(&minor, &buf, cred);
+ check_gsserr("gss_import_cred", major, minor);
+ (void)gss_release_buffer(&minor, &buf);
+}
+
+void
+display_canon_name(const char *tag, gss_name_t name, gss_OID mech)
+{
+ gss_name_t canon;
+ OM_uint32 major, minor;
+ gss_buffer_desc buf;
+
+ major = gss_canonicalize_name(&minor, name, mech, &canon);
+ check_gsserr("gss_canonicalize_name", major, minor);
+
+ major = gss_display_name(&minor, canon, &buf, NULL);
+ check_gsserr("gss_display_name", major, minor);
+
+ printf("%s:\t%.*s\n", tag, (int)buf.length, (char *)buf.value);
+
+ (void)gss_release_name(&minor, &canon);
+ (void)gss_release_buffer(&minor, &buf);
+}
+
+void
+display_oid(const char *tag, gss_OID oid)
+{
+ OM_uint32 major, minor;
+ gss_buffer_desc buf;
+
+ major = gss_oid_to_str(&minor, oid, &buf);
+ check_gsserr("gss_oid_to_str", major, minor);
+ if (tag != NULL)
+ printf("%s:\t", tag);
+ printf("%.*s\n", (int)buf.length, (char *)buf.value);
+ (void)gss_release_buffer(&minor, &buf);
+}
+
+static void
+dump_attribute(gss_name_t name, gss_buffer_t attribute, int noisy)
+{
+ OM_uint32 major, minor;
+ gss_buffer_desc value;
+ gss_buffer_desc display_value;
+ int authenticated = 0;
+ int complete = 0;
+ int more = -1;
+ unsigned int i;
+
+ while (more != 0) {
+ value.value = NULL;
+ display_value.value = NULL;
+
+ major = gss_get_name_attribute(&minor, name, attribute, &authenticated,
+ &complete, &value, &display_value,
+ &more);
+ check_gsserr("gss_get_name_attribute", major, minor);
+
+ printf("Attribute %.*s %s %s\n\n%.*s\n",
+ (int)attribute->length, (char *)attribute->value,
+ authenticated ? "Authenticated" : "",
+ complete ? "Complete" : "",
+ (int)display_value.length, (char *)display_value.value);
+
+ if (noisy) {
+ for (i = 0; i < value.length; i++) {
+ if ((i % 32) == 0)
+ printf("\n");
+ printf("%02x", ((char *)value.value)[i] & 0xFF);
+ }
+ printf("\n\n");
+ }
+
+ (void)gss_release_buffer(&minor, &value);
+ (void)gss_release_buffer(&minor, &display_value);
+ }
+}
+
+void
+enumerate_attributes(gss_name_t name, int noisy)
+{
+ OM_uint32 major, minor;
+ int is_mechname;
+ gss_buffer_set_t attrs = GSS_C_NO_BUFFER_SET;
+ size_t i;
+
+ major = gss_inquire_name(&minor, name, &is_mechname, NULL, &attrs);
+ check_gsserr("gss_inquire_name", major, minor);
+
+ if (attrs != GSS_C_NO_BUFFER_SET) {
+ for (i = 0; i < attrs->count; i++)
+ dump_attribute(name, &attrs->elements[i], noisy);
+ }
+
+ (void)gss_release_buffer_set(&minor, &attrs);
+}
+
+void
+print_hex(FILE *fp, gss_buffer_t buf)
+{
+ size_t i;
+ const unsigned char *bytes = buf->value;
+
+ for (i = 0; i < buf->length; i++)
+ printf("%02X", bytes[i]);
+ printf("\n");
+}
diff --git a/src/tests/gssapi/common.h b/src/tests/gssapi/common.h
new file mode 100644
index 000000000000..ae11b51d4124
--- /dev/null
+++ b/src/tests/gssapi/common.h
@@ -0,0 +1,81 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* tests/gssapi/common.h - Declarations for GSSAPI test utility functions */
+/*
+ * Copyright (C) 2012 by the Massachusetts Institute of Technology.
+ * 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.
+ */
+
+#ifndef COMMON_H
+#define COMMON_H
+
+#include <gssapi/gssapi_krb5.h>
+
+extern gss_OID_desc mech_krb5;
+extern gss_OID_desc mech_spnego;
+extern gss_OID_desc mech_iakerb;
+extern gss_OID_set_desc mechset_krb5;
+extern gss_OID_set_desc mechset_spnego;
+extern gss_OID_set_desc mechset_iakerb;
+
+/* Display an error message (containing msg) and exit if major is an error. */
+void check_gsserr(const char *msg, OM_uint32 major, OM_uint32 minor);
+
+/* Display an error message (containing msg) and exit if code is an error. */
+void check_k5err(krb5_context context, const char *msg, krb5_error_code code);
+
+/* Display an error message containing msg and exit. */
+void errout(const char *msg);
+
+/* Import a GSSAPI name based on a string of the form 'u:username',
+ * 'p:principalname', or 'h:host@service' (or just 'h:service'). */
+gss_name_t import_name(const char *str);
+
+/* Establish contexts using gss_init_sec_context and gss_accept_sec_context. */
+void establish_contexts(gss_OID imech, gss_cred_id_t icred,
+ gss_cred_id_t acred, gss_name_t tname, OM_uint32 flags,
+ gss_ctx_id_t *ictx, gss_ctx_id_t *actx,
+ gss_name_t *src_name, gss_OID *amech,
+ gss_cred_id_t *deleg_cred);
+
+/* Export *cred to a token, then release *cred and replace it by re-importing
+ * the token. */
+void export_import_cred(gss_cred_id_t *cred);
+
+/* Display name as canonicalized to mech, preceded by tag. */
+void display_canon_name(const char *tag, gss_name_t name, gss_OID mech);
+
+/* Display oid in printable form, preceded by tag (if not NULL). */
+void display_oid(const char *tag, gss_OID oid);
+
+/* Display attributes of name, including hex value if noisy is true. */
+void enumerate_attributes(gss_name_t name, int noisy);
+
+/* Display the contents of buf to fp in hex, followed by a newline. */
+void print_hex(FILE *fp, gss_buffer_t buf);
+
+#endif /* COMMON_H */
diff --git a/src/tests/gssapi/deps b/src/tests/gssapi/deps
new file mode 100644
index 000000000000..be3cefbb456b
--- /dev/null
+++ b/src/tests/gssapi/deps
@@ -0,0 +1,173 @@
+#
+# Generated makefile dependencies follow.
+#
+$(OUTPRE)ccinit.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \
+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
+ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
+ $(top_srcdir)/include/socket-utils.h ccinit.c
+$(OUTPRE)ccrefresh.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \
+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
+ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
+ $(top_srcdir)/include/socket-utils.h ccrefresh.c
+$(OUTPRE)common.$(OBJEXT): $(BUILDTOP)/include/gssapi/gssapi.h \
+ $(BUILDTOP)/include/gssapi/gssapi_ext.h $(BUILDTOP)/include/gssapi/gssapi_krb5.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(COM_ERR_DEPS) $(top_srcdir)/include/krb5.h \
+ common.c common.h
+$(OUTPRE)t_accname.$(OBJEXT): $(BUILDTOP)/include/gssapi/gssapi.h \
+ $(BUILDTOP)/include/gssapi/gssapi_ext.h $(BUILDTOP)/include/gssapi/gssapi_krb5.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(COM_ERR_DEPS) $(top_srcdir)/include/krb5.h \
+ common.h t_accname.c
+$(OUTPRE)t_ccselect.$(OBJEXT): $(BUILDTOP)/include/gssapi/gssapi.h \
+ $(BUILDTOP)/include/gssapi/gssapi_ext.h $(BUILDTOP)/include/gssapi/gssapi_krb5.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(COM_ERR_DEPS) $(top_srcdir)/include/krb5.h \
+ common.h t_ccselect.c
+$(OUTPRE)t_ciflags.$(OBJEXT): $(BUILDTOP)/include/gssapi/gssapi.h \
+ $(BUILDTOP)/include/gssapi/gssapi_ext.h $(BUILDTOP)/include/gssapi/gssapi_krb5.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(COM_ERR_DEPS) $(top_srcdir)/include/krb5.h \
+ common.h t_ciflags.c
+$(OUTPRE)t_credstore.$(OBJEXT): $(BUILDTOP)/include/gssapi/gssapi.h \
+ $(BUILDTOP)/include/gssapi/gssapi_ext.h $(BUILDTOP)/include/gssapi/gssapi_krb5.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(COM_ERR_DEPS) $(top_srcdir)/include/krb5.h \
+ common.h t_credstore.c
+$(OUTPRE)t_enctypes.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \
+ $(BUILDTOP)/include/gssapi/gssapi_krb5.h $(BUILDTOP)/include/krb5/krb5.h \
+ $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
+ $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
+ $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
+ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
+ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
+ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \
+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
+ common.h t_enctypes.c
+$(OUTPRE)t_err.$(OBJEXT): $(BUILDTOP)/include/gssapi/gssapi.h \
+ $(BUILDTOP)/include/gssapi/gssapi_ext.h $(BUILDTOP)/include/gssapi/gssapi_krb5.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(COM_ERR_DEPS) $(top_srcdir)/include/krb5.h \
+ common.h t_err.c
+$(OUTPRE)t_export_cred.$(OBJEXT): $(BUILDTOP)/include/gssapi/gssapi.h \
+ $(BUILDTOP)/include/gssapi/gssapi_ext.h $(BUILDTOP)/include/gssapi/gssapi_krb5.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(COM_ERR_DEPS) $(top_srcdir)/include/krb5.h \
+ common.h t_export_cred.c
+$(OUTPRE)t_export_name.$(OBJEXT): $(BUILDTOP)/include/gssapi/gssapi.h \
+ $(BUILDTOP)/include/gssapi/gssapi_ext.h $(BUILDTOP)/include/gssapi/gssapi_krb5.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(COM_ERR_DEPS) $(top_srcdir)/include/krb5.h \
+ common.h t_export_name.c
+$(OUTPRE)t_gssexts.$(OBJEXT): $(BUILDTOP)/include/gssapi/gssapi.h \
+ $(BUILDTOP)/include/gssapi/gssapi_ext.h $(BUILDTOP)/include/gssapi/gssapi_krb5.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(COM_ERR_DEPS) $(top_srcdir)/include/krb5.h \
+ common.h t_gssexts.c
+$(OUTPRE)t_imp_cred.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \
+ $(BUILDTOP)/include/gssapi/gssapi_krb5.h $(BUILDTOP)/include/krb5/krb5.h \
+ $(COM_ERR_DEPS) $(top_srcdir)/include/k5-platform.h \
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/krb5.h \
+ common.h t_imp_cred.c
+$(OUTPRE)t_imp_name.$(OBJEXT): $(BUILDTOP)/include/gssapi/gssapi.h \
+ $(BUILDTOP)/include/gssapi/gssapi_ext.h $(BUILDTOP)/include/gssapi/gssapi_krb5.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(COM_ERR_DEPS) $(top_srcdir)/include/krb5.h \
+ common.h t_imp_name.c
+$(OUTPRE)t_invalid.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssapi/gssapi_alloc.h \
+ $(BUILDTOP)/include/gssapi/gssapi_ext.h $(BUILDTOP)/include/gssapi/gssapi_krb5.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
+ $(BUILDTOP)/include/profile.h $(BUILDTOP)/lib/gssapi/generic/gssapi_err_generic.h \
+ $(BUILDTOP)/lib/gssapi/krb5/gssapi_err_krb5.h $(COM_ERR_DEPS) \
+ $(srcdir)/../../lib/gssapi/generic/gssapiP_generic.h \
+ $(srcdir)/../../lib/gssapi/generic/gssapi_ext.h $(srcdir)/../../lib/gssapi/generic/gssapi_generic.h \
+ $(srcdir)/../../lib/gssapi/krb5/gssapiP_krb5.h $(srcdir)/../../lib/gssapi/krb5/gssapi_krb5.h \
+ $(srcdir)/../../lib/gssapi/mechglue/mechglue.h $(srcdir)/../../lib/gssapi/mechglue/mglueP.h \
+ $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
+ $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
+ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
+ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
+ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \
+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
+ common.h t_invalid.c
+$(OUTPRE)t_inq_cred.$(OBJEXT): $(BUILDTOP)/include/gssapi/gssapi.h \
+ $(BUILDTOP)/include/gssapi/gssapi_ext.h $(BUILDTOP)/include/gssapi/gssapi_krb5.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(COM_ERR_DEPS) $(top_srcdir)/include/krb5.h \
+ common.h t_inq_cred.c
+$(OUTPRE)t_inq_ctx.$(OBJEXT): $(BUILDTOP)/include/gssapi/gssapi.h \
+ $(BUILDTOP)/include/gssapi/gssapi_ext.h $(BUILDTOP)/include/gssapi/gssapi_krb5.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(COM_ERR_DEPS) $(top_srcdir)/include/krb5.h \
+ common.h t_inq_ctx.c
+$(OUTPRE)t_inq_mechs_name.$(OBJEXT): $(BUILDTOP)/include/gssapi/gssapi.h \
+ $(BUILDTOP)/include/gssapi/gssapi_ext.h $(BUILDTOP)/include/gssapi/gssapi_krb5.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(COM_ERR_DEPS) $(top_srcdir)/include/krb5.h \
+ common.h t_inq_mechs_name.c
+$(OUTPRE)t_iov.$(OBJEXT): $(BUILDTOP)/include/gssapi/gssapi.h \
+ $(BUILDTOP)/include/gssapi/gssapi_ext.h $(BUILDTOP)/include/gssapi/gssapi_krb5.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(COM_ERR_DEPS) $(top_srcdir)/include/krb5.h \
+ common.h t_iov.c
+$(OUTPRE)t_namingexts.$(OBJEXT): $(BUILDTOP)/include/gssapi/gssapi.h \
+ $(BUILDTOP)/include/gssapi/gssapi_ext.h $(BUILDTOP)/include/gssapi/gssapi_krb5.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(COM_ERR_DEPS) $(top_srcdir)/include/krb5.h \
+ common.h t_namingexts.c
+$(OUTPRE)t_oid.$(OBJEXT): $(BUILDTOP)/include/gssapi/gssapi.h \
+ $(BUILDTOP)/include/gssapi/gssapi_ext.h $(BUILDTOP)/include/gssapi/gssapi_krb5.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(COM_ERR_DEPS) $(top_srcdir)/include/krb5.h \
+ common.h t_oid.c
+$(OUTPRE)t_pcontok.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \
+ $(BUILDTOP)/include/gssapi/gssapi_krb5.h $(BUILDTOP)/include/krb5/krb5.h \
+ $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
+ $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
+ $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
+ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
+ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
+ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \
+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
+ common.h t_pcontok.c
+$(OUTPRE)t_prf.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssapi/gssapi_alloc.h \
+ $(BUILDTOP)/include/gssapi/gssapi_ext.h $(BUILDTOP)/include/gssapi/gssapi_krb5.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
+ $(BUILDTOP)/include/profile.h $(BUILDTOP)/lib/gssapi/generic/gssapi_err_generic.h \
+ $(BUILDTOP)/lib/gssapi/krb5/gssapi_err_krb5.h $(COM_ERR_DEPS) \
+ $(srcdir)/../../lib/gssapi/generic/gssapiP_generic.h \
+ $(srcdir)/../../lib/gssapi/generic/gssapi_ext.h $(srcdir)/../../lib/gssapi/generic/gssapi_generic.h \
+ $(srcdir)/../../lib/gssapi/krb5/gssapiP_krb5.h $(srcdir)/../../lib/gssapi/krb5/gssapi_krb5.h \
+ $(srcdir)/../../lib/gssapi/mechglue/mechglue.h $(srcdir)/../../lib/gssapi/mechglue/mglueP.h \
+ $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
+ $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
+ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
+ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
+ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \
+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
+ common.h t_prf.c
+$(OUTPRE)t_s4u.$(OBJEXT): $(BUILDTOP)/include/gssapi/gssapi.h \
+ $(BUILDTOP)/include/gssapi/gssapi_ext.h $(BUILDTOP)/include/gssapi/gssapi_krb5.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(COM_ERR_DEPS) $(top_srcdir)/include/krb5.h \
+ common.h t_s4u.c
+$(OUTPRE)t_s4u2proxy_krb5.$(OBJEXT): $(BUILDTOP)/include/gssapi/gssapi.h \
+ $(BUILDTOP)/include/gssapi/gssapi_ext.h $(BUILDTOP)/include/gssapi/gssapi_krb5.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(COM_ERR_DEPS) $(top_srcdir)/include/krb5.h \
+ common.h t_s4u2proxy_krb5.c
+$(OUTPRE)t_saslname.$(OBJEXT): $(BUILDTOP)/include/gssapi/gssapi.h \
+ $(BUILDTOP)/include/gssapi/gssapi_ext.h $(BUILDTOP)/include/gssapi/gssapi_krb5.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(COM_ERR_DEPS) $(top_srcdir)/include/krb5.h \
+ common.h t_saslname.c
+$(OUTPRE)t_spnego.$(OBJEXT): $(BUILDTOP)/include/gssapi/gssapi.h \
+ $(BUILDTOP)/include/gssapi/gssapi_ext.h $(BUILDTOP)/include/gssapi/gssapi_krb5.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(COM_ERR_DEPS) $(top_srcdir)/include/krb5.h \
+ common.h t_spnego.c
+$(OUTPRE)t_srcattrs.$(OBJEXT): $(BUILDTOP)/include/gssapi/gssapi.h \
+ $(BUILDTOP)/include/gssapi/gssapi_ext.h $(BUILDTOP)/include/gssapi/gssapi_krb5.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(COM_ERR_DEPS) $(top_srcdir)/include/krb5.h \
+ common.h t_srcattrs.c
diff --git a/src/tests/gssapi/t_accname.c b/src/tests/gssapi/t_accname.c
new file mode 100644
index 000000000000..30b5db54f3ae
--- /dev/null
+++ b/src/tests/gssapi/t_accname.c
@@ -0,0 +1,93 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright 2011 by the Massachusetts Institute of Technology.
+ * 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 M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "common.h"
+
+/*
+ * Test program for acceptor names, intended to be run from a Python test
+ * script. Establishes contexts with the default initiator name, a specified
+ * principal name as target name, and a specified host-based name as acceptor
+ * name (or GSS_C_NO_NAME if no acceptor name is given). If the exchange is
+ * successful, queries the context for the acceptor name and prints it. If any
+ * call is unsuccessful, displays an error message. Exits with status 0 if all
+ * operations are successful, or 1 if not.
+ *
+ * Usage: ./t_accname targetname [acceptorname]
+ */
+
+int
+main(int argc, char *argv[])
+{
+ OM_uint32 minor, major, flags;
+ gss_cred_id_t acceptor_cred;
+ gss_name_t target_name, acceptor_name = GSS_C_NO_NAME, real_acceptor_name;
+ gss_buffer_desc namebuf;
+ gss_ctx_id_t initiator_context, acceptor_context;
+
+ if (argc < 2 || argc > 3) {
+ fprintf(stderr, "Usage: %s targetname [acceptorname]\n", argv[0]);
+ return 1;
+ }
+
+ /* Import target and acceptor names. */
+ target_name = import_name(argv[1]);
+ if (argc >= 3)
+ acceptor_name = import_name(argv[2]);
+
+ /* Get acceptor cred. */
+ major = gss_acquire_cred(&minor, acceptor_name, GSS_C_INDEFINITE,
+ GSS_C_NO_OID_SET, GSS_C_ACCEPT,
+ &acceptor_cred, NULL, NULL);
+ check_gsserr("gss_acquire_cred", major, minor);
+
+ flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG;
+ establish_contexts(&mech_krb5, GSS_C_NO_CREDENTIAL, acceptor_cred,
+ target_name, flags, &initiator_context,
+ &acceptor_context, NULL, NULL, NULL);
+
+ major = gss_inquire_context(&minor, acceptor_context, NULL,
+ &real_acceptor_name, NULL, NULL, NULL, NULL,
+ NULL);
+ check_gsserr("gss_inquire_context", major, minor);
+
+ namebuf.value = NULL;
+ namebuf.length = 0;
+ major = gss_display_name(&minor, real_acceptor_name, &namebuf, NULL);
+ check_gsserr("gss_display_name", major, minor);
+
+ printf("%.*s\n", (int)namebuf.length, (char *)namebuf.value);
+
+ (void)gss_release_name(&minor, &target_name);
+ (void)gss_release_name(&minor, &acceptor_name);
+ (void)gss_release_name(&minor, &real_acceptor_name);
+ (void)gss_release_cred(&minor, &acceptor_cred);
+ (void)gss_delete_sec_context(&minor, &initiator_context, NULL);
+ (void)gss_delete_sec_context(&minor, &acceptor_context, NULL);
+ (void)gss_release_buffer(&minor, &namebuf);
+ return 0;
+}
diff --git a/src/tests/gssapi/t_authind.py b/src/tests/gssapi/t_authind.py
new file mode 100644
index 000000000000..316bc4093801
--- /dev/null
+++ b/src/tests/gssapi/t_authind.py
@@ -0,0 +1,38 @@
+#!/usr/bin/python
+from k5test import *
+
+# Test authentication indicators. Load the test preauth module so we
+# can control the indicators asserted.
+testpreauth = os.path.join(buildtop, 'plugins', 'preauth', 'test', 'test.so')
+conf = {'plugins': {'kdcpreauth': {'module': 'test:' + testpreauth},
+ 'clpreauth': {'module': 'test:' + testpreauth}}}
+realm = K5Realm(krb5_conf=conf)
+realm.run([kadminl, 'addprinc', '-randkey', 'service/1'])
+realm.run([kadminl, 'addprinc', '-randkey', 'service/2'])
+realm.run([kadminl, 'modprinc', '+requires_preauth', realm.user_princ])
+realm.run([kadminl, 'setstr', 'service/1', 'require_auth', 'superstrong'])
+realm.run([kadminl, 'setstr', 'service/2', 'require_auth', 'one two'])
+realm.run([kadminl, 'xst', 'service/1'])
+realm.run([kadminl, 'xst', 'service/2'])
+
+realm.kinit(realm.user_princ, password('user'),
+ ['-X', 'indicators=superstrong'])
+out = realm.run(['./t_srcattrs', 'p:service/1'])
+if ('Attribute auth-indicators Authenticated Complete') not in out:
+ fail('Expected attribute type data not seen')
+# UTF8 "superstrong"
+if '73757065727374726f6e67' not in out:
+ fail('Expected auth indicator not seen in name attributes')
+
+out = realm.run(['./t_srcattrs', 'p:service/2'], expected_code=1)
+if 'gss_init_sec_context: KDC policy rejects request' not in out:
+ fail('Expected error message not seen for indicator mismatch')
+
+realm.kinit(realm.user_princ, password('user'), ['-X', 'indicators=one two'])
+out = realm.run(['./t_srcattrs', 'p:service/2'])
+# Hexademical "one" and "two"
+if '6f6e65' not in out or '74776f' not in out:
+ fail('Expected auth indicator not seen in name attributes')
+
+realm.stop()
+success('GSSAPI auth indicator tests')
diff --git a/src/tests/gssapi/t_ccselect.c b/src/tests/gssapi/t_ccselect.c
new file mode 100644
index 000000000000..cc4f73a1f1c5
--- /dev/null
+++ b/src/tests/gssapi/t_ccselect.c
@@ -0,0 +1,90 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* tests/gssapi/t_ccselect.c - Test program for GSSAPI cred selection */
+/*
+ * Copyright 2011 by the Massachusetts Institute of Technology.
+ * 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 M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+
+/*
+ * Test program for client credential selection, intended to be run from a
+ * Python test script. Establishes contexts with an optionally specified
+ * initiator name, a specified target name, and the default acceptor cred. If
+ * the exchange is successful, prints the initiator name as seen by the
+ * acceptor. If any call is unsuccessful, displays an error message. Exits
+ * with status 0 if all operations are successful, or 1 if not.
+ *
+ * Usage: ./t_ccselect targetname [initiatorname|-]
+ */
+
+int
+main(int argc, char *argv[])
+{
+ OM_uint32 minor, major, flags;
+ gss_cred_id_t initiator_cred = GSS_C_NO_CREDENTIAL;
+ gss_name_t target_name, initiator_name = GSS_C_NO_NAME;
+ gss_name_t real_initiator_name;
+ gss_buffer_desc namebuf;
+ gss_ctx_id_t initiator_context, acceptor_context;
+
+ if (argc < 2 || argc > 3) {
+ fprintf(stderr, "Usage: %s targetname [initiatorname|-]\n", argv[0]);
+ return 1;
+ }
+
+ target_name = import_name(argv[1]);
+
+ if (argc >= 3) {
+ /* Get initiator cred. */
+ if (strcmp(argv[2], "-") != 0)
+ initiator_name = import_name(argv[2]);
+ major = gss_acquire_cred(&minor, initiator_name, GSS_C_INDEFINITE,
+ GSS_C_NO_OID_SET, GSS_C_INITIATE,
+ &initiator_cred, NULL, NULL);
+ check_gsserr("gss_acquire_cred", major, minor);
+ }
+
+ flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG;
+ establish_contexts(&mech_krb5, initiator_cred, GSS_C_NO_CREDENTIAL,
+ target_name, flags, &initiator_context,
+ &acceptor_context, &real_initiator_name, NULL, NULL);
+
+ namebuf.value = NULL;
+ namebuf.length = 0;
+ major = gss_display_name(&minor, real_initiator_name, &namebuf, NULL);
+ check_gsserr("gss_display_name(initiator)", major, minor);
+ printf("%.*s\n", (int)namebuf.length, (char *)namebuf.value);
+
+ (void)gss_release_name(&minor, &target_name);
+ (void)gss_release_name(&minor, &initiator_name);
+ (void)gss_release_name(&minor, &real_initiator_name);
+ (void)gss_release_cred(&minor, &initiator_cred);
+ (void)gss_delete_sec_context(&minor, &initiator_context, NULL);
+ (void)gss_delete_sec_context(&minor, &acceptor_context, NULL);
+ (void)gss_release_buffer(&minor, &namebuf);
+ return 0;
+}
diff --git a/src/tests/gssapi/t_ccselect.py b/src/tests/gssapi/t_ccselect.py
new file mode 100755
index 000000000000..6be6b4ec06c1
--- /dev/null
+++ b/src/tests/gssapi/t_ccselect.py
@@ -0,0 +1,124 @@
+#!/usr/bin/python
+
+# Copyright (C) 2011 by the Massachusetts Institute of Technology.
+# 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 M.I.T. not be used in advertising or publicity pertaining
+# to distribution of the software without specific, written prior
+# permission. Furthermore if you modify this software you must label
+# your software as modified software and not distribute it in such a
+# fashion that it might be confused with the original M.I.T. software.
+# M.I.T. makes no representations about the suitability of
+# this software for any purpose. It is provided "as is" without express
+# or implied warranty.
+
+from k5test import *
+
+# Create two independent realms (no cross-realm TGTs).
+r1 = K5Realm(create_user=False)
+r2 = K5Realm(create_user=False, realm='KRBTEST2.COM', portbase=62000,
+ testdir=os.path.join(r1.testdir, 'r2'))
+
+host1 = 'p:' + r1.host_princ
+host2 = 'p:' + r2.host_princ
+
+# gsserver specifies the target as a GSS name. The resulting
+# principal will have the host-based type, but the realm won't be
+# known before the client cache is selected (since k5test realms have
+# no domain-realm mapping by default).
+gssserver = 'h:host@' + hostname
+
+# refserver specifies the target as a principal in the referral realm.
+# The principal won't be treated as a host principal by the
+# .k5identity rules since it has unknown type.
+refserver = 'p:host/' + hostname + '@'
+
+# Verify that we can't get initiator creds with no credentials in the
+# collection.
+output = r1.run(['./t_ccselect', host1, '-'], expected_code=1)
+if 'No Kerberos credentials available' not in output:
+ fail('Expected error not seen in output when no credentials available')
+
+# Make a directory collection and use it for client commands in both realms.
+ccdir = os.path.join(r1.testdir, 'cc')
+ccname = 'DIR:' + ccdir
+r1.env['KRB5CCNAME'] = ccname
+r2.env['KRB5CCNAME'] = ccname
+
+# Use .k5identity from testdir and not from the tester's homedir.
+r1.env['HOME'] = r1.testdir
+r2.env['HOME'] = r1.testdir
+
+# Create two users in r1 and one in r2.
+alice='alice@KRBTEST.COM'
+bob='bob@KRBTEST.COM'
+zaphod='zaphod@KRBTEST2.COM'
+r1.addprinc(alice, password('alice'))
+r1.addprinc(bob, password('bob'))
+r2.addprinc(zaphod, password('zaphod'))
+
+# Get tickets for one user in each realm (zaphod will be primary).
+r1.kinit(alice, password('alice'))
+r2.kinit(zaphod, password('zaphod'))
+
+# Check that we can find a cache for a specified client principal.
+output = r1.run(['./t_ccselect', host1, 'p:' + alice])
+if output != (alice + '\n'):
+ fail('alice not chosen when specified')
+output = r2.run(['./t_ccselect', host2, 'p:' + zaphod])
+if output != (zaphod + '\n'):
+ fail('zaphod not chosen when specified')
+
+# Check that we can guess a cache based on the service realm.
+output = r1.run(['./t_ccselect', host1])
+if output != (alice + '\n'):
+ fail('alice not chosen as default initiator cred for server in r1')
+output = r1.run(['./t_ccselect', host1, '-'])
+if output != (alice + '\n'):
+ fail('alice not chosen as default initiator name for server in r1')
+output = r2.run(['./t_ccselect', host2])
+if output != (zaphod + '\n'):
+ fail('zaphod not chosen as default initiator cred for server in r1')
+output = r2.run(['./t_ccselect', host2, '-'])
+if output != (zaphod + '\n'):
+ fail('zaphod not chosen as default initiator name for server in r1')
+
+# Check that primary cache is used if server realm is unknown.
+output = r2.run(['./t_ccselect', gssserver])
+if output != (zaphod + '\n'):
+ fail('zaphod not chosen via primary cache for unknown server realm')
+r1.run(['./t_ccselect', gssserver], expected_code=1)
+
+# Get a second cred in r1 (bob will be primary).
+r1.kinit(bob, password('bob'))
+
+# Try some cache selections using .k5identity.
+k5id = open(os.path.join(r1.testdir, '.k5identity'), 'w')
+k5id.write('%s realm=%s\n' % (alice, r1.realm))
+k5id.write('%s service=ho*t host=%s\n' % (zaphod, hostname))
+k5id.write('noprinc service=bogus')
+k5id.close()
+output = r1.run(['./t_ccselect', host1])
+if output != (alice + '\n'):
+ fail('alice not chosen via .k5identity realm line.')
+output = r2.run(['./t_ccselect', gssserver])
+if output != (zaphod + '\n'):
+ fail('zaphod not chosen via .k5identity service/host line.')
+output = r1.run(['./t_ccselect', refserver])
+if output != (bob + '\n'):
+ fail('bob not chosen via primary cache when no .k5identity line matches.')
+output = r1.run(['./t_ccselect', 'h:bogus@' + hostname], expected_code=1)
+if 'Can\'t find client principal noprinc' not in output:
+ fail('Expected error not seen when k5identity selects bad principal.')
+
+success('GSSAPI credential selection tests')
diff --git a/src/tests/gssapi/t_ciflags.c b/src/tests/gssapi/t_ciflags.c
new file mode 100644
index 000000000000..315062c9e74a
--- /dev/null
+++ b/src/tests/gssapi/t_ciflags.c
@@ -0,0 +1,120 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* tests/gssapi/t_ciflags.c - GSS_KRB5_CRED_NO_CI_FLAGS_X tests */
+/*
+ * Copyright (C) 2015 by the Massachusetts Institute of Technology.
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "common.h"
+
+static void
+flagtest(gss_OID mech, gss_cred_id_t icred, gss_name_t tname,
+ OM_uint32 inflags, OM_uint32 expflags)
+{
+ gss_ctx_id_t ictx, actx;
+ OM_uint32 major, minor, flags;
+
+ establish_contexts(mech, icred, GSS_C_NO_CREDENTIAL, tname, inflags, &ictx,
+ &actx, NULL, NULL, NULL);
+
+ major = gss_inquire_context(&minor, actx, NULL, NULL, NULL, NULL, &flags,
+ NULL, NULL);
+ check_gsserr("gss_inquire_context", major, minor);
+ assert(flags == expflags);
+
+ (void)gss_delete_sec_context(&minor, &ictx, NULL);
+ (void)gss_delete_sec_context(&minor, &actx, NULL);
+}
+
+int
+main(int argc, char *argv[])
+{
+ OM_uint32 minor, major;
+ gss_cred_id_t icred;
+ gss_name_t tname;
+ gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: %s targetname\n", argv[0]);
+ return 1;
+ }
+ tname = import_name(argv[1]);
+
+ /* With no flags, the initiator asserts conf, integ, trans */
+ flagtest(&mech_krb5, GSS_C_NO_CREDENTIAL, tname, 0,
+ GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG | GSS_C_TRANS_FLAG);
+ flagtest(&mech_spnego, GSS_C_NO_CREDENTIAL, tname, 0,
+ GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG | GSS_C_TRANS_FLAG);
+
+ /* The initiator also asserts most flags specified by the caller. */
+ flagtest(&mech_krb5, GSS_C_NO_CREDENTIAL, tname, GSS_C_SEQUENCE_FLAG,
+ GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG | GSS_C_TRANS_FLAG |
+ GSS_C_SEQUENCE_FLAG);
+ flagtest(&mech_spnego, GSS_C_NO_CREDENTIAL, tname, GSS_C_SEQUENCE_FLAG,
+ GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG | GSS_C_TRANS_FLAG |
+ GSS_C_SEQUENCE_FLAG);
+
+ /* Get a normal initiator cred and re-test with no flags. */
+ major = gss_acquire_cred(&minor, GSS_C_NO_NAME, GSS_C_INDEFINITE,
+ GSS_C_NO_OID_SET, GSS_C_INITIATE, &icred, NULL,
+ NULL);
+ check_gsserr("gss_acquire_cred", major, minor);
+ flagtest(&mech_krb5, icred, tname, 0,
+ GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG | GSS_C_TRANS_FLAG);
+ flagtest(&mech_spnego, icred, tname, 0,
+ GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG | GSS_C_TRANS_FLAG);
+
+ /* Suppress confidentiality and integrity flags on the initiator cred and
+ * check that they are suppressed, but can still be asserted explicitly. */
+ major = gss_set_cred_option(&minor, &icred,
+ (gss_OID)GSS_KRB5_CRED_NO_CI_FLAGS_X,
+ &empty_buffer);
+ check_gsserr("gss_set_cred_option", major, minor);
+ flagtest(&mech_krb5, icred, tname, 0, GSS_C_TRANS_FLAG);
+ flagtest(&mech_krb5, icred, tname, GSS_C_CONF_FLAG,
+ GSS_C_CONF_FLAG | GSS_C_TRANS_FLAG);
+ flagtest(&mech_krb5, icred, tname, GSS_C_INTEG_FLAG,
+ GSS_C_INTEG_FLAG | GSS_C_TRANS_FLAG);
+ flagtest(&mech_krb5, icred, tname, GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG,
+ GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG | GSS_C_TRANS_FLAG);
+ flagtest(&mech_spnego, icred, tname, 0, GSS_C_TRANS_FLAG);
+ flagtest(&mech_spnego, icred, tname, GSS_C_INTEG_FLAG,
+ GSS_C_INTEG_FLAG | GSS_C_TRANS_FLAG);
+ flagtest(&mech_spnego, icred, tname, GSS_C_CONF_FLAG,
+ GSS_C_CONF_FLAG | GSS_C_TRANS_FLAG);
+ flagtest(&mech_spnego, icred, tname, GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG,
+ GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG | GSS_C_TRANS_FLAG);
+
+ (void)gss_release_name(&minor, &tname);
+ (void)gss_release_cred(&minor, &icred);
+ return 0;
+}
diff --git a/src/tests/gssapi/t_client_keytab.py b/src/tests/gssapi/t_client_keytab.py
new file mode 100755
index 000000000000..4c8747a506c1
--- /dev/null
+++ b/src/tests/gssapi/t_client_keytab.py
@@ -0,0 +1,152 @@
+#!/usr/bin/python
+from k5test import *
+
+# Set up a basic realm and a client keytab containing two user principals.
+# Point HOME at realm.testdir for tests using .k5identity.
+realm = K5Realm(get_creds=False)
+bob = 'bob@' + realm.realm
+phost = 'p:' + realm.host_princ
+puser = 'p:' + realm.user_princ
+pbob = 'p:' + bob
+gssserver = 'h:host@' + hostname
+realm.env['HOME'] = realm.testdir
+realm.addprinc(bob, password('bob'))
+realm.extract_keytab(realm.user_princ, realm.client_keytab)
+realm.extract_keytab(bob, realm.client_keytab)
+
+# Test 1: no name/cache specified, pick first principal from client keytab
+out = realm.run(['./t_ccselect', phost])
+if realm.user_princ not in out:
+ fail('Authenticated as wrong principal')
+realm.run([kdestroy])
+
+# Test 2: no name/cache specified, pick principal from k5identity
+k5idname = os.path.join(realm.testdir, '.k5identity')
+k5id = open(k5idname, 'w')
+k5id.write('%s service=host host=%s\n' % (bob, hostname))
+k5id.close()
+out = realm.run(['./t_ccselect', gssserver])
+if bob not in out:
+ fail('Authenticated as wrong principal')
+os.remove(k5idname)
+realm.run([kdestroy])
+
+# Test 3: no name/cache specified, default ccache has name but no creds
+realm.run(['./ccinit', realm.ccache, bob])
+out = realm.run(['./t_ccselect', phost])
+if bob not in out:
+ fail('Authenticated as wrong principal')
+# Leave tickets for next test.
+
+# Test 4: name specified, non-collectable default cache doesn't match
+out = realm.run(['./t_ccselect', phost, puser], expected_code=1)
+if 'Principal in credential cache does not match desired name' not in out:
+ fail('Expected error not seen')
+realm.run([kdestroy])
+
+# Test 5: name specified, nonexistent default cache
+out = realm.run(['./t_ccselect', phost, pbob])
+if bob not in out:
+ fail('Authenticated as wrong principal')
+# Leave tickets for next test.
+
+# Test 6: name specified, matches default cache, time to refresh
+realm.run(['./ccrefresh', realm.ccache, '1'])
+out = realm.run(['./t_ccselect', phost, pbob])
+if bob not in out:
+ fail('Authenticated as wrong principal')
+out = realm.run(['./ccrefresh', realm.ccache])
+if int(out) < 1000:
+ fail('Credentials apparently not refreshed')
+realm.run([kdestroy])
+
+# Test 7: empty ccache specified, pick first principal from client keytab
+realm.run(['./t_imp_cred', phost])
+realm.klist(realm.user_princ)
+realm.run([kdestroy])
+
+# Test 8: ccache specified with name but no creds; name not in client keytab
+realm.run(['./ccinit', realm.ccache, realm.host_princ])
+out = realm.run(['./t_imp_cred', phost], expected_code=1)
+if 'Credential cache is empty' not in out:
+ fail('Expected error not seen')
+realm.run([kdestroy])
+
+# Test 9: ccache specified with name but no creds; name in client keytab
+realm.run(['./ccinit', realm.ccache, bob])
+realm.run(['./t_imp_cred', phost])
+realm.klist(bob)
+# Leave tickets for next test.
+
+# Test 10: ccache specified with creds, time to refresh
+realm.run(['./ccrefresh', realm.ccache, '1'])
+realm.run(['./t_imp_cred', phost])
+realm.klist(bob)
+out = realm.run(['./ccrefresh', realm.ccache])
+if int(out) < 1000:
+ fail('Credentials apparently not refreshed')
+realm.run([kdestroy])
+
+# Test 11: gss_import_cred_from with client_keytab value
+store_keytab = os.path.join(realm.testdir, 'store_keytab')
+os.rename(realm.client_keytab, store_keytab)
+realm.run(['./t_credstore', '-i', 'p:' + realm.user_princ, 'client_keytab',
+ store_keytab])
+realm.klist(realm.user_princ)
+os.rename(store_keytab, realm.client_keytab)
+
+# Use a cache collection for the remaining tests.
+ccdir = os.path.join(realm.testdir, 'cc')
+ccname = 'DIR:' + ccdir
+os.mkdir(ccdir)
+realm.env['KRB5CCNAME'] = ccname
+
+# Test 12: name specified, matching cache in collection with no creds
+bobcache = os.path.join(ccdir, 'tktbob')
+realm.run(['./ccinit', bobcache, bob])
+out = realm.run(['./t_ccselect', phost, pbob])
+if bob not in out:
+ fail('Authenticated as wrong principal')
+# Leave tickets for next test.
+
+# Test 13: name specified, matching cache in collection, time to refresh
+realm.run(['./ccrefresh', bobcache, '1'])
+out = realm.run(['./t_ccselect', phost, pbob])
+if bob not in out:
+ fail('Authenticated as wrong principal')
+out = realm.run(['./ccrefresh', bobcache])
+if int(out) < 1000:
+ fail('Credentials apparently not refreshed')
+realm.run([kdestroy, '-A'])
+
+# Test 14: name specified, collection has default for different principal
+realm.kinit(realm.user_princ, password('user'))
+out = realm.run(['./t_ccselect', phost, pbob])
+if bob not in out:
+ fail('Authenticated as wrong principal')
+out = realm.run([klist])
+if 'Default principal: %s\n' % realm.user_princ not in out:
+ fail('Default cache overwritten by acquire_cred')
+realm.run([kdestroy, '-A'])
+
+# Test 15: name specified, collection has no default cache
+out = realm.run(['./t_ccselect', phost, pbob])
+if bob not in out:
+ fail('Authenticated as wrong principal')
+# Make sure the tickets we acquired didn't become the default
+out = realm.run([klist], expected_code=1)
+if 'No credentials cache found' not in out:
+ fail('Expected error not seen')
+realm.run([kdestroy, '-A'])
+
+# Test 16: default client keytab cannot be resolved, but valid
+# credentials exist in ccache.
+conf = {'libdefaults': {'default_client_keytab_name': '%{'}}
+bad_cktname = realm.special_env('bad_cktname', False, krb5_conf=conf)
+del bad_cktname['KRB5_CLIENT_KTNAME']
+realm.kinit(realm.user_princ, password('user'))
+out = realm.run(['./t_ccselect', phost], env=bad_cktname)
+if realm.user_princ not in out:
+ fail('Expected principal not seen for bad client keytab name')
+
+success('Client keytab tests')
diff --git a/src/tests/gssapi/t_credstore.c b/src/tests/gssapi/t_credstore.c
new file mode 100644
index 000000000000..0278581ba6e2
--- /dev/null
+++ b/src/tests/gssapi/t_credstore.c
@@ -0,0 +1,138 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright 2011 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+
+static void
+usage(void)
+{
+ fprintf(stderr,
+ "Usage: t_credstore [-sabi] principal [{key value} ...]\n");
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ OM_uint32 minor, major;
+ gss_key_value_set_desc store;
+ gss_name_t name;
+ gss_cred_usage_t cred_usage = GSS_C_BOTH;
+ gss_OID_set mechs = GSS_C_NO_OID_SET;
+ gss_cred_id_t cred = GSS_C_NO_CREDENTIAL;
+ gss_ctx_id_t ictx = GSS_C_NO_CONTEXT, actx = GSS_C_NO_CONTEXT;
+ gss_buffer_desc itok, atok;
+ krb5_boolean store_creds = FALSE, replay = FALSE;
+ char opt;
+
+ /* Parse options. */
+ for (argv++; *argv != NULL && **argv == '-'; argv++) {
+ opt = (*argv)[1];
+ if (opt == 's')
+ store_creds = TRUE;
+ else if (opt == 'r')
+ replay = TRUE;
+ else if (opt == 'a')
+ cred_usage = GSS_C_ACCEPT;
+ else if (opt == 'b')
+ cred_usage = GSS_C_BOTH;
+ else if (opt == 'i')
+ cred_usage = GSS_C_INITIATE;
+ else
+ usage();
+ }
+
+ /* Get the principal name. */
+ if (*argv == NULL)
+ usage();
+ name = import_name(*argv++);
+
+ /* Put any remaining arguments into the store. */
+ store.elements = calloc(argc, sizeof(struct gss_key_value_element_struct));
+ if (!store.elements)
+ errout("OOM");
+ store.count = 0;
+ while (*argv != NULL) {
+ if (*(argv + 1) == NULL)
+ usage();
+ store.elements[store.count].key = *argv;
+ store.elements[store.count].value = *(argv + 1);
+ store.count++;
+ argv += 2;
+ }
+
+ if (store_creds) {
+ /* Acquire default creds and try to store them in the cred store. */
+ major = gss_acquire_cred(&minor, GSS_C_NO_NAME, 0, GSS_C_NO_OID_SET,
+ GSS_C_INITIATE, &cred, NULL, NULL);
+ check_gsserr("gss_acquire_cred", major, minor);
+
+ major = gss_store_cred_into(&minor, cred, GSS_C_INITIATE,
+ GSS_C_NO_OID, 1, 0, &store, NULL, NULL);
+ check_gsserr("gss_store_cred_into", major, minor);
+
+ gss_release_cred(&minor, &cred);
+ }
+
+ /* Try to acquire creds from store. */
+ major = gss_acquire_cred_from(&minor, name, 0, mechs, cred_usage,
+ &store, &cred, NULL, NULL);
+ check_gsserr("gss_acquire_cred_from", major, minor);
+
+ if (replay) {
+ /* Induce a replay using cred as the acceptor cred, to test the replay
+ * cache indicated by the store. */
+ major = gss_init_sec_context(&minor, GSS_C_NO_CREDENTIAL, &ictx, name,
+ &mech_krb5, 0, GSS_C_INDEFINITE,
+ GSS_C_NO_CHANNEL_BINDINGS,
+ GSS_C_NO_BUFFER, NULL, &itok, NULL, NULL);
+ check_gsserr("gss_init_sec_context", major, minor);
+ (void)gss_delete_sec_context(&minor, &ictx, NULL);
+
+ major = gss_accept_sec_context(&minor, &actx, cred, &itok,
+ GSS_C_NO_CHANNEL_BINDINGS, NULL, NULL,
+ &atok, NULL, NULL, NULL);
+ check_gsserr("gss_accept_sec_context(1)", major, minor);
+ (void)gss_release_buffer(&minor, &atok);
+ (void)gss_delete_sec_context(&minor, &actx, NULL);
+
+ major = gss_accept_sec_context(&minor, &actx, cred, &itok,
+ GSS_C_NO_CHANNEL_BINDINGS, NULL, NULL,
+ &atok, NULL, NULL, NULL);
+ check_gsserr("gss_accept_sec_context(2)", major, minor);
+ (void)gss_release_buffer(&minor, &itok);
+ (void)gss_release_buffer(&minor, &atok);
+ (void)gss_delete_sec_context(&minor, &actx, NULL);
+ }
+
+ gss_release_name(&minor, &name);
+ gss_release_cred(&minor, &cred);
+ free(store.elements);
+ return 0;
+}
diff --git a/src/tests/gssapi/t_enctypes.c b/src/tests/gssapi/t_enctypes.c
new file mode 100644
index 000000000000..a2ad18f47aba
--- /dev/null
+++ b/src/tests/gssapi/t_enctypes.c
@@ -0,0 +1,191 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* tests/gssapi/t_enctypes.c - gss_krb5_set_allowable_enctypes test */
+/*
+ * Copyright (C) 2013 by the Massachusetts Institute of Technology.
+ * 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.
+ */
+
+#include "k5-int.h"
+#include "common.h"
+
+/*
+ * This test program establishes contexts with the krb5 mech, the default
+ * initiator name, a specified target name, and the default acceptor name.
+ * Before the exchange, gss_set_allowable_enctypes is called for the initiator
+ * and the acceptor cred if requested. If the exchange is successful, the
+ * resulting contexts are exported with gss_krb5_export_lucid_sec_context,
+ * checked for mismatches, and the GSS protocol and keys are displayed. Exits
+ * with status 0 if all operations are successful, or 1 if not.
+ *
+ * Usage: ./t_enctypes [-i initenctypes] [-a accenctypes] targetname
+ */
+
+static void
+usage()
+{
+ errout("Usage: t_enctypes [-i initenctypes] [-a accenctypes] "
+ "targetname");
+}
+
+/* Error out if ikey is not the same as akey. */
+static void
+check_key_match(gss_krb5_lucid_key_t *ikey, gss_krb5_lucid_key_t *akey)
+{
+ if (ikey->type != akey->type || ikey->length != akey->length ||
+ memcmp(ikey->data, akey->data, ikey->length) != 0)
+ errout("Initiator and acceptor keys do not match");
+}
+
+/* Display the name of enctype. */
+static void
+display_enctype(krb5_enctype enctype)
+{
+ char ename[128];
+
+ if (krb5_enctype_to_name(enctype, FALSE, ename, sizeof(ename)) == 0)
+ fputs(ename, stdout);
+ else
+ fputs("(unknown)", stdout);
+}
+
+int
+main(int argc, char *argv[])
+{
+ krb5_error_code ret;
+ krb5_context kctx = NULL;
+ krb5_enctype *ienc = NULL, *aenc = NULL, zero = 0;
+ OM_uint32 minor, major, flags;
+ gss_name_t tname;
+ gss_cred_id_t icred = GSS_C_NO_CREDENTIAL, acred = GSS_C_NO_CREDENTIAL;
+ gss_ctx_id_t ictx, actx;
+ gss_krb5_lucid_context_v1_t *ilucid, *alucid;
+ gss_krb5_rfc1964_keydata_t *i1964, *a1964;
+ gss_krb5_cfx_keydata_t *icfx, *acfx;
+ size_t count;
+ void *lptr;
+ int c;
+
+ ret = krb5_init_context(&kctx);
+ check_k5err(kctx, "krb5_init_context", ret);
+
+ /* Parse arguments. */
+ while ((c = getopt(argc, argv, "i:a:")) != -1) {
+ switch (c) {
+ case 'i':
+ ret = krb5int_parse_enctype_list(kctx, "", optarg, &zero, &ienc);
+ check_k5err(kctx, "krb5_parse_enctype_list(initiator)", ret);
+ break;
+ case 'a':
+ ret = krb5int_parse_enctype_list(kctx, "", optarg, &zero, &aenc);
+ check_k5err(kctx, "krb5_parse_enctype_list(acceptor)", ret);
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc != 1)
+ usage();
+ tname = import_name(*argv);
+
+ if (ienc != NULL) {
+ major = gss_acquire_cred(&minor, GSS_C_NO_NAME, GSS_C_INDEFINITE,
+ &mechset_krb5, GSS_C_INITIATE, &icred, NULL,
+ NULL);
+ check_gsserr("gss_acquire_cred(initiator)", major, minor);
+
+ for (count = 0; ienc[count]; count++);
+ major = gss_krb5_set_allowable_enctypes(&minor, icred, count, ienc);
+ check_gsserr("gss_krb5_set_allowable_enctypes(init)", major, minor);
+ }
+ if (aenc != NULL) {
+ major = gss_acquire_cred(&minor, GSS_C_NO_NAME, GSS_C_INDEFINITE,
+ &mechset_krb5, GSS_C_ACCEPT, &acred, NULL,
+ NULL);
+ check_gsserr("gss_acquire_cred(acceptor)", major, minor);
+
+ for (count = 0; aenc[count]; count++);
+ major = gss_krb5_set_allowable_enctypes(&minor, acred, count, aenc);
+ check_gsserr("gss_krb5_set_allowable_enctypes(acc)", major, minor);
+ }
+
+ flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG | GSS_C_MUTUAL_FLAG;
+ establish_contexts(&mech_krb5, icred, acred, tname, flags, &ictx, &actx,
+ NULL, NULL, NULL);
+
+ /* Export to lucid contexts. */
+ major = gss_krb5_export_lucid_sec_context(&minor, &ictx, 1, &lptr);
+ check_gsserr("gss_export_lucid_sec_context(initiator)", major, minor);
+ ilucid = lptr;
+ major = gss_krb5_export_lucid_sec_context(&minor, &actx, 1, &lptr);
+ check_gsserr("gss_export_lucid_sec_context(acceptor)", major, minor);
+ alucid = lptr;
+
+ /* Grab the session keys and make sure they match. */
+ if (ilucid->protocol != alucid->protocol)
+ errout("Initiator/acceptor protocol mismatch");
+ if (ilucid->protocol) {
+ icfx = &ilucid->cfx_kd;
+ acfx = &alucid->cfx_kd;
+ if (icfx->have_acceptor_subkey != acfx->have_acceptor_subkey)
+ errout("Initiator/acceptor have_acceptor_subkey mismatch");
+ check_key_match(&icfx->ctx_key, &acfx->ctx_key);
+ if (icfx->have_acceptor_subkey)
+ check_key_match(&icfx->acceptor_subkey, &acfx->acceptor_subkey);
+ fputs("cfx ", stdout);
+ display_enctype(icfx->ctx_key.type);
+ if (icfx->have_acceptor_subkey) {
+ fputs(" ", stdout);
+ display_enctype(icfx->acceptor_subkey.type);
+ }
+ fputs("\n", stdout);
+ } else {
+ i1964 = &ilucid->rfc1964_kd;
+ a1964 = &alucid->rfc1964_kd;
+ if (i1964->sign_alg != a1964->sign_alg ||
+ i1964->seal_alg != a1964->seal_alg)
+ errout("Initiator/acceptor sign or seal alg mismatch");
+ check_key_match(&i1964->ctx_key, &a1964->ctx_key);
+ fputs("rfc1964 ", stdout);
+ display_enctype(i1964->ctx_key.type);
+ fputs("\n", stdout);
+ }
+
+ krb5_free_context(kctx);
+ free(ienc);
+ free(aenc);
+ (void)gss_release_name(&minor, &tname);
+ (void)gss_release_cred(&minor, &icred);
+ (void)gss_release_cred(&minor, &acred);
+ (void)gss_delete_sec_context(&minor, &ictx, NULL);
+ (void)gss_delete_sec_context(&minor, &actx, NULL);
+ (void)gss_krb5_free_lucid_sec_context(&minor, ilucid);
+ (void)gss_krb5_free_lucid_sec_context(&minor, alucid);
+ return 0;
+}
diff --git a/src/tests/gssapi/t_enctypes.py b/src/tests/gssapi/t_enctypes.py
new file mode 100755
index 000000000000..862f229895b2
--- /dev/null
+++ b/src/tests/gssapi/t_enctypes.py
@@ -0,0 +1,149 @@
+#!/usr/bin/python
+from k5test import *
+
+# Define some convenience abbreviations for enctypes we will see in
+# test program output. For background, aes256 and aes128 are "CFX
+# enctypes", meaning that they imply support for RFC 4121, while des3
+# and rc4 are not. DES3 keys will appear as 'des3-cbc-raw' in
+# t_enctypes output because that's how GSSAPI does raw triple-DES
+# encryption without the RFC3961 framing.
+aes256 = 'aes256-cts-hmac-sha1-96'
+aes128 = 'aes128-cts-hmac-sha1-96'
+des3 = 'des3-cbc-sha1'
+des3raw = 'des3-cbc-raw'
+rc4 = 'arcfour-hmac'
+
+# These tests make assumptions about the default enctype lists, so set
+# them explicitly rather than relying on the library defaults.
+enctypes='aes des3 rc4'
+supp='aes256-cts:normal aes128-cts:normal des3-cbc-sha1:normal rc4-hmac:normal'
+conf = {'libdefaults': {
+ 'default_tgs_enctypes': enctypes,
+ 'default_tkt_enctypes': enctypes,
+ 'permitted_enctypes': enctypes},
+ 'realms': {'$realm': {'supported_enctypes': supp}}}
+realm = K5Realm(krb5_conf=conf)
+shutil.copyfile(realm.ccache, os.path.join(realm.testdir, 'save'))
+
+# Return an argument list for running t_enctypes with optional initiator
+# and acceptor enctype lists.
+def cmdline(ienc, aenc):
+ iflags = ienc and ['-i', ienc] or []
+ aflags = aenc and ['-a', aenc] or []
+ return ['./t_enctypes'] + iflags + aflags + ['p:' + realm.host_princ]
+
+
+# Run t_enctypes with optional initiator and acceptor enctype lists,
+# and check that it succeeds with the expected output. Also check
+# that the ticket we got has the expected encryption key and session
+# key.
+def test(msg, ienc, aenc, tktenc='', tktsession='', proto='', isubkey='',
+ asubkey=None):
+ shutil.copyfile(os.path.join(realm.testdir, 'save'), realm.ccache)
+ # Run the test program and check its output.
+ out = realm.run(cmdline(ienc, aenc)).split()
+ if out[0] != proto or out[1] != isubkey:
+ fail(msg)
+ if asubkey is not None and (len(out) < 3 or out[2] != asubkey):
+ fail(msg)
+ lines = realm.run([klist, '-e']).splitlines()
+ for ind, line in enumerate(lines):
+ if realm.host_princ in line:
+ if lines[ind + 1].strip() != ('Etype (skey, tkt): %s, %s' %
+ (tktsession, tktenc)):
+ fail(msg)
+ break
+
+# Run t_enctypes with optional initiator and acceptor enctype lists,
+# and check that it fails with the expected error message.
+def test_err(msg, ienc, aenc, expected_err):
+ shutil.copyfile(os.path.join(realm.testdir, 'save'), realm.ccache)
+ out = realm.run(cmdline(ienc, aenc), expected_code=1)
+ if expected_err not in out:
+ fail(msg)
+
+
+# By default, all of the key enctypes should be aes256.
+test('noargs', None, None,
+ tktenc=aes256, tktsession=aes256,
+ proto='cfx', isubkey=aes256, asubkey=aes256)
+
+# When the initiator constrains the permitted session enctypes to
+# aes128, the ticket encryption key should remain aes256. The client
+# initiator will not send an RFC 4537 upgrade list because it sees no
+# other permitted enctypes, so the acceptor subkey will not be
+# upgraded from aes128.
+test('init aes128', 'aes128-cts', None,
+ tktenc=aes256, tktsession=aes128,
+ proto='cfx', isubkey=aes128, asubkey=aes128)
+
+# If the initiator and acceptor both constrain the permitted session
+# enctypes to aes128, we should see the same keys as above. This
+# tests that the acceptor does not mistakenly contrain the ticket
+# encryption key.
+test('both aes128', 'aes128-cts', 'aes128-cts',
+ tktenc=aes256, tktsession=aes128,
+ proto='cfx', isubkey=aes128, asubkey=aes128)
+
+# If only the acceptor constrains the permitted session enctypes to
+# aes128, subkey negotiation fails because the acceptor considers the
+# aes256 session key to be non-permitted.
+test_err('acc aes128', None, 'aes128-cts', 'Encryption type not permitted')
+
+# If the initiator constrains the permitted session enctypes to des3,
+# no acceptor subkey will be generated because we can't upgrade to a
+# CFX enctype.
+test('init des3', 'des3', None,
+ tktenc=aes256, tktsession=des3,
+ proto='rfc1964', isubkey=des3raw, asubkey=None)
+
+# Force the ticket session key to be rc4, so we can test some subkey
+# upgrade cases. The ticket encryption key remains aes256.
+realm.run([kadminl, 'setstr', realm.host_princ, 'session_enctypes', 'rc4'])
+
+# With no arguments, the initiator should send an upgrade list of
+# [aes256 aes128 des3] and the acceptor should upgrade to an aes256
+# subkey.
+test('upgrade noargs', None, None,
+ tktenc=aes256, tktsession=rc4,
+ proto='cfx', isubkey=rc4, asubkey=aes256)
+
+# If the initiator won't permit rc4 as a session key, it won't be able
+# to get a ticket.
+test_err('upgrade init aes', 'aes', None, 'no support for encryption type')
+
+# If the initiator permits rc4 but prefers aes128, it will send an
+# upgrade list of [aes128] and the acceptor will upgrade to aes128.
+test('upgrade init aes128+rc4', 'aes128-cts rc4', None,
+ tktenc=aes256, tktsession=rc4,
+ proto='cfx', isubkey=rc4, asubkey=aes128)
+
+# If the initiator permits rc4 but prefers des3, it will send an
+# upgrade list of [des3], but the acceptor won't generate a subkey
+# because des3 isn't a CFX enctype.
+test('upgrade init des3+rc4', 'des3 rc4', None,
+ tktenc=aes256, tktsession=rc4,
+ proto='rfc1964', isubkey=rc4, asubkey=None)
+
+# If the acceptor permits only aes128, subkey negotiation will fail
+# because the ticket session key and initiator subkey are
+# non-permitted. (This is unfortunate if the acceptor's restriction
+# is only for the sake of the kernel, since we could upgrade to an
+# aes128 subkey, but it's the current semantics.)
+test_err('upgrade acc aes128', None, 'aes128-cts',
+ 'Encryption type ArcFour with HMAC/md5 not permitted')
+
+# If the acceptor permits rc4 but prefers aes128, it will negotiate an
+# upgrade to aes128.
+test('upgrade acc aes128 rc4', None, 'aes128-cts rc4',
+ tktenc=aes256, tktsession=rc4,
+ proto='cfx', isubkey=rc4, asubkey=aes128)
+
+# In this test, the initiator and acceptor each prefer an AES enctype
+# to rc4, but they can't agree on which one, so no subkey is
+# generated.
+test('upgrade mismatch', 'aes128-cts rc4', 'aes256-cts rc4',
+ tktenc=aes256, tktsession=rc4,
+ proto='rfc1964', isubkey=rc4, asubkey=None)
+
+success('gss_krb5_set_allowable_enctypes tests')
diff --git a/src/tests/gssapi/t_err.c b/src/tests/gssapi/t_err.c
new file mode 100644
index 000000000000..b7c32b463f93
--- /dev/null
+++ b/src/tests/gssapi/t_err.c
@@ -0,0 +1,121 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* tests/gssapi/t_err.c - Test accept_sec_context error generation */
+/*
+ * Copyright (C) 2013 by the Massachusetts Institute of Technology.
+ * 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.
+ */
+
+/*
+ * This test program verifies that the krb5 gss_accept_sec_context can produce
+ * error tokens and that gss_init_sec_context can interpret them.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "common.h"
+
+static void
+check_replay_error(const char *msg, OM_uint32 major, OM_uint32 minor)
+{
+ OM_uint32 tmpmin, msg_ctx = 0;
+ const char *replay = "Request is a replay";
+ gss_buffer_desc m;
+
+ if (major != GSS_S_FAILURE) {
+ fprintf(stderr, "%s: expected major code GSS_S_FAILURE\n", msg);
+ check_gsserr(msg, major, minor);
+ exit(1);
+ }
+
+ (void)gss_display_status(&tmpmin, minor, GSS_C_MECH_CODE, GSS_C_NULL_OID,
+ &msg_ctx, &m);
+ if (m.length != strlen(replay) || memcmp(m.value, replay, m.length) != 0) {
+ fprintf(stderr, "%s: expected replay error; got %.*s\n", msg,
+ (int)m.length, (char *)m.value);
+ exit(1);
+ }
+ (void)gss_release_buffer(&tmpmin, &m);
+}
+
+int
+main(int argc, char *argv[])
+{
+ OM_uint32 minor, major, flags;
+ gss_OID mech = &mech_krb5;
+ gss_name_t tname;
+ gss_buffer_desc itok, atok;
+ gss_ctx_id_t ictx = GSS_C_NO_CONTEXT, actx = GSS_C_NO_CONTEXT;
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: %s targetname\n", argv[0]);
+ return 1;
+ }
+ tname = import_name(argv[1]);
+
+ /* Get the initial context token. */
+ flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG | GSS_C_MUTUAL_FLAG;
+ major = gss_init_sec_context(&minor, GSS_C_NO_CREDENTIAL, &ictx, tname,
+ mech, flags, GSS_C_INDEFINITE,
+ GSS_C_NO_CHANNEL_BINDINGS, GSS_C_NO_BUFFER,
+ NULL, &itok, NULL, NULL);
+ check_gsserr("gss_init_sec_context(1)", major, minor);
+ assert(major == GSS_S_CONTINUE_NEEDED);
+
+ /* Process this token into an acceptor context, then discard it. */
+ major = gss_accept_sec_context(&minor, &actx, GSS_C_NO_CREDENTIAL, &itok,
+ GSS_C_NO_CHANNEL_BINDINGS, NULL,
+ NULL, &atok, NULL, NULL, NULL);
+ check_gsserr("gss_accept_sec_context(1)", major, minor);
+ (void)gss_release_buffer(&minor, &atok);
+ (void)gss_delete_sec_context(&minor, &actx, NULL);
+
+ /* Process the same token again, producing a replay error. */
+ major = gss_accept_sec_context(&minor, &actx, GSS_C_NO_CREDENTIAL, &itok,
+ GSS_C_NO_CHANNEL_BINDINGS, NULL,
+ NULL, &atok, NULL, NULL, NULL);
+ check_replay_error("gss_accept_sec_context(2)", major, minor);
+ assert(atok.length != 0);
+
+ /* Send the error token back the initiator. */
+ (void)gss_release_buffer(&minor, &itok);
+ major = gss_init_sec_context(&minor, GSS_C_NO_CREDENTIAL, &ictx, tname,
+ mech, flags, GSS_C_INDEFINITE,
+ GSS_C_NO_CHANNEL_BINDINGS, &atok,
+ NULL, &itok, NULL, NULL);
+ check_replay_error("gss_init_sec_context(2)", major, minor);
+
+ (void)gss_release_name(&minor, &tname);
+ (void)gss_release_buffer(&minor, &itok);
+ (void)gss_release_buffer(&minor, &atok);
+ (void)gss_delete_sec_context(&minor, &ictx, NULL);
+ (void)gss_delete_sec_context(&minor, &actx, NULL);
+ return 0;
+}
diff --git a/src/tests/gssapi/t_export_cred.c b/src/tests/gssapi/t_export_cred.c
new file mode 100644
index 000000000000..4d7c028e6d46
--- /dev/null
+++ b/src/tests/gssapi/t_export_cred.c
@@ -0,0 +1,115 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright 2011 by the Massachusetts Institute of Technology.
+ * 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 M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "common.h"
+
+/* Display a usage error message and exit. */
+static void
+usage(void)
+{
+ fprintf(stderr, "Usage: t_export_cred [-k|-s] [-i initiatorname] "
+ "[-a acceptorname] targetname\n");
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ OM_uint32 major, minor, flags;
+ gss_name_t initiator_name = GSS_C_NO_NAME, acceptor_name = GSS_C_NO_NAME;
+ gss_name_t target_name;
+ gss_cred_id_t initiator_cred, acceptor_cred, delegated_cred;
+ gss_ctx_id_t initiator_context = GSS_C_NO_CONTEXT;
+ gss_ctx_id_t acceptor_context = GSS_C_NO_CONTEXT;
+ gss_OID mech = GSS_C_NO_OID;
+ gss_OID_set mechs = GSS_C_NO_OID_SET;
+ char optchar;
+
+ /* Parse arguments. */
+ argv++;
+ while (*argv != NULL && **argv == '-') {
+ optchar = (*argv)[1];
+ argv++;
+ if (optchar == 'i') {
+ if (*argv == NULL)
+ usage();
+ initiator_name = import_name(*argv++);
+ } else if (optchar == 'a') {
+ if (*argv == NULL)
+ usage();
+ acceptor_name = import_name(*argv++);
+ } else if (optchar == 'k') {
+ mech = &mech_krb5;
+ mechs = &mechset_krb5;
+ } else if (optchar == 's') {
+ mech = &mech_spnego;
+ mechs = &mechset_spnego;
+ } else {
+ usage();
+ }
+ }
+ if (*argv == NULL || *(argv + 1) != NULL)
+ usage();
+ target_name = import_name(argv[0]);
+
+ /* Get initiator cred and export/import it. */
+ major = gss_acquire_cred(&minor, initiator_name, GSS_C_INDEFINITE, mechs,
+ GSS_C_INITIATE, &initiator_cred, NULL, NULL);
+ check_gsserr("gss_acquire_cred(initiator)", major, minor);
+ export_import_cred(&initiator_cred);
+
+ /* Get acceptor cred and export/import it. */
+ major = gss_acquire_cred(&minor, acceptor_name, GSS_C_INDEFINITE, mechs,
+ GSS_C_ACCEPT, &acceptor_cred, NULL, NULL);
+ check_gsserr("gss_acquire_cred(acceptor)", major, minor);
+ export_import_cred(&acceptor_cred);
+
+ /* Initiate and accept a security context (one-token exchange only),
+ * delegating credentials. */
+ flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG | GSS_C_CONF_FLAG |
+ GSS_C_INTEG_FLAG | GSS_C_DELEG_FLAG;
+ establish_contexts(mech, initiator_cred, acceptor_cred, target_name, flags,
+ &initiator_context, &acceptor_context, NULL, NULL,
+ &delegated_cred);
+
+ /* Import, release, export, and store delegated creds */
+ export_import_cred(&delegated_cred);
+ major = gss_store_cred(&minor, delegated_cred, GSS_C_INITIATE,
+ GSS_C_NULL_OID, 1, 1, NULL, NULL);
+ check_gsserr("gss_store_cred", major, minor);
+
+ (void)gss_release_name(&minor, &initiator_name);
+ (void)gss_release_name(&minor, &acceptor_name);
+ (void)gss_release_name(&minor, &target_name);
+ (void)gss_release_cred(&minor, &initiator_cred);
+ (void)gss_release_cred(&minor, &acceptor_cred);
+ (void)gss_release_cred(&minor, &delegated_cred);
+ (void)gss_delete_sec_context(&minor, &initiator_context, NULL);
+ (void)gss_delete_sec_context(&minor, &acceptor_context, NULL);
+ return 0;
+}
diff --git a/src/tests/gssapi/t_export_cred.py b/src/tests/gssapi/t_export_cred.py
new file mode 100755
index 000000000000..698835928901
--- /dev/null
+++ b/src/tests/gssapi/t_export_cred.py
@@ -0,0 +1,53 @@
+#!/usr/bin/python
+from k5test import *
+
+# Test gss_export_cred and gss_import_cred for initiator creds,
+# acceptor creds, and traditional delegated creds. t_s4u.py tests
+# exporting and importing a synthesized S4U2Proxy delegated
+# credential.
+
+# Make up a filename to hold user's initial credentials.
+def ccache_savefile(realm):
+ return os.path.join(realm.testdir, 'ccache.copy')
+
+# Move user's initial credentials into the save file.
+def ccache_save(realm):
+ os.rename(realm.ccache, ccache_savefile(realm))
+
+# Copy user's initial credentials from the save file into the ccache.
+def ccache_restore(realm):
+ shutil.copyfile(ccache_savefile(realm), realm.ccache)
+
+# Run t_export_cred with the saved ccache and verify that it stores a
+# forwarded cred into the default ccache.
+def check(realm, args):
+ ccache_restore(realm)
+ realm.run(['./t_export_cred'] + args)
+ output = realm.run([klist, '-f'])
+ if 'Flags: Ff' not in output:
+ fail('Forwarded tickets not found in ccache after t_export_cred')
+
+# Check a given set of arguments with no specified mech and with krb5
+# and SPNEGO as the specified mech.
+def check_mechs(realm, args):
+ check(realm, args)
+ check(realm, ['-k'] + args)
+ check(realm, ['-s'] + args)
+
+# Make a realm, get forwardable tickets, and save a copy for each test.
+realm = K5Realm(get_creds=False)
+realm.kinit(realm.user_princ, password('user'), ['-f'])
+ccache_save(realm)
+
+# Test with default initiator and acceptor cred.
+tname = 'p:' + realm.host_princ
+check_mechs(realm, [tname])
+
+# Test with principal-named initiator and acceptor cred.
+iname = 'p:' + realm.user_princ
+check_mechs(realm, ['-i', iname, '-a', tname, tname])
+
+# Test with host-based acceptor cred.
+check_mechs(realm, ['-a', 'h:host', tname])
+
+success('gss_export_cred/gss_import_cred tests')
diff --git a/src/tests/gssapi/t_export_name.c b/src/tests/gssapi/t_export_name.c
new file mode 100644
index 000000000000..b7eebd4eff89
--- /dev/null
+++ b/src/tests/gssapi/t_export_name.c
@@ -0,0 +1,119 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* tests/gssapi/t_export_name.c - Test program for gss_export_name behavior */
+/*
+ * Copyright 2012 by the Massachusetts Institute of Technology.
+ * 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 M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+/*
+ * Test program for gss_export_name, intended to be run from a Python test
+ * script. Imports a name, canonicalizes it to a mech, exports it,
+ * re-imports/exports it to compare results, and then prints the hex form of
+ * the exported name followed by a newline.
+ *
+ * Usage: ./t_export_name [-k|-s] user:username|krb5:princ|host:service@host
+ *
+ * The name is imported as a username, krb5 principal, or hostbased name.
+ * By default or with -k, the name is canonicalized to the krb5 mech; -s
+ * indicates SPNEGO instead.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+
+static void
+usage(void)
+{
+ fprintf(stderr, "Usage: t_export_name [-k|-s] name\n");
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ OM_uint32 minor, major;
+ gss_OID mech = (gss_OID)gss_mech_krb5;
+ gss_name_t name, mechname, impname;
+ gss_buffer_desc buf, buf2;
+ krb5_boolean use_composite = FALSE;
+ gss_OID ntype;
+ const char *name_arg;
+ char opt;
+
+ /* Parse arguments. */
+ while (argc > 1 && argv[1][0] == '-') {
+ opt = argv[1][1];
+ argc--, argv++;
+ if (opt == 'k')
+ mech = &mech_krb5;
+ else if (opt == 's')
+ mech = &mech_spnego;
+ else if (opt == 'c')
+ use_composite = TRUE;
+ else
+ usage();
+ }
+ if (argc != 2)
+ usage();
+ name_arg = argv[1];
+
+ /* Import the name. */
+ name = import_name(name_arg);
+
+ /* Canonicalize and export the name. */
+ major = gss_canonicalize_name(&minor, name, mech, &mechname);
+ check_gsserr("gss_canonicalize_name", major, minor);
+ if (use_composite)
+ major = gss_export_name_composite(&minor, mechname, &buf);
+ else
+ major = gss_export_name(&minor, mechname, &buf);
+ check_gsserr("gss_export_name", major, minor);
+
+ /* Import and re-export the name, and compare the results. */
+ ntype = use_composite ? GSS_C_NT_COMPOSITE_EXPORT : GSS_C_NT_EXPORT_NAME;
+ major = gss_import_name(&minor, &buf, ntype, &impname);
+ check_gsserr("gss_import_name", major, minor);
+ if (use_composite)
+ major = gss_export_name_composite(&minor, impname, &buf2);
+ else
+ major = gss_export_name(&minor, impname, &buf2);
+ check_gsserr("gss_export_name", major, minor);
+ if (buf.length != buf2.length ||
+ memcmp(buf.value, buf2.value, buf.length) != 0) {
+ fprintf(stderr, "Mismatched results:\n");
+ print_hex(stderr, &buf);
+ print_hex(stderr, &buf2);
+ return 1;
+ }
+
+ print_hex(stdout, &buf);
+
+ (void)gss_release_name(&minor, &name);
+ (void)gss_release_name(&minor, &mechname);
+ (void)gss_release_name(&minor, &impname);
+ (void)gss_release_buffer(&minor, &buf);
+ (void)gss_release_buffer(&minor, &buf2);
+ return 0;
+}
diff --git a/src/tests/gssapi/t_gssapi.py b/src/tests/gssapi/t_gssapi.py
new file mode 100755
index 000000000000..e23c936d7f44
--- /dev/null
+++ b/src/tests/gssapi/t_gssapi.py
@@ -0,0 +1,223 @@
+#!/usr/bin/python
+from k5test import *
+
+# Test krb5 negotiation under SPNEGO for all enctype configurations. Also
+# test IOV wrap/unwrap with and without SPNEGO.
+for realm in multipass_realms():
+ realm.run(['./t_spnego','p:' + realm.host_princ, realm.keytab])
+ realm.run(['./t_iov', 'p:' + realm.host_princ])
+ realm.run(['./t_iov', '-s', 'p:' + realm.host_princ])
+ realm.run(['./t_pcontok', 'p:' + realm.host_princ])
+
+### Test acceptor name behavior.
+
+realm = K5Realm()
+
+# Create some host-based principals and put most of them into the
+# keytab. Rename one principal so that the keytab name matches the
+# key but not the client name.
+realm.run([kadminl, 'addprinc', '-randkey', 'service1/abraham'])
+realm.run([kadminl, 'addprinc', '-randkey', 'service1/barack'])
+realm.run([kadminl, 'addprinc', '-randkey', 'service2/calvin'])
+realm.run([kadminl, 'addprinc', '-randkey', 'service2/dwight'])
+realm.run([kadminl, 'addprinc', '-randkey', 'host/-nomatch-'])
+realm.run([kadminl, 'xst', 'service1/abraham'])
+realm.run([kadminl, 'xst', 'service1/barack'])
+realm.run([kadminl, 'xst', 'service2/calvin'])
+realm.run([kadminl, 'renprinc', 'service1/abraham', 'service1/andrew'])
+
+# Test with no acceptor name, including client/keytab principal
+# mismatch (non-fatal) and missing keytab entry (fatal).
+output = realm.run(['./t_accname', 'p:service1/andrew'])
+if 'service1/abraham' not in output:
+ fail('Expected service1/abraham in t_accname output')
+output = realm.run(['./t_accname', 'p:service1/barack'])
+if 'service1/barack' not in output:
+ fail('Expected service1/barack in t_accname output')
+output = realm.run(['./t_accname', 'p:service2/calvin'])
+if 'service2/calvin' not in output:
+ fail('Expected service1/barack in t_accname output')
+output = realm.run(['./t_accname', 'p:service2/dwight'], expected_code=1)
+if ' not found in keytab' not in output:
+ fail('Expected error message not seen in t_accname output')
+
+# Test with acceptor name containing service only, including
+# client/keytab hostname mismatch (non-fatal) and service name
+# mismatch (fatal).
+output = realm.run(['./t_accname', 'p:service1/andrew', 'h:service1'])
+if 'service1/abraham' not in output:
+ fail('Expected service1/abraham in t_accname output')
+output = realm.run(['./t_accname', 'p:service1/andrew', 'h:service2'],
+ expected_code=1)
+if ' not found in keytab' not in output:
+ fail('Expected error message not seen in t_accname output')
+output = realm.run(['./t_accname', 'p:service2/calvin', 'h:service2'])
+if 'service2/calvin' not in output:
+ fail('Expected service2/calvin in t_accname output')
+output = realm.run(['./t_accname', 'p:service2/calvin', 'h:service1'],
+ expected_code=1)
+if ' found in keytab but does not match server principal' not in output:
+ fail('Expected error message not seen in t_accname output')
+
+# Test with acceptor name containing service and host. Use the
+# client's un-canonicalized hostname as acceptor input to mirror what
+# many servers do.
+output = realm.run(['./t_accname', 'p:' + realm.host_princ,
+ 'h:host@%s' % socket.gethostname()])
+if realm.host_princ not in output:
+ fail('Expected %s in t_accname output' % realm.host_princ)
+output = realm.run(['./t_accname', 'p:host/-nomatch-',
+ 'h:host@%s' % socket.gethostname()],
+ expected_code=1)
+if ' not found in keytab' not in output:
+ fail('Expected error message not seen in t_accname output')
+
+# Test krb5_gss_import_cred.
+realm.run(['./t_imp_cred', 'p:service1/barack'])
+realm.run(['./t_imp_cred', 'p:service1/barack', 'service1/barack'])
+realm.run(['./t_imp_cred', 'p:service1/andrew', 'service1/abraham'])
+output = realm.run(['./t_imp_cred', 'p:service2/dwight'], expected_code=1)
+if ' not found in keytab' not in output:
+ fail('Expected error message not seen in t_imp_cred output')
+
+# Test credential store extension.
+tmpccname = 'FILE:' + os.path.join(realm.testdir, 'def_cache')
+realm.env['KRB5CCNAME'] = tmpccname
+storagecache = 'FILE:' + os.path.join(realm.testdir, 'user_store')
+servicekeytab = os.path.join(realm.testdir, 'kt')
+service_cs = 'service/cs@%s' % realm.realm
+realm.addprinc(service_cs)
+realm.extract_keytab(service_cs, servicekeytab)
+realm.kinit(service_cs, None, ['-k', '-t', servicekeytab])
+realm.run(['./t_credstore', '-s', 'p:' + service_cs, 'ccache', storagecache,
+ 'keytab', servicekeytab])
+
+# Test rcache feature of cred stores. t_credstore -r should produce a
+# replay error normally, but not with rcache set to "none:".
+output = realm.run(['./t_credstore', '-r', '-a', 'p:' + realm.host_princ],
+ expected_code=1)
+if 'gss_accept_sec_context(2): Request is a replay' not in output:
+ fail('Expected replay error not seen in t_credstore output')
+realm.run(['./t_credstore', '-r', '-a', 'p:' + realm.host_princ,
+ 'rcache', 'none:'])
+
+# Verify that we can't acquire acceptor creds without a keytab.
+os.remove(realm.keytab)
+output = realm.run(['./t_accname', 'p:abc'], expected_code=1)
+if ('gss_acquire_cred: Keytab' not in output or
+ 'nonexistent or empty' not in output):
+ fail('Expected error message not seen for nonexistent keytab')
+
+realm.stop()
+
+# Re-run the last acceptor name test with ignore_acceptor_hostname set
+# and the principal for the mismatching hostname in the keytab.
+ignore_conf = {'libdefaults': {'ignore_acceptor_hostname': 'true'}}
+realm = K5Realm(krb5_conf=ignore_conf)
+realm.run([kadminl, 'addprinc', '-randkey', 'host/-nomatch-'])
+realm.run([kadminl, 'xst', 'host/-nomatch-'])
+output = realm.run(['./t_accname', 'p:host/-nomatch-',
+ 'h:host@%s' % socket.gethostname()])
+if 'host/-nomatch-' not in output:
+ fail('Expected host/-nomatch- in t_accname output')
+
+realm.stop()
+
+# Make sure a GSSAPI acceptor can handle cross-realm tickets with a
+# transited field. (Regression test for #7639.)
+r1, r2, r3 = cross_realms(3, xtgts=((0,1), (1,2)),
+ create_user=False, create_host=False,
+ args=[{'realm': 'A.X', 'create_user': True},
+ {'realm': 'X'},
+ {'realm': 'B.X', 'create_host': True}])
+os.rename(r3.keytab, r1.keytab)
+r1.run(['./t_accname', 'p:' + r3.host_princ, 'h:host'])
+r1.stop()
+r2.stop()
+r3.stop()
+
+### Test gss_inquire_cred behavior.
+
+realm = K5Realm()
+
+# Test deferred resolution of the default ccache for initiator creds.
+output = realm.run(['./t_inq_cred'])
+if realm.user_princ not in output:
+ fail('Expected %s in t_inq_cred output' % realm.user_princ)
+output = realm.run(['./t_inq_cred', '-k'])
+if realm.user_princ not in output:
+ fail('Expected %s in t_inq_cred output' % realm.user_princ)
+output = realm.run(['./t_inq_cred', '-s'])
+if realm.user_princ not in output:
+ fail('Expected %s in t_inq_cred output' % realm.user_princ)
+
+# Test picking a name from the keytab for acceptor creds.
+output = realm.run(['./t_inq_cred', '-a'])
+if realm.host_princ not in output:
+ fail('Expected %s in t_inq_cred output' % realm.host_princ)
+output = realm.run(['./t_inq_cred', '-k', '-a'])
+if realm.host_princ not in output:
+ fail('Expected %s in t_inq_cred output' % realm.host_princ)
+output = realm.run(['./t_inq_cred', '-s', '-a'])
+if realm.host_princ not in output:
+ fail('Expected %s in t_inq_cred output' % realm.host_princ)
+
+# Test client keytab initiation (non-deferred) with a specified name.
+realm.extract_keytab(realm.user_princ, realm.client_keytab)
+os.remove(realm.ccache)
+output = realm.run(['./t_inq_cred', '-k'])
+if realm.user_princ not in output:
+ fail('Expected %s in t_inq_cred output' % realm.user_princ)
+
+# Test deferred client keytab initiation and GSS_C_BOTH cred usage.
+os.remove(realm.client_keytab)
+os.remove(realm.ccache)
+shutil.copyfile(realm.keytab, realm.client_keytab)
+output = realm.run(['./t_inq_cred', '-k', '-b'])
+if realm.host_princ not in output:
+ fail('Expected %s in t_inq_cred output' % realm.host_princ)
+
+# Test gss_export_name behavior.
+out = realm.run(['./t_export_name', 'u:x'])
+if out != '0401000B06092A864886F7120102020000000D78404B5242544553542E434F4D\n':
+ fail('Unexpected output from t_export_name (krb5 username)')
+output = realm.run(['./t_export_name', '-s', 'u:xyz'])
+if output != '0401000806062B06010505020000000378797A\n':
+ fail('Unexpected output from t_export_name (SPNEGO username)')
+output = realm.run(['./t_export_name', 'p:a@b'])
+if output != '0401000B06092A864886F71201020200000003614062\n':
+ fail('Unexpected output from t_export_name (krb5 principal)')
+output = realm.run(['./t_export_name', '-s', 'p:a@b'])
+if output != '0401000806062B060105050200000003614062\n':
+ fail('Unexpected output from t_export_name (SPNEGO krb5 principal)')
+
+# Test that composite-export tokens can be imported.
+output = realm.run(['./t_export_name', '-c', 'p:a@b'])
+if (output != '0402000B06092A864886F7120102020000000361406200000000\n'):
+ fail('Unexpected output from t_export_name (using COMPOSITE_EXPORT)')
+
+# Test gss_inquire_mechs_for_name behavior.
+krb5_mech = '{ 1 2 840 113554 1 2 2 }'
+spnego_mech = '{ 1 3 6 1 5 5 2 }'
+out = realm.run(['./t_inq_mechs_name', 'p:a@b'])
+if krb5_mech not in out:
+ fail('t_inq_mechs_name (principal)')
+out = realm.run(['./t_inq_mechs_name', 'u:x'])
+if krb5_mech not in out or spnego_mech not in out:
+ fail('t_inq_mecs_name (user)')
+out = realm.run(['./t_inq_mechs_name', 'h:host'])
+if krb5_mech not in out or spnego_mech not in out:
+ fail('t_inq_mecs_name (hostbased)')
+
+# Test that accept_sec_context can produce an error token and
+# init_sec_context can interpret it.
+realm.run(['./t_err', 'p:' + realm.host_princ])
+
+# Test the GSS_KRB5_CRED_NO_CI_FLAGS_X cred option.
+realm.run(['./t_ciflags', 'p:' + realm.host_princ])
+
+# Test that inquire_context works properly, even on incomplete
+# contexts.
+realm.run(['./t_inq_ctx', 'user', password('user'), 'p:%s' % realm.host_princ])
+
+success('GSSAPI tests')
diff --git a/src/tests/gssapi/t_gssexts.c b/src/tests/gssapi/t_gssexts.c
new file mode 100644
index 000000000000..41d62b9262e6
--- /dev/null
+++ b/src/tests/gssapi/t_gssexts.c
@@ -0,0 +1,247 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright 2009 by the Massachusetts Institute of Technology.
+ * 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 M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+
+/*
+ * Test program for protocol transition (S4U2Self) and constrained delegation
+ * (S4U2Proxy)
+ *
+ * Note: because of name canonicalization, the following tips may help
+ * when configuring with Active Directory:
+ *
+ * - Create a computer account FOO$
+ * - Set the UPN to host/foo.domain (no suffix); this is necessary to
+ * be able to send an AS-REQ as this principal, otherwise you would
+ * need to use the canonical name (FOO$), which will cause principal
+ * comparison errors in gss_accept_sec_context().
+ * - Add a SPN of host/foo.domain
+ * - Configure the computer account to support constrained delegation with
+ * protocol transition (Trust this computer for delegation to specified
+ * services only / Use any authentication protocol)
+ * - Add host/foo.domain to the keytab (possibly easiest to do this
+ * with ktadd)
+ *
+ * For S4U2Proxy to work the TGT must be forwardable too.
+ *
+ * Usage eg:
+ *
+ * kinit -k -t test.keytab -f 'host/test.win.mit.edu@WIN.MIT.EDU'
+ * ./t_s4u p:delegtest@WIN.MIT.EDU p:HOST/WIN-EQ7E4AA2WR8.win.mit.edu@WIN.MIT.EDU test.keytab
+ */
+
+static int use_spnego = 0;
+
+static void
+test_prf(gss_ctx_id_t initiatorContext, gss_ctx_id_t acceptorContext,
+ int flags)
+{
+ gss_buffer_desc constant;
+ OM_uint32 major, minor;
+ unsigned int i;
+ gss_buffer_desc initiatorPrf;
+ gss_buffer_desc acceptorPrf;
+
+ constant.value = "gss prf test";
+ constant.length = strlen((char *)constant.value);
+
+ initiatorPrf.value = NULL;
+ acceptorPrf.value = NULL;
+
+ major = gss_pseudo_random(&minor, initiatorContext, flags, &constant, 19,
+ &initiatorPrf);
+ check_gsserr("gss_pseudo_random", major, minor);
+
+ printf("%s\n", flags == GSS_C_PRF_KEY_FULL ?
+ "PRF_KEY_FULL" : "PRF_KEY_PARTIAL");
+
+ printf("Initiator PRF: ");
+ for (i = 0; i < initiatorPrf.length; i++)
+ printf("%02x ", ((char *)initiatorPrf.value)[i] & 0xFF);
+ printf("\n");
+
+ major = gss_pseudo_random(&minor, acceptorContext, flags, &constant, 19,
+ &acceptorPrf);
+ check_gsserr("gss_pseudo_random", major, minor);
+
+ printf("Acceptor PRF: ");
+ for (i = 0; i < acceptorPrf.length; i++)
+ printf("%02x ", ((char *)acceptorPrf.value)[i] & 0xFF);
+ printf("\n");
+
+ if (acceptorPrf.length != initiatorPrf.length ||
+ memcmp(acceptorPrf.value, initiatorPrf.value, initiatorPrf.length)) {
+ fprintf(stderr, "Initiator and acceptor PRF output does not match\n");
+ exit(1);
+ }
+
+ (void)gss_release_buffer(&minor, &initiatorPrf);
+ (void)gss_release_buffer(&minor, &acceptorPrf);
+}
+
+static void
+init_accept_sec_context(gss_cred_id_t claimant_cred_handle,
+ gss_cred_id_t verifier_cred_handle,
+ gss_cred_id_t *deleg_cred_handle)
+{
+ OM_uint32 major, minor, flags;
+ gss_name_t source_name = GSS_C_NO_NAME, target_name = GSS_C_NO_NAME;
+ gss_ctx_id_t initiator_context, acceptor_context;
+ gss_OID mech;
+
+ *deleg_cred_handle = GSS_C_NO_CREDENTIAL;
+
+ major = gss_inquire_cred(&minor, verifier_cred_handle, &target_name, NULL,
+ NULL, NULL);
+ check_gsserr("gss_inquire_cred", major, minor);
+ display_canon_name("Target name", target_name, &mech_krb5);
+
+ mech = use_spnego ? &mech_spnego : &mech_krb5;
+ display_oid("Target mech", mech);
+
+ flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG;
+ establish_contexts(mech, claimant_cred_handle, verifier_cred_handle,
+ target_name, flags, &initiator_context,
+ &acceptor_context, &source_name, NULL,
+ deleg_cred_handle);
+
+ test_prf(initiator_context, acceptor_context, GSS_C_PRF_KEY_FULL);
+ test_prf(initiator_context, acceptor_context, GSS_C_PRF_KEY_PARTIAL);
+
+ (void)gss_release_name(&minor, &source_name);
+ (void)gss_delete_sec_context(&minor, &acceptor_context, NULL);
+ (void)gss_delete_sec_context(&minor, &initiator_context, NULL);
+}
+
+static void
+get_default_cred(const char *keytab_name, gss_OID_set mechs,
+ gss_cred_id_t *impersonator_cred_handle)
+{
+ OM_uint32 major = GSS_S_FAILURE, minor;
+ krb5_error_code ret;
+ krb5_context context = NULL;
+ krb5_keytab keytab = NULL;
+ krb5_principal keytab_principal = NULL;
+ krb5_ccache ccache = NULL;
+
+ if (keytab_name != NULL) {
+ ret = krb5_init_context(&context);
+ check_k5err(context, "krb5_init_context", ret);
+
+ ret = krb5_kt_resolve(context, keytab_name, &keytab);
+ check_k5err(context, "krb5_kt_resolve", ret);
+
+ ret = krb5_cc_default(context, &ccache);
+ check_k5err(context, "krb5_cc_default", ret);
+
+ ret = krb5_cc_get_principal(context, ccache, &keytab_principal);
+ check_k5err(context, "krb5_cc_get_principal", ret);
+
+ major = gss_krb5_import_cred(&minor, ccache, keytab_principal, keytab,
+ impersonator_cred_handle);
+ check_gsserr("gss_krb5_import_cred", major, minor);
+
+ krb5_free_principal(context, keytab_principal);
+ krb5_cc_close(context, ccache);
+ krb5_kt_close(context, keytab);
+ krb5_free_context(context);
+ } else {
+ major = gss_acquire_cred(&minor, GSS_C_NO_NAME, GSS_C_INDEFINITE,
+ mechs, GSS_C_BOTH, impersonator_cred_handle,
+ NULL, NULL);
+ check_gsserr("gss_acquire_cred", major, minor);
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ OM_uint32 minor, major;
+ gss_cred_id_t impersonator_cred_handle = GSS_C_NO_CREDENTIAL;
+ gss_cred_id_t user_cred_handle = GSS_C_NO_CREDENTIAL;
+ gss_cred_id_t delegated_cred_handle = GSS_C_NO_CREDENTIAL;
+ gss_name_t user = GSS_C_NO_NAME, target = GSS_C_NO_NAME;
+ gss_OID_set mechs, actual_mechs = GSS_C_NO_OID_SET;
+ uid_t uid;
+
+ if (argc < 2 || argc > 5) {
+ fprintf(stderr, "Usage: %s [--spnego] [user] "
+ "[proxy-target] [keytab]\n", argv[0]);
+ fprintf(stderr, " proxy-target and keytab are optional\n");
+ exit(1);
+ }
+
+ if (strcmp(argv[1], "--spnego") == 0) {
+ use_spnego++;
+ argc--;
+ argv++;
+ }
+
+ user = import_name(argv[1]);
+
+ major = gss_pname_to_uid(&minor, user, NULL, &uid);
+ check_gsserr("gss_pname_to_uid(user)", major, minor);
+
+ if (argc > 2 && strcmp(argv[2], "-") != 0)
+ target = import_name(argv[2]);
+
+ mechs = use_spnego ? &mechset_spnego : &mechset_krb5;
+
+ get_default_cred((argc > 3) ? argv[3] : NULL, mechs,
+ &impersonator_cred_handle);
+
+ printf("Protocol transition tests follow\n");
+ printf("-----------------------------------\n\n");
+
+ /* get S4U2Self cred */
+ major = gss_acquire_cred_impersonate_name(&minor, impersonator_cred_handle,
+ user, GSS_C_INDEFINITE, mechs,
+ GSS_C_INITIATE,
+ &user_cred_handle, &actual_mechs,
+ NULL);
+ check_gsserr("gss_acquire_cred_impersonate_name", major, minor);
+
+ /* Try to store it in default ccache */
+ major = gss_store_cred(&minor, user_cred_handle, GSS_C_INITIATE,
+ &mechs->elements[0], 1, 1, NULL, NULL);
+ check_gsserr("gss_store_cred", major, minor);
+
+ init_accept_sec_context(user_cred_handle, impersonator_cred_handle,
+ &delegated_cred_handle);
+
+ printf("\n");
+
+ (void)gss_release_name(&minor, &user);
+ (void)gss_release_name(&minor, &target);
+ (void)gss_release_cred(&minor, &delegated_cred_handle);
+ (void)gss_release_cred(&minor, &impersonator_cred_handle);
+ (void)gss_release_cred(&minor, &user_cred_handle);
+ (void)gss_release_oid_set(&minor, &actual_mechs);
+ return 0;
+}
diff --git a/src/tests/gssapi/t_imp_cred.c b/src/tests/gssapi/t_imp_cred.c
new file mode 100644
index 000000000000..a2aa5fbaed26
--- /dev/null
+++ b/src/tests/gssapi/t_imp_cred.c
@@ -0,0 +1,101 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* tests/gssapi/t_imp_cred.c - krb5_gss_import_cred test harness */
+/*
+ * Copyright 2011 by the Massachusetts Institute of Technology.
+ * 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 M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+/*
+ * Test program for krb5_gss_import_cred, intended to be run from a Python test
+ * script. Creates an initiator credential for the default ccache and an
+ * acceptor principal for the default keytab (possibly using a specified keytab
+ * principal), and performs a one-token context exchange using a specified
+ * target principal. If the exchange is successful, queries the context for
+ * the acceptor name and prints it. If any call is unsuccessful, displays an
+ * error message. Exits with status 0 if all operations are successful, or 1
+ * if not.
+ *
+ * Usage: ./t_imp_cred target-princ [keytab-princ]
+ */
+
+#include "k5-platform.h"
+#include <krb5.h>
+
+#include "common.h"
+
+int
+main(int argc, char *argv[])
+{
+ OM_uint32 minor, major, flags;
+ gss_cred_id_t initiator_cred, acceptor_cred;
+ gss_ctx_id_t initiator_context, acceptor_context;
+ gss_name_t target_name;
+ krb5_context context = NULL;
+ krb5_ccache cc;
+ krb5_keytab kt;
+ krb5_principal princ = NULL;
+ krb5_error_code ret;
+
+ if (argc < 2 || argc > 3) {
+ fprintf(stderr, "Usage: %s targetname [acceptorprinc]\n", argv[0]);
+ return 1;
+ }
+
+ /* Import the target name. */
+ target_name = import_name(argv[1]);
+
+ /* Acquire the krb5 objects we need. */
+ ret = krb5_init_context(&context);
+ check_k5err(context, "krb5_init_context", ret);
+ ret = krb5_cc_default(context, &cc);
+ check_k5err(context, "krb5_cc_default", ret);
+ ret = krb5_kt_default(context, &kt);
+ check_k5err(context, "krb5_kt_default", ret);
+ if (argc >= 3) {
+ ret = krb5_parse_name(context, argv[2], &princ);
+ check_k5err(context, "krb5_parse_name", ret);
+ }
+
+ /* Get initiator cred. */
+ major = gss_krb5_import_cred(&minor, cc, NULL, NULL, &initiator_cred);
+ check_gsserr("gss_krb5_import_cred (initiator)", major, minor);
+
+ /* Get acceptor cred. */
+ major = gss_krb5_import_cred(&minor, NULL, princ, kt, &acceptor_cred);
+ check_gsserr("gss_krb5_import_cred (acceptor)", major, minor);
+
+ flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG;
+ establish_contexts(&mech_krb5, initiator_cred, acceptor_cred, target_name,
+ flags, &initiator_context, &acceptor_context, NULL,
+ NULL, NULL);
+
+ krb5_cc_close(context, cc);
+ krb5_kt_close(context, kt);
+ krb5_free_principal(context, princ);
+ krb5_free_context(context);
+ (void)gss_release_name(&minor, &target_name);
+ (void)gss_release_cred(&minor, &initiator_cred);
+ (void)gss_release_cred(&minor, &acceptor_cred);
+ (void)gss_delete_sec_context(&minor, &initiator_context, NULL);
+ (void)gss_delete_sec_context(&minor, &acceptor_context, NULL);
+ return 0;
+}
diff --git a/src/tests/gssapi/t_imp_name.c b/src/tests/gssapi/t_imp_name.c
new file mode 100644
index 000000000000..4fcd61b50f20
--- /dev/null
+++ b/src/tests/gssapi/t_imp_name.c
@@ -0,0 +1,58 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright 1996, Massachusetts Institute of Technology.
+ * 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 M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+/*
+ * Simple test program for testing how GSSAPI import name works. (May
+ * be made into a more full-fledged test program later.)
+ */
+
+#include <stdio.h>
+
+#include "common.h"
+
+int
+main(int argc, char **argv)
+{
+ const char *name = "host@dcl.mit.edu";
+ OM_uint32 major, minor;
+ gss_name_t gss_name;
+ gss_buffer_desc buf;
+ gss_OID name_oid;
+
+ gss_name = import_name(name);
+
+ major = gss_display_name(&minor, gss_name, &buf, &name_oid);
+ check_gsserr("gss_display_name", major, minor);
+ printf("name is: %.*s\n", (int)buf.length, (char *)buf.value);
+ (void)gss_release_buffer(&minor, &buf);
+
+ major = gss_oid_to_str(&minor, name_oid, &buf);
+ check_gsserr("gss_oid_to_str", major, minor);
+ printf("name type is: %.*s\n", (int)buf.length, (char *)buf.value);
+ (void)gss_release_buffer(&minor, &buf);
+ (void)gss_release_name(&minor, &gss_name);
+
+ return 0;
+}
diff --git a/src/tests/gssapi/t_inq_cred.c b/src/tests/gssapi/t_inq_cred.c
new file mode 100644
index 000000000000..8dd331d679a7
--- /dev/null
+++ b/src/tests/gssapi/t_inq_cred.c
@@ -0,0 +1,116 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* tests/gssapi/t_inq_cred.c - Test program for gss_inquire_cred behavior */
+/*
+ * Copyright 2012 by the Massachusetts Institute of Technology.
+ * 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 M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+/*
+ * Test program for gss_inquire_cred, intended to be run from a Python test
+ * script. Acquires credentials, inquires them, and prints the resulting name
+ * and lifetime.
+ *
+ * Usage: ./t_inq_cred [-k|-s] [-a|-b|-i] [initiatorname]
+ *
+ * By default no mechanism is specified when acquiring credentials; -k
+ * indicates the krb5 mech and -s indicates SPNEGO. By default or with -i,
+ * initiator credentials are acquired; -a indicates acceptor credentials and -b
+ * indicates credentials of both types. The credential is acquired with no
+ * name by default; a krb5 principal name or host-based name (prefixed with
+ * "gss:") may be supplied as an argument.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+
+static void
+usage(void)
+{
+ fprintf(stderr,
+ "Usage: t_inq_cred [-k|-s] [-a|-b|-i] [princ|gss:service@host]\n");
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ OM_uint32 minor, major, lifetime;
+ gss_cred_usage_t cred_usage = GSS_C_INITIATE;
+ gss_OID_set mechs = GSS_C_NO_OID_SET;
+ gss_cred_id_t cred = GSS_C_NO_CREDENTIAL;
+ gss_name_t name = GSS_C_NO_NAME;
+ gss_buffer_desc buf;
+ const char *name_arg = NULL;
+ char opt;
+
+ while (argc > 1 && argv[1][0] == '-') {
+ opt = argv[1][1];
+ argc--, argv++;
+ if (opt == 'a')
+ cred_usage = GSS_C_ACCEPT;
+ else if (opt == 'b')
+ cred_usage = GSS_C_BOTH;
+ else if (opt == 'i')
+ cred_usage = GSS_C_INITIATE;
+ else if (opt == 'k')
+ mechs = &mechset_krb5;
+ else if (opt == 's')
+ mechs = &mechset_spnego;
+ else
+ usage();
+ }
+ if (argc > 2)
+ usage();
+ if (argc > 1)
+ name_arg = argv[1];
+
+ /* Import the name, if given. */
+ if (name_arg != NULL)
+ name = import_name(name_arg);
+
+ /* Acquire a credential. */
+ major = gss_acquire_cred(&minor, name, GSS_C_INDEFINITE, mechs, cred_usage,
+ &cred, NULL, NULL);
+ check_gsserr("gss_acquire_cred", major, minor);
+
+ /* Inquire about the credential. */
+ (void)gss_release_name(&minor, &name);
+ major = gss_inquire_cred(&minor, cred, &name, &lifetime, NULL, NULL);
+ check_gsserr("gss_inquire_cred", major, minor);
+
+ /* Get a display form of the name. */
+ buf.value = NULL;
+ buf.length = 0;
+ major = gss_display_name(&minor, name, &buf, NULL);
+ check_gsserr("gss_display_name", major, minor);
+
+ printf("name: %.*s\n", (int)buf.length, (char *)buf.value);
+ printf("lifetime: %d\n", (int)lifetime);
+
+ (void)gss_release_cred(&minor, &cred);
+ (void)gss_release_name(&minor, &name);
+ (void)gss_release_buffer(&minor, &buf);
+ return 0;
+}
diff --git a/src/tests/gssapi/t_inq_ctx.c b/src/tests/gssapi/t_inq_ctx.c
new file mode 100644
index 000000000000..ac427a4ee736
--- /dev/null
+++ b/src/tests/gssapi/t_inq_ctx.c
@@ -0,0 +1,241 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright 2015 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "common.h"
+
+
+/*
+ * Test program for inquiring about a security context, intented to be run from
+ * a Python test script. Partially establishes a context to test inquiring
+ * about an incomplete context, and then establishes full contexts and inquires
+ * them. Exits with status 0 if all operations are successful, or 1 if not.
+ *
+ * Usage: ./t_inq_ctx target_name
+ */
+
+static void
+check_inq_context(gss_ctx_id_t context, int incomplete, gss_OID expected_mech,
+ OM_uint32 expected_flags, int expected_locally_init)
+{
+ OM_uint32 major, minor;
+ gss_name_t out_init_name, out_accept_name;
+ OM_uint32 out_lifetime;
+ gss_OID out_mech_type;
+ OM_uint32 out_flags;
+ int out_locally_init;
+ int out_open;
+
+ major = gss_inquire_context(&minor, context, &out_init_name,
+ &out_accept_name, &out_lifetime,
+ &out_mech_type, &out_flags, &out_locally_init,
+ &out_open);
+ check_gsserr("gss_inquire_context", major, minor);
+
+ assert(gss_oid_equal(out_mech_type, expected_mech));
+ assert(out_flags == expected_flags);
+ assert(out_locally_init == expected_locally_init);
+ if (incomplete) {
+ assert(!out_open);
+ assert(out_lifetime == 0);
+ assert(out_init_name == GSS_C_NO_NAME);
+ assert(out_accept_name == GSS_C_NO_NAME);
+ } else {
+ assert(out_open);
+ assert(out_lifetime > 0);
+ assert(out_init_name != GSS_C_NO_NAME);
+ assert(out_accept_name != GSS_C_NO_NAME);
+ }
+
+ (void)gss_release_name(&minor, &out_accept_name);
+ (void)gss_release_name(&minor, &out_init_name);
+}
+
+/* Call gss_init_sec_context() once to create an initiator context (which will
+ * be partial if flags includes GSS_C_MUTUAL_FLAG and the mech is krb5). */
+static void
+start_init_context(gss_OID mech, gss_cred_id_t cred, gss_name_t tname,
+ OM_uint32 flags, gss_ctx_id_t *ctx)
+{
+ OM_uint32 major, minor;
+ gss_buffer_desc itok = GSS_C_EMPTY_BUFFER;
+
+ *ctx = GSS_C_NO_CONTEXT;
+ major = gss_init_sec_context(&minor, cred, ctx, tname, mech, flags,
+ GSS_C_INDEFINITE, GSS_C_NO_CHANNEL_BINDINGS,
+ NULL, NULL, &itok, NULL, NULL);
+ check_gsserr("gss_init_sec_context", major, minor);
+ (void)gss_release_buffer(&minor, &itok);
+}
+
+/* Call gss_init_sec_context() and gss_accept_sec_context() once to create an
+ * acceptor context. */
+static void
+start_accept_context(gss_OID mech, gss_cred_id_t icred, gss_cred_id_t acred,
+ gss_name_t tname, OM_uint32 flags, gss_ctx_id_t *ctx)
+{
+ OM_uint32 major, minor;
+ gss_ctx_id_t ictx = GSS_C_NO_CONTEXT;
+ gss_buffer_desc itok = GSS_C_EMPTY_BUFFER, atok = GSS_C_EMPTY_BUFFER;
+
+ major = gss_init_sec_context(&minor, icred, &ictx, tname, mech, flags,
+ GSS_C_INDEFINITE, GSS_C_NO_CHANNEL_BINDINGS,
+ NULL, NULL, &itok, NULL, NULL);
+ check_gsserr("gss_init_sec_context", major, minor);
+
+ *ctx = GSS_C_NO_CONTEXT;
+ major = gss_accept_sec_context(&minor, ctx, acred, &itok,
+ GSS_C_NO_CHANNEL_BINDINGS, NULL, NULL,
+ &atok, NULL, NULL, NULL);
+ check_gsserr("gss_accept_sec_context", major, minor);
+
+ (void)gss_release_buffer(&minor, &itok);
+ (void)gss_release_buffer(&minor, &atok);
+ (void)gss_delete_sec_context(&minor, &ictx, NULL);
+}
+
+static void
+partial_iakerb_acceptor(const char *username, const char *password,
+ gss_name_t tname, OM_uint32 flags, gss_ctx_id_t *ctx)
+{
+ OM_uint32 major, minor;
+ gss_name_t name;
+ gss_buffer_desc ubuf, pwbuf;
+ gss_OID_set_desc mechlist;
+ gss_cred_id_t icred, acred;
+
+ mechlist.count = 1;
+ mechlist.elements = &mech_iakerb;
+
+ /* Import the username. */
+ ubuf.value = (void *)username;
+ ubuf.length = strlen(username);
+ major = gss_import_name(&minor, &ubuf, GSS_C_NT_USER_NAME, &name);
+ check_gsserr("gss_import_name", major, minor);
+
+ /* Create an IAKERB initiator cred with the username and password. */
+ pwbuf.value = (void *)password;
+ pwbuf.length = strlen(password);
+ major = gss_acquire_cred_with_password(&minor, name, &pwbuf, 0,
+ &mechlist, GSS_C_INITIATE, &icred,
+ NULL, NULL);
+ check_gsserr("gss_acquire_cred_with_password", major, minor);
+
+ /* Create an acceptor cred with support for IAKERB. */
+ major = gss_acquire_cred(&minor, GSS_C_NO_NAME, GSS_C_INDEFINITE,
+ &mechlist, GSS_C_ACCEPT, &acred, NULL, NULL);
+ check_gsserr("gss_acquire_cred", major, minor);
+
+ /* Begin context establishment to get a partial acceptor context. */
+ start_accept_context(&mech_iakerb, icred, acred, tname, flags, ctx);
+
+ (void)gss_release_name(&minor, &name);
+ (void)gss_release_cred(&minor, &icred);
+ (void)gss_release_cred(&minor, &acred);
+}
+
+/* Create a partially established SPNEGO acceptor. */
+static void
+partial_spnego_acceptor(gss_name_t tname, gss_ctx_id_t *ctx)
+{
+ OM_uint32 major, minor;
+ gss_buffer_desc itok = GSS_C_EMPTY_BUFFER, atok;
+
+ /*
+ * We could construct a fixed SPNEGO initiator token which forces a
+ * renegotiation, but a simpler approach is to pass an empty token to
+ * gss_accept_sec_context(), taking advantage of our compatibility support
+ * for SPNEGO NegHints.
+ */
+ *ctx = GSS_C_NO_CONTEXT;
+ major = gss_accept_sec_context(&minor, ctx, GSS_C_NO_CREDENTIAL, &itok,
+ GSS_C_NO_CHANNEL_BINDINGS, NULL, NULL,
+ &atok, NULL, NULL, NULL);
+ check_gsserr("gss_accept_sec_context(neghints)", major, minor);
+
+ (void)gss_release_buffer(&minor, &atok);
+}
+
+int
+main(int argc, char *argv[])
+{
+ OM_uint32 minor, flags, dce_flags;
+ gss_name_t tname;
+ gss_ctx_id_t ictx, actx;
+ const char *username, *password;
+
+ if (argc != 4) {
+ fprintf(stderr, "Usage: %s username password targetname\n", argv[0]);
+ return 1;
+ }
+ username = argv[1];
+ password = argv[2];
+ tname = import_name(argv[3]);
+
+ flags = GSS_C_SEQUENCE_FLAG | GSS_C_MUTUAL_FLAG | GSS_C_CONF_FLAG |
+ GSS_C_INTEG_FLAG;
+ start_init_context(&mech_krb5, GSS_C_NO_CREDENTIAL, tname, flags, &ictx);
+ check_inq_context(ictx, 1, &mech_krb5, flags | GSS_C_TRANS_FLAG, 1);
+ (void)gss_delete_sec_context(&minor, &ictx, NULL);
+
+ start_init_context(&mech_iakerb, GSS_C_NO_CREDENTIAL, tname, flags, &ictx);
+ check_inq_context(ictx, 1, &mech_iakerb, flags, 1);
+ (void)gss_delete_sec_context(&minor, &ictx, NULL);
+
+ start_init_context(&mech_spnego, GSS_C_NO_CREDENTIAL, tname, flags, &ictx);
+ check_inq_context(ictx, 1, &mech_spnego, flags, 1);
+ (void)gss_delete_sec_context(&minor, &ictx, NULL);
+
+ dce_flags = flags | GSS_C_DCE_STYLE;
+ start_accept_context(&mech_krb5, GSS_C_NO_CREDENTIAL, GSS_C_NO_CREDENTIAL,
+ tname, dce_flags, &actx);
+ check_inq_context(actx, 1, &mech_krb5, dce_flags | GSS_C_TRANS_FLAG, 0);
+ (void)gss_delete_sec_context(&minor, &actx, NULL);
+
+ partial_iakerb_acceptor(username, password, tname, flags, &actx);
+ check_inq_context(actx, 1, &mech_iakerb, 0, 0);
+ (void)gss_delete_sec_context(&minor, &actx, NULL);
+
+ partial_spnego_acceptor(tname, &actx);
+ check_inq_context(actx, 1, &mech_spnego, 0, 0);
+ (void)gss_delete_sec_context(&minor, &actx, NULL);
+
+ establish_contexts(&mech_krb5, GSS_C_NO_CREDENTIAL, GSS_C_NO_CREDENTIAL,
+ tname, flags, &ictx, &actx, NULL, NULL, NULL);
+
+ check_inq_context(ictx, 0, &mech_krb5, flags | GSS_C_TRANS_FLAG, 1);
+ check_inq_context(actx, 0, &mech_krb5,
+ flags | GSS_C_TRANS_FLAG | GSS_C_PROT_READY_FLAG, 0);
+
+ (void)gss_delete_sec_context(&minor, &ictx, NULL);
+ (void)gss_delete_sec_context(&minor, &actx, NULL);
+
+ (void)gss_release_name(&minor, &tname);
+ return 0;
+}
diff --git a/src/tests/gssapi/t_inq_mechs_name.c b/src/tests/gssapi/t_inq_mechs_name.c
new file mode 100644
index 000000000000..9f4ae4e16397
--- /dev/null
+++ b/src/tests/gssapi/t_inq_mechs_name.c
@@ -0,0 +1,64 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* tests/gssapi/t_inq_mechs_name.c - Exercise gss_inquire_mechs_for_name */
+/*
+ * Copyright (C) 2012 by the Massachusetts Institute of Technology.
+ * 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.
+ */
+
+/*
+ * Test program to exercise gss_inquire_mechs_for_name by importing a name and
+ * reporting the mech OIDs which are reported as being able to process it.
+ *
+ * Usage: ./t_inq_mechs_name name
+ */
+
+#include <stdio.h>
+
+#include "common.h"
+
+int
+main(int argc, char *argv[])
+{
+ OM_uint32 minor, major;
+ gss_name_t name;
+ gss_OID_set mechs;
+ size_t i;
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: t_inq_mechs_for_name name\n");
+ return 1;
+ }
+ name = import_name(argv[1]);
+ major = gss_inquire_mechs_for_name(&minor, name, &mechs);
+ check_gsserr("gss_inquire_mechs_for_name", major, minor);
+ for (i = 0; i < mechs->count; i++)
+ display_oid(NULL, &mechs->elements[i]);
+ (void)gss_release_oid_set(&minor, &mechs);
+ (void)gss_release_name(&minor, &name);
+ return 0;
+}
diff --git a/src/tests/gssapi/t_invalid.c b/src/tests/gssapi/t_invalid.c
new file mode 100644
index 000000000000..5c8ddac8dc84
--- /dev/null
+++ b/src/tests/gssapi/t_invalid.c
@@ -0,0 +1,429 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* tests/gssapi/t_invalid.c - Invalid message token regression tests */
+/*
+ * Copyright (C) 2014 by the Massachusetts Institute of Technology.
+ * 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.
+ */
+
+/*
+ * This file contains regression tests for some GSSAPI krb5 invalid per-message
+ * token vulnerabilities.
+ *
+ * 1. A pre-CFX wrap or MIC token processed with a CFX-only context causes a
+ * null pointer dereference. (The token must use SEAL_ALG_NONE or it will
+ * be rejected.)
+ *
+ * 2. A pre-CFX wrap or MIC token with fewer than 24 bytes after the ASN.1
+ * header causes an input buffer overrun, usually leading to either a segv
+ * or a GSS_S_DEFECTIVE_TOKEN error due to garbage algorithm, filler, or
+ * sequence number values.
+ *
+ * 3. A pre-CFX wrap token with fewer than 16 + cksumlen bytes after the ASN.1
+ * header causes an integer underflow when computing the ciphertext length,
+ * leading to an allocation error on 32-bit platforms or a segv on 64-bit
+ * platforms. A pre-CFX MIC token of this size causes an input buffer
+ * overrun when comparing the checksum, perhaps leading to a segv.
+ *
+ * 4. A pre-CFX wrap token with fewer than conflen + padlen bytes in the
+ * ciphertext (where padlen is the last byte of the decrypted ciphertext)
+ * causes an integer underflow when computing the original message length,
+ * leading to an allocation error.
+ *
+ * Vulnerabilities #1 and #2 also apply to IOV unwrap, although tokens with
+ * fewer than 16 bytes after the ASN.1 header will be rejected. Vulnerability
+ * #2 can only be robustly detected using a memory-checking environment such as
+ * valgrind.
+ */
+
+#include "k5-int.h"
+#include "common.h"
+#include "mglueP.h"
+#include "gssapiP_krb5.h"
+
+/*
+ * The following samples contain context parameters and otherwise valid seal
+ * tokens where the plain text is padded with byte value 100 instead of the
+ * proper value 1.
+ */
+struct test {
+ krb5_enctype enctype;
+ krb5_enctype encseq_enctype;
+ int sealalg;
+ int signalg;
+ size_t cksum_size;
+ size_t keylen;
+ const char *keydata;
+ size_t toklen;
+ const char *token;
+} tests[] = {
+ {
+ ENCTYPE_DES_CBC_CRC, ENCTYPE_DES_CBC_RAW,
+ SEAL_ALG_DES, SGN_ALG_DES_MAC_MD5, 8,
+ 8,
+ "\x26\xEC\xBA\xB6\xFE\xBA\x91\xCE",
+ 53,
+ "\x60\x33\x06\x09\x2A\x86\x48\x86\xF7\x12\x01\x02\x02\x02\x01\x00"
+ "\x00\x00\x00\xFF\xFF\xF0\x0B\x90\x7B\xC4\xFC\xEB\xF4\x84\x9C\x5A"
+ "\xA8\x56\x41\x3E\xE1\x62\xEE\x38\xD1\x34\x9A\xE3\xFB\xC9\xFD\x0A"
+ "\xDC\x83\xE1\x4A\xE4"
+ },
+ {
+ ENCTYPE_DES3_CBC_SHA1, ENCTYPE_DES3_CBC_RAW,
+ SEAL_ALG_DES3KD, SGN_ALG_HMAC_SHA1_DES3_KD, 20,
+ 24,
+ "\x4F\xEA\x19\x19\x5E\x0E\x10\xDF\x3D\x29\xB5\x13\x8F\x01\xC7\xA7"
+ "\x92\x3D\x38\xF7\x26\x73\x0D\x6D",
+ 65,
+ "\x60\x3F\x06\x09\x2A\x86\x48\x86\xF7\x12\x01\x02\x02\x02\x01\x04"
+ "\x00\x02\x00\xFF\xFF\xEB\xF3\x9A\x89\x24\x57\xB8\x63\x95\x25\xE8"
+ "\x6E\x8E\x79\xE6\x2E\xCA\xD3\xFF\x57\x9F\x8C\xAB\xEF\xDD\x28\x10"
+ "\x2F\x93\x21\x2E\xF2\x52\xB6\x6F\xA8\xBB\x8A\x6D\xAA\x6F\xB7\xF4\xD4"
+ },
+ {
+ ENCTYPE_ARCFOUR_HMAC, ENCTYPE_ARCFOUR_HMAC,
+ SEAL_ALG_MICROSOFT_RC4, SGN_ALG_HMAC_MD5, 8,
+ 16,
+ "\x66\x64\x41\x64\x55\x78\x21\xD0\xD0\xFD\x05\x6A\xFF\x6F\xE8\x09",
+ 53,
+ "\x60\x33\x06\x09\x2A\x86\x48\x86\xF7\x12\x01\x02\x02\x02\x01\x11"
+ "\x00\x10\x00\xFF\xFF\x35\xD4\x79\xF3\x8C\x47\x8F\x6E\x23\x6F\x3E"
+ "\xCC\x5E\x57\x5C\x6A\x89\xF0\xA2\x03\x4F\x0B\x51\x11\xEE\x89\x7E"
+ "\xD6\xF6\xB5\xD6\x51"
+ }
+};
+
+/* Fake up enough of a CFX GSS context for gss_unwrap, using an AES key. */
+static gss_ctx_id_t
+make_fake_cfx_context()
+{
+ gss_union_ctx_id_t uctx;
+ krb5_gss_ctx_id_t kgctx;
+ krb5_keyblock kb;
+
+ kgctx = calloc(1, sizeof(*kgctx));
+ if (kgctx == NULL)
+ abort();
+ kgctx->established = 1;
+ kgctx->proto = 1;
+ if (g_seqstate_init(&kgctx->seqstate, 0, 0, 0, 0) != 0)
+ abort();
+ kgctx->mech_used = &mech_krb5;
+ kgctx->sealalg = -1;
+ kgctx->signalg = -1;
+
+ kb.enctype = ENCTYPE_AES128_CTS_HMAC_SHA1_96;
+ kb.length = 16;
+ kb.contents = (unsigned char *)"1234567887654321";
+ if (krb5_k_create_key(NULL, &kb, &kgctx->subkey) != 0)
+ abort();
+
+ uctx = calloc(1, sizeof(*uctx));
+ if (uctx == NULL)
+ abort();
+ uctx->mech_type = &mech_krb5;
+ uctx->internal_ctx_id = (gss_ctx_id_t)kgctx;
+ return (gss_ctx_id_t)uctx;
+}
+
+/* Fake up enough of a GSS context for gss_unwrap, using keys from test. */
+static gss_ctx_id_t
+make_fake_context(const struct test *test)
+{
+ gss_union_ctx_id_t uctx;
+ krb5_gss_ctx_id_t kgctx;
+ krb5_keyblock kb;
+ unsigned char encbuf[8];
+ size_t i;
+
+ kgctx = calloc(1, sizeof(*kgctx));
+ if (kgctx == NULL)
+ abort();
+ kgctx->established = 1;
+ if (g_seqstate_init(&kgctx->seqstate, 0, 0, 0, 0) != 0)
+ abort();
+ kgctx->mech_used = &mech_krb5;
+ kgctx->sealalg = test->sealalg;
+ kgctx->signalg = test->signalg;
+ kgctx->cksum_size = test->cksum_size;
+
+ kb.enctype = test->enctype;
+ kb.length = test->keylen;
+ kb.contents = (unsigned char *)test->keydata;
+ if (krb5_k_create_key(NULL, &kb, &kgctx->subkey) != 0)
+ abort();
+
+ kb.enctype = test->encseq_enctype;
+ if (krb5_k_create_key(NULL, &kb, &kgctx->seq) != 0)
+ abort();
+
+ if (kb.enctype == ENCTYPE_DES_CBC_RAW) {
+ for (i = 0; i < 8; i++)
+ encbuf[i] = kb.contents[i] ^ 0xF0;
+ kb.contents = encbuf;
+ }
+ if (krb5_k_create_key(NULL, &kb, &kgctx->enc) != 0)
+ abort();
+
+ uctx = calloc(1, sizeof(*uctx));
+ if (uctx == NULL)
+ abort();
+ uctx->mech_type = &mech_krb5;
+ uctx->internal_ctx_id = (gss_ctx_id_t)kgctx;
+ return (gss_ctx_id_t)uctx;
+}
+
+/* Free a context created by make_fake_context. */
+static void
+free_fake_context(gss_ctx_id_t ctx)
+{
+ gss_union_ctx_id_t uctx = (gss_union_ctx_id_t)ctx;
+ krb5_gss_ctx_id_t kgctx = (krb5_gss_ctx_id_t)uctx->internal_ctx_id;
+
+ free(kgctx->seqstate);
+ krb5_k_free_key(NULL, kgctx->subkey);
+ krb5_k_free_key(NULL, kgctx->seq);
+ krb5_k_free_key(NULL, kgctx->enc);
+ free(kgctx);
+ free(uctx);
+}
+
+/* Prefix a token (starting at the two-byte ID) with an ASN.1 header and return
+ * it in an allocated block to facilitate checking by valgrind or similar. */
+static void
+make_token(unsigned char *token, size_t len, gss_buffer_t out)
+{
+ char *wrapped;
+
+ assert(mech_krb5.length == 9);
+ assert(len + 11 < 128);
+ wrapped = malloc(len + 13);
+ if (wrapped == NULL)
+ abort();
+ wrapped[0] = 0x60;
+ wrapped[1] = len + 11;
+ wrapped[2] = 0x06;
+ wrapped[3] = 9;
+ memcpy(wrapped + 4, mech_krb5.elements, 9);
+ memcpy(wrapped + 13, token, len);
+ out->length = len + 13;
+ out->value = wrapped;
+}
+
+/* Unwrap a superficially valid RFC 1964 token with a CFX-only context, with
+ * regular and IOV unwrap. */
+static void
+test_bogus_1964_token(gss_ctx_id_t ctx)
+{
+ OM_uint32 minor, major;
+ unsigned char tokbuf[128];
+ gss_buffer_desc in, out;
+ gss_iov_buffer_desc iov;
+
+ store_16_be(KG_TOK_SIGN_MSG, tokbuf);
+ store_16_le(SGN_ALG_DES_MAC_MD5, tokbuf + 2);
+ store_16_le(SEAL_ALG_NONE, tokbuf + 4);
+ store_16_le(0xFFFF, tokbuf + 6);
+ memset(tokbuf + 8, 0, 16);
+ make_token(tokbuf, 24, &in);
+
+ major = gss_unwrap(&minor, ctx, &in, &out, NULL, NULL);
+ if (major != GSS_S_DEFECTIVE_TOKEN)
+ abort();
+ (void)gss_release_buffer(&minor, &out);
+
+ iov.type = GSS_IOV_BUFFER_TYPE_HEADER;
+ iov.buffer = in;
+ major = gss_unwrap_iov(&minor, ctx, NULL, NULL, &iov, 1);
+ if (major != GSS_S_DEFECTIVE_TOKEN)
+ abort();
+
+ free(in.value);
+}
+
+/* Process wrap and MIC tokens with incomplete headers. */
+static void
+test_short_header(gss_ctx_id_t ctx)
+{
+ OM_uint32 minor, major;
+ unsigned char tokbuf[128];
+ gss_buffer_desc in, out, empty = GSS_C_EMPTY_BUFFER;
+
+ /* Seal token, 2-24 bytes */
+ store_16_be(KG_TOK_SEAL_MSG, tokbuf);
+ make_token(tokbuf, 2, &in);
+ major = gss_unwrap(&minor, ctx, &in, &out, NULL, NULL);
+ if (major != GSS_S_DEFECTIVE_TOKEN)
+ abort();
+ free(in.value);
+ (void)gss_release_buffer(&minor, &out);
+
+ /* Sign token, 2-24 bytes */
+ store_16_be(KG_TOK_SIGN_MSG, tokbuf);
+ make_token(tokbuf, 2, &in);
+ major = gss_unwrap(&minor, ctx, &in, &out, NULL, NULL);
+ if (major != GSS_S_DEFECTIVE_TOKEN)
+ abort();
+ free(in.value);
+ (void)gss_release_buffer(&minor, &out);
+
+ /* MIC token, 2-24 bytes */
+ store_16_be(KG_TOK_MIC_MSG, tokbuf);
+ make_token(tokbuf, 2, &in);
+ major = gss_verify_mic(&minor, ctx, &empty, &in, NULL);
+ if (major != GSS_S_DEFECTIVE_TOKEN)
+ abort();
+ free(in.value);
+}
+
+/* Process wrap and MIC tokens with incomplete headers. */
+static void
+test_short_header_iov(gss_ctx_id_t ctx, const struct test *test)
+{
+ OM_uint32 minor, major;
+ unsigned char tokbuf[128];
+ gss_iov_buffer_desc iov;
+
+ /* IOV seal token, 16-23 bytes */
+ store_16_be(KG_TOK_SEAL_MSG, tokbuf);
+ store_16_le(test->signalg, tokbuf + 2);
+ store_16_le(test->sealalg, tokbuf + 4);
+ store_16_be(0xFFFF, tokbuf + 6);
+ memset(tokbuf + 8, 0, 8);
+ iov.type = GSS_IOV_BUFFER_TYPE_HEADER;
+ make_token(tokbuf, 16, &iov.buffer);
+ major = gss_unwrap_iov(&minor, ctx, NULL, NULL, &iov, 1);
+ if (major != GSS_S_DEFECTIVE_TOKEN)
+ abort();
+ free(iov.buffer.value);
+
+ /* IOV sign token, 16-23 bytes */
+ store_16_be(KG_TOK_SIGN_MSG, tokbuf);
+ store_16_le(test->signalg, tokbuf + 2);
+ store_16_le(SEAL_ALG_NONE, tokbuf + 4);
+ store_16_le(0xFFFF, tokbuf + 6);
+ memset(tokbuf + 8, 0, 8);
+ iov.type = GSS_IOV_BUFFER_TYPE_HEADER;
+ make_token(tokbuf, 16, &iov.buffer);
+ major = gss_unwrap_iov(&minor, ctx, NULL, NULL, &iov, 1);
+ if (major != GSS_S_DEFECTIVE_TOKEN)
+ abort();
+ free(iov.buffer.value);
+
+ /* IOV MIC token, 16-23 bytes */
+ store_16_be(KG_TOK_MIC_MSG, tokbuf);
+ store_16_be(test->signalg, tokbuf + 2);
+ store_16_le(SEAL_ALG_NONE, tokbuf + 4);
+ store_16_le(0xFFFF, tokbuf + 6);
+ memset(tokbuf + 8, 0, 8);
+ iov.type = GSS_IOV_BUFFER_TYPE_MIC_TOKEN;
+ make_token(tokbuf, 16, &iov.buffer);
+ major = gss_verify_mic_iov(&minor, ctx, NULL, &iov, 1);
+ if (major != GSS_S_DEFECTIVE_TOKEN)
+ abort();
+ free(iov.buffer.value);
+}
+
+/* Process wrap and MIC tokens with incomplete checksums. */
+static void
+test_short_checksum(gss_ctx_id_t ctx, const struct test *test)
+{
+ OM_uint32 minor, major;
+ unsigned char tokbuf[128];
+ gss_buffer_desc in, out, empty = GSS_C_EMPTY_BUFFER;
+
+ /* Can only do this with the DES3 checksum, as we can't easily get past
+ * retrieving the sequence number when the checksum is only eight bytes. */
+ if (test->cksum_size <= 8)
+ return;
+ /* Seal token, fewer than 16 + cksum_size bytes. Use the token from the
+ * test data to get a valid sequence number. */
+ make_token((unsigned char *)test->token + 13, 24, &in);
+ major = gss_unwrap(&minor, ctx, &in, &out, NULL, NULL);
+ if (major != GSS_S_DEFECTIVE_TOKEN)
+ abort();
+ free(in.value);
+ (void)gss_release_buffer(&minor, &out);
+
+ /* Sign token, fewer than 16 + cksum_size bytes. */
+ memcpy(tokbuf, test->token + 13, 24);
+ store_16_be(KG_TOK_SIGN_MSG, tokbuf);
+ store_16_le(SEAL_ALG_NONE, tokbuf + 4);
+ make_token(tokbuf, 24, &in);
+ major = gss_unwrap(&minor, ctx, &in, &out, NULL, NULL);
+ if (major != GSS_S_DEFECTIVE_TOKEN)
+ abort();
+ free(in.value);
+ (void)gss_release_buffer(&minor, &out);
+
+ /* MIC token, fewer than 16 + cksum_size bytes. */
+ memcpy(tokbuf, test->token + 13, 24);
+ store_16_be(KG_TOK_MIC_MSG, tokbuf);
+ store_16_le(SEAL_ALG_NONE, tokbuf + 4);
+ make_token(tokbuf, 24, &in);
+ major = gss_verify_mic(&minor, ctx, &empty, &in, NULL);
+ if (major != GSS_S_DEFECTIVE_TOKEN)
+ abort();
+ free(in.value);
+}
+
+/* Unwrap a token with a bogus padding byte in the decrypted ciphertext. */
+static void
+test_bad_pad(gss_ctx_id_t ctx, const struct test *test)
+{
+ OM_uint32 minor, major;
+ gss_buffer_desc in, out;
+
+ in.length = test->toklen;
+ in.value = (char *)test->token;
+ major = gss_unwrap(&minor, ctx, &in, &out, NULL, NULL);
+ if (major != GSS_S_BAD_SIG)
+ abort();
+ (void)gss_release_buffer(&minor, &out);
+}
+
+int
+main(int argc, char **argv)
+{
+ gss_ctx_id_t ctx;
+ size_t i;
+
+ ctx = make_fake_cfx_context();
+ test_bogus_1964_token(ctx);
+ free_fake_context(ctx);
+
+ for (i = 0; i < sizeof(tests) / sizeof(*tests); i++) {
+ ctx = make_fake_context(&tests[i]);
+ test_short_header(ctx);
+ test_short_header_iov(ctx, &tests[i]);
+ test_short_checksum(ctx, &tests[i]);
+ test_bad_pad(ctx, &tests[i]);
+ free_fake_context(ctx);
+ }
+
+ return 0;
+}
diff --git a/src/tests/gssapi/t_iov.c b/src/tests/gssapi/t_iov.c
new file mode 100644
index 000000000000..f900b8835f79
--- /dev/null
+++ b/src/tests/gssapi/t_iov.c
@@ -0,0 +1,547 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* tests/gssapi/t_iov.c - Test program for IOV functions */
+/*
+ * Copyright (C) 2013 by the Massachusetts Institute of Technology.
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stddef.h>
+#include "common.h"
+
+/* Concatenate iov (except for sign-only buffers) into a contiguous token. */
+static void
+concat_iov(gss_iov_buffer_desc *iov, size_t iovlen, char **buf_out,
+ size_t *len_out)
+{
+ size_t len, i;
+ char *buf;
+
+ /* Concatenate the result into a contiguous buffer. */
+ len = 0;
+ for (i = 0; i < iovlen; i++) {
+ if (GSS_IOV_BUFFER_TYPE(iov[i].type) != GSS_IOV_BUFFER_TYPE_SIGN_ONLY)
+ len += iov[i].buffer.length;
+ }
+ buf = malloc(len);
+ if (buf == NULL)
+ errout("malloc failed");
+ len = 0;
+ for (i = 0; i < iovlen; i++) {
+ if (GSS_IOV_BUFFER_TYPE(iov[i].type) == GSS_IOV_BUFFER_TYPE_SIGN_ONLY)
+ continue;
+ memcpy(buf + len, iov[i].buffer.value, iov[i].buffer.length);
+ len += iov[i].buffer.length;
+ }
+ *buf_out = buf;
+ *len_out = len;
+}
+
+static void
+check_encrypted(const char *msg, int conf, const char *buf, const char *plain)
+{
+ int same = memcmp(buf, plain, strlen(plain)) == 0;
+
+ if ((conf && same) || (!conf && !same))
+ errout(msg);
+}
+
+/*
+ * Wrap str in standard form (HEADER | DATA | PADDING | TRAILER) using the
+ * caller-provided array iov, which must have space for four elements. Library
+ * allocation will be used for the header/padding/trailer buffers, so the
+ * caller must check and free them.
+ */
+static void
+wrap_std(gss_ctx_id_t ctx, char *str, gss_iov_buffer_desc *iov, int conf)
+{
+ OM_uint32 minor, major;
+ int oconf;
+
+ /* Lay out iov array. */
+ iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER | GSS_IOV_BUFFER_FLAG_ALLOCATE;
+ iov[1].type = GSS_IOV_BUFFER_TYPE_DATA;
+ iov[1].buffer.value = str;
+ iov[1].buffer.length = strlen(str);
+ iov[2].type = GSS_IOV_BUFFER_TYPE_PADDING | GSS_IOV_BUFFER_FLAG_ALLOCATE;
+ iov[3].type = GSS_IOV_BUFFER_TYPE_TRAILER | GSS_IOV_BUFFER_FLAG_ALLOCATE;
+
+ /* Wrap. This will allocate header/padding/trailer buffers as necessary
+ * and encrypt str in place. */
+ major = gss_wrap_iov(&minor, ctx, conf, GSS_C_QOP_DEFAULT, &oconf, iov, 4);
+ check_gsserr("gss_wrap_iov(std)", major, minor);
+ if (oconf != conf)
+ errout("gss_wrap_iov(std) conf");
+}
+
+/* Create standard tokens using gss_wrap_iov and ctx1, and make sure we can
+ * unwrap them using ctx2 in all of the supported ways. */
+static void
+test_standard_wrap(gss_ctx_id_t ctx1, gss_ctx_id_t ctx2, int conf)
+{
+ OM_uint32 major, minor;
+ gss_iov_buffer_desc iov[4], stiov[2];
+ gss_qop_t qop;
+ gss_buffer_desc input, output;
+ const char *string1 = "The swift brown fox jumped over the lazy dog.";
+ const char *string2 = "Now is the time!";
+ const char *string3 = "x";
+ const char *string4 = "!@#";
+ char data[1024], *fulltoken;
+ size_t len;
+ int oconf;
+ ptrdiff_t offset;
+
+ /* Wrap a standard token and unwrap it using the iov array. */
+ memcpy(data, string1, strlen(string1) + 1);
+ wrap_std(ctx1, data, iov, conf);
+ check_encrypted("gss_wrap_iov(std1) encryption", conf, data, string1);
+ major = gss_unwrap_iov(&minor, ctx2, &oconf, &qop, iov, 4);
+ check_gsserr("gss_unwrap_iov(std1)", major, minor);
+ if (oconf != conf || qop != GSS_C_QOP_DEFAULT)
+ errout("gss_unwrap_iov(std1) conf/qop");
+ if (iov[1].buffer.value != data || iov[1].buffer.length != strlen(string1))
+ errout("gss_unwrap_iov(std1) data buffer");
+ if (memcmp(data, string1, iov[1].buffer.length) != 0)
+ errout("gss_unwrap_iov(std1) decryption");
+ (void)gss_release_iov_buffer(&minor, iov, 4);
+
+ /* Wrap a standard token and unwrap it using gss_unwrap(). */
+ memcpy(data, string2, strlen(string2) + 1);
+ wrap_std(ctx1, data, iov, conf);
+ concat_iov(iov, 4, &fulltoken, &len);
+ input.value = fulltoken;
+ input.length = len;
+ major = gss_unwrap(&minor, ctx2, &input, &output, &oconf, &qop);
+ check_gsserr("gss_unwrap(std2)", major, minor);
+ if (oconf != conf || qop != GSS_C_QOP_DEFAULT)
+ errout("gss_unwrap(std2) conf/qop");
+ if (output.length != strlen(string2) ||
+ memcmp(output.value, string2, output.length) != 0)
+ errout("gss_unwrap(std2) decryption");
+ (void)gss_release_buffer(&minor, &output);
+ (void)gss_release_iov_buffer(&minor, iov, 4);
+ free(fulltoken);
+
+ /* Wrap a standard token and unwrap it using a stream buffer. */
+ memcpy(data, string3, strlen(string3) + 1);
+ wrap_std(ctx1, data, iov, conf);
+ concat_iov(iov, 4, &fulltoken, &len);
+ stiov[0].type = GSS_IOV_BUFFER_TYPE_STREAM;
+ stiov[0].buffer.value = fulltoken;
+ stiov[0].buffer.length = len;
+ stiov[1].type = GSS_IOV_BUFFER_TYPE_DATA;
+ major = gss_unwrap_iov(&minor, ctx2, &oconf, &qop, stiov, 2);
+ check_gsserr("gss_unwrap_iov(std3)", major, minor);
+ if (oconf != conf || qop != GSS_C_QOP_DEFAULT)
+ errout("gss_unwrap_iov(std3) conf/qop");
+ if (stiov[1].buffer.length != strlen(string3) ||
+ memcmp(stiov[1].buffer.value, string3, strlen(string3)) != 0)
+ errout("gss_unwrap_iov(std3) decryption");
+ offset = (char *)stiov[1].buffer.value - fulltoken;
+ if (offset < 0 || (size_t)offset > len)
+ errout("gss_unwrap_iov(std3) offset");
+ (void)gss_release_iov_buffer(&minor, iov, 4);
+ free(fulltoken);
+
+ /* Wrap a token using gss_wrap and unwrap it using a stream buffer with
+ * allocation and copying. */
+ input.value = (char *)string4;
+ input.length = strlen(string4);
+ major = gss_wrap(&minor, ctx1, conf, GSS_C_QOP_DEFAULT, &input, &oconf,
+ &output);
+ check_gsserr("gss_wrap(std4)", major, minor);
+ if (oconf != conf)
+ errout("gss_wrap(std4) conf");
+ stiov[0].type = GSS_IOV_BUFFER_TYPE_STREAM;
+ stiov[0].buffer = output;
+ stiov[1].type = GSS_IOV_BUFFER_TYPE_DATA | GSS_IOV_BUFFER_FLAG_ALLOCATE;
+ major = gss_unwrap_iov(&minor, ctx2, &oconf, &qop, stiov, 2);
+ check_gsserr("gss_unwrap_iov(std4)", major, minor);
+ if (!(GSS_IOV_BUFFER_FLAGS(stiov[1].type) & GSS_IOV_BUFFER_FLAG_ALLOCATED))
+ errout("gss_unwrap_iov(std4) allocated");
+ if (oconf != conf || qop != GSS_C_QOP_DEFAULT)
+ errout("gss_unwrap_iov(std4) conf/qop");
+ if (stiov[1].buffer.length != strlen(string4) ||
+ memcmp(stiov[1].buffer.value, string4, strlen(string4)) != 0)
+ errout("gss_unwrap_iov(std4) decryption");
+ (void)gss_release_buffer(&minor, &output);
+ (void)gss_release_iov_buffer(&minor, stiov, 2);
+}
+
+/*
+ * Wrap an AEAD token (HEADER | SIGN_ONLY | DATA | PADDING | TRAILER) using the
+ * caller-provided array iov, which must have space for five elements, and the
+ * caller-provided buffer data, which must be big enough to handle the test
+ * inputs. Library allocation will not be used.
+ */
+static void
+wrap_aead(gss_ctx_id_t ctx, const char *sign, const char *wrap,
+ gss_iov_buffer_desc *iov, char *data, int conf)
+{
+ OM_uint32 major, minor;
+ int oconf;
+ char *ptr;
+
+ /* Lay out iov array. */
+ iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER;
+ iov[1].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY;
+ iov[1].buffer.value = (char *)sign;
+ iov[1].buffer.length = strlen(sign);
+ iov[2].type = GSS_IOV_BUFFER_TYPE_DATA;
+ iov[2].buffer.value = (char *)wrap;
+ iov[2].buffer.length = strlen(wrap);
+ iov[3].type = GSS_IOV_BUFFER_TYPE_PADDING;
+ iov[4].type = GSS_IOV_BUFFER_TYPE_TRAILER;
+
+ /* Get header/padding/trailer lengths. */
+ major = gss_wrap_iov_length(&minor, ctx, conf, GSS_C_QOP_DEFAULT, &oconf,
+ iov, 5);
+ check_gsserr("gss_wrap_iov_length(aead)", major, minor);
+ if (oconf != conf)
+ errout("gss_wrap_iov_length(aead) conf");
+ if (iov[1].buffer.value != sign || iov[1].buffer.length != strlen(sign))
+ errout("gss_wrap_iov_length(aead) sign-only buffer");
+ if (iov[2].buffer.value != wrap || iov[2].buffer.length != strlen(wrap))
+ errout("gss_wrap_iov_length(aead) data buffer");
+
+ /* Set iov buffer pointers using returned lengths. */
+ iov[0].buffer.value = data;
+ ptr = data + iov[0].buffer.length;
+ memcpy(ptr, wrap, strlen(wrap));
+ iov[2].buffer.value = ptr;
+ ptr += iov[2].buffer.length;
+ iov[3].buffer.value = ptr;
+ ptr += iov[3].buffer.length;
+ iov[4].buffer.value = ptr;
+
+ /* Wrap the AEAD token. */
+ major = gss_wrap_iov(&minor, ctx, conf, GSS_C_QOP_DEFAULT, &oconf, iov, 5);
+ check_gsserr("gss_wrap_iov(aead)", major, minor);
+ if (oconf != conf)
+ errout("gss_wrap_iov(aead) conf");
+ if (iov[1].buffer.value != sign || iov[1].buffer.length != strlen(sign))
+ errout("gss_wrap_iov(aead) sign-only buffer");
+ if (iov[2].buffer.length != strlen(wrap))
+ errout("gss_wrap_iov(aead) data buffer");
+ check_encrypted("gss_wrap_iov(aead) encryption", conf, iov[2].buffer.value,
+ wrap);
+}
+
+/* Create AEAD tokens using gss_wrap_iov and ctx1, and make sure we can unwrap
+ * them using ctx2 in all of the supported ways. */
+static void
+test_aead(gss_ctx_id_t ctx1, gss_ctx_id_t ctx2, int conf)
+{
+ OM_uint32 major, minor;
+ gss_iov_buffer_desc iov[5], stiov[3];
+ gss_qop_t qop;
+ gss_buffer_desc input, assoc, output;
+ const char *sign = "This data is only signed.";
+ const char *wrap = "This data is wrapped in-place.";
+ char data[1024], *fulltoken;
+ size_t len;
+ int oconf;
+ ptrdiff_t offset;
+
+ /* Wrap an AEAD token and unwrap it using the IOV array. */
+ wrap_aead(ctx1, sign, wrap, iov, data, conf);
+ major = gss_unwrap_iov(&minor, ctx2, &oconf, &qop, iov, 5);
+ check_gsserr("gss_unwrap_iov(aead1)", major, minor);
+ if (oconf != conf || qop != GSS_C_QOP_DEFAULT)
+ errout("gss_unwrap_iov(aead1) conf/qop");
+ if (iov[1].buffer.value != sign || iov[1].buffer.length != strlen(sign))
+ errout("gss_unwrap_iov(aead1) sign-only buffer");
+ if (iov[2].buffer.length != strlen(wrap) ||
+ memcmp(iov[2].buffer.value, wrap, iov[2].buffer.length) != 0)
+ errout("gss_unwrap_iov(aead1) decryption");
+
+ /* Wrap an AEAD token and unwrap it using gss_unwrap_aead. */
+ wrap_aead(ctx1, sign, wrap, iov, data, conf);
+ concat_iov(iov, 5, &fulltoken, &len);
+ input.value = fulltoken;
+ input.length = len;
+ assoc.value = (char *)sign;
+ assoc.length = strlen(sign);
+ major = gss_unwrap_aead(&minor, ctx2, &input, &assoc, &output, &oconf,
+ &qop);
+ check_gsserr("gss_unwrap_aead(aead2)", major, minor);
+ if (output.length != strlen(wrap) ||
+ memcmp(output.value, wrap, output.length) != 0)
+ errout("gss_unwrap_aead(aead2) decryption");
+ free(fulltoken);
+ (void)gss_release_buffer(&minor, &output);
+
+ /* Wrap an AEAD token and unwrap it using a stream buffer. */
+ wrap_aead(ctx1, sign, wrap, iov, data, conf);
+ concat_iov(iov, 5, &fulltoken, &len);
+ stiov[0].type = GSS_IOV_BUFFER_TYPE_STREAM;
+ stiov[0].buffer.value = fulltoken;
+ stiov[0].buffer.length = len;
+ stiov[1].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY;
+ stiov[1].buffer.value = (char *)sign;
+ stiov[1].buffer.length = strlen(sign);
+ stiov[2].type = GSS_IOV_BUFFER_TYPE_DATA;
+ major = gss_unwrap_iov(&minor, ctx2, &oconf, &qop, stiov, 3);
+ check_gsserr("gss_unwrap_iov(aead3)", major, minor);
+ if (oconf != conf || qop != GSS_C_QOP_DEFAULT)
+ errout("gss_unwrap_iov(aead3) conf/qop");
+ if (stiov[2].buffer.length != strlen(wrap) ||
+ memcmp(stiov[2].buffer.value, wrap, strlen(wrap)) != 0)
+ errout("gss_unwrap_iov(aead3) decryption");
+ offset = (char *)stiov[2].buffer.value - fulltoken;
+ if (offset < 0 || (size_t)offset > len)
+ errout("gss_unwrap_iov(aead3) offset");
+ free(fulltoken);
+ (void)gss_release_iov_buffer(&minor, iov, 4);
+
+ /* Wrap a token using gss_wrap_aead and unwrap it using a stream buffer
+ * with allocation and copying. */
+ input.value = (char *)wrap;
+ input.length = strlen(wrap);
+ assoc.value = (char *)sign;
+ assoc.length = strlen(sign);
+ major = gss_wrap_aead(&minor, ctx1, conf, GSS_C_QOP_DEFAULT, &assoc,
+ &input, &oconf, &output);
+ check_gsserr("gss_wrap_aead(aead4)", major, minor);
+ if (oconf != conf)
+ errout("gss_wrap(aead4) conf");
+ stiov[0].type = GSS_IOV_BUFFER_TYPE_STREAM;
+ stiov[0].buffer = output;
+ stiov[1].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY;
+ stiov[1].buffer = assoc;
+ stiov[2].type = GSS_IOV_BUFFER_TYPE_DATA | GSS_IOV_BUFFER_FLAG_ALLOCATE;
+ major = gss_unwrap_iov(&minor, ctx2, &oconf, &qop, stiov, 3);
+ check_gsserr("gss_unwrap_iov(aead4)", major, minor);
+ if (!(GSS_IOV_BUFFER_FLAGS(stiov[2].type) & GSS_IOV_BUFFER_FLAG_ALLOCATED))
+ errout("gss_unwrap_iov(aead4) allocated");
+ if (oconf != conf || qop != GSS_C_QOP_DEFAULT)
+ errout("gss_unwrap_iov(aead4) conf/qop");
+ if (stiov[2].buffer.length != strlen(wrap) ||
+ memcmp(stiov[2].buffer.value, wrap, strlen(wrap)) != 0)
+ errout("gss_unwrap_iov(aead4) decryption");
+ (void)gss_release_buffer(&minor, &output);
+ (void)gss_release_iov_buffer(&minor, stiov, 3);
+}
+
+/*
+ * Get a MIC for sign1, sign2, and sign3 using the caller-provided array iov,
+ * which must have space for four elements, and the caller-provided buffer
+ * data, which must be big enough for the MIC. If data is NULL, the library
+ * will be asked to allocate the MIC buffer. The MIC will be located in
+ * iov[3].buffer.
+ */
+static void
+mic(gss_ctx_id_t ctx, const char *sign1, const char *sign2, const char *sign3,
+ gss_iov_buffer_desc *iov, char *data)
+{
+ OM_uint32 minor, major;
+ krb5_boolean allocated;
+
+ /* Lay out iov array. */
+ iov[0].type = GSS_IOV_BUFFER_TYPE_DATA;
+ iov[0].buffer.value = (char *)sign1;
+ iov[0].buffer.length = strlen(sign1);
+ iov[1].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY;
+ iov[1].buffer.value = (char *)sign2;
+ iov[1].buffer.length = strlen(sign2);
+ iov[2].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY;
+ iov[2].buffer.value = (char *)sign3;
+ iov[2].buffer.length = strlen(sign3);
+ iov[3].type = GSS_IOV_BUFFER_TYPE_MIC_TOKEN;
+ if (data == NULL) {
+ /* Ask the library to allocate the MIC buffer. */
+ iov[3].type |= GSS_IOV_BUFFER_FLAG_ALLOCATE;
+ } else {
+ /* Get the MIC length and use the caller-provided buffer. */
+ major = gss_get_mic_iov_length(&minor, ctx, GSS_C_QOP_DEFAULT, iov, 4);
+ check_gsserr("gss_get_mic_iov_length", major, minor);
+ iov[3].buffer.value = data;
+ }
+ major = gss_get_mic_iov(&minor, ctx, GSS_C_QOP_DEFAULT, iov, 4);
+ check_gsserr("gss_get_mic_iov", major, minor);
+ allocated = (GSS_IOV_BUFFER_FLAGS(iov[3].type) &
+ GSS_IOV_BUFFER_FLAG_ALLOCATED) != 0;
+ if (allocated != (data == NULL))
+ errout("gss_get_mic_iov allocated");
+}
+
+static void
+test_mic(gss_ctx_id_t ctx1, gss_ctx_id_t ctx2)
+{
+ OM_uint32 major, minor;
+ gss_iov_buffer_desc iov[4];
+ gss_qop_t qop;
+ gss_buffer_desc concatbuf, micbuf;
+ const char *sign1 = "Data and sign-only ";
+ const char *sign2 = "buffers are treated ";
+ const char *sign3 = "equally by gss_get_mic_iov";
+ char concat[1024], data[1024];
+
+ (void)snprintf(concat, sizeof(concat), "%s%s%s", sign1, sign2, sign3);
+ concatbuf.value = concat;
+ concatbuf.length = strlen(concat);
+
+ /* MIC with a caller-provided buffer and verify with the IOV array. */
+ mic(ctx1, sign1, sign2, sign3, iov, data);
+ major = gss_verify_mic_iov(&minor, ctx2, &qop, iov, 4);
+ check_gsserr("gss_verify_mic_iov(mic1)", major, minor);
+ if (qop != GSS_C_QOP_DEFAULT)
+ errout("gss_verify_mic_iov(mic1) qop");
+
+ /* MIC with an allocated buffer and verify with gss_verify_mic. */
+ mic(ctx1, sign1, sign2, sign3, iov, NULL);
+ major = gss_verify_mic(&minor, ctx2, &concatbuf, &iov[3].buffer, &qop);
+ check_gsserr("gss_verify_mic(mic2)", major, minor);
+ if (qop != GSS_C_QOP_DEFAULT)
+ errout("gss_verify_mic(mic2) qop");
+ (void)gss_release_iov_buffer(&minor, iov, 4);
+
+ /* MIC with gss_c_get_mic and verify using the IOV array (which is still
+ * mostly set up from the last call to mic(). */
+ major = gss_get_mic(&minor, ctx1, GSS_C_QOP_DEFAULT, &concatbuf, &micbuf);
+ check_gsserr("gss_get_mic(mic3)", major, minor);
+ iov[3].buffer = micbuf;
+ major = gss_verify_mic_iov(&minor, ctx2, &qop, iov, 4);
+ check_gsserr("gss_verify_mic_iov(mic3)", major, minor);
+ if (qop != GSS_C_QOP_DEFAULT)
+ errout("gss_verify_mic_iov(mic3) qop");
+ (void)gss_release_buffer(&minor, &micbuf);
+}
+
+/* Create a DCE-style token and make sure we can unwrap it. */
+static void
+test_dce(gss_ctx_id_t ctx1, gss_ctx_id_t ctx2, int conf)
+{
+ OM_uint32 major, minor;
+ gss_iov_buffer_desc iov[4];
+ gss_qop_t qop;
+ const char *sign1 = "First data to be signed";
+ const char *sign2 = "Second data to be signed";
+ const char *wrap = "This data must align to 16 bytes";
+ int oconf;
+ char data[1024];
+
+ /* Wrap a SIGN_ONLY_1 | DATA | SIGN_ONLY_2 | HEADER token. */
+ memcpy(data, wrap, strlen(wrap) + 1);
+ iov[0].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY;
+ iov[0].buffer.value = (char *)sign1;
+ iov[0].buffer.length = strlen(sign1);
+ iov[1].type = GSS_IOV_BUFFER_TYPE_DATA;
+ iov[1].buffer.value = data;
+ iov[1].buffer.length = strlen(wrap);
+ iov[2].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY;
+ iov[2].buffer.value = (char *)sign2;
+ iov[2].buffer.length = strlen(sign2);
+ iov[3].type = GSS_IOV_BUFFER_TYPE_HEADER | GSS_IOV_BUFFER_FLAG_ALLOCATE;
+ major = gss_wrap_iov(&minor, ctx1, conf, GSS_C_QOP_DEFAULT, &oconf, iov,
+ 4);
+ check_gsserr("gss_wrap_iov(dce)", major, minor);
+ if (oconf != conf)
+ errout("gss_wrap_iov(dce) conf");
+ if (iov[0].buffer.value != sign1 || iov[0].buffer.length != strlen(sign1))
+ errout("gss_wrap_iov(dce) sign1 buffer");
+ if (iov[1].buffer.value != data || iov[1].buffer.length != strlen(wrap))
+ errout("gss_wrap_iov(dce) data buffer");
+ if (iov[2].buffer.value != sign2 || iov[2].buffer.length != strlen(sign2))
+ errout("gss_wrap_iov(dce) sign2 buffer");
+ check_encrypted("gss_wrap_iov(dce) encryption", conf, data, wrap);
+
+ /* Make sure we can unwrap it. */
+ major = gss_unwrap_iov(&minor, ctx2, &oconf, &qop, iov, 4);
+ check_gsserr("gss_unwrap_iov(std1)", major, minor);
+ if (oconf != conf || qop != GSS_C_QOP_DEFAULT)
+ errout("gss_unwrap_iov(std1) conf/qop");
+ if (iov[0].buffer.value != sign1 || iov[0].buffer.length != strlen(sign1))
+ errout("gss_unwrap_iov(dce) sign1 buffer");
+ if (iov[1].buffer.value != data || iov[1].buffer.length != strlen(wrap))
+ errout("gss_unwrap_iov(dce) data buffer");
+ if (iov[2].buffer.value != sign2 || iov[2].buffer.length != strlen(sign2))
+ errout("gss_unwrap_iov(dce) sign2 buffer");
+ if (memcmp(data, wrap, iov[1].buffer.length) != 0)
+ errout("gss_unwrap_iov(dce) decryption");
+ (void)gss_release_iov_buffer(&minor, iov, 4);
+}
+
+int
+main(int argc, char *argv[])
+{
+ OM_uint32 minor, flags;
+ gss_OID mech = &mech_krb5;
+ gss_name_t tname;
+ gss_ctx_id_t ictx, actx;
+
+ /* Parse arguments. */
+ argv++;
+ if (*argv != NULL && strcmp(*argv, "-s") == 0) {
+ mech = &mech_spnego;
+ argv++;
+ }
+ if (*argv == NULL || *(argv + 1) != NULL)
+ errout("Usage: t_iov [-s] targetname");
+ tname = import_name(*argv);
+
+ flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG | GSS_C_MUTUAL_FLAG;
+ establish_contexts(mech, GSS_C_NO_CREDENTIAL, GSS_C_NO_CREDENTIAL, tname,
+ flags, &ictx, &actx, NULL, NULL, NULL);
+
+ /* Test standard token wrapping and unwrapping in both directions, with and
+ * without confidentiality. */
+ test_standard_wrap(ictx, actx, 0);
+ test_standard_wrap(ictx, actx, 1);
+ test_standard_wrap(actx, ictx, 0);
+ test_standard_wrap(actx, ictx, 1);
+
+ /* Test AEAD wrapping. */
+ test_aead(ictx, actx, 0);
+ test_aead(ictx, actx, 1);
+ test_aead(actx, ictx, 0);
+ test_aead(actx, ictx, 1);
+
+ /* Test MIC tokens. */
+ test_mic(ictx, actx);
+ test_mic(actx, ictx);
+
+ /* Test DCE wrapping with DCE-style contexts. */
+ (void)gss_delete_sec_context(&minor, &ictx, NULL);
+ (void)gss_delete_sec_context(&minor, &actx, NULL);
+ flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG | GSS_C_DCE_STYLE;
+ establish_contexts(mech, GSS_C_NO_CREDENTIAL, GSS_C_NO_CREDENTIAL, tname,
+ flags, &ictx, &actx, NULL, NULL, NULL);
+ test_dce(ictx, actx, 0);
+ test_dce(ictx, actx, 1);
+ test_dce(actx, ictx, 0);
+ test_dce(actx, ictx, 1);
+
+ (void)gss_release_name(&minor, &tname);
+ (void)gss_delete_sec_context(&minor, &ictx, NULL);
+ (void)gss_delete_sec_context(&minor, &actx, NULL);
+ return 0;
+}
diff --git a/src/tests/gssapi/t_namingexts.c b/src/tests/gssapi/t_namingexts.c
new file mode 100644
index 000000000000..739592b9064e
--- /dev/null
+++ b/src/tests/gssapi/t_namingexts.c
@@ -0,0 +1,227 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright 2009 by the Massachusetts Institute of Technology.
+ * 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 M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+
+static int use_spnego = 0;
+
+static void
+display_name(const char *tag, gss_name_t name)
+{
+ OM_uint32 major, minor;
+ gss_buffer_desc buf;
+
+ major = gss_display_name(&minor, name, &buf, NULL);
+ check_gsserr("gss_display_name", major, minor);
+
+ printf("%s:\t%.*s\n", tag, (int)buf.length, (char *)buf.value);
+
+ (void)gss_release_buffer(&minor, &buf);
+}
+
+static void
+test_export_import_name(gss_name_t name)
+{
+ OM_uint32 major, minor;
+ gss_buffer_desc exported_name = GSS_C_EMPTY_BUFFER;
+ gss_name_t imported_name = GSS_C_NO_NAME;
+ gss_name_t imported_name_comp = GSS_C_NO_NAME;
+ unsigned int i;
+
+ major = gss_export_name_composite(&minor, name, &exported_name);
+ check_gsserr("gss_export_name_composite", major, minor);
+
+ printf("Exported name:\n");
+ for (i = 0; i < exported_name.length; i++) {
+ if ((i % 32) == 0)
+ printf("\n");
+ printf("%02x", ((char *)exported_name.value)[i] & 0xFF);
+ }
+ printf("\n");
+
+ major = gss_import_name(&minor, &exported_name, GSS_C_NT_EXPORT_NAME,
+ &imported_name);
+ check_gsserr("gss_import_name", major, minor);
+
+ major = gss_import_name(&minor, &exported_name, GSS_C_NT_COMPOSITE_EXPORT,
+ &imported_name_comp);
+ check_gsserr("gss_import_name", major, minor);
+ (void)gss_release_buffer(&minor, &exported_name);
+
+ printf("\n");
+ display_canon_name("Re-imported name", imported_name, &mech_krb5);
+ printf("Re-imported attributes:\n\n");
+ enumerate_attributes(imported_name, 0);
+
+ display_name("Re-imported (as composite) name", imported_name_comp);
+ printf("Re-imported (as composite) attributes:\n\n");
+ enumerate_attributes(imported_name_comp, 0);
+
+ (void)gss_release_name(&minor, &imported_name);
+ (void)gss_release_name(&minor, &imported_name_comp);
+}
+
+static void
+test_greet_authz_data(gss_name_t name)
+{
+ OM_uint32 major, minor;
+ gss_buffer_desc attr;
+ gss_buffer_desc value;
+
+ attr.value = "urn:greet:greeting";
+ attr.length = strlen((char *)attr.value);
+
+ major = gss_delete_name_attribute(&minor, name, &attr);
+ if (major == GSS_S_UNAVAILABLE) {
+ fprintf(stderr, "Warning: greet_client plugin not installed\n");
+ exit(1);
+ }
+ check_gsserr("gss_delete_name_attribute", major, minor);
+
+ value.value = "Hello, acceptor world!";
+ value.length = strlen((char *)value.value);
+ major = gss_set_name_attribute(&minor, name, 1, &attr, &value);
+ if (major == GSS_S_UNAVAILABLE)
+ return;
+ check_gsserr("gss_set_name_attribute", major, minor);
+}
+
+static void
+test_map_name_to_any(gss_name_t name)
+{
+ OM_uint32 major, minor;
+ gss_buffer_desc type_id;
+ krb5_pac pac;
+ krb5_context context = NULL;
+ krb5_error_code ret;
+ size_t len, i;
+ krb5_ui_4 *types;
+
+ type_id.value = "mspac";
+ type_id.length = strlen((char *)type_id.value);
+
+ major = gss_map_name_to_any(&minor, name, 1, &type_id, (gss_any_t *)&pac);
+ if (major == GSS_S_UNAVAILABLE)
+ return;
+ check_gsserr("gss_map_name_to_any", major, minor);
+
+ ret = krb5_init_context(&context);
+ check_k5err(context, "krb5_init_context", ret);
+
+ if (krb5_pac_get_types(context, pac, &len, &types) == 0) {
+ printf("PAC buffer types:");
+ for (i = 0; i < len; i++)
+ printf(" %d", types[i]);
+ printf("\n");
+ free(types);
+ }
+
+ (void)gss_release_any_name_mapping(&minor, name, &type_id,
+ (gss_any_t *)&pac);
+}
+
+static void
+init_accept_sec_context(gss_cred_id_t verifier_cred_handle)
+{
+ OM_uint32 major, minor, flags;
+ gss_name_t source_name = GSS_C_NO_NAME, target_name = GSS_C_NO_NAME;
+ gss_ctx_id_t initiator_context, acceptor_context;
+ gss_OID mech = use_spnego ? &mech_spnego : &mech_krb5;
+
+ major = gss_inquire_cred(&minor, verifier_cred_handle, &target_name, NULL,
+ NULL, NULL);
+ check_gsserr("gss_inquire_cred", major, minor);
+
+ display_canon_name("Target name", target_name, &mech_krb5);
+
+ flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG;
+ establish_contexts(mech, verifier_cred_handle, verifier_cred_handle,
+ target_name, flags, &initiator_context,
+ &acceptor_context, &source_name, NULL, NULL);
+
+ display_canon_name("Source name", source_name, &mech_krb5);
+ enumerate_attributes(source_name, 1);
+ test_export_import_name(source_name);
+ test_map_name_to_any(source_name);
+
+ (void)gss_release_name(&minor, &source_name);
+ (void)gss_release_name(&minor, &target_name);
+ (void)gss_delete_sec_context(&minor, &initiator_context, NULL);
+ (void)gss_delete_sec_context(&minor, &acceptor_context, NULL);
+}
+
+int
+main(int argc, char *argv[])
+{
+ OM_uint32 minor, major;
+ gss_cred_id_t cred_handle = GSS_C_NO_CREDENTIAL;
+ gss_OID_set mechs, actual_mechs = GSS_C_NO_OID_SET;
+ gss_name_t tmp_name, name;
+
+ if (argc > 1 && strcmp(argv[1], "--spnego") == 0) {
+ use_spnego++;
+ argc--;
+ argv++;
+ }
+
+ if (argc < 2) {
+ fprintf(stderr, "Usage: %s [--spnego] principal [keytab]\n", argv[0]);
+ exit(1);
+ }
+
+ tmp_name = import_name(argv[1]);
+ major = gss_canonicalize_name(&minor, tmp_name, &mech_krb5, &name);
+ check_gsserr("gss_canonicalze_name", major, minor);
+ (void)gss_release_name(&minor, &tmp_name);
+
+ test_greet_authz_data(name);
+
+ if (argc >= 3) {
+ major = krb5_gss_register_acceptor_identity(argv[2]);
+ check_gsserr("krb5_gss_register_acceptor_identity", major, minor);
+ }
+
+ mechs = use_spnego ? &mechset_spnego : &mechset_krb5;
+
+ /* get default cred */
+ major = gss_acquire_cred(&minor, name, GSS_C_INDEFINITE, mechs, GSS_C_BOTH,
+ &cred_handle, &actual_mechs, NULL);
+ check_gsserr("gss_acquire_cred", major, minor);
+
+ (void)gss_release_oid_set(&minor, &actual_mechs);
+
+ init_accept_sec_context(cred_handle);
+
+ printf("\n");
+
+ (void)gss_release_cred(&minor, &cred_handle);
+ (void)gss_release_oid_set(&minor, &actual_mechs);
+ (void)gss_release_name(&minor, &name);
+ return 0;
+}
diff --git a/src/tests/gssapi/t_oid.c b/src/tests/gssapi/t_oid.c
new file mode 100644
index 000000000000..417f7b9c6a02
--- /dev/null
+++ b/src/tests/gssapi/t_oid.c
@@ -0,0 +1,221 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* tests/gssapi/t_oid.c - Test OID manipulation functions */
+/*
+ * Copyright (C) 2012 by the Massachusetts Institute of Technology.
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+
+static struct {
+ char *canonical;
+ char *variant;
+ gss_OID_desc oid;
+} tests[] = {
+ /* GSS_C_NT_USER_NAME */
+ { "{ 1 2 840 113554 1 2 1 1 }", "1.2.840.113554.1.2.1.1",
+ { 10, "\x2A\x86\x48\x86\xF7\x12\x01\x02\x01\x01" } },
+ /* GSS_C_NT_MACHINE_UID_NAME */
+ { "{ 1 2 840 113554 1 2 1 2 }", "1 2 840 113554 1 2 1 2",
+ { 10, "\x2A\x86\x48\x86\xF7\x12\x01\x02\x01\x02" } },
+ /* GSS_C_NT_STRING_UID_NAME */
+ { "{ 1 2 840 113554 1 2 1 3 }", "{1 2 840 113554 1 2 1 3}",
+ { 10, "\x2A\x86\x48\x86\xF7\x12\x01\x02\x01\x03" } },
+ /* GSS_C_NT_HOSTBASED_SERVICE_X */
+ { "{ 1 3 6 1 5 6 2 }", "{ 1 3 6 1 5 6 2 }",
+ { 6, "\x2B\x06\x01\x05\x06\x02" } },
+ /* GSS_C_NT_ANONYMOUS */
+ { "{ 1 3 6 1 5 6 3 }", "{ 01 03 06 01 05 06 03 }",
+ { 6, "\x2B\x06\x01\x05\x06\x03" } },
+ /* GSS_KRB5_NT_PRINCIPAL_NAME */
+ { "{ 1 2 840 113554 1 2 2 1 }", " {01 2 840 113554 1 2 2 1 } ",
+ { 10, "\x2A\x86\x48\x86\xF7\x12\x01\x02\x02\x01" } },
+ /* gss_krb5_nt_principal */
+ { "{ 1 2 840 113554 1 2 2 2 }", "{1.2.840.113554.1.2.2.2}",
+ { 10, "\x2A\x86\x48\x86\xF7\x12\x01\x02\x02\x02" } },
+ /* gss_mech_krb5 */
+ { "{ 1 2 840 113554 1 2 2 }", "{ 1.2.840.113554.1.2.2 }",
+ { 9, "\x2A\x86\x48\x86\xF7\x12\x01\x02\x02" } },
+ /* gss_mech_krb5_old */
+ { "{ 1 3 5 1 5 2 }", "001 . 003 . 005 . 001 . 005 . 002",
+ { 5, "\x2B\x05\x01\x05\x02" } },
+ /* gss_mech_krb5_wrong */
+ { "{ 1 2 840 48018 1 2 2 }", "1.2.840.48018.1.2.2 trailing garbage",
+ { 9, "\x2A\x86\x48\x82\xF7\x12\x01\x02\x02" } },
+ /* gss_mech_iakerb */
+ { "{ 1 3 6 1 5 2 5 }", "{ 1 3 6 1 5 2 5 } trailing garbage",
+ { 6, "\x2B\x06\x01\x05\x02\x05" } },
+ /* SPNEGO */
+ { "{ 1 3 6 1 5 5 2 }", "{1 3 6 1 5 5 2} trailing garbage",
+ { 6, "\x2B\x06\x01\x05\x05\x02" } },
+ /* Edge cases for the first two arcs */
+ { "{ 0 0 }", NULL, { 1, "\x00" } },
+ { "{ 0 39 }", NULL, { 1, "\x27" } },
+ { "{ 1 0 }", NULL, { 1, "\x28" } },
+ { "{ 1 39 }", NULL, { 1, "\x4F" } },
+ { "{ 2 0 }", NULL, { 1, "\x50" } },
+ { "{ 2 40 }", NULL, { 1, "\x78" } },
+ { "{ 2 47 }", NULL, { 1, "\x7F" } },
+ { "{ 2 48 }", NULL, { 2, "\x81\x00" } },
+ { "{ 2 16304 }", NULL, { 3, "\x81\x80\x00" } },
+ /* Zero-valued arcs */
+ { "{ 0 0 0 }", NULL, { 2, "\x00\x00" } },
+ { "{ 0 0 1 0 }", NULL, { 3, "\x00\x01\x00" } },
+ { "{ 0 0 128 0 }", NULL, { 4, "\x00\x81\x00\x00 " } },
+ { "{ 0 0 0 1 }", NULL, { 3, "\x00\x00\x01" } },
+ { "{ 0 0 128 0 1 0 128 }", NULL,
+ { 8, "\x00\x81\x00\x00\x01\x00\x81\x00 " } }
+};
+
+static char *invalid_strings[] = {
+ "",
+ "{}",
+ "{",
+ "}",
+ " ",
+ " { } ",
+ "x",
+ "+1 1",
+ "-1.1",
+ "1.+0",
+ "+0.1",
+ "{ 1 garbage }",
+ "{ 1 }",
+ "{ 0 40 }",
+ "{ 1 40 }",
+ "{ 1 128 }",
+ "{ 1 1",
+ "{ 1 2 3 4 +5 }",
+ "{ 1.2.-3.4.5 }"
+};
+
+static int
+oid_equal(gss_OID o1, gss_OID o2)
+{
+ return o1->length == o2->length &&
+ memcmp(o1->elements, o2->elements, o1->length) == 0;
+}
+
+int
+main()
+{
+ size_t i;
+ OM_uint32 major, minor;
+ gss_buffer_desc buf;
+ gss_OID oid;
+ gss_OID_set set;
+ int status = 0, present;
+
+ for (i = 0; i < sizeof(tests) / sizeof(*tests); i++) {
+ /* Check that this test's OID converts to its canonical string form. */
+ major = gss_oid_to_str(&minor, &tests[i].oid, &buf);
+ check_gsserr("gss_oid_to_str", major, minor);
+ if (buf.length != strlen(tests[i].canonical) + 1 ||
+ memcmp(buf.value, tests[i].canonical, buf.length) != 0) {
+ status = 1;
+ printf("test %d: OID converts to %.*s, wanted %s\n", (int)i,
+ (int)buf.length, (char *)buf.value, tests[i].canonical);
+ }
+ (void)gss_release_buffer(&minor, &buf);
+
+ /* Check that this test's canonical string form converts to its OID. */
+ buf.value = tests[i].canonical;
+ buf.length = strlen(tests[i].canonical);
+ major = gss_str_to_oid(&minor, &buf, &oid);
+ check_gsserr("gss_str_to_oid", major, minor);
+ if (!oid_equal(oid, &tests[i].oid)) {
+ status = 1;
+ printf("test %d: %s converts to wrong OID\n", (int)i,
+ tests[i].canonical);
+ display_oid("wanted", &tests[i].oid);
+ display_oid("actual", oid);
+ }
+ (void)gss_release_oid(&minor, &oid);
+
+ /* Check that this test's variant string form converts to its OID. */
+ if (tests[i].variant == NULL)
+ continue;
+ buf.value = tests[i].variant;
+ buf.length = strlen(tests[i].variant);
+ major = gss_str_to_oid(&minor, &buf, &oid);
+ check_gsserr("gss_str_to_oid", major, minor);
+ if (!oid_equal(oid, &tests[i].oid)) {
+ status = 1;
+ printf("test %d: %s converts to wrong OID\n", (int)i,
+ tests[i].variant);
+ display_oid("wanted", &tests[i].oid);
+ display_oid("actual", oid);
+ }
+ (void)gss_release_oid(&minor, &oid);
+ }
+
+ for (i = 0; i < sizeof(invalid_strings) / sizeof(*invalid_strings); i++) {
+ buf.value = invalid_strings[i];
+ buf.length = strlen(invalid_strings[i]);
+ major = gss_str_to_oid(&minor, &buf, &oid);
+ if (major == GSS_S_COMPLETE) {
+ status = 1;
+ printf("invalid %d: %s converted when it should not have\n",
+ (int)i, invalid_strings[i]);
+ (void)gss_release_oid(&minor, &oid);
+ }
+ }
+
+ major = gss_create_empty_oid_set(&minor, &set);
+ check_gsserr("gss_create_empty_oid_set", major, minor);
+ for (i = 0; i < sizeof(tests) / sizeof(*tests); i++) {
+ major = gss_add_oid_set_member(&minor, &tests[i].oid, &set);
+ check_gsserr("gss_add_oid_set_member", major, minor);
+ }
+ if (set->count != i) {
+ status = 1;
+ printf("oid set has wrong size: wanted %d, actual %d\n", (int)i,
+ (int)set->count);
+ }
+ for (i = 0; i < set->count; i++) {
+ if (!oid_equal(&set->elements[i], &tests[i].oid)) {
+ status = 1;
+ printf("oid set has wrong element %d\n", (int)i);
+ display_oid("wanted", &tests[i].oid);
+ display_oid("actual", &set->elements[i]);
+ }
+ major = gss_test_oid_set_member(&minor, &tests[i].oid, set, &present);
+ check_gsserr("gss_test_oid_set_member", major, minor);
+ if (!present) {
+ status = 1;
+ printf("oid set does not contain OID %d\n", (int)i);
+ display_oid("wanted", &tests[i].oid);
+ }
+ }
+ (void)gss_release_oid_set(&minor, &set);
+ return status;
+}
diff --git a/src/tests/gssapi/t_pcontok.c b/src/tests/gssapi/t_pcontok.c
new file mode 100644
index 000000000000..b966f8129e8d
--- /dev/null
+++ b/src/tests/gssapi/t_pcontok.c
@@ -0,0 +1,202 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* tests/gssapi/t_pcontok.c - gss_process_context_token tests */
+/*
+ * Copyright (C) 2014 by the Massachusetts Institute of Technology.
+ * 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.
+ */
+
+/*
+ * This test program exercises krb5 gss_process_context_token. It first
+ * establishes a context to a named target. Then, if the resulting context
+ * uses RFC 1964, it creates a context deletion token from the acceptor to the
+ * initiator and passes it to the initiator using gss_process_context_token.
+ * If the established context uses RFC 4121, this program feeds a made-up
+ * context token to gss_process_context_token and checks for the expected
+ * error.
+ */
+
+#include "k5-int.h"
+#include "common.h"
+
+#define SGN_ALG_DES_MAC_MD5 0x00
+#define SGN_ALG_HMAC_SHA1_DES3_KD 0x04
+#define SGN_ALG_HMAC_MD5 0x11
+
+/*
+ * Create a valid RFC 1964 context deletion token using the information in *
+ * lctx. We must do this by hand since we no longer create context deletion
+ * tokens from gss_delete_sec_context.
+ */
+static void
+make_delete_token(gss_krb5_lucid_context_v1_t *lctx, gss_buffer_desc *out)
+{
+ krb5_error_code ret;
+ krb5_context context;
+ krb5_keyblock seqkb;
+ krb5_key seq;
+ krb5_checksum cksum;
+ krb5_cksumtype cktype;
+ krb5_keyusage ckusage;
+ krb5_crypto_iov iov;
+ krb5_data d;
+ size_t cksize, tlen;
+ unsigned char *token, *ptr, iv[8];
+ gss_krb5_lucid_key_t *lkey = &lctx->rfc1964_kd.ctx_key;
+ int signalg = lctx->rfc1964_kd.sign_alg;
+
+ ret = krb5_init_context(&context);
+ check_k5err(context, "krb5_init_context", ret);
+
+ seqkb.enctype = lkey->type;
+ seqkb.length = lkey->length;
+ seqkb.contents = lkey->data;
+ ret = krb5_k_create_key(context, &seqkb, &seq);
+ check_k5err(context, "krb5_k_create_key", ret);
+
+ if (signalg == SGN_ALG_DES_MAC_MD5) {
+ cktype = CKSUMTYPE_RSA_MD5;
+ cksize = 8;
+ ckusage = 0;
+ } else if (signalg == SGN_ALG_HMAC_SHA1_DES3_KD) {
+ cktype = CKSUMTYPE_HMAC_SHA1_DES3;
+ cksize = 20;
+ ckusage = 23;
+ } else if (signalg == SGN_ALG_HMAC_MD5) {
+ cktype = CKSUMTYPE_HMAC_MD5_ARCFOUR;
+ cksize = 8;
+ ckusage = 15;
+ } else {
+ abort();
+ }
+
+ tlen = 20 + mech_krb5.length + cksize;
+ token = malloc(tlen);
+ assert(token != NULL);
+
+ /* Create the ASN.1 wrapper (4 + mech_krb5.length bytes). Assume the ASN.1
+ * lengths fit in one byte since deletion tokens are short. */
+ ptr = token;
+ *ptr++ = 0x60;
+ *ptr++ = tlen - 2;
+ *ptr++ = 0x06;
+ *ptr++ = mech_krb5.length;
+ memcpy(ptr, mech_krb5.elements, mech_krb5.length);
+ ptr += mech_krb5.length;
+
+ /* Create the RFC 1964 token header (8 bytes). */
+ *ptr++ = 0x01;
+ *ptr++ = 0x02;
+ store_16_le(signalg, ptr);
+ ptr += 2;
+ *ptr++ = 0xFF;
+ *ptr++ = 0xFF;
+ *ptr++ = 0xFF;
+ *ptr++ = 0xFF;
+
+ /* Create the checksum (cksize bytes at offset 8 from the header). */
+ d = make_data(ptr - 8, 8);
+ ret = krb5_k_make_checksum(context, cktype, seq, ckusage, &d, &cksum);
+ check_k5err(context, "krb5_k_make_checksum", ret);
+ if (signalg == SGN_ALG_DES_MAC_MD5) {
+ iov.flags = KRB5_CRYPTO_TYPE_DATA;
+ iov.data = make_data(cksum.contents, 16);
+ ret = krb5_k_encrypt_iov(context, seq, 0, NULL, &iov, 1);
+ memcpy(ptr + 8, cksum.contents + 8, 8);
+ } else {
+ memcpy(ptr + 8, cksum.contents, cksize);
+ }
+
+ /* Create the sequence number (8 bytes). */
+ iov.flags = KRB5_CRYPTO_TYPE_DATA;
+ iov.data = make_data(ptr, 8);
+ ptr[4] = ptr[5] = ptr[6] = ptr[7] = lctx->initiate ? 0 : 0xFF;
+ memcpy(iv, ptr + 8, 8);
+ d = make_data(iv, 8);
+ if (signalg == SGN_ALG_HMAC_MD5) {
+ store_32_be(lctx->send_seq, ptr);
+ ret = krb5int_arcfour_gsscrypt(&seq->keyblock, 0, &d, &iov, 1);
+ check_k5err(context, "krb5int_arcfour_gsscrypt(seq)", ret);
+ } else {
+ store_32_le(lctx->send_seq, ptr);
+ ret = krb5_k_encrypt_iov(context, seq, 24, &d, &iov, 1);
+ check_k5err(context, "krb5_k_encrypt_iov(seq)", ret);
+ }
+
+ krb5_free_checksum_contents(context, &cksum);
+ krb5_k_free_key(context, seq);
+ krb5_free_context(context);
+
+ out->length = tlen;
+ out->value = token;
+}
+
+int
+main(int argc, char *argv[])
+{
+ OM_uint32 minor, major, flags;
+ gss_name_t tname;
+ gss_buffer_desc token, in = GSS_C_EMPTY_BUFFER, out;
+ gss_ctx_id_t ictx, actx;
+ gss_krb5_lucid_context_v1_t *lctx;
+ void *lptr;
+
+ assert(argc == 2);
+ tname = import_name(argv[1]);
+
+ flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG;
+ establish_contexts(&mech_krb5, GSS_C_NO_CREDENTIAL, GSS_C_NO_CREDENTIAL,
+ tname, flags, &ictx, &actx, NULL, NULL, NULL);
+
+ /* Export the acceptor context to a lucid context so we can look inside. */
+ major = gss_krb5_export_lucid_sec_context(&minor, &actx, 1, &lptr);
+ check_gsserr("gss_export_lucid_sec_context", major, minor);
+ lctx = lptr;
+ if (!lctx->protocol) {
+ /* Make an RFC 1964 context deletion token and pass it to
+ * gss_process_context_token. */
+ make_delete_token(lctx, &token);
+ major = gss_process_context_token(&minor, ictx, &token);
+ free(token.value);
+ check_gsserr("gss_process_context_token", major, minor);
+ /* Check for the appropriate major code from gss_wrap. */
+ major = gss_wrap(&minor, ictx, 1, GSS_C_QOP_DEFAULT, &in, NULL, &out);
+ assert(major == GSS_S_NO_CONTEXT);
+ } else {
+ /* RFC 4121 defines no context deletion token, so try passing something
+ * arbitrary and check for the appropriate major code. */
+ token.value = "abcd";
+ token.length = 4;
+ major = gss_process_context_token(&minor, ictx, &token);
+ assert(major == GSS_S_DEFECTIVE_TOKEN);
+ }
+
+ (void)gss_release_name(&minor, &tname);
+ (void)gss_delete_sec_context(&minor, &ictx, NULL);
+ (void)gss_krb5_free_lucid_sec_context(&minor, lptr);
+ return 0;
+}
diff --git a/src/tests/gssapi/t_prf.c b/src/tests/gssapi/t_prf.c
new file mode 100644
index 000000000000..2c8c85188ab0
--- /dev/null
+++ b/src/tests/gssapi/t_prf.c
@@ -0,0 +1,194 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright 2014 by the Massachusetts Institute of Technology.
+ * 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 M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+#include "k5-int.h"
+#include "common.h"
+#include "mglueP.h"
+#include "gssapiP_krb5.h"
+
+static const char inputstr[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz123456789";
+
+/* For each test, out1 corresponds to key1 with an empty input, and out2
+ * corresponds to key2 with the above 61-byte input string. */
+static struct {
+ krb5_enctype enctype;
+ const char *key1;
+ const char *out1;
+ const char *key2;
+ const char *out2;
+} tests[] = {
+ { ENCTYPE_DES_CBC_CRC,
+ "E607FE9DABB57AE0",
+ "803C4121379FC4B87CE413B67707C4632EBED2C6D6B7"
+ "2A55E878836E35E21600D915D590DED5B6D77BB30A1F",
+ "54758316B6257A75",
+ "279E4105F7ADC9BD6EF28ABE31D89B442FE0058388BA"
+ "33264ACB5729562DC637950F6BD144B654BE7700B2D6" },
+ { ENCTYPE_DES3_CBC_SHA1,
+ "70378A19CD64134580C27C0115D6B34A1CF2FEECEF9886A2",
+ "9F8D127C520BB826BFF3E0FE5EF352389C17E0C073D9"
+ "AC4A333D644D21BA3EF24F4A886D143F85AC9F6377FB",
+ "3452A167DF1094BA1089E0A20E9E51ABEF1525922558B69E",
+ "6BF24FABC858F8DD9752E4FCD331BB831F238B5BE190"
+ "4EEA42E38F7A60C588F075C5C96A67E7F8B7BD0AECF4" },
+ { ENCTYPE_ARCFOUR_HMAC,
+ "3BB3AE288C12B3B9D06B208A4151B3B6",
+ "9AEA11A3BCF3C53F1F91F5A0BA2132E2501ADF5F3C28"
+ "3C8A983AB88757CE865A22132D6100EAD63E9E291AFA",
+ "6DB7B33A01BD2B72F7655CB7B3D5FA0B",
+ "CDA9A544869FC84873B692663A82AFDA101C8611498B"
+ "A46138B01E927C9B95EEC953B562807434037837DDDF" },
+ { ENCTYPE_AES128_CTS_HMAC_SHA1_96,
+ "6C742096EB896230312B73972FA28B5D",
+ "94208D982FC1BB7778128BDD77904420B45C9DA699F3"
+ "117BCE66E39602128EF0296611A6D191A5828530F20F",
+ "FA61138C109D834A477D24C7311BE6DA",
+ "0FAEDF0F842CC834FEE750487E1B622739286B975FE5"
+ "B7F45AB053143C75CA0DF5D3D4BBB80F6A616C7C9027" },
+ { ENCTYPE_AES256_CTS_HMAC_SHA1_96,
+ "08FCDAFD5832611B73BA7B497FEBFF8C954B4B58031CAD9B977C3B8C25192FD6",
+ "E627EFC14EF5B6D629F830C7109DEA0D3D7D36E8CD57"
+ "A1F301C5452494A1928F05AFFBEE3360232209D3BE0D",
+ "F5B68B7823D8944F33F41541B4E4D38C9B2934F8D16334A796645B066152B4BE",
+ "112F2B2D878590653CCC7DE278E9F0AA46FA5A380B62"
+ "59F774CB7C134FCD37F61A50FD0D9F89BF8FE1A6B593" },
+ { ENCTYPE_CAMELLIA128_CTS_CMAC,
+ "866E0466A178279A32AC0BDA92B72AEB",
+ "97FBB354BF341C3A160DCC86A7A910FDA824601DF677"
+ "68797BACEEBF5D250AE929DEC9760772084267F50A54",
+ "D4893FD37DA1A211E12DD1E03E0F03B7",
+ "1DEE2FF126CA563A2A2326B9DD3F0095013257414C83"
+ "FAD4398901013D55F367C82681186B7B2FE62F746BA4" },
+ { ENCTYPE_CAMELLIA256_CTS_CMAC,
+ "203071B1AE77BD3D6FCE70174AF95C225B1CED46B35CF52B6479EFEB47E6B063",
+ "9B30020634C10FDA28420CEE7B96B70A90A771CED43A"
+ "D8346554163E5949CBAE2FB8EF36AFB6B32CE75116A0",
+ "A171AD582C1AFBBAD52ABD622EE6B6A14D19BF95C6914B2BA40FFD99A88EC660",
+ "A47CBB6E104DCC77E4DB48A7A474B977F2FB6A7A1AB6"
+ "52317D50508AE72B7BE2E4E4BA24164E029CBACF786B" },
+ { ENCTYPE_AES128_CTS_HMAC_SHA256_128,
+ "089BCA48B105EA6EA77CA5D2F39DC5E7",
+ "ED1736209B7C59C9F6A3AE8CCC8A7C97ADFDD11688AD"
+ "F304F2F74252CBACD311A2D9253211FDA49745CE4F62",
+ "3705D96080C17728A0E800EAB6E0D23C",
+ "2BB41B183D76D8D5B30CBB049A7EFE9F350EFA058DC2"
+ "C4D868308D354A7B199BE6FD1F22B53C038BC6036581" },
+ { ENCTYPE_AES256_CTS_HMAC_SHA384_192,
+ "45BD806DBF6A833A9CFFC1C94589A222367A79BC21C413718906E9F578A78467",
+ "1C613AE8B77A3B4D783F3DCE6C9178FC025E87F48A44"
+ "784A69CB5FC697FE266A6141905067EF78566D309085",
+ "6D404D37FAF79F9DF0D33568D320669800EB4836472EA8A026D16B7182460C52",
+ "D15944B0A44508D1E61213F6455F292A02298F870C01"
+ "A3F74AD0345A4A6651EBE101976E933F32D44F0B5947" },
+};
+
+/* Decode hexstr into out. No length checking. */
+static size_t
+fromhex(const char *hexstr, unsigned char *out)
+{
+ const char *p;
+ size_t count;
+
+ for (p = hexstr, count = 0; *p != '\0'; p += 2, count++)
+ sscanf(p, "%2hhx", &out[count]);
+ return count;
+}
+
+int
+main(int argc, char *argv[])
+{
+ OM_uint32 minor, major;
+ gss_ctx_id_t context;
+ gss_union_ctx_id_desc uctx;
+ krb5_gss_ctx_id_rec kgctx;
+ krb5_key k1, k2;
+ krb5_keyblock kb1, kb2;
+ gss_buffer_desc in, out;
+ unsigned char k1buf[32], k2buf[32], outbuf[44];
+ size_t i;
+
+ /*
+ * Fake up just enough of a krb5 GSS context to make gss_pseudo_random
+ * work, with chosen subkeys and acceptor subkeys. If we implement
+ * gss_import_lucid_sec_context, we can rewrite this to use public
+ * interfaces and stop using private headers and internal knowledge of the
+ * implementation.
+ */
+ context = (gss_ctx_id_t)&uctx;
+ memset(&uctx, 0, sizeof(uctx));
+ uctx.mech_type = &mech_krb5;
+ uctx.internal_ctx_id = (gss_ctx_id_t)&kgctx;
+ memset(&kgctx, 0, sizeof(kgctx));
+ kgctx.k5_context = NULL;
+ kgctx.established = 1;
+ kgctx.have_acceptor_subkey = 1;
+ kb1.contents = k1buf;
+ kb2.contents = k2buf;
+ for (i = 0; i < sizeof(tests) / sizeof(*tests); i++) {
+ /* Set up the keys for this test. */
+ kb1.enctype = tests[i].enctype;
+ kb1.length = fromhex(tests[i].key1, k1buf);
+ check_k5err(NULL, "create_key", krb5_k_create_key(NULL, &kb1, &k1));
+ kgctx.subkey = k1;
+ kb2.enctype = tests[i].enctype;
+ kb2.length = fromhex(tests[i].key2, k2buf);
+ check_k5err(NULL, "create_key", krb5_k_create_key(NULL, &kb2, &k2));
+ kgctx.acceptor_subkey = k2;
+
+ /* Generate a PRF value with the subkey and an empty input, and compare
+ * it to the first expected output. */
+ in.length = 0;
+ in.value = NULL;
+ major = gss_pseudo_random(&minor, context, GSS_C_PRF_KEY_PARTIAL, &in,
+ 44, &out);
+ check_gsserr("gss_pseudo_random", major, minor);
+ (void)fromhex(tests[i].out1, outbuf);
+ assert(out.length == 44 && memcmp(out.value, outbuf, 44) == 0);
+ (void)gss_release_buffer(&minor, &out);
+
+ /* Generate a PRF value with the acceptor subkey and the 61-byte input
+ * string, and compare it to the second expected output. */
+ in.length = strlen(inputstr);
+ in.value = (char *)inputstr;
+ major = gss_pseudo_random(&minor, context, GSS_C_PRF_KEY_FULL, &in, 44,
+ &out);
+ check_gsserr("gss_pseudo_random", major, minor);
+ (void)fromhex(tests[i].out2, outbuf);
+ assert(out.length == 44 && memcmp(out.value, outbuf, 44) == 0);
+ (void)gss_release_buffer(&minor, &out);
+
+ /* Also check that generating zero bytes of output works. */
+ major = gss_pseudo_random(&minor, context, GSS_C_PRF_KEY_FULL, &in, 0,
+ &out);
+ check_gsserr("gss_pseudo_random", major, minor);
+ assert(out.length == 0);
+ (void)gss_release_buffer(&minor, &out);
+
+ krb5_k_free_key(NULL, k1);
+ krb5_k_free_key(NULL, k2);
+ }
+ return 0;
+}
diff --git a/src/tests/gssapi/t_s4u.c b/src/tests/gssapi/t_s4u.c
new file mode 100644
index 000000000000..5bc1e4470bdc
--- /dev/null
+++ b/src/tests/gssapi/t_s4u.c
@@ -0,0 +1,314 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright 2009 by the Massachusetts Institute of Technology.
+ * 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 M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+/*
+ * Test program for protocol transition (S4U2Self) and constrained delegation
+ * (S4U2Proxy)
+ *
+ * Note: because of name canonicalization, the following tips may help
+ * when configuring with Active Directory:
+ *
+ * - Create a computer account FOO$
+ * - Set the UPN to host/foo.domain (no suffix); this is necessary to
+ * be able to send an AS-REQ as this principal, otherwise you would
+ * need to use the canonical name (FOO$), which will cause principal
+ * comparison errors in gss_accept_sec_context().
+ * - Add a SPN of host/foo.domain
+ * - Configure the computer account to support constrained delegation with
+ * protocol transition (Trust this computer for delegation to specified
+ * services only / Use any authentication protocol)
+ * - Add host/foo.domain to the keytab (possibly easiest to do this
+ * with ktadd)
+ *
+ * For S4U2Proxy to work the TGT must be forwardable too.
+ *
+ * Usage eg:
+ *
+ * kinit -k -t test.keytab -f 'host/test.win.mit.edu@WIN.MIT.EDU'
+ * ./t_s4u p:delegtest@WIN.MIT.EDU p:HOST/WIN-EQ7E4AA2WR8.win.mit.edu@WIN.MIT.EDU test.keytab
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+
+static int use_spnego = 0;
+
+static void
+test_greet_authz_data(gss_name_t *name)
+{
+ OM_uint32 major, minor;
+ gss_buffer_desc attr;
+ gss_buffer_desc value;
+ gss_name_t canon;
+
+ major = gss_canonicalize_name(&minor, *name, &mech_krb5, &canon);
+ check_gsserr("gss_canonicalize_name", major, minor);
+
+ attr.value = "greet:greeting";
+ attr.length = strlen((char *)attr.value);
+
+ value.value = "Hello, acceptor world!";
+ value.length = strlen((char *)value.value);
+
+ major = gss_set_name_attribute(&minor, canon, 1, &attr, &value);
+ if (major == GSS_S_UNAVAILABLE) {
+ (void)gss_release_name(&minor, &canon);
+ return;
+ }
+ check_gsserr("gss_set_name_attribute", major, minor);
+ gss_release_name(&minor, name);
+ *name = canon;
+}
+
+static void
+init_accept_sec_context(gss_cred_id_t claimant_cred_handle,
+ gss_cred_id_t verifier_cred_handle,
+ gss_cred_id_t *deleg_cred_handle)
+{
+ OM_uint32 major, minor, flags;
+ gss_name_t source_name = GSS_C_NO_NAME, target_name = GSS_C_NO_NAME;
+ gss_ctx_id_t initiator_context, acceptor_context;
+ gss_OID mech = GSS_C_NO_OID;
+
+ *deleg_cred_handle = GSS_C_NO_CREDENTIAL;
+
+ major = gss_inquire_cred(&minor, verifier_cred_handle, &target_name, NULL,
+ NULL, NULL);
+ check_gsserr("gss_inquire_cred", major, minor);
+
+ display_canon_name("Target name", target_name, &mech_krb5);
+
+ mech = use_spnego ? &mech_spnego : &mech_krb5;
+ display_oid("Target mech", mech);
+
+ flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG;
+ establish_contexts(mech, claimant_cred_handle, verifier_cred_handle,
+ target_name, flags, &initiator_context,
+ &acceptor_context, &source_name, &mech,
+ deleg_cred_handle);
+
+ display_canon_name("Source name", source_name, &mech_krb5);
+ display_oid("Source mech", mech);
+ enumerate_attributes(source_name, 1);
+
+ (void)gss_release_name(&minor, &source_name);
+ (void)gss_release_name(&minor, &target_name);
+ (void)gss_delete_sec_context(&minor, &initiator_context, NULL);
+ (void)gss_delete_sec_context(&minor, &acceptor_context, NULL);
+}
+
+static void
+check_ticket_count(gss_cred_id_t cred, int expected)
+{
+ krb5_error_code ret;
+ krb5_context context = NULL;
+ krb5_creds kcred;
+ krb5_cc_cursor cur;
+ krb5_ccache ccache;
+ int count = 0;
+ gss_key_value_set_desc store;
+ gss_key_value_element_desc elem;
+ OM_uint32 major, minor;
+ const char *ccname = "MEMORY:count";
+
+ store.count = 1;
+ store.elements = &elem;
+ elem.key = "ccache";
+ elem.value = ccname;
+ major = gss_store_cred_into(&minor, cred, GSS_C_INITIATE, &mech_krb5, 1, 0,
+ &store, NULL, NULL);
+ check_gsserr("gss_store_cred_into", major, minor);
+
+ ret = krb5_init_context(&context);
+ check_k5err(context, "krb5_init_context", ret);
+
+ ret = krb5_cc_resolve(context, ccname, &ccache);
+ check_k5err(context, "krb5_cc_resolve", ret);
+
+ ret = krb5_cc_start_seq_get(context, ccache, &cur);
+ check_k5err(context, "krb5_cc_start_seq_get", ret);
+
+ while (!krb5_cc_next_cred(context, ccache, &cur, &kcred)) {
+ if (!krb5_is_config_principal(context, kcred.server))
+ count++;
+ krb5_free_cred_contents(context, &kcred);
+ }
+
+ ret = krb5_cc_end_seq_get(context, ccache, &cur);
+ check_k5err(context, "krb5_cc_end_seq_get", ret);
+
+ if (expected != count) {
+ printf("Expected %d tickets but got %d\n", expected, count);
+ exit(1);
+ }
+
+ krb5_cc_destroy(context, ccache);
+ krb5_free_context(context);
+}
+
+static void
+constrained_delegate(gss_OID_set desired_mechs, gss_name_t target,
+ gss_cred_id_t delegated_cred_handle,
+ gss_cred_id_t verifier_cred_handle)
+{
+ OM_uint32 major, minor;
+ gss_ctx_id_t initiator_context = GSS_C_NO_CONTEXT;
+ gss_name_t cred_name = GSS_C_NO_NAME;
+ OM_uint32 time_rec, lifetime;
+ gss_cred_usage_t usage;
+ gss_buffer_desc token;
+ gss_OID_set mechs;
+
+ printf("Constrained delegation tests follow\n");
+ printf("-----------------------------------\n\n");
+
+ if (gss_inquire_cred(&minor, verifier_cred_handle, &cred_name,
+ &lifetime, &usage, NULL) == GSS_S_COMPLETE) {
+ display_canon_name("Proxy name", cred_name, &mech_krb5);
+ (void)gss_release_name(&minor, &cred_name);
+ }
+ display_canon_name("Target name", target, &mech_krb5);
+ if (gss_inquire_cred(&minor, delegated_cred_handle, &cred_name,
+ &lifetime, &usage, &mechs) == GSS_S_COMPLETE) {
+ display_canon_name("Delegated name", cred_name, &mech_krb5);
+ display_oid("Delegated mech", &mechs->elements[0]);
+ (void)gss_release_name(&minor, &cred_name);
+ }
+
+ printf("\n");
+
+ major = gss_init_sec_context(&minor, delegated_cred_handle,
+ &initiator_context, target,
+ mechs ? &mechs->elements[0] : &mech_krb5,
+ GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG,
+ GSS_C_INDEFINITE, GSS_C_NO_CHANNEL_BINDINGS,
+ GSS_C_NO_BUFFER, NULL, &token, NULL,
+ &time_rec);
+ check_gsserr("gss_init_sec_context", major, minor);
+
+ (void)gss_release_buffer(&minor, &token);
+ (void)gss_delete_sec_context(&minor, &initiator_context, NULL);
+
+ /* Ensure a second call does not acquire new ticket. */
+ major = gss_init_sec_context(&minor, delegated_cred_handle,
+ &initiator_context, target,
+ mechs ? &mechs->elements[0] : &mech_krb5,
+ GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG,
+ GSS_C_INDEFINITE, GSS_C_NO_CHANNEL_BINDINGS,
+ GSS_C_NO_BUFFER, NULL, &token, NULL,
+ &time_rec);
+ check_gsserr("gss_init_sec_context", major, minor);
+
+ (void)gss_release_buffer(&minor, &token);
+ (void)gss_delete_sec_context(&minor, &initiator_context, NULL);
+ (void)gss_release_oid_set(&minor, &mechs);
+
+ /* We expect three tickets: our TGT, the evidence ticket, and the ticket to
+ * the target service. */
+ check_ticket_count(delegated_cred_handle, 3);
+}
+
+int
+main(int argc, char *argv[])
+{
+ OM_uint32 minor, major;
+ gss_cred_id_t impersonator_cred_handle = GSS_C_NO_CREDENTIAL;
+ gss_cred_id_t user_cred_handle = GSS_C_NO_CREDENTIAL;
+ gss_cred_id_t delegated_cred_handle = GSS_C_NO_CREDENTIAL;
+ gss_name_t user = GSS_C_NO_NAME, target = GSS_C_NO_NAME;
+ gss_OID_set mechs;
+
+ if (argc < 2 || argc > 5) {
+ fprintf(stderr, "Usage: %s [--spnego] [user] "
+ "[proxy-target] [keytab]\n", argv[0]);
+ fprintf(stderr, " proxy-target and keytab are optional\n");
+ exit(1);
+ }
+
+ if (strcmp(argv[1], "--spnego") == 0) {
+ use_spnego++;
+ argc--;
+ argv++;
+ }
+
+ user = import_name(argv[1]);
+
+ if (argc > 2 && strcmp(argv[2], "-"))
+ target = import_name(argv[2]);
+
+ if (argc > 3) {
+ major = krb5_gss_register_acceptor_identity(argv[3]);
+ check_gsserr("krb5_gss_register_acceptor_identity", major, 0);
+ }
+
+ /* Get default cred. */
+ mechs = use_spnego ? &mechset_spnego : &mechset_krb5;
+ major = gss_acquire_cred(&minor, GSS_C_NO_NAME, GSS_C_INDEFINITE, mechs,
+ GSS_C_BOTH, &impersonator_cred_handle, NULL,
+ NULL);
+ check_gsserr("gss_acquire_cred", major, minor);
+
+ printf("Protocol transition tests follow\n");
+ printf("-----------------------------------\n\n");
+
+ test_greet_authz_data(&user);
+
+ /* Get S4U2Self cred. */
+ major = gss_acquire_cred_impersonate_name(&minor, impersonator_cred_handle,
+ user, GSS_C_INDEFINITE, mechs,
+ GSS_C_INITIATE,
+ &user_cred_handle, NULL, NULL);
+ check_gsserr("gss_acquire_cred_impersonate_name", major, minor);
+
+ init_accept_sec_context(user_cred_handle, impersonator_cred_handle,
+ &delegated_cred_handle);
+ printf("\n");
+
+ if (target != GSS_C_NO_NAME &&
+ delegated_cred_handle != GSS_C_NO_CREDENTIAL) {
+ constrained_delegate(mechs, target, delegated_cred_handle,
+ impersonator_cred_handle);
+ } else if (target != GSS_C_NO_NAME) {
+ fprintf(stderr, "Warning: no delegated cred handle returned\n\n");
+ fprintf(stderr, "Verify:\n\n");
+ fprintf(stderr, " - The TGT for the impersonating service is "
+ "forwardable\n");
+ fprintf(stderr, " - The T2A4D flag set on the impersonating service's "
+ "UAC\n");
+ fprintf(stderr, " - The user is not marked sensitive and cannot be "
+ "delegated\n");
+ fprintf(stderr, "\n");
+ }
+
+ (void)gss_release_name(&minor, &user);
+ (void)gss_release_name(&minor, &target);
+ (void)gss_release_cred(&minor, &delegated_cred_handle);
+ (void)gss_release_cred(&minor, &impersonator_cred_handle);
+ (void)gss_release_cred(&minor, &user_cred_handle);
+ return 0;
+}
diff --git a/src/tests/gssapi/t_s4u.py b/src/tests/gssapi/t_s4u.py
new file mode 100755
index 000000000000..7366e3915ee3
--- /dev/null
+++ b/src/tests/gssapi/t_s4u.py
@@ -0,0 +1,162 @@
+#!/usr/bin/python
+from k5test import *
+
+realm = K5Realm(create_host=False, get_creds=False)
+usercache = 'FILE:' + os.path.join(realm.testdir, 'usercache')
+storagecache = 'FILE:' + os.path.join(realm.testdir, 'save')
+
+# Create two service principals with keys in the default keytab.
+service1 = 'service/1@%s' % realm.realm
+realm.addprinc(service1)
+realm.extract_keytab(service1, realm.keytab)
+service2 = 'service/2@%s' % realm.realm
+realm.addprinc(service2)
+realm.extract_keytab(service2, realm.keytab)
+
+puser = 'p:' + realm.user_princ
+pservice1 = 'p:' + service1
+pservice2 = 'p:' + service2
+
+# Get forwardable creds for service1 in the default cache.
+realm.kinit(service1, None, ['-f', '-k'])
+
+# Try krb5 -> S4U2Proxy with forwardable user creds. This should fail
+# at the S4U2Proxy step since the DB2 back end currently has no
+# support for allowing it.
+realm.kinit(realm.user_princ, password('user'), ['-f', '-c', usercache])
+output = realm.run(['./t_s4u2proxy_krb5', usercache, storagecache, '-',
+ pservice1, pservice2], expected_code=1)
+if ('auth1: ' + realm.user_princ not in output or
+ 'NOT_ALLOWED_TO_DELEGATE' not in output):
+ fail('krb5 -> s4u2proxy')
+
+# Again with SPNEGO.
+output = realm.run(['./t_s4u2proxy_krb5', '--spnego', usercache, storagecache,
+ '-', pservice1, pservice2],
+ expected_code=1)
+if ('auth1: ' + realm.user_princ not in output or
+ 'NOT_ALLOWED_TO_DELEGATE' not in output):
+ fail('krb5 -> s4u2proxy (SPNEGO)')
+
+# Try krb5 -> S4U2Proxy without forwardable user creds. This should
+# result in no delegated credential being created by
+# accept_sec_context.
+realm.kinit(realm.user_princ, password('user'), ['-c', usercache])
+output = realm.run(['./t_s4u2proxy_krb5', usercache, storagecache, pservice1,
+ pservice1, pservice2])
+if 'no credential delegated' not in output:
+ fail('krb5 -> no delegated cred')
+
+# Try S4U2Self. Ask for an S4U2Proxy step; this won't happen because
+# service/1 isn't allowed to get a forwardable S4U2Self ticket.
+output = realm.run(['./t_s4u', puser, pservice2])
+if ('Warning: no delegated cred handle' not in output or
+ 'Source name:\t' + realm.user_princ not in output):
+ fail('s4u2self')
+output = realm.run(['./t_s4u', '--spnego', puser, pservice2])
+if ('Warning: no delegated cred handle' not in output or
+ 'Source name:\t' + realm.user_princ not in output):
+ fail('s4u2self (SPNEGO)')
+
+# Correct that problem and try again. As above, the S4U2Proxy step
+# won't actually succeed since we don't support that in DB2.
+realm.run([kadminl, 'modprinc', '+ok_to_auth_as_delegate', service1])
+output = realm.run(['./t_s4u', puser, pservice2], expected_code=1)
+if 'NOT_ALLOWED_TO_DELEGATE' not in output:
+ fail('s4u2self')
+
+# Again with SPNEGO. This uses SPNEGO for the initial authentication,
+# but still uses krb5 for S4U2Proxy--the delegated cred is returned as
+# a krb5 cred, not a SPNEGO cred, and t_s4u uses the delegated cred
+# directly rather than saving and reacquiring it.
+output = realm.run(['./t_s4u', '--spnego', puser, pservice2], expected_code=1)
+if 'NOT_ALLOWED_TO_DELEGATE' not in output:
+ fail('s4u2self')
+
+realm.stop()
+
+# Set up a realm using the test KDB module so that we can do
+# successful S4U2Proxy delegations.
+testprincs = {'krbtgt/KRBTEST.COM': {'keys': 'aes128-cts'},
+ 'user': {'keys': 'aes128-cts'},
+ 'service/1': {'flags': '+ok-to-auth-as-delegate',
+ 'keys': 'aes128-cts'},
+ 'service/2': {'keys': 'aes128-cts'}}
+conf = {'realms': {'$realm': {'database_module': 'test'}},
+ 'dbmodules': {'test': {'db_library': 'test',
+ 'princs': testprincs,
+ 'delegation': {'service/1': 'service/2'}}}}
+realm = K5Realm(create_kdb=False, kdc_conf=conf)
+userkeytab = 'FILE:' + os.path.join(realm.testdir, 'userkeytab')
+realm.extract_keytab(realm.user_princ, userkeytab)
+realm.extract_keytab(service1, realm.keytab)
+realm.extract_keytab(service2, realm.keytab)
+realm.start_kdc()
+
+# Get forwardable creds for service1 in the default cache.
+realm.kinit(service1, None, ['-f', '-k'])
+
+# Successful krb5 -> S4U2Proxy, with krb5 and SPNEGO mechs.
+realm.kinit(realm.user_princ, None, ['-f', '-k', '-c', usercache,
+ '-t', userkeytab])
+out = realm.run(['./t_s4u2proxy_krb5', usercache, storagecache, '-',
+ pservice1, pservice2])
+if 'auth1: user@' not in out or 'auth2: user@' not in out:
+ fail('krb5 -> s4u2proxy')
+out = realm.run(['./t_s4u2proxy_krb5', '--spnego', usercache, storagecache,
+ '-', pservice1, pservice2])
+if 'auth1: user@' not in out or 'auth2: user@' not in out:
+ fail('krb5 -> s4u2proxy')
+
+# Successful S4U2Self -> S4U2Proxy.
+out = realm.run(['./t_s4u', puser, pservice2])
+
+# Regression test for #8139: get a user ticket directly for service1 and
+# try krb5 -> S4U2Proxy.
+realm.kinit(realm.user_princ, None, ['-f', '-k', '-c', usercache,
+ '-t', userkeytab, '-S', service1])
+out = realm.run(['./t_s4u2proxy_krb5', usercache, storagecache, '-',
+ pservice1, pservice2])
+if 'auth1: user@' not in out or 'auth2: user@' not in out:
+ fail('krb5 -> s4u2proxy')
+
+# Simulate a krbtgt rollover and verify that the user ticket can still
+# be validated.
+realm.stop_kdc()
+newtgt_keys = ['2 aes128-cts', '1 aes128-cts']
+newtgt_princs = {'krbtgt/KRBTEST.COM': {'keys': newtgt_keys}}
+newtgt_conf = {'dbmodules': {'test': {'princs': newtgt_princs}}}
+newtgt_env = realm.special_env('newtgt', True, kdc_conf=newtgt_conf)
+realm.start_kdc(env=newtgt_env)
+out = realm.run(['./t_s4u2proxy_krb5', usercache, storagecache, '-',
+ pservice1, pservice2])
+if 'auth1: user@' not in out or 'auth2: user@' not in out:
+ fail('krb5 -> s4u2proxy')
+
+# Get a user ticket after the krbtgt rollover and verify that
+# S4U2Proxy delegation works (also a #8139 regression test).
+realm.kinit(realm.user_princ, None, ['-f', '-k', '-c', usercache,
+ '-t', userkeytab])
+out = realm.run(['./t_s4u2proxy_krb5', usercache, storagecache, '-',
+ pservice1, pservice2])
+if 'auth1: user@' not in out or 'auth2: user@' not in out:
+ fail('krb5 -> s4u2proxy')
+
+realm.stop()
+
+# Exercise cross-realm S4U2Self. The query in the foreign realm will
+# fail, but we can check that the right server principal was used.
+r1, r2 = cross_realms(2, create_user=False)
+r1.run([kinit, '-k', r1.host_princ])
+out = r1.run(['./t_s4u', 'p:' + r2.host_princ], expected_code=1)
+if 'Server not found in Kerberos database' not in out:
+ fail('cross-realm s4u2self (t_s4u output)')
+r1.stop()
+r2.stop()
+with open(os.path.join(r2.testdir, 'kdc.log')) as f:
+ kdclog = f.read()
+exp_princ = r1.host_princ.replace('/', '\\/').replace('@', '\\@')
+if ('for %s@%s, Server not found' % (exp_princ, r2.realm)) not in kdclog:
+ fail('cross-realm s4u2self (kdc log)')
+
+success('S4U test cases')
diff --git a/src/tests/gssapi/t_s4u2proxy_krb5.c b/src/tests/gssapi/t_s4u2proxy_krb5.c
new file mode 100644
index 000000000000..0027f1f3ff38
--- /dev/null
+++ b/src/tests/gssapi/t_s4u2proxy_krb5.c
@@ -0,0 +1,164 @@
+/* -*- mode: c; indent-tabs-mode: nil -*- */
+/* tests/gssapi/t_s4u2proxy_deleg.c - Test S4U2Proxy after krb5 auth */
+/*
+ * Copyright 2011 by the Massachusetts Institute of Technology.
+ * 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 M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+
+/*
+ * Usage: ./t_s4u2proxy_krb5 [--spnego] client_cache storage_cache
+ * [accname|-] service1 service2
+ *
+ * This program performs a regular Kerberos or SPNEGO authentication from the
+ * default principal of client_cache to service1. If that authentication
+ * yields delegated credentials, the program stores those credentials in
+ * sorage_ccache and uses that cache to perform a second authentication to
+ * service2 using S4U2Proxy.
+ *
+ * The default keytab must contain keys for service1 and service2. The default
+ * ccache must contain a TGT for service1. This program assumes that krb5 or
+ * SPNEGO authentication requires only one token exchange.
+ */
+
+int
+main(int argc, char *argv[])
+{
+ const char *client_ccname, *storage_ccname, *accname, *service1, *service2;
+ krb5_context context = NULL;
+ krb5_error_code ret;
+ krb5_boolean use_spnego = FALSE;
+ krb5_ccache storage_ccache = NULL;
+ krb5_principal client_princ = NULL;
+ OM_uint32 minor, major, flags;
+ gss_buffer_desc buf = GSS_C_EMPTY_BUFFER;
+ gss_OID mech;
+ gss_OID_set mechs;
+ gss_name_t acceptor_name = GSS_C_NO_NAME, client_name = GSS_C_NO_NAME;
+ gss_name_t service1_name = GSS_C_NO_NAME, service2_name = GSS_C_NO_NAME;
+ gss_cred_id_t service1_cred = GSS_C_NO_CREDENTIAL;
+ gss_cred_id_t deleg_cred = GSS_C_NO_CREDENTIAL;
+ gss_ctx_id_t initiator_context, acceptor_context;
+
+ /* Parse arguments. */
+ if (argc >= 2 && strcmp(argv[1], "--spnego") == 0) {
+ use_spnego = TRUE;
+ argc--;
+ argv++;
+ }
+ if (argc != 6) {
+ fprintf(stderr, "./t_s4u2proxy_krb5 [--spnego] client_ccache "
+ "storage_ccache [accname|-] service1 service2\n");
+ return 1;
+ }
+ client_ccname = argv[1];
+ storage_ccname = argv[2];
+ accname = argv[3];
+ service1 = argv[4];
+ service2 = argv[5];
+
+ mech = use_spnego ? &mech_spnego : &mech_krb5;
+ mechs = use_spnego ? &mechset_spnego : &mechset_krb5;
+ ret = krb5_init_context(&context);
+ check_k5err(context, "krb5_init_context", ret);
+
+ /* Get GSS_C_BOTH acceptor credentials, using the default ccache. */
+ acceptor_name = GSS_C_NO_NAME;
+ if (strcmp(accname, "-") != 0)
+ acceptor_name = import_name(service1);
+ major = gss_acquire_cred(&minor, acceptor_name, GSS_C_INDEFINITE,
+ mechs, GSS_C_BOTH, &service1_cred, NULL, NULL);
+ check_gsserr("gss_acquire_cred(service1)", major, minor);
+
+ /* Establish contexts using the client ccache. */
+ service1_name = import_name(service1);
+ major = gss_krb5_ccache_name(&minor, client_ccname, NULL);
+ check_gsserr("gss_krb5_ccache_name(1)", major, minor);
+ flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG;
+ establish_contexts(mech, GSS_C_NO_CREDENTIAL, service1_cred, service1_name,
+ flags, &initiator_context, &acceptor_context,
+ &client_name, NULL, &deleg_cred);
+
+ /* Display and remember the client principal. */
+ major = gss_display_name(&minor, client_name, &buf, NULL);
+ check_gsserr("gss_display_name(1)", major, minor);
+ printf("auth1: %.*s\n", (int)buf.length, (char *)buf.value);
+ /* Assumes buffer is null-terminated, which in our implementation it is. */
+ ret = krb5_parse_name(context, buf.value, &client_princ);
+ check_k5err(context, "krb5_parse_name", ret);
+ (void)gss_release_buffer(&minor, &buf);
+
+ if (deleg_cred == GSS_C_NO_CREDENTIAL) {
+ printf("no credential delegated.\n");
+ goto cleanup;
+ }
+
+ /* Take the opportunity to test cred export/import on the synthesized
+ * S4U2Proxy delegated cred. */
+ export_import_cred(&deleg_cred);
+
+ /* Store the delegated credentials. */
+ ret = krb5_cc_resolve(context, storage_ccname, &storage_ccache);
+ check_k5err(context, "krb5_cc_resolve", ret);
+ ret = krb5_cc_initialize(context, storage_ccache, client_princ);
+ check_k5err(context, "krb5_cc_initialize", ret);
+ major = gss_krb5_copy_ccache(&minor, deleg_cred, storage_ccache);
+ check_gsserr("gss_krb5_copy_ccache", major, minor);
+ ret = krb5_cc_close(context, storage_ccache);
+ check_k5err(context, "krb5_cc_close", ret);
+
+ (void)gss_delete_sec_context(&minor, &initiator_context, GSS_C_NO_BUFFER);
+ (void)gss_delete_sec_context(&minor, &acceptor_context, GSS_C_NO_BUFFER);
+ (void)gss_release_name(&minor, &client_name);
+ (void)gss_release_cred(&minor, &deleg_cred);
+
+ /* Establish contexts using the storage ccache. */
+ service2_name = import_name(service2);
+ major = gss_krb5_ccache_name(&minor, storage_ccname, NULL);
+ check_gsserr("gss_krb5_ccache_name(2)", major, minor);
+ establish_contexts(mech, GSS_C_NO_CREDENTIAL, GSS_C_NO_CREDENTIAL,
+ service2_name, flags, &initiator_context,
+ &acceptor_context, &client_name, NULL, &deleg_cred);
+
+ major = gss_display_name(&minor, client_name, &buf, NULL);
+ check_gsserr("gss_display_name(2)", major, minor);
+ printf("auth2: %.*s\n", (int)buf.length, (char *)buf.value);
+ (void)gss_release_buffer(&minor, &buf);
+
+cleanup:
+ (void)gss_release_name(&minor, &acceptor_name);
+ (void)gss_release_name(&minor, &client_name);
+ (void)gss_release_name(&minor, &service1_name);
+ (void)gss_release_name(&minor, &service2_name);
+ (void)gss_release_cred(&minor, &service1_cred);
+ (void)gss_release_cred(&minor, &deleg_cred);
+ (void)gss_delete_sec_context(&minor, &initiator_context, GSS_C_NO_BUFFER);
+ (void)gss_delete_sec_context(&minor, &acceptor_context, GSS_C_NO_BUFFER);
+ krb5_free_principal(context, client_princ);
+ krb5_free_context(context);
+ return 0;
+}
diff --git a/src/tests/gssapi/t_saslname.c b/src/tests/gssapi/t_saslname.c
new file mode 100644
index 000000000000..b874caf97f1b
--- /dev/null
+++ b/src/tests/gssapi/t_saslname.c
@@ -0,0 +1,165 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright 2009 by the Massachusetts Institute of Technology.
+ * 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 M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+
+static void
+dump_known_mech_attrs(gss_OID mech)
+{
+ OM_uint32 major, minor;
+ gss_OID_set mech_attrs = GSS_C_NO_OID_SET;
+ gss_OID_set known_attrs = GSS_C_NO_OID_SET;
+ size_t i;
+
+ major = gss_inquire_attrs_for_mech(&minor, mech, &mech_attrs,
+ &known_attrs);
+ check_gsserr("gss_inquire_attrs_for_mech", major, minor);
+
+ printf("Known attributes\n");
+ printf("----------------\n");
+ for (i = 0; i < known_attrs->count; i++) {
+ gss_buffer_desc name = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc short_desc = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc long_desc = GSS_C_EMPTY_BUFFER;
+
+ major = gss_display_mech_attr(&minor, &known_attrs->elements[i],
+ &name, &short_desc, &long_desc);
+ check_gsserr("gss_display_mech_attr", major, minor);
+ printf("%.*s (%.*s): %.*s\n", (int)short_desc.length,
+ (char *)short_desc.value, (int)name.length, (char *)name.value,
+ (int)long_desc.length, (char *)long_desc.value);
+ (void)gss_release_buffer(&minor, &name);
+ (void)gss_release_buffer(&minor, &short_desc);
+ (void)gss_release_buffer(&minor, &long_desc);
+ }
+ printf("\n");
+ (void)gss_release_oid_set(&minor, &mech_attrs);
+ (void)gss_release_oid_set(&minor, &known_attrs);
+}
+
+static void
+dump_mech_attrs(gss_OID mech)
+{
+ OM_uint32 major, minor;
+ gss_OID_set mech_attrs = GSS_C_NO_OID_SET;
+ gss_OID_set known_attrs = GSS_C_NO_OID_SET;
+ size_t i;
+
+ major = gss_inquire_attrs_for_mech(&minor, mech, &mech_attrs,
+ &known_attrs);
+ check_gsserr("gss_inquire_attrs_for_mech", major, minor);
+
+ printf("Mech attrs: ");
+
+ for (i = 0; i < mech_attrs->count; i++) {
+ gss_buffer_desc name = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc short_desc = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc long_desc = GSS_C_EMPTY_BUFFER;
+
+ major = gss_display_mech_attr(&minor, &mech_attrs->elements[i],
+ &name, &short_desc, &long_desc);
+ check_gsserr("gss_display_mech_attr", major, minor);
+ printf("%.*s ", (int)name.length, (char *)name.value);
+ (void)gss_release_buffer(&minor, &name);
+ (void)gss_release_buffer(&minor, &short_desc);
+ (void)gss_release_buffer(&minor, &long_desc);
+ }
+ printf("\n");
+
+ (void)gss_release_oid_set(&minor, &mech_attrs);
+ (void)gss_release_oid_set(&minor, &known_attrs);
+}
+
+int
+main(int argc, char *argv[])
+{
+ gss_OID_set mechs;
+ OM_uint32 major, minor;
+ size_t i;
+
+ major = gss_indicate_mechs(&minor, &mechs);
+ check_gsserr("gss_indicate_mechs", major, minor);
+ if (mechs->count > 0)
+ dump_known_mech_attrs(mechs->elements);
+
+ for (i = 0; i < mechs->count; i++) {
+ gss_buffer_desc oidstr = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc sasl_mech_name = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc mech_name = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc mech_description = GSS_C_EMPTY_BUFFER;
+ gss_OID oid = GSS_C_NO_OID;
+
+ major = gss_oid_to_str(&minor, &mechs->elements[i], &oidstr);
+ if (GSS_ERROR(major))
+ continue;
+
+ major = gss_inquire_saslname_for_mech(&minor, &mechs->elements[i],
+ &sasl_mech_name, &mech_name,
+ &mech_description);
+ if (GSS_ERROR(major)) {
+ gss_release_buffer(&minor, &oidstr);
+ continue;
+ }
+
+ printf("-------------------------------------------------------------"
+ "-----------------\n");
+ printf("OID : %.*s\n", (int)oidstr.length,
+ (char *)oidstr.value);
+ printf("SASL mech : %.*s\n", (int)sasl_mech_name.length,
+ (char *)sasl_mech_name.value);
+ printf("Mech name : %.*s\n", (int)mech_name.length,
+ (char *)mech_name.value);
+ printf("Mech desc : %.*s\n", (int)mech_description.length,
+ (char *)mech_description.value);
+ dump_mech_attrs(&mechs->elements[i]);
+ printf("-------------------------------------------------------------"
+ "-----------------\n");
+
+ major = gss_inquire_mech_for_saslname(&minor, &sasl_mech_name, &oid);
+ check_gsserr("gss_inquire_mech_for_saslname", major, minor);
+
+ if (oid == GSS_C_NO_OID ||
+ (oid->length != mechs->elements[i].length &&
+ memcmp(oid->elements, mechs->elements[i].elements,
+ oid->length) != 0)) {
+ (void)gss_release_buffer(&minor, &oidstr);
+ (void)gss_oid_to_str(&minor, oid, &oidstr);
+ fprintf(stderr, "Got different OID %.*s for mechanism %.*s\n",
+ (int)oidstr.length, (char *)oidstr.value,
+ (int)sasl_mech_name.length, (char *)sasl_mech_name.value);
+ }
+ (void)gss_release_buffer(&minor, &oidstr);
+ (void)gss_release_buffer(&minor, &sasl_mech_name);
+ (void)gss_release_buffer(&minor, &mech_name);
+ (void)gss_release_buffer(&minor, &mech_description);
+ }
+
+ (void)gss_release_oid_set(&minor, &mechs);
+ return 0;
+}
diff --git a/src/tests/gssapi/t_spnego.c b/src/tests/gssapi/t_spnego.c
new file mode 100644
index 000000000000..2483228b1b88
--- /dev/null
+++ b/src/tests/gssapi/t_spnego.c
@@ -0,0 +1,314 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright 2010 by the Massachusetts Institute of Technology.
+ * 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 M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "common.h"
+
+static gss_OID_desc mech_krb5_wrong = {
+ 9, "\052\206\110\202\367\022\001\002\002"
+};
+gss_OID_set_desc mechset_krb5_wrong = { 1, &mech_krb5_wrong };
+
+/*
+ * Test program for SPNEGO and gss_set_neg_mechs
+ *
+ * Example usage:
+ *
+ * kinit testuser
+ * ./t_spnego host/test.host@REALM testhost.keytab
+ */
+
+/* Replace *tok and *len with the concatenation of prefix and *tok. */
+static void
+prepend(const void *prefix, size_t plen, uint8_t **tok, size_t *len)
+{
+ uint8_t *newtok;
+
+ newtok = malloc(plen + *len);
+ assert(newtok != NULL);
+ memcpy(newtok, prefix, plen);
+ memcpy(newtok + plen, *tok, *len);
+ free(*tok);
+ *tok = newtok;
+ *len = plen + *len;
+}
+
+/* Replace *tok and *len with *tok wrapped in a DER tag with the given tag
+ * byte. *len must be less than 2^16. */
+static void
+der_wrap(uint8_t tag, uint8_t **tok, size_t *len)
+{
+ char lenbuf[3];
+ uint8_t *wrapped;
+ size_t llen;
+
+ if (*len < 128) {
+ lenbuf[0] = *len;
+ llen = 1;
+ } else if (*len < 256) {
+ lenbuf[0] = 0x81;
+ lenbuf[1] = *len;
+ llen = 2;
+ } else {
+ assert(*len >> 16 == 0);
+ lenbuf[0] = 0x82;
+ lenbuf[1] = *len >> 8;
+ lenbuf[2] = *len & 0xFF;
+ llen = 3;
+ }
+ wrapped = malloc(1 + llen + *len);
+ assert(wrapped != NULL);
+ *wrapped = tag;
+ memcpy(wrapped + 1, lenbuf, llen);
+ memcpy(wrapped + 1 + llen, *tok, *len);
+ free(*tok);
+ *tok = wrapped;
+ *len = 1 + llen + *len;
+}
+
+/*
+ * Create a SPNEGO initiator token for the erroneous Microsoft krb5 mech OID,
+ * wrapping a krb5 token ktok. The token should look like:
+ *
+ * 60 <len> (GSS framing sequence)
+ * 06 06 2B 06 01 05 05 02 (SPNEGO OID)
+ * A0 <len> (NegotiationToken choice 0, negTokenInit)
+ * 30 <len> (sequence)
+ * A0 0D (context tag 0, mechTypes)
+ * 30 0B (sequence of)
+ * 06 09 2A 86 48 82 F7 12 01 02 02 (wrong krb5 OID)
+ * A2 <len> (context tag 2, mechToken)
+ * 04 <len> (octet string)
+ * <mech token octets>
+ */
+static void
+create_mskrb5_spnego_token(gss_buffer_t ktok, gss_buffer_desc *tok_out)
+{
+ uint8_t *tok;
+ size_t len;
+
+ len = ktok->length;
+ tok = malloc(len);
+ assert(tok != NULL);
+ memcpy(tok, ktok->value, len);
+ /* Wrap the krb5 token in OCTET STRING and [2] tags. */
+ der_wrap(0x04, &tok, &len);
+ der_wrap(0xA2, &tok, &len);
+ /* Prepend the wrong krb5 OID inside OBJECT IDENTIFIER and [0] tags. */
+ prepend("\xA0\x0D\x30\x0B\x06\x09\x2A\x86\x48\x82\xF7\x12\x01\x02\x02", 15,
+ &tok, &len);
+ /* Wrap the previous two things in SEQUENCE and [0] tags. */
+ der_wrap(0x30, &tok, &len);
+ der_wrap(0xA0, &tok, &len);
+ /* Prepend the SPNEGO OID in an OBJECT IDENTIFIER tag. */
+ prepend("\x06\x06\x2B\x06\x01\x05\x05\x02", 8, &tok, &len);
+ /* Wrap the whole thing in an [APPLICATION 0] tag. */
+ der_wrap(0x60, &tok, &len);
+ tok_out->value = tok;
+ tok_out->length = len;
+}
+
+/*
+ * Test that the SPNEGO acceptor code accepts and properly reflects back the
+ * erroneous Microsoft mech OID in the supportedMech field of the NegTokenResp
+ * message. Use acred as the verifier cred handle.
+ */
+static void
+test_mskrb_oid(gss_name_t tname, gss_cred_id_t acred)
+{
+ OM_uint32 major, minor;
+ gss_ctx_id_t ictx = GSS_C_NO_CONTEXT, actx = GSS_C_NO_CONTEXT;
+ gss_buffer_desc atok = GSS_C_EMPTY_BUFFER, ktok = GSS_C_EMPTY_BUFFER, stok;
+ const unsigned char *atok_oid;
+
+ /*
+ * Our SPNEGO mech no longer acquires creds for the wrong mech OID, so we
+ * have to construct a SPNEGO token ourselves.
+ */
+ major = gss_init_sec_context(&minor, GSS_C_NO_CREDENTIAL, &ictx, tname,
+ &mech_krb5, 0, GSS_C_INDEFINITE,
+ GSS_C_NO_CHANNEL_BINDINGS, &atok, NULL, &ktok,
+ NULL, NULL);
+ check_gsserr("gss_init_sec_context(mskrb)", major, minor);
+ assert(major == GSS_S_COMPLETE);
+ create_mskrb5_spnego_token(&ktok, &stok);
+
+ /*
+ * Look directly at the DER encoding of the response token. Since we
+ * didn't request mutual authentication, the SPNEGO reply will contain no
+ * underlying mech token; therefore, the encoding of the correct
+ * NegotiationToken response is completely predictable:
+ *
+ * A1 14 (choice 1, length 20, meaning negTokenResp)
+ * 30 12 (sequence, length 18)
+ * A0 03 (context tag 0, length 3)
+ * 0A 01 00 (enumerated value 0, meaning accept-completed)
+ * A1 0B (context tag 1, length 11)
+ * 06 09 (object identifier, length 9)
+ * 2A 86 48 82 F7 12 01 02 02 (the erroneous krb5 OID)
+ *
+ * So we can just compare the length to 22 and the nine bytes at offset 13
+ * to the expected OID.
+ */
+ major = gss_accept_sec_context(&minor, &actx, acred, &stok,
+ GSS_C_NO_CHANNEL_BINDINGS, NULL,
+ NULL, &atok, NULL, NULL, NULL);
+ check_gsserr("gss_accept_sec_context(mskrb)", major, minor);
+ assert(atok.length == 22);
+ atok_oid = (unsigned char *)atok.value + 13;
+ assert(memcmp(atok_oid, mech_krb5_wrong.elements, 9) == 0);
+
+ (void)gss_delete_sec_context(&minor, &ictx, NULL);
+ (void)gss_delete_sec_context(&minor, &actx, NULL);
+ (void)gss_release_buffer(&minor, &ktok);
+ (void)gss_release_buffer(&minor, &atok);
+ free(stok.value);
+}
+
+/* Check that we return a compatibility NegTokenInit2 message containing
+ * NegHints for an empty initiator token. */
+static void
+test_neghints()
+{
+ OM_uint32 major, minor;
+ gss_buffer_desc itok = GSS_C_EMPTY_BUFFER, atok;
+ gss_ctx_id_t actx = GSS_C_NO_CONTEXT;
+ const char *expected =
+ /* RFC 2743 token framing: [APPLICATION 0] IMPLICIT SEQUENCE followed
+ * by OBJECT IDENTIFIER and the SPNEGO OID */
+ "\x60\x47\x06\x06" "\x2B\x06\x01\x05\x05\x02"
+ /* [0] SEQUENCE for the NegotiationToken negtokenInit choice */
+ "\xA0\x3D\x30\x3B"
+ /* [0] MechTypeList containing the krb5 OID */
+ "\xA0\x0D\x30\x0B\x06\x09" "\x2A\x86\x48\x86\xF7\x12\x01\x02\x02"
+ /* [3] NegHints containing [0] GeneralString containing the dummy
+ * hintName string defined in [MS-SPNG] */
+ "\xA3\x2A\x30\x28\xA0\x26\x1B\x24"
+ "not_defined_in_RFC4178@please_ignore";
+
+ /* Produce a hint token. */
+ major = gss_accept_sec_context(&minor, &actx, GSS_C_NO_CREDENTIAL, &itok,
+ GSS_C_NO_CHANNEL_BINDINGS, NULL, NULL,
+ &atok, NULL, NULL, NULL);
+ check_gsserr("gss_accept_sec_context(neghints)", major, minor);
+
+ /* Verify it against the expected contents, which are fixed as long as we
+ * only list the krb5 mech in the token. */
+ assert(atok.length == strlen(expected));
+ assert(memcmp(atok.value, expected, atok.length) == 0);
+
+ (void)gss_release_buffer(&minor, &atok);
+ (void)gss_delete_sec_context(&minor, &actx, NULL);
+}
+
+int
+main(int argc, char *argv[])
+{
+ OM_uint32 minor, major, flags;
+ gss_cred_id_t verifier_cred_handle = GSS_C_NO_CREDENTIAL;
+ gss_cred_id_t initiator_cred_handle = GSS_C_NO_CREDENTIAL;
+ gss_OID_set actual_mechs = GSS_C_NO_OID_SET;
+ gss_ctx_id_t initiator_context, acceptor_context;
+ gss_name_t target_name, source_name = GSS_C_NO_NAME;
+ gss_OID mech = GSS_C_NO_OID;
+ gss_OID_desc pref_oids[2];
+ gss_OID_set_desc pref_mechs;
+
+ if (argc < 2 || argc > 3) {
+ fprintf(stderr, "Usage: %s target_name [keytab]\n", argv[0]);
+ exit(1);
+ }
+
+ target_name = import_name(argv[1]);
+
+ if (argc >= 3) {
+ major = krb5_gss_register_acceptor_identity(argv[2]);
+ check_gsserr("krb5_gss_register_acceptor_identity", major, 0);
+ }
+
+ /* Get default initiator cred. */
+ major = gss_acquire_cred(&minor, GSS_C_NO_NAME, GSS_C_INDEFINITE,
+ &mechset_spnego, GSS_C_INITIATE,
+ &initiator_cred_handle, NULL, NULL);
+ check_gsserr("gss_acquire_cred(initiator)", major, minor);
+
+ /*
+ * The following test is designed to exercise SPNEGO reselection on the
+ * client and server. Unfortunately, it no longer does so after tickets
+ * #8217 and #8021, since SPNEGO now only acquires a single krb5 cred and
+ * there is no way to expand the underlying creds with gss_set_neg_mechs().
+ * To fix this we need gss_acquire_cred_with_cred() or some other way to
+ * turn a cred with a specifically requested mech set into a SPNEGO cred.
+ */
+
+ /* Make the initiator prefer IAKERB and offer krb5 as an alternative. */
+ pref_oids[0] = mech_iakerb;
+ pref_oids[1] = mech_krb5;
+ pref_mechs.count = 2;
+ pref_mechs.elements = pref_oids;
+ major = gss_set_neg_mechs(&minor, initiator_cred_handle, &pref_mechs);
+ check_gsserr("gss_set_neg_mechs(initiator)", major, minor);
+
+ /* Get default acceptor cred. */
+ major = gss_acquire_cred(&minor, GSS_C_NO_NAME, GSS_C_INDEFINITE,
+ &mechset_spnego, GSS_C_ACCEPT,
+ &verifier_cred_handle, &actual_mechs, NULL);
+ check_gsserr("gss_acquire_cred(acceptor)", major, minor);
+
+ /* Restrict the acceptor to krb5 (which will force a reselection). */
+ major = gss_set_neg_mechs(&minor, verifier_cred_handle, &mechset_krb5);
+ check_gsserr("gss_set_neg_mechs(acceptor)", major, minor);
+
+ flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG;
+ establish_contexts(&mech_spnego, initiator_cred_handle,
+ verifier_cred_handle, target_name, flags,
+ &initiator_context, &acceptor_context, &source_name,
+ &mech, NULL);
+
+ display_canon_name("Source name", source_name, &mech_krb5);
+ display_oid("Source mech", mech);
+
+ /* Test acceptance of the erroneous Microsoft krb5 OID, with and without an
+ * acceptor cred. */
+ test_mskrb_oid(target_name, verifier_cred_handle);
+ test_mskrb_oid(target_name, GSS_C_NO_CREDENTIAL);
+
+ test_neghints();
+
+ (void)gss_delete_sec_context(&minor, &initiator_context, NULL);
+ (void)gss_delete_sec_context(&minor, &acceptor_context, NULL);
+ (void)gss_release_name(&minor, &source_name);
+ (void)gss_release_name(&minor, &target_name);
+ (void)gss_release_cred(&minor, &initiator_cred_handle);
+ (void)gss_release_cred(&minor, &verifier_cred_handle);
+ (void)gss_release_oid_set(&minor, &actual_mechs);
+
+ return 0;
+}
diff --git a/src/tests/gssapi/t_srcattrs.c b/src/tests/gssapi/t_srcattrs.c
new file mode 100644
index 000000000000..e83c35690680
--- /dev/null
+++ b/src/tests/gssapi/t_srcattrs.c
@@ -0,0 +1,63 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* src/tests/gssapi/t_accname_authind.c - test harness for auth indicators */
+/*
+ * Copyright (C) 2016 by Red Hat, 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "common.h"
+
+/* Establish a context to the given target name and enumerate the attributes of
+ * the source name. */
+
+int
+main(int argc, char *argv[])
+{
+ OM_uint32 minor, flags;
+ gss_name_t tname, sname;
+ gss_ctx_id_t ictx, actx;
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: %s targetname\n", argv[0]);
+ return 1;
+ }
+ tname = import_name(argv[1]);
+
+ flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG;
+ establish_contexts(&mech_krb5, GSS_C_NO_CREDENTIAL, GSS_C_NO_CREDENTIAL,
+ tname, flags, &ictx, &actx, &sname, NULL, NULL);
+ enumerate_attributes(sname, 1);
+
+ (void)gss_release_name(&minor, &tname);
+ (void)gss_release_name(&minor, &sname);
+ (void)gss_delete_sec_context(&minor, &ictx, NULL);
+ (void)gss_delete_sec_context(&minor, &actx, NULL);
+ return 0;
+}