From 7f08016fb8be5d917246c3d66ce8dec27378d459 Mon Sep 17 00:00:00 2001 From: Kevin Krakauer Date: Tue, 31 Oct 2023 21:15:06 -0700 Subject: [PATCH] iptables: check revision numbers and support owner matcher v1 This change also replaces use of kernel.Task with a narrower type (IDMapper) in preparation for a follow-up CL. PiperOrigin-RevId: 578387814 --- pkg/abi/linux/netfilter.go | 28 +++- pkg/sentry/socket/netfilter/BUILD | 14 +- pkg/sentry/socket/netfilter/extensions.go | 50 ++++-- pkg/sentry/socket/netfilter/ipv4.go | 5 +- pkg/sentry/socket/netfilter/ipv6.go | 5 +- pkg/sentry/socket/netfilter/istio_blob | Bin 0 -> 5688 bytes pkg/sentry/socket/netfilter/netfilter.go | 19 ++- pkg/sentry/socket/netfilter/netfilter_test.go | 56 +++++++ pkg/sentry/socket/netfilter/owner_matcher.go | 16 +- .../socket/netfilter/owner_matcher_v1.go | 152 ++++++++++++++++++ pkg/sentry/socket/netfilter/tcp_matcher.go | 11 +- pkg/sentry/socket/netfilter/udp_matcher.go | 11 +- pkg/sentry/socket/netstack/netstack.go | 4 +- 13 files changed, 329 insertions(+), 42 deletions(-) create mode 100755 pkg/sentry/socket/netfilter/istio_blob create mode 100644 pkg/sentry/socket/netfilter/netfilter_test.go create mode 100644 pkg/sentry/socket/netfilter/owner_matcher_v1.go diff --git a/pkg/abi/linux/netfilter.go b/pkg/abi/linux/netfilter.go index 1470a5578..3c5f40c1e 100644 --- a/pkg/abi/linux/netfilter.go +++ b/pkg/abi/linux/netfilter.go @@ -633,8 +633,8 @@ const ( XT_UDP_INV_MASK = 0x03 ) -// IPTOwnerInfo holds data for matching packets with owner. It corresponds -// to struct ipt_owner_info in libxt_owner.c of iptables binary. +// IPTOwnerInfo holds data for matching packets with the owner v0 matcher. It +// corresponds to struct ipt_owner_info in libxt_owner.c of iptables binary. // // +marshal type IPTOwnerInfo struct { @@ -661,11 +661,29 @@ type IPTOwnerInfo struct { Invert uint8 `marshal:"unaligned"` } -// SizeOfIPTOwnerInfo is the size of an XTOwnerMatchInfo. +// SizeOfIPTOwnerInfo is the size of an IPTOwnerInfo. const SizeOfIPTOwnerInfo = 34 -// Flags in IPTOwnerInfo.Match. Corresponding constants are in -// include/uapi/linux/netfilter/xt_owner.h. +// XTOwnerMatchInfo holds data for matching packets with the owner v1 matcher. +// It corresponds to struct xt_owner_match_info in +// include/uapi/linux/netfilter/xt_owner.h +// +// +marshal +type XTOwnerMatchInfo struct { + UIDMin uint32 + UIDMax uint32 + GIDMin uint32 + GIDMax uint32 + Match uint8 + Invert uint8 + _ [2]byte +} + +// SizeOfXTOwnerMatchInfo is the size of an XTOwnerMatchInfo. +const SizeOfXTOwnerMatchInfo = 20 + +// Flags in IPTOwnerInfo.Match and XTOwnerMatchInfo.Match. Corresponding +// constants are in include/uapi/linux/netfilter/xt_owner.h. const ( // Match the UID of the packet. XT_OWNER_UID = 1 << 0 diff --git a/pkg/sentry/socket/netfilter/BUILD b/pkg/sentry/socket/netfilter/BUILD index 09bb4de18..edb5007b8 100644 --- a/pkg/sentry/socket/netfilter/BUILD +++ b/pkg/sentry/socket/netfilter/BUILD @@ -1,4 +1,4 @@ -load("//tools:defs.bzl", "go_library") +load("//tools:defs.bzl", "go_library", "go_test") package( default_applicable_licenses = ["//:license"], @@ -13,6 +13,7 @@ go_library( "ipv6.go", "netfilter.go", "owner_matcher.go", + "owner_matcher_v1.go", "targets.go", "tcp_matcher.go", "udp_matcher.go", @@ -35,3 +36,14 @@ go_library( "//pkg/tcpip/stack", ], ) + +go_test( + name = "netfilter_test", + srcs = ["netfilter_test.go"], + embedsrcs = ["istio_blob"], + library = ":netfilter", + deps = [ + "//pkg/sentry/kernel/auth", + "//pkg/tcpip/stack", + ], +) diff --git a/pkg/sentry/socket/netfilter/extensions.go b/pkg/sentry/socket/netfilter/extensions.go index 7606d2bbb..56ced4ae6 100644 --- a/pkg/sentry/socket/netfilter/extensions.go +++ b/pkg/sentry/socket/netfilter/extensions.go @@ -19,45 +19,65 @@ import ( "gvisor.dev/gvisor/pkg/abi/linux" "gvisor.dev/gvisor/pkg/bits" - "gvisor.dev/gvisor/pkg/sentry/kernel" "gvisor.dev/gvisor/pkg/syserr" "gvisor.dev/gvisor/pkg/tcpip" "gvisor.dev/gvisor/pkg/tcpip/stack" ) -// matchMaker knows how to (un)marshal the matcher named name(). +// matchMaker knows how to (un)marshal the matcher named name(). UPDATE THIS type matchMaker interface { // name is the matcher name as stored in the xt_entry_match struct. name() string + // revision is the match revision as stored in the xt_entry_match + // struct. + revision() uint8 + // marshal converts from a stack.Matcher to an ABI struct. marshal(matcher matcher) []byte // unmarshal converts from the ABI matcher struct to an // stack.Matcher. - unmarshal(task *kernel.Task, buf []byte, filter stack.IPHeaderFilter) (stack.Matcher, error) + unmarshal(mapper IDMapper, buf []byte, filter stack.IPHeaderFilter) (stack.Matcher, error) +} + +type matchKey struct { + name string + revision uint8 +} + +func key(mm matchMaker) matchKey { + return matchKey{ + name: mm.name(), + revision: mm.revision(), + } } type matcher interface { name() string + revision() uint8 } // matchMakers maps the name of supported matchers to the matchMaker that // marshals and unmarshals it. It is immutable after package initialization. -var matchMakers = map[string]matchMaker{} +var matchMakers = map[matchKey]matchMaker{} // registermatchMaker should be called by match extensions to register them // with the netfilter package. func registerMatchMaker(mm matchMaker) { - if _, ok := matchMakers[mm.name()]; ok { - panic(fmt.Sprintf("Multiple matches registered with name %q.", mm.name())) + if _, ok := matchMakers[key(mm)]; ok { + panic(fmt.Sprintf("Multiple matches registered with key %+v.", key(mm))) } - matchMakers[mm.name()] = mm + matchMakers[key(mm)] = mm } func marshalMatcher(mr stack.Matcher) []byte { matcher := mr.(matcher) - matchMaker, ok := matchMakers[matcher.name()] + key := matchKey{ + name: matcher.name(), + revision: matcher.revision(), + } + matchMaker, ok := matchMakers[key] if !ok { panic(fmt.Sprintf("Unknown matcher of type %T.", matcher)) } @@ -85,12 +105,16 @@ func marshalEntryMatch(name string, data []byte) []byte { return buf } -func unmarshalMatcher(task *kernel.Task, match linux.XTEntryMatch, filter stack.IPHeaderFilter, buf []byte) (stack.Matcher, error) { - matchMaker, ok := matchMakers[match.Name.String()] - if !ok { - return nil, fmt.Errorf("unsupported matcher with name %q", match.Name.String()) +func unmarshalMatcher(mapper IDMapper, match linux.XTEntryMatch, filter stack.IPHeaderFilter, buf []byte) (stack.Matcher, error) { + key := matchKey{ + name: match.Name.String(), + revision: match.Revision, } - return matchMaker.unmarshal(task, buf, filter) + matchMaker, ok := matchMakers[key] + if !ok { + return nil, fmt.Errorf("unsupported matcher with name %q and revision %d", match.Name.String(), match.Revision) + } + return matchMaker.unmarshal(mapper, buf, filter) } // targetMaker knows how to (un)marshal a target. Once registered, diff --git a/pkg/sentry/socket/netfilter/ipv4.go b/pkg/sentry/socket/netfilter/ipv4.go index f3dbf5612..853956837 100644 --- a/pkg/sentry/socket/netfilter/ipv4.go +++ b/pkg/sentry/socket/netfilter/ipv4.go @@ -18,7 +18,6 @@ import ( "fmt" "gvisor.dev/gvisor/pkg/abi/linux" - "gvisor.dev/gvisor/pkg/sentry/kernel" "gvisor.dev/gvisor/pkg/syserr" "gvisor.dev/gvisor/pkg/tcpip" "gvisor.dev/gvisor/pkg/tcpip/header" @@ -126,7 +125,7 @@ func getEntries4(table stack.Table, tablename linux.TableName) (linux.KernelIPTG return entries, info } -func modifyEntries4(task *kernel.Task, stk *stack.Stack, optVal []byte, replace *linux.IPTReplace, table *stack.Table) (map[uint32]int, *syserr.Error) { +func modifyEntries4(mapper IDMapper, stk *stack.Stack, optVal []byte, replace *linux.IPTReplace, table *stack.Table) (map[uint32]int, *syserr.Error) { nflog("set entries: setting entries in table %q", replace.Name.String()) // Convert input into a list of rules and their offsets. @@ -162,7 +161,7 @@ func modifyEntries4(task *kernel.Task, stk *stack.Stack, optVal []byte, replace nflog("entry doesn't have enough room for its matchers (only %d bytes remain)", len(optVal)) return nil, syserr.ErrInvalidArgument } - matchers, err := parseMatchers(task, filter, optVal[:matchersSize]) + matchers, err := parseMatchers(mapper, filter, optVal[:matchersSize]) if err != nil { nflog("failed to parse matchers: %v", err) return nil, syserr.ErrInvalidArgument diff --git a/pkg/sentry/socket/netfilter/ipv6.go b/pkg/sentry/socket/netfilter/ipv6.go index e1c02c100..51e1ca58b 100644 --- a/pkg/sentry/socket/netfilter/ipv6.go +++ b/pkg/sentry/socket/netfilter/ipv6.go @@ -18,7 +18,6 @@ import ( "fmt" "gvisor.dev/gvisor/pkg/abi/linux" - "gvisor.dev/gvisor/pkg/sentry/kernel" "gvisor.dev/gvisor/pkg/syserr" "gvisor.dev/gvisor/pkg/tcpip" "gvisor.dev/gvisor/pkg/tcpip/header" @@ -129,7 +128,7 @@ func getEntries6(table stack.Table, tablename linux.TableName) (linux.KernelIP6T return entries, info } -func modifyEntries6(task *kernel.Task, stk *stack.Stack, optVal []byte, replace *linux.IPTReplace, table *stack.Table) (map[uint32]int, *syserr.Error) { +func modifyEntries6(mapper IDMapper, stk *stack.Stack, optVal []byte, replace *linux.IPTReplace, table *stack.Table) (map[uint32]int, *syserr.Error) { nflog("set entries: setting entries in table %q", replace.Name.String()) // Convert input into a list of rules and their offsets. @@ -165,7 +164,7 @@ func modifyEntries6(task *kernel.Task, stk *stack.Stack, optVal []byte, replace nflog("entry doesn't have enough room for its matchers (only %d bytes remain)", len(optVal)) return nil, syserr.ErrInvalidArgument } - matchers, err := parseMatchers(task, filter, optVal[:matchersSize]) + matchers, err := parseMatchers(mapper, filter, optVal[:matchersSize]) if err != nil { nflog("failed to parse matchers: %v", err) return nil, syserr.ErrInvalidArgument diff --git a/pkg/sentry/socket/netfilter/istio_blob b/pkg/sentry/socket/netfilter/istio_blob new file mode 100755 index 0000000000000000000000000000000000000000..31142ffa08a929f49b57022db00c9f9a0dfd925c GIT binary patch literal 5688 zcmc~xEMdS0q!}0(6c`v7Ziqru889+1{Qv*|{|QD0h967}3^N!Q7+_)vOb{`MO4^WY zP-6=iW-u^9*~pXzG6xIWfdz{o6@|!k*T4V&|5MQ!7)F8I#e^Zsz(6RxXyq>Cc;q0% zT?X7(G*M9q6kg=Gi&o(U^BTxq0x%8|Jy7uob3y^b1{f2Cc3^M~3i1y^5kuy928Ve1 z$9wuY`G@+sAWP7Q4NG?m7*1d z;u}=oVQbG2Q{RzOPt-FofUO4g6~GQbBXaW51kkv+s!dROz~&E9(nCE1r0U1!HZt4= zZjP;BxWItTT^0=a<$0+^XugAU87)~E7(f^%0?J)uP_nVOYGnmpF)cN2v2TFIavYb%-4A%69;UB1n2>Ay&eqd%$lO{L4 z!Q23f8xV$ihhRS)*+2Nw8_axCX=;?aq}oZ0dUD(+Ur__{KkK!@vLl`6&FH literal 0 HcmV?d00001 diff --git a/pkg/sentry/socket/netfilter/netfilter.go b/pkg/sentry/socket/netfilter/netfilter.go index 278f07b72..ee99c8ecb 100644 --- a/pkg/sentry/socket/netfilter/netfilter.go +++ b/pkg/sentry/socket/netfilter/netfilter.go @@ -26,6 +26,7 @@ import ( "gvisor.dev/gvisor/pkg/hostarch" "gvisor.dev/gvisor/pkg/log" "gvisor.dev/gvisor/pkg/sentry/kernel" + "gvisor.dev/gvisor/pkg/sentry/kernel/auth" "gvisor.dev/gvisor/pkg/syserr" "gvisor.dev/gvisor/pkg/tcpip" "gvisor.dev/gvisor/pkg/tcpip/stack" @@ -34,7 +35,7 @@ import ( // enableLogging controls whether to log the (de)serialization of netfilter // structs between userspace and netstack. These logs are useful when // developing iptables, but can pollute sentry logs otherwise. -const enableLogging = false +const enableLogging = true // nflog logs messages related to the writing and reading of iptables. func nflog(format string, args ...any) { @@ -173,9 +174,15 @@ func setHooksAndUnderflow(info *linux.IPTGetinfo, table stack.Table, offset uint } } +// An IDMapper maps UIDs and GIDs to KUIDs and KGIDs. +type IDMapper interface { + MapToKUID(uid auth.UID) auth.KUID + MapToKGID(uid auth.GID) auth.KGID +} + // SetEntries sets iptables rules for a single table. See // net/ipv4/netfilter/ip_tables.c:translate_table for reference. -func SetEntries(task *kernel.Task, stk *stack.Stack, optVal []byte, ipv6 bool) *syserr.Error { +func SetEntries(mapper IDMapper, stk *stack.Stack, optVal []byte, ipv6 bool) *syserr.Error { var replace linux.IPTReplace optVal = replace.UnmarshalBytes(optVal) @@ -193,9 +200,9 @@ func SetEntries(task *kernel.Task, stk *stack.Stack, optVal []byte, ipv6 bool) * var err *syserr.Error var offsets map[uint32]int if ipv6 { - offsets, err = modifyEntries6(task, stk, optVal, &replace, &table) + offsets, err = modifyEntries6(mapper, stk, optVal, &replace, &table) } else { - offsets, err = modifyEntries4(task, stk, optVal, &replace, &table) + offsets, err = modifyEntries4(mapper, stk, optVal, &replace, &table) } if err != nil { return err @@ -295,7 +302,7 @@ func SetEntries(task *kernel.Task, stk *stack.Stack, optVal []byte, ipv6 bool) * // parseMatchers parses 0 or more matchers from optVal. optVal should contain // only the matchers. -func parseMatchers(task *kernel.Task, filter stack.IPHeaderFilter, optVal []byte) ([]stack.Matcher, error) { +func parseMatchers(mapper IDMapper, filter stack.IPHeaderFilter, optVal []byte) ([]stack.Matcher, error) { nflog("set entries: parsing matchers of size %d", len(optVal)) var matchers []stack.Matcher for len(optVal) > 0 { @@ -318,7 +325,7 @@ func parseMatchers(task *kernel.Task, filter stack.IPHeaderFilter, optVal []byte } // Parse the specific matcher. - matcher, err := unmarshalMatcher(task, match, filter, optVal[linux.SizeOfXTEntryMatch:match.MatchSize]) + matcher, err := unmarshalMatcher(mapper, match, filter, optVal[linux.SizeOfXTEntryMatch:match.MatchSize]) if err != nil { return nil, fmt.Errorf("failed to create matcher: %v", err) } diff --git a/pkg/sentry/socket/netfilter/netfilter_test.go b/pkg/sentry/socket/netfilter/netfilter_test.go new file mode 100644 index 000000000..bd36d5f52 --- /dev/null +++ b/pkg/sentry/socket/netfilter/netfilter_test.go @@ -0,0 +1,56 @@ +// Copyright 2023 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package netfilter + +import ( + _ "embed" + "testing" + + "gvisor.dev/gvisor/pkg/sentry/kernel/auth" + "gvisor.dev/gvisor/pkg/tcpip/stack" +) + +// istioBlob is a golden iptables ruleset generated by Istio. It is already in +// the format ready to by passed to IPT_SO_SET_REPLACE. +// +// Updating this requires running Istio, calling IPT_SO_GET_INFO and +// IPT_SO_GET_ENTRIES, then stitching the structs up. So be careful when +// updating! +// +//go:embed istio_blob +var istioBlob []byte + +// FakeIDMapper implements IDMapper. +type FakeIDMapper struct{} + +// MapToKUID implements IDMapper. +func (*FakeIDMapper) MapToKUID(auth.UID) auth.KUID { + return 0 +} + +// MapToKGID implements IDMapper. +func (*FakeIDMapper) MapToKGID(auth.GID) auth.KGID { + return 0 +} + +// TestIstioBlob tests that we support the iptables ruleset generated by Istio, +// i.e. that we can parse the rules and set them in netstack. +func TestIstioBlob(t *testing.T) { + mapper := FakeIDMapper{} + stack := stack.New(stack.Options{}) + if err := SetEntries(&mapper, stack, istioBlob, false); err != nil { + t.Fatalf("failed to set Istio rules, try setting enableLogging and running again: %v", err) + } +} diff --git a/pkg/sentry/socket/netfilter/owner_matcher.go b/pkg/sentry/socket/netfilter/owner_matcher.go index 3a3df0838..92bc4811b 100644 --- a/pkg/sentry/socket/netfilter/owner_matcher.go +++ b/pkg/sentry/socket/netfilter/owner_matcher.go @@ -19,7 +19,6 @@ import ( "gvisor.dev/gvisor/pkg/abi/linux" "gvisor.dev/gvisor/pkg/marshal" - "gvisor.dev/gvisor/pkg/sentry/kernel" "gvisor.dev/gvisor/pkg/sentry/kernel/auth" "gvisor.dev/gvisor/pkg/tcpip/stack" ) @@ -38,6 +37,10 @@ func (ownerMarshaler) name() string { return matcherNameOwner } +func (ownerMarshaler) revision() uint8 { + return 0 +} + // marshal implements matchMaker.marshal. func (ownerMarshaler) marshal(mr matcher) []byte { matcher := mr.(*OwnerMatcher) @@ -65,7 +68,7 @@ func (ownerMarshaler) marshal(mr matcher) []byte { } // unmarshal implements matchMaker.unmarshal. -func (ownerMarshaler) unmarshal(task *kernel.Task, buf []byte, filter stack.IPHeaderFilter) (stack.Matcher, error) { +func (ownerMarshaler) unmarshal(mapper IDMapper, buf []byte, filter stack.IPHeaderFilter) (stack.Matcher, error) { if len(buf) < linux.SizeOfIPTOwnerInfo { return nil, fmt.Errorf("buf has insufficient size for owner match: %d", len(buf)) } @@ -77,9 +80,8 @@ func (ownerMarshaler) unmarshal(task *kernel.Task, buf []byte, filter stack.IPHe nflog("parsed IPTOwnerInfo: %+v", matchData) var owner OwnerMatcher - creds := task.Credentials() - owner.uid = creds.UserNamespace.MapToKUID(auth.UID(matchData.UID)) - owner.gid = creds.UserNamespace.MapToKGID(auth.GID(matchData.GID)) + owner.uid = mapper.MapToKUID(auth.UID(matchData.UID)) + owner.gid = mapper.MapToKGID(auth.GID(matchData.GID)) // Check flags. if matchData.Match&linux.XT_OWNER_UID != 0 { @@ -113,6 +115,10 @@ func (*OwnerMatcher) name() string { return matcherNameOwner } +func (*OwnerMatcher) revision() uint8 { + return 0 +} + // Match implements Matcher.Match. func (om *OwnerMatcher) Match(hook stack.Hook, pkt stack.PacketBufferPtr, _, _ string) (bool, bool) { // Support only for OUTPUT chain. diff --git a/pkg/sentry/socket/netfilter/owner_matcher_v1.go b/pkg/sentry/socket/netfilter/owner_matcher_v1.go new file mode 100644 index 000000000..8c740745e --- /dev/null +++ b/pkg/sentry/socket/netfilter/owner_matcher_v1.go @@ -0,0 +1,152 @@ +// Copyright 2023 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package netfilter + +import ( + "fmt" + + "gvisor.dev/gvisor/pkg/abi/linux" + "gvisor.dev/gvisor/pkg/marshal" + "gvisor.dev/gvisor/pkg/sentry/kernel/auth" + "gvisor.dev/gvisor/pkg/tcpip/stack" +) + +func init() { + registerMatchMaker(ownerMarshalerV1{}) +} + +// ownerMarshalerV1 implements matchMaker for owner matching. +type ownerMarshalerV1 struct{} + +// name implements matchMaker.name. +func (ownerMarshalerV1) name() string { + return matcherNameOwner +} + +func (ownerMarshalerV1) revision() uint8 { + return 1 +} + +// marshal implements matchMaker.marshal. +func (ownerMarshalerV1) marshal(mr matcher) []byte { + matcher := mr.(*OwnerMatcherV1) + ownerInfo := linux.XTOwnerMatchInfo{ + UIDMin: uint32(matcher.uid), + UIDMax: uint32(matcher.uid), + GIDMin: uint32(matcher.gid), + GIDMax: uint32(matcher.gid), + } + + // Support for UID and GID match. + if matcher.matchUID { + ownerInfo.Match |= linux.XT_OWNER_UID + } + if matcher.matchGID { + ownerInfo.Match |= linux.XT_OWNER_GID + } + if matcher.invertUID { + ownerInfo.Invert |= linux.XT_OWNER_UID + } + if matcher.invertGID { + ownerInfo.Invert |= linux.XT_OWNER_GID + } + buf := marshal.Marshal(&ownerInfo) + return marshalEntryMatch(matcherNameOwner, buf) +} + +// unmarshal implements matchMaker.unmarshal. +func (ownerMarshalerV1) unmarshal(mapper IDMapper, buf []byte, filter stack.IPHeaderFilter) (stack.Matcher, error) { + if len(buf) < linux.SizeOfXTOwnerMatchInfo { + return nil, fmt.Errorf("buf has insufficient size for owner match: %d", len(buf)) + } + + // For alignment reasons, the match's total size may + // exceed what's strictly necessary to hold matchData. + var matchData linux.XTOwnerMatchInfo + matchData.UnmarshalUnsafe(buf) + nflog("parsed XTOwnerMatchInfo: %+v", matchData) + + if matchData.UIDMin != matchData.UIDMax { + nflog("owner v1 doesn't support differing UID min/max") + } + if matchData.GIDMin != matchData.GIDMax { + nflog("owner v1 doesn't support differing GID min/max") + } + owner := OwnerMatcherV1{ + uid: mapper.MapToKUID(auth.UID(matchData.UIDMin)), + gid: mapper.MapToKGID(auth.GID(matchData.GIDMin)), + matchUID: matchData.Match&linux.XT_OWNER_UID != 0, + matchGID: matchData.Match&linux.XT_OWNER_GID != 0, + invertUID: matchData.Invert&linux.XT_OWNER_UID != 0, + invertGID: matchData.Invert&linux.XT_OWNER_GID != 0, + } + return &owner, nil +} + +// OwnerMatcherV1 matches against a UID and/or GID. +type OwnerMatcherV1 struct { + uid auth.KUID + gid auth.KGID + matchUID bool + matchGID bool + invertUID bool + invertGID bool +} + +// name implements matcher.name. +func (*OwnerMatcherV1) name() string { + return matcherNameOwner +} + +func (*OwnerMatcherV1) revision() uint8 { + return 1 +} + +// Match implements Matcher.Match. +func (om *OwnerMatcherV1) Match(hook stack.Hook, pkt stack.PacketBufferPtr, _, _ string) (bool, bool) { + // Support only for OUTPUT chain. + if hook != stack.Output { + return false, true + } + + // If the packet owner is not set, drop the packet. + if pkt.Owner == nil { + return false, true + } + + var matches bool + // Check for UID match. + if om.matchUID { + if auth.KUID(pkt.Owner.KUID()) == om.uid { + matches = true + } + if matches == om.invertUID { + return false, false + } + } + + // Check for GID match. + if om.matchGID { + matches = false + if auth.KGID(pkt.Owner.KGID()) == om.gid { + matches = true + } + if matches == om.invertGID { + return false, false + } + } + + return true, false +} diff --git a/pkg/sentry/socket/netfilter/tcp_matcher.go b/pkg/sentry/socket/netfilter/tcp_matcher.go index 7d5b95cc7..264a5b0ae 100644 --- a/pkg/sentry/socket/netfilter/tcp_matcher.go +++ b/pkg/sentry/socket/netfilter/tcp_matcher.go @@ -19,7 +19,6 @@ import ( "gvisor.dev/gvisor/pkg/abi/linux" "gvisor.dev/gvisor/pkg/marshal" - "gvisor.dev/gvisor/pkg/sentry/kernel" "gvisor.dev/gvisor/pkg/tcpip/header" "gvisor.dev/gvisor/pkg/tcpip/stack" ) @@ -38,6 +37,10 @@ func (tcpMarshaler) name() string { return matcherNameTCP } +func (tcpMarshaler) revision() uint8 { + return 0 +} + // marshal implements matchMaker.marshal. func (tcpMarshaler) marshal(mr matcher) []byte { matcher := mr.(*TCPMatcher) @@ -51,7 +54,7 @@ func (tcpMarshaler) marshal(mr matcher) []byte { } // unmarshal implements matchMaker.unmarshal. -func (tcpMarshaler) unmarshal(_ *kernel.Task, buf []byte, filter stack.IPHeaderFilter) (stack.Matcher, error) { +func (tcpMarshaler) unmarshal(_ IDMapper, buf []byte, filter stack.IPHeaderFilter) (stack.Matcher, error) { if len(buf) < linux.SizeOfXTTCP { return nil, fmt.Errorf("buf has insufficient size for TCP match: %d", len(buf)) } @@ -94,6 +97,10 @@ func (*TCPMatcher) name() string { return matcherNameTCP } +func (*TCPMatcher) revision() uint8 { + return 0 +} + // Match implements Matcher.Match. func (tm *TCPMatcher) Match(hook stack.Hook, pkt stack.PacketBufferPtr, _, _ string) (bool, bool) { switch pkt.NetworkProtocolNumber { diff --git a/pkg/sentry/socket/netfilter/udp_matcher.go b/pkg/sentry/socket/netfilter/udp_matcher.go index 7d7bc3c95..73aa15eef 100644 --- a/pkg/sentry/socket/netfilter/udp_matcher.go +++ b/pkg/sentry/socket/netfilter/udp_matcher.go @@ -19,7 +19,6 @@ import ( "gvisor.dev/gvisor/pkg/abi/linux" "gvisor.dev/gvisor/pkg/marshal" - "gvisor.dev/gvisor/pkg/sentry/kernel" "gvisor.dev/gvisor/pkg/tcpip/header" "gvisor.dev/gvisor/pkg/tcpip/stack" ) @@ -38,6 +37,10 @@ func (udpMarshaler) name() string { return matcherNameUDP } +func (udpMarshaler) revision() uint8 { + return 0 +} + // marshal implements matchMaker.marshal. func (udpMarshaler) marshal(mr matcher) []byte { matcher := mr.(*UDPMatcher) @@ -51,7 +54,7 @@ func (udpMarshaler) marshal(mr matcher) []byte { } // unmarshal implements matchMaker.unmarshal. -func (udpMarshaler) unmarshal(_ *kernel.Task, buf []byte, filter stack.IPHeaderFilter) (stack.Matcher, error) { +func (udpMarshaler) unmarshal(_ IDMapper, buf []byte, filter stack.IPHeaderFilter) (stack.Matcher, error) { if len(buf) < linux.SizeOfXTUDP { return nil, fmt.Errorf("buf has insufficient size for UDP match: %d", len(buf)) } @@ -91,6 +94,10 @@ func (*UDPMatcher) name() string { return matcherNameUDP } +func (*UDPMatcher) revision() uint8 { + return 0 +} + // Match implements Matcher.Match. func (um *UDPMatcher) Match(hook stack.Hook, pkt stack.PacketBufferPtr, _, _ string) (bool, bool) { switch pkt.NetworkProtocolNumber { diff --git a/pkg/sentry/socket/netstack/netstack.go b/pkg/sentry/socket/netstack/netstack.go index e177f8487..e1cfbf61c 100644 --- a/pkg/sentry/socket/netstack/netstack.go +++ b/pkg/sentry/socket/netstack/netstack.go @@ -2311,7 +2311,7 @@ func setSockOptIPv6(t *kernel.Task, s socket.Socket, ep commonEndpoint, name int return syserr.ErrNoDevice } // Stack must be a netstack stack. - return netfilter.SetEntries(t, stk.(*Stack).Stack, optVal, true) + return netfilter.SetEntries(t.Credentials().UserNamespace, stk.(*Stack).Stack, optVal, true) case linux.IP6T_SO_SET_ADD_COUNTERS: log.Infof("IP6T_SO_SET_ADD_COUNTERS is not supported") @@ -2558,7 +2558,7 @@ func setSockOptIP(t *kernel.Task, s socket.Socket, ep commonEndpoint, name int, return syserr.ErrNoDevice } // Stack must be a netstack stack. - return netfilter.SetEntries(t, stk.(*Stack).Stack, optVal, false) + return netfilter.SetEntries(t.Credentials().UserNamespace, stk.(*Stack).Stack, optVal, false) case linux.IPT_SO_SET_ADD_COUNTERS: log.Infof("IPT_SO_SET_ADD_COUNTERS is not supported")