1#include <sys/syscall.h>
2#include <unistd.h>
3#include <fcntl.h>
4#include <pthread.h>
5#include <string.h>
6#include <stdlib.h>
7#include <stdio.h>
8#include <errno.h>
9#include "selinux_internal.h"
10#include "policy.h"
11
12#define UNSET (char *) -1
13
14static __thread char *prev_current = UNSET;
15static __thread char * prev_exec = UNSET;
16static __thread char * prev_fscreate = UNSET;
17static __thread char * prev_keycreate = UNSET;
18static __thread char * prev_sockcreate = UNSET;
19
20static pthread_once_t once = PTHREAD_ONCE_INIT;
21static pthread_key_t destructor_key;
22static int destructor_key_initialized = 0;
23static __thread char destructor_initialized;
24
25static pid_t gettid(void)
26{
27	return syscall(__NR_gettid);
28}
29
30static void procattr_thread_destructor(void __attribute__((unused)) *unused)
31{
32	if (prev_current != UNSET)
33		free(prev_current);
34	if (prev_exec != UNSET)
35		free(prev_exec);
36	if (prev_fscreate != UNSET)
37		free(prev_fscreate);
38	if (prev_keycreate != UNSET)
39		free(prev_keycreate);
40	if (prev_sockcreate != UNSET)
41		free(prev_sockcreate);
42}
43
44void __attribute__((destructor)) procattr_destructor(void);
45
46void hidden __attribute__((destructor)) procattr_destructor(void)
47{
48	if (destructor_key_initialized)
49		__selinux_key_delete(destructor_key);
50}
51
52static inline void init_thread_destructor(void)
53{
54	if (destructor_initialized == 0) {
55		__selinux_setspecific(destructor_key, (void *)1);
56		destructor_initialized = 1;
57	}
58}
59
60static void init_procattr(void)
61{
62	if (__selinux_key_create(&destructor_key, procattr_thread_destructor) == 0) {
63		destructor_key_initialized = 1;
64	}
65}
66
67static int openattr(pid_t pid, const char *attr, int flags)
68{
69	int fd, rc;
70	char *path;
71	pid_t tid;
72
73	if (pid > 0)
74		rc = asprintf(&path, "/proc/%d/attr/%s", pid, attr);
75	else {
76		rc = asprintf(&path, "/proc/thread-self/attr/%s", attr);
77		if (rc < 0)
78			return -1;
79		fd = open(path, flags | O_CLOEXEC);
80		if (fd >= 0 || errno != ENOENT)
81			goto out;
82		free(path);
83		tid = gettid();
84		rc = asprintf(&path, "/proc/self/task/%d/attr/%s", tid, attr);
85	}
86	if (rc < 0)
87		return -1;
88
89	fd = open(path, flags | O_CLOEXEC);
90out:
91	free(path);
92	return fd;
93}
94
95static int getprocattrcon_raw(char ** context,
96			      pid_t pid, const char *attr)
97{
98	char *buf;
99	size_t size;
100	int fd;
101	ssize_t ret;
102	int errno_hold;
103	char * prev_context;
104
105	__selinux_once(once, init_procattr);
106	init_thread_destructor();
107
108	switch (attr[0]) {
109		case 'c':
110			prev_context = prev_current;
111			break;
112		case 'e':
113			prev_context = prev_exec;
114			break;
115		case 'f':
116			prev_context = prev_fscreate;
117			break;
118		case 'k':
119			prev_context = prev_keycreate;
120			break;
121		case 's':
122			prev_context = prev_sockcreate;
123			break;
124		case 'p':
125			prev_context = NULL;
126			break;
127		default:
128			errno = ENOENT;
129			return -1;
130	};
131
132	if (prev_context && prev_context != UNSET) {
133		*context = strdup(prev_context);
134		if (!(*context)) {
135			return -1;
136		}
137		return 0;
138	}
139
140	fd = openattr(pid, attr, O_RDONLY);
141	if (fd < 0)
142		return -1;
143
144	size = selinux_page_size;
145	buf = malloc(size);
146	if (!buf) {
147		ret = -1;
148		goto out;
149	}
150	memset(buf, 0, size);
151
152	do {
153		ret = read(fd, buf, size - 1);
154	} while (ret < 0 && errno == EINTR);
155	if (ret < 0)
156		goto out2;
157
158	if (ret == 0) {
159		*context = NULL;
160		goto out2;
161	}
162
163	*context = strdup(buf);
164	if (!(*context)) {
165		ret = -1;
166		goto out2;
167	}
168	ret = 0;
169      out2:
170	free(buf);
171      out:
172	errno_hold = errno;
173	close(fd);
174	errno = errno_hold;
175	return ret;
176}
177
178static int getprocattrcon(char ** context,
179			  pid_t pid, const char *attr)
180{
181	int ret;
182	char * rcontext;
183
184	ret = getprocattrcon_raw(&rcontext, pid, attr);
185
186	if (!ret) {
187		ret = selinux_raw_to_trans_context(rcontext, context);
188		freecon(rcontext);
189	}
190
191	return ret;
192}
193
194static int setprocattrcon_raw(const char * context,
195			      pid_t pid, const char *attr)
196{
197	int fd;
198	ssize_t ret;
199	int errno_hold;
200	char **prev_context, *context2 = NULL;
201
202	__selinux_once(once, init_procattr);
203	init_thread_destructor();
204
205	switch (attr[0]) {
206		case 'c':
207			prev_context = &prev_current;
208			break;
209		case 'e':
210			prev_context = &prev_exec;
211			break;
212		case 'f':
213			prev_context = &prev_fscreate;
214			break;
215		case 'k':
216			prev_context = &prev_keycreate;
217			break;
218		case 's':
219			prev_context = &prev_sockcreate;
220			break;
221		default:
222			errno = ENOENT;
223			return -1;
224	};
225
226	if (!context && !*prev_context)
227		return 0;
228	if (context && *prev_context && *prev_context != UNSET
229	    && !strcmp(context, *prev_context))
230		return 0;
231
232	fd = openattr(pid, attr, O_RDWR);
233	if (fd < 0)
234		return -1;
235	if (context) {
236		ret = -1;
237		context2 = strdup(context);
238		if (!context2)
239			goto out;
240		do {
241			ret = write(fd, context2, strlen(context2) + 1);
242		} while (ret < 0 && errno == EINTR);
243	} else {
244		do {
245			ret = write(fd, NULL, 0);	/* clear */
246		} while (ret < 0 && errno == EINTR);
247	}
248out:
249	errno_hold = errno;
250	close(fd);
251	errno = errno_hold;
252	if (ret < 0) {
253		free(context2);
254		return -1;
255	} else {
256		if (*prev_context != UNSET)
257			free(*prev_context);
258		*prev_context = context2;
259		return 0;
260	}
261}
262
263static int setprocattrcon(const char * context,
264			  pid_t pid, const char *attr)
265{
266	int ret;
267	char * rcontext;
268
269	if (selinux_trans_to_raw_context(context, &rcontext))
270		return -1;
271
272	ret = setprocattrcon_raw(rcontext, pid, attr);
273
274	freecon(rcontext);
275
276	return ret;
277}
278
279#define getselfattr_def(fn, attr) \
280	int get##fn##_raw(char **c) \
281	{ \
282		return getprocattrcon_raw(c, 0, #attr); \
283	} \
284	int get##fn(char **c) \
285	{ \
286		return getprocattrcon(c, 0, #attr); \
287	}
288
289#define setselfattr_def(fn, attr) \
290	int set##fn##_raw(const char * c) \
291	{ \
292		return setprocattrcon_raw(c, 0, #attr); \
293	} \
294	int set##fn(const char * c) \
295	{ \
296		return setprocattrcon(c, 0, #attr); \
297	}
298
299#define all_selfattr_def(fn, attr) \
300	getselfattr_def(fn, attr)	 \
301	setselfattr_def(fn, attr)
302
303#define getpidattr_def(fn, attr) \
304	int get##fn##_raw(pid_t pid, char **c)	\
305	{ \
306		return getprocattrcon_raw(c, pid, #attr); \
307	} \
308	int get##fn(pid_t pid, char **c)	\
309	{ \
310		return getprocattrcon(c, pid, #attr); \
311	}
312
313all_selfattr_def(con, current)
314    getpidattr_def(pidcon, current)
315    getselfattr_def(prevcon, prev)
316    all_selfattr_def(execcon, exec)
317    all_selfattr_def(fscreatecon, fscreate)
318    all_selfattr_def(sockcreatecon, sockcreate)
319    all_selfattr_def(keycreatecon, keycreate)
320
321    hidden_def(getcon_raw)
322    hidden_def(getcon)
323    hidden_def(getexeccon_raw)
324    hidden_def(getfilecon_raw)
325    hidden_def(getfilecon)
326    hidden_def(getfscreatecon_raw)
327    hidden_def(getkeycreatecon_raw)
328    hidden_def(getpeercon_raw)
329    hidden_def(getpidcon_raw)
330    hidden_def(getprevcon_raw)
331    hidden_def(getprevcon)
332    hidden_def(getsockcreatecon_raw)
333    hidden_def(setcon_raw)
334    hidden_def(setexeccon_raw)
335    hidden_def(setexeccon)
336    hidden_def(setfilecon_raw)
337    hidden_def(setfscreatecon_raw)
338    hidden_def(setkeycreatecon_raw)
339    hidden_def(setsockcreatecon_raw)
340