This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
access() Security Hole
I quote from man page access(2):
Warning: Using access() to check if a user is authorized to, for example, open a file before actually doing so using open(2) creates a security hole, because the user might exploit the short time interval between checking and opening the file to manipulate it. For this reason, the use of this system call should be avoided.
What does this mean, and in what situation would it be a concern?
It is a classic "Time of check to time of use" race condition.
This is a security concern only for Set-user-ID and set-group-ID applications. For applications running as the user itself there is no threat, because the operation in question would be rejected by the operating system anyway.
Consider this scenario: you have a UNIX program running as root via set-user-id. The program uses access to check file permissions of another user, and then runs the file as root, but only if the permission check has been successful. Let's say the program is called securerun, and you run it as follows:
securerun myfile
An attacker can make a program that exploits this security hole to run, using this algorithm:
Write a file xyz of which the user has executing permissions
Start two threads, A and B
Thread A waits a few milliseconds, and executes cp norunning xyz to replace xyz with a file that the attacker wants to run, but has no run permissions to do so
Thread B calls securerun xyz
If the attacker gets lucky by getting his timing right, your securerun would check the execute permissions on the old xyz, but it would run the new xyz, a copy of norunning that the hacker wasn't supposed to run. Since there is a short time window between the check and the execution, the attacker is bound to get lucky at some point, if he tries his strategy many times in a loop.
Typical erroneous code:
Use access to check whether to read file on user's behalf, in a program running with elevated privileges
Short gap here
Open file
During the "short gap", the user might be able to manipulate the file system, for example:
ln -f secret_file.txt non_secret_file.txt
Then open will open the secret file for reading, even though it would have failed the access check had the link been in place at the time the check was done.
Related
I created a program that monitors for events.
I want to log these events "in the right way".
Currently I have a string array, log[500][100].
Each line is a string of characters (up to 100) that report something about the event.
I have it set up so that only the last 500 events are saved in the array.
After that, new events overwrite the oldest events.
Currently I just keep revolving through the array until the program terminates, then I write the array to a file.
Going forward I would like to view the log in real time, any time I wish, without disturbing the event processing and logging process.
I considered opening the file for "appending" but here are my concerns:
(1) The program is running on a Raspberry Pi which has a flash memory as a "disk drive". I believe flash memories have a limited number of write cycles before problems can occur. This program runs 24/7 "forever" so I am afraid the "disk drive" will "wear out".
(2) I am using pretty much all the CPU capacity of the RPi so I don't want to add a lot of overhead/CPU cycles.
How would experienced programmers attack this problem?
Please go easy on me, this is my first C program.
[EDIT]
I began reviewing all the information and I became intrigued by Mark A's suggestion for tmpfs. I looked into it more and I am sure this answers my question. It allows the creation of files in RAM not the SD card. They are lost on power down but I don't care.
In order to keep the files from growing to large I created a double buffer approach. First I write 500 events to file A then switch to file B. When 500 events have been written to file B I close and reopen file A (to delete the contents and start at 0 events) and switch to writing to file A. I found I needed to fflush(file...) after each write or else the file was empty until fclose.
Normally that would be OK but right now I am fighting a nasty segmentation fault so I want as much insight into what is going on. When I hit the fault, I never get to my fclose statements.
Welcome to Stack Overflow and to C programming! A wonderful world of possibilities awaits you.
I have several thoughts in response to your situation.
The very short summary is to use stdout and delegate the output-file management to the shell.
The longer, rambling answer full of my personal musing is as follows:
1 : A very typical thing for C programs to do is not be in charge of how outputs are kept. You might have heard of the "built in" file handles, stdin, stdout, and stderr. These file handles are (under normal circumstances) always available to your program for input (from stdin) and output (stdout and stderr). As you might guess from their names stdout is customarily used for regular output and stderr is customarily used for error / exception output. It is exceedingly typical for a C program to simply read from stdin and output to stdout and stderr, and let something else (e.g., the shell) take care of what those actually are.
For example, reading from stdin means that your program can be used for keyboard entry and for file reading, without needing to change your program's code. The same goes for stdout and stderr; simply output to those file handles, and let the user decide whether those should go to the screen or be redirected to a file. And, because stdout and stderr are separate file handles, the user can have them go to separate 'destinations'.
In your case, to implement this, drop the array entirely, and simply
fprintf(stdout, "event notice : %s\n", eventdetailstring);
(or similar) every time your program has something to say. Take a look at fflush(), too, because of potential output buffering.
2a : This gets you continuous output. This itself can help with your concern about memory wear on the Pi's flash disk. If you do something like:
eventmonitor > logfile
then logfile will be being appended to during the lifetime of your program, which will tend to be writing to new parts of the flash disk. Of course, if you only ever append, you will eventually run out of space on the disk, so you might set up a cron job to kill the currently running eventmonitor and restart it every day at midnight. Done with the above command, that would cause it to overwrite logfile once per day. This prevents endless growth, and it might even use a new physical area of the flash drive for the new file (even though it's the same name; underneath, it's a different file, with a different inode, etc.) But even if it reuses the exact same area of the flash drive, now you are down to worrying if this will last more than 10,000 days, instead of 10,000 writes. I'm betting that within 10,000 days, new options will be available -- worst case, you buy a new Pi every 27 years or so!
There are other possible variations on this theme, as well. e.g., you could have a sophisticated script kicked off by cron every day at midnight that kills any currently running eventmonitor, deletes output files older than a week, and starts a new eventmonitor outputting to a file whose filename is based partly on the date so that past days' file aren't overwritten. But all of this is in the realm of using your program. You can make your program easier to use by writing it to use stdin, stdout, and stderr.
2b : Or, you can just have stdout go to the screen, which is typically how it already is when a program is started from an interactive shell / terminal window. I imagine you could have the Pi running headless most of the time, and when you want to see what your program is outputting, hook up a monitor. Generally, things will stay running between disconnecting and reconnecting your monitor. This avoids affecting the flash drive at all.
3 : Another approach is to have your event monitoring program send its output somewhere off-system. This is getting into more advanced programming territory, so you might want to save this for a later enhancement, after you've mastered more of the basics. But, your program could establish a network connection to, say, a JSON API and send event information there. This would let you separate the functions of event monitoring from event reporting.
You will discover as you learn more programming that this idea of separation of concerns is an important concept, and applies at various levels of a program or a system of interoperating programs. In this case, the Pi is a good fit for the data monitoring aspect because it is a lightweight solution, and some other system with more capacity and more stable storage can cover the data collection aspect.
I have a problem with running the following C program:
#include<unistd.h>
void main()
{
if (access("/root/main.c",R_OK)==0)
{
printf("/root/main.c can be read\n");
}
else
{
printf("can't be read\n");
}
}
When I run the executable as root, I can get the output:
/root/main.c can be read
But when I set the executable's permissions to 4755, and run it as normal user, I only get the output:
can't be read
Is there any error in my protocol?
access ignores setuid/setgid bits. This is by design.
Quoting from man 2 access on Linux:
The check is done using the calling process's real UID and GID,
rather than the effective IDs as is done when actually attempting an
operation (e.g., open(2)) on the file. Similarly, for the root user,
the check uses the set of permitted capabilities rather than the set
of effective capabilities; and for non-root users, the check uses an
empty set of capabilities.
This allows set-user-ID programs and capability-endowed programs to
easily determine the invoking user's authority. In other words,
access() does not answer the "can I read/write/execute this file?"
question. It answers a slightly different question: "(assuming I'm a
setuid binary) can the user who invoked me read/write/execute this
file?", which gives set-user-ID programs the possibility to prevent
malicious users from causing them to read files which users shouldn't
be able to read.
If you want to know whether your process can actually open a file for reading, just open() it and handle the error, if any. (This also avoids a race condition.)
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.
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.