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 #pragma clang diagnostic push
222 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
223 case kSecTrustResultConfirm
:
224 #pragma clang diagnostic pop
225 res
= "kSecTrustResultConfirm"; break;
226 case kSecTrustResultDeny
:
227 res
= "kSecTrustResultDeny"; break;
228 case kSecTrustResultUnspecified
:
229 res
= "kSecTrustResultUnspecified"; break;
230 case kSecTrustResultRecoverableTrustFailure
:
231 res
= "kSecTrustResultRecoverableTrustFailure"; break;
232 case kSecTrustResultFatalTrustFailure
:
233 res
= "kSecTrustResultFatalTrustFailure"; break;
234 case kSecTrustResultOtherError
:
235 res
= "kSecTrustResultOtherError"; break;
237 res
= "UNKNOWN"; break;
239 printf("\nSecTrustEvaluate(): secTrustResult %s\n", res
);
242 switch(secTrustResult
) {
243 case kSecTrustResultUnspecified
:
244 /* cert chain valid, no special UserTrust assignments */
245 case kSecTrustResultProceed
:
246 /* cert chain valid AND user explicitly trusts this */
249 printf("\n***SecTrustEvaluate reported secTrustResult %d\n",
250 (int)secTrustResult
);
251 ortn
= errSSLXCertChainInvalid
;
260 /* print reply received from server, safely */
261 static void dumpAscii(
265 char *cp
= (char *)rcvBuf
;
269 for(i
=0; i
<len
; i
++) {
282 if(isprint(c
) && (c
!= '\n')) {
286 printf("<%02X>", ((unsigned)c
) & 0xff);
296 alpnFunc(SSLContextRef ctx
,
298 const void *alpnData
,
299 size_t alpnDataLength
)
301 printf("[selected ALPN]");
304 #pragma clang diagnostic push
305 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
308 * Perform one SSL diagnostic session. Returns nonzero on error. Normally no
309 * output to stdout except initial "connecting to" message, unless there
310 * is a really screwed up error (i.e., something not directly related
311 * to the SSL connection).
313 #define RCV_BUF_SIZE 256
315 static OSStatus
sslPing(
321 SSLContextRef ctx
= NULL
;
324 uint8_t rcvBuf
[RCV_BUF_SIZE
];
325 CFAbsoluteTime startHandshake
;
326 CFAbsoluteTime endHandshake
;
328 pargs
->negVersion
= kSSLProtocolUnknown
;
329 pargs
->negCipher
= SSL_NULL_WITH_NULL_NULL
;
330 pargs
->peerCerts
= NULL
;
332 /* first make sure requested server is there */
333 ortn
= MakeServerConnection(pargs
->hostName
, pargs
->port
, pargs
->nonBlocking
,
336 printf("MakeServerConnection returned %d; aborting\n", (int)ortn
);
340 printf("...connected to server; starting SecureTransport\n");
344 * Set up a SecureTransport session.
345 * First the standard calls.
347 ctx
= SSLCreateContext(kCFAllocatorDefault
, kSSLClientSide
, kSSLStreamType
);
349 printf("SSLCreateContext\n");
352 ortn
= SSLSetIOFuncs(ctx
, SocketRead
, SocketWrite
);
354 printSslErrStr("SSLSetIOFuncs", ortn
);
357 ortn
= SSLSetConnection(ctx
, (SSLConnectionRef
)(intptr_t)sock
);
359 printSslErrStr("SSLSetConnection", ortn
);
362 SSLConnectionRef getConn
;
363 ortn
= SSLGetConnection(ctx
, &getConn
);
365 printSslErrStr("SSLGetConnection", ortn
);
368 if(getConn
!= (SSLConnectionRef
)(intptr_t)sock
) {
369 printf("***SSLGetConnection error\n");
373 if(!pargs
->allowHostnameSpoof
) {
374 /* if this isn't set, it isn't checked by AppleX509TP */
375 const char *vfyHost
= pargs
->hostName
;
376 if(pargs
->vfyHostName
) {
377 /* generally means we're expecting an error */
378 vfyHost
= pargs
->vfyHostName
;
380 ortn
= SSLSetPeerDomainName(ctx
, vfyHost
, strlen(vfyHost
));
382 printSslErrStr("SSLSetPeerDomainName", ortn
);
388 * SecureTransport options.
390 if(pargs
->acceptedProts
) {
391 ortn
= SSLSetProtocolVersionEnabled(ctx
, kSSLProtocolAll
, false);
393 printSslErrStr("SSLSetProtocolVersionEnabled(all off)", ortn
);
396 for(const char *cp
= pargs
->acceptedProts
; *cp
; cp
++) {
400 prot
= kSSLProtocol2
;
403 prot
= kSSLProtocol3
;
406 prot
= kTLSProtocol12
;
411 ortn
= SSLSetProtocolVersionEnabled(ctx
, prot
, true);
413 printSslErrStr("SSLSetProtocolVersionEnabled", ortn
);
419 ortn
= SSLSetProtocolVersion(ctx
, pargs
->tryVersion
);
421 printSslErrStr("SSLSetProtocolVersion", ortn
);
425 ortn
= SSLGetProtocolVersion(ctx
, &getVers
);
427 printSslErrStr("SSLGetProtocolVersion", ortn
);
430 if(getVers
!= pargs
->tryVersion
) {
431 printf("***SSLGetProtocolVersion screwup: try %s get %s\n",
432 sslGetProtocolVersionString(pargs
->tryVersion
),
433 sslGetProtocolVersionString(getVers
));
438 if(pargs
->resumableEnable
) {
439 const void *rtnId
= NULL
;
442 ortn
= SSLSetPeerID(ctx
, &peerId
, sizeof(PeerSpec
));
444 printSslErrStr("SSLSetPeerID", ortn
);
447 /* quick test of the get fcn */
448 ortn
= SSLGetPeerID(ctx
, &rtnId
, &rtnIdLen
);
450 printSslErrStr("SSLGetPeerID", ortn
);
453 if((rtnId
== NULL
) || (rtnIdLen
!= sizeof(PeerSpec
))) {
454 printf("***SSLGetPeerID screwup\n");
456 else if(memcmp(&peerId
, rtnId
, rtnIdLen
) != 0) {
457 printf("***SSLGetPeerID data mismatch\n");
460 if(pargs
->allowExpired
) {
461 ortn
= SSLSetAllowsExpiredCerts(ctx
, true);
463 printSslErrStr("SSLSetAllowExpiredCerts", ortn
);
467 if(pargs
->allowExpiredRoot
) {
468 ortn
= SSLSetAllowsExpiredRoots(ctx
, true);
470 printSslErrStr("SSLSetAllowsExpiredRoots", ortn
);
474 if(pargs
->disableCertVerify
) {
475 ortn
= SSLSetEnableCertVerify(ctx
, false);
477 printSslErrStr("SSLSetEnableCertVerify", ortn
);
481 if(pargs
->allowAnyRoot
) {
482 ortn
= SSLSetAllowsAnyRoot(ctx
, true);
484 printSslErrStr("SSLSetAllowAnyRoot", ortn
);
488 if(pargs
->cipherRestrict
!= '\0') {
489 ortn
= sslSetCipherRestrictions(ctx
, pargs
->cipherRestrict
);
494 if(pargs
->anchorFile
) {
495 ortn
= sslAddTrustedRoot(ctx
, pargs
->anchorFile
, pargs
->replaceAnchors
);
497 printf("***Error obtaining anchor file %s\n", pargs
->anchorFile
);
501 if(pargs
->clientCerts
) {
503 if(pargs
->anchorFile
== NULL
) {
504 /* assume this is a root we want to implicitly trust */
505 ortn
= addIdentityAsTrustedRoot(ctx
, pargs
->clientCerts
);
510 ortn
= SSLSetCertificate(ctx
, pargs
->clientCerts
);
512 printSslErrStr("SSLSetCertificate", ortn
);
515 /* quickie test for this new function */
516 ortn
= SSLGetCertificate(ctx
, &dummy
);
518 printSslErrStr("SSLGetCertificate", ortn
);
521 if(dummy
!= pargs
->clientCerts
) {
522 printf("***SSLGetCertificate error\n");
527 if (pargs
->alpnNames
) {
528 CFMutableDataRef alpn
= CFDataCreateMutable(NULL
, 0);
530 CFArrayForEach(pargs
->alpnNames
, ^(const void *value
) {
531 CFDataRef data
= (CFDataRef
)value
;
532 uint8_t len
= CFDataGetLength(data
);
533 CFDataAppendBytes(alpn
, (const UInt8
*)&len
, sizeof(len
));
534 CFDataAppend(alpn
, data
);
537 SSLSetALPNData(ctx
, CFDataGetBytePtr(alpn
), CFDataGetLength(alpn
));
538 SSLSetALPNFunc(ctx
, alpnFunc
, (void *)NULL
);
542 /*** end options ***/
545 printf("...starting SSL handshake\n");
547 startHandshake
= CFAbsoluteTimeGetCurrent();
550 { ortn
= SSLHandshake(ctx
);
551 if((ortn
== errSSLWouldBlock
) && !pargs
->silent
) {
552 /* keep UI responsive */
555 } while (ortn
== errSSLWouldBlock
);
557 endHandshake
= CFAbsoluteTimeGetCurrent();
558 pargs
->handshakeTimeOp
= endHandshake
- startHandshake
;
559 if(pargs
->numHandshakes
== 0) {
560 /* special case, this one is always way longer */
561 pargs
->handshakeTimeFirst
= pargs
->handshakeTimeOp
;
564 /* normal running total */
565 pargs
->handshakeTimeTotal
+= pargs
->handshakeTimeOp
;
567 pargs
->numHandshakes
++;
569 ortn
= SSLCopyPeerTrust(ctx
, &pargs
->peerTrust
);
571 printf("***SSLCopyPeerTrust error %" PRIdOSStatus
"\n", ortn
);
572 pargs
->peerTrust
= NULL
;
576 SSLGetClientCertificateState(ctx
, &pargs
->certState
);
577 SSLGetNegotiatedCipher(ctx
, &pargs
->negCipher
);
578 SSLGetNegotiatedProtocolVersion(ctx
, &pargs
->negVersion
);
579 pargs
->sessionIDLength
= MAX_SESSION_ID_LENGTH
;
580 ortn
= SSLGetResumableSessionInfo(ctx
, &pargs
->sessionWasResumed
, pargs
->sessionID
, &pargs
->sessionIDLength
);
582 OSStatus certRtn
= sslEvaluateTrust(ctx
, pargs
, &pargs
->peerCerts
);
584 if (certRtn
&& !pargs
->manualCertVerify
) {
585 SSLCopyPeerCertificates(ctx
, &pargs
->peerCerts
);
589 if(certRtn
&& !ortn
) {
602 printf("...SSL handshake complete\n");
604 length
= strlen(pargs
->getMsg
);
605 (void) SSLWrite(ctx
, pargs
->getMsg
, length
, &actLen
);
608 * Try to snag RCV_BUF_SIZE bytes. Exit if (!keepConnected and we get any data
609 * at all), or (keepConnected and err != (none, wouldBlock)).
613 if(pargs
->dumpRxData
) {
616 ortn
= SSLGetBufferedReadSize(ctx
, &avail
);
618 printf("***SSLGetBufferedReadSize error\n");
622 printf("\n%d bytes available: ", (int)avail
);
625 ortn
= SSLRead(ctx
, rcvBuf
, RCV_BUF_SIZE
, &actLen
);
626 if((actLen
== 0) && !pargs
->silent
) {
629 if((actLen
== 0) && (ortn
== errSecSuccess
)) {
630 printf("***Radar 2984932 confirmed***\n");
632 if (ortn
== errSSLWouldBlock
) {
633 /* for this loop, these are identical */
634 ortn
= errSecSuccess
;
636 if((actLen
> 0) && pargs
->dumpRxData
) {
637 dumpAscii(rcvBuf
, actLen
);
639 if(ortn
!= errSecSuccess
) {
640 /* connection closed by server or by error */
643 if(!pargs
->keepConnected
&& (actLen
> 0)) {
644 /* good enough, we connected */
652 /* snag these again in case of renegotiate */
653 SSLGetClientCertificateState(ctx
, &pargs
->certState
);
654 SSLGetNegotiatedCipher(ctx
, &pargs
->negCipher
);
655 SSLGetNegotiatedProtocolVersion(ctx
, &pargs
->negVersion
);
657 /* convert normal "shutdown" into zero err rtn */
658 if(ortn
== errSSLClosedGraceful
) {
659 ortn
= errSecSuccess
;
661 if((ortn
== errSSLClosedNoNotify
) && !pargs
->requireNotify
) {
662 /* relaxed disconnect rules */
663 ortn
= errSecSuccess
;
667 * always do close, even on error - to flush outgoing write queue
669 OSStatus cerr
= SSLClose(ctx
);
670 if(ortn
== errSecSuccess
) {
674 endpointShutdown(sock
);
684 static void add_key(const void *key
, const void *value
, void *context
) {
685 CFArrayAppendValue((CFMutableArrayRef
)context
, key
);
689 static void showInfo(CFDictionaryRef info
) {
690 CFIndex dict_count
, key_ix
, key_count
;
691 CFMutableArrayRef keys
= NULL
;
692 CFIndex maxWidth
= 20; /* Maybe precompute this or grab from context? */
694 dict_count
= CFDictionaryGetCount(info
);
695 keys
= CFArrayCreateMutable(kCFAllocatorDefault
, dict_count
,
696 &kCFTypeArrayCallBacks
);
697 CFDictionaryApplyFunction(info
, add_key
, keys
);
698 key_count
= CFArrayGetCount(keys
);
699 CFArraySortValues(keys
, CFRangeMake(0, key_count
),
700 (CFComparatorFunction
)CFStringCompare
, 0);
702 for (key_ix
= 0; key_ix
< key_count
; ++key_ix
) {
703 CFStringRef key
= (CFStringRef
)CFArrayGetValueAtIndex(keys
, key_ix
);
704 CFTypeRef value
= CFDictionaryGetValue(info
, key
);
705 CFMutableStringRef line
= CFStringCreateMutable(NULL
, 0);
707 CFStringAppend(line
, key
);
709 for (jx
= CFStringGetLength(key
);
710 jx
< maxWidth
; ++jx
) {
711 CFStringAppend(line
, CFSTR(" "));
713 CFStringAppend(line
, CFSTR(" : "));
714 if (CFStringGetTypeID() == CFGetTypeID(value
)) {
715 CFStringAppend(line
, (CFStringRef
)value
);
716 } else if (CFDateGetTypeID() == CFGetTypeID(value
)) {
717 CFLocaleRef lc
= CFLocaleCopyCurrent();
718 CFDateFormatterRef df
= CFDateFormatterCreate(NULL
, lc
,
719 kCFDateFormatterFullStyle
, kCFDateFormatterFullStyle
);
720 CFDateRef date
= (CFDateRef
)value
;
721 CFStringRef ds
= CFDateFormatterCreateStringWithDate(NULL
, df
,
723 CFStringAppend(line
, ds
);
727 } else if (CFURLGetTypeID() == CFGetTypeID(value
)) {
728 CFURLRef url
= (CFURLRef
)value
;
729 CFStringAppend(line
, CFSTR("<"));
730 CFStringAppend(line
, CFURLGetString(url
));
731 CFStringAppend(line
, CFSTR(">"));
732 } else if (CFDataGetTypeID() == CFGetTypeID(value
)) {
733 CFDataRef v_d
= (CFDataRef
)value
;
734 CFStringRef v_s
= CFStringCreateFromExternalRepresentation(
735 kCFAllocatorDefault
, v_d
, kCFStringEncodingUTF8
);
737 CFStringAppend(line
, CFSTR("/"));
738 CFStringAppend(line
, v_s
);
739 CFStringAppend(line
, CFSTR("/ "));
742 const uint8_t *bytes
= CFDataGetBytePtr(v_d
);
743 CFIndex len
= CFDataGetLength(v_d
);
744 for (jx
= 0; jx
< len
; ++jx
) {
745 CFStringAppendFormat(line
, NULL
, CFSTR("%.02X"), bytes
[jx
]);
748 CFStringAppendFormat(line
, NULL
, CFSTR("%@"), value
);
750 CFStringWriteToFileWithNewline(line
, stdout
);
757 #pragma clang diagnostic pop
759 static void showPeerTrust(SecTrustRef peerTrust
, bool verbose
) {
761 if(peerTrust
== NULL
) {
768 printf("\n=============== Peer Trust Properties ===============\n");
769 CFArrayRef plist
= SecTrustCopyProperties(peerTrust
);
775 printf("\n================== Peer Trust Info ==================\n");
776 CFDictionaryRef info
= SecTrustCopyInfo(peerTrust
);
777 if (info
&& CFDictionaryGetCount(info
)) {
783 numCerts
= SecTrustGetCertificateCount(peerTrust
);
784 for(i
=0; i
<numCerts
; i
++) {
785 plist
= SecTrustCopySummaryPropertiesAtIndex(peerTrust
, i
);
786 printf("\n============= Peer Trust Cert %lu Summary =============\n\n", i
);
790 printf("\n============= Peer Trust Cert %lu Details =============\n\n", i
);
791 plist
= SecTrustCopyDetailedPropertiesAtIndex(peerTrust
, i
);
795 printf("\n============= End of Peer Trust Cert %lu ==============\n", i
);
800 static void showPeerCerts(
801 CFArrayRef __unused peerCerts
,
802 bool __unused verbose
)
806 SecCertificateRef certRef
;
809 if(peerCerts
== NULL
) {
812 numCerts
= CFArrayGetCount(peerCerts
);
813 for(i
=0; i
<numCerts
; i
++) {
814 certRef
= (SecCertificateRef
)CFArrayGetValueAtIndex(peerCerts
, i
);
815 printf("\n==================== Peer Cert %lu ====================\n\n", i
);
816 print_cert(certRef
, verbose
);
817 printf("\n================ End of Peer Cert %lu =================\n", i
);
822 static void writePeerCerts(
823 CFArrayRef peerCerts
,
824 const char *fileBase
)
827 SecCertificateRef certRef
;
829 char fileName
[_maxFileStringSize
];
831 if(peerCerts
== NULL
) {
834 numCerts
= CFArrayGetCount(peerCerts
);
835 for(i
=0; i
<numCerts
; i
++) {
836 snprintf(fileName
, _maxFileStringSize
, "%s%02d.cer", fileBase
, (int)i
);
837 certRef
= (SecCertificateRef
)CFArrayGetValueAtIndex(peerCerts
, i
);
838 CFDataRef derCert
= SecCertificateCopyData(certRef
);
840 writeFileSizet(fileName
, CFDataGetBytePtr(derCert
),
841 CFDataGetLength(derCert
));
845 printf("...wrote %lu certs to fileBase %s\n", numCerts
, fileBase
);
849 * Show result of an sslPing().
850 * Assumes the following from sslPingArgs:
864 static void showSSLResult(
865 const sslPingArgs
*pargs
,
867 int displayPeerCerts
,
868 char *fileBase
) // non-NULL: write certs to file
870 CFIndex numPeerCerts
;
874 if(pargs
->acceptedProts
) {
875 printf(" Allowed SSL versions : %s\n", pargs
->acceptedProts
);
878 printf(" Attempted SSL version : %s\n",
879 sslGetProtocolVersionString(pargs
->tryVersion
));
882 printf(" Result : %s\n", sslGetSSLErrString(err
));
883 printf(" Negotiated SSL version : %s\n",
884 sslGetProtocolVersionString(pargs
->negVersion
));
885 printf(" Negotiated CipherSuite : %s\n",
886 sslGetCipherSuiteString(pargs
->negCipher
));
887 if(pargs
->certState
!= kSSLClientCertNone
) {
888 printf(" Client Cert State : %s\n",
889 sslGetClientCertStateString(pargs
->certState
));
892 printf(" Resumed Session : ");
893 if(pargs
->sessionWasResumed
) {
894 for(unsigned dex
=0; dex
<pargs
->sessionIDLength
; dex
++) {
895 printf("%02X ", pargs
->sessionID
[dex
]);
896 if(((dex
% 8) == 7) && (dex
!= (pargs
->sessionIDLength
- 1))) {
903 printf("NOT RESUMED\n");
905 printf(" Handshake time : %f seconds\n", pargs
->handshakeTimeOp
);
907 if(pargs
->peerCerts
== NULL
) {
911 numPeerCerts
= CFArrayGetCount(pargs
->peerCerts
);
913 printf(" Number of server certs : %lu\n", numPeerCerts
);
914 if(numPeerCerts
!= 0) {
915 if (displayPeerCerts
== 1) {
916 showPeerCerts(pargs
->peerCerts
, false);
917 } else if (displayPeerCerts
== 2) {
918 showPeerTrust(pargs
->peerTrust
, false);
920 if(fileBase
!= NULL
) {
921 writePeerCerts(pargs
->peerCerts
, fileBase
);
928 static int verifyProtocol(
930 SSLProtocol maxProtocol
,
931 SSLProtocol reqProtocol
,
932 SSLProtocol negProtocol
)
937 if(reqProtocol
> maxProtocol
) {
938 /* known not to support this attempt, relax */
939 reqProtocol
= maxProtocol
;
941 if(reqProtocol
!= negProtocol
) {
942 printf("***Expected protocol %s; negotiated %s\n",
943 sslGetProtocolVersionString(reqProtocol
),
944 sslGetProtocolVersionString(negProtocol
));
952 static int verifyClientCertState(
953 bool verifyCertState
,
954 SSLClientCertificateState expectState
,
955 SSLClientCertificateState gotState
)
957 if(!verifyCertState
) {
960 if(expectState
== gotState
) {
963 printf("***Expected clientCertState %s; got %s\n",
964 sslGetClientCertStateString(expectState
),
965 sslGetClientCertStateString(gotState
));
969 static SSLProtocol
charToProt(
975 return kSSLProtocol2
;
977 return kSSLProtocol3
;
979 return kTLSProtocol12
;
985 int main(int argc
, char **argv
)
991 char fullFileBase
[_maxFileStringSize
];
992 int ourRtn
= 0; // exit status - sum of all errors
994 SecKeychainRef serverKc
= nil
;
997 /* user-spec'd parameters */
998 const char *getPath
= DEFAULT_PATH
;
999 char *fileBase
= NULL
;
1000 int displayCerts
= 0;
1001 bool doSslV2
= false;
1002 bool doSslV3
= false;
1003 bool doTlsV1
= true;
1004 bool doTlsV11
= false;
1005 bool doTlsV12
= false;
1006 bool protXOnly
= false; // kSSLProtocol3Only, kTLSProtocol1Only
1007 bool doProtUnknown
= false;
1008 unsigned loopCount
= 1;
1009 bool doPause
= false;
1010 bool pauseFirstLoop
= false;
1011 bool verifyProt
= false;
1012 SSLProtocol maxProtocol
= kTLSProtocol12
; // for verifying negotiated
1014 char *acceptedProts
= NULL
;
1015 char *keyChainName
= NULL
;
1016 char *getMsgSpec
= NULL
;
1017 bool vfyCertState
= false;
1018 SSLClientCertificateState expectCertState
= kSSLClientCertNone
;
1019 bool displayHandshakeTimes
= false;
1020 bool completeCertChain
= false;
1022 /* special case - one arg of "h" or "-h" or "hv" */
1024 if((strcmp(argv
[1], "h") == 0) || (strcmp(argv
[1], "-h") == 0)) {
1027 if(strcmp(argv
[1], "hv") == 0) {
1032 /* set up defaults */
1033 memset(&pargs
, 0, sizeof(sslPingArgs
));
1034 pargs
.hostName
= DEFAULT_HOST
;
1035 pargs
.port
= DEFAULT_PORT
;
1036 pargs
.resumableEnable
= true;
1039 for(arg
=1; arg
<argc
; arg
++) {
1042 /* first arg, is always hostname; '-' means default */
1043 if(argp
[0] != '-') {
1044 pargs
.hostName
= argp
;
1048 if(argp
[0] == '/') {
1049 /* path always starts with leading slash */
1057 /* requires another arg */
1060 if (pargs
.alpnNames
== NULL
) {
1061 pargs
.alpnNames
= CFArrayCreateMutableForCFTypes(NULL
);
1064 CFDataRef alpn
= CFDataCreate(NULL
, (const UInt8
*)argv
[arg
], strlen(argv
[arg
]));
1065 CFArrayAppendValue(pargs
.alpnNames
, alpn
);
1066 CFReleaseNull(alpn
);
1071 CFDictionaryRef context
= NULL
;
1074 /* requires another arg */
1078 if (argp
[0] == 'W') {
1079 context
= CFDictionaryCreateForCFTypes(NULL
,
1080 CFSTR("AppleServerAuthenticationAllowUATAPN"), kCFBooleanTrue
,
1081 CFSTR("AppleServerAuthenticationAllowUATIDS"), kCFBooleanTrue
,
1082 CFSTR("AppleServerAuthenticationAllowUATGS"), kCFBooleanTrue
,
1085 const char *verifyName
= pargs
.hostName
;
1087 if (pargs
.policies
== NULL
) {
1088 pargs
.policies
= CFArrayCreateMutableForCFTypes(NULL
);
1091 if (pargs
.vfyHostName
)
1092 verifyName
= pargs
.vfyHostName
;
1094 SecPolicyRef policy
= NULL
;
1095 CFStringRef hostname
= CFStringCreateWithCString(NULL
, verifyName
, kCFStringEncodingUTF8
);
1097 if (strcasecmp(argv
[arg
], "PushLegacy") == 0) {
1098 policy
= SecPolicyCreateApplePushServiceLegacy(hostname
);
1099 } else if (strcasecmp(argv
[arg
], "Push") == 0) {
1100 policy
= SecPolicyCreateApplePushService(hostname
, context
);
1101 } else if (strcasecmp(argv
[arg
], "IDS") == 0) {
1102 policy
= SecPolicyCreateAppleIDSServiceContext(hostname
, context
);
1103 } else if (strcasecmp(argv
[arg
], "GS") == 0) {
1104 policy
= SecPolicyCreateAppleGSService(hostname
, context
);
1106 printf("unknown policy: %s", argv
[arg
]);
1107 CFReleaseNull(hostname
);
1108 CFReleaseNull(context
);
1113 CFArrayAppendValue(pargs
.policies
, policy
);
1116 CFReleaseNull(policy
);
1117 CFReleaseNull(hostname
);
1118 CFReleaseNull(context
);
1123 pargs
.allowExpired
= true;
1126 pargs
.allowExpiredRoot
= true;
1129 pargs
.disableCertVerify
= true;
1132 pargs
.disableCertVerify
= true; // implied
1133 pargs
.manualCertVerify
= true;
1137 /* requires another arg */
1140 pargs
.anchorFile
= argv
[arg
];
1144 /* requires another arg */
1147 pargs
.anchorFile
= argv
[arg
];
1148 pargs
.replaceAnchors
= true;
1151 pargs
.allowAnyRoot
= true;
1154 pargs
.dumpRxData
= true;
1161 /* requires another arg */
1164 fileBase
= argv
[arg
];
1167 pargs
.cipherRestrict
= argp
[2];
1170 doSslV3
= doTlsV1
= doTlsV11
= false;
1174 doSslV2
= doTlsV1
= doTlsV11
= doTlsV12
= false;
1178 doSslV2
= doSslV3
= doTlsV11
= doTlsV12
= false;
1182 doSslV2
= doSslV3
= doTlsV1
= false;
1186 doSslV2
= doSslV3
= doTlsV1
= doTlsV12
= false;
1190 doSslV2
= doSslV3
= doTlsV1
= doTlsV11
= doTlsV12
= true;
1196 doSslV2
= doSslV3
= doTlsV1
= doTlsV11
= doTlsV12
= false;
1197 doProtUnknown
= true;
1200 pargs
.keepConnected
= true;
1203 pargs
.requireNotify
= true;
1204 pargs
.keepConnected
= true;
1207 pargs
.resumableEnable
= false;
1210 pargs
.nonBlocking
= true;
1216 if(argp
[1] != '=') {
1219 verifyProt
= true; // implied
1220 maxProtocol
= charToProt(argp
[2], argv
);
1223 if(argp
[1] != '=') {
1226 acceptedProts
= argv
[arg
];
1227 doSslV3
= doSslV2
= doTlsV1
= doTlsV11
= doTlsV12
= false;
1231 /* requires another arg */
1234 int parsedLoopCount
= atoi(argv
[arg
]);
1235 if (parsedLoopCount
<= 0) {
1236 printf("***bad loopCount\n");
1239 loopCount
= (unsigned) parsedLoopCount
;
1243 /* requires another arg */
1246 pargs
.port
= atoi(argv
[arg
]);
1249 pargs
.allowHostnameSpoof
= true;
1253 /* requires another arg */
1256 pargs
.vfyHostName
= argv
[arg
];
1260 /* requires another arg */
1263 keyChainName
= &argp
[2];
1266 getMsgSpec
= &argp
[2];
1269 if(argp
[1] != '=') {
1272 vfyCertState
= true;
1275 expectCertState
= kSSLClientCertNone
;
1278 expectCertState
= kSSLClientCertRequested
;
1281 expectCertState
= kSSLClientCertSent
;
1284 expectCertState
= kSSLClientCertRejected
;
1291 pargs
.password
= &argp
[2];
1297 pauseFirstLoop
= true;
1303 pargs
.verbose
= true;
1306 pargs
.silent
= pargs
.quiet
= true;
1309 displayHandshakeTimes
= true;
1312 completeCertChain
= true;
1315 if(pargs
.verbose
|| (argp
[1] == 'v')) {
1326 pargs
.getMsg
= getMsgSpec
;
1329 sprintf(getMsg
, "%s %s %s",
1330 DEFAULT_GETMSG
, getPath
, DEFAULT_GET_SUFFIX
);
1331 pargs
.getMsg
= getMsg
;
1334 /* get client cert and optional encryption cert as CFArrayRef */
1336 pargs
.clientCerts
= getSslCerts(keyChainName
, false, completeCertChain
,
1337 pargs
.anchorFile
, &serverKc
);
1338 if(pargs
.clientCerts
== nil
) {
1344 struct sigaction sa
;
1345 memset(&sa
, 0, sizeof(sa
));
1346 sa
.sa_flags
= SA_RESTART
;
1347 sa
.sa_handler
= sigpipe
;
1348 sigaction(SIGPIPE
, &sa
, NULL
);
1351 for(loop
=0; loop
<loopCount
; loop
++) {
1353 * One pass for each protocol version, skipping any explicit version if
1354 * an attempt at a higher version and succeeded in doing so successfully fell
1358 pargs
.tryVersion
= kTLSProtocol12
;
1359 pargs
.acceptedProts
= NULL
;
1361 printf("Connecting to host %s with TLS V1.2...", pargs
.hostName
);
1364 err
= sslPing(&pargs
);
1370 snprintf(fullFileBase
, _maxFileStringSize
, "%s_v3.1", fileBase
);
1372 showSSLResult(&pargs
,
1375 fileBase
? fullFileBase
: NULL
);
1377 CFReleaseNull(pargs
.peerCerts
);
1379 /* deal with fallbacks, skipping redundant tests */
1380 switch(pargs
.negVersion
) {
1381 case kTLSProtocol11
:
1402 ourRtn
+= verifyProtocol(verifyProt
, maxProtocol
, kTLSProtocol12
,
1405 /* note we do this regardless since the client state might be
1406 * the cause of a failure */
1407 ourRtn
+= verifyClientCertState(vfyCertState
, expectCertState
,
1411 pargs
.tryVersion
= kTLSProtocol11
;
1412 pargs
.acceptedProts
= NULL
;
1414 printf("Connecting to host %s with TLS V1.1...", pargs
.hostName
);
1417 err
= sslPing(&pargs
);
1423 snprintf(fullFileBase
, _maxFileStringSize
, "%s_v3.1", fileBase
);
1425 showSSLResult(&pargs
,
1428 fileBase
? fullFileBase
: NULL
);
1430 CFReleaseNull(pargs
.peerCerts
);
1432 /* deal with fallbacks, skipping redundant tests */
1433 switch(pargs
.negVersion
) {
1449 ourRtn
+= verifyProtocol(verifyProt
, maxProtocol
, kTLSProtocol11
,
1452 /* note we do this regardless since the client state might be
1453 * the cause of a failure */
1454 ourRtn
+= verifyClientCertState(vfyCertState
, expectCertState
,
1459 protXOnly
? kTLSProtocol1Only
: kTLSProtocol1
;
1460 pargs
.acceptedProts
= NULL
;
1462 printf("Connecting to host %s with TLS V1...", pargs
.hostName
);
1465 err
= sslPing(&pargs
);
1471 snprintf(fullFileBase
, _maxFileStringSize
, "%s_v3.1", fileBase
);
1473 showSSLResult(&pargs
,
1476 fileBase
? fullFileBase
: NULL
);
1478 CFReleaseNull(pargs
.peerCerts
);
1480 /* deal with fallbacks, skipping redundant tests */
1481 switch(pargs
.negVersion
) {
1492 ourRtn
+= verifyProtocol(verifyProt
, maxProtocol
, kTLSProtocol1
,
1495 /* note we do this regardless since the client state might be
1496 * the cause of a failure */
1497 ourRtn
+= verifyClientCertState(vfyCertState
, expectCertState
,
1501 pargs
.tryVersion
= protXOnly
? kSSLProtocol3Only
: kSSLProtocol3
;
1502 pargs
.acceptedProts
= NULL
;
1504 printf("Connecting to host %s with SSL V3...", pargs
.hostName
);
1507 err
= sslPing(&pargs
);
1513 snprintf(fullFileBase
, _maxFileStringSize
, "%s_v3.0", fileBase
);
1515 showSSLResult(&pargs
,
1518 fileBase
? fullFileBase
: NULL
);
1520 CFReleaseNull(pargs
.peerCerts
);
1522 /* deal with fallbacks, skipping redundant tests */
1523 switch(pargs
.negVersion
) {
1530 ourRtn
+= verifyProtocol(verifyProt
, maxProtocol
, kSSLProtocol3
,
1533 /* note we do this regardless since the client state might be
1534 * the cause of a failure */
1535 ourRtn
+= verifyClientCertState(vfyCertState
, expectCertState
,
1541 snprintf(fullFileBase
, _maxFileStringSize
, "%s_v2", fileBase
);
1544 printf("Connecting to host %s with SSL V2...", pargs
.hostName
);
1547 pargs
.tryVersion
= kSSLProtocol2
;
1548 pargs
.acceptedProts
= NULL
;
1549 err
= sslPing(&pargs
);
1555 snprintf(fullFileBase
, _maxFileStringSize
, "%s_v2", fileBase
);
1557 showSSLResult(&pargs
,
1560 fileBase
? fullFileBase
: NULL
);
1562 CFReleaseNull(pargs
.peerCerts
);
1564 ourRtn
+= verifyProtocol(verifyProt
, maxProtocol
, kSSLProtocol2
,
1567 /* note we do this regardless since the client state might be
1568 * the cause of a failure */
1569 ourRtn
+= verifyClientCertState(vfyCertState
, expectCertState
,
1574 printf("Connecting to host %s with kSSLProtocolUnknown...",
1578 pargs
.tryVersion
= kSSLProtocolUnknown
;
1579 pargs
.acceptedProts
= NULL
;
1580 err
= sslPing(&pargs
);
1586 snprintf(fullFileBase
, _maxFileStringSize
, "%s_def", fileBase
);
1588 showSSLResult(&pargs
,
1591 fileBase
? fullFileBase
: NULL
);
1593 CFReleaseNull(pargs
.peerCerts
);
1595 if(acceptedProts
!= NULL
) {
1596 pargs
.acceptedProts
= acceptedProts
;
1597 pargs
.tryVersion
= kSSLProtocolUnknown
; // not used
1599 printf("Connecting to host %s with acceptedProts %s...",
1600 pargs
.hostName
, pargs
.acceptedProts
);
1603 err
= sslPing(&pargs
);
1609 snprintf(fullFileBase
, _maxFileStringSize
, "%s_def", fileBase
);
1611 showSSLResult(&pargs
,
1614 fileBase
? fullFileBase
: NULL
);
1616 CFReleaseNull(pargs
.peerCerts
);
1620 /* pause after first, before last to grab trace */
1621 ((loop
== 0) || (loop
== loopCount
- 1))
1626 printf("a to abort, c to continue: ");
1627 resp
= (char) getchar();
1633 if(displayHandshakeTimes
) {
1634 CFAbsoluteTime totalTime
;
1635 unsigned numHandshakes
;
1636 if(pargs
.numHandshakes
== 1) {
1637 /* just display the first one */
1638 totalTime
= pargs
.handshakeTimeFirst
;
1642 /* skip the first one */
1643 totalTime
= pargs
.handshakeTimeTotal
;
1644 numHandshakes
= pargs
.numHandshakes
- 1;
1646 if(numHandshakes
!= 0) {
1647 printf(" %u handshakes in %f seconds; %f seconds per handshake\n",
1648 numHandshakes
, totalTime
,
1649 (totalTime
/ numHandshakes
));
1654 printf("===%s exiting with %d %s for host %s\n", argv
[0], ourRtn
,
1655 (ourRtn
> 1) ? "errors" : "error", pargs
.hostName
);