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> 45da749672SJonathan Anderson #include <stdio.h> 46da749672SJonathan Anderson #include <stdlib.h> 47da749672SJonathan Anderson #include <unistd.h> 48da749672SJonathan Anderson 49da749672SJonathan Anderson #include "cap_test.h" 50da749672SJonathan Anderson 51da749672SJonathan Anderson #define SYSCALL_FAIL(syscall, message) \ 52da749672SJonathan Anderson FAIL("%s:\t%s (rights 0x%jx)", #syscall, message, rights) 53da749672SJonathan Anderson 54da749672SJonathan Anderson /* 55da749672SJonathan Anderson * Ensure that, if the capability had enough rights for the system call to 56da749672SJonathan Anderson * pass, then it did. Otherwise, ensure that the errno is ENOTCAPABLE; 57da749672SJonathan Anderson * capability restrictions should kick in before any other error logic. 58da749672SJonathan Anderson */ 59da749672SJonathan Anderson #define CHECK_RESULT(syscall, rights_needed, succeeded) do { \ 60da749672SJonathan Anderson if ((rights & (rights_needed)) == (rights_needed)) { \ 61da749672SJonathan Anderson if (!(succeeded)) \ 62da749672SJonathan Anderson SYSCALL_FAIL(syscall, "failed"); \ 63da749672SJonathan Anderson } else { \ 64da749672SJonathan Anderson if (succeeded) \ 65da749672SJonathan Anderson FAILX("%s:\tsucceeded when it shouldn't have" \ 66da749672SJonathan Anderson " (rights 0x%jx)", #syscall, rights); \ 67da749672SJonathan Anderson else if (errno != ENOTCAPABLE) \ 68da749672SJonathan Anderson SYSCALL_FAIL(syscall, "errno != ENOTCAPABLE"); \ 69da749672SJonathan Anderson } \ 70da749672SJonathan Anderson } while (0) 71da749672SJonathan Anderson 72da749672SJonathan Anderson /* 73da749672SJonathan Anderson * As above, but for the special mmap() case: unmap after successful mmap(). 74da749672SJonathan Anderson */ 75da749672SJonathan Anderson #define CHECK_MMAP_RESULT(rights_needed) do { \ 76da749672SJonathan Anderson if ((rights & (rights_needed)) == (rights_needed)) { \ 77da749672SJonathan Anderson if (p == MAP_FAILED) \ 78da749672SJonathan Anderson SYSCALL_FAIL(mmap, "failed"); \ 79da749672SJonathan Anderson else \ 80da749672SJonathan Anderson (void)munmap(p, getpagesize()); \ 81da749672SJonathan Anderson } else { \ 82da749672SJonathan Anderson if (p != MAP_FAILED) { \ 83da749672SJonathan Anderson FAILX("%s:\tsucceeded when it shouldn't have" \ 84da749672SJonathan Anderson " (rights 0x%jx)", "mmap", rights); \ 85da749672SJonathan Anderson (void)munmap(p, getpagesize()); \ 86da749672SJonathan Anderson } else if (errno != ENOTCAPABLE) \ 87da749672SJonathan Anderson SYSCALL_FAIL(syscall, "errno != ENOTCAPABLE"); \ 88da749672SJonathan Anderson } \ 89da749672SJonathan Anderson } while (0) 90da749672SJonathan Anderson 91da749672SJonathan Anderson /* 92da749672SJonathan Anderson * Given a file descriptor, create a capability with specific rights and 93da749672SJonathan Anderson * make sure only those rights work. 94da749672SJonathan Anderson */ 95da749672SJonathan Anderson static int 96da749672SJonathan Anderson try_file_ops(int fd, cap_rights_t rights) 97da749672SJonathan Anderson { 98da749672SJonathan Anderson struct stat sb; 99da749672SJonathan Anderson struct statfs sf; 100da749672SJonathan Anderson int fd_cap, fd_capcap; 101da749672SJonathan Anderson ssize_t ssize, ssize2; 102da749672SJonathan Anderson off_t off; 103da749672SJonathan Anderson void *p; 104da749672SJonathan Anderson char ch; 105da749672SJonathan Anderson int ret; 106da749672SJonathan Anderson int success = PASSED; 107da749672SJonathan Anderson 108da749672SJonathan Anderson REQUIRE(fd_cap = cap_new(fd, rights)); 109da749672SJonathan Anderson REQUIRE(fd_capcap = cap_new(fd_cap, rights)); 110da749672SJonathan Anderson CHECK(fd_capcap != fd_cap); 111da749672SJonathan Anderson 112da749672SJonathan Anderson ssize = read(fd_cap, &ch, sizeof(ch)); 113da749672SJonathan Anderson CHECK_RESULT(read, CAP_READ | CAP_SEEK, ssize >= 0); 114da749672SJonathan Anderson 115da749672SJonathan Anderson ssize = pread(fd_cap, &ch, sizeof(ch), 0); 116da749672SJonathan Anderson ssize2 = pread(fd_cap, &ch, sizeof(ch), 0); 117da749672SJonathan Anderson CHECK_RESULT(pread, CAP_READ, ssize >= 0); 118da749672SJonathan Anderson CHECK(ssize == ssize2); 119da749672SJonathan Anderson 120da749672SJonathan Anderson ssize = write(fd_cap, &ch, sizeof(ch)); 121da749672SJonathan Anderson CHECK_RESULT(write, CAP_WRITE | CAP_SEEK, ssize >= 0); 122da749672SJonathan Anderson 123da749672SJonathan Anderson ssize = pwrite(fd_cap, &ch, sizeof(ch), 0); 124da749672SJonathan Anderson CHECK_RESULT(pwrite, CAP_WRITE, ssize >= 0); 125da749672SJonathan Anderson 126da749672SJonathan Anderson off = lseek(fd_cap, 0, SEEK_SET); 127da749672SJonathan Anderson CHECK_RESULT(lseek, CAP_SEEK, off >= 0); 128da749672SJonathan Anderson 129da749672SJonathan Anderson ret = fchflags(fd_cap, UF_NODUMP); 130da749672SJonathan Anderson CHECK_RESULT(fchflags, CAP_FCHFLAGS, ret == 0); 131da749672SJonathan Anderson 132da749672SJonathan Anderson ret = fstat(fd_cap, &sb); 133da749672SJonathan Anderson CHECK_RESULT(fstat, CAP_FSTAT, ret == 0); 134da749672SJonathan Anderson 135da749672SJonathan Anderson p = mmap(NULL, getpagesize(), PROT_READ, MAP_SHARED, fd_cap, 0); 136da749672SJonathan Anderson CHECK_MMAP_RESULT(CAP_MMAP | CAP_READ); 137da749672SJonathan Anderson 138da749672SJonathan Anderson p = mmap(NULL, getpagesize(), PROT_WRITE, MAP_SHARED, fd_cap, 0); 139da749672SJonathan Anderson CHECK_MMAP_RESULT(CAP_MMAP | CAP_WRITE); 140da749672SJonathan Anderson 141da749672SJonathan Anderson p = mmap(NULL, getpagesize(), PROT_EXEC, MAP_SHARED, fd_cap, 0); 142da749672SJonathan Anderson CHECK_MMAP_RESULT(CAP_MMAP | CAP_MAPEXEC); 143da749672SJonathan Anderson 144da749672SJonathan Anderson p = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED, 145da749672SJonathan Anderson fd_cap, 0); 146da749672SJonathan Anderson CHECK_MMAP_RESULT(CAP_MMAP | CAP_READ | CAP_WRITE); 147da749672SJonathan Anderson 148da749672SJonathan Anderson p = mmap(NULL, getpagesize(), PROT_READ | PROT_EXEC, MAP_SHARED, 149da749672SJonathan Anderson fd_cap, 0); 150da749672SJonathan Anderson CHECK_MMAP_RESULT(CAP_MMAP | CAP_READ | CAP_MAPEXEC); 151da749672SJonathan Anderson 152da749672SJonathan Anderson p = mmap(NULL, getpagesize(), PROT_EXEC | PROT_WRITE, MAP_SHARED, 153da749672SJonathan Anderson fd_cap, 0); 154da749672SJonathan Anderson CHECK_MMAP_RESULT(CAP_MMAP | CAP_MAPEXEC | CAP_WRITE); 155da749672SJonathan Anderson 156da749672SJonathan Anderson p = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE | PROT_EXEC, 157da749672SJonathan Anderson MAP_SHARED, fd_cap, 0); 158da749672SJonathan Anderson CHECK_MMAP_RESULT(CAP_MMAP | CAP_READ | CAP_WRITE | CAP_MAPEXEC); 159da749672SJonathan Anderson 160da749672SJonathan Anderson ret = fsync(fd_cap); 161da749672SJonathan Anderson CHECK_RESULT(fsync, CAP_FSYNC, ret == 0); 162da749672SJonathan Anderson 163da749672SJonathan Anderson ret = fchown(fd_cap, -1, -1); 164da749672SJonathan Anderson CHECK_RESULT(fchown, CAP_FCHOWN, ret == 0); 165da749672SJonathan Anderson 166da749672SJonathan Anderson ret = fchmod(fd_cap, 0644); 167da749672SJonathan Anderson CHECK_RESULT(fchmod, CAP_FCHMOD, ret == 0); 168da749672SJonathan Anderson 169da749672SJonathan Anderson /* XXX flock */ 170da749672SJonathan Anderson 171da749672SJonathan Anderson ret = ftruncate(fd_cap, 0); 172da749672SJonathan Anderson CHECK_RESULT(ftruncate, CAP_FTRUNCATE, ret == 0); 173da749672SJonathan Anderson 174da749672SJonathan Anderson ret = fstatfs(fd_cap, &sf); 175da749672SJonathan Anderson CHECK_RESULT(fstatfs, CAP_FSTATFS, ret == 0); 176da749672SJonathan Anderson 177da749672SJonathan Anderson ret = fpathconf(fd_cap, _PC_NAME_MAX); 178da749672SJonathan Anderson CHECK_RESULT(fpathconf, CAP_FPATHCONF, ret >= 0); 179da749672SJonathan Anderson 180da749672SJonathan Anderson ret = futimes(fd_cap, NULL); 181da749672SJonathan Anderson CHECK_RESULT(futimes, CAP_FUTIMES, ret == 0); 182da749672SJonathan Anderson 183da749672SJonathan Anderson /* XXX select / poll / kqueue */ 184da749672SJonathan Anderson 185da749672SJonathan Anderson close (fd_cap); 186da749672SJonathan Anderson return (success); 187da749672SJonathan Anderson } 188da749672SJonathan Anderson 189da749672SJonathan Anderson #define TRY(fd, rights) \ 190da749672SJonathan Anderson do { \ 191da749672SJonathan Anderson if (success == PASSED) \ 192da749672SJonathan Anderson success = try_file_ops(fd, rights); \ 193da749672SJonathan Anderson else \ 194da749672SJonathan Anderson /* We've already failed, but try the test anyway. */ \ 195da749672SJonathan Anderson try_file_ops(fd, rights); \ 196da749672SJonathan Anderson } while (0) 197da749672SJonathan Anderson 198da749672SJonathan Anderson int 199da749672SJonathan Anderson test_capabilities(void) 200da749672SJonathan Anderson { 201da749672SJonathan Anderson int fd; 202da749672SJonathan Anderson int success = PASSED; 203da749672SJonathan Anderson 204da749672SJonathan Anderson fd = open("/tmp/cap_test", O_RDWR | O_CREAT, 0644); 205da749672SJonathan Anderson if (fd < 0) 206da749672SJonathan Anderson err(-1, "open"); 207da749672SJonathan Anderson 208da749672SJonathan Anderson if (cap_enter() < 0) 209da749672SJonathan Anderson err(-1, "cap_enter"); 210da749672SJonathan Anderson 211da749672SJonathan Anderson /* XXX: Really want to try all combinations. */ 212da749672SJonathan Anderson TRY(fd, CAP_READ); 213da749672SJonathan Anderson TRY(fd, CAP_READ | CAP_SEEK); 214da749672SJonathan Anderson TRY(fd, CAP_WRITE); 215da749672SJonathan Anderson TRY(fd, CAP_WRITE | CAP_SEEK); 216da749672SJonathan Anderson TRY(fd, CAP_READ | CAP_WRITE); 217da749672SJonathan Anderson TRY(fd, CAP_READ | CAP_WRITE | CAP_SEEK); 218da749672SJonathan Anderson TRY(fd, CAP_SEEK); 219da749672SJonathan Anderson TRY(fd, CAP_FCHFLAGS); 220da749672SJonathan Anderson TRY(fd, CAP_IOCTL); 221da749672SJonathan Anderson TRY(fd, CAP_FSTAT); 222da749672SJonathan Anderson TRY(fd, CAP_MMAP); 223da749672SJonathan Anderson TRY(fd, CAP_MMAP | CAP_READ); 224da749672SJonathan Anderson TRY(fd, CAP_MMAP | CAP_WRITE); 225da749672SJonathan Anderson TRY(fd, CAP_MMAP | CAP_MAPEXEC); 226da749672SJonathan Anderson TRY(fd, CAP_MMAP | CAP_READ | CAP_WRITE); 227da749672SJonathan Anderson TRY(fd, CAP_MMAP | CAP_READ | CAP_MAPEXEC); 228da749672SJonathan Anderson TRY(fd, CAP_MMAP | CAP_MAPEXEC | CAP_WRITE); 229da749672SJonathan Anderson TRY(fd, CAP_MMAP | CAP_READ | CAP_WRITE | CAP_MAPEXEC); 230da749672SJonathan Anderson TRY(fd, CAP_FCNTL); 231da749672SJonathan Anderson TRY(fd, CAP_EVENT); 232da749672SJonathan Anderson TRY(fd, CAP_KEVENT); 233da749672SJonathan Anderson TRY(fd, CAP_FSYNC); 234da749672SJonathan Anderson TRY(fd, CAP_FCHOWN); 235da749672SJonathan Anderson TRY(fd, CAP_FCHMOD); 236da749672SJonathan Anderson TRY(fd, CAP_FTRUNCATE); 237da749672SJonathan Anderson TRY(fd, CAP_FLOCK); 238da749672SJonathan Anderson TRY(fd, CAP_FSTATFS); 239da749672SJonathan Anderson TRY(fd, CAP_FPATHCONF); 240da749672SJonathan Anderson TRY(fd, CAP_FUTIMES); 241da749672SJonathan Anderson TRY(fd, CAP_ACL_GET); 242da749672SJonathan Anderson TRY(fd, CAP_ACL_SET); 243da749672SJonathan Anderson TRY(fd, CAP_ACL_DELETE); 244da749672SJonathan Anderson TRY(fd, CAP_ACL_CHECK); 245da749672SJonathan Anderson TRY(fd, CAP_EXTATTR_GET); 246da749672SJonathan Anderson TRY(fd, CAP_EXTATTR_SET); 247da749672SJonathan Anderson TRY(fd, CAP_EXTATTR_DELETE); 248da749672SJonathan Anderson TRY(fd, CAP_EXTATTR_LIST); 249da749672SJonathan Anderson TRY(fd, CAP_MAC_GET); 250da749672SJonathan Anderson TRY(fd, CAP_MAC_SET); 251da749672SJonathan Anderson 252da749672SJonathan Anderson /* 253da749672SJonathan Anderson * Socket-specific. 254da749672SJonathan Anderson */ 255da749672SJonathan Anderson TRY(fd, CAP_GETPEERNAME); 256da749672SJonathan Anderson TRY(fd, CAP_GETSOCKNAME); 257da749672SJonathan Anderson TRY(fd, CAP_ACCEPT); 258da749672SJonathan Anderson 259da749672SJonathan Anderson return (success); 260da749672SJonathan Anderson } 261