]>
Commit | Line | Data |
---|---|---|
d8f41ccd A |
1 | /* |
2 | * Copyright (c) 2011,2013-2014 Apple Inc. All Rights Reserved. | |
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 | ||
427c49bc A |
24 | |
25 | #include <stdbool.h> | |
26 | #include <pthread.h> | |
27 | #include <fcntl.h> | |
28 | #include <sys/mman.h> | |
29 | #include <unistd.h> | |
30 | #include <sys/types.h> | |
31 | #include <netinet/in.h> | |
32 | #include <sys/socket.h> | |
33 | #include <netdb.h> | |
34 | #include <arpa/inet.h> | |
35 | #include <CoreFoundation/CoreFoundation.h> | |
36 | ||
37 | #include <AssertMacros.h> | |
38 | #include <Security/SecureTransportPriv.h> /* SSLSetOption */ | |
39 | #include <Security/SecureTransport.h> | |
40 | ||
41 | #include <string.h> | |
42 | #include <sys/types.h> | |
43 | #include <sys/socket.h> | |
44 | #include <errno.h> | |
45 | #include <stdlib.h> | |
46 | #include <mach/mach_time.h> | |
47 | ||
48 | ||
49 | #include "ssl_regressions.h" | |
50 | ||
51 | ||
52 | typedef struct { | |
53 | uint32_t session_id; | |
54 | bool is_session_resume; | |
55 | SSLContextRef st; | |
56 | bool is_server; | |
57 | bool client_side_auth; | |
58 | bool dh_anonymous; | |
59 | int comm; | |
60 | CFArrayRef certs; | |
61 | } ssl_test_handle; | |
62 | ||
63 | ||
64 | ||
65 | #if 0 | |
66 | static void hexdump(const uint8_t *bytes, size_t len) { | |
67 | size_t ix; | |
68 | printf("socket write(%p, %lu)\n", bytes, len); | |
69 | for (ix = 0; ix < len; ++ix) { | |
70 | if (!(ix % 16)) | |
71 | printf("\n"); | |
72 | printf("%02X ", bytes[ix]); | |
73 | } | |
74 | printf("\n"); | |
75 | } | |
76 | #else | |
77 | #define hexdump(bytes, len) | |
78 | #endif | |
79 | ||
80 | static int SocketConnect(const char *hostName, int port) | |
81 | { | |
82 | struct sockaddr_in addr; | |
83 | struct in_addr host; | |
84 | int sock; | |
85 | int err; | |
86 | struct hostent *ent; | |
87 | ||
88 | if (hostName[0] >= '0' && hostName[0] <= '9') | |
89 | { | |
90 | host.s_addr = inet_addr(hostName); | |
91 | } | |
92 | else { | |
93 | unsigned dex; | |
94 | #define GETHOST_RETRIES 5 | |
95 | /* seeing a lot of soft failures here that I really don't want to track down */ | |
96 | for(dex=0; dex<GETHOST_RETRIES; dex++) { | |
97 | if(dex != 0) { | |
98 | printf("\n...retrying gethostbyname(%s)", hostName); | |
99 | } | |
100 | ent = gethostbyname(hostName); | |
101 | if(ent != NULL) { | |
102 | break; | |
103 | } | |
104 | } | |
105 | if(ent == NULL) { | |
106 | printf("\n***gethostbyname(%s) returned: %s\n", hostName, hstrerror(h_errno)); | |
107 | return -1; | |
108 | } | |
109 | memcpy(&host, ent->h_addr, sizeof(struct in_addr)); | |
110 | } | |
111 | ||
112 | ||
113 | sock = socket(AF_INET, SOCK_STREAM, 0); | |
114 | addr.sin_addr = host; | |
115 | addr.sin_port = htons((u_short)port); | |
116 | ||
117 | addr.sin_family = AF_INET; | |
118 | err = connect(sock, (struct sockaddr *) &addr, sizeof(struct sockaddr_in)); | |
119 | ||
120 | if(err!=0) | |
121 | { | |
122 | perror("connect failed"); | |
123 | return err; | |
124 | } | |
125 | ||
126 | /* make non blocking */ | |
127 | fcntl(sock, F_SETFL, O_NONBLOCK); | |
128 | ||
129 | ||
130 | return sock; | |
131 | } | |
132 | ||
133 | ||
134 | static OSStatus SocketWrite(SSLConnectionRef conn, const void *data, size_t *length) | |
135 | { | |
136 | size_t len = *length; | |
137 | uint8_t *ptr = (uint8_t *)data; | |
138 | ||
139 | do { | |
140 | ssize_t ret; | |
141 | do { | |
142 | hexdump(ptr, len); | |
143 | ret = write((int)conn, ptr, len); | |
144 | if (ret < 0) | |
145 | perror("send"); | |
146 | } while ((ret < 0) && (errno == EAGAIN || errno == EINTR)); | |
147 | if (ret > 0) { | |
148 | len -= ret; | |
149 | ptr += ret; | |
150 | } | |
151 | else | |
152 | return -36; | |
153 | } while (len > 0); | |
154 | ||
155 | *length = *length - len; | |
156 | return errSecSuccess; | |
157 | } | |
158 | ||
159 | static | |
160 | OSStatus SocketRead( | |
161 | SSLConnectionRef connection, | |
162 | void *data, | |
163 | size_t *dataLength) | |
164 | { | |
165 | int fd = (int)connection; | |
166 | ssize_t len; | |
167 | ||
168 | len = read(fd, data, *dataLength); | |
169 | ||
170 | if(len<0) { | |
171 | int theErr = errno; | |
172 | switch(theErr) { | |
173 | case EAGAIN: | |
174 | //printf("SocketRead: EAGAIN\n"); | |
175 | *dataLength=0; | |
176 | /* nonblocking, no data */ | |
177 | return errSSLWouldBlock; | |
178 | default: | |
179 | perror("SocketRead"); | |
180 | return -36; | |
181 | } | |
182 | } | |
183 | ||
184 | if(len<(ssize_t)*dataLength) { | |
185 | *dataLength=len; | |
186 | return errSSLWouldBlock; | |
187 | } | |
188 | ||
189 | return errSecSuccess; | |
190 | } | |
191 | ||
192 | static SSLContextRef make_ssl_ref(int sock, SSLProtocol maxprot, Boolean false_start) | |
193 | { | |
194 | SSLContextRef ctx = NULL; | |
195 | ||
196 | require_noerr(SSLNewContext(false, &ctx), out); | |
197 | require_noerr(SSLSetIOFuncs(ctx, | |
198 | (SSLReadFunc)SocketRead, (SSLWriteFunc)SocketWrite), out); | |
199 | require_noerr(SSLSetConnection(ctx, (SSLConnectionRef)(intptr_t)sock), out); | |
200 | ||
201 | require_noerr(SSLSetSessionOption(ctx, | |
202 | kSSLSessionOptionBreakOnServerAuth, true), out); | |
203 | ||
204 | require_noerr(SSLSetSessionOption(ctx, | |
205 | kSSLSessionOptionFalseStart, false_start), out); | |
206 | ||
207 | require_noerr(SSLSetProtocolVersionMax(ctx, maxprot), out); | |
208 | ||
209 | return ctx; | |
210 | out: | |
211 | if (ctx) | |
212 | SSLDisposeContext(ctx); | |
213 | return NULL; | |
214 | } | |
215 | ||
216 | const char request[]="GET / HTTP/1.1\n\n"; | |
217 | char reply[2048]; | |
218 | ||
219 | static OSStatus securetransport(ssl_test_handle * ssl) | |
220 | { | |
221 | OSStatus ortn; | |
222 | SSLContextRef ctx = ssl->st; | |
223 | SecTrustRef trust = NULL; | |
224 | bool got_server_auth = false, got_client_cert_req = false; | |
225 | ||
226 | ortn = SSLHandshake(ctx); | |
227 | //fprintf(stderr, "Fell out of SSLHandshake with error: %ld\n", (long)ortn); | |
228 | ||
229 | size_t sent, received; | |
230 | const char *r=request; | |
231 | size_t l=sizeof(request); | |
232 | ||
233 | do { | |
234 | ||
235 | ortn = SSLWrite(ctx, r, l, &sent); | |
236 | ||
237 | if(ortn == errSSLWouldBlock) { | |
238 | r+=sent; | |
239 | l-=sent; | |
240 | } | |
241 | ||
242 | if (ortn == errSSLServerAuthCompleted) | |
243 | { | |
244 | require_string(!got_server_auth, out, "second server auth"); | |
245 | require_string(!got_client_cert_req, out, "got client cert req before server auth"); | |
246 | got_server_auth = true; | |
247 | require_string(!trust, out, "Got errSSLServerAuthCompleted twice?"); | |
248 | /* verify peer cert chain */ | |
249 | require_noerr(SSLCopyPeerTrust(ctx, &trust), out); | |
250 | SecTrustResultType trust_result = 0; | |
251 | /* this won't verify without setting up a trusted anchor */ | |
252 | require_noerr(SecTrustEvaluate(trust, &trust_result), out); | |
253 | } | |
427c49bc A |
254 | } while(ortn == errSSLWouldBlock || ortn == errSSLServerAuthCompleted); |
255 | ||
256 | //fprintf(stderr, "\nHTTP Request Sent\n"); | |
257 | ||
258 | require_noerr_action_quiet(ortn, out, printf("SSLWrite failed with err %ld\n", (long)ortn)); | |
259 | ||
260 | require_string(got_server_auth, out, "never got server auth"); | |
261 | ||
262 | do { | |
263 | ortn = SSLRead(ctx, reply, sizeof(reply)-1, &received); | |
264 | //fprintf(stderr, "r"); usleep(1000); | |
265 | } while(ortn == errSSLWouldBlock); | |
266 | ||
267 | //fprintf(stderr, "\n"); | |
268 | ||
269 | require_noerr_action_quiet(ortn, out, printf("SSLRead failed with err %ld\n", (long)ortn)); | |
270 | ||
271 | reply[received]=0; | |
272 | ||
273 | //fprintf(stderr, "HTTP reply:\n"); | |
274 | //fprintf(stderr, "%s\n",reply); | |
275 | ||
276 | out: | |
277 | SSLClose(ctx); | |
278 | SSLDisposeContext(ctx); | |
279 | if (trust) CFRelease(trust); | |
280 | ||
281 | return ortn; | |
282 | } | |
283 | ||
427c49bc A |
284 | static ssl_test_handle * |
285 | ssl_test_handle_create(int comm, SSLProtocol maxprot, Boolean false_start) | |
286 | { | |
287 | ssl_test_handle *handle = calloc(1, sizeof(ssl_test_handle)); | |
288 | if (handle) { | |
289 | handle->comm = comm; | |
290 | handle->st = make_ssl_ref(comm, maxprot, false_start); | |
291 | } | |
292 | return handle; | |
293 | } | |
294 | ||
295 | static | |
296 | struct s_server { | |
297 | char *host; | |
298 | int port; | |
299 | SSLProtocol maxprot; | |
300 | } servers[] = { | |
301 | /* Good tls 1.2 servers */ | |
302 | {"encrypted.google.com", 443, kTLSProtocol12 }, | |
303 | {"www.amazon.com",443, kTLSProtocol12 }, | |
d8f41ccd | 304 | //{"www.mikestoolbox.org",443, kTLSProtocol12 }, |
427c49bc A |
305 | }; |
306 | ||
307 | #define NSERVERS (int)(sizeof(servers)/sizeof(servers[0])) | |
308 | #define NLOOPS 1 | |
309 | ||
310 | static void | |
311 | tests(void) | |
312 | { | |
313 | int p; | |
314 | int fs; | |
315 | ||
316 | for(p=0; p<NSERVERS;p++) { | |
317 | for(int loops=0; loops<NLOOPS; loops++) { | |
318 | for(fs=0;fs<2; fs++) { | |
319 | ||
320 | ssl_test_handle *client; | |
321 | ||
322 | int s; | |
323 | OSStatus r; | |
324 | ||
325 | s=SocketConnect(servers[p].host, servers[p].port); | |
326 | if(s<0) { | |
327 | fail("connect failed with err=%d - %s:%d (try %d)", s, servers[p].host, servers[p].port, loops); | |
328 | break; | |
329 | } | |
330 | ||
331 | client = ssl_test_handle_create(s, servers[p].maxprot, fs); | |
332 | ||
333 | r=securetransport(client); | |
334 | ok(!r, "handshake failed with err=%ld - %s:%d (try %d), false start=%d", (long)r, servers[p].host, servers[p].port, loops, fs); | |
335 | ||
336 | close(s); | |
d8f41ccd | 337 | free(client); |
427c49bc A |
338 | } } } |
339 | } | |
340 | ||
341 | int ssl_47_falsestart(int argc, char *const *argv) | |
342 | { | |
343 | plan_tests(NSERVERS*NLOOPS*2); | |
344 | ||
345 | tests(); | |
346 | ||
347 | return 0; | |
348 | } |