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