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