aboutsummaryrefslogtreecommitdiff
path: root/tests/sys/kern/unix_passfd_test.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/sys/kern/unix_passfd_test.c')
-rw-r--r--tests/sys/kern/unix_passfd_test.c205
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());
}