1 /* Author: Trusted Computer Solutions, Inc.
2  *
3  * Modified:
4  * Yuichi Nakamura <ynakam@hitachisoft.jp>
5  - Stubs are used when DISABLE_SETRANS is defined,
6    it is to reduce size for such as embedded devices.
7 */
8 
9 #include <sys/types.h>
10 #include <sys/socket.h>
11 #include <sys/un.h>
12 
13 #include <errno.h>
14 #include <stdlib.h>
15 #include <netdb.h>
16 #include <fcntl.h>
17 #include <stdio.h>
18 #include <string.h>
19 #include <ctype.h>
20 #include <unistd.h>
21 #include "dso.h"
22 #include "selinux_internal.h"
23 #include "setrans_internal.h"
24 
25 #ifndef DISABLE_SETRANS
26 static int mls_enabled = -1;
27 
28 // Simple cache
29 static __thread char * prev_t2r_trans = NULL;
30 static __thread char * prev_t2r_raw = NULL;
31 static __thread char * prev_r2t_trans = NULL;
32 static __thread char * prev_r2t_raw = NULL;
33 static __thread char *prev_r2c_trans = NULL;
34 static __thread char * prev_r2c_raw = NULL;
35 
36 static pthread_once_t once = PTHREAD_ONCE_INIT;
37 static pthread_key_t destructor_key;
38 static int destructor_key_initialized = 0;
39 static __thread char destructor_initialized;
40 
41 /*
42  * setransd_open
43  *
44  * This function opens a socket to the setransd.
45  * Returns:  on success, a file descriptor ( >= 0 ) to the socket
46  *           on error, a negative value
47  */
setransd_open(void)48 static int setransd_open(void)
49 {
50 	struct sockaddr_un addr;
51 	int fd;
52 #ifdef SOCK_CLOEXEC
53 	fd = socket(PF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
54 	if (fd < 0 && errno == EINVAL)
55 #endif
56 	{
57 		fd = socket(PF_UNIX, SOCK_STREAM, 0);
58 		if (fd >= 0)
59 			if (fcntl(fd, F_SETFD, FD_CLOEXEC)) {
60 				close(fd);
61 				return -1;
62 			}
63 	}
64 	if (fd < 0)
65 		return -1;
66 
67 	memset(&addr, 0, sizeof(addr));
68 	addr.sun_family = AF_UNIX;
69 	strncpy(addr.sun_path, SETRANS_UNIX_SOCKET, sizeof(addr.sun_path));
70 	if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
71 		close(fd);
72 		return -1;
73 	}
74 
75 	return fd;
76 }
77 
78 /* Returns: 0 on success, <0 on failure */
79 static int
send_request(int fd,uint32_t function,const char * data1,const char * data2)80 send_request(int fd, uint32_t function, const char *data1, const char *data2)
81 {
82 	struct msghdr msgh;
83 	struct iovec iov[5];
84 	uint32_t data1_size;
85 	uint32_t data2_size;
86 	ssize_t count, expected;
87 	unsigned int i;
88 
89 	if (fd < 0)
90 		return -1;
91 
92 	if (!data1)
93 		data1 = "";
94 	if (!data2)
95 		data2 = "";
96 
97 	data1_size = strlen(data1) + 1;
98 	data2_size = strlen(data2) + 1;
99 
100 	iov[0].iov_base = &function;
101 	iov[0].iov_len = sizeof(function);
102 	iov[1].iov_base = &data1_size;
103 	iov[1].iov_len = sizeof(data1_size);
104 	iov[2].iov_base = &data2_size;
105 	iov[2].iov_len = sizeof(data2_size);
106 	iov[3].iov_base = (char *)data1;
107 	iov[3].iov_len = data1_size;
108 	iov[4].iov_base = (char *)data2;
109 	iov[4].iov_len = data2_size;
110 	memset(&msgh, 0, sizeof(msgh));
111 	msgh.msg_iov = iov;
112 	msgh.msg_iovlen = sizeof(iov) / sizeof(iov[0]);
113 
114 	expected = 0;
115 	for (i = 0; i < sizeof(iov) / sizeof(iov[0]); i++)
116 		expected += iov[i].iov_len;
117 
118 	while (((count = sendmsg(fd, &msgh, MSG_NOSIGNAL)) < 0)
119 	       && (errno == EINTR)) ;
120 	if (count < 0 || count != expected)
121 		return -1;
122 
123 	return 0;
124 }
125 
126 /* Returns: 0 on success, <0 on failure */
127 static int
receive_response(int fd,uint32_t function,char ** outdata,int32_t * ret_val)128 receive_response(int fd, uint32_t function, char **outdata, int32_t * ret_val)
129 {
130 	struct iovec resp_hdr[3];
131 	uint32_t func;
132 	uint32_t data_size;
133 	char *data;
134 	struct iovec resp_data;
135 	ssize_t count;
136 
137 	if (fd < 0)
138 		return -1;
139 
140 	resp_hdr[0].iov_base = &func;
141 	resp_hdr[0].iov_len = sizeof(func);
142 	resp_hdr[1].iov_base = &data_size;
143 	resp_hdr[1].iov_len = sizeof(data_size);
144 	resp_hdr[2].iov_base = ret_val;
145 	resp_hdr[2].iov_len = sizeof(*ret_val);
146 
147 	while (((count = readv(fd, resp_hdr, 3)) < 0) && (errno == EINTR)) ;
148 	if (count != (sizeof(func) + sizeof(data_size) + sizeof(*ret_val))) {
149 		return -1;
150 	}
151 
152 	if (func != function || !data_size || data_size > MAX_DATA_BUF) {
153 		return -1;
154 	}
155 
156 	data = malloc(data_size);
157 	if (!data)
158 		return -1;
159 	/* coveriety doesn't realize that data will be initialized in readv */
160 	memset(data, 0, data_size);
161 
162 	resp_data.iov_base = data;
163 	resp_data.iov_len = data_size;
164 
165 	while (((count = readv(fd, &resp_data, 1))) < 0 && (errno == EINTR)) ;
166 	if (count < 0 || (uint32_t) count != data_size ||
167 	    data[data_size - 1] != '\0') {
168 		free(data);
169 		return -1;
170 	}
171 	*outdata = data;
172 	return 0;
173 }
174 
raw_to_trans_context(const char * raw,char ** transp)175 static int raw_to_trans_context(const char *raw, char **transp)
176 {
177 	int ret;
178 	int32_t ret_val;
179 	int fd;
180 
181 	*transp = NULL;
182 
183 	fd = setransd_open();
184 	if (fd < 0)
185 		return fd;
186 
187 	ret = send_request(fd, RAW_TO_TRANS_CONTEXT, raw, NULL);
188 	if (ret)
189 		goto out;
190 
191 	ret = receive_response(fd, RAW_TO_TRANS_CONTEXT, transp, &ret_val);
192 	if (ret)
193 		goto out;
194 
195 	ret = ret_val;
196       out:
197 	close(fd);
198 	return ret;
199 }
200 
trans_to_raw_context(const char * trans,char ** rawp)201 static int trans_to_raw_context(const char *trans, char **rawp)
202 {
203 	int ret;
204 	int32_t ret_val;
205 	int fd;
206 
207 	*rawp = NULL;
208 
209 	fd = setransd_open();
210 	if (fd < 0)
211 		return fd;
212 	ret = send_request(fd, TRANS_TO_RAW_CONTEXT, trans, NULL);
213 	if (ret)
214 		goto out;
215 
216 	ret = receive_response(fd, TRANS_TO_RAW_CONTEXT, rawp, &ret_val);
217 	if (ret)
218 		goto out;
219 
220 	ret = ret_val;
221       out:
222 	close(fd);
223 	return ret;
224 }
225 
raw_context_to_color(const char * raw,char ** colors)226 static int raw_context_to_color(const char *raw, char **colors)
227 {
228 	int ret;
229 	int32_t ret_val;
230 	int fd;
231 
232 	fd = setransd_open();
233 	if (fd < 0)
234 		return fd;
235 
236 	ret = send_request(fd, RAW_CONTEXT_TO_COLOR, raw, NULL);
237 	if (ret)
238 		goto out;
239 
240 	ret = receive_response(fd, RAW_CONTEXT_TO_COLOR, colors, &ret_val);
241 	if (ret)
242 		goto out;
243 
244 	ret = ret_val;
245 out:
246 	close(fd);
247 	return ret;
248 }
249 
setrans_thread_destructor(void * unused)250 static void setrans_thread_destructor(void __attribute__((unused)) *unused)
251 {
252 	free(prev_t2r_trans);
253 	free(prev_t2r_raw);
254 	free(prev_r2t_trans);
255 	free(prev_r2t_raw);
256 	free(prev_r2c_trans);
257 	free(prev_r2c_raw);
258 }
259 
260 void __attribute__((destructor)) setrans_lib_destructor(void);
261 
setrans_lib_destructor(void)262 void hidden __attribute__((destructor)) setrans_lib_destructor(void)
263 {
264 	if (destructor_key_initialized)
265 		__selinux_key_delete(destructor_key);
266 }
267 
init_thread_destructor(void)268 static inline void init_thread_destructor(void)
269 {
270 	if (destructor_initialized == 0) {
271 		__selinux_setspecific(destructor_key, (void *)1);
272 		destructor_initialized = 1;
273 	}
274 }
275 
init_context_translations(void)276 static void init_context_translations(void)
277 {
278 	if (__selinux_key_create(&destructor_key, setrans_thread_destructor) == 0)
279 		destructor_key_initialized = 1;
280 
281 	mls_enabled = is_selinux_mls_enabled();
282 }
283 
selinux_trans_to_raw_context(const char * trans,char ** rawp)284 int selinux_trans_to_raw_context(const char * trans,
285 				 char ** rawp)
286 {
287 	if (!trans) {
288 		*rawp = NULL;
289 		return 0;
290 	}
291 
292 	__selinux_once(once, init_context_translations);
293 	init_thread_destructor();
294 
295 	if (!mls_enabled) {
296 		*rawp = strdup(trans);
297 		goto out;
298 	}
299 
300 	if (prev_t2r_trans && strcmp(prev_t2r_trans, trans) == 0) {
301 		*rawp = strdup(prev_t2r_raw);
302 	} else {
303 		free(prev_t2r_trans);
304 		prev_t2r_trans = NULL;
305 		free(prev_t2r_raw);
306 		prev_t2r_raw = NULL;
307 		if (trans_to_raw_context(trans, rawp))
308 			*rawp = strdup(trans);
309 		if (*rawp) {
310 			prev_t2r_trans = strdup(trans);
311 			if (!prev_t2r_trans)
312 				goto out;
313 			prev_t2r_raw = strdup(*rawp);
314 			if (!prev_t2r_raw) {
315 				free(prev_t2r_trans);
316 				prev_t2r_trans = NULL;
317 			}
318 		}
319 	}
320       out:
321 	return *rawp ? 0 : -1;
322 }
323 
hidden_def(selinux_trans_to_raw_context)324 hidden_def(selinux_trans_to_raw_context)
325 
326 int selinux_raw_to_trans_context(const char * raw,
327 				 char ** transp)
328 {
329 	if (!raw) {
330 		*transp = NULL;
331 		return 0;
332 	}
333 
334 	__selinux_once(once, init_context_translations);
335 	init_thread_destructor();
336 
337 	if (!mls_enabled) {
338 		*transp = strdup(raw);
339 		goto out;
340 	}
341 
342 	if (prev_r2t_raw && strcmp(prev_r2t_raw, raw) == 0) {
343 		*transp = strdup(prev_r2t_trans);
344 	} else {
345 		free(prev_r2t_raw);
346 		prev_r2t_raw = NULL;
347 		free(prev_r2t_trans);
348 		prev_r2t_trans = NULL;
349 		if (raw_to_trans_context(raw, transp))
350 			*transp = strdup(raw);
351 		if (*transp) {
352 			prev_r2t_raw = strdup(raw);
353 			if (!prev_r2t_raw)
354 				goto out;
355 			prev_r2t_trans = strdup(*transp);
356 			if (!prev_r2t_trans) {
357 				free(prev_r2t_raw);
358 				prev_r2t_raw = NULL;
359 			}
360 		}
361 	}
362       out:
363 	return *transp ? 0 : -1;
364 }
365 
hidden_def(selinux_raw_to_trans_context)366 hidden_def(selinux_raw_to_trans_context)
367 
368 int selinux_raw_context_to_color(const char * raw, char **transp)
369 {
370 	if (!raw) {
371 		*transp = NULL;
372 		return -1;
373 	}
374 
375 	__selinux_once(once, init_context_translations);
376 	init_thread_destructor();
377 
378 	if (prev_r2c_raw && strcmp(prev_r2c_raw, raw) == 0) {
379 		*transp = strdup(prev_r2c_trans);
380 	} else {
381 		free(prev_r2c_raw);
382 		prev_r2c_raw = NULL;
383 		free(prev_r2c_trans);
384 		prev_r2c_trans = NULL;
385 		if (raw_context_to_color(raw, transp))
386 			return -1;
387 		if (*transp) {
388 			prev_r2c_raw = strdup(raw);
389 			if (!prev_r2c_raw)
390 				goto out;
391 			prev_r2c_trans = strdup(*transp);
392 			if (!prev_r2c_trans) {
393 				free(prev_r2c_raw);
394 				prev_r2c_raw = NULL;
395 			}
396 		}
397 	}
398       out:
399 	return *transp ? 0 : -1;
400 }
401 
402 hidden_def(selinux_raw_context_to_color)
403 #else /*DISABLE_SETRANS*/
404 
405 int selinux_trans_to_raw_context(const char * trans,
406 				 char ** rawp)
407 {
408 	if (!trans) {
409 		*rawp = NULL;
410 		return 0;
411 	}
412 
413 	*rawp = strdup(trans);
414 
415 	return *rawp ? 0 : -1;
416 }
417 
418 hidden_def(selinux_trans_to_raw_context)
419 
420 int selinux_raw_to_trans_context(const char * raw,
421 				 char ** transp)
422 {
423 	if (!raw) {
424 		*transp = NULL;
425 		return 0;
426 	}
427 	*transp = strdup(raw);
428 
429 	return *transp ? 0 : -1;
430 }
431 
432 hidden_def(selinux_raw_to_trans_context)
433 #endif /*DISABLE_SETRANS*/
434