/* * Author: Karl MacMillan <kmacmillan@tresys.com> * * Modified: * Dan Walsh <dwalsh@redhat.com> - Added security_load_booleans(). */ #include <assert.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdlib.h> #include <dirent.h> #include <string.h> #include <stdio.h> #include <stdio_ext.h> #include <unistd.h> #include <fnmatch.h> #include <limits.h> #include <ctype.h> #include <errno.h> #include "selinux_internal.h" #include "policy.h" #define SELINUX_BOOL_DIR "/booleans/" static int filename_select(const struct dirent *d) { if (d->d_name[0] == '.' && (d->d_name[1] == '\0' || (d->d_name[1] == '.' && d->d_name[2] == '\0'))) return 0; return 1; } int security_get_boolean_names(char ***names, int *len) { char path[PATH_MAX]; int i, rc; struct dirent **namelist; char **n; if (!len || names == NULL) { errno = EINVAL; return -1; } if (!selinux_mnt) { errno = ENOENT; return -1; } snprintf(path, sizeof path, "%s%s", selinux_mnt, SELINUX_BOOL_DIR); *len = scandir(path, &namelist, &filename_select, alphasort); if (*len <= 0) { return -1; } n = (char **)malloc(sizeof(char *) * *len); if (!n) { rc = -1; goto bad; } for (i = 0; i < *len; i++) { n[i] = (char *)malloc(_D_ALLOC_NAMLEN(namelist[i])); if (!n[i]) { rc = -1; goto bad_freen; } strcpy(n[i], namelist[i]->d_name); } rc = 0; *names = n; out: for (i = 0; i < *len; i++) { free(namelist[i]); } free(namelist); return rc; bad_freen: for (--i; i >= 0; --i) free(n[i]); free(n); bad: goto out; } hidden_def(security_get_boolean_names) char *selinux_boolean_sub(const char *name) { char *sub = NULL; char *line_buf = NULL; size_t line_len; FILE *cfg; if (!name) return NULL; cfg = fopen(selinux_booleans_subs_path(), "r"); if (!cfg) goto out; while (getline(&line_buf, &line_len, cfg) != -1) { char *ptr; char *src = line_buf; char *dst; while (*src && isspace(*src)) src++; if (!*src) continue; if (src[0] == '#') continue; ptr = src; while (*ptr && !isspace(*ptr)) ptr++; *ptr++ = '\0'; if (strcmp(src, name) != 0) continue; dst = ptr; while (*dst && isspace(*dst)) dst++; if (!*dst) continue; ptr=dst; while (*ptr && !isspace(*ptr)) ptr++; *ptr='\0'; sub = strdup(dst); break; } free(line_buf); fclose(cfg); out: if (!sub) sub = strdup(name); return sub; } hidden_def(selinux_boolean_sub) static int bool_open(const char *name, int flag) { char *fname = NULL; char *alt_name = NULL; int len; int fd = -1; int ret; char *ptr; if (!name) { errno = EINVAL; return -1; } /* note the 'sizeof' gets us enough room for the '\0' */ len = strlen(name) + strlen(selinux_mnt) + sizeof(SELINUX_BOOL_DIR); fname = malloc(sizeof(char) * len); if (!fname) return -1; ret = snprintf(fname, len, "%s%s%s", selinux_mnt, SELINUX_BOOL_DIR, name); if (ret < 0) goto out; assert(ret < len); fd = open(fname, flag); if (fd >= 0 || errno != ENOENT) goto out; alt_name = selinux_boolean_sub(name); if (!alt_name) goto out; /* note the 'sizeof' gets us enough room for the '\0' */ len = strlen(alt_name) + strlen(selinux_mnt) + sizeof(SELINUX_BOOL_DIR); ptr = realloc(fname, len); if (!ptr) goto out; fname = ptr; ret = snprintf(fname, len, "%s%s%s", selinux_mnt, SELINUX_BOOL_DIR, alt_name); if (ret < 0) goto out; assert(ret < len); fd = open(fname, flag); out: free(fname); free(alt_name); return fd; } #define STRBUF_SIZE 3 static int get_bool_value(const char *name, char **buf) { int fd, len; int errno_tmp; if (!selinux_mnt) { errno = ENOENT; return -1; } *buf = malloc(sizeof(char) * (STRBUF_SIZE + 1)); if (!*buf) return -1; (*buf)[STRBUF_SIZE] = 0; fd = bool_open(name, O_RDONLY); if (fd < 0) goto out_err; len = read(fd, *buf, STRBUF_SIZE); errno_tmp = errno; close(fd); errno = errno_tmp; if (len != STRBUF_SIZE) goto out_err; return 0; out_err: free(*buf); return -1; } int security_get_boolean_pending(const char *name) { char *buf; int val; if (get_bool_value(name, &buf)) return -1; if (atoi(&buf[1])) val = 1; else val = 0; free(buf); return val; } int security_get_boolean_active(const char *name) { char *buf; int val; if (get_bool_value(name, &buf)) return -1; buf[1] = '\0'; if (atoi(buf)) val = 1; else val = 0; free(buf); return val; } hidden_def(security_get_boolean_active) int security_set_boolean(const char *name, int value) { int fd, ret; char buf[2]; if (!selinux_mnt) { errno = ENOENT; return -1; } if (value < 0 || value > 1) { errno = EINVAL; return -1; } fd = bool_open(name, O_WRONLY); if (fd < 0) return -1; if (value) buf[0] = '1'; else buf[0] = '0'; buf[1] = '\0'; ret = write(fd, buf, 2); close(fd); if (ret > 0) return 0; else return -1; } hidden_def(security_set_boolean) int security_commit_booleans(void) { int fd, ret; char buf[2]; char path[PATH_MAX]; if (!selinux_mnt) { errno = ENOENT; return -1; } snprintf(path, sizeof path, "%s/commit_pending_bools", selinux_mnt); fd = open(path, O_WRONLY); if (fd < 0) return -1; buf[0] = '1'; buf[1] = '\0'; ret = write(fd, buf, 2); close(fd); if (ret > 0) return 0; else return -1; } hidden_def(security_commit_booleans) static char *strtrim(char *dest, char *source, int size) { int i = 0; char *ptr = source; i = 0; while (isspace(*ptr) && i < size) { ptr++; i++; } strncpy(dest, ptr, size); for (i = strlen(dest) - 1; i > 0; i--) { if (!isspace(dest[i])) break; } dest[i + 1] = '\0'; return dest; } static int process_boolean(char *buffer, char *name, int namesize, int *val) { char name1[BUFSIZ]; char *ptr = NULL; char *tok = strtok_r(buffer, "=", &ptr); if (tok) { strncpy(name1, tok, BUFSIZ - 1); strtrim(name, name1, namesize - 1); if (name[0] == '#') return 0; tok = strtok_r(NULL, "\0", &ptr); if (tok) { while (isspace(*tok)) tok++; *val = -1; if (isdigit(tok[0])) *val = atoi(tok); else if (!strncasecmp(tok, "true", sizeof("true") - 1)) *val = 1; else if (!strncasecmp (tok, "false", sizeof("false") - 1)) *val = 0; if (*val != 0 && *val != 1) { errno = EINVAL; return -1; } } } return 1; } static int save_booleans(size_t boolcnt, SELboolean * boollist) { ssize_t len; size_t i; char outbuf[BUFSIZ]; char *inbuf = NULL; /* Open file */ const char *bool_file = selinux_booleans_path(); char local_bool_file[PATH_MAX]; char tmp_bool_file[PATH_MAX]; FILE *boolf; int fd; int *used = (int *)malloc(sizeof(int) * boolcnt); if (!used) { return -1; } /* zero out used field */ for (i = 0; i < boolcnt; i++) used[i] = 0; snprintf(tmp_bool_file, sizeof(tmp_bool_file), "%s.XXXXXX", bool_file); fd = mkstemp(tmp_bool_file); if (fd < 0) { free(used); return -1; } snprintf(local_bool_file, sizeof(local_bool_file), "%s.local", bool_file); boolf = fopen(local_bool_file, "r"); if (boolf != NULL) { ssize_t ret; size_t size = 0; int val; char boolname[BUFSIZ]; char *buffer; inbuf = NULL; __fsetlocking(boolf, FSETLOCKING_BYCALLER); while ((len = getline(&inbuf, &size, boolf)) > 0) { buffer = strdup(inbuf); if (!buffer) goto close_remove_fail; ret = process_boolean(inbuf, boolname, sizeof(boolname), &val); if (ret != 1) { ret = write(fd, buffer, len); free(buffer); if (ret != len) goto close_remove_fail; } else { free(buffer); for (i = 0; i < boolcnt; i++) { if (strcmp(boollist[i].name, boolname) == 0) { snprintf(outbuf, sizeof(outbuf), "%s=%d\n", boolname, boollist[i].value); len = strlen(outbuf); used[i] = 1; if (write(fd, outbuf, len) != len) goto close_remove_fail; else break; } } if (i == boolcnt) { snprintf(outbuf, sizeof(outbuf), "%s=%d\n", boolname, val); len = strlen(outbuf); if (write(fd, outbuf, len) != len) goto close_remove_fail; } } free(inbuf); inbuf = NULL; } fclose(boolf); } for (i = 0; i < boolcnt; i++) { if (used[i] == 0) { snprintf(outbuf, sizeof(outbuf), "%s=%d\n", boollist[i].name, boollist[i].value); len = strlen(outbuf); if (write(fd, outbuf, len) != len) { close_remove_fail: free(inbuf); close(fd); remove_fail: unlink(tmp_bool_file); free(used); return -1; } } } if (fchmod(fd, S_IRUSR | S_IWUSR) != 0) goto close_remove_fail; close(fd); if (rename(tmp_bool_file, local_bool_file) != 0) goto remove_fail; free(used); return 0; } static void rollback(SELboolean * boollist, int end) { int i; for (i = 0; i < end; i++) security_set_boolean(boollist[i].name, security_get_boolean_active(boollist[i]. name)); } int security_set_boolean_list(size_t boolcnt, SELboolean * boollist, int permanent) { size_t i; for (i = 0; i < boolcnt; i++) { if (security_set_boolean(boollist[i].name, boollist[i].value)) { rollback(boollist, i); return -1; } } /* OK, let's do the commit */ if (security_commit_booleans()) { return -1; } if (permanent) return save_booleans(boolcnt, boollist); return 0; } int security_load_booleans(char *path) { FILE *boolf; char *inbuf; char localbools[BUFSIZ]; size_t len = 0, errors = 0; int val; char name[BUFSIZ]; boolf = fopen(path ? path : selinux_booleans_path(), "r"); if (boolf == NULL) goto localbool; __fsetlocking(boolf, FSETLOCKING_BYCALLER); while (getline(&inbuf, &len, boolf) > 0) { int ret = process_boolean(inbuf, name, sizeof(name), &val); if (ret == -1) errors++; if (ret == 1) if (security_set_boolean(name, val) < 0) { errors++; } } fclose(boolf); localbool: snprintf(localbools, sizeof(localbools), "%s.local", (path ? path : selinux_booleans_path())); boolf = fopen(localbools, "r"); if (boolf != NULL) { int ret; __fsetlocking(boolf, FSETLOCKING_BYCALLER); while (getline(&inbuf, &len, boolf) > 0) { ret = process_boolean(inbuf, name, sizeof(name), &val); if (ret == -1) errors++; if (ret == 1) if (security_set_boolean(name, val) < 0) { errors++; } } fclose(boolf); } if (security_commit_booleans() < 0) return -1; if (errors) errno = EINVAL; return errors ? -1 : 0; }