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
28static 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
37int 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
89hidden_def(security_get_boolean_names)
90
91char *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);
139out:
140	if (!sub)
141		sub = strdup(name);
142	return sub;
143}
144
145hidden_def(selinux_boolean_sub)
146
147static 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);
192out:
193	free(fname);
194	free(alt_name);
195
196	return fd;
197}
198
199#define STRBUF_SIZE 3
200static 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;
228out_err:
229	free(*buf);
230	return -1;
231}
232
233int 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
249int 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
266hidden_def(security_get_boolean_active)
267
268int 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
301hidden_def(security_set_boolean)
302
303int 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
331hidden_def(security_commit_booleans)
332
333static 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}
350static 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}
381static 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}
489static 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
499int 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}
521int 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