cap_test_capabilities.c revision 248394
1224653Sjonathan/*- 2224653Sjonathan * Copyright (c) 2009-2011 Robert N. M. Watson 3224653Sjonathan * Copyright (c) 2011 Jonathan Anderson 4247605Spjd * Copyright (c) 2012 FreeBSD Foundation 5224653Sjonathan * All rights reserved. 6224653Sjonathan * 7247605Spjd * Portions of this software were developed by Pawel Jakub Dawidek under 8247605Spjd * sponsorship from the FreeBSD Foundation. 9247605Spjd * 10224653Sjonathan * Redistribution and use in source and binary forms, with or without 11224653Sjonathan * modification, are permitted provided that the following conditions 12224653Sjonathan * are met: 13224653Sjonathan * 1. Redistributions of source code must retain the above copyright 14224653Sjonathan * notice, this list of conditions and the following disclaimer. 15224653Sjonathan * 2. Redistributions in binary form must reproduce the above copyright 16224653Sjonathan * notice, this list of conditions and the following disclaimer in the 17224653Sjonathan * documentation and/or other materials provided with the distribution. 18224653Sjonathan * 19224653Sjonathan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20224653Sjonathan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21224653Sjonathan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22224653Sjonathan * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23224653Sjonathan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24224653Sjonathan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25224653Sjonathan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26224653Sjonathan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27224653Sjonathan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28224653Sjonathan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29224653Sjonathan * SUCH DAMAGE. 30224653Sjonathan */ 31224653Sjonathan 32224653Sjonathan/* 33224653Sjonathan * Test whether various operations on capabilities are properly masked for 34224653Sjonathan * various object types. 35224653Sjonathan */ 36224653Sjonathan 37224653Sjonathan#include <sys/cdefs.h> 38224653Sjonathan__FBSDID("$FreeBSD: head/tools/regression/security/cap_test/cap_test_capabilities.c 248394 2013-03-16 23:10:40Z pjd $"); 39224653Sjonathan 40224653Sjonathan#include <sys/param.h> 41224653Sjonathan#include <sys/capability.h> 42224653Sjonathan#include <sys/errno.h> 43224653Sjonathan#include <sys/mman.h> 44224653Sjonathan#include <sys/mount.h> 45224653Sjonathan#include <sys/stat.h> 46224653Sjonathan 47224653Sjonathan#include <err.h> 48224653Sjonathan#include <fcntl.h> 49224910Sjonathan#include <poll.h> 50247605Spjd#include <stdint.h> 51224653Sjonathan#include <stdio.h> 52224653Sjonathan#include <stdlib.h> 53224660Sjonathan#include <string.h> 54224653Sjonathan#include <unistd.h> 55224653Sjonathan 56224653Sjonathan#include "cap_test.h" 57224653Sjonathan 58224653Sjonathan#define SYSCALL_FAIL(syscall, message) \ 59224653Sjonathan FAIL("%s:\t%s (rights 0x%jx)", #syscall, message, rights) 60224653Sjonathan 61224653Sjonathan/* 62224653Sjonathan * Ensure that, if the capability had enough rights for the system call to 63224653Sjonathan * pass, then it did. Otherwise, ensure that the errno is ENOTCAPABLE; 64224653Sjonathan * capability restrictions should kick in before any other error logic. 65224653Sjonathan */ 66224653Sjonathan#define CHECK_RESULT(syscall, rights_needed, succeeded) do { \ 67224653Sjonathan if ((rights & (rights_needed)) == (rights_needed)) { \ 68247605Spjd if (succeeded) { \ 69247605Spjd if (success == -1) \ 70247605Spjd success = PASSED; \ 71247605Spjd } else { \ 72224653Sjonathan SYSCALL_FAIL(syscall, "failed"); \ 73247605Spjd } \ 74224653Sjonathan } else { \ 75247605Spjd if (succeeded) { \ 76224653Sjonathan FAILX("%s:\tsucceeded when it shouldn't have" \ 77247605Spjd " (rights 0x%jx)", #syscall, \ 78247605Spjd (uintmax_t)rights); \ 79247605Spjd } else if (errno != ENOTCAPABLE) { \ 80224653Sjonathan SYSCALL_FAIL(syscall, "errno != ENOTCAPABLE"); \ 81247605Spjd } \ 82224653Sjonathan } \ 83224910Sjonathan errno = 0; \ 84224653Sjonathan} while (0) 85224653Sjonathan 86224653Sjonathan/* 87224653Sjonathan * As above, but for the special mmap() case: unmap after successful mmap(). 88224653Sjonathan */ 89224653Sjonathan#define CHECK_MMAP_RESULT(rights_needed) do { \ 90224653Sjonathan if ((rights & (rights_needed)) == (rights_needed)) { \ 91224653Sjonathan if (p == MAP_FAILED) \ 92224653Sjonathan SYSCALL_FAIL(mmap, "failed"); \ 93247605Spjd else { \ 94224653Sjonathan (void)munmap(p, getpagesize()); \ 95247605Spjd if (success == -1) \ 96247605Spjd success = PASSED; \ 97247605Spjd } \ 98224653Sjonathan } else { \ 99224653Sjonathan if (p != MAP_FAILED) { \ 100224653Sjonathan FAILX("%s:\tsucceeded when it shouldn't have" \ 101224653Sjonathan " (rights 0x%jx)", "mmap", rights); \ 102224653Sjonathan (void)munmap(p, getpagesize()); \ 103224653Sjonathan } else if (errno != ENOTCAPABLE) \ 104224653Sjonathan SYSCALL_FAIL(syscall, "errno != ENOTCAPABLE"); \ 105224653Sjonathan } \ 106224910Sjonathan errno = 0; \ 107224653Sjonathan} while (0) 108224653Sjonathan 109224653Sjonathan/* 110224653Sjonathan * Given a file descriptor, create a capability with specific rights and 111224653Sjonathan * make sure only those rights work. 112224653Sjonathan*/ 113224653Sjonathanstatic int 114247605Spjdtry_file_ops(int filefd, int dirfd, cap_rights_t rights) 115224653Sjonathan{ 116224653Sjonathan struct stat sb; 117224653Sjonathan struct statfs sf; 118247605Spjd cap_rights_t erights; 119247605Spjd int fd_cap, fd_capcap, dfd_cap; 120224653Sjonathan ssize_t ssize, ssize2; 121224653Sjonathan off_t off; 122224653Sjonathan void *p; 123224653Sjonathan char ch; 124224660Sjonathan int ret, is_nfs; 125224910Sjonathan struct pollfd pollfd; 126247605Spjd int success = -1; 127224653Sjonathan 128247605Spjd REQUIRE(fstatfs(filefd, &sf)); 129247605Spjd is_nfs = (strcmp("nfs", sf.f_fstypename) == 0); 130224660Sjonathan 131247605Spjd REQUIRE(fd_cap = cap_new(filefd, rights)); 132247605Spjd CHECK(cap_getrights(fd_cap, &erights) == 0); 133247605Spjd CHECK(rights == erights); 134224653Sjonathan REQUIRE(fd_capcap = cap_new(fd_cap, rights)); 135247605Spjd CHECK(cap_getrights(fd_capcap, &erights) == 0); 136247605Spjd CHECK(rights == erights); 137224653Sjonathan CHECK(fd_capcap != fd_cap); 138247605Spjd REQUIRE(dfd_cap = cap_new(dirfd, rights)); 139247605Spjd CHECK(cap_getrights(dfd_cap, &erights) == 0); 140247605Spjd CHECK(rights == erights); 141224653Sjonathan 142224653Sjonathan ssize = read(fd_cap, &ch, sizeof(ch)); 143247605Spjd CHECK_RESULT(read, CAP_READ, ssize >= 0); 144224653Sjonathan 145247605Spjd ssize = write(fd_cap, &ch, sizeof(ch)); 146247605Spjd CHECK_RESULT(write, CAP_WRITE, ssize >= 0); 147247605Spjd 148247605Spjd off = lseek(fd_cap, 0, SEEK_SET); 149247605Spjd CHECK_RESULT(lseek, CAP_SEEK, off >= 0); 150247605Spjd 151224653Sjonathan ssize = pread(fd_cap, &ch, sizeof(ch), 0); 152224653Sjonathan ssize2 = pread(fd_cap, &ch, sizeof(ch), 0); 153247605Spjd CHECK_RESULT(pread, CAP_PREAD, ssize >= 0); 154224653Sjonathan CHECK(ssize == ssize2); 155224653Sjonathan 156224653Sjonathan ssize = pwrite(fd_cap, &ch, sizeof(ch), 0); 157247605Spjd CHECK_RESULT(pwrite, CAP_PWRITE, ssize >= 0); 158224653Sjonathan 159247605Spjd p = mmap(NULL, getpagesize(), PROT_NONE, MAP_SHARED, fd_cap, 0); 160247605Spjd CHECK_MMAP_RESULT(CAP_MMAP); 161224653Sjonathan 162224653Sjonathan p = mmap(NULL, getpagesize(), PROT_READ, MAP_SHARED, fd_cap, 0); 163247605Spjd CHECK_MMAP_RESULT(CAP_MMAP_R); 164224653Sjonathan 165224653Sjonathan p = mmap(NULL, getpagesize(), PROT_WRITE, MAP_SHARED, fd_cap, 0); 166247605Spjd CHECK_MMAP_RESULT(CAP_MMAP_W); 167224653Sjonathan 168224653Sjonathan p = mmap(NULL, getpagesize(), PROT_EXEC, MAP_SHARED, fd_cap, 0); 169247605Spjd CHECK_MMAP_RESULT(CAP_MMAP_X); 170224653Sjonathan 171224653Sjonathan p = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED, 172224653Sjonathan fd_cap, 0); 173247605Spjd CHECK_MMAP_RESULT(CAP_MMAP_RW); 174224653Sjonathan 175224653Sjonathan p = mmap(NULL, getpagesize(), PROT_READ | PROT_EXEC, MAP_SHARED, 176224653Sjonathan fd_cap, 0); 177247605Spjd CHECK_MMAP_RESULT(CAP_MMAP_RX); 178224653Sjonathan 179224653Sjonathan p = mmap(NULL, getpagesize(), PROT_EXEC | PROT_WRITE, MAP_SHARED, 180224653Sjonathan fd_cap, 0); 181247605Spjd CHECK_MMAP_RESULT(CAP_MMAP_WX); 182224653Sjonathan 183224653Sjonathan p = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE | PROT_EXEC, 184224653Sjonathan MAP_SHARED, fd_cap, 0); 185247605Spjd CHECK_MMAP_RESULT(CAP_MMAP_RWX); 186224653Sjonathan 187247605Spjd /* TODO: openat(O_APPEND) */ 188247605Spjd ret = openat(dfd_cap, "cap_create", O_CREAT | O_RDONLY, 0600); 189247605Spjd CHECK_RESULT(openat(O_CREATE | O_RDONLY), 190247605Spjd CAP_CREATE | CAP_READ | CAP_LOOKUP, ret >= 0); 191247605Spjd CHECK(ret == -1 || close(ret) == 0); 192247605Spjd CHECK(ret == -1 || unlinkat(dirfd, "cap_create", 0) == 0); 193247605Spjd ret = openat(dfd_cap, "cap_create", O_CREAT | O_WRONLY, 0600); 194247605Spjd CHECK_RESULT(openat(O_CREATE | O_WRONLY), 195247605Spjd CAP_CREATE | CAP_WRITE | CAP_LOOKUP, ret >= 0); 196247605Spjd CHECK(ret == -1 || close(ret) == 0); 197247605Spjd CHECK(ret == -1 || unlinkat(dirfd, "cap_create", 0) == 0); 198247605Spjd ret = openat(dfd_cap, "cap_create", O_CREAT | O_RDWR, 0600); 199247605Spjd CHECK_RESULT(openat(O_CREATE | O_RDWR), 200247605Spjd CAP_CREATE | CAP_READ | CAP_WRITE | CAP_LOOKUP, ret >= 0); 201247605Spjd CHECK(ret == -1 || close(ret) == 0); 202247605Spjd CHECK(ret == -1 || unlinkat(dirfd, "cap_create", 0) == 0); 203247605Spjd 204224653Sjonathan ret = fsync(fd_cap); 205224653Sjonathan CHECK_RESULT(fsync, CAP_FSYNC, ret == 0); 206224653Sjonathan 207247605Spjd ret = openat(dirfd, "cap_fsync", O_CREAT, 0600); 208247605Spjd CHECK(ret >= 0); 209247605Spjd CHECK(close(ret) == 0); 210248394Spjd ret = openat(dfd_cap, "cap_fsync", O_FSYNC | O_RDONLY); 211247605Spjd CHECK_RESULT(openat(O_FSYNC | O_RDONLY), 212247605Spjd CAP_FSYNC | CAP_READ | CAP_LOOKUP, ret >= 0); 213247605Spjd CHECK(ret == -1 || close(ret) == 0); 214248394Spjd ret = openat(dfd_cap, "cap_fsync", O_FSYNC | O_WRONLY); 215247605Spjd CHECK_RESULT(openat(O_FSYNC | O_WRONLY), 216247605Spjd CAP_FSYNC | CAP_WRITE | CAP_LOOKUP, ret >= 0); 217247605Spjd CHECK(ret == -1 || close(ret) == 0); 218248394Spjd ret = openat(dfd_cap, "cap_fsync", O_FSYNC | O_RDWR); 219247605Spjd CHECK_RESULT(openat(O_FSYNC | O_RDWR), 220247605Spjd CAP_FSYNC | CAP_READ | CAP_WRITE | CAP_LOOKUP, ret >= 0); 221247605Spjd CHECK(ret == -1 || close(ret) == 0); 222248394Spjd ret = openat(dfd_cap, "cap_fsync", O_SYNC | O_RDONLY); 223247605Spjd CHECK_RESULT(openat(O_SYNC | O_RDONLY), 224247605Spjd CAP_FSYNC | CAP_READ | CAP_LOOKUP, ret >= 0); 225247605Spjd CHECK(ret == -1 || close(ret) == 0); 226248394Spjd ret = openat(dfd_cap, "cap_fsync", O_SYNC | O_WRONLY); 227247605Spjd CHECK_RESULT(openat(O_SYNC | O_WRONLY), 228247605Spjd CAP_FSYNC | CAP_WRITE | CAP_LOOKUP, ret >= 0); 229247605Spjd CHECK(ret == -1 || close(ret) == 0); 230248394Spjd ret = openat(dfd_cap, "cap_fsync", O_SYNC | O_RDWR); 231247605Spjd CHECK_RESULT(openat(O_SYNC | O_RDWR), 232247605Spjd CAP_FSYNC | CAP_READ | CAP_WRITE | CAP_LOOKUP, ret >= 0); 233247605Spjd CHECK(ret == -1 || close(ret) == 0); 234247605Spjd CHECK(unlinkat(dirfd, "cap_fsync", 0) == 0); 235247605Spjd 236247605Spjd ret = ftruncate(fd_cap, 0); 237247605Spjd CHECK_RESULT(ftruncate, CAP_FTRUNCATE, ret == 0); 238247605Spjd 239247605Spjd ret = openat(dirfd, "cap_ftruncate", O_CREAT, 0600); 240247605Spjd CHECK(ret >= 0); 241247605Spjd CHECK(close(ret) == 0); 242247605Spjd ret = openat(dfd_cap, "cap_ftruncate", O_TRUNC | O_RDONLY); 243247605Spjd CHECK_RESULT(openat(O_TRUNC | O_RDONLY), 244247605Spjd CAP_FTRUNCATE | CAP_READ | CAP_LOOKUP, ret >= 0); 245247605Spjd CHECK(ret == -1 || close(ret) == 0); 246247605Spjd ret = openat(dfd_cap, "cap_ftruncate", O_TRUNC | O_WRONLY); 247247605Spjd CHECK_RESULT(openat(O_TRUNC | O_WRONLY), 248247605Spjd CAP_FTRUNCATE | CAP_WRITE | CAP_LOOKUP, ret >= 0); 249247605Spjd CHECK(ret == -1 || close(ret) == 0); 250247605Spjd ret = openat(dfd_cap, "cap_ftruncate", O_TRUNC | O_RDWR); 251247605Spjd CHECK_RESULT(openat(O_TRUNC | O_RDWR), 252247605Spjd CAP_FTRUNCATE | CAP_READ | CAP_WRITE | CAP_LOOKUP, ret >= 0); 253247605Spjd CHECK(ret == -1 || close(ret) == 0); 254247605Spjd CHECK(unlinkat(dirfd, "cap_ftruncate", 0) == 0); 255247605Spjd 256247605Spjd /* 257247605Spjd * Note: this is not expected to work over NFS. 258247605Spjd */ 259247605Spjd ret = fchflags(fd_cap, UF_NODUMP); 260247605Spjd CHECK_RESULT(fchflags, CAP_FCHFLAGS, 261247605Spjd ret == 0 || (is_nfs && errno == EOPNOTSUPP)); 262247605Spjd 263247605Spjd#ifdef TODO /* No such syscalls yet. */ 264247605Spjd ret = openat(dirfd, "cap_fchflagsat", O_CREAT, 0600); 265247605Spjd CHECK(ret >= 0); 266247605Spjd CHECK(close(ret) == 0); 267247605Spjd ret = fchflagsat(dfd_cap, "cap_fchflagsat", UF_NODUMP, 0); 268247605Spjd CHECK_RESULT(fchflagsat, CAP_FCHFLAGSAT | CAP_LOOKUP, ret == 0); 269247605Spjd CHECK(unlinkat(dirfd, "cap_fchflagsat", 0) == 0); 270247605Spjd#endif 271247605Spjd 272224653Sjonathan ret = fchown(fd_cap, -1, -1); 273224653Sjonathan CHECK_RESULT(fchown, CAP_FCHOWN, ret == 0); 274224653Sjonathan 275247605Spjd ret = openat(dirfd, "cap_fchownat", O_CREAT, 0600); 276247605Spjd CHECK(ret >= 0); 277247605Spjd CHECK(close(ret) == 0); 278247605Spjd ret = fchownat(dfd_cap, "cap_fchownat", -1, -1, 0); 279247605Spjd CHECK_RESULT(fchownat, CAP_FCHOWN | CAP_LOOKUP, ret == 0); 280247605Spjd CHECK(unlinkat(dirfd, "cap_fchownat", 0) == 0); 281247605Spjd 282224653Sjonathan ret = fchmod(fd_cap, 0644); 283224653Sjonathan CHECK_RESULT(fchmod, CAP_FCHMOD, ret == 0); 284224653Sjonathan 285247605Spjd ret = openat(dirfd, "cap_fchmodat", O_CREAT, 0600); 286247605Spjd CHECK(ret >= 0); 287247605Spjd CHECK(close(ret) == 0); 288247605Spjd ret = fchmodat(dfd_cap, "cap_fchmodat", 0600, 0); 289247605Spjd CHECK_RESULT(fchmodat, CAP_FCHMOD | CAP_LOOKUP, ret == 0); 290247605Spjd CHECK(unlinkat(dirfd, "cap_fchmodat", 0) == 0); 291247605Spjd 292247605Spjd ret = fcntl(fd_cap, F_GETFL); 293247605Spjd CHECK_RESULT(fcntl(F_GETFL), CAP_FCNTL, ret >= 0); 294247605Spjd ret = fcntl(fd_cap, F_SETFL, ret); 295247605Spjd CHECK_RESULT(fcntl(F_SETFL), CAP_FCNTL, ret == 0); 296247605Spjd 297224653Sjonathan /* XXX flock */ 298224653Sjonathan 299247605Spjd ret = fstat(fd_cap, &sb); 300247605Spjd CHECK_RESULT(fstat, CAP_FSTAT, ret == 0); 301224653Sjonathan 302247605Spjd ret = openat(dirfd, "cap_fstatat", O_CREAT, 0600); 303247605Spjd CHECK(ret >= 0); 304247605Spjd CHECK(close(ret) == 0); 305247605Spjd ret = fstatat(dfd_cap, "cap_fstatat", &sb, 0); 306247605Spjd CHECK_RESULT(fstatat, CAP_FSTAT | CAP_LOOKUP, ret == 0); 307247605Spjd CHECK(unlinkat(dirfd, "cap_fstatat", 0) == 0); 308247605Spjd 309224653Sjonathan ret = fstatfs(fd_cap, &sf); 310224653Sjonathan CHECK_RESULT(fstatfs, CAP_FSTATFS, ret == 0); 311224653Sjonathan 312224653Sjonathan ret = fpathconf(fd_cap, _PC_NAME_MAX); 313224653Sjonathan CHECK_RESULT(fpathconf, CAP_FPATHCONF, ret >= 0); 314224653Sjonathan 315224653Sjonathan ret = futimes(fd_cap, NULL); 316224653Sjonathan CHECK_RESULT(futimes, CAP_FUTIMES, ret == 0); 317224653Sjonathan 318247605Spjd ret = openat(dirfd, "cap_futimesat", O_CREAT, 0600); 319247605Spjd CHECK(ret >= 0); 320247605Spjd CHECK(close(ret) == 0); 321247605Spjd ret = futimesat(dfd_cap, "cap_futimesat", NULL); 322247605Spjd CHECK_RESULT(futimesat, CAP_FUTIMES | CAP_LOOKUP, ret == 0); 323247605Spjd CHECK(unlinkat(dirfd, "cap_futimesat", 0) == 0); 324247605Spjd 325247605Spjd ret = openat(dirfd, "cap_linkat_src", O_CREAT, 0600); 326247605Spjd CHECK(ret >= 0); 327247605Spjd CHECK(close(ret) == 0); 328247605Spjd ret = linkat(dirfd, "cap_linkat_src", dfd_cap, "cap_linkat_dst", 0); 329247605Spjd CHECK_RESULT(linkat, CAP_LINKAT | CAP_LOOKUP, ret == 0); 330247605Spjd CHECK(unlinkat(dirfd, "cap_linkat_src", 0) == 0); 331247605Spjd CHECK(ret == -1 || unlinkat(dirfd, "cap_linkat_dst", 0) == 0); 332247605Spjd 333247605Spjd ret = mkdirat(dfd_cap, "cap_mkdirat", 0700); 334247605Spjd CHECK_RESULT(mkdirat, CAP_MKDIRAT | CAP_LOOKUP, ret == 0); 335247605Spjd CHECK(ret == -1 || unlinkat(dirfd, "cap_mkdirat", AT_REMOVEDIR) == 0); 336247605Spjd 337247605Spjd ret = mkfifoat(dfd_cap, "cap_mkfifoat", 0600); 338247605Spjd CHECK_RESULT(mkfifoat, CAP_MKFIFOAT | CAP_LOOKUP, ret == 0); 339247605Spjd CHECK(ret == -1 || unlinkat(dirfd, "cap_mkfifoat", 0) == 0); 340247605Spjd 341247605Spjd ret = mknodat(dfd_cap, "cap_mknodat", S_IFCHR | 0600, 0); 342247605Spjd CHECK_RESULT(mknodat, CAP_MKNODAT | CAP_LOOKUP, ret == 0); 343247605Spjd CHECK(ret == -1 || unlinkat(dirfd, "cap_mknodat", 0) == 0); 344247605Spjd 345247605Spjd /* TODO: renameat(2) */ 346247605Spjd 347247605Spjd ret = symlinkat("test", dfd_cap, "cap_symlinkat"); 348247605Spjd CHECK_RESULT(symlinkat, CAP_SYMLINKAT | CAP_LOOKUP, ret == 0); 349247605Spjd CHECK(ret == -1 || unlinkat(dirfd, "cap_symlinkat", 0) == 0); 350247605Spjd 351247605Spjd ret = openat(dirfd, "cap_unlinkat", O_CREAT, 0600); 352247605Spjd CHECK(ret >= 0); 353247605Spjd CHECK(close(ret) == 0); 354247605Spjd ret = unlinkat(dfd_cap, "cap_unlinkat", 0); 355247605Spjd CHECK_RESULT(unlinkat, CAP_UNLINKAT | CAP_LOOKUP, ret == 0); 356247605Spjd CHECK(ret == 0 || unlinkat(dirfd, "cap_unlinkat", 0) == 0); 357247605Spjd ret = mkdirat(dirfd, "cap_unlinkat", 0700); 358247605Spjd CHECK(ret == 0); 359247605Spjd ret = unlinkat(dfd_cap, "cap_unlinkat", AT_REMOVEDIR); 360247605Spjd CHECK_RESULT(unlinkat, CAP_UNLINKAT | CAP_LOOKUP, ret == 0); 361247605Spjd CHECK(ret == 0 || unlinkat(dirfd, "cap_unlinkat", AT_REMOVEDIR) == 0); 362247605Spjd 363247605Spjd pollfd.fd = fd_cap; 364247605Spjd pollfd.events = POLLIN | POLLERR | POLLHUP; 365247605Spjd pollfd.revents = 0; 366247605Spjd 367224910Sjonathan ret = poll(&pollfd, 1, 0); 368224910Sjonathan if (rights & CAP_POLL_EVENT) 369224910Sjonathan CHECK((pollfd.revents & POLLNVAL) == 0); 370224910Sjonathan else 371224910Sjonathan CHECK((pollfd.revents & POLLNVAL) != 0); 372224653Sjonathan 373224910Sjonathan /* XXX: select, kqueue */ 374224910Sjonathan 375247605Spjd close(fd_cap); 376247605Spjd close(fd_capcap); 377247605Spjd 378247605Spjd if (success == -1) { 379247605Spjd fprintf(stderr, "No tests for rights 0x%jx.\n", 380247605Spjd (uintmax_t)rights); 381247605Spjd success = FAILED; 382247605Spjd } 383224653Sjonathan return (success); 384224653Sjonathan} 385224653Sjonathan 386247605Spjd#define TRY(rights) \ 387224653Sjonathando { \ 388224653Sjonathan if (success == PASSED) \ 389247605Spjd success = try_file_ops(filefd, dirfd, (rights)); \ 390224653Sjonathan else \ 391224653Sjonathan /* We've already failed, but try the test anyway. */ \ 392247605Spjd try_file_ops(filefd, dirfd, (rights)); \ 393224653Sjonathan} while (0) 394224653Sjonathan 395247605Spjd#define KEEP_ERRNO(...) do { \ 396247605Spjd int _saved_errno = errno; \ 397247605Spjd __VA_ARGS__; \ 398247605Spjd errno = _saved_errno; \ 399247605Spjd} while (0); 400247605Spjd 401224653Sjonathanint 402224653Sjonathantest_capabilities(void) 403224653Sjonathan{ 404247605Spjd int filefd, dirfd, tmpfd; 405224653Sjonathan int success = PASSED; 406247605Spjd char file[] = "/tmp/cap_test.XXXXXXXXXX"; 407247605Spjd char dir[] = "/tmp/cap_test.XXXXXXXXXX"; 408224653Sjonathan 409247605Spjd filefd = mkstemp(file); 410247605Spjd if (filefd < 0) 411247605Spjd err(-1, "mkstemp"); 412247605Spjd if (mkdtemp(dir) == NULL) { 413247605Spjd KEEP_ERRNO(unlink(file)); 414247605Spjd err(-1, "mkdtemp"); 415247605Spjd } 416247605Spjd dirfd = open(dir, O_RDONLY | O_DIRECTORY); 417247605Spjd if (dirfd == -1) { 418247605Spjd KEEP_ERRNO(unlink(file)); 419247605Spjd KEEP_ERRNO(rmdir(dir)); 420224653Sjonathan err(-1, "open"); 421247605Spjd } 422247605Spjd tmpfd = open("/tmp", O_RDONLY | O_DIRECTORY); 423247605Spjd if (tmpfd == -1) { 424247605Spjd KEEP_ERRNO(unlink(file)); 425247605Spjd KEEP_ERRNO(rmdir(dir)); 426247605Spjd err(-1, "open"); 427247605Spjd } 428224653Sjonathan 429247605Spjd if (cap_enter() == -1) { 430247605Spjd KEEP_ERRNO(unlink(file)); 431247605Spjd KEEP_ERRNO(rmdir(dir)); 432224653Sjonathan err(-1, "cap_enter"); 433247605Spjd } 434224653Sjonathan 435247605Spjd TRY(CAP_READ); 436247605Spjd TRY(CAP_WRITE); 437247605Spjd TRY(CAP_SEEK); 438247605Spjd TRY(CAP_PREAD); 439247605Spjd TRY(CAP_PWRITE); 440247605Spjd TRY(CAP_READ | CAP_WRITE); 441247605Spjd TRY(CAP_PREAD | CAP_PWRITE); 442247605Spjd TRY(CAP_MMAP); 443247605Spjd TRY(CAP_MMAP_R); 444247605Spjd TRY(CAP_MMAP_W); 445247605Spjd TRY(CAP_MMAP_X); 446247605Spjd TRY(CAP_MMAP_RW); 447247605Spjd TRY(CAP_MMAP_RX); 448247605Spjd TRY(CAP_MMAP_WX); 449247605Spjd TRY(CAP_MMAP_RWX); 450247605Spjd TRY(CAP_CREATE | CAP_READ | CAP_LOOKUP); 451247605Spjd TRY(CAP_CREATE | CAP_WRITE | CAP_LOOKUP); 452247605Spjd TRY(CAP_CREATE | CAP_READ | CAP_WRITE | CAP_LOOKUP); 453247605Spjd#ifdef TODO 454247605Spjd TRY(CAP_FEXECVE); 455247605Spjd#endif 456247605Spjd TRY(CAP_FSYNC); 457247605Spjd TRY(CAP_FSYNC | CAP_READ | CAP_LOOKUP); 458247605Spjd TRY(CAP_FSYNC | CAP_WRITE | CAP_LOOKUP); 459247605Spjd TRY(CAP_FSYNC | CAP_READ | CAP_WRITE | CAP_LOOKUP); 460247605Spjd TRY(CAP_FTRUNCATE); 461247605Spjd TRY(CAP_FTRUNCATE | CAP_READ | CAP_LOOKUP); 462247605Spjd TRY(CAP_FTRUNCATE | CAP_WRITE | CAP_LOOKUP); 463247605Spjd TRY(CAP_FTRUNCATE | CAP_READ | CAP_WRITE | CAP_LOOKUP); 464247605Spjd#ifdef TODO 465247605Spjd TRY(CAP_FCHDIR); 466247605Spjd#endif 467247605Spjd TRY(CAP_FCHFLAGS); 468247605Spjd TRY(CAP_FCHOWN); 469247605Spjd TRY(CAP_FCHOWN | CAP_LOOKUP); 470247605Spjd TRY(CAP_FCHMOD | CAP_LOOKUP); 471247605Spjd TRY(CAP_FCNTL); 472247605Spjd#ifdef TODO 473247605Spjd TRY(CAP_FLOCK); 474247605Spjd#endif 475247605Spjd TRY(CAP_FPATHCONF); 476247605Spjd#ifdef TODO 477247605Spjd TRY(CAP_FSCK); 478247605Spjd#endif 479247605Spjd TRY(CAP_FSTAT | CAP_LOOKUP); 480247605Spjd TRY(CAP_FSTATFS); 481247605Spjd TRY(CAP_FUTIMES | CAP_LOOKUP); 482247605Spjd TRY(CAP_LINKAT | CAP_LOOKUP); 483247605Spjd TRY(CAP_MKDIRAT | CAP_LOOKUP); 484247605Spjd TRY(CAP_MKFIFOAT | CAP_LOOKUP); 485247605Spjd TRY(CAP_MKNODAT | CAP_LOOKUP); 486247605Spjd TRY(CAP_SYMLINKAT | CAP_LOOKUP); 487247605Spjd TRY(CAP_UNLINKAT | CAP_LOOKUP); 488247605Spjd /* Rename needs CAP_RENAMEAT on source directory and CAP_LINKAT on destination directory. */ 489247605Spjd TRY(CAP_RENAMEAT | CAP_UNLINKAT | CAP_LOOKUP); 490247605Spjd#ifdef TODO 491247605Spjd TRY(CAP_LOOKUP); 492247605Spjd TRY(CAP_EXTATTR_DELETE); 493247605Spjd TRY(CAP_EXTATTR_GET); 494247605Spjd TRY(CAP_EXTATTR_LIST); 495247605Spjd TRY(CAP_EXTATTR_SET); 496247605Spjd TRY(CAP_ACL_CHECK); 497247605Spjd TRY(CAP_ACL_DELETE); 498247605Spjd TRY(CAP_ACL_GET); 499247605Spjd TRY(CAP_ACL_SET); 500247605Spjd TRY(CAP_ACCEPT); 501247605Spjd TRY(CAP_BIND); 502247605Spjd TRY(CAP_CONNECT); 503247605Spjd TRY(CAP_GETPEERNAME); 504247605Spjd TRY(CAP_GETSOCKNAME); 505247605Spjd TRY(CAP_GETSOCKOPT); 506247605Spjd TRY(CAP_LISTEN); 507247605Spjd TRY(CAP_PEELOFF); 508247605Spjd TRY(CAP_RECV); 509247605Spjd TRY(CAP_SEND); 510247605Spjd TRY(CAP_SETSOCKOPT); 511247605Spjd TRY(CAP_SHUTDOWN); 512247605Spjd TRY(CAP_MAC_GET); 513247605Spjd TRY(CAP_MAC_SET); 514247605Spjd TRY(CAP_SEM_GETVALUE); 515247605Spjd TRY(CAP_SEM_POST); 516247605Spjd TRY(CAP_SEM_WAIT); 517247605Spjd TRY(CAP_POST_EVENT); 518247605Spjd TRY(CAP_POLL_EVENT); 519247605Spjd TRY(CAP_IOCTL); 520247605Spjd TRY(CAP_TTYHOOK); 521247605Spjd TRY(CAP_PDGETPID); 522247605Spjd TRY(CAP_PDWAIT); 523247605Spjd TRY(CAP_PDKILL); 524247605Spjd#endif 525224653Sjonathan 526247605Spjd (void)unlinkat(tmpfd, file + strlen("/tmp/"), 0); 527247605Spjd (void)unlinkat(tmpfd, dir + strlen("/tmp/"), AT_REMOVEDIR); 528224653Sjonathan 529224653Sjonathan return (success); 530224653Sjonathan} 531