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 #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 
     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 
     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 
     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 
    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
    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 
    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 
    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 
    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 
    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 
    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 }
    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 }
    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 }
    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 
    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 }
    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