xref: /freebsd/crypto/heimdal/appl/gssmask/gssmaestro.c (revision f4b37ed0f8b307b1f3f0f630ca725d68f1dff30d)
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$");
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 wrap_token_ext(struct client *client, int32_t hContext, int32_t flags,
181 	       int32_t bflags, krb5_data *header, krb5_data *in, krb5_data *trailer,
182 	       krb5_data *out)
183 {
184     int32_t val;
185     put32(client, eWrapExt);
186     put32(client, hContext);
187     put32(client, flags);
188     put32(client, bflags);
189     putdata(client, *header);
190     putdata(client, *in);
191     putdata(client, *trailer);
192     ret32(client, val);
193     retdata(client, *out);
194     return val;
195 }
196 
197 static int32_t
198 unwrap_token_ext(struct client *client, int32_t hContext, int32_t flags,
199 	       int32_t bflags, krb5_data *header, krb5_data *in, krb5_data *trailer,
200 	       krb5_data *out)
201 {
202     int32_t val;
203     put32(client, eUnwrapExt);
204     put32(client, hContext);
205     put32(client, flags);
206     put32(client, bflags);
207     putdata(client, *header);
208     putdata(client, *in);
209     putdata(client, *trailer);
210     ret32(client, val);
211     retdata(client, *out);
212     return val;
213 }
214 
215 static int32_t
216 get_mic(struct client *client, int32_t hContext,
217 	krb5_data *in, krb5_data *mic)
218 {
219     int32_t val;
220     put32(client, eSign);
221     put32(client, hContext);
222     put32(client, 0);
223     put32(client, 0);
224     putdata(client, *in);
225     ret32(client, val);
226     retdata(client, *mic);
227     return val;
228 }
229 
230 static int32_t
231 verify_mic(struct client *client, int32_t hContext,
232 	   krb5_data *in, krb5_data *mic)
233 {
234     int32_t val;
235     put32(client, eVerify);
236     put32(client, hContext);
237     put32(client, 0);
238     put32(client, 0);
239     putdata(client, *in);
240     putdata(client, *mic);
241     ret32(client, val);
242     return val;
243 }
244 
245 
246 static int32_t
247 get_version_capa(struct client *client,
248 		 int32_t *version, int32_t *capa,
249 		 char **version_str)
250 {
251     put32(client, eGetVersionAndCapabilities);
252     ret32(client, *version);
253     ret32(client, *capa);
254     retstring(client, *version_str);
255     return GSMERR_OK;
256 }
257 
258 static int32_t
259 get_moniker(struct client *client,
260 	    char **moniker)
261 {
262     put32(client, eGetMoniker);
263     retstring(client, *moniker);
264     return GSMERR_OK;
265 }
266 
267 static int
268 wait_log(struct client *c)
269 {
270     int32_t port;
271     struct sockaddr_storage sast;
272     socklen_t salen = sizeof(sast);
273     int fd, fd2, ret;
274 
275     memset(&sast, 0, sizeof(sast));
276 
277     assert(sizeof(sast) >= c->salen);
278 
279     fd = socket(c->sa->sa_family, SOCK_STREAM, 0);
280     if (fd < 0)
281 	err(1, "failed to build socket for %s's logging port", c->moniker);
282 
283     ((struct sockaddr *)&sast)->sa_family = c->sa->sa_family;
284     ret = bind(fd, (struct sockaddr *)&sast, c->salen);
285     if (ret < 0)
286 	err(1, "failed to bind %s's logging port", c->moniker);
287 
288     if (listen(fd, SOMAXCONN) < 0)
289 	err(1, "failed to listen %s's logging port", c->moniker);
290 
291     salen = sizeof(sast);
292     ret = getsockname(fd, (struct sockaddr *)&sast, &salen);
293     if (ret < 0)
294 	err(1, "failed to get address of local socket for %s", c->moniker);
295 
296     port = socket_get_port((struct sockaddr *)&sast);
297 
298     put32(c, eSetLoggingSocket);
299     put32(c, ntohs(port));
300 
301     salen = sizeof(sast);
302     fd2 = accept(fd, (struct sockaddr *)&sast, &salen);
303     if (fd2 < 0)
304 	err(1, "failed to accept local socket for %s", c->moniker);
305     close(fd);
306 
307     return fd2;
308 }
309 
310 
311 
312 
313 static int
314 build_context(struct client *ipeer, struct client *apeer,
315 	      int32_t flags, int32_t hCred,
316 	      int32_t *iContext, int32_t *aContext, int32_t *hDelegCred)
317 {
318     int32_t val = GSMERR_ERROR, ic = 0, ac = 0, deleg = 0;
319     krb5_data itoken, otoken;
320     int iDone = 0, aDone = 0;
321     int step = 0;
322     int first_call = 0x80;
323 
324     if (apeer->target_name == NULL)
325 	errx(1, "apeer %s have no target name", apeer->name);
326 
327     krb5_data_zero(&itoken);
328 
329     while (!iDone || !aDone) {
330 
331 	if (iDone) {
332 	    warnx("iPeer already done, aPeer want extra rtt");
333 	    val = GSMERR_ERROR;
334 	    goto out;
335 	}
336 
337 	val = init_sec_context(ipeer, &ic, &hCred, flags|first_call,
338 			       apeer->target_name, &itoken, &otoken);
339 	step++;
340 	switch(val) {
341 	case GSMERR_OK:
342 	    iDone = 1;
343 	    if (aDone)
344 		continue;
345 	    break;
346 	case GSMERR_CONTINUE_NEEDED:
347 	    break;
348 	default:
349 	    warnx("iPeer %s failed with %d (step %d)",
350 		  ipeer->name, (int)val, step);
351 	    goto out;
352 	}
353 
354 	if (aDone) {
355 	    warnx("aPeer already done, iPeer want extra rtt");
356 	    val = GSMERR_ERROR;
357 	    goto out;
358 	}
359 
360 	val = accept_sec_context(apeer, &ac, flags|first_call,
361 				 &otoken, &itoken, &deleg);
362 	step++;
363 	switch(val) {
364 	case GSMERR_OK:
365 	    aDone = 1;
366 	    if (iDone)
367 		continue;
368 	    break;
369 	case GSMERR_CONTINUE_NEEDED:
370 	    break;
371 	default:
372 	    warnx("aPeer %s failed with %d (step %d)",
373 		 apeer->name, (int)val, step);
374 	    val = GSMERR_ERROR;
375 	    goto out;
376 	}
377 	first_call = 0;
378 	val = GSMERR_OK;
379     }
380 
381     if (iContext == NULL || val != GSMERR_OK) {
382 	if (ic)
383 	    toast_resource(ipeer, ic);
384 	if (iContext)
385 	    *iContext = 0;
386     } else
387 	*iContext = ic;
388 
389     if (aContext == NULL || val != GSMERR_OK) {
390 	if (ac)
391 	    toast_resource(apeer, ac);
392 	if (aContext)
393 	    *aContext = 0;
394     } else
395 	*aContext = ac;
396 
397     if (hDelegCred == NULL || val != GSMERR_OK) {
398 	if (deleg)
399 	    toast_resource(apeer, deleg);
400 	if (hDelegCred)
401 	    *hDelegCred = 0;
402     } else
403 	*hDelegCred = deleg;
404 
405 out:
406     return val;
407 }
408 
409 static void
410 test_mic(struct client *c1, int32_t hc1, struct client *c2, int32_t hc2)
411 {
412     krb5_data msg, mic;
413     int32_t val;
414 
415     msg.data = "foo";
416     msg.length = 3;
417 
418     krb5_data_zero(&mic);
419 
420     val = get_mic(c1, hc1, &msg, &mic);
421     if (val)
422 	errx(1, "get_mic failed to host: %s", c1->moniker);
423     val = verify_mic(c2, hc2, &msg, &mic);
424     if (val)
425 	errx(1, "verify_mic failed to host: %s", c2->moniker);
426 
427     krb5_data_free(&mic);
428 }
429 
430 static int32_t
431 test_wrap(struct client *c1, int32_t hc1, struct client *c2, int32_t hc2,
432 	  int conf)
433 {
434     krb5_data msg, wrapped, out;
435     int32_t val;
436 
437     msg.data = "foo";
438     msg.length = 3;
439 
440     krb5_data_zero(&wrapped);
441     krb5_data_zero(&out);
442 
443     val = encrypt_token(c1, hc1, conf, &msg, &wrapped);
444     if (val) {
445 	warnx("encrypt_token failed to host: %s", c1->moniker);
446 	return val;
447     }
448     val = decrypt_token(c2, hc2, conf, &wrapped, &out);
449     if (val) {
450 	krb5_data_free(&wrapped);
451 	warnx("decrypt_token failed to host: %s", c2->moniker);
452 	return val;
453     }
454 
455     if (msg.length != out.length) {
456 	warnx("decrypted'ed token have wrong length (%lu != %lu)",
457 	      (unsigned long)msg.length, (unsigned long)out.length);
458 	val = GSMERR_ERROR;
459     } else if (memcmp(msg.data, out.data, msg.length) != 0) {
460 	warnx("decryptd'ed token have wrong data");
461 	val = GSMERR_ERROR;
462     }
463 
464     krb5_data_free(&wrapped);
465     krb5_data_free(&out);
466     return val;
467 }
468 
469 static int32_t
470 test_wrap_ext(struct client *c1, int32_t hc1, struct client *c2, int32_t hc2,
471 	      int conf, int bflags)
472 {
473     krb5_data header, msg, trailer, wrapped, out;
474     int32_t val;
475 
476     header.data = "header";
477     header.length = 6;
478 
479     msg.data = "0123456789abcdef"; /* padded for most enctypes */
480     msg.length = 32;
481 
482     trailer.data = "trailer";
483     trailer.length = 7;
484 
485     krb5_data_zero(&wrapped);
486     krb5_data_zero(&out);
487 
488     val = wrap_token_ext(c1, hc1, conf, bflags, &header, &msg, &trailer, &wrapped);
489     if (val) {
490 	warnx("encrypt_token failed to host: %s", c1->moniker);
491 	return val;
492     }
493     val = unwrap_token_ext(c2, hc2, conf, bflags, &header, &wrapped, &trailer, &out);
494     if (val) {
495 	krb5_data_free(&wrapped);
496 	warnx("decrypt_token failed to host: %s", c2->moniker);
497 	return val;
498     }
499 
500     if (msg.length != out.length) {
501 	warnx("decrypted'ed token have wrong length (%lu != %lu)",
502 	      (unsigned long)msg.length, (unsigned long)out.length);
503 	val = GSMERR_ERROR;
504     } else if (memcmp(msg.data, out.data, msg.length) != 0) {
505 	warnx("decryptd'ed token have wrong data");
506 	val = GSMERR_ERROR;
507     }
508 
509     krb5_data_free(&wrapped);
510     krb5_data_free(&out);
511     return val;
512 }
513 
514 
515 static int32_t
516 test_token(struct client *c1, int32_t hc1, struct client *c2, int32_t hc2, int wrap_ext)
517 {
518     int32_t val;
519     int i;
520 
521     for (i = 0; i < 10; i++) {
522 	/* mic */
523 	test_mic(c1, hc1, c2, hc2);
524 	test_mic(c2, hc2, c1, hc1);
525 
526 	/* wrap */
527 	val = test_wrap(c1, hc1, c2, hc2, 0);
528 	if (val) return val;
529 	val = test_wrap(c2, hc2, c1, hc1, 0);
530 	if (val) return val;
531 
532 	val = test_wrap(c1, hc1, c2, hc2, 1);
533 	if (val) return val;
534 	val = test_wrap(c2, hc2, c1, hc1, 1);
535 	if (val) return val;
536 
537 	if (wrap_ext) {
538 	    /* wrap ext */
539 	    val = test_wrap_ext(c1, hc1, c2, hc2, 1, 0);
540 	    if (val) return val;
541 	    val = test_wrap_ext(c2, hc2, c1, hc1, 1, 0);
542 	    if (val) return val;
543 
544 	    val = test_wrap_ext(c1, hc1, c2, hc2, 1, 1);
545 	    if (val) return val;
546 	    val = test_wrap_ext(c2, hc2, c1, hc1, 1, 1);
547 	    if (val) return val;
548 
549 	    val = test_wrap_ext(c1, hc1, c2, hc2, 0, 0);
550 	    if (val) return val;
551 	    val = test_wrap_ext(c2, hc2, c1, hc1, 0, 0);
552 	    if (val) return val;
553 
554 	    val = test_wrap_ext(c1, hc1, c2, hc2, 0, 1);
555 	    if (val) return val;
556 	    val = test_wrap_ext(c2, hc2, c1, hc1, 0, 1);
557 	    if (val) return val;
558 	}
559     }
560     return GSMERR_OK;
561 }
562 
563 static int
564 log_function(void *ptr)
565 {
566     struct client *c = ptr;
567     int32_t cmd, line;
568     char *file, *string;
569 
570     while (1) {
571         if (krb5_ret_int32(c->logsock, &cmd))
572 	    goto out;
573 
574 	switch (cmd) {
575 	case eLogSetMoniker:
576 	    if (krb5_ret_string(c->logsock, &file))
577 		goto out;
578 	    free(file);
579 	    break;
580 	case eLogInfo:
581 	case eLogFailure:
582 	    if (krb5_ret_string(c->logsock, &file))
583 		goto out;
584 	    if (krb5_ret_int32(c->logsock, &line))
585 		goto out;
586 	    if (krb5_ret_string(c->logsock, &string))
587 		goto out;
588 	    printf("%s:%lu: %s\n",
589 		   file, (unsigned long)line, string);
590 	    fprintf(logfile, "%s:%lu: %s\n",
591 		    file, (unsigned long)line, string);
592 	    fflush(logfile);
593 	    free(file);
594 	    free(string);
595 	    if (krb5_store_int32(c->logsock, 0))
596 		goto out;
597 	    break;
598 	default:
599 	    errx(1, "client send bad log command: %d", (int)cmd);
600 	}
601     }
602 out:
603 
604     return 0;
605 }
606 
607 static void
608 connect_client(const char *slave)
609 {
610     char *name, *port;
611     struct client *c = ecalloc(1, sizeof(*c));
612     struct addrinfo hints, *res0, *res;
613     int ret, fd;
614 
615     name = estrdup(slave);
616     port = strchr(name, ':');
617     if (port == NULL)
618 	errx(1, "port missing from %s", name);
619     *port++ = 0;
620 
621     c->name = estrdup(slave);
622 
623     memset(&hints, 0, sizeof(hints));
624     hints.ai_family = PF_UNSPEC;
625     hints.ai_socktype = SOCK_STREAM;
626 
627     ret = getaddrinfo(name, port, &hints, &res0);
628     if (ret)
629 	errx(1, "error resolving %s", name);
630 
631     for (res = res0, fd = -1; res; res = res->ai_next) {
632 	fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
633 	if (fd < 0)
634 	    continue;
635 	if (connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
636 	    close(fd);
637 	    fd = -1;
638 	    continue;
639 	}
640 	c->sa = ecalloc(1, res->ai_addrlen);
641 	memcpy(c->sa, res->ai_addr, res->ai_addrlen);
642 	c->salen = res->ai_addrlen;
643 	break;  /* okay we got one */
644     }
645     if (fd < 0)
646 	err(1, "connect to host: %s", name);
647     freeaddrinfo(res);
648 
649     c->sock = krb5_storage_from_fd(fd);
650     close(fd);
651     if (c->sock == NULL)
652 	errx(1, "krb5_storage_from_fd");
653 
654     {
655 	int32_t version;
656 	char *str = NULL;
657 	get_version_capa(c, &version, &c->capabilities, &str);
658 	if (str) {
659 	    free(str);
660 	}
661 	if (c->capabilities & HAS_MONIKER)
662 	    get_moniker(c, &c->moniker);
663 	else
664 	    c->moniker = c->name;
665 	if (c->capabilities & ISSERVER)
666 	    get_targetname(c, &c->target_name);
667     }
668 
669     if (logfile) {
670 	int fd;
671 
672 	printf("starting log socket to client %s\n", c->moniker);
673 
674 	fd = wait_log(c);
675 
676 	c->logsock = krb5_storage_from_fd(fd);
677 	close(fd);
678 	if (c->logsock == NULL)
679 	    errx(1, "failed to create log krb5_storage");
680 #ifdef ENABLE_PTHREAD_SUPPORT
681 	pthread_create(&c->thr, NULL, log_function, c);
682 #else
683 	c->child = fork();
684 	if (c->child == -1)
685 	    errx(1, "failed to fork");
686 	else if (c->child == 0) {
687 	    log_function(c);
688 	    fclose(logfile);
689 	    exit(0);
690 	}
691 #endif
692    }
693 
694 
695     clients = erealloc(clients, (num_clients + 1) * sizeof(*clients));
696 
697     clients[num_clients] = c;
698     num_clients++;
699 
700     free(name);
701 }
702 
703 static struct client *
704 get_client(const char *slave)
705 {
706     size_t i;
707     for (i = 0; i < num_clients; i++)
708 	if (strcmp(slave, clients[i]->name) == 0)
709 	    return clients[i];
710     errx(1, "failed to find client %s", slave);
711 }
712 
713 /*
714  *
715  */
716 
717 static int version_flag;
718 static int help_flag;
719 static int wrap_ext = 0;
720 static char *logfile_str;
721 static getarg_strings principals;
722 static getarg_strings slaves;
723 
724 struct getargs args[] = {
725     { "principals", 0,  arg_strings,	&principals,	"Test principal",
726       NULL },
727     { "slaves", 0,  arg_strings,	&slaves,	"Slaves",
728       NULL },
729     { "log-file", 0, arg_string,	&logfile_str,	"Logfile",
730       NULL },
731     { "wrap-ext", 0,  arg_flag,		&wrap_ext,	"test wrap extended",
732       NULL },
733     { "version", 0,  arg_flag,		&version_flag,	"Print version",
734       NULL },
735     { "help",	 0,  arg_flag,		&help_flag,	NULL,
736       NULL }
737 };
738 
739 static void
740 usage(int ret)
741 {
742     arg_printusage (args,
743 		    sizeof(args) / sizeof(args[0]),
744 		    NULL,
745 		    "");
746     exit (ret);
747 }
748 
749 int
750 main(int argc, char **argv)
751 {
752     int optidx= 0;
753     char *user;
754     char *password;
755     char ***list, **p;
756     size_t num_list, i, j, k;
757     int failed = 0;
758 
759     setprogname (argv[0]);
760 
761     if (getarg (args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
762 	usage (1);
763 
764     if (help_flag)
765 	usage (0);
766 
767     if (version_flag) {
768 	print_version (NULL);
769 	return 0;
770     }
771 
772     if (optidx != argc)
773 	usage (1);
774 
775     if (principals.num_strings == 0)
776 	errx(1, "no principals");
777 
778     user = estrdup(principals.strings[0]);
779     password = strchr(user, ':');
780     if (password == NULL)
781 	errx(1, "password missing from %s", user);
782     *password++ = 0;
783 
784     if (slaves.num_strings == 0)
785 	errx(1, "no principals");
786 
787     if (logfile_str) {
788 	printf("open logfile %s\n", logfile_str);
789 	logfile = fopen(logfile_str, "w+");
790 	if (logfile == NULL)
791 	    err(1, "failed to open: %s", logfile_str);
792     }
793 
794     /*
795      *
796      */
797 
798     list = permutate_all(&slaves, &num_list);
799 
800     /*
801      * Set up connection to all clients
802      */
803 
804     printf("Connecting to slaves\n");
805     for (i = 0; i < slaves.num_strings; i++)
806 	connect_client(slaves.strings[i]);
807 
808     /*
809      * Test acquire credentials
810      */
811 
812     printf("Test acquire credentials\n");
813     for (i = 0; i < slaves.num_strings; i++) {
814 	int32_t hCred, val;
815 
816 	val = acquire_cred(clients[i], user, password, 1, &hCred);
817 	if (val != GSMERR_OK) {
818 	    warnx("Failed to acquire_cred on host %s: %d",
819 		 clients[i]->moniker, (int)val);
820 	    failed = 1;
821 	} else
822 	    toast_resource(clients[i], hCred);
823     }
824 
825     if (failed)
826 	goto out;
827 
828     /*
829      * First test if all slaves can build context to them-self.
830      */
831 
832     printf("Self context tests\n");
833     for (i = 0; i < num_clients; i++) {
834 	int32_t hCred, val, delegCred;
835 	int32_t clientC, serverC;
836 	struct client *c = clients[i];
837 
838 	if (c->target_name == NULL)
839 	    continue;
840 
841 	printf("%s connects to self using %s\n",
842 	       c->moniker, c->target_name);
843 
844 	val = acquire_cred(c, user, password, 1, &hCred);
845 	if (val != GSMERR_OK)
846 	    errx(1, "failed to acquire_cred: %d", (int)val);
847 
848 	val = build_context(c, c,
849 			    GSS_C_REPLAY_FLAG|GSS_C_SEQUENCE_FLAG|
850 			    GSS_C_INTEG_FLAG|GSS_C_CONF_FLAG|
851 			    GSS_C_DELEG_FLAG|GSS_C_MUTUAL_FLAG,
852 			    hCred, &clientC, &serverC, &delegCred);
853 	if (val == GSMERR_OK) {
854 	    test_token(c, clientC, c, serverC, wrap_ext);
855 	    toast_resource(c, clientC);
856 	    toast_resource(c, serverC);
857 	    if (delegCred)
858 		toast_resource(c, delegCred);
859 	} else {
860 	    warnx("build_context failed: %d", (int)val);
861 	}
862 	/*
863 	 *
864 	 */
865 
866 	val = build_context(c, c,
867 			    GSS_C_INTEG_FLAG|GSS_C_CONF_FLAG,
868 			    hCred, &clientC, &serverC, &delegCred);
869 	if (val == GSMERR_OK) {
870 	    test_token(c, clientC, c, serverC, wrap_ext);
871 	    toast_resource(c, clientC);
872 	    toast_resource(c, serverC);
873 	    if (delegCred)
874 		toast_resource(c, delegCred);
875 	} else {
876 	    warnx("build_context failed: %d", (int)val);
877 	}
878 
879 	toast_resource(c, hCred);
880     }
881     /*
882      * Build contexts though all entries in each lists, including the
883      * step from the last entry to the first, ie treat the list as a
884      * circle.
885      *
886      * Only follow the delegated credential, but test "all"
887      * flags. (XXX only do deleg|mutual right now.
888      */
889 
890     printf("\"All\" permutation tests\n");
891 
892     for (i = 0; i < num_list; i++) {
893 	int32_t hCred, val, delegCred = 0;
894 	int32_t clientC = 0, serverC = 0;
895 	struct client *client, *server;
896 
897 	p = list[i];
898 
899 	client = get_client(p[0]);
900 
901 	val = acquire_cred(client, user, password, 1, &hCred);
902 	if (val != GSMERR_OK)
903 	    errx(1, "failed to acquire_cred: %d", (int)val);
904 
905 	for (j = 1; j < num_clients + 1; j++) {
906 	    server = get_client(p[j % num_clients]);
907 
908 	    if (server->target_name == NULL)
909 		break;
910 
911 	    for (k = 1; k < j; k++)
912 		printf("\t");
913 	    printf("%s -> %s\n", client->moniker, server->moniker);
914 
915 	    val = build_context(client, server,
916 				GSS_C_REPLAY_FLAG|GSS_C_SEQUENCE_FLAG|
917 				GSS_C_INTEG_FLAG|GSS_C_CONF_FLAG|
918 				GSS_C_DELEG_FLAG|GSS_C_MUTUAL_FLAG,
919 				hCred, &clientC, &serverC, &delegCred);
920 	    if (val != GSMERR_OK) {
921 		warnx("build_context failed: %d", (int)val);
922 		break;
923 	    }
924 
925 	    val = test_token(client, clientC, server, serverC, wrap_ext);
926 	    if (val)
927 		break;
928 
929 	    toast_resource(client, clientC);
930 	    toast_resource(server, serverC);
931 	    if (!delegCred) {
932 		warnx("no delegated cred on %s", server->moniker);
933 		break;
934 	    }
935 	    toast_resource(client, hCred);
936 	    hCred = delegCred;
937 	    client = server;
938 	}
939 	if (hCred)
940 	    toast_resource(client, hCred);
941     }
942 
943     /*
944      * Close all connections to clients
945      */
946 
947 out:
948     printf("sending goodbye and waiting for log sockets\n");
949     for (i = 0; i < num_clients; i++) {
950 	goodbye(clients[i]);
951 	if (clients[i]->logsock) {
952 #ifdef ENABLE_PTHREAD_SUPPORT
953 	    pthread_join(&clients[i]->thr, NULL);
954 #else
955 	    waitpid(clients[i]->child, NULL, 0);
956 #endif
957 	}
958     }
959 
960     printf("done\n");
961 
962     return 0;
963 }
964