⬢ DragonFlyBSD Kernel Audit
DF-3203 / fix_all.diff
← back to finding ↓ download raw
--- a/sys/vfs/nfs/krpc_subr.c
+++ b/sys/vfs/nfs/krpc_subr.c
@@ -198,7 +198,6 @@
 	struct timeval tv;
 	struct sockbuf sio;
 	int error, rcvflg, timo, secs, len;
-	static u_int32_t static_xid = ~0xFF;
 	u_int32_t xid;
 	u_int16_t tport;
 	u_int32_t saddr;
@@ -278,9 +277,7 @@
 	mhead->m_len = sizeof(*call);
 	bzero((caddr_t)call, sizeof(*call));
 	/* rpc_call part */
-	do {
-		xid = atomic_fetchadd_int(&static_xid, 1);
-	} while (xid == 0);
+	xid = karc4random();
 	call->rp_xid = txdr_unsigned(xid);
 	/* call->rp_direction = 0; */
 	call->rp_rpcvers = txdr_unsigned(2);
@@ -363,13 +360,47 @@
 			}
 			if (error)
 				goto out;
-			len -= sio.sb_cc;
+			/*
+			 * The reply must come from the server we asked
+			 * (address AND port): this UDP socket is never
+			 * connect()ed, so without a source check any
+			 * LAN host that guesses the xid becomes the
+			 * boot client's RPC authority.  The xid is now
+			 * karc4random()-backed, but check the origin
+			 * anyway.
+			 */
+			if (from == NULL ||
+			    from->sa_len < sizeof(struct sockaddr_in) ||
+			    ((struct sockaddr_in *)(void *)from)->
+			      sin_addr.s_addr != sa->sin_addr.s_addr ||
+			    ((struct sockaddr_in *)(void *)from)->
+			      sin_port != sa->sin_port) {
+				if (m) {
+					m_freem(m);
+					m = NULL;
+				}
+				continue;
+			}
+			/*
+			 * sbinit() zeroes sb_cc and soreceive() counts
+			 * the DELIVERED bytes into it, so the residual
+			 * math below could never gate a UDP reply (the
+			 * max datagram is far below 65520).  Use the
+			 * received length.
+			 */
+			len = (int)sio.sb_cc;
 			m = sio.sb_mb;
 
-			/* Does the reply contain at least a header? */
-			if (len < MIN_REPLY_HDR)
+			/*
+			 * rp_status lives at offset 20..23 of the
+			 * 24-byte struct and is read below BEFORE any
+			 * m_pullup(), so require the full header up
+			 * front and pull it into the first mbuf now.
+			 */
+			if (len < (int)sizeof(struct rpc_reply))
 				continue;
-			if (m->m_len < MIN_REPLY_HDR)
+			m = m_pullup(m, sizeof(struct rpc_reply));
+			if (m == NULL)
 				continue;
 			reply = mtod(m, struct rpc_reply *);
 
@@ -391,6 +422,10 @@
 			if (reply->rp_status != 0) {
 				error = fxdr_unsigned(u_int32_t, reply->rp_status);
 				if (error == RPC_PROGMISMATCH) {
+					if (m) {
+						m_freem(m);
+						m = NULL;
+					}
 				  error = EBADRPC;
 				  goto out;
 				}