diff options
-rw-r--r-- | share/man/man9/mbuf_tags.9 | 14 | ||||
-rw-r--r-- | sys/net/pf.c | 46 | ||||
-rw-r--r-- | sys/netinet/in_pcb.c | 47 | ||||
-rw-r--r-- | sys/netinet/in_pcb.h | 6 | ||||
-rw-r--r-- | sys/netinet/tcp_input.c | 8 | ||||
-rw-r--r-- | sys/netinet/tcp_usrreq.c | 6 | ||||
-rw-r--r-- | sys/netinet/udp_usrreq.c | 10 | ||||
-rw-r--r-- | sys/sys/mbuf.h | 3 |
8 files changed, 108 insertions, 32 deletions
diff --git a/share/man/man9/mbuf_tags.9 b/share/man/man9/mbuf_tags.9 index 9a598ec858d..953e835200d 100644 --- a/share/man/man9/mbuf_tags.9 +++ b/share/man/man9/mbuf_tags.9 @@ -1,4 +1,4 @@ -.\" $OpenBSD: mbuf_tags.9,v 1.17 2003/06/06 20:56:32 jmc Exp $ +.\" $OpenBSD: mbuf_tags.9,v 1.18 2003/12/08 07:07:35 mcbride Exp $ .\" .\" The author of this man page is Angelos D. Keromytis (angelos@cis.upenn.edu) .\" @@ -193,6 +193,18 @@ The tag contains the ID of the queue this packet should go to. Used by .Xr pf 4 to tag packets and filtering on those later on. +.It PACKET_TAG_PF_TRANSLATE_LOCALHOST +Used by +.Xr pf 4 +to mark TCP and UDP packets redirected to loopback addresses. +The functions tcp_input() and udp_input() reverse the order of +lookups in in_pcblookup_listen(), when this tag is present, so +unspecific listeners are matched before specific ones. +This prevents external connections from appearing local to daemons +such as +.Xr portmap 8 +listening on both unspecific and specific loopback sockets in order to +grant higher privileges to local users. .El .Pp .Fn m_tag_free diff --git a/sys/net/pf.c b/sys/net/pf.c index 95765175c08..57bc51437dc 100644 --- a/sys/net/pf.c +++ b/sys/net/pf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pf.c,v 1.404 2003/11/28 01:06:59 mcbride Exp $ */ +/* $OpenBSD: pf.c,v 1.405 2003/12/08 07:07:35 mcbride Exp $ */ /* * Copyright (c) 2001 Daniel Hartmeier @@ -204,6 +204,7 @@ int pf_check_proto_cksum(struct mbuf *, int, int, u_int8_t, sa_family_t); int pf_addr_wrap_neq(struct pf_addr_wrap *, struct pf_addr_wrap *); +static int pf_add_mbuf_tag(struct mbuf *, u_int); struct pf_pool_limit pf_pool_limits[PF_LIMIT_MAX] = @@ -2001,7 +2002,7 @@ pf_socket_lookup(uid_t *uid, gid_t *gid, int direction, struct pf_pdesc *pd) case AF_INET: inp = in_pcbhashlookup(tb, saddr->v4, sport, daddr->v4, dport); if (inp == NULL) { - inp = in_pcblookup_listen(tb, daddr->v4, dport); + inp = in_pcblookup_listen(tb, daddr->v4, dport, 0); if (inp == NULL) return (0); } @@ -2011,7 +2012,7 @@ pf_socket_lookup(uid_t *uid, gid_t *gid, int direction, struct pf_pdesc *pd) inp = in6_pcbhashlookup(tb, &saddr->v6, sport, &daddr->v6, dport); if (inp == NULL) { - inp = in6_pcblookup_listen(tb, &daddr->v6, dport); + inp = in6_pcblookup_listen(tb, &daddr->v6, dport, 0); if (inp == NULL) return (0); } @@ -4829,6 +4830,20 @@ pf_check_proto_cksum(struct mbuf *m, int off, int len, u_int8_t p, sa_family_t a return (0); } +static int +pf_add_mbuf_tag(struct mbuf *m, u_int tag) +{ + struct m_tag *mtag; + + if (m_tag_find(m, tag, NULL) != NULL) + return (0); + mtag = m_tag_get(tag, 0, M_NOWAIT); + if (mtag == NULL) + return (1); + m_tag_prepend(m, mtag); + return (0); +} + #ifdef INET int pf_test(int dir, struct ifnet *ifp, struct mbuf **m0) @@ -5016,6 +5031,21 @@ done: } #endif + /* + * connections redirected to loopback should not match sockets + * bound specifically to loopback due to security implications, + * see tcp_input() and in_pcblookup_listen(). + */ + if (dir == PF_IN && action == PF_PASS && (pd.proto == IPPROTO_TCP || + pd.proto == IPPROTO_UDP) && s != NULL && s->nat_rule.ptr != NULL && + (s->nat_rule.ptr->action == PF_RDR || + s->nat_rule.ptr->action == PF_BINAT) && + (ntohl(pd.dst->v4.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET && + pf_add_mbuf_tag(m, PACKET_TAG_PF_TRANSLATE_LOCALHOST)) { + action = PF_DROP; + REASON_SET(&reason, PFRES_MEMORY); + } + if (log) PFLOG_PACKET(ifp, h, m, AF_INET, dir, reason, r, a, ruleset); @@ -5271,6 +5301,16 @@ done: } #endif + if (dir == PF_IN && action == PF_PASS && (pd.proto == IPPROTO_TCP || + pd.proto == IPPROTO_UDP) && s != NULL && s->nat_rule.ptr != NULL && + (s->nat_rule.ptr->action == PF_RDR || + s->nat_rule.ptr->action == PF_BINAT) && + IN6_IS_ADDR_LOOPBACK(&pd.dst->v6) && + pf_add_mbuf_tag(m, PACKET_TAG_PF_TRANSLATE_LOCALHOST)) { + action = PF_DROP; + REASON_SET(&reason, PFRES_MEMORY); + } + if (log) PFLOG_PACKET(ifp, h, m, AF_INET6, dir, reason, r, a, ruleset); diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c index 5d8e28ff09c..82c2c71ef96 100644 --- a/sys/netinet/in_pcb.c +++ b/sys/netinet/in_pcb.c @@ -1,4 +1,4 @@ -/* $OpenBSD: in_pcb.c,v 1.69 2003/11/04 21:43:16 markus Exp $ */ +/* $OpenBSD: in_pcb.c,v 1.70 2003/12/08 07:07:36 mcbride Exp $ */ /* $NetBSD: in_pcb.c,v 1.25 1996/02/13 23:41:53 christos Exp $ */ /* @@ -1055,35 +1055,45 @@ in6_pcbhashlookup(table, faddr, fport_arg, laddr, lport_arg) * *.* <-> *.lport */ struct inpcb * -in_pcblookup_listen(table, laddr, lport_arg) +in_pcblookup_listen(table, laddr, lport_arg, reverse) struct inpcbtable *table; struct in_addr laddr; u_int lport_arg; + int reverse; { struct inpcbhead *head; + struct in_addr *key1, *key2; register struct inpcb *inp; u_int16_t lport = lport_arg; - head = INPCBHASH(table, &zeroin_addr, 0, &laddr, lport); + if (reverse) { + key1 = &zeroin_addr; + key2 = &laddr; + } else { + key1 = &laddr; + key2 = &zeroin_addr; + } + + head = INPCBHASH(table, &zeroin_addr, 0, key1, lport); LIST_FOREACH(inp, head, inp_hash) { #ifdef INET6 if (inp->inp_flags & INP_IPV6) continue; /*XXX*/ #endif if (inp->inp_lport == lport && inp->inp_fport == 0 && - inp->inp_laddr.s_addr == laddr.s_addr && + inp->inp_laddr.s_addr == key1->s_addr && inp->inp_faddr.s_addr == INADDR_ANY) break; } - if (inp == NULL && laddr.s_addr != INADDR_ANY) { - head = INPCBHASH(table, &zeroin_addr, 0, &zeroin_addr, lport); + if (inp == NULL && key1->s_addr != key2->s_addr) { + head = INPCBHASH(table, &zeroin_addr, 0, key2, lport); LIST_FOREACH(inp, head, inp_hash) { #ifdef INET6 if (inp->inp_flags & INP_IPV6) continue; /*XXX*/ #endif if (inp->inp_lport == lport && inp->inp_fport == 0 && - inp->inp_laddr.s_addr == INADDR_ANY && + inp->inp_laddr.s_addr == key2->s_addr && inp->inp_faddr.s_addr == INADDR_ANY) break; } @@ -1108,32 +1118,41 @@ in_pcblookup_listen(table, laddr, lport_arg) #ifdef INET6 struct inpcb * -in6_pcblookup_listen(table, laddr, lport_arg) +in6_pcblookup_listen(table, laddr, lport_arg, reverse) struct inpcbtable *table; struct in6_addr *laddr; u_int lport_arg; + int reverse; { struct inpcbhead *head; + struct in6_addr *key1, *key2; register struct inpcb *inp; u_int16_t lport = lport_arg; - head = IN6PCBHASH(table, &zeroin6_addr, 0, laddr, lport); + if (reverse) { + key1 = &zeroin6_addr; + key2 = laddr; + } else { + key1 = laddr; + key2 = &zeroin6_addr; + } + + head = IN6PCBHASH(table, &zeroin6_addr, 0, key1, lport); LIST_FOREACH(inp, head, inp_hash) { if (!(inp->inp_flags & INP_IPV6)) continue; if (inp->inp_lport == lport && inp->inp_fport == 0 && - IN6_ARE_ADDR_EQUAL(&inp->inp_laddr6, laddr) && + IN6_ARE_ADDR_EQUAL(&inp->inp_laddr6, key1) && IN6_IS_ADDR_UNSPECIFIED(&inp->inp_faddr6)) break; } - if (inp == NULL && !IN6_IS_ADDR_UNSPECIFIED(laddr)) { - head = IN6PCBHASH(table, &zeroin6_addr, 0, - &zeroin6_addr, lport); + if (inp == NULL && ! IN6_ARE_ADDR_EQUAL(key1, key2)) { + head = IN6PCBHASH(table, &zeroin6_addr, 0, key2, lport); LIST_FOREACH(inp, head, inp_hash) { if (!(inp->inp_flags & INP_IPV6)) continue; if (inp->inp_lport == lport && inp->inp_fport == 0 && - IN6_IS_ADDR_UNSPECIFIED(&inp->inp_laddr6) && + IN6_ARE_ADDR_EQUAL(&inp->inp_laddr6, key2) && IN6_IS_ADDR_UNSPECIFIED(&inp->inp_faddr6)) break; } diff --git a/sys/netinet/in_pcb.h b/sys/netinet/in_pcb.h index 5f5ec71b589..27d411b4a5c 100644 --- a/sys/netinet/in_pcb.h +++ b/sys/netinet/in_pcb.h @@ -1,4 +1,4 @@ -/* $OpenBSD: in_pcb.h,v 1.47 2003/11/04 21:43:16 markus Exp $ */ +/* $OpenBSD: in_pcb.h,v 1.48 2003/12/08 07:07:36 mcbride Exp $ */ /* $NetBSD: in_pcb.h,v 1.14 1996/02/13 23:42:00 christos Exp $ */ /* @@ -239,14 +239,14 @@ struct inpcb * in_pcbhashlookup(struct inpcbtable *, struct in_addr, u_int, struct in_addr, u_int); struct inpcb * - in_pcblookup_listen(struct inpcbtable *, struct in_addr, u_int); + in_pcblookup_listen(struct inpcbtable *, struct in_addr, u_int, int); #ifdef INET6 struct inpcb * in6_pcbhashlookup(struct inpcbtable *, struct in6_addr *, u_int, struct in6_addr *, u_int); struct inpcb * in6_pcblookup_listen(struct inpcbtable *, - struct in6_addr *, u_int); + struct in6_addr *, u_int, int); int in6_pcbbind(struct inpcb *, struct mbuf *); int in6_pcbconnect(struct inpcb *, struct mbuf *); int in6_setsockaddr(struct inpcb *, struct mbuf *); diff --git a/sys/netinet/tcp_input.c b/sys/netinet/tcp_input.c index a786851a75a..945076d1f62 100644 --- a/sys/netinet/tcp_input.c +++ b/sys/netinet/tcp_input.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tcp_input.c,v 1.134 2003/11/04 21:43:16 markus Exp $ */ +/* $OpenBSD: tcp_input.c,v 1.135 2003/12/08 07:07:36 mcbride Exp $ */ /* $NetBSD: tcp_input.c,v 1.23 1996/02/13 23:43:44 christos Exp $ */ /* @@ -671,12 +671,14 @@ findpcb: #ifdef INET6 case AF_INET6: inp = in6_pcblookup_listen(&tcbtable, - &ip6->ip6_dst, th->th_dport); + &ip6->ip6_dst, th->th_dport, m_tag_find(m, + PACKET_TAG_PF_TRANSLATE_LOCALHOST, NULL) != NULL); break; #endif /* INET6 */ case AF_INET: inp = in_pcblookup_listen(&tcbtable, - ip->ip_dst, th->th_dport); + ip->ip_dst, th->th_dport, m_tag_find(m, + PACKET_TAG_PF_TRANSLATE_LOCALHOST, NULL) != NULL); break; } /* diff --git a/sys/netinet/tcp_usrreq.c b/sys/netinet/tcp_usrreq.c index ff9983289b7..7512d1a99b0 100644 --- a/sys/netinet/tcp_usrreq.c +++ b/sys/netinet/tcp_usrreq.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tcp_usrreq.c,v 1.72 2003/11/04 21:43:16 markus Exp $ */ +/* $OpenBSD: tcp_usrreq.c,v 1.73 2003/12/08 07:07:36 mcbride Exp $ */ /* $NetBSD: tcp_usrreq.c,v 1.20 1996/02/13 23:44:16 christos Exp $ */ /* @@ -835,12 +835,12 @@ tcp_ident(oldp, oldlenp, newp, newlen) #ifdef INET6 case AF_INET6: inp = in6_pcblookup_listen(&tcbtable, - &l6, lin6->sin6_port); + &l6, lin6->sin6_port, 0); break; #endif case AF_INET: inp = in_pcblookup_listen(&tcbtable, - lin->sin_addr, lin->sin_port); + lin->sin_addr, lin->sin_port, 0); break; } } diff --git a/sys/netinet/udp_usrreq.c b/sys/netinet/udp_usrreq.c index 2d5aed4bbec..b5823ac6073 100644 --- a/sys/netinet/udp_usrreq.c +++ b/sys/netinet/udp_usrreq.c @@ -1,4 +1,4 @@ -/* $OpenBSD: udp_usrreq.c,v 1.93 2003/12/02 23:16:29 markus Exp $ */ +/* $OpenBSD: udp_usrreq.c,v 1.94 2003/12/08 07:07:36 mcbride Exp $ */ /* $NetBSD: udp_usrreq.c,v 1.28 1996/03/16 23:54:03 christos Exp $ */ /* @@ -525,11 +525,13 @@ udp_input(struct mbuf *m, ...) #ifdef INET6 if (ip6) { inp = in6_pcblookup_listen(&udbtable, - &ip6->ip6_dst, uh->uh_dport); + &ip6->ip6_dst, uh->uh_dport, m_tag_find(m, + PACKET_TAG_PF_TRANSLATE_LOCALHOST, NULL) != NULL); } else #endif /* INET6 */ inp = in_pcblookup_listen(&udbtable, - ip->ip_dst, uh->uh_dport); + ip->ip_dst, uh->uh_dport, m_tag_find(m, + PACKET_TAG_PF_TRANSLATE_LOCALHOST, NULL) != NULL); if (inp == 0) { udpstat.udps_noport++; if (m->m_flags & (M_BCAST | M_MCAST)) { @@ -820,7 +822,7 @@ udp6_ctlinput(cmd, sa, d) * is really ours. */ else if (in6_pcblookup_listen(&udbtable, - &sa6_src.sin6_addr, uh.uh_sport)) + &sa6_src.sin6_addr, uh.uh_sport, 0); valid = 1; #endif diff --git a/sys/sys/mbuf.h b/sys/sys/mbuf.h index 1f22798d048..d860e2dfc17 100644 --- a/sys/sys/mbuf.h +++ b/sys/sys/mbuf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: mbuf.h,v 1.73 2003/10/17 21:04:59 mcbride Exp $ */ +/* $OpenBSD: mbuf.h,v 1.74 2003/12/08 07:07:36 mcbride Exp $ */ /* $NetBSD: mbuf.h,v 1.19 1996/02/09 18:25:14 christos Exp $ */ /* @@ -598,6 +598,7 @@ struct m_tag *m_tag_next(struct mbuf *, struct m_tag *); #define PACKET_TAG_PF_QID 14 /* PF queue id */ #define PACKET_TAG_PF_TAG 15 /* PF tags */ #define PACKET_TAG_CARP 16 /* CARP info */ +#define PACKET_TAG_PF_TRANSLATE_LOCALHOST 17 /* translated to localhost */ #ifdef MBTYPES int mbtypes[] = { /* XXX */ |