|
|
Subscribe / Log in / New account

OpenBSD's unveil()

Please consider subscribing to LWN

Subscriptions are the lifeblood of LWN.net. If you appreciate this content and would like to see more of it, your subscription will help to ensure that LWN continues to thrive. Please visit this page to join up and keep LWN on the net.

By Jonathan Corbet
September 28, 2018
One of the key aspects of hardening the user-space side of an operating system is to provide mechanisms for restricting which parts of the filesystem hierarchy a given process can access. Linux has a number of mechanisms of varying capability and complexity for this purpose, but other kernels have taken a different approach. Over the last few months, OpenBSD has inaugurated a new system call named unveil() for this type of hardening that differs significantly from the mechanisms found in Linux.

The value of restricting access to the filesystem, from a security point of view, is fairly obvious. A compromised process cannot exfiltrate data that it cannot read, and it cannot corrupt files that it cannot write. Preventing unwanted access is, of course, the purpose of the permissions bits attached to every file, but permissions fall short in an important way: just because a particular user has access to a given file does not necessarily imply that every program run by that user should also have access to that file. There is no reason why your PDF viewer should be able to read your SSH keys, for example. Relying on just the permission bits makes it easy for a compromised process to access files that have nothing to do with that process's actual job.

In a Linux system, there are many ways of trying to restrict that access; that is one of the purposes behind the Linux security module (LSM) architecture, for example. The SELinux LSM uses a complex matrix of labels and roles to make access-control decisions. The AppArmor LSM, instead, uses a relatively simple table of permissible pathnames associated with each application; that approach was highly controversial when AppArmor was first merged, and is still looked down upon by some security developers. Mount namespaces can be used to create a special view of the filesystem hierarchy for a set of processes, rendering much of that hierarchy invisible and, thus, inaccessible. The seccomp mechanism can be used to make decisions on attempts by a process to access files, but that approach is complex and error-prone. Yet another approach can be seen in the Qubes OS distribution, which runs applications in virtual machines to strictly control what they can access.

Compared to many of the options found in Linux, unveil() is an exercise in simplicity. This system call, introduced in July, has this prototype:

    int unveil(const char *path, const char *permissions);

A process that has never called unveil() has full access to the filesystem hierarchy, modulo the usual file permissions and any restrictions that may have been applied by calling pledge(). 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: the file or directory hierarchy starting at path will be accessible with the given permissions. The permissions string can contain any of "r" for read access, "w" for write, "x" for execute, and "c" for the ability to create or remove the path.

Subsequent calls to unveil() will make other parts of the filesystem hierarchy accessible; the unveil() system call itself still has access to the entire hierarchy, so there is no problem with unveiling distinct subtrees that are, until the call is made, invisible to the process. If one unveil() call applies to a subtree of a hierarchy unveiled by another call, the permissions associated with the more specific call apply.

Calling unveil() with both arguments as null will block any further calls, setting the current view of the filesystem in stone. Calls to unveil() can also be blocked using pledge(). Either way, once the view of the filesystem has been set up appropriately, it is possible to lock it so that the process cannot expand its access in the future should it be taken over and turn hostile.

unveil() thus looks a bit like AppArmor, in that it is a path-based mechanism for restricting access to files. In either case, one must first study the program in question to gain a solid understanding of which files it needs to access before closing things down, or the program is likely to break. One significant difference (beyond the other sorts of behavior that AppArmor can control) is that AppArmor's permissions are stored in an external policy file, while unveil() calls are made by the application itself. That approach keeps the access rules tightly tied to the application and easy for the developers to modify, but it also makes it harder for system administrators to change them without having to rebuild the application from source.

One can certainly aim a number of criticisms at unveil() — all of the complaints that have been leveled at path-based access control and more. But the simplicity of unveil() brings a certain kind of utility, as can be seen in the large number of OpenBSD applications that are being modified to use it. OpenBSD is gaining a base level of protection against unintended program behavior; while it is arguably possible to protect a Linux system to a much greater extent, the complexity of the mechanisms involved keeps that from happening in a lot of real-world deployments. There is a certain kind of virtue to simplicity in security mechanisms.

Index entries for this article
SecurityHardening
SecurityOpenBSD


(Log in to post comments)

OpenBSD's unveil()

Posted Sep 28, 2018 20:58 UTC (Fri) by bangert (subscriber, #28342) [Link]

the article could have pointed out one of the major benefits of this: the application can take into consideration the running configuration of this specific invocation. e.g. if you want the process to write output to a file given as argument on the command line, the apropriate unvail() calls can be made by the process before invoking the possibly dangerous html parser. AFAICT this is not possible with apparmor/SELinux (or not practical).

OpenBSD's unveil()

Posted Sep 29, 2018 3:54 UTC (Sat) by TheJH (subscriber, #101155) [Link]

This is a large part of the motivation for Mickaël Salaün's Landlock project: Wanting to allow unprivileged applications to apply custom fine-grained filesystem access policies to themselves.

OpenBSD's unveil()

Posted Sep 29, 2018 9:54 UTC (Sat) by roc (subscriber, #30627) [Link]

A lot of similar effects can be achieved by opening the files you care about before you drop privileges.

OpenBSD's unveil()

Posted Sep 29, 2018 10:46 UTC (Sat) by mpr22 (subscriber, #60784) [Link]

Dropping privileges protects root's files from roc.

It doesn't let roc restrict the filesystem access enjoyed by an internet-facing process run by roc.

OpenBSD's unveil()

Posted Sep 29, 2018 19:33 UTC (Sat) by roc (subscriber, #30627) [Link]

By "dropping privileges" I meant more general privilege restriction such as installing a seccomp filter and entering a restricted filesystem namespace.

OpenBSD's unveil()

Posted Sep 30, 2018 0:08 UTC (Sun) by justincormack (subscriber, #70439) [Link]

entering a filesystem namespace is a privileged operation though, potentially you can use user namespaces to workaround that but that adds a lot of complexity.

OpenBSD's unveil()

Posted Sep 30, 2018 2:01 UTC (Sun) by roc (subscriber, #30627) [Link]

Yeah, it is complex, but it's what the Firefox and Chrome sandboxes do. I've even written standalone code to do it.

OpenBSD's unveil()

Posted Sep 28, 2018 21:38 UTC (Fri) by Cyberax (✭ supporter ✭, #52523) [Link]

It should be noted that an application with root access can load a custom AppArmor policy itself.

OpenBSD's unveil()

Posted Sep 29, 2018 0:58 UTC (Sat) by k8to (subscriber, #15413) [Link]

Ignorant question :

Are you identifying that programs can opt into specialized apparmor protection, or are you identifying that root programs can opt out of apparmor protection?

OpenBSD's unveil()

Posted Sep 29, 2018 4:46 UTC (Sat) by Cyberax (✭ supporter ✭, #52523) [Link]

Unconfied root programs can opt-in into additional protection. As an example of it, you can check Apache's mod_apparmor: https://gitlab.com/apparmor/apparmor/wikis/mod_apparmor_e...

OpenBSD's unveil()

Posted Oct 13, 2018 2:07 UTC (Sat) by jrjohansen (subscriber, #75010) [Link]

And soon (hopefully 4.21) if an application policy namespace is enabled for the application, it will be able to load policy of its own. The application policy won't interfere with system policy, it will be stacked with it so both policies will be applied.

Once this lands applications will be able to do something very similar to unveil as long as they use the api (its not a syscall in apparmor).

unveil("/", "rwxc")

Posted Sep 28, 2018 22:37 UTC (Fri) by vstinner (subscriber, #42675) [Link]

An exploit can still call unveil("/", "rwxc") to get back access to the filesystem, right? Or is it possible to block further calls to unveil()? I'm not saying that unveil() is useless, I'm just trying to understand its limitations.

unveil("/", "rwxc")

Posted Sep 28, 2018 22:54 UTC (Fri) by khim (subscriber, #9252) [Link]

Have you actually read the article? Specifically this part:

Calling unveil() with both arguments as null will block any further calls, setting the current view of the filesystem in stone.

unveil("/", "rwxc")

Posted Sep 29, 2018 6:37 UTC (Sat) by pr1268 (subscriber, #24648) [Link]

I believe the OP was curious about calling unveil("/", "rwxc") after someone else had previously unveil()ed a specific subdirectory in the same program.

If one were to unveil("/", "rwxc") and subsequently unveil(NULL, NULL) then that would give you the whole filesystem. Set in stone! (Modulo the existing security bits, etc.)

Why anyone would want to do that (other than for the obvious nefarious reasons) is beyond me.

unveil("/", "rwxc")

Posted Sep 29, 2018 8:36 UTC (Sat) by MarcB (subscriber, #101804) [Link]

Obviously, unveil() needs to be set up before a program is compromised. And, of course, attempts to call unveil() after it has been finalized must report an error.

unveil("/", "rwxc")

Posted Sep 29, 2018 13:53 UTC (Sat) by WolfWings (subscriber, #56790) [Link]

The point of unveil() is that before you load any internet-sourced content or otherwise potentially nefarious material you've already called unveil(NULL, NULL) to prevent any unknown bugs from exposing extra files.

Or in simpler terms: You should be calling unveil(NULL, NULL) as far before any likely compromise as possible, that's the whole point of the 'set and bake' approach unveil() is using.

Is it limited? Yup.

Is it also a huge step forward allowing programs that use it to provide a pretty decent 'baseline' of security? Boy howdy!

Security is never a silver bullet, but this is a very developer-oriented additional layer for the onion.

unveil("/", "rwxc")

Posted Sep 29, 2018 21:34 UTC (Sat) by vstinner (subscriber, #42675) [Link]

Oh right, that's perfect. I missed that part ;-)

OpenBSD's unveil()

Posted Sep 29, 2018 5:33 UTC (Sat) by flussence (subscriber, #85566) [Link]

The real value of this syscall is that it'll become a new sane default across the entire OS, as was the case with pledge().

AFAIK, Linux offers no comparable “seatbelts and airbags” API for application code to opt in to. Seccomp exists now, but it's far too hard for the average developer to use, and so they don't. And the existing security subsystems seem more focused on creating sysadmin job security than system security; it's a full-time effort to understand and configure some of them. This whole situation really ought to change.

OpenBSD's unveil()

Posted Sep 29, 2018 18:17 UTC (Sat) by felixfix (subscriber, #242) [Link]

You say " The real value of this syscall is that it'll become a new sane default across the entire OS", which I do not understand. Do you mean other processes too? In the future?

Near as I understand this syscall, it applies to this program, now, only. It has no affect on other programs. It has no effect on other invocations of this program, now or in the future.

OpenBSD's unveil()

Posted Sep 30, 2018 2:27 UTC (Sun) by k8to (subscriber, #15413) [Link]

Well, in the case of OpenBSD, you can be pretty sure they'll be adding it to all the programs they ship, over time.

OpenBSD's unveil()

Posted Sep 29, 2018 5:51 UTC (Sat) by Cyberax (✭ supporter ✭, #52523) [Link]

I've been playing around with seccomp, trying to create at least a subset of pledge(). Yeah, not going well. I seriously think that Linux should just port pledge() and be done with it.

unveil() though can be implemented as an LSM fairly easily.

OpenBSD's unveil()

Posted Sep 29, 2018 9:59 UTC (Sat) by roc (subscriber, #30627) [Link]

Is pledge() really flexible enough to sandbox arbitrary third-party code? I'm a bit suspicious that the community that wrote pledge() also maintains almost everything using pledge(), and that pledge() seems specifically designed around the needs of the official OpenBSD userspace.

OpenBSD's unveil()

Posted Sep 29, 2018 11:34 UTC (Sat) by ralfh (subscriber, #21380) [Link]

The OpenBSD ports tree contains patches for a number of applications to make use of pledge(). E.g. bzip2, p7zip, mupdf, chromium, iridium, firefox, i3 to name a few. Also chromium and iridium recently got experimental patches for unveil().

So pledge() and unveil() seem flexible enough for applications beyond OpenBSD base userspace.

OpenBSD's unveil()

Posted Oct 5, 2018 15:59 UTC (Fri) by ortalo (guest, #4654) [Link]

Admittedly, OpenBSD applications are only an empirical validation of the significance of pledge() possibilities (especially the possible "promises" validation). But that's still a significant one IMHO, because it addresses a real code base.

OpenBSD's unveil()

Posted Sep 29, 2018 23:54 UTC (Sat) by Cyberax (✭ supporter ✭, #52523) [Link]

pledge() is far LESS flexible than seccomp filters and that's the point. It has a lot of pre-made policies that cover most of the needed functionality for services. If you need something special - you're out of luck.

OpenBSD's unveil()

Posted Sep 30, 2018 0:11 UTC (Sun) by justincormack (subscriber, #70439) [Link]

seccomp is also inflexible as you cannot examine arguments that are pointers, so it is actually very limiting in practise.

OpenBSD's unveil()

Posted Oct 5, 2018 16:05 UTC (Fri) by ortalo (guest, #4654) [Link]

According to Theo, that is a feature of pledge()... and a problem with seccomp. ;-)
There was an interesting presentation on that at EuroBSDCon2017: https://youtu.be/fYgG0ds2_UQ

OpenBSD's unveil()

Posted Oct 12, 2018 6:14 UTC (Fri) by oridb (subscriber, #85941) [Link]

It's successfully been used to sandbox many ports. In addition to easy things like archivers, it's been used successfully with large third party applications like Chrome and Firefox.

OpenBSD's unveil()

Posted Oct 14, 2018 19:27 UTC (Sun) by glaubitz (subscriber, #96452) [Link]

Yes, but how many upstream projects are using pledge() in their code such that OpenBSD ports doesn't need to carry any patches?

OpenBSD's unveil()

Posted Sep 29, 2018 18:21 UTC (Sat) by felixfix (subscriber, #242) [Link]

Does these self-imposed restrictions apply through fork() and exec()?

If so, it seems an interesting means to have a security wrapper, where you list all the path and permission restrictions you want to apply, plus the command you want to run and its args.

runprot /tmp/input r /tmp/output w -- /usr/bin/transmorgify -i /tmp/input -o /tmp/output

OpenBSD's unveil()

Posted Sep 29, 2018 22:34 UTC (Sat) by amarao (subscriber, #87073) [Link]

You don't need to create this program. It's exist already. It's called firejail, and God, I love it. Both per-application profiles and command line ad-hoc modes are available.

OpenBSD's unveil()

Posted Sep 29, 2018 23:02 UTC (Sat) by felixfix (subscriber, #242) [Link]

Thanks -- looks great --- I might be smart, but I make up for it by being late :-)

OpenBSD's unveil()

Posted Oct 4, 2018 11:44 UTC (Thu) by jsegitz (subscriber, #102650) [Link]

nsjail is also very neat

OpenBSD's unveil()

Posted Sep 29, 2018 23:04 UTC (Sat) by karkhaz (subscriber, #99844) [Link]

The article says that

> Subsequent calls to unveil() will make other parts of the filesystem hierarchy accessible

but it seems that you can also use it to make subdirectories of already-unveiled paths _inaccessible_, by passing in an empty string as the second argument. This could be used as follows, for example:

unveil("/home/karkhaz", "rwxc");
unveil("/home/karkhaz/.gnupg", "");
unveil("/home/karkhaz/.ssh", "");
unveil(NULL, NULL);

This usage isn't made explicit in the man page either, though the man page does state that

> A path that is a directory will enable all filesystem access underneath path using permissions if and only if no more specific matching unveil() exists at a lower level

so I guess that that usage should work (but I don't have a system to test it on).

OpenBSD's unveil()

Posted Sep 30, 2018 2:30 UTC (Sun) by k8to (subscriber, #15413) [Link]

I think it's just hadr to succinctly and clearly describe, because the call does three things.

1 - switches on path restriction
2 - selects a path to not be restricted
3 - optionally prevents further items of type 2

Further the name "Unveil" implies part 2 but not parts 1 or 3.

I'm not sure this is really that bad, but it's kind of clunky conceptually, and thus the confusion and imperfect descriptions.

In some kind of "design the universe entirely at once" scenario, I could imagine many facilities having a common call for step 3, the "lock this type of thing" call.

OpenBSD's unveil()

Posted Sep 30, 2018 2:56 UTC (Sun) by Cyberax (✭ supporter ✭, #52523) [Link]

You can do 3. by calling pledge(). Perhaps they should have created another libc to just block further unveil() calls but that's kinda redundant.

OpenBSD's unveil()

Posted Oct 2, 2018 12:06 UTC (Tue) by mfuzzey (subscriber, #57966) [Link]

>unveil("/home/karkhaz", "rwxc");
>unveil("/home/karkhaz/.gnupg", "");
>unveil("/home/karkhaz/.ssh", "");
>unveil(NULL, NULL);

I think this illustrates one of the shortcomings of unveil().

Why should applications have to explicitly exclude files that they know nothing about?

If the application is a text editor for example it will need access to all the files in your home directory.
But if it has to specifically know about directories with sensitive files belonging to other applications what happens when a new one comes along?

Having the user or administrator configure per user or system wide exclusions is fine but in each app?

If the application gets the files it is supposed to work on upfront (from the command line for example) then unveil() could be applied quite well on *just* the files to be edited.
But if the user chooses the files at run time (eg through a GUI) and wants to open a new file at any time it won't work.
I suppose it could use a per tab process like Chrome, but then how do you do "search in all open files"?

I presume also this applies to all filesystem access performed directly by the application or by some library it uses.
So, consider the case of a media player. It could use unveil() before opening the media to allow access to the media file but what happens if playing the media involves loading codec plugins? That would mean the application would have to know about all the plugins that could be used. That's fine if the application does everything itself, but what if it uses a media framework library like gstreamer? I guess the library could do the uveil in an "init" function but that starts to get intrusive quickly.

OpenBSD's unveil()

Posted Oct 2, 2018 12:47 UTC (Tue) by karkhaz (subscriber, #99844) [Link]

> Having the user or administrator configure per user or system wide exclusions is fine but in each app?

Right, I assumed this was partially the intended use case. The administrator (or the distribution) specify things that are globally-sensitive.

I don't use a desktop environment, but I assume GNOME and KDE have some sort of application launcher (equivalent to macOS Finder or Dock or Windows Start Menu). Assuming that these application launchers exec/posix_spawn the applications, and assuming that child processes inherit the veil (neither the article nor man page mention whether this is the case), then the desktop environment could be used to configure block/allow rulesets.

Tangentially, I am curious about whether child processes do inherit the veil. Either answer seems like a bad idea to me. If children do not inherit, then this seems like a trivial way to escape the security measure. OTOH, if children do inherit, then they must also inherit the lock enabled by calling unveil(NULL, NULL) (otherwise the veil is useless), therefore preventing them from veiling or unveiling more specific paths. This means that children performing more specialised tasks nevertheless are forced to keep the more lax permissions of their parent.

OpenBSD's unveil()

Posted Oct 2, 2018 15:09 UTC (Tue) by itvirta (guest, #49997) [Link]

> This means that children performing more specialised tasks nevertheless are forced to keep the more lax permissions of their parent.

It's the same as with dropping privileges through setuid() and such: you need to fork off the privileged
part of the program before before permanently locking everything up.

OpenBSD's unveil()

Posted Oct 12, 2018 6:19 UTC (Fri) by oridb (subscriber, #85941) [Link]

This is where privsep steps in. You put the file selector into its own process, and it sends an imsg over to the editor with the open FD.

OpenBSD's unveil()

Posted Oct 2, 2018 12:58 UTC (Tue) by dgm (subscriber, #49227) [Link]

> Why should applications have to explicitly exclude files that they know nothing about?

They shouldn't. You answer yourself later:

> If the application gets the files it is supposed to work on upfront (from the command line for example) then unveil() could be applied quite well on *just* the files to be edited.

Aha.

> I suppose it could use a per tab process like Chrome, but then how do you do "search in all open files"?

You use IPC.

> Having the user or administrator configure per user or system wide exclusions is fine but in each app?

Most systems do not have an admin. Most users don't know what files are there in their systems.

> what if it uses a media framework library like gstreamer?

You could run the media decoding in a separate process, and IPC for buffer passing.

OpenBSD's unveil()

Posted Oct 2, 2018 14:46 UTC (Tue) by NAR (guest, #1313) [Link]

If the application is a text editor for example it will need access to all the files in your home directory.

Not necessarily. The editor might be confined to the ~/Documents directory and to its own ~/.editor-name directory for its configuration settings. It's up to the user to keep documents there. I'm not sure it's a good idea though.

OpenBSD's unveil()

Posted Oct 2, 2018 16:51 UTC (Tue) by james (subscriber, #1325) [Link]

For pretty much anything other than a text editor, you could veil all hidden files and directories that the application didn't know about.

That would cover .gnupg and .ssh, and a lot of other private information (passwords in email client config files, for example).

There's a good argument that hidden files and directories are supposed to be hidden from programs that don't know about them. The semantic link between "veiling" and "hiding" illustrates that (and the link with "private").

OpenBSD's unveil()

Posted Oct 2, 2018 15:57 UTC (Tue) by epa (subscriber, #39769) [Link]

There have been systems to sandbox applications with a special "open file" dialogue box which then grants the application permission to that file when the user selects it. (So the dialogue box runs in a separate process or is otherwise more privileged than the rest of the application.) This is an excellent idea, in my view, but it's hard to push it out to all GUI applications on the system when they are using a wide variety of different toolkits.

OpenBSD's unveil()

Posted Oct 12, 2018 22:49 UTC (Fri) by ssokolow (guest, #94568) [Link]

Flatpak's portals system (based on D-Bus) is working to do this and has been gaining quite a bit of traction.

It's trivial to retrofit onto existing applications because no special APIs are required to access files for which permission has been granted. The portal host just mounts the files into the sandbox (currently, by exposing them through a FUSE filesystem, according to the docs) before the D-Bus call returns the in-sandbox path.

Last I checked:
  • Apps:
    • GTK+: As of GTK+ 3.22, GtkFileChooserNative has been retrofitted to detect the existence of org.freedesktop.portal.FileChooser on the D-Bus session bus and transparently indirect through it.

      ...though some applications will need to switch from GtkFileChooser (intended for customizations platform-native dialogs don't offer) to GtkFileChooserNative.

    • Qt: A QPA (Qt Platform Abstraction) plugin has been written for Qt 5 that produces similar behaviour in QFileDialog. (Which should, in theory, support all Qt 5 versions back to 5.0.)

    • Electron: There's discussion about getting a native Qt file dialog into Electron which is being steered in the direction of just switching the existing GtkFileChooser code to use GtkFileChooserNative since that'll result in a KDE file picker if run on a KDE desktop recent enough to include xdg-desktop-portal-kde.

    • Other: In order to feel more native, pretty much everything finds some way to delegate to Qt or GTK+ for file dialogs these days and, failing that, the raw D-Bus API couldn't be a simpler retrofit.

  • Desktops:
    • KDE: Plasma releases starting with 5.10 bundle a KDE backend for the portal host.

    • GTK-based desktops: The GTK+ backend for the portal host appears to only depend on GNOME is the sense that, if a GTK+-based desktop doesn't expose D-Bus APIs like org.gnome.Shell.Screenshot, you won't get those features.

    • Other Environments: xdg-desktop-portal is designed to unify as much code as possible to make new backends simple and easy to maintain.

      I also suspect that the KDE portal backend could be used by something like LXQt, given that it appears to only rely on KDE as a build dependency and specifically only on KCoreAddons, KI18n, and KNotifications.

  • Snappy: I don't have a citation somewhere, but I remember reading that, thanks to Flatpak's modular design and their uptake by toolkits and desktops, Snappy is or will be also using Flatpak portals. (Which makes sense. Flatpak began as a FreeDesktop.org project named xdg-app and everyone uses tools like xdg-open.)

OpenBSD's unveil()

Posted Oct 13, 2018 7:18 UTC (Sat) by ssokolow (guest, #94568) [Link]

I managed to track down a relevant URL for Snappy:

https://forum.snapcraft.io/t/snapd-support-for-xdg-deskto...

OpenBSD's unveil()

Posted Sep 30, 2018 12:38 UTC (Sun) by rweikusat2 (subscriber, #117920) [Link]

The kernel is massively more complicated than most application, hence, conventional wisdom would suggest that there also massively more unknown bugs in the kernel than in a typical application. Further, all so-called untrusted input necessarily flows through the kernel before reaching an application. This casts some doubt on the wisdom of creating (complex) kernel-level workarounds for hypothetical application bugs.

History so far also suggests that all sandboxes end up being defeated.

OpenBSD's unveil()

Posted Sep 30, 2018 17:24 UTC (Sun) by MarcB (subscriber, #101804) [Link]

I can't really agree with any of your statements.

Userspace does things that are much more complicated. Arguably, as soon as an application includes a full-fledged data (de)serializer, it has surpassed the kernel in complexity. Or think of compilers and interpreters, including SQL interpreters.
Every web browser is more complicated than the kernel - and that is before you add audio, video, images and Javascript.

And while input to applications passes through the kernel, it does just that: pass through it, in form of a meaningless blob. The trouble only starts once you begin parsing this blob.

Of course, sandboxes can be broken out of. That is why they are not a magical cure for security issues. But just look at how they have raised the bar for browser exploits.

OpenBSD's unveil()

Posted Sep 30, 2018 17:55 UTC (Sun) by rweikusat2 (subscriber, #117920) [Link]

The postgresql 8.4 source tree has 549,041 lines of code. The Linux 3.16.48 source tree has 12,654,231 lines of code. That's about 23 times the size of the former. And Postgres is an example of an unusually complicated application (as are 'web browsers', BTW). The assertion that "data just passes through the kernel" is also not really true: The kernel does quite a bit of data processing for tasks like USB communication or networking, eg, IPsec or 802.11.

As to "sandboxes", one can view this in the other way: They've considerably lowered the bar for "download random code from the net and execute it" which has led to no end of exploits, eg, all kinds of attacks against TLS.

OpenBSD's unveil()

Posted Sep 30, 2018 20:20 UTC (Sun) by pj (subscriber, #4506) [Link]

Most of the kernel code is drivers. Most devices don't use most drivers. Also, any given kernel only uses 1 of the N architectures that have code in the kernel. So it's quite possible that any given kernel is running less than 500kloc of the 12.5M kloc of kernel code. It's kind of an interesting question, actually. Is there a way to get that actual number for any given Kconfig ?

OpenBSD's unveil()

Posted Sep 30, 2018 21:01 UTC (Sun) by nix (subscriber, #2304) [Link]

Several spring to mind. The most baroque and ridiculous involves looking at the DWARF, collating the highest line number seen for any given file, and adding those up: but this is overkill, given that a far simpler approach is just to add up the .c's corresponding to each .o in a compiled kernel (with appropriate adjustments for O=... builds), ignoring all .o's that don't have a corresponding .c. This will still miss headers, of course, but suffices for a rough guess given that all the headers added together for, say, x86 come to 'only' 311,000 lines. Given that my x86 desktop box comes in at 1930348 lines by this metric, this is not a terribly important degree of error.

Compare to GCC 7.3.0 (which is notably smaller in LoC count than GCC 6 due to the lack of gcj and libjava). A similar check there with a plausible configuration suggests on the order of four million lines, *excluding* generated code. And GCC is a helluvalot more complex than most parts of the kernel.

OpenBSD's unveil()

Posted Oct 1, 2018 4:42 UTC (Mon) by cyphar (subscriber, #110703) [Link]

GregKH used to give approximations of how many lines of code his laptop (and Android phones) used in his older talks on kernel development. From memory it was sub-1M but I don't remember the exact figure (nor do I remember if he explained how he got that figure).

OpenBSD's unveil()

Posted Oct 1, 2018 16:30 UTC (Mon) by rweikusat2 (subscriber, #117920) [Link]

That's besides the point: Every particular use case of any programm will involve only a subset of the features of a sufficiently complex program. But every feature is used by someone.

BTW, counting LOC on the same kernel tree while omitting all drivers and all architecture-specific code (ie, counting based on 'unreasonable smallness' assumptions) still yields 2,217,513 lines of code.

OpenBSD's unveil()

Posted Oct 1, 2018 23:39 UTC (Mon) by k8to (subscriber, #15413) [Link]

Having worked in both operating systems and applications, I think it's a defensible point that operating systems tend to be "thinner" than applications. Call paths from intent to action tend to be shorter, and the number of parts tied together and datastructures involved tend to be fewer in operating systems.

The total system may be larger than many applications but in some respects it's easier to reason about.

That said, I worked on operating systems considerably simpler than Linux for most of my time in that space.

OpenBSD's unveil()

Posted Oct 1, 2018 19:30 UTC (Mon) by xtifr (subscriber, #143) [Link]

Lines of code is a notoriously bad measurement of complexity. I can write a couple of lines of lisp or python to do things that would require many dozens of lines of C. I've rewritten small C programs in C++, and reduced the LoC count by a third, without changing the complexity of the actual *program* at all.

Furthermore, while the kernel has a lot of lines of code, that code is not only verbose C, but is all basically standalone. Compare to something like Libreoffice, which not only has a huge LoC count of its own, but links to over 100 libraries, each with its own not-insubstantial complexity, and *then* maybe you'll have something! ;)

I'm not saying the kernel isn't an amazing, truly astounding, feat of engineering. It certainly is. But these sorts of naive comparisons are not only not helpful, they can be actively misleading.

OpenBSD's unveil()

Posted Oct 1, 2018 19:49 UTC (Mon) by rweikusat2 (subscriber, #117920) [Link]

> Lines of code is a notoriously bad measurement of complexity.

A difference of an order (or some orders of) magnitude is nevertheless a good indicator of more complex versus less complex. And it becomes better for the given situation: If there's a lot more code, there are obviously also a lot more opportunities for coding errors
and unexpected interactions between "geographically distant" code sharing an address space.

> I can write a couple of lines of lisp or python to do things that would require many dozens of lines of C.

The runtime environment enabling use of lisp or python would need to be counted here as well.

> I've rewritten small C programs in C++, and reduced the LoC count by a third, without changing the complexity of the
> actual *program* at all.

Certainly not. Again, some of the tasks which were explicitly coded in the C program were pushed to libraries/ the runtime environment and considering how, say, the STL code looks like, you're probably vastly increased both the amount of source code representing the complete, running program and its complexity.

But this starts to drift a bit from what I originally meant to express: The kernel is a complex program (massively more complex than a lot of others), hence, it ought to contains loads of unknown bugs and thus, adding complicated, kernel-level workaround for hypothetical application errors, like sophisticated systems for restricting access to the filesystem namespace, is a bit of a dubious idea. It's entirely conceivable that a working application becomes exploitable because of bugs in the access restriction code as that's not anyhow magic: It's just code and thus, will have bugs.

One could (and I definitely would) argue that time would be better spent finding and fixing real software errors instead of creating workarounds for software errors whose existence is conjectured.

OpenBSD's unveil()

Posted Oct 1, 2018 20:45 UTC (Mon) by anselm (subscriber, #2796) [Link]

One could (and I definitely would) argue that time would be better spent finding and fixing real software errors instead of creating workarounds for software errors whose existence is conjectured.

OTOH, it may be more economical to add the code once to the kernel, where multiple programs can benefit from it (especially if you're using a wrapper-type tool that sets up the file system rules so the individual programs don't even need to be changed), than to adapt many user-level programs.

Which is not to say that fixing bugs in application-level software isn't also a good idea, but if you can have wide-ranging protection against unexpected/unwanted file system accesses and can make misbehaving programs crash hard so you can more easily find the places where they need to be fixed, what's not to like? This is not a zero-sum game.

OpenBSD's unveil()

Posted Oct 2, 2018 2:46 UTC (Tue) by roc (subscriber, #30627) [Link]

The kernel is more complicated than most individual applications. However, it's a lot less complicated than all applications put together. Kernel security mechanisms add a relatively small amount of complexity to the kernel and confine a vastly larger amount of user-space code.

Sure, "untrusted input flows through the kernel before reaching an application", but as long as the kernel treats it as just a list of bytes, there's not much that can go wrong. When it's interpreted --- e.g. treated as a Javascript program to be executed --- there is much more that can go wrong, and in any sane system that happens in userspace.

Why you do you describe application bugs are "hypothetical"? We have found a vast number of real application bugs and it's impossible to believe we've found them all.

Sandboxes are sometimes defeated, but if your exploit requires a sandbox escape as well as a regular exploitable application bug, that raises the cost of exploitation dramatically. The common sandboxing mechanisms have been pretty well scrutinized and you can't use a sandbox escape very often before it gets detected and fixed.

OpenBSD's unveil()

Posted Oct 3, 2018 19:13 UTC (Wed) by rweikusat2 (subscriber, #117920) [Link]

> Why you do you describe application bugs are "hypothetical"? We have found a vast number of real application bugs and it's
> impossible to believe we've found them all.

Because they are hypothetical: It is conjectured that yet unknown applications bugs exist which could be exploited weren't it for the restricted filesystem access. There are really two assumptions involved here: It's assumed that unknown bugs with certain, known properties exist. And to me, this has an air of "We must do something. This is something. Therefore, we must do it", ie, file system access is restricted not so much because this is demonstrably useful but because it's possible to restrict this access.

NB: It's entirely conceivable that these restrictions will end up being useful. But this cannot be known by the time they're put in place.

OpenBSD's unveil()

Posted Sep 30, 2018 16:08 UTC (Sun) by kaliszad (guest, #125214) [Link]

Bob Beck presented Unveil at EuroBSDcon 2018 in Bucharest and I just posted the video:
https://www.youtube.com/watch?v=wdeFQJXd_II&list=PLbx...

I was a bit short on space and FullHD really took me by surprise, but the missing piece is about 5 minutes at most.

OpenBSD's unveil()

Posted Oct 5, 2018 16:09 UTC (Fri) by ortalo (guest, #4654) [Link]

Thank you very much for the recording by the way!

OpenBSD's unveil()

Posted Oct 2, 2018 9:54 UTC (Tue) by ortalo (guest, #4654) [Link]

To elaborate on your conclusion: is there any virtue to security mechanisms complex enough to slow down (or even preclude) their adoption? Wouldn't it be better to just take the risk rather than suffer the consequences of the usability problems?

Arguably, some developers find all security mechanisms too complex. (But, obviously, those *are* the security problem themselves so any manager which is not also part of the problem already has a simple solution to clear out the field).

But for all other cases where the security design really is difficult to use and where the security developer is deaf to all his/her peers usability complaints ; I do see ease of use as a strong requirement for any security mechanism and even a key one for adoption.

Second only to the protection function itself. (Of course, I try not to support any of the snake-oil devices many companies successful market these days: always easy to use and deploy, absolutely inoccuous and most of the time pretty useless outside of security marketing campaigns.)

OpenBSD's unveil()

Posted Oct 2, 2018 16:14 UTC (Tue) by kjp (guest, #39639) [Link]

I'm not even sure this feature is that simple.

This is like locking down the garage door on my house. Yes, that's my biggest opening and the most tonnage goes through it.
But I still have windows and doors. Just like the kernel has IPC and network interfaces and PIDs and god knows what else not accessed through the FS interface.
Ultimately, adding more patches to a 50 year old OS, and not taking anything away, isn't going to simplify anything.

So yeah, in isolation, unveil looks great. But a system-level look still confuses me. Does anyone even understand all of Linux/POSIX resource APIs.

OpenBSD's unveil()

Posted Oct 2, 2018 16:51 UTC (Tue) by kjp (guest, #39639) [Link]

I guess pledge() covers what I'm talking about, but it's still some cognitive load. I don't have any better suggestions though.
https://man.openbsd.org/pledge.2

OpenBSD's unveil()

Posted Oct 5, 2018 15:53 UTC (Fri) by ortalo (guest, #4654) [Link]

Do not underestimate the strength of a 50 years old battle proven operating system fine tuned and put on a security diet for decades either, you young careless flesh!

More seriously, given the complexity of modern code bases, most operating system developpers rarely have enough time to look at *other* operating systems too. That's not a reason not to encourage them to do it, not to mock them when they assume that everyone else has just been accumulating technical debt.

OpenBSD's unveil()

Posted Oct 3, 2018 2:31 UTC (Wed) by wahern (subscriber, #37304) [Link]

> Does anyone even understand all of Linux/POSIX resource APIs.

The breadth of POSIX APIs is much narrower than of Linux APIs. I think pledge and unveil represent a choice *not* to go down the reductionist path of Linux, which is attempting to abstract and break down the POSIX environment into a much larger but more flexible and, ideally, more consistent set of APIs.

Notably, unveil works by caching inodes. Contrast that with Linux user namespaces, which is a conceptual rethink of the traditional Unix VFS model. The latter is more appealing on its face and is certainly more flexible, yet the lived reality is messy and cumbersome--most applications (i.e. Docker) end up effectively creating a giant chroot with a couple of single-file overlays (config files, etc) to break the encapsulation. Linux isn't Plan9 and it never will be; instead it's becoming a self-loathing Unix, much like proprietary Unix systems.

OpenBSD's unveil()

Posted Oct 3, 2018 3:33 UTC (Wed) by Cyberax (✭ supporter ✭, #52523) [Link]

Uhm, unveil() is not comparable to namespaces.

Namespaces are just that - they allow to code in a separate environment, including the filesystem. Secure confinement is actually a secondary priority for most containers.

unveil() more properly can be compared to AppArmor or SELinux.


Copyright © 2018, Eklektix, Inc.
This article may be redistributed under the terms of the Creative Commons CC BY-SA 4.0 license
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds