/ sslViewer / sslServer.cpp
sslServer.cpp
  1  /*
  2   * Copyright (c) 2008-2013 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  #include <Security/SecureTransport.h>
 30  #include <Security/SecureTransportPriv.h>
 31  #include "sslAppUtils.h"
 32  #include "ioSock.h"
 33  #include "utilities/fileIo.h"
 34  
 35  #include <Security/SecBase.h>
 36  
 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 "SecurityTool/sharedTool/print_cert.h"
 49  
 50  #if NO_SERVER
 51  #include "keychain/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  
 77  static 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 SecIndentityRef 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 */
122  static 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() */
139  static 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 */
168  static void dumpAscii(
169  	uint8_t *rcvBuf, 
170  	size_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  
202  static 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  
222  static 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  	bool				allowExpired,
230  	bool				allowAnyRoot,
231  	bool				allowExpiredRoot,
232  	bool				disableCertVerify,
233  	char					*anchorFile,
234  	bool				replaceAnchors,
235  	char					cipherRestrict,		// '2', 'd'. etc...'\0' for no
236  												//   restriction
237  	SSLAuthenticate			authenticate,
238  	unsigned char			*dhParams,			// optional D-H parameters	
239  	unsigned				dhParamsLen,
240  	CFArrayRef				acceptableDNList,	// optional 
241  	bool				resumableEnable,
242  	uint32_t					sessionCacheTimeout,// optional
243  	bool				disableAnonCiphers,
244  	bool				silent,				// no stdout
245  	bool				pause,
246  	SSLProtocol				*negVersion,		// RETURNED
247  	SSLCipherSuite			*negCipher,			// RETURNED
248  	SSLClientCertificateState *certState,		// RETURNED
249  	Boolean					*sessionWasResumed,	// RETURNED
250  	unsigned char			*sessionID,			// mallocd by caller, RETURNED
251  	size_t					*sessionIDLength,	// RETURNED
252  	CFArrayRef				*peerCerts,			// mallocd & RETURNED
253  	char					**argv)
254  {
255  	otSocket			acceptSock;
256      PeerSpec            peerId;
257      OSStatus            ortn;
258      SSLContextRef       ctx = NULL;
259      size_t              length;
260      uint8_t             rcvBuf[RCV_BUF_SIZE];
261  	const char *outMsg = SERVER_MESSAGE;
262  
263  #pragma clang diagnostic push
264  #pragma clang diagnostic ignored "-Wdeprecated-declarations"
265      *negVersion = kSSLProtocolUnknown;
266      *negCipher = SSL_NULL_WITH_NULL_NULL;
267      *peerCerts = NULL;
268      
269  	#if IGNORE_SIGPIPE
270  	signal(SIGPIPE, sigpipe);
271  	#endif
272  	
273  	/* first wait for a connection */
274  	if(!silent) {
275  		printf("Waiting for client connection on port %u...", portNum);
276  		fflush(stdout);
277  	}
278  	ortn = AcceptClientConnection(listenSock, &acceptSock, &peerId);
279      if(ortn) {
280      	printf("AcceptClientConnection returned %d; aborting\n", (int)ortn);
281      	return ortn;
282      }
283  
284  	/* 
285  	 * Set up a SecureTransport session.
286  	 * First the standard calls.
287  	 */
288  	ortn = SSLNewContext(true, &ctx);
289  	if(ortn) {
290  		printSslErrStr("SSLNewContext", ortn);
291  		goto cleanup;
292  	}
293  	ortn = SSLSetIOFuncs(ctx, SocketRead, SocketWrite);
294  	if(ortn) {
295  		printSslErrStr("SSLSetIOFuncs", ortn);
296  		goto cleanup;
297  	} 
298  	ortn = SSLSetConnection(ctx, (SSLConnectionRef)(intptr_t)acceptSock);
299  	if(ortn) {
300  		printSslErrStr("SSLSetConnection", ortn);
301  		goto cleanup;
302  	}
303  	
304  	/* have to do these options befor setting server certs */
305  	if(allowExpired) {
306  		ortn = SSLSetAllowsExpiredCerts(ctx, true);
307  		if(ortn) {
308  			printSslErrStr("SSLSetAllowExpiredCerts", ortn);
309  			goto cleanup;
310  		}
311  	}
312  	if(allowAnyRoot) {
313  		ortn = SSLSetAllowsAnyRoot(ctx, true);
314  		if(ortn) {
315  			printSslErrStr("SSLSetAllowAnyRoot", ortn);
316  			goto cleanup;
317  		}
318  	}
319  
320  	if(anchorFile) {
321  		ortn = sslAddTrustedRoot(ctx, anchorFile, replaceAnchors);
322  		if(ortn) {
323  			printf("***Error obtaining anchor file %s\n", anchorFile);
324  			goto cleanup;
325  		}
326  	}
327  	if(serverCerts != NULL) {
328  		if(anchorFile == NULL) {
329  			/* no specific anchors, so assume we want to trust this one */
330  			ortn = addIdentityAsTrustedRoot(ctx, serverCerts);
331  			if(ortn) {
332  				goto cleanup;
333  			}
334  		}
335  		ortn = SSLSetCertificate(ctx, serverCerts);
336  		if(ortn) {
337  			printSslErrStr("SSLSetCertificate", ortn);
338  			goto cleanup;
339  		}
340  	}
341  	if(allowExpiredRoot) {
342  		ortn = SSLSetAllowsExpiredRoots(ctx, true);
343  		if(ortn) {
344  			printSslErrStr("SSLSetAllowsExpiredRoots", ortn);
345  			goto cleanup;
346  		}
347  	}
348  	if(disableCertVerify) {
349  		ortn = SSLSetEnableCertVerify(ctx, false);
350  		if(ortn) {
351  			printSslErrStr("SSLSetEnableCertVerify", ortn);
352  			goto cleanup;
353  		}
354  	}
355  	
356  	/* 
357  	 * SecureTransport options.
358  	 */ 
359  	if(acceptedProts) {
360  		ortn = SSLSetProtocolVersionEnabled(ctx, kSSLProtocolAll, false);
361  		if(ortn) {
362  			printSslErrStr("SSLSetProtocolVersionEnabled(all off)", ortn);
363  			goto cleanup;
364  		}
365  		for(const char *cp = acceptedProts; *cp; cp++) {
366  			SSLProtocol prot = kSSLProtocolUnknown;
367  			switch(*cp) {
368  				case '2':
369  					prot = kSSLProtocol2;
370  					break;
371  				case '3':
372  					prot = kSSLProtocol3;
373  					break;
374  				case 't':
375  					prot = kTLSProtocol1;
376  					break;
377  				default:
378  					usage(argv);
379  			}
380  			ortn = SSLSetProtocolVersionEnabled(ctx, prot, true);
381  			if(ortn) {
382  				printSslErrStr("SSLSetProtocolVersionEnabled", ortn);
383  				goto cleanup;
384  			}
385  		}
386  	}
387  	else {
388  		ortn = SSLSetProtocolVersion(ctx, tryVersion);
389  		if(ortn) {
390  			printSslErrStr("SSLSetProtocolVersion", ortn);
391  			goto cleanup;
392  		} 
393  	}
394  	if(resumableEnable) {
395  		ortn = SSLSetPeerID(ctx, &peerId, sizeof(PeerSpec));
396  		if(ortn) {
397  			printSslErrStr("SSLSetPeerID", ortn);
398  			goto cleanup;
399  		}
400  	}
401  	if(cipherRestrict != '\0') {
402  		ortn = sslSetCipherRestrictions(ctx, cipherRestrict);
403  		if(ortn) {
404  			goto cleanup;
405  		}
406  	}
407  	if(authenticate != kNeverAuthenticate) {
408  		ortn = SSLSetClientSideAuthenticate(ctx, authenticate);
409  		if(ortn) {
410  			printSslErrStr("SSLSetClientSideAuthenticate", ortn);
411  			goto cleanup;
412  		}
413  	}
414  	if(dhParams) {
415  		ortn = SSLSetDiffieHellmanParams(ctx, dhParams, dhParamsLen);
416  		if(ortn) {
417  			printSslErrStr("SSLSetDiffieHellmanParams", ortn);
418  			goto cleanup;
419  		}
420  	}
421  	if(sessionCacheTimeout) {
422  		ortn = SSLSetSessionCacheTimeout(ctx, sessionCacheTimeout);
423  		if(ortn) {
424  			printSslErrStr("SSLSetSessionCacheTimeout", ortn);
425  			goto cleanup;
426  		}
427  	}
428  	if(disableAnonCiphers) {
429  		ortn = SSLSetAllowAnonymousCiphers(ctx, false);
430  		if(ortn) {
431  			printSslErrStr("SSLSetAllowAnonymousCiphers", ortn);
432  			goto cleanup;
433  		}
434  		/* quickie test of the getter */
435  		Boolean e;
436  		ortn = SSLGetAllowAnonymousCiphers(ctx, &e);
437  		if(ortn) {
438  			printSslErrStr("SSLGetAllowAnonymousCiphers", ortn);
439  			goto cleanup;
440  		}
441  		if(e) {
442  			printf("***SSLGetAllowAnonymousCiphers() returned true; expected false\n");
443  			ortn = errSecIO;
444  			goto cleanup;
445  		}
446  	}
447  /* XXX/cs
448  	if(acceptableDNList) {
449  		ortn = SSLSetCertificateAuthorities(ctx, acceptableDNList, TRUE);
450  		if(ortn) {
451  			printSslErrStr("SSLSetCertificateAuthorities", ortn);
452  			goto cleanup;
453  		}
454  	}
455  */
456  	/* end options */
457  
458  	if(pause) {
459  		doPause("SSLContext initialized");
460  	}
461  	
462  	/* Perform SSL/TLS handshake */
463      do
464      {   ortn = SSLHandshake(ctx);
465  	    if((ortn == errSSLWouldBlock) && !silent) {
466  	    	/* keep UI responsive */ 
467  	    	sslOutputDot();
468  	    }
469      } while (ortn == errSSLWouldBlock);
470  	
471  	/* this works even if handshake failed due to cert chain invalid */
472  	copyPeerCerts(ctx, peerCerts);
473  
474  	SSLGetClientCertificateState(ctx, certState);
475  	SSLGetNegotiatedCipher(ctx, negCipher);
476  	SSLGetNegotiatedProtocolVersion(ctx, negVersion);
477  	*sessionIDLength = MAX_SESSION_ID_LENGTH;
478  	ortn = SSLGetResumableSessionInfo(ctx, sessionWasResumed, sessionID, sessionIDLength);
479  	
480  	if(!silent) {
481  		printf("\n");
482  	}
483      if(ortn) {
484      	goto cleanup;
485      }
486  	if(pause) {
487  		doPause("SSLContext handshake complete");
488  	}
489  
490  	/* wait for one complete line or user says they've had enough */
491  	while(ortn == errSecSuccess) {
492  	    length = sizeof(rcvBuf);
493  	    ortn = SSLRead(ctx, rcvBuf, length, &length);
494  	    if(length == 0) {
495  	    	/* keep UI responsive */ 
496  	    	sslOutputDot();
497  	    }
498  	    else {
499  	    	/* print what we have */
500  	    	printf("client request: ");
501  	    	dumpAscii(rcvBuf, length);
502  	    }
503  	    if(pause) {
504  	    	/* allow user to bail */
505  	    	char resp;
506  	    	
507  			fpurge(stdin);
508  	    	printf("\nMore client request (y/anything): ");
509  	    	resp = getchar();
510  	    	if(resp != 'y') {
511  	    		break;
512  	    	}
513  	    }
514  	    
515  	    /* poor person's line completion scan */
516  	    for(unsigned i=0; i<length; i++) {
517  	    	if((rcvBuf[i] == '\n') || (rcvBuf[i] == '\r')) {
518  	    		/* a labelled break would be nice here.... */
519  	    		goto serverResp;
520  	    	}
521  	    }
522  	    if (ortn == errSSLWouldBlock) {
523  	        ortn = errSecSuccess;
524  	    }
525  	}
526  	
527  serverResp:
528  	if(pause) {
529  		doPause("Client GET msg received");
530  	}
531  
532  	/* send out canned response */
533  	length = strlen(outMsg);
534   	ortn = SSLWrite(ctx, outMsg, length, &length);
535   	if(ortn) {
536   		printSslErrStr("SSLWrite", ortn);
537   	}
538  	if(pause) {
539  		doPause("Server response sent");
540  	}
541  cleanup:
542  	/*
543  	 * always do close, even on error - to flush outgoing write queue 
544  	 */
545  	OSStatus cerr = SSLClose(ctx);
546  	if(ortn == errSecSuccess) {
547  		ortn = cerr;
548  	}
549  	if(acceptSock) {
550  		endpointShutdown(acceptSock);
551  	}
552  	if(ctx) {
553  	    SSLDisposeContext(ctx);  
554  	}
555  
556  #pragma clang diagnostic pop
557  
558  	/* FIXME - dispose of serverCerts */
559  	return ortn;
560  }
561  
562  static void showPeerCerts(
563  	CFArrayRef			peerCerts,
564  	bool			verbose)
565  {
566  	CFIndex numCerts;
567  	SecCertificateRef certRef;
568  	CFIndex i;
569  	
570  	if(peerCerts == NULL) {
571  		return;
572  	}
573  	numCerts = CFArrayGetCount(peerCerts);
574  	for(i=0; i<numCerts; i++) {
575  		certRef = (SecCertificateRef)CFArrayGetValueAtIndex(peerCerts, i);
576  		printf("\n================== Server Cert %lu ===================\n\n", i);
577          print_cert(certRef, verbose);
578  		printf("\n=============== End of Server Cert %lu ===============\n", i);
579  	}
580  }
581  
582  static void writePeerCerts(
583  	CFArrayRef			peerCerts,
584  	const char			*fileBase)
585  {
586  	CFIndex numCerts;
587  	SecCertificateRef certRef;
588  	CFIndex i;
589  	char fileName[100];
590  	
591  	if(peerCerts == NULL) {
592  		return;
593  	}
594  	numCerts = CFArrayGetCount(peerCerts);
595  	for(i=0; i<numCerts; i++) {
596  		sprintf(fileName, "%s%02d.cer", fileBase, (int)i);
597  		certRef = (SecCertificateRef)CFArrayGetValueAtIndex(peerCerts, i);
598  		writeFileSizet(fileName, SecCertificateGetBytePtr(certRef),
599  			SecCertificateGetLength(certRef));
600  	}
601  	printf("...wrote %lu certs to fileBase %s\n", numCerts, fileBase);
602  }
603  
604  static void showSSLResult(
605  	SSLProtocol			tryVersion,
606  	char				*acceptedProts,
607  	OSStatus			err,
608  	SSLProtocol			negVersion,
609  	SSLCipherSuite		negCipher,
610  	Boolean				sessionWasResumed,	
611  	unsigned char		*sessionID,			
612  	size_t				sessionIDLength,	
613  	CFArrayRef			peerCerts,
614  	bool			displayPeerCerts,
615  	SSLClientCertificateState	certState,
616  	char				*fileBase)		// non-NULL: write certs to file
617  {
618  	CFIndex numPeerCerts;
619  	
620  	printf("\n");
621  	if(acceptedProts) {
622  		printf("   Allowed SSL versions   : %s\n", acceptedProts);
623  	}
624  	else {
625  		printf("   Attempted  SSL version : %s\n", 
626  			sslGetProtocolVersionString(tryVersion));
627  	}
628  	printf("   Result                 : %s\n", sslGetSSLErrString(err));
629  	printf("   Negotiated SSL version : %s\n", 
630  		sslGetProtocolVersionString(negVersion));
631  	printf("   Negotiated CipherSuite : %s\n",
632  		sslGetCipherSuiteString(negCipher));
633  
634  #pragma clang diagnostic push
635  #pragma clang diagnostic ignored "-Wdeprecated-declarations"
636  	if(certState != kSSLClientCertNone) {
637  		printf("   Client Cert State      : %s\n",
638  			sslGetClientCertStateString(certState));
639  	}
640  #pragma clang diagnostic pop
641  
642  	printf("   Resumed Session        : ");
643  	if(sessionWasResumed) {
644  		for(unsigned dex=0; dex<sessionIDLength; dex++) {
645  			printf("%02X ", sessionID[dex]);
646  			if(((dex % 8) == 7) && (dex != (sessionIDLength - 1))) {
647  				printf("\n                            ");
648  			}
649  		}
650  		printf("\n");
651  	}
652  	else {
653  		printf("NOT RESUMED\n");
654  	}
655  	if(peerCerts == NULL) {
656  		numPeerCerts = 0;
657  	}
658  	else {
659  		numPeerCerts = CFArrayGetCount(peerCerts);
660  	}
661  	printf("   Number of peer certs : %lu\n", numPeerCerts);
662  	if(numPeerCerts != 0) {
663  		if(displayPeerCerts) {
664  			showPeerCerts(peerCerts, false);
665  		}
666  		if(fileBase != NULL) {
667  			writePeerCerts(peerCerts, fileBase);
668  		}
669  	}
670  	printf("\n");
671  }
672  
673  static int verifyClientCertState(
674  	bool					verifyCertState,
675  	SSLClientCertificateState	expectState,
676  	SSLClientCertificateState	gotState)
677  {
678  	if(!verifyCertState) {
679  		return 0;
680  	}
681  	if(expectState == gotState) {
682  		return 0;
683  	}
684  	printf("***Expected clientCertState %s; got %s\n",
685  		sslGetClientCertStateString(expectState),
686  		sslGetClientCertStateString(gotState));
687  	return 1;
688  }
689  
690  int main(int argc, char **argv)
691  {   
692      OSStatus            err;
693  	int					arg;
694  	char				fullFileBase[100];
695  	SSLProtocol			negVersion;
696  	SSLCipherSuite		negCipher;
697  	Boolean				sessionWasResumed;
698  	unsigned char		sessionID[MAX_SESSION_ID_LENGTH];
699  	size_t				sessionIDLength;
700  	CFArrayRef			peerCerts = NULL;		
701  	char				*argp;
702  	otSocket			listenSock;
703  	CFArrayRef			serverCerts = nil;		// required
704  	SecKeychainRef		serverKc = nil;
705  	int 				loopNum;
706  	int					errCount = 0;
707  	SSLClientCertificateState certState;		// obtained from sslServe
708  
709  #pragma clang diagnostic push
710  #pragma clang diagnostic ignored "-Wdeprecated-declarations"
711  
712  	/* user-spec'd parameters */
713  	unsigned short		portNum = DEFAULT_PORT;
714  	bool			allowExpired = false;
715  	bool			allowAnyRoot = false;
716  	char				*fileBase = NULL;
717  	bool			displayCerts = false;
718  	char				cipherRestrict = '\0';
719  	SSLProtocol			attemptProt = kTLSProtocol1;
720  	bool			protXOnly = false;	// kSSLProtocol3Only, 
721  												//    kTLSProtocol1Only
722  	char				*acceptedProts = NULL;	// "23t" ==> SSLSetProtocolVersionEnabled
723  	bool			quiet = false;
724  	bool			resumableEnable = true;
725  	bool			pause = false;
726  	char				*keyChainName = 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  	unsigned char		*dhParams = NULL;
738  	unsigned			dhParamsLen = 0;
739  	bool			completeCertChain = false;
740  	uint32_t				sessionCacheTimeout = 0;
741  	bool			disableAnonCiphers = false;
742  	CFMutableArrayRef	acceptableDNList = NULL;
743  
744  	for(arg=1; arg<argc; arg++) {
745  		argp = argv[arg];
746  		switch(argp[0]) {
747  			case 'P':
748  				portNum = atoi(&argp[2]);
749  				break;
750  			case 'k':
751  				keyChainName = &argp[2];
752  				break;
753  			case 'e':
754  				allowExpired = true;
755  				break;
756  			case 'E':
757  				allowExpiredRoot = true;
758  				break;
759  			case 'x':
760  				disableCertVerify = true;
761  				break;
762  			case 'a':
763  				if(++arg == argc)  {
764  					/* requires another arg */
765  					usage(argv);
766  				}
767  				anchorFile = argv[arg];
768  				break;
769  			case 'A':
770  				if(++arg == argc)  {
771  					/* requires another arg */
772  					usage(argv);
773  				}
774  				anchorFile = argv[arg];
775  				replaceAnchors = true;
776  				break;
777  			case 'T':
778  				if(argp[1] != '=') {
779  					usage(argv);
780  				}
781  				vfyCertState = true;
782  				switch(argp[2]) {
783  					case 'n':
784  						expectCertState = kSSLClientCertNone;
785  						break;
786  					case 'r':
787  						expectCertState = kSSLClientCertRequested;
788  						break;
789  					case 's':
790  						expectCertState = kSSLClientCertSent;
791  						break;
792  					case 'j':
793  						expectCertState = kSSLClientCertRejected;
794  						break;
795  					default:
796  						usage(argv);
797  				}
798  				break;
799  			case 'r':
800  				allowAnyRoot = true;
801  				break;
802  			case 'd':
803  				break;
804  			case 'c':
805  				displayCerts = true;
806  				break;
807  			case 'f':
808  				fileBase = &argp[2];
809  				break;
810  			case 'C':
811  				cipherRestrict = argp[2];
812  				break;
813  			case '2':
814  				attemptProt = kSSLProtocol2;
815  				break;
816  			case '3':
817  				attemptProt = kSSLProtocol3;
818  				break;
819  			case 't':
820  				attemptProt = kTLSProtocol1;
821  				break;
822  			case 'o':
823  				protXOnly = true;
824  				break;
825  			case 'g':
826  				if(argp[1] != '=') {
827  					usage(argv);
828  				}
829  				acceptedProts = &argp[2];
830  				break;
831  			case 'R':
832  				resumableEnable = false;
833  				break;
834  			case 'b':
835  				nonBlocking = true;
836  				break;
837  			case 'u':
838  				if(argp[1] != '=') {
839  					usage(argv);
840  				}
841  				switch(argp[2]) {
842  					case 'a': authenticate = kAlwaysAuthenticate; break;
843  					case 'n': authenticate = kNeverAuthenticate; break;
844  					case 't': authenticate = kTryAuthenticate; break;
845  					default: usage(argv);
846  				}
847  				break;
848  			case 'D':
849  				if(++arg == argc)  {
850  					/* requires another arg */
851  					usage(argv);
852  				}
853  				break;
854  			case 'z':
855  				password = &argp[2];
856  				break;
857  			case 'H':
858  				break;
859  			case 'M':
860  				completeCertChain = true;
861  				break;
862  			case 'i':
863  				sessionCacheTimeout = atoi(&argp[2]);
864  				break;
865  			case '4':
866  				disableAnonCiphers = true;
867  				break;
868  			case 'p':
869  				pause = true;
870  				break;
871  			case 'q':
872  				quiet = true;
873  				break;
874  			case 'l':
875  				if(argp[1] == '\0') {
876  					/* no loop count --> loop forever */
877  					loops = 0;
878  					break;
879  				}
880  				else if(argp[1] != '=') {
881  					usage(argv);
882  				}
883  				loops = atoi(&argp[2]);
884  				break;
885  			default:
886  				usage(argv);
887  		}
888  	}
889  
890  #if NO_SERVER
891  # if DEBUG
892      securityd_init(NULL);
893  # endif
894  #endif
895  
896  	/* get server cert and optional encryption cert as CFArrayRef */
897  	if(keyChainName) {
898  		serverCerts = getSslCerts(keyChainName, false, completeCertChain, 
899  			anchorFile, &serverKc);
900  		if(serverCerts == nil) {
901  			exit(1);
902  		}
903  	} else if(protXOnly) {
904  		switch(attemptProt) {
905  			case kTLSProtocol1:
906  				attemptProt = kTLSProtocol1Only;
907  				break;
908  			case kSSLProtocol3:
909  				attemptProt = kSSLProtocol3Only;
910  				break;
911  			default:
912  				break;
913  		}
914  	}
915  
916  #pragma clang diagnostic pop
917  
918  	/* one-time only server port setup */
919  	err = ListenForClients(portNum, nonBlocking, &listenSock);
920  	if(err) {
921      	printf("ListenForClients returned %d; aborting\n", (int)err);
922  		exit(1);
923  	}
924  
925  	for(loopNum=1; ; loopNum++) {
926  		err = sslServe(listenSock,
927  			portNum,
928  			attemptProt,
929  			acceptedProts,
930  			serverCerts,
931  			password,
932  			allowExpired,
933  			allowAnyRoot,
934  			allowExpiredRoot,
935  			disableCertVerify,
936  			anchorFile,
937  			replaceAnchors,
938  			cipherRestrict,
939  			authenticate,
940  			dhParams,
941  			dhParamsLen,
942  			acceptableDNList,
943  			resumableEnable,
944  			sessionCacheTimeout,
945  			disableAnonCiphers,
946  			quiet,
947  			pause,
948  			&negVersion,
949  			&negCipher,
950  			&certState,
951  			&sessionWasResumed,
952  			sessionID,
953  			&sessionIDLength,
954  			&peerCerts,
955  			argv);
956  		if(err) {
957  			errCount++;
958  		}
959  		if(!quiet) {
960  			SSLProtocol tryProt = attemptProt;
961  			showSSLResult(tryProt,
962  				acceptedProts,
963  				err, 
964  				negVersion, 
965  				negCipher, 
966  				sessionWasResumed,
967  				sessionID,
968  				sessionIDLength,
969  				peerCerts,
970  				displayCerts,
971  				certState,
972  				fileBase ? fullFileBase : NULL);
973  		}
974  		errCount += verifyClientCertState(vfyCertState, expectCertState, 
975  			certState);
976  		freePeerCerts(peerCerts);
977  		if(loops && (loopNum == loops)) {
978  			break;
979  		}
980  	};
981  	
982  	endpointShutdown(listenSock);
983  
984  	if(serverKc) {
985  		CFRelease(serverKc);
986  	}
987      return errCount;
988  
989  }
990  
991