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 unsigned char has_setrans;
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 (!has_setrans)
265 		return;
266 	if (destructor_key_initialized)
267 		__selinux_key_delete(destructor_key);
268 }
269 
init_thread_destructor(void)270 static inline void init_thread_destructor(void)
271 {
272 	if (!has_setrans)
273 		return;
274 	if (destructor_initialized == 0) {
275 		__selinux_setspecific(destructor_key, (void *)1);
276 		destructor_initialized = 1;
277 	}
278 }
279 
init_context_translations(void)280 static void init_context_translations(void)
281 {
282 	has_setrans = (access(SETRANS_UNIX_SOCKET, F_OK) == 0);
283 	if (!has_setrans)
284 		return;
285 	if (__selinux_key_create(&destructor_key, setrans_thread_destructor) == 0)
286 		destructor_key_initialized = 1;
287 }
288 
selinux_trans_to_raw_context(const char * trans,char ** rawp)289 int selinux_trans_to_raw_context(const char * trans,
290 				 char ** rawp)
291 {
292 	if (!trans) {
293 		*rawp = NULL;
294 		return 0;
295 	}
296 
297 	__selinux_once(once, init_context_translations);
298 	init_thread_destructor();
299 
300 	if (!has_setrans) {
301 		*rawp = strdup(trans);
302 		goto out;
303 	}
304 
305 	if (prev_t2r_trans && strcmp(prev_t2r_trans, trans) == 0) {
306 		*rawp = strdup(prev_t2r_raw);
307 	} else {
308 		free(prev_t2r_trans);
309 		prev_t2r_trans = NULL;
310 		free(prev_t2r_raw);
311 		prev_t2r_raw = NULL;
312 		if (trans_to_raw_context(trans, rawp))
313 			*rawp = strdup(trans);
314 		if (*rawp) {
315 			prev_t2r_trans = strdup(trans);
316 			if (!prev_t2r_trans)
317 				goto out;
318 			prev_t2r_raw = strdup(*rawp);
319 			if (!prev_t2r_raw) {
320 				free(prev_t2r_trans);
321 				prev_t2r_trans = NULL;
322 			}
323 		}
324 	}
325       out:
326 	return *rawp ? 0 : -1;
327 }
328 
hidden_def(selinux_trans_to_raw_context)329 hidden_def(selinux_trans_to_raw_context)
330 
331 int selinux_raw_to_trans_context(const char * raw,
332 				 char ** transp)
333 {
334 	if (!raw) {
335 		*transp = NULL;
336 		return 0;
337 	}
338 
339 	__selinux_once(once, init_context_translations);
340 	init_thread_destructor();
341 
342 	if (!has_setrans)  {
343 		*transp = strdup(raw);
344 		goto out;
345 	}
346 
347 	if (prev_r2t_raw && strcmp(prev_r2t_raw, raw) == 0) {
348 		*transp = strdup(prev_r2t_trans);
349 	} else {
350 		free(prev_r2t_raw);
351 		prev_r2t_raw = NULL;
352 		free(prev_r2t_trans);
353 		prev_r2t_trans = NULL;
354 		if (raw_to_trans_context(raw, transp))
355 			*transp = strdup(raw);
356 		if (*transp) {
357 			prev_r2t_raw = strdup(raw);
358 			if (!prev_r2t_raw)
359 				goto out;
360 			prev_r2t_trans = strdup(*transp);
361 			if (!prev_r2t_trans) {
362 				free(prev_r2t_raw);
363 				prev_r2t_raw = NULL;
364 			}
365 		}
366 	}
367       out:
368 	return *transp ? 0 : -1;
369 }
370 
hidden_def(selinux_raw_to_trans_context)371 hidden_def(selinux_raw_to_trans_context)
372 
373 int selinux_raw_context_to_color(const char * raw, char **transp)
374 {
375 	if (!raw) {
376 		*transp = NULL;
377 		return -1;
378 	}
379 
380 	__selinux_once(once, init_context_translations);
381 	init_thread_destructor();
382 
383 	if (!has_setrans) {
384 		*transp = strdup(raw);
385 		goto out;
386 	}
387 
388 	if (prev_r2c_raw && strcmp(prev_r2c_raw, raw) == 0) {
389 		*transp = strdup(prev_r2c_trans);
390 	} else {
391 		free(prev_r2c_raw);
392 		prev_r2c_raw = NULL;
393 		free(prev_r2c_trans);
394 		prev_r2c_trans = NULL;
395 		if (raw_context_to_color(raw, transp))
396 			return -1;
397 		if (*transp) {
398 			prev_r2c_raw = strdup(raw);
399 			if (!prev_r2c_raw)
400 				goto out;
401 			prev_r2c_trans = strdup(*transp);
402 			if (!prev_r2c_trans) {
403 				free(prev_r2c_raw);
404 				prev_r2c_raw = NULL;
405 			}
406 		}
407 	}
408       out:
409 	return *transp ? 0 : -1;
410 }
411 
412 hidden_def(selinux_raw_context_to_color)
413 #else /*DISABLE_SETRANS*/
414 
415 int selinux_trans_to_raw_context(const char * trans,
416 				 char ** rawp)
417 {
418 	if (!trans) {
419 		*rawp = NULL;
420 		return 0;
421 	}
422 
423 	*rawp = strdup(trans);
424 
425 	return *rawp ? 0 : -1;
426 }
427 
428 hidden_def(selinux_trans_to_raw_context)
429 
430 int selinux_raw_to_trans_context(const char * raw,
431 				 char ** transp)
432 {
433 	if (!raw) {
434 		*transp = NULL;
435 		return 0;
436 	}
437 	*transp = strdup(raw);
438 
439 	return *transp ? 0 : -1;
440 }
441 
442 hidden_def(selinux_raw_to_trans_context)
443 #endif /*DISABLE_SETRANS*/
444