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