gssmaestro.c revision 178825
1178825Sdfr/*
2178825Sdfr * Copyright (c) 2006 Kungliga Tekniska H�gskolan
3178825Sdfr * (Royal Institute of Technology, Stockholm, Sweden).
4178825Sdfr * All rights reserved.
5178825Sdfr *
6178825Sdfr * Redistribution and use in source and binary forms, with or without
7178825Sdfr * modification, are permitted provided that the following conditions
8178825Sdfr * are met:
9178825Sdfr *
10178825Sdfr * 1. Redistributions of source code must retain the above copyright
11178825Sdfr *    notice, this list of conditions and the following disclaimer.
12178825Sdfr *
13178825Sdfr * 2. Redistributions in binary form must reproduce the above copyright
14178825Sdfr *    notice, this list of conditions and the following disclaimer in the
15178825Sdfr *    documentation and/or other materials provided with the distribution.
16178825Sdfr *
17178825Sdfr * 3. Neither the name of KTH nor the names of its contributors may be
18178825Sdfr *    used to endorse or promote products derived from this software without
19178825Sdfr *    specific prior written permission.
20178825Sdfr *
21178825Sdfr * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY
22178825Sdfr * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23178825Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24178825Sdfr * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE
25178825Sdfr * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26178825Sdfr * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27178825Sdfr * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28178825Sdfr * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29178825Sdfr * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30178825Sdfr * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
31178825Sdfr * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32178825Sdfr */
33178825Sdfr
34178825Sdfr#include <common.h>
35178825SdfrRCSID("$Id: gssmaestro.c 21605 2007-07-17 06:51:57Z lha $");
36178825Sdfr
37178825Sdfrstatic FILE *logfile;
38178825Sdfr
39178825Sdfr/*
40178825Sdfr *
41178825Sdfr */
42178825Sdfr
43178825Sdfrstruct client {
44178825Sdfr    char *name;
45178825Sdfr    struct sockaddr *sa;
46178825Sdfr    socklen_t salen;
47178825Sdfr    krb5_storage *sock;
48178825Sdfr    int32_t capabilities;
49178825Sdfr    char *target_name;
50178825Sdfr    char *moniker;
51178825Sdfr    krb5_storage *logsock;
52178825Sdfr    int have_log;
53178825Sdfr#ifdef ENABLE_PTHREAD_SUPPORT
54178825Sdfr    pthread_t thr;
55178825Sdfr#else
56178825Sdfr    pid_t child;
57178825Sdfr#endif
58178825Sdfr};
59178825Sdfr
60178825Sdfrstatic struct client **clients;
61178825Sdfrstatic int num_clients;
62178825Sdfr
63178825Sdfrstatic int
64178825Sdfrinit_sec_context(struct client *client,
65178825Sdfr		 int32_t *hContext, int32_t *hCred,
66178825Sdfr		 int32_t flags,
67178825Sdfr		 const char *targetname,
68178825Sdfr		 const krb5_data *itoken, krb5_data *otoken)
69178825Sdfr{
70178825Sdfr    int32_t val;
71178825Sdfr    krb5_data_zero(otoken);
72178825Sdfr    put32(client, eInitContext);
73178825Sdfr    put32(client, *hContext);
74178825Sdfr    put32(client, *hCred);
75178825Sdfr    put32(client, flags);
76178825Sdfr    putstring(client, targetname);
77178825Sdfr    putdata(client, *itoken);
78178825Sdfr    ret32(client, *hContext);
79178825Sdfr    ret32(client, val);
80178825Sdfr    retdata(client, *otoken);
81178825Sdfr    return val;
82178825Sdfr}
83178825Sdfr
84178825Sdfrstatic int
85178825Sdfraccept_sec_context(struct client *client,
86178825Sdfr		   int32_t *hContext,
87178825Sdfr		   int32_t flags,
88178825Sdfr		   const krb5_data *itoken,
89178825Sdfr		   krb5_data *otoken,
90178825Sdfr		   int32_t *hDelegCred)
91178825Sdfr{
92178825Sdfr    int32_t val;
93178825Sdfr    krb5_data_zero(otoken);
94178825Sdfr    put32(client, eAcceptContext);
95178825Sdfr    put32(client, *hContext);
96178825Sdfr    put32(client, flags);
97178825Sdfr    putdata(client, *itoken);
98178825Sdfr    ret32(client, *hContext);
99178825Sdfr    ret32(client, val);
100178825Sdfr    retdata(client, *otoken);
101178825Sdfr    ret32(client, *hDelegCred);
102178825Sdfr    return val;
103178825Sdfr}
104178825Sdfr
105178825Sdfrstatic int
106178825Sdfracquire_cred(struct client *client,
107178825Sdfr	     const char *username,
108178825Sdfr	     const char *password,
109178825Sdfr	     int32_t flags,
110178825Sdfr	     int32_t *hCred)
111178825Sdfr{
112178825Sdfr    int32_t val;
113178825Sdfr    put32(client, eAcquireCreds);
114178825Sdfr    putstring(client, username);
115178825Sdfr    putstring(client, password);
116178825Sdfr    put32(client, flags);
117178825Sdfr    ret32(client, val);
118178825Sdfr    ret32(client, *hCred);
119178825Sdfr    return val;
120178825Sdfr}
121178825Sdfr
122178825Sdfrstatic int
123178825Sdfrtoast_resource(struct client *client,
124178825Sdfr	       int32_t hCred)
125178825Sdfr{
126178825Sdfr    int32_t val;
127178825Sdfr    put32(client, eToastResource);
128178825Sdfr    put32(client, hCred);
129178825Sdfr    ret32(client, val);
130178825Sdfr    return val;
131178825Sdfr}
132178825Sdfr
133178825Sdfrstatic int
134178825Sdfrgoodbye(struct client *client)
135178825Sdfr{
136178825Sdfr    put32(client, eGoodBye);
137178825Sdfr    return GSMERR_OK;
138178825Sdfr}
139178825Sdfr
140178825Sdfrstatic int
141178825Sdfrget_targetname(struct client *client,
142178825Sdfr	       char **target)
143178825Sdfr{
144178825Sdfr    put32(client, eGetTargetName);
145178825Sdfr    retstring(client, *target);
146178825Sdfr    return GSMERR_OK;
147178825Sdfr}
148178825Sdfr
149178825Sdfrstatic int32_t
150178825Sdfrencrypt_token(struct client *client, int32_t hContext, int32_t flags,
151178825Sdfr	   krb5_data *in, krb5_data *out)
152178825Sdfr{
153178825Sdfr    int32_t val;
154178825Sdfr    put32(client, eEncrypt);
155178825Sdfr    put32(client, hContext);
156178825Sdfr    put32(client, flags);
157178825Sdfr    put32(client, 0);
158178825Sdfr    putdata(client, *in);
159178825Sdfr    ret32(client, val);
160178825Sdfr    retdata(client, *out);
161178825Sdfr    return val;
162178825Sdfr}
163178825Sdfr
164178825Sdfrstatic int32_t
165178825Sdfrdecrypt_token(struct client *client, int32_t hContext, int flags,
166178825Sdfr	     krb5_data *in, krb5_data *out)
167178825Sdfr{
168178825Sdfr    int32_t val;
169178825Sdfr    put32(client, eDecrypt);
170178825Sdfr    put32(client, hContext);
171178825Sdfr    put32(client, flags);
172178825Sdfr    put32(client, 0);
173178825Sdfr    putdata(client, *in);
174178825Sdfr    ret32(client, val);
175178825Sdfr    retdata(client, *out);
176178825Sdfr    return val;
177178825Sdfr}
178178825Sdfr
179178825Sdfrstatic int32_t
180178825Sdfrget_mic(struct client *client, int32_t hContext,
181178825Sdfr	krb5_data *in, krb5_data *mic)
182178825Sdfr{
183178825Sdfr    int32_t val;
184178825Sdfr    put32(client, eSign);
185178825Sdfr    put32(client, hContext);
186178825Sdfr    put32(client, 0);
187178825Sdfr    put32(client, 0);
188178825Sdfr    putdata(client, *in);
189178825Sdfr    ret32(client, val);
190178825Sdfr    retdata(client, *mic);
191178825Sdfr    return val;
192178825Sdfr}
193178825Sdfr
194178825Sdfrstatic int32_t
195178825Sdfrverify_mic(struct client *client, int32_t hContext,
196178825Sdfr	   krb5_data *in, krb5_data *mic)
197178825Sdfr{
198178825Sdfr    int32_t val;
199178825Sdfr    put32(client, eVerify);
200178825Sdfr    put32(client, hContext);
201178825Sdfr    put32(client, 0);
202178825Sdfr    put32(client, 0);
203178825Sdfr    putdata(client, *in);
204178825Sdfr    putdata(client, *mic);
205178825Sdfr    ret32(client, val);
206178825Sdfr    return val;
207178825Sdfr}
208178825Sdfr
209178825Sdfr
210178825Sdfrstatic int32_t
211178825Sdfrget_version_capa(struct client *client,
212178825Sdfr		 int32_t *version, int32_t *capa,
213178825Sdfr		 char **version_str)
214178825Sdfr{
215178825Sdfr    put32(client, eGetVersionAndCapabilities);
216178825Sdfr    ret32(client, *version);
217178825Sdfr    ret32(client, *capa);
218178825Sdfr    retstring(client, *version_str);
219178825Sdfr    return GSMERR_OK;
220178825Sdfr}
221178825Sdfr
222178825Sdfrstatic int32_t
223178825Sdfrget_moniker(struct client *client,
224178825Sdfr	    char **moniker)
225178825Sdfr{
226178825Sdfr    put32(client, eGetMoniker);
227178825Sdfr    retstring(client, *moniker);
228178825Sdfr    return GSMERR_OK;
229178825Sdfr}
230178825Sdfr
231178825Sdfrstatic int
232178825Sdfrwait_log(struct client *c)
233178825Sdfr{
234178825Sdfr    int32_t port;
235178825Sdfr    struct sockaddr_storage sast;
236178825Sdfr    socklen_t salen = sizeof(sast);
237178825Sdfr    int fd, fd2, ret;
238178825Sdfr
239178825Sdfr    memset(&sast, 0, sizeof(sast));
240178825Sdfr
241178825Sdfr    assert(sizeof(sast) >= c->salen);
242178825Sdfr
243178825Sdfr    fd = socket(c->sa->sa_family, SOCK_STREAM, 0);
244178825Sdfr    if (fd < 0)
245178825Sdfr	err(1, "failed to build socket for %s's logging port", c->moniker);
246178825Sdfr
247178825Sdfr    ((struct sockaddr *)&sast)->sa_family = c->sa->sa_family;
248178825Sdfr    ret = bind(fd, (struct sockaddr *)&sast, c->salen);
249178825Sdfr    if (ret < 0)
250178825Sdfr	err(1, "failed to bind %s's logging port", c->moniker);
251178825Sdfr
252178825Sdfr    if (listen(fd, SOMAXCONN) < 0)
253178825Sdfr	err(1, "failed to listen %s's logging port", c->moniker);
254178825Sdfr
255178825Sdfr    salen = sizeof(sast);
256178825Sdfr    ret = getsockname(fd, (struct sockaddr *)&sast, &salen);
257178825Sdfr    if (ret < 0)
258178825Sdfr	err(1, "failed to get address of local socket for %s", c->moniker);
259178825Sdfr
260178825Sdfr    port = socket_get_port((struct sockaddr *)&sast);
261178825Sdfr
262178825Sdfr    put32(c, eSetLoggingSocket);
263178825Sdfr    put32(c, ntohs(port));
264178825Sdfr
265178825Sdfr    salen = sizeof(sast);
266178825Sdfr    fd2 = accept(fd, (struct sockaddr *)&sast, &salen);
267178825Sdfr    if (fd2 < 0)
268178825Sdfr	err(1, "failed to accept local socket for %s", c->moniker);
269178825Sdfr    close(fd);
270178825Sdfr
271178825Sdfr    return fd2;
272178825Sdfr}
273178825Sdfr
274178825Sdfr
275178825Sdfr
276178825Sdfr
277178825Sdfrstatic int
278178825Sdfrbuild_context(struct client *ipeer, struct client *apeer,
279178825Sdfr	      int32_t flags, int32_t hCred,
280178825Sdfr	      int32_t *iContext, int32_t *aContext, int32_t *hDelegCred)
281178825Sdfr{
282178825Sdfr    int32_t val = GSMERR_ERROR, ic = 0, ac = 0, deleg = 0;
283178825Sdfr    krb5_data itoken, otoken;
284178825Sdfr    int iDone = 0, aDone = 0;
285178825Sdfr    int step = 0;
286178825Sdfr    int first_call = 0x80;
287178825Sdfr
288178825Sdfr    if (apeer->target_name == NULL)
289178825Sdfr	errx(1, "apeer %s have no target name", apeer->name);
290178825Sdfr
291178825Sdfr    krb5_data_zero(&itoken);
292178825Sdfr
293178825Sdfr    while (!iDone || !aDone) {
294178825Sdfr
295178825Sdfr	if (iDone) {
296178825Sdfr	    warnx("iPeer already done, aPeer want extra rtt");
297178825Sdfr	    val = GSMERR_ERROR;
298178825Sdfr	    goto out;
299178825Sdfr	}
300178825Sdfr
301178825Sdfr	val = init_sec_context(ipeer, &ic, &hCred, flags|first_call,
302178825Sdfr			       apeer->target_name, &itoken, &otoken);
303178825Sdfr	step++;
304178825Sdfr	switch(val) {
305178825Sdfr	case GSMERR_OK:
306178825Sdfr	    iDone = 1;
307178825Sdfr	    if (aDone)
308178825Sdfr		continue;
309178825Sdfr	    break;
310178825Sdfr	case GSMERR_CONTINUE_NEEDED:
311178825Sdfr	    break;
312178825Sdfr	default:
313178825Sdfr	    warnx("iPeer %s failed with %d (step %d)",
314178825Sdfr		  ipeer->name, (int)val, step);
315178825Sdfr	    goto out;
316178825Sdfr	}
317178825Sdfr
318178825Sdfr	if (aDone) {
319178825Sdfr	    warnx("aPeer already done, iPeer want extra rtt");
320178825Sdfr	    val = GSMERR_ERROR;
321178825Sdfr	    goto out;
322178825Sdfr	}
323178825Sdfr
324178825Sdfr	val = accept_sec_context(apeer, &ac, flags|first_call,
325178825Sdfr				 &otoken, &itoken, &deleg);
326178825Sdfr	step++;
327178825Sdfr	switch(val) {
328178825Sdfr	case GSMERR_OK:
329178825Sdfr	    aDone = 1;
330178825Sdfr	    if (iDone)
331178825Sdfr		continue;
332178825Sdfr	    break;
333178825Sdfr	case GSMERR_CONTINUE_NEEDED:
334178825Sdfr	    break;
335178825Sdfr	default:
336178825Sdfr	    warnx("aPeer %s failed with %d (step %d)",
337178825Sdfr		 apeer->name, (int)val, step);
338178825Sdfr	    val = GSMERR_ERROR;
339178825Sdfr	    goto out;
340178825Sdfr	}
341178825Sdfr	first_call = 0;
342178825Sdfr	val = GSMERR_OK;
343178825Sdfr    }
344178825Sdfr
345178825Sdfr    if (iContext == NULL || val != GSMERR_OK) {
346178825Sdfr	if (ic)
347178825Sdfr	    toast_resource(ipeer, ic);
348178825Sdfr	if (iContext)
349178825Sdfr	    *iContext = 0;
350178825Sdfr    } else
351178825Sdfr	*iContext = ic;
352178825Sdfr
353178825Sdfr    if (aContext == NULL || val != GSMERR_OK) {
354178825Sdfr	if (ac)
355178825Sdfr	    toast_resource(apeer, ac);
356178825Sdfr	if (aContext)
357178825Sdfr	    *aContext = 0;
358178825Sdfr    } else
359178825Sdfr	*aContext = ac;
360178825Sdfr
361178825Sdfr    if (hDelegCred == NULL || val != GSMERR_OK) {
362178825Sdfr	if (deleg)
363178825Sdfr	    toast_resource(apeer, deleg);
364178825Sdfr	if (hDelegCred)
365178825Sdfr	    *hDelegCred = 0;
366178825Sdfr    } else
367178825Sdfr	*hDelegCred = deleg;
368178825Sdfr
369178825Sdfrout:
370178825Sdfr    return val;
371178825Sdfr}
372178825Sdfr
373178825Sdfrstatic void
374178825Sdfrtest_mic(struct client *c1, int32_t hc1, struct client *c2, int32_t hc2)
375178825Sdfr{
376178825Sdfr    krb5_data msg, mic;
377178825Sdfr    int32_t val;
378178825Sdfr
379178825Sdfr    msg.data = "foo";
380178825Sdfr    msg.length = 3;
381178825Sdfr
382178825Sdfr    krb5_data_zero(&mic);
383178825Sdfr
384178825Sdfr    val = get_mic(c1, hc1, &msg, &mic);
385178825Sdfr    if (val)
386178825Sdfr	errx(1, "get_mic failed to host: %s", c1->moniker);
387178825Sdfr    val = verify_mic(c2, hc2, &msg, &mic);
388178825Sdfr    if (val)
389178825Sdfr	errx(1, "verify_mic failed to host: %s", c2->moniker);
390178825Sdfr
391178825Sdfr    krb5_data_free(&mic);
392178825Sdfr}
393178825Sdfr
394178825Sdfrstatic int32_t
395178825Sdfrtest_wrap(struct client *c1, int32_t hc1, struct client *c2, int32_t hc2,
396178825Sdfr	  int conf)
397178825Sdfr{
398178825Sdfr    krb5_data msg, wrapped, out;
399178825Sdfr    int32_t val;
400178825Sdfr
401178825Sdfr    msg.data = "foo";
402178825Sdfr    msg.length = 3;
403178825Sdfr
404178825Sdfr    krb5_data_zero(&wrapped);
405178825Sdfr    krb5_data_zero(&out);
406178825Sdfr
407178825Sdfr    val = encrypt_token(c1, hc1, conf, &msg, &wrapped);
408178825Sdfr    if (val) {
409178825Sdfr	warnx("encrypt_token failed to host: %s", c1->moniker);
410178825Sdfr	return val;
411178825Sdfr    }
412178825Sdfr    val = decrypt_token(c2, hc2, conf, &wrapped, &out);
413178825Sdfr    if (val) {
414178825Sdfr	krb5_data_free(&wrapped);
415178825Sdfr	warnx("decrypt_token failed to host: %s", c2->moniker);
416178825Sdfr	return val;
417178825Sdfr    }
418178825Sdfr
419178825Sdfr    if (msg.length != out.length) {
420178825Sdfr	warnx("decrypted'ed token have wrong length (%lu != %lu)",
421178825Sdfr	      (unsigned long)msg.length, (unsigned long)out.length);
422178825Sdfr	val = GSMERR_ERROR;
423178825Sdfr    } else if (memcmp(msg.data, out.data, msg.length) != 0) {
424178825Sdfr	warnx("decryptd'ed token have wrong data");
425178825Sdfr	val = GSMERR_ERROR;
426178825Sdfr    }
427178825Sdfr
428178825Sdfr    krb5_data_free(&wrapped);
429178825Sdfr    krb5_data_free(&out);
430178825Sdfr    return val;
431178825Sdfr}
432178825Sdfr
433178825Sdfrstatic int32_t
434178825Sdfrtest_token(struct client *c1, int32_t hc1, struct client *c2, int32_t hc2)
435178825Sdfr{
436178825Sdfr    int32_t val;
437178825Sdfr    int i;
438178825Sdfr
439178825Sdfr    for (i = 0; i < 10; i++) {
440178825Sdfr	test_mic(c1, hc1, c2, hc2);
441178825Sdfr	test_mic(c2, hc2, c1, hc1);
442178825Sdfr	val = test_wrap(c1, hc1, c2, hc2, 0);
443178825Sdfr	if (val) return val;
444178825Sdfr	val = test_wrap(c2, hc2, c1, hc1, 0);
445178825Sdfr	if (val) return val;
446178825Sdfr	val = test_wrap(c1, hc1, c2, hc2, 1);
447178825Sdfr	if (val) return val;
448178825Sdfr	val = test_wrap(c2, hc2, c1, hc1, 1);
449178825Sdfr	if (val) return val;
450178825Sdfr    }
451178825Sdfr    return GSMERR_OK;
452178825Sdfr}
453178825Sdfr
454178825Sdfrstatic int
455178825Sdfrlog_function(void *ptr)
456178825Sdfr{
457178825Sdfr    struct client *c = ptr;
458178825Sdfr    int32_t cmd, line;
459178825Sdfr    char *file, *string;
460178825Sdfr
461178825Sdfr    while (1) {
462178825Sdfr        if (krb5_ret_int32(c->logsock, &cmd))
463178825Sdfr	    goto out;
464178825Sdfr
465178825Sdfr	switch (cmd) {
466178825Sdfr	case eLogSetMoniker:
467178825Sdfr	    if (krb5_ret_string(c->logsock, &file))
468178825Sdfr		goto out;
469178825Sdfr	    free(file);
470178825Sdfr	    break;
471178825Sdfr	case eLogInfo:
472178825Sdfr	case eLogFailure:
473178825Sdfr	    if (krb5_ret_string(c->logsock, &file))
474178825Sdfr		goto out;
475178825Sdfr	    if (krb5_ret_int32(c->logsock, &line))
476178825Sdfr		goto out;
477178825Sdfr	    if (krb5_ret_string(c->logsock, &string))
478178825Sdfr		goto out;
479178825Sdfr	    printf("%s:%lu: %s\n",
480178825Sdfr		   file, (unsigned long)line, string);
481178825Sdfr	    fprintf(logfile, "%s:%lu: %s\n",
482178825Sdfr		    file, (unsigned long)line, string);
483178825Sdfr	    fflush(logfile);
484178825Sdfr	    free(file);
485178825Sdfr	    free(string);
486178825Sdfr	    if (krb5_store_int32(c->logsock, 0))
487178825Sdfr		goto out;
488178825Sdfr	    break;
489178825Sdfr	default:
490178825Sdfr	    errx(1, "client send bad log command: %d", (int)cmd);
491178825Sdfr	}
492178825Sdfr    }
493178825Sdfrout:
494178825Sdfr
495178825Sdfr    return 0;
496178825Sdfr}
497178825Sdfr
498178825Sdfrstatic void
499178825Sdfrconnect_client(const char *slave)
500178825Sdfr{
501178825Sdfr    char *name, *port;
502178825Sdfr    struct client *c = ecalloc(1, sizeof(*c));
503178825Sdfr    struct addrinfo hints, *res0, *res;
504178825Sdfr    int ret, fd;
505178825Sdfr
506178825Sdfr    name = estrdup(slave);
507178825Sdfr    port = strchr(name, ':');
508178825Sdfr    if (port == NULL)
509178825Sdfr	errx(1, "port missing from %s", name);
510178825Sdfr    *port++ = 0;
511178825Sdfr
512178825Sdfr    c->name = estrdup(slave);
513178825Sdfr
514178825Sdfr    memset(&hints, 0, sizeof(hints));
515178825Sdfr    hints.ai_family = PF_UNSPEC;
516178825Sdfr    hints.ai_socktype = SOCK_STREAM;
517178825Sdfr
518178825Sdfr    ret = getaddrinfo(name, port, &hints, &res0);
519178825Sdfr    if (ret)
520178825Sdfr	errx(1, "error resolving %s", name);
521178825Sdfr
522178825Sdfr    for (res = res0, fd = -1; res; res = res->ai_next) {
523178825Sdfr	fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
524178825Sdfr	if (fd < 0)
525178825Sdfr	    continue;
526178825Sdfr	if (connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
527178825Sdfr	    close(fd);
528178825Sdfr	    fd = -1;
529178825Sdfr	    continue;
530178825Sdfr	}
531178825Sdfr	c->sa = ecalloc(1, res->ai_addrlen);
532178825Sdfr	memcpy(c->sa, res->ai_addr, res->ai_addrlen);
533178825Sdfr	c->salen = res->ai_addrlen;
534178825Sdfr	break;  /* okay we got one */
535178825Sdfr    }
536178825Sdfr    if (fd < 0)
537178825Sdfr	err(1, "connect to host: %s", name);
538178825Sdfr    freeaddrinfo(res);
539178825Sdfr
540178825Sdfr    c->sock = krb5_storage_from_fd(fd);
541178825Sdfr    close(fd);
542178825Sdfr    if (c->sock == NULL)
543178825Sdfr	errx(1, "krb5_storage_from_fd");
544178825Sdfr
545178825Sdfr    {
546178825Sdfr	int32_t version;
547178825Sdfr	char *str = NULL;
548178825Sdfr	get_version_capa(c, &version, &c->capabilities, &str);
549178825Sdfr	if (str) {
550178825Sdfr	    free(str);
551178825Sdfr	}
552178825Sdfr	if (c->capabilities & HAS_MONIKER)
553178825Sdfr	    get_moniker(c, &c->moniker);
554178825Sdfr	else
555178825Sdfr	    c->moniker = c->name;
556178825Sdfr	if (c->capabilities & ISSERVER)
557178825Sdfr	    get_targetname(c, &c->target_name);
558178825Sdfr    }
559178825Sdfr
560178825Sdfr    if (logfile) {
561178825Sdfr	int fd;
562178825Sdfr
563178825Sdfr	printf("starting log socket to client %s\n", c->moniker);
564178825Sdfr
565178825Sdfr	fd = wait_log(c);
566178825Sdfr
567178825Sdfr	c->logsock = krb5_storage_from_fd(fd);
568178825Sdfr	close(fd);
569178825Sdfr	if (c->logsock == NULL)
570178825Sdfr	    errx(1, "failed to create log krb5_storage");
571178825Sdfr#ifdef ENABLE_PTHREAD_SUPPORT
572178825Sdfr	pthread_create(&c->thr, NULL, log_function, c);
573178825Sdfr#else
574178825Sdfr	c->child = fork();
575178825Sdfr	if (c->child == -1)
576178825Sdfr	    errx(1, "failed to fork");
577178825Sdfr	else if (c->child == 0) {
578178825Sdfr	    log_function(c);
579178825Sdfr	    fclose(logfile);
580178825Sdfr	    exit(0);
581178825Sdfr	}
582178825Sdfr#endif
583178825Sdfr   }
584178825Sdfr
585178825Sdfr
586178825Sdfr    clients = erealloc(clients, (num_clients + 1) * sizeof(*clients));
587178825Sdfr
588178825Sdfr    clients[num_clients] = c;
589178825Sdfr    num_clients++;
590178825Sdfr
591178825Sdfr    free(name);
592178825Sdfr}
593178825Sdfr
594178825Sdfrstatic struct client *
595178825Sdfrget_client(const char *slave)
596178825Sdfr{
597178825Sdfr    size_t i;
598178825Sdfr    for (i = 0; i < num_clients; i++)
599178825Sdfr	if (strcmp(slave, clients[i]->name) == 0)
600178825Sdfr	    return clients[i];
601178825Sdfr    errx(1, "failed to find client %s", slave);
602178825Sdfr}
603178825Sdfr
604178825Sdfr/*
605178825Sdfr *
606178825Sdfr */
607178825Sdfr
608178825Sdfrstatic int version_flag;
609178825Sdfrstatic int help_flag;
610178825Sdfrstatic char *logfile_str;
611178825Sdfrstatic getarg_strings principals;
612178825Sdfrstatic getarg_strings slaves;
613178825Sdfr
614178825Sdfrstruct getargs args[] = {
615178825Sdfr    { "principals", 0,  arg_strings,	&principals,	"Test principal",
616178825Sdfr      NULL },
617178825Sdfr    { "slaves", 0,  arg_strings,	&slaves,	"Slaves",
618178825Sdfr      NULL },
619178825Sdfr    { "log-file", 0, arg_string,	&logfile_str,	"Logfile",
620178825Sdfr      NULL },
621178825Sdfr    { "version", 0,  arg_flag,		&version_flag,	"Print version",
622178825Sdfr      NULL },
623178825Sdfr    { "help",	 0,  arg_flag,		&help_flag,	NULL,
624178825Sdfr      NULL }
625178825Sdfr};
626178825Sdfr
627178825Sdfrstatic void
628178825Sdfrusage(int ret)
629178825Sdfr{
630178825Sdfr    arg_printusage (args,
631178825Sdfr		    sizeof(args) / sizeof(args[0]),
632178825Sdfr		    NULL,
633178825Sdfr		    "");
634178825Sdfr    exit (ret);
635178825Sdfr}
636178825Sdfr
637178825Sdfrint
638178825Sdfrmain(int argc, char **argv)
639178825Sdfr{
640178825Sdfr    int optidx= 0;
641178825Sdfr    char *user;
642178825Sdfr    char *password;
643178825Sdfr    char ***list, **p;
644178825Sdfr    size_t num_list, i, j, k;
645178825Sdfr    int failed = 0;
646178825Sdfr
647178825Sdfr    setprogname (argv[0]);
648178825Sdfr
649178825Sdfr    if (getarg (args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
650178825Sdfr	usage (1);
651178825Sdfr
652178825Sdfr    if (help_flag)
653178825Sdfr	usage (0);
654178825Sdfr
655178825Sdfr    if (version_flag) {
656178825Sdfr	print_version (NULL);
657178825Sdfr	return 0;
658178825Sdfr    }
659178825Sdfr
660178825Sdfr    if (optidx != argc)
661178825Sdfr	usage (1);
662178825Sdfr
663178825Sdfr    if (principals.num_strings == 0)
664178825Sdfr	errx(1, "no principals");
665178825Sdfr
666178825Sdfr    user = estrdup(principals.strings[0]);
667178825Sdfr    password = strchr(user, ':');
668178825Sdfr    if (password == NULL)
669178825Sdfr	errx(1, "password missing from %s", user);
670178825Sdfr    *password++ = 0;
671178825Sdfr
672178825Sdfr    if (slaves.num_strings == 0)
673178825Sdfr	errx(1, "no principals");
674178825Sdfr
675178825Sdfr    if (logfile_str) {
676178825Sdfr	printf("open logfile %s\n", logfile_str);
677178825Sdfr	logfile = fopen(logfile_str, "w+");
678178825Sdfr	if (logfile == NULL)
679178825Sdfr	    err(1, "failed to open: %s", logfile_str);
680178825Sdfr    }
681178825Sdfr
682178825Sdfr    /*
683178825Sdfr     *
684178825Sdfr     */
685178825Sdfr
686178825Sdfr    list = permutate_all(&slaves, &num_list);
687178825Sdfr
688178825Sdfr    /*
689178825Sdfr     * Set up connection to all clients
690178825Sdfr     */
691178825Sdfr
692178825Sdfr    printf("Connecting to slaves\n");
693178825Sdfr    for (i = 0; i < slaves.num_strings; i++)
694178825Sdfr	connect_client(slaves.strings[i]);
695178825Sdfr
696178825Sdfr    /*
697178825Sdfr     * Test acquire credentials
698178825Sdfr     */
699178825Sdfr
700178825Sdfr    printf("Test acquire credentials\n");
701178825Sdfr    for (i = 0; i < slaves.num_strings; i++) {
702178825Sdfr	int32_t hCred, val;
703178825Sdfr
704178825Sdfr	val = acquire_cred(clients[i], user, password, 1, &hCred);
705178825Sdfr	if (val != GSMERR_OK) {
706178825Sdfr	    warnx("Failed to acquire_cred on host %s: %d",
707178825Sdfr		 clients[i]->moniker, (int)val);
708178825Sdfr	    failed = 1;
709178825Sdfr	} else
710178825Sdfr	    toast_resource(clients[i], hCred);
711178825Sdfr    }
712178825Sdfr
713178825Sdfr    if (failed)
714178825Sdfr	goto out;
715178825Sdfr
716178825Sdfr    /*
717178825Sdfr     * First test if all slaves can build context to them-self.
718178825Sdfr     */
719178825Sdfr
720178825Sdfr    printf("Self context tests\n");
721178825Sdfr    for (i = 0; i < num_clients; i++) {
722178825Sdfr	int32_t hCred, val, delegCred;
723178825Sdfr	int32_t clientC, serverC;
724178825Sdfr	struct client *c = clients[i];
725178825Sdfr
726178825Sdfr	if (c->target_name == NULL)
727178825Sdfr	    continue;
728178825Sdfr
729178825Sdfr	printf("%s connects to self using %s\n",
730178825Sdfr	       c->moniker, c->target_name);
731178825Sdfr
732178825Sdfr	val = acquire_cred(c, user, password, 1, &hCred);
733178825Sdfr	if (val != GSMERR_OK)
734178825Sdfr	    errx(1, "failed to acquire_cred: %d", (int)val);
735178825Sdfr
736178825Sdfr	val = build_context(c, c,
737178825Sdfr			    GSS_C_REPLAY_FLAG|GSS_C_SEQUENCE_FLAG|
738178825Sdfr			    GSS_C_INTEG_FLAG|GSS_C_CONF_FLAG|
739178825Sdfr			    GSS_C_DELEG_FLAG|GSS_C_MUTUAL_FLAG,
740178825Sdfr			    hCred, &clientC, &serverC, &delegCred);
741178825Sdfr	if (val == GSMERR_OK) {
742178825Sdfr	    test_token(c, clientC, c, serverC);
743178825Sdfr	    toast_resource(c, clientC);
744178825Sdfr	    toast_resource(c, serverC);
745178825Sdfr	    if (delegCred)
746178825Sdfr		toast_resource(c, delegCred);
747178825Sdfr	} else {
748178825Sdfr	    warnx("build_context failed: %d", (int)val);
749178825Sdfr	}
750178825Sdfr	/*
751178825Sdfr	 *
752178825Sdfr	 */
753178825Sdfr
754178825Sdfr	val = build_context(c, c,
755178825Sdfr			    GSS_C_INTEG_FLAG|GSS_C_CONF_FLAG,
756178825Sdfr			    hCred, &clientC, &serverC, &delegCred);
757178825Sdfr	if (val == GSMERR_OK) {
758178825Sdfr	    test_token(c, clientC, c, serverC);
759178825Sdfr	    toast_resource(c, clientC);
760178825Sdfr	    toast_resource(c, serverC);
761178825Sdfr	    if (delegCred)
762178825Sdfr		toast_resource(c, delegCred);
763178825Sdfr	} else {
764178825Sdfr	    warnx("build_context failed: %d", (int)val);
765178825Sdfr	}
766178825Sdfr
767178825Sdfr	toast_resource(c, hCred);
768178825Sdfr    }
769178825Sdfr    /*
770178825Sdfr     * Build contexts though all entries in each lists, including the
771178825Sdfr     * step from the last entry to the first, ie treat the list as a
772178825Sdfr     * circle.
773178825Sdfr     *
774178825Sdfr     * Only follow the delegated credential, but test "all"
775178825Sdfr     * flags. (XXX only do deleg|mutual right now.
776178825Sdfr     */
777178825Sdfr
778178825Sdfr    printf("\"All\" permutation tests\n");
779178825Sdfr
780178825Sdfr    for (i = 0; i < num_list; i++) {
781178825Sdfr	int32_t hCred, val, delegCred = 0;
782178825Sdfr	int32_t clientC = 0, serverC = 0;
783178825Sdfr	struct client *client, *server;
784178825Sdfr
785178825Sdfr	p = list[i];
786178825Sdfr
787178825Sdfr	client = get_client(p[0]);
788178825Sdfr
789178825Sdfr	val = acquire_cred(client, user, password, 1, &hCred);
790178825Sdfr	if (val != GSMERR_OK)
791178825Sdfr	    errx(1, "failed to acquire_cred: %d", (int)val);
792178825Sdfr
793178825Sdfr	for (j = 1; j < num_clients + 1; j++) {
794178825Sdfr	    server = get_client(p[j % num_clients]);
795178825Sdfr
796178825Sdfr	    if (server->target_name == NULL)
797178825Sdfr		break;
798178825Sdfr
799178825Sdfr	    for (k = 1; k < j; k++)
800178825Sdfr		printf("\t");
801178825Sdfr	    printf("%s -> %s\n", client->moniker, server->moniker);
802178825Sdfr
803178825Sdfr	    val = build_context(client, server,
804178825Sdfr				GSS_C_REPLAY_FLAG|GSS_C_SEQUENCE_FLAG|
805178825Sdfr				GSS_C_INTEG_FLAG|GSS_C_CONF_FLAG|
806178825Sdfr				GSS_C_DELEG_FLAG|GSS_C_MUTUAL_FLAG,
807178825Sdfr				hCred, &clientC, &serverC, &delegCred);
808178825Sdfr	    if (val != GSMERR_OK) {
809178825Sdfr		warnx("build_context failed: %d", (int)val);
810178825Sdfr		break;
811178825Sdfr	    }
812178825Sdfr
813178825Sdfr	    val = test_token(client, clientC, server, serverC);
814178825Sdfr	    if (val)
815178825Sdfr		break;
816178825Sdfr
817178825Sdfr	    toast_resource(client, clientC);
818178825Sdfr	    toast_resource(server, serverC);
819178825Sdfr	    if (!delegCred) {
820178825Sdfr		warnx("no delegated cred on %s", server->moniker);
821178825Sdfr		break;
822178825Sdfr	    }
823178825Sdfr	    toast_resource(client, hCred);
824178825Sdfr	    hCred = delegCred;
825178825Sdfr	    client = server;
826178825Sdfr	}
827178825Sdfr	if (hCred)
828178825Sdfr	    toast_resource(client, hCred);
829178825Sdfr    }
830178825Sdfr
831178825Sdfr    /*
832178825Sdfr     * Close all connections to clients
833178825Sdfr     */
834178825Sdfr
835178825Sdfrout:
836178825Sdfr    printf("sending goodbye and waiting for log sockets\n");
837178825Sdfr    for (i = 0; i < num_clients; i++) {
838178825Sdfr	goodbye(clients[i]);
839178825Sdfr	if (clients[i]->logsock) {
840178825Sdfr#ifdef ENABLE_PTHREAD_SUPPORT
841178825Sdfr	    pthread_join(&clients[i]->thr, NULL);
842178825Sdfr#else
843178825Sdfr	    waitpid(clients[i]->child, NULL, 0);
844178825Sdfr#endif
845178825Sdfr	}
846178825Sdfr    }
847178825Sdfr
848178825Sdfr    printf("done\n");
849178825Sdfr
850178825Sdfr    return 0;
851178825Sdfr}
852