5

Here's a full source of a program that demonstrates my problem (the OS is Ubuntu 14.04 32-bit if it matters):

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, const char *argv[])
{
   int status, fd;

   printf("CURRENT UID: %d, CURRENT GID: %d\n", getuid(), getgid());

   fd = open("/dev/ttyS0", O_WRONLY);
   if(fd < 0)
   {
      printf("Error opening /dev/ttyS0: %s\n", strerror(errno));
      return 1;
   }
   printf("Successfully opened /dev/ttyS0\n");
   close(fd);

   /* DROP PRIVILEGES */

   setgid(1000);
   setuid(1000);

   printf("CURRENT UID: %d, CURRENT GID: %d\n", getuid(), getgid());

   fd = open("/dev/ttyS0", O_WRONLY);
   if(fd < 0)
   {
      printf("Error opening /dev/ttyS0: %s\n", strerror(errno));
      return 1;
   }
   printf("Successfully opened /dev/ttyS0\n");
   return 0;
}

There are two users in the system: root and a regular, non-root user (let's call him "ubuntu") with id=1000. The above program is trying to open a serial port (/dev/ttyS0) twice: first time as root or ubuntu (depending on how it's invoked) and second time always as ubuntu. First unsuccessful attempt causes the program to abort. User ubuntu is a member of dialout group so theoretically he has necessary permissions to open /dev/ttyS0. I invoke the program in four different ways:

1) run directly as ubuntu

invocation:

<path to my program>

2) run as ubuntu, but using sudo

invocation:

sudo -u ubuntu <path to my program>

3) run as root, but with privileges dropped to those of ubuntu (so, effectively, run as ubuntu):

invocation:

sudo su
sudo -u ubuntu <path to my program>

In all three cases I get the following expected result:

CURRENT UID: 1000, CURRENT GID: 1000
Successfully opened /dev/ttyS0

CURRENT UID: 1000, CURRENT GID: 1000
Successfully opened /dev/ttyS0

In the last case, however, something strange happens:

4) run directly as root

invocation:

sudo su
<path to my program>

result:

CURRENT UID: 0, CURRENT GID: 0
Successfully opened /dev/ttyS0

CURRENT UID: 1000, CURRENT GID: 1000
Error opening /dev/ttyS0: Permission denied

Of course it's the last two lines of the output that I don't understand: this time, when root drops his privileges, it turns out that ubuntu has insufficient privileges to open /dev/ttyS0, but why? How is this case different from cases 1-3?

One last thing worth mentioning: if I change this line of my code:

setgid(1000);

to this:

setgid(20); /* 20 is the id of dialout group */ 

then the last attempt to open /dev/ttyS0 is successful as well.

Does it mean the information about ubuntu being a member of dialout group gets lost for some reason when I run the program as root and then drop privileges to those of ubuntu by changing uid and gid to 1000? Can you please give me a detailed explanation of what happens in case 4 of my example and why the result is different than I expected?

Peter
  • 313
  • 1
  • 3
  • 10
  • Google "real and effective user ID". Or [see this](http://stackoverflow.com/questions/32455684/difference-between-real-user-id-effective-user-id-and-saved-user-id). – David Schwartz Feb 01 '16 at 22:26
  • Do you see the same results using `seteuid` and `setegid`? – tkausl Feb 01 '16 at 22:26
  • If I substitute all occurrences of UID, GID, getuid(), getgid(), setuid(1000), and setgid(1000) with EUID, EGID, geteuid(), getegid(), seteuid(1000) and setegid(1000) respectively, the result remains exactly the same in all four cases. – Peter Feb 02 '16 at 17:27

1 Answers1

2

Setting the userid and groupid does not set all supplementary groups (i.e. all groups, which are not the main group of the user but assigned to him in /etc/groups) automagically. Try to use

initgroups("ubuntu", 1000);

prior to the setgid()-call. Then the process should have the privileges of the dialout-group.

Ctx
  • 18,090
  • 24
  • 36
  • 51
  • Thanks, this must be the exact reason why my approach failed. Apparently David Schwartz' and tkausi's suggestions it had something to do with euid/egid were wrong. – Peter Feb 02 '16 at 17:35