diff options
Diffstat (limited to 'lib/vtls/bearssl.c')
-rw-r--r-- | lib/vtls/bearssl.c | 286 |
1 files changed, 166 insertions, 120 deletions
diff --git a/lib/vtls/bearssl.c b/lib/vtls/bearssl.c index 44e7406e8..9b772d064 100644 --- a/lib/vtls/bearssl.c +++ b/lib/vtls/bearssl.c @@ -5,11 +5,11 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2019 - 2020, Michael Forney, <mforney@mforney.org> + * Copyright (C) 2019 - 2021, Michael Forney, <mforney@mforney.org> * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms - * are also available at https://curl.haxx.se/docs/copyright.html. + * are also available at https://curl.se/docs/copyright.html. * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is @@ -68,6 +68,14 @@ struct cafile_parser { size_t dn_len; }; +#define CAFILE_SOURCE_PATH 1 +#define CAFILE_SOURCE_BLOB 2 +struct cafile_source { + const int type; + const char * const data; + const size_t len; +}; + static void append_dn(void *ctx, const void *buf, size_t len) { struct cafile_parser *ca = ctx; @@ -90,7 +98,8 @@ static void x509_push(void *ctx, const void *buf, size_t len) br_x509_decoder_push(&ca->xc, buf, len); } -static CURLcode load_cafile(const char *path, br_x509_trust_anchor **anchors, +static CURLcode load_cafile(struct cafile_source *source, + br_x509_trust_anchor **anchors, size_t *anchors_len) { struct cafile_parser ca; @@ -100,13 +109,22 @@ static CURLcode load_cafile(const char *path, br_x509_trust_anchor **anchors, br_x509_trust_anchor *new_anchors; size_t new_anchors_len; br_x509_pkey *pkey; - FILE *fp; - unsigned char buf[BUFSIZ], *p; + FILE *fp = 0; + unsigned char buf[BUFSIZ]; + const unsigned char *p; const char *name; size_t n, i, pushed; - fp = fopen(path, "rb"); - if(!fp) + DEBUGASSERT(source->type == CAFILE_SOURCE_PATH + || source->type == CAFILE_SOURCE_BLOB); + + if(source->type == CAFILE_SOURCE_PATH) { + fp = fopen(source->data, "rb"); + if(!fp) + return CURLE_SSL_CACERT_BADFILE; + } + + if(source->type == CAFILE_SOURCE_BLOB && source->len > (size_t)INT_MAX) return CURLE_SSL_CACERT_BADFILE; ca.err = CURLE_OK; @@ -115,11 +133,17 @@ static CURLcode load_cafile(const char *path, br_x509_trust_anchor **anchors, ca.anchors_len = 0; br_pem_decoder_init(&pc); br_pem_decoder_setdest(&pc, x509_push, &ca); - for(;;) { - n = fread(buf, 1, sizeof(buf), fp); - if(n == 0) - break; - p = buf; + do { + if(source->type == CAFILE_SOURCE_PATH) { + n = fread(buf, 1, sizeof(buf), fp); + if(n == 0) + break; + p = buf; + } + else if(source->type == CAFILE_SOURCE_BLOB) { + n = source->len; + p = (unsigned char *) source->data; + } while(n) { pushed = br_pem_decoder_push(&pc, p, n); if(ca.err) @@ -211,12 +235,13 @@ static CURLcode load_cafile(const char *path, br_x509_trust_anchor **anchors, goto fail; } } - } - if(ferror(fp)) + } while(source->type != CAFILE_SOURCE_BLOB); + if(fp && ferror(fp)) ca.err = CURLE_READ_ERROR; fail: - fclose(fp); + if(fp) + fclose(fp); if(ca.err == CURLE_OK) { *anchors = ca.anchors; *anchors_len = ca.anchors_len; @@ -294,18 +319,16 @@ static const br_x509_class x509_vtable = { x509_get_pkey }; -static CURLcode bearssl_connect_step1(struct connectdata *conn, int sockindex) +static CURLcode bearssl_connect_step1(struct Curl_easy *data, + struct connectdata *conn, int sockindex) { - struct Curl_easy *data = conn->data; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; struct ssl_backend_data *backend = connssl->backend; - const char * const ssl_cafile = SSL_CONN_CONFIG(CAfile); -#ifndef CURL_DISABLE_PROXY - const char *hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name : - conn->host.name; -#else - const char *hostname = conn->host.name; -#endif + const struct curl_blob *ca_info_blob = SSL_CONN_CONFIG(ca_info_blob); + const char * const ssl_cafile = + /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */ + (ca_info_blob ? NULL : SSL_CONN_CONFIG(CAfile)); + const char *hostname = SSL_HOST_NAME(); const bool verifypeer = SSL_CONN_CONFIG(verifypeer); const bool verifyhost = SSL_CONN_CONFIG(verifyhost); CURLcode ret; @@ -345,16 +368,38 @@ static CURLcode bearssl_connect_step1(struct connectdata *conn, int sockindex) return CURLE_SSL_CONNECT_ERROR; } + if(ca_info_blob) { + struct cafile_source source = { + CAFILE_SOURCE_BLOB, + ca_info_blob->data, + ca_info_blob->len, + }; + ret = load_cafile(&source, &backend->anchors, &backend->anchors_len); + if(ret != CURLE_OK) { + if(verifypeer) { + failf(data, "error importing CA certificate blob"); + return ret; + } + /* Only warn if no certificate verification is required. */ + infof(data, "error importing CA certificate blob, continuing anyway"); + } + } + if(ssl_cafile) { - ret = load_cafile(ssl_cafile, &backend->anchors, &backend->anchors_len); + struct cafile_source source = { + CAFILE_SOURCE_PATH, + ssl_cafile, + 0, + }; + ret = load_cafile(&source, &backend->anchors, &backend->anchors_len); if(ret != CURLE_OK) { if(verifypeer) { - failf(data, "error setting certificate verify locations:\n" - " CAfile: %s\n", ssl_cafile); + failf(data, "error setting certificate verify locations." + " CAfile: %s", ssl_cafile); return ret; } infof(data, "error setting certificate verify locations," - " continuing anyway:\n"); + " continuing anyway:"); } } @@ -374,12 +419,13 @@ static CURLcode bearssl_connect_step1(struct connectdata *conn, int sockindex) if(SSL_SET_OPTION(primary.sessionid)) { void *session; - Curl_ssl_sessionid_lock(conn); - if(!Curl_ssl_getsessionid(conn, &session, NULL, sockindex)) { + Curl_ssl_sessionid_lock(data); + if(!Curl_ssl_getsessionid(data, conn, SSL_IS_PROXY() ? TRUE : FALSE, + &session, NULL, sockindex)) { br_ssl_engine_set_session_parameters(&backend->ctx.eng, session); - infof(data, "BearSSL: re-using session ID\n"); + infof(data, "BearSSL: re-using session ID"); } - Curl_ssl_sessionid_unlock(conn); + Curl_ssl_sessionid_unlock(data); } if(conn->bits.tls_enable_alpn) { @@ -389,19 +435,19 @@ static CURLcode bearssl_connect_step1(struct connectdata *conn, int sockindex) * protocols array in `struct ssl_backend_data`. */ -#ifdef USE_NGHTTP2 - if(data->set.httpversion >= CURL_HTTP_VERSION_2 +#ifdef USE_HTTP2 + if(data->state.httpwant >= CURL_HTTP_VERSION_2 #ifndef CURL_DISABLE_PROXY && (!SSL_IS_PROXY() || !conn->bits.tunnel_proxy) #endif ) { - backend->protocols[cur++] = NGHTTP2_PROTO_VERSION_ID; - infof(data, "ALPN, offering %s\n", NGHTTP2_PROTO_VERSION_ID); + backend->protocols[cur++] = ALPN_H2; + infof(data, "ALPN, offering %s", ALPN_H2); } #endif backend->protocols[cur++] = ALPN_HTTP_1_1; - infof(data, "ALPN, offering %s\n", ALPN_HTTP_1_1); + infof(data, "ALPN, offering %s", ALPN_HTTP_1_1); br_ssl_engine_set_protocol_names(&backend->ctx.eng, backend->protocols, cur); @@ -429,10 +475,10 @@ static CURLcode bearssl_connect_step1(struct connectdata *conn, int sockindex) return CURLE_OK; } -static CURLcode bearssl_run_until(struct connectdata *conn, int sockindex, +static CURLcode bearssl_run_until(struct Curl_easy *data, + struct connectdata *conn, int sockindex, unsigned target) { - struct Curl_easy *data = conn->data; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; struct ssl_backend_data *backend = connssl->backend; curl_socket_t sockfd = conn->sock[sockindex]; @@ -507,14 +553,15 @@ static CURLcode bearssl_run_until(struct connectdata *conn, int sockindex, } } -static CURLcode bearssl_connect_step2(struct connectdata *conn, int sockindex) +static CURLcode bearssl_connect_step2(struct Curl_easy *data, + struct connectdata *conn, int sockindex) { - struct Curl_easy *data = conn->data; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; struct ssl_backend_data *backend = connssl->backend; CURLcode ret; - ret = bearssl_run_until(conn, sockindex, BR_SSL_SENDAPP | BR_SSL_RECVAPP); + ret = bearssl_run_until(data, conn, sockindex, + BR_SSL_SENDAPP | BR_SSL_RECVAPP); if(ret == CURLE_AGAIN) return CURLE_OK; if(ret == CURLE_OK) { @@ -527,9 +574,9 @@ static CURLcode bearssl_connect_step2(struct connectdata *conn, int sockindex) return ret; } -static CURLcode bearssl_connect_step3(struct connectdata *conn, int sockindex) +static CURLcode bearssl_connect_step3(struct Curl_easy *data, + struct connectdata *conn, int sockindex) { - struct Curl_easy *data = conn->data; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; struct ssl_backend_data *backend = connssl->backend; CURLcode ret; @@ -541,26 +588,27 @@ static CURLcode bearssl_connect_step3(struct connectdata *conn, int sockindex) protocol = br_ssl_engine_get_selected_protocol(&backend->ctx.eng); if(protocol) { - infof(data, "ALPN, server accepted to use %s\n", protocol); + infof(data, "ALPN, server accepted to use %s", protocol); -#ifdef USE_NGHTTP2 - if(!strcmp(protocol, NGHTTP2_PROTO_VERSION_ID)) +#ifdef USE_HTTP2 + if(!strcmp(protocol, ALPN_H2)) conn->negnpn = CURL_HTTP_VERSION_2; else #endif if(!strcmp(protocol, ALPN_HTTP_1_1)) conn->negnpn = CURL_HTTP_VERSION_1_1; else - infof(data, "ALPN, unrecognized protocol %s\n", protocol); - Curl_multiuse_state(conn, conn->negnpn == CURL_HTTP_VERSION_2 ? + infof(data, "ALPN, unrecognized protocol %s", protocol); + Curl_multiuse_state(data, conn->negnpn == CURL_HTTP_VERSION_2 ? BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); } else - infof(data, "ALPN, server did not agree to a protocol\n"); + infof(data, "ALPN, server did not agree to a protocol"); } if(SSL_SET_OPTION(primary.sessionid)) { bool incache; + bool added = FALSE; void *oldsession; br_ssl_session_parameters *session; @@ -568,14 +616,19 @@ static CURLcode bearssl_connect_step3(struct connectdata *conn, int sockindex) if(!session) return CURLE_OUT_OF_MEMORY; br_ssl_engine_get_session_parameters(&backend->ctx.eng, session); - Curl_ssl_sessionid_lock(conn); - incache = !(Curl_ssl_getsessionid(conn, &oldsession, NULL, sockindex)); + Curl_ssl_sessionid_lock(data); + incache = !(Curl_ssl_getsessionid(data, conn, + SSL_IS_PROXY() ? TRUE : FALSE, + &oldsession, NULL, sockindex)); if(incache) - Curl_ssl_delsessionid(conn, oldsession); - ret = Curl_ssl_addsessionid(conn, session, 0, sockindex); - Curl_ssl_sessionid_unlock(conn); - if(ret) { + Curl_ssl_delsessionid(data, oldsession); + ret = Curl_ssl_addsessionid(data, conn, + SSL_IS_PROXY() ? TRUE : FALSE, + session, 0, sockindex, &added); + Curl_ssl_sessionid_unlock(data); + if(!added) free(session); + if(ret) { return CURLE_OUT_OF_MEMORY; } } @@ -585,17 +638,17 @@ static CURLcode bearssl_connect_step3(struct connectdata *conn, int sockindex) return CURLE_OK; } -static ssize_t bearssl_send(struct connectdata *conn, int sockindex, +static ssize_t bearssl_send(struct Curl_easy *data, int sockindex, const void *buf, size_t len, CURLcode *err) { - struct Curl_easy *data = conn->data; + struct connectdata *conn = data->conn; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; struct ssl_backend_data *backend = connssl->backend; unsigned char *app; size_t applen; for(;;) { - *err = bearssl_run_until(conn, sockindex, BR_SSL_SENDAPP); + *err = bearssl_run_until(data, conn, sockindex, BR_SSL_SENDAPP); if (*err != CURLE_OK) return -1; app = br_ssl_engine_sendapp_buf(&backend->ctx.eng, &applen); @@ -618,15 +671,16 @@ static ssize_t bearssl_send(struct connectdata *conn, int sockindex, } } -static ssize_t bearssl_recv(struct connectdata *conn, int sockindex, +static ssize_t bearssl_recv(struct Curl_easy *data, int sockindex, char *buf, size_t len, CURLcode *err) { + struct connectdata *conn = data->conn; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; struct ssl_backend_data *backend = connssl->backend; unsigned char *app; size_t applen; - *err = bearssl_run_until(conn, sockindex, BR_SSL_RECVAPP); + *err = bearssl_run_until(data, conn, sockindex, BR_SSL_RECVAPP); if(*err != CURLE_OK) return -1; app = br_ssl_engine_recvapp_buf(&backend->ctx.eng, &applen); @@ -640,13 +694,13 @@ static ssize_t bearssl_recv(struct connectdata *conn, int sockindex, return applen; } -static CURLcode bearssl_connect_common(struct connectdata *conn, +static CURLcode bearssl_connect_common(struct Curl_easy *data, + struct connectdata *conn, int sockindex, bool nonblocking, bool *done) { CURLcode ret; - struct Curl_easy *data = conn->data; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; curl_socket_t sockfd = conn->sock[sockindex]; timediff_t timeout_ms; @@ -659,7 +713,7 @@ static CURLcode bearssl_connect_common(struct connectdata *conn, } if(ssl_connect_1 == connssl->connecting_state) { - ret = bearssl_connect_step1(conn, sockindex); + ret = bearssl_connect_step1(data, conn, sockindex); if(ret) return ret; } @@ -712,7 +766,7 @@ static CURLcode bearssl_connect_common(struct connectdata *conn, * before step2 has completed while ensuring that a client using select() * or epoll() will always have a valid fdset to wait on. */ - ret = bearssl_connect_step2(conn, sockindex); + ret = bearssl_connect_step2(data, conn, sockindex); if(ret || (nonblocking && (ssl_connect_2 == connssl->connecting_state || ssl_connect_2_reading == connssl->connecting_state || @@ -721,7 +775,7 @@ static CURLcode bearssl_connect_common(struct connectdata *conn, } if(ssl_connect_3 == connssl->connecting_state) { - ret = bearssl_connect_step3(conn, sockindex); + ret = bearssl_connect_step3(data, conn, sockindex); if(ret) return ret; } @@ -741,21 +795,21 @@ static CURLcode bearssl_connect_common(struct connectdata *conn, return CURLE_OK; } -static size_t Curl_bearssl_version(char *buffer, size_t size) +static size_t bearssl_version(char *buffer, size_t size) { return msnprintf(buffer, size, "BearSSL"); } -static bool Curl_bearssl_data_pending(const struct connectdata *conn, - int connindex) +static bool bearssl_data_pending(const struct connectdata *conn, + int connindex) { const struct ssl_connect_data *connssl = &conn->ssl[connindex]; struct ssl_backend_data *backend = connssl->backend; return br_ssl_engine_current_state(&backend->ctx.eng) & BR_SSL_RECVAPP; } -static CURLcode Curl_bearssl_random(struct Curl_easy *data UNUSED_PARAM, - unsigned char *entropy, size_t length) +static CURLcode bearssl_random(struct Curl_easy *data UNUSED_PARAM, + unsigned char *entropy, size_t length) { static br_hmac_drbg_context ctx; static bool seeded = FALSE; @@ -774,12 +828,13 @@ static CURLcode Curl_bearssl_random(struct Curl_easy *data UNUSED_PARAM, return CURLE_OK; } -static CURLcode Curl_bearssl_connect(struct connectdata *conn, int sockindex) +static CURLcode bearssl_connect(struct Curl_easy *data, + struct connectdata *conn, int sockindex) { CURLcode ret; bool done = FALSE; - ret = bearssl_connect_common(conn, sockindex, FALSE, &done); + ret = bearssl_connect_common(data, conn, sockindex, FALSE, &done); if(ret) return ret; @@ -788,20 +843,22 @@ static CURLcode Curl_bearssl_connect(struct connectdata *conn, int sockindex) return CURLE_OK; } -static CURLcode Curl_bearssl_connect_nonblocking(struct connectdata *conn, - int sockindex, bool *done) +static CURLcode bearssl_connect_nonblocking(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, bool *done) { - return bearssl_connect_common(conn, sockindex, TRUE, done); + return bearssl_connect_common(data, conn, sockindex, TRUE, done); } -static void *Curl_bearssl_get_internals(struct ssl_connect_data *connssl, - CURLINFO info UNUSED_PARAM) +static void *bearssl_get_internals(struct ssl_connect_data *connssl, + CURLINFO info UNUSED_PARAM) { struct ssl_backend_data *backend = connssl->backend; return &backend->ctx; } -static void Curl_bearssl_close(struct connectdata *conn, int sockindex) +static void bearssl_close(struct Curl_easy *data, + struct connectdata *conn, int sockindex) { struct ssl_connect_data *connssl = &conn->ssl[sockindex]; struct ssl_backend_data *backend = connssl->backend; @@ -809,35 +866,22 @@ static void Curl_bearssl_close(struct connectdata *conn, int sockindex) if(backend->active) { br_ssl_engine_close(&backend->ctx.eng); - (void)bearssl_run_until(conn, sockindex, BR_SSL_CLOSED); + (void)bearssl_run_until(data, conn, sockindex, BR_SSL_CLOSED); } for(i = 0; i < backend->anchors_len; ++i) free(backend->anchors[i].dn.data); free(backend->anchors); } -static void Curl_bearssl_session_free(void *ptr) +static void bearssl_session_free(void *ptr) { free(ptr); } -static CURLcode Curl_bearssl_md5sum(unsigned char *input, - size_t inputlen, - unsigned char *md5sum, - size_t md5len UNUSED_PARAM) -{ - br_md5_context ctx; - - br_md5_init(&ctx); - br_md5_update(&ctx, input, inputlen); - br_md5_out(&ctx, md5sum); - return CURLE_OK; -} - -static CURLcode Curl_bearssl_sha256sum(const unsigned char *input, - size_t inputlen, - unsigned char *sha256sum, - size_t sha256len UNUSED_PARAM) +static CURLcode bearssl_sha256sum(const unsigned char *input, + size_t inputlen, + unsigned char *sha256sum, + size_t sha256len UNUSED_PARAM) { br_sha256_context ctx; @@ -848,30 +892,32 @@ static CURLcode Curl_bearssl_sha256sum(const unsigned char *input, } const struct Curl_ssl Curl_ssl_bearssl = { - { CURLSSLBACKEND_BEARSSL, "bearssl" }, - 0, + { CURLSSLBACKEND_BEARSSL, "bearssl" }, /* info */ + SSLSUPP_CAINFO_BLOB, sizeof(struct ssl_backend_data), - Curl_none_init, - Curl_none_cleanup, - Curl_bearssl_version, - Curl_none_check_cxn, - Curl_none_shutdown, - Curl_bearssl_data_pending, - Curl_bearssl_random, - Curl_none_cert_status_request, - Curl_bearssl_connect, - Curl_bearssl_connect_nonblocking, - Curl_bearssl_get_internals, - Curl_bearssl_close, - Curl_none_close_all, - Curl_bearssl_session_free, - Curl_none_set_engine, - Curl_none_set_engine_default, - Curl_none_engines_list, - Curl_none_false_start, - Curl_bearssl_md5sum, - Curl_bearssl_sha256sum + Curl_none_init, /* init */ + Curl_none_cleanup, /* cleanup */ + bearssl_version, /* version */ + Curl_none_check_cxn, /* check_cxn */ + Curl_none_shutdown, /* shutdown */ + bearssl_data_pending, /* data_pending */ + bearssl_random, /* random */ + Curl_none_cert_status_request, /* cert_status_request */ + bearssl_connect, /* connect */ + bearssl_connect_nonblocking, /* connect_nonblocking */ + Curl_ssl_getsock, /* getsock */ + bearssl_get_internals, /* get_internals */ + bearssl_close, /* close_one */ + Curl_none_close_all, /* close_all */ + bearssl_session_free, /* session_free */ + Curl_none_set_engine, /* set_engine */ + Curl_none_set_engine_default, /* set_engine_default */ + Curl_none_engines_list, /* engines_list */ + Curl_none_false_start, /* false_start */ + bearssl_sha256sum, /* sha256sum */ + NULL, /* associate_connection */ + NULL /* disassociate_connection */ }; #endif /* USE_BEARSSL */ |