Revert "pf: stricter state checking for ICMP and ICMPv6 packets"

This reverts commit 9ceb7fda9d.
This commit is contained in:
Franco Fichtner 2024-08-12 09:16:09 +02:00
parent 860e2a2937
commit ae593daa43
2 changed files with 83 additions and 317 deletions

View file

@ -295,8 +295,6 @@ static void pf_change_ap(struct mbuf *, struct pf_addr *, u_int16_t *,
u_int16_t, u_int8_t, sa_family_t);
static int pf_modulate_sack(struct mbuf *, int, struct pf_pdesc *,
struct tcphdr *, struct pf_state_peer *);
int pf_icmp_mapping(struct pf_pdesc *, u_int8_t, int *,
int *, u_int16_t *, u_int16_t *);
static void pf_change_icmp(struct pf_addr *, u_int16_t *,
struct pf_addr *, struct pf_addr *, u_int16_t,
u_int16_t *, u_int16_t *, u_int16_t *,
@ -343,10 +341,6 @@ static int pf_test_state_tcp(struct pf_kstate **,
static int pf_test_state_udp(struct pf_kstate **,
struct pfi_kkif *, struct mbuf *, int,
void *, struct pf_pdesc *);
int pf_icmp_state_lookup(struct pf_state_key_cmp *,
struct pf_pdesc *, struct pf_kstate **, struct mbuf *,
int, struct pfi_kkif *, u_int16_t, u_int16_t,
int, int *, int);
static int pf_test_state_icmp(struct pf_kstate **,
struct pfi_kkif *, struct mbuf *, int,
void *, struct pf_pdesc *, u_short *);
@ -399,8 +393,6 @@ extern struct proc *pf_purge_proc;
VNET_DEFINE(struct pf_limit, pf_limits[PF_LIMIT_MAX]);
enum { PF_ICMP_MULTI_NONE, PF_ICMP_MULTI_SOLICITED, PF_ICMP_MULTI_LINK };
#define PACKET_UNDO_NAT(_m, _pd, _off, _s) \
do { \
struct pf_state_key *nk; \
@ -1767,142 +1759,6 @@ pf_isforlocal(struct mbuf *m, int af)
return (false);
}
int
pf_icmp_mapping(struct pf_pdesc *pd, u_int8_t type,
int *icmp_dir, int *multi, u_int16_t *icmpid, u_int16_t *icmptype)
{
/*
* ICMP types marked with PF_OUT are typically responses to
* PF_IN, and will match states in the opposite direction.
* PF_IN ICMP types need to match a state with that type.
*/
*icmp_dir = PF_OUT;
*multi = PF_ICMP_MULTI_LINK;
/* Queries (and responses) */
switch (type) {
case ICMP_ECHO:
*icmp_dir = PF_IN;
case ICMP_ECHOREPLY:
*icmptype = ICMP_ECHO;
*icmpid = pd->hdr.icmp.icmp_id;
break;
case ICMP_TSTAMP:
*icmp_dir = PF_IN;
case ICMP_TSTAMPREPLY:
*icmptype = ICMP_TSTAMP;
*icmpid = 0; /* Time is not a secret. */
break;
case ICMP_IREQ:
*icmp_dir = PF_IN;
case ICMP_IREQREPLY:
*icmptype = ICMP_IREQ;
*icmpid = 0; /* Nothing sane to match on! */
break;
case ICMP_MASKREQ:
*icmp_dir = PF_IN;
case ICMP_MASKREPLY:
*icmptype = ICMP_MASKREQ;
*icmpid = 0; /* Nothing sane to match on! */
break;
case ICMP_IPV6_WHEREAREYOU:
*icmp_dir = PF_IN;
case ICMP_IPV6_IAMHERE:
*icmptype = ICMP_IPV6_WHEREAREYOU;
*icmpid = 0; /* Nothing sane to match on! */
break;
case ICMP_MOBILE_REGREQUEST:
*icmp_dir = PF_IN;
case ICMP_MOBILE_REGREPLY:
*icmptype = ICMP_MOBILE_REGREQUEST;
*icmpid = 0; /* Nothing sane to match on! */
break;
case ICMP_ROUTERSOLICIT:
*icmp_dir = PF_IN;
case ICMP_ROUTERADVERT:
*icmptype = ICMP_ROUTERSOLICIT;
*icmpid = 0; /* Nothing sane to match on! */
break;
#ifdef INET6
case ICMP6_ECHO_REQUEST:
*icmp_dir = PF_IN;
case ICMP6_ECHO_REPLY:
*icmptype = ICMP6_ECHO_REQUEST;
*icmpid = pd->hdr.icmp6.icmp6_id;
break;
case MLD_LISTENER_QUERY:
*icmp_dir = PF_IN;
case MLD_LISTENER_REPORT: {
*icmptype = MLD_LISTENER_QUERY;
*icmpid = 0;
break;
}
/* ICMP6_FQDN and ICMP6_NI query/reply are the same type as ICMP6_WRU */
case ICMP6_WRUREQUEST:
*icmp_dir = PF_IN;
case ICMP6_WRUREPLY:
*icmptype = ICMP6_WRUREQUEST;
*icmpid = 0; /* Nothing sane to match on! */
break;
case MLD_MTRACE:
*icmp_dir = PF_IN;
case MLD_MTRACE_RESP:
*icmptype = MLD_MTRACE;
*icmpid = 0; /* Nothing sane to match on! */
break;
case ND_NEIGHBOR_SOLICIT:
*icmp_dir = PF_IN;
case ND_NEIGHBOR_ADVERT: {
*icmptype = ND_NEIGHBOR_SOLICIT;
*multi = PF_ICMP_MULTI_SOLICITED;
*icmpid = 0;
break;
}
#endif /* INET6 */
/* These ICMP types map to other connections */
case ICMP_UNREACH:
case ICMP_SOURCEQUENCH:
case ICMP_REDIRECT:
case ICMP_TIMXCEED:
case ICMP_PARAMPROB:
#ifdef INET6
/*
* ICMP6_TIME_EXCEEDED is the same type as ICMP_UNREACH
* ND_REDIRECT can't be in this list because the triggering packet
* header is optional.
*/
case ICMP6_PACKET_TOO_BIG:
#endif /* INET6 */
/* These will not be used, but set them anyways */
*icmp_dir = PF_IN;
*icmptype = htons(type);
*icmpid = 0;
return (1); /* These types are matched to other state */
/*
* All remaining ICMP types get their own states,
* and will only match in one direction.
*/
default:
*icmp_dir = PF_IN;
*icmptype = type;
*icmpid = 0;
break;
}
HTONS(*icmptype);
return (0);
}
void
pf_intr(void *v)
{
@ -4566,8 +4422,8 @@ pf_test_rule(struct pf_krule **rm, struct pf_kstate **sm, struct pfi_kkif *kif,
int tag = -1;
int asd = 0;
int match = 0;
int state_icmp = 0, icmp_dir, multi;
u_int16_t sport = 0, dport = 0, virtual_type, virtual_id;
int state_icmp = 0;
u_int16_t sport = 0, dport = 0;
u_int16_t bproto_sum = 0, bip_sum = 0;
u_int8_t icmptype = 0, icmpcode = 0;
struct pf_kanchor_stackframe anchor_stack[PF_ANCHOR_STACKSIZE];
@ -4601,37 +4457,33 @@ pf_test_rule(struct pf_krule **rm, struct pf_kstate **sm, struct pfi_kkif *kif,
case IPPROTO_ICMP:
if (pd->af != AF_INET)
break;
sport = dport = pd->hdr.icmp.icmp_id;
hdrlen = sizeof(pd->hdr.icmp);
icmptype = pd->hdr.icmp.icmp_type;
icmpcode = pd->hdr.icmp.icmp_code;
state_icmp = pf_icmp_mapping(pd, icmptype,
&icmp_dir, &multi, &virtual_id, &virtual_type);
if (icmp_dir == PF_IN) {
sport = virtual_id;
dport = virtual_type;
} else {
sport = virtual_type;
dport = virtual_id;
}
if (icmptype == ICMP_UNREACH ||
icmptype == ICMP_SOURCEQUENCH ||
icmptype == ICMP_REDIRECT ||
icmptype == ICMP_TIMXCEED ||
icmptype == ICMP_PARAMPROB)
state_icmp++;
break;
#endif /* INET */
#ifdef INET6
case IPPROTO_ICMPV6:
if (af != AF_INET6)
break;
sport = dport = pd->hdr.icmp6.icmp6_id;
hdrlen = sizeof(pd->hdr.icmp6);
icmptype = pd->hdr.icmp6.icmp6_type;
icmpcode = pd->hdr.icmp6.icmp6_code;
state_icmp = pf_icmp_mapping(pd, icmptype,
&icmp_dir, &multi, &virtual_id, &virtual_type);
if (icmp_dir == PF_IN) {
sport = virtual_id;
dport = virtual_type;
} else {
sport = virtual_type;
dport = virtual_id;
}
if (icmptype == ICMP6_DST_UNREACH ||
icmptype == ICMP6_PACKET_TOO_BIG ||
icmptype == ICMP6_TIME_EXCEEDED ||
icmptype == ICMP6_PARAM_PROB)
state_icmp++;
break;
#endif /* INET6 */
default:
@ -4725,6 +4577,7 @@ pf_test_rule(struct pf_krule **rm, struct pf_kstate **sm, struct pfi_kkif *kif,
}
#ifdef INET
case IPPROTO_ICMP:
nk->port[0] = nk->port[1];
if (PF_ANEQ(saddr, &nk->addr[pd->sidx], AF_INET))
pf_change_a(&saddr->v4.s_addr, pd->ip_sum,
nk->addr[pd->sidx].v4.s_addr, 0);
@ -4733,12 +4586,11 @@ pf_test_rule(struct pf_krule **rm, struct pf_kstate **sm, struct pfi_kkif *kif,
pf_change_a(&daddr->v4.s_addr, pd->ip_sum,
nk->addr[pd->didx].v4.s_addr, 0);
if (virtual_type == ICMP_ECHO &&
nk->port[pd->sidx] != pd->hdr.icmp.icmp_id) {
if (nk->port[1] != pd->hdr.icmp.icmp_id) {
pd->hdr.icmp.icmp_cksum = pf_cksum_fixup(
pd->hdr.icmp.icmp_cksum, sport,
nk->port[pd->sidx], 0);
pd->hdr.icmp.icmp_id = nk->port[pd->sidx];
nk->port[1], 0);
pd->hdr.icmp.icmp_id = nk->port[1];
pd->sport = &pd->hdr.icmp.icmp_id;
}
m_copyback(m, off, ICMP_MINLEN, (caddr_t)&pd->hdr.icmp);
@ -4746,6 +4598,7 @@ pf_test_rule(struct pf_krule **rm, struct pf_kstate **sm, struct pfi_kkif *kif,
#endif /* INET */
#ifdef INET6
case IPPROTO_ICMPV6:
nk->port[0] = nk->port[1];
if (PF_ANEQ(saddr, &nk->addr[pd->sidx], AF_INET6))
pf_change_a6(saddr, &pd->hdr.icmp6.icmp6_cksum,
&nk->addr[pd->sidx], 0);
@ -6578,73 +6431,15 @@ pf_multihome_scan_asconf(struct mbuf *m, int start, int len,
return (pf_multihome_scan(m, start, len, pd, kif, SCTP_ADD_IP_ADDRESS));
}
int
pf_icmp_state_lookup(struct pf_state_key_cmp *key, struct pf_pdesc *pd,
struct pf_kstate **state, struct mbuf *m, int direction, struct pfi_kkif *kif,
u_int16_t icmpid, u_int16_t type, int icmp_dir, int *iidx, int multi)
{
key->af = pd->af;
key->proto = pd->proto;
if (icmp_dir == PF_IN) {
*iidx = pd->sidx;
key->port[pd->sidx] = icmpid;
key->port[pd->didx] = type;
} else {
*iidx = pd->didx;
key->port[pd->sidx] = type;
key->port[pd->didx] = icmpid;
}
if (pd->af == AF_INET6 && multi != PF_ICMP_MULTI_NONE) {
switch (multi) {
case PF_ICMP_MULTI_SOLICITED:
key->addr[pd->sidx].addr32[0] = IPV6_ADDR_INT32_MLL;
key->addr[pd->sidx].addr32[1] = 0;
key->addr[pd->sidx].addr32[2] = IPV6_ADDR_INT32_ONE;
key->addr[pd->sidx].addr32[3] = pd->src->addr32[3];
key->addr[pd->sidx].addr8[12] = 0xff;
break;
case PF_ICMP_MULTI_LINK:
key->addr[pd->sidx].addr32[0] = IPV6_ADDR_INT32_MLL;
key->addr[pd->sidx].addr32[1] = 0;
key->addr[pd->sidx].addr32[2] = 0;
key->addr[pd->sidx].addr32[3] = IPV6_ADDR_INT32_ONE;
break;
}
} else
PF_ACPY(&key->addr[pd->sidx], pd->src, key->af);
PF_ACPY(&key->addr[pd->didx], pd->dst, key->af);
STATE_LOOKUP(kif, key, *state, pd);
/* Is this ICMP message flowing in right direction? */
if ((*state)->rule.ptr->type &&
(((*state)->direction == direction) ?
PF_IN : PF_OUT) != icmp_dir) {
if (V_pf_status.debug >= PF_DEBUG_MISC) {
printf("pf: icmp type %d in wrong direction (%d): ",
icmp_dir, pd->dir);
pf_print_state(*state);
printf("\n");
}
return (PF_DROP);
}
return (-1);
}
static int
pf_test_state_icmp(struct pf_kstate **state, struct pfi_kkif *kif,
struct mbuf *m, int off, void *h, struct pf_pdesc *pd, u_short *reason)
{
struct pf_addr *saddr = pd->src, *daddr = pd->dst;
u_int16_t *icmpsum, virtual_id, virtual_type;
u_int16_t icmpid = 0, *icmpsum;
u_int8_t icmptype, icmpcode;
int icmp_dir, iidx, ret, multi;
int state_icmp = 0;
struct pf_state_key_cmp key;
#ifdef INET
u_int16_t icmpid;
#endif
MPASS(*state == NULL);
bzero(&key, sizeof(key));
switch (pd->proto) {
@ -6654,43 +6449,49 @@ pf_test_state_icmp(struct pf_kstate **state, struct pfi_kkif *kif,
icmpcode = pd->hdr.icmp.icmp_code;
icmpid = pd->hdr.icmp.icmp_id;
icmpsum = &pd->hdr.icmp.icmp_cksum;
if (icmptype == ICMP_UNREACH ||
icmptype == ICMP_SOURCEQUENCH ||
icmptype == ICMP_REDIRECT ||
icmptype == ICMP_TIMXCEED ||
icmptype == ICMP_PARAMPROB)
state_icmp++;
break;
#endif /* INET */
#ifdef INET6
case IPPROTO_ICMPV6:
icmptype = pd->hdr.icmp6.icmp6_type;
icmpcode = pd->hdr.icmp6.icmp6_code;
#ifdef INET
icmpid = pd->hdr.icmp6.icmp6_id;
#endif
icmpsum = &pd->hdr.icmp6.icmp6_cksum;
if (icmptype == ICMP6_DST_UNREACH ||
icmptype == ICMP6_PACKET_TOO_BIG ||
icmptype == ICMP6_TIME_EXCEEDED ||
icmptype == ICMP6_PARAM_PROB)
state_icmp++;
break;
#endif /* INET6 */
}
if (pf_icmp_mapping(pd, icmptype, &icmp_dir, &multi,
&virtual_id, &virtual_type) == 0) {
if (!state_icmp) {
/*
* ICMP query/reply message not related to a TCP/UDP packet.
* Search for an ICMP state.
*/
ret = pf_icmp_state_lookup(&key, pd, state, m, pd->dir,
kif, virtual_id, virtual_type, icmp_dir, &iidx,
PF_ICMP_MULTI_NONE);
if (ret >= 0) {
if (ret == PF_DROP && pd->af == AF_INET6 &&
icmp_dir == PF_OUT) {
if (*state != NULL)
PF_STATE_UNLOCK((*state));
ret = pf_icmp_state_lookup(&key, pd, state, m,
pd->dir, kif, virtual_id, virtual_type,
icmp_dir, &iidx, multi);
if (ret >= 0)
return (ret);
} else
return (ret);
key.af = pd->af;
key.proto = pd->proto;
key.port[0] = key.port[1] = icmpid;
if (pd->dir == PF_IN) { /* wire side, straight */
PF_ACPY(&key.addr[0], pd->src, key.af);
PF_ACPY(&key.addr[1], pd->dst, key.af);
} else { /* stack side, reverse */
PF_ACPY(&key.addr[1], pd->src, key.af);
PF_ACPY(&key.addr[0], pd->dst, key.af);
}
STATE_LOOKUP(kif, &key, *state, pd);
(*state)->expire = time_uptime;
(*state)->timeout = PFTM_ICMP_ERROR_REPLY;
@ -6713,14 +6514,14 @@ pf_test_state_icmp(struct pf_kstate **state, struct pfi_kkif *kif,
pd->ip_sum,
nk->addr[pd->didx].v4.s_addr, 0);
if (nk->port[iidx] !=
if (nk->port[0] !=
pd->hdr.icmp.icmp_id) {
pd->hdr.icmp.icmp_cksum =
pf_cksum_fixup(
pd->hdr.icmp.icmp_cksum, icmpid,
nk->port[iidx], 0);
nk->port[pd->sidx], 0);
pd->hdr.icmp.icmp_id =
nk->port[iidx];
nk->port[pd->sidx];
}
m_copyback(m, off, ICMP_MINLEN,
@ -7085,15 +6886,13 @@ pf_test_state_icmp(struct pf_kstate **state, struct pfi_kkif *kif,
return (PF_DROP);
}
icmpid = iih.icmp_id;
pf_icmp_mapping(&pd2, iih.icmp_type,
&icmp_dir, &multi, &virtual_id, &virtual_type);
key.af = pd2.af;
key.proto = IPPROTO_ICMP;
PF_ACPY(&key.addr[pd2.sidx], pd2.src, key.af);
PF_ACPY(&key.addr[pd2.didx], pd2.dst, key.af);
key.port[0] = key.port[1] = iih.icmp_id;
ret = pf_icmp_state_lookup(&key, &pd2, state, m,
pd->dir, kif, virtual_id, virtual_type,
icmp_dir, &iidx, PF_ICMP_MULTI_NONE);
if (ret >= 0)
return (ret);
STATE_LOOKUP(kif, &key, *state, pd);
/* translate source/destination address, if necessary */
if ((*state)->key[PF_SK_WIRE] !=
@ -7103,23 +6902,21 @@ pf_test_state_icmp(struct pf_kstate **state, struct pfi_kkif *kif,
if (PF_ANEQ(pd2.src,
&nk->addr[pd2.sidx], pd2.af) ||
(virtual_type == ICMP_ECHO &&
nk->port[iidx] != iih.icmp_id))
pf_change_icmp(pd2.src,
(virtual_type == ICMP_ECHO) ?
&iih.icmp_id : NULL,
nk->port[pd2.sidx] != iih.icmp_id)
pf_change_icmp(pd2.src, &iih.icmp_id,
daddr, &nk->addr[pd2.sidx],
(virtual_type == ICMP_ECHO) ?
nk->port[iidx] : 0, NULL,
nk->port[pd2.sidx], NULL,
pd2.ip_sum, icmpsum,
pd->ip_sum, 0, AF_INET);
if (PF_ANEQ(pd2.dst,
&nk->addr[pd2.didx], pd2.af))
pf_change_icmp(pd2.dst, NULL, NULL,
&nk->addr[pd2.didx], 0, NULL,
pd2.ip_sum, icmpsum, pd->ip_sum, 0,
AF_INET);
&nk->addr[pd2.didx], pd2.af) ||
nk->port[pd2.didx] != iih.icmp_id)
pf_change_icmp(pd2.dst, &iih.icmp_id,
saddr, &nk->addr[pd2.didx],
nk->port[pd2.didx], NULL,
pd2.ip_sum, icmpsum,
pd->ip_sum, 0, AF_INET);
m_copyback(m, off, ICMP_MINLEN, (caddr_t)&pd->hdr.icmp);
m_copyback(m, ipoff2, sizeof(h2), (caddr_t)&h2);
@ -7141,25 +6938,13 @@ pf_test_state_icmp(struct pf_kstate **state, struct pfi_kkif *kif,
return (PF_DROP);
}
pf_icmp_mapping(&pd2, iih.icmp6_type,
&icmp_dir, &multi, &virtual_id, &virtual_type);
ret = pf_icmp_state_lookup(&key, &pd2, state, m,
pd->dir, kif, virtual_id, virtual_type,
icmp_dir, &iidx, PF_ICMP_MULTI_NONE);
if (ret >= 0) {
if (ret == PF_DROP && pd->af == AF_INET6 &&
icmp_dir == PF_OUT) {
if (*state != NULL)
PF_STATE_UNLOCK((*state));
ret = pf_icmp_state_lookup(&key, pd,
state, m, pd->dir, kif,
virtual_id, virtual_type,
icmp_dir, &iidx, multi);
if (ret >= 0)
return (ret);
} else
return (ret);
}
key.af = pd2.af;
key.proto = IPPROTO_ICMPV6;
PF_ACPY(&key.addr[pd2.sidx], pd2.src, key.af);
PF_ACPY(&key.addr[pd2.didx], pd2.dst, key.af);
key.port[0] = key.port[1] = iih.icmp6_id;
STATE_LOOKUP(kif, &key, *state, pd);
/* translate source/destination address, if necessary */
if ((*state)->key[PF_SK_WIRE] !=
@ -7169,21 +6954,19 @@ pf_test_state_icmp(struct pf_kstate **state, struct pfi_kkif *kif,
if (PF_ANEQ(pd2.src,
&nk->addr[pd2.sidx], pd2.af) ||
((virtual_type == ICMP6_ECHO_REQUEST) &&
nk->port[pd2.sidx] != iih.icmp6_id))
pf_change_icmp(pd2.src,
(virtual_type == ICMP6_ECHO_REQUEST)
? &iih.icmp6_id : NULL,
nk->port[pd2.sidx] != iih.icmp6_id)
pf_change_icmp(pd2.src, &iih.icmp6_id,
daddr, &nk->addr[pd2.sidx],
(virtual_type == ICMP6_ECHO_REQUEST)
? nk->port[iidx] : 0, NULL,
nk->port[pd2.sidx], NULL,
pd2.ip_sum, icmpsum,
pd->ip_sum, 0, AF_INET6);
if (PF_ANEQ(pd2.dst,
&nk->addr[pd2.didx], pd2.af))
pf_change_icmp(pd2.dst, NULL, NULL,
&nk->addr[pd2.didx], 0, NULL,
&nk->addr[pd2.didx], pd2.af) ||
nk->port[pd2.didx] != iih.icmp6_id)
pf_change_icmp(pd2.dst, &iih.icmp6_id,
saddr, &nk->addr[pd2.didx],
nk->port[pd2.didx], NULL,
pd2.ip_sum, icmpsum,
pd->ip_sum, 0, AF_INET6);

View file

@ -225,23 +225,6 @@ pf_get_sport(sa_family_t af, u_int8_t proto, struct pf_krule *r,
if (pf_map_addr(af, r, saddr, naddr, NULL, &init_addr, sn))
return (1);
if (proto == IPPROTO_ICMP) {
if (*nport == htons(ICMP_ECHO)) {
low = 1;
high = 65535;
} else
return (0); /* Don't try to modify non-echo ICMP */
}
#ifdef INET6
if (proto == IPPROTO_ICMPV6) {
if (*nport == htons(ICMP6_ECHO_REQUEST)) {
low = 1;
high = 65535;
} else
return (0); /* Don't try to modify non-echo ICMP */
}
#endif /* INET6 */
bzero(&key, sizeof(key));
key.af = af;
key.proto = proto;
@ -650,7 +633,7 @@ pf_get_translation(struct pf_pdesc *pd, struct mbuf *m, int off,
switch (r->action) {
case PF_NAT:
if (pd->proto == IPPROTO_ICMP) {
low = 1;
low = 1;
high = 65535;
} else {
low = r->rpool.proxy_port[0];