1/*
2 * sslEAP - test EAP-FAST style PAC-based session resumption.
3 *
4 * This only works with a debug Security.framework since server side
5 * PAC support is not present in deployment builds.
6 *
7 * Written by Doug Mitchell.
8 */
9#include <Security/SecureTransport.h>
10#include <Security/SecureTransportPriv.h>
11#include <clAppUtils/sslAppUtils.h>
12#include <security_cdsa_utils/cuFileIo.h>
13#include <utilLib/common.h>
14#include <clAppUtils/ringBufferIo.h>
15#include "ringBufferThreads.h"		/* like the ones in clAppUtils, tailored for EAP/PAC */
16
17#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
18#include <stdio.h>
19#include <stdlib.h>
20#include <string.h>
21#include <time.h>
22#include <ctype.h>
23#include <sys/param.h>
24#include <CoreFoundation/CoreFoundation.h>
25#include <security_utilities/devrandom.h>
26
27#define DEFAULT_XFER	1024		/* total xfer size in bytes */
28
29/* we might make these user-tweakable */
30#define DEFAULT_NUM_BUFS	16
31#define DEFAULT_BUF_SIZE	2048		/* in the ring buffers */
32#define DEFAULT_CHUNK		1024		/* bytes to write per SSLWrite() */
33#define SESSION_TICKET_SIZE	512
34
35static void usage(char **argv)
36{
37    printf("Usage: %s [option ...]\n", argv[0]);
38    printf("Options:\n");
39	printf("   -x transferSize   -- default=%d; 0=forever\n", DEFAULT_XFER);
40	printf("   -k keychainName   -- not needed if PAC will be done\n");
41	printf("   -n                -- *NO* PAC\n");
42	printf("   -h hostName       -- force a SSLSetPeerDomainName on client side\n");
43	printf("   -p (pause on error)\n");
44    exit(1);
45}
46
47int main(int argc, char **argv)
48{
49	RingBuffer		serverToClientRing;
50	RingBuffer		clientToServerRing;
51	unsigned		numBufs = DEFAULT_NUM_BUFS;
52	unsigned		bufSize = DEFAULT_BUF_SIZE;
53	unsigned		chunkSize = DEFAULT_CHUNK;
54	unsigned char	clientBuf[DEFAULT_CHUNK];
55	unsigned char	serverBuf[DEFAULT_CHUNK];
56	RingBufferArgs	clientArgs;
57	RingBufferArgs	serverArgs;
58	bool			abortFlag = false;
59	pthread_t		client_thread = NULL;
60	int				result;
61	OSStatus		ortn;
62	unsigned char	sessionTicket[SESSION_TICKET_SIZE];
63	int				ourRtn = 0;
64	CFArrayRef		idArray = NULL;				/* for SSLSetCertificate */
65	CFArrayRef		anchorArray = NULL;			/* trusted roots */
66	char			*hostName = NULL;
67
68	/* user-spec'd variables */
69	char 			*kcName = NULL;
70	unsigned 		xferSize = DEFAULT_XFER;
71	bool			pauseOnError = false;
72	bool			runForever = false;
73	bool			skipPAC = false;
74
75	extern int optind;
76	extern char *optarg;
77	int arg;
78	optind = 1;
79	while ((arg = getopt(argc, argv, "x:c:k:h:np")) != -1) {
80		switch (arg) {
81			case 'x':
82			{
83				unsigned xsize = atoi(optarg);
84				if(xsize == 0) {
85					runForever = true;
86					/* and leave xferSize alone */
87				}
88				else {
89					xferSize = xsize;
90				}
91				break;
92			}
93			case 'k':
94				kcName = optarg;
95				break;
96			case 'n':
97				skipPAC = true;
98				break;
99			case 'h':
100				/* mainly to test EAP session ticket and ServerName simultaneously */
101				hostName = optarg;
102				break;
103			case 'p':
104				pauseOnError = true;
105				break;
106			default:
107				usage(argv);
108		}
109	}
110	if(optind != argc) {
111		usage(argv);
112	}
113
114	/* set up ring buffers */
115	ringBufSetup(&serverToClientRing, "serveToClient", numBufs, bufSize);
116	ringBufSetup(&clientToServerRing, "clientToServe", numBufs, bufSize);
117
118	/* get optional server SecIdentity */
119	if(kcName) {
120		SecKeychainRef	kcRef = NULL;
121		SecCertificateRef anchorCert = NULL;
122		SecIdentityRef	idRef = NULL;
123		idArray = getSslCerts(kcName,
124			CSSM_FALSE,		/* encryptOnly */
125			CSSM_FALSE,		/* completeCertChain */
126			NULL,			/* anchorFile */
127			&kcRef);
128		if(idArray == NULL) {
129			printf("***Can't get signing cert from %s\n", kcName);
130			exit(1);
131		}
132		idRef = (SecIdentityRef)CFArrayGetValueAtIndex(idArray, 0);
133		ortn = SecIdentityCopyCertificate(idRef, &anchorCert);
134		if(ortn) {
135			cssmPerror("SecIdentityCopyCertificate", ortn);
136			exit(1);
137		}
138		anchorArray = CFArrayCreate(NULL, (const void **)&anchorCert,
139				1, &kCFTypeArrayCallBacks);
140		CFRelease(kcRef);
141		CFRelease(anchorCert);
142	}
143
144	/* set up server side */
145	memset(&serverArgs, 0, sizeof(serverArgs));
146	serverArgs.xferSize = xferSize;
147	serverArgs.xferBuf = serverBuf;
148	serverArgs.chunkSize = chunkSize;
149	serverArgs.ringWrite = &serverToClientRing;
150	serverArgs.ringRead = &clientToServerRing;
151	serverArgs.goFlag = &clientArgs.iAmReady;
152	serverArgs.abortFlag = &abortFlag;
153	serverArgs.pauseOnError = pauseOnError;
154	appGetRandomBytes(serverArgs.sharedSecret, SHARED_SECRET_SIZE);
155	if(!skipPAC) {
156		serverArgs.setMasterSecret = true;
157	}
158	serverArgs.idArray = idArray;
159	serverArgs.trustedRoots = anchorArray;
160
161	/* set up client side */
162	memset(&clientArgs, 0, sizeof(clientArgs));
163	clientArgs.xferSize = xferSize;
164	clientArgs.xferBuf = clientBuf;
165	clientArgs.chunkSize = chunkSize;
166	clientArgs.ringWrite = &clientToServerRing;
167	clientArgs.ringRead = &serverToClientRing;
168	clientArgs.goFlag = &serverArgs.iAmReady;
169	clientArgs.abortFlag = &abortFlag;
170	clientArgs.pauseOnError = pauseOnError;
171	memmove(clientArgs.sharedSecret, serverArgs.sharedSecret, SHARED_SECRET_SIZE);
172	clientArgs.hostName = hostName;
173
174	/* for now set up an easily recognizable ticket */
175	for(unsigned dex=0; dex<SESSION_TICKET_SIZE; dex++) {
176		sessionTicket[dex] = dex;
177	}
178	clientArgs.sessionTicket = sessionTicket;
179	clientArgs.sessionTicketLen = SESSION_TICKET_SIZE;
180	/* client always tries setting the master secret in this test */
181	clientArgs.setMasterSecret = true;
182	clientArgs.trustedRoots = anchorArray;
183
184	/* fire up client thread */
185	result = pthread_create(&client_thread, NULL,
186			rbClientThread, &clientArgs);
187	if(result) {
188		printf("***pthread_create returned %d, aborting\n", result);
189		exit(1);
190	}
191
192	/*
193	 * And the server pseudo thread. This returns when all data has been transferred.
194	 */
195	ortn = rbServerThread(&serverArgs);
196
197	if(abortFlag) {
198		printf("***Test aborted.\n");
199		exit(1);
200	}
201
202	printf("\n");
203
204	printf("SSL Protocol Version : %s\n",
205		sslGetProtocolVersionString(serverArgs.negotiatedProt));
206	printf("SSL Cipher           : %s\n",
207		sslGetCipherSuiteString(serverArgs.negotiatedCipher));
208
209	if(skipPAC) {
210		if(clientArgs.sessionWasResumed) {
211			printf("***skipPAC true, but client reported sessionWasResumed\n");
212			ourRtn = -1;
213		}
214		if(serverArgs.sessionWasResumed) {
215			printf("***skipPAC true, but server reported sessionWasResumed\n");
216			ourRtn = -1;
217		}
218		if(ourRtn == 0) {
219			printf("...PAC session attempted by client; refused by server;\n");
220			printf("   Normal session proceeded correctly.\n");
221		}
222	}
223	else {
224		if(!clientArgs.sessionWasResumed) {
225			printf("***client reported !sessionWasResumed\n");
226			ourRtn = -1;
227		}
228		if(!serverArgs.sessionWasResumed) {
229			printf("***server reported !sessionWasResumed\n");
230			ourRtn = -1;
231		}
232		if(memcmp(clientBuf, serverBuf, DEFAULT_CHUNK)) {
233			printf("***Data miscompare***\n");
234			ourRtn = -1;
235		}
236		if(ourRtn == 0) {
237			printf("...PAC session resumed correctly.\n");
238		}
239	}
240	/* FIXME other stuff? */
241
242	return ourRtn;
243}
244
245
246
247