Compare commits

..
18 Commits
Author SHA1 Message Date
Sergio R. Caprile 5b6c08acfb unbreak VC98 weird 64-bit handling 2026-11-09 12:00:04 -03:00
Sergio R. Caprile 528be76a23 unbreak tests against cesanta.com 2026-11-09 11:40:26 -03:00
Sergio R. Caprile b6ba6a56d9 Merge commit from fork
avoid cross IP version feeds
2026-11-09 11:18:41 -03:00
Sergio R. Caprile 1efc211a51 Merge commit from fork
change mg_tls_verify_cert_signature() to boolean and enforce consistent return values
2026-11-09 11:16:01 -03:00
Sergio R. Caprile ca5ca3403c change mg_tls_verify_cert_signature() to boolean and enforce consistent return values 2026-11-09 11:12:30 -03:00
Sergio R. Caprile 7a86c401b9 Merge commit from fork
fix checks
2026-11-09 11:04:19 -03:00
Sergio R. Caprile 52cc8ef1d3 Merge commit from fork
honor WS mask
2026-11-09 10:58:29 -03:00
Sergio R. Caprile b975a8e761 Merge commit from fork
honor parse and next return values
2026-11-09 10:55:39 -03:00
Sergio R. Caprile 2d7e44201d honor parse and next return values 2026-11-09 10:53:05 -03:00
Sergio R. Caprile b415c23bc5 Merge commit from fork
fix ws data length handling
2026-11-09 10:12:25 -03:00
Sergio R. Caprile cba6c4f8df Merge commit from fork
add safety checks for month
2026-11-09 10:06:41 -03:00
Sergio R. Caprile 9d6d064b39 Merge commit from fork
Check header table bounds
2026-11-09 09:56:57 -03:00
Sergio R. Caprile fc79fd0be5 avoid cross IP version feeds 2026-11-03 10:59:34 -03:00
Sergio R. Caprile 22a94c0906 fix checks 2026-10-19 11:48:51 -03:00
Sergio R. Caprile f347dbf2b5 honor WS mask 2026-10-09 11:58:25 -03:00
Sergio R. Caprile 39a3478429 fix ws data length handling 2026-09-02 16:28:35 -03:00
Sergio R. Caprile 7fccc2b75f add safety checks for month 2026-09-02 12:51:31 -03:00
robert 07e580d956 Check header table bounds 2026-08-21 15:07:11 +03:00
11 changed files with 185 additions and 117 deletions

No files matched your search

+74 -54
View File
@@ -3913,30 +3913,31 @@ size_t mg_usage(int task, int metric) {
}
#endif
static size_t print_mem_stat(mg_pfn_t fn, void *arg, const char *name,
size_t free, size_t total) {
return mg_xprintf(fn, arg, "{%m:%m,%m:%zu,%m:%zu}", MG_ESC("name"),
MG_ESC(name == NULL ? "" : name), MG_ESC("free"), free,
MG_ESC("total"), total);
}
size_t mg_print_mem_stats(mg_pfn_t fn, void *arg, va_list *ap) {
int i, count;
size_t heap_total = mg_usage(0, MG_USAGE_HEAP_TOTAL);
size_t heap_free = mg_usage(0, MG_USAGE_HEAP_FREE);
static size_t print_stacks(mg_pfn_t fn, void *arg, va_list *ap) {
int i;
int count = (int) mg_usage(-1, MG_USAGE_NUM_TASKS);
size_t len = mg_xprintf(fn, arg, "[");
len += print_mem_stat(fn, arg, "heap", heap_free, heap_total);
count = (int) mg_usage(-1, MG_USAGE_NUM_TASKS);
for (i = 0; i < count; i++) {
const char *name = (const char *) mg_usage(i, MG_USAGE_TASK_NAME);
len += mg_xprintf(fn, arg, ",");
len += print_mem_stat(fn, arg, name, mg_usage(i, MG_USAGE_STACK_FREE),
mg_usage(i, MG_USAGE_STACK_TOTAL));
len += mg_xprintf(fn, arg, "%s{%m:%m,%m:%zu,%m:%zu}",
i == 0 ? "" : ",", MG_ESC("name"),
MG_ESC(name == NULL ? "" : name), MG_ESC("free"),
(size_t) mg_usage(i, MG_USAGE_STACK_FREE), MG_ESC("total"),
(size_t) mg_usage(i, MG_USAGE_STACK_TOTAL));
}
(void) ap;
return len + mg_xprintf(fn, arg, "]");
}
size_t mg_print_mem_stats(mg_pfn_t fn, void *arg, va_list *ap) {
size_t heap_total = (size_t) mg_usage(0, MG_USAGE_HEAP_TOTAL);
size_t heap_free = (size_t) mg_usage(0, MG_USAGE_HEAP_FREE);
(void) ap;
return mg_xprintf(fn, arg, "{%m:%zu,%m:%zu,%m:%M}",
MG_ESC("heap_total"), heap_total, MG_ESC("heap_free"),
heap_free, MG_ESC("stacks"), print_stacks);
}
#if MG_ARCH == MG_ARCH_ESP32
// reusable for other platforms, currently ESP32-only
struct mg_health_coredump {
@@ -4391,6 +4392,12 @@ static bool mg_http_parse_headers(const char *s, const char *end,
// MG_INFO(("--HH [%.*s] [%.*s]", (int) k.len, k.buf, (int) v.len, v.buf));
h[i].name = k, h[i].value = v; // Success. Assign values
}
if (i == max_hdrs &&
!(s < end &&
(s[0] == '\n' ||
(s[0] == '\r' && s + 1 < end && s[1] == '\n')))) {
return false;
}
return true;
}
@@ -10239,6 +10246,7 @@ static struct mg_connection *getpeer(struct mg_mgr *mgr, struct pkt *pkt,
if (!c->is_udp && pkt->tcp && c->loc.port == pkt->tcp->dport &&
((lsn && c->is_listening && !(c->loc.is_ip6 ^ (pkt->ip6 != NULL))) ||
(!lsn && !c->is_listening && c->rem.port == pkt->tcp->sport &&
!(c->loc.is_ip6 ^ (pkt->ip6 != NULL)) && // IP or IPv6 to same dest
((!c->loc.is_ip6 && c->rem.addr.ip4 == pkt->ip->src)
#if MG_ENABLE_IPV6
|| (c->loc.is_ip6 && MG_IP6MATCH(c->rem.addr.ip6, pkt->ip6->src))
@@ -19040,7 +19048,7 @@ static int mg_tls_server_recv_hello(struct mg_connection *c) {
if (!is_retry || rio->buf[0] != MG_TLS_CHANGE_CIPHER) break;
mg_tls_drop_record(c); // compatibility mode CCS before 2nd ClientHello
}
if (rio->buf[0] != MG_TLS_HANDSHAKE || rio->buf[5] != MG_TLS_CLIENT_HELLO) {
if (rio->buf[0] != MG_TLS_HANDSHAKE || rio->len < 6 || rio->buf[5] != MG_TLS_CLIENT_HELLO) {
mg_error(c, "not a client hello packet");
return -1;
}
@@ -19070,17 +19078,21 @@ static int mg_tls_server_recv_hello(struct mg_connection *c) {
uint16_t k;
uint16_t key_exchange_len;
uint8_t *key_exchange;
uint16_t n = MG_LOAD_BE16(ext + j + 2);
uint16_t n;
if ((ext_len - j) < 4) goto fail;
n = MG_LOAD_BE16(ext + j + 2);
if (((uint32_t) n + j + 4) > ext_len) goto fail;
if (MG_LOAD_BE16(ext + j) != 0x0033) { // not a key share extension, ignore
j += (uint16_t) (n + 4);
continue;
}
if (n < 2) goto fail;
key_exchange_len = MG_LOAD_BE16(ext + j + 4);
key_exchange = ext + j + 6;
if ((key_exchange + key_exchange_len) > ((uint8_t *) rio->buf + rio->len))
if (key_exchange_len > n - 2)
goto fail;
for (k = 0; k < key_exchange_len;) {
if ((key_exchange_len - k) < 4) goto fail;
uint16_t m = MG_LOAD_BE16(key_exchange + k + 2);
uint16_t group = MG_LOAD_BE16(key_exchange + k);
if (((uint32_t) m + k + 4) > key_exchange_len) goto fail;
@@ -20071,41 +20083,39 @@ static int mg_tls_verify_cert_san(const uint8_t *der, size_t dersz,
return -1;
}
static int mg_tls_verify_cert_signature(const struct mg_tls_cert *cert,
static void unpadlen(uint8_t *sig, struct mg_der_tlv *a, struct mg_der_tlv *b,
uint32_t n) {
if (a->len > n) a->value += (a->len - n), a->len = n;
if (b->len > n) b->value += (b->len - n), b->len = n;
memset(sig, 0, n - a->len); // short encoding
memmove(sig + (n - a->len), a->value, a->len);
memset(sig + n, 0, n - b->len);
memmove(sig + n + (n - b->len), b->value, b->len);
}
static bool mg_tls_verify_cert_signature(const struct mg_tls_cert *cert,
const struct mg_tls_cert *issuer) {
if (issuer->is_ec_pubkey) {
uint8_t sig[128];
struct mg_der_tlv seq = {0, 0, 0}, a = {0, 0, 0}, b = {0, 0, 0};
mg_der_parse((uint8_t *) cert->sig.buf, cert->sig.len, &seq);
mg_der_next(&seq, &a);
mg_der_next(&seq, &b);
if (a.len == 0 || b.len == 0) {
if (mg_der_parse((uint8_t *) cert->sig.buf, cert->sig.len, &seq) <= 0 ||
seq.type != 0x30 || mg_der_next(&seq, &a) <= 0 || a.type != 0x02 ||
mg_der_next(&seq, &b) <= 0 || b.type != 0x02 || a.len == 0 ||
b.len == 0 || seq.len != 0) {
MG_ERROR(("cert verification error"));
return 0;
return false;
}
if (issuer->pubkey.len == 64) {
const uint32_t N = 32;
if (a.len > N) a.value += (a.len - N), a.len = N; // padding
if (b.len > N) b.value += (b.len - N), b.len = N;
memset(sig, 0, N - a.len); // short encoding
memmove(sig + (N - a.len), a.value, a.len);
memset(sig + N, 0, N - b.len);
memmove(sig + N + (N - b.len), b.value, b.len);
unpadlen(sig, &a, &b, 32);
return mg_p256_verify((uint8_t *) issuer->pubkey.buf, cert->tbshash,
cert->tbshashsz, sig);
cert->tbshashsz, sig) == 1;
} else if (issuer->pubkey.len == 96) {
const uint32_t N = 48;
if (a.len > N) a.value += (a.len - N), a.len = N; // padding
if (b.len > N) b.value += (b.len - N), b.len = N;
memset(sig, 0, N - a.len); // short encoding
memmove(sig + (N - a.len), a.value, a.len);
memset(sig + N, 0, N - b.len);
memmove(sig + N + (N - b.len), b.value, b.len);
unpadlen(sig, &a, &b, 48);
return mg_p384_verify((uint8_t *) issuer->pubkey.buf, cert->tbshash,
cert->tbshashsz, sig);
cert->tbshashsz, sig) == 1;
} else {
MG_ERROR(("unsupported public key length: %d", issuer->pubkey.len));
return 0;
return false;
}
} else {
const uint8_t *n;
@@ -20117,14 +20127,16 @@ static int mg_tls_verify_cert_signature(const struct mg_tls_cert *cert,
mg_der_next(&seq, &modulus) <= 0 || modulus.type != 2 ||
modulus.len == 0 || mg_der_next(&seq, &exponent) <= 0 ||
exponent.type != 2 || exponent.len == 0) {
return -1;
MG_ERROR(("failed to parse RSA issuer public key"));
return false;
}
n = modulus.value, nlen = mg_rsa_trim_len(&n, modulus.len);
if (nlen > sizeof(sig2) || cert->tbshashsz > nlen ||
mg_rsa_mod_pow(modulus.value, modulus.len, exponent.value,
exponent.len, (uint8_t *) cert->sig.buf, cert->sig.len,
sig2, nlen) != 0) {
return 0;
MG_ERROR(("failed to process RSA cert sig"));
return false;
}
return mg_rsa_pkcs_verify(sig2, nlen, cert->tbshash, cert->tbshashsz);
@@ -30137,7 +30149,7 @@ uint64_t mg_timegm(unsigned int year, unsigned int month, unsigned int day,
const uint64_t day_secs = 86400;
const uint64_t year_secs = 31536000;
unsigned int y, ly;
if (year < 1970) return 0;
if (year < 1970 || month == 0 || month > 12) return 0;
y = year - 1900;
ly = month > 2 ? y + 1 : y;
return (uint64_t) sec + 60 * min + 3600 * hour +
@@ -30502,12 +30514,14 @@ static uint32_t be32(const uint8_t *p) {
(((uint32_t) p[1]) << 16) | (((uint32_t) p[0]) << 24);
}
static size_t ws_process(uint8_t *buf, size_t len, struct ws_msg *msg) {
static size_t ws_process(uint8_t *buf, size_t len, struct ws_msg *msg,
bool *masked) {
size_t i, n = 0, mask_len = 0;
memset(msg, 0, sizeof(*msg));
if (len >= 2) {
n = buf[1] & 0x7f; // Frame length
mask_len = buf[1] & 128 ? 4 : 0; // last bit is a mask bit
*masked = (buf[1] & 0x80) != 0; // b7 Mask, b6:0 Frame length
n = buf[1] & 0x7f;
mask_len = *masked ? 4 : 0;
msg->flags = buf[0];
if (n < 126 && len >= mask_len) {
msg->data_len = n;
@@ -30516,14 +30530,14 @@ static size_t ws_process(uint8_t *buf, size_t len, struct ws_msg *msg) {
msg->header_len = 4 + mask_len;
msg->data_len = (((size_t) buf[2]) << 8) | buf[3];
} else if (len >= 10 + mask_len) {
// Sanity check and protect the boundary check below: data_len should not
// be larger than 1 GB, so it fits in 32-bits
uint32_t hi = be32(buf + 2), lo = be32(buf + 6);
if (hi != 0 || lo > 1024U * 1024U * 1024U) return 0;
msg->data_len = (size_t) lo;
msg->header_len = 10 + mask_len;
msg->data_len =
(size_t) (((uint64_t) be32(buf + 2) << 32) + be32(buf + 6));
}
}
// Sanity check, and integer overflow protection for the boundary check below
// data_len should not be larger than 1 Gb
if (msg->data_len > 1024 * 1024 * 1024) return 0;
if (msg->header_len + msg->data_len > len) return 0;
if (mask_len > 0) {
uint8_t *p = buf + msg->header_len, *m = p - mask_len;
@@ -30604,17 +30618,23 @@ static bool mg_ws_client_handshake(struct mg_connection *c) {
static void mg_ws_cb(struct mg_connection *c, int ev, void *ev_data) {
struct ws_msg msg;
bool msk; // is this frame masked ?
size_t ofs = (size_t) c->pfn_data;
// assert(ofs < c->recv.len);
if (ev == MG_EV_READ) {
if (c->is_client && !c->is_websocket && mg_ws_client_handshake(c)) return;
while (ws_process(c->recv.buf + ofs, c->recv.len - ofs, &msg) > 0) {
while (ws_process(c->recv.buf + ofs, c->recv.len - ofs, &msg, &msk) > 0) {
char *s = (char *) c->recv.buf + ofs + msg.header_len;
struct mg_ws_message m;
size_t len;
uint8_t final, op;
if (msk == c->is_client) {
mg_error(c, c->is_client ? "masked WS server frame"
: "unmasked WS client frame");
return;
}
m.data.buf = s, m.data.len = msg.data_len, m.flags = msg.flags;
len = msg.header_len + msg.data_len;
final = msg.flags & 128;
+3 -3
View File
@@ -4516,8 +4516,8 @@ size_t mg_usage(int task, int metric);
#define MG_HEALTH_INIT() mg_health_init()
extern size_t mg_print_crash_record(mg_pfn_t, void *, va_list *);
// JSON memory statistics via mg_usage(), suitable for %M: an array of
// {name, free, total} entries, starting with "heap", then task stacks
// JSON memory statistics via mg_usage(), suitable for %M: heap_total, heap_free,
// and a stacks array of {name, free, total}
// A zero total means unknown
extern size_t mg_print_mem_stats(mg_pfn_t, void *, va_list *);
extern void mg_health_init(void);
@@ -4540,7 +4540,7 @@ static inline size_t mg_print_crash_record(mg_pfn_t fn, void *arg,
}
static inline size_t mg_print_mem_stats(mg_pfn_t fn, void *arg, va_list *ap) {
(void) ap;
return mg_xprintf(fn, arg, "[]");
return mg_xprintf(fn, arg, "{}");
}
static inline bool mg_health_coredump_begin(size_t *size) {
(void) size;
+17 -16
View File
@@ -58,30 +58,31 @@ size_t mg_usage(int task, int metric) {
}
#endif
static size_t print_mem_stat(mg_pfn_t fn, void *arg, const char *name,
size_t free, size_t total) {
return mg_xprintf(fn, arg, "{%m:%m,%m:%zu,%m:%zu}", MG_ESC("name"),
MG_ESC(name == NULL ? "" : name), MG_ESC("free"), free,
MG_ESC("total"), total);
}
size_t mg_print_mem_stats(mg_pfn_t fn, void *arg, va_list *ap) {
int i, count;
size_t heap_total = mg_usage(0, MG_USAGE_HEAP_TOTAL);
size_t heap_free = mg_usage(0, MG_USAGE_HEAP_FREE);
static size_t print_stacks(mg_pfn_t fn, void *arg, va_list *ap) {
int i;
int count = (int) mg_usage(-1, MG_USAGE_NUM_TASKS);
size_t len = mg_xprintf(fn, arg, "[");
len += print_mem_stat(fn, arg, "heap", heap_free, heap_total);
count = (int) mg_usage(-1, MG_USAGE_NUM_TASKS);
for (i = 0; i < count; i++) {
const char *name = (const char *) mg_usage(i, MG_USAGE_TASK_NAME);
len += mg_xprintf(fn, arg, ",");
len += print_mem_stat(fn, arg, name, mg_usage(i, MG_USAGE_STACK_FREE),
mg_usage(i, MG_USAGE_STACK_TOTAL));
len += mg_xprintf(fn, arg, "%s{%m:%m,%m:%zu,%m:%zu}",
i == 0 ? "" : ",", MG_ESC("name"),
MG_ESC(name == NULL ? "" : name), MG_ESC("free"),
(size_t) mg_usage(i, MG_USAGE_STACK_FREE), MG_ESC("total"),
(size_t) mg_usage(i, MG_USAGE_STACK_TOTAL));
}
(void) ap;
return len + mg_xprintf(fn, arg, "]");
}
size_t mg_print_mem_stats(mg_pfn_t fn, void *arg, va_list *ap) {
size_t heap_total = (size_t) mg_usage(0, MG_USAGE_HEAP_TOTAL);
size_t heap_free = (size_t) mg_usage(0, MG_USAGE_HEAP_FREE);
(void) ap;
return mg_xprintf(fn, arg, "{%m:%zu,%m:%zu,%m:%M}",
MG_ESC("heap_total"), heap_total, MG_ESC("heap_free"),
heap_free, MG_ESC("stacks"), print_stacks);
}
#if MG_ARCH == MG_ARCH_ESP32
// reusable for other platforms, currently ESP32-only
struct mg_health_coredump {
+3 -3
View File
@@ -139,8 +139,8 @@ size_t mg_usage(int task, int metric);
#define MG_HEALTH_INIT() mg_health_init()
extern size_t mg_print_crash_record(mg_pfn_t, void *, va_list *);
// JSON memory statistics via mg_usage(), suitable for %M: an array of
// {name, free, total} entries, starting with "heap", then task stacks
// JSON memory statistics via mg_usage(), suitable for %M: heap_total, heap_free,
// and a stacks array of {name, free, total}
// A zero total means unknown
extern size_t mg_print_mem_stats(mg_pfn_t, void *, va_list *);
extern void mg_health_init(void);
@@ -163,7 +163,7 @@ static inline size_t mg_print_crash_record(mg_pfn_t fn, void *arg,
}
static inline size_t mg_print_mem_stats(mg_pfn_t fn, void *arg, va_list *ap) {
(void) ap;
return mg_xprintf(fn, arg, "[]");
return mg_xprintf(fn, arg, "{}");
}
static inline bool mg_health_coredump_begin(size_t *size) {
(void) size;
+6
View File
@@ -265,6 +265,12 @@ static bool mg_http_parse_headers(const char *s, const char *end,
// MG_INFO(("--HH [%.*s] [%.*s]", (int) k.len, k.buf, (int) v.len, v.buf));
h[i].name = k, h[i].value = v; // Success. Assign values
}
if (i == max_hdrs &&
!(s < end &&
(s[0] == '\n' ||
(s[0] == '\r' && s + 1 < end && s[1] == '\n')))) {
return false;
}
return true;
}
+1
View File
@@ -602,6 +602,7 @@ static struct mg_connection *getpeer(struct mg_mgr *mgr, struct pkt *pkt,
if (!c->is_udp && pkt->tcp && c->loc.port == pkt->tcp->dport &&
((lsn && c->is_listening && !(c->loc.is_ip6 ^ (pkt->ip6 != NULL))) ||
(!lsn && !c->is_listening && c->rem.port == pkt->tcp->sport &&
!(c->loc.is_ip6 ^ (pkt->ip6 != NULL)) && // IP or IPv6 to same dest
((!c->loc.is_ip6 && c->rem.addr.ip4 == pkt->ip->src)
#if MG_ENABLE_IPV6
|| (c->loc.is_ip6 && MG_IP6MATCH(c->rem.addr.ip6, pkt->ip6->src))
+32 -28
View File
@@ -681,7 +681,7 @@ static int mg_tls_server_recv_hello(struct mg_connection *c) {
if (!is_retry || rio->buf[0] != MG_TLS_CHANGE_CIPHER) break;
mg_tls_drop_record(c); // compatibility mode CCS before 2nd ClientHello
}
if (rio->buf[0] != MG_TLS_HANDSHAKE || rio->buf[5] != MG_TLS_CLIENT_HELLO) {
if (rio->buf[0] != MG_TLS_HANDSHAKE || rio->len < 6 || rio->buf[5] != MG_TLS_CLIENT_HELLO) {
mg_error(c, "not a client hello packet");
return -1;
}
@@ -711,17 +711,21 @@ static int mg_tls_server_recv_hello(struct mg_connection *c) {
uint16_t k;
uint16_t key_exchange_len;
uint8_t *key_exchange;
uint16_t n = MG_LOAD_BE16(ext + j + 2);
uint16_t n;
if ((ext_len - j) < 4) goto fail;
n = MG_LOAD_BE16(ext + j + 2);
if (((uint32_t) n + j + 4) > ext_len) goto fail;
if (MG_LOAD_BE16(ext + j) != 0x0033) { // not a key share extension, ignore
j += (uint16_t) (n + 4);
continue;
}
if (n < 2) goto fail;
key_exchange_len = MG_LOAD_BE16(ext + j + 4);
key_exchange = ext + j + 6;
if ((key_exchange + key_exchange_len) > ((uint8_t *) rio->buf + rio->len))
if (key_exchange_len > n - 2)
goto fail;
for (k = 0; k < key_exchange_len;) {
if ((key_exchange_len - k) < 4) goto fail;
uint16_t m = MG_LOAD_BE16(key_exchange + k + 2);
uint16_t group = MG_LOAD_BE16(key_exchange + k);
if (((uint32_t) m + k + 4) > key_exchange_len) goto fail;
@@ -1712,41 +1716,39 @@ static int mg_tls_verify_cert_san(const uint8_t *der, size_t dersz,
return -1;
}
static int mg_tls_verify_cert_signature(const struct mg_tls_cert *cert,
static void unpadlen(uint8_t *sig, struct mg_der_tlv *a, struct mg_der_tlv *b,
uint32_t n) {
if (a->len > n) a->value += (a->len - n), a->len = n;
if (b->len > n) b->value += (b->len - n), b->len = n;
memset(sig, 0, n - a->len); // short encoding
memmove(sig + (n - a->len), a->value, a->len);
memset(sig + n, 0, n - b->len);
memmove(sig + n + (n - b->len), b->value, b->len);
}
static bool mg_tls_verify_cert_signature(const struct mg_tls_cert *cert,
const struct mg_tls_cert *issuer) {
if (issuer->is_ec_pubkey) {
uint8_t sig[128];
struct mg_der_tlv seq = {0, 0, 0}, a = {0, 0, 0}, b = {0, 0, 0};
mg_der_parse((uint8_t *) cert->sig.buf, cert->sig.len, &seq);
mg_der_next(&seq, &a);
mg_der_next(&seq, &b);
if (a.len == 0 || b.len == 0) {
if (mg_der_parse((uint8_t *) cert->sig.buf, cert->sig.len, &seq) <= 0 ||
seq.type != 0x30 || mg_der_next(&seq, &a) <= 0 || a.type != 0x02 ||
mg_der_next(&seq, &b) <= 0 || b.type != 0x02 || a.len == 0 ||
b.len == 0 || seq.len != 0) {
MG_ERROR(("cert verification error"));
return 0;
return false;
}
if (issuer->pubkey.len == 64) {
const uint32_t N = 32;
if (a.len > N) a.value += (a.len - N), a.len = N; // padding
if (b.len > N) b.value += (b.len - N), b.len = N;
memset(sig, 0, N - a.len); // short encoding
memmove(sig + (N - a.len), a.value, a.len);
memset(sig + N, 0, N - b.len);
memmove(sig + N + (N - b.len), b.value, b.len);
unpadlen(sig, &a, &b, 32);
return mg_p256_verify((uint8_t *) issuer->pubkey.buf, cert->tbshash,
cert->tbshashsz, sig);
cert->tbshashsz, sig) == 1;
} else if (issuer->pubkey.len == 96) {
const uint32_t N = 48;
if (a.len > N) a.value += (a.len - N), a.len = N; // padding
if (b.len > N) b.value += (b.len - N), b.len = N;
memset(sig, 0, N - a.len); // short encoding
memmove(sig + (N - a.len), a.value, a.len);
memset(sig + N, 0, N - b.len);
memmove(sig + N + (N - b.len), b.value, b.len);
unpadlen(sig, &a, &b, 48);
return mg_p384_verify((uint8_t *) issuer->pubkey.buf, cert->tbshash,
cert->tbshashsz, sig);
cert->tbshashsz, sig) == 1;
} else {
MG_ERROR(("unsupported public key length: %d", issuer->pubkey.len));
return 0;
return false;
}
} else {
const uint8_t *n;
@@ -1758,14 +1760,16 @@ static int mg_tls_verify_cert_signature(const struct mg_tls_cert *cert,
mg_der_next(&seq, &modulus) <= 0 || modulus.type != 2 ||
modulus.len == 0 || mg_der_next(&seq, &exponent) <= 0 ||
exponent.type != 2 || exponent.len == 0) {
return -1;
MG_ERROR(("failed to parse RSA issuer public key"));
return false;
}
n = modulus.value, nlen = mg_rsa_trim_len(&n, modulus.len);
if (nlen > sizeof(sig2) || cert->tbshashsz > nlen ||
mg_rsa_mod_pow(modulus.value, modulus.len, exponent.value,
exponent.len, (uint8_t *) cert->sig.buf, cert->sig.len,
sig2, nlen) != 0) {
return 0;
MG_ERROR(("failed to process RSA cert sig"));
return false;
}
return mg_rsa_pkcs_verify(sig2, nlen, cert->tbshash, cert->tbshashsz);
+1 -1
View File
@@ -17,7 +17,7 @@ uint64_t mg_timegm(unsigned int year, unsigned int month, unsigned int day,
const uint64_t day_secs = 86400;
const uint64_t year_secs = 31536000;
unsigned int y, ly;
if (year < 1970) return 0;
if (year < 1970 || month == 0 || month > 12) return 0;
y = year - 1900;
ly = month > 2 ? y + 1 : y;
return (uint64_t) sec + 60 * min + 3600 * hour +
+17 -9
View File
@@ -63,12 +63,14 @@ static uint32_t be32(const uint8_t *p) {
(((uint32_t) p[1]) << 16) | (((uint32_t) p[0]) << 24);
}
static size_t ws_process(uint8_t *buf, size_t len, struct ws_msg *msg) {
static size_t ws_process(uint8_t *buf, size_t len, struct ws_msg *msg,
bool *masked) {
size_t i, n = 0, mask_len = 0;
memset(msg, 0, sizeof(*msg));
if (len >= 2) {
n = buf[1] & 0x7f; // Frame length
mask_len = buf[1] & 128 ? 4 : 0; // last bit is a mask bit
*masked = (buf[1] & 0x80) != 0; // b7 Mask, b6:0 Frame length
n = buf[1] & 0x7f;
mask_len = *masked ? 4 : 0;
msg->flags = buf[0];
if (n < 126 && len >= mask_len) {
msg->data_len = n;
@@ -77,14 +79,14 @@ static size_t ws_process(uint8_t *buf, size_t len, struct ws_msg *msg) {
msg->header_len = 4 + mask_len;
msg->data_len = (((size_t) buf[2]) << 8) | buf[3];
} else if (len >= 10 + mask_len) {
// Sanity check and protect the boundary check below: data_len should not
// be larger than 1 GB, so it fits in 32-bits
uint32_t hi = be32(buf + 2), lo = be32(buf + 6);
if (hi != 0 || lo > 1024U * 1024U * 1024U) return 0;
msg->data_len = (size_t) lo;
msg->header_len = 10 + mask_len;
msg->data_len =
(size_t) (((uint64_t) be32(buf + 2) << 32) + be32(buf + 6));
}
}
// Sanity check, and integer overflow protection for the boundary check below
// data_len should not be larger than 1 Gb
if (msg->data_len > 1024 * 1024 * 1024) return 0;
if (msg->header_len + msg->data_len > len) return 0;
if (mask_len > 0) {
uint8_t *p = buf + msg->header_len, *m = p - mask_len;
@@ -165,17 +167,23 @@ static bool mg_ws_client_handshake(struct mg_connection *c) {
static void mg_ws_cb(struct mg_connection *c, int ev, void *ev_data) {
struct ws_msg msg;
bool msk; // is this frame masked ?
size_t ofs = (size_t) c->pfn_data;
// assert(ofs < c->recv.len);
if (ev == MG_EV_READ) {
if (c->is_client && !c->is_websocket && mg_ws_client_handshake(c)) return;
while (ws_process(c->recv.buf + ofs, c->recv.len - ofs, &msg) > 0) {
while (ws_process(c->recv.buf + ofs, c->recv.len - ofs, &msg, &msk) > 0) {
char *s = (char *) c->recv.buf + ofs + msg.header_len;
struct mg_ws_message m;
size_t len;
uint8_t final, op;
if (msk == c->is_client) {
mg_error(c, c->is_client ? "masked WS server frame"
: "unmasked WS client frame");
return;
}
m.data.buf = s, m.data.len = msg.data_len, m.flags = msg.flags;
len = msg.header_len + msg.data_len;
final = msg.flags & 128;
+1 -1
View File
@@ -1,6 +1,6 @@
SRCS = mongoose.c unit_test.c packed_fs.c
HDRS = $(wildcard ../src/*.h) $(wildcard ../src/drivers/*.h)
DEFS ?= -DMG_MAX_HTTP_HEADERS=7 -DMG_ENABLE_LINES -DMG_ENABLE_SSI=1 -DMG_ENABLE_ASSERT=1 -DMG_ENABLE_IPV6=$(IPV6) -DMG_ENABLE_MDASH=$(MDASH)
DEFS ?= -DMG_ENABLE_LINES -DMG_ENABLE_SSI=1 -DMG_ENABLE_ASSERT=1 -DMG_ENABLE_IPV6=$(IPV6) -DMG_ENABLE_MDASH=$(MDASH)
WARN ?= -pedantic -W -Wall -Werror -Wshadow -Wdouble-promotion -fno-common -Wconversion -Wundef
OPTS ?= -O3 -g3
INCS ?= -Isrc -I.
+30 -2
View File
@@ -1987,13 +1987,41 @@ static void test_http_parse(void) {
}
{
const char *s = "a b HTTP/1.0\na:1\nb:2\nc:3\nd:4\ne:5\nf:6\ng:7\nh:8\n\n";
const char *s = "a b HTTP/1.0\na:1\nb:2\nc:3\nd:4\ne:5\nf:6\ng:7\n\n";
ASSERT(mg_http_parse(s, strlen(s), &req) == (int) strlen(s));
ASSERT((v = mg_http_get_header(&req, "e")) != NULL);
ASSERT(vcmp(*v, "5"));
ASSERT((v = mg_http_get_header(&req, "g")) != NULL);
ASSERT(vcmp(*v, "7"));
ASSERT((v = mg_http_get_header(&req, "h")) == NULL); // MG_MAX_HTTP_HEADERS
{
char headers[MG_MAX_HTTP_HEADERS * 10 + 32];
size_t i, len = 0;
len += (size_t) mg_snprintf(headers + len, sizeof(headers) - len,
"a b HTTP/1.0\n");
for (i = 0; i <= MG_MAX_HTTP_HEADERS; i++) {
len += (size_t) mg_snprintf(headers + len, sizeof(headers) - len,
"h%u:1\n", (unsigned) i);
}
len += (size_t) mg_snprintf(headers + len, sizeof(headers) - len, "\n");
ASSERT(mg_http_parse(headers, len, &req) == -1); // MG_MAX_HTTP_HEADERS
}
}
{
char headers[MG_MAX_HTTP_HEADERS * 10 + 128];
size_t i, len = 0;
len += (size_t) mg_snprintf(headers + len, sizeof(headers) - len,
"GET /public HTTP/1.1\r\n");
for (i = 0; i < MG_MAX_HTTP_HEADERS; i++) {
len += (size_t) mg_snprintf(headers + len, sizeof(headers) - len,
"h%u:1\r\n", (unsigned) i);
}
len += (size_t) mg_snprintf(headers + len, sizeof(headers) - len,
"Content-Length: 43\r\n\r\n"
"POST /admin HTTP/1.1\r\n"
"Content-Length: 0\r\n\r\n");
ASSERT(mg_http_parse(headers, len, &req) == -1);
}
{