...
This noncompliant code example compiles cleanly on most POSIX systems, but no explicit checks have been made to ensure that privilege relinquishment has succeeded. This may be dangerous depending on the sequence of the preceding privilege changes.
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 */
}
/*
* Code intended to run with lower privileges,
* but if privilege relinquishment failed,
* attacker can regain elevated privileges!
*/
|
...
Wiki Markup |
---|
This compliant solution was implemented in sendmail, a popular mail transfer agent, to determine if superuser privileges were successfully dropped \[[Wheeler 2003|AA. Bibliography#Wheeler 03]\]. If the {{setuid()}} call succeeds after (supposedly) dropping privileges permanently, then the privileges were not dropped as intended. |
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;
* attacker cannot regain elevated privileges
*/
|
...
A better solution is to ensure that proper privileges exist before attempting to carry out a permanent drop.
Code Block |
---|
|
/* Store the privileged ID for later verification */
uid_t privid = geteuid();
/* 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(privid) != 0) {
/* Handle error */
}
/* Code intended to run with elevated privileges */
}
/* ... */
/* Restore privileges if needed */
if (geteuid() != privid) {
if (seteuid(privid) != 0) {
/* Handle error */
}
}
/* 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;
* attacker cannot regain elevated privileges
*/
|
...
Rule POS36-C. Observe correct revocation order while relinquishing privileges discusses how to drop supplementary group IDs. To ensure that supplementary group IDs are indeed relinquished, you can use the following eql_sups
function:
Code Block |
---|
|
/* Returns nonzero if the two group lists are equivalent (taking into
account that the lists may differ wrt the egid */
int eql_sups(const int cursups_size, const gid_t* const cursups_list,
const int targetsups_size, const gid_t* const targetsups_list) {
int i;
int j;
const int n = targetsups_size;
const int diff = cursups_size - targetsups_size;
const gid_t egid = getegid();
if (diff > 1 || diff < 0 ) {
return 0;
}
for (i=0, j=0; i < n; i++, j++) {
if (cursups_list[j] != targetsups_list[i]) {
if (cursups_list[j] == egid) {
i--; /* skipping j */
} else {
return 0;
}
}
}
/* If reached here, we're sure i==targetsups_size. Now, either
j==cursups_size (skipped the egid or it wasn't there), or we didn't
get to the egid yet because it's the last entry in cursups */
return j == cursups_size ||
(j+1 == cursups_size && cursups_list[j] == egid);
}
|
...