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
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
= SecTrustEvaluate(secTrust
, &secTrustResult
);
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 SSLSetProtocolVersionMax(ctx
, pargs
->tryVersion
);
432 if(pargs
->resumableEnable
) {
433 const void *rtnId
= NULL
;
436 ortn
= SSLSetPeerID(ctx
, &peerId
, sizeof(PeerSpec
));
438 printSslErrStr("SSLSetPeerID", ortn
);
441 /* quick test of the get fcn */
442 ortn
= SSLGetPeerID(ctx
, &rtnId
, &rtnIdLen
);
444 printSslErrStr("SSLGetPeerID", ortn
);
447 if((rtnId
== NULL
) || (rtnIdLen
!= sizeof(PeerSpec
))) {
448 printf("***SSLGetPeerID screwup\n");
450 else if(memcmp(&peerId
, rtnId
, rtnIdLen
) != 0) {
451 printf("***SSLGetPeerID data mismatch\n");
454 if(pargs
->allowExpired
) {
455 ortn
= SSLSetAllowsExpiredCerts(ctx
, true);
457 printSslErrStr("SSLSetAllowExpiredCerts", ortn
);
461 if(pargs
->allowExpiredRoot
) {
462 ortn
= SSLSetAllowsExpiredRoots(ctx
, true);
464 printSslErrStr("SSLSetAllowsExpiredRoots", ortn
);
468 if(pargs
->disableCertVerify
) {
469 ortn
= SSLSetEnableCertVerify(ctx
, false);
471 printSslErrStr("SSLSetEnableCertVerify", ortn
);
475 if(pargs
->allowAnyRoot
) {
476 ortn
= SSLSetAllowsAnyRoot(ctx
, true);
478 printSslErrStr("SSLSetAllowAnyRoot", ortn
);
482 if(pargs
->cipherRestrict
!= '\0') {
483 ortn
= sslSetCipherRestrictions(ctx
, pargs
->cipherRestrict
);
488 if(pargs
->anchorFile
) {
489 ortn
= sslAddTrustedRoot(ctx
, pargs
->anchorFile
, pargs
->replaceAnchors
);
491 printf("***Error obtaining anchor file %s\n", pargs
->anchorFile
);
495 if(pargs
->clientCerts
) {
497 if(pargs
->anchorFile
== NULL
) {
498 /* assume this is a root we want to implicitly trust */
499 ortn
= addIdentityAsTrustedRoot(ctx
, pargs
->clientCerts
);
504 ortn
= SSLSetCertificate(ctx
, pargs
->clientCerts
);
506 printSslErrStr("SSLSetCertificate", ortn
);
509 /* quickie test for this new function */
510 ortn
= SSLGetCertificate(ctx
, &dummy
);
512 printSslErrStr("SSLGetCertificate", ortn
);
515 if(dummy
!= pargs
->clientCerts
) {
516 printf("***SSLGetCertificate error\n");
521 if (pargs
->alpnNames
) {
522 CFMutableDataRef alpn
= CFDataCreateMutable(NULL
, 0);
524 CFArrayForEach(pargs
->alpnNames
, ^(const void *value
) {
525 CFDataRef data
= (CFDataRef
)value
;
526 uint8_t len
= CFDataGetLength(data
);
527 CFDataAppendBytes(alpn
, (const UInt8
*)&len
, sizeof(len
));
528 CFDataAppend(alpn
, data
);
531 SSLSetALPNData(ctx
, CFDataGetBytePtr(alpn
), CFDataGetLength(alpn
));
532 SSLSetALPNFunc(ctx
, alpnFunc
, (void *)NULL
);
536 /*** end options ***/
539 printf("...starting SSL handshake\n");
541 startHandshake
= CFAbsoluteTimeGetCurrent();
544 { ortn
= SSLHandshake(ctx
);
545 if((ortn
== errSSLWouldBlock
) && !pargs
->silent
) {
546 /* keep UI responsive */
549 } while (ortn
== errSSLWouldBlock
);
551 endHandshake
= CFAbsoluteTimeGetCurrent();
552 pargs
->handshakeTimeOp
= endHandshake
- startHandshake
;
553 if(pargs
->numHandshakes
== 0) {
554 /* special case, this one is always way longer */
555 pargs
->handshakeTimeFirst
= pargs
->handshakeTimeOp
;
558 /* normal running total */
559 pargs
->handshakeTimeTotal
+= pargs
->handshakeTimeOp
;
561 pargs
->numHandshakes
++;
563 ortn
= SSLCopyPeerTrust(ctx
, &pargs
->peerTrust
);
565 printf("***SSLCopyPeerTrust error %" PRIdOSStatus
"\n", ortn
);
566 pargs
->peerTrust
= NULL
;
570 SSLGetClientCertificateState(ctx
, &pargs
->certState
);
571 SSLGetNegotiatedCipher(ctx
, &pargs
->negCipher
);
572 SSLGetNegotiatedProtocolVersion(ctx
, &pargs
->negVersion
);
573 pargs
->sessionIDLength
= MAX_SESSION_ID_LENGTH
;
574 ortn
= SSLGetResumableSessionInfo(ctx
, &pargs
->sessionWasResumed
, pargs
->sessionID
, &pargs
->sessionIDLength
);
576 OSStatus certRtn
= sslEvaluateTrust(ctx
, pargs
, &pargs
->peerCerts
);
578 if (certRtn
&& !pargs
->manualCertVerify
) {
579 SSLCopyPeerCertificates(ctx
, &pargs
->peerCerts
);
583 if(certRtn
&& !ortn
) {
596 printf("...SSL handshake complete\n");
598 length
= strlen(pargs
->getMsg
);
599 (void) SSLWrite(ctx
, pargs
->getMsg
, length
, &actLen
);
602 * Try to snag RCV_BUF_SIZE bytes. Exit if (!keepConnected and we get any data
603 * at all), or (keepConnected and err != (none, wouldBlock)).
607 if(pargs
->dumpRxData
) {
610 ortn
= SSLGetBufferedReadSize(ctx
, &avail
);
612 printf("***SSLGetBufferedReadSize error\n");
616 printf("\n%d bytes available: ", (int)avail
);
619 ortn
= SSLRead(ctx
, rcvBuf
, RCV_BUF_SIZE
, &actLen
);
620 if((actLen
== 0) && !pargs
->silent
) {
623 if((actLen
== 0) && (ortn
== errSecSuccess
)) {
624 printf("***Radar 2984932 confirmed***\n");
626 if (ortn
== errSSLWouldBlock
) {
627 /* for this loop, these are identical */
628 ortn
= errSecSuccess
;
630 if((actLen
> 0) && pargs
->dumpRxData
) {
631 dumpAscii(rcvBuf
, actLen
);
633 if(ortn
!= errSecSuccess
) {
634 /* connection closed by server or by error */
637 if(!pargs
->keepConnected
&& (actLen
> 0)) {
638 /* good enough, we connected */
646 /* snag these again in case of renegotiate */
647 SSLGetClientCertificateState(ctx
, &pargs
->certState
);
648 SSLGetNegotiatedCipher(ctx
, &pargs
->negCipher
);
649 SSLGetNegotiatedProtocolVersion(ctx
, &pargs
->negVersion
);
651 /* convert normal "shutdown" into zero err rtn */
652 if(ortn
== errSSLClosedGraceful
) {
653 ortn
= errSecSuccess
;
655 if((ortn
== errSSLClosedNoNotify
) && !pargs
->requireNotify
) {
656 /* relaxed disconnect rules */
657 ortn
= errSecSuccess
;
661 * always do close, even on error - to flush outgoing write queue
663 OSStatus cerr
= SSLClose(ctx
);
664 if(ortn
== errSecSuccess
) {
668 endpointShutdown(sock
);
678 static void add_key(const void *key
, const void *value
, void *context
) {
679 CFArrayAppendValue((CFMutableArrayRef
)context
, key
);
683 static void showInfo(CFDictionaryRef info
) {
684 CFIndex dict_count
, key_ix
, key_count
;
685 CFMutableArrayRef keys
= NULL
;
686 CFIndex maxWidth
= 20; /* Maybe precompute this or grab from context? */
688 dict_count
= CFDictionaryGetCount(info
);
689 keys
= CFArrayCreateMutable(kCFAllocatorDefault
, dict_count
,
690 &kCFTypeArrayCallBacks
);
691 CFDictionaryApplyFunction(info
, add_key
, keys
);
692 key_count
= CFArrayGetCount(keys
);
693 CFArraySortValues(keys
, CFRangeMake(0, key_count
),
694 (CFComparatorFunction
)CFStringCompare
, 0);
696 for (key_ix
= 0; key_ix
< key_count
; ++key_ix
) {
697 CFStringRef key
= (CFStringRef
)CFArrayGetValueAtIndex(keys
, key_ix
);
698 CFTypeRef value
= CFDictionaryGetValue(info
, key
);
699 CFMutableStringRef line
= CFStringCreateMutable(NULL
, 0);
701 CFStringAppend(line
, key
);
703 for (jx
= CFStringGetLength(key
);
704 jx
< maxWidth
; ++jx
) {
705 CFStringAppend(line
, CFSTR(" "));
707 CFStringAppend(line
, CFSTR(" : "));
708 if (CFStringGetTypeID() == CFGetTypeID(value
)) {
709 CFStringAppend(line
, (CFStringRef
)value
);
710 } else if (CFDateGetTypeID() == CFGetTypeID(value
)) {
711 CFLocaleRef lc
= CFLocaleCopyCurrent();
712 CFDateFormatterRef df
= CFDateFormatterCreate(NULL
, lc
,
713 kCFDateFormatterFullStyle
, kCFDateFormatterFullStyle
);
714 CFDateRef date
= (CFDateRef
)value
;
715 CFStringRef ds
= CFDateFormatterCreateStringWithDate(NULL
, df
,
717 CFStringAppend(line
, ds
);
721 } else if (CFURLGetTypeID() == CFGetTypeID(value
)) {
722 CFURLRef url
= (CFURLRef
)value
;
723 CFStringAppend(line
, CFSTR("<"));
724 CFStringAppend(line
, CFURLGetString(url
));
725 CFStringAppend(line
, CFSTR(">"));
726 } else if (CFDataGetTypeID() == CFGetTypeID(value
)) {
727 CFDataRef v_d
= (CFDataRef
)value
;
728 CFStringRef v_s
= CFStringCreateFromExternalRepresentation(
729 kCFAllocatorDefault
, v_d
, kCFStringEncodingUTF8
);
731 CFStringAppend(line
, CFSTR("/"));
732 CFStringAppend(line
, v_s
);
733 CFStringAppend(line
, CFSTR("/ "));
736 const uint8_t *bytes
= CFDataGetBytePtr(v_d
);
737 CFIndex len
= CFDataGetLength(v_d
);
738 for (jx
= 0; jx
< len
; ++jx
) {
739 CFStringAppendFormat(line
, NULL
, CFSTR("%.02X"), bytes
[jx
]);
742 CFStringAppendFormat(line
, NULL
, CFSTR("%@"), value
);
744 CFStringWriteToFileWithNewline(line
, stdout
);
751 #pragma clang diagnostic pop
753 static void showPeerTrust(SecTrustRef peerTrust
, bool verbose
) {
755 if(peerTrust
== NULL
) {
762 printf("\n=============== Peer Trust Properties ===============\n");
763 CFArrayRef plist
= SecTrustCopyProperties(peerTrust
);
769 printf("\n================== Peer Trust Info ==================\n");
770 CFDictionaryRef info
= SecTrustCopyInfo(peerTrust
);
771 if (info
&& CFDictionaryGetCount(info
)) {
777 numCerts
= SecTrustGetCertificateCount(peerTrust
);
778 for(i
=0; i
<numCerts
; i
++) {
779 plist
= SecTrustCopySummaryPropertiesAtIndex(peerTrust
, i
);
780 printf("\n============= Peer Trust Cert %lu Summary =============\n\n", i
);
784 printf("\n============= Peer Trust Cert %lu Details =============\n\n", i
);
785 plist
= SecTrustCopyDetailedPropertiesAtIndex(peerTrust
, i
);
789 printf("\n============= End of Peer Trust Cert %lu ==============\n", i
);
794 static void showPeerCerts(
795 CFArrayRef __unused peerCerts
,
796 bool __unused verbose
)
800 SecCertificateRef certRef
;
803 if(peerCerts
== NULL
) {
806 numCerts
= CFArrayGetCount(peerCerts
);
807 for(i
=0; i
<numCerts
; i
++) {
808 certRef
= (SecCertificateRef
)CFArrayGetValueAtIndex(peerCerts
, i
);
809 printf("\n==================== Peer Cert %lu ====================\n\n", i
);
810 print_cert(certRef
, verbose
);
811 printf("\n================ End of Peer Cert %lu =================\n", i
);
816 static void writePeerCerts(
817 CFArrayRef peerCerts
,
818 const char *fileBase
)
821 SecCertificateRef certRef
;
823 char fileName
[_maxFileStringSize
];
825 if(peerCerts
== NULL
) {
828 numCerts
= CFArrayGetCount(peerCerts
);
829 for(i
=0; i
<numCerts
; i
++) {
830 snprintf(fileName
, _maxFileStringSize
, "%s%02d.cer", fileBase
, (int)i
);
831 certRef
= (SecCertificateRef
)CFArrayGetValueAtIndex(peerCerts
, i
);
832 CFDataRef derCert
= SecCertificateCopyData(certRef
);
834 writeFileSizet(fileName
, CFDataGetBytePtr(derCert
),
835 CFDataGetLength(derCert
));
839 printf("...wrote %lu certs to fileBase %s\n", numCerts
, fileBase
);
843 * Show result of an sslPing().
844 * Assumes the following from sslPingArgs:
858 static void showSSLResult(
859 const sslPingArgs
*pargs
,
861 int displayPeerCerts
,
862 char *fileBase
) // non-NULL: write certs to file
864 CFIndex numPeerCerts
;
868 if(pargs
->acceptedProts
) {
869 printf(" Allowed SSL versions : %s\n", pargs
->acceptedProts
);
872 printf(" Attempted SSL version : %s\n",
873 sslGetProtocolVersionString(pargs
->tryVersion
));
876 printf(" Result : %s\n", sslGetSSLErrString(err
));
877 printf(" Negotiated SSL version : %s\n",
878 sslGetProtocolVersionString(pargs
->negVersion
));
879 printf(" Negotiated CipherSuite : %s\n",
880 sslGetCipherSuiteString(pargs
->negCipher
));
881 if(pargs
->certState
!= kSSLClientCertNone
) {
882 printf(" Client Cert State : %s\n",
883 sslGetClientCertStateString(pargs
->certState
));
886 printf(" Resumed Session : ");
887 if(pargs
->sessionWasResumed
) {
888 for(unsigned dex
=0; dex
<pargs
->sessionIDLength
; dex
++) {
889 printf("%02X ", pargs
->sessionID
[dex
]);
890 if(((dex
% 8) == 7) && (dex
!= (pargs
->sessionIDLength
- 1))) {
897 printf("NOT RESUMED\n");
899 printf(" Handshake time : %f seconds\n", pargs
->handshakeTimeOp
);
901 if(pargs
->peerCerts
== NULL
) {
905 numPeerCerts
= CFArrayGetCount(pargs
->peerCerts
);
907 printf(" Number of server certs : %lu\n", numPeerCerts
);
908 if(numPeerCerts
!= 0) {
909 if (displayPeerCerts
== 1) {
910 showPeerCerts(pargs
->peerCerts
, false);
911 } else if (displayPeerCerts
== 2) {
912 showPeerTrust(pargs
->peerTrust
, false);
914 if(fileBase
!= NULL
) {
915 writePeerCerts(pargs
->peerCerts
, fileBase
);
922 static int verifyProtocol(
924 SSLProtocol maxProtocol
,
925 SSLProtocol reqProtocol
,
926 SSLProtocol negProtocol
)
931 if(reqProtocol
> maxProtocol
) {
932 /* known not to support this attempt, relax */
933 reqProtocol
= maxProtocol
;
935 if(reqProtocol
!= negProtocol
) {
936 printf("***Expected protocol %s; negotiated %s\n",
937 sslGetProtocolVersionString(reqProtocol
),
938 sslGetProtocolVersionString(negProtocol
));
946 static int verifyClientCertState(
947 bool verifyCertState
,
948 SSLClientCertificateState expectState
,
949 SSLClientCertificateState gotState
)
951 if(!verifyCertState
) {
954 if(expectState
== gotState
) {
957 printf("***Expected clientCertState %s; got %s\n",
958 sslGetClientCertStateString(expectState
),
959 sslGetClientCertStateString(gotState
));
963 static SSLProtocol
charToProt(
969 return kSSLProtocol2
;
971 return kSSLProtocol3
;
973 return kTLSProtocol12
;
979 int main(int argc
, char **argv
)
985 char fullFileBase
[_maxFileStringSize
];
986 int ourRtn
= 0; // exit status - sum of all errors
988 SecKeychainRef serverKc
= nil
;
991 /* user-spec'd parameters */
992 const char *getPath
= DEFAULT_PATH
;
993 char *fileBase
= NULL
;
994 int displayCerts
= 0;
995 bool doSslV2
= false;
996 bool doSslV3
= false;
998 bool doTlsV11
= false;
999 bool doTlsV12
= false;
1000 bool protXOnly
= false; // kSSLProtocol3Only, kTLSProtocol1Only
1001 bool doProtUnknown
= false;
1002 unsigned loopCount
= 1;
1003 bool doPause
= false;
1004 bool pauseFirstLoop
= false;
1005 bool verifyProt
= false;
1006 SSLProtocol maxProtocol
= kTLSProtocol12
; // for verifying negotiated
1008 char *acceptedProts
= NULL
;
1009 char *keyChainName
= NULL
;
1010 char *getMsgSpec
= NULL
;
1011 bool vfyCertState
= false;
1012 SSLClientCertificateState expectCertState
= kSSLClientCertNone
;
1013 bool displayHandshakeTimes
= false;
1014 bool completeCertChain
= false;
1016 /* special case - one arg of "h" or "-h" or "hv" */
1018 if((strcmp(argv
[1], "h") == 0) || (strcmp(argv
[1], "-h") == 0)) {
1021 if(strcmp(argv
[1], "hv") == 0) {
1026 /* set up defaults */
1027 memset(&pargs
, 0, sizeof(sslPingArgs
));
1028 pargs
.hostName
= DEFAULT_HOST
;
1029 pargs
.port
= DEFAULT_PORT
;
1030 pargs
.resumableEnable
= true;
1033 for(arg
=1; arg
<argc
; arg
++) {
1036 /* first arg, is always hostname; '-' means default */
1037 if(argp
[0] != '-') {
1038 pargs
.hostName
= argp
;
1042 if(argp
[0] == '/') {
1043 /* path always starts with leading slash */
1051 /* requires another arg */
1054 if (pargs
.alpnNames
== NULL
) {
1055 pargs
.alpnNames
= CFArrayCreateMutableForCFTypes(NULL
);
1058 CFDataRef alpn
= CFDataCreate(NULL
, (const UInt8
*)argv
[arg
], strlen(argv
[arg
]));
1059 CFArrayAppendValue(pargs
.alpnNames
, alpn
);
1060 CFReleaseNull(alpn
);
1065 CFDictionaryRef context
= NULL
;
1068 /* requires another arg */
1072 if (argp
[0] == 'W') {
1073 context
= CFDictionaryCreateForCFTypes(NULL
,
1074 CFSTR("AppleServerAuthenticationAllowUATAPN"), kCFBooleanTrue
,
1075 CFSTR("AppleServerAuthenticationAllowUATIDS"), kCFBooleanTrue
,
1076 CFSTR("AppleServerAuthenticationAllowUATGS"), kCFBooleanTrue
,
1079 const char *verifyName
= pargs
.hostName
;
1081 if (pargs
.policies
== NULL
) {
1082 pargs
.policies
= CFArrayCreateMutableForCFTypes(NULL
);
1085 if (pargs
.vfyHostName
)
1086 verifyName
= pargs
.vfyHostName
;
1088 SecPolicyRef policy
= NULL
;
1089 CFStringRef hostname
= CFStringCreateWithCString(NULL
, verifyName
, kCFStringEncodingUTF8
);
1091 if (strcasecmp(argv
[arg
], "PushLegacy") == 0) {
1092 policy
= SecPolicyCreateApplePushServiceLegacy(hostname
);
1093 } else if (strcasecmp(argv
[arg
], "Push") == 0) {
1094 policy
= SecPolicyCreateApplePushService(hostname
, context
);
1095 } else if (strcasecmp(argv
[arg
], "IDS") == 0) {
1096 policy
= SecPolicyCreateAppleIDSServiceContext(hostname
, context
);
1097 } else if (strcasecmp(argv
[arg
], "GS") == 0) {
1098 policy
= SecPolicyCreateAppleGSService(hostname
, context
);
1100 printf("unknown policy: %s", argv
[arg
]);
1101 CFReleaseNull(hostname
);
1102 CFReleaseNull(context
);
1107 CFArrayAppendValue(pargs
.policies
, policy
);
1110 CFReleaseNull(policy
);
1111 CFReleaseNull(hostname
);
1112 CFReleaseNull(context
);
1117 pargs
.allowExpired
= true;
1120 pargs
.allowExpiredRoot
= true;
1123 pargs
.disableCertVerify
= true;
1126 pargs
.disableCertVerify
= true; // implied
1127 pargs
.manualCertVerify
= true;
1131 /* requires another arg */
1134 pargs
.anchorFile
= argv
[arg
];
1138 /* requires another arg */
1141 pargs
.anchorFile
= argv
[arg
];
1142 pargs
.replaceAnchors
= true;
1145 pargs
.allowAnyRoot
= true;
1148 pargs
.dumpRxData
= true;
1155 /* requires another arg */
1158 fileBase
= argv
[arg
];
1161 pargs
.cipherRestrict
= argp
[2];
1164 doSslV3
= doTlsV1
= doTlsV11
= false;
1168 doSslV2
= doTlsV1
= doTlsV11
= doTlsV12
= false;
1172 doSslV2
= doSslV3
= doTlsV11
= doTlsV12
= false;
1176 doSslV2
= doSslV3
= doTlsV1
= false;
1180 doSslV2
= doSslV3
= doTlsV1
= doTlsV12
= false;
1184 doSslV2
= doSslV3
= doTlsV1
= doTlsV11
= doTlsV12
= true;
1190 doSslV2
= doSslV3
= doTlsV1
= doTlsV11
= doTlsV12
= false;
1191 doProtUnknown
= true;
1194 pargs
.keepConnected
= true;
1197 pargs
.requireNotify
= true;
1198 pargs
.keepConnected
= true;
1201 pargs
.resumableEnable
= false;
1204 pargs
.nonBlocking
= true;
1210 if(argp
[1] != '=') {
1213 verifyProt
= true; // implied
1214 maxProtocol
= charToProt(argp
[2], argv
);
1217 if(argp
[1] != '=') {
1220 acceptedProts
= argv
[arg
];
1221 doSslV3
= doSslV2
= doTlsV1
= doTlsV11
= doTlsV12
= false;
1225 /* requires another arg */
1228 int parsedLoopCount
= atoi(argv
[arg
]);
1229 if (parsedLoopCount
<= 0) {
1230 printf("***bad loopCount\n");
1233 loopCount
= (unsigned) parsedLoopCount
;
1237 /* requires another arg */
1240 pargs
.port
= atoi(argv
[arg
]);
1243 pargs
.allowHostnameSpoof
= true;
1247 /* requires another arg */
1250 pargs
.vfyHostName
= argv
[arg
];
1254 /* requires another arg */
1257 keyChainName
= &argp
[2];
1260 getMsgSpec
= &argp
[2];
1263 if(argp
[1] != '=') {
1266 vfyCertState
= true;
1269 expectCertState
= kSSLClientCertNone
;
1272 expectCertState
= kSSLClientCertRequested
;
1275 expectCertState
= kSSLClientCertSent
;
1278 expectCertState
= kSSLClientCertRejected
;
1285 pargs
.password
= &argp
[2];
1291 pauseFirstLoop
= true;
1297 pargs
.verbose
= true;
1300 pargs
.silent
= pargs
.quiet
= true;
1303 displayHandshakeTimes
= true;
1306 completeCertChain
= true;
1309 if(pargs
.verbose
|| (argp
[1] == 'v')) {
1320 pargs
.getMsg
= getMsgSpec
;
1323 sprintf(getMsg
, "%s %s %s",
1324 DEFAULT_GETMSG
, getPath
, DEFAULT_GET_SUFFIX
);
1325 pargs
.getMsg
= getMsg
;
1328 /* get client cert and optional encryption cert as CFArrayRef */
1330 pargs
.clientCerts
= getSslCerts(keyChainName
, false, completeCertChain
,
1331 pargs
.anchorFile
, &serverKc
);
1332 if(pargs
.clientCerts
== nil
) {
1335 #ifdef USE_CDSA_CRYPTO
1336 if(pargs
.password
) {
1337 OSStatus ortn
= SecKeychainUnlock(serverKc
,
1338 strlen(pargs
.password
), pargs
.password
, true);
1340 printf("SecKeychainUnlock returned %d\n", (int)ortn
);
1348 struct sigaction sa
;
1349 memset(&sa
, 0, sizeof(sa
));
1350 sa
.sa_flags
= SA_RESTART
;
1351 sa
.sa_handler
= sigpipe
;
1352 sigaction(SIGPIPE
, &sa
, NULL
);
1355 for(loop
=0; loop
<loopCount
; loop
++) {
1357 * One pass for each protocol version, skipping any explicit version if
1358 * an attempt at a higher version and succeeded in doing so successfully fell
1362 pargs
.tryVersion
= kTLSProtocol12
;
1363 pargs
.acceptedProts
= NULL
;
1365 printf("Connecting to host %s with TLS V1.2...", pargs
.hostName
);
1368 err
= sslPing(&pargs
);
1374 snprintf(fullFileBase
, _maxFileStringSize
, "%s_v3.1", fileBase
);
1376 showSSLResult(&pargs
,
1379 fileBase
? fullFileBase
: NULL
);
1381 CFReleaseNull(pargs
.peerCerts
);
1383 /* deal with fallbacks, skipping redundant tests */
1384 switch(pargs
.negVersion
) {
1385 case kTLSProtocol11
:
1406 ourRtn
+= verifyProtocol(verifyProt
, maxProtocol
, kTLSProtocol12
,
1409 /* note we do this regardless since the client state might be
1410 * the cause of a failure */
1411 ourRtn
+= verifyClientCertState(vfyCertState
, expectCertState
,
1415 pargs
.tryVersion
= kTLSProtocol11
;
1416 pargs
.acceptedProts
= NULL
;
1418 printf("Connecting to host %s with TLS V1.1...", pargs
.hostName
);
1421 err
= sslPing(&pargs
);
1427 snprintf(fullFileBase
, _maxFileStringSize
, "%s_v3.1", fileBase
);
1429 showSSLResult(&pargs
,
1432 fileBase
? fullFileBase
: NULL
);
1434 CFReleaseNull(pargs
.peerCerts
);
1436 /* deal with fallbacks, skipping redundant tests */
1437 switch(pargs
.negVersion
) {
1453 ourRtn
+= verifyProtocol(verifyProt
, maxProtocol
, kTLSProtocol11
,
1456 /* note we do this regardless since the client state might be
1457 * the cause of a failure */
1458 ourRtn
+= verifyClientCertState(vfyCertState
, expectCertState
,
1463 protXOnly
? kTLSProtocol1Only
: kTLSProtocol1
;
1464 pargs
.acceptedProts
= NULL
;
1466 printf("Connecting to host %s with TLS V1...", pargs
.hostName
);
1469 err
= sslPing(&pargs
);
1475 snprintf(fullFileBase
, _maxFileStringSize
, "%s_v3.1", fileBase
);
1477 showSSLResult(&pargs
,
1480 fileBase
? fullFileBase
: NULL
);
1482 CFReleaseNull(pargs
.peerCerts
);
1484 /* deal with fallbacks, skipping redundant tests */
1485 switch(pargs
.negVersion
) {
1496 ourRtn
+= verifyProtocol(verifyProt
, maxProtocol
, kTLSProtocol1
,
1499 /* note we do this regardless since the client state might be
1500 * the cause of a failure */
1501 ourRtn
+= verifyClientCertState(vfyCertState
, expectCertState
,
1505 pargs
.tryVersion
= protXOnly
? kSSLProtocol3Only
: kSSLProtocol3
;
1506 pargs
.acceptedProts
= NULL
;
1508 printf("Connecting to host %s with SSL V3...", pargs
.hostName
);
1511 err
= sslPing(&pargs
);
1517 snprintf(fullFileBase
, _maxFileStringSize
, "%s_v3.0", fileBase
);
1519 showSSLResult(&pargs
,
1522 fileBase
? fullFileBase
: NULL
);
1524 CFReleaseNull(pargs
.peerCerts
);
1526 /* deal with fallbacks, skipping redundant tests */
1527 switch(pargs
.negVersion
) {
1534 ourRtn
+= verifyProtocol(verifyProt
, maxProtocol
, kSSLProtocol3
,
1537 /* note we do this regardless since the client state might be
1538 * the cause of a failure */
1539 ourRtn
+= verifyClientCertState(vfyCertState
, expectCertState
,
1545 snprintf(fullFileBase
, _maxFileStringSize
, "%s_v2", fileBase
);
1548 printf("Connecting to host %s with SSL V2...", pargs
.hostName
);
1551 pargs
.tryVersion
= kSSLProtocol2
;
1552 pargs
.acceptedProts
= NULL
;
1553 err
= sslPing(&pargs
);
1559 snprintf(fullFileBase
, _maxFileStringSize
, "%s_v2", fileBase
);
1561 showSSLResult(&pargs
,
1564 fileBase
? fullFileBase
: NULL
);
1566 CFReleaseNull(pargs
.peerCerts
);
1568 ourRtn
+= verifyProtocol(verifyProt
, maxProtocol
, kSSLProtocol2
,
1571 /* note we do this regardless since the client state might be
1572 * the cause of a failure */
1573 ourRtn
+= verifyClientCertState(vfyCertState
, expectCertState
,
1578 printf("Connecting to host %s with kSSLProtocolUnknown...",
1582 pargs
.tryVersion
= kSSLProtocolUnknown
;
1583 pargs
.acceptedProts
= NULL
;
1584 err
= sslPing(&pargs
);
1590 snprintf(fullFileBase
, _maxFileStringSize
, "%s_def", fileBase
);
1592 showSSLResult(&pargs
,
1595 fileBase
? fullFileBase
: NULL
);
1597 CFReleaseNull(pargs
.peerCerts
);
1599 if(acceptedProts
!= NULL
) {
1600 pargs
.acceptedProts
= acceptedProts
;
1601 pargs
.tryVersion
= kSSLProtocolUnknown
; // not used
1603 printf("Connecting to host %s with acceptedProts %s...",
1604 pargs
.hostName
, pargs
.acceptedProts
);
1607 err
= sslPing(&pargs
);
1613 snprintf(fullFileBase
, _maxFileStringSize
, "%s_def", fileBase
);
1615 showSSLResult(&pargs
,
1618 fileBase
? fullFileBase
: NULL
);
1620 CFReleaseNull(pargs
.peerCerts
);
1624 /* pause after first, before last to grab trace */
1625 ((loop
== 0) || (loop
== loopCount
- 1))
1630 printf("a to abort, c to continue: ");
1631 resp
= (char) getchar();
1637 if(displayHandshakeTimes
) {
1638 CFAbsoluteTime totalTime
;
1639 unsigned numHandshakes
;
1640 if(pargs
.numHandshakes
== 1) {
1641 /* just display the first one */
1642 totalTime
= pargs
.handshakeTimeFirst
;
1646 /* skip the first one */
1647 totalTime
= pargs
.handshakeTimeTotal
;
1648 numHandshakes
= pargs
.numHandshakes
- 1;
1650 if(numHandshakes
!= 0) {
1651 printf(" %u handshakes in %f seconds; %f seconds per handshake\n",
1652 numHandshakes
, totalTime
,
1653 (totalTime
/ numHandshakes
));
1658 printf("===%s exiting with %d %s for host %s\n", argv
[0], ourRtn
,
1659 (ourRtn
> 1) ? "errors" : "error", pargs
.hostName
);