I noticed the hard way that the access(2) system call uses the real and not effective user ID for the access control check. While this is in line with what the access(2) man page says on Linux, it still makes little sense to me...
For example, a setuid root program would run under the effective UID of root and real UID of whoever ran the program..now, the program would be able to open /etc/shadow with open(2), but the call access("/etc/shadow", R_OK); would fail with EACCESS
Can anyone educate my on why access(2) works the way it does?
The simplest answer is that access() works the way the standard says it should:
The access() function shall check the file named by the pathname pointed to by the path argument for accessibility according to the bit pattern contained in amode, using the real user ID in place of the effective user ID and the real group ID in place of the effective group ID.
The standard also describes a different function faccessat() which has an option to use the effective UID:
AT_EACCESS
The checks for accessibility are performed using the effective user and group IDs instead of the real user and group ID as required in a call to access().
There is also a nonstandard function eaccess() or euidaccess() which you'll find on some systems, which checks using the EUID. The standard talks a bit about this function and why it's not part of the standard.
As for why access() was original designed the way it was, consider that typical programs don't actually need access() very often. If you want to know if you can open a file, it's probably because you're about to open it. So just try to open it and see if it works. It's rare to need to test whether you can do something without actually doing it.
The rationale on the standards page hints at the original purpose for access():
The superuser has complete access to all files on a system. As a consequence, programs started by the superuser and switched to the effective user ID with lesser privileges cannot use access() to test their file access permissions.
The original purpose of access() was for a setuid program to test whether the user could perform an action without considering the program's elevated access. That permits a setuid program to limit the scope of what it does without relinquishing its elevated status.
Per the Linux man page, "This allows set-user-ID programs to easily determine the invoking user’s authority."
I suppose this is actually the crux of the function. Any program can just attempt the desired type of access and handle failure if it happens. Most do. You need to check access separately only if you care whether the answer differs from what you would get from the "try and see" approach.
Related
At the moment I am playing around with bluetooth LE and iBeacon devices.
I wrote an Server that keeps looking for nearby beacons.
My server follows this example (Link)
Unfortunately calling the function:
hci_le_set_scan_parameters()
requires root privileges.
As I don't want to run the whole server with root privileges I wanted to ask if there is any possibility for calling only this function with root privileges?
I am aware that asking for sudo when executing a program is always at least questionable I could not find any other possibility to scan for iBeacons.
If there is another possibility I am happy to hear about it as well.
Thanks for your help and kind regards
nPLus
According to POSIX, UID/GID are process attributes. All code inside your process is executed with UID/GID currently set for the whole process.
You could start server as root and immediately drop root privileges. Then, you can temporary gain root privileges using seteuid(2) when executing your function.
See also this answer.
You can also gain only selected capabilities(7) instead (temporary or permanently).
Thread-safety note
AFAIK, on Linux UID/GID are per-thread attributes and it's possible to set them for single thread, see NOTES section in seteuid() man-page and this post.
If you can move the privileged part into a separate process, I warmly recommend doing so. The parent process will construct at least one Unix Domain socket pair, keeping one end for itself, and put the other end as the child process standard input or output.
The reason for using an Unix domain socket pair is that such a pair is not only bidirectional, but also supports identifying the process at the other end, and passing open file descriptors from one process to another.
For example, if your main process needs superuser access to read a file, perhaps in a specific directory, or otherwise identifiable, you can move the opening of such files into a separate helper program. By using an Unix domain socket pair for communication between the two, the helper program can use getsockopt(ufd, SOL_SOCKET, SO_PEERCRED, &ucred, &ucred_size) to obtain the peer credentials: process ID, effective user ID, and effective group ID. Using readlink() on the pseudofile /proc/PID/exe (where PID is the process ID as a positive decimal number) you can obtain the executable the other end is currently running.
If the target file/device can be opened, then the helper can pass the open file descriptor back to the parent process. (Access checks in Linux are only done when the file descriptor is opened. Read accesses will only be blocked later if the descriptor was opened write-only or the socket read end has been shut down, and write accesses only blocked if the descriptor was opened read-only or the socket write end has been shut down.)
I recommend passing an int as the data, which is 0 if successful with the descriptor as an ancillary message, and an errno error code otherwise (without ancillary data).
However, it is important to consider the possible ways how such helpers might be exploited. Limiting to a specific directory, or perhaps having a system-wide configuration file that specifies allowed path glob patterns (and not writable by everyone), and using e.g. fnmatch() to check if the passed path is listed, are good approaches.
The helper process can gain privileges either by being setuid, or via Linux filesystem capabilities. For example, giving the helper only the CAP_DAC_OVERRIDE capability would let it bypass file read, write, and execute checks. In Debian derivatives, the command-line tool to manipulate filesystem capabilities, setcap, is in the libcap2-bin package.
If you cannot move the privileged part into a separate process, you can use the interface supported in Linux, BSDs, and HP-UX systems: setresuid(), which sets the real, effective, and saved user IDs in a single call. (There is a corresponding setresgid() call for the real, effective, and saved group IDs, but when using that one, remember that the supplementary group list is not modified; you need to explicitly call setgroups() or initgroups() to modify the supplementary group list.)
There are also filesystem user ID and filesystem group ID, but the C library will set these to match the effective ones whenever effective user and/or group ID is set.
If the process is started with superuser privileges, then the effective user ID will be zero. If you first use getresuid(&ruid, &euid, &suid) and getresgid(&rgid, &egid, &sgid), you can use setresgid(rgid, rgid, rgid) to ensure only the real group identity remains, and temporarily drop superuser privileges by calling setresuid(ruid, ruid, 0). To re-gain superuser privileges, use setresuid(0, ruid, 0), and to permanently drop superuser privileges, use setresuid(ruid, ruid, ruid).
This works, because a process is allowed to switch between real, effective, and saved identities. Effective is the one that governs access to resources.
There is a way to restrict the privilege to a dedicated thread within the process, but it is hacky and fragile, and I don't recommend it.
To keep the privilege restricted to within a single thread, you create custom wrappers around the SYS_setresuid/SYS_setresuid32, SYS_setresgid/SYS_setresgid32, SYS_getresuid/SYS_getresuid32, SYS_getresgid/SYS_getresgid32, SYS_setfsuid/SYS_setfsuid32, and SYS_setfsgid/SYS_setfsgid32 syscalls. (Have the wrapper call the 32-bit version, and if it returns -ENOSYS, fall back to the 16-bit version.)
In Linux, the user and group identities are actually per-thread, not per-process. The standard C library used will use e.g. realtime POSIX signals and an internal handler to signal other threads to switch identity, as part of the library functions that manipulate these identities.
Early in your process, create a privileged thread, which will keep root (0) as the saved user identity, but otherwise copy real identity to effective and saved identities. For the main process, copy real identity to effective and saved identities. When the privileged thread needs to do something, it first sets the effective user identity to root, does the thing, then resets effective user identity to the real user identity. This way the privileged part is limited to this one thread, and is only applied for the sections when it is necessary, so that most common signal etc. exploits will not have a chance of working unless they occur just during such a privileged section.
The downside to this is that it is imperative that none of the identity-changing C library functions (setuid(), seteuid(), setgid(), setegid(), setfsuid(), setfsgid(), setreuid(), setregid(), setresuid(), setresgid()) must be used by any code within the process. Because in Linux C library functions are weak, you can ensure that by replacing them with your own versions: define those functions yourself, with the correct name (both as shown and with two underscores) and parameters.
Of all the various methods, I do believe the separate process with identity verification through an Unix domain socket pair, is the most sensible.
It is the easiest to make robust, and can be ported between POSIX and BSD systems at least.
Say I want to change the behavior of kill for educational reasons. If a user directly types it in the shell, then nothing will happen. If some other program/entity-who-is-not-the-user calls it, it performs normally. A wrapping if-statement is probably sufficient, but what do I put in that if?
Edit I don't want to do this in the shell. I'm asking about kernel programming.
In line 2296 of the kernel source, kill is defined. I will wrap an if statement around the code inside. In that statement, there should be a check to see whether the one who called this was the user or just some process. The check is the part I don't know how to implement.
Regarding security
Goal:
Block the user from directly calling kill from any shell
Literally everything else is fine and will not be blocked
While other answers are technically true, I think they're being too strict regarding the question. What you want to do it not possible to do in a 100% reliable way, but you can get pretty close by making some reasonable assumptions.
Specifically if you define an interactive kill as:
called by process owned by a logged in user
called directly from/by a process named like a shell (it may be a new process, or it may be a built-in operation)
called by a process which is connected to a serial/pseudo-terminal (possibly also belonging to the logged in user)
then you can check for each of those properties when processing a syscall and make your choice that way.
There are ways this will not be reliable (sudo + expect + sh should work around most of these checks), but it may be enough to have fun with. How to implement those checks is a longer story and probably each point would deserve its own question. Check the documentation about users and pty devices - that should give you a good idea.
Edit: Actually, this may be even possible to implement as a LKM. Selinux can do similar kind of checks.
It looks you are quite confused and do not understand what exactly a system call is and how does a Linux computer works. Everything is done inside some process thru system calls.
there should be a check to see whether the one who called this was directly done by the user or just some process
The above sentence has no sense. Everything is done by some process thru some system call. The notion of user exists only as an "attribute" of processes, see credentials(7) (so "directly done by the user" is vague). Read syscalls(2) and spend several days reading about Advanced Linux Programming, then ask a more focused question.
(I really believe you should not dare patching the kernel without knowing quite well what the ALP book above is explaining; then you would ask your question differently)
You should spend also several days or weeks reading about Operating Systems and Computer Architecture. You need to get a more precise idea of how a computer works, and that will take times (perhaps many years) and any answer here cannot cover all of it.
When the user types kill, he probably uses the shell builtin (type which kill and type kill) and the shell calls kill(2). When the user types /bin/kill he is execve(2) a program which will call kill(2). And the command might not come from the terminal (e.g. echo kill $$ | sh, the command is then coming from a pipe, or echo kill 1234|at midnight the kill is happening outside of user interaction and without any user interactively using the computer, the command being read from some file in /var/spool/cron/atjobs/, see atd(8)) In both cases the kernel only sees a SYS_kill system call.
BTW, modifying the kernel's behavior on kill could affect a lot of system software, so be careful when doing that. Read also signal(7) (some signals are not coming from a kill(2)).
You might use isatty(STDIN_FILENO) (see isatty(3)) to detect if a program is run in a terminal (no need to patch the kernel, you could just patch the shell). but I gave several cases where it is not. You -and your user- could also write a desktop application (using GTK or Qt) calling kill(2) and started on the desktop (it probably won't have any terminal attached when running, read about X11).
See also the notion of session and setsid(2); recent systemd based Linuxes have a notion of multi-seat which I am not familiar with (I don't know what kernel stuff is related to it).
If you only want to change the behavior of interactive terminals running some (well identified) shells, you need only to change the shell -with chsh(1)- (e.g. patch it to remove its kill builtin, and perhaps to avoid the shell doing an execve(2) of /bin/kill), no need to patch the kernel. But this won't prohibit the advanced user to code a small C program calling kill(2) (or even code his own shell in C and use it), compile his C source code, and run his freshly compiled ELF executable. See also restricted shell in bash.
If you just want to learn by making the exercise to patch the kernel and change its behavior for the kill(2) syscall, you need to define what process state you want to filter. So think in terms of processes making the kill(2) syscall, not in terms of "user" (processes do have several user ids)
BTW, patching the kernel is very difficult (if you want that to be reliable and safe), since by definition it is affecting your entire Linux system. The rule of thumb is to avoid patching the kernel when possible .... In your case, it looks like patching the shell could be enough for your goals, so prefer patching the shell (or perhaps patching the libc which is practically used by all shells...) to patching the kernel. See also LD_PRELOAD tricks.
Perhaps you just want the uid 1234 (assuming 1234 is the uid of your user) to be denied by your patched kernel using the kill(2) syscall (so he will need to have a setuid executable to do that), but your question is not formulated this way. That is probably simple to achieve, perhaps by adding in kill_ok_by_cred (near line 692 on Linux 4.4 file kernel/signal.c) something as simple as
if (uid_eq(1234, tcred->uid))
return 0;
But I might be completely wrong (I never patched the kernel, except for some drivers). Surely in a few hours Craig Ester would give a more authoritative answer.
You can use aliases to change the behavior of commands. Aliases are only applied at interactive shells. Shell scripts ignore them. For example:
$ alias kill='echo hello'
$ kill
hello
If you want an alias to be available all the time, you could add it to ~/.bashrc (or whatever the equivalent file is if your shell isn't bash).
As a learning exercise, I am trying to find a weakness in the following code snippet to gain access as the owner of the executable.
setresuid(geteuid(), geteuid(), geteuid());
system("/usr/bin/id");
FWIW, I can't see any. I know setresuid will set the uid to the owner of the file, but I can't change the owner to anyone but myself. I thought about trying to redirect the id command by altering the PATH, but since it uses an absolute path, that trick doesn't work.
Hints?
It is possible to exploit an obscure (and now patched) issue involving the unchecked usage of setresuid():
Under Linux 2.6 and later, setresuid() can fail if the process is running with an RLIMIT_NPROC (that is, a limit on the number of processes as set by ulimit -n) such that the target UID would have too many processes if the setresuid() succeeded.
However, under Linux 3.1 and later, failing a setresuid() sets a flag on a process such that any subsequent execve() calls will fail. This would prevent the system() from running on any modern Linux if the setresuid() failed.
Unless there is some larger context which has been omitted, it may be possible to set environment variables (e.g, LD_PRELOAD) which cause code to be injected into /usr/bin/id. These variables are ignored for setuid executables, but will not be ignored for executables launched by a setuid executable, as is occurring here.
If you are on a vulnerable system (Linux 2.6 through 3.0), you may be able to exploit this vulnerability by setting environment variables and causing the setresuid() to fail, so that /usr/bin/id runs user-specified code as root.
The system() function executes the command given as its argument by passing it to /bin/sh -c. I think the /usr/bin/id program is not particularly relevant; it is the shell's behavior that is key. In particular, note that the shell's startup behavior is different when the real and effective UIDs differ:
If the shell is started with the effective user (group) id not equal to the real user (group) id [...] no startup files are read, shell functions are not inherited from the environment, the SHELLOPTS, BASHOPTS, CDPATH, and GLOBIGNORE variables, if they appear in the environment, are ignored, and the effective user id is set to the real user id.
-- BASH 4.1 manual
In the event that a program containing the code you presented is installed suid, the code prevents the condition given in that paragraph from applying by setting real, effective, and saved UIDs all equal to the effective UID (which will be the UID of the owner of the executable).
Exploits typically revolve around unsafe use of untrustworthy data, with environment variables being a frequent offender. The ENV environment variable in particular names a file that under some circumstances the shell will execute at startup. bash will not run it when the real and effective UIDs differ, as documented in the excerpt above, but otherwise will do so when invoked interactively in POSIX compatibility mode or as sh.
That doesn't help for non-interactive invocation, as applies here, so now I have to go speculative. I suspect, but cannot currently document, that some other past -- and maybe even present -- versions of the shell do read and execute commands from the file named by ENV when invoked non-interactively. That would provide a vector for executing arbitrary commands as the owner of the setuid program.
In weak support of that speculation I direct your attention to the BASH_ENV variable, which is analogous to ENV, but is used when bash is invoked noninteractively, as bash. I am supposing that once these two variables were more parallel, applicable to both interactive and non-interactive modes, but the non-interactive use of ENV and the interactive use of BASH_ENV were removed at different times. for different reasons. Quite possibly the non-interactive use of ENV was removed to plug exactly the hole that you are looking for.
The Linux function access() allows me to check file permissions for the current user.
Is there a similar function that gives me the same information - but instead of checking the current user it checks the permissions of any given system user?
Something like int access_for(const char *pathname, uid_t uid, int mode); or whatever
I can't use seteuid() as I need this for a multithreaded process (POSIX threads) which would affect all threads at the same time. That's why I need to check file permissions myself.
Edit: The process itself is known/assumed to have at least the privileges of the relevant user. So, in theory I could also walk the file system and calculate the rights by hand, but I'd need something much more efficient as the check needs to be done several (up to hundreds) times per second. Possible?
not sure how it could work. if you're running as user X, you couldn't reliably check if user Y has access to something, because the check would be done under YOUR permissions. You may not have access to something that Y does, meaning you'd get a false negative.
Beware of TOCTOU. If you check NOW that a file can be accessed, it doesn't mean that NOW it can (or can't), because the time it took you to read those words between "NOW" and "NOW", the file privileges may well have changed.
So, the CORRECT solution is to run in a thread/process as the user that you want to access the file as. Otherwise, you run a risk of "the file privileges changed while you were working" problem.
Of course, this applies to any type of access to "things that may be restricted based on who I am running as".
On Linux, fundamentally all set*id operations are thread-local. This is wrong and non-conforming (the standard specifies that a process, not a thread, has ids that are set by these functions) and the userspace code (in libc) has to work around the issue via delicate and error-prone logic to change all the thread uids in a synchronized way. However, you may be able to invoke the syscall directly (via syscall()) to change the ids for just one thread.
Also, Linux has the concept of "filesystem uid" set by the setfsuid function. If I'm not mistaken, libc leaves this one thread-local, since it's not specified by any standard (and thus does not have any requirements imposed on it) and since the whole purpose of this function is thread-local use like what you're doing. I think this second option is much better if it works.
Finally, there's one solution that's completely portable but slow: fork then use seteuid in the child, call access there, pass the result back to the parent, and _exit.
Is there a way to determine if the process may execute a file without having to actually execute it (e.g. by calling execv(filepath, args) only to fail and discover that errno == EACCES)?
I could stat the file and observe st_mode, but then I still don’t know how that pertains to the process.
Ideally, I’m looking for a function along the lines of
get_me_permissions(filepath, &status); // me: the process calling this function
// when decoded, status tells if the process can read, write, and/or execute
// the file given by filepath.
Thanks in advance.
Assuming your end goal is to eventually execute the program, you don't. This test would be useless because the result is potentially wrong even before the function to make the check returns! You must be prepared to handle failure of execve due to permission errors.
As pointed out by Steve Jessop, checking whether a given file is executable can be useful in some situations, such as a file listing (ls -l) or visual file manager. One could certainly think of more esoteric uses such a using the permission bits of a file for inter-process communication (interesting as a method which would not require any resource allocation), but I suspect stat ("What are the permission bits set to?") rather than access ("Does the calling process have X permission?") is more likely to be the interesting question...
http://linux.die.net/man/2/access
Note the slight tricksiness around suid - if the process has effective superuser privilege it isn't taken into account, since the general idea of the function is to check whether the original invoking user should have access to the file, not to check whether this process could access the file.