diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 8f500b6c..68386ad1 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -54,6 +54,18 @@ jobs: - uses: actions/checkout@v4 with: { fetch-depth: 2 } - run: if [ "${{ matrix.target }}" == "mip_tap_test" ]; then ./test/setup_ga_network.sh ; fi && sudo apt -y update ; sudo apt -y install libmbedtls-dev libwolfssl-dev && make -C test ${{ matrix.target }} IPV6=0 + mip89: + runs-on: ubuntu-latest + strategy: + fail-fast: false + max-parallel: 1 # no parallel runs, to minimize MQTT errors + matrix: + target: [mip_vc98] + name: mip89 ${{ matrix.target }} + steps: + - uses: actions/checkout@v4 + with: { fetch-depth: 2 } + - run: make -C test ${{ matrix.target }} s390: runs-on: ubuntu-latest strategy: diff --git a/mongoose.c b/mongoose.c index 8339491a..37146b5d 100644 --- a/mongoose.c +++ b/mongoose.c @@ -4327,7 +4327,7 @@ static uint32_t csumup(uint32_t sum, const void *buf, size_t len) { static uint16_t csumfin(uint32_t sum) { while (sum >> 16) sum = (sum & 0xffff) + (sum >> 16); - return mg_htons(~sum & 0xffff); + return mg_htons((uint16_t) ((uint16_t) ~sum & 0xffff)); } static uint16_t ipcsum(const void *buf, size_t len) { @@ -4414,12 +4414,13 @@ static bool tx_udp(struct mg_tcpip_if *ifp, uint8_t *mac_dst, uint32_t ip_src, tx_ip(ifp, mac_dst, 17, ip_src, ip_dst, len + sizeof(struct udp)); struct udp *udp = (struct udp *) (ip + 1); size_t eth_len; + uint32_t cs; // MG_DEBUG(("UDP XX LEN %d %d", (int) len, (int) ifp->tx.len)); udp->sport = sport; udp->dport = dport; udp->len = mg_htons((uint16_t) (sizeof(*udp) + len)); udp->csum = 0; - uint32_t cs = csumup(0, udp, sizeof(*udp)); + cs = csumup(0, udp, sizeof(*udp)); cs = csumup(cs, buf, len); cs = csumup(cs, &ip->src, sizeof(ip->src)); cs = csumup(cs, &ip->dst, sizeof(ip->dst)); @@ -4503,7 +4504,8 @@ static struct mg_connection *getpeer(struct mg_mgr *mgr, struct pkt *pkt, break; if (c->is_udp && pkt->udp && c->loc.port == pkt->udp->dport) break; if (!c->is_udp && pkt->tcp && c->loc.port == pkt->tcp->dport && - lsn == c->is_listening && (lsn || c->rem.port == pkt->tcp->sport)) + lsn == (bool) c->is_listening && + (lsn || c->rem.port == pkt->tcp->sport)) break; } return c; @@ -4558,10 +4560,12 @@ static void rx_icmp(struct mg_tcpip_if *ifp, struct pkt *pkt) { if (pkt->icmp->type == 8 && pkt->ip != NULL && pkt->ip->dst == ifp->ip) { size_t hlen = sizeof(struct eth) + sizeof(struct ip) + sizeof(struct icmp); size_t space = ifp->tx.len - hlen, plen = pkt->pay.len; + struct ip *ip; + struct icmp *icmp; if (plen > space) plen = space; - struct ip *ip = tx_ip(ifp, pkt->eth->src, 1, ifp->ip, pkt->ip->src, - sizeof(struct icmp) + plen); - struct icmp *icmp = (struct icmp *) (ip + 1); + ip = tx_ip(ifp, pkt->eth->src, 1, ifp->ip, pkt->ip->src, + sizeof(*icmp) + plen); + icmp = (struct icmp *) (ip + 1); memset(icmp, 0, sizeof(*icmp)); // Set csum to 0 memcpy(icmp + 1, pkt->pay.buf, plen); // Copy RX payload to TX icmp->csum = ipcsum(icmp, sizeof(*icmp) + plen); @@ -4608,13 +4612,13 @@ static void rx_dhcp_client(struct mg_tcpip_if *ifp, struct pkt *pkt) { ifp->state = MG_TCPIP_STATE_REQ; // REQUESTING state } else if (msgtype == 5) { // DHCPACK if (ifp->state == MG_TCPIP_STATE_REQ && ip && gw && lease) { // got an IP + uint64_t rand; ifp->lease_expire = ifp->now + lease * 1000; MG_INFO(("Lease: %u sec (%lld)", lease, ifp->lease_expire / 1000)); // assume DHCP server = router until ARP resolves memcpy(ifp->gwmac, pkt->eth->src, sizeof(ifp->gwmac)); ifp->ip = ip, ifp->gw = gw, ifp->mask = mask; ifp->state = MG_TCPIP_STATE_IP; // BOUND state - uint64_t rand; mg_random(&rand, sizeof(rand)); srand((unsigned int) (rand + mg_millis())); if (ifp->enable_req_dns && dns != 0) @@ -4633,9 +4637,9 @@ static void rx_dhcp_client(struct mg_tcpip_if *ifp, struct pkt *pkt) { static void rx_dhcp_server(struct mg_tcpip_if *ifp, struct pkt *pkt) { uint8_t op = 0, *p = pkt->dhcp->options, *end = (uint8_t *) &pkt->raw.buf[pkt->raw.len]; - if (end < (uint8_t *) (pkt->dhcp + 1)) return; // struct dhcp *req = pkt->dhcp; struct dhcp res = {2, 1, 6, 0, 0, 0, 0, 0, 0, 0, 0, {0}, 0, {0}}; + if (end < (uint8_t *) (pkt->dhcp + 1)) return; res.yiaddr = ifp->ip; ((uint8_t *) (&res.yiaddr))[3]++; // Offer our IP + 1 while (p + 1 < end && p[0] != 255) { // Parse options @@ -4674,9 +4678,9 @@ static void rx_udp(struct mg_tcpip_if *ifp, struct pkt *pkt) { if (c == NULL) { // No UDP listener on this port. Should send ICMP, but keep silent. } else { + struct connstate *s = (struct connstate *) (c + 1); c->rem.port = pkt->udp->sport; memcpy(c->rem.ip, &pkt->ip->src, sizeof(uint32_t)); - struct connstate *s = (struct connstate *) (c + 1); memcpy(s->mac, pkt->eth->src, sizeof(s->mac)); if (c->recv.len >= MG_MAX_RECV_SIZE) { mg_error(c, "max_recv_buf_size reached"); @@ -4697,9 +4701,9 @@ static size_t tx_tcp(struct mg_tcpip_if *ifp, uint8_t *dst_mac, uint32_t dst_ip, struct ip *ip; struct tcp *tcp; uint16_t opts[4 / 2]; - if (flags & TH_SYN) { // Send MSS, RFC-9293 3.7.1 - opts[0] = mg_htons(0x0204); // RFC-9293 3.2 - opts[1] = mg_htons(ifp->mtu - 40); // RFC-6691 + if (flags & TH_SYN) { // Send MSS, RFC-9293 3.7.1 + opts[0] = mg_htons(0x0204); // RFC-9293 3.2 + opts[1] = mg_htons((uint16_t) (ifp->mtu - 40)); // RFC-6691 buf = opts; len = sizeof(opts); } @@ -4715,15 +4719,16 @@ static size_t tx_tcp(struct mg_tcpip_if *ifp, uint8_t *dst_mac, uint32_t dst_ip, tcp->win = mg_htons(MIP_TCP_WIN); tcp->off = (uint8_t) (sizeof(*tcp) / 4 << 4); if (flags & TH_SYN) tcp->off += (uint8_t) (sizeof(opts) / 4 << 4); - - uint32_t cs = 0; - uint16_t n = (uint16_t) (sizeof(*tcp) + len); - uint8_t pseudo[] = {0, ip->proto, (uint8_t) (n >> 8), (uint8_t) (n & 255)}; - cs = csumup(cs, tcp, n); - cs = csumup(cs, &ip->src, sizeof(ip->src)); - cs = csumup(cs, &ip->dst, sizeof(ip->dst)); - cs = csumup(cs, pseudo, sizeof(pseudo)); - tcp->csum = csumfin(cs); + { + uint32_t cs = 0; + uint16_t n = (uint16_t) (sizeof(*tcp) + len); + uint8_t pseudo[] = {0, ip->proto, (uint8_t) (n >> 8), (uint8_t) (n & 255)}; + cs = csumup(cs, tcp, n); + cs = csumup(cs, &ip->src, sizeof(ip->src)); + cs = csumup(cs, &ip->dst, sizeof(ip->dst)); + cs = csumup(cs, pseudo, sizeof(pseudo)); + tcp->csum = csumfin(cs); + } MG_VERBOSE(("TCP %M:%hu -> %M:%hu fl %x len %u", mg_print_ip4, &ip->src, mg_ntohs(tcp->sport), mg_print_ip4, &ip->dst, mg_ntohs(tcp->dport), tcp->flags, len)); @@ -4743,11 +4748,12 @@ static size_t tx_tcp_pkt(struct mg_tcpip_if *ifp, struct pkt *pkt, static struct mg_connection *accept_conn(struct mg_connection *lsn, struct pkt *pkt) { struct mg_connection *c = mg_alloc_conn(lsn->mgr); + struct connstate *s; if (c == NULL) { MG_ERROR(("OOM")); return NULL; } - struct connstate *s = (struct connstate *) (c + 1); + s = (struct connstate *) (c + 1); s->dmss = 536; // assume default, RFC-9293 3.7.1 s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq); memcpy(s->mac, pkt->eth->src, sizeof(s->mac)); @@ -5005,9 +5011,10 @@ static void rx_tcp(struct mg_tcpip_if *ifp, struct pkt *pkt) { static void rx_ip(struct mg_tcpip_if *ifp, struct pkt *pkt) { uint16_t frag = mg_ntohs(pkt->ip->frag); if (frag & IP_MORE_FRAGS_MSK || frag & IP_FRAG_OFFSET_MSK) { + struct mg_connection *c; if (pkt->ip->proto == 17) pkt->udp = (struct udp *) (pkt->ip + 1); if (pkt->ip->proto == 6) pkt->tcp = (struct tcp *) (pkt->ip + 1); - struct mg_connection *c = getpeer(ifp->mgr, pkt, false); + c = getpeer(ifp->mgr, pkt, false); if (c) mg_error(c, "Received fragmented packet"); } else if (pkt->ip->proto == 1) { pkt->icmp = (struct icmp *) (pkt->ip + 1); @@ -5033,11 +5040,12 @@ static void rx_ip(struct mg_tcpip_if *ifp, struct pkt *pkt) { rx_udp(ifp, pkt); } } else if (pkt->ip->proto == 6) { + uint16_t iplen, off; pkt->tcp = (struct tcp *) (pkt->ip + 1); if (pkt->pay.len < sizeof(*pkt->tcp)) return; mkpay(pkt, pkt->tcp + 1); - uint16_t iplen = mg_ntohs(pkt->ip->len); - uint16_t off = (uint16_t) (sizeof(*pkt->ip) + ((pkt->tcp->off >> 4) * 4U)); + iplen = mg_ntohs(pkt->ip->len); + off = (uint16_t) (sizeof(*pkt->ip) + ((pkt->tcp->off >> 4) * 4U)); if (iplen >= off) pkt->pay.len = (size_t) (iplen - off); MG_VERBOSE(("TCP %M:%hu -> %M:%hu len %u", mg_print_ip4, &pkt->ip->src, mg_ntohs(pkt->tcp->sport), mg_print_ip4, &pkt->ip->dst, @@ -5075,8 +5083,9 @@ static void mg_tcpip_rx(struct mg_tcpip_if *ifp, void *buf, size_t len) { memcmp(pkt.eth->dst, broadcast, sizeof(pkt.eth->dst)) != 0) return; if (ifp->enable_crc32_check && len > 4) { + uint32_t crc; len -= 4; // TODO(scaprile): check on bigendian - uint32_t crc = mg_crc32(0, (const char *) buf, len); + crc = mg_crc32(0, (const char *) buf, len); if (memcmp((void *) ((size_t) buf + len), &crc, sizeof(crc))) return; } if (pkt.eth->type == mg_htons(0x806)) { @@ -5185,10 +5194,10 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) { // Process timeouts for (c = ifp->mgr->conns; c != NULL; c = c->next) { - if ((c->is_udp && !c->is_arplooking) || c->is_listening || c->is_resolving) - continue; struct connstate *s = (struct connstate *) (c + 1); uint32_t rem_ip; + if ((c->is_udp && !c->is_arplooking) || c->is_listening || c->is_resolving) + continue; memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t)); if (ifp->now > s->timer) { if (s->ttype == MIP_TTYPE_ARP) { @@ -5386,10 +5395,10 @@ void mg_mgr_poll(struct mg_mgr *mgr, int ms) { if (mgr->ifp == NULL || mgr->ifp->driver == NULL) return; mg_tcpip_poll(mgr->ifp, now); for (c = mgr->conns; c != NULL; c = tmp) { - tmp = c->next; struct connstate *s = (struct connstate *) (c + 1); bool is_tls = c->is_tls && !c->is_resolving && !c->is_arplooking && !c->is_listening && !c->is_connecting; + tmp = c->next; mg_call(c, MG_EV_POLL, &now); MG_VERBOSE(("%lu .. %c%c%c%c%c %lu %lu", c->id, c->is_tls ? 'T' : 't', c->is_connecting ? 'C' : 'c', c->is_tls_hs ? 'H' : 'h', @@ -11022,10 +11031,10 @@ static int mg_der_parse(uint8_t *der, size_t dersz, struct mg_der_tlv *tlv) { if (dersz < 2) return -1; // Invalid DER tlv->type = der[0]; if (len > 0x7F) { // long-form length - uint8_t len_bytes = len & 0x7F; + uint8_t len_bytes = len & 0x7F, i; if (dersz < (size_t) (2 + len_bytes)) return -1; len = 0; - for (uint8_t i = 0; i < len_bytes; i++) { + for (i = 0; i < len_bytes; i++) { len = (len << 8) | der[2 + i]; } header_len += len_bytes; @@ -11037,8 +11046,9 @@ static int mg_der_parse(uint8_t *der, size_t dersz, struct mg_der_tlv *tlv) { } static int mg_der_next(struct mg_der_tlv *parent, struct mg_der_tlv *child) { + int consumed; if (parent->len == 0) return 0; - int consumed = mg_der_parse(parent->value, parent->len, child); + consumed = mg_der_parse(parent->value, parent->len, child); if (consumed < 0) return -1; parent->value += consumed; parent->len -= (uint32_t) consumed; @@ -11296,7 +11306,7 @@ static void mg_tls_encrypt(struct mg_connection *c, const uint8_t *msg, nonce[8] ^= (uint8_t) ((seq >> 24) & 255U); nonce[9] ^= (uint8_t) ((seq >> 16) & 255U); nonce[10] ^= (uint8_t) ((seq >> 8) & 255U); - nonce[11] ^= (uint8_t) ((seq) & 255U); + nonce[11] ^= (uint8_t) ((seq) &255U); mg_iobuf_add(wio, wio->len, hdr, sizeof(hdr)); mg_iobuf_resize(wio, wio->len + encsz); @@ -11375,7 +11385,7 @@ static int mg_tls_recv_record(struct mg_connection *c) { nonce[8] ^= (uint8_t) ((seq >> 24) & 255U); nonce[9] ^= (uint8_t) ((seq >> 16) & 255U); nonce[10] ^= (uint8_t) ((seq >> 8) & 255U); - nonce[11] ^= (uint8_t) ((seq) & 255U); + nonce[11] ^= (uint8_t) ((seq) &255U); #if CHACHA20 { uint8_t *dec = (uint8_t *) mg_calloc(1, msgsz); @@ -12122,91 +12132,91 @@ static int mg_tls_client_recv_cert(struct mg_connection *c) { return -1; } - uint32_t full_cert_chain_len = MG_LOAD_BE24(recv_buf + 1); - uint32_t cert_chain_len = MG_LOAD_BE24(recv_buf + 5); - if (cert_chain_len != full_cert_chain_len - 4) { - MG_ERROR(("full chain length: %d, chain length: %d", full_cert_chain_len, - cert_chain_len)); - mg_error(c, "certificate chain length mismatch"); - return -1; - } + { + // Normally, there are 2-3 certs in a chain + struct mg_tls_cert certs[8]; + int certnum = 0; + uint32_t full_cert_chain_len = MG_LOAD_BE24(recv_buf + 1); + uint32_t cert_chain_len = MG_LOAD_BE24(recv_buf + 5); + uint8_t *p = recv_buf + 8; + uint8_t *endp = recv_buf + cert_chain_len; + bool found_ca = false; + struct mg_tls_cert ca; - // Normally, there are 2-3 certs in a chain - struct mg_tls_cert certs[8]; - int certnum = 0; - uint8_t *p = recv_buf + 8; - // uint8_t *endp = recv_buf + tls->recv_len; - uint8_t *endp = recv_buf + cert_chain_len; - - int found_ca = 0; - struct mg_tls_cert ca; - - memset(certs, 0, sizeof(certs)); - memset(&ca, 0, sizeof(ca)); - - if (tls->ca_der.len > 0) { - if (mg_tls_parse_cert_der(tls->ca_der.buf, tls->ca_der.len, &ca) < 0) { - mg_error(c, "failed to parse CA certificate"); - return -1; - } - MG_VERBOSE(("CA serial: %M", mg_print_hex, ca.sn.len, ca.sn.buf)); - } - - while (p < endp) { - struct mg_tls_cert *ci = &certs[certnum++]; - uint32_t certsz = MG_LOAD_BE24(p); - uint8_t *cert = p + 3; - uint16_t certext = MG_LOAD_BE16(cert + certsz); - if (certext != 0) { - mg_error(c, "certificate extensions are not supported"); - return -1; - } - p = cert + certsz + 2; - - if (mg_tls_parse_cert_der(cert, certsz, ci) < 0) { - mg_error(c, "failed to parse certificate"); + if (cert_chain_len != full_cert_chain_len - 4) { + MG_ERROR(("full chain length: %d, chain length: %d", full_cert_chain_len, + cert_chain_len)); + mg_error(c, "certificate chain length mismatch"); return -1; } - if (ci == certs) { - // First certificate in the chain is peer cert, check SAN and store - // public key for further CertVerify step - if (mg_tls_verify_cert_san(cert, certsz, tls->hostname) <= 0) { - mg_error(c, "failed to verify hostname"); + memset(certs, 0, sizeof(certs)); + memset(&ca, 0, sizeof(ca)); + + if (tls->ca_der.len > 0) { + if (mg_tls_parse_cert_der(tls->ca_der.buf, tls->ca_der.len, &ca) < 0) { + mg_error(c, "failed to parse CA certificate"); return -1; } - memmove(tls->pubkey, ci->pubkey.buf, ci->pubkey.len); - tls->pubkeysz = ci->pubkey.len; - } else { - if (!mg_tls_verify_cert_signature(ci - 1, ci)) { - mg_error(c, "failed to verify certificate chain"); + MG_VERBOSE(("CA serial: %M", mg_print_hex, ca.sn.len, ca.sn.buf)); + } + + while (p < endp) { + struct mg_tls_cert *ci = &certs[certnum++]; + uint32_t certsz = MG_LOAD_BE24(p); + uint8_t *cert = p + 3; + uint16_t certext = MG_LOAD_BE16(cert + certsz); + if (certext != 0) { + mg_error(c, "certificate extensions are not supported"); + return -1; + } + p = cert + certsz + 2; + + if (mg_tls_parse_cert_der(cert, certsz, ci) < 0) { + mg_error(c, "failed to parse certificate"); + return -1; + } + + if (ci == certs) { + // First certificate in the chain is peer cert, check SAN and store + // public key for further CertVerify step + if (mg_tls_verify_cert_san(cert, certsz, tls->hostname) <= 0) { + mg_error(c, "failed to verify hostname"); + return -1; + } + memmove(tls->pubkey, ci->pubkey.buf, ci->pubkey.len); + tls->pubkeysz = ci->pubkey.len; + } else { + if (!mg_tls_verify_cert_signature(ci - 1, ci)) { + mg_error(c, "failed to verify certificate chain"); + return -1; + } + } + + if (ca.pubkey.len == ci->pubkey.len && + memcmp(ca.pubkey.buf, ci->pubkey.buf, ca.pubkey.len) == 0) { + found_ca = true; + break; + } + + if (certnum == sizeof(certs) / sizeof(certs[0]) - 1) { + mg_error(c, "too many certificates in the chain"); return -1; } } - if (ca.pubkey.len == ci->pubkey.len && - memcmp(ca.pubkey.buf, ci->pubkey.buf, ca.pubkey.len) == 0) { - found_ca = 1; - break; - } - - if (certnum == sizeof(certs) / sizeof(certs[0]) - 1) { - mg_error(c, "too many certificates in the chain"); - return -1; + if (!found_ca && tls->ca_der.len > 0) { + if (certnum < 1 || + !mg_tls_verify_cert_signature(&certs[certnum - 1], &ca)) { + mg_error(c, "failed to verify CA"); + return -1; + } else { + MG_VERBOSE( + ("CA was not in the chain, but verification with builtin CA " + "passed")); + } } } - - if (!found_ca && tls->ca_der.len > 0) { - if (certnum < 1 || - !mg_tls_verify_cert_signature(&certs[certnum - 1], &ca)) { - mg_error(c, "failed to verify CA"); - return -1; - } else { - MG_VERBOSE( - ("CA was not in the chain, but verification with builtin CA passed")); - } - } - mg_tls_drop_message(c); mg_tls_calc_cert_verify_hash(c, tls->sighash, 0); return 0; @@ -12236,78 +12246,80 @@ static int mg_tls_client_recv_cert_verify(struct mg_connection *c) { return 0; } - uint16_t sigalg = MG_LOAD_BE16(recv_buf + 4); - uint16_t siglen = MG_LOAD_BE16(recv_buf + 6); - uint8_t *sigbuf = recv_buf + 8; - if (siglen > tls->recv_len - 8) { - mg_error(c, "invalid certverify signature length: %d, expected %d", siglen, - tls->recv_len - 8); - return -1; - } - MG_VERBOSE( - ("certificate verification, algo=%04x, siglen=%d", sigalg, siglen)); - - if (sigalg == 0x0804) { // rsa_pss_rsae_sha256 - uint8_t sig2[512]; // 2048 or 4096 bits - struct mg_der_tlv seq, modulus, exponent; - - if (mg_der_parse(tls->pubkey, tls->pubkeysz, &seq) <= 0 || - mg_der_next(&seq, &modulus) <= 0 || modulus.type != 2 || - mg_der_next(&seq, &exponent) <= 0 || exponent.type != 2) { - mg_error(c, "invalid public key"); + { + uint16_t sigalg = MG_LOAD_BE16(recv_buf + 4); + uint16_t siglen = MG_LOAD_BE16(recv_buf + 6); + uint8_t *sigbuf = recv_buf + 8; + if (siglen > tls->recv_len - 8) { + mg_error(c, "invalid certverify signature length: %d, expected %d", + siglen, tls->recv_len - 8); return -1; } + MG_VERBOSE( + ("certificate verification, algo=%04x, siglen=%d", sigalg, siglen)); - mg_rsa_mod_pow(modulus.value, modulus.len, exponent.value, exponent.len, - sigbuf, siglen, sig2, sizeof(sig2)); + if (sigalg == 0x0804) { // rsa_pss_rsae_sha256 + uint8_t sig2[512]; // 2048 or 4096 bits + struct mg_der_tlv seq, modulus, exponent; - if (sig2[sizeof(sig2) - 1] != 0xbc) { - mg_error(c, "failed to verify RSA certificate (certverify)"); - return -1; - } - MG_DEBUG(("certificate verification successful (RSA)")); - } else if (sigalg == 0x0403) { // ecdsa_secp256r1_sha256 - // Extract certificate signature and verify it using pubkey and sighash - uint8_t sig[64]; - struct mg_der_tlv seq, r, s; - if (mg_der_to_tlv(sigbuf, siglen, &seq) < 0) { - mg_error(c, "verification message is not an ASN.1 DER sequence"); - return -1; - } - if (mg_der_to_tlv(seq.value, seq.len, &r) < 0) { - mg_error(c, "missing first part of the signature"); - return -1; - } - if (mg_der_to_tlv(r.value + r.len, seq.len - r.len, &s) < 0) { - mg_error(c, "missing second part of the signature"); - return -1; - } - // Integers may be padded with zeroes - if (r.len > 32) r.value = r.value + (r.len - 32), r.len = 32; - if (s.len > 32) s.value = s.value + (s.len - 32), s.len = 32; + if (mg_der_parse(tls->pubkey, tls->pubkeysz, &seq) <= 0 || + mg_der_next(&seq, &modulus) <= 0 || modulus.type != 2 || + mg_der_next(&seq, &exponent) <= 0 || exponent.type != 2) { + mg_error(c, "invalid public key"); + return -1; + } - memmove(sig, r.value, r.len); - memmove(sig + 32, s.value, s.len); + mg_rsa_mod_pow(modulus.value, modulus.len, exponent.value, exponent.len, + sigbuf, siglen, sig2, sizeof(sig2)); - if (mg_uecc_verify(tls->pubkey, tls->sighash, sizeof(tls->sighash), sig, - mg_uecc_secp256r1()) != 1) { - mg_error(c, "failed to verify EC certificate (certverify)"); + if (sig2[sizeof(sig2) - 1] != 0xbc) { + mg_error(c, "failed to verify RSA certificate (certverify)"); + return -1; + } + MG_DEBUG(("certificate verification successful (RSA)")); + } else if (sigalg == 0x0403) { // ecdsa_secp256r1_sha256 + // Extract certificate signature and verify it using pubkey and sighash + uint8_t sig[64]; + struct mg_der_tlv seq, r, s; + if (mg_der_to_tlv(sigbuf, siglen, &seq) < 0) { + mg_error(c, "verification message is not an ASN.1 DER sequence"); + return -1; + } + if (mg_der_to_tlv(seq.value, seq.len, &r) < 0) { + mg_error(c, "missing first part of the signature"); + return -1; + } + if (mg_der_to_tlv(r.value + r.len, seq.len - r.len, &s) < 0) { + mg_error(c, "missing second part of the signature"); + return -1; + } + // Integers may be padded with zeroes + if (r.len > 32) r.value = r.value + (r.len - 32), r.len = 32; + if (s.len > 32) s.value = s.value + (s.len - 32), s.len = 32; + + memmove(sig, r.value, r.len); + memmove(sig + 32, s.value, s.len); + + if (mg_uecc_verify(tls->pubkey, tls->sighash, sizeof(tls->sighash), sig, + mg_uecc_secp256r1()) != 1) { + mg_error(c, "failed to verify EC certificate (certverify)"); + return -1; + } + MG_DEBUG(("certificate verification successful (EC)")); + } else { + // From + // https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml: + // 0805 = rsa_pss_rsae_sha384 + // 0806 = rsa_pss_rsae_sha512 + // 0807 = ed25519 + // 0808 = ed448 + // 0809 = rsa_pss_pss_sha256 + // 080A = rsa_pss_pss_sha384 + // 080B = rsa_pss_pss_sha512 + MG_ERROR(("unsupported certverify signature scheme: %x of %d bytes", + sigalg, siglen)); return -1; } - MG_DEBUG(("certificate verification successful (EC)")); - } else { - // From - // https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml: - // 0805 = rsa_pss_rsae_sha384 - // 0806 = rsa_pss_rsae_sha512 - // 0807 = ed25519 - // 0808 = ed448 - // 0809 = rsa_pss_pss_sha256 - // 080A = rsa_pss_pss_sha384 - // 080B = rsa_pss_pss_sha512 - MG_ERROR(("unsupported certverify signature scheme: %x of %d bytes", sigalg, - siglen)); - return -1; } mg_tls_drop_message(c); return 0; @@ -12395,7 +12407,9 @@ static void mg_tls_client_handshake(struct mg_connection *c) { c->is_tls_hs = 0; mg_call(c, MG_EV_TLS_HS, NULL); break; - default: mg_error(c, "unexpected client state: %d", tls->state); break; + default: + mg_error(c, "unexpected client state: %d", tls->state); + break; } } @@ -12422,7 +12436,9 @@ static void mg_tls_server_handshake(struct mg_connection *c) { tls->state = MG_TLS_STATE_SERVER_CONNECTED; c->is_tls_hs = 0; return; - default: mg_error(c, "unexpected server state: %d", tls->state); break; + default: + mg_error(c, "unexpected server state: %d", tls->state); + break; } } @@ -16144,11 +16160,12 @@ NS_INTERNAL bigint *bi_crt(BI_CTX *ctx, bigint *bi, bigint *dP, bigint *dQ, int mg_rsa_mod_pow(const uint8_t *mod, size_t modsz, const uint8_t *exp, size_t expsz, const uint8_t *msg, size_t msgsz, uint8_t *out, size_t outsz) { BI_CTX *bi_ctx = bi_initialize(); + bigint *m1; bigint *n = bi_import(bi_ctx, mod, (int) modsz); bigint *e = bi_import(bi_ctx, exp, (int) expsz); bigint *h = bi_import(bi_ctx, msg, (int) msgsz); bi_set_mod(bi_ctx, n, 0); - bigint *m1 = bi_mod_power(bi_ctx, h, e); + m1 = bi_mod_power(bi_ctx, h, e); bi_free_mod(bi_ctx, 0); bi_export(bi_ctx, m1, out, (int) outsz); bi_terminate(bi_ctx); @@ -22054,8 +22071,8 @@ struct mg_tcpip_driver mg_tcpip_driver_imxrt = {mg_tcpip_driver_imxrt_init, #endif -enum { // ID1 ID2 - MG_PHY_KSZ8x = 0x22, // 0022 1561 - KSZ8081RNB +enum { // ID1 ID2 + MG_PHY_KSZ8x = 0x22, // 0022 1561 - KSZ8081RNB MG_PHY_DP83x = 0x2000, MG_PHY_DP83867 = 0xa231, // 2000 a231 - TI DP83867I MG_PHY_DP83825 = 0xa140, // 2000 a140 - TI DP83825I @@ -22063,7 +22080,7 @@ enum { // ID1 ID2 MG_PHY_LAN87x = 0x7, // 0007 c0fx - LAN8720 MG_PHY_RTL8201 = 0x1C, // 001c c816 - RTL8201, MG_PHY_ICS1894x = 0x15, - MG_PHY_ICS189432 = 0xf450 // 0015 f450 - ICS1894 + MG_PHY_ICS189432 = 0xf450 // 0015 f450 - ICS1894 }; enum { @@ -22136,11 +22153,13 @@ void mg_phy_init(struct mg_phy *phy, uint8_t phy_addr, uint8_t config) { phy->write_reg(phy_addr, MG_PHY_DP83x_REG_RCSR, MG_BIT(7) | MG_BIT(0)); } else if (id1 == MG_PHY_KSZ8x) { // Disable isolation (override hw, it doesn't make sense at this point) - phy->write_reg( // #2848, some NXP boards set ISO, even though - phy_addr, MG_PHY_REG_BCR, // docs say they don't - phy->read_reg(phy_addr, MG_PHY_REG_BCR) & (uint16_t) ~MG_BIT(10)); - phy->write_reg(phy_addr, MG_PHY_KSZ8x_REG_PC2R, // now do clock stuff - MG_BIT(15) | MG_BIT(8) | MG_BIT(7)); + // - #2848, some NXP boards set ISO, even though docs say they don't + phy->write_reg(phy_addr, MG_PHY_REG_BCR, + (uint16_t) (phy->read_reg(phy_addr, MG_PHY_REG_BCR) & + (uint16_t) ~MG_BIT(10))); + // now do clock stuff + phy->write_reg(phy_addr, MG_PHY_KSZ8x_REG_PC2R, + (uint16_t) (MG_BIT(15) | MG_BIT(8) | MG_BIT(7))); } else if (id1 == MG_PHY_LAN87x) { // nothing to do } else if (id1 == MG_PHY_RTL8201) { diff --git a/src/drivers/phy.c b/src/drivers/phy.c index 506f6cba..60e7d9d8 100644 --- a/src/drivers/phy.c +++ b/src/drivers/phy.c @@ -1,7 +1,7 @@ #include "phy.h" -enum { // ID1 ID2 - MG_PHY_KSZ8x = 0x22, // 0022 1561 - KSZ8081RNB +enum { // ID1 ID2 + MG_PHY_KSZ8x = 0x22, // 0022 1561 - KSZ8081RNB MG_PHY_DP83x = 0x2000, MG_PHY_DP83867 = 0xa231, // 2000 a231 - TI DP83867I MG_PHY_DP83825 = 0xa140, // 2000 a140 - TI DP83825I @@ -9,7 +9,7 @@ enum { // ID1 ID2 MG_PHY_LAN87x = 0x7, // 0007 c0fx - LAN8720 MG_PHY_RTL8201 = 0x1C, // 001c c816 - RTL8201, MG_PHY_ICS1894x = 0x15, - MG_PHY_ICS189432 = 0xf450 // 0015 f450 - ICS1894 + MG_PHY_ICS189432 = 0xf450 // 0015 f450 - ICS1894 }; enum { @@ -82,11 +82,13 @@ void mg_phy_init(struct mg_phy *phy, uint8_t phy_addr, uint8_t config) { phy->write_reg(phy_addr, MG_PHY_DP83x_REG_RCSR, MG_BIT(7) | MG_BIT(0)); } else if (id1 == MG_PHY_KSZ8x) { // Disable isolation (override hw, it doesn't make sense at this point) - phy->write_reg( // #2848, some NXP boards set ISO, even though - phy_addr, MG_PHY_REG_BCR, // docs say they don't - phy->read_reg(phy_addr, MG_PHY_REG_BCR) & (uint16_t) ~MG_BIT(10)); - phy->write_reg(phy_addr, MG_PHY_KSZ8x_REG_PC2R, // now do clock stuff - MG_BIT(15) | MG_BIT(8) | MG_BIT(7)); + // - #2848, some NXP boards set ISO, even though docs say they don't + phy->write_reg(phy_addr, MG_PHY_REG_BCR, + (uint16_t) (phy->read_reg(phy_addr, MG_PHY_REG_BCR) & + (uint16_t) ~MG_BIT(10))); + // now do clock stuff + phy->write_reg(phy_addr, MG_PHY_KSZ8x_REG_PC2R, + (uint16_t) (MG_BIT(15) | MG_BIT(8) | MG_BIT(7))); } else if (id1 == MG_PHY_LAN87x) { // nothing to do } else if (id1 == MG_PHY_RTL8201) { diff --git a/src/net_builtin.c b/src/net_builtin.c index 461a37df..a9e54f8d 100644 --- a/src/net_builtin.c +++ b/src/net_builtin.c @@ -161,7 +161,7 @@ static uint32_t csumup(uint32_t sum, const void *buf, size_t len) { static uint16_t csumfin(uint32_t sum) { while (sum >> 16) sum = (sum & 0xffff) + (sum >> 16); - return mg_htons(~sum & 0xffff); + return mg_htons((uint16_t) ((uint16_t) ~sum & 0xffff)); } static uint16_t ipcsum(const void *buf, size_t len) { @@ -248,12 +248,13 @@ static bool tx_udp(struct mg_tcpip_if *ifp, uint8_t *mac_dst, uint32_t ip_src, tx_ip(ifp, mac_dst, 17, ip_src, ip_dst, len + sizeof(struct udp)); struct udp *udp = (struct udp *) (ip + 1); size_t eth_len; + uint32_t cs; // MG_DEBUG(("UDP XX LEN %d %d", (int) len, (int) ifp->tx.len)); udp->sport = sport; udp->dport = dport; udp->len = mg_htons((uint16_t) (sizeof(*udp) + len)); udp->csum = 0; - uint32_t cs = csumup(0, udp, sizeof(*udp)); + cs = csumup(0, udp, sizeof(*udp)); cs = csumup(cs, buf, len); cs = csumup(cs, &ip->src, sizeof(ip->src)); cs = csumup(cs, &ip->dst, sizeof(ip->dst)); @@ -337,7 +338,8 @@ static struct mg_connection *getpeer(struct mg_mgr *mgr, struct pkt *pkt, break; if (c->is_udp && pkt->udp && c->loc.port == pkt->udp->dport) break; if (!c->is_udp && pkt->tcp && c->loc.port == pkt->tcp->dport && - lsn == c->is_listening && (lsn || c->rem.port == pkt->tcp->sport)) + lsn == (bool) c->is_listening && + (lsn || c->rem.port == pkt->tcp->sport)) break; } return c; @@ -392,10 +394,12 @@ static void rx_icmp(struct mg_tcpip_if *ifp, struct pkt *pkt) { if (pkt->icmp->type == 8 && pkt->ip != NULL && pkt->ip->dst == ifp->ip) { size_t hlen = sizeof(struct eth) + sizeof(struct ip) + sizeof(struct icmp); size_t space = ifp->tx.len - hlen, plen = pkt->pay.len; + struct ip *ip; + struct icmp *icmp; if (plen > space) plen = space; - struct ip *ip = tx_ip(ifp, pkt->eth->src, 1, ifp->ip, pkt->ip->src, - sizeof(struct icmp) + plen); - struct icmp *icmp = (struct icmp *) (ip + 1); + ip = tx_ip(ifp, pkt->eth->src, 1, ifp->ip, pkt->ip->src, + sizeof(*icmp) + plen); + icmp = (struct icmp *) (ip + 1); memset(icmp, 0, sizeof(*icmp)); // Set csum to 0 memcpy(icmp + 1, pkt->pay.buf, plen); // Copy RX payload to TX icmp->csum = ipcsum(icmp, sizeof(*icmp) + plen); @@ -442,13 +446,13 @@ static void rx_dhcp_client(struct mg_tcpip_if *ifp, struct pkt *pkt) { ifp->state = MG_TCPIP_STATE_REQ; // REQUESTING state } else if (msgtype == 5) { // DHCPACK if (ifp->state == MG_TCPIP_STATE_REQ && ip && gw && lease) { // got an IP + uint64_t rand; ifp->lease_expire = ifp->now + lease * 1000; MG_INFO(("Lease: %u sec (%lld)", lease, ifp->lease_expire / 1000)); // assume DHCP server = router until ARP resolves memcpy(ifp->gwmac, pkt->eth->src, sizeof(ifp->gwmac)); ifp->ip = ip, ifp->gw = gw, ifp->mask = mask; ifp->state = MG_TCPIP_STATE_IP; // BOUND state - uint64_t rand; mg_random(&rand, sizeof(rand)); srand((unsigned int) (rand + mg_millis())); if (ifp->enable_req_dns && dns != 0) @@ -467,9 +471,9 @@ static void rx_dhcp_client(struct mg_tcpip_if *ifp, struct pkt *pkt) { static void rx_dhcp_server(struct mg_tcpip_if *ifp, struct pkt *pkt) { uint8_t op = 0, *p = pkt->dhcp->options, *end = (uint8_t *) &pkt->raw.buf[pkt->raw.len]; - if (end < (uint8_t *) (pkt->dhcp + 1)) return; // struct dhcp *req = pkt->dhcp; struct dhcp res = {2, 1, 6, 0, 0, 0, 0, 0, 0, 0, 0, {0}, 0, {0}}; + if (end < (uint8_t *) (pkt->dhcp + 1)) return; res.yiaddr = ifp->ip; ((uint8_t *) (&res.yiaddr))[3]++; // Offer our IP + 1 while (p + 1 < end && p[0] != 255) { // Parse options @@ -508,9 +512,9 @@ static void rx_udp(struct mg_tcpip_if *ifp, struct pkt *pkt) { if (c == NULL) { // No UDP listener on this port. Should send ICMP, but keep silent. } else { + struct connstate *s = (struct connstate *) (c + 1); c->rem.port = pkt->udp->sport; memcpy(c->rem.ip, &pkt->ip->src, sizeof(uint32_t)); - struct connstate *s = (struct connstate *) (c + 1); memcpy(s->mac, pkt->eth->src, sizeof(s->mac)); if (c->recv.len >= MG_MAX_RECV_SIZE) { mg_error(c, "max_recv_buf_size reached"); @@ -531,9 +535,9 @@ static size_t tx_tcp(struct mg_tcpip_if *ifp, uint8_t *dst_mac, uint32_t dst_ip, struct ip *ip; struct tcp *tcp; uint16_t opts[4 / 2]; - if (flags & TH_SYN) { // Send MSS, RFC-9293 3.7.1 - opts[0] = mg_htons(0x0204); // RFC-9293 3.2 - opts[1] = mg_htons(ifp->mtu - 40); // RFC-6691 + if (flags & TH_SYN) { // Send MSS, RFC-9293 3.7.1 + opts[0] = mg_htons(0x0204); // RFC-9293 3.2 + opts[1] = mg_htons((uint16_t) (ifp->mtu - 40)); // RFC-6691 buf = opts; len = sizeof(opts); } @@ -549,15 +553,16 @@ static size_t tx_tcp(struct mg_tcpip_if *ifp, uint8_t *dst_mac, uint32_t dst_ip, tcp->win = mg_htons(MIP_TCP_WIN); tcp->off = (uint8_t) (sizeof(*tcp) / 4 << 4); if (flags & TH_SYN) tcp->off += (uint8_t) (sizeof(opts) / 4 << 4); - - uint32_t cs = 0; - uint16_t n = (uint16_t) (sizeof(*tcp) + len); - uint8_t pseudo[] = {0, ip->proto, (uint8_t) (n >> 8), (uint8_t) (n & 255)}; - cs = csumup(cs, tcp, n); - cs = csumup(cs, &ip->src, sizeof(ip->src)); - cs = csumup(cs, &ip->dst, sizeof(ip->dst)); - cs = csumup(cs, pseudo, sizeof(pseudo)); - tcp->csum = csumfin(cs); + { + uint32_t cs = 0; + uint16_t n = (uint16_t) (sizeof(*tcp) + len); + uint8_t pseudo[] = {0, ip->proto, (uint8_t) (n >> 8), (uint8_t) (n & 255)}; + cs = csumup(cs, tcp, n); + cs = csumup(cs, &ip->src, sizeof(ip->src)); + cs = csumup(cs, &ip->dst, sizeof(ip->dst)); + cs = csumup(cs, pseudo, sizeof(pseudo)); + tcp->csum = csumfin(cs); + } MG_VERBOSE(("TCP %M:%hu -> %M:%hu fl %x len %u", mg_print_ip4, &ip->src, mg_ntohs(tcp->sport), mg_print_ip4, &ip->dst, mg_ntohs(tcp->dport), tcp->flags, len)); @@ -577,11 +582,12 @@ static size_t tx_tcp_pkt(struct mg_tcpip_if *ifp, struct pkt *pkt, static struct mg_connection *accept_conn(struct mg_connection *lsn, struct pkt *pkt) { struct mg_connection *c = mg_alloc_conn(lsn->mgr); + struct connstate *s; if (c == NULL) { MG_ERROR(("OOM")); return NULL; } - struct connstate *s = (struct connstate *) (c + 1); + s = (struct connstate *) (c + 1); s->dmss = 536; // assume default, RFC-9293 3.7.1 s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq); memcpy(s->mac, pkt->eth->src, sizeof(s->mac)); @@ -839,9 +845,10 @@ static void rx_tcp(struct mg_tcpip_if *ifp, struct pkt *pkt) { static void rx_ip(struct mg_tcpip_if *ifp, struct pkt *pkt) { uint16_t frag = mg_ntohs(pkt->ip->frag); if (frag & IP_MORE_FRAGS_MSK || frag & IP_FRAG_OFFSET_MSK) { + struct mg_connection *c; if (pkt->ip->proto == 17) pkt->udp = (struct udp *) (pkt->ip + 1); if (pkt->ip->proto == 6) pkt->tcp = (struct tcp *) (pkt->ip + 1); - struct mg_connection *c = getpeer(ifp->mgr, pkt, false); + c = getpeer(ifp->mgr, pkt, false); if (c) mg_error(c, "Received fragmented packet"); } else if (pkt->ip->proto == 1) { pkt->icmp = (struct icmp *) (pkt->ip + 1); @@ -867,11 +874,12 @@ static void rx_ip(struct mg_tcpip_if *ifp, struct pkt *pkt) { rx_udp(ifp, pkt); } } else if (pkt->ip->proto == 6) { + uint16_t iplen, off; pkt->tcp = (struct tcp *) (pkt->ip + 1); if (pkt->pay.len < sizeof(*pkt->tcp)) return; mkpay(pkt, pkt->tcp + 1); - uint16_t iplen = mg_ntohs(pkt->ip->len); - uint16_t off = (uint16_t) (sizeof(*pkt->ip) + ((pkt->tcp->off >> 4) * 4U)); + iplen = mg_ntohs(pkt->ip->len); + off = (uint16_t) (sizeof(*pkt->ip) + ((pkt->tcp->off >> 4) * 4U)); if (iplen >= off) pkt->pay.len = (size_t) (iplen - off); MG_VERBOSE(("TCP %M:%hu -> %M:%hu len %u", mg_print_ip4, &pkt->ip->src, mg_ntohs(pkt->tcp->sport), mg_print_ip4, &pkt->ip->dst, @@ -909,8 +917,9 @@ static void mg_tcpip_rx(struct mg_tcpip_if *ifp, void *buf, size_t len) { memcmp(pkt.eth->dst, broadcast, sizeof(pkt.eth->dst)) != 0) return; if (ifp->enable_crc32_check && len > 4) { + uint32_t crc; len -= 4; // TODO(scaprile): check on bigendian - uint32_t crc = mg_crc32(0, (const char *) buf, len); + crc = mg_crc32(0, (const char *) buf, len); if (memcmp((void *) ((size_t) buf + len), &crc, sizeof(crc))) return; } if (pkt.eth->type == mg_htons(0x806)) { @@ -1019,10 +1028,10 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) { // Process timeouts for (c = ifp->mgr->conns; c != NULL; c = c->next) { - if ((c->is_udp && !c->is_arplooking) || c->is_listening || c->is_resolving) - continue; struct connstate *s = (struct connstate *) (c + 1); uint32_t rem_ip; + if ((c->is_udp && !c->is_arplooking) || c->is_listening || c->is_resolving) + continue; memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t)); if (ifp->now > s->timer) { if (s->ttype == MIP_TTYPE_ARP) { @@ -1220,10 +1229,10 @@ void mg_mgr_poll(struct mg_mgr *mgr, int ms) { if (mgr->ifp == NULL || mgr->ifp->driver == NULL) return; mg_tcpip_poll(mgr->ifp, now); for (c = mgr->conns; c != NULL; c = tmp) { - tmp = c->next; struct connstate *s = (struct connstate *) (c + 1); bool is_tls = c->is_tls && !c->is_resolving && !c->is_arplooking && !c->is_listening && !c->is_connecting; + tmp = c->next; mg_call(c, MG_EV_POLL, &now); MG_VERBOSE(("%lu .. %c%c%c%c%c %lu %lu", c->id, c->is_tls ? 'T' : 't', c->is_connecting ? 'C' : 'c', c->is_tls_hs ? 'H' : 'h', diff --git a/src/tls_builtin.c b/src/tls_builtin.c index 6d16542d..4cf82f4f 100644 --- a/src/tls_builtin.c +++ b/src/tls_builtin.c @@ -150,10 +150,10 @@ static int mg_der_parse(uint8_t *der, size_t dersz, struct mg_der_tlv *tlv) { if (dersz < 2) return -1; // Invalid DER tlv->type = der[0]; if (len > 0x7F) { // long-form length - uint8_t len_bytes = len & 0x7F; + uint8_t len_bytes = len & 0x7F, i; if (dersz < (size_t) (2 + len_bytes)) return -1; len = 0; - for (uint8_t i = 0; i < len_bytes; i++) { + for (i = 0; i < len_bytes; i++) { len = (len << 8) | der[2 + i]; } header_len += len_bytes; @@ -165,8 +165,9 @@ static int mg_der_parse(uint8_t *der, size_t dersz, struct mg_der_tlv *tlv) { } static int mg_der_next(struct mg_der_tlv *parent, struct mg_der_tlv *child) { + int consumed; if (parent->len == 0) return 0; - int consumed = mg_der_parse(parent->value, parent->len, child); + consumed = mg_der_parse(parent->value, parent->len, child); if (consumed < 0) return -1; parent->value += consumed; parent->len -= (uint32_t) consumed; @@ -424,7 +425,7 @@ static void mg_tls_encrypt(struct mg_connection *c, const uint8_t *msg, nonce[8] ^= (uint8_t) ((seq >> 24) & 255U); nonce[9] ^= (uint8_t) ((seq >> 16) & 255U); nonce[10] ^= (uint8_t) ((seq >> 8) & 255U); - nonce[11] ^= (uint8_t) ((seq) & 255U); + nonce[11] ^= (uint8_t) ((seq) &255U); mg_iobuf_add(wio, wio->len, hdr, sizeof(hdr)); mg_iobuf_resize(wio, wio->len + encsz); @@ -503,7 +504,7 @@ static int mg_tls_recv_record(struct mg_connection *c) { nonce[8] ^= (uint8_t) ((seq >> 24) & 255U); nonce[9] ^= (uint8_t) ((seq >> 16) & 255U); nonce[10] ^= (uint8_t) ((seq >> 8) & 255U); - nonce[11] ^= (uint8_t) ((seq) & 255U); + nonce[11] ^= (uint8_t) ((seq) &255U); #if CHACHA20 { uint8_t *dec = (uint8_t *) mg_calloc(1, msgsz); @@ -1250,91 +1251,91 @@ static int mg_tls_client_recv_cert(struct mg_connection *c) { return -1; } - uint32_t full_cert_chain_len = MG_LOAD_BE24(recv_buf + 1); - uint32_t cert_chain_len = MG_LOAD_BE24(recv_buf + 5); - if (cert_chain_len != full_cert_chain_len - 4) { - MG_ERROR(("full chain length: %d, chain length: %d", full_cert_chain_len, - cert_chain_len)); - mg_error(c, "certificate chain length mismatch"); - return -1; - } + { + // Normally, there are 2-3 certs in a chain + struct mg_tls_cert certs[8]; + int certnum = 0; + uint32_t full_cert_chain_len = MG_LOAD_BE24(recv_buf + 1); + uint32_t cert_chain_len = MG_LOAD_BE24(recv_buf + 5); + uint8_t *p = recv_buf + 8; + uint8_t *endp = recv_buf + cert_chain_len; + bool found_ca = false; + struct mg_tls_cert ca; - // Normally, there are 2-3 certs in a chain - struct mg_tls_cert certs[8]; - int certnum = 0; - uint8_t *p = recv_buf + 8; - // uint8_t *endp = recv_buf + tls->recv_len; - uint8_t *endp = recv_buf + cert_chain_len; - - int found_ca = 0; - struct mg_tls_cert ca; - - memset(certs, 0, sizeof(certs)); - memset(&ca, 0, sizeof(ca)); - - if (tls->ca_der.len > 0) { - if (mg_tls_parse_cert_der(tls->ca_der.buf, tls->ca_der.len, &ca) < 0) { - mg_error(c, "failed to parse CA certificate"); - return -1; - } - MG_VERBOSE(("CA serial: %M", mg_print_hex, ca.sn.len, ca.sn.buf)); - } - - while (p < endp) { - struct mg_tls_cert *ci = &certs[certnum++]; - uint32_t certsz = MG_LOAD_BE24(p); - uint8_t *cert = p + 3; - uint16_t certext = MG_LOAD_BE16(cert + certsz); - if (certext != 0) { - mg_error(c, "certificate extensions are not supported"); - return -1; - } - p = cert + certsz + 2; - - if (mg_tls_parse_cert_der(cert, certsz, ci) < 0) { - mg_error(c, "failed to parse certificate"); + if (cert_chain_len != full_cert_chain_len - 4) { + MG_ERROR(("full chain length: %d, chain length: %d", full_cert_chain_len, + cert_chain_len)); + mg_error(c, "certificate chain length mismatch"); return -1; } - if (ci == certs) { - // First certificate in the chain is peer cert, check SAN and store - // public key for further CertVerify step - if (mg_tls_verify_cert_san(cert, certsz, tls->hostname) <= 0) { - mg_error(c, "failed to verify hostname"); + memset(certs, 0, sizeof(certs)); + memset(&ca, 0, sizeof(ca)); + + if (tls->ca_der.len > 0) { + if (mg_tls_parse_cert_der(tls->ca_der.buf, tls->ca_der.len, &ca) < 0) { + mg_error(c, "failed to parse CA certificate"); return -1; } - memmove(tls->pubkey, ci->pubkey.buf, ci->pubkey.len); - tls->pubkeysz = ci->pubkey.len; - } else { - if (!mg_tls_verify_cert_signature(ci - 1, ci)) { - mg_error(c, "failed to verify certificate chain"); + MG_VERBOSE(("CA serial: %M", mg_print_hex, ca.sn.len, ca.sn.buf)); + } + + while (p < endp) { + struct mg_tls_cert *ci = &certs[certnum++]; + uint32_t certsz = MG_LOAD_BE24(p); + uint8_t *cert = p + 3; + uint16_t certext = MG_LOAD_BE16(cert + certsz); + if (certext != 0) { + mg_error(c, "certificate extensions are not supported"); + return -1; + } + p = cert + certsz + 2; + + if (mg_tls_parse_cert_der(cert, certsz, ci) < 0) { + mg_error(c, "failed to parse certificate"); + return -1; + } + + if (ci == certs) { + // First certificate in the chain is peer cert, check SAN and store + // public key for further CertVerify step + if (mg_tls_verify_cert_san(cert, certsz, tls->hostname) <= 0) { + mg_error(c, "failed to verify hostname"); + return -1; + } + memmove(tls->pubkey, ci->pubkey.buf, ci->pubkey.len); + tls->pubkeysz = ci->pubkey.len; + } else { + if (!mg_tls_verify_cert_signature(ci - 1, ci)) { + mg_error(c, "failed to verify certificate chain"); + return -1; + } + } + + if (ca.pubkey.len == ci->pubkey.len && + memcmp(ca.pubkey.buf, ci->pubkey.buf, ca.pubkey.len) == 0) { + found_ca = true; + break; + } + + if (certnum == sizeof(certs) / sizeof(certs[0]) - 1) { + mg_error(c, "too many certificates in the chain"); return -1; } } - if (ca.pubkey.len == ci->pubkey.len && - memcmp(ca.pubkey.buf, ci->pubkey.buf, ca.pubkey.len) == 0) { - found_ca = 1; - break; - } - - if (certnum == sizeof(certs) / sizeof(certs[0]) - 1) { - mg_error(c, "too many certificates in the chain"); - return -1; + if (!found_ca && tls->ca_der.len > 0) { + if (certnum < 1 || + !mg_tls_verify_cert_signature(&certs[certnum - 1], &ca)) { + mg_error(c, "failed to verify CA"); + return -1; + } else { + MG_VERBOSE( + ("CA was not in the chain, but verification with builtin CA " + "passed")); + } } } - - if (!found_ca && tls->ca_der.len > 0) { - if (certnum < 1 || - !mg_tls_verify_cert_signature(&certs[certnum - 1], &ca)) { - mg_error(c, "failed to verify CA"); - return -1; - } else { - MG_VERBOSE( - ("CA was not in the chain, but verification with builtin CA passed")); - } - } - mg_tls_drop_message(c); mg_tls_calc_cert_verify_hash(c, tls->sighash, 0); return 0; @@ -1364,78 +1365,80 @@ static int mg_tls_client_recv_cert_verify(struct mg_connection *c) { return 0; } - uint16_t sigalg = MG_LOAD_BE16(recv_buf + 4); - uint16_t siglen = MG_LOAD_BE16(recv_buf + 6); - uint8_t *sigbuf = recv_buf + 8; - if (siglen > tls->recv_len - 8) { - mg_error(c, "invalid certverify signature length: %d, expected %d", siglen, - tls->recv_len - 8); - return -1; - } - MG_VERBOSE( - ("certificate verification, algo=%04x, siglen=%d", sigalg, siglen)); - - if (sigalg == 0x0804) { // rsa_pss_rsae_sha256 - uint8_t sig2[512]; // 2048 or 4096 bits - struct mg_der_tlv seq, modulus, exponent; - - if (mg_der_parse(tls->pubkey, tls->pubkeysz, &seq) <= 0 || - mg_der_next(&seq, &modulus) <= 0 || modulus.type != 2 || - mg_der_next(&seq, &exponent) <= 0 || exponent.type != 2) { - mg_error(c, "invalid public key"); + { + uint16_t sigalg = MG_LOAD_BE16(recv_buf + 4); + uint16_t siglen = MG_LOAD_BE16(recv_buf + 6); + uint8_t *sigbuf = recv_buf + 8; + if (siglen > tls->recv_len - 8) { + mg_error(c, "invalid certverify signature length: %d, expected %d", + siglen, tls->recv_len - 8); return -1; } + MG_VERBOSE( + ("certificate verification, algo=%04x, siglen=%d", sigalg, siglen)); - mg_rsa_mod_pow(modulus.value, modulus.len, exponent.value, exponent.len, - sigbuf, siglen, sig2, sizeof(sig2)); + if (sigalg == 0x0804) { // rsa_pss_rsae_sha256 + uint8_t sig2[512]; // 2048 or 4096 bits + struct mg_der_tlv seq, modulus, exponent; - if (sig2[sizeof(sig2) - 1] != 0xbc) { - mg_error(c, "failed to verify RSA certificate (certverify)"); - return -1; - } - MG_DEBUG(("certificate verification successful (RSA)")); - } else if (sigalg == 0x0403) { // ecdsa_secp256r1_sha256 - // Extract certificate signature and verify it using pubkey and sighash - uint8_t sig[64]; - struct mg_der_tlv seq, r, s; - if (mg_der_to_tlv(sigbuf, siglen, &seq) < 0) { - mg_error(c, "verification message is not an ASN.1 DER sequence"); - return -1; - } - if (mg_der_to_tlv(seq.value, seq.len, &r) < 0) { - mg_error(c, "missing first part of the signature"); - return -1; - } - if (mg_der_to_tlv(r.value + r.len, seq.len - r.len, &s) < 0) { - mg_error(c, "missing second part of the signature"); - return -1; - } - // Integers may be padded with zeroes - if (r.len > 32) r.value = r.value + (r.len - 32), r.len = 32; - if (s.len > 32) s.value = s.value + (s.len - 32), s.len = 32; + if (mg_der_parse(tls->pubkey, tls->pubkeysz, &seq) <= 0 || + mg_der_next(&seq, &modulus) <= 0 || modulus.type != 2 || + mg_der_next(&seq, &exponent) <= 0 || exponent.type != 2) { + mg_error(c, "invalid public key"); + return -1; + } - memmove(sig, r.value, r.len); - memmove(sig + 32, s.value, s.len); + mg_rsa_mod_pow(modulus.value, modulus.len, exponent.value, exponent.len, + sigbuf, siglen, sig2, sizeof(sig2)); - if (mg_uecc_verify(tls->pubkey, tls->sighash, sizeof(tls->sighash), sig, - mg_uecc_secp256r1()) != 1) { - mg_error(c, "failed to verify EC certificate (certverify)"); + if (sig2[sizeof(sig2) - 1] != 0xbc) { + mg_error(c, "failed to verify RSA certificate (certverify)"); + return -1; + } + MG_DEBUG(("certificate verification successful (RSA)")); + } else if (sigalg == 0x0403) { // ecdsa_secp256r1_sha256 + // Extract certificate signature and verify it using pubkey and sighash + uint8_t sig[64]; + struct mg_der_tlv seq, r, s; + if (mg_der_to_tlv(sigbuf, siglen, &seq) < 0) { + mg_error(c, "verification message is not an ASN.1 DER sequence"); + return -1; + } + if (mg_der_to_tlv(seq.value, seq.len, &r) < 0) { + mg_error(c, "missing first part of the signature"); + return -1; + } + if (mg_der_to_tlv(r.value + r.len, seq.len - r.len, &s) < 0) { + mg_error(c, "missing second part of the signature"); + return -1; + } + // Integers may be padded with zeroes + if (r.len > 32) r.value = r.value + (r.len - 32), r.len = 32; + if (s.len > 32) s.value = s.value + (s.len - 32), s.len = 32; + + memmove(sig, r.value, r.len); + memmove(sig + 32, s.value, s.len); + + if (mg_uecc_verify(tls->pubkey, tls->sighash, sizeof(tls->sighash), sig, + mg_uecc_secp256r1()) != 1) { + mg_error(c, "failed to verify EC certificate (certverify)"); + return -1; + } + MG_DEBUG(("certificate verification successful (EC)")); + } else { + // From + // https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml: + // 0805 = rsa_pss_rsae_sha384 + // 0806 = rsa_pss_rsae_sha512 + // 0807 = ed25519 + // 0808 = ed448 + // 0809 = rsa_pss_pss_sha256 + // 080A = rsa_pss_pss_sha384 + // 080B = rsa_pss_pss_sha512 + MG_ERROR(("unsupported certverify signature scheme: %x of %d bytes", + sigalg, siglen)); return -1; } - MG_DEBUG(("certificate verification successful (EC)")); - } else { - // From - // https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml: - // 0805 = rsa_pss_rsae_sha384 - // 0806 = rsa_pss_rsae_sha512 - // 0807 = ed25519 - // 0808 = ed448 - // 0809 = rsa_pss_pss_sha256 - // 080A = rsa_pss_pss_sha384 - // 080B = rsa_pss_pss_sha512 - MG_ERROR(("unsupported certverify signature scheme: %x of %d bytes", sigalg, - siglen)); - return -1; } mg_tls_drop_message(c); return 0; @@ -1523,7 +1526,9 @@ static void mg_tls_client_handshake(struct mg_connection *c) { c->is_tls_hs = 0; mg_call(c, MG_EV_TLS_HS, NULL); break; - default: mg_error(c, "unexpected client state: %d", tls->state); break; + default: + mg_error(c, "unexpected client state: %d", tls->state); + break; } } @@ -1550,7 +1555,9 @@ static void mg_tls_server_handshake(struct mg_connection *c) { tls->state = MG_TLS_STATE_SERVER_CONNECTED; c->is_tls_hs = 0; return; - default: mg_error(c, "unexpected server state: %d", tls->state); break; + default: + mg_error(c, "unexpected server state: %d", tls->state); + break; } } diff --git a/src/tls_rsa.c b/src/tls_rsa.c index 75300d12..752bf9ea 100644 --- a/src/tls_rsa.c +++ b/src/tls_rsa.c @@ -1576,11 +1576,12 @@ NS_INTERNAL bigint *bi_crt(BI_CTX *ctx, bigint *bi, bigint *dP, bigint *dQ, int mg_rsa_mod_pow(const uint8_t *mod, size_t modsz, const uint8_t *exp, size_t expsz, const uint8_t *msg, size_t msgsz, uint8_t *out, size_t outsz) { BI_CTX *bi_ctx = bi_initialize(); + bigint *m1; bigint *n = bi_import(bi_ctx, mod, (int) modsz); bigint *e = bi_import(bi_ctx, exp, (int) expsz); bigint *h = bi_import(bi_ctx, msg, (int) msgsz); bi_set_mod(bi_ctx, n, 0); - bigint *m1 = bi_mod_power(bi_ctx, h, e); + m1 = bi_mod_power(bi_ctx, h, e); bi_free_mod(bi_ctx, 0); bi_export(bi_ctx, m1, out, (int) outsz); bi_terminate(bi_ctx); diff --git a/test/Makefile b/test/Makefile index eb316cc0..cc4d04db 100644 --- a/test/Makefile +++ b/test/Makefile @@ -91,6 +91,10 @@ mip_tap_test: mip_tap_test.c mongoose.c mongoose.h packed_fs.c Makefile tls_mult $(CC) mip_tap_test.c packed_fs.c $(CFLAGS) $(LDFLAGS) -o $@ ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./$@ +mip_vc98: mip_test.c mongoose.c mongoose.h packed_fs.c Makefile + $(DOCKER) mdashnet/vc98 wine cl mip_test.c packed_fs.c $(VCFLAGS) $(DEFS) $(TFLAGS) /Fe$@.exe + $(DOCKER) mdashnet/vc98 wine $@.exe + packed_fs.c: Makefile data/ssi.h fuzz.c data/a.txt data/ca.pem certs/ca.crt certs/server.crt certs/server.key $(CC) $(CFLAGS) pack.c -o pack $(RUN) ./pack Makefile data/ssi.h fuzz.c data/a.txt data/range.txt data/ca.pem certs/ca.crt certs/server.crt certs/server.key certs/client.key certs/client.crt > $@ diff --git a/test/mip_test.c b/test/mip_test.c index 8e4904bd..56f02936 100644 --- a/test/mip_test.c +++ b/test/mip_test.c @@ -1,11 +1,11 @@ #define MG_ENABLE_TCPIP 1 #define MG_ENABLE_TCPIP_DRIVER_INIT 0 -#include "mongoose.c" // order is important, this one first +#include "mongoose.c" // order is important, this one first #include "driver_mock.c" static int s_num_tests = 0; -static bool s_sent_fragment = 0; +static int s_sent_fragment = 0; static int s_seg_sent = 0; #ifdef NO_SLEEP_ABORT @@ -25,6 +25,13 @@ static int s_seg_sent = 0; } \ } while (0) +static void test_csum(void) { + uint8_t ip[20] = {0x45, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x28, 0x11, + 0x94, 0xcf, 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00, 0x00, 0x01}; + ASSERT(ipcsum(ip, 20) == 0); + // UDP and TCP checksum calc funcions use the same basic calls as ipcsum() +} + static void test_statechange(void) { char tx[1540]; struct mg_tcpip_if iface; @@ -47,8 +54,10 @@ static void fn(struct mg_connection *c, int ev, void *ev_data) { static void frag_recv_fn(struct mg_connection *c, int ev, void *ev_data) { if (ev == MG_EV_ERROR) { - if (s_sent_fragment) { + if (s_sent_fragment > 0) { + ASSERT(s_sent_fragment == 1); ASSERT(strcmp((char *) ev_data, "Received fragmented packet") == 0); + s_sent_fragment = 2; } } (void) c, (void) ev_data; @@ -57,11 +66,11 @@ static void frag_recv_fn(struct mg_connection *c, int ev, void *ev_data) { // mock send to a non-existent peer using the listener connection static void frag_send_fn(struct mg_connection *c, int ev, void *ev_data) { static bool s_sent; - static int s_seg_sizes[] = {416, 416, 368}; // based on len=1200 and MTU=500 + static int s_seg_sizes[] = {416, 416, 368}; // based on len=1200 and MTU=500 if (ev == MG_EV_POLL) { if (!s_sent) { struct connstate *s = (struct connstate *) (c + 1); - s->dmss = 1500; // mock set some destination MSS way larger + s->dmss = 1500; // mock set some destination MSS way larger c->send.len = 1200; // setting TCP payload size s_sent = true; } @@ -74,9 +83,9 @@ static void frag_send_fn(struct mg_connection *c, int ev, void *ev_data) { static void test_poll(void) { int count = 0, i; + struct mg_tcpip_if mif; struct mg_mgr mgr; mg_mgr_init(&mgr); - struct mg_tcpip_if mif; memset(&mif, 0, sizeof(mif)); mif.driver = &mg_tcpip_driver_mock; mg_tcpip_init(&mgr, &mif); @@ -284,8 +293,9 @@ static void test_frag_recv_path(void) { ip.len = mg_htons(sizeof(struct ip) + sizeof(struct tcp) + 1000); ip.frag |= IP_MORE_FRAGS_MSK; // setting More Fragments bit to 1 create_tcp_pkt(&e, &ip, 1001, 1, TH_PUSH | TH_ACK, 1000); - s_sent_fragment = true; - mg_mgr_poll(&mgr, 0); + s_sent_fragment = 1; // "enable" fn + mg_mgr_poll(&mgr, 0); // call it (process fake frag IP) + ASSERT(s_sent_fragment == 2); // check it followed the right path s_driver_data.len = 0; mg_mgr_free(&mgr); @@ -295,6 +305,7 @@ static void test_frag_send_path(void) { struct mg_mgr mgr; struct mg_tcpip_driver driver; struct mg_tcpip_if mif; + unsigned int i; mg_mgr_init(&mgr); memset(&mif, 0, sizeof(mif)); @@ -307,7 +318,7 @@ static void test_frag_send_path(void) { mif.mtu = 500; // force ad hoc small MTU to fragment IP mg_http_listen(&mgr, "http://0.0.0.0:0", frag_send_fn, NULL); mgr.conns->pfn = NULL; - for (int i = 0; i < 10; i++) mg_mgr_poll(&mgr, 0); + for (i = 0; i < 10; i++) mg_mgr_poll(&mgr, 0); ASSERT(s_seg_sent == 3); s_driver_data.len = 0; mg_mgr_free(&mgr); @@ -319,6 +330,7 @@ static void test_fragmentation(void) { } int main(void) { + test_csum(); test_statechange(); test_poll(); test_retransmit(); diff --git a/test/unit_test.c b/test/unit_test.c index 02d8dd48..296e735d 100644 --- a/test/unit_test.c +++ b/test/unit_test.c @@ -619,9 +619,11 @@ static void test_mqtt_basic(void) { #if MG_TLS // send more than 1 record, content is not relevant - static char somedata[21098]; - mg_random(somedata, sizeof(somedata)); - opts.message = mg_str_n(somedata, sizeof(somedata)); + { + static char somedata[21098]; + mg_random(somedata, sizeof(somedata)); + opts.message = mg_str_n(somedata, sizeof(somedata)); + } opts.qos = 1, opts.retain = false, opts.retransmit_id = 0; mg_mqtt_pub(c, &opts); tbuf[0] = 0;