1 /*
2  * Author: Karl MacMillan <kmacmillan@tresys.com>
3  *
4  * Modified:
5  *   Dan Walsh <dwalsh@redhat.com> - Added security_load_booleans().
6  */
7 
8 #include <assert.h>
9 #include <sys/types.h>
10 #include <sys/stat.h>
11 #include <fcntl.h>
12 #include <stdlib.h>
13 #include <dirent.h>
14 #include <string.h>
15 #include <stdio.h>
16 #include <stdio_ext.h>
17 #include <unistd.h>
18 #include <fnmatch.h>
19 #include <limits.h>
20 #include <ctype.h>
21 #include <errno.h>
22 
23 #include "selinux_internal.h"
24 #include "policy.h"
25 
26 #define SELINUX_BOOL_DIR "/booleans/"
27 
filename_select(const struct dirent * d)28 static int filename_select(const struct dirent *d)
29 {
30 	if (d->d_name[0] == '.'
31 	    && (d->d_name[1] == '\0'
32 		|| (d->d_name[1] == '.' && d->d_name[2] == '\0')))
33 		return 0;
34 	return 1;
35 }
36 
security_get_boolean_names(char *** names,int * len)37 int security_get_boolean_names(char ***names, int *len)
38 {
39 	char path[PATH_MAX];
40 	int i, rc;
41 	struct dirent **namelist;
42 	char **n;
43 
44 	if (!len || names == NULL) {
45 		errno = EINVAL;
46 		return -1;
47 	}
48 	if (!selinux_mnt) {
49 		errno = ENOENT;
50 		return -1;
51 	}
52 
53 	snprintf(path, sizeof path, "%s%s", selinux_mnt, SELINUX_BOOL_DIR);
54 	*len = scandir(path, &namelist, &filename_select, alphasort);
55 	if (*len <= 0) {
56 		return -1;
57 	}
58 
59 	n = (char **)malloc(sizeof(char *) * *len);
60 	if (!n) {
61 		rc = -1;
62 		goto bad;
63 	}
64 
65 	for (i = 0; i < *len; i++) {
66 		n[i] = (char *)malloc(_D_ALLOC_NAMLEN(namelist[i]));
67 		if (!n[i]) {
68 			rc = -1;
69 			goto bad_freen;
70 		}
71 		strcpy(n[i], namelist[i]->d_name);
72 	}
73 	rc = 0;
74 	*names = n;
75       out:
76 	for (i = 0; i < *len; i++) {
77 		free(namelist[i]);
78 	}
79 	free(namelist);
80 	return rc;
81       bad_freen:
82 	for (--i; i >= 0; --i)
83 		free(n[i]);
84 	free(n);
85       bad:
86 	goto out;
87 }
88 
hidden_def(security_get_boolean_names)89 hidden_def(security_get_boolean_names)
90 
91 char *selinux_boolean_sub(const char *name)
92 {
93 	char *sub = NULL;
94 	char *line_buf = NULL;
95 	size_t line_len;
96 	FILE *cfg;
97 
98 	if (!name)
99 		return NULL;
100 
101 	cfg = fopen(selinux_booleans_subs_path(), "r");
102 	if (!cfg)
103 		goto out;
104 
105 	while (getline(&line_buf, &line_len, cfg) != -1) {
106 		char *ptr;
107 		char *src = line_buf;
108 		char *dst;
109 		while (*src && isspace(*src))
110 			src++;
111 		if (!*src)
112 			continue;
113 		if (src[0] == '#')
114 			continue;
115 
116 		ptr = src;
117 		while (*ptr && !isspace(*ptr))
118 			ptr++;
119 		*ptr++ = '\0';
120 		if (strcmp(src, name) != 0)
121 			continue;
122 
123 		dst = ptr;
124 		while (*dst && isspace(*dst))
125 			dst++;
126 		if (!*dst)
127 			continue;
128 		ptr=dst;
129 		while (*ptr && !isspace(*ptr))
130 			ptr++;
131 		*ptr='\0';
132 
133 		sub = strdup(dst);
134 
135 		break;
136 	}
137 	free(line_buf);
138 	fclose(cfg);
139 out:
140 	if (!sub)
141 		sub = strdup(name);
142 	return sub;
143 }
144 
hidden_def(selinux_boolean_sub)145 hidden_def(selinux_boolean_sub)
146 
147 static int bool_open(const char *name, int flag) {
148 	char *fname = NULL;
149 	char *alt_name = NULL;
150 	int len;
151 	int fd = -1;
152 	int ret;
153 	char *ptr;
154 
155 	if (!name) {
156 		errno = EINVAL;
157 		return -1;
158 	}
159 
160 	/* note the 'sizeof' gets us enough room for the '\0' */
161 	len = strlen(name) + strlen(selinux_mnt) + sizeof(SELINUX_BOOL_DIR);
162 	fname = malloc(sizeof(char) * len);
163 	if (!fname)
164 		return -1;
165 
166 	ret = snprintf(fname, len, "%s%s%s", selinux_mnt, SELINUX_BOOL_DIR, name);
167 	if (ret < 0)
168 		goto out;
169 	assert(ret < len);
170 
171 	fd = open(fname, flag);
172 	if (fd >= 0 || errno != ENOENT)
173 		goto out;
174 
175 	alt_name = selinux_boolean_sub(name);
176 	if (!alt_name)
177 		goto out;
178 
179 	/* note the 'sizeof' gets us enough room for the '\0' */
180 	len = strlen(alt_name) + strlen(selinux_mnt) + sizeof(SELINUX_BOOL_DIR);
181 	ptr = realloc(fname, len);
182 	if (!ptr)
183 		goto out;
184 	fname = ptr;
185 
186 	ret = snprintf(fname, len, "%s%s%s", selinux_mnt, SELINUX_BOOL_DIR, alt_name);
187 	if (ret < 0)
188 		goto out;
189 	assert(ret < len);
190 
191 	fd = open(fname, flag);
192 out:
193 	free(fname);
194 	free(alt_name);
195 
196 	return fd;
197 }
198 
199 #define STRBUF_SIZE 3
get_bool_value(const char * name,char ** buf)200 static int get_bool_value(const char *name, char **buf)
201 {
202 	int fd, len;
203 	int errno_tmp;
204 
205 	if (!selinux_mnt) {
206 		errno = ENOENT;
207 		return -1;
208 	}
209 
210 	*buf = malloc(sizeof(char) * (STRBUF_SIZE + 1));
211 	if (!*buf)
212 		return -1;
213 
214 	(*buf)[STRBUF_SIZE] = 0;
215 
216 	fd = bool_open(name, O_RDONLY);
217 	if (fd < 0)
218 		goto out_err;
219 
220 	len = read(fd, *buf, STRBUF_SIZE);
221 	errno_tmp = errno;
222 	close(fd);
223 	errno = errno_tmp;
224 	if (len != STRBUF_SIZE)
225 		goto out_err;
226 
227 	return 0;
228 out_err:
229 	free(*buf);
230 	return -1;
231 }
232 
security_get_boolean_pending(const char * name)233 int security_get_boolean_pending(const char *name)
234 {
235 	char *buf;
236 	int val;
237 
238 	if (get_bool_value(name, &buf))
239 		return -1;
240 
241 	if (atoi(&buf[1]))
242 		val = 1;
243 	else
244 		val = 0;
245 	free(buf);
246 	return val;
247 }
248 
security_get_boolean_active(const char * name)249 int security_get_boolean_active(const char *name)
250 {
251 	char *buf;
252 	int val;
253 
254 	if (get_bool_value(name, &buf))
255 		return -1;
256 
257 	buf[1] = '\0';
258 	if (atoi(buf))
259 		val = 1;
260 	else
261 		val = 0;
262 	free(buf);
263 	return val;
264 }
265 
hidden_def(security_get_boolean_active)266 hidden_def(security_get_boolean_active)
267 
268 int security_set_boolean(const char *name, int value)
269 {
270 	int fd, ret;
271 	char buf[2];
272 
273 	if (!selinux_mnt) {
274 		errno = ENOENT;
275 		return -1;
276 	}
277 	if (value < 0 || value > 1) {
278 		errno = EINVAL;
279 		return -1;
280 	}
281 
282 	fd = bool_open(name, O_WRONLY);
283 	if (fd < 0)
284 		return -1;
285 
286 	if (value)
287 		buf[0] = '1';
288 	else
289 		buf[0] = '0';
290 	buf[1] = '\0';
291 
292 	ret = write(fd, buf, 2);
293 	close(fd);
294 
295 	if (ret > 0)
296 		return 0;
297 	else
298 		return -1;
299 }
300 
hidden_def(security_set_boolean)301 hidden_def(security_set_boolean)
302 
303 int security_commit_booleans(void)
304 {
305 	int fd, ret;
306 	char buf[2];
307 	char path[PATH_MAX];
308 
309 	if (!selinux_mnt) {
310 		errno = ENOENT;
311 		return -1;
312 	}
313 
314 	snprintf(path, sizeof path, "%s/commit_pending_bools", selinux_mnt);
315 	fd = open(path, O_WRONLY);
316 	if (fd < 0)
317 		return -1;
318 
319 	buf[0] = '1';
320 	buf[1] = '\0';
321 
322 	ret = write(fd, buf, 2);
323 	close(fd);
324 
325 	if (ret > 0)
326 		return 0;
327 	else
328 		return -1;
329 }
330 
hidden_def(security_commit_booleans)331 hidden_def(security_commit_booleans)
332 
333 static char *strtrim(char *dest, char *source, int size)
334 {
335 	int i = 0;
336 	char *ptr = source;
337 	i = 0;
338 	while (isspace(*ptr) && i < size) {
339 		ptr++;
340 		i++;
341 	}
342 	strncpy(dest, ptr, size);
343 	for (i = strlen(dest) - 1; i > 0; i--) {
344 		if (!isspace(dest[i]))
345 			break;
346 	}
347 	dest[i + 1] = '\0';
348 	return dest;
349 }
process_boolean(char * buffer,char * name,int namesize,int * val)350 static int process_boolean(char *buffer, char *name, int namesize, int *val)
351 {
352 	char name1[BUFSIZ];
353 	char *ptr = NULL;
354 	char *tok = strtok_r(buffer, "=", &ptr);
355 	if (tok) {
356 		strncpy(name1, tok, BUFSIZ - 1);
357 		strtrim(name, name1, namesize - 1);
358 		if (name[0] == '#')
359 			return 0;
360 		tok = strtok_r(NULL, "\0", &ptr);
361 		if (tok) {
362 			while (isspace(*tok))
363 				tok++;
364 			*val = -1;
365 			if (isdigit(tok[0]))
366 				*val = atoi(tok);
367 			else if (!strncasecmp(tok, "true", sizeof("true") - 1))
368 				*val = 1;
369 			else if (!strncasecmp
370 				 (tok, "false", sizeof("false") - 1))
371 				*val = 0;
372 			if (*val != 0 && *val != 1) {
373 				errno = EINVAL;
374 				return -1;
375 			}
376 
377 		}
378 	}
379 	return 1;
380 }
save_booleans(size_t boolcnt,SELboolean * boollist)381 static int save_booleans(size_t boolcnt, SELboolean * boollist)
382 {
383 	ssize_t len;
384 	size_t i;
385 	char outbuf[BUFSIZ];
386 	char *inbuf = NULL;
387 
388 	/* Open file */
389 	const char *bool_file = selinux_booleans_path();
390 	char local_bool_file[PATH_MAX];
391 	char tmp_bool_file[PATH_MAX];
392 	FILE *boolf;
393 	int fd;
394 	int *used = (int *)malloc(sizeof(int) * boolcnt);
395 	if (!used) {
396 		return -1;
397 	}
398 	/* zero out used field */
399 	for (i = 0; i < boolcnt; i++)
400 		used[i] = 0;
401 
402 	snprintf(tmp_bool_file, sizeof(tmp_bool_file), "%s.XXXXXX", bool_file);
403 	fd = mkstemp(tmp_bool_file);
404 	if (fd < 0) {
405 		free(used);
406 		return -1;
407 	}
408 
409 	snprintf(local_bool_file, sizeof(local_bool_file), "%s.local",
410 		 bool_file);
411 	boolf = fopen(local_bool_file, "r");
412 	if (boolf != NULL) {
413 		ssize_t ret;
414 		size_t size = 0;
415 		int val;
416 		char boolname[BUFSIZ];
417 		char *buffer;
418 		inbuf = NULL;
419 		__fsetlocking(boolf, FSETLOCKING_BYCALLER);
420 		while ((len = getline(&inbuf, &size, boolf)) > 0) {
421 			buffer = strdup(inbuf);
422 			if (!buffer)
423 				goto close_remove_fail;
424 			ret =
425 			    process_boolean(inbuf, boolname, sizeof(boolname),
426 					    &val);
427 			if (ret != 1) {
428 				ret = write(fd, buffer, len);
429 				free(buffer);
430 				if (ret != len)
431 					goto close_remove_fail;
432 			} else {
433 				free(buffer);
434 				for (i = 0; i < boolcnt; i++) {
435 					if (strcmp(boollist[i].name, boolname)
436 					    == 0) {
437 						snprintf(outbuf, sizeof(outbuf),
438 							 "%s=%d\n", boolname,
439 							 boollist[i].value);
440 						len = strlen(outbuf);
441 						used[i] = 1;
442 						if (write(fd, outbuf, len) !=
443 						    len)
444 							goto close_remove_fail;
445 						else
446 							break;
447 					}
448 				}
449 				if (i == boolcnt) {
450 					snprintf(outbuf, sizeof(outbuf),
451 						 "%s=%d\n", boolname, val);
452 					len = strlen(outbuf);
453 					if (write(fd, outbuf, len) != len)
454 						goto close_remove_fail;
455 				}
456 			}
457 			free(inbuf);
458 			inbuf = NULL;
459 		}
460 		fclose(boolf);
461 	}
462 
463 	for (i = 0; i < boolcnt; i++) {
464 		if (used[i] == 0) {
465 			snprintf(outbuf, sizeof(outbuf), "%s=%d\n",
466 				 boollist[i].name, boollist[i].value);
467 			len = strlen(outbuf);
468 			if (write(fd, outbuf, len) != len) {
469 			      close_remove_fail:
470 				free(inbuf);
471 				close(fd);
472 			      remove_fail:
473 				unlink(tmp_bool_file);
474 				free(used);
475 				return -1;
476 			}
477 		}
478 
479 	}
480 	if (fchmod(fd, S_IRUSR | S_IWUSR) != 0)
481 		goto close_remove_fail;
482 	close(fd);
483 	if (rename(tmp_bool_file, local_bool_file) != 0)
484 		goto remove_fail;
485 
486 	free(used);
487 	return 0;
488 }
rollback(SELboolean * boollist,int end)489 static void rollback(SELboolean * boollist, int end)
490 {
491 	int i;
492 
493 	for (i = 0; i < end; i++)
494 		security_set_boolean(boollist[i].name,
495 				     security_get_boolean_active(boollist[i].
496 								 name));
497 }
498 
security_set_boolean_list(size_t boolcnt,SELboolean * boollist,int permanent)499 int security_set_boolean_list(size_t boolcnt, SELboolean * boollist,
500 			      int permanent)
501 {
502 
503 	size_t i;
504 	for (i = 0; i < boolcnt; i++) {
505 		if (security_set_boolean(boollist[i].name, boollist[i].value)) {
506 			rollback(boollist, i);
507 			return -1;
508 		}
509 	}
510 
511 	/* OK, let's do the commit */
512 	if (security_commit_booleans()) {
513 		return -1;
514 	}
515 
516 	if (permanent)
517 		return save_booleans(boolcnt, boollist);
518 
519 	return 0;
520 }
security_load_booleans(char * path)521 int security_load_booleans(char *path)
522 {
523 	FILE *boolf;
524 	char *inbuf;
525 	char localbools[BUFSIZ];
526 	size_t len = 0, errors = 0;
527 	int val;
528 	char name[BUFSIZ];
529 
530 	boolf = fopen(path ? path : selinux_booleans_path(), "r");
531 	if (boolf == NULL)
532 		goto localbool;
533 
534 	__fsetlocking(boolf, FSETLOCKING_BYCALLER);
535 	while (getline(&inbuf, &len, boolf) > 0) {
536 		int ret = process_boolean(inbuf, name, sizeof(name), &val);
537 		if (ret == -1)
538 			errors++;
539 		if (ret == 1)
540 			if (security_set_boolean(name, val) < 0) {
541 				errors++;
542 			}
543 	}
544 	fclose(boolf);
545       localbool:
546 	snprintf(localbools, sizeof(localbools), "%s.local",
547 		 (path ? path : selinux_booleans_path()));
548 	boolf = fopen(localbools, "r");
549 
550 	if (boolf != NULL) {
551 		int ret;
552 		__fsetlocking(boolf, FSETLOCKING_BYCALLER);
553 		while (getline(&inbuf, &len, boolf) > 0) {
554 			ret = process_boolean(inbuf, name, sizeof(name), &val);
555 			if (ret == -1)
556 				errors++;
557 			if (ret == 1)
558 				if (security_set_boolean(name, val) < 0) {
559 					errors++;
560 				}
561 		}
562 		fclose(boolf);
563 	}
564 	if (security_commit_booleans() < 0)
565 		return -1;
566 
567 	if (errors)
568 		errno = EINVAL;
569 	return errors ? -1 : 0;
570 }
571