A second getpwuid call appears to overwrite old value

241 views Asked by At

Here's a small C program that prints (well, supposed to print) the real and effective IDs of a process when the file has the setuid flag set. In this program, when I call getpwuid a second time (L.No 38), it tends to overwrite the value of the variable realUserName that was obtained in L.No 24. I'm unable to explain this behavior. Is this the expected behavior and why? I'm trying this in a Linux box (RHEL 2.6.18-371.1.2.el5).

  1 /* Filename: test.c
  2  * Notes:
  3  * 1] ./test owned by user cadmn (userID: 3585)
  4  * 2] ./test run by user pmn (4471)
  5  * 3] ./test has the setuid bit switched-on.
  6  */
  7 #include <stdio.h>
  8 #include <pwd.h>
  9 #include <sys/types.h>
 10 #include <unistd.h>
 11 int main()
 12 {
 13
 14     uid_t realId, effectiveId;
 15     struct passwd *realUser, *effUser;
 16
 17     realId = getuid(); // realId = 4471
 18     effectiveId = geteuid(); //effectiveId = 3585
 19
 20     printf("Real ID is %i and Effective ID is %i\n", (int)realId, (int)effectiveId);
 21     //prints 4472 and 3585, respectively
 22
 23     realUser = getpwuid(realId);
 24     char *realUserName = realUser->pw_name; //realUserName = pmn
 25
 26     printf("Real ID (name) at this point is %s\n", realUserName);
 27     // prints pmn.
 28
 29     /*
 30      *********************************************************
 31      *                                                       *
 32      *    everything works as expected up to this point      *
 33      *                                                       *
 34      *********************************************************
 35      */
 36
 37     // The value obtained from this call is not used anywhere in this program
 38     effUser = getpwuid(effectiveId);
 39     printf("\nCalled getpwuid with the effectiveId\n\n");
 40
 41     printf("Real ID is %i and Effective ID is %i\n", (int)realId, (int)effectiveId);
 42     //prints 4472 and 3585, respectively
 43
 44     printf("Real ID (name) at this point is %s.\n", realUserName);
 45     // Expect to still see 'pmn' printed; though see 'cadmn' as the output!
 46     // Why does this happen?
 47
 48     return 0;
 49 }
 50

Output:

pmn@rhel /tmp/temp > id pmn
uid=4471(pmn) gid=1000(nusers) groups=1000(nusers)
pmn@rhel /tmp/temp >

pmn@rhel /tmp/temp > id cadmn
uid=3585(cadmn) gid=401(cusers) groups=401(cusers)
pmn@rhel /tmp/temp >

pmn@rhel /tmp/temp > ls -l ./test
-r-sr-xr-x 1 cadmn cusers 9377 Dec 24 19:48 ./test
pmn@rhel /tmp/temp >

pmn@rhel /tmp/temp > ./test
Real ID is 4471 and Effective ID is 3585
Real ID (name) at this point is pmn

Called getpwuid with the effectiveId

Real ID is 4471 and Effective ID is 3585
Real ID (name) at this point is cadmn.
pmn@rhel /tmp/temp >
1

There are 1 answers

4
alk On BEST ANSWER

The behaviour you observe is the expected one.

The structure referenced by the return value of getpwuid() is defined statically internal to the latter, so it is expected to be filled (and with this overwritten) for each call to getpwuid().

This line

char * realUserName = realUser->pw_name; 

just stores a reference to a value held by this statically internal structure. This value is also overwritten if the statically internal structure is overwritten by the next call to getpwuid().

To get around this there are two possibilities:

  • Use te reentrant version of getpwuid() which is getpwuid_r(). To be able to use it, add

    #define _POSIX_SOURCE
    

    before the very 1st #include statement in your program's sources.

  • Create copy of the members you need, pw_namein this case. The can be achieved by for example doing:

    char * realUserName = strdup(realUser->pw_name);
    

    Be awre that realUserName is now pointing to dynamically allocated memory, which needs to be free()ed by the program itself if not need anymore. To do so call

    free(realUserName);