diff options
| author | Cy Schubert <cy@FreeBSD.org> | 2017-07-07 17:03:42 +0000 |
|---|---|---|
| committer | Cy Schubert <cy@FreeBSD.org> | 2017-07-07 17:03:42 +0000 |
| commit | 33a9b234e7087f573ef08cd7318c6497ba08b439 (patch) | |
| tree | d0ea40ad3bf5463a3c55795977c71bcb7d781b4b /src/tests/gssapi | |
Diffstat (limited to 'src/tests/gssapi')
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; +} |
