
OpenBSD's unveil() - messe
https://lwn.net/Articles/767137/
======
rwmj
_> There is no reason why your PDF viewer should be able to read your SSH
keys, for example._

Well GNOME's evince can read remote files over at least http/https and ftp,
eg: evince ftp://remote/foo.pdf. I guess it's just a short step to adding
ssh/sftp support. The remote support is implemented using glib's
[https://developer.gnome.org/gio//2.36/GFile.html#g-file-
new-...](https://developer.gnome.org/gio//2.36/GFile.html#g-file-new-for-uri)
which has a generic plugin mechanism for URL schemas, although I didn't look
into whether ssh is already supported.

:-)

~~~
Athas
I'd argue this is a misfeature. Different users have different needs, but I
really like knowing that especially my basic "view a file downloaded from the
Internet"-applications have a limited attack surface, even if that means it
has fewer features. Actually, I'd prefer to have that confidence for _all_ my
applications, possibly except necessarily overcomplicated behemoths like web
browsers or Emacs.

~~~
mikepurvis
Wouldn't it be better to expose these other schemas via FUSE mounts anyway? I
suppose the main issue is one of UX— how do you make a system "Open file/path"
dialog that under the hood is capable of setting up and properly lifecycling
something like an sshfs mount? Almost seems like a thing you'd want support
for at the systemd service level.

OTOH, doing it as a system dialog that runs safely in its own process feels a
lot better to me than building the functionality into every app in a piecemeal
way.

------
amluto
It’s a great concept. But I would NAK the API for Linux:

> Calling unveil() for the first time will "drop a veil" across the entire
> filesystem, rendering the whole thing invisible to the process, with one
> exception...

Sorry, but the semantics of calling it once should be similar to the semantics
of calling it twice. The first call _veils_ and the second call unveils.

Also, on Linux at least, efficiently implementing “you can only see this
subtree” is quite tricky due to races against moving the directories in
question around. I wonder how OpenBSD gets around those races.

~~~
x1798DE
From context I understand the meaning, but what does "NAK" mean here? I'm
guessing it's an acronym?

~~~
kalenx
Not acknowledge, e.g. would advise against and, if were the maintainer, would
reject the change.

ACK and NAK are used quite a lot in communication protocols, and the usage
drift from there.

------
setquk
I like this. It's simple, progressive, delivers a real benefit instantly,
cheap and easy to understand.

Entirely the opposite of SELinux.

------
dmm
I'm working on a problem where I have a single executable that is run as a
dynamic number of long-running processes where I'd like to restrict each
process to a separate directory.

For example, process 1 could only r/w files in "/some/path/1" and process 2
could access "/some/path/2". But the number of processes is dynamic. I might
have two or I might have 100. So I need to be able to specify new directories
at runtime.

I've been looking at the linux hardening mechanisms but I couldn't find a way
to do this with, for example, selinux. With selinux it seems you are mostly
able to create system level policies that applied to whole programs. I haven't
found a way to provide something like a parameter to change the policy for a
given process.

But it looks like this would be very easy to accomplish with unveil(2). This
is very interesting work!

~~~
the_duke
Out of curiosity, what would be the point of restricting child processes to
specific directories?

If I trust a binary to read a directory, what's the point of constraining
specific processes further?

The only use case I could think of is where the binary itself runs untrusted
code from a third party. (e.g. plugins).

~~~
masklinn
The point of pledge(2) (and unveil which builds on and complements pledge(2))
is to protect against the consequences of human failing: if the software has
an accessible exploit, limiting its ability to see the system means the
attacker has a much harder time pivoting into something useful.

That's why pledge(2) and unveil(2) are useful despite _literally being part of
the software it restricts_.

------
notaplumber
An audience member recorded a recent talk about OpenBSD unveil(2), given last
month.

[https://www.youtube.com/watch?v=wdeFQJXd_II&list=PLbx1j8Pb6u...](https://www.youtube.com/watch?v=wdeFQJXd_II&list=PLbx1j8Pb6u3CMxFPYESt2eBoLh8CFdCrd)

------
bubblethink
The simplicity, and the fact that it forces conscious choices at
source/compile time are great, but I wonder if it comes at the cost of
flexibility. For anything sufficiently complex, you'll need some configuration
after deployment, which selinux handles through policies and labels. In case
of unveil, are the file paths hardcoded ? As a simple example, you build
chromium with write access to ~/Downloads. What happens if you want to write
to ~/New_Downloads ?

~~~
brynet
The paths that you unveil(2) can be hardcoded, set from a command line flag,
or parsed in from a config file. What can't be done, however, is arbitrary
file access at runtime, as there would be no point at which you could lock
unveil or drop the equivalent pledge promise.

For the chrome case, you can currently only access ~/Downloads. This involves
changing our habits as /users/. If you need to access files from outside this
directory, you need to open a terminal and copy/move files there yourself.

That said, for now.. the chromium package does actually have a external unveil
policy in /etc/chromium/unveil.* for each process type (main, gpu, renderer,
etc). But that won't necessarily always be there.

~~~
masklinn
> For the chrome case, you can currently only access ~/Downloads. This
> involves changing our habits as /users/. If you need to access files from
> outside this directory, you need to open a terminal and copy/move files
> there yourself.

That is where Apple's approach is probably better: have an entirely separate
process bridging the software and specific tasks. The bridge process should be
easier to audit (and can have its own restrictions and sanboxing e.g. the only
things it needs are to communicate over a socket and see the filesystem), and
the browser never gets to see the filesystem.

~~~
brynet
OpenBSD was already using pledge(2) to protect the different chrome process
types, many of them can't access the filesystem directly. Chrome actually has
a privsep design, IPC, fd passing over Unix sockets etc.

For example, the GPU and plugin processes can only act on file descriptors
received this way:

[https://github.com/openbsd/ports/blob/master/www/chromium/pa...](https://github.com/openbsd/ports/blob/master/www/chromium/patches/patch-
services_service_manager_sandbox_openbsd_sandbox_openbsd_cc#L227)

But this alone doesn't protect this "bridge" process itself from accessing the
filesystem, this includes your SSH keys, which is where unveil() comes in.

~~~
masklinn
> But this alone doesn't protect this "bridge" process itself from accessing
> the the filesystem

Of course not, but the point is that the bridge process only comes in contact
with the user and a software invoking it, and its sole purpose is to pick a
file on the filesystem.

Including your ssh pubkey if that's what you need to select/upload.

~~~
brynet
Perhaps that's the case, and that's all it's doing, but at least for chromium,
it's the main browser process itself currently handling the Open/Save File
dialog box.

------
mysterypie
> _There is no reason why your PDF viewer should be able to read your SSH
> keys, for example._

Is there any way to make use of unveil() at the _command line_ to accomplish
the above? To clarify, I suppose unveil() is not intended to be used that way,
but will there be some sort of wrapper that allows us to restrict a third-
party already-compiled program to certain directories only?

~~~
throwaway2048
The entire point of unveil() and pledge() is tight integration with the
program itself, the programmers know best what their program requires, and
unveils/pledges are typically invoked AFTER all the startup junk executes
(such as reading in libraries, config file dances, etc).

To restrict it before the program even starts would require a huge amount of
what amounts to spurious unveils once the program starts its main operating
loop, and really misses the point of the abstraction its trying to create.

This is exactly where approaches like selinux fail, they rely on static
distro/user defined policy that can only ever be set from before the program
starting, and thus must encapsulate everything the program might ever try to
do. With unveil and pledge, the program itself indicates what each branch
needs to do, and can much much more tightly restrict itself.

~~~
dvfjsdhgfv
I don't think they fail. Usually you profile the program first, and once you
set up the rules, a deviation might get blocked. If you want to define what
the program is definitely not authorized to do, this is the way to go.

Using the unveil() is a completely different approach that should yield less
false positive and involve less hassle. There is just one caveat: you need to
make sure the binary hasn't been tampered with. If someone replaced/modified
it, SELinux will block unauthorized behavior, whereas counting on unveil()
still working in this case might let you down.

~~~
Someone
_”If someone replaced /modified it […] counting on unveil() still working […]
might let you down.”_

 _Might_ is an understatement. If somebody changed what you run, there’s no
guarantee at all that it even still _calls_ unveil.

However, I would think chances that your system is set up such that third
parties can tamper with your binaries are about equal to those that the system
setup allows them to ‘tweak’ your SELinux configurations.

------
ikarus
Is it just me or are they reinventing the plan9 vfs and it’s mount, bind and
rfork calls?

------
makecheck
It would make more sense for process spawns to specify veiling behavior, such
that unveil() only unveils things.

