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/sharedTool/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
35 static const int _maxFileStringSize
= 100;
37 static void usageNorm(char **argv
)
39 printf("Usage: %s [hostname|-] [path] [option ...]\n", argv
[0]);
40 printf(" %s hostname [path] [option ...]\n", argv
[0]);
41 printf("Specifying '-' for hostname, or no args, uses default of %s.\n",
43 printf("Optional path argument must start with leading '/'.\n");
45 printf(" e Allow Expired Certs\n");
46 printf(" E Allow Expired Roots\n");
47 printf(" r Allow any root cert\n");
48 printf(" c Display peer certs\n");
49 printf(" c c Display peer SecTrust\n");
50 printf(" d Display received data\n");
51 printf(" 2 SSLv2 only (default is TLSv1)\n");
52 printf(" 3 SSLv3 only (default is TLSv1)\n");
54 printf(" %% TLSv1.1 only\n");
55 printf(" ^ TLSv1.2 only\n");
56 printf(" L all - TLSv1.2, TLSv1.1, TLSv1.0, SSLv3, SSLv2 (default = TLSv1.2)\n");
57 printf(" g={prot...} Specify legal protocols; prot = any combo of"
59 printf(" k=keychain Contains cert and keys. Optional.\n");
60 printf(" l=loopCount Perform loopCount ops (default = 1)\n");
61 printf(" P=port Default = %d\n", DEFAULT_PORT
);
62 printf(" p Pause after each loop\n");
63 printf(" q Quiet/diagnostic mode (site names and errors only)\n");
64 printf(" a fileName Add fileName to list of trusted roots\n");
65 printf(" A fileName fileName is ONLY trusted root\n");
66 printf(" x Disable Cert Verification\n");
67 printf(" Z string ALPN setting\n");
68 printf(" z=password Unlock client keychain with password.\n");
69 printf(" 8 Complete cert chains (default is out cert is a root)\n");
70 printf(" s Silent\n");
71 printf(" V Verbose\n");
73 printf(" hv More, verbose help\n");
76 static void usageVerbose(char **argv
) __attribute__((noreturn
));
77 static void usageVerbose(char **argv
)
80 printf("Obscure Usage:\n");
81 printf(" u kSSLProtocolUnknown only (TLSv1)\n");
82 printf(" M Manual cert verification via "
83 "SecTrustEvaluate\n");
84 printf(" f fileBase Write Peer Certs to fileBase*\n");
85 printf(" o TLSv1, SSLv3 use kSSLProtocol__X__Only\n");
86 printf(" C=cipherSuite (e=40-bit d=DES D=40-bit DES 3=3DES 4=RC4 "
88 " 2=RC2 a=AES128 A=AES256 h=DH H=Anon DH r=DHE/RSA s=DH/DSS\n");
89 printf(" y=keychain Encryption-only cert and keys. Optional.\n");
90 printf(" K Keep connected until server disconnects\n");
91 printf(" n Require closure notify message in TLSv1, "
92 "SSLv3 mode (implies K)\n");
93 printf(" R Disable resumable session support\n");
94 printf(" b Non-blocking I/O\n");
95 printf(" v Verify negotiated protocol equals attempted\n");
96 printf(" m=[23t] Max protocol supported as specified; implies "
98 printf(" T=[nrsj] Verify client cert state = "
99 "none/requested/sent/rejected\n");
100 printf(" H allow hostname spoofing\n");
101 printf(" F=vfyHost Verify certs with specified host name\n");
102 printf(" G=getMsg Specify entire GET, POST, etc.\n");
103 printf(" N Log handshake timing\n");
104 printf(" 7 Pause only after first loop\n");
108 static void usage(char **argv
) __attribute__((noreturn
));
109 static void usage(char **argv
)
116 * Arguments to top-level sslPing()
119 SSLProtocol tryVersion
; // only used if acceptedProts NULL
120 // uses SSLSetProtocolVersion
121 char *acceptedProts
; // optional, any combo of {2,3,t}
122 // uses SSLSetProtocolVersionEnabled
123 const char *hostName
; // e.g., "www.amazon.com"
124 const char *vfyHostName
; // use this for cert vfy if non-NULL,
127 const char *getMsg
; // e.g.,
128 // "GET / HTTP/1.0\r\n\r\n"
131 bool allowExpiredRoot
;
132 bool disableCertVerify
;
133 bool manualCertVerify
;
134 bool dumpRxData
; // display server data
135 char cipherRestrict
; // '2', 'd'. etc...; '\0' for
138 bool requireNotify
; // require closure notify
140 bool resumableEnable
;
141 bool allowHostnameSpoof
;
145 CFArrayRef clientCerts
; // optional
146 bool quiet
; // minimal stdout
147 bool silent
; // no stdout
149 SSLProtocol negVersion
; // RETURNED
150 SSLCipherSuite negCipher
; // RETURNED
151 CFArrayRef peerCerts
; // mallocd & RETURNED
152 SecTrustRef peerTrust
; // RETURNED
153 SSLClientCertificateState certState
; // RETURNED
154 char *password
; // optional to open clientCerts
156 Boolean sessionWasResumed
;
157 unsigned char sessionID
[MAX_SESSION_ID_LENGTH
];
158 size_t sessionIDLength
;
159 CFAbsoluteTime handshakeTimeOp
; // time for this op
160 CFAbsoluteTime handshakeTimeFirst
; // time for FIRST op, not averaged
161 CFAbsoluteTime handshakeTimeTotal
; // time for all ops except first
162 unsigned numHandshakes
;
164 CFMutableArrayRef alpnNames
;
165 CFMutableArrayRef policies
;
173 printf("***SIGPIPE***\n");
177 * Manually evaluate session's SecTrustRef.
180 static OSStatus
sslEvaluateTrust(
183 CFArrayRef
*peerCerts
) // fetched and retained
185 OSStatus ortn
= errSecSuccess
;
186 SecTrustRef secTrust
= NULL
;
188 ortn
= SSLGetPeerSecTrust(ctx
, &secTrust
);
190 printf("\n***Error obtaining peer SecTrustRef: %s\n",
191 sslGetSSLErrString(ortn
));
194 if(secTrust
== NULL
) {
195 /* this is the normal case for resumed sessions, in which
196 * no cert evaluation is performed */
198 printf("...No SecTrust available - this is a resumed session, right?\n");
200 return errSecSuccess
;
204 if (pargs
->policies
) {
205 SecTrustSetPolicies(secTrust
, pargs
->policies
);
208 SecTrustResultType secTrustResult
;
209 ortn
= SecTrustGetTrustResult(secTrust
, &secTrustResult
); // implicitly does trust evaluate
211 printf("\n***Error on SecTrustEvaluate: %d\n", (int)ortn
);
215 const char *res
= NULL
;
216 switch(secTrustResult
) {
217 case kSecTrustResultInvalid
:
218 res
= "kSecTrustResultInvalid"; break;
219 case kSecTrustResultProceed
:
220 res
= "kSecTrustResultProceed"; break;
221 case kSecTrustResultDeny
:
222 res
= "kSecTrustResultDeny"; break;
223 case kSecTrustResultUnspecified
:
224 res
= "kSecTrustResultUnspecified"; break;
225 case kSecTrustResultRecoverableTrustFailure
:
226 res
= "kSecTrustResultRecoverableTrustFailure"; break;
227 case kSecTrustResultFatalTrustFailure
:
228 res
= "kSecTrustResultFatalTrustFailure"; break;
229 case kSecTrustResultOtherError
:
230 res
= "kSecTrustResultOtherError"; break;
232 res
= "UNKNOWN"; break;
234 printf("\nSecTrustEvaluate(): secTrustResult %s\n", res
);
237 switch(secTrustResult
) {
238 case kSecTrustResultUnspecified
:
239 /* cert chain valid, no special UserTrust assignments */
240 case kSecTrustResultProceed
:
241 /* cert chain valid AND user explicitly trusts this */
244 printf("\n***SecTrustEvaluate reported secTrustResult %d\n",
245 (int)secTrustResult
);
246 ortn
= errSSLXCertChainInvalid
;
252 #ifdef USE_CDSA_CRYPTO
253 /* one more thing - get peer certs in the form of an evidence chain */
254 CSSM_TP_APPLE_EVIDENCE_INFO
*dummyEv
;
255 OSStatus thisRtn
= SecTrustGetResult(secTrust
, &secTrustResult
,
256 peerCerts
, &dummyEv
);
258 printSslErrStr("SecTrustGetResult", thisRtn
);
261 /* workaround for the fact that SSLGetPeerCertificates()
262 * leaves a retain count on each element in the returned array,
263 * requiring us to do a release on each cert.
265 CFIndex numCerts
= CFArrayGetCount(*peerCerts
);
266 for(CFIndex dex
=0; dex
<numCerts
; dex
++) {
267 CFRetain(CFArrayGetValueAtIndex(*peerCerts
, dex
));
274 /* print reply received from server, safely */
275 static void dumpAscii(
279 char *cp
= (char *)rcvBuf
;
283 for(i
=0; i
<len
; i
++) {
296 if(isprint(c
) && (c
!= '\n')) {
300 printf("<%02X>", ((unsigned)c
) & 0xff);
310 alpnFunc(SSLContextRef ctx
,
312 const void *alpnData
,
313 size_t alpnDataLength
)
315 printf("[selected ALPN]");
318 #pragma clang diagnostic push
319 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
322 * Perform one SSL diagnostic session. Returns nonzero on error. Normally no
323 * output to stdout except initial "connecting to" message, unless there
324 * is a really screwed up error (i.e., something not directly related
325 * to the SSL connection).
327 #define RCV_BUF_SIZE 256
329 static OSStatus
sslPing(
335 SSLContextRef ctx
= NULL
;
338 uint8_t rcvBuf
[RCV_BUF_SIZE
];
339 CFAbsoluteTime startHandshake
;
340 CFAbsoluteTime endHandshake
;
342 pargs
->negVersion
= kSSLProtocolUnknown
;
343 pargs
->negCipher
= SSL_NULL_WITH_NULL_NULL
;
344 pargs
->peerCerts
= NULL
;
346 /* first make sure requested server is there */
347 ortn
= MakeServerConnection(pargs
->hostName
, pargs
->port
, pargs
->nonBlocking
,
350 printf("MakeServerConnection returned %d; aborting\n", (int)ortn
);
354 printf("...connected to server; starting SecureTransport\n");
358 * Set up a SecureTransport session.
359 * First the standard calls.
361 ctx
= SSLCreateContext(kCFAllocatorDefault
, kSSLClientSide
, kSSLStreamType
);
363 printf("SSLCreateContext\n");
366 ortn
= SSLSetIOFuncs(ctx
, SocketRead
, SocketWrite
);
368 printSslErrStr("SSLSetIOFuncs", ortn
);
371 ortn
= SSLSetConnection(ctx
, (SSLConnectionRef
)(intptr_t)sock
);
373 printSslErrStr("SSLSetConnection", ortn
);
376 SSLConnectionRef getConn
;
377 ortn
= SSLGetConnection(ctx
, &getConn
);
379 printSslErrStr("SSLGetConnection", ortn
);
382 if(getConn
!= (SSLConnectionRef
)(intptr_t)sock
) {
383 printf("***SSLGetConnection error\n");
387 if(!pargs
->allowHostnameSpoof
) {
388 /* if this isn't set, it isn't checked by AppleX509TP */
389 const char *vfyHost
= pargs
->hostName
;
390 if(pargs
->vfyHostName
) {
391 /* generally means we're expecting an error */
392 vfyHost
= pargs
->vfyHostName
;
394 ortn
= SSLSetPeerDomainName(ctx
, vfyHost
, strlen(vfyHost
));
396 printSslErrStr("SSLSetPeerDomainName", ortn
);
402 * SecureTransport options.
404 if(pargs
->acceptedProts
) {
406 printSslErrStr("SSLSetProtocolVersionEnabled(all off)", ortn
);
409 for(const char *cp
= pargs
->acceptedProts
; *cp
; cp
++) {
412 ortn
= SSLSetProtocolVersionMax(ctx
, kSSLProtocol2
);
415 ortn
= SSLSetProtocolVersionMax(ctx
, kSSLProtocol3
);
418 ortn
= SSLSetProtocolVersionMax(ctx
, kTLSProtocol12
);
424 printSslErrStr("SSLSetProtocolVersionMax", ortn
);
429 ortn
= SSLSetProtocolVersionMax(ctx
, pargs
->tryVersion
);
431 printSslErrStr("SSLSetProtocolVersionMax", ortn
);
436 if(pargs
->resumableEnable
) {
437 const void *rtnId
= NULL
;
440 ortn
= SSLSetPeerID(ctx
, &peerId
, sizeof(PeerSpec
));
442 printSslErrStr("SSLSetPeerID", ortn
);
445 /* quick test of the get fcn */
446 ortn
= SSLGetPeerID(ctx
, &rtnId
, &rtnIdLen
);
448 printSslErrStr("SSLGetPeerID", ortn
);
451 if((rtnId
== NULL
) || (rtnIdLen
!= sizeof(PeerSpec
))) {
452 printf("***SSLGetPeerID screwup\n");
454 else if(memcmp(&peerId
, rtnId
, rtnIdLen
) != 0) {
455 printf("***SSLGetPeerID data mismatch\n");
458 if(pargs
->allowExpired
) {
459 ortn
= SSLSetAllowsExpiredCerts(ctx
, true);
461 printSslErrStr("SSLSetAllowExpiredCerts", ortn
);
465 if(pargs
->allowExpiredRoot
) {
466 ortn
= SSLSetAllowsExpiredRoots(ctx
, true);
468 printSslErrStr("SSLSetAllowsExpiredRoots", ortn
);
472 if(pargs
->disableCertVerify
) {
473 ortn
= SSLSetEnableCertVerify(ctx
, false);
475 printSslErrStr("SSLSetEnableCertVerify", ortn
);
479 if(pargs
->allowAnyRoot
) {
480 ortn
= SSLSetAllowsAnyRoot(ctx
, true);
482 printSslErrStr("SSLSetAllowAnyRoot", ortn
);
486 if(pargs
->cipherRestrict
!= '\0') {
487 ortn
= sslSetCipherRestrictions(ctx
, pargs
->cipherRestrict
);
492 if(pargs
->anchorFile
) {
493 ortn
= sslAddTrustedRoot(ctx
, pargs
->anchorFile
, pargs
->replaceAnchors
);
495 printf("***Error obtaining anchor file %s\n", pargs
->anchorFile
);
499 if(pargs
->clientCerts
) {
501 if(pargs
->anchorFile
== NULL
) {
502 /* assume this is a root we want to implicitly trust */
503 ortn
= addIdentityAsTrustedRoot(ctx
, pargs
->clientCerts
);
508 ortn
= SSLSetCertificate(ctx
, pargs
->clientCerts
);
510 printSslErrStr("SSLSetCertificate", ortn
);
513 /* quickie test for this new function */
514 ortn
= SSLGetCertificate(ctx
, &dummy
);
516 printSslErrStr("SSLGetCertificate", ortn
);
519 if(dummy
!= pargs
->clientCerts
) {
520 printf("***SSLGetCertificate error\n");
525 if (pargs
->alpnNames
) {
526 CFMutableDataRef alpn
= CFDataCreateMutable(NULL
, 0);
528 CFArrayForEach(pargs
->alpnNames
, ^(const void *value
) {
529 CFDataRef data
= (CFDataRef
)value
;
530 uint8_t len
= CFDataGetLength(data
);
531 CFDataAppendBytes(alpn
, (const UInt8
*)&len
, sizeof(len
));
532 CFDataAppend(alpn
, data
);
535 SSLSetALPNData(ctx
, CFDataGetBytePtr(alpn
), CFDataGetLength(alpn
));
536 SSLSetALPNFunc(ctx
, alpnFunc
, (void *)NULL
);
540 /*** end options ***/
543 printf("...starting SSL handshake\n");
545 startHandshake
= CFAbsoluteTimeGetCurrent();
548 { ortn
= SSLHandshake(ctx
);
549 if((ortn
== errSSLWouldBlock
) && !pargs
->silent
) {
550 /* keep UI responsive */
553 } while (ortn
== errSSLWouldBlock
);
555 endHandshake
= CFAbsoluteTimeGetCurrent();
556 pargs
->handshakeTimeOp
= endHandshake
- startHandshake
;
557 if(pargs
->numHandshakes
== 0) {
558 /* special case, this one is always way longer */
559 pargs
->handshakeTimeFirst
= pargs
->handshakeTimeOp
;
562 /* normal running total */
563 pargs
->handshakeTimeTotal
+= pargs
->handshakeTimeOp
;
565 pargs
->numHandshakes
++;
567 ortn
= SSLCopyPeerTrust(ctx
, &pargs
->peerTrust
);
569 printf("***SSLCopyPeerTrust error %" PRIdOSStatus
"\n", ortn
);
570 pargs
->peerTrust
= NULL
;
574 SSLGetClientCertificateState(ctx
, &pargs
->certState
);
575 SSLGetNegotiatedCipher(ctx
, &pargs
->negCipher
);
576 SSLGetNegotiatedProtocolVersion(ctx
, &pargs
->negVersion
);
577 pargs
->sessionIDLength
= MAX_SESSION_ID_LENGTH
;
578 ortn
= SSLGetResumableSessionInfo(ctx
, &pargs
->sessionWasResumed
, pargs
->sessionID
, &pargs
->sessionIDLength
);
580 OSStatus certRtn
= sslEvaluateTrust(ctx
, pargs
, &pargs
->peerCerts
);
582 if (certRtn
&& !pargs
->manualCertVerify
) {
583 SSLCopyPeerCertificates(ctx
, &pargs
->peerCerts
);
587 if(certRtn
&& !ortn
) {
600 printf("...SSL handshake complete\n");
602 length
= strlen(pargs
->getMsg
);
603 (void) SSLWrite(ctx
, pargs
->getMsg
, length
, &actLen
);
606 * Try to snag RCV_BUF_SIZE bytes. Exit if (!keepConnected and we get any data
607 * at all), or (keepConnected and err != (none, wouldBlock)).
611 if(pargs
->dumpRxData
) {
614 ortn
= SSLGetBufferedReadSize(ctx
, &avail
);
616 printf("***SSLGetBufferedReadSize error\n");
620 printf("\n%d bytes available: ", (int)avail
);
623 ortn
= SSLRead(ctx
, rcvBuf
, RCV_BUF_SIZE
, &actLen
);
624 if((actLen
== 0) && !pargs
->silent
) {
627 if((actLen
== 0) && (ortn
== errSecSuccess
)) {
628 printf("***Radar 2984932 confirmed***\n");
630 if (ortn
== errSSLWouldBlock
) {
631 /* for this loop, these are identical */
632 ortn
= errSecSuccess
;
634 if((actLen
> 0) && pargs
->dumpRxData
) {
635 dumpAscii(rcvBuf
, actLen
);
637 if(ortn
!= errSecSuccess
) {
638 /* connection closed by server or by error */
641 if(!pargs
->keepConnected
&& (actLen
> 0)) {
642 /* good enough, we connected */
650 /* snag these again in case of renegotiate */
651 SSLGetClientCertificateState(ctx
, &pargs
->certState
);
652 SSLGetNegotiatedCipher(ctx
, &pargs
->negCipher
);
653 SSLGetNegotiatedProtocolVersion(ctx
, &pargs
->negVersion
);
655 /* convert normal "shutdown" into zero err rtn */
656 if(ortn
== errSSLClosedGraceful
) {
657 ortn
= errSecSuccess
;
659 if((ortn
== errSSLClosedNoNotify
) && !pargs
->requireNotify
) {
660 /* relaxed disconnect rules */
661 ortn
= errSecSuccess
;
665 * always do close, even on error - to flush outgoing write queue
667 OSStatus cerr
= errSecParam
;
669 cerr
= SSLClose(ctx
);
671 if(ortn
== errSecSuccess
) {
675 endpointShutdown(sock
);
685 static void add_key(const void *key
, const void *value
, void *context
) {
686 CFArrayAppendValue((CFMutableArrayRef
)context
, key
);
690 static void showInfo(CFDictionaryRef info
) {
691 CFIndex dict_count
, key_ix
, key_count
;
692 CFMutableArrayRef keys
= NULL
;
693 CFIndex maxWidth
= 20; /* Maybe precompute this or grab from context? */
695 dict_count
= CFDictionaryGetCount(info
);
696 keys
= CFArrayCreateMutable(kCFAllocatorDefault
, dict_count
,
697 &kCFTypeArrayCallBacks
);
698 CFDictionaryApplyFunction(info
, add_key
, keys
);
699 key_count
= CFArrayGetCount(keys
);
700 CFArraySortValues(keys
, CFRangeMake(0, key_count
),
701 (CFComparatorFunction
)CFStringCompare
, 0);
703 for (key_ix
= 0; key_ix
< key_count
; ++key_ix
) {
704 CFStringRef key
= (CFStringRef
)CFArrayGetValueAtIndex(keys
, key_ix
);
705 CFTypeRef value
= CFDictionaryGetValue(info
, key
);
706 CFMutableStringRef line
= CFStringCreateMutable(NULL
, 0);
708 CFStringAppend(line
, key
);
710 for (jx
= CFStringGetLength(key
);
711 jx
< maxWidth
; ++jx
) {
712 CFStringAppend(line
, CFSTR(" "));
714 CFStringAppend(line
, CFSTR(" : "));
715 if (CFStringGetTypeID() == CFGetTypeID(value
)) {
716 CFStringAppend(line
, (CFStringRef
)value
);
717 } else if (CFDateGetTypeID() == CFGetTypeID(value
)) {
718 CFLocaleRef lc
= CFLocaleCopyCurrent();
719 CFDateFormatterRef df
= CFDateFormatterCreate(NULL
, lc
,
720 kCFDateFormatterFullStyle
, kCFDateFormatterFullStyle
);
721 CFDateRef date
= (CFDateRef
)value
;
722 CFStringRef ds
= CFDateFormatterCreateStringWithDate(NULL
, df
,
724 CFStringAppend(line
, ds
);
728 } else if (CFURLGetTypeID() == CFGetTypeID(value
)) {
729 CFURLRef url
= (CFURLRef
)value
;
730 CFStringAppend(line
, CFSTR("<"));
731 CFStringAppend(line
, CFURLGetString(url
));
732 CFStringAppend(line
, CFSTR(">"));
733 } else if (CFDataGetTypeID() == CFGetTypeID(value
)) {
734 CFDataRef v_d
= (CFDataRef
)value
;
735 CFStringRef v_s
= CFStringCreateFromExternalRepresentation(
736 kCFAllocatorDefault
, v_d
, kCFStringEncodingUTF8
);
738 CFStringAppend(line
, CFSTR("/"));
739 CFStringAppend(line
, v_s
);
740 CFStringAppend(line
, CFSTR("/ "));
743 const uint8_t *bytes
= CFDataGetBytePtr(v_d
);
744 CFIndex len
= CFDataGetLength(v_d
);
745 for (jx
= 0; jx
< len
; ++jx
) {
746 CFStringAppendFormat(line
, NULL
, CFSTR("%.02X"), bytes
[jx
]);
749 CFStringAppendFormat(line
, NULL
, CFSTR("%@"), value
);
751 CFStringWriteToFileWithNewline(line
, stdout
);
758 #pragma clang diagnostic pop
760 static void showPeerTrust(SecTrustRef peerTrust
, bool verbose
) {
762 if(peerTrust
== NULL
) {
769 printf("\n=============== Peer Trust Properties ===============\n");
770 CFArrayRef plist
= SecTrustCopyProperties(peerTrust
);
776 printf("\n================== Peer Trust Info ==================\n");
777 CFDictionaryRef info
= SecTrustCopyInfo(peerTrust
);
778 if (info
&& CFDictionaryGetCount(info
)) {
784 numCerts
= SecTrustGetCertificateCount(peerTrust
);
785 for(i
=0; i
<numCerts
; i
++) {
786 plist
= SecTrustCopySummaryPropertiesAtIndex(peerTrust
, i
);
787 printf("\n============= Peer Trust Cert %lu Summary =============\n\n", i
);
791 printf("\n============= Peer Trust Cert %lu Details =============\n\n", i
);
792 plist
= SecTrustCopyDetailedPropertiesAtIndex(peerTrust
, i
);
796 printf("\n============= End of Peer Trust Cert %lu ==============\n", i
);
801 static void showPeerCerts(
802 CFArrayRef __unused peerCerts
,
803 bool __unused verbose
)
807 SecCertificateRef certRef
;
810 if(peerCerts
== NULL
) {
813 numCerts
= CFArrayGetCount(peerCerts
);
814 for(i
=0; i
<numCerts
; i
++) {
815 certRef
= (SecCertificateRef
)CFArrayGetValueAtIndex(peerCerts
, i
);
816 printf("\n==================== Peer Cert %lu ====================\n\n", i
);
817 print_cert(certRef
, verbose
);
818 printf("\n================ End of Peer Cert %lu =================\n", i
);
823 static void writePeerCerts(
824 CFArrayRef peerCerts
,
825 const char *fileBase
)
828 SecCertificateRef certRef
;
830 char fileName
[_maxFileStringSize
];
832 if(peerCerts
== NULL
) {
835 numCerts
= CFArrayGetCount(peerCerts
);
836 for(i
=0; i
<numCerts
; i
++) {
837 snprintf(fileName
, _maxFileStringSize
, "%s%02d.cer", fileBase
, (int)i
);
838 certRef
= (SecCertificateRef
)CFArrayGetValueAtIndex(peerCerts
, i
);
839 CFDataRef derCert
= SecCertificateCopyData(certRef
);
841 writeFileSizet(fileName
, CFDataGetBytePtr(derCert
),
842 CFDataGetLength(derCert
));
846 printf("...wrote %lu certs to fileBase %s\n", numCerts
, fileBase
);
850 * Show result of an sslPing().
851 * Assumes the following from sslPingArgs:
865 static void showSSLResult(
866 const sslPingArgs
*pargs
,
868 int displayPeerCerts
,
869 char *fileBase
) // non-NULL: write certs to file
871 CFIndex numPeerCerts
;
875 if(pargs
->acceptedProts
) {
876 printf(" Allowed SSL versions : %s\n", pargs
->acceptedProts
);
879 printf(" Attempted SSL version : %s\n",
880 sslGetProtocolVersionString(pargs
->tryVersion
));
883 printf(" Result : %s\n", sslGetSSLErrString(err
));
884 printf(" Negotiated SSL version : %s\n",
885 sslGetProtocolVersionString(pargs
->negVersion
));
886 printf(" Negotiated CipherSuite : %s\n",
887 sslGetCipherSuiteString(pargs
->negCipher
));
888 #pragma clang diagnostic push
889 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
890 if(pargs
->certState
!= kSSLClientCertNone
) {
891 printf(" Client Cert State : %s\n",
892 sslGetClientCertStateString(pargs
->certState
));
894 #pragma clang diagnostic pop
896 printf(" Resumed Session : ");
897 if(pargs
->sessionWasResumed
) {
898 for(unsigned dex
=0; dex
<pargs
->sessionIDLength
; dex
++) {
899 printf("%02X ", pargs
->sessionID
[dex
]);
900 if(((dex
% 8) == 7) && (dex
!= (pargs
->sessionIDLength
- 1))) {
907 printf("NOT RESUMED\n");
909 printf(" Handshake time : %f seconds\n", pargs
->handshakeTimeOp
);
911 if(pargs
->peerCerts
== NULL
) {
915 numPeerCerts
= CFArrayGetCount(pargs
->peerCerts
);
917 printf(" Number of server certs : %lu\n", numPeerCerts
);
918 if(numPeerCerts
!= 0) {
919 if (displayPeerCerts
== 1) {
920 showPeerCerts(pargs
->peerCerts
, false);
921 } else if (displayPeerCerts
== 2) {
922 showPeerTrust(pargs
->peerTrust
, false);
924 if(fileBase
!= NULL
) {
925 writePeerCerts(pargs
->peerCerts
, fileBase
);
932 static int verifyProtocol(
934 SSLProtocol maxProtocol
,
935 SSLProtocol reqProtocol
,
936 SSLProtocol negProtocol
)
941 if(reqProtocol
> maxProtocol
) {
942 /* known not to support this attempt, relax */
943 reqProtocol
= maxProtocol
;
945 if(reqProtocol
!= negProtocol
) {
946 printf("***Expected protocol %s; negotiated %s\n",
947 sslGetProtocolVersionString(reqProtocol
),
948 sslGetProtocolVersionString(negProtocol
));
956 static int verifyClientCertState(
957 bool verifyCertState
,
958 SSLClientCertificateState expectState
,
959 SSLClientCertificateState gotState
)
961 if(!verifyCertState
) {
964 if(expectState
== gotState
) {
967 printf("***Expected clientCertState %s; got %s\n",
968 sslGetClientCertStateString(expectState
),
969 sslGetClientCertStateString(gotState
));
973 static SSLProtocol
charToProt(
977 #pragma clang diagnostic push
978 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
981 return kSSLProtocol2
;
983 return kSSLProtocol3
;
985 return kTLSProtocol12
;
989 #pragma clang diagnostic pop
992 int main(int argc
, char **argv
)
998 char fullFileBase
[_maxFileStringSize
];
999 int ourRtn
= 0; // exit status - sum of all errors
1001 SecKeychainRef serverKc
= nil
;
1004 /* user-spec'd parameters */
1005 const char *getPath
= DEFAULT_PATH
;
1006 char *fileBase
= NULL
;
1007 int displayCerts
= 0;
1008 bool doSslV2
= false;
1009 bool doSslV3
= false;
1010 bool doTlsV1
= true;
1011 bool doTlsV11
= false;
1012 bool doTlsV12
= false;
1013 bool protXOnly
= false; // kSSLProtocol3Only, kTLSProtocol1Only
1014 bool doProtUnknown
= false;
1015 unsigned loopCount
= 1;
1016 bool doPause
= false;
1017 bool pauseFirstLoop
= false;
1018 bool verifyProt
= false;
1019 char *acceptedProts
= NULL
;
1020 char *keyChainName
= NULL
;
1021 char *getMsgSpec
= NULL
;
1022 bool vfyCertState
= false;
1023 bool displayHandshakeTimes
= false;
1024 bool completeCertChain
= false;
1025 #pragma clang diagnostic push
1026 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
1027 SSLClientCertificateState expectCertState
= kSSLClientCertNone
;
1028 SSLProtocol maxProtocol
= kTLSProtocol12
; // for verifying negotiated protocol
1029 #pragma clang diagnostic pop
1031 /* special case - one arg of "h" or "-h" or "hv" */
1033 if((strcmp(argv
[1], "h") == 0) || (strcmp(argv
[1], "-h") == 0)) {
1036 if(strcmp(argv
[1], "hv") == 0) {
1041 /* set up defaults */
1042 memset(&pargs
, 0, sizeof(sslPingArgs
));
1043 pargs
.hostName
= DEFAULT_HOST
;
1044 pargs
.port
= DEFAULT_PORT
;
1045 pargs
.resumableEnable
= true;
1048 for(arg
=1; arg
<argc
; arg
++) {
1051 /* first arg, is always hostname; '-' means default */
1052 if(argp
[0] != '-') {
1053 pargs
.hostName
= argp
;
1057 if(argp
[0] == '/') {
1058 /* path always starts with leading slash */
1066 /* requires another arg */
1069 if (pargs
.alpnNames
== NULL
) {
1070 pargs
.alpnNames
= CFArrayCreateMutableForCFTypes(NULL
);
1073 CFDataRef alpn
= CFDataCreate(NULL
, (const UInt8
*)argv
[arg
], strlen(argv
[arg
]));
1074 CFArrayAppendValue(pargs
.alpnNames
, alpn
);
1075 CFReleaseNull(alpn
);
1080 CFDictionaryRef context
= NULL
;
1083 /* requires another arg */
1087 if (argp
[0] == 'W') {
1088 context
= CFDictionaryCreateForCFTypes(NULL
,
1089 CFSTR("AppleServerAuthenticationAllowUATAPN"), kCFBooleanTrue
,
1090 CFSTR("AppleServerAuthenticationAllowUATIDS"), kCFBooleanTrue
,
1091 CFSTR("AppleServerAuthenticationAllowUATGS"), kCFBooleanTrue
,
1094 const char *verifyName
= pargs
.hostName
;
1096 if (pargs
.policies
== NULL
) {
1097 pargs
.policies
= CFArrayCreateMutableForCFTypes(NULL
);
1100 if (pargs
.vfyHostName
)
1101 verifyName
= pargs
.vfyHostName
;
1103 SecPolicyRef policy
= NULL
;
1104 CFStringRef hostname
= CFStringCreateWithCString(NULL
, verifyName
, kCFStringEncodingUTF8
);
1106 if (strcasecmp(argv
[arg
], "PushLegacy") == 0) {
1107 policy
= SecPolicyCreateApplePushServiceLegacy(hostname
);
1108 } else if (strcasecmp(argv
[arg
], "Push") == 0) {
1109 policy
= SecPolicyCreateApplePushService(hostname
, context
);
1110 } else if (strcasecmp(argv
[arg
], "IDS") == 0) {
1111 policy
= SecPolicyCreateAppleIDSServiceContext(hostname
, context
);
1112 } else if (strcasecmp(argv
[arg
], "GS") == 0) {
1113 policy
= SecPolicyCreateAppleGSService(hostname
, context
);
1115 printf("unknown policy: %s", argv
[arg
]);
1116 CFReleaseNull(hostname
);
1117 CFReleaseNull(context
);
1122 CFArrayAppendValue(pargs
.policies
, policy
);
1125 CFReleaseNull(policy
);
1126 CFReleaseNull(hostname
);
1127 CFReleaseNull(context
);
1132 pargs
.allowExpired
= true;
1135 pargs
.allowExpiredRoot
= true;
1138 pargs
.disableCertVerify
= true;
1141 pargs
.disableCertVerify
= true; // implied
1142 pargs
.manualCertVerify
= true;
1146 /* requires another arg */
1149 pargs
.anchorFile
= argv
[arg
];
1153 /* requires another arg */
1156 pargs
.anchorFile
= argv
[arg
];
1157 pargs
.replaceAnchors
= true;
1160 pargs
.allowAnyRoot
= true;
1163 pargs
.dumpRxData
= true;
1170 /* requires another arg */
1173 fileBase
= argv
[arg
];
1176 pargs
.cipherRestrict
= argp
[2];
1179 doSslV3
= doTlsV1
= doTlsV11
= false;
1183 doSslV2
= doTlsV1
= doTlsV11
= doTlsV12
= false;
1187 doSslV2
= doSslV3
= doTlsV11
= doTlsV12
= false;
1191 doSslV2
= doSslV3
= doTlsV1
= false;
1195 doSslV2
= doSslV3
= doTlsV1
= doTlsV12
= false;
1199 doSslV2
= doSslV3
= doTlsV1
= doTlsV11
= doTlsV12
= true;
1205 doSslV2
= doSslV3
= doTlsV1
= doTlsV11
= doTlsV12
= false;
1206 doProtUnknown
= true;
1209 pargs
.keepConnected
= true;
1212 pargs
.requireNotify
= true;
1213 pargs
.keepConnected
= true;
1216 pargs
.resumableEnable
= false;
1219 pargs
.nonBlocking
= true;
1225 if(argp
[1] != '=') {
1228 verifyProt
= true; // implied
1229 maxProtocol
= charToProt(argp
[2], argv
);
1232 if(argp
[1] != '=') {
1235 acceptedProts
= argv
[arg
];
1236 doSslV3
= doSslV2
= doTlsV1
= doTlsV11
= doTlsV12
= false;
1240 /* requires another arg */
1243 int parsedLoopCount
= atoi(argv
[arg
]);
1244 if (parsedLoopCount
<= 0) {
1245 printf("***bad loopCount\n");
1248 loopCount
= (unsigned) parsedLoopCount
;
1252 /* requires another arg */
1255 pargs
.port
= atoi(argv
[arg
]);
1258 pargs
.allowHostnameSpoof
= true;
1262 /* requires another arg */
1265 pargs
.vfyHostName
= argv
[arg
];
1269 /* requires another arg */
1272 keyChainName
= &argp
[2];
1275 getMsgSpec
= &argp
[2];
1278 if(argp
[1] != '=') {
1281 vfyCertState
= true;
1282 #pragma clang diagnostic push
1283 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
1286 expectCertState
= kSSLClientCertNone
;
1289 expectCertState
= kSSLClientCertRequested
;
1292 expectCertState
= kSSLClientCertSent
;
1295 expectCertState
= kSSLClientCertRejected
;
1300 #pragma clang diagnostic pop
1303 pargs
.password
= &argp
[2];
1309 pauseFirstLoop
= true;
1315 pargs
.verbose
= true;
1318 pargs
.silent
= pargs
.quiet
= true;
1321 displayHandshakeTimes
= true;
1324 completeCertChain
= true;
1327 if(pargs
.verbose
|| (argp
[1] == 'v')) {
1338 pargs
.getMsg
= getMsgSpec
;
1341 sprintf(getMsg
, "%s %s %s",
1342 DEFAULT_GETMSG
, getPath
, DEFAULT_GET_SUFFIX
);
1343 pargs
.getMsg
= getMsg
;
1346 /* get client cert and optional encryption cert as CFArrayRef */
1348 pargs
.clientCerts
= getSslCerts(keyChainName
, false, completeCertChain
,
1349 pargs
.anchorFile
, &serverKc
);
1350 if(pargs
.clientCerts
== nil
) {
1353 #ifdef USE_CDSA_CRYPTO
1354 if(pargs
.password
) {
1355 OSStatus ortn
= SecKeychainUnlock(serverKc
,
1356 strlen(pargs
.password
), pargs
.password
, true);
1358 printf("SecKeychainUnlock returned %d\n", (int)ortn
);
1366 struct sigaction sa
;
1367 memset(&sa
, 0, sizeof(sa
));
1368 sa
.sa_flags
= SA_RESTART
;
1369 sa
.sa_handler
= sigpipe
;
1370 sigaction(SIGPIPE
, &sa
, NULL
);
1373 #pragma clang diagnostic push
1374 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
1375 for(loop
=0; loop
<loopCount
; loop
++) {
1377 * One pass for each protocol version, skipping any explicit version if
1378 * an attempt at a higher version and succeeded in doing so successfully fell
1382 pargs
.tryVersion
= kTLSProtocol12
;
1383 pargs
.acceptedProts
= NULL
;
1385 printf("Connecting to host %s with TLS V1.2...", pargs
.hostName
);
1388 err
= sslPing(&pargs
);
1394 snprintf(fullFileBase
, _maxFileStringSize
, "%s_v3.1", fileBase
);
1396 showSSLResult(&pargs
,
1399 fileBase
? fullFileBase
: NULL
);
1401 CFReleaseNull(pargs
.peerCerts
);
1403 /* deal with fallbacks, skipping redundant tests */
1404 switch(pargs
.negVersion
) {
1405 case kTLSProtocol11
:
1426 ourRtn
+= verifyProtocol(verifyProt
, maxProtocol
, kTLSProtocol12
,
1429 /* note we do this regardless since the client state might be
1430 * the cause of a failure */
1431 ourRtn
+= verifyClientCertState(vfyCertState
, expectCertState
,
1435 pargs
.tryVersion
= kTLSProtocol11
;
1436 pargs
.acceptedProts
= NULL
;
1438 printf("Connecting to host %s with TLS V1.1...", pargs
.hostName
);
1441 err
= sslPing(&pargs
);
1447 snprintf(fullFileBase
, _maxFileStringSize
, "%s_v3.1", fileBase
);
1449 showSSLResult(&pargs
,
1452 fileBase
? fullFileBase
: NULL
);
1454 CFReleaseNull(pargs
.peerCerts
);
1456 /* deal with fallbacks, skipping redundant tests */
1457 switch(pargs
.negVersion
) {
1473 ourRtn
+= verifyProtocol(verifyProt
, maxProtocol
, kTLSProtocol11
,
1476 /* note we do this regardless since the client state might be
1477 * the cause of a failure */
1478 ourRtn
+= verifyClientCertState(vfyCertState
, expectCertState
,
1483 protXOnly
? kTLSProtocol1Only
: kTLSProtocol1
;
1484 pargs
.acceptedProts
= NULL
;
1486 printf("Connecting to host %s with TLS V1...", pargs
.hostName
);
1489 err
= sslPing(&pargs
);
1495 snprintf(fullFileBase
, _maxFileStringSize
, "%s_v3.1", fileBase
);
1497 showSSLResult(&pargs
,
1500 fileBase
? fullFileBase
: NULL
);
1502 CFReleaseNull(pargs
.peerCerts
);
1504 /* deal with fallbacks, skipping redundant tests */
1505 switch(pargs
.negVersion
) {
1516 ourRtn
+= verifyProtocol(verifyProt
, maxProtocol
, kTLSProtocol1
,
1519 /* note we do this regardless since the client state might be
1520 * the cause of a failure */
1521 ourRtn
+= verifyClientCertState(vfyCertState
, expectCertState
,
1525 pargs
.tryVersion
= protXOnly
? kSSLProtocol3Only
: kSSLProtocol3
;
1526 pargs
.acceptedProts
= NULL
;
1528 printf("Connecting to host %s with SSL V3...", pargs
.hostName
);
1531 err
= sslPing(&pargs
);
1537 snprintf(fullFileBase
, _maxFileStringSize
, "%s_v3.0", fileBase
);
1539 showSSLResult(&pargs
,
1542 fileBase
? fullFileBase
: NULL
);
1544 CFReleaseNull(pargs
.peerCerts
);
1546 /* deal with fallbacks, skipping redundant tests */
1547 switch(pargs
.negVersion
) {
1554 ourRtn
+= verifyProtocol(verifyProt
, maxProtocol
, kSSLProtocol3
,
1557 /* note we do this regardless since the client state might be
1558 * the cause of a failure */
1559 ourRtn
+= verifyClientCertState(vfyCertState
, expectCertState
,
1565 snprintf(fullFileBase
, _maxFileStringSize
, "%s_v2", fileBase
);
1568 printf("Connecting to host %s with SSL V2...", pargs
.hostName
);
1571 pargs
.tryVersion
= kSSLProtocol2
;
1572 pargs
.acceptedProts
= NULL
;
1573 err
= sslPing(&pargs
);
1579 snprintf(fullFileBase
, _maxFileStringSize
, "%s_v2", fileBase
);
1581 showSSLResult(&pargs
,
1584 fileBase
? fullFileBase
: NULL
);
1586 CFReleaseNull(pargs
.peerCerts
);
1588 ourRtn
+= verifyProtocol(verifyProt
, maxProtocol
, kSSLProtocol2
,
1591 /* note we do this regardless since the client state might be
1592 * the cause of a failure */
1593 ourRtn
+= verifyClientCertState(vfyCertState
, expectCertState
,
1598 printf("Connecting to host %s with kSSLProtocolUnknown...",
1602 pargs
.tryVersion
= kSSLProtocolUnknown
;
1603 pargs
.acceptedProts
= NULL
;
1604 err
= sslPing(&pargs
);
1610 snprintf(fullFileBase
, _maxFileStringSize
, "%s_def", fileBase
);
1612 showSSLResult(&pargs
,
1615 fileBase
? fullFileBase
: NULL
);
1617 CFReleaseNull(pargs
.peerCerts
);
1619 if(acceptedProts
!= NULL
) {
1620 pargs
.acceptedProts
= acceptedProts
;
1621 pargs
.tryVersion
= kSSLProtocolUnknown
; // not used
1623 printf("Connecting to host %s with acceptedProts %s...",
1624 pargs
.hostName
, pargs
.acceptedProts
);
1627 err
= sslPing(&pargs
);
1633 snprintf(fullFileBase
, _maxFileStringSize
, "%s_def", fileBase
);
1635 showSSLResult(&pargs
,
1638 fileBase
? fullFileBase
: NULL
);
1640 CFReleaseNull(pargs
.peerCerts
);
1644 /* pause after first, before last to grab trace */
1645 ((loop
== 0) || (loop
== loopCount
- 1))
1650 printf("a to abort, c to continue: ");
1651 resp
= (char) getchar();
1657 #pragma clang diagnostic pop
1659 if(displayHandshakeTimes
) {
1660 CFAbsoluteTime totalTime
;
1661 unsigned numHandshakes
;
1662 if(pargs
.numHandshakes
== 1) {
1663 /* just display the first one */
1664 totalTime
= pargs
.handshakeTimeFirst
;
1668 /* skip the first one */
1669 totalTime
= pargs
.handshakeTimeTotal
;
1670 numHandshakes
= pargs
.numHandshakes
- 1;
1672 if(numHandshakes
!= 0) {
1673 printf(" %u handshakes in %f seconds; %f seconds per handshake\n",
1674 numHandshakes
, totalTime
,
1675 (totalTime
/ numHandshakes
));
1680 printf("===%s exiting with %d %s for host %s\n", argv
[0], ourRtn
,
1681 (ourRtn
> 1) ? "errors" : "error", pargs
.hostName
);