DragonFlyBSD Kernel Audit
DF-0301 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/netinet/ip_carp.c b/sys/netinet/ip_carp.c
--- a/sys/netinet/ip_carp.c
+++ b/sys/netinet/ip_carp.c
@@ -301,6 +301,7 @@
 	int			 sc_advbase;	/* seconds */
 	int			 sc_init_counter;
 	uint64_t		 sc_counter;
+	uint64_t		 sc_replay_counter;	/* DF-0301: last seen adv counter for replay protection */
 
 	/* authentication */
 #define CARP_HMAC_PAD	64
@@ -629,6 +630,7 @@
 	sc->sc_vhid = -1;	/* required setting */
 	sc->sc_advskew = 0;
 	sc->sc_init_counter = 1;
+	sc->sc_replay_counter = 0;
 	sc->sc_naddrs = 0;
 	sc->sc_naddrs6 = 0;
 
@@ -1145,7 +1147,23 @@
 	tmp_counter = tmp_counter<<32;
 	tmp_counter += ntohl(ch->carp_counter[1]);
 
-	/* XXX Replay protection goes here */
+	/* Replay protection: reject advertisements whose counter is not
+	 * strictly greater than the last one we accepted.  This prevents
+	 * an on-path attacker from replaying a captured advertisement
+	 * to suppress failover (DoS).  A counter of 0 means uninitialized;
+	 * the first advertisement is always accepted.
+	 */
+	if (sc->sc_replay_counter != 0 &&
+	    tmp_counter <= sc->sc_replay_counter) {
+		carpstats.carps_badauth++;
+		CARP_LOG("%s: replayed advertisement (counter %llu <= %llu)\n",
+		    cifp->if_xname,
+		    (unsigned long long)tmp_counter,
+		    (unsigned long long)sc->sc_replay_counter);
+		m_freem(m);
+		return;
+	}
+	sc->sc_replay_counter = tmp_counter;
 
 	sc->sc_init_counter = 0;
 	sc->sc_counter = tmp_counter;