/* * Author: Karl MacMillan * * Modified: * Dan Walsh - Added security_load_booleans(). */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "selinux_internal.h" #include "policy.h" #define SELINUX_BOOL_DIR "/booleans/" static int filename_select(const struct dirent *d) { int len; len = strlen(d->d_name); if (len == 1 && d->d_name[0] == '.') return 0; if (len == 2 && d->d_name[0] == '.' && d->d_name[1] == '.') 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; assert(len); 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; } memset(n, 0, sizeof(char*) * *len); for (i = 0; i < *len; i++) { n[i] = (char*)malloc(sizeof(char) * (namelist[i]->d_reclen + 1)); if (!n[i]) { rc = -1; goto bad_freen; } strncpy(n[i], namelist[i]->d_name, namelist[i]->d_reclen + 1); } rc = 0; *names = n; out: for (i = 0; i < *len; i++) { free(namelist[i]); } free(namelist); return rc; bad_freen: for (i = 0; i < *len; i++) { if (n[i]) free(n[i]); } free(n); bad: goto out; } hidden_def(security_get_boolean_names) #define STRBUF_SIZE 3 static int get_bool_value(const char *name, char **buf) { int fd, len; char *fname = NULL; if (!selinux_mnt) { errno = ENOENT; return -1; } *buf = (char*)malloc(sizeof(char) * (STRBUF_SIZE + 1)); if (!*buf) goto out; (*buf)[STRBUF_SIZE] = 0; len = strlen(name) + strlen(selinux_mnt) + sizeof(SELINUX_BOOL_DIR); fname = (char*)malloc(sizeof(char) * len); if (!fname) goto out; snprintf(fname, len, "%s%s%s", selinux_mnt, SELINUX_BOOL_DIR, name); fd = open(fname, O_RDONLY); if (fd < 0) goto out; len = read(fd, *buf, STRBUF_SIZE); close(fd); if (len != STRBUF_SIZE) goto out; free(fname); return 0; out: if (*buf) free(*buf); if (fname) free(fname); 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, len; char buf[2], *fname; if (!selinux_mnt) { errno = ENOENT; return -1; } if ( value < 0 || value > 1 ) { errno = EINVAL; return -1; } len = strlen(name) + strlen(selinux_mnt) + sizeof(SELINUX_BOOL_DIR); fname = (char*)malloc(sizeof(char) * len); if (!fname) return -1; snprintf(fname, len, "%s%s%s", selinux_mnt, SELINUX_BOOL_DIR, name); fd = open(fname, O_WRONLY); if (fd < 0) { ret = -1; goto out; } if (value) buf[0] = '1'; else buf[0] = '0'; buf[1] = '\0'; ret = write(fd, buf, 2); close(fd); out: free(fname); 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; 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; char *inbuf=NULL; 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 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; 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; }