]> git.saurik.com Git - apple/security.git/blob - sslViewer/SSLViewer.c
Security-59754.80.3.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/sharedTool/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 = SecTrustGetTrustResult(secTrust, &secTrustResult); // implicitly does trust evaluate
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 ortn = SSLSetProtocolVersionMax(ctx, pargs->tryVersion);
430 if(ortn) {
431 printSslErrStr("SSLSetProtocolVersionMax", ortn);
432 goto cleanup;
433 }
434 }
435
436 if(pargs->resumableEnable) {
437 const void *rtnId = NULL;
438 size_t rtnIdLen = 0;
439
440 ortn = SSLSetPeerID(ctx, &peerId, sizeof(PeerSpec));
441 if(ortn) {
442 printSslErrStr("SSLSetPeerID", ortn);
443 goto cleanup;
444 }
445 /* quick test of the get fcn */
446 ortn = SSLGetPeerID(ctx, &rtnId, &rtnIdLen);
447 if(ortn) {
448 printSslErrStr("SSLGetPeerID", ortn);
449 goto cleanup;
450 }
451 if((rtnId == NULL) || (rtnIdLen != sizeof(PeerSpec))) {
452 printf("***SSLGetPeerID screwup\n");
453 }
454 else if(memcmp(&peerId, rtnId, rtnIdLen) != 0) {
455 printf("***SSLGetPeerID data mismatch\n");
456 }
457 }
458 if(pargs->allowExpired) {
459 ortn = SSLSetAllowsExpiredCerts(ctx, true);
460 if(ortn) {
461 printSslErrStr("SSLSetAllowExpiredCerts", ortn);
462 goto cleanup;
463 }
464 }
465 if(pargs->allowExpiredRoot) {
466 ortn = SSLSetAllowsExpiredRoots(ctx, true);
467 if(ortn) {
468 printSslErrStr("SSLSetAllowsExpiredRoots", ortn);
469 goto cleanup;
470 }
471 }
472 if(pargs->disableCertVerify) {
473 ortn = SSLSetEnableCertVerify(ctx, false);
474 if(ortn) {
475 printSslErrStr("SSLSetEnableCertVerify", ortn);
476 goto cleanup;
477 }
478 }
479 if(pargs->allowAnyRoot) {
480 ortn = SSLSetAllowsAnyRoot(ctx, true);
481 if(ortn) {
482 printSslErrStr("SSLSetAllowAnyRoot", ortn);
483 goto cleanup;
484 }
485 }
486 if(pargs->cipherRestrict != '\0') {
487 ortn = sslSetCipherRestrictions(ctx, pargs->cipherRestrict);
488 if(ortn) {
489 goto cleanup;
490 }
491 }
492 if(pargs->anchorFile) {
493 ortn = sslAddTrustedRoot(ctx, pargs->anchorFile, pargs->replaceAnchors);
494 if(ortn) {
495 printf("***Error obtaining anchor file %s\n", pargs->anchorFile);
496 goto cleanup;
497 }
498 }
499 if(pargs->clientCerts) {
500 CFArrayRef dummy;
501 if(pargs->anchorFile == NULL) {
502 /* assume this is a root we want to implicitly trust */
503 ortn = addIdentityAsTrustedRoot(ctx, pargs->clientCerts);
504 if(ortn) {
505 goto cleanup;
506 }
507 }
508 ortn = SSLSetCertificate(ctx, pargs->clientCerts);
509 if(ortn) {
510 printSslErrStr("SSLSetCertificate", ortn);
511 goto cleanup;
512 }
513 /* quickie test for this new function */
514 ortn = SSLGetCertificate(ctx, &dummy);
515 if(ortn) {
516 printSslErrStr("SSLGetCertificate", ortn);
517 goto cleanup;
518 }
519 if(dummy != pargs->clientCerts) {
520 printf("***SSLGetCertificate error\n");
521 ortn = errSecIO;
522 goto cleanup;
523 }
524 }
525 if (pargs->alpnNames) {
526 CFMutableDataRef alpn = CFDataCreateMutable(NULL, 0);
527
528 CFArrayForEach(pargs->alpnNames, ^(const void *value) {
529 CFDataRef data = (CFDataRef)value;
530 uint8_t len = CFDataGetLength(data);
531 CFDataAppendBytes(alpn, (const UInt8 *)&len, sizeof(len));
532 CFDataAppend(alpn, data);
533 });
534
535 SSLSetALPNData(ctx, CFDataGetBytePtr(alpn), CFDataGetLength(alpn));
536 SSLSetALPNFunc(ctx, alpnFunc, (void *)NULL);
537 CFRelease(alpn);
538 }
539
540 /*** end options ***/
541
542 if(pargs->verbose) {
543 printf("...starting SSL handshake\n");
544 }
545 startHandshake = CFAbsoluteTimeGetCurrent();
546
547 do
548 { ortn = SSLHandshake(ctx);
549 if((ortn == errSSLWouldBlock) && !pargs->silent) {
550 /* keep UI responsive */
551 sslOutputDot();
552 }
553 } while (ortn == errSSLWouldBlock);
554
555 endHandshake = CFAbsoluteTimeGetCurrent();
556 pargs->handshakeTimeOp = endHandshake - startHandshake;
557 if(pargs->numHandshakes == 0) {
558 /* special case, this one is always way longer */
559 pargs->handshakeTimeFirst = pargs->handshakeTimeOp;
560 }
561 else {
562 /* normal running total */
563 pargs->handshakeTimeTotal += pargs->handshakeTimeOp;
564 }
565 pargs->numHandshakes++;
566
567 ortn = SSLCopyPeerTrust(ctx, &pargs->peerTrust);
568 if(ortn) {
569 printf("***SSLCopyPeerTrust error %" PRIdOSStatus "\n", ortn);
570 pargs->peerTrust = NULL;
571 }
572
573 /* ditto */
574 SSLGetClientCertificateState(ctx, &pargs->certState);
575 SSLGetNegotiatedCipher(ctx, &pargs->negCipher);
576 SSLGetNegotiatedProtocolVersion(ctx, &pargs->negVersion);
577 pargs->sessionIDLength = MAX_SESSION_ID_LENGTH;
578 ortn = SSLGetResumableSessionInfo(ctx, &pargs->sessionWasResumed, pargs->sessionID, &pargs->sessionIDLength);
579 if(!ortn) {
580 OSStatus certRtn = sslEvaluateTrust(ctx, pargs, &pargs->peerCerts);
581
582 if (certRtn && !pargs->manualCertVerify) {
583 SSLCopyPeerCertificates(ctx, &pargs->peerCerts);
584 certRtn = 0;
585 }
586
587 if(certRtn && !ortn ) {
588 ortn = certRtn;
589 }
590 }
591
592 if(ortn) {
593 if(!pargs->silent) {
594 printf("\n");
595 }
596 goto cleanup;
597 }
598
599 if(pargs->verbose) {
600 printf("...SSL handshake complete\n");
601 }
602 length = strlen(pargs->getMsg);
603 (void) SSLWrite(ctx, pargs->getMsg, length, &actLen);
604
605 /*
606 * Try to snag RCV_BUF_SIZE bytes. Exit if (!keepConnected and we get any data
607 * at all), or (keepConnected and err != (none, wouldBlock)).
608 */
609 while (1) {
610 actLen = 0;
611 if(pargs->dumpRxData) {
612 size_t avail = 0;
613
614 ortn = SSLGetBufferedReadSize(ctx, &avail);
615 if(ortn) {
616 printf("***SSLGetBufferedReadSize error\n");
617 break;
618 }
619 if(avail != 0) {
620 printf("\n%d bytes available: ", (int)avail);
621 }
622 }
623 ortn = SSLRead(ctx, rcvBuf, RCV_BUF_SIZE, &actLen);
624 if((actLen == 0) && !pargs->silent) {
625 sslOutputDot();
626 }
627 if((actLen == 0) && (ortn == errSecSuccess)) {
628 printf("***Radar 2984932 confirmed***\n");
629 }
630 if (ortn == errSSLWouldBlock) {
631 /* for this loop, these are identical */
632 ortn = errSecSuccess;
633 }
634 if((actLen > 0) && pargs->dumpRxData) {
635 dumpAscii(rcvBuf, actLen);
636 }
637 if(ortn != errSecSuccess) {
638 /* connection closed by server or by error */
639 break;
640 }
641 if(!pargs->keepConnected && (actLen > 0)) {
642 /* good enough, we connected */
643 break;
644 }
645 }
646 if(!pargs->silent) {
647 printf("\n");
648 }
649
650 /* snag these again in case of renegotiate */
651 SSLGetClientCertificateState(ctx, &pargs->certState);
652 SSLGetNegotiatedCipher(ctx, &pargs->negCipher);
653 SSLGetNegotiatedProtocolVersion(ctx, &pargs->negVersion);
654
655 /* convert normal "shutdown" into zero err rtn */
656 if(ortn == errSSLClosedGraceful) {
657 ortn = errSecSuccess;
658 }
659 if((ortn == errSSLClosedNoNotify) && !pargs->requireNotify) {
660 /* relaxed disconnect rules */
661 ortn = errSecSuccess;
662 }
663 cleanup: ;
664 /*
665 * always do close, even on error - to flush outgoing write queue
666 */
667 OSStatus cerr = errSecParam;
668 if (ctx) {
669 cerr = SSLClose(ctx);
670 }
671 if(ortn == errSecSuccess) {
672 ortn = cerr;
673 }
674 if(sock) {
675 endpointShutdown(sock);
676 }
677 if(ctx) {
678 CFRelease(ctx);
679 }
680 return ortn;
681 }
682
683 #if TARGET_OS_IPHONE
684
685 static void add_key(const void *key, const void *value, void *context) {
686 CFArrayAppendValue((CFMutableArrayRef)context, key);
687 }
688
689
690 static void showInfo(CFDictionaryRef info) {
691 CFIndex dict_count, key_ix, key_count;
692 CFMutableArrayRef keys = NULL;
693 CFIndex maxWidth = 20; /* Maybe precompute this or grab from context? */
694
695 dict_count = CFDictionaryGetCount(info);
696 keys = CFArrayCreateMutable(kCFAllocatorDefault, dict_count,
697 &kCFTypeArrayCallBacks);
698 CFDictionaryApplyFunction(info, add_key, keys);
699 key_count = CFArrayGetCount(keys);
700 CFArraySortValues(keys, CFRangeMake(0, key_count),
701 (CFComparatorFunction)CFStringCompare, 0);
702
703 for (key_ix = 0; key_ix < key_count; ++key_ix) {
704 CFStringRef key = (CFStringRef)CFArrayGetValueAtIndex(keys, key_ix);
705 CFTypeRef value = CFDictionaryGetValue(info, key);
706 CFMutableStringRef line = CFStringCreateMutable(NULL, 0);
707
708 CFStringAppend(line, key);
709 CFIndex jx;
710 for (jx = CFStringGetLength(key);
711 jx < maxWidth; ++jx) {
712 CFStringAppend(line, CFSTR(" "));
713 }
714 CFStringAppend(line, CFSTR(" : "));
715 if (CFStringGetTypeID() == CFGetTypeID(value)) {
716 CFStringAppend(line, (CFStringRef)value);
717 } else if (CFDateGetTypeID() == CFGetTypeID(value)) {
718 CFLocaleRef lc = CFLocaleCopyCurrent();
719 CFDateFormatterRef df = CFDateFormatterCreate(NULL, lc,
720 kCFDateFormatterFullStyle, kCFDateFormatterFullStyle);
721 CFDateRef date = (CFDateRef)value;
722 CFStringRef ds = CFDateFormatterCreateStringWithDate(NULL, df,
723 date);
724 CFStringAppend(line, ds);
725 CFRelease(ds);
726 CFRelease(df);
727 CFRelease(lc);
728 } else if (CFURLGetTypeID() == CFGetTypeID(value)) {
729 CFURLRef url = (CFURLRef)value;
730 CFStringAppend(line, CFSTR("<"));
731 CFStringAppend(line, CFURLGetString(url));
732 CFStringAppend(line, CFSTR(">"));
733 } else if (CFDataGetTypeID() == CFGetTypeID(value)) {
734 CFDataRef v_d = (CFDataRef)value;
735 CFStringRef v_s = CFStringCreateFromExternalRepresentation(
736 kCFAllocatorDefault, v_d, kCFStringEncodingUTF8);
737 if (v_s) {
738 CFStringAppend(line, CFSTR("/"));
739 CFStringAppend(line, v_s);
740 CFStringAppend(line, CFSTR("/ "));
741 CFRelease(v_s);
742 }
743 const uint8_t *bytes = CFDataGetBytePtr(v_d);
744 CFIndex len = CFDataGetLength(v_d);
745 for (jx = 0; jx < len; ++jx) {
746 CFStringAppendFormat(line, NULL, CFSTR("%.02X"), bytes[jx]);
747 }
748 } else {
749 CFStringAppendFormat(line, NULL, CFSTR("%@"), value);
750 }
751 CFStringWriteToFileWithNewline(line, stdout);
752 CFRelease(line);
753 }
754 CFRelease(keys);
755 }
756 #endif
757
758 #pragma clang diagnostic pop
759
760 static void showPeerTrust(SecTrustRef peerTrust, bool verbose) {
761
762 if(peerTrust == NULL) {
763 return;
764 }
765 #if TARGET_OS_IPHONE
766 CFIndex numCerts;
767 CFIndex i;
768
769 printf("\n=============== Peer Trust Properties ===============\n");
770 CFArrayRef plist = SecTrustCopyProperties(peerTrust);
771 if (plist) {
772 print_plist(plist);
773 CFRelease(plist);
774 }
775
776 printf("\n================== Peer Trust Info ==================\n");
777 CFDictionaryRef info = SecTrustCopyInfo(peerTrust);
778 if (info && CFDictionaryGetCount(info)) {
779 showInfo(info);
780 }
781 if (info) {
782 CFRelease(info);
783 }
784
785 numCerts = SecTrustGetCertificateCount(peerTrust);
786 for(i=0; i<numCerts; i++) {
787 plist = SecTrustCopySummaryPropertiesAtIndex(peerTrust, i);
788 printf("\n============= Peer Trust Cert %lu Summary =============\n\n", i);
789 print_plist(plist);
790 if (plist) {
791 CFRelease(plist);
792 }
793 printf("\n============= Peer Trust Cert %lu Details =============\n\n", i);
794 plist = SecTrustCopyDetailedPropertiesAtIndex(peerTrust, i);
795 print_plist(plist);
796 if (plist) {
797 CFRelease(plist);
798 }
799 printf("\n============= End of Peer Trust Cert %lu ==============\n", i);
800 }
801 #endif
802 }
803
804 static void showPeerCerts(
805 CFArrayRef __unused peerCerts,
806 bool __unused verbose)
807 {
808 #if 0
809 CFIndex numCerts;
810 SecCertificateRef certRef;
811 CFIndex i;
812
813 if(peerCerts == NULL) {
814 return;
815 }
816 numCerts = CFArrayGetCount(peerCerts);
817 for(i=0; i<numCerts; i++) {
818 certRef = (SecCertificateRef)CFArrayGetValueAtIndex(peerCerts, i);
819 printf("\n==================== Peer Cert %lu ====================\n\n", i);
820 print_cert(certRef, verbose);
821 printf("\n================ End of Peer Cert %lu =================\n", i);
822 }
823 #endif
824 }
825
826 static void writePeerCerts(
827 CFArrayRef peerCerts,
828 const char *fileBase)
829 {
830 CFIndex numCerts;
831 SecCertificateRef certRef;
832 CFIndex i;
833 char fileName[_maxFileStringSize];
834
835 if(peerCerts == NULL) {
836 return;
837 }
838 numCerts = CFArrayGetCount(peerCerts);
839 for(i=0; i<numCerts; i++) {
840 snprintf(fileName, _maxFileStringSize, "%s%02d.cer", fileBase, (int)i);
841 certRef = (SecCertificateRef)CFArrayGetValueAtIndex(peerCerts, i);
842 CFDataRef derCert = SecCertificateCopyData(certRef);
843 if (derCert) {
844 writeFileSizet(fileName, CFDataGetBytePtr(derCert),
845 CFDataGetLength(derCert));
846 CFRelease(derCert);
847 }
848 }
849 printf("...wrote %lu certs to fileBase %s\n", numCerts, fileBase);
850 }
851
852 /*
853 * Show result of an sslPing().
854 * Assumes the following from sslPingArgs:
855 *
856 * verbose
857 * tryVersion
858 * acceptedProts
859 * negVersion
860 * negCipher
861 * peerCerts
862 * certState
863 * sessionWasResumed
864 * sessionID
865 * sessionIDLength
866 * handshakeTime
867 */
868 static void showSSLResult(
869 const sslPingArgs *pargs,
870 OSStatus err,
871 int displayPeerCerts,
872 char *fileBase) // non-NULL: write certs to file
873 {
874 CFIndex numPeerCerts;
875
876 printf("\n");
877
878 if(pargs->acceptedProts) {
879 printf(" Allowed SSL versions : %s\n", pargs->acceptedProts);
880 }
881 else {
882 printf(" Attempted SSL version : %s\n",
883 sslGetProtocolVersionString(pargs->tryVersion));
884 }
885
886 printf(" Result : %s\n", sslGetSSLErrString(err));
887 printf(" Negotiated SSL version : %s\n",
888 sslGetProtocolVersionString(pargs->negVersion));
889 printf(" Negotiated CipherSuite : %s\n",
890 sslGetCipherSuiteString(pargs->negCipher));
891 #pragma clang diagnostic push
892 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
893 if(pargs->certState != kSSLClientCertNone) {
894 printf(" Client Cert State : %s\n",
895 sslGetClientCertStateString(pargs->certState));
896 }
897 #pragma clang diagnostic pop
898 if(pargs->verbose) {
899 printf(" Resumed Session : ");
900 if(pargs->sessionWasResumed) {
901 for(unsigned dex=0; dex<pargs->sessionIDLength; dex++) {
902 printf("%02X ", pargs->sessionID[dex]);
903 if(((dex % 8) == 7) && (dex != (pargs->sessionIDLength - 1))) {
904 printf("\n ");
905 }
906 }
907 printf("\n");
908 }
909 else {
910 printf("NOT RESUMED\n");
911 }
912 printf(" Handshake time : %f seconds\n", pargs->handshakeTimeOp);
913 }
914 if(pargs->peerCerts == NULL) {
915 numPeerCerts = 0;
916 }
917 else {
918 numPeerCerts = CFArrayGetCount(pargs->peerCerts);
919 }
920 printf(" Number of server certs : %lu\n", numPeerCerts);
921 if(numPeerCerts != 0) {
922 if (displayPeerCerts == 1) {
923 showPeerCerts(pargs->peerCerts, false);
924 } else if (displayPeerCerts == 2) {
925 showPeerTrust(pargs->peerTrust, false);
926 }
927 if(fileBase != NULL) {
928 writePeerCerts(pargs->peerCerts, fileBase);
929 }
930 }
931
932 printf("\n");
933 }
934
935 static int verifyProtocol(
936 bool verifyProt,
937 SSLProtocol maxProtocol,
938 SSLProtocol reqProtocol,
939 SSLProtocol negProtocol)
940 {
941 if(!verifyProt) {
942 return 0;
943 }
944 if(reqProtocol > maxProtocol) {
945 /* known not to support this attempt, relax */
946 reqProtocol = maxProtocol;
947 }
948 if(reqProtocol != negProtocol) {
949 printf("***Expected protocol %s; negotiated %s\n",
950 sslGetProtocolVersionString(reqProtocol),
951 sslGetProtocolVersionString(negProtocol));
952 return 1;
953 }
954 else {
955 return 0;
956 }
957 }
958
959 static int verifyClientCertState(
960 bool verifyCertState,
961 SSLClientCertificateState expectState,
962 SSLClientCertificateState gotState)
963 {
964 if(!verifyCertState) {
965 return 0;
966 }
967 if(expectState == gotState) {
968 return 0;
969 }
970 printf("***Expected clientCertState %s; got %s\n",
971 sslGetClientCertStateString(expectState),
972 sslGetClientCertStateString(gotState));
973 return 1;
974 }
975
976 static SSLProtocol charToProt(
977 char c, // 2, 3, t
978 char **argv)
979 {
980 #pragma clang diagnostic push
981 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
982 switch(c) {
983 case '2':
984 return kSSLProtocol2;
985 case '3':
986 return kSSLProtocol3;
987 case 't':
988 return kTLSProtocol12;
989 default:
990 usage(argv);
991 }
992 #pragma clang diagnostic pop
993 }
994
995 int main(int argc, char **argv)
996 {
997 OSStatus err;
998 int arg;
999 char *argp;
1000 char getMsg[300];
1001 char fullFileBase[_maxFileStringSize];
1002 int ourRtn = 0; // exit status - sum of all errors
1003 unsigned loop;
1004 SecKeychainRef serverKc = nil;
1005 sslPingArgs pargs;
1006
1007 /* user-spec'd parameters */
1008 const char *getPath = DEFAULT_PATH;
1009 char *fileBase = NULL;
1010 int displayCerts = 0;
1011 bool doSslV2 = false;
1012 bool doSslV3 = false;
1013 bool doTlsV1 = true;
1014 bool doTlsV11 = false;
1015 bool doTlsV12 = false;
1016 bool protXOnly = false; // kSSLProtocol3Only, kTLSProtocol1Only
1017 bool doProtUnknown = false;
1018 unsigned loopCount = 1;
1019 bool doPause = false;
1020 bool pauseFirstLoop = false;
1021 bool verifyProt = false;
1022 char *acceptedProts = NULL;
1023 char *keyChainName = NULL;
1024 char *getMsgSpec = NULL;
1025 bool vfyCertState = false;
1026 bool displayHandshakeTimes = false;
1027 bool completeCertChain = false;
1028 #pragma clang diagnostic push
1029 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
1030 SSLClientCertificateState expectCertState = kSSLClientCertNone;
1031 SSLProtocol maxProtocol = kTLSProtocol12; // for verifying negotiated protocol
1032 #pragma clang diagnostic pop
1033
1034 /* special case - one arg of "h" or "-h" or "hv" */
1035 if(argc == 2) {
1036 if((strcmp(argv[1], "h") == 0) || (strcmp(argv[1], "-h") == 0)) {
1037 usage(argv);
1038 }
1039 if(strcmp(argv[1], "hv") == 0) {
1040 usageVerbose(argv);
1041 }
1042 }
1043
1044 /* set up defaults */
1045 memset(&pargs, 0, sizeof(sslPingArgs));
1046 pargs.hostName = DEFAULT_HOST;
1047 pargs.port = DEFAULT_PORT;
1048 pargs.resumableEnable = true;
1049 pargs.argv = argv;
1050
1051 for(arg=1; arg<argc; arg++) {
1052 argp = argv[arg];
1053 if(arg == 1) {
1054 /* first arg, is always hostname; '-' means default */
1055 if(argp[0] != '-') {
1056 pargs.hostName = argp;
1057 }
1058 continue;
1059 }
1060 if(argp[0] == '/') {
1061 /* path always starts with leading slash */
1062 getPath = argp;
1063 continue;
1064 }
1065 /* options */
1066 switch(argp[0]) {
1067 case 'Z': {
1068 if(++arg == argc) {
1069 /* requires another arg */
1070 usage(argv);
1071 }
1072 if (pargs.alpnNames == NULL) {
1073 pargs.alpnNames = CFArrayCreateMutableForCFTypes(NULL);
1074 }
1075
1076 CFDataRef alpn = CFDataCreate(NULL, (const UInt8 *)argv[arg], strlen(argv[arg]));
1077 CFArrayAppendValue(pargs.alpnNames, alpn);
1078 CFReleaseNull(alpn);
1079 break;
1080 }
1081 case 'W':
1082 case 'w': {
1083 CFDictionaryRef context = NULL;
1084
1085 if(++arg == argc) {
1086 /* requires another arg */
1087 usage(argv);
1088 }
1089
1090 if (argp[0] == 'W') {
1091 context = CFDictionaryCreateForCFTypes(NULL,
1092 CFSTR("AppleServerAuthenticationAllowUATAPN"), kCFBooleanTrue,
1093 CFSTR("AppleServerAuthenticationAllowUATIDS"), kCFBooleanTrue,
1094 CFSTR("AppleServerAuthenticationAllowUATGS"), kCFBooleanTrue,
1095 NULL);
1096 }
1097 const char *verifyName = pargs.hostName;
1098
1099 if (pargs.policies == NULL) {
1100 pargs.policies = CFArrayCreateMutableForCFTypes(NULL);
1101 }
1102
1103 if (pargs.vfyHostName)
1104 verifyName = pargs.vfyHostName;
1105
1106 SecPolicyRef policy = NULL;
1107 CFStringRef hostname = CFStringCreateWithCString(NULL, verifyName, kCFStringEncodingUTF8);
1108
1109 if (strcasecmp(argv[arg], "PushLegacy") == 0) {
1110 policy = SecPolicyCreateApplePushServiceLegacy(hostname);
1111 } else if (strcasecmp(argv[arg], "Push") == 0) {
1112 policy = SecPolicyCreateApplePushService(hostname, context);
1113 } else if (strcasecmp(argv[arg], "IDS") == 0) {
1114 policy = SecPolicyCreateAppleIDSServiceContext(hostname, context);
1115 } else if (strcasecmp(argv[arg], "GS") == 0) {
1116 policy = SecPolicyCreateAppleGSService(hostname, context);
1117 } else {
1118 printf("unknown policy: %s", argv[arg]);
1119 CFReleaseNull(hostname);
1120 CFReleaseNull(context);
1121 usage(argv);
1122 }
1123
1124 if (policy) {
1125 CFArrayAppendValue(pargs.policies, policy);
1126 }
1127
1128 CFReleaseNull(policy);
1129 CFReleaseNull(hostname);
1130 CFReleaseNull(context);
1131
1132 break;
1133 }
1134 case 'e':
1135 pargs.allowExpired = true;
1136 break;
1137 case 'E':
1138 pargs.allowExpiredRoot = true;
1139 break;
1140 case 'x':
1141 pargs.disableCertVerify = true;
1142 break;
1143 case 'M':
1144 pargs.disableCertVerify = true; // implied
1145 pargs.manualCertVerify = true;
1146 break;
1147 case 'a':
1148 if(++arg == argc) {
1149 /* requires another arg */
1150 usage(argv);
1151 }
1152 pargs.anchorFile = argv[arg];
1153 break;
1154 case 'A':
1155 if(++arg == argc) {
1156 /* requires another arg */
1157 usage(argv);
1158 }
1159 pargs.anchorFile = argv[arg];
1160 pargs.replaceAnchors = true;
1161 break;
1162 case 'r':
1163 pargs.allowAnyRoot = true;
1164 break;
1165 case 'd':
1166 pargs.dumpRxData = true;
1167 break;
1168 case 'c':
1169 displayCerts++;
1170 break;
1171 case 'f':
1172 if(++arg == argc) {
1173 /* requires another arg */
1174 usage(argv);
1175 }
1176 fileBase = argv[arg];
1177 break;
1178 case 'C':
1179 pargs.cipherRestrict = argp[2];
1180 break;
1181 case '2':
1182 doSslV3 = doTlsV1 = doTlsV11 = false;
1183 doSslV2 = true;
1184 break;
1185 case '3':
1186 doSslV2 = doTlsV1 = doTlsV11 = doTlsV12 = false;
1187 doSslV3 = true;
1188 break;
1189 case 't':
1190 doSslV2 = doSslV3 = doTlsV11 = doTlsV12 = false;
1191 doTlsV1 = true;
1192 break;
1193 case '%':
1194 doSslV2 = doSslV3 = doTlsV1 = false;
1195 doTlsV11 = true;
1196 break;
1197 case '^':
1198 doSslV2 = doSslV3 = doTlsV1 = false;
1199 doTlsV12 = true;
1200 break;
1201 case 'L':
1202 doSslV2 = doSslV3 = doTlsV1 = doTlsV11 = doTlsV12 = true;
1203 break;
1204 case 'o':
1205 protXOnly = true;
1206 break;
1207 case 'u':
1208 doSslV2 = doSslV3 = doTlsV1 = doTlsV11 = doTlsV12 = false;
1209 doProtUnknown = true;
1210 break;
1211 case 'K':
1212 pargs.keepConnected = true;
1213 break;
1214 case 'n':
1215 pargs.requireNotify = true;
1216 pargs.keepConnected = true;
1217 break;
1218 case 'R':
1219 pargs.resumableEnable = false;
1220 break;
1221 case 'b':
1222 pargs.nonBlocking = true;
1223 break;
1224 case 'v':
1225 verifyProt = true;
1226 break;
1227 case 'm':
1228 if(argp[1] != '=') {
1229 usage(argv);
1230 }
1231 verifyProt = true; // implied
1232 maxProtocol = charToProt(argp[2], argv);
1233 break;
1234 case 'g':
1235 if(argp[1] != '=') {
1236 usage(argv);
1237 }
1238 acceptedProts = argv[arg];
1239 doSslV3 = doSslV2 = doTlsV1 = doTlsV11 = doTlsV12 = false;
1240 break;
1241 case 'l':
1242 if(++arg == argc) {
1243 /* requires another arg */
1244 usage(argv);
1245 }
1246 int parsedLoopCount = atoi(argv[arg]);
1247 if (parsedLoopCount <= 0) {
1248 printf("***bad loopCount\n");
1249 usage(argv);
1250 }
1251 loopCount = (unsigned) parsedLoopCount;
1252 break;
1253 case 'P':
1254 if(++arg == argc) {
1255 /* requires another arg */
1256 usage(argv);
1257 }
1258 pargs.port = atoi(argv[arg]);
1259 break;
1260 case 'H':
1261 pargs.allowHostnameSpoof = true;
1262 break;
1263 case 'F':
1264 if(++arg == argc) {
1265 /* requires another arg */
1266 usage(argv);
1267 }
1268 pargs.vfyHostName = argv[arg];
1269 break;
1270 case 'k':
1271 if(++arg == argc) {
1272 /* requires another arg */
1273 usage(argv);
1274 }
1275 keyChainName = &argp[2];
1276 break;
1277 case 'G':
1278 getMsgSpec = &argp[2];
1279 break;
1280 case 'T':
1281 if(argp[1] != '=') {
1282 usage(argv);
1283 }
1284 vfyCertState = true;
1285 #pragma clang diagnostic push
1286 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
1287 switch(argp[2]) {
1288 case 'n':
1289 expectCertState = kSSLClientCertNone;
1290 break;
1291 case 'r':
1292 expectCertState = kSSLClientCertRequested;
1293 break;
1294 case 's':
1295 expectCertState = kSSLClientCertSent;
1296 break;
1297 case 'j':
1298 expectCertState = kSSLClientCertRejected;
1299 break;
1300 default:
1301 usage(argv);
1302 }
1303 #pragma clang diagnostic pop
1304 break;
1305 case 'z':
1306 pargs.password = &argp[2];
1307 break;
1308 case 'p':
1309 doPause = true;
1310 break;
1311 case '7':
1312 pauseFirstLoop = true;
1313 break;
1314 case 'q':
1315 pargs.quiet = true;
1316 break;
1317 case 'V':
1318 pargs.verbose = true;
1319 break;
1320 case 's':
1321 pargs.silent = pargs.quiet = true;
1322 break;
1323 case 'N':
1324 displayHandshakeTimes = true;
1325 break;
1326 case '8':
1327 completeCertChain = true;
1328 break;
1329 case 'h':
1330 if(pargs.verbose || (argp[1] == 'v')) {
1331 usageVerbose(argv);
1332 }
1333 else {
1334 usage(argv);
1335 }
1336 default:
1337 usage(argv);
1338 }
1339 }
1340 if(getMsgSpec) {
1341 pargs.getMsg = getMsgSpec;
1342 }
1343 else {
1344 sprintf(getMsg, "%s %s %s",
1345 DEFAULT_GETMSG, getPath, DEFAULT_GET_SUFFIX);
1346 pargs.getMsg = getMsg;
1347 }
1348
1349 /* get client cert and optional encryption cert as CFArrayRef */
1350 if(keyChainName) {
1351 pargs.clientCerts = getSslCerts(keyChainName, false, completeCertChain,
1352 pargs.anchorFile, &serverKc);
1353 if(pargs.clientCerts == nil) {
1354 exit(1);
1355 }
1356 #ifdef USE_CDSA_CRYPTO
1357 if(pargs.password) {
1358 OSStatus ortn = SecKeychainUnlock(serverKc,
1359 strlen(pargs.password), pargs.password, true);
1360 if(ortn) {
1361 printf("SecKeychainUnlock returned %d\n", (int)ortn);
1362 /* oh well */
1363 }
1364 }
1365 #endif
1366 }
1367
1368 {
1369 struct sigaction sa;
1370 memset(&sa, 0, sizeof(sa));
1371 sa.sa_flags = SA_RESTART;
1372 sa.sa_handler = sigpipe;
1373 sigaction(SIGPIPE, &sa, NULL);
1374 }
1375
1376 #pragma clang diagnostic push
1377 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
1378 for(loop=0; loop<loopCount; loop++) {
1379 /*
1380 * One pass for each protocol version, skipping any explicit version if
1381 * an attempt at a higher version and succeeded in doing so successfully fell
1382 * back.
1383 */
1384 if(doTlsV12) {
1385 pargs.tryVersion = kTLSProtocol12;
1386 pargs.acceptedProts = NULL;
1387 if(!pargs.silent) {
1388 printf("Connecting to host %s with TLS V1.2...", pargs.hostName);
1389 }
1390 fflush(stdout);
1391 err = sslPing(&pargs);
1392 if(err) {
1393 ourRtn++;
1394 }
1395 if(!pargs.quiet) {
1396 if(fileBase) {
1397 snprintf(fullFileBase, _maxFileStringSize, "%s_v3.1", fileBase);
1398 }
1399 showSSLResult(&pargs,
1400 err,
1401 displayCerts,
1402 fileBase ? fullFileBase : NULL);
1403 }
1404 CFReleaseNull(pargs.peerCerts);
1405 if(!err) {
1406 /* deal with fallbacks, skipping redundant tests */
1407 switch(pargs.negVersion) {
1408 case kTLSProtocol11:
1409 doTlsV11 =false;
1410 break;
1411 case kTLSProtocol1:
1412 doTlsV11 =false;
1413 doTlsV1 =false;
1414 break;
1415 case kSSLProtocol3:
1416 doTlsV11 =false;
1417 doTlsV1 =false;
1418 doSslV3 = false;
1419 break;
1420 case kSSLProtocol2:
1421 doTlsV11 =false;
1422 doTlsV1 =false;
1423 doSslV3 = false;
1424 doSslV2 = false;
1425 break;
1426 default:
1427 break;
1428 }
1429 ourRtn += verifyProtocol(verifyProt, maxProtocol, kTLSProtocol12,
1430 pargs.negVersion);
1431 }
1432 /* note we do this regardless since the client state might be
1433 * the cause of a failure */
1434 ourRtn += verifyClientCertState(vfyCertState, expectCertState,
1435 pargs.certState);
1436 }
1437 if(doTlsV11) {
1438 pargs.tryVersion = kTLSProtocol11;
1439 pargs.acceptedProts = NULL;
1440 if(!pargs.silent) {
1441 printf("Connecting to host %s with TLS V1.1...", pargs.hostName);
1442 }
1443 fflush(stdout);
1444 err = sslPing(&pargs);
1445 if(err) {
1446 ourRtn++;
1447 }
1448 if(!pargs.quiet) {
1449 if(fileBase) {
1450 snprintf(fullFileBase, _maxFileStringSize, "%s_v3.1", fileBase);
1451 }
1452 showSSLResult(&pargs,
1453 err,
1454 displayCerts,
1455 fileBase ? fullFileBase : NULL);
1456 }
1457 CFReleaseNull(pargs.peerCerts);
1458 if(!err) {
1459 /* deal with fallbacks, skipping redundant tests */
1460 switch(pargs.negVersion) {
1461 case kTLSProtocol1:
1462 doTlsV1 =false;
1463 break;
1464 case kSSLProtocol3:
1465 doTlsV1 =false;
1466 doSslV3 = false;
1467 break;
1468 case kSSLProtocol2:
1469 doTlsV1 =false;
1470 doSslV3 = false;
1471 doSslV2 = false;
1472 break;
1473 default:
1474 break;
1475 }
1476 ourRtn += verifyProtocol(verifyProt, maxProtocol, kTLSProtocol11,
1477 pargs.negVersion);
1478 }
1479 /* note we do this regardless since the client state might be
1480 * the cause of a failure */
1481 ourRtn += verifyClientCertState(vfyCertState, expectCertState,
1482 pargs.certState);
1483 }
1484 if(doTlsV1) {
1485 pargs.tryVersion =
1486 protXOnly ? kTLSProtocol1Only : kTLSProtocol1;
1487 pargs.acceptedProts = NULL;
1488 if(!pargs.silent) {
1489 printf("Connecting to host %s with TLS V1...", pargs.hostName);
1490 }
1491 fflush(stdout);
1492 err = sslPing(&pargs);
1493 if(err) {
1494 ourRtn++;
1495 }
1496 if(!pargs.quiet) {
1497 if(fileBase) {
1498 snprintf(fullFileBase, _maxFileStringSize, "%s_v3.1", fileBase);
1499 }
1500 showSSLResult(&pargs,
1501 err,
1502 displayCerts,
1503 fileBase ? fullFileBase : NULL);
1504 }
1505 CFReleaseNull(pargs.peerCerts);
1506 if(!err) {
1507 /* deal with fallbacks, skipping redundant tests */
1508 switch(pargs.negVersion) {
1509 case kSSLProtocol3:
1510 doSslV3 = false;
1511 break;
1512 case kSSLProtocol2:
1513 doSslV3 = false;
1514 doSslV2 = false;
1515 break;
1516 default:
1517 break;
1518 }
1519 ourRtn += verifyProtocol(verifyProt, maxProtocol, kTLSProtocol1,
1520 pargs.negVersion);
1521 }
1522 /* note we do this regardless since the client state might be
1523 * the cause of a failure */
1524 ourRtn += verifyClientCertState(vfyCertState, expectCertState,
1525 pargs.certState);
1526 }
1527 if(doSslV3) {
1528 pargs.tryVersion = protXOnly ? kSSLProtocol3Only : kSSLProtocol3;
1529 pargs.acceptedProts = NULL;
1530 if(!pargs.silent) {
1531 printf("Connecting to host %s with SSL V3...", pargs.hostName);
1532 }
1533 fflush(stdout);
1534 err = sslPing(&pargs);
1535 if(err) {
1536 ourRtn++;
1537 }
1538 if(!pargs.quiet) {
1539 if(fileBase) {
1540 snprintf(fullFileBase, _maxFileStringSize, "%s_v3.0", fileBase);
1541 }
1542 showSSLResult(&pargs,
1543 err,
1544 displayCerts,
1545 fileBase ? fullFileBase : NULL);
1546 }
1547 CFReleaseNull(pargs.peerCerts);
1548 if(!err) {
1549 /* deal with fallbacks, skipping redundant tests */
1550 switch(pargs.negVersion) {
1551 case kSSLProtocol2:
1552 doSslV2 = false;
1553 break;
1554 default:
1555 break;
1556 }
1557 ourRtn += verifyProtocol(verifyProt, maxProtocol, kSSLProtocol3,
1558 pargs.negVersion);
1559 }
1560 /* note we do this regardless since the client state might be
1561 * the cause of a failure */
1562 ourRtn += verifyClientCertState(vfyCertState, expectCertState,
1563 pargs.certState);
1564 }
1565
1566 if(doSslV2) {
1567 if(fileBase) {
1568 snprintf(fullFileBase, _maxFileStringSize, "%s_v2", fileBase);
1569 }
1570 if(!pargs.silent) {
1571 printf("Connecting to host %s with SSL V2...", pargs.hostName);
1572 }
1573 fflush(stdout);
1574 pargs.tryVersion = kSSLProtocol2;
1575 pargs.acceptedProts = NULL;
1576 err = sslPing(&pargs);
1577 if(err) {
1578 ourRtn++;
1579 }
1580 if(!pargs.quiet) {
1581 if(fileBase) {
1582 snprintf(fullFileBase, _maxFileStringSize, "%s_v2", fileBase);
1583 }
1584 showSSLResult(&pargs,
1585 err,
1586 displayCerts,
1587 fileBase ? fullFileBase : NULL);
1588 }
1589 CFReleaseNull(pargs.peerCerts);
1590 if(!err) {
1591 ourRtn += verifyProtocol(verifyProt, maxProtocol, kSSLProtocol2,
1592 pargs.negVersion);
1593 }
1594 /* note we do this regardless since the client state might be
1595 * the cause of a failure */
1596 ourRtn += verifyClientCertState(vfyCertState, expectCertState,
1597 pargs.certState);
1598 }
1599 if(doProtUnknown) {
1600 if(!pargs.silent) {
1601 printf("Connecting to host %s with kSSLProtocolUnknown...",
1602 pargs.hostName);
1603 }
1604 fflush(stdout);
1605 pargs.tryVersion = kSSLProtocolUnknown;
1606 pargs.acceptedProts = NULL;
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(acceptedProts != NULL) {
1623 pargs.acceptedProts = acceptedProts;
1624 pargs.tryVersion = kSSLProtocolUnknown; // not used
1625 if(!pargs.silent) {
1626 printf("Connecting to host %s with acceptedProts %s...",
1627 pargs.hostName, pargs.acceptedProts);
1628 }
1629 fflush(stdout);
1630 err = sslPing(&pargs);
1631 if(err) {
1632 ourRtn++;
1633 }
1634 if(!pargs.quiet) {
1635 if(fileBase) {
1636 snprintf(fullFileBase, _maxFileStringSize, "%s_def", fileBase);
1637 }
1638 showSSLResult(&pargs,
1639 err,
1640 displayCerts,
1641 fileBase ? fullFileBase : NULL);
1642 }
1643 CFReleaseNull(pargs.peerCerts);
1644 }
1645 if(doPause ||
1646 (pauseFirstLoop &&
1647 /* pause after first, before last to grab trace */
1648 ((loop == 0) || (loop == loopCount - 1))
1649 )
1650 ) {
1651 char resp;
1652 fpurge(stdin);
1653 printf("a to abort, c to continue: ");
1654 resp = (char) getchar();
1655 if(resp == 'a') {
1656 break;
1657 }
1658 }
1659 } /* main loop */
1660 #pragma clang diagnostic pop
1661
1662 if(displayHandshakeTimes) {
1663 CFAbsoluteTime totalTime;
1664 unsigned numHandshakes;
1665 if(pargs.numHandshakes == 1) {
1666 /* just display the first one */
1667 totalTime = pargs.handshakeTimeFirst;
1668 numHandshakes = 1;
1669 }
1670 else {
1671 /* skip the first one */
1672 totalTime = pargs.handshakeTimeTotal;
1673 numHandshakes = pargs.numHandshakes - 1;
1674 }
1675 if(numHandshakes != 0) {
1676 printf(" %u handshakes in %f seconds; %f seconds per handshake\n",
1677 numHandshakes, totalTime,
1678 (totalTime / numHandshakes));
1679 }
1680 }
1681
1682 if(ourRtn) {
1683 printf("===%s exiting with %d %s for host %s\n", argv[0], ourRtn,
1684 (ourRtn > 1) ? "errors" : "error", pargs.hostName);
1685 }
1686 return ourRtn;
1687
1688 }