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