2 * Copyright (c) 2006-2013 Apple Inc. All Rights Reserved.
4 * SSL viewer tool, SecureTransport / Aspen version.
7 #include <Security/SecureTransport.h>
8 #include <Security/SecureTransportPriv.h> // for SSLGetPeerSecTrust
9 #include <Security/SecTrustPriv.h>
10 #include "sslAppUtils.h"
12 #include "utilities/fileIo.h"
13 #include "utilities/SecCFWrappers.h"
15 #include <Security/SecBase.h>
22 #include <CoreFoundation/CoreFoundation.h>
23 #include "SecurityTool/print_cert.h"
25 #include <utilities/SecIOFormat.h>
28 #include <securityd/spi.h>
31 #define DEFAULT_GETMSG "GET"
32 #define DEFAULT_PATH "/"
33 #define DEFAULT_GET_SUFFIX "HTTP/1.0\r\n\r\n"
35 #define DEFAULT_HOST "www.amazon.com"
36 #define DEFAULT_PORT 443
38 #include <Security/SecCertificate.h>
40 static void usageNorm(char **argv
)
42 printf("Usage: %s [hostname|-] [path] [option ...]\n", argv
[0]);
43 printf(" %s hostname [path] [option ...]\n", argv
[0]);
44 printf("Specifying '-' for hostname, or no args, uses default of %s.\n",
46 printf("Optional path argument must start with leading '/'.\n");
48 printf(" e Allow Expired Certs\n");
49 printf(" E Allow Expired Roots\n");
50 printf(" r Allow any root cert\n");
51 printf(" c Display peer certs\n");
52 printf(" c c Display peer SecTrust\n");
53 printf(" d Display received data\n");
54 printf(" 2 SSLv2 only (default is TLSv1)\n");
55 printf(" 3 SSLv3 only (default is TLSv1)\n");
57 printf(" %% TLSv1.1 only\n");
58 printf(" ^ TLSv1.2 only\n");
59 printf(" L all - TLSv1.2, TLSv1.1, TLSv1.0, SSLv3, SSLv2 (default = TLSv1.2)\n");
60 printf(" g={prot...} Specify legal protocols; prot = any combo of"
62 printf(" k=keychain Contains cert and keys. Optional.\n");
63 printf(" l=loopCount Perform loopCount ops (default = 1)\n");
64 printf(" P=port Default = %d\n", DEFAULT_PORT
);
65 printf(" p Pause after each loop\n");
66 printf(" q Quiet/diagnostic mode (site names and errors"
68 printf(" a fileName Add fileName to list of trusted roots\n");
69 printf(" A fileName fileName is ONLY trusted root\n");
70 printf(" x Disable Cert Verification\n");
71 printf(" z=password Unlock client keychain with password.\n");
72 printf(" 8 Complete cert chains (default is out cert is a root)\n");
73 printf(" s Silent\n");
74 printf(" V Verbose\n");
76 printf(" hv More, verbose help\n");
79 static void usageVerbose(char **argv
) __attribute__((noreturn
));
80 static void usageVerbose(char **argv
)
83 printf("Obscure Usage:\n");
84 printf(" u kSSLProtocolUnknown only (TLSv1)\n");
85 printf(" M Manual cert verification via "
86 "SecTrustEvaluate\n");
87 printf(" f fileBase Write Peer Certs to fileBase*\n");
88 printf(" o TLSv1, SSLv3 use kSSLProtocol__X__Only\n");
89 printf(" C=cipherSuite (e=40-bit d=DES D=40-bit DES 3=3DES 4=RC4 "
91 " 2=RC2 a=AES128 A=AES256 h=DH H=Anon DH r=DHE/RSA s=DH/DSS\n");
92 printf(" y=keychain Encryption-only cert and keys. Optional.\n");
93 printf(" K Keep connected until server disconnects\n");
94 printf(" n Require closure notify message in TLSv1, "
95 "SSLv3 mode (implies K)\n");
96 printf(" R Disable resumable session support\n");
97 printf(" b Non-blocking I/O\n");
98 printf(" v Verify negotiated protocol equals attempted\n");
99 printf(" m=[23t] Max protocol supported as specified; implies "
101 printf(" T=[nrsj] Verify client cert state = "
102 "none/requested/sent/rejected\n");
103 printf(" H allow hostname spoofing\n");
104 printf(" F=vfyHost Verify certs with specified host name\n");
105 printf(" G=getMsg Specify entire GET, POST, etc.\n");
106 printf(" N Log handshake timing\n");
107 printf(" 7 Pause only after first loop\n");
111 static void usage(char **argv
) __attribute__((noreturn
));
112 static void usage(char **argv
)
119 * Arguments to top-level sslPing()
122 SSLProtocol tryVersion
; // only used if acceptedProts NULL
123 // uses SSLSetProtocolVersion
124 char *acceptedProts
; // optional, any combo of {2,3,t}
125 // uses SSLSetProtocolVersionEnabled
126 const char *hostName
; // e.g., "www.amazon.com"
127 const char *vfyHostName
; // use this for cert vfy if non-NULL,
130 const char *getMsg
; // e.g.,
131 // "GET / HTTP/1.0\r\n\r\n"
134 bool allowExpiredRoot
;
135 bool disableCertVerify
;
136 bool manualCertVerify
;
137 bool dumpRxData
; // display server data
138 char cipherRestrict
; // '2', 'd'. etc...; '\0' for
141 bool requireNotify
; // require closure notify
143 bool resumableEnable
;
144 bool allowHostnameSpoof
;
148 CFArrayRef clientCerts
; // optional
149 CFArrayRef encryptClientCerts
; // optional
150 bool quiet
; // minimal stdout
151 bool silent
; // no stdout
153 SSLProtocol negVersion
; // RETURNED
154 SSLCipherSuite negCipher
; // RETURNED
155 CFArrayRef peerCerts
; // mallocd & RETURNED
156 SecTrustRef peerTrust
; // RETURNED
157 SSLClientCertificateState certState
; // RETURNED
158 char *password
; // optional to open clientCerts
160 Boolean sessionWasResumed
;
161 unsigned char sessionID
[MAX_SESSION_ID_LENGTH
];
162 size_t sessionIDLength
;
163 CFAbsoluteTime handshakeTimeOp
; // time for this op
164 CFAbsoluteTime handshakeTimeFirst
; // time for FIRST op, not averaged
165 CFAbsoluteTime handshakeTimeTotal
; // time for all ops except first
166 unsigned numHandshakes
;
171 static void sigpipe(int sig
)
174 printf("***SIGPIPE***\n");
178 * Snag a copy of current connection's peer certs so we can
179 * examine them later after the connection is closed.
180 * SecureTransport actually does the create and retain for us.
182 static OSStatus
copyPeerCerts(
184 CFArrayRef
*peerCerts
) // mallocd & RETURNED
186 OSStatus ortn
= SSLCopyPeerCertificates(ctx
, peerCerts
);
188 printf("***Error obtaining peer certs: %s\n",
189 sslGetSSLErrString(ortn
));
194 /* free the cert array obtained via SSLGetPeerCertificates() */
195 static void freePeerCerts(
196 CFArrayRef peerCerts
)
199 CFRelease(peerCerts
);
204 * Manually evaluate session's SecTrustRef.
206 #define SSL_SEC_TRUST 1
208 static OSStatus
sslEvaluateTrust(
212 CFArrayRef
*peerCerts
) // fetched and retained
214 OSStatus ortn
= errSecSuccess
;
216 SecTrustRef secTrust
= NULL
;
219 ortn
= SSLGetPeerSecTrust(ctx
, &secTrust
);
221 ortn
= errSecUnimplemented
;
224 printf("\n***Error obtaining peer SecTrustRef: %s\n",
225 sslGetSSLErrString(ortn
));
228 if(secTrust
== NULL
) {
229 /* this is the normal case for resumed sessions, in which
230 * no cert evaluation is performed */
232 printf("...No SecTrust available - this is a resumed session, right?\n");
234 return errSecSuccess
;
236 SecTrustResultType secTrustResult
;
237 ortn
= SecTrustEvaluate(secTrust
, &secTrustResult
);
239 printf("\n***Error on SecTrustEvaluate: %d\n", (int)ortn
);
244 switch(secTrustResult
) {
245 case kSecTrustResultInvalid
:
246 res
= "kSecTrustResultInvalid"; break;
247 case kSecTrustResultProceed
:
248 res
= "kSecTrustResultProceed"; break;
249 case kSecTrustResultConfirm
:
250 res
= "kSecTrustResultConfirm"; break;
251 case kSecTrustResultDeny
:
252 res
= "kSecTrustResultDeny"; break;
253 case kSecTrustResultUnspecified
:
254 res
= "kSecTrustResultUnspecified"; break;
255 case kSecTrustResultRecoverableTrustFailure
:
256 res
= "kSecTrustResultRecoverableTrustFailure"; break;
257 case kSecTrustResultFatalTrustFailure
:
258 res
= "kSecTrustResultFatalTrustFailure"; break;
259 case kSecTrustResultOtherError
:
260 res
= "kSecTrustResultOtherError"; break;
262 res
= "UNKNOWN"; break;
264 printf("\nSecTrustEvaluate(): secTrustResult %s\n", res
);
267 switch(secTrustResult
) {
268 case kSecTrustResultUnspecified
:
269 /* cert chain valid, no special UserTrust assignments */
270 case kSecTrustResultProceed
:
271 /* cert chain valid AND user explicitly trusts this */
274 printf("\n***SecTrustEvaluate reported secTrustResult %d\n",
275 (int)secTrustResult
);
276 ortn
= errSSLXCertChainInvalid
;
283 #ifdef USE_CDSA_CRYPTO
284 /* one more thing - get peer certs in the form of an evidence chain */
285 CSSM_TP_APPLE_EVIDENCE_INFO
*dummyEv
;
286 OSStatus thisRtn
= SecTrustGetResult(secTrust
, &secTrustResult
,
287 peerCerts
, &dummyEv
);
289 printSslErrStr("SecTrustGetResult", thisRtn
);
292 /* workaround for the fact that SSLGetPeerCertificates()
293 * leaves a retain count on each element in the returned array,
294 * requiring us to do a release on each cert.
296 CFIndex numCerts
= CFArrayGetCount(*peerCerts
);
297 for(CFIndex dex
=0; dex
<numCerts
; dex
++) {
298 CFRetain(CFArrayGetValueAtIndex(*peerCerts
, dex
));
305 /* print reply received from server, safely */
306 static void dumpAscii(
310 char *cp
= (char *)rcvBuf
;
314 for(i
=0; i
<len
; i
++) {
327 if(isprint(c
) && (c
!= '\n')) {
331 printf("<%02X>", ((unsigned)c
) & 0xff);
341 * Perform one SSL diagnostic session. Returns nonzero on error. Normally no
342 * output to stdout except initial "connecting to" message, unless there
343 * is a really screwed up error (i.e., something not directly related
344 * to the SSL connection).
346 #define RCV_BUF_SIZE 256
348 static OSStatus
sslPing(
354 SSLContextRef ctx
= NULL
;
357 uint8_t rcvBuf
[RCV_BUF_SIZE
];
358 CFAbsoluteTime startHandshake
;
359 CFAbsoluteTime endHandshake
;
361 pargs
->negVersion
= kSSLProtocolUnknown
;
362 pargs
->negCipher
= SSL_NULL_WITH_NULL_NULL
;
363 pargs
->peerCerts
= NULL
;
365 /* first make sure requested server is there */
366 ortn
= MakeServerConnection(pargs
->hostName
, pargs
->port
, pargs
->nonBlocking
,
369 printf("MakeServerConnection returned %d; aborting\n", (int)ortn
);
373 printf("...connected to server; starting SecureTransport\n");
377 * Set up a SecureTransport session.
378 * First the standard calls.
380 ortn
= SSLNewContext(false, &ctx
);
382 printSslErrStr("SSLNewContext", ortn
);
385 ortn
= SSLSetIOFuncs(ctx
, SocketRead
, SocketWrite
);
387 printSslErrStr("SSLSetIOFuncs", ortn
);
390 ortn
= SSLSetConnection(ctx
, (SSLConnectionRef
)(intptr_t)sock
);
392 printSslErrStr("SSLSetConnection", ortn
);
395 SSLConnectionRef getConn
;
396 ortn
= SSLGetConnection(ctx
, &getConn
);
398 printSslErrStr("SSLGetConnection", ortn
);
401 if(getConn
!= (SSLConnectionRef
)(intptr_t)sock
) {
402 printf("***SSLGetConnection error\n");
406 if(!pargs
->allowHostnameSpoof
) {
407 /* if this isn't set, it isn't checked by AppleX509TP */
408 const char *vfyHost
= pargs
->hostName
;
409 if(pargs
->vfyHostName
) {
410 /* generally means we're expecting an error */
411 vfyHost
= pargs
->vfyHostName
;
413 ortn
= SSLSetPeerDomainName(ctx
, vfyHost
, strlen(vfyHost
));
415 printSslErrStr("SSLSetPeerDomainName", ortn
);
421 * SecureTransport options.
423 if(pargs
->acceptedProts
) {
424 ortn
= SSLSetProtocolVersionEnabled(ctx
, kSSLProtocolAll
, false);
426 printSslErrStr("SSLSetProtocolVersionEnabled(all off)", ortn
);
429 for(const char *cp
= pargs
->acceptedProts
; *cp
; cp
++) {
433 prot
= kSSLProtocol2
;
436 prot
= kSSLProtocol3
;
439 prot
= kTLSProtocol12
;
444 ortn
= SSLSetProtocolVersionEnabled(ctx
, prot
, true);
446 printSslErrStr("SSLSetProtocolVersionEnabled", ortn
);
452 ortn
= SSLSetProtocolVersion(ctx
, pargs
->tryVersion
);
454 printSslErrStr("SSLSetProtocolVersion", ortn
);
458 ortn
= SSLGetProtocolVersion(ctx
, &getVers
);
460 printSslErrStr("SSLGetProtocolVersion", ortn
);
463 if(getVers
!= pargs
->tryVersion
) {
464 printf("***SSLGetProtocolVersion screwup: try %s get %s\n",
465 sslGetProtocolVersionString(pargs
->tryVersion
),
466 sslGetProtocolVersionString(getVers
));
471 if(pargs
->resumableEnable
) {
472 const void *rtnId
= NULL
;
475 ortn
= SSLSetPeerID(ctx
, &peerId
, sizeof(PeerSpec
));
477 printSslErrStr("SSLSetPeerID", ortn
);
480 /* quick test of the get fcn */
481 ortn
= SSLGetPeerID(ctx
, &rtnId
, &rtnIdLen
);
483 printSslErrStr("SSLGetPeerID", ortn
);
486 if((rtnId
== NULL
) || (rtnIdLen
!= sizeof(PeerSpec
))) {
487 printf("***SSLGetPeerID screwup\n");
489 else if(memcmp(&peerId
, rtnId
, rtnIdLen
) != 0) {
490 printf("***SSLGetPeerID data mismatch\n");
493 if(pargs
->allowExpired
) {
494 ortn
= SSLSetAllowsExpiredCerts(ctx
, true);
496 printSslErrStr("SSLSetAllowExpiredCerts", ortn
);
500 if(pargs
->allowExpiredRoot
) {
501 ortn
= SSLSetAllowsExpiredRoots(ctx
, true);
503 printSslErrStr("SSLSetAllowsExpiredRoots", ortn
);
507 if(pargs
->disableCertVerify
) {
508 ortn
= SSLSetEnableCertVerify(ctx
, false);
510 printSslErrStr("SSLSetEnableCertVerify", ortn
);
514 if(pargs
->allowAnyRoot
) {
515 ortn
= SSLSetAllowsAnyRoot(ctx
, true);
517 printSslErrStr("SSLSetAllowAnyRoot", ortn
);
521 if(pargs
->cipherRestrict
!= '\0') {
522 ortn
= sslSetCipherRestrictions(ctx
, pargs
->cipherRestrict
);
527 if(pargs
->anchorFile
) {
528 ortn
= sslAddTrustedRoot(ctx
, pargs
->anchorFile
, pargs
->replaceAnchors
);
530 printf("***Error obtaining anchor file %s\n", pargs
->anchorFile
);
534 if(pargs
->clientCerts
) {
536 if(pargs
->anchorFile
== NULL
) {
537 /* assume this is a root we want to implicitly trust */
538 ortn
= addIdentityAsTrustedRoot(ctx
, pargs
->clientCerts
);
543 ortn
= SSLSetCertificate(ctx
, pargs
->clientCerts
);
545 printSslErrStr("SSLSetCertificate", ortn
);
548 /* quickie test for this new function */
549 ortn
= SSLGetCertificate(ctx
, &dummy
);
551 printSslErrStr("SSLGetCertificate", ortn
);
554 if(dummy
!= pargs
->clientCerts
) {
555 printf("***SSLGetCertificate error\n");
560 if(pargs
->encryptClientCerts
) {
561 if(pargs
->anchorFile
== NULL
) {
562 ortn
= addIdentityAsTrustedRoot(ctx
, pargs
->encryptClientCerts
);
567 ortn
= SSLSetEncryptionCertificate(ctx
, pargs
->encryptClientCerts
);
569 printSslErrStr("SSLSetEncryptionCertificate", ortn
);
574 /*** end options ***/
577 printf("...starting SSL handshake\n");
579 startHandshake
= CFAbsoluteTimeGetCurrent();
582 { ortn
= SSLHandshake(ctx
);
583 if((ortn
== errSSLWouldBlock
) && !pargs
->silent
) {
584 /* keep UI responsive */
587 } while (ortn
== errSSLWouldBlock
);
589 endHandshake
= CFAbsoluteTimeGetCurrent();
590 pargs
->handshakeTimeOp
= endHandshake
- startHandshake
;
591 if(pargs
->numHandshakes
== 0) {
592 /* special case, this one is always way longer */
593 pargs
->handshakeTimeFirst
= pargs
->handshakeTimeOp
;
596 /* normal running total */
597 pargs
->handshakeTimeTotal
+= pargs
->handshakeTimeOp
;
599 pargs
->numHandshakes
++;
601 /* this works even if handshake failed due to cert chain invalid */
602 if(!pargs
->manualCertVerify
) {
603 copyPeerCerts(ctx
, &pargs
->peerCerts
);
606 /* else fetched via SecTrust later */
607 pargs
->peerCerts
= NULL
;
610 ortn
= SSLCopyPeerTrust(ctx
, &pargs
->peerTrust
);
612 printf("***SSLCopyPeerTrust error %" PRIdOSStatus
"\n", ortn
);
613 pargs
->peerTrust
= NULL
;
617 SSLGetClientCertificateState(ctx
, &pargs
->certState
);
618 SSLGetNegotiatedCipher(ctx
, &pargs
->negCipher
);
619 SSLGetNegotiatedProtocolVersion(ctx
, &pargs
->negVersion
);
620 pargs
->sessionIDLength
= MAX_SESSION_ID_LENGTH
;
621 SSLGetResumableSessionInfo(ctx
, &pargs
->sessionWasResumed
, pargs
->sessionID
,
622 &pargs
->sessionIDLength
);
623 if(pargs
->manualCertVerify
) {
624 OSStatus certRtn
= sslEvaluateTrust(ctx
, pargs
->verbose
, pargs
->silent
,
626 if(certRtn
&& !ortn
) {
639 printf("...SSL handshake complete\n");
641 length
= strlen(pargs
->getMsg
);
642 (void) SSLWrite(ctx
, pargs
->getMsg
, length
, &actLen
);
645 * Try to snag RCV_BUF_SIZE bytes. Exit if (!keepConnected and we get any data
646 * at all), or (keepConnected and err != (none, wouldBlock)).
650 if(pargs
->dumpRxData
) {
653 ortn
= SSLGetBufferedReadSize(ctx
, &avail
);
655 printf("***SSLGetBufferedReadSize error\n");
659 printf("\n%d bytes available: ", (int)avail
);
662 ortn
= SSLRead(ctx
, rcvBuf
, RCV_BUF_SIZE
, &actLen
);
663 if((actLen
== 0) && !pargs
->silent
) {
666 if((actLen
== 0) && (ortn
== errSecSuccess
)) {
667 printf("***Radar 2984932 confirmed***\n");
669 if (ortn
== errSSLWouldBlock
) {
670 /* for this loop, these are identical */
671 ortn
= errSecSuccess
;
673 if((actLen
> 0) && pargs
->dumpRxData
) {
674 dumpAscii(rcvBuf
, actLen
);
676 if(ortn
!= errSecSuccess
) {
677 /* connection closed by server or by error */
680 if(!pargs
->keepConnected
&& (actLen
> 0)) {
681 /* good enough, we connected */
689 /* snag these again in case of renegotiate */
690 SSLGetClientCertificateState(ctx
, &pargs
->certState
);
691 SSLGetNegotiatedCipher(ctx
, &pargs
->negCipher
);
692 SSLGetNegotiatedProtocolVersion(ctx
, &pargs
->negVersion
);
694 /* convert normal "shutdown" into zero err rtn */
695 if(ortn
== errSSLClosedGraceful
) {
696 ortn
= errSecSuccess
;
698 if((ortn
== errSSLClosedNoNotify
) && !pargs
->requireNotify
) {
699 /* relaxed disconnect rules */
700 ortn
= errSecSuccess
;
704 * always do close, even on error - to flush outgoing write queue
706 OSStatus cerr
= SSLClose(ctx
);
707 if(ortn
== errSecSuccess
) {
711 endpointShutdown(sock
);
714 SSLDisposeContext(ctx
);
719 static void add_key(const void *key
, const void *value
, void *context
) {
720 CFArrayAppendValue((CFMutableArrayRef
)context
, key
);
723 static void showInfo(CFDictionaryRef info
) {
724 CFIndex dict_count
, key_ix
, key_count
;
725 CFMutableArrayRef keys
= NULL
;
726 CFIndex maxWidth
= 20; /* Maybe precompute this or grab from context? */
728 dict_count
= CFDictionaryGetCount(info
);
729 keys
= CFArrayCreateMutable(kCFAllocatorDefault
, dict_count
,
730 &kCFTypeArrayCallBacks
);
731 CFDictionaryApplyFunction(info
, add_key
, keys
);
732 key_count
= CFArrayGetCount(keys
);
733 CFArraySortValues(keys
, CFRangeMake(0, key_count
),
734 (CFComparatorFunction
)CFStringCompare
, 0);
736 for (key_ix
= 0; key_ix
< key_count
; ++key_ix
) {
737 CFStringRef key
= (CFStringRef
)CFArrayGetValueAtIndex(keys
, key_ix
);
738 CFTypeRef value
= CFDictionaryGetValue(info
, key
);
739 CFMutableStringRef line
= CFStringCreateMutable(NULL
, 0);
741 CFStringAppend(line
, key
);
743 for (jx
= CFStringGetLength(key
);
744 jx
< maxWidth
; ++jx
) {
745 CFStringAppend(line
, CFSTR(" "));
747 CFStringAppend(line
, CFSTR(" : "));
748 if (CFStringGetTypeID() == CFGetTypeID(value
)) {
749 CFStringAppend(line
, (CFStringRef
)value
);
750 } else if (CFDateGetTypeID() == CFGetTypeID(value
)) {
751 CFLocaleRef lc
= CFLocaleCopyCurrent();
752 CFDateFormatterRef df
= CFDateFormatterCreate(NULL
, lc
,
753 kCFDateFormatterFullStyle
, kCFDateFormatterFullStyle
);
754 CFDateRef date
= (CFDateRef
)value
;
755 CFStringRef ds
= CFDateFormatterCreateStringWithDate(NULL
, df
,
757 CFStringAppend(line
, ds
);
761 } else if (CFURLGetTypeID() == CFGetTypeID(value
)) {
762 CFURLRef url
= (CFURLRef
)value
;
763 CFStringAppend(line
, CFSTR("<"));
764 CFStringAppend(line
, CFURLGetString(url
));
765 CFStringAppend(line
, CFSTR(">"));
766 } else if (CFDataGetTypeID() == CFGetTypeID(value
)) {
767 CFDataRef v_d
= (CFDataRef
)value
;
768 CFStringRef v_s
= CFStringCreateFromExternalRepresentation(
769 kCFAllocatorDefault
, v_d
, kCFStringEncodingUTF8
);
771 CFStringAppend(line
, CFSTR("/"));
772 CFStringAppend(line
, v_s
);
773 CFStringAppend(line
, CFSTR("/ "));
776 const uint8_t *bytes
= CFDataGetBytePtr(v_d
);
777 CFIndex len
= CFDataGetLength(v_d
);
778 for (jx
= 0; jx
< len
; ++jx
) {
779 CFStringAppendFormat(line
, NULL
, CFSTR("%.02X"), bytes
[jx
]);
782 CFStringAppendFormat(line
, NULL
, CFSTR("%@"), value
);
784 CFStringWriteToFileWithNewline(line
, stdout
);
790 static void showPeerTrust(SecTrustRef peerTrust
, bool verbose
) {
794 if(peerTrust
== NULL
) {
798 printf("\n=============== Peer Trust Properties ===============\n");
799 CFArrayRef plist
= SecTrustCopyProperties(peerTrust
);
805 printf("\n================== Peer Trust Info ==================\n");
806 CFDictionaryRef info
= SecTrustCopyInfo(peerTrust
);
807 if (info
&& CFDictionaryGetCount(info
)) {
813 numCerts
= SecTrustGetCertificateCount(peerTrust
);
814 for(i
=0; i
<numCerts
; i
++) {
815 plist
= SecTrustCopySummaryPropertiesAtIndex(peerTrust
, i
);
816 printf("\n============= Peer Trust Cert %lu Summary =============\n\n", i
);
820 printf("\n============= Peer Trust Cert %lu Details =============\n\n", i
);
821 plist
= SecTrustCopyDetailedPropertiesAtIndex(peerTrust
, i
);
825 printf("\n============= End of Peer Trust Cert %lu ==============\n", i
);
829 static void showPeerCerts(
830 CFArrayRef peerCerts
,
834 SecCertificateRef certRef
;
837 if(peerCerts
== NULL
) {
840 numCerts
= CFArrayGetCount(peerCerts
);
841 for(i
=0; i
<numCerts
; i
++) {
842 certRef
= (SecCertificateRef
)CFArrayGetValueAtIndex(peerCerts
, i
);
843 printf("\n==================== Peer Cert %lu ====================\n\n", i
);
844 print_cert(certRef
, verbose
);
845 printf("\n================ End of Peer Cert %lu =================\n", i
);
849 static void writePeerCerts(
850 CFArrayRef peerCerts
,
851 const char *fileBase
)
854 SecCertificateRef certRef
;
858 if(peerCerts
== NULL
) {
861 numCerts
= CFArrayGetCount(peerCerts
);
862 for(i
=0; i
<numCerts
; i
++) {
863 sprintf(fileName
, "%s%02d.cer", fileBase
, (int)i
);
864 certRef
= (SecCertificateRef
)CFArrayGetValueAtIndex(peerCerts
, i
);
865 CFDataRef derCert
= SecCertificateCopyData(certRef
);
867 writeFile(fileName
, CFDataGetBytePtr(derCert
),
868 CFDataGetLength(derCert
));
872 printf("...wrote %lu certs to fileBase %s\n", numCerts
, fileBase
);
876 * Show result of an sslPing().
877 * Assumes the following from sslPingArgs:
891 static void showSSLResult(
892 const sslPingArgs
&pargs
,
894 int displayPeerCerts
,
895 char *fileBase
) // non-NULL: write certs to file
897 CFIndex numPeerCerts
;
901 if(pargs
.acceptedProts
) {
902 printf(" Allowed SSL versions : %s\n", pargs
.acceptedProts
);
905 printf(" Attempted SSL version : %s\n",
906 sslGetProtocolVersionString(pargs
.tryVersion
));
909 printf(" Result : %s\n", sslGetSSLErrString(err
));
910 printf(" Negotiated SSL version : %s\n",
911 sslGetProtocolVersionString(pargs
.negVersion
));
912 printf(" Negotiated CipherSuite : %s\n",
913 sslGetCipherSuiteString(pargs
.negCipher
));
914 if(pargs
.certState
!= kSSLClientCertNone
) {
915 printf(" Client Cert State : %s\n",
916 sslGetClientCertStateString(pargs
.certState
));
919 printf(" Resumed Session : ");
920 if(pargs
.sessionWasResumed
) {
921 for(unsigned dex
=0; dex
<pargs
.sessionIDLength
; dex
++) {
922 printf("%02X ", pargs
.sessionID
[dex
]);
923 if(((dex
% 8) == 7) && (dex
!= (pargs
.sessionIDLength
- 1))) {
930 printf("NOT RESUMED\n");
932 printf(" Handshake time : %f seconds\n", pargs
.handshakeTimeOp
);
934 if(pargs
.peerCerts
== NULL
) {
938 numPeerCerts
= CFArrayGetCount(pargs
.peerCerts
);
940 printf(" Number of server certs : %lu\n", numPeerCerts
);
941 if(numPeerCerts
!= 0) {
942 if (displayPeerCerts
== 1) {
943 showPeerCerts(pargs
.peerCerts
, false);
944 } else if (displayPeerCerts
== 2) {
945 showPeerTrust(pargs
.peerTrust
, false);
947 if(fileBase
!= NULL
) {
948 writePeerCerts(pargs
.peerCerts
, fileBase
);
955 static int verifyProtocol(
957 SSLProtocol maxProtocol
,
958 SSLProtocol reqProtocol
,
959 SSLProtocol negProtocol
)
964 if(reqProtocol
> maxProtocol
) {
965 /* known not to support this attempt, relax */
966 reqProtocol
= maxProtocol
;
968 if(reqProtocol
!= negProtocol
) {
969 printf("***Expected protocol %s; negotiated %s\n",
970 sslGetProtocolVersionString(reqProtocol
),
971 sslGetProtocolVersionString(negProtocol
));
979 static int verifyClientCertState(
980 bool verifyCertState
,
981 SSLClientCertificateState expectState
,
982 SSLClientCertificateState gotState
)
984 if(!verifyCertState
) {
987 if(expectState
== gotState
) {
990 printf("***Expected clientCertState %s; got %s\n",
991 sslGetClientCertStateString(expectState
),
992 sslGetClientCertStateString(gotState
));
996 static SSLProtocol
charToProt(
1002 return kSSLProtocol2
;
1004 return kSSLProtocol3
;
1006 return kTLSProtocol12
;
1011 return kSSLProtocolUnknown
;
1014 int main(int argc
, char **argv
)
1020 char fullFileBase
[100];
1021 int ourRtn
= 0; // exit status - sum of all errors
1023 SecKeychainRef serverKc
= nil
;
1024 SecKeychainRef encryptKc
= nil
;
1027 /* user-spec'd parameters */
1028 const char *getPath
= DEFAULT_PATH
;
1029 char *fileBase
= NULL
;
1030 int displayCerts
= 0;
1031 bool doSslV2
= false;
1032 bool doSslV3
= false;
1033 bool doTlsV1
= true;
1034 bool doTlsV11
= false;
1035 bool doTlsV12
= false;
1036 bool protXOnly
= false; // kSSLProtocol3Only, kTLSProtocol1Only
1037 bool doProtUnknown
= false;
1038 unsigned loopCount
= 1;
1039 bool doPause
= false;
1040 bool pauseFirstLoop
= false;
1041 bool verifyProt
= false;
1042 SSLProtocol maxProtocol
= kTLSProtocol12
; // for verifying negotiated
1044 char *acceptedProts
= NULL
;
1045 char *keyChainName
= NULL
;
1046 char *encryptKeyChainName
= NULL
;
1047 char *getMsgSpec
= NULL
;
1048 bool vfyCertState
= false;
1049 SSLClientCertificateState expectCertState
= kSSLClientCertNone
;
1050 bool displayHandshakeTimes
= false;
1051 bool completeCertChain
= false;
1053 /* special case - one arg of "h" or "-h" or "hv" */
1055 if((strcmp(argv
[1], "h") == 0) || (strcmp(argv
[1], "-h") == 0)) {
1058 if(strcmp(argv
[1], "hv") == 0) {
1063 /* set up defaults */
1064 memset(&pargs
, 0, sizeof(sslPingArgs
));
1065 pargs
.hostName
= DEFAULT_HOST
;
1066 pargs
.port
= DEFAULT_PORT
;
1067 pargs
.resumableEnable
= true;
1070 for(arg
=1; arg
<argc
; arg
++) {
1073 /* first arg, is always hostname; '-' means default */
1074 if(argp
[0] != '-') {
1075 pargs
.hostName
= argp
;
1079 if(argp
[0] == '/') {
1080 /* path always starts with leading slash */
1087 pargs
.allowExpired
= true;
1090 pargs
.allowExpiredRoot
= true;
1093 pargs
.disableCertVerify
= true;
1096 pargs
.disableCertVerify
= true; // implied
1097 pargs
.manualCertVerify
= true;
1101 /* requires another arg */
1104 pargs
.anchorFile
= argv
[arg
];
1108 /* requires another arg */
1111 pargs
.anchorFile
= argv
[arg
];
1112 pargs
.replaceAnchors
= true;
1115 pargs
.allowAnyRoot
= true;
1118 pargs
.dumpRxData
= true;
1125 /* requires another arg */
1128 fileBase
= argv
[arg
];
1131 pargs
.cipherRestrict
= argp
[2];
1134 doSslV3
= doTlsV1
= doTlsV11
= false;
1138 doSslV2
= doTlsV1
= doTlsV11
= doTlsV12
= false;
1142 doSslV2
= doSslV3
= doTlsV11
= doTlsV12
= false;
1146 doSslV2
= doSslV3
= doTlsV1
= false;
1150 doSslV2
= doSslV3
= doTlsV1
= doTlsV12
= false;
1154 doSslV2
= doSslV3
= doTlsV1
= doTlsV11
= doTlsV12
= true;
1160 doSslV2
= doSslV3
= doTlsV1
= doTlsV11
= doTlsV12
= false;
1161 doProtUnknown
= true;
1164 pargs
.keepConnected
= true;
1167 pargs
.requireNotify
= true;
1168 pargs
.keepConnected
= true;
1171 pargs
.resumableEnable
= false;
1174 pargs
.nonBlocking
= true;
1180 if(argp
[1] != '=') {
1183 verifyProt
= true; // implied
1184 maxProtocol
= charToProt(argp
[2], argv
);
1187 if(argp
[1] != '=') {
1190 acceptedProts
= &argp
[2];
1191 doSslV3
= doSslV2
= doTlsV1
= doTlsV11
= doTlsV12
= false;
1194 loopCount
= atoi(&argp
[2]);
1195 if(loopCount
== 0) {
1196 printf("***bad loopCount\n");
1201 pargs
.port
= atoi(&argp
[2]);
1204 pargs
.allowHostnameSpoof
= true;
1207 pargs
.vfyHostName
= &argp
[2];
1210 keyChainName
= &argp
[2];
1213 encryptKeyChainName
= &argp
[2];
1216 getMsgSpec
= &argp
[2];
1219 if(argp
[1] != '=') {
1222 vfyCertState
= true;
1225 expectCertState
= kSSLClientCertNone
;
1228 expectCertState
= kSSLClientCertRequested
;
1231 expectCertState
= kSSLClientCertSent
;
1234 expectCertState
= kSSLClientCertRejected
;
1241 pargs
.password
= &argp
[2];
1247 pauseFirstLoop
= true;
1253 pargs
.verbose
= true;
1256 pargs
.silent
= pargs
.quiet
= true;
1259 displayHandshakeTimes
= true;
1262 completeCertChain
= true;
1265 if(pargs
.verbose
|| (argp
[1] == 'v')) {
1276 pargs
.getMsg
= getMsgSpec
;
1279 sprintf(getMsg
, "%s %s %s",
1280 DEFAULT_GETMSG
, getPath
, DEFAULT_GET_SUFFIX
);
1281 pargs
.getMsg
= getMsg
;
1286 securityd_init(NULL
);
1290 /* get client cert and optional encryption cert as CFArrayRef */
1292 pargs
.clientCerts
= getSslCerts(keyChainName
, false, completeCertChain
,
1293 pargs
.anchorFile
, &serverKc
);
1294 if(pargs
.clientCerts
== nil
) {
1297 #ifdef USE_CDSA_CRYPTO
1298 if(pargs
.password
) {
1299 OSStatus ortn
= SecKeychainUnlock(serverKc
,
1300 strlen(pargs
.password
), pargs
.password
, true);
1302 printf("SecKeychainUnlock returned %d\n", (int)ortn
);
1308 if(encryptKeyChainName
) {
1309 pargs
.encryptClientCerts
= getSslCerts(encryptKeyChainName
, true,
1310 completeCertChain
, pargs
.anchorFile
, &encryptKc
);
1311 if(pargs
.encryptClientCerts
== nil
) {
1315 signal(SIGPIPE
, sigpipe
);
1317 for(loop
=0; loop
<loopCount
; loop
++) {
1319 * One pass for each protocol version, skipping any explicit version if
1320 * an attempt at a higher version and succeeded in doing so successfully fell
1324 pargs
.tryVersion
= kTLSProtocol12
;
1325 pargs
.acceptedProts
= NULL
;
1327 printf("Connecting to host %s with TLS V1.2...", pargs
.hostName
);
1330 err
= sslPing(&pargs
);
1336 sprintf(fullFileBase
, "%s_v3.1", fileBase
);
1338 showSSLResult(pargs
,
1341 fileBase
? fullFileBase
: NULL
);
1343 freePeerCerts(pargs
.peerCerts
);
1345 /* deal with fallbacks, skipping redundant tests */
1346 switch(pargs
.negVersion
) {
1347 case kTLSProtocol11
:
1368 ourRtn
+= verifyProtocol(verifyProt
, maxProtocol
, kTLSProtocol12
,
1371 /* note we do this regardless since the client state might be
1372 * the cause of a failure */
1373 ourRtn
+= verifyClientCertState(vfyCertState
, expectCertState
,
1377 pargs
.tryVersion
= kTLSProtocol11
;
1378 pargs
.acceptedProts
= NULL
;
1380 printf("Connecting to host %s with TLS V1.1...", pargs
.hostName
);
1383 err
= sslPing(&pargs
);
1389 sprintf(fullFileBase
, "%s_v3.1", fileBase
);
1391 showSSLResult(pargs
,
1394 fileBase
? fullFileBase
: NULL
);
1396 freePeerCerts(pargs
.peerCerts
);
1398 /* deal with fallbacks, skipping redundant tests */
1399 switch(pargs
.negVersion
) {
1415 ourRtn
+= verifyProtocol(verifyProt
, maxProtocol
, kTLSProtocol11
,
1418 /* note we do this regardless since the client state might be
1419 * the cause of a failure */
1420 ourRtn
+= verifyClientCertState(vfyCertState
, expectCertState
,
1425 protXOnly
? kTLSProtocol1Only
: kTLSProtocol1
;
1426 pargs
.acceptedProts
= NULL
;
1428 printf("Connecting to host %s with TLS V1...", pargs
.hostName
);
1431 err
= sslPing(&pargs
);
1437 sprintf(fullFileBase
, "%s_v3.1", fileBase
);
1439 showSSLResult(pargs
,
1442 fileBase
? fullFileBase
: NULL
);
1444 freePeerCerts(pargs
.peerCerts
);
1446 /* deal with fallbacks, skipping redundant tests */
1447 switch(pargs
.negVersion
) {
1458 ourRtn
+= verifyProtocol(verifyProt
, maxProtocol
, kTLSProtocol1
,
1461 /* note we do this regardless since the client state might be
1462 * the cause of a failure */
1463 ourRtn
+= verifyClientCertState(vfyCertState
, expectCertState
,
1467 pargs
.tryVersion
= protXOnly
? kSSLProtocol3Only
: kSSLProtocol3
;
1468 pargs
.acceptedProts
= NULL
;
1470 printf("Connecting to host %s with SSL V3...", pargs
.hostName
);
1473 err
= sslPing(&pargs
);
1479 sprintf(fullFileBase
, "%s_v3.0", fileBase
);
1481 showSSLResult(pargs
,
1484 fileBase
? fullFileBase
: NULL
);
1486 freePeerCerts(pargs
.peerCerts
);
1488 /* deal with fallbacks, skipping redundant tests */
1489 switch(pargs
.negVersion
) {
1496 ourRtn
+= verifyProtocol(verifyProt
, maxProtocol
, kSSLProtocol3
,
1499 /* note we do this regardless since the client state might be
1500 * the cause of a failure */
1501 ourRtn
+= verifyClientCertState(vfyCertState
, expectCertState
,
1507 sprintf(fullFileBase
, "%s_v2", fileBase
);
1510 printf("Connecting to host %s with SSL V2...", pargs
.hostName
);
1513 pargs
.tryVersion
= kSSLProtocol2
;
1514 pargs
.acceptedProts
= NULL
;
1515 err
= sslPing(&pargs
);
1521 sprintf(fullFileBase
, "%s_v2", fileBase
);
1523 showSSLResult(pargs
,
1526 fileBase
? fullFileBase
: NULL
);
1528 freePeerCerts(pargs
.peerCerts
);
1530 ourRtn
+= verifyProtocol(verifyProt
, maxProtocol
, kSSLProtocol2
,
1533 /* note we do this regardless since the client state might be
1534 * the cause of a failure */
1535 ourRtn
+= verifyClientCertState(vfyCertState
, expectCertState
,
1540 printf("Connecting to host %s with kSSLProtocolUnknown...",
1544 pargs
.tryVersion
= kSSLProtocolUnknown
;
1545 pargs
.acceptedProts
= NULL
;
1546 err
= sslPing(&pargs
);
1552 sprintf(fullFileBase
, "%s_def", fileBase
);
1554 showSSLResult(pargs
,
1557 fileBase
? fullFileBase
: NULL
);
1559 freePeerCerts(pargs
.peerCerts
);
1561 if(acceptedProts
!= NULL
) {
1562 pargs
.acceptedProts
= acceptedProts
;
1563 pargs
.tryVersion
= kSSLProtocolUnknown
; // not used
1565 printf("Connecting to host %s with acceptedProts %s...",
1566 pargs
.hostName
, pargs
.acceptedProts
);
1569 err
= sslPing(&pargs
);
1575 sprintf(fullFileBase
, "%s_def", fileBase
);
1577 showSSLResult(pargs
,
1580 fileBase
? fullFileBase
: NULL
);
1582 freePeerCerts(pargs
.peerCerts
);
1586 /* pause after first, before last to grab trace */
1587 ((loop
== 0) || (loop
== loopCount
- 1))
1592 printf("a to abort, c to continue: ");
1599 if(displayHandshakeTimes
) {
1600 CFAbsoluteTime totalTime
;
1601 unsigned numHandshakes
;
1602 if(pargs
.numHandshakes
== 1) {
1603 /* just display the first one */
1604 totalTime
= pargs
.handshakeTimeFirst
;
1608 /* skip the first one */
1609 totalTime
= pargs
.handshakeTimeTotal
;
1610 numHandshakes
= pargs
.numHandshakes
- 1;
1612 if(numHandshakes
!= 0) {
1613 printf(" %u handshakes in %f seconds; %f seconds per handshake\n",
1614 numHandshakes
, totalTime
,
1615 (totalTime
/ numHandshakes
));
1618 //printCertShutdown();
1620 printf("===%s exiting with %d %s for host %s\n", argv
[0], ourRtn
,
1621 (ourRtn
> 1) ? "errors" : "error", pargs
.hostName
);