|
14 | 14 | #include <sys/stat.h>
|
15 | 15 | #include <termios.h>
|
16 | 16 |
|
| 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 | + |
17 | 28 | static void resize_winsz(int height, int width);
|
18 | 29 | static gboolean read_from_ctrl_buffer(int fd, gboolean (*line_process_func)(char *));
|
19 | 30 | static gboolean process_terminal_ctrl_line(char *line);
|
20 | 31 | static gboolean process_winsz_ctrl_line(char *line);
|
21 | 32 | static void setup_fifo(int *fifo_r, int *fifo_w, char *filename, char *error_var_name);
|
22 | 33 |
|
| 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 | + |
23 | 258 | gboolean terminal_accept_cb(int fd, G_GNUC_UNUSED GIOCondition condition, G_GNUC_UNUSED gpointer user_data)
|
24 | 259 | {
|
25 | 260 |
|
|
0 commit comments