xref: /minix3/crypto/external/bsd/heimdal/dist/lib/gssapi/test_context.c (revision ebfedea0ce5bbe81e252ddf32d732e40fb633fae)
1 /*	$NetBSD: test_context.c,v 1.1.1.1 2011/04/13 18:14:44 elric 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 *ret_mech_string;
48 static char *client_name;
49 static int dns_canon_flag = -1;
50 static int mutual_auth_flag = 0;
51 static int dce_style_flag = 0;
52 static int wrapunwrap_flag = 0;
53 static int iov_flag = 0;
54 static int getverifymic_flag = 0;
55 static int deleg_flag = 0;
56 static int policy_deleg_flag = 0;
57 static int server_no_deleg_flag = 0;
58 static int ei_flag = 0;
59 static char *gsskrb5_acceptor_identity = NULL;
60 static char *session_enctype_string = NULL;
61 static int client_time_offset = 0;
62 static int server_time_offset = 0;
63 static int max_loops = 0;
64 static char *limit_enctype_string = NULL;
65 static int version_flag = 0;
66 static int verbose_flag = 0;
67 static int help_flag	= 0;
68 
69 static krb5_context context;
70 static krb5_enctype limit_enctype = 0;
71 
72 static struct {
73     const char *name;
74     gss_OID oid;
75 } o2n[] = {
76     { "krb5", NULL /* GSS_KRB5_MECHANISM */ },
77     { "spnego", NULL /* GSS_SPNEGO_MECHANISM */ },
78     { "ntlm", NULL /* GSS_NTLM_MECHANISM */ },
79     { "sasl-digest-md5", NULL /* GSS_SASL_DIGEST_MD5_MECHANISM */ }
80 };
81 
82 static void
83 init_o2n(void)
84 {
85     o2n[0].oid = GSS_KRB5_MECHANISM;
86     o2n[1].oid = GSS_SPNEGO_MECHANISM;
87     o2n[2].oid = GSS_NTLM_MECHANISM;
88     o2n[3].oid = GSS_SASL_DIGEST_MD5_MECHANISM;
89 }
90 
91 static gss_OID
92 string_to_oid(const char *name)
93 {
94     int i;
95     for (i = 0; i < sizeof(o2n)/sizeof(o2n[0]); i++)
96 	if (strcasecmp(name, o2n[i].name) == 0)
97 	    return o2n[i].oid;
98     errx(1, "name '%s' not unknown", name);
99 }
100 
101 static const char *
102 oid_to_string(const gss_OID oid)
103 {
104     int i;
105     for (i = 0; i < sizeof(o2n)/sizeof(o2n[0]); i++)
106 	if (gss_oid_equal(oid, o2n[i].oid))
107 	    return o2n[i].name;
108     return "unknown oid";
109 }
110 
111 static void
112 loop(gss_OID mechoid,
113      gss_OID nameoid, const char *target,
114      gss_cred_id_t init_cred,
115      gss_ctx_id_t *sctx, gss_ctx_id_t *cctx,
116      gss_OID *actual_mech,
117      gss_cred_id_t *deleg_cred)
118 {
119     int server_done = 0, client_done = 0;
120     int num_loops = 0;
121     OM_uint32 maj_stat, min_stat;
122     gss_name_t gss_target_name;
123     gss_buffer_desc input_token, output_token;
124     OM_uint32 flags = 0, ret_cflags, ret_sflags;
125     gss_OID actual_mech_client;
126     gss_OID actual_mech_server;
127 
128     *actual_mech = GSS_C_NO_OID;
129 
130     flags |= GSS_C_INTEG_FLAG;
131     flags |= GSS_C_CONF_FLAG;
132 
133     if (mutual_auth_flag)
134 	flags |= GSS_C_MUTUAL_FLAG;
135     if (dce_style_flag)
136 	flags |= GSS_C_DCE_STYLE;
137     if (deleg_flag)
138 	flags |= GSS_C_DELEG_FLAG;
139     if (policy_deleg_flag)
140 	flags |= GSS_C_DELEG_POLICY_FLAG;
141 
142     input_token.value = rk_UNCONST(target);
143     input_token.length = strlen(target);
144 
145     maj_stat = gss_import_name(&min_stat,
146 			       &input_token,
147 			       nameoid,
148 			       &gss_target_name);
149     if (GSS_ERROR(maj_stat))
150 	err(1, "import name creds failed with: %d", maj_stat);
151 
152     input_token.length = 0;
153     input_token.value = NULL;
154 
155     while (!server_done || !client_done) {
156 	num_loops++;
157 
158 	gsskrb5_set_time_offset(client_time_offset);
159 
160 	maj_stat = gss_init_sec_context(&min_stat,
161 					init_cred,
162 					cctx,
163 					gss_target_name,
164 					mechoid,
165 					flags,
166 					0,
167 					NULL,
168 					&input_token,
169 					&actual_mech_client,
170 					&output_token,
171 					&ret_cflags,
172 					NULL);
173 	if (GSS_ERROR(maj_stat))
174 	    errx(1, "init_sec_context: %s",
175 		 gssapi_err(maj_stat, min_stat, mechoid));
176 	if (maj_stat & GSS_S_CONTINUE_NEEDED)
177 	    ;
178 	else
179 	    client_done = 1;
180 
181 	gsskrb5_get_time_offset(&client_time_offset);
182 
183 	if (client_done && server_done)
184 	    break;
185 
186 	if (input_token.length != 0)
187 	    gss_release_buffer(&min_stat, &input_token);
188 
189 	gsskrb5_set_time_offset(server_time_offset);
190 
191 	maj_stat = gss_accept_sec_context(&min_stat,
192 					  sctx,
193 					  GSS_C_NO_CREDENTIAL,
194 					  &output_token,
195 					  GSS_C_NO_CHANNEL_BINDINGS,
196 					  NULL,
197 					  &actual_mech_server,
198 					  &input_token,
199 					  &ret_sflags,
200 					  NULL,
201 					  deleg_cred);
202 	if (GSS_ERROR(maj_stat))
203 		errx(1, "accept_sec_context: %s",
204 		     gssapi_err(maj_stat, min_stat, actual_mech_server));
205 
206 	gsskrb5_get_time_offset(&server_time_offset);
207 
208 	if (output_token.length != 0)
209 	    gss_release_buffer(&min_stat, &output_token);
210 
211 	if (maj_stat & GSS_S_CONTINUE_NEEDED)
212 	    ;
213 	else
214 	    server_done = 1;
215     }
216     if (output_token.length != 0)
217 	gss_release_buffer(&min_stat, &output_token);
218     if (input_token.length != 0)
219 	gss_release_buffer(&min_stat, &input_token);
220     gss_release_name(&min_stat, &gss_target_name);
221 
222     if (deleg_flag || policy_deleg_flag) {
223 	if (server_no_deleg_flag) {
224 	    if (*deleg_cred != GSS_C_NO_CREDENTIAL)
225 		errx(1, "got delegated cred but didn't expect one");
226 	} else if (*deleg_cred == GSS_C_NO_CREDENTIAL)
227 	    errx(1, "asked for delegarated cred but did get one");
228     } else if (*deleg_cred != GSS_C_NO_CREDENTIAL)
229 	  errx(1, "got deleg_cred cred but didn't ask");
230 
231     if (gss_oid_equal(actual_mech_server, actual_mech_client) == 0)
232 	errx(1, "mech mismatch");
233     *actual_mech = actual_mech_server;
234 
235     if (max_loops && num_loops > max_loops)
236 	errx(1, "num loops %d was lager then max loops %d",
237 	     num_loops, max_loops);
238 
239     if (verbose_flag) {
240 	printf("server time offset: %d\n", server_time_offset);
241 	printf("client time offset: %d\n", client_time_offset);
242 	printf("num loops %d\n", num_loops);
243     }
244 }
245 
246 static void
247 wrapunwrap(gss_ctx_id_t cctx, gss_ctx_id_t sctx, int flags, gss_OID mechoid)
248 {
249     gss_buffer_desc input_token, output_token, output_token2;
250     OM_uint32 min_stat, maj_stat;
251     gss_qop_t qop_state;
252     int conf_state;
253 
254     input_token.value = "foo";
255     input_token.length = 3;
256 
257     maj_stat = gss_wrap(&min_stat, cctx, flags, 0, &input_token,
258 			&conf_state, &output_token);
259     if (maj_stat != GSS_S_COMPLETE)
260 	errx(1, "gss_wrap failed: %s",
261 	     gssapi_err(maj_stat, min_stat, mechoid));
262 
263     maj_stat = gss_unwrap(&min_stat, sctx, &output_token,
264 			  &output_token2, &conf_state, &qop_state);
265     if (maj_stat != GSS_S_COMPLETE)
266 	errx(1, "gss_unwrap failed: %s",
267 	     gssapi_err(maj_stat, min_stat, mechoid));
268 
269     gss_release_buffer(&min_stat, &output_token);
270     gss_release_buffer(&min_stat, &output_token2);
271 
272 #if 0 /* doesn't work for NTLM yet */
273     if (!!conf_state != !!flags)
274 	errx(1, "conf_state mismatch");
275 #endif
276 }
277 
278 #define USE_CONF		1
279 #define USE_HEADER_ONLY		2
280 #define USE_SIGN_ONLY		4
281 #define FORCE_IOV		8
282 
283 static void
284 wrapunwrap_iov(gss_ctx_id_t cctx, gss_ctx_id_t sctx, int flags, gss_OID mechoid)
285 {
286     krb5_data token, header, trailer;
287     OM_uint32 min_stat, maj_stat;
288     gss_qop_t qop_state;
289     int conf_state, conf_state2;
290     gss_iov_buffer_desc iov[6];
291     unsigned char *p;
292     int iov_len;
293     char header_data[9] = "ABCheader";
294     char trailer_data[10] = "trailerXYZ";
295 
296     char token_data[16] = "0123456789abcdef";
297 
298     memset(&iov, 0, sizeof(iov));
299 
300     if (flags & USE_SIGN_ONLY) {
301 	header.data = header_data;
302 	header.length = 9;
303 	trailer.data = trailer_data;
304 	trailer.length = 10;
305     } else {
306 	header.data = NULL;
307 	header.length = 0;
308 	trailer.data = NULL;
309 	trailer.length = 0;
310     }
311 
312     token.data = token_data;
313     token.length = 16;
314 
315     iov_len = sizeof(iov)/sizeof(iov[0]);
316 
317     memset(iov, 0, sizeof(iov));
318 
319     iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER | GSS_IOV_BUFFER_TYPE_FLAG_ALLOCATE;
320 
321     if (header.length != 0) {
322 	iov[1].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY;
323 	iov[1].buffer.length = header.length;
324 	iov[1].buffer.value = header.data;
325     } else {
326 	iov[1].type = GSS_IOV_BUFFER_TYPE_EMPTY;
327 	iov[1].buffer.length = 0;
328 	iov[1].buffer.value = NULL;
329     }
330     iov[2].type = GSS_IOV_BUFFER_TYPE_DATA;
331     iov[2].buffer.length = token.length;
332     iov[2].buffer.value = token.data;
333     if (trailer.length != 0) {
334 	iov[3].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY;
335 	iov[3].buffer.length = trailer.length;
336 	iov[3].buffer.value = trailer.data;
337     } else {
338 	iov[3].type = GSS_IOV_BUFFER_TYPE_EMPTY;
339 	iov[3].buffer.length = 0;
340 	iov[3].buffer.value = NULL;
341     }
342     if (dce_style_flag) {
343 	iov[4].type = GSS_IOV_BUFFER_TYPE_EMPTY;
344     } else {
345 	iov[4].type = GSS_IOV_BUFFER_TYPE_PADDING | GSS_IOV_BUFFER_TYPE_FLAG_ALLOCATE;
346     }
347     iov[4].buffer.length = 0;
348     iov[4].buffer.value = 0;
349     if (dce_style_flag) {
350 	iov[5].type = GSS_IOV_BUFFER_TYPE_EMPTY;
351     } else if (flags & USE_HEADER_ONLY) {
352 	iov[5].type = GSS_IOV_BUFFER_TYPE_EMPTY;
353     } else {
354 	iov[5].type = GSS_IOV_BUFFER_TYPE_TRAILER | GSS_IOV_BUFFER_TYPE_FLAG_ALLOCATE;
355     }
356     iov[5].buffer.length = 0;
357     iov[5].buffer.value = 0;
358 
359     maj_stat = gss_wrap_iov(&min_stat, cctx, dce_style_flag || flags & USE_CONF, 0, &conf_state,
360 			    iov, iov_len);
361     if (maj_stat != GSS_S_COMPLETE)
362 	errx(1, "gss_wrap_iov failed");
363 
364     token.length =
365 	iov[0].buffer.length +
366 	iov[1].buffer.length +
367 	iov[2].buffer.length +
368 	iov[3].buffer.length +
369 	iov[4].buffer.length +
370 	iov[5].buffer.length;
371     token.data = emalloc(token.length);
372 
373     p = token.data;
374     memcpy(p, iov[0].buffer.value, iov[0].buffer.length);
375     p += iov[0].buffer.length;
376     memcpy(p, iov[1].buffer.value, iov[1].buffer.length);
377     p += iov[1].buffer.length;
378     memcpy(p, iov[2].buffer.value, iov[2].buffer.length);
379     p += iov[2].buffer.length;
380     memcpy(p, iov[3].buffer.value, iov[3].buffer.length);
381     p += iov[3].buffer.length;
382     memcpy(p, iov[4].buffer.value, iov[4].buffer.length);
383     p += iov[4].buffer.length;
384     memcpy(p, iov[5].buffer.value, iov[5].buffer.length);
385     p += iov[5].buffer.length;
386 
387     assert(p - ((unsigned char *)token.data) == token.length);
388 
389     if ((flags & (USE_SIGN_ONLY|FORCE_IOV)) == 0) {
390 	gss_buffer_desc input, output;
391 
392 	input.value = token.data;
393 	input.length = token.length;
394 
395 	maj_stat = gss_unwrap(&min_stat, sctx, &input,
396 			      &output, &conf_state2, &qop_state);
397 
398 	if (maj_stat != GSS_S_COMPLETE)
399 	    errx(1, "gss_unwrap from gss_wrap_iov failed: %s",
400 		 gssapi_err(maj_stat, min_stat, mechoid));
401 
402 	gss_release_buffer(&min_stat, &output);
403     } else {
404 	maj_stat = gss_unwrap_iov(&min_stat, sctx, &conf_state2, &qop_state,
405 				  iov, iov_len);
406 
407 	if (maj_stat != GSS_S_COMPLETE)
408 	    errx(1, "gss_unwrap_iov failed: %x %s", flags,
409 		 gssapi_err(maj_stat, min_stat, mechoid));
410 
411     }
412     if (conf_state2 != conf_state)
413 	errx(1, "conf state wrong for iov: %x", flags);
414 
415 
416     free(token.data);
417 }
418 
419 static void
420 getverifymic(gss_ctx_id_t cctx, gss_ctx_id_t sctx, gss_OID mechoid)
421 {
422     gss_buffer_desc input_token, output_token;
423     OM_uint32 min_stat, maj_stat;
424     gss_qop_t qop_state;
425 
426     input_token.value = "bar";
427     input_token.length = 3;
428 
429     maj_stat = gss_get_mic(&min_stat, cctx, 0, &input_token,
430 			   &output_token);
431     if (maj_stat != GSS_S_COMPLETE)
432 	errx(1, "gss_get_mic failed: %s",
433 	     gssapi_err(maj_stat, min_stat, mechoid));
434 
435     maj_stat = gss_verify_mic(&min_stat, sctx, &input_token,
436 			      &output_token, &qop_state);
437     if (maj_stat != GSS_S_COMPLETE)
438 	errx(1, "gss_verify_mic failed: %s",
439 	     gssapi_err(maj_stat, min_stat, mechoid));
440 
441     gss_release_buffer(&min_stat, &output_token);
442 }
443 
444 static void
445 empty_release(void)
446 {
447     gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
448     gss_cred_id_t cred = GSS_C_NO_CREDENTIAL;
449     gss_name_t name = GSS_C_NO_NAME;
450     gss_OID_set oidset = GSS_C_NO_OID_SET;
451     OM_uint32 junk;
452 
453     gss_delete_sec_context(&junk, &ctx, NULL);
454     gss_release_cred(&junk, &cred);
455     gss_release_name(&junk, &name);
456     gss_release_oid_set(&junk, &oidset);
457 }
458 
459 /*
460  *
461  */
462 
463 static struct getargs args[] = {
464     {"name-type",0,	arg_string, &type_string,  "type of name", NULL },
465     {"mech-type",0,	arg_string, &mech_string,  "type of mech", NULL },
466     {"ret-mech-type",0,	arg_string, &ret_mech_string,
467      "type of return mech", NULL },
468     {"dns-canonicalize",0,arg_negative_flag, &dns_canon_flag,
469      "use dns to canonicalize", NULL },
470     {"mutual-auth",0,	arg_flag,	&mutual_auth_flag,"mutual auth", NULL },
471     {"client-name", 0,  arg_string,     &client_name, "client name", NULL },
472     {"limit-enctype",0,	arg_string,	&limit_enctype_string, "enctype", NULL },
473     {"dce-style",0,	arg_flag,	&dce_style_flag, "dce-style", NULL },
474     {"wrapunwrap",0,	arg_flag,	&wrapunwrap_flag, "wrap/unwrap", NULL },
475     {"iov", 0, 		arg_flag,	&iov_flag, "wrap/unwrap iov", NULL },
476     {"getverifymic",0,	arg_flag,	&getverifymic_flag,
477      "get and verify mic", NULL },
478     {"delegate",0,	arg_flag,	&deleg_flag, "delegate credential", NULL },
479     {"policy-delegate",0,	arg_flag,	&policy_deleg_flag, "policy delegate credential", NULL },
480     {"server-no-delegate",0,	arg_flag,	&server_no_deleg_flag,
481      "server should get a credential", NULL },
482     {"export-import-cred",0,	arg_flag,	&ei_flag, "test export/import cred", NULL },
483     {"gsskrb5-acceptor-identity", 0, arg_string, &gsskrb5_acceptor_identity, "keytab", NULL },
484     {"session-enctype",	0, arg_string,	&session_enctype_string, "enctype", NULL },
485     {"client-time-offset",	0, arg_integer,	&client_time_offset, "time", NULL },
486     {"server-time-offset",	0, arg_integer,	&server_time_offset, "time", NULL },
487     {"max-loops",	0, arg_integer,	&max_loops, "time", NULL },
488     {"version",	0,	arg_flag,	&version_flag, "print version", NULL },
489     {"verbose",	'v',	arg_flag,	&verbose_flag, "verbose", NULL },
490     {"help",	0,	arg_flag,	&help_flag,  NULL, NULL }
491 };
492 
493 static void
494 usage (int ret)
495 {
496     arg_printusage (args, sizeof(args)/sizeof(*args),
497 		    NULL, "service@host");
498     exit (ret);
499 }
500 
501 int
502 main(int argc, char **argv)
503 {
504     int optind = 0;
505     OM_uint32 min_stat, maj_stat;
506     gss_ctx_id_t cctx, sctx;
507     void *ctx;
508     gss_OID nameoid, mechoid, actual_mech, actual_mech2;
509     gss_cred_id_t client_cred = GSS_C_NO_CREDENTIAL, deleg_cred = GSS_C_NO_CREDENTIAL;
510 
511     setprogname(argv[0]);
512 
513     init_o2n();
514 
515     if (krb5_init_context(&context))
516 	errx(1, "krb5_init_context");
517 
518     cctx = sctx = GSS_C_NO_CONTEXT;
519 
520     if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optind))
521 	usage(1);
522 
523     if (help_flag)
524 	usage (0);
525 
526     if(version_flag){
527 	print_version(NULL);
528 	exit(0);
529     }
530 
531     argc -= optind;
532     argv += optind;
533 
534     if (argc != 1)
535 	usage(1);
536 
537     if (dns_canon_flag != -1)
538 	gsskrb5_set_dns_canonicalize(dns_canon_flag);
539 
540     if (type_string == NULL)
541 	nameoid = GSS_C_NT_HOSTBASED_SERVICE;
542     else if (strcmp(type_string, "hostbased-service") == 0)
543 	nameoid = GSS_C_NT_HOSTBASED_SERVICE;
544     else if (strcmp(type_string, "krb5-principal-name") == 0)
545 	nameoid = GSS_KRB5_NT_PRINCIPAL_NAME;
546     else
547 	errx(1, "%s not suppported", type_string);
548 
549     if (mech_string == NULL)
550 	mechoid = GSS_KRB5_MECHANISM;
551     else
552 	mechoid = string_to_oid(mech_string);
553 
554     if (gsskrb5_acceptor_identity)
555 	gsskrb5_register_acceptor_identity(gsskrb5_acceptor_identity);
556 
557     if (client_name) {
558 	gss_buffer_desc cn;
559 	gss_name_t cname;
560 	cn.value = client_name;
561 	cn.length = strlen(client_name);
562 	maj_stat = gss_import_name(&min_stat, &cn, GSS_C_NT_USER_NAME, &cname);
563 	if (maj_stat)
564 	    errx(1, "gss_import_name: %s",
565 		 gssapi_err(maj_stat, min_stat, GSS_C_NO_OID));
566 
567 	maj_stat = gss_acquire_cred(&min_stat, cname, 0, NULL,
568 				    GSS_C_INITIATE, &client_cred, NULL, NULL);
569 	if (GSS_ERROR(maj_stat))
570 	    errx(1, "gss_import_name: %s",
571 		 gssapi_err(maj_stat, min_stat, GSS_C_NO_OID));
572 	gss_release_name(&min_stat, &cname);
573     }
574 
575     if (limit_enctype_string) {
576 	krb5_error_code ret;
577 
578 	ret = krb5_string_to_enctype(context,
579 				     limit_enctype_string,
580 				     &limit_enctype);
581 	if (ret)
582 	    krb5_err(context, 1, ret, "krb5_string_to_enctype");
583     }
584 
585 
586     if (limit_enctype) {
587 	if (client_cred == NULL)
588 	    errx(1, "client_cred missing");
589 
590 	maj_stat = gss_krb5_set_allowable_enctypes(&min_stat, client_cred,
591 						   1, &limit_enctype);
592 	if (maj_stat)
593 	    errx(1, "gss_krb5_set_allowable_enctypes: %s",
594 		 gssapi_err(maj_stat, min_stat, GSS_C_NO_OID));
595     }
596 
597     loop(mechoid, nameoid, argv[0], client_cred,
598 	 &sctx, &cctx, &actual_mech, &deleg_cred);
599 
600     if (verbose_flag)
601 	printf("resulting mech: %s\n", oid_to_string(actual_mech));
602 
603     if (ret_mech_string) {
604 	gss_OID retoid;
605 
606 	retoid = string_to_oid(ret_mech_string);
607 
608 	if (gss_oid_equal(retoid, actual_mech) == 0)
609 	    errx(1, "actual_mech mech is not the expected type %s",
610 		 ret_mech_string);
611     }
612 
613     /* XXX should be actual_mech */
614     if (gss_oid_equal(mechoid, GSS_KRB5_MECHANISM)) {
615 	time_t time;
616 	gss_buffer_desc authz_data;
617 	gss_buffer_desc in, out1, out2;
618 	krb5_keyblock *keyblock, *keyblock2;
619 	krb5_timestamp now;
620 	krb5_error_code ret;
621 
622 	ret = krb5_timeofday(context, &now);
623 	if (ret)
624 	    errx(1, "krb5_timeofday failed");
625 
626 	/* client */
627 	maj_stat = gss_krb5_export_lucid_sec_context(&min_stat,
628 						     &cctx,
629 						     1, /* version */
630 						     &ctx);
631 	if (maj_stat != GSS_S_COMPLETE)
632 	    errx(1, "gss_krb5_export_lucid_sec_context failed: %s",
633 		 gssapi_err(maj_stat, min_stat, actual_mech));
634 
635 
636 	maj_stat = gss_krb5_free_lucid_sec_context(&maj_stat, ctx);
637 	if (maj_stat != GSS_S_COMPLETE)
638 	    errx(1, "gss_krb5_free_lucid_sec_context failed: %s",
639 		     gssapi_err(maj_stat, min_stat, actual_mech));
640 
641 	/* server */
642 	maj_stat = gss_krb5_export_lucid_sec_context(&min_stat,
643 						     &sctx,
644 						     1, /* version */
645 						     &ctx);
646 	if (maj_stat != GSS_S_COMPLETE)
647 	    errx(1, "gss_krb5_export_lucid_sec_context failed: %s",
648 		     gssapi_err(maj_stat, min_stat, actual_mech));
649 	maj_stat = gss_krb5_free_lucid_sec_context(&min_stat, ctx);
650 	if (maj_stat != GSS_S_COMPLETE)
651 	    errx(1, "gss_krb5_free_lucid_sec_context failed: %s",
652 		     gssapi_err(maj_stat, min_stat, actual_mech));
653 
654  	maj_stat = gsskrb5_extract_authtime_from_sec_context(&min_stat,
655 							     sctx,
656 							     &time);
657 	if (maj_stat != GSS_S_COMPLETE)
658 	    errx(1, "gsskrb5_extract_authtime_from_sec_context failed: %s",
659 		     gssapi_err(maj_stat, min_stat, actual_mech));
660 
661 	if (time > now)
662 	    errx(1, "gsskrb5_extract_authtime_from_sec_context failed: "
663 		 "time authtime is before now: %ld %ld",
664 		 (long)time, (long)now);
665 
666  	maj_stat = gsskrb5_extract_service_keyblock(&min_stat,
667 						    sctx,
668 						    &keyblock);
669 	if (maj_stat != GSS_S_COMPLETE)
670 	    errx(1, "gsskrb5_export_service_keyblock failed: %s",
671 		     gssapi_err(maj_stat, min_stat, actual_mech));
672 
673 	krb5_free_keyblock(context, keyblock);
674 
675  	maj_stat = gsskrb5_get_subkey(&min_stat,
676 				      sctx,
677 				      &keyblock);
678 	if (maj_stat != GSS_S_COMPLETE
679 	    && (!(maj_stat == GSS_S_FAILURE && min_stat == GSS_KRB5_S_KG_NO_SUBKEY)))
680 	    errx(1, "gsskrb5_get_subkey server failed: %s",
681 		     gssapi_err(maj_stat, min_stat, actual_mech));
682 
683 	if (maj_stat != GSS_S_COMPLETE)
684 	    keyblock = NULL;
685 	else if (limit_enctype && keyblock->keytype != limit_enctype)
686 	    errx(1, "gsskrb5_get_subkey wrong enctype");
687 
688  	maj_stat = gsskrb5_get_subkey(&min_stat,
689 				      cctx,
690 				      &keyblock2);
691 	if (maj_stat != GSS_S_COMPLETE
692 	    && (!(maj_stat == GSS_S_FAILURE && min_stat == GSS_KRB5_S_KG_NO_SUBKEY)))
693 	    errx(1, "gsskrb5_get_subkey client failed: %s",
694 		     gssapi_err(maj_stat, min_stat, actual_mech));
695 
696 	if (maj_stat != GSS_S_COMPLETE)
697 	    keyblock2 = NULL;
698 	else if (limit_enctype && keyblock->keytype != limit_enctype)
699 	    errx(1, "gsskrb5_get_subkey wrong enctype");
700 
701 	if (keyblock || keyblock2) {
702 	    if (keyblock == NULL)
703 		errx(1, "server missing token keyblock");
704 	    if (keyblock2 == NULL)
705 		errx(1, "client missing token keyblock");
706 
707 	    if (keyblock->keytype != keyblock2->keytype)
708 		errx(1, "enctype mismatch");
709 	    if (keyblock->keyvalue.length != keyblock2->keyvalue.length)
710 		errx(1, "key length mismatch");
711 	    if (memcmp(keyblock->keyvalue.data, keyblock2->keyvalue.data,
712 		       keyblock2->keyvalue.length) != 0)
713 		errx(1, "key data mismatch");
714 	}
715 
716 	if (session_enctype_string) {
717 	    krb5_enctype enctype;
718 
719 	    ret = krb5_string_to_enctype(context,
720 					 session_enctype_string,
721 					 &enctype);
722 
723 	    if (ret)
724 		krb5_err(context, 1, ret, "krb5_string_to_enctype");
725 
726 	    if (enctype != keyblock->keytype)
727 		errx(1, "keytype is not the expected %d != %d",
728 		     (int)enctype, (int)keyblock2->keytype);
729 	}
730 
731 	if (keyblock)
732 	    krb5_free_keyblock(context, keyblock);
733 	if (keyblock2)
734 	    krb5_free_keyblock(context, keyblock2);
735 
736  	maj_stat = gsskrb5_get_initiator_subkey(&min_stat,
737 						sctx,
738 						&keyblock);
739 	if (maj_stat != GSS_S_COMPLETE
740 	    && (!(maj_stat == GSS_S_FAILURE && min_stat == GSS_KRB5_S_KG_NO_SUBKEY)))
741 	    errx(1, "gsskrb5_get_initiator_subkey failed: %s",
742 		     gssapi_err(maj_stat, min_stat, actual_mech));
743 
744 	if (maj_stat == GSS_S_COMPLETE) {
745 
746 	    if (limit_enctype && keyblock->keytype != limit_enctype)
747 		errx(1, "gsskrb5_get_initiator_subkey wrong enctype");
748 	    krb5_free_keyblock(context, keyblock);
749 	}
750 
751  	maj_stat = gsskrb5_extract_authz_data_from_sec_context(&min_stat,
752 							       sctx,
753 							       128,
754 							       &authz_data);
755 	if (maj_stat == GSS_S_COMPLETE)
756 	    gss_release_buffer(&min_stat, &authz_data);
757 
758 
759 	memset(&out1, 0, sizeof(out1));
760 	memset(&out2, 0, sizeof(out2));
761 
762 	in.value = "foo";
763 	in.length = 3;
764 
765 	gss_pseudo_random(&min_stat, sctx, GSS_C_PRF_KEY_FULL, &in,
766 			  100, &out1);
767 	gss_pseudo_random(&min_stat, cctx, GSS_C_PRF_KEY_FULL, &in,
768 			  100, &out2);
769 
770 	if (out1.length != out2.length)
771 	    errx(1, "prf len mismatch");
772 	if (memcmp(out1.value, out2.value, out1.length) != 0)
773 	    errx(1, "prf data mismatch");
774 
775 	gss_release_buffer(&min_stat, &out1);
776 
777 	gss_pseudo_random(&min_stat, sctx, GSS_C_PRF_KEY_FULL, &in,
778 			  100, &out1);
779 
780 	if (out1.length != out2.length)
781 	    errx(1, "prf len mismatch");
782 	if (memcmp(out1.value, out2.value, out1.length) != 0)
783 	    errx(1, "prf data mismatch");
784 
785 	gss_release_buffer(&min_stat, &out1);
786 	gss_release_buffer(&min_stat, &out2);
787 
788 	in.value = "bar";
789 	in.length = 3;
790 
791 	gss_pseudo_random(&min_stat, sctx, GSS_C_PRF_KEY_PARTIAL, &in,
792 			  100, &out1);
793 	gss_pseudo_random(&min_stat, cctx, GSS_C_PRF_KEY_PARTIAL, &in,
794 			  100, &out2);
795 
796 	if (out1.length != out2.length)
797 	    errx(1, "prf len mismatch");
798 	if (memcmp(out1.value, out2.value, out1.length) != 0)
799 	    errx(1, "prf data mismatch");
800 
801 	gss_release_buffer(&min_stat, &out1);
802 	gss_release_buffer(&min_stat, &out2);
803 
804 	wrapunwrap_flag = 1;
805 	getverifymic_flag = 1;
806     }
807 
808     if (wrapunwrap_flag) {
809 	wrapunwrap(cctx, sctx, 0, actual_mech);
810 	wrapunwrap(cctx, sctx, 1, actual_mech);
811 	wrapunwrap(sctx, cctx, 0, actual_mech);
812 	wrapunwrap(sctx, cctx, 1, actual_mech);
813     }
814 
815     if (iov_flag) {
816 	wrapunwrap_iov(cctx, sctx, 0, actual_mech);
817 	wrapunwrap_iov(cctx, sctx, USE_HEADER_ONLY|FORCE_IOV, actual_mech);
818 	wrapunwrap_iov(cctx, sctx, USE_HEADER_ONLY, actual_mech);
819 	wrapunwrap_iov(cctx, sctx, USE_CONF, actual_mech);
820 	wrapunwrap_iov(cctx, sctx, USE_CONF|USE_HEADER_ONLY, actual_mech);
821 
822 	wrapunwrap_iov(cctx, sctx, FORCE_IOV, actual_mech);
823 	wrapunwrap_iov(cctx, sctx, USE_CONF|FORCE_IOV, actual_mech);
824 	wrapunwrap_iov(cctx, sctx, USE_HEADER_ONLY|FORCE_IOV, actual_mech);
825 	wrapunwrap_iov(cctx, sctx, USE_CONF|USE_HEADER_ONLY|FORCE_IOV, actual_mech);
826 
827 	wrapunwrap_iov(cctx, sctx, USE_SIGN_ONLY|FORCE_IOV, actual_mech);
828 	wrapunwrap_iov(cctx, sctx, USE_CONF|USE_SIGN_ONLY|FORCE_IOV, actual_mech);
829 	wrapunwrap_iov(cctx, sctx, USE_CONF|USE_HEADER_ONLY|USE_SIGN_ONLY|FORCE_IOV, actual_mech);
830 
831 /* works */
832 	wrapunwrap_iov(cctx, sctx, 0, actual_mech);
833 	wrapunwrap_iov(cctx, sctx, FORCE_IOV, actual_mech);
834 
835 	wrapunwrap_iov(cctx, sctx, USE_CONF, actual_mech);
836 	wrapunwrap_iov(cctx, sctx, USE_CONF|FORCE_IOV, actual_mech);
837 
838 	wrapunwrap_iov(cctx, sctx, USE_SIGN_ONLY, actual_mech);
839 	wrapunwrap_iov(cctx, sctx, USE_SIGN_ONLY|FORCE_IOV, actual_mech);
840 
841 	wrapunwrap_iov(cctx, sctx, USE_CONF|USE_SIGN_ONLY, actual_mech);
842 	wrapunwrap_iov(cctx, sctx, USE_CONF|USE_SIGN_ONLY|FORCE_IOV, actual_mech);
843 
844 	wrapunwrap_iov(cctx, sctx, USE_HEADER_ONLY, actual_mech);
845 	wrapunwrap_iov(cctx, sctx, USE_HEADER_ONLY|FORCE_IOV, actual_mech);
846 
847 	wrapunwrap_iov(cctx, sctx, USE_CONF|USE_HEADER_ONLY, actual_mech);
848 	wrapunwrap_iov(cctx, sctx, USE_CONF|USE_HEADER_ONLY|FORCE_IOV, actual_mech);
849     }
850 
851     if (getverifymic_flag) {
852 	getverifymic(cctx, sctx, actual_mech);
853 	getverifymic(cctx, sctx, actual_mech);
854 	getverifymic(sctx, cctx, actual_mech);
855 	getverifymic(sctx, cctx, actual_mech);
856     }
857 
858 
859     gss_delete_sec_context(&min_stat, &cctx, NULL);
860     gss_delete_sec_context(&min_stat, &sctx, NULL);
861 
862     if (deleg_cred != GSS_C_NO_CREDENTIAL) {
863 	gss_cred_id_t cred2 = GSS_C_NO_CREDENTIAL;
864 	gss_buffer_desc cb;
865 
866 	if (verbose_flag)
867 	    printf("checking actual mech (%s) on delegated cred\n",
868 		   oid_to_string(actual_mech));
869 	loop(actual_mech, nameoid, argv[0], deleg_cred, &sctx, &cctx, &actual_mech2, &cred2);
870 
871 	gss_delete_sec_context(&min_stat, &cctx, NULL);
872 	gss_delete_sec_context(&min_stat, &sctx, NULL);
873 
874 	gss_release_cred(&min_stat, &cred2);
875 
876 	/* try again using SPNEGO */
877 	if (verbose_flag)
878 	    printf("checking spnego on delegated cred\n");
879 	loop(GSS_SPNEGO_MECHANISM, nameoid, argv[0], deleg_cred, &sctx, &cctx,
880 	     &actual_mech2, &cred2);
881 
882 	gss_delete_sec_context(&min_stat, &cctx, NULL);
883 	gss_delete_sec_context(&min_stat, &sctx, NULL);
884 
885 	gss_release_cred(&min_stat, &cred2);
886 
887 	/* check export/import */
888 	if (ei_flag) {
889 
890 	    maj_stat = gss_export_cred(&min_stat, deleg_cred, &cb);
891 	    if (maj_stat != GSS_S_COMPLETE)
892 		errx(1, "export failed: %s",
893 		     gssapi_err(maj_stat, min_stat, NULL));
894 
895 	    maj_stat = gss_import_cred(&min_stat, &cb, &cred2);
896 	    if (maj_stat != GSS_S_COMPLETE)
897 		errx(1, "import failed: %s",
898 		     gssapi_err(maj_stat, min_stat, NULL));
899 
900 	    gss_release_buffer(&min_stat, &cb);
901 	    gss_release_cred(&min_stat, &deleg_cred);
902 
903 	    if (verbose_flag)
904 		printf("checking actual mech (%s) on export/imported cred\n",
905 		       oid_to_string(actual_mech));
906 	    loop(actual_mech, nameoid, argv[0], cred2, &sctx, &cctx,
907 		 &actual_mech2, &deleg_cred);
908 
909 	    gss_release_cred(&min_stat, &deleg_cred);
910 
911 	    gss_delete_sec_context(&min_stat, &cctx, NULL);
912 	    gss_delete_sec_context(&min_stat, &sctx, NULL);
913 
914 	    /* try again using SPNEGO */
915 	    if (verbose_flag)
916 		printf("checking SPNEGO on export/imported cred\n");
917 	    loop(GSS_SPNEGO_MECHANISM, nameoid, argv[0], cred2, &sctx, &cctx,
918 		 &actual_mech2, &deleg_cred);
919 
920 	    gss_release_cred(&min_stat, &deleg_cred);
921 
922 	    gss_delete_sec_context(&min_stat, &cctx, NULL);
923 	    gss_delete_sec_context(&min_stat, &sctx, NULL);
924 
925 	    gss_release_cred(&min_stat, &cred2);
926 
927 	} else  {
928 	    gss_release_cred(&min_stat, &deleg_cred);
929 	}
930 
931     }
932 
933     empty_release();
934 
935     krb5_free_context(context);
936 
937     return 0;
938 }
939