diff --git a/examples/cp/.gitignore b/examples/cp/.gitignore new file mode 100644 index 0000000..4df168f --- /dev/null +++ b/examples/cp/.gitignore @@ -0,0 +1 @@ +cp_priv_sep diff --git a/examples/cp/Makefile b/examples/cp/Makefile index c67ab5e..4d3e0e4 100644 --- a/examples/cp/Makefile +++ b/examples/cp/Makefile @@ -1,13 +1,17 @@ C=clang -all: c c_cgroup ml +all: c cp_priv_sep c_cgroup ml clean: - rm -f c ml + rm -f c cp_priv_sep c_cgroup ml c: cp.c ${C} -o c cp.c +cp_priv_sep: cp_priv_sep.c ../../lib/clone3.c ../../include/clone3.h + ${C} -I../../include -o cp_priv_sep cp_priv_sep.c ../../lib/clone3.c + sudo setcap CAP_SYS_ADMIN+eip ./cp_priv_sep + c_cgroup: cp_cgroup.c ${C} -o c_cgroup cp_cgroup.c diff --git a/examples/cp/cp_priv_sep.c b/examples/cp/cp_priv_sep.c new file mode 100644 index 0000000..5db929f --- /dev/null +++ b/examples/cp/cp_priv_sep.c @@ -0,0 +1,154 @@ +// For privilege separation +#include +#include +#include +#include +#include +#include +#include +#include + +// This program +#include + +#define BUF_SIZE 4096 + +struct copy_args { + char *src; + char *dst; +}; + +int process_args(int argc, char **argv, struct copy_args *); +int do_copy(struct copy_args *args); + +int main(int argc, char **argv) { + pid_t child; + + // Spawn args processor + int args_pid_fd; + + struct clone_args args_processor_cl_args = { + .flags = CLONE_NEWIPC | CLONE_NEWNET | CLONE_NEWNS | CLONE_NEWPID | + CLONE_NEWUSER | CLONE_NEWUTS | CLONE_PIDFD, + .pidfd = (uint64_t)&args_pid_fd, + .child_tid = (uint64_t)NULL, + .parent_tid = (uint64_t)NULL, + .exit_signal = SIGCHLD, + .stack = (uint64_t)NULL, + .stack_size = 0, + .tls = (uint64_t)NULL, + }; + + int args_pipe_fds[2]; + if (pipe(args_pipe_fds) < 0) { + perror("pipe"); + exit(-1); + } + + if ((child = clone3(&args_processor_cl_args)) == 0) { + char args[sizeof(struct copy_args)]; + + int code = process_args(argc, argv, &args); + if (code != 0) { + exit(code); + } + + write(args_pipe_fds[1], &args, sizeof(args)); + exit(0); + } else if (child < 0) { + perror("clone3"); + return -1; + } + + // Wait for args processor + // Implicit assertion that sizeof(args) < the pipe buffer size (won't block) + siginfo_t status; + if (waitid(P_PIDFD, args_pid_fd, &status, WEXITED) == -1) { + perror("waitid"); + return -1; + } + + if (status.si_status != 0) { + return status.si_status; + } + + char args[sizeof(struct copy_args)]; + read(args_pipe_fds[0], &args, sizeof(args)); + + // Spawn copier (can be more separated) + int copy_pid_fd; + + struct clone_args do_copy_cl_args = { + .flags = CLONE_NEWIPC | CLONE_NEWNET | CLONE_NEWPID | CLONE_NEWUTS | + CLONE_PIDFD, + .pidfd = (uint64_t)©_pid_fd, + .child_tid = (uint64_t)NULL, + .parent_tid = (uint64_t)NULL, + .exit_signal = SIGCHLD, + .stack = (uint64_t)NULL, + .stack_size = 0, + .tls = (uint64_t)NULL, + }; + + if ((child = clone3(&do_copy_cl_args)) == 0) { + int code = do_copy(&args); + exit(code); + } else if (child < 0) { + perror("clone3"); + return -1; + } + + if (waitid(P_PIDFD, copy_pid_fd, &status, WEXITED) == -1) { + perror("waitid"); + return -1; + } + + return status.si_status; +} + +int process_args(int argc, char **argv, struct copy_args *args) { + if (argc != 3) { + fprintf(stderr, "usage: %s SRC DST\n", argv[0]); + return -1; + } + + args->src = argv[1]; + args->dst = argv[2]; + + return 0; +} + +int do_copy(struct copy_args *args) { + fprintf(stderr, "src: `%s`; dst: `%s`\n", args->src, args->dst); + int src_fd, dst_fd; + + if ((src_fd = open(args->src, O_RDONLY)) < 0) { + perror("open"); + return 1; + } + if ((dst_fd = open(args->dst, O_WRONLY | O_CREAT | O_TRUNC)) < 0) { + perror("open"); + return 1; + } + + char buf[BUF_SIZE]; + while (1) { + int read_bytes = read(src_fd, buf, BUF_SIZE); + if (read_bytes < 0) { + perror("read"); + return read_bytes; + } + + if (!read_bytes) { + fprintf(stderr, "copy complete\n"); + return 0; + } + + if (write(dst_fd, buf, read_bytes) < 0) { + perror("write"); + return -1; + } + } + + return 0; +}