1 /*
2  * Copyright (c) 2006 Kungliga Tekniska H�gskolan
3  * (Royal Institute of Technology, Stockholm, Sweden).
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * 3. Neither the name of KTH nor the names of its contributors may be
18  *    used to endorse or promote products derived from this software without
19  *    specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY
22  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE
25  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
31  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include <common.h>
35 RCSID("$Id: gssmaestro.c 21605 2007-07-17 06:51:57Z lha $");
36 
37 static FILE *logfile;
38 
39 /*
40  *
41  */
42 
43 struct client {
44     char *name;
45     struct sockaddr *sa;
46     socklen_t salen;
47     krb5_storage *sock;
48     int32_t capabilities;
49     char *target_name;
50     char *moniker;
51     krb5_storage *logsock;
52     int have_log;
53 #ifdef ENABLE_PTHREAD_SUPPORT
54     pthread_t thr;
55 #else
56     pid_t child;
57 #endif
58 };
59 
60 static struct client **clients;
61 static int num_clients;
62 
63 static int
64 init_sec_context(struct client *client,
65 		 int32_t *hContext, int32_t *hCred,
66 		 int32_t flags,
67 		 const char *targetname,
68 		 const krb5_data *itoken, krb5_data *otoken)
69 {
70     int32_t val;
71     krb5_data_zero(otoken);
72     put32(client, eInitContext);
73     put32(client, *hContext);
74     put32(client, *hCred);
75     put32(client, flags);
76     putstring(client, targetname);
77     putdata(client, *itoken);
78     ret32(client, *hContext);
79     ret32(client, val);
80     retdata(client, *otoken);
81     return val;
82 }
83 
84 static int
85 accept_sec_context(struct client *client,
86 		   int32_t *hContext,
87 		   int32_t flags,
88 		   const krb5_data *itoken,
89 		   krb5_data *otoken,
90 		   int32_t *hDelegCred)
91 {
92     int32_t val;
93     krb5_data_zero(otoken);
94     put32(client, eAcceptContext);
95     put32(client, *hContext);
96     put32(client, flags);
97     putdata(client, *itoken);
98     ret32(client, *hContext);
99     ret32(client, val);
100     retdata(client, *otoken);
101     ret32(client, *hDelegCred);
102     return val;
103 }
104 
105 static int
106 acquire_cred(struct client *client,
107 	     const char *username,
108 	     const char *password,
109 	     int32_t flags,
110 	     int32_t *hCred)
111 {
112     int32_t val;
113     put32(client, eAcquireCreds);
114     putstring(client, username);
115     putstring(client, password);
116     put32(client, flags);
117     ret32(client, val);
118     ret32(client, *hCred);
119     return val;
120 }
121 
122 static int
123 toast_resource(struct client *client,
124 	       int32_t hCred)
125 {
126     int32_t val;
127     put32(client, eToastResource);
128     put32(client, hCred);
129     ret32(client, val);
130     return val;
131 }
132 
133 static int
134 goodbye(struct client *client)
135 {
136     put32(client, eGoodBye);
137     return GSMERR_OK;
138 }
139 
140 static int
141 get_targetname(struct client *client,
142 	       char **target)
143 {
144     put32(client, eGetTargetName);
145     retstring(client, *target);
146     return GSMERR_OK;
147 }
148 
149 static int32_t
150 encrypt_token(struct client *client, int32_t hContext, int32_t flags,
151 	   krb5_data *in, krb5_data *out)
152 {
153     int32_t val;
154     put32(client, eEncrypt);
155     put32(client, hContext);
156     put32(client, flags);
157     put32(client, 0);
158     putdata(client, *in);
159     ret32(client, val);
160     retdata(client, *out);
161     return val;
162 }
163 
164 static int32_t
165 decrypt_token(struct client *client, int32_t hContext, int flags,
166 	     krb5_data *in, krb5_data *out)
167 {
168     int32_t val;
169     put32(client, eDecrypt);
170     put32(client, hContext);
171     put32(client, flags);
172     put32(client, 0);
173     putdata(client, *in);
174     ret32(client, val);
175     retdata(client, *out);
176     return val;
177 }
178 
179 static int32_t
180 get_mic(struct client *client, int32_t hContext,
181 	krb5_data *in, krb5_data *mic)
182 {
183     int32_t val;
184     put32(client, eSign);
185     put32(client, hContext);
186     put32(client, 0);
187     put32(client, 0);
188     putdata(client, *in);
189     ret32(client, val);
190     retdata(client, *mic);
191     return val;
192 }
193 
194 static int32_t
195 verify_mic(struct client *client, int32_t hContext,
196 	   krb5_data *in, krb5_data *mic)
197 {
198     int32_t val;
199     put32(client, eVerify);
200     put32(client, hContext);
201     put32(client, 0);
202     put32(client, 0);
203     putdata(client, *in);
204     putdata(client, *mic);
205     ret32(client, val);
206     return val;
207 }
208 
209 
210 static int32_t
211 get_version_capa(struct client *client,
212 		 int32_t *version, int32_t *capa,
213 		 char **version_str)
214 {
215     put32(client, eGetVersionAndCapabilities);
216     ret32(client, *version);
217     ret32(client, *capa);
218     retstring(client, *version_str);
219     return GSMERR_OK;
220 }
221 
222 static int32_t
223 get_moniker(struct client *client,
224 	    char **moniker)
225 {
226     put32(client, eGetMoniker);
227     retstring(client, *moniker);
228     return GSMERR_OK;
229 }
230 
231 static int
232 wait_log(struct client *c)
233 {
234     int32_t port;
235     struct sockaddr_storage sast;
236     socklen_t salen = sizeof(sast);
237     int fd, fd2, ret;
238 
239     memset(&sast, 0, sizeof(sast));
240 
241     assert(sizeof(sast) >= c->salen);
242 
243     fd = socket(c->sa->sa_family, SOCK_STREAM, 0);
244     if (fd < 0)
245 	err(1, "failed to build socket for %s's logging port", c->moniker);
246 
247     ((struct sockaddr *)&sast)->sa_family = c->sa->sa_family;
248     ret = bind(fd, (struct sockaddr *)&sast, c->salen);
249     if (ret < 0)
250 	err(1, "failed to bind %s's logging port", c->moniker);
251 
252     if (listen(fd, SOMAXCONN) < 0)
253 	err(1, "failed to listen %s's logging port", c->moniker);
254 
255     salen = sizeof(sast);
256     ret = getsockname(fd, (struct sockaddr *)&sast, &salen);
257     if (ret < 0)
258 	err(1, "failed to get address of local socket for %s", c->moniker);
259 
260     port = socket_get_port((struct sockaddr *)&sast);
261 
262     put32(c, eSetLoggingSocket);
263     put32(c, ntohs(port));
264 
265     salen = sizeof(sast);
266     fd2 = accept(fd, (struct sockaddr *)&sast, &salen);
267     if (fd2 < 0)
268 	err(1, "failed to accept local socket for %s", c->moniker);
269     close(fd);
270 
271     return fd2;
272 }
273 
274 
275 
276 
277 static int
278 build_context(struct client *ipeer, struct client *apeer,
279 	      int32_t flags, int32_t hCred,
280 	      int32_t *iContext, int32_t *aContext, int32_t *hDelegCred)
281 {
282     int32_t val = GSMERR_ERROR, ic = 0, ac = 0, deleg = 0;
283     krb5_data itoken, otoken;
284     int iDone = 0, aDone = 0;
285     int step = 0;
286     int first_call = 0x80;
287 
288     if (apeer->target_name == NULL)
289 	errx(1, "apeer %s have no target name", apeer->name);
290 
291     krb5_data_zero(&itoken);
292 
293     while (!iDone || !aDone) {
294 
295 	if (iDone) {
296 	    warnx("iPeer already done, aPeer want extra rtt");
297 	    val = GSMERR_ERROR;
298 	    goto out;
299 	}
300 
301 	val = init_sec_context(ipeer, &ic, &hCred, flags|first_call,
302 			       apeer->target_name, &itoken, &otoken);
303 	step++;
304 	switch(val) {
305 	case GSMERR_OK:
306 	    iDone = 1;
307 	    if (aDone)
308 		continue;
309 	    break;
310 	case GSMERR_CONTINUE_NEEDED:
311 	    break;
312 	default:
313 	    warnx("iPeer %s failed with %d (step %d)",
314 		  ipeer->name, (int)val, step);
315 	    goto out;
316 	}
317 
318 	if (aDone) {
319 	    warnx("aPeer already done, iPeer want extra rtt");
320 	    val = GSMERR_ERROR;
321 	    goto out;
322 	}
323 
324 	val = accept_sec_context(apeer, &ac, flags|first_call,
325 				 &otoken, &itoken, &deleg);
326 	step++;
327 	switch(val) {
328 	case GSMERR_OK:
329 	    aDone = 1;
330 	    if (iDone)
331 		continue;
332 	    break;
333 	case GSMERR_CONTINUE_NEEDED:
334 	    break;
335 	default:
336 	    warnx("aPeer %s failed with %d (step %d)",
337 		 apeer->name, (int)val, step);
338 	    val = GSMERR_ERROR;
339 	    goto out;
340 	}
341 	first_call = 0;
342 	val = GSMERR_OK;
343     }
344 
345     if (iContext == NULL || val != GSMERR_OK) {
346 	if (ic)
347 	    toast_resource(ipeer, ic);
348 	if (iContext)
349 	    *iContext = 0;
350     } else
351 	*iContext = ic;
352 
353     if (aContext == NULL || val != GSMERR_OK) {
354 	if (ac)
355 	    toast_resource(apeer, ac);
356 	if (aContext)
357 	    *aContext = 0;
358     } else
359 	*aContext = ac;
360 
361     if (hDelegCred == NULL || val != GSMERR_OK) {
362 	if (deleg)
363 	    toast_resource(apeer, deleg);
364 	if (hDelegCred)
365 	    *hDelegCred = 0;
366     } else
367 	*hDelegCred = deleg;
368 
369 out:
370     return val;
371 }
372 
373 static void
374 test_mic(struct client *c1, int32_t hc1, struct client *c2, int32_t hc2)
375 {
376     krb5_data msg, mic;
377     int32_t val;
378 
379     msg.data = "foo";
380     msg.length = 3;
381 
382     krb5_data_zero(&mic);
383 
384     val = get_mic(c1, hc1, &msg, &mic);
385     if (val)
386 	errx(1, "get_mic failed to host: %s", c1->moniker);
387     val = verify_mic(c2, hc2, &msg, &mic);
388     if (val)
389 	errx(1, "verify_mic failed to host: %s", c2->moniker);
390 
391     krb5_data_free(&mic);
392 }
393 
394 static int32_t
395 test_wrap(struct client *c1, int32_t hc1, struct client *c2, int32_t hc2,
396 	  int conf)
397 {
398     krb5_data msg, wrapped, out;
399     int32_t val;
400 
401     msg.data = "foo";
402     msg.length = 3;
403 
404     krb5_data_zero(&wrapped);
405     krb5_data_zero(&out);
406 
407     val = encrypt_token(c1, hc1, conf, &msg, &wrapped);
408     if (val) {
409 	warnx("encrypt_token failed to host: %s", c1->moniker);
410 	return val;
411     }
412     val = decrypt_token(c2, hc2, conf, &wrapped, &out);
413     if (val) {
414 	krb5_data_free(&wrapped);
415 	warnx("decrypt_token failed to host: %s", c2->moniker);
416 	return val;
417     }
418 
419     if (msg.length != out.length) {
420 	warnx("decrypted'ed token have wrong length (%lu != %lu)",
421 	      (unsigned long)msg.length, (unsigned long)out.length);
422 	val = GSMERR_ERROR;
423     } else if (memcmp(msg.data, out.data, msg.length) != 0) {
424 	warnx("decryptd'ed token have wrong data");
425 	val = GSMERR_ERROR;
426     }
427 
428     krb5_data_free(&wrapped);
429     krb5_data_free(&out);
430     return val;
431 }
432 
433 static int32_t
434 test_token(struct client *c1, int32_t hc1, struct client *c2, int32_t hc2)
435 {
436     int32_t val;
437     int i;
438 
439     for (i = 0; i < 10; i++) {
440 	test_mic(c1, hc1, c2, hc2);
441 	test_mic(c2, hc2, c1, hc1);
442 	val = test_wrap(c1, hc1, c2, hc2, 0);
443 	if (val) return val;
444 	val = test_wrap(c2, hc2, c1, hc1, 0);
445 	if (val) return val;
446 	val = test_wrap(c1, hc1, c2, hc2, 1);
447 	if (val) return val;
448 	val = test_wrap(c2, hc2, c1, hc1, 1);
449 	if (val) return val;
450     }
451     return GSMERR_OK;
452 }
453 
454 static int
455 log_function(void *ptr)
456 {
457     struct client *c = ptr;
458     int32_t cmd, line;
459     char *file, *string;
460 
461     while (1) {
462         if (krb5_ret_int32(c->logsock, &cmd))
463 	    goto out;
464 
465 	switch (cmd) {
466 	case eLogSetMoniker:
467 	    if (krb5_ret_string(c->logsock, &file))
468 		goto out;
469 	    free(file);
470 	    break;
471 	case eLogInfo:
472 	case eLogFailure:
473 	    if (krb5_ret_string(c->logsock, &file))
474 		goto out;
475 	    if (krb5_ret_int32(c->logsock, &line))
476 		goto out;
477 	    if (krb5_ret_string(c->logsock, &string))
478 		goto out;
479 	    printf("%s:%lu: %s\n",
480 		   file, (unsigned long)line, string);
481 	    fprintf(logfile, "%s:%lu: %s\n",
482 		    file, (unsigned long)line, string);
483 	    fflush(logfile);
484 	    free(file);
485 	    free(string);
486 	    if (krb5_store_int32(c->logsock, 0))
487 		goto out;
488 	    break;
489 	default:
490 	    errx(1, "client send bad log command: %d", (int)cmd);
491 	}
492     }
493 out:
494 
495     return 0;
496 }
497 
498 static void
499 connect_client(const char *slave)
500 {
501     char *name, *port;
502     struct client *c = ecalloc(1, sizeof(*c));
503     struct addrinfo hints, *res0, *res;
504     int ret, fd;
505 
506     name = estrdup(slave);
507     port = strchr(name, ':');
508     if (port == NULL)
509 	errx(1, "port missing from %s", name);
510     *port++ = 0;
511 
512     c->name = estrdup(slave);
513 
514     memset(&hints, 0, sizeof(hints));
515     hints.ai_family = PF_UNSPEC;
516     hints.ai_socktype = SOCK_STREAM;
517 
518     ret = getaddrinfo(name, port, &hints, &res0);
519     if (ret)
520 	errx(1, "error resolving %s", name);
521 
522     for (res = res0, fd = -1; res; res = res->ai_next) {
523 	fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
524 	if (fd < 0)
525 	    continue;
526 	if (connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
527 	    close(fd);
528 	    fd = -1;
529 	    continue;
530 	}
531 	c->sa = ecalloc(1, res->ai_addrlen);
532 	memcpy(c->sa, res->ai_addr, res->ai_addrlen);
533 	c->salen = res->ai_addrlen;
534 	break;  /* okay we got one */
535     }
536     if (fd < 0)
537 	err(1, "connect to host: %s", name);
538     freeaddrinfo(res);
539 
540     c->sock = krb5_storage_from_fd(fd);
541     close(fd);
542     if (c->sock == NULL)
543 	errx(1, "krb5_storage_from_fd");
544 
545     {
546 	int32_t version;
547 	char *str = NULL;
548 	get_version_capa(c, &version, &c->capabilities, &str);
549 	if (str) {
550 	    free(str);
551 	}
552 	if (c->capabilities & HAS_MONIKER)
553 	    get_moniker(c, &c->moniker);
554 	else
555 	    c->moniker = c->name;
556 	if (c->capabilities & ISSERVER)
557 	    get_targetname(c, &c->target_name);
558     }
559 
560     if (logfile) {
561 	int fd;
562 
563 	printf("starting log socket to client %s\n", c->moniker);
564 
565 	fd = wait_log(c);
566 
567 	c->logsock = krb5_storage_from_fd(fd);
568 	close(fd);
569 	if (c->logsock == NULL)
570 	    errx(1, "failed to create log krb5_storage");
571 #ifdef ENABLE_PTHREAD_SUPPORT
572 	pthread_create(&c->thr, NULL, log_function, c);
573 #else
574 	c->child = fork();
575 	if (c->child == -1)
576 	    errx(1, "failed to fork");
577 	else if (c->child == 0) {
578 	    log_function(c);
579 	    fclose(logfile);
580 	    exit(0);
581 	}
582 #endif
583    }
584 
585 
586     clients = erealloc(clients, (num_clients + 1) * sizeof(*clients));
587 
588     clients[num_clients] = c;
589     num_clients++;
590 
591     free(name);
592 }
593 
594 static struct client *
595 get_client(const char *slave)
596 {
597     size_t i;
598     for (i = 0; i < num_clients; i++)
599 	if (strcmp(slave, clients[i]->name) == 0)
600 	    return clients[i];
601     errx(1, "failed to find client %s", slave);
602 }
603 
604 /*
605  *
606  */
607 
608 static int version_flag;
609 static int help_flag;
610 static char *logfile_str;
611 static getarg_strings principals;
612 static getarg_strings slaves;
613 
614 struct getargs args[] = {
615     { "principals", 0,  arg_strings,	&principals,	"Test principal",
616       NULL },
617     { "slaves", 0,  arg_strings,	&slaves,	"Slaves",
618       NULL },
619     { "log-file", 0, arg_string,	&logfile_str,	"Logfile",
620       NULL },
621     { "version", 0,  arg_flag,		&version_flag,	"Print version",
622       NULL },
623     { "help",	 0,  arg_flag,		&help_flag,	NULL,
624       NULL }
625 };
626 
627 static void
628 usage(int ret)
629 {
630     arg_printusage (args,
631 		    sizeof(args) / sizeof(args[0]),
632 		    NULL,
633 		    "");
634     exit (ret);
635 }
636 
637 int
638 main(int argc, char **argv)
639 {
640     int optidx= 0;
641     char *user;
642     char *password;
643     char ***list, **p;
644     size_t num_list, i, j, k;
645     int failed = 0;
646 
647     setprogname (argv[0]);
648 
649     if (getarg (args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
650 	usage (1);
651 
652     if (help_flag)
653 	usage (0);
654 
655     if (version_flag) {
656 	print_version (NULL);
657 	return 0;
658     }
659 
660     if (optidx != argc)
661 	usage (1);
662 
663     if (principals.num_strings == 0)
664 	errx(1, "no principals");
665 
666     user = estrdup(principals.strings[0]);
667     password = strchr(user, ':');
668     if (password == NULL)
669 	errx(1, "password missing from %s", user);
670     *password++ = 0;
671 
672     if (slaves.num_strings == 0)
673 	errx(1, "no principals");
674 
675     if (logfile_str) {
676 	printf("open logfile %s\n", logfile_str);
677 	logfile = fopen(logfile_str, "w+");
678 	if (logfile == NULL)
679 	    err(1, "failed to open: %s", logfile_str);
680     }
681 
682     /*
683      *
684      */
685 
686     list = permutate_all(&slaves, &num_list);
687 
688     /*
689      * Set up connection to all clients
690      */
691 
692     printf("Connecting to slaves\n");
693     for (i = 0; i < slaves.num_strings; i++)
694 	connect_client(slaves.strings[i]);
695 
696     /*
697      * Test acquire credentials
698      */
699 
700     printf("Test acquire credentials\n");
701     for (i = 0; i < slaves.num_strings; i++) {
702 	int32_t hCred, val;
703 
704 	val = acquire_cred(clients[i], user, password, 1, &hCred);
705 	if (val != GSMERR_OK) {
706 	    warnx("Failed to acquire_cred on host %s: %d",
707 		 clients[i]->moniker, (int)val);
708 	    failed = 1;
709 	} else
710 	    toast_resource(clients[i], hCred);
711     }
712 
713     if (failed)
714 	goto out;
715 
716     /*
717      * First test if all slaves can build context to them-self.
718      */
719 
720     printf("Self context tests\n");
721     for (i = 0; i < num_clients; i++) {
722 	int32_t hCred, val, delegCred;
723 	int32_t clientC, serverC;
724 	struct client *c = clients[i];
725 
726 	if (c->target_name == NULL)
727 	    continue;
728 
729 	printf("%s connects to self using %s\n",
730 	       c->moniker, c->target_name);
731 
732 	val = acquire_cred(c, user, password, 1, &hCred);
733 	if (val != GSMERR_OK)
734 	    errx(1, "failed to acquire_cred: %d", (int)val);
735 
736 	val = build_context(c, c,
737 			    GSS_C_REPLAY_FLAG|GSS_C_SEQUENCE_FLAG|
738 			    GSS_C_INTEG_FLAG|GSS_C_CONF_FLAG|
739 			    GSS_C_DELEG_FLAG|GSS_C_MUTUAL_FLAG,
740 			    hCred, &clientC, &serverC, &delegCred);
741 	if (val == GSMERR_OK) {
742 	    test_token(c, clientC, c, serverC);
743 	    toast_resource(c, clientC);
744 	    toast_resource(c, serverC);
745 	    if (delegCred)
746 		toast_resource(c, delegCred);
747 	} else {
748 	    warnx("build_context failed: %d", (int)val);
749 	}
750 	/*
751 	 *
752 	 */
753 
754 	val = build_context(c, c,
755 			    GSS_C_INTEG_FLAG|GSS_C_CONF_FLAG,
756 			    hCred, &clientC, &serverC, &delegCred);
757 	if (val == GSMERR_OK) {
758 	    test_token(c, clientC, c, serverC);
759 	    toast_resource(c, clientC);
760 	    toast_resource(c, serverC);
761 	    if (delegCred)
762 		toast_resource(c, delegCred);
763 	} else {
764 	    warnx("build_context failed: %d", (int)val);
765 	}
766 
767 	toast_resource(c, hCred);
768     }
769     /*
770      * Build contexts though all entries in each lists, including the
771      * step from the last entry to the first, ie treat the list as a
772      * circle.
773      *
774      * Only follow the delegated credential, but test "all"
775      * flags. (XXX only do deleg|mutual right now.
776      */
777 
778     printf("\"All\" permutation tests\n");
779 
780     for (i = 0; i < num_list; i++) {
781 	int32_t hCred, val, delegCred = 0;
782 	int32_t clientC = 0, serverC = 0;
783 	struct client *client, *server;
784 
785 	p = list[i];
786 
787 	client = get_client(p[0]);
788 
789 	val = acquire_cred(client, user, password, 1, &hCred);
790 	if (val != GSMERR_OK)
791 	    errx(1, "failed to acquire_cred: %d", (int)val);
792 
793 	for (j = 1; j < num_clients + 1; j++) {
794 	    server = get_client(p[j % num_clients]);
795 
796 	    if (server->target_name == NULL)
797 		break;
798 
799 	    for (k = 1; k < j; k++)
800 		printf("\t");
801 	    printf("%s -> %s\n", client->moniker, server->moniker);
802 
803 	    val = build_context(client, server,
804 				GSS_C_REPLAY_FLAG|GSS_C_SEQUENCE_FLAG|
805 				GSS_C_INTEG_FLAG|GSS_C_CONF_FLAG|
806 				GSS_C_DELEG_FLAG|GSS_C_MUTUAL_FLAG,
807 				hCred, &clientC, &serverC, &delegCred);
808 	    if (val != GSMERR_OK) {
809 		warnx("build_context failed: %d", (int)val);
810 		break;
811 	    }
812 
813 	    val = test_token(client, clientC, server, serverC);
814 	    if (val)
815 		break;
816 
817 	    toast_resource(client, clientC);
818 	    toast_resource(server, serverC);
819 	    if (!delegCred) {
820 		warnx("no delegated cred on %s", server->moniker);
821 		break;
822 	    }
823 	    toast_resource(client, hCred);
824 	    hCred = delegCred;
825 	    client = server;
826 	}
827 	if (hCred)
828 	    toast_resource(client, hCred);
829     }
830 
831     /*
832      * Close all connections to clients
833      */
834 
835 out:
836     printf("sending goodbye and waiting for log sockets\n");
837     for (i = 0; i < num_clients; i++) {
838 	goodbye(clients[i]);
839 	if (clients[i]->logsock) {
840 #ifdef ENABLE_PTHREAD_SUPPORT
841 	    pthread_join(&clients[i]->thr, NULL);
842 #else
843 	    waitpid(clients[i]->child, NULL, 0);
844 #endif
845 	}
846     }
847 
848     printf("done\n");
849 
850     return 0;
851 }
852