 dba026b5ef
			
		
	
	dba026b5ef
	
	
	
		
			
			On Windows a file descriptor is an int value allocated by the local module instance of the C Run Time Library. A socket handle is a SOCKET value allocated by a Winsock Provider for the requested family and protocol. These two values cannot be mixed and there is no mechanism for converting between the two. The _get_osfhandle() and _open_osfhandle() functions can work with a standard HANDLE (file, pipe, etc) but cannot be used for a SOCKET. The Heimdal krb5_storage_from_fd() routine counted on the osf conversion functions working on SOCKET values. Since they do not any attempt to call krb5_storage_from_fd() on a socket resulted in an assertion being thrown by the C RTL. Another problem is SOCKET value truncation when storing a 64-bit value into a 32-bit int. To address these problems a new krb5_storage_from_socket() routine is introduced. This routine setups a krb5_storage that stores a socket value as a rk_socket_t and provides a set of helper routines that always use network ready functions. The krb5_storage_from_fd() routines no longer use net_read() and net_write() but provide helpers that follow their logic so that pipes can be processed. All call sites that allocate a socket now store the socket as rk_socket_t and call krb5_storage_from_socket(). All locations that previously called the bare close() on a socket value now call rk_closesocket(). Change-Id: I045f775b2a5dbf5cf803751409490bc27fffe597
		
			
				
	
	
		
			964 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			964 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * 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 = 6;
 | |
| 
 | |
|     msg.data = "0123456789abcdef"; /* padded for most enctypes */
 | |
|     msg.length = 32;
 | |
| 
 | |
|     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, *string;
 | |
| 
 | |
|     while (1) {
 | |
|         if (krb5_ret_int32(c->logsock, &cmd))
 | |
| 	    goto out;
 | |
| 
 | |
| 	switch (cmd) {
 | |
| 	case eLogSetMoniker:
 | |
| 	    if (krb5_ret_string(c->logsock, &file))
 | |
| 		goto out;
 | |
| 	    free(file);
 | |
| 	    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);
 | |
| 	    free(file);
 | |
| 	    free(string);
 | |
| 	    if (krb5_store_int32(c->logsock, 0))
 | |
| 		goto out;
 | |
| 	    break;
 | |
| 	default:
 | |
| 	    errx(1, "client send bad log command: %d", (int)cmd);
 | |
| 	}
 | |
|     }
 | |
| out:
 | |
| 
 | |
|     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(res);
 | |
| 
 | |
|     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;
 | |
| }
 |