Skip to content

Commit 51ea723

Browse files
committed
seccomp: add support for seccomp notify
add support for seccomp notify and add a basic support for emulating mknod and mknodat. The handler implementation is likely going to change, for now it is just a PoC to show how it would work. Requires: containers/crun#438 Requires: libseccomp-2.5 Signed-off-by: Giuseppe Scrivano <[email protected]>
1 parent 3c396d4 commit 51ea723

File tree

7 files changed

+270
-9
lines changed

7 files changed

+270
-9
lines changed

src/conmon.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ int main(int argc, char *argv[])
124124
}
125125

126126
_cleanup_free_ char *csname = NULL;
127+
_cleanup_free_ char *seccomp_listener = NULL;
127128
int workerfd_stdin = -1;
128129
int workerfd_stdout = -1;
129130
int workerfd_stderr = -1;
@@ -165,6 +166,8 @@ int main(int argc, char *argv[])
165166
g_unix_fd_add(winsz_fd_r, G_IO_IN, ctrl_winsz_cb, NULL);
166167
}
167168

169+
seccomp_listener = setup_seccomp_socket();
170+
168171
/* We always create a stderr pipe, because that way we can capture
169172
runc stderr messages before the tty is created */
170173
if (pipe2(fds, O_CLOEXEC) < 0)
@@ -173,6 +176,8 @@ int main(int argc, char *argv[])
173176
mainfd_stderr = fds[0];
174177
workerfd_stderr = fds[1];
175178

179+
setenv("RUN_OCI_SECCOMP_RECEIVER", seccomp_listener, 1);
180+
176181
GPtrArray *runtime_argv = configure_runtime_args(csname);
177182

178183
/* Setup endpoint for attach */
@@ -307,6 +312,9 @@ int main(int argc, char *argv[])
307312
if (workerfd_stderr > -1)
308313
close(workerfd_stderr);
309314

315+
if (seccomp_listener != NULL)
316+
g_unix_fd_add(seccomp_socket_fd, G_IO_IN, seccomp_accept_cb, csname);
317+
310318
if (csname != NULL) {
311319
g_unix_fd_add(console_socket_fd, G_IO_IN, terminal_accept_cb, csname);
312320
/* Process any SIGCHLD we may have missed before the signal handler was in place. */

src/conn_sock.c

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,14 @@ void conn_sock_shutdown(struct conn_sock_s *sock, int how);
2121
static void sock_try_write_to_mainfd_stdin(struct conn_sock_s *sock);
2222
static gboolean mainfd_write_cb(G_GNUC_UNUSED int fd, G_GNUC_UNUSED GIOCondition condition, G_GNUC_UNUSED gpointer user_data);
2323

24-
char *setup_console_socket(void)
24+
static char *setup_socket(int *fd)
2525
{
2626
struct sockaddr_un addr = {0};
27-
_cleanup_free_ const char *tmpdir = g_get_tmp_dir();
28-
_cleanup_free_ char *csname = g_build_filename(tmpdir, "conmon-term.XXXXXX", NULL);
27+
const char *tmpdir = NULL;
28+
_cleanup_free_ char *csname = NULL;
29+
30+
tmpdir = g_get_tmp_dir();
31+
csname = g_build_filename(tmpdir, "conmon.XXXXXX", NULL);
2932
/*
3033
* Generate a temporary name. Is this unsafe? Probably, but we can
3134
* replace it with a rename(2) setup if necessary.
@@ -42,22 +45,32 @@ char *setup_console_socket(void)
4245
ninfof("addr{sun_family=AF_UNIX, sun_path=%s}", addr.sun_path);
4346

4447
/* Bind to the console socket path. */
45-
console_socket_fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
46-
if (console_socket_fd < 0)
47-
pexit("Failed to create console-socket");
48-
if (fchmod(console_socket_fd, 0700))
48+
*fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
49+
if (*fd < 0)
50+
pexit("Failed to create socket");
51+
if (fchmod(*fd, 0700))
4952
pexit("Failed to change console-socket permissions");
5053
/* XXX: This should be handled with a rename(2). */
5154
if (unlink(csname) < 0)
5255
pexit("Failed to unlink temporary random path");
53-
if (bind(console_socket_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
56+
if (bind(*fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
5457
pexit("Failed to bind to console-socket");
55-
if (listen(console_socket_fd, 128) < 0)
58+
if (listen(*fd, 128) < 0)
5659
pexit("Failed to listen on console-socket");
5760

5861
return g_strdup(csname);
5962
}
6063

64+
char *setup_console_socket(void)
65+
{
66+
return setup_socket(&console_socket_fd);
67+
}
68+
69+
char *setup_seccomp_socket(void)
70+
{
71+
return setup_socket(&seccomp_socket_fd);
72+
}
73+
6174
char *setup_attach_socket(void)
6275
{
6376
struct sockaddr_un attach_addr = {0};

src/conn_sock.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ struct conn_sock_s {
1616
};
1717

1818
char *setup_console_socket(void);
19+
char *setup_seccomp_socket(void);
1920
char *setup_attach_socket(void);
2021
void conn_sock_shutdown(struct conn_sock_s *sock, int how);
2122
void schedule_main_stdin_write();

src/ctrl.c

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,247 @@
1414
#include <sys/stat.h>
1515
#include <termios.h>
1616

17+
#include <seccomp.h>
18+
#include <linux/seccomp.h>
19+
#include <sys/sysmacros.h>
20+
#include <sys/wait.h>
21+
#include <sys/mount.h>
22+
#include <signal.h>
23+
24+
static struct seccomp_notif_sizes sizes;
25+
static struct seccomp_notif *req;
26+
static struct seccomp_notif_resp *resp;
27+
1728
static void resize_winsz(int height, int width);
1829
static gboolean read_from_ctrl_buffer(int fd, gboolean (*line_process_func)(char *));
1930
static gboolean process_terminal_ctrl_line(char *line);
2031
static gboolean process_winsz_ctrl_line(char *line);
2132
static void setup_fifo(int *fifo_r, int *fifo_w, char *filename, char *error_var_name);
2233

34+
static int seccomp(unsigned int op, unsigned int flags, void *args)
35+
{
36+
errno = 0;
37+
return syscall(__NR_seccomp, op, flags, args);
38+
}
39+
40+
static ssize_t read_pid_memory(pid_t pid, char *to, off_t from, size_t len)
41+
{
42+
int fd;
43+
char p[32];
44+
ssize_t ret;
45+
46+
sprintf(p, "/proc/%d/mem", pid);
47+
fd = open(p, O_RDONLY|O_CLOEXEC);
48+
if (fd < 0)
49+
return -1;
50+
51+
ret = pread(fd, to, len, from);
52+
53+
close(fd);
54+
return ret;
55+
}
56+
57+
struct device_s
58+
{
59+
const char *path;
60+
bool block;
61+
int major;
62+
int minor;
63+
};
64+
65+
struct device_s allowed_devs[] =
66+
{
67+
{"/dev/null", false, 1, 3},
68+
{"/dev/zero", false, 1, 5},
69+
{"/dev/full", false, 1, 7},
70+
{"/dev/random", false, 1, 8},
71+
{"/dev/urandom", false, 1, 9},
72+
{NULL}
73+
};
74+
75+
static struct device_s *get_dev(bool block, uint64_t dev)
76+
{
77+
size_t i;
78+
int min = minor(dev);
79+
int maj = major(dev);
80+
for (i = 0; allowed_devs[i].path; i++) {
81+
if (min == allowed_devs[i].minor
82+
&& maj == allowed_devs[i].major
83+
&& block == allowed_devs[i].block)
84+
return &allowed_devs[i];
85+
}
86+
return NULL;
87+
}
88+
89+
/* This is just a example handler for mknod and mknodat using the device
90+
allow list above. This is likely going to change in future. */
91+
static int handle_req(struct seccomp_notif *sreq,
92+
struct seccomp_notif_resp *sresp, int listener)
93+
{
94+
(void) listener;
95+
96+
sresp->id = sreq->id;
97+
sresp->error = 0;
98+
sresp->val = 0;
99+
100+
if (sreq->data.nr == __NR_mknod || sreq->data.nr == __NR_mknodat) {
101+
char path[PATH_MAX+1];
102+
int dirfd = AT_FDCWD;
103+
struct device_s *d;
104+
mode_t mode_arg;
105+
int path_arg;
106+
dev_t dev_arg;
107+
pid_t p, pid;
108+
ssize_t len;
109+
int r;
110+
111+
sresp->flags = 0;
112+
113+
if (sreq->data.nr == __NR_mknod) {
114+
path_arg = 0;
115+
mode_arg = 1;
116+
dev_arg = 2;
117+
} else {
118+
dirfd = sreq->data.args[0];
119+
path_arg = 1;
120+
mode_arg = 2;
121+
dev_arg = 3;
122+
}
123+
124+
len = read_pid_memory(sreq->pid, path, sreq->data.args[path_arg], sizeof(path));
125+
if (len < 0) {
126+
sresp->error = -errno;
127+
return len;
128+
}
129+
path[len] = '\0';
130+
131+
d = get_dev((sreq->data.args[mode_arg] & S_IFMT) == S_IFBLK, sreq->data.args[dev_arg]);
132+
if (d == NULL) {
133+
sresp->error = -EPERM;
134+
sresp->flags = 0;
135+
return 0;
136+
}
137+
138+
pid = fork();
139+
if (pid < 0) {
140+
sresp->error = -errno;
141+
sresp->flags = 0;
142+
return 0;
143+
}
144+
if (pid == 0) {
145+
char proc_path[32];
146+
sigset_t sigset;
147+
int fd;
148+
149+
if (sigfillset(&sigset) < 0)
150+
_exit(errno);
151+
if (sigprocmask(SIG_BLOCK, &sigset, NULL) < 0)
152+
_exit(errno);
153+
154+
if (dirfd == AT_FDCWD)
155+
sprintf (proc_path, "/proc/%d/cwd", sreq->pid);
156+
else
157+
sprintf (proc_path, "/proc/%d/fd/%d", sreq->pid, dirfd);
158+
159+
dirfd = open(proc_path, O_PATH|O_CLOEXEC);
160+
if (dirfd < 0)
161+
_exit(errno);
162+
163+
sprintf (proc_path, "/proc/%d/ns/mnt", sreq->pid);
164+
fd = open(proc_path, O_RDONLY|O_CLOEXEC);
165+
if (fd < 0)
166+
_exit(errno);
167+
168+
if (setns (fd, 0) < 0)
169+
_exit(errno);
170+
close(fd);
171+
172+
if (fchdir (dirfd) < 0)
173+
_exit(errno);
174+
close(dirfd);
175+
176+
fd = open(path, O_RDWR|O_EXCL|O_CREAT, sreq->data.args[mode_arg] & 01777);
177+
if (fd < 0)
178+
_exit(errno);
179+
180+
if (mount(d->path, path, NULL, MS_BIND|MS_SLAVE, NULL) < 0) {
181+
unlink(path);
182+
_exit(errno);
183+
}
184+
_exit(0);
185+
}
186+
187+
do {
188+
p = waitpid(pid, &r, 0);
189+
}
190+
while (p != pid);
191+
192+
if (WEXITSTATUS(r) != 0) {
193+
sresp->error = -WEXITSTATUS(r);
194+
sresp->flags = 0;
195+
return 0;
196+
}
197+
198+
sresp->error = 0;
199+
sresp->flags = 0;
200+
} else {
201+
sresp->error = -ENOTSUP;
202+
sresp->flags = 0;
203+
}
204+
205+
return 0;
206+
}
207+
208+
gboolean seccomp_accept_cb(int fd, G_GNUC_UNUSED GIOCondition condition, G_GNUC_UNUSED gpointer user_data)
209+
{
210+
ninfof("about to accept from seccomp_socket_fd: %d", fd);
211+
int connfd = accept4(fd, NULL, NULL, SOCK_CLOEXEC);
212+
if (connfd < 0) {
213+
nwarn("Failed to accept console-socket connection");
214+
return G_SOURCE_CONTINUE;
215+
}
216+
217+
struct file_t console = recvfd(connfd);
218+
close(connfd);
219+
if (req == NULL) {
220+
if (seccomp(SECCOMP_GET_NOTIF_SIZES, 0, &sizes) < 0) {
221+
pexitf("seccomp");
222+
}
223+
req = malloc(sizes.seccomp_notif);
224+
if (!req)
225+
pexitf("malloc");
226+
227+
resp = malloc(sizes.seccomp_notif_resp);
228+
if (!resp)
229+
pexitf("malloc");
230+
memset(resp, 0, sizes.seccomp_notif_resp);
231+
}
232+
g_unix_fd_add(console.fd, G_IO_IN|G_IO_HUP, seccomp_cb, NULL);
233+
return G_SOURCE_CONTINUE;
234+
}
235+
236+
gboolean seccomp_cb(int fd, GIOCondition condition, G_GNUC_UNUSED gpointer user_data)
237+
{
238+
(void) fd;
239+
if (condition & G_IO_IN) {
240+
memset(req, 0, sizes.seccomp_notif);
241+
if (ioctl(fd, SECCOMP_IOCTL_NOTIF_RECV, req)) {
242+
return G_SOURCE_CONTINUE;
243+
}
244+
245+
if (handle_req(req, resp, fd) < 0) {
246+
return G_SOURCE_CONTINUE;
247+
}
248+
249+
if (ioctl(fd, SECCOMP_IOCTL_NOTIF_SEND, resp) < 0 &&
250+
errno != ENOENT) {
251+
return G_SOURCE_CONTINUE;
252+
}
253+
254+
}
255+
return G_SOURCE_CONTINUE;
256+
}
257+
23258
gboolean terminal_accept_cb(int fd, G_GNUC_UNUSED GIOCondition condition, G_GNUC_UNUSED gpointer user_data)
24259
{
25260

src/ctrl.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33

44
#include <glib.h> /* gpointer */
55

6+
gboolean seccomp_accept_cb(int fd, G_GNUC_UNUSED GIOCondition condition, G_GNUC_UNUSED gpointer user_data);
67
gboolean terminal_accept_cb(int fd, G_GNUC_UNUSED GIOCondition condition, G_GNUC_UNUSED gpointer user_data);
78
gboolean ctrl_winsz_cb(int fd, G_GNUC_UNUSED GIOCondition condition, G_GNUC_UNUSED gpointer user_data);
9+
gboolean seccomp_cb(int fd, GIOCondition condition, G_GNUC_UNUSED gpointer user_data);
810
gboolean ctrl_cb(int fd, G_GNUC_UNUSED GIOCondition condition, G_GNUC_UNUSED gpointer user_data);
911
void setup_console_fifo();
1012
int setup_terminal_control_fifo();

src/globals.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ GPtrArray *conn_socks = NULL;
1111

1212
int attach_socket_fd = -1;
1313
int console_socket_fd = -1;
14+
int seccomp_socket_fd = -1;
1415
int terminal_ctrl_fd = -1;
1516
int inotify_fd = -1;
1617
int winsz_fd_w = -1;

src/globals.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ extern GPtrArray *conn_socks;
1616

1717
extern int attach_socket_fd;
1818
extern int console_socket_fd;
19+
extern int seccomp_socket_fd;
1920
extern int terminal_ctrl_fd;
2021
extern int inotify_fd;
2122
extern int winsz_fd_w;

0 commit comments

Comments
 (0)