diff options
Diffstat (limited to 'third_party/heimdal/appl/gssmask/gssmaestro.c')
-rw-r--r-- | third_party/heimdal/appl/gssmask/gssmaestro.c | 962 |
1 files changed, 962 insertions, 0 deletions
diff --git a/third_party/heimdal/appl/gssmask/gssmaestro.c b/third_party/heimdal/appl/gssmask/gssmaestro.c new file mode 100644 index 0000000..9ccf1de --- /dev/null +++ b/third_party/heimdal/appl/gssmask/gssmaestro.c @@ -0,0 +1,962 @@ +/* + * Copyright (c) 2006 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of KTH nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <common.h> +RCSID("$Id$"); + +static FILE *logfile; + +/* + * + */ + +struct client { + char *name; + struct sockaddr *sa; + socklen_t salen; + krb5_storage *sock; + int32_t capabilities; + char *target_name; + char *moniker; + krb5_storage *logsock; + int have_log; +#ifdef ENABLE_PTHREAD_SUPPORT + pthread_t thr; +#else + pid_t child; +#endif +}; + +static struct client **clients; +static int num_clients; + +static int +init_sec_context(struct client *client, + int32_t *hContext, int32_t *hCred, + int32_t flags, + const char *targetname, + const krb5_data *itoken, krb5_data *otoken) +{ + int32_t val; + krb5_data_zero(otoken); + put32(client, eInitContext); + put32(client, *hContext); + put32(client, *hCred); + put32(client, flags); + putstring(client, targetname); + putdata(client, *itoken); + ret32(client, *hContext); + ret32(client, val); + retdata(client, *otoken); + return val; +} + +static int +accept_sec_context(struct client *client, + int32_t *hContext, + int32_t flags, + const krb5_data *itoken, + krb5_data *otoken, + int32_t *hDelegCred) +{ + int32_t val; + krb5_data_zero(otoken); + put32(client, eAcceptContext); + put32(client, *hContext); + put32(client, flags); + putdata(client, *itoken); + ret32(client, *hContext); + ret32(client, val); + retdata(client, *otoken); + ret32(client, *hDelegCred); + return val; +} + +static int +acquire_cred(struct client *client, + const char *username, + const char *password, + int32_t flags, + int32_t *hCred) +{ + int32_t val; + put32(client, eAcquireCreds); + putstring(client, username); + putstring(client, password); + put32(client, flags); + ret32(client, val); + ret32(client, *hCred); + return val; +} + +static int +toast_resource(struct client *client, + int32_t hCred) +{ + int32_t val; + put32(client, eToastResource); + put32(client, hCred); + ret32(client, val); + return val; +} + +static int +goodbye(struct client *client) +{ + put32(client, eGoodBye); + return GSMERR_OK; +} + +static int +get_targetname(struct client *client, + char **target) +{ + put32(client, eGetTargetName); + retstring(client, *target); + return GSMERR_OK; +} + +static int32_t +encrypt_token(struct client *client, int32_t hContext, int32_t flags, + krb5_data *in, krb5_data *out) +{ + int32_t val; + put32(client, eEncrypt); + put32(client, hContext); + put32(client, flags); + put32(client, 0); + putdata(client, *in); + ret32(client, val); + retdata(client, *out); + return val; +} + +static int32_t +decrypt_token(struct client *client, int32_t hContext, int flags, + krb5_data *in, krb5_data *out) +{ + int32_t val; + put32(client, eDecrypt); + put32(client, hContext); + put32(client, flags); + put32(client, 0); + putdata(client, *in); + ret32(client, val); + retdata(client, *out); + return val; +} + +static int32_t +wrap_token_ext(struct client *client, int32_t hContext, int32_t flags, + int32_t bflags, krb5_data *header, krb5_data *in, krb5_data *trailer, + krb5_data *out) +{ + int32_t val; + put32(client, eWrapExt); + put32(client, hContext); + put32(client, flags); + put32(client, bflags); + putdata(client, *header); + putdata(client, *in); + putdata(client, *trailer); + ret32(client, val); + retdata(client, *out); + return val; +} + +static int32_t +unwrap_token_ext(struct client *client, int32_t hContext, int32_t flags, + int32_t bflags, krb5_data *header, krb5_data *in, krb5_data *trailer, + krb5_data *out) +{ + int32_t val; + put32(client, eUnwrapExt); + put32(client, hContext); + put32(client, flags); + put32(client, bflags); + putdata(client, *header); + putdata(client, *in); + putdata(client, *trailer); + ret32(client, val); + retdata(client, *out); + return val; +} + +static int32_t +get_mic(struct client *client, int32_t hContext, + krb5_data *in, krb5_data *mic) +{ + int32_t val; + put32(client, eSign); + put32(client, hContext); + put32(client, 0); + put32(client, 0); + putdata(client, *in); + ret32(client, val); + retdata(client, *mic); + return val; +} + +static int32_t +verify_mic(struct client *client, int32_t hContext, + krb5_data *in, krb5_data *mic) +{ + int32_t val; + put32(client, eVerify); + put32(client, hContext); + put32(client, 0); + put32(client, 0); + putdata(client, *in); + putdata(client, *mic); + ret32(client, val); + return val; +} + + +static int32_t +get_version_capa(struct client *client, + int32_t *version, int32_t *capa, + char **version_str) +{ + put32(client, eGetVersionAndCapabilities); + ret32(client, *version); + ret32(client, *capa); + retstring(client, *version_str); + return GSMERR_OK; +} + +static int32_t +get_moniker(struct client *client, + char **moniker) +{ + put32(client, eGetMoniker); + retstring(client, *moniker); + return GSMERR_OK; +} + +static int +wait_log(struct client *c) +{ + int32_t port; + struct sockaddr_storage sast; + socklen_t salen = sizeof(sast); + krb5_socket_t sock, sock2; + int ret; + + memset(&sast, 0, sizeof(sast)); + + assert(sizeof(sast) >= c->salen); + + sock = socket(c->sa->sa_family, SOCK_STREAM, 0); + if (sock == rk_INVALID_SOCKET) + err(1, "failed to build socket for %s's logging port", c->moniker); + + sast.ss_family = c->sa->sa_family; + ret = bind(sock, (struct sockaddr *)&sast, c->salen); + if (ret < 0) + err(1, "failed to bind %s's logging port", c->moniker); + + if (listen(sock, SOMAXCONN) < 0) + err(1, "failed to listen %s's logging port", c->moniker); + + salen = sizeof(sast); + ret = getsockname(sock, (struct sockaddr *)&sast, &salen); + if (ret < 0) + err(1, "failed to get address of local socket for %s", c->moniker); + + port = socket_get_port((struct sockaddr *)&sast); + + put32(c, eSetLoggingSocket); + put32(c, ntohs(port)); + + salen = sizeof(sast); + sock2 = accept(sock, (struct sockaddr *)&sast, &salen); + if (sock2 == rk_INVALID_SOCKET) + err(1, "failed to accept local socket for %s", c->moniker); + rk_closesocket(sock); + + return sock2; +} + + + + +static int +build_context(struct client *ipeer, struct client *apeer, + int32_t flags, int32_t hCred, + int32_t *iContext, int32_t *aContext, int32_t *hDelegCred) +{ + int32_t val = GSMERR_ERROR, ic = 0, ac = 0, deleg = 0; + krb5_data itoken, otoken; + int iDone = 0, aDone = 0; + int step = 0; + int first_call = 0x80; + + if (apeer->target_name == NULL) + errx(1, "apeer %s have no target name", apeer->name); + + krb5_data_zero(&itoken); + + while (!iDone || !aDone) { + + if (iDone) { + warnx("iPeer already done, aPeer want extra rtt"); + val = GSMERR_ERROR; + goto out; + } + + val = init_sec_context(ipeer, &ic, &hCred, flags|first_call, + apeer->target_name, &itoken, &otoken); + step++; + switch(val) { + case GSMERR_OK: + iDone = 1; + if (aDone) + continue; + break; + case GSMERR_CONTINUE_NEEDED: + break; + default: + warnx("iPeer %s failed with %d (step %d)", + ipeer->name, (int)val, step); + goto out; + } + + if (aDone) { + warnx("aPeer already done, iPeer want extra rtt"); + val = GSMERR_ERROR; + goto out; + } + + val = accept_sec_context(apeer, &ac, flags|first_call, + &otoken, &itoken, &deleg); + step++; + switch(val) { + case GSMERR_OK: + aDone = 1; + if (iDone) + continue; + break; + case GSMERR_CONTINUE_NEEDED: + break; + default: + warnx("aPeer %s failed with %d (step %d)", + apeer->name, (int)val, step); + val = GSMERR_ERROR; + goto out; + } + first_call = 0; + val = GSMERR_OK; + } + + if (iContext == NULL || val != GSMERR_OK) { + if (ic) + toast_resource(ipeer, ic); + if (iContext) + *iContext = 0; + } else + *iContext = ic; + + if (aContext == NULL || val != GSMERR_OK) { + if (ac) + toast_resource(apeer, ac); + if (aContext) + *aContext = 0; + } else + *aContext = ac; + + if (hDelegCred == NULL || val != GSMERR_OK) { + if (deleg) + toast_resource(apeer, deleg); + if (hDelegCred) + *hDelegCred = 0; + } else + *hDelegCred = deleg; + +out: + return val; +} + +static void +test_mic(struct client *c1, int32_t hc1, struct client *c2, int32_t hc2) +{ + krb5_data msg, mic; + int32_t val; + + msg.data = "foo"; + msg.length = 3; + + krb5_data_zero(&mic); + + val = get_mic(c1, hc1, &msg, &mic); + if (val) + errx(1, "get_mic failed to host: %s", c1->moniker); + val = verify_mic(c2, hc2, &msg, &mic); + if (val) + errx(1, "verify_mic failed to host: %s", c2->moniker); + + krb5_data_free(&mic); +} + +static int32_t +test_wrap(struct client *c1, int32_t hc1, struct client *c2, int32_t hc2, + int conf) +{ + krb5_data msg, wrapped, out; + int32_t val; + + msg.data = "foo"; + msg.length = 3; + + krb5_data_zero(&wrapped); + krb5_data_zero(&out); + + val = encrypt_token(c1, hc1, conf, &msg, &wrapped); + if (val) { + warnx("encrypt_token failed to host: %s", c1->moniker); + return val; + } + val = decrypt_token(c2, hc2, conf, &wrapped, &out); + if (val) { + krb5_data_free(&wrapped); + warnx("decrypt_token failed to host: %s", c2->moniker); + return val; + } + + if (msg.length != out.length) { + warnx("decrypted'ed token have wrong length (%lu != %lu)", + (unsigned long)msg.length, (unsigned long)out.length); + val = GSMERR_ERROR; + } else if (memcmp(msg.data, out.data, msg.length) != 0) { + warnx("decryptd'ed token have wrong data"); + val = GSMERR_ERROR; + } + + krb5_data_free(&wrapped); + krb5_data_free(&out); + return val; +} + +static int32_t +test_wrap_ext(struct client *c1, int32_t hc1, struct client *c2, int32_t hc2, + int conf, int bflags) +{ + krb5_data header, msg, trailer, wrapped, out; + int32_t val; + + header.data = "header"; + header.length = sizeof("header") - 1; + + msg.data = "0123456789abcdef"; /* padded for most enctypes */ + msg.length = sizeof("0123456789abcdef") - 1; + + trailer.data = "trailer"; + trailer.length = 7; + + krb5_data_zero(&wrapped); + krb5_data_zero(&out); + + val = wrap_token_ext(c1, hc1, conf, bflags, &header, &msg, &trailer, &wrapped); + if (val) { + warnx("encrypt_token failed to host: %s", c1->moniker); + return val; + } + val = unwrap_token_ext(c2, hc2, conf, bflags, &header, &wrapped, &trailer, &out); + if (val) { + krb5_data_free(&wrapped); + warnx("decrypt_token failed to host: %s", c2->moniker); + return val; + } + + if (msg.length != out.length) { + warnx("decrypted'ed token have wrong length (%lu != %lu)", + (unsigned long)msg.length, (unsigned long)out.length); + val = GSMERR_ERROR; + } else if (memcmp(msg.data, out.data, msg.length) != 0) { + warnx("decryptd'ed token have wrong data"); + val = GSMERR_ERROR; + } + + krb5_data_free(&wrapped); + krb5_data_free(&out); + return val; +} + + +static int32_t +test_token(struct client *c1, int32_t hc1, struct client *c2, int32_t hc2, int wrap_ext) +{ + int32_t val; + int i; + + for (i = 0; i < 10; i++) { + /* mic */ + test_mic(c1, hc1, c2, hc2); + test_mic(c2, hc2, c1, hc1); + + /* wrap */ + val = test_wrap(c1, hc1, c2, hc2, 0); + if (val) return val; + val = test_wrap(c2, hc2, c1, hc1, 0); + if (val) return val; + + val = test_wrap(c1, hc1, c2, hc2, 1); + if (val) return val; + val = test_wrap(c2, hc2, c1, hc1, 1); + if (val) return val; + + if (wrap_ext) { + /* wrap ext */ + val = test_wrap_ext(c1, hc1, c2, hc2, 1, 0); + if (val) return val; + val = test_wrap_ext(c2, hc2, c1, hc1, 1, 0); + if (val) return val; + + val = test_wrap_ext(c1, hc1, c2, hc2, 1, 1); + if (val) return val; + val = test_wrap_ext(c2, hc2, c1, hc1, 1, 1); + if (val) return val; + + val = test_wrap_ext(c1, hc1, c2, hc2, 0, 0); + if (val) return val; + val = test_wrap_ext(c2, hc2, c1, hc1, 0, 0); + if (val) return val; + + val = test_wrap_ext(c1, hc1, c2, hc2, 0, 1); + if (val) return val; + val = test_wrap_ext(c2, hc2, c1, hc1, 0, 1); + if (val) return val; + } + } + return GSMERR_OK; +} + +static int +log_function(void *ptr) +{ + struct client *c = ptr; + int32_t cmd, line; + char *file = NULL, *string = NULL; + + while (1) { + if (krb5_ret_int32(c->logsock, &cmd)) + goto out; + + switch (cmd) { + case eLogSetMoniker: + if (krb5_ret_string(c->logsock, &file)) + goto out; + break; + case eLogInfo: + case eLogFailure: + if (krb5_ret_string(c->logsock, &file)) + goto out; + if (krb5_ret_int32(c->logsock, &line)) + goto out; + if (krb5_ret_string(c->logsock, &string)) + goto out; + printf("%s:%lu: %s\n", + file, (unsigned long)line, string); + fprintf(logfile, "%s:%lu: %s\n", + file, (unsigned long)line, string); + fflush(logfile); + if (krb5_store_int32(c->logsock, 0)) + goto out; + break; + default: + errx(1, "client send bad log command: %d", (int)cmd); + } + } +out: + free(file); + free(string); + + return 0; +} + +static void +connect_client(const char *slave) +{ + char *name, *port; + struct client *c = ecalloc(1, sizeof(*c)); + struct addrinfo hints, *res0, *res; + int ret; + krb5_socket_t sock; + + name = estrdup(slave); + port = strchr(name, ':'); + if (port == NULL) + errx(1, "port missing from %s", name); + *port++ = 0; + + c->name = estrdup(slave); + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + ret = getaddrinfo(name, port, &hints, &res0); + if (ret) + errx(1, "error resolving %s", name); + + for (res = res0, sock = rk_INVALID_SOCKET; res; res = res->ai_next) { + sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (sock == rk_INVALID_SOCKET) + continue; + if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) { + rk_closesocket(sock); + sock = rk_INVALID_SOCKET; + continue; + } + c->sa = ecalloc(1, res->ai_addrlen); + memcpy(c->sa, res->ai_addr, res->ai_addrlen); + c->salen = res->ai_addrlen; + break; /* okay we got one */ + } + if (sock == rk_INVALID_SOCKET) + err(1, "connect to host: %s", name); + freeaddrinfo(res0); + + c->sock = krb5_storage_from_socket(sock); + rk_closesocket(sock); + if (c->sock == NULL) + errx(1, "krb5_storage_from_fd"); + + { + int32_t version; + char *str = NULL; + get_version_capa(c, &version, &c->capabilities, &str); + if (str) { + free(str); + } + if (c->capabilities & HAS_MONIKER) + get_moniker(c, &c->moniker); + else + c->moniker = c->name; + if (c->capabilities & ISSERVER) + get_targetname(c, &c->target_name); + } + + if (logfile) { + printf("starting log socket to client %s\n", c->moniker); + + sock = wait_log(c); + + c->logsock = krb5_storage_from_socket(sock); + rk_closesocket(sock); + if (c->logsock == NULL) + errx(1, "failed to create log krb5_storage"); +#ifdef ENABLE_PTHREAD_SUPPORT + pthread_create(&c->thr, NULL, log_function, c); +#else + c->child = fork(); + if (c->child == -1) + errx(1, "failed to fork"); + else if (c->child == 0) { + log_function(c); + fclose(logfile); + exit(0); + } +#endif + } + + + clients = erealloc(clients, (num_clients + 1) * sizeof(*clients)); + + clients[num_clients] = c; + num_clients++; + + free(name); +} + +static struct client * +get_client(const char *slave) +{ + size_t i; + for (i = 0; i < num_clients; i++) + if (strcmp(slave, clients[i]->name) == 0) + return clients[i]; + errx(1, "failed to find client %s", slave); +} + +/* + * + */ + +static int version_flag; +static int help_flag; +static int wrap_ext = 0; +static char *logfile_str; +static getarg_strings principals; +static getarg_strings slaves; + +struct getargs args[] = { + { "principals", 0, arg_strings, &principals, "Test principal", + NULL }, + { "slaves", 0, arg_strings, &slaves, "Slaves", + NULL }, + { "log-file", 0, arg_string, &logfile_str, "Logfile", + NULL }, + { "wrap-ext", 0, arg_flag, &wrap_ext, "test wrap extended", + NULL }, + { "version", 0, arg_flag, &version_flag, "Print version", + NULL }, + { "help", 0, arg_flag, &help_flag, NULL, + NULL } +}; + +static void +usage(int ret) +{ + arg_printusage (args, + sizeof(args) / sizeof(args[0]), + NULL, + ""); + exit (ret); +} + +int +main(int argc, char **argv) +{ + int optidx= 0; + char *user; + char *password; + char ***list, **p; + size_t num_list, i, j, k; + int failed = 0; + + setprogname (argv[0]); + + if (getarg (args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx)) + usage (1); + + if (help_flag) + usage (0); + + if (version_flag) { + print_version (NULL); + return 0; + } + + if (optidx != argc) + usage (1); + + if (principals.num_strings == 0) + errx(1, "no principals"); + + user = estrdup(principals.strings[0]); + password = strchr(user, ':'); + if (password == NULL) + errx(1, "password missing from %s", user); + *password++ = 0; + + if (slaves.num_strings == 0) + errx(1, "no principals"); + + if (logfile_str) { + printf("open logfile %s\n", logfile_str); + logfile = fopen(logfile_str, "w+"); + if (logfile == NULL) + err(1, "failed to open: %s", logfile_str); + } + + /* + * + */ + + list = permutate_all(&slaves, &num_list); + + /* + * Set up connection to all clients + */ + + printf("Connecting to slaves\n"); + for (i = 0; i < slaves.num_strings; i++) + connect_client(slaves.strings[i]); + + /* + * Test acquire credentials + */ + + printf("Test acquire credentials\n"); + for (i = 0; i < slaves.num_strings; i++) { + int32_t hCred, val; + + val = acquire_cred(clients[i], user, password, 1, &hCred); + if (val != GSMERR_OK) { + warnx("Failed to acquire_cred on host %s: %d", + clients[i]->moniker, (int)val); + failed = 1; + } else + toast_resource(clients[i], hCred); + } + + if (failed) + goto out; + + /* + * First test if all slaves can build context to them-self. + */ + + printf("Self context tests\n"); + for (i = 0; i < num_clients; i++) { + int32_t hCred, val, delegCred; + int32_t clientC, serverC; + struct client *c = clients[i]; + + if (c->target_name == NULL) + continue; + + printf("%s connects to self using %s\n", + c->moniker, c->target_name); + + val = acquire_cred(c, user, password, 1, &hCred); + if (val != GSMERR_OK) + errx(1, "failed to acquire_cred: %d", (int)val); + + val = build_context(c, c, + GSS_C_REPLAY_FLAG|GSS_C_SEQUENCE_FLAG| + GSS_C_INTEG_FLAG|GSS_C_CONF_FLAG| + GSS_C_DELEG_FLAG|GSS_C_MUTUAL_FLAG, + hCred, &clientC, &serverC, &delegCred); + if (val == GSMERR_OK) { + test_token(c, clientC, c, serverC, wrap_ext); + toast_resource(c, clientC); + toast_resource(c, serverC); + if (delegCred) + toast_resource(c, delegCred); + } else { + warnx("build_context failed: %d", (int)val); + } + /* + * + */ + + val = build_context(c, c, + GSS_C_INTEG_FLAG|GSS_C_CONF_FLAG, + hCred, &clientC, &serverC, &delegCred); + if (val == GSMERR_OK) { + test_token(c, clientC, c, serverC, wrap_ext); + toast_resource(c, clientC); + toast_resource(c, serverC); + if (delegCred) + toast_resource(c, delegCred); + } else { + warnx("build_context failed: %d", (int)val); + } + + toast_resource(c, hCred); + } + /* + * Build contexts though all entries in each lists, including the + * step from the last entry to the first, ie treat the list as a + * circle. + * + * Only follow the delegated credential, but test "all" + * flags. (XXX only do deleg|mutual right now. + */ + + printf("\"All\" permutation tests\n"); + + for (i = 0; i < num_list; i++) { + int32_t hCred, val, delegCred = 0; + int32_t clientC = 0, serverC = 0; + struct client *client, *server; + + p = list[i]; + + client = get_client(p[0]); + + val = acquire_cred(client, user, password, 1, &hCred); + if (val != GSMERR_OK) + errx(1, "failed to acquire_cred: %d", (int)val); + + for (j = 1; j < num_clients + 1; j++) { + server = get_client(p[j % num_clients]); + + if (server->target_name == NULL) + break; + + for (k = 1; k < j; k++) + printf("\t"); + printf("%s -> %s\n", client->moniker, server->moniker); + + val = build_context(client, server, + GSS_C_REPLAY_FLAG|GSS_C_SEQUENCE_FLAG| + GSS_C_INTEG_FLAG|GSS_C_CONF_FLAG| + GSS_C_DELEG_FLAG|GSS_C_MUTUAL_FLAG, + hCred, &clientC, &serverC, &delegCred); + if (val != GSMERR_OK) { + warnx("build_context failed: %d", (int)val); + break; + } + + val = test_token(client, clientC, server, serverC, wrap_ext); + if (val) + break; + + toast_resource(client, clientC); + toast_resource(server, serverC); + if (!delegCred) { + warnx("no delegated cred on %s", server->moniker); + break; + } + toast_resource(client, hCred); + hCred = delegCred; + client = server; + } + if (hCred) + toast_resource(client, hCred); + } + + /* + * Close all connections to clients + */ + +out: + printf("sending goodbye and waiting for log sockets\n"); + for (i = 0; i < num_clients; i++) { + goodbye(clients[i]); + if (clients[i]->logsock) { +#ifdef ENABLE_PTHREAD_SUPPORT + pthread_join(&clients[i]->thr, NULL); +#else + waitpid(clients[i]->child, NULL, 0); +#endif + } + } + + printf("done\n"); + + return 0; +} |