Home | History | Annotate | Download | only in src
      1 /*
      2  * Author: Karl MacMillan <kmacmillan (at) tresys.com>
      3  *
      4  * Modified:
      5  *   Dan Walsh <dwalsh (at) 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 
     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 
     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 
     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 
    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
    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 
    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 
    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 
    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 
    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 
    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 }
    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 }
    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 }
    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 
    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 }
    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 
    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 
    587 int security_load_booleans(char *path __attribute__((unused)))
    588 {
    589 	return -1;
    590 }
    591 
    592 int security_get_boolean_names(char ***names __attribute__((unused)),
    593 	int *len __attribute__((unused)))
    594 {
    595 	return -1;
    596 }
    597 
    598 int security_get_boolean_pending(const char *name __attribute__((unused)))
    599 {
    600 	return -1;
    601 }
    602 
    603 int security_get_boolean_active(const char *name __attribute__((unused)))
    604 {
    605 	return -1;
    606 }
    607 
    608 int security_set_boolean(const char *name __attribute__((unused)),
    609 	int value __attribute__((unused)))
    610 {
    611 	return -1;
    612 }
    613 
    614 int security_commit_booleans(void)
    615 {
    616 	return -1;
    617 }
    618 
    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