1/*
2 * Author: Karl MacMillan <kmacmillan@tresys.com>
3 *
4 * Modified:
5 *   Dan Walsh <dwalsh@redhat.com> - Added security_load_booleans().
6 */
7
8#ifndef DISABLE_BOOL
9
10#include <sys/types.h>
11#include <sys/stat.h>
12#include <fcntl.h>
13#include <stdlib.h>
14#include <dirent.h>
15#include <string.h>
16#include <stdio.h>
17#include <stdio_ext.h>
18#include <unistd.h>
19#include <fnmatch.h>
20#include <limits.h>
21#include <ctype.h>
22#include <errno.h>
23
24#include "selinux_internal.h"
25#include "policy.h"
26
27#define SELINUX_BOOL_DIR "/booleans/"
28
29static int filename_select(const struct dirent *d)
30{
31	if (d->d_name[0] == '.'
32	    && (d->d_name[1] == '\0'
33		|| (d->d_name[1] == '.' && d->d_name[2] == '\0')))
34		return 0;
35	return 1;
36}
37
38int security_get_boolean_names(char ***names, int *len)
39{
40	char path[PATH_MAX];
41	int i, rc;
42	struct dirent **namelist;
43	char **n;
44
45	if (!len || names == NULL) {
46		errno = EINVAL;
47		return -1;
48	}
49	if (!selinux_mnt) {
50		errno = ENOENT;
51		return -1;
52	}
53
54	snprintf(path, sizeof path, "%s%s", selinux_mnt, SELINUX_BOOL_DIR);
55	*len = scandir(path, &namelist, &filename_select, alphasort);
56	if (*len <= 0) {
57		errno = ENOENT;
58		return -1;
59	}
60
61	n = (char **)malloc(sizeof(char *) * *len);
62	if (!n) {
63		rc = -1;
64		goto bad;
65	}
66
67	for (i = 0; i < *len; i++) {
68		n[i] = strdup(namelist[i]->d_name);
69		if (!n[i]) {
70			rc = -1;
71			goto bad_freen;
72		}
73	}
74	rc = 0;
75	*names = n;
76      out:
77	for (i = 0; i < *len; i++) {
78		free(namelist[i]);
79	}
80	free(namelist);
81	return rc;
82      bad_freen:
83	if (i > 0) {
84		while (i >= 1)
85			free(n[--i]);
86	}
87	free(n);
88      bad:
89	goto out;
90}
91
92char *selinux_boolean_sub(const char *name)
93{
94	char *sub = NULL;
95	char *line_buf = NULL;
96	size_t line_len;
97	FILE *cfg;
98
99	if (!name)
100		return NULL;
101
102	cfg = fopen(selinux_booleans_subs_path(), "re");
103	if (!cfg)
104		goto out;
105
106	while (getline(&line_buf, &line_len, cfg) != -1) {
107		char *ptr;
108		char *src = line_buf;
109		char *dst;
110		while (*src && isspace(*src))
111			src++;
112		if (!*src)
113			continue;
114		if (src[0] == '#')
115			continue;
116
117		ptr = src;
118		while (*ptr && !isspace(*ptr))
119			ptr++;
120		*ptr++ = '\0';
121		if (strcmp(src, name) != 0)
122			continue;
123
124		dst = ptr;
125		while (*dst && isspace(*dst))
126			dst++;
127		if (!*dst)
128			continue;
129		ptr = dst;
130		while (*ptr && !isspace(*ptr))
131			ptr++;
132		*ptr = '\0';
133
134		if (!strchr(dst, '/'))
135			sub = strdup(dst);
136
137		break;
138	}
139	free(line_buf);
140	fclose(cfg);
141out:
142	if (!sub)
143		sub = strdup(name);
144	return sub;
145}
146
147static int bool_open(const char *name, int flag) {
148	char *fname = NULL;
149	char *alt_name = NULL;
150	size_t len;
151	int fd = -1;
152	int ret;
153	char *ptr;
154
155	if (!name || strchr(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 || (size_t)ret >= len)
168		goto out;
169
170	fd = open(fname, flag);
171	if (fd >= 0 || errno != ENOENT)
172		goto out;
173
174	alt_name = selinux_boolean_sub(name);
175	if (!alt_name)
176		goto out;
177
178	/* note the 'sizeof' gets us enough room for the '\0' */
179	len = strlen(alt_name) + strlen(selinux_mnt) + sizeof(SELINUX_BOOL_DIR);
180	ptr = realloc(fname, len);
181	if (!ptr)
182		goto out;
183	fname = ptr;
184
185	ret = snprintf(fname, len, "%s%s%s", selinux_mnt, SELINUX_BOOL_DIR, alt_name);
186	if (ret < 0 || (size_t)ret >= len)
187		goto out;
188
189	fd = open(fname, flag);
190out:
191	free(fname);
192	free(alt_name);
193
194	return fd;
195}
196
197#define STRBUF_SIZE 3
198static int get_bool_value(const char *name, char **buf)
199{
200	int fd, len;
201	int errno_tmp;
202
203	if (!selinux_mnt) {
204		errno = ENOENT;
205		return -1;
206	}
207
208	*buf = malloc(sizeof(char) * (STRBUF_SIZE + 1));
209	if (!*buf)
210		return -1;
211
212	(*buf)[STRBUF_SIZE] = 0;
213
214	fd = bool_open(name, O_RDONLY | O_CLOEXEC);
215	if (fd < 0)
216		goto out_err;
217
218	len = read(fd, *buf, STRBUF_SIZE);
219	errno_tmp = errno;
220	close(fd);
221	errno = errno_tmp;
222	if (len != STRBUF_SIZE)
223		goto out_err;
224
225	return 0;
226out_err:
227	free(*buf);
228	return -1;
229}
230
231int security_get_boolean_pending(const char *name)
232{
233	char *buf;
234	int val;
235
236	if (get_bool_value(name, &buf))
237		return -1;
238
239	if (atoi(&buf[1]))
240		val = 1;
241	else
242		val = 0;
243	free(buf);
244	return val;
245}
246
247int security_get_boolean_active(const char *name)
248{
249	char *buf;
250	int val;
251
252	if (get_bool_value(name, &buf))
253		return -1;
254
255	buf[1] = '\0';
256	if (atoi(buf))
257		val = 1;
258	else
259		val = 0;
260	free(buf);
261	return val;
262}
263
264int security_set_boolean(const char *name, int value)
265{
266	int fd, ret;
267	char buf[2];
268
269	if (!selinux_mnt) {
270		errno = ENOENT;
271		return -1;
272	}
273	if (value < 0 || value > 1) {
274		errno = EINVAL;
275		return -1;
276	}
277
278	fd = bool_open(name, O_WRONLY | O_CLOEXEC);
279	if (fd < 0)
280		return -1;
281
282	if (value)
283		buf[0] = '1';
284	else
285		buf[0] = '0';
286	buf[1] = '\0';
287
288	ret = write(fd, buf, 2);
289	close(fd);
290
291	if (ret > 0)
292		return 0;
293	else
294		return -1;
295}
296
297int security_commit_booleans(void)
298{
299	int fd, ret;
300	char buf[2];
301	char path[PATH_MAX];
302
303	if (!selinux_mnt) {
304		errno = ENOENT;
305		return -1;
306	}
307
308	snprintf(path, sizeof path, "%s/commit_pending_bools", selinux_mnt);
309	fd = open(path, O_WRONLY | O_CLOEXEC);
310	if (fd < 0)
311		return -1;
312
313	buf[0] = '1';
314	buf[1] = '\0';
315
316	ret = write(fd, buf, 2);
317	close(fd);
318
319	if (ret > 0)
320		return 0;
321	else
322		return -1;
323}
324
325static void rollback(SELboolean * boollist, int end)
326{
327	int i;
328
329	for (i = 0; i < end; i++)
330		security_set_boolean(boollist[i].name,
331				     security_get_boolean_active(boollist[i].
332								 name));
333}
334
335int security_set_boolean_list(size_t boolcnt, SELboolean * boollist,
336			      int permanent)
337{
338
339	size_t i;
340	for (i = 0; i < boolcnt; i++) {
341		boollist[i].value = !!boollist[i].value;
342		if (security_set_boolean(boollist[i].name, boollist[i].value)) {
343			rollback(boollist, i);
344			return -1;
345		}
346	}
347
348	/* OK, let's do the commit */
349	if (security_commit_booleans()) {
350		return -1;
351	}
352
353	/* Return error as flag no longer used */
354	if (permanent)
355		return -1;
356
357	return 0;
358}
359
360/* This function is deprecated */
361int security_load_booleans(char *path __attribute__((unused)))
362{
363	return -1;
364}
365#else
366
367#include <stdlib.h>
368#include "selinux_internal.h"
369
370int security_set_boolean_list(size_t boolcnt __attribute__((unused)),
371	SELboolean * boollist __attribute__((unused)),
372	int permanent __attribute__((unused)))
373{
374	return -1;
375}
376
377int security_load_booleans(char *path __attribute__((unused)))
378{
379	return -1;
380}
381
382int security_get_boolean_names(char ***names __attribute__((unused)),
383	int *len __attribute__((unused)))
384{
385	return -1;
386}
387
388int security_get_boolean_pending(const char *name __attribute__((unused)))
389{
390	return -1;
391}
392
393int security_get_boolean_active(const char *name __attribute__((unused)))
394{
395	return -1;
396}
397
398int security_set_boolean(const char *name __attribute__((unused)),
399	int value __attribute__((unused)))
400{
401	return -1;
402}
403
404int security_commit_booleans(void)
405{
406	return -1;
407}
408
409char *selinux_boolean_sub(const char *name __attribute__((unused)))
410{
411	return NULL;
412}
413#endif
414
415