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