DragonFlyBSD Kernel Audit
DF-0431 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/net/pf/if_pfsync.c b/sys/net/pf/if_pfsync.c
--- a/sys/net/pf/if_pfsync.c
+++ b/sys/net/pf/if_pfsync.c
@@ -399,13 +399,25 @@
 	/* copy to state */
 	bcopy(&sp->rt_addr, &st->rt_addr, sizeof(st->rt_addr));
 	st->creation = time_second - ntohl(sp->creation);
-	st->expire = time_second;
-	if (sp->expire) {
-		/* XXX No adaptive scaling. */
-		st->expire -= r->timeout[sp->timeout] - ntohl(sp->expire);
-	}
+	/*
+	 * DF-0431: the exported sp->expire is REMAINING seconds (see
+	 * export at pfsync_state_export :273-277). Bound the imported
+	 * state's lifetime to the rule's configured timeout for this
+	 * state type instead of trusting the peer's raw value. The prior
+	 * block (scaled expire) was DEAD CODE, unconditionally overwritten
+	 * below by `ntohl(sp->expire) + time_second`, which let a peer send
+	 * sp->expire=0xffffffff to create a ~136-year (never-expiring)
+	 * state -- enabling persistent authorization + state-table
+	 * exhaustion. Use the smaller of the peer's remaining seconds and
+	 * the rule timeout.
+	 */
+	{
+		u_int32_t peer_remain = ntohl(sp->expire);
+		u_int32_t rule_to = r->timeout[sp->timeout];
 
-	st->expire = ntohl(sp->expire) + time_second;
+		st->expire = time_second +
+		    (peer_remain < rule_to ? peer_remain : rule_to);
+	}
 	st->direction = sp->direction;
 	st->log = sp->log;
 	st->timeout = sp->timeout;