gradm/0000755000175000017510000000000013152754432012041 5ustar spenderspendergradm/gradm_fulllearn_pass2.l0000644000175000017510000000372013152754421016464 0ustar spenderspender%{ /* * Copyright (C) 2002-2014 Bradley Spengler, Open Source Security, Inc. * http://www.grsecurity.net spender@grsecurity.net * * This file is part of gradm. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "gradm.h" #include "fulllearn_pass2.tab.h" void fulllearn_pass2error(const char *s); int fulllearn_pass2wrap(void); static struct in_addr ip; %} ROLENAME ^[_a-zA-Z0-9.-]{1,64} NOTAFILE [a-z]+"["[0-9]+"]" NUM [-]?[0-9]+ FILENAME [/][^\t\n]* IPADDR [0-9]{1,3}"."[0-9]{1,3}"."[0-9]{1,3}"."[0-9]{1,3} %option nounput %x ERROR %% "u" { fulllearn_pass2lval.num = USER; return USER; } "g" { fulllearn_pass2lval.num = GROUP; return GROUP; } {NUM} { fulllearn_pass2lval.num = atol(yytext); return NUM; } {NOTAFILE} { fulllearn_pass2lval.string = gr_strdup("/proc"); return FILENAME; } {ROLENAME} { /* not used in grammar */ return ROLENAME; } {FILENAME} { fulllearn_pass2lval.string = gr_strdup(yytext); return FILENAME; } {IPADDR} { if (inet_aton(yytext, &ip)) fulllearn_pass2lval.num = ip.s_addr; else fulllearn_pass2lval.num = 0; return IPADDR; } [\t] { return ':'; } . ; %% void fulllearn_pass2error(const char *s) { return; } int fulllearn_pass2wrap(void) { return 1; } gradm/grlearn.c0000644000175000017510000002101013152754421013627 0ustar spenderspender/* * Copyright (C) 2002-2014 Bradley Spengler, Open Source Security, Inc. * http://www.grsecurity.net spender@grsecurity.net * * This file is part of gradm. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "gradm.h" #include static struct always_reduce_entry { char *str; unsigned int len; } *always_reduce_paths; #define LEARN_BUFFER_SIZE (512 * 1024) #define MAX_ENTRY_SIZE 16384 #define NUM_CACHE_ENTRIES 640 static char *writebuf; static char *writep; static int fd2 = -1; extern FILE *grlearn_configin; extern int grlearn2_configparse(void); static void parse_learn2_config(void) { grlearn_configin = fopen(GR_LEARN_CONFIG_PATH, "r"); if (grlearn_configin == NULL) { fprintf(stdout, "Unable to open %s: %s\n", GR_LEARN_CONFIG_PATH, strerror(errno)); exit(EXIT_FAILURE); } grlearn2_configparse(); fclose(grlearn_configin); return; } void add_always_reduce(char *str) { unsigned int size = 0; if (always_reduce_paths == NULL) always_reduce_paths = calloc(2, sizeof(struct always_reduce_entry)); if (always_reduce_paths == NULL) exit(EXIT_FAILURE); while (always_reduce_paths[size].str) size++; always_reduce_paths = realloc(always_reduce_paths, (size + 2) * sizeof(struct always_reduce_entry)); if (always_reduce_paths == NULL) exit(EXIT_FAILURE); memset(always_reduce_paths + size, 0, 2 * sizeof(struct always_reduce_entry)); always_reduce_paths[size].str = str; always_reduce_paths[size].len = strlen(str); return; } /* handle flushing of buffer when grlearn is stopped */ void term_handler(int sig) { int ignore_ret; signal(sig, SIG_IGN); if (fd2 >= 0) ignore_ret = write(fd2, writebuf, writep - writebuf); exit(0); } int stop_daemon(void) { int fd; int ignore_ret; pid_t learn_pid; fd = open(GR_LEARN_PID_PATH, O_RDONLY); if (fd < 0) exit(EXIT_FAILURE); ignore_ret = read(fd, &learn_pid, sizeof(learn_pid)); /* send SIGTERM, will be handled */ kill(learn_pid, 15); close(fd); unlink(GR_LEARN_PID_PATH); return 0; } int write_pid_log(pid_t pid) { struct stat fstat; int fd; pid_t learn_pid; char pathname[PATH_MAX] = {0}; char procname[64] = {0}; int ignore_ret; char *grlearn_path; if (bikeshedding_detected()) grlearn_path = get_bikeshedded_path(GRLEARN_PATH); else grlearn_path = GRLEARN_PATH; if (!stat(GR_LEARN_PID_PATH, &fstat)) { fd = open(GR_LEARN_PID_PATH, O_RDONLY); if (fd < 0) { fprintf(stdout, "Unable to open %s:\n" "%s\n", GR_LEARN_PID_PATH, strerror(errno)); kill(pid, 9); exit(EXIT_FAILURE); } ignore_ret = read(fd, &learn_pid, sizeof(learn_pid)); close(fd); unlink(GR_LEARN_PID_PATH); snprintf(procname, sizeof(procname) - 1, "/proc/%d/exe", learn_pid); if (readlink(procname, pathname, PATH_MAX - 1) < 0) goto start; if (strcmp(pathname, grlearn_path)) goto start; fprintf(stdout, "Learning daemon possibly running already...killing process.\n"); kill(learn_pid, 15); } start: fd = open(GR_LEARN_PID_PATH, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); if (fd < 0) { fprintf(stdout, "Unable to open %s:\n" "%s\n", GR_LEARN_PID_PATH, strerror(errno)); kill(pid, 9); exit(EXIT_FAILURE); } ignore_ret = write(fd, &pid, sizeof(pid)); close(fd); return 0; } struct cache_entry { char *entryname; unsigned long used; unsigned long checked; unsigned int len; unsigned char taken; } *cache[NUM_CACHE_ENTRIES]; static unsigned long check_count = 0; /* maintain a cache of most recently used items */ int check_cache(char *str, unsigned int len) { int i; check_count++; for (i = 0; i < NUM_CACHE_ENTRIES; i++) { if (cache[i]->taken && cache[i]->len == len && !strcmp(cache[i]->entryname, str)) { cache[i]->used++; return 1; } } return 0; } void insert_into_cache(char *str, unsigned int len) { int i; struct cache_entry *least; int start = random() % (NUM_CACHE_ENTRIES - 1); least = cache[start]; for (i = start + 1; i != start; i = (i + 1) % NUM_CACHE_ENTRIES) { if (!cache[i]->taken) { cache[i]->taken = 1; least = cache[i]; break; } if (cache[i]->used < least->used && (cache[i]->checked + (NUM_CACHE_ENTRIES * 2)) < check_count) least = cache[i]; } strcpy(least->entryname, str); least->used = 0; least->len = len; least->checked = check_count; return; } char * rewrite_learn_entry(char *p) { int i; char *tmp = p; char *endobj; char *next; unsigned int len; struct always_reduce_entry *arep; for (i = 0; i < 8; i++) { tmp = strchr(tmp, '\t'); if (!tmp) return p; tmp++; } /* now we have a pointer to the object name */ endobj = strchr(tmp, '\t'); if (!endobj) return p; *endobj = '\0'; /* now we have separated the string */ if (!strncmp(tmp, "/proc/", 6) && (*(tmp + 6) >= '1') && (*(tmp + 6) <= '9')) { *endobj = '\t'; next = endobj; while (*next++); len = next - endobj; memmove(tmp + 5, endobj, len); return next; } if (always_reduce_paths) { arep = always_reduce_paths; while (arep && arep->str) { if (!strncmp(tmp, arep->str, arep->len) && (*(tmp + arep->len) == '/')) { *endobj = '\t'; next = endobj; while (*next++); len = next - endobj; memmove(tmp + arep->len, endobj, len); return next; } arep++; } } *endobj = '\t'; return p; } int main(int argc, char *argv[]) { char *buf; char *next; char *p; ssize_t retval; struct pollfd fds; int fd; pid_t pid; struct sched_param schedulerparam; unsigned int len; int i; int ignore_ret; if (argc != 2) return 1; if (!strcmp(argv[1], "-stop")) return stop_daemon(); signal(SIGTERM, term_handler); parse_learn2_config(); /* perform various operations to make us act in near real-time */ srandom(getpid()); mlockall(MCL_CURRENT | MCL_FUTURE); buf = calloc(1, LEARN_BUFFER_SIZE); if (!buf) return 1; writebuf = calloc(1, 4 * MAX_ENTRY_SIZE); if (!writebuf) return 1; writep = writebuf; for(i = 0; i < NUM_CACHE_ENTRIES; i++) { cache[i] = calloc(1, sizeof(struct cache_entry)); if (!cache[i]) return 1; cache[i]->entryname = calloc(1, MAX_ENTRY_SIZE); if (!cache[i]->entryname) return 1; } setpriority(PRIO_PROCESS, 0, -20); ignore_ret = nice(-19); schedulerparam.sched_priority = sched_get_priority_max(SCHED_FIFO); sched_setscheduler(0, SCHED_FIFO, &schedulerparam); fd = open(GRDEV_PATH, O_RDONLY); if (fd < 0) { fprintf(stdout, "Error opening %s:\n" "%s\n", GRDEV_PATH, strerror(errno)); exit(EXIT_FAILURE); } fd2 = open(argv[1], O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR); if (fd2 < 0) { fprintf(stdout, "Error opening %s\n" "%s\n", argv[1], strerror(errno)); exit(EXIT_FAILURE); } fcntl(fd, F_SETFD, FD_CLOEXEC); fcntl(fd2, F_SETFD, FD_CLOEXEC); pid = fork(); if (pid > 0) { exit(EXIT_SUCCESS); } else if (!pid) { char b = 0; int pipefd; write_pid_log(getpid()); pipefd = open(GR_LEARN_PIPE_PATH, O_WRONLY); if (pipefd >= 0) { ignore_ret = write(pipefd, &b, 1); close(pipefd); } close(0); close(1); close(2); } else { char b = 0; int pipefd; pipefd = open(GR_LEARN_PIPE_PATH, O_WRONLY); if (pipefd >= 0) { ignore_ret = write(pipefd, &b, 1); close(pipefd); } fprintf(stdout, "Unable to fork.\n"); exit(EXIT_FAILURE); } fds.fd = fd; fds.events = POLLIN; while (poll(&fds, 1, -1) > 0) { retval = read(fd, buf, LEARN_BUFFER_SIZE - 1); if (retval > 0) { p = buf; while (p < (buf + retval)) { next = rewrite_learn_entry(p); len = strlen(p); if (!check_cache(p, len)) { insert_into_cache(p, len); if (((4 * MAX_ENTRY_SIZE) - (writep - writebuf)) > len) { memcpy(writep, p, len); writep += len; } else { ignore_ret = write(fd2, writebuf, writep - writebuf); memset(writebuf, 0, 4 * MAX_ENTRY_SIZE); writep = writebuf; } } if (next == p) p += len + 1; else p = next; } } } close(fd); close(fd2); return 0; } gradm/COPYRIGHT0000644000175000017510000000146013152754421013333 0ustar spenderspendergradm - Userland grsecurity RBAC administration and policy analysis utility Copyright (C) 2002-2014 Bradley Spengler, Open Source Security, Inc. http://www.grsecurity.net spender@grsecurity.net This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. gradm/gradm_newlearn.c0000644000175000017510000014322613152754421015200 0ustar spenderspender/* * Copyright (C) 2002-2014 Bradley Spengler, Open Source Security, Inc. * http://www.grsecurity.net spender@grsecurity.net * * This file is part of gradm. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "gradm.h" struct gr_learn_file_node *cachednode = NULL; unsigned int cachedlen = 0; void add_grlearn_option(u_int32_t option) { grlearn_options |= option; return; } int is_protected_path(const char *filename, u_int32_t mode) { if (is_read_protected_path(filename, mode) || is_write_protected_path(filename, mode)) return 1; return 0; } int is_read_protected_path(const char *filename, u_int32_t mode) { char **tmp; unsigned int len; int is_glob; if (!(mode & GR_READ)) return 0; tmp = read_protected_paths; if (tmp == NULL) return 0; while (*tmp) { len = strlen(*tmp); is_glob = is_globbed_file(*tmp); if (!match_filename(filename, *tmp, len, is_glob)) return 1; tmp++; } return 0; } int is_write_protected_path(const char *filename, u_int32_t mode) { char **tmp; unsigned int len; int is_glob; if (!(mode & (GR_WRITE | GR_APPEND))) return 0; tmp = protected_paths; if (tmp == NULL) return 0; while (*tmp) { len = strlen(*tmp); is_glob = is_globbed_file(*tmp); if (!match_filename(filename, *tmp, len, is_glob)) return 1; tmp++; } return 0; } void enforce_high_protected_paths(struct gr_learn_file_node *subject) { struct gr_learn_file_tmp_node **tmptable; char **tmp; unsigned int len; int is_glob; unsigned long i; tmp = high_protected_paths; if (tmp == NULL) return; tmptable = (struct gr_learn_file_tmp_node **)subject->hash->table; while (*tmp) { is_glob = is_globbed_file(*tmp); len = strlen(*tmp); for (i = 0; i < subject->hash->table_size; i++) { if (tmptable[i] == NULL) continue; if (!tmptable[i]->mode) continue; /* do we want to add the globbed object regardless of whether a matching access was found or not? */ if (!match_filename(tmptable[i]->filename, *tmp, len, is_glob)) goto next; } cachednode = NULL; cachedlen = 0; insert_file(&(subject->object_list), *tmp, 0, 0); next: tmp++; } return; } void match_role(struct gr_learn_group_node *grouplist, uid_t uid, gid_t gid, struct gr_learn_group_node **group, struct gr_learn_user_node **user) { struct gr_learn_group_node *tmpgroup; struct gr_learn_user_node *tmpuser; *group = NULL; *user = NULL; for_each_list_entry(tmpgroup, grouplist) { for_each_list_entry(tmpuser, tmpgroup->users) { if (tmpuser->uid == uid) { *user = tmpuser; return; } } } for_each_list_entry(tmpgroup, grouplist) { if (tmpgroup->gid == gid) { *group = tmpgroup; return; } } return; } void traverse_roles(struct gr_learn_group_node *grouplist, int (*act)(struct gr_learn_group_node *group, struct gr_learn_user_node *user, FILE *stream), FILE *stream) { struct gr_learn_group_node *tmpgroup; struct gr_learn_user_node *tmpuser; for_each_list_entry(tmpgroup, grouplist) { if (tmpgroup->users == NULL) act(tmpgroup, NULL, stream); else { for_each_list_entry(tmpuser, tmpgroup->users) { act(tmpgroup, tmpuser, stream); } } } return; } /* uses mergesort, preserves prev and next functionality, filelist may be modified by the sort modified from algorithm by Simon Tatham */ void sort_file_node_list(struct gr_learn_file_node *root) { struct gr_learn_file_node **filelist; int count = 0; int i; int list_size = 1; int num_merges; int left_size, right_size; struct gr_learn_file_node *cur, *left, *right, *end; const char *basename; unsigned int baselen; if (root == NULL) return; filelist = &root->leaves; if (*filelist == NULL) return; basename = root->filename; baselen = strlen(basename); /* special case the root / */ if (baselen == 1) baselen = 0; for_each_list_entry(cur, *filelist) { sort_file_node_list(cur); count++; } if (count < 2) return; while (1) { left = *filelist; *filelist = NULL; end = NULL; num_merges = 0; while (left) { num_merges++; right = left; left_size = 0; for (i = 0; i < list_size; i++) { left_size++; right = right->next; if (right == NULL) break; } right_size = list_size; while (left_size > 0 || (right_size > 0 && right != NULL)) { if (left_size == 0) { cur = right; right = right->next; right_size--; } else if (right_size == 0 || right == NULL || strcmp(left->filename + baselen + 1, right->filename + baselen + 1) <= 0) { cur = left; left = left->next; left_size--; } else { cur = right; right = right->next; right_size--; } if (end) end->next = cur; else *filelist = cur; cur->prev = end; end = cur; } left = right; } end->next = NULL; if (num_merges <= 1) return; list_size <<= 1; } return; } int display_role(struct gr_learn_group_node *group, struct gr_learn_user_node *user, FILE *stream) { struct gr_learn_file_node *subject = NULL; output_role_info(group, user, stream); if (user) { if (user->subject_list) sort_file_node_list(user->subject_list); subject = user->subject_list; } else { if (group->subject_list) sort_file_node_list(group->subject_list); subject = group->subject_list; } if (subject) display_tree(subject, stream); fprintf(stream, "\n"); return 0; } void display_roles(struct gr_learn_group_node *grouplist, FILE *stream) { output_learn_header(stream); traverse_roles(grouplist, &display_role, stream); return; } struct gr_learn_group_node *find_insert_group(struct gr_learn_group_node *grouplist, gid_t gid) { struct gr_learn_group_node *tmp; for_each_list_entry(tmp, grouplist) { if (tmp->gid == gid) return tmp; } return NULL; } unsigned long count_users(struct gr_learn_group_node *group) { struct gr_learn_user_node *tmp; unsigned long ret = 0; for_each_list_entry(tmp, group->users) ret++; return ret; } static unsigned long count_users_nomultgroups(struct gr_learn_group_node *group) { struct gr_learn_user_node *tmp; unsigned long ret = 0; for_each_list_entry(tmp, group->users) { if (!tmp->multgroups) ret++; } return ret; } struct gr_learn_user_node * create_new_user(const char *username, uid_t uid, struct gr_learn_group_node *group, int multgroups) { struct gr_learn_user_node *user; user = (struct gr_learn_user_node *)gr_alloc(sizeof(struct gr_learn_user_node)); user->rolename = gr_strdup(username); user->uid = uid; user->group = group; user->multgroups = multgroups; return user; } struct gr_learn_group_node * create_new_group(const char *groupname, gid_t gid) { struct gr_learn_group_node *group; group = (struct gr_learn_group_node *)gr_alloc(sizeof(struct gr_learn_group_node)); group->rolename = gr_strdup(groupname); group->gid = gid; return group; } void insert_user(struct gr_learn_group_node **grouplist, const char *username, const char *groupname, uid_t uid, gid_t gid) { struct gr_learn_group_node *group; struct gr_learn_group_node *tmpgroup; struct gr_learn_user_node *user; struct gr_learn_user_node *tmpuser; int multgroups = 0; /* first check to see if the user exists in any group */ for_each_list_entry(group, *grouplist) { for_each_list_entry(user, group->users) { /* found them, check if we've noted the group membership observed */ if (user->uid == uid) { /* user belongs to multiple groups, don't use them for reduction */ if (user->group->gid != gid) { user->multgroups = 1; multgroups = 1; } else /* this entry is a duplicate */ return; } } } group = find_insert_group(*grouplist, gid); if (group == NULL) { group = create_new_group(groupname, gid); establish_new_head(*grouplist, group, tmpgroup); } user = create_new_user(username, uid, group, multgroups); establish_new_head(group->users, user, tmpuser); return; } void free_entire_user_node_list(struct gr_learn_user_node **userlist) { struct gr_learn_user_node *freeuser, *tmpuser; for_each_removable_list_entry(tmpuser, *userlist) { freeuser = tmpuser; tmpuser = tmpuser->next; free(freeuser->rolename); gr_free(freeuser); } *userlist = NULL; return; } void unlink_and_free_user_node_entry(struct gr_learn_user_node *remove) { if (remove->prev == NULL) { remove->group->users = remove->next; if (remove->next != NULL) remove->next->prev = NULL; } else { remove->prev->next = remove->next; if (remove->next != NULL) remove->next->prev = remove->prev; } free(remove->rolename); gr_free(remove); return; } struct gr_learn_file_node * unlink_and_free_file_node_entry(struct gr_learn_file_node *remove, struct gr_learn_file_node **filelist) { struct gr_learn_file_node *ret; ret = remove->next; if (remove->prev == NULL) { *filelist = remove->next; if (remove->next != NULL) remove->next->prev = NULL; } else { remove->prev->next = remove->next; if (remove->next != NULL) remove->next->prev = remove->prev; } free(remove->filename); gr_free(remove); /* clear cache when removing a node */ cachednode = NULL; cachedlen = 0; return ret; } /* unlink a file node entry, return the next entry, and stuff the unlinked entry in the argument */ struct gr_learn_file_node * unlink_file_node_entry(struct gr_learn_file_node *remove, struct gr_learn_file_node **filelist, struct gr_learn_file_node ** unlinked) { struct gr_learn_file_node *ret; ret = remove->next; if (remove->prev == NULL) { *filelist = remove->next; if (remove->next != NULL) remove->next->prev = NULL; } else { remove->prev->next = remove->next; if (remove->next != NULL) remove->next->prev = remove->prev; } /* clear cache when removing a node */ cachednode = NULL; cachedlen = 0; *unlinked = remove; return ret; } struct gr_learn_ip_node * unlink_and_free_ip_node_entry(struct gr_learn_ip_node *remove, struct gr_learn_ip_node **iplist) { struct gr_learn_ip_node *ret; ret = remove->next; if (remove->prev == NULL) { *iplist = remove->next; if (remove->next != NULL) remove->next->prev = NULL; } else { remove->prev->next = remove->next; if (remove->next != NULL) remove->next->prev = remove->prev; } gr_free(remove); return ret; } struct gr_learn_group_node * unlink_and_free_group_node_entry(struct gr_learn_group_node *remove, struct gr_learn_group_node **grouplist) { struct gr_learn_group_node *ret; ret = remove->next; if (remove->prev == NULL) { *grouplist = remove->next; if (remove->next != NULL) remove->next->prev = NULL; } else { remove->prev->next = remove->next; if (remove->next != NULL) remove->next->prev = remove->prev; } free(remove->rolename); gr_free(remove); return ret; } void reduce_roles(struct gr_learn_group_node **grouplist) { unsigned int thresh = 3; struct gr_learn_group_node *group, *group2; struct gr_learn_user_node *tmpuser, *tmpuser2; unsigned long num; int removed = 0; for_each_list_entry(group, *grouplist) { num = count_users_nomultgroups(group); if (num < thresh) continue; free_entire_user_node_list(&group->users); } /* make sure only one role is created for each user */ for_each_list_entry(group, *grouplist) { for_each_list_entry(tmpuser, group->users) { if (!tmpuser->multgroups) continue; /* check to see if the user is in another group, and remove them from this group if so */ for_each_removable_list_entry(group2, group->next) { for_each_list_entry(tmpuser2, group2->users) { if (tmpuser2->uid == tmpuser->uid) { unlink_and_free_user_node_entry(tmpuser2); /* we removed the only user in this group, so remove the group as well */ if (group2->users == NULL) { group2 = unlink_and_free_group_node_entry(group2, grouplist); removed = 1; } goto done; } } done: for_each_removable_list_entry_end(group2); } } } return; } void traverse_file_tree(struct gr_learn_file_node *base, int (*act)(struct gr_learn_file_node *node, const void *optarg, FILE *stream), const void *optarg, FILE *stream) { struct gr_learn_file_node *node; if (!base) return; act(base, optarg, stream); for_each_list_entry(node, base->leaves) traverse_file_tree(node, act, optarg, stream); return; } struct gr_learn_file_node *match_file_node(struct gr_learn_file_node *base, const char *filename) { struct gr_learn_file_node *node, *ret; unsigned int baselen, filelen; filelen = strlen(filename); if (base == NULL) return base; baselen = strlen(base->filename); if ((filelen == baselen) && !strcmp(base->filename, filename)) return base; if ((baselen >= filelen) || (filename[baselen] != '/' && baselen != 1) || strncmp(base->filename, filename, baselen)) return NULL; for_each_list_entry(node, base->leaves) { if ((ret = match_file_node(node, filename))) return ret; } return base; } unsigned long count_nodes(struct gr_learn_file_node *node) { unsigned long ret = 0; for_each_list_entry(node, node) ret++; return ret; } unsigned long count_leaf_nodes(struct gr_learn_file_node *node) { unsigned long ret = 0; for_each_list_entry(node, node) { if (node->leaves == NULL) ret++; } return ret; } unsigned long count_total_leaves(struct gr_learn_file_node *node) { unsigned long leaves = 0; struct gr_learn_file_node *tmp; for_each_list_entry(tmp, node->leaves) { leaves++; leaves += count_total_leaves(tmp); } return leaves; } unsigned long count_max_depth(struct gr_learn_file_node *node) { unsigned long max = 0, tmpmax = 0; struct gr_learn_file_node *tmp; if (node->leaves == NULL) return 0; max++; for_each_list_entry(tmp, node->leaves) { tmpmax = count_max_depth(tmp); if ((max + tmpmax) > max) max = tmpmax + max; } return max; } unsigned long count_nested_depth(struct gr_learn_file_node *node) { unsigned long depth = 0; struct gr_learn_file_node *tmp; for_each_parent_entry(tmp, node->parent) depth++; return depth; } /* this reduces all files in a directory, but not including any subdirectories */ int reduce_all_children(struct gr_learn_file_node *node) { unsigned long not_leaf = 0; unsigned long i; struct gr_learn_file_node *tmp; int removed = 0; for_each_list_entry(tmp, node->leaves) { if (tmp->leaves != NULL) { not_leaf++; continue; } node->mode |= tmp->mode; if (node->subject == NULL || tmp->subject == NULL) continue; /* merge capabilities */ node->subject->cap_raise = cap_combine(node->subject->cap_raise, tmp->subject->cap_raise); /* merge resources */ node->subject->resmask |= tmp->subject->resmask; for (i = 0; i < GR_NLIMITS; i++) { if (tmp->subject->res[i].rlim_cur > node->subject->res[i].rlim_cur) node->subject->res[i].rlim_cur = tmp->subject->res[i].rlim_cur; if (tmp->subject->res[i].rlim_max > node->subject->res[i].rlim_max) node->subject->res[i].rlim_max = tmp->subject->res[i].rlim_max; } /* merge socket families */ for (i = 0; i < SIZE(node->subject->sock_families); i++) node->subject->sock_families[i] |= tmp->subject->sock_families[i]; } for_each_removable_list_entry(tmp, node->leaves) { if (tmp->leaves != NULL) goto next_entry; tmp = unlink_and_free_file_node_entry(tmp, &node->leaves); removed = 1; next_entry: for_each_removable_list_entry_end(tmp); } if (!not_leaf) node->leaves = NULL; return 0; } int reduce_all_leaves(struct gr_learn_file_node *node) { struct gr_learn_file_node *tmp; unsigned int i; int removed = 0; for_each_removable_list_entry(tmp, node->leaves) { reduce_all_leaves(tmp); node->mode |= tmp->mode; if (node->subject == NULL || tmp->subject == NULL) goto remove_node; /* merge capabilities */ node->subject->cap_raise = cap_combine(node->subject->cap_raise, tmp->subject->cap_raise); /* merge resources */ node->subject->resmask |= tmp->subject->resmask; for (i = 0; i < GR_NLIMITS; i++) { if (tmp->subject->res[i].rlim_cur > node->subject->res[i].rlim_cur) node->subject->res[i].rlim_cur = tmp->subject->res[i].rlim_cur; if (tmp->subject->res[i].rlim_max > node->subject->res[i].rlim_max) node->subject->res[i].rlim_max = tmp->subject->res[i].rlim_max; } /* merge socket families */ for (i = 0; i < SIZE(node->subject->sock_families); i++) node->subject->sock_families[i] |= tmp->subject->sock_families[i]; remove_node: tmp = unlink_and_free_file_node_entry(tmp, &node->leaves); removed = 1; for_each_removable_list_entry_end(tmp); } node->leaves = NULL; return 0; } void greatest_occurring_modes(struct gr_learn_file_node *node, u_int32_t *modeary) { struct gr_learn_file_node *tmp; u_int32_t modes[12] = { GR_FIND, GR_FIND | GR_READ, GR_FIND | GR_READ | GR_WRITE, GR_FIND | GR_READ | GR_EXEC, GR_FIND | GR_EXEC, GR_FIND | GR_WRITE, GR_FIND | GR_WRITE | GR_CREATE, GR_FIND | GR_WRITE | GR_DELETE, GR_FIND | GR_WRITE | GR_CREATE | GR_DELETE, GR_FIND | GR_READ | GR_WRITE | GR_CREATE | GR_DELETE, GR_FIND | GR_READ | GR_WRITE | GR_DELETE, GR_FIND | GR_READ | GR_WRITE | GR_CREATE, }; unsigned long counts[12] = {0}; u_int16_t max, max2; int i; for_each_list_entry(tmp, node->leaves) { for (i = 0; i < 12; i++) { if (tmp->mode == modes[i]) counts[i]++; } } max = 0; max2 = 0; for (i = 0; i < 12; i++) { if (counts[i] > counts[max]) max = i; else if (max == max2 || counts[i] > counts[max2]) max2 = i; } *modeary = modes[max]; *(modeary + 1) = modes[max2]; } int reduce_children_mode(struct gr_learn_file_node *node) { struct gr_learn_file_node *tmp; u_int32_t modes[2]; int ret = 0; int tmpdir = 0; int removed = 0; if (node->leaves == NULL) return 0; greatest_occurring_modes(node, (u_int32_t *)&modes); node->mode |= modes[0]; node->mode |= modes[1]; if (node->mode == (GR_FIND | GR_READ | GR_WRITE | GR_CREATE | GR_DELETE)) tmpdir = 1; for_each_removable_list_entry(tmp, node->leaves) { if (((tmpdir && !(tmp->mode & GR_EXEC)) || (tmp->mode == modes[0] || tmp->mode == modes[1])) && tmp->leaves == NULL) { ret++; tmp = unlink_and_free_file_node_entry(tmp, &node->leaves); removed = 1; } for_each_removable_list_entry_end(tmp); } return ret; } int analyze_node_read_permissions(struct gr_learn_file_node *node) { struct gr_learn_file_node *tmp; for_each_list_entry(tmp, node->leaves) { if ((tmp->mode & GR_WRITE) && !(tmp->mode & GR_READ)) return 0; if (!analyze_node_read_permissions(tmp)) return 0; } return 1; } int analyze_node_write_permissions(struct gr_learn_file_node *node) { struct gr_learn_file_node *tmp; for_each_list_entry(tmp, node->leaves) { if (!(tmp->mode & GR_WRITE) && (tmp->mode & GR_READ)) return 0; if (!analyze_node_write_permissions(tmp)) return 0; } return 1; } int analyze_child_read_permissions(struct gr_learn_file_node *node) { struct gr_learn_file_node *tmp; for_each_list_entry(tmp, node->leaves) { if (tmp->leaves) continue; if ((tmp->mode & GR_WRITE) && !(tmp->mode & GR_READ)) return 0; } return 1; } int analyze_child_write_permissions(struct gr_learn_file_node *node) { struct gr_learn_file_node *tmp; for_each_list_entry(tmp, node->leaves) { if (tmp->leaves) continue; if (!(tmp->mode & GR_WRITE) && (tmp->mode & GR_READ)) return 0; } return 1; } int *analyze_node_reduction(struct gr_learn_file_node *node) { int reduce_child_thresh = 4; int reduce_leaves_thresh = 8; int reduction_level = 0; unsigned long node_num; unsigned long child_num; unsigned long depth_num; unsigned long nested_num; int child_reduced = 0; char **tmp; /* revert all the changes i made to this function */ if (node->leaves == NULL) return NULL; tmp = dont_reduce_dirs; if (tmp) { while (*tmp) { if (!strcmp(node->filename, *tmp)) return NULL; tmp++; } } tmp = always_reduce_dirs; if (tmp) { while (*tmp) { if (!strcmp(node->filename, *tmp)) return (int *)&reduce_all_leaves; tmp++; } } node_num = count_leaf_nodes(node->leaves); child_num = count_total_leaves(node); depth_num = count_max_depth(node); nested_num = count_nested_depth(node); if (node_num > 3) reduction_level++; if (node_num > 6) reduction_level++; if (node_num > 10) reduction_level++; if (node_num > 15) reduction_level++; if (node_num > 20) reduction_level++; if (nested_num > 2) reduction_level++; if (nested_num > 4) reduction_level++; if (nested_num > 6) reduction_level++; if (child_num > 5) reduction_level++; if (child_num > 10) reduction_level++; if (child_num > 20) reduction_level++; if (child_num > 40) reduction_level++; if (child_num > 80) reduction_level++; if (depth_num > 2) reduction_level++; if (depth_num > 4) reduction_level++; if (depth_num > 6) reduction_level++; tmp = high_reduce_dirs; if (tmp) { while (*tmp) { if (!strcmp(node->filename, *tmp) && ((node_num > 2) || child_num > 5)) reduction_level *= 2; tmp++; } } if (node->subject) goto final; if (analyze_node_read_permissions(node) || analyze_node_write_permissions(node)) reduction_level *= 2; else { if (reduction_level >= reduce_child_thresh) { child_reduced = reduce_children_mode(node); if (child_reduced > ((3 *node_num) / 4)) return 0; } } final: if (reduction_level >= reduce_leaves_thresh) return (int *)&reduce_all_leaves; else if (reduction_level >= reduce_child_thresh) return (int *)&reduce_all_children; else return NULL; } /* for this stage based on some heuristics we decide if for a given directory, all files within it should be reduced, or if all files and subdirectories in it should be reduced */ int second_reduce_node(struct gr_learn_file_node *node, const void *unused1, FILE *unused) { int (* retval)(struct gr_learn_file_node *node); retval = (int (*)(struct gr_learn_file_node *))analyze_node_reduction(node); if (retval) retval(node); return 0; } void second_stage_reduce_tree(struct gr_learn_file_node *base) { return traverse_file_tree(base, &second_reduce_node, NULL, NULL); } int third_reduce_node(struct gr_learn_file_node *node, const void *unused1, FILE *unused) { struct gr_learn_file_node *tmp; int removed = 0; for_each_removable_list_entry(tmp, node->leaves) { if (tmp->leaves) goto next_entry; if (tmp->mode == node->mode || (((node->mode & (GR_WRITE | GR_CREATE)) == (GR_WRITE | GR_CREATE)) && (tmp->mode & GR_WRITE))) { node->mode |= tmp->mode; tmp = unlink_and_free_file_node_entry(tmp, &node->leaves); removed = 1; } next_entry: for_each_removable_list_entry_end(tmp); } return 0; } void third_stage_reduce_tree(struct gr_learn_file_node *base) { return traverse_file_tree(base, &third_reduce_node, NULL, NULL); } struct gr_learn_file_node *do_find_insert_file(struct gr_learn_file_node **base, const char *filename, unsigned int filelen) { struct gr_learn_file_node *node, *tmpnode, *ret; unsigned int baselen; /* base needs to at least contain a root node for /, if it doesn't then we add it here */ if (!*base) { *base = (struct gr_learn_file_node *)gr_alloc(sizeof(struct gr_learn_file_node)); /* the base has a NULL parent */ (*base)->parent = NULL; return *base; } baselen = strlen((*base)->filename); /* simple lookup, the basename we gave was a match for the filename we were trying to add */ if ((filelen == baselen) && !strcmp((*base)->filename, filename)) return *base; node = (*base)->leaves; /* if there are no leaves for this base and the directory for the base matches the file we're inserting, add the first leaf */ if (!node && (baselen < filelen) && (baselen == 1 || filename[baselen] == '/') && !strncmp((*base)->filename, filename, baselen)) { (*base)->leaves = node = (struct gr_learn_file_node *)gr_alloc(sizeof(struct gr_learn_file_node)); node->parent = *base; cachednode = *base; cachedlen = baselen; return node; } else if (!node) { /* there are no leaves for this base, and it didn't match the filename we're inserting */ return NULL; } for_each_list_entry(tmpnode, node) { ret = do_find_insert_file(&tmpnode, filename, filelen); if (ret) return ret; } /* this is not a match for the file we're inserting */ if ((baselen >= filelen) || (baselen != 1 && filename[baselen] != '/') || strncmp((*base)->filename, filename, baselen)) return NULL; cachednode = *base; cachedlen = baselen; ret = (struct gr_learn_file_node *)gr_alloc(sizeof(struct gr_learn_file_node)); ret->parent = *base; establish_new_head((*base)->leaves, ret, tmpnode); return ret; } #ifdef GRADM_DEBUG static struct gr_learn_file_node *find_file(struct gr_learn_file_node *filelist, const char *filename) { struct gr_learn_file_node *tmp, *ret; unsigned int alen, blen; if (filelist == NULL) return NULL; alen = strlen(filelist->filename); blen = strlen(filename); /* return if we've found a perfect match */ if (alen == blen && !strcmp(filelist->filename, filename)) return filelist; if (alen >= blen) return NULL; /* if this is a subdirectory match, then work our way up through the leaves to find the most specific match to return */ if (!strncmp(filelist->filename, filename, alen) && (alen == 1 || filename[alen] == '/')) { for_each_list_entry(tmp, filelist->leaves) { ret = find_file(tmp, filename); if (ret != NULL) return ret; } return filelist; } /* if this wasn't a subdirectory match, then try to match against the other nodes at the current level */ for_each_list_entry(tmp, filelist->leaves) { ret = find_file(tmp, filename); if (ret) return ret; } return NULL; } #endif struct gr_learn_file_node *find_insert_file(struct gr_learn_file_node **base, const char *filename, unsigned int filelen) { if (cachednode && (cachedlen < filelen) && !strncmp(cachednode->filename, filename, cachedlen) && filename[cachedlen] == '/') { return do_find_insert_file(&cachednode, filename, filelen); } else if (cachednode && (cachedlen >= filelen)) { cachednode = NULL; cachedlen = 0; } return do_find_insert_file(base, filename, filelen); } void update_parent_pointers(struct gr_learn_file_node *base) { struct gr_learn_file_node *tmp; if (base->leaves == NULL) return; for_each_list_entry(tmp, base->leaves) tmp->parent = base; return; } void do_replace_file(struct gr_learn_file_node **base, struct gr_learn_file_node *replace) { struct gr_learn_file_node *node; node = find_insert_file(base, replace->filename, strlen(replace->filename)); assert(node != NULL); node->mode = replace->mode; node->dont_display = 0; assert(node->leaves == NULL); node->leaves = replace->leaves; assert(node->filename == NULL); node->filename = gr_strdup(replace->filename); /* important: we need to update all the parent pointers for these directly-linked nodes */ update_parent_pointers(node); return; } void do_insert_file(struct gr_learn_file_node **base, const char *filename, u_int32_t mode, u_int8_t subj) { struct gr_learn_file_node *node; node = find_insert_file(base, filename, strlen(filename)); assert(node != NULL); node->mode |= mode; node->dont_display = 0; if (node->filename == NULL) node->filename = gr_strdup(filename); if (subj) insert_file(&(node->object_list), "/", 0, 0); return; } void insert_file(struct gr_learn_file_node **base, const char *filename, u_int32_t mode, u_int8_t subj) { /* we're inserting a new file, and an entry for / does not exist, add it */ if (!(*base)) { if (subj) { do_insert_file(base, "/", GR_PROCFIND, subj); if (subj == 2) { /* learning in non-full mode, don't display / subject */ assert(*base != NULL); (*base)->dont_display = 1; } } else do_insert_file(base, "/", 0, subj); } do_insert_file(base, filename, mode, subj); return; } /* if this node has above the threshold number of leaves, then terminate each leaf at its next path component and reinsert them all as directories into the tree then, re-anchor each leaf to the newly created directory nodes this algorithm gets called against every node/leaf in the tree */ int first_reduce_node(struct gr_learn_file_node *node, const void *unused1, FILE *unused) { unsigned long thresh = 5; unsigned long num = count_nodes(node->leaves); struct gr_learn_file_node *tmp, *tmp2; char *p; const char *p2; unsigned int node_len = strlen(node->filename); int removed = 0; if (num < thresh) return 0; for_each_list_entry(tmp, node->leaves) { if (node_len == 1) p2 = strchr(tmp->filename + 1, '/'); else p2 = strchr(tmp->filename + node_len + 1, '/'); if (p2 == NULL) continue; p = gr_strdup(tmp->filename); *(p + (p2 - tmp->filename)) = '\0'; cachednode = NULL; cachedlen = 0; insert_file(&node, p, 0, 0); free(p); } /* we're pulling out each leaf in this node and re-inserting it we need to find where to insert the node, and then copy the unlinked one in directly, preserving any attached leaves it may have */ for_each_removable_list_entry(tmp, node->leaves) { tmp = unlink_file_node_entry(tmp, &node->leaves, &tmp2); removed = 1; do_replace_file(&node, tmp2); free(tmp2->filename); gr_free(tmp2); for_each_removable_list_entry_end(tmp); } return 0; } void first_stage_reduce_tree(struct gr_learn_file_node *base) { return traverse_file_tree(base, &first_reduce_node, NULL, NULL); } void display_tree(struct gr_learn_file_node *base, FILE *stream) { traverse_file_tree(base, &display_leaf, NULL, stream); return; } void display_tree_with_role(struct gr_learn_file_node *base, const char *rolename, FILE *stream) { traverse_file_tree(base, &display_leaf, rolename, stream); return; } #ifdef GRADM_DEBUG void check_high_protected_path_enforcement(struct gr_learn_file_node *subject) { struct gr_learn_file_node *find; struct gr_learn_file_tmp_node **tmptable; char **tmp; unsigned int len; int is_glob; unsigned long i; tmp = high_protected_paths; if (tmp == NULL) return; /* don't check if there are no file objects */ if (subject->hash == NULL) return; tmptable = (struct gr_learn_file_tmp_node **)subject->hash->table; while (*tmp) { len = strlen(*tmp); is_glob = is_globbed_file(*tmp); for (i = 0; i < subject->hash->table_size; i++) { if (tmptable[i] == NULL) continue; if (!tmptable[i]->mode) continue; if (!match_filename(tmptable[i]->filename, *tmp, len, is_glob)) goto next; } /* for all the ones that we didn't have a matching access from the learning logs, find the object that matches us and make sure it's hidden */ find = find_file(subject->object_list, *tmp); assert(find != NULL); if (find->mode != 0) printf("Failed to enforce high-protected rule %s by object %s\n", *tmp, find->filename); next: tmp++; } return; } void check_conformity_with_learned_rules(struct gr_learn_file_node *subject) { struct gr_learn_file_node *tmp; struct gr_learn_file_tmp_node **tmptable; unsigned long i, table_size; /* don't check if there are no file objects */ if (subject->hash == NULL) return; tmptable = (struct gr_learn_file_tmp_node **)subject->hash->table; table_size = subject->hash->table_size; for (i = 0; i < table_size; i++) { if (tmptable[i] == NULL) continue; tmp = find_file(subject->object_list, tmptable[i]->filename); assert(tmp != NULL); if ((tmp->mode & tmptable[i]->mode) != tmptable[i]->mode) printf("Nonconformance detected in object %s with mode %x, %s requires %x\n", tmp->filename, tmp->mode, tmptable[i]->filename, tmptable[i]->mode); } return; } void check_file_node_list_integrity(struct gr_learn_file_node **filelist) { struct gr_learn_file_node *node; unsigned int parentlen, ourlen; struct gr_learn_file_node *tmpnode; int i; if (*filelist == NULL) return; for_each_list_entry(node, *filelist) { check_file_node_list_integrity(&node->leaves); if (strcmp(node->filename, "/") && node->parent == NULL) goto inconsistency; else if (node->parent == NULL) goto ok; parentlen = strlen(node->parent->filename); ourlen = strlen(node->filename); if (parentlen >= ourlen) goto inconsistency; if (strncmp(node->filename, node->parent->filename, parentlen)) goto inconsistency; if (parentlen != 1 && node->filename[parentlen] != '/') goto inconsistency; if (node->next && node->next->prev != node) goto inconsistency; if (node->prev && node->prev->next != node) goto inconsistency; tmpnode = node; i = 4096; while (tmpnode->parent && i) { tmpnode = tmpnode->parent; i--; } if (i == 0) goto inconsistency; goto ok; inconsistency: printf("Inconsistency detected with file %s, parent %s\n", node->filename, node->parent ? node->parent->filename : "NULL"); ok: ; } } #endif int display_leaf(struct gr_learn_file_node *node, const void *rolename, FILE *stream) { char modes[33]; int i; if (node->dont_display) return 0; if (node->object_list) { struct gr_learn_file_node *object; struct gr_learn_ip_node *connect; struct gr_learn_ip_node *bind; unsigned int raise_num; object = node->object_list; connect = node->connect_list; bind = node->bind_list; conv_subj_mode_to_str(node->mode &~ (GR_LEARN | GR_INHERITLEARN), modes, sizeof(modes)); if (rolename && (!gr_fulllearn || !(grlearn_options & GR_SPLIT_ROLES))) fprintf(stream, "# Role: %s\n", (const char *)rolename); fprintf(stream, "subject %s %s {\n", node->filename, modes); if (node->user_trans_list) { unsigned int **p = node->user_trans_list; struct passwd *pwd; fprintf(stream, "user_transition_allow"); while (*p) { pwd = getpwuid(**p); if (pwd == NULL) { fprintf(stream, " %d", **p); p++; continue; } fprintf(stream, " %s", pwd->pw_name); p++; } if (node->group_trans_list == NULL) fprintf(stream, "\n\n"); else fprintf(stream, "\n"); } if (node->group_trans_list) { unsigned int **p = node->group_trans_list; struct group *grp; fprintf(stream, "group_transition_allow"); while (*p) { grp = getgrgid(**p); if (grp == NULL) { fprintf(stream, " %d", **p); p++; continue; } fprintf(stream, " %s", grp->gr_name); p++; } fprintf(stream, "\n\n"); } if (object) { sort_file_node_list(object); #ifdef GRADM_DEBUG check_file_node_list_integrity(&object->leaves); check_conformity_with_learned_rules(node); check_high_protected_path_enforcement(node); #endif display_tree(object, stream); } if (!node->subject) { fprintf(stream, "\t-CAP_ALL\n"); goto show_ips; } for(i = raise_num = 0; i < ((sizeof(capability_list)/sizeof(struct capability_set)) - 1); i++) if (cap_raised(node->subject->cap_raise, capability_list[i].cap_val)) raise_num++; if (raise_num < ((sizeof(capability_list)/sizeof(struct capability_set)) - 1) / 2) { fprintf(stream, "\t-CAP_ALL\n"); for(i = 0; i < ((sizeof(capability_list)/sizeof(struct capability_set)) - 1); i++) if (cap_raised(node->subject->cap_raise, capability_list[i].cap_val)) fprintf(stream, "\t+%s\n", capability_list[i].cap_name); } else { fprintf(stream, "\t+CAP_ALL\n"); for(i = 0; i < ((sizeof(capability_list)/sizeof(struct capability_set)) - 1); i++) if (!cap_raised(node->subject->cap_raise, capability_list[i].cap_val)) fprintf(stream, "\t-%s\n", capability_list[i].cap_name); } for (i = 0; i < SIZE(paxflag_list); i++) { if (node->subject->pax_flags & (1U << paxflag_list[i].paxflag_val)) fprintf(stream, "\t+%s\n", paxflag_list[i].paxflag_name); else if (node->subject->pax_flags & (0x8000 | (1U << paxflag_list[i].paxflag_val))) fprintf(stream, "\t-%s\n", paxflag_list[i].paxflag_name); } for(i = 0; i < SIZE(rlim_table); i++) if (node->subject->resmask & (1U << i)) fprintf(stream, "\t%s %lu %lu\n", rlim_table[i], node->subject->res[i].rlim_cur, node->subject->res[i].rlim_max); show_ips: if (bind) display_ip_tree(bind, GR_IP_BIND, stream); else fprintf(stream, "\tbind\tdisabled\n"); if (connect) display_ip_tree(connect, GR_IP_CONNECT, stream); else fprintf(stream, "\tconnect\tdisabled\n"); /* display socket families */ if (node->subject != NULL) { int cnt = 0; for (i = 0; i < (SIZE(node->subject->sock_families) * 32); i++) if (node->subject->sock_families[i / 32] & (1U << (i % 32))) cnt++; /* if we have bind/connect rules and no extra family allowance outside of the default, then don't add a sock_family rule */ if ((bind || connect) && !(node->subject->sock_families[0] & ~((1U << AF_UNIX) | (1U << AF_INET))) && node->subject->sock_families[1] == 0) ; else if (cnt > 10) fprintf(stream, "\tsock_allow_family all\n"); else if (cnt) { fprintf(stream, "\tsock_allow_family"); for (i = 0; i < AF_MAX; i++) { if ((bind || connect) && (i == AF_UNIX || i == AF_INET)) continue; if (node->subject->sock_families[i / 32] & (1U << (i % 32))) fprintf(stream, " %s", get_sock_family_from_val(i)); } fprintf(stream, "\n"); } } if (node->subject != NULL && node->subject->inaddr_any_override) { struct in_addr addr; addr.s_addr = node->subject->inaddr_any_override; fprintf(stream, "\tip_override\t%s\n", inet_ntoa(addr)); } fprintf(stream, "}\n\n"); } else { conv_mode_to_str(node->mode, modes, sizeof(modes)); i = strlen(node->filename); if (strchr(node->filename, ' ')) { if (i < 8) fprintf(stream, "\t\"%s\"\t\t\t\t%s\n", node->filename, modes); else if (i < 16) fprintf(stream, "\t\"%s\"\t\t\t%s\n", node->filename, modes); else if (i < 24) fprintf(stream, "\t\"%s\"\t\t%s\n", node->filename, modes); else fprintf(stream, "\t\"%s\"\t%s\n", node->filename, modes); } else { if (i < 8) fprintf(stream, "\t%s\t\t\t\t%s\n", node->filename, modes); else if (i < 16) fprintf(stream, "\t%s\t\t\t%s\n", node->filename, modes); else if (i < 24) fprintf(stream, "\t%s\t\t%s\n", node->filename, modes); else fprintf(stream, "\t%s\t%s\n", node->filename, modes); } } fflush(stream); return 0; } void traverse_ip_tree(struct gr_learn_ip_node *base, struct gr_learn_ip_node **optarg, int (*act)(struct gr_learn_ip_node *node, struct gr_learn_ip_node **optarg, u_int8_t contype, FILE *stream), u_int8_t contype, FILE *stream) { struct gr_learn_ip_node *node; if (!base) return; act(base, optarg, contype, stream); for_each_list_entry(node, base->leaves) traverse_ip_tree(node, optarg, act, contype, stream); return; } int count_ip_depth(struct gr_learn_ip_node *node) { int depth = 0; for_each_parent_entry(node, node->parent) depth++; return depth; } unsigned long count_total_ips(struct gr_learn_ip_node *node) { unsigned long ips = 0; struct gr_learn_ip_node *tmp; if (node->leaves == NULL) return 1; for_each_list_entry(tmp, node->leaves) ips += count_total_ips(tmp); return ips; } int display_ip_node(struct gr_learn_ip_node *node, struct gr_learn_ip_node **unused, u_int8_t contype, FILE *stream) { struct gr_learn_ip_node *saved = node; int depth = count_ip_depth(node); u_int16_t **tmpport; u_int8_t ip[4]; char ipandtype[64] = {0}; char socktypeandprotos[4096] = {0}; struct protoent *proto; int netmask = 0; int i; if (node->leaves) return 0; if (!node->root_node) netmask = 8 * depth; else { ip[0] = ip[1] = ip[2] = ip[3] = 0; goto print_ip; } for(i = 3; i >= 0; i--) { if (depth < (i + 1)) ip[i] = 0; else { ip[i] = node->ip_node; node = node->parent; } } print_ip: node = saved; if (contype == GR_IP_CONNECT) sprintf(ipandtype, "\tconnect %u.%u.%u.%u/%u", ip[0], ip[1], ip[2], ip[3], netmask); else if (contype == GR_IP_BIND) sprintf(ipandtype, "\tbind %u.%u.%u.%u/%u", ip[0], ip[1], ip[2], ip[3], netmask); for (i = 1; i < 5; i++) { if (node->ip_type & (1U << i)) { switch (i) { case SOCK_RAW: strcat(socktypeandprotos, " raw_sock"); break; case SOCK_DGRAM: strcat(socktypeandprotos, " dgram"); break; case SOCK_STREAM: strcat(socktypeandprotos, " stream"); break; case SOCK_RDM: strcat(socktypeandprotos, " rdm"); break; } } } for (i = 0; i < 256; i++) { if (node->ip_proto[i / 32] & (1U << (i % 32))) { if (i == IPPROTO_RAW) { strcat(socktypeandprotos, " raw_proto"); } else { proto = getprotobynumber(i); strcat(socktypeandprotos, " "); if (proto) strcat(socktypeandprotos, proto->p_name); else sprintf(socktypeandprotos+strlen(socktypeandprotos), "proto:%d", i); } } } if (node->all_low_ports && node->all_high_ports) fprintf(stream, "%s:0-65535%s\n", ipandtype, socktypeandprotos); else if (node->all_low_ports) fprintf(stream, "%s:0-1023%s\n", ipandtype, socktypeandprotos); else if (node->all_high_ports) fprintf(stream, "%s:1024-65535%s\n", ipandtype, socktypeandprotos); tmpport = node->ports; while(tmpport && *tmpport) { if (!(node->all_low_ports && **tmpport < 1024) && !(node->all_high_ports && **tmpport >= 1024)) fprintf(stream, "%s:%u%s\n", ipandtype, **tmpport, socktypeandprotos); tmpport++; } return 0; } int display_only_ip(struct gr_learn_ip_node *node, struct gr_learn_ip_node **unused, u_int8_t unused2, FILE *stream) { struct gr_learn_ip_node *saved = node; int depth = count_ip_depth(node); u_int8_t ip[4]; int netmask = 0; int i; if (node->leaves) return 0; if (!node->root_node) netmask = 8 * depth; else { ip[0] = ip[1] = ip[2] = ip[3] = 0; goto print_ip; } for(i = 3; i >= 0; i--) { if (depth < (i + 1)) ip[i] = 0; else { ip[i] = node->ip_node; node = node->parent; } } print_ip: node = saved; fprintf(stream, "role_allow_ip\t%u.%u.%u.%u/%u\n", ip[0], ip[1], ip[2], ip[3], netmask); return 0; } void display_ip_tree(struct gr_learn_ip_node *base, u_int8_t contype, FILE *stream) { traverse_ip_tree(base, NULL, &display_ip_node, contype, stream); return; } unsigned long count_ports(u_int16_t **ports) { unsigned long ret = 0; if (!ports) return ret; while (*ports) { ports++; ret++; } return ret; } unsigned long count_ips(struct gr_learn_ip_node *ips) { unsigned long ret = 0; struct gr_learn_ip_node *tmp; for_each_list_entry(tmp, ips) ret++; return ret; } int analyze_ip_node(struct gr_learn_ip_node *node) { int depth = count_ip_depth(node); unsigned long num_ips = count_total_ips(node); unsigned long analysis_factor = (depth + 1) * num_ips; if (analysis_factor > 19) return 1; else return 0; } void insert_port(struct gr_learn_ip_node *node, u_int16_t port) { u_int16_t **tmpport; unsigned long num; tmpport = node->ports; num = count_ports(tmpport); while(tmpport && *tmpport) { if (**tmpport == port) return; tmpport++; } if (!num) { node->ports = (u_int16_t **)gr_alloc(2 * sizeof(u_int16_t *)); *(node->ports) = (u_int16_t *)gr_alloc(sizeof(u_int16_t)); **(node->ports) = port; } else { node->ports = (u_int16_t **)gr_realloc(node->ports, (num + 2) * sizeof(u_int16_t *)); memset(node->ports + num, 0, 2 * sizeof(u_int16_t *)); *(node->ports + num) = (u_int16_t *)gr_alloc(sizeof(u_int16_t)); **(node->ports + num) = port; } return; } void remove_port(struct gr_learn_ip_node *node, u_int16_t port) { u_int16_t **ports = node->ports; unsigned long num = count_ports(ports); unsigned long i; for(i = 0; i < num; i++) { if (**(ports + i) == port) { gr_free(*(ports + i)); while (i < num) { **(ports + i) = **(ports + i + 1); i++; } } } return; } void do_reduce_ip_node(struct gr_learn_ip_node *node, struct gr_learn_ip_node *actor) { u_int16_t **tmpport = node->ports; struct gr_learn_ip_node *tmpip; int i; int removed = 0; while (tmpport && *tmpport) { insert_port(actor, **tmpport); gr_free(*tmpport); *tmpport = NULL; tmpport++; } if (node->ports) { gr_free(node->ports); node->ports = NULL; } for (i = 0; i < (sizeof(node->ip_proto)/sizeof(node->ip_proto[0])); i++) actor->ip_proto[i] |= node->ip_proto[i]; actor->ip_type |= node->ip_type; for_each_removable_list_entry(tmpip, node->leaves) { do_reduce_ip_node(tmpip, actor); tmpip = unlink_and_free_ip_node_entry(tmpip, &node->leaves); removed = 1; for_each_removable_list_entry_end(tmpip); } node->leaves = NULL; return; } int reduce_ip_node(struct gr_learn_ip_node *node, struct gr_learn_ip_node **actor, u_int8_t unused1, FILE *unused2) { if (analyze_ip_node(node)) { *actor = node; do_reduce_ip_node(node, *actor); } return 0; } int analyze_port_node(struct gr_learn_ip_node *node) { unsigned long low_ports = 0, high_ports = 0; int ret = 0; u_int16_t **tmpport; tmpport = node->ports; while (tmpport && *tmpport) { if (**tmpport < 1024) low_ports++; else high_ports++; tmpport++; } if (low_ports > 5) ret += 1; if (high_ports > 4) ret += 2; return ret; } int reduce_port_node(struct gr_learn_ip_node *node, struct gr_learn_ip_node **unused, u_int8_t unused1, FILE *unused2) { switch(analyze_port_node(node)) { case 1: node->all_low_ports = 1; break; case 2: node->all_high_ports = 1; break; case 3: node->all_low_ports = 1; node->all_high_ports = 1; break; } return 0; } void reduce_ip_tree(struct gr_learn_ip_node *base) { struct gr_learn_ip_node *tmp = NULL; traverse_ip_tree(base, &tmp, &reduce_ip_node, 0, NULL); return; } void reduce_ports_tree(struct gr_learn_ip_node *base) { traverse_ip_tree(base, NULL, &reduce_port_node, 0, NULL); return; } u_int8_t extract_ip_field(u_int32_t ip, unsigned long depth) { u_int8_t ip_node[4]; memcpy(ip_node, &ip, sizeof(ip)); if (depth > 3) return 0; return ip_node[depth]; } struct gr_learn_ip_node * find_insert_ip(struct gr_learn_ip_node **base, u_int32_t ip) { struct gr_learn_ip_node *node, *tmpip, *newip; int depth = 0; int match = 0; if (!(*base)) { (*base) = (struct gr_learn_ip_node *)gr_alloc(sizeof(struct gr_learn_ip_node)); (*base)->root_node = 1; } depth = count_ip_depth(*base); node = (*base)->leaves; for_each_list_entry(tmpip, node) { if (tmpip->ip_node == extract_ip_field(ip, depth)) { match = 1; break; } } if (match && depth < 3) { /* partial match, try to match at the next depth */ return find_insert_ip(&tmpip, ip); } else if (match) { /* complete match, return it */ return tmpip; } else { /* no match, need to allocate a new node */ newip = (struct gr_learn_ip_node *)gr_alloc(sizeof(struct gr_learn_ip_node)); newip->parent = *base; newip->ip_node = extract_ip_field(ip, depth); establish_new_head((*base)->leaves, newip, tmpip); if (depth < 3) return find_insert_ip(&newip, ip); else return newip; } } void insert_ip(struct gr_learn_ip_node **base, u_int32_t ip, u_int16_t port, u_int8_t proto, u_int8_t socktype) { struct gr_learn_ip_node *node; node = find_insert_ip(base, ip); /* the IP has already been added to the tree, so just OR in the information we've filled out in the insert structure */ node->ip_proto[proto / 32] |= (1U << (proto % 32)); node->ip_type |= (1U << socktype); insert_port(node, port); return; } static int strcompare(const void *x, const void *y) { const struct gr_learn_file_tmp_node *x1 = *(const struct gr_learn_file_tmp_node * const *) x; const struct gr_learn_file_tmp_node *y1 = *(const struct gr_learn_file_tmp_node * const *) y; if (x1 == NULL && y1 == NULL) return 0; if (x1 == NULL && y1 != NULL) return 1; if (x1 != NULL && y1 == NULL) return -1; return strcmp(x1->filename, y1->filename); } /* use this function to operate on a hash table, thus we need to handle null entries in the table. we modify strcompare above to make null pointers lexicographically greater than all filenames, effectively pushing them to the end of the table */ void sort_file_list(struct gr_hash_struct *hash) { if (hash == NULL) return; return qsort(hash->table, hash->table_size, sizeof (struct gr_learn_file_tmp_node *), strcompare); } struct gr_learn_role_entry * insert_learn_role(struct gr_learn_role_entry **role_list, const char *rolename, u_int16_t rolemode) { struct gr_learn_role_entry *tmp; struct gr_learn_role_entry *newrole; for_each_list_entry(tmp, *role_list) { if (!strcmp(tmp->rolename, rolename)) { tmp->rolemode |= rolemode; return tmp; } } newrole = (struct gr_learn_role_entry *)gr_alloc(sizeof(struct gr_learn_role_entry)); newrole->rolename = gr_strdup(rolename); newrole->rolemode = rolemode; establish_new_head(*role_list, newrole, tmp); /* give every learned role a / subject */ insert_learn_role_subject(newrole, conv_filename_to_struct("/", GR_PROCFIND | GR_OVERRIDE)); return newrole; } struct gr_learn_role_entry * find_learn_role(struct gr_learn_role_entry *role_list, const char *rolename) { struct gr_learn_role_entry *tmp; for_each_list_entry(tmp, role_list) { if (!strcmp(tmp->rolename, rolename)) return tmp; } return NULL; } void insert_learn_id_transition(unsigned int ***list, int real, int eff, int fs) { unsigned int ids[] = { real, eff, fs }; int x, good, num; unsigned int **p; if (*list == NULL) *list = (unsigned int **)gr_alloc(2 * sizeof(unsigned int *)); for (x = 0; x < sizeof(ids)/sizeof(ids[0]); x++) { good = 1; if (ids[x] == -1) good = 0; for (p = *list; *p; p++) { if (ids[x] == **p) good = 0; } if (good) { p = *list; num = 0; while (*p) { p++; num++; } *list = (unsigned int **)gr_realloc(*list, (num + 2) * sizeof(unsigned int *)); memset(*list + num, 0, 2 * sizeof(unsigned int *)); *(*list + num) = (unsigned int *)gr_alloc(sizeof(unsigned int)); **(*list + num) = ids[x]; } } return; } gradm/gradm_func.h0000644000175000017510000002666513152754421014334 0ustar spenderspender/* * Copyright (C) 2002-2014 Bradley Spengler, Open Source Security, Inc. * http://www.grsecurity.net spender@grsecurity.net * * This file is part of gradm. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __GRADM_FUNC_H #define __GRADM_FUNC_H void yyerror(const char *s); FILE *open_acl_file(const char *filename); void get_user_passwd(struct gr_pw_entry *entry, int mode); int transmit_to_kernel(struct gr_arg_wrapper *buf); void generate_salt(struct gr_pw_entry *entry); void write_user_passwd(struct gr_pw_entry *entry); void parse_acls(void); void analyze_acls(void); void generate_hash(struct gr_pw_entry *entry); void init_variables(void); void parse_args(int argc, char *argv[]); gr_cap_t cap_conv(const char *cap); gr_cap_t cap_drop(gr_cap_t a, gr_cap_t b); gr_cap_t cap_combine(gr_cap_t a, gr_cap_t b); gr_cap_t cap_intersect(gr_cap_t a, gr_cap_t b); int cap_same(gr_cap_t a, gr_cap_t b); gr_cap_t cap_invert(gr_cap_t a); int cap_isclear(gr_cap_t a); u_int32_t file_mode_conv(const char *mode); u_int32_t proc_subject_mode_conv(const char *mode); u_int32_t proc_object_mode_conv(const char *mode); int add_proc_subject_acl(struct role_acl *role, const char *filename, u_int32_t mode, int flag); int add_proc_object_acl(struct proc_acl *subject, const char *filename, u_int32_t mode, int type); void add_cap_acl(struct proc_acl *subject, const char *cap, const char *audit); void add_paxflag_acl(struct proc_acl *subject, const char *paxflag); void add_gradm_acl(struct role_acl *role); void add_gradm_pam_acl(struct role_acl *role); void add_grlearn_acl(struct role_acl *role); int add_globbed_object_acl(struct proc_acl *subject, const char *filename, u_int32_t mode, int type, const char *policy_file, unsigned long line); struct file_acl *get_exact_matching_object(struct proc_acl *subject, const char *filename); struct file_acl *get_matching_object(struct proc_acl *subject, const char *filename); void add_domain_child(struct role_acl *role, const char *idname); void change_current_acl_file(const char *filename); struct gr_arg_wrapper *conv_user_to_kernel(struct gr_pw_entry *entry); int parent_dir(const char *filename, char *parent_dirent[]); void rem_proc_object_acl(struct proc_acl *proc, struct file_acl *filp); void expand_acls(void); int test_perm(const char *obj, const char *subj); void add_res_acl(struct proc_acl *subject, const char *name, const char *soft, const char *hard); void pass_struct_to_human(FILE * stream); int is_valid_elf_binary(const char *filename); void handle_learn_logs(FILE *logfile, FILE * stream); void modify_caps(struct proc_acl *proc, int cap); void modify_res(struct proc_acl *proc, int res, unsigned long cur, unsigned long max); void add_ip_acl(struct proc_acl *subject, u_int8_t mode, struct ip_acl *tmp); void add_host_acl(struct proc_acl *subject, u_int8_t mode, const char *host, struct ip_acl *tmp); int read_saltandpass(const unsigned char *rolename, unsigned char *salt, unsigned char *pass); void add_kernel_acl(void); int add_role_acl(struct role_acl **role, const char *rolename, u_int16_t type, int ignore); u_int16_t role_mode_conv(const char *mode); u_int32_t get_ip(char *p); void conv_name_to_type(struct ip_acl *ip, const char *name); void add_role_allowed_ip(struct role_acl *role, u_int32_t addr, u_int32_t netmask); void add_role_allowed_host(struct role_acl *role, const char *host, u_int32_t netmask); void add_role_transition(struct role_acl *role, const char *rolename); void add_id_transition(struct proc_acl *subject, const char *idname, int usergroup, int allowdeny); void add_proc_nested_acl(struct role_acl *role, const char *mainsubjname, const char * const *nestednames, int nestlen, u_int32_t nestmode); void start_grlearn(char *logfile); void stop_grlearn(void); void sym_store(char *symname, struct var_object *object); struct var_object *sym_retrieve(char *symname); void add_file_var_object(struct var_object **object, const char *name, u_int32_t mode); void add_var_object(struct var_object **object, struct var_object *var); void add_net_var_object(struct var_object **object, struct ip_acl *ip, u_int8_t mode, const char *host); void add_cap_var_object(struct var_object **object, const char *name, const char *audit); void interpret_variable(struct var_object *var); struct var_object *union_objects(struct var_object *var1, struct var_object *var2); struct var_object *intersect_objects(struct var_object *var1, struct var_object *var2); struct var_object *differentiate_objects(struct var_object *var1, struct var_object *var2); void sort_file_list(struct gr_hash_struct *hash); struct gr_learn_file_node *match_file_node(struct gr_learn_file_node *base, const char *filename); struct gr_learn_file_tmp_node *conv_filename_to_struct(const char *filename, u_int32_t mode); struct gr_hash_struct *create_hash_table(int type); void match_role(struct gr_learn_group_node *grouplist, uid_t uid, gid_t gid, struct gr_learn_group_node **group, struct gr_learn_user_node **user); struct gr_learn_ip_node * find_insert_ip(struct gr_learn_ip_node **base, u_int32_t ip); void conv_mode_to_str(u_int32_t mode, char *modestr, unsigned short len); void conv_subj_mode_to_str(u_int32_t mode, char *modestr, unsigned short len); void generate_full_learned_acls(FILE *learn_log, FILE *stream); void reduce_roles(struct gr_learn_group_node **grouplist); void insert_file(struct gr_learn_file_node **base, const char *filename, u_int32_t mode, u_int8_t subj); void first_stage_reduce_tree(struct gr_learn_file_node *base); void second_stage_reduce_tree(struct gr_learn_file_node *base); void third_stage_reduce_tree(struct gr_learn_file_node *base); void traverse_roles(struct gr_learn_group_node *grouplist, int (*act)(struct gr_learn_group_node *group, struct gr_learn_user_node *user, FILE *stream), FILE *stream); void traverse_file_tree(struct gr_learn_file_node *base, int (*act)(struct gr_learn_file_node *node, const void *optarg, FILE *stream), const void *optarg, FILE *stream); void reduce_ip_tree(struct gr_learn_ip_node *base); void reduce_ports_tree(struct gr_learn_ip_node *base); void display_roles(struct gr_learn_group_node *grouplist, FILE *stream); void add_fulllearn_acl(void); void insert_ip(struct gr_learn_ip_node **base, u_int32_t ip, u_int16_t port, u_int8_t proto, u_int8_t socktype); int is_globbed_file(const char *filename); int match_filename(const char *filename, const char *pattern, unsigned int len, int is_glob); int is_protected_path(const char *filename, u_int32_t mode); int is_read_protected_path(const char *filename, u_int32_t mode); int is_write_protected_path(const char *filename, u_int32_t mode); void add_grlearn_option(u_int32_t option); struct gr_learn_role_entry * insert_learn_role(struct gr_learn_role_entry **role_list, const char *rolename, u_int16_t rolemode); void insert_learn_object(struct gr_learn_file_node *subject, struct gr_learn_file_tmp_node *object); void insert_learn_role_subject(struct gr_learn_role_entry *role, struct gr_learn_file_tmp_node *subject); void insert_learn_group_subject(struct gr_learn_group_node *role, struct gr_learn_file_tmp_node *subject); void insert_learn_user_subject(struct gr_learn_user_node *role, struct gr_learn_file_tmp_node *subject); struct gr_learn_role_entry * find_learn_role(struct gr_learn_role_entry *role_list, const char *rolename); int full_reduce_object_node(struct gr_learn_file_node *subject, const void *unused1, FILE *unused2); void conv_role_mode_to_str(u_int16_t mode, char *modestr, unsigned short len); int full_reduce_ip_node(struct gr_learn_file_node *subject, const void *unused1, FILE *unused2); void display_ip_tree(struct gr_learn_ip_node *base, u_int8_t contype, FILE *stream); int display_only_ip(struct gr_learn_ip_node *node, struct gr_learn_ip_node **unused, u_int8_t unused2, FILE *stream); void traverse_ip_tree(struct gr_learn_ip_node *base, struct gr_learn_ip_node **optarg, int (*act)(struct gr_learn_ip_node *node, struct gr_learn_ip_node **optarg, u_int8_t contype, FILE *stream), u_int8_t contype, FILE *stream); void display_tree(struct gr_learn_file_node *base, FILE *stream); void display_tree_with_role(struct gr_learn_file_node *base, const char *rolename, FILE *stream); void enforce_high_protected_paths(struct gr_learn_file_node *subject); void insert_user(struct gr_learn_group_node **grouplist, const char *username, const char *groupname, uid_t uid, gid_t gid); void add_rolelearn_acl(void); int ensure_subject_security(struct gr_learn_file_node *subject, const void *unused1, FILE *unused2); void check_acl_status(u_int16_t reqmode); struct file_acl *lookup_acl_object_by_name(struct proc_acl *subject, const char *name); struct file_acl *lookup_acl_object_by_inodev(struct proc_acl *subject, const char *name); struct file_acl *lookup_acl_object_by_inodev_nofollow(struct proc_acl *subject, const char *name); struct proc_acl *lookup_acl_subject_by_name(struct role_acl *role, const char *name); struct file_acl *lookup_acl_object(struct proc_acl *subject, struct file_acl *object); struct proc_acl *lookup_acl_subject(struct role_acl *role, struct proc_acl *subject); void * gr_alloc(size_t len); void * gr_realloc(void *addr, size_t len); void gr_free(void *addr); char * gr_strdup(const char *str); void insert_acl_object(struct proc_acl *subject, struct file_acl *object); void insert_acl_subject(struct role_acl *role, struct proc_acl *subject); void insert_nested_acl_subject(struct proc_acl *subject); const char *gr_get_user_name(uid_t uid); const char *gr_get_group_name(gid_t gid); void output_role_info(struct gr_learn_group_node *group, struct gr_learn_user_node *user, FILE *stream); void output_learn_header(FILE *stream); int display_leaf(struct gr_learn_file_node *node, const void *unused1, FILE *stream); void insert_learn_id_transition(unsigned int ***list, int real, int eff, int fs); void add_to_string_array(char ***array, const char *str); void parse_learn_config(void); void check_pam_auth(const unsigned char *rolename); void add_replace_string(const char *name, char *replacewith); char *lookup_replace_string(const char *name); char *process_string_replace(const char *str); void sort_file_node_list(struct gr_learn_file_node *root); void add_sock_family(struct proc_acl *subject, const char *family); const char *get_sock_family_from_val(int val); void set_role_umask(struct role_acl *role, u_int16_t umask); char *get_anchor(const char *filename); int anchorcmp(const char *path1, const char *path2); char *strip_trailing_slash(char *filename); int get_canonical_inodev(const char *name, u_int64_t *ino, u_int32_t *dev, int *is_symlink); void init_res_table(void); int bikeshedding_detected(void); char *get_bikeshedded_path(const char *path); #ifdef GRADM_DEBUG void check_file_node_list_integrity(struct gr_learn_file_node **filelist); void check_conformity_with_learned_rules(struct gr_learn_file_node *subject); void check_high_protected_path_enforcement(struct gr_learn_file_node *subject); #endif #endif gradm/gradm_misc.c0000644000175000017510000001600613152754421014313 0ustar spenderspender/* * Copyright (C) 2002-2014 Bradley Spengler, Open Source Security, Inc. * http://www.grsecurity.net spender@grsecurity.net * * This file is part of gradm. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "gradm.h" extern FILE *grlearn_configin; extern int grlearn_configparse(void); void check_pam_auth(const unsigned char *rolename) { struct stat fstat; int pid; if (stat(GRPAM_PATH, &fstat)) { fprintf(stderr, "PAM authentication support has been disabled " "in this install. Please reinstall gradm with PAM " "authentication support.\n"); exit(EXIT_FAILURE); } pid = fork(); if (pid == 0) { execl(GRPAM_PATH, GRPAM_PATH, rolename, (char *)NULL); exit(EXIT_FAILURE); } else if (pid > 0) { int status = 0; wait(&status); if (WIFEXITED(status) && WEXITSTATUS(status) == 0) return; } else { fprintf(stderr, "Error forking.\n"); exit(EXIT_FAILURE); } fprintf(stderr, "PAM authentication failed.\n"); exit(EXIT_FAILURE); return; } void parse_learn_config(void) { struct stat st; grlearn_configin = fopen(GR_LEARN_CONFIG_PATH, "r"); if (grlearn_configin == NULL) { fprintf(stderr, "Unable to open %s: %s\n", GR_LEARN_CONFIG_PATH, strerror(errno)); exit(EXIT_FAILURE); } grlearn_configparse(); fclose(grlearn_configin); if (high_protected_paths == NULL) { fprintf(stderr, "Invalid learn_config detected. Please use the learn_config " "provided with gradm as a starting point, otherwise learning will not " "produce desired results.\n"); exit(EXIT_FAILURE); } return; } FILE * open_acl_file(const char *filename) { FILE *aclfile; if ((aclfile = fopen(filename, "r")) == NULL) { fprintf(stderr, "Unable to open %s for reading.\n", filename); failure("fopen"); } return aclfile; } int transmit_to_kernel(struct gr_arg_wrapper *buf) { int fd; int err = 0; if ((fd = open(GRDEV_PATH, O_WRONLY)) < 0) { fprintf(stderr, "Could not open %s.\n", GRDEV_PATH); failure("open"); } if (write(fd, buf, sizeof(struct gr_arg_wrapper)) != sizeof(struct gr_arg_wrapper)) { err = 1; switch (errno) { case EFAULT: fprintf(stderr, "Error copying structures to the " "kernel.\n"); break; case ENOMEM: fprintf(stderr, "Out of memory.\n"); break; case EBUSY: fprintf(stderr, "You have attempted to authenticate " "while authentication was locked, try " "again later.\n"); break; case EAGAIN: fprintf(stderr, "Your request was ignored, " "please check the kernel logs for more " "info.\n"); break; case EPERM: if (buf->arg->mode != GRADM_UNSPROLE) fprintf(stderr, "Invalid password.\n"); else fprintf(stderr, "You are not in a special role.\n"); break; case EINVAL: default: fprintf(stderr, "You are using incompatible " "versions of gradm and grsecurity.\n" "Please update both versions to the " "ones available on the website.\n" "Make sure your gradm has been compiled " "for the kernel you are currently running.\n"); } } close(fd); ioctl(0, TIOCNXCL); if (buf->arg->mode != GRADM_DISABLE) { memset(buf->arg, 0, sizeof(struct gr_arg)); if (err) exit(EXIT_FAILURE); } else { memset(buf->arg, 0, sizeof(struct gr_arg)); if (err) exit(EXIT_FAILURE); else stop_grlearn(); } return err; } void ctrl_sighandler(int sig) { struct termios term; signal(sig, SIG_IGN); tcgetattr(STDIN_FILENO, &term); term.c_lflag |= ECHO; tcsetattr(STDIN_FILENO, TCSANOW, &term); printf("\n"); fflush(stdout); ioctl(0, TIOCNXCL); exit(EXIT_FAILURE); } void check_acl_status(u_int16_t reqmode) { int fd; int retval; struct gr_arg arg; struct gr_arg_wrapper wrapper; signal(SIGINT, ctrl_sighandler); ioctl(0, TIOCEXCL); wrapper.version = GRADM_VERSION; wrapper.size = sizeof(struct gr_arg); wrapper.arg = &arg; arg.mode = GRADM_STATUS; if ((fd = open(GRDEV_PATH, O_WRONLY)) < 0) { fprintf(stderr, "Could not open %s.\n", GRDEV_PATH); failure("open"); } retval = write(fd, &wrapper, sizeof(struct gr_arg_wrapper)); close(fd); switch (reqmode) { case GRADM_PASSSET: if (retval == 3) { printf("The terminal you are using is unsafe for this operation. Use another terminal.\n"); ioctl(0, TIOCNXCL); exit(EXIT_FAILURE); } break; case GRADM_STATUS: ioctl(0, TIOCNXCL); if (retval == 1) { printf("The RBAC system is currently enabled.\n"); exit(0); } else if (retval == 2) { printf("The RBAC system is currently disabled.\n"); exit(1); } else if (retval == 3) { printf("The terminal you are using is unsafe. Use another terminal.\n"); exit(2); } else { printf("The /dev/grsec device is not properly installed on your system or you are not using a grsecurity kernel.\n"); exit(-1); } break; case GRADM_ENABLE: ioctl(0, TIOCNXCL); if (retval == 1) { printf("The operation you requested cannot be performed " "because the RBAC system is currently enabled.\n"); exit(EXIT_FAILURE); } break; case GRADM_RELOAD: case GRADM_DISABLE: case GRADM_SPROLE: case GRADM_UNSPROLE: case GRADM_MODSEGV: if (retval == 2) { printf("The operation you requested cannot be performed " "because the RBAC system is currently disabled.\n"); ioctl(0, TIOCNXCL); exit(EXIT_FAILURE); } else if (retval == 3 && reqmode != GRADM_UNSPROLE) { printf("The terminal you are using is unsafe for this operation. Use another terminal.\n"); ioctl(0, TIOCNXCL); exit(EXIT_FAILURE); } break; } return; } void init_variables(void) { extern struct ip_acl ip; lineno = 1; current_acl_file = NULL; current_role = NULL; current_subject = NULL; num_roles = 0; num_subjects = 0; num_objects = 0; num_pointers = 0; dont_reduce_dirs = NULL; always_reduce_dirs = NULL; protected_paths = NULL; read_protected_paths = NULL; high_reduce_dirs = NULL; high_protected_paths = NULL; add_to_string_array(&high_protected_paths, GRSEC_DIR); add_to_string_array(&high_protected_paths, GRDEV_PATH); memset(&ip, 0, sizeof (ip)); return; } void change_current_acl_file(const char *filename) { char *p; if ((p = (char *) calloc(strlen(filename) + 1, sizeof (char))) == NULL) failure("calloc"); strcpy(p, filename); current_acl_file = p; return; } int parent_dir(const char *filename, char *parent_dirent[]) { int i; if ((strlen(*parent_dirent) <= 1) || (strlen(filename) <= 1)) return 0; for (i = strlen(*parent_dirent) - 1; i >= 0; i--) { if (i) (*parent_dirent)[i] = '\0'; if (filename[i] == '/') return 1; } return 0; } gradm/policy0000644000175000017510000004034713152754421013271 0ustar spenderspender#sample default policy for grsecurity # # Role flags: # A -> This role is an administrative role, thus it has special privilege normal # roles do not have. In particular, this role bypasses the # additional ptrace restrictions # N -> Don't require authentication for this role. To access # the role, use gradm -n # s -> This role is a special role, meaning it does not belong to a # user or group, and does not require an enforced secure policy # base to be included in the ruleset # u -> This role is a user role # g -> This role is a group role # G -> This role can use gradm to authenticate to the kernel # A policy for gradm will automatically be added to the role # T -> Enable TPE for this role # l -> Enable learning for this role # P -> Use PAM authentication for this role. # R -> Enable persistence of special role. Normal special roles will # be removed upon exit of the process that entered the role, or # upon unauth (this is what changes the apache process' role back # to its normal role after being restarted from the admin role, for # instance). Role persistence allows a special role to be used for # system shutdown, as the point at which the admin's shell/SSH # session is terminated won't cause the rest of the shutdown # sequence to execute with reduced privilege. Do *NOT* use this # flag with any role that does anything but shut the system down. # This role will also be transferred to the init process upon # writing to /dev/initctl. This allows init to execute the rc # scripts for shutdown with the necessary privilege. # For usability reasons, we allow the removal of persistence through # the normal unauth process (so persistence only survives exit). # # a role can only be one of user, group, or special # # role_allow_ip IP/optional netmask # eg: role_allow_ip 192.168.1.0/24 # You can have as many of these per role as you want # They restrict the use of a role to a list of IPs. If a user # is on the system that would normally get the role does not # belong to those lists of IPs, the system falls back through # its method of determining a role for the user # # Role hierarchy # user -> group -> default # First a user role attempts to match, if one is not found, # a group role attempts to match, if one is not found, # the default role is used. # # role_transitions ... # eg: role_transitions www_admin dns_admin # # role transitions specify which special roles a given role is allowed # to authenticate to. This applies to special roles that do not # require password authentication as well. If a user tries to # authenticate to a role that is not within his transition table, he # will receive a permission denied error # # Nested subjects # subject /bin/su:/bin/bash:/bin/cat # / rwx # +CAP_ALL # grant privilege to specific processes if they are executed # within a trusted path. In this case, privilege is # granted if /bin/cat is executed from /bin/bash, which is # executed from /bin/su. # # Configuration inheritance on nested subjects # nested subjects inherit rules from their parents. In the # example above, the nested subject would inherit rules # from the nested subject for /bin/su:/bin/bash, # and the subject /bin/su # View the 1.9.x documentation for more information on # configuration inheritance # # new object modes: # m -> allow creation of setuid/setgid files/directories # and modification of files/directories to be setuid/setgid # M -> audit the setuid/setgid creation/modification # c -> allow creation of the file/directory # C -> audit the creation # d -> allow deletion of the file/directory # D -> audit the deletion # p -> reject all ptraces to this object # l -> allow a hardlink at this path # (hardlinking requires at a minimum c and l modes, and the target # link cannot have any greater permission than the source file) # L -> audit link creation # f -> needed to mark the pipe used for communication with init # to transfer the privilege of the persistent role; only valid # within a persistent role. Transfer only occurs when the file is # opened for writing # Z -> tells gradm to ignore earlier object of the same name and use this # one instead # # new subject modes: # O -> disable "writable library" restrictions for this task # t -> allow this process to ptrace any process (use with caution) # r -> relax ptrace restrictions (allows process to ptrace processes # other than its own descendants) # i -> enable inheritance-based learning for this subject, causing # all accesses of this subject and anything it executes to be placed # in this subject, and inheritance flags added to executable objects # in this subject # a -> allow this process to talk to the /dev/grsec device # s -> enable AT_SECURE when entering this subject # (enables the same environment sanitization that occurs in glibc # upon execution of a suid binary) # x -> allows executable anonymous shared memory for this subject # Z -> tells gradm to ignore earlier subject of the same path and use this # one instead # user/group transitions: # You may now specify what users and groups a given subject can # transition to. This can be done on an inclusive or exclusive basis. # Omitting these rules allows a process with proper privilege granted by # capabilities to transition to any user/group. # # Examples: # subject /bin/su # user_transition_allow root spender # group_transition_allow root spender # subject /bin/su # user_transition_deny evilhacker # subject /bin/su # group_transition_deny evilhacker1 evilhacker2 # # Domains: # With domains you can combine users that don't share a common # GID as well as groups so that they share a single policy # Domains work just like roles, with the only exception being that # the line starting with "role" is replaced with one of the following: # domain somedomainname u user1 user2 user3 user4 ... usern # domain somedomainname g group1 group2 group3 group4 ... groupn # # Inverted socket policies: # Rules such as # connect ! www.google.com:80 stream tcp # are now allowed, which allows you to specify that a process can connect to anything # except to port 80 of www.google.com with a stream tcp socket # the inverted socket matching also works on bind rules # # INADDR_ANY overriding # You can now force a given subject to bind to a particular IP address on the machine # This is useful for some chrooted environments, to ensure that the source IP they # use is one of your choosing # to use, add a line like: # ip_override 192.168.0.1 # # Per-interface socket policies: # Rules such as # bind eth1:80 stream tcp # bind eth0#1:22 stream tcp # are now allowed, giving you the ability to tie specific socket rules # to a single interface (or by using the inverted rules, all but one # interface). Virtual interfaces are specified by the # # syntax. If an interface is specified, no IP/netmask or host may be # specified for the rule. # # Allowing additional socket families: # Before v2.2.1 of the RBAC system, a subject that specified # connect/bind rules limited only the socket usage of IPv4, allowing # any other socket families to be used. Starting with v2.2.1 of the # RBAC system, when connect/bind rules are used, additional rules # will be required to unlock the use of additional socket families # (outside of the common unix family). Multiple families can be # specified per line. # To enable use of IPv6, add the line: # sock_allow_family ipv6 # To enable use of netlink, add the line: # sock_allow_family netlink # To enable all other families, add the line: # sock_allow_family all # # New learning system: # To learn on a given subject: add l (the letter l, not the number 1) # to the subject mode # If you want to learn with the most restrictive policy, use the # following: # subject /path/to/bin lo # / h # -CAP_ALL # connect disabled # bind disabled # Resource learning is also supported, so lines like # RES_AS 0 0 # can be used to learn a particular resource # # To learn on a given role, add l to the role mode # For both of these, to enable learning, enable the system like: # gradm -L /etc/grsec/learning.logs -E # and then generate the rules after disabling the system after the # learning phase with: # gradm -L /etc/grsec/learning.logs -O /etc/grsec/policy # To use full system learning, enable the system like: # gradm -F -L /etc/grsec/learning.logs # and then generate the rules after disabling the system after the # learning phase with: # gradm -F -L /etc/grsec/learning.logs -O /etc/grsec/policy # # New PaX flag format (replaces PaX subject flags): # PaX flags can be forced on or off, regardless of the flags on the # binary, by using + or - before the following PaX flag names: # PAX_SEGMEXEC # PAX_PAGEEXEC # PAX_MPROTECT # PAX_RANDMMAP # PAX_EMUTRAMP # # New feature for easier policy maintenance: # replace # e.g.: # replace CVSROOT /home/cvs # now $(CVSROOT) can be used in any subject or object pathname, like: # $(CVSROOT)/grsecurity r # This will translate to /home/cvs/grsecurity r # This feature makes it easier to update policies by naming specific # paths by their function, then only having to update those paths once # to have it affect a large number of subjects/objects. # # capability auditing / log suppression # use of a capability can be audited by adding "audit" to the line, eg: # +CAP_SYS_RAWIO audit # log suppression for denial of a capbility can be done by adding "suppress": # -CAP_SYS_RAWIO suppress # # Per-role umask enforcement: # If you have a user that you want to be assured cannot accidentally # create a file that others can read (a confidentiality issue) # add the following under the role declaration: # role_umask 077 # any normal octal umask may be specified # Note that unlike the normal umask, this umask will also apply # to the permissions one can chmod/fchmod a file to # # Note that the omission of any feature of a role or subject # results in a default-allow # For instance, if no capability rules are added in a subject without # policy inheritance ("o" in subject mode), an implicit +CAP_ALL is used # # Also note that policy inheritance does not exist for network policies, only # file objects and capabilities inherit policy # # Commonly-used objects can be defined and used in multiple subjects # As an example, we'll create a variable out of a list of objects # and their associated permissions that RBAC enforces # files, connect/bind rules, and capabilities can currently be added to a define define grsec_denied { /boot h /dev/grsec h /dev/kmem h /dev/mem h /dev/port h /etc/grsec h /proc/kcore h /proc/slabinfo h /proc/modules h /proc/kallsyms h # hide and suppress logs about accessing this path /lib/modules hs /lib32/modules hs /lib64/modules hs /etc/ssh h } # usage: # $grsec_denied role shutdown sARG subject / rvka / /dev /dev/urandom r /dev/random r /etc r /bin rx /sbin rx /lib rx /lib32 rx /libx32 rx /lib64 rx /usr rx /proc r $grsec_denied -CAP_ALL connect disabled bind disabled subject /sbin/init rvkao / rwcdmlxi subject /sbin/halt rvkao / rwcdmlxi /dev/initctl rwf /run/initctl rwf /run/systemd/initctl/fifo rwf subject /sbin/shutdown rvkao / rwcdmlxi /dev/initctl rwf /run/initctl rwf /run/systemd/initctl/fifo rwf # Make sure to unauthenticate with gradm -u from # the admin role after restarting a service # The service started will run with admin # privileges until you run gradm -u or your shell exits role admin sA subject / rvka / rwcdmlxi role default G role_transitions admin shutdown subject / / r /opt rx /home rwxcd /mnt rw /dev /dev/urandom r /dev/random r /dev/zero rw /dev/input rw /dev/psaux rw /dev/null rw /dev/tty? rw /dev/console rw /dev/tty rw /dev/pts rw /dev/ptmx rw /dev/dsp rw /dev/mixer rw /dev/initctl rw /run/systemd/initctl/fifo rw /dev/fd0 r /dev/cdrom r /dev/sr0 r /bin rx /sbin rx /lib rx /lib32 rx /libx32 rx /lib64 rx /usr rx # compilation of kernel code should be done within the admin role /usr/src h /etc rx /proc rwx /proc/sys r /sys h /root r /run r /tmp rwcd /var rwxcd /var/tmp rwcd /var/log r # hide the kernel images and modules $grsec_denied # if sshd needs to be restarted, it can be done through the admin role # restarting sshd should be followed immediately by a gradm -u /usr/sbin/sshd -CAP_KILL -CAP_SYS_TTY_CONFIG -CAP_LINUX_IMMUTABLE -CAP_NET_RAW -CAP_MKNOD -CAP_SYS_ADMIN -CAP_SYS_RAWIO -CAP_SYS_MODULE -CAP_SYS_PTRACE -CAP_NET_ADMIN -CAP_NET_BIND_SERVICE -CAP_NET_RAW -CAP_SYS_CHROOT -CAP_SYS_BOOT -CAP_SETFCAP -CAP_SYSLOG # RES_AS 100M 100M # connect 192.168.1.0/24:22 stream tcp # bind 0.0.0.0 stream dgram tcp udp # the d flag protects /proc fd and mem entries for sshd # all daemons should have 'p' in their subject mode to prevent # an attacker from killing the service (and restarting it with trojaned # config file or taking the port it reserved to run a trojaned service) subject /usr/sbin/sshd dpo / /* h /bin/bash x /dev h /dev/log rw /run/systemd/journal/dev-log rw /dev/random r /dev/urandom r /dev/null rw /dev/ptmx rw /dev/pts rw /dev/tty rw /dev/tty? rw /etc r /etc/grsec h /home /home/*/.ssh/authorized_keys r /home/*/.ssh/authorized_keys2 r /lib rx /lib32 rx /libx32 rx /lib64 rx /root /proc r /proc/*/oom_adj rw /proc/*/oom_score_adj rw /proc/kcore h /proc/sys h /proc/sys/kernel/ngroups_max r /selinux r /usr/lib rx /usr/lib32 rx /usr/libx32 rx /usr/lib64 rx /usr/share/zoneinfo r /var/log /var/mail /var/log/lastlog rw /var/log/wtmp w /var/run /run /var/run/sshd /var/run/utmp rw /var/run/utmpx rw /var/run/.nscd_socket rw -CAP_ALL +CAP_CHOWN +CAP_SETGID +CAP_SETUID +CAP_SYS_CHROOT +CAP_SYS_RESOURCE +CAP_SYS_TTY_CONFIG +CAP_AUDIT_WRITE +CAP_KILL # to access user keys +CAP_DAC_OVERRIDE subject /usr/X11R6/bin/Xorg /dev/mem rw +CAP_SYS_ADMIN +CAP_SYS_TTY_CONFIG +CAP_SYS_RAWIO subject /usr/lib/xorg/Xorg /dev/mem rw +CAP_SYS_ADMIN +CAP_SYS_TTY_CONFIG +CAP_SYS_RAWIO subject /usr/X11R6/bin/XFree86 /dev/mem rw +CAP_SYS_ADMIN +CAP_SYS_TTY_CONFIG +CAP_SYS_RAWIO -PAX_SEGMEXEC -PAX_PAGEEXEC -PAX_MPROTECT subject /usr/bin/ssh /etc/ssh/ssh_config r subject /usr/bin/postgres /dev/log rw /run/systemd/journal/dev-log rw subject /usr/bin/exim /dev/log rw /run/systemd/journal/dev-log rw subject /sbin/klogd +CAP_SYS_ADMIN subject /sbin/syslog-ng +CAP_SYS_ADMIN subject /usr/sbin/rsyslogd +CAP_SYS_ADMIN subject /usr/sbin/cron /dev/log rw /run/systemd/journal/dev-log rw subject /usr/sbin/crond /dev/log rw /run/systemd/journal/dev-log rw subject /bin/login /dev/log rw /run/systemd/journal/dev-log rw /var/log/wtmp w /var/log/faillog rwcd subject /bin/su /dev/log rw /run/systemd/journal/dev-log rw subject /usr/bin/sudo /dev/log rw /run/systemd/journal/dev-log rw subject /sbin/getty /var/log/wtmp w # /sbin/init is usually a symlink to /lib/systemd/systemd # so no need to add a subject explicitly for systemd subject /sbin/init /var/log/wtmp w subject /usr/bin/xauth /home r /home/*/.Xauthority-* rwcdl # systemd-specific policies subject /lib/systemd/systemd-journald /run/log/journal rwcd /run/systemd/journal rwcd /etc/localtime r /sys/fs/cgroup/systemd r /proc r +CAP_SYS_ADMIN +CAP_SYS_PTRACE connect disabled bind disabled subject /lib/systemd/systemd-timesyncd /run/systemd/notify rw connect disabled bind disabled subject /lib/systemd/systemd-logind /run/systemd/notify rw connect disabled bind disabled subject /lib/systemd/systemd-networkd /run/systemd/notify rw +CAP_NET_ADMIN connect disabled bind disabled subject /lib/systemd/systemd-udevd /run/systemd/notify rw +CAP_NET_ADMIN connect disabled bind disabled subject /usr/lib/NetworkManager/nm-dhcp-helper /run/NetworkManager/private-dhcp rw subject /usr/sbin/NetworkManager /run/systemd/journal/socket rw /run h /run/dhclient* rwcd connect disabled bind disabled # prevent ld.so breakouts of subjects with /lib rx # many distros clutter up /lib with shell scripts # that can be easily hijacked for malicious purposes subject /lib o / h -CAP_ALL connect disabled bind disabled subject /lib/ld-linux.so.2 o / h -CAP_ALL connect disabled bind disabled subject /lib64/ld-linux-x86-64.so.2 o / h -CAP_ALL connect disabled bind disabled gradm/gradm_defs.h0000644000175000017510000003221413152754421014305 0ustar spenderspender#ifndef __GRADM_DEFS_H #define __GRADM_DEFS_H #ifndef GRSEC_DIR #define GRSEC_DIR "/etc/grsec" #endif #define GRLEARN_PATH "/sbin/grlearn" #define GRADM_PATH "/sbin/gradm" #define GRPAM_PATH "/sbin/gradm_pam" #define GRDEV_PATH "/dev/grsec" #define GR_POLICY_PATH GRSEC_DIR "/policy" #define GR_PW_PATH GRSEC_DIR "/pw" #define GR_LEARN_CONFIG_PATH GRSEC_DIR "/learn_config" #define GR_LEARN_PIPE_PATH GRSEC_DIR "/.grlearn.pipe" #define GR_LEARN_PID_PATH GRSEC_DIR "/.grlearn.pid" #define GR_VERSION "3.1" #define GRADM_VERSION 0x3100 #define LEARN_LOG_BUFFER_SIZE (16 * 1024 * 1024) #define GR_PWONLY 0 #define GR_PWANDSUM 1 #define GR_PW_LEN 128 #define GR_SALT_SIZE 16 #define GR_SHA_SUM_SIZE 32 #define GR_SPROLE_LEN 64 #define GR_FEXIST 0x1 #define GR_FFAKE 0x2 #define GR_FLEARN 0x4 #define CHK_FILE 0 #define CHK_CAP 1 #undef PATH_MAX #define PATH_MAX 4096 #define MAX_LINE_LEN 5000 // CAP_AUDIT_READ #define CAP_MAX 37 #define MAX_INCLUDE_DEPTH 20 #define MAX_NEST_DEPTH 8 #define MAX_SYMLINK_DEPTH 8 #ifndef RLIMIT_LOCKS #define RLIMIT_LOCKS 10 #endif #ifndef RLIMIT_SIGPENDING #define RLIMIT_SIGPENDING 11 #endif #ifndef RLIMIT_MSGQUEUE #define RLIMIT_MSGQUEUE 12 #endif #ifndef RLIMIT_NICE #define RLIMIT_NICE 13 #endif #ifndef RLIMIT_RTPRIO #define RLIMIT_RTPRIO 14 #endif #ifndef RLIMIT_RTTIME #define RLIMIT_RTTIME 15 #endif #ifndef AF_RDS #define AF_RDS 21 #endif #ifndef AF_SNA #define AF_SNA 22 #endif #ifndef AF_IRDA #define AF_IRDA 23 #endif #ifndef AF_PPOX #define AF_PPOX 24 #endif #ifndef AF_WANPIPE #define AF_WANPIPE 25 #endif #ifndef AF_LLC #define AF_LLC 26 #endif #ifndef AF_IB #define AF_IB 27 #endif #ifndef AF_MPLS #define AF_MPLS 28 #endif #ifndef AF_CAN #define AF_CAN 29 #endif #ifndef AF_TIPC #define AF_TIPC 30 #endif #ifndef AF_BLUETOOTH #define AF_BLUETOOTH 31 #endif #ifndef AF_IUCV #define AF_IUCV 32 #endif #ifndef AF_RXRPC #define AF_RXRPC 33 #endif #ifndef AF_ISDN #define AF_ISDN 34 #endif #ifndef AF_PHONET #define AF_PHONET 35 #endif #ifndef AF_IEEE802154 #define AF_IEEE802154 36 #endif #ifndef AF_CAIF #define AF_CAIF 37 #endif #ifndef AF_ALG #define AF_ALG 38 #endif #ifndef AF_NFC #define AF_NFC 39 #endif #ifndef AF_VSOCK #define AF_VSOCK 40 #endif #ifndef AF_KCM #define AF_KCM 41 #endif #ifndef AF_QIPCRTR #define AF_QIPCRTR 42 #endif #ifndef AF_SMC #define AF_SMC 43 #endif #undef AF_MAX #define AF_MAX 44 #define GR_NLIMITS 32 #define GR_CRASH_RES 31 #undef CAP_TO_INDEX #undef CAP_TO_MASK #undef cap_raise #undef cap_lower #undef cap_raised #define CAP_TO_INDEX(x) ((x) >> 5) /* 1 << 5 == bits in __u32 */ #define CAP_TO_MASK(x) (1U << ((x) & 31)) /* mask for indexed __u32 */ #define cap_raise(c, flag) ((c).cap[CAP_TO_INDEX(flag)] |= CAP_TO_MASK(flag)) #define cap_lower(c, flag) ((c).cap[CAP_TO_INDEX(flag)] &= ~CAP_TO_MASK(flag)) #define cap_raised(c, flag) ((c).cap[CAP_TO_INDEX(flag)] & CAP_TO_MASK(flag)) #define CAP_SETUID 7 #define CAP_SETGID 6 enum { GRADM_DISABLE = 0, GRADM_ENABLE = 1, GRADM_SPROLE = 2, GRADM_OLDRELOAD = 3, GRADM_MODSEGV = 4, GRADM_STATUS = 5, GRADM_UNSPROLE = 6, GRADM_PASSSET = 7, GRADM_SPROLEPAM = 8, GRADM_RELOAD = 9 }; enum { GR_IP_BIND = 0x01, GR_IP_CONNECT = 0x02, GR_IP_INVERT = 0x04, GR_SOCK_FAMILY = 0x20 }; enum { GR_ID_USER = 0x01, GR_ID_GROUP = 0x02, }; enum { GR_ID_ALLOW = 0x01, GR_ID_DENY = 0x02, }; enum { GR_READ = 0x00000001, GR_APPEND = 0x00000002, GR_WRITE = 0x00000004, GR_EXEC = 0x00000008, GR_FIND = 0x00000010, GR_INHERIT = 0x00000020, GR_SETID = 0x00000040, GR_CREATE = 0x00000080, GR_DELETE = 0x00000100, GR_LINK = 0x00000200, GR_AUDIT_READ = 0x00000400, GR_AUDIT_APPEND = 0x00000800, GR_AUDIT_WRITE = 0x00001000, GR_AUDIT_EXEC = 0x00002000, GR_AUDIT_FIND = 0x00004000, GR_AUDIT_INHERIT= 0x00008000, GR_AUDIT_SETID = 0x00010000, GR_AUDIT_CREATE = 0x00020000, GR_AUDIT_DELETE = 0x00040000, GR_AUDIT_LINK = 0x00080000, GR_PTRACERD = 0x00100000, GR_NOPTRACE = 0x00200000, GR_SUPPRESS = 0x00400000, GR_NOLEARN = 0x00800000, GR_INIT_TRANSFER= 0x01000000, GR_OBJ_REPLACE = 0x02000000 }; enum { GR_ROLE_USER = 0x0001, GR_ROLE_GROUP = 0x0002, GR_ROLE_DEFAULT = 0x0004, GR_ROLE_SPECIAL = 0x0008, GR_ROLE_AUTH = 0x0010, GR_ROLE_NOPW = 0x0020, GR_ROLE_GOD = 0x0040, GR_ROLE_LEARN = 0x0080, GR_ROLE_TPE = 0x0100, GR_ROLE_DOMAIN = 0x0200, GR_ROLE_PAM = 0x0400, GR_ROLE_PERSIST = 0x0800, GR_ROLE_ISID = 0x1000, GR_ROLE_IGNORENOEXIST = 0x8000 }; enum { GR_DELETED = 0x80000000 }; enum { GR_KILL = 0x00000001, GR_VIEW = 0x00000002, GR_PROTECTED = 0x00000004, GR_LEARN = 0x00000008, GR_IGNORE = 0x00000010, GR_OVERRIDE = 0x00000020, GR_PROTSHM = 0x00000040, GR_KILLPROC = 0x00000080, GR_KILLIPPROC = 0x00000100, GR_NOTROJAN = 0x00000200, GR_PROTPROCFD = 0x00000400, GR_PROCACCT = 0x00000800, GR_RELAXPTRACE = 0x00001000, GR_INHERITLEARN = 0x00004000, GR_PROCFIND = 0x00008000, GR_POVERRIDE = 0x00010000, GR_KERNELAUTH = 0x00020000, GR_ATSECURE = 0x00040000, GR_SHMEXEC = 0x00080000, GR_GLOBANCHOR = 0x00100000, GR_SUBJ_REPLACE = 0x00200000 }; enum { GR_DONT_LEARN_ALLOWED_IPS = 0x00000001, GR_SPLIT_ROLES = 0x00000002 }; /* internal use only. not to be modified */ typedef struct _gr_rlimit_t { unsigned long rlim_cur; unsigned long rlim_max; } gr_rlimit_t; typedef struct _gr_cap_t { u_int32_t cap[2]; } gr_cap_t; struct capability_set { const char *cap_name; int cap_val; }; struct family_set { const char *family_name; int family_val; }; struct paxflag_set { const char *paxflag_name; u_int16_t paxflag_val; }; struct rlimconv { const char *name; unsigned short val; }; struct chk_perm { unsigned short type; u_int32_t w_modes; u_int32_t u_modes; gr_cap_t w_caps; gr_cap_t u_caps; }; struct role_allowed_ip { u_int32_t addr; u_int32_t netmask; struct role_allowed_ip *prev; struct role_allowed_ip *next; }; struct ip_acl { char *iface; u_int32_t addr; u_int32_t netmask; u_int16_t low, high; u_int8_t mode; // connect or bind u_int32_t type; // stream, dgram, raw..etc u_int32_t proto[8]; // we have to support all 255 protocols struct ip_acl *prev; struct ip_acl *next; }; struct file_acl { const char *filename; u_int64_t inode; u_int32_t dev; u_int32_t mode; struct proc_acl *nested; struct file_acl *globbed; struct file_acl *prev; struct file_acl *next; }; enum { VAR_FILE_OBJECT = 0, VAR_NET_OBJECT = 1, VAR_CAP_OBJECT = 2 }; struct var_object { union { struct { const char *filename; u_int32_t mode; } file_obj; struct { struct ip_acl ip; u_int8_t mode; const char *host; } net_obj; struct { const char *cap; const char *audit; } cap_obj; }; unsigned int type; struct var_object *prev; struct var_object *next; }; struct role_transition { const char *rolename; struct role_transition *prev; struct role_transition *next; }; struct role_acl { const char *rolename; uid_t uidgid; u_int16_t roletype; u_int16_t auth_attempts; unsigned long expires; struct proc_acl *root_label; struct gr_hash_struct *hash; struct role_acl *prev; struct role_acl *next; struct role_transition *transitions; struct role_allowed_ip *allowed_ips; uid_t *domain_children; u_int16_t domain_child_num; u_int16_t umask; struct proc_acl **subj_hash; u_int32_t subj_hash_size; }; struct proc_acl { const char *filename; u_int64_t inode; u_int32_t dev; u_int32_t mode; gr_cap_t cap_mask; gr_cap_t cap_drop; gr_cap_t cap_invert_audit; gr_rlimit_t res[GR_NLIMITS]; u_int32_t resmask; u_int8_t user_trans_type; u_int8_t group_trans_type; uid_t *user_transitions; gid_t *group_transitions; u_int16_t user_trans_num; u_int16_t group_trans_num; u_int32_t sock_families[2]; u_int32_t ip_proto[8]; u_int32_t ip_type; struct ip_acl **ips; u_int32_t ip_num; u_int32_t inaddr_any_override; u_int32_t crashes; unsigned long expires; struct proc_acl *parent_subject; struct gr_hash_struct *hash; struct proc_acl *prev; struct proc_acl *next; struct file_acl **obj_hash; u_int32_t obj_hash_size; u_int16_t pax_flags; }; struct gr_learn_ip_node { struct gr_learn_ip_node *prev; struct gr_learn_ip_node *next; u_int8_t ip_node; u_int16_t **ports; u_int32_t ip_proto[8]; u_int32_t ip_type; unsigned char root_node:1; unsigned char all_low_ports:1; unsigned char all_high_ports:1; struct gr_learn_ip_node *parent; struct gr_learn_ip_node *leaves; }; struct gr_learn_role_entry { struct gr_learn_role_entry *prev; struct gr_learn_role_entry *next; const char *rolename; u_int16_t rolemode; unsigned int id; struct gr_hash_struct *hash; struct gr_learn_file_node *subject_list; struct gr_learn_ip_node *allowed_ips; }; struct gr_learn_group_node { struct gr_learn_group_node *prev; struct gr_learn_group_node *next; char *rolename; gid_t gid; struct gr_learn_user_node *users; struct gr_hash_struct *hash; struct gr_learn_file_node *subject_list; struct gr_learn_ip_node *allowed_ips; }; struct gr_learn_file_tmp_node { char *filename; u_int32_t key; u_int32_t mode; }; struct gr_learn_user_node { struct gr_learn_user_node *prev; struct gr_learn_user_node *next; char *rolename; uid_t uid; int multgroups; struct gr_learn_group_node *group; struct gr_hash_struct *hash; struct gr_learn_file_node *subject_list; struct gr_learn_ip_node *allowed_ips; }; struct gr_learn_subject_node { gr_cap_t cap_raise; gr_rlimit_t res[GR_NLIMITS]; u_int32_t resmask; u_int16_t pax_flags; u_int32_t inaddr_any_override; u_int32_t sock_families[2]; }; struct gr_learn_file_node { struct gr_learn_file_node *prev; struct gr_learn_file_node *next; char *filename; u_int32_t mode; struct gr_learn_file_node *leaves; struct gr_learn_file_node *parent; struct gr_hash_struct *hash; struct gr_learn_file_node *object_list; struct gr_learn_ip_node *connect_list; struct gr_learn_ip_node *bind_list; unsigned int **user_trans_list; unsigned int **group_trans_list; struct gr_learn_subject_node *subject; unsigned char dont_display:1; }; struct gr_pw_entry { unsigned char rolename[GR_SPROLE_LEN]; unsigned char passwd[GR_PW_LEN]; unsigned char sum[GR_SHA_SUM_SIZE]; unsigned char salt[GR_SALT_SIZE]; u_int32_t segv_dev; u_int64_t segv_inode; uid_t segv_uid; u_int16_t mode; }; /* We use this to keep track of deleted files, since each subject needs to agree on an inode/dev */ struct deleted_file { const char *filename; u_int64_t ino; struct deleted_file *next; }; /* to keep track of symlinks, for processing after all other objects have been added */ struct symlink { struct role_acl *role; struct file_acl *obj; struct proc_acl *subj; const char *policy_file; unsigned long lineno; struct symlink *next; }; /* to keep track of globbed files, so that the ordering of their anchor doesn't matter */ struct glob_file { struct role_acl *role; struct proc_acl *subj; const char *filename; u_int32_t mode; int type; const char *policy_file; unsigned long lineno; struct glob_file *next; }; extern struct glob_file *glob_files_head; extern struct glob_file *glob_files_tail; extern struct symlink *symlinks; extern struct proc_acl *global_nested_subject_list; extern struct deleted_file *deleted_files; extern unsigned long lineno; extern struct role_acl *current_role; extern struct proc_acl *current_subject; extern char *current_acl_file; enum { GR_HASH_SUBJECT, GR_HASH_OBJECT, GR_HASH_FILENAME }; struct gr_hash_struct { void **table; void **nametable; void *first; u_int32_t table_size; u_int32_t used_size; int type; }; struct user_acl_role_db { struct role_acl **r_table; u_int32_t num_pointers; /* Number of allocations to track */ u_int32_t num_roles; /* Number of roles */ u_int32_t num_domain_children; /* Number of domain children */ u_int32_t num_subjects; /* Number of subjects */ u_int32_t num_objects; /* Number of objects */ }; struct sprole_pw { const unsigned char *rolename; unsigned char salt[GR_SALT_SIZE]; unsigned char sum[GR_SHA_SUM_SIZE]; }; struct gr_arg { struct user_acl_role_db role_db; unsigned char pw[GR_PW_LEN]; unsigned char salt[GR_SALT_SIZE]; unsigned char sum[GR_SHA_SUM_SIZE]; unsigned char sp_role[GR_SPROLE_LEN]; struct sprole_pw *sprole_pws; u_int32_t segv_dev; u_int64_t segv_inode; uid_t segv_uid; u_int16_t num_sprole_pws; u_int16_t mode; }; struct gr_arg_wrapper { struct gr_arg *arg; u_int32_t version; u_int32_t size; }; extern const char *rlim_table[GR_NLIMITS]; extern struct capability_set capability_list[CAP_MAX+2]; extern struct paxflag_set paxflag_list[5]; /* 5 extra = local, ipv4, ipv6, route, all */ //extern struct family_set sock_families[AF_MAX+5]; extern int is_24_kernel; extern uid_t special_role_uid; extern u_int32_t num_subjects; extern u_int32_t num_roles; extern u_int32_t num_objects; extern u_int32_t num_pointers; extern u_int32_t num_domain_children; extern char *current_learn_rolename; extern char *current_learn_subject; extern u_int16_t current_learn_rolemode; extern char ** dont_reduce_dirs; extern char ** always_reduce_dirs; extern char ** protected_paths; extern char ** read_protected_paths; extern char ** high_reduce_dirs; extern char ** high_protected_paths; extern u_int32_t grlearn_options; extern int gr_learn; extern int gr_fulllearn; extern char *output_log; extern char *learn_log_buffer; #endif gradm/gradm_fulllearn.c0000644000175000017510000003736413152754421015356 0ustar spenderspender/* * Copyright (C) 2002-2014 Bradley Spengler, Open Source Security, Inc. * http://www.grsecurity.net spender@grsecurity.net * * This file is part of gradm. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "gradm.h" extern struct gr_learn_file_node **cachednode; extern unsigned int cachedlen; struct gr_learn_group_node *the_role_list = NULL; extern FILE *fulllearn_pass1in; extern FILE *fulllearn_pass2in; extern FILE *fulllearn_pass3in; extern int fulllearn_pass1parse(void); extern int fulllearn_pass2parse(void); extern int fulllearn_pass3parse(void); void fulllearn_pass1(FILE *stream) { fulllearn_pass1in = stream; printf("Beginning full learning 1st pass..."); fflush(stdout); fulllearn_pass1parse(); printf("done.\n"); fflush(stdout); printf("Beginning full learning role reduction..."); fflush(stdout); reduce_roles(&the_role_list); printf("done.\n"); fflush(stdout); return; } int full_reduce_subjects(struct gr_learn_group_node *group, struct gr_learn_user_node *user, FILE *unused) { struct gr_learn_file_tmp_node **tmptable; unsigned long i; u_int32_t table_size; if (user) { printf("Beginning full learning subject reduction for user %s...", user->rolename); fflush(stdout); if (!user->hash) insert_file(&(user->subject_list), "/", GR_PROCFIND, 1); else { sort_file_list(user->hash); tmptable = (struct gr_learn_file_tmp_node **)user->hash->table; table_size = user->hash->table_size; for (i = 0; i < table_size; i++) { if (tmptable[i] == NULL) continue; insert_file(&(user->subject_list), tmptable[i]->filename, tmptable[i]->mode, 1); } } printf("done.\n"); fflush(stdout); } else { printf("Beginning full learning subject reduction for group %s...", group->rolename); fflush(stdout); if (!group->hash) insert_file(&(group->subject_list), "/", GR_PROCFIND, 1); else { sort_file_list(group->hash); tmptable = (struct gr_learn_file_tmp_node **)group->hash->table; table_size = group->hash->table_size; for (i = 0; i < table_size; i++) { if (tmptable[i] == NULL) continue; insert_file(&(group->subject_list), tmptable[i]->filename, tmptable[i]->mode, 1); } } printf("done.\n"); fflush(stdout); } return 0; } int full_reduce_allowed_ips(struct gr_learn_group_node *group, struct gr_learn_user_node *user, FILE *unused) { if (user) reduce_ip_tree(user->allowed_ips); else if (group) reduce_ip_tree(group->allowed_ips); return 0; } void fulllearn_pass2(FILE *stream) { fulllearn_pass2in = stream; printf("Beginning full learning 2nd pass..."); fflush(stdout); fulllearn_pass2parse(); printf("done.\n"); fflush(stdout); traverse_roles(the_role_list, &full_reduce_subjects, NULL); traverse_roles(the_role_list, &full_reduce_allowed_ips, NULL); return; } int full_reduce_object_node(struct gr_learn_file_node *subject, const void *unused1, FILE *unused2) { struct gr_learn_file_tmp_node **tmptable; unsigned long i; u_int32_t table_size; if (subject->hash == NULL) return 0; printf("Beginning full learning object reduction for subject %s...", subject->filename); fflush(stdout); sort_file_list(subject->hash); tmptable = (struct gr_learn_file_tmp_node **)subject->hash->table; table_size = subject->hash->table_size; for (i = 0; i < table_size; i++) { if (tmptable[i] == NULL) continue; insert_file(&(subject->object_list), tmptable[i]->filename, tmptable[i]->mode, 0); } #ifdef GRADM_DEBUG printf("\nBeginning first stage object reduction...\n"); check_file_node_list_integrity(&subject->object_list); #endif first_stage_reduce_tree(subject->object_list); #ifdef GRADM_DEBUG printf("Beginning second stage object reduction...\n"); check_file_node_list_integrity(&subject->object_list); #endif second_stage_reduce_tree(subject->object_list); #ifdef GRADM_DEBUG printf("Beginning high protected path enforcement...\n"); check_file_node_list_integrity(&subject->object_list); #endif enforce_high_protected_paths(subject); #ifdef GRADM_DEBUG printf("Beginning third stage object reduction...\n"); check_file_node_list_integrity(&subject->object_list); #endif third_stage_reduce_tree(subject->object_list); printf("done.\n"); fflush(stdout); return 0; } int full_reduce_ip_node(struct gr_learn_file_node *subject, const void *unused1, FILE *unused2) { struct gr_learn_ip_node *tmp = subject->connect_list; reduce_ip_tree(tmp); reduce_ports_tree(tmp); tmp = subject->bind_list; reduce_ip_tree(tmp); reduce_ports_tree(tmp); return 0; } void free_subject_ids(unsigned int ***list, int thresh) { unsigned int **p; unsigned int size; int i; size = 0; p = *list; if (p == NULL) return; while (*p) { p++; size++; } if (size > thresh) { for (i = 0; i < size; i++) free(*(*list + i)); free(*list); *list = NULL; } else if (thresh == 0) { free(*list); *list = NULL; } return; } int full_reduce_id_node(struct gr_learn_file_node *subject, const void *unused1, FILE *unused2) { if (subject->subject == NULL || !cap_raised(subject->subject->cap_raise, CAP_SETUID)) free_subject_ids(&(subject->user_trans_list), 0); else free_subject_ids(&(subject->user_trans_list), 3); if (subject->subject == NULL || !cap_raised(subject->subject->cap_raise, CAP_SETGID)) free_subject_ids(&(subject->group_trans_list), 0); else free_subject_ids(&(subject->group_trans_list), 3); return 0; } int full_reduce_ips(struct gr_learn_group_node *group, struct gr_learn_user_node *user, FILE *unused) { struct gr_learn_file_node *subjects; if (user) subjects = user->subject_list; else subjects = group->subject_list; traverse_file_tree(subjects, &full_reduce_ip_node, NULL, NULL); return 0; } void free_ip_ports(struct gr_learn_ip_node *node) { struct gr_learn_ip_node *tmp, *tmp3; u_int16_t **tmp2; int removed = 0; if (node == NULL) return; for_each_removable_list_entry(tmp, node->leaves) { tmp3 = tmp->next; free_ip_ports(tmp); removed = 1; tmp = tmp3; for_each_removable_list_entry_end(tmp); } node->leaves = NULL; tmp2 = node->ports; while (tmp2 && *tmp2) { gr_free(*tmp2); tmp2++; } if (node->ports) gr_free(node->ports); node->ports = NULL; gr_free(node); return; } void free_subject_objects(struct gr_learn_file_node *node) { struct gr_learn_file_node *tmp, *tmp2; int removed = 0; if (node == NULL) return; for_each_removable_list_entry(tmp, node->leaves) { tmp2 = tmp->next; free_subject_objects(tmp); removed = 1; tmp = tmp2; for_each_removable_list_entry_end(tmp); } node->leaves = NULL; free_ip_ports(node->connect_list); free_ip_ports(node->bind_list); if (node->subject) { free(node->subject); node->subject = NULL; } free(node->filename); gr_free(node); return; } void free_subject_full(struct gr_learn_file_node *subject) { struct gr_learn_file_tmp_node **tmptable; unsigned long table_size, i; cachednode = NULL; cachedlen = 0; if (subject->hash) { tmptable = (struct gr_learn_file_tmp_node **)subject->hash->table; table_size = subject->hash->table_size; for (i = 0; i < table_size; i++) { if (tmptable[i] == NULL) continue; free(tmptable[i]->filename); free(tmptable[i]); } free(tmptable); free(subject->hash); } free_subject_ids(&(subject->user_trans_list), 0); free_subject_ids(&(subject->group_trans_list), 0); free_subject_objects(subject->object_list); return; } void free_role_user_full(struct gr_learn_user_node *user) { struct gr_learn_file_tmp_node **tmptable; unsigned long table_size, i; if (user->hash) { tmptable = (struct gr_learn_file_tmp_node **)user->hash->table; table_size = user->hash->table_size; for (i = 0; i < table_size; i++) { if (tmptable[i] == NULL) continue; free(tmptable[i]->filename); free(tmptable[i]); } free(tmptable); free(user->hash); } free_subject_objects(user->subject_list); free_ip_ports(user->allowed_ips); return; } void free_role_group_full(struct gr_learn_group_node *group) { struct gr_learn_file_tmp_node **tmptable; unsigned long table_size, i; if (group->hash) { tmptable = (struct gr_learn_file_tmp_node **)group->hash->table; table_size = group->hash->table_size; for (i = 0; i < table_size; i++) { if (tmptable[i] == NULL) continue; free(tmptable[i]->filename); free(tmptable[i]); } free(tmptable); free(group->hash); } free_subject_objects(group->subject_list); free_ip_ports(group->allowed_ips); return; } int fulllearn_pass3(struct gr_learn_file_node *subject, const void *rolename, FILE *stream) { fseek(fulllearn_pass3in, 0, SEEK_SET); current_learn_subject = subject->filename; fflush(stdout); fulllearn_pass3parse(); fflush(stdout); full_reduce_object_node(subject, NULL, NULL); full_reduce_ip_node(subject, NULL, NULL); full_reduce_id_node(subject, NULL, NULL); display_leaf(subject, rolename, stream); free_subject_full(subject); return 0; } void enforce_hidden_file(struct gr_learn_file_node *subject, const char *filename) { struct gr_learn_file_node *objects = subject->object_list; struct gr_learn_file_node *retobj; retobj = match_file_node(objects, filename); if (retobj->mode & GR_FIND && !strcmp(retobj->filename, filename)) retobj->mode = 0; else if (retobj->mode & GR_FIND) insert_file(&(subject->object_list), filename, 0, 0); return; } int ensure_subject_security(struct gr_learn_file_node *subject, const void *unused1, FILE *unused2) { if (strcmp(subject->filename, "/")) return 0; enforce_hidden_file(subject, "/etc/ssh"); enforce_hidden_file(subject, "/dev/mem"); enforce_hidden_file(subject, "/dev/kmem"); enforce_hidden_file(subject, "/dev/port"); enforce_hidden_file(subject, "/proc/kcore"); enforce_hidden_file(subject, GRSEC_DIR); enforce_hidden_file(subject, GRDEV_PATH); return 0; } int ensure_role_security(struct gr_learn_group_node *group, struct gr_learn_user_node *user, FILE *unused) { struct gr_learn_file_node *subjects; if (user) subjects = user->subject_list; else subjects = group->subject_list; traverse_file_tree(subjects, &ensure_subject_security, NULL, NULL); return 0; } static const char *initial_roles_str = "# policy generated from full system learning\n\n" "define grsec_denied {\n" "\t/boot\th\n" "\t/dev/grsec\th\n" "\t/dev/kmem\th\n" "\t/dev/mem\th\n" "\t/dev/port\th\n" "\t/etc/grsec\th\n" "\t/proc/kcore\th\n" "\t/proc/slabinfo\th\n" "\t/proc/modules\th\n" "\t/proc/kallsyms\th\n" "\t/lib/modules\ths\n" "\t/lib64/modules\ths\n" "\t/etc/ssh\th\n" "}\n\n" "role admin sA\n" "subject / rvka\n" "\t/ rwcdmlxi\n\n" "role shutdown sARG\n" "subject / rvka\n" "\t/\n" "\t/dev\n" "\t/dev/urandom r\n" "\t/dev/random r\n" "\t/etc r\n" "\t/bin rx\n" "\t/sbin rx\n" "\t/lib rx\n" "\t/lib64 rx\n" "\t/usr rx\n" "\t/proc r\n" "\t$grsec_denied\n" "\t-CAP_ALL\n" "\tconnect disabled\n" "\tbind disabled\n\n" "role default\n" "subject /\n" "\t/\t\t\th\n" "\t-CAP_ALL\n" "\tconnect\tdisabled\n" "\tbind\tdisabled\n\n"; void output_learn_header(FILE *stream) { fprintf(stream, "%s", initial_roles_str); fflush(stream); return; } void output_role_info(struct gr_learn_group_node *group, struct gr_learn_user_node *user, FILE *stream) { struct gr_learn_ip_node *allowed_ips = NULL; if (user) { fprintf(stream, "role %s u%s\n", user->rolename, strcmp(user->rolename, "root") ? "" : "G"); if (!strcmp(user->rolename, "root")) { fprintf(stream, "role_transitions admin shutdown\n"); } allowed_ips = user->allowed_ips; } else { fprintf(stream, "role %s g\n", group->rolename); allowed_ips = group->allowed_ips; } if (allowed_ips && !(grlearn_options & GR_DONT_LEARN_ALLOWED_IPS)) traverse_ip_tree(allowed_ips, NULL, &display_only_ip, 0, stream); return; } void generate_full_learned_acls(FILE *learnlog, FILE *stream) { struct gr_learn_group_node *group, *tmpgroup; struct gr_learn_user_node *user, *tmpuser; int removed = 0; char *current_output_file = NULL; int got_users = 0; int got_groups = 0; FILE *policystream; umask(0077); if (grlearn_options & GR_SPLIT_ROLES) { if (stream) { fprintf(stderr, "Error: Output path must be a directory when \"split-roles\" is used in learn_config.\n"); exit(EXIT_FAILURE); } current_output_file = (char *)alloca(strlen(output_log) + 16384); sprintf(current_output_file, "%s/policy", output_log); stream = fopen(current_output_file, "w"); if (stream == NULL) { fprintf(stderr, "Unable to open %s for writing.\n" "Error: %s\n", current_output_file, strerror(errno)); exit(EXIT_FAILURE); } } else { if (stream == NULL) { fprintf(stderr, "Error: Output path must be a file when \"split-roles\" is not used in learn_config.\n"); exit(EXIT_FAILURE); } } output_learn_header(stream); fulllearn_pass1(learnlog); fseek(learnlog, 0, SEEK_SET); fulllearn_pass2(learnlog); fulllearn_pass3in = learnlog; for_each_removable_list_entry(group, the_role_list) { if (group->users == NULL) { got_groups = 1; current_learn_rolename = group->rolename; current_learn_rolemode = GR_ROLE_GROUP; if (grlearn_options & GR_SPLIT_ROLES) { fclose(stream); sprintf(current_output_file, "%s/groups", output_log); mkdir(current_output_file, 0700); sprintf(current_output_file, "%s/groups/%s", output_log, group->rolename); stream = fopen(current_output_file, "w"); if (stream == NULL) { fprintf(stderr, "Unable to open %s for writing.\n" "Error: %s\n", current_output_file, strerror(errno)); exit(EXIT_FAILURE); } } output_role_info(group, NULL, stream); sort_file_node_list(group->subject_list); traverse_file_tree(group->subject_list, &fulllearn_pass3, group->rolename, stream); } else { for_each_removable_list_entry(user, group->users) { got_users = 1; current_learn_rolename = user->rolename; current_learn_rolemode = GR_ROLE_USER; if (grlearn_options & GR_SPLIT_ROLES) { fclose(stream); sprintf(current_output_file, "%s/users", output_log); mkdir(current_output_file, 0700); sprintf(current_output_file, "%s/users/%s", output_log, user->rolename); stream = fopen(current_output_file, "w"); if (stream == NULL) { fprintf(stderr, "Unable to open %s for writing.\n" "Error: %s\n", current_output_file, strerror(errno)); exit(EXIT_FAILURE); } } output_role_info(NULL, user, stream); sort_file_node_list(user->subject_list); traverse_file_tree(user->subject_list, &fulllearn_pass3, user->rolename, stream); tmpuser = user->next; free_role_user_full(user); user = tmpuser; removed = 1; for_each_removable_list_entry_end(user); } } tmpgroup = group->next; free_role_group_full(group); group = tmpgroup; removed = 1; for_each_removable_list_entry_end(group); } if (grlearn_options & GR_SPLIT_ROLES) { sprintf(current_output_file, "%s/policy", output_log); policystream = fopen(current_output_file, "a"); if (policystream == NULL) { fprintf(stderr, "Unable to open %s for writing.\n" "Error: %s\n", current_output_file, strerror(errno)); exit(EXIT_FAILURE); } if (got_users) fprintf(policystream, "include <%s/users>\n", output_log); if (got_groups) fprintf(policystream, "include <%s/groups>\n", output_log); fclose(policystream); } fprintf(stdout, "Full learning complete.\n"); fclose(learnlog); return; } gradm/gradm_sym.c0000644000175000017510000001472013152754421014171 0ustar spenderspender/* * Copyright (C) 2002-2014 Bradley Spengler, Open Source Security, Inc. * http://www.grsecurity.net spender@grsecurity.net * * This file is part of gradm. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "gradm.h" struct object_variable { char *symname; struct var_object *object; }; static struct object_variable *symtab = NULL; static unsigned int symtab_size = 0; void interpret_variable(struct var_object *var) { struct var_object *tmp; struct var_object *varhead = get_list_head(var); for_each_variable(tmp, varhead) { switch (tmp->type) { case VAR_FILE_OBJECT: add_proc_object_acl(current_subject, tmp->file_obj.filename, tmp->file_obj.mode, GR_FEXIST); break; case VAR_NET_OBJECT: if (tmp->net_obj.host) add_host_acl(current_subject, tmp->net_obj.mode, tmp->net_obj.host, &tmp->net_obj.ip); else add_ip_acl(current_subject, tmp->net_obj.mode, &tmp->net_obj.ip); break; case VAR_CAP_OBJECT: add_cap_acl(current_subject, tmp->cap_obj.cap, tmp->cap_obj.audit); break; default: break; } } return; } struct var_object * intersect_objects(struct var_object *var1, struct var_object *var2) { struct var_object *tmpvar1, *tmpvar2, *retvar = NULL; struct var_object *var1head = get_list_head(var1); struct var_object *var2head = get_list_head(var2); for_each_variable(tmpvar1, var1head) { switch (tmpvar1->type) { case VAR_FILE_OBJECT: for_each_variable(tmpvar2, var2head) { switch (tmpvar2->type) { case VAR_FILE_OBJECT: if (!strcmp(tmpvar1->file_obj.filename, tmpvar2->file_obj.filename)) { add_file_var_object(&retvar, tmpvar1->file_obj.filename, tmpvar1->file_obj.mode & tmpvar2->file_obj.mode); break; } break; default: break; } } break; default: break; } } return retvar; } struct var_object * union_objects(struct var_object *var1, struct var_object *var2) { struct var_object *tmpvar1, *tmpvar2, *retvar = NULL; struct var_object *var1head = get_list_head(var1); struct var_object *var2head = get_list_head(var2); int found_dupe = 0; for_each_variable(tmpvar1, var1head) { switch (tmpvar1->type) { case VAR_FILE_OBJECT: found_dupe = 0; for_each_variable(tmpvar2, var2head) { switch (tmpvar2->type) { case VAR_FILE_OBJECT: if (!strcmp(tmpvar1->file_obj.filename, tmpvar2->file_obj.filename)) { add_file_var_object(&retvar, tmpvar1->file_obj.filename, tmpvar1->file_obj.mode | tmpvar2->file_obj.mode); found_dupe = 1; break; } break; default: break; } } if (!found_dupe) add_file_var_object(&retvar, tmpvar1->file_obj.filename, tmpvar1->file_obj.mode); break; default: break; } } for_each_variable(tmpvar2, var2head) { switch (tmpvar2->type) { case VAR_FILE_OBJECT: found_dupe = 0; for_each_variable(tmpvar1, var1head) { switch (tmpvar1->type) { case VAR_FILE_OBJECT: if (!strcmp(tmpvar1->file_obj.filename, tmpvar2->file_obj.filename)) { found_dupe = 1; break; } break; default: break; } } if (!found_dupe) add_file_var_object(&retvar, tmpvar2->file_obj.filename, tmpvar2->file_obj.mode); break; default: break; } } return retvar; } struct var_object * differentiate_objects(struct var_object *var1, struct var_object *var2) { struct var_object *tmpvar1, *tmpvar2, *retvar = NULL; struct var_object *var1head = get_list_head(var1); struct var_object *var2head = get_list_head(var2); int found_dupe = 0; char *path; for_each_variable(tmpvar1, var1head) { switch (tmpvar1->type) { case VAR_FILE_OBJECT: path = gr_strdup(tmpvar1->file_obj.filename); found_dupe = 0; do { for_each_variable(tmpvar2, var2head) { switch (tmpvar2->type) { case VAR_FILE_OBJECT: if (!strcmp(path, tmpvar2->file_obj.filename)) { found_dupe = 1; add_file_var_object(&retvar, tmpvar1->file_obj.filename, tmpvar1->file_obj.mode & ~tmpvar2->file_obj.mode); goto done; } break; default: break; } } } while(parent_dir(tmpvar1->file_obj.filename, &path)); done: if (!found_dupe) add_file_var_object(&retvar, tmpvar1->file_obj.filename, tmpvar1->file_obj.mode); free(path); break; default: break; } } return retvar; } void add_var_object(struct var_object **object, struct var_object *var) { struct var_object *v; v = (struct var_object *) calloc(1, sizeof(struct var_object)); if (!v) failure("calloc"); if (*object) (*object)->next = v; memcpy(v, var, sizeof(struct var_object)); v->prev = *object; v->next = NULL; *object = v; return; } void add_file_var_object(struct var_object **object, const char *name, u_int32_t mode) { struct var_object var; var.type = VAR_FILE_OBJECT; var.file_obj.filename = name; var.file_obj.mode = mode; add_var_object(object, &var); } void add_net_var_object(struct var_object **object, struct ip_acl *ip, u_int8_t mode, const char *host) { struct var_object var; var.type = VAR_NET_OBJECT; memcpy(&var.net_obj.ip, ip, sizeof(struct ip_acl)); var.net_obj.mode = mode; var.net_obj.host = host ? gr_strdup(host) : NULL; add_var_object(object, &var); } void add_cap_var_object(struct var_object **object, const char *name, const char *audit) { struct var_object var; var.type = VAR_CAP_OBJECT; var.cap_obj.cap = name ? gr_strdup(name) : NULL; var.cap_obj.audit = audit ? gr_strdup(audit) : NULL; add_var_object(object, &var); } struct var_object * sym_retrieve(char *symname) { unsigned int i; for (i = 0; i < symtab_size; i++) if (!strcmp(symname, symtab[i].symname)) return symtab[i].object; return NULL; } void sym_store(char *symname, struct var_object *object) { symtab_size++; symtab = (struct object_variable *)gr_realloc(symtab, symtab_size * sizeof(struct object_variable)); symtab[symtab_size - 1].symname = symname; symtab[symtab_size - 1].object = object; return; } gradm/gradm.l0000644000175000017510000005100513152754421013307 0ustar spenderspender%{ /* * Copyright (C) 2002-2014 Bradley Spengler, Open Source Security, Inc. * http://www.grsecurity.net spender@grsecurity.net * * This file is part of gradm. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "gradm.h" #include "gradm.tab.h" unsigned long lineno; char *gr_line; unsigned int old_state; unsigned int old_state2; int gradmerror(const char *s); int gradmwrap(void); int is_eof(void); void add_include(char *includename); struct include_entry { unsigned long lineno; YY_BUFFER_STATE buffer; FILE *file; char *name; struct include_entry *dirents; struct include_entry *next; struct include_entry **stack; int sp; }; static struct include_entry include_stack; static struct include_entry *main_stack; %} IP [0-9]{1,3}"."[0-9]{1,3}"."[0-9]{1,3}"."[0-9]{1,3} %option nounput %x ROLE_STATE SUBJECT_STATE IP_STATE RES_STATE COMMENT_STATE ROLETYPE_STATE %x INCLUDE_STATE IPNETMASK_STATE IPPORT_STATE ROLETRANS_STATE UMASK_STATE %x VAR_STATE VAR_OBJ_STATE IDTRANS_STATE DOMAIN_STATE DOMAINTYPE_STATE %x DOMAINLIST_STATE IPIP_STATE IPONLY_STATE REP_STATE CAP_STATE FAMILY_STATE %x VAR_IPIP_STATE VAR_CAP_STATE VAR_IPNETMASK_STATE VAR_IP_STATE VAR_IPPORT_STATE %% <*>"\n" { lineno++; if (YYSTATE == COMMENT_STATE) BEGIN(old_state2); if (YYSTATE == VAR_IPIP_STATE || YYSTATE == VAR_CAP_STATE || YYSTATE == VAR_IPNETMASK_STATE || YYSTATE == VAR_IP_STATE || YYSTATE == VAR_IPPORT_STATE) BEGIN(VAR_OBJ_STATE); if (YYSTATE != VAR_STATE && YYSTATE != VAR_OBJ_STATE) BEGIN(INITIAL); } <*>"#" { if (YYSTATE != COMMENT_STATE) old_state2 = YYSTATE; BEGIN(COMMENT_STATE); } <*>[ \t]+ ; .* ; [ugslGNATPR]* { gradmlval.shortnum = role_mode_conv(yytext); return ROLE_TYPE; } ([ug][GTl]*)|([GTl]*[ug]) { BEGIN(DOMAINLIST_STATE); gradmlval.string = gr_strdup(yytext); return DOMAIN_TYPE; } "\\\n" ; [0-7]{3,4} { sscanf(yytext, "%ho", &gradmlval.shortnum); return UMASK; } [_a-zA-Z0-9.-]{1,64} { BEGIN(ROLETYPE_STATE); gradmlval.string = gr_strdup(yytext); return ROLE_NAME; } [_a-zA-Z0-9.-]{1,64} { BEGIN(DOMAINTYPE_STATE); gradmlval.string = gr_strdup(yytext); return ROLE_NAME; } [_a-zA-Z0-9.-]{1,64} { gradmlval.string = gr_strdup(yytext); return ROLE_NAME; } [_a-zA-Z0-9.-]{1,64} { gradmlval.string = gr_strdup(yytext); return ROLE_NAME; } ":" { return ':'; } ([/]|$[(])[^ :\t\n]* { gradmlval.string = strip_trailing_slash(process_string_replace(yytext)); if (strstr(gradmlval.string, "//") || strstr(gradmlval.string, "/./") || strstr(gradmlval.string, "/../") || strchr(gradmlval.string, '*') || strchr(gradmlval.string, '?'), strchr(gradmlval.string, '[')) gradmerror("invalid pathname error"); return SUBJ_NAME; } ["]([/]|$[(])[^:\n]*["] { gr_line = yytext; gr_line++; *(gr_line + strlen(gr_line) - 1) = '\0'; gradmlval.string = strip_trailing_slash(process_string_replace(gr_line)); if (strstr(gradmlval.string, "//") || strstr(gradmlval.string, "/./") || strstr(gradmlval.string, "/../")) gradmerror("invalid pathname error"); return SUBJ_NAME; } $HOME[/]?[^ :\t\n]* { gradmlval.string = strip_trailing_slash(process_string_replace(yytext)); if (strstr(gradmlval.string, "//") || strstr(gradmlval.string, "/./") || strstr(gradmlval.string, "/../")) gradmerror("invalid pathname error"); return SUBJ_NAME; } ["]$HOME[/]?[^:\n]*["] { gr_line = yytext; gr_line++; *(gr_line + strlen(gr_line) - 1) = '\0'; gradmlval.string = strip_trailing_slash(process_string_replace(gr_line)); if (strstr(gradmlval.string, "//") || strstr(gradmlval.string, "/./") || strstr(gradmlval.string, "/../")) gradmerror("invalid pathname error"); return SUBJ_NAME; } [TKCAOtolhpkvdbriasxZ]+ { gradmlval.num = proc_subject_mode_conv(yytext); return SUBJ_MODE; } "unlimited" { gradmlval.string = gr_strdup(yytext); return RES_SOFTHARD; } [0-9]+[smhdKMG]? { gradmlval.string = gr_strdup(yytext); return RES_SOFTHARD; } "!" { return NOT; } [-a-zA-Z0-9_]{1,7}("#"[0-9]{1,3})? { if (YYSTATE == IPIP_STATE) BEGIN(IP_STATE); else BEGIN(VAR_IP_STATE); gr_line = yytext; gradmlval.string = gr_strdup(gr_line); gr_line = strchr(gradmlval.string, '#'); if (gr_line != NULL) *gr_line = ':'; return INTERFACE; } "disabled" { return DISABLED; } [-0-9a-zA-Z.]*[a-zA-Z][-0-9a-zA-Z.]* { if (YYSTATE == IPIP_STATE) BEGIN(IP_STATE); else BEGIN(VAR_IP_STATE); gradmlval.string = gr_strdup(yytext); return HOSTNAME; } {IP} { gradmlval.num = get_ip(yytext); return IPADDR; } {IP} { if (YYSTATE == IPIP_STATE) BEGIN(IP_STATE); else BEGIN(VAR_IP_STATE); gradmlval.num = get_ip(yytext); return IPADDR; } [/] { if (YYSTATE == IP_STATE) BEGIN(IPNETMASK_STATE); else BEGIN(VAR_IPNETMASK_STATE); return *yytext; } [:-] { if (YYSTATE == IP_STATE) BEGIN(IPPORT_STATE); else BEGIN(VAR_IPPORT_STATE); return *yytext; } "raw_sock"|"dgram"|"rdm"|"stream"|"any_sock" { gradmlval.string = gr_strdup(yytext); return IPTYPE; } [a-z_-]+ { gradmlval.string = gr_strdup(yytext); return IPPROTO; } "proto:"[0-9]{1,3} { gradmlval.string = gr_strdup(yytext); return IPPROTO; } [0-9a-z]+ { gradmlval.string = gr_strdup(yytext); return SOCKFAMILY; } [0-9]{1,2} { unsigned int bits = atoi(yytext); if (YYSTATE == IPNETMASK_STATE) BEGIN(IP_STATE); else BEGIN(VAR_IP_STATE); if (!bits) gradmlval.num = 0; else gradmlval.num = 0xffffffff << (32 - bits); return IPNETMASK; } [0-9]{1,5} { unsigned int portcheck = atoi(yytext); if (YYSTATE == IPPORT_STATE) BEGIN(IP_STATE); else BEGIN(VAR_IP_STATE); if (portcheck > 65535) gradmerror("invalid port number error"); gradmlval.shortnum = portcheck; return IPPORT; } [^ "':()\t\n]+ { gradmlval.string = gr_strdup(yytext); return REP_ARG; } ["][^"\t\n]+["] { gr_line = yytext; gr_line++; *(gr_line + strlen(gr_line) - 1) = '\0'; gradmlval.string = gr_strdup(gr_line); return REP_ARG; } "replace" { old_state = YYSTATE; BEGIN(REP_STATE); return REPLACE; } "define" { old_state = YYSTATE; BEGIN(VAR_STATE); return DEFINE; } [$][a-zA-Z0-9_]+ { gr_line = yytext; if (!strcmp(gr_line, "$HOME")) { gradmlval.string = gr_strdup(gr_line); return OBJ_NAME; } else { gr_line++; gradmlval.string = gr_strdup(gr_line); return VARIABLE; } } [a-zA-Z0-9_]+ { gradmlval.string = gr_strdup(yytext); return DEFINE_NAME; } "{" { BEGIN(VAR_OBJ_STATE); return '{'; } "}" { BEGIN(old_state); return '}'; } [_a-zA-Z0-9.-]{1,64} { gradmlval.string = gr_strdup(yytext); return ID_NAME; } "user_transition_allow" { BEGIN(IDTRANS_STATE); return USER_TRANS_ALLOW; } "user_transition_deny" { BEGIN(IDTRANS_STATE); return USER_TRANS_DENY; } "group_transition_allow" { BEGIN(IDTRANS_STATE); return GROUP_TRANS_ALLOW; } "group_transition_deny" { BEGIN(IDTRANS_STATE); return GROUP_TRANS_DENY; } "role" { BEGIN(ROLE_STATE); return ROLE; } "domain" { BEGIN(DOMAIN_STATE); return DOMAIN; } "role_allow_ip" { BEGIN(IPIP_STATE); return ROLE_ALLOW_IP; } "role_umask" { BEGIN(UMASK_STATE); return ROLE_UMASK; } "role_transitions" { BEGIN(ROLETRANS_STATE); return ROLE_TRANSITION; } "subject" { BEGIN(SUBJECT_STATE); return SUBJECT; } "connect" { if (YYSTATE == INITIAL) BEGIN(IPIP_STATE); else BEGIN(VAR_IPIP_STATE); return CONNECT; } "bind" { if (YYSTATE == INITIAL) BEGIN(IPIP_STATE); else BEGIN(VAR_IPIP_STATE); return BIND; } "ip_override" { BEGIN(IPONLY_STATE); return IPOVERRIDE; } "sock_allow_family" { BEGIN(FAMILY_STATE); return SOCKALLOWFAMILY; } "include"[ \t]*[<][/].*[>] { gr_line = strchr(yytext, '/'); *(gr_line + strlen(gr_line) - 1) = '\0'; add_include(gr_line); } "audit"|"suppress" { gradmlval.string = gr_strdup(yytext); return AUDIT; } [+-]"CAP_"[_A-Z]+ { if (YYSTATE == INITIAL) BEGIN(CAP_STATE); else BEGIN(VAR_CAP_STATE); gradmlval.string = gr_strdup(yytext); return CAP_NAME; } [+-]"PAX_"[_A-Z]+ { gradmlval.string = gr_strdup(yytext); return PAX_NAME; } "RES_"[A-Z]+ { BEGIN(RES_STATE); gradmlval.string = gr_strdup(yytext); return RES_NAME; } ([/]|$[(])[^ \t\n]* { gradmlval.string = strip_trailing_slash(process_string_replace(yytext)); if (strstr(gradmlval.string, "//") || strstr(gradmlval.string, "/./") || strstr(gradmlval.string, "/../")) gradmerror("invalid pathname error"); return OBJ_NAME; } ["]([/]|$[(]).*["] { gr_line = yytext; gr_line++; *(gr_line + strlen(gr_line) - 1) = '\0'; gradmlval.string = strip_trailing_slash(process_string_replace(gr_line)); if (strstr(gradmlval.string, "//") || strstr(gradmlval.string, "/./") || strstr(gradmlval.string, "/../")) gradmerror("invalid pathname error"); return OBJ_NAME; } $HOME[/]?[^ \t\n]* { gradmlval.string = strip_trailing_slash(gr_strdup(yytext)); if (strstr(gradmlval.string, "//") || strstr(gradmlval.string, "/./") || strstr(gradmlval.string, "/../")) gradmerror("invalid pathname error"); return OBJ_NAME; } ["]$HOME[/]?.*["] { gr_line = yytext; gr_line++; *(gr_line + strlen(gr_line) - 1) = '\0'; gradmlval.string = strip_trailing_slash(gr_strdup(gr_line)); if (strstr(gradmlval.string, "//") || strstr(gradmlval.string, "/./") || strstr(gradmlval.string, "/../")) gradmerror("invalid pathname error"); return OBJ_NAME; } [rwxahitmlLFRWXAIMcCdDspofZ]+ { gradmlval.num = proc_object_mode_conv(yytext); return OBJ_MODE; } [|&()-] { return *yytext; } <*>[{}] ; <> { if (is_eof()) yyterminate(); } <*>. { gradmerror("invalid character"); } %% void push_include(unsigned long linenum, YY_BUFFER_STATE buffer, FILE *file, char *name) { int sp = main_stack->sp; main_stack->stack = (struct include_entry **) realloc(main_stack->stack,(1 + main_stack->sp) * sizeof(struct include_entry *)); if (main_stack->stack == NULL) failure("realloc"); main_stack->stack[sp] = (struct include_entry *)calloc(1, sizeof(struct include_entry)); if (main_stack->stack[sp] == NULL) failure("calloc"); main_stack->stack[sp]->sp = -1; main_stack->stack[sp]->lineno = linenum; main_stack->stack[sp]->file = gradmin; main_stack->stack[sp]->name = current_acl_file; main_stack->stack[sp]->buffer = YY_CURRENT_BUFFER; return; } void add_include(char *includename) { struct stat fstat; FILE *tmpfile; struct dirent **namelist; struct include_entry *last = NULL; struct include_entry *first = NULL; struct include_entry *tmp = NULL; char *path; int n, i; int sp; sp = ++main_stack->sp; if (sp >= MAX_INCLUDE_DEPTH) { fprintf(stderr, "Includes too deep while trying to process " "%s\n", includename); exit(EXIT_FAILURE); } if (stat(includename, &fstat)) { fprintf(stderr, "Unable to access included path: %s.\n" "Error: %s\n", includename, strerror(errno)); exit(EXIT_FAILURE); } push_include(lineno, YY_CURRENT_BUFFER, gradmin, current_acl_file); lineno = 1; if (!S_ISDIR(fstat.st_mode)) goto not_a_dir; n = scandir(includename, &namelist, 0, alphasort); if (n < 3) return; for (i = 0; i < n; i++) { if (!strcmp(namelist[i]->d_name, ".") || !strcmp(namelist[i]->d_name, "..") || !strcmp(namelist[i]->d_name+strlen(namelist[i]->d_name)-1, "~")) continue; tmp = (struct include_entry *)calloc(1, sizeof(struct include_entry)); if (tmp == NULL) failure("calloc"); path = (char *)calloc(1, strlen(includename) + strlen(namelist[i]->d_name) + 2); if (path == NULL) failure("calloc"); sprintf(path, "%s/%s", includename, namelist[i]->d_name); if (stat(path, &fstat)) { fprintf(stderr, "Unable to access included path: %s.\n" "Error: %s\n", includename, strerror(errno)); exit(EXIT_FAILURE); } if (S_ISDIR(fstat.st_mode)) { /* ignore the .svn and CVS directories */ if (!strcmp(namelist[i]->d_name, ".svn") || !strcmp(namelist[i]->d_name, "CVS")) { free(tmp); free(path); continue; } fprintf(stderr, "Including the nested directory %s is not currently supported.\n", path); exit(EXIT_FAILURE); } tmp->sp = -1; tmp->name = path; tmp->lineno = 1; if (last) last->next = tmp; last = tmp; if (first == NULL) first = tmp; } if (first == NULL) return; main_stack->stack[main_stack->sp]->dirents = first->next; path = first->name; change_current_acl_file(path); tmpfile = fopen(path, "r"); if (!tmpfile) { fprintf(stderr, "Unable to open included file: %s\n", path); exit(EXIT_FAILURE); } gradmin = tmpfile; yy_switch_to_buffer(yy_create_buffer(gradmin, YY_BUF_SIZE)); return; not_a_dir: tmpfile = fopen(includename, "r"); if (!tmpfile) { fprintf(stderr, "Unable to open included file: %s\n", includename); exit(EXIT_FAILURE); } change_current_acl_file(includename); gradmin = tmpfile; yy_switch_to_buffer(yy_create_buffer(gradmin, YY_BUF_SIZE)); return; } int is_eof(void) { struct include_entry *tmp = NULL; fclose(gradmin); if (main_stack->sp < 0) return 1; tmp = main_stack->stack[main_stack->sp]; if (tmp->dirents != NULL) { tmp = tmp->dirents; main_stack->stack[main_stack->sp]->dirents = tmp->next; change_current_acl_file(tmp->name); yy_delete_buffer(YY_CURRENT_BUFFER); gradmin = fopen(tmp->name, "r"); if (gradmin == NULL) { fprintf(stderr, "Unable to open included file: %s\n", tmp->name); exit(EXIT_FAILURE); } lineno = tmp->lineno; yy_switch_to_buffer(yy_create_buffer(gradmin, YY_BUF_SIZE)); return 0; } else if (!strcmp(main_stack->stack[main_stack->sp]->name, current_acl_file)) return 1; tmp = main_stack->stack[main_stack->sp]; change_current_acl_file(tmp->name); yy_delete_buffer(YY_CURRENT_BUFFER); lineno = tmp->lineno; gradmin = tmp->file; yy_switch_to_buffer(tmp->buffer); if (main_stack->sp > 0) main_stack->sp--; return 0; } int gradmwrap(void) { return 1; } int gradmerror(const char *s) { fflush(stderr); fprintf(stderr, "\"%s\" caused a %s on line %lu of %s\n", yytext, s, lineno, current_acl_file); exit(EXIT_FAILURE); } static void no_coredump(void) { struct rlimit rlim; rlim.rlim_cur = 0; rlim.rlim_max = 0; setrlimit(RLIMIT_CORE, &rlim); return; } static void tailor_to_kernel(void) { struct utsname buf; uname(&buf); if (!strncmp(buf.release, "2.4", 3)) is_24_kernel = 1; else is_24_kernel = 0; return; } #ifdef GRADM_DEBUG void show_policy(void) { struct file_acl *filp; struct ip_acl *ipp; struct proc_acl *proc; struct role_acl *rolp; struct role_transition *rolet; int i; for (rolp = current_role;rolp;rolp=rolp->prev) { printf("ROLE: %s type:%s uid/gid:%u\n", rolp->rolename, rolp->roletype & GR_ROLE_SPECIAL ? "special" : rolp->roletype & GR_ROLE_USER ? "user" : rolp->roletype & GR_ROLE_GROUP ? "group" : rolp->roletype & GR_ROLE_DEFAULT ? "default" : "", rolp->uidgid); printf("\tTRANSITIONS:"); for (rolet = rolp->transitions; rolet; rolet=rolet->prev) printf(" %s", rolet->rolename); printf("\n"); for (proc = rolp->hash->first;proc;proc=proc->prev) { printf("\tSUBJECT: %s dev:%lu inode:%lu mode:%lu c_raise:%x c_drop:%x\n", proc->filename, proc->dev, proc->inode, proc->mode, cap_invert(proc->cap_drop), proc->cap_drop); if (proc->user_trans_num > 0) { printf("\tUSER_TRANSITIONS_"); if (proc->user_trans_type & GR_ID_ALLOW) printf("ALLOW:"); else printf("DENY:"); for (i = 0; i < proc->user_trans_num; i++) printf(" %u", proc->user_transitions[i]); printf("\n"); } if (proc->group_trans_num > 0) { printf("\tGROUP_TRANSITIONS_"); if (proc->group_trans_type & GR_ID_ALLOW) printf("ALLOW:"); else printf("DENY:"); for (i = 0; i < proc->group_trans_num; i++) printf(" %u", proc->group_transitions[i]); printf("\n"); } for (i = 0; i < proc->ip_num; i++) { char ipaddr[4]; int c; int netmask = 0; ipp = *(proc->ips + i); memcpy(&ipaddr, &(ipp->addr), sizeof(ipaddr)); for (c = 0; c < 32; c++) if (ipp->netmask & (1U << c)) netmask++; if (ipp->mode &= GR_IP_CONNECT) printf("\t\tCONNECT %s %u.%u.%u.%u/%u:%d-%d", ipp->mode & GR_IP_INVERT ? "!" : "", ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3], netmask, ipp->low, ipp->high); else printf("\t\tBIND %s %u.%u.%u.%u/%u:%d-%d", ipp->mode & GR_IP_INVERT ? "!" : "", ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3], netmask, ipp->low, ipp->high); for (c = 1; c < 5; c++) { if (ipp->type & (1U << c)) { switch (c) { case SOCK_RAW: printf(" raw_sock"); break; case SOCK_DGRAM: printf(" dgram"); break; case SOCK_STREAM: printf(" stream"); break; case SOCK_RDM: printf(" rdm"); break; } } } for (c = 0; c < 256; c++) { if (ipp->proto[c / 32] & (1U << (c % 32))) { if (c == IPPROTO_RAW) printf(" raw_proto"); else { struct protoent *proto; proto = getprotobynumber(c); if(proto) printf(" %s", proto->p_name); else printf(" proto:%d", c); } } } printf("\n"); } for (filp = proc->hash->first;filp;filp=filp->prev) printf("\t\tOBJECT: %s dev:%lu inode:%lu mode:%lu\n", filp->filename, filp->dev, filp->inode, filp->mode); for (i=0;iresmask & (1U << i)) printf("\t\t%s: soft: %lu hard: %lu\n", rlim_table[i], proc->res[i].rlim_cur, proc->res[i].rlim_max); } } } #endif int main(int argc, char *argv[]) { if (geteuid() != getuid()) { fprintf(stderr, "gradm is not meant to run suid root.\n"); exit(EXIT_FAILURE); } init_res_table(); special_role_uid = 0; tailor_to_kernel(); #ifndef GRADM_DEBUG no_coredump(); #endif main_stack = &include_stack; memset(main_stack, 0, sizeof(struct include_entry)); main_stack->sp = -1; init_variables(); parse_args(argc, argv); #ifdef GRADM_DEBUG show_policy(); #endif return 0; } gradm/gradm_learn.c0000644000175000017510000002753313152754421014470 0ustar spenderspender/* * Copyright (C) 2002-2014 Bradley Spengler, Open Source Security, Inc. * http://www.grsecurity.net spender@grsecurity.net * * This file is part of gradm. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "gradm.h" struct gr_learn_role_entry *default_role_entry; struct gr_learn_role_entry *group_role_list; struct gr_learn_role_entry *user_role_list; struct gr_learn_role_entry *special_role_list; extern FILE *learn_pass1in; extern FILE *learn_pass2in; extern int learn_pass1parse(void); extern int learn_pass2parse(void); void learn_pass1(FILE *stream) { struct gr_learn_role_entry *tmp; struct gr_learn_file_tmp_node **tmptable; unsigned long i; u_int32_t table_size; learn_pass1in = stream; learn_pass1parse(); if (default_role_entry && default_role_entry->hash) { if (default_role_entry->hash) { tmptable = (struct gr_learn_file_tmp_node **)default_role_entry->hash->table; table_size = default_role_entry->hash->table_size; sort_file_list(default_role_entry->hash); for (i = 0; i < table_size; i++) { if (tmptable[i] == NULL) continue; if (default_role_entry->rolemode & GR_ROLE_LEARN) insert_file(&(default_role_entry->subject_list), tmptable[i]->filename, tmptable[i]->mode, 1); else insert_file(&(default_role_entry->subject_list), tmptable[i]->filename, tmptable[i]->mode, 2); } } if (default_role_entry->rolemode & GR_ROLE_LEARN) reduce_ip_tree(default_role_entry->allowed_ips); } for_each_list_entry(tmp, group_role_list) { if (tmp->hash) { tmptable = (struct gr_learn_file_tmp_node **)tmp->hash->table; table_size = tmp->hash->table_size; sort_file_list(tmp->hash); for (i = 0; i < table_size; i++) { if (tmptable[i] == NULL) continue; if (tmp->rolemode & GR_ROLE_LEARN) insert_file(&(tmp->subject_list), tmptable[i]->filename, tmptable[i]->mode, 1); else insert_file(&(tmp->subject_list), tmptable[i]->filename, tmptable[i]->mode, 2); } } if (tmp->rolemode & GR_ROLE_LEARN) reduce_ip_tree(tmp->allowed_ips); } for_each_list_entry(tmp, user_role_list) { if (tmp->hash) { tmptable = (struct gr_learn_file_tmp_node **)tmp->hash->table; table_size = tmp->hash->table_size; sort_file_list(tmp->hash); for (i = 0; i < table_size; i++) { if (tmptable[i] == NULL) continue; if (tmp->rolemode & GR_ROLE_LEARN) insert_file(&(tmp->subject_list), tmptable[i]->filename, tmptable[i]->mode, 1); else insert_file(&(tmp->subject_list), tmptable[i]->filename, tmptable[i]->mode, 2); } } if (tmp->rolemode & GR_ROLE_LEARN) reduce_ip_tree(tmp->allowed_ips); } for_each_list_entry(tmp, special_role_list) { if (tmp->hash) { tmptable = (struct gr_learn_file_tmp_node **)tmp->hash->table; table_size = tmp->hash->table_size; sort_file_list(tmp->hash); for (i = 0; i < table_size; i++) { if (tmptable[i] == NULL) continue; if (tmp->rolemode & GR_ROLE_LEARN) insert_file(&(tmp->subject_list), tmptable[i]->filename, tmptable[i]->mode, 1); else insert_file(&(tmp->subject_list), tmptable[i]->filename, tmptable[i]->mode, 2); } } if (tmp->rolemode & GR_ROLE_LEARN) reduce_ip_tree(tmp->allowed_ips); } return; } void merge_acl_rules(void) { struct gr_learn_role_entry *matchrole = NULL; struct gr_learn_file_node *matchsubj = NULL; struct role_acl *role; struct proc_acl *subject; struct file_acl *object; struct ip_acl *ipp; unsigned int i, x, y, port; for_each_role(role, current_role) { if (role->roletype & GR_ROLE_LEARN) continue; if (role->roletype & GR_ROLE_USER) matchrole = find_learn_role(user_role_list, role->rolename); else if (role->roletype & GR_ROLE_GROUP) matchrole = find_learn_role(group_role_list, role->rolename); else if (role->roletype & GR_ROLE_SPECIAL) matchrole = find_learn_role(special_role_list, role->rolename); else matchrole = default_role_entry; for_each_subject(subject, role) { if (matchrole) matchsubj = match_file_node(matchrole->subject_list, subject->filename); if (matchrole && matchsubj) { if (!(subject->mode & (GR_LEARN | GR_INHERITLEARN))) { matchsubj->dont_display = 1; continue; } if (matchsubj->subject == NULL) { matchsubj->subject = (struct gr_learn_subject_node *)calloc(1, sizeof(struct gr_learn_subject_node)); if (matchsubj->subject == NULL) failure("calloc"); } matchsubj->mode |= subject->mode; /* learned subject was using policy inheritance */ if (subject->parent_subject) matchsubj->mode &= ~GR_OVERRIDE; matchsubj->subject->pax_flags = subject->pax_flags; matchsubj->subject->cap_raise = cap_combine(matchsubj->subject->cap_raise, cap_intersect(cap_invert(subject->cap_drop), subject->cap_mask)); matchsubj->subject->resmask |= subject->resmask; matchsubj->subject->inaddr_any_override = subject->inaddr_any_override; for (i = 0; i < SIZE(subject->sock_families); i++) matchsubj->subject->sock_families[i] |= subject->sock_families[i]; for (i = 0; i < subject->user_trans_num; i++) { x = *(subject->user_transitions + i); insert_learn_id_transition(&(matchsubj->user_trans_list), x, x, x); } for (i = 0; i < subject->group_trans_num; i++) { x = *(subject->group_transitions + i); insert_learn_id_transition(&(matchsubj->group_trans_list), x, x, x); } for (i = 0; i < GR_NLIMITS; i++) { if (subject->res[i].rlim_cur > matchsubj->subject->res[i].rlim_cur) matchsubj->subject->res[i].rlim_cur = subject->res[i].rlim_cur; if (subject->res[i].rlim_max > matchsubj->subject->res[i].rlim_max) matchsubj->subject->res[i].rlim_max = subject->res[i].rlim_max; } for_each_file_object(object, subject) { insert_learn_object(matchsubj, conv_filename_to_struct(object->filename, object->mode)); } for (i = 0; i < subject->ip_num; i++) { ipp = subject->ips[i]; if (ipp->mode == GR_IP_CONNECT) { for (port = ipp->low; port <= ipp->high; port++) for (x = 0; x < 5; x++) for (y = 0; y < 256; y++) if ((ipp->type & (1U << x)) && (ipp->proto[y / 32] & (1U << y % 32))) insert_ip(&(matchsubj->connect_list), ipp->addr, port, x, y); } else if (ipp->mode == GR_IP_BIND) { for (port = ipp->low; port <= ipp->high; port++) for (x = 0; x < 5; x++) for (y = 0; y < 256; y++) if ((ipp->type & (1U << x)) && (ipp->proto[y / 32] & (1U << y % 32))) insert_ip(&(matchsubj->bind_list), ipp->addr, port, x, y); } } } } } return; } void learn_pass2(FILE *stream) { struct gr_learn_role_entry *tmp; struct gr_learn_file_node *subjects; learn_pass2in = stream; learn_pass2parse(); merge_acl_rules(); if (default_role_entry) { subjects = default_role_entry->subject_list; traverse_file_tree(subjects, &full_reduce_object_node, NULL, NULL); traverse_file_tree(subjects, &full_reduce_ip_node, NULL, NULL); traverse_file_tree(subjects, &ensure_subject_security, NULL, NULL); } for_each_list_entry(tmp, group_role_list) { subjects = tmp->subject_list; traverse_file_tree(subjects, &full_reduce_object_node, NULL, NULL); traverse_file_tree(subjects, &full_reduce_ip_node, NULL, NULL); traverse_file_tree(subjects, &ensure_subject_security, NULL, NULL); } for_each_list_entry(tmp, user_role_list) { subjects = tmp->subject_list; traverse_file_tree(subjects, &full_reduce_object_node, NULL, NULL); traverse_file_tree(subjects, &full_reduce_ip_node, NULL, NULL); traverse_file_tree(subjects, &ensure_subject_security, NULL, NULL); } for_each_list_entry(tmp, special_role_list) { subjects = tmp->subject_list; traverse_file_tree(subjects, &full_reduce_object_node, NULL, NULL); traverse_file_tree(subjects, &full_reduce_ip_node, NULL, NULL); traverse_file_tree(subjects, &ensure_subject_security, NULL, NULL); } return; } void perform_parse_and_reduce(FILE *learnlog) { learn_pass1(learnlog); fseek(learnlog, 0, SEEK_SET); learn_pass2(learnlog); fclose(learnlog); return; } void display_learn_logs(FILE *stream) { struct gr_learn_role_entry *tmp; struct gr_learn_file_node *subjects; struct gr_learn_ip_node *allowed_ips; char rolemode[17]; if (default_role_entry) { if (!(default_role_entry->rolemode & GR_ROLE_LEARN)) fprintf(stream, "### THE BELOW SUBJECT(S) SHOULD BE ADDED TO THE DEFAULT ROLE ###\n"); else fprintf(stream, "role default G\n"); subjects = default_role_entry->subject_list; allowed_ips = default_role_entry->allowed_ips; if (allowed_ips && !(grlearn_options & GR_DONT_LEARN_ALLOWED_IPS)) traverse_ip_tree(allowed_ips, NULL, &display_only_ip, 0, stream); if (subjects) { sort_file_node_list(default_role_entry->subject_list); display_tree_with_role(subjects, "default", stream); } fprintf(stream, "\n"); } for_each_list_entry(tmp, group_role_list) { if (!(tmp->rolemode & GR_ROLE_LEARN)) fprintf(stream, "### THE BELOW SUBJECT(S) SHOULD BE ADDED TO THE GROUP ROLE \"%s\" ###\n", tmp->rolename); else { conv_role_mode_to_str(tmp->rolemode, rolemode, sizeof(rolemode)); fprintf(stream, "role %s %s\n", tmp->rolename, rolemode); } subjects = tmp->subject_list; allowed_ips = tmp->allowed_ips; if (allowed_ips && !(grlearn_options & GR_DONT_LEARN_ALLOWED_IPS)) traverse_ip_tree(allowed_ips, NULL, &display_only_ip, 0, stream); if (subjects) { sort_file_node_list(group_role_list->subject_list); display_tree_with_role(subjects, tmp->rolename, stream); } fprintf(stream, "\n"); } for_each_list_entry(tmp, user_role_list) { if (!(tmp->rolemode & GR_ROLE_LEARN)) fprintf(stream, "### THE BELOW SUBJECT(S) SHOULD BE ADDED TO THE USER ROLE \"%s\" ###\n", tmp->rolename); else { conv_role_mode_to_str(tmp->rolemode, rolemode, sizeof(rolemode)); fprintf(stream, "role %s %s\n", tmp->rolename, rolemode); } subjects = tmp->subject_list; allowed_ips = tmp->allowed_ips; if (allowed_ips && !(grlearn_options & GR_DONT_LEARN_ALLOWED_IPS)) traverse_ip_tree(allowed_ips, NULL, &display_only_ip, 0, stream); if (subjects) { sort_file_node_list(user_role_list->subject_list); display_tree_with_role(subjects, tmp->rolename, stream); } fprintf(stream, "\n"); } for_each_list_entry(tmp, special_role_list) { if (!(tmp->rolemode & GR_ROLE_LEARN)) fprintf(stream, "### THE BELOW SUBJECT(S) SHOULD BE ADDED TO THE SPECIAL ROLE \"%s\" ###\n", tmp->rolename); else { conv_role_mode_to_str(tmp->rolemode, rolemode, sizeof(rolemode)); fprintf(stream, "role %s %s\n", tmp->rolename, rolemode); } subjects = tmp->subject_list; allowed_ips = tmp->allowed_ips; if (allowed_ips && !(grlearn_options & GR_DONT_LEARN_ALLOWED_IPS)) traverse_ip_tree(allowed_ips, NULL, &display_only_ip, 0, stream); if (subjects) { sort_file_node_list(special_role_list->subject_list); display_tree_with_role(subjects, tmp->rolename, stream); } fprintf(stream, "\n"); } return; } void handle_learn_logs(FILE *learnlog, FILE * stream) { struct glob_file *glob; parse_acls(); expand_acls(); /* since we don't call analyze_acls(), we'll defer the errors till they load the policy */ for (glob = glob_files_head; glob; glob = glob->next) add_globbed_object_acl(glob->subj, glob->filename, glob->mode, glob->type, glob->policy_file, glob->lineno); perform_parse_and_reduce(learnlog); display_learn_logs(stream); return; } gradm/grlearn_config.l0000644000175000017510000000416113152754421015175 0ustar spenderspender%{ /* * Copyright (C) 2002-2014 Bradley Spengler, Open Source Security, Inc. * http://www.grsecurity.net spender@grsecurity.net * * This file is part of gradm. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "gradm.h" #ifdef IS_GRLEARN #include "grlearn2_config.tab.h" #define grlearn_configlval grlearn2_configlval #else #include "grlearn_config.tab.h" #endif void grlearn_configerror(const char *s); int grlearn_configwrap(void); %} NUM [0-9]+ FILENAME [/][^\t\n]* %option nounput %x ERROR COMMENT_STATE %% <*>"#" { BEGIN(COMMENT_STATE); } <*>"\n" { BEGIN(INITIAL); } "inherit-learn" { return INHERITLEARN; } "inherit-no-learn" { return INHERITNOLEARN; } "no-learn" { return NOLEARN; } "dont-reduce-path" { return DONTREDUCE; } "high-reduce-path" { return HIGHREDUCE; } "always-reduce-path" { return ALWAYSREDUCE; } "protected-path" { return PROTECTED; } "read-protected-path" { return READPROTECTED; } "high-protected-path" { return HIGHPROTECTED; } "dont-learn-allowed-ips" { return NOALLOWEDIPS; } "split-roles" { return SPLITROLES; } {NUM} { grlearn_configlval.num = atol(yytext); return NUM; } {FILENAME} { grlearn_configlval.string = gr_strdup(yytext); return FILENAME; } . ; %% void grlearn_configerror(const char *s) { return; } int grlearn_configwrap(void) { return 1; } gradm/README0000644000175000017510000000021013152754421012710 0ustar spenderspenderTo install gradm with PAM support for special roles: make make install To install gradm without PAM support: make nopam make install gradm/gradm_learn_pass2.y0000644000175000017510000001345613152754421015625 0ustar spenderspender%{ /* * Copyright (C) 2002-2014 Bradley Spengler, Open Source Security, Inc. * http://www.grsecurity.net spender@grsecurity.net * * This file is part of gradm. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "gradm.h" extern int learn_pass2lex(void); extern struct gr_learn_role_entry *default_role_entry; extern struct gr_learn_role_entry *group_role_list; extern struct gr_learn_role_entry *user_role_list; extern struct gr_learn_role_entry *special_role_list; %} %union { char * string; unsigned long num; } %token FILENAME ROLENAME %token NUM IPADDR USER GROUP %type filename %type id_type %% learn_logs: learn_log | learn_logs learn_log ; filename: /*empty*/ { $$ = gr_strdup(""); } | FILENAME { if (!strcmp($1, "//")) $1[1] = '\0'; $$ = $1; } ; id_type: USER | GROUP ; learn_log: error | ROLENAME ':' NUM ':' NUM ':' NUM ':' filename ':' filename ':' NUM ':' NUM ':' filename ':' NUM ':' IPADDR { struct gr_learn_role_entry *role; struct gr_learn_file_node *subjlist; struct gr_learn_file_node *subject; u_int32_t mode; u_int16_t rolemode; unsigned long res1, res2; rolemode = $3; mode = $19; res1 = $13; res2 = $15; if (rolemode & GR_ROLE_USER) role = insert_learn_role(&user_role_list, $1, rolemode); else if (rolemode & GR_ROLE_GROUP) role = insert_learn_role(&group_role_list, $1, rolemode); else if (rolemode & GR_ROLE_SPECIAL) role = insert_learn_role(&special_role_list, $1, rolemode); else role = default_role_entry; free($1); subjlist = role->subject_list; if (rolemode & GR_ROLE_LEARN) subject = match_file_node(subjlist, $9); else subject = match_file_node(subjlist, $11); if (strcmp($17, "")) insert_learn_object(subject, conv_filename_to_struct($17, mode | GR_FIND)); else if ((strlen($9) > 1) && !res1 && !res2) { // capability if (subject->subject == NULL) { subject->subject = (struct gr_learn_subject_node *)calloc(1, sizeof(struct gr_learn_subject_node)); if (subject->subject == NULL) failure("calloc"); } cap_raise(subject->subject->cap_raise, mode); } else if (strlen($9) > 1) { // resource if (subject->subject == NULL) { subject->subject = (struct gr_learn_subject_node *)calloc(1, sizeof(struct gr_learn_subject_node)); if (subject->subject == NULL) failure("calloc"); } if (mode < GR_NLIMITS) { subject->subject->resmask |= (1U << mode); subject->subject->res[mode].rlim_cur = res1; subject->subject->res[mode].rlim_max = res2; } } free($9); free($11); free($17); } | ROLENAME ':' NUM ':' NUM ':' NUM ':' filename ':' filename ':' IPADDR ':' NUM ':' NUM ':' NUM ':' NUM ':' IPADDR { struct gr_learn_role_entry *role; struct gr_learn_file_node *subjlist; struct gr_learn_file_node *subject; u_int16_t rolemode; u_int32_t addr; u_int16_t port; u_int8_t mode, proto, socktype; mode = $19; rolemode = $3; addr = $13; port = $15; socktype = $17; proto = $19; mode = $21; if (rolemode & GR_ROLE_USER) role = insert_learn_role(&user_role_list, $1, rolemode); else if (rolemode & GR_ROLE_GROUP) role = insert_learn_role(&group_role_list, $1, rolemode); else if (rolemode & GR_ROLE_SPECIAL) role = insert_learn_role(&special_role_list, $1, rolemode); else role = default_role_entry; free($1); subjlist = role->subject_list; if (rolemode & GR_ROLE_LEARN) subject = match_file_node(subjlist, $9); else subject = match_file_node(subjlist, $11); if (mode == GR_IP_CONNECT) insert_ip(&(subject->connect_list), addr, port, proto, socktype); else if (mode == GR_IP_BIND) insert_ip(&(subject->bind_list), addr, port, proto, socktype); else if (mode == GR_SOCK_FAMILY) { if (subject->subject == NULL) { subject->subject = (struct gr_learn_subject_node *)calloc(1, sizeof(struct gr_learn_subject_node)); if (subject->subject == NULL) failure("calloc"); } subject->subject->sock_families[port / 32] |= (1U << (port % 32)); } free($9); free($11); } | ROLENAME ':' NUM ':' NUM ':' NUM ':' filename ':' filename ':' id_type ':' NUM ':' NUM ':' NUM ':' IPADDR { struct gr_learn_role_entry *role; struct gr_learn_file_node *subjlist; struct gr_learn_file_node *subject; u_int16_t rolemode; unsigned int real, eff, fs; rolemode = $3; real = $15; eff = $17; fs = $19; if (rolemode & GR_ROLE_USER) role = insert_learn_role(&user_role_list, $1, rolemode); else if (rolemode & GR_ROLE_GROUP) role = insert_learn_role(&group_role_list, $1, rolemode); else if (rolemode & GR_ROLE_SPECIAL) role = insert_learn_role(&special_role_list, $1, rolemode); else role = default_role_entry; free($1); subjlist = role->subject_list; if (rolemode & GR_ROLE_LEARN) subject = match_file_node(subjlist, $9); else subject = match_file_node(subjlist, $11); if ($13 == USER) insert_learn_id_transition(&(subject->user_trans_list), real, eff, fs); else if ($13 == GROUP) insert_learn_id_transition(&(subject->group_trans_list), real, eff, fs); free($9); free($11); } ; %% gradm/gradm_pw.c0000644000175000017510000001154413152754421014010 0ustar spenderspender/* * Copyright (C) 2002-2014 Bradley Spengler, Open Source Security, Inc. * http://www.grsecurity.net spender@grsecurity.net * * This file is part of gradm. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "gradm.h" void write_user_passwd(struct gr_pw_entry *entry) { int fd; int len; off_t offset; unsigned char total[GR_SPROLE_LEN + GR_SHA_SUM_SIZE + GR_SALT_SIZE]; if ((access(GR_PW_PATH, F_OK)) != 0) { if ((fd = open(GR_PW_PATH, O_EXCL | O_CREAT, S_IRUSR | S_IWUSR)) < 0) { fprintf(stderr, "Could not open %s\n", GR_PW_PATH); failure("open"); } close(fd); } if ((fd = open(GR_PW_PATH, O_RDWR)) < 0) { fprintf(stderr, "Could not open %s\n", GR_PW_PATH); failure("open"); } while ((len = read(fd, total, sizeof (total))) == sizeof (total)) { if (!memcmp(&total, entry->rolename, GR_SPROLE_LEN)) { if ((offset = lseek(fd, -sizeof (total), SEEK_CUR)) == (off_t) - 1) failure("lseek"); break; } } if (write(fd, entry->rolename, GR_SPROLE_LEN) != GR_SPROLE_LEN) { fprintf(stderr, "Error writing to %s\n", GR_PW_PATH); failure("write"); } if (write(fd, entry->salt, GR_SALT_SIZE) != GR_SALT_SIZE) { fprintf(stderr, "Error writing to %s\n", GR_PW_PATH); failure("write"); } if (write(fd, entry->sum, GR_SHA_SUM_SIZE) != GR_SHA_SUM_SIZE) { fprintf(stderr, "Error writing to %s\n", GR_PW_PATH); failure("write"); } close(fd); return; } void get_user_passwd(struct gr_pw_entry *entry, int mode) { struct termios term; struct gr_pw_entry *old = NULL; struct gr_pw_entry newpw; int i, err; err = mlock(&newpw, sizeof (newpw)); if (err && !getuid()) fprintf(stderr, "Warning: Unable to lock password " "into physical memory.\n"); start_pw: memset(&newpw, 0, sizeof (struct gr_pw_entry)); for (i = 0; i <= mode; i++) { if (i == GR_PWANDSUM) { old = entry; entry = &newpw; } fprintf(stderr, "%s", (i ? "Re-enter Password: " : "Password: ")); fflush(stderr); tcgetattr(STDIN_FILENO, &term); if (term.c_lflag & ECHO) { term.c_lflag &= ~ECHO; tcsetattr(STDIN_FILENO, TCSANOW, &term); } if ((read(STDIN_FILENO, entry->passwd, GR_PW_LEN - 1)) < 0) { fprintf(stderr, "\nError reading password from user.\n"); term.c_lflag |= ECHO; tcsetattr(STDIN_FILENO, TCSANOW, &term); failure("read"); } fprintf(stderr, "\n"); fflush(stderr); term.c_lflag |= ECHO; tcsetattr(STDIN_FILENO, TCSANOW, &term); entry->passwd[GR_PW_LEN - 1] = '\0'; /* strip newline */ entry->passwd[strlen((char *)entry->passwd) - 1] = '\0'; if ((strlen((char *)entry->passwd) < 6) && mode == 1) { fprintf(stderr, "Your password must be at least 6 characters in length.\n"); goto start_pw; } if (i == GR_PWANDSUM) { if (strcmp((char *)old->passwd, (char *)entry->passwd)) { fprintf(stderr, "Passwords do not match.\n"); exit(EXIT_FAILURE); } entry = old; memset(&newpw, 0, sizeof (struct gr_pw_entry)); printf("Password written to %s.\n", GR_PW_PATH); } } return; } void generate_salt(struct gr_pw_entry *entry) { int fd; if ((fd = open("/dev/urandom", O_RDONLY)) < 0) { fprintf(stderr, "Unable to open /dev/urandom for reading.\n"); failure("open"); } if (read(fd, entry->salt, GR_SALT_SIZE) != GR_SALT_SIZE) { fprintf(stderr, "Unable to read from /dev/urandom\n"); failure("read"); } return; } int read_saltandpass(const unsigned char *rolename, unsigned char *salt, unsigned char *pass) { int fd; int len; int found = 0; unsigned char cmp[GR_SPROLE_LEN]; unsigned char total[GR_SPROLE_LEN + GR_SHA_SUM_SIZE + GR_SALT_SIZE]; memset(&cmp, 0, sizeof (cmp)); fd = open(GR_PW_PATH, O_RDONLY); if (fd < 0) { fprintf(stderr, "Error opening: %s\n", GR_PW_PATH); perror("open"); exit(EXIT_FAILURE); } while ((len = read(fd, total, sizeof (total))) == sizeof (total)) { if (!memcmp(&total, rolename, GR_SPROLE_LEN)) { found = 1; break; } } close(fd); if (!found && !memcmp(rolename, &cmp, GR_SPROLE_LEN)) { fprintf(stderr, "Your password file is not set up correctly.\n" "Run gradm -P to set a password.\n"); exit(EXIT_FAILURE); } else if (!found) return 0; memcpy(salt, total + GR_SPROLE_LEN, GR_SALT_SIZE); memcpy(pass, total + GR_SPROLE_LEN + GR_SALT_SIZE, GR_SHA_SUM_SIZE); return 1; } gradm/gradm_parse.c0000644000175000017510000007247213152754421014503 0ustar spenderspender/* * Copyright (C) 2002-2015 Bradley Spengler, Open Source Security, Inc. * http://www.grsecurity.net spender@grsecurity.net * * This file is part of gradm. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "gradm.h" extern FILE *gradmin; extern int gradmparse(void); void set_role_umask(struct role_acl *role, u_int16_t umask) { role->umask = umask; } char *strip_trailing_slash(char *filename) { unsigned int file_len = strlen(filename); if (file_len > 1 && filename[file_len - 1] == '/') filename[file_len - 1] = '\0'; if (file_len >= PATH_MAX) { fprintf(stderr, "Filename too long on line %lu of file %s.\n", lineno, current_acl_file); exit(EXIT_FAILURE); } return filename; } static int get_id_from_role_name(const char *rolename, u_int16_t type, int *retid) { unsigned long the_id = 0; struct passwd *pwd; struct group *grp; char *endptr; if (type & GR_ROLE_USER) { pwd = getpwnam(rolename); if (!pwd) { /* now try it as a uid */ the_id = strtoul(rolename, &endptr, 10); if (*endptr == '\0') pwd = getpwuid((int)the_id); if (the_id > INT_MAX || *endptr != '\0') { fprintf(stderr, "Warning: User %s on line %lu of %s " "is invalid.\n", rolename, lineno, current_acl_file); return 1; } } if (pwd) the_id = pwd->pw_uid; /* else, the_id obtained above via strtoul is valid */ } else if (type & GR_ROLE_GROUP) { grp = getgrnam(rolename); if (!grp) { /* now try it as a gid */ the_id = strtoul(rolename, &endptr, 10); if (*endptr == '\0') grp = getgrgid((int)the_id); if (the_id > INT_MAX || *endptr != '\0') { fprintf(stderr, "Warning: Group %s on line %lu of %s " "is invalid.\n", rolename, lineno, current_acl_file); return 1; } } if (grp) the_id = grp->gr_gid; /* else, the_id obtained above via strtoul is valid */ } *retid = (int)the_id; return 0; } void add_id_transition(struct proc_acl *subject, const char *idname, int usergroup, int allowdeny) { int i; int id; int ret; if (usergroup == GR_ID_USER) { if ((subject->user_trans_type | allowdeny) == (GR_ID_ALLOW | GR_ID_DENY)) { fprintf(stderr, "Error on line %lu of %s. You cannot use " "both user_transition_allow and user_transition_deny.\n" "The RBAC system will not be allowed to be enabled until " "this error is fixed.\n", lineno, current_acl_file); exit(EXIT_FAILURE); } subject->user_trans_type |= allowdeny; ret = get_id_from_role_name(idname, GR_ROLE_USER, &id); if (ret) return; /* dupecheck */ for (i = 0; i < subject->user_trans_num; i++) if (subject->user_transitions[i] == id) return; /* increment pointer count upon allocation of user transition list */ if (subject->user_transitions == NULL) num_pointers++; subject->user_trans_num++; subject->user_transitions = (uid_t *)gr_realloc(subject->user_transitions, subject->user_trans_num * sizeof(uid_t)); subject->user_transitions[subject->user_trans_num - 1] = id; } else if (usergroup == GR_ID_GROUP) { if ((subject->group_trans_type | allowdeny) == (GR_ID_ALLOW | GR_ID_DENY)) { fprintf(stderr, "Error on line %lu of %s. You cannot use " "both group_transition_allow and group_transition_deny.\n" "The RBAC system will not be allowed to be enabled until " "this error is fixed.\n", lineno, current_acl_file); exit(EXIT_FAILURE); } subject->group_trans_type |= allowdeny; ret = get_id_from_role_name(idname, GR_ROLE_GROUP, &id); if (ret) return; /* dupecheck */ for (i = 0; i < subject->group_trans_num; i++) if (subject->group_transitions[i] == id) return; /* increment pointer count upon allocation of group transition list */ if (subject->group_transitions == NULL) num_pointers++; subject->group_trans_num++; subject->group_transitions = (gid_t *)gr_realloc(subject->group_transitions, subject->group_trans_num * sizeof(gid_t)); subject->group_transitions[subject->group_trans_num - 1] = id; } return; } static int is_role_dupe(struct role_acl *role, const char *rolename, const u_int16_t type) { struct role_acl *tmp; int id = 0; int ret; int i; if ((type & GR_ROLE_ISID) || ((type & (GR_ROLE_USER | GR_ROLE_GROUP)) && !(type & GR_ROLE_DOMAIN))) { ret = get_id_from_role_name(rolename, type, &id); if (ret) return 0; } for_each_role(tmp, role) { if ((tmp->roletype & (GR_ROLE_USER | GR_ROLE_GROUP | GR_ROLE_SPECIAL) & type) && !strcmp(tmp->rolename, rolename)) return 1; if ((tmp->roletype & GR_ROLE_DOMAIN) && (type & (GR_ROLE_USER | GR_ROLE_GROUP))) { for (i = 0; i < tmp->domain_child_num; i++) { if (tmp->domain_children[i] == id) return 1; } } } return 0; } void add_domain_child(struct role_acl *role, const char *idname) { int ret, id; if (!(role->roletype & (GR_ROLE_USER | GR_ROLE_GROUP))) { // should never get here fprintf(stderr, "Unhandled exception 1.\n"); exit(EXIT_FAILURE); } if (is_role_dupe(current_role, idname, role->roletype | GR_ROLE_ISID)) { fprintf(stderr, "Duplicate role %s on line %lu of %s.\n" "The RBAC system will not be allowed to be " "enabled until this error is fixed.\n", idname, lineno, current_acl_file); exit(EXIT_FAILURE); } ret = get_id_from_role_name(idname, role->roletype, &id); if (ret) return; /* reason for this is that in the kernel, the hash table which is keyed by UID/GID has a size dependent on the number of roles. Since we want to fake a domain as being a real role for each of those users/groups by providing a pointer to the domain for each user/group, we need to count each of these against the role count */ num_domain_children++; /* increment pointer count upon allocation of domain list */ if (role->domain_children == NULL) num_pointers++; role->domain_child_num++; role->domain_children = (gid_t *)gr_realloc(role->domain_children, role->domain_child_num * sizeof(gid_t)); *(role->domain_children + role->domain_child_num - 1) = id; return; } void add_role_transition(struct role_acl *role, const char *rolename) { struct role_transition **roletpp; struct role_transition *roletp; /* one for transition, one for name */ num_pointers += 2; roletp = (struct role_transition *) gr_alloc(sizeof (struct role_transition)); roletpp = &(role->transitions); if (*roletpp) (*roletpp)->next = roletp; roletp->prev = *roletpp; roletp->rolename = rolename; *roletpp = roletp; return; } void add_symlink(struct proc_acl *subj, struct file_acl *obj) { struct symlink *sym = (struct symlink *)gr_alloc(sizeof (struct symlink)); sym->role = current_role; sym->subj = subj; sym->obj = obj; sym->policy_file = current_acl_file; sym->lineno = lineno; sym->next = symlinks; symlinks = sym; return; } static struct deleted_file * is_deleted_file_dupe(const char *filename) { struct deleted_file *tmp; for (tmp = deleted_files; tmp; tmp = tmp->next) { if (!strcmp(filename, tmp->filename)) return tmp; } return NULL; } static struct deleted_file * add_deleted_file(const char *filename) { struct deleted_file *dfile; struct deleted_file *retfile; static u_int64_t ino = 0x10000000; ino++; retfile = is_deleted_file_dupe(filename); if (retfile) return retfile; dfile = (struct deleted_file *)gr_alloc(sizeof (struct deleted_file)); dfile->filename = filename; dfile->ino = ++ino; dfile->next = deleted_files; deleted_files = dfile; return deleted_files; } static struct file_acl * is_proc_object_dupe(struct proc_acl *subject, struct file_acl *object) { struct file_acl *tmp = NULL; tmp = lookup_acl_object_by_name(subject, object->filename); if (tmp == NULL) tmp = lookup_acl_object(subject, object); else { /* found a match by filename, handle 'Z' flag here */ if (object->mode & GR_OBJ_REPLACE) tmp->mode = object->mode &~ GR_OBJ_REPLACE; } return tmp; } static struct proc_acl * is_proc_subject_dupe(struct role_acl *role, struct proc_acl *subject) { struct proc_acl *tmp = NULL; tmp = lookup_acl_subject_by_name(role, subject->filename); if (tmp == NULL) tmp = lookup_acl_subject(role, subject); else { /* found a match by filename, handle 'Z' flag here */ if (subject->mode & GR_SUBJ_REPLACE) { // FIXME: we leak allocations here /* save off the old ->prev and restore it */ struct proc_acl *prev = tmp->prev; memcpy(tmp, subject, sizeof(struct proc_acl)); tmp->prev = prev; tmp->mode = subject->mode &~ GR_SUBJ_REPLACE; tmp->hash = create_hash_table(GR_HASH_OBJECT); current_subject = tmp; } } return tmp; } int add_role_acl(struct role_acl **role, const char *rolename, u_int16_t type, int ignore) { struct role_acl *rtmp; int id, ret; if (current_role && current_role->hash == NULL) { fprintf(stderr, "Error on line %lu of %s: " "Attempting to add the role \"%s\" when " "no subjects have been specified for " "the previous role \"%s\".\nThe RBAC " "system will not be allowed to be " "enabled until this error is fixed.\n", lineno, current_acl_file, rolename, current_role->rolename); exit(EXIT_FAILURE); } num_roles++; /* one for role, one for name */ num_pointers += 2; if (!rolename) { fprintf(stderr, "Out of memory.\n"); exit(EXIT_FAILURE); } rtmp = (struct role_acl *) gr_alloc(sizeof (struct role_acl)); rtmp->umask = 0; rtmp->roletype = type; rtmp->rolename = rolename; if (strcmp(rolename, "default") && (type & GR_ROLE_DEFAULT)) { fprintf(stderr, "No role type specified for %s on line %lu " "of %s.\nThe RBAC system will not be allowed to be " "enabled until this error is fixed.\n", rolename, lineno, current_acl_file); exit(EXIT_FAILURE); } if (is_role_dupe(*role, rtmp->rolename, rtmp->roletype)) { fprintf(stderr, "Duplicate role %s on line %lu of %s.\n" "The RBAC system will not be allowed to be " "enabled until this error is fixed.\n", rtmp->rolename, lineno, current_acl_file); exit(EXIT_FAILURE); } if (ignore) rtmp->uidgid = special_role_uid++; else if (strcmp(rolename, "default") || !(type & GR_ROLE_DEFAULT)) { if (type & (GR_ROLE_USER | GR_ROLE_GROUP)) { ret = get_id_from_role_name(rolename, type, &id); if (ret) { /* ignore roles for nonexistent users/groups */ rtmp->roletype |= GR_ROLE_IGNORENOEXIST; num_roles--; num_pointers -= 2; id = -1; } rtmp->uidgid = id; } else if (type & GR_ROLE_SPECIAL) { rtmp->uidgid = special_role_uid++; } } if (*role) (*role)->next = rtmp; rtmp->prev = *role; *role = rtmp; if (type & GR_ROLE_SPECIAL) add_role_transition(rtmp,rolename); if (type & GR_ROLE_AUTH) { add_gradm_acl(rtmp); add_gradm_pam_acl(rtmp); } if (!(type & GR_ROLE_SPECIAL)) add_grlearn_acl(rtmp); if (type & GR_ROLE_LEARN) add_rolelearn_acl(); return 1; } int count_slashes(const char *str) { int i = 0; while (*str) { if (*str == '/') i++; str++; } return i; } static int add_globbing_file(struct proc_acl *subject, const char *filename, u_int32_t mode, int type) { struct glob_file *glob = (struct glob_file *)gr_alloc(sizeof (struct glob_file)); glob->role = current_role; glob->subj = subject; glob->filename = filename; glob->mode = mode; glob->type = type; glob->policy_file = current_acl_file; glob->lineno = lineno; glob->next = NULL; if (!glob_files_head) { glob_files_head = glob; } else { glob_files_tail->next = glob; } glob_files_tail = glob; return 1; } int add_globbed_object_acl(struct proc_acl *subject, const char *filename, u_int32_t mode, int type, const char *policy_file, unsigned long line) { char *basepoint; char *p, *p2; struct file_acl *anchor; struct file_acl *glob, *glob2; int lnum, onum; /* one for the object itself, one for the filename */ num_pointers += 2; basepoint = get_anchor(filename); anchor = lookup_acl_object_by_name(subject, basepoint); if (!anchor) { fprintf(stderr, "Error on line %lu of %s:\n" "Object %s needs to be specified in the same subject as globbed object %s.\n" "The RBAC system will not be allowed to be enabled until this error is corrected.\n\n", line, policy_file, basepoint, filename); exit(EXIT_FAILURE); } free(basepoint); if (anchor->globbed) { glob = anchor->globbed; glob2 = (struct file_acl *)gr_alloc(sizeof(struct file_acl)); onum = count_slashes(filename); lnum = count_slashes(glob->filename); if (onum > lnum) { glob2->next = glob; anchor->globbed = glob2; glob2->filename = filename; glob2->mode = mode; glob->prev = glob2; return 1; } while (glob->next) { lnum = count_slashes(glob->next->filename); if (onum > lnum) { glob2->next = glob->next; glob->next = glob2; glob2->filename = filename; glob2->mode = mode; glob2->prev = glob; glob->next->prev = glob2; return 1; } glob = glob->next; } glob2->filename = filename; glob2->mode = mode; glob2->prev = glob; glob->next = glob2; } else { glob2 = (struct file_acl *)gr_alloc(sizeof(struct file_acl)); glob2->filename = filename; glob2->mode = mode; anchor->globbed = glob2; } return 1; } static void display_all_dupes(struct proc_acl *subject, struct file_acl *filp2) { struct file_acl *tmp; struct stat64 fstat; struct file_acl ftmp; for_each_file_object(tmp, subject) { if (get_canonical_inodev(tmp->filename, &ftmp.inode, &ftmp.dev, NULL)) { if (ftmp.inode == filp2->inode && ftmp.dev == filp2->dev) fprintf(stderr, "%s (due to symlinking/hardlinking)\n", tmp->filename); } else if (!strcmp(tmp->filename, filp2->filename)) { fprintf(stderr, "%s\n", tmp->filename); } } return; } static char * parse_homedir(const char *filename) { struct passwd *pwd; unsigned int newlen; char *newfilename; if (!(current_role->roletype & GR_ROLE_USER) || (current_role->roletype & GR_ROLE_DOMAIN)) { fprintf(stderr, "Error on line %lu of %s. $HOME " "is supported only on user roles.\n", lineno, current_acl_file); exit(EXIT_FAILURE); } pwd = getpwuid(current_role->uidgid); if (pwd == NULL) { fprintf(stderr, "Error: Unable to use $HOME on line %lu of %s" ", as it can only be used in roles for users that exist" " at the time RBAC policy is enabled.\n", lineno, current_acl_file); exit(EXIT_FAILURE); } newlen = strlen(pwd->pw_dir) + strlen(filename) - 5 + 1; newfilename = (char *)gr_alloc(newlen); if (!newfilename) { fprintf(stderr, "Out of memory.\n"); exit(EXIT_FAILURE); } strcpy(newfilename, pwd->pw_dir); strcat(newfilename, (filename + 5)); return newfilename; } int add_proc_object_acl(struct proc_acl *subject, const char *filename, u_int32_t mode, int type) { struct file_acl *p; struct file_acl *p2; struct deleted_file *dfile; const char *str; u_int64_t inode; u_int32_t dev; int is_symlink; if (!subject) { fprintf(stderr, "Error on line %lu of %s. Attempt to " "add an object without a subject declaration.\n" "The RBAC system will not load until this " "error is fixed.\n", lineno, current_acl_file); exit(EXIT_FAILURE); } if (!filename) { fprintf(stderr, "Out of memory.\n"); exit(EXIT_FAILURE); } if (!strncmp(filename, "$HOME", 5)) filename = parse_homedir(filename); else if (!strncmp(filename, "/dev/pts/", 9)) { fprintf(stderr, "Error on line %lu of %s. Grsecurity does " "not support fine-grained policy on devpts mounts.\n" "Please change your more fine-grained object to a /dev/pts " "object. This will in addition produce a better policy that " "will not break as unnecessarily.\n" "The RBAC system will not load until this " "error is fixed.\n", lineno, current_acl_file); exit(EXIT_FAILURE); } str = filename; if (!strncmp(filename, "/SYSV", 5)) return add_globbing_file(subject, filename, mode, type); while (*str) { if (*str == '?' || *str == '*') return add_globbing_file(subject, filename, mode, type); if (*str == '[') { const char *str2 = str; while (*str2) { if (*str2 == ']') return add_globbing_file(subject, filename, mode, type); str2++; } } str++; } if (!get_canonical_inodev(filename, &inode, &dev, &is_symlink)) { dfile = add_deleted_file(filename); inode = dfile->ino; dev = 0; mode |= GR_DELETED; } num_objects++; /* one for the object, one for the filename, one for the name entry struct, and one for the inodev_entry struct in the kernel*/ num_pointers += 4; p = (struct file_acl *) gr_alloc(sizeof (struct file_acl)); p->filename = filename; p->mode = mode; p->inode = inode; p->dev = dev; if (type & GR_FLEARN) { struct file_acl *tmp; tmp = lookup_acl_object_by_name(subject, p->filename); if (tmp) { tmp->mode |= mode; return 1; } tmp = lookup_acl_object(subject, p); if (tmp) { tmp->mode |= mode; return 1; } } else if ((p2 = is_proc_object_dupe(subject, p))) { if (p2->mode == mode) return 1; fprintf(stderr, "Duplicate object found for \"%s\"" " in role %s, subject %s, on line %lu of %s.\n" "\"%s\" references the same object as the following object(s):\n", p->filename, current_role->rolename, subject->filename, lineno, current_acl_file ? current_acl_file : "", p->filename); display_all_dupes(subject, p); fprintf(stderr, "specified on an earlier line.\n"); fprintf(stderr, "The RBAC system will not load until this error is fixed.\n"); exit(EXIT_FAILURE); } insert_acl_object(subject, p); if (is_symlink) add_symlink(subject, p); return 1; } int add_proc_subject_acl(struct role_acl *role, const char *filename, u_int32_t mode, int flag) { struct proc_acl *p; struct proc_acl *p2; struct deleted_file *dfile; struct stat fstat; num_subjects++; /* one for the subject, one for the filename */ num_pointers += 2; if (!role) { fprintf(stderr, "Error on line %lu of %s. Attempt to " "add a subject without a role declaration.\n" "The RBAC system will not load until this " "error is fixed.\n", lineno, current_acl_file); exit(EXIT_FAILURE); } if (!filename) { fprintf(stderr, "Out of memory.\n"); exit(EXIT_FAILURE); } if (mode & GR_LEARN && mode & GR_INHERITLEARN) { fprintf(stderr, "Error on line %lu of %s. Subject mode " "may not include both learn and inherit-learn.\n" "The RBAC system will not load until this " "error is fixed.\n", lineno, current_acl_file); exit(EXIT_FAILURE); } if (!strncmp(filename, "$HOME", 5)) filename = parse_homedir(filename); p = (struct proc_acl *) gr_alloc(sizeof (struct proc_acl)); // FIXME: for subjects we currently follow symlinks if (!get_canonical_inodev(filename, &p->inode, &p->dev, NULL)) { dfile = add_deleted_file(filename); p->inode = dfile->ino; p->dev = 0; mode |= GR_DELETED; } if (!strcmp(filename, "/") && !(flag & GR_FFAKE)) role->root_label = p; p->filename = filename; p->mode = mode; if (!(flag & GR_FFAKE) && (p2 = is_proc_subject_dupe(role, p))) { if (mode & GR_SUBJ_REPLACE) return 1; fprintf(stderr, "Duplicate subject found for \"%s\"" " in role %s, on line %lu of %s.\n" "\"%s\" references the same object as \"%s\"" " specified on an earlier line.\n" "The RBAC system will not load until this" " error is fixed.\n", p->filename, role->rolename, lineno, current_acl_file, p->filename, p2->filename); exit(EXIT_FAILURE); } /* don't insert nested subjects into main hash */ if (!(flag & GR_FFAKE)) insert_acl_subject(role, p); else insert_nested_acl_subject(p); current_subject = p; return 1; } u_int16_t role_mode_conv(const char *mode) { int len = strlen(mode) - 1; u_int16_t retmode = GR_ROLE_DEFAULT; for (; len >= 0; len--) { switch (mode[len]) { case 'u': retmode &= ~GR_ROLE_DEFAULT; retmode |= GR_ROLE_USER; break; case 'g': retmode &= ~GR_ROLE_DEFAULT; retmode |= GR_ROLE_GROUP; break; case 's': retmode &= ~GR_ROLE_DEFAULT; retmode |= GR_ROLE_SPECIAL; break; case 'l': retmode |= GR_ROLE_LEARN; break; case 'G': retmode |= GR_ROLE_AUTH; break; case 'N': retmode |= GR_ROLE_NOPW; break; case 'A': retmode |= GR_ROLE_GOD; break; case 'R': retmode |= GR_ROLE_PERSIST; break; case 'T': retmode |= GR_ROLE_TPE; break; case 'P': retmode |= GR_ROLE_PAM; break; default: fprintf(stderr, "Invalid role mode " "\'%c\' found on line %lu " "of %s\n", mode[len], lineno, current_acl_file); exit(EXIT_FAILURE); } } if ((retmode & (GR_ROLE_SPECIAL | GR_ROLE_PERSIST)) == GR_ROLE_PERSIST) { fprintf(stderr, "Error on line %lu of %s. Persistent " "roles are only valid in the context of special roles.\n" "The RBAC system will not load until this error is fixed.\n", lineno, current_acl_file); exit(EXIT_FAILURE); } if ((retmode & (GR_ROLE_NOPW | GR_ROLE_PAM)) == (GR_ROLE_NOPW | GR_ROLE_PAM)) { fprintf(stderr, "Error on line %lu of %s. The role mode must contain only one of the noauth and pamauth modes.\n", lineno, current_acl_file); exit(EXIT_FAILURE); } if ((retmode & GR_ROLE_SPECIAL) && (retmode & (GR_ROLE_USER | GR_ROLE_GROUP))) { fprintf(stderr, "Error on line %lu of %s. The role mode must be either " "special, or user/group, not both.\n" "The RBAC system will not load until this" " error is fixed.\n", lineno, current_acl_file); exit(EXIT_FAILURE); } if ((retmode & (GR_ROLE_USER | GR_ROLE_GROUP)) && (retmode & GR_ROLE_NOPW)) { fprintf(stderr, "Error on line %lu of %s. The role mode \"N\" can only " "be used with a special role.\n" "The RBAC system will not load until this" " error is fixed.\n", lineno, current_acl_file); exit(EXIT_FAILURE); } if ((retmode & (GR_ROLE_USER | GR_ROLE_GROUP)) == (GR_ROLE_USER | GR_ROLE_GROUP)) { fprintf(stderr, "Error on line %lu of %s. The role mode cannot be both " "user or group, you must choose one.\n" "The RBAC system will not load until this" " error is fixed.\n", lineno, current_acl_file); exit(EXIT_FAILURE); } return retmode; } u_int32_t proc_subject_mode_conv(const char *mode) { int i; u_int32_t retmode = 0; retmode |= GR_PROCFIND; for (i = 0; i < strlen(mode); i++) { switch (mode[i]) { case 'T': retmode |= GR_NOTROJAN; break; case 'K': retmode |= GR_KILLPROC; break; case 'C': retmode |= GR_KILLIPPROC; break; case 'A': retmode |= GR_PROTSHM; break; case 'O': retmode |= GR_IGNORE; break; case 'Z': retmode |= GR_SUBJ_REPLACE; break; case 'o': retmode |= GR_OVERRIDE; break; case 't': retmode |= GR_POVERRIDE; break; case 'l': retmode |= GR_LEARN; break; case 'h': retmode &= ~GR_PROCFIND; break; case 'p': retmode |= GR_PROTECTED; break; case 'k': retmode |= GR_KILL; break; case 'v': retmode |= GR_VIEW; break; case 'd': retmode |= GR_PROTPROCFD; break; case 'b': retmode |= GR_PROCACCT; break; case 'r': retmode |= GR_RELAXPTRACE; break; case 'i': retmode |= GR_INHERITLEARN; break; case 'a': retmode |= GR_KERNELAUTH; break; case 's': retmode |= GR_ATSECURE; break; case 'x': retmode |= GR_SHMEXEC; break; default: fprintf(stderr, "Invalid subject mode " "\'%c\' found on line %lu " "of %s\n", mode[i], lineno, current_acl_file); exit(EXIT_FAILURE); } } return retmode; } u_int32_t proc_object_mode_conv(const char *mode) { int i; u_int32_t retmode = 0; retmode |= GR_FIND; for (i = 0; i < strlen(mode); i++) { switch (mode[i]) { case 'r': retmode |= GR_READ; break; case 'w': retmode |= GR_WRITE; retmode |= GR_APPEND; break; case 'c': retmode |= GR_CREATE; break; case 'd': retmode |= GR_DELETE; break; case 'x': retmode |= GR_EXEC; break; case 'a': retmode |= GR_APPEND; break; case 'h': retmode &= ~GR_FIND; break; case 'i': retmode |= GR_INHERIT; break; case 't': retmode |= GR_PTRACERD; break; case 'l': retmode |= GR_LINK; break; case 'Z': retmode |= GR_OBJ_REPLACE; break; case 'F': retmode |= GR_AUDIT_FIND; break; case 'R': retmode |= GR_AUDIT_READ; break; case 'W': retmode |= GR_AUDIT_WRITE; retmode |= GR_AUDIT_APPEND; break; case 'X': retmode |= GR_AUDIT_EXEC; break; case 'A': retmode |= GR_AUDIT_APPEND; break; case 'I': retmode |= GR_AUDIT_INHERIT; break; case 'M': retmode |= GR_AUDIT_SETID; break; case 'C': retmode |= GR_AUDIT_CREATE; break; case 'D': retmode |= GR_AUDIT_DELETE; break; case 'L': retmode |= GR_AUDIT_LINK; break; case 's': retmode |= GR_SUPPRESS; break; case 'f': if (!(current_role->roletype & GR_ROLE_PERSIST)) { fprintf(stderr, "Error on line %lu of " "%s. The 'f' mode is only permitted " "within persistent special roles.\n" "The RBAC system will not be allowed to " "be enabled until this error is corrected.\n", lineno, current_acl_file); exit(EXIT_FAILURE); } retmode |= GR_INIT_TRANSFER; break; case 'm': retmode |= GR_SETID; break; case 'p': retmode |= GR_NOPTRACE; break; default: fprintf(stderr, "Invalid proc object mode " "\'%c\' found on line %lu " "of %s\n", mode[i], lineno, current_acl_file); } } return retmode; } void parse_acls(void) { if (chdir(GRSEC_DIR) < 0) { fprintf(stderr, "Error changing directory to %s\n" "Error: %s\n", GRSEC_DIR, strerror(errno)); exit(EXIT_FAILURE); } gradmin = open_acl_file(GR_POLICY_PATH); change_current_acl_file(GR_POLICY_PATH); gradmparse(); add_kernel_acl(); return; } static void setup_special_roles(struct gr_arg *grarg) { struct role_acl *rtmp = NULL; struct gr_pw_entry entry; int err; u_int16_t i = 0; memset(&entry, 0, sizeof (struct gr_pw_entry)); err = mlock(&entry, sizeof (struct gr_pw_entry)); if (err && !getuid()) fprintf(stderr, "Warning, unable to lock authentication " "structure in physical memory.\n"); for_each_role(rtmp, current_role) { if (rtmp->roletype & GR_ROLE_SPECIAL && !(rtmp->roletype & (GR_ROLE_NOPW | GR_ROLE_PAM))) { strncpy((char *)entry.rolename, rtmp->rolename, GR_SPROLE_LEN); entry.rolename[GR_SPROLE_LEN - 1] = '\0'; if (!read_saltandpass (entry.rolename, entry.salt, entry.sum)) { fprintf(stderr, "No password exists for special " "role %s.\nRun gradm -P %s to set up a password " "for the role.\n", rtmp->rolename, rtmp->rolename); exit(EXIT_FAILURE); } grarg->sprole_pws[i].rolename = (const unsigned char *) rtmp->rolename; memcpy(grarg->sprole_pws[i].salt, entry.salt, GR_SALT_SIZE); memcpy(grarg->sprole_pws[i].sum, entry.sum, GR_SHA_SUM_SIZE); memset(&entry, 0, sizeof (struct gr_pw_entry)); i++; } } return; } struct gr_arg_wrapper * conv_user_to_kernel(struct gr_pw_entry *entry) { struct gr_arg_wrapper *wrapper; struct gr_arg *retarg; struct user_acl_role_db *role_db; struct role_acl *rtmp = NULL; struct role_acl **r_tmp = NULL; unsigned long racls = 0; u_int16_t sproles = 0; int err; for_each_role(rtmp, current_role) { racls++; if (rtmp->roletype & GR_ROLE_SPECIAL && !(rtmp->roletype & (GR_ROLE_NOPW | GR_ROLE_PAM))) sproles++; } retarg = (struct gr_arg *) gr_alloc(sizeof (struct gr_arg)); wrapper = (struct gr_arg_wrapper *) gr_alloc(sizeof (struct gr_arg_wrapper)); wrapper->version = GRADM_VERSION; wrapper->size = sizeof(struct gr_arg); wrapper->arg = retarg; err = mlock(retarg, sizeof (struct gr_arg)); if (err && !getuid()) fprintf(stderr, "Warning, unable to lock authentication " "structure in physical memory.\n"); if (!racls) // we are disabling, don't want to calloc 0 goto set_pw; retarg->sprole_pws = (struct sprole_pw *) gr_alloc(sproles * sizeof (struct sprole_pw)); err = mlock(retarg->sprole_pws, sproles * sizeof (struct sprole_pw)); if (err && !getuid()) fprintf(stderr, "Warning, unable to lock authentication " "structure in physical memory.\n"); setup_special_roles(retarg); retarg->num_sprole_pws = sproles; role_db = (struct user_acl_role_db *) gr_alloc(sizeof (struct user_acl_role_db)); role_db->num_pointers = num_pointers; role_db->num_roles = num_roles; role_db->num_domain_children = num_domain_children; role_db->num_subjects = num_subjects; role_db->num_objects = num_objects; if (racls >= ULONG_MAX/sizeof(struct role_acl *)) { fprintf(stderr, "Too many roles.\n"); exit(EXIT_FAILURE); } r_tmp = role_db->r_table = (struct role_acl **) gr_alloc(racls * sizeof (struct role_acl *)); for_each_role(rtmp, current_role) { if (!(rtmp->roletype & GR_ROLE_IGNORENOEXIST)) { *r_tmp = rtmp; r_tmp++; } } memcpy(&retarg->role_db, role_db, sizeof (struct user_acl_role_db)); set_pw: strncpy((char *)retarg->pw, (char *)entry->passwd, GR_PW_LEN - 1); retarg->pw[GR_PW_LEN - 1] = '\0'; strncpy((char *)retarg->sp_role, (char *)entry->rolename, GR_SPROLE_LEN); retarg->sp_role[GR_SPROLE_LEN - 1] = '\0'; retarg->mode = entry->mode; retarg->segv_inode = entry->segv_inode; retarg->segv_dev = entry->segv_dev; retarg->segv_uid = entry->segv_uid; memset(entry, 0, sizeof (struct gr_pw_entry)); return wrapper; } gradm/gradm_replace.c0000644000175000017510000000574413152754421015002 0ustar spenderspender/* * Copyright (C) 2002-2014 Bradley Spengler, Open Source Security, Inc. * http://www.grsecurity.net spender@grsecurity.net * * This file is part of gradm. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "gradm.h" typedef struct _replace_string_entry { struct _replace_string_entry *next; const char *name; char *replacewith; } replace_string_entry; static replace_string_entry *replace_list; char *lookup_replace_string(const char *name) { replace_string_entry *tmp; for (tmp = replace_list; tmp; tmp = tmp->next) { if (!strcmp(tmp->name, name)) return tmp->replacewith; } return NULL; } /* called with already strdup'd strings */ void add_replace_string(const char *name, char *replacewith) { replace_string_entry *entry; replace_string_entry *tmp; /* replace an existing entry if the name is redefined */ for (entry = replace_list; entry; entry = entry->next) { if (!strcmp(entry->name, name)) { free(entry->replacewith); entry->replacewith = replacewith; return; } if (entry->next == NULL) break; } tmp = (replace_string_entry *)gr_alloc(sizeof(replace_string_entry)); tmp->next = NULL; tmp->name = name; tmp->replacewith = replacewith; if (replace_list == NULL) replace_list = tmp; if (entry != NULL) entry->next = tmp; return; } /* returns newly allocated string */ char *process_string_replace(const char *str) { char *p, *p2; char *replacewith; char *newstr; unsigned int newlen; p = strstr(str, "$("); if (p == NULL) goto normal_str; p2 = strchr(p, ')'); if (p2 == NULL) { fprintf(stderr, "Error: Missing terminating \")\" for symbol on line %ld " "of %s.\n", lineno, current_acl_file); exit(EXIT_FAILURE); } *p2 = '\0'; replacewith = lookup_replace_string(p + 2); if (replacewith == NULL) { fprintf(stderr, "Error: Undefined symbol \"%s\" on line %ld " "of %s.\n", p + 2, lineno, current_acl_file); exit(EXIT_FAILURE); } *p = '\0'; newlen = strlen(str) + strlen(p2 + 1) + strlen(replacewith); newstr = (char *)gr_alloc(newlen + 1); strcpy(newstr, str); strcat(newstr, replacewith); strcat(newstr, p2 + 1); newstr[newlen] = '\0'; *p = '$'; *p2 = ')'; if (newstr[0] != '/' && (newlen < 5 || strncmp(newstr, "$HOME", 5))) { fprintf(stderr, "Error: Malformed path on line %ld of %s.\n", lineno, current_acl_file); exit(EXIT_FAILURE); } return newstr; normal_str: return strdup(str); } gradm/gradm_learn_pass1.l0000644000175000017510000000366213152754421015605 0ustar spenderspender%{ /* * Copyright (C) 2002-2014 Bradley Spengler, Open Source Security, Inc. * http://www.grsecurity.net spender@grsecurity.net * * This file is part of gradm. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "gradm.h" #include "learn_pass1.tab.h" void learn_pass1error(const char *s); int learn_pass1wrap(void); static struct in_addr ip; %} ROLENAME ^[_a-zA-Z0-9.-]{1,64} NOTAFILE [a-z]+"["[0-9]+"]" NUM [-]?[0-9]+ FILENAME [/][^\t\n]* IPADDR [0-9]{1,3}"."[0-9]{1,3}"."[0-9]{1,3}"."[0-9]{1,3} %option nounput %x ERROR %% "u" { learn_pass1lval.num = USER; return USER; } "g" { learn_pass1lval.num = GROUP; return GROUP; } {NUM} { learn_pass1lval.num = atol(yytext); return NUM; } {NOTAFILE} { learn_pass1lval.string = gr_strdup("/proc"); return FILENAME; } {ROLENAME} { learn_pass1lval.string = gr_strdup(yytext); return ROLENAME; } {FILENAME} { learn_pass1lval.string = gr_strdup(yytext); return FILENAME; } {IPADDR} { if (inet_aton(yytext, &ip)) learn_pass1lval.num = ip.s_addr; else learn_pass1lval.num = 0; return IPADDR; } [\t] { return ':'; } . ; %% void learn_pass1error(const char *s) { return; } int learn_pass1wrap(void) { return 1; } gradm/gradm_fulllearn_pass3.l0000644000175000017510000000222213152754421016461 0ustar spenderspender%{ #include "gradm.h" #include "fulllearn_pass3.tab.h" void fulllearn_pass3error(const char *s); int fulllearn_pass3wrap(void); static struct in_addr ip; %} ROLENAME ^[_a-zA-Z0-9.-]{1,64} NOTAFILE [a-z]+"["[0-9]+"]" NUM [-]?[0-9]+ FILENAME [/][^\t\n]* IPADDR [0-9]{1,3}"."[0-9]{1,3}"."[0-9]{1,3}"."[0-9]{1,3} %option nounput %x ERROR %% "u" { fulllearn_pass3lval.num = USER; return USER; } "g" { fulllearn_pass3lval.num = GROUP; return GROUP; } {NUM} { fulllearn_pass3lval.num = atol(yytext); return NUM; } {NOTAFILE} { fulllearn_pass3lval.string = gr_strdup("/proc"); return FILENAME; } {ROLENAME} { /* not used in grammar */ return ROLENAME; } {FILENAME} { fulllearn_pass3lval.string = gr_strdup(yytext); return FILENAME; } {IPADDR} { if (inet_aton(yytext, &ip)) fulllearn_pass3lval.num = ip.s_addr; else fulllearn_pass3lval.num = 0; return IPADDR; } [\t] { return ':'; } . ; %% void fulllearn_pass3error(const char *s) { return; } int fulllearn_pass3wrap(void) { return 1; } gradm/gradm.80000644000175000017510000000563613152754421013234 0ustar spenderspender.TH GRADM 8 .SH NAME gradm \- Administration program for the grsecurity RBAC system .SH SYNOPSIS .B gradm [ .B \-E ] [ .B \-R ] [ .B \-C ] [ .B \-F ] [ .B \-L ] [ .B \-O ] [ .B \-M ] [ .B \-D ] [ .B \-P [rolename] ] [ .B \-a ] [ .B \-n ] [ .B \-p ] [ .B \-u ] [ .B \-V ] [ .B \-h ] [ .B \-v ] .SH DESCRIPTION .I gradm is the userspace RBAC parsing and authentication program for .I grsecurity grsecurity aims to be a complete security system for Linux 2.4. gradm performs several tasks for the RBAC system including authenticated via a password to the kernel and parsing rules to be passed to the kernel. .SH OPTIONS .TP All options to gradm are mutually exclusive, except for -L and -O. .TP .B \-E Enable the RBAC system .TP .B \-R Reload the RBAC system (only valid while in admin mode) .TP .B \-C Perform a check of the RBAC policy, running the same analysis against it that is performed when enabling. .TP .B \-F Toggle full learning mode. If used only with -L, it enables the RBAC system in full learning mode. If used with -L and -O, it parses the full learning logs and generates a complete ruleset. .TP .B \-M Remove an execution ban on a given uid or filename that has been put in place by the RES_CRASH resource restriction of the RBAC system. .TP .B \-L Parses the learning logs. Accepts an argument which specifies the logfile to scan for the learning logs. If "-" is specified as the logfile, stdin will be used as the learning log. This option can be used with -E, -O, or -F. .TP .B \-O Specifies output mode. Requires a single argument that can be "stdout", "stderr", or a regular file. Only used with -L or -F. .TP .B \-D Disable the RBAC system .TP .B \-P [rolename] Without an argument, it sets the password for administering the RBAC system. With a role name as an argument, it sets the password for that given special role. .TP .B \-a Authenticate to a special role that requires a password. .TP .B \-n Authenticate to a special role that does not require a password. .TP .B \-p Authenticate through PAM to a special role. .TP .B \-u Removes yourself from your current special role, reverting back to the normal role selection. To be used, for instance, for logging out of an admin role without exiting your shell. .TP .B \-V Displays verbose policy statistics when enabling the RBAC system or checking the RBAC policy. Can only be used with -C, -E, or -F -L .TP .B \-h Display help information .TP .B \-v Print version information and exit .TP .BR .SH REPORTING BUGS Please include as much information as possible(using any available debugging options) and send bug reports for gradm or the grsecurity RBAC system to .B spender@grsecurity.net. .SH AUTHOR .B grsecurity and gradm were created and are maintained by Brad Spengler gradm/learn_config0000644000175000017510000001564113152754421014417 0ustar spenderspender#This configuration file aids the learning process by tweaking #the learning algorithm for specific paths. # #It accepts lines in the form of #Where can be inherit-learn, no-learn, inherit-no-learn, #high-reduce-path, dont-reduce-path, protected-path, high-protected-path, #read-protected-path, and always-reduce-path # #inherit-learn, no-learn, and inherit-no-learn operate only with #full learning # #high-reduce-path, dont-reduce-path, always-reduce-path, protected-path, #and high-protected-path operate on both full and and regular learning #(subject and role learning) # #inherit-learn changes the learning process for the specified path #by throwing all learned accesses for every binary executed by the #processes contained in the pathname into the subject specified #by the pathname. This is useful for cron in the case of full #system learning, so that scripts that eventually end up executing #mv or rm with privilege don't cause the root policy to grant #that privilege to mv or rm in all cases. # #no-learn allows processes within the path to perform any operation #that normal system usage would allow without restriction. If #a process is generating a huge number of learning logs, it may be #best to use this command on that process and configure its policy #manually. # #inherit-no-learn combines the above two cases, such that processes #within the specified path will be able to perform any normal system #operation without restriction as will any binaries executed by #these processes. # #high-reduce-path modifies the heuristics of the learning process #to weight in favor of reducing accesses for this path # #dont-reduce-path modifies the heuristics of the learning process #so that it will never reduce accesses for this path # #always-reduce-path modifies the heuristics of the learning process #so that the path specified will always have all files and directories #within it reduced to the path specified. # #protected-path specifies a path on your system that is considered an #important resource. Any process that modifies one of these paths #is given its own subject in the learning process, facilitating #a secure policy. # #read-protected-path specifies a path on your system that contains #sensitive information. Any process that reads one of these paths is #given its own subject in the learning process, facilitating a secure #policy. # #high-protected-path specifies a path that should be hidden from #all processes but those that access it directly. It is recommended #to use highly sensitive files for this command. # #regular expressions are not supported for pathnames in this config file # # # uncomment this next line if you don't wish to generate a policy that # restricts roles to specific IP ranges: # dont-learn-allowed-ips # # to write out your generated policy such that roles are split into separate # files by the name of the role (within user/group directories), uncomment # the next line: # split-roles always-reduce-path /dev/pts always-reduce-path /var/spool/qmailscan/tmp always-reduce-path /var/spool/exim4 always-reduce-path /var/run/screen always-reduce-path /usr/share/locale always-reduce-path /usr/share/zoneinfo always-reduce-path /usr/share/terminfo always-reduce-path /usr/portage always-reduce-path /tmp always-reduce-path /var/tmp high-reduce-path /dev/.udev high-reduce-path /dev/mapper high-reduce-path /dev/snd high-reduce-path /proc high-reduce-path /lib high-reduce-path /lib32 high-reduce-path /libx32 high-reduce-path /lib64 high-reduce-path /lib/tls high-reduce-path /lib32/tls high-reduce-path /libx32/tls high-reduce-path /lib64/tls high-reduce-path /lib/security high-reduce-path /lib/modules high-reduce-path /lib32/modules high-reduce-path /lib64/modules high-reduce-path /usr/lib high-reduce-path /usr/lib32 high-reduce-path /usr/libx32 high-reduce-path /usr/lib64 high-reduce-path /usr/lib/tls high-reduce-path /usr/lib32/tls high-reduce-path /usr/libx32/tls high-reduce-path /usr/lib64/tls high-reduce-path /usr/lib64/openoffice high-reduce-path /var/lib high-reduce-path /usr/bin high-reduce-path /usr/sbin high-reduce-path /sbin high-reduce-path /bin high-reduce-path /usr/local/share high-reduce-path /usr/local/bin high-reduce-path /usr/local/sbin high-reduce-path /usr/local/etc high-reduce-path /usr/local/lib high-reduce-path /usr/share high-reduce-path /usr/X11R6/lib high-reduce-path /var/lib/openldap-data high-reduce-path /var/lib/krb5kdc dont-reduce-path / dont-reduce-path /home dont-reduce-path /dev dont-reduce-path /usr dont-reduce-path /var dont-reduce-path /opt protected-path /etc protected-path /lib protected-path /boot protected-path /run protected-path /usr protected-path /opt protected-path /var protected-path /dev/log protected-path /root protected-path /sys read-protected-path /etc/ssh read-protected-path /proc/kallsyms read-protected-path /proc/kcore read-protected-path /proc/slabinfo read-protected-path /proc/modules read-protected-path /lib/modules read-protected-path /lib64/modules read-protected-path /boot read-protected-path /etc/shadow read-protected-path /etc/shadow- read-protected-path /etc/gshadow read-protected-path /etc/gshadow- read-protected-path /sys high-protected-path /etc/ssh high-protected-path /proc/kcore high-protected-path /proc/sys high-protected-path /proc/bus high-protected-path /proc/slabinfo high-protected-path /proc/modules high-protected-path /proc/kallsyms high-protected-path /etc/passwd high-protected-path /etc/shadow high-protected-path /var/backups high-protected-path /etc/shadow- high-protected-path /etc/gshadow high-protected-path /etc/gshadow- high-protected-path /var/log high-protected-path /dev/mem high-protected-path /dev/kmem high-protected-path /dev/port high-protected-path /dev/log high-protected-path /sys high-protected-path /etc/ppp high-protected-path /etc/samba/smbpasswd # to protect kernel images high-protected-path /boot high-protected-path /lib/modules high-protected-path /lib64/modules high-protected-path /usr/src inherit-learn /etc/cron.d inherit-learn /etc/cron.hourly inherit-learn /etc/cron.daily inherit-learn /etc/cron.weekly inherit-learn /etc/cron.monthly #It is important that software updates be performed manually by someone in #an admin role, not performed automatically via cron jobs #With just the /etc/cron.daily rule above, a policy will be generated that #allows the automatic package updater script to update services and #restart them. With its inherit rules, this would also cause the services #to be restarted with the ability to update packages, etc. #This rule below makes sure for the case of apt-based auto-updates that #no learning is performed for this behavior, to force the admin to deal with #this in some way inherit-no-learn /etc/cron.daily/apt # the below lines are for catching the occasional use of init.d scripts at runtime # comment them out if you are starting learning before services are started by init # (a highly non-recommended choice) inherit-learn /etc/init.d inherit-learn /etc/rc.d/init.d gradm/gradm_learn_pass2.l0000644000175000017510000000366213152754421015606 0ustar spenderspender%{ /* * Copyright (C) 2002-2014 Bradley Spengler, Open Source Security, Inc. * http://www.grsecurity.net spender@grsecurity.net * * This file is part of gradm. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "gradm.h" #include "learn_pass2.tab.h" void learn_pass2error(const char *s); int learn_pass2wrap(void); static struct in_addr ip; %} ROLENAME ^[_a-zA-Z0-9.-]{1,64} NOTAFILE [a-z]+"["[0-9]+"]" NUM [-]?[0-9]+ FILENAME [/][^\t\n]* IPADDR [0-9]{1,3}"."[0-9]{1,3}"."[0-9]{1,3}"."[0-9]{1,3} %option nounput %x ERROR %% "u" { learn_pass2lval.num = USER; return USER; } "g" { learn_pass2lval.num = GROUP; return GROUP; } {NUM} { learn_pass2lval.num = atol(yytext); return NUM; } {NOTAFILE} { learn_pass2lval.string = gr_strdup("/proc"); return FILENAME; } {ROLENAME} { learn_pass2lval.string = gr_strdup(yytext); return ROLENAME; } {FILENAME} { learn_pass2lval.string = gr_strdup(yytext); return FILENAME; } {IPADDR} { if (inet_aton(yytext, &ip)) learn_pass2lval.num = ip.s_addr; else learn_pass2lval.num = 0; return IPADDR; } [\t] { return ':'; } . ; %% void learn_pass2error(const char *s) { return; } int learn_pass2wrap(void) { return 1; } gradm/gradm_globals.c0000644000175000017510000000311713152754421015002 0ustar spenderspender/* * Copyright (C) 2002-2014 Bradley Spengler, Open Source Security, Inc. * http://www.grsecurity.net spender@grsecurity.net * * This file is part of gradm. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "gradm.h" struct glob_file *glob_files_head; struct glob_file *glob_files_tail; struct symlink *symlinks; struct proc_acl *global_nested_subject_list; struct deleted_file *deleted_files; struct role_acl *current_role; struct proc_acl *current_subject; char *current_acl_file; int is_24_kernel; uid_t special_role_uid; u_int32_t num_subjects; u_int32_t num_roles; u_int32_t num_objects; u_int32_t num_pointers; u_int32_t num_domain_children; char *current_learn_rolename; char *current_learn_subject; u_int16_t current_learn_rolemode; char ** dont_reduce_dirs; char ** always_reduce_dirs; char ** protected_paths; char ** read_protected_paths; char ** high_reduce_dirs; char ** high_protected_paths; u_int32_t grlearn_options; char *output_log; char *learn_log_buffer; gradm/gradm_human.c0000644000175000017510000000777613152754421014506 0ustar spenderspender/* * Copyright (C) 2002-2014 Bradley Spengler, Open Source Security, Inc. * http://www.grsecurity.net spender@grsecurity.net * * This file is part of gradm. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "gradm.h" static struct role_name_table { u_int16_t modeint; char modechar; } role_mode_table[] = { { GR_ROLE_USER, 'u'}, { GR_ROLE_GROUP, 'g'}, { GR_ROLE_SPECIAL, 's'}, { GR_ROLE_AUTH, 'G'}, { GR_ROLE_NOPW, 'N'}, { GR_ROLE_GOD, 'A'}, { GR_ROLE_TPE, 'T'}, { GR_ROLE_PERSIST, 'R'}, { GR_ROLE_PAM, 'P'} }; static struct mode_name_table { u_int32_t modeint; char modechar; } mode_table[] = { { GR_READ, 'r'}, { GR_EXEC, 'x'}, { GR_WRITE, 'w'}, { GR_APPEND, 'a'}, { GR_INHERIT, 'i'}, { GR_PTRACERD, 't'}, { GR_SETID, 'm'}, { GR_CREATE, 'c'}, { GR_DELETE, 'd'}, { GR_LINK, 'l'}, { GR_AUDIT_FIND, 'F'}, { GR_AUDIT_READ, 'R'}, { GR_AUDIT_WRITE, 'W'}, { GR_AUDIT_EXEC, 'X'}, { GR_AUDIT_APPEND, 'A'}, { GR_AUDIT_INHERIT, 'I'}, { GR_AUDIT_SETID, 'M'}, { GR_AUDIT_CREATE, 'C'}, { GR_AUDIT_DELETE, 'D'}, { GR_AUDIT_LINK, 'L'}, { GR_SUPPRESS, 's'}, { GR_NOPTRACE, 'p'}, { GR_INIT_TRANSFER, 'f'}, { GR_FIND, 'h'} }; static struct subj_mode_name_table { u_int32_t modeint; char modechar; } subj_mode_table[] = { { GR_OVERRIDE, 'o'}, { GR_KILL, 'k'}, { GR_PROTECTED, 'p'}, { GR_VIEW, 'v'}, { GR_IGNORE, 'O'}, { GR_PROCFIND, 'h'}, { GR_PROTSHM, 'A'}, { GR_KILLPROC, 'K'}, { GR_KILLIPPROC, 'C'}, { GR_NOTROJAN, 'T'}, { GR_PROTPROCFD, 'd'}, { GR_PROCACCT, 'b'}, { GR_RELAXPTRACE, 'r'}, { GR_LEARN, 'l'}, { GR_INHERITLEARN, 'i'}, { GR_POVERRIDE, 't'}, { GR_KERNELAUTH, 'a'}, { GR_ATSECURE, 's'}, { GR_SHMEXEC, 'x'} }; void conv_mode_to_str(u_int32_t mode, char *modestr, unsigned short len) { unsigned short i; unsigned short x; memset(modestr, 0, len); for (x = 0, i = 0; i < len && x < (sizeof (mode_table) / sizeof (struct mode_name_table)); x++) { if (mode_table[x].modeint == GR_WRITE && (mode & GR_WRITE)) { modestr[i] = 'w'; mode &= ~GR_APPEND; i++; continue; } if (mode_table[x].modeint == GR_AUDIT_WRITE && (mode & GR_AUDIT_WRITE)) { modestr[i] = 'W'; mode &= ~GR_AUDIT_APPEND; i++; continue; } if (mode_table[x].modeint == GR_FIND && !(mode & GR_FIND)) { modestr[i] = 'h'; i++; continue; } else if (mode_table[x].modeint == GR_FIND) continue; if (mode & mode_table[x].modeint) { modestr[i] = mode_table[x].modechar; i++; } } return; } void conv_subj_mode_to_str(u_int32_t mode, char *modestr, unsigned short len) { unsigned short i; unsigned short x; memset(modestr, 0, len); for (x = 0, i = 0; i < len && x < (sizeof (subj_mode_table) / sizeof (struct subj_mode_name_table)); x++) { if (subj_mode_table[x].modeint == GR_PROCFIND && !(mode & GR_PROCFIND)) { modestr[i] = 'h'; i++; continue; } else if (subj_mode_table[x].modeint == GR_PROCFIND) continue; if (mode & subj_mode_table[x].modeint) { modestr[i] = subj_mode_table[x].modechar; i++; } } return; } void conv_role_mode_to_str(u_int16_t mode, char *modestr, unsigned short len) { unsigned short i; unsigned short x; memset(modestr, 0, len); for (x = 0, i = 0; i < len && x < (sizeof (role_mode_table) / sizeof (struct role_name_table)); x++) { if (mode & role_mode_table[x].modeint) { modestr[i] = role_mode_table[x].modechar; i++; } } return; } gradm/LICENSE0000644000175000017510000003556413152754421013061 0ustar spenderspender GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS gradm/gradm_sha256.c0000644000175000017510000001602013152754421014364 0ustar spenderspender#include "gradm.h" /* digest-sha256.c,v 1.13 2002/10/02 22:02:08 hvr Exp $ * * SHA-256 code by Jean-Luc Cooke . * * Glue code originally by Andrew McDonald and Alan Smithee, mailed * to maintainer on pulped trees. * * This file is part of gradm. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ typedef struct { u_int32_t state[8]; u_int32_t count[2]; u_int8_t buf[128]; } sha256_ctx_t; static __inline__ u_int32_t generic_rotr32(const u_int32_t x, const unsigned bits) { const unsigned n = bits % 32; return (x >> n) | (x << (32 - n)); } #define Ch(x,y,z) ((x & y) ^ (~x & z)) #define Maj(x,y,z) ((x & y) ^ ( x & z) ^ (y & z)) #define RORu32(x,y) generic_rotr32(x, y) #define e0(x) (RORu32(x, 2) ^ RORu32(x,13) ^ RORu32(x,22)) #define e1(x) (RORu32(x, 6) ^ RORu32(x,11) ^ RORu32(x,25)) #define s0(x) (RORu32(x, 7) ^ RORu32(x,18) ^ (x >> 3)) #define s1(x) (RORu32(x,17) ^ RORu32(x,19) ^ (x >> 10)) #define H0 0x6a09e667 #define H1 0xbb67ae85 #define H2 0x3c6ef372 #define H3 0xa54ff53a #define H4 0x510e527f #define H5 0x9b05688c #define H6 0x1f83d9ab #define H7 0x5be0cd19 static const u_int32_t sha256_K[64] = { 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 }; #define LOAD_OP(I)\ {\ t1 = input[(4*I) ] & 0xff; t1<<=8;\ t1 |= input[(4*I)+1] & 0xff; t1<<=8;\ t1 |= input[(4*I)+2] & 0xff; t1<<=8;\ t1 |= input[(4*I)+3] & 0xff;\ W[I] = t1;\ } #define BLEND_OP(I) W[I] = s1(W[I-2]) + W[I-7] + s0(W[I-15]) + W[I-16]; static void SHA256Transform(u_int32_t * state, const u_int8_t * input) { u_int32_t a, b, c, d, e, f, g, h, t1, t2; u_int32_t W[64]; int i; /* load the input */ LOAD_OP(0); LOAD_OP(1); LOAD_OP(2); LOAD_OP(3); LOAD_OP(4); LOAD_OP(5); LOAD_OP(6); LOAD_OP(7); LOAD_OP(8); LOAD_OP(9); LOAD_OP(10); LOAD_OP(11); LOAD_OP(12); LOAD_OP(13); LOAD_OP(14); LOAD_OP(15); /* now blend */ for (i = 16; i < 64; i += 8) { BLEND_OP(i); BLEND_OP(i + 1); BLEND_OP(i + 2); BLEND_OP(i + 3); BLEND_OP(i + 4); BLEND_OP(i + 5); BLEND_OP(i + 6); BLEND_OP(i + 7); } /* load the state into our registers */ a = state[0]; b = state[1]; c = state[2]; d = state[3]; e = state[4]; f = state[5]; g = state[6]; h = state[7]; /* now iterate */ for (i = 0; i < 64; i += 8) { t1 = h + e1(e) + Ch(e, f, g) + sha256_K[i] + W[i]; t2 = e0(a) + Maj(a, b, c); d += t1; h = t1 + t2; t1 = g + e1(d) + Ch(d, e, f) + sha256_K[i + 1] + W[i + 1]; t2 = e0(h) + Maj(h, a, b); c += t1; g = t1 + t2; t1 = f + e1(c) + Ch(c, d, e) + sha256_K[i + 2] + W[i + 2]; t2 = e0(g) + Maj(g, h, a); b += t1; f = t1 + t2; t1 = e + e1(b) + Ch(b, c, d) + sha256_K[i + 3] + W[i + 3]; t2 = e0(f) + Maj(f, g, h); a += t1; e = t1 + t2; t1 = d + e1(a) + Ch(a, b, c) + sha256_K[i + 4] + W[i + 4]; t2 = e0(e) + Maj(e, f, g); h += t1; d = t1 + t2; t1 = c + e1(h) + Ch(h, a, b) + sha256_K[i + 5] + W[i + 5]; t2 = e0(d) + Maj(d, e, f); g += t1; c = t1 + t2; t1 = b + e1(g) + Ch(g, h, a) + sha256_K[i + 6] + W[i + 6]; t2 = e0(c) + Maj(c, d, e); f += t1; b = t1 + t2; t1 = a + e1(f) + Ch(f, g, h) + sha256_K[i + 7] + W[i + 7]; t2 = e0(b) + Maj(b, c, d); e += t1; a = t1 + t2; } state[0] += a; state[1] += b; state[2] += c; state[3] += d; state[4] += e; state[5] += f; state[6] += g; state[7] += h; /* clear any sensitive info... */ a = b = c = d = e = f = g = h = t1 = t2 = 0; memset(W, 0, 64 * sizeof (u_int32_t)); } static void SHA256Init(sha256_ctx_t * C) { C->state[0] = H0; C->state[1] = H1; C->state[2] = H2; C->state[3] = H3; C->state[4] = H4; C->state[5] = H5; C->state[6] = H6; C->state[7] = H7; C->count[0] = C->count[1] = 0; memset(C->buf, 0, 128); } static void SHA256Update(sha256_ctx_t * C, const u_int8_t * input, u_int32_t inputLen) { u_int32_t i, index, partLen; /* Compute number of bytes mod 128 */ index = (u_int32_t) ((C->count[0] >> 3) & 0x3f); /* Update number of bits */ if ((C->count[0] += (inputLen << 3)) < (inputLen << 3)) { C->count[1]++; C->count[1] += (inputLen >> 29); } partLen = 64 - index; /* Transform as many times as possible. */ if (inputLen >= partLen) { memcpy(&C->buf[index], input, partLen); SHA256Transform(C->state, C->buf); for (i = partLen; i + 63 < inputLen; i += 64) SHA256Transform(C->state, &input[i]); index = 0; } else { i = 0; } /* Buffer remaining input */ memcpy(&C->buf[index], &input[i], inputLen - i); } static void SHA256Final(sha256_ctx_t * C, u_int8_t * digest) { static const u_int8_t padding[64] = { 0x80, }; u_int8_t bits[8]; u_int32_t t, index, padLen; int i, j; /* Save number of bits */ t = C->count[0]; bits[7] = t; t >>= 8; bits[6] = t; t >>= 8; bits[5] = t; t >>= 8; bits[4] = t; t = C->count[1]; bits[3] = t; t >>= 8; bits[2] = t; t >>= 8; bits[1] = t; t >>= 8; bits[0] = t; /* Pad out to 56 mod 64. */ index = (C->count[0] >> 3) & 0x3f; padLen = (index < 56) ? (56 - index) : ((64 + 56) - index); SHA256Update(C, padding, padLen); /* Append length (before padding) */ SHA256Update(C, bits, sizeof (bits)); /* Store state in digest */ for (i = j = 0; i < 8; i++, j += 4) { t = C->state[i]; digest[j + 3] = t; t >>= 8; digest[j + 2] = t; t >>= 8; digest[j + 1] = t; t >>= 8; digest[j] = t; } /* Zeroize sensitive information. */ memset(C, 0, sizeof (sha256_ctx_t)); } void generate_hash(struct gr_pw_entry *entry) { sha256_ctx_t context; char *pos; pos = (char *)memchr(entry->passwd, '\n', strlen((char *)entry->passwd)); if (pos) *pos = '\0'; SHA256Init(&context); SHA256Update(&context, entry->salt, GR_SALT_SIZE); SHA256Update(&context, entry->passwd, strlen((char *)entry->passwd)); SHA256Final(&context, entry->sum); return; } gradm/gradm_learn_pass1.y0000644000175000017510000001170613152754421015620 0ustar spenderspender%{ /* * Copyright (C) 2002-2014 Bradley Spengler, Open Source Security, Inc. * http://www.grsecurity.net spender@grsecurity.net * * This file is part of gradm. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "gradm.h" extern int learn_pass1lex(void); extern struct gr_learn_role_entry *default_role_entry; extern struct gr_learn_role_entry *group_role_list; extern struct gr_learn_role_entry *user_role_list; extern struct gr_learn_role_entry *special_role_list; %} %union { char * string; unsigned long num; } %token FILENAME ROLENAME %token NUM IPADDR USER GROUP %type filename %type id_type %% learn_logs: learn_log | learn_logs learn_log ; filename: /*empty*/ { $$ = gr_strdup(""); } | FILENAME { if (!strcmp($1, "//")) $1[1] = '\0'; $$ = $1; } ; id_type: USER | GROUP ; learn_log: error | ROLENAME ':' NUM ':' NUM ':' NUM ':' filename ':' filename ':' NUM ':' NUM ':' filename ':' NUM ':' IPADDR { struct gr_learn_role_entry *role; unsigned long res1, res2; u_int16_t rolemode; u_int32_t addr; rolemode = $3; res1 = $13; res2 = $15; addr = $21; if (rolemode & GR_ROLE_USER) role = insert_learn_role(&user_role_list, $1, rolemode); else if (rolemode & GR_ROLE_GROUP) role = insert_learn_role(&group_role_list, $1, rolemode); else if (rolemode & GR_ROLE_SPECIAL) role = insert_learn_role(&special_role_list, $1, rolemode); else { if (default_role_entry == NULL) { default_role_entry = (struct gr_learn_role_entry *)calloc(1, sizeof(struct gr_learn_role_entry)); if (!default_role_entry) failure("calloc"); } role = default_role_entry; } free($1); if (rolemode & GR_ROLE_LEARN) { insert_ip(&(role->allowed_ips), addr, 0, 0, 0); if ((!strcmp($17, "") && strlen($9) > 1 && !res1 && !res2) || is_protected_path($17, $19)) insert_learn_role_subject(role, conv_filename_to_struct($9, GR_PROCFIND | GR_OVERRIDE)); } else if (strlen($9) > 1) insert_learn_role_subject(role, conv_filename_to_struct($11, GR_PROCFIND | GR_OVERRIDE)); free($9); free($11); free($17); } | ROLENAME ':' NUM ':' NUM ':' NUM ':' filename ':' filename ':' IPADDR ':' NUM ':' NUM ':' NUM ':' NUM ':' IPADDR { struct gr_learn_role_entry *role; u_int16_t rolemode; u_int32_t addr; rolemode = $3; addr = $23; if (rolemode & GR_ROLE_USER) role = insert_learn_role(&user_role_list, $1, rolemode); else if (rolemode & GR_ROLE_GROUP) role = insert_learn_role(&group_role_list, $1, rolemode); else if (rolemode & GR_ROLE_SPECIAL) role = insert_learn_role(&special_role_list, $1, rolemode); else { if (default_role_entry == NULL) { default_role_entry = (struct gr_learn_role_entry *)calloc(1, sizeof(struct gr_learn_role_entry)); if (!default_role_entry) failure("calloc"); } role = default_role_entry; } free($1); if (rolemode & GR_ROLE_LEARN) { insert_ip(&(role->allowed_ips), addr, 0, 0, 0); insert_learn_role_subject(role, conv_filename_to_struct($9, GR_PROCFIND | GR_OVERRIDE)); } else if (strlen($9) > 1) insert_learn_role_subject(role, conv_filename_to_struct($11, GR_PROCFIND | GR_OVERRIDE)); free($9); free($11); } | ROLENAME ':' NUM ':' NUM ':' NUM ':' filename ':' filename ':' id_type ':' NUM ':' NUM ':' NUM ':' IPADDR { struct gr_learn_role_entry *role; u_int16_t rolemode; u_int32_t addr; rolemode = $3; addr = $21; if (rolemode & GR_ROLE_USER) role = insert_learn_role(&user_role_list, $1, rolemode); else if (rolemode & GR_ROLE_GROUP) role = insert_learn_role(&group_role_list, $1, rolemode); else if (rolemode & GR_ROLE_SPECIAL) role = insert_learn_role(&special_role_list, $1, rolemode); else { if (default_role_entry == NULL) { default_role_entry = (struct gr_learn_role_entry *)calloc(1, sizeof(struct gr_learn_role_entry)); if (!default_role_entry) failure("calloc"); } role = default_role_entry; } if (rolemode & GR_ROLE_LEARN) { insert_ip(&(role->allowed_ips), addr, 0, 0, 0); insert_learn_role_subject(role, conv_filename_to_struct($9, GR_PROCFIND | GR_OVERRIDE)); } else if (strlen($9) > 1) insert_learn_role_subject(role, conv_filename_to_struct($11, GR_PROCFIND | GR_OVERRIDE)); free($1); free($9); free($11); } ; %% gradm/gradm_fulllearn_pass1.y0000644000175000017510000000444413152754421016504 0ustar spenderspender%{ /* * Copyright (C) 2002-2014 Bradley Spengler, Open Source Security, Inc. * http://www.grsecurity.net spender@grsecurity.net * * This file is part of gradm. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "gradm.h" extern int fulllearn_pass1lex(void); extern struct gr_learn_group_node *the_role_list; %} %union { char * string; unsigned long num; } %token NUM IPADDR FILENAME ROLENAME USER GROUP %type filename id_type %% learn_logs: learn_log | learn_logs learn_log ; filename: /*empty*/ { $$ = 1; } | FILENAME { $$ = 1; } ; id_type: USER | GROUP ; learn_log: error | ROLENAME ':' NUM ':' NUM ':' NUM ':' filename ':' filename ':' NUM ':' NUM ':' filename ':' NUM ':' IPADDR { const char *user; const char *group; uid_t uid; gid_t gid; uid = $5; gid = $7; user = gr_get_user_name(uid); group = gr_get_group_name(gid); if (user && group) insert_user(&the_role_list, user, group, uid, gid); } | ROLENAME ':' NUM ':' NUM ':' NUM ':' filename ':' filename ':' IPADDR ':' NUM ':' NUM ':' NUM ':' NUM ':' IPADDR { const char *user; const char *group; uid_t uid; gid_t gid; uid = $5; gid = $7; user = gr_get_user_name(uid); group = gr_get_group_name(gid); if (user && group) insert_user(&the_role_list, user, group, uid, gid); } | ROLENAME ':' NUM ':' NUM ':' NUM ':' filename ':' filename ':' id_type ':' NUM ':' NUM ':' NUM ':' IPADDR { const char *user; const char *group; uid_t uid; gid_t gid; uid = $5; gid = $7; user = gr_get_user_name(uid); group = gr_get_group_name(gid); if (user && group) insert_user(&the_role_list, user, group, uid, gid); } ; %% gradm/Makefile0000644000175000017510000001656613152754421013515 0ustar spenderspender############################################################################## # gradm (c) 2002-2015 - Brad Spengler, Open Source Security Inc. # # http://www.grsecurity.net # #----------------------------------------------------------------------------# # gradm is licensed under the GNU GPL v2 only http://www.gnu.org # # see COPYRIGHT and LICENSE files for more information # ############################################################################## GRADM_BIN=gradm GRADM_PAM=gradm_pam GRSEC_DIR=/etc/grsec LLEX=/usr/bin/lex FLEX=/usr/bin/flex LEX := $(shell if [ -x $(FLEX) ]; then echo $(FLEX); else echo $(LLEX); fi) LEXFLAGS=-Cfa -B -8 #ubuntu broke byacc for who knows why, disable it #BYACC=/usr/bin/byacc BISON=/usr/bin/bison #YACC := $(shell if [ -x $(BYACC) ]; then echo $(BYACC); else echo $(BISON); fi) YACC=$(BISON) MKNOD=/bin/mknod #for dietlibc #CC=/usr/bin/diet /usr/bin/gcc CC=/usr/bin/gcc FIND=/usr/bin/find STRIP=/usr/bin/strip LIBS := $(shell if [ "`uname -m`" != "sparc64" -a "`uname -m`" != "x86_64" ]; then echo "-lfl" ; else echo "" ; fi) OPT_FLAGS := -O2 # for older versions of grsecurity, comment the above line and uncomment the below: #OPT_FLAGS := $(shell if [ "`uname -m`" != "sparc64" ] && [ "`uname -m`" != "x86_64" ]; then echo "-O2" ; else echo "-O2 -m64" ; fi) CFLAGS := $(OPT_FLAGS) -fPIE -Wcast-qual -DGRSEC_DIR=\"$(GRSEC_DIR)\" -D_LARGEFILE64_SOURCE LDFLAGS= -pie INSTALL = /usr/bin/install -c # FHS MANDIR=/usr/share/man # older MANDIR #MANDIR=/usr/man DESTDIR= OBJECTS=gradm.tab.o lex.gradm.o learn_pass1.tab.o learn_pass2.tab.o \ fulllearn_pass1.tab.o fulllearn_pass2.tab.o fulllearn_pass3.tab.o \ gradm_misc.o gradm_parse.o gradm_arg.o gradm_pw.o gradm_opt.o \ gradm_cap.o gradm_sha256.o gradm_adm.o gradm_analyze.o gradm_res.o \ gradm_human.o gradm_learn.o gradm_net.o gradm_nest.o gradm_pax.o \ gradm_sym.o gradm_newlearn.o gradm_fulllearn.o gradm_lib.o \ lex.fulllearn_pass1.o lex.fulllearn_pass2.o \ lex.fulllearn_pass3.o lex.learn_pass1.o lex.learn_pass2.o \ grlearn_config.tab.o lex.grlearn_config.o gradm_globals.o \ gradm_replace.o all: $(GRADM_BIN) $(GRADM_PAM) grlearn nopam: $(GRADM_BIN) grlearn $(GRADM_BIN): $(OBJECTS) gradm.h gradm_defs.h gradm_func.h $(CC) $(CFLAGS) -o $@ $(OBJECTS) $(LIBS) $(LDFLAGS) $(GRADM_PAM): gradm_pam.c gradm.h gradm_defs.h gradm_func.h @if [ ! -f /usr/include/security/pam_appl.h ] ; then \ echo "Unable to detect PAM headers, disabling PAM support." ; \ else \ $(CC) $(CFLAGS) -o $@ gradm_pam.c -lpam -lpam_misc $(LDFLAGS) ; \ fi grlearn: grlearn.c gradm_lib.c gradm_globals.c grlearn2_config.tab.c lex.grlearn_config.c $(CC) $(CFLAGS) -DIS_GRLEARN -o $@ grlearn.c gradm_lib.c gradm_globals.c grlearn2_config.tab.c lex.grlearn_config.c $(LIBS) $(LDFLAGS) grlearn2_config.tab.c grlearn2_config.tab.h: grlearn2_config_tab_c_h_wrapper grlearn2_config_tab_c_h_wrapper: grlearn2_config.y $(YACC) -b grlearn2_config -p grlearn2_config -d ./grlearn2_config.y grlearn_config.tab.c grlearn_config.tab.h: grlearn_config_tab_c_h_wrapper grlearn_config_tab_c_h_wrapper: grlearn_config.y $(YACC) -b grlearn_config -p grlearn_config -d ./grlearn_config.y lex.grlearn_config.c: grlearn_config.l $(LEX) $(LEXFLAGS) -Pgrlearn_config ./grlearn_config.l lex.grlearn_config.o: lex.grlearn_config.c grlearn_config.tab.h gradm.tab.c gradm.tab.h: gradm_tab_c_h_wrapper gradm_tab_c_h_wrapper: gradm.y $(YACC) -b gradm -p gradm -d ./gradm.y lex.gradm.c: gradm.l $(LEX) $(LEXFLAGS) -Pgradm ./gradm.l lex.gradm.o: lex.gradm.c gradm.tab.h fulllearn_pass1.tab.c fulllearn_pass1.tab.h: fulllearn_pass1_tab_c_h_wrapper fulllearn_pass2.tab.c fulllearn_pass2.tab.h: fulllearn_pass2_tab_c_h_wrapper fulllearn_pass3.tab.c fulllearn_pass3.tab.h: fulllearn_pass3_tab_c_h_wrapper fulllearn_pass1_tab_c_h_wrapper: gradm_fulllearn_pass1.y $(YACC) -b fulllearn_pass1 -p fulllearn_pass1 -d ./gradm_fulllearn_pass1.y fulllearn_pass2_tab_c_h_wrapper: gradm_fulllearn_pass2.y $(YACC) -b fulllearn_pass2 -p fulllearn_pass2 -d ./gradm_fulllearn_pass2.y fulllearn_pass3_tab_c_h_wrapper: gradm_fulllearn_pass3.y $(YACC) -b fulllearn_pass3 -p fulllearn_pass3 -d ./gradm_fulllearn_pass3.y lex.fulllearn_pass1.c: gradm_fulllearn_pass1.l $(LEX) $(LEXFLAGS) -Pfulllearn_pass1 ./gradm_fulllearn_pass1.l lex.fulllearn_pass2.c: gradm_fulllearn_pass2.l $(LEX) $(LEXFLAGS) -Pfulllearn_pass2 ./gradm_fulllearn_pass2.l lex.fulllearn_pass3.c: gradm_fulllearn_pass3.l $(LEX) $(LEXFLAGS) -Pfulllearn_pass3 ./gradm_fulllearn_pass3.l lex.fulllearn_pass1.o: lex.fulllearn_pass1.c fulllearn_pass1.tab.h lex.fulllearn_pass2.o: lex.fulllearn_pass2.c fulllearn_pass2.tab.h lex.fulllearn_pass3.o: lex.fulllearn_pass3.c fulllearn_pass3.tab.h learn_pass1.tab.c learn_pass1.tab.h: learn_pass1_tab_c_h_wrapper learn_pass1_tab_c_h_wrapper: gradm_learn_pass1.y $(YACC) -b learn_pass1 -p learn_pass1 -d ./gradm_learn_pass1.y learn_pass2.tab.c learn_pass2.tab.h: learn_pass2_tab_c_h_wrapper learn_pass2_tab_c_h_wrapper: gradm_learn_pass2.y $(YACC) -b learn_pass2 -p learn_pass2 -d ./gradm_learn_pass2.y lex.learn_pass1.c: gradm_learn_pass1.l $(LEX) $(LEXFLAGS) -Plearn_pass1 ./gradm_learn_pass1.l lex.learn_pass2.c: gradm_learn_pass2.l $(LEX) $(LEXFLAGS) -Plearn_pass2 ./gradm_learn_pass2.l lex.learn_pass1.o: lex.learn_pass1.c learn_pass1.tab.h lex.learn_pass2.o: lex.learn_pass2.c learn_pass2.tab.h install: $(GRADM_BIN) gradm.8 policy grlearn @mkdir -p $(DESTDIR)/sbin @echo "Installing gradm..." @$(INSTALL) -m 0755 $(GRADM_BIN) $(DESTDIR)/sbin @$(STRIP) $(DESTDIR)/sbin/$(GRADM_BIN) @if [ -f $(GRADM_PAM) ] ; then \ echo "Installing gradm_pam..." ; \ $(INSTALL) -m 4755 $(GRADM_PAM) $(DESTDIR)/sbin ; \ $(STRIP) $(DESTDIR)/sbin/$(GRADM_PAM) ; \ fi @echo "Installing grlearn..." @$(INSTALL) -m 0700 grlearn $(DESTDIR)/sbin @$(STRIP) $(DESTDIR)/sbin/grlearn @mkdir -p -m 700 $(DESTDIR)$(GRSEC_DIR) @if [ ! -f $(DESTDIR)$(GRSEC_DIR)/policy ] ; then \ if [ -f $(DESTDIR)$(GRSEC_DIR)/acl ] ; then \ mv $(DESTDIR)$(GRSEC_DIR)/acl $(DESTDIR)$(GRSEC_DIR)/policy ; \ else \ $(INSTALL) -m 0600 policy $(DESTDIR)$(GRSEC_DIR) ; \ fi \ fi @$(FIND) $(DESTDIR)$(GRSEC_DIR) -type f -name learn_config -size 1291c -exec rm -f $(DESTDIR)$(GRSEC_DIR)/learn_config \; @if [ ! -f $(DESTDIR)$(GRSEC_DIR)/learn_config ] ; then \ $(INSTALL) -m 0600 learn_config $(DESTDIR)$(GRSEC_DIR) ; \ fi @if [ -z "`cut -d" " -f3 /proc/mounts | grep "^devfs"`" ] ; then \ rm -f $(DESTDIR)/dev/grsec ; \ if [ ! -e $(DESTDIR)/dev/grsec ] ; then \ mkdir -p $(DESTDIR)/dev ; \ $(MKNOD) -m 0622 $(DESTDIR)/dev/grsec c 1 13 ; \ fi \ fi @if [ -d $(DESTDIR)/etc/udev/rules.d ] ; then \ echo "ACTION!=\"add|change\", GOTO=\"permissions_end\"" > $(DESTDIR)/etc/udev/rules.d/80-grsec.rules ; \ echo "KERNEL==\"grsec\", MODE=\"0622\"" >> $(DESTDIR)/etc/udev/rules.d/80-grsec.rules ; \ echo "LABEL=\"permissions_end\"" >> $(DESTDIR)/etc/udev/rules.d/80-grsec.rules ; \ fi @if [ -f $(DESTDIR)/sbin/udevadm ] ; then \ $(DESTDIR)/sbin/udevadm trigger --action=change ; \ fi @echo "Installing gradm manpage..." @mkdir -p $(DESTDIR)$(MANDIR)/man8 @$(INSTALL) -m 0644 gradm.8 $(DESTDIR)$(MANDIR)/man8/$(GRADM_BIN).8 @if [ -x /sbin/$(GRADM_BIN) ] ; then \ if [ -z $(DESTDIR) ] && [ ! -f $(GRSEC_DIR)/pw ] ; then \ /sbin/$(GRADM_BIN) -P ; \ fi \ fi @true clean: rm -f core *.o $(GRADM_BIN) $(GRADM_PAM) lex.*.c *.tab.c *.tab.h grlearn gradm/gradm_opt.c0000644000175000017510000000574113152754421014166 0ustar spenderspender/* * Copyright (C) 2002-2014 Bradley Spengler, Open Source Security, Inc. * http://www.grsecurity.net spender@grsecurity.net * * This file is part of gradm. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "gradm.h" static void expand_acl(struct proc_acl *proc, struct role_acl *role) { char *tmpproc; struct proc_acl *tmpp; tmpproc = (char *)alloca(strlen(proc->filename) + 1); strcpy(tmpproc, proc->filename); while (parent_dir(proc->filename, &tmpproc)) { tmpp = lookup_acl_subject_by_name(role, tmpproc); if (tmpp) { proc->parent_subject = tmpp; return; } } return; } static void expand_socket_families(struct proc_acl *proc) { /* set up the socket families if proc->ips != NULL, then some connect/bind rules were specified we default to allowing unix/ipv4 sockets if any connect/bind rules are specified */ if (proc->ips != NULL) { add_sock_family(proc, "unix"); add_sock_family(proc, "ipv4"); } else if (!proc->sock_families[0] && !proc->sock_families[1]) { /* there are no connect/bind rules and no socket_family rules, so we must allow all families */ add_sock_family(proc, "all"); } } void expand_acls(void) { struct proc_acl *proc; struct role_acl *role; struct stat fstat; /* handle expansion of nested subjects */ for_each_nested_subject(proc) { expand_socket_families(proc); } /* handle expansion of all non-nested subjects */ for_each_role(role, current_role) { for_each_subject(proc, role) { expand_socket_families(proc); /* add an object into each non-dir subject that allows it to read/exec itself for nested subjects this is handled in gradm_nest.c */ if (!lstat(proc->filename, &fstat)) { char buf[PATH_MAX] = {0}; if (S_ISLNK(fstat.st_mode)) { readlink(proc->filename, buf, sizeof(buf) - 1); if (!lstat(buf, &fstat) && S_ISREG(fstat.st_mode)) { add_proc_object_acl(proc, gr_strdup(buf), proc_object_mode_conv("rx"), GR_FLEARN); } } else if (S_ISREG(fstat.st_mode)) { add_proc_object_acl(proc, gr_strdup(proc->filename), proc_object_mode_conv("rx"), GR_FLEARN); } } /* if we're not /, set parent subject setting the parent subject for nested subjects is handled in gradm_nest.c when creating the subject */ if (!(proc->mode & GR_OVERRIDE) && strcmp(proc->filename, "/")) expand_acl(proc, role); } } return; } gradm/gradm_net.c0000644000175000017510000001657313152754421014157 0ustar spenderspender/* * Copyright (C) 2002-2014 Bradley Spengler, Open Source Security, Inc. * http://www.grsecurity.net spender@grsecurity.net * * This file is part of gradm. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "gradm.h" static struct family_set sock_families[] = { { "unspec", AF_UNSPEC }, { "unix", AF_UNIX }, { "local", AF_LOCAL }, { "inet", AF_INET }, { "ipv4", AF_INET }, { "ax25", AF_AX25 }, { "ipx", AF_IPX }, { "appletalk", AF_APPLETALK }, { "netrom", AF_NETROM }, { "bridge", AF_BRIDGE }, { "atmpvc", AF_ATMPVC }, { "x25", AF_X25 }, { "ipv6", AF_INET6 }, { "inet6", AF_INET6 }, { "rose", AF_ROSE }, { "decnet", AF_DECnet }, { "netbeui", AF_NETBEUI }, { "security", AF_SECURITY }, { "key", AF_KEY }, { "netlink", AF_NETLINK }, { "route", AF_ROUTE }, { "packet", AF_PACKET }, { "ash", AF_ASH }, { "econet", AF_ECONET }, { "atmsvc", AF_ATMSVC }, { "rds", AF_RDS }, { "sna", AF_SNA }, { "irda", AF_IRDA }, { "ppox", AF_PPOX }, { "wanpipe", AF_WANPIPE }, { "llc", AF_LLC }, { "ib", AF_IB }, { "mpls", AF_MPLS }, { "can", AF_CAN }, { "tipc", AF_TIPC }, { "bluetooth", AF_BLUETOOTH }, { "iucv", AF_IUCV }, { "rxrpc", AF_RXRPC }, { "isdn", AF_ISDN }, { "phonet", AF_PHONET }, { "ieee802154", AF_IEEE802154 }, { "caif", AF_CAIF }, { "alg", AF_ALG }, { "nfc", AF_NFC }, { "vsock", AF_VSOCK }, { "kcm", AF_KCM }, { "qipcrtr", AF_QIPCRTR }, { "smc", AF_SMC }, { "all", -1 } }; const char * get_sock_family_from_val(int val) { int i; for (i = 0; i < SIZE(sock_families); i++) { if (sock_families[i].family_val == val) return sock_families[i].family_name; } fprintf(stderr, "Invalid socket family detected.\n"); exit(EXIT_FAILURE); return NULL; } void add_sock_family(struct proc_acl *subject, const char *family) { int i; if (!strcmp(family, "all")) { for (i = 0; i < SIZE(subject->sock_families); i++) { subject->sock_families[i] = -1; } return; } for (i = 0; i < SIZE(sock_families); i++) { if (strcmp(sock_families[i].family_name, family)) continue; else break; } if (i == SIZE(sock_families)) { fprintf(stderr, "Invalid socket family %s on line %lu of %s.\n", family, lineno, current_acl_file); exit(EXIT_FAILURE); } subject->sock_families[sock_families[i].family_val / 32] |= (1U << (sock_families[i].family_val % 32)); return; } void add_role_allowed_host(struct role_acl *role, const char *host, u_int32_t netmask) { struct hostent *he; char **p; he = gethostbyname(host); if (he == NULL) { fprintf(stderr, "Error resolving hostname %s, on line %lu of %s\n", host, lineno, current_acl_file); exit(EXIT_FAILURE); } if (he->h_addrtype != AF_INET) { fprintf(stderr, "Hostname %s on line %lu of %s does not resolve to an IPv4 address.\n", host, lineno, current_acl_file); exit(EXIT_FAILURE); } p = he->h_addr_list; while (*p) { add_role_allowed_ip(role, (u_int32_t)**p, netmask); p++; } return; } void add_role_allowed_ip(struct role_acl *role, u_int32_t addr, u_int32_t netmask) { struct role_allowed_ip **roleipp; struct role_allowed_ip *roleip; num_pointers++; roleip = (struct role_allowed_ip *) calloc(1, sizeof (struct role_allowed_ip)); if (!roleip) failure("calloc"); roleipp = &(role->allowed_ips); if (*roleipp) (*roleipp)->next = roleip; roleip->prev = *roleipp; roleip->addr = addr; roleip->netmask = netmask; *roleipp = roleip; return; } void add_host_acl(struct proc_acl *subject, u_int8_t mode, const char *host, struct ip_acl *acl_tmp) { struct hostent *he; char **p; he = gethostbyname(host); if (he == NULL) { fprintf(stderr, "Error resolving hostname %s, on line %lu of %s\n", host, lineno, current_acl_file); exit(EXIT_FAILURE); } if (he->h_addrtype != AF_INET) { fprintf(stderr, "Hostname %s on line %lu of %s does not resolve to an IPv4 address.\n", host, lineno, current_acl_file); exit(EXIT_FAILURE); } p = he->h_addr_list; while (*p) { memcpy(&(acl_tmp->addr), *p, sizeof(acl_tmp->addr)); add_ip_acl(subject, mode, acl_tmp); p++; } return; } void add_ip_acl(struct proc_acl *subject, u_int8_t mode, struct ip_acl *acl_tmp) { struct ip_acl *p; int i; if (!subject) { fprintf(stderr, "Error on line %lu of %s.\n Definition " "of an IP policy without a subject definition.\n" "The RBAC system will not be allowed to be " "enabled until this problem is fixed.\n", lineno, current_acl_file); exit(EXIT_FAILURE); } /* add one for the pointer to array of pointers */ if (subject->ips == NULL) num_pointers++; num_pointers++; subject->ip_num++; if (subject->ips == NULL) subject->ips = (struct ip_acl **)gr_alloc(subject->ip_num * sizeof(struct ip_acl *)); else subject->ips = (struct ip_acl **)gr_realloc(subject->ips, subject->ip_num * sizeof(struct ip_acl *)); p = (struct ip_acl *)gr_alloc(sizeof (struct ip_acl)); *(subject->ips + subject->ip_num - 1) = p; p->mode = mode; if (acl_tmp->iface != NULL) num_pointers++; p->iface = acl_tmp->iface; p->addr = acl_tmp->addr; p->netmask = acl_tmp->netmask; p->low = acl_tmp->low; p->high = acl_tmp->high; memcpy(p->proto, acl_tmp->proto, sizeof (acl_tmp->proto)); p->type = acl_tmp->type; for (i = 0; i < 8; i++) subject->ip_proto[i] |= p->proto[i]; subject->ip_type |= p->type; return; } u_int32_t get_ip(char *ip) { struct in_addr address; if (!inet_aton(ip, &address)) { fprintf(stderr, "Invalid IP on line %lu of %s.\n", lineno, current_acl_file); exit(EXIT_FAILURE); } return address.s_addr; } void conv_name_to_type(struct ip_acl *ip, const char *name) { struct protoent *proto; unsigned short i; if (!strcmp(name, "raw_proto")) ip->proto[IPPROTO_RAW / 32] |= (1U << (IPPROTO_RAW % 32)); else if (!strcmp(name, "raw_sock")) ip->type |= (1U << SOCK_RAW); else if (!strcmp(name, "any_sock")) { ip->type = ~0; ip->type &= ~(1U << 0); // there is no sock type 0 } else if (!strcmp(name, "any_proto")) { for (i = 0; i < 8; i++) ip->proto[i] = ~0; } else if (!strcmp(name, "stream")) ip->type |= (1U << SOCK_STREAM); else if (!strcmp(name, "dgram")) ip->type |= (1U << SOCK_DGRAM); else if (!strcmp(name, "rdm")) ip->type |= (1U << SOCK_RDM); else if (!strcmp(name, "tcp")) { // silly protocol 0 ip->proto[IPPROTO_IP / 32] |= (1U << (IPPROTO_IP % 32)); ip->proto[IPPROTO_TCP / 32] |= (1U << (IPPROTO_TCP % 32)); } else if (!strcmp(name, "udp")) { // silly protocol 0 ip->proto[IPPROTO_IP / 32] |= (1U << (IPPROTO_IP % 32)); ip->proto[IPPROTO_UDP / 32] |= (1U << (IPPROTO_UDP % 32)); } else if (!strncmp(name, "proto:", strlen("proto:"))) { int pro = atoi(name+strlen("proto:")); ip->proto[pro / 32] |= 1U << (pro % 32); } else if ((proto = getprotobyname(name))) ip->proto[proto->p_proto / 32] |= (1U << (proto->p_proto % 32)); else { fprintf(stderr, "Invalid type/protocol: %s\n", name); exit(EXIT_FAILURE); } return; } gradm/grlearn2_config.y0000644000175000017510000000337613152754421015303 0ustar spenderspender%{ /* * Copyright (C) 2002-2014 Bradley Spengler, Open Source Security, Inc. * http://www.grsecurity.net spender@grsecurity.net * * This file is part of gradm. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "gradm.h" extern int grlearn_configlex(void); extern void add_always_reduce(char *str); extern void grlearn_configerror(const char *s); #define grlearn2_configerror grlearn_configerror #define grlearn2_configlex grlearn_configlex %} %union { char * string; unsigned long num; } %token FILENAME NOLEARN INHERITLEARN INHERITNOLEARN DONTREDUCE %token PROTECTED HIGHPROTECTED HIGHREDUCE ALWAYSREDUCE NOALLOWEDIPS %token READPROTECTED SPLITROLES %token NUM %% learn_config_file: learn_config | learn_config_file learn_config ; learn_config: NOLEARN FILENAME { } | INHERITLEARN FILENAME { } | INHERITNOLEARN FILENAME { } | DONTREDUCE FILENAME { } | PROTECTED FILENAME { } | READPROTECTED FILENAME { } | HIGHREDUCE FILENAME { } | ALWAYSREDUCE FILENAME { add_always_reduce($2); } | HIGHPROTECTED FILENAME { } | NOALLOWEDIPS { } | SPLITROLES { } ; %% gradm/gradm.h0000644000175000017510000000653613152754421013314 0ustar spenderspender/* * Copyright (C) 2002-2014 Bradley Spengler, Open Source Security, Inc. * http://www.grsecurity.net spender@grsecurity.net * * This file is part of gradm. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef GRADM_H #define GRADM_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define SIZE(x) (sizeof(x) / sizeof(x[0])) #define failure(x) do { \ fprintf(stderr, x ": %s\n\n", strerror(errno)); \ exit(EXIT_FAILURE);\ } while(0) #define for_each_role(x, y) \ for(x = y; x; x = (x)->prev) #define for_each_subject(x, y) \ for(x = (struct proc_acl *)(y)->hash->first; x; x = (x)->prev) #define for_each_nested_subject(x) \ for (x = global_nested_subject_list; x; x = (x)->next) #define for_each_include(x) \ for(x = includes; x; x = (x)->prev) #define for_each_file_object(x, y) \ for(x = (struct file_acl *)(y)->hash->first; x; x = (x)->prev) #define for_each_allowed_ip(x, y) \ for(x = y; x; x = (x)->prev) #define for_each_transition(x, y) \ for(x = y; x; x = (x)->prev) #define for_each_globbed(x, y) \ for(x = (y)->globbed; x; x = (x)->next) #define for_each_leaf(x, y) \ for (x = (y)->leaves; x; x = (x)->next) #define for_each_list_entry(x, y) \ for (x = (y); x; x = (x)->next) #define for_each_removable_list_entry(x, y) \ for (x = (y); x;) #define for_each_removable_list_entry_end(x) \ if (removed) \ removed = 0; \ else \ x = (x)->next; #define for_each_parent_entry(x, y) \ for (x = (y); x; x = (x)->parent) #define establish_new_head(list, head, tmp) \ do { \ (tmp) = (list); \ (head)->next = (tmp); \ if ((tmp)) \ (tmp)->prev = (head); \ (list) = (head); \ } while (0); #define for_each_variable(x, y) \ for (x = (y); x; x = (x)->next) #define get_list_head(x) \ ({ \ typeof (x) _x = (x); \ while (_x->prev) \ _x = _x->prev; \ _x; \ }) #define MAJOR_26(dev) ((unsigned int) ((dev)>>20)) #define MINOR_26(dev) ((unsigned int) ((dev) & ((1U << 20) - 1))) #define MKDEV_26(ma,mi) ((mi & 0xff) | (ma << 8) | ((mi & ~0xff) << 12)) #define MAJOR_24(dev) ((dev)>>8) #define MINOR_24(dev) ((dev) & 0xff) #define MKDEV_24(ma,mi) ((ma)<<8 | (mi)) #include "gradm_defs.h" #include "gradm_func.h" #endif gradm/gradm_analyze.c0000644000175000017510000010254413152754421015026 0ustar spenderspender/* * Copyright (C) 2002-2016 Bradley Spengler, Open Source Security, Inc. * http://www.grsecurity.net spender@grsecurity.net * * This file is part of gradm. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "gradm.h" struct file_acl *get_exact_matching_object(struct proc_acl *subject, const char *filename) { struct file_acl *tmpf = NULL; struct proc_acl *tmpp = subject; struct file_acl *tmpg = NULL; char *tmpname = (char *)alloca(strlen(filename) + 1); int reduced_dir = 0; strcpy(tmpname, filename); do { tmpp = subject; do { tmpf = lookup_acl_object_by_name(tmpp, tmpname); if (!tmpf) tmpf = lookup_acl_object_by_inodev_nofollow(tmpp, tmpname); if (tmpf) { /* check globbed objects */ for_each_globbed(tmpg, tmpf) { if (!fnmatch(tmpg->filename, filename, 0)) return tmpg; } if (!reduced_dir) return tmpf; } } while ((tmpp = tmpp->parent_subject)); reduced_dir = 1; } while (parent_dir(filename, &tmpname)); // won't get here return NULL; } static struct file_acl *get_a_matching_object(struct proc_acl *subject, const char *filename, const char *origname, int follow) { struct file_acl *tmpf, *tmpg; struct proc_acl *tmpp = subject; do { tmpf = lookup_acl_object_by_name(tmpp, filename); if (!tmpf) { if (follow) tmpf = lookup_acl_object_by_inodev(tmpp, filename); else tmpf = lookup_acl_object_by_inodev_nofollow(tmpp, filename); } if (tmpf) { /* check globbed objects */ for_each_globbed(tmpg, tmpf) { if (!fnmatch(tmpg->filename, origname, 0)) return tmpg; } return tmpf; } } while ((tmpp = tmpp->parent_subject)); return NULL; } static struct file_acl *__get_matching_object(struct proc_acl *subject, const char *filename, int follow) { struct file_acl *tmpf = NULL; char *tmpname = (char *)alloca(strlen(filename) + 1); strcpy(tmpname, filename); do { tmpf = get_a_matching_object(subject, tmpname, filename, follow); if (tmpf) return tmpf; } while (parent_dir(filename, &tmpname)); // won't get here return NULL; } struct file_acl *get_matching_object(struct proc_acl *subject, const char *filename) { return __get_matching_object(subject, filename, 1); } struct file_acl *get_matching_object_nofollow(struct proc_acl *subject, const char *filename) { return __get_matching_object(subject, filename, 0); } static int check_permission(struct role_acl *role, struct proc_acl *def_acl, const char *filename, struct chk_perm *chk) { struct file_acl *tmpf = NULL; struct proc_acl *tmpp = def_acl; gr_cap_t cap_drp = {{ 0, 0 }}, cap_mask = {{ 0, 0 }}; gr_cap_t cap_full = {{ ~0, ~0 }}; if (chk->type == CHK_FILE) { tmpf = get_matching_object(def_acl, filename); if (((chk->w_modes == 0xffff) || (tmpf->mode & chk->w_modes)) && ((chk->u_modes == 0xffff) || !(tmpf->mode & chk->u_modes))) { return 1; } else { return 0; } } else if (chk->type == CHK_CAP) { cap_mask = tmpp->cap_mask; cap_drp = tmpp->cap_drop; while ((tmpp = tmpp->parent_subject)) { cap_drp = cap_combine(cap_drp, cap_intersect(tmpp->cap_drop, cap_drop(tmpp->cap_mask, cap_mask))); cap_mask = cap_combine(cap_mask, tmpp->cap_mask); } if (((cap_same(chk->w_caps, cap_full)) || cap_isclear(cap_intersect(cap_drp, chk->w_caps))) && ((cap_same(chk->u_caps, cap_full)) || !cap_isclear(cap_intersect(cap_drp, chk->u_caps)))) return 1; } return 0; } static unsigned int insert_globbed_objects(void) { struct glob_file *glob; struct glob_file *tmp; struct glob_file *subj_start = glob_files_head; unsigned int num_errors = 0; for (glob = glob_files_head; glob; glob = glob->next) { /* check previous globbed objects for this subject, looking for one that completely matches this later object */ if (subj_start->subj != glob->subj) subj_start = glob; for (tmp = subj_start; tmp && tmp != glob; tmp = tmp->next) { /* doesn't cover all cases, but covers enough */ if (!anchorcmp(tmp->filename, glob->filename) && !fnmatch(tmp->filename, glob->filename, 0)) { fprintf(stderr, "Error on line %lu of %s: Globbed object %s in subject %s is completely matched by previous " "globbed object %s. As globbed objects with the same anchor are matched on a " "first-rule-matches-first policy, the ordering present in your policy likely does not reflect " "your intentions.\r\n", glob->lineno, glob->policy_file, glob->filename, glob->subj->filename, tmp->filename); num_errors++; } } add_globbed_object_acl(glob->subj, glob->filename, glob->mode, glob->type, glob->policy_file, glob->lineno); } return num_errors; } static void check_symlinks(void) { struct symlink *sym; struct file_acl *tmpf; for (sym = symlinks; sym; sym = sym->next) { char buf[PATH_MAX]; struct stat64 src_st, dst_st; memset(&buf, 0, sizeof (buf)); if (!realpath(sym->obj->filename, buf)) continue; /* warning exemptions */ if (!strcmp(buf, "/proc/self")) continue; tmpf = get_matching_object(sym->subj, buf); if (tmpf->mode != sym->obj->mode) { fprintf(stdout, "Warning: permission for symlink %s in role %s, subject %s does not match that of its matching target object %s. Symlink is specified on line %lu of %s.\n", sym->obj->filename, sym->role->rolename, sym->subj->filename, tmpf->filename, sym->lineno, sym->policy_file); } else if (!lstat64(buf, &dst_st) && !lstat64(sym->obj->filename, &src_st) && src_st.st_uid != dst_st.st_uid) { fprintf(stdout, "Warning: owner of symlink %s in role %s, subject %s does not match that of its target %s. Symlink is specified on line %lu of %s.\n", sym->obj->filename, sym->role->rolename, sym->subj->filename, buf, sym->lineno, sym->policy_file); } } return; } static int check_subjects(struct role_acl *role) { struct proc_acl *tmp; struct proc_acl *def_acl; struct chk_perm chk; unsigned int errs_found = 0; chk.type = CHK_FILE; chk.u_modes = GR_WRITE; chk.w_modes = 0xffff; def_acl = role->root_label; if (!def_acl) return 0; for_each_subject(tmp, role) if ((tmp->filename[0] == '/') && (tmp->filename[1] != '\0')) if (!check_permission(role, def_acl, tmp->filename, &chk)) fprintf(stderr, "Warning: write access is allowed to your " "subject for %s in role %s. Please ensure that the subject is running with less privilege than the default subject.\n", tmp->filename, role->rolename); return errs_found; } static int check_learning(struct role_acl *role) { struct proc_acl *tmp; struct proc_acl *def_acl; unsigned int errs_found = 0; def_acl = role->root_label; if (!def_acl) return 0; if (gr_learn) return 0; if (!gr_learn && role->roletype & GR_ROLE_LEARN) { fprintf(stderr, "Error: You have enabled learning on the role " "%s. You have not used -L on the command " "line however. If you wish to use learning " "on this role, use the -L argument to gradm. " "Otherwise, remove the learning flag on this role.\n", role->rolename); errs_found++; } for_each_subject(tmp, role) { if (!gr_learn && (tmp->mode & (GR_LEARN | GR_INHERITLEARN))) { fprintf(stderr, "Error: You have enabled some form of " "learning on the subject for %s in role " "%s. You have not used -L on the command " "line however. If you wish to use learning " "on this subject, use the -L argument to gradm. " "Otherwise, remove the learning flag on this subject.\n", tmp->filename, role->rolename); errs_found++; } } return errs_found; } static void check_default_objects(struct role_acl *role) { struct proc_acl *tmp; struct file_acl *tmpf; for_each_subject(tmp, role) { /* skip all inherited subjects */ if (tmp->parent_subject != NULL) continue; tmpf = lookup_acl_object_by_name(tmp, "/"); if (tmpf == NULL) { fprintf(stderr, "Default object not found for " "role %s subject %s\nThe RBAC system will " "not load until you correct this " "error.\n", role->rolename, tmp->filename); exit(EXIT_FAILURE); } } return; } static unsigned int check_nested_default_objects(void) { struct proc_acl *tmp; struct file_acl *tmpf; unsigned int errs_found = 0; for_each_nested_subject(tmp) { /* skip all inherited subjects */ if (tmp->parent_subject != NULL) continue; tmpf = lookup_acl_object_by_name(tmp, "/"); if (tmpf == NULL) { fprintf(stderr, "Default object not found for " "nested subject %s\n", tmp->filename); errs_found++; } } return errs_found; } static void check_subject_modes(struct role_acl *role) { struct proc_acl *tmp; for_each_subject(tmp, role) { if ((tmp->mode & GR_LEARN) && (tmp->mode & GR_INHERITLEARN)) { fprintf(stderr, "Invalid subject mode found for " "role %s subject %s\nBoth \"i\" and \"l\" modes " "cannot be used together. Please choose either " "normal or inheritance-based learning for the " "subject.\nThe RBAC system will not load until you " "correct this error.\n", role->rolename, tmp->filename); exit(EXIT_FAILURE); } } return; } static int get_symlinked_dir(const char *filename, char *out, char *target) { char *p = out; struct stat64 st; strncpy(out, filename, PATH_MAX); out[PATH_MAX-1] = '\0'; p = strchr(p + 1, '/'); while (p) { *p = '\0'; if (lstat64(out, &st)) break; if (S_ISLNK(st.st_mode)) { realpath(out, target); return 1; } *p = '/'; p = strchr(p + 1, '/'); } return 0; } static void check_noncanonical_paths(struct role_acl *role) { struct proc_acl *subj; struct file_acl *obj; struct file_acl *targobj, *targobj2; struct stat64 st1, st2; char tmp[PATH_MAX]; char tmp2[PATH_MAX]; for_each_subject(subj, role) { for_each_file_object(obj, subj) { if (get_symlinked_dir(obj->filename, (char *)tmp, (char *)tmp2)) { targobj = get_matching_object_nofollow(subj, tmp); if (targobj->mode & GR_WRITE) { fprintf(stderr, "Warning: In role %s subject %s, pathname \"%s\":\nA writable and symlinked directory \"%s\" points to \"%s\".\n", role->rolename, subj->filename, obj->filename, tmp, tmp2); } } } } return; } static void check_socket_policies(struct role_acl *role) { struct proc_acl *tmp; struct ip_acl *tmpi; int has_connect; int has_bind; unsigned int i; for_each_subject(tmp, role) { has_connect = 0; has_bind = 0; for (i = 0; i < tmp->ip_num; i++) { tmpi = tmp->ips[i]; if (tmpi->mode & GR_IP_BIND) has_bind = 1; if (tmpi->mode & GR_IP_CONNECT) has_connect = 1; } /* if we have either a bind or a connect, but not either and not both */ if (has_bind ^ has_connect) { fprintf(stderr, "A %s rule exists but a %s rule is missing for " "role %s subject %s\nThe RBAC system will " "not load until you correct this " "error.\n", has_connect ? "connect" : "bind", has_bind ? "connect" : "bind", role->rolename, tmp->filename); exit(EXIT_FAILURE); } if (tmp->parent_subject && tmp->parent_subject->ips && !tmp->ips) fprintf(stderr, "Warning: Network policies do not support policy inheritance. Please inspect policy for subject %s in role %s to make sure you intended to allow all network activity.\n", tmp->filename, role->rolename); } return; } static int check_lilo_conf(struct role_acl *role, struct proc_acl *def_acl) { FILE *liloconf; char buf[PATH_MAX]; struct stat fstat; struct chk_perm chk; unsigned int errs_found = 0; char *p; if ((liloconf = fopen("/etc/lilo.conf", "r")) == NULL) return 0; chk.type = CHK_FILE; chk.u_modes = GR_WRITE; chk.w_modes = 0xffff; while (fgets(buf, PATH_MAX - 1, liloconf)) { if (buf[strlen(buf) - 1] == '\n') buf[strlen(buf) - 1] = '\0'; if ((p = strstr(buf, "image="))) { p += 6; if (!stat(p, &fstat) && !check_permission(role, def_acl, p, &chk)) { fprintf(stderr, "Write access is allowed by role %s to %s, a kernel " "for your system specified in " "/etc/lilo.conf.\n\n", role->rolename, p); errs_found++; } } } fclose(liloconf); return errs_found; } static int check_lib_paths(struct role_acl *role, struct proc_acl *def_acl) { FILE *ldconf; char buf[PATH_MAX]; struct stat fstat; struct chk_perm chk; unsigned int errs_found = 0; if ((ldconf = fopen("/etc/ld.so.conf", "r")) == NULL) return 0; chk.type = CHK_FILE; chk.u_modes = GR_WRITE; chk.w_modes = 0xffff; while (fgets(buf, PATH_MAX - 1, ldconf)) { if (buf[strlen(buf) - 1] == '\n') buf[strlen(buf) - 1] = '\0'; if (!stat(buf, &fstat) && !check_permission(role, def_acl, buf, &chk)) { fprintf(stderr, "Write access is allowed by role %s to %s, a directory which " "holds libraries for your system and is included " "in /etc/ld.so.conf.\n\n", role->rolename, buf); errs_found++; } } fclose(ldconf); return errs_found; } static int check_path_env(struct role_acl *role, struct proc_acl *def_acl) { char *pathstr, *p, *p2; char pathbuf[PATH_MAX]; struct stat fstat; struct chk_perm chk; unsigned int errs_found = 0; if ((pathstr = getenv("PATH")) == NULL) return 0; p = pathstr; chk.type = CHK_FILE; chk.u_modes = GR_WRITE; chk.w_modes = 0xffff; while ((p2 = strchr(p, ':'))) { *p2++ = '\0'; memset(pathbuf, 0, sizeof(pathbuf)); if (!realpath(p, pathbuf)) goto next; if (!stat(pathbuf, &fstat) && !check_permission(role, def_acl, pathbuf, &chk)) { fprintf(stderr, "Write access is allowed by role %s to %s, a directory which " "holds binaries for your system and is included " "in the PATH environment variable.\n\n", role->rolename, pathbuf); errs_found++; } next: p = p2; } memset(pathbuf, 0, sizeof(pathbuf)); if (!realpath(p, pathbuf)) goto reterr; if (!stat(pathbuf, &fstat) && !check_permission(role, def_acl, pathbuf, &chk)) { fprintf(stderr, "Write access is allowed by role %s to %s, a directory which " "holds binaries for your system and is included " "in the PATH environment variable.\n\n", role->rolename, pathbuf); errs_found++; } reterr: return errs_found; } static int handle_notrojan_mode(void) { struct proc_acl *subj, *subj2; struct file_acl *obj, *obj2; struct role_acl *role, *role2; char *objname; int ret = 0; for_each_role(role, current_role) { if (!strcmp(role->rolename, ":::kernel:::")) continue; for_each_subject(subj, role) { if (!(subj->mode & GR_NOTROJAN)) continue; for_each_file_object(obj, subj) { if (!(obj->mode & GR_EXEC)) continue; for_each_role(role2, current_role) { if (!strcmp(role2->rolename, ":::kernel:::")) continue; if (role2->roletype & GR_ROLE_GOD) continue; for_each_subject(subj2, role2) { if (subj2 == subj || (subj2-> filename[0] != '/')) continue; objname = gr_strdup(obj->filename); do { obj2 = lookup_acl_object_by_name(subj2, objname); if (obj2 && obj2->mode & GR_WRITE) { ret++; fprintf(stderr, "\'T\' specified in mode for role %s, subject %s.\n" "%s's executable object %s is " "writable by role %s, subject %s, due to its " "writable object %s.\nThis would " "allow %s to execute trojaned code.\n\n", role->rolename, subj->filename, subj->filename, obj->filename, role2->rolename, subj2->filename, obj2->filename, subj->filename); break; } else if (obj2) { /* if we found a match, but it wasn't writable, then just break otherwise we'd end up matching on a directory whose mode doesn't apply to what we're looking up */ break; } } while (parent_dir(obj->filename, &objname)); free(objname); } } } } } return ret; } int check_role_transitions(void) { struct role_acl *role, *role2; struct role_transition *trans; int num_sproles = 0; int found = 0; int i; int errors = 0; struct role_acl **sprole_table; for_each_role(role, current_role) { if (role->roletype & GR_ROLE_SPECIAL) num_sproles++; } sprole_table = (struct role_acl **)malloc(num_sproles * sizeof(struct role_acl *)); if (sprole_table == NULL) failure("malloc"); i = 0; for_each_role(role, current_role) { if (role->roletype & GR_ROLE_SPECIAL) { sprole_table[i] = role; i++; } } for_each_role(role, current_role) { if (role->transitions && !(role->roletype & (GR_ROLE_SPECIAL | GR_ROLE_AUTH))) { fprintf(stderr, "Error in role %s: a transition to a special role exists, " "but the \"G\" flag is not present on the role to grant it " "permission to use gradm to change to the special role.\n", role->rolename); errors++; } for_each_transition(trans, role->transitions) { found = 0; for_each_role(role2, current_role) { if (!(role2->roletype & GR_ROLE_SPECIAL)) continue; if (!strcmp(role2->rolename, trans->rolename)) { found = 1; for(i = 0; i < num_sproles; i++) { if (sprole_table[i] == role2) { sprole_table[i] = NULL; break; } } } } if (!found) { fprintf(stderr, "Error in transition to special role %s in role " "%s.\nSpecial role %s does not exist.\n", trans->rolename, role->rolename, trans->rolename); errors++; } } } for (i = 0; i < num_sproles; i++) { if (sprole_table[i] != NULL) { fprintf(stderr, "Special role %s is not accessible from any role. Make sure " "you have a role_transitions line added in all roles that will " "access the special role.\n", sprole_table[i]->rolename); errors++; } } free(sprole_table); return errors; } void analyze_acls(void) { struct proc_acl *def_acl; struct chk_perm chk; unsigned int errs_found = 0; struct role_acl *role; int def_role_found = 0; struct stat fstat; gr_cap_t cap_full = {{ ~0, ~0 }}; errs_found += insert_globbed_objects(); errs_found += check_role_transitions(); errs_found += check_nested_default_objects(); for_each_role(role, current_role) if (role->roletype & GR_ROLE_DEFAULT) def_role_found = 1; if (!def_role_found) { fprintf(stderr, "There is no default role present in your " "configuration.\nPlease read the RBAC " "documentation and create a default role before " "attempting to enable the RBAC system.\n\n"); exit(EXIT_FAILURE); } for_each_role(role, current_role) { if (((role->roletype & (GR_ROLE_GOD | GR_ROLE_PERSIST)) == (GR_ROLE_GOD | GR_ROLE_PERSIST)) && !strcmp(role->rolename, "admin")) { fprintf(stderr, "The admin role has been marked " "as a persistent role. This severely compromises " "security as any process restarted via an admin " "role will retain the admin role indefinitely.\n" "Please create a specific role for the handling " "of system shutdown (the common use case of " "persistent special roles). The RBAC system will " "not be allowed to be enabled until this error is " "fixed.\n"); exit(EXIT_FAILURE); } def_acl = role->root_label; if (!def_acl) { fprintf(stderr, "There is no default subject for " "the role for %s present in your " "configuration.\nPlease read the RBAC " "documentation and create a default subject " "before attempting to enable the RBAC " "system.\n", role->rolename); exit(EXIT_FAILURE); } check_default_objects(role); check_subject_modes(role); check_socket_policies(role); check_noncanonical_paths(role); /* non-critical warnings aren't issued for special roles */ if (role->roletype & GR_ROLE_SPECIAL) continue; errs_found += check_subjects(role); errs_found += check_learning(role); chk.type = CHK_FILE; chk.u_modes = GR_FIND; chk.w_modes = 0xffff; if (!check_permission(role, def_acl, GRDEV_PATH, &chk)) { fprintf(stderr, "Viewing access is allowed by role %s to %s.\n" "If you want this role to be able to authenticate to the kernel, add G to its role mode.\n\n", role->rolename, GRDEV_PATH); errs_found++; } if (!check_permission(role, def_acl, GRSEC_DIR, &chk)) { fprintf(stderr, "Viewing access is allowed by role %s to %s, the directory which " "stores RBAC policies and RBAC password information.\n\n", role->rolename, GRSEC_DIR); errs_found++; } if (!check_permission(role, def_acl, "/dev/kmem", &chk)) { fprintf(stderr, "Viewing access is allowed by role %s to /dev/kmem. This could " "allow an attacker to modify the code of your " "running kernel.\n\n", role->rolename); errs_found++; } if (!check_permission(role, def_acl, "/dev/mem", &chk)) { fprintf(stderr, "Viewing access is allowed by role %s to /dev/mem. This would " "allow an attacker to modify the code of programs " "running on your system.\n\n", role->rolename); errs_found++; } if (!check_permission(role, def_acl, "/dev/port", &chk)) { fprintf(stderr, "Viewing access is allowed by role %s to /dev/port. This would " "allow an attacker to modify the code of programs " "running on your system.\n\n", role->rolename); errs_found++; } if (!stat("/proc/kcore", &fstat) && !check_permission(role, def_acl, "/proc/kcore", &chk)) { fprintf(stderr, "Viewing access is allowed by role %s to /proc/kcore. This would " "allow an attacker to view the raw memory of processes " "running on your system.\n\n", role->rolename); errs_found++; } chk.u_modes = GR_WRITE; if (!check_permission(role, def_acl, "/boot", &chk)) { fprintf(stderr, "Writing access is allowed by role %s to /boot, the directory which " "holds boot and kernel information.\n\n", role->rolename); errs_found++; } if (!check_permission(role, def_acl, "/run", &chk)) { fprintf(stderr, "Writing access is allowed by role %s to /run, the directory which " "holds information for running services and potentially the initctl device.\n\n", role->rolename); errs_found++; } if (!stat("/lib/modules", &fstat) && !check_permission(role, def_acl, "/lib/modules", &chk)) { fprintf(stderr, "Writing access is allowed by role %s to /lib/modules, the directory which " "holds kernel modules.\n\n", role->rolename); errs_found++; } if (!stat("/lib64/modules", &fstat) && !check_permission(role, def_acl, "/lib64/modules", &chk)) { fprintf(stderr, "Writing access is allowed by role %s to /lib64/modules, the directory which " "holds kernel modules.\n\n", role->rolename); errs_found++; } if (!check_permission(role, def_acl, "/dev", &chk)) { fprintf(stderr, "Writing access is allowed by role %s to /dev, the directory which " "holds system devices.\n\n", role->rolename); errs_found++; } if (!check_permission(role, def_acl, "/dev/log", &chk)) { fprintf(stderr, "Writing access is allowed by role %s to /dev/log. This could in some cases allow an attacker" " to spoof syslog warnings on your system.\n\n", role->rolename); errs_found++; } if (!check_permission(role, def_acl, "/dev/grsec", &chk)) { fprintf(stderr, "Writing access is allowed by role %s to /dev/grsec. This could allow an attacker to bypass the PAM authentication feature of the RBAC system.\n\n", role->rolename); errs_found++; } if (!check_permission(role, def_acl, "/root", &chk)) { fprintf(stderr, "Writing access is allowed by role %s to /root, the directory which " "holds shell configurations for the root user. " "If writing is allowed to this directory, an attacker " "could modify your $PATH environment to fool you " "into executing a trojaned gradm.\n\n", role->rolename); errs_found++; } if (!check_permission(role, def_acl, "/proc/sys", &chk)) { fprintf(stderr, "Write access is allowed by role %s to /proc/sys, the directory which " "holds entries that allow modifying kernel variables.\n\n", role->rolename); errs_found++; } if (!stat("/sys", &fstat) && !check_permission(role, def_acl, "/sys", &chk)) { fprintf(stderr, "Write access is allowed by role %s to /sys, the directory which " "holds entries that allow modifying kernel variables.\n\n", role->rolename); errs_found++; } if (!check_permission(role, def_acl, "/etc", &chk)) { fprintf(stderr, "Write access is allowed by role %s to /etc, the directory which " "holds initialization scripts and configuration files.\n\n", role->rolename); errs_found++; } if (!check_permission(role, def_acl, "/lib", &chk)) { fprintf(stderr, "Write access is allowed by role %s to /lib, a directory which " "holds system libraries and loadable modules.\n\n", role->rolename); errs_found++; } if (!check_permission(role, def_acl, "/usr/lib", &chk)) { fprintf(stderr, "Write access is allowed by role %s to /usr/lib, a directory which " "holds system libraries.\n\n", role->rolename); errs_found++; } if (!stat("/lib32", &fstat) && !check_permission(role, def_acl, "/lib32", &chk)) { fprintf(stderr, "Write access is allowed by role %s to /lib32, a directory which " "holds system libraries.\n\n", role->rolename); errs_found++; } if (!stat("/libx32", &fstat) && !check_permission(role, def_acl, "/libx32", &chk)) { fprintf(stderr, "Write access is allowed by role %s to /libx32, a directory which " "holds system libraries.\n\n", role->rolename); errs_found++; } if (!stat("/usr/lib32", &fstat) && !check_permission(role, def_acl, "/usr/lib32", &chk)) { fprintf(stderr, "Write access is allowed by role %s to /usr/lib32, a directory which " "holds system libraries.\n\n", role->rolename); errs_found++; } if (!stat("/usr/libx32", &fstat) && !check_permission(role, def_acl, "/usr/libx32", &chk)) { fprintf(stderr, "Write access is allowed by role %s to /usr/libx32, a directory which " "holds system libraries.\n\n", role->rolename); errs_found++; } if (!stat("/lib64", &fstat) && !check_permission(role, def_acl, "/lib64", &chk)) { fprintf(stderr, "Write access is allowed by role %s to /lib64, a directory which " "holds system libraries.\n\n", role->rolename); errs_found++; } if (!stat("/usr/lib64", &fstat) && !check_permission(role, def_acl, "/usr/lib64", &chk)) { fprintf(stderr, "Write access is allowed by role %s to /usr/lib64, a directory which " "holds system libraries.\n\n", role->rolename); errs_found++; } chk.u_modes = GR_READ; if (!check_permission(role, def_acl, "/dev", &chk)) { fprintf(stderr, "Reading access is allowed by role %s to /dev, the directory which " "holds system devices.\n\n", role->rolename); errs_found++; } if (!stat("/sys", &fstat) && !check_permission(role, def_acl, "/sys", &chk)) { fprintf(stderr, "Read access is allowed by role %s to /sys, the directory which " "holds entries that often leak information from the kernel.\n\n", role->rolename); errs_found++; } if (!stat("/proc/slabinfo", &fstat) && !check_permission(role, def_acl, "/proc/slabinfo", &chk)) { fprintf(stderr, "Reading access is allowed by role %s to " "/proc/slabinfo, an entry that provides " "useful information to an attacker " "for reliable heap exploitation in the " "kernel.\n\n", role->rolename); errs_found++; } if (!stat("/proc/modules", &fstat) && !check_permission(role, def_acl, "/proc/modules", &chk)) { fprintf(stderr, "Reading access is allowed by role %s to " "/proc/modules, an entry that provides " "useful kernel addresses to an attacker " "for reliable exploitation of the " "kernel.\n\n", role->rolename); errs_found++; } if (!check_permission(role, def_acl, "/boot", &chk)) { fprintf(stderr, "Reading access is allowed by role %s to " "/boot, the directory which holds kernel " "images. The ability to read these " "images provides an attacker with very " "useful information for launching \"ret-to-libc\" " "style attacks against the kernel" ".\n\n", role->rolename); errs_found++; } if (!stat("/lib/modules", &fstat) && !check_permission(role, def_acl, "/lib/modules", &chk)) { fprintf(stderr, "Reading access is allowed by role %s to " "/lib/modules, the directory which holds kernel " "kernel modules. The ability to read these " "images provides an attacker with very " "useful information for launching \"ret-to-libc\" " "style attacks against the kernel" ".\n\n", role->rolename); errs_found++; } if (!stat("/lib32/modules", &fstat) && !check_permission(role, def_acl, "/lib32/modules", &chk)) { fprintf(stderr, "Reading access is allowed by role %s to " "/lib32/modules, the directory which holds kernel " "kernel modules. The ability to read these " "images provides an attacker with very " "useful information for launching \"ret-to-libc\" " "style attacks against the kernel" ".\n\n", role->rolename); errs_found++; } if (!stat("/lib64/modules", &fstat) && !check_permission(role, def_acl, "/lib64/modules", &chk)) { fprintf(stderr, "Reading access is allowed by role %s to " "/lib64/modules, the directory which holds kernel " "kernel modules. The ability to read these " "images provides an attacker with very " "useful information for launching \"ret-to-libc\" " "style attacks against the kernel" ".\n\n", role->rolename); errs_found++; } if (!stat("/proc/kallsyms", &fstat) && !check_permission(role, def_acl, "/proc/kallsyms", &chk)) { fprintf(stderr, "Reading access is allowed by role %s to " "/proc/kallsyms, a pseudo-file that " "holds a mapping between kernel " "addresses and symbols. This information " "is very useful to an attacker in " "sophisticated kernel exploits.\n\n", role->rolename); errs_found++; } chk.type = CHK_CAP; chk.u_caps = cap_combine(cap_combine(cap_conv("CAP_SYS_MODULE"), cap_conv("CAP_SYS_RAWIO")), cap_conv("CAP_MKNOD")); chk.w_caps = cap_full; if (!check_permission(role, def_acl, "", &chk)) { fprintf(stderr, "CAP_SYS_MODULE, CAP_SYS_RAWIO, and CAP_MKNOD are all not " "removed in role %s. This would allow an " "attacker to modify the kernel by means of a " "module or corrupt devices on your system.\n\n", role->rolename); errs_found++; } chk.u_caps = cap_conv("CAP_SYS_ADMIN"); chk.w_caps = cap_full; if (!check_permission(role, def_acl, "", &chk)) { fprintf(stderr, "CAP_SYS_ADMIN is not " "removed in role %s. This would allow an " "attacker to mount filesystems to bypass your policies\n\n", role->rolename); errs_found++; } chk.u_caps = cap_conv("CAP_SYSLOG"); chk.w_caps = cap_full; if (!check_permission(role, def_acl, "", &chk)) { fprintf(stderr, "CAP_SYSLOG is not " "removed in role %s. This would allow an " "attacker to view OOPs messages in dmesg that contain addresses useful for kernel exploitation.\n\n", role->rolename); errs_found++; } chk.u_caps = cap_conv("CAP_SYS_BOOT"); chk.w_caps = cap_full; if (!check_permission(role, def_acl, "", &chk)) { fprintf(stderr, "CAP_SYS_BOOT is not " "removed in role %s. This would allow an " "attacker to reboot the system or to load a new kernel through the kexec interface .\n\n", role->rolename); errs_found++; } chk.u_caps = cap_conv("CAP_NET_ADMIN"); chk.w_caps = cap_full; if (!check_permission(role, def_acl, "", &chk)) { fprintf(stderr, "CAP_NET_ADMIN is not " "removed for role %s. This would allow an " "attacker to modify your firewall configuration or redirect private information\n\n", role->rolename); errs_found++; } chk.u_caps = cap_conv("CAP_NET_BIND_SERVICE"); chk.w_caps = cap_full; if (!check_permission(role, def_acl, "", &chk)) { fprintf(stderr, "CAP_NET_BIND_SERVICE is not " "removed for role %s. This would allow an " "attacker (if he can kill a network daemon) to " "launch a trojaned daemon that could steal privileged information\n\n", role->rolename); errs_found++; } chk.u_caps = cap_conv("CAP_SYS_TTY_CONFIG"); chk.w_caps = cap_full; if (!check_permission(role, def_acl, "", &chk)) { fprintf(stderr, "CAP_SYS_TTY_CONFIG is not " "removed for role %s. This would allow an " "attacker to hijack terminals of " "privileged processes\n\n", role->rolename); errs_found++; } chk.u_caps = cap_conv("CAP_SETFCAP"); chk.w_caps = cap_full; if (!check_permission(role, def_acl, "", &chk)) { fprintf(stderr, "CAP_SETFCAP is not " "removed for role %s. This would allow an " "attacker to set and modify file " "capabilities.\n\n", role->rolename); errs_found++; } errs_found += check_path_env(role, def_acl); errs_found += check_lib_paths(role, def_acl); errs_found += check_lilo_conf(role, def_acl); } /* end of per-role checks */ errs_found += handle_notrojan_mode(); check_symlinks(); if (errs_found) { printf("There were %d holes found in your RBAC " "configuration. These must be fixed before the " "RBAC system will be allowed to be enabled.\n", errs_found); exit(EXIT_FAILURE); } return; } gradm/gradm_adm.c0000644000175000017510000003021313152754421014115 0ustar spenderspender/* * Copyright (C) 2002-2014 Bradley Spengler, Open Source Security, Inc. * http://www.grsecurity.net spender@grsecurity.net * * This file is part of gradm. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "gradm.h" #define ADD_OBJ(x, y) \ add_proc_object_acl(current_subject, (x), proc_object_mode_conv(y), GR_FEXIST) static struct protoent *gr_getprotobyname(const char *name) { struct protoent *proto; proto = getprotobyname(name); if (proto == NULL) { fprintf(stderr, "Error while parsing /etc/protocols.\n"); exit(EXIT_FAILURE); } return proto; } int is_valid_elf_binary(const char *filename) { Elf32_Ehdr header_elf; Elf64_Ehdr header_elf64; int fd; if ((fd = open(filename, O_RDONLY)) < 0) return 0; if ((read(fd, &header_elf64, sizeof (header_elf64)) != sizeof (header_elf64))) goto failure; memcpy(&header_elf, &header_elf64, sizeof(header_elf)); /* binary is 32bit */ if (header_elf.e_ident[EI_CLASS] == 1) { if (strncmp((char *)header_elf.e_ident, ELFMAG, SELFMAG)) goto failure; if (header_elf.e_type != ET_EXEC && header_elf.e_type != ET_DYN) goto failure; /* binary is 64bit */ } else if (header_elf64.e_ident[EI_CLASS] == 2) { if (strncmp((char *)header_elf64.e_ident, ELFMAG, SELFMAG)) goto failure; if (header_elf64.e_type != ET_EXEC && header_elf64.e_type != ET_DYN) goto failure; } else goto failure; close(fd); return 1; failure: close(fd); return 0; } static void find_gradm_path(char *gradm_realpath) { char gradm_procpath[21] = { 0 }; snprintf(gradm_procpath, sizeof (gradm_procpath), "/proc/%d/exe", getpid()); if (readlink(gradm_procpath, gradm_realpath, PATH_MAX - 1) < 0) failure("readlink"); gradm_realpath[PATH_MAX - 1] = '\0'; return; } extern int gr_enable; void add_gradm_acl(struct role_acl *role) { struct stat fstat; char gradm_realpath[PATH_MAX] = { 0 }; char *gradm_name; struct ip_acl ip; struct protoent *proto; char *gradm_path; char *grpam_path; find_gradm_path(gradm_realpath); gradm_name = gr_strdup(gradm_realpath); if (bikeshedding_detected()) { gradm_path = get_bikeshedded_path(GRADM_PATH); grpam_path = get_bikeshedded_path(GRPAM_PATH); } else { gradm_path = GRADM_PATH; grpam_path = GRPAM_PATH; } if (gr_enable && strcmp(gradm_name, gradm_path)) { printf("You are attempting to use a gradm binary other " "than the installed version. Depending on your " "policy, you could be locking yourself out of " "your machine by enabling the RBAC system with " "this binary. Press \'y\' if you wish to ignore " "this warning, or any other key to cancel.\n>"); if (getchar() != 'y') exit(EXIT_FAILURE); } add_proc_subject_acl(role, gradm_name, proc_subject_mode_conv("ado"), 0); if (!stat(GRDEV_PATH, &fstat)) { ADD_OBJ(GRDEV_PATH, "w"); } else { fprintf(stderr, "%s does not " "exist. Please recompile your kernel with " "grsecurity and install a newer version of gradm.\n", GRDEV_PATH); exit(EXIT_FAILURE); } /* for NFS */ proto = gr_getprotobyname("udp"); memset(&ip, 0, sizeof (ip)); ip.low = 2049; ip.high = 2049; ip.type = (1U << SOCK_DGRAM); ip.proto[IPPROTO_IP / 32] |= (1U << (IPPROTO_IP % 32)); ip.proto[proto->p_proto / 32] |= (1U << (proto->p_proto % 32)); add_ip_acl(current_subject, GR_IP_CONNECT, &ip); memset(&ip, 0, sizeof (ip)); add_ip_acl(current_subject, GR_IP_BIND, &ip); ADD_OBJ("/", "h"); ADD_OBJ("/etc/ld.so.cache", "r"); ADD_OBJ("/etc/ld.so.preload", "r"); ADD_OBJ("/etc/ld.so.nohwcap", "r"); ADD_OBJ("/etc/protocols", "r"); ADD_OBJ("/dev/urandom", "r"); ADD_OBJ("/lib", "rx"); ADD_OBJ("/usr/lib", "rx"); ADD_OBJ("/lib32", "rx"); ADD_OBJ("/libx32", "rx"); ADD_OBJ("/usr/lib32", "rx"); ADD_OBJ("/usr/libx32", "rx"); ADD_OBJ("/lib64", "rx"); ADD_OBJ("/usr/lib64", "rx"); ADD_OBJ(gradm_name, "x"); ADD_OBJ(grpam_path, "x"); add_cap_acl(current_subject, "-CAP_ALL", NULL); add_cap_acl(current_subject, "+CAP_IPC_LOCK", NULL); return; } void add_gradm_pam_acl(struct role_acl *role) { struct ip_acl ip; struct protoent *proto; char *grpam_path; if (bikeshedding_detected()) grpam_path = get_bikeshedded_path(GRPAM_PATH); else grpam_path = GRPAM_PATH; add_proc_subject_acl(role, grpam_path, proc_subject_mode_conv("ado"), 0); ADD_OBJ(GRDEV_PATH, "w"); /* for NFS */ proto = gr_getprotobyname("udp"); memset(&ip, 0, sizeof (ip)); ip.low = 2049; ip.high = 2049; ip.type = (1U << SOCK_DGRAM); ip.proto[IPPROTO_IP / 32] |= (1U << (IPPROTO_IP % 32)); ip.proto[proto->p_proto / 32] |= (1U << (proto->p_proto % 32)); add_ip_acl(current_subject, GR_IP_CONNECT, &ip); /* for TCP/UDP LDAP */ proto = gr_getprotobyname("tcp"); memset(&ip, 0, sizeof (ip)); ip.low = 389; ip.high = 389; ip.type = (1U << SOCK_STREAM); ip.proto[IPPROTO_IP / 32] |= (1U << (IPPROTO_IP % 32)); ip.proto[proto->p_proto / 32] |= (1U << (proto->p_proto % 32)); add_ip_acl(current_subject, GR_IP_CONNECT, &ip); proto = gr_getprotobyname("udp"); memset(&ip, 0, sizeof (ip)); ip.low = 389; ip.high = 389; ip.type = (1U << SOCK_DGRAM); ip.proto[IPPROTO_IP / 32] |= (1U << (IPPROTO_IP % 32)); ip.proto[proto->p_proto / 32] |= (1U << (proto->p_proto % 32)); add_ip_acl(current_subject, GR_IP_CONNECT, &ip); /* for TCP SSL LDAP */ proto = gr_getprotobyname("tcp"); memset(&ip, 0, sizeof (ip)); ip.low = 636; ip.high = 636; ip.type = (1U << SOCK_STREAM); ip.proto[IPPROTO_IP / 32] |= (1U << (IPPROTO_IP % 32)); ip.proto[proto->p_proto / 32] |= (1U << (proto->p_proto % 32)); add_ip_acl(current_subject, GR_IP_CONNECT, &ip); /* for Kerberos */ proto = gr_getprotobyname("tcp"); memset(&ip, 0, sizeof (ip)); ip.low = 88; ip.high = 88; ip.type = (1U << SOCK_STREAM); ip.proto[IPPROTO_IP / 32] |= (1U << (IPPROTO_IP % 32)); ip.proto[proto->p_proto / 32] |= (1U << (proto->p_proto % 32)); add_ip_acl(current_subject, GR_IP_CONNECT, &ip); /* no binding allowed */ memset(&ip, 0, sizeof (ip)); add_ip_acl(current_subject, GR_IP_BIND, &ip); ADD_OBJ("/", "h"); ADD_OBJ("/etc/default/passwd", "r"); ADD_OBJ("/etc/ld.so.cache", "r"); ADD_OBJ("/etc/ld.so.preload", "r"); ADD_OBJ("/etc/ld.so.nohwcap", "r"); ADD_OBJ("/etc/localtime", "r"); ADD_OBJ("/etc/login.defs", "r"); ADD_OBJ("/etc/protocols", "r"); ADD_OBJ("/etc/passwd", "r"); ADD_OBJ("/etc/shadow", "r"); ADD_OBJ("/etc/pam.d", "r"); ADD_OBJ("/etc/pam.conf", "r"); ADD_OBJ("/etc/security", "r"); ADD_OBJ("/usr/share/zoneinfo", "r"); ADD_OBJ("/etc/nsswitch.conf", "r"); ADD_OBJ("/etc/ldap.conf", "r"); ADD_OBJ("/etc/ldap/ldap.conf", "r"); ADD_OBJ("/etc/krb5.conf", "r"); ADD_OBJ("/dev/urandom", "r"); ADD_OBJ("/proc", ""); ADD_OBJ("/proc/filesystems", "r"); ADD_OBJ("/selinux", "r"); ADD_OBJ("/dev", ""); ADD_OBJ("/dev/tty", "rw"); ADD_OBJ("/dev/tty?", "rw"); ADD_OBJ("/dev/pts", "rw"); ADD_OBJ("/var/run", ""); ADD_OBJ("/run", ""); ADD_OBJ("/run/resolvconf/resolv.conf", "r"); ADD_OBJ("/run/nscd/socket", "rw"); ADD_OBJ("/var/run/utmp", "rw"); ADD_OBJ("/var/run/utmpx", "rw"); ADD_OBJ("/var/log/faillog", "rw"); ADD_OBJ("/dev/log", "rw"); ADD_OBJ("/run/systemd/journal/dev-log", "rw"); ADD_OBJ("/dev/null", "rw"); ADD_OBJ("/lib", "rx"); ADD_OBJ("/usr/lib", "rx"); ADD_OBJ("/lib32", "rx"); ADD_OBJ("/usr/lib32", "rx"); ADD_OBJ("/lib64", "rx"); ADD_OBJ("/usr/lib64", "rx"); ADD_OBJ("/tmp", ""); ADD_OBJ("/tmp/krb5cc_pam*", "rwcd"); ADD_OBJ(grpam_path, "x"); add_cap_acl(current_subject, "-CAP_ALL", NULL); add_cap_acl(current_subject, "+CAP_IPC_LOCK", NULL); add_cap_acl(current_subject, "+CAP_AUDIT_WRITE", NULL); add_sock_family(current_subject, "netlink"); return; } void add_kernel_acl(void) { add_role_acl(¤t_role, gr_strdup(":::kernel:::"), role_mode_conv("sN"), 1); add_proc_subject_acl(current_role, "/", proc_subject_mode_conv("kvo"), 0); ADD_OBJ("/", "rwxcdl"); ADD_OBJ(GRSEC_DIR, "h"); return; } void add_grlearn_acl(struct role_acl *role) { struct stat fstat; struct ip_acl ip; char *grlearn_path; if (bikeshedding_detected()) grlearn_path = get_bikeshedded_path(GRLEARN_PATH); else grlearn_path = GRLEARN_PATH; if (stat(grlearn_path, &fstat)) { fprintf(stderr, "%s does not exist. Please reinstall gradm.\n", grlearn_path); exit(EXIT_FAILURE); } add_proc_subject_acl(role, grlearn_path, proc_subject_mode_conv("hpado"), 0); memset(&ip, 0, sizeof (ip)); add_ip_acl(current_subject, GR_IP_CONNECT, &ip); add_ip_acl(current_subject, GR_IP_BIND, &ip); ADD_OBJ("/", "h"); ADD_OBJ(grlearn_path, "x"); add_cap_acl(current_subject, "-CAP_ALL", NULL); return; } static void add_fulllearn_admin_acl(void) { add_role_acl(¤t_role, gr_strdup("admin"), role_mode_conv("sA"), 0); add_proc_subject_acl(current_role, "/", proc_subject_mode_conv("aorvk"), 0); ADD_OBJ("/", "rwcdmlxi"); return; } static void add_fulllearn_shutdown_acl(void) { struct ip_acl ip; add_role_acl(¤t_role, gr_strdup("shutdown"), role_mode_conv("sARG"), 0); add_proc_subject_acl(current_role, "/", proc_subject_mode_conv("rvkao"), 0); ADD_OBJ("/", ""); ADD_OBJ("/dev", ""); ADD_OBJ("/dev/urandom", "r"); ADD_OBJ("/dev/random", "r"); ADD_OBJ("/etc", "r"); ADD_OBJ("/bin", "rx"); ADD_OBJ("/sbin", "rx"); ADD_OBJ("/lib", "rx"); ADD_OBJ("/lib32", "rx"); ADD_OBJ("/lib64", "rx"); ADD_OBJ("/usr", "rx"); ADD_OBJ("/proc", "r"); ADD_OBJ("/boot", "h"); ADD_OBJ("/dev/grsec", "h"); ADD_OBJ("/dev/kmem", "h"); ADD_OBJ("/dev/mem", "h"); ADD_OBJ("/dev/port", "h"); ADD_OBJ("/etc/grsec", "h"); ADD_OBJ("/proc/kcore", "h"); ADD_OBJ("/proc/slabinfo", "h"); ADD_OBJ("/proc/modules", "h"); ADD_OBJ("/proc/kallsyms", "h"); ADD_OBJ("/lib/modules", "hs"); ADD_OBJ("/lib32/modules", "hs"); ADD_OBJ("/lib64/modules", "hs"); ADD_OBJ("/etc/ssh", "h"); add_cap_acl(current_subject, "-CAP_ALL", NULL); memset(&ip, 0, sizeof (ip)); add_ip_acl(current_subject, GR_IP_CONNECT, &ip); add_ip_acl(current_subject, GR_IP_BIND, &ip); return; } void add_fulllearn_acl(void) { struct ip_acl ip; add_kernel_acl(); add_fulllearn_admin_acl(); add_fulllearn_shutdown_acl(); add_role_acl(¤t_role, gr_strdup("default"), role_mode_conv("A"), 0); add_role_transition(current_role, "admin"); add_role_transition(current_role, "shutdown"); add_proc_subject_acl(current_role, "/", proc_subject_mode_conv("ol"), 0); ADD_OBJ("/", "h"); add_cap_acl(current_subject, "-CAP_ALL", NULL); memset(&ip, 0, sizeof (ip)); add_ip_acl(current_subject, GR_IP_CONNECT, &ip); add_ip_acl(current_subject, GR_IP_BIND, &ip); add_gradm_acl(current_role); parse_learn_config(); expand_acls(); return; } void add_rolelearn_acl(void) { struct ip_acl ip; add_proc_subject_acl(current_role, "/", proc_subject_mode_conv("ol"), 0); ADD_OBJ("/", "h"); add_cap_acl(current_subject, "-CAP_ALL", NULL); memset(&ip, 0, sizeof (ip)); add_ip_acl(current_subject, GR_IP_CONNECT, &ip); add_ip_acl(current_subject, GR_IP_BIND, &ip); return; } void start_grlearn(char *logfile) { pid_t pid; int ret; unlink(GR_LEARN_PIPE_PATH); ret = mkfifo(GR_LEARN_PIPE_PATH, S_IRUSR | S_IWUSR); if (ret == -1) { fprintf(stderr, "Error creating pipe.\n"); exit(EXIT_FAILURE); } pid = fork(); if (!pid) { execl(GRLEARN_PATH, GRLEARN_PATH, logfile, (char *)NULL); exit(EXIT_FAILURE); } else if (pid > 0) { char b; int fd; fd = open(GR_LEARN_PIPE_PATH, O_RDONLY); if (fd < 0) { fprintf(stderr, "Unable to open pipe.\n"); kill(pid, 9); exit(EXIT_FAILURE); } (void)read(fd, &b, 1); close(fd); } else { fprintf(stderr, "Error starting grlearn.\n"); exit(EXIT_FAILURE); } return; } void stop_grlearn(void) { pid_t pid; pid = fork(); if (!pid) { execl(GRLEARN_PATH, GRLEARN_PATH, "-stop", (char *)NULL); } return; } gradm/gradm_pam.c0000644000175000017510000000676313152754421014146 0ustar spenderspender/* * Copyright (C) 2002-2014 Bradley Spengler, Open Source Security, Inc. * http://www.grsecurity.net spender@grsecurity.net * * This file is part of gradm. It constitutes the PAM-based authentication of gradm. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include "gradm.h" #define PAM_SERVICENAME "gradm" int gradm_pam_conv(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr) { int i, x; struct pam_response *response; char *p; /* arbitrary OpenSSH-style limiting */ if (num_msg <= 0 || num_msg > 1000) return PAM_CONV_ERR; response = malloc(num_msg * sizeof(struct pam_response)); if (response == NULL) return PAM_CONV_ERR; for (i = 0; i < num_msg; i++) { response[i].resp_retcode = 0; response[i].resp = NULL; switch (msg[i]->msg_style) { case PAM_PROMPT_ECHO_ON: fputs(msg[i]->msg, stdout); response[i].resp = calloc(1, PAM_MAX_RESP_SIZE); if (response[i].resp == NULL) failure("calloc"); p = NULL; while (p == NULL) p = fgets(response[i].resp, PAM_MAX_RESP_SIZE, stdin); while (*p) { if (*p == '\n') *p = '\0'; else p++; } break; case PAM_PROMPT_ECHO_OFF: p = getpass(msg[i]->msg); if (p == NULL) failure("getpass"); response[i].resp = strdup(p); /* zero out static buffer */ memset(p, 0, strlen(p)); if (response[i].resp == NULL) failure("strdup"); break; case PAM_ERROR_MSG: fputs(msg[i]->msg, stderr); break; case PAM_TEXT_INFO: fputs(msg[i]->msg, stdout); break; default: for (x = i; x >= 0; x--) { if (response[x].resp != NULL) { memset(response[x].resp, 0, strlen(response[x].resp)); free(response[x].resp); response[x].resp = NULL; } } free(response); return PAM_CONV_ERR; } } *resp = response; return PAM_SUCCESS; } int main(int argc, char *argv[]) { pam_handle_t *pamh = NULL; int retval; struct pam_conv conv = { gradm_pam_conv, NULL }; struct gr_arg_wrapper wrapper; struct gr_arg arg; int fd; if (argc != 2) exit(EXIT_FAILURE); wrapper.version = GRADM_VERSION; wrapper.size = sizeof(struct gr_arg); wrapper.arg = &arg; arg.mode = GRADM_STATUS; if ((fd = open(GRDEV_PATH, O_WRONLY)) < 0) { fprintf(stderr, "Could not open %s.\n", GRDEV_PATH); failure("open"); } retval = write(fd, &wrapper, sizeof(struct gr_arg_wrapper)); close(fd); if (retval != 1) exit(EXIT_FAILURE); retval = pam_start(PAM_SERVICENAME, argv[1], &conv, &pamh); if (retval == PAM_SUCCESS) retval = pam_authenticate(pamh, 0); if (retval == PAM_SUCCESS) retval = pam_acct_mgmt(pamh, 0); if (retval == PAM_AUTHTOK_EXPIRED) retval = pam_chauthtok(pamh, 0); if (pamh) pam_end(pamh, retval); if (retval != PAM_SUCCESS) exit(EXIT_FAILURE); return EXIT_SUCCESS; } gradm/gradm_arg.c0000644000175000017510000003454713152754421014143 0ustar spenderspender/* * Copyright (C) 2002-2015 Bradley Spengler, Open Source Security, Inc. * http://www.grsecurity.net spender@grsecurity.net * * This file is part of gradm. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "gradm.h" static void show_version(void) { printf("gradm v%s - grsecurity RBAC administration and policy analysis utility\n" "Copyright 2002-2015 - Brad Spengler, Open Source Security, Inc.\n" "Email: spender@grsecurity.net\n\n" "This program is free software: you can redistribute it and/or modify\n" "it under the terms of the GNU General Public License version 2 as published\n" "by the Free Software Foundation.\n\n" "This program is distributed in the hope that it will be useful,\n" "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" "GNU General Public License for more details.\n\n" "You should have received a copy of the GNU General Public License\n" "along with this program. If not, see .\n\n" , GR_VERSION); exit(EXIT_SUCCESS); } static void show_help(void) { printf("gradm %s\n" "grsecurity RBAC administration and policy analysis utility\n\n" "Usage: gradm [option] ... \n\n" "Examples:\n" " gradm -P\n" " gradm -F -L /etc/grsec/learning.logs -O /etc/grsec/policy\n" "Options:\n" " -E, --enable Enable the grsecurity RBAC system\n" " -D, --disable Disable the grsecurity RBAC system\n" " -C, --check Check RBAC policy for errors\n" " -S, --status Check status of RBAC system\n" " -F, --fulllearn Enable full system learning\n" " -P [rolename], --passwd\n" " Create password for RBAC administration\n" " or a special role\n" " -R, --reload Reload the RBAC system while in admin mode\n" " Reloading will happen atomically, preserving\n" " special roles and inherited subjects\n" " -r, --oldreload Reload the RBAC system using the old method that\n" " drops existing special roles and inherited subjects\n" " -L , --learn\n" " Specify the pathname for learning logs\n" " -O , --output\n" " Specify where to place policies generated from\n" " learning mode. Should be a directory only if\n" " \"split-roles\" is specified in learn_config and\n" " full-learning is used.\n" " -M , --modsegv\n" " Remove a ban on a specific file or UID\n" " -a , --auth\n" " Authenticates to a special role that requires auth\n" " -u, --unauth Remove yourself from your current special role\n" " -n , --noauth\n" " Transitions to a special role that doesn't\n" " require authentication\n" " -p , --pamauth\n" " Authenticates to a special role through PAM\n" " -V, --verbose Display verbose policy statistics when enabling system\n" " -h, --help Display this help\n" " -v, --version Display version and GPLv2 license information\n", GR_VERSION); exit(EXIT_SUCCESS); return; } static void conv_name_to_num(const char *filename, u_int32_t *dev, u_int64_t * inode) { struct stat64 fstat; if (stat64(filename, &fstat) != 0) { fprintf(stderr, "Unable to stat %s: %s\n", filename, strerror(errno)); exit(EXIT_FAILURE); } if (is_24_kernel != 0) { *dev = MKDEV_24(MAJOR_24(fstat.st_dev), MINOR_24(fstat.st_dev)); } else { *dev = MKDEV_26(MAJOR_26(fstat.st_dev), MINOR_26(fstat.st_dev)); } *inode = fstat.st_ino; return; } static void verbose_stats(void) { struct role_acl *rtmp; struct proc_acl *stmp; struct file_acl *otmp; unsigned int uroles=0, groles=0, saroles=0, snroles=0, aroles=0, troles=0; unsigned int ksubjs=0, smsubjs=0, tsubjs=0, nsubjs=0, ussubjs=0; unsigned int chsobjs=0, tobjs=0; for_each_role(rtmp, current_role) { if (strcmp(rtmp->rolename,":::kernel:::") == 0) { continue; } troles++; if (rtmp->roletype & GR_ROLE_SPECIAL) { if (rtmp->roletype & (GR_ROLE_NOPW | GR_ROLE_PAM)) { snroles++; } else { saroles++; } if (rtmp->roletype & GR_ROLE_GOD) { aroles++; } } else if (rtmp->roletype & GR_ROLE_USER) { uroles++; } else if (rtmp->roletype & GR_ROLE_GROUP) { groles++; } else { /* default role */ ; } for_each_subject(stmp, rtmp) { tsubjs++; if ((stmp->mode & GR_PROTECTED) == 0) ksubjs++; if ((stmp->mode & GR_PROTSHM) == 0) smsubjs++; if (stmp->ips == NULL) ussubjs++; for_each_file_object(otmp, stmp) { tobjs++; if (otmp->mode & GR_SETID && ((rtmp->roletype & GR_ROLE_GOD) == 0)) chsobjs++; } } } printf("Policy statistics:\n"); printf("-------------------------------------------------------\n"); printf("Role summary:\n"); printf("\t%u user roles\n", uroles); printf("\t%u group roles\n", groles); printf("\t%u special roles with authentication\n", saroles); printf("\t%u special roles without authentication\n", snroles); printf("\t%u admin roles\n", aroles); printf("\t%u total roles\n\n", troles); printf("Subject summary:\n"); printf("\t%u nested subjects\n", nsubjs); printf("\t%u subjects can be killed by outside processes\n", ksubjs); printf("\t%u subjects have unprotected shared memory\n", smsubjs); printf("\t%u subjects with unrestricted sockets\n", ussubjs); printf("\t%u total subjects\n\n", tsubjs); printf("Object summary:\n"); printf("\t%u objects in non-admin roles allow chmod +s\n", chsobjs); printf("\t%u total objects\n", tobjs); return; } static FILE *open_learn_log(char *learn_log) { FILE *learnfile = NULL; if (strcmp(learn_log, "-") == 0) { learnfile = stdin; } else { learnfile = fopen(learn_log, "r"); if (learnfile == NULL) { fprintf(stderr, "Unable to open learning log: %s.\n" "Error: %s\n", learn_log, strerror(errno)); exit(EXIT_FAILURE); } } learn_log_buffer = (char *)gr_alloc(LEARN_LOG_BUFFER_SIZE); // buffer the learn log if possible setvbuf(learnfile, learn_log_buffer, _IOFBF, LEARN_LOG_BUFFER_SIZE); return learnfile; } int gr_learn = 0; int gr_enable = 0; int gr_check = 0; int gr_fulllearn = 0; void parse_args(int argc, char *argv[]) { int next_option = 0; int err; int verbose = 0; char *learn_log = NULL; int gr_output = 0; struct gr_pw_entry entry; struct gr_arg_wrapper *grarg; char cwd[PATH_MAX]; const char *const short_opts = "SVECFuDP::RrL:O:M:a:p:n:hv"; const struct option long_opts[] = { {"help", 0, NULL, 'h'}, {"version", 0, NULL, 'v'}, {"status", 0, NULL, 'S'}, {"enable", 0, NULL, 'E'}, {"check", 0, NULL, 'C'}, {"disable", 0, NULL, 'D'}, {"passwd", 2, NULL, 'P'}, {"auth", 1, NULL, 'a'}, {"noauth", 1, NULL, 'n'}, {"reload", 0, NULL, 'R'}, {"oldreload", 0, NULL, 'r'}, {"modsegv", 1, NULL, 'M'}, {"verbose", 0, NULL, 'V'}, {"learn", 1, NULL, 'L'}, {"fulllearn", 0, NULL, 'F'}, {"output", 1, NULL, 'O'}, {"unauth", 0, NULL, 'u'}, {"pamauth", 1, NULL, 'p'}, {NULL, 0, NULL, 0} }; if (!getcwd(cwd, PATH_MAX - 1)) { fprintf(stderr, "Error getting current directory.\n"); exit(EXIT_FAILURE); } err = mlock(&entry, sizeof (entry)); if (err && !getuid()) fprintf(stderr, "Warning: Unable to lock password " "into physical memory.\n"); memset(&entry, 0, sizeof (struct gr_pw_entry)); if (argc < 2) show_help(); while ((next_option = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) { switch (next_option) { case 'V': verbose = 1; break; case 'S': if (argc > 2) show_help(); check_acl_status(GRADM_STATUS); break; case 'C': if (argc > 3 || gr_enable) show_help(); gr_check = 1; parse_acls(); expand_acls(); analyze_acls(); break; case 'E': if (argc > 5 || gr_check) show_help(); entry.mode = GRADM_ENABLE; check_acl_status(entry.mode); gr_enable = 1; parse_acls(); expand_acls(); break; case 'F': if (argc > 7) show_help(); entry.mode = GRADM_ENABLE; gr_fulllearn = 1; gr_enable = 1; break; case 'u': if (argc > 2) show_help(); entry.mode = GRADM_UNSPROLE; check_acl_status(entry.mode); grarg = conv_user_to_kernel(&entry); transmit_to_kernel(grarg); break; case 'R': case 'r': if (argc > 3) show_help(); if (next_option == 'R') entry.mode = GRADM_RELOAD; else entry.mode = GRADM_OLDRELOAD; check_acl_status(entry.mode); get_user_passwd(&entry, GR_PWONLY); parse_acls(); expand_acls(); analyze_acls(); grarg = conv_user_to_kernel(&entry); read_saltandpass(entry.rolename, grarg->arg->salt, grarg->arg->sum); transmit_to_kernel(grarg); break; case 'M': if ((argc != 3) || (optind > argc) || (strlen(optarg) < 1)) show_help(); entry.mode = GRADM_MODSEGV; check_acl_status(entry.mode); get_user_passwd(&entry, GR_PWONLY); if (isdigit(optarg[0])) entry.segv_uid = atoi(optarg); else conv_name_to_num(optarg, &entry.segv_dev, &entry.segv_inode); grarg = conv_user_to_kernel(&entry); transmit_to_kernel(grarg); exit(EXIT_SUCCESS); break; case 'D': if (argc > 2) show_help(); entry.mode = GRADM_DISABLE; check_acl_status(entry.mode); get_user_passwd(&entry, GR_PWONLY); grarg = conv_user_to_kernel(&entry); transmit_to_kernel(grarg); exit(EXIT_SUCCESS); break; case 'L': if (argc > 7 || argc < 3) show_help(); gr_learn = 1; if (optarg) { char pathbuf[PATH_MAX]; if ((*optarg == '/') || !strcmp(optarg, "-")) learn_log = gr_strdup(optarg); else { strcpy(pathbuf, cwd); if (strlen(optarg) + strlen(pathbuf) + 2 > PATH_MAX) { fprintf(stderr, "Unable to open %s for learning logs.\n", optarg); exit(EXIT_FAILURE); } strcat(pathbuf, "/"); strcat(pathbuf, optarg); learn_log = gr_strdup(pathbuf); } } break; case 'O': if (argc > 6 || argc < 3) show_help(); gr_output = 1; if (optarg) output_log = gr_strdup(optarg); break; case 'P': if (argc > 3) show_help(); entry.mode = GRADM_PASSSET; check_acl_status(entry.mode); if (argc == 3) { strncpy((char *)entry.rolename, argv[2], GR_SPROLE_LEN); entry.rolename[GR_SPROLE_LEN - 1] = '\0'; printf("Setting up password for role %s\n", entry.rolename); } else printf("Setting up grsecurity RBAC password\n"); get_user_passwd(&entry, GR_PWANDSUM); generate_salt(&entry); generate_hash(&entry); write_user_passwd(&entry); memset(&entry, 0, sizeof (struct gr_pw_entry)); exit(EXIT_SUCCESS); break; case 'a': if (argc != 3) show_help(); strncpy((char *)entry.rolename, argv[2], GR_SPROLE_LEN); entry.rolename[GR_SPROLE_LEN - 1] = '\0'; entry.mode = GRADM_SPROLE; check_acl_status(entry.mode); get_user_passwd(&entry, GR_PWONLY); grarg = conv_user_to_kernel(&entry); transmit_to_kernel(grarg); exit(EXIT_SUCCESS); break; case 'p': if (argc != 3) show_help(); strncpy((char *)entry.rolename, argv[2], GR_SPROLE_LEN); entry.rolename[GR_SPROLE_LEN - 1] = '\0'; entry.mode = GRADM_SPROLEPAM; check_pam_auth(entry.rolename); check_acl_status(entry.mode); grarg = conv_user_to_kernel(&entry); transmit_to_kernel(grarg); exit(EXIT_SUCCESS); break; case 'n': if (argc != 3) show_help(); strncpy((char *)entry.rolename, argv[2], GR_SPROLE_LEN); entry.rolename[GR_SPROLE_LEN - 1] = '\0'; entry.mode = GRADM_SPROLE; check_acl_status(entry.mode); grarg = conv_user_to_kernel(&entry); transmit_to_kernel(grarg); exit(EXIT_SUCCESS); break; case 'v': if (argc > 2) show_help(); show_version(); break; case 'h': show_help(); break; default: show_help(); break; } } if (optind < argc) show_help(); if (gr_check) { if (verbose) verbose_stats(); return; } if ((gr_output && !gr_learn)) { fprintf(stderr, "-L and -O must be used together.\n"); exit(EXIT_FAILURE); } if ((gr_fulllearn && !gr_learn)) { fprintf(stderr, "-L and -F must be used together.\n"); exit(EXIT_FAILURE); } if (gr_fulllearn && gr_learn && gr_output) gr_enable = 0; if (gr_enable) { /* analyze here since we know if learning is being used */ if (!gr_fulllearn) analyze_acls(); check_acl_status(entry.mode); if (verbose) verbose_stats(); if (gr_fulllearn) add_fulllearn_acl(); grarg = conv_user_to_kernel(&entry); read_saltandpass(entry.rolename, grarg->arg->salt, grarg->arg->sum); if (gr_learn) { start_grlearn(learn_log); free(learn_log); } transmit_to_kernel(grarg); } else if (gr_learn && gr_output) { FILE *stream = NULL; FILE *learnfile; learnfile = open_learn_log(learn_log); if (!strcmp(output_log, "stdout")) stream = stdout; else if (!strcmp(output_log, "stderr")) stream = stderr; else { struct stat logstat; if (!gr_fulllearn || stat(output_log, &logstat) || !S_ISDIR(logstat.st_mode)) { if (gr_fulllearn) { /* wipe out any existing policy */ stream = fopen(output_log, "w"); } else { stream = fopen(output_log, "a"); } if (!stream) { fprintf(stderr, "Unable to open %s for writing.\n" "Error: %s\n", output_log, strerror(errno)); exit(EXIT_FAILURE); } } } add_to_string_array(&high_protected_paths, GRSEC_DIR); add_to_string_array(&high_protected_paths, GRDEV_PATH); parse_learn_config(); if (gr_fulllearn) generate_full_learned_acls(learnfile, stream); else handle_learn_logs(learnfile, stream); free(learn_log); free(output_log); } return; } gradm/gradm_cap.c0000644000175000017510000001123513152754421014122 0ustar spenderspender/* * Copyright (C) 2002-2014 Bradley Spengler, Open Source Security, Inc. * http://www.grsecurity.net spender@grsecurity.net * * This file is part of gradm. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "gradm.h" struct capability_set capability_list[] = { {"CAP_CHOWN", 0}, {"CAP_DAC_OVERRIDE", 1}, {"CAP_DAC_READ_SEARCH", 2}, {"CAP_FOWNER", 3}, {"CAP_FSETID", 4}, {"CAP_KILL", 5}, {"CAP_SETGID", 6}, {"CAP_SETUID", 7}, {"CAP_SETPCAP", 8}, {"CAP_LINUX_IMMUTABLE", 9}, {"CAP_NET_BIND_SERVICE", 10}, {"CAP_NET_BROADCAST", 11}, {"CAP_NET_ADMIN", 12}, {"CAP_NET_RAW", 13}, {"CAP_IPC_LOCK", 14}, {"CAP_IPC_OWNER", 15}, {"CAP_SYS_MODULE", 16}, {"CAP_SYS_RAWIO", 17}, {"CAP_SYS_CHROOT", 18}, {"CAP_SYS_PTRACE", 19}, {"CAP_SYS_PACCT", 20}, {"CAP_SYS_ADMIN", 21}, {"CAP_SYS_BOOT", 22}, {"CAP_SYS_NICE", 23}, {"CAP_SYS_RESOURCE", 24}, {"CAP_SYS_TIME", 25}, {"CAP_SYS_TTY_CONFIG", 26}, {"CAP_MKNOD", 27}, {"CAP_LEASE", 28}, {"CAP_AUDIT_WRITE", 29}, {"CAP_AUDIT_CONTROL", 30}, {"CAP_SETFCAP", 31}, {"CAP_MAC_OVERRIDE", 32}, {"CAP_MAC_ADMIN", 33}, {"CAP_SYSLOG", 34}, {"CAP_WAKE_ALARM", 35}, {"CAP_BLOCK_SUSPEND", 36}, {"CAP_AUDIT_READ", 37}, {"CAP_ALL", ~0} }; gr_cap_t cap_combine(gr_cap_t a, gr_cap_t b) { int i; gr_cap_t ret; for (i = 0; i < 2; i++) ret.cap[i] = a.cap[i] | b.cap[i]; return ret; } gr_cap_t cap_drop(gr_cap_t a, gr_cap_t b) { int i; gr_cap_t ret; for (i = 0; i < 2; i++) ret.cap[i] = a.cap[i] &~ b.cap[i]; return ret; } gr_cap_t cap_intersect(gr_cap_t a, gr_cap_t b) { int i; gr_cap_t ret; for (i = 0; i < 2; i++) ret.cap[i] = a.cap[i] & b.cap[i]; return ret; } gr_cap_t cap_invert(gr_cap_t a) { int i; gr_cap_t ret; for (i = 0; i < 2; i++) ret.cap[i] = ~a.cap[i]; return ret; } int cap_isclear(gr_cap_t a) { if (a.cap[0] || a.cap[1]) return 0; return 1; } int cap_same(gr_cap_t a, gr_cap_t b) { if (a.cap[0] == b.cap[0] && a.cap[1] == b.cap[1]) return 1; return 0; } gr_cap_t cap_conv(const char *cap) { gr_cap_t retcap = {{ 0, 0 }}; int i; for (i = 0; i < sizeof (capability_list) / sizeof (struct capability_set); i++) if (!strcmp(cap, capability_list[i].cap_name)) { if (i == (sizeof (capability_list) / sizeof (struct capability_set) - 1)) { retcap.cap[0] = ~0; retcap.cap[1] = ~0; /* CAP_ALL */ } else cap_raise(retcap, capability_list[i].cap_val); return retcap; } fprintf(stderr, "Invalid capability name \"%s\" on line %lu of %s.\n" "The RBAC system will not load until this" " error is fixed.\n", cap, lineno, current_acl_file); exit(EXIT_FAILURE); return retcap; } void add_cap_acl(struct proc_acl *subject, const char *cap, const char *audit) { gr_cap_t kcap = cap_conv(cap + 1); if (!subject) { fprintf(stderr, "Error on line %lu of %s. Attempt to " "add a capability without a subject declaration.\n" "The RBAC system will not load until this " "error is fixed.\n", lineno, current_acl_file); exit(EXIT_FAILURE); } if (*cap == '+') { subject->cap_drop = cap_drop(subject->cap_drop, kcap); subject->cap_mask = cap_combine(subject->cap_mask, kcap); if (audit && !strcmp(audit, "audit")) subject->cap_invert_audit = cap_combine(subject->cap_invert_audit, kcap); else if (audit) { fprintf(stderr, "Error on line %lu of %s. \"suppress\" can only be applied to denied capabilities. The RBAC system will not load until this error is fixed.\n", lineno, current_acl_file); exit(EXIT_FAILURE); } } else { subject->cap_drop = cap_combine(subject->cap_drop, kcap); subject->cap_mask = cap_combine(subject->cap_mask, kcap); if (audit && !strcmp(audit, "suppress")) subject->cap_invert_audit = cap_combine(subject->cap_invert_audit, kcap); else if (audit) { fprintf(stderr, "Error on line %lu of %s. \"audit\" can only be applied to permitted capabilities. The RBAC system will not load until this error is fixed.\n", lineno, current_acl_file); exit(EXIT_FAILURE); } } return; } void modify_caps(struct proc_acl *proc, int cap) { cap_lower(proc->cap_drop, cap); cap_raise(proc->cap_mask, cap); return; } gradm/gradm_fulllearn_pass2.y0000644000175000017510000000675213152754421016511 0ustar spenderspender%{ /* * Copyright (C) 2002-2014 Bradley Spengler, Open Source Security, Inc. * http://www.grsecurity.net spender@grsecurity.net * * This file is part of gradm. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "gradm.h" extern int fulllearn_pass2lex(void); extern struct gr_learn_group_node *the_role_list; %} %union { char * string; unsigned long num; } %token FILENAME ROLENAME %token NUM IPADDR USER GROUP %type filename %type id_type %% learn_logs: learn_log | learn_logs learn_log ; filename: /*empty*/ { $$ = gr_strdup(""); } | FILENAME { if (!strcmp($1, "//")) $1[1] = '\0'; $$ = $1; } ; id_type: USER | GROUP ; learn_log: error | ROLENAME ':' NUM ':' NUM ':' NUM ':' filename ':' filename ':' NUM ':' NUM ':' filename ':' NUM ':' IPADDR { struct gr_learn_group_node *group = NULL; struct gr_learn_user_node *user = NULL; uid_t uid; gid_t gid; unsigned long res1, res2; u_int32_t addr; char *filename = $9; /* check if we have an inherited learning subject */ if (strcmp($11, "/")) { filename = $11; free($9); } else free($11); uid = $5; gid = $7; res1 = $13; res2 = $15; addr = $21; match_role(the_role_list, uid, gid, &group, &user); if (user) insert_ip(&(user->allowed_ips), addr, 0, 0, 0); else if (group) insert_ip(&(group->allowed_ips), addr, 0, 0, 0); if (user && ((!strcmp($17, "") && strlen(filename) > 1 && !res1 && !res2) || is_protected_path($17, $19))) insert_learn_user_subject(user, conv_filename_to_struct(filename, GR_PROCFIND | GR_OVERRIDE)); else if (group && ((!strcmp($17, "") && strlen(filename) > 1 && !res1 && !res2) || is_protected_path($17, $19))) insert_learn_group_subject(group, conv_filename_to_struct(filename, GR_PROCFIND | GR_OVERRIDE)); free(filename); free($17); } | ROLENAME ':' NUM ':' NUM ':' NUM ':' filename ':' filename ':' IPADDR ':' NUM ':' NUM ':' NUM ':' NUM ':' IPADDR { struct gr_learn_group_node *group = NULL; struct gr_learn_user_node *user = NULL; uid_t uid; gid_t gid; u_int32_t addr; char *filename = $9; /* check if we have an inherited learning subject */ if (strcmp($11, "/")) { filename = $11; free($9); } else free($11); uid = $5; gid = $7; addr = $23; match_role(the_role_list, uid, gid, &group, &user); if (user) { insert_ip(&(user->allowed_ips), addr, 0, 0, 0); insert_learn_user_subject(user, conv_filename_to_struct(filename, GR_PROCFIND | GR_OVERRIDE)); } else if (group) { insert_ip(&(group->allowed_ips), addr, 0, 0, 0); insert_learn_group_subject(group, conv_filename_to_struct(filename, GR_PROCFIND | GR_OVERRIDE)); } free(filename); } | ROLENAME ':' NUM ':' NUM ':' NUM ':' filename ':' filename ':' id_type ':' NUM ':' NUM ':' NUM ':' IPADDR { free($9); free($11); } ; %% gradm/gradm_pax.c0000644000175000017510000000361613152754421014153 0ustar spenderspender/* * Copyright (C) 2002-2014 Bradley Spengler, Open Source Security, Inc. * http://www.grsecurity.net spender@grsecurity.net * * This file is part of gradm. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "gradm.h" struct paxflag_set paxflag_list[] = { {"PAX_SEGMEXEC", 0}, {"PAX_PAGEEXEC", 1}, {"PAX_MPROTECT", 2}, {"PAX_RANDMMAP", 3}, {"PAX_EMUTRAMP", 4} }; u_int16_t paxflag_conv(const char *paxflag) { int i; for (i = 0; i < sizeof (paxflag_list) / sizeof (struct paxflag_set); i++) if (!strcmp(paxflag, paxflag_list[i].paxflag_name)) return (1U << (paxflag_list[i].paxflag_val)); fprintf(stderr, "Invalid PaX flag name \"%s\" on line %lu of %s.\n" "The RBAC system will not load until this" " error is fixed.\n", paxflag, lineno, current_acl_file); exit(EXIT_FAILURE); return 0; } void add_paxflag_acl(struct proc_acl *subject, const char *paxflag) { u_int16_t kpaxflag = paxflag_conv(paxflag + 1); if (!subject) { fprintf(stderr, "Error on line %lu of %s. Attempt to " "add a PaX flag without a subject declaration.\n" "The RBAC system will not load until this " "error is fixed.\n", lineno, current_acl_file); exit(EXIT_FAILURE); } if (*paxflag == '+') subject->pax_flags |= kpaxflag; else subject->pax_flags |= (kpaxflag << 8); return; } gradm/grlearn_config.y0000644000175000017510000000532713152754421015217 0ustar spenderspender%{ /* * Copyright (C) 2002-2014 Bradley Spengler, Open Source Security, Inc. * http://www.grsecurity.net spender@grsecurity.net * * This file is part of gradm. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "gradm.h" extern int grlearn_configlex(void); %} %union { char * string; unsigned long num; } %token FILENAME NOLEARN INHERITLEARN INHERITNOLEARN DONTREDUCE %token PROTECTED HIGHPROTECTED HIGHREDUCE ALWAYSREDUCE NOALLOWEDIPS %token READPROTECTED SPLITROLES %token NUM %% learn_config_file: learn_config | learn_config_file learn_config ; learn_config: NOLEARN FILENAME { if (current_role != NULL) { add_proc_subject_acl(current_role, $2, proc_subject_mode_conv("o"), 0); add_proc_object_acl(current_subject, "/", proc_object_mode_conv("rwxcdm"), GR_FEXIST); } } | INHERITLEARN FILENAME { struct ip_acl ip; if (current_role != NULL) { add_proc_subject_acl(current_role, $2, proc_subject_mode_conv("oi"), 0); add_proc_object_acl(current_subject, "/", proc_object_mode_conv("h"), GR_FEXIST); add_cap_acl(current_subject, "-CAP_ALL", NULL); memset(&ip, 0, sizeof (ip)); add_ip_acl(current_subject, GR_IP_CONNECT, &ip); add_ip_acl(current_subject, GR_IP_BIND, &ip); } } | INHERITNOLEARN FILENAME { if (current_role != NULL) { add_proc_subject_acl(current_role, $2, proc_subject_mode_conv("o"), 0); add_proc_object_acl(current_subject, "/", proc_object_mode_conv("rwxcdmi"), GR_FEXIST); } } | DONTREDUCE FILENAME { add_to_string_array(&dont_reduce_dirs, $2); } | PROTECTED FILENAME { add_to_string_array(&protected_paths, $2); } | READPROTECTED FILENAME { add_to_string_array(&read_protected_paths, $2); } | HIGHREDUCE FILENAME { add_to_string_array(&high_reduce_dirs, $2); } | ALWAYSREDUCE FILENAME { add_to_string_array(&always_reduce_dirs, $2); } | HIGHPROTECTED FILENAME { add_to_string_array(&high_protected_paths, $2); } | NOALLOWEDIPS { add_grlearn_option(GR_DONT_LEARN_ALLOWED_IPS); } | SPLITROLES { add_grlearn_option(GR_SPLIT_ROLES); } ; %% gradm/gradm_nest.c0000644000175000017510000000640113152754421014327 0ustar spenderspender/* * Copyright (C) 2002-2016 Bradley Spengler, Open Source Security, Inc. * http://www.grsecurity.net spender@grsecurity.net * * This file is part of gradm. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "gradm.h" void add_proc_nested_acl(struct role_acl *role, const char *mainsubjname, const char * const *nestednames, int nestlen, u_int32_t nestmode) { int i; char *nestname; unsigned int namelen = 0; struct proc_acl *stmp; struct file_acl *otmp = NULL; struct stat fstat; if (nestmode & GR_LEARN) { fprintf(stderr, "Error on line %lu of %s:\n", lineno, current_acl_file); fprintf(stderr, "Learning is not yet implemented for nested subjects.\n"); exit(EXIT_FAILURE); } namelen += strlen(mainsubjname); for (i = 0; i < nestlen; i++) namelen += strlen(nestednames[i]) + 1; nestname = (char *)gr_alloc(namelen + 1); strcpy(nestname, mainsubjname); for (i = 0; i < nestlen; i++) sprintf(nestname + strlen(nestname), ":%s", nestednames[i]); stmp = lookup_acl_subject_by_name(role, mainsubjname); if (stmp == NULL) { fprintf(stderr, "No subject %s found for nested subject %s specified on line %lu of %s.\n", mainsubjname, nestname, lineno, current_acl_file); exit(EXIT_FAILURE); } for (i = 0; i < nestlen; i++) { otmp = lookup_acl_object_by_name(stmp, nestednames[i]); if (otmp == NULL) { fprintf(stderr, "No object %s found for nested subject %s " "specified on line %lu of %s.\n", nestednames[i], nestname, lineno, current_acl_file); exit(EXIT_FAILURE); } else if (!otmp->nested && (i != nestlen - 1)) { fprintf(stderr, "No nested subject %s found for nested " "subject %s specified on line %lu of %s.\n", nestednames[i], nestname, lineno, current_acl_file); exit(EXIT_FAILURE); } else if (otmp->nested && (i == nestlen - 1)) { fprintf(stderr, "Duplicate nested subject %s found on line " "%lu of %s.\n", nestname, lineno, current_acl_file); exit(EXIT_FAILURE); } if (i != nestlen - 1) stmp = otmp->nested; } add_proc_subject_acl(role, nestednames[i - 1], nestmode, GR_FFAKE); namelen = strlen(nestednames[i-1]); for_each_file_object(otmp, stmp) { if (!strncmp(nestednames[i-1], otmp->filename, namelen) && (otmp->filename[namelen] == '/' || otmp->filename[namelen] == '\0')) if (otmp->mode & GR_EXEC) otmp->nested = current_subject; } if (!(current_subject->mode & GR_OVERRIDE) && strcmp(current_subject->filename, "/")) current_subject->parent_subject = stmp; if (!stat(nestednames[i - 1], &fstat) && S_ISREG(fstat.st_mode)) add_proc_object_acl(current_subject, nestednames[i - 1], proc_object_mode_conv("rx"), GR_FLEARN); return; } gradm/gradm_lib.c0000644000175000017510000004716313152754421014136 0ustar spenderspender/* * Copyright (C) 2002-2015 Bradley Spengler, Open Source Security, Inc. * http://www.grsecurity.net spender@grsecurity.net * * This file is part of gradm. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "gradm.h" int bikeshedding_detected(void) { struct stat64 st; if (!lstat64("/sbin", &st) && S_ISLNK(st.st_mode)) return 1; return 0; } char *get_bikeshedded_path(const char *path) { unsigned int len = strlen(path); struct stat64 st; char *buf = gr_alloc(len + strlen("/usr") + 1); if (strncmp(path, "/bin/", 5) && strncmp(path, "/sbin/", 6)) { strcpy(buf, path); return buf; } strcpy(buf, "/usr"); /* lennart breaking things for the fun of it */ if (!lstat64("/usr/sbin", &st) && S_ISLNK(st.st_mode) && !lstat64("/usr/bin", &st) && !S_ISLNK(st.st_mode) && !strncmp("/sbin/", path, 6)) { strcat(buf, "/bin/"); strcat(buf, path + 6); } else strcat(buf, path); return buf; } char *get_anchor(const char *filename) { char *basepoint = gr_strdup(filename); char *p, *p2; /* calculates basepoint, eg basepoint of /home/ * /test is /home */ p = p2 = basepoint; while (*p != '\0') { if (*p == '/') p2 = p; if (*p == '?' || *p == '*' || *p == '[') break; p++; } /* if base is / */ if (p2 == basepoint) *(p2 + 1) = '\0'; else *p2 = '\0'; return basepoint; } int anchorcmp(const char *path1, const char *path2) { char *anchor1, *anchor2; int ret; anchor1 = get_anchor(path1); anchor2 = get_anchor(path2); ret = strcmp(anchor1, anchor2); free(anchor1); free(anchor2); return ret; } int is_globbed_file(const char *filename) { if (strchr(filename, '*') || strchr(filename, '?') || strchr(filename, '[')) return 1; else return 0; } int match_filename(const char *filename, const char *pattern, unsigned int len, int is_glob) { if (is_glob) return fnmatch(pattern, filename, 0); else if (!strncmp(filename, pattern, len) && (filename[len] == '\0' || filename[len] == '/')) return 0; return 1; } void add_to_string_array(char *** array, const char *str) { unsigned int size = 0; if (*array == NULL) *array = (char **)gr_alloc(2 * sizeof(char *)); while (*(*array + size)) size++; *array = (char **)gr_realloc(*array, (size + 2) * sizeof(char *)); memset(*array + size, 0, 2 * sizeof(char *)); // fix the warning for this *(const char **)(*array + size) = str; return; } char * gr_strdup(const char *p) { char *ret; ret = strdup(p); if (ret == NULL) failure("strdup"); return ret; } void * gr_alloc(size_t len) { void *ptr; ptr = calloc(1, len); if (ptr == NULL) failure("calloc"); return ptr; } void * gr_realloc(void *addr, size_t len) { void *ptr; if (addr == NULL) return gr_alloc(len); ptr = realloc(addr, len); if (ptr == NULL) failure("realloc"); return ptr; } void gr_free(void *addr) { free(addr); return; } unsigned long table_sizes[] = { 13, 31, 61, 127, 251, 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521, 131071, 262139, 524287, 1048573, 2097143, 4194301, 8388593, 16777213, 33554393, 67108859, 134217689, 268435399, 536870909, 1073741789, 2147483647 }; static __inline__ unsigned long fhash(const unsigned long ino, const unsigned int dev, const unsigned long sz) { return (((ino + dev) ^ ((ino << 13) + (ino << 23) + (dev << 9))) % sz); } /* Name hashing routines. Initial hash value */ /* Hash courtesy of the R5 hash in reiserfs modulo sign bits */ #define init_name_hash() 0 /* partial hash update function. Assume roughly 4 bits per character */ static __inline__ unsigned long partial_name_hash(unsigned long c, unsigned long prevhash) { return (prevhash + (c << 4) + (c >> 4)) * 11; } /* Finally: cut down the number of bits to a int value (and try to avoid losing bits) */ static __inline__ unsigned long end_name_hash(unsigned long hash) { return (unsigned int) hash; } /* Compute the hash for a name string. */ static __inline__ unsigned int full_name_hash(const unsigned char * name) { unsigned long hash = init_name_hash(); while (*name != '\0') hash = partial_name_hash(*name++, hash); return end_name_hash(hash); } static __inline__ unsigned long nhash(const char *name, const unsigned long sz) { return full_name_hash((const unsigned char *)name) % sz; } void insert_hash_entry(struct gr_hash_struct *hash, void *entry); void insert_name_entry(struct gr_hash_struct *hash, void *entry); void resize_hash_table(struct gr_hash_struct *hash) { unsigned long i; struct gr_hash_struct *newhash; newhash = (struct gr_hash_struct *)gr_alloc(sizeof(struct gr_hash_struct)); for (i = 0; i < sizeof(table_sizes)/sizeof(table_sizes[0]); i++) { if (table_sizes[i] > hash->table_size) { newhash->table_size = table_sizes[i]; break; } } if (newhash->table_size == 0) { fprintf(stderr, "Out of memory.\n"); exit(EXIT_FAILURE); } newhash->table = (void **)gr_alloc(newhash->table_size * sizeof(void *)); newhash->nametable = NULL; if (hash->type != GR_HASH_FILENAME) { newhash->nametable = (void **)gr_alloc(newhash->table_size * sizeof(void *)); } newhash->used_size = 0; newhash->type = hash->type; newhash->first = hash->first; for (i = 0; i < hash->table_size; i++) if (hash->table[i]) { insert_hash_entry(newhash, hash->table[i]); insert_name_entry(newhash, hash->table[i]); } free(hash->table); if (hash->nametable) free(hash->nametable); memcpy(hash, newhash, sizeof(struct gr_hash_struct)); free(newhash); return; } void *lookup_name_entry(struct gr_hash_struct *hash, const char *name) { if (hash == NULL) return NULL; if (hash->type == GR_HASH_OBJECT) { unsigned long index = nhash(name, hash->table_size); struct file_acl *match; unsigned char i = 0; match = (struct file_acl *)hash->nametable[index]; while (match && strcmp(match->filename, name)) { index = (index + (1U << i)) % hash->table_size; match = (struct file_acl *)hash->nametable[index]; i = (i + 1) % 32; } return match; } else if (hash->type == GR_HASH_SUBJECT) { unsigned long index = nhash(name, hash->table_size); struct proc_acl *match; unsigned char i = 0; match = (struct proc_acl *)hash->nametable[index]; while (match && strcmp(match->filename, name)) { index = (index + (1U << i)) % hash->table_size; match = (struct proc_acl *)hash->nametable[index]; i = (i + 1) % 32; } return match; } return NULL; } struct file_acl *lookup_acl_object_by_name(struct proc_acl *subject, const char *name) { return (struct file_acl *)lookup_name_entry(subject->hash, name); } struct proc_acl *lookup_acl_subject_by_name(struct role_acl *role, const char *name) { return (struct proc_acl *)lookup_name_entry(role->hash, name); } void *lookup_hash_entry(struct gr_hash_struct *hash, const void *entry) { if (hash == NULL) return NULL; if (hash->type == GR_HASH_OBJECT) { const struct file_acl *object = (const struct file_acl *)entry; unsigned long index = fhash(object->inode, object->dev, hash->table_size); struct file_acl *match; unsigned char i = 0; match = (struct file_acl *)hash->table[index]; while (match && (match->inode != object->inode || match->dev != object->dev)) { index = (index + (1U << i)) % hash->table_size; match = (struct file_acl *)hash->table[index]; i = (i + 1) % 32; } return match; } else if (hash->type == GR_HASH_SUBJECT) { const struct proc_acl *subject = (const struct proc_acl *)entry; unsigned long index = fhash(subject->inode, subject->dev, hash->table_size); struct proc_acl *match; unsigned char i = 0; match = (struct proc_acl *)hash->table[index]; while (match && (match->inode != subject->inode || match->dev != subject->dev)) { index = (index + (1U << i)) % hash->table_size; match = (struct proc_acl *)hash->table[index]; i = (i + 1) % 32; } return match; } else if (hash->type == GR_HASH_FILENAME) { const char *filename = (const char *)entry; u_int32_t key = full_name_hash((const unsigned char *)filename); u_int32_t index = key % hash->table_size; struct gr_learn_file_tmp_node *match; unsigned char i = 0; match = (struct gr_learn_file_tmp_node *)hash->table[index]; while (match && (match->key != key || strcmp(match->filename, filename))) { index = (index + (1U << i)) % hash->table_size; match = (struct gr_learn_file_tmp_node *)hash->table[index]; i = (i + 1) % 32; } return match; } return NULL; } static struct gr_hash_struct *mount_hash; struct gr_learn_file_tmp_node *conv_filename_to_struct(const char *filename, u_int32_t mode) { struct gr_learn_file_tmp_node *node; node = (struct gr_learn_file_tmp_node *)gr_alloc(sizeof(struct gr_learn_file_tmp_node)); node->filename = gr_strdup(filename); node->mode = mode; return node; } void create_mount_hash(void) { FILE *f = fopen("/proc/mounts", "r"); char buf[4096] = { }; char *p, *p2; struct gr_learn_file_tmp_node *node; mount_hash = create_hash_table(GR_HASH_FILENAME); if (f == NULL) return; while(fgets(buf, sizeof(buf)-1, f)) { p = strchr(buf, ' '); if (!p) continue; p2 = strchr(p+1, ' '); if (!p2) continue; *p2 = '\0'; node = conv_filename_to_struct((const char *)p+1, 0); insert_hash_entry(mount_hash, node); } fclose(f); } int get_canonical_inodev(const char *name, u_int64_t *ino, u_int32_t *dev, int *is_symlink) { struct stat64 st; char *dirname; char *parentdir; DIR *dir; struct dirent *dirent; if (is_symlink) { if (lstat64(name, &st)) return 0; *is_symlink = S_ISLNK(st.st_mode); } else { if (stat64(name, &st)) return 0; } *ino = st.st_ino; if (mount_hash == NULL) create_mount_hash(); if (mount_hash == NULL || !lookup_hash_entry(mount_hash, name)) goto normal; // if this is a mount root , obtain the inode of the mountpoint // instead so that we can hide mountpoints from readdir at least dirname = strrchr(name, '/'); if (dirname == NULL) goto normal; parentdir = gr_alloc(strlen(name) + 4); /* skip past / */ dirname++; strcpy(parentdir, name); strcat(parentdir, "/.."); dir = opendir(parentdir); if (dir == NULL) { gr_free(parentdir); goto normal; } while ((dirent = readdir(dir))) { if (strcmp(dirent->d_name, dirname)) continue; if (lstat64(parentdir, &st)) { closedir(dir); free(parentdir); goto normal; } *ino = dirent->d_ino; break; } closedir(dir); normal: if (is_24_kernel) *dev = MKDEV_24(MAJOR_24(st.st_dev), MINOR_24(st.st_dev)); else *dev = MKDEV_26(MAJOR_26(st.st_dev), MINOR_26(st.st_dev)); return 1; } struct file_acl *lookup_acl_object_by_inodev(struct proc_acl *subject, const char *name) { struct file_acl obj; if (!get_canonical_inodev(name, &obj.inode, &obj.dev, NULL)) return NULL; return (struct file_acl *)lookup_hash_entry(subject->hash, (const void *)&obj); } struct file_acl *lookup_acl_object_by_inodev_nofollow(struct proc_acl *subject, const char *name) { struct file_acl obj; int is_symlink; if (!get_canonical_inodev(name, &obj.inode, &obj.dev, &is_symlink)) return NULL; return (struct file_acl *)lookup_hash_entry(subject->hash, (const void *)&obj); } struct file_acl *lookup_acl_object(struct proc_acl *subject, struct file_acl *object) { struct file_acl *obj; obj = (struct file_acl *)lookup_hash_entry(subject->hash, (const struct file_acl *)object); if (obj && !(obj->mode & GR_DELETED) && !(object->mode & GR_DELETED)) return obj; else return NULL; } struct gr_learn_file_tmp_node *lookup_learn_object(struct gr_learn_file_node *subject, char *filename) { return (struct gr_learn_file_tmp_node *)lookup_hash_entry(subject->hash, (const char *)filename); } struct gr_learn_file_tmp_node *lookup_learn_role_subject(struct gr_learn_role_entry *role, char *filename) { return (struct gr_learn_file_tmp_node *)lookup_hash_entry(role->hash, (const char *)filename); } struct gr_learn_file_tmp_node *lookup_learn_group_subject(struct gr_learn_group_node *role, char *filename) { return (struct gr_learn_file_tmp_node *)lookup_hash_entry(role->hash, (const char *)filename); } struct gr_learn_file_tmp_node *lookup_learn_user_subject(struct gr_learn_user_node *role, char *filename) { return (struct gr_learn_file_tmp_node *)lookup_hash_entry(role->hash, (const char *)filename); } struct proc_acl *lookup_acl_subject(struct role_acl *role, struct proc_acl *subject) { return (struct proc_acl *)lookup_hash_entry(role->hash, (const struct proc_acl *)subject); } void insert_name_entry(struct gr_hash_struct *hash, void *entry) { if (hash->type == GR_HASH_OBJECT) { struct file_acl *object = (struct file_acl *)entry; unsigned long index = nhash(object->filename, hash->table_size); struct file_acl **curr; unsigned char i = 0; curr = (struct file_acl **)&hash->nametable[index]; while (*curr) { index = (index + (1U << i)) % hash->table_size; curr = (struct file_acl **)&hash->nametable[index]; i = (i + 1) % 32; } *curr = (struct file_acl *)entry; } else if (hash->type == GR_HASH_SUBJECT) { struct proc_acl *subject = (struct proc_acl *)entry; unsigned long index = nhash(subject->filename, hash->table_size); struct proc_acl **curr; unsigned char i = 0; curr = (struct proc_acl **)&hash->nametable[index]; while (*curr) { index = (index + (1U << i)) % hash->table_size; curr = (struct proc_acl **)&hash->nametable[index]; i = (i + 1) % 32; } *curr = (struct proc_acl *)entry; } } void insert_hash_entry(struct gr_hash_struct *hash, void *entry) { /* resize if we're over 50% full */ if ((hash->used_size + 1) > (hash->table_size / 2)) resize_hash_table(hash); if (hash->type == GR_HASH_OBJECT) { struct file_acl *object = (struct file_acl *)entry; unsigned long index = fhash(object->inode, object->dev, hash->table_size); struct file_acl **curr; unsigned char i = 0; curr = (struct file_acl **)&hash->table[index]; while (*curr) { index = (index + (1U << i)) % hash->table_size; curr = (struct file_acl **)&hash->table[index]; i = (i + 1) % 32; } *curr = (struct file_acl *)entry; insert_name_entry(hash, *curr); hash->used_size++; } else if (hash->type == GR_HASH_SUBJECT) { struct proc_acl *subject = (struct proc_acl *)entry; unsigned long index = fhash(subject->inode, subject->dev, hash->table_size); struct proc_acl **curr; unsigned char i = 0; curr = (struct proc_acl **)&hash->table[index]; while (*curr) { index = (index + (1U << i)) % hash->table_size; curr = (struct proc_acl **)&hash->table[index]; i = (i + 1) % 32; } *curr = (struct proc_acl *)entry; insert_name_entry(hash, *curr); hash->used_size++; } else if (hash->type == GR_HASH_FILENAME) { struct gr_learn_file_tmp_node *node = (struct gr_learn_file_tmp_node *)entry; u_int32_t key = full_name_hash((unsigned char *)node->filename); u_int32_t index = key % hash->table_size; struct gr_learn_file_tmp_node **curr; unsigned char i = 0; curr = (struct gr_learn_file_tmp_node **)&hash->table[index]; while (*curr && ((*curr)->key != key || strcmp(node->filename, (*curr)->filename))) { index = (index + (1U << i)) % hash->table_size; curr = (struct gr_learn_file_tmp_node **)&hash->table[index]; i = (i + 1) % 32; } if (*curr) { (*curr)->mode |= node->mode; free(node->filename); gr_free(node); } else { *curr = (struct gr_learn_file_tmp_node *)entry; (*curr)->key = key; hash->used_size++; } } } struct gr_hash_struct *create_hash_table(int type) { struct gr_hash_struct *hash; hash = (struct gr_hash_struct *)gr_alloc(sizeof(struct gr_hash_struct)); hash->table = (void **)gr_alloc(table_sizes[0] * sizeof(void *)); if (type != GR_HASH_FILENAME) { hash->nametable = (void **)gr_alloc(table_sizes[0] * sizeof(void *)); } hash->table_size = table_sizes[0]; hash->type = type; return hash; } void insert_learn_object(struct gr_learn_file_node *subject, struct gr_learn_file_tmp_node *object) { if (subject->hash == NULL) subject->hash = create_hash_table(GR_HASH_FILENAME); insert_hash_entry(subject->hash, object); } void insert_learn_role_subject(struct gr_learn_role_entry *role, struct gr_learn_file_tmp_node *subject) { if (role->hash == NULL) role->hash = create_hash_table(GR_HASH_FILENAME); insert_hash_entry(role->hash, subject); } void insert_learn_group_subject(struct gr_learn_group_node *role, struct gr_learn_file_tmp_node *subject) { if (role->hash == NULL) role->hash = create_hash_table(GR_HASH_FILENAME); insert_hash_entry(role->hash, subject); } void insert_learn_user_subject(struct gr_learn_user_node *role, struct gr_learn_file_tmp_node *subject) { if (role->hash == NULL) role->hash = create_hash_table(GR_HASH_FILENAME); insert_hash_entry(role->hash, subject); } void insert_acl_object(struct proc_acl *subject, struct file_acl *object) { if (subject->hash->first == NULL) { subject->hash->first = object; } else { ((struct file_acl *)subject->hash->first)->next = object; object->prev = (struct file_acl *)subject->hash->first; subject->hash->first = object; } insert_hash_entry(subject->hash, object); return; } void insert_acl_subject(struct role_acl *role, struct proc_acl *subject) { if (role->hash == NULL) { /* create object hash table */ role->hash = create_hash_table(GR_HASH_SUBJECT); role->hash->first = subject; } else { ((struct proc_acl *)role->hash->first)->next = subject; subject->prev = (struct proc_acl *)role->hash->first; role->hash->first = subject; } /* force every subject to have a hash table whether or not they have any objects */ subject->hash = create_hash_table(GR_HASH_OBJECT); insert_hash_entry(role->hash, subject); return; } void insert_nested_acl_subject(struct proc_acl *subject) { /* we won't iterate over these subjects via for_each_subject, so add them to a special list */ if (global_nested_subject_list == NULL) { global_nested_subject_list = subject; subject->next = NULL; } else { subject->next = global_nested_subject_list; global_nested_subject_list = subject; } /* force every subject to have a hash table whether or not they have any objects */ subject->hash = create_hash_table(GR_HASH_OBJECT); return; } struct gr_user_map { uid_t uid; char *user; struct gr_user_map *next; }; struct gr_group_map { gid_t gid; char *group; struct gr_group_map *next; }; static struct gr_user_map *user_list; static struct gr_group_map *group_list; const char *gr_get_user_name(uid_t uid) { struct gr_user_map *tmpuser = user_list; struct passwd *pwd; for_each_list_entry(tmpuser, user_list) { if (tmpuser->uid == uid) return tmpuser->user; } pwd = getpwuid(uid); if (pwd) { tmpuser = (struct gr_user_map *)gr_alloc(sizeof(struct gr_user_map)); tmpuser->uid = uid; tmpuser->user = gr_strdup(pwd->pw_name); tmpuser->next = user_list; user_list = tmpuser; return pwd->pw_name; } else return NULL; } const char *gr_get_group_name(gid_t gid) { struct gr_group_map *tmpgroup; struct group *grp; for_each_list_entry (tmpgroup, group_list) { if (tmpgroup->gid == gid) return tmpgroup->group; } grp = getgrgid(gid); if (grp) { tmpgroup = (struct gr_group_map *)gr_alloc(sizeof(struct gr_group_map)); tmpgroup->gid = gid; tmpgroup->group = gr_strdup(grp->gr_name); tmpgroup->next = group_list; group_list = tmpgroup; return grp->gr_name; } else return NULL; } gradm/gradm_fulllearn_pass3.y0000644000175000017510000001455713152754423016516 0ustar spenderspender%{ /* * Copyright (C) 2002-2014 Bradley Spengler, Open Source Security, Inc. * http://www.grsecurity.net spender@grsecurity.net * * This file is part of gradm. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "gradm.h" extern int fulllearn_pass3lex(void); extern struct gr_learn_group_node *the_role_list; %} %union { char * string; unsigned long num; } %token FILENAME ROLENAME %token NUM IPADDR USER GROUP %type filename %type id_type %% learn_logs: learn_log | learn_logs learn_log ; filename: /*empty*/ { $$ = gr_strdup(""); } | FILENAME { if (!strcmp($1, "//")) $1[1] = '\0'; $$ = $1; } ; id_type: USER | GROUP ; learn_log: error | ROLENAME ':' NUM ':' NUM ':' NUM ':' filename ':' filename ':' NUM ':' NUM ':' filename ':' NUM ':' IPADDR { struct gr_learn_group_node *group = NULL; struct gr_learn_user_node *user = NULL; struct gr_learn_file_node *subjlist = NULL; struct gr_learn_file_node *subject = NULL; uid_t uid; gid_t gid; u_int32_t mode; unsigned long res1, res2; char *filename = $9; /* check if we have an inherited learning subject */ if (strcmp($11, "/")) { filename = $11; free($9); } else free($11); uid = $5; gid = $7; mode = $19; res1 = $13; res2 = $15; match_role(the_role_list, uid, gid, &group, &user); /* only add objects for the role currently in memory */ if ((current_learn_rolemode == GR_ROLE_GROUP && group && !strcmp(group->rolename, current_learn_rolename)) || (current_learn_rolemode == GR_ROLE_USER && user && !strcmp(user->rolename, current_learn_rolename))) { if (user) subjlist = user->subject_list; else if (group) subjlist = group->subject_list; if (subjlist) subject = match_file_node(subjlist, filename); /* only learn objects for current subject in memory */ if (subject && !strcmp(subject->filename, current_learn_subject)) { if (subject && strcmp($17, "")) insert_learn_object(subject, conv_filename_to_struct($17, mode | GR_FIND)); else if (subject && strlen(filename) > 1 && !res1 && !res2) { if (subject->subject == NULL) { subject->subject = (struct gr_learn_subject_node *)calloc(1, sizeof(struct gr_learn_subject_node)); if (subject->subject == NULL) failure("calloc"); } cap_raise(subject->subject->cap_raise, mode); } } } free(filename); free($17); } | ROLENAME ':' NUM ':' NUM ':' NUM ':' filename ':' filename ':' IPADDR ':' NUM ':' NUM ':' NUM ':' NUM ':' IPADDR { struct gr_learn_group_node *group = NULL; struct gr_learn_user_node *user = NULL; struct gr_learn_file_node *subjlist = NULL; struct gr_learn_file_node *subject = NULL; uid_t uid; gid_t gid; u_int32_t addr; u_int16_t port; u_int8_t mode, proto, socktype; char *filename = $9; /* check if we have an inherited learning subject */ if (strcmp($11, "/")) { filename = $11; free($9); } else free($11); uid = $5; gid = $7; mode = $19; addr = $13; port = $15; socktype = $17; proto = $19; mode = $21; match_role(the_role_list, uid, gid, &group, &user); /* only add objects for the role currently in memory */ if ((current_learn_rolemode == GR_ROLE_GROUP && group && !strcmp(group->rolename, current_learn_rolename)) || (current_learn_rolemode == GR_ROLE_USER && user && !strcmp(user->rolename, current_learn_rolename))) { if (user) subjlist = user->subject_list; else if (group) subjlist = group->subject_list; if (subjlist) subject = match_file_node(subjlist, filename); /* only learn objects for current subject in memory */ if (subject && !strcmp(subject->filename, current_learn_subject)) { if (subject && mode == GR_IP_CONNECT) insert_ip(&(subject->connect_list), addr, port, proto, socktype); else if (subject && mode == GR_IP_BIND) insert_ip(&(subject->bind_list), addr, port, proto, socktype); else if (subject && mode == GR_SOCK_FAMILY) { if (subject->subject == NULL) { subject->subject = (struct gr_learn_subject_node *)calloc(1, sizeof(struct gr_learn_subject_node)); if (subject->subject == NULL) failure("calloc"); } subject->subject->sock_families[port / 32] |= (1U << (port % 32)); } } } free(filename); } | ROLENAME ':' NUM ':' NUM ':' NUM ':' filename ':' filename ':' id_type ':' NUM ':' NUM ':' NUM ':' IPADDR { struct gr_learn_group_node *group = NULL; struct gr_learn_user_node *user = NULL; struct gr_learn_file_node *subjlist = NULL; struct gr_learn_file_node *subject = NULL; uid_t uid; gid_t gid; unsigned int real, eff, fs; char *filename = $9; /* check if we have an inherited learning subject */ if (strcmp($11, "/")) { filename = $11; free($9); } else free($11); uid = $5; gid = $7; real = $15; eff = $17; fs = $19; match_role(the_role_list, uid, gid, &group, &user); /* only add objects for the role currently in memory */ if ((current_learn_rolemode == GR_ROLE_GROUP && group && !strcmp(group->rolename, current_learn_rolename)) || (current_learn_rolemode == GR_ROLE_USER && user && !strcmp(user->rolename, current_learn_rolename))) { if (user) subjlist = user->subject_list; else if (group) subjlist = group->subject_list; if (subjlist) subject = match_file_node(subjlist, filename); /* only learn objects for current subject in memory */ if (subject && !strcmp(subject->filename, current_learn_subject)) { if (subject && $13 == USER) insert_learn_id_transition(&(subject->user_trans_list), real, eff, fs); else if (subject && $13 == GROUP) insert_learn_id_transition(&(subject->group_trans_list), real, eff, fs); } } free(filename); } ; %% gradm/gradm_res.c0000644000175000017510000000751113152754423014154 0ustar spenderspender/* * Copyright (C) 2002-2014 Bradley Spengler, Open Source Security, Inc. * http://www.grsecurity.net spender@grsecurity.net * * This file is part of gradm. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "gradm.h" const char *rlim_table[GR_NLIMITS]; void init_res_table(void) { rlim_table[RLIMIT_CPU] = "RES_CPU"; rlim_table[RLIMIT_FSIZE] = "RES_FSIZE"; rlim_table[RLIMIT_DATA] = "RES_DATA"; rlim_table[RLIMIT_STACK] = "RES_STACK"; rlim_table[RLIMIT_CORE] = "RES_CORE"; rlim_table[RLIMIT_RSS] = "RES_RSS"; rlim_table[RLIMIT_NPROC] = "RES_NPROC"; rlim_table[RLIMIT_NOFILE] = "RES_NOFILE"; rlim_table[RLIMIT_MEMLOCK] = "RES_MEMLOCK"; rlim_table[RLIMIT_AS] = "RES_AS"; rlim_table[RLIMIT_LOCKS] = "RES_LOCKS"; rlim_table[RLIMIT_SIGPENDING] = "RES_SIGPENDING"; rlim_table[RLIMIT_MSGQUEUE] = "RES_MSGQUEUE"; rlim_table[RLIMIT_NICE] = "RES_NICE"; rlim_table[RLIMIT_RTPRIO] = "RES_RTPRIO"; rlim_table[RLIMIT_RTTIME] = "RES_RTTIME"; rlim_table[GR_CRASH_RES] = "RES_CRASH"; } static unsigned short name_to_res(const char *name) { int i; for (i = 0; i < SIZE(rlim_table); i++) { if (!rlim_table[i]) continue; if (!strcmp(rlim_table[i], name)) return i; } fprintf(stderr, "Invalid resource name: %s " "found on line %lu of %s.\n", name, lineno, current_acl_file); exit(EXIT_FAILURE); return 0; } static unsigned int res_to_mask(unsigned short res) { return (1U << res); } static unsigned long conv_res(const char *lim) { unsigned long res; char *p; int i; unsigned int len = strlen(lim); if (!strcmp("unlimited", lim)) return ~0UL; if (isdigit(lim[len - 1])) return atol(lim); if ((p = (char *) calloc(len + 1, sizeof (char))) == NULL) failure("calloc"); strcpy(p, lim); for (i = 0; i < len - 1; i++) { if (!isdigit(lim[i])) { fprintf(stderr, "Invalid resource limit: %s " "found on line %lu of %s.\n", lim, lineno, current_acl_file); exit(EXIT_FAILURE); } } p[i] = '\0'; res = atol(p); free(p); switch (lim[i]) { case 'm': res = res * 60; break; case 'h': res = res * 60 * 60; break; case 'd': res = res * 60 * 60 * 24; break; case 's': //res = res; break; case 'K': res = res << 10; break; case 'M': res = res << 20; break; case 'G': res = res << 30; break; default: fprintf(stderr, "Invalid resource limit: %s " "found on line %lu of %s.\n", lim, lineno, current_acl_file); exit(EXIT_FAILURE); } return res; } void modify_res(struct proc_acl *proc, int res, unsigned long cur, unsigned long max) { if ((res < 0) || (res >= SIZE(rlim_table))) return; if (proc->resmask & res_to_mask(res)) { proc->res[res].rlim_cur = cur; proc->res[res].rlim_max = max; } return; } void add_res_acl(struct proc_acl *subject, const char *name, const char *soft, const char *hard) { gr_rlimit_t lim; if (!subject) { fprintf(stderr, "Error on line %lu of %s. Attempt to " "add a resource without a subject declaration.\n" "The RBAC system will not load until this " "error is fixed.\n", lineno, current_acl_file); exit(EXIT_FAILURE); } lim.rlim_cur = conv_res(soft); lim.rlim_max = conv_res(hard); subject->resmask |= res_to_mask(name_to_res(name)); memcpy(&(subject->res[name_to_res(name)]), &lim, sizeof (lim)); return; } gradm/gradm.y0000644000175000017510000002722713152754423013337 0ustar spenderspender%{ /* * Copyright (C) 2002-2014 Bradley Spengler, Open Source Security, Inc. * http://www.grsecurity.net spender@grsecurity.net * * This file is part of gradm. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "gradm.h" extern int gradmlex(void); struct ip_acl ip; struct var_object *var_obj = NULL; char *nested[MAX_NEST_DEPTH]; int current_nest_depth = 0; %} %union { char *string; u_int32_t num; u_int16_t shortnum; struct var_object * var; } %token ROLE ROLE_NAME SUBJECT SUBJ_NAME OBJ_NAME HOSTNAME %token RES_NAME RES_SOFTHARD CONNECT BIND IPTYPE %token IPPROTO CAP_NAME ROLE_ALLOW_IP PAX_NAME %token ROLE_TRANSITION VARIABLE DEFINE DEFINE_NAME DISABLED %token ID_NAME USER_TRANS_ALLOW GROUP_TRANS_ALLOW %token USER_TRANS_DENY GROUP_TRANS_DENY DOMAIN_TYPE DOMAIN %token INTERFACE IPOVERRIDE REPLACE REP_ARG AUDIT %token SOCKALLOWFAMILY SOCKFAMILY ROLE_UMASK %token OBJ_MODE SUBJ_MODE IPADDR IPNETMASK NOT %token IPPORT ROLE_TYPE UMASK %type subj_mode obj_mode ip_netmask invert_socket %type role_type %type variable_expression %left '&' %left '|' %left '-' %% compiled_acl: various_acls | compiled_acl various_acls ; various_acls: role_label | replace_rule | domain_label | role_allow_ip | role_umask | role_transitions | subject_label | user_allow_label | user_deny_label | group_allow_label | group_deny_label | variable_object | variable_expression { interpret_variable($1); } | nested_label | object_file_label | object_cap_label | object_paxflag_label | object_res_label | object_connect_ip_label | object_bind_ip_label | object_ip_override_label | object_sock_allow_family ; replace_rule: REPLACE REP_ARG REP_ARG { add_replace_string($2, $3); } ; variable_expression: VARIABLE { $$ = sym_retrieve($1); if (!$$) { fprintf(stderr, "Variable \"%s\" not defined on line %lu of %s.\n", $1, lineno, current_acl_file); exit(EXIT_FAILURE); } } | variable_expression '|' variable_expression { $$ = union_objects($1, $3); } | variable_expression '&' variable_expression { $$ = intersect_objects($1, $3); } | variable_expression '-' variable_expression { $$ = differentiate_objects($1, $3); } | '(' variable_expression ')' { $$ = $2; } ; variable_object: DEFINE DEFINE_NAME '{' var_object_list '}' { if (sym_retrieve($2)) { fprintf(stderr, "Duplicate variable \"%s\" defined on line %lu of %s.\n", $2, lineno, current_acl_file); exit(EXIT_FAILURE); } sym_store($2, var_obj); var_obj = NULL; } ; var_object_file: OBJ_NAME obj_mode { add_file_var_object(&var_obj, $1, $2); } ; var_object_cap: CAP_NAME AUDIT { add_cap_var_object(&var_obj, $1, $2); free($1); free($2); } | CAP_NAME { add_cap_var_object(&var_obj, $1, NULL); free($1); } ; var_object_net: CONNECT invert_socket IPADDR ip_netmask ip_ports ip_typeproto { ip.addr = $3; ip.netmask = $4; add_net_var_object(&var_obj, &ip, GR_IP_CONNECT | $2, NULL); memset(&ip, 0, sizeof(ip)); } | CONNECT invert_socket HOSTNAME ip_netmask ip_ports ip_typeproto { ip.netmask = $4; add_net_var_object(&var_obj, &ip, GR_IP_CONNECT | $2, $3); memset(&ip, 0, sizeof(ip)); free($3); } | CONNECT DISABLED { add_net_var_object(&var_obj, &ip, GR_IP_CONNECT, NULL); } | BIND invert_socket IPADDR ip_netmask ip_ports ip_typeproto { ip.addr = $3; ip.netmask = $4; add_net_var_object(&var_obj, &ip, GR_IP_BIND | $2, NULL); memset(&ip, 0, sizeof(ip)); } | BIND invert_socket HOSTNAME ip_netmask ip_ports ip_typeproto { ip.netmask = $4; add_net_var_object(&var_obj, &ip, GR_IP_BIND | $2, $3); memset(&ip, 0, sizeof(ip)); free($3); } | BIND invert_socket INTERFACE ip_ports ip_typeproto { ip.iface = $3; add_net_var_object(&var_obj, &ip, GR_IP_BIND | $2, NULL); memset(&ip, 0, sizeof(ip)); } | BIND DISABLED { add_net_var_object(&var_obj, &ip, GR_IP_BIND, NULL); } ; var_object_list: var_object_file | var_object_net | var_object_cap | var_object_list var_object_file | var_object_list var_object_net | var_object_list var_object_cap ; domain_label: DOMAIN ROLE_NAME DOMAIN_TYPE { add_role_acl(¤t_role, $2, GR_ROLE_DOMAIN | role_mode_conv($3), 1); } domain_user_list ; domain_user_list: ROLE_NAME { add_domain_child(current_role, $1); } | domain_user_list ROLE_NAME { add_domain_child(current_role, $2); } ; role_label: ROLE ROLE_NAME role_type { add_role_acl(¤t_role, $2, $3, 0); } ; role_type: /* empty */ { $$ = role_mode_conv(""); } | ROLE_TYPE { $$ = $1; } ; subject_label: SUBJECT SUBJ_NAME subj_mode { if (current_role && current_role->roletype & GR_ROLE_LEARN) { fprintf(stderr, "Error on line %lu of %s.\n" "Subjects are not allowed for a role with learning enabled, as " "they are generated by the learning mode.\n" "The RBAC system will not load until you correct this error.\n\n", lineno, current_acl_file); exit(EXIT_FAILURE); } add_proc_subject_acl(current_role, $2, $3, 0); } ; nested_label: SUBJECT SUBJ_NAME nested_subjs subj_mode { add_proc_nested_acl(current_role, $2, (const char * const *)nested, current_nest_depth, $4); current_nest_depth = 0; } ; nested_subjs: ':' SUBJ_NAME { nested[current_nest_depth] = $2; current_nest_depth++; } | nested_subjs ':' SUBJ_NAME { if (current_nest_depth >= MAX_NEST_DEPTH) { fprintf(stderr, "Nesting too deep (over %d) on line %lu of %s.\n", MAX_NEST_DEPTH, lineno, current_acl_file); exit(EXIT_FAILURE); } nested[current_nest_depth] = $3; current_nest_depth++; } ; user_allow_ids: ID_NAME { add_id_transition(current_subject, $1, GR_ID_USER, GR_ID_ALLOW); } | user_allow_ids ID_NAME { add_id_transition(current_subject, $2, GR_ID_USER, GR_ID_ALLOW); } ; user_allow_label: USER_TRANS_ALLOW user_allow_ids { } ; user_deny_ids: ID_NAME { add_id_transition(current_subject, $1, GR_ID_USER, GR_ID_DENY); } | user_deny_ids ID_NAME { add_id_transition(current_subject, $2, GR_ID_USER, GR_ID_DENY); } ; user_deny_label: USER_TRANS_DENY user_deny_ids { } ; group_allow_ids: ID_NAME { add_id_transition(current_subject, $1, GR_ID_GROUP, GR_ID_ALLOW); } | group_allow_ids ID_NAME { add_id_transition(current_subject, $2, GR_ID_GROUP, GR_ID_ALLOW); } ; group_allow_label: GROUP_TRANS_ALLOW group_allow_ids { } ; group_deny_ids: ID_NAME { add_id_transition(current_subject, $1, GR_ID_GROUP, GR_ID_DENY); } | group_deny_ids ID_NAME { add_id_transition(current_subject, $2, GR_ID_GROUP, GR_ID_DENY); } ; group_deny_label: GROUP_TRANS_DENY group_deny_ids { } ; object_file_label: OBJ_NAME obj_mode { add_proc_object_acl(current_subject, $1, $2, GR_FEXIST); } ; object_cap_label: CAP_NAME AUDIT { add_cap_acl(current_subject, $1, $2); free($1); free($2); } | CAP_NAME { add_cap_acl(current_subject, $1, NULL); free($1); } ; object_paxflag_label: PAX_NAME { add_paxflag_acl(current_subject, $1); free($1); } ; object_res_label: RES_NAME RES_SOFTHARD RES_SOFTHARD { add_res_acl(current_subject, $1, $2, $3); free($1); free($2); free($3); } ; subj_mode: /* empty */ { $$ = proc_subject_mode_conv(""); } | SUBJ_MODE { $$ = $1; } ; obj_mode: /* empty */ { $$ = proc_object_mode_conv(""); } | OBJ_MODE { $$ = $1; } ; invert_socket: /* empty */ { $$ = 0; } | NOT { $$ = GR_IP_INVERT; } ; role_transitions: ROLE_TRANSITION role_names { } ; role_names: ROLE_NAME { add_role_transition(current_role, $1); } | role_names ROLE_NAME { add_role_transition(current_role, $2); } ; role_umask: ROLE_UMASK UMASK { set_role_umask(current_role, $2); } ; role_allow_ip: ROLE_ALLOW_IP IPADDR ip_netmask { add_role_allowed_ip(current_role, $2, $3); } | ROLE_ALLOW_IP HOSTNAME ip_netmask { add_role_allowed_host(current_role, $2, $3); } ; object_connect_ip_label: CONNECT invert_socket IPADDR ip_netmask ip_ports ip_typeproto { ip.addr = $3; ip.netmask = $4; add_ip_acl(current_subject, GR_IP_CONNECT | $2, &ip); memset(&ip, 0, sizeof(ip)); } | CONNECT invert_socket HOSTNAME ip_netmask ip_ports ip_typeproto { ip.netmask = $4; add_host_acl(current_subject, GR_IP_CONNECT | $2, $3, &ip); memset(&ip, 0, sizeof(ip)); free($3); } | CONNECT DISABLED { add_ip_acl(current_subject, GR_IP_CONNECT, &ip); } ; object_sock_allow_family: SOCKALLOWFAMILY sock_families { } ; sock_families: SOCKFAMILY { add_sock_family(current_subject, $1); } | sock_families SOCKFAMILY { add_sock_family(current_subject, $2); } ; object_ip_override_label: IPOVERRIDE IPADDR { current_subject->inaddr_any_override = $2; } ; object_bind_ip_label: BIND invert_socket IPADDR ip_netmask ip_ports ip_typeproto { ip.addr = $3; ip.netmask = $4; add_ip_acl(current_subject, GR_IP_BIND | $2, &ip); memset(&ip, 0, sizeof(ip)); } | BIND invert_socket HOSTNAME ip_netmask ip_ports ip_typeproto { ip.netmask = $4; add_host_acl(current_subject, GR_IP_BIND | $2, $3, &ip); memset(&ip, 0, sizeof(ip)); free($3); } | BIND invert_socket INTERFACE ip_ports ip_typeproto { ip.iface = $3; add_ip_acl(current_subject, GR_IP_BIND | $2, &ip); memset(&ip, 0, sizeof(ip)); } | BIND DISABLED { add_ip_acl(current_subject, GR_IP_BIND, &ip); } ; ip_netmask: /* emtpy */ { $$ = 0xffffffff; } | '/' IPNETMASK { $$ = $2; } ; ip_ports: /* emtpy */ { ip.low = 0; ip.high = 65535; } | ':' IPPORT { ip.low = ip.high = $2; } | ':' IPPORT '-' IPPORT { ip.low = $2; ip.high = $4; } ; ip_typeproto: IPPROTO { conv_name_to_type(&ip, $1); free($1); } | IPTYPE { conv_name_to_type(&ip, $1); free($1); } | ip_typeproto IPPROTO { conv_name_to_type(&ip, $2); free($2); } | ip_typeproto IPTYPE { conv_name_to_type(&ip, $2); free($2); } ; gradm/gradm_fulllearn_pass1.l0000644000175000017510000000364413152754423016472 0ustar spenderspender%{ /* * Copyright (C) 2002-2014 Bradley Spengler, Open Source Security, Inc. * http://www.grsecurity.net spender@grsecurity.net * * This file is part of gradm. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "gradm.h" #include "fulllearn_pass1.tab.h" void fulllearn_pass1error(const char *s); int fulllearn_pass1wrap(void); static struct in_addr ip; %} ROLENAME ^[_a-zA-Z0-9.-]{1,64} NOTAFILE [a-z]+"["[0-9]+"]" NUM [-]?[0-9]+ FILENAME [/][^\t\n]* IPADDR [0-9]{1,3}"."[0-9]{1,3}"."[0-9]{1,3}"."[0-9]{1,3} %option nounput %x ERROR %% "u" { fulllearn_pass1lval.num = USER; return USER; } "g" { fulllearn_pass1lval.num = GROUP; return GROUP; } {NUM} { fulllearn_pass1lval.num = atol(yytext); return NUM; } {NOTAFILE} { /* not used in grammar */ return FILENAME; } {ROLENAME} { /* not used in grammar */ return ROLENAME; } {FILENAME} { /* not used in grammar */ return FILENAME; } {IPADDR} { if (inet_aton(yytext, &ip)) fulllearn_pass1lval.num = ip.s_addr; else fulllearn_pass1lval.num = 0; return IPADDR; } [\t] { return ':'; } . ; %% void fulllearn_pass1error(const char *s) { return; } int fulllearn_pass1wrap(void) { return 1; }