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