aboutsummaryrefslogtreecommitdiffstats
path: root/src/jailtest/utils.c
blob: b24783355df9519add73bc0b3782b9f6c37d4352 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#include "jailtest.h"
#include <errno.h>
#include <pwd.h>
#include <dirent.h>

#define BUFLEN 4096

char *get_sudo_user(void) {
	char *user = getenv("SUDO_USER");
	if (!user) {
		user = getpwuid(getuid())->pw_name;
		if (!user) {
			fprintf(stderr, "Error: cannot detect login user\n");
			exit(1);
		}
	}

	return user;
}

char *get_homedir(const char *user, uid_t *uid, gid_t *gid) {
	// find home directory
	struct passwd *pw = getpwnam(user);
	if (!pw)
		goto errexit;

	char *home = pw->pw_dir;
	if (!home)
		goto errexit;

	*uid = pw->pw_uid;
	*gid = pw->pw_gid;

	return home;

errexit:
	fprintf(stderr, "Error: cannot find home directory for user %s\n", user);
	exit(1);
}

int find_child(pid_t parent, pid_t *child) {
	*child = 0;				  // use it to flag a found child

	DIR *dir;
	if (!(dir = opendir("/proc"))) {
		// sleep 2 seconds and try again
		sleep(2);
		if (!(dir = opendir("/proc"))) {
			fprintf(stderr, "Error: cannot open /proc directory\n");
			exit(1);
		}
	}

	struct dirent *entry;
	char *end;
	while (*child == 0 && (entry = readdir(dir))) {
		pid_t pid = strtol(entry->d_name, &end, 10);
		if (end == entry->d_name || *end)
			continue;
		if (pid == parent)
			continue;

		// open stat file
		char *file;
		if (asprintf(&file, "/proc/%u/status", pid) == -1) {
			perror("asprintf");
			exit(1);
		}
		FILE *fp = fopen(file, "r");
		if (!fp) {
			free(file);
			continue;
		}

		// look for firejail executable name
		char buf[BUFLEN];
		while (fgets(buf, BUFLEN - 1, fp)) {
			if (strncmp(buf, "PPid:", 5) == 0) {
				char *ptr = buf + 5;
				while (*ptr != '\0' && (*ptr == ' ' || *ptr == '\t')) {
					ptr++;
				}
				if (*ptr == '\0') {
					fprintf(stderr, "Error: cannot read /proc file\n");
					exit(1);
				}
				if (parent == atoi(ptr)) {
					// we don't want /usr/bin/xdg-dbus-proxy!
					char *cmdline = pid_proc_cmdline(pid);
					if (strncmp(cmdline, XDG_DBUS_PROXY_PATH, strlen(XDG_DBUS_PROXY_PATH)) != 0)
						*child = pid;
					free(cmdline);
				}
				break;		  // stop reading the file
			}
		}
		fclose(fp);
		free(file);
	}
	closedir(dir);
	return (*child)? 0:1;			  // 0 = found, 1 = not found
}

pid_t switch_to_child(pid_t pid) {
	pid_t rv = pid;
	errno = 0;
	char *comm = pid_proc_comm(pid);
	if (!comm) {
		if (errno == ENOENT)
			fprintf(stderr, "Error: cannot find process with pid %d\n", pid);
		else
			fprintf(stderr, "Error: cannot read /proc file\n");
		exit(1);
	}

	if (strcmp(comm, "firejail") == 0) {
		if (find_child(pid, &rv) == 1) {
			fprintf(stderr, "Error: no valid sandbox\n");
			exit(1);
		}
	}
	free(comm);
	return rv;
}