Skip to content

Commit 792bb9e

Browse files
kerumetogvisor-bot
authored andcommitted
Fix: Ensure that unbound Netlink sockets are bound when sending a message.
If a Netlink socket is not bound when a message is sent through it, it should automatically be bound to assign it a unique portID. This becomes relevant in cases where the portId is used for access verification, such as for table owners in NETLINK_NETFILTER sockets. PiperOrigin-RevId: 786348487
1 parent d335069 commit 792bb9e

File tree

2 files changed

+90
-0
lines changed

2 files changed

+90
-0
lines changed

pkg/sentry/socket/netlink/socket.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -796,6 +796,13 @@ func (s *Socket) sendMsg(ctx context.Context, src usermem.IOSequence, to []byte,
796796
s.mu.Lock()
797797
defer s.mu.Unlock()
798798

799+
// An unbound socket has a port ID defaulted to 0. If it has not yet been bound,
800+
// bind it to assign it a unique port ID.
801+
// From net/netlink/af_netlink.c:netlink_sendmsg
802+
if !s.bound {
803+
s.bindPort(kernel.TaskFromContext(ctx), 0)
804+
}
805+
799806
// For simplicity, and consistency with Linux, we copy in the entire
800807
// message up front.
801808
if src.NumBytes() > int64(s.sendBufferSize) {

test/syscalls/linux/socket_netlink_netfilter.cc

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1966,6 +1966,89 @@ TEST(NetlinkNetfilterTest, DeleteBaseChainByHandle) {
19661966
delete_chain_request_buffer.size()));
19671967
}
19681968

1969+
TEST(NetlinkNetfilterTest, ErrRetrieveTableWithOwnerMismatchUnboundSocket) {
1970+
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
1971+
const char test_table_name[] = "test_table";
1972+
uint32_t table_flags = NFT_TABLE_F_DORMANT | NFT_TABLE_F_OWNER;
1973+
uint8_t expected_udata[3] = {0x01, 0x02, 0x03};
1974+
FileDescriptor fd =
1975+
ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_NETFILTER));
1976+
FileDescriptor fd_2 = ASSERT_NO_ERRNO_AND_VALUE(
1977+
Socket(AF_NETLINK, SOCK_RAW, NETLINK_NETFILTER));
1978+
1979+
std::vector<char> add_request_buffer =
1980+
NlReq("newtable req ack inet")
1981+
.Seq(kSeq)
1982+
.StrAttr(NFTA_TABLE_NAME, test_table_name)
1983+
.U32Attr(NFTA_TABLE_FLAGS, &table_flags)
1984+
.RawAttr(NFTA_TABLE_USERDATA, expected_udata, sizeof(expected_udata))
1985+
.Build();
1986+
1987+
std::vector<char> get_request_buffer =
1988+
NlReq("gettable req ack inet")
1989+
.Seq(kSeq + 1)
1990+
.StrAttr(NFTA_TABLE_NAME, test_table_name)
1991+
.Build();
1992+
1993+
ASSERT_NO_ERRNO(NetlinkRequestAckOrError(fd, kSeq, add_request_buffer.data(),
1994+
add_request_buffer.size()));
1995+
1996+
ASSERT_THAT(
1997+
NetlinkRequestAckOrError(fd_2, kSeq + 1, get_request_buffer.data(),
1998+
get_request_buffer.size()),
1999+
PosixErrorIs(EPERM, _));
2000+
}
2001+
2002+
TEST(NetlinkNetfilterTest, AddTableWithUnboundSocket) {
2003+
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
2004+
const char test_table_name[] = "test_table";
2005+
uint32_t table_flags = NFT_TABLE_F_DORMANT | NFT_TABLE_F_OWNER;
2006+
uint32_t expected_port_id = 0;
2007+
uint8_t expected_udata[3] = {0x01, 0x02, 0x03};
2008+
FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(
2009+
Socket(AF_NETLINK, SOCK_RAW, NETLINK_NETFILTER));
2010+
bool correct_response = false;
2011+
2012+
std::vector<char> add_request_buffer =
2013+
NlReq("newtable req ack inet")
2014+
.Seq(kSeq)
2015+
.StrAttr(NFTA_TABLE_NAME, test_table_name)
2016+
.U32Attr(NFTA_TABLE_FLAGS, &table_flags)
2017+
.RawAttr(NFTA_TABLE_USERDATA, expected_udata, sizeof(expected_udata))
2018+
.Build();
2019+
2020+
std::vector<char> get_request_buffer =
2021+
NlReq("gettable req inet")
2022+
.Seq(kSeq + 1)
2023+
.StrAttr(NFTA_TABLE_NAME, test_table_name)
2024+
.Build();
2025+
2026+
ASSERT_NO_ERRNO(NetlinkRequestAckOrError(fd, kSeq, add_request_buffer.data(),
2027+
add_request_buffer.size()));
2028+
2029+
ASSERT_NO_ERRNO(NetlinkRequestResponse(
2030+
fd, get_request_buffer.data(), get_request_buffer.size(),
2031+
[&](const struct nlmsghdr* hdr) {
2032+
const struct nfattr* owner_attr =
2033+
FindNfAttr(hdr, nullptr, NFTA_TABLE_OWNER);
2034+
EXPECT_NE(owner_attr, nullptr);
2035+
uint32_t owner = *(reinterpret_cast<uint32_t*>(NFA_DATA(owner_attr)));
2036+
EXPECT_NE(owner, 0);
2037+
expected_port_id = owner;
2038+
correct_response = true;
2039+
},
2040+
false));
2041+
ASSERT_TRUE(correct_response);
2042+
2043+
// Ensure that the port ID assigned to the table is not 0 and matches the
2044+
// port id retrieved from getsockname() syscall.
2045+
uint32_t assigned_port_id =
2046+
ASSERT_NO_ERRNO_AND_VALUE(NetlinkPortID(fd.get()));
2047+
ASSERT_NE(expected_port_id, 0);
2048+
ASSERT_NE(assigned_port_id, 0);
2049+
ASSERT_EQ(expected_port_id, assigned_port_id);
2050+
}
2051+
19692052
} // namespace
19702053

19712054
} // namespace testing

0 commit comments

Comments
 (0)