determine file permissions for the current user - c

I am looking for a way to determine file permissions for the current user (i.e. the process's UID) on POSIX-compliant systems. I don't want to try opening the file - that could get messy with directories and all kinds of special files.
I am compiling a directory listing of a specified directory, and for each file, reporting a bunch of things: filename, size, type (file/directory/other), permissions (you can read, you can write). For size and type, i already have results of stat call available.
Here's what i came up with:
if ((dirent->st_uid == getuid() && dirent->st_mode & S_IRUSR)
|| (dirent->st_gid == getgid() && dirent->st_mode & S_IRGRP)
|| (dirent->st_mode && S_IROTH)) entry->perm |= PERM_READ;
if ((dirent->st_uid == getuid() && dirent->st_mode & S_IWUSR)
|| (dirent->st_gid == getgid() && dirent->st_mode & S_IWGRP)
|| (dirent->st_mode && S_IWOTH)) entry->perm |= PERM_WRITE;
Do i have to do this way, or is there a simple call/macro that would accomplish the same thing? Bonus points for ACL support, although that is not strictly necessary at this point.

access(2) will perform the full suite of permissions tests for you, in the kernel:
#include <unistd.h>
#include <stdio.h>
int main(int argc, char* argv[]) {
int i;
for (i=0;i<argc;i++) {
if(access(argv[i], R_OK)) {
printf("%s\n", argv[i]);
perror("R_OK");
}
if(access(argv[i], W_OK)) {
printf("%s\n", argv[i]);
perror("W_OK");
}
}
return 0;
}
Some sample output:
$ ./foo ./foo /etc/passwd /etc/shadow
/etc/passwd
W_OK: Permission denied
/etc/shadow
R_OK: Permission denied
/etc/shadow
W_OK: Permission denied
EDIT
Note that access(2) is vulnerable to a TOCTTOU Time-of-check-to-time-of-use race condition. You shouldn't use access(2) to grant or deny access to files to a user from a privileged process, your program would be vulnerable to a race condition that could be exploited. If this is what you want the test for, use setfsuid(2) before doing any open(2) or exec*() calls.

Use access() to check for permissions.

Related

Why can't my program set 0777 mode with the mkdir system call?

I wrote following code to try to create a directory with 0777 mode on Linux:
#include <sys/stat.h>
#include <sys/types.h>
int main () {
mkdir("/tmp/mkdir-test", 0777);
return 0;
}
But actually, the new directory has 0755 mode.
# stat /tmp/mkdir-test
File: `/tmp/mkdir-test'
Size: 4096 Blocks: 8 IO Block: 4096 directory
Device: 802h/2050d Inode: 1772304 Links: 2
Access: (0755/drwxr-xr-x) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2016-09-27 20:23:54.000000000 -0700
Modify: 2016-09-27 20:23:54.000000000 -0700
Change: 2016-09-27 20:23:54.000000000 -0700
Can someone explain this? And how can the program create a real 0777 mode directory?
Run umask in the shell; it will report 022. The bits set in the umask value are removed from permissions when creating files or directories.
In a single-threaded program, one way to really set 0777 permissions is to dink with umask():
mode_t old_mask = umask(0);
mkdir("/tmp/mkdir-test", 0777);
umask(old_mask);
That preserves the current setting except when you are sure you need to override it. However, this is dangerous in multi-threaded programs because the umask value is global per-process, not per-thread (and thanks to Peter Cordes for pointing this out).
The other option for setting the permissions is to use chmod(), and doing so is safe with multi-threaded programs too:
const char *dirname = "/tmp/mkdir-test";
mode_t target_mode = 0777;
if (mkdir(dirname, 0) == 0)
chmod(dirname, target_mode);
The permissions set by the chmod() function are not affected by umask values.
The permissions on the call to mkdir() are probably best set to 0 as shown; it will always work reliably and doesn't risk affecting other threads by modifying the umask value. Alternatively, you could use the desired target permissions in the call to mkdir() if you wanted to (and a previous version of this answer suggested doing so, using 0777 as the hard-coded target permissions).
const char *dirname = "/tmp/mkdir-test";
mode_t target_mode = 0777;
if (mkdir(dirname, target_mode) == 0)
chmod(dirname, target_mode);
If you use this idiom, it is important that the mode passed to mkdir() is the same as, or less permissive than, the mode passed to chmod() — and 0 is less permissive than any other mode (and always works).
If you use a more relaxed mode in the call to mkdir(), there's a TOCTOU (Time-of-Check, Time-of-Use) style vulnerability between the mkdir() and chmod() calls during which someone (some process) could get into the directory with relaxed permissions and wreak havoc; this could be a security risk.
Note that umask() is a very simple system call and therefore very quick too (as system calls go, compared with heavyweights such as open(), mkdir() or chmod()).
You can simply do it using the following code:
mkdirfile.cpp
#include <stdio.h>
#include <unistd.h>
#include <sys/syscall.h>
int main() {
long result;
result = syscall(__NR_mkdir, "/tmp/mkdir-test", 0777);
printf("The result is %ld.\n", result);
return 0;
}
Then compile the code using gcc and run it:
gcc mkdirfile.cpp && ./a.out

check if something exists and is executable in C, using stat function

For example, I have directory a and b under my current working directory. I'm trying to locate file X, how can I modify the stat() command so that it checks both directory a and b instead of just current working directory? would stat(a/file, &buf) work? also, to check if it's executable, I know the code is buf.S_IXUSR, does if (buf.S_IXUSR) work?
thanks!
I suggest you consult the stat(2) man page.
Here's an example of how to use stat:
struct stat buf;
if (stat("a/file", &buf) != 0) {
// handle failure
}
// Check the `st_mode` field to see if the `S_IXUSR` bit is set
if (buf.st_mode & S_IXUSR) {
// Executable by user
}
However, for your use case, you might consider access(2) instead:
if (access("/path/to/file", X_OK) == 0) {
// File exists and is executable by the calling process's
// _real_ UID / GID.
}

How to ensure correct file permissions

In order to protect an application from begin used wrongly, I'm trying to check that its configuration files have correct permissions, so that the application can trust the content of the files not being modified by someone else.
I believe the following rules are corrects:
the file must not be writable by others
the file must be owned by a trusted user/group: root
or
the file must be owned by the effective user/group running the application (think of setuid program)
Here an example:
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <string.h>
#include <errno.h>
static
int is_secure(const char *name)
{
struct stat st;
uid_t euid = geteuid();
gid_t egid = getegid();
if (stat(name, &st) != 0) {
int err = errno;
fprintf(stderr, "can't stat() '%s': %d (%s)\n", name, err, strerror(err));
return 0;
}
/* writable by other: unsecure */
if ((st.st_mode & S_IWOTH) != 0) {
return 0;
}
/* not owned by group root and not owned by effective group: unsecure */
if (st.st_gid != 0 && st.st_gid != egid) {
return 0;
}
/* not owned by user root and not owned by effective user: unsecure */
if (st.st_uid != 0 && st.st_uid != euid) {
return 0;
}
return 1;
}
int
main(int argc, char *argv[])
{
int i;
for(i = 1; i < argc; i++) {
printf("'%s' : %s\n", argv[i], is_secure(argv[i]) ? "sure" : "unsure");
}
return 0;
}
Since I'm not sure about my assumptions, can someone check if I leave some loophole in the file permissions check.
Update
sudo has a function for that: sudo_secure_path, it only check for one uid/gid, but it take care of checking for group write bit.
Regards.
Your rules and your code look correct to me, although you should be aware of the following security risks that could still affect your implementation.
An attacker with physical access to the machine or NFS/SMB access could mount the file system with a box that has root privileges, and then modify your file.
A vulnerability in another program being run as either the trusted user or root could allow that program to be exploited to modify your file.
All it would take to break your security check would be a careless user or sys-admin that messes up the privilege settings of the file. I've seen this happen during backups and copies to thumb drives, etc.
Also make sure the file is not executable. I can't think of an instance where this could be exploited on a config file, but the general rule with security is don't give any privileges that aren't required for the job.
As you can see these are not issues under control of your code. Therefore, you should make sure you client is aware of these risks before assuring them of the non-tamperability of the config file.
I believe you also want to check permissions on the directory.
A user would be able to mv another file belonging to the correct user to replace this one, if they are allowed to write to the directory.
Something like:
sudo touch foo.conf
sudo touch foo.conf-insecure-sample
mv -f foo.conf-insecure-sample foo.conf

How can I check to see if user has execute permissions?

I wish to know how to check if the "user" (other than who is running this program)
has execute permissions on a file ? [C api]
I have looked at "access" it gives information with respect to the caller.
I am looking for something like :-
"<cmd> <user_name> <file_name>"
here I am trying to get if <user_name> has execute permissions for <file_name> ?
I am looking C api ?
Possible solution :- I am using the following algo to get this information
boolean_t
is_user_has_execute_permissions(char *run_as_user)
{
/* Check world execute permission */
if ((cmd_stat.st_mode & S_IXOTH) == S_IXOTH) {
return (TRUE);
}
/* group id for run_as_user */
getpwnam_r(run_as_user, &pw, buf, passwd_len);
/* Check group execute permission */
if ((cmd_stat.st_mode & S_IXGRP) == S_IXGRP) {
if (pw->pw_gid == cmd_stat.st_gid)
return (TRUE);
}
return (FALSE);
}
Did anyone see any error in this one ?
You need the call stat(2), which returns the permission bits, for the owner of the file, the owner group, and the others. Than you have to find out the id of the user you're interested in and ids its groups: see getpwent(3) and getgrouplist(3). The first which match, will give the resulting permissions.
From the command line, you can use an Awk program easily enough. Something like
ls -l filename | awk -F '{ if (substring($1,3,1) == "x" exit(0); exit(1)}'
will set the return code to 0 if it's found, 1 if it's not.
From a C program, you want to use fstat. Basically you open the file
int fd = fopen("filename", "r");
then get the file stat block with fstat
fstat(fd, &bufr)
and look at bufr.st_mode.
Here's a description of fstat.
Update
I'll note crankily that when the OP originally posted, it wasn't clear the C API was what was desired.

Getting the environment of a process running under a different user

Assume I have a process with PID 1234 running in the background under user A.
If I run the following program as user A, it succeeds. If I run it as user B, it fails with open: Permission denied.
This makes sense, as the environ file is owned by user A and has read permission only for A.
But if I make the program set-user-ID for user A and run it as user B, it fails with read: Permission denied. This doesn't seem to happen with a regular file having the same permissions. It also doesn't happen if A is root.
Any ideas why? Is there any other way to get the environment of another process that works around this issue?
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main(int argc, const char *argv[])
{
unsigned char ch = 0;
int fd = -1;
int read_result = -1;
setresuid(geteuid(), geteuid(), geteuid());
fd = open("/proc/1234/environ", O_RDONLY);
if (-1 == fd) {
perror("open");
return EXIT_FAILURE;
}
read_result = read(fd, &ch, 1);
if (-1 == read_result) {
perror("read");
return EXIT_FAILURE;
}
close(fd);
return EXIT_SUCCESS;
}
As you can see, if your program run without SETUID, open(2) gives you Permission denied, whereas if you run the program with SETUID, open(2) works ok, but read(2) causes the same error. This happens because of additional permission check during each file operation on /proc/* inodes. Looks like this additional permission check uses something other than EUID of the running process. If you run GNU/Linux, for more details see NOTE at the beginning of the code in <kernel_source>/fs/proc/base.c and environ_read() function in the same file.
One of the possible quick solutions:
set owner of the program file to root
set owner group to some special group
add user that should run the program (user B) to that special group
set mode bits to 4550 (r-sr-x---)
call setuid(getuid()) to drop priveleges as soon as possible, i.e. right after reading environ file
In this case any user from the given group could read /proc/*/environ of any other user.
If you want to reduce the permissions of your program to allow only read environ files of the specific user (user A), you probably should think of some other tricks. For example config file, containing the user(s) whose environ file(s) could be read.
Always be careful with extra permissions. Especially with root permissions. Do necessary privileged operations and drop permissions as soon as possible.

Resources