summaryrefslogtreecommitdiffstats
path: root/third_party/heimdal/lib/gssapi/test_context.c
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/heimdal/lib/gssapi/test_context.c')
-rw-r--r--third_party/heimdal/lib/gssapi/test_context.c1610
1 files changed, 1610 insertions, 0 deletions
diff --git a/third_party/heimdal/lib/gssapi/test_context.c b/third_party/heimdal/lib/gssapi/test_context.c
new file mode 100644
index 0000000..7446d15
--- /dev/null
+++ b/third_party/heimdal/lib/gssapi/test_context.c
@@ -0,0 +1,1610 @@
+/*
+ * Copyright (c) 2006 - 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of KTH nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY KTH AND ITS 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 KTH OR ITS 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 "krb5/gsskrb5_locl.h"
+#include <err.h>
+#include <getarg.h>
+#include <gssapi.h>
+#include <gssapi_krb5.h>
+#include <gssapi_spnego.h>
+#include <gssapi_ntlm.h>
+#include "test_common.h"
+
+#ifdef NOTYET
+/*
+ * export/import of sec contexts on the initiator side
+ * don't work properly, yet.
+ */
+#define DO_IMPORT_EXPORT_OF_CLIENT_CONTEXT 1
+#endif
+
+static char *type_string;
+static char *mech_string;
+static char *mechs_string;
+static char *ret_mech_string;
+static char *localname_string;
+static char *client_name;
+static char *client_password;
+static char *localname_string;
+static char *on_behalf_of_string;
+static int dns_canon_flag = -1;
+static int mutual_auth_flag = 0;
+static int dce_style_flag = 0;
+static int wrapunwrap_flag = 0;
+static int iov_flag = 0;
+static int aead_flag = 0;
+static int getverifymic_flag = 0;
+static int deleg_flag = 0;
+static int anon_flag = 0;
+static int policy_deleg_flag = 0;
+static int server_no_deleg_flag = 0;
+static int ei_cred_flag = 0;
+static int ei_ctx_flag = 0;
+static char *client_ccache = NULL;
+static char *client_keytab = NULL;
+static char *gsskrb5_acceptor_identity = NULL;
+static char *session_enctype_string = NULL;
+static int client_time_offset = 0;
+static int server_time_offset = 0;
+static int max_loops = 0;
+static char *limit_enctype_string = NULL;
+static int token_split = 0;
+static int version_flag = 0;
+static int verbose_flag = 0;
+static int help_flag = 0;
+static char *i_channel_bindings = NULL;
+static char *a_channel_bindings = NULL;
+
+static krb5_context context;
+static krb5_enctype limit_enctype = 0;
+
+static gss_OID
+string_to_oid(const char *name)
+{
+ gss_OID oid = gss_name_to_oid(name);
+
+ if (oid == GSS_C_NO_OID)
+ errx(1, "name '%s' not known", name);
+
+ return oid;
+}
+
+static void
+string_to_oids(gss_OID_set *oidsetp, char *names)
+{
+ OM_uint32 maj_stat, min_stat;
+ char *name;
+ char *s;
+
+ if (names[0] == '\0') {
+ *oidsetp = GSS_C_NO_OID_SET;
+ return;
+ }
+
+ if (strcasecmp(names, "all") == 0) {
+ maj_stat = gss_indicate_mechs(&min_stat, oidsetp);
+ if (GSS_ERROR(maj_stat))
+ errx(1, "gss_indicate_mechs: %s",
+ gssapi_err(maj_stat, min_stat, GSS_C_NO_OID));
+ } else {
+ maj_stat = gss_create_empty_oid_set(&min_stat, oidsetp);
+ if (GSS_ERROR(maj_stat))
+ errx(1, "gss_create_empty_oid_set: %s",
+ gssapi_err(maj_stat, min_stat, GSS_C_NO_OID));
+
+ for (name = strtok_r(names, ", ", &s);
+ name != NULL;
+ name = strtok_r(NULL, ", ", &s)) {
+ gss_OID oid = string_to_oid(name);
+
+ maj_stat = gss_add_oid_set_member(&min_stat, oid, oidsetp);
+ if (GSS_ERROR(maj_stat))
+ errx(1, "gss_add_oid_set_member: %s",
+ gssapi_err(maj_stat, min_stat, GSS_C_NO_OID));
+ }
+ }
+}
+
+static void
+show_pac_client_info(gss_name_t n)
+{
+ gss_buffer_desc dv = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc v = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc a;
+ OM_uint32 maj, min;
+ int authenticated, complete, more, name_is_MN, found;
+ gss_OID MN_mech;
+ gss_buffer_set_t attrs = GSS_C_NO_BUFFER_SET;
+ size_t i;
+
+ krb5_error_code ret;
+ krb5_storage *sp = NULL;
+ uint16_t len = 0, *s;
+ uint64_t tmp;
+ char *logon_string = NULL;
+
+ maj = gss_inquire_name(&min, n, &name_is_MN, &MN_mech, &attrs);
+ if (maj != GSS_S_COMPLETE)
+ errx(1, "gss_inquire_name: %s",
+ gssapi_err(maj, min, GSS_KRB5_MECHANISM));
+
+ a.value = "urn:mspac:client-info";
+ a.length = sizeof("urn:mspac:client-info") - 1;
+
+ for (found = 0, i = 0; i < attrs->count; i++) {
+ gss_buffer_t attr = &attrs->elements[i];
+
+ if (attr->length == a.length &&
+ memcmp(attr->value, a.value, a.length) == 0) {
+ found++;
+ break;
+ }
+ }
+
+ gss_release_buffer_set(&min, &attrs);
+
+ if (!found)
+ errx(1, "gss_inquire_name: attribute %.*s not enumerated",
+ (int)a.length, (char *)a.value);
+
+ more = 0;
+ maj = gss_get_name_attribute(&min, n, &a, &authenticated, &complete, &v,
+ &dv, &more);
+ if (maj != GSS_S_COMPLETE)
+ errx(1, "gss_get_name_attribute: %s",
+ gssapi_err(maj, min, GSS_KRB5_MECHANISM));
+
+
+ sp = krb5_storage_from_readonly_mem(v.value, v.length);
+ if (sp == NULL)
+ errx(1, "show_pac_client_info: out of memory");
+
+ krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
+
+ ret = krb5_ret_uint64(sp, &tmp); /* skip over time */
+ if (ret == 0)
+ ret = krb5_ret_uint16(sp, &len);
+ if (ret || len == 0)
+ errx(1, "show_pac_client_info: invalid PAC logon info length");
+
+ s = malloc(len);
+ ret = krb5_storage_read(sp, s, len);
+ if (ret != len)
+ errx(1, "show_pac_client_info:, failed to read PAC logon name");
+
+ krb5_storage_free(sp);
+
+ {
+ size_t ucs2len = len / 2;
+ uint16_t *ucs2;
+ size_t u8len;
+ unsigned int flags = WIND_RW_LE;
+
+ ucs2 = malloc(sizeof(ucs2[0]) * ucs2len);
+ if (ucs2 == NULL)
+ errx(1, "show_pac_client_info: out of memory");
+
+ ret = wind_ucs2read(s, len, &flags, ucs2, &ucs2len);
+ free(s);
+ if (ret)
+ errx(1, "failed to convert string to UCS-2");
+
+ ret = wind_ucs2utf8_length(ucs2, ucs2len, &u8len);
+ if (ret)
+ errx(1, "failed to count length of UCS-2 string");
+
+ u8len += 1; /* Add space for NUL */
+ logon_string = malloc(u8len);
+ if (logon_string == NULL)
+ errx(1, "show_pac_client_info: out of memory");
+
+ ret = wind_ucs2utf8(ucs2, ucs2len, logon_string, &u8len);
+ free(ucs2);
+ if (ret)
+ errx(1, "failed to convert to UTF-8");
+ }
+
+ printf("logon name: %s\n", logon_string);
+ free(logon_string);
+
+ gss_release_buffer(&min, &dv);
+ gss_release_buffer(&min, &v);
+}
+
+static void
+loop(gss_OID mechoid,
+ gss_OID nameoid, const char *target,
+ gss_cred_id_t init_cred,
+ gss_ctx_id_t *sctx, gss_ctx_id_t *cctx,
+ gss_OID *actual_mech,
+ gss_cred_id_t *deleg_cred)
+{
+ int server_done = 0, client_done = 0;
+ int num_loops = 0;
+ OM_uint32 maj_stat, min_stat;
+ gss_name_t gss_target_name, src_name = GSS_C_NO_NAME;
+ gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
+#ifdef DO_IMPORT_EXPORT_OF_CLIENT_CONTEXT
+ gss_buffer_desc cctx_tok = GSS_C_EMPTY_BUFFER;
+#endif
+ gss_buffer_desc sctx_tok = GSS_C_EMPTY_BUFFER;
+ OM_uint32 flags = 0, ret_cflags = 0, ret_sflags = 0;
+ gss_OID actual_mech_client = GSS_C_NO_OID;
+ gss_OID actual_mech_server = GSS_C_NO_OID;
+ struct gss_channel_bindings_struct i_channel_bindings_data;
+ struct gss_channel_bindings_struct a_channel_bindings_data;
+ gss_channel_bindings_t i_channel_bindings_p = GSS_C_NO_CHANNEL_BINDINGS;
+ gss_channel_bindings_t a_channel_bindings_p = GSS_C_NO_CHANNEL_BINDINGS;
+ size_t offset = 0;
+
+ memset(&i_channel_bindings_data, 0, sizeof(i_channel_bindings_data));
+ memset(&a_channel_bindings_data, 0, sizeof(a_channel_bindings_data));
+
+ *actual_mech = GSS_C_NO_OID;
+
+ flags |= GSS_C_REPLAY_FLAG;
+ flags |= GSS_C_INTEG_FLAG;
+ flags |= GSS_C_CONF_FLAG;
+
+ if (mutual_auth_flag)
+ flags |= GSS_C_MUTUAL_FLAG;
+ if (anon_flag)
+ flags |= GSS_C_ANON_FLAG;
+ if (dce_style_flag)
+ flags |= GSS_C_DCE_STYLE;
+ if (deleg_flag)
+ flags |= GSS_C_DELEG_FLAG;
+ if (policy_deleg_flag)
+ flags |= GSS_C_DELEG_POLICY_FLAG;
+
+ input_token.value = rk_UNCONST(target);
+ input_token.length = strlen(target);
+
+ maj_stat = gss_import_name(&min_stat,
+ &input_token,
+ nameoid,
+ &gss_target_name);
+ if (GSS_ERROR(maj_stat))
+ err(1, "import name creds failed with: %d", maj_stat);
+
+ if (on_behalf_of_string) {
+ AuthorizationDataElement e;
+ gss_buffer_desc attr, value;
+ int32_t kret;
+ size_t sz;
+
+ memset(&e, 0, sizeof(e));
+ e.ad_type = KRB5_AUTHDATA_ON_BEHALF_OF;
+ e.ad_data.length = strlen(on_behalf_of_string);
+ e.ad_data.data = on_behalf_of_string;
+ ASN1_MALLOC_ENCODE(AuthorizationDataElement, value.value, value.length,
+ &e, &sz, kret);
+ if (kret)
+ errx(1, "Could not encode AD-ON-BEHALF-OF AuthorizationDataElement");
+ attr.value =
+ GSS_KRB5_NAME_ATTRIBUTE_BASE_URN "authenticator-authz-data";
+ attr.length =
+ sizeof(GSS_KRB5_NAME_ATTRIBUTE_BASE_URN "authenticator-authz-data") - 1;
+ maj_stat = gss_set_name_attribute(&min_stat, gss_target_name, 1, &attr,
+ &value);
+ if (maj_stat != GSS_S_COMPLETE)
+ errx(1, "gss_set_name_attribute() failed with: %s",
+ gssapi_err(maj_stat, min_stat, GSS_KRB5_MECHANISM));
+ free(value.value);
+ }
+
+ input_token.length = 0;
+ input_token.value = NULL;
+
+ if (i_channel_bindings) {
+ i_channel_bindings_data.application_data.length = strlen(i_channel_bindings);
+ i_channel_bindings_data.application_data.value = i_channel_bindings;
+ i_channel_bindings_p = &i_channel_bindings_data;
+ }
+ if (a_channel_bindings) {
+ a_channel_bindings_data.application_data.length = strlen(a_channel_bindings);
+ a_channel_bindings_data.application_data.value = a_channel_bindings;
+ a_channel_bindings_p = &a_channel_bindings_data;
+ }
+
+ /*
+ * This loop simulates both the initiator and acceptor sides of
+ * a GSS conversation. We also simulate tokens that are broken
+ * into pieces before handed to gss_accept_sec_context(). Each
+ * iteration of the loop optionally calls gss_init_sec_context()
+ * then optionally calls gss_accept_sec_context().
+ */
+
+ while (!server_done || !client_done) {
+ num_loops++;
+ if (verbose_flag)
+ printf("loop #%d: input_token.length=%zu client_done=%d\n",
+ num_loops, input_token.length, client_done);
+
+ /*
+ * First, we need to call gss_import_sec_context() if we are
+ * running through the loop the first time, or if we have been
+ * given a token (input_token) by gss_accept_sec_context().
+ * We aren't going to be called every time because when we are
+ * using split tokens, we may need to call gss_accept_sec_context()
+ * multiple times because it receives an entire token.
+ */
+ if ((num_loops == 1 || input_token.length > 0) && !client_done) {
+ gsskrb5_set_time_offset(client_time_offset);
+#ifdef DO_IMPORT_EXPORT_OF_CLIENT_CONTEXT
+ if (ei_ctx_flag && cctx_tok.length > 0) {
+ maj_stat = gss_import_sec_context(&min_stat, &cctx_tok, cctx);
+ if (maj_stat != GSS_S_COMPLETE)
+ errx(1, "import client context failed: %s",
+ gssapi_err(maj_stat, min_stat, NULL));
+ gss_release_buffer(&min_stat, &cctx_tok);
+ }
+#endif
+
+ maj_stat = gss_init_sec_context(&min_stat, init_cred, cctx,
+ gss_target_name, mechoid,
+ flags, 0, i_channel_bindings_p,
+ &input_token, &actual_mech_client,
+ &output_token, &ret_cflags, NULL);
+ if (GSS_ERROR(maj_stat))
+ errx(1, "init_sec_context: %s",
+ gssapi_err(maj_stat, min_stat, mechoid));
+ client_done = !(maj_stat & GSS_S_CONTINUE_NEEDED);
+
+ gss_release_buffer(&min_stat, &input_token);
+ input_token.length = 0;
+ input_token.value = NULL;
+
+#if DO_IMPORT_EXPORT_OF_CLIENT_CONTEXT
+ if (!client_done && ei_ctx_flag) {
+ maj_stat = gss_export_sec_context(&min_stat, cctx, &cctx_tok);
+ if (maj_stat != GSS_S_COMPLETE)
+ errx(1, "export server context failed: %s",
+ gssapi_err(maj_stat, min_stat, NULL));
+ if (*cctx != GSS_C_NO_CONTEXT)
+ errx(1, "export client context did not release it");
+ }
+#endif
+
+ if (verbose_flag)
+ printf("loop #%d: output_token.length=%zu\n", num_loops,
+ output_token.length);
+
+ offset = 0;
+ }
+
+ /*
+ * We now call gss_accept_sec_context(). To support split
+ * tokens, we keep track of the offset into the token that
+ * we have used and keep handing in chunks until we're done.
+ */
+
+ if (offset < output_token.length && !server_done) {
+ gss_buffer_desc tmp;
+
+ gsskrb5_get_time_offset(&client_time_offset);
+ gsskrb5_set_time_offset(server_time_offset);
+
+ if (output_token.length && ((uint8_t *)output_token.value)[0] == 0x60) {
+ tmp.length = output_token.length - offset;
+ if (token_split && tmp.length > token_split)
+ tmp.length = token_split;
+ tmp.value = (char *)output_token.value + offset;
+ } else
+ tmp = output_token;
+
+ if (verbose_flag)
+ printf("loop #%d: accept offset=%zu len=%zu\n", num_loops,
+ offset, tmp.length);
+
+ if (ei_ctx_flag && sctx_tok.length > 0) {
+ maj_stat = gss_import_sec_context(&min_stat, &sctx_tok, sctx);
+ if (maj_stat != GSS_S_COMPLETE)
+ errx(1, "import server context failed: %s",
+ gssapi_err(maj_stat, min_stat, NULL));
+ gss_release_buffer(&min_stat, &sctx_tok);
+ }
+
+ maj_stat = gss_accept_sec_context(&min_stat, sctx,
+ GSS_C_NO_CREDENTIAL, &tmp,
+ a_channel_bindings_p, &src_name,
+ &actual_mech_server,
+ &input_token, &ret_sflags,
+ NULL, deleg_cred);
+ if (GSS_ERROR(maj_stat))
+ errx(1, "accept_sec_context: %s",
+ gssapi_err(maj_stat, min_stat, actual_mech_server));
+ offset += tmp.length;
+ if (maj_stat & GSS_S_CONTINUE_NEEDED)
+ gss_release_name(&min_stat, &src_name);
+ else
+ server_done = 1;
+
+ if (ei_ctx_flag && !server_done) {
+ maj_stat = gss_export_sec_context(&min_stat, sctx, &sctx_tok);
+ if (maj_stat != GSS_S_COMPLETE)
+ errx(1, "export server context failed: %s",
+ gssapi_err(maj_stat, min_stat, NULL));
+ if (*sctx != GSS_C_NO_CONTEXT)
+ errx(1, "export server context did not release it");
+ }
+
+ gsskrb5_get_time_offset(&server_time_offset);
+
+ if (output_token.length == offset)
+ gss_release_buffer(&min_stat, &output_token);
+ }
+ if (verbose_flag)
+ printf("loop #%d: end\n", num_loops);
+ }
+ if (output_token.length != 0)
+ gss_release_buffer(&min_stat, &output_token);
+ if (input_token.length != 0)
+ gss_release_buffer(&min_stat, &input_token);
+ gss_release_name(&min_stat, &gss_target_name);
+
+ if (deleg_flag || policy_deleg_flag) {
+ if (server_no_deleg_flag) {
+ if (*deleg_cred != GSS_C_NO_CREDENTIAL)
+ errx(1, "got delegated cred but didn't expect one");
+ } else if (*deleg_cred == GSS_C_NO_CREDENTIAL)
+ errx(1, "asked for delegarated cred but did get one");
+ } else if (*deleg_cred != GSS_C_NO_CREDENTIAL)
+ errx(1, "got deleg_cred cred but didn't ask");
+
+ if (gss_oid_equal(actual_mech_server, actual_mech_client) == 0)
+ errx(1, "mech mismatch");
+ *actual_mech = actual_mech_server;
+
+ if (on_behalf_of_string) {
+ gss_buffer_desc attr, value;
+
+ attr.value =
+ GSS_KRB5_NAME_ATTRIBUTE_BASE_URN "authz-data#580";
+ attr.length =
+ sizeof(GSS_KRB5_NAME_ATTRIBUTE_BASE_URN "authz-data#580") - 1;
+ maj_stat = gss_get_name_attribute(&min_stat, src_name, &attr, NULL,
+ NULL, &value, NULL, NULL);
+ if (maj_stat != GSS_S_COMPLETE)
+ errx(1, "gss_get_name_attribute(authz-data#580) failed with %s",
+ gssapi_err(maj_stat, min_stat, GSS_KRB5_MECHANISM));
+
+ if (value.length != strlen(on_behalf_of_string) ||
+ strncmp(value.value, on_behalf_of_string,
+ strlen(on_behalf_of_string)) != 0)
+ errx(1, "AD-ON-BEHALF-OF did not match");
+ (void) gss_release_buffer(&min_stat, &value);
+ }
+ if (localname_string) {
+ gss_buffer_desc lname;
+
+ maj_stat = gss_localname(&min_stat, src_name, GSS_C_NO_OID, &lname);
+ if (maj_stat != GSS_S_COMPLETE)
+ errx(1, "localname: %s",
+ gssapi_err(maj_stat, min_stat, GSS_C_NO_OID));
+ if (verbose_flag)
+ printf("localname: %.*s\n", (int)lname.length,
+ (char *)lname.value);
+ if (lname.length != strlen(localname_string) ||
+ strncmp(localname_string, lname.value, lname.length) != 0)
+ errx(1, "localname: expected \"%s\", got \"%.*s\" (1)",
+ localname_string, (int)lname.length, (char *)lname.value);
+ gss_release_buffer(&min_stat, &lname);
+ maj_stat = gss_localname(&min_stat, src_name, actual_mech_server,
+ &lname);
+ if (maj_stat != GSS_S_COMPLETE)
+ errx(1, "localname: %s",
+ gssapi_err(maj_stat, min_stat, actual_mech_server));
+ if (lname.length != strlen(localname_string) ||
+ strncmp(localname_string, lname.value, lname.length) != 0)
+ errx(1, "localname: expected \"%s\", got \"%.*s\" (2)",
+ localname_string, (int)lname.length, (char *)lname.value);
+ gss_release_buffer(&min_stat, &lname);
+
+ if (!gss_userok(src_name, localname_string))
+ errx(1, "localname is not userok");
+ if (gss_userok(src_name, "nosuchuser:no"))
+ errx(1, "gss_userok() appears broken");
+ }
+ if (verbose_flag) {
+ gss_buffer_desc iname;
+
+ maj_stat = gss_display_name(&min_stat, src_name, &iname, NULL);
+ if (maj_stat == GSS_S_COMPLETE) {
+ printf("client name: %.*s\n", (int)iname.length,
+ (char *)iname.value);
+ gss_release_buffer(&min_stat, &iname);
+ } else
+ warnx("display_name: %s",
+ gssapi_err(maj_stat, min_stat, GSS_C_NO_OID));
+ if (!anon_flag &&
+ gss_oid_equal(actual_mech_server, GSS_KRB5_MECHANISM))
+ show_pac_client_info(src_name);
+ }
+ gss_release_name(&min_stat, &src_name);
+
+ if (max_loops && num_loops > max_loops)
+ errx(1, "num loops %d was lager then max loops %d",
+ num_loops, max_loops);
+
+ if (verbose_flag) {
+ printf("server time offset: %d\n", server_time_offset);
+ printf("client time offset: %d\n", client_time_offset);
+ printf("num loops %d\n", num_loops);
+ printf("cflags: ");
+ if (ret_cflags & GSS_C_DELEG_FLAG)
+ printf("deleg ");
+ if (ret_cflags & GSS_C_MUTUAL_FLAG)
+ printf("mutual ");
+ if (ret_cflags & GSS_C_REPLAY_FLAG)
+ printf("replay ");
+ if (ret_cflags & GSS_C_SEQUENCE_FLAG)
+ printf("sequence ");
+ if (ret_cflags & GSS_C_CONF_FLAG)
+ printf("conf ");
+ if (ret_cflags & GSS_C_INTEG_FLAG)
+ printf("integ ");
+ if (ret_cflags & GSS_C_ANON_FLAG)
+ printf("anon ");
+ if (ret_cflags & GSS_C_PROT_READY_FLAG)
+ printf("prot-ready ");
+ if (ret_cflags & GSS_C_TRANS_FLAG)
+ printf("trans ");
+ if (ret_cflags & GSS_C_DCE_STYLE)
+ printf("dce-style ");
+ if (ret_cflags & GSS_C_IDENTIFY_FLAG)
+ printf("identify " );
+ if (ret_cflags & GSS_C_EXTENDED_ERROR_FLAG)
+ printf("extended-error " );
+ if (ret_cflags & GSS_C_DELEG_POLICY_FLAG)
+ printf("deleg-policy " );
+ printf("\n");
+ printf("sflags: ");
+ if (ret_sflags & GSS_C_CHANNEL_BOUND_FLAG)
+ printf("channel-bound " );
+ printf("\n");
+ }
+}
+
+static void
+wrapunwrap(gss_ctx_id_t cctx, gss_ctx_id_t sctx, int flags, gss_OID mechoid)
+{
+ gss_buffer_desc input_token, output_token, output_token2;
+ OM_uint32 min_stat, maj_stat;
+ gss_qop_t qop_state;
+ int conf_state;
+
+ input_token.value = "foo";
+ input_token.length = 3;
+
+ maj_stat = gss_wrap(&min_stat, cctx, flags, 0, &input_token,
+ &conf_state, &output_token);
+ if (maj_stat != GSS_S_COMPLETE)
+ errx(1, "gss_wrap failed: %s",
+ gssapi_err(maj_stat, min_stat, mechoid));
+
+ maj_stat = gss_unwrap(&min_stat, sctx, &output_token,
+ &output_token2, &conf_state, &qop_state);
+ if (maj_stat != GSS_S_COMPLETE)
+ errx(1, "gss_unwrap failed: %s",
+ gssapi_err(maj_stat, min_stat, mechoid));
+
+ gss_release_buffer(&min_stat, &output_token);
+ gss_release_buffer(&min_stat, &output_token2);
+
+#if 0 /* doesn't work for NTLM yet */
+ if (!!conf_state != !!flags)
+ errx(1, "conf_state mismatch");
+#endif
+}
+
+#define USE_CONF 1
+#define USE_HEADER_ONLY 2
+#define USE_SIGN_ONLY 4
+#define FORCE_IOV 8
+/* NO_DATA comes from <netdb.h>; we don't use it here; we appropriate it */
+#ifdef NO_DATA
+#undef NO_DATA
+#endif
+#define NO_DATA 16
+
+static void
+wrapunwrap_iov(gss_ctx_id_t cctx, gss_ctx_id_t sctx, int flags, gss_OID mechoid)
+{
+ krb5_data token, header, trailer;
+ OM_uint32 min_stat, maj_stat;
+ gss_qop_t qop_state;
+ int conf_state, conf_state2;
+ gss_iov_buffer_desc iov[6];
+ unsigned char *p;
+ int iov_len;
+ char header_data[9] = "ABCheader";
+ char trailer_data[10] = "trailerXYZ";
+
+ char token_data[16] = "0123456789abcdef";
+
+ memset(&iov, 0, sizeof(iov));
+
+ if (flags & USE_SIGN_ONLY) {
+ header.data = header_data;
+ header.length = 9;
+ trailer.data = trailer_data;
+ trailer.length = 10;
+ } else {
+ header.data = NULL;
+ header.length = 0;
+ trailer.data = NULL;
+ trailer.length = 0;
+ }
+
+ token.data = token_data;
+ token.length = 16;
+
+ iov_len = sizeof(iov)/sizeof(iov[0]);
+
+ memset(iov, 0, sizeof(iov));
+
+ iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER | GSS_IOV_BUFFER_FLAG_ALLOCATE;
+
+ if (header.length != 0) {
+ iov[1].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY;
+ iov[1].buffer.length = header.length;
+ iov[1].buffer.value = header.data;
+ } else {
+ iov[1].type = GSS_IOV_BUFFER_TYPE_EMPTY;
+ iov[1].buffer.length = 0;
+ iov[1].buffer.value = NULL;
+ }
+ iov[2].type = GSS_IOV_BUFFER_TYPE_DATA;
+ if (flags & NO_DATA) {
+ iov[2].buffer.length = 0;
+ } else {
+ iov[2].buffer.length = token.length;
+ }
+ iov[2].buffer.value = token.data;
+ if (trailer.length != 0) {
+ iov[3].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY;
+ iov[3].buffer.length = trailer.length;
+ iov[3].buffer.value = trailer.data;
+ } else {
+ iov[3].type = GSS_IOV_BUFFER_TYPE_EMPTY;
+ iov[3].buffer.length = 0;
+ iov[3].buffer.value = NULL;
+ }
+ if (dce_style_flag) {
+ iov[4].type = GSS_IOV_BUFFER_TYPE_EMPTY;
+ } else {
+ iov[4].type = GSS_IOV_BUFFER_TYPE_PADDING | GSS_IOV_BUFFER_FLAG_ALLOCATE;
+ }
+ iov[4].buffer.length = 0;
+ iov[4].buffer.value = 0;
+ if (dce_style_flag) {
+ iov[5].type = GSS_IOV_BUFFER_TYPE_EMPTY;
+ } else if (flags & USE_HEADER_ONLY) {
+ iov[5].type = GSS_IOV_BUFFER_TYPE_EMPTY;
+ } else {
+ iov[5].type = GSS_IOV_BUFFER_TYPE_TRAILER | GSS_IOV_BUFFER_FLAG_ALLOCATE;
+ }
+ iov[5].buffer.length = 0;
+ iov[5].buffer.value = 0;
+
+ maj_stat = gss_wrap_iov(&min_stat, cctx, dce_style_flag || flags & USE_CONF, 0, &conf_state,
+ iov, iov_len);
+ if (maj_stat != GSS_S_COMPLETE)
+ errx(1, "gss_wrap_iov failed");
+
+ token.length =
+ iov[0].buffer.length +
+ iov[1].buffer.length +
+ iov[2].buffer.length +
+ iov[3].buffer.length +
+ iov[4].buffer.length +
+ iov[5].buffer.length;
+ token.data = emalloc(token.length);
+
+ p = token.data;
+ memcpy(p, iov[0].buffer.value, iov[0].buffer.length);
+ p += iov[0].buffer.length;
+ memcpy(p, iov[1].buffer.value, iov[1].buffer.length);
+ p += iov[1].buffer.length;
+ memcpy(p, iov[2].buffer.value, iov[2].buffer.length);
+ p += iov[2].buffer.length;
+ memcpy(p, iov[3].buffer.value, iov[3].buffer.length);
+ p += iov[3].buffer.length;
+ memcpy(p, iov[4].buffer.value, iov[4].buffer.length);
+ p += iov[4].buffer.length;
+ memcpy(p, iov[5].buffer.value, iov[5].buffer.length);
+ p += iov[5].buffer.length;
+
+ assert(p - ((unsigned char *)token.data) == token.length);
+
+ if ((flags & (USE_SIGN_ONLY|FORCE_IOV)) == 0) {
+ gss_buffer_desc input, output;
+
+ input.value = token.data;
+ input.length = token.length;
+
+ maj_stat = gss_unwrap(&min_stat, sctx, &input,
+ &output, &conf_state2, &qop_state);
+
+ if (maj_stat != GSS_S_COMPLETE)
+ errx(1, "gss_unwrap from gss_wrap_iov failed: %s",
+ gssapi_err(maj_stat, min_stat, mechoid));
+
+ gss_release_buffer(&min_stat, &output);
+ } else {
+ maj_stat = gss_unwrap_iov(&min_stat, sctx, &conf_state2, &qop_state,
+ iov, iov_len);
+
+ if (maj_stat != GSS_S_COMPLETE)
+ errx(1, "gss_unwrap_iov failed: %x %s", flags,
+ gssapi_err(maj_stat, min_stat, mechoid));
+
+ }
+ if (conf_state2 != conf_state)
+ errx(1, "conf state wrong for iov: %x", flags);
+
+ gss_release_iov_buffer(&min_stat, iov, iov_len);
+
+ free(token.data);
+}
+
+static void
+wrapunwrap_aead(gss_ctx_id_t cctx, gss_ctx_id_t sctx, int flags, gss_OID mechoid)
+{
+ gss_buffer_desc token, assoc, message = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc output;
+ OM_uint32 min_stat, maj_stat;
+ gss_qop_t qop_state;
+ int conf_state, conf_state2;
+ char assoc_data[9] = "ABCheader";
+ char token_data[16] = "0123456789abcdef";
+
+ if (flags & USE_SIGN_ONLY) {
+ assoc.value = assoc_data;
+ assoc.length = 9;
+ } else {
+ assoc.value = NULL;
+ assoc.length = 0;
+ }
+
+ token.value = token_data;
+ token.length = 16;
+
+ maj_stat = gss_wrap_aead(&min_stat, cctx, dce_style_flag || flags & USE_CONF,
+ GSS_C_QOP_DEFAULT, &assoc, &token,
+ &conf_state, &message);
+ if (maj_stat != GSS_S_COMPLETE)
+ errx(1, "gss_wrap_aead failed");
+
+ if ((flags & (USE_SIGN_ONLY|FORCE_IOV)) == 0) {
+ maj_stat = gss_unwrap(&min_stat, sctx, &message,
+ &output, &conf_state2, &qop_state);
+
+ if (maj_stat != GSS_S_COMPLETE)
+ errx(1, "gss_unwrap from gss_wrap_aead failed: %s",
+ gssapi_err(maj_stat, min_stat, mechoid));
+ } else {
+ maj_stat = gss_unwrap_aead(&min_stat, sctx, &message, &assoc,
+ &output, &conf_state2, &qop_state);
+ if (maj_stat != GSS_S_COMPLETE)
+ errx(1, "gss_unwrap_aead failed: %x %s", flags,
+ gssapi_err(maj_stat, min_stat, mechoid));
+ }
+
+ if (output.length != token.length)
+ errx(1, "plaintext length wrong for aead");
+ else if (memcmp(output.value, token.value, token.length) != 0)
+ errx(1, "plaintext wrong for aead");
+ if (conf_state2 != conf_state)
+ errx(1, "conf state wrong for aead: %x", flags);
+
+ gss_release_buffer(&min_stat, &message);
+ gss_release_buffer(&min_stat, &output);
+}
+
+static void
+getverifymic(gss_ctx_id_t cctx, gss_ctx_id_t sctx, gss_OID mechoid)
+{
+ gss_buffer_desc input_token, output_token;
+ OM_uint32 min_stat, maj_stat;
+ gss_qop_t qop_state;
+
+ input_token.value = "bar";
+ input_token.length = 3;
+
+ maj_stat = gss_get_mic(&min_stat, cctx, 0, &input_token,
+ &output_token);
+ if (maj_stat != GSS_S_COMPLETE)
+ errx(1, "gss_get_mic failed: %s",
+ gssapi_err(maj_stat, min_stat, mechoid));
+
+ maj_stat = gss_verify_mic(&min_stat, sctx, &input_token,
+ &output_token, &qop_state);
+ if (maj_stat != GSS_S_COMPLETE)
+ errx(1, "gss_verify_mic failed: %s",
+ gssapi_err(maj_stat, min_stat, mechoid));
+
+ gss_release_buffer(&min_stat, &output_token);
+}
+
+static void
+empty_release(void)
+{
+ gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
+ gss_cred_id_t cred = GSS_C_NO_CREDENTIAL;
+ gss_name_t name = GSS_C_NO_NAME;
+ gss_OID_set oidset = GSS_C_NO_OID_SET;
+ OM_uint32 junk;
+
+ gss_delete_sec_context(&junk, &ctx, NULL);
+ gss_release_cred(&junk, &cred);
+ gss_release_name(&junk, &name);
+ gss_release_oid_set(&junk, &oidset);
+}
+
+/*
+ *
+ */
+
+static struct getargs args[] = {
+ {"name-type",0, arg_string, &type_string, "type of name", NULL },
+ {"mech-type",0, arg_string, &mech_string, "mech type (name)", NULL },
+ {"mech-types",0, arg_string, &mechs_string, "mech types (names)", NULL },
+ {"ret-mech-type",0, arg_string, &ret_mech_string,
+ "type of return mech", NULL },
+ {"dns-canonicalize",0,arg_negative_flag, &dns_canon_flag,
+ "use dns to canonicalize", NULL },
+ {"mutual-auth",0, arg_flag, &mutual_auth_flag,"mutual auth", NULL },
+ {"client-ccache",0, arg_string, &client_ccache, "client credentials cache", NULL },
+ {"client-keytab",0, arg_string, &client_keytab, "client keytab", NULL },
+ {"client-name", 0, arg_string, &client_name, "client name", NULL },
+ {"client-password", 0, arg_string, &client_password, "client password", NULL },
+ {"anonymous", 0, arg_flag, &anon_flag, "anonymous auth", NULL },
+ {"i-channel-bindings", 0, arg_string, &i_channel_bindings, "initiator channel binding data", NULL },
+ {"a-channel-bindings", 0, arg_string, &a_channel_bindings, "acceptor channel binding data", NULL },
+ {"limit-enctype",0, arg_string, &limit_enctype_string, "enctype", NULL },
+ {"dce-style",0, arg_flag, &dce_style_flag, "dce-style", NULL },
+ {"wrapunwrap",0, arg_flag, &wrapunwrap_flag, "wrap/unwrap", NULL },
+ {"iov", 0, arg_flag, &iov_flag, "wrap/unwrap iov", NULL },
+ {"aead", 0, arg_flag, &aead_flag, "wrap/unwrap aead", NULL },
+ {"getverifymic",0, arg_flag, &getverifymic_flag,
+ "get and verify mic", NULL },
+ {"delegate",0, arg_flag, &deleg_flag, "delegate credential", NULL },
+ {"policy-delegate",0, arg_flag, &policy_deleg_flag, "policy delegate credential", NULL },
+ {"server-no-delegate",0, arg_flag, &server_no_deleg_flag,
+ "server should get a credential", NULL },
+ {"export-import-context",0, arg_flag, &ei_ctx_flag, "test export/import context", NULL },
+ {"export-import-cred",0, arg_flag, &ei_cred_flag, "test export/import cred", NULL },
+ {"localname",0, arg_string, &localname_string, "expected localname for client", "USERNAME"},
+ {"gsskrb5-acceptor-identity", 0, arg_string, &gsskrb5_acceptor_identity, "keytab", NULL },
+ {"session-enctype", 0, arg_string, &session_enctype_string, "enctype", NULL },
+ {"client-time-offset", 0, arg_integer, &client_time_offset, "time", NULL },
+ {"server-time-offset", 0, arg_integer, &server_time_offset, "time", NULL },
+ {"max-loops", 0, arg_integer, &max_loops, "time", NULL },
+ {"token-split", 0, arg_integer, &token_split, "bytes", NULL },
+ {"on-behalf-of", 0, arg_string, &on_behalf_of_string, "principal",
+ "send authenticator authz-data AD-ON-BEHALF-OF" },
+ {"version", 0, arg_flag, &version_flag, "print version", NULL },
+ {"verbose", 'v', arg_flag, &verbose_flag, "verbose", NULL },
+ {"help", 0, arg_flag, &help_flag, NULL, NULL }
+};
+
+static void
+usage (int ret)
+{
+ arg_printusage (args, sizeof(args)/sizeof(*args),
+ NULL, "service@host");
+ exit (ret);
+}
+
+int
+main(int argc, char **argv)
+{
+ int optidx = 0;
+ OM_uint32 min_stat, maj_stat;
+ gss_ctx_id_t cctx, sctx;
+ void *ctx;
+ gss_OID nameoid, mechoid, actual_mech, actual_mech2;
+ gss_cred_id_t client_cred = GSS_C_NO_CREDENTIAL, deleg_cred = GSS_C_NO_CREDENTIAL;
+ gss_name_t cname = GSS_C_NO_NAME;
+ gss_buffer_desc credential_data = GSS_C_EMPTY_BUFFER;
+ gss_OID_desc oids[7];
+ gss_OID_set_desc mechoid_descs;
+ gss_OID_set mechoids = GSS_C_NO_OID_SET;
+ gss_key_value_element_desc client_cred_elements[2];
+ gss_key_value_set_desc client_cred_store;
+ gss_OID_set actual_mechs = GSS_C_NO_OID_SET;
+
+ setprogname(argv[0]);
+
+ if (krb5_init_context(&context))
+ errx(1, "krb5_init_context");
+
+ cctx = sctx = GSS_C_NO_CONTEXT;
+
+ if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
+ usage(1);
+
+ if (help_flag)
+ usage (0);
+
+ if(version_flag){
+ print_version(NULL);
+ exit(0);
+ }
+
+ argc -= optidx;
+ argv += optidx;
+
+ if (argc != 1)
+ usage(1);
+
+ if (dns_canon_flag != -1)
+ gsskrb5_set_dns_canonicalize(dns_canon_flag);
+
+ if (type_string == NULL)
+ nameoid = GSS_C_NT_HOSTBASED_SERVICE;
+ else if (strcmp(type_string, "hostbased-service") == 0)
+ nameoid = GSS_C_NT_HOSTBASED_SERVICE;
+ else if (strcmp(type_string, "krb5-principal-name") == 0)
+ nameoid = GSS_KRB5_NT_PRINCIPAL_NAME;
+ else
+ errx(1, "%s not supported", type_string);
+
+ if (mech_string == NULL)
+ mechoid = GSS_KRB5_MECHANISM;
+ else
+ mechoid = string_to_oid(mech_string);
+
+ if (mechs_string == NULL) {
+ /*
+ * We ought to be able to use the OID set of the one mechanism
+ * OID given. But there's some breakage that conspires to make
+ * that fail though it should succeed:
+ *
+ * - the NTLM gss_acquire_cred() refuses to work with
+ * desired_name == GSS_C_NO_NAME
+ * - gss_acquire_cred() with desired_mechs == GSS_C_NO_OID_SET
+ * does work here because we happen to have Kerberos
+ * credentials in check-ntlm, and the subsequent
+ * gss_init_sec_context() call finds no cred element for NTLM
+ * but plows on anyways, surprisingly enough, and then the
+ * NTLM gss_init_sec_context() just works.
+ *
+ * In summary, there's some breakage in gss_init_sec_context()
+ * and some breakage in NTLM that conspires against us here.
+ *
+ * We work around this in check-ntlm and check-spnego by adding
+ * --client-name=user1@${R} to the invocations of this test
+ * program that require it.
+ */
+ oids[0] = *mechoid;
+ mechoid_descs.elements = &oids[0];
+ mechoid_descs.count = 1;
+ mechoids = &mechoid_descs;
+ } else {
+ string_to_oids(&mechoids, mechs_string);
+ }
+
+ if (gsskrb5_acceptor_identity) {
+ /* XXX replace this with cred store, but test suites will need work */
+ maj_stat = gsskrb5_register_acceptor_identity(gsskrb5_acceptor_identity);
+ if (maj_stat)
+ errx(1, "gsskrb5_acceptor_identity: %s",
+ gssapi_err(maj_stat, 0, GSS_C_NO_OID));
+ }
+
+ if (client_password && (client_ccache || client_keytab)) {
+ errx(1, "password option mutually exclusive with ccache or keytab option");
+ }
+
+ if (client_password) {
+ credential_data.value = client_password;
+ credential_data.length = strlen(client_password);
+ }
+
+ client_cred_store.count = 0;
+ client_cred_store.elements = client_cred_elements;
+
+ if (client_ccache) {
+ client_cred_store.elements[client_cred_store.count].key = "ccache";
+ client_cred_store.elements[client_cred_store.count].value = client_ccache;
+
+ client_cred_store.count++;
+ }
+
+ if (client_keytab) {
+ client_cred_store.elements[client_cred_store.count].key = "client_keytab";
+ client_cred_store.elements[client_cred_store.count].value = client_keytab;
+
+ client_cred_store.count++;
+ }
+
+ if (client_name) {
+ gss_buffer_desc cn;
+
+ cn.value = client_name;
+ cn.length = strlen(client_name);
+
+ maj_stat = gss_import_name(&min_stat, &cn, GSS_C_NT_USER_NAME, &cname);
+ if (maj_stat)
+ errx(1, "gss_import_name: %s",
+ gssapi_err(maj_stat, min_stat, GSS_C_NO_OID));
+ }
+
+ if (client_password) {
+ maj_stat = gss_acquire_cred_with_password(&min_stat,
+ cname,
+ &credential_data,
+ GSS_C_INDEFINITE,
+ mechoids,
+ GSS_C_INITIATE,
+ &client_cred,
+ &actual_mechs,
+ NULL);
+ if (GSS_ERROR(maj_stat)) {
+ if (mechoids != GSS_C_NO_OID_SET && mechoids->count == 1)
+ mechoid = &mechoids->elements[0];
+ else
+ mechoid = GSS_C_NO_OID;
+ errx(1, "gss_acquire_cred_with_password: %s",
+ gssapi_err(maj_stat, min_stat, mechoid));
+ }
+ } else {
+ maj_stat = gss_acquire_cred_from(&min_stat,
+ cname,
+ GSS_C_INDEFINITE,
+ mechoids,
+ GSS_C_INITIATE,
+ client_cred_store.count ? &client_cred_store
+ : GSS_C_NO_CRED_STORE,
+ &client_cred,
+ &actual_mechs,
+ NULL);
+ if (GSS_ERROR(maj_stat) && !anon_flag)
+ errx(1, "gss_acquire_cred: %s",
+ gssapi_err(maj_stat, min_stat, GSS_C_NO_OID));
+ }
+
+ gss_release_name(&min_stat, &cname);
+
+ if (verbose_flag) {
+ size_t i;
+
+ printf("cred mechs:");
+ for (i = 0; i < actual_mechs->count; i++)
+ printf(" %s", gss_oid_to_name(&actual_mechs->elements[i]));
+ printf("\n");
+ }
+
+ if (gss_oid_equal(mechoid, GSS_SPNEGO_MECHANISM) && mechs_string) {
+ maj_stat = gss_set_neg_mechs(&min_stat, client_cred, mechoids);
+ if (GSS_ERROR(maj_stat))
+ errx(1, "gss_set_neg_mechs: %s",
+ gssapi_err(maj_stat, min_stat, GSS_SPNEGO_MECHANISM));
+
+ mechoid_descs.elements = GSS_SPNEGO_MECHANISM;
+ mechoid_descs.count = 1;
+ mechoids = &mechoid_descs;
+ }
+
+ if (ei_cred_flag) {
+ gss_cred_id_t cred2 = GSS_C_NO_CREDENTIAL;
+ gss_buffer_desc cb;
+
+ maj_stat = gss_export_cred(&min_stat, client_cred, &cb);
+ if (maj_stat != GSS_S_COMPLETE)
+ errx(1, "export cred failed: %s",
+ gssapi_err(maj_stat, min_stat, NULL));
+
+ maj_stat = gss_import_cred(&min_stat, &cb, &cred2);
+ if (maj_stat != GSS_S_COMPLETE)
+ errx(1, "import cred failed: %s",
+ gssapi_err(maj_stat, min_stat, NULL));
+
+ gss_release_buffer(&min_stat, &cb);
+ gss_release_cred(&min_stat, &client_cred);
+ client_cred = cred2;
+ }
+
+ if (limit_enctype_string) {
+ krb5_error_code ret;
+
+ ret = krb5_string_to_enctype(context,
+ limit_enctype_string,
+ &limit_enctype);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_string_to_enctype");
+ }
+
+
+ if (limit_enctype) {
+ if (client_cred == NULL)
+ errx(1, "client_cred missing");
+
+ maj_stat = gss_krb5_set_allowable_enctypes(&min_stat, client_cred,
+ 1, &limit_enctype);
+ if (maj_stat)
+ errx(1, "gss_krb5_set_allowable_enctypes: %s",
+ gssapi_err(maj_stat, min_stat, GSS_C_NO_OID));
+ }
+
+ loop(mechoid, nameoid, argv[0], client_cred,
+ &sctx, &cctx, &actual_mech, &deleg_cred);
+
+ if (verbose_flag)
+ printf("resulting mech: %s\n", gss_oid_to_name(actual_mech));
+
+ if (ret_mech_string) {
+ gss_OID retoid;
+
+ retoid = string_to_oid(ret_mech_string);
+
+ if (gss_oid_equal(retoid, actual_mech) == 0)
+ errx(1, "actual_mech mech is not the expected type %s",
+ ret_mech_string);
+ }
+
+ /* XXX should be actual_mech */
+ if (gss_oid_equal(mechoid, GSS_KRB5_MECHANISM)) {
+ time_t sc_time;
+ gss_buffer_desc authz_data;
+ gss_buffer_desc in, out1, out2;
+ krb5_keyblock *keyblock, *keyblock2;
+ krb5_timestamp now;
+ krb5_error_code ret;
+
+ ret = krb5_timeofday(context, &now);
+ if (ret)
+ errx(1, "krb5_timeofday failed");
+
+ /* client */
+ maj_stat = gss_krb5_export_lucid_sec_context(&min_stat,
+ &cctx,
+ 1, /* version */
+ &ctx);
+ if (maj_stat != GSS_S_COMPLETE)
+ errx(1, "gss_krb5_export_lucid_sec_context failed: %s",
+ gssapi_err(maj_stat, min_stat, actual_mech));
+
+
+ maj_stat = gss_krb5_free_lucid_sec_context(&maj_stat, ctx);
+ if (maj_stat != GSS_S_COMPLETE)
+ errx(1, "gss_krb5_free_lucid_sec_context failed: %s",
+ gssapi_err(maj_stat, min_stat, actual_mech));
+
+ /* server */
+ maj_stat = gss_krb5_export_lucid_sec_context(&min_stat,
+ &sctx,
+ 1, /* version */
+ &ctx);
+ if (maj_stat != GSS_S_COMPLETE)
+ errx(1, "gss_krb5_export_lucid_sec_context failed: %s",
+ gssapi_err(maj_stat, min_stat, actual_mech));
+ maj_stat = gss_krb5_free_lucid_sec_context(&min_stat, ctx);
+ if (maj_stat != GSS_S_COMPLETE)
+ errx(1, "gss_krb5_free_lucid_sec_context failed: %s",
+ gssapi_err(maj_stat, min_stat, actual_mech));
+
+ maj_stat = gsskrb5_extract_authtime_from_sec_context(&min_stat,
+ sctx,
+ &sc_time);
+ if (maj_stat != GSS_S_COMPLETE)
+ errx(1, "gsskrb5_extract_authtime_from_sec_context failed: %s",
+ gssapi_err(maj_stat, min_stat, actual_mech));
+
+ if (sc_time > now)
+ errx(1, "gsskrb5_extract_authtime_from_sec_context failed: "
+ "time authtime is before now: %ld %ld",
+ (long)sc_time, (long)now);
+
+ maj_stat = gsskrb5_extract_service_keyblock(&min_stat,
+ sctx,
+ &keyblock);
+ if (maj_stat != GSS_S_COMPLETE)
+ errx(1, "gsskrb5_export_service_keyblock failed: %s",
+ gssapi_err(maj_stat, min_stat, actual_mech));
+
+ krb5_free_keyblock(context, keyblock);
+
+ maj_stat = gsskrb5_get_subkey(&min_stat,
+ sctx,
+ &keyblock);
+ if (maj_stat != GSS_S_COMPLETE
+ && (!(maj_stat == GSS_S_FAILURE && min_stat == GSS_KRB5_S_KG_NO_SUBKEY)))
+ errx(1, "gsskrb5_get_subkey server failed: %s",
+ gssapi_err(maj_stat, min_stat, actual_mech));
+
+ if (maj_stat != GSS_S_COMPLETE)
+ keyblock = NULL;
+ else if (limit_enctype && keyblock->keytype != limit_enctype)
+ errx(1, "gsskrb5_get_subkey wrong enctype");
+
+ maj_stat = gsskrb5_get_subkey(&min_stat,
+ cctx,
+ &keyblock2);
+ if (maj_stat != GSS_S_COMPLETE
+ && (!(maj_stat == GSS_S_FAILURE && min_stat == GSS_KRB5_S_KG_NO_SUBKEY)))
+ errx(1, "gsskrb5_get_subkey client failed: %s",
+ gssapi_err(maj_stat, min_stat, actual_mech));
+
+ if (maj_stat != GSS_S_COMPLETE)
+ keyblock2 = NULL;
+ else if (limit_enctype && keyblock && keyblock->keytype != limit_enctype)
+ errx(1, "gsskrb5_get_subkey wrong enctype");
+
+ if (keyblock || keyblock2) {
+ if (keyblock == NULL)
+ errx(1, "server missing token keyblock");
+ if (keyblock2 == NULL)
+ errx(1, "client missing token keyblock");
+
+ if (keyblock->keytype != keyblock2->keytype)
+ errx(1, "enctype mismatch");
+ if (keyblock->keyvalue.length != keyblock2->keyvalue.length)
+ errx(1, "key length mismatch");
+ if (memcmp(keyblock->keyvalue.data, keyblock2->keyvalue.data,
+ keyblock2->keyvalue.length) != 0)
+ errx(1, "key data mismatch");
+ }
+
+ if (session_enctype_string) {
+ krb5_enctype enctype;
+
+ ret = krb5_string_to_enctype(context,
+ session_enctype_string,
+ &enctype);
+
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_string_to_enctype");
+
+ if (keyblock && enctype != keyblock->keytype)
+ errx(1, "keytype is not the expected %d != %d",
+ (int)enctype, (int)keyblock2->keytype);
+ }
+
+ if (keyblock)
+ krb5_free_keyblock(context, keyblock);
+ if (keyblock2)
+ krb5_free_keyblock(context, keyblock2);
+
+ maj_stat = gsskrb5_get_initiator_subkey(&min_stat,
+ sctx,
+ &keyblock);
+ if (maj_stat != GSS_S_COMPLETE
+ && (!(maj_stat == GSS_S_FAILURE && min_stat == GSS_KRB5_S_KG_NO_SUBKEY)))
+ errx(1, "gsskrb5_get_initiator_subkey failed: %s",
+ gssapi_err(maj_stat, min_stat, actual_mech));
+
+ if (maj_stat == GSS_S_COMPLETE) {
+
+ if (limit_enctype && keyblock->keytype != limit_enctype)
+ errx(1, "gsskrb5_get_initiator_subkey wrong enctype");
+ krb5_free_keyblock(context, keyblock);
+ }
+
+ maj_stat = gsskrb5_extract_authz_data_from_sec_context(&min_stat,
+ sctx,
+ 128,
+ &authz_data);
+ if (maj_stat == GSS_S_COMPLETE)
+ gss_release_buffer(&min_stat, &authz_data);
+
+
+ memset(&out1, 0, sizeof(out1));
+ memset(&out2, 0, sizeof(out2));
+
+ in.value = "foo";
+ in.length = 3;
+
+ gss_pseudo_random(&min_stat, sctx, GSS_C_PRF_KEY_FULL, &in,
+ 100, &out1);
+ gss_pseudo_random(&min_stat, cctx, GSS_C_PRF_KEY_FULL, &in,
+ 100, &out2);
+
+ if (out1.length != out2.length)
+ errx(1, "prf len mismatch");
+ if (memcmp(out1.value, out2.value, out1.length) != 0)
+ errx(1, "prf data mismatch");
+
+ gss_release_buffer(&min_stat, &out1);
+
+ gss_pseudo_random(&min_stat, sctx, GSS_C_PRF_KEY_FULL, &in,
+ 100, &out1);
+
+ if (out1.length != out2.length)
+ errx(1, "prf len mismatch");
+ if (memcmp(out1.value, out2.value, out1.length) != 0)
+ errx(1, "prf data mismatch");
+
+ gss_release_buffer(&min_stat, &out1);
+ gss_release_buffer(&min_stat, &out2);
+
+ in.value = "bar";
+ in.length = 3;
+
+ gss_pseudo_random(&min_stat, sctx, GSS_C_PRF_KEY_PARTIAL, &in,
+ 100, &out1);
+ gss_pseudo_random(&min_stat, cctx, GSS_C_PRF_KEY_PARTIAL, &in,
+ 100, &out2);
+
+ if (out1.length != out2.length)
+ errx(1, "prf len mismatch");
+ if (memcmp(out1.value, out2.value, out1.length) != 0)
+ errx(1, "prf data mismatch");
+
+ gss_release_buffer(&min_stat, &out1);
+ gss_release_buffer(&min_stat, &out2);
+
+ wrapunwrap_flag = 1;
+ getverifymic_flag = 1;
+ }
+
+ if (ei_ctx_flag) {
+ gss_buffer_desc ctx_token = GSS_C_EMPTY_BUFFER;
+
+ maj_stat = gss_export_sec_context(&min_stat, &cctx, &ctx_token);
+ if (maj_stat != GSS_S_COMPLETE)
+ errx(1, "export client context failed: %s",
+ gssapi_err(maj_stat, min_stat, NULL));
+
+ if (cctx != GSS_C_NO_CONTEXT)
+ errx(1, "export client context did not release it");
+
+ maj_stat = gss_import_sec_context(&min_stat, &ctx_token, &cctx);
+ if (maj_stat != GSS_S_COMPLETE)
+ errx(1, "import client context failed: %s",
+ gssapi_err(maj_stat, min_stat, NULL));
+
+ gss_release_buffer(&min_stat, &ctx_token);
+
+ maj_stat = gss_export_sec_context(&min_stat, &sctx, &ctx_token);
+ if (maj_stat != GSS_S_COMPLETE)
+ errx(1, "export server context failed: %s",
+ gssapi_err(maj_stat, min_stat, NULL));
+
+ if (sctx != GSS_C_NO_CONTEXT)
+ errx(1, "export server context did not release it");
+
+ maj_stat = gss_import_sec_context(&min_stat, &ctx_token, &sctx);
+ if (maj_stat != GSS_S_COMPLETE)
+ errx(1, "import server context failed: %s",
+ gssapi_err(maj_stat, min_stat, NULL));
+
+ gss_release_buffer(&min_stat, &ctx_token);
+ }
+
+ if (wrapunwrap_flag) {
+ wrapunwrap(cctx, sctx, 0, actual_mech);
+ wrapunwrap(cctx, sctx, 1, actual_mech);
+ wrapunwrap(sctx, cctx, 0, actual_mech);
+ wrapunwrap(sctx, cctx, 1, actual_mech);
+ }
+
+ if (iov_flag) {
+ wrapunwrap_iov(cctx, sctx, 0, actual_mech);
+ wrapunwrap_iov(cctx, sctx, USE_HEADER_ONLY|FORCE_IOV, actual_mech);
+ wrapunwrap_iov(cctx, sctx, USE_HEADER_ONLY, actual_mech);
+ wrapunwrap_iov(cctx, sctx, USE_CONF, actual_mech);
+ wrapunwrap_iov(cctx, sctx, USE_CONF|USE_HEADER_ONLY, actual_mech);
+
+ wrapunwrap_iov(cctx, sctx, FORCE_IOV, actual_mech);
+ wrapunwrap_iov(cctx, sctx, USE_CONF|FORCE_IOV, actual_mech);
+ wrapunwrap_iov(cctx, sctx, USE_HEADER_ONLY|FORCE_IOV, actual_mech);
+ wrapunwrap_iov(cctx, sctx, USE_CONF|USE_HEADER_ONLY|FORCE_IOV, actual_mech);
+
+ wrapunwrap_iov(cctx, sctx, USE_SIGN_ONLY|FORCE_IOV, actual_mech);
+ wrapunwrap_iov(cctx, sctx, USE_CONF|USE_SIGN_ONLY|FORCE_IOV, actual_mech);
+ wrapunwrap_iov(cctx, sctx, USE_CONF|USE_HEADER_ONLY|USE_SIGN_ONLY|FORCE_IOV, actual_mech);
+
+/* works */
+ wrapunwrap_iov(cctx, sctx, 0, actual_mech);
+ wrapunwrap_iov(cctx, sctx, FORCE_IOV, actual_mech);
+
+ wrapunwrap_iov(cctx, sctx, USE_CONF, actual_mech);
+ wrapunwrap_iov(cctx, sctx, USE_CONF|FORCE_IOV, actual_mech);
+
+ wrapunwrap_iov(cctx, sctx, USE_SIGN_ONLY, actual_mech);
+ wrapunwrap_iov(cctx, sctx, USE_SIGN_ONLY|FORCE_IOV, actual_mech);
+
+ wrapunwrap_iov(cctx, sctx, USE_CONF|USE_SIGN_ONLY, actual_mech);
+ wrapunwrap_iov(cctx, sctx, USE_CONF|USE_SIGN_ONLY|FORCE_IOV, actual_mech);
+
+ wrapunwrap_iov(cctx, sctx, USE_HEADER_ONLY, actual_mech);
+ wrapunwrap_iov(cctx, sctx, USE_HEADER_ONLY|FORCE_IOV, actual_mech);
+
+ wrapunwrap_iov(cctx, sctx, USE_CONF|USE_HEADER_ONLY, actual_mech);
+ wrapunwrap_iov(cctx, sctx, USE_CONF|USE_HEADER_ONLY|FORCE_IOV, actual_mech);
+
+ wrapunwrap_iov(cctx, sctx, NO_DATA, actual_mech);
+ wrapunwrap_iov(cctx, sctx, NO_DATA|USE_HEADER_ONLY|FORCE_IOV, actual_mech);
+ wrapunwrap_iov(cctx, sctx, NO_DATA|USE_HEADER_ONLY, actual_mech);
+ wrapunwrap_iov(cctx, sctx, NO_DATA|USE_CONF, actual_mech);
+ wrapunwrap_iov(cctx, sctx, NO_DATA|USE_CONF|USE_HEADER_ONLY, actual_mech);
+
+ wrapunwrap_iov(cctx, sctx, NO_DATA|FORCE_IOV, actual_mech);
+ wrapunwrap_iov(cctx, sctx, NO_DATA|USE_CONF|FORCE_IOV, actual_mech);
+ wrapunwrap_iov(cctx, sctx, NO_DATA|USE_HEADER_ONLY|FORCE_IOV, actual_mech);
+ wrapunwrap_iov(cctx, sctx, NO_DATA|USE_CONF|USE_HEADER_ONLY|FORCE_IOV, actual_mech);
+
+ wrapunwrap_iov(cctx, sctx, NO_DATA|USE_SIGN_ONLY|FORCE_IOV, actual_mech);
+ wrapunwrap_iov(cctx, sctx, NO_DATA|USE_CONF|USE_SIGN_ONLY|FORCE_IOV, actual_mech);
+ wrapunwrap_iov(cctx, sctx, NO_DATA|USE_CONF|USE_HEADER_ONLY|USE_SIGN_ONLY|FORCE_IOV, actual_mech);
+
+ /* works */
+ wrapunwrap_iov(cctx, sctx, NO_DATA, actual_mech);
+ wrapunwrap_iov(cctx, sctx, NO_DATA|FORCE_IOV, actual_mech);
+
+ wrapunwrap_iov(cctx, sctx, NO_DATA|USE_CONF, actual_mech);
+ wrapunwrap_iov(cctx, sctx, NO_DATA|USE_CONF|FORCE_IOV, actual_mech);
+
+ wrapunwrap_iov(cctx, sctx, NO_DATA|USE_SIGN_ONLY, actual_mech);
+ wrapunwrap_iov(cctx, sctx, NO_DATA|USE_SIGN_ONLY|FORCE_IOV, actual_mech);
+
+ wrapunwrap_iov(cctx, sctx, NO_DATA|USE_CONF|USE_SIGN_ONLY, actual_mech);
+ wrapunwrap_iov(cctx, sctx, NO_DATA|USE_CONF|USE_SIGN_ONLY|FORCE_IOV, actual_mech);
+
+ wrapunwrap_iov(cctx, sctx, NO_DATA|USE_HEADER_ONLY, actual_mech);
+ wrapunwrap_iov(cctx, sctx, NO_DATA|USE_HEADER_ONLY|FORCE_IOV, actual_mech);
+
+ wrapunwrap_iov(cctx, sctx, NO_DATA|USE_CONF|USE_HEADER_ONLY, actual_mech);
+ wrapunwrap_iov(cctx, sctx, NO_DATA|USE_CONF|USE_HEADER_ONLY|FORCE_IOV, actual_mech);
+ }
+
+ if (aead_flag) {
+ wrapunwrap_aead(cctx, sctx, 0, actual_mech);
+ wrapunwrap_aead(cctx, sctx, USE_CONF, actual_mech);
+
+ wrapunwrap_aead(cctx, sctx, FORCE_IOV, actual_mech);
+ wrapunwrap_aead(cctx, sctx, USE_CONF|FORCE_IOV, actual_mech);
+
+ wrapunwrap_aead(cctx, sctx, USE_SIGN_ONLY|FORCE_IOV, actual_mech);
+ wrapunwrap_aead(cctx, sctx, USE_CONF|USE_SIGN_ONLY|FORCE_IOV, actual_mech);
+
+ wrapunwrap_aead(cctx, sctx, 0, actual_mech);
+ wrapunwrap_aead(cctx, sctx, FORCE_IOV, actual_mech);
+
+ wrapunwrap_aead(cctx, sctx, USE_CONF, actual_mech);
+ wrapunwrap_aead(cctx, sctx, USE_CONF|FORCE_IOV, actual_mech);
+
+ wrapunwrap_aead(cctx, sctx, USE_SIGN_ONLY, actual_mech);
+ wrapunwrap_aead(cctx, sctx, USE_SIGN_ONLY|FORCE_IOV, actual_mech);
+
+ wrapunwrap_aead(cctx, sctx, USE_CONF|USE_SIGN_ONLY, actual_mech);
+ wrapunwrap_aead(cctx, sctx, USE_CONF|USE_SIGN_ONLY|FORCE_IOV, actual_mech);
+ }
+
+ if (getverifymic_flag) {
+ getverifymic(cctx, sctx, actual_mech);
+ getverifymic(cctx, sctx, actual_mech);
+ getverifymic(sctx, cctx, actual_mech);
+ getverifymic(sctx, cctx, actual_mech);
+ }
+
+ gss_delete_sec_context(&min_stat, &cctx, NULL);
+ gss_delete_sec_context(&min_stat, &sctx, NULL);
+
+ if (deleg_cred != GSS_C_NO_CREDENTIAL) {
+ gss_cred_id_t cred2 = GSS_C_NO_CREDENTIAL;
+ gss_buffer_desc cb;
+
+ if (verbose_flag)
+ printf("checking actual mech (%s) on delegated cred\n",
+ gss_oid_to_name(actual_mech));
+ loop(actual_mech, nameoid, argv[0], deleg_cred, &sctx, &cctx, &actual_mech2, &cred2);
+
+ gss_delete_sec_context(&min_stat, &cctx, NULL);
+ gss_delete_sec_context(&min_stat, &sctx, NULL);
+
+ gss_release_cred(&min_stat, &cred2);
+
+#if 0
+ /*
+ * XXX We can't do this. Delegated credentials only work with
+ * the actual_mech. We could gss_store_cred the delegated
+ * credentials *then* gss_add/acquire_cred() with SPNEGO, then
+ * we could try loop() with those credentials.
+ */
+ /* try again using SPNEGO */
+ if (verbose_flag)
+ printf("checking spnego on delegated cred\n");
+ loop(GSS_SPNEGO_MECHANISM, nameoid, argv[0], deleg_cred, &sctx, &cctx,
+ &actual_mech2, &cred2);
+
+ gss_delete_sec_context(&min_stat, &cctx, NULL);
+ gss_delete_sec_context(&min_stat, &sctx, NULL);
+
+ gss_release_cred(&min_stat, &cred2);
+#endif
+
+ /* check export/import */
+ if (ei_cred_flag) {
+
+ maj_stat = gss_export_cred(&min_stat, deleg_cred, &cb);
+ if (maj_stat != GSS_S_COMPLETE)
+ errx(1, "export cred failed: %s",
+ gssapi_err(maj_stat, min_stat, NULL));
+
+ maj_stat = gss_import_cred(&min_stat, &cb, &cred2);
+ if (maj_stat != GSS_S_COMPLETE)
+ errx(1, "import cred failed: %s",
+ gssapi_err(maj_stat, min_stat, NULL));
+
+ gss_release_buffer(&min_stat, &cb);
+ gss_release_cred(&min_stat, &deleg_cred);
+
+ if (verbose_flag)
+ printf("checking actual mech (%s) on export/imported cred\n",
+ gss_oid_to_name(actual_mech));
+ loop(actual_mech, nameoid, argv[0], cred2, &sctx, &cctx,
+ &actual_mech2, &deleg_cred);
+
+ gss_release_cred(&min_stat, &deleg_cred);
+
+ gss_delete_sec_context(&min_stat, &cctx, NULL);
+ gss_delete_sec_context(&min_stat, &sctx, NULL);
+
+#if 0
+ /* XXX See above */
+ /* try again using SPNEGO */
+ if (verbose_flag)
+ printf("checking SPNEGO on export/imported cred\n");
+ loop(GSS_SPNEGO_MECHANISM, nameoid, argv[0], cred2, &sctx, &cctx,
+ &actual_mech2, &deleg_cred);
+
+ gss_release_cred(&min_stat, &deleg_cred);
+
+ gss_delete_sec_context(&min_stat, &cctx, NULL);
+ gss_delete_sec_context(&min_stat, &sctx, NULL);
+#endif
+
+ gss_release_cred(&min_stat, &cred2);
+
+ } else {
+ gss_release_cred(&min_stat, &deleg_cred);
+ }
+
+ }
+
+ gss_release_cred(&min_stat, &client_cred);
+ gss_release_oid_set(&min_stat, &actual_mechs);
+ if (mechoids != GSS_C_NO_OID_SET && mechoids != &mechoid_descs)
+ gss_release_oid_set(&min_stat, &mechoids);
+ empty_release();
+
+ krb5_free_context(context);
+
+ return 0;
+}