cap_test_capabilities.c revision 248394
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: head/tools/regression/security/cap_test/cap_test_capabilities.c 248394 2013-03-16 23:10:40Z pjd $");
39224653Sjonathan
40224653Sjonathan#include <sys/param.h>
41224653Sjonathan#include <sys/capability.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	/* TODO: openat(O_APPEND) */
188247605Spjd	ret = openat(dfd_cap, "cap_create", O_CREAT | O_RDONLY, 0600);
189247605Spjd	CHECK_RESULT(openat(O_CREATE | O_RDONLY),
190247605Spjd	    CAP_CREATE | CAP_READ | CAP_LOOKUP, ret >= 0);
191247605Spjd	CHECK(ret == -1 || close(ret) == 0);
192247605Spjd	CHECK(ret == -1 || unlinkat(dirfd, "cap_create", 0) == 0);
193247605Spjd	ret = openat(dfd_cap, "cap_create", O_CREAT | O_WRONLY, 0600);
194247605Spjd	CHECK_RESULT(openat(O_CREATE | O_WRONLY),
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);
198247605Spjd	ret = openat(dfd_cap, "cap_create", O_CREAT | O_RDWR, 0600);
199247605Spjd	CHECK_RESULT(openat(O_CREATE | O_RDWR),
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);
214248394Spjd	ret = openat(dfd_cap, "cap_fsync", O_FSYNC | O_WRONLY);
215247605Spjd	CHECK_RESULT(openat(O_FSYNC | O_WRONLY),
216247605Spjd	    CAP_FSYNC | CAP_WRITE | CAP_LOOKUP, ret >= 0);
217247605Spjd	CHECK(ret == -1 || close(ret) == 0);
218248394Spjd	ret = openat(dfd_cap, "cap_fsync", O_FSYNC | O_RDWR);
219247605Spjd	CHECK_RESULT(openat(O_FSYNC | O_RDWR),
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);
226248394Spjd	ret = openat(dfd_cap, "cap_fsync", O_SYNC | O_WRONLY);
227247605Spjd	CHECK_RESULT(openat(O_SYNC | O_WRONLY),
228247605Spjd	    CAP_FSYNC | CAP_WRITE | CAP_LOOKUP, ret >= 0);
229247605Spjd	CHECK(ret == -1 || close(ret) == 0);
230248394Spjd	ret = openat(dfd_cap, "cap_fsync", O_SYNC | O_RDWR);
231247605Spjd	CHECK_RESULT(openat(O_SYNC | O_RDWR),
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
256247605Spjd	/*
257247605Spjd	 * Note: this is not expected to work over NFS.
258247605Spjd	 */
259247605Spjd	ret = fchflags(fd_cap, UF_NODUMP);
260247605Spjd	CHECK_RESULT(fchflags, CAP_FCHFLAGS,
261247605Spjd	    ret == 0 || (is_nfs && errno == EOPNOTSUPP));
262247605Spjd
263247605Spjd#ifdef TODO	/* No such syscalls yet. */
264247605Spjd	ret = openat(dirfd, "cap_fchflagsat", O_CREAT, 0600);
265247605Spjd	CHECK(ret >= 0);
266247605Spjd	CHECK(close(ret) == 0);
267247605Spjd	ret = fchflagsat(dfd_cap, "cap_fchflagsat", UF_NODUMP, 0);
268247605Spjd	CHECK_RESULT(fchflagsat, CAP_FCHFLAGSAT | CAP_LOOKUP, ret == 0);
269247605Spjd	CHECK(unlinkat(dirfd, "cap_fchflagsat", 0) == 0);
270247605Spjd#endif
271247605Spjd
272224653Sjonathan	ret = fchown(fd_cap, -1, -1);
273224653Sjonathan	CHECK_RESULT(fchown, CAP_FCHOWN, ret == 0);
274224653Sjonathan
275247605Spjd	ret = openat(dirfd, "cap_fchownat", O_CREAT, 0600);
276247605Spjd	CHECK(ret >= 0);
277247605Spjd	CHECK(close(ret) == 0);
278247605Spjd	ret = fchownat(dfd_cap, "cap_fchownat", -1, -1, 0);
279247605Spjd	CHECK_RESULT(fchownat, CAP_FCHOWN | CAP_LOOKUP, ret == 0);
280247605Spjd	CHECK(unlinkat(dirfd, "cap_fchownat", 0) == 0);
281247605Spjd
282224653Sjonathan	ret = fchmod(fd_cap, 0644);
283224653Sjonathan	CHECK_RESULT(fchmod, CAP_FCHMOD, ret == 0);
284224653Sjonathan
285247605Spjd	ret = openat(dirfd, "cap_fchmodat", O_CREAT, 0600);
286247605Spjd	CHECK(ret >= 0);
287247605Spjd	CHECK(close(ret) == 0);
288247605Spjd	ret = fchmodat(dfd_cap, "cap_fchmodat", 0600, 0);
289247605Spjd	CHECK_RESULT(fchmodat, CAP_FCHMOD | CAP_LOOKUP, ret == 0);
290247605Spjd	CHECK(unlinkat(dirfd, "cap_fchmodat", 0) == 0);
291247605Spjd
292247605Spjd	ret = fcntl(fd_cap, F_GETFL);
293247605Spjd	CHECK_RESULT(fcntl(F_GETFL), CAP_FCNTL, ret >= 0);
294247605Spjd	ret = fcntl(fd_cap, F_SETFL, ret);
295247605Spjd	CHECK_RESULT(fcntl(F_SETFL), CAP_FCNTL, ret == 0);
296247605Spjd
297224653Sjonathan	/* XXX flock */
298224653Sjonathan
299247605Spjd	ret = fstat(fd_cap, &sb);
300247605Spjd	CHECK_RESULT(fstat, CAP_FSTAT, ret == 0);
301224653Sjonathan
302247605Spjd	ret = openat(dirfd, "cap_fstatat", O_CREAT, 0600);
303247605Spjd	CHECK(ret >= 0);
304247605Spjd	CHECK(close(ret) == 0);
305247605Spjd	ret = fstatat(dfd_cap, "cap_fstatat", &sb, 0);
306247605Spjd	CHECK_RESULT(fstatat, CAP_FSTAT | CAP_LOOKUP, ret == 0);
307247605Spjd	CHECK(unlinkat(dirfd, "cap_fstatat", 0) == 0);
308247605Spjd
309224653Sjonathan	ret = fstatfs(fd_cap, &sf);
310224653Sjonathan	CHECK_RESULT(fstatfs, CAP_FSTATFS, ret == 0);
311224653Sjonathan
312224653Sjonathan	ret = fpathconf(fd_cap, _PC_NAME_MAX);
313224653Sjonathan	CHECK_RESULT(fpathconf, CAP_FPATHCONF, ret >= 0);
314224653Sjonathan
315224653Sjonathan	ret = futimes(fd_cap, NULL);
316224653Sjonathan	CHECK_RESULT(futimes, CAP_FUTIMES, ret == 0);
317224653Sjonathan
318247605Spjd	ret = openat(dirfd, "cap_futimesat", O_CREAT, 0600);
319247605Spjd	CHECK(ret >= 0);
320247605Spjd	CHECK(close(ret) == 0);
321247605Spjd	ret = futimesat(dfd_cap, "cap_futimesat", NULL);
322247605Spjd	CHECK_RESULT(futimesat, CAP_FUTIMES | CAP_LOOKUP, ret == 0);
323247605Spjd	CHECK(unlinkat(dirfd, "cap_futimesat", 0) == 0);
324247605Spjd
325247605Spjd	ret = openat(dirfd, "cap_linkat_src", O_CREAT, 0600);
326247605Spjd	CHECK(ret >= 0);
327247605Spjd	CHECK(close(ret) == 0);
328247605Spjd	ret = linkat(dirfd, "cap_linkat_src", dfd_cap, "cap_linkat_dst", 0);
329247605Spjd	CHECK_RESULT(linkat, CAP_LINKAT | CAP_LOOKUP, ret == 0);
330247605Spjd	CHECK(unlinkat(dirfd, "cap_linkat_src", 0) == 0);
331247605Spjd	CHECK(ret == -1 || unlinkat(dirfd, "cap_linkat_dst", 0) == 0);
332247605Spjd
333247605Spjd	ret = mkdirat(dfd_cap, "cap_mkdirat", 0700);
334247605Spjd	CHECK_RESULT(mkdirat, CAP_MKDIRAT | CAP_LOOKUP, ret == 0);
335247605Spjd	CHECK(ret == -1 || unlinkat(dirfd, "cap_mkdirat", AT_REMOVEDIR) == 0);
336247605Spjd
337247605Spjd	ret = mkfifoat(dfd_cap, "cap_mkfifoat", 0600);
338247605Spjd	CHECK_RESULT(mkfifoat, CAP_MKFIFOAT | CAP_LOOKUP, ret == 0);
339247605Spjd	CHECK(ret == -1 || unlinkat(dirfd, "cap_mkfifoat", 0) == 0);
340247605Spjd
341247605Spjd	ret = mknodat(dfd_cap, "cap_mknodat", S_IFCHR | 0600, 0);
342247605Spjd	CHECK_RESULT(mknodat, CAP_MKNODAT | CAP_LOOKUP, ret == 0);
343247605Spjd	CHECK(ret == -1 || unlinkat(dirfd, "cap_mknodat", 0) == 0);
344247605Spjd
345247605Spjd	/* TODO: renameat(2) */
346247605Spjd
347247605Spjd	ret = symlinkat("test", dfd_cap, "cap_symlinkat");
348247605Spjd	CHECK_RESULT(symlinkat, CAP_SYMLINKAT | CAP_LOOKUP, ret == 0);
349247605Spjd	CHECK(ret == -1 || unlinkat(dirfd, "cap_symlinkat", 0) == 0);
350247605Spjd
351247605Spjd	ret = openat(dirfd, "cap_unlinkat", O_CREAT, 0600);
352247605Spjd	CHECK(ret >= 0);
353247605Spjd	CHECK(close(ret) == 0);
354247605Spjd	ret = unlinkat(dfd_cap, "cap_unlinkat", 0);
355247605Spjd	CHECK_RESULT(unlinkat, CAP_UNLINKAT | CAP_LOOKUP, ret == 0);
356247605Spjd	CHECK(ret == 0 || unlinkat(dirfd, "cap_unlinkat", 0) == 0);
357247605Spjd	ret = mkdirat(dirfd, "cap_unlinkat", 0700);
358247605Spjd	CHECK(ret == 0);
359247605Spjd	ret = unlinkat(dfd_cap, "cap_unlinkat", AT_REMOVEDIR);
360247605Spjd	CHECK_RESULT(unlinkat, CAP_UNLINKAT | CAP_LOOKUP, ret == 0);
361247605Spjd	CHECK(ret == 0 || unlinkat(dirfd, "cap_unlinkat", AT_REMOVEDIR) == 0);
362247605Spjd
363247605Spjd	pollfd.fd = fd_cap;
364247605Spjd	pollfd.events = POLLIN | POLLERR | POLLHUP;
365247605Spjd	pollfd.revents = 0;
366247605Spjd
367224910Sjonathan	ret = poll(&pollfd, 1, 0);
368224910Sjonathan	if (rights & CAP_POLL_EVENT)
369224910Sjonathan		CHECK((pollfd.revents & POLLNVAL) == 0);
370224910Sjonathan	else
371224910Sjonathan		CHECK((pollfd.revents & POLLNVAL) != 0);
372224653Sjonathan
373224910Sjonathan	/* XXX: select, kqueue */
374224910Sjonathan
375247605Spjd	close(fd_cap);
376247605Spjd	close(fd_capcap);
377247605Spjd
378247605Spjd	if (success == -1) {
379247605Spjd		fprintf(stderr, "No tests for rights 0x%jx.\n",
380247605Spjd		    (uintmax_t)rights);
381247605Spjd		success = FAILED;
382247605Spjd	}
383224653Sjonathan	return (success);
384224653Sjonathan}
385224653Sjonathan
386247605Spjd#define TRY(rights) \
387224653Sjonathando { \
388224653Sjonathan	if (success == PASSED) \
389247605Spjd		success = try_file_ops(filefd, dirfd, (rights)); \
390224653Sjonathan	else \
391224653Sjonathan		/* We've already failed, but try the test anyway. */ \
392247605Spjd		try_file_ops(filefd, dirfd, (rights)); \
393224653Sjonathan} while (0)
394224653Sjonathan
395247605Spjd#define	KEEP_ERRNO(...)	do {						\
396247605Spjd	int _saved_errno = errno;					\
397247605Spjd	__VA_ARGS__;							\
398247605Spjd	errno = _saved_errno;						\
399247605Spjd} while (0);
400247605Spjd
401224653Sjonathanint
402224653Sjonathantest_capabilities(void)
403224653Sjonathan{
404247605Spjd	int filefd, dirfd, tmpfd;
405224653Sjonathan	int success = PASSED;
406247605Spjd	char file[] = "/tmp/cap_test.XXXXXXXXXX";
407247605Spjd	char dir[] = "/tmp/cap_test.XXXXXXXXXX";
408224653Sjonathan
409247605Spjd	filefd = mkstemp(file);
410247605Spjd	if (filefd < 0)
411247605Spjd		err(-1, "mkstemp");
412247605Spjd	if (mkdtemp(dir) == NULL) {
413247605Spjd		KEEP_ERRNO(unlink(file));
414247605Spjd		err(-1, "mkdtemp");
415247605Spjd	}
416247605Spjd	dirfd = open(dir, O_RDONLY | O_DIRECTORY);
417247605Spjd	if (dirfd == -1) {
418247605Spjd		KEEP_ERRNO(unlink(file));
419247605Spjd		KEEP_ERRNO(rmdir(dir));
420224653Sjonathan		err(-1, "open");
421247605Spjd	}
422247605Spjd	tmpfd = open("/tmp", O_RDONLY | O_DIRECTORY);
423247605Spjd	if (tmpfd == -1) {
424247605Spjd		KEEP_ERRNO(unlink(file));
425247605Spjd		KEEP_ERRNO(rmdir(dir));
426247605Spjd		err(-1, "open");
427247605Spjd	}
428224653Sjonathan
429247605Spjd	if (cap_enter() == -1) {
430247605Spjd		KEEP_ERRNO(unlink(file));
431247605Spjd		KEEP_ERRNO(rmdir(dir));
432224653Sjonathan		err(-1, "cap_enter");
433247605Spjd	}
434224653Sjonathan
435247605Spjd	TRY(CAP_READ);
436247605Spjd	TRY(CAP_WRITE);
437247605Spjd	TRY(CAP_SEEK);
438247605Spjd	TRY(CAP_PREAD);
439247605Spjd	TRY(CAP_PWRITE);
440247605Spjd	TRY(CAP_READ | CAP_WRITE);
441247605Spjd	TRY(CAP_PREAD | CAP_PWRITE);
442247605Spjd	TRY(CAP_MMAP);
443247605Spjd	TRY(CAP_MMAP_R);
444247605Spjd	TRY(CAP_MMAP_W);
445247605Spjd	TRY(CAP_MMAP_X);
446247605Spjd	TRY(CAP_MMAP_RW);
447247605Spjd	TRY(CAP_MMAP_RX);
448247605Spjd	TRY(CAP_MMAP_WX);
449247605Spjd	TRY(CAP_MMAP_RWX);
450247605Spjd	TRY(CAP_CREATE | CAP_READ | CAP_LOOKUP);
451247605Spjd	TRY(CAP_CREATE | CAP_WRITE | CAP_LOOKUP);
452247605Spjd	TRY(CAP_CREATE | CAP_READ | CAP_WRITE | CAP_LOOKUP);
453247605Spjd#ifdef TODO
454247605Spjd	TRY(CAP_FEXECVE);
455247605Spjd#endif
456247605Spjd	TRY(CAP_FSYNC);
457247605Spjd	TRY(CAP_FSYNC | CAP_READ | CAP_LOOKUP);
458247605Spjd	TRY(CAP_FSYNC | CAP_WRITE | CAP_LOOKUP);
459247605Spjd	TRY(CAP_FSYNC | CAP_READ | CAP_WRITE | CAP_LOOKUP);
460247605Spjd	TRY(CAP_FTRUNCATE);
461247605Spjd	TRY(CAP_FTRUNCATE | CAP_READ | CAP_LOOKUP);
462247605Spjd	TRY(CAP_FTRUNCATE | CAP_WRITE | CAP_LOOKUP);
463247605Spjd	TRY(CAP_FTRUNCATE | CAP_READ | CAP_WRITE | CAP_LOOKUP);
464247605Spjd#ifdef TODO
465247605Spjd	TRY(CAP_FCHDIR);
466247605Spjd#endif
467247605Spjd	TRY(CAP_FCHFLAGS);
468247605Spjd	TRY(CAP_FCHOWN);
469247605Spjd	TRY(CAP_FCHOWN | CAP_LOOKUP);
470247605Spjd	TRY(CAP_FCHMOD | CAP_LOOKUP);
471247605Spjd	TRY(CAP_FCNTL);
472247605Spjd#ifdef TODO
473247605Spjd	TRY(CAP_FLOCK);
474247605Spjd#endif
475247605Spjd	TRY(CAP_FPATHCONF);
476247605Spjd#ifdef TODO
477247605Spjd	TRY(CAP_FSCK);
478247605Spjd#endif
479247605Spjd	TRY(CAP_FSTAT | CAP_LOOKUP);
480247605Spjd	TRY(CAP_FSTATFS);
481247605Spjd	TRY(CAP_FUTIMES | CAP_LOOKUP);
482247605Spjd	TRY(CAP_LINKAT | CAP_LOOKUP);
483247605Spjd	TRY(CAP_MKDIRAT | CAP_LOOKUP);
484247605Spjd	TRY(CAP_MKFIFOAT | CAP_LOOKUP);
485247605Spjd	TRY(CAP_MKNODAT | CAP_LOOKUP);
486247605Spjd	TRY(CAP_SYMLINKAT | CAP_LOOKUP);
487247605Spjd	TRY(CAP_UNLINKAT | CAP_LOOKUP);
488247605Spjd	/* Rename needs CAP_RENAMEAT on source directory and CAP_LINKAT on destination directory. */
489247605Spjd	TRY(CAP_RENAMEAT | CAP_UNLINKAT | CAP_LOOKUP);
490247605Spjd#ifdef TODO
491247605Spjd	TRY(CAP_LOOKUP);
492247605Spjd	TRY(CAP_EXTATTR_DELETE);
493247605Spjd	TRY(CAP_EXTATTR_GET);
494247605Spjd	TRY(CAP_EXTATTR_LIST);
495247605Spjd	TRY(CAP_EXTATTR_SET);
496247605Spjd	TRY(CAP_ACL_CHECK);
497247605Spjd	TRY(CAP_ACL_DELETE);
498247605Spjd	TRY(CAP_ACL_GET);
499247605Spjd	TRY(CAP_ACL_SET);
500247605Spjd	TRY(CAP_ACCEPT);
501247605Spjd	TRY(CAP_BIND);
502247605Spjd	TRY(CAP_CONNECT);
503247605Spjd	TRY(CAP_GETPEERNAME);
504247605Spjd	TRY(CAP_GETSOCKNAME);
505247605Spjd	TRY(CAP_GETSOCKOPT);
506247605Spjd	TRY(CAP_LISTEN);
507247605Spjd	TRY(CAP_PEELOFF);
508247605Spjd	TRY(CAP_RECV);
509247605Spjd	TRY(CAP_SEND);
510247605Spjd	TRY(CAP_SETSOCKOPT);
511247605Spjd	TRY(CAP_SHUTDOWN);
512247605Spjd	TRY(CAP_MAC_GET);
513247605Spjd	TRY(CAP_MAC_SET);
514247605Spjd	TRY(CAP_SEM_GETVALUE);
515247605Spjd	TRY(CAP_SEM_POST);
516247605Spjd	TRY(CAP_SEM_WAIT);
517247605Spjd	TRY(CAP_POST_EVENT);
518247605Spjd	TRY(CAP_POLL_EVENT);
519247605Spjd	TRY(CAP_IOCTL);
520247605Spjd	TRY(CAP_TTYHOOK);
521247605Spjd	TRY(CAP_PDGETPID);
522247605Spjd	TRY(CAP_PDWAIT);
523247605Spjd	TRY(CAP_PDKILL);
524247605Spjd#endif
525224653Sjonathan
526247605Spjd	(void)unlinkat(tmpfd, file + strlen("/tmp/"), 0);
527247605Spjd	(void)unlinkat(tmpfd, dir + strlen("/tmp/"), AT_REMOVEDIR);
528224653Sjonathan
529224653Sjonathan	return (success);
530224653Sjonathan}
531