How to ensure correct file permissions - c

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

Related

Retrieve logged on user name or id in the process running under root

I need to run from a privileged (root) application another application with current logged on user.
getenv("USER") and getenv("LOGNAME") return "root" in the privileged application. Effective and current user ids are 0.
cat /proc/self/status | grep [GU]id:
Uid: 0 0 0 0
Gid: 0 0 0 0
I found something close to my needs, user-1000.slice, but I would like to avoid using fscanf on the file /proc/self/cgroup.
cat /proc/self/cgroup | fgrep user
9:devices:/user.slice
8:pids:/user.slice/user-1000.slice
1:name=systemd:/user.slice/user-1000.slice/session-c1.scope
The code bellow outputs 1000 as desired. Is there any more convenient way or API to get a user name of current UI session from a privileged application?
#include <stdio.h>
int main() {
int luid = 0;
char line[100];
FILE* file = fopen("/proc/self/cgroup", "r");
while (fgets(line, sizeof(line), file)) {
if (sscanf(line, "%*d:pids:/user.slice/user-%d.slice", &luid) == 1)
break;
}
fclose(file);
printf("Logged on User Id: ");
luid ? printf("%d\n", luid) : printf("Not found\n");
return 0;
}
The is no machine wide notion of "current UI session" under Unix. It does not make sense as Unix has its origin as a multi-user system, that mean that several people may be logged on at the same time, each with several UI session. It's the case for me on the machine I'm working one, there are 4 people logged on, 6 UI sessions and about 20 non UI sessions.
There is a database of current logged on people, /var/run/utmp. Maintaining this database is somewhat to the programs discretion (for instance, the version of gnome-terminal I'm using allow to add or not entries in the database). The database format is described in utmp(5), and there are functions to access it (see getutent(3)). There are programs to display its content (who(1) and w(1)) and modify it (sessreg(1)).
I'm not sure what your purpose is, so it is difficult to recommend an approach. Ensuring that your display manager add an entry in utmp and parsing the result of who is probably the easiest but you have to pay attention to assumptions you are making.
Assuming that root session was launched with 'pkexec' from a terminal.
When running pkexec from a termnal, the following process hierarchies is displayed:
pstree -p | grep $$
...
|-gnome-terminal-(3295)-+-bash(3302)---bash(93463)-+-grep(93500)
...
In this situation possible to extract the user based on the following logic:
Find the grand parent of the current process: cur_user_pid
Depending on the specific setup, might have to go up additional layers, potentially, until detecting a non-root ID.
Lookup the user id cur_user_pid
Much easier to do in shell, so if possible, escape to the shell, following the partial example:
char buff[50], cur_user[50] ;
int cur_user_pid = 0 ;
sprintf(buff, "x=$(ps -oppid= %d) ; ps -ouid,user= $x", getppid()) ;
FILE *fp = popen(buff, "r") ;
fscanf(fp, "%d %s", &cur_user_pid, curr_user) ;
pclose(fp) ;
If not possible to escape to the shell, I believe possible to travel process tree using /proc file system. Per How to get a grandparents/ancestors process ID?, it available in the fourth field of '/proc/[pid]/stat'.
I noticed it lately that the upstart process is running under a log on user, I thought it is running under root. So, my solution below looks better than one with extracting user id from /proc/self/cgroup.
#include <stdio.h>
int main() {
int uid = -1;
char line[100];
pid_t sid = getsid(0);
sprintf(line, "/proc/%d/status", sid);
FILE *file = fopen(line, "r");
while (fgets(line, sizeof(line), file)) {
if (sscanf(line, "Uid: %d", &uid) == 1)
break;
}
fclose(file);
printf("Logged on User Id: ");
uid != -1 ? printf("%d\n", uid) : printf("Not found\n");
}

Create a directory in filesystem in C

I am doing a project in C, and I'm stuck on one thing, i need to check if the directoy "/var/log/PROJECT " exist, if not, my program must create it, the aplication will always run on super user, this is what im doing:
struct stat st = {0};
if (stat("/var/log/project/PROJECT", &st) == -1) {
printf("im creating the folder\n");
mode_t process_mask = umask(0);
int result_code = mkdir("/var/log/project/PROJECT", 0777);
umask(process_mask);
}else{
printf("exist\n");
}
Sorry for asking to "do my homework" but im really stuck...
Well, I'm going to run with my suspicion. If the problem is that the parent directory of the directory you're trying to create does not exist, the solution is to create the parent directory before it. This is not terribly difficult to do with recursion, thankfully. Try this:
#include <errno.h>
#include <libgen.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
int create_directory_with_parents(char const *pathname, mode_t modus) {
struct stat st;
if(stat(pathname, &st) == -1) {
// If the directory does not yet exist:
//
// Make sure the parent directory is there. dirname() gives us the name of
// the parent directory, then we call this very function recursively because
// we are, after all, in a function that makes sure directories exist.
char *path_cpy = strdup(pathname);
char *dir = dirname(path_cpy);
int err = create_directory_with_parents(dir, modus);
free(path_cpy);
// If we were able to make sure the parent directory exists,
// make the directory we really want.
if(err == 0) {
mode_t process_mask = umask(0);
int err = mkdir(pathname, modus);
umask(process_mask);
}
// err is 0 if everything went well.
return err;
} else if(!S_ISDIR(st.st_mode)) {
// If the "directory" exists but is not a directory, complain.
errno = ENOTDIR;
return -1;
}
// If the directory exists and is a directory, there's nothing to do and
// nothing to complain about.
return 0;
}
int main(void) {
if(0 != create_directory_with_parents("/var/log/project/PROJECT", 0777)) {
perror("Could not create directory or parent of directory: ");
}
}
The recursion ends when the first parent directory is found that exists; that is with / at the latest.
One limitation of this implementation is that all parent directories will have the same access rights as the leaf directory, which may or may not be what you want. If this is not what you want, you'll have to change the modus parameter in the recursive call to create_directory_with_parents. How to pass several modus parameters for several layers of parent directories that may have to be created is something of a design question that depends on what exactly your needs are, so I cannot give a general answer to it.
Why not just execute the mkdir command anyway, and simply ignore the error it will produce if the directory already exists? This will save you playing around with stat.
Is there a reason you need the file permissions to be 777? If not, you can drop the umask bit too.

How to create directory with right permissions using C on Posix

I am trying to write a simple C program that creates directories (a mkdir clone.). This is what I have so far:
#include <stdlib.h>
#include <sys/stat.h> // mkdir
#include <stdio.h> // perror
mode_t getumask()
{
mode_t mask = umask(0);
umask (mask);
return mask;
}
int main(int argc, const char *argv[])
{
mode_t mask = getumask();
printf("%i",mask);
if (mkdir("trial",mask) == -1) {
perror(argv[0]);
exit(EXIT_FAILURE);
}
return 0;
}
This code creates directory with d--------- but I want it to create it with drwxr-xr-x like mkdir do? What am I doing wrong here?
You seem to be misunderstanding what umask is used for. It sets/retrieves the process's file mode creation mask, which in turn is used to turn off bits in the file mode you specify in calls like mkdir, like this (pseduo-code):
real_mode = requested_mode & ~umask
So in your code, since you pass in the value of the umask itself, you end up specifying permissions as zero, which is exactly what you see.
Instead, you should specify the permissions you want in the call to mkdir, like this:
mkdir("trial", 0755)
As Eric says, umask is the complement of the actual permission mode you get. So instead of passing mask itself to mkdir(), you should pass 0777-mask to mkdir().
Disclaimer: I extracted this answer from the OPs question. Answers should not be contained in the question itself.
Answer provided by yasar:
This is the working solution for me:
int main(int argc, const char *argv[])
{
if (mkdir("trial",0777) == -1) {
perror(argv[0]);
exit(EXIT_FAILURE);
}
return 0;
}
Setting right permissions according to umask is automatically handled. So I only needed to call mkdir with full permissions, and that gets chopped according to current umask.

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.

determine file permissions for the current user

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.

Resources