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$"); 39224653Sjonathan 40224653Sjonathan#include <sys/param.h> 41263234Srwatson#include <sys/capsicum.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 ret = openat(dfd_cap, "cap_create", O_CREAT | O_RDONLY, 0600); 188247605Spjd CHECK_RESULT(openat(O_CREATE | O_RDONLY), 189247605Spjd CAP_CREATE | CAP_READ | CAP_LOOKUP, ret >= 0); 190247605Spjd CHECK(ret == -1 || close(ret) == 0); 191247605Spjd CHECK(ret == -1 || unlinkat(dirfd, "cap_create", 0) == 0); 192248396Spjd ret = openat(dfd_cap, "cap_create", O_CREAT | O_WRONLY | O_APPEND, 193248396Spjd 0600); 194248396Spjd CHECK_RESULT(openat(O_CREATE | O_WRONLY | O_APPEND), 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); 198248396Spjd ret = openat(dfd_cap, "cap_create", O_CREAT | O_RDWR | O_APPEND, 0600); 199248396Spjd CHECK_RESULT(openat(O_CREATE | O_RDWR | O_APPEND), 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); 214248396Spjd ret = openat(dfd_cap, "cap_fsync", O_FSYNC | O_WRONLY | O_APPEND); 215248396Spjd CHECK_RESULT(openat(O_FSYNC | O_WRONLY | O_APPEND), 216247605Spjd CAP_FSYNC | CAP_WRITE | CAP_LOOKUP, ret >= 0); 217247605Spjd CHECK(ret == -1 || close(ret) == 0); 218248396Spjd ret = openat(dfd_cap, "cap_fsync", O_FSYNC | O_RDWR | O_APPEND); 219248396Spjd CHECK_RESULT(openat(O_FSYNC | O_RDWR | O_APPEND), 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); 226248396Spjd ret = openat(dfd_cap, "cap_fsync", O_SYNC | O_WRONLY | O_APPEND); 227248396Spjd CHECK_RESULT(openat(O_SYNC | O_WRONLY | O_APPEND), 228247605Spjd CAP_FSYNC | CAP_WRITE | CAP_LOOKUP, ret >= 0); 229247605Spjd CHECK(ret == -1 || close(ret) == 0); 230248396Spjd ret = openat(dfd_cap, "cap_fsync", O_SYNC | O_RDWR | O_APPEND); 231248396Spjd CHECK_RESULT(openat(O_SYNC | O_RDWR | O_APPEND), 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 256248396Spjd ret = openat(dfd_cap, "cap_create", O_CREAT | O_WRONLY, 0600); 257248396Spjd CHECK_RESULT(openat(O_CREATE | O_WRONLY), 258248396Spjd CAP_CREATE | CAP_WRITE | CAP_SEEK | CAP_LOOKUP, ret >= 0); 259248396Spjd CHECK(ret == -1 || close(ret) == 0); 260248396Spjd CHECK(ret == -1 || unlinkat(dirfd, "cap_create", 0) == 0); 261248396Spjd ret = openat(dfd_cap, "cap_create", O_CREAT | O_RDWR, 0600); 262248396Spjd CHECK_RESULT(openat(O_CREATE | O_RDWR), 263248396Spjd CAP_CREATE | CAP_READ | CAP_WRITE | CAP_SEEK | CAP_LOOKUP, 264248396Spjd ret >= 0); 265248396Spjd CHECK(ret == -1 || close(ret) == 0); 266248396Spjd CHECK(ret == -1 || unlinkat(dirfd, "cap_create", 0) == 0); 267248396Spjd 268248396Spjd ret = openat(dirfd, "cap_fsync", O_CREAT, 0600); 269248396Spjd CHECK(ret >= 0); 270248396Spjd CHECK(close(ret) == 0); 271248396Spjd ret = openat(dfd_cap, "cap_fsync", O_FSYNC | O_WRONLY); 272248396Spjd CHECK_RESULT(openat(O_FSYNC | O_WRONLY), 273248396Spjd CAP_FSYNC | CAP_WRITE | CAP_SEEK | CAP_LOOKUP, ret >= 0); 274248396Spjd CHECK(ret == -1 || close(ret) == 0); 275248396Spjd ret = openat(dfd_cap, "cap_fsync", O_FSYNC | O_RDWR); 276248396Spjd CHECK_RESULT(openat(O_FSYNC | O_RDWR), 277248396Spjd CAP_FSYNC | CAP_READ | CAP_WRITE | CAP_SEEK | CAP_LOOKUP, ret >= 0); 278248396Spjd CHECK(ret == -1 || close(ret) == 0); 279248396Spjd ret = openat(dfd_cap, "cap_fsync", O_SYNC | O_WRONLY); 280248396Spjd CHECK_RESULT(openat(O_SYNC | O_WRONLY), 281248396Spjd CAP_FSYNC | CAP_WRITE | CAP_SEEK | CAP_LOOKUP, ret >= 0); 282248396Spjd CHECK(ret == -1 || close(ret) == 0); 283248396Spjd ret = openat(dfd_cap, "cap_fsync", O_SYNC | O_RDWR); 284248396Spjd CHECK_RESULT(openat(O_SYNC | O_RDWR), 285248396Spjd CAP_FSYNC | CAP_READ | CAP_WRITE | CAP_SEEK | CAP_LOOKUP, ret >= 0); 286248396Spjd CHECK(ret == -1 || close(ret) == 0); 287248396Spjd CHECK(unlinkat(dirfd, "cap_fsync", 0) == 0); 288248396Spjd 289247605Spjd /* 290247605Spjd * Note: this is not expected to work over NFS. 291247605Spjd */ 292247605Spjd ret = fchflags(fd_cap, UF_NODUMP); 293247605Spjd CHECK_RESULT(fchflags, CAP_FCHFLAGS, 294247605Spjd ret == 0 || (is_nfs && errno == EOPNOTSUPP)); 295247605Spjd 296248603Spjd ret = openat(dirfd, "cap_chflagsat", O_CREAT, 0600); 297247605Spjd CHECK(ret >= 0); 298247605Spjd CHECK(close(ret) == 0); 299248603Spjd ret = chflagsat(dfd_cap, "cap_chflagsat", UF_NODUMP, 0); 300248603Spjd CHECK_RESULT(chflagsat, CAP_CHFLAGSAT | CAP_LOOKUP, ret == 0); 301248603Spjd CHECK(unlinkat(dirfd, "cap_chflagsat", 0) == 0); 302247605Spjd 303224653Sjonathan ret = fchown(fd_cap, -1, -1); 304224653Sjonathan CHECK_RESULT(fchown, CAP_FCHOWN, ret == 0); 305224653Sjonathan 306247605Spjd ret = openat(dirfd, "cap_fchownat", O_CREAT, 0600); 307247605Spjd CHECK(ret >= 0); 308247605Spjd CHECK(close(ret) == 0); 309247605Spjd ret = fchownat(dfd_cap, "cap_fchownat", -1, -1, 0); 310247605Spjd CHECK_RESULT(fchownat, CAP_FCHOWN | CAP_LOOKUP, ret == 0); 311247605Spjd CHECK(unlinkat(dirfd, "cap_fchownat", 0) == 0); 312247605Spjd 313224653Sjonathan ret = fchmod(fd_cap, 0644); 314224653Sjonathan CHECK_RESULT(fchmod, CAP_FCHMOD, ret == 0); 315224653Sjonathan 316247605Spjd ret = openat(dirfd, "cap_fchmodat", O_CREAT, 0600); 317247605Spjd CHECK(ret >= 0); 318247605Spjd CHECK(close(ret) == 0); 319247605Spjd ret = fchmodat(dfd_cap, "cap_fchmodat", 0600, 0); 320247605Spjd CHECK_RESULT(fchmodat, CAP_FCHMOD | CAP_LOOKUP, ret == 0); 321247605Spjd CHECK(unlinkat(dirfd, "cap_fchmodat", 0) == 0); 322247605Spjd 323247605Spjd ret = fcntl(fd_cap, F_GETFL); 324247605Spjd CHECK_RESULT(fcntl(F_GETFL), CAP_FCNTL, ret >= 0); 325247605Spjd ret = fcntl(fd_cap, F_SETFL, ret); 326247605Spjd CHECK_RESULT(fcntl(F_SETFL), CAP_FCNTL, ret == 0); 327247605Spjd 328224653Sjonathan /* XXX flock */ 329224653Sjonathan 330247605Spjd ret = fstat(fd_cap, &sb); 331247605Spjd CHECK_RESULT(fstat, CAP_FSTAT, ret == 0); 332224653Sjonathan 333247605Spjd ret = openat(dirfd, "cap_fstatat", O_CREAT, 0600); 334247605Spjd CHECK(ret >= 0); 335247605Spjd CHECK(close(ret) == 0); 336247605Spjd ret = fstatat(dfd_cap, "cap_fstatat", &sb, 0); 337247605Spjd CHECK_RESULT(fstatat, CAP_FSTAT | CAP_LOOKUP, ret == 0); 338247605Spjd CHECK(unlinkat(dirfd, "cap_fstatat", 0) == 0); 339247605Spjd 340224653Sjonathan ret = fstatfs(fd_cap, &sf); 341224653Sjonathan CHECK_RESULT(fstatfs, CAP_FSTATFS, ret == 0); 342224653Sjonathan 343224653Sjonathan ret = fpathconf(fd_cap, _PC_NAME_MAX); 344224653Sjonathan CHECK_RESULT(fpathconf, CAP_FPATHCONF, ret >= 0); 345224653Sjonathan 346224653Sjonathan ret = futimes(fd_cap, NULL); 347224653Sjonathan CHECK_RESULT(futimes, CAP_FUTIMES, ret == 0); 348224653Sjonathan 349247605Spjd ret = openat(dirfd, "cap_futimesat", O_CREAT, 0600); 350247605Spjd CHECK(ret >= 0); 351247605Spjd CHECK(close(ret) == 0); 352247605Spjd ret = futimesat(dfd_cap, "cap_futimesat", NULL); 353247605Spjd CHECK_RESULT(futimesat, CAP_FUTIMES | CAP_LOOKUP, ret == 0); 354247605Spjd CHECK(unlinkat(dirfd, "cap_futimesat", 0) == 0); 355247605Spjd 356247605Spjd ret = openat(dirfd, "cap_linkat_src", O_CREAT, 0600); 357247605Spjd CHECK(ret >= 0); 358247605Spjd CHECK(close(ret) == 0); 359247605Spjd ret = linkat(dirfd, "cap_linkat_src", dfd_cap, "cap_linkat_dst", 0); 360247605Spjd CHECK_RESULT(linkat, CAP_LINKAT | CAP_LOOKUP, ret == 0); 361247605Spjd CHECK(unlinkat(dirfd, "cap_linkat_src", 0) == 0); 362247605Spjd CHECK(ret == -1 || unlinkat(dirfd, "cap_linkat_dst", 0) == 0); 363247605Spjd 364247605Spjd ret = mkdirat(dfd_cap, "cap_mkdirat", 0700); 365247605Spjd CHECK_RESULT(mkdirat, CAP_MKDIRAT | CAP_LOOKUP, ret == 0); 366247605Spjd CHECK(ret == -1 || unlinkat(dirfd, "cap_mkdirat", AT_REMOVEDIR) == 0); 367247605Spjd 368247605Spjd ret = mkfifoat(dfd_cap, "cap_mkfifoat", 0600); 369247605Spjd CHECK_RESULT(mkfifoat, CAP_MKFIFOAT | CAP_LOOKUP, ret == 0); 370247605Spjd CHECK(ret == -1 || unlinkat(dirfd, "cap_mkfifoat", 0) == 0); 371247605Spjd 372247605Spjd ret = mknodat(dfd_cap, "cap_mknodat", S_IFCHR | 0600, 0); 373247605Spjd CHECK_RESULT(mknodat, CAP_MKNODAT | CAP_LOOKUP, ret == 0); 374247605Spjd CHECK(ret == -1 || unlinkat(dirfd, "cap_mknodat", 0) == 0); 375247605Spjd 376247605Spjd /* TODO: renameat(2) */ 377247605Spjd 378247605Spjd ret = symlinkat("test", dfd_cap, "cap_symlinkat"); 379247605Spjd CHECK_RESULT(symlinkat, CAP_SYMLINKAT | CAP_LOOKUP, ret == 0); 380247605Spjd CHECK(ret == -1 || unlinkat(dirfd, "cap_symlinkat", 0) == 0); 381247605Spjd 382247605Spjd ret = openat(dirfd, "cap_unlinkat", O_CREAT, 0600); 383247605Spjd CHECK(ret >= 0); 384247605Spjd CHECK(close(ret) == 0); 385247605Spjd ret = unlinkat(dfd_cap, "cap_unlinkat", 0); 386247605Spjd CHECK_RESULT(unlinkat, CAP_UNLINKAT | CAP_LOOKUP, ret == 0); 387247605Spjd CHECK(ret == 0 || unlinkat(dirfd, "cap_unlinkat", 0) == 0); 388247605Spjd ret = mkdirat(dirfd, "cap_unlinkat", 0700); 389247605Spjd CHECK(ret == 0); 390247605Spjd ret = unlinkat(dfd_cap, "cap_unlinkat", AT_REMOVEDIR); 391247605Spjd CHECK_RESULT(unlinkat, CAP_UNLINKAT | CAP_LOOKUP, ret == 0); 392247605Spjd CHECK(ret == 0 || unlinkat(dirfd, "cap_unlinkat", AT_REMOVEDIR) == 0); 393247605Spjd 394247605Spjd pollfd.fd = fd_cap; 395247605Spjd pollfd.events = POLLIN | POLLERR | POLLHUP; 396247605Spjd pollfd.revents = 0; 397247605Spjd 398224910Sjonathan ret = poll(&pollfd, 1, 0); 399261566Sbrueffer if (rights & CAP_EVENT) 400224910Sjonathan CHECK((pollfd.revents & POLLNVAL) == 0); 401224910Sjonathan else 402224910Sjonathan CHECK((pollfd.revents & POLLNVAL) != 0); 403224653Sjonathan 404224910Sjonathan /* XXX: select, kqueue */ 405224910Sjonathan 406247605Spjd close(fd_cap); 407247605Spjd close(fd_capcap); 408247605Spjd 409247605Spjd if (success == -1) { 410247605Spjd fprintf(stderr, "No tests for rights 0x%jx.\n", 411247605Spjd (uintmax_t)rights); 412247605Spjd success = FAILED; 413247605Spjd } 414224653Sjonathan return (success); 415224653Sjonathan} 416224653Sjonathan 417247605Spjd#define TRY(rights) \ 418224653Sjonathando { \ 419224653Sjonathan if (success == PASSED) \ 420247605Spjd success = try_file_ops(filefd, dirfd, (rights)); \ 421224653Sjonathan else \ 422224653Sjonathan /* We've already failed, but try the test anyway. */ \ 423247605Spjd try_file_ops(filefd, dirfd, (rights)); \ 424224653Sjonathan} while (0) 425224653Sjonathan 426247605Spjd#define KEEP_ERRNO(...) do { \ 427247605Spjd int _saved_errno = errno; \ 428247605Spjd __VA_ARGS__; \ 429247605Spjd errno = _saved_errno; \ 430247605Spjd} while (0); 431247605Spjd 432224653Sjonathanint 433224653Sjonathantest_capabilities(void) 434224653Sjonathan{ 435247605Spjd int filefd, dirfd, tmpfd; 436224653Sjonathan int success = PASSED; 437247605Spjd char file[] = "/tmp/cap_test.XXXXXXXXXX"; 438247605Spjd char dir[] = "/tmp/cap_test.XXXXXXXXXX"; 439224653Sjonathan 440247605Spjd filefd = mkstemp(file); 441247605Spjd if (filefd < 0) 442247605Spjd err(-1, "mkstemp"); 443247605Spjd if (mkdtemp(dir) == NULL) { 444247605Spjd KEEP_ERRNO(unlink(file)); 445247605Spjd err(-1, "mkdtemp"); 446247605Spjd } 447247605Spjd dirfd = open(dir, O_RDONLY | O_DIRECTORY); 448247605Spjd if (dirfd == -1) { 449247605Spjd KEEP_ERRNO(unlink(file)); 450247605Spjd KEEP_ERRNO(rmdir(dir)); 451224653Sjonathan err(-1, "open"); 452247605Spjd } 453247605Spjd tmpfd = open("/tmp", O_RDONLY | O_DIRECTORY); 454247605Spjd if (tmpfd == -1) { 455247605Spjd KEEP_ERRNO(unlink(file)); 456247605Spjd KEEP_ERRNO(rmdir(dir)); 457247605Spjd err(-1, "open"); 458247605Spjd } 459224653Sjonathan 460247605Spjd if (cap_enter() == -1) { 461247605Spjd KEEP_ERRNO(unlink(file)); 462247605Spjd KEEP_ERRNO(rmdir(dir)); 463224653Sjonathan err(-1, "cap_enter"); 464247605Spjd } 465224653Sjonathan 466247605Spjd TRY(CAP_READ); 467247605Spjd TRY(CAP_WRITE); 468247605Spjd TRY(CAP_SEEK); 469247605Spjd TRY(CAP_PREAD); 470247605Spjd TRY(CAP_PWRITE); 471247605Spjd TRY(CAP_READ | CAP_WRITE); 472247605Spjd TRY(CAP_PREAD | CAP_PWRITE); 473247605Spjd TRY(CAP_MMAP); 474247605Spjd TRY(CAP_MMAP_R); 475247605Spjd TRY(CAP_MMAP_W); 476247605Spjd TRY(CAP_MMAP_X); 477247605Spjd TRY(CAP_MMAP_RW); 478247605Spjd TRY(CAP_MMAP_RX); 479247605Spjd TRY(CAP_MMAP_WX); 480247605Spjd TRY(CAP_MMAP_RWX); 481247605Spjd TRY(CAP_CREATE | CAP_READ | CAP_LOOKUP); 482247605Spjd TRY(CAP_CREATE | CAP_WRITE | CAP_LOOKUP); 483247605Spjd TRY(CAP_CREATE | CAP_READ | CAP_WRITE | CAP_LOOKUP); 484247605Spjd#ifdef TODO 485247605Spjd TRY(CAP_FEXECVE); 486247605Spjd#endif 487247605Spjd TRY(CAP_FSYNC); 488247605Spjd TRY(CAP_FSYNC | CAP_READ | CAP_LOOKUP); 489247605Spjd TRY(CAP_FSYNC | CAP_WRITE | CAP_LOOKUP); 490247605Spjd TRY(CAP_FSYNC | CAP_READ | CAP_WRITE | CAP_LOOKUP); 491247605Spjd TRY(CAP_FTRUNCATE); 492247605Spjd TRY(CAP_FTRUNCATE | CAP_READ | CAP_LOOKUP); 493247605Spjd TRY(CAP_FTRUNCATE | CAP_WRITE | CAP_LOOKUP); 494247605Spjd TRY(CAP_FTRUNCATE | CAP_READ | CAP_WRITE | CAP_LOOKUP); 495247605Spjd#ifdef TODO 496247605Spjd TRY(CAP_FCHDIR); 497247605Spjd#endif 498247605Spjd TRY(CAP_FCHFLAGS); 499247605Spjd TRY(CAP_FCHOWN); 500247605Spjd TRY(CAP_FCHOWN | CAP_LOOKUP); 501247605Spjd TRY(CAP_FCHMOD | CAP_LOOKUP); 502247605Spjd TRY(CAP_FCNTL); 503247605Spjd#ifdef TODO 504247605Spjd TRY(CAP_FLOCK); 505247605Spjd#endif 506247605Spjd TRY(CAP_FPATHCONF); 507247605Spjd#ifdef TODO 508247605Spjd TRY(CAP_FSCK); 509247605Spjd#endif 510247605Spjd TRY(CAP_FSTAT | CAP_LOOKUP); 511247605Spjd TRY(CAP_FSTATFS); 512247605Spjd TRY(CAP_FUTIMES | CAP_LOOKUP); 513247605Spjd TRY(CAP_LINKAT | CAP_LOOKUP); 514247605Spjd TRY(CAP_MKDIRAT | CAP_LOOKUP); 515247605Spjd TRY(CAP_MKFIFOAT | CAP_LOOKUP); 516247605Spjd TRY(CAP_MKNODAT | CAP_LOOKUP); 517247605Spjd TRY(CAP_SYMLINKAT | CAP_LOOKUP); 518247605Spjd TRY(CAP_UNLINKAT | CAP_LOOKUP); 519247605Spjd /* Rename needs CAP_RENAMEAT on source directory and CAP_LINKAT on destination directory. */ 520247605Spjd TRY(CAP_RENAMEAT | CAP_UNLINKAT | CAP_LOOKUP); 521247605Spjd#ifdef TODO 522247605Spjd TRY(CAP_LOOKUP); 523247605Spjd TRY(CAP_EXTATTR_DELETE); 524247605Spjd TRY(CAP_EXTATTR_GET); 525247605Spjd TRY(CAP_EXTATTR_LIST); 526247605Spjd TRY(CAP_EXTATTR_SET); 527247605Spjd TRY(CAP_ACL_CHECK); 528247605Spjd TRY(CAP_ACL_DELETE); 529247605Spjd TRY(CAP_ACL_GET); 530247605Spjd TRY(CAP_ACL_SET); 531247605Spjd TRY(CAP_ACCEPT); 532247605Spjd TRY(CAP_BIND); 533247605Spjd TRY(CAP_CONNECT); 534247605Spjd TRY(CAP_GETPEERNAME); 535247605Spjd TRY(CAP_GETSOCKNAME); 536247605Spjd TRY(CAP_GETSOCKOPT); 537247605Spjd TRY(CAP_LISTEN); 538247605Spjd TRY(CAP_PEELOFF); 539247605Spjd TRY(CAP_RECV); 540247605Spjd TRY(CAP_SEND); 541247605Spjd TRY(CAP_SETSOCKOPT); 542247605Spjd TRY(CAP_SHUTDOWN); 543247605Spjd TRY(CAP_MAC_GET); 544247605Spjd TRY(CAP_MAC_SET); 545247605Spjd TRY(CAP_SEM_GETVALUE); 546247605Spjd TRY(CAP_SEM_POST); 547247605Spjd TRY(CAP_SEM_WAIT); 548247605Spjd TRY(CAP_POST_EVENT); 549261566Sbrueffer TRY(CAP_EVENT); 550247605Spjd TRY(CAP_IOCTL); 551247605Spjd TRY(CAP_TTYHOOK); 552247605Spjd TRY(CAP_PDGETPID); 553247605Spjd TRY(CAP_PDWAIT); 554247605Spjd TRY(CAP_PDKILL); 555247605Spjd#endif 556224653Sjonathan 557247605Spjd (void)unlinkat(tmpfd, file + strlen("/tmp/"), 0); 558247605Spjd (void)unlinkat(tmpfd, dir + strlen("/tmp/"), AT_REMOVEDIR); 559224653Sjonathan 560224653Sjonathan return (success); 561224653Sjonathan} 562