unveil(2)
Hide the whole filesystem except the paths you name, with per-path r/w/x/c permissions.
- Since
- OpenBSD 6.4check your kernel →
- System
- OpenBSD
- Man page
- unveil(2)
Synopsis
#include <unistd.h>
int unveil(const char *path, const char *permissions);Rod's take
pledge decides which syscall families you get; unveil decides which parts of the filesystem exist at all. Name the paths with r, w, x or c, then lock it with unveil(NULL, NULL). Anything you didn't name comes back ENOENT, so a popped process can't even confirm /etc/shadow is there; a named path used the wrong way gets EACCES. You can tighten later, never loosen. Stack it with pledge and a daemon is boxed on both axes in an afternoon. Unveil directories rather than single files; programs misbehave badly when their files quietly vanish.
Flags and arguments that matter
| Name | What it does |
|---|---|
| r | Path available for read operations; corresponds to the pledge "rpath" promise. |
| w | Path available for writes (pledge chown, fattr, wpath); also allows connect(2) to AF_UNIX sockets. |
| x | Path available for execute; corresponds to the pledge "exec" promise. |
| c | Path may be created and removed; corresponds to pledge cpath, dpath and unix. |
| unveil(NULL, NULL) | Lock: no further unveil() calls allowed. A pledge without the "unveil" promise also locks it. |
Errors you will actually hit
| errno | Meaning |
|---|---|
| EPERM | Attempt to increase permissions, path not accessible, or unveil() called after locking. |
| E2BIG | Adding path would exceed the per-process limit on unveiled paths. |
| ENOENT | A directory in path did not exist. |
| EINVAL | Invalid permissions string. |
Example
/* privilege-separated worker: docroot only, then lock */
void worker_setup(const char *docroot) {
if (unveil(docroot, "r") == -1)
err(1, "unveil docroot");
if (unveil(NULL, NULL) == -1)
err(1, "unveil lock");
if (pledge("stdio rpath inet sendfd", NULL) == -1)
err(1, "pledge");
}Trimmed from the full walkthrough in pledge(2) and unveil(2): what OpenBSD figured out that Linux is still catching up to.
Linux equivalent
Linux: Landlock (landlock_create_ruleset(2)) is the closest path-based restriction, though denied paths aren't made invisible. FreeBSD: Capsicum cap_enter(2) with pre-opened directory fds.
The long version, from the console
Sources
Version numbers are read off the cited man pages, not memory. Spot one that's wrong? Tell Rod.
