diff options
Diffstat (limited to 'tests/sys/kern/unix_passfd_test.c')
-rw-r--r-- | tests/sys/kern/unix_passfd_test.c | 205 |
1 files changed, 205 insertions, 0 deletions
diff --git a/tests/sys/kern/unix_passfd_test.c b/tests/sys/kern/unix_passfd_test.c index 74095859d899..7dc4541ad402 100644 --- a/tests/sys/kern/unix_passfd_test.c +++ b/tests/sys/kern/unix_passfd_test.c @@ -27,15 +27,19 @@ */ #include <sys/param.h> +#include <sys/jail.h> #include <sys/socket.h> #include <sys/stat.h> #include <sys/sysctl.h> #include <sys/time.h> #include <sys/resource.h> #include <sys/un.h> +#include <sys/wait.h> +#include <err.h> #include <errno.h> #include <fcntl.h> +#include <jail.h> #include <limits.h> #include <stdio.h> #include <stdlib.h> @@ -376,6 +380,30 @@ ATF_TC_BODY(simple_send_fd_msg_cmsg_cloexec, tc) } /* + * Like simple_send_fd but also sets MSG_CMSG_CLOFORK and checks that the + * received file descriptor has the FD_CLOFORK flag set. + */ +ATF_TC_WITHOUT_HEAD(simple_send_fd_msg_cmsg_clofork); +ATF_TC_BODY(simple_send_fd_msg_cmsg_clofork, tc) +{ + struct stat getfd_stat, putfd_stat; + int fd[2], getfd, putfd; + + domainsocketpair(fd); + tempfile(&putfd); + dofstat(putfd, &putfd_stat); + sendfd(fd[0], putfd); + recvfd(fd[1], &getfd, MSG_CMSG_CLOFORK); + dofstat(getfd, &getfd_stat); + samefile(&putfd_stat, &getfd_stat); + ATF_REQUIRE_EQ_MSG(fcntl(getfd, F_GETFD) & FD_CLOFORK, FD_CLOFORK, + "FD_CLOFORK not set on the received file descriptor"); + close(putfd); + close(getfd); + closesocketpair(fd); +} + +/* * Same as simple_send_fd, only close the file reference after sending, so that * the only reference is the descriptor in the UNIX domain socket buffer. */ @@ -544,6 +572,51 @@ ATF_TC_BODY(send_overflow, tc) closesocketpair(fd); } +/* + * Make sure that we do not receive descriptors with MSG_PEEK. + */ +ATF_TC_WITHOUT_HEAD(peek); +ATF_TC_BODY(peek, tc) +{ + int fd[2], getfd, putfd, nfds; + + domainsocketpair(fd); + tempfile(&putfd); + nfds = getnfds(); + sendfd(fd[0], putfd); + ATF_REQUIRE(getnfds() == nfds); + + /* First make MSG_PEEK recvmsg(2)... */ + char cbuf[CMSG_SPACE(sizeof(int))]; + char buf[1]; + struct iovec iov = { + .iov_base = buf, + .iov_len = sizeof(buf) + }; + struct msghdr msghdr = { + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = cbuf, + .msg_controllen = sizeof(cbuf), + }; + ATF_REQUIRE(1 == recvmsg(fd[1], &msghdr, MSG_PEEK)); + for (struct cmsghdr *cmsghdr = CMSG_FIRSTHDR(&msghdr); + cmsghdr != NULL; cmsghdr = CMSG_NXTHDR(&msghdr, cmsghdr)) { + /* Usually this is some garbage. */ + printf("level %d type %d len %u\n", + cmsghdr->cmsg_level, cmsghdr->cmsg_type, cmsghdr->cmsg_len); + } + + /* ... and make sure we did not receive any descriptors! */ + ATF_REQUIRE(getnfds() == nfds); + + /* Now really receive a descriptor. */ + recvfd(fd[1], &getfd, 0); + ATF_REQUIRE(getnfds() == nfds + 1); + close(putfd); + close(getfd); + closesocketpair(fd); +} /* * Send two files. Then receive them. Make sure they are returned in the @@ -987,16 +1060,147 @@ ATF_TC_BODY(control_creates_records, tc) closesocketpair(fd); } +ATF_TC_WITH_CLEANUP(cross_jail_dirfd); +ATF_TC_HEAD(cross_jail_dirfd, tc) +{ + atf_tc_set_md_var(tc, "require.user", "root"); +} +ATF_TC_BODY(cross_jail_dirfd, tc) +{ + int error, sock[2], jid1, jid2, status; + pid_t pid1, pid2; + + domainsocketpair(sock); + + error = mkdir("./a", 0755); + ATF_REQUIRE(error == 0); + error = mkdir("./b", 0755); + ATF_REQUIRE(error == 0); + error = mkdir("./c", 0755); + ATF_REQUIRE(error == 0); + error = mkdir("./a/c", 0755); + ATF_REQUIRE(error == 0); + + jid1 = jail_setv(JAIL_CREATE, + "name", "passfd_test_cross_jail_dirfd1", + "path", "./a", + "persist", NULL, + NULL); + ATF_REQUIRE_MSG(jid1 >= 0, "jail_setv: %s", jail_errmsg); + + jid2 = jail_setv(JAIL_CREATE, + "name", "passfd_test_cross_jail_dirfd2", + "path", "./b", + "persist", NULL, + NULL); + ATF_REQUIRE_MSG(jid2 >= 0, "jail_setv: %s", jail_errmsg); + + pid1 = fork(); + ATF_REQUIRE(pid1 >= 0); + if (pid1 == 0) { + ssize_t len; + int dfd, error; + char ch; + + error = jail_attach(jid1); + if (error != 0) + err(1, "jail_attach"); + + dfd = open(".", O_RDONLY | O_DIRECTORY); + if (dfd < 0) + err(1, "open(\".\") in jail %d", jid1); + + ch = 0; + len = sendfd_payload(sock[0], dfd, &ch, sizeof(ch)); + if (len == -1) + err(1, "sendmsg"); + + _exit(0); + } + + pid2 = fork(); + ATF_REQUIRE(pid2 >= 0); + if (pid2 == 0) { + ssize_t len; + int dfd, dfd2, error, fd; + char ch; + + error = jail_attach(jid2); + if (error != 0) + err(1, "jail_attach"); + + /* Get a directory from outside the jail root. */ + len = recvfd_payload(sock[1], &dfd, &ch, sizeof(ch), + CMSG_SPACE(sizeof(int)), 0); + if (len == -1) + err(1, "recvmsg"); + + if ((fcntl(dfd, F_GETFD) & FD_RESOLVE_BENEATH) == 0) + errx(1, "dfd does not have FD_RESOLVE_BENEATH set"); + + /* Make sure we can't chdir. */ + error = fchdir(dfd); + if (error == 0) + errx(1, "fchdir succeeded"); + if (errno != ENOTCAPABLE) + err(1, "fchdir"); + + /* Make sure a dotdot access fails. */ + fd = openat(dfd, "../c", O_RDONLY | O_DIRECTORY); + if (fd >= 0) + errx(1, "openat(\"../c\") succeeded"); + if (errno != ENOTCAPABLE) + err(1, "openat"); + + /* Accesses within the sender's jail root are ok. */ + fd = openat(dfd, "c", O_RDONLY | O_DIRECTORY); + if (fd < 0) + err(1, "openat(\"c\")"); + + dfd2 = openat(dfd, "", O_EMPTY_PATH | O_RDONLY | O_DIRECTORY); + if (dfd2 < 0) + err(1, "openat(\"\")"); + if ((fcntl(dfd2, F_GETFD) & FD_RESOLVE_BENEATH) == 0) + errx(1, "dfd2 does not have FD_RESOLVE_BENEATH set"); + + _exit(0); + } + + error = waitpid(pid1, &status, 0); + ATF_REQUIRE(error != -1); + ATF_REQUIRE(WIFEXITED(status)); + ATF_REQUIRE(WEXITSTATUS(status) == 0); + error = waitpid(pid2, &status, 0); + ATF_REQUIRE(error != -1); + ATF_REQUIRE(WIFEXITED(status)); + ATF_REQUIRE(WEXITSTATUS(status) == 0); + + closesocketpair(sock); +} +ATF_TC_CLEANUP(cross_jail_dirfd, tc) +{ + int jid; + + jid = jail_getid("passfd_test_cross_jail_dirfd1"); + if (jid >= 0 && jail_remove(jid) != 0) + err(1, "jail_remove"); + jid = jail_getid("passfd_test_cross_jail_dirfd2"); + if (jid >= 0 && jail_remove(jid) != 0) + err(1, "jail_remove"); +} + ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, simple_send_fd); ATF_TP_ADD_TC(tp, simple_send_fd_msg_cmsg_cloexec); + ATF_TP_ADD_TC(tp, simple_send_fd_msg_cmsg_clofork); ATF_TP_ADD_TC(tp, send_and_close); ATF_TP_ADD_TC(tp, send_and_cancel); ATF_TP_ADD_TC(tp, send_and_shutdown); ATF_TP_ADD_TC(tp, send_a_lot); ATF_TP_ADD_TC(tp, send_overflow); + ATF_TP_ADD_TC(tp, peek); ATF_TP_ADD_TC(tp, two_files); ATF_TP_ADD_TC(tp, bundle); ATF_TP_ADD_TC(tp, bundle_cancel); @@ -1006,6 +1210,7 @@ ATF_TP_ADD_TCS(tp) ATF_TP_ADD_TC(tp, copyout_rights_error); ATF_TP_ADD_TC(tp, empty_rights_message); ATF_TP_ADD_TC(tp, control_creates_records); + ATF_TP_ADD_TC(tp, cross_jail_dirfd); return (atf_no_error()); } |