1/*
2 * Copyright (c) 2008-2012 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24/*
25 * Trivial SSL server example, using SecureTransport / OS X version.
26 *
27 * Written by Doug Mitchell.
28 */
29
30#include <Security/SecureTransport.h>
31#include <Security/SecureTransportPriv.h>
32#include "sslAppUtils.h"
33#include "ioSock.h"
34#include "fileIo.h"
35
36#include <Security/SecBase.h>
37#include <stdio.h>
38#include <stdlib.h>
39#include <string.h>
40#include <time.h>
41#include <ctype.h>
42#include <sys/param.h>
43
44#include <Security/Security.h>
45#include <Security/SecCertificatePriv.h>
46
47#include <CoreFoundation/CoreFoundation.h>
48#include "printCert.h"
49
50#if NO_SERVER
51#include <securityd/spi.h>
52#endif
53
54/* Set true when PR-3074739 is merged to TOT */
55#define SET_DH_PARAMS_ENABLE		1
56
57/* true when using SSLCopyPeerCertificates() per Radar 3311892 */
58#define USE_COPY_PEER_CERTS		1
59
60/*
61 * Defaults, overridable by user.
62 */
63#define SERVER_MESSAGE  "HTTP/1.0 200 OK\015\012Content-Type: text/html\015\012\015\012" \
64	"<HTML><HEAD><TITLE>SecureTransport Test Server</TITLE></HEAD>" \
65	"<BODY><H2>Secure connection established.</H2>" \
66	"Message from the 'sslServer' sample application.\015\012</BODY>" \
67	"</HTML>\015\012"
68
69/* For ease of debugging, pick a non-privileged port */
70#define DEFAULT_PORT     1200
71// #define DEFAULT_PORT     443
72
73#define DEFAULT_HOST	"localhost"
74
75#define DEFAULT_KC		"certkc"
76
77static void usage(char **argv)
78{
79    printf("Usage: %s [option ...]\n", argv[0]);
80    printf("Options:\n");
81	printf("   P=port      Port to listen on; default is %d\n", DEFAULT_PORT);
82	printf("   k=keychain  Contains server cert and keys.\n");
83	printf("   y=keychain  Encryption-only cert and keys.\n");
84    printf("   e           Allow Expired Certs\n");
85    printf("   r           Allow any root cert\n");
86    printf("   E           Allow Expired Roots\n");
87	printf("   x           Disable Cert Verification\n");
88	printf("   f=fileBase  Write Peer Certs to fileBase*\n");
89	printf("   c           Display peer certs\n");
90	printf("   d           Display received data\n");
91	printf("   C=cipherSuite (e=40-bit d=DES D=40-bit DES 3=3DES 4=RC4 $=40-bit RC4\n"
92		   "                  2=RC2 a=AES128 A=AES256 h=DH H=Anon DH r=DHE/RSA s=DH/DSS\n"
93		   "                  n=RSA/NULL\n");
94	printf("   2           SSLv2 only (default is best fit)\n");
95	printf("   3           SSLv3 only (default is best fit)\n");
96	printf("   t           TLSv1 only (default is best fit)\n");
97	printf("   o           TLSv1, SSLv3 use kSSLProtocol__X__Only\n");
98	printf("   g={prot...} Specify legal protocols; prot = any combo of [23t]\n");
99	printf("   T=[nrsj]    Verify client cert state = "
100							"none/requested/sent/rejected\n");
101	printf("   R           Disable resumable session support\n");
102	printf("   i=timeout   Session cache timeout\n");
103	printf("   u=[nat]     Authentication: n=never; a=always; t=try\n");
104	printf("   b           Non-blocking I/O\n");
105	printf("   a fileNmae  Add fileName to list of trusted roots\n");
106	printf("   A fileName  fileName is ONLY trusted root\n");
107	printf("   U filename  Add filename to acceptable DNList (multiple times OK)\n");
108	printf("   D filename  Diffie-Hellman parameters from filename\n");
109	printf("   z=password  Unlock server keychain with password.\n");
110	printf("   H           Do SecIdentityRef search instead of specific keychain\n");
111	printf("   M           Complete cert chain (default assumes that our identity is root)\n");
112	printf("   4           Disable anonymous ciphers\n");
113	printf("   p           Pause after each phase\n");
114	printf("   l[=loops]   Loop, performing multiple transactions\n");
115	printf("   q           Quiet/diagnostic mode (site names and errors only)\n");
116	printf("   h           Help\n");
117    exit(1);
118}
119
120/* snag a copy of current connection's peer certs so we can
121 * examine them later after the connection is closed */
122static OSStatus copyPeerCerts(
123	SSLContext 	*ctx,
124	CFArrayRef	*peerCerts)		// mallocd & RETURNED
125{
126	#if USE_COPY_PEER_CERTS
127	OSStatus ortn = SSLCopyPeerCertificates(ctx, peerCerts);
128	#else
129	OSStatus ortn = SSLGetPeerCertificates(ctx, peerCerts);
130	#endif
131	if(ortn) {
132		printf("***Error obtaining peer certs: %s\n",
133			sslGetSSLErrString(ortn));
134	}
135	return ortn;
136}
137
138/* free the cert array obtained via SSLGetPeerCertificates() */
139static void	freePeerCerts(
140	CFArrayRef			peerCerts)
141{
142	if(peerCerts == NULL) {
143		return;
144	}
145
146	#if USE_COPY_PEER_CERTS
147
148	/* Voila! Problem fixed. */
149	CFRelease(peerCerts);
150	return;
151
152	#else
153
154	CFIndex numCerts;
155	SecCertificateRef certData;
156	CFIndex i;
157
158	numCerts = CFArrayGetCount(peerCerts);
159	for(i=0; i<numCerts; i++) {
160		certData = (SecCertificateRef)CFArrayGetValueAtIndex(peerCerts, i);
161		CFRelease(certData);
162	}
163	CFRelease(peerCerts);
164	#endif
165}
166
167/* print reply received from server */
168static void dumpAscii(
169	uint8_t *rcvBuf,
170	uint32_t len)
171{
172	char *cp = (char *)rcvBuf;
173	uint32_t i;
174	char c;
175
176	for(i=0; i<len; i++) {
177		c = *cp++;
178		if(c == '\0') {
179			break;
180		}
181		switch(c) {
182			case '\n':
183				printf("\\n");
184				break;
185			case '\r':
186				printf("\\r");
187				break;
188			default:
189				if(isprint(c) && (c != '\n')) {
190					printf("%c", c);
191				}
192				else {
193					printf("<%02X>", ((unsigned)c) & 0xff);
194				}
195			break;
196		}
197
198	}
199	printf("\n");
200}
201
202static void doPause(const char *prompt) {
203	if(prompt) {
204		printf("%s. ", prompt);
205	}
206	fpurge(stdin);
207	printf("Continue (n/anything)? ");
208	char c = getchar();
209	if(c == 'n') {
210		exit(0);
211	}
212}
213
214/*
215 * Perform one SSL diagnostic server-side session. Returns nonzero on error.
216 * Normally no output to stdout except initial "waiting for connection" message,
217 * unless there is a really screwed up error (i.e., something not directly related
218 * to the SSL connection).
219 */
220#define RCV_BUF_SIZE		256
221
222static OSStatus sslServe(
223	otSocket				listenSock,
224	unsigned short			portNum,
225	SSLProtocol				tryVersion,			// only used if acceptedProts NULL
226	const char				*acceptedProts,
227	CFArrayRef				serverCerts,		// required
228	char					*password,			// optional
229	CFArrayRef				encryptServerCerts,	// optional
230	bool				allowExpired,
231	bool				allowAnyRoot,
232	bool				allowExpiredRoot,
233	bool				disableCertVerify,
234	char					*anchorFile,
235	bool				replaceAnchors,
236	char					cipherRestrict,		// '2', 'd'. etc...'\0' for no
237												//   restriction
238	SSLAuthenticate			authenticate,
239	unsigned char			*dhParams,			// optional D-H parameters
240	unsigned				dhParamsLen,
241	CFArrayRef				acceptableDNList,	// optional
242	bool				resumableEnable,
243	uint32_t					sessionCacheTimeout,// optional
244	bool				disableAnonCiphers,
245	bool				silent,				// no stdout
246	bool				pause,
247	SSLProtocol				*negVersion,		// RETURNED
248	SSLCipherSuite			*negCipher,			// RETURNED
249	SSLClientCertificateState *certState,		// RETURNED
250	Boolean					*sessionWasResumed,	// RETURNED
251	unsigned char			*sessionID,			// mallocd by caller, RETURNED
252	size_t					*sessionIDLength,	// RETURNED
253	CFArrayRef				*peerCerts,			// mallocd & RETURNED
254	char					**argv)
255{
256	otSocket			acceptSock;
257    PeerSpec            peerId;
258    OSStatus            ortn;
259    SSLContextRef       ctx = NULL;
260    size_t              length;
261    uint8_t               rcvBuf[RCV_BUF_SIZE];
262	const char *outMsg = SERVER_MESSAGE;
263
264    *negVersion = kSSLProtocolUnknown;
265    *negCipher = SSL_NULL_WITH_NULL_NULL;
266    *peerCerts = NULL;
267
268	#if IGNORE_SIGPIPE
269	signal(SIGPIPE, sigpipe);
270	#endif
271
272	/* first wait for a connection */
273	if(!silent) {
274		printf("Waiting for client connection on port %u...", portNum);
275		fflush(stdout);
276	}
277	ortn = AcceptClientConnection(listenSock, &acceptSock, &peerId);
278    if(ortn) {
279    	printf("AcceptClientConnection returned %d; aborting\n", (int)ortn);
280    	return ortn;
281    }
282
283	/*
284	 * Set up a SecureTransport session.
285	 * First the standard calls.
286	 */
287	ortn = SSLNewContext(true, &ctx);
288	if(ortn) {
289		printSslErrStr("SSLNewContext", ortn);
290		goto cleanup;
291	}
292	ortn = SSLSetIOFuncs(ctx, SocketRead, SocketWrite);
293	if(ortn) {
294		printSslErrStr("SSLSetIOFuncs", ortn);
295		goto cleanup;
296	}
297	ortn = SSLSetConnection(ctx, (SSLConnectionRef)acceptSock);
298	if(ortn) {
299		printSslErrStr("SSLSetConnection", ortn);
300		goto cleanup;
301	}
302
303	/* have to do these options befor setting server certs */
304	if(allowExpired) {
305		ortn = SSLSetAllowsExpiredCerts(ctx, true);
306		if(ortn) {
307			printSslErrStr("SSLSetAllowExpiredCerts", ortn);
308			goto cleanup;
309		}
310	}
311	if(allowAnyRoot) {
312		ortn = SSLSetAllowsAnyRoot(ctx, true);
313		if(ortn) {
314			printSslErrStr("SSLSetAllowAnyRoot", ortn);
315			goto cleanup;
316		}
317	}
318
319	if(anchorFile) {
320		ortn = sslAddTrustedRoot(ctx, anchorFile, replaceAnchors);
321		if(ortn) {
322			printf("***Error obtaining anchor file %s\n", anchorFile);
323			goto cleanup;
324		}
325	}
326	if(serverCerts != NULL) {
327		if(anchorFile == NULL) {
328			/* no specific anchors, so assume we want to trust this one */
329			ortn = addIdentityAsTrustedRoot(ctx, serverCerts);
330			if(ortn) {
331				goto cleanup;
332			}
333		}
334		ortn = SSLSetCertificate(ctx, serverCerts);
335		if(ortn) {
336			printSslErrStr("SSLSetCertificate", ortn);
337			goto cleanup;
338		}
339	}
340	if(encryptServerCerts) {
341		ortn = SSLSetEncryptionCertificate(ctx, encryptServerCerts);
342		if(ortn) {
343			printSslErrStr("SSLSetEncryptionCertificate", ortn);
344			goto cleanup;
345		}
346	}
347	if(allowExpiredRoot) {
348		ortn = SSLSetAllowsExpiredRoots(ctx, true);
349		if(ortn) {
350			printSslErrStr("SSLSetAllowsExpiredRoots", ortn);
351			goto cleanup;
352		}
353	}
354	if(disableCertVerify) {
355		ortn = SSLSetEnableCertVerify(ctx, false);
356		if(ortn) {
357			printSslErrStr("SSLSetEnableCertVerify", ortn);
358			goto cleanup;
359		}
360	}
361
362	/*
363	 * SecureTransport options.
364	 */
365	if(acceptedProts) {
366		ortn = SSLSetProtocolVersionEnabled(ctx, kSSLProtocolAll, false);
367		if(ortn) {
368			printSslErrStr("SSLSetProtocolVersionEnabled(all off)", ortn);
369			goto cleanup;
370		}
371		for(const char *cp = acceptedProts; *cp; cp++) {
372			SSLProtocol prot = kSSLProtocolUnknown;
373			switch(*cp) {
374				case '2':
375					prot = kSSLProtocol2;
376					break;
377				case '3':
378					prot = kSSLProtocol3;
379					break;
380				case 't':
381					prot = kTLSProtocol1;
382					break;
383				default:
384					usage(argv);
385			}
386			ortn = SSLSetProtocolVersionEnabled(ctx, prot, true);
387			if(ortn) {
388				printSslErrStr("SSLSetProtocolVersionEnabled", ortn);
389				goto cleanup;
390			}
391		}
392	}
393	else {
394		ortn = SSLSetProtocolVersion(ctx, tryVersion);
395		if(ortn) {
396			printSslErrStr("SSLSetProtocolVersion", ortn);
397			goto cleanup;
398		}
399	}
400	if(resumableEnable) {
401		ortn = SSLSetPeerID(ctx, &peerId, sizeof(PeerSpec));
402		if(ortn) {
403			printSslErrStr("SSLSetPeerID", ortn);
404			goto cleanup;
405		}
406	}
407	if(cipherRestrict != '\0') {
408		ortn = sslSetCipherRestrictions(ctx, cipherRestrict);
409		if(ortn) {
410			goto cleanup;
411		}
412	}
413	if(authenticate != kNeverAuthenticate) {
414		ortn = SSLSetClientSideAuthenticate(ctx, authenticate);
415		if(ortn) {
416			printSslErrStr("SSLSetClientSideAuthenticate", ortn);
417			goto cleanup;
418		}
419	}
420	if(dhParams) {
421		ortn = SSLSetDiffieHellmanParams(ctx, dhParams, dhParamsLen);
422		if(ortn) {
423			printSslErrStr("SSLSetDiffieHellmanParams", ortn);
424			goto cleanup;
425		}
426	}
427	if(sessionCacheTimeout) {
428		ortn = SSLSetSessionCacheTimeout(ctx, sessionCacheTimeout);
429		if(ortn) {
430			printSslErrStr("SSLSetSessionCacheTimeout", ortn);
431			goto cleanup;
432		}
433	}
434	if(disableAnonCiphers) {
435		ortn = SSLSetAllowAnonymousCiphers(ctx, false);
436		if(ortn) {
437			printSslErrStr("SSLSetAllowAnonymousCiphers", ortn);
438			goto cleanup;
439		}
440		/* quickie test of the getter */
441		Boolean e;
442		ortn = SSLGetAllowAnonymousCiphers(ctx, &e);
443		if(ortn) {
444			printSslErrStr("SSLGetAllowAnonymousCiphers", ortn);
445			goto cleanup;
446		}
447		if(e) {
448			printf("***SSLGetAllowAnonymousCiphers() returned true; expected false\n");
449			ortn = errSecIO;
450			goto cleanup;
451		}
452	}
453/* XXX/cs
454	if(acceptableDNList) {
455		ortn = SSLSetCertificateAuthorities(ctx, acceptableDNList, TRUE);
456		if(ortn) {
457			printSslErrStr("SSLSetCertificateAuthorities", ortn);
458			goto cleanup;
459		}
460	}
461*/
462	/* end options */
463
464	if(pause) {
465		doPause("SSLContext initialized");
466	}
467
468	/* Perform SSL/TLS handshake */
469    do
470    {   ortn = SSLHandshake(ctx);
471		if((ortn == errSSLWouldBlock) && !silent) {
472			/* keep UI responsive */
473			sslOutputDot();
474		}
475    } while (ortn == errSSLWouldBlock);
476
477	/* this works even if handshake failed due to cert chain invalid */
478	copyPeerCerts(ctx, peerCerts);
479
480	SSLGetClientCertificateState(ctx, certState);
481	SSLGetNegotiatedCipher(ctx, negCipher);
482	SSLGetNegotiatedProtocolVersion(ctx, negVersion);
483	*sessionIDLength = MAX_SESSION_ID_LENGTH;
484	SSLGetResumableSessionInfo(ctx, sessionWasResumed, sessionID,
485		sessionIDLength);
486
487	if(!silent) {
488		printf("\n");
489	}
490    if(ortn) {
491    	goto cleanup;
492    }
493	if(pause) {
494		doPause("SSLContext handshake complete");
495	}
496
497	/* wait for one complete line or user says they've had enough */
498	while(ortn == errSecSuccess) {
499		length = sizeof(rcvBuf);
500		ortn = SSLRead(ctx, rcvBuf, length, &length);
501		if(length == 0) {
502			/* keep UI responsive */
503			sslOutputDot();
504		}
505		else {
506			/* print what we have */
507			printf("client request: ");
508			dumpAscii(rcvBuf, length);
509		}
510		if(pause) {
511			/* allow user to bail */
512			char resp;
513
514			fpurge(stdin);
515			printf("\nMore client request (y/anything): ");
516			resp = getchar();
517			if(resp != 'y') {
518				break;
519			}
520		}
521
522		/* poor person's line completion scan */
523		for(unsigned i=0; i<length; i++) {
524			if((rcvBuf[i] == '\n') || (rcvBuf[i] == '\r')) {
525				/* a labelled break would be nice here.... */
526				goto serverResp;
527			}
528		}
529		if (ortn == errSSLWouldBlock) {
530			ortn = errSecSuccess;
531		}
532	}
533
534serverResp:
535	if(pause) {
536		doPause("Client GET msg received");
537	}
538
539	/* send out canned response */
540	length = strlen(outMsg);
541	ortn = SSLWrite(ctx, outMsg, length, &length);
542	if(ortn) {
543		printSslErrStr("SSLWrite", ortn);
544	}
545	if(pause) {
546		doPause("Server response sent");
547	}
548cleanup:
549	/*
550	 * always do close, even on error - to flush outgoing write queue
551	 */
552	OSStatus cerr = SSLClose(ctx);
553	if(ortn == errSecSuccess) {
554		ortn = cerr;
555	}
556	if(acceptSock) {
557		endpointShutdown(acceptSock);
558	}
559	if(ctx) {
560	    SSLDisposeContext(ctx);
561	}
562	/* FIXME - dispose of serverCerts */
563	return ortn;
564}
565
566static void showPeerCerts(
567	CFArrayRef			peerCerts,
568	bool			verbose)
569{
570	CFIndex numCerts;
571	SecCertificateRef certRef;
572	CFIndex i;
573
574	if(peerCerts == NULL) {
575		return;
576	}
577	numCerts = CFArrayGetCount(peerCerts);
578	for(i=0; i<numCerts; i++) {
579		certRef = (SecCertificateRef)CFArrayGetValueAtIndex(peerCerts, i);
580		printf("\n================== Server Cert %lu ===================\n\n", i);
581		print_cert(certRef, verbose);
582		printf("\n=============== End of Server Cert %lu ===============\n", i);
583	}
584}
585
586static void writePeerCerts(
587	CFArrayRef			peerCerts,
588	const char			*fileBase)
589{
590	CFIndex numCerts;
591	SecCertificateRef certRef;
592	CFIndex i;
593	char fileName[100];
594
595	if(peerCerts == NULL) {
596		return;
597	}
598	numCerts = CFArrayGetCount(peerCerts);
599	for(i=0; i<numCerts; i++) {
600		sprintf(fileName, "%s%02d.cer", fileBase, (int)i);
601		certRef = (SecCertificateRef)CFArrayGetValueAtIndex(peerCerts, i);
602		writeFile(fileName, SecCertificateGetBytePtr(certRef),
603			SecCertificateGetLength(certRef));
604	}
605	printf("...wrote %lu certs to fileBase %s\n", numCerts, fileBase);
606}
607
608static void showSSLResult(
609	SSLProtocol			tryVersion,
610	char				*acceptedProts,
611	OSStatus			err,
612	SSLProtocol			negVersion,
613	SSLCipherSuite		negCipher,
614	Boolean				sessionWasResumed,
615	unsigned char		*sessionID,
616	size_t				sessionIDLength,
617	CFArrayRef			peerCerts,
618	bool			displayPeerCerts,
619	SSLClientCertificateState	certState,
620	char				*fileBase)		// non-NULL: write certs to file
621{
622	CFIndex numPeerCerts;
623
624	printf("\n");
625	if(acceptedProts) {
626		printf("   Allowed SSL versions   : %s\n", acceptedProts);
627	}
628	else {
629		printf("   Attempted  SSL version : %s\n",
630			sslGetProtocolVersionString(tryVersion));
631	}
632	printf("   Result                 : %s\n", sslGetSSLErrString(err));
633	printf("   Negotiated SSL version : %s\n",
634		sslGetProtocolVersionString(negVersion));
635	printf("   Negotiated CipherSuite : %s\n",
636		sslGetCipherSuiteString(negCipher));
637	if(certState != kSSLClientCertNone) {
638		printf("   Client Cert State      : %s\n",
639			sslGetClientCertStateString(certState));
640	}
641	printf("   Resumed Session        : ");
642	if(sessionWasResumed) {
643		for(unsigned dex=0; dex<sessionIDLength; dex++) {
644			printf("%02X ", sessionID[dex]);
645			if(((dex % 8) == 7) && (dex != (sessionIDLength - 1))) {
646				printf("\n                            ");
647			}
648		}
649		printf("\n");
650	}
651	else {
652		printf("NOT RESUMED\n");
653	}
654	if(peerCerts == NULL) {
655		numPeerCerts = 0;
656	}
657	else {
658		numPeerCerts = CFArrayGetCount(peerCerts);
659	}
660	printf("   Number of peer certs : %lu\n", numPeerCerts);
661	if(numPeerCerts != 0) {
662		if(displayPeerCerts) {
663			showPeerCerts(peerCerts, false);
664		}
665		if(fileBase != NULL) {
666			writePeerCerts(peerCerts, fileBase);
667		}
668	}
669	printf("\n");
670}
671
672static int verifyClientCertState(
673	bool					verifyCertState,
674	SSLClientCertificateState	expectState,
675	SSLClientCertificateState	gotState)
676{
677	if(!verifyCertState) {
678		return 0;
679	}
680	if(expectState == gotState) {
681		return 0;
682	}
683	printf("***Expected clientCertState %s; got %s\n",
684		sslGetClientCertStateString(expectState),
685		sslGetClientCertStateString(gotState));
686	return 1;
687}
688
689int main(int argc, char **argv)
690{
691	OSStatus            err;
692	int					arg;
693	char				fullFileBase[100];
694	SSLProtocol			negVersion;
695	SSLCipherSuite		negCipher;
696	Boolean				sessionWasResumed;
697	unsigned char		sessionID[MAX_SESSION_ID_LENGTH];
698	size_t				sessionIDLength;
699	CFArrayRef			peerCerts = NULL;
700	char				*argp;
701	otSocket			listenSock;
702	CFArrayRef			serverCerts = nil;		// required
703	CFArrayRef			encryptCerts = nil;		// optional
704	SecKeychainRef		serverKc = nil;
705	SecKeychainRef		encryptKc = nil;
706	int 				loopNum;
707	int					errCount = 0;
708	SSLClientCertificateState certState;		// obtained from sslServe
709
710	/* user-spec'd parameters */
711	unsigned short		portNum = DEFAULT_PORT;
712	bool			allowExpired = false;
713	bool			allowAnyRoot = false;
714	char				*fileBase = NULL;
715	bool			displayRxData = false;
716	bool			displayCerts = false;
717	char				cipherRestrict = '\0';
718	SSLProtocol			attemptProt = kTLSProtocol1;
719	bool			protXOnly = false;	// kSSLProtocol3Only,
720												//    kTLSProtocol1Only
721	char				*acceptedProts = NULL;	// "23t" ==> SSLSetProtocolVersionEnabled
722	bool			quiet = false;
723	bool			resumableEnable = true;
724	bool			pause = false;
725	char				*keyChainName = NULL;
726	char				*encryptKeyChainName = NULL;
727	int					loops = 1;
728	SSLAuthenticate		authenticate = kNeverAuthenticate;
729	bool			nonBlocking = false;
730	bool			allowExpiredRoot = false;
731	bool			disableCertVerify = false;
732	char				*anchorFile = NULL;
733	bool			replaceAnchors = false;
734	bool			vfyCertState = false;
735	SSLClientCertificateState expectCertState = kSSLClientCertNone;
736	char				*password = NULL;
737	char				*dhParamsFile = NULL;
738	unsigned char		*dhParams = NULL;
739	unsigned			dhParamsLen = 0;
740	bool			doIdSearch = false;
741	bool			completeCertChain = false;
742	uint32_t				sessionCacheTimeout = 0;
743	bool			disableAnonCiphers = false;
744	CFMutableArrayRef	acceptableDNList = NULL;
745
746	for(arg=1; arg<argc; arg++) {
747		argp = argv[arg];
748		switch(argp[0]) {
749			case 'P':
750				portNum = atoi(&argp[2]);
751				break;
752			case 'k':
753				keyChainName = &argp[2];
754				break;
755			case 'y':
756				encryptKeyChainName = &argp[2];
757				break;
758			case 'e':
759				allowExpired = true;
760				break;
761			case 'E':
762				allowExpiredRoot = true;
763				break;
764			case 'x':
765				disableCertVerify = true;
766				break;
767			case 'a':
768				if(++arg == argc)  {
769					/* requires another arg */
770					usage(argv);
771				}
772				anchorFile = argv[arg];
773				break;
774			case 'A':
775				if(++arg == argc)  {
776					/* requires another arg */
777					usage(argv);
778				}
779				anchorFile = argv[arg];
780				replaceAnchors = true;
781				break;
782			case 'T':
783				if(argp[1] != '=') {
784					usage(argv);
785				}
786				vfyCertState = true;
787				switch(argp[2]) {
788					case 'n':
789						expectCertState = kSSLClientCertNone;
790						break;
791					case 'r':
792						expectCertState = kSSLClientCertRequested;
793						break;
794					case 's':
795						expectCertState = kSSLClientCertSent;
796						break;
797					case 'j':
798						expectCertState = kSSLClientCertRejected;
799						break;
800					default:
801						usage(argv);
802				}
803				break;
804			case 'r':
805				allowAnyRoot = true;
806				break;
807			case 'd':
808				displayRxData = true;
809				break;
810			case 'c':
811				displayCerts = true;
812				break;
813			case 'f':
814				fileBase = &argp[2];
815				break;
816			case 'C':
817				cipherRestrict = argp[2];
818				break;
819			case '2':
820				attemptProt = kSSLProtocol2;
821				break;
822			case '3':
823				attemptProt = kSSLProtocol3;
824				break;
825			case 't':
826				attemptProt = kTLSProtocol1;
827				break;
828			case 'o':
829				protXOnly = true;
830				break;
831			case 'g':
832				if(argp[1] != '=') {
833					usage(argv);
834				}
835				acceptedProts = &argp[2];
836				break;
837			case 'R':
838				resumableEnable = false;
839				break;
840			case 'b':
841				nonBlocking = true;
842				break;
843			case 'u':
844				if(argp[1] != '=') {
845					usage(argv);
846				}
847				switch(argp[2]) {
848					case 'a': authenticate = kAlwaysAuthenticate; break;
849					case 'n': authenticate = kNeverAuthenticate; break;
850					case 't': authenticate = kTryAuthenticate; break;
851					default: usage(argv);
852				}
853				break;
854			case 'D':
855				if(++arg == argc)  {
856					/* requires another arg */
857					usage(argv);
858				}
859				dhParamsFile = argv[arg];
860				break;
861			case 'z':
862				password = &argp[2];
863				break;
864			case 'H':
865				doIdSearch = true;
866				break;
867			case 'M':
868				completeCertChain = true;
869				break;
870			case 'i':
871				sessionCacheTimeout = atoi(&argp[2]);
872				break;
873			case '4':
874				disableAnonCiphers = true;
875				break;
876			case 'p':
877				pause = true;
878				break;
879			case 'q':
880				quiet = true;
881				break;
882#if 0
883			case 'U':
884				if(++arg == argc)  {
885					/* requires another arg */
886					usage(argv);
887				}
888				if(cspReadFile(argv[arg], &caCert, &caCertLen)) {
889					printf("***Error reading file %s. Aborting.\n", argv[arg]);
890					exit(1);
891				}
892				if(acceptableDNList == NULL) {
893					acceptableDNList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
894				}
895				certData.Data = caCert;
896				certData.Length = caCertLen;
897				ortn = SecCertificateCreateFromData(&certData,
898													CSSM_CERT_X_509v3,
899													CSSM_CERT_ENCODING_DER,
900													&secCert);
901				if(ortn) {
902					cssmPerror("SecCertificateCreateFromData", ortn);
903					exit(1);
904				}
905				CFArrayAppendValue(acceptableDNList, secCert);
906				CFRelease(secCert);
907				break;
908#endif
909			case 'l':
910				if(argp[1] == '\0') {
911					/* no loop count --> loop forever */
912					loops = 0;
913					break;
914				}
915				else if(argp[1] != '=') {
916					usage(argv);
917				}
918				loops = atoi(&argp[2]);
919				break;
920			default:
921				usage(argv);
922		}
923	}
924
925#if NO_SERVER
926# if DEBUG
927    securityd_init();
928# endif
929#endif
930
931	/* get server cert and optional encryption cert as CFArrayRef */
932	if(keyChainName) {
933		serverCerts = getSslCerts(keyChainName, false, completeCertChain,
934			anchorFile, &serverKc);
935		if(serverCerts == nil) {
936			exit(1);
937		}
938	}
939	else
940#if 0
941    if(doIdSearch) {
942		OSStatus ortn = sslIdentityPicker(NULL, anchorFile, true, NULL, &serverCerts);
943		if(ortn) {
944			printf("***IdentitySearch failure; aborting.\n");
945			exit(1);
946		}
947	}
948	if(password) {
949		OSStatus ortn = SecKeychainUnlock(serverKc, strlen(password), password, true);
950		if(ortn) {
951			printf("SecKeychainUnlock returned %d\n", (int)ortn);
952			/* oh well */
953		}
954	}
955	if(encryptKeyChainName) {
956		encryptCerts = getSslCerts(encryptKeyChainName, true, completeCertChain,
957			anchorFile, &encryptKc);
958		if(encryptCerts == nil) {
959			exit(1);
960		}
961	}
962#endif
963	if(protXOnly) {
964		switch(attemptProt) {
965			case kTLSProtocol1:
966				attemptProt = kTLSProtocol1Only;
967				break;
968			case kSSLProtocol3:
969				attemptProt = kSSLProtocol3Only;
970				break;
971			default:
972				break;
973		}
974	}
975#if 0
976	if(dhParamsFile) {
977		int r = cspReadFile(dhParamsFile, &dhParams, &dhParamsLen);
978		if(r) {
979			printf("***Error reading diffie-hellman params from %s; aborting\n",
980				dhParamsFile);
981		}
982	}
983#endif
984
985	/* one-time only server port setup */
986	err = ListenForClients(portNum, nonBlocking, &listenSock);
987	if(err) {
988    	printf("ListenForClients returned %d; aborting\n", (int)err);
989		exit(1);
990	}
991
992	for(loopNum=1; ; loopNum++) {
993		err = sslServe(listenSock,
994			portNum,
995			attemptProt,
996			acceptedProts,
997			serverCerts,
998			password,
999			encryptCerts,
1000			allowExpired,
1001			allowAnyRoot,
1002			allowExpiredRoot,
1003			disableCertVerify,
1004			anchorFile,
1005			replaceAnchors,
1006			cipherRestrict,
1007			authenticate,
1008			dhParams,
1009			dhParamsLen,
1010			acceptableDNList,
1011			resumableEnable,
1012			sessionCacheTimeout,
1013			disableAnonCiphers,
1014			quiet,
1015			pause,
1016			&negVersion,
1017			&negCipher,
1018			&certState,
1019			&sessionWasResumed,
1020			sessionID,
1021			&sessionIDLength,
1022			&peerCerts,
1023			argv);
1024		if(err) {
1025			errCount++;
1026		}
1027		if(!quiet) {
1028			SSLProtocol tryProt = attemptProt;
1029			showSSLResult(tryProt,
1030				acceptedProts,
1031				err,
1032				negVersion,
1033				negCipher,
1034				sessionWasResumed,
1035				sessionID,
1036				sessionIDLength,
1037				peerCerts,
1038				displayCerts,
1039				certState,
1040				fileBase ? fullFileBase : NULL);
1041		}
1042		errCount += verifyClientCertState(vfyCertState, expectCertState,
1043			certState);
1044		freePeerCerts(peerCerts);
1045		if(loops && (loopNum == loops)) {
1046			break;
1047		}
1048	};
1049
1050	endpointShutdown(listenSock);
1051
1052	if(serverKc) {
1053		CFRelease(serverKc);
1054	}
1055	if(encryptKc) {
1056		CFRelease(encryptKc);
1057	}
1058    return errCount;
1059
1060}
1061
1062
1063