]> git.saurik.com Git - apple/security.git/blob - sslViewer/SSLViewer.c
Security-58286.1.32.tar.gz
[apple/security.git] / sslViewer / SSLViewer.c
1 /*
2 * Copyright (c) 2006-2013, 2015 Apple Inc. All Rights Reserved.
3 *
4 * SSL viewer tool
5 */
6
7 #include <CoreFoundation/CoreFoundation.h>
8 #include <Security/Security.h>
9 #include <Security/SecureTransport.h>
10 #include <Security/SecureTransportPriv.h>
11 #include <Security/SecTrustPriv.h>
12 #include <Security/SecPolicyPriv.h>
13
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <signal.h>
18 #include <time.h>
19 #include <ctype.h>
20
21 #include "sslAppUtils.h"
22 #include "ioSock.h"
23 #include "utilities/fileIo.h"
24 #include "utilities/SecCFWrappers.h"
25 #include "utilities/SecIOFormat.h"
26 #include "SecurityTool/print_cert.h"
27
28 #define DEFAULT_GETMSG "GET"
29 #define DEFAULT_PATH "/"
30 #define DEFAULT_GET_SUFFIX "HTTP/1.0\r\n\r\n"
31
32 #define DEFAULT_HOST "www.amazon.com"
33 #define DEFAULT_PORT 443
34
35 static const int _maxFileStringSize = 100;
36
37 static void usageNorm(char **argv)
38 {
39 printf("Usage: %s [hostname|-] [path] [option ...]\n", argv[0]);
40 printf(" %s hostname [path] [option ...]\n", argv[0]);
41 printf("Specifying '-' for hostname, or no args, uses default of %s.\n",
42 DEFAULT_HOST);
43 printf("Optional path argument must start with leading '/'.\n");
44 printf("Options:\n");
45 printf(" e Allow Expired Certs\n");
46 printf(" E Allow Expired Roots\n");
47 printf(" r Allow any root cert\n");
48 printf(" c Display peer certs\n");
49 printf(" c c Display peer SecTrust\n");
50 printf(" d Display received data\n");
51 printf(" 2 SSLv2 only (default is TLSv1)\n");
52 printf(" 3 SSLv3 only (default is TLSv1)\n");
53 printf(" t TLSv1\n");
54 printf(" %% TLSv1.1 only\n");
55 printf(" ^ TLSv1.2 only\n");
56 printf(" L all - TLSv1.2, TLSv1.1, TLSv1.0, SSLv3, SSLv2 (default = TLSv1.2)\n");
57 printf(" g={prot...} Specify legal protocols; prot = any combo of"
58 " [23t]\n");
59 printf(" k=keychain Contains cert and keys. Optional.\n");
60 printf(" l=loopCount Perform loopCount ops (default = 1)\n");
61 printf(" P=port Default = %d\n", DEFAULT_PORT);
62 printf(" p Pause after each loop\n");
63 printf(" q Quiet/diagnostic mode (site names and errors only)\n");
64 printf(" a fileName Add fileName to list of trusted roots\n");
65 printf(" A fileName fileName is ONLY trusted root\n");
66 printf(" x Disable Cert Verification\n");
67 printf(" Z string ALPN setting\n");
68 printf(" z=password Unlock client keychain with password.\n");
69 printf(" 8 Complete cert chains (default is out cert is a root)\n");
70 printf(" s Silent\n");
71 printf(" V Verbose\n");
72 printf(" h Help\n");
73 printf(" hv More, verbose help\n");
74 }
75
76 static void usageVerbose(char **argv) __attribute__((noreturn));
77 static void usageVerbose(char **argv)
78 {
79 usageNorm(argv);
80 printf("Obscure Usage:\n");
81 printf(" u kSSLProtocolUnknown only (TLSv1)\n");
82 printf(" M Manual cert verification via "
83 "SecTrustEvaluate\n");
84 printf(" f fileBase Write Peer Certs to fileBase*\n");
85 printf(" o TLSv1, SSLv3 use kSSLProtocol__X__Only\n");
86 printf(" C=cipherSuite (e=40-bit d=DES D=40-bit DES 3=3DES 4=RC4 "
87 "$=40-bit RC4\n"
88 " 2=RC2 a=AES128 A=AES256 h=DH H=Anon DH r=DHE/RSA s=DH/DSS\n");
89 printf(" y=keychain Encryption-only cert and keys. Optional.\n");
90 printf(" K Keep connected until server disconnects\n");
91 printf(" n Require closure notify message in TLSv1, "
92 "SSLv3 mode (implies K)\n");
93 printf(" R Disable resumable session support\n");
94 printf(" b Non-blocking I/O\n");
95 printf(" v Verify negotiated protocol equals attempted\n");
96 printf(" m=[23t] Max protocol supported as specified; implies "
97 "v\n");
98 printf(" T=[nrsj] Verify client cert state = "
99 "none/requested/sent/rejected\n");
100 printf(" H allow hostname spoofing\n");
101 printf(" F=vfyHost Verify certs with specified host name\n");
102 printf(" G=getMsg Specify entire GET, POST, etc.\n");
103 printf(" N Log handshake timing\n");
104 printf(" 7 Pause only after first loop\n");
105 exit(1);
106 }
107
108 static void usage(char **argv) __attribute__((noreturn));
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 bool replaceAnchors;
145 CFArrayRef clientCerts; // optional
146 bool quiet; // minimal stdout
147 bool silent; // no stdout
148 bool verbose;
149 SSLProtocol negVersion; // RETURNED
150 SSLCipherSuite negCipher; // RETURNED
151 CFArrayRef peerCerts; // mallocd & RETURNED
152 SecTrustRef peerTrust; // RETURNED
153 SSLClientCertificateState certState; // RETURNED
154 char *password; // optional to open clientCerts
155 char **argv;
156 Boolean sessionWasResumed;
157 unsigned char sessionID[MAX_SESSION_ID_LENGTH];
158 size_t sessionIDLength;
159 CFAbsoluteTime handshakeTimeOp; // time for this op
160 CFAbsoluteTime handshakeTimeFirst; // time for FIRST op, not averaged
161 CFAbsoluteTime handshakeTimeTotal; // time for all ops except first
162 unsigned numHandshakes;
163
164 CFMutableArrayRef alpnNames;
165 CFMutableArrayRef policies;
166
167 } sslPingArgs;
168
169 static void
170 sigpipe(int sig)
171 {
172 fflush(stdin);
173 printf("***SIGPIPE***\n");
174 }
175
176 /*
177 * Manually evaluate session's SecTrustRef.
178 */
179
180 static OSStatus sslEvaluateTrust(
181 SSLContextRef ctx,
182 sslPingArgs *pargs,
183 CFArrayRef *peerCerts) // fetched and retained
184 {
185 OSStatus ortn = errSecSuccess;
186 SecTrustRef secTrust = NULL;
187
188 ortn = SSLGetPeerSecTrust(ctx, &secTrust);
189 if(ortn) {
190 printf("\n***Error obtaining peer SecTrustRef: %s\n",
191 sslGetSSLErrString(ortn));
192 return ortn;
193 }
194 if(secTrust == NULL) {
195 /* this is the normal case for resumed sessions, in which
196 * no cert evaluation is performed */
197 if(!pargs->silent) {
198 printf("...No SecTrust available - this is a resumed session, right?\n");
199 }
200 return errSecSuccess;
201 }
202
203
204 if (pargs->policies) {
205 SecTrustSetPolicies(secTrust, pargs->policies);
206 }
207
208 SecTrustResultType secTrustResult;
209 ortn = SecTrustEvaluate(secTrust, &secTrustResult);
210 if(ortn) {
211 printf("\n***Error on SecTrustEvaluate: %d\n", (int)ortn);
212 return ortn;
213 }
214 if(pargs->verbose) {
215 const char *res = NULL;
216 switch(secTrustResult) {
217 case kSecTrustResultInvalid:
218 res = "kSecTrustResultInvalid"; break;
219 case kSecTrustResultProceed:
220 res = "kSecTrustResultProceed"; break;
221 case kSecTrustResultDeny:
222 res = "kSecTrustResultDeny"; break;
223 case kSecTrustResultUnspecified:
224 res = "kSecTrustResultUnspecified"; break;
225 case kSecTrustResultRecoverableTrustFailure:
226 res = "kSecTrustResultRecoverableTrustFailure"; break;
227 case kSecTrustResultFatalTrustFailure:
228 res = "kSecTrustResultFatalTrustFailure"; break;
229 case kSecTrustResultOtherError:
230 res = "kSecTrustResultOtherError"; break;
231 default:
232 res = "UNKNOWN"; break;
233 }
234 printf("\nSecTrustEvaluate(): secTrustResult %s\n", res);
235 }
236
237 switch(secTrustResult) {
238 case kSecTrustResultUnspecified:
239 /* cert chain valid, no special UserTrust assignments */
240 case kSecTrustResultProceed:
241 /* cert chain valid AND user explicitly trusts this */
242 break;
243 default:
244 printf("\n***SecTrustEvaluate reported secTrustResult %d\n",
245 (int)secTrustResult);
246 ortn = errSSLXCertChainInvalid;
247 break;
248 }
249
250 *peerCerts = NULL;
251
252 #ifdef USE_CDSA_CRYPTO
253 /* one more thing - get peer certs in the form of an evidence chain */
254 CSSM_TP_APPLE_EVIDENCE_INFO *dummyEv;
255 OSStatus thisRtn = SecTrustGetResult(secTrust, &secTrustResult,
256 peerCerts, &dummyEv);
257 if(thisRtn) {
258 printSslErrStr("SecTrustGetResult", thisRtn);
259 }
260 else {
261 /* workaround for the fact that SSLGetPeerCertificates()
262 * leaves a retain count on each element in the returned array,
263 * requiring us to do a release on each cert.
264 */
265 CFIndex numCerts = CFArrayGetCount(*peerCerts);
266 for(CFIndex dex=0; dex<numCerts; dex++) {
267 CFRetain(CFArrayGetValueAtIndex(*peerCerts, dex));
268 }
269 }
270 #endif
271 return ortn;
272 }
273
274 /* print reply received from server, safely */
275 static void dumpAscii(
276 uint8_t *rcvBuf,
277 size_t len)
278 {
279 char *cp = (char *)rcvBuf;
280 uint32_t i;
281 char c;
282
283 for(i=0; i<len; i++) {
284 c = *cp++;
285 if(c == '\0') {
286 break;
287 }
288 switch(c) {
289 case '\n':
290 printf("\\n");
291 break;
292 case '\r':
293 printf("\\r");
294 break;
295 default:
296 if(isprint(c) && (c != '\n')) {
297 printf("%c", c);
298 }
299 else {
300 printf("<%02X>", ((unsigned)c) & 0xff);
301 }
302 break;
303 }
304
305 }
306 printf("\n");
307 }
308
309 static void
310 alpnFunc(SSLContextRef ctx,
311 void *info,
312 const void *alpnData,
313 size_t alpnDataLength)
314 {
315 printf("[selected ALPN]");
316 }
317
318 #pragma clang diagnostic push
319 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
320
321 /*
322 * Perform one SSL diagnostic session. Returns nonzero on error. Normally no
323 * output to stdout except initial "connecting to" message, unless there
324 * is a really screwed up error (i.e., something not directly related
325 * to the SSL connection).
326 */
327 #define RCV_BUF_SIZE 256
328
329 static OSStatus sslPing(
330 sslPingArgs *pargs)
331 {
332 PeerSpec peerId;
333 otSocket sock = 0;
334 OSStatus ortn;
335 SSLContextRef ctx = NULL;
336 size_t length;
337 size_t actLen;
338 uint8_t rcvBuf[RCV_BUF_SIZE];
339 CFAbsoluteTime startHandshake;
340 CFAbsoluteTime endHandshake;
341
342 pargs->negVersion = kSSLProtocolUnknown;
343 pargs->negCipher = SSL_NULL_WITH_NULL_NULL;
344 pargs->peerCerts = NULL;
345
346 /* first make sure requested server is there */
347 ortn = MakeServerConnection(pargs->hostName, pargs->port, pargs->nonBlocking,
348 &sock, &peerId);
349 if(ortn) {
350 printf("MakeServerConnection returned %d; aborting\n", (int)ortn);
351 return ortn;
352 }
353 if(pargs->verbose) {
354 printf("...connected to server; starting SecureTransport\n");
355 }
356
357 /*
358 * Set up a SecureTransport session.
359 * First the standard calls.
360 */
361 ctx = SSLCreateContext(kCFAllocatorDefault, kSSLClientSide, kSSLStreamType);
362 if(ctx == NULL) {
363 printf("SSLCreateContext\n");
364 goto cleanup;
365 }
366 ortn = SSLSetIOFuncs(ctx, SocketRead, SocketWrite);
367 if(ortn) {
368 printSslErrStr("SSLSetIOFuncs", ortn);
369 goto cleanup;
370 }
371 ortn = SSLSetConnection(ctx, (SSLConnectionRef)(intptr_t)sock);
372 if(ortn) {
373 printSslErrStr("SSLSetConnection", ortn);
374 goto cleanup;
375 }
376 SSLConnectionRef getConn;
377 ortn = SSLGetConnection(ctx, &getConn);
378 if(ortn) {
379 printSslErrStr("SSLGetConnection", ortn);
380 goto cleanup;
381 }
382 if(getConn != (SSLConnectionRef)(intptr_t)sock) {
383 printf("***SSLGetConnection error\n");
384 ortn = errSecParam;
385 goto cleanup;
386 }
387 if(!pargs->allowHostnameSpoof) {
388 /* if this isn't set, it isn't checked by AppleX509TP */
389 const char *vfyHost = pargs->hostName;
390 if(pargs->vfyHostName) {
391 /* generally means we're expecting an error */
392 vfyHost = pargs->vfyHostName;
393 }
394 ortn = SSLSetPeerDomainName(ctx, vfyHost, strlen(vfyHost));
395 if(ortn) {
396 printSslErrStr("SSLSetPeerDomainName", ortn);
397 goto cleanup;
398 }
399 }
400
401 /*
402 * SecureTransport options.
403 */
404 if(pargs->acceptedProts) {
405 if(ortn) {
406 printSslErrStr("SSLSetProtocolVersionEnabled(all off)", ortn);
407 goto cleanup;
408 }
409 for(const char *cp = pargs->acceptedProts; *cp; cp++) {
410 switch(*cp) {
411 case '2':
412 ortn = SSLSetProtocolVersionMax(ctx, kSSLProtocol2);
413 break;
414 case '3':
415 ortn = SSLSetProtocolVersionMax(ctx, kSSLProtocol3);
416 break;
417 case 't':
418 ortn = SSLSetProtocolVersionMax(ctx, kTLSProtocol12);
419 break;
420 default:
421 usage(pargs->argv);
422 }
423 if(ortn) {
424 printSslErrStr("SSLSetProtocolVersionMax", ortn);
425 goto cleanup;
426 }
427 }
428 } else {
429 SSLSetProtocolVersionMax(ctx, pargs->tryVersion);
430 }
431
432 if(pargs->resumableEnable) {
433 const void *rtnId = NULL;
434 size_t rtnIdLen = 0;
435
436 ortn = SSLSetPeerID(ctx, &peerId, sizeof(PeerSpec));
437 if(ortn) {
438 printSslErrStr("SSLSetPeerID", ortn);
439 goto cleanup;
440 }
441 /* quick test of the get fcn */
442 ortn = SSLGetPeerID(ctx, &rtnId, &rtnIdLen);
443 if(ortn) {
444 printSslErrStr("SSLGetPeerID", ortn);
445 goto cleanup;
446 }
447 if((rtnId == NULL) || (rtnIdLen != sizeof(PeerSpec))) {
448 printf("***SSLGetPeerID screwup\n");
449 }
450 else if(memcmp(&peerId, rtnId, rtnIdLen) != 0) {
451 printf("***SSLGetPeerID data mismatch\n");
452 }
453 }
454 if(pargs->allowExpired) {
455 ortn = SSLSetAllowsExpiredCerts(ctx, true);
456 if(ortn) {
457 printSslErrStr("SSLSetAllowExpiredCerts", ortn);
458 goto cleanup;
459 }
460 }
461 if(pargs->allowExpiredRoot) {
462 ortn = SSLSetAllowsExpiredRoots(ctx, true);
463 if(ortn) {
464 printSslErrStr("SSLSetAllowsExpiredRoots", ortn);
465 goto cleanup;
466 }
467 }
468 if(pargs->disableCertVerify) {
469 ortn = SSLSetEnableCertVerify(ctx, false);
470 if(ortn) {
471 printSslErrStr("SSLSetEnableCertVerify", ortn);
472 goto cleanup;
473 }
474 }
475 if(pargs->allowAnyRoot) {
476 ortn = SSLSetAllowsAnyRoot(ctx, true);
477 if(ortn) {
478 printSslErrStr("SSLSetAllowAnyRoot", ortn);
479 goto cleanup;
480 }
481 }
482 if(pargs->cipherRestrict != '\0') {
483 ortn = sslSetCipherRestrictions(ctx, pargs->cipherRestrict);
484 if(ortn) {
485 goto cleanup;
486 }
487 }
488 if(pargs->anchorFile) {
489 ortn = sslAddTrustedRoot(ctx, pargs->anchorFile, pargs->replaceAnchors);
490 if(ortn) {
491 printf("***Error obtaining anchor file %s\n", pargs->anchorFile);
492 goto cleanup;
493 }
494 }
495 if(pargs->clientCerts) {
496 CFArrayRef dummy;
497 if(pargs->anchorFile == NULL) {
498 /* assume this is a root we want to implicitly trust */
499 ortn = addIdentityAsTrustedRoot(ctx, pargs->clientCerts);
500 if(ortn) {
501 goto cleanup;
502 }
503 }
504 ortn = SSLSetCertificate(ctx, pargs->clientCerts);
505 if(ortn) {
506 printSslErrStr("SSLSetCertificate", ortn);
507 goto cleanup;
508 }
509 /* quickie test for this new function */
510 ortn = SSLGetCertificate(ctx, &dummy);
511 if(ortn) {
512 printSslErrStr("SSLGetCertificate", ortn);
513 goto cleanup;
514 }
515 if(dummy != pargs->clientCerts) {
516 printf("***SSLGetCertificate error\n");
517 ortn = errSecIO;
518 goto cleanup;
519 }
520 }
521 if (pargs->alpnNames) {
522 CFMutableDataRef alpn = CFDataCreateMutable(NULL, 0);
523
524 CFArrayForEach(pargs->alpnNames, ^(const void *value) {
525 CFDataRef data = (CFDataRef)value;
526 uint8_t len = CFDataGetLength(data);
527 CFDataAppendBytes(alpn, (const UInt8 *)&len, sizeof(len));
528 CFDataAppend(alpn, data);
529 });
530
531 SSLSetALPNData(ctx, CFDataGetBytePtr(alpn), CFDataGetLength(alpn));
532 SSLSetALPNFunc(ctx, alpnFunc, (void *)NULL);
533 CFRelease(alpn);
534 }
535
536 /*** end options ***/
537
538 if(pargs->verbose) {
539 printf("...starting SSL handshake\n");
540 }
541 startHandshake = CFAbsoluteTimeGetCurrent();
542
543 do
544 { ortn = SSLHandshake(ctx);
545 if((ortn == errSSLWouldBlock) && !pargs->silent) {
546 /* keep UI responsive */
547 sslOutputDot();
548 }
549 } while (ortn == errSSLWouldBlock);
550
551 endHandshake = CFAbsoluteTimeGetCurrent();
552 pargs->handshakeTimeOp = endHandshake - startHandshake;
553 if(pargs->numHandshakes == 0) {
554 /* special case, this one is always way longer */
555 pargs->handshakeTimeFirst = pargs->handshakeTimeOp;
556 }
557 else {
558 /* normal running total */
559 pargs->handshakeTimeTotal += pargs->handshakeTimeOp;
560 }
561 pargs->numHandshakes++;
562
563 ortn = SSLCopyPeerTrust(ctx, &pargs->peerTrust);
564 if(ortn) {
565 printf("***SSLCopyPeerTrust error %" PRIdOSStatus "\n", ortn);
566 pargs->peerTrust = NULL;
567 }
568
569 /* ditto */
570 SSLGetClientCertificateState(ctx, &pargs->certState);
571 SSLGetNegotiatedCipher(ctx, &pargs->negCipher);
572 SSLGetNegotiatedProtocolVersion(ctx, &pargs->negVersion);
573 pargs->sessionIDLength = MAX_SESSION_ID_LENGTH;
574 ortn = SSLGetResumableSessionInfo(ctx, &pargs->sessionWasResumed, pargs->sessionID, &pargs->sessionIDLength);
575 if(!ortn) {
576 OSStatus certRtn = sslEvaluateTrust(ctx, pargs, &pargs->peerCerts);
577
578 if (certRtn && !pargs->manualCertVerify) {
579 SSLCopyPeerCertificates(ctx, &pargs->peerCerts);
580 certRtn = 0;
581 }
582
583 if(certRtn && !ortn ) {
584 ortn = certRtn;
585 }
586 }
587
588 if(ortn) {
589 if(!pargs->silent) {
590 printf("\n");
591 }
592 goto cleanup;
593 }
594
595 if(pargs->verbose) {
596 printf("...SSL handshake complete\n");
597 }
598 length = strlen(pargs->getMsg);
599 (void) SSLWrite(ctx, pargs->getMsg, length, &actLen);
600
601 /*
602 * Try to snag RCV_BUF_SIZE bytes. Exit if (!keepConnected and we get any data
603 * at all), or (keepConnected and err != (none, wouldBlock)).
604 */
605 while (1) {
606 actLen = 0;
607 if(pargs->dumpRxData) {
608 size_t avail = 0;
609
610 ortn = SSLGetBufferedReadSize(ctx, &avail);
611 if(ortn) {
612 printf("***SSLGetBufferedReadSize error\n");
613 break;
614 }
615 if(avail != 0) {
616 printf("\n%d bytes available: ", (int)avail);
617 }
618 }
619 ortn = SSLRead(ctx, rcvBuf, RCV_BUF_SIZE, &actLen);
620 if((actLen == 0) && !pargs->silent) {
621 sslOutputDot();
622 }
623 if((actLen == 0) && (ortn == errSecSuccess)) {
624 printf("***Radar 2984932 confirmed***\n");
625 }
626 if (ortn == errSSLWouldBlock) {
627 /* for this loop, these are identical */
628 ortn = errSecSuccess;
629 }
630 if((actLen > 0) && pargs->dumpRxData) {
631 dumpAscii(rcvBuf, actLen);
632 }
633 if(ortn != errSecSuccess) {
634 /* connection closed by server or by error */
635 break;
636 }
637 if(!pargs->keepConnected && (actLen > 0)) {
638 /* good enough, we connected */
639 break;
640 }
641 }
642 if(!pargs->silent) {
643 printf("\n");
644 }
645
646 /* snag these again in case of renegotiate */
647 SSLGetClientCertificateState(ctx, &pargs->certState);
648 SSLGetNegotiatedCipher(ctx, &pargs->negCipher);
649 SSLGetNegotiatedProtocolVersion(ctx, &pargs->negVersion);
650
651 /* convert normal "shutdown" into zero err rtn */
652 if(ortn == errSSLClosedGraceful) {
653 ortn = errSecSuccess;
654 }
655 if((ortn == errSSLClosedNoNotify) && !pargs->requireNotify) {
656 /* relaxed disconnect rules */
657 ortn = errSecSuccess;
658 }
659 cleanup: ;
660 /*
661 * always do close, even on error - to flush outgoing write queue
662 */
663 OSStatus cerr = SSLClose(ctx);
664 if(ortn == errSecSuccess) {
665 ortn = cerr;
666 }
667 if(sock) {
668 endpointShutdown(sock);
669 }
670 if(ctx) {
671 CFRelease(ctx);
672 }
673 return ortn;
674 }
675
676 #if TARGET_OS_IPHONE
677
678 static void add_key(const void *key, const void *value, void *context) {
679 CFArrayAppendValue((CFMutableArrayRef)context, key);
680 }
681
682
683 static void showInfo(CFDictionaryRef info) {
684 CFIndex dict_count, key_ix, key_count;
685 CFMutableArrayRef keys = NULL;
686 CFIndex maxWidth = 20; /* Maybe precompute this or grab from context? */
687
688 dict_count = CFDictionaryGetCount(info);
689 keys = CFArrayCreateMutable(kCFAllocatorDefault, dict_count,
690 &kCFTypeArrayCallBacks);
691 CFDictionaryApplyFunction(info, add_key, keys);
692 key_count = CFArrayGetCount(keys);
693 CFArraySortValues(keys, CFRangeMake(0, key_count),
694 (CFComparatorFunction)CFStringCompare, 0);
695
696 for (key_ix = 0; key_ix < key_count; ++key_ix) {
697 CFStringRef key = (CFStringRef)CFArrayGetValueAtIndex(keys, key_ix);
698 CFTypeRef value = CFDictionaryGetValue(info, key);
699 CFMutableStringRef line = CFStringCreateMutable(NULL, 0);
700
701 CFStringAppend(line, key);
702 CFIndex jx;
703 for (jx = CFStringGetLength(key);
704 jx < maxWidth; ++jx) {
705 CFStringAppend(line, CFSTR(" "));
706 }
707 CFStringAppend(line, CFSTR(" : "));
708 if (CFStringGetTypeID() == CFGetTypeID(value)) {
709 CFStringAppend(line, (CFStringRef)value);
710 } else if (CFDateGetTypeID() == CFGetTypeID(value)) {
711 CFLocaleRef lc = CFLocaleCopyCurrent();
712 CFDateFormatterRef df = CFDateFormatterCreate(NULL, lc,
713 kCFDateFormatterFullStyle, kCFDateFormatterFullStyle);
714 CFDateRef date = (CFDateRef)value;
715 CFStringRef ds = CFDateFormatterCreateStringWithDate(NULL, df,
716 date);
717 CFStringAppend(line, ds);
718 CFRelease(ds);
719 CFRelease(df);
720 CFRelease(lc);
721 } else if (CFURLGetTypeID() == CFGetTypeID(value)) {
722 CFURLRef url = (CFURLRef)value;
723 CFStringAppend(line, CFSTR("<"));
724 CFStringAppend(line, CFURLGetString(url));
725 CFStringAppend(line, CFSTR(">"));
726 } else if (CFDataGetTypeID() == CFGetTypeID(value)) {
727 CFDataRef v_d = (CFDataRef)value;
728 CFStringRef v_s = CFStringCreateFromExternalRepresentation(
729 kCFAllocatorDefault, v_d, kCFStringEncodingUTF8);
730 if (v_s) {
731 CFStringAppend(line, CFSTR("/"));
732 CFStringAppend(line, v_s);
733 CFStringAppend(line, CFSTR("/ "));
734 CFRelease(v_s);
735 }
736 const uint8_t *bytes = CFDataGetBytePtr(v_d);
737 CFIndex len = CFDataGetLength(v_d);
738 for (jx = 0; jx < len; ++jx) {
739 CFStringAppendFormat(line, NULL, CFSTR("%.02X"), bytes[jx]);
740 }
741 } else {
742 CFStringAppendFormat(line, NULL, CFSTR("%@"), value);
743 }
744 CFStringWriteToFileWithNewline(line, stdout);
745 CFRelease(line);
746 }
747 CFRelease(keys);
748 }
749 #endif
750
751 #pragma clang diagnostic pop
752
753 static void showPeerTrust(SecTrustRef peerTrust, bool verbose) {
754
755 if(peerTrust == NULL) {
756 return;
757 }
758 #if TARGET_OS_IPHONE
759 CFIndex numCerts;
760 CFIndex i;
761
762 printf("\n=============== Peer Trust Properties ===============\n");
763 CFArrayRef plist = SecTrustCopyProperties(peerTrust);
764 if (plist) {
765 print_plist(plist);
766 CFRelease(plist);
767 }
768
769 printf("\n================== Peer Trust Info ==================\n");
770 CFDictionaryRef info = SecTrustCopyInfo(peerTrust);
771 if (info && CFDictionaryGetCount(info)) {
772 showInfo(info);
773 }
774 if (info)
775 CFRelease(info);
776
777 numCerts = SecTrustGetCertificateCount(peerTrust);
778 for(i=0; i<numCerts; i++) {
779 plist = SecTrustCopySummaryPropertiesAtIndex(peerTrust, i);
780 printf("\n============= Peer Trust Cert %lu Summary =============\n\n", i);
781 print_plist(plist);
782 if (plist)
783 CFRelease(plist);
784 printf("\n============= Peer Trust Cert %lu Details =============\n\n", i);
785 plist = SecTrustCopyDetailedPropertiesAtIndex(peerTrust, i);
786 print_plist(plist);
787 if (plist)
788 CFRelease(plist);
789 printf("\n============= End of Peer Trust Cert %lu ==============\n", i);
790 }
791 #endif
792 }
793
794 static void showPeerCerts(
795 CFArrayRef __unused peerCerts,
796 bool __unused verbose)
797 {
798 #if 0
799 CFIndex numCerts;
800 SecCertificateRef certRef;
801 CFIndex i;
802
803 if(peerCerts == NULL) {
804 return;
805 }
806 numCerts = CFArrayGetCount(peerCerts);
807 for(i=0; i<numCerts; i++) {
808 certRef = (SecCertificateRef)CFArrayGetValueAtIndex(peerCerts, i);
809 printf("\n==================== Peer Cert %lu ====================\n\n", i);
810 print_cert(certRef, verbose);
811 printf("\n================ End of Peer Cert %lu =================\n", i);
812 }
813 #endif
814 }
815
816 static void writePeerCerts(
817 CFArrayRef peerCerts,
818 const char *fileBase)
819 {
820 CFIndex numCerts;
821 SecCertificateRef certRef;
822 CFIndex i;
823 char fileName[_maxFileStringSize];
824
825 if(peerCerts == NULL) {
826 return;
827 }
828 numCerts = CFArrayGetCount(peerCerts);
829 for(i=0; i<numCerts; i++) {
830 snprintf(fileName, _maxFileStringSize, "%s%02d.cer", fileBase, (int)i);
831 certRef = (SecCertificateRef)CFArrayGetValueAtIndex(peerCerts, i);
832 CFDataRef derCert = SecCertificateCopyData(certRef);
833 if (derCert) {
834 writeFileSizet(fileName, CFDataGetBytePtr(derCert),
835 CFDataGetLength(derCert));
836 CFRelease(derCert);
837 }
838 }
839 printf("...wrote %lu certs to fileBase %s\n", numCerts, fileBase);
840 }
841
842 /*
843 * Show result of an sslPing().
844 * Assumes the following from sslPingArgs:
845 *
846 * verbose
847 * tryVersion
848 * acceptedProts
849 * negVersion
850 * negCipher
851 * peerCerts
852 * certState
853 * sessionWasResumed
854 * sessionID
855 * sessionIDLength
856 * handshakeTime
857 */
858 static void showSSLResult(
859 const sslPingArgs *pargs,
860 OSStatus err,
861 int displayPeerCerts,
862 char *fileBase) // non-NULL: write certs to file
863 {
864 CFIndex numPeerCerts;
865
866 printf("\n");
867
868 if(pargs->acceptedProts) {
869 printf(" Allowed SSL versions : %s\n", pargs->acceptedProts);
870 }
871 else {
872 printf(" Attempted SSL version : %s\n",
873 sslGetProtocolVersionString(pargs->tryVersion));
874 }
875
876 printf(" Result : %s\n", sslGetSSLErrString(err));
877 printf(" Negotiated SSL version : %s\n",
878 sslGetProtocolVersionString(pargs->negVersion));
879 printf(" Negotiated CipherSuite : %s\n",
880 sslGetCipherSuiteString(pargs->negCipher));
881 if(pargs->certState != kSSLClientCertNone) {
882 printf(" Client Cert State : %s\n",
883 sslGetClientCertStateString(pargs->certState));
884 }
885 if(pargs->verbose) {
886 printf(" Resumed Session : ");
887 if(pargs->sessionWasResumed) {
888 for(unsigned dex=0; dex<pargs->sessionIDLength; dex++) {
889 printf("%02X ", pargs->sessionID[dex]);
890 if(((dex % 8) == 7) && (dex != (pargs->sessionIDLength - 1))) {
891 printf("\n ");
892 }
893 }
894 printf("\n");
895 }
896 else {
897 printf("NOT RESUMED\n");
898 }
899 printf(" Handshake time : %f seconds\n", pargs->handshakeTimeOp);
900 }
901 if(pargs->peerCerts == NULL) {
902 numPeerCerts = 0;
903 }
904 else {
905 numPeerCerts = CFArrayGetCount(pargs->peerCerts);
906 }
907 printf(" Number of server certs : %lu\n", numPeerCerts);
908 if(numPeerCerts != 0) {
909 if (displayPeerCerts == 1) {
910 showPeerCerts(pargs->peerCerts, false);
911 } else if (displayPeerCerts == 2) {
912 showPeerTrust(pargs->peerTrust, false);
913 }
914 if(fileBase != NULL) {
915 writePeerCerts(pargs->peerCerts, fileBase);
916 }
917 }
918
919 printf("\n");
920 }
921
922 static int verifyProtocol(
923 bool verifyProt,
924 SSLProtocol maxProtocol,
925 SSLProtocol reqProtocol,
926 SSLProtocol negProtocol)
927 {
928 if(!verifyProt) {
929 return 0;
930 }
931 if(reqProtocol > maxProtocol) {
932 /* known not to support this attempt, relax */
933 reqProtocol = maxProtocol;
934 }
935 if(reqProtocol != negProtocol) {
936 printf("***Expected protocol %s; negotiated %s\n",
937 sslGetProtocolVersionString(reqProtocol),
938 sslGetProtocolVersionString(negProtocol));
939 return 1;
940 }
941 else {
942 return 0;
943 }
944 }
945
946 static int verifyClientCertState(
947 bool verifyCertState,
948 SSLClientCertificateState expectState,
949 SSLClientCertificateState gotState)
950 {
951 if(!verifyCertState) {
952 return 0;
953 }
954 if(expectState == gotState) {
955 return 0;
956 }
957 printf("***Expected clientCertState %s; got %s\n",
958 sslGetClientCertStateString(expectState),
959 sslGetClientCertStateString(gotState));
960 return 1;
961 }
962
963 static SSLProtocol charToProt(
964 char c, // 2, 3, t
965 char **argv)
966 {
967 switch(c) {
968 case '2':
969 return kSSLProtocol2;
970 case '3':
971 return kSSLProtocol3;
972 case 't':
973 return kTLSProtocol12;
974 default:
975 usage(argv);
976 }
977 }
978
979 int main(int argc, char **argv)
980 {
981 OSStatus err;
982 int arg;
983 char *argp;
984 char getMsg[300];
985 char fullFileBase[_maxFileStringSize];
986 int ourRtn = 0; // exit status - sum of all errors
987 unsigned loop;
988 SecKeychainRef serverKc = nil;
989 sslPingArgs pargs;
990
991 /* user-spec'd parameters */
992 const char *getPath = DEFAULT_PATH;
993 char *fileBase = NULL;
994 int displayCerts = 0;
995 bool doSslV2 = false;
996 bool doSslV3 = false;
997 bool doTlsV1 = true;
998 bool doTlsV11 = false;
999 bool doTlsV12 = false;
1000 bool protXOnly = false; // kSSLProtocol3Only, kTLSProtocol1Only
1001 bool doProtUnknown = false;
1002 unsigned loopCount = 1;
1003 bool doPause = false;
1004 bool pauseFirstLoop = false;
1005 bool verifyProt = false;
1006 SSLProtocol maxProtocol = kTLSProtocol12; // for verifying negotiated
1007 // protocol
1008 char *acceptedProts = NULL;
1009 char *keyChainName = NULL;
1010 char *getMsgSpec = NULL;
1011 bool vfyCertState = false;
1012 SSLClientCertificateState expectCertState = kSSLClientCertNone;
1013 bool displayHandshakeTimes = false;
1014 bool completeCertChain = false;
1015
1016 /* special case - one arg of "h" or "-h" or "hv" */
1017 if(argc == 2) {
1018 if((strcmp(argv[1], "h") == 0) || (strcmp(argv[1], "-h") == 0)) {
1019 usage(argv);
1020 }
1021 if(strcmp(argv[1], "hv") == 0) {
1022 usageVerbose(argv);
1023 }
1024 }
1025
1026 /* set up defaults */
1027 memset(&pargs, 0, sizeof(sslPingArgs));
1028 pargs.hostName = DEFAULT_HOST;
1029 pargs.port = DEFAULT_PORT;
1030 pargs.resumableEnable = true;
1031 pargs.argv = argv;
1032
1033 for(arg=1; arg<argc; arg++) {
1034 argp = argv[arg];
1035 if(arg == 1) {
1036 /* first arg, is always hostname; '-' means default */
1037 if(argp[0] != '-') {
1038 pargs.hostName = argp;
1039 }
1040 continue;
1041 }
1042 if(argp[0] == '/') {
1043 /* path always starts with leading slash */
1044 getPath = argp;
1045 continue;
1046 }
1047 /* options */
1048 switch(argp[0]) {
1049 case 'Z': {
1050 if(++arg == argc) {
1051 /* requires another arg */
1052 usage(argv);
1053 }
1054 if (pargs.alpnNames == NULL) {
1055 pargs.alpnNames = CFArrayCreateMutableForCFTypes(NULL);
1056 }
1057
1058 CFDataRef alpn = CFDataCreate(NULL, (const UInt8 *)argv[arg], strlen(argv[arg]));
1059 CFArrayAppendValue(pargs.alpnNames, alpn);
1060 CFReleaseNull(alpn);
1061 break;
1062 }
1063 case 'W':
1064 case 'w': {
1065 CFDictionaryRef context = NULL;
1066
1067 if(++arg == argc) {
1068 /* requires another arg */
1069 usage(argv);
1070 }
1071
1072 if (argp[0] == 'W') {
1073 context = CFDictionaryCreateForCFTypes(NULL,
1074 CFSTR("AppleServerAuthenticationAllowUATAPN"), kCFBooleanTrue,
1075 CFSTR("AppleServerAuthenticationAllowUATIDS"), kCFBooleanTrue,
1076 CFSTR("AppleServerAuthenticationAllowUATGS"), kCFBooleanTrue,
1077 NULL);
1078 }
1079 const char *verifyName = pargs.hostName;
1080
1081 if (pargs.policies == NULL) {
1082 pargs.policies = CFArrayCreateMutableForCFTypes(NULL);
1083 }
1084
1085 if (pargs.vfyHostName)
1086 verifyName = pargs.vfyHostName;
1087
1088 SecPolicyRef policy = NULL;
1089 CFStringRef hostname = CFStringCreateWithCString(NULL, verifyName, kCFStringEncodingUTF8);
1090
1091 if (strcasecmp(argv[arg], "PushLegacy") == 0) {
1092 policy = SecPolicyCreateApplePushServiceLegacy(hostname);
1093 } else if (strcasecmp(argv[arg], "Push") == 0) {
1094 policy = SecPolicyCreateApplePushService(hostname, context);
1095 } else if (strcasecmp(argv[arg], "IDS") == 0) {
1096 policy = SecPolicyCreateAppleIDSServiceContext(hostname, context);
1097 } else if (strcasecmp(argv[arg], "GS") == 0) {
1098 policy = SecPolicyCreateAppleGSService(hostname, context);
1099 } else {
1100 printf("unknown policy: %s", argv[arg]);
1101 CFReleaseNull(hostname);
1102 CFReleaseNull(context);
1103 usage(argv);
1104 }
1105
1106 if (policy) {
1107 CFArrayAppendValue(pargs.policies, policy);
1108 }
1109
1110 CFReleaseNull(policy);
1111 CFReleaseNull(hostname);
1112 CFReleaseNull(context);
1113
1114 break;
1115 }
1116 case 'e':
1117 pargs.allowExpired = true;
1118 break;
1119 case 'E':
1120 pargs.allowExpiredRoot = true;
1121 break;
1122 case 'x':
1123 pargs.disableCertVerify = true;
1124 break;
1125 case 'M':
1126 pargs.disableCertVerify = true; // implied
1127 pargs.manualCertVerify = true;
1128 break;
1129 case 'a':
1130 if(++arg == argc) {
1131 /* requires another arg */
1132 usage(argv);
1133 }
1134 pargs.anchorFile = argv[arg];
1135 break;
1136 case 'A':
1137 if(++arg == argc) {
1138 /* requires another arg */
1139 usage(argv);
1140 }
1141 pargs.anchorFile = argv[arg];
1142 pargs.replaceAnchors = true;
1143 break;
1144 case 'r':
1145 pargs.allowAnyRoot = true;
1146 break;
1147 case 'd':
1148 pargs.dumpRxData = true;
1149 break;
1150 case 'c':
1151 displayCerts++;
1152 break;
1153 case 'f':
1154 if(++arg == argc) {
1155 /* requires another arg */
1156 usage(argv);
1157 }
1158 fileBase = argv[arg];
1159 break;
1160 case 'C':
1161 pargs.cipherRestrict = argp[2];
1162 break;
1163 case '2':
1164 doSslV3 = doTlsV1 = doTlsV11 = false;
1165 doSslV2 = true;
1166 break;
1167 case '3':
1168 doSslV2 = doTlsV1 = doTlsV11 = doTlsV12 = false;
1169 doSslV3 = true;
1170 break;
1171 case 't':
1172 doSslV2 = doSslV3 = doTlsV11 = doTlsV12 = false;
1173 doTlsV1 = true;
1174 break;
1175 case '%':
1176 doSslV2 = doSslV3 = doTlsV1 = false;
1177 doTlsV11 = true;
1178 break;
1179 case '^':
1180 doSslV2 = doSslV3 = doTlsV1 = doTlsV12 = false;
1181 doTlsV12 = true;
1182 break;
1183 case 'L':
1184 doSslV2 = doSslV3 = doTlsV1 = doTlsV11 = doTlsV12 = true;
1185 break;
1186 case 'o':
1187 protXOnly = true;
1188 break;
1189 case 'u':
1190 doSslV2 = doSslV3 = doTlsV1 = doTlsV11 = doTlsV12 = false;
1191 doProtUnknown = true;
1192 break;
1193 case 'K':
1194 pargs.keepConnected = true;
1195 break;
1196 case 'n':
1197 pargs.requireNotify = true;
1198 pargs.keepConnected = true;
1199 break;
1200 case 'R':
1201 pargs.resumableEnable = false;
1202 break;
1203 case 'b':
1204 pargs.nonBlocking = true;
1205 break;
1206 case 'v':
1207 verifyProt = true;
1208 break;
1209 case 'm':
1210 if(argp[1] != '=') {
1211 usage(argv);
1212 }
1213 verifyProt = true; // implied
1214 maxProtocol = charToProt(argp[2], argv);
1215 break;
1216 case 'g':
1217 if(argp[1] != '=') {
1218 usage(argv);
1219 }
1220 acceptedProts = argv[arg];
1221 doSslV3 = doSslV2 = doTlsV1 = doTlsV11 = doTlsV12 = false;
1222 break;
1223 case 'l':
1224 if(++arg == argc) {
1225 /* requires another arg */
1226 usage(argv);
1227 }
1228 int parsedLoopCount = atoi(argv[arg]);
1229 if (parsedLoopCount <= 0) {
1230 printf("***bad loopCount\n");
1231 usage(argv);
1232 }
1233 loopCount = (unsigned) parsedLoopCount;
1234 break;
1235 case 'P':
1236 if(++arg == argc) {
1237 /* requires another arg */
1238 usage(argv);
1239 }
1240 pargs.port = atoi(argv[arg]);
1241 break;
1242 case 'H':
1243 pargs.allowHostnameSpoof = true;
1244 break;
1245 case 'F':
1246 if(++arg == argc) {
1247 /* requires another arg */
1248 usage(argv);
1249 }
1250 pargs.vfyHostName = argv[arg];
1251 break;
1252 case 'k':
1253 if(++arg == argc) {
1254 /* requires another arg */
1255 usage(argv);
1256 }
1257 keyChainName = &argp[2];
1258 break;
1259 case 'G':
1260 getMsgSpec = &argp[2];
1261 break;
1262 case 'T':
1263 if(argp[1] != '=') {
1264 usage(argv);
1265 }
1266 vfyCertState = true;
1267 switch(argp[2]) {
1268 case 'n':
1269 expectCertState = kSSLClientCertNone;
1270 break;
1271 case 'r':
1272 expectCertState = kSSLClientCertRequested;
1273 break;
1274 case 's':
1275 expectCertState = kSSLClientCertSent;
1276 break;
1277 case 'j':
1278 expectCertState = kSSLClientCertRejected;
1279 break;
1280 default:
1281 usage(argv);
1282 }
1283 break;
1284 case 'z':
1285 pargs.password = &argp[2];
1286 break;
1287 case 'p':
1288 doPause = true;
1289 break;
1290 case '7':
1291 pauseFirstLoop = true;
1292 break;
1293 case 'q':
1294 pargs.quiet = true;
1295 break;
1296 case 'V':
1297 pargs.verbose = true;
1298 break;
1299 case 's':
1300 pargs.silent = pargs.quiet = true;
1301 break;
1302 case 'N':
1303 displayHandshakeTimes = true;
1304 break;
1305 case '8':
1306 completeCertChain = true;
1307 break;
1308 case 'h':
1309 if(pargs.verbose || (argp[1] == 'v')) {
1310 usageVerbose(argv);
1311 }
1312 else {
1313 usage(argv);
1314 }
1315 default:
1316 usage(argv);
1317 }
1318 }
1319 if(getMsgSpec) {
1320 pargs.getMsg = getMsgSpec;
1321 }
1322 else {
1323 sprintf(getMsg, "%s %s %s",
1324 DEFAULT_GETMSG, getPath, DEFAULT_GET_SUFFIX);
1325 pargs.getMsg = getMsg;
1326 }
1327
1328 /* get client cert and optional encryption cert as CFArrayRef */
1329 if(keyChainName) {
1330 pargs.clientCerts = getSslCerts(keyChainName, false, completeCertChain,
1331 pargs.anchorFile, &serverKc);
1332 if(pargs.clientCerts == nil) {
1333 exit(1);
1334 }
1335 #ifdef USE_CDSA_CRYPTO
1336 if(pargs.password) {
1337 OSStatus ortn = SecKeychainUnlock(serverKc,
1338 strlen(pargs.password), pargs.password, true);
1339 if(ortn) {
1340 printf("SecKeychainUnlock returned %d\n", (int)ortn);
1341 /* oh well */
1342 }
1343 }
1344 #endif
1345 }
1346
1347 {
1348 struct sigaction sa;
1349 memset(&sa, 0, sizeof(sa));
1350 sa.sa_flags = SA_RESTART;
1351 sa.sa_handler = sigpipe;
1352 sigaction(SIGPIPE, &sa, NULL);
1353 }
1354
1355 for(loop=0; loop<loopCount; loop++) {
1356 /*
1357 * One pass for each protocol version, skipping any explicit version if
1358 * an attempt at a higher version and succeeded in doing so successfully fell
1359 * back.
1360 */
1361 if(doTlsV12) {
1362 pargs.tryVersion = kTLSProtocol12;
1363 pargs.acceptedProts = NULL;
1364 if(!pargs.silent) {
1365 printf("Connecting to host %s with TLS V1.2...", pargs.hostName);
1366 }
1367 fflush(stdout);
1368 err = sslPing(&pargs);
1369 if(err) {
1370 ourRtn++;
1371 }
1372 if(!pargs.quiet) {
1373 if(fileBase) {
1374 snprintf(fullFileBase, _maxFileStringSize, "%s_v3.1", fileBase);
1375 }
1376 showSSLResult(&pargs,
1377 err,
1378 displayCerts,
1379 fileBase ? fullFileBase : NULL);
1380 }
1381 CFReleaseNull(pargs.peerCerts);
1382 if(!err) {
1383 /* deal with fallbacks, skipping redundant tests */
1384 switch(pargs.negVersion) {
1385 case kTLSProtocol11:
1386 doTlsV11 =false;
1387 break;
1388 case kTLSProtocol1:
1389 doTlsV11 =false;
1390 doTlsV1 =false;
1391 break;
1392 case kSSLProtocol3:
1393 doTlsV11 =false;
1394 doTlsV1 =false;
1395 doSslV3 = false;
1396 break;
1397 case kSSLProtocol2:
1398 doTlsV11 =false;
1399 doTlsV1 =false;
1400 doSslV3 = false;
1401 doSslV2 = false;
1402 break;
1403 default:
1404 break;
1405 }
1406 ourRtn += verifyProtocol(verifyProt, maxProtocol, kTLSProtocol12,
1407 pargs.negVersion);
1408 }
1409 /* note we do this regardless since the client state might be
1410 * the cause of a failure */
1411 ourRtn += verifyClientCertState(vfyCertState, expectCertState,
1412 pargs.certState);
1413 }
1414 if(doTlsV11) {
1415 pargs.tryVersion = kTLSProtocol11;
1416 pargs.acceptedProts = NULL;
1417 if(!pargs.silent) {
1418 printf("Connecting to host %s with TLS V1.1...", pargs.hostName);
1419 }
1420 fflush(stdout);
1421 err = sslPing(&pargs);
1422 if(err) {
1423 ourRtn++;
1424 }
1425 if(!pargs.quiet) {
1426 if(fileBase) {
1427 snprintf(fullFileBase, _maxFileStringSize, "%s_v3.1", fileBase);
1428 }
1429 showSSLResult(&pargs,
1430 err,
1431 displayCerts,
1432 fileBase ? fullFileBase : NULL);
1433 }
1434 CFReleaseNull(pargs.peerCerts);
1435 if(!err) {
1436 /* deal with fallbacks, skipping redundant tests */
1437 switch(pargs.negVersion) {
1438 case kTLSProtocol1:
1439 doTlsV1 =false;
1440 break;
1441 case kSSLProtocol3:
1442 doTlsV1 =false;
1443 doSslV3 = false;
1444 break;
1445 case kSSLProtocol2:
1446 doTlsV1 =false;
1447 doSslV3 = false;
1448 doSslV2 = false;
1449 break;
1450 default:
1451 break;
1452 }
1453 ourRtn += verifyProtocol(verifyProt, maxProtocol, kTLSProtocol11,
1454 pargs.negVersion);
1455 }
1456 /* note we do this regardless since the client state might be
1457 * the cause of a failure */
1458 ourRtn += verifyClientCertState(vfyCertState, expectCertState,
1459 pargs.certState);
1460 }
1461 if(doTlsV1) {
1462 pargs.tryVersion =
1463 protXOnly ? kTLSProtocol1Only : kTLSProtocol1;
1464 pargs.acceptedProts = NULL;
1465 if(!pargs.silent) {
1466 printf("Connecting to host %s with TLS V1...", pargs.hostName);
1467 }
1468 fflush(stdout);
1469 err = sslPing(&pargs);
1470 if(err) {
1471 ourRtn++;
1472 }
1473 if(!pargs.quiet) {
1474 if(fileBase) {
1475 snprintf(fullFileBase, _maxFileStringSize, "%s_v3.1", fileBase);
1476 }
1477 showSSLResult(&pargs,
1478 err,
1479 displayCerts,
1480 fileBase ? fullFileBase : NULL);
1481 }
1482 CFReleaseNull(pargs.peerCerts);
1483 if(!err) {
1484 /* deal with fallbacks, skipping redundant tests */
1485 switch(pargs.negVersion) {
1486 case kSSLProtocol3:
1487 doSslV3 = false;
1488 break;
1489 case kSSLProtocol2:
1490 doSslV3 = false;
1491 doSslV2 = false;
1492 break;
1493 default:
1494 break;
1495 }
1496 ourRtn += verifyProtocol(verifyProt, maxProtocol, kTLSProtocol1,
1497 pargs.negVersion);
1498 }
1499 /* note we do this regardless since the client state might be
1500 * the cause of a failure */
1501 ourRtn += verifyClientCertState(vfyCertState, expectCertState,
1502 pargs.certState);
1503 }
1504 if(doSslV3) {
1505 pargs.tryVersion = protXOnly ? kSSLProtocol3Only : kSSLProtocol3;
1506 pargs.acceptedProts = NULL;
1507 if(!pargs.silent) {
1508 printf("Connecting to host %s with SSL V3...", pargs.hostName);
1509 }
1510 fflush(stdout);
1511 err = sslPing(&pargs);
1512 if(err) {
1513 ourRtn++;
1514 }
1515 if(!pargs.quiet) {
1516 if(fileBase) {
1517 snprintf(fullFileBase, _maxFileStringSize, "%s_v3.0", fileBase);
1518 }
1519 showSSLResult(&pargs,
1520 err,
1521 displayCerts,
1522 fileBase ? fullFileBase : NULL);
1523 }
1524 CFReleaseNull(pargs.peerCerts);
1525 if(!err) {
1526 /* deal with fallbacks, skipping redundant tests */
1527 switch(pargs.negVersion) {
1528 case kSSLProtocol2:
1529 doSslV2 = false;
1530 break;
1531 default:
1532 break;
1533 }
1534 ourRtn += verifyProtocol(verifyProt, maxProtocol, kSSLProtocol3,
1535 pargs.negVersion);
1536 }
1537 /* note we do this regardless since the client state might be
1538 * the cause of a failure */
1539 ourRtn += verifyClientCertState(vfyCertState, expectCertState,
1540 pargs.certState);
1541 }
1542
1543 if(doSslV2) {
1544 if(fileBase) {
1545 snprintf(fullFileBase, _maxFileStringSize, "%s_v2", fileBase);
1546 }
1547 if(!pargs.silent) {
1548 printf("Connecting to host %s with SSL V2...", pargs.hostName);
1549 }
1550 fflush(stdout);
1551 pargs.tryVersion = kSSLProtocol2;
1552 pargs.acceptedProts = NULL;
1553 err = sslPing(&pargs);
1554 if(err) {
1555 ourRtn++;
1556 }
1557 if(!pargs.quiet) {
1558 if(fileBase) {
1559 snprintf(fullFileBase, _maxFileStringSize, "%s_v2", fileBase);
1560 }
1561 showSSLResult(&pargs,
1562 err,
1563 displayCerts,
1564 fileBase ? fullFileBase : NULL);
1565 }
1566 CFReleaseNull(pargs.peerCerts);
1567 if(!err) {
1568 ourRtn += verifyProtocol(verifyProt, maxProtocol, kSSLProtocol2,
1569 pargs.negVersion);
1570 }
1571 /* note we do this regardless since the client state might be
1572 * the cause of a failure */
1573 ourRtn += verifyClientCertState(vfyCertState, expectCertState,
1574 pargs.certState);
1575 }
1576 if(doProtUnknown) {
1577 if(!pargs.silent) {
1578 printf("Connecting to host %s with kSSLProtocolUnknown...",
1579 pargs.hostName);
1580 }
1581 fflush(stdout);
1582 pargs.tryVersion = kSSLProtocolUnknown;
1583 pargs.acceptedProts = NULL;
1584 err = sslPing(&pargs);
1585 if(err) {
1586 ourRtn++;
1587 }
1588 if(!pargs.quiet) {
1589 if(fileBase) {
1590 snprintf(fullFileBase, _maxFileStringSize, "%s_def", fileBase);
1591 }
1592 showSSLResult(&pargs,
1593 err,
1594 displayCerts,
1595 fileBase ? fullFileBase : NULL);
1596 }
1597 CFReleaseNull(pargs.peerCerts);
1598 }
1599 if(acceptedProts != NULL) {
1600 pargs.acceptedProts = acceptedProts;
1601 pargs.tryVersion = kSSLProtocolUnknown; // not used
1602 if(!pargs.silent) {
1603 printf("Connecting to host %s with acceptedProts %s...",
1604 pargs.hostName, pargs.acceptedProts);
1605 }
1606 fflush(stdout);
1607 err = sslPing(&pargs);
1608 if(err) {
1609 ourRtn++;
1610 }
1611 if(!pargs.quiet) {
1612 if(fileBase) {
1613 snprintf(fullFileBase, _maxFileStringSize, "%s_def", fileBase);
1614 }
1615 showSSLResult(&pargs,
1616 err,
1617 displayCerts,
1618 fileBase ? fullFileBase : NULL);
1619 }
1620 CFReleaseNull(pargs.peerCerts);
1621 }
1622 if(doPause ||
1623 (pauseFirstLoop &&
1624 /* pause after first, before last to grab trace */
1625 ((loop == 0) || (loop == loopCount - 1))
1626 )
1627 ) {
1628 char resp;
1629 fpurge(stdin);
1630 printf("a to abort, c to continue: ");
1631 resp = (char) getchar();
1632 if(resp == 'a') {
1633 break;
1634 }
1635 }
1636 } /* main loop */
1637 if(displayHandshakeTimes) {
1638 CFAbsoluteTime totalTime;
1639 unsigned numHandshakes;
1640 if(pargs.numHandshakes == 1) {
1641 /* just display the first one */
1642 totalTime = pargs.handshakeTimeFirst;
1643 numHandshakes = 1;
1644 }
1645 else {
1646 /* skip the first one */
1647 totalTime = pargs.handshakeTimeTotal;
1648 numHandshakes = pargs.numHandshakes - 1;
1649 }
1650 if(numHandshakes != 0) {
1651 printf(" %u handshakes in %f seconds; %f seconds per handshake\n",
1652 numHandshakes, totalTime,
1653 (totalTime / numHandshakes));
1654 }
1655 }
1656
1657 if(ourRtn) {
1658 printf("===%s exiting with %d %s for host %s\n", argv[0], ourRtn,
1659 (ourRtn > 1) ? "errors" : "error", pargs.hostName);
1660 }
1661 return ourRtn;
1662
1663 }