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
14 static __thread char *prev_current = UNSET;
15 static __thread char * prev_exec = UNSET;
16 static __thread char * prev_fscreate = UNSET;
17 static __thread char * prev_keycreate = UNSET;
18 static __thread char * prev_sockcreate = UNSET;
19
20 static pthread_once_t once = PTHREAD_ONCE_INIT;
21 static pthread_key_t destructor_key;
22 static int destructor_key_initialized = 0;
23 static __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
selinux_gettid(void)37 static pid_t selinux_gettid(void)
38 {
39 #if HAVE_GETTID
40 return gettid();
41 #else
42 return syscall(__NR_gettid);
43 #endif
44 }
45
procattr_thread_destructor(void * unused)46 static 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
60 void __attribute__((destructor)) procattr_destructor(void);
61
procattr_destructor(void)62 void __attribute__((destructor)) procattr_destructor(void)
63 {
64 if (destructor_key_initialized)
65 __selinux_key_delete(destructor_key);
66 }
67
init_thread_destructor(void)68 static 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
init_procattr(void)76 static void init_procattr(void)
77 {
78 if (__selinux_key_create(&destructor_key, procattr_thread_destructor) == 0) {
79 destructor_key_initialized = 1;
80 }
81 }
82
openattr(pid_t pid,const char * attr,int flags)83 static 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);
109 out:
110 free(path);
111 return fd;
112 }
113
getprocattrcon_raw(char ** context,pid_t pid,const char * attr)114 static 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
getprocattrcon(char ** context,pid_t pid,const char * attr)197 static 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
setprocattrcon_raw(const char * context,pid_t pid,const char * attr)213 static 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 }
267 out:
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
setprocattrcon(const char * context,pid_t pid,const char * attr)282 static 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
342 all_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