|
|
|
@ -57,8 +57,7 @@
|
|
|
|
|
#include "tls_wolfssl_mod.h"
|
|
|
|
|
#include "tls_server.h"
|
|
|
|
|
#include "tls_select.h"
|
|
|
|
|
#include "tls_bio.h"
|
|
|
|
|
#include "tls_dump_vf.h"
|
|
|
|
|
|
|
|
|
|
#include "tls_cfg.h"
|
|
|
|
|
|
|
|
|
|
int tls_run_event_routes(struct tcp_connection *c);
|
|
|
|
@ -225,6 +224,9 @@ static int tls_complete_init(struct tcp_connection *c)
|
|
|
|
|
str *sname = NULL;
|
|
|
|
|
str *srvid = NULL;
|
|
|
|
|
|
|
|
|
|
WOLFSSL_BIO *internal_bio, *rw_bio;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Get current TLS configuration and increase reference
|
|
|
|
|
* count immediately.
|
|
|
|
|
*/
|
|
|
|
@ -258,8 +260,7 @@ static int tls_complete_init(struct tcp_connection *c)
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
DBG("Using initial TLS domain %s (dom %p ctx %p sn [%s])\n",
|
|
|
|
|
tls_domain_str(dom), dom, dom->ctx[process_no],
|
|
|
|
|
ZSW(dom->server_name.s));
|
|
|
|
|
tls_domain_str(dom), dom, dom->ctx[0], ZSW(dom->server_name.s));
|
|
|
|
|
|
|
|
|
|
data = (struct tls_extra_data *)shm_malloc(sizeof(struct tls_extra_data));
|
|
|
|
|
if(!data) {
|
|
|
|
@ -267,8 +268,10 @@ static int tls_complete_init(struct tcp_connection *c)
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
memset(data, '\0', sizeof(struct tls_extra_data));
|
|
|
|
|
data->ssl = wolfSSL_new(dom->ctx[process_no]);
|
|
|
|
|
data->rwbio = tls_BIO_new_mbuf(0, 0);
|
|
|
|
|
data->ssl = wolfSSL_new(dom->ctx[0]);
|
|
|
|
|
wolfSSL_BIO_new_bio_pair(
|
|
|
|
|
&internal_bio, TLS_WR_MBUF_SZ, &rw_bio, TLS_RD_MBUF_SZ);
|
|
|
|
|
data->rwbio = rw_bio;
|
|
|
|
|
data->cfg = cfg;
|
|
|
|
|
data->state = state;
|
|
|
|
|
|
|
|
|
@ -277,7 +280,7 @@ static int tls_complete_init(struct tcp_connection *c)
|
|
|
|
|
if(data->ssl)
|
|
|
|
|
wolfSSL_free(data->ssl);
|
|
|
|
|
if(data->rwbio)
|
|
|
|
|
BIO_free(data->rwbio);
|
|
|
|
|
wolfSSL_BIO_free(data->rwbio);
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -287,14 +290,14 @@ static int tls_complete_init(struct tcp_connection *c)
|
|
|
|
|
if(data->ssl)
|
|
|
|
|
wolfSSL_free(data->ssl);
|
|
|
|
|
if(data->rwbio)
|
|
|
|
|
BIO_free(data->rwbio);
|
|
|
|
|
wolfSSL_BIO_free(data->rwbio);
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
LM_DBG("outbound TLS server name set to: %s\n", sname->s);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
wolfSSL_set_bio(data->ssl, data->rwbio, data->rwbio);
|
|
|
|
|
wolfSSL_set_bio(data->ssl, internal_bio, internal_bio);
|
|
|
|
|
c->extra_data = data;
|
|
|
|
|
|
|
|
|
|
/* link the extra data struct inside ssl connection*/
|
|
|
|
@ -351,33 +354,15 @@ static int tls_fix_connection(struct tcp_connection *c)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** sets an mbuf pair for the bio used by the tls connection.
|
|
|
|
|
* WARNING: must be called with c->write_lock held.
|
|
|
|
|
* @return 0 on success, -1 on error.
|
|
|
|
|
*/
|
|
|
|
|
static int tls_set_mbufs(
|
|
|
|
|
struct tcp_connection *c, struct tls_mbuf *rd, struct tls_mbuf *wr)
|
|
|
|
|
{
|
|
|
|
|
BIO *rwbio;
|
|
|
|
|
|
|
|
|
|
rwbio = ((struct tls_extra_data *)c->extra_data)->rwbio;
|
|
|
|
|
if(unlikely(tls_BIO_mbuf_set(rwbio, rd, wr) <= 0)) {
|
|
|
|
|
/* it should be always 1 */
|
|
|
|
|
ERR("failed to set mbufs");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void tls_dump_cert_info(char *s, X509 *cert)
|
|
|
|
|
static void tls_dump_cert_info(char *s, WOLFSSL_X509 *cert)
|
|
|
|
|
{
|
|
|
|
|
char *subj;
|
|
|
|
|
char *issuer;
|
|
|
|
|
|
|
|
|
|
subj = issuer = 0;
|
|
|
|
|
subj = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
|
|
|
|
|
issuer = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
|
|
|
|
|
subj = wolfSSL_X509_NAME_oneline(wolfSSL_X509_get_subject_name(cert), 0, 0);
|
|
|
|
|
issuer =
|
|
|
|
|
wolfSSL_X509_NAME_oneline(wolfSSL_X509_get_issuer_name(cert), 0, 0);
|
|
|
|
|
|
|
|
|
|
if(subj) {
|
|
|
|
|
LOG(cfg_get(tls, tls_cfg, log), "%s subject:%s\n", s ? s : "", subj);
|
|
|
|
@ -407,12 +392,12 @@ static void tls_dump_cert_info(char *s, X509 *cert)
|
|
|
|
|
int tls_accept(struct tcp_connection *c, int *error)
|
|
|
|
|
{
|
|
|
|
|
int ret;
|
|
|
|
|
SSL *ssl = NULL;
|
|
|
|
|
X509 *cert;
|
|
|
|
|
WOLFSSL *ssl = NULL;
|
|
|
|
|
WOLFSSL_X509 *cert;
|
|
|
|
|
struct tls_extra_data *tls_c;
|
|
|
|
|
int tls_log;
|
|
|
|
|
|
|
|
|
|
*error = SSL_ERROR_NONE;
|
|
|
|
|
*error = WOLFSSL_ERROR_NONE;
|
|
|
|
|
tls_c = (struct tls_extra_data *)c->extra_data;
|
|
|
|
|
ssl = tls_c->ssl;
|
|
|
|
|
|
|
|
|
@ -439,7 +424,7 @@ int tls_accept(struct tcp_connection *c, int *error)
|
|
|
|
|
"verification failed!!!\n");
|
|
|
|
|
tls_dump_verification_failure(wolfSSL_get_verify_result(ssl));
|
|
|
|
|
}
|
|
|
|
|
X509_free(cert);
|
|
|
|
|
wolfSSL_X509_free(cert);
|
|
|
|
|
} else {
|
|
|
|
|
LOG(tls_log, "tls_accept: client did not present a certificate\n");
|
|
|
|
|
}
|
|
|
|
@ -469,13 +454,13 @@ err:
|
|
|
|
|
*/
|
|
|
|
|
int tls_connect(struct tcp_connection *c, int *error)
|
|
|
|
|
{
|
|
|
|
|
SSL *ssl;
|
|
|
|
|
WOLFSSL *ssl;
|
|
|
|
|
int ret;
|
|
|
|
|
X509 *cert;
|
|
|
|
|
WOLFSSL_X509 *cert;
|
|
|
|
|
struct tls_extra_data *tls_c;
|
|
|
|
|
int tls_log;
|
|
|
|
|
|
|
|
|
|
*error = SSL_ERROR_NONE;
|
|
|
|
|
*error = WOLFSSL_ERROR_NONE;
|
|
|
|
|
tls_c = (struct tls_extra_data *)c->extra_data;
|
|
|
|
|
ssl = tls_c->ssl;
|
|
|
|
|
|
|
|
|
@ -502,7 +487,7 @@ int tls_connect(struct tcp_connection *c, int *error)
|
|
|
|
|
"verification failed!!!\n");
|
|
|
|
|
tls_dump_verification_failure(wolfSSL_get_verify_result(ssl));
|
|
|
|
|
}
|
|
|
|
|
X509_free(cert);
|
|
|
|
|
wolfSSL_X509_free(cert);
|
|
|
|
|
} else {
|
|
|
|
|
/* this should not happen, servers always present a cert */
|
|
|
|
|
LOG(tls_log, "tls_connect: server did not "
|
|
|
|
@ -526,7 +511,7 @@ static int tls_shutdown(struct tcp_connection *c)
|
|
|
|
|
{
|
|
|
|
|
int ret, err, ssl_err;
|
|
|
|
|
struct tls_extra_data *tls_c;
|
|
|
|
|
SSL *ssl;
|
|
|
|
|
WOLFSSL *ssl;
|
|
|
|
|
|
|
|
|
|
tls_c = (struct tls_extra_data *)c->extra_data;
|
|
|
|
|
if(unlikely(tls_c == 0 || tls_c->ssl == 0)) {
|
|
|
|
@ -549,31 +534,31 @@ static int tls_shutdown(struct tcp_connection *c)
|
|
|
|
|
} else {
|
|
|
|
|
err = wolfSSL_get_error(ssl, ret);
|
|
|
|
|
switch(err) {
|
|
|
|
|
case SSL_ERROR_ZERO_RETURN:
|
|
|
|
|
case WOLFSSL_ERROR_ZERO_RETURN:
|
|
|
|
|
DBG("TLS shutdown failed cleanly\n");
|
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
|
|
case SSL_ERROR_WANT_READ:
|
|
|
|
|
case WOLFSSL_ERROR_WANT_READ:
|
|
|
|
|
DBG("Need to get more data to finish TLS shutdown\n");
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SSL_ERROR_WANT_WRITE:
|
|
|
|
|
case WOLFSSL_ERROR_WANT_WRITE:
|
|
|
|
|
DBG("Need to send more data to finish TLS shutdown\n");
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SSL_ERROR_WANT_CONNECT:
|
|
|
|
|
case WOLFSSL_ERROR_WANT_CONNECT:
|
|
|
|
|
DBG("Need to retry connect\n");
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SSL_ERROR_WANT_ACCEPT:
|
|
|
|
|
case WOLFSSL_ERROR_WANT_ACCEPT:
|
|
|
|
|
DBG("Need to retry accept\n");
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SSL_ERROR_WANT_X509_LOOKUP:
|
|
|
|
|
case WOLFSSL_ERROR_WANT_X509_LOOKUP:
|
|
|
|
|
DBG("Application callback asked to be called again\n");
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SSL_ERROR_SYSCALL:
|
|
|
|
|
case WOLFSSL_ERROR_SYSCALL:
|
|
|
|
|
TLS_ERR_RET(ssl_err, "TLS shutdown");
|
|
|
|
|
if(!ssl_err) {
|
|
|
|
|
if(ret == 0) {
|
|
|
|
@ -585,7 +570,7 @@ static int tls_shutdown(struct tcp_connection *c)
|
|
|
|
|
}
|
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
|
|
case SSL_ERROR_SSL:
|
|
|
|
|
case WOLFSSL_ERROR_SSL:
|
|
|
|
|
default:
|
|
|
|
|
TLS_ERR("SSL error:");
|
|
|
|
|
goto err;
|
|
|
|
@ -627,8 +612,8 @@ void tls_h_tcpconn_clean_f(struct tcp_connection *c)
|
|
|
|
|
{
|
|
|
|
|
struct tls_extra_data *extra;
|
|
|
|
|
/*
|
|
|
|
|
* runs within global tcp lock
|
|
|
|
|
*/
|
|
|
|
|
* runs within global tcp lock
|
|
|
|
|
*/
|
|
|
|
|
if((c->type != PROTO_TLS) && (c->type != PROTO_WSS)) {
|
|
|
|
|
BUG("Bad connection structure\n");
|
|
|
|
|
abort();
|
|
|
|
@ -636,13 +621,10 @@ void tls_h_tcpconn_clean_f(struct tcp_connection *c)
|
|
|
|
|
if(c->extra_data) {
|
|
|
|
|
extra = (struct tls_extra_data *)c->extra_data;
|
|
|
|
|
wolfSSL_free(extra->ssl);
|
|
|
|
|
wolfSSL_BIO_free_all(extra->rwbio);
|
|
|
|
|
atomic_dec(&extra->cfg->ref_count);
|
|
|
|
|
if(extra->ct_wq)
|
|
|
|
|
tls_ct_wq_free(&extra->ct_wq);
|
|
|
|
|
if(extra->enc_rd_buf) {
|
|
|
|
|
shm_free(extra->enc_rd_buf);
|
|
|
|
|
extra->enc_rd_buf = 0;
|
|
|
|
|
}
|
|
|
|
|
shm_free(c->extra_data);
|
|
|
|
|
c->extra_data = 0;
|
|
|
|
|
}
|
|
|
|
@ -651,10 +633,20 @@ void tls_h_tcpconn_clean_f(struct tcp_connection *c)
|
|
|
|
|
|
|
|
|
|
/** perform one-way shutdown, do not wait for notify from the remote peer.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* wolfSSL implementation detail: BIO pair uses a ring buffer -
|
|
|
|
|
* wolfSSL_BIO_read does not do wrap-around; you may need a 2-pass read
|
|
|
|
|
* 1st read - front of buffer, 2nd read - wrap-around
|
|
|
|
|
* fixed in 4f1d777090 post-v5.6.6-stable
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void tls_h_tcpconn_close_f(struct tcp_connection *c, int fd)
|
|
|
|
|
{
|
|
|
|
|
unsigned char wr_buf[TLS_WR_MBUF_SZ];
|
|
|
|
|
struct tls_mbuf rd, wr;
|
|
|
|
|
WOLFSSL_BIO *rwbio;
|
|
|
|
|
size_t wr_used, nr, npos;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* runs either within global tcp lock or after the connection has
|
|
|
|
@ -671,19 +663,27 @@ void tls_h_tcpconn_close_f(struct tcp_connection *c, int fd)
|
|
|
|
|
lock_release(&c->write_lock);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
tls_mbuf_init(&rd, 0, 0); /* no read */
|
|
|
|
|
tls_mbuf_init(&wr, wr_buf, sizeof(wr_buf));
|
|
|
|
|
if(tls_set_mbufs(c, &rd, &wr) == 0) {
|
|
|
|
|
tls_shutdown(c); /* shutdown only on successful set fd */
|
|
|
|
|
/* write as much as possible and update wr.
|
|
|
|
|
* Since this is a close, we don't want to queue the write
|
|
|
|
|
* (if it can't write immediately, just fail silently)
|
|
|
|
|
*/
|
|
|
|
|
if(wr.used)
|
|
|
|
|
_tcpconn_write_nb(fd, c, (char *)wr.buf, wr.used);
|
|
|
|
|
/* we don't bother reading anything (we don't want to wait
|
|
|
|
|
on close) */
|
|
|
|
|
rwbio = ((struct tls_extra_data *)c->extra_data)->rwbio;
|
|
|
|
|
tls_shutdown(c); /* shutdown only on successful set fd */
|
|
|
|
|
/* write as much as possible and update wr.
|
|
|
|
|
* Since this is a close, we don't want to queue the write
|
|
|
|
|
* (if it can't write immediately, just fail silently)
|
|
|
|
|
*/
|
|
|
|
|
/* use 2-pass read for wolfSSL ring buffer */
|
|
|
|
|
wr_used = wolfSSL_BIO_pending(rwbio);
|
|
|
|
|
if(wr_used) {
|
|
|
|
|
for(nr = 0; nr < wr_used;) {
|
|
|
|
|
npos = wolfSSL_BIO_read(rwbio, wr_buf + nr, wr_used - nr);
|
|
|
|
|
if(npos <= 0)
|
|
|
|
|
break;
|
|
|
|
|
nr += npos;
|
|
|
|
|
}
|
|
|
|
|
assert(nr == wr_used);
|
|
|
|
|
_tcpconn_write_nb(fd, c, (char *)wr_buf, wr_used);
|
|
|
|
|
}
|
|
|
|
|
/* we don't bother reading anything (we don't want to wait
|
|
|
|
|
on close) */
|
|
|
|
|
|
|
|
|
|
lock_release(&c->write_lock);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -724,10 +724,12 @@ int tls_h_encode_f(struct tcp_connection *c, const char **pbuf,
|
|
|
|
|
snd_flags_t *send_flags)
|
|
|
|
|
{
|
|
|
|
|
int n, offs;
|
|
|
|
|
SSL *ssl = NULL;
|
|
|
|
|
WOLFSSL *ssl = NULL;
|
|
|
|
|
WOLFSSL_BIO *rwbio;
|
|
|
|
|
struct tls_extra_data *tls_c;
|
|
|
|
|
static unsigned char wr_buf[TLS_WR_MBUF_SZ];
|
|
|
|
|
struct tls_mbuf rd, wr;
|
|
|
|
|
size_t wr_used = 0, nr, npos;
|
|
|
|
|
|
|
|
|
|
int ssl_error;
|
|
|
|
|
char *err_src;
|
|
|
|
|
char ip_buf[64];
|
|
|
|
@ -744,7 +746,7 @@ int tls_h_encode_f(struct tcp_connection *c, const char **pbuf,
|
|
|
|
|
su2a(&c->rcv.src_su, sizeof(c->rcv.src_su)));
|
|
|
|
|
n = 0;
|
|
|
|
|
offs = 0;
|
|
|
|
|
ssl_error = SSL_ERROR_NONE;
|
|
|
|
|
ssl_error = WOLFSSL_ERROR_NONE;
|
|
|
|
|
err_src = "TLS write:";
|
|
|
|
|
if(unlikely(tls_fix_connection_unsafe(c) < 0)) {
|
|
|
|
|
/* c->extra_data might be null => exit immediately */
|
|
|
|
@ -755,8 +757,7 @@ int tls_h_encode_f(struct tcp_connection *c, const char **pbuf,
|
|
|
|
|
}
|
|
|
|
|
tls_c = (struct tls_extra_data *)c->extra_data;
|
|
|
|
|
ssl = tls_c->ssl;
|
|
|
|
|
tls_mbuf_init(&rd, 0, 0); /* no read */
|
|
|
|
|
tls_mbuf_init(&wr, wr_buf, sizeof(wr_buf));
|
|
|
|
|
rwbio = tls_c->rwbio;
|
|
|
|
|
/* clear text already queued (WANTS_READ) queue directly*/
|
|
|
|
|
if(unlikely(tls_write_wants_read(tls_c))) {
|
|
|
|
|
TLS_WR_TRACE("(%p) WANTS_READ queue present => queueing"
|
|
|
|
@ -773,10 +774,7 @@ int tls_h_encode_f(struct tcp_connection *c, const char **pbuf,
|
|
|
|
|
send_flags->f &= ~SND_F_CON_CLOSE;
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
if(unlikely(tls_set_mbufs(c, &rd, &wr) < 0)) {
|
|
|
|
|
ERR("tls_set_mbufs failed\n");
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
redo_wr:
|
|
|
|
|
if(unlikely(tls_c->state == S_TLS_CONNECTING)) {
|
|
|
|
|
n = tls_connect(c, &ssl_error);
|
|
|
|
@ -787,7 +785,7 @@ redo_wr:
|
|
|
|
|
ssl_error = wolfSSL_get_error(ssl, n);
|
|
|
|
|
} else {
|
|
|
|
|
/* tls_connect failed/needs more IO */
|
|
|
|
|
if(unlikely(n < 0 && ssl_error == SSL_ERROR_NONE))
|
|
|
|
|
if(unlikely(n < 0 && ssl_error == WOLFSSL_ERROR_NONE))
|
|
|
|
|
goto error;
|
|
|
|
|
err_src = "TLS connect:";
|
|
|
|
|
}
|
|
|
|
@ -800,29 +798,31 @@ redo_wr:
|
|
|
|
|
ssl_error = wolfSSL_get_error(ssl, n);
|
|
|
|
|
} else {
|
|
|
|
|
/* tls_accept failed/needs more IO */
|
|
|
|
|
if(unlikely(n < 0 && ssl_error == SSL_ERROR_NONE))
|
|
|
|
|
if(unlikely(n < 0 && ssl_error == WOLFSSL_ERROR_NONE))
|
|
|
|
|
goto error;
|
|
|
|
|
err_src = "TLS accept:";
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
n = SSL_write(ssl, buf + offs, len - offs);
|
|
|
|
|
n = wolfSSL_write(ssl, buf + offs, len - offs);
|
|
|
|
|
if(unlikely(n <= 0))
|
|
|
|
|
ssl_error = SSL_get_error(ssl, n);
|
|
|
|
|
ssl_error = wolfSSL_get_error(ssl, n);
|
|
|
|
|
}
|
|
|
|
|
TLS_WR_TRACE("(%p) SSL_write(%p + %d, %d) => %d (err=%d)\n", c, buf, offs,
|
|
|
|
|
len - offs, n, ssl_error);
|
|
|
|
|
TLS_WR_TRACE("(%p) wolfSSL_write(%p + %d, %d) => %d (err=%d)\n", c, buf,
|
|
|
|
|
offs, len - offs, n, ssl_error);
|
|
|
|
|
/* check for possible ssl errors */
|
|
|
|
|
wr_used = wolfSSL_BIO_pending(rwbio);
|
|
|
|
|
|
|
|
|
|
if(unlikely(n <= 0)) {
|
|
|
|
|
switch(ssl_error) {
|
|
|
|
|
case SSL_ERROR_NONE:
|
|
|
|
|
case WOLFSSL_ERROR_NONE:
|
|
|
|
|
BUG("unexpected SSL_ERROR_NONE for n=%d\n", n);
|
|
|
|
|
goto error;
|
|
|
|
|
break;
|
|
|
|
|
case SSL_ERROR_ZERO_RETURN:
|
|
|
|
|
case WOLFSSL_ERROR_ZERO_RETURN:
|
|
|
|
|
/* SSL EOF */
|
|
|
|
|
ERR("ssl level EOF\n");
|
|
|
|
|
goto ssl_eof;
|
|
|
|
|
case SSL_ERROR_WANT_READ:
|
|
|
|
|
case WOLFSSL_ERROR_WANT_READ:
|
|
|
|
|
/* queue write buffer */
|
|
|
|
|
TLS_WR_TRACE("(%p) SSL_ERROR_WANT_READ => queueing for read"
|
|
|
|
|
" (%p + %d, %d)\n",
|
|
|
|
@ -835,24 +835,24 @@ redo_wr:
|
|
|
|
|
}
|
|
|
|
|
tls_c->flags |= F_TLS_CON_WR_WANTS_RD;
|
|
|
|
|
/* buffer queued for a future send attempt, after first
|
|
|
|
|
* reading some data (key exchange) => don't allow immediate
|
|
|
|
|
* closing of the connection */
|
|
|
|
|
* reading some data (key exchange) => don't allow immediate
|
|
|
|
|
* closing of the connection */
|
|
|
|
|
send_flags->f &= ~SND_F_CON_CLOSE;
|
|
|
|
|
break; /* or goto end */
|
|
|
|
|
case SSL_ERROR_WANT_WRITE:
|
|
|
|
|
case WOLFSSL_ERROR_WANT_WRITE:
|
|
|
|
|
if(unlikely(offs == 0)) {
|
|
|
|
|
/* error, no record fits in the buffer or
|
|
|
|
|
* no partial write enabled and buffer to small to fit
|
|
|
|
|
* all the records */
|
|
|
|
|
BUG("write buffer too small (%d/%d bytes)\n", wr.used,
|
|
|
|
|
wr.size);
|
|
|
|
|
* no partial write enabled and buffer to small to fit
|
|
|
|
|
* all the records */
|
|
|
|
|
BUG("write buffer too small (%d/%d bytes)\n", (int)wr_used,
|
|
|
|
|
TLS_WR_MBUF_SZ);
|
|
|
|
|
goto bug;
|
|
|
|
|
} else {
|
|
|
|
|
/* offs != 0 => something was "written" */
|
|
|
|
|
*rest_buf = buf + offs;
|
|
|
|
|
*rest_len = len - offs;
|
|
|
|
|
/* this function should be called again => disallow
|
|
|
|
|
* immediate closing of the connection */
|
|
|
|
|
* immediate closing of the connection */
|
|
|
|
|
send_flags->f &= ~SND_F_CON_CLOSE;
|
|
|
|
|
TLS_WR_TRACE("(%p) SSL_ERROR_WANT_WRITE partial write"
|
|
|
|
|
" (written %p , %d, rest_buf=%p"
|
|
|
|
@ -860,7 +860,7 @@ redo_wr:
|
|
|
|
|
c, buf, offs, *rest_buf, *rest_len);
|
|
|
|
|
}
|
|
|
|
|
break; /* or goto end */
|
|
|
|
|
case SSL_ERROR_SSL:
|
|
|
|
|
case WOLFSSL_ERROR_SSL:
|
|
|
|
|
/* protocol level error */
|
|
|
|
|
ERR("protocol level error\n");
|
|
|
|
|
TLS_ERR_SSL(err_src, ssl);
|
|
|
|
@ -873,27 +873,27 @@ redo_wr:
|
|
|
|
|
|
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
|
|
case SSL_ERROR_WANT_CONNECT:
|
|
|
|
|
case WOLFSSL_ERROR_WANT_CONNECT:
|
|
|
|
|
/* only if the underlying BIO is not yet connected
|
|
|
|
|
* and the call would block in connect().
|
|
|
|
|
* (not possible in our case) */
|
|
|
|
|
* and the call would block in connect().
|
|
|
|
|
* (not possible in our case) */
|
|
|
|
|
BUG("unexpected SSL_ERROR_WANT_CONNECT\n");
|
|
|
|
|
break;
|
|
|
|
|
case SSL_ERROR_WANT_ACCEPT:
|
|
|
|
|
case WOLFSSL_ERROR_WANT_ACCEPT:
|
|
|
|
|
/* only if the underlying BIO is not yet connected
|
|
|
|
|
* and call would block in accept()
|
|
|
|
|
* (not possible in our case) */
|
|
|
|
|
* and call would block in accept()
|
|
|
|
|
* (not possible in our case) */
|
|
|
|
|
BUG("unexpected SSL_ERROR_WANT_ACCEPT\n");
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SSL_ERROR_WANT_X509_LOOKUP:
|
|
|
|
|
case WOLFSSL_ERROR_WANT_X509_LOOKUP:
|
|
|
|
|
/* can only appear on client application and it indicates that
|
|
|
|
|
* an installed client cert. callback should be called again
|
|
|
|
|
* (it returned < 0 indicated that it wants to be called
|
|
|
|
|
* later). Not possible in our case */
|
|
|
|
|
* an installed client cert. callback should be called again
|
|
|
|
|
* (it returned < 0 indicated that it wants to be called
|
|
|
|
|
* later). Not possible in our case */
|
|
|
|
|
BUG("unsupported SSL_ERROR_WANT_X509_LOOKUP");
|
|
|
|
|
goto bug;
|
|
|
|
|
case SSL_ERROR_SYSCALL:
|
|
|
|
|
case WOLFSSL_ERROR_SYSCALL:
|
|
|
|
|
TLS_ERR_RET(x, err_src);
|
|
|
|
|
if(!x) {
|
|
|
|
|
if(n == 0) {
|
|
|
|
@ -916,26 +916,42 @@ redo_wr:
|
|
|
|
|
offs += n;
|
|
|
|
|
goto redo_wr;
|
|
|
|
|
}
|
|
|
|
|
tls_set_mbufs(c, 0, 0);
|
|
|
|
|
end:
|
|
|
|
|
*pbuf = (const char *)wr.buf;
|
|
|
|
|
*plen = wr.used;
|
|
|
|
|
/* use 2-pass read for wolfSSL ring buffer */
|
|
|
|
|
wr_used = wolfSSL_BIO_pending(rwbio);
|
|
|
|
|
for(nr = 0; nr < wr_used;) {
|
|
|
|
|
npos = wolfSSL_BIO_read(rwbio, wr_buf + nr, wr_used - nr);
|
|
|
|
|
if(npos <= 0)
|
|
|
|
|
break;
|
|
|
|
|
nr += npos;
|
|
|
|
|
}
|
|
|
|
|
assert(nr == wr_used);
|
|
|
|
|
*plen = wr_used;
|
|
|
|
|
*pbuf = (const char *)wr_buf;
|
|
|
|
|
TLS_WR_TRACE("(%p) end (offs %d, rest_buf=%p rest_len=%d 0x%0x) => %d \n",
|
|
|
|
|
c, offs, *rest_buf, *rest_len, send_flags->f, *plen);
|
|
|
|
|
return *plen;
|
|
|
|
|
error:
|
|
|
|
|
/*error_send:*/
|
|
|
|
|
/*error_send:*/
|
|
|
|
|
error_wq_full:
|
|
|
|
|
bug:
|
|
|
|
|
tls_set_mbufs(c, 0, 0);
|
|
|
|
|
TLS_WR_TRACE(
|
|
|
|
|
"(%p) end error (offs %d, %d encoded) => -1\n", c, offs, wr.used);
|
|
|
|
|
"(%p) end error (offs %d, %d encoded) => -1\n", c, offs, wr_used);
|
|
|
|
|
return -1;
|
|
|
|
|
ssl_eof:
|
|
|
|
|
c->state = S_CONN_EOF;
|
|
|
|
|
c->flags |= F_CONN_FORCE_EOF;
|
|
|
|
|
*pbuf = (const char *)wr.buf;
|
|
|
|
|
*plen = wr.used;
|
|
|
|
|
/* use 2-pass read for wolfSSL ring buffer */
|
|
|
|
|
wr_used = wolfSSL_BIO_pending(rwbio);
|
|
|
|
|
for(nr = 0; nr < wr_used;) {
|
|
|
|
|
npos = wolfSSL_BIO_read(rwbio, wr_buf + nr, wr_used - nr);
|
|
|
|
|
if(npos <= 0)
|
|
|
|
|
break;
|
|
|
|
|
nr += npos;
|
|
|
|
|
}
|
|
|
|
|
assert(nr == wr_used);
|
|
|
|
|
*plen = wr_used;
|
|
|
|
|
*pbuf = (const char *)wr_buf;
|
|
|
|
|
DBG("TLS connection has been closed\n");
|
|
|
|
|
TLS_WR_TRACE("(%p) end EOF (offs %d) => (%d\n", c, offs, *plen);
|
|
|
|
|
return *plen;
|
|
|
|
@ -973,12 +989,13 @@ int tls_h_read_f(struct tcp_connection *c, rd_conn_flags_t *flags)
|
|
|
|
|
{
|
|
|
|
|
struct tcp_req *r;
|
|
|
|
|
int bytes_free, bytes_read, read_size, ssl_error, ssl_read;
|
|
|
|
|
SSL *ssl;
|
|
|
|
|
WOLFSSL *ssl;
|
|
|
|
|
WOLFSSL_BIO *rwbio;
|
|
|
|
|
unsigned char rd_buf[TLS_RD_MBUF_SZ];
|
|
|
|
|
unsigned char wr_buf[TLS_WR_MBUF_SZ];
|
|
|
|
|
struct tls_mbuf rd, wr;
|
|
|
|
|
size_t wr_used, rd_unused;
|
|
|
|
|
size_t nr, npos, nw;
|
|
|
|
|
struct tls_extra_data *tls_c;
|
|
|
|
|
struct tls_rd_buf *enc_rd_buf;
|
|
|
|
|
int n, flush_flags;
|
|
|
|
|
char *err_src;
|
|
|
|
|
char ip_buf[64];
|
|
|
|
@ -991,7 +1008,7 @@ int tls_h_read_f(struct tcp_connection *c, rd_conn_flags_t *flags)
|
|
|
|
|
ip_addr2a(&c->rcv.dst_ip), c->rcv.dst_port);
|
|
|
|
|
ssl_read = 0;
|
|
|
|
|
r = &c->req;
|
|
|
|
|
enc_rd_buf = 0;
|
|
|
|
|
|
|
|
|
|
*flags &= ~RD_CONN_REPEAT_READ;
|
|
|
|
|
if(unlikely(tls_fix_connection(c) < 0)) {
|
|
|
|
|
TLS_RD_TRACE("(%p, %p) end: tls_fix_connection failed =>"
|
|
|
|
@ -1003,6 +1020,7 @@ int tls_h_read_f(struct tcp_connection *c, rd_conn_flags_t *flags)
|
|
|
|
|
* If it's != 0 is changed only on destroy. It's not possible to have
|
|
|
|
|
* parallel reads.*/
|
|
|
|
|
tls_c = c->extra_data;
|
|
|
|
|
rwbio = tls_c->rwbio;
|
|
|
|
|
bytes_free = c->req.b_size - (unsigned int)(r->pos - r->buf);
|
|
|
|
|
if(unlikely(bytes_free <= 0)) {
|
|
|
|
|
ERR("Buffer overrun, dropping\n");
|
|
|
|
@ -1010,53 +1028,35 @@ int tls_h_read_f(struct tcp_connection *c, rd_conn_flags_t *flags)
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
redo_read:
|
|
|
|
|
/* if data queued from a previous read(), use it (don't perform
|
|
|
|
|
* a real read()).
|
|
|
|
|
*/
|
|
|
|
|
if(unlikely(tls_c->enc_rd_buf)) {
|
|
|
|
|
/* use queued data */
|
|
|
|
|
/* safe to use without locks, because only read changes it and
|
|
|
|
|
* there can't be parallel reads on the same connection */
|
|
|
|
|
enc_rd_buf = tls_c->enc_rd_buf;
|
|
|
|
|
tls_c->enc_rd_buf = 0;
|
|
|
|
|
TLS_RD_TRACE("(%p, %p) using queued data (%p: %p %d bytes)\n", c, flags,
|
|
|
|
|
enc_rd_buf, enc_rd_buf->buf + enc_rd_buf->pos,
|
|
|
|
|
enc_rd_buf->size - enc_rd_buf->pos);
|
|
|
|
|
tls_mbuf_init(&rd, enc_rd_buf->buf + enc_rd_buf->pos,
|
|
|
|
|
enc_rd_buf->size - enc_rd_buf->pos);
|
|
|
|
|
rd.used = enc_rd_buf->size - enc_rd_buf->pos;
|
|
|
|
|
} else {
|
|
|
|
|
/* if we were using using queued data before, free & reset the
|
|
|
|
|
the queued read data before performing the real read() */
|
|
|
|
|
if(unlikely(enc_rd_buf)) {
|
|
|
|
|
TLS_RD_TRACE("(%p, %p) reset prev. used enc_rd_buf (%p)\n", c,
|
|
|
|
|
flags, enc_rd_buf);
|
|
|
|
|
shm_free(enc_rd_buf);
|
|
|
|
|
enc_rd_buf = 0;
|
|
|
|
|
/* real read() */
|
|
|
|
|
/* read() only if no previously detected EOF, or previous
|
|
|
|
|
* short read (which means the socket buffer was emptied) */
|
|
|
|
|
if(likely(!(*flags & (RD_CONN_EOF | RD_CONN_SHORT_READ)))) {
|
|
|
|
|
/* don't read more than the free bytes in the tcp req buffer */
|
|
|
|
|
read_size = MIN_unsigned(TLS_RD_MBUF_SZ, bytes_free);
|
|
|
|
|
bytes_read = tcp_read_data(c->fd, c, (char *)rd_buf, read_size, flags);
|
|
|
|
|
TLS_RD_TRACE("(%p, %p) tcp_read_data(..., %d, *%d) => %d bytes\n", c,
|
|
|
|
|
flags, read_size, *flags, bytes_read);
|
|
|
|
|
/* try SSL_read even on 0 bytes read, it might have
|
|
|
|
|
* internally buffered data */
|
|
|
|
|
if(unlikely(bytes_read < 0)) {
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
/* real read() */
|
|
|
|
|
tls_mbuf_init(&rd, rd_buf, sizeof(rd_buf));
|
|
|
|
|
/* read() only if no previously detected EOF, or previous
|
|
|
|
|
* short read (which means the socket buffer was emptied) */
|
|
|
|
|
if(likely(!(*flags & (RD_CONN_EOF | RD_CONN_SHORT_READ)))) {
|
|
|
|
|
/* don't read more than the free bytes in the tcp req buffer */
|
|
|
|
|
read_size = MIN_unsigned(rd.size, bytes_free);
|
|
|
|
|
bytes_read =
|
|
|
|
|
tcp_read_data(c->fd, c, (char *)rd.buf, read_size, flags);
|
|
|
|
|
TLS_RD_TRACE("(%p, %p) tcp_read_data(..., %d, *%d) => %d bytes\n",
|
|
|
|
|
c, flags, read_size, *flags, bytes_read);
|
|
|
|
|
/* try SSL_read even on 0 bytes read, it might have
|
|
|
|
|
* internally buffered data */
|
|
|
|
|
if(unlikely(bytes_read < 0)) {
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
rd.used = bytes_read;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* use 2-pass write for wolfSSL ring buffer
|
|
|
|
|
* fixed in 4f1d777090, post-v5.6.6-stable
|
|
|
|
|
*/
|
|
|
|
|
for(nw = 0; nw < bytes_read;) {
|
|
|
|
|
npos = wolfSSL_BIO_write(rwbio, rd_buf + nw, bytes_read - nw);
|
|
|
|
|
if(npos <= 0)
|
|
|
|
|
break;
|
|
|
|
|
nw += npos;
|
|
|
|
|
}
|
|
|
|
|
assert(nw == bytes_read);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
continue_ssl_read:
|
|
|
|
|
tls_mbuf_init(&wr, wr_buf, sizeof(wr_buf));
|
|
|
|
|
ssl_error = SSL_ERROR_NONE;
|
|
|
|
|
ssl_error = WOLFSSL_ERROR_NONE;
|
|
|
|
|
err_src = "TLS read:";
|
|
|
|
|
/* we have to avoid to run in the same time
|
|
|
|
|
* with a tls_write because of the
|
|
|
|
@ -1064,7 +1064,6 @@ continue_ssl_read:
|
|
|
|
|
* stealing the wbio or rbio under us or vice versa)
|
|
|
|
|
* => lock on con->write_lock (ugly hack) */
|
|
|
|
|
lock_get(&c->write_lock);
|
|
|
|
|
tls_set_mbufs(c, &rd, &wr);
|
|
|
|
|
ssl = tls_c->ssl;
|
|
|
|
|
n = 0;
|
|
|
|
|
if(unlikely(tls_write_wants_read(tls_c) && !(*flags & RD_CONN_EOF))) {
|
|
|
|
@ -1073,7 +1072,6 @@ continue_ssl_read:
|
|
|
|
|
" ct_wq_flush()=> %d (ff=%d ssl_error=%d))\n",
|
|
|
|
|
c, flags, n, flush_flags, ssl_error);
|
|
|
|
|
if(unlikely(n < 0)) {
|
|
|
|
|
tls_set_mbufs(c, 0, 0);
|
|
|
|
|
lock_release(&c->write_lock);
|
|
|
|
|
ERR("write flush error (%d)\n", n);
|
|
|
|
|
goto error;
|
|
|
|
@ -1083,7 +1081,7 @@ continue_ssl_read:
|
|
|
|
|
if(unlikely(flush_flags & F_BUFQ_ERROR_FLUSH))
|
|
|
|
|
err_src = "TLS write:";
|
|
|
|
|
}
|
|
|
|
|
if(likely(ssl_error == SSL_ERROR_NONE)) {
|
|
|
|
|
if(likely(ssl_error == WOLFSSL_ERROR_NONE)) {
|
|
|
|
|
if(unlikely(tls_c->state == S_TLS_CONNECTING)) {
|
|
|
|
|
n = tls_connect(c, &ssl_error);
|
|
|
|
|
TLS_RD_TRACE("(%p, %p) tls_connect() => %d (err=%d)\n", c, flags, n,
|
|
|
|
@ -1092,7 +1090,7 @@ continue_ssl_read:
|
|
|
|
|
n = wolfSSL_read(ssl, r->pos, bytes_free);
|
|
|
|
|
} else {
|
|
|
|
|
/* tls_connect failed/needs more IO */
|
|
|
|
|
if(unlikely(n < 0 && ssl_error == SSL_ERROR_NONE)) {
|
|
|
|
|
if(unlikely(n < 0 && ssl_error == WOLFSSL_ERROR_NONE)) {
|
|
|
|
|
lock_release(&c->write_lock);
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
@ -1107,7 +1105,7 @@ continue_ssl_read:
|
|
|
|
|
n = wolfSSL_read(ssl, r->pos, bytes_free);
|
|
|
|
|
} else {
|
|
|
|
|
/* tls_accept failed/needs more IO */
|
|
|
|
|
if(unlikely(n < 0 && ssl_error == SSL_ERROR_NONE)) {
|
|
|
|
|
if(unlikely(n < 0 && ssl_error == WOLFSSL_ERROR_NONE)) {
|
|
|
|
|
lock_release(&c->write_lock);
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
@ -1116,74 +1114,74 @@ continue_ssl_read:
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
/* if bytes in then decrypt read buffer into tcpconn req.
|
|
|
|
|
* buffer */
|
|
|
|
|
* buffer */
|
|
|
|
|
n = wolfSSL_read(ssl, r->pos, bytes_free);
|
|
|
|
|
}
|
|
|
|
|
/** handle SSL_read() return.
|
|
|
|
|
* There are 3 main cases, each with several sub-cases, depending
|
|
|
|
|
* on whether or not the output buffer was filled, if there
|
|
|
|
|
* is still unconsumed input data in the input buffer (rd)
|
|
|
|
|
* and if there is "cached" data in the internal openssl
|
|
|
|
|
* buffers.
|
|
|
|
|
* 0. error (n<=0):
|
|
|
|
|
* SSL_ERROR_WANT_READ - input data fully
|
|
|
|
|
* consumed, no more returnable cached data inside openssl
|
|
|
|
|
* => exit.
|
|
|
|
|
* SSL_ERROR_WANT_WRITE - should never happen (the write
|
|
|
|
|
* buffer is big enough to handle any re-negociation).
|
|
|
|
|
* SSL_ERROR_ZERO_RETURN - ssl level shutdown => exit.
|
|
|
|
|
* other errors are unexpected.
|
|
|
|
|
* 1. output buffer filled (n == bytes_free):
|
|
|
|
|
* 1i. - still unconsumed input, nothing buffered by openssl
|
|
|
|
|
* 1ip. - unconsumed input + buffered data by openssl (pending
|
|
|
|
|
* on the next SSL_read).
|
|
|
|
|
* 1p. - completely consumed input, buffered data internally
|
|
|
|
|
* by openssl (pending).
|
|
|
|
|
* Likely to happen, about the only case when
|
|
|
|
|
* SSL_pending() could be used (but only if readahead=0).
|
|
|
|
|
* 1f. - consumed input, no buffered data.
|
|
|
|
|
* 2. output buffer not fully filled (n < bytes_free):
|
|
|
|
|
* 2i. - still unconsumed input, nothing buffered by openssl.
|
|
|
|
|
* This can appear if SSL readahead is 0 (SSL_read()
|
|
|
|
|
* tries to get only 1 record from the input).
|
|
|
|
|
* 2ip. - unconsumed input and buffered data by openssl.
|
|
|
|
|
* Unlikely to happen (e.g. readahead is 1, more
|
|
|
|
|
* records are buffered internally by openssl, but
|
|
|
|
|
* there was not enough space for buffering the whole
|
|
|
|
|
* input).
|
|
|
|
|
* 2p - consumed input, but buffered data by openssl.
|
|
|
|
|
* It happens especially when readahead is 1.
|
|
|
|
|
* 2f. - consumed input, no buffered data.
|
|
|
|
|
*
|
|
|
|
|
* One should repeat SSL_read() until and error is detected
|
|
|
|
|
* (0*) or the input and internal ssl buffers are fully consumed
|
|
|
|
|
* (1f or 2f). However in general is not possible to see if
|
|
|
|
|
* SSL_read() could return more data. SSL_pending() has very
|
|
|
|
|
* limited usability (basically it would return !=0 only if there
|
|
|
|
|
* was no enough space in the output buffer and only if this did
|
|
|
|
|
* not happen at a record boundary).
|
|
|
|
|
* The solution is to repeat SSL_read() until error or until
|
|
|
|
|
* the output buffer is filled (0* or 1*).
|
|
|
|
|
* In the later case, this whole function should be called again
|
|
|
|
|
* once there is more output space (set RD_CONN_REPEAT_READ).
|
|
|
|
|
*/
|
|
|
|
|
* There are 3 main cases, each with several sub-cases, depending
|
|
|
|
|
* on whether or not the output buffer was filled, if there
|
|
|
|
|
* is still unconsumed input data in the input buffer (rd)
|
|
|
|
|
* and if there is "cached" data in the internal openssl
|
|
|
|
|
* buffers.
|
|
|
|
|
* 0. error (n<=0):
|
|
|
|
|
* SSL_ERROR_WANT_READ - input data fully
|
|
|
|
|
* consumed, no more returnable cached data inside openssl
|
|
|
|
|
* => exit.
|
|
|
|
|
* SSL_ERROR_WANT_WRITE - should never happen (the write
|
|
|
|
|
* buffer is big enough to handle any re-negociation).
|
|
|
|
|
* SSL_ERROR_ZERO_RETURN - ssl level shutdown => exit.
|
|
|
|
|
* other errors are unexpected.
|
|
|
|
|
* 1. output buffer filled (n == bytes_free):
|
|
|
|
|
* 1i. - still unconsumed input, nothing buffered by openssl
|
|
|
|
|
* 1ip. - unconsumed input + buffered data by openssl (pending
|
|
|
|
|
* on the next SSL_read).
|
|
|
|
|
* 1p. - completely consumed input, buffered data internally
|
|
|
|
|
* by openssl (pending).
|
|
|
|
|
* Likely to happen, about the only case when
|
|
|
|
|
* SSL_pending() could be used (but only if readahead=0).
|
|
|
|
|
* 1f. - consumed input, no buffered data.
|
|
|
|
|
* 2. output buffer not fully filled (n < bytes_free):
|
|
|
|
|
* 2i. - still unconsumed input, nothing buffered by openssl.
|
|
|
|
|
* This can appear if SSL readahead is 0 (SSL_read()
|
|
|
|
|
* tries to get only 1 record from the input).
|
|
|
|
|
* 2ip. - unconsumed input and buffered data by openssl.
|
|
|
|
|
* Unlikely to happen (e.g. readahead is 1, more
|
|
|
|
|
* records are buffered internally by openssl, but
|
|
|
|
|
* there was not enough space for buffering the whole
|
|
|
|
|
* input).
|
|
|
|
|
* 2p - consumed input, but buffered data by openssl.
|
|
|
|
|
* It happens especially when readahead is 1.
|
|
|
|
|
* 2f. - consumed input, no buffered data.
|
|
|
|
|
*
|
|
|
|
|
* One should repeat SSL_read() until and error is detected
|
|
|
|
|
* (0*) or the input and internal ssl buffers are fully consumed
|
|
|
|
|
* (1f or 2f). However in general is not possible to see if
|
|
|
|
|
* SSL_read() could return more data. SSL_pending() has very
|
|
|
|
|
* limited usability (basically it would return !=0 only if there
|
|
|
|
|
* was no enough space in the output buffer and only if this did
|
|
|
|
|
* not happen at a record boundary).
|
|
|
|
|
* The solution is to repeat SSL_read() until error or until
|
|
|
|
|
* the output buffer is filled (0* or 1*).
|
|
|
|
|
* In the later case, this whole function should be called again
|
|
|
|
|
* once there is more output space (set RD_CONN_REPEAT_READ).
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if(unlikely(tls_c->flags & F_TLS_CON_RENEGOTIATION)) {
|
|
|
|
|
/* Fix CVE-2009-3555 - disable renegotiation if started by client
|
|
|
|
|
* - simulate SSL EOF to force close connection*/
|
|
|
|
|
* - simulate SSL EOF to force close connection*/
|
|
|
|
|
tls_dbg = cfg_get(tls, tls_cfg, debug);
|
|
|
|
|
LOG(tls_dbg,
|
|
|
|
|
"Reading on a renegotiation of connection (n:%d) (%d)\n", n,
|
|
|
|
|
wolfSSL_get_error(ssl, n));
|
|
|
|
|
err_src = "TLS R-N read:";
|
|
|
|
|
ssl_error = SSL_ERROR_ZERO_RETURN;
|
|
|
|
|
ssl_error = WOLFSSL_ERROR_ZERO_RETURN;
|
|
|
|
|
} else {
|
|
|
|
|
if(unlikely(n <= 0)) {
|
|
|
|
|
ssl_error = wolfSSL_get_error(ssl, n);
|
|
|
|
|
err_src = "TLS read:";
|
|
|
|
|
/* errors handled below, outside the lock */
|
|
|
|
|
} else {
|
|
|
|
|
ssl_error = SSL_ERROR_NONE;
|
|
|
|
|
ssl_error = WOLFSSL_ERROR_NONE;
|
|
|
|
|
r->pos += n;
|
|
|
|
|
ssl_read += n;
|
|
|
|
|
bytes_free -= n;
|
|
|
|
@ -1194,68 +1192,76 @@ continue_ssl_read:
|
|
|
|
|
c, flags, n, ssl_error, ssl_read, *flags, tls_c->flags);
|
|
|
|
|
ssl_read_skipped:;
|
|
|
|
|
}
|
|
|
|
|
if(unlikely(wr.used != 0 && ssl_error != SSL_ERROR_ZERO_RETURN)) {
|
|
|
|
|
wr_used = wolfSSL_BIO_pending(rwbio);
|
|
|
|
|
if(unlikely(wr_used != 0 && ssl_error != WOLFSSL_ERROR_ZERO_RETURN)) {
|
|
|
|
|
TLS_RD_TRACE(
|
|
|
|
|
"(%p, %p) tcpconn_send_unsafe %d bytes\n", c, flags, wr.used);
|
|
|
|
|
"(%p, %p) tcpconn_send_unsafe %d bytes\n", c, flags, wr_used);
|
|
|
|
|
/* something was written and it's not ssl EOF*/
|
|
|
|
|
/* use 2-pass read for wolfSSL ring buffer */
|
|
|
|
|
for(nr = 0; nr < wr_used;) {
|
|
|
|
|
npos = wolfSSL_BIO_read(rwbio, wr_buf + nr, wr_used - nr);
|
|
|
|
|
if(npos <= 0)
|
|
|
|
|
break;
|
|
|
|
|
nr += npos;
|
|
|
|
|
}
|
|
|
|
|
assert(nr == wr_used);
|
|
|
|
|
if(unlikely(tcpconn_send_unsafe(
|
|
|
|
|
c->fd, c, (char *)wr.buf, wr.used, c->send_flags)
|
|
|
|
|
c->fd, c, (char *)wr_buf, wr_used, c->send_flags)
|
|
|
|
|
< 0)) {
|
|
|
|
|
tls_set_mbufs(c, 0, 0);
|
|
|
|
|
lock_release(&c->write_lock);
|
|
|
|
|
TLS_RD_TRACE("(%p, %p) tcpconn_send_unsafe error\n", c, flags);
|
|
|
|
|
goto error_send;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/* quickly catch bugs: segfault if accessed and not set */
|
|
|
|
|
tls_set_mbufs(c, 0, 0);
|
|
|
|
|
lock_release(&c->write_lock);
|
|
|
|
|
switch(ssl_error) {
|
|
|
|
|
case SSL_ERROR_NONE:
|
|
|
|
|
case WOLFSSL_ERROR_NONE:
|
|
|
|
|
if(unlikely(n < 0)) {
|
|
|
|
|
BUG("unexpected SSL_ERROR_NONE for n=%d\n", n);
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case SSL_ERROR_ZERO_RETURN:
|
|
|
|
|
case WOLFSSL_ERROR_ZERO_RETURN:
|
|
|
|
|
/* SSL EOF */
|
|
|
|
|
TLS_RD_TRACE("(%p, %p) SSL EOF (fd=%d)\n", c, flags, c->fd);
|
|
|
|
|
goto ssl_eof;
|
|
|
|
|
case SSL_ERROR_WANT_READ:
|
|
|
|
|
case WOLFSSL_ERROR_WANT_READ:
|
|
|
|
|
TLS_RD_TRACE("(%p, %p) SSL_ERROR_WANT_READ *flags=%d\n", c, flags,
|
|
|
|
|
*flags);
|
|
|
|
|
/* needs to read more data */
|
|
|
|
|
if(unlikely(rd.pos != rd.used)) {
|
|
|
|
|
if(unlikely((rd_unused = wolfSSL_BIO_wpending(rwbio)))) {
|
|
|
|
|
/* data still in the read buffer */
|
|
|
|
|
BUG("SSL_ERROR_WANT_READ but data still in"
|
|
|
|
|
" the rbio (%p, %d bytes at %d)\n",
|
|
|
|
|
rd.buf, rd.used - rd.pos, rd.pos);
|
|
|
|
|
" the rbio (%d bytes)\n",
|
|
|
|
|
(int)rd_unused);
|
|
|
|
|
goto bug;
|
|
|
|
|
}
|
|
|
|
|
if(unlikely((*flags & (RD_CONN_EOF | RD_CONN_SHORT_READ)) == 0)
|
|
|
|
|
&& bytes_free) {
|
|
|
|
|
/* there might still be data to read and there is space
|
|
|
|
|
* to decrypt it in tcp_req (no byte has been written into
|
|
|
|
|
* tcp_req in this case) */
|
|
|
|
|
* to decrypt it in tcp_req (no byte has been written into
|
|
|
|
|
* tcp_req in this case) */
|
|
|
|
|
TLS_RD_TRACE("(%p, %p) redo read *flags=%d bytes_free=%d\n", c,
|
|
|
|
|
flags, *flags, bytes_free);
|
|
|
|
|
goto redo_read;
|
|
|
|
|
}
|
|
|
|
|
goto end; /* no more data to read */
|
|
|
|
|
case SSL_ERROR_WANT_WRITE:
|
|
|
|
|
if(wr.used) {
|
|
|
|
|
case WOLFSSL_ERROR_WANT_WRITE:
|
|
|
|
|
if(wr_used) {
|
|
|
|
|
/* something was written => buffer not big enough to hold
|
|
|
|
|
* everything => reset buffer & retry (the tcp_write already
|
|
|
|
|
* happened if we are here) */
|
|
|
|
|
* everything => reset buffer & retry (the tcp_write already
|
|
|
|
|
* happened if we are here) */
|
|
|
|
|
TLS_RD_TRACE("(%p) SSL_ERROR_WANT_WRITE partial write"
|
|
|
|
|
" (written %d), retrying\n",
|
|
|
|
|
c, wr.used);
|
|
|
|
|
c, wr_used);
|
|
|
|
|
goto continue_ssl_read;
|
|
|
|
|
}
|
|
|
|
|
/* else write buffer too small, nothing written */
|
|
|
|
|
BUG("write buffer too small (%d/%d bytes)\n", wr.used, wr.size);
|
|
|
|
|
BUG("write buffer too small (%d/%d bytes)\n", (int)wr_used,
|
|
|
|
|
TLS_WR_MBUF_SZ);
|
|
|
|
|
goto bug;
|
|
|
|
|
case SSL_ERROR_SSL:
|
|
|
|
|
case WOLFSSL_ERROR_SSL:
|
|
|
|
|
/* protocol level error */
|
|
|
|
|
ERR("protocol level error\n");
|
|
|
|
|
TLS_ERR_SSL(err_src, ssl);
|
|
|
|
@ -1268,27 +1274,27 @@ continue_ssl_read:
|
|
|
|
|
|
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
|
|
case SSL_ERROR_WANT_CONNECT:
|
|
|
|
|
case WOLFSSL_ERROR_WANT_CONNECT:
|
|
|
|
|
/* only if the underlying BIO is not yet connected
|
|
|
|
|
* and the call would block in connect().
|
|
|
|
|
* (not possible in our case) */
|
|
|
|
|
* and the call would block in connect().
|
|
|
|
|
* (not possible in our case) */
|
|
|
|
|
BUG("unexpected SSL_ERROR_WANT_CONNECT\n");
|
|
|
|
|
goto bug;
|
|
|
|
|
case SSL_ERROR_WANT_ACCEPT:
|
|
|
|
|
case WOLFSSL_ERROR_WANT_ACCEPT:
|
|
|
|
|
/* only if the underlying BIO is not yet connected
|
|
|
|
|
* and call would block in accept()
|
|
|
|
|
* (not possible in our case) */
|
|
|
|
|
* and call would block in accept()
|
|
|
|
|
* (not possible in our case) */
|
|
|
|
|
BUG("unexpected SSL_ERROR_WANT_ACCEPT\n");
|
|
|
|
|
goto bug;
|
|
|
|
|
|
|
|
|
|
case SSL_ERROR_WANT_X509_LOOKUP:
|
|
|
|
|
case WOLFSSL_ERROR_WANT_X509_LOOKUP:
|
|
|
|
|
/* can only appear on client application and it indicates that
|
|
|
|
|
* an installed client cert. callback should be called again
|
|
|
|
|
* (it returned < 0 indicated that it wants to be called
|
|
|
|
|
* later). Not possible in our case */
|
|
|
|
|
* an installed client cert. callback should be called again
|
|
|
|
|
* (it returned < 0 indicated that it wants to be called
|
|
|
|
|
* later). Not possible in our case */
|
|
|
|
|
BUG("unsupported SSL_ERROR_WANT_X509_LOOKUP");
|
|
|
|
|
goto bug;
|
|
|
|
|
case SSL_ERROR_SYSCALL:
|
|
|
|
|
case WOLFSSL_ERROR_SYSCALL:
|
|
|
|
|
TLS_ERR_RET(x, err_src);
|
|
|
|
|
if(!x) {
|
|
|
|
|
if(n == 0) {
|
|
|
|
@ -1308,65 +1314,8 @@ continue_ssl_read:
|
|
|
|
|
BUG("unexpected value (n = %d)\n", n);
|
|
|
|
|
goto bug;
|
|
|
|
|
}
|
|
|
|
|
if(unlikely(rd.pos != rd.used)) {
|
|
|
|
|
/* encrypted data still in the read buffer (SSL_read() did not
|
|
|
|
|
* consume all of it) */
|
|
|
|
|
if(unlikely(n < 0))
|
|
|
|
|
/* here n should always be >= 0 */
|
|
|
|
|
BUG("unexpected value (n = %d)\n", n);
|
|
|
|
|
else {
|
|
|
|
|
if(unlikely(bytes_free != 0)) {
|
|
|
|
|
/* 2i or 2ip: unconsumed input and output buffer not filled =>
|
|
|
|
|
* retry ssl read (SSL_read() will read will stop at
|
|
|
|
|
* record boundaries, unless readahead==1).
|
|
|
|
|
* No tcp_read() is attempted, since that would reset the
|
|
|
|
|
* current no-yet-consumed input data.
|
|
|
|
|
*/
|
|
|
|
|
TLS_RD_TRACE("(%p, %p) input not fully consumed =>"
|
|
|
|
|
" retry SSL_read"
|
|
|
|
|
" (pos: %d, remaining %d, output free %d)\n",
|
|
|
|
|
c, flags, rd.pos, rd.used - rd.pos, bytes_free);
|
|
|
|
|
goto continue_ssl_read;
|
|
|
|
|
}
|
|
|
|
|
/* 1i or 1ip: bytes_free == 0
|
|
|
|
|
* (unconsumed input, but filled output buffer) =>
|
|
|
|
|
* queue read data, and exit asking for repeating the call
|
|
|
|
|
* once there is some space in the output buffer.
|
|
|
|
|
*/
|
|
|
|
|
if(likely(!enc_rd_buf)) {
|
|
|
|
|
TLS_RD_TRACE("(%p, %p) creating enc_rd_buf (for %d bytes)\n", c,
|
|
|
|
|
flags, rd.used - rd.pos);
|
|
|
|
|
enc_rd_buf =
|
|
|
|
|
shm_malloc(sizeof(*enc_rd_buf) - sizeof(enc_rd_buf->buf)
|
|
|
|
|
+ rd.used - rd.pos);
|
|
|
|
|
if(unlikely(enc_rd_buf == 0)) {
|
|
|
|
|
ERR("memory allocation error (%d bytes requested)\n",
|
|
|
|
|
(int)(sizeof(*enc_rd_buf) + sizeof(enc_rd_buf->buf)
|
|
|
|
|
+ rd.used - rd.pos));
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
enc_rd_buf->pos = 0;
|
|
|
|
|
enc_rd_buf->size = rd.used - rd.pos;
|
|
|
|
|
memcpy(enc_rd_buf->buf, rd.buf + rd.pos, enc_rd_buf->size);
|
|
|
|
|
} else if((enc_rd_buf->buf + enc_rd_buf->pos) == rd.buf) {
|
|
|
|
|
TLS_RD_TRACE("(%p, %p) enc_rd_buf already in use,"
|
|
|
|
|
" updating pos %d\n",
|
|
|
|
|
c, flags, enc_rd_buf->pos);
|
|
|
|
|
enc_rd_buf->pos += rd.pos;
|
|
|
|
|
} else {
|
|
|
|
|
BUG("enc_rd_buf->buf = %p, pos = %d, rd_buf.buf = %p\n",
|
|
|
|
|
enc_rd_buf->buf, enc_rd_buf->pos, rd.buf);
|
|
|
|
|
goto bug;
|
|
|
|
|
}
|
|
|
|
|
if(unlikely(tls_c->enc_rd_buf))
|
|
|
|
|
BUG("tls_c->enc_rd_buf!=0 (%p)\n", tls_c->enc_rd_buf);
|
|
|
|
|
/* there can't be 2 reads in parallel, so no locking is needed
|
|
|
|
|
* here */
|
|
|
|
|
tls_c->enc_rd_buf = enc_rd_buf;
|
|
|
|
|
enc_rd_buf = 0;
|
|
|
|
|
*flags |= RD_CONN_REPEAT_READ;
|
|
|
|
|
}
|
|
|
|
|
} else if(bytes_free != 0) {
|
|
|
|
|
|
|
|
|
|
if(bytes_free != 0) {
|
|
|
|
|
/* 2f or 2p: input fully consumed (rd.pos == rd.used),
|
|
|
|
|
* output buffer not filled, still possible to have pending
|
|
|
|
|
* data buffered by openssl */
|
|
|
|
@ -1402,15 +1351,11 @@ continue_ssl_read:
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
end:
|
|
|
|
|
if(enc_rd_buf)
|
|
|
|
|
shm_free(enc_rd_buf);
|
|
|
|
|
TLS_RD_TRACE(
|
|
|
|
|
"(%p, %p) end => %d (*flags=%d)\n", c, flags, ssl_read, *flags);
|
|
|
|
|
return ssl_read;
|
|
|
|
|
ssl_eof:
|
|
|
|
|
/* behave as an EOF would have been received at the tcp level */
|
|
|
|
|
if(enc_rd_buf)
|
|
|
|
|
shm_free(enc_rd_buf);
|
|
|
|
|
c->state = S_CONN_EOF;
|
|
|
|
|
*flags |= RD_CONN_EOF;
|
|
|
|
|
TLS_RD_TRACE(
|
|
|
|
@ -1419,8 +1364,6 @@ ssl_eof:
|
|
|
|
|
error_send:
|
|
|
|
|
error:
|
|
|
|
|
bug:
|
|
|
|
|
if(enc_rd_buf)
|
|
|
|
|
shm_free(enc_rd_buf);
|
|
|
|
|
r->error = TCP_READ_ERROR;
|
|
|
|
|
TLS_RD_TRACE("(%p, %p) end error => %d (*flags=%d)\n", c, flags, ssl_read,
|
|
|
|
|
*flags);
|
|
|
|
|