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