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
gettid(void)25 static pid_t gettid(void)
26 {
27 return syscall(__NR_gettid);
28 }
29
procattr_thread_destructor(void * unused)30 static 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
44 void __attribute__((destructor)) procattr_destructor(void);
45
procattr_destructor(void)46 void hidden __attribute__((destructor)) procattr_destructor(void)
47 {
48 if (destructor_key_initialized)
49 __selinux_key_delete(destructor_key);
50 }
51
init_thread_destructor(void)52 static 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
init_procattr(void)60 static void init_procattr(void)
61 {
62 if (__selinux_key_create(&destructor_key, procattr_thread_destructor) == 0) {
63 destructor_key_initialized = 1;
64 }
65 }
66
openattr(pid_t pid,const char * attr,int flags)67 static 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);
90 out:
91 free(path);
92 return fd;
93 }
94
getprocattrcon_raw(char ** context,pid_t pid,const char * attr)95 static 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
getprocattrcon(char ** context,pid_t pid,const char * attr)178 static 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
setprocattrcon_raw(const char * context,pid_t pid,const char * attr)194 static 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 }
248 out:
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
setprocattrcon(const char * context,pid_t pid,const char * attr)263 static 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
313 all_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