Back to home page

OSCL-LXR

 
 

    


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     /* Only allow bind mounts. */
0120     if (!(req->data.args[3] & MS_BIND))
0121         return 0;
0122 
0123     /*
0124      * Ok, let's read the task's memory to see where they wanted their
0125      * mount to go.
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      * Now we avoid a TOCTOU: we referred to a pid by its pid, but since
0136      * the pid that made the syscall may have died, we need to confirm that
0137      * the pid is still valid after we open its /proc/pid/mem file. We can
0138      * ask the listener fd this as follows.
0139      *
0140      * Note that this check should occur *after* any task-specific
0141      * resources are opened, to make sure that the task has not died and
0142      * we're not wrongly reading someone else's state in order to make
0143      * decisions.
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      * Phew, we've got the right /proc/pid/mem. Now we can read it. Note
0152      * that to avoid another TOCTOU, we should read all of the pointer args
0153      * before we decide to allow the syscall.
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      * Our policy is to only allow bind mounts inside /tmp. This isn't very
0179      * interesting, because we could do unprivlieged bind mounts with user
0180      * namespaces already, but you get the idea.
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     /* Even if we didn't allow it because of policy, generating the
0192      * response was be a success, because we want to tell the worker EPERM.
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          * Drop privileges. We definitely can't mount as uid 1000.
0227          */
0228         if (setuid(1000) < 0) {
0229             perror("setuid");
0230             exit(1);
0231         }
0232 
0233         /*
0234          * Send the listener to the parent; also serves as
0235          * synchronization.
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          * Try a bad mount just for grins.
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          * Ok, we expect this one to succeed.
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      * Get the listener from the child.
0272      */
0273     listener = recv_fd(sk_pair[0]);
0274     if (listener < 0)
0275         goto out_kill;
0276 
0277     /*
0278      * Fork a task to handle the requests. This isn't strictly necessary,
0279      * but it makes the particular writing of this sample easier, since we
0280      * can just wait ofr the tracee to exit and kill the tracer.
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              * ENOENT here means that the task may have gotten a
0319              * signal and restarted the syscall. It's up to the
0320              * handler to decide what to do in this case, but for
0321              * the sample code, we just ignore it. Probably
0322              * something better should happen, like undoing the
0323              * mount, or keeping track of the args to make sure we
0324              * don't do it again.
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 }