WireGuard bounces off FreeBSD—for now
WireGuard bounces off FreeBSD—for now
Posted Mar 24, 2021 20:30 UTC (Wed) by excors (subscriber, #95769)In reply to: WireGuard bounces off FreeBSD—for now by bredelings
Parent article: WireGuard bounces off FreeBSD—for now
From the imported Netgate code (https://cgit.freebsd.org/src/commit/sys/dev/if_wg?id=2338...), I think the "random sleep" are the 'pause("link_down", hz/4)' etc. And they were explicitly added to avoid race conditions (https://reviews.freebsd.org/D26137#611017). wg_allowedip_valid is a "validation functions that just returned true". __chacha20poly1305_decrypt looks like "random printf statements deep in crypto code". (The commented-out WARN_ON calls are sloppy but probably don't count).
That doesn't look as terrible as Donenfeld implies, but his points seem basically true, and those are all quite obvious issues that suggest the code was merged without being reviewed to a high standard. Those specific issues could have been fixed with some iterations of reviewing and patching, but it seems hard to have much confidence that there aren't a lot of more subtle bugs that will slip past all those reviews, since it doesn't look like the code was developed with a particularly security-oriented mindset from the start.
It may have been developed and reviewed to the same standards as a lot of kernel code, and maybe that's good enough to probably avoid any major security bugs (so Netgate is not wrong in claiming there are no exploitable vulnerabilities), but Donenfeld appears to deliberately set higher quality standards for WireGuard implementations. It seems he views it as a brand, where a bad implementation on one OS will hurt the overall brand and will discourage users from using it on other OSes where it has been implemented well; it's important to ensure every implementation is not just technically compatible with the protocol but is also implemented to a high standard. But few kernel developers have the same standards, and that leads to some stepping on toes.
Posted Mar 25, 2021 0:13 UTC (Thu)
by zx2c4 (subscriber, #82519)
[Link]
There are more bugs than the ones you identified. Copying and pasting from https://cgit.freebsd.org/src/commit/?id=2338da0373f19b511... , for example:
+ out_len = sizeof(struct wg_pkt_data) + plaintext_len + NOISE_MAC_SIZE;
If out_len is greater than out MCLBYTES (2048), that m_copydata overflows a heap buffer.
+ /* A packet with length 0 is a keepalive packet */
It checks for len == 0, but doesn't check for len >= 20 or >= 40, for being able to read the IPv4/IPv6 headers, so you wind overrunning and reading nonsense off the heap. Later this too-short packet is passed onto directly to the rest of the networking stack.
+ aip = malloc(count*sizeof(*aip), M_TEMP, M_WAITOK);
Probably should be using `mallocarray` on that multiplication.
+ if ((m = m_defrag(m0, M_NOWAIT)) == NULL) {
m_defrag organizes the chain into optimally sized fragments. If the data doesn't fit into a single fragment, it winds up just being truncated and then later decrypted?
+void curve25519_generic(u8 out[CURVE25519_KEY_SIZE],
What happened to the call to clamp that scalar? DJB's paper pretty clearly lays out the case for that.
+ if (curthread->td_ucred->cr_uid == 0)
Just like on Linux, where checking current->uid is not the right way and you should use capable or ns_capable, so too here priv_check should be used.
...and so on and so forth.
It really was as bad as I described.
Posted Apr 5, 2021 19:41 UTC (Mon)
by rlhamil (guest, #6472)
[Link] (1 responses)
MAYBE SeL4 gets close.
In the long run, only AI's should code; humans are too defective; whether too sloppy or too egotistical matters not.
Posted Apr 7, 2021 7:10 UTC (Wed)
by jezuch (subscriber, #52988)
[Link]
WireGuard bounces off FreeBSD—for now
+
+ if ((mc = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, MCLBYTES)) == NULL)
+ goto error;
+
+ data = mtod(mc, struct wg_pkt_data *);
+ m_copydata(m, 0, m->m_pkthdr.len, data->data.buf);
+ if (m->m_pkthdr.len == 0) {
+ DPRINTF(peer->p_sc, "Receiving keepalive packet from peer "
+ "%llu\n", (unsigned long long)peer->p_id);
+ goto done;
+ }
+
+ version = mtod(m, struct ip *)->ip_v;
+ if (version != IPVERSION && version != 6) {
+ DPRINTF(peer->p_sc, "Packet is neither ipv4 nor ipv6 from peer "
+ "%llu\n", (unsigned long long)peer->p_id);
+ goto error;
+ }
+
+ routed_peer = wg_route_lookup(&peer->p_sc->sc_routes, m, IN);
+ DPRINTF(sc, "DEFRAG fail\n");
+ return;
+ }
+ data = mtod(m, void *);
+ const u8 scalar[CURVE25519_KEY_SIZE],
+ const u8 point[CURVE25519_KEY_SIZE])
+{
+ fe x1, x2, z2, x3, z3;
+ fe_loose x2l, z2l, x3l;
+ unsigned swap = 0;
+ int pos;
+ u8 e[32];
+
+ memcpy(e, scalar, 32);
+
+ /* The following implementation was
+ nvlist_add_binary(nvl, "private-key", sc->sc_local.l_private, WG_KEY_SIZE);
WireGuard bounces off FreeBSD—for now
WireGuard bounces off FreeBSD—for now
