Home | History | Annotate | Line # | Download | only in gssapi
      1 /*	$NetBSD: test_context.c,v 1.3 2023/06/19 21:41:42 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2006 - 2008 Kungliga Tekniska Hgskolan
      5  * (Royal Institute of Technology, Stockholm, Sweden).
      6  * All rights reserved.
      7  *
      8  * Redistribution and use in source and binary forms, with or without
      9  * modification, are permitted provided that the following conditions
     10  * are met:
     11  *
     12  * 1. Redistributions of source code must retain the above copyright
     13  *    notice, this list of conditions and the following disclaimer.
     14  *
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  *
     19  * 3. Neither the name of KTH nor the names of its contributors may be
     20  *    used to endorse or promote products derived from this software without
     21  *    specific prior written permission.
     22  *
     23  * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY
     24  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     26  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE
     27  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     28  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     29  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
     30  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     31  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     32  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
     33  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     34  */
     35 
     36 #include "krb5/gsskrb5_locl.h"
     37 #include <err.h>
     38 #include <krb5/getarg.h>
     39 #include <gssapi/gssapi.h>
     40 #include <gssapi/gssapi_krb5.h>
     41 #include <gssapi/gssapi_spnego.h>
     42 #include <gssapi/gssapi_ntlm.h>
     43 #include "test_common.h"
     44 
     45 static char *type_string;
     46 static char *mech_string;
     47 static char *mechs_string;
     48 static char *ret_mech_string;
     49 static char *client_name;
     50 static char *client_password;
     51 static int dns_canon_flag = -1;
     52 static int mutual_auth_flag = 0;
     53 static int dce_style_flag = 0;
     54 static int wrapunwrap_flag = 0;
     55 static int iov_flag = 0;
     56 static int aead_flag = 0;
     57 static int getverifymic_flag = 0;
     58 static int deleg_flag = 0;
     59 static int policy_deleg_flag = 0;
     60 static int server_no_deleg_flag = 0;
     61 static int ei_flag = 0;
     62 static char *gsskrb5_acceptor_identity = NULL;
     63 static char *session_enctype_string = NULL;
     64 static int client_time_offset = 0;
     65 static int server_time_offset = 0;
     66 static int max_loops = 0;
     67 static char *limit_enctype_string = NULL;
     68 static int version_flag = 0;
     69 static int verbose_flag = 0;
     70 static int help_flag	= 0;
     71 
     72 static krb5_context context;
     73 static krb5_enctype limit_enctype = 0;
     74 
     75 static struct {
     76     const char *name;
     77     gss_OID oid;
     78 } o2n[] = {
     79     { "krb5", NULL /* GSS_KRB5_MECHANISM */ },
     80     { "spnego", NULL /* GSS_SPNEGO_MECHANISM */ },
     81     { "ntlm", NULL /* GSS_NTLM_MECHANISM */ },
     82     { "sasl-digest-md5", NULL /* GSS_SASL_DIGEST_MD5_MECHANISM */ }
     83 };
     84 
     85 static void
     86 init_o2n(void)
     87 {
     88     o2n[0].oid = GSS_KRB5_MECHANISM;
     89     o2n[1].oid = GSS_SPNEGO_MECHANISM;
     90     o2n[2].oid = GSS_NTLM_MECHANISM;
     91     o2n[3].oid = GSS_SASL_DIGEST_MD5_MECHANISM;
     92 }
     93 
     94 static gss_OID
     95 string_to_oid(const char *name)
     96 {
     97     size_t i;
     98     for (i = 0; i < sizeof(o2n)/sizeof(o2n[0]); i++)
     99 	if (strcasecmp(name, o2n[i].name) == 0)
    100 	    return o2n[i].oid;
    101     errx(1, "name '%s' not unknown", name);
    102 }
    103 
    104 static void
    105 string_to_oids(gss_OID_set *oidsetp, gss_OID_set oidset,
    106                gss_OID_desc *oidarray, size_t oidarray_len,
    107                char *names)
    108 {
    109     char *name;
    110     char *s;
    111 
    112     if (names[0] == '\0') {
    113         *oidsetp = GSS_C_NO_OID_SET;
    114         return;
    115     }
    116 
    117     oidset->elements = &oidarray[0];
    118     if (strcasecmp(names, "all") == 0) {
    119         if (sizeof(o2n)/sizeof(o2n[0]) > oidarray_len)
    120             errx(1, "internal error: oidarray must be enlarged");
    121         for (oidset->count = 0; oidset->count < oidarray_len; oidset->count++)
    122             oidset->elements[oidset->count] = *o2n[oidset->count].oid;
    123     } else {
    124         for (oidset->count = 0, name = strtok_r(names, ", ", &s);
    125              name != NULL;
    126              oidset->count++, name = strtok_r(NULL, ", ", &s)) {
    127             if (oidset->count >= oidarray_len)
    128                 errx(1, "too many mech names given");
    129             oidset->elements[oidset->count] = *string_to_oid(name);
    130         }
    131         oidset->count = oidset->count;
    132     }
    133     *oidsetp = oidset;
    134 }
    135 
    136 static const char *
    137 oid_to_string(const gss_OID oid)
    138 {
    139     size_t i;
    140     for (i = 0; i < sizeof(o2n)/sizeof(o2n[0]); i++)
    141 	if (gss_oid_equal(oid, o2n[i].oid))
    142 	    return o2n[i].name;
    143     return "unknown oid";
    144 }
    145 
    146 static void
    147 loop(gss_OID mechoid,
    148      gss_OID nameoid, const char *target,
    149      gss_cred_id_t init_cred,
    150      gss_ctx_id_t *sctx, gss_ctx_id_t *cctx,
    151      gss_OID *actual_mech,
    152      gss_cred_id_t *deleg_cred)
    153 {
    154     int server_done = 0, client_done = 0;
    155     int num_loops = 0;
    156     OM_uint32 maj_stat, min_stat;
    157     gss_name_t gss_target_name;
    158     gss_buffer_desc input_token, output_token;
    159     OM_uint32 flags = 0, ret_cflags, ret_sflags;
    160     gss_OID actual_mech_client;
    161     gss_OID actual_mech_server;
    162 
    163     *actual_mech = GSS_C_NO_OID;
    164 
    165     flags |= GSS_C_INTEG_FLAG;
    166     flags |= GSS_C_CONF_FLAG;
    167 
    168     if (mutual_auth_flag)
    169 	flags |= GSS_C_MUTUAL_FLAG;
    170     if (dce_style_flag)
    171 	flags |= GSS_C_DCE_STYLE;
    172     if (deleg_flag)
    173 	flags |= GSS_C_DELEG_FLAG;
    174     if (policy_deleg_flag)
    175 	flags |= GSS_C_DELEG_POLICY_FLAG;
    176 
    177     input_token.value = rk_UNCONST(target);
    178     input_token.length = strlen(target);
    179 
    180     maj_stat = gss_import_name(&min_stat,
    181 			       &input_token,
    182 			       nameoid,
    183 			       &gss_target_name);
    184     if (GSS_ERROR(maj_stat))
    185 	err(1, "import name creds failed with: %d", maj_stat);
    186 
    187     input_token.length = 0;
    188     input_token.value = NULL;
    189 
    190     while (!server_done || !client_done) {
    191 	num_loops++;
    192 
    193 	gsskrb5_set_time_offset(client_time_offset);
    194 
    195 	maj_stat = gss_init_sec_context(&min_stat,
    196 					init_cred,
    197 					cctx,
    198 					gss_target_name,
    199 					mechoid,
    200 					flags,
    201 					0,
    202 					NULL,
    203 					&input_token,
    204 					&actual_mech_client,
    205 					&output_token,
    206 					&ret_cflags,
    207 					NULL);
    208 	if (GSS_ERROR(maj_stat))
    209 	    errx(1, "init_sec_context: %s",
    210 		 gssapi_err(maj_stat, min_stat, mechoid));
    211 	if (maj_stat & GSS_S_CONTINUE_NEEDED)
    212 	    ;
    213 	else
    214 	    client_done = 1;
    215 
    216 	gsskrb5_get_time_offset(&client_time_offset);
    217 
    218 	if (client_done && server_done)
    219 	    break;
    220 
    221 	if (input_token.length != 0)
    222 	    gss_release_buffer(&min_stat, &input_token);
    223 
    224 	gsskrb5_set_time_offset(server_time_offset);
    225 
    226 	maj_stat = gss_accept_sec_context(&min_stat,
    227 					  sctx,
    228 					  GSS_C_NO_CREDENTIAL,
    229 					  &output_token,
    230 					  GSS_C_NO_CHANNEL_BINDINGS,
    231 					  NULL,
    232 					  &actual_mech_server,
    233 					  &input_token,
    234 					  &ret_sflags,
    235 					  NULL,
    236 					  deleg_cred);
    237 	if (GSS_ERROR(maj_stat))
    238 		errx(1, "accept_sec_context: %s",
    239 		     gssapi_err(maj_stat, min_stat, actual_mech_server));
    240 
    241 	gsskrb5_get_time_offset(&server_time_offset);
    242 
    243 	if (output_token.length != 0)
    244 	    gss_release_buffer(&min_stat, &output_token);
    245 
    246 	if (maj_stat & GSS_S_CONTINUE_NEEDED)
    247 	    ;
    248 	else
    249 	    server_done = 1;
    250     }
    251     if (output_token.length != 0)
    252 	gss_release_buffer(&min_stat, &output_token);
    253     if (input_token.length != 0)
    254 	gss_release_buffer(&min_stat, &input_token);
    255     gss_release_name(&min_stat, &gss_target_name);
    256 
    257     if (deleg_flag || policy_deleg_flag) {
    258 	if (server_no_deleg_flag) {
    259 	    if (*deleg_cred != GSS_C_NO_CREDENTIAL)
    260 		errx(1, "got delegated cred but didn't expect one");
    261 	} else if (*deleg_cred == GSS_C_NO_CREDENTIAL)
    262 	    errx(1, "asked for delegarated cred but did get one");
    263     } else if (*deleg_cred != GSS_C_NO_CREDENTIAL)
    264 	  errx(1, "got deleg_cred cred but didn't ask");
    265 
    266     if (gss_oid_equal(actual_mech_server, actual_mech_client) == 0)
    267 	errx(1, "mech mismatch");
    268     *actual_mech = actual_mech_server;
    269 
    270     if (max_loops && num_loops > max_loops)
    271 	errx(1, "num loops %d was lager then max loops %d",
    272 	     num_loops, max_loops);
    273 
    274     if (verbose_flag) {
    275 	printf("server time offset: %d\n", server_time_offset);
    276 	printf("client time offset: %d\n", client_time_offset);
    277 	printf("num loops %d\n", num_loops);
    278     }
    279 }
    280 
    281 static void
    282 wrapunwrap(gss_ctx_id_t cctx, gss_ctx_id_t sctx, int flags, gss_OID mechoid)
    283 {
    284     gss_buffer_desc input_token, output_token, output_token2;
    285     OM_uint32 min_stat, maj_stat;
    286     gss_qop_t qop_state;
    287     int conf_state;
    288 
    289     input_token.value = "foo";
    290     input_token.length = 3;
    291 
    292     maj_stat = gss_wrap(&min_stat, cctx, flags, 0, &input_token,
    293 			&conf_state, &output_token);
    294     if (maj_stat != GSS_S_COMPLETE)
    295 	errx(1, "gss_wrap failed: %s",
    296 	     gssapi_err(maj_stat, min_stat, mechoid));
    297 
    298     maj_stat = gss_unwrap(&min_stat, sctx, &output_token,
    299 			  &output_token2, &conf_state, &qop_state);
    300     if (maj_stat != GSS_S_COMPLETE)
    301 	errx(1, "gss_unwrap failed: %s",
    302 	     gssapi_err(maj_stat, min_stat, mechoid));
    303 
    304     gss_release_buffer(&min_stat, &output_token);
    305     gss_release_buffer(&min_stat, &output_token2);
    306 
    307 #if 0 /* doesn't work for NTLM yet */
    308     if (!!conf_state != !!flags)
    309 	errx(1, "conf_state mismatch");
    310 #endif
    311 }
    312 
    313 #define USE_CONF		1
    314 #define USE_HEADER_ONLY		2
    315 #define USE_SIGN_ONLY		4
    316 #define FORCE_IOV		8
    317 
    318 static void
    319 wrapunwrap_iov(gss_ctx_id_t cctx, gss_ctx_id_t sctx, int flags, gss_OID mechoid)
    320 {
    321     krb5_data token, header, trailer;
    322     OM_uint32 min_stat, maj_stat;
    323     gss_qop_t qop_state;
    324     int conf_state, conf_state2;
    325     gss_iov_buffer_desc iov[6];
    326     unsigned char *p;
    327     int iov_len;
    328     char header_data[9] = "ABCheader";
    329     char trailer_data[10] = "trailerXYZ";
    330 
    331     char token_data[16] = "0123456789abcdef";
    332 
    333     memset(&iov, 0, sizeof(iov));
    334 
    335     if (flags & USE_SIGN_ONLY) {
    336 	header.data = header_data;
    337 	header.length = 9;
    338 	trailer.data = trailer_data;
    339 	trailer.length = 10;
    340     } else {
    341 	header.data = NULL;
    342 	header.length = 0;
    343 	trailer.data = NULL;
    344 	trailer.length = 0;
    345     }
    346 
    347     token.data = token_data;
    348     token.length = 16;
    349 
    350     iov_len = sizeof(iov)/sizeof(iov[0]);
    351 
    352     memset(iov, 0, sizeof(iov));
    353 
    354     iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER | GSS_IOV_BUFFER_FLAG_ALLOCATE;
    355 
    356     if (header.length != 0) {
    357 	iov[1].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY;
    358 	iov[1].buffer.length = header.length;
    359 	iov[1].buffer.value = header.data;
    360     } else {
    361 	iov[1].type = GSS_IOV_BUFFER_TYPE_EMPTY;
    362 	iov[1].buffer.length = 0;
    363 	iov[1].buffer.value = NULL;
    364     }
    365     iov[2].type = GSS_IOV_BUFFER_TYPE_DATA;
    366     iov[2].buffer.length = token.length;
    367     iov[2].buffer.value = token.data;
    368     if (trailer.length != 0) {
    369 	iov[3].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY;
    370 	iov[3].buffer.length = trailer.length;
    371 	iov[3].buffer.value = trailer.data;
    372     } else {
    373 	iov[3].type = GSS_IOV_BUFFER_TYPE_EMPTY;
    374 	iov[3].buffer.length = 0;
    375 	iov[3].buffer.value = NULL;
    376     }
    377     if (dce_style_flag) {
    378 	iov[4].type = GSS_IOV_BUFFER_TYPE_EMPTY;
    379     } else {
    380 	iov[4].type = GSS_IOV_BUFFER_TYPE_PADDING | GSS_IOV_BUFFER_FLAG_ALLOCATE;
    381     }
    382     iov[4].buffer.length = 0;
    383     iov[4].buffer.value = 0;
    384     if (dce_style_flag) {
    385 	iov[5].type = GSS_IOV_BUFFER_TYPE_EMPTY;
    386     } else if (flags & USE_HEADER_ONLY) {
    387 	iov[5].type = GSS_IOV_BUFFER_TYPE_EMPTY;
    388     } else {
    389 	iov[5].type = GSS_IOV_BUFFER_TYPE_TRAILER | GSS_IOV_BUFFER_FLAG_ALLOCATE;
    390     }
    391     iov[5].buffer.length = 0;
    392     iov[5].buffer.value = 0;
    393 
    394     maj_stat = gss_wrap_iov(&min_stat, cctx, dce_style_flag || flags & USE_CONF, 0, &conf_state,
    395 			    iov, iov_len);
    396     if (maj_stat != GSS_S_COMPLETE)
    397 	errx(1, "gss_wrap_iov failed");
    398 
    399     token.length =
    400 	iov[0].buffer.length +
    401 	iov[1].buffer.length +
    402 	iov[2].buffer.length +
    403 	iov[3].buffer.length +
    404 	iov[4].buffer.length +
    405 	iov[5].buffer.length;
    406     token.data = emalloc(token.length);
    407 
    408     p = token.data;
    409 
    410     if (iov[0].buffer.length)
    411         memcpy(p, iov[0].buffer.value, iov[0].buffer.length);
    412     p += iov[0].buffer.length;
    413 
    414     if (iov[1].buffer.length)
    415         memcpy(p, iov[1].buffer.value, iov[1].buffer.length);
    416     p += iov[1].buffer.length;
    417 
    418     if (iov[2].buffer.length)
    419         memcpy(p, iov[2].buffer.value, iov[2].buffer.length);
    420     p += iov[2].buffer.length;
    421 
    422     if (iov[3].buffer.length)
    423         memcpy(p, iov[3].buffer.value, iov[3].buffer.length);
    424     p += iov[3].buffer.length;
    425 
    426     if (iov[4].buffer.length)
    427         memcpy(p, iov[4].buffer.value, iov[4].buffer.length);
    428     p += iov[4].buffer.length;
    429 
    430     if (iov[5].buffer.length)
    431         memcpy(p, iov[5].buffer.value, iov[5].buffer.length);
    432     p += iov[5].buffer.length;
    433 
    434     assert(p - ((unsigned char *)token.data) == token.length);
    435 
    436     if ((flags & (USE_SIGN_ONLY|FORCE_IOV)) == 0) {
    437 	gss_buffer_desc input, output;
    438 
    439 	input.value = token.data;
    440 	input.length = token.length;
    441 
    442 	maj_stat = gss_unwrap(&min_stat, sctx, &input,
    443 			      &output, &conf_state2, &qop_state);
    444 
    445 	if (maj_stat != GSS_S_COMPLETE)
    446 	    errx(1, "gss_unwrap from gss_wrap_iov failed: %s",
    447 		 gssapi_err(maj_stat, min_stat, mechoid));
    448 
    449 	gss_release_buffer(&min_stat, &output);
    450     } else {
    451 	maj_stat = gss_unwrap_iov(&min_stat, sctx, &conf_state2, &qop_state,
    452 				  iov, iov_len);
    453 
    454 	if (maj_stat != GSS_S_COMPLETE)
    455 	    errx(1, "gss_unwrap_iov failed: %x %s", flags,
    456 		 gssapi_err(maj_stat, min_stat, mechoid));
    457 
    458     }
    459     if (conf_state2 != conf_state)
    460 	errx(1, "conf state wrong for iov: %x", flags);
    461 
    462     gss_release_iov_buffer(&min_stat, iov, iov_len);
    463 
    464     free(token.data);
    465 }
    466 
    467 static void
    468 wrapunwrap_aead(gss_ctx_id_t cctx, gss_ctx_id_t sctx, int flags, gss_OID mechoid)
    469 {
    470     gss_buffer_desc token, assoc, message = GSS_C_EMPTY_BUFFER;
    471     gss_buffer_desc output;
    472     OM_uint32 min_stat, maj_stat;
    473     gss_qop_t qop_state;
    474     int conf_state, conf_state2;
    475     char assoc_data[9] = "ABCheader";
    476     char token_data[16] = "0123456789abcdef";
    477 
    478     if (flags & USE_SIGN_ONLY) {
    479 	assoc.value = assoc_data;
    480 	assoc.length = 9;
    481     } else {
    482 	assoc.value = NULL;
    483 	assoc.length = 0;
    484     }
    485 
    486     token.value = token_data;
    487     token.length = 16;
    488 
    489     maj_stat = gss_wrap_aead(&min_stat, cctx, dce_style_flag || flags & USE_CONF,
    490 			     GSS_C_QOP_DEFAULT, &assoc, &token,
    491 			     &conf_state, &message);
    492     if (maj_stat != GSS_S_COMPLETE)
    493 	errx(1, "gss_wrap_aead failed");
    494 
    495     if ((flags & (USE_SIGN_ONLY|FORCE_IOV)) == 0) {
    496 	maj_stat = gss_unwrap(&min_stat, sctx, &message,
    497 			      &output, &conf_state2, &qop_state);
    498 
    499 	if (maj_stat != GSS_S_COMPLETE)
    500 	    errx(1, "gss_unwrap from gss_wrap_aead failed: %s",
    501 		 gssapi_err(maj_stat, min_stat, mechoid));
    502     } else {
    503 	maj_stat = gss_unwrap_aead(&min_stat, sctx, &message, &assoc,
    504 				   &output, &conf_state2, &qop_state);
    505 	if (maj_stat != GSS_S_COMPLETE)
    506 	    errx(1, "gss_unwrap_aead failed: %x %s", flags,
    507 		 gssapi_err(maj_stat, min_stat, mechoid));
    508     }
    509 
    510     if (output.length != token.length)
    511 	errx(1, "plaintext length wrong for aead");
    512     else if (memcmp(output.value, token.value, token.length) != 0)
    513 	errx(1, "plaintext wrong for aead");
    514     if (conf_state2 != conf_state)
    515 	errx(1, "conf state wrong for aead: %x", flags);
    516 
    517     gss_release_buffer(&min_stat, &message);
    518     gss_release_buffer(&min_stat, &output);
    519 }
    520 
    521 static void
    522 getverifymic(gss_ctx_id_t cctx, gss_ctx_id_t sctx, gss_OID mechoid)
    523 {
    524     gss_buffer_desc input_token, output_token;
    525     OM_uint32 min_stat, maj_stat;
    526     gss_qop_t qop_state;
    527 
    528     input_token.value = "bar";
    529     input_token.length = 3;
    530 
    531     maj_stat = gss_get_mic(&min_stat, cctx, 0, &input_token,
    532 			   &output_token);
    533     if (maj_stat != GSS_S_COMPLETE)
    534 	errx(1, "gss_get_mic failed: %s",
    535 	     gssapi_err(maj_stat, min_stat, mechoid));
    536 
    537     maj_stat = gss_verify_mic(&min_stat, sctx, &input_token,
    538 			      &output_token, &qop_state);
    539     if (maj_stat != GSS_S_COMPLETE)
    540 	errx(1, "gss_verify_mic failed: %s",
    541 	     gssapi_err(maj_stat, min_stat, mechoid));
    542 
    543     gss_release_buffer(&min_stat, &output_token);
    544 }
    545 
    546 static void
    547 empty_release(void)
    548 {
    549     gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
    550     gss_cred_id_t cred = GSS_C_NO_CREDENTIAL;
    551     gss_name_t name = GSS_C_NO_NAME;
    552     gss_OID_set oidset = GSS_C_NO_OID_SET;
    553     OM_uint32 junk;
    554 
    555     gss_delete_sec_context(&junk, &ctx, NULL);
    556     gss_release_cred(&junk, &cred);
    557     gss_release_name(&junk, &name);
    558     gss_release_oid_set(&junk, &oidset);
    559 }
    560 
    561 /*
    562  *
    563  */
    564 
    565 static struct getargs args[] = {
    566     {"name-type",0,	arg_string, &type_string,  "type of name", NULL },
    567     {"mech-type",0,	arg_string, &mech_string,  "mech type (name)", NULL },
    568     {"mech-types",0,	arg_string, &mechs_string, "mech types (names)", NULL },
    569     {"ret-mech-type",0,	arg_string, &ret_mech_string,
    570      "type of return mech", NULL },
    571     {"dns-canonicalize",0,arg_negative_flag, &dns_canon_flag,
    572      "use dns to canonicalize", NULL },
    573     {"mutual-auth",0,	arg_flag,	&mutual_auth_flag,"mutual auth", NULL },
    574     {"client-name", 0,  arg_string,     &client_name, "client name", NULL },
    575     {"client-password", 0,  arg_string, &client_password, "client password", NULL },
    576     {"limit-enctype",0,	arg_string,	&limit_enctype_string, "enctype", NULL },
    577     {"dce-style",0,	arg_flag,	&dce_style_flag, "dce-style", NULL },
    578     {"wrapunwrap",0,	arg_flag,	&wrapunwrap_flag, "wrap/unwrap", NULL },
    579     {"iov", 0, 		arg_flag,	&iov_flag, "wrap/unwrap iov", NULL },
    580     {"aead", 0, 	arg_flag,	&aead_flag, "wrap/unwrap aead", NULL },
    581     {"getverifymic",0,	arg_flag,	&getverifymic_flag,
    582      "get and verify mic", NULL },
    583     {"delegate",0,	arg_flag,	&deleg_flag, "delegate credential", NULL },
    584     {"policy-delegate",0,	arg_flag,	&policy_deleg_flag, "policy delegate credential", NULL },
    585     {"server-no-delegate",0,	arg_flag,	&server_no_deleg_flag,
    586      "server should get a credential", NULL },
    587     {"export-import-cred",0,	arg_flag,	&ei_flag, "test export/import cred", NULL },
    588     {"gsskrb5-acceptor-identity", 0, arg_string, &gsskrb5_acceptor_identity, "keytab", NULL },
    589     {"session-enctype",	0, arg_string,	&session_enctype_string, "enctype", NULL },
    590     {"client-time-offset",	0, arg_integer,	&client_time_offset, "time", NULL },
    591     {"server-time-offset",	0, arg_integer,	&server_time_offset, "time", NULL },
    592     {"max-loops",	0, arg_integer,	&max_loops, "time", NULL },
    593     {"version",	0,	arg_flag,	&version_flag, "print version", NULL },
    594     {"verbose",	'v',	arg_flag,	&verbose_flag, "verbose", NULL },
    595     {"help",	0,	arg_flag,	&help_flag,  NULL, NULL }
    596 };
    597 
    598 static void
    599 usage (int ret)
    600 {
    601     arg_printusage (args, sizeof(args)/sizeof(*args),
    602 		    NULL, "service@host");
    603     exit (ret);
    604 }
    605 
    606 int
    607 main(int argc, char **argv)
    608 {
    609     int optidx = 0;
    610     OM_uint32 min_stat, maj_stat;
    611     gss_ctx_id_t cctx, sctx;
    612     void *ctx;
    613     gss_OID nameoid, mechoid, actual_mech, actual_mech2;
    614     gss_cred_id_t client_cred = GSS_C_NO_CREDENTIAL, deleg_cred = GSS_C_NO_CREDENTIAL;
    615     gss_name_t cname = GSS_C_NO_NAME;
    616     gss_buffer_desc credential_data = GSS_C_EMPTY_BUFFER;
    617     gss_OID_desc oids[4];
    618     gss_OID_set_desc mechoid_descs;
    619     gss_OID_set mechoids = GSS_C_NO_OID_SET;
    620 
    621     setprogname(argv[0]);
    622 
    623     init_o2n();
    624 
    625     if (krb5_init_context(&context))
    626 	errx(1, "krb5_init_context");
    627 
    628     cctx = sctx = GSS_C_NO_CONTEXT;
    629 
    630     if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
    631 	usage(1);
    632 
    633     if (help_flag)
    634 	usage (0);
    635 
    636     if(version_flag){
    637 	print_version(NULL);
    638 	exit(0);
    639     }
    640 
    641     argc -= optidx;
    642     argv += optidx;
    643 
    644     if (argc != 1)
    645 	usage(1);
    646 
    647     if (dns_canon_flag != -1)
    648 	gsskrb5_set_dns_canonicalize(dns_canon_flag);
    649 
    650     if (type_string == NULL)
    651 	nameoid = GSS_C_NT_HOSTBASED_SERVICE;
    652     else if (strcmp(type_string, "hostbased-service") == 0)
    653 	nameoid = GSS_C_NT_HOSTBASED_SERVICE;
    654     else if (strcmp(type_string, "krb5-principal-name") == 0)
    655 	nameoid = GSS_KRB5_NT_PRINCIPAL_NAME;
    656     else
    657 	errx(1, "%s not supported", type_string);
    658 
    659     if (mech_string == NULL)
    660 	mechoid = GSS_KRB5_MECHANISM;
    661     else
    662 	mechoid = string_to_oid(mech_string);
    663 
    664     if (mechs_string == NULL) {
    665         /*
    666          * We ought to be able to use the OID set of the one mechanism
    667          * OID given.  But there's some breakage that conspires to make
    668          * that fail though it should succeed:
    669          *
    670          *  - the NTLM gss_acquire_cred() refuses to work with
    671          *    desired_name == GSS_C_NO_NAME
    672          *  - gss_acquire_cred() with desired_mechs == GSS_C_NO_OID_SET
    673          *    does work here because we happen to have Kerberos
    674          *    credentials in check-ntlm, and the subsequent
    675          *    gss_init_sec_context() call finds no cred element for NTLM
    676          *    but plows on anyways, surprisingly enough, and then the
    677          *    NTLM gss_init_sec_context() just works.
    678          *
    679          * In summary, there's some breakage in gss_init_sec_context()
    680          * and some breakage in NTLM that conspires against us here.
    681          *
    682          * We work around this in check-ntlm and check-spnego by adding
    683          * --client-name=user1@${R} to the invocations of this test
    684          * program that require it.
    685          */
    686         oids[0] = *mechoid;
    687         mechoid_descs.elements = &oids[0];
    688         mechoid_descs.count = 1;
    689         mechoids = &mechoid_descs;
    690     } else {
    691         string_to_oids(&mechoids, &mechoid_descs,
    692                        oids, sizeof(oids)/sizeof(oids[0]), mechs_string);
    693     }
    694 
    695     if (gsskrb5_acceptor_identity) {
    696 	maj_stat = gsskrb5_register_acceptor_identity(gsskrb5_acceptor_identity);
    697 	if (maj_stat)
    698 	    errx(1, "gsskrb5_acceptor_identity: %s",
    699 		 gssapi_err(maj_stat, 0, GSS_C_NO_OID));
    700     }
    701 
    702     if (client_password) {
    703 	credential_data.value = client_password;
    704 	credential_data.length = strlen(client_password);
    705     }
    706 
    707     if (client_name) {
    708 	gss_buffer_desc cn;
    709 
    710 	cn.value = client_name;
    711 	cn.length = strlen(client_name);
    712 
    713 	maj_stat = gss_import_name(&min_stat, &cn, GSS_C_NT_USER_NAME, &cname);
    714 	if (maj_stat)
    715 	    errx(1, "gss_import_name: %s",
    716 		 gssapi_err(maj_stat, min_stat, GSS_C_NO_OID));
    717     }
    718 
    719     if (client_password) {
    720 	maj_stat = gss_acquire_cred_with_password(&min_stat,
    721 						  cname,
    722 						  &credential_data,
    723 						  GSS_C_INDEFINITE,
    724 						  mechoids,
    725 						  GSS_C_INITIATE,
    726 						  &client_cred,
    727 						  NULL,
    728 						  NULL);
    729 	if (GSS_ERROR(maj_stat)) {
    730             if (mechoids != GSS_C_NO_OID_SET && mechoids->count == 1)
    731                 mechoid = &mechoids->elements[0];
    732             else
    733                 mechoid = GSS_C_NO_OID;
    734 	    errx(1, "gss_acquire_cred_with_password: %s",
    735 		 gssapi_err(maj_stat, min_stat, mechoid));
    736         }
    737     } else {
    738 	maj_stat = gss_acquire_cred(&min_stat,
    739 				    cname,
    740 				    GSS_C_INDEFINITE,
    741 				    mechoids,
    742 				    GSS_C_INITIATE,
    743 				    &client_cred,
    744 				    NULL,
    745 				    NULL);
    746 	if (GSS_ERROR(maj_stat))
    747 	    errx(1, "gss_acquire_cred: %s",
    748 		 gssapi_err(maj_stat, min_stat, GSS_C_NO_OID));
    749     }
    750 
    751     if (limit_enctype_string) {
    752 	krb5_error_code ret;
    753 
    754 	ret = krb5_string_to_enctype(context,
    755 				     limit_enctype_string,
    756 				     &limit_enctype);
    757 	if (ret)
    758 	    krb5_err(context, 1, ret, "krb5_string_to_enctype");
    759     }
    760 
    761 
    762     if (limit_enctype) {
    763 	if (client_cred == NULL)
    764 	    errx(1, "client_cred missing");
    765 
    766 	maj_stat = gss_krb5_set_allowable_enctypes(&min_stat, client_cred,
    767 						   1, &limit_enctype);
    768 	if (maj_stat)
    769 	    errx(1, "gss_krb5_set_allowable_enctypes: %s",
    770 		 gssapi_err(maj_stat, min_stat, GSS_C_NO_OID));
    771     }
    772 
    773     loop(mechoid, nameoid, argv[0], client_cred,
    774 	 &sctx, &cctx, &actual_mech, &deleg_cred);
    775 
    776     if (verbose_flag)
    777 	printf("resulting mech: %s\n", oid_to_string(actual_mech));
    778 
    779     if (ret_mech_string) {
    780 	gss_OID retoid;
    781 
    782 	retoid = string_to_oid(ret_mech_string);
    783 
    784 	if (gss_oid_equal(retoid, actual_mech) == 0)
    785 	    errx(1, "actual_mech mech is not the expected type %s",
    786 		 ret_mech_string);
    787     }
    788 
    789     /* XXX should be actual_mech */
    790     if (gss_oid_equal(mechoid, GSS_KRB5_MECHANISM)) {
    791 	time_t sc_time;
    792 	gss_buffer_desc authz_data;
    793 	gss_buffer_desc in, out1, out2;
    794 	krb5_keyblock *keyblock, *keyblock2;
    795 	krb5_timestamp now;
    796 	krb5_error_code ret;
    797 
    798 	ret = krb5_timeofday(context, &now);
    799 	if (ret)
    800 	    errx(1, "krb5_timeofday failed");
    801 
    802 	/* client */
    803 	maj_stat = gss_krb5_export_lucid_sec_context(&min_stat,
    804 						     &cctx,
    805 						     1, /* version */
    806 						     &ctx);
    807 	if (maj_stat != GSS_S_COMPLETE)
    808 	    errx(1, "gss_krb5_export_lucid_sec_context failed: %s",
    809 		 gssapi_err(maj_stat, min_stat, actual_mech));
    810 
    811 
    812 	maj_stat = gss_krb5_free_lucid_sec_context(&maj_stat, ctx);
    813 	if (maj_stat != GSS_S_COMPLETE)
    814 	    errx(1, "gss_krb5_free_lucid_sec_context failed: %s",
    815 		     gssapi_err(maj_stat, min_stat, actual_mech));
    816 
    817 	/* server */
    818 	maj_stat = gss_krb5_export_lucid_sec_context(&min_stat,
    819 						     &sctx,
    820 						     1, /* version */
    821 						     &ctx);
    822 	if (maj_stat != GSS_S_COMPLETE)
    823 	    errx(1, "gss_krb5_export_lucid_sec_context failed: %s",
    824 		     gssapi_err(maj_stat, min_stat, actual_mech));
    825 	maj_stat = gss_krb5_free_lucid_sec_context(&min_stat, ctx);
    826 	if (maj_stat != GSS_S_COMPLETE)
    827 	    errx(1, "gss_krb5_free_lucid_sec_context failed: %s",
    828 		     gssapi_err(maj_stat, min_stat, actual_mech));
    829 
    830  	maj_stat = gsskrb5_extract_authtime_from_sec_context(&min_stat,
    831 							     sctx,
    832 							     &sc_time);
    833 	if (maj_stat != GSS_S_COMPLETE)
    834 	    errx(1, "gsskrb5_extract_authtime_from_sec_context failed: %s",
    835 		     gssapi_err(maj_stat, min_stat, actual_mech));
    836 
    837 	if (sc_time > now)
    838 	    errx(1, "gsskrb5_extract_authtime_from_sec_context failed: "
    839 		 "time authtime is before now: %ld %ld",
    840 		 (long)sc_time, (long)now);
    841 
    842  	maj_stat = gsskrb5_extract_service_keyblock(&min_stat,
    843 						    sctx,
    844 						    &keyblock);
    845 	if (maj_stat != GSS_S_COMPLETE)
    846 	    errx(1, "gsskrb5_export_service_keyblock failed: %s",
    847 		     gssapi_err(maj_stat, min_stat, actual_mech));
    848 
    849 	krb5_free_keyblock(context, keyblock);
    850 
    851  	maj_stat = gsskrb5_get_subkey(&min_stat,
    852 				      sctx,
    853 				      &keyblock);
    854 	if (maj_stat != GSS_S_COMPLETE
    855 	    && (!(maj_stat == GSS_S_FAILURE && min_stat == GSS_KRB5_S_KG_NO_SUBKEY)))
    856 	    errx(1, "gsskrb5_get_subkey server failed: %s",
    857 		     gssapi_err(maj_stat, min_stat, actual_mech));
    858 
    859 	if (maj_stat != GSS_S_COMPLETE)
    860 	    keyblock = NULL;
    861 	else if (limit_enctype && keyblock->keytype != limit_enctype)
    862 	    errx(1, "gsskrb5_get_subkey wrong enctype");
    863 
    864  	maj_stat = gsskrb5_get_subkey(&min_stat,
    865 				      cctx,
    866 				      &keyblock2);
    867 	if (maj_stat != GSS_S_COMPLETE
    868 	    && (!(maj_stat == GSS_S_FAILURE && min_stat == GSS_KRB5_S_KG_NO_SUBKEY)))
    869 	    errx(1, "gsskrb5_get_subkey client failed: %s",
    870 		     gssapi_err(maj_stat, min_stat, actual_mech));
    871 
    872 	if (maj_stat != GSS_S_COMPLETE)
    873 	    keyblock2 = NULL;
    874 	else if (limit_enctype && keyblock->keytype != limit_enctype)
    875 	    errx(1, "gsskrb5_get_subkey wrong enctype");
    876 
    877 	if (keyblock || keyblock2) {
    878 	    if (keyblock == NULL)
    879 		errx(1, "server missing token keyblock");
    880 	    if (keyblock2 == NULL)
    881 		errx(1, "client missing token keyblock");
    882 
    883 	    if (keyblock->keytype != keyblock2->keytype)
    884 		errx(1, "enctype mismatch");
    885 	    if (keyblock->keyvalue.length != keyblock2->keyvalue.length)
    886 		errx(1, "key length mismatch");
    887 	    if (memcmp(keyblock->keyvalue.data, keyblock2->keyvalue.data,
    888 		       keyblock2->keyvalue.length) != 0)
    889 		errx(1, "key data mismatch");
    890 	}
    891 
    892 	if (session_enctype_string) {
    893 	    krb5_enctype enctype;
    894 
    895 	    ret = krb5_string_to_enctype(context,
    896 					 session_enctype_string,
    897 					 &enctype);
    898 
    899 	    if (ret)
    900 		krb5_err(context, 1, ret, "krb5_string_to_enctype");
    901 
    902 	    if (enctype != keyblock->keytype)
    903 		errx(1, "keytype is not the expected %d != %d",
    904 		     (int)enctype, (int)keyblock2->keytype);
    905 	}
    906 
    907 	if (keyblock)
    908 	    krb5_free_keyblock(context, keyblock);
    909 	if (keyblock2)
    910 	    krb5_free_keyblock(context, keyblock2);
    911 
    912  	maj_stat = gsskrb5_get_initiator_subkey(&min_stat,
    913 						sctx,
    914 						&keyblock);
    915 	if (maj_stat != GSS_S_COMPLETE
    916 	    && (!(maj_stat == GSS_S_FAILURE && min_stat == GSS_KRB5_S_KG_NO_SUBKEY)))
    917 	    errx(1, "gsskrb5_get_initiator_subkey failed: %s",
    918 		     gssapi_err(maj_stat, min_stat, actual_mech));
    919 
    920 	if (maj_stat == GSS_S_COMPLETE) {
    921 
    922 	    if (limit_enctype && keyblock->keytype != limit_enctype)
    923 		errx(1, "gsskrb5_get_initiator_subkey wrong enctype");
    924 	    krb5_free_keyblock(context, keyblock);
    925 	}
    926 
    927  	maj_stat = gsskrb5_extract_authz_data_from_sec_context(&min_stat,
    928 							       sctx,
    929 							       128,
    930 							       &authz_data);
    931 	if (maj_stat == GSS_S_COMPLETE)
    932 	    gss_release_buffer(&min_stat, &authz_data);
    933 
    934 
    935 	memset(&out1, 0, sizeof(out1));
    936 	memset(&out2, 0, sizeof(out2));
    937 
    938 	in.value = "foo";
    939 	in.length = 3;
    940 
    941 	gss_pseudo_random(&min_stat, sctx, GSS_C_PRF_KEY_FULL, &in,
    942 			  100, &out1);
    943 	gss_pseudo_random(&min_stat, cctx, GSS_C_PRF_KEY_FULL, &in,
    944 			  100, &out2);
    945 
    946 	if (out1.length != out2.length)
    947 	    errx(1, "prf len mismatch");
    948 	if (out1.length && memcmp(out1.value, out2.value, out1.length) != 0)
    949 	    errx(1, "prf data mismatch");
    950 
    951 	gss_release_buffer(&min_stat, &out1);
    952 
    953 	gss_pseudo_random(&min_stat, sctx, GSS_C_PRF_KEY_FULL, &in,
    954 			  100, &out1);
    955 
    956 	if (out1.length != out2.length)
    957 	    errx(1, "prf len mismatch");
    958 	if (out1.length && memcmp(out1.value, out2.value, out1.length) != 0)
    959 	    errx(1, "prf data mismatch");
    960 
    961 	gss_release_buffer(&min_stat, &out1);
    962 	gss_release_buffer(&min_stat, &out2);
    963 
    964 	in.value = "bar";
    965 	in.length = 3;
    966 
    967 	gss_pseudo_random(&min_stat, sctx, GSS_C_PRF_KEY_PARTIAL, &in,
    968 			  100, &out1);
    969 	gss_pseudo_random(&min_stat, cctx, GSS_C_PRF_KEY_PARTIAL, &in,
    970 			  100, &out2);
    971 
    972 	if (out1.length != out2.length)
    973 	    errx(1, "prf len mismatch");
    974 	if (memcmp(out1.value, out2.value, out1.length) != 0)
    975 	    errx(1, "prf data mismatch");
    976 
    977 	gss_release_buffer(&min_stat, &out1);
    978 	gss_release_buffer(&min_stat, &out2);
    979 
    980 	wrapunwrap_flag = 1;
    981 	getverifymic_flag = 1;
    982     }
    983 
    984     if (wrapunwrap_flag) {
    985 	wrapunwrap(cctx, sctx, 0, actual_mech);
    986 	wrapunwrap(cctx, sctx, 1, actual_mech);
    987 	wrapunwrap(sctx, cctx, 0, actual_mech);
    988 	wrapunwrap(sctx, cctx, 1, actual_mech);
    989     }
    990 
    991     if (iov_flag) {
    992 	wrapunwrap_iov(cctx, sctx, 0, actual_mech);
    993 	wrapunwrap_iov(cctx, sctx, USE_HEADER_ONLY|FORCE_IOV, actual_mech);
    994 	wrapunwrap_iov(cctx, sctx, USE_HEADER_ONLY, actual_mech);
    995 	wrapunwrap_iov(cctx, sctx, USE_CONF, actual_mech);
    996 	wrapunwrap_iov(cctx, sctx, USE_CONF|USE_HEADER_ONLY, actual_mech);
    997 
    998 	wrapunwrap_iov(cctx, sctx, FORCE_IOV, actual_mech);
    999 	wrapunwrap_iov(cctx, sctx, USE_CONF|FORCE_IOV, actual_mech);
   1000 	wrapunwrap_iov(cctx, sctx, USE_HEADER_ONLY|FORCE_IOV, actual_mech);
   1001 	wrapunwrap_iov(cctx, sctx, USE_CONF|USE_HEADER_ONLY|FORCE_IOV, actual_mech);
   1002 
   1003 	wrapunwrap_iov(cctx, sctx, USE_SIGN_ONLY|FORCE_IOV, actual_mech);
   1004 	wrapunwrap_iov(cctx, sctx, USE_CONF|USE_SIGN_ONLY|FORCE_IOV, actual_mech);
   1005 	wrapunwrap_iov(cctx, sctx, USE_CONF|USE_HEADER_ONLY|USE_SIGN_ONLY|FORCE_IOV, actual_mech);
   1006 
   1007 /* works */
   1008 	wrapunwrap_iov(cctx, sctx, 0, actual_mech);
   1009 	wrapunwrap_iov(cctx, sctx, FORCE_IOV, actual_mech);
   1010 
   1011 	wrapunwrap_iov(cctx, sctx, USE_CONF, actual_mech);
   1012 	wrapunwrap_iov(cctx, sctx, USE_CONF|FORCE_IOV, actual_mech);
   1013 
   1014 	wrapunwrap_iov(cctx, sctx, USE_SIGN_ONLY, actual_mech);
   1015 	wrapunwrap_iov(cctx, sctx, USE_SIGN_ONLY|FORCE_IOV, actual_mech);
   1016 
   1017 	wrapunwrap_iov(cctx, sctx, USE_CONF|USE_SIGN_ONLY, actual_mech);
   1018 	wrapunwrap_iov(cctx, sctx, USE_CONF|USE_SIGN_ONLY|FORCE_IOV, actual_mech);
   1019 
   1020 	wrapunwrap_iov(cctx, sctx, USE_HEADER_ONLY, actual_mech);
   1021 	wrapunwrap_iov(cctx, sctx, USE_HEADER_ONLY|FORCE_IOV, actual_mech);
   1022 
   1023 	wrapunwrap_iov(cctx, sctx, USE_CONF|USE_HEADER_ONLY, actual_mech);
   1024 	wrapunwrap_iov(cctx, sctx, USE_CONF|USE_HEADER_ONLY|FORCE_IOV, actual_mech);
   1025     }
   1026 
   1027     if (aead_flag) {
   1028 	wrapunwrap_aead(cctx, sctx, 0, actual_mech);
   1029 	wrapunwrap_aead(cctx, sctx, USE_CONF, actual_mech);
   1030 
   1031 	wrapunwrap_aead(cctx, sctx, FORCE_IOV, actual_mech);
   1032 	wrapunwrap_aead(cctx, sctx, USE_CONF|FORCE_IOV, actual_mech);
   1033 
   1034 	wrapunwrap_aead(cctx, sctx, USE_SIGN_ONLY|FORCE_IOV, actual_mech);
   1035 	wrapunwrap_aead(cctx, sctx, USE_CONF|USE_SIGN_ONLY|FORCE_IOV, actual_mech);
   1036 
   1037 	wrapunwrap_aead(cctx, sctx, 0, actual_mech);
   1038 	wrapunwrap_aead(cctx, sctx, FORCE_IOV, actual_mech);
   1039 
   1040 	wrapunwrap_aead(cctx, sctx, USE_CONF, actual_mech);
   1041 	wrapunwrap_aead(cctx, sctx, USE_CONF|FORCE_IOV, actual_mech);
   1042 
   1043 	wrapunwrap_aead(cctx, sctx, USE_SIGN_ONLY, actual_mech);
   1044 	wrapunwrap_aead(cctx, sctx, USE_SIGN_ONLY|FORCE_IOV, actual_mech);
   1045 
   1046 	wrapunwrap_aead(cctx, sctx, USE_CONF|USE_SIGN_ONLY, actual_mech);
   1047 	wrapunwrap_aead(cctx, sctx, USE_CONF|USE_SIGN_ONLY|FORCE_IOV, actual_mech);
   1048     }
   1049 
   1050     if (getverifymic_flag) {
   1051 	getverifymic(cctx, sctx, actual_mech);
   1052 	getverifymic(cctx, sctx, actual_mech);
   1053 	getverifymic(sctx, cctx, actual_mech);
   1054 	getverifymic(sctx, cctx, actual_mech);
   1055     }
   1056 
   1057 
   1058     gss_delete_sec_context(&min_stat, &cctx, NULL);
   1059     gss_delete_sec_context(&min_stat, &sctx, NULL);
   1060 
   1061     if (deleg_cred != GSS_C_NO_CREDENTIAL) {
   1062 	gss_cred_id_t cred2 = GSS_C_NO_CREDENTIAL;
   1063 	gss_buffer_desc cb;
   1064 
   1065 	if (verbose_flag)
   1066 	    printf("checking actual mech (%s) on delegated cred\n",
   1067 		   oid_to_string(actual_mech));
   1068 	loop(actual_mech, nameoid, argv[0], deleg_cred, &sctx, &cctx, &actual_mech2, &cred2);
   1069 
   1070 	gss_delete_sec_context(&min_stat, &cctx, NULL);
   1071 	gss_delete_sec_context(&min_stat, &sctx, NULL);
   1072 
   1073 	gss_release_cred(&min_stat, &cred2);
   1074 
   1075 #if 0
   1076         /*
   1077          * XXX We can't do this.  Delegated credentials only work with
   1078          * the actual_mech.  We could gss_store_cred the delegated
   1079          * credentials *then* gss_add/acquire_cred() with SPNEGO, then
   1080          * we could try loop() with those credentials.
   1081          */
   1082 	/* try again using SPNEGO */
   1083 	if (verbose_flag)
   1084 	    printf("checking spnego on delegated cred\n");
   1085 	loop(GSS_SPNEGO_MECHANISM, nameoid, argv[0], deleg_cred, &sctx, &cctx,
   1086 	     &actual_mech2, &cred2);
   1087 
   1088 	gss_delete_sec_context(&min_stat, &cctx, NULL);
   1089 	gss_delete_sec_context(&min_stat, &sctx, NULL);
   1090 
   1091 	gss_release_cred(&min_stat, &cred2);
   1092 #endif
   1093 
   1094 	/* check export/import */
   1095 	if (ei_flag) {
   1096 
   1097 	    maj_stat = gss_export_cred(&min_stat, deleg_cred, &cb);
   1098 	    if (maj_stat != GSS_S_COMPLETE)
   1099 		errx(1, "export failed: %s",
   1100 		     gssapi_err(maj_stat, min_stat, NULL));
   1101 
   1102 	    maj_stat = gss_import_cred(&min_stat, &cb, &cred2);
   1103 	    if (maj_stat != GSS_S_COMPLETE)
   1104 		errx(1, "import failed: %s",
   1105 		     gssapi_err(maj_stat, min_stat, NULL));
   1106 
   1107 	    gss_release_buffer(&min_stat, &cb);
   1108 	    gss_release_cred(&min_stat, &deleg_cred);
   1109 
   1110 	    if (verbose_flag)
   1111 		printf("checking actual mech (%s) on export/imported cred\n",
   1112 		       oid_to_string(actual_mech));
   1113 	    loop(actual_mech, nameoid, argv[0], cred2, &sctx, &cctx,
   1114 		 &actual_mech2, &deleg_cred);
   1115 
   1116 	    gss_release_cred(&min_stat, &deleg_cred);
   1117 
   1118 	    gss_delete_sec_context(&min_stat, &cctx, NULL);
   1119 	    gss_delete_sec_context(&min_stat, &sctx, NULL);
   1120 
   1121 #if 0
   1122             /* XXX See above */
   1123 	    /* try again using SPNEGO */
   1124 	    if (verbose_flag)
   1125 		printf("checking SPNEGO on export/imported cred\n");
   1126 	    loop(GSS_SPNEGO_MECHANISM, nameoid, argv[0], cred2, &sctx, &cctx,
   1127 		 &actual_mech2, &deleg_cred);
   1128 
   1129 	    gss_release_cred(&min_stat, &deleg_cred);
   1130 
   1131 	    gss_delete_sec_context(&min_stat, &cctx, NULL);
   1132 	    gss_delete_sec_context(&min_stat, &sctx, NULL);
   1133 #endif
   1134 
   1135 	    gss_release_cred(&min_stat, &cred2);
   1136 
   1137 	} else  {
   1138 	    gss_release_cred(&min_stat, &deleg_cred);
   1139 	}
   1140 
   1141     }
   1142 
   1143     empty_release();
   1144 
   1145     krb5_free_context(context);
   1146 
   1147     return 0;
   1148 }
   1149