]> git.saurik.com Git - apple/security.git/blob - libsecurity_ssl/sslViewer/sslViewer.cpp
Security-55178.0.1.tar.gz
[apple/security.git] / libsecurity_ssl / sslViewer / sslViewer.cpp
1 /*
2 * Copyright (c) 2006-2012 Apple Inc. All Rights Reserved.
3 *
4 * SSL viewer tool, Secure Transport.
5 */
6
7 #include "SecureTransport.h"
8
9 #include <Security/SecureTransport.h>
10 #include <Security/SecureTransportPriv.h>
11 #include <Security/SecCertificate.h>
12 #include <Security/SecTrust.h>
13 #include <Security/SecTrustPriv.h>
14 #include "sslAppUtils.h"
15 #include "printCert.h"
16 #include "ioSock.h"
17 #include "fileIo.h"
18
19 #include <MacErrors.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <time.h>
24 #include <ctype.h>
25 #include <CoreFoundation/CoreFoundation.h>
26
27 #if NO_SERVER
28 #include <securityd/spi.h>
29 #endif
30
31 #define DEFAULT_GETMSG "GET"
32 #define DEFAULT_PATH "/"
33 #define DEFAULT_GET_SUFFIX "HTTP/1.0\r\n\r\n"
34
35 #define DEFAULT_HOST "store.apple.com"
36 #define DEFAULT_PORT 443
37
38 #define CFReleaseSafe(CF) { CFTypeRef _cf = (CF); if (_cf) CFRelease(_cf); }
39 #define CFReleaseNull(CF) { CFTypeRef _cf = (CF); \
40 if (_cf) { (CF) = NULL; CFRelease(_cf); } }
41
42 static void usageNorm(char **argv)
43 {
44 printf("Usage: %s [hostname|-] [path] [option ...]\n", argv[0]);
45 printf(" %s hostname [path] [option ...]\n", argv[0]);
46 printf("Specifying '-' for hostname, or no args, uses default of %s.\n",
47 DEFAULT_HOST);
48 printf("Optional path argument must start with leading '/'.\n");
49 printf("Options:\n");
50 printf(" e Allow Expired Certs\n");
51 printf(" E Allow Expired Roots\n");
52 printf(" r Allow any root cert\n");
53 printf(" c Display peer certs\n");
54 printf(" cc Display peer SecTrust\n");
55 printf(" d Display received data\n");
56 printf(" S Display enabled cipher suites\n");
57 printf(" 2 SSLv2 only\n");
58 printf(" 3 SSLv3 only\n");
59 printf(" tls10 | t TLSv1 only\n");
60 printf(" tls11 TLSv1.1 only\n");
61 printf(" tls12 TLSv1.2 only\n");
62 printf(" L all - TLSv1.2, TLSv1.1, TLSv1, SSLv3, SSLv2 (default = TLSv1.2)\n");
63 printf(" g={prot...} Specify legal protocols; prot = any combo of"
64 " [2|3|t|tls10|tls11|tls12]\n");
65 printf(" k=keychain Contains (client|server) cert and keys. Optional.\n");
66 printf(" l=loopCount Perform loopCount ops (default = 1)\n");
67 printf(" P=port Default = %d\n", DEFAULT_PORT);
68 printf(" p Pause after each loop\n");
69 printf(" q Quiet/diagnostic mode (site names and errors"
70 " only)\n");
71 printf(" a fileName Add fileName to list of trusted roots\n");
72 printf(" A fileName fileName is ONLY trusted root\n");
73 printf(" Z fileName fileName is a trusted leaf cert\n");
74 printf(" x Disable Cert Verification\n");
75 printf(" z=password Unlock client keychain with password.\n");
76 printf(" 8 Complete cert chains (default is out cert is a root)\n");
77 printf(" s Silent\n");
78 printf(" V Verbose\n");
79 printf(" h Help\n");
80 printf(" hv More, verbose help\n");
81 }
82
83 static void usageVerbose(char **argv) __attribute__((noreturn));
84 static void usageVerbose(char **argv)
85 {
86 usageNorm(argv);
87 printf("Obscure Usage:\n");
88 printf(" u kSSLProtocolUnknown only (TLSv1)\n");
89 printf(" M Manual cert verification via "
90 "SecTrustEvaluate\n");
91 printf(" f fileBase Write Peer Certs to fileBase*\n");
92 printf(" o TLSv1, SSLv3 use kSSLProtocol__X__Only\n");
93 printf(" C=cipherSuite (e=40-bit d=DES D=40-bit DES 3=3DES 4=RC4 "
94 "$=40-bit RC4\n"
95 " 2=RC2 a=AES128 A=AES256 h=DH H=Anon DH r=DHE/RSA s=DH/DSS\n");
96 printf(" y=keychain Encryption-only cert and keys. Optional.\n");
97 printf(" K Keep connected until server disconnects\n");
98 printf(" n Require closure notify message in TLSv1, "
99 "SSLv3 mode (implies K)\n");
100 printf(" R Disable resumable session support\n");
101 printf(" b Non-blocking I/O\n");
102 printf(" v Verify negotiated protocol equals attempted\n");
103 printf(" m=[23t] Max protocol supported as specified; implies "
104 "v\n");
105 printf(" T=[nrsj] Verify client cert state = "
106 "none/requested/sent/rejected\n");
107 printf(" H allow hostname spoofing\n");
108 printf(" F=vfyHost Verify certs with specified host name\n");
109 printf(" G=getMsg Specify entire GET, POST, etc.\n");
110 printf(" N Log handshake timing\n");
111 printf(" 7 Pause only after first loop\n");
112 exit(1);
113 }
114
115 static void usage(char **argv) __attribute__((noreturn));
116 static void usage(char **argv)
117 {
118 usageNorm(argv);
119 exit(1);
120 }
121
122 /*
123 * Arguments to top-level sslPing()
124 */
125 typedef struct {
126 SSLProtocol tryVersion; // only used if acceptedProts NULL
127 // uses SSLSetProtocolVersion
128 char *acceptedProts; // optional, any combo of {2,3,t}
129 // uses SSLSetProtocolVersionEnabled
130 const char *hostName; // e.g., "store.apple.com"
131 const char *vfyHostName; // use this for cert vfy if non-NULL,
132 // else use hostName
133 unsigned short port;
134 const char *getMsg; // e.g.,
135 // "GET / HTTP/1.0\r\n\r\n"
136 bool allowExpired;
137 bool allowAnyRoot;
138 bool allowExpiredRoot;
139 bool disableCertVerify;
140 bool manualCertVerify;
141 bool dumpRxData; // display server data
142 char cipherRestrict; // '2', 'd'. etc...; '\0' for
143 // no restriction
144 bool keepConnected;
145 bool requireNotify; // require closure notify
146 // in V3 mode
147 bool resumableEnable;
148 bool allowHostnameSpoof;
149 bool nonBlocking;
150 char *anchorFile;
151 char *trustedLeafFile;
152 bool replaceAnchors;
153 bool interactiveAuth;
154 CFArrayRef clientCerts; // optional
155 CFArrayRef encryptClientCerts; // optional
156 uint32 sessionCacheTimeout;// optional
157 bool disableAnonCiphers;
158 bool showCipherSuites;
159 bool quiet; // minimal stdout
160 bool silent; // no stdout
161 bool verbose;
162 SSLProtocol negVersion; // RETURNED
163 SSLCipherSuite negCipher; // RETURNED
164 CFArrayRef peerCerts; // mallocd & RETURNED
165 SecTrustRef peerTrust; // RETURNED
166 SSLClientCertificateState certState; // RETURNED
167 #if TARGET_OS_MAC && MAC_OS_X_VERSION_MAX_ALLOWED < 1060
168 int authType;
169 #else
170 SSLClientAuthenticationType authType; // RETURNED
171 #endif
172 CFArrayRef dnList; // RETURNED
173 char *password; // optional to open clientCerts
174 char **argv;
175 Boolean sessionWasResumed;
176 unsigned char sessionID[MAX_SESSION_ID_LENGTH];
177 size_t sessionIDLength;
178 CFAbsoluteTime handshakeTimeOp; // time for this op
179 CFAbsoluteTime handshakeTimeFirst; // time for FIRST op, not averaged
180 CFAbsoluteTime handshakeTimeTotal; // time for all ops except first
181 unsigned numHandshakes;
182
183 } sslPingArgs;
184
185 #include <signal.h>
186 static void sigpipe(int sig)
187 {
188 fflush(stdin);
189 printf("***SIGPIPE***\n");
190 }
191
192 /*
193 * Snag a copy of current connection's peer certs so we can
194 * examine them later after the connection is closed.
195 * SecureTransport actually does the create and retain for us.
196 */
197 static OSStatus copyPeerCerts(
198 SSLContext *ctx,
199 CFArrayRef *peerCerts) // mallocd & RETURNED
200 {
201 OSStatus ortn = SSLCopyPeerCertificates(ctx, peerCerts);
202 if(ortn) {
203 printf("***Error obtaining peer certs: %s\n",
204 sslGetSSLErrString(ortn));
205 }
206 return ortn;
207 }
208
209 /*
210 * Manually evaluate session's SecTrustRef.
211 */
212
213 static OSStatus sslEvaluateTrust(
214 SSLContext *ctx,
215 bool verbose,
216 bool silent,
217 CFArrayRef *peerCerts) // fetched and retained
218 {
219 OSStatus ortn = noErr;
220 #if USE_CDSA_CRYPTO
221 SecTrustRef secTrust = NULL;
222 ortn = SSLGetPeerSecTrust(ctx, &secTrust);
223 if(ortn) {
224 printf("\n***Error obtaining peer SecTrustRef: %s\n",
225 sslGetSSLErrString(ortn));
226 return ortn;
227 }
228 if(secTrust == NULL) {
229 /* this is the normal case for resumed sessions, in which
230 * no cert evaluation is performed */
231 if(!silent) {
232 printf("...No SecTrust available - this is a resumed session, right?\n");
233 }
234 return noErr;
235 }
236 SecTrustResultType secTrustResult;
237 ortn = SecTrustEvaluate(secTrust, &secTrustResult);
238 if(ortn) {
239 printf("\n***Error on SecTrustEvaluate: %d\n", (int)ortn);
240 return ortn;
241 }
242 if(verbose) {
243 const char *res = NULL;
244 switch(secTrustResult) {
245 case kSecTrustResultInvalid:
246 res = "kSecTrustResultInvalid"; break;
247 case kSecTrustResultProceed:
248 res = "kSecTrustResultProceed"; break;
249 case kSecTrustResultConfirm:
250 res = "kSecTrustResultConfirm"; break;
251 case kSecTrustResultDeny:
252 res = "kSecTrustResultDeny"; break;
253 case kSecTrustResultUnspecified:
254 res = "kSecTrustResultUnspecified"; break;
255 case kSecTrustResultRecoverableTrustFailure:
256 res = "kSecTrustResultRecoverableTrustFailure"; break;
257 case kSecTrustResultFatalTrustFailure:
258 res = "kSecTrustResultFatalTrustFailure"; break;
259 case kSecTrustResultOtherError:
260 res = "kSecTrustResultOtherError"; break;
261 default:
262 res = "UNKNOWN"; break;
263 }
264 printf("\nSecTrustEvaluate(): secTrustResult %s\n", res);
265 }
266
267 switch(secTrustResult) {
268 case kSecTrustResultUnspecified:
269 /* cert chain valid, no special UserTrust assignments */
270 case kSecTrustResultProceed:
271 /* cert chain valid AND user explicitly trusts this */
272 break;
273 default:
274 printf("\n***SecTrustEvaluate reported secTrustResult %d\n",
275 (int)secTrustResult);
276 ortn = errSSLXCertChainInvalid;
277 break;
278 }
279 #endif
280
281 *peerCerts = NULL;
282
283 #ifdef USE_CDSA_CRYPTO
284 /* one more thing - get peer certs in the form of an evidence chain */
285 CSSM_TP_APPLE_EVIDENCE_INFO *dummyEv;
286 OSStatus thisRtn = SecTrustGetResult(secTrust, &secTrustResult,
287 peerCerts, &dummyEv);
288 if(thisRtn) {
289 printSslErrStr("SecTrustGetResult", thisRtn);
290 }
291 #endif
292 return ortn;
293 }
294
295 static void sslShowEnabledCipherSuites(
296 SSLContextRef ctx)
297 {
298 OSStatus status;
299 SSLCipherSuite *ciphers;
300 size_t numCiphers, totalCiphers;
301 unsigned int i;
302
303 status = SSLGetNumberSupportedCiphers(ctx, &totalCiphers);
304 status = SSLGetNumberEnabledCiphers(ctx, &numCiphers);
305 ciphers = (SSLCipherSuite *)malloc(sizeof(SSLCipherSuite) * numCiphers);
306 status = SSLGetEnabledCiphers(ctx, ciphers, &numCiphers);
307
308 printf(" Total enabled ciphers : %ld of %ld\n", numCiphers, totalCiphers);
309
310 for(i=0; i<numCiphers; i++) {
311 printf(" %s (0x%04X)\n", sslGetCipherSuiteString(ciphers[i]), ciphers[i]);
312 fflush(stdout);
313 }
314 free(ciphers);
315 }
316
317 /* print reply received from server, safely */
318 static void dumpAscii(
319 uint8_t *rcvBuf,
320 uint32_t len)
321 {
322 char *cp = (char *)rcvBuf;
323 uint32_t i;
324 char c;
325
326 for(i=0; i<len; i++) {
327 c = *cp++;
328 if(c == '\0') {
329 break;
330 }
331 switch(c) {
332 case '\n':
333 printf("\\n");
334 break;
335 case '\r':
336 printf("\\r");
337 break;
338 default:
339 if(isprint(c) && (c != '\n')) {
340 printf("%c", c);
341 }
342 else {
343 printf("<%02X>", ((unsigned)c) & 0xff);
344 }
345 break;
346 }
347
348 }
349 printf("\n");
350 }
351
352 /*
353 * Perform one SSL diagnostic session. Returns nonzero on error. Normally no
354 * output to stdout except initial "connecting to" message, unless there
355 * is a really screwed up error (i.e., something not directly related
356 * to the SSL connection).
357 */
358 #define RCV_BUF_SIZE 256
359
360 static OSStatus sslPing(
361 sslPingArgs *pargs)
362 {
363 PeerSpec peerId;
364 otSocket sock = 0;
365 OSStatus ortn;
366 SSLContextRef ctx = NULL;
367 size_t length;
368 size_t actLen;
369 uint8_t rcvBuf[RCV_BUF_SIZE];
370 CFAbsoluteTime startHandshake;
371 CFAbsoluteTime endHandshake;
372
373 pargs->negVersion = kSSLProtocolUnknown;
374 pargs->negCipher = SSL_NULL_WITH_NULL_NULL;
375 pargs->peerCerts = NULL;
376
377 /* first make sure requested server is there */
378 ortn = MakeServerConnection(pargs->hostName, pargs->port, pargs->nonBlocking,
379 &sock, &peerId);
380 if(ortn) {
381 printf("MakeServerConnection returned %d; aborting\n", (int)ortn);
382 return ortn;
383 }
384 if(pargs->verbose) {
385 printf("...connected to server; starting SecureTransport\n");
386 }
387
388 /*
389 * Set up a SecureTransport session.
390 * First the standard calls.
391 */
392 ortn = SSLNewContext(false, &ctx);
393 if(ortn) {
394 printSslErrStr("SSLNewContext", ortn);
395 goto cleanup;
396 }
397 ortn = SSLSetIOFuncs(ctx, SocketRead, SocketWrite);
398 if(ortn) {
399 printSslErrStr("SSLSetIOFuncs", ortn);
400 goto cleanup;
401 }
402 ortn = SSLSetConnection(ctx, (SSLConnectionRef)sock);
403 if(ortn) {
404 printSslErrStr("SSLSetConnection", ortn);
405 goto cleanup;
406 }
407 SSLConnectionRef getConn;
408 ortn = SSLGetConnection(ctx, &getConn);
409 if(ortn) {
410 printSslErrStr("SSLGetConnection", ortn);
411 goto cleanup;
412 }
413 if(getConn != (SSLConnectionRef)sock) {
414 printf("***SSLGetConnection error\n");
415 ortn = paramErr;
416 goto cleanup;
417 }
418 if(!pargs->allowHostnameSpoof) {
419 /* if this isn't set, it isn't checked by AppleX509TP */
420 const char *vfyHost = pargs->hostName;
421 if(pargs->vfyHostName) {
422 /* generally means we're expecting an error */
423 vfyHost = pargs->vfyHostName;
424 }
425 ortn = SSLSetPeerDomainName(ctx, vfyHost, strlen(vfyHost));
426 if(ortn) {
427 printSslErrStr("SSLSetPeerDomainName", ortn);
428 goto cleanup;
429 }
430 }
431
432 /*
433 * SecureTransport options.
434 */
435 if(pargs->acceptedProts) {
436 ortn = SSLSetProtocolVersionEnabled(ctx, kSSLProtocolAll, false);
437 if(ortn) {
438 printSslErrStr("SSLSetProtocolVersionEnabled(all off)", ortn);
439 goto cleanup;
440 }
441 for(const char *cp = pargs->acceptedProts; *cp; cp++) {
442 SSLProtocol prot;
443 switch(*cp) {
444 case '2':
445 prot = kSSLProtocol2;
446 break;
447 case '3':
448 prot = kSSLProtocol3;
449 break;
450 case 't':
451 prot = kTLSProtocol1;
452 if (cp[1] == 'l' && cp[2] == 's' && cp[3] == '1') {
453 cp += 3;
454 if (cp[1] == '1') {
455 cp++;
456 prot = kTLSProtocol11;
457 }
458 else if (cp[1] == '2') {
459 cp++;
460 prot = kTLSProtocol12;
461 }
462 }
463 break;
464 default:
465 usage(pargs->argv);
466 }
467 ortn = SSLSetProtocolVersionEnabled(ctx, prot, true);
468 if(ortn) {
469 printSslErrStr("SSLSetProtocolVersionEnabled", ortn);
470 goto cleanup;
471 }
472 }
473 }
474 else {
475 ortn = SSLSetProtocolVersion(ctx, pargs->tryVersion);
476 if(ortn) {
477 printSslErrStr("SSLSetProtocolVersion", ortn);
478 goto cleanup;
479 }
480 SSLProtocol getVers;
481 ortn = SSLGetProtocolVersion(ctx, &getVers);
482 if(ortn) {
483 printSslErrStr("SSLGetProtocolVersion", ortn);
484 goto cleanup;
485 }
486 if(getVers != pargs->tryVersion && getVers != kSSLProtocolAll) {
487 printf("***SSLGetProtocolVersion screwup: try %s get %s\n",
488 sslGetProtocolVersionString(pargs->tryVersion),
489 sslGetProtocolVersionString(getVers));
490 ortn = paramErr;
491 goto cleanup;
492 }
493 }
494 if(pargs->resumableEnable) {
495 const void *rtnId = NULL;
496 size_t rtnIdLen = 0;
497
498 ortn = SSLSetPeerID(ctx, &peerId, sizeof(PeerSpec));
499 if(ortn) {
500 printSslErrStr("SSLSetPeerID", ortn);
501 goto cleanup;
502 }
503 /* quick test of the get fcn */
504 ortn = SSLGetPeerID(ctx, &rtnId, &rtnIdLen);
505 if(ortn) {
506 printSslErrStr("SSLGetPeerID", ortn);
507 goto cleanup;
508 }
509 if((rtnId == NULL) || (rtnIdLen != sizeof(PeerSpec))) {
510 printf("***SSLGetPeerID screwup\n");
511 }
512 else if(memcmp(&peerId, rtnId, rtnIdLen) != 0) {
513 printf("***SSLGetPeerID data mismatch\n");
514 }
515 }
516 if(pargs->allowExpired) {
517 ortn = SSLSetAllowsExpiredCerts(ctx, true);
518 if(ortn) {
519 printSslErrStr("SSLSetAllowExpiredCerts", ortn);
520 goto cleanup;
521 }
522 }
523 if(pargs->allowExpiredRoot) {
524 ortn = SSLSetAllowsExpiredRoots(ctx, true);
525 if(ortn) {
526 printSslErrStr("SSLSetAllowsExpiredRoots", ortn);
527 goto cleanup;
528 }
529 }
530 if(pargs->disableCertVerify) {
531 ortn = SSLSetEnableCertVerify(ctx, false);
532 if(ortn) {
533 printSslErrStr("SSLSetEnableCertVerify", ortn);
534 goto cleanup;
535 }
536 }
537 if(pargs->allowAnyRoot) {
538 ortn = SSLSetAllowsAnyRoot(ctx, true);
539 if(ortn) {
540 printSslErrStr("SSLSetAllowAnyRoot", ortn);
541 goto cleanup;
542 }
543 }
544 if(pargs->cipherRestrict != '\0') {
545 ortn = sslSetCipherRestrictions(ctx, pargs->cipherRestrict);
546 if(ortn) {
547 goto cleanup;
548 }
549 }
550 if(pargs->anchorFile) {
551 ortn = sslAddTrustedRoot(ctx, pargs->anchorFile, pargs->replaceAnchors);
552 if(ortn) {
553 printf("***Error obtaining anchor file %s\n", pargs->anchorFile);
554 goto cleanup;
555 }
556 }
557 if(pargs->trustedLeafFile) {
558 SecCertificateRef leafCertRef = NULL;
559 CFMutableArrayRef leafCerts = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
560 /* sslReadAnchor is a misnomer; it just creates a SecCertificateRef from a file */
561 ortn = sslReadAnchor(pargs->trustedLeafFile, &leafCertRef);
562 if (!ortn) {
563 CFArrayAppendValue(leafCerts, leafCertRef);
564 CFRelease(leafCertRef);
565 ortn = SSLSetTrustedLeafCertificates(ctx, leafCerts);
566 CFRelease(leafCerts);
567 }
568 if(ortn) {
569 goto cleanup;
570 }
571 }
572 if(pargs->interactiveAuth) {
573 /* we want to get errSSLServerAuthCompleted from SSLHandshake on server auth completion */
574 SSLSetSessionOption(ctx, kSSLSessionOptionBreakOnServerAuth, true);
575 /* we want to get errSSLClientCertRequested from SSLHandshake on client auth request */
576 SSLSetSessionOption(ctx, kSSLSessionOptionBreakOnCertRequested, true);
577 }
578 else if(pargs->clientCerts) {
579 CFArrayRef dummy;
580 if(pargs->anchorFile == NULL) {
581 /* assume this is a root we want to implicitly trust */
582 ortn = addIdentityAsTrustedRoot(ctx, pargs->clientCerts);
583 if(ortn) {
584 goto cleanup;
585 }
586 }
587 ortn = SSLSetCertificate(ctx, pargs->clientCerts);
588 if(ortn) {
589 printSslErrStr("SSLSetCertificate", ortn);
590 goto cleanup;
591 }
592 /* quickie test for this new function */
593 ortn = SSLGetCertificate(ctx, &dummy);
594 if(ortn) {
595 printSslErrStr("SSLGetCertificate", ortn);
596 goto cleanup;
597 }
598 if(dummy != pargs->clientCerts) {
599 printf("***SSLGetCertificate error\n");
600 ortn = ioErr;
601 goto cleanup;
602 }
603 }
604 if(pargs->encryptClientCerts) {
605 if(pargs->anchorFile == NULL) {
606 ortn = addIdentityAsTrustedRoot(ctx, pargs->encryptClientCerts);
607 if(ortn) {
608 goto cleanup;
609 }
610 }
611 ortn = SSLSetEncryptionCertificate(ctx, pargs->encryptClientCerts);
612 if(ortn) {
613 printSslErrStr("SSLSetEncryptionCertificate", ortn);
614 goto cleanup;
615 }
616 }
617 if(pargs->sessionCacheTimeout) {
618 ortn = SSLSetSessionCacheTimeout(ctx, pargs->sessionCacheTimeout);
619 if(ortn) {
620 printSslErrStr("SSLSetSessionCacheTimeout", ortn);
621 goto cleanup;
622 }
623 }
624 if(!pargs->disableAnonCiphers) {
625 ortn = SSLSetAllowAnonymousCiphers(ctx, true);
626 if(ortn) {
627 printSslErrStr("SSLSetAllowAnonymousCiphers", ortn);
628 goto cleanup;
629 }
630 /* quickie test of the getter */
631 Boolean e;
632 ortn = SSLGetAllowAnonymousCiphers(ctx, &e);
633 if(ortn) {
634 printSslErrStr("SSLGetAllowAnonymousCiphers", ortn);
635 goto cleanup;
636 }
637 if(!e) {
638 printf("***SSLGetAllowAnonymousCiphers() returned false; expected true\n");
639 ortn = ioErr;
640 goto cleanup;
641 }
642 }
643 if(pargs->showCipherSuites) {
644 sslShowEnabledCipherSuites(ctx);
645 }
646 /*** end options ***/
647
648 if(pargs->verbose) {
649 printf("...starting SSL handshake\n");
650 }
651 startHandshake = CFAbsoluteTimeGetCurrent();
652
653 do
654 { ortn = SSLHandshake(ctx);
655 if((ortn == errSSLWouldBlock) && !pargs->silent) {
656 /* keep UI responsive */
657 sslOutputDot();
658 }
659 else if(ortn == errSSLServerAuthCompleted) {
660 if(pargs->verbose) {
661 printf("...server authentication completed\n");
662 }
663 }
664 else if(ortn == errSSLClientCertRequested) {
665 if(pargs->verbose) {
666 printf("...received client cert request\n");
667 }
668 /* %%% could prompt interactively here for client cert to use;
669 * for now, just use the client cert passed on the command line
670 */
671 if(pargs->clientCerts) {
672 CFArrayRef dummy;
673 if(pargs->anchorFile == NULL) {
674 /* assume this is a root we want to implicitly trust */
675 ortn = addIdentityAsTrustedRoot(ctx, pargs->clientCerts);
676 if(ortn) {
677 goto cleanup;
678 }
679 }
680 if(pargs->verbose) {
681 printf("...setting client certificate\n");
682 }
683 ortn = SSLSetCertificate(ctx, pargs->clientCerts);
684 if(ortn) {
685 printSslErrStr("SSLSetCertificate", ortn);
686 goto cleanup;
687 }
688 /* quickie test for this new function */
689 ortn = SSLGetCertificate(ctx, &dummy);
690 if(ortn) {
691 printSslErrStr("SSLGetCertificate", ortn);
692 goto cleanup;
693 }
694 if(dummy != pargs->clientCerts) {
695 printf("***SSLGetCertificate error\n");
696 ortn = ioErr;
697 goto cleanup;
698 }
699 }
700 else {
701 printf("***no client certificate specified!\n");
702 }
703 }
704 } while (ortn == errSSLWouldBlock ||
705 ortn == errSSLServerAuthCompleted ||
706 ortn == errSSLClientCertRequested);
707
708 endHandshake = CFAbsoluteTimeGetCurrent();
709 pargs->handshakeTimeOp = endHandshake - startHandshake;
710 if(pargs->numHandshakes == 0) {
711 /* special case, this one is always way longer */
712 pargs->handshakeTimeFirst = pargs->handshakeTimeOp;
713 }
714 else {
715 /* normal running total */
716 pargs->handshakeTimeTotal += pargs->handshakeTimeOp;
717 }
718 pargs->numHandshakes++;
719
720 /* this works even if handshake failed due to cert chain invalid */
721 CFReleaseSafe(pargs->peerCerts);
722 if(!pargs->manualCertVerify) {
723 copyPeerCerts(ctx, &pargs->peerCerts);
724 }
725 else {
726 /* else fetched via SecTrust later */
727 pargs->peerCerts = NULL;
728 }
729
730 ortn = SSLCopyPeerTrust(ctx, &pargs->peerTrust);
731 if(ortn) {
732 printf("***SSLCopyPeerTrust error %d\n", (int)ortn);
733 pargs->peerTrust = NULL;
734 }
735
736 /* ditto */
737 SSLGetClientCertificateState(ctx, &pargs->certState);
738 #if TARGET_OS_MAC && MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
739 SSLGetNegotiatedClientAuthType(ctx, &pargs->authType);
740 #endif
741 SSLGetNegotiatedCipher(ctx, &pargs->negCipher);
742 SSLGetNegotiatedProtocolVersion(ctx, &pargs->negVersion);
743 CFReleaseSafe(pargs->dnList);
744 SSLCopyDistinguishedNames(ctx, &pargs->dnList);
745 pargs->sessionIDLength = MAX_SESSION_ID_LENGTH;
746 SSLGetResumableSessionInfo(ctx, &pargs->sessionWasResumed, pargs->sessionID,
747 &pargs->sessionIDLength);
748 if(pargs->manualCertVerify) {
749 OSStatus certRtn = sslEvaluateTrust(ctx, pargs->verbose, pargs->silent,
750 &pargs->peerCerts);
751 if(certRtn && !ortn ) {
752 ortn = certRtn;
753 }
754 }
755
756 if(ortn) {
757 if(!pargs->silent) {
758 printf("\n");
759 }
760 goto cleanup;
761 }
762
763 if(pargs->verbose) {
764 printf("...SSL handshake complete\n");
765 }
766
767 /* Write our GET request */
768 length = strlen(pargs->getMsg);
769 ortn = SSLWrite(ctx, pargs->getMsg, length, &actLen);
770 if(ortn) {
771 printf("***SSLWrite error: %d\n", (int)ortn);
772 } else if((actLen > 0) && pargs->dumpRxData) {
773 dumpAscii((uint8_t*)pargs->getMsg, actLen);
774 }
775
776 /*
777 * Try to snag RCV_BUF_SIZE bytes. Exit if (!keepConnected and we get any data
778 * at all), or (keepConnected and err != (none, wouldBlock)).
779 */
780 while (ortn == noErr) {
781 actLen = 0;
782 if(pargs->dumpRxData) {
783 size_t avail = 0;
784
785 ortn = SSLGetBufferedReadSize(ctx, &avail);
786 if(ortn) {
787 printf("***SSLGetBufferedReadSize error\n");
788 break;
789 }
790 if(avail != 0) {
791 printf("\n%d bytes available: ", (int)avail);
792 }
793 }
794 ortn = SSLRead(ctx, rcvBuf, RCV_BUF_SIZE, &actLen);
795 if((actLen == 0) && !pargs->silent) {
796 sslOutputDot();
797 }
798 if((actLen == 0) && (ortn == noErr)) {
799 printf("***Radar 2984932 confirmed***\n");
800 }
801 if (ortn == errSSLWouldBlock) {
802 /* for this loop, these are identical */
803 ortn = noErr;
804 }
805 if(ortn == errSSLServerAuthCompleted ||
806 ortn == errSSLClientCertRequested) {
807 /* should never get these once the handshake is complete */
808 printf("***SSLRead returned unexpected handshake error!\n");
809 }
810
811 if((actLen > 0) && pargs->dumpRxData) {
812 dumpAscii(rcvBuf, actLen);
813 }
814 if(ortn != noErr) {
815 /* connection closed by server or by error */
816 break;
817 }
818 if(!pargs->keepConnected && (actLen > 0)) {
819 /* good enough, we connected */
820 break;
821 }
822 }
823 if(!pargs->silent) {
824 printf("\n");
825 }
826
827 /* snag these again in case of renegotiate */
828 SSLGetClientCertificateState(ctx, &pargs->certState);
829 SSLGetNegotiatedCipher(ctx, &pargs->negCipher);
830 SSLGetNegotiatedProtocolVersion(ctx, &pargs->negVersion);
831 CFReleaseSafe(pargs->dnList);
832 SSLCopyDistinguishedNames(ctx, &pargs->dnList);
833
834 /* convert normal "shutdown" into zero err rtn */
835 if(ortn == errSSLClosedGraceful) {
836 ortn = noErr;
837 }
838 if((ortn == errSSLClosedNoNotify) && !pargs->requireNotify) {
839 /* relaxed disconnect rules */
840 ortn = noErr;
841 }
842 cleanup:
843 /*
844 * always do close, even on error - to flush outgoing write queue
845 */
846 OSStatus cerr = SSLClose(ctx);
847 if(ortn == noErr) {
848 ortn = cerr;
849 }
850 if(sock) {
851 endpointShutdown(sock);
852 }
853 if(ctx) {
854 SSLDisposeContext(ctx);
855 }
856 return ortn;
857 }
858
859 static void add_key(const void *key, const void *value, void *context) {
860 CFArrayAppendValue((CFMutableArrayRef)context, key);
861 }
862
863 static void showInfo(CFDictionaryRef info) {
864 CFIndex dict_count, key_ix, key_count;
865 CFMutableArrayRef keys = NULL;
866 CFIndex maxWidth = 20; /* Maybe precompute this or grab from context? */
867
868 dict_count = CFDictionaryGetCount(info);
869 keys = CFArrayCreateMutable(kCFAllocatorDefault, dict_count,
870 &kCFTypeArrayCallBacks);
871 CFDictionaryApplyFunction(info, add_key, keys);
872 key_count = CFArrayGetCount(keys);
873 CFArraySortValues(keys, CFRangeMake(0, key_count),
874 (CFComparatorFunction)CFStringCompare, 0);
875
876 for (key_ix = 0; key_ix < key_count; ++key_ix) {
877 CFStringRef key = (CFStringRef)CFArrayGetValueAtIndex(keys, key_ix);
878 CFTypeRef value = CFDictionaryGetValue(info, key);
879 CFMutableStringRef line = CFStringCreateMutable(NULL, 0);
880
881 CFStringAppend(line, key);
882 CFIndex jx;
883 for (jx = CFStringGetLength(key);
884 jx < maxWidth; ++jx) {
885 CFStringAppend(line, CFSTR(" "));
886 }
887 CFStringAppend(line, CFSTR(" : "));
888 if (CFStringGetTypeID() == CFGetTypeID(value)) {
889 CFStringAppend(line, (CFStringRef)value);
890 } else if (CFDateGetTypeID() == CFGetTypeID(value)) {
891 CFLocaleRef lc = CFLocaleCopyCurrent();
892 CFDateFormatterRef df = CFDateFormatterCreate(NULL, lc,
893 kCFDateFormatterFullStyle, kCFDateFormatterFullStyle);
894 CFDateRef date = (CFDateRef)value;
895 CFStringRef ds = CFDateFormatterCreateStringWithDate(NULL, df,
896 date);
897 CFStringAppend(line, ds);
898 CFRelease(ds);
899 CFRelease(df);
900 CFRelease(lc);
901 } else if (CFURLGetTypeID() == CFGetTypeID(value)) {
902 CFURLRef url = (CFURLRef)value;
903 CFStringAppend(line, CFSTR("<"));
904 CFStringAppend(line, CFURLGetString(url));
905 CFStringAppend(line, CFSTR(">"));
906 } else if (CFDataGetTypeID() == CFGetTypeID(value)) {
907 CFDataRef v_d = (CFDataRef)value;
908 CFStringRef v_s = CFStringCreateFromExternalRepresentation(
909 kCFAllocatorDefault, v_d, kCFStringEncodingUTF8);
910 if (v_s) {
911 CFStringAppend(line, CFSTR("/"));
912 CFStringAppend(line, v_s);
913 CFStringAppend(line, CFSTR("/ "));
914 CFRelease(v_s);
915 }
916 const uint8_t *bytes = CFDataGetBytePtr(v_d);
917 CFIndex len = CFDataGetLength(v_d);
918 for (jx = 0; jx < len; ++jx) {
919 CFStringAppendFormat(line, NULL, CFSTR("%.02X"), bytes[jx]);
920 }
921 } else {
922 CFStringAppendFormat(line, NULL, CFSTR("%@"), value);
923 }
924 print_line(line);
925 CFRelease(line);
926 }
927 CFRelease(keys);
928 }
929
930 static void showPeerTrust(SecTrustRef peerTrust, bool verbose) {
931 CFIndex numCerts;
932 CFIndex i;
933
934 if(peerTrust == NULL) {
935 return;
936 }
937 #if TARGET_OS_EMBEDDED
938 printf("\n=============== Peer Trust Properties ===============\n");
939 CFArrayRef plist = SecTrustCopyProperties(peerTrust);
940 if (plist) {
941 print_plist(plist);
942 CFRelease(plist);
943 }
944
945 printf("\n================== Peer Trust Info ==================\n");
946 CFDictionaryRef info = SecTrustCopyInfo(peerTrust);
947 if (info && CFDictionaryGetCount(info)) {
948 showInfo(info);
949 CFRelease(info);
950 }
951
952 numCerts = SecTrustGetCertificateCount(peerTrust);
953 for(i=0; i<numCerts; i++) {
954 plist = SecTrustCopySummaryPropertiesAtIndex(peerTrust, i);
955 printf("\n============= Peer Trust Cert %lu Summary =============\n\n", i);
956 print_plist(plist);
957 if (plist)
958 CFRelease(plist);
959 printf("\n============= Peer Trust Cert %lu Details =============\n\n", i);
960 plist = SecTrustCopyDetailedPropertiesAtIndex(peerTrust, i);
961 print_plist(plist);
962 if (plist)
963 CFRelease(plist);
964 printf("\n============= End of Peer Trust Cert %lu ==============\n", i);
965 }
966 #endif
967 }
968
969 static void showPeerCerts(
970 CFArrayRef peerCerts,
971 bool verbose)
972 {
973 CFIndex numCerts;
974 SecCertificateRef certRef;
975 CFIndex i;
976
977 if(peerCerts == NULL) {
978 return;
979 }
980 numCerts = CFArrayGetCount(peerCerts);
981 for(i=0; i<numCerts; i++) {
982 certRef = (SecCertificateRef)CFArrayGetValueAtIndex(peerCerts, i);
983 printf("\n==================== Peer Cert %lu ====================\n\n", i);
984 print_cert(certRef, verbose);
985 printf("\n================ End of Peer Cert %lu =================\n", i);
986 }
987 }
988
989 static void writePeerCerts(
990 CFArrayRef peerCerts,
991 const char *fileBase)
992 {
993 CFIndex numCerts;
994 SecCertificateRef certRef;
995 CFIndex i;
996 char fileName[100];
997
998 if(peerCerts == NULL) {
999 return;
1000 }
1001 numCerts = CFArrayGetCount(peerCerts);
1002 for(i=0; i<numCerts; i++) {
1003 sprintf(fileName, "%s%02d.cer", fileBase, (int)i);
1004 certRef = (SecCertificateRef)CFArrayGetValueAtIndex(peerCerts, i);
1005 CFDataRef derCert = SecCertificateCopyData(certRef);
1006 if (derCert) {
1007 writeFile(fileName, CFDataGetBytePtr(derCert),
1008 CFDataGetLength(derCert));
1009 }
1010 CFRelease(derCert);
1011 }
1012 printf("...wrote %lu certs to fileBase %s\n", numCerts, fileBase);
1013 }
1014
1015 static void writeDnList(
1016 CFArrayRef dnList,
1017 const char *fileBase)
1018 {
1019 CFIndex numDns;
1020 CFDataRef cfDn;
1021 CFIndex i;
1022 char fileName[100];
1023
1024 if(dnList == NULL) {
1025 return;
1026 }
1027 numDns = CFArrayGetCount(dnList);
1028 for(i=0; i<numDns; i++) {
1029 sprintf(fileName, "%s%02d.der", fileBase, (int)i);
1030 cfDn = (CFDataRef)CFArrayGetValueAtIndex(dnList, i);
1031 writeFile(fileName, CFDataGetBytePtr(cfDn), CFDataGetLength(cfDn));
1032 }
1033 printf("...wrote %lu RDNs to fileBase %s\n", numDns, fileBase);
1034 }
1035
1036 /*
1037 * Show result of an sslPing().
1038 * Assumes the following from sslPingArgs:
1039 *
1040 * verbose
1041 * tryVersion
1042 * acceptedProts
1043 * negVersion
1044 * negCipher
1045 * peerCerts
1046 * certState
1047 * authType
1048 * sessionWasResumed
1049 * sessionID
1050 * sessionIDLength
1051 * handshakeTime
1052 */
1053 static void showSSLResult(
1054 const sslPingArgs &pargs,
1055 OSStatus err,
1056 int displayPeerCerts,
1057 const char *fileBase, // non-NULL: write certs to file
1058 const char *dnFileBase) // non-NULL: write DNList to file
1059 {
1060 CFIndex numPeerCerts;
1061
1062 printf("\n");
1063
1064 if(pargs.acceptedProts) {
1065 printf(" Allowed SSL versions : %s\n", pargs.acceptedProts);
1066 }
1067 else {
1068 printf(" Attempted SSL version : %s\n",
1069 sslGetProtocolVersionString(pargs.tryVersion));
1070 }
1071
1072 printf(" Result : %s\n", sslGetSSLErrString(err));
1073 printf(" Negotiated SSL version : %s\n",
1074 sslGetProtocolVersionString(pargs.negVersion));
1075 printf(" Negotiated CipherSuite : %s\n",
1076 sslGetCipherSuiteString(pargs.negCipher));
1077 if(pargs.certState != kSSLClientCertNone) {
1078 printf(" Client Cert State : %s\n",
1079 sslGetClientCertStateString(pargs.certState));
1080 #if TARGET_OS_MAC && MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
1081 printf(" Client Auth Type : %s\n",
1082 sslGetClientAuthTypeString(pargs.authType));
1083 #endif
1084 }
1085 if(pargs.verbose) {
1086 printf(" Resumed Session : ");
1087 if(pargs.sessionWasResumed) {
1088 for(unsigned dex=0; dex<pargs.sessionIDLength; dex++) {
1089 printf("%02X ", pargs.sessionID[dex]);
1090 if(((dex % 8) == 7) && (dex != (pargs.sessionIDLength - 1))) {
1091 printf("\n ");
1092 }
1093 }
1094 printf("\n");
1095 }
1096 else {
1097 printf("NOT RESUMED\n");
1098 }
1099 printf(" Handshake time : %f seconds\n", pargs.handshakeTimeOp);
1100 }
1101 if(pargs.peerCerts == NULL) {
1102 numPeerCerts = 0;
1103 }
1104 else {
1105 numPeerCerts = CFArrayGetCount(pargs.peerCerts);
1106 }
1107 printf(" Number of peer certs : %lu\n", numPeerCerts);
1108 if(numPeerCerts != 0) {
1109 if (displayPeerCerts == 1) {
1110 showPeerCerts(pargs.peerCerts, false);
1111 } else if (displayPeerCerts == 2) {
1112 showPeerTrust(pargs.peerTrust, false);
1113 }
1114 if(fileBase != NULL) {
1115 writePeerCerts(pargs.peerCerts, fileBase);
1116 }
1117 }
1118 if(dnFileBase != NULL) {
1119 writeDnList(pargs.dnList, dnFileBase);
1120 }
1121
1122 printf("\n");
1123 }
1124
1125 static int verifyProtocol(
1126 bool verifyProt,
1127 SSLProtocol maxProtocol,
1128 SSLProtocol reqProtocol,
1129 SSLProtocol negProtocol)
1130 {
1131 if(!verifyProt) {
1132 return 0;
1133 }
1134 if(reqProtocol > maxProtocol) {
1135 /* known not to support this attempt, relax */
1136 reqProtocol = maxProtocol;
1137 }
1138 if(reqProtocol != negProtocol) {
1139 printf("***Expected protocol %s; negotiated %s\n",
1140 sslGetProtocolVersionString(reqProtocol),
1141 sslGetProtocolVersionString(negProtocol));
1142 return 1;
1143 }
1144 else {
1145 return 0;
1146 }
1147 }
1148
1149 static int verifyClientCertState(
1150 bool verifyCertState,
1151 SSLClientCertificateState expectState,
1152 SSLClientCertificateState gotState)
1153 {
1154 if(!verifyCertState) {
1155 return 0;
1156 }
1157 if(expectState == gotState) {
1158 return 0;
1159 }
1160 printf("***Expected clientCertState %s; got %s\n",
1161 sslGetClientCertStateString(expectState),
1162 sslGetClientCertStateString(gotState));
1163 return 1;
1164 }
1165
1166 /*
1167 * Free everything allocated by sslPing in an sslPingArgs.
1168 * Mainly for looping and malloc debugging.
1169 */
1170 static void freePingArgs(
1171 sslPingArgs *pargs)
1172 {
1173 CFReleaseNull(pargs->peerCerts);
1174 CFReleaseNull(pargs->peerTrust);
1175 CFReleaseNull(pargs->dnList);
1176 /* more, later, for client retry/identity fetch */
1177 }
1178
1179 static SSLProtocol strToProt(
1180 const char *c, // 2, 3, t, tls10, tls11, tls12
1181 char **argv)
1182 {
1183 if (c == NULL)
1184 return kSSLProtocolUnknown;
1185
1186 switch(c[0]) {
1187 case '2':
1188 return kSSLProtocol2;
1189 case '3':
1190 return kSSLProtocol3;
1191 case 't':
1192 if (c[1] == '\0')
1193 return kTLSProtocol1;
1194 if (c[1] == 'l' && c[2] == 's' && c[3] == '1') {
1195 if (c[4] == '0')
1196 return kTLSProtocol1;
1197 if (c[4] == '1')
1198 return kTLSProtocol11;
1199 if (c[4] == '2')
1200 return kTLSProtocol12;
1201 }
1202 default:
1203 usage(argv);
1204 }
1205 /* NOT REACHED */
1206 return kSSLProtocolUnknown;
1207 }
1208
1209 int main(int argc, char **argv)
1210 {
1211 OSStatus err;
1212 int arg;
1213 char *argp;
1214 char getMsg[300];
1215 char fullFileBase[100];
1216 int ourRtn = 0; // exit status - sum of all errors
1217 unsigned loop;
1218 SecKeychainRef serverKc = nil;
1219 SecKeychainRef encryptKc = nil;
1220 sslPingArgs pargs;
1221
1222 /* user-spec'd parameters */
1223 char *getPath = (char *)DEFAULT_PATH;
1224 char *fileBase = NULL;
1225 bool displayCerts = false;
1226 bool doSslV2 = false;
1227 bool doSslV3 = false;
1228 bool doTlsV1 = true;
1229 bool doTlsV11 = true;
1230 bool doTlsV12 = true;
1231 bool protXOnly = false; // kSSLProtocol3Only, kTLSProtocol1Only
1232 bool doProtUnknown = false;
1233 unsigned loopCount = 1;
1234 bool doPause = false;
1235 bool pauseFirstLoop = false;
1236 bool verifyProt = false;
1237 SSLProtocol maxProtocol = kTLSProtocol12; // for verifying negotiated
1238 // protocol
1239 char *acceptedProts = NULL;
1240 char *keyChainName = NULL;
1241 char *encryptKeyChainName = NULL;
1242 char *getMsgSpec = NULL;
1243 bool vfyCertState = false;
1244 SSLClientCertificateState expectCertState = kSSLClientCertNone;
1245 bool displayHandshakeTimes = false;
1246 bool completeCertChain = false;
1247 char *dnFileBase = NULL;
1248
1249 /* special case - one arg of "h" or "-h" or "hv" */
1250 if(argc == 2) {
1251 if((strcmp(argv[1], "h") == 0) || (strcmp(argv[1], "-h") == 0)) {
1252 usage(argv);
1253 }
1254 if(strcmp(argv[1], "hv") == 0) {
1255 usageVerbose(argv);
1256 }
1257 }
1258
1259 /* set up defaults */
1260 memset(&pargs, 0, sizeof(sslPingArgs));
1261 pargs.hostName = DEFAULT_HOST;
1262 pargs.port = DEFAULT_PORT;
1263 pargs.resumableEnable = true;
1264 pargs.argv = argv;
1265
1266 for(arg=1; arg<argc; arg++) {
1267 argp = argv[arg];
1268 if(arg == 1) {
1269 /* first arg, is always hostname; '-' means default */
1270 if(argp[0] != '-') {
1271 pargs.hostName = argp;
1272 }
1273 continue;
1274 }
1275 if(argp[0] == '/') {
1276 /* path always starts with leading slash */
1277 getPath = argp;
1278 continue;
1279 }
1280 /* options */
1281 switch(argp[0]) {
1282 case 'e':
1283 pargs.allowExpired = true;
1284 break;
1285 case 'E':
1286 pargs.allowExpiredRoot = true;
1287 break;
1288 case 'x':
1289 pargs.disableCertVerify = true;
1290 break;
1291 case 'M':
1292 pargs.disableCertVerify = true; // implied
1293 pargs.manualCertVerify = true;
1294 break;
1295 case 'I':
1296 pargs.interactiveAuth = true;
1297 break;
1298 case 'a':
1299 if(++arg == argc) {
1300 /* requires another arg */
1301 usage(argv);
1302 }
1303 pargs.anchorFile = argv[arg];
1304 break;
1305 case 'A':
1306 if(++arg == argc) {
1307 /* requires another arg */
1308 usage(argv);
1309 }
1310 pargs.anchorFile = argv[arg];
1311 pargs.replaceAnchors = true;
1312 break;
1313 case 'Z':
1314 if(++arg == argc) {
1315 /* requires another arg */
1316 usage(argv);
1317 }
1318 pargs.trustedLeafFile = argv[arg];
1319 break;
1320 case 'r':
1321 pargs.allowAnyRoot = true;
1322 break;
1323 case 'd':
1324 pargs.dumpRxData = true;
1325 break;
1326 case 'c':
1327 displayCerts = 1;
1328 if (argp[1] == 'c')
1329 ++displayCerts;
1330 break;
1331 case 'f':
1332 if(++arg == argc) {
1333 /* requires another arg */
1334 usage(argv);
1335 }
1336 fileBase = argv[arg];
1337 break;
1338 case 'C':
1339 pargs.cipherRestrict = argp[2];
1340 break;
1341 case 'S':
1342 pargs.showCipherSuites = true;
1343 break;
1344 case '2':
1345 doSslV3 = doTlsV1 = doTlsV11 = false;
1346 doSslV2 = true;
1347 break;
1348 case '3':
1349 doSslV2 = doTlsV1 = doTlsV11 = doTlsV12 = false;
1350 doSslV3 = true;
1351 break;
1352 case 't':
1353 if (argp[1] == 'l' && argp[2] == 's' && argp[3] == '1') {
1354 if (argp[4] == '0') {
1355 doSslV2 = doSslV3 = doTlsV11 = doTlsV12 = false;
1356 doTlsV1 = true;
1357 break;
1358 }
1359 if (argp[4] == '1') {
1360 doSslV2 = doSslV3 = doTlsV1 = doTlsV12 = false;
1361 doTlsV11 = true;
1362 break;
1363 }
1364 else if (argp[4] == '2') {
1365 doSslV2 = doSslV3 = doTlsV1 = doTlsV11 = false;
1366 doTlsV12 = true;
1367 break;
1368 }
1369 }
1370 if (argp[1] != '\0') {
1371 usage(argv);
1372 }
1373 doSslV2 = doSslV3 = doTlsV11 = doTlsV12 = false;
1374 doTlsV1 = true;
1375 break;
1376 case 'L':
1377 doSslV2 = doSslV3 = doTlsV1 = doTlsV11 = doTlsV12 = true;
1378 break;
1379 case 'o':
1380 protXOnly = true;
1381 break;
1382 case 'u':
1383 doSslV2 = doSslV3 = doTlsV1 = doTlsV11 = doTlsV12 = false;
1384 doProtUnknown = true;
1385 break;
1386 case 'K':
1387 pargs.keepConnected = true;
1388 break;
1389 case 'n':
1390 pargs.requireNotify = true;
1391 pargs.keepConnected = true;
1392 break;
1393 case 'R':
1394 pargs.resumableEnable = false;
1395 break;
1396 case 'b':
1397 pargs.nonBlocking = true;
1398 break;
1399 case 'v':
1400 verifyProt = true;
1401 break;
1402 case 'm':
1403 if(argp[1] != '=') {
1404 usage(argv);
1405 }
1406 verifyProt = true; // implied
1407 maxProtocol = strToProt(&argp[2], argv);
1408 break;
1409 case 'g':
1410 if(argp[1] != '=') {
1411 usage(argv);
1412 }
1413 acceptedProts = &argp[2];
1414 doSslV3 = doSslV2 = doTlsV1 = doTlsV11 = doTlsV12 = false;
1415 break;
1416 case 'l':
1417 loopCount = atoi(&argp[2]);
1418 if(loopCount == 0) {
1419 printf("***bad loopCount\n");
1420 usage(argv);
1421 }
1422 break;
1423 case 'P':
1424 pargs.port = atoi(&argp[2]);
1425 break;
1426 case 'H':
1427 pargs.allowHostnameSpoof = true;
1428 break;
1429 case 'F':
1430 pargs.vfyHostName = &argp[2];
1431 break;
1432 case 'k':
1433 keyChainName = &argp[2];
1434 break;
1435 case 'y':
1436 encryptKeyChainName = &argp[2];
1437 break;
1438 case 'G':
1439 getMsgSpec = &argp[2];
1440 break;
1441 case 'T':
1442 if(argp[1] != '=') {
1443 usage(argv);
1444 }
1445 vfyCertState = true;
1446 switch(argp[2]) {
1447 case 'n':
1448 expectCertState = kSSLClientCertNone;
1449 break;
1450 case 'r':
1451 expectCertState = kSSLClientCertRequested;
1452 break;
1453 case 's':
1454 expectCertState = kSSLClientCertSent;
1455 break;
1456 case 'j':
1457 expectCertState = kSSLClientCertRejected;
1458 break;
1459 default:
1460 usage(argv);
1461 }
1462 break;
1463 case 'z':
1464 pargs.password = &argp[2];
1465 break;
1466 case 'p':
1467 doPause = true;
1468 break;
1469 case '7':
1470 pauseFirstLoop = true;
1471 break;
1472 case 'q':
1473 pargs.quiet = true;
1474 break;
1475 case 'V':
1476 pargs.verbose = true;
1477 break;
1478 case 's':
1479 pargs.silent = pargs.quiet = true;
1480 break;
1481 case 'N':
1482 displayHandshakeTimes = true;
1483 break;
1484 case '8':
1485 completeCertChain = true;
1486 break;
1487 case 'i':
1488 pargs.sessionCacheTimeout = atoi(&argp[2]);
1489 break;
1490 case '4':
1491 pargs.disableAnonCiphers = true;
1492 break;
1493 case 'D':
1494 if(++arg == argc) {
1495 /* requires another arg */
1496 usage(argv);
1497 }
1498 dnFileBase = argv[arg];
1499 break;
1500 case 'h':
1501 if(pargs.verbose || (argp[1] == 'v')) {
1502 usageVerbose(argv);
1503 }
1504 else {
1505 usage(argv);
1506 }
1507 default:
1508 usage(argv);
1509 }
1510 }
1511 if(getMsgSpec) {
1512 pargs.getMsg = getMsgSpec;
1513 }
1514 else {
1515 sprintf(getMsg, "%s %s %s",
1516 DEFAULT_GETMSG, getPath, DEFAULT_GET_SUFFIX);
1517 pargs.getMsg = getMsg;
1518 }
1519
1520 #if NO_SERVER
1521 # if DEBUG
1522 securityd_init();
1523 # endif
1524 #endif
1525
1526 /* get client cert and optional encryption cert as CFArrayRef */
1527 if(keyChainName) {
1528 pargs.clientCerts = getSslCerts(keyChainName, false, completeCertChain,
1529 pargs.anchorFile, &serverKc);
1530 if(pargs.clientCerts == nil) {
1531 exit(1);
1532 }
1533 #ifdef USE_CDSA_CRYPTO
1534 if(pargs.password) {
1535 OSStatus ortn = SecKeychainUnlock(serverKc,
1536 strlen(pargs.password), pargs.password, true);
1537 if(ortn) {
1538 printf("SecKeychainUnlock returned %d\n", (int)ortn);
1539 /* oh well */
1540 }
1541 }
1542 #endif
1543 }
1544 if(encryptKeyChainName) {
1545 pargs.encryptClientCerts = getSslCerts(encryptKeyChainName, true,
1546 completeCertChain, pargs.anchorFile, &encryptKc);
1547 if(pargs.encryptClientCerts == nil) {
1548 exit(1);
1549 }
1550 }
1551 signal(SIGPIPE, sigpipe);
1552
1553 for(loop=0; loop<loopCount; loop++) {
1554 /*
1555 * One pass for each protocol version, skipping any explicit version if
1556 * an attempt at a higher version and succeeded in doing so successfully fell
1557 * back.
1558 */
1559 if(doTlsV12) {
1560 pargs.tryVersion = kTLSProtocol12;
1561 pargs.acceptedProts = NULL;
1562 if(!pargs.silent) {
1563 printf("Connecting to host %s with TLS V1.2\n", pargs.hostName);
1564 }
1565 fflush(stdout);
1566 err = sslPing(&pargs);
1567 if(err) {
1568 ourRtn++;
1569 }
1570 if(!pargs.quiet) {
1571 if(fileBase) {
1572 sprintf(fullFileBase, "%s_v3.1", fileBase);
1573 }
1574 showSSLResult(pargs,
1575 err,
1576 displayCerts,
1577 fileBase ? fullFileBase : NULL,
1578 dnFileBase);
1579 }
1580 freePingArgs(&pargs);
1581 if(!err) {
1582 /* deal with fallbacks, skipping redundant tests */
1583 switch(pargs.negVersion) {
1584 case kTLSProtocol11:
1585 doTlsV11 =false;
1586 break;
1587 case kTLSProtocol1:
1588 doTlsV11 =false;
1589 doTlsV1 =false;
1590 break;
1591 case kSSLProtocol3:
1592 doTlsV11 =false;
1593 doTlsV1 =false;
1594 doSslV3 = false;
1595 break;
1596 case kSSLProtocol2:
1597 doTlsV11 =false;
1598 doTlsV1 =false;
1599 doSslV3 = false;
1600 doSslV2 = false;
1601 break;
1602 default:
1603 break;
1604 }
1605 ourRtn += verifyProtocol(verifyProt, maxProtocol, kTLSProtocol12,
1606 pargs.negVersion);
1607 }
1608 /* note we do this regardless since the client state might be
1609 * the cause of a failure */
1610 ourRtn += verifyClientCertState(vfyCertState, expectCertState,
1611 pargs.certState);
1612 }
1613 if(doTlsV11) {
1614 pargs.tryVersion = kTLSProtocol11;
1615 pargs.acceptedProts = NULL;
1616 if(!pargs.silent) {
1617 printf("Connecting to host %s with TLS V1.1\n", pargs.hostName);
1618 }
1619 fflush(stdout);
1620 err = sslPing(&pargs);
1621 if(err) {
1622 ourRtn++;
1623 }
1624 if(!pargs.quiet) {
1625 if(fileBase) {
1626 sprintf(fullFileBase, "%s_v3.1", fileBase);
1627 }
1628 showSSLResult(pargs,
1629 err,
1630 displayCerts,
1631 fileBase ? fullFileBase : NULL,
1632 dnFileBase);
1633 }
1634 freePingArgs(&pargs);
1635 if(!err) {
1636 /* deal with fallbacks, skipping redundant tests */
1637 switch(pargs.negVersion) {
1638 case kTLSProtocol1:
1639 doTlsV1 =false;
1640 break;
1641 case kSSLProtocol3:
1642 doTlsV1 =false;
1643 doSslV3 = false;
1644 break;
1645 case kSSLProtocol2:
1646 doTlsV1 =false;
1647 doSslV3 = false;
1648 doSslV2 = false;
1649 break;
1650 default:
1651 break;
1652 }
1653 ourRtn += verifyProtocol(verifyProt, maxProtocol, kTLSProtocol11,
1654 pargs.negVersion);
1655 }
1656 /* note we do this regardless since the client state might be
1657 * the cause of a failure */
1658 ourRtn += verifyClientCertState(vfyCertState, expectCertState,
1659 pargs.certState);
1660 }
1661 if(doTlsV1) {
1662 pargs.tryVersion =
1663 protXOnly ? kTLSProtocol1Only : kTLSProtocol1;
1664 pargs.acceptedProts = NULL;
1665 if(!pargs.silent) {
1666 printf("Connecting to host %s with TLS V1.0\n", pargs.hostName);
1667 }
1668 fflush(stdout);
1669 err = sslPing(&pargs);
1670 if(err) {
1671 ourRtn++;
1672 }
1673 if(!pargs.quiet) {
1674 if(fileBase) {
1675 sprintf(fullFileBase, "%s_v3.1", fileBase);
1676 }
1677 showSSLResult(pargs,
1678 err,
1679 displayCerts,
1680 fileBase ? fullFileBase : NULL,
1681 dnFileBase);
1682 }
1683 freePingArgs(&pargs);
1684 if(!err) {
1685 /* deal with fallbacks, skipping redundant tests */
1686 switch(pargs.negVersion) {
1687 case kSSLProtocol3:
1688 doSslV3 = false;
1689 break;
1690 case kSSLProtocol2:
1691 doSslV3 = false;
1692 doSslV2 = false;
1693 break;
1694 default:
1695 break;
1696 }
1697 ourRtn += verifyProtocol(verifyProt, maxProtocol, kTLSProtocol1,
1698 pargs.negVersion);
1699 }
1700 /* note we do this regardless since the client state might be
1701 * the cause of a failure */
1702 ourRtn += verifyClientCertState(vfyCertState, expectCertState,
1703 pargs.certState);
1704 }
1705 if(doSslV3) {
1706 pargs.tryVersion = protXOnly ? kSSLProtocol3Only : kSSLProtocol3;
1707 pargs.acceptedProts = NULL;
1708 if(!pargs.silent) {
1709 printf("Connecting to host %s with SSL V3\n", pargs.hostName);
1710 }
1711 fflush(stdout);
1712 err = sslPing(&pargs);
1713 if(err) {
1714 ourRtn++;
1715 }
1716 if(!pargs.quiet) {
1717 if(fileBase) {
1718 sprintf(fullFileBase, "%s_v3.0", fileBase);
1719 }
1720 showSSLResult(pargs,
1721 err,
1722 displayCerts,
1723 fileBase ? fullFileBase : NULL,
1724 dnFileBase);
1725 }
1726 freePingArgs(&pargs);
1727 if(!err) {
1728 /* deal with fallbacks, skipping redundant tests */
1729 switch(pargs.negVersion) {
1730 case kSSLProtocol2:
1731 doSslV2 = false;
1732 break;
1733 default:
1734 break;
1735 }
1736 ourRtn += verifyProtocol(verifyProt, maxProtocol, kSSLProtocol3,
1737 pargs.negVersion);
1738 }
1739 /* note we do this regardless since the client state might be
1740 * the cause of a failure */
1741 ourRtn += verifyClientCertState(vfyCertState, expectCertState,
1742 pargs.certState);
1743 }
1744
1745 if(doSslV2) {
1746 if(fileBase) {
1747 sprintf(fullFileBase, "%s_v2", fileBase);
1748 }
1749 if(!pargs.silent) {
1750 printf("Connecting to host %s with SSL V2\n", pargs.hostName);
1751 }
1752 fflush(stdout);
1753 pargs.tryVersion = kSSLProtocol2;
1754 pargs.acceptedProts = NULL;
1755 err = sslPing(&pargs);
1756 if(err) {
1757 ourRtn++;
1758 }
1759 if(!pargs.quiet) {
1760 if(fileBase) {
1761 sprintf(fullFileBase, "%s_v2", fileBase);
1762 }
1763 showSSLResult(pargs,
1764 err,
1765 displayCerts,
1766 fileBase ? fullFileBase : NULL,
1767 dnFileBase);
1768 }
1769 freePingArgs(&pargs);
1770 if(!err) {
1771 ourRtn += verifyProtocol(verifyProt, maxProtocol, kSSLProtocol2,
1772 pargs.negVersion);
1773 }
1774 /* note we do this regardless since the client state might be
1775 * the cause of a failure */
1776 ourRtn += verifyClientCertState(vfyCertState, expectCertState,
1777 pargs.certState);
1778 }
1779 if(doProtUnknown) {
1780 if(!pargs.silent) {
1781 printf("Connecting to host %s with kSSLProtocolUnknown\n",
1782 pargs.hostName);
1783 }
1784 fflush(stdout);
1785 pargs.tryVersion = kSSLProtocolUnknown;
1786 pargs.acceptedProts = NULL;
1787 err = sslPing(&pargs);
1788 if(err) {
1789 ourRtn++;
1790 }
1791 if(!pargs.quiet) {
1792 if(fileBase) {
1793 sprintf(fullFileBase, "%s_def", fileBase);
1794 }
1795 showSSLResult(pargs,
1796 err,
1797 displayCerts,
1798 fileBase ? fullFileBase : NULL,
1799 dnFileBase);
1800 }
1801 freePingArgs(&pargs);
1802 }
1803 if(acceptedProts != NULL) {
1804 pargs.acceptedProts = acceptedProts;
1805 pargs.tryVersion = kSSLProtocolUnknown; // not used
1806 if(!pargs.silent) {
1807 printf("Connecting to host %s with acceptedProts %s\n",
1808 pargs.hostName, pargs.acceptedProts);
1809 }
1810 fflush(stdout);
1811 err = sslPing(&pargs);
1812 if(err) {
1813 ourRtn++;
1814 }
1815 if(!pargs.quiet) {
1816 if(fileBase) {
1817 sprintf(fullFileBase, "%s_def", fileBase);
1818 }
1819 showSSLResult(pargs,
1820 err,
1821 displayCerts,
1822 fileBase ? fullFileBase : NULL,
1823 dnFileBase);
1824 }
1825 freePingArgs(&pargs);
1826 }
1827 if(doPause ||
1828 (pauseFirstLoop &&
1829 /* pause after first, before last to grab trace */
1830 ((loop == 0) || (loop == loopCount - 1))
1831 )
1832 ) {
1833 char resp;
1834 fpurge(stdin);
1835 printf("a to abort, c to continue: ");
1836 resp = getchar();
1837 if(resp == 'a') {
1838 break;
1839 }
1840 }
1841 } /* main loop */
1842 if(displayHandshakeTimes) {
1843 CFAbsoluteTime totalTime;
1844 unsigned numHandshakes;
1845 if(pargs.numHandshakes == 1) {
1846 /* just display the first one */
1847 totalTime = pargs.handshakeTimeFirst;
1848 numHandshakes = 1;
1849 }
1850 else {
1851 /* skip the first one */
1852 totalTime = pargs.handshakeTimeTotal;
1853 numHandshakes = pargs.numHandshakes - 1;
1854 }
1855 if(numHandshakes != 0) {
1856 printf(" %u handshakes in %f seconds; %f seconds per handshake\n",
1857 numHandshakes, totalTime,
1858 (totalTime / numHandshakes));
1859 }
1860 }
1861 //printCertShutdown();
1862 if(ourRtn) {
1863 printf("===%s exiting with %d %s for host %s\n", argv[0], ourRtn,
1864 (ourRtn > 1) ? "errors" : "error", pargs.hostName);
1865 }
1866 return ourRtn;
1867
1868 }
1869
1870