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. There is also a Linux capabilities overview that shows all available capabilities and their purpose.
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.