How and why Linux daemons drop privileges

In this article we have a look at the privileges of Linux daemons and dropping privileges in particular. The samples provided are in C.

Why drop privileges?

Some daemons need root permissions to start. This happens for example when a daemon wants to bind to a low port (<1024). However running network based daemons with root permissions is considered to be a serious risk. In case of compromise of the process, an attacker has full access to the system. This is why software like nginx starts with a master process and forks non-privileged child processes. These child processes (or workers), run under the context of non-privileged account like www-data.

root      2034     1  0 Jun10 ?        00:00:00 nginx: master process /usr/sbin/nginx
www-data  2036  2034  0 Jun10 ?      00:00:35 nginx: worker process
www-data  2037  2034  0 Jun10 ?        00:00:36 nginx: worker process
www-data  2038  2034  0 Jun10 ?        00:00:33 nginx: worker process
www-data  2039  2034  0 Jun10 ?        00:00:37 nginx: worker process

How to drop privileges

First the program needs to check its current user ID. If it is zero, the equal of root, then it should drop both the user ID and group ID. This can be done with the setuid and setgid functions.

if (getuid() == 0) {
 /* process is running as root, drop privileges */
 if (setgid(groupid) != 0)
 fatal("setgid: Unable to drop group privileges: %s", strerror(errno));
 if (setuid(userid) != 0)
 fatal("setuid: Unable to drop user privileges: %S", strerror(errno));
 }

Once a process has switched to a non-privileged user, it should not be able to regain root permissions. This can be tested with the following snippet:

if (setuid(0) != -1)
fatal(“ERROR: setuid back to zero succeeded, quitting as this is a security risk”);

If it succeeds for what reason, the program should terminate.

Additional groups

The root account may also be part of supplementary groups, besides the usually root or wheel group. With the help of the function initgroups, any of the supplementary groups of the root user can be dropped. This way the process can’t access any data by accident.

Current work directory

When starting a process, the current work directory might still be something owned by the root user. For safety, the chdir function can be used to move to another safe work directory. Usually this will be the home directory or data directory.

Capabilities

Another way to provide only limited privileges to a binary, is giving them capabilities. This is implemented in the Linux kernel and splits all different “roles” the root user can have. Examples include this earlier mentioned binding to a low port, open network sockets or loading a kernel module. For daemons it is not wise to run fully under the root context, but you might want to use the capability to open up a port.

For more information about how capabilities work, see our Linux capabilities 101 post.

Conclusion

Safe programming takes a lot of effort, but helps not in introducing weaknesses into software, systems and our valuable data. After all it is this same data which needs to be protected, hence every effort is another useful step in achieving this important goal.

Feedback

Small picture of Michael Boelen

This article has been written by our Linux security expert Michael Boelen. With focus on creating high-quality articles and relevant examples, he wants to improve the field of Linux security. No more web full of copy-pasted blog posts.

Discovered outdated information or have a question? Share your thoughts. Thanks for your contribution.

Mastodon icon