xref: /netbsd-src/crypto/external/bsd/heimdal/dist/lib/gssapi/test_context.c (revision afab4e300d3a9fb07dd8c80daf53d0feb3345706)
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 Högskolan
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
init_o2n(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
string_to_oid(const char * name)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
string_to_oids(gss_OID_set * oidsetp,gss_OID_set oidset,gss_OID_desc * oidarray,size_t oidarray_len,char * names)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 *
oid_to_string(const gss_OID oid)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
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)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
wrapunwrap(gss_ctx_id_t cctx,gss_ctx_id_t sctx,int flags,gss_OID mechoid)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
wrapunwrap_iov(gss_ctx_id_t cctx,gss_ctx_id_t sctx,int flags,gss_OID mechoid)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
wrapunwrap_aead(gss_ctx_id_t cctx,gss_ctx_id_t sctx,int flags,gss_OID mechoid)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
getverifymic(gss_ctx_id_t cctx,gss_ctx_id_t sctx,gss_OID mechoid)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
empty_release(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
usage(int ret)599 usage (int ret)
600 {
601     arg_printusage (args, sizeof(args)/sizeof(*args),
602 		    NULL, "service@host");
603     exit (ret);
604 }
605 
606 int
main(int argc,char ** argv)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