1 | /*************************************************************************** |
2 | * _ _ ____ _ |
3 | * Project ___| | | | _ \| | |
4 | * / __| | | | |_) | | |
5 | * | (__| |_| | _ <| |___ |
6 | * \___|\___/|_| \_\_____| |
7 | * |
8 | * Copyright (C) 2019 - 2021, Michael Forney, <mforney@mforney.org> |
9 | * |
10 | * This software is licensed as described in the file COPYING, which |
11 | * you should have received as part of this distribution. The terms |
12 | * are also available at https://curl.se/docs/copyright.html. |
13 | * |
14 | * You may opt to use, copy, modify, merge, publish, distribute and/or sell |
15 | * copies of the Software, and permit persons to whom the Software is |
16 | * furnished to do so, under the terms of the COPYING file. |
17 | * |
18 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
19 | * KIND, either express or implied. |
20 | * |
21 | ***************************************************************************/ |
22 | #include "curl_setup.h" |
23 | |
24 | #ifdef USE_BEARSSL |
25 | |
26 | #include <bearssl.h> |
27 | |
28 | #include "bearssl.h" |
29 | #include "urldata.h" |
30 | #include "sendf.h" |
31 | #include "inet_pton.h" |
32 | #include "vtls.h" |
33 | #include "connect.h" |
34 | #include "select.h" |
35 | #include "multiif.h" |
36 | #include "curl_printf.h" |
37 | #include "curl_memory.h" |
38 | |
39 | struct x509_context { |
40 | const br_x509_class *vtable; |
41 | br_x509_minimal_context minimal; |
42 | bool verifyhost; |
43 | bool verifypeer; |
44 | }; |
45 | |
46 | struct ssl_backend_data { |
47 | br_ssl_client_context ctx; |
48 | struct x509_context x509; |
49 | unsigned char buf[BR_SSL_BUFSIZE_BIDI]; |
50 | br_x509_trust_anchor *anchors; |
51 | size_t anchors_len; |
52 | const char *protocols[2]; |
53 | /* SSL client context is active */ |
54 | bool active; |
55 | /* size of pending write, yet to be flushed */ |
56 | size_t pending_write; |
57 | }; |
58 | |
59 | struct cafile_parser { |
60 | CURLcode err; |
61 | bool in_cert; |
62 | br_x509_decoder_context xc; |
63 | /* array of trust anchors loaded from CAfile */ |
64 | br_x509_trust_anchor *anchors; |
65 | size_t anchors_len; |
66 | /* buffer for DN data */ |
67 | unsigned char dn[1024]; |
68 | size_t dn_len; |
69 | }; |
70 | |
71 | #define CAFILE_SOURCE_PATH 1 |
72 | #define CAFILE_SOURCE_BLOB 2 |
73 | struct cafile_source { |
74 | const int type; |
75 | const char * const data; |
76 | const size_t len; |
77 | }; |
78 | |
79 | static void append_dn(void *ctx, const void *buf, size_t len) |
80 | { |
81 | struct cafile_parser *ca = ctx; |
82 | |
83 | if(ca->err != CURLE_OK || !ca->in_cert) |
84 | return; |
85 | if(sizeof(ca->dn) - ca->dn_len < len) { |
86 | ca->err = CURLE_FAILED_INIT; |
87 | return; |
88 | } |
89 | memcpy(ca->dn + ca->dn_len, buf, len); |
90 | ca->dn_len += len; |
91 | } |
92 | |
93 | static void x509_push(void *ctx, const void *buf, size_t len) |
94 | { |
95 | struct cafile_parser *ca = ctx; |
96 | |
97 | if(ca->in_cert) |
98 | br_x509_decoder_push(&ca->xc, buf, len); |
99 | } |
100 | |
101 | static CURLcode load_cafile(struct cafile_source *source, |
102 | br_x509_trust_anchor **anchors, |
103 | size_t *anchors_len) |
104 | { |
105 | struct cafile_parser ca; |
106 | br_pem_decoder_context pc; |
107 | br_x509_trust_anchor *ta; |
108 | size_t ta_size; |
109 | br_x509_trust_anchor *new_anchors; |
110 | size_t new_anchors_len; |
111 | br_x509_pkey *pkey; |
112 | FILE *fp = 0; |
113 | unsigned char buf[BUFSIZ]; |
114 | const unsigned char *p; |
115 | const char *name; |
116 | size_t n, i, pushed; |
117 | |
118 | DEBUGASSERT(source->type == CAFILE_SOURCE_PATH |
119 | || source->type == CAFILE_SOURCE_BLOB); |
120 | |
121 | if(source->type == CAFILE_SOURCE_PATH) { |
122 | fp = fopen(source->data, "rb" ); |
123 | if(!fp) |
124 | return CURLE_SSL_CACERT_BADFILE; |
125 | } |
126 | |
127 | if(source->type == CAFILE_SOURCE_BLOB && source->len > (size_t)INT_MAX) |
128 | return CURLE_SSL_CACERT_BADFILE; |
129 | |
130 | ca.err = CURLE_OK; |
131 | ca.in_cert = FALSE; |
132 | ca.anchors = NULL; |
133 | ca.anchors_len = 0; |
134 | br_pem_decoder_init(&pc); |
135 | br_pem_decoder_setdest(&pc, x509_push, &ca); |
136 | do { |
137 | if(source->type == CAFILE_SOURCE_PATH) { |
138 | n = fread(buf, 1, sizeof(buf), fp); |
139 | if(n == 0) |
140 | break; |
141 | p = buf; |
142 | } |
143 | else if(source->type == CAFILE_SOURCE_BLOB) { |
144 | n = source->len; |
145 | p = (unsigned char *) source->data; |
146 | } |
147 | while(n) { |
148 | pushed = br_pem_decoder_push(&pc, p, n); |
149 | if(ca.err) |
150 | goto fail; |
151 | p += pushed; |
152 | n -= pushed; |
153 | |
154 | switch(br_pem_decoder_event(&pc)) { |
155 | case 0: |
156 | break; |
157 | case BR_PEM_BEGIN_OBJ: |
158 | name = br_pem_decoder_name(&pc); |
159 | if(strcmp(name, "CERTIFICATE" ) && strcmp(name, "X509 CERTIFICATE" )) |
160 | break; |
161 | br_x509_decoder_init(&ca.xc, append_dn, &ca); |
162 | if(ca.anchors_len == SIZE_MAX / sizeof(ca.anchors[0])) { |
163 | ca.err = CURLE_OUT_OF_MEMORY; |
164 | goto fail; |
165 | } |
166 | new_anchors_len = ca.anchors_len + 1; |
167 | new_anchors = realloc(ca.anchors, |
168 | new_anchors_len * sizeof(ca.anchors[0])); |
169 | if(!new_anchors) { |
170 | ca.err = CURLE_OUT_OF_MEMORY; |
171 | goto fail; |
172 | } |
173 | ca.anchors = new_anchors; |
174 | ca.anchors_len = new_anchors_len; |
175 | ca.in_cert = TRUE; |
176 | ca.dn_len = 0; |
177 | ta = &ca.anchors[ca.anchors_len - 1]; |
178 | ta->dn.data = NULL; |
179 | break; |
180 | case BR_PEM_END_OBJ: |
181 | if(!ca.in_cert) |
182 | break; |
183 | ca.in_cert = FALSE; |
184 | if(br_x509_decoder_last_error(&ca.xc)) { |
185 | ca.err = CURLE_SSL_CACERT_BADFILE; |
186 | goto fail; |
187 | } |
188 | ta->flags = 0; |
189 | if(br_x509_decoder_isCA(&ca.xc)) |
190 | ta->flags |= BR_X509_TA_CA; |
191 | pkey = br_x509_decoder_get_pkey(&ca.xc); |
192 | if(!pkey) { |
193 | ca.err = CURLE_SSL_CACERT_BADFILE; |
194 | goto fail; |
195 | } |
196 | ta->pkey = *pkey; |
197 | |
198 | /* calculate space needed for trust anchor data */ |
199 | ta_size = ca.dn_len; |
200 | switch(pkey->key_type) { |
201 | case BR_KEYTYPE_RSA: |
202 | ta_size += pkey->key.rsa.nlen + pkey->key.rsa.elen; |
203 | break; |
204 | case BR_KEYTYPE_EC: |
205 | ta_size += pkey->key.ec.qlen; |
206 | break; |
207 | default: |
208 | ca.err = CURLE_FAILED_INIT; |
209 | goto fail; |
210 | } |
211 | |
212 | /* fill in trust anchor DN and public key data */ |
213 | ta->dn.data = malloc(ta_size); |
214 | if(!ta->dn.data) { |
215 | ca.err = CURLE_OUT_OF_MEMORY; |
216 | goto fail; |
217 | } |
218 | memcpy(ta->dn.data, ca.dn, ca.dn_len); |
219 | ta->dn.len = ca.dn_len; |
220 | switch(pkey->key_type) { |
221 | case BR_KEYTYPE_RSA: |
222 | ta->pkey.key.rsa.n = ta->dn.data + ta->dn.len; |
223 | memcpy(ta->pkey.key.rsa.n, pkey->key.rsa.n, pkey->key.rsa.nlen); |
224 | ta->pkey.key.rsa.e = ta->pkey.key.rsa.n + ta->pkey.key.rsa.nlen; |
225 | memcpy(ta->pkey.key.rsa.e, pkey->key.rsa.e, pkey->key.rsa.elen); |
226 | break; |
227 | case BR_KEYTYPE_EC: |
228 | ta->pkey.key.ec.q = ta->dn.data + ta->dn.len; |
229 | memcpy(ta->pkey.key.ec.q, pkey->key.ec.q, pkey->key.ec.qlen); |
230 | break; |
231 | } |
232 | break; |
233 | default: |
234 | ca.err = CURLE_SSL_CACERT_BADFILE; |
235 | goto fail; |
236 | } |
237 | } |
238 | } while(source->type != CAFILE_SOURCE_BLOB); |
239 | if(fp && ferror(fp)) |
240 | ca.err = CURLE_READ_ERROR; |
241 | |
242 | fail: |
243 | if(fp) |
244 | fclose(fp); |
245 | if(ca.err == CURLE_OK) { |
246 | *anchors = ca.anchors; |
247 | *anchors_len = ca.anchors_len; |
248 | } |
249 | else { |
250 | for(i = 0; i < ca.anchors_len; ++i) |
251 | free(ca.anchors[i].dn.data); |
252 | free(ca.anchors); |
253 | } |
254 | |
255 | return ca.err; |
256 | } |
257 | |
258 | static void x509_start_chain(const br_x509_class **ctx, |
259 | const char *server_name) |
260 | { |
261 | struct x509_context *x509 = (struct x509_context *)ctx; |
262 | |
263 | if(!x509->verifyhost) |
264 | server_name = NULL; |
265 | x509->minimal.vtable->start_chain(&x509->minimal.vtable, server_name); |
266 | } |
267 | |
268 | static void x509_start_cert(const br_x509_class **ctx, uint32_t length) |
269 | { |
270 | struct x509_context *x509 = (struct x509_context *)ctx; |
271 | |
272 | x509->minimal.vtable->start_cert(&x509->minimal.vtable, length); |
273 | } |
274 | |
275 | static void x509_append(const br_x509_class **ctx, const unsigned char *buf, |
276 | size_t len) |
277 | { |
278 | struct x509_context *x509 = (struct x509_context *)ctx; |
279 | |
280 | x509->minimal.vtable->append(&x509->minimal.vtable, buf, len); |
281 | } |
282 | |
283 | static void x509_end_cert(const br_x509_class **ctx) |
284 | { |
285 | struct x509_context *x509 = (struct x509_context *)ctx; |
286 | |
287 | x509->minimal.vtable->end_cert(&x509->minimal.vtable); |
288 | } |
289 | |
290 | static unsigned x509_end_chain(const br_x509_class **ctx) |
291 | { |
292 | struct x509_context *x509 = (struct x509_context *)ctx; |
293 | unsigned err; |
294 | |
295 | err = x509->minimal.vtable->end_chain(&x509->minimal.vtable); |
296 | if(err && !x509->verifypeer) { |
297 | /* ignore any X.509 errors */ |
298 | err = BR_ERR_OK; |
299 | } |
300 | |
301 | return err; |
302 | } |
303 | |
304 | static const br_x509_pkey *x509_get_pkey(const br_x509_class *const *ctx, |
305 | unsigned *usages) |
306 | { |
307 | struct x509_context *x509 = (struct x509_context *)ctx; |
308 | |
309 | return x509->minimal.vtable->get_pkey(&x509->minimal.vtable, usages); |
310 | } |
311 | |
312 | static const br_x509_class x509_vtable = { |
313 | sizeof(struct x509_context), |
314 | x509_start_chain, |
315 | x509_start_cert, |
316 | x509_append, |
317 | x509_end_cert, |
318 | x509_end_chain, |
319 | x509_get_pkey |
320 | }; |
321 | |
322 | static CURLcode bearssl_connect_step1(struct Curl_easy *data, |
323 | struct connectdata *conn, int sockindex) |
324 | { |
325 | struct ssl_connect_data *connssl = &conn->ssl[sockindex]; |
326 | struct ssl_backend_data *backend = connssl->backend; |
327 | const struct curl_blob *ca_info_blob = SSL_CONN_CONFIG(ca_info_blob); |
328 | const char * const ssl_cafile = |
329 | /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */ |
330 | (ca_info_blob ? NULL : SSL_CONN_CONFIG(CAfile)); |
331 | const char *hostname = SSL_HOST_NAME(); |
332 | const bool verifypeer = SSL_CONN_CONFIG(verifypeer); |
333 | const bool verifyhost = SSL_CONN_CONFIG(verifyhost); |
334 | CURLcode ret; |
335 | unsigned version_min, version_max; |
336 | #ifdef ENABLE_IPV6 |
337 | struct in6_addr addr; |
338 | #else |
339 | struct in_addr addr; |
340 | #endif |
341 | |
342 | switch(SSL_CONN_CONFIG(version)) { |
343 | case CURL_SSLVERSION_SSLv2: |
344 | failf(data, "BearSSL does not support SSLv2" ); |
345 | return CURLE_SSL_CONNECT_ERROR; |
346 | case CURL_SSLVERSION_SSLv3: |
347 | failf(data, "BearSSL does not support SSLv3" ); |
348 | return CURLE_SSL_CONNECT_ERROR; |
349 | case CURL_SSLVERSION_TLSv1_0: |
350 | version_min = BR_TLS10; |
351 | version_max = BR_TLS10; |
352 | break; |
353 | case CURL_SSLVERSION_TLSv1_1: |
354 | version_min = BR_TLS11; |
355 | version_max = BR_TLS11; |
356 | break; |
357 | case CURL_SSLVERSION_TLSv1_2: |
358 | version_min = BR_TLS12; |
359 | version_max = BR_TLS12; |
360 | break; |
361 | case CURL_SSLVERSION_DEFAULT: |
362 | case CURL_SSLVERSION_TLSv1: |
363 | version_min = BR_TLS10; |
364 | version_max = BR_TLS12; |
365 | break; |
366 | default: |
367 | failf(data, "BearSSL: unknown CURLOPT_SSLVERSION" ); |
368 | return CURLE_SSL_CONNECT_ERROR; |
369 | } |
370 | |
371 | if(ca_info_blob) { |
372 | struct cafile_source source = { |
373 | CAFILE_SOURCE_BLOB, |
374 | ca_info_blob->data, |
375 | ca_info_blob->len, |
376 | }; |
377 | ret = load_cafile(&source, &backend->anchors, &backend->anchors_len); |
378 | if(ret != CURLE_OK) { |
379 | if(verifypeer) { |
380 | failf(data, "error importing CA certificate blob" ); |
381 | return ret; |
382 | } |
383 | /* Only warn if no certificate verification is required. */ |
384 | infof(data, "error importing CA certificate blob, continuing anyway" ); |
385 | } |
386 | } |
387 | |
388 | if(ssl_cafile) { |
389 | struct cafile_source source = { |
390 | CAFILE_SOURCE_PATH, |
391 | ssl_cafile, |
392 | 0, |
393 | }; |
394 | ret = load_cafile(&source, &backend->anchors, &backend->anchors_len); |
395 | if(ret != CURLE_OK) { |
396 | if(verifypeer) { |
397 | failf(data, "error setting certificate verify locations." |
398 | " CAfile: %s" , ssl_cafile); |
399 | return ret; |
400 | } |
401 | infof(data, "error setting certificate verify locations," |
402 | " continuing anyway:" ); |
403 | } |
404 | } |
405 | |
406 | /* initialize SSL context */ |
407 | br_ssl_client_init_full(&backend->ctx, &backend->x509.minimal, |
408 | backend->anchors, backend->anchors_len); |
409 | br_ssl_engine_set_versions(&backend->ctx.eng, version_min, version_max); |
410 | br_ssl_engine_set_buffer(&backend->ctx.eng, backend->buf, |
411 | sizeof(backend->buf), 1); |
412 | |
413 | /* initialize X.509 context */ |
414 | backend->x509.vtable = &x509_vtable; |
415 | backend->x509.verifypeer = verifypeer; |
416 | backend->x509.verifyhost = verifyhost; |
417 | br_ssl_engine_set_x509(&backend->ctx.eng, &backend->x509.vtable); |
418 | |
419 | if(SSL_SET_OPTION(primary.sessionid)) { |
420 | void *session; |
421 | |
422 | Curl_ssl_sessionid_lock(data); |
423 | if(!Curl_ssl_getsessionid(data, conn, SSL_IS_PROXY() ? TRUE : FALSE, |
424 | &session, NULL, sockindex)) { |
425 | br_ssl_engine_set_session_parameters(&backend->ctx.eng, session); |
426 | infof(data, "BearSSL: re-using session ID" ); |
427 | } |
428 | Curl_ssl_sessionid_unlock(data); |
429 | } |
430 | |
431 | if(conn->bits.tls_enable_alpn) { |
432 | int cur = 0; |
433 | |
434 | /* NOTE: when adding more protocols here, increase the size of the |
435 | * protocols array in `struct ssl_backend_data`. |
436 | */ |
437 | |
438 | #ifdef USE_HTTP2 |
439 | if(data->state.httpwant >= CURL_HTTP_VERSION_2 |
440 | #ifndef CURL_DISABLE_PROXY |
441 | && (!SSL_IS_PROXY() || !conn->bits.tunnel_proxy) |
442 | #endif |
443 | ) { |
444 | backend->protocols[cur++] = ALPN_H2; |
445 | infof(data, "ALPN, offering %s" , ALPN_H2); |
446 | } |
447 | #endif |
448 | |
449 | backend->protocols[cur++] = ALPN_HTTP_1_1; |
450 | infof(data, "ALPN, offering %s" , ALPN_HTTP_1_1); |
451 | |
452 | br_ssl_engine_set_protocol_names(&backend->ctx.eng, |
453 | backend->protocols, cur); |
454 | } |
455 | |
456 | if((1 == Curl_inet_pton(AF_INET, hostname, &addr)) |
457 | #ifdef ENABLE_IPV6 |
458 | || (1 == Curl_inet_pton(AF_INET6, hostname, &addr)) |
459 | #endif |
460 | ) { |
461 | if(verifyhost) { |
462 | failf(data, "BearSSL: " |
463 | "host verification of IP address is not supported" ); |
464 | return CURLE_PEER_FAILED_VERIFICATION; |
465 | } |
466 | hostname = NULL; |
467 | } |
468 | |
469 | if(!br_ssl_client_reset(&backend->ctx, hostname, 0)) |
470 | return CURLE_FAILED_INIT; |
471 | backend->active = TRUE; |
472 | |
473 | connssl->connecting_state = ssl_connect_2; |
474 | |
475 | return CURLE_OK; |
476 | } |
477 | |
478 | static CURLcode bearssl_run_until(struct Curl_easy *data, |
479 | struct connectdata *conn, int sockindex, |
480 | unsigned target) |
481 | { |
482 | struct ssl_connect_data *connssl = &conn->ssl[sockindex]; |
483 | struct ssl_backend_data *backend = connssl->backend; |
484 | curl_socket_t sockfd = conn->sock[sockindex]; |
485 | unsigned state; |
486 | unsigned char *buf; |
487 | size_t len; |
488 | ssize_t ret; |
489 | int err; |
490 | |
491 | for(;;) { |
492 | state = br_ssl_engine_current_state(&backend->ctx.eng); |
493 | if(state & BR_SSL_CLOSED) { |
494 | err = br_ssl_engine_last_error(&backend->ctx.eng); |
495 | switch(err) { |
496 | case BR_ERR_OK: |
497 | /* TLS close notify */ |
498 | if(connssl->state != ssl_connection_complete) { |
499 | failf(data, "SSL: connection closed during handshake" ); |
500 | return CURLE_SSL_CONNECT_ERROR; |
501 | } |
502 | return CURLE_OK; |
503 | case BR_ERR_X509_EXPIRED: |
504 | failf(data, "SSL: X.509 verification: " |
505 | "certificate is expired or not yet valid" ); |
506 | return CURLE_PEER_FAILED_VERIFICATION; |
507 | case BR_ERR_X509_BAD_SERVER_NAME: |
508 | failf(data, "SSL: X.509 verification: " |
509 | "expected server name was not found in the chain" ); |
510 | return CURLE_PEER_FAILED_VERIFICATION; |
511 | case BR_ERR_X509_NOT_TRUSTED: |
512 | failf(data, "SSL: X.509 verification: " |
513 | "chain could not be linked to a trust anchor" ); |
514 | return CURLE_PEER_FAILED_VERIFICATION; |
515 | } |
516 | /* X.509 errors are documented to have the range 32..63 */ |
517 | if(err >= 32 && err < 64) |
518 | return CURLE_PEER_FAILED_VERIFICATION; |
519 | return CURLE_SSL_CONNECT_ERROR; |
520 | } |
521 | if(state & target) |
522 | return CURLE_OK; |
523 | if(state & BR_SSL_SENDREC) { |
524 | buf = br_ssl_engine_sendrec_buf(&backend->ctx.eng, &len); |
525 | ret = swrite(sockfd, buf, len); |
526 | if(ret == -1) { |
527 | if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) { |
528 | if(connssl->state != ssl_connection_complete) |
529 | connssl->connecting_state = ssl_connect_2_writing; |
530 | return CURLE_AGAIN; |
531 | } |
532 | return CURLE_WRITE_ERROR; |
533 | } |
534 | br_ssl_engine_sendrec_ack(&backend->ctx.eng, ret); |
535 | } |
536 | else if(state & BR_SSL_RECVREC) { |
537 | buf = br_ssl_engine_recvrec_buf(&backend->ctx.eng, &len); |
538 | ret = sread(sockfd, buf, len); |
539 | if(ret == 0) { |
540 | failf(data, "SSL: EOF without close notify" ); |
541 | return CURLE_READ_ERROR; |
542 | } |
543 | if(ret == -1) { |
544 | if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) { |
545 | if(connssl->state != ssl_connection_complete) |
546 | connssl->connecting_state = ssl_connect_2_reading; |
547 | return CURLE_AGAIN; |
548 | } |
549 | return CURLE_READ_ERROR; |
550 | } |
551 | br_ssl_engine_recvrec_ack(&backend->ctx.eng, ret); |
552 | } |
553 | } |
554 | } |
555 | |
556 | static CURLcode bearssl_connect_step2(struct Curl_easy *data, |
557 | struct connectdata *conn, int sockindex) |
558 | { |
559 | struct ssl_connect_data *connssl = &conn->ssl[sockindex]; |
560 | struct ssl_backend_data *backend = connssl->backend; |
561 | CURLcode ret; |
562 | |
563 | ret = bearssl_run_until(data, conn, sockindex, |
564 | BR_SSL_SENDAPP | BR_SSL_RECVAPP); |
565 | if(ret == CURLE_AGAIN) |
566 | return CURLE_OK; |
567 | if(ret == CURLE_OK) { |
568 | if(br_ssl_engine_current_state(&backend->ctx.eng) == BR_SSL_CLOSED) { |
569 | failf(data, "SSL: connection closed during handshake" ); |
570 | return CURLE_SSL_CONNECT_ERROR; |
571 | } |
572 | connssl->connecting_state = ssl_connect_3; |
573 | } |
574 | return ret; |
575 | } |
576 | |
577 | static CURLcode bearssl_connect_step3(struct Curl_easy *data, |
578 | struct connectdata *conn, int sockindex) |
579 | { |
580 | struct ssl_connect_data *connssl = &conn->ssl[sockindex]; |
581 | struct ssl_backend_data *backend = connssl->backend; |
582 | CURLcode ret; |
583 | |
584 | DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); |
585 | |
586 | if(conn->bits.tls_enable_alpn) { |
587 | const char *protocol; |
588 | |
589 | protocol = br_ssl_engine_get_selected_protocol(&backend->ctx.eng); |
590 | if(protocol) { |
591 | infof(data, "ALPN, server accepted to use %s" , protocol); |
592 | |
593 | #ifdef USE_HTTP2 |
594 | if(!strcmp(protocol, ALPN_H2)) |
595 | conn->negnpn = CURL_HTTP_VERSION_2; |
596 | else |
597 | #endif |
598 | if(!strcmp(protocol, ALPN_HTTP_1_1)) |
599 | conn->negnpn = CURL_HTTP_VERSION_1_1; |
600 | else |
601 | infof(data, "ALPN, unrecognized protocol %s" , protocol); |
602 | Curl_multiuse_state(data, conn->negnpn == CURL_HTTP_VERSION_2 ? |
603 | BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); |
604 | } |
605 | else |
606 | infof(data, "ALPN, server did not agree to a protocol" ); |
607 | } |
608 | |
609 | if(SSL_SET_OPTION(primary.sessionid)) { |
610 | bool incache; |
611 | void *oldsession; |
612 | br_ssl_session_parameters *session; |
613 | |
614 | session = malloc(sizeof(*session)); |
615 | if(!session) |
616 | return CURLE_OUT_OF_MEMORY; |
617 | br_ssl_engine_get_session_parameters(&backend->ctx.eng, session); |
618 | Curl_ssl_sessionid_lock(data); |
619 | incache = !(Curl_ssl_getsessionid(data, conn, |
620 | SSL_IS_PROXY() ? TRUE : FALSE, |
621 | &oldsession, NULL, sockindex)); |
622 | if(incache) |
623 | Curl_ssl_delsessionid(data, oldsession); |
624 | ret = Curl_ssl_addsessionid(data, conn, |
625 | SSL_IS_PROXY() ? TRUE : FALSE, |
626 | session, 0, sockindex); |
627 | Curl_ssl_sessionid_unlock(data); |
628 | if(ret) { |
629 | free(session); |
630 | return CURLE_OUT_OF_MEMORY; |
631 | } |
632 | } |
633 | |
634 | connssl->connecting_state = ssl_connect_done; |
635 | |
636 | return CURLE_OK; |
637 | } |
638 | |
639 | static ssize_t bearssl_send(struct Curl_easy *data, int sockindex, |
640 | const void *buf, size_t len, CURLcode *err) |
641 | { |
642 | struct connectdata *conn = data->conn; |
643 | struct ssl_connect_data *connssl = &conn->ssl[sockindex]; |
644 | struct ssl_backend_data *backend = connssl->backend; |
645 | unsigned char *app; |
646 | size_t applen; |
647 | |
648 | for(;;) { |
649 | *err = bearssl_run_until(data, conn, sockindex, BR_SSL_SENDAPP); |
650 | if (*err != CURLE_OK) |
651 | return -1; |
652 | app = br_ssl_engine_sendapp_buf(&backend->ctx.eng, &applen); |
653 | if(!app) { |
654 | failf(data, "SSL: connection closed during write" ); |
655 | *err = CURLE_SEND_ERROR; |
656 | return -1; |
657 | } |
658 | if(backend->pending_write) { |
659 | applen = backend->pending_write; |
660 | backend->pending_write = 0; |
661 | return applen; |
662 | } |
663 | if(applen > len) |
664 | applen = len; |
665 | memcpy(app, buf, applen); |
666 | br_ssl_engine_sendapp_ack(&backend->ctx.eng, applen); |
667 | br_ssl_engine_flush(&backend->ctx.eng, 0); |
668 | backend->pending_write = applen; |
669 | } |
670 | } |
671 | |
672 | static ssize_t bearssl_recv(struct Curl_easy *data, int sockindex, |
673 | char *buf, size_t len, CURLcode *err) |
674 | { |
675 | struct connectdata *conn = data->conn; |
676 | struct ssl_connect_data *connssl = &conn->ssl[sockindex]; |
677 | struct ssl_backend_data *backend = connssl->backend; |
678 | unsigned char *app; |
679 | size_t applen; |
680 | |
681 | *err = bearssl_run_until(data, conn, sockindex, BR_SSL_RECVAPP); |
682 | if(*err != CURLE_OK) |
683 | return -1; |
684 | app = br_ssl_engine_recvapp_buf(&backend->ctx.eng, &applen); |
685 | if(!app) |
686 | return 0; |
687 | if(applen > len) |
688 | applen = len; |
689 | memcpy(buf, app, applen); |
690 | br_ssl_engine_recvapp_ack(&backend->ctx.eng, applen); |
691 | |
692 | return applen; |
693 | } |
694 | |
695 | static CURLcode bearssl_connect_common(struct Curl_easy *data, |
696 | struct connectdata *conn, |
697 | int sockindex, |
698 | bool nonblocking, |
699 | bool *done) |
700 | { |
701 | CURLcode ret; |
702 | struct ssl_connect_data *connssl = &conn->ssl[sockindex]; |
703 | curl_socket_t sockfd = conn->sock[sockindex]; |
704 | timediff_t timeout_ms; |
705 | int what; |
706 | |
707 | /* check if the connection has already been established */ |
708 | if(ssl_connection_complete == connssl->state) { |
709 | *done = TRUE; |
710 | return CURLE_OK; |
711 | } |
712 | |
713 | if(ssl_connect_1 == connssl->connecting_state) { |
714 | ret = bearssl_connect_step1(data, conn, sockindex); |
715 | if(ret) |
716 | return ret; |
717 | } |
718 | |
719 | while(ssl_connect_2 == connssl->connecting_state || |
720 | ssl_connect_2_reading == connssl->connecting_state || |
721 | ssl_connect_2_writing == connssl->connecting_state) { |
722 | /* check allowed time left */ |
723 | timeout_ms = Curl_timeleft(data, NULL, TRUE); |
724 | |
725 | if(timeout_ms < 0) { |
726 | /* no need to continue if time already is up */ |
727 | failf(data, "SSL connection timeout" ); |
728 | return CURLE_OPERATION_TIMEDOUT; |
729 | } |
730 | |
731 | /* if ssl is expecting something, check if it's available. */ |
732 | if(ssl_connect_2_reading == connssl->connecting_state || |
733 | ssl_connect_2_writing == connssl->connecting_state) { |
734 | |
735 | curl_socket_t writefd = ssl_connect_2_writing == |
736 | connssl->connecting_state?sockfd:CURL_SOCKET_BAD; |
737 | curl_socket_t readfd = ssl_connect_2_reading == |
738 | connssl->connecting_state?sockfd:CURL_SOCKET_BAD; |
739 | |
740 | what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, |
741 | nonblocking?0:timeout_ms); |
742 | if(what < 0) { |
743 | /* fatal error */ |
744 | failf(data, "select/poll on SSL socket, errno: %d" , SOCKERRNO); |
745 | return CURLE_SSL_CONNECT_ERROR; |
746 | } |
747 | else if(0 == what) { |
748 | if(nonblocking) { |
749 | *done = FALSE; |
750 | return CURLE_OK; |
751 | } |
752 | else { |
753 | /* timeout */ |
754 | failf(data, "SSL connection timeout" ); |
755 | return CURLE_OPERATION_TIMEDOUT; |
756 | } |
757 | } |
758 | /* socket is readable or writable */ |
759 | } |
760 | |
761 | /* Run transaction, and return to the caller if it failed or if this |
762 | * connection is done nonblocking and this loop would execute again. This |
763 | * permits the owner of a multi handle to abort a connection attempt |
764 | * before step2 has completed while ensuring that a client using select() |
765 | * or epoll() will always have a valid fdset to wait on. |
766 | */ |
767 | ret = bearssl_connect_step2(data, conn, sockindex); |
768 | if(ret || (nonblocking && |
769 | (ssl_connect_2 == connssl->connecting_state || |
770 | ssl_connect_2_reading == connssl->connecting_state || |
771 | ssl_connect_2_writing == connssl->connecting_state))) |
772 | return ret; |
773 | } |
774 | |
775 | if(ssl_connect_3 == connssl->connecting_state) { |
776 | ret = bearssl_connect_step3(data, conn, sockindex); |
777 | if(ret) |
778 | return ret; |
779 | } |
780 | |
781 | if(ssl_connect_done == connssl->connecting_state) { |
782 | connssl->state = ssl_connection_complete; |
783 | conn->recv[sockindex] = bearssl_recv; |
784 | conn->send[sockindex] = bearssl_send; |
785 | *done = TRUE; |
786 | } |
787 | else |
788 | *done = FALSE; |
789 | |
790 | /* Reset our connect state machine */ |
791 | connssl->connecting_state = ssl_connect_1; |
792 | |
793 | return CURLE_OK; |
794 | } |
795 | |
796 | static size_t bearssl_version(char *buffer, size_t size) |
797 | { |
798 | return msnprintf(buffer, size, "BearSSL" ); |
799 | } |
800 | |
801 | static bool bearssl_data_pending(const struct connectdata *conn, |
802 | int connindex) |
803 | { |
804 | const struct ssl_connect_data *connssl = &conn->ssl[connindex]; |
805 | struct ssl_backend_data *backend = connssl->backend; |
806 | return br_ssl_engine_current_state(&backend->ctx.eng) & BR_SSL_RECVAPP; |
807 | } |
808 | |
809 | static CURLcode bearssl_random(struct Curl_easy *data UNUSED_PARAM, |
810 | unsigned char *entropy, size_t length) |
811 | { |
812 | static br_hmac_drbg_context ctx; |
813 | static bool seeded = FALSE; |
814 | |
815 | if(!seeded) { |
816 | br_prng_seeder seeder; |
817 | |
818 | br_hmac_drbg_init(&ctx, &br_sha256_vtable, NULL, 0); |
819 | seeder = br_prng_seeder_system(NULL); |
820 | if(!seeder || !seeder(&ctx.vtable)) |
821 | return CURLE_FAILED_INIT; |
822 | seeded = TRUE; |
823 | } |
824 | br_hmac_drbg_generate(&ctx, entropy, length); |
825 | |
826 | return CURLE_OK; |
827 | } |
828 | |
829 | static CURLcode bearssl_connect(struct Curl_easy *data, |
830 | struct connectdata *conn, int sockindex) |
831 | { |
832 | CURLcode ret; |
833 | bool done = FALSE; |
834 | |
835 | ret = bearssl_connect_common(data, conn, sockindex, FALSE, &done); |
836 | if(ret) |
837 | return ret; |
838 | |
839 | DEBUGASSERT(done); |
840 | |
841 | return CURLE_OK; |
842 | } |
843 | |
844 | static CURLcode bearssl_connect_nonblocking(struct Curl_easy *data, |
845 | struct connectdata *conn, |
846 | int sockindex, bool *done) |
847 | { |
848 | return bearssl_connect_common(data, conn, sockindex, TRUE, done); |
849 | } |
850 | |
851 | static void *bearssl_get_internals(struct ssl_connect_data *connssl, |
852 | CURLINFO info UNUSED_PARAM) |
853 | { |
854 | struct ssl_backend_data *backend = connssl->backend; |
855 | return &backend->ctx; |
856 | } |
857 | |
858 | static void bearssl_close(struct Curl_easy *data, |
859 | struct connectdata *conn, int sockindex) |
860 | { |
861 | struct ssl_connect_data *connssl = &conn->ssl[sockindex]; |
862 | struct ssl_backend_data *backend = connssl->backend; |
863 | size_t i; |
864 | |
865 | if(backend->active) { |
866 | br_ssl_engine_close(&backend->ctx.eng); |
867 | (void)bearssl_run_until(data, conn, sockindex, BR_SSL_CLOSED); |
868 | } |
869 | for(i = 0; i < backend->anchors_len; ++i) |
870 | free(backend->anchors[i].dn.data); |
871 | free(backend->anchors); |
872 | } |
873 | |
874 | static void bearssl_session_free(void *ptr) |
875 | { |
876 | free(ptr); |
877 | } |
878 | |
879 | static CURLcode bearssl_sha256sum(const unsigned char *input, |
880 | size_t inputlen, |
881 | unsigned char *sha256sum, |
882 | size_t sha256len UNUSED_PARAM) |
883 | { |
884 | br_sha256_context ctx; |
885 | |
886 | br_sha256_init(&ctx); |
887 | br_sha256_update(&ctx, input, inputlen); |
888 | br_sha256_out(&ctx, sha256sum); |
889 | return CURLE_OK; |
890 | } |
891 | |
892 | const struct Curl_ssl Curl_ssl_bearssl = { |
893 | { CURLSSLBACKEND_BEARSSL, "bearssl" }, /* info */ |
894 | SSLSUPP_CAINFO_BLOB, |
895 | sizeof(struct ssl_backend_data), |
896 | |
897 | Curl_none_init, /* init */ |
898 | Curl_none_cleanup, /* cleanup */ |
899 | bearssl_version, /* version */ |
900 | Curl_none_check_cxn, /* check_cxn */ |
901 | Curl_none_shutdown, /* shutdown */ |
902 | bearssl_data_pending, /* data_pending */ |
903 | bearssl_random, /* random */ |
904 | Curl_none_cert_status_request, /* cert_status_request */ |
905 | bearssl_connect, /* connect */ |
906 | bearssl_connect_nonblocking, /* connect_nonblocking */ |
907 | Curl_ssl_getsock, /* getsock */ |
908 | bearssl_get_internals, /* get_internals */ |
909 | bearssl_close, /* close_one */ |
910 | Curl_none_close_all, /* close_all */ |
911 | bearssl_session_free, /* session_free */ |
912 | Curl_none_set_engine, /* set_engine */ |
913 | Curl_none_set_engine_default, /* set_engine_default */ |
914 | Curl_none_engines_list, /* engines_list */ |
915 | Curl_none_false_start, /* false_start */ |
916 | bearssl_sha256sum, /* sha256sum */ |
917 | NULL, /* associate_connection */ |
918 | NULL /* disassociate_connection */ |
919 | }; |
920 | |
921 | #endif /* USE_BEARSSL */ |
922 | |