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