diff --git a/CMakeLists.txt b/CMakeLists.txt index 13aff5f4b62351..ce03d4bdaeb3ff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1463,7 +1463,10 @@ _add_if("TLS-SRP" USE_TLS_SRP) _add_if("HTTP2" USE_NGHTTP2) _add_if("HTTP3" USE_NGTCP2 OR USE_QUICHE) _add_if("MultiSSL" CURL_WITH_MULTI_SSL) -_add_if("HTTPS-proxy" SSL_ENABLED AND (USE_OPENSSL OR USE_GNUTLS OR USE_NSS)) +# TODO wolfSSL only support this from v5.0.0 onwards +_add_if("HTTPS-proxy" SSL_ENABLED AND (USE_OPENSSL OR USE_GNUTLS OR USE_NSS + OR USE_SCHANNEL OR USE_RUSTLS OR USE_BEARSSL OR + USE_MBEDTLS OR USE_SECTRANSP)) _add_if("unicode" ENABLE_UNICODE) _add_if("threadsafe" HAVE_ATOMIC OR (WIN32 AND HAVE_WIN32_WINNT GREATER_EQUAL 0x600)) diff --git a/configure.ac b/configure.ac index 8f161254ba6e6f..bfbf828f00157d 100644 --- a/configure.ac +++ b/configure.ac @@ -4387,8 +4387,17 @@ fi dnl if not explicitly turned off, HTTPS-proxy comes with some TLS backends if test "x$https_proxy" != "xno"; then - if test "x$OPENSSL_ENABLED" = "x1" -o "x$GNUTLS_ENABLED" = "x1" \ - -o "x$NSS_ENABLED" = "x1"; then + if test "x$OPENSSL_ENABLED" = "x1" \ + -o "x$GNUTLS_ENABLED" = "x1" \ + -o "x$NSS_ENABLED" = "x1" \ + -o "x$SECURETRANSPORT_ENABLED" = "x1" \ + -o "x$RUSTLS_ENABLED" = "x1" \ + -o "x$BEARSSL_ENABLED" = "x1" \ + -o "x$SCHANNEL_ENABLED" = "x1" \ + -o "x$GNUTLS_ENABLED" = "x1" \ + -o "x$MBEDTLS_ENABLED" = "x1"; then + SUPPORT_FEATURES="$SUPPORT_FEATURES HTTPS-proxy" + elif test "x$WOLFSSL_ENABLED" = "x1" -a "x$WOLFSSL_FULL_BIO" = "x1"; then SUPPORT_FEATURES="$SUPPORT_FEATURES HTTPS-proxy" fi fi diff --git a/lib/cfilters.c b/lib/cfilters.c index 2d04390c10a099..bcb33da7715ef5 100644 --- a/lib/cfilters.c +++ b/lib/cfilters.c @@ -134,7 +134,6 @@ void Curl_conn_cf_discard_all(struct Curl_easy *data, struct Curl_cfilter *cfn, *cf = conn->cfilter[index]; if(cf) { - DEBUGF(infof(data, CMSGI(conn, index, "Curl_conn_cf_discard_all()"))); conn->cfilter[index] = NULL; while(cf) { cfn = cf->next; @@ -153,7 +152,6 @@ void Curl_conn_close(struct Curl_easy *data, int index) /* it is valid to call that without filters being present */ cf = data->conn->cfilter[index]; if(cf) { - DEBUGF(infof(data, DMSGI(data, index, "close()"))); cf->cft->close(cf, data); } } @@ -261,6 +259,18 @@ void Curl_conn_cf_discard(struct Curl_cfilter *cf, struct Curl_easy *data) free(cf); } +ssize_t Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data, + const void *buf, size_t len, CURLcode *err) +{ + return cf->cft->do_send(cf, data, buf, len, err); +} + +ssize_t Curl_conn_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data, + char *buf, size_t len, CURLcode *err) +{ + return cf->cft->do_recv(cf, data, buf, len, err); +} + CURLcode Curl_conn_setup(struct Curl_easy *data, struct connectdata *conn, int sockindex, @@ -384,6 +394,21 @@ bool Curl_conn_is_ip_connected(struct Curl_easy *data, int sockindex) return FALSE; } +bool Curl_conn_is_ssl(struct Curl_easy *data, int sockindex) +{ + struct Curl_cfilter *cf = data->conn? data->conn->cfilter[sockindex] : NULL; + + (void)data; + for(; cf; cf = cf->next) { + if(cf->cft->flags & CF_TYPE_SSL) + return TRUE; + if(cf->cft->flags & CF_TYPE_IP_CONNECT) + return FALSE; + } + return FALSE; +} + + bool Curl_conn_data_pending(struct Curl_easy *data, int sockindex) { struct Curl_cfilter *cf; @@ -427,7 +452,6 @@ void Curl_conn_attach_data(struct connectdata *conn, for(i = 0; i < ARRAYSIZE(conn->cfilter); ++i) { cf = conn->cfilter[i]; if(cf) { - DEBUGF(infof(data, DMSGI(data, i, "attach_data()"))); while(cf) { cf->cft->attach_data(cf, data); cf = cf->next; @@ -445,7 +469,6 @@ void Curl_conn_detach_data(struct connectdata *conn, for(i = 0; i < ARRAYSIZE(conn->cfilter); ++i) { cf = conn->cfilter[i]; if(cf) { - DEBUGF(infof(data, DMSGI(data, i, "detach_data()"))); while(cf) { cf->cft->detach_data(cf, data); cf = cf->next; diff --git a/lib/cfilters.h b/lib/cfilters.h index c339e46513fcf9..4b81b42e6be9f9 100644 --- a/lib/cfilters.h +++ b/lib/cfilters.h @@ -33,20 +33,20 @@ struct connectdata; /* Callback to destroy resources held by this filter instance. * Implementations MUST NOT chain calls to cf->next. */ -typedef void Curl_cf_destroy_this(struct Curl_cfilter *cf, - struct Curl_easy *data); +typedef void Curl_cft_destroy_this(struct Curl_cfilter *cf, + struct Curl_easy *data); /* Setup the connection for `data`, using destination `remotehost`. */ -typedef CURLcode Curl_cf_setup(struct Curl_cfilter *cf, - struct Curl_easy *data, - const struct Curl_dns_entry *remotehost); -typedef void Curl_cf_close(struct Curl_cfilter *cf, - struct Curl_easy *data); +typedef CURLcode Curl_cft_setup(struct Curl_cfilter *cf, + struct Curl_easy *data, + const struct Curl_dns_entry *remotehost); +typedef void Curl_cft_close(struct Curl_cfilter *cf, + struct Curl_easy *data); -typedef CURLcode Curl_cf_connect(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool blocking, bool *done); +typedef CURLcode Curl_cft_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool blocking, bool *done); /* Return the hostname and port the connection goes to. * This may change with the connection state of filters when tunneling @@ -59,40 +59,40 @@ typedef CURLcode Curl_cf_connect(struct Curl_cfilter *cf, * this is owned by the connection. * @param pport on return, contains the port number */ -typedef void Curl_cf_get_host(struct Curl_cfilter *cf, - struct Curl_easy *data, - const char **phost, - const char **pdisplay_host, - int *pport); +typedef void Curl_cft_get_host(struct Curl_cfilter *cf, + struct Curl_easy *data, + const char **phost, + const char **pdisplay_host, + int *pport); /* Filters may return sockets and fdset flags they are waiting for. * The passes array has room for up to MAX_SOCKSPEREASYHANDLE sockets. * @return read/write fdset for index in socks * or GETSOCK_BLANK when nothing to wait on */ -typedef int Curl_cf_get_select_socks(struct Curl_cfilter *cf, - struct Curl_easy *data, - curl_socket_t *socks); - -typedef bool Curl_cf_data_pending(struct Curl_cfilter *cf, - const struct Curl_easy *data); - -typedef ssize_t Curl_cf_send(struct Curl_cfilter *cf, - struct Curl_easy *data, /* transfer */ - const void *buf, /* data to write */ - size_t len, /* max amount to write */ - CURLcode *err); /* error to return */ - -typedef ssize_t Curl_cf_recv(struct Curl_cfilter *cf, - struct Curl_easy *data, /* transfer */ - char *buf, /* store data here */ - size_t len, /* max amount to read */ - CURLcode *err); /* error to return */ - -typedef void Curl_cf_attach_data(struct Curl_cfilter *cf, - struct Curl_easy *data); -typedef void Curl_cf_detach_data(struct Curl_cfilter *cf, - struct Curl_easy *data); +typedef int Curl_cft_get_select_socks(struct Curl_cfilter *cf, + struct Curl_easy *data, + curl_socket_t *socks); + +typedef bool Curl_cft_data_pending(struct Curl_cfilter *cf, + const struct Curl_easy *data); + +typedef ssize_t Curl_cft_send(struct Curl_cfilter *cf, + struct Curl_easy *data, /* transfer */ + const void *buf, /* data to write */ + size_t len, /* amount to write */ + CURLcode *err); /* error to return */ + +typedef ssize_t Curl_cft_recv(struct Curl_cfilter *cf, + struct Curl_easy *data, /* transfer */ + char *buf, /* store data here */ + size_t len, /* amount to read */ + CURLcode *err); /* error to return */ + +typedef void Curl_cft_attach_data(struct Curl_cfilter *cf, + struct Curl_easy *data); +typedef void Curl_cft_detach_data(struct Curl_cfilter *cf, + struct Curl_easy *data); /** * The easy handle `data` is being detached (no longer served) @@ -108,19 +108,19 @@ void Curl_conn_detach(struct connectdata *conn, struct Curl_easy *data); /* A connection filter type, e.g. specific implementation. */ struct Curl_cftype { - const char *name; /* name of the filter type */ - long flags; /* flags of filter type */ - Curl_cf_destroy_this *destroy; /* destroy resources of this cf */ - Curl_cf_setup *setup; /* setup for a connection */ - Curl_cf_connect *connect; /* establish connection */ - Curl_cf_close *close; /* close conn */ - Curl_cf_get_host *get_host; /* host filter talks to */ - Curl_cf_get_select_socks *get_select_socks;/* sockets to select on */ - Curl_cf_data_pending *has_data_pending;/* conn has data pending */ - Curl_cf_send *do_send; /* send data */ - Curl_cf_recv *do_recv; /* receive data */ - Curl_cf_attach_data *attach_data; /* data is being handled here */ - Curl_cf_detach_data *detach_data; /* data is no longer handled here */ + const char *name; /* name of the filter type */ + long flags; /* flags of filter type */ + Curl_cft_destroy_this *destroy; /* destroy resources of this cf */ + Curl_cft_setup *setup; /* setup for a connection */ + Curl_cft_connect *connect; /* establish connection */ + Curl_cft_close *close; /* close conn */ + Curl_cft_get_host *get_host; /* host filter talks to */ + Curl_cft_get_select_socks *get_select_socks;/* sockets to select on */ + Curl_cft_data_pending *has_data_pending;/* conn has data pending */ + Curl_cft_send *do_send; /* send data */ + Curl_cft_recv *do_recv; /* receive data */ + Curl_cft_attach_data *attach_data; /* data is being handled here */ + Curl_cft_detach_data *detach_data; /* data is no longer handled here */ }; /* A connection filter instance, e.g. registered at a connection */ @@ -198,6 +198,12 @@ void Curl_conn_cf_discard_all(struct Curl_easy *data, */ void Curl_conn_cf_discard(struct Curl_cfilter *cf, struct Curl_easy *data); + +ssize_t Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data, + const void *buf, size_t len, CURLcode *err); +ssize_t Curl_conn_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data, + char *buf, size_t len, CURLcode *err); + #define CURL_CF_SSL_DEFAULT -1 #define CURL_CF_SSL_DISABLE 0 #define CURL_CF_SSL_ENABLE 1 @@ -237,6 +243,13 @@ bool Curl_conn_is_connected(struct connectdata *conn, int sockindex); */ bool Curl_conn_is_ip_connected(struct Curl_easy *data, int sockindex); +/** + * Determine if the connection is using SSL to the remote host + * (or will be once connected). This will return FALSE, if SSL + * is only used in proxying and not for the tunnel itself. + */ +bool Curl_conn_is_ssl(struct Curl_easy *data, int sockindex); + /** * Close the filter chain at `sockindex` for connection `data->conn`. * Filters remain in place and may be connected again afterwards. diff --git a/lib/connect.c b/lib/connect.c index 9ec664ef5ff365..6cb9459718ebf1 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -1690,8 +1690,6 @@ static CURLcode socket_cf_connect(struct Curl_cfilter *cf, result = Curl_connecthost(data, conn, ctx->remotehost); if(!result) ctx->state = SCFST_WAITING; - DEBUGF(infof(data, CFMSG(cf, "connect(INIT) -> %d, done=%d"), - result, *done)); break; case SCFST_WAITING: result = is_connected(data, conn, sockindex, done); @@ -1704,13 +1702,9 @@ static CURLcode socket_cf_connect(struct Curl_cfilter *cf, ctx->state = SCFST_DONE; cf->connected = TRUE; } - DEBUGF(infof(data, CFMSG(cf, "connect(WAIT) -> %d, done=%d"), - result, *done)); break; case SCFST_DONE: *done = TRUE; - DEBUGF(infof(data, CFMSG(cf, "connect(DONE) -> %d, done=%d"), - result, *done)); break; } return result; @@ -1783,8 +1777,6 @@ static ssize_t socket_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data, { ssize_t nwritten; nwritten = Curl_send_plain(data, cf->sockindex, buf, len, err); - DEBUGF(infof(data, CFMSG(cf, "send(len=%ld) -> %ld, err=%d"), - len, nwritten, *err)); return nwritten; } @@ -1793,7 +1785,6 @@ static ssize_t socket_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data, { ssize_t nread; nread = Curl_recv_plain(data, cf->sockindex, buf, len, err); - DEBUGF(infof(data, CFMSG(cf, "recv() -> %ld"), nread)); return nread; } @@ -1802,7 +1793,6 @@ static void socket_cf_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) struct socket_cf_ctx *state = cf->ctx; (void)data; - DEBUGF(infof(data, CFMSG(cf, "destroy()"))); if(cf->connected) { socket_cf_close(cf, data); } diff --git a/lib/ftp.c b/lib/ftp.c index c07bafe17752bc..8f0ac2e694bd4a 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -2742,7 +2742,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data, if((ftpcode == 234) || (ftpcode == 334)) { /* this was BLOCKING, keep it so for now */ bool done; - if(!Curl_ssl_conn_is_ssl(data, FIRSTSOCKET)) { + if(!Curl_conn_is_ssl(data, FIRSTSOCKET)) { result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET); if(result) { /* we failed and bail out */ diff --git a/lib/http_proxy.c b/lib/http_proxy.c index 0519c8ea871da4..e30730acabdc87 100644 --- a/lib/http_proxy.c +++ b/lib/http_proxy.c @@ -172,8 +172,6 @@ static void tunnel_go_state(struct Curl_cfilter *cf, { if(ts->tunnel_state == new_state) return; - DEBUGF(infof(data, CFMSG(cf, "tunnel %p go_state %d -> %d"), - ts, ts->tunnel_state, new_state)); /* leaving this one */ switch(ts->tunnel_state) { case TUNNEL_CONNECT: @@ -488,7 +486,6 @@ static CURLcode recv_CONNECT_resp(struct Curl_easy *data, #define SELECT_OK 0 #define SELECT_ERROR 1 - DEBUGF(infof(data, "CONNECT: recv response, keepon=%d", ts->keepon)); error = SELECT_OK; *done = FALSE; @@ -642,7 +639,6 @@ static CURLcode recv_CONNECT_resp(struct Curl_easy *data, } } else { - DEBUGF(infof(data, "CONNECT: no end of response headers")); ts->keepon = KEEPON_DONE; } @@ -1085,8 +1081,6 @@ static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf, cf->ctx = ts; } - DEBUGF(infof(data, CFMSG(cf, "connect(%s:%d, state=%d)"), - ts->hostname, ts->remote_port, ts->tunnel_state)); result = CONNECT(cf, data, ts); if(result) goto out; @@ -1098,8 +1092,6 @@ static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf, cf->connected = TRUE; tunnel_free(cf, data); } - DEBUGF(infof(data, CFMSG(cf, "connect(block=%d) -> %d, done=%d"), - blocking, result, *done)); return result; } diff --git a/lib/imap.c b/lib/imap.c index a462ff04283004..95689c9d1580ec 100644 --- a/lib/imap.c +++ b/lib/imap.c @@ -476,7 +476,7 @@ static CURLcode imap_perform_upgrade_tls(struct Curl_easy *data, struct imap_conn *imapc = &conn->proto.imapc; CURLcode result; - if(!Curl_ssl_conn_is_ssl(data, FIRSTSOCKET)) { + if(!Curl_conn_is_ssl(data, FIRSTSOCKET)) { result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET); if(result) goto out; diff --git a/lib/pop3.c b/lib/pop3.c index e94d7e5cb66fa6..c5ad07aa0a9315 100644 --- a/lib/pop3.c +++ b/lib/pop3.c @@ -371,7 +371,7 @@ static CURLcode pop3_perform_upgrade_tls(struct Curl_easy *data, struct pop3_conn *pop3c = &conn->proto.pop3c; CURLcode result; - if(!Curl_ssl_conn_is_ssl(data, FIRSTSOCKET)) { + if(!Curl_conn_is_ssl(data, FIRSTSOCKET)) { result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET); if(result) goto out; diff --git a/lib/smtp.c b/lib/smtp.c index 085faf9e35cc77..1c29fab37d51f5 100644 --- a/lib/smtp.c +++ b/lib/smtp.c @@ -398,7 +398,7 @@ static CURLcode smtp_perform_upgrade_tls(struct Curl_easy *data) struct smtp_conn *smtpc = &conn->proto.smtpc; CURLcode result; - if(!Curl_ssl_conn_is_ssl(data, FIRSTSOCKET)) { + if(!Curl_conn_is_ssl(data, FIRSTSOCKET)) { result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET); if(result) goto out; diff --git a/lib/vtls/bearssl.c b/lib/vtls/bearssl.c index 3ec8c0acd6b820..d9c0ce0eed11d7 100644 --- a/lib/vtls/bearssl.c +++ b/lib/vtls/bearssl.c @@ -764,11 +764,11 @@ static CURLcode bearssl_run_until(struct Curl_cfilter *cf, { struct ssl_connect_data *connssl = cf->ctx; struct ssl_backend_data *backend = connssl->backend; - curl_socket_t sockfd = cf->conn->sock[cf->sockindex]; unsigned state; unsigned char *buf; size_t len; ssize_t ret; + CURLcode result; int err; DEBUGASSERT(backend); @@ -807,31 +807,21 @@ static CURLcode bearssl_run_until(struct Curl_cfilter *cf, return CURLE_OK; if(state & BR_SSL_SENDREC) { buf = br_ssl_engine_sendrec_buf(&backend->ctx.eng, &len); - ret = swrite(sockfd, buf, len); - if(ret == -1) { - if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) { - if(connssl->state != ssl_connection_complete) - connssl->connecting_state = ssl_connect_2_writing; - return CURLE_AGAIN; - } - return CURLE_WRITE_ERROR; + ret = Curl_conn_cf_send(cf->next, data, (char *)buf, len, &result); + if(ret <= 0) { + return result; } br_ssl_engine_sendrec_ack(&backend->ctx.eng, ret); } else if(state & BR_SSL_RECVREC) { buf = br_ssl_engine_recvrec_buf(&backend->ctx.eng, &len); - ret = sread(sockfd, buf, len); + ret = Curl_conn_cf_recv(cf->next, data, (char *)buf, len, &result); if(ret == 0) { failf(data, "SSL: EOF without close notify"); return CURLE_READ_ERROR; } - if(ret == -1) { - if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) { - if(connssl->state != ssl_connection_complete) - connssl->connecting_state = ssl_connect_2_reading; - return CURLE_AGAIN; - } - return CURLE_READ_ERROR; + if(ret <= 0) { + return result; } br_ssl_engine_recvrec_ack(&backend->ctx.eng, ret); } @@ -1183,7 +1173,7 @@ static CURLcode bearssl_sha256sum(const unsigned char *input, const struct Curl_ssl Curl_ssl_bearssl = { { CURLSSLBACKEND_BEARSSL, "bearssl" }, /* info */ - SSLSUPP_CAINFO_BLOB | SSLSUPP_SSL_CTX, + SSLSUPP_CAINFO_BLOB | SSLSUPP_SSL_CTX | SSLSUPP_HTTPS_PROXY, sizeof(struct ssl_backend_data), Curl_none_init, /* init */ diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c index 787d04a6bee5db..b55b707d1165e9 100644 --- a/lib/vtls/gtls.c +++ b/lib/vtls/gtls.c @@ -92,28 +92,40 @@ struct ssl_backend_data { #endif }; -static ssize_t gtls_push(void *s, const void *buf, size_t len) +static ssize_t gtls_push(void *s, const void *buf, size_t blen) { - curl_socket_t sock = *(curl_socket_t *)s; - ssize_t ret = swrite(sock, buf, len); - return ret; -} + struct Curl_cfilter *cf = s; + struct ssl_connect_data *connssl = cf->ctx; + struct Curl_easy *data = connssl->call_data; + ssize_t nwritten; + CURLcode result; -static ssize_t gtls_pull(void *s, void *buf, size_t len) -{ - curl_socket_t sock = *(curl_socket_t *)s; - ssize_t ret = sread(sock, buf, len); - return ret; + DEBUGASSERT(data); + nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, &result); + if(nwritten < 0) { + gnutls_transport_set_errno(connssl->backend->session, + (CURLE_AGAIN == result)? EAGAIN : EINVAL); + nwritten = -1; + } + return nwritten; } -static ssize_t gtls_push_ssl(void *s, const void *buf, size_t len) +static ssize_t gtls_pull(void *s, void *buf, size_t blen) { - return gnutls_record_send((gnutls_session_t) s, buf, len); -} + struct Curl_cfilter *cf = s; + struct ssl_connect_data *connssl = cf->ctx; + struct Curl_easy *data = connssl->call_data; + ssize_t nread; + CURLcode result; -static ssize_t gtls_pull_ssl(void *s, void *buf, size_t len) -{ - return gnutls_record_recv((gnutls_session_t) s, buf, len); + DEBUGASSERT(data); + nread = Curl_conn_cf_recv(cf->next, data, buf, blen, &result); + if(nread < 0) { + gnutls_transport_set_errno(connssl->backend->session, + (CURLE_AGAIN == result)? EAGAIN : EINVAL); + nread = -1; + } + return nread; } /* gtls_init() @@ -419,9 +431,6 @@ gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) const char *hostname = connssl->hostname; long * const certverifyresult = &ssl_config->certverifyresult; const char *tls13support; - struct Curl_cfilter *cf_ssl_next = Curl_ssl_cf_get_ssl(cf->next); - struct ssl_connect_data *connssl_next = cf_ssl_next? - cf_ssl_next->ctx : NULL; CURLcode result; DEBUGASSERT(backend); @@ -720,18 +729,10 @@ gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) } } - if(connssl_next) { - DEBUGASSERT(connssl_next->backend); - transport_ptr = connssl_next->backend; - gnutls_transport_push = gtls_push_ssl; - gnutls_transport_pull = gtls_pull_ssl; - } - else { - /* file descriptor for the socket */ - transport_ptr = &cf->conn->sock[cf->sockindex]; - gnutls_transport_push = gtls_push; - gnutls_transport_pull = gtls_pull; - } + /* push/pull through filter chain */ + transport_ptr = cf; + gnutls_transport_push = gtls_push; + gnutls_transport_pull = gtls_pull; /* set the connection handle */ gnutls_transport_set_ptr(session, transport_ptr); @@ -1352,20 +1353,25 @@ gtls_connect_common(struct Curl_cfilter *cf, bool nonblocking, bool *done) { - int rc; struct ssl_connect_data *connssl = cf->ctx; + int rc; + CURLcode result = CURLE_OK; /* Initiate the connection, if not already done */ if(ssl_connect_1 == connssl->connecting_state) { rc = gtls_connect_step1(cf, data); - if(rc) - return rc; + if(rc) { + result = rc; + goto out; + } } rc = handshake(cf, data, TRUE, nonblocking); - if(rc) + if(rc) { /* handshake() sets its own error message with failf() */ - return rc; + result = rc; + goto out; + } /* Finish connecting once the handshake is done */ if(ssl_connect_1 == connssl->connecting_state) { @@ -1374,13 +1380,16 @@ gtls_connect_common(struct Curl_cfilter *cf, DEBUGASSERT(backend); session = backend->session; rc = Curl_gtls_verifyserver(cf, data, session); - if(rc) - return rc; + if(rc) { + result = rc; + goto out; + } } +out: *done = ssl_connect_1 == connssl->connecting_state; - return CURLE_OK; + return result; } static CURLcode gtls_connect_nonblocking(struct Curl_cfilter *cf, @@ -1570,7 +1579,8 @@ static ssize_t gtls_recv(struct Curl_cfilter *cf, ret = gnutls_record_recv(backend->session, buf, buffersize); if((ret == GNUTLS_E_AGAIN) || (ret == GNUTLS_E_INTERRUPTED)) { *curlcode = CURLE_AGAIN; - return -1; + ret = -1; + goto out; } if(ret == GNUTLS_E_REHANDSHAKE) { @@ -1582,7 +1592,8 @@ static ssize_t gtls_recv(struct Curl_cfilter *cf, *curlcode = result; else *curlcode = CURLE_AGAIN; /* then return as if this was a wouldblock */ - return -1; + ret = -1; + goto out; } if(ret < 0) { @@ -1590,9 +1601,11 @@ static ssize_t gtls_recv(struct Curl_cfilter *cf, (int)ret, gnutls_strerror((int)ret)); *curlcode = CURLE_RECV_ERROR; - return -1; + ret = -1; + goto out; } +out: return ret; } diff --git a/lib/vtls/mbedtls.c b/lib/vtls/mbedtls.c index f919a842e70697..e131ac06c2c53c 100644 --- a/lib/vtls/mbedtls.c +++ b/lib/vtls/mbedtls.c @@ -156,6 +156,46 @@ static void mbed_debug(void *context, int level, const char *f_name, #else #endif +static int bio_cf_write(void *bio, const unsigned char *buf, size_t blen) +{ + struct Curl_cfilter *cf = bio; + struct ssl_connect_data *connssl = cf->ctx; + struct Curl_easy *data = connssl->call_data; + ssize_t nwritten; + CURLcode result; + + DEBUGASSERT(data); + nwritten = Curl_conn_cf_send(cf->next, data, (char *)buf, blen, &result); + /* DEBUGF(infof(data, CFMSG(cf, "bio_cf_out_write(len=%d) -> %d, err=%d"), + blen, (int)nwritten, result)); */ + if(nwritten < 0 && CURLE_AGAIN == result) { + nwritten = MBEDTLS_ERR_SSL_WANT_WRITE; + } + return (int)nwritten; +} + +static int bio_cf_read(void *bio, unsigned char *buf, size_t blen) +{ + struct Curl_cfilter *cf = bio; + struct ssl_connect_data *connssl = cf->ctx; + struct Curl_easy *data = connssl->call_data; + ssize_t nread; + CURLcode result; + + DEBUGASSERT(data); + /* OpenSSL catches this case, so should we. */ + if(!buf) + return 0; + + nread = Curl_conn_cf_recv(cf->next, data, (char *)buf, blen, &result); + /* DEBUGF(infof(data, CFMSG(cf, "bio_cf_in_read(len=%d) -> %d, err=%d"), + blen, (int)nread, result)); */ + if(nread < 0 && CURLE_AGAIN == result) { + nread = MBEDTLS_ERR_SSL_WANT_READ; + } + return (int)nread; +} + /* * profile */ @@ -551,9 +591,7 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) mbedtls_ssl_conf_rng(&backend->config, mbedtls_ctr_drbg_random, &backend->ctr_drbg); - mbedtls_ssl_set_bio(&backend->ssl, &cf->conn->sock[cf->sockindex], - mbedtls_net_send, - mbedtls_net_recv, + mbedtls_ssl_set_bio(&backend->ssl, cf, bio_cf_write, bio_cf_read, NULL /* rev_timeout() */); mbedtls_ssl_conf_ciphersuites(&backend->config, @@ -902,7 +940,6 @@ static ssize_t mbed_send(struct Curl_cfilter *cf, struct Curl_easy *data, (void)data; DEBUGASSERT(backend); - ret = mbedtls_ssl_write(&backend->ssl, (unsigned char *)mem, len); if(ret < 0) { @@ -924,8 +961,8 @@ static void mbedtls_close(struct Curl_cfilter *cf, struct Curl_easy *data) struct ssl_connect_data *connssl = cf->ctx; struct ssl_backend_data *backend = connssl->backend; char buf[32]; - (void) data; + (void)data; DEBUGASSERT(backend); /* Maybe the server has already sent a close notify alert. @@ -1229,7 +1266,8 @@ const struct Curl_ssl Curl_ssl_mbedtls = { SSLSUPP_CA_PATH | SSLSUPP_CAINFO_BLOB | SSLSUPP_PINNEDPUBKEY | - SSLSUPP_SSL_CTX, + SSLSUPP_SSL_CTX | + SSLSUPP_HTTPS_PROXY, sizeof(struct ssl_backend_data), diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index a6bd294dec4986..8d93d41e27ebf3 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -635,6 +635,166 @@ CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl) #ifdef USE_OPENSSL +#if USE_PRE_1_1_API +#if !defined(LIBRESSL_VERSION_NUMBER) || LIBRESSL_VERSION_NUMBER < 0x2070000fL +#define BIO_set_init(x,v) ((x)->init=(v)) +#define BIO_get_data(x) ((x)->ptr) +#define BIO_set_data(x,v) ((x)->ptr=(v)) +#endif +#define BIO_get_shutdown(x) ((x)->shutdown) +#define BIO_set_shutdown(x,v) ((x)->shutdown=(v)) +#endif /* USE_PRE_1_1_API */ + +static int bio_cf_create(BIO *bio) +{ + BIO_set_shutdown(bio, 1); + BIO_set_init(bio, 1); +#if USE_PRE_1_1_API + bio->num = -1; +#endif + BIO_set_data(bio, NULL); + return 1; +} + +static int bio_cf_destroy(BIO *bio) +{ + if(!bio) + return 0; + return 1; +} + +static long bio_cf_ctrl(BIO *bio, int cmd, long num, void *ptr) +{ + struct Curl_cfilter *cf = BIO_get_data(bio); + long ret = 1; + + (void)cf; + (void)ptr; + switch(cmd) { + case BIO_CTRL_GET_CLOSE: + ret = (long)BIO_get_shutdown(bio); + break; + case BIO_CTRL_SET_CLOSE: + BIO_set_shutdown(bio, (int)num); + break; + case BIO_CTRL_FLUSH: + /* we do no delayed writes, but if we ever would, this + * needs to trigger it. */ + ret = 1; + break; + case BIO_CTRL_DUP: + ret = 1; + break; +#ifdef BIO_CTRL_EOF + case BIO_CTRL_EOF: + /* EOF has been reached on input? */ + return (!cf->next || !cf->next->connected); +#endif + default: + ret = 0; + break; + } + return ret; +} + +static int bio_cf_out_write(BIO *bio, const char *buf, int blen) +{ + struct Curl_cfilter *cf = BIO_get_data(bio); + struct ssl_connect_data *connssl = cf->ctx; + struct Curl_easy *data = connssl->call_data; + ssize_t nwritten; + CURLcode result = CURLE_SEND_ERROR; + + DEBUGASSERT(data); + nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, &result); + /* DEBUGF(infof(data, CFMSG(cf, "bio_cf_out_write(len=%d) -> %d, err=%d"), + blen, (int)nwritten, result)); */ + BIO_clear_retry_flags(bio); + if(nwritten < 0) { + if(CURLE_AGAIN == result) { + BIO_set_retry_write(bio); + nwritten = 0; + } + else { + nwritten = -1; + } + } + return (int)nwritten; +} + +static int bio_cf_in_read(BIO *bio, char *buf, int blen) +{ + struct Curl_cfilter *cf = BIO_get_data(bio); + struct ssl_connect_data *connssl = cf->ctx; + struct Curl_easy *data = connssl->call_data; + ssize_t nread; + CURLcode result = CURLE_RECV_ERROR; + + DEBUGASSERT(data); + /* OpenSSL catches this case, so should we. */ + if(!buf) + return 0; + + nread = Curl_conn_cf_recv(cf->next, data, buf, blen, &result); + /* DEBUGF(infof(data, CFMSG(cf, "bio_cf_in_read(len=%d) -> %d, err=%d"), + blen, (int)nread, result)); */ + BIO_clear_retry_flags(bio); + if(nread < 0) { + if(CURLE_AGAIN == result) { + BIO_set_retry_read(bio); + nread = 0; + } + else { + nread = -1; + } + } + return (int)nread; +} + +static BIO_METHOD *bio_cf_method = NULL; + +#if USE_PRE_1_1_API + +static BIO_METHOD bio_cf_meth_1_0 = { + BIO_TYPE_MEM, + "OpenSSL CF BIO", + bio_cf_out_write, + bio_cf_in_read, + NULL, /* puts is never called */ + NULL, /* gets is never called */ + bio_cf_ctrl, + bio_cf_create, + bio_cf_destroy, + NULL +}; + +static void bio_cf_init_methods(void) +{ + bio_cf_method = &bio_cf_meth_1_0; +} + +#define bio_cf_free_methods() Curl_nop_stmt + +#else + +static void bio_cf_init_methods(void) +{ + bio_cf_method = BIO_meth_new(BIO_TYPE_MEM, "OpenSSL CF BIO"); + BIO_meth_set_write(bio_cf_method, &bio_cf_out_write); + BIO_meth_set_read(bio_cf_method, &bio_cf_in_read); + BIO_meth_set_ctrl(bio_cf_method, &bio_cf_ctrl); + BIO_meth_set_create(bio_cf_method, &bio_cf_create); + BIO_meth_set_destroy(bio_cf_method, &bio_cf_destroy); +} + +static void bio_cf_free_methods(void) +{ + BIO_meth_free(bio_cf_method); +} + +#endif + + static bool ossl_attach_data(struct Curl_cfilter *cf, struct Curl_easy *data); @@ -1602,6 +1762,7 @@ static int ossl_init(void) OpenSSL_add_all_algorithms(); #endif + bio_cf_init_methods(); Curl_tls_keylog_open(); /* Initialize the extra data indexes */ @@ -1647,6 +1808,7 @@ static void ossl_cleanup(void) #endif Curl_tls_keylog_close(); + bio_cf_free_methods(); } /* @@ -1803,21 +1965,17 @@ static void ossl_close(struct Curl_cfilter *cf, struct Curl_easy *data) DEBUGASSERT(backend); if(backend->handle) { - char buf[32]; set_logger(connssl, data); - /* - * The conn->sock[0] socket is passed to openssl with SSL_set_fd(). Make - * sure the socket is not closed before calling OpenSSL functions that - * will use it. - */ - DEBUGASSERT(cf->conn->sock[FIRSTSOCKET] != CURL_SOCKET_BAD); - /* Maybe the server has already sent a close notify alert. - Read it to avoid an RST on the TCP connection. */ - (void)SSL_read(backend->handle, buf, (int)sizeof(buf)); + if(cf->next && cf->next->connected) { + char buf[32]; + /* Maybe the server has already sent a close notify alert. + Read it to avoid an RST on the TCP connection. */ + (void)SSL_read(backend->handle, buf, (int)sizeof(buf)); - (void)SSL_shutdown(backend->handle); - SSL_set_connect_state(backend->handle); + (void)SSL_shutdown(backend->handle); + SSL_set_connect_state(backend->handle); + } SSL_free(backend->handle); backend->handle = NULL; @@ -2032,6 +2190,7 @@ CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, int port; size_t hostlen; + (void)conn; Curl_conn_get_host(data, FIRSTSOCKET, &hostname, &dispname, &port); hostlen = strlen(hostname); @@ -3311,17 +3470,12 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, CURLcode result = CURLE_OK; char *ciphers; SSL_METHOD_QUAL SSL_METHOD *req_method = NULL; - curl_socket_t sockfd = cf->conn->sock[cf->sockindex]; struct ssl_connect_data *connssl = cf->ctx; ctx_option_t ctx_options = 0; void *ssl_sessionid = NULL; struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); -#ifndef CURL_DISABLE_PROXY - struct Curl_cfilter *cf_ssl_next = Curl_ssl_cf_get_ssl(cf->next); - struct ssl_connect_data *connssl_next = cf_ssl_next? - cf_ssl_next->ctx : NULL; -#endif + BIO *bio; #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME bool sni; @@ -3382,7 +3536,12 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, return CURLE_SSL_CONNECT_ERROR; } - DEBUGASSERT(!backend->ctx); + if(backend->ctx) { + /* This happens when an error was encountered before in this + * step and we are called to do it again. Get rid of any leftover + * from the previous call. */ + ossl_close(cf, data); + } backend->ctx = SSL_CTX_new(req_method); if(!backend->ctx) { @@ -3707,24 +3866,12 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, Curl_ssl_sessionid_unlock(data); } -#ifndef CURL_DISABLE_PROXY - if(connssl_next) { - BIO *const bio = BIO_new(BIO_f_ssl()); - DEBUGASSERT(connssl_next->backend); - DEBUGASSERT(ssl_connection_complete == connssl_next->state); - DEBUGASSERT(connssl_next->backend->handle != NULL); - DEBUGASSERT(bio != NULL); - BIO_set_ssl(bio, connssl_next->backend->handle, FALSE); - SSL_set_bio(backend->handle, bio, bio); - } - else -#endif - if(!SSL_set_fd(backend->handle, (int)sockfd)) { - /* pass the raw socket into the SSL layers */ - failf(data, "SSL: SSL_set_fd failed: %s", - ossl_strerror(ERR_get_error(), error_buffer, sizeof(error_buffer))); - return CURLE_SSL_CONNECT_ERROR; - } + bio = BIO_new(bio_cf_method); + if(!bio) + return CURLE_OUT_OF_MEMORY; + + BIO_set_data(bio, cf); + SSL_set_bio(backend->handle, bio, bio); connssl->connecting_state = ssl_connect_2; @@ -4205,7 +4352,7 @@ static CURLcode ossl_connect_common(struct Curl_cfilter *cf, bool nonblocking, bool *done) { - CURLcode result; + CURLcode result = CURLE_OK; struct ssl_connect_data *connssl = cf->ctx; curl_socket_t sockfd = cf->conn->sock[cf->sockindex]; int what; @@ -4228,7 +4375,7 @@ static CURLcode ossl_connect_common(struct Curl_cfilter *cf, result = ossl_connect_step1(cf, data); if(result) - return result; + goto out; } while(ssl_connect_2 == connssl->connecting_state || @@ -4241,7 +4388,8 @@ static CURLcode ossl_connect_common(struct Curl_cfilter *cf, if(timeout_ms < 0) { /* no need to continue if time already is up */ failf(data, "SSL connection timeout"); - return CURLE_OPERATION_TIMEDOUT; + result = CURLE_OPERATION_TIMEDOUT; + goto out; } /* if ssl is expecting something, check if it's available. */ @@ -4258,16 +4406,19 @@ static CURLcode ossl_connect_common(struct Curl_cfilter *cf, if(what < 0) { /* fatal error */ failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); - return CURLE_SSL_CONNECT_ERROR; + result = CURLE_SSL_CONNECT_ERROR; + goto out; } if(0 == what) { if(nonblocking) { *done = FALSE; - return CURLE_OK; + result = CURLE_OK; + goto out; } /* timeout */ failf(data, "SSL connection timeout"); - return CURLE_OPERATION_TIMEDOUT; + result = CURLE_OPERATION_TIMEDOUT; + goto out; } /* socket is readable or writable */ } @@ -4283,14 +4434,14 @@ static CURLcode ossl_connect_common(struct Curl_cfilter *cf, (ssl_connect_2 == connssl->connecting_state || ssl_connect_2_reading == connssl->connecting_state || ssl_connect_2_writing == connssl->connecting_state))) - return result; + goto out; } /* repeat step2 until all transactions are done. */ if(ssl_connect_3 == connssl->connecting_state) { result = ossl_connect_step3(cf, data); if(result) - return result; + goto out; } if(ssl_connect_done == connssl->connecting_state) { @@ -4303,7 +4454,8 @@ static CURLcode ossl_connect_common(struct Curl_cfilter *cf, /* Reset our connect state machine */ connssl->connecting_state = ssl_connect_1; - return CURLE_OK; +out: + return result; } static CURLcode ossl_connect_nonblocking(struct Curl_cfilter *cf, @@ -4377,7 +4529,8 @@ static ssize_t ossl_send(struct Curl_cfilter *cf, should be called again later. This is basically an EWOULDBLOCK equivalent. */ *curlcode = CURLE_AGAIN; - return -1; + rc = -1; + goto out; case SSL_ERROR_SYSCALL: { int sockerr = SOCKERRNO; @@ -4393,7 +4546,8 @@ static ssize_t ossl_send(struct Curl_cfilter *cf, failf(data, OSSL_PACKAGE " SSL_write: %s, errno %d", error_buffer, sockerr); *curlcode = CURLE_SEND_ERROR; - return -1; + rc = -1; + goto out; } case SSL_ERROR_SSL: { /* A failure in the SSL library occurred, usually a protocol error. @@ -4415,17 +4569,21 @@ static ssize_t ossl_send(struct Curl_cfilter *cf, failf(data, "SSL_write() error: %s", ossl_strerror(sslerror, error_buffer, sizeof(error_buffer))); *curlcode = CURLE_SEND_ERROR; - return -1; + rc = -1; + goto out; } default: /* a true error */ failf(data, OSSL_PACKAGE " SSL_write: %s, errno %d", SSL_ERROR_to_str(err), SOCKERRNO); *curlcode = CURLE_SEND_ERROR; - return -1; + rc = -1; + goto out; } } *curlcode = CURLE_OK; + +out: return (ssize_t)rc; /* number of bytes */ } @@ -4451,6 +4609,7 @@ static ssize_t ossl_recv(struct Curl_cfilter *cf, buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize; set_logger(connssl, data); nread = (ssize_t)SSL_read(backend->handle, buf, buffsize); + if(nread <= 0) { /* failed SSL_read */ int err = SSL_get_error(backend->handle, (int)nread); @@ -4469,7 +4628,8 @@ static ssize_t ossl_recv(struct Curl_cfilter *cf, case SSL_ERROR_WANT_WRITE: /* there's data pending, re-invoke SSL_read() */ *curlcode = CURLE_AGAIN; - return -1; + nread = -1; + goto out; default: /* openssl/ssl.h for SSL_ERROR_SYSCALL says "look at error stack/return value/errno" */ @@ -4490,7 +4650,8 @@ static ssize_t ossl_recv(struct Curl_cfilter *cf, failf(data, OSSL_PACKAGE " SSL_read: %s, errno %d", error_buffer, sockerr); *curlcode = CURLE_RECV_ERROR; - return -1; + nread = -1; + goto out; } /* For debug builds be a little stricter and error on any SSL_ERROR_SYSCALL. For example a server may have closed the connection @@ -4513,11 +4674,14 @@ static ssize_t ossl_recv(struct Curl_cfilter *cf, " (Fatal because this is a curl debug build)", error_buffer, sockerr); *curlcode = CURLE_RECV_ERROR; - return -1; + nread = -1; + goto out; } #endif } } + +out: return nread; } diff --git a/lib/vtls/rustls.c b/lib/vtls/rustls.c index 36cc08207a2dd3..27f4ec8d8c2ad2 100644 --- a/lib/vtls/rustls.c +++ b/lib/vtls/rustls.c @@ -81,26 +81,47 @@ cr_connect(struct Curl_cfilter *cf UNUSED_PARAM, return CURLE_SSL_CONNECT_ERROR; } +struct io_ctx { + struct Curl_cfilter *cf; + struct Curl_easy *data; +}; + static int read_cb(void *userdata, uint8_t *buf, uintptr_t len, uintptr_t *out_n) { - ssize_t n = sread(*(int *)userdata, buf, len); - if(n < 0) { - return SOCKERRNO; + struct io_ctx *io_ctx = userdata; + CURLcode result; + int ret = 0; + ssize_t nread = Curl_conn_cf_recv(io_ctx->cf->next, io_ctx->data, + (char *)buf, len, &result); + if(nread < 0) { + nread = 0; + if(CURLE_AGAIN == result) + ret = EAGAIN; + else + ret = EINVAL; } - *out_n = n; - return 0; + *out_n = (int)nread; + return ret; } static int write_cb(void *userdata, const uint8_t *buf, uintptr_t len, uintptr_t *out_n) { - ssize_t n = swrite(*(int *)userdata, buf, len); - if(n < 0) { - return SOCKERRNO; + struct io_ctx *io_ctx = userdata; + CURLcode result; + int ret = 0; + ssize_t nwritten = Curl_conn_cf_send(io_ctx->cf->next, io_ctx->data, + (const char *)buf, len, &result); + if(nwritten < 0) { + nwritten = 0; + if(CURLE_AGAIN == result) + ret = EAGAIN; + else + ret = EINVAL; } - *out_n = n; - return 0; + *out_n = (int)nwritten; + return ret; } /* @@ -122,6 +143,7 @@ cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data, struct ssl_connect_data *const connssl = cf->ctx; struct ssl_backend_data *const backend = connssl->backend; struct rustls_connection *rconn = NULL; + struct io_ctx io_ctx; size_t n = 0; size_t tls_bytes_read = 0; @@ -133,10 +155,13 @@ cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data, DEBUGASSERT(backend); rconn = backend->conn; - io_error = rustls_connection_read_tls(rconn, read_cb, - &cf->conn->sock[cf->sockindex], &tls_bytes_read); + io_ctx.cf = cf; + io_ctx.data = data; + + io_error = rustls_connection_read_tls(rconn, read_cb, &io_ctx, + &tls_bytes_read); if(io_error == EAGAIN || io_error == EWOULDBLOCK) { - infof(data, "sread: EAGAIN or EWOULDBLOCK"); + infof(data, CFMSG(cf, "cr_recv: EAGAIN or EWOULDBLOCK")); } else if(io_error) { char buffer[STRERROR_LEN]; @@ -146,7 +171,7 @@ cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data, return -1; } - infof(data, "cr_recv read %ld bytes from the network", tls_bytes_read); + infof(data, CFMSG(cf, "cr_recv: read %ld TLS bytes"), tls_bytes_read); rresult = rustls_connection_process_new_packets(rconn); if(rresult != RUSTLS_RESULT_OK) { @@ -164,7 +189,8 @@ cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data, plainlen - plain_bytes_copied, &n); if(rresult == RUSTLS_RESULT_PLAINTEXT_EMPTY) { - infof(data, "cr_recv got PLAINTEXT_EMPTY. will try again later."); + infof(data, CFMSG(cf, "cr_recv: got PLAINTEXT_EMPTY. " + "will try again later.")); backend->data_pending = FALSE; break; } @@ -181,7 +207,7 @@ cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data, break; } else { - infof(data, "cr_recv copied out %ld bytes of plaintext", n); + infof(data, CFMSG(cf, "cr_recv: got %ld plain bytes"), n); plain_bytes_copied += n; } } @@ -222,6 +248,7 @@ cr_send(struct Curl_cfilter *cf, struct Curl_easy *data, struct ssl_connect_data *const connssl = cf->ctx; struct ssl_backend_data *const backend = connssl->backend; struct rustls_connection *rconn = NULL; + struct io_ctx io_ctx; size_t plainwritten = 0; size_t tlswritten = 0; size_t tlswritten_total = 0; @@ -231,7 +258,7 @@ cr_send(struct Curl_cfilter *cf, struct Curl_easy *data, DEBUGASSERT(backend); rconn = backend->conn; - infof(data, "cr_send %ld bytes of plaintext", plainlen); + infof(data, CFMSG(cf, "cr_send: %ld plain bytes"), plainlen); if(plainlen > 0) { rresult = rustls_connection_write(rconn, plainbuf, plainlen, @@ -248,11 +275,15 @@ cr_send(struct Curl_cfilter *cf, struct Curl_easy *data, } } + io_ctx.cf = cf; + io_ctx.data = data; + while(rustls_connection_wants_write(rconn)) { - io_error = rustls_connection_write_tls(rconn, write_cb, - &cf->conn->sock[cf->sockindex], &tlswritten); + io_error = rustls_connection_write_tls(rconn, write_cb, &io_ctx, + &tlswritten); if(io_error == EAGAIN || io_error == EWOULDBLOCK) { - infof(data, "swrite: EAGAIN after %ld bytes", tlswritten_total); + infof(data, CFMSG(cf, "cr_send: EAGAIN after %ld bytes"), + tlswritten_total); *err = CURLE_AGAIN; return -1; } @@ -268,7 +299,7 @@ cr_send(struct Curl_cfilter *cf, struct Curl_easy *data, *err = CURLE_WRITE_ERROR; return -1; } - infof(data, "cr_send wrote %ld bytes to network", tlswritten); + infof(data, CFMSG(cf, "cr_send: wrote %ld TLS bytes"), tlswritten); tlswritten_total += tlswritten; } @@ -606,7 +637,8 @@ static size_t cr_version(char *buffer, size_t size) const struct Curl_ssl Curl_ssl_rustls = { { CURLSSLBACKEND_RUSTLS, "rustls" }, SSLSUPP_CAINFO_BLOB | /* supports */ - SSLSUPP_TLS13_CIPHERSUITES, + SSLSUPP_TLS13_CIPHERSUITES | + SSLSUPP_HTTPS_PROXY, sizeof(struct ssl_backend_data), Curl_none_init, /* init */ diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c index ef9a66382b9cd9..7eab9542af335b 100644 --- a/lib/vtls/schannel.c +++ b/lib/vtls/schannel.c @@ -1313,8 +1313,9 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) "sending %lu bytes.", outbuf.cbBuffer)); /* send initial handshake data which is now stored in output buffer */ - result = Curl_write_plain(data, cf->conn->sock[cf->sockindex], - outbuf.pvBuffer, outbuf.cbBuffer, &written); + written = Curl_conn_cf_send(cf->next, data, + outbuf.pvBuffer, outbuf.cbBuffer, + &result); s_pSecFn->FreeContextBuffer(outbuf.pvBuffer); if((result != CURLE_OK) || (outbuf.cbBuffer != (size_t) written)) { failf(data, "schannel: failed to send initial handshake data: " @@ -1411,12 +1412,12 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) for(;;) { if(doread) { /* read encrypted handshake data from socket */ - result = Curl_read_plain(data, cf->conn->sock[cf->sockindex], + nread = Curl_conn_cf_recv(cf->next, data, (char *) (backend->encdata_buffer + backend->encdata_offset), backend->encdata_length - backend->encdata_offset, - &nread); + &result); if(result == CURLE_AGAIN) { if(connssl->connecting_state != ssl_connect_2_writing) connssl->connecting_state = ssl_connect_2_reading; @@ -1500,9 +1501,9 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) "sending %lu bytes.", outbuf[i].cbBuffer)); /* send handshake token to server */ - result = Curl_write_plain(data, cf->conn->sock[cf->sockindex], - outbuf[i].pvBuffer, outbuf[i].cbBuffer, - &written); + written = Curl_conn_cf_send(cf->next, data, + outbuf[i].pvBuffer, outbuf[i].cbBuffer, + &result); if((result != CURLE_OK) || (outbuf[i].cbBuffer != (size_t) written)) { failf(data, "schannel: failed to send next handshake data: " @@ -1964,7 +1965,6 @@ schannel_send(struct Curl_cfilter *cf, struct Curl_easy *data, ssize_t written = -1; size_t data_len = 0; unsigned char *ptr = NULL; - struct connectdata *conn = cf->conn; struct ssl_connect_data *connssl = cf->ctx; SecBuffer outbuf[4]; SecBufferDesc outbuf_desc; @@ -2073,8 +2073,9 @@ schannel_send(struct Curl_cfilter *cf, struct Curl_easy *data, } /* socket is writable */ - result = Curl_write_plain(data, conn->sock[cf->sockindex], - ptr + written, len - written, &this_write); + this_write = Curl_conn_cf_send(cf->next, data, + ptr + written, len - written, + &result); if(result == CURLE_AGAIN) continue; else if(result != CURLE_OK) { @@ -2185,19 +2186,19 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, backend->encdata_offset, backend->encdata_length)); /* read encrypted data from socket */ - *err = Curl_read_plain(data, cf->conn->sock[cf->sockindex], - (char *)(backend->encdata_buffer + + nread = Curl_conn_cf_recv(cf->next, data, + (char *)(backend->encdata_buffer + backend->encdata_offset), - size, &nread); + size, err); if(*err) { nread = -1; if(*err == CURLE_AGAIN) DEBUGF(infof(data, - "schannel: Curl_read_plain returned CURLE_AGAIN")); + "schannel: recv returned CURLE_AGAIN")); else if(*err == CURLE_RECV_ERROR) - infof(data, "schannel: Curl_read_plain returned CURLE_RECV_ERROR"); + infof(data, "schannel: recv returned CURLE_RECV_ERROR"); else - infof(data, "schannel: Curl_read_plain returned error %d", *err); + infof(data, "schannel: recv returned error %d", *err); } else if(nread == 0) { backend->recv_connection_closed = true; @@ -2546,11 +2547,9 @@ static int schannel_shutdown(struct Curl_cfilter *cf, if((sspi_status == SEC_E_OK) || (sspi_status == SEC_I_CONTEXT_EXPIRED)) { /* send close message which is in output buffer */ - ssize_t written; - result = Curl_write_plain(data, cf->conn->sock[cf->sockindex], - outbuf.pvBuffer, outbuf.cbBuffer, - &written); - + ssize_t written = Curl_conn_cf_send(cf->next, data, + outbuf.pvBuffer, outbuf.cbBuffer, + &result); s_pSecFn->FreeContextBuffer(outbuf.pvBuffer); if((result != CURLE_OK) || (outbuf.cbBuffer != (size_t) written)) { infof(data, "schannel: failed to send close msg: %s" @@ -2767,7 +2766,8 @@ const struct Curl_ssl Curl_ssl_schannel = { SSLSUPP_CAINFO_BLOB | #endif SSLSUPP_PINNEDPUBKEY | - SSLSUPP_TLS13_CIPHERSUITES, + SSLSUPP_TLS13_CIPHERSUITES | + SSLSUPP_HTTPS_PROXY, sizeof(struct ssl_backend_data), diff --git a/lib/vtls/sectransp.c b/lib/vtls/sectransp.c index 78076f623c17e6..ab7965465f5a32 100644 --- a/lib/vtls/sectransp.c +++ b/lib/vtls/sectransp.c @@ -142,7 +142,6 @@ struct ssl_backend_data { SSLContextRef ssl_ctx; - curl_socket_t ssl_sockfd; bool ssl_direction; /* true if writing, false if reading */ size_t ssl_write_buffered_length; }; @@ -825,113 +824,62 @@ static const unsigned char ecDsaSecp384r1SpkiHeader[] = { #endif /* SECTRANSP_PINNEDPUBKEY_V1 */ #endif /* SECTRANSP_PINNEDPUBKEY */ -/* The following two functions were ripped from Apple sample code, - * with some modifications: */ -static OSStatus SocketRead(SSLConnectionRef connection, - void *data, /* owned by - * caller, data - * RETURNED */ - size_t *dataLength) /* IN/OUT */ +static OSStatus bio_cf_in_read(SSLConnectionRef connection, + void *buf, + size_t *dataLength) /* IN/OUT */ { - size_t bytesToGo = *dataLength; - size_t initLen = bytesToGo; - UInt8 *currData = (UInt8 *)data; - struct ssl_connect_data *connssl = (struct ssl_connect_data *)connection; + struct Curl_cfilter *cf = (struct Curl_cfilter *)connection; + struct ssl_connect_data *connssl = cf->ctx; struct ssl_backend_data *backend = connssl->backend; - int sock; + struct Curl_easy *data = connssl->call_data; + ssize_t nread; + CURLcode result; OSStatus rtn = noErr; - size_t bytesRead; - ssize_t rrtn; - int theErr; - - DEBUGASSERT(backend); - sock = backend->ssl_sockfd; - *dataLength = 0; - - for(;;) { - bytesRead = 0; - rrtn = read(sock, currData, bytesToGo); - if(rrtn <= 0) { - /* this is guesswork... */ - theErr = errno; - if(rrtn == 0) { /* EOF = server hung up */ - /* the framework will turn this into errSSLClosedNoNotify */ - rtn = errSSLClosedGraceful; - } - else /* do the switch */ - switch(theErr) { - case ENOENT: - /* connection closed */ - rtn = errSSLClosedGraceful; - break; - case ECONNRESET: - rtn = errSSLClosedAbort; - break; - case EAGAIN: - rtn = errSSLWouldBlock; - backend->ssl_direction = false; - break; - default: - rtn = ioErr; - break; - } - break; - } - else { - bytesRead = rrtn; - } - bytesToGo -= bytesRead; - currData += bytesRead; - if(bytesToGo == 0) { - /* filled buffer with incoming data, done */ - break; + DEBUGASSERT(data); + nread = Curl_conn_cf_recv(cf->next, data, buf, *dataLength, &result); + if(nread < 0) { + switch(result) { + case CURLE_OK: + case CURLE_AGAIN: + rtn = errSSLWouldBlock; + backend->ssl_direction = false; + break; + default: + rtn = ioErr; + break; } + nread = 0; } - *dataLength = initLen - bytesToGo; - + *dataLength = nread; return rtn; } -static OSStatus SocketWrite(SSLConnectionRef connection, - const void *data, - size_t *dataLength) /* IN/OUT */ +static OSStatus bio_cf_out_write(SSLConnectionRef connection, + const void *buf, + size_t *dataLength) /* IN/OUT */ { - size_t bytesSent = 0; - struct ssl_connect_data *connssl = (struct ssl_connect_data *)connection; + struct Curl_cfilter *cf = (struct Curl_cfilter *)connection; + struct ssl_connect_data *connssl = cf->ctx; struct ssl_backend_data *backend = connssl->backend; - int sock; - ssize_t length; - size_t dataLen = *dataLength; - const UInt8 *dataPtr = (UInt8 *)data; - OSStatus ortn; - int theErr; - - DEBUGASSERT(backend); - sock = backend->ssl_sockfd; - *dataLength = 0; + struct Curl_easy *data = connssl->call_data; + ssize_t nwritten; + CURLcode result; + OSStatus ortn = noErr; - do { - length = write(sock, - (char *)dataPtr + bytesSent, - dataLen - bytesSent); - } while((length > 0) && - ( (bytesSent += length) < dataLen) ); - - if(length <= 0) { - theErr = errno; - if(theErr == EAGAIN) { + DEBUGASSERT(data); + nwritten = Curl_conn_cf_send(cf->next, data, buf, *dataLength, &result); + if(nwritten <= 0) { + if(result == CURLE_AGAIN) { ortn = errSSLWouldBlock; backend->ssl_direction = true; } else { ortn = ioErr; } + nwritten = 0; } - else { - ortn = noErr; - } - *dataLength = bytesSent; + *dataLength = nwritten; return ortn; } @@ -1666,7 +1614,6 @@ static CURLcode sectransp_set_selected_ciphers(struct Curl_easy *data, static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) { - curl_socket_t sockfd = cf->conn->sock[cf->sockindex]; struct ssl_connect_data *connssl = cf->ctx; struct ssl_backend_data *backend = connssl->backend; struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); @@ -2132,18 +2079,13 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, } } - err = SSLSetIOFuncs(backend->ssl_ctx, SocketRead, SocketWrite); + err = SSLSetIOFuncs(backend->ssl_ctx, bio_cf_in_read, bio_cf_out_write); if(err != noErr) { failf(data, "SSL: SSLSetIOFuncs() failed: OSStatus %d", err); return CURLE_SSL_CONNECT_ERROR; } - /* pass the raw socket into the SSL layers */ - /* We need to store the FD in a constant memory address, because - * SSLSetConnection() will not copy that address. I've found that - * conn->sock[sockindex] may change on its own. */ - backend->ssl_sockfd = sockfd; - err = SSLSetConnection(backend->ssl_ctx, connssl); + err = SSLSetConnection(backend->ssl_ctx, cf); if(err != noErr) { failf(data, "SSL: SSLSetConnection() failed: %d", err); return CURLE_SSL_CONNECT_ERROR; @@ -3185,7 +3127,6 @@ static void sectransp_close(struct Curl_cfilter *cf, struct Curl_easy *data) #endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */ backend->ssl_ctx = NULL; } - backend->ssl_sockfd = 0; } static int sectransp_shutdown(struct Curl_cfilter *cf, @@ -3198,6 +3139,7 @@ static int sectransp_shutdown(struct Curl_cfilter *cf, int rc; char buf[120]; int loop = 10; /* avoid getting stuck */ + CURLcode result; DEBUGASSERT(backend); @@ -3231,12 +3173,10 @@ static int sectransp_shutdown(struct Curl_cfilter *cf, /* Something to read, let's do it and hope that it is the close notify alert from the server. No way to SSL_Read now, so use read(). */ - nread = read(cf->conn->sock[cf->sockindex], buf, sizeof(buf)); + nread = Curl_conn_cf_recv(cf->next, data, buf, sizeof(buf), &result); if(nread < 0) { - char buffer[STRERROR_LEN]; - failf(data, "read: %s", - Curl_strerror(errno, buffer, sizeof(buffer))); + failf(data, "read: %s", curl_easy_strerror(result)); rc = -1; } @@ -3494,10 +3434,9 @@ const struct Curl_ssl Curl_ssl_sectransp = { SSLSUPP_CAINFO_BLOB | SSLSUPP_CERTINFO | #ifdef SECTRANSP_PINNEDPUBKEY - SSLSUPP_PINNEDPUBKEY, -#else - 0, + SSLSUPP_PINNEDPUBKEY | #endif /* SECTRANSP_PINNEDPUBKEY */ + SSLSUPP_HTTPS_PROXY, sizeof(struct ssl_backend_data), @@ -3511,7 +3450,7 @@ const struct Curl_ssl Curl_ssl_sectransp = { Curl_none_cert_status_request, /* cert_status_request */ sectransp_connect, /* connect */ sectransp_connect_nonblocking, /* connect_nonblocking */ - Curl_ssl_get_select_socks, /* getsock */ + Curl_ssl_get_select_socks, /* getsock */ sectransp_get_internals, /* get_internals */ sectransp_close, /* close_one */ Curl_none_close_all, /* close_all */ diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c index 197624632d2dc9..303efc39d21357 100644 --- a/lib/vtls/vtls.c +++ b/lib/vtls/vtls.c @@ -297,11 +297,12 @@ static struct ssl_connect_data *cf_ctx_new(struct Curl_easy *data) { struct ssl_connect_data *ctx; + (void)data; ctx = calloc(1, sizeof(*ctx)); if(!ctx) return NULL; - ctx->backend = calloc(1, Curl_ssl_get_backend_data_size(data)); + ctx->backend = calloc(1, Curl_ssl->sizeof_ssl_backend_data); if(!ctx->backend) { free(ctx); return NULL; @@ -317,6 +318,13 @@ static void cf_ctx_free(struct ssl_connect_data *ctx) } } +static void cf_ctx_set_data(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + if(cf->ctx) + ((struct ssl_connect_data *)cf->ctx)->call_data = data; +} + static CURLcode ssl_connect(struct Curl_cfilter *cf, struct Curl_easy *data) { struct ssl_connect_data *connssl = cf->ctx; @@ -1467,29 +1475,19 @@ static void reinit_hostname(struct Curl_cfilter *cf) static void ssl_cf_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) { + cf_ctx_set_data(cf, data); cf_close(cf, data); cf_ctx_free(cf->ctx); -} - -static CURLcode ssl_cf_setup(struct Curl_cfilter *cf, - struct Curl_easy *data, - const struct Curl_dns_entry *remotehost) -{ - CURLcode result; - - result = cf->next->cft->setup(cf->next, data, remotehost); - if(result) - return result; - - /* TODO our setup */ - return result; + cf->ctx = NULL; } static void ssl_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data) { + cf_ctx_set_data(cf, data); cf_close(cf, data); cf->next->cft->close(cf->next, data); + cf_ctx_set_data(cf, NULL); } static CURLcode ssl_cf_connect(struct Curl_cfilter *cf, @@ -1504,6 +1502,7 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf, return CURLE_OK; } + cf_ctx_set_data(cf, data); (void)connssl; DEBUGASSERT(data->conn); DEBUGASSERT(data->conn == cf->conn); @@ -1512,7 +1511,7 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf, result = cf->next->cft->connect(cf->next, data, blocking, done); if(result || !*done) - return result; + goto out; /* TODO: right now we do not fully control when hostname is set, * assign it on each connect call. */ @@ -1533,15 +1532,23 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf, Curl_pgrsTime(data, TIMER_APPCONNECT); /* SSL is connected */ DEBUGASSERT(connssl->state == ssl_connection_complete); } +out: + cf_ctx_set_data(cf, NULL); return result; } static bool ssl_cf_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data) { + bool result; + + cf_ctx_set_data(cf, (struct Curl_easy *)data); if(cf->ctx && Curl_ssl->data_pending(cf, data)) - return TRUE; - return cf->next->cft->has_data_pending(cf->next, data); + result = TRUE; + else + result = cf->next->cft->has_data_pending(cf->next, data); + cf_ctx_set_data(cf, NULL); + return result; } static ssize_t ssl_cf_send(struct Curl_cfilter *cf, @@ -1551,9 +1558,9 @@ static ssize_t ssl_cf_send(struct Curl_cfilter *cf, ssize_t nwritten; *err = CURLE_OK; + cf_ctx_set_data(cf, data); nwritten = Curl_ssl->send_plain(cf, data, buf, len, err); - DEBUGF(infof(data, CFMSG(cf, "send(len=%ld) -> %ld, code=%d"), - len, nwritten, *err)); + cf_ctx_set_data(cf, NULL); return nwritten; } @@ -1564,9 +1571,9 @@ static ssize_t ssl_cf_recv(struct Curl_cfilter *cf, ssize_t nread; *err = CURLE_OK; + cf_ctx_set_data(cf, data); nread = Curl_ssl->recv_plain(cf, data, buf, len, err); - DEBUGF(infof(data, CFMSG(cf, "recv -> %ld, code=%d"), - nread, *err)); + cf_ctx_set_data(cf, NULL); return nread; } @@ -1574,20 +1581,21 @@ static int ssl_cf_get_select_socks(struct Curl_cfilter *cf, struct Curl_easy *data, curl_socket_t *socks) { - /* TODO, this needs to work for other than SOCKETFIRST filters - * and also nested filters. Needs change of implementations. - * What we really want to know if the SSL implementation wants - * to READ or WRITE or needs nothing. - */ - (void)data; - return Curl_ssl->get_select_socks(cf, data, socks); + int result; + + cf_ctx_set_data(cf, data); + result = Curl_ssl->get_select_socks(cf, data, socks); + cf_ctx_set_data(cf, NULL); + return result; } static void ssl_cf_attach_data(struct Curl_cfilter *cf, struct Curl_easy *data) { if(Curl_ssl->attach_data) { + cf_ctx_set_data(cf, data); Curl_ssl->attach_data(cf, data); + cf_ctx_set_data(cf, NULL); } } @@ -1595,7 +1603,9 @@ static void ssl_cf_detach_data(struct Curl_cfilter *cf, struct Curl_easy *data) { if(Curl_ssl->detach_data) { + cf_ctx_set_data(cf, data); Curl_ssl->detach_data(cf, data); + cf_ctx_set_data(cf, NULL); } } @@ -1603,7 +1613,7 @@ static const struct Curl_cftype cft_ssl = { "SSL", CF_TYPE_SSL, ssl_cf_destroy, - ssl_cf_setup, + Curl_cf_def_setup, ssl_cf_connect, ssl_cf_close, Curl_cf_def_get_host, @@ -1619,7 +1629,7 @@ static const struct Curl_cftype cft_ssl_proxy = { "SSL-PROXY", CF_TYPE_SSL, ssl_cf_destroy, - ssl_cf_setup, + Curl_cf_def_setup, ssl_cf_connect, ssl_cf_close, Curl_cf_def_get_host, @@ -1691,12 +1701,6 @@ CURLcode Curl_ssl_cfilter_proxy_add(struct Curl_easy *data, #endif /* !CURL_DISABLE_PROXY */ -size_t Curl_ssl_get_backend_data_size(struct Curl_easy *data) -{ - (void)data; - return Curl_ssl->sizeof_ssl_backend_data; -} - bool Curl_ssl_supports(struct Curl_easy *data, int option) { (void)data; @@ -1706,15 +1710,19 @@ bool Curl_ssl_supports(struct Curl_easy *data, int option) void *Curl_ssl_get_internals(struct Curl_easy *data, int sockindex, CURLINFO info, int n) { + void *result = NULL; (void)n; if(data->conn) { struct Curl_cfilter *cf; /* get first filter in chain, if any is present */ cf = Curl_ssl_cf_get_ssl(data->conn->cfilter[sockindex]); - if(cf) - return Curl_ssl->get_internals(cf->ctx, info); + if(cf) { + cf_ctx_set_data(cf, data); + result = Curl_ssl->get_internals(cf->ctx, info); + cf_ctx_set_data(cf, NULL); + } } - return NULL; + return result; } bool Curl_ssl_use(struct connectdata *conn, int sockindex) @@ -1722,22 +1730,6 @@ bool Curl_ssl_use(struct connectdata *conn, int sockindex) return Curl_ssl_cf_get_ssl(conn->cfilter[sockindex]) != NULL; } -bool Curl_ssl_conn_is_ssl(struct Curl_easy *data, - int sockindex) -{ - struct Curl_cfilter *cf = data->conn? data->conn->cfilter[sockindex] : NULL; - - /* TODO: this is an inomplete check. We might skip filters here that - * tunnel/transform and only use SSL for part of the connection. - */ - (void)data; - for(; cf; cf = cf->next) { - if(cf->cft == &cft_ssl) - return TRUE; - } - return FALSE; -} - CURLcode Curl_ssl_cfilter_remove(struct Curl_easy *data, int sockindex) { diff --git a/lib/vtls/vtls.h b/lib/vtls/vtls.h index a3601babde5584..ca7a96096907ef 100644 --- a/lib/vtls/vtls.h +++ b/lib/vtls/vtls.h @@ -165,14 +165,6 @@ CURLcode Curl_ssl_cfilter_proxy_add(struct Curl_easy *data, int sockindex); #endif /* !CURL_DISABLE_PROXY */ -/** - * Return TRUE iff the filter chain `sockindex` at connection `conn` - * is using/prepared for SSL encryption. This tests the presence of the - * necessary filters and not their connectedness. - */ -bool Curl_ssl_conn_is_ssl(struct Curl_easy *data, - int sockindex); - /** * Get the SSL configuration that is used on the connection. * This returns NULL if no SSL is configured. @@ -216,8 +208,6 @@ bool Curl_ssl_supports(struct Curl_easy *data, int ssl_option); void *Curl_ssl_get_internals(struct Curl_easy *data, int sockindex, CURLINFO info, int n); -size_t Curl_ssl_get_backend_data_size(struct Curl_easy *data); - bool Curl_ssl_use(struct connectdata *conn, int sockindex); #else /* if not USE_SSL */ @@ -238,9 +228,7 @@ bool Curl_ssl_use(struct connectdata *conn, int sockindex); #define Curl_ssl_false_start(a) FALSE #define Curl_ssl_get_internals(a,b,c,d) NULL #define Curl_ssl_supports(a,b) FALSE -#define Curl_ssl_get_backend_data_size(a) 0 #define Curl_ssl_use(a,b) FALSE -#define Curl_ssl_conn_is_ssl(a,b) FALSE #define Curl_ssl_cfilter_add(a,b,c) CURLE_NOT_BUILT_IN #define Curl_ssl_cfilter_proxy_add(a,b,c) CURLE_NOT_BUILT_IN #define Curl_ssl_get_config(a,b) NULL diff --git a/lib/vtls/vtls_int.h b/lib/vtls/vtls_int.h index 90d737d7f5997a..e2dc34f1d20450 100644 --- a/lib/vtls/vtls_int.h +++ b/lib/vtls/vtls_int.h @@ -33,10 +33,15 @@ struct ssl_connect_data { ssl_connection_state state; ssl_connect_state connecting_state; - const char *hostname; - const char *dispname; - int port; - struct ssl_backend_data *backend; + const char *hostname; /* hostnaem for verification */ + const char *dispname; /* display version of hostname */ + int port; /* remote port at origin */ + struct ssl_backend_data *backend; /* vtls backend specific props */ + struct Curl_easy *call_data; /* data handle used in current call, + * same as paramter passed, but available + * here for backend internal callbacks + * that need it. NULLed after at the + * end of each vtls filter invcocation. */ }; diff --git a/lib/vtls/wolfssl.c b/lib/vtls/wolfssl.c index a622d63d8734a5..aaac828ab7ffe2 100644 --- a/lib/vtls/wolfssl.c +++ b/lib/vtls/wolfssl.c @@ -85,9 +85,16 @@ #endif #endif +#if defined(HAVE_WOLFSSL_FULL_BIO) && HAVE_WOLFSSL_FULL_BIO +#define USE_BIO_CHAIN +#else +#undef USE_BIO_CHAIN +#endif + struct ssl_backend_data { SSL_CTX* ctx; SSL* handle; + CURLcode io_result; }; #ifdef OPENSSL_EXTRA @@ -239,6 +246,139 @@ static const struct group_name_map gnm[] = { }; #endif +#ifdef USE_BIO_CHAIN + +static int bio_cf_create(WOLFSSL_BIO *bio) +{ + wolfSSL_BIO_set_shutdown(bio, 1); + wolfSSL_BIO_set_init(bio, 1); + wolfSSL_BIO_set_data(bio, NULL); + return 1; +} + +static int bio_cf_destroy(WOLFSSL_BIO *bio) +{ + if(!bio) + return 0; + return 1; +} + +static long bio_cf_ctrl(WOLFSSL_BIO *bio, int cmd, long num, void *ptr) +{ + struct Curl_cfilter *cf = BIO_get_data(bio); + long ret = 1; + + (void)cf; + (void)ptr; + switch(cmd) { + case BIO_CTRL_GET_CLOSE: + ret = (long)wolfSSL_BIO_get_shutdown(bio); + break; + case BIO_CTRL_SET_CLOSE: + wolfSSL_BIO_set_shutdown(bio, (int)num); + break; + case BIO_CTRL_FLUSH: + /* we do no delayed writes, but if we ever would, this + * needs to trigger it. */ + ret = 1; + break; + case BIO_CTRL_DUP: + ret = 1; + break; +#ifdef BIO_CTRL_EOF + case BIO_CTRL_EOF: + /* EOF has been reached on input? */ + return (!cf->next || !cf->next->connected); +#endif + default: + ret = 0; + break; + } + return ret; +} + +static int bio_cf_out_write(WOLFSSL_BIO *bio, const char *buf, int blen) +{ + struct Curl_cfilter *cf = wolfSSL_BIO_get_data(bio); + struct ssl_connect_data *connssl = cf->ctx; + struct Curl_easy *data = connssl->call_data; + ssize_t nwritten; + CURLcode result = CURLE_OK; + + DEBUGASSERT(data); + nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, &result); + wolfSSL_BIO_clear_retry_flags(bio); + /* wolfSSL is limited in error handling and SSL_read() will + * return WANT_READ, even though retry was not indicated by + * the installed BIO. */ + connssl->backend->io_result = result; + if(nwritten < 0) { + if(CURLE_AGAIN == result) { + BIO_set_retry_read(bio); + nwritten = 0; + } + else { + nwritten = -1; + } + } + return (int)nwritten; +} + +static int bio_cf_in_read(WOLFSSL_BIO *bio, char *buf, int blen) +{ + struct Curl_cfilter *cf = wolfSSL_BIO_get_data(bio); + struct ssl_connect_data *connssl = cf->ctx; + struct Curl_easy *data = connssl->call_data; + ssize_t nread; + CURLcode result = CURLE_OK; + + DEBUGASSERT(data); + /* OpenSSL catches this case, so should we. */ + if(!buf) + return 0; + + nread = Curl_conn_cf_recv(cf->next, data, buf, blen, &result); + wolfSSL_BIO_clear_retry_flags(bio); + /* wolfSSL is limited in error handling and SSL_read() will + * return WANT_READ, even though retry was not indicated by + * the installed BIO. */ + connssl->backend->io_result = result; + if(nread < 0) { + if(CURLE_AGAIN == result) { + BIO_set_retry_read(bio); + nread = 0; + } + else { + nread = -1; + } + } + return (int)nread; +} + +static WOLFSSL_BIO_METHOD *bio_cf_method = NULL; + +static void bio_cf_init_methods(void) +{ + bio_cf_method = wolfSSL_BIO_meth_new(BIO_TYPE_MEM, "wolfSSL CF BIO"); + wolfSSL_BIO_meth_set_write(bio_cf_method, &bio_cf_out_write); + wolfSSL_BIO_meth_set_read(bio_cf_method, &bio_cf_in_read); + wolfSSL_BIO_meth_set_ctrl(bio_cf_method, &bio_cf_ctrl); + wolfSSL_BIO_meth_set_create(bio_cf_method, &bio_cf_create); + wolfSSL_BIO_meth_set_destroy(bio_cf_method, &bio_cf_destroy); +} + +static void bio_cf_free_methods(void) +{ + wolfSSL_BIO_meth_free(bio_cf_method); +} + +#else /* USE_BIO_CHAIN */ + +#define bio_cf_init_methods() Curl_nop_stmt +#define bio_cf_free_methods() Curl_nop_stmt + +#endif /* !USE_BIO_CHAIN */ + /* * This function loads all the client/CA certificates and CRLs. Setup the TLS * layer and do all necessary magic. @@ -252,7 +392,6 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); const struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); SSL_METHOD* req_method = NULL; - curl_socket_t sockfd = cf->conn->sock[cf->sockindex]; #ifdef HAVE_LIBOQS word16 oqsAlg = 0; size_t idx = 0; @@ -578,11 +717,24 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) Curl_ssl_sessionid_unlock(data); } +#ifdef USE_BIO_CHAIN + { + WOLFSSL_BIO *bio; + + bio = BIO_new(bio_cf_method); + if(!bio) + return CURLE_OUT_OF_MEMORY; + + wolfSSL_BIO_set_data(bio, cf); + wolfSSL_set_bio(backend->handle, bio, bio); + } +#else /* USE_BIO_CHAIN */ /* pass the raw socket into the SSL layer */ - if(!SSL_set_fd(backend->handle, (int)sockfd)) { + if(!SSL_set_fd(backend->handle, (int)cf->conn->sock[cf->sockindex])) { failf(data, "SSL: SSL_set_fd failed"); return CURLE_SSL_CONNECT_ERROR; } +#endif /* !USE_BIO_CHAIN */ connssl->connecting_state = ssl_connect_2; return CURLE_OK; @@ -642,7 +794,10 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) char error_buffer[WOLFSSL_MAX_ERROR_SZ]; int detail = SSL_get_error(backend->handle, ret); - if(SSL_ERROR_WANT_READ == detail) { + if(backend->io_result != CURLE_OK && backend->io_result != CURLE_AGAIN) { + return backend->io_result; + } + else if(SSL_ERROR_WANT_READ == detail) { connssl->connecting_state = ssl_connect_2_reading; return CURLE_OK; } @@ -870,6 +1025,10 @@ static ssize_t wolfssl_send(struct Curl_cfilter *cf, if(rc <= 0) { int err = SSL_get_error(backend->handle, rc); + if(backend->io_result != CURLE_OK && backend->io_result != CURLE_AGAIN) { + *curlcode = backend->io_result; + return -1; + } switch(err) { case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: @@ -932,6 +1091,10 @@ static ssize_t wolfssl_recv(struct Curl_cfilter *cf, if(nread <= 0) { int err = SSL_get_error(backend->handle, nread); + if(backend->io_result != CURLE_OK && backend->io_result != CURLE_AGAIN) { + *curlcode = backend->io_result; + return -1; + } switch(err) { case SSL_ERROR_ZERO_RETURN: /* no more data */ break; @@ -972,15 +1135,20 @@ static size_t wolfssl_version(char *buffer, size_t size) static int wolfssl_init(void) { + int ret; + #ifdef OPENSSL_EXTRA Curl_tls_keylog_open(); #endif - return (wolfSSL_Init() == SSL_SUCCESS); + ret = (wolfSSL_Init() == SSL_SUCCESS); + bio_cf_init_methods(); + return ret; } static void wolfssl_cleanup(void) { + bio_cf_free_methods(); wolfSSL_Cleanup(); #ifdef OPENSSL_EXTRA Curl_tls_keylog_close(); @@ -1200,6 +1368,9 @@ const struct Curl_ssl Curl_ssl_wolfssl = { #ifdef KEEP_PEER_CERT SSLSUPP_PINNEDPUBKEY | +#endif +#ifdef USE_BIO_CHAIN + SSLSUPP_HTTPS_PROXY | #endif SSLSUPP_SSL_CTX, diff --git a/m4/curl-wolfssl.m4 b/m4/curl-wolfssl.m4 index d66c97935e29f8..0872bcb78c4ba5 100644 --- a/m4/curl-wolfssl.m4 +++ b/m4/curl-wolfssl.m4 @@ -143,6 +143,15 @@ if test "x$OPT_WOLFSSL" != xno; then ] ) + dnl if this symbol is present, we can make use of BIO filter chains + AC_CHECK_FUNC(wolfSSL_BIO_set_shutdown, + [ + AC_DEFINE(HAVE_WOLFSSL_FULL_BIO, 1, + [if you have wolfSSL_BIO_set_shutdown]) + WOLFSSL_FULL_BIO=1 + ] + ) + if test -n "$wolfssllibpath"; then dnl when shared libs were found in a path that the run-time dnl linker doesn't search through, we need to add it to