All of lore.kernel.org
 help / color / mirror / Atom feed
* A security bug w.r.t. fsuid?
@ 2002-04-26 19:25 Hao Chen
  0 siblings, 0 replies; only message in thread
From: Hao Chen @ 2002-04-26 19:25 UTC (permalink / raw)
  To: linux-kernel; +Cc: David Wagner, ddean, Hao Chen

We came across a possible security bug with regard to fsuid while doing
research on Linux's user ID model.  In linux/kernel/sys.c, in the comment
just before the body of cap_emulate_setxuid(), it says:

   *  fsuid is handled elsewhere. fsuid == 0 and {r,e,s}uid!= 0 should
   *  never happen.

However, the following program shows that a process can enter the state
where ruid=euid=suid!=0 and fsuid=0.  The problem is with setresuid().
While setuid() and setreuid() always set fsuid to euid, setresuid(ruid,
euid, suid) fails to do the same when the euid parameter is -1.

Normally, a programmer expects that if none of the ruid, euid, and suid of a
process is zero, the process has no root privileges.  This seems to be the
reason behind the above comment from linux/kernel/sys.c.  If this invariant
is not satisfied, i.e. if a process can get ruid=euid=suid!=0 and fsuid=0, 
an unwary programmer may allow the process to create a file whose data are
supplied by an untrusted user.  Since fsuid=0, the file will be owned by
root.   If the file happens to be setuid-root, then the user can gain root
access (by asking the process to write shell command into the file and then
running the file).

We are not sure if this invariant is a true invariant or just a 
documentation error, and how much kernel code rely on this invariant.  Could 
anyone point out?

Thanks,

- Hao


Run the following program as user root
-------------------------------------------------------
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>

int getresuid(uid_t *ruid, uid_t *euid, uid_t *suid);
int setresuid(uid_t ruid, uid_t euid, uid_t suid);
int setfsuid(uid_t);

int getfsuid(uid_t *fsuid)
{
    pid_t pid;
    char filename[1024], str[1024], *str2;
    FILE *fp;
    int i;

    if ((pid = getpid()) < 0)
    {
      perror("getpid()");
      exit(1);
    }
    sprintf(filename, "/proc/%u/status", pid);
    if ((fp = fopen(filename, "r")) == NULL)
    {
      perror("fopen()");
      exit(1);
    }
    while (fgets(str, 1023, fp) != NULL)
    {
      if ((str2 = strstr(str, "Uid:")) == str)
      {
        strtok(str, "\t");
        for (i = 0; i < 4; i++)
          str2 = strtok(NULL, "\t");
        *fsuid = atoi(str2);
        fclose(fp);
        return 0;
      }
    }

    return -1;
}

int main()
{
    uid_t alice = 100;
    uid_t ruid, euid, suid, fsuid;

    // Now ruid==0, euid==0, suid==0, fsuid==0
    if (setresuid(alice, alice, -1) < 0)
    {
      perror("setresuid");
      exit(1);
    }
    // Now ruid==alice, euid==alice, suid==0, fsuid==alice
    setfsuid(0);
    // Now ruid==alice, euid==alice, suid==0, fsuid==0
    if (setresuid(-1, -1, alice) < 0)
    {
      perror("setresuid");
      exit(1);
    }
    // Now ruid==alice, euid==alice, suid==alice, fsuid==0
    // But according to linux/kernel/sys.c:
    // "fsuid == 0 and {r,e,s}uid!= 0 should never happen."
    if (getresuid(&ruid, &euid, &suid) < 0)
    {
      perror("getresuid");
      exit(1);
    }
    if (getfsuid(&fsuid) < 0)
    {
      fprintf(stderr, "getfsuid failed\n");
    }
    printf("ruid=%u  euid=%u  suid=%d  fsuid=%d\n", ruid, euid, suid, fsuid);
}



^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2002-04-26 19:25 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2002-04-26 19:25 A security bug w.r.t. fsuid? Hao Chen

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.