mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-21 11:30:01 +00:00
Handle OOM situations in iobuf expansion
This commit is contained in:
1 parent
b53d8439e5
commit
b8994f8bc3
9 files changed
+264
-224
No files matched your search
+129
-109
@@ -2590,8 +2590,8 @@ static size_t roundup(size_t size, size_t align) {
|
||||
return align == 0 ? size : (size + align - 1) / align * align;
|
||||
}
|
||||
|
||||
int mg_iobuf_resize(struct mg_iobuf *io, size_t new_size) {
|
||||
int ok = 1;
|
||||
bool mg_iobuf_resize(struct mg_iobuf *io, size_t new_size) {
|
||||
bool ok = true;
|
||||
new_size = roundup(new_size, io->align);
|
||||
if (new_size == 0) {
|
||||
mg_bzero(io->buf, io->size);
|
||||
@@ -2610,14 +2610,14 @@ int mg_iobuf_resize(struct mg_iobuf *io, size_t new_size) {
|
||||
io->size = new_size;
|
||||
io->len = len;
|
||||
} else {
|
||||
ok = 0;
|
||||
ok = false;
|
||||
MG_ERROR(("%lld->%lld", (uint64_t) io->size, (uint64_t) new_size));
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
int mg_iobuf_init(struct mg_iobuf *io, size_t size, size_t align) {
|
||||
bool mg_iobuf_init(struct mg_iobuf *io, size_t size, size_t align) {
|
||||
io->buf = NULL;
|
||||
io->align = align;
|
||||
io->size = io->len = 0;
|
||||
@@ -6168,6 +6168,7 @@ bool mg_send(struct mg_connection *c, const void *buf, size_t len) {
|
||||
res = tx_udp(ifp, s->mac, &c->loc, &c->rem, buf, len);
|
||||
} else {
|
||||
res = mg_iobuf_add(&c->send, c->send.len, buf, len);
|
||||
// res == 0 means an OOM condition (iobuf couldn't resize), yet this is so far recoverable, let the caller decide
|
||||
}
|
||||
return res;
|
||||
}
|
||||
@@ -9523,6 +9524,8 @@ bool mg_send(struct mg_connection *c, const void *buf, size_t len) {
|
||||
return n > 0;
|
||||
} else {
|
||||
return mg_iobuf_add(&c->send, c->send.len, buf, len);
|
||||
// returning 0 means an OOM condition (iobuf couldn't resize), yet this is
|
||||
// so far recoverable, let the caller decide
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9544,7 +9547,7 @@ static void mg_set_non_blocking_mode(MG_SOCKET_TYPE fd) {
|
||||
#elif MG_ARCH == MG_ARCH_THREADX
|
||||
// NetxDuo fails to send large blocks of data to the non-blocking sockets
|
||||
(void) fd;
|
||||
//fcntl(fd, F_SETFL, O_NONBLOCK);
|
||||
// fcntl(fd, F_SETFL, O_NONBLOCK);
|
||||
#elif MG_ARCH == MG_ARCH_TIRTOS
|
||||
int val = 0;
|
||||
setsockopt(fd, SOL_SOCKET, SO_BLOCKING, &val, sizeof(val));
|
||||
@@ -9576,9 +9579,10 @@ void mg_multicast_add(struct mg_connection *c, char *ip) {
|
||||
struct ip_mreq mreq;
|
||||
mreq.imr_multiaddr.s_addr = inet_addr(ip);
|
||||
mreq.imr_interface.s_addr = mg_htonl(INADDR_ANY);
|
||||
setsockopt(FD(c), IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &mreq, sizeof(mreq));
|
||||
#endif // !Zephyr
|
||||
#endif // !lwIP
|
||||
setsockopt(FD(c), IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &mreq,
|
||||
sizeof(mreq));
|
||||
#endif // !Zephyr
|
||||
#endif // !lwIP
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -9696,9 +9700,9 @@ static void read_conn(struct mg_connection *c) {
|
||||
if (n == MG_IO_ERR || n == MG_IO_RESET) { // Windows, see #3031
|
||||
if (c->rtls.len == 0 || m < 0) {
|
||||
// Close only when we have fully drained both rtls and TLS buffers
|
||||
c->is_closing = 1; // or there's nothing we can do about it.
|
||||
if (m < 0) m = MG_IO_ERR; // but return last record data, see #3104
|
||||
} else { // see #2885
|
||||
c->is_closing = 1; // or there's nothing we can do about it.
|
||||
if (m < 0) m = MG_IO_ERR; // but return last record data, see #3104
|
||||
} else { // see #2885
|
||||
// TLS buffer is capped to max record size, even though, there can
|
||||
// be more than one record, give TLS a chance to process them.
|
||||
}
|
||||
@@ -9748,7 +9752,7 @@ static void connect_conn(struct mg_connection *c) {
|
||||
mg_call(c, MG_EV_CONNECT, NULL);
|
||||
MG_EPOLL_MOD(c, 0);
|
||||
if (c->is_tls_hs) mg_tls_handshake(c);
|
||||
if (!c->is_tls_hs) c->is_tls = 0; // user did not call mg_tls_init()
|
||||
if (!c->is_tls_hs) c->is_tls = 0; // user did not call mg_tls_init()
|
||||
} else {
|
||||
mg_error(c, "socket error");
|
||||
}
|
||||
@@ -9800,9 +9804,9 @@ void mg_connect_resolved(struct mg_connection *c) {
|
||||
rc = connect(FD(c), &usa.sa, slen); // Attempt to connect
|
||||
if (rc == 0) { // Success
|
||||
setlocaddr(FD(c), &c->loc);
|
||||
mg_call(c, MG_EV_CONNECT, NULL); // Send MG_EV_CONNECT to the user
|
||||
if (!c->is_tls_hs) c->is_tls = 0; // user did not call mg_tls_init()
|
||||
} else if (MG_SOCK_PENDING(rc)) { // Need to wait for TCP handshake
|
||||
mg_call(c, MG_EV_CONNECT, NULL); // Send MG_EV_CONNECT to the user
|
||||
if (!c->is_tls_hs) c->is_tls = 0; // user did not call mg_tls_init()
|
||||
} else if (MG_SOCK_PENDING(rc)) { // Need to wait for TCP handshake
|
||||
MG_DEBUG(("%lu %ld -> %M pend", c->id, c->fd, mg_print_ip_port, &c->rem));
|
||||
c->is_connecting = 1;
|
||||
} else {
|
||||
@@ -9862,7 +9866,7 @@ static void accept_conn(struct mg_mgr *mgr, struct mg_connection *lsn) {
|
||||
&c->rem, mg_print_ip_port, &c->loc));
|
||||
mg_call(c, MG_EV_OPEN, NULL);
|
||||
mg_call(c, MG_EV_ACCEPT, NULL);
|
||||
if (!c->is_tls_hs) c->is_tls = 0; // user did not call mg_tls_init()
|
||||
if (!c->is_tls_hs) c->is_tls = 0; // user did not call mg_tls_init()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10178,31 +10182,35 @@ static char *mg_ssi(const char *path, const char *root, int depth) {
|
||||
mg_snprintf(tmp, sizeof(tmp), "%.*s%s", (int) (p - path), path, arg);
|
||||
if (depth < MG_MAX_SSI_DEPTH &&
|
||||
(data = mg_ssi(tmp, root, depth + 1)) != NULL) {
|
||||
mg_iobuf_add(&b, b.len, data, strlen(data));
|
||||
size_t datalen = strlen(data);
|
||||
size_t ret = mg_iobuf_add(&b, b.len, data, datalen);
|
||||
mg_free(data);
|
||||
if (datalen > 0 && ret == 0) goto fail;
|
||||
} else {
|
||||
MG_ERROR(("%s: file=%s error or too deep", path, arg));
|
||||
}
|
||||
} // TODO(): or OOM at recursive call
|
||||
} else if (sscanf(buf, "<!--#include virtual=\"%[^\"]", arg) > 0) {
|
||||
char tmp[MG_PATH_MAX + MG_SSI_BUFSIZ + 10], *data;
|
||||
mg_snprintf(tmp, sizeof(tmp), "%s%s", root, arg);
|
||||
if (depth < MG_MAX_SSI_DEPTH &&
|
||||
(data = mg_ssi(tmp, root, depth + 1)) != NULL) {
|
||||
mg_iobuf_add(&b, b.len, data, strlen(data));
|
||||
size_t datalen = strlen(data);
|
||||
size_t ret = mg_iobuf_add(&b, b.len, data, datalen);
|
||||
mg_free(data);
|
||||
if (datalen > 0 && ret == 0) goto fail;
|
||||
} else {
|
||||
MG_ERROR(("%s: virtual=%s error or too deep", path, arg));
|
||||
}
|
||||
} // TODO(): or OOM at recursive call
|
||||
} else {
|
||||
// Unknown SSI tag
|
||||
MG_ERROR(("Unknown SSI tag: %.*s", (int) len, buf));
|
||||
mg_iobuf_add(&b, b.len, buf, len);
|
||||
if (len > 0 && mg_iobuf_add(&b, b.len, buf, len) == 0) goto fail;
|
||||
}
|
||||
intag = 0;
|
||||
len = 0;
|
||||
} else if (ch == '<') {
|
||||
intag = 1;
|
||||
if (len > 0) mg_iobuf_add(&b, b.len, buf, len);
|
||||
if (len > 0 && mg_iobuf_add(&b, b.len, buf, len) == 0) goto fail;
|
||||
len = 0;
|
||||
buf[len++] = (char) (ch & 0xff);
|
||||
} else if (intag) {
|
||||
@@ -10216,24 +10224,33 @@ static char *mg_ssi(const char *path, const char *root, int depth) {
|
||||
} else {
|
||||
buf[len++] = (char) (ch & 0xff);
|
||||
if (len >= sizeof(buf)) {
|
||||
mg_iobuf_add(&b, b.len, buf, len);
|
||||
if (mg_iobuf_add(&b, b.len, buf, len) == 0) goto fail;
|
||||
len = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (len > 0) mg_iobuf_add(&b, b.len, buf, len);
|
||||
if (b.len > 0) mg_iobuf_add(&b, b.len, "", 1); // nul-terminate
|
||||
if (len > 0 && mg_iobuf_add(&b, b.len, buf, len) == 0) goto fail;
|
||||
if (b.len > 0 && mg_iobuf_add(&b, b.len, "", 1) == 0) // nul-terminate
|
||||
goto fail;
|
||||
fclose(fp);
|
||||
}
|
||||
(void) depth;
|
||||
(void) root;
|
||||
return (char *) b.buf;
|
||||
|
||||
fail:
|
||||
fclose(fp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void mg_http_serve_ssi(struct mg_connection *c, const char *root,
|
||||
const char *fullpath) {
|
||||
const char *headers = "Content-Type: text/html; charset=utf-8\r\n";
|
||||
char *data = mg_ssi(fullpath, root, 0);
|
||||
if (data == NULL) {
|
||||
mg_error(c, "OOM");
|
||||
return;
|
||||
}
|
||||
mg_http_reply(c, 200, headers, "%s", data == NULL ? "" : data);
|
||||
mg_free(data);
|
||||
}
|
||||
@@ -12015,7 +12032,7 @@ static void mg_tls_generate_application_keys(struct mg_connection *c) {
|
||||
}
|
||||
|
||||
// AES GCM encryption of the message + put encoded data into the write buffer
|
||||
static void mg_tls_encrypt(struct mg_connection *c, const uint8_t *msg,
|
||||
static bool mg_tls_encrypt(struct mg_connection *c, const uint8_t *msg,
|
||||
size_t msgsz, uint8_t msgtype) {
|
||||
struct tls_data *tls = (struct tls_data *) c->tls;
|
||||
struct mg_iobuf *wio = &tls->send;
|
||||
@@ -12046,8 +12063,9 @@ static void mg_tls_encrypt(struct mg_connection *c, const uint8_t *msg,
|
||||
nonce[10] ^= (uint8_t) ((seq >> 8) & 255U);
|
||||
nonce[11] ^= (uint8_t) ((seq) & 255U);
|
||||
|
||||
mg_iobuf_add(wio, wio->len, hdr, sizeof(hdr));
|
||||
mg_iobuf_resize(wio, wio->len + encsz);
|
||||
if (mg_iobuf_add(wio, wio->len, hdr, sizeof(hdr)) == 0 ||
|
||||
!mg_iobuf_resize(wio, wio->len + encsz))
|
||||
return false;
|
||||
outmsg = wio->buf + wio->len;
|
||||
tag = wio->buf + wio->len + msgsz + 1;
|
||||
memmove(outmsg, msg, msgsz);
|
||||
@@ -12056,17 +12074,14 @@ static void mg_tls_encrypt(struct mg_connection *c, const uint8_t *msg,
|
||||
(void) tag; // tag is only used in aes gcm
|
||||
{
|
||||
size_t maxlen = MG_IO_SIZE > 16384 ? 16384 : MG_IO_SIZE;
|
||||
size_t n;
|
||||
uint8_t *enc = (uint8_t *) mg_calloc(1, maxlen + 256 + 1);
|
||||
if (enc == NULL) {
|
||||
mg_error(c, "TLS OOM");
|
||||
return;
|
||||
} else {
|
||||
size_t n = mg_chacha20_poly1305_encrypt(enc, key, nonce, associated_data,
|
||||
sizeof(associated_data), outmsg,
|
||||
msgsz + 1);
|
||||
memmove(outmsg, enc, n);
|
||||
mg_free(enc);
|
||||
}
|
||||
if (enc == NULL) return false;
|
||||
n = mg_chacha20_poly1305_encrypt(enc, key, nonce, associated_data,
|
||||
sizeof(associated_data), outmsg,
|
||||
msgsz + 1);
|
||||
memmove(outmsg, enc, n);
|
||||
mg_free(enc);
|
||||
}
|
||||
#else
|
||||
mg_aes_gcm_encrypt(outmsg, outmsg, msgsz + 1, key, 16, nonce, sizeof(nonce),
|
||||
@@ -12074,6 +12089,7 @@ static void mg_tls_encrypt(struct mg_connection *c, const uint8_t *msg,
|
||||
#endif
|
||||
c->is_client ? tls->enc.cseq++ : tls->enc.sseq++;
|
||||
wio->len += encsz;
|
||||
return true;
|
||||
}
|
||||
|
||||
// read an encrypted record, decrypt it in place
|
||||
@@ -12249,7 +12265,7 @@ fail:
|
||||
#define PLACEHOLDER_32B PLACEHOLDER_16B, PLACEHOLDER_16B
|
||||
|
||||
// put ServerHello record into wio buffer
|
||||
static void mg_tls_server_send_hello(struct mg_connection *c) {
|
||||
static bool mg_tls_server_send_hello(struct mg_connection *c) {
|
||||
struct tls_data *tls = (struct tls_data *) c->tls;
|
||||
struct mg_iobuf *wio = &tls->send;
|
||||
|
||||
@@ -12290,20 +12306,24 @@ static void mg_tls_server_send_hello(struct mg_connection *c) {
|
||||
memmove(msg_server_hello + 84, x25519_pub, sizeof(x25519_pub));
|
||||
|
||||
// server hello message
|
||||
mg_iobuf_add(wio, wio->len, "\x16\x03\x03\x00\x7a", 5);
|
||||
mg_iobuf_add(wio, wio->len, msg_server_hello, sizeof(msg_server_hello));
|
||||
if (mg_iobuf_add(wio, wio->len, "\x16\x03\x03\x00\x7a", 5) == 0 ||
|
||||
mg_iobuf_add(wio, wio->len, msg_server_hello, sizeof(msg_server_hello)) ==
|
||||
0)
|
||||
return false;
|
||||
mg_sha256_update(&tls->sha256, msg_server_hello, sizeof(msg_server_hello));
|
||||
|
||||
// change cipher message
|
||||
mg_iobuf_add(wio, wio->len, "\x14\x03\x03\x00\x01\x01", 6);
|
||||
if (mg_iobuf_add(wio, wio->len, "\x14\x03\x03\x00\x01\x01", 6) == 0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void mg_tls_server_send_ext(struct mg_connection *c) {
|
||||
static bool mg_tls_server_send_ext(struct mg_connection *c) {
|
||||
struct tls_data *tls = (struct tls_data *) c->tls;
|
||||
// server extensions
|
||||
uint8_t ext[6] = {0x08, 0, 0, 2, 0, 0};
|
||||
mg_sha256_update(&tls->sha256, ext, sizeof(ext));
|
||||
mg_tls_encrypt(c, ext, sizeof(ext), MG_TLS_HANDSHAKE);
|
||||
return mg_tls_encrypt(c, ext, sizeof(ext), MG_TLS_HANDSHAKE);
|
||||
}
|
||||
|
||||
// signature algorithms we actually support:
|
||||
@@ -12312,13 +12332,11 @@ static const uint8_t secp256r1_sig_algs[12] = {
|
||||
0x00, 0x0d, 0x00, 0x08, 0x00, 0x06, 0x04, 0x03, 0x08, 0x04, 0x04, 0x01
|
||||
};
|
||||
|
||||
static void mg_tls_server_send_cert_request(struct mg_connection *c) {
|
||||
static bool mg_tls_server_send_cert_request(struct mg_connection *c) {
|
||||
struct tls_data *tls = (struct tls_data *) c->tls;
|
||||
uint8_t *req = (uint8_t *) mg_calloc(1, 13 + sizeof(secp256r1_sig_algs));
|
||||
if (req == NULL) {
|
||||
mg_error(c, "tls cert req oom");
|
||||
return;
|
||||
}
|
||||
bool res;
|
||||
if (req == NULL) return false;
|
||||
req[0] = MG_TLS_CERTIFICATE_REQUEST; // handshake header
|
||||
MG_STORE_BE24(req + 1, 9 + sizeof(secp256r1_sig_algs));
|
||||
req[4] = 0; // context length
|
||||
@@ -12330,20 +12348,19 @@ static void mg_tls_server_send_cert_request(struct mg_connection *c) {
|
||||
sizeof(secp256r1_sig_algs)); // signature hash algorithms length
|
||||
memcpy(req + 13, (uint8_t *) secp256r1_sig_algs, sizeof(secp256r1_sig_algs));
|
||||
mg_sha256_update(&tls->sha256, req, 13 + sizeof(secp256r1_sig_algs));
|
||||
mg_tls_encrypt(c, req, 13 + sizeof(secp256r1_sig_algs), MG_TLS_HANDSHAKE);
|
||||
res = mg_tls_encrypt(c, req, 13 + sizeof(secp256r1_sig_algs), MG_TLS_HANDSHAKE);
|
||||
mg_free(req);
|
||||
return res;
|
||||
}
|
||||
|
||||
static void mg_tls_send_cert(struct mg_connection *c, bool is_client) {
|
||||
static bool mg_tls_send_cert(struct mg_connection *c, bool is_client) {
|
||||
struct tls_data *tls = (struct tls_data *) c->tls;
|
||||
int send_ca = !is_client && tls->ca_der.len > 0;
|
||||
// DER certificate + CA (server optional)
|
||||
size_t n = tls->cert_der.len + (send_ca ? tls->ca_der.len + 5 : 0);
|
||||
uint8_t *cert = (uint8_t *) mg_calloc(1, 13 + n);
|
||||
if (cert == NULL) {
|
||||
mg_error(c, "tls cert oom");
|
||||
return;
|
||||
}
|
||||
bool res;
|
||||
if (cert == NULL) return false;
|
||||
cert[0] = MG_TLS_CERTIFICATE; // handshake header
|
||||
MG_STORE_BE24(cert + 1, n + 9);
|
||||
cert[4] = 0; // request context
|
||||
@@ -12361,8 +12378,9 @@ static void mg_tls_send_cert(struct mg_connection *c, bool is_client) {
|
||||
MG_STORE_BE16(cert + 11 + n, 0); // certificate extensions (none)
|
||||
}
|
||||
mg_sha256_update(&tls->sha256, cert, 13 + n);
|
||||
mg_tls_encrypt(c, cert, 13 + n, MG_TLS_HANDSHAKE);
|
||||
res = mg_tls_encrypt(c, cert, 13 + n, MG_TLS_HANDSHAKE);
|
||||
mg_free(cert);
|
||||
return res;
|
||||
}
|
||||
|
||||
// type adapter between uECC hash context and our sha256 implementation
|
||||
@@ -12387,7 +12405,7 @@ static void finish_SHA256(const MG_UECC_HashContext *base,
|
||||
mg_sha256_final(hash_result, &c->ctx);
|
||||
}
|
||||
|
||||
static void mg_tls_send_cert_verify(struct mg_connection *c, bool is_client) {
|
||||
static bool mg_tls_send_cert_verify(struct mg_connection *c, bool is_client) {
|
||||
struct tls_data *tls = (struct tls_data *) c->tls;
|
||||
// server certificate verify packet
|
||||
uint8_t verify[82] = {0x0f, 0x00, 0x00, 0x00, 0x04, 0x03, 0x00, 0x00};
|
||||
@@ -12421,10 +12439,10 @@ static void mg_tls_send_cert_verify(struct mg_connection *c, bool is_client) {
|
||||
verify[7] = (uint8_t) sigsz;
|
||||
|
||||
mg_sha256_update(&tls->sha256, verify, verifysz);
|
||||
mg_tls_encrypt(c, verify, verifysz, MG_TLS_HANDSHAKE);
|
||||
return mg_tls_encrypt(c, verify, verifysz, MG_TLS_HANDSHAKE);
|
||||
}
|
||||
|
||||
static void mg_tls_server_send_finish(struct mg_connection *c) {
|
||||
static bool mg_tls_server_send_finish(struct mg_connection *c) {
|
||||
struct tls_data *tls = (struct tls_data *) c->tls;
|
||||
mg_sha256_ctx sha256;
|
||||
uint8_t hash[32];
|
||||
@@ -12432,8 +12450,10 @@ static void mg_tls_server_send_finish(struct mg_connection *c) {
|
||||
memmove(&sha256, &tls->sha256, sizeof(mg_sha256_ctx));
|
||||
mg_sha256_final(hash, &sha256);
|
||||
mg_hmac_sha256(finish + 4, tls->enc.server_finished_key, 32, hash, 32);
|
||||
mg_tls_encrypt(c, finish, sizeof(finish), MG_TLS_HANDSHAKE);
|
||||
if (!mg_tls_encrypt(c, finish, sizeof(finish), MG_TLS_HANDSHAKE))
|
||||
return false;
|
||||
mg_sha256_update(&tls->sha256, finish, sizeof(finish));
|
||||
return true;
|
||||
}
|
||||
|
||||
static int mg_tls_server_recv_finish(struct mg_connection *c) {
|
||||
@@ -12458,7 +12478,7 @@ static int mg_tls_server_recv_finish(struct mg_connection *c) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mg_tls_client_send_hello(struct mg_connection *c) {
|
||||
static bool mg_tls_client_send_hello(struct mg_connection *c) {
|
||||
struct tls_data *tls = (struct tls_data *) c->tls;
|
||||
struct mg_iobuf *wio = &tls->send;
|
||||
|
||||
@@ -12541,20 +12561,27 @@ static void mg_tls_client_send_hello(struct mg_connection *c) {
|
||||
memmove(msg_client_hello + 94, x25519_pub, sizeof(x25519_pub));
|
||||
|
||||
// client hello message
|
||||
mg_iobuf_add(wio, wio->len, msg_client_hello, sizeof(msg_client_hello));
|
||||
if (mg_iobuf_add(wio, wio->len, msg_client_hello, sizeof(msg_client_hello)) ==
|
||||
0)
|
||||
return false;
|
||||
mg_sha256_update(&tls->sha256, msg_client_hello + 5,
|
||||
sizeof(msg_client_hello) - 5);
|
||||
mg_iobuf_add(wio, wio->len, sig_alg, sig_alg_sz);
|
||||
if (mg_iobuf_add(wio, wio->len, sig_alg, sig_alg_sz) == 0) return false;
|
||||
mg_sha256_update(&tls->sha256, sig_alg, sig_alg_sz);
|
||||
if (hostnamesz > 0) {
|
||||
mg_iobuf_add(wio, wio->len, server_name_ext, sizeof(server_name_ext));
|
||||
mg_iobuf_add(wio, wio->len, hostname, hostnamesz);
|
||||
if (mg_iobuf_add(wio, wio->len, server_name_ext, sizeof(server_name_ext)) ==
|
||||
0 ||
|
||||
mg_iobuf_add(wio, wio->len, hostname, hostnamesz) == 0)
|
||||
return false;
|
||||
mg_sha256_update(&tls->sha256, server_name_ext, sizeof(server_name_ext));
|
||||
mg_sha256_update(&tls->sha256, (uint8_t *) hostname, hostnamesz);
|
||||
}
|
||||
|
||||
// change cipher message
|
||||
mg_iobuf_add(wio, wio->len, (const char *) "\x14\x03\x03\x00\x01\x01", 6);
|
||||
if (mg_iobuf_add(wio, wio->len, (const char *) "\x14\x03\x03\x00\x01\x01",
|
||||
6) == 0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int mg_tls_client_recv_hello(struct mg_connection *c) {
|
||||
@@ -13129,7 +13156,7 @@ static int mg_tls_client_recv_finish(struct mg_connection *c) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mg_tls_client_send_finish(struct mg_connection *c) {
|
||||
static bool mg_tls_client_send_finish(struct mg_connection *c) {
|
||||
struct tls_data *tls = (struct tls_data *) c->tls;
|
||||
mg_sha256_ctx sha256;
|
||||
uint8_t hash[32];
|
||||
@@ -13137,56 +13164,46 @@ static void mg_tls_client_send_finish(struct mg_connection *c) {
|
||||
memmove(&sha256, &tls->sha256, sizeof(mg_sha256_ctx));
|
||||
mg_sha256_final(hash, &sha256);
|
||||
mg_hmac_sha256(finish + 4, tls->enc.client_finished_key, 32, hash, 32);
|
||||
mg_tls_encrypt(c, finish, sizeof(finish), MG_TLS_HANDSHAKE);
|
||||
return mg_tls_encrypt(c, finish, sizeof(finish), MG_TLS_HANDSHAKE);
|
||||
}
|
||||
|
||||
static void mg_tls_client_handshake(struct mg_connection *c) {
|
||||
static bool mg_tls_client_handshake(struct mg_connection *c) {
|
||||
struct tls_data *tls = (struct tls_data *) c->tls;
|
||||
switch (tls->state) {
|
||||
case MG_TLS_STATE_CLIENT_START:
|
||||
mg_tls_client_send_hello(c);
|
||||
if (!mg_tls_client_send_hello(c)) return false;
|
||||
tls->state = MG_TLS_STATE_CLIENT_WAIT_SH;
|
||||
// Fallthrough
|
||||
case MG_TLS_STATE_CLIENT_WAIT_SH:
|
||||
if (mg_tls_client_recv_hello(c) < 0) {
|
||||
break;
|
||||
}
|
||||
if (mg_tls_client_recv_hello(c) < 0) break;
|
||||
tls->state = MG_TLS_STATE_CLIENT_WAIT_EE;
|
||||
// Fallthrough
|
||||
case MG_TLS_STATE_CLIENT_WAIT_EE:
|
||||
if (mg_tls_client_recv_ext(c) < 0) {
|
||||
break;
|
||||
}
|
||||
if (mg_tls_client_recv_ext(c) < 0) break;
|
||||
tls->state = MG_TLS_STATE_CLIENT_WAIT_CERT;
|
||||
// Fallthrough
|
||||
case MG_TLS_STATE_CLIENT_WAIT_CERT:
|
||||
if (mg_tls_recv_cert(c, true) < 0) {
|
||||
break;
|
||||
}
|
||||
if (mg_tls_recv_cert(c, true) < 0) break;
|
||||
tls->state = MG_TLS_STATE_CLIENT_WAIT_CV;
|
||||
// Fallthrough
|
||||
case MG_TLS_STATE_CLIENT_WAIT_CV:
|
||||
if (mg_tls_recv_cert_verify(c) < 0) {
|
||||
break;
|
||||
}
|
||||
if (mg_tls_recv_cert_verify(c) < 0) break;
|
||||
tls->state = MG_TLS_STATE_CLIENT_WAIT_FINISH;
|
||||
// Fallthrough
|
||||
case MG_TLS_STATE_CLIENT_WAIT_FINISH:
|
||||
if (mg_tls_client_recv_finish(c) < 0) {
|
||||
break;
|
||||
}
|
||||
if (mg_tls_client_recv_finish(c) < 0) break;
|
||||
if (tls->cert_requested && tls->cert_der.len > 0) { // two-way auth
|
||||
// generate application keys at this point, keep using handshake keys
|
||||
struct tls_enc hs_keys = tls->enc;
|
||||
mg_tls_generate_application_keys(c);
|
||||
tls->app_keys = tls->enc;
|
||||
tls->enc = hs_keys;
|
||||
mg_tls_send_cert(c, true);
|
||||
mg_tls_send_cert_verify(c, true);
|
||||
mg_tls_client_send_finish(c);
|
||||
if (!mg_tls_send_cert(c, true) || !mg_tls_send_cert_verify(c, true) ||
|
||||
!mg_tls_client_send_finish(c))
|
||||
return false;
|
||||
tls->enc = tls->app_keys;
|
||||
} else {
|
||||
mg_tls_client_send_finish(c);
|
||||
if (!mg_tls_client_send_finish(c)) return false;
|
||||
mg_tls_generate_application_keys(c);
|
||||
}
|
||||
tls->state = MG_TLS_STATE_CLIENT_CONNECTED;
|
||||
@@ -13197,22 +13214,19 @@ static void mg_tls_client_handshake(struct mg_connection *c) {
|
||||
mg_error(c, "unexpected client state: %d", tls->state);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void mg_tls_server_handshake(struct mg_connection *c) {
|
||||
static bool mg_tls_server_handshake(struct mg_connection *c) {
|
||||
struct tls_data *tls = (struct tls_data *) c->tls;
|
||||
switch (tls->state) {
|
||||
case MG_TLS_STATE_SERVER_START:
|
||||
if (mg_tls_server_recv_hello(c) < 0) {
|
||||
return;
|
||||
}
|
||||
mg_tls_server_send_hello(c);
|
||||
if (mg_tls_server_recv_hello(c) < 0) break;
|
||||
if (!mg_tls_server_send_hello(c)) return false;
|
||||
mg_tls_generate_handshake_keys(c);
|
||||
mg_tls_server_send_ext(c);
|
||||
if (tls->is_twoway) mg_tls_server_send_cert_request(c);
|
||||
mg_tls_send_cert(c, false);
|
||||
mg_tls_send_cert_verify(c, false);
|
||||
mg_tls_server_send_finish(c);
|
||||
if (!mg_tls_server_send_ext(c)) return false;
|
||||
if (tls->is_twoway && !mg_tls_server_send_cert_request(c)) return false;
|
||||
if (!mg_tls_send_cert(c, false) || !mg_tls_send_cert_verify(c, false) || !mg_tls_server_send_finish(c)) return false;
|
||||
if (tls->is_twoway) {
|
||||
// generate application keys at this point, keep using handshake keys
|
||||
struct tls_enc hs_keys = tls->enc;
|
||||
@@ -13225,9 +13239,7 @@ static void mg_tls_server_handshake(struct mg_connection *c) {
|
||||
tls->state = MG_TLS_STATE_SERVER_NEGOTIATED;
|
||||
// fallthrough
|
||||
case MG_TLS_STATE_SERVER_NEGOTIATED:
|
||||
if (mg_tls_server_recv_finish(c) < 0) {
|
||||
return;
|
||||
}
|
||||
if (mg_tls_server_recv_finish(c) < 0) break;
|
||||
if (tls->is_twoway) { // use previously generated keys
|
||||
tls->enc = tls->app_keys;
|
||||
} else { // generate keys now
|
||||
@@ -13235,7 +13247,7 @@ static void mg_tls_server_handshake(struct mg_connection *c) {
|
||||
}
|
||||
tls->state = MG_TLS_STATE_SERVER_CONNECTED;
|
||||
c->is_tls_hs = 0;
|
||||
return;
|
||||
break;
|
||||
case MG_TLS_STATE_SERVER_WAIT_CERT:
|
||||
if (mg_tls_recv_cert(c, false) < 0) break;
|
||||
tls->state = MG_TLS_STATE_SERVER_WAIT_CV;
|
||||
@@ -13248,16 +13260,22 @@ static void mg_tls_server_handshake(struct mg_connection *c) {
|
||||
mg_error(c, "unexpected server state: %d", tls->state);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void mg_tls_handshake(struct mg_connection *c) {
|
||||
struct tls_data *tls = (struct tls_data *) c->tls;
|
||||
long n;
|
||||
bool res;
|
||||
if (c->is_client) {
|
||||
// will clear is_hs when sending last chunk
|
||||
mg_tls_client_handshake(c);
|
||||
res = mg_tls_client_handshake(c);
|
||||
} else {
|
||||
mg_tls_server_handshake(c);
|
||||
res = mg_tls_server_handshake(c);
|
||||
}
|
||||
if (!res) {
|
||||
mg_error(c, "TLS OOM");
|
||||
return;
|
||||
}
|
||||
while (tls->send.len > 0 &&
|
||||
(n = mg_io_send(c, tls->send.buf, tls->send.len)) > 0) {
|
||||
@@ -13394,8 +13412,10 @@ long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) {
|
||||
if (!was_throttled) { // encrypt new data
|
||||
if (len > MG_IO_SIZE) len = MG_IO_SIZE;
|
||||
if (len > 16384) len = 16384;
|
||||
mg_tls_encrypt(c, (const uint8_t *) buf, len, MG_TLS_APP_DATA);
|
||||
} // else, resend outstanding encrypted data in tls->send
|
||||
if (!mg_tls_encrypt(c, (const uint8_t *) buf, len, MG_TLS_APP_DATA))
|
||||
return 0; // returning 0 means an OOM condition (iobuf couldn't resize),
|
||||
// yet this is so far recoverable, let the caller decide
|
||||
} // else, resend outstanding encrypted data in tls->send
|
||||
while (tls->send.len > 0 &&
|
||||
(n = mg_io_send(c, tls->send.buf, tls->send.len)) > 0) {
|
||||
mg_iobuf_del(&tls->send, 0, (size_t) n);
|
||||
@@ -21142,8 +21162,8 @@ size_t mg_ws_wrap(struct mg_connection *c, size_t len, int op) {
|
||||
memmove(p, p - header_len, len); // Shift data
|
||||
memcpy(p - header_len, header, header_len); // Prepend header
|
||||
mg_ws_mask(c, len); // Mask data
|
||||
}
|
||||
return c->send.len;
|
||||
} // returning 0 means an OOM condition (iobuf couldn't resize), yet this is
|
||||
return c->send.len; // so far recoverable, let the caller decide
|
||||
}
|
||||
|
||||
#ifdef MG_ENABLE_LINES
|
||||
|
||||
+2
-2
@@ -1539,8 +1539,8 @@ struct mg_iobuf {
|
||||
size_t align; // Alignment during allocation
|
||||
};
|
||||
|
||||
int mg_iobuf_init(struct mg_iobuf *, size_t, size_t);
|
||||
int mg_iobuf_resize(struct mg_iobuf *, size_t);
|
||||
bool mg_iobuf_init(struct mg_iobuf *, size_t, size_t);
|
||||
bool mg_iobuf_resize(struct mg_iobuf *, size_t);
|
||||
void mg_iobuf_free(struct mg_iobuf *);
|
||||
size_t mg_iobuf_add(struct mg_iobuf *, size_t, const void *, size_t);
|
||||
size_t mg_iobuf_del(struct mg_iobuf *, size_t ofs, size_t len);
|
||||
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
#include "arch.h"
|
||||
#include "iobuf.h"
|
||||
#include "arch.h"
|
||||
#include "log.h"
|
||||
#include "util.h"
|
||||
|
||||
@@ -7,8 +7,8 @@ static size_t roundup(size_t size, size_t align) {
|
||||
return align == 0 ? size : (size + align - 1) / align * align;
|
||||
}
|
||||
|
||||
int mg_iobuf_resize(struct mg_iobuf *io, size_t new_size) {
|
||||
int ok = 1;
|
||||
bool mg_iobuf_resize(struct mg_iobuf *io, size_t new_size) {
|
||||
bool ok = true;
|
||||
new_size = roundup(new_size, io->align);
|
||||
if (new_size == 0) {
|
||||
mg_bzero(io->buf, io->size);
|
||||
@@ -27,14 +27,14 @@ int mg_iobuf_resize(struct mg_iobuf *io, size_t new_size) {
|
||||
io->size = new_size;
|
||||
io->len = len;
|
||||
} else {
|
||||
ok = 0;
|
||||
ok = false;
|
||||
MG_ERROR(("%lld->%lld", (uint64_t) io->size, (uint64_t) new_size));
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
int mg_iobuf_init(struct mg_iobuf *io, size_t size, size_t align) {
|
||||
bool mg_iobuf_init(struct mg_iobuf *io, size_t size, size_t align) {
|
||||
io->buf = NULL;
|
||||
io->align = align;
|
||||
io->size = io->len = 0;
|
||||
|
||||
+2
-2
@@ -9,8 +9,8 @@ struct mg_iobuf {
|
||||
size_t align; // Alignment during allocation
|
||||
};
|
||||
|
||||
int mg_iobuf_init(struct mg_iobuf *, size_t, size_t);
|
||||
int mg_iobuf_resize(struct mg_iobuf *, size_t);
|
||||
bool mg_iobuf_init(struct mg_iobuf *, size_t, size_t);
|
||||
bool mg_iobuf_resize(struct mg_iobuf *, size_t);
|
||||
void mg_iobuf_free(struct mg_iobuf *);
|
||||
size_t mg_iobuf_add(struct mg_iobuf *, size_t, const void *, size_t);
|
||||
size_t mg_iobuf_del(struct mg_iobuf *, size_t ofs, size_t len);
|
||||
@@ -2004,6 +2004,7 @@ bool mg_send(struct mg_connection *c, const void *buf, size_t len) {
|
||||
res = tx_udp(ifp, s->mac, &c->loc, &c->rem, buf, len);
|
||||
} else {
|
||||
res = mg_iobuf_add(&c->send, c->send.len, buf, len);
|
||||
// res == 0 means an OOM condition (iobuf couldn't resize), yet this is so far recoverable, let the caller decide
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
+15
-12
@@ -142,6 +142,8 @@ bool mg_send(struct mg_connection *c, const void *buf, size_t len) {
|
||||
return n > 0;
|
||||
} else {
|
||||
return mg_iobuf_add(&c->send, c->send.len, buf, len);
|
||||
// returning 0 means an OOM condition (iobuf couldn't resize), yet this is
|
||||
// so far recoverable, let the caller decide
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,7 +165,7 @@ static void mg_set_non_blocking_mode(MG_SOCKET_TYPE fd) {
|
||||
#elif MG_ARCH == MG_ARCH_THREADX
|
||||
// NetxDuo fails to send large blocks of data to the non-blocking sockets
|
||||
(void) fd;
|
||||
//fcntl(fd, F_SETFL, O_NONBLOCK);
|
||||
// fcntl(fd, F_SETFL, O_NONBLOCK);
|
||||
#elif MG_ARCH == MG_ARCH_TIRTOS
|
||||
int val = 0;
|
||||
setsockopt(fd, SOL_SOCKET, SO_BLOCKING, &val, sizeof(val));
|
||||
@@ -195,9 +197,10 @@ void mg_multicast_add(struct mg_connection *c, char *ip) {
|
||||
struct ip_mreq mreq;
|
||||
mreq.imr_multiaddr.s_addr = inet_addr(ip);
|
||||
mreq.imr_interface.s_addr = mg_htonl(INADDR_ANY);
|
||||
setsockopt(FD(c), IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &mreq, sizeof(mreq));
|
||||
#endif // !Zephyr
|
||||
#endif // !lwIP
|
||||
setsockopt(FD(c), IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &mreq,
|
||||
sizeof(mreq));
|
||||
#endif // !Zephyr
|
||||
#endif // !lwIP
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -315,9 +318,9 @@ static void read_conn(struct mg_connection *c) {
|
||||
if (n == MG_IO_ERR || n == MG_IO_RESET) { // Windows, see #3031
|
||||
if (c->rtls.len == 0 || m < 0) {
|
||||
// Close only when we have fully drained both rtls and TLS buffers
|
||||
c->is_closing = 1; // or there's nothing we can do about it.
|
||||
if (m < 0) m = MG_IO_ERR; // but return last record data, see #3104
|
||||
} else { // see #2885
|
||||
c->is_closing = 1; // or there's nothing we can do about it.
|
||||
if (m < 0) m = MG_IO_ERR; // but return last record data, see #3104
|
||||
} else { // see #2885
|
||||
// TLS buffer is capped to max record size, even though, there can
|
||||
// be more than one record, give TLS a chance to process them.
|
||||
}
|
||||
@@ -367,7 +370,7 @@ static void connect_conn(struct mg_connection *c) {
|
||||
mg_call(c, MG_EV_CONNECT, NULL);
|
||||
MG_EPOLL_MOD(c, 0);
|
||||
if (c->is_tls_hs) mg_tls_handshake(c);
|
||||
if (!c->is_tls_hs) c->is_tls = 0; // user did not call mg_tls_init()
|
||||
if (!c->is_tls_hs) c->is_tls = 0; // user did not call mg_tls_init()
|
||||
} else {
|
||||
mg_error(c, "socket error");
|
||||
}
|
||||
@@ -419,9 +422,9 @@ void mg_connect_resolved(struct mg_connection *c) {
|
||||
rc = connect(FD(c), &usa.sa, slen); // Attempt to connect
|
||||
if (rc == 0) { // Success
|
||||
setlocaddr(FD(c), &c->loc);
|
||||
mg_call(c, MG_EV_CONNECT, NULL); // Send MG_EV_CONNECT to the user
|
||||
if (!c->is_tls_hs) c->is_tls = 0; // user did not call mg_tls_init()
|
||||
} else if (MG_SOCK_PENDING(rc)) { // Need to wait for TCP handshake
|
||||
mg_call(c, MG_EV_CONNECT, NULL); // Send MG_EV_CONNECT to the user
|
||||
if (!c->is_tls_hs) c->is_tls = 0; // user did not call mg_tls_init()
|
||||
} else if (MG_SOCK_PENDING(rc)) { // Need to wait for TCP handshake
|
||||
MG_DEBUG(("%lu %ld -> %M pend", c->id, c->fd, mg_print_ip_port, &c->rem));
|
||||
c->is_connecting = 1;
|
||||
} else {
|
||||
@@ -481,7 +484,7 @@ static void accept_conn(struct mg_mgr *mgr, struct mg_connection *lsn) {
|
||||
&c->rem, mg_print_ip_port, &c->loc));
|
||||
mg_call(c, MG_EV_OPEN, NULL);
|
||||
mg_call(c, MG_EV_ACCEPT, NULL);
|
||||
if (!c->is_tls_hs) c->is_tls = 0; // user did not call mg_tls_init()
|
||||
if (!c->is_tls_hs) c->is_tls = 0; // user did not call mg_tls_init()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "ssi.h"
|
||||
#include "log.h"
|
||||
#include "printf.h"
|
||||
#include "ssi.h"
|
||||
#include "util.h"
|
||||
|
||||
#ifndef MG_MAX_SSI_DEPTH
|
||||
@@ -31,31 +31,35 @@ static char *mg_ssi(const char *path, const char *root, int depth) {
|
||||
mg_snprintf(tmp, sizeof(tmp), "%.*s%s", (int) (p - path), path, arg);
|
||||
if (depth < MG_MAX_SSI_DEPTH &&
|
||||
(data = mg_ssi(tmp, root, depth + 1)) != NULL) {
|
||||
mg_iobuf_add(&b, b.len, data, strlen(data));
|
||||
size_t datalen = strlen(data);
|
||||
size_t ret = mg_iobuf_add(&b, b.len, data, datalen);
|
||||
mg_free(data);
|
||||
if (datalen > 0 && ret == 0) goto fail;
|
||||
} else {
|
||||
MG_ERROR(("%s: file=%s error or too deep", path, arg));
|
||||
}
|
||||
} // TODO(): or OOM at recursive call
|
||||
} else if (sscanf(buf, "<!--#include virtual=\"%[^\"]", arg) > 0) {
|
||||
char tmp[MG_PATH_MAX + MG_SSI_BUFSIZ + 10], *data;
|
||||
mg_snprintf(tmp, sizeof(tmp), "%s%s", root, arg);
|
||||
if (depth < MG_MAX_SSI_DEPTH &&
|
||||
(data = mg_ssi(tmp, root, depth + 1)) != NULL) {
|
||||
mg_iobuf_add(&b, b.len, data, strlen(data));
|
||||
size_t datalen = strlen(data);
|
||||
size_t ret = mg_iobuf_add(&b, b.len, data, datalen);
|
||||
mg_free(data);
|
||||
if (datalen > 0 && ret == 0) goto fail;
|
||||
} else {
|
||||
MG_ERROR(("%s: virtual=%s error or too deep", path, arg));
|
||||
}
|
||||
} // TODO(): or OOM at recursive call
|
||||
} else {
|
||||
// Unknown SSI tag
|
||||
MG_ERROR(("Unknown SSI tag: %.*s", (int) len, buf));
|
||||
mg_iobuf_add(&b, b.len, buf, len);
|
||||
if (len > 0 && mg_iobuf_add(&b, b.len, buf, len) == 0) goto fail;
|
||||
}
|
||||
intag = 0;
|
||||
len = 0;
|
||||
} else if (ch == '<') {
|
||||
intag = 1;
|
||||
if (len > 0) mg_iobuf_add(&b, b.len, buf, len);
|
||||
if (len > 0 && mg_iobuf_add(&b, b.len, buf, len) == 0) goto fail;
|
||||
len = 0;
|
||||
buf[len++] = (char) (ch & 0xff);
|
||||
} else if (intag) {
|
||||
@@ -69,24 +73,33 @@ static char *mg_ssi(const char *path, const char *root, int depth) {
|
||||
} else {
|
||||
buf[len++] = (char) (ch & 0xff);
|
||||
if (len >= sizeof(buf)) {
|
||||
mg_iobuf_add(&b, b.len, buf, len);
|
||||
if (mg_iobuf_add(&b, b.len, buf, len) == 0) goto fail;
|
||||
len = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (len > 0) mg_iobuf_add(&b, b.len, buf, len);
|
||||
if (b.len > 0) mg_iobuf_add(&b, b.len, "", 1); // nul-terminate
|
||||
if (len > 0 && mg_iobuf_add(&b, b.len, buf, len) == 0) goto fail;
|
||||
if (b.len > 0 && mg_iobuf_add(&b, b.len, "", 1) == 0) // nul-terminate
|
||||
goto fail;
|
||||
fclose(fp);
|
||||
}
|
||||
(void) depth;
|
||||
(void) root;
|
||||
return (char *) b.buf;
|
||||
|
||||
fail:
|
||||
fclose(fp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void mg_http_serve_ssi(struct mg_connection *c, const char *root,
|
||||
const char *fullpath) {
|
||||
const char *headers = "Content-Type: text/html; charset=utf-8\r\n";
|
||||
char *data = mg_ssi(fullpath, root, 0);
|
||||
if (data == NULL) {
|
||||
mg_error(c, "OOM");
|
||||
return;
|
||||
}
|
||||
mg_http_reply(c, 200, headers, "%s", data == NULL ? "" : data);
|
||||
mg_free(data);
|
||||
}
|
||||
|
||||
+85
-82
@@ -399,7 +399,7 @@ static void mg_tls_generate_application_keys(struct mg_connection *c) {
|
||||
}
|
||||
|
||||
// AES GCM encryption of the message + put encoded data into the write buffer
|
||||
static void mg_tls_encrypt(struct mg_connection *c, const uint8_t *msg,
|
||||
static bool mg_tls_encrypt(struct mg_connection *c, const uint8_t *msg,
|
||||
size_t msgsz, uint8_t msgtype) {
|
||||
struct tls_data *tls = (struct tls_data *) c->tls;
|
||||
struct mg_iobuf *wio = &tls->send;
|
||||
@@ -430,8 +430,9 @@ static void mg_tls_encrypt(struct mg_connection *c, const uint8_t *msg,
|
||||
nonce[10] ^= (uint8_t) ((seq >> 8) & 255U);
|
||||
nonce[11] ^= (uint8_t) ((seq) & 255U);
|
||||
|
||||
mg_iobuf_add(wio, wio->len, hdr, sizeof(hdr));
|
||||
mg_iobuf_resize(wio, wio->len + encsz);
|
||||
if (mg_iobuf_add(wio, wio->len, hdr, sizeof(hdr)) == 0 ||
|
||||
!mg_iobuf_resize(wio, wio->len + encsz))
|
||||
return false;
|
||||
outmsg = wio->buf + wio->len;
|
||||
tag = wio->buf + wio->len + msgsz + 1;
|
||||
memmove(outmsg, msg, msgsz);
|
||||
@@ -440,17 +441,14 @@ static void mg_tls_encrypt(struct mg_connection *c, const uint8_t *msg,
|
||||
(void) tag; // tag is only used in aes gcm
|
||||
{
|
||||
size_t maxlen = MG_IO_SIZE > 16384 ? 16384 : MG_IO_SIZE;
|
||||
size_t n;
|
||||
uint8_t *enc = (uint8_t *) mg_calloc(1, maxlen + 256 + 1);
|
||||
if (enc == NULL) {
|
||||
mg_error(c, "TLS OOM");
|
||||
return;
|
||||
} else {
|
||||
size_t n = mg_chacha20_poly1305_encrypt(enc, key, nonce, associated_data,
|
||||
sizeof(associated_data), outmsg,
|
||||
msgsz + 1);
|
||||
memmove(outmsg, enc, n);
|
||||
mg_free(enc);
|
||||
}
|
||||
if (enc == NULL) return false;
|
||||
n = mg_chacha20_poly1305_encrypt(enc, key, nonce, associated_data,
|
||||
sizeof(associated_data), outmsg,
|
||||
msgsz + 1);
|
||||
memmove(outmsg, enc, n);
|
||||
mg_free(enc);
|
||||
}
|
||||
#else
|
||||
mg_aes_gcm_encrypt(outmsg, outmsg, msgsz + 1, key, 16, nonce, sizeof(nonce),
|
||||
@@ -458,6 +456,7 @@ static void mg_tls_encrypt(struct mg_connection *c, const uint8_t *msg,
|
||||
#endif
|
||||
c->is_client ? tls->enc.cseq++ : tls->enc.sseq++;
|
||||
wio->len += encsz;
|
||||
return true;
|
||||
}
|
||||
|
||||
// read an encrypted record, decrypt it in place
|
||||
@@ -633,7 +632,7 @@ fail:
|
||||
#define PLACEHOLDER_32B PLACEHOLDER_16B, PLACEHOLDER_16B
|
||||
|
||||
// put ServerHello record into wio buffer
|
||||
static void mg_tls_server_send_hello(struct mg_connection *c) {
|
||||
static bool mg_tls_server_send_hello(struct mg_connection *c) {
|
||||
struct tls_data *tls = (struct tls_data *) c->tls;
|
||||
struct mg_iobuf *wio = &tls->send;
|
||||
|
||||
@@ -674,20 +673,24 @@ static void mg_tls_server_send_hello(struct mg_connection *c) {
|
||||
memmove(msg_server_hello + 84, x25519_pub, sizeof(x25519_pub));
|
||||
|
||||
// server hello message
|
||||
mg_iobuf_add(wio, wio->len, "\x16\x03\x03\x00\x7a", 5);
|
||||
mg_iobuf_add(wio, wio->len, msg_server_hello, sizeof(msg_server_hello));
|
||||
if (mg_iobuf_add(wio, wio->len, "\x16\x03\x03\x00\x7a", 5) == 0 ||
|
||||
mg_iobuf_add(wio, wio->len, msg_server_hello, sizeof(msg_server_hello)) ==
|
||||
0)
|
||||
return false;
|
||||
mg_sha256_update(&tls->sha256, msg_server_hello, sizeof(msg_server_hello));
|
||||
|
||||
// change cipher message
|
||||
mg_iobuf_add(wio, wio->len, "\x14\x03\x03\x00\x01\x01", 6);
|
||||
if (mg_iobuf_add(wio, wio->len, "\x14\x03\x03\x00\x01\x01", 6) == 0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void mg_tls_server_send_ext(struct mg_connection *c) {
|
||||
static bool mg_tls_server_send_ext(struct mg_connection *c) {
|
||||
struct tls_data *tls = (struct tls_data *) c->tls;
|
||||
// server extensions
|
||||
uint8_t ext[6] = {0x08, 0, 0, 2, 0, 0};
|
||||
mg_sha256_update(&tls->sha256, ext, sizeof(ext));
|
||||
mg_tls_encrypt(c, ext, sizeof(ext), MG_TLS_HANDSHAKE);
|
||||
return mg_tls_encrypt(c, ext, sizeof(ext), MG_TLS_HANDSHAKE);
|
||||
}
|
||||
|
||||
// signature algorithms we actually support:
|
||||
@@ -696,13 +699,11 @@ static const uint8_t secp256r1_sig_algs[12] = {
|
||||
0x00, 0x0d, 0x00, 0x08, 0x00, 0x06, 0x04, 0x03, 0x08, 0x04, 0x04, 0x01
|
||||
};
|
||||
|
||||
static void mg_tls_server_send_cert_request(struct mg_connection *c) {
|
||||
static bool mg_tls_server_send_cert_request(struct mg_connection *c) {
|
||||
struct tls_data *tls = (struct tls_data *) c->tls;
|
||||
uint8_t *req = (uint8_t *) mg_calloc(1, 13 + sizeof(secp256r1_sig_algs));
|
||||
if (req == NULL) {
|
||||
mg_error(c, "tls cert req oom");
|
||||
return;
|
||||
}
|
||||
bool res;
|
||||
if (req == NULL) return false;
|
||||
req[0] = MG_TLS_CERTIFICATE_REQUEST; // handshake header
|
||||
MG_STORE_BE24(req + 1, 9 + sizeof(secp256r1_sig_algs));
|
||||
req[4] = 0; // context length
|
||||
@@ -714,20 +715,19 @@ static void mg_tls_server_send_cert_request(struct mg_connection *c) {
|
||||
sizeof(secp256r1_sig_algs)); // signature hash algorithms length
|
||||
memcpy(req + 13, (uint8_t *) secp256r1_sig_algs, sizeof(secp256r1_sig_algs));
|
||||
mg_sha256_update(&tls->sha256, req, 13 + sizeof(secp256r1_sig_algs));
|
||||
mg_tls_encrypt(c, req, 13 + sizeof(secp256r1_sig_algs), MG_TLS_HANDSHAKE);
|
||||
res = mg_tls_encrypt(c, req, 13 + sizeof(secp256r1_sig_algs), MG_TLS_HANDSHAKE);
|
||||
mg_free(req);
|
||||
return res;
|
||||
}
|
||||
|
||||
static void mg_tls_send_cert(struct mg_connection *c, bool is_client) {
|
||||
static bool mg_tls_send_cert(struct mg_connection *c, bool is_client) {
|
||||
struct tls_data *tls = (struct tls_data *) c->tls;
|
||||
int send_ca = !is_client && tls->ca_der.len > 0;
|
||||
// DER certificate + CA (server optional)
|
||||
size_t n = tls->cert_der.len + (send_ca ? tls->ca_der.len + 5 : 0);
|
||||
uint8_t *cert = (uint8_t *) mg_calloc(1, 13 + n);
|
||||
if (cert == NULL) {
|
||||
mg_error(c, "tls cert oom");
|
||||
return;
|
||||
}
|
||||
bool res;
|
||||
if (cert == NULL) return false;
|
||||
cert[0] = MG_TLS_CERTIFICATE; // handshake header
|
||||
MG_STORE_BE24(cert + 1, n + 9);
|
||||
cert[4] = 0; // request context
|
||||
@@ -745,8 +745,9 @@ static void mg_tls_send_cert(struct mg_connection *c, bool is_client) {
|
||||
MG_STORE_BE16(cert + 11 + n, 0); // certificate extensions (none)
|
||||
}
|
||||
mg_sha256_update(&tls->sha256, cert, 13 + n);
|
||||
mg_tls_encrypt(c, cert, 13 + n, MG_TLS_HANDSHAKE);
|
||||
res = mg_tls_encrypt(c, cert, 13 + n, MG_TLS_HANDSHAKE);
|
||||
mg_free(cert);
|
||||
return res;
|
||||
}
|
||||
|
||||
// type adapter between uECC hash context and our sha256 implementation
|
||||
@@ -771,7 +772,7 @@ static void finish_SHA256(const MG_UECC_HashContext *base,
|
||||
mg_sha256_final(hash_result, &c->ctx);
|
||||
}
|
||||
|
||||
static void mg_tls_send_cert_verify(struct mg_connection *c, bool is_client) {
|
||||
static bool mg_tls_send_cert_verify(struct mg_connection *c, bool is_client) {
|
||||
struct tls_data *tls = (struct tls_data *) c->tls;
|
||||
// server certificate verify packet
|
||||
uint8_t verify[82] = {0x0f, 0x00, 0x00, 0x00, 0x04, 0x03, 0x00, 0x00};
|
||||
@@ -805,10 +806,10 @@ static void mg_tls_send_cert_verify(struct mg_connection *c, bool is_client) {
|
||||
verify[7] = (uint8_t) sigsz;
|
||||
|
||||
mg_sha256_update(&tls->sha256, verify, verifysz);
|
||||
mg_tls_encrypt(c, verify, verifysz, MG_TLS_HANDSHAKE);
|
||||
return mg_tls_encrypt(c, verify, verifysz, MG_TLS_HANDSHAKE);
|
||||
}
|
||||
|
||||
static void mg_tls_server_send_finish(struct mg_connection *c) {
|
||||
static bool mg_tls_server_send_finish(struct mg_connection *c) {
|
||||
struct tls_data *tls = (struct tls_data *) c->tls;
|
||||
mg_sha256_ctx sha256;
|
||||
uint8_t hash[32];
|
||||
@@ -816,8 +817,10 @@ static void mg_tls_server_send_finish(struct mg_connection *c) {
|
||||
memmove(&sha256, &tls->sha256, sizeof(mg_sha256_ctx));
|
||||
mg_sha256_final(hash, &sha256);
|
||||
mg_hmac_sha256(finish + 4, tls->enc.server_finished_key, 32, hash, 32);
|
||||
mg_tls_encrypt(c, finish, sizeof(finish), MG_TLS_HANDSHAKE);
|
||||
if (!mg_tls_encrypt(c, finish, sizeof(finish), MG_TLS_HANDSHAKE))
|
||||
return false;
|
||||
mg_sha256_update(&tls->sha256, finish, sizeof(finish));
|
||||
return true;
|
||||
}
|
||||
|
||||
static int mg_tls_server_recv_finish(struct mg_connection *c) {
|
||||
@@ -842,7 +845,7 @@ static int mg_tls_server_recv_finish(struct mg_connection *c) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mg_tls_client_send_hello(struct mg_connection *c) {
|
||||
static bool mg_tls_client_send_hello(struct mg_connection *c) {
|
||||
struct tls_data *tls = (struct tls_data *) c->tls;
|
||||
struct mg_iobuf *wio = &tls->send;
|
||||
|
||||
@@ -925,20 +928,27 @@ static void mg_tls_client_send_hello(struct mg_connection *c) {
|
||||
memmove(msg_client_hello + 94, x25519_pub, sizeof(x25519_pub));
|
||||
|
||||
// client hello message
|
||||
mg_iobuf_add(wio, wio->len, msg_client_hello, sizeof(msg_client_hello));
|
||||
if (mg_iobuf_add(wio, wio->len, msg_client_hello, sizeof(msg_client_hello)) ==
|
||||
0)
|
||||
return false;
|
||||
mg_sha256_update(&tls->sha256, msg_client_hello + 5,
|
||||
sizeof(msg_client_hello) - 5);
|
||||
mg_iobuf_add(wio, wio->len, sig_alg, sig_alg_sz);
|
||||
if (mg_iobuf_add(wio, wio->len, sig_alg, sig_alg_sz) == 0) return false;
|
||||
mg_sha256_update(&tls->sha256, sig_alg, sig_alg_sz);
|
||||
if (hostnamesz > 0) {
|
||||
mg_iobuf_add(wio, wio->len, server_name_ext, sizeof(server_name_ext));
|
||||
mg_iobuf_add(wio, wio->len, hostname, hostnamesz);
|
||||
if (mg_iobuf_add(wio, wio->len, server_name_ext, sizeof(server_name_ext)) ==
|
||||
0 ||
|
||||
mg_iobuf_add(wio, wio->len, hostname, hostnamesz) == 0)
|
||||
return false;
|
||||
mg_sha256_update(&tls->sha256, server_name_ext, sizeof(server_name_ext));
|
||||
mg_sha256_update(&tls->sha256, (uint8_t *) hostname, hostnamesz);
|
||||
}
|
||||
|
||||
// change cipher message
|
||||
mg_iobuf_add(wio, wio->len, (const char *) "\x14\x03\x03\x00\x01\x01", 6);
|
||||
if (mg_iobuf_add(wio, wio->len, (const char *) "\x14\x03\x03\x00\x01\x01",
|
||||
6) == 0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int mg_tls_client_recv_hello(struct mg_connection *c) {
|
||||
@@ -1513,7 +1523,7 @@ static int mg_tls_client_recv_finish(struct mg_connection *c) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mg_tls_client_send_finish(struct mg_connection *c) {
|
||||
static bool mg_tls_client_send_finish(struct mg_connection *c) {
|
||||
struct tls_data *tls = (struct tls_data *) c->tls;
|
||||
mg_sha256_ctx sha256;
|
||||
uint8_t hash[32];
|
||||
@@ -1521,56 +1531,46 @@ static void mg_tls_client_send_finish(struct mg_connection *c) {
|
||||
memmove(&sha256, &tls->sha256, sizeof(mg_sha256_ctx));
|
||||
mg_sha256_final(hash, &sha256);
|
||||
mg_hmac_sha256(finish + 4, tls->enc.client_finished_key, 32, hash, 32);
|
||||
mg_tls_encrypt(c, finish, sizeof(finish), MG_TLS_HANDSHAKE);
|
||||
return mg_tls_encrypt(c, finish, sizeof(finish), MG_TLS_HANDSHAKE);
|
||||
}
|
||||
|
||||
static void mg_tls_client_handshake(struct mg_connection *c) {
|
||||
static bool mg_tls_client_handshake(struct mg_connection *c) {
|
||||
struct tls_data *tls = (struct tls_data *) c->tls;
|
||||
switch (tls->state) {
|
||||
case MG_TLS_STATE_CLIENT_START:
|
||||
mg_tls_client_send_hello(c);
|
||||
if (!mg_tls_client_send_hello(c)) return false;
|
||||
tls->state = MG_TLS_STATE_CLIENT_WAIT_SH;
|
||||
// Fallthrough
|
||||
case MG_TLS_STATE_CLIENT_WAIT_SH:
|
||||
if (mg_tls_client_recv_hello(c) < 0) {
|
||||
break;
|
||||
}
|
||||
if (mg_tls_client_recv_hello(c) < 0) break;
|
||||
tls->state = MG_TLS_STATE_CLIENT_WAIT_EE;
|
||||
// Fallthrough
|
||||
case MG_TLS_STATE_CLIENT_WAIT_EE:
|
||||
if (mg_tls_client_recv_ext(c) < 0) {
|
||||
break;
|
||||
}
|
||||
if (mg_tls_client_recv_ext(c) < 0) break;
|
||||
tls->state = MG_TLS_STATE_CLIENT_WAIT_CERT;
|
||||
// Fallthrough
|
||||
case MG_TLS_STATE_CLIENT_WAIT_CERT:
|
||||
if (mg_tls_recv_cert(c, true) < 0) {
|
||||
break;
|
||||
}
|
||||
if (mg_tls_recv_cert(c, true) < 0) break;
|
||||
tls->state = MG_TLS_STATE_CLIENT_WAIT_CV;
|
||||
// Fallthrough
|
||||
case MG_TLS_STATE_CLIENT_WAIT_CV:
|
||||
if (mg_tls_recv_cert_verify(c) < 0) {
|
||||
break;
|
||||
}
|
||||
if (mg_tls_recv_cert_verify(c) < 0) break;
|
||||
tls->state = MG_TLS_STATE_CLIENT_WAIT_FINISH;
|
||||
// Fallthrough
|
||||
case MG_TLS_STATE_CLIENT_WAIT_FINISH:
|
||||
if (mg_tls_client_recv_finish(c) < 0) {
|
||||
break;
|
||||
}
|
||||
if (mg_tls_client_recv_finish(c) < 0) break;
|
||||
if (tls->cert_requested && tls->cert_der.len > 0) { // two-way auth
|
||||
// generate application keys at this point, keep using handshake keys
|
||||
struct tls_enc hs_keys = tls->enc;
|
||||
mg_tls_generate_application_keys(c);
|
||||
tls->app_keys = tls->enc;
|
||||
tls->enc = hs_keys;
|
||||
mg_tls_send_cert(c, true);
|
||||
mg_tls_send_cert_verify(c, true);
|
||||
mg_tls_client_send_finish(c);
|
||||
if (!mg_tls_send_cert(c, true) || !mg_tls_send_cert_verify(c, true) ||
|
||||
!mg_tls_client_send_finish(c))
|
||||
return false;
|
||||
tls->enc = tls->app_keys;
|
||||
} else {
|
||||
mg_tls_client_send_finish(c);
|
||||
if (!mg_tls_client_send_finish(c)) return false;
|
||||
mg_tls_generate_application_keys(c);
|
||||
}
|
||||
tls->state = MG_TLS_STATE_CLIENT_CONNECTED;
|
||||
@@ -1581,22 +1581,19 @@ static void mg_tls_client_handshake(struct mg_connection *c) {
|
||||
mg_error(c, "unexpected client state: %d", tls->state);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void mg_tls_server_handshake(struct mg_connection *c) {
|
||||
static bool mg_tls_server_handshake(struct mg_connection *c) {
|
||||
struct tls_data *tls = (struct tls_data *) c->tls;
|
||||
switch (tls->state) {
|
||||
case MG_TLS_STATE_SERVER_START:
|
||||
if (mg_tls_server_recv_hello(c) < 0) {
|
||||
return;
|
||||
}
|
||||
mg_tls_server_send_hello(c);
|
||||
if (mg_tls_server_recv_hello(c) < 0) break;
|
||||
if (!mg_tls_server_send_hello(c)) return false;
|
||||
mg_tls_generate_handshake_keys(c);
|
||||
mg_tls_server_send_ext(c);
|
||||
if (tls->is_twoway) mg_tls_server_send_cert_request(c);
|
||||
mg_tls_send_cert(c, false);
|
||||
mg_tls_send_cert_verify(c, false);
|
||||
mg_tls_server_send_finish(c);
|
||||
if (!mg_tls_server_send_ext(c)) return false;
|
||||
if (tls->is_twoway && !mg_tls_server_send_cert_request(c)) return false;
|
||||
if (!mg_tls_send_cert(c, false) || !mg_tls_send_cert_verify(c, false) || !mg_tls_server_send_finish(c)) return false;
|
||||
if (tls->is_twoway) {
|
||||
// generate application keys at this point, keep using handshake keys
|
||||
struct tls_enc hs_keys = tls->enc;
|
||||
@@ -1609,9 +1606,7 @@ static void mg_tls_server_handshake(struct mg_connection *c) {
|
||||
tls->state = MG_TLS_STATE_SERVER_NEGOTIATED;
|
||||
// fallthrough
|
||||
case MG_TLS_STATE_SERVER_NEGOTIATED:
|
||||
if (mg_tls_server_recv_finish(c) < 0) {
|
||||
return;
|
||||
}
|
||||
if (mg_tls_server_recv_finish(c) < 0) break;
|
||||
if (tls->is_twoway) { // use previously generated keys
|
||||
tls->enc = tls->app_keys;
|
||||
} else { // generate keys now
|
||||
@@ -1619,7 +1614,7 @@ static void mg_tls_server_handshake(struct mg_connection *c) {
|
||||
}
|
||||
tls->state = MG_TLS_STATE_SERVER_CONNECTED;
|
||||
c->is_tls_hs = 0;
|
||||
return;
|
||||
break;
|
||||
case MG_TLS_STATE_SERVER_WAIT_CERT:
|
||||
if (mg_tls_recv_cert(c, false) < 0) break;
|
||||
tls->state = MG_TLS_STATE_SERVER_WAIT_CV;
|
||||
@@ -1632,16 +1627,22 @@ static void mg_tls_server_handshake(struct mg_connection *c) {
|
||||
mg_error(c, "unexpected server state: %d", tls->state);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void mg_tls_handshake(struct mg_connection *c) {
|
||||
struct tls_data *tls = (struct tls_data *) c->tls;
|
||||
long n;
|
||||
bool res;
|
||||
if (c->is_client) {
|
||||
// will clear is_hs when sending last chunk
|
||||
mg_tls_client_handshake(c);
|
||||
res = mg_tls_client_handshake(c);
|
||||
} else {
|
||||
mg_tls_server_handshake(c);
|
||||
res = mg_tls_server_handshake(c);
|
||||
}
|
||||
if (!res) {
|
||||
mg_error(c, "TLS OOM");
|
||||
return;
|
||||
}
|
||||
while (tls->send.len > 0 &&
|
||||
(n = mg_io_send(c, tls->send.buf, tls->send.len)) > 0) {
|
||||
@@ -1778,8 +1779,10 @@ long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) {
|
||||
if (!was_throttled) { // encrypt new data
|
||||
if (len > MG_IO_SIZE) len = MG_IO_SIZE;
|
||||
if (len > 16384) len = 16384;
|
||||
mg_tls_encrypt(c, (const uint8_t *) buf, len, MG_TLS_APP_DATA);
|
||||
} // else, resend outstanding encrypted data in tls->send
|
||||
if (!mg_tls_encrypt(c, (const uint8_t *) buf, len, MG_TLS_APP_DATA))
|
||||
return 0; // returning 0 means an OOM condition (iobuf couldn't resize),
|
||||
// yet this is so far recoverable, let the caller decide
|
||||
} // else, resend outstanding encrypted data in tls->send
|
||||
while (tls->send.len > 0 &&
|
||||
(n = mg_io_send(c, tls->send.buf, tls->send.len)) > 0) {
|
||||
mg_iobuf_del(&tls->send, 0, (size_t) n);
|
||||
|
||||
@@ -292,6 +292,6 @@ size_t mg_ws_wrap(struct mg_connection *c, size_t len, int op) {
|
||||
memmove(p, p - header_len, len); // Shift data
|
||||
memcpy(p - header_len, header, header_len); // Prepend header
|
||||
mg_ws_mask(c, len); // Mask data
|
||||
}
|
||||
return c->send.len;
|
||||
} // returning 0 means an OOM condition (iobuf couldn't resize), yet this is
|
||||
return c->send.len; // so far recoverable, let the caller decide
|
||||
}
|
||||
Reference in new issue
Block a user