]> git.saurik.com Git - apple/security.git/blob - sslViewer/SSLViewer.c
Security-59306.11.20.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 numCerts = SecTrustGetCertificateCount(peerTrust);
785 for(i=0; i<numCerts; i++) {
786 plist = SecTrustCopySummaryPropertiesAtIndex(peerTrust, i);
787 printf("\n============= Peer Trust Cert %lu Summary =============\n\n", i);
788 print_plist(plist);
789 if (plist)
790 CFRelease(plist);
791 printf("\n============= Peer Trust Cert %lu Details =============\n\n", i);
792 plist = SecTrustCopyDetailedPropertiesAtIndex(peerTrust, i);
793 print_plist(plist);
794 if (plist)
795 CFRelease(plist);
796 printf("\n============= End of Peer Trust Cert %lu ==============\n", i);
797 }
798 #endif
799 }
800
801 static void showPeerCerts(
802 CFArrayRef __unused peerCerts,
803 bool __unused verbose)
804 {
805 #if 0
806 CFIndex numCerts;
807 SecCertificateRef certRef;
808 CFIndex i;
809
810 if(peerCerts == NULL) {
811 return;
812 }
813 numCerts = CFArrayGetCount(peerCerts);
814 for(i=0; i<numCerts; i++) {
815 certRef = (SecCertificateRef)CFArrayGetValueAtIndex(peerCerts, i);
816 printf("\n==================== Peer Cert %lu ====================\n\n", i);
817 print_cert(certRef, verbose);
818 printf("\n================ End of Peer Cert %lu =================\n", i);
819 }
820 #endif
821 }
822
823 static void writePeerCerts(
824 CFArrayRef peerCerts,
825 const char *fileBase)
826 {
827 CFIndex numCerts;
828 SecCertificateRef certRef;
829 CFIndex i;
830 char fileName[_maxFileStringSize];
831
832 if(peerCerts == NULL) {
833 return;
834 }
835 numCerts = CFArrayGetCount(peerCerts);
836 for(i=0; i<numCerts; i++) {
837 snprintf(fileName, _maxFileStringSize, "%s%02d.cer", fileBase, (int)i);
838 certRef = (SecCertificateRef)CFArrayGetValueAtIndex(peerCerts, i);
839 CFDataRef derCert = SecCertificateCopyData(certRef);
840 if (derCert) {
841 writeFileSizet(fileName, CFDataGetBytePtr(derCert),
842 CFDataGetLength(derCert));
843 CFRelease(derCert);
844 }
845 }
846 printf("...wrote %lu certs to fileBase %s\n", numCerts, fileBase);
847 }
848
849 /*
850 * Show result of an sslPing().
851 * Assumes the following from sslPingArgs:
852 *
853 * verbose
854 * tryVersion
855 * acceptedProts
856 * negVersion
857 * negCipher
858 * peerCerts
859 * certState
860 * sessionWasResumed
861 * sessionID
862 * sessionIDLength
863 * handshakeTime
864 */
865 static void showSSLResult(
866 const sslPingArgs *pargs,
867 OSStatus err,
868 int displayPeerCerts,
869 char *fileBase) // non-NULL: write certs to file
870 {
871 CFIndex numPeerCerts;
872
873 printf("\n");
874
875 if(pargs->acceptedProts) {
876 printf(" Allowed SSL versions : %s\n", pargs->acceptedProts);
877 }
878 else {
879 printf(" Attempted SSL version : %s\n",
880 sslGetProtocolVersionString(pargs->tryVersion));
881 }
882
883 printf(" Result : %s\n", sslGetSSLErrString(err));
884 printf(" Negotiated SSL version : %s\n",
885 sslGetProtocolVersionString(pargs->negVersion));
886 printf(" Negotiated CipherSuite : %s\n",
887 sslGetCipherSuiteString(pargs->negCipher));
888 #pragma clang diagnostic push
889 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
890 if(pargs->certState != kSSLClientCertNone) {
891 printf(" Client Cert State : %s\n",
892 sslGetClientCertStateString(pargs->certState));
893 }
894 #pragma clang diagnostic pop
895 if(pargs->verbose) {
896 printf(" Resumed Session : ");
897 if(pargs->sessionWasResumed) {
898 for(unsigned dex=0; dex<pargs->sessionIDLength; dex++) {
899 printf("%02X ", pargs->sessionID[dex]);
900 if(((dex % 8) == 7) && (dex != (pargs->sessionIDLength - 1))) {
901 printf("\n ");
902 }
903 }
904 printf("\n");
905 }
906 else {
907 printf("NOT RESUMED\n");
908 }
909 printf(" Handshake time : %f seconds\n", pargs->handshakeTimeOp);
910 }
911 if(pargs->peerCerts == NULL) {
912 numPeerCerts = 0;
913 }
914 else {
915 numPeerCerts = CFArrayGetCount(pargs->peerCerts);
916 }
917 printf(" Number of server certs : %lu\n", numPeerCerts);
918 if(numPeerCerts != 0) {
919 if (displayPeerCerts == 1) {
920 showPeerCerts(pargs->peerCerts, false);
921 } else if (displayPeerCerts == 2) {
922 showPeerTrust(pargs->peerTrust, false);
923 }
924 if(fileBase != NULL) {
925 writePeerCerts(pargs->peerCerts, fileBase);
926 }
927 }
928
929 printf("\n");
930 }
931
932 static int verifyProtocol(
933 bool verifyProt,
934 SSLProtocol maxProtocol,
935 SSLProtocol reqProtocol,
936 SSLProtocol negProtocol)
937 {
938 if(!verifyProt) {
939 return 0;
940 }
941 if(reqProtocol > maxProtocol) {
942 /* known not to support this attempt, relax */
943 reqProtocol = maxProtocol;
944 }
945 if(reqProtocol != negProtocol) {
946 printf("***Expected protocol %s; negotiated %s\n",
947 sslGetProtocolVersionString(reqProtocol),
948 sslGetProtocolVersionString(negProtocol));
949 return 1;
950 }
951 else {
952 return 0;
953 }
954 }
955
956 static int verifyClientCertState(
957 bool verifyCertState,
958 SSLClientCertificateState expectState,
959 SSLClientCertificateState gotState)
960 {
961 if(!verifyCertState) {
962 return 0;
963 }
964 if(expectState == gotState) {
965 return 0;
966 }
967 printf("***Expected clientCertState %s; got %s\n",
968 sslGetClientCertStateString(expectState),
969 sslGetClientCertStateString(gotState));
970 return 1;
971 }
972
973 static SSLProtocol charToProt(
974 char c, // 2, 3, t
975 char **argv)
976 {
977 #pragma clang diagnostic push
978 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
979 switch(c) {
980 case '2':
981 return kSSLProtocol2;
982 case '3':
983 return kSSLProtocol3;
984 case 't':
985 return kTLSProtocol12;
986 default:
987 usage(argv);
988 }
989 #pragma clang diagnostic pop
990 }
991
992 int main(int argc, char **argv)
993 {
994 OSStatus err;
995 int arg;
996 char *argp;
997 char getMsg[300];
998 char fullFileBase[_maxFileStringSize];
999 int ourRtn = 0; // exit status - sum of all errors
1000 unsigned loop;
1001 SecKeychainRef serverKc = nil;
1002 sslPingArgs pargs;
1003
1004 /* user-spec'd parameters */
1005 const char *getPath = DEFAULT_PATH;
1006 char *fileBase = NULL;
1007 int displayCerts = 0;
1008 bool doSslV2 = false;
1009 bool doSslV3 = false;
1010 bool doTlsV1 = true;
1011 bool doTlsV11 = false;
1012 bool doTlsV12 = false;
1013 bool protXOnly = false; // kSSLProtocol3Only, kTLSProtocol1Only
1014 bool doProtUnknown = false;
1015 unsigned loopCount = 1;
1016 bool doPause = false;
1017 bool pauseFirstLoop = false;
1018 bool verifyProt = false;
1019 char *acceptedProts = NULL;
1020 char *keyChainName = NULL;
1021 char *getMsgSpec = NULL;
1022 bool vfyCertState = false;
1023 bool displayHandshakeTimes = false;
1024 bool completeCertChain = false;
1025 #pragma clang diagnostic push
1026 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
1027 SSLClientCertificateState expectCertState = kSSLClientCertNone;
1028 SSLProtocol maxProtocol = kTLSProtocol12; // for verifying negotiated protocol
1029 #pragma clang diagnostic pop
1030
1031 /* special case - one arg of "h" or "-h" or "hv" */
1032 if(argc == 2) {
1033 if((strcmp(argv[1], "h") == 0) || (strcmp(argv[1], "-h") == 0)) {
1034 usage(argv);
1035 }
1036 if(strcmp(argv[1], "hv") == 0) {
1037 usageVerbose(argv);
1038 }
1039 }
1040
1041 /* set up defaults */
1042 memset(&pargs, 0, sizeof(sslPingArgs));
1043 pargs.hostName = DEFAULT_HOST;
1044 pargs.port = DEFAULT_PORT;
1045 pargs.resumableEnable = true;
1046 pargs.argv = argv;
1047
1048 for(arg=1; arg<argc; arg++) {
1049 argp = argv[arg];
1050 if(arg == 1) {
1051 /* first arg, is always hostname; '-' means default */
1052 if(argp[0] != '-') {
1053 pargs.hostName = argp;
1054 }
1055 continue;
1056 }
1057 if(argp[0] == '/') {
1058 /* path always starts with leading slash */
1059 getPath = argp;
1060 continue;
1061 }
1062 /* options */
1063 switch(argp[0]) {
1064 case 'Z': {
1065 if(++arg == argc) {
1066 /* requires another arg */
1067 usage(argv);
1068 }
1069 if (pargs.alpnNames == NULL) {
1070 pargs.alpnNames = CFArrayCreateMutableForCFTypes(NULL);
1071 }
1072
1073 CFDataRef alpn = CFDataCreate(NULL, (const UInt8 *)argv[arg], strlen(argv[arg]));
1074 CFArrayAppendValue(pargs.alpnNames, alpn);
1075 CFReleaseNull(alpn);
1076 break;
1077 }
1078 case 'W':
1079 case 'w': {
1080 CFDictionaryRef context = NULL;
1081
1082 if(++arg == argc) {
1083 /* requires another arg */
1084 usage(argv);
1085 }
1086
1087 if (argp[0] == 'W') {
1088 context = CFDictionaryCreateForCFTypes(NULL,
1089 CFSTR("AppleServerAuthenticationAllowUATAPN"), kCFBooleanTrue,
1090 CFSTR("AppleServerAuthenticationAllowUATIDS"), kCFBooleanTrue,
1091 CFSTR("AppleServerAuthenticationAllowUATGS"), kCFBooleanTrue,
1092 NULL);
1093 }
1094 const char *verifyName = pargs.hostName;
1095
1096 if (pargs.policies == NULL) {
1097 pargs.policies = CFArrayCreateMutableForCFTypes(NULL);
1098 }
1099
1100 if (pargs.vfyHostName)
1101 verifyName = pargs.vfyHostName;
1102
1103 SecPolicyRef policy = NULL;
1104 CFStringRef hostname = CFStringCreateWithCString(NULL, verifyName, kCFStringEncodingUTF8);
1105
1106 if (strcasecmp(argv[arg], "PushLegacy") == 0) {
1107 policy = SecPolicyCreateApplePushServiceLegacy(hostname);
1108 } else if (strcasecmp(argv[arg], "Push") == 0) {
1109 policy = SecPolicyCreateApplePushService(hostname, context);
1110 } else if (strcasecmp(argv[arg], "IDS") == 0) {
1111 policy = SecPolicyCreateAppleIDSServiceContext(hostname, context);
1112 } else if (strcasecmp(argv[arg], "GS") == 0) {
1113 policy = SecPolicyCreateAppleGSService(hostname, context);
1114 } else {
1115 printf("unknown policy: %s", argv[arg]);
1116 CFReleaseNull(hostname);
1117 CFReleaseNull(context);
1118 usage(argv);
1119 }
1120
1121 if (policy) {
1122 CFArrayAppendValue(pargs.policies, policy);
1123 }
1124
1125 CFReleaseNull(policy);
1126 CFReleaseNull(hostname);
1127 CFReleaseNull(context);
1128
1129 break;
1130 }
1131 case 'e':
1132 pargs.allowExpired = true;
1133 break;
1134 case 'E':
1135 pargs.allowExpiredRoot = true;
1136 break;
1137 case 'x':
1138 pargs.disableCertVerify = true;
1139 break;
1140 case 'M':
1141 pargs.disableCertVerify = true; // implied
1142 pargs.manualCertVerify = true;
1143 break;
1144 case 'a':
1145 if(++arg == argc) {
1146 /* requires another arg */
1147 usage(argv);
1148 }
1149 pargs.anchorFile = argv[arg];
1150 break;
1151 case 'A':
1152 if(++arg == argc) {
1153 /* requires another arg */
1154 usage(argv);
1155 }
1156 pargs.anchorFile = argv[arg];
1157 pargs.replaceAnchors = true;
1158 break;
1159 case 'r':
1160 pargs.allowAnyRoot = true;
1161 break;
1162 case 'd':
1163 pargs.dumpRxData = true;
1164 break;
1165 case 'c':
1166 displayCerts++;
1167 break;
1168 case 'f':
1169 if(++arg == argc) {
1170 /* requires another arg */
1171 usage(argv);
1172 }
1173 fileBase = argv[arg];
1174 break;
1175 case 'C':
1176 pargs.cipherRestrict = argp[2];
1177 break;
1178 case '2':
1179 doSslV3 = doTlsV1 = doTlsV11 = false;
1180 doSslV2 = true;
1181 break;
1182 case '3':
1183 doSslV2 = doTlsV1 = doTlsV11 = doTlsV12 = false;
1184 doSslV3 = true;
1185 break;
1186 case 't':
1187 doSslV2 = doSslV3 = doTlsV11 = doTlsV12 = false;
1188 doTlsV1 = true;
1189 break;
1190 case '%':
1191 doSslV2 = doSslV3 = doTlsV1 = false;
1192 doTlsV11 = true;
1193 break;
1194 case '^':
1195 doSslV2 = doSslV3 = doTlsV1 = doTlsV12 = false;
1196 doTlsV12 = true;
1197 break;
1198 case 'L':
1199 doSslV2 = doSslV3 = doTlsV1 = doTlsV11 = doTlsV12 = true;
1200 break;
1201 case 'o':
1202 protXOnly = true;
1203 break;
1204 case 'u':
1205 doSslV2 = doSslV3 = doTlsV1 = doTlsV11 = doTlsV12 = false;
1206 doProtUnknown = true;
1207 break;
1208 case 'K':
1209 pargs.keepConnected = true;
1210 break;
1211 case 'n':
1212 pargs.requireNotify = true;
1213 pargs.keepConnected = true;
1214 break;
1215 case 'R':
1216 pargs.resumableEnable = false;
1217 break;
1218 case 'b':
1219 pargs.nonBlocking = true;
1220 break;
1221 case 'v':
1222 verifyProt = true;
1223 break;
1224 case 'm':
1225 if(argp[1] != '=') {
1226 usage(argv);
1227 }
1228 verifyProt = true; // implied
1229 maxProtocol = charToProt(argp[2], argv);
1230 break;
1231 case 'g':
1232 if(argp[1] != '=') {
1233 usage(argv);
1234 }
1235 acceptedProts = argv[arg];
1236 doSslV3 = doSslV2 = doTlsV1 = doTlsV11 = doTlsV12 = false;
1237 break;
1238 case 'l':
1239 if(++arg == argc) {
1240 /* requires another arg */
1241 usage(argv);
1242 }
1243 int parsedLoopCount = atoi(argv[arg]);
1244 if (parsedLoopCount <= 0) {
1245 printf("***bad loopCount\n");
1246 usage(argv);
1247 }
1248 loopCount = (unsigned) parsedLoopCount;
1249 break;
1250 case 'P':
1251 if(++arg == argc) {
1252 /* requires another arg */
1253 usage(argv);
1254 }
1255 pargs.port = atoi(argv[arg]);
1256 break;
1257 case 'H':
1258 pargs.allowHostnameSpoof = true;
1259 break;
1260 case 'F':
1261 if(++arg == argc) {
1262 /* requires another arg */
1263 usage(argv);
1264 }
1265 pargs.vfyHostName = argv[arg];
1266 break;
1267 case 'k':
1268 if(++arg == argc) {
1269 /* requires another arg */
1270 usage(argv);
1271 }
1272 keyChainName = &argp[2];
1273 break;
1274 case 'G':
1275 getMsgSpec = &argp[2];
1276 break;
1277 case 'T':
1278 if(argp[1] != '=') {
1279 usage(argv);
1280 }
1281 vfyCertState = true;
1282 #pragma clang diagnostic push
1283 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
1284 switch(argp[2]) {
1285 case 'n':
1286 expectCertState = kSSLClientCertNone;
1287 break;
1288 case 'r':
1289 expectCertState = kSSLClientCertRequested;
1290 break;
1291 case 's':
1292 expectCertState = kSSLClientCertSent;
1293 break;
1294 case 'j':
1295 expectCertState = kSSLClientCertRejected;
1296 break;
1297 default:
1298 usage(argv);
1299 }
1300 #pragma clang diagnostic pop
1301 break;
1302 case 'z':
1303 pargs.password = &argp[2];
1304 break;
1305 case 'p':
1306 doPause = true;
1307 break;
1308 case '7':
1309 pauseFirstLoop = true;
1310 break;
1311 case 'q':
1312 pargs.quiet = true;
1313 break;
1314 case 'V':
1315 pargs.verbose = true;
1316 break;
1317 case 's':
1318 pargs.silent = pargs.quiet = true;
1319 break;
1320 case 'N':
1321 displayHandshakeTimes = true;
1322 break;
1323 case '8':
1324 completeCertChain = true;
1325 break;
1326 case 'h':
1327 if(pargs.verbose || (argp[1] == 'v')) {
1328 usageVerbose(argv);
1329 }
1330 else {
1331 usage(argv);
1332 }
1333 default:
1334 usage(argv);
1335 }
1336 }
1337 if(getMsgSpec) {
1338 pargs.getMsg = getMsgSpec;
1339 }
1340 else {
1341 sprintf(getMsg, "%s %s %s",
1342 DEFAULT_GETMSG, getPath, DEFAULT_GET_SUFFIX);
1343 pargs.getMsg = getMsg;
1344 }
1345
1346 /* get client cert and optional encryption cert as CFArrayRef */
1347 if(keyChainName) {
1348 pargs.clientCerts = getSslCerts(keyChainName, false, completeCertChain,
1349 pargs.anchorFile, &serverKc);
1350 if(pargs.clientCerts == nil) {
1351 exit(1);
1352 }
1353 #ifdef USE_CDSA_CRYPTO
1354 if(pargs.password) {
1355 OSStatus ortn = SecKeychainUnlock(serverKc,
1356 strlen(pargs.password), pargs.password, true);
1357 if(ortn) {
1358 printf("SecKeychainUnlock returned %d\n", (int)ortn);
1359 /* oh well */
1360 }
1361 }
1362 #endif
1363 }
1364
1365 {
1366 struct sigaction sa;
1367 memset(&sa, 0, sizeof(sa));
1368 sa.sa_flags = SA_RESTART;
1369 sa.sa_handler = sigpipe;
1370 sigaction(SIGPIPE, &sa, NULL);
1371 }
1372
1373 #pragma clang diagnostic push
1374 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
1375 for(loop=0; loop<loopCount; loop++) {
1376 /*
1377 * One pass for each protocol version, skipping any explicit version if
1378 * an attempt at a higher version and succeeded in doing so successfully fell
1379 * back.
1380 */
1381 if(doTlsV12) {
1382 pargs.tryVersion = kTLSProtocol12;
1383 pargs.acceptedProts = NULL;
1384 if(!pargs.silent) {
1385 printf("Connecting to host %s with TLS V1.2...", pargs.hostName);
1386 }
1387 fflush(stdout);
1388 err = sslPing(&pargs);
1389 if(err) {
1390 ourRtn++;
1391 }
1392 if(!pargs.quiet) {
1393 if(fileBase) {
1394 snprintf(fullFileBase, _maxFileStringSize, "%s_v3.1", fileBase);
1395 }
1396 showSSLResult(&pargs,
1397 err,
1398 displayCerts,
1399 fileBase ? fullFileBase : NULL);
1400 }
1401 CFReleaseNull(pargs.peerCerts);
1402 if(!err) {
1403 /* deal with fallbacks, skipping redundant tests */
1404 switch(pargs.negVersion) {
1405 case kTLSProtocol11:
1406 doTlsV11 =false;
1407 break;
1408 case kTLSProtocol1:
1409 doTlsV11 =false;
1410 doTlsV1 =false;
1411 break;
1412 case kSSLProtocol3:
1413 doTlsV11 =false;
1414 doTlsV1 =false;
1415 doSslV3 = false;
1416 break;
1417 case kSSLProtocol2:
1418 doTlsV11 =false;
1419 doTlsV1 =false;
1420 doSslV3 = false;
1421 doSslV2 = false;
1422 break;
1423 default:
1424 break;
1425 }
1426 ourRtn += verifyProtocol(verifyProt, maxProtocol, kTLSProtocol12,
1427 pargs.negVersion);
1428 }
1429 /* note we do this regardless since the client state might be
1430 * the cause of a failure */
1431 ourRtn += verifyClientCertState(vfyCertState, expectCertState,
1432 pargs.certState);
1433 }
1434 if(doTlsV11) {
1435 pargs.tryVersion = kTLSProtocol11;
1436 pargs.acceptedProts = NULL;
1437 if(!pargs.silent) {
1438 printf("Connecting to host %s with TLS V1.1...", pargs.hostName);
1439 }
1440 fflush(stdout);
1441 err = sslPing(&pargs);
1442 if(err) {
1443 ourRtn++;
1444 }
1445 if(!pargs.quiet) {
1446 if(fileBase) {
1447 snprintf(fullFileBase, _maxFileStringSize, "%s_v3.1", fileBase);
1448 }
1449 showSSLResult(&pargs,
1450 err,
1451 displayCerts,
1452 fileBase ? fullFileBase : NULL);
1453 }
1454 CFReleaseNull(pargs.peerCerts);
1455 if(!err) {
1456 /* deal with fallbacks, skipping redundant tests */
1457 switch(pargs.negVersion) {
1458 case kTLSProtocol1:
1459 doTlsV1 =false;
1460 break;
1461 case kSSLProtocol3:
1462 doTlsV1 =false;
1463 doSslV3 = false;
1464 break;
1465 case kSSLProtocol2:
1466 doTlsV1 =false;
1467 doSslV3 = false;
1468 doSslV2 = false;
1469 break;
1470 default:
1471 break;
1472 }
1473 ourRtn += verifyProtocol(verifyProt, maxProtocol, kTLSProtocol11,
1474 pargs.negVersion);
1475 }
1476 /* note we do this regardless since the client state might be
1477 * the cause of a failure */
1478 ourRtn += verifyClientCertState(vfyCertState, expectCertState,
1479 pargs.certState);
1480 }
1481 if(doTlsV1) {
1482 pargs.tryVersion =
1483 protXOnly ? kTLSProtocol1Only : kTLSProtocol1;
1484 pargs.acceptedProts = NULL;
1485 if(!pargs.silent) {
1486 printf("Connecting to host %s with TLS V1...", pargs.hostName);
1487 }
1488 fflush(stdout);
1489 err = sslPing(&pargs);
1490 if(err) {
1491 ourRtn++;
1492 }
1493 if(!pargs.quiet) {
1494 if(fileBase) {
1495 snprintf(fullFileBase, _maxFileStringSize, "%s_v3.1", fileBase);
1496 }
1497 showSSLResult(&pargs,
1498 err,
1499 displayCerts,
1500 fileBase ? fullFileBase : NULL);
1501 }
1502 CFReleaseNull(pargs.peerCerts);
1503 if(!err) {
1504 /* deal with fallbacks, skipping redundant tests */
1505 switch(pargs.negVersion) {
1506 case kSSLProtocol3:
1507 doSslV3 = false;
1508 break;
1509 case kSSLProtocol2:
1510 doSslV3 = false;
1511 doSslV2 = false;
1512 break;
1513 default:
1514 break;
1515 }
1516 ourRtn += verifyProtocol(verifyProt, maxProtocol, kTLSProtocol1,
1517 pargs.negVersion);
1518 }
1519 /* note we do this regardless since the client state might be
1520 * the cause of a failure */
1521 ourRtn += verifyClientCertState(vfyCertState, expectCertState,
1522 pargs.certState);
1523 }
1524 if(doSslV3) {
1525 pargs.tryVersion = protXOnly ? kSSLProtocol3Only : kSSLProtocol3;
1526 pargs.acceptedProts = NULL;
1527 if(!pargs.silent) {
1528 printf("Connecting to host %s with SSL V3...", pargs.hostName);
1529 }
1530 fflush(stdout);
1531 err = sslPing(&pargs);
1532 if(err) {
1533 ourRtn++;
1534 }
1535 if(!pargs.quiet) {
1536 if(fileBase) {
1537 snprintf(fullFileBase, _maxFileStringSize, "%s_v3.0", fileBase);
1538 }
1539 showSSLResult(&pargs,
1540 err,
1541 displayCerts,
1542 fileBase ? fullFileBase : NULL);
1543 }
1544 CFReleaseNull(pargs.peerCerts);
1545 if(!err) {
1546 /* deal with fallbacks, skipping redundant tests */
1547 switch(pargs.negVersion) {
1548 case kSSLProtocol2:
1549 doSslV2 = false;
1550 break;
1551 default:
1552 break;
1553 }
1554 ourRtn += verifyProtocol(verifyProt, maxProtocol, kSSLProtocol3,
1555 pargs.negVersion);
1556 }
1557 /* note we do this regardless since the client state might be
1558 * the cause of a failure */
1559 ourRtn += verifyClientCertState(vfyCertState, expectCertState,
1560 pargs.certState);
1561 }
1562
1563 if(doSslV2) {
1564 if(fileBase) {
1565 snprintf(fullFileBase, _maxFileStringSize, "%s_v2", fileBase);
1566 }
1567 if(!pargs.silent) {
1568 printf("Connecting to host %s with SSL V2...", pargs.hostName);
1569 }
1570 fflush(stdout);
1571 pargs.tryVersion = kSSLProtocol2;
1572 pargs.acceptedProts = NULL;
1573 err = sslPing(&pargs);
1574 if(err) {
1575 ourRtn++;
1576 }
1577 if(!pargs.quiet) {
1578 if(fileBase) {
1579 snprintf(fullFileBase, _maxFileStringSize, "%s_v2", fileBase);
1580 }
1581 showSSLResult(&pargs,
1582 err,
1583 displayCerts,
1584 fileBase ? fullFileBase : NULL);
1585 }
1586 CFReleaseNull(pargs.peerCerts);
1587 if(!err) {
1588 ourRtn += verifyProtocol(verifyProt, maxProtocol, kSSLProtocol2,
1589 pargs.negVersion);
1590 }
1591 /* note we do this regardless since the client state might be
1592 * the cause of a failure */
1593 ourRtn += verifyClientCertState(vfyCertState, expectCertState,
1594 pargs.certState);
1595 }
1596 if(doProtUnknown) {
1597 if(!pargs.silent) {
1598 printf("Connecting to host %s with kSSLProtocolUnknown...",
1599 pargs.hostName);
1600 }
1601 fflush(stdout);
1602 pargs.tryVersion = kSSLProtocolUnknown;
1603 pargs.acceptedProts = NULL;
1604 err = sslPing(&pargs);
1605 if(err) {
1606 ourRtn++;
1607 }
1608 if(!pargs.quiet) {
1609 if(fileBase) {
1610 snprintf(fullFileBase, _maxFileStringSize, "%s_def", fileBase);
1611 }
1612 showSSLResult(&pargs,
1613 err,
1614 displayCerts,
1615 fileBase ? fullFileBase : NULL);
1616 }
1617 CFReleaseNull(pargs.peerCerts);
1618 }
1619 if(acceptedProts != NULL) {
1620 pargs.acceptedProts = acceptedProts;
1621 pargs.tryVersion = kSSLProtocolUnknown; // not used
1622 if(!pargs.silent) {
1623 printf("Connecting to host %s with acceptedProts %s...",
1624 pargs.hostName, pargs.acceptedProts);
1625 }
1626 fflush(stdout);
1627 err = sslPing(&pargs);
1628 if(err) {
1629 ourRtn++;
1630 }
1631 if(!pargs.quiet) {
1632 if(fileBase) {
1633 snprintf(fullFileBase, _maxFileStringSize, "%s_def", fileBase);
1634 }
1635 showSSLResult(&pargs,
1636 err,
1637 displayCerts,
1638 fileBase ? fullFileBase : NULL);
1639 }
1640 CFReleaseNull(pargs.peerCerts);
1641 }
1642 if(doPause ||
1643 (pauseFirstLoop &&
1644 /* pause after first, before last to grab trace */
1645 ((loop == 0) || (loop == loopCount - 1))
1646 )
1647 ) {
1648 char resp;
1649 fpurge(stdin);
1650 printf("a to abort, c to continue: ");
1651 resp = (char) getchar();
1652 if(resp == 'a') {
1653 break;
1654 }
1655 }
1656 } /* main loop */
1657 #pragma clang diagnostic pop
1658
1659 if(displayHandshakeTimes) {
1660 CFAbsoluteTime totalTime;
1661 unsigned numHandshakes;
1662 if(pargs.numHandshakes == 1) {
1663 /* just display the first one */
1664 totalTime = pargs.handshakeTimeFirst;
1665 numHandshakes = 1;
1666 }
1667 else {
1668 /* skip the first one */
1669 totalTime = pargs.handshakeTimeTotal;
1670 numHandshakes = pargs.numHandshakes - 1;
1671 }
1672 if(numHandshakes != 0) {
1673 printf(" %u handshakes in %f seconds; %f seconds per handshake\n",
1674 numHandshakes, totalTime,
1675 (totalTime / numHandshakes));
1676 }
1677 }
1678
1679 if(ourRtn) {
1680 printf("===%s exiting with %d %s for host %s\n", argv[0], ourRtn,
1681 (ourRtn > 1) ? "errors" : "error", pargs.hostName);
1682 }
1683 return ourRtn;
1684
1685 }