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