Drop privileges as regular non-root user for sandboxing? - c

Is it possible to use a set of C library or system calls to drop all user privileges on POSIX, or at least on Linux? Note that I am not asking how to drop root privileges, which is what all of the other StackOverflow search results seem to be asking and answering.
I want the same effect as switching to user nobody, but stronger if possible. That is, I want my C application to do the following:
Run as a normal user, not root, and without the setuid file permission bit
Retain the ability to access specific files and open outgoing network connections
Voluntarily and permanently lose the ability to read and write files in specified (or all) directories, especially $HOME
If possible, give up or sandbox all other nonessential abilities, like opening a listening socket with accept
Things I have considered so far that don't fit the bill:
Switching to user nobody with setuid/setgid
Ordinary users are forbidden from switching to other users (like nobody), and the application should not require root just to switch to nobody.
Linux/POSIX.1e Capabilities
Capabilities only add root-like privileges, not take away ordinary user privileges
Traditional seccomp
My application will need more than just exit, sigreturn, read, and write
Things that look interesting, but for which I couldn't find documentation, appear to be unmaintained, or appear to be non-portable:
seccomp-bpf
AppArmor
grsecurity RBAC/role-based access control
So is there a well-documented, preferably portable way to drop nonessential user privileges and sandbox a process without having to become root first?

It's unlikely any solution will work on all POSIX, since POSIX doesn't define the mechanism you're looking for.
Looking at just the requirements and just Linux, probably the easiest way to satisfy them is actual via the security modules. Any of apparmor, selinux, RBAC will do what you need, but only via external profile - not something built into your app. The problem may be that adding a profile in all those cases requires the root user to do it (but the profile applies to user process too).
A bit more complicated solution that almost satisfies the requirements is seccomp. While it doesn't understand paths at all (you can only see pointers), there are ways to limit the access: seccomp policies can be defined per thread, so you could redesign your system to have a "path verification thread", which doesn't do anything apart from reading paths and returning sockets if they match your specification. Then limit that thread to just recv(), open() and send(). Thread doing other work can then drop open() and use the other service.
Or if you can configure the paths at program startup, you can put them into an array, mark that page as read-only, and setup seccomp policy which will only accept open() with filenames from that array (that's just a pointer comparison in that case).
To some extent, the approach of splitting application into separate processes which have very limited responsibilities is something you could replicate on other systems, but without the same guarantees as on Linux. For example qmail is kind of a system of very small processes which work as a pipeline for data (simplification). On Linux you could still apply seccomp to them, on Solaris just drop exec and other capabilities, on other systems... I don't know, but probably you can do something.

Related

Restricting folder/file access to one program?

What I need, boiled down, is a way to 'selectively' encrypt either a folder or a zip file - Whatever the solution would be, it needs to block (or redirect) all reads/writes EXCEPT from one specific program (not mine, a legacy application that I do not have source code access to - I cannot modify the program who would have the sole permission to perform reads and writes on the encrypted folder/zip file). I would like to avoid having a constantly running background app (as all the end-user would have to do to circumvent the protection would be to kill the program)
The purpose is to, of course, protect the files within the folder from tampering.
I could modify folder permissions at install, but this would block all programs from access wouldn't it? I more or less need to only block File Explorer from accessing the files, but not the program which needs to read them... if that makes sense. Or, if I could protect the (plaintext) files somehow without affecting the legacy application's reading of them... argh.
I wonder if it would be possible with CreateProcess() to run the legacy application as a high-level user and give the folders it needs access to the same permission, such as TrustedInstaller or SYSTEM, (who, in Windows, own things that not even administrators can touch, like System Volume Information)
This would allow the program to read/write to the folders, but not the user.
I was looking at LockFile, seems to be close to what I am looking for but not quite. I need something like semi-exclusive access.
I am fairly fluent in C++, Visual Basic.net, only some Python, but I am willing to use any language which would allow a solution to this problem (Though it probably could be implemented in any language, if possible at all.)

How do I drop supplementary groups in Linux?

I have an executable that's ug+s to a non-root user and group. I would like it to leave all the supplementary groups it originally had behind. Is there a way to make this happen? It doesn't look like the setgroups call allows you to do this if you're not root. And when I try it, I get EPERM.
It seems like there ought to be a way to lose privileges you previously possessed.
The short answer is "A process without CAP_SETGID can't.". And it is by design that setgroups can't be used to remove groups when the process has no privileges. The previous link is to an excellent LWN article detailing why not.
In my particular case, since I'm immediately creating a user namespace, those supplementary groups get mapped to 'nobody' anyway. And so it isn't very important. If it is important, you can use setcap(8) to set capabilities on a wrapper executable that checks that it's being run by the expected user with the expected group permissions and then drops the groups and drops CAP_SETGID and runs the executable you really want to run. The command you would use to do this is:
sudo setcap cap_setgid+ep wrapper_exe
Be careful though, it's now possible that someone who has a shell on your system can use that executable to run some sort of privilege escalation attack if you did not write it very carefully.

Modify file permissions using C API

I have a server component that reads its configuration from a file. The configuration file is also read and written by an administration component. The components are separate programs, and they will be running under separate accounts. I want to change the ACL on the configuration file to:
application: read
administration: read and write
Its important to drop write from the application because the application opens a listening socket. That is, its high risk and I want to contain it as much as possible.
After some searching, it looks like ioctl is used to do it in C. ioctl_list offers a list of control codes but its not apparent to me how to add two users with different permissions on the configuration file. The man page online at die.net does not discuss the permissions in-depth or offer sample code. For example the word "perm" (root of "permission") only shows up once in the discussion.
On Windows, reducing permission to honor least privilege is drawn out but its pretty straight forward (Richter gives good examples in his Advanced Windows programming series).
How does one change permissions on a linux file using C APIs? What control codes are to be used?
Thanks in advance.
This is not done with ioctl, but rather with dedicated system calls: chmod, chown, and chgrp for the basic user/group/other set of file permissions, and acl_* for full-fledged ACLs. You probably also want to know about the setuid, setgid, and setgroups system calls, which are how you drop privileges in a running application.
You can accomplish your goals using only the basic user/group/other permissions. Configure your system as follows:
The server application has a dedicated user ID and group; let's say they are both named nlserver ("nl" for "noloader").
The administrative component also has a dedicated user ID and group, let's say nladmin.
The configuration file is set to be mode 0640 (aka -rw-r-----) and owned by user nladmin, group nlserver. (The code responsible for doing this should be in the administrative component, not the server itself.)
When the server starts up, after doing everything it needs to do as root (e.g. bind low-numbered ports), it uses setgroups, setgid, and setuid (IN THAT ORDER!) to change to user nlserver, group nlserver, and clear the supplementary groups list. Only after doing so does it open the configuration file.

Prevent unauthorised write access to a part of filesystem or partition

Hello all I have some very important system files which I want to protect from accidental deletion even by root user. I can create a new partition for that and mount it with readonly access but the problem is that I want my application which handles those system files to have write access to that part and be able to modify them. Is that possible using VFS? As VFS handles access to the files I could have a module inserted in the VFS layer which can see if there is a write access to that part then see the authorization and allow it or otherwise reject it.
If not please provide me suggestions regarding how can such a system be implemented what would I need in that case.
If there exists a system like this please suggest about them also.
I am using linux and want to implement this in C, I think it would be possible in C only.
Edit: There are such kind of programs implemented in windows which can restrict access to administrator even, to some important folders, would that be possible in linux?
My application is a system backup and restore program which needs to keep its backup information safe and secure. So I would like to have a secured part of a partition which could not be accidently deleted in any way. There are methods of locking a flashdrive can we use some of those methods for locking a partition in linux also ? so that mount is password protected ? I am not writing a virus application, my application would give user option to delete the backups but I don't wanna allow them to be deleted by any other application.
Edit: I am writing a system restore and backup program for ubuntu, I am a computer engineering student.
Edit: As I have got opinion from Basile Starynkevitch that I would be committing worst sin of programming if I do anything like this, but you could provide me suggestions considering this as a experimental project, I could make some changes in the VFS layer so that this could work.
You could use chattr, e.g.
chattr +i yourfile
But I don't think it is a good thing to do that. People using root access are expected to be careful. Those having root access can still issue the command undoing the above.
There is no way to forbid people having root access, or people having physical access to the computer, to access, remove, change your file, if they really want to (they could update & hack the kernel, for instance). Read more about trusted compute base
And I believe it is even unethical (and perhaps illegal, in some countries) to want to do that. I own my PC, and I don't understand why you should disallow me to change some data on it, because I happened to install some software.
By definition of root on Linux, it can do anything... You won't be able to prohibit him to erase or alter data... People with root access can write arbitrary bytes at arbitrary places on the disk.
And on a machine that I own (or perhaps just have physical access to), I will, thanks God, always be able to remove a file (even under Windows: I could for example boot a Linux CDROM and remove the file from Linux accessing an NTFS, and then reboot the Windows...).
So I think you should not bother and take even a minute to find out how to make root altering your precious files more difficult. Leave them as other root files...
PHILOSOPHICAL RANT
The unix philosophy has always been to trust the system administrator (while protecting newbie users from mistakes), that is the root user. The root is able to do anything (this is why people avoid being root, even on a personal machine). There have never been strong features to prohibit root doing mistakes, because the system administrator is expected to know well the system, and is trusted.
And Unix sysadmins understand this fact: it is part of their culture. (This is probably in contrast with Windows administration culture). They know when to be careful, they don't expect software to prevent mistakes as root.
In order to use root squashing (which makes it so that root can't even see files for a local user) you can set up a local nfs. This forum page explains how to mount an nfs locally. The command is:
mount -t nfs nameofcomputer:/directory_on_that_machine /directory_you_should_have_already_created
nfs has root squashing enabled by default, which should solve your problem. From there, you just make sure your program stores its files on the nfs mount.
Sounds to me like you're trying to write a virus.
No doubt you will disagree.
But I'm willing to bet the poor people that install your software will feel like it's a virus, because it will be behaving like one by making itself hard to remove.
Simply setting r/w flags should suffice for anything else.

UID/GID change notification

My application creates a rudimentary ACL and communicates this to a Kernel Module. The ACL contains a list of UID/GID. The checks on these IDs are custom and on top of what the system already provides.
Now if the root/user changes UID/GID the ACL needs to be refreshed. Is there a way to get notification in an application whenever UID/GID changes e.g. through usermod command? Right now the platform is Linux but we could be porting this to other platforms as well(AIX/Solaris)
I'd say there is no way to reliably detect a change in the UID/Username mapping, especially when you go cross-platform. The user database may reside in NIS, NIS+, or nowadays, LDAP. They might be integrating their UNIX/Linux systems with a Windows Server running Active Directory, or they might be using something more obscure like Hesoid. I know of no way to receive any automatic feedback from these various databases. But also, any good book on UNIX Administration will tell you to not change these mappings, or at the very least, if you must, don't ever re-use a UID. Don't forget, the filesystem whether it's using standard POSIX permissions or POSIX ACLs will also be storing UIDs that won't be getting updates if an Administrator decides to change the UID for a user. The Kernel and filesystem both treat a specific UID as a specific user, regardless of their username. I really don't think you need to bother with the case of an Administrator changing UIDs for a user, it's likely to be too error prone for everyone.
Also, a quick look at NTFS on Windows will reveal that it also stores the equivalent of a UID called SID, it'a a long number used to represent a user and Windows does not provide or expect you to be changing the mapping of that SID to a specific Username. The SID is what the NT Kernel uses internally, not a username. If I open up the Security tab on a file, for a split second I may see numbers until Windows is able to query the Active Directory Controller and give convenient usernames to those numbers. Ultimately, it's the SID, not username that uniquely identifies the user.
On Linux, inotify does what you want. On other systems, try FAM.
One possible way (triggered from Frédéric's reply) is that I could possibly set a inotify/(dnotify gasp! for older kernels) operation on /etc/passwd because that file will always change for at least any change in local user privileges. Trouble is how to do this in NIS environment.
In the typical Unix security model, you should only care about the numerical value for GID/UID, not the mapping between human-readable string and numeric value. As that seems to be the case, have you carefully considered what you are trying to accomplish? Maybe it would be better to pass the name or a name/id tuple, instead of just the id?

Resources