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