...
Code Block | ||
---|---|---|
| ||
/* Code intended to run with elevated privileges */ /* Temporarily drop privileges */ if (seteuid(getuid()) != 0) { /* Handle error */ } /* Code intended to run with lower privileges */ if (need_more_privileges) { /* Restore Privileges */ if (seteuid(0) != 0) { /* Handle error */ } /* Code intended to run with elevated privileges */ } /* ... */ /* Permanently drop privileges */ if (setuid(getuid()) != 0) { /* Handle error */ } if (setuid(0) != -1) { /* Privileges can be restored, handle error */ } /* Code intended to run with lower privileges */ |
...
Compliant
...
Solution
A better solution would be to ensure that we have proper privileges before attempting to carry out a permanent dropThe function in this non-compliant code example correctly follows the principle of least privilege, however, due to inconsistencies and implementation defined behavior of certain functions (such as setuid()
) across various operating systems, the final result may be unexpected. Here, when privileges are given up temporarily for the final time, the effective user ID of the process is set to the real user ID. Unexpectedly, the call to setuid(realuid)
that follows, does not affect the saved set-user-ID since effective UID is no longer 0 (Except on FreeBSD and NetBSD). If a seteuid(0)
gets executed maliciously after this statement, root privileges would be recovered from the saved set-user-ID.
Code Block | ||
---|---|---|
| ||
void doSomething(void) { /* Store the privileged ID for later verification */ uid_t realuidprivid = getuidgeteuid(); /* seteuid(realuid);Code intended to run with elevated /* Give up privileges temporarily */ seteuid(0); /* RegainTemporarily superuserdrop privileges */ /* Carry out the privileged task */ seteuid(realuid); /* Give up privileges temporarily */ setuid(realuid); /* Failed attempt at giving up privileges permanently */ } |
Compliant Solution
The following code shows how the effective UID should be obtained and compared against 0 (superuser's EUID) to make sure privileges can be successfully dropped permanently. This constitutes a more portable and safe solution.
Code Block | ||
---|---|---|
| ||
void doSomething(voidif (seteuid(getuid()) != 0) { /* Handle error */ } /* Code intended to run with lower privileges */ if (need_more_privileges) { uid_t realuid = getuid();/* Restore Privileges */ if (seteuid(realuidprivid); != 0) { /* GiveHandle up privilegeserror temporarily */ seteuid(0);} /* Code intended to run with elevated privileges /* Regain superuser privileges */ } /* Carry out privileged task... */ /* If seteuid(realuid);we have low privileges, restore them */* Give up privileges temporarily */ if (geteuid() != privid) { if (!geteuidseteuid(privid())) { /* Handle Error */ } } /* Permanently Check if the effective uid is still that of the superuser */ setuid(realuid); drop privileges */ if (setuid(getuid()) != 0) { /* Handle error */ } if (setuid(0) != -1) { /* Privileges Gocan aheadbe andrestored, give up privileges permanently handle error */ } else { /* Code intended /*to Handlerun thewith possiblelower implementationprivileges defined behavior */ } } |
Risk Assessment
If privilege relinquishment conditions are left unchecked, any flaw in the program may lead to unintended system compromise corresponding to the more privileged user or group account.
...