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