22575: Remove extra ;, From Dennis Davis.
[heimdal.git] / appl / gssmask / gssmaestro.c
blob7da52cdfc803fe965e703867dd5393de6d29e4ba
1 /*
2 * Copyright (c) 2006 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
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.
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.
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.
34 #include <common.h>
35 RCSID("$Id$");
37 static FILE *logfile;
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
60 static struct client **clients;
61 static int num_clients;
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)
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;
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)
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;
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)
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;
122 static int
123 toast_resource(struct client *client,
124 int32_t hCred)
126 int32_t val;
127 put32(client, eToastResource);
128 put32(client, hCred);
129 ret32(client, val);
130 return val;
133 static int
134 goodbye(struct client *client)
136 put32(client, eGoodBye);
137 return GSMERR_OK;
140 static int
141 get_targetname(struct client *client,
142 char **target)
144 put32(client, eGetTargetName);
145 retstring(client, *target);
146 return GSMERR_OK;
149 static int32_t
150 encrypt_token(struct client *client, int32_t hContext, int32_t flags,
151 krb5_data *in, krb5_data *out)
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;
164 static int32_t
165 decrypt_token(struct client *client, int32_t hContext, int flags,
166 krb5_data *in, krb5_data *out)
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;
179 static int32_t
180 get_mic(struct client *client, int32_t hContext,
181 krb5_data *in, krb5_data *mic)
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;
194 static int32_t
195 verify_mic(struct client *client, int32_t hContext,
196 krb5_data *in, krb5_data *mic)
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;
210 static int32_t
211 get_version_capa(struct client *client,
212 int32_t *version, int32_t *capa,
213 char **version_str)
215 put32(client, eGetVersionAndCapabilities);
216 ret32(client, *version);
217 ret32(client, *capa);
218 retstring(client, *version_str);
219 return GSMERR_OK;
222 static int32_t
223 get_moniker(struct client *client,
224 char **moniker)
226 put32(client, eGetMoniker);
227 retstring(client, *moniker);
228 return GSMERR_OK;
231 static int
232 wait_log(struct client *c)
234 int32_t port;
235 struct sockaddr_storage sast;
236 socklen_t salen = sizeof(sast);
237 int fd, fd2, ret;
239 memset(&sast, 0, sizeof(sast));
241 assert(sizeof(sast) >= c->salen);
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);
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);
252 if (listen(fd, SOMAXCONN) < 0)
253 err(1, "failed to listen %s's logging port", c->moniker);
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);
260 port = socket_get_port((struct sockaddr *)&sast);
262 put32(c, eSetLoggingSocket);
263 put32(c, ntohs(port));
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);
271 return fd2;
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)
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;
288 if (apeer->target_name == NULL)
289 errx(1, "apeer %s have no target name", apeer->name);
291 krb5_data_zero(&itoken);
293 while (!iDone || !aDone) {
295 if (iDone) {
296 warnx("iPeer already done, aPeer want extra rtt");
297 val = GSMERR_ERROR;
298 goto out;
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;
318 if (aDone) {
319 warnx("aPeer already done, iPeer want extra rtt");
320 val = GSMERR_ERROR;
321 goto out;
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;
341 first_call = 0;
342 val = GSMERR_OK;
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;
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;
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;
369 out:
370 return val;
373 static void
374 test_mic(struct client *c1, int32_t hc1, struct client *c2, int32_t hc2)
376 krb5_data msg, mic;
377 int32_t val;
379 msg.data = "foo";
380 msg.length = 3;
382 krb5_data_zero(&mic);
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);
391 krb5_data_free(&mic);
394 static int32_t
395 test_wrap(struct client *c1, int32_t hc1, struct client *c2, int32_t hc2,
396 int conf)
398 krb5_data msg, wrapped, out;
399 int32_t val;
401 msg.data = "foo";
402 msg.length = 3;
404 krb5_data_zero(&wrapped);
405 krb5_data_zero(&out);
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;
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;
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;
428 krb5_data_free(&wrapped);
429 krb5_data_free(&out);
430 return val;
433 static int32_t
434 test_token(struct client *c1, int32_t hc1, struct client *c2, int32_t hc2)
436 int32_t val;
437 int i;
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;
451 return GSMERR_OK;
454 static int
455 log_function(void *ptr)
457 struct client *c = ptr;
458 int32_t cmd, line;
459 char *file, *string;
461 while (1) {
462 if (krb5_ret_int32(c->logsock, &cmd))
463 goto out;
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);
493 out:
495 return 0;
498 static void
499 connect_client(const char *slave)
501 char *name, *port;
502 struct client *c = ecalloc(1, sizeof(*c));
503 struct addrinfo hints, *res0, *res;
504 int ret, fd;
506 name = estrdup(slave);
507 port = strchr(name, ':');
508 if (port == NULL)
509 errx(1, "port missing from %s", name);
510 *port++ = 0;
512 c->name = estrdup(slave);
514 memset(&hints, 0, sizeof(hints));
515 hints.ai_family = PF_UNSPEC;
516 hints.ai_socktype = SOCK_STREAM;
518 ret = getaddrinfo(name, port, &hints, &res0);
519 if (ret)
520 errx(1, "error resolving %s", name);
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;
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 */
536 if (fd < 0)
537 err(1, "connect to host: %s", name);
538 freeaddrinfo(res);
540 c->sock = krb5_storage_from_fd(fd);
541 close(fd);
542 if (c->sock == NULL)
543 errx(1, "krb5_storage_from_fd");
546 int32_t version;
547 char *str = NULL;
548 get_version_capa(c, &version, &c->capabilities, &str);
549 if (str) {
550 free(str);
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);
560 if (logfile) {
561 int fd;
563 printf("starting log socket to client %s\n", c->moniker);
565 fd = wait_log(c);
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);
582 #endif
586 clients = erealloc(clients, (num_clients + 1) * sizeof(*clients));
588 clients[num_clients] = c;
589 num_clients++;
591 free(name);
594 static struct client *
595 get_client(const char *slave)
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);
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;
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 }
627 static void
628 usage(int ret)
630 arg_printusage (args,
631 sizeof(args) / sizeof(args[0]),
632 NULL,
633 "");
634 exit (ret);
638 main(int argc, char **argv)
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;
647 setprogname (argv[0]);
649 if (getarg (args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
650 usage (1);
652 if (help_flag)
653 usage (0);
655 if (version_flag) {
656 print_version (NULL);
657 return 0;
660 if (optidx != argc)
661 usage (1);
663 if (principals.num_strings == 0)
664 errx(1, "no principals");
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;
672 if (slaves.num_strings == 0)
673 errx(1, "no principals");
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);
686 list = permutate_all(&slaves, &num_list);
689 * Set up connection to all clients
692 printf("Connecting to slaves\n");
693 for (i = 0; i < slaves.num_strings; i++)
694 connect_client(slaves.strings[i]);
697 * Test acquire credentials
700 printf("Test acquire credentials\n");
701 for (i = 0; i < slaves.num_strings; i++) {
702 int32_t hCred, val;
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);
713 if (failed)
714 goto out;
717 * First test if all slaves can build context to them-self.
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];
726 if (c->target_name == NULL)
727 continue;
729 printf("%s connects to self using %s\n",
730 c->moniker, c->target_name);
732 val = acquire_cred(c, user, password, 1, &hCred);
733 if (val != GSMERR_OK)
734 errx(1, "failed to acquire_cred: %d", (int)val);
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);
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);
767 toast_resource(c, hCred);
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.
774 * Only follow the delegated credential, but test "all"
775 * flags. (XXX only do deleg|mutual right now.
778 printf("\"All\" permutation tests\n");
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;
785 p = list[i];
787 client = get_client(p[0]);
789 val = acquire_cred(client, user, password, 1, &hCred);
790 if (val != GSMERR_OK)
791 errx(1, "failed to acquire_cred: %d", (int)val);
793 for (j = 1; j < num_clients + 1; j++) {
794 server = get_client(p[j % num_clients]);
796 if (server->target_name == NULL)
797 break;
799 for (k = 1; k < j; k++)
800 printf("\t");
801 printf("%s -> %s\n", client->moniker, server->moniker);
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;
813 val = test_token(client, clientC, server, serverC);
814 if (val)
815 break;
817 toast_resource(client, clientC);
818 toast_resource(server, serverC);
819 if (!delegCred) {
820 warnx("no delegated cred on %s", server->moniker);
821 break;
823 toast_resource(client, hCred);
824 hCred = delegCred;
825 client = server;
827 if (hCred)
828 toast_resource(client, hCred);
832 * Close all connections to clients
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
848 printf("done\n");
850 return 0;