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
)) {
785 numCerts
= SecTrustGetCertificateCount(peerTrust
);
786 for(i
=0; i
<numCerts
; i
++) {
787 plist
= SecTrustCopySummaryPropertiesAtIndex(peerTrust
, i
);
788 printf("\n============= Peer Trust Cert %lu Summary =============\n\n", i
);
793 printf("\n============= Peer Trust Cert %lu Details =============\n\n", i
);
794 plist
= SecTrustCopyDetailedPropertiesAtIndex(peerTrust
, i
);
799 printf("\n============= End of Peer Trust Cert %lu ==============\n", i
);
804 static void showPeerCerts(
805 CFArrayRef __unused peerCerts
,
806 bool __unused verbose
)
810 SecCertificateRef certRef
;
813 if(peerCerts
== NULL
) {
816 numCerts
= CFArrayGetCount(peerCerts
);
817 for(i
=0; i
<numCerts
; i
++) {
818 certRef
= (SecCertificateRef
)CFArrayGetValueAtIndex(peerCerts
, i
);
819 printf("\n==================== Peer Cert %lu ====================\n\n", i
);
820 print_cert(certRef
, verbose
);
821 printf("\n================ End of Peer Cert %lu =================\n", i
);
826 static void writePeerCerts(
827 CFArrayRef peerCerts
,
828 const char *fileBase
)
831 SecCertificateRef certRef
;
833 char fileName
[_maxFileStringSize
];
835 if(peerCerts
== NULL
) {
838 numCerts
= CFArrayGetCount(peerCerts
);
839 for(i
=0; i
<numCerts
; i
++) {
840 snprintf(fileName
, _maxFileStringSize
, "%s%02d.cer", fileBase
, (int)i
);
841 certRef
= (SecCertificateRef
)CFArrayGetValueAtIndex(peerCerts
, i
);
842 CFDataRef derCert
= SecCertificateCopyData(certRef
);
844 writeFileSizet(fileName
, CFDataGetBytePtr(derCert
),
845 CFDataGetLength(derCert
));
849 printf("...wrote %lu certs to fileBase %s\n", numCerts
, fileBase
);
853 * Show result of an sslPing().
854 * Assumes the following from sslPingArgs:
868 static void showSSLResult(
869 const sslPingArgs
*pargs
,
871 int displayPeerCerts
,
872 char *fileBase
) // non-NULL: write certs to file
874 CFIndex numPeerCerts
;
878 if(pargs
->acceptedProts
) {
879 printf(" Allowed SSL versions : %s\n", pargs
->acceptedProts
);
882 printf(" Attempted SSL version : %s\n",
883 sslGetProtocolVersionString(pargs
->tryVersion
));
886 printf(" Result : %s\n", sslGetSSLErrString(err
));
887 printf(" Negotiated SSL version : %s\n",
888 sslGetProtocolVersionString(pargs
->negVersion
));
889 printf(" Negotiated CipherSuite : %s\n",
890 sslGetCipherSuiteString(pargs
->negCipher
));
891 #pragma clang diagnostic push
892 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
893 if(pargs
->certState
!= kSSLClientCertNone
) {
894 printf(" Client Cert State : %s\n",
895 sslGetClientCertStateString(pargs
->certState
));
897 #pragma clang diagnostic pop
899 printf(" Resumed Session : ");
900 if(pargs
->sessionWasResumed
) {
901 for(unsigned dex
=0; dex
<pargs
->sessionIDLength
; dex
++) {
902 printf("%02X ", pargs
->sessionID
[dex
]);
903 if(((dex
% 8) == 7) && (dex
!= (pargs
->sessionIDLength
- 1))) {
910 printf("NOT RESUMED\n");
912 printf(" Handshake time : %f seconds\n", pargs
->handshakeTimeOp
);
914 if(pargs
->peerCerts
== NULL
) {
918 numPeerCerts
= CFArrayGetCount(pargs
->peerCerts
);
920 printf(" Number of server certs : %lu\n", numPeerCerts
);
921 if(numPeerCerts
!= 0) {
922 if (displayPeerCerts
== 1) {
923 showPeerCerts(pargs
->peerCerts
, false);
924 } else if (displayPeerCerts
== 2) {
925 showPeerTrust(pargs
->peerTrust
, false);
927 if(fileBase
!= NULL
) {
928 writePeerCerts(pargs
->peerCerts
, fileBase
);
935 static int verifyProtocol(
937 SSLProtocol maxProtocol
,
938 SSLProtocol reqProtocol
,
939 SSLProtocol negProtocol
)
944 if(reqProtocol
> maxProtocol
) {
945 /* known not to support this attempt, relax */
946 reqProtocol
= maxProtocol
;
948 if(reqProtocol
!= negProtocol
) {
949 printf("***Expected protocol %s; negotiated %s\n",
950 sslGetProtocolVersionString(reqProtocol
),
951 sslGetProtocolVersionString(negProtocol
));
959 static int verifyClientCertState(
960 bool verifyCertState
,
961 SSLClientCertificateState expectState
,
962 SSLClientCertificateState gotState
)
964 if(!verifyCertState
) {
967 if(expectState
== gotState
) {
970 printf("***Expected clientCertState %s; got %s\n",
971 sslGetClientCertStateString(expectState
),
972 sslGetClientCertStateString(gotState
));
976 static SSLProtocol
charToProt(
980 #pragma clang diagnostic push
981 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
984 return kSSLProtocol2
;
986 return kSSLProtocol3
;
988 return kTLSProtocol12
;
992 #pragma clang diagnostic pop
995 int main(int argc
, char **argv
)
1001 char fullFileBase
[_maxFileStringSize
];
1002 int ourRtn
= 0; // exit status - sum of all errors
1004 SecKeychainRef serverKc
= nil
;
1007 /* user-spec'd parameters */
1008 const char *getPath
= DEFAULT_PATH
;
1009 char *fileBase
= NULL
;
1010 int displayCerts
= 0;
1011 bool doSslV2
= false;
1012 bool doSslV3
= false;
1013 bool doTlsV1
= true;
1014 bool doTlsV11
= false;
1015 bool doTlsV12
= false;
1016 bool protXOnly
= false; // kSSLProtocol3Only, kTLSProtocol1Only
1017 bool doProtUnknown
= false;
1018 unsigned loopCount
= 1;
1019 bool doPause
= false;
1020 bool pauseFirstLoop
= false;
1021 bool verifyProt
= false;
1022 char *acceptedProts
= NULL
;
1023 char *keyChainName
= NULL
;
1024 char *getMsgSpec
= NULL
;
1025 bool vfyCertState
= false;
1026 bool displayHandshakeTimes
= false;
1027 bool completeCertChain
= false;
1028 #pragma clang diagnostic push
1029 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
1030 SSLClientCertificateState expectCertState
= kSSLClientCertNone
;
1031 SSLProtocol maxProtocol
= kTLSProtocol12
; // for verifying negotiated protocol
1032 #pragma clang diagnostic pop
1034 /* special case - one arg of "h" or "-h" or "hv" */
1036 if((strcmp(argv
[1], "h") == 0) || (strcmp(argv
[1], "-h") == 0)) {
1039 if(strcmp(argv
[1], "hv") == 0) {
1044 /* set up defaults */
1045 memset(&pargs
, 0, sizeof(sslPingArgs
));
1046 pargs
.hostName
= DEFAULT_HOST
;
1047 pargs
.port
= DEFAULT_PORT
;
1048 pargs
.resumableEnable
= true;
1051 for(arg
=1; arg
<argc
; arg
++) {
1054 /* first arg, is always hostname; '-' means default */
1055 if(argp
[0] != '-') {
1056 pargs
.hostName
= argp
;
1060 if(argp
[0] == '/') {
1061 /* path always starts with leading slash */
1069 /* requires another arg */
1072 if (pargs
.alpnNames
== NULL
) {
1073 pargs
.alpnNames
= CFArrayCreateMutableForCFTypes(NULL
);
1076 CFDataRef alpn
= CFDataCreate(NULL
, (const UInt8
*)argv
[arg
], strlen(argv
[arg
]));
1077 CFArrayAppendValue(pargs
.alpnNames
, alpn
);
1078 CFReleaseNull(alpn
);
1083 CFDictionaryRef context
= NULL
;
1086 /* requires another arg */
1090 if (argp
[0] == 'W') {
1091 context
= CFDictionaryCreateForCFTypes(NULL
,
1092 CFSTR("AppleServerAuthenticationAllowUATAPN"), kCFBooleanTrue
,
1093 CFSTR("AppleServerAuthenticationAllowUATIDS"), kCFBooleanTrue
,
1094 CFSTR("AppleServerAuthenticationAllowUATGS"), kCFBooleanTrue
,
1097 const char *verifyName
= pargs
.hostName
;
1099 if (pargs
.policies
== NULL
) {
1100 pargs
.policies
= CFArrayCreateMutableForCFTypes(NULL
);
1103 if (pargs
.vfyHostName
)
1104 verifyName
= pargs
.vfyHostName
;
1106 SecPolicyRef policy
= NULL
;
1107 CFStringRef hostname
= CFStringCreateWithCString(NULL
, verifyName
, kCFStringEncodingUTF8
);
1109 if (strcasecmp(argv
[arg
], "PushLegacy") == 0) {
1110 policy
= SecPolicyCreateApplePushServiceLegacy(hostname
);
1111 } else if (strcasecmp(argv
[arg
], "Push") == 0) {
1112 policy
= SecPolicyCreateApplePushService(hostname
, context
);
1113 } else if (strcasecmp(argv
[arg
], "IDS") == 0) {
1114 policy
= SecPolicyCreateAppleIDSServiceContext(hostname
, context
);
1115 } else if (strcasecmp(argv
[arg
], "GS") == 0) {
1116 policy
= SecPolicyCreateAppleGSService(hostname
, context
);
1118 printf("unknown policy: %s", argv
[arg
]);
1119 CFReleaseNull(hostname
);
1120 CFReleaseNull(context
);
1125 CFArrayAppendValue(pargs
.policies
, policy
);
1128 CFReleaseNull(policy
);
1129 CFReleaseNull(hostname
);
1130 CFReleaseNull(context
);
1135 pargs
.allowExpired
= true;
1138 pargs
.allowExpiredRoot
= true;
1141 pargs
.disableCertVerify
= true;
1144 pargs
.disableCertVerify
= true; // implied
1145 pargs
.manualCertVerify
= true;
1149 /* requires another arg */
1152 pargs
.anchorFile
= argv
[arg
];
1156 /* requires another arg */
1159 pargs
.anchorFile
= argv
[arg
];
1160 pargs
.replaceAnchors
= true;
1163 pargs
.allowAnyRoot
= true;
1166 pargs
.dumpRxData
= true;
1173 /* requires another arg */
1176 fileBase
= argv
[arg
];
1179 pargs
.cipherRestrict
= argp
[2];
1182 doSslV3
= doTlsV1
= doTlsV11
= false;
1186 doSslV2
= doTlsV1
= doTlsV11
= doTlsV12
= false;
1190 doSslV2
= doSslV3
= doTlsV11
= doTlsV12
= false;
1194 doSslV2
= doSslV3
= doTlsV1
= false;
1198 doSslV2
= doSslV3
= doTlsV1
= false;
1202 doSslV2
= doSslV3
= doTlsV1
= doTlsV11
= doTlsV12
= true;
1208 doSslV2
= doSslV3
= doTlsV1
= doTlsV11
= doTlsV12
= false;
1209 doProtUnknown
= true;
1212 pargs
.keepConnected
= true;
1215 pargs
.requireNotify
= true;
1216 pargs
.keepConnected
= true;
1219 pargs
.resumableEnable
= false;
1222 pargs
.nonBlocking
= true;
1228 if(argp
[1] != '=') {
1231 verifyProt
= true; // implied
1232 maxProtocol
= charToProt(argp
[2], argv
);
1235 if(argp
[1] != '=') {
1238 acceptedProts
= argv
[arg
];
1239 doSslV3
= doSslV2
= doTlsV1
= doTlsV11
= doTlsV12
= false;
1243 /* requires another arg */
1246 int parsedLoopCount
= atoi(argv
[arg
]);
1247 if (parsedLoopCount
<= 0) {
1248 printf("***bad loopCount\n");
1251 loopCount
= (unsigned) parsedLoopCount
;
1255 /* requires another arg */
1258 pargs
.port
= atoi(argv
[arg
]);
1261 pargs
.allowHostnameSpoof
= true;
1265 /* requires another arg */
1268 pargs
.vfyHostName
= argv
[arg
];
1272 /* requires another arg */
1275 keyChainName
= &argp
[2];
1278 getMsgSpec
= &argp
[2];
1281 if(argp
[1] != '=') {
1284 vfyCertState
= true;
1285 #pragma clang diagnostic push
1286 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
1289 expectCertState
= kSSLClientCertNone
;
1292 expectCertState
= kSSLClientCertRequested
;
1295 expectCertState
= kSSLClientCertSent
;
1298 expectCertState
= kSSLClientCertRejected
;
1303 #pragma clang diagnostic pop
1306 pargs
.password
= &argp
[2];
1312 pauseFirstLoop
= true;
1318 pargs
.verbose
= true;
1321 pargs
.silent
= pargs
.quiet
= true;
1324 displayHandshakeTimes
= true;
1327 completeCertChain
= true;
1330 if(pargs
.verbose
|| (argp
[1] == 'v')) {
1341 pargs
.getMsg
= getMsgSpec
;
1344 sprintf(getMsg
, "%s %s %s",
1345 DEFAULT_GETMSG
, getPath
, DEFAULT_GET_SUFFIX
);
1346 pargs
.getMsg
= getMsg
;
1349 /* get client cert and optional encryption cert as CFArrayRef */
1351 pargs
.clientCerts
= getSslCerts(keyChainName
, false, completeCertChain
,
1352 pargs
.anchorFile
, &serverKc
);
1353 if(pargs
.clientCerts
== nil
) {
1356 #ifdef USE_CDSA_CRYPTO
1357 if(pargs
.password
) {
1358 OSStatus ortn
= SecKeychainUnlock(serverKc
,
1359 strlen(pargs
.password
), pargs
.password
, true);
1361 printf("SecKeychainUnlock returned %d\n", (int)ortn
);
1369 struct sigaction sa
;
1370 memset(&sa
, 0, sizeof(sa
));
1371 sa
.sa_flags
= SA_RESTART
;
1372 sa
.sa_handler
= sigpipe
;
1373 sigaction(SIGPIPE
, &sa
, NULL
);
1376 #pragma clang diagnostic push
1377 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
1378 for(loop
=0; loop
<loopCount
; loop
++) {
1380 * One pass for each protocol version, skipping any explicit version if
1381 * an attempt at a higher version and succeeded in doing so successfully fell
1385 pargs
.tryVersion
= kTLSProtocol12
;
1386 pargs
.acceptedProts
= NULL
;
1388 printf("Connecting to host %s with TLS V1.2...", pargs
.hostName
);
1391 err
= sslPing(&pargs
);
1397 snprintf(fullFileBase
, _maxFileStringSize
, "%s_v3.1", fileBase
);
1399 showSSLResult(&pargs
,
1402 fileBase
? fullFileBase
: NULL
);
1404 CFReleaseNull(pargs
.peerCerts
);
1406 /* deal with fallbacks, skipping redundant tests */
1407 switch(pargs
.negVersion
) {
1408 case kTLSProtocol11
:
1429 ourRtn
+= verifyProtocol(verifyProt
, maxProtocol
, kTLSProtocol12
,
1432 /* note we do this regardless since the client state might be
1433 * the cause of a failure */
1434 ourRtn
+= verifyClientCertState(vfyCertState
, expectCertState
,
1438 pargs
.tryVersion
= kTLSProtocol11
;
1439 pargs
.acceptedProts
= NULL
;
1441 printf("Connecting to host %s with TLS V1.1...", pargs
.hostName
);
1444 err
= sslPing(&pargs
);
1450 snprintf(fullFileBase
, _maxFileStringSize
, "%s_v3.1", fileBase
);
1452 showSSLResult(&pargs
,
1455 fileBase
? fullFileBase
: NULL
);
1457 CFReleaseNull(pargs
.peerCerts
);
1459 /* deal with fallbacks, skipping redundant tests */
1460 switch(pargs
.negVersion
) {
1476 ourRtn
+= verifyProtocol(verifyProt
, maxProtocol
, kTLSProtocol11
,
1479 /* note we do this regardless since the client state might be
1480 * the cause of a failure */
1481 ourRtn
+= verifyClientCertState(vfyCertState
, expectCertState
,
1486 protXOnly
? kTLSProtocol1Only
: kTLSProtocol1
;
1487 pargs
.acceptedProts
= NULL
;
1489 printf("Connecting to host %s with TLS V1...", pargs
.hostName
);
1492 err
= sslPing(&pargs
);
1498 snprintf(fullFileBase
, _maxFileStringSize
, "%s_v3.1", fileBase
);
1500 showSSLResult(&pargs
,
1503 fileBase
? fullFileBase
: NULL
);
1505 CFReleaseNull(pargs
.peerCerts
);
1507 /* deal with fallbacks, skipping redundant tests */
1508 switch(pargs
.negVersion
) {
1519 ourRtn
+= verifyProtocol(verifyProt
, maxProtocol
, kTLSProtocol1
,
1522 /* note we do this regardless since the client state might be
1523 * the cause of a failure */
1524 ourRtn
+= verifyClientCertState(vfyCertState
, expectCertState
,
1528 pargs
.tryVersion
= protXOnly
? kSSLProtocol3Only
: kSSLProtocol3
;
1529 pargs
.acceptedProts
= NULL
;
1531 printf("Connecting to host %s with SSL V3...", pargs
.hostName
);
1534 err
= sslPing(&pargs
);
1540 snprintf(fullFileBase
, _maxFileStringSize
, "%s_v3.0", fileBase
);
1542 showSSLResult(&pargs
,
1545 fileBase
? fullFileBase
: NULL
);
1547 CFReleaseNull(pargs
.peerCerts
);
1549 /* deal with fallbacks, skipping redundant tests */
1550 switch(pargs
.negVersion
) {
1557 ourRtn
+= verifyProtocol(verifyProt
, maxProtocol
, kSSLProtocol3
,
1560 /* note we do this regardless since the client state might be
1561 * the cause of a failure */
1562 ourRtn
+= verifyClientCertState(vfyCertState
, expectCertState
,
1568 snprintf(fullFileBase
, _maxFileStringSize
, "%s_v2", fileBase
);
1571 printf("Connecting to host %s with SSL V2...", pargs
.hostName
);
1574 pargs
.tryVersion
= kSSLProtocol2
;
1575 pargs
.acceptedProts
= NULL
;
1576 err
= sslPing(&pargs
);
1582 snprintf(fullFileBase
, _maxFileStringSize
, "%s_v2", fileBase
);
1584 showSSLResult(&pargs
,
1587 fileBase
? fullFileBase
: NULL
);
1589 CFReleaseNull(pargs
.peerCerts
);
1591 ourRtn
+= verifyProtocol(verifyProt
, maxProtocol
, kSSLProtocol2
,
1594 /* note we do this regardless since the client state might be
1595 * the cause of a failure */
1596 ourRtn
+= verifyClientCertState(vfyCertState
, expectCertState
,
1601 printf("Connecting to host %s with kSSLProtocolUnknown...",
1605 pargs
.tryVersion
= kSSLProtocolUnknown
;
1606 pargs
.acceptedProts
= NULL
;
1607 err
= sslPing(&pargs
);
1613 snprintf(fullFileBase
, _maxFileStringSize
, "%s_def", fileBase
);
1615 showSSLResult(&pargs
,
1618 fileBase
? fullFileBase
: NULL
);
1620 CFReleaseNull(pargs
.peerCerts
);
1622 if(acceptedProts
!= NULL
) {
1623 pargs
.acceptedProts
= acceptedProts
;
1624 pargs
.tryVersion
= kSSLProtocolUnknown
; // not used
1626 printf("Connecting to host %s with acceptedProts %s...",
1627 pargs
.hostName
, pargs
.acceptedProts
);
1630 err
= sslPing(&pargs
);
1636 snprintf(fullFileBase
, _maxFileStringSize
, "%s_def", fileBase
);
1638 showSSLResult(&pargs
,
1641 fileBase
? fullFileBase
: NULL
);
1643 CFReleaseNull(pargs
.peerCerts
);
1647 /* pause after first, before last to grab trace */
1648 ((loop
== 0) || (loop
== loopCount
- 1))
1653 printf("a to abort, c to continue: ");
1654 resp
= (char) getchar();
1660 #pragma clang diagnostic pop
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
);