From: Stefan Eissing Date: Mon, 24 Feb 2025 12:35:20 +0000 (+0100) Subject: vtls: move common early data code into vtls.c X-Git-Tag: curl-8_13_0~334 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=4ed9db9eef9dc110c92e8c61a91141c16bc2a06b;p=thirdparty%2Fcurl.git vtls: move common early data code into vtls.c With now 2 backends implementing early data, it makes sense to have the common handling in a single place. Closes #16450 --- diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c index 54e4a9f908..2807f4f95f 100644 --- a/lib/vtls/gtls.c +++ b/lib/vtls/gtls.c @@ -1064,7 +1064,7 @@ static CURLcode gtls_on_session_reuse(struct Curl_cfilter *cf, else { infof(data, "SSL session allows %zu bytes of early data, " "reusing ALPN '%s'", connssl->earlydata_max, scs->alpn); - connssl->earlydata_state = ssl_earlydata_use; + connssl->earlydata_state = ssl_earlydata_await; connssl->state = ssl_connection_deferred; result = Curl_alpn_set_negotiated(cf, data, connssl, (const unsigned char *)scs->alpn, @@ -1756,30 +1756,6 @@ out: return result; } -static CURLcode gtls_set_earlydata(struct Curl_cfilter *cf, - struct Curl_easy *data, - const void *buf, size_t blen) -{ - struct ssl_connect_data *connssl = cf->ctx; - ssize_t nwritten = 0; - CURLcode result = CURLE_OK; - - DEBUGASSERT(connssl->earlydata_state == ssl_earlydata_use); - DEBUGASSERT(Curl_bufq_is_empty(&connssl->earlydata)); - if(blen) { - if(blen > connssl->earlydata_max) - blen = connssl->earlydata_max; - nwritten = Curl_bufq_write(&connssl->earlydata, buf, blen, &result); - CURL_TRC_CF(data, cf, "gtls_set_earlydata(len=%zu) -> %zd", - blen, nwritten); - if(nwritten < 0) - return result; - } - connssl->earlydata_state = ssl_earlydata_sending; - connssl->earlydata_skip = Curl_bufq_len(&connssl->earlydata); - return CURLE_OK; -} - static CURLcode gtls_send_earlydata(struct Curl_cfilter *cf, struct Curl_easy *data) { @@ -1855,7 +1831,7 @@ static CURLcode gtls_connect_common(struct Curl_cfilter *cf, } if(connssl->connecting_state == ssl_connect_2) { - if(connssl->earlydata_state == ssl_earlydata_use) { + if(connssl->earlydata_state == ssl_earlydata_await) { goto out; } else if(connssl->earlydata_state == ssl_earlydata_sending) { @@ -1863,8 +1839,6 @@ static CURLcode gtls_connect_common(struct Curl_cfilter *cf, if(result) goto out; connssl->earlydata_state = ssl_earlydata_sent; - if(!Curl_ssl_cf_is_proxy(cf)) - Curl_pgrsEarlyData(data, (curl_off_t)connssl->earlydata_skip); } DEBUGASSERT((connssl->earlydata_state == ssl_earlydata_none) || (connssl->earlydata_state == ssl_earlydata_sent)); @@ -1896,31 +1870,19 @@ static CURLcode gtls_connect_common(struct Curl_cfilter *cf, if(result) goto out; - if(connssl->earlydata_state == ssl_earlydata_sent) { - /* report the true time the handshake was done */ - connssl->handshake_done = Curl_now(); - Curl_pgrsTimeWas(data, TIMER_APPCONNECT, connssl->handshake_done); - if(gnutls_session_get_flags(backend->gtls.session) & - GNUTLS_SFLAGS_EARLY_DATA) { - connssl->earlydata_state = ssl_earlydata_accepted; - infof(data, "Server accepted %zu bytes of TLS early data.", - connssl->earlydata_skip); - } - else { - connssl->earlydata_state = ssl_earlydata_rejected; - if(!Curl_ssl_cf_is_proxy(cf)) - Curl_pgrsEarlyData(data, -(curl_off_t)connssl->earlydata_skip); - infof(data, "Server rejected TLS early data."); - connssl->earlydata_skip = 0; - } + if(connssl->earlydata_state > ssl_earlydata_none) { + /* We should be in this state by now */ + DEBUGASSERT(connssl->earlydata_state == ssl_earlydata_sent); + connssl->earlydata_state = + (gnutls_session_get_flags(backend->gtls.session) & + GNUTLS_SFLAGS_EARLY_DATA) ? + ssl_earlydata_accepted : ssl_earlydata_rejected; } connssl->connecting_state = ssl_connect_done; } - if(ssl_connect_done == connssl->connecting_state) { - connssl->state = ssl_connection_complete; - *done = TRUE; - } + if(connssl->connecting_state == ssl_connect_done) + DEBUGASSERT(connssl->state == ssl_connection_complete); out: if(result == CURLE_AGAIN) { @@ -1938,7 +1900,8 @@ static CURLcode gtls_connect(struct Curl_cfilter *cf, bool *done) { struct ssl_connect_data *connssl = cf->ctx; - if(connssl->state == ssl_connection_deferred) { + if((connssl->state == ssl_connection_deferred) && + (connssl->earlydata_state == ssl_earlydata_await)) { /* We refuse to be pushed, we are waiting for someone to send/recv. */ *done = TRUE; return CURLE_OK; @@ -1946,26 +1909,6 @@ static CURLcode gtls_connect(struct Curl_cfilter *cf, return gtls_connect_common(cf, data, done); } -static CURLcode gtls_connect_deferred(struct Curl_cfilter *cf, - struct Curl_easy *data, - const void *buf, - size_t blen, - bool *done) -{ - struct ssl_connect_data *connssl = cf->ctx; - CURLcode result = CURLE_OK; - - DEBUGASSERT(connssl->state == ssl_connection_deferred); - *done = FALSE; - if(connssl->earlydata_state == ssl_earlydata_use) { - result = gtls_set_earlydata(cf, data, buf, blen); - if(result) - return result; - } - - return gtls_connect_common(cf, data, done); -} - static bool gtls_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data) { @@ -1993,38 +1936,9 @@ static ssize_t gtls_send(struct Curl_cfilter *cf, ssize_t rc; size_t nwritten, total_written = 0; + (void)data; DEBUGASSERT(backend); - if(connssl->state == ssl_connection_deferred) { - bool done = FALSE; - *curlcode = gtls_connect_deferred(cf, data, buf, blen, &done); - if(*curlcode) { - rc = -1; - goto out; - } - else if(!done) { - *curlcode = CURLE_AGAIN; - rc = -1; - goto out; - } - DEBUGASSERT(connssl->state == ssl_connection_complete); - } - - if(connssl->earlydata_skip) { - if(connssl->earlydata_skip >= blen) { - connssl->earlydata_skip -= blen; - *curlcode = CURLE_OK; - rc = (ssize_t)blen; - goto out; - } - else { - total_written += connssl->earlydata_skip; - buf = ((const char *)buf) + connssl->earlydata_skip; - blen -= connssl->earlydata_skip; - connssl->earlydata_skip = 0; - } - } - while(blen) { backend->gtls.io_result = CURLE_OK; rc = gnutls_record_send(backend->gtls.session, buf, blen); @@ -2171,21 +2085,6 @@ static ssize_t gtls_recv(struct Curl_cfilter *cf, (void)data; DEBUGASSERT(backend); - if(connssl->state == ssl_connection_deferred) { - bool done = FALSE; - *curlcode = gtls_connect_deferred(cf, data, NULL, 0, &done); - if(*curlcode) { - ret = -1; - goto out; - } - else if(!done) { - *curlcode = CURLE_AGAIN; - ret = -1; - goto out; - } - DEBUGASSERT(connssl->state == ssl_connection_complete); - } - ret = gnutls_record_recv(backend->gtls.session, buf, buffersize); if((ret == GNUTLS_E_AGAIN) || (ret == GNUTLS_E_INTERRUPTED)) { *curlcode = CURLE_AGAIN; diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c index 015a43f72b..5284b90755 100644 --- a/lib/vtls/vtls.c +++ b/lib/vtls/vtls.c @@ -485,18 +485,6 @@ static void cf_ctx_free(struct ssl_connect_data *ctx) } } -static CURLcode -ssl_connect(struct Curl_cfilter *cf, struct Curl_easy *data, bool *done) -{ - struct ssl_connect_data *connssl = cf->ctx; - - if(!ssl_prefs_check(data)) - return CURLE_SSL_CONNECT_ERROR; - - /* mark this is being ssl requested from here on. */ - return connssl->ssl_impl->do_connect(cf, data, done); -} - CURLcode Curl_ssl_get_channel_binding(struct Curl_easy *data, int sockindex, struct dynbuf *binding) { @@ -1318,7 +1306,7 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf, struct cf_call_data save; CURLcode result; - if(cf->connected) { + if(cf->connected && (connssl->state != ssl_connection_deferred)) { *done = TRUE; return CURLE_OK; } @@ -1336,8 +1324,6 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf, CF_DATA_SAVE(save, cf, data); CURL_TRC_CF(data, cf, "cf_connect()"); - DEBUGASSERT(data->conn); - DEBUGASSERT(data->conn == cf->conn); DEBUGASSERT(connssl); *done = FALSE; @@ -1349,7 +1335,13 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf, goto out; } - result = ssl_connect(cf, data, done); + if(!connssl->prefs_checked) { + if(!ssl_prefs_check(data)) + return CURLE_SSL_CONNECT_ERROR; + connssl->prefs_checked = TRUE; + } + + result = connssl->ssl_impl->do_connect(cf, data, done); if(!result && *done) { cf->connected = TRUE; @@ -1358,6 +1350,8 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf, /* Connection can be deferred when sending early data */ DEBUGASSERT(connssl->state == ssl_connection_complete || connssl->state == ssl_connection_deferred); + DEBUGASSERT(connssl->state != ssl_connection_deferred || + connssl->earlydata_state > ssl_earlydata_none); } out: CURL_TRC_CF(data, cf, "cf_connect() -> %d, done=%d", result, *done); @@ -1365,6 +1359,77 @@ out: return result; } +static CURLcode ssl_cf_set_earlydata(struct Curl_cfilter *cf, + struct Curl_easy *data, + const void *buf, size_t blen) +{ + struct ssl_connect_data *connssl = cf->ctx; + ssize_t nwritten = 0; + CURLcode result = CURLE_OK; + + DEBUGASSERT(connssl->earlydata_state == ssl_earlydata_await); + DEBUGASSERT(Curl_bufq_is_empty(&connssl->earlydata)); + if(blen) { + if(blen > connssl->earlydata_max) + blen = connssl->earlydata_max; + nwritten = Curl_bufq_write(&connssl->earlydata, buf, blen, &result); + CURL_TRC_CF(data, cf, "ssl_cf_set_earlydata(len=%zu) -> %zd", + blen, nwritten); + if(nwritten < 0) + return result; + } + return CURLE_OK; +} + +static CURLcode ssl_cf_connect_deferred(struct Curl_cfilter *cf, + struct Curl_easy *data, + const void *buf, size_t blen, + bool *done) +{ + struct ssl_connect_data *connssl = cf->ctx; + CURLcode result = CURLE_OK; + + DEBUGASSERT(connssl->state == ssl_connection_deferred); + *done = FALSE; + if(connssl->earlydata_state == ssl_earlydata_await) { + result = ssl_cf_set_earlydata(cf, data, buf, blen); + if(result) + return result; + /* we buffered any early data we'd like to send. Actually + * do the connect now which sends it and performs the handshake. */ + connssl->earlydata_state = ssl_earlydata_sending; + connssl->earlydata_skip = Curl_bufq_len(&connssl->earlydata); + } + + result = ssl_cf_connect(cf, data, done); + + if(!result && *done) { + Curl_pgrsTimeWas(data, TIMER_APPCONNECT, connssl->handshake_done); + switch(connssl->earlydata_state) { + case ssl_earlydata_none: + break; + case ssl_earlydata_accepted: + if(!Curl_ssl_cf_is_proxy(cf)) + Curl_pgrsEarlyData(data, (curl_off_t)connssl->earlydata_skip); + infof(data, "Server accepted %zu bytes of TLS early data.", + connssl->earlydata_skip); + break; + case ssl_earlydata_rejected: + if(!Curl_ssl_cf_is_proxy(cf)) + Curl_pgrsEarlyData(data, -(curl_off_t)connssl->earlydata_skip); + infof(data, "Server rejected TLS early data."); + connssl->earlydata_skip = 0; + break; + default: + /* This should not happen. Either we do not use early data or we + * should know if it was accepted or not. */ + DEBUGASSERT(NULL); + break; + } + } + return result; +} + static bool ssl_cf_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data) { @@ -1383,21 +1448,57 @@ static bool ssl_cf_data_pending(struct Curl_cfilter *cf, } static ssize_t ssl_cf_send(struct Curl_cfilter *cf, - struct Curl_easy *data, const void *buf, size_t len, + struct Curl_easy *data, + const void *buf, size_t blen, bool eos, CURLcode *err) { struct ssl_connect_data *connssl = cf->ctx; struct cf_call_data save; - ssize_t nwritten = 0; + ssize_t nwritten = 0, early_written = 0; (void)eos; - /* OpenSSL and maybe other TLS libs do not like 0-length writes. Skip. */ *err = CURLE_OK; - if(len > 0) { - CF_DATA_SAVE(save, cf, data); - nwritten = connssl->ssl_impl->send_plain(cf, data, buf, len, err); - CF_DATA_RESTORE(cf, save); + CF_DATA_SAVE(save, cf, data); + + if(connssl->state == ssl_connection_deferred) { + bool done = FALSE; + *err = ssl_cf_connect_deferred(cf, data, buf, blen, &done); + if(*err) { + nwritten = -1; + goto out; + } + else if(!done) { + *err = CURLE_AGAIN; + nwritten = -1; + goto out; + } + DEBUGASSERT(connssl->state == ssl_connection_complete); } + + if(connssl->earlydata_skip) { + if(connssl->earlydata_skip >= blen) { + connssl->earlydata_skip -= blen; + *err = CURLE_OK; + nwritten = (ssize_t)blen; + goto out; + } + else { + early_written = connssl->earlydata_skip; + buf = ((const char *)buf) + connssl->earlydata_skip; + blen -= connssl->earlydata_skip; + connssl->earlydata_skip = 0; + } + } + + /* OpenSSL and maybe other TLS libs do not like 0-length writes. Skip. */ + if(blen > 0) + nwritten = connssl->ssl_impl->send_plain(cf, data, buf, blen, err); + + if(nwritten >= 0) + nwritten += early_written; + +out: + CF_DATA_RESTORE(cf, save); return nwritten; } @@ -1411,6 +1512,21 @@ static ssize_t ssl_cf_recv(struct Curl_cfilter *cf, CF_DATA_SAVE(save, cf, data); *err = CURLE_OK; + if(connssl->state == ssl_connection_deferred) { + bool done = FALSE; + *err = ssl_cf_connect_deferred(cf, data, NULL, 0, &done); + if(*err) { + nread = -1; + goto out; + } + else if(!done) { + *err = CURLE_AGAIN; + nread = -1; + goto out; + } + DEBUGASSERT(connssl->state == ssl_connection_complete); + } + nread = connssl->ssl_impl->recv_plain(cf, data, buf, len, err); if(nread > 0) { DEBUGASSERT((size_t)nread <= len); @@ -1419,6 +1535,8 @@ static ssize_t ssl_cf_recv(struct Curl_cfilter *cf, /* eof */ *err = CURLE_OK; } + +out: CURL_TRC_CF(data, cf, "cf_recv(len=%zu) -> %zd, %d", len, nread, *err); CF_DATA_RESTORE(cf, save); @@ -1433,7 +1551,9 @@ static CURLcode ssl_cf_shutdown(struct Curl_cfilter *cf, CURLcode result = CURLE_OK; *done = TRUE; - if(!cf->shutdown && Curl_ssl->shut_down) { + /* If we have done the SSL handshake, shut down the connection cleanly */ + if(cf->connected && (connssl->state == ssl_connection_complete) && + !cf->shutdown && Curl_ssl->shut_down) { struct cf_call_data save; CF_DATA_SAVE(save, cf, data); diff --git a/lib/vtls/vtls_int.h b/lib/vtls/vtls_int.h index 0e9862a059..5dadbb1f24 100644 --- a/lib/vtls/vtls_int.h +++ b/lib/vtls/vtls_int.h @@ -91,7 +91,7 @@ typedef enum { typedef enum { ssl_earlydata_none, - ssl_earlydata_use, + ssl_earlydata_await, ssl_earlydata_sending, ssl_earlydata_sent, ssl_earlydata_accepted, @@ -126,6 +126,7 @@ struct ssl_connect_data { int io_need; /* TLS signals special SEND/RECV needs */ BIT(use_alpn); /* if ALPN shall be used in handshake */ BIT(peer_closed); /* peer has closed connection */ + BIT(prefs_checked); /* SSL preferences have been checked */ }; diff --git a/lib/vtls/wolfssl.c b/lib/vtls/wolfssl.c index 9d112616ce..5d909b645d 100644 --- a/lib/vtls/wolfssl.c +++ b/lib/vtls/wolfssl.c @@ -525,7 +525,7 @@ static CURLcode wssl_on_session_reuse(struct Curl_cfilter *cf, else { infof(data, "SSL session allows %zu bytes of early data, " "reusing ALPN '%s'", connssl->earlydata_max, scs->alpn); - connssl->earlydata_state = ssl_earlydata_use; + connssl->earlydata_state = ssl_earlydata_await; connssl->state = ssl_connection_deferred; result = Curl_alpn_set_negotiated(cf, data, connssl, (const unsigned char *)scs->alpn, @@ -1662,6 +1662,8 @@ static CURLcode wssl_handshake(struct Curl_cfilter *cf, } DEBUGASSERT((connssl->earlydata_state == ssl_earlydata_none) || (connssl->earlydata_state == ssl_earlydata_sent)); +#else + DEBUGASSERT(connssl->earlydata_state == ssl_earlydata_none); #endif /* WOLFSSL_EARLY_DATA */ wolfSSL_ERR_clear_error(); @@ -1788,52 +1790,6 @@ static CURLcode wssl_handshake(struct Curl_cfilter *cf, } } -#ifdef WOLFSSL_EARLY_DATA -static CURLcode wssl_set_earlydata(struct Curl_cfilter *cf, - struct Curl_easy *data, - const void *buf, size_t blen) -{ - struct ssl_connect_data *connssl = cf->ctx; - ssize_t nwritten = 0; - CURLcode result = CURLE_OK; - - DEBUGASSERT(connssl->earlydata_state == ssl_earlydata_use); - DEBUGASSERT(Curl_bufq_is_empty(&connssl->earlydata)); - if(blen) { - if(blen > connssl->earlydata_max) - blen = connssl->earlydata_max; - nwritten = Curl_bufq_write(&connssl->earlydata, buf, blen, &result); - CURL_TRC_CF(data, cf, "wssl_set_earlydata(len=%zu) -> %zd", - blen, nwritten); - if(nwritten < 0) - return result; - } - connssl->earlydata_state = ssl_earlydata_sending; - connssl->earlydata_skip = Curl_bufq_len(&connssl->earlydata); - return CURLE_OK; -} - -static CURLcode wssl_connect_deferred(struct Curl_cfilter *cf, - struct Curl_easy *data, - const void *buf, - size_t blen, - bool *done) -{ - struct ssl_connect_data *connssl = cf->ctx; - CURLcode result = CURLE_OK; - - DEBUGASSERT(connssl->state == ssl_connection_deferred); - *done = FALSE; - if(connssl->earlydata_state == ssl_earlydata_use) { - result = wssl_set_earlydata(cf, data, buf, blen); - if(result) - return result; - } - - return wssl_connect(cf, data, done); -} -#endif /* WOLFSSL_EARLY_DATA */ - static ssize_t wssl_send(struct Curl_cfilter *cf, struct Curl_easy *data, const void *buf, size_t blen, @@ -1847,38 +1803,6 @@ static ssize_t wssl_send(struct Curl_cfilter *cf, wolfSSL_ERR_clear_error(); -#ifdef WOLFSSL_EARLY_DATA - if(connssl->state == ssl_connection_deferred) { - bool done = FALSE; - *curlcode = wssl_connect_deferred(cf, data, buf, blen, &done); - if(*curlcode) { - nwritten = -1; - goto out; - } - else if(!done) { - *curlcode = CURLE_AGAIN; - nwritten = -1; - goto out; - } - DEBUGASSERT(connssl->state == ssl_connection_complete); - } - - if(connssl->earlydata_skip) { - if(connssl->earlydata_skip >= blen) { - connssl->earlydata_skip -= blen; - *curlcode = CURLE_OK; - nwritten = (ssize_t)blen; - goto out; - } - else { - total_written += connssl->earlydata_skip; - buf = ((const char *)buf) + connssl->earlydata_skip; - blen -= connssl->earlydata_skip; - connssl->earlydata_skip = 0; - } - } -#endif /* WOLFSSL_EARLY_DATA */ - if(blen) { int memlen = (blen > (size_t)INT_MAX) ? INT_MAX : (int)blen; int rc; @@ -2071,21 +1995,6 @@ static ssize_t wssl_recv(struct Curl_cfilter *cf, DEBUGASSERT(wssl); -#ifdef WOLFSSL_EARLY_DATA - if(connssl->state == ssl_connection_deferred) { - bool done = FALSE; - *curlcode = wssl_connect_deferred(cf, data, NULL, 0, &done); - if(*curlcode) { - return -1; - } - else if(!done) { - *curlcode = CURLE_AGAIN; - return-1; - } - DEBUGASSERT(connssl->state == ssl_connection_complete); - } -#endif - wolfSSL_ERR_clear_error(); *curlcode = CURLE_OK; @@ -2212,7 +2121,7 @@ static CURLcode wssl_connect(struct Curl_cfilter *cf, } if(ssl_connect_2 == connssl->connecting_state) { - if(connssl->earlydata_state == ssl_earlydata_use) { + if(connssl->earlydata_state == ssl_earlydata_await) { /* We defer the handshake until request data arrives. */ DEBUGASSERT(connssl->state == ssl_connection_deferred); goto out; @@ -2272,23 +2181,13 @@ static CURLcode wssl_connect(struct Curl_cfilter *cf, connssl->state = ssl_connection_complete; #ifdef WOLFSSL_EARLY_DATA - if(connssl->earlydata_state == ssl_earlydata_sent) { - /* report the true time the handshake was done */ - connssl->handshake_done = Curl_now(); - Curl_pgrsTimeWas(data, TIMER_APPCONNECT, connssl->handshake_done); - if(wolfSSL_get_early_data_status(wssl->ssl) == - WOLFSSL_EARLY_DATA_REJECTED) { - connssl->earlydata_state = ssl_earlydata_rejected; - if(!Curl_ssl_cf_is_proxy(cf)) - Curl_pgrsEarlyData(data, -(curl_off_t)connssl->earlydata_skip); - infof(data, "Server rejected TLS early data."); - connssl->earlydata_skip = 0; - } - else if(connssl->earlydata_skip) { - connssl->earlydata_state = ssl_earlydata_accepted; - infof(data, "Server accepted %zu bytes of TLS early data.", - connssl->earlydata_skip); - } + if(connssl->earlydata_state > ssl_earlydata_none) { + /* We should be in this state by now */ + DEBUGASSERT(connssl->earlydata_state == ssl_earlydata_sent); + connssl->earlydata_state = + (wolfSSL_get_early_data_status(wssl->ssl) == + WOLFSSL_EARLY_DATA_REJECTED) ? + ssl_earlydata_rejected : ssl_earlydata_accepted; } #endif /* WOLFSSL_EARLY_DATA */ }