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 errno = ENOENT;
59 return -1;
60 }
61
62 n = (char **)malloc(sizeof(char *) * *len);
63 if (!n) {
64 rc = -1;
65 goto bad;
66 }
67
68 for (i = 0; i < *len; i++) {
69 n[i] = strdup(namelist[i]->d_name);
70 if (!n[i]) {
71 rc = -1;
72 goto bad_freen;
73 }
74 }
75 rc = 0;
76 *names = n;
77 out:
78 for (i = 0; i < *len; i++) {
79 free(namelist[i]);
80 }
81 free(namelist);
82 return rc;
83 bad_freen:
84 for (--i; i >= 0; --i)
85 free(n[i]);
86 free(n);
87 bad:
88 goto out;
89 }
90
selinux_boolean_sub(const char * name)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(), "re");
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
bool_open(const char * name,int flag)145 static int bool_open(const char *name, int flag) {
146 char *fname = NULL;
147 char *alt_name = NULL;
148 int len;
149 int fd = -1;
150 int ret;
151 char *ptr;
152
153 if (!name) {
154 errno = EINVAL;
155 return -1;
156 }
157
158 /* note the 'sizeof' gets us enough room for the '\0' */
159 len = strlen(name) + strlen(selinux_mnt) + sizeof(SELINUX_BOOL_DIR);
160 fname = malloc(sizeof(char) * len);
161 if (!fname)
162 return -1;
163
164 ret = snprintf(fname, len, "%s%s%s", selinux_mnt, SELINUX_BOOL_DIR, name);
165 if (ret < 0)
166 goto out;
167 assert(ret < len);
168
169 fd = open(fname, flag);
170 if (fd >= 0 || errno != ENOENT)
171 goto out;
172
173 alt_name = selinux_boolean_sub(name);
174 if (!alt_name)
175 goto out;
176
177 /* note the 'sizeof' gets us enough room for the '\0' */
178 len = strlen(alt_name) + strlen(selinux_mnt) + sizeof(SELINUX_BOOL_DIR);
179 ptr = realloc(fname, len);
180 if (!ptr)
181 goto out;
182 fname = ptr;
183
184 ret = snprintf(fname, len, "%s%s%s", selinux_mnt, SELINUX_BOOL_DIR, alt_name);
185 if (ret < 0)
186 goto out;
187 assert(ret < len);
188
189 fd = open(fname, flag);
190 out:
191 free(fname);
192 free(alt_name);
193
194 return fd;
195 }
196
197 #define STRBUF_SIZE 3
get_bool_value(const char * name,char ** buf)198 static 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;
226 out_err:
227 free(*buf);
228 return -1;
229 }
230
security_get_boolean_pending(const char * name)231 int 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
security_get_boolean_active(const char * name)247 int 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
security_set_boolean(const char * name,int value)264 int 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
security_commit_booleans(void)297 int 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
strtrim(char * dest,char * source,int size)325 static char *strtrim(char *dest, char *source, int size)
326 {
327 int i = 0;
328 char *ptr = source;
329 i = 0;
330 while (isspace(*ptr) && i < size) {
331 ptr++;
332 i++;
333 }
334 strncpy(dest, ptr, size);
335 for (i = strlen(dest) - 1; i > 0; i--) {
336 if (!isspace(dest[i]))
337 break;
338 }
339 dest[i + 1] = '\0';
340 return dest;
341 }
process_boolean(char * buffer,char * name,int namesize,int * val)342 static int process_boolean(char *buffer, char *name, int namesize, int *val)
343 {
344 char name1[BUFSIZ];
345 char *ptr = NULL;
346 char *tok;
347
348 /* Skip spaces */
349 while (isspace(buffer[0]))
350 buffer++;
351 /* Ignore comments */
352 if (buffer[0] == '#')
353 return 0;
354
355 tok = strtok_r(buffer, "=", &ptr);
356 if (!tok) {
357 errno = EINVAL;
358 return -1;
359 }
360 strncpy(name1, tok, BUFSIZ - 1);
361 strtrim(name, name1, namesize - 1);
362
363 tok = strtok_r(NULL, "\0", &ptr);
364 if (!tok) {
365 errno = EINVAL;
366 return -1;
367 }
368
369 while (isspace(*tok))
370 tok++;
371
372 *val = -1;
373 if (isdigit(tok[0]))
374 *val = atoi(tok);
375 else if (!strncasecmp(tok, "true", sizeof("true") - 1))
376 *val = 1;
377 else if (!strncasecmp(tok, "false", sizeof("false") - 1))
378 *val = 0;
379 if (*val != 0 && *val != 1) {
380 errno = EINVAL;
381 return -1;
382 }
383 return 1;
384 }
save_booleans(size_t boolcnt,SELboolean * boollist)385 static int save_booleans(size_t boolcnt, SELboolean * boollist)
386 {
387 ssize_t len;
388 size_t i;
389 char outbuf[BUFSIZ];
390 char *inbuf = NULL;
391
392 /* Open file */
393 const char *bool_file = selinux_booleans_path();
394 char local_bool_file[PATH_MAX];
395 char tmp_bool_file[PATH_MAX];
396 FILE *boolf;
397 int fd;
398 int *used = (int *)malloc(sizeof(int) * boolcnt);
399 if (!used) {
400 return -1;
401 }
402 /* zero out used field */
403 for (i = 0; i < boolcnt; i++)
404 used[i] = 0;
405
406 snprintf(tmp_bool_file, sizeof(tmp_bool_file), "%s.XXXXXX", bool_file);
407 fd = mkstemp(tmp_bool_file);
408 if (fd < 0) {
409 free(used);
410 return -1;
411 }
412
413 snprintf(local_bool_file, sizeof(local_bool_file), "%s.local",
414 bool_file);
415 boolf = fopen(local_bool_file, "re");
416 if (boolf != NULL) {
417 ssize_t ret;
418 size_t size = 0;
419 int val;
420 char boolname[BUFSIZ-3];
421 char *buffer;
422 inbuf = NULL;
423 __fsetlocking(boolf, FSETLOCKING_BYCALLER);
424 while ((len = getline(&inbuf, &size, boolf)) > 0) {
425 buffer = strdup(inbuf);
426 if (!buffer)
427 goto close_remove_fail;
428 ret =
429 process_boolean(inbuf, boolname, sizeof(boolname),
430 &val);
431 if (ret != 1) {
432 ret = write(fd, buffer, len);
433 free(buffer);
434 if (ret != len)
435 goto close_remove_fail;
436 } else {
437 free(buffer);
438 for (i = 0; i < boolcnt; i++) {
439 if (strcmp(boollist[i].name, boolname)
440 == 0) {
441 snprintf(outbuf, sizeof(outbuf),
442 "%s=%d\n", boolname,
443 boollist[i].value);
444 len = strlen(outbuf);
445 used[i] = 1;
446 if (write(fd, outbuf, len) !=
447 len)
448 goto close_remove_fail;
449 else
450 break;
451 }
452 }
453 if (i == boolcnt) {
454 val = !!val;
455 snprintf(outbuf, sizeof(outbuf),
456 "%s=%d\n", boolname, val);
457 len = strlen(outbuf);
458 if (write(fd, outbuf, len) != len)
459 goto close_remove_fail;
460 }
461 }
462 free(inbuf);
463 inbuf = NULL;
464 }
465 fclose(boolf);
466 }
467
468 for (i = 0; i < boolcnt; i++) {
469 if (used[i] == 0) {
470 snprintf(outbuf, sizeof(outbuf), "%s=%d\n",
471 boollist[i].name, boollist[i].value);
472 len = strlen(outbuf);
473 if (write(fd, outbuf, len) != len) {
474 close_remove_fail:
475 free(inbuf);
476 close(fd);
477 remove_fail:
478 unlink(tmp_bool_file);
479 free(used);
480 return -1;
481 }
482 }
483
484 }
485 if (fchmod(fd, S_IRUSR | S_IWUSR) != 0)
486 goto close_remove_fail;
487 close(fd);
488 if (rename(tmp_bool_file, local_bool_file) != 0)
489 goto remove_fail;
490
491 free(used);
492 return 0;
493 }
rollback(SELboolean * boollist,int end)494 static void rollback(SELboolean * boollist, int end)
495 {
496 int i;
497
498 for (i = 0; i < end; i++)
499 security_set_boolean(boollist[i].name,
500 security_get_boolean_active(boollist[i].
501 name));
502 }
503
security_set_boolean_list(size_t boolcnt,SELboolean * boollist,int permanent)504 int security_set_boolean_list(size_t boolcnt, SELboolean * boollist,
505 int permanent)
506 {
507
508 size_t i;
509 for (i = 0; i < boolcnt; i++) {
510 boollist[i].value = !!boollist[i].value;
511 if (security_set_boolean(boollist[i].name, boollist[i].value)) {
512 rollback(boollist, i);
513 return -1;
514 }
515 }
516
517 /* OK, let's do the commit */
518 if (security_commit_booleans()) {
519 return -1;
520 }
521
522 if (permanent)
523 return save_booleans(boolcnt, boollist);
524
525 return 0;
526 }
security_load_booleans(char * path)527 int security_load_booleans(char *path)
528 {
529 FILE *boolf;
530 char *inbuf;
531 char localbools[BUFSIZ];
532 size_t len = 0, errors = 0;
533 int val;
534 char name[BUFSIZ];
535
536 boolf = fopen(path ? path : selinux_booleans_path(), "re");
537 if (boolf == NULL)
538 goto localbool;
539
540 __fsetlocking(boolf, FSETLOCKING_BYCALLER);
541 while (getline(&inbuf, &len, boolf) > 0) {
542 int ret = process_boolean(inbuf, name, sizeof(name), &val);
543 if (ret == -1)
544 errors++;
545 if (ret == 1)
546 if (security_set_boolean(name, val) < 0) {
547 errors++;
548 }
549 }
550 fclose(boolf);
551 localbool:
552 snprintf(localbools, sizeof(localbools), "%s.local",
553 (path ? path : selinux_booleans_path()));
554 boolf = fopen(localbools, "re");
555
556 if (boolf != NULL) {
557 int ret;
558 __fsetlocking(boolf, FSETLOCKING_BYCALLER);
559 while (getline(&inbuf, &len, boolf) > 0) {
560 ret = process_boolean(inbuf, name, sizeof(name), &val);
561 if (ret == -1)
562 errors++;
563 if (ret == 1)
564 if (security_set_boolean(name, val) < 0) {
565 errors++;
566 }
567 }
568 fclose(boolf);
569 }
570 if (security_commit_booleans() < 0)
571 return -1;
572
573 if (errors)
574 errno = EINVAL;
575 return errors ? -1 : 0;
576 }
577
578 #else
579
580 #include <stdlib.h>
581 #include "selinux_internal.h"
582
security_set_boolean_list(size_t boolcnt,SELboolean * boollist,int permanent)583 int security_set_boolean_list(size_t boolcnt __attribute__((unused)),
584 SELboolean * boollist __attribute__((unused)),
585 int permanent __attribute__((unused)))
586 {
587 return -1;
588 }
589
security_load_booleans(char * path)590 int security_load_booleans(char *path __attribute__((unused)))
591 {
592 return -1;
593 }
594
security_get_boolean_names(char *** names,int * len)595 int security_get_boolean_names(char ***names __attribute__((unused)),
596 int *len __attribute__((unused)))
597 {
598 return -1;
599 }
600
security_get_boolean_pending(const char * name)601 int security_get_boolean_pending(const char *name __attribute__((unused)))
602 {
603 return -1;
604 }
605
security_get_boolean_active(const char * name)606 int security_get_boolean_active(const char *name __attribute__((unused)))
607 {
608 return -1;
609 }
610
security_set_boolean(const char * name,int value)611 int security_set_boolean(const char *name __attribute__((unused)),
612 int value __attribute__((unused)))
613 {
614 return -1;
615 }
616
security_commit_booleans(void)617 int security_commit_booleans(void)
618 {
619 return -1;
620 }
621
selinux_boolean_sub(const char * name)622 char *selinux_boolean_sub(const char *name __attribute__((unused)))
623 {
624 return NULL;
625 }
626 #endif
627
628 hidden_def(security_get_boolean_names)
629 hidden_def(selinux_boolean_sub)
630 hidden_def(security_get_boolean_active)
631 hidden_def(security_set_boolean)
632 hidden_def(security_commit_booleans)
633