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