]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 | 1 | /* |
d8f41ccd | 2 | * Copyright (c) 2008-2013 Apple Inc. All Rights Reserved. |
b1ab9ed8 A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * This file contains Original Code and/or Modifications of Original Code | |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. Please obtain a copy of the License at | |
10 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
11 | * file. | |
12 | * | |
13 | * The Original Code and all software distributed under the License are | |
14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
18 | * Please see the License for the specific language governing rights and | |
19 | * limitations under the License. | |
20 | * | |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | */ | |
23 | ||
24 | /* | |
25 | * Trivial SSL server example, using SecureTransport / OS X version. | |
26 | * | |
d8f41ccd | 27 | * Written by Doug Mitchell. |
b1ab9ed8 | 28 | */ |
b1ab9ed8 A |
29 | #include <Security/SecureTransport.h> |
30 | #include <Security/SecureTransportPriv.h> | |
31 | #include "sslAppUtils.h" | |
32 | #include "ioSock.h" | |
d8f41ccd | 33 | #include "utilities/fileIo.h" |
b1ab9ed8 | 34 | |
427c49bc | 35 | #include <Security/SecBase.h> |
d8f41ccd | 36 | |
b1ab9ed8 A |
37 | #include <stdio.h> |
38 | #include <stdlib.h> | |
39 | #include <string.h> | |
40 | #include <time.h> | |
41 | #include <ctype.h> | |
42 | #include <sys/param.h> | |
43 | ||
44 | #include <Security/Security.h> | |
45 | #include <Security/SecCertificatePriv.h> | |
46 | ||
47 | #include <CoreFoundation/CoreFoundation.h> | |
b54c578e | 48 | #include "SecurityTool/sharedTool/print_cert.h" |
b1ab9ed8 A |
49 | |
50 | #if NO_SERVER | |
7fb2cbd2 | 51 | #include "keychain/securityd/spi.h" |
b1ab9ed8 A |
52 | #endif |
53 | ||
54 | /* Set true when PR-3074739 is merged to TOT */ | |
55 | #define SET_DH_PARAMS_ENABLE 1 | |
56 | ||
57 | /* true when using SSLCopyPeerCertificates() per Radar 3311892 */ | |
58 | #define USE_COPY_PEER_CERTS 1 | |
59 | ||
60 | /* | |
d8f41ccd | 61 | * Defaults, overridable by user. |
b1ab9ed8 A |
62 | */ |
63 | #define SERVER_MESSAGE "HTTP/1.0 200 OK\015\012Content-Type: text/html\015\012\015\012" \ | |
64 | "<HTML><HEAD><TITLE>SecureTransport Test Server</TITLE></HEAD>" \ | |
65 | "<BODY><H2>Secure connection established.</H2>" \ | |
66 | "Message from the 'sslServer' sample application.\015\012</BODY>" \ | |
67 | "</HTML>\015\012" | |
68 | ||
69 | /* For ease of debugging, pick a non-privileged port */ | |
70 | #define DEFAULT_PORT 1200 | |
71 | // #define DEFAULT_PORT 443 | |
72 | ||
73 | #define DEFAULT_HOST "localhost" | |
74 | ||
75 | #define DEFAULT_KC "certkc" | |
76 | ||
77 | static void usage(char **argv) | |
78 | { | |
79 | printf("Usage: %s [option ...]\n", argv[0]); | |
80 | printf("Options:\n"); | |
81 | printf(" P=port Port to listen on; default is %d\n", DEFAULT_PORT); | |
82 | printf(" k=keychain Contains server cert and keys.\n"); | |
83 | printf(" y=keychain Encryption-only cert and keys.\n"); | |
84 | printf(" e Allow Expired Certs\n"); | |
85 | printf(" r Allow any root cert\n"); | |
86 | printf(" E Allow Expired Roots\n"); | |
87 | printf(" x Disable Cert Verification\n"); | |
88 | printf(" f=fileBase Write Peer Certs to fileBase*\n"); | |
89 | printf(" c Display peer certs\n"); | |
90 | printf(" d Display received data\n"); | |
91 | printf(" C=cipherSuite (e=40-bit d=DES D=40-bit DES 3=3DES 4=RC4 $=40-bit RC4\n" | |
92 | " 2=RC2 a=AES128 A=AES256 h=DH H=Anon DH r=DHE/RSA s=DH/DSS\n" | |
93 | " n=RSA/NULL\n"); | |
94 | printf(" 2 SSLv2 only (default is best fit)\n"); | |
95 | printf(" 3 SSLv3 only (default is best fit)\n"); | |
96 | printf(" t TLSv1 only (default is best fit)\n"); | |
97 | printf(" o TLSv1, SSLv3 use kSSLProtocol__X__Only\n"); | |
98 | printf(" g={prot...} Specify legal protocols; prot = any combo of [23t]\n"); | |
99 | printf(" T=[nrsj] Verify client cert state = " | |
100 | "none/requested/sent/rejected\n"); | |
101 | printf(" R Disable resumable session support\n"); | |
102 | printf(" i=timeout Session cache timeout\n"); | |
103 | printf(" u=[nat] Authentication: n=never; a=always; t=try\n"); | |
104 | printf(" b Non-blocking I/O\n"); | |
105 | printf(" a fileNmae Add fileName to list of trusted roots\n"); | |
106 | printf(" A fileName fileName is ONLY trusted root\n"); | |
107 | printf(" U filename Add filename to acceptable DNList (multiple times OK)\n"); | |
108 | printf(" D filename Diffie-Hellman parameters from filename\n"); | |
109 | printf(" z=password Unlock server keychain with password.\n"); | |
d8f41ccd | 110 | printf(" H Do SecIndentityRef search instead of specific keychain\n"); |
b1ab9ed8 A |
111 | printf(" M Complete cert chain (default assumes that our identity is root)\n"); |
112 | printf(" 4 Disable anonymous ciphers\n"); | |
113 | printf(" p Pause after each phase\n"); | |
114 | printf(" l[=loops] Loop, performing multiple transactions\n"); | |
115 | printf(" q Quiet/diagnostic mode (site names and errors only)\n"); | |
116 | printf(" h Help\n"); | |
117 | exit(1); | |
118 | } | |
119 | ||
d8f41ccd | 120 | /* snag a copy of current connection's peer certs so we can |
b1ab9ed8 A |
121 | * examine them later after the connection is closed */ |
122 | static OSStatus copyPeerCerts( | |
123 | SSLContext *ctx, | |
124 | CFArrayRef *peerCerts) // mallocd & RETURNED | |
125 | { | |
126 | #if USE_COPY_PEER_CERTS | |
127 | OSStatus ortn = SSLCopyPeerCertificates(ctx, peerCerts); | |
128 | #else | |
129 | OSStatus ortn = SSLGetPeerCertificates(ctx, peerCerts); | |
130 | #endif | |
131 | if(ortn) { | |
132 | printf("***Error obtaining peer certs: %s\n", | |
133 | sslGetSSLErrString(ortn)); | |
134 | } | |
135 | return ortn; | |
136 | } | |
137 | ||
138 | /* free the cert array obtained via SSLGetPeerCertificates() */ | |
139 | static void freePeerCerts( | |
140 | CFArrayRef peerCerts) | |
141 | { | |
142 | if(peerCerts == NULL) { | |
143 | return; | |
144 | } | |
d8f41ccd | 145 | |
b1ab9ed8 | 146 | #if USE_COPY_PEER_CERTS |
d8f41ccd | 147 | |
b1ab9ed8 A |
148 | /* Voila! Problem fixed. */ |
149 | CFRelease(peerCerts); | |
150 | return; | |
d8f41ccd A |
151 | |
152 | #else | |
b1ab9ed8 A |
153 | |
154 | CFIndex numCerts; | |
155 | SecCertificateRef certData; | |
156 | CFIndex i; | |
d8f41ccd | 157 | |
b1ab9ed8 A |
158 | numCerts = CFArrayGetCount(peerCerts); |
159 | for(i=0; i<numCerts; i++) { | |
160 | certData = (SecCertificateRef)CFArrayGetValueAtIndex(peerCerts, i); | |
161 | CFRelease(certData); | |
162 | } | |
163 | CFRelease(peerCerts); | |
164 | #endif | |
d8f41ccd | 165 | } |
b1ab9ed8 A |
166 | |
167 | /* print reply received from server */ | |
168 | static void dumpAscii( | |
d8f41ccd A |
169 | uint8_t *rcvBuf, |
170 | size_t len) | |
b1ab9ed8 A |
171 | { |
172 | char *cp = (char *)rcvBuf; | |
173 | uint32_t i; | |
174 | char c; | |
d8f41ccd | 175 | |
b1ab9ed8 A |
176 | for(i=0; i<len; i++) { |
177 | c = *cp++; | |
178 | if(c == '\0') { | |
179 | break; | |
180 | } | |
181 | switch(c) { | |
182 | case '\n': | |
183 | printf("\\n"); | |
184 | break; | |
185 | case '\r': | |
186 | printf("\\r"); | |
187 | break; | |
188 | default: | |
189 | if(isprint(c) && (c != '\n')) { | |
190 | printf("%c", c); | |
191 | } | |
192 | else { | |
193 | printf("<%02X>", ((unsigned)c) & 0xff); | |
194 | } | |
195 | break; | |
196 | } | |
197 | ||
198 | } | |
199 | printf("\n"); | |
200 | } | |
201 | ||
d8f41ccd | 202 | static void doPause(const char *prompt) { |
b1ab9ed8 A |
203 | if(prompt) { |
204 | printf("%s. ", prompt); | |
205 | } | |
206 | fpurge(stdin); | |
207 | printf("Continue (n/anything)? "); | |
208 | char c = getchar(); | |
209 | if(c == 'n') { | |
210 | exit(0); | |
211 | } | |
212 | } | |
213 | ||
214 | /* | |
d8f41ccd A |
215 | * Perform one SSL diagnostic server-side session. Returns nonzero on error. |
216 | * Normally no output to stdout except initial "waiting for connection" message, | |
217 | * unless there is a really screwed up error (i.e., something not directly related | |
218 | * to the SSL connection). | |
b1ab9ed8 A |
219 | */ |
220 | #define RCV_BUF_SIZE 256 | |
221 | ||
222 | static OSStatus sslServe( | |
223 | otSocket listenSock, | |
224 | unsigned short portNum, | |
225 | SSLProtocol tryVersion, // only used if acceptedProts NULL | |
226 | const char *acceptedProts, | |
227 | CFArrayRef serverCerts, // required | |
228 | char *password, // optional | |
b1ab9ed8 A |
229 | bool allowExpired, |
230 | bool allowAnyRoot, | |
231 | bool allowExpiredRoot, | |
232 | bool disableCertVerify, | |
233 | char *anchorFile, | |
234 | bool replaceAnchors, | |
235 | char cipherRestrict, // '2', 'd'. etc...'\0' for no | |
236 | // restriction | |
237 | SSLAuthenticate authenticate, | |
d8f41ccd | 238 | unsigned char *dhParams, // optional D-H parameters |
b1ab9ed8 | 239 | unsigned dhParamsLen, |
d8f41ccd | 240 | CFArrayRef acceptableDNList, // optional |
b1ab9ed8 A |
241 | bool resumableEnable, |
242 | uint32_t sessionCacheTimeout,// optional | |
243 | bool disableAnonCiphers, | |
244 | bool silent, // no stdout | |
245 | bool pause, | |
246 | SSLProtocol *negVersion, // RETURNED | |
247 | SSLCipherSuite *negCipher, // RETURNED | |
248 | SSLClientCertificateState *certState, // RETURNED | |
249 | Boolean *sessionWasResumed, // RETURNED | |
250 | unsigned char *sessionID, // mallocd by caller, RETURNED | |
251 | size_t *sessionIDLength, // RETURNED | |
252 | CFArrayRef *peerCerts, // mallocd & RETURNED | |
253 | char **argv) | |
254 | { | |
255 | otSocket acceptSock; | |
256 | PeerSpec peerId; | |
257 | OSStatus ortn; | |
258 | SSLContextRef ctx = NULL; | |
259 | size_t length; | |
d8f41ccd | 260 | uint8_t rcvBuf[RCV_BUF_SIZE]; |
b1ab9ed8 | 261 | const char *outMsg = SERVER_MESSAGE; |
b54c578e A |
262 | |
263 | #pragma clang diagnostic push | |
264 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" | |
b1ab9ed8 A |
265 | *negVersion = kSSLProtocolUnknown; |
266 | *negCipher = SSL_NULL_WITH_NULL_NULL; | |
267 | *peerCerts = NULL; | |
d8f41ccd | 268 | |
b1ab9ed8 A |
269 | #if IGNORE_SIGPIPE |
270 | signal(SIGPIPE, sigpipe); | |
271 | #endif | |
d8f41ccd | 272 | |
b1ab9ed8 A |
273 | /* first wait for a connection */ |
274 | if(!silent) { | |
275 | printf("Waiting for client connection on port %u...", portNum); | |
276 | fflush(stdout); | |
277 | } | |
278 | ortn = AcceptClientConnection(listenSock, &acceptSock, &peerId); | |
279 | if(ortn) { | |
280 | printf("AcceptClientConnection returned %d; aborting\n", (int)ortn); | |
281 | return ortn; | |
282 | } | |
283 | ||
d8f41ccd | 284 | /* |
b1ab9ed8 A |
285 | * Set up a SecureTransport session. |
286 | * First the standard calls. | |
287 | */ | |
288 | ortn = SSLNewContext(true, &ctx); | |
289 | if(ortn) { | |
290 | printSslErrStr("SSLNewContext", ortn); | |
291 | goto cleanup; | |
b54c578e | 292 | } |
b1ab9ed8 A |
293 | ortn = SSLSetIOFuncs(ctx, SocketRead, SocketWrite); |
294 | if(ortn) { | |
295 | printSslErrStr("SSLSetIOFuncs", ortn); | |
296 | goto cleanup; | |
d8f41ccd A |
297 | } |
298 | ortn = SSLSetConnection(ctx, (SSLConnectionRef)(intptr_t)acceptSock); | |
b1ab9ed8 A |
299 | if(ortn) { |
300 | printSslErrStr("SSLSetConnection", ortn); | |
301 | goto cleanup; | |
302 | } | |
d8f41ccd | 303 | |
b1ab9ed8 A |
304 | /* have to do these options befor setting server certs */ |
305 | if(allowExpired) { | |
306 | ortn = SSLSetAllowsExpiredCerts(ctx, true); | |
307 | if(ortn) { | |
308 | printSslErrStr("SSLSetAllowExpiredCerts", ortn); | |
309 | goto cleanup; | |
310 | } | |
311 | } | |
312 | if(allowAnyRoot) { | |
313 | ortn = SSLSetAllowsAnyRoot(ctx, true); | |
314 | if(ortn) { | |
315 | printSslErrStr("SSLSetAllowAnyRoot", ortn); | |
316 | goto cleanup; | |
317 | } | |
318 | } | |
319 | ||
320 | if(anchorFile) { | |
321 | ortn = sslAddTrustedRoot(ctx, anchorFile, replaceAnchors); | |
322 | if(ortn) { | |
323 | printf("***Error obtaining anchor file %s\n", anchorFile); | |
324 | goto cleanup; | |
325 | } | |
326 | } | |
327 | if(serverCerts != NULL) { | |
328 | if(anchorFile == NULL) { | |
329 | /* no specific anchors, so assume we want to trust this one */ | |
330 | ortn = addIdentityAsTrustedRoot(ctx, serverCerts); | |
331 | if(ortn) { | |
332 | goto cleanup; | |
333 | } | |
334 | } | |
335 | ortn = SSLSetCertificate(ctx, serverCerts); | |
336 | if(ortn) { | |
337 | printSslErrStr("SSLSetCertificate", ortn); | |
338 | goto cleanup; | |
339 | } | |
340 | } | |
b1ab9ed8 A |
341 | if(allowExpiredRoot) { |
342 | ortn = SSLSetAllowsExpiredRoots(ctx, true); | |
343 | if(ortn) { | |
344 | printSslErrStr("SSLSetAllowsExpiredRoots", ortn); | |
345 | goto cleanup; | |
346 | } | |
347 | } | |
348 | if(disableCertVerify) { | |
349 | ortn = SSLSetEnableCertVerify(ctx, false); | |
350 | if(ortn) { | |
351 | printSslErrStr("SSLSetEnableCertVerify", ortn); | |
352 | goto cleanup; | |
353 | } | |
354 | } | |
d8f41ccd A |
355 | |
356 | /* | |
b1ab9ed8 | 357 | * SecureTransport options. |
d8f41ccd | 358 | */ |
b1ab9ed8 A |
359 | if(acceptedProts) { |
360 | ortn = SSLSetProtocolVersionEnabled(ctx, kSSLProtocolAll, false); | |
361 | if(ortn) { | |
362 | printSslErrStr("SSLSetProtocolVersionEnabled(all off)", ortn); | |
363 | goto cleanup; | |
364 | } | |
365 | for(const char *cp = acceptedProts; *cp; cp++) { | |
366 | SSLProtocol prot = kSSLProtocolUnknown; | |
367 | switch(*cp) { | |
368 | case '2': | |
369 | prot = kSSLProtocol2; | |
370 | break; | |
371 | case '3': | |
372 | prot = kSSLProtocol3; | |
373 | break; | |
374 | case 't': | |
375 | prot = kTLSProtocol1; | |
376 | break; | |
377 | default: | |
378 | usage(argv); | |
379 | } | |
380 | ortn = SSLSetProtocolVersionEnabled(ctx, prot, true); | |
381 | if(ortn) { | |
382 | printSslErrStr("SSLSetProtocolVersionEnabled", ortn); | |
383 | goto cleanup; | |
384 | } | |
385 | } | |
386 | } | |
387 | else { | |
388 | ortn = SSLSetProtocolVersion(ctx, tryVersion); | |
389 | if(ortn) { | |
390 | printSslErrStr("SSLSetProtocolVersion", ortn); | |
391 | goto cleanup; | |
d8f41ccd | 392 | } |
b1ab9ed8 A |
393 | } |
394 | if(resumableEnable) { | |
395 | ortn = SSLSetPeerID(ctx, &peerId, sizeof(PeerSpec)); | |
396 | if(ortn) { | |
397 | printSslErrStr("SSLSetPeerID", ortn); | |
398 | goto cleanup; | |
399 | } | |
400 | } | |
401 | if(cipherRestrict != '\0') { | |
402 | ortn = sslSetCipherRestrictions(ctx, cipherRestrict); | |
403 | if(ortn) { | |
404 | goto cleanup; | |
405 | } | |
406 | } | |
407 | if(authenticate != kNeverAuthenticate) { | |
408 | ortn = SSLSetClientSideAuthenticate(ctx, authenticate); | |
409 | if(ortn) { | |
410 | printSslErrStr("SSLSetClientSideAuthenticate", ortn); | |
411 | goto cleanup; | |
412 | } | |
413 | } | |
414 | if(dhParams) { | |
415 | ortn = SSLSetDiffieHellmanParams(ctx, dhParams, dhParamsLen); | |
416 | if(ortn) { | |
417 | printSslErrStr("SSLSetDiffieHellmanParams", ortn); | |
418 | goto cleanup; | |
419 | } | |
420 | } | |
421 | if(sessionCacheTimeout) { | |
422 | ortn = SSLSetSessionCacheTimeout(ctx, sessionCacheTimeout); | |
423 | if(ortn) { | |
424 | printSslErrStr("SSLSetSessionCacheTimeout", ortn); | |
425 | goto cleanup; | |
426 | } | |
427 | } | |
428 | if(disableAnonCiphers) { | |
429 | ortn = SSLSetAllowAnonymousCiphers(ctx, false); | |
430 | if(ortn) { | |
431 | printSslErrStr("SSLSetAllowAnonymousCiphers", ortn); | |
432 | goto cleanup; | |
433 | } | |
434 | /* quickie test of the getter */ | |
435 | Boolean e; | |
436 | ortn = SSLGetAllowAnonymousCiphers(ctx, &e); | |
437 | if(ortn) { | |
438 | printSslErrStr("SSLGetAllowAnonymousCiphers", ortn); | |
439 | goto cleanup; | |
440 | } | |
441 | if(e) { | |
442 | printf("***SSLGetAllowAnonymousCiphers() returned true; expected false\n"); | |
427c49bc | 443 | ortn = errSecIO; |
b1ab9ed8 A |
444 | goto cleanup; |
445 | } | |
446 | } | |
447 | /* XXX/cs | |
448 | if(acceptableDNList) { | |
449 | ortn = SSLSetCertificateAuthorities(ctx, acceptableDNList, TRUE); | |
450 | if(ortn) { | |
451 | printSslErrStr("SSLSetCertificateAuthorities", ortn); | |
452 | goto cleanup; | |
453 | } | |
454 | } | |
455 | */ | |
456 | /* end options */ | |
457 | ||
458 | if(pause) { | |
459 | doPause("SSLContext initialized"); | |
460 | } | |
d8f41ccd | 461 | |
b1ab9ed8 A |
462 | /* Perform SSL/TLS handshake */ |
463 | do | |
464 | { ortn = SSLHandshake(ctx); | |
d8f41ccd A |
465 | if((ortn == errSSLWouldBlock) && !silent) { |
466 | /* keep UI responsive */ | |
467 | sslOutputDot(); | |
468 | } | |
b1ab9ed8 | 469 | } while (ortn == errSSLWouldBlock); |
d8f41ccd | 470 | |
b1ab9ed8 A |
471 | /* this works even if handshake failed due to cert chain invalid */ |
472 | copyPeerCerts(ctx, peerCerts); | |
473 | ||
474 | SSLGetClientCertificateState(ctx, certState); | |
475 | SSLGetNegotiatedCipher(ctx, negCipher); | |
476 | SSLGetNegotiatedProtocolVersion(ctx, negVersion); | |
477 | *sessionIDLength = MAX_SESSION_ID_LENGTH; | |
866f8763 | 478 | ortn = SSLGetResumableSessionInfo(ctx, sessionWasResumed, sessionID, sessionIDLength); |
d8f41ccd | 479 | |
b1ab9ed8 A |
480 | if(!silent) { |
481 | printf("\n"); | |
482 | } | |
483 | if(ortn) { | |
484 | goto cleanup; | |
485 | } | |
486 | if(pause) { | |
487 | doPause("SSLContext handshake complete"); | |
488 | } | |
489 | ||
490 | /* wait for one complete line or user says they've had enough */ | |
427c49bc | 491 | while(ortn == errSecSuccess) { |
d8f41ccd A |
492 | length = sizeof(rcvBuf); |
493 | ortn = SSLRead(ctx, rcvBuf, length, &length); | |
494 | if(length == 0) { | |
495 | /* keep UI responsive */ | |
496 | sslOutputDot(); | |
497 | } | |
498 | else { | |
499 | /* print what we have */ | |
500 | printf("client request: "); | |
501 | dumpAscii(rcvBuf, length); | |
502 | } | |
503 | if(pause) { | |
504 | /* allow user to bail */ | |
505 | char resp; | |
506 | ||
b1ab9ed8 | 507 | fpurge(stdin); |
d8f41ccd A |
508 | printf("\nMore client request (y/anything): "); |
509 | resp = getchar(); | |
510 | if(resp != 'y') { | |
511 | break; | |
512 | } | |
513 | } | |
514 | ||
515 | /* poor person's line completion scan */ | |
516 | for(unsigned i=0; i<length; i++) { | |
517 | if((rcvBuf[i] == '\n') || (rcvBuf[i] == '\r')) { | |
518 | /* a labelled break would be nice here.... */ | |
519 | goto serverResp; | |
520 | } | |
521 | } | |
522 | if (ortn == errSSLWouldBlock) { | |
523 | ortn = errSecSuccess; | |
524 | } | |
b1ab9ed8 | 525 | } |
d8f41ccd | 526 | |
b1ab9ed8 A |
527 | serverResp: |
528 | if(pause) { | |
529 | doPause("Client GET msg received"); | |
530 | } | |
531 | ||
532 | /* send out canned response */ | |
533 | length = strlen(outMsg); | |
d8f41ccd A |
534 | ortn = SSLWrite(ctx, outMsg, length, &length); |
535 | if(ortn) { | |
536 | printSslErrStr("SSLWrite", ortn); | |
537 | } | |
b1ab9ed8 A |
538 | if(pause) { |
539 | doPause("Server response sent"); | |
540 | } | |
541 | cleanup: | |
542 | /* | |
d8f41ccd | 543 | * always do close, even on error - to flush outgoing write queue |
b1ab9ed8 A |
544 | */ |
545 | OSStatus cerr = SSLClose(ctx); | |
427c49bc | 546 | if(ortn == errSecSuccess) { |
b1ab9ed8 A |
547 | ortn = cerr; |
548 | } | |
549 | if(acceptSock) { | |
550 | endpointShutdown(acceptSock); | |
551 | } | |
552 | if(ctx) { | |
d8f41ccd | 553 | SSLDisposeContext(ctx); |
b54c578e A |
554 | } |
555 | ||
556 | #pragma clang diagnostic pop | |
557 | ||
b1ab9ed8 A |
558 | /* FIXME - dispose of serverCerts */ |
559 | return ortn; | |
560 | } | |
561 | ||
562 | static void showPeerCerts( | |
563 | CFArrayRef peerCerts, | |
564 | bool verbose) | |
565 | { | |
566 | CFIndex numCerts; | |
567 | SecCertificateRef certRef; | |
568 | CFIndex i; | |
d8f41ccd | 569 | |
b1ab9ed8 A |
570 | if(peerCerts == NULL) { |
571 | return; | |
572 | } | |
573 | numCerts = CFArrayGetCount(peerCerts); | |
574 | for(i=0; i<numCerts; i++) { | |
575 | certRef = (SecCertificateRef)CFArrayGetValueAtIndex(peerCerts, i); | |
576 | printf("\n================== Server Cert %lu ===================\n\n", i); | |
d8f41ccd | 577 | print_cert(certRef, verbose); |
b1ab9ed8 A |
578 | printf("\n=============== End of Server Cert %lu ===============\n", i); |
579 | } | |
580 | } | |
581 | ||
582 | static void writePeerCerts( | |
583 | CFArrayRef peerCerts, | |
584 | const char *fileBase) | |
585 | { | |
586 | CFIndex numCerts; | |
587 | SecCertificateRef certRef; | |
588 | CFIndex i; | |
589 | char fileName[100]; | |
d8f41ccd | 590 | |
b1ab9ed8 A |
591 | if(peerCerts == NULL) { |
592 | return; | |
593 | } | |
594 | numCerts = CFArrayGetCount(peerCerts); | |
595 | for(i=0; i<numCerts; i++) { | |
596 | sprintf(fileName, "%s%02d.cer", fileBase, (int)i); | |
597 | certRef = (SecCertificateRef)CFArrayGetValueAtIndex(peerCerts, i); | |
fa7225c8 | 598 | writeFileSizet(fileName, SecCertificateGetBytePtr(certRef), |
b1ab9ed8 A |
599 | SecCertificateGetLength(certRef)); |
600 | } | |
601 | printf("...wrote %lu certs to fileBase %s\n", numCerts, fileBase); | |
602 | } | |
603 | ||
604 | static void showSSLResult( | |
605 | SSLProtocol tryVersion, | |
606 | char *acceptedProts, | |
607 | OSStatus err, | |
608 | SSLProtocol negVersion, | |
609 | SSLCipherSuite negCipher, | |
d8f41ccd A |
610 | Boolean sessionWasResumed, |
611 | unsigned char *sessionID, | |
612 | size_t sessionIDLength, | |
b1ab9ed8 A |
613 | CFArrayRef peerCerts, |
614 | bool displayPeerCerts, | |
615 | SSLClientCertificateState certState, | |
616 | char *fileBase) // non-NULL: write certs to file | |
617 | { | |
618 | CFIndex numPeerCerts; | |
d8f41ccd | 619 | |
b1ab9ed8 A |
620 | printf("\n"); |
621 | if(acceptedProts) { | |
622 | printf(" Allowed SSL versions : %s\n", acceptedProts); | |
623 | } | |
624 | else { | |
d8f41ccd | 625 | printf(" Attempted SSL version : %s\n", |
b1ab9ed8 A |
626 | sslGetProtocolVersionString(tryVersion)); |
627 | } | |
628 | printf(" Result : %s\n", sslGetSSLErrString(err)); | |
d8f41ccd | 629 | printf(" Negotiated SSL version : %s\n", |
b1ab9ed8 A |
630 | sslGetProtocolVersionString(negVersion)); |
631 | printf(" Negotiated CipherSuite : %s\n", | |
632 | sslGetCipherSuiteString(negCipher)); | |
b54c578e A |
633 | |
634 | #pragma clang diagnostic push | |
635 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" | |
b1ab9ed8 A |
636 | if(certState != kSSLClientCertNone) { |
637 | printf(" Client Cert State : %s\n", | |
638 | sslGetClientCertStateString(certState)); | |
639 | } | |
b54c578e A |
640 | #pragma clang diagnostic pop |
641 | ||
b1ab9ed8 A |
642 | printf(" Resumed Session : "); |
643 | if(sessionWasResumed) { | |
644 | for(unsigned dex=0; dex<sessionIDLength; dex++) { | |
645 | printf("%02X ", sessionID[dex]); | |
646 | if(((dex % 8) == 7) && (dex != (sessionIDLength - 1))) { | |
647 | printf("\n "); | |
648 | } | |
649 | } | |
650 | printf("\n"); | |
651 | } | |
652 | else { | |
653 | printf("NOT RESUMED\n"); | |
654 | } | |
655 | if(peerCerts == NULL) { | |
656 | numPeerCerts = 0; | |
657 | } | |
658 | else { | |
659 | numPeerCerts = CFArrayGetCount(peerCerts); | |
660 | } | |
661 | printf(" Number of peer certs : %lu\n", numPeerCerts); | |
662 | if(numPeerCerts != 0) { | |
663 | if(displayPeerCerts) { | |
664 | showPeerCerts(peerCerts, false); | |
665 | } | |
666 | if(fileBase != NULL) { | |
667 | writePeerCerts(peerCerts, fileBase); | |
668 | } | |
669 | } | |
670 | printf("\n"); | |
671 | } | |
672 | ||
673 | static int verifyClientCertState( | |
674 | bool verifyCertState, | |
675 | SSLClientCertificateState expectState, | |
676 | SSLClientCertificateState gotState) | |
677 | { | |
678 | if(!verifyCertState) { | |
679 | return 0; | |
680 | } | |
681 | if(expectState == gotState) { | |
682 | return 0; | |
683 | } | |
684 | printf("***Expected clientCertState %s; got %s\n", | |
685 | sslGetClientCertStateString(expectState), | |
686 | sslGetClientCertStateString(gotState)); | |
687 | return 1; | |
688 | } | |
689 | ||
690 | int main(int argc, char **argv) | |
d8f41ccd A |
691 | { |
692 | OSStatus err; | |
b1ab9ed8 A |
693 | int arg; |
694 | char fullFileBase[100]; | |
695 | SSLProtocol negVersion; | |
696 | SSLCipherSuite negCipher; | |
697 | Boolean sessionWasResumed; | |
698 | unsigned char sessionID[MAX_SESSION_ID_LENGTH]; | |
699 | size_t sessionIDLength; | |
d8f41ccd | 700 | CFArrayRef peerCerts = NULL; |
b1ab9ed8 A |
701 | char *argp; |
702 | otSocket listenSock; | |
703 | CFArrayRef serverCerts = nil; // required | |
b1ab9ed8 | 704 | SecKeychainRef serverKc = nil; |
b1ab9ed8 A |
705 | int loopNum; |
706 | int errCount = 0; | |
707 | SSLClientCertificateState certState; // obtained from sslServe | |
708 | ||
b54c578e A |
709 | #pragma clang diagnostic push |
710 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" | |
711 | ||
b1ab9ed8 A |
712 | /* user-spec'd parameters */ |
713 | unsigned short portNum = DEFAULT_PORT; | |
714 | bool allowExpired = false; | |
715 | bool allowAnyRoot = false; | |
716 | char *fileBase = NULL; | |
b1ab9ed8 A |
717 | bool displayCerts = false; |
718 | char cipherRestrict = '\0'; | |
719 | SSLProtocol attemptProt = kTLSProtocol1; | |
d8f41ccd | 720 | bool protXOnly = false; // kSSLProtocol3Only, |
b1ab9ed8 A |
721 | // kTLSProtocol1Only |
722 | char *acceptedProts = NULL; // "23t" ==> SSLSetProtocolVersionEnabled | |
723 | bool quiet = false; | |
724 | bool resumableEnable = true; | |
725 | bool pause = false; | |
726 | char *keyChainName = NULL; | |
b1ab9ed8 A |
727 | int loops = 1; |
728 | SSLAuthenticate authenticate = kNeverAuthenticate; | |
729 | bool nonBlocking = false; | |
730 | bool allowExpiredRoot = false; | |
731 | bool disableCertVerify = false; | |
732 | char *anchorFile = NULL; | |
733 | bool replaceAnchors = false; | |
734 | bool vfyCertState = false; | |
735 | SSLClientCertificateState expectCertState = kSSLClientCertNone; | |
736 | char *password = NULL; | |
b1ab9ed8 A |
737 | unsigned char *dhParams = NULL; |
738 | unsigned dhParamsLen = 0; | |
b1ab9ed8 A |
739 | bool completeCertChain = false; |
740 | uint32_t sessionCacheTimeout = 0; | |
741 | bool disableAnonCiphers = false; | |
742 | CFMutableArrayRef acceptableDNList = NULL; | |
743 | ||
744 | for(arg=1; arg<argc; arg++) { | |
745 | argp = argv[arg]; | |
746 | switch(argp[0]) { | |
747 | case 'P': | |
748 | portNum = atoi(&argp[2]); | |
749 | break; | |
750 | case 'k': | |
751 | keyChainName = &argp[2]; | |
752 | break; | |
b1ab9ed8 A |
753 | case 'e': |
754 | allowExpired = true; | |
755 | break; | |
756 | case 'E': | |
757 | allowExpiredRoot = true; | |
758 | break; | |
759 | case 'x': | |
760 | disableCertVerify = true; | |
761 | break; | |
762 | case 'a': | |
763 | if(++arg == argc) { | |
764 | /* requires another arg */ | |
765 | usage(argv); | |
766 | } | |
767 | anchorFile = argv[arg]; | |
768 | break; | |
769 | case 'A': | |
770 | if(++arg == argc) { | |
771 | /* requires another arg */ | |
772 | usage(argv); | |
773 | } | |
774 | anchorFile = argv[arg]; | |
775 | replaceAnchors = true; | |
776 | break; | |
777 | case 'T': | |
778 | if(argp[1] != '=') { | |
779 | usage(argv); | |
780 | } | |
781 | vfyCertState = true; | |
782 | switch(argp[2]) { | |
783 | case 'n': | |
784 | expectCertState = kSSLClientCertNone; | |
785 | break; | |
786 | case 'r': | |
787 | expectCertState = kSSLClientCertRequested; | |
788 | break; | |
789 | case 's': | |
790 | expectCertState = kSSLClientCertSent; | |
791 | break; | |
792 | case 'j': | |
793 | expectCertState = kSSLClientCertRejected; | |
794 | break; | |
795 | default: | |
796 | usage(argv); | |
797 | } | |
798 | break; | |
799 | case 'r': | |
800 | allowAnyRoot = true; | |
801 | break; | |
802 | case 'd': | |
b1ab9ed8 A |
803 | break; |
804 | case 'c': | |
805 | displayCerts = true; | |
806 | break; | |
807 | case 'f': | |
808 | fileBase = &argp[2]; | |
809 | break; | |
810 | case 'C': | |
811 | cipherRestrict = argp[2]; | |
812 | break; | |
813 | case '2': | |
814 | attemptProt = kSSLProtocol2; | |
815 | break; | |
816 | case '3': | |
817 | attemptProt = kSSLProtocol3; | |
818 | break; | |
819 | case 't': | |
820 | attemptProt = kTLSProtocol1; | |
821 | break; | |
822 | case 'o': | |
823 | protXOnly = true; | |
824 | break; | |
825 | case 'g': | |
826 | if(argp[1] != '=') { | |
827 | usage(argv); | |
828 | } | |
829 | acceptedProts = &argp[2]; | |
830 | break; | |
831 | case 'R': | |
832 | resumableEnable = false; | |
833 | break; | |
834 | case 'b': | |
835 | nonBlocking = true; | |
836 | break; | |
837 | case 'u': | |
838 | if(argp[1] != '=') { | |
839 | usage(argv); | |
840 | } | |
841 | switch(argp[2]) { | |
842 | case 'a': authenticate = kAlwaysAuthenticate; break; | |
843 | case 'n': authenticate = kNeverAuthenticate; break; | |
844 | case 't': authenticate = kTryAuthenticate; break; | |
845 | default: usage(argv); | |
846 | } | |
847 | break; | |
848 | case 'D': | |
849 | if(++arg == argc) { | |
850 | /* requires another arg */ | |
851 | usage(argv); | |
852 | } | |
b1ab9ed8 A |
853 | break; |
854 | case 'z': | |
855 | password = &argp[2]; | |
856 | break; | |
857 | case 'H': | |
b1ab9ed8 A |
858 | break; |
859 | case 'M': | |
860 | completeCertChain = true; | |
861 | break; | |
862 | case 'i': | |
863 | sessionCacheTimeout = atoi(&argp[2]); | |
864 | break; | |
865 | case '4': | |
866 | disableAnonCiphers = true; | |
867 | break; | |
868 | case 'p': | |
869 | pause = true; | |
870 | break; | |
871 | case 'q': | |
872 | quiet = true; | |
873 | break; | |
b1ab9ed8 A |
874 | case 'l': |
875 | if(argp[1] == '\0') { | |
876 | /* no loop count --> loop forever */ | |
877 | loops = 0; | |
878 | break; | |
879 | } | |
880 | else if(argp[1] != '=') { | |
881 | usage(argv); | |
882 | } | |
883 | loops = atoi(&argp[2]); | |
884 | break; | |
885 | default: | |
886 | usage(argv); | |
887 | } | |
888 | } | |
889 | ||
890 | #if NO_SERVER | |
891 | # if DEBUG | |
d8f41ccd | 892 | securityd_init(NULL); |
b1ab9ed8 A |
893 | # endif |
894 | #endif | |
895 | ||
896 | /* get server cert and optional encryption cert as CFArrayRef */ | |
897 | if(keyChainName) { | |
d8f41ccd | 898 | serverCerts = getSslCerts(keyChainName, false, completeCertChain, |
b1ab9ed8 A |
899 | anchorFile, &serverKc); |
900 | if(serverCerts == nil) { | |
901 | exit(1); | |
902 | } | |
b54c578e | 903 | } else if(protXOnly) { |
b1ab9ed8 A |
904 | switch(attemptProt) { |
905 | case kTLSProtocol1: | |
906 | attemptProt = kTLSProtocol1Only; | |
907 | break; | |
908 | case kSSLProtocol3: | |
909 | attemptProt = kSSLProtocol3Only; | |
910 | break; | |
911 | default: | |
912 | break; | |
913 | } | |
914 | } | |
b54c578e A |
915 | |
916 | #pragma clang diagnostic pop | |
b1ab9ed8 A |
917 | |
918 | /* one-time only server port setup */ | |
919 | err = ListenForClients(portNum, nonBlocking, &listenSock); | |
920 | if(err) { | |
921 | printf("ListenForClients returned %d; aborting\n", (int)err); | |
922 | exit(1); | |
923 | } | |
924 | ||
925 | for(loopNum=1; ; loopNum++) { | |
926 | err = sslServe(listenSock, | |
927 | portNum, | |
928 | attemptProt, | |
929 | acceptedProts, | |
930 | serverCerts, | |
931 | password, | |
b1ab9ed8 A |
932 | allowExpired, |
933 | allowAnyRoot, | |
934 | allowExpiredRoot, | |
935 | disableCertVerify, | |
936 | anchorFile, | |
937 | replaceAnchors, | |
938 | cipherRestrict, | |
939 | authenticate, | |
940 | dhParams, | |
941 | dhParamsLen, | |
942 | acceptableDNList, | |
943 | resumableEnable, | |
944 | sessionCacheTimeout, | |
945 | disableAnonCiphers, | |
946 | quiet, | |
947 | pause, | |
948 | &negVersion, | |
949 | &negCipher, | |
950 | &certState, | |
951 | &sessionWasResumed, | |
952 | sessionID, | |
953 | &sessionIDLength, | |
954 | &peerCerts, | |
955 | argv); | |
956 | if(err) { | |
957 | errCount++; | |
958 | } | |
959 | if(!quiet) { | |
960 | SSLProtocol tryProt = attemptProt; | |
961 | showSSLResult(tryProt, | |
962 | acceptedProts, | |
d8f41ccd A |
963 | err, |
964 | negVersion, | |
965 | negCipher, | |
b1ab9ed8 A |
966 | sessionWasResumed, |
967 | sessionID, | |
968 | sessionIDLength, | |
969 | peerCerts, | |
970 | displayCerts, | |
971 | certState, | |
972 | fileBase ? fullFileBase : NULL); | |
973 | } | |
d8f41ccd | 974 | errCount += verifyClientCertState(vfyCertState, expectCertState, |
b1ab9ed8 A |
975 | certState); |
976 | freePeerCerts(peerCerts); | |
977 | if(loops && (loopNum == loops)) { | |
978 | break; | |
979 | } | |
980 | }; | |
d8f41ccd | 981 | |
b1ab9ed8 A |
982 | endpointShutdown(listenSock); |
983 | ||
984 | if(serverKc) { | |
985 | CFRelease(serverKc); | |
986 | } | |
b1ab9ed8 A |
987 | return errCount; |
988 | ||
989 | } | |
990 | ||
991 |