Module Name:    src
Committed By:   riastradh
Date:           Mon Aug 31 20:31:03 UTC 2020

Modified Files:
        src/sys/net: if_wg.c

Log Message:
wg: wg_sockaddr audit.

- Ensure all access to struct wg_peer::wgp_endpoint happens while
  holding a psref.

- Simplify internalize/externalize logic and be more careful about
  verifying it before printing anything.


To generate a diff of this commit:
cvs rdiff -u -r1.46 -r1.47 src/sys/net/if_wg.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/net/if_wg.c
diff -u src/sys/net/if_wg.c:1.46 src/sys/net/if_wg.c:1.47
--- src/sys/net/if_wg.c:1.46	Mon Aug 31 20:30:34 2020
+++ src/sys/net/if_wg.c	Mon Aug 31 20:31:03 2020
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_wg.c,v 1.46 2020/08/31 20:30:34 riastradh Exp $	*/
+/*	$NetBSD: if_wg.c,v 1.47 2020/08/31 20:31:03 riastradh Exp $	*/
 
 /*
  * Copyright (C) Ryota Ozaki <ozaki.ry...@gmail.com>
@@ -41,7 +41,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if_wg.c,v 1.46 2020/08/31 20:30:34 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_wg.c,v 1.47 2020/08/31 20:31:03 riastradh Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_inet.h"
@@ -491,10 +491,13 @@ struct wg_sockaddr {
 	struct psref_target	wgsa_psref;
 };
 
+#define wgsatoss(wgsa)		(&(wgsa)->_ss)
 #define wgsatosa(wgsa)		(&(wgsa)->_sa)
 #define wgsatosin(wgsa)		(&(wgsa)->_sin)
 #define wgsatosin6(wgsa)	(&(wgsa)->_sin6)
 
+#define	wgsa_family(wgsa)	(wgsatosa(wgsa)->sa_family)
+
 struct wg_peer;
 struct wg_allowedip {
 	struct radix_node	wga_nodes[2];
@@ -533,10 +536,6 @@ struct wg_peer {
 
 	uint8_t	wgp_pubkey[WG_STATIC_KEY_LEN];
 	struct wg_sockaddr	*wgp_endpoint;
-#define wgp_ss		wgp_endpoint->_ss
-#define wgp_sa		wgp_endpoint->_sa
-#define wgp_sin		wgp_endpoint->_sin
-#define wgp_sin6	wgp_endpoint->_sin6
 	struct wg_sockaddr	*wgp_endpoint0;
 	bool			wgp_endpoint_changing;
 	bool			wgp_endpoint_available;
@@ -1536,10 +1535,10 @@ wg_get_so_by_af(struct wg_worker *wgw, c
 }
 
 static struct socket *
-wg_get_so_by_peer(struct wg_peer *wgp)
+wg_get_so_by_peer(struct wg_peer *wgp, struct wg_sockaddr *wgsa)
 {
 
-	return wg_get_so_by_af(wgp->wgp_sc->wg_worker, wgp->wgp_sa.sa_family);
+	return wg_get_so_by_af(wgp->wgp_sc->wg_worker, wgsa_family(wgsa));
 }
 
 static struct wg_sockaddr *
@@ -1549,7 +1548,7 @@ wg_get_endpoint_sa(struct wg_peer *wgp, 
 	int s;
 
 	s = pserialize_read_enter();
-	wgsa = wgp->wgp_endpoint;
+	wgsa = atomic_load_consume(&wgp->wgp_endpoint);
 	psref_acquire(psref, &wgsa->wgsa_psref, wg_psref_class);
 	pserialize_read_exit(s);
 
@@ -1571,8 +1570,8 @@ wg_send_so(struct wg_peer *wgp, struct m
 	struct psref psref;
 	struct wg_sockaddr *wgsa;
 
-	so = wg_get_so_by_peer(wgp);
 	wgsa = wg_get_endpoint_sa(wgp, &psref);
+	so = wg_get_so_by_peer(wgp, wgsa);
 	error = sosend(so, wgsatosa(wgsa), NULL, m, NULL, 0, curlwp);
 	wg_put_sa(wgp, wgsa, &psref);
 
@@ -1995,7 +1994,7 @@ wg_fill_msg_cookie(struct wg_softc *wg, 
 	    }
 #endif
 	default:
-		panic("invalid af=%d", wgp->wgp_sa.sa_family);
+		panic("invalid af=%d", src->sa_family);
 	}
 
 	wg_algo_mac(cookie, sizeof(cookie),
@@ -2188,10 +2187,12 @@ wg_change_endpoint(struct wg_peer *wgp, 
 	WG_TRACE("Changing endpoint");
 
 	memcpy(wgp->wgp_endpoint0, new, new->sa_len);
+#ifndef __HAVE_ATOMIC_AS_MEMBAR	/* store-release */
+	membar_exit();
+#endif
 	wgp->wgp_endpoint0 = atomic_swap_ptr(&wgp->wgp_endpoint,
 	    wgp->wgp_endpoint0);
-	if (!wgp->wgp_endpoint_available)
-		wgp->wgp_endpoint_available = true;
+	wgp->wgp_endpoint_available = true;
 	wgp->wgp_endpoint_changing = true;
 	wg_schedule_peer_task(wgp, WGP_TASK_ENDPOINT_CHANGED);
 }
@@ -2318,10 +2319,14 @@ static void
 wg_update_endpoint_if_necessary(struct wg_peer *wgp,
     const struct sockaddr *src)
 {
+	struct wg_sockaddr *wgsa;
+	struct psref psref;
+
+	wgsa = wg_get_endpoint_sa(wgp, &psref);
 
 #ifdef WG_DEBUG_LOG
 	char oldaddr[128], newaddr[128];
-	sockaddr_format(&wgp->wgp_sa, oldaddr, sizeof(oldaddr));
+	sockaddr_format(wgsatosa(wgsa), oldaddr, sizeof(oldaddr));
 	sockaddr_format(src, newaddr, sizeof(newaddr));
 	WG_DLOG("old=%s, new=%s\n", oldaddr, newaddr);
 #endif
@@ -2330,8 +2335,8 @@ wg_update_endpoint_if_necessary(struct w
 	 * III: "Since the packet has authenticated correctly, the source IP of
 	 * the outer UDP/IP packet is used to update the endpoint for peer..."
 	 */
-	if (__predict_false(sockaddr_cmp(src, &wgp->wgp_sa) != 0 ||
-	                    !sockaddr_port_match(src, &wgp->wgp_sa))) {
+	if (__predict_false(sockaddr_cmp(src, wgsatosa(wgsa)) != 0 ||
+		!sockaddr_port_match(src, wgsatosa(wgsa)))) {
 		mutex_enter(wgp->wgp_lock);
 		/* XXX We can't change the endpoint twice in a short period */
 		if (!wgp->wgp_endpoint_changing) {
@@ -2339,6 +2344,8 @@ wg_update_endpoint_if_necessary(struct w
 		}
 		mutex_exit(wgp->wgp_lock);
 	}
+
+	wg_put_sa(wgp, wgsa, &psref);
 }
 
 static void
@@ -3681,10 +3688,11 @@ wg_send_udp(struct wg_peer *wgp, struct 
 	struct psref psref;
 	struct wg_sockaddr *wgsa;
 	int error;
-	struct socket *so = wg_get_so_by_peer(wgp);
+	struct socket *so;
 
-	solock(so);
 	wgsa = wg_get_endpoint_sa(wgp, &psref);
+	so = wg_get_so_by_peer(wgp, wgsa);
+	solock(so);
 	if (wgsatosa(wgsa)->sa_family == AF_INET) {
 		error = udp_send(so, m, wgsatosa(wgsa), NULL, curlwp);
 	} else {
@@ -3693,11 +3701,11 @@ wg_send_udp(struct wg_peer *wgp, struct 
 		    NULL, curlwp);
 #else
 		m_freem(m);
-		error = EPROTONOSUPPORT;
+		error = EPFNOSUPPORT;
 #endif
 	}
-	wg_put_sa(wgp, wgsa, &psref);
 	sounlock(so);
+	wg_put_sa(wgp, wgsa, &psref);
 
 	return error;
 }
@@ -3924,41 +3932,37 @@ wg_handle_prop_peer(struct wg_softc *wg,
 		memcpy(wgp->wgp_psk, psk, sizeof(wgp->wgp_psk));
 	}
 
-	struct sockaddr_storage sockaddr;
 	const void *addr;
 	size_t addr_len;
+	struct wg_sockaddr *wgsa = wgp->wgp_endpoint;
 
 	if (!prop_dictionary_get_data(peer, "endpoint", &addr, &addr_len))
 		goto skip_endpoint;
-	memcpy(&sockaddr, addr, addr_len);
-	switch (sockaddr.ss_family) {
-	case AF_INET: {
-		struct sockaddr_in sin;
-		sockaddr_copy(sintosa(&sin), sizeof(sin),
-		    (const struct sockaddr *)&sockaddr);
-		sockaddr_copy(sintosa(&wgp->wgp_sin),
-		    sizeof(wgp->wgp_sin), (const struct sockaddr *)&sockaddr);
-		char addrstr[128];
-		sockaddr_format(sintosa(&sin), addrstr, sizeof(addrstr));
-		WG_DLOG("addr=%s\n", addrstr);
-		break;
-	    }
+	if (addr_len < sizeof(*wgsatosa(wgsa)) ||
+	    addr_len > sizeof(*wgsatoss(wgsa))) {
+		error = EINVAL;
+		goto out;
+	}
+	memcpy(wgsatoss(wgsa), addr, addr_len);
+	switch (wgsa_family(wgsa)) {
+	case AF_INET:
 #ifdef INET6
-	case AF_INET6: {
-		struct sockaddr_in6 sin6;
-		char addrstr[128];
-		sockaddr_copy(sintosa(&sin6), sizeof(sin6),
-		    (const struct sockaddr *)&sockaddr);
-		sockaddr_format(sintosa(&sin6), addrstr, sizeof(addrstr));
-		WG_DLOG("addr=%s\n", addrstr);
-		sockaddr_copy(sin6tosa(&wgp->wgp_sin6),
-		    sizeof(wgp->wgp_sin6), (const struct sockaddr *)&sockaddr);
-		break;
-	    }
+	case AF_INET6:
 #endif
-	default:
 		break;
+	default:
+		error = EPFNOSUPPORT;
+		goto out;
+	}
+	if (addr_len != sockaddr_getsize_by_family(wgsa_family(wgsa))) {
+		error = EINVAL;
+		goto out;
 	}
+    {
+	char addrstr[128];
+	sockaddr_format(wgsatosa(wgsa), addrstr, sizeof(addrstr));
+	WG_DLOG("addr=%s\n", addrstr);
+    }
 	wgp->wgp_endpoint_available = true;
 
 	prop_array_t allowedips;
@@ -4248,10 +4252,11 @@ wg_ioctl_get(struct wg_softc *wg, struct
 	s = pserialize_read_enter();
 	i = 0;
 	WG_PEER_READER_FOREACH(wgp, wg) {
-		struct psref psref;
+		struct wg_sockaddr *wgsa;
+		struct psref wgp_psref, wgsa_psref;
 		prop_dictionary_t prop_peer;
 
-		wg_get_peer(wgp, &psref);
+		wg_get_peer(wgp, &wgp_psref);
 		pserialize_read_exit(s);
 
 		prop_peer = prop_dictionary_create();
@@ -4277,20 +4282,16 @@ wg_ioctl_get(struct wg_softc *wg, struct
 				goto next;
 		}
 
-		switch (wgp->wgp_sa.sa_family) {
-		case AF_INET:
-			if (!prop_dictionary_set_data(prop_peer, "endpoint",
-				&wgp->wgp_sin, sizeof(wgp->wgp_sin)))
-				goto next;
-			break;
-#ifdef INET6
-		case AF_INET6:
-			if (!prop_dictionary_set_data(prop_peer, "endpoint",
-				&wgp->wgp_sin6, sizeof(wgp->wgp_sin6)))
-				goto next;
-			break;
-#endif
+		wgsa = wg_get_endpoint_sa(wgp, &wgsa_psref);
+		CTASSERT(AF_UNSPEC == 0);
+		if (wgsa_family(wgsa) != 0 /*AF_UNSPEC*/ &&
+		    !prop_dictionary_set_data(prop_peer, "endpoint",
+			wgsatoss(wgsa),
+			sockaddr_getsize_by_family(wgsa_family(wgsa)))) {
+			wg_put_sa(wgp, wgsa, &wgsa_psref);
+			goto next;
 		}
+		wg_put_sa(wgp, wgsa, &wgsa_psref);
 
 		const struct timespec *t = &wgp->wgp_last_handshake_time;
 
@@ -4356,7 +4357,7 @@ wg_ioctl_get(struct wg_softc *wg, struct
 		i++;
 
 		s = pserialize_read_enter();
-		wg_put_peer(wgp, &psref);
+		wg_put_peer(wgp, &wgp_psref);
 	}
 	pserialize_read_exit(s);
 

Reply via email to