Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

POSIX defines setuid() in a rather non-intuitive way:

If the process has appropriate privileges, setuid() shall set the real user ID, effective user ID, and the saved set-user-ID of the calling process to uid.

If the process does not have appropriate privileges, but uid is equal to the real user ID or the saved set-user-ID, setuid() shall set the effective user ID to uid; the real user ID and saved set-user-ID shall remain unchanged.

Because of this, there There may be cases where the desired privilege drops are unsuccessful. For example, the range of Linux Kernel versions (2.2.0-2.2.15) is vulnerable to an insufficient privilege attack wherein setuid(getuid()) did not drop privileges as expected when the capability bits were set to zero. As a precautionary measure, subtle behavior and error conditions for the targeted implementation must be carefully noted.

...

The following non-compliant code example compiles cleanly on most POSIX based systems, however no explicit checks have been made to ensure that privilege relinquishment is carried out successfully. This may be dangerous depending on the sequence of the preceding privilege changes.

Code Block
bgColor#ffcccc
/*  Code intended to run with elevated privileges   */

/* Temporary Drop */
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   */
}

/* Permanent Drop */
if (setuid(getuid()); != 0) {
  /* Handle Error */
}

/*  Code intended to run with lower privileges  */ 

If the program is run with the setuid-0 flag, the state of the UID's over time might be:

Description

code

EUID

RUID

SSUID

program startup

 

0

user

0

temporary drop

seteuid(getuid())

user

user

0

restore

seteuid(0)

0

user

0

permanent drop

setuid(getuid())

user

user

user

restore (attacker)

setuid(0) (fails)

user

user

user

If, for some reason, the program fails to restore privileges, it will be unable to permanently drop them later:

Description

code

EUID

RUID

SSUID

program startup

 

0

user

0

temporary drop

seteuid(getuid())

user

user

0

restore

seteuid(0)

user

user

0

permanent drop

setuid(getuid())

user

user

0

restore (attacker)

setuid(0)

0

0

0

Compliant Solution

Wiki Markup
This compliant solution was implemented in sendmail, a popular mail transfer agent \[[Wheeler 03|AA. C References#Wheeler 03]\]. It checks whether superuser privileges were dropped successfully. Note that if the {{setuid()}} call succeeds after the {{setuid(getuid())}} operation, privileges were not dropped as was originally intended.

Code Block
bgColor#ccccff

/*  Code intended to run with elevated privileges   */

/* Temporary Drop */
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   */
}

/* Permanent Drop */
/*  Code intended to run with elevated privileges  */

if (setuid(getuid()) == -1) {
  /* Handle Error */
}

if  (setuid(0) != -1) {
   /* Privileges can be restored, handle error */
}

/*  Code intended to run with lower privileges  */ 

...