1/*
2 * Measure performance of SecureTransport handshake
3 *
4 * Written by Doug Mitchell.
5 */
6#include <Security/SecureTransport.h>
7#include <clAppUtils/sslAppUtils.h>
8#include <clAppUtils/ioSock.h>
9#include <security_cdsa_utils/cuFileIo.h>
10#include <utilLib/common.h>
11
12#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16#include <time.h>
17#include <ctype.h>
18#include <sys/param.h>
19#include <CoreFoundation/CoreFoundation.h>
20
21/* default - run both server and client on this machine, port 1200  */
22#define HOST_DEF		"localhost"
23#define PORT_DEF		1200
24
25/* default keychain */
26#define DEFAULT_KC		"localcert"
27
28#define DH_PARAM_FILE	"dhParams_1024.der"
29
30#define GET_MSG			"GET / HTTP/1.0\r\n\r\n"
31
32static void usage(char **argv)
33{
34    printf("Usage: %s s[erver]|c[lient] loops [option ...]\n", argv[0]);
35    printf("Options:\n");
36	printf("   -h hostname (default = %s)\n", HOST_DEF);
37	printf("   -p port (default = %d)\n", PORT_DEF);
38	printf("   -k keychain (default = %s)\n", DEFAULT_KC);
39	printf("   -c cipher (default = RSA/RC4; server side only)\n");
40	printf("      ciphers: r=RSA/RC4; d=RSA/DES; D=RSA/3DES; h=DHA/RC4; "
41					"H=DH/DSS/DES\n");
42	printf("   -v version (t|2|3 default = t(TLS1); server side only)\n");
43	printf("   -w password  (unlock server keychain with password)\n");
44	printf("   -a (enable client authentication)\n");
45	printf("   -r (resumable session enabled; default is disabled)\n");
46	printf("   -n (No client side anchor specification; root is in system KC)\n");
47	printf("   -d (disable cert verify)\n");
48	printf("   -o (Allow hostname spoofing)\n");
49	printf("   -g Send GET msg (needs for talking to real servers)\n");
50	printf("   -V (verbose)\n");
51    exit(1);
52}
53
54#include <signal.h>
55
56void sigpipe(int sig)
57{
58	fflush(stdin);
59	printf("***SIGPIPE***\n");
60}
61
62int main(int argc, char **argv)
63{
64	/* user-spec'd variables */
65	unsigned		loops;
66	char 			*kcName = DEFAULT_KC;
67	int 			port = PORT_DEF;
68	char 			*hostName = HOST_DEF;
69	SSLCipherSuite 	cipherSuite = SSL_RSA_WITH_RC4_128_SHA;
70	SSLProtocol 	prot = kTLSProtocol1Only;
71	char 			password[200];
72	bool 			clientAuthEnable = false;
73	bool 			isServer = false;
74	bool			diffieHellman = false;
75	bool 			verbose = false;
76	bool			resumeEnable = false;
77	bool			setClientAnchor = true;
78	bool			certVerifyEnable = true;
79	bool			checkHostName = true;
80	bool			sendGet = false;
81
82	otSocket 		listenSock = 0;			// for server only
83	CFArrayRef 		myCerts = NULL;
84
85	signal(SIGPIPE, sigpipe);
86	if(argc < 3) {
87		usage(argv);
88	}
89	password[0] = 0;
90	switch(argv[1][0]) {
91		case 's':
92			isServer = true;
93			break;
94		case 'c':
95			isServer = false;
96			break;
97		default:
98			usage(argv);
99	}
100	loops = atoi(argv[2]);
101	if(loops == 0) {
102		usage(argv);
103	}
104
105	extern int optind;
106	extern char *optarg;
107	int arg;
108	optind = 3;
109	while ((arg = getopt(argc, argv, "h:p:k:x:c:v:w:b:aVrndog")) != -1) {
110		switch (arg) {
111			case 'h':
112				hostName = optarg;
113				break;
114			case 'p':
115				port = atoi(optarg);
116				break;
117			case 'k':
118				kcName = optarg;
119				break;
120			case 'c':
121				if(!isServer) {
122					printf("***Specify cipherSuite on server side.\n");
123					exit(1);
124				}
125				switch(optarg[0]) {
126					case 'r':
127						cipherSuite = SSL_RSA_WITH_RC4_128_SHA;
128						break;
129					case 'd':
130						cipherSuite = SSL_RSA_WITH_DES_CBC_SHA;
131						break;
132					case 'D':
133						cipherSuite = SSL_RSA_WITH_3DES_EDE_CBC_SHA;
134						break;
135					case 'h':
136						cipherSuite = SSL_DH_anon_WITH_RC4_128_MD5;
137						diffieHellman = true;
138						break;
139					case 'H':
140						cipherSuite = SSL_DHE_DSS_WITH_DES_CBC_SHA;
141						diffieHellman = true;
142						break;
143					default:
144						usage(argv);
145				}
146				break;
147			case 'v':
148				if(!isServer) {
149					printf("***Specify protocol on server side.\n");
150					exit(1);
151				}
152				switch(optarg[0]) {
153					case 't':
154						prot = kTLSProtocol1Only;
155						break;
156					case '2':
157						prot = kSSLProtocol2;
158						break;
159					case '3':
160						prot = kSSLProtocol3Only;
161						break;
162					default:
163						usage(argv);
164				}
165				break;
166			case 'w':
167				strcpy(password, optarg);
168				break;
169			case 'a':
170				clientAuthEnable = true;
171				break;
172			case 'V':
173				verbose = true;
174				break;
175			case 'r':
176				resumeEnable = true;
177				break;
178			case 'n':
179				setClientAnchor = false;
180				break;
181			case 'd':
182				certVerifyEnable = false;
183				break;
184			case 'o':
185				checkHostName = false;
186				break;
187			case 'g':
188				sendGet = true;
189				break;
190			default:
191				usage(argv);
192		}
193	}
194
195	/* gather Diffie-Hellman params from cwd */
196	if(JAGUAR_BUILD && diffieHellman) {
197		printf("***SOrry, DIffie Hellman not available in this config.\n");
198		exit(1);
199	}
200	unsigned char *dhParams = NULL;
201	unsigned dhParamsLen = 0;
202	if(diffieHellman && isServer) {
203		if(readFile(DH_PARAM_FILE, &dhParams, &dhParamsLen)) {
204			printf("***Error reading Diffie-Hellman Params. Prepare to "
205				"wait for a minute during SSL handshake.\n");
206		}
207	}
208
209	/*
210	 * Open keychain; both sides use the same one.
211	 */
212	OSStatus ortn;
213	SecKeychainRef certKc = NULL;
214	if(isServer || clientAuthEnable || setClientAnchor) {
215		ortn = SecKeychainOpen(kcName, &certKc);
216		if(ortn) {
217			printf("Error opening keychain %s (%d); aborting.\n",
218				kcName, (int)ortn);
219			exit(1);
220		}
221		if(password[0]) {
222			ortn = SecKeychainUnlock(certKc, strlen(password), password, true);
223			if(ortn) {
224				printf("SecKeychainUnlock returned %lu\n", ortn);
225				/* oh well */
226			}
227		}
228	}
229
230	/* just do this once */
231	if(clientAuthEnable || isServer || setClientAnchor) {
232		myCerts = sslKcRefToCertArray(certKc, CSSM_FALSE, CSSM_TRUE, NULL);
233		if(myCerts == NULL) {
234			exit(1);
235		}
236	}
237
238	/* server sets up listen port just once */
239	if(isServer) {
240		printf("...listening for client connection on port %d\n", port);
241		ortn = ListenForClients(port, 0, &listenSock);
242		if(ortn) {
243			printf("...error establishing a listen socket. Aborting.\n");
244			exit(1);
245		}
246	}
247
248	CFAbsoluteTime setupTotal = 0;
249	CFAbsoluteTime handShakeTotal = 0;
250
251	for(unsigned loop=0; loop<loops; loop++) {
252		otSocket peerSock = 0;
253		PeerSpec peerId;
254
255		if(isServer) {
256			ortn = AcceptClientConnection(listenSock, &peerSock, &peerId);
257			if(ortn) {
258				printf("...error listening for connection. Aborting.\n");
259				exit(1);
260			}
261		}
262		else {
263			/* client side */
264			if(verbose) {
265				printf("...connecting to host %s at port %d\n", hostName, port);
266			}
267			ortn = MakeServerConnection(hostName, port, 0, &peerSock,
268				&peerId);
269			if(ortn) {
270				printf("...error connecting to server %s. Aborting.\n",
271					hostName);
272				exit(1);
273			}
274		}
275
276		/* start timing SSL setup */
277		CFAbsoluteTime setupStart = CFAbsoluteTimeGetCurrent();
278
279		SSLContextRef ctx;
280		ortn = SSLNewContext(isServer, &ctx);
281		if(ortn) {
282			printSslErrStr("SSLNewContext", ortn);
283			exit(1);
284		}
285		ortn = SSLSetIOFuncs(ctx, SocketRead, SocketWrite);
286		if(ortn) {
287			printSslErrStr("SSLSetIOFuncs", ortn);
288			exit(1);
289		}
290		ortn = SSLSetConnection(ctx, peerSock);
291		if(ortn) {
292			printSslErrStr("SSLSetConnection", ortn);
293			exit(1);
294		}
295		if(checkHostName) {
296			ortn = SSLSetPeerDomainName(ctx, hostName, strlen(hostName) + 1);
297			if(ortn) {
298				printSslErrStr("SSLSetPeerDomainName", ortn);
299				exit(1);
300			}
301		}
302		if(resumeEnable) {
303			ortn = SSLSetPeerID(ctx, &peerId, sizeof(PeerSpec));
304			if(ortn) {
305				printSslErrStr("SSLSetPeerID", ortn);
306				exit(1);
307			}
308		}
309		if(!certVerifyEnable) {
310			/*
311			 * Do this before setting up certs to allow for optimization
312			 * on server side (setting valid cert without verifying them)
313			 */
314			ortn = SSLSetEnableCertVerify(ctx, false);
315			if(ortn) {
316				printSslErrStr("SSLSetPeerID", ortn);
317				exit(1);
318			}
319		}
320
321		/*
322		 * Server/client specific setup.
323		 *
324		 * Client uses the same keychain as server, but it uses it for
325		 * sslAddTrustedRoots() instead of getSslCerts() and
326		 * SSLSetCertificate().
327		 */
328		if(clientAuthEnable || isServer) {
329			if(!certVerifyEnable) {
330				/* don't bother...this is heavyweight */
331				ortn = addIdentityAsTrustedRoot(ctx, myCerts);
332				if(ortn) {
333					exit(1);
334				}
335			}
336			ortn = SSLSetCertificate(ctx, myCerts);
337			if(ortn) {
338				printSslErrStr("SSLSetCertificate", ortn);
339				exit(1);
340			}
341		}
342		if(isServer) {
343			SSLAuthenticate auth;
344			if(clientAuthEnable) {
345				auth = kAlwaysAuthenticate;
346			}
347			else {
348				auth = kNeverAuthenticate;
349			}
350			ortn = SSLSetClientSideAuthenticate(ctx, auth);
351			if(ortn) {
352				printSslErrStr("SSLSetClientSideAuthenticate", ortn);
353				exit(1);
354			}
355			ortn = SSLSetEnabledCiphers(ctx, &cipherSuite, 1);
356			if(ortn) {
357				printSslErrStr("SSLSetEnabledCiphers", ortn);
358				exit(1);
359			}
360			#if 0
361			/* FIXME why does this fail on Jaguar? */
362			ortn = SSLSetProtocolVersion(ctx, prot);
363			if(ortn) {
364				printSslErrStr("SSLSetProtocolVersion", ortn);
365				exit(1);
366			}
367			#endif
368			#if !JAGUAR_BUILD
369			if(dhParams != NULL) {
370				ortn = SSLSetDiffieHellmanParams(ctx, dhParams, dhParamsLen);
371				if(ortn) {
372					printSslErrStr("SSLSetDiffieHellmanParams", ortn);
373					exit(1);
374				}
375			}
376			#endif
377		}
378		else {
379			/* client setup */
380			if(!clientAuthEnable && setClientAnchor) {
381				/* We're not presenting a cert; trust the server certs */
382				bool foundOne;
383				if(certKc == NULL) {
384					printf("sslAddTrustedRoots screwup\n");
385					exit(1);
386				}
387				ortn = sslAddTrustedRoots(ctx, certKc, &foundOne);
388				if(ortn) {
389					printSslErrStr("sslAddTrustedRoots", ortn);
390					exit(1);
391				}
392			}
393		}
394
395		/*
396		 * Context setup complete. Start timing handshake.
397		 */
398		CFAbsoluteTime hshakeStart = CFAbsoluteTimeGetCurrent();
399		do {
400			ortn = SSLHandshake(ctx);
401		} while (ortn == errSSLWouldBlock);
402		if(ortn) {
403			printSslErrStr("SSLHandshake", ortn);
404			exit(1);
405		}
406		CFAbsoluteTime hshakeEnd = CFAbsoluteTimeGetCurrent();
407
408		/* snag these before data xfer possibly shuts down connection */
409		SSLProtocol	negVersion;
410		SSLCipherSuite negCipher;
411		SSLClientCertificateState certState;		// RETURNED
412
413		SSLGetNegotiatedCipher(ctx, &negCipher);
414		SSLGetNegotiatedProtocolVersion(ctx, &negVersion);
415		SSLGetClientCertificateState(ctx, &certState);
416
417		/*
418		 * Shut down. Server does the SSLClose while client tries to read. Client gets
419		 * a errSSLClosedGraceful then does its SSLCLose.
420		 */
421		if(!isServer) {
422			char data[2];
423			size_t actRead;
424			bool done = false;
425			if(sendGet) {
426				ortn = SSLWrite(ctx, GET_MSG, strlen(GET_MSG), &actRead);
427				if(ortn) {
428					printSslErrStr("SSLWrite", ortn);
429				}
430			}
431			do {
432				ortn = SSLRead(ctx, data, 2, &actRead);
433				switch(ortn) {
434					case errSSLClosedGraceful:
435						done = true;
436						break;
437					case noErr:
438						/* try again */
439						break;
440					default:
441						printf("Unexpected rtn on client SSLRead(); bytesRead %u\n",
442							(unsigned)actRead);
443						printSslErrStr("SSLRead", ortn);
444						done = true;
445						/* ah, keep going */
446						break;
447				}
448			} while(!done);
449		}
450		/* shut down channel */
451		ortn = SSLClose(ctx);
452		if(ortn) {
453			printSslErrStr("SSLCLose", ortn);
454			exit(1);
455		}
456
457		/* how'd we do? */
458		if(verbose) {
459			printf("SSL version          : %s\n",
460				sslGetProtocolVersionString(negVersion));
461			printf("CipherSuite          : %s\n",
462				sslGetCipherSuiteString(negCipher));
463			printf("Client Cert State    : %s\n",
464					sslGetClientCertStateString(certState));
465			printf("SSLContext setup     : %f s\n", hshakeStart - setupStart);
466			printf("SSL Handshake        : %f s\n", hshakeEnd - hshakeStart);
467		}
468		setupTotal     += (hshakeStart - setupStart);
469		handShakeTotal += (hshakeEnd - hshakeStart);
470	}
471
472	printf("\n");
473	printf("SSL setup     avg %f s\n", setupTotal / loops);
474	printf("SSL handshake avg %f s\n", handShakeTotal / loops);
475	return 0;
476}
477
478
479
480