mirror of
https://github.com/moparisthebest/curl
synced 2024-11-17 15:05:02 -05:00
bearssl: Improve I/O handling
Factor out common I/O loop as bearssl_run_until, which reads/writes TLS records until the desired engine state is reached. This is now used for the handshake, read, write, and close. Match OpenSSL SSL_write behavior, and don't return the number of bytes written until the corresponding records have been completely flushed across the socket. This involves keeping track of the length of data buffered into the TLS engine, and assumes that when CURLE_AGAIN is returned, the write function will be called again with the same data and length arguments. This is the same requirement of SSL_write. Handle TLS close notify as EOF when reading by returning 0. Closes https://github.com/curl/curl/pull/4748
This commit is contained in:
parent
9434864770
commit
9024b01387
@ -52,6 +52,8 @@ struct ssl_backend_data {
|
|||||||
const char *protocols[2];
|
const char *protocols[2];
|
||||||
/* SSL client context is active */
|
/* SSL client context is active */
|
||||||
bool active;
|
bool active;
|
||||||
|
/* size of pending write, yet to be flushed */
|
||||||
|
size_t pending_write;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define BACKEND connssl->backend
|
#define BACKEND connssl->backend
|
||||||
@ -421,7 +423,8 @@ static CURLcode bearssl_connect_step1(struct connectdata *conn, int sockindex)
|
|||||||
return CURLE_OK;
|
return CURLE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static CURLcode bearssl_connect_step2(struct connectdata *conn, int sockindex)
|
static CURLcode bearssl_run_until(struct connectdata *conn, int sockindex,
|
||||||
|
unsigned target)
|
||||||
{
|
{
|
||||||
struct Curl_easy *data = conn->data;
|
struct Curl_easy *data = conn->data;
|
||||||
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
|
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
|
||||||
@ -437,6 +440,13 @@ static CURLcode bearssl_connect_step2(struct connectdata *conn, int sockindex)
|
|||||||
if(state & BR_SSL_CLOSED) {
|
if(state & BR_SSL_CLOSED) {
|
||||||
err = br_ssl_engine_last_error(&BACKEND->ctx.eng);
|
err = br_ssl_engine_last_error(&BACKEND->ctx.eng);
|
||||||
switch(err) {
|
switch(err) {
|
||||||
|
case BR_ERR_OK:
|
||||||
|
/* TLS close notify */
|
||||||
|
if(connssl->state != ssl_connection_complete) {
|
||||||
|
failf(data, "SSL: connection closed during handshake");
|
||||||
|
return CURLE_SSL_CONNECT_ERROR;
|
||||||
|
}
|
||||||
|
return CURLE_OK;
|
||||||
case BR_ERR_X509_EXPIRED:
|
case BR_ERR_X509_EXPIRED:
|
||||||
failf(data, "SSL: X.509 verification: "
|
failf(data, "SSL: X.509 verification: "
|
||||||
"certificate is expired or not yet valid");
|
"certificate is expired or not yet valid");
|
||||||
@ -455,39 +465,60 @@ static CURLcode bearssl_connect_step2(struct connectdata *conn, int sockindex)
|
|||||||
return CURLE_PEER_FAILED_VERIFICATION;
|
return CURLE_PEER_FAILED_VERIFICATION;
|
||||||
return CURLE_SSL_CONNECT_ERROR;
|
return CURLE_SSL_CONNECT_ERROR;
|
||||||
}
|
}
|
||||||
if(state & (BR_SSL_SENDAPP | BR_SSL_RECVAPP)) {
|
if(state & target)
|
||||||
connssl->connecting_state = ssl_connect_3;
|
|
||||||
return CURLE_OK;
|
return CURLE_OK;
|
||||||
}
|
|
||||||
if(state & BR_SSL_SENDREC) {
|
if(state & BR_SSL_SENDREC) {
|
||||||
buf = br_ssl_engine_sendrec_buf(&BACKEND->ctx.eng, &len);
|
buf = br_ssl_engine_sendrec_buf(&BACKEND->ctx.eng, &len);
|
||||||
ret = swrite(sockfd, buf, len);
|
ret = swrite(sockfd, buf, len);
|
||||||
if(ret == -1) {
|
if(ret == -1) {
|
||||||
if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) {
|
if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) {
|
||||||
|
if(connssl->state != ssl_connection_complete)
|
||||||
connssl->connecting_state = ssl_connect_2_writing;
|
connssl->connecting_state = ssl_connect_2_writing;
|
||||||
return CURLE_OK;
|
return CURLE_AGAIN;
|
||||||
}
|
}
|
||||||
return CURLE_SEND_ERROR;
|
return CURLE_WRITE_ERROR;
|
||||||
}
|
}
|
||||||
br_ssl_engine_sendrec_ack(&BACKEND->ctx.eng, ret);
|
br_ssl_engine_sendrec_ack(&BACKEND->ctx.eng, ret);
|
||||||
}
|
}
|
||||||
else if(state & BR_SSL_RECVREC) {
|
else if(state & BR_SSL_RECVREC) {
|
||||||
buf = br_ssl_engine_recvrec_buf(&BACKEND->ctx.eng, &len);
|
buf = br_ssl_engine_recvrec_buf(&BACKEND->ctx.eng, &len);
|
||||||
ret = sread(sockfd, buf, len);
|
ret = sread(sockfd, buf, len);
|
||||||
|
if(ret == 0) {
|
||||||
|
failf(data, "SSL: EOF without close notify");
|
||||||
|
return CURLE_READ_ERROR;
|
||||||
|
}
|
||||||
if(ret == -1) {
|
if(ret == -1) {
|
||||||
if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) {
|
if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) {
|
||||||
|
if(connssl->state != ssl_connection_complete)
|
||||||
connssl->connecting_state = ssl_connect_2_reading;
|
connssl->connecting_state = ssl_connect_2_reading;
|
||||||
return CURLE_OK;
|
return CURLE_AGAIN;
|
||||||
}
|
}
|
||||||
return CURLE_READ_ERROR;
|
return CURLE_READ_ERROR;
|
||||||
}
|
}
|
||||||
if(ret == 0)
|
|
||||||
return CURLE_SSL_CONNECT_ERROR;
|
|
||||||
br_ssl_engine_recvrec_ack(&BACKEND->ctx.eng, ret);
|
br_ssl_engine_recvrec_ack(&BACKEND->ctx.eng, ret);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static CURLcode bearssl_connect_step2(struct connectdata *conn, int sockindex)
|
||||||
|
{
|
||||||
|
struct Curl_easy *data = conn->data;
|
||||||
|
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
|
||||||
|
CURLcode ret;
|
||||||
|
|
||||||
|
ret = bearssl_run_until(conn, sockindex, BR_SSL_SENDAPP | BR_SSL_RECVAPP);
|
||||||
|
if(ret == CURLE_AGAIN)
|
||||||
|
return CURLE_OK;
|
||||||
|
if(ret == CURLE_OK) {
|
||||||
|
if(br_ssl_engine_current_state(&BACKEND->ctx.eng) == BR_SSL_CLOSED) {
|
||||||
|
failf(data, "SSL: connection closed during handshake");
|
||||||
|
return CURLE_SSL_CONNECT_ERROR;
|
||||||
|
}
|
||||||
|
connssl->connecting_state = ssl_connect_3;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static CURLcode bearssl_connect_step3(struct connectdata *conn, int sockindex)
|
static CURLcode bearssl_connect_step3(struct connectdata *conn, int sockindex)
|
||||||
{
|
{
|
||||||
struct Curl_easy *data = conn->data;
|
struct Curl_easy *data = conn->data;
|
||||||
@ -548,83 +579,52 @@ static CURLcode bearssl_connect_step3(struct connectdata *conn, int sockindex)
|
|||||||
static ssize_t bearssl_send(struct connectdata *conn, int sockindex,
|
static ssize_t bearssl_send(struct connectdata *conn, int sockindex,
|
||||||
const void *buf, size_t len, CURLcode *err)
|
const void *buf, size_t len, CURLcode *err)
|
||||||
{
|
{
|
||||||
|
struct Curl_easy *data = conn->data;
|
||||||
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
|
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
|
||||||
unsigned state;
|
unsigned char *app;
|
||||||
unsigned char *rec, *app;
|
size_t applen;
|
||||||
size_t reclen, applen;
|
|
||||||
ssize_t ret;
|
|
||||||
|
|
||||||
applen = 0;
|
|
||||||
for(;;) {
|
for(;;) {
|
||||||
state = br_ssl_engine_current_state(&BACKEND->ctx.eng);
|
*err = bearssl_run_until(conn, sockindex, BR_SSL_SENDAPP);
|
||||||
if(state & BR_SSL_SENDREC) {
|
if (*err != CURLE_OK)
|
||||||
rec = br_ssl_engine_sendrec_buf(&BACKEND->ctx.eng, &reclen);
|
return -1;
|
||||||
ret = swrite(conn->sock[sockindex], rec, reclen);
|
app = br_ssl_engine_sendapp_buf(&BACKEND->ctx.eng, &applen);
|
||||||
if(ret == -1) {
|
if(!app) {
|
||||||
if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK)
|
failf(data, "SSL: connection closed during write");
|
||||||
*err = CURLE_AGAIN;
|
|
||||||
else
|
|
||||||
*err = CURLE_SEND_ERROR;
|
*err = CURLE_SEND_ERROR;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
br_ssl_engine_sendrec_ack(&BACKEND->ctx.eng, ret);
|
if(BACKEND->pending_write) {
|
||||||
|
applen = BACKEND->pending_write;
|
||||||
|
BACKEND->pending_write = 0;
|
||||||
|
return applen;
|
||||||
}
|
}
|
||||||
else if(state & BR_SSL_SENDAPP && applen == 0) {
|
|
||||||
app = br_ssl_engine_sendapp_buf(&BACKEND->ctx.eng, &applen);
|
|
||||||
if(applen > len)
|
if(applen > len)
|
||||||
applen = len;
|
applen = len;
|
||||||
memcpy(app, buf, applen);
|
memcpy(app, buf, applen);
|
||||||
br_ssl_engine_sendapp_ack(&BACKEND->ctx.eng, applen);
|
br_ssl_engine_sendapp_ack(&BACKEND->ctx.eng, applen);
|
||||||
br_ssl_engine_flush(&BACKEND->ctx.eng, 0);
|
br_ssl_engine_flush(&BACKEND->ctx.eng, 0);
|
||||||
|
BACKEND->pending_write = applen;
|
||||||
}
|
}
|
||||||
else if(state & BR_SSL_CLOSED || applen == 0) {
|
|
||||||
*err = CURLE_SEND_ERROR;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return applen;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t bearssl_recv(struct connectdata *conn, int sockindex,
|
static ssize_t bearssl_recv(struct connectdata *conn, int sockindex,
|
||||||
char *buf, size_t len, CURLcode *err)
|
char *buf, size_t len, CURLcode *err)
|
||||||
{
|
{
|
||||||
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
|
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
|
||||||
unsigned state;
|
unsigned char *app;
|
||||||
unsigned char *rec, *app;
|
size_t applen;
|
||||||
size_t reclen, applen;
|
|
||||||
ssize_t ret;
|
|
||||||
|
|
||||||
for(;;) {
|
*err = bearssl_run_until(conn, sockindex, BR_SSL_RECVAPP);
|
||||||
state = br_ssl_engine_current_state(&BACKEND->ctx.eng);
|
if(*err != CURLE_OK)
|
||||||
if(state & BR_SSL_RECVREC) {
|
|
||||||
rec = br_ssl_engine_recvrec_buf(&BACKEND->ctx.eng, &reclen);
|
|
||||||
ret = sread(conn->sock[sockindex], rec, reclen);
|
|
||||||
if(ret == -1 && (SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK)) {
|
|
||||||
*err = CURLE_AGAIN;
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
|
||||||
if(ret <= 0) {
|
|
||||||
*err = CURLE_RECV_ERROR;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
br_ssl_engine_recvrec_ack(&BACKEND->ctx.eng, ret);
|
|
||||||
}
|
|
||||||
else if(state & BR_SSL_RECVAPP) {
|
|
||||||
app = br_ssl_engine_recvapp_buf(&BACKEND->ctx.eng, &applen);
|
app = br_ssl_engine_recvapp_buf(&BACKEND->ctx.eng, &applen);
|
||||||
|
if(!app)
|
||||||
|
return 0;
|
||||||
if(applen > len)
|
if(applen > len)
|
||||||
applen = len;
|
applen = len;
|
||||||
memcpy(buf, app, applen);
|
memcpy(buf, app, applen);
|
||||||
br_ssl_engine_recvapp_ack(&BACKEND->ctx.eng, applen);
|
br_ssl_engine_recvapp_ack(&BACKEND->ctx.eng, applen);
|
||||||
break;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
*err = CURLE_RECV_ERROR;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return applen;
|
return applen;
|
||||||
}
|
}
|
||||||
@ -792,19 +792,11 @@ static void *Curl_bearssl_get_internals(struct ssl_connect_data *connssl,
|
|||||||
static void Curl_bearssl_close(struct connectdata *conn, int sockindex)
|
static void Curl_bearssl_close(struct connectdata *conn, int sockindex)
|
||||||
{
|
{
|
||||||
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
|
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
|
||||||
unsigned char *buf;
|
size_t i;
|
||||||
size_t len, i;
|
|
||||||
ssize_t ret;
|
|
||||||
|
|
||||||
if(BACKEND->active) {
|
if(BACKEND->active) {
|
||||||
br_ssl_engine_close(&BACKEND->ctx.eng);
|
br_ssl_engine_close(&BACKEND->ctx.eng);
|
||||||
while(br_ssl_engine_current_state(&BACKEND->ctx.eng) & BR_SSL_SENDREC) {
|
(void)bearssl_run_until(conn, sockindex, BR_SSL_CLOSED);
|
||||||
buf = br_ssl_engine_sendrec_buf(&BACKEND->ctx.eng, &len);
|
|
||||||
ret = swrite(conn->sock[sockindex], buf, len);
|
|
||||||
if(ret < 0)
|
|
||||||
break;
|
|
||||||
br_ssl_engine_sendrec_ack(&BACKEND->ctx.eng, ret);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
for(i = 0; i < BACKEND->anchors_len; ++i)
|
for(i = 0; i < BACKEND->anchors_len; ++i)
|
||||||
free(BACKEND->anchors[i].dn.data);
|
free(BACKEND->anchors[i].dn.data);
|
||||||
|
Loading…
Reference in New Issue
Block a user