DF-0431 / fix.diff
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; |