0001 #include <signal.h>
0002 #include <stdio.h>
0003 #include <stdlib.h>
0004 #include <unistd.h>
0005 #include <errno.h>
0006 #include <fcntl.h>
0007 #include <string.h>
0008 #include <stddef.h>
0009 #include <sys/sysmacros.h>
0010 #include <sys/types.h>
0011 #include <sys/wait.h>
0012 #include <sys/socket.h>
0013 #include <sys/stat.h>
0014 #include <sys/mman.h>
0015 #include <sys/syscall.h>
0016 #include <sys/user.h>
0017 #include <sys/ioctl.h>
0018 #include <sys/ptrace.h>
0019 #include <sys/mount.h>
0020 #include <linux/limits.h>
0021 #include <linux/filter.h>
0022 #include <linux/seccomp.h>
0023
0024 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
0025
0026 static int seccomp(unsigned int op, unsigned int flags, void *args)
0027 {
0028 errno = 0;
0029 return syscall(__NR_seccomp, op, flags, args);
0030 }
0031
0032 static int send_fd(int sock, int fd)
0033 {
0034 struct msghdr msg = {};
0035 struct cmsghdr *cmsg;
0036 char buf[CMSG_SPACE(sizeof(int))] = {0}, c = 'c';
0037 struct iovec io = {
0038 .iov_base = &c,
0039 .iov_len = 1,
0040 };
0041
0042 msg.msg_iov = &io;
0043 msg.msg_iovlen = 1;
0044 msg.msg_control = buf;
0045 msg.msg_controllen = sizeof(buf);
0046 cmsg = CMSG_FIRSTHDR(&msg);
0047 cmsg->cmsg_level = SOL_SOCKET;
0048 cmsg->cmsg_type = SCM_RIGHTS;
0049 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
0050 *((int *)CMSG_DATA(cmsg)) = fd;
0051 msg.msg_controllen = cmsg->cmsg_len;
0052
0053 if (sendmsg(sock, &msg, 0) < 0) {
0054 perror("sendmsg");
0055 return -1;
0056 }
0057
0058 return 0;
0059 }
0060
0061 static int recv_fd(int sock)
0062 {
0063 struct msghdr msg = {};
0064 struct cmsghdr *cmsg;
0065 char buf[CMSG_SPACE(sizeof(int))] = {0}, c = 'c';
0066 struct iovec io = {
0067 .iov_base = &c,
0068 .iov_len = 1,
0069 };
0070
0071 msg.msg_iov = &io;
0072 msg.msg_iovlen = 1;
0073 msg.msg_control = buf;
0074 msg.msg_controllen = sizeof(buf);
0075
0076 if (recvmsg(sock, &msg, 0) < 0) {
0077 perror("recvmsg");
0078 return -1;
0079 }
0080
0081 cmsg = CMSG_FIRSTHDR(&msg);
0082
0083 return *((int *)CMSG_DATA(cmsg));
0084 }
0085
0086 static int user_trap_syscall(int nr, unsigned int flags)
0087 {
0088 struct sock_filter filter[] = {
0089 BPF_STMT(BPF_LD+BPF_W+BPF_ABS,
0090 offsetof(struct seccomp_data, nr)),
0091 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, nr, 0, 1),
0092 BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_USER_NOTIF),
0093 BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW),
0094 };
0095
0096 struct sock_fprog prog = {
0097 .len = (unsigned short)ARRAY_SIZE(filter),
0098 .filter = filter,
0099 };
0100
0101 return seccomp(SECCOMP_SET_MODE_FILTER, flags, &prog);
0102 }
0103
0104 static int handle_req(struct seccomp_notif *req,
0105 struct seccomp_notif_resp *resp, int listener)
0106 {
0107 char path[PATH_MAX], source[PATH_MAX], target[PATH_MAX];
0108 int ret = -1, mem;
0109
0110 resp->id = req->id;
0111 resp->error = -EPERM;
0112 resp->val = 0;
0113
0114 if (req->data.nr != __NR_mount) {
0115 fprintf(stderr, "huh? trapped something besides mount? %d\n", req->data.nr);
0116 return -1;
0117 }
0118
0119
0120 if (!(req->data.args[3] & MS_BIND))
0121 return 0;
0122
0123
0124
0125
0126
0127 snprintf(path, sizeof(path), "/proc/%d/mem", req->pid);
0128 mem = open(path, O_RDONLY);
0129 if (mem < 0) {
0130 perror("open mem");
0131 return -1;
0132 }
0133
0134
0135
0136
0137
0138
0139
0140
0141
0142
0143
0144
0145 if (ioctl(listener, SECCOMP_IOCTL_NOTIF_ID_VALID, &req->id) < 0) {
0146 fprintf(stderr, "task died before we could map its memory\n");
0147 goto out;
0148 }
0149
0150
0151
0152
0153
0154
0155 if (lseek(mem, req->data.args[0], SEEK_SET) < 0) {
0156 perror("seek");
0157 goto out;
0158 }
0159
0160 ret = read(mem, source, sizeof(source));
0161 if (ret < 0) {
0162 perror("read");
0163 goto out;
0164 }
0165
0166 if (lseek(mem, req->data.args[1], SEEK_SET) < 0) {
0167 perror("seek");
0168 goto out;
0169 }
0170
0171 ret = read(mem, target, sizeof(target));
0172 if (ret < 0) {
0173 perror("read");
0174 goto out;
0175 }
0176
0177
0178
0179
0180
0181
0182 if (!strncmp(source, "/tmp/", 5) && !strncmp(target, "/tmp/", 5)) {
0183 if (mount(source, target, NULL, req->data.args[3], NULL) < 0) {
0184 ret = -1;
0185 perror("actual mount");
0186 goto out;
0187 }
0188 resp->error = 0;
0189 }
0190
0191
0192
0193
0194 ret = 0;
0195
0196 out:
0197 close(mem);
0198 return ret;
0199 }
0200
0201 int main(void)
0202 {
0203 int sk_pair[2], ret = 1, status, listener;
0204 pid_t worker = 0 , tracer = 0;
0205
0206 if (socketpair(PF_LOCAL, SOCK_SEQPACKET, 0, sk_pair) < 0) {
0207 perror("socketpair");
0208 return 1;
0209 }
0210
0211 worker = fork();
0212 if (worker < 0) {
0213 perror("fork");
0214 goto close_pair;
0215 }
0216
0217 if (worker == 0) {
0218 listener = user_trap_syscall(__NR_mount,
0219 SECCOMP_FILTER_FLAG_NEW_LISTENER);
0220 if (listener < 0) {
0221 perror("seccomp");
0222 exit(1);
0223 }
0224
0225
0226
0227
0228 if (setuid(1000) < 0) {
0229 perror("setuid");
0230 exit(1);
0231 }
0232
0233
0234
0235
0236
0237 if (send_fd(sk_pair[1], listener) < 0)
0238 exit(1);
0239 close(listener);
0240
0241 if (mkdir("/tmp/foo", 0755) < 0) {
0242 perror("mkdir");
0243 exit(1);
0244 }
0245
0246
0247
0248
0249 if (mount("/dev/sda", "/tmp/foo", NULL, 0, NULL) != -1) {
0250 fprintf(stderr, "huh? mounted /dev/sda?\n");
0251 exit(1);
0252 }
0253
0254 if (errno != EPERM) {
0255 perror("bad error from mount");
0256 exit(1);
0257 }
0258
0259
0260
0261
0262 if (mount("/tmp/foo", "/tmp/foo", NULL, MS_BIND, NULL) < 0) {
0263 perror("mount");
0264 exit(1);
0265 }
0266
0267 exit(0);
0268 }
0269
0270
0271
0272
0273 listener = recv_fd(sk_pair[0]);
0274 if (listener < 0)
0275 goto out_kill;
0276
0277
0278
0279
0280
0281
0282 tracer = fork();
0283 if (tracer < 0) {
0284 perror("fork");
0285 goto out_kill;
0286 }
0287
0288 if (tracer == 0) {
0289 struct seccomp_notif *req;
0290 struct seccomp_notif_resp *resp;
0291 struct seccomp_notif_sizes sizes;
0292
0293 if (seccomp(SECCOMP_GET_NOTIF_SIZES, 0, &sizes) < 0) {
0294 perror("seccomp(GET_NOTIF_SIZES)");
0295 goto out_close;
0296 }
0297
0298 req = malloc(sizes.seccomp_notif);
0299 if (!req)
0300 goto out_close;
0301
0302 resp = malloc(sizes.seccomp_notif_resp);
0303 if (!resp)
0304 goto out_req;
0305 memset(resp, 0, sizes.seccomp_notif_resp);
0306
0307 while (1) {
0308 memset(req, 0, sizes.seccomp_notif);
0309 if (ioctl(listener, SECCOMP_IOCTL_NOTIF_RECV, req)) {
0310 perror("ioctl recv");
0311 goto out_resp;
0312 }
0313
0314 if (handle_req(req, resp, listener) < 0)
0315 goto out_resp;
0316
0317
0318
0319
0320
0321
0322
0323
0324
0325
0326 if (ioctl(listener, SECCOMP_IOCTL_NOTIF_SEND, resp) < 0 &&
0327 errno != ENOENT) {
0328 perror("ioctl send");
0329 goto out_resp;
0330 }
0331 }
0332 out_resp:
0333 free(resp);
0334 out_req:
0335 free(req);
0336 out_close:
0337 close(listener);
0338 exit(1);
0339 }
0340
0341 close(listener);
0342
0343 if (waitpid(worker, &status, 0) != worker) {
0344 perror("waitpid");
0345 goto out_kill;
0346 }
0347
0348 if (umount2("/tmp/foo", MNT_DETACH) < 0 && errno != EINVAL) {
0349 perror("umount2");
0350 goto out_kill;
0351 }
0352
0353 if (remove("/tmp/foo") < 0 && errno != ENOENT) {
0354 perror("remove");
0355 exit(1);
0356 }
0357
0358 if (!WIFEXITED(status) || WEXITSTATUS(status)) {
0359 fprintf(stderr, "worker exited nonzero\n");
0360 goto out_kill;
0361 }
0362
0363 ret = 0;
0364
0365 out_kill:
0366 if (tracer > 0)
0367 kill(tracer, SIGKILL);
0368 if (worker > 0)
0369 kill(worker, SIGKILL);
0370
0371 close_pair:
0372 close(sk_pair[0]);
0373 close(sk_pair[1]);
0374 return ret;
0375 }