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