Path: blob/main/contrib/capsicum-test/capability-fd.cc
39475 views
#include <stdio.h>1#include <unistd.h>2#include <sys/file.h>3#include <sys/types.h>4#include <sys/wait.h>5#include <sys/stat.h>6#include <sys/mman.h>7#include <sys/select.h>8#include <sys/socket.h>9#include <sys/time.h>10#include <errno.h>11#include <fcntl.h>12#include <poll.h>13#include <stdint.h>1415#include "capsicum.h"16#include "syscalls.h"17#include "capsicum-test.h"1819/* Utilities for printing rights information */20/* Written in C style to allow for: */21/* TODO(drysdale): migrate these to somewhere in libcaprights/ */22#define RIGHTS_INFO(RR) { (RR), #RR}23typedef struct {24uint64_t right;25const char* name;26} right_info;27static right_info known_rights[] = {28/* Rights that are common to all versions of Capsicum */29RIGHTS_INFO(CAP_READ),30RIGHTS_INFO(CAP_WRITE),31RIGHTS_INFO(CAP_SEEK_TELL),32RIGHTS_INFO(CAP_SEEK),33RIGHTS_INFO(CAP_PREAD),34RIGHTS_INFO(CAP_PWRITE),35RIGHTS_INFO(CAP_MMAP),36RIGHTS_INFO(CAP_MMAP_R),37RIGHTS_INFO(CAP_MMAP_W),38RIGHTS_INFO(CAP_MMAP_X),39RIGHTS_INFO(CAP_MMAP_RW),40RIGHTS_INFO(CAP_MMAP_RX),41RIGHTS_INFO(CAP_MMAP_WX),42RIGHTS_INFO(CAP_MMAP_RWX),43RIGHTS_INFO(CAP_CREATE),44RIGHTS_INFO(CAP_FEXECVE),45RIGHTS_INFO(CAP_FSYNC),46RIGHTS_INFO(CAP_FTRUNCATE),47RIGHTS_INFO(CAP_LOOKUP),48RIGHTS_INFO(CAP_FCHDIR),49RIGHTS_INFO(CAP_FCHFLAGS),50RIGHTS_INFO(CAP_CHFLAGSAT),51RIGHTS_INFO(CAP_FCHMOD),52RIGHTS_INFO(CAP_FCHMODAT),53RIGHTS_INFO(CAP_FCHOWN),54RIGHTS_INFO(CAP_FCHOWNAT),55RIGHTS_INFO(CAP_FCNTL),56RIGHTS_INFO(CAP_FLOCK),57RIGHTS_INFO(CAP_FPATHCONF),58RIGHTS_INFO(CAP_FSCK),59RIGHTS_INFO(CAP_FSTAT),60RIGHTS_INFO(CAP_FSTATAT),61RIGHTS_INFO(CAP_FSTATFS),62RIGHTS_INFO(CAP_FUTIMES),63RIGHTS_INFO(CAP_FUTIMESAT),64RIGHTS_INFO(CAP_MKDIRAT),65RIGHTS_INFO(CAP_MKFIFOAT),66RIGHTS_INFO(CAP_MKNODAT),67RIGHTS_INFO(CAP_RENAMEAT_SOURCE),68RIGHTS_INFO(CAP_SYMLINKAT),69RIGHTS_INFO(CAP_UNLINKAT),70RIGHTS_INFO(CAP_ACCEPT),71RIGHTS_INFO(CAP_BIND),72RIGHTS_INFO(CAP_CONNECT),73RIGHTS_INFO(CAP_GETPEERNAME),74RIGHTS_INFO(CAP_GETSOCKNAME),75RIGHTS_INFO(CAP_GETSOCKOPT),76RIGHTS_INFO(CAP_LISTEN),77RIGHTS_INFO(CAP_PEELOFF),78RIGHTS_INFO(CAP_RECV),79RIGHTS_INFO(CAP_SEND),80RIGHTS_INFO(CAP_SETSOCKOPT),81RIGHTS_INFO(CAP_SHUTDOWN),82RIGHTS_INFO(CAP_BINDAT),83RIGHTS_INFO(CAP_CONNECTAT),84RIGHTS_INFO(CAP_LINKAT_SOURCE),85RIGHTS_INFO(CAP_RENAMEAT_TARGET),86RIGHTS_INFO(CAP_SOCK_CLIENT),87RIGHTS_INFO(CAP_SOCK_SERVER),88RIGHTS_INFO(CAP_MAC_GET),89RIGHTS_INFO(CAP_MAC_SET),90RIGHTS_INFO(CAP_SEM_GETVALUE),91RIGHTS_INFO(CAP_SEM_POST),92RIGHTS_INFO(CAP_SEM_WAIT),93RIGHTS_INFO(CAP_EVENT),94RIGHTS_INFO(CAP_KQUEUE_EVENT),95RIGHTS_INFO(CAP_IOCTL),96RIGHTS_INFO(CAP_TTYHOOK),97RIGHTS_INFO(CAP_PDWAIT),98RIGHTS_INFO(CAP_PDGETPID),99RIGHTS_INFO(CAP_PDKILL),100RIGHTS_INFO(CAP_EXTATTR_DELETE),101RIGHTS_INFO(CAP_EXTATTR_GET),102RIGHTS_INFO(CAP_EXTATTR_LIST),103RIGHTS_INFO(CAP_EXTATTR_SET),104RIGHTS_INFO(CAP_ACL_CHECK),105RIGHTS_INFO(CAP_ACL_DELETE),106RIGHTS_INFO(CAP_ACL_GET),107RIGHTS_INFO(CAP_ACL_SET),108RIGHTS_INFO(CAP_KQUEUE_CHANGE),109RIGHTS_INFO(CAP_KQUEUE),110/* Rights that are only present in some version or some OS, and so are #ifdef'ed */111/* LINKAT got split */112#ifdef CAP_LINKAT113RIGHTS_INFO(CAP_LINKAT),114#endif115#ifdef CAP_LINKAT_SOURCE116RIGHTS_INFO(CAP_LINKAT_SOURCE),117#endif118#ifdef CAP_LINKAT_TARGET119RIGHTS_INFO(CAP_LINKAT_TARGET),120#endif121/* Linux aliased some FD operations for pdgetpid/pdkill */122#ifdef CAP_PDGETPID_FREEBSD123RIGHTS_INFO(CAP_PDGETPID_FREEBSD),124#endif125#ifdef CAP_PDKILL_FREEBSD126RIGHTS_INFO(CAP_PDKILL_FREEBSD),127#endif128/* Linux-specific rights */129#ifdef CAP_FSIGNAL130RIGHTS_INFO(CAP_FSIGNAL),131#endif132#ifdef CAP_EPOLL_CTL133RIGHTS_INFO(CAP_EPOLL_CTL),134#endif135#ifdef CAP_NOTIFY136RIGHTS_INFO(CAP_NOTIFY),137#endif138#ifdef CAP_SETNS139RIGHTS_INFO(CAP_SETNS),140#endif141#ifdef CAP_PERFMON142RIGHTS_INFO(CAP_PERFMON),143#endif144#ifdef CAP_BPF145RIGHTS_INFO(CAP_BPF),146#endif147/* Rights in later versions of FreeBSD (>10.0) */148};149150void ShowCapRights(FILE *out, int fd) {151size_t ii;152bool first = true;153cap_rights_t rights;154CAP_SET_NONE(&rights);155if (cap_rights_get(fd, &rights) < 0) {156fprintf(out, "Failed to get rights for fd %d: errno %d\n", fd, errno);157return;158}159160/* First print out all known rights */161size_t num_known = (sizeof(known_rights)/sizeof(known_rights[0]));162for (ii = 0; ii < num_known; ii++) {163if (cap_rights_is_set(&rights, known_rights[ii].right)) {164if (!first) fprintf(out, ",");165first = false;166fprintf(out, "%s", known_rights[ii].name);167}168}169/* Now repeat the loop, clearing rights we know of; this needs to be170* a separate loop because some named rights overlap.171*/172for (ii = 0; ii < num_known; ii++) {173cap_rights_clear(&rights, known_rights[ii].right);174}175/* The following relies on the internal structure of cap_rights_t to176* try to show rights we don't know about. */177for (ii = 0; ii < (size_t)CAPARSIZE(&rights); ii++) {178uint64_t bits = (rights.cr_rights[0] & 0x01ffffffffffffffULL);179if (bits != 0) {180uint64_t which = 1;181for (which = 1; which < 0x0200000000000000 ; which <<= 1) {182if (bits & which) {183if (!first) fprintf(out, ",");184fprintf(out, "CAP_RIGHT(%d, 0x%016llxULL)", (int)ii, (long long unsigned)which);185}186}187}188}189fprintf(out, "\n");190}191192void ShowAllCapRights(FILE *out) {193int fd;194struct rlimit limits;195if (getrlimit(RLIMIT_NOFILE, &limits) != 0) {196fprintf(out, "Failed to getrlimit for max FDs: errno %d\n", errno);197return;198}199for (fd = 0; fd < (int)limits.rlim_cur; fd++) {200if (fcntl(fd, F_GETFD, 0) != 0) {201continue;202}203fprintf(out, "fd %d: ", fd);204ShowCapRights(out, fd);205}206}207208FORK_TEST(Capability, CapNew) {209cap_rights_t r_rws;210cap_rights_init(&r_rws, CAP_READ, CAP_WRITE, CAP_SEEK);211cap_rights_t r_all;212CAP_SET_ALL(&r_all);213214int cap_fd = dup(STDOUT_FILENO);215cap_rights_t rights;216CAP_SET_NONE(&rights);217EXPECT_OK(cap_rights_get(cap_fd, &rights));218EXPECT_RIGHTS_EQ(&r_all, &rights);219220EXPECT_OK(cap_fd);221EXPECT_OK(cap_rights_limit(cap_fd, &r_rws));222if (cap_fd < 0) return;223int rc = write(cap_fd, "OK!\n", 4);224EXPECT_OK(rc);225EXPECT_EQ(4, rc);226EXPECT_OK(cap_rights_get(cap_fd, &rights));227EXPECT_RIGHTS_EQ(&r_rws, &rights);228229// dup/dup2 should preserve rights.230int cap_dup = dup(cap_fd);231EXPECT_OK(cap_dup);232EXPECT_OK(cap_rights_get(cap_dup, &rights));233EXPECT_RIGHTS_EQ(&r_rws, &rights);234close(cap_dup);235EXPECT_OK(dup2(cap_fd, cap_dup));236EXPECT_OK(cap_rights_get(cap_dup, &rights));237EXPECT_RIGHTS_EQ(&r_rws, &rights);238close(cap_dup);239#ifdef HAVE_DUP3240EXPECT_OK(dup3(cap_fd, cap_dup, 0));241EXPECT_OK(cap_rights_get(cap_dup, &rights));242EXPECT_RIGHTS_EQ(&r_rws, &rights);243close(cap_dup);244#endif245246// Try to get a disjoint set of rights in a sub-capability.247cap_rights_t r_rs;248cap_rights_init(&r_rs, CAP_READ, CAP_SEEK);249cap_rights_t r_rsmapchmod;250cap_rights_init(&r_rsmapchmod, CAP_READ, CAP_SEEK, CAP_MMAP, CAP_FCHMOD);251int cap_cap_fd = dup(cap_fd);252EXPECT_OK(cap_cap_fd);253EXPECT_NOTCAPABLE(cap_rights_limit(cap_cap_fd, &r_rsmapchmod));254255// Dump rights info to stderr (mostly to ensure that Show[All]CapRights()256// is working.257ShowAllCapRights(stderr);258259EXPECT_OK(close(cap_fd));260}261262FORK_TEST(Capability, CapEnter) {263EXPECT_EQ(0, cap_enter());264}265266FORK_TEST(Capability, BasicInterception) {267cap_rights_t r_0;268cap_rights_init(&r_0, 0);269int cap_fd = dup(1);270EXPECT_OK(cap_fd);271EXPECT_OK(cap_rights_limit(cap_fd, &r_0));272273EXPECT_NOTCAPABLE(write(cap_fd, "", 0));274275EXPECT_OK(cap_enter()); // Enter capability mode276277EXPECT_NOTCAPABLE(write(cap_fd, "", 0));278279// Create a new capability which does have write permission280cap_rights_t r_ws;281cap_rights_init(&r_ws, CAP_WRITE, CAP_SEEK);282int cap_fd2 = dup(1);283EXPECT_OK(cap_fd2);284EXPECT_OK(cap_rights_limit(cap_fd2, &r_ws));285EXPECT_OK(write(cap_fd2, "", 0));286287// Tidy up.288if (cap_fd >= 0) close(cap_fd);289if (cap_fd2 >= 0) close(cap_fd2);290}291292FORK_TEST_ON(Capability, OpenAtDirectoryTraversal, TmpFile("cap_openat_testfile")) {293int dir = open(tmpdir.c_str(), O_RDONLY);294EXPECT_OK(dir);295296cap_enter();297298int file = openat(dir, "cap_openat_testfile", O_RDONLY|O_CREAT, 0644);299EXPECT_OK(file);300301// Test that we are confined to /tmp, and cannot302// escape using absolute paths or ../.303int new_file = openat(dir, "../dev/null", O_RDONLY);304EXPECT_EQ(-1, new_file);305306new_file = openat(dir, "..", O_RDONLY);307EXPECT_EQ(-1, new_file);308309new_file = openat(dir, "/dev/null", O_RDONLY);310EXPECT_EQ(-1, new_file);311312new_file = openat(dir, "/", O_RDONLY);313EXPECT_EQ(-1, new_file);314315// Tidy up.316close(file);317close(dir);318}319320FORK_TEST_ON(Capability, FileInSync, TmpFile("cap_file_sync")) {321int fd = open(TmpFile("cap_file_sync"), O_RDWR|O_CREAT, 0644);322EXPECT_OK(fd);323const char* message = "Hello capability world";324EXPECT_OK(write(fd, message, strlen(message)));325326cap_rights_t r_rsstat;327cap_rights_init(&r_rsstat, CAP_READ, CAP_SEEK, CAP_FSTAT);328329int cap_fd = dup(fd);330EXPECT_OK(cap_fd);331EXPECT_OK(cap_rights_limit(cap_fd, &r_rsstat));332int cap_cap_fd = dup(cap_fd);333EXPECT_OK(cap_cap_fd);334EXPECT_OK(cap_rights_limit(cap_cap_fd, &r_rsstat));335336EXPECT_OK(cap_enter()); // Enter capability mode.337338// Changes to one file descriptor affect the others.339EXPECT_EQ(1, lseek(fd, 1, SEEK_SET));340EXPECT_EQ(1, lseek(fd, 0, SEEK_CUR));341EXPECT_EQ(1, lseek(cap_fd, 0, SEEK_CUR));342EXPECT_EQ(1, lseek(cap_cap_fd, 0, SEEK_CUR));343EXPECT_EQ(3, lseek(cap_fd, 3, SEEK_SET));344EXPECT_EQ(3, lseek(fd, 0, SEEK_CUR));345EXPECT_EQ(3, lseek(cap_fd, 0, SEEK_CUR));346EXPECT_EQ(3, lseek(cap_cap_fd, 0, SEEK_CUR));347EXPECT_EQ(5, lseek(cap_cap_fd, 5, SEEK_SET));348EXPECT_EQ(5, lseek(fd, 0, SEEK_CUR));349EXPECT_EQ(5, lseek(cap_fd, 0, SEEK_CUR));350EXPECT_EQ(5, lseek(cap_cap_fd, 0, SEEK_CUR));351352close(cap_cap_fd);353close(cap_fd);354close(fd);355}356357// Create a capability on /tmp that does not allow CAP_WRITE,358// and check that this restriction is inherited through openat().359FORK_TEST_ON(Capability, Inheritance, TmpFile("cap_openat_write_testfile")) {360int dir = open(tmpdir.c_str(), O_RDONLY);361EXPECT_OK(dir);362363cap_rights_t r_rl;364cap_rights_init(&r_rl, CAP_READ, CAP_LOOKUP);365366int cap_dir = dup(dir);367EXPECT_OK(cap_dir);368EXPECT_OK(cap_rights_limit(cap_dir, &r_rl));369370const char *filename = "cap_openat_write_testfile";371int file = openat(dir, filename, O_WRONLY|O_CREAT, 0644);372EXPECT_OK(file);373EXPECT_EQ(5, write(file, "TEST\n", 5));374if (file >= 0) close(file);375376EXPECT_OK(cap_enter());377file = openat(cap_dir, filename, O_RDONLY);378EXPECT_OK(file);379380cap_rights_t rights;381cap_rights_init(&rights, 0);382EXPECT_OK(cap_rights_get(file, &rights));383EXPECT_RIGHTS_EQ(&r_rl, &rights);384if (file >= 0) close(file);385386file = openat(cap_dir, filename, O_WRONLY|O_APPEND);387EXPECT_NOTCAPABLE(file);388if (file > 0) close(file);389390if (dir > 0) close(dir);391if (cap_dir > 0) close(cap_dir);392}393394395// Ensure that, if the capability had enough rights for the system call to396// pass, then it did. Otherwise, ensure that the errno is ENOTCAPABLE;397// capability restrictions should kick in before any other error logic.398#define CHECK_RIGHT_RESULT(result, rights, ...) do { \399cap_rights_t rights_needed; \400cap_rights_init(&rights_needed, __VA_ARGS__); \401if (cap_rights_contains(&rights, &rights_needed)) { \402EXPECT_OK(result) << std::endl \403<< " need: " << rights_needed \404<< std::endl \405<< " got: " << rights; \406} else { \407EXPECT_EQ(-1, result) << " need: " << rights_needed \408<< std::endl \409<< " got: "<< rights; \410EXPECT_EQ(ENOTCAPABLE, errno); \411} \412} while (0)413414#define EXPECT_MMAP_NOTCAPABLE(result) do { \415void *rv = result; \416EXPECT_EQ(MAP_FAILED, rv); \417EXPECT_EQ(ENOTCAPABLE, errno); \418if (rv != MAP_FAILED) munmap(rv, getpagesize()); \419} while (0)420421#define EXPECT_MMAP_OK(result) do { \422void *rv = result; \423EXPECT_NE(MAP_FAILED, rv) << " with errno " << errno; \424if (rv != MAP_FAILED) munmap(rv, getpagesize()); \425} while (0)426427428// As above, but for the special mmap() case: unmap after successful mmap().429#define CHECK_RIGHT_MMAP_RESULT(result, rights, ...) do { \430cap_rights_t rights_needed; \431cap_rights_init(&rights_needed, __VA_ARGS__); \432if (cap_rights_contains(&rights, &rights_needed)) { \433EXPECT_MMAP_OK(result); \434} else { \435EXPECT_MMAP_NOTCAPABLE(result); \436} \437} while (0)438439FORK_TEST_ON(Capability, Mmap, TmpFile("cap_mmap_operations")) {440int fd = open(TmpFile("cap_mmap_operations"), O_RDWR | O_CREAT, 0644);441EXPECT_OK(fd);442if (fd < 0) return;443444cap_rights_t r_0;445cap_rights_init(&r_0, 0);446cap_rights_t r_mmap;447cap_rights_init(&r_mmap, CAP_MMAP);448cap_rights_t r_r;449cap_rights_init(&r_r, CAP_PREAD);450cap_rights_t r_rmmap;451cap_rights_init(&r_rmmap, CAP_PREAD, CAP_MMAP);452453// If we're missing a capability, it will fail.454int cap_none = dup(fd);455EXPECT_OK(cap_none);456EXPECT_OK(cap_rights_limit(cap_none, &r_0));457int cap_mmap = dup(fd);458EXPECT_OK(cap_mmap);459EXPECT_OK(cap_rights_limit(cap_mmap, &r_mmap));460int cap_read = dup(fd);461EXPECT_OK(cap_read);462EXPECT_OK(cap_rights_limit(cap_read, &r_r));463int cap_both = dup(fd);464EXPECT_OK(cap_both);465EXPECT_OK(cap_rights_limit(cap_both, &r_rmmap));466467EXPECT_OK(cap_enter()); // Enter capability mode.468469EXPECT_MMAP_NOTCAPABLE(mmap(NULL, getpagesize(), PROT_READ, MAP_PRIVATE, cap_none, 0));470EXPECT_MMAP_NOTCAPABLE(mmap(NULL, getpagesize(), PROT_READ, MAP_PRIVATE, cap_mmap, 0));471EXPECT_MMAP_NOTCAPABLE(mmap(NULL, getpagesize(), PROT_READ, MAP_PRIVATE, cap_read, 0));472473EXPECT_MMAP_OK(mmap(NULL, getpagesize(), PROT_READ, MAP_PRIVATE, cap_both, 0));474475// A call with MAP_ANONYMOUS should succeed without any capability requirements.476EXPECT_MMAP_OK(mmap(NULL, getpagesize(), PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0));477478EXPECT_OK(close(cap_both));479EXPECT_OK(close(cap_read));480EXPECT_OK(close(cap_mmap));481EXPECT_OK(close(cap_none));482EXPECT_OK(close(fd));483}484485// Given a file descriptor, create a capability with specific rights and486// make sure only those rights work.487#define TRY_FILE_OPS(fd, ...) do { \488SCOPED_TRACE(#__VA_ARGS__); \489cap_rights_t rights; \490cap_rights_init(&rights, __VA_ARGS__); \491TryFileOps((fd), rights); \492} while (0)493494static void TryFileOps(int fd, cap_rights_t rights) {495int cap_fd = dup(fd);496EXPECT_OK(cap_fd);497EXPECT_OK(cap_rights_limit(cap_fd, &rights));498if (cap_fd < 0) return;499cap_rights_t erights;500EXPECT_OK(cap_rights_get(cap_fd, &erights));501EXPECT_RIGHTS_EQ(&rights, &erights);502503// Check creation of a capability from a capability.504int cap_cap_fd = dup(cap_fd);505EXPECT_OK(cap_cap_fd);506EXPECT_OK(cap_rights_limit(cap_cap_fd, &rights));507EXPECT_NE(cap_fd, cap_cap_fd);508EXPECT_OK(cap_rights_get(cap_cap_fd, &erights));509EXPECT_RIGHTS_EQ(&rights, &erights);510close(cap_cap_fd);511512char ch;513CHECK_RIGHT_RESULT(read(cap_fd, &ch, sizeof(ch)), rights, CAP_READ, CAP_SEEK_ASWAS);514515ssize_t len1 = pread(cap_fd, &ch, sizeof(ch), 0);516CHECK_RIGHT_RESULT(len1, rights, CAP_PREAD);517ssize_t len2 = pread(cap_fd, &ch, sizeof(ch), 0);518CHECK_RIGHT_RESULT(len2, rights, CAP_PREAD);519EXPECT_EQ(len1, len2);520521CHECK_RIGHT_RESULT(write(cap_fd, &ch, sizeof(ch)), rights, CAP_WRITE, CAP_SEEK_ASWAS);522CHECK_RIGHT_RESULT(pwrite(cap_fd, &ch, sizeof(ch), 0), rights, CAP_PWRITE);523CHECK_RIGHT_RESULT(lseek(cap_fd, 0, SEEK_SET), rights, CAP_SEEK);524525#ifdef HAVE_CHFLAGS526// Note: this is not expected to work over NFS.527struct statfs sf;528EXPECT_OK(fstatfs(fd, &sf));529bool is_nfs = (strncmp("nfs", sf.f_fstypename, sizeof(sf.f_fstypename)) == 0);530if (!is_nfs) {531CHECK_RIGHT_RESULT(fchflags(cap_fd, UF_NODUMP), rights, CAP_FCHFLAGS);532}533#endif534535CHECK_RIGHT_MMAP_RESULT(mmap(NULL, getpagesize(), PROT_NONE, MAP_SHARED, cap_fd, 0),536rights, CAP_MMAP);537CHECK_RIGHT_MMAP_RESULT(mmap(NULL, getpagesize(), PROT_READ, MAP_SHARED, cap_fd, 0),538rights, CAP_MMAP_R);539CHECK_RIGHT_MMAP_RESULT(mmap(NULL, getpagesize(), PROT_WRITE, MAP_SHARED, cap_fd, 0),540rights, CAP_MMAP_W);541CHECK_RIGHT_MMAP_RESULT(mmap(NULL, getpagesize(), PROT_EXEC, MAP_SHARED, cap_fd, 0),542rights, CAP_MMAP_X);543CHECK_RIGHT_MMAP_RESULT(mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED, cap_fd, 0),544rights, CAP_MMAP_RW);545CHECK_RIGHT_MMAP_RESULT(mmap(NULL, getpagesize(), PROT_READ | PROT_EXEC, MAP_SHARED, cap_fd, 0),546rights, CAP_MMAP_RX);547CHECK_RIGHT_MMAP_RESULT(mmap(NULL, getpagesize(), PROT_EXEC | PROT_WRITE, MAP_SHARED, cap_fd, 0),548rights, CAP_MMAP_WX);549CHECK_RIGHT_MMAP_RESULT(mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED, cap_fd, 0),550rights, CAP_MMAP_RWX);551552CHECK_RIGHT_RESULT(fsync(cap_fd), rights, CAP_FSYNC);553#ifdef HAVE_SYNC_FILE_RANGE554CHECK_RIGHT_RESULT(sync_file_range(cap_fd, 0, 1, 0), rights, CAP_FSYNC, CAP_SEEK);555#endif556557int rc = fcntl(cap_fd, F_GETFL);558CHECK_RIGHT_RESULT(rc, rights, CAP_FCNTL);559rc = fcntl(cap_fd, F_SETFL, rc);560CHECK_RIGHT_RESULT(rc, rights, CAP_FCNTL);561562CHECK_RIGHT_RESULT(fchown(cap_fd, -1, -1), rights, CAP_FCHOWN);563564CHECK_RIGHT_RESULT(fchmod(cap_fd, 0644), rights, CAP_FCHMOD);565566CHECK_RIGHT_RESULT(flock(cap_fd, LOCK_SH), rights, CAP_FLOCK);567CHECK_RIGHT_RESULT(flock(cap_fd, LOCK_UN), rights, CAP_FLOCK);568569CHECK_RIGHT_RESULT(ftruncate(cap_fd, 0), rights, CAP_FTRUNCATE);570571struct stat sb;572CHECK_RIGHT_RESULT(fstat(cap_fd, &sb), rights, CAP_FSTAT);573574struct statfs cap_sf;575CHECK_RIGHT_RESULT(fstatfs(cap_fd, &cap_sf), rights, CAP_FSTATFS);576577#ifdef HAVE_FPATHCONF578CHECK_RIGHT_RESULT(fpathconf(cap_fd, _PC_NAME_MAX), rights, CAP_FPATHCONF);579#endif580581CHECK_RIGHT_RESULT(futimes(cap_fd, NULL), rights, CAP_FUTIMES);582583struct pollfd pollfd;584pollfd.fd = cap_fd;585pollfd.events = POLLIN | POLLERR | POLLHUP;586pollfd.revents = 0;587int ret = poll(&pollfd, 1, 0);588if (cap_rights_is_set(&rights, CAP_EVENT)) {589EXPECT_OK(ret);590} else {591EXPECT_NE(0, (pollfd.revents & POLLNVAL));592}593594struct timeval tv;595tv.tv_sec = 0;596tv.tv_usec = 100;597fd_set rset;598FD_ZERO(&rset);599FD_SET(cap_fd, &rset);600fd_set wset;601FD_ZERO(&wset);602FD_SET(cap_fd, &wset);603ret = select(cap_fd+1, &rset, &wset, NULL, &tv);604if (cap_rights_is_set(&rights, CAP_EVENT)) {605EXPECT_OK(ret);606} else {607EXPECT_NOTCAPABLE(ret);608}609610// TODO(FreeBSD): kqueue611612EXPECT_OK(close(cap_fd));613}614615FORK_TEST_ON(Capability, Operations, TmpFile("cap_fd_operations")) {616int fd = open(TmpFile("cap_fd_operations"), O_RDWR | O_CREAT, 0644);617EXPECT_OK(fd);618if (fd < 0) return;619620EXPECT_OK(cap_enter()); // Enter capability mode.621622// Try a variety of different combinations of rights - a full623// enumeration is too large (2^N with N~30+) to perform.624TRY_FILE_OPS(fd, CAP_READ);625TRY_FILE_OPS(fd, CAP_PREAD);626TRY_FILE_OPS(fd, CAP_WRITE);627TRY_FILE_OPS(fd, CAP_PWRITE);628TRY_FILE_OPS(fd, CAP_READ, CAP_WRITE);629TRY_FILE_OPS(fd, CAP_PREAD, CAP_PWRITE);630TRY_FILE_OPS(fd, CAP_SEEK);631TRY_FILE_OPS(fd, CAP_FCHFLAGS);632TRY_FILE_OPS(fd, CAP_IOCTL);633TRY_FILE_OPS(fd, CAP_FSTAT);634TRY_FILE_OPS(fd, CAP_MMAP);635TRY_FILE_OPS(fd, CAP_MMAP_R);636TRY_FILE_OPS(fd, CAP_MMAP_W);637TRY_FILE_OPS(fd, CAP_MMAP_X);638TRY_FILE_OPS(fd, CAP_MMAP_RW);639TRY_FILE_OPS(fd, CAP_MMAP_RX);640TRY_FILE_OPS(fd, CAP_MMAP_WX);641TRY_FILE_OPS(fd, CAP_MMAP_RWX);642TRY_FILE_OPS(fd, CAP_FCNTL);643TRY_FILE_OPS(fd, CAP_EVENT);644TRY_FILE_OPS(fd, CAP_FSYNC);645TRY_FILE_OPS(fd, CAP_FCHOWN);646TRY_FILE_OPS(fd, CAP_FCHMOD);647TRY_FILE_OPS(fd, CAP_FTRUNCATE);648TRY_FILE_OPS(fd, CAP_FLOCK);649TRY_FILE_OPS(fd, CAP_FSTATFS);650TRY_FILE_OPS(fd, CAP_FPATHCONF);651TRY_FILE_OPS(fd, CAP_FUTIMES);652TRY_FILE_OPS(fd, CAP_ACL_GET);653TRY_FILE_OPS(fd, CAP_ACL_SET);654TRY_FILE_OPS(fd, CAP_ACL_DELETE);655TRY_FILE_OPS(fd, CAP_ACL_CHECK);656TRY_FILE_OPS(fd, CAP_EXTATTR_GET);657TRY_FILE_OPS(fd, CAP_EXTATTR_SET);658TRY_FILE_OPS(fd, CAP_EXTATTR_DELETE);659TRY_FILE_OPS(fd, CAP_EXTATTR_LIST);660TRY_FILE_OPS(fd, CAP_MAC_GET);661TRY_FILE_OPS(fd, CAP_MAC_SET);662663// Socket-specific.664TRY_FILE_OPS(fd, CAP_GETPEERNAME);665TRY_FILE_OPS(fd, CAP_GETSOCKNAME);666TRY_FILE_OPS(fd, CAP_ACCEPT);667668close(fd);669}670671#define TRY_DIR_OPS(dfd, ...) do { \672cap_rights_t rights; \673cap_rights_init(&rights, __VA_ARGS__); \674TryDirOps((dfd), rights); \675} while (0)676677static void TryDirOps(int dirfd, cap_rights_t rights) {678cap_rights_t erights;679int dfd_cap = dup(dirfd);680EXPECT_OK(dfd_cap);681EXPECT_OK(cap_rights_limit(dfd_cap, &rights));682EXPECT_OK(cap_rights_get(dfd_cap, &erights));683EXPECT_RIGHTS_EQ(&rights, &erights);684685int rc = openat(dfd_cap, "cap_create", O_CREAT | O_RDONLY, 0600);686CHECK_RIGHT_RESULT(rc, rights, CAP_CREATE, CAP_READ, CAP_LOOKUP);687if (rc >= 0) {688EXPECT_OK(close(rc));689EXPECT_OK(unlinkat(dirfd, "cap_create", 0));690}691rc = openat(dfd_cap, "cap_create", O_CREAT | O_WRONLY | O_APPEND, 0600);692CHECK_RIGHT_RESULT(rc, rights, CAP_CREATE, CAP_WRITE, CAP_LOOKUP);693if (rc >= 0) {694EXPECT_OK(close(rc));695EXPECT_OK(unlinkat(dirfd, "cap_create", 0));696}697rc = openat(dfd_cap, "cap_create", O_CREAT | O_RDWR | O_APPEND, 0600);698CHECK_RIGHT_RESULT(rc, rights, CAP_CREATE, CAP_READ, CAP_WRITE, CAP_LOOKUP);699if (rc >= 0) {700EXPECT_OK(close(rc));701EXPECT_OK(unlinkat(dirfd, "cap_create", 0));702}703704rc = openat(dirfd, "cap_faccess", O_CREAT, 0600);705EXPECT_OK(rc);706EXPECT_OK(close(rc));707rc = faccessat(dfd_cap, "cap_faccess", F_OK, 0);708CHECK_RIGHT_RESULT(rc, rights, CAP_FSTAT, CAP_LOOKUP);709EXPECT_OK(unlinkat(dirfd, "cap_faccess", 0));710711rc = openat(dirfd, "cap_fsync", O_CREAT, 0600);712EXPECT_OK(rc);713EXPECT_OK(close(rc));714rc = openat(dfd_cap, "cap_fsync", O_FSYNC | O_RDONLY);715CHECK_RIGHT_RESULT(rc, rights, CAP_FSYNC, CAP_READ, CAP_LOOKUP);716if (rc >= 0) {717EXPECT_OK(close(rc));718}719rc = openat(dfd_cap, "cap_fsync", O_FSYNC | O_WRONLY | O_APPEND);720CHECK_RIGHT_RESULT(rc, rights, CAP_FSYNC, CAP_WRITE, CAP_LOOKUP);721if (rc >= 0) {722EXPECT_OK(close(rc));723}724rc = openat(dfd_cap, "cap_fsync", O_FSYNC | O_RDWR | O_APPEND);725CHECK_RIGHT_RESULT(rc, rights, CAP_FSYNC, CAP_READ, CAP_WRITE, CAP_LOOKUP);726if (rc >= 0) {727EXPECT_OK(close(rc));728}729rc = openat(dfd_cap, "cap_fsync", O_SYNC | O_RDONLY);730CHECK_RIGHT_RESULT(rc, rights, CAP_FSYNC, CAP_READ, CAP_LOOKUP);731if (rc >= 0) {732EXPECT_OK(close(rc));733}734rc = openat(dfd_cap, "cap_fsync", O_SYNC | O_WRONLY | O_APPEND);735CHECK_RIGHT_RESULT(rc, rights, CAP_FSYNC, CAP_WRITE, CAP_LOOKUP);736if (rc >= 0) {737EXPECT_OK(close(rc));738}739rc = openat(dfd_cap, "cap_fsync", O_SYNC | O_RDWR | O_APPEND);740CHECK_RIGHT_RESULT(rc, rights, CAP_FSYNC, CAP_READ, CAP_WRITE, CAP_LOOKUP);741if (rc >= 0) {742EXPECT_OK(close(rc));743}744EXPECT_OK(unlinkat(dirfd, "cap_fsync", 0));745746rc = openat(dirfd, "cap_ftruncate", O_CREAT, 0600);747EXPECT_OK(rc);748EXPECT_OK(close(rc));749rc = openat(dfd_cap, "cap_ftruncate", O_TRUNC | O_RDONLY);750CHECK_RIGHT_RESULT(rc, rights, CAP_FTRUNCATE, CAP_READ, CAP_LOOKUP);751if (rc >= 0) {752EXPECT_OK(close(rc));753}754rc = openat(dfd_cap, "cap_ftruncate", O_TRUNC | O_WRONLY);755CHECK_RIGHT_RESULT(rc, rights, CAP_FTRUNCATE, CAP_WRITE, CAP_LOOKUP);756if (rc >= 0) {757EXPECT_OK(close(rc));758}759rc = openat(dfd_cap, "cap_ftruncate", O_TRUNC | O_RDWR);760CHECK_RIGHT_RESULT(rc, rights, CAP_FTRUNCATE, CAP_READ, CAP_WRITE, CAP_LOOKUP);761if (rc >= 0) {762EXPECT_OK(close(rc));763}764EXPECT_OK(unlinkat(dirfd, "cap_ftruncate", 0));765766rc = openat(dfd_cap, "cap_create", O_CREAT | O_WRONLY, 0600);767CHECK_RIGHT_RESULT(rc, rights, CAP_CREATE, CAP_WRITE, CAP_SEEK, CAP_LOOKUP);768if (rc >= 0) {769EXPECT_OK(close(rc));770EXPECT_OK(unlinkat(dirfd, "cap_create", 0));771}772rc = openat(dfd_cap, "cap_create", O_CREAT | O_RDWR, 0600);773CHECK_RIGHT_RESULT(rc, rights, CAP_CREATE, CAP_READ, CAP_WRITE, CAP_SEEK, CAP_LOOKUP);774if (rc >= 0) {775EXPECT_OK(close(rc));776EXPECT_OK(unlinkat(dirfd, "cap_create", 0));777}778779rc = openat(dirfd, "cap_fsync", O_CREAT, 0600);780EXPECT_OK(rc);781EXPECT_OK(close(rc));782rc = openat(dfd_cap, "cap_fsync", O_FSYNC | O_WRONLY);783CHECK_RIGHT_RESULT(rc,784rights, CAP_FSYNC, CAP_WRITE, CAP_SEEK, CAP_LOOKUP);785if (rc >= 0) {786EXPECT_OK(close(rc));787}788rc = openat(dfd_cap, "cap_fsync", O_FSYNC | O_RDWR);789CHECK_RIGHT_RESULT(rc,790rights, CAP_FSYNC, CAP_READ, CAP_WRITE, CAP_SEEK, CAP_LOOKUP);791if (rc >= 0) {792EXPECT_OK(close(rc));793}794rc = openat(dfd_cap, "cap_fsync", O_SYNC | O_WRONLY);795CHECK_RIGHT_RESULT(rc,796rights, CAP_FSYNC, CAP_WRITE, CAP_SEEK, CAP_LOOKUP);797if (rc >= 0) {798EXPECT_OK(close(rc));799}800rc = openat(dfd_cap, "cap_fsync", O_SYNC | O_RDWR);801CHECK_RIGHT_RESULT(rc,802rights, CAP_FSYNC, CAP_READ, CAP_WRITE, CAP_SEEK, CAP_LOOKUP);803if (rc >= 0) {804EXPECT_OK(close(rc));805}806EXPECT_OK(unlinkat(dirfd, "cap_fsync", 0));807808#ifdef HAVE_CHFLAGSAT809rc = openat(dirfd, "cap_chflagsat", O_CREAT, 0600);810EXPECT_OK(rc);811EXPECT_OK(close(rc));812rc = chflagsat(dfd_cap, "cap_chflagsat", UF_NODUMP, 0);813CHECK_RIGHT_RESULT(rc, rights, CAP_CHFLAGSAT, CAP_LOOKUP);814EXPECT_OK(unlinkat(dirfd, "cap_chflagsat", 0));815#endif816817rc = openat(dirfd, "cap_fchownat", O_CREAT, 0600);818EXPECT_OK(rc);819EXPECT_OK(close(rc));820rc = fchownat(dfd_cap, "cap_fchownat", -1, -1, 0);821CHECK_RIGHT_RESULT(rc, rights, CAP_FCHOWN, CAP_LOOKUP);822EXPECT_OK(unlinkat(dirfd, "cap_fchownat", 0));823824rc = openat(dirfd, "cap_fchmodat", O_CREAT, 0600);825EXPECT_OK(rc);826EXPECT_OK(close(rc));827rc = fchmodat(dfd_cap, "cap_fchmodat", 0600, 0);828CHECK_RIGHT_RESULT(rc, rights, CAP_FCHMOD, CAP_LOOKUP);829EXPECT_OK(unlinkat(dirfd, "cap_fchmodat", 0));830831rc = openat(dirfd, "cap_fstatat", O_CREAT, 0600);832EXPECT_OK(rc);833EXPECT_OK(close(rc));834struct stat sb;835rc = fstatat(dfd_cap, "cap_fstatat", &sb, 0);836CHECK_RIGHT_RESULT(rc, rights, CAP_FSTAT, CAP_LOOKUP);837EXPECT_OK(unlinkat(dirfd, "cap_fstatat", 0));838839rc = openat(dirfd, "cap_futimesat", O_CREAT, 0600);840EXPECT_OK(rc);841EXPECT_OK(close(rc));842rc = futimesat(dfd_cap, "cap_futimesat", NULL);843CHECK_RIGHT_RESULT(rc, rights, CAP_FUTIMES, CAP_LOOKUP);844EXPECT_OK(unlinkat(dirfd, "cap_futimesat", 0));845846// For linkat(2), need:847// - CAP_LINKAT_SOURCE on source848// - CAP_LINKAT_TARGET on destination.849rc = openat(dirfd, "cap_linkat_src", O_CREAT, 0600);850EXPECT_OK(rc);851EXPECT_OK(close(rc));852853rc = linkat(dirfd, "cap_linkat_src", dfd_cap, "cap_linkat_dst", 0);854CHECK_RIGHT_RESULT(rc, rights, CAP_LINKAT_TARGET);855if (rc >= 0) {856EXPECT_OK(unlinkat(dirfd, "cap_linkat_dst", 0));857}858859rc = linkat(dfd_cap, "cap_linkat_src", dirfd, "cap_linkat_dst", 0);860CHECK_RIGHT_RESULT(rc, rights, CAP_LINKAT_SOURCE);861if (rc >= 0) {862EXPECT_OK(unlinkat(dirfd, "cap_linkat_dst", 0));863}864865EXPECT_OK(unlinkat(dirfd, "cap_linkat_src", 0));866867rc = mkdirat(dfd_cap, "cap_mkdirat", 0700);868CHECK_RIGHT_RESULT(rc, rights, CAP_MKDIRAT, CAP_LOOKUP);869if (rc >= 0) {870EXPECT_OK(unlinkat(dirfd, "cap_mkdirat", AT_REMOVEDIR));871}872873#ifdef HAVE_MKFIFOAT874rc = mkfifoat(dfd_cap, "cap_mkfifoat", 0600);875CHECK_RIGHT_RESULT(rc, rights, CAP_MKFIFOAT, CAP_LOOKUP);876if (rc >= 0) {877EXPECT_OK(unlinkat(dirfd, "cap_mkfifoat", 0));878}879#endif880881if (getuid() == 0) {882rc = mknodat(dfd_cap, "cap_mknodat", S_IFCHR | 0600, 0);883CHECK_RIGHT_RESULT(rc, rights, CAP_MKNODAT, CAP_LOOKUP);884if (rc >= 0) {885EXPECT_OK(unlinkat(dirfd, "cap_mknodat", 0));886}887}888889// For renameat(2), need:890// - CAP_RENAMEAT_SOURCE on source891// - CAP_RENAMEAT_TARGET on destination.892rc = openat(dirfd, "cap_renameat_src", O_CREAT, 0600);893EXPECT_OK(rc);894EXPECT_OK(close(rc));895896rc = renameat(dirfd, "cap_renameat_src", dfd_cap, "cap_renameat_dst");897CHECK_RIGHT_RESULT(rc, rights, CAP_RENAMEAT_TARGET);898if (rc >= 0) {899EXPECT_OK(unlinkat(dirfd, "cap_renameat_dst", 0));900} else {901EXPECT_OK(unlinkat(dirfd, "cap_renameat_src", 0));902}903904rc = openat(dirfd, "cap_renameat_src", O_CREAT, 0600);905EXPECT_OK(rc);906EXPECT_OK(close(rc));907908rc = renameat(dfd_cap, "cap_renameat_src", dirfd, "cap_renameat_dst");909CHECK_RIGHT_RESULT(rc, rights, CAP_RENAMEAT_SOURCE);910911if (rc >= 0) {912EXPECT_OK(unlinkat(dirfd, "cap_renameat_dst", 0));913} else {914EXPECT_OK(unlinkat(dirfd, "cap_renameat_src", 0));915}916917rc = symlinkat("test", dfd_cap, "cap_symlinkat");918CHECK_RIGHT_RESULT(rc, rights, CAP_SYMLINKAT, CAP_LOOKUP);919if (rc >= 0) {920EXPECT_OK(unlinkat(dirfd, "cap_symlinkat", 0));921}922923rc = openat(dirfd, "cap_unlinkat", O_CREAT, 0600);924EXPECT_OK(rc);925EXPECT_OK(close(rc));926rc = unlinkat(dfd_cap, "cap_unlinkat", 0);927CHECK_RIGHT_RESULT(rc, rights, CAP_UNLINKAT, CAP_LOOKUP);928unlinkat(dirfd, "cap_unlinkat", 0);929EXPECT_OK(mkdirat(dirfd, "cap_unlinkat", 0700));930rc = unlinkat(dfd_cap, "cap_unlinkat", AT_REMOVEDIR);931CHECK_RIGHT_RESULT(rc, rights, CAP_UNLINKAT, CAP_LOOKUP);932unlinkat(dirfd, "cap_unlinkat", AT_REMOVEDIR);933934EXPECT_OK(close(dfd_cap));935}936937void DirOperationsTest(int extra) {938int rc = mkdir(TmpFile("cap_dirops"), 0755);939EXPECT_OK(rc);940if (rc < 0 && errno != EEXIST) return;941int dfd = open(TmpFile("cap_dirops"), O_RDONLY | O_DIRECTORY | extra);942EXPECT_OK(dfd);943int tmpfd = open(tmpdir.c_str(), O_RDONLY | O_DIRECTORY);944EXPECT_OK(tmpfd);945946EXPECT_OK(cap_enter()); // Enter capability mode.947948TRY_DIR_OPS(dfd, CAP_LINKAT_SOURCE);949TRY_DIR_OPS(dfd, CAP_LINKAT_TARGET);950TRY_DIR_OPS(dfd, CAP_CREATE, CAP_READ, CAP_LOOKUP);951TRY_DIR_OPS(dfd, CAP_CREATE, CAP_WRITE, CAP_LOOKUP);952TRY_DIR_OPS(dfd, CAP_CREATE, CAP_READ, CAP_WRITE, CAP_LOOKUP);953TRY_DIR_OPS(dfd, CAP_FSYNC, CAP_READ, CAP_LOOKUP);954TRY_DIR_OPS(dfd, CAP_FSYNC, CAP_WRITE, CAP_LOOKUP);955TRY_DIR_OPS(dfd, CAP_FSYNC, CAP_READ, CAP_WRITE, CAP_LOOKUP);956TRY_DIR_OPS(dfd, CAP_FTRUNCATE, CAP_READ, CAP_LOOKUP);957TRY_DIR_OPS(dfd, CAP_FTRUNCATE, CAP_WRITE, CAP_LOOKUP);958TRY_DIR_OPS(dfd, CAP_FTRUNCATE, CAP_READ, CAP_WRITE, CAP_LOOKUP);959TRY_DIR_OPS(dfd, CAP_FCHOWN, CAP_LOOKUP);960TRY_DIR_OPS(dfd, CAP_FCHMOD, CAP_LOOKUP);961TRY_DIR_OPS(dfd, CAP_FSTAT, CAP_LOOKUP);962TRY_DIR_OPS(dfd, CAP_FUTIMES, CAP_LOOKUP);963TRY_DIR_OPS(dfd, CAP_MKDIRAT, CAP_LOOKUP);964TRY_DIR_OPS(dfd, CAP_MKFIFOAT, CAP_LOOKUP);965TRY_DIR_OPS(dfd, CAP_MKNODAT, CAP_LOOKUP);966TRY_DIR_OPS(dfd, CAP_SYMLINKAT, CAP_LOOKUP);967TRY_DIR_OPS(dfd, CAP_UNLINKAT, CAP_LOOKUP);968// Rename needs CAP_RENAMEAT_SOURCE on source directory and969// CAP_RENAMEAT_TARGET on destination directory.970TRY_DIR_OPS(dfd, CAP_RENAMEAT_SOURCE, CAP_UNLINKAT, CAP_LOOKUP);971TRY_DIR_OPS(dfd, CAP_RENAMEAT_TARGET, CAP_UNLINKAT, CAP_LOOKUP);972973EXPECT_OK(unlinkat(tmpfd, "cap_dirops", AT_REMOVEDIR));974EXPECT_OK(close(tmpfd));975EXPECT_OK(close(dfd));976}977978FORK_TEST(Capability, DirOperations) {979DirOperationsTest(0);980}981982#ifdef O_PATH983FORK_TEST(Capability, PathDirOperations) {984// Make the dfd in the test a path-only file descriptor.985DirOperationsTest(O_PATH);986}987#endif988989static void TryReadWrite(int cap_fd) {990char buffer[64];991EXPECT_OK(read(cap_fd, buffer, sizeof(buffer)));992int rc = write(cap_fd, "", 0);993EXPECT_EQ(-1, rc);994EXPECT_EQ(ENOTCAPABLE, errno);995}996997FORK_TEST_ON(Capability, SocketTransfer, TmpFile("cap_fd_transfer")) {998int sock_fds[2];999EXPECT_OK(socketpair(AF_UNIX, SOCK_STREAM, 0, sock_fds));10001001struct msghdr mh;1002mh.msg_name = NULL; // No address needed1003mh.msg_namelen = 0;1004char buffer1[1024];1005struct iovec iov[1];1006iov[0].iov_base = buffer1;1007iov[0].iov_len = sizeof(buffer1);1008mh.msg_iov = iov;1009mh.msg_iovlen = 1;1010char buffer2[1024];1011mh.msg_control = buffer2;1012mh.msg_controllen = sizeof(buffer2);1013struct cmsghdr *cmptr;10141015cap_rights_t r_rs;1016cap_rights_init(&r_rs, CAP_READ, CAP_SEEK);10171018pid_t child = fork();1019if (child == 0) {1020// Child: enter cap mode1021EXPECT_OK(cap_enter());1022// Child: send startup notification1023SEND_INT_MESSAGE(sock_fds[0], MSG_CHILD_STARTED);10241025// Child: wait to receive FD over socket1026int rc = recvmsg(sock_fds[0], &mh, 0);1027EXPECT_OK(rc);1028EXPECT_LE(CMSG_LEN(sizeof(int)), mh.msg_controllen);1029cmptr = CMSG_FIRSTHDR(&mh);1030int cap_fd = *(int*)CMSG_DATA(cmptr);1031EXPECT_EQ(CMSG_LEN(sizeof(int)), cmptr->cmsg_len);1032cmptr = CMSG_NXTHDR(&mh, cmptr);1033EXPECT_TRUE(cmptr == NULL);10341035// Child: confirm we can do the right operations on the capability1036cap_rights_t rights;1037EXPECT_OK(cap_rights_get(cap_fd, &rights));1038EXPECT_RIGHTS_EQ(&r_rs, &rights);1039TryReadWrite(cap_fd);10401041// Child: acknowledge that we have received and tested the file descriptor1042SEND_INT_MESSAGE(sock_fds[0], MSG_CHILD_FD_RECEIVED);10431044// Child: wait for a normal read1045AWAIT_INT_MESSAGE(sock_fds[0], MSG_PARENT_REQUEST_CHILD_EXIT);1046exit(testing::Test::HasFailure());1047}10481049int fd = open(TmpFile("cap_fd_transfer"), O_RDWR | O_CREAT, 0644);1050EXPECT_OK(fd);1051if (fd < 0) return;1052int cap_fd = dup(fd);1053EXPECT_OK(cap_fd);1054EXPECT_OK(cap_rights_limit(cap_fd, &r_rs));10551056EXPECT_OK(cap_enter()); // Enter capability mode.10571058// Confirm we can do the right operations on the capability1059TryReadWrite(cap_fd);10601061// Wait for child to start up:1062AWAIT_INT_MESSAGE(sock_fds[1], MSG_CHILD_STARTED);10631064// Send the file descriptor over the pipe to the sub-process1065mh.msg_controllen = CMSG_LEN(sizeof(int));1066cmptr = CMSG_FIRSTHDR(&mh);1067cmptr->cmsg_level = SOL_SOCKET;1068cmptr->cmsg_type = SCM_RIGHTS;1069cmptr->cmsg_len = CMSG_LEN(sizeof(int));1070*(int *)CMSG_DATA(cmptr) = cap_fd;1071buffer1[0] = 0;1072iov[0].iov_len = 1;1073int rc = sendmsg(sock_fds[1], &mh, 0);1074EXPECT_OK(rc);10751076// Check that the child received the message1077AWAIT_INT_MESSAGE(sock_fds[1], MSG_CHILD_FD_RECEIVED);10781079// Tell the child to exit1080SEND_INT_MESSAGE(sock_fds[1], MSG_PARENT_REQUEST_CHILD_EXIT);1081}10821083TEST(Capability, SyscallAt) {1084int rc = mkdir(TmpFile("cap_at_topdir"), 0755);1085EXPECT_OK(rc);1086if (rc < 0 && errno != EEXIST) return;10871088cap_rights_t r_all;1089cap_rights_init(&r_all, CAP_READ, CAP_LOOKUP, CAP_MKNODAT, CAP_UNLINKAT, CAP_MKDIRAT, CAP_MKFIFOAT);1090cap_rights_t r_no_unlink;1091cap_rights_init(&r_no_unlink, CAP_READ, CAP_LOOKUP, CAP_MKDIRAT, CAP_MKFIFOAT);1092cap_rights_t r_no_mkdir;1093cap_rights_init(&r_no_mkdir, CAP_READ, CAP_LOOKUP, CAP_UNLINKAT, CAP_MKFIFOAT);1094cap_rights_t r_no_mkfifo;1095cap_rights_init(&r_no_mkfifo, CAP_READ, CAP_LOOKUP, CAP_UNLINKAT, CAP_MKDIRAT);1096cap_rights_t r_create;1097cap_rights_init(&r_create, CAP_READ, CAP_LOOKUP, CAP_CREATE);1098cap_rights_t r_bind;1099cap_rights_init(&r_bind, CAP_READ, CAP_LOOKUP, CAP_BIND);11001101int dfd = open(TmpFile("cap_at_topdir"), O_RDONLY);1102EXPECT_OK(dfd);1103int cap_dfd_all = dup(dfd);1104EXPECT_OK(cap_dfd_all);1105EXPECT_OK(cap_rights_limit(cap_dfd_all, &r_all));1106int cap_dfd_no_unlink = dup(dfd);1107EXPECT_OK(cap_dfd_no_unlink);1108EXPECT_OK(cap_rights_limit(cap_dfd_no_unlink, &r_no_unlink));1109int cap_dfd_no_mkdir = dup(dfd);1110EXPECT_OK(cap_dfd_no_mkdir);1111EXPECT_OK(cap_rights_limit(cap_dfd_no_mkdir, &r_no_mkdir));1112int cap_dfd_no_mkfifo = dup(dfd);1113EXPECT_OK(cap_dfd_no_mkfifo);1114EXPECT_OK(cap_rights_limit(cap_dfd_no_mkfifo, &r_no_mkfifo));1115int cap_dfd_create = dup(dfd);1116EXPECT_OK(cap_dfd_create);1117EXPECT_OK(cap_rights_limit(cap_dfd_create, &r_create));1118int cap_dfd_bind = dup(dfd);1119EXPECT_OK(cap_dfd_bind);1120EXPECT_OK(cap_rights_limit(cap_dfd_bind, &r_bind));11211122// Need CAP_MKDIRAT to mkdirat(2).1123EXPECT_NOTCAPABLE(mkdirat(cap_dfd_no_mkdir, "cap_subdir", 0755));1124rmdir(TmpFile("cap_at_topdir/cap_subdir"));1125EXPECT_OK(mkdirat(cap_dfd_all, "cap_subdir", 0755));11261127// Need CAP_UNLINKAT to unlinkat(dfd, name, AT_REMOVEDIR).1128EXPECT_NOTCAPABLE(unlinkat(cap_dfd_no_unlink, "cap_subdir", AT_REMOVEDIR));1129EXPECT_OK(unlinkat(cap_dfd_all, "cap_subdir", AT_REMOVEDIR));1130rmdir(TmpFile("cap_at_topdir/cap_subdir"));11311132// Need CAP_MKFIFOAT to mkfifoat(2).1133EXPECT_NOTCAPABLE(mkfifoat(cap_dfd_no_mkfifo, "cap_fifo", 0755));1134unlink(TmpFile("cap_at_topdir/cap_fifo"));1135EXPECT_OK(mkfifoat(cap_dfd_all, "cap_fifo", 0755));1136unlink(TmpFile("cap_at_topdir/cap_fifo"));11371138#ifdef HAVE_MKNOD_REG1139// Need CAP_CREATE to create a regular file with mknodat(2).1140EXPECT_NOTCAPABLE(mknodat(cap_dfd_all, "cap_regular", S_IFREG|0755, 0));1141unlink(TmpFile("cap_at_topdir/cap_regular"));1142EXPECT_OK(mknodat(cap_dfd_create, "cap_regular", S_IFREG|0755, 0));1143unlink(TmpFile("cap_at_topdir/cap_regular"));1144#endif11451146#ifdef HAVE_MKNOD_SOCKET1147// Need CAP_BIND to create a UNIX domain socket with mknodat(2).1148EXPECT_NOTCAPABLE(mknodat(cap_dfd_all, "cap_socket", S_IFSOCK|0755, 0));1149unlink(TmpFile("cap_at_topdir/cap_socket"));1150EXPECT_OK(mknodat(cap_dfd_bind, "cap_socket", S_IFSOCK|0755, 0));1151unlink(TmpFile("cap_at_topdir/cap_socket"));1152#endif11531154close(cap_dfd_all);1155close(cap_dfd_no_mkfifo);1156close(cap_dfd_no_mkdir);1157close(cap_dfd_no_unlink);1158close(cap_dfd_create);1159close(cap_dfd_bind);1160close(dfd);11611162// Tidy up.1163rmdir(TmpFile("cap_at_topdir"));1164}11651166TEST(Capability, SyscallAtIfRoot) {1167GTEST_SKIP_IF_NOT_ROOT();1168int rc = mkdir(TmpFile("cap_at_topdir"), 0755);1169EXPECT_OK(rc);1170if (rc < 0 && errno != EEXIST) return;11711172cap_rights_t r_all;1173cap_rights_init(&r_all, CAP_READ, CAP_LOOKUP, CAP_MKNODAT, CAP_UNLINKAT, CAP_MKDIRAT, CAP_MKFIFOAT);1174cap_rights_t r_no_mkfifo;1175cap_rights_init(&r_no_mkfifo, CAP_READ, CAP_LOOKUP, CAP_UNLINKAT, CAP_MKDIRAT);1176cap_rights_t r_no_mknod;1177cap_rights_init(&r_no_mknod, CAP_READ, CAP_LOOKUP, CAP_UNLINKAT, CAP_MKDIRAT);11781179int dfd = open(TmpFile("cap_at_topdir"), O_RDONLY);1180EXPECT_OK(dfd);1181int cap_dfd_all = dup(dfd);1182EXPECT_OK(cap_dfd_all);1183EXPECT_OK(cap_rights_limit(cap_dfd_all, &r_all));1184int cap_dfd_no_mkfifo = dup(dfd);1185EXPECT_OK(cap_dfd_no_mkfifo);1186EXPECT_OK(cap_rights_limit(cap_dfd_no_mkfifo, &r_no_mkfifo));1187int cap_dfd_no_mknod = dup(dfd);1188EXPECT_OK(cap_dfd_no_mknod);1189EXPECT_OK(cap_rights_limit(cap_dfd_no_mknod, &r_no_mknod));11901191// Need CAP_MKNODAT to mknodat(2) a device1192EXPECT_NOTCAPABLE(mknodat(cap_dfd_no_mknod, "cap_device", S_IFCHR|0755, makedev(99, 123)));1193unlink(TmpFile("cap_at_topdir/cap_device"));1194EXPECT_OK(mknodat(cap_dfd_all, "cap_device", S_IFCHR|0755, makedev(99, 123)));1195unlink(TmpFile("cap_at_topdir/cap_device"));11961197// Need CAP_MKFIFOAT to mknodat(2) for a FIFO.1198EXPECT_NOTCAPABLE(mknodat(cap_dfd_no_mkfifo, "cap_fifo", S_IFIFO|0755, 0));1199unlink(TmpFile("cap_at_topdir/cap_fifo"));1200EXPECT_OK(mknodat(cap_dfd_all, "cap_fifo", S_IFIFO|0755, 0));1201unlink(TmpFile("cap_at_topdir/cap_fifo"));12021203close(cap_dfd_all);1204close(cap_dfd_no_mknod);1205close(cap_dfd_no_mkfifo);1206close(dfd);12071208// Tidy up.1209rmdir(TmpFile("cap_at_topdir"));1210}12111212FORK_TEST_ON(Capability, ExtendedAttributesIfAvailable, TmpFile("cap_extattr")) {1213int fd = open(TmpFile("cap_extattr"), O_RDONLY|O_CREAT, 0644);1214EXPECT_OK(fd);12151216char buffer[1024];1217int rc = fgetxattr_(fd, "user.capsicumtest", buffer, sizeof(buffer));1218if (rc < 0 && errno == ENOTSUP) {1219// Need user_xattr mount option for non-root users on Linux1220close(fd);1221GTEST_SKIP() << "/tmp doesn't support extended attributes";1222}12231224cap_rights_t r_rws;1225cap_rights_init(&r_rws, CAP_READ, CAP_WRITE, CAP_SEEK);1226cap_rights_t r_xlist;1227cap_rights_init(&r_xlist, CAP_EXTATTR_LIST);1228cap_rights_t r_xget;1229cap_rights_init(&r_xget, CAP_EXTATTR_GET);1230cap_rights_t r_xset;1231cap_rights_init(&r_xset, CAP_EXTATTR_SET);1232cap_rights_t r_xdel;1233cap_rights_init(&r_xdel, CAP_EXTATTR_DELETE);12341235int cap = dup(fd);1236EXPECT_OK(cap);1237EXPECT_OK(cap_rights_limit(cap, &r_rws));1238int cap_xlist = dup(fd);1239EXPECT_OK(cap_xlist);1240EXPECT_OK(cap_rights_limit(cap_xlist, &r_xlist));1241int cap_xget = dup(fd);1242EXPECT_OK(cap_xget);1243EXPECT_OK(cap_rights_limit(cap_xget, &r_xget));1244int cap_xset = dup(fd);1245EXPECT_OK(cap_xset);1246EXPECT_OK(cap_rights_limit(cap_xset, &r_xset));1247int cap_xdel = dup(fd);1248EXPECT_OK(cap_xdel);1249EXPECT_OK(cap_rights_limit(cap_xdel, &r_xdel));12501251const char* value = "capsicum";1252int len = strlen(value) + 1;1253EXPECT_NOTCAPABLE(fsetxattr_(cap, "user.capsicumtest", value, len, 0));1254EXPECT_NOTCAPABLE(fsetxattr_(cap_xlist, "user.capsicumtest", value, len, 0));1255EXPECT_NOTCAPABLE(fsetxattr_(cap_xget, "user.capsicumtest", value, len, 0));1256EXPECT_NOTCAPABLE(fsetxattr_(cap_xdel, "user.capsicumtest", value, len, 0));1257EXPECT_OK(fsetxattr_(cap_xset, "user.capsicumtest", value, len, 0));12581259EXPECT_NOTCAPABLE(flistxattr_(cap, buffer, sizeof(buffer)));1260EXPECT_NOTCAPABLE(flistxattr_(cap_xget, buffer, sizeof(buffer)));1261EXPECT_NOTCAPABLE(flistxattr_(cap_xset, buffer, sizeof(buffer)));1262EXPECT_NOTCAPABLE(flistxattr_(cap_xdel, buffer, sizeof(buffer)));1263EXPECT_OK(flistxattr_(cap_xlist, buffer, sizeof(buffer)));12641265EXPECT_NOTCAPABLE(fgetxattr_(cap, "user.capsicumtest", buffer, sizeof(buffer)));1266EXPECT_NOTCAPABLE(fgetxattr_(cap_xlist, "user.capsicumtest", buffer, sizeof(buffer)));1267EXPECT_NOTCAPABLE(fgetxattr_(cap_xset, "user.capsicumtest", buffer, sizeof(buffer)));1268EXPECT_NOTCAPABLE(fgetxattr_(cap_xdel, "user.capsicumtest", buffer, sizeof(buffer)));1269EXPECT_OK(fgetxattr_(cap_xget, "user.capsicumtest", buffer, sizeof(buffer)));12701271EXPECT_NOTCAPABLE(fremovexattr_(cap, "user.capsicumtest"));1272EXPECT_NOTCAPABLE(fremovexattr_(cap_xlist, "user.capsicumtest"));1273EXPECT_NOTCAPABLE(fremovexattr_(cap_xget, "user.capsicumtest"));1274EXPECT_NOTCAPABLE(fremovexattr_(cap_xset, "user.capsicumtest"));1275EXPECT_OK(fremovexattr_(cap_xdel, "user.capsicumtest"));12761277close(cap_xdel);1278close(cap_xset);1279close(cap_xget);1280close(cap_xlist);1281close(cap);1282close(fd);1283}12841285TEST(Capability, PipeUnseekable) {1286int fds[2];1287EXPECT_OK(pipe(fds));12881289// Some programs detect pipes by calling seek() and getting ESPIPE.1290EXPECT_EQ(-1, lseek(fds[0], 0, SEEK_SET));1291EXPECT_EQ(ESPIPE, errno);12921293cap_rights_t rights;1294cap_rights_init(&rights, CAP_READ, CAP_WRITE, CAP_SEEK);1295EXPECT_OK(cap_rights_limit(fds[0], &rights));12961297EXPECT_EQ(-1, lseek(fds[0], 0, SEEK_SET));1298EXPECT_EQ(ESPIPE, errno);12991300// Remove CAP_SEEK and see if ENOTCAPABLE trumps ESPIPE.1301cap_rights_init(&rights, CAP_READ, CAP_WRITE);1302EXPECT_OK(cap_rights_limit(fds[0], &rights));1303EXPECT_EQ(-1, lseek(fds[0], 0, SEEK_SET));1304EXPECT_EQ(ENOTCAPABLE, errno);1305// TODO(drysdale): in practical terms it might be nice if ESPIPE trumped ENOTCAPABLE.1306// EXPECT_EQ(ESPIPE, errno);13071308close(fds[0]);1309close(fds[1]);1310}13111312TEST(Capability, NoBypassDACIfRoot) {1313GTEST_SKIP_IF_NOT_ROOT();1314int fd = open(TmpFile("cap_root_owned"), O_RDONLY|O_CREAT, 0644);1315EXPECT_OK(fd);1316cap_rights_t rights;1317cap_rights_init(&rights, CAP_READ, CAP_WRITE, CAP_FCHMOD, CAP_FSTAT);1318EXPECT_OK(cap_rights_limit(fd, &rights));13191320pid_t child = fork();1321if (child == 0) {1322// Child: change uid to a lesser being1323ASSERT_NE(0u, other_uid) << "other_uid not initialized correctly, "1324"please pass the -u <uid> flag.";1325EXPECT_EQ(0, setuid(other_uid));1326EXPECT_EQ(other_uid, getuid());1327// Attempt to fchmod the file, and fail.1328// Having CAP_FCHMOD doesn't bypass the need to comply with DAC policy.1329int rc = fchmod(fd, 0666);1330EXPECT_EQ(-1, rc);1331EXPECT_EQ(EPERM, errno);1332exit(HasFailure());1333}1334int status;1335EXPECT_EQ(child, waitpid(child, &status, 0));1336EXPECT_TRUE(WIFEXITED(status)) << "0x" << std::hex << status;1337EXPECT_EQ(0, WEXITSTATUS(status));1338struct stat info;1339EXPECT_OK(fstat(fd, &info));1340EXPECT_EQ((mode_t)(S_IFREG|0644), info.st_mode);1341close(fd);1342unlink(TmpFile("cap_root_owned"));1343}13441345TEST(Capability, CheckIsEmpty) {1346cap_rights_t rights;13471348cap_rights_init(&rights);1349EXPECT_TRUE(cap_rights_is_empty(&rights));13501351size_t num_known = (sizeof(known_rights)/sizeof(known_rights[0]));1352for (size_t ii = 0; ii < num_known; ii++) {1353cap_rights_init(&rights, known_rights[ii].right);1354EXPECT_FALSE(cap_rights_is_empty(&rights));1355cap_rights_clear(&rights, known_rights[ii].right);1356EXPECT_TRUE(cap_rights_is_empty(&rights));1357}1358}135913601361