/* * RSBAC REG decision module for RBAC model * * Author and (c) 1999-2009 Amon Ott <ao@rsbac.org> * Last change: 20/Jan/2009 */ /* general stuff */ #include <linux/module.h> #include <linux/types.h> #include <linux/kernel.h> #include <linux/string.h> /* for (un)lock_kernel() */ #include <linux/sched.h> #include <linux/smp.h> #include <linux/smp_lock.h> /* for file access */ #include <linux/fs.h> #include <linux/seq_file.h> #include <asm/uaccess.h> /* rsbac */ #include <rsbac/types.h> #include <rsbac/reg.h> #include <rsbac/adf.h> #include <rsbac/aci.h> #include <rsbac/lists.h> #include <rsbac/getname.h> #include <rsbac/error.h> #include <rsbac/proc_fs.h> #include <rsbac/request_groups.h> #include "rbac.h" #define RBAC_ROLE_USER \ { \ .parent = 0, \ .name = "General User", \ .fd_rights = ((RSBAC_READ_WRITE_REQUEST_VECTOR | RSBAC_EXECUTE_REQUEST_VECTOR) & RSBAC_FD_REQUEST_VECTOR) \ } #define RBAC_ROLE_SECOFF \ { \ .parent = 0, \ .name = "Security Officer", \ .fd_rights = ((RSBAC_READ_WRITE_REQUEST_VECTOR | RSBAC_EXECUTE_REQUEST_VECTOR | RSBAC_SECURITY_REQUEST_VECTOR) & RSBAC_FD_REQUEST_VECTOR) \ } #define RBAC_ROLE_SYSADM \ { \ .parent = 0, \ .name = "System Admin", \ .fd_rights = ((RSBAC_READ_WRITE_REQUEST_VECTOR | RSBAC_EXECUTE_REQUEST_VECTOR | RSBAC_SYSTEM_REQUEST_VECTOR) & RSBAC_FD_REQUEST_VECTOR) \ } static u_long nr_request_calls = 0; static u_long nr_set_attr_calls = 0; static u_long nr_need_overwrite_calls = 0; static u_long nr_write_calls = 0; static u_long nr_system_calls = 0; MODULE_AUTHOR("Amon Ott"); MODULE_DESCRIPTION("RSBAC REG RBAC module"); MODULE_LICENSE("GPL"); static u_int listkey = 769764297; static long handle = 783497290; #define SYSLISTROLESHANDLE 98239829; static long sys_list_roles_handle = 0; #define SYSGETROLEHANDLE 78400429; static long sys_get_role_handle = 0; #define SYSSETROLEHANDLE 183487242; static long sys_set_role_handle = 0; #define SYSDELROLEHANDLE 198754082; static long sys_del_role_handle = 0; /* Filenames for persistent data in /rsbac.dat dir of ROOT_DEV (max 7 chars) */ #define ROLEFILENAME "rbac_r" #define USERFILENAME "rbac_u" #define PROC_NAME "rbac" /* Version number for on disk data structures */ #define ROLE_LIST_VERSION 1 #define USER_LIST_VERSION 1 static rsbac_list_handle_t role_list_handle = NULL; static rsbac_list_handle_t user_list_handle = NULL; /* Helper functions */ static void detach_lists (void) { if(role_list_handle && rsbac_list_detach(&role_list_handle, listkey)) rsbac_printk(KERN_WARNING "RSBAC REG RBAC module: Unregistering list failed - beware!\n"); if(user_list_handle && rsbac_list_lol_detach(&user_list_handle, listkey)) rsbac_printk(KERN_WARNING "RSBAC REG RBAC module: Unregistering user list failed - beware!\n"); } static void unregister_syscalls (void) { if (sys_list_roles_handle > 0) rsbac_reg_unregister_syscall (sys_list_roles_handle); if (sys_get_role_handle > 0) rsbac_reg_unregister_syscall (sys_get_role_handle); if (sys_set_role_handle > 0) rsbac_reg_unregister_syscall (sys_set_role_handle); if (sys_del_role_handle > 0) rsbac_reg_unregister_syscall (sys_del_role_handle); } static void cleanup (void) { #if defined(CONFIG_RSBAC_PROC) remove_proc_entry(PROC_NAME, proc_rsbac_root_p); #endif rsbac_reg_unregister(handle); unregister_syscalls(); detach_lists(); } /* PROC functions */ #if defined(CONFIG_RSBAC_PROC) static struct proc_dir_entry * rbac_proc_p; static int rbac_proc_show(struct seq_file *m, void *v) { union rsbac_target_id_t rsbac_target_id; union rsbac_attribute_value_t rsbac_attribute_value; if (!rsbac_is_initialized()) return -ENOSYS; rsbac_target_id.scd = ST_rsbac; rsbac_attribute_value.dummy = 0; if (!rsbac_adf_request(R_GET_STATUS_DATA, task_pid(current), T_SCD, rsbac_target_id, A_none, rsbac_attribute_value)) { return -EPERM; } seq_puts(m, "RSBAC REG RBAC module\n---------------------\n"); seq_printf(m, "%lu calls to request function.\n", nr_request_calls); seq_printf(m, "%lu calls to set_attr function.\n", nr_set_attr_calls); seq_printf(m, "%lu calls to need_overwrite function.\n", nr_need_overwrite_calls); seq_printf(m, "%lu calls to write function.\n", nr_write_calls); seq_printf(m, "%lu RBAC system calls\n", nr_system_calls); seq_printf(m, "%li roles defined.\n", rsbac_list_count(role_list_handle)); seq_printf(m, "%li users have %li roles defined.\n", rsbac_list_lol_count(user_list_handle), rsbac_list_lol_all_subcount(user_list_handle)); return 0; } static ssize_t rbac_proc_open(struct inode *inode, struct file *file) { return single_open(file, rbac_proc_show, NULL); } static const struct file_operations rbac_proc_fops = { .owner = THIS_MODULE, .open = rbac_proc_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; #endif /* CONFIG_RSBAC_PROC */ /**** Decision Functions ****/ static rsbac_boolean_t check_fd_right(__u32 role, rsbac_request_vector_t request_vector, u_int inherit) { struct rbac_role_entry_t entry; if (rsbac_list_get_data(role_list_handle, &role, &entry)) return FALSE; if (entry.fd_rights & request_vector) return TRUE; if ( (entry.parent > 0) && (inherit > 0) ) return check_fd_right(entry.parent, request_vector, inherit - 1); return FALSE; } static int request_func ( enum rsbac_adf_request_t request, rsbac_pid_t owner_pid, enum rsbac_target_t target, union rsbac_target_id_t tid, enum rsbac_attribute_t attr, union rsbac_attribute_value_t attr_val, rsbac_uid_t owner) { __u32 user; __u32 * role_array; enum rsbac_adf_req_ret_t result = DO_NOT_CARE; int count; nr_request_calls++; switch (target) { case T_FILE: result = NOT_GRANTED; user = RSBAC_UID_NUM(owner); count = rsbac_list_lol_get_all_subdesc(user_list_handle, &user, (void **) &role_array); if (count > 0) { u_int i; for (i=0; i<count; i++) { if (check_fd_right(role_array[i], RSBAC_REQUEST_VECTOR(request), MAXINHERIT)) { result = GRANTED; break; } } rsbac_vfree(role_array); } break; default: break; } return result; } static int set_attr_func ( enum rsbac_adf_request_t request, rsbac_pid_t owner_pid, enum rsbac_target_t target, union rsbac_target_id_t tid, enum rsbac_target_t new_target, union rsbac_target_id_t new_tid, enum rsbac_attribute_t attr, union rsbac_attribute_value_t attr_val, rsbac_uid_t owner) { nr_set_attr_calls++; return 0; } static rsbac_boolean_t need_overwrite_func (struct dentry * dentry_p) { nr_need_overwrite_calls++; return FALSE; } static int write_func(rsbac_boolean_t need_lock) { nr_write_calls++; return 0; } static int sys_list_roles (void * arg) { __u32 * role_array; struct rbac_sys_list_roles_arg k_arg; int err; int tmperr; nr_system_calls++; if (!arg) return -RSBAC_EINVALIDPOINTER; err = rsbac_get_user((u_char *) &k_arg, (u_char *) arg, sizeof(k_arg) ); if (err) return err; if (!k_arg.maxnum) return rsbac_list_count(role_list_handle); err = rsbac_list_get_all_desc(role_list_handle, (void **) &role_array); if (err <= 0) return err; err = rsbac_min(err, k_arg.maxnum); tmperr = rsbac_put_user((u_char *) role_array, (u_char *) k_arg.role_array, sizeof(*role_array) * err); if (tmperr) err = tmperr; rsbac_vfree (role_array); return err; } static int sys_get_role (void * arg) { struct rbac_sys_get_role_arg k_arg; struct rbac_role_entry_t k_entry; int err; nr_system_calls++; if (!arg) return -RSBAC_EINVALIDPOINTER; err = rsbac_get_user((u_char *) &k_arg, (u_char *) arg, sizeof(k_arg) ); if (err) return err; err = rsbac_list_get_data(role_list_handle, &k_arg.role, &k_entry); if (!err) err = rsbac_put_user((u_char *) &k_entry, (u_char *) k_arg.entry_p, sizeof(k_entry)); return err; } static int sys_set_role (void * arg) { struct rbac_sys_set_role_arg k_arg; int err; nr_system_calls++; if (!arg) return -RSBAC_EINVALIDPOINTER; err = rsbac_get_user((u_char *) &k_arg, (u_char *) arg, sizeof(k_arg) ); if (err) return err; err = rsbac_list_add(role_list_handle, &k_arg.role, &k_arg.entry); return err; } static int sys_del_role (void * arg) { int err; __u32 k_role; nr_system_calls++; if (!arg) return -RSBAC_EINVALIDPOINTER; err = rsbac_get_user((u_char *) &k_role, (u_char *) arg, sizeof(k_role) ); if (err) return err; err = rsbac_list_remove(role_list_handle, &k_role); if(err) return err; err = rsbac_list_lol_subremove_from_all(user_list_handle, &k_role); return err; } /**** Init ****/ int init_module(void) { struct rsbac_reg_entry_t entry; struct rsbac_reg_syscall_entry_t syscall_entry; struct rsbac_list_info_t list_info; struct rsbac_list_lol_info_t list_lol_info; rsbac_printk(KERN_INFO "RSBAC REG RBAC module: Initializing.\n"); /* clearing registration entries */ memset(&entry, 0, sizeof(entry)); memset(&syscall_entry, 0, sizeof(syscall_entry)); /* Register role list */ list_info.version = ROLE_LIST_VERSION; list_info.key = listkey; list_info.desc_size = sizeof(__u32); list_info.data_size = sizeof(struct rbac_role_entry_t); list_info.max_age = 0; if(rsbac_list_register_hashed(RSBAC_LIST_VERSION, &role_list_handle, &list_info, RSBAC_LIST_BACKUP | RSBAC_LIST_AUTO_HASH_RESIZE, NULL, NULL, NULL, ROLEFILENAME, 0, 1, rsbac_list_hash_u32, NULL)) { rsbac_printk(KERN_WARNING "RSBAC REG RBAC module: Registering role list failed. Unloading.\n"); return -ENOEXEC; } /* Create default roles, if list is empty */ if (!rsbac_list_count(role_list_handle)) { __u32 id; struct rbac_role_entry_t user_entry = RBAC_ROLE_USER; struct rbac_role_entry_t secoff_entry = RBAC_ROLE_SECOFF; struct rbac_role_entry_t sysadm_entry = RBAC_ROLE_SYSADM; rsbac_printk(KERN_INFO "RSBAC REG RBAC module: role list is empty, generating default roles\n"); id = 0; if (rsbac_list_add(role_list_handle, &id, &user_entry)) { rsbac_printk(KERN_WARNING "RSBAC REG RBAC module: adding role 0 failed\n"); } id = 1; if (rsbac_list_add(role_list_handle, &id, &secoff_entry)) { rsbac_printk(KERN_WARNING "RSBAC REG RBAC module: adding role 1 failed\n"); } id = 2; if (rsbac_list_add(role_list_handle, &id, &sysadm_entry)) { rsbac_printk(KERN_WARNING "RSBAC REG RBAC module: adding role 2 failed\n"); } } /* Register user list of lists */ list_lol_info.version = ROLE_LIST_VERSION; list_lol_info.key = listkey; list_lol_info.desc_size = sizeof(__u32); /* uid */ list_lol_info.data_size = 0; list_lol_info.subdesc_size = sizeof(__u32); /* role */ list_lol_info.subdata_size = 0; list_lol_info.max_age = 0; if(rsbac_list_lol_register_hashed(RSBAC_LIST_VERSION, &user_list_handle, &list_lol_info, RSBAC_LIST_BACKUP | RSBAC_LIST_AUTO_HASH_RESIZE | RSBAC_LIST_DEF_DATA, NULL, NULL, /* compare / subcompare */ NULL, NULL, /* get_conv / get_subconv */ NULL, NULL, /* def_data / def_subdata */ USERFILENAME, 0, 1, rsbac_list_hash_u32, NULL)) { rsbac_printk(KERN_WARNING "RSBAC REG RBAC module: Registering user list failed. Unloading.\n"); cleanup(); return -ENOEXEC; } if (!rsbac_list_lol_count(user_list_handle)) { __u32 user; __u32 role; rsbac_printk(KERN_INFO "RSBAC REG RBAC module: user list is empty, generating default users\n"); user = 0; role = 2; if (rsbac_list_lol_subadd(user_list_handle, &user, &role, NULL)) rsbac_printk(KERN_WARNING "RSBAC REG RBAC module: adding user 0 with role 2 failed\n"); user = 400; role = 1; if (rsbac_list_lol_subadd(user_list_handle, &user, &role, NULL)) rsbac_printk(KERN_WARNING "RSBAC REG RBAC module: adding user 400 with role 1 failed\n"); } rsbac_printk(KERN_INFO "RSBAC REG RBAC module: List Version: %u, Name: %s, Handle: %p, Key: %u\n", RSBAC_LIST_VERSION, ROLEFILENAME, role_list_handle, listkey); /* Register to ADF */ strcpy(entry.name, "RSBAC REG RBAC module"); rsbac_printk(KERN_INFO "RSBAC REG RBAC module: REG Version: %u, Name: %s, Handle: %li\n", RSBAC_REG_VERSION, entry.name, handle); entry.handle = handle; entry.request_func = request_func; entry.set_attr_func = set_attr_func; entry.need_overwrite_func = need_overwrite_func; entry.write_func = write_func; entry.switch_on = TRUE; rsbac_printk(KERN_INFO "RSBAC REG RBAC module: Registering to ADF.\n"); if(rsbac_reg_register(RSBAC_REG_VERSION, entry) < 0) { rsbac_printk(KERN_WARNING "RSBAC REG RBAC module: Registering failed. Unloading.\n"); cleanup(); return -ENOEXEC; } // rsbac_printk(KERN_INFO "RSBAC REG RBAC module: REG Version: %u, Name: %s, Dispatcher Handle: %li\n", // RSBAC_REG_VERSION, syscall_entry.name, RBAC_sys_list_roles); rsbac_printk(KERN_INFO "RSBAC REG RBAC module: Registering syscall.\n"); strcpy(syscall_entry.name, RBAC_sys_list_roles_name); syscall_entry.registration_handle = SYSLISTROLESHANDLE; syscall_entry.dispatcher_handle = RBAC_sys_list_roles; syscall_entry.syscall_func = sys_list_roles; sys_list_roles_handle = rsbac_reg_register_syscall(RSBAC_REG_VERSION, syscall_entry); if(sys_list_roles_handle < 0) { rsbac_printk(KERN_WARNING "RSBAC REG RBAC module: Registering syscall list_roles failed. Unloading.\n"); cleanup(); return -ENOEXEC; } strcpy(syscall_entry.name, RBAC_sys_get_role_name); syscall_entry.registration_handle = SYSGETROLEHANDLE; syscall_entry.dispatcher_handle = RBAC_sys_get_role; syscall_entry.syscall_func = sys_get_role; sys_get_role_handle = rsbac_reg_register_syscall(RSBAC_REG_VERSION, syscall_entry); if(sys_get_role_handle < 0) { rsbac_printk(KERN_WARNING "RSBAC REG RBAC module: Registering syscall get_role failed. Unloading.\n"); cleanup(); return -ENOEXEC; } strcpy(syscall_entry.name, RBAC_sys_set_role_name); syscall_entry.registration_handle = SYSSETROLEHANDLE; syscall_entry.dispatcher_handle = RBAC_sys_set_role; syscall_entry.syscall_func = sys_set_role; sys_set_role_handle = rsbac_reg_register_syscall(RSBAC_REG_VERSION, syscall_entry); if(sys_set_role_handle < 0) { rsbac_printk(KERN_WARNING "RSBAC REG RBAC module: Registering syscall set_role failed. Unloading.\n"); cleanup(); return -ENOEXEC; } strcpy(syscall_entry.name, RBAC_sys_del_role_name); syscall_entry.registration_handle = SYSDELROLEHANDLE; syscall_entry.dispatcher_handle = RBAC_sys_del_role; syscall_entry.syscall_func = sys_del_role; sys_del_role_handle = rsbac_reg_register_syscall(RSBAC_REG_VERSION, syscall_entry); if(sys_del_role_handle < 0) { rsbac_printk(KERN_WARNING "RSBAC REG RBAC module: Registering syscall del_role failed. Unloading.\n"); cleanup(); return -ENOEXEC; } #if defined(CONFIG_RSBAC_PROC) rbac_proc_p = proc_create(PROC_NAME, S_IFREG | S_IRUGO, proc_rsbac_root_p, &rbac_proc_fops); if(!rbac_proc_p) { rsbac_printk(KERN_WARNING "RSBAC REG RBAC module: Not loaded due to failed proc entry registering.\n"); cleanup(); return -ENOEXEC; } #endif rsbac_printk(KERN_INFO "RSBAC REG RBAC module: Loaded.\n"); return 0; } void cleanup_module(void) { rsbac_printk(KERN_INFO "RSBAC REG RBAC module: Unregistering.\n"); cleanup(); rsbac_printk(KERN_INFO "RSBAC REG RBAC module: Unloaded.\n"); }