]> git.saurik.com Git - apple/security.git/blob - SecurityTests/clxutils/sslViewer/SSLViewer.cpp
Security-57031.10.10.tar.gz
[apple/security.git] / SecurityTests / clxutils / sslViewer / SSLViewer.cpp
1 /*
2 * Copyright (c) 2003-2010 Apple Inc. All Rights Reserved.
3 *
4 * SSL viewer tool, SecureTransport / OS X version.
5 */
6
7 #include <Security/SecureTransport.h>
8 #include <Security/SecureTransportPriv.h> // for SSLGetPeerSecTrust
9 #include <Security/SecCertificate.h>
10 #include <clAppUtils/sslAppUtils.h>
11 #include <clAppUtils/ioSock.h>
12 #include <security_cdsa_utils/cuPrintCert.h>
13 #include <utilLib/fileIo.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 <CoreFoundation/CoreFoundation.h>
22
23 #define DEFAULT_GETMSG "GET"
24 #define DEFAULT_PATH "/"
25 #define DEFAULT_GET_SUFFIX "HTTP/1.0\r\n\r\n"
26
27 #define DEFAULT_HOST "www.amazon.com"
28 #define DEFAULT_PORT 443
29
30 #define CFRELEASE(cf) if (cf) { CFRelease(cf); cf = NULL; }
31
32 /* true when using SSLCopyPeerCertificates() per Radar 3311892 */
33 #define USE_COPY_PEER_CERTS 1
34
35 static void usageNorm(char **argv)
36 {
37 printf("Usage: %s [hostname|-] [path] [option ...]\n", argv[0]);
38 printf(" %s hostname [path] [option ...]\n", argv[0]);
39 printf("Specifying '-' for hostname, or no args, uses default of %s.\n",
40 DEFAULT_HOST);
41 printf("Optional path argument must start with leading '/'.\n");
42 printf("Options:\n");
43 printf(" e Allow Expired Certs\n");
44 printf(" E Allow Expired Roots\n");
45 printf(" r Allow any root cert\n");
46 printf(" c Display peer certs\n");
47 printf(" d Display received data\n");
48 printf(" S Display enabled cipher suites\n");
49 printf(" 2 SSLv2 only (default is TLSv1)\n");
50 printf(" 3 SSLv3 only w/SSLv2 enabled (default is TLSv1)\n");
51 printf(" t TLSv1 only w/SSLv2,SSLv3 enabled (this is the default)\n");
52 printf(" L all - TLSv1, SSLv3, SSLv2 (default = TLSv1)\n");
53 printf(" o TLSv1, SSLv3 use kSSLProtocol__X__Only\n");
54 printf(" g={prot...} Specify legal protocols; prot = any combo of"
55 " [23t]\n");
56 printf(" k=keychain Contains (client|server) cert and keys. Optional.\n");
57 printf(" l=loopCount Perform loopCount ops (default = 1)\n");
58 printf(" P=port Default = %d\n", DEFAULT_PORT);
59 printf(" p Pause after each loop\n");
60 printf(" q Quiet/diagnostic mode (site names and errors"
61 " only)\n");
62 printf(" a fileName Add fileName to list of trusted roots\n");
63 printf(" A fileName fileName is ONLY trusted root\n");
64 printf(" Z fileName fileName is a trusted leaf cert\n");
65 printf(" x Disable Cert Verification\n");
66 printf(" z=password Unlock client keychain with password.\n");
67 printf(" 8 Complete cert chains (default is out cert is a root)\n");
68 printf(" s Silent\n");
69 printf(" V Verbose\n");
70 printf(" h Help\n");
71 printf(" hv More, verbose help\n");
72 }
73
74 static void usageVerbose(char **argv)
75 {
76 usageNorm(argv);
77 printf("Obscure Usage:\n");
78 printf(" u kSSLProtocolUnknown only (TLSv1)\n");
79 printf(" M Manual cert verification via "
80 "SecTrustEvaluate\n");
81 printf(" f fileBase Write Peer Certs to fileBase*\n");
82 printf(" D fileBase Write DNList to fileBase*\n");
83 printf(" C=cipherSuite (e=40-bit d=DES D=40-bit DES 3=3DES 4=RC4"
84 "$=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 E=ECDHE F=ECDH\n");
87 printf(" y=keychain Encryption-only cert and keys. Optional.\n");
88 printf(" K Keep connected until server disconnects\n");
89 printf(" n Require closure notify message in TLSv1, "
90 "SSLv3 mode (implies K)\n");
91 printf(" R Disable resumable session support\n");
92 printf(" i=timeout Session cache timeout\n");
93 printf(" b Non-blocking I/O\n");
94 printf(" v Verify negotiated protocol equals attempted\n");
95 printf(" m=[23t] Max protocol supported as specified; implies "
96 "v\n");
97 printf(" T=[nrsj] Verify client cert state = "
98 "none/requested/sent/rejected\n");
99 printf(" H allow hostname spoofing\n");
100 printf(" F=vfyHost Verify certs with specified host name\n");
101 printf(" G=getMsg Specify entire GET, POST, etc.\n");
102 printf(" I Interactive client authentication\n");
103 printf(" N Log handshake timing\n");
104 printf(" 4 Disable anonymous ciphers\n");
105 printf(" 7 Pause only after first loop\n");
106 exit(1);
107 }
108
109 static void usage(char **argv)
110 {
111 usageNorm(argv);
112 exit(1);
113 }
114
115 /*
116 * Arguments to top-level sslPing()
117 */
118 typedef struct {
119 SSLProtocol tryVersion; // only used if acceptedProts NULL
120 // uses SSLSetProtocolVersion
121 char *acceptedProts; // optional, any combo of {2,3,t}
122 // uses SSLSetProtocolVersionEnabled
123 const char *hostName; // e.g., "www.amazon.com"
124 const char *vfyHostName; // use this for cert vfy if non-NULL,
125 // else use hostName
126 unsigned short port;
127 const char *getMsg; // e.g.,
128 // "GET / HTTP/1.0\r\n\r\n"
129 bool allowExpired;
130 bool allowAnyRoot;
131 bool allowExpiredRoot;
132 bool disableCertVerify;
133 bool manualCertVerify;
134 bool dumpRxData; // display server data
135 char cipherRestrict; // '2', 'd'. etc...; '\0' for
136 // no restriction
137 bool keepConnected;
138 bool requireNotify; // require closure notify
139 // in V3 mode
140 bool resumableEnable;
141 bool allowHostnameSpoof;
142 bool nonBlocking;
143 char *anchorFile;
144 char *trustedLeafFile;
145 bool replaceAnchors;
146 bool interactiveAuth;
147 CFArrayRef clientCerts; // optional
148 CFArrayRef encryptClientCerts; // optional
149 uint32 sessionCacheTimeout;// optional
150 bool disableAnonCiphers;
151 bool showCipherSuites;
152 bool quiet; // minimal stdout
153 bool silent; // no stdout
154 bool verbose;
155 SSLProtocol negVersion; // RETURNED
156 SSLCipherSuite negCipher; // RETURNED
157 CFArrayRef peerCerts; // mallocd & RETURNED
158 SecTrustRef peerTrust; // RETURNED
159 SSLClientCertificateState certState; // RETURNED
160 SSLClientAuthenticationType authType; // RETURNED
161 CFArrayRef dnList; // RETURNED
162 char *password; // optional to open clientCerts
163 char **argv;
164 Boolean sessionWasResumed;
165 unsigned char sessionID[MAX_SESSION_ID_LENGTH];
166 size_t sessionIDLength;
167 CFAbsoluteTime handshakeTimeOp; // time for this op
168 CFAbsoluteTime handshakeTimeFirst; // time for FIRST op, not averaged
169 CFAbsoluteTime handshakeTimeTotal; // time for all ops except first
170 unsigned numHandshakes;
171
172 } sslPingArgs;
173
174 #include <signal.h>
175 static void sigpipe(int sig)
176 {
177 fflush(stdin);
178 printf("***SIGPIPE***\n");
179 }
180
181 /*
182 * Start up a CFRunLoop. This is needed to field keychain event callbacks, used
183 * to maintain root cert cache coherency.
184 */
185
186 /* first we need something to register so we *have* a run loop */
187 static OSStatus kcCacheCallback (
188 SecKeychainEvent keychainEvent,
189 SecKeychainCallbackInfo *info,
190 void *context)
191 {
192 return noErr;
193 }
194
195 /* main thread has to wait for this to be set to know a run loop has been set up */
196 static int runLoopInitialized = 0;
197
198 /* this is the thread which actually runs the CFRunLoop */
199 void *cfRunLoopThread(void *arg)
200 {
201 OSStatus ortn = SecKeychainAddCallback(kcCacheCallback,
202 kSecTrustSettingsChangedEventMask, NULL);
203 if(ortn) {
204 printf("registerCacheCallbacks: SecKeychainAddCallback returned %d", (int32_t)ortn);
205 /* Not sure how this could ever happen - maybe if there is no run loop active? */
206 return NULL;
207 }
208 runLoopInitialized = 1;
209 CFRunLoopRun();
210 /* should not be reached */
211 printf("\n*** Hey! CFRunLoopRun() exited!***\n");
212 return NULL;
213 }
214
215 static int startCFRunLoop()
216 {
217 pthread_t runLoopThread;
218
219 int result = pthread_create(&runLoopThread, NULL, cfRunLoopThread, NULL);
220 if(result) {
221 printf("***pthread_create returned %d, aborting\n", result);
222 return -1;
223 }
224 return 0;
225 }
226
227 /*
228 * Snag a copy of current connection's peer certs so we can
229 * examine them later after the connection is closed.
230 * SecureTransport actually does the create and retain for us.
231 */
232 static OSStatus copyPeerCerts(
233 SSLContext *ctx,
234 CFArrayRef *peerCerts) // mallocd & RETURNED
235 {
236 #if USE_COPY_PEER_CERTS
237 OSStatus ortn = SSLCopyPeerCertificates(ctx, peerCerts);
238 #else
239 OSStatus ortn = SSLGetPeerCertificates(ctx, peerCerts);
240 #endif
241 if(ortn) {
242 printf("***Error obtaining peer certs: %s\n",
243 sslGetSSLErrString(ortn));
244 }
245 return ortn;
246 }
247
248 /* free the cert array obtained via SSLGetPeerCertificates() */
249 /* necessary due to a buggy SSLGetPeerCertificates which really should
250 * release its certs after they get added to this array */
251 static void freePeerCerts(
252 CFArrayRef peerCerts)
253 {
254 if(peerCerts == NULL) {
255 return;
256 }
257
258 #if USE_COPY_PEER_CERTS
259
260 /* Voila! Problem fixed. */
261 CFRelease(peerCerts);
262 return;
263
264 #else
265 CFIndex numCerts;
266 SecCertificateRef certData;
267 CFIndex i;
268
269 numCerts = CFArrayGetCount(peerCerts);
270 for(i=0; i<numCerts; i++) {
271 certData = (SecCertificateRef)CFArrayGetValueAtIndex(peerCerts, i);
272 CFRelease(certData);
273 }
274 CFRelease(peerCerts);
275 #endif /* USE_COPY_PEER_CERTS */
276 }
277
278 /*
279 * Manually evaluate session's SecTrustRef.
280 */
281 #define SSL_SEC_TRUST 1
282
283 static OSStatus sslEvaluateTrust(
284 SSLContext *ctx,
285 bool verbose,
286 bool silent,
287 CFArrayRef *peerCerts) // fetched and retained
288 {
289 OSStatus ortn;
290 SecTrustRef secTrust = NULL;
291
292 #if SSL_SEC_TRUST
293 ortn = SSLGetPeerSecTrust(ctx, &secTrust);
294 #else
295 ortn = unimpErr;
296 #endif
297 if(ortn) {
298 printf("\n***Error obtaining peer SecTrustRef: %s\n",
299 sslGetSSLErrString(ortn));
300 return ortn;
301 }
302 if(secTrust == NULL) {
303 /* this is the normal case for resumed sessions, in which
304 * no cert evaluation is performed */
305 if(!silent) {
306 printf("...No SecTrust available - this is a resumed session, right?\n");
307 }
308 return noErr;
309 }
310 SecTrustResultType secTrustResult;
311 ortn = SecTrustEvaluate(secTrust, &secTrustResult);
312 if(ortn) {
313 printf("\n***Error on SecTrustEvaluate: %d\n", (int)ortn);
314 return ortn;
315 }
316 if(verbose) {
317 const char *res = NULL;
318 switch(secTrustResult) {
319 case kSecTrustResultInvalid:
320 res = "kSecTrustResultInvalid"; break;
321 case kSecTrustResultProceed:
322 res = "kSecTrustResultProceed"; break;
323 case kSecTrustResultConfirm:
324 res = "kSecTrustResultConfirm"; break;
325 case kSecTrustResultDeny:
326 res = "kSecTrustResultDeny"; break;
327 case kSecTrustResultUnspecified:
328 res = "kSecTrustResultUnspecified"; break;
329 case kSecTrustResultRecoverableTrustFailure:
330 res = "kSecTrustResultRecoverableTrustFailure"; break;
331 case kSecTrustResultFatalTrustFailure:
332 res = "kSecTrustResultFatalTrustFailure"; break;
333 case kSecTrustResultOtherError:
334 res = "kSecTrustResultOtherError"; break;
335 default:
336 res = "UNKNOWN"; break;
337 }
338 printf("\nSecTrustEvaluate(): secTrustResult %s\n", res);
339 }
340
341 switch(secTrustResult) {
342 case kSecTrustResultUnspecified:
343 /* cert chain valid, no special UserTrust assignments */
344 case kSecTrustResultProceed:
345 /* cert chain valid AND user explicitly trusts this */
346 break;
347 default:
348 printf("\n***SecTrustEvaluate reported secTrustResult %d\n",
349 (int)secTrustResult);
350 ortn = errSSLXCertChainInvalid;
351 break;
352 }
353
354 *peerCerts = NULL;
355
356 /* one more thing - get peer certs in the form of an evidence chain */
357 CSSM_TP_APPLE_EVIDENCE_INFO *dummyEv;
358 OSStatus thisRtn = SecTrustGetResult(secTrust, &secTrustResult,
359 peerCerts, &dummyEv);
360 if(thisRtn) {
361 printSslErrStr("SecTrustGetResult", thisRtn);
362 }
363 #if !USE_COPY_PEER_CERTS
364 else {
365 /* workaround for the fact that SSLGetPeerCertificates()
366 * leaves a retain count on each element in the returned array,
367 * requiring us to do a release on each cert.
368 */
369 CFIndex numCerts = CFArrayGetCount(*peerCerts);
370 for(CFIndex dex=0; dex<numCerts; dex++) {
371 CFRetain(CFArrayGetValueAtIndex(*peerCerts, dex));
372 }
373 }
374 #endif /* !USE_COPY_PEER_CERTS */
375 return ortn;
376 }
377
378 static void sslShowEnabledCipherSuites(
379 SSLContextRef ctx)
380 {
381 OSStatus status;
382 SSLCipherSuite *ciphers;
383 size_t numCiphers, totalCiphers;
384 const char *c;
385 unsigned int i;
386
387 status = SSLGetNumberSupportedCiphers(ctx, &totalCiphers);
388 status = SSLGetNumberEnabledCiphers(ctx, &numCiphers);
389 ciphers = (SSLCipherSuite *)malloc(sizeof(SSLCipherSuite) * numCiphers);
390 status = SSLGetEnabledCiphers(ctx, ciphers, &numCiphers);
391
392 printf(" Total enabled ciphers : %ld of %ld\n", numCiphers, totalCiphers);
393
394 for(i=0; i<numCiphers; i++) {
395 switch(ciphers[i]) {
396 case SSL_NULL_WITH_NULL_NULL: c="SSL_NULL_WITH_NULL_NULL"; break;
397 case SSL_RSA_WITH_NULL_MD5: c="SSL_RSA_WITH_NULL_MD5"; break;
398 case SSL_RSA_WITH_NULL_SHA: c="SSL_RSA_WITH_NULL_SHA"; break;
399 case SSL_RSA_EXPORT_WITH_RC4_40_MD5: c="SSL_RSA_EXPORT_WITH_RC4_40_MD5"; break;
400 case SSL_RSA_WITH_RC4_128_MD5: c="SSL_RSA_WITH_RC4_128_MD5"; break;
401 case SSL_RSA_WITH_RC4_128_SHA: c="SSL_RSA_WITH_RC4_128_SHA"; break;
402 case SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5: c="SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5"; break;
403 case SSL_RSA_WITH_IDEA_CBC_SHA: c="SSL_RSA_WITH_IDEA_CBC_SHA"; break;
404 case SSL_RSA_EXPORT_WITH_DES40_CBC_SHA: c="SSL_RSA_EXPORT_WITH_DES40_CBC_SHA"; break;
405 case SSL_RSA_WITH_DES_CBC_SHA: c="SSL_RSA_WITH_DES_CBC_SHA"; break;
406 case SSL_RSA_WITH_3DES_EDE_CBC_SHA: c="SSL_RSA_WITH_3DES_EDE_CBC_SHA"; break;
407 case SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA: c="SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA"; break;
408 case SSL_DH_DSS_WITH_DES_CBC_SHA: c="SSL_DH_DSS_WITH_DES_CBC_SHA"; break;
409 case SSL_DH_DSS_WITH_3DES_EDE_CBC_SHA: c="SSL_DH_DSS_WITH_3DES_EDE_CBC_SHA"; break;
410 case SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA: c="SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA"; break;
411 case SSL_DH_RSA_WITH_DES_CBC_SHA: c="SSL_DH_RSA_WITH_DES_CBC_SHA"; break;
412 case SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA: c="SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA"; break;
413 case SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA: c="SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA"; break;
414 case SSL_DHE_DSS_WITH_DES_CBC_SHA: c="SSL_DHE_DSS_WITH_DES_CBC_SHA"; break;
415 case SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA: c="SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA"; break;
416 case SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA: c="SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA"; break;
417 case SSL_DHE_RSA_WITH_DES_CBC_SHA: c="SSL_DHE_RSA_WITH_DES_CBC_SHA"; break;
418 case SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA: c="SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA"; break;
419 case SSL_DH_anon_EXPORT_WITH_RC4_40_MD5: c="SSL_DH_anon_EXPORT_WITH_RC4_40_MD5"; break;
420 case SSL_DH_anon_WITH_RC4_128_MD5: c="SSL_DH_anon_WITH_RC4_128_MD5"; break;
421 case SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA: c="SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA"; break;
422 case SSL_DH_anon_WITH_DES_CBC_SHA: c="SSL_DH_anon_WITH_DES_CBC_SHA"; break;
423 case SSL_DH_anon_WITH_3DES_EDE_CBC_SHA: c="SSL_DH_anon_WITH_3DES_EDE_CBC_SHA"; break;
424 case SSL_FORTEZZA_DMS_WITH_NULL_SHA: c="SSL_FORTEZZA_DMS_WITH_NULL_SHA"; break;
425 case SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA:c="SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA"; break;
426
427 /* TLS addenda using AES, per RFC 3268 */
428 case TLS_RSA_WITH_AES_128_CBC_SHA: c="TLS_RSA_WITH_AES_128_CBC_SHA"; break;
429 case TLS_DH_DSS_WITH_AES_128_CBC_SHA: c="TLS_DH_DSS_WITH_AES_128_CBC_SHA"; break;
430 case TLS_DH_RSA_WITH_AES_128_CBC_SHA: c="TLS_DH_RSA_WITH_AES_128_CBC_SHA"; break;
431 case TLS_DHE_DSS_WITH_AES_128_CBC_SHA: c="TLS_DHE_DSS_WITH_AES_128_CBC_SHA"; break;
432 case TLS_DHE_RSA_WITH_AES_128_CBC_SHA: c="TLS_DHE_RSA_WITH_AES_128_CBC_SHA"; break;
433 case TLS_DH_anon_WITH_AES_128_CBC_SHA: c="TLS_DH_anon_WITH_AES_128_CBC_SHA"; break;
434 case TLS_RSA_WITH_AES_256_CBC_SHA: c="TLS_RSA_WITH_AES_256_CBC_SHA"; break;
435 case TLS_DH_DSS_WITH_AES_256_CBC_SHA: c="TLS_DH_DSS_WITH_AES_256_CBC_SHA"; break;
436 case TLS_DH_RSA_WITH_AES_256_CBC_SHA: c="TLS_DH_RSA_WITH_AES_256_CBC_SHA"; break;
437 case TLS_DHE_DSS_WITH_AES_256_CBC_SHA: c="TLS_DHE_DSS_WITH_AES_256_CBC_SHA"; break;
438 case TLS_DHE_RSA_WITH_AES_256_CBC_SHA: c="TLS_DHE_RSA_WITH_AES_256_CBC_SHA"; break;
439 case TLS_DH_anon_WITH_AES_256_CBC_SHA: c="TLS_DH_anon_WITH_AES_256_CBC_SHA"; break;
440
441 /* ECDSA addenda, RFC 4492 */
442 case TLS_ECDH_ECDSA_WITH_NULL_SHA: c="TLS_ECDH_ECDSA_WITH_NULL_SHA"; break;
443 case TLS_ECDH_ECDSA_WITH_RC4_128_SHA: c="TLS_ECDH_ECDSA_WITH_RC4_128_SHA"; break;
444 case TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA: c="TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA"; break;
445 case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA: c="TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA"; break;
446 case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA: c="TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA"; break;
447 case TLS_ECDHE_ECDSA_WITH_NULL_SHA: c="TLS_ECDHE_ECDSA_WITH_NULL_SHA"; break;
448 case TLS_ECDHE_ECDSA_WITH_RC4_128_SHA: c="TLS_ECDHE_ECDSA_WITH_RC4_128_SHA"; break;
449 case TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA: c="TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA"; break;
450 case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: c="TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA"; break;
451 case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: c="TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA"; break;
452 case TLS_ECDH_RSA_WITH_NULL_SHA: c="TLS_ECDH_RSA_WITH_NULL_SHA"; break;
453 case TLS_ECDH_RSA_WITH_RC4_128_SHA: c="TLS_ECDH_RSA_WITH_RC4_128_SHA"; break;
454 case TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA: c="TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA"; break;
455 case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA: c="TLS_ECDH_RSA_WITH_AES_128_CBC_SHA"; break;
456 case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA: c="TLS_ECDH_RSA_WITH_AES_256_CBC_SHA"; break;
457 case TLS_ECDHE_RSA_WITH_NULL_SHA: c="TLS_ECDHE_RSA_WITH_NULL_SHA"; break;
458 case TLS_ECDHE_RSA_WITH_RC4_128_SHA: c="TLS_ECDHE_RSA_WITH_RC4_128_SHA"; break;
459 case TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: c="TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA"; break;
460 case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: c="TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA"; break;
461 case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: c="TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA"; break;
462 case TLS_ECDH_anon_WITH_NULL_SHA: c="TLS_ECDH_anon_WITH_NULL_SHA"; break;
463 case TLS_ECDH_anon_WITH_RC4_128_SHA: c="TLS_ECDH_anon_WITH_RC4_128_SHA"; break;
464 case TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA: c="TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA"; break;
465 case TLS_ECDH_anon_WITH_AES_128_CBC_SHA: c="TLS_ECDH_anon_WITH_AES_128_CBC_SHA"; break;
466 case TLS_ECDH_anon_WITH_AES_256_CBC_SHA: c="TLS_ECDH_anon_WITH_AES_256_CBC_SHA"; break;
467
468 /*
469 * Tags for SSL 2 cipher kinds which are not specified
470 * for SSL 3.
471 */
472 case SSL_RSA_WITH_RC2_CBC_MD5: c="SSL_RSA_WITH_RC2_CBC_MD5"; break;
473 case SSL_RSA_WITH_IDEA_CBC_MD5: c="SSL_RSA_WITH_IDEA_CBC_MD5"; break;
474 case SSL_RSA_WITH_DES_CBC_MD5: c="SSL_RSA_WITH_DES_CBC_MD5"; break;
475 case SSL_RSA_WITH_3DES_EDE_CBC_MD5: c="SSL_RSA_WITH_3DES_EDE_CBC_MD5"; break;
476 case SSL_NO_SUCH_CIPHERSUITE:
477 default:
478 c="SSL_NO_SUCH_CIPHERSUITE"; break;
479 }
480 printf(" %s\n", c);
481 fflush(stdout);
482 }
483 free(ciphers);
484 }
485
486 /* print reply received from server, safely */
487 static void dumpAscii(
488 uint8_t *rcvBuf,
489 uint32_t len)
490 {
491 char *cp = (char *)rcvBuf;
492 uint32_t i;
493 char c;
494
495 for(i=0; i<len; i++) {
496 c = *cp++;
497 if(c == '\0') {
498 break;
499 }
500 switch(c) {
501 case '\n':
502 printf("\\n");
503 break;
504 case '\r':
505 printf("\\r");
506 break;
507 default:
508 if(isprint(c) && (c != '\n')) {
509 printf("%c", c);
510 }
511 else {
512 printf("<%02X>", ((unsigned)c) & 0xff);
513 }
514 break;
515 }
516
517 }
518 printf("\n");
519 }
520
521 /*
522 * Perform one SSL diagnostic session. Returns nonzero on error. Normally no
523 * output to stdout except initial "connecting to" message, unless there
524 * is a really screwed up error (i.e., something not directly related
525 * to the SSL connection).
526 */
527 #define RCV_BUF_SIZE 256
528
529 static OSStatus sslPing(
530 sslPingArgs *pargs)
531 {
532 PeerSpec peerId;
533 otSocket sock = 0;
534 OSStatus ortn;
535 SSLContextRef ctx = NULL;
536 size_t length;
537 size_t actLen;
538 uint8_t rcvBuf[RCV_BUF_SIZE];
539 CFAbsoluteTime startHandshake;
540 CFAbsoluteTime endHandshake;
541
542 pargs->negVersion = kSSLProtocolUnknown;
543 pargs->negCipher = SSL_NULL_WITH_NULL_NULL;
544 pargs->peerCerts = NULL;
545
546 /* first make sure requested server is there */
547 ortn = MakeServerConnection(pargs->hostName, pargs->port, pargs->nonBlocking,
548 &sock, &peerId);
549 if(ortn) {
550 printf("MakeServerConnection returned %d; aborting\n", (int)ortn);
551 return ortn;
552 }
553 if(pargs->verbose) {
554 printf("...connected to server; starting SecureTransport\n");
555 }
556
557 /*
558 * Set up a SecureTransport session.
559 * First the standard calls.
560 */
561 ortn = SSLNewContext(false, &ctx);
562 if(ortn) {
563 printSslErrStr("SSLNewContext", ortn);
564 goto cleanup;
565 }
566 ortn = SSLSetIOFuncs(ctx, SocketRead, SocketWrite);
567 if(ortn) {
568 printSslErrStr("SSLSetIOFuncs", ortn);
569 goto cleanup;
570 }
571 ortn = SSLSetConnection(ctx, (SSLConnectionRef)sock);
572 if(ortn) {
573 printSslErrStr("SSLSetConnection", ortn);
574 goto cleanup;
575 }
576 SSLConnectionRef getConn;
577 ortn = SSLGetConnection(ctx, &getConn);
578 if(ortn) {
579 printSslErrStr("SSLGetConnection", ortn);
580 goto cleanup;
581 }
582 if(getConn != (SSLConnectionRef)sock) {
583 printf("***SSLGetConnection error\n");
584 ortn = paramErr;
585 goto cleanup;
586 }
587 if(!pargs->allowHostnameSpoof) {
588 /* if this isn't set, it isn't checked by AppleX509TP */
589 const char *vfyHost = pargs->hostName;
590 if(pargs->vfyHostName) {
591 /* generally means we're expecting an error */
592 vfyHost = pargs->vfyHostName;
593 }
594 ortn = SSLSetPeerDomainName(ctx, vfyHost, strlen(vfyHost));
595 if(ortn) {
596 printSslErrStr("SSLSetPeerDomainName", ortn);
597 goto cleanup;
598 }
599 }
600
601 /*
602 * SecureTransport options.
603 */
604 if(pargs->acceptedProts) {
605 ortn = SSLSetProtocolVersionEnabled(ctx, kSSLProtocolAll, false);
606 if(ortn) {
607 printSslErrStr("SSLSetProtocolVersionEnabled(all off)", ortn);
608 goto cleanup;
609 }
610 for(const char *cp = pargs->acceptedProts; *cp; cp++) {
611 SSLProtocol prot;
612 switch(*cp) {
613 case '2':
614 prot = kSSLProtocol2;
615 break;
616 case '3':
617 prot = kSSLProtocol3;
618 break;
619 case 't':
620 prot = kTLSProtocol1;
621 break;
622 default:
623 usage(pargs->argv);
624 }
625 ortn = SSLSetProtocolVersionEnabled(ctx, prot, true);
626 if(ortn) {
627 printSslErrStr("SSLSetProtocolVersionEnabled", ortn);
628 goto cleanup;
629 }
630 }
631 }
632 else {
633 ortn = SSLSetProtocolVersion(ctx, pargs->tryVersion);
634 if(ortn) {
635 printSslErrStr("SSLSetProtocolVersion", ortn);
636 goto cleanup;
637 }
638 SSLProtocol getVers;
639 ortn = SSLGetProtocolVersion(ctx, &getVers);
640 if(ortn) {
641 printSslErrStr("SSLSetProtocolVersion", ortn);
642 goto cleanup;
643 }
644 if(getVers != pargs->tryVersion) {
645 printf("***SSLGetProtocolVersion screwup: try %s get %s\n",
646 sslGetProtocolVersionString(pargs->tryVersion),
647 sslGetProtocolVersionString(getVers));
648 ortn = paramErr;
649 goto cleanup;
650 }
651 }
652 if(pargs->resumableEnable) {
653 const void *rtnId = NULL;
654 size_t rtnIdLen = 0;
655
656 ortn = SSLSetPeerID(ctx, &peerId, sizeof(PeerSpec));
657 if(ortn) {
658 printSslErrStr("SSLSetPeerID", ortn);
659 goto cleanup;
660 }
661 /* quick test of the get fcn */
662 ortn = SSLGetPeerID(ctx, &rtnId, &rtnIdLen);
663 if(ortn) {
664 printSslErrStr("SSLGetPeerID", ortn);
665 goto cleanup;
666 }
667 if((rtnId == NULL) || (rtnIdLen != sizeof(PeerSpec))) {
668 printf("***SSLGetPeerID screwup\n");
669 }
670 else if(memcmp(&peerId, rtnId, rtnIdLen) != 0) {
671 printf("***SSLGetPeerID data mismatch\n");
672 }
673 }
674 if(pargs->allowExpired) {
675 ortn = SSLSetAllowsExpiredCerts(ctx, true);
676 if(ortn) {
677 printSslErrStr("SSLSetAllowExpiredCerts", ortn);
678 goto cleanup;
679 }
680 }
681 if(pargs->allowExpiredRoot) {
682 ortn = SSLSetAllowsExpiredRoots(ctx, true);
683 if(ortn) {
684 printSslErrStr("SSLSetAllowsExpiredRoots", ortn);
685 goto cleanup;
686 }
687 }
688 if(pargs->disableCertVerify) {
689 ortn = SSLSetEnableCertVerify(ctx, false);
690 if(ortn) {
691 printSslErrStr("SSLSetEnableCertVerify", ortn);
692 goto cleanup;
693 }
694 }
695 if(pargs->allowAnyRoot) {
696 ortn = SSLSetAllowsAnyRoot(ctx, true);
697 if(ortn) {
698 printSslErrStr("SSLSetAllowAnyRoot", ortn);
699 goto cleanup;
700 }
701 }
702 if(pargs->cipherRestrict != '\0') {
703 ortn = sslSetCipherRestrictions(ctx, pargs->cipherRestrict);
704 if(ortn) {
705 goto cleanup;
706 }
707 }
708 if(pargs->anchorFile) {
709 ortn = sslAddTrustedRoot(ctx, pargs->anchorFile, pargs->replaceAnchors);
710 if(ortn) {
711 printf("***Error obtaining anchor file %s\n", pargs->anchorFile);
712 goto cleanup;
713 }
714 }
715 if(pargs->trustedLeafFile) {
716 SecCertificateRef leafCertRef = NULL;
717 CFMutableArrayRef leafCerts = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
718 /* sslReadAnchor is a misnomer; it just creates a SecCertificateRef from a file */
719 ortn = sslReadAnchor(pargs->trustedLeafFile, &leafCertRef);
720 if (!ortn) {
721 CFArrayAppendValue(leafCerts, leafCertRef);
722 CFRelease(leafCertRef);
723 ortn = SSLSetTrustedLeafCertificates(ctx, leafCerts);
724 CFRelease(leafCerts);
725 }
726 if(ortn) {
727 goto cleanup;
728 }
729 }
730 if(pargs->interactiveAuth) {
731 /* we want to get errSSLServerAuthCompleted from SSLHandshake on server auth completion */
732 SSLSetSessionOption(ctx, kSSLSessionOptionBreakOnServerAuth, true);
733 /* we want to get errSSLClientCertRequested from SSLHandshake on client auth request */
734 SSLSetSessionOption(ctx, kSSLSessionOptionBreakOnCertRequested, true);
735 }
736 else if(pargs->clientCerts) {
737 CFArrayRef dummy;
738 if(pargs->anchorFile == NULL) {
739 /* assume this is a root we want to implicitly trust */
740 ortn = addIdentityAsTrustedRoot(ctx, pargs->clientCerts);
741 if(ortn) {
742 goto cleanup;
743 }
744 }
745 ortn = SSLSetCertificate(ctx, pargs->clientCerts);
746 if(ortn) {
747 printSslErrStr("SSLSetCertificate", ortn);
748 goto cleanup;
749 }
750 /* quickie test for this new function */
751 ortn = SSLGetCertificate(ctx, &dummy);
752 if(ortn) {
753 printSslErrStr("SSLGetCertificate", ortn);
754 goto cleanup;
755 }
756 if(dummy != pargs->clientCerts) {
757 printf("***SSLGetCertificate error\n");
758 ortn = ioErr;
759 goto cleanup;
760 }
761 }
762 if(pargs->encryptClientCerts) {
763 if(pargs->anchorFile == NULL) {
764 ortn = addIdentityAsTrustedRoot(ctx, pargs->encryptClientCerts);
765 if(ortn) {
766 goto cleanup;
767 }
768 }
769 ortn = SSLSetEncryptionCertificate(ctx, pargs->encryptClientCerts);
770 if(ortn) {
771 printSslErrStr("SSLSetEncryptionCertificate", ortn);
772 goto cleanup;
773 }
774 }
775 if(pargs->sessionCacheTimeout) {
776 ortn = SSLSetSessionCacheTimeout(ctx, pargs->sessionCacheTimeout);
777 if(ortn) {
778 printSslErrStr("SSLSetSessionCacheTimeout", ortn);
779 goto cleanup;
780 }
781 }
782 if(pargs->disableAnonCiphers) {
783 ortn = SSLSetAllowAnonymousCiphers(ctx, false);
784 if(ortn) {
785 printSslErrStr("SSLSetAllowAnonymousCiphers", ortn);
786 goto cleanup;
787 }
788 /* quickie test of the getter */
789 Boolean e;
790 ortn = SSLGetAllowAnonymousCiphers(ctx, &e);
791 if(ortn) {
792 printSslErrStr("SSLGetAllowAnonymousCiphers", ortn);
793 goto cleanup;
794 }
795 if(e) {
796 printf("***SSLGetAllowAnonymousCiphers() returned true; expected false\n");
797 ortn = ioErr;
798 goto cleanup;
799 }
800 }
801 if(pargs->showCipherSuites) {
802 sslShowEnabledCipherSuites(ctx);
803 }
804 /*** end options ***/
805
806 if(pargs->verbose) {
807 printf("...starting SSL handshake\n");
808 }
809 startHandshake = CFAbsoluteTimeGetCurrent();
810
811 do
812 { ortn = SSLHandshake(ctx);
813 if((ortn == errSSLWouldBlock) && !pargs->silent) {
814 /* keep UI responsive */
815 sslOutputDot();
816 }
817 else if(ortn == errSSLServerAuthCompleted) {
818 if(pargs->verbose) {
819 printf("...server authentication completed\n");
820 }
821 }
822 else if(ortn == errSSLClientCertRequested) {
823 if(pargs->verbose) {
824 printf("...received client cert request\n");
825 }
826 /* %%% could prompt interactively here for client cert to use;
827 * for now, just use the client cert passed on the command line
828 */
829 if(pargs->clientCerts) {
830 CFArrayRef dummy;
831 if(pargs->anchorFile == NULL) {
832 /* assume this is a root we want to implicitly trust */
833 ortn = addIdentityAsTrustedRoot(ctx, pargs->clientCerts);
834 if(ortn) {
835 goto cleanup;
836 }
837 }
838 if(pargs->verbose) {
839 printf("...setting client certificate\n");
840 }
841 ortn = SSLSetCertificate(ctx, pargs->clientCerts);
842 if(ortn) {
843 printSslErrStr("SSLSetCertificate", ortn);
844 goto cleanup;
845 }
846 /* quickie test for this new function */
847 ortn = SSLGetCertificate(ctx, &dummy);
848 if(ortn) {
849 printSslErrStr("SSLGetCertificate", ortn);
850 goto cleanup;
851 }
852 if(dummy != pargs->clientCerts) {
853 printf("***SSLGetCertificate error\n");
854 ortn = ioErr;
855 goto cleanup;
856 }
857 }
858 else {
859 printf("***no client certificate specified!\n");
860 }
861 }
862 } while (ortn == errSSLWouldBlock ||
863 ortn == errSSLServerAuthCompleted ||
864 ortn == errSSLClientCertRequested);
865
866 endHandshake = CFAbsoluteTimeGetCurrent();
867 pargs->handshakeTimeOp = endHandshake - startHandshake;
868 if(pargs->numHandshakes == 0) {
869 /* special case, this one is always way longer */
870 pargs->handshakeTimeFirst = pargs->handshakeTimeOp;
871 }
872 else {
873 /* normal running total */
874 pargs->handshakeTimeTotal += pargs->handshakeTimeOp;
875 }
876 pargs->numHandshakes++;
877
878 /* this works even if handshake failed due to cert chain invalid */
879 CFRELEASE(pargs->peerCerts);
880 if(!pargs->manualCertVerify) {
881 copyPeerCerts(ctx, &pargs->peerCerts);
882 }
883 else {
884 /* else fetched via SecTrust later */
885 pargs->peerCerts = NULL;
886 }
887
888 ortn = SSLCopyPeerTrust(ctx, &pargs->peerTrust);
889 if(ortn) {
890 printf("***SSLCopyPeerTrust error %d\n", (int)ortn);
891 pargs->peerTrust = NULL;
892 }
893
894 /* ditto */
895 SSLGetClientCertificateState(ctx, &pargs->certState);
896 SSLGetNegotiatedClientAuthType(ctx, &pargs->authType);
897 SSLGetNegotiatedCipher(ctx, &pargs->negCipher);
898 SSLGetNegotiatedProtocolVersion(ctx, &pargs->negVersion);
899 CFRELEASE(pargs->dnList);
900 SSLCopyDistinguishedNames(ctx, &pargs->dnList);
901 pargs->sessionIDLength = MAX_SESSION_ID_LENGTH;
902 SSLGetResumableSessionInfo(ctx, &pargs->sessionWasResumed, pargs->sessionID,
903 &pargs->sessionIDLength);
904 if(pargs->manualCertVerify) {
905 OSStatus certRtn = sslEvaluateTrust(ctx, pargs->verbose, pargs->silent,
906 &pargs->peerCerts);
907 if(certRtn && !ortn ) {
908 ortn = certRtn;
909 }
910 }
911
912 if(ortn) {
913 if(!pargs->silent) {
914 printf("\n");
915 }
916 goto cleanup;
917 }
918
919 if(pargs->verbose) {
920 printf("...SSL handshake complete\n");
921 }
922
923 /* Write our GET request */
924 length = strlen(pargs->getMsg);
925 ortn = SSLWrite(ctx, pargs->getMsg, length, &actLen);
926 if(ortn) {
927 printf("***SSLWrite error: %d\n", (int)ortn);
928 } else if((actLen > 0) && pargs->dumpRxData) {
929 dumpAscii((uint8_t*)pargs->getMsg, actLen);
930 }
931
932 /*
933 * Try to snag RCV_BUF_SIZE bytes. Exit if (!keepConnected and we get any data
934 * at all), or (keepConnected and err != (none, wouldBlock)).
935 */
936 while (ortn == noErr) {
937 actLen = 0;
938 if(pargs->dumpRxData) {
939 size_t avail = 0;
940
941 ortn = SSLGetBufferedReadSize(ctx, &avail);
942 if(ortn) {
943 printf("***SSLGetBufferedReadSize error\n");
944 break;
945 }
946 if(avail != 0) {
947 printf("\n%d bytes available: ", (int)avail);
948 }
949 }
950 ortn = SSLRead(ctx, rcvBuf, RCV_BUF_SIZE, &actLen);
951 if((actLen == 0) && !pargs->silent) {
952 sslOutputDot();
953 }
954 if((actLen == 0) && (ortn == noErr)) {
955 printf("***Radar 2984932 confirmed***\n");
956 }
957 if (ortn == errSSLWouldBlock) {
958 /* for this loop, these are identical */
959 ortn = noErr;
960 }
961 if(ortn == errSSLServerAuthCompleted ||
962 ortn == errSSLClientCertRequested) {
963 /* should never get these once the handshake is complete */
964 printf("***SSLRead returned unexpected handshake error!\n");
965 }
966
967 if((actLen > 0) && pargs->dumpRxData) {
968 dumpAscii(rcvBuf, actLen);
969 }
970 if(ortn != noErr) {
971 /* connection closed by server or by error */
972 break;
973 }
974 if(!pargs->keepConnected && (actLen > 0)) {
975 /* good enough, we connected */
976 break;
977 }
978 }
979 if(!pargs->silent) {
980 printf("\n");
981 }
982
983 /* snag these again in case of renegotiate */
984 SSLGetClientCertificateState(ctx, &pargs->certState);
985 SSLGetNegotiatedCipher(ctx, &pargs->negCipher);
986 SSLGetNegotiatedProtocolVersion(ctx, &pargs->negVersion);
987 CFRELEASE(pargs->dnList);
988 SSLCopyDistinguishedNames(ctx, &pargs->dnList);
989
990 /* convert normal "shutdown" into zero err rtn */
991 if(ortn == errSSLClosedGraceful) {
992 ortn = noErr;
993 }
994 if((ortn == errSSLClosedNoNotify) && !pargs->requireNotify) {
995 /* relaxed disconnect rules */
996 ortn = noErr;
997 }
998 cleanup:
999 /*
1000 * always do close, even on error - to flush outgoing write queue
1001 */
1002 OSStatus cerr = SSLClose(ctx);
1003 if(ortn == noErr) {
1004 ortn = cerr;
1005 }
1006 if(sock) {
1007 endpointShutdown(sock);
1008 }
1009 if(ctx) {
1010 SSLDisposeContext(ctx);
1011 }
1012 return ortn;
1013 }
1014
1015 static void showPeerCerts(
1016 CFArrayRef peerCerts,
1017 bool verbose)
1018 {
1019 CFIndex numCerts;
1020 SecCertificateRef certRef;
1021 OSStatus ortn;
1022 CSSM_DATA certData;
1023 CFIndex i;
1024
1025 if(peerCerts == NULL) {
1026 return;
1027 }
1028 numCerts = CFArrayGetCount(peerCerts);
1029 for(i=0; i<numCerts; i++) {
1030 certRef = (SecCertificateRef)CFArrayGetValueAtIndex(peerCerts, i);
1031 ortn = SecCertificateGetData(certRef, &certData);
1032 if(ortn) {
1033 printf("***SecCertificateGetData returned %d\n", (int)ortn);
1034 continue;
1035 }
1036 printf("\n================== Server Cert %lu ===================\n\n", i);
1037 printCert(certData.Data, certData.Length, verbose);
1038 printf("\n=============== End of Server Cert %lu ===============\n", i);
1039 }
1040 }
1041
1042 static void writePeerCerts(
1043 CFArrayRef peerCerts,
1044 const char *fileBase)
1045 {
1046 CFIndex numCerts;
1047 SecCertificateRef certRef;
1048 OSStatus ortn;
1049 CSSM_DATA certData;
1050 CFIndex i;
1051 char fileName[100];
1052
1053 if(peerCerts == NULL) {
1054 return;
1055 }
1056 numCerts = CFArrayGetCount(peerCerts);
1057 for(i=0; i<numCerts; i++) {
1058 sprintf(fileName, "%s%02d.cer", fileBase, (int)i);
1059 certRef = (SecCertificateRef)CFArrayGetValueAtIndex(peerCerts, i);
1060 ortn = SecCertificateGetData(certRef, &certData);
1061 if(ortn) {
1062 printf("***SecCertificateGetData returned %d\n", (int)ortn);
1063 continue;
1064 }
1065 cspWriteFile(fileName, certData.Data, certData.Length);
1066 }
1067 printf("...wrote %lu certs to fileBase %s\n", numCerts, fileBase);
1068 }
1069
1070 static void writeDnList(
1071 CFArrayRef dnList,
1072 const char *fileBase)
1073 {
1074 CFIndex numDns;
1075 CFDataRef cfDn;
1076 CFIndex i;
1077 char fileName[100];
1078
1079 if(dnList == NULL) {
1080 return;
1081 }
1082 numDns = CFArrayGetCount(dnList);
1083 for(i=0; i<numDns; i++) {
1084 sprintf(fileName, "%s%02d.der", fileBase, (int)i);
1085 cfDn = (CFDataRef)CFArrayGetValueAtIndex(dnList, i);
1086 cspWriteFile(fileName, CFDataGetBytePtr(cfDn), CFDataGetLength(cfDn));
1087 }
1088 printf("...wrote %lu RDNs to fileBase %s\n", numDns, fileBase);
1089 }
1090
1091 /*
1092 * Show result of an sslPing().
1093 * Assumes the following from sslPingArgs:
1094 *
1095 * verbose
1096 * tryVersion
1097 * acceptedProts
1098 * negVersion
1099 * negCipher
1100 * peerCerts
1101 * certState
1102 * authType
1103 * sessionWasResumed
1104 * sessionID
1105 * sessionIDLength
1106 * handshakeTime
1107 */
1108 static void showSSLResult(
1109 const sslPingArgs &pargs,
1110 OSStatus err,
1111 bool displayPeerCerts,
1112 const char *fileBase, // non-NULL: write certs to file
1113 const char *dnFileBase) // non-NULL: write DNList to file
1114 {
1115 CFIndex numPeerCerts;
1116
1117 printf("\n");
1118
1119 if(pargs.acceptedProts) {
1120 printf(" Allowed SSL versions : %s\n", pargs.acceptedProts);
1121 }
1122 else {
1123 printf(" Attempted SSL version : %s\n",
1124 sslGetProtocolVersionString(pargs.tryVersion));
1125 }
1126
1127 printf(" Result : %s\n", sslGetSSLErrString(err));
1128 printf(" Negotiated SSL version : %s\n",
1129 sslGetProtocolVersionString(pargs.negVersion));
1130 printf(" Negotiated CipherSuite : %s\n",
1131 sslGetCipherSuiteString(pargs.negCipher));
1132 if(pargs.certState != kSSLClientCertNone) {
1133 printf(" Client Cert State : %s\n",
1134 sslGetClientCertStateString(pargs.certState));
1135 printf(" Client Auth Type : %s\n",
1136 sslGetClientAuthTypeString(pargs.authType));
1137 }
1138 if(pargs.verbose) {
1139 printf(" Resumed Session : ");
1140 if(pargs.sessionWasResumed) {
1141 for(unsigned dex=0; dex<pargs.sessionIDLength; dex++) {
1142 printf("%02X ", pargs.sessionID[dex]);
1143 if(((dex % 8) == 7) && (dex != (pargs.sessionIDLength - 1))) {
1144 printf("\n ");
1145 }
1146 }
1147 printf("\n");
1148 }
1149 else {
1150 printf("NOT RESUMED\n");
1151 }
1152 printf(" Handshake time : %f seconds\n", pargs.handshakeTimeOp);
1153 }
1154 if(pargs.peerCerts == NULL) {
1155 numPeerCerts = 0;
1156 }
1157 else {
1158 numPeerCerts = CFArrayGetCount(pargs.peerCerts);
1159 }
1160 printf(" Number of server certs : %lu\n", numPeerCerts);
1161 if(numPeerCerts != 0) {
1162 if(displayPeerCerts) {
1163 showPeerCerts(pargs.peerCerts, false);
1164 }
1165 if(fileBase != NULL) {
1166 writePeerCerts(pargs.peerCerts, fileBase);
1167 }
1168 }
1169 if(dnFileBase != NULL) {
1170 writeDnList(pargs.dnList, dnFileBase);
1171 }
1172
1173 printf("\n");
1174 }
1175
1176 static int verifyProtocol(
1177 bool verifyProt,
1178 SSLProtocol maxProtocol,
1179 SSLProtocol reqProtocol,
1180 SSLProtocol negProtocol)
1181 {
1182 if(!verifyProt) {
1183 return 0;
1184 }
1185 if(reqProtocol > maxProtocol) {
1186 /* known not to support this attempt, relax */
1187 reqProtocol = maxProtocol;
1188 }
1189 if(reqProtocol != negProtocol) {
1190 printf("***Expected protocol %s; negotiated %s\n",
1191 sslGetProtocolVersionString(reqProtocol),
1192 sslGetProtocolVersionString(negProtocol));
1193 return 1;
1194 }
1195 else {
1196 return 0;
1197 }
1198 }
1199
1200 static int verifyClientCertState(
1201 bool verifyCertState,
1202 SSLClientCertificateState expectState,
1203 SSLClientCertificateState gotState)
1204 {
1205 if(!verifyCertState) {
1206 return 0;
1207 }
1208 if(expectState == gotState) {
1209 return 0;
1210 }
1211 printf("***Expected clientCertState %s; got %s\n",
1212 sslGetClientCertStateString(expectState),
1213 sslGetClientCertStateString(gotState));
1214 return 1;
1215 }
1216
1217 /*
1218 * Free everything allocated by sslPing in an sslPingArgs.
1219 * Mainly for looping and malloc debugging.
1220 */
1221 static void freePingArgs(
1222 sslPingArgs *pargs)
1223 {
1224 freePeerCerts(pargs->peerCerts);
1225 pargs->peerCerts = NULL;
1226 CFRELEASE(pargs->peerTrust);
1227 CFRELEASE(pargs->dnList);
1228 /* more, later, for client retry/identity fetch */
1229 }
1230
1231 static SSLProtocol charToProt(
1232 char c, // 2, 3, t
1233 char **argv)
1234 {
1235 switch(c) {
1236 case '2':
1237 return kSSLProtocol2;
1238 case '3':
1239 return kSSLProtocol3;
1240 case 't':
1241 return kTLSProtocol1;
1242 default:
1243 usage(argv);
1244 }
1245 /* NOT REACHED */
1246 return kSSLProtocolUnknown;
1247 }
1248
1249 int main(int argc, char **argv)
1250 {
1251 OSStatus err;
1252 int arg;
1253 char *argp;
1254 char getMsg[300];
1255 char fullFileBase[100];
1256 int ourRtn = 0; // exit status - sum of all errors
1257 unsigned loop;
1258 SecKeychainRef serverKc = nil;
1259 SecKeychainRef encryptKc = nil;
1260 sslPingArgs pargs;
1261
1262 /* user-spec'd parameters */
1263 char *getPath = (char *)DEFAULT_PATH;
1264 char *fileBase = NULL;
1265 bool displayCerts = false;
1266 bool doSslV2 = false;
1267 bool doSslV3 = false;
1268 bool doTlsV1 = true;
1269 bool protXOnly = false; // kSSLProtocol3Only, kTLSProtocol1Only
1270 bool doProtUnknown = false;
1271 unsigned loopCount = 1;
1272 bool doPause = false;
1273 bool pauseFirstLoop = false;
1274 bool verifyProt = false;
1275 SSLProtocol maxProtocol = kTLSProtocol1; // for verifying negotiated
1276 // protocol
1277 char *acceptedProts = NULL;
1278 char *keyChainName = NULL;
1279 char *encryptKeyChainName = NULL;
1280 char *getMsgSpec = NULL;
1281 bool vfyCertState = false;
1282 SSLClientCertificateState expectCertState;
1283 bool displayHandshakeTimes = false;
1284 bool completeCertChain = false;
1285 char *dnFileBase = NULL;
1286
1287 /* special case - one arg of "h" or "-h" or "hv" */
1288 if(argc == 2) {
1289 if((strcmp(argv[1], "h") == 0) || (strcmp(argv[1], "-h") == 0)) {
1290 usage(argv);
1291 }
1292 if(strcmp(argv[1], "hv") == 0) {
1293 usageVerbose(argv);
1294 }
1295 }
1296
1297 /* set up defaults */
1298 memset(&pargs, 0, sizeof(sslPingArgs));
1299 pargs.hostName = DEFAULT_HOST;
1300 pargs.port = DEFAULT_PORT;
1301 pargs.resumableEnable = true;
1302 pargs.argv = argv;
1303
1304 for(arg=1; arg<argc; arg++) {
1305 argp = argv[arg];
1306 if(arg == 1) {
1307 /* first arg, is always hostname; '-' means default */
1308 if(argp[0] != '-') {
1309 pargs.hostName = argp;
1310 }
1311 continue;
1312 }
1313 if(argp[0] == '/') {
1314 /* path always starts with leading slash */
1315 getPath = argp;
1316 continue;
1317 }
1318 /* options */
1319 switch(argp[0]) {
1320 case 'e':
1321 pargs.allowExpired = true;
1322 break;
1323 case 'E':
1324 pargs.allowExpiredRoot = true;
1325 break;
1326 case 'x':
1327 pargs.disableCertVerify = true;
1328 break;
1329 case 'M':
1330 pargs.disableCertVerify = true; // implied
1331 pargs.manualCertVerify = true;
1332 break;
1333 case 'I':
1334 pargs.interactiveAuth = true;
1335 break;
1336 case 'a':
1337 if(++arg == argc) {
1338 /* requires another arg */
1339 usage(argv);
1340 }
1341 pargs.anchorFile = argv[arg];
1342 break;
1343 case 'A':
1344 if(++arg == argc) {
1345 /* requires another arg */
1346 usage(argv);
1347 }
1348 pargs.anchorFile = argv[arg];
1349 pargs.replaceAnchors = true;
1350 break;
1351 case 'Z':
1352 if(++arg == argc) {
1353 /* requires another arg */
1354 usage(argv);
1355 }
1356 pargs.trustedLeafFile = argv[arg];
1357 break;
1358 case 'r':
1359 pargs.allowAnyRoot = true;
1360 break;
1361 case 'd':
1362 pargs.dumpRxData = true;
1363 break;
1364 case 'c':
1365 displayCerts = true;
1366 break;
1367 case 'f':
1368 if(++arg == argc) {
1369 /* requires another arg */
1370 usage(argv);
1371 }
1372 fileBase = argv[arg];
1373 break;
1374 case 'C':
1375 pargs.cipherRestrict = argp[2];
1376 break;
1377 case 'S':
1378 pargs.showCipherSuites = true;
1379 break;
1380 case '2':
1381 doSslV3 = doTlsV1 = false;
1382 doSslV2 = true;
1383 break;
1384 case '3':
1385 doSslV2 = doTlsV1 = false;
1386 doSslV3 = true;
1387 break;
1388 case 't':
1389 /* currently the default... */
1390 doSslV2 = doSslV3 = false;
1391 doTlsV1 = true;
1392 break;
1393 case 'L':
1394 doSslV2 = doSslV3 = doTlsV1 = true;
1395 break;
1396 case 'o':
1397 protXOnly = true;
1398 break;
1399 case 'u':
1400 doSslV2 = doSslV3 = doTlsV1 = false;
1401 doProtUnknown = true;
1402 break;
1403 case 'K':
1404 pargs.keepConnected = true;
1405 break;
1406 case 'n':
1407 pargs.requireNotify = true;
1408 pargs.keepConnected = true;
1409 break;
1410 case 'R':
1411 pargs.resumableEnable = false;
1412 break;
1413 case 'b':
1414 pargs.nonBlocking = true;
1415 break;
1416 case 'v':
1417 verifyProt = true;
1418 break;
1419 case 'm':
1420 if(argp[1] != '=') {
1421 usage(argv);
1422 }
1423 verifyProt = true; // implied
1424 maxProtocol = charToProt(argp[2], argv);
1425 break;
1426 case 'g':
1427 if(argp[1] != '=') {
1428 usage(argv);
1429 }
1430 acceptedProts = &argp[2];
1431 doSslV3 = doSslV2 = doTlsV1 = false;
1432 break;
1433 case 'l':
1434 loopCount = atoi(&argp[2]);
1435 if(loopCount == 0) {
1436 printf("***bad loopCount\n");
1437 usage(argv);
1438 }
1439 break;
1440 case 'P':
1441 pargs.port = atoi(&argp[2]);
1442 break;
1443 case 'H':
1444 pargs.allowHostnameSpoof = true;
1445 break;
1446 case 'F':
1447 pargs.vfyHostName = &argp[2];
1448 break;
1449 case 'k':
1450 keyChainName = &argp[2];
1451 break;
1452 case 'y':
1453 encryptKeyChainName = &argp[2];
1454 break;
1455 case 'G':
1456 getMsgSpec = &argp[2];
1457 break;
1458 case 'T':
1459 if(argp[1] != '=') {
1460 usage(argv);
1461 }
1462 vfyCertState = true;
1463 switch(argp[2]) {
1464 case 'n':
1465 expectCertState = kSSLClientCertNone;
1466 break;
1467 case 'r':
1468 expectCertState = kSSLClientCertRequested;
1469 break;
1470 case 's':
1471 expectCertState = kSSLClientCertSent;
1472 break;
1473 case 'j':
1474 expectCertState = kSSLClientCertRejected;
1475 break;
1476 default:
1477 usage(argv);
1478 }
1479 break;
1480 case 'z':
1481 pargs.password = &argp[2];
1482 break;
1483 case 'p':
1484 doPause = true;
1485 break;
1486 case '7':
1487 pauseFirstLoop = true;
1488 break;
1489 case 'q':
1490 pargs.quiet = true;
1491 break;
1492 case 'V':
1493 pargs.verbose = true;
1494 break;
1495 case 's':
1496 pargs.silent = pargs.quiet = true;
1497 break;
1498 case 'N':
1499 displayHandshakeTimes = true;
1500 break;
1501 case '8':
1502 completeCertChain = true;
1503 break;
1504 case 'i':
1505 pargs.sessionCacheTimeout = atoi(&argp[2]);
1506 break;
1507 case '4':
1508 pargs.disableAnonCiphers = true;
1509 break;
1510 case 'D':
1511 if(++arg == argc) {
1512 /* requires another arg */
1513 usage(argv);
1514 }
1515 dnFileBase = argv[arg];
1516 break;
1517 case 'h':
1518 if(pargs.verbose || (argp[1] == 'v')) {
1519 usageVerbose(argv);
1520 }
1521 else {
1522 usage(argv);
1523 }
1524 default:
1525 usage(argv);
1526 }
1527 }
1528 if(getMsgSpec) {
1529 pargs.getMsg = getMsgSpec;
1530 }
1531 else {
1532 sprintf(getMsg, "%s %s %s",
1533 DEFAULT_GETMSG, getPath, DEFAULT_GET_SUFFIX);
1534 pargs.getMsg = getMsg;
1535 }
1536
1537 /* get client cert and optional encryption cert as CFArrayRef */
1538 if(keyChainName) {
1539 pargs.clientCerts = getSslCerts(keyChainName, false, completeCertChain,
1540 pargs.anchorFile, &serverKc);
1541 if(pargs.clientCerts == nil) {
1542 exit(1);
1543 }
1544 if(pargs.password) {
1545 OSStatus ortn = SecKeychainUnlock(serverKc,
1546 strlen(pargs.password), pargs.password, true);
1547 if(ortn) {
1548 printf("SecKeychainUnlock returned %d\n", (int)ortn);
1549 /* oh well */
1550 }
1551 }
1552 }
1553 if(encryptKeyChainName) {
1554 pargs.encryptClientCerts = getSslCerts(encryptKeyChainName, true,
1555 completeCertChain, pargs.anchorFile, &encryptKc);
1556 if(pargs.encryptClientCerts == nil) {
1557 exit(1);
1558 }
1559 }
1560 signal(SIGPIPE, sigpipe);
1561
1562 if(loopCount != 0) {
1563 /* prepare to handle KC callbacks for root cert cache invalidation */
1564 if(startCFRunLoop()) {
1565 exit(1);
1566 }
1567
1568 /* give that thread a chance right now */
1569 while(!runLoopInitialized) {
1570 usleep(1000);
1571 };
1572 }
1573
1574 if(doPause) {
1575 char resp;
1576 fpurge(stdin);
1577 printf("Before main loop. Hit a to abort, c to continue: ");
1578 resp = getchar();
1579 if(resp == 'a') {
1580 exit(1);
1581 }
1582 }
1583
1584 for(loop=0; loop<loopCount; loop++) {
1585 /*
1586 * One pass for each protocol version, skipping any explicit version if
1587 * an attempt at a higher version and succeeded in doing so successfully fell
1588 * back.
1589 */
1590 if(doTlsV1) {
1591 pargs.tryVersion =
1592 protXOnly ? kTLSProtocol1Only : kTLSProtocol1;
1593 pargs.acceptedProts = NULL;
1594 if(!pargs.silent) {
1595 printf("Connecting to host %s with TLS V1...\n", pargs.hostName);
1596 }
1597 fflush(stdout);
1598 err = sslPing(&pargs);
1599 if(err) {
1600 ourRtn++;
1601 }
1602 if(!pargs.quiet) {
1603 if(fileBase) {
1604 sprintf(fullFileBase, "%s_v3.1", fileBase);
1605 }
1606 showSSLResult(pargs,
1607 err,
1608 displayCerts,
1609 fileBase ? fullFileBase : NULL,
1610 dnFileBase);
1611 }
1612 freePingArgs(&pargs);
1613 if(!err) {
1614 /* deal with fallbacks, skipping redundant tests */
1615 switch(pargs.negVersion) {
1616 case kSSLProtocol3:
1617 doSslV3 = false;
1618 break;
1619 case kSSLProtocol2:
1620 doSslV3 = false;
1621 doSslV2 = false;
1622 break;
1623 default:
1624 break;
1625 }
1626 ourRtn += verifyProtocol(verifyProt, maxProtocol, kTLSProtocol1,
1627 pargs.negVersion);
1628 }
1629 /* note we do this regardless since the client state might be
1630 * the cause of a failure */
1631 ourRtn += verifyClientCertState(vfyCertState, expectCertState,
1632 pargs.certState);
1633 }
1634 if(doSslV3) {
1635 pargs.tryVersion = protXOnly ? kSSLProtocol3Only : kSSLProtocol3;
1636 pargs.acceptedProts = NULL;
1637 if(!pargs.silent) {
1638 printf("Connecting to host %s with SSL V3...\n", pargs.hostName);
1639 }
1640 fflush(stdout);
1641 err = sslPing(&pargs);
1642 if(err) {
1643 ourRtn++;
1644 }
1645 if(!pargs.quiet) {
1646 if(fileBase) {
1647 sprintf(fullFileBase, "%s_v3.0", fileBase);
1648 }
1649 showSSLResult(pargs,
1650 err,
1651 displayCerts,
1652 fileBase ? fullFileBase : NULL,
1653 dnFileBase);
1654 }
1655 freePingArgs(&pargs);
1656 if(!err) {
1657 /* deal with fallbacks, skipping redundant tests */
1658 switch(pargs.negVersion) {
1659 case kSSLProtocol2:
1660 doSslV2 = false;
1661 break;
1662 default:
1663 break;
1664 }
1665 ourRtn += verifyProtocol(verifyProt, maxProtocol, kSSLProtocol3,
1666 pargs.negVersion);
1667 }
1668 /* note we do this regardless since the client state might be
1669 * the cause of a failure */
1670 ourRtn += verifyClientCertState(vfyCertState, expectCertState,
1671 pargs.certState);
1672 }
1673
1674 if(doSslV2) {
1675 if(fileBase) {
1676 sprintf(fullFileBase, "%s_v2", fileBase);
1677 }
1678 if(!pargs.silent) {
1679 printf("Connecting to host %s with SSL V2...\n", pargs.hostName);
1680 }
1681 fflush(stdout);
1682 pargs.tryVersion = kSSLProtocol2;
1683 pargs.acceptedProts = NULL;
1684 err = sslPing(&pargs);
1685 if(err) {
1686 ourRtn++;
1687 }
1688 if(!pargs.quiet) {
1689 if(fileBase) {
1690 sprintf(fullFileBase, "%s_v2", fileBase);
1691 }
1692 showSSLResult(pargs,
1693 err,
1694 displayCerts,
1695 fileBase ? fullFileBase : NULL,
1696 dnFileBase);
1697 }
1698 freePingArgs(&pargs);
1699 if(!err) {
1700 ourRtn += verifyProtocol(verifyProt, maxProtocol, kSSLProtocol2,
1701 pargs.negVersion);
1702 }
1703 /* note we do this regardless since the client state might be
1704 * the cause of a failure */
1705 ourRtn += verifyClientCertState(vfyCertState, expectCertState,
1706 pargs.certState);
1707 }
1708 if(doProtUnknown) {
1709 if(!pargs.silent) {
1710 printf("Connecting to host %s with kSSLProtocolUnknown...\n",
1711 pargs.hostName);
1712 }
1713 fflush(stdout);
1714 pargs.tryVersion = kSSLProtocolUnknown;
1715 pargs.acceptedProts = NULL;
1716 err = sslPing(&pargs);
1717 if(err) {
1718 ourRtn++;
1719 }
1720 if(!pargs.quiet) {
1721 if(fileBase) {
1722 sprintf(fullFileBase, "%s_def", fileBase);
1723 }
1724 showSSLResult(pargs,
1725 err,
1726 displayCerts,
1727 fileBase ? fullFileBase : NULL,
1728 dnFileBase);
1729 }
1730 freePingArgs(&pargs);
1731 }
1732 if(acceptedProts != NULL) {
1733 pargs.acceptedProts = acceptedProts;
1734 pargs.tryVersion = kSSLProtocolUnknown; // not used
1735 if(!pargs.silent) {
1736 printf("Connecting to host %s with acceptedProts %s...\n",
1737 pargs.hostName, pargs.acceptedProts);
1738 }
1739 fflush(stdout);
1740 err = sslPing(&pargs);
1741 if(err) {
1742 ourRtn++;
1743 }
1744 if(!pargs.quiet) {
1745 if(fileBase) {
1746 sprintf(fullFileBase, "%s_def", fileBase);
1747 }
1748 showSSLResult(pargs,
1749 err,
1750 displayCerts,
1751 fileBase ? fullFileBase : NULL,
1752 dnFileBase);
1753 }
1754 freePingArgs(&pargs);
1755 }
1756 if(doPause ||
1757 (pauseFirstLoop &&
1758 /* pause after first, before last to grab trace */
1759 ((loop == 0) || (loop == loopCount - 1))
1760 )
1761 ) {
1762 char resp;
1763 fpurge(stdin);
1764 printf("a to abort, c to continue: ");
1765 resp = getchar();
1766 if(resp == 'a') {
1767 break;
1768 }
1769 }
1770 } /* main loop */
1771 if(displayHandshakeTimes) {
1772 CFAbsoluteTime totalTime;
1773 unsigned numHandshakes;
1774 if(pargs.numHandshakes == 1) {
1775 /* just display the first one */
1776 totalTime = pargs.handshakeTimeFirst;
1777 numHandshakes = 1;
1778 }
1779 else {
1780 /* skip the first one */
1781 totalTime = pargs.handshakeTimeTotal;
1782 numHandshakes = pargs.numHandshakes - 1;
1783 }
1784 if(numHandshakes != 0) {
1785 printf(" %u handshakes in %f seconds; %f seconds per handshake\n",
1786 numHandshakes, totalTime,
1787 (totalTime / numHandshakes));
1788 }
1789 }
1790 printCertShutdown();
1791 if(ourRtn) {
1792 printf("===%s exiting with %d %s for host %s\n", argv[0], ourRtn,
1793 (ourRtn > 1) ? "errors" : "error", pargs.hostName);
1794 }
1795 return ourRtn;
1796
1797 }
1798
1799