2 * Copyright (c) 2006-2013, 2015 Apple Inc. All Rights Reserved.
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>
21 #include "sslAppUtils.h"
23 #include "utilities/fileIo.h"
24 #include "utilities/SecCFWrappers.h"
25 #include "utilities/SecIOFormat.h"
26 #include "SecurityTool/print_cert.h"
28 #define DEFAULT_GETMSG "GET"
29 #define DEFAULT_PATH "/"
30 #define DEFAULT_GET_SUFFIX "HTTP/1.0\r\n\r\n"
32 #define DEFAULT_HOST "www.amazon.com"
33 #define DEFAULT_PORT 443
36 static void usageNorm(char **argv
)
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",
42 printf("Optional path argument must start with leading '/'.\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");
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"
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");
72 printf(" hv More, verbose help\n");
75 static void usageVerbose(char **argv
) __attribute__((noreturn
));
76 static void usageVerbose(char **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 "
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 "
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");
107 static void usage(char **argv
) __attribute__((noreturn
));
108 static void usage(char **argv
)
115 * Arguments to top-level sslPing()
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,
126 const char *getMsg
; // e.g.,
127 // "GET / HTTP/1.0\r\n\r\n"
130 bool allowExpiredRoot
;
131 bool disableCertVerify
;
132 bool manualCertVerify
;
133 bool dumpRxData
; // display server data
134 char cipherRestrict
; // '2', 'd'. etc...; '\0' for
137 bool requireNotify
; // require closure notify
139 bool resumableEnable
;
140 bool allowHostnameSpoof
;
144 CFArrayRef clientCerts
; // optional
145 bool quiet
; // minimal stdout
146 bool silent
; // no stdout
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
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
;
163 CFMutableArrayRef alpnNames
;
164 CFMutableArrayRef policies
;
172 printf("***SIGPIPE***\n");
176 * Manually evaluate session's SecTrustRef.
179 static OSStatus
sslEvaluateTrust(
182 CFArrayRef
*peerCerts
) // fetched and retained
184 OSStatus ortn
= errSecSuccess
;
185 SecTrustRef secTrust
= NULL
;
187 ortn
= SSLGetPeerSecTrust(ctx
, &secTrust
);
189 printf("\n***Error obtaining peer SecTrustRef: %s\n",
190 sslGetSSLErrString(ortn
));
193 if(secTrust
== NULL
) {
194 /* this is the normal case for resumed sessions, in which
195 * no cert evaluation is performed */
197 printf("...No SecTrust available - this is a resumed session, right?\n");
199 return errSecSuccess
;
203 if (pargs
->policies
) {
204 SecTrustSetPolicies(secTrust
, pargs
->policies
);
207 SecTrustResultType secTrustResult
;
208 ortn
= SecTrustEvaluate(secTrust
, &secTrustResult
);
210 printf("\n***Error on SecTrustEvaluate: %d\n", (int)ortn
);
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;
236 res
= "UNKNOWN"; break;
238 printf("\nSecTrustEvaluate(): secTrustResult %s\n", res
);
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 */
248 printf("\n***SecTrustEvaluate reported secTrustResult %d\n",
249 (int)secTrustResult
);
250 ortn
= errSSLXCertChainInvalid
;
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
);
262 printSslErrStr("SecTrustGetResult", thisRtn
);
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.
269 CFIndex numCerts
= CFArrayGetCount(*peerCerts
);
270 for(CFIndex dex
=0; dex
<numCerts
; dex
++) {
271 CFRetain(CFArrayGetValueAtIndex(*peerCerts
, dex
));
278 /* print reply received from server, safely */
279 static void dumpAscii(
283 char *cp
= (char *)rcvBuf
;
287 for(i
=0; i
<len
; i
++) {
300 if(isprint(c
) && (c
!= '\n')) {
304 printf("<%02X>", ((unsigned)c
) & 0xff);
314 alpnFunc(SSLContextRef ctx
,
316 const void *alpnData
,
317 size_t alpnDataLength
)
319 printf("[selected ALPN]");
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).
329 #define RCV_BUF_SIZE 256
331 static OSStatus
sslPing(
337 SSLContextRef ctx
= NULL
;
340 uint8_t rcvBuf
[RCV_BUF_SIZE
];
341 CFAbsoluteTime startHandshake
;
342 CFAbsoluteTime endHandshake
;
344 pargs
->negVersion
= kSSLProtocolUnknown
;
345 pargs
->negCipher
= SSL_NULL_WITH_NULL_NULL
;
346 pargs
->peerCerts
= NULL
;
348 /* first make sure requested server is there */
349 ortn
= MakeServerConnection(pargs
->hostName
, pargs
->port
, pargs
->nonBlocking
,
352 printf("MakeServerConnection returned %d; aborting\n", (int)ortn
);
356 printf("...connected to server; starting SecureTransport\n");
360 * Set up a SecureTransport session.
361 * First the standard calls.
363 ctx
= SSLCreateContext(kCFAllocatorDefault
, kSSLClientSide
, kSSLStreamType
);
365 printf("SSLCreateContext\n");
368 ortn
= SSLSetIOFuncs(ctx
, SocketRead
, SocketWrite
);
370 printSslErrStr("SSLSetIOFuncs", ortn
);
373 ortn
= SSLSetConnection(ctx
, (SSLConnectionRef
)(intptr_t)sock
);
375 printSslErrStr("SSLSetConnection", ortn
);
378 SSLConnectionRef getConn
;
379 ortn
= SSLGetConnection(ctx
, &getConn
);
381 printSslErrStr("SSLGetConnection", ortn
);
384 if(getConn
!= (SSLConnectionRef
)(intptr_t)sock
) {
385 printf("***SSLGetConnection error\n");
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
;
396 ortn
= SSLSetPeerDomainName(ctx
, vfyHost
, strlen(vfyHost
));
398 printSslErrStr("SSLSetPeerDomainName", ortn
);
404 * SecureTransport options.
406 if(pargs
->acceptedProts
) {
407 ortn
= SSLSetProtocolVersionEnabled(ctx
, kSSLProtocolAll
, false);
409 printSslErrStr("SSLSetProtocolVersionEnabled(all off)", ortn
);
412 for(const char *cp
= pargs
->acceptedProts
; *cp
; cp
++) {
416 prot
= kSSLProtocol2
;
419 prot
= kSSLProtocol3
;
422 prot
= kTLSProtocol12
;
427 ortn
= SSLSetProtocolVersionEnabled(ctx
, prot
, true);
429 printSslErrStr("SSLSetProtocolVersionEnabled", ortn
);
435 ortn
= SSLSetProtocolVersion(ctx
, pargs
->tryVersion
);
437 printSslErrStr("SSLSetProtocolVersion", ortn
);
441 ortn
= SSLGetProtocolVersion(ctx
, &getVers
);
443 printSslErrStr("SSLGetProtocolVersion", ortn
);
446 if(getVers
!= pargs
->tryVersion
) {
447 printf("***SSLGetProtocolVersion screwup: try %s get %s\n",
448 sslGetProtocolVersionString(pargs
->tryVersion
),
449 sslGetProtocolVersionString(getVers
));
454 if(pargs
->resumableEnable
) {
455 const void *rtnId
= NULL
;
458 ortn
= SSLSetPeerID(ctx
, &peerId
, sizeof(PeerSpec
));
460 printSslErrStr("SSLSetPeerID", ortn
);
463 /* quick test of the get fcn */
464 ortn
= SSLGetPeerID(ctx
, &rtnId
, &rtnIdLen
);
466 printSslErrStr("SSLGetPeerID", ortn
);
469 if((rtnId
== NULL
) || (rtnIdLen
!= sizeof(PeerSpec
))) {
470 printf("***SSLGetPeerID screwup\n");
472 else if(memcmp(&peerId
, rtnId
, rtnIdLen
) != 0) {
473 printf("***SSLGetPeerID data mismatch\n");
476 if(pargs
->allowExpired
) {
477 ortn
= SSLSetAllowsExpiredCerts(ctx
, true);
479 printSslErrStr("SSLSetAllowExpiredCerts", ortn
);
483 if(pargs
->allowExpiredRoot
) {
484 ortn
= SSLSetAllowsExpiredRoots(ctx
, true);
486 printSslErrStr("SSLSetAllowsExpiredRoots", ortn
);
490 if(pargs
->disableCertVerify
) {
491 ortn
= SSLSetEnableCertVerify(ctx
, false);
493 printSslErrStr("SSLSetEnableCertVerify", ortn
);
497 if(pargs
->allowAnyRoot
) {
498 ortn
= SSLSetAllowsAnyRoot(ctx
, true);
500 printSslErrStr("SSLSetAllowAnyRoot", ortn
);
504 if(pargs
->cipherRestrict
!= '\0') {
505 ortn
= sslSetCipherRestrictions(ctx
, pargs
->cipherRestrict
);
510 if(pargs
->anchorFile
) {
511 ortn
= sslAddTrustedRoot(ctx
, pargs
->anchorFile
, pargs
->replaceAnchors
);
513 printf("***Error obtaining anchor file %s\n", pargs
->anchorFile
);
517 if(pargs
->clientCerts
) {
519 if(pargs
->anchorFile
== NULL
) {
520 /* assume this is a root we want to implicitly trust */
521 ortn
= addIdentityAsTrustedRoot(ctx
, pargs
->clientCerts
);
526 ortn
= SSLSetCertificate(ctx
, pargs
->clientCerts
);
528 printSslErrStr("SSLSetCertificate", ortn
);
531 /* quickie test for this new function */
532 ortn
= SSLGetCertificate(ctx
, &dummy
);
534 printSslErrStr("SSLGetCertificate", ortn
);
537 if(dummy
!= pargs
->clientCerts
) {
538 printf("***SSLGetCertificate error\n");
543 if (pargs
->alpnNames
) {
544 CFMutableDataRef alpn
= CFDataCreateMutable(NULL
, 0);
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
);
553 SSLSetALPNData(ctx
, CFDataGetBytePtr(alpn
), CFDataGetLength(alpn
));
554 SSLSetALPNFunc(ctx
, alpnFunc
, (void *)NULL
);
558 /*** end options ***/
561 printf("...starting SSL handshake\n");
563 startHandshake
= CFAbsoluteTimeGetCurrent();
566 { ortn
= SSLHandshake(ctx
);
567 if((ortn
== errSSLWouldBlock
) && !pargs
->silent
) {
568 /* keep UI responsive */
571 } while (ortn
== errSSLWouldBlock
);
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
;
580 /* normal running total */
581 pargs
->handshakeTimeTotal
+= pargs
->handshakeTimeOp
;
583 pargs
->numHandshakes
++;
585 ortn
= SSLCopyPeerTrust(ctx
, &pargs
->peerTrust
);
587 printf("***SSLCopyPeerTrust error %" PRIdOSStatus
"\n", ortn
);
588 pargs
->peerTrust
= NULL
;
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
);
600 OSStatus certRtn
= sslEvaluateTrust(ctx
, pargs
, &pargs
->peerCerts
);
602 if (certRtn
&& !pargs
->manualCertVerify
) {
603 SSLCopyPeerCertificates(ctx
, &pargs
->peerCerts
);
607 if(certRtn
&& !ortn
) {
620 printf("...SSL handshake complete\n");
622 length
= strlen(pargs
->getMsg
);
623 (void) SSLWrite(ctx
, pargs
->getMsg
, length
, &actLen
);
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)).
631 if(pargs
->dumpRxData
) {
634 ortn
= SSLGetBufferedReadSize(ctx
, &avail
);
636 printf("***SSLGetBufferedReadSize error\n");
640 printf("\n%d bytes available: ", (int)avail
);
643 ortn
= SSLRead(ctx
, rcvBuf
, RCV_BUF_SIZE
, &actLen
);
644 if((actLen
== 0) && !pargs
->silent
) {
647 if((actLen
== 0) && (ortn
== errSecSuccess
)) {
648 printf("***Radar 2984932 confirmed***\n");
650 if (ortn
== errSSLWouldBlock
) {
651 /* for this loop, these are identical */
652 ortn
= errSecSuccess
;
654 if((actLen
> 0) && pargs
->dumpRxData
) {
655 dumpAscii(rcvBuf
, actLen
);
657 if(ortn
!= errSecSuccess
) {
658 /* connection closed by server or by error */
661 if(!pargs
->keepConnected
&& (actLen
> 0)) {
662 /* good enough, we connected */
670 /* snag these again in case of renegotiate */
671 SSLGetClientCertificateState(ctx
, &pargs
->certState
);
672 SSLGetNegotiatedCipher(ctx
, &pargs
->negCipher
);
673 SSLGetNegotiatedProtocolVersion(ctx
, &pargs
->negVersion
);
675 /* convert normal "shutdown" into zero err rtn */
676 if(ortn
== errSSLClosedGraceful
) {
677 ortn
= errSecSuccess
;
679 if((ortn
== errSSLClosedNoNotify
) && !pargs
->requireNotify
) {
680 /* relaxed disconnect rules */
681 ortn
= errSecSuccess
;
685 * always do close, even on error - to flush outgoing write queue
687 OSStatus cerr
= SSLClose(ctx
);
688 if(ortn
== errSecSuccess
) {
692 endpointShutdown(sock
);
702 static void add_key(const void *key
, const void *value
, void *context
) {
703 CFArrayAppendValue((CFMutableArrayRef
)context
, key
);
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? */
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);
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);
725 CFStringAppend(line
, key
);
727 for (jx
= CFStringGetLength(key
);
728 jx
< maxWidth
; ++jx
) {
729 CFStringAppend(line
, CFSTR(" "));
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
,
741 CFStringAppend(line
, ds
);
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
);
755 CFStringAppend(line
, CFSTR("/"));
756 CFStringAppend(line
, v_s
);
757 CFStringAppend(line
, CFSTR("/ "));
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
]);
766 CFStringAppendFormat(line
, NULL
, CFSTR("%@"), value
);
768 CFStringWriteToFileWithNewline(line
, stdout
);
775 static void showPeerTrust(SecTrustRef peerTrust
, bool verbose
) {
777 if(peerTrust
== NULL
) {
784 printf("\n=============== Peer Trust Properties ===============\n");
785 CFArrayRef plist
= SecTrustCopyProperties(peerTrust
);
791 printf("\n================== Peer Trust Info ==================\n");
792 CFDictionaryRef info
= SecTrustCopyInfo(peerTrust
);
793 if (info
&& CFDictionaryGetCount(info
)) {
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
);
806 printf("\n============= Peer Trust Cert %lu Details =============\n\n", i
);
807 plist
= SecTrustCopyDetailedPropertiesAtIndex(peerTrust
, i
);
811 printf("\n============= End of Peer Trust Cert %lu ==============\n", i
);
816 static void showPeerCerts(
817 CFArrayRef __unused peerCerts
,
818 bool __unused verbose
)
822 SecCertificateRef certRef
;
825 if(peerCerts
== NULL
) {
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
);
838 static void writePeerCerts(
839 CFArrayRef peerCerts
,
840 const char *fileBase
)
843 SecCertificateRef certRef
;
847 if(peerCerts
== NULL
) {
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
);
856 writeFile(fileName
, CFDataGetBytePtr(derCert
),
857 CFDataGetLength(derCert
));
861 printf("...wrote %lu certs to fileBase %s\n", numCerts
, fileBase
);
865 * Show result of an sslPing().
866 * Assumes the following from sslPingArgs:
880 static void showSSLResult(
881 const sslPingArgs
*pargs
,
883 int displayPeerCerts
,
884 char *fileBase
) // non-NULL: write certs to file
886 CFIndex numPeerCerts
;
890 if(pargs
->acceptedProts
) {
891 printf(" Allowed SSL versions : %s\n", pargs
->acceptedProts
);
894 printf(" Attempted SSL version : %s\n",
895 sslGetProtocolVersionString(pargs
->tryVersion
));
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
));
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))) {
919 printf("NOT RESUMED\n");
921 printf(" Handshake time : %f seconds\n", pargs
->handshakeTimeOp
);
923 if(pargs
->peerCerts
== NULL
) {
927 numPeerCerts
= CFArrayGetCount(pargs
->peerCerts
);
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);
936 if(fileBase
!= NULL
) {
937 writePeerCerts(pargs
->peerCerts
, fileBase
);
944 static int verifyProtocol(
946 SSLProtocol maxProtocol
,
947 SSLProtocol reqProtocol
,
948 SSLProtocol negProtocol
)
953 if(reqProtocol
> maxProtocol
) {
954 /* known not to support this attempt, relax */
955 reqProtocol
= maxProtocol
;
957 if(reqProtocol
!= negProtocol
) {
958 printf("***Expected protocol %s; negotiated %s\n",
959 sslGetProtocolVersionString(reqProtocol
),
960 sslGetProtocolVersionString(negProtocol
));
968 static int verifyClientCertState(
969 bool verifyCertState
,
970 SSLClientCertificateState expectState
,
971 SSLClientCertificateState gotState
)
973 if(!verifyCertState
) {
976 if(expectState
== gotState
) {
979 printf("***Expected clientCertState %s; got %s\n",
980 sslGetClientCertStateString(expectState
),
981 sslGetClientCertStateString(gotState
));
985 static SSLProtocol
charToProt(
991 return kSSLProtocol2
;
993 return kSSLProtocol3
;
995 return kTLSProtocol12
;
1000 return kSSLProtocolUnknown
;
1003 int main(int argc
, char **argv
)
1009 char fullFileBase
[100];
1010 int ourRtn
= 0; // exit status - sum of all errors
1012 SecKeychainRef serverKc
= nil
;
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
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;
1040 /* special case - one arg of "h" or "-h" or "hv" */
1042 if((strcmp(argv
[1], "h") == 0) || (strcmp(argv
[1], "-h") == 0)) {
1045 if(strcmp(argv
[1], "hv") == 0) {
1050 /* set up defaults */
1051 memset(&pargs
, 0, sizeof(sslPingArgs
));
1052 pargs
.hostName
= DEFAULT_HOST
;
1053 pargs
.port
= DEFAULT_PORT
;
1054 pargs
.resumableEnable
= true;
1057 for(arg
=1; arg
<argc
; arg
++) {
1060 /* first arg, is always hostname; '-' means default */
1061 if(argp
[0] != '-') {
1062 pargs
.hostName
= argp
;
1066 if(argp
[0] == '/') {
1067 /* path always starts with leading slash */
1075 /* requires another arg */
1078 if (pargs
.alpnNames
== NULL
) {
1079 pargs
.alpnNames
= CFArrayCreateMutableForCFTypes(NULL
);
1082 CFDataRef alpn
= CFDataCreate(NULL
, (const UInt8
*)argv
[arg
], strlen(argv
[arg
]));
1083 CFArrayAppendValue(pargs
.alpnNames
, alpn
);
1084 CFReleaseNull(alpn
);
1089 CFDictionaryRef context
= NULL
;
1092 /* requires another arg */
1096 if (argp
[0] == 'W') {
1097 context
= CFDictionaryCreateForCFTypes(NULL
,
1098 CFSTR("AppleServerAuthenticationAllowUATAPN"), kCFBooleanTrue
,
1099 CFSTR("AppleServerAuthenticationAllowUATIDS"), kCFBooleanTrue
,
1100 CFSTR("AppleServerAuthenticationAllowUATGS"), kCFBooleanTrue
,
1103 const char *verifyName
= pargs
.hostName
;
1105 if (pargs
.policies
== NULL
) {
1106 pargs
.policies
= CFArrayCreateMutableForCFTypes(NULL
);
1109 if (pargs
.vfyHostName
)
1110 verifyName
= pargs
.vfyHostName
;
1112 SecPolicyRef policy
= NULL
;
1113 CFStringRef hostname
= CFStringCreateWithCString(NULL
, verifyName
, kCFStringEncodingUTF8
);
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
);
1124 printf("unknown policy: %s", argv
[arg
]);
1125 CFReleaseNull(hostname
);
1126 CFReleaseNull(context
);
1131 CFArrayAppendValue(pargs
.policies
, policy
);
1134 CFReleaseNull(policy
);
1135 CFReleaseNull(hostname
);
1136 CFReleaseNull(context
);
1141 pargs
.allowExpired
= true;
1144 pargs
.allowExpiredRoot
= true;
1147 pargs
.disableCertVerify
= true;
1150 pargs
.disableCertVerify
= true; // implied
1151 pargs
.manualCertVerify
= true;
1155 /* requires another arg */
1158 pargs
.anchorFile
= argv
[arg
];
1162 /* requires another arg */
1165 pargs
.anchorFile
= argv
[arg
];
1166 pargs
.replaceAnchors
= true;
1169 pargs
.allowAnyRoot
= true;
1172 pargs
.dumpRxData
= true;
1179 /* requires another arg */
1182 fileBase
= argv
[arg
];
1185 pargs
.cipherRestrict
= argp
[2];
1188 doSslV3
= doTlsV1
= doTlsV11
= false;
1192 doSslV2
= doTlsV1
= doTlsV11
= doTlsV12
= false;
1196 doSslV2
= doSslV3
= doTlsV11
= doTlsV12
= false;
1200 doSslV2
= doSslV3
= doTlsV1
= false;
1204 doSslV2
= doSslV3
= doTlsV1
= doTlsV12
= false;
1208 doSslV2
= doSslV3
= doTlsV1
= doTlsV11
= doTlsV12
= true;
1214 doSslV2
= doSslV3
= doTlsV1
= doTlsV11
= doTlsV12
= false;
1215 doProtUnknown
= true;
1218 pargs
.keepConnected
= true;
1221 pargs
.requireNotify
= true;
1222 pargs
.keepConnected
= true;
1225 pargs
.resumableEnable
= false;
1228 pargs
.nonBlocking
= true;
1234 if(argp
[1] != '=') {
1237 verifyProt
= true; // implied
1238 maxProtocol
= charToProt(argp
[2], argv
);
1241 if(argp
[1] != '=') {
1244 acceptedProts
= argv
[arg
];
1245 doSslV3
= doSslV2
= doTlsV1
= doTlsV11
= doTlsV12
= false;
1249 /* requires another arg */
1252 loopCount
= atoi(argv
[arg
]);
1253 if(loopCount
== 0) {
1254 printf("***bad loopCount\n");
1260 /* requires another arg */
1263 pargs
.port
= atoi(argv
[arg
]);
1266 pargs
.allowHostnameSpoof
= true;
1270 /* requires another arg */
1273 pargs
.vfyHostName
= argv
[arg
];
1277 /* requires another arg */
1280 keyChainName
= &argp
[2];
1283 getMsgSpec
= &argp
[2];
1286 if(argp
[1] != '=') {
1289 vfyCertState
= true;
1292 expectCertState
= kSSLClientCertNone
;
1295 expectCertState
= kSSLClientCertRequested
;
1298 expectCertState
= kSSLClientCertSent
;
1301 expectCertState
= kSSLClientCertRejected
;
1308 pargs
.password
= &argp
[2];
1314 pauseFirstLoop
= true;
1320 pargs
.verbose
= true;
1323 pargs
.silent
= pargs
.quiet
= true;
1326 displayHandshakeTimes
= true;
1329 completeCertChain
= true;
1332 if(pargs
.verbose
|| (argp
[1] == 'v')) {
1345 pargs
.getMsg
= getMsgSpec
;
1348 sprintf(getMsg
, "%s %s %s",
1349 DEFAULT_GETMSG
, getPath
, DEFAULT_GET_SUFFIX
);
1350 pargs
.getMsg
= getMsg
;
1353 /* get client cert and optional encryption cert as CFArrayRef */
1355 pargs
.clientCerts
= getSslCerts(keyChainName
, false, completeCertChain
,
1356 pargs
.anchorFile
, &serverKc
);
1357 if(pargs
.clientCerts
== nil
) {
1360 #ifdef USE_CDSA_CRYPTO
1361 if(pargs
.password
) {
1362 OSStatus ortn
= SecKeychainUnlock(serverKc
,
1363 strlen(pargs
.password
), pargs
.password
, true);
1365 printf("SecKeychainUnlock returned %d\n", (int)ortn
);
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
);
1380 for(loop
=0; loop
<loopCount
; loop
++) {
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
1387 pargs
.tryVersion
= kTLSProtocol12
;
1388 pargs
.acceptedProts
= NULL
;
1390 printf("Connecting to host %s with TLS V1.2...", pargs
.hostName
);
1393 err
= sslPing(&pargs
);
1399 sprintf(fullFileBase
, "%s_v3.1", fileBase
);
1401 showSSLResult(&pargs
,
1404 fileBase
? fullFileBase
: NULL
);
1406 CFReleaseNull(pargs
.peerCerts
);
1408 /* deal with fallbacks, skipping redundant tests */
1409 switch(pargs
.negVersion
) {
1410 case kTLSProtocol11
:
1431 ourRtn
+= verifyProtocol(verifyProt
, maxProtocol
, kTLSProtocol12
,
1434 /* note we do this regardless since the client state might be
1435 * the cause of a failure */
1436 ourRtn
+= verifyClientCertState(vfyCertState
, expectCertState
,
1440 pargs
.tryVersion
= kTLSProtocol11
;
1441 pargs
.acceptedProts
= NULL
;
1443 printf("Connecting to host %s with TLS V1.1...", pargs
.hostName
);
1446 err
= sslPing(&pargs
);
1452 sprintf(fullFileBase
, "%s_v3.1", fileBase
);
1454 showSSLResult(&pargs
,
1457 fileBase
? fullFileBase
: NULL
);
1459 CFReleaseNull(pargs
.peerCerts
);
1461 /* deal with fallbacks, skipping redundant tests */
1462 switch(pargs
.negVersion
) {
1478 ourRtn
+= verifyProtocol(verifyProt
, maxProtocol
, kTLSProtocol11
,
1481 /* note we do this regardless since the client state might be
1482 * the cause of a failure */
1483 ourRtn
+= verifyClientCertState(vfyCertState
, expectCertState
,
1488 protXOnly
? kTLSProtocol1Only
: kTLSProtocol1
;
1489 pargs
.acceptedProts
= NULL
;
1491 printf("Connecting to host %s with TLS V1...", pargs
.hostName
);
1494 err
= sslPing(&pargs
);
1500 sprintf(fullFileBase
, "%s_v3.1", fileBase
);
1502 showSSLResult(&pargs
,
1505 fileBase
? fullFileBase
: NULL
);
1507 CFReleaseNull(pargs
.peerCerts
);
1509 /* deal with fallbacks, skipping redundant tests */
1510 switch(pargs
.negVersion
) {
1521 ourRtn
+= verifyProtocol(verifyProt
, maxProtocol
, kTLSProtocol1
,
1524 /* note we do this regardless since the client state might be
1525 * the cause of a failure */
1526 ourRtn
+= verifyClientCertState(vfyCertState
, expectCertState
,
1530 pargs
.tryVersion
= protXOnly
? kSSLProtocol3Only
: kSSLProtocol3
;
1531 pargs
.acceptedProts
= NULL
;
1533 printf("Connecting to host %s with SSL V3...", pargs
.hostName
);
1536 err
= sslPing(&pargs
);
1542 sprintf(fullFileBase
, "%s_v3.0", fileBase
);
1544 showSSLResult(&pargs
,
1547 fileBase
? fullFileBase
: NULL
);
1549 CFReleaseNull(pargs
.peerCerts
);
1551 /* deal with fallbacks, skipping redundant tests */
1552 switch(pargs
.negVersion
) {
1559 ourRtn
+= verifyProtocol(verifyProt
, maxProtocol
, kSSLProtocol3
,
1562 /* note we do this regardless since the client state might be
1563 * the cause of a failure */
1564 ourRtn
+= verifyClientCertState(vfyCertState
, expectCertState
,
1570 sprintf(fullFileBase
, "%s_v2", fileBase
);
1573 printf("Connecting to host %s with SSL V2...", pargs
.hostName
);
1576 pargs
.tryVersion
= kSSLProtocol2
;
1577 pargs
.acceptedProts
= NULL
;
1578 err
= sslPing(&pargs
);
1584 sprintf(fullFileBase
, "%s_v2", fileBase
);
1586 showSSLResult(&pargs
,
1589 fileBase
? fullFileBase
: NULL
);
1591 CFReleaseNull(pargs
.peerCerts
);
1593 ourRtn
+= verifyProtocol(verifyProt
, maxProtocol
, kSSLProtocol2
,
1596 /* note we do this regardless since the client state might be
1597 * the cause of a failure */
1598 ourRtn
+= verifyClientCertState(vfyCertState
, expectCertState
,
1603 printf("Connecting to host %s with kSSLProtocolUnknown...",
1607 pargs
.tryVersion
= kSSLProtocolUnknown
;
1608 pargs
.acceptedProts
= NULL
;
1609 err
= sslPing(&pargs
);
1615 sprintf(fullFileBase
, "%s_def", fileBase
);
1617 showSSLResult(&pargs
,
1620 fileBase
? fullFileBase
: NULL
);
1622 CFReleaseNull(pargs
.peerCerts
);
1624 if(acceptedProts
!= NULL
) {
1625 pargs
.acceptedProts
= acceptedProts
;
1626 pargs
.tryVersion
= kSSLProtocolUnknown
; // not used
1628 printf("Connecting to host %s with acceptedProts %s...",
1629 pargs
.hostName
, pargs
.acceptedProts
);
1632 err
= sslPing(&pargs
);
1638 sprintf(fullFileBase
, "%s_def", fileBase
);
1640 showSSLResult(&pargs
,
1643 fileBase
? fullFileBase
: NULL
);
1645 CFReleaseNull(pargs
.peerCerts
);
1649 /* pause after first, before last to grab trace */
1650 ((loop
== 0) || (loop
== loopCount
- 1))
1655 printf("a to abort, c to continue: ");
1662 if(displayHandshakeTimes
) {
1663 CFAbsoluteTime totalTime
;
1664 unsigned numHandshakes
;
1665 if(pargs
.numHandshakes
== 1) {
1666 /* just display the first one */
1667 totalTime
= pargs
.handshakeTimeFirst
;
1671 /* skip the first one */
1672 totalTime
= pargs
.handshakeTimeTotal
;
1673 numHandshakes
= pargs
.numHandshakes
- 1;
1675 if(numHandshakes
!= 0) {
1676 printf(" %u handshakes in %f seconds; %f seconds per handshake\n",
1677 numHandshakes
, totalTime
,
1678 (totalTime
/ numHandshakes
));
1683 printf("===%s exiting with %d %s for host %s\n", argv
[0], ourRtn
,
1684 (ourRtn
> 1) ? "errors" : "error", pargs
.hostName
);