6

As far as I understood the different user IDs are as follows (from the perspective of a process):

  • real user ID: the user ID that owns the process
  • effective user ID: the user ID which determines what is currently allowed to do and not allowed to do
  • saved user ID: basically the original effective user ID to be able to return to the original effective user ID when necessary

Now I have two questions:

  1. Wouldn't saving the effective user ID in a variable at the beginning of the program make the saved user ID unnecessary?

  2. How can I retrieve the saved user ID in a C program ? I was not able to find any functions doing that.

Chris
  • 185
  • This question is very narrow. Who ever said it was too broad, must have pressed the wrong button, or not understand the concept. (However, I would not be surprised if it had been asked before). – ctrl-alt-delor Oct 27 '19 at 18:24
  • 1
    @ctrl-alt-delor it's narrow, but has multiple questions. Neither of the two questions is particularly tied to the other, and there's no reason to ask both in the same post. – muru Oct 29 '19 at 02:18

2 Answers2

8

Wouldn't saving the effective user ID in a variable at the beginning of the program would make the saved user ID unnecessary?

It's not a question of what the userspace program remembers, but what rights the kernel lets it use. For the separation between users to work, it has to be system that controls what user IDs a process can use. Otherwise any process could just ask to become root.

How can I retrieve the saved user ID in a C program ? I was not able to find any functions doing that.

With standard functions you can't (there's only getuid() and geteuid()). At least Linux has getresuid() that return all three user IDs, though.

Anyway, usually you wouldn't need to read it. It's there to allow switching between the real user ID, and the effective user ID in case of a setuid program, so it starts as a copy of the effective user ID.

In a setuid program, the real user ID is that of the user running it, and the effective and saved user IDs are those of the user owning the program. The effective user ID is the one that matters for privilege checks, so if the process wants to temporarily drop privileges, it changes the effective user ID between the real and the saved user IDs.

In what way does the kernel use the saved user ID to check whether a process can or cannot change its user ID? Does this mean that when a process tries to change its effective user ID, the kernel checks the saved user ID to make sure, the process is allowed to do so?

Yes. The Linux man page for setuid() mentions this, but it's somewhat hidden:

ERRORS     
EPERM  The user is not privileged and uid does not match the real
       UID or saved set-user-ID of the calling process.

In other words, you can only set (the effective) user ID to one of the real or saved IDs.

The man page for setreuid() is clearer on that:

Unprivileged processes may only set the effective user ID to the real
user ID, the effective user ID, or the saved set-user-ID.
ilkkachu
  • 138,973
  • In what way does the kernel use the saved user ID to check whether a process can or cannot change its user ID?

    Does this mean that when a process tries to change its effective user ID, the kernel checks the saved user ID to make sure, the process is allowed to do so?

    – Chris Oct 27 '19 at 17:10
  • @Chris I have added another answer. I think it addresses "In what way does the kernel use the saved user ID to check whether a process can or cannot change its user ID?" -- Tell us if it is clear. – ctrl-alt-delor Oct 27 '19 at 17:49
  • @Chris, in a word: yes. – ilkkachu Oct 27 '19 at 18:23
  • Thank you for your answers. Let's assume the following: I have a setuid program which does not know which will be the effective user in the end. Let's assume on my machine this setuid program will be owned by root so it grants root rights. Now this program changes the effective user ID to something less privileged than root. Using the saved user ID it can know that the original effective user ID was root. But on non-linux systems there will be no function to get this value right? I mean it won't be able to know that the original effective user ID was root since there is no function to get it. – Chris Oct 27 '19 at 18:39
  • 1
    @Chris, if you have a program meant to be running as root, it's probably going to written so that it's implicitly aware of that. Also, the saved user ID starts off as a copy of the effective user ID, so the program can get it from there when it starts if it needs to swap the user IDs around, see edit. (And then there's the thing that calling setuid() while root will also drop the saved user ID...) – ilkkachu Oct 27 '19 at 18:52
  • Thanks a lot you both! – Chris Oct 27 '19 at 19:01
  • As a side note: if you are using a setuid root binary to gain privileges, then you should be using capabilities. See https://linux.die.net/man/7/capabilities and https://unix.stackexchange.com/questions/101263/what-are-the-different-ways-to-set-file-permissions-etc-on-gnu-linux – ctrl-alt-delor Oct 27 '19 at 19:20
1

If the saved uid is a variable.

You can not use a regular variable, because you won't have permission to copy it to the effective or real uid. The kernel can not allow you to copy from a regular variable, as this would allow you to become whoever you wish (Ok it does, but only if the value is already in one of the uids).

You are correct that effective uid is used for checking permission of system calls. The exception is setuid/setgid (also see capabilities).

The permission for setuid is: you can move IDs around (from one uid to another), but can not introduce a new one (unless you have CAP_SETUID: root will have this).

Therefore as a normal user. You will start with one or two uids (real and effective, but they may both be the same). If they differ, then you may wish to copy the real to the effective. If you were to do this then you would be down to one, with no way to get it back. The kernel will not let you load it from a regular variable. Therefore you copy the effective to saved. For the rest of the execution you have the original effective in saved, and real in real. You can now make ether of these effective, by copying it to the effective-uid. Some time latter, you may choose to drop one. e.g. by copying real to the other two. There is now no way back. The only way to gain a privilege now is to exec a setuid binary. (or a binary with the new capabilities, but don't worry about this yet).

And to get suid, you can use getresuid -- http://man7.org/linux/man-pages/man2/getresuid.2.html

In practise just use the regular C variables. But be aware that if the uid value is not already in one of the uids, then you are not allowed to add it.

/*setup UIDs*/
uid_t real_uid = getuid(void);
uid_t original_effective_uid = geteuid(void);
int err = setresuid(real_uid, original_effective_uid, original_effective_uid); /*this line not needed as done by kernel for setuid binary*/
#ifdef see_effect_of_no_suid
   int err = setresuid(real_uid, original_effective_uid, real_uid);     
#endif
while (true) {
   err=seteuid(original_effective_uid);
   /*do stuff as original_effective uid*/

err=seteuid(real_uid); /do stuff as real uid/ }