1da749672SJonathan Anderson /*- 2da749672SJonathan Anderson * Copyright (c) 2009-2011 Robert N. M. Watson 3da749672SJonathan Anderson * Copyright (c) 2011 Jonathan Anderson 4da749672SJonathan Anderson * All rights reserved. 5da749672SJonathan Anderson * 6da749672SJonathan Anderson * Redistribution and use in source and binary forms, with or without 7da749672SJonathan Anderson * modification, are permitted provided that the following conditions 8da749672SJonathan Anderson * are met: 9da749672SJonathan Anderson * 1. Redistributions of source code must retain the above copyright 10da749672SJonathan Anderson * notice, this list of conditions and the following disclaimer. 11da749672SJonathan Anderson * 2. Redistributions in binary form must reproduce the above copyright 12da749672SJonathan Anderson * notice, this list of conditions and the following disclaimer in the 13da749672SJonathan Anderson * documentation and/or other materials provided with the distribution. 14da749672SJonathan Anderson * 15da749672SJonathan Anderson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16da749672SJonathan Anderson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17da749672SJonathan Anderson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18da749672SJonathan Anderson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19da749672SJonathan Anderson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20da749672SJonathan Anderson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21da749672SJonathan Anderson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22da749672SJonathan Anderson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23da749672SJonathan Anderson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24da749672SJonathan Anderson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25da749672SJonathan Anderson * SUCH DAMAGE. 26da749672SJonathan Anderson */ 27da749672SJonathan Anderson 28da749672SJonathan Anderson /* 29da749672SJonathan Anderson * Test whether various operations on capabilities are properly masked for 30da749672SJonathan Anderson * various object types. 31da749672SJonathan Anderson */ 32da749672SJonathan Anderson 33da749672SJonathan Anderson #include <sys/cdefs.h> 34da749672SJonathan Anderson __FBSDID("$FreeBSD$"); 35da749672SJonathan Anderson 36da749672SJonathan Anderson #include <sys/param.h> 37da749672SJonathan Anderson #include <sys/capability.h> 38da749672SJonathan Anderson #include <sys/errno.h> 39da749672SJonathan Anderson #include <sys/mman.h> 40da749672SJonathan Anderson #include <sys/mount.h> 41da749672SJonathan Anderson #include <sys/stat.h> 42da749672SJonathan Anderson 43da749672SJonathan Anderson #include <err.h> 44da749672SJonathan Anderson #include <fcntl.h> 45d6f72489SJonathan Anderson #include <poll.h> 46da749672SJonathan Anderson #include <stdio.h> 47da749672SJonathan Anderson #include <stdlib.h> 48b7f2d66aSJonathan Anderson #include <string.h> 49da749672SJonathan Anderson #include <unistd.h> 50da749672SJonathan Anderson 51da749672SJonathan Anderson #include "cap_test.h" 52da749672SJonathan Anderson 53da749672SJonathan Anderson #define SYSCALL_FAIL(syscall, message) \ 54da749672SJonathan Anderson FAIL("%s:\t%s (rights 0x%jx)", #syscall, message, rights) 55da749672SJonathan Anderson 56da749672SJonathan Anderson /* 57da749672SJonathan Anderson * Ensure that, if the capability had enough rights for the system call to 58da749672SJonathan Anderson * pass, then it did. Otherwise, ensure that the errno is ENOTCAPABLE; 59da749672SJonathan Anderson * capability restrictions should kick in before any other error logic. 60da749672SJonathan Anderson */ 61da749672SJonathan Anderson #define CHECK_RESULT(syscall, rights_needed, succeeded) do { \ 62da749672SJonathan Anderson if ((rights & (rights_needed)) == (rights_needed)) { \ 63da749672SJonathan Anderson if (!(succeeded)) \ 64da749672SJonathan Anderson SYSCALL_FAIL(syscall, "failed"); \ 65da749672SJonathan Anderson } else { \ 66da749672SJonathan Anderson if (succeeded) \ 67da749672SJonathan Anderson FAILX("%s:\tsucceeded when it shouldn't have" \ 68da749672SJonathan Anderson " (rights 0x%jx)", #syscall, rights); \ 69da749672SJonathan Anderson else if (errno != ENOTCAPABLE) \ 70da749672SJonathan Anderson SYSCALL_FAIL(syscall, "errno != ENOTCAPABLE"); \ 71da749672SJonathan Anderson } \ 72d6f72489SJonathan Anderson errno = 0; \ 73da749672SJonathan Anderson } while (0) 74da749672SJonathan Anderson 75da749672SJonathan Anderson /* 76da749672SJonathan Anderson * As above, but for the special mmap() case: unmap after successful mmap(). 77da749672SJonathan Anderson */ 78da749672SJonathan Anderson #define CHECK_MMAP_RESULT(rights_needed) do { \ 79da749672SJonathan Anderson if ((rights & (rights_needed)) == (rights_needed)) { \ 80da749672SJonathan Anderson if (p == MAP_FAILED) \ 81da749672SJonathan Anderson SYSCALL_FAIL(mmap, "failed"); \ 82da749672SJonathan Anderson else \ 83da749672SJonathan Anderson (void)munmap(p, getpagesize()); \ 84da749672SJonathan Anderson } else { \ 85da749672SJonathan Anderson if (p != MAP_FAILED) { \ 86da749672SJonathan Anderson FAILX("%s:\tsucceeded when it shouldn't have" \ 87da749672SJonathan Anderson " (rights 0x%jx)", "mmap", rights); \ 88da749672SJonathan Anderson (void)munmap(p, getpagesize()); \ 89da749672SJonathan Anderson } else if (errno != ENOTCAPABLE) \ 90da749672SJonathan Anderson SYSCALL_FAIL(syscall, "errno != ENOTCAPABLE"); \ 91da749672SJonathan Anderson } \ 92d6f72489SJonathan Anderson errno = 0; \ 93da749672SJonathan Anderson } while (0) 94da749672SJonathan Anderson 95da749672SJonathan Anderson /* 96da749672SJonathan Anderson * Given a file descriptor, create a capability with specific rights and 97da749672SJonathan Anderson * make sure only those rights work. 98da749672SJonathan Anderson */ 99da749672SJonathan Anderson static int 100da749672SJonathan Anderson try_file_ops(int fd, cap_rights_t rights) 101da749672SJonathan Anderson { 102da749672SJonathan Anderson struct stat sb; 103da749672SJonathan Anderson struct statfs sf; 104da749672SJonathan Anderson int fd_cap, fd_capcap; 105da749672SJonathan Anderson ssize_t ssize, ssize2; 106da749672SJonathan Anderson off_t off; 107da749672SJonathan Anderson void *p; 108da749672SJonathan Anderson char ch; 109b7f2d66aSJonathan Anderson int ret, is_nfs; 110d6f72489SJonathan Anderson struct pollfd pollfd; 111da749672SJonathan Anderson int success = PASSED; 112da749672SJonathan Anderson 113b7f2d66aSJonathan Anderson REQUIRE(fstatfs(fd, &sf)); 114b7f2d66aSJonathan Anderson is_nfs = (strncmp("nfs", sf.f_fstypename, sizeof(sf.f_fstypename)) 115b7f2d66aSJonathan Anderson == 0); 116b7f2d66aSJonathan Anderson 117da749672SJonathan Anderson REQUIRE(fd_cap = cap_new(fd, rights)); 118da749672SJonathan Anderson REQUIRE(fd_capcap = cap_new(fd_cap, rights)); 119da749672SJonathan Anderson CHECK(fd_capcap != fd_cap); 120da749672SJonathan Anderson 121d6f72489SJonathan Anderson pollfd.fd = fd_cap; 122d6f72489SJonathan Anderson pollfd.events = POLLIN | POLLERR | POLLHUP; 123d6f72489SJonathan Anderson pollfd.revents = 0; 124d6f72489SJonathan Anderson 125da749672SJonathan Anderson ssize = read(fd_cap, &ch, sizeof(ch)); 126da749672SJonathan Anderson CHECK_RESULT(read, CAP_READ | CAP_SEEK, ssize >= 0); 127da749672SJonathan Anderson 128da749672SJonathan Anderson ssize = pread(fd_cap, &ch, sizeof(ch), 0); 129da749672SJonathan Anderson ssize2 = pread(fd_cap, &ch, sizeof(ch), 0); 130da749672SJonathan Anderson CHECK_RESULT(pread, CAP_READ, ssize >= 0); 131da749672SJonathan Anderson CHECK(ssize == ssize2); 132da749672SJonathan Anderson 133da749672SJonathan Anderson ssize = write(fd_cap, &ch, sizeof(ch)); 134da749672SJonathan Anderson CHECK_RESULT(write, CAP_WRITE | CAP_SEEK, ssize >= 0); 135da749672SJonathan Anderson 136da749672SJonathan Anderson ssize = pwrite(fd_cap, &ch, sizeof(ch), 0); 137da749672SJonathan Anderson CHECK_RESULT(pwrite, CAP_WRITE, ssize >= 0); 138da749672SJonathan Anderson 139da749672SJonathan Anderson off = lseek(fd_cap, 0, SEEK_SET); 140da749672SJonathan Anderson CHECK_RESULT(lseek, CAP_SEEK, off >= 0); 141da749672SJonathan Anderson 142b7f2d66aSJonathan Anderson /* 143b7f2d66aSJonathan Anderson * Note: this is not expected to work over NFS. 144b7f2d66aSJonathan Anderson */ 145da749672SJonathan Anderson ret = fchflags(fd_cap, UF_NODUMP); 146b7f2d66aSJonathan Anderson CHECK_RESULT(fchflags, CAP_FCHFLAGS, 147b7f2d66aSJonathan Anderson (ret == 0) || (is_nfs && (errno == EOPNOTSUPP))); 148da749672SJonathan Anderson 149da749672SJonathan Anderson ret = fstat(fd_cap, &sb); 150da749672SJonathan Anderson CHECK_RESULT(fstat, CAP_FSTAT, ret == 0); 151da749672SJonathan Anderson 152da749672SJonathan Anderson p = mmap(NULL, getpagesize(), PROT_READ, MAP_SHARED, fd_cap, 0); 153da749672SJonathan Anderson CHECK_MMAP_RESULT(CAP_MMAP | CAP_READ); 154da749672SJonathan Anderson 155da749672SJonathan Anderson p = mmap(NULL, getpagesize(), PROT_WRITE, MAP_SHARED, fd_cap, 0); 156da749672SJonathan Anderson CHECK_MMAP_RESULT(CAP_MMAP | CAP_WRITE); 157da749672SJonathan Anderson 158da749672SJonathan Anderson p = mmap(NULL, getpagesize(), PROT_EXEC, MAP_SHARED, fd_cap, 0); 159da749672SJonathan Anderson CHECK_MMAP_RESULT(CAP_MMAP | CAP_MAPEXEC); 160da749672SJonathan Anderson 161da749672SJonathan Anderson p = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED, 162da749672SJonathan Anderson fd_cap, 0); 163da749672SJonathan Anderson CHECK_MMAP_RESULT(CAP_MMAP | CAP_READ | CAP_WRITE); 164da749672SJonathan Anderson 165da749672SJonathan Anderson p = mmap(NULL, getpagesize(), PROT_READ | PROT_EXEC, MAP_SHARED, 166da749672SJonathan Anderson fd_cap, 0); 167da749672SJonathan Anderson CHECK_MMAP_RESULT(CAP_MMAP | CAP_READ | CAP_MAPEXEC); 168da749672SJonathan Anderson 169da749672SJonathan Anderson p = mmap(NULL, getpagesize(), PROT_EXEC | PROT_WRITE, MAP_SHARED, 170da749672SJonathan Anderson fd_cap, 0); 171da749672SJonathan Anderson CHECK_MMAP_RESULT(CAP_MMAP | CAP_MAPEXEC | CAP_WRITE); 172da749672SJonathan Anderson 173da749672SJonathan Anderson p = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE | PROT_EXEC, 174da749672SJonathan Anderson MAP_SHARED, fd_cap, 0); 175da749672SJonathan Anderson CHECK_MMAP_RESULT(CAP_MMAP | CAP_READ | CAP_WRITE | CAP_MAPEXEC); 176da749672SJonathan Anderson 177da749672SJonathan Anderson ret = fsync(fd_cap); 178da749672SJonathan Anderson CHECK_RESULT(fsync, CAP_FSYNC, ret == 0); 179da749672SJonathan Anderson 180da749672SJonathan Anderson ret = fchown(fd_cap, -1, -1); 181da749672SJonathan Anderson CHECK_RESULT(fchown, CAP_FCHOWN, ret == 0); 182da749672SJonathan Anderson 183da749672SJonathan Anderson ret = fchmod(fd_cap, 0644); 184da749672SJonathan Anderson CHECK_RESULT(fchmod, CAP_FCHMOD, ret == 0); 185da749672SJonathan Anderson 186da749672SJonathan Anderson /* XXX flock */ 187da749672SJonathan Anderson 188da749672SJonathan Anderson ret = ftruncate(fd_cap, 0); 189da749672SJonathan Anderson CHECK_RESULT(ftruncate, CAP_FTRUNCATE, ret == 0); 190da749672SJonathan Anderson 191da749672SJonathan Anderson ret = fstatfs(fd_cap, &sf); 192da749672SJonathan Anderson CHECK_RESULT(fstatfs, CAP_FSTATFS, ret == 0); 193da749672SJonathan Anderson 194da749672SJonathan Anderson ret = fpathconf(fd_cap, _PC_NAME_MAX); 195da749672SJonathan Anderson CHECK_RESULT(fpathconf, CAP_FPATHCONF, ret >= 0); 196da749672SJonathan Anderson 197da749672SJonathan Anderson ret = futimes(fd_cap, NULL); 198da749672SJonathan Anderson CHECK_RESULT(futimes, CAP_FUTIMES, ret == 0); 199da749672SJonathan Anderson 200d6f72489SJonathan Anderson ret = poll(&pollfd, 1, 0); 201d6f72489SJonathan Anderson if (rights & CAP_POLL_EVENT) 202d6f72489SJonathan Anderson CHECK((pollfd.revents & POLLNVAL) == 0); 203d6f72489SJonathan Anderson else 204d6f72489SJonathan Anderson CHECK((pollfd.revents & POLLNVAL) != 0); 205d6f72489SJonathan Anderson 206d6f72489SJonathan Anderson /* XXX: select, kqueue */ 207da749672SJonathan Anderson 208da749672SJonathan Anderson close (fd_cap); 209da749672SJonathan Anderson return (success); 210da749672SJonathan Anderson } 211da749672SJonathan Anderson 212da749672SJonathan Anderson #define TRY(fd, rights) \ 213da749672SJonathan Anderson do { \ 214da749672SJonathan Anderson if (success == PASSED) \ 215da749672SJonathan Anderson success = try_file_ops(fd, rights); \ 216da749672SJonathan Anderson else \ 217da749672SJonathan Anderson /* We've already failed, but try the test anyway. */ \ 218da749672SJonathan Anderson try_file_ops(fd, rights); \ 219da749672SJonathan Anderson } while (0) 220da749672SJonathan Anderson 221da749672SJonathan Anderson int 222da749672SJonathan Anderson test_capabilities(void) 223da749672SJonathan Anderson { 224da749672SJonathan Anderson int fd; 225da749672SJonathan Anderson int success = PASSED; 226da749672SJonathan Anderson 227d6f72489SJonathan Anderson fd = open("/tmp/cap_test_capabilities", O_RDWR | O_CREAT, 0644); 228da749672SJonathan Anderson if (fd < 0) 229da749672SJonathan Anderson err(-1, "open"); 230da749672SJonathan Anderson 231da749672SJonathan Anderson if (cap_enter() < 0) 232da749672SJonathan Anderson err(-1, "cap_enter"); 233da749672SJonathan Anderson 234da749672SJonathan Anderson /* XXX: Really want to try all combinations. */ 235da749672SJonathan Anderson TRY(fd, CAP_READ); 236da749672SJonathan Anderson TRY(fd, CAP_READ | CAP_SEEK); 237da749672SJonathan Anderson TRY(fd, CAP_WRITE); 238da749672SJonathan Anderson TRY(fd, CAP_WRITE | CAP_SEEK); 239da749672SJonathan Anderson TRY(fd, CAP_READ | CAP_WRITE); 240da749672SJonathan Anderson TRY(fd, CAP_READ | CAP_WRITE | CAP_SEEK); 241da749672SJonathan Anderson TRY(fd, CAP_SEEK); 242da749672SJonathan Anderson TRY(fd, CAP_FCHFLAGS); 243da749672SJonathan Anderson TRY(fd, CAP_IOCTL); 244da749672SJonathan Anderson TRY(fd, CAP_FSTAT); 245da749672SJonathan Anderson TRY(fd, CAP_MMAP); 246da749672SJonathan Anderson TRY(fd, CAP_MMAP | CAP_READ); 247da749672SJonathan Anderson TRY(fd, CAP_MMAP | CAP_WRITE); 248da749672SJonathan Anderson TRY(fd, CAP_MMAP | CAP_MAPEXEC); 249da749672SJonathan Anderson TRY(fd, CAP_MMAP | CAP_READ | CAP_WRITE); 250da749672SJonathan Anderson TRY(fd, CAP_MMAP | CAP_READ | CAP_MAPEXEC); 251da749672SJonathan Anderson TRY(fd, CAP_MMAP | CAP_MAPEXEC | CAP_WRITE); 252da749672SJonathan Anderson TRY(fd, CAP_MMAP | CAP_READ | CAP_WRITE | CAP_MAPEXEC); 253da749672SJonathan Anderson TRY(fd, CAP_FCNTL); 254d1b6899eSJonathan Anderson TRY(fd, CAP_POST_EVENT); 255d1b6899eSJonathan Anderson TRY(fd, CAP_POLL_EVENT); 256da749672SJonathan Anderson TRY(fd, CAP_FSYNC); 257da749672SJonathan Anderson TRY(fd, CAP_FCHOWN); 258da749672SJonathan Anderson TRY(fd, CAP_FCHMOD); 259da749672SJonathan Anderson TRY(fd, CAP_FTRUNCATE); 260da749672SJonathan Anderson TRY(fd, CAP_FLOCK); 261da749672SJonathan Anderson TRY(fd, CAP_FSTATFS); 262da749672SJonathan Anderson TRY(fd, CAP_FPATHCONF); 263da749672SJonathan Anderson TRY(fd, CAP_FUTIMES); 264da749672SJonathan Anderson TRY(fd, CAP_ACL_GET); 265da749672SJonathan Anderson TRY(fd, CAP_ACL_SET); 266da749672SJonathan Anderson TRY(fd, CAP_ACL_DELETE); 267da749672SJonathan Anderson TRY(fd, CAP_ACL_CHECK); 268da749672SJonathan Anderson TRY(fd, CAP_EXTATTR_GET); 269da749672SJonathan Anderson TRY(fd, CAP_EXTATTR_SET); 270da749672SJonathan Anderson TRY(fd, CAP_EXTATTR_DELETE); 271da749672SJonathan Anderson TRY(fd, CAP_EXTATTR_LIST); 272da749672SJonathan Anderson TRY(fd, CAP_MAC_GET); 273da749672SJonathan Anderson TRY(fd, CAP_MAC_SET); 274da749672SJonathan Anderson 275da749672SJonathan Anderson /* 276da749672SJonathan Anderson * Socket-specific. 277da749672SJonathan Anderson */ 278da749672SJonathan Anderson TRY(fd, CAP_GETPEERNAME); 279da749672SJonathan Anderson TRY(fd, CAP_GETSOCKNAME); 280da749672SJonathan Anderson TRY(fd, CAP_ACCEPT); 281da749672SJonathan Anderson 282da749672SJonathan Anderson return (success); 283da749672SJonathan Anderson } 284