mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-02-07 06:53:33 +00:00
Address weakness with new pledge("anet") promise
The intent with pledge("anet") has been to prevent outbound connections. However we were only doing that for TCP sockets, and outbound UDP could still get through, by using socket() plus sendto(). This change fixed that by preventing UDP sockets from being created. Credit goes to chc4 on Hacker News for finding this.
This commit is contained in:
parent
fb2bd313ae
commit
48b2afb192
3 changed files with 74 additions and 6 deletions
|
@ -60,6 +60,7 @@
|
|||
#define SELF 0x8000
|
||||
#define ADDRLESS 0x2000
|
||||
#define INET 0x2000
|
||||
#define ANET 0x8000
|
||||
#define LOCK 0x4000
|
||||
#define NOEXEC 0x8000
|
||||
#define EXEC 0x4000
|
||||
|
@ -815,7 +816,7 @@ static const uint16_t kPledgeInet[] = {
|
|||
// anet is similar to init, but without connect;
|
||||
// this allows to accept, but not initiate socket connections
|
||||
static const uint16_t kPledgeAnet[] = {
|
||||
__NR_linux_socket | INET, //
|
||||
__NR_linux_socket | ANET, //
|
||||
__NR_linux_listen, //
|
||||
__NR_linux_bind, //
|
||||
__NR_linux_sendto, //
|
||||
|
@ -1953,6 +1954,45 @@ static privileged void AllowSocketInet(struct Filter *f) {
|
|||
AppendFilter(f, PLEDGE(fragment));
|
||||
}
|
||||
|
||||
// The family parameter of socket() must be one of:
|
||||
//
|
||||
// - AF_INET (0x02)
|
||||
// - AF_INET6 (0x0a)
|
||||
//
|
||||
// The type parameter of socket() will ignore:
|
||||
//
|
||||
// - SOCK_CLOEXEC (0x80000)
|
||||
// - SOCK_NONBLOCK (0x00800)
|
||||
//
|
||||
// The type parameter of socket() must be one of:
|
||||
//
|
||||
// - SOCK_STREAM (0x01)
|
||||
//
|
||||
// The protocol parameter of socket() must be one of:
|
||||
//
|
||||
// - 0
|
||||
// - IPPROTO_ICMP (0x01)
|
||||
// - IPPROTO_TCP (0x06)
|
||||
//
|
||||
static privileged void AllowSocketAnet(struct Filter *f) {
|
||||
static const struct sock_filter fragment[] = {
|
||||
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_linux_socket, 0, 12),
|
||||
BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(args[0])),
|
||||
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x02, 1, 0),
|
||||
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x0a, 0, 8),
|
||||
BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(args[1])),
|
||||
BPF_STMT(BPF_ALU | BPF_AND | BPF_K, ~0x80800),
|
||||
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x01, 0, 5),
|
||||
BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(args[2])),
|
||||
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x00, 2, 0),
|
||||
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x01, 1, 0),
|
||||
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x06, 0, 1),
|
||||
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
|
||||
BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(nr)),
|
||||
};
|
||||
AppendFilter(f, PLEDGE(fragment));
|
||||
}
|
||||
|
||||
// The family parameter of socket() must be one of:
|
||||
//
|
||||
// - AF_UNIX (1)
|
||||
|
@ -2209,6 +2249,9 @@ static privileged void AppendPledge(struct Filter *f, //
|
|||
case __NR_linux_socket | INET:
|
||||
AllowSocketInet(f);
|
||||
break;
|
||||
case __NR_linux_socket | ANET:
|
||||
AllowSocketAnet(f);
|
||||
break;
|
||||
case __NR_linux_socket | UNIX:
|
||||
AllowSocketUnix(f);
|
||||
break;
|
||||
|
|
|
@ -365,6 +365,23 @@ TEST(pledge, inet_forbidsOtherSockets) {
|
|||
EXPECT_TRUE(WIFEXITED(ws) && !WEXITSTATUS(ws));
|
||||
}
|
||||
|
||||
TEST(pledge, anet_forbidsUdpSocketsAndConnect) {
|
||||
if (IsOpenbsd()) return; // b/c testing linux bpf
|
||||
int ws, pid, yes = 1;
|
||||
ASSERT_NE(-1, (pid = fork()));
|
||||
if (!pid) {
|
||||
ASSERT_SYS(0, 0, pledge("stdio anet", 0));
|
||||
ASSERT_SYS(0, 3, socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
|
||||
ASSERT_SYS(EPERM, -1, socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP));
|
||||
ASSERT_SYS(EPERM, -1, setsockopt(3, SOL_SOCKET, SO_TIMESTAMP, &yes, 4));
|
||||
struct sockaddr_in sin = {AF_INET, 0, {htonl(0x7f000001)}};
|
||||
ASSERT_SYS(EPERM, -1, connect(4, (struct sockaddr *)&sin, sizeof(sin)));
|
||||
_Exit(0);
|
||||
}
|
||||
EXPECT_NE(-1, wait(&ws));
|
||||
EXPECT_EQ(0, ws);
|
||||
}
|
||||
|
||||
TEST(pledge, mmap) {
|
||||
if (IsOpenbsd()) return; // b/c testing linux bpf
|
||||
char *p;
|
||||
|
|
18
third_party/python/Modules/tokenbucket.c
vendored
18
third_party/python/Modules/tokenbucket.c
vendored
|
@ -32,6 +32,7 @@
|
|||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/af.h"
|
||||
#include "libc/sysv/consts/clock.h"
|
||||
#include "libc/sysv/consts/f.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/consts/sock.h"
|
||||
#include "libc/sysv/consts/timer.h"
|
||||
|
@ -55,6 +56,7 @@ PYTHON_PROVIDE("tokenbucket.count");
|
|||
PYTHON_PROVIDE("tokenbucket.program");
|
||||
|
||||
struct TokenBucket {
|
||||
int fd;
|
||||
int pid;
|
||||
signed char cidr;
|
||||
struct timespec replenish;
|
||||
|
@ -229,7 +231,7 @@ or errno on error. To test if blackholed is running, ban 0.0.0.0.");
|
|||
static PyObject *
|
||||
tokenbucket_blackhole(PyObject *self, PyObject *args)
|
||||
{
|
||||
int fd;
|
||||
int fd, fd2;
|
||||
char buf[4];
|
||||
uint32_t ip;
|
||||
const char *ipstr;
|
||||
|
@ -241,15 +243,21 @@ tokenbucket_blackhole(PyObject *self, PyObject *args)
|
|||
PyErr_SetString(PyExc_ValueError, "bad ipv4 address");
|
||||
return 0;
|
||||
}
|
||||
if ((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1) {
|
||||
return PyLong_FromLong(errno);
|
||||
if (!(fd = g_tokenbucket.fd)) {
|
||||
if ((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1) {
|
||||
return PyLong_FromLong(errno);
|
||||
}
|
||||
fd2 = fcntl(fd, F_DUPFD_CLOEXEC, 10);
|
||||
close(fd);
|
||||
if (fd2 == -1) {
|
||||
return PyLong_FromLong(errno);
|
||||
}
|
||||
g_tokenbucket.fd = fd = fd2;
|
||||
}
|
||||
WRITE32BE(buf, ip);
|
||||
if (sendto(fd, buf, 4, 0, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
|
||||
close(fd);
|
||||
return PyLong_FromLong(errno);
|
||||
}
|
||||
close(fd);
|
||||
return PyLong_FromLong(0);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue