]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - crypto/heimdal/appl/gssmask/gssmaestro.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / crypto / heimdal / appl / gssmask / gssmaestro.c
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 }