Re: [blare] Test Blare Policy |
[ Thread Index | Date Index | More blare-ids.org/blare Archives ]
On 03/08/2013 03:40 PM, Plane Benjamin
wrote:
Thank you for coming over.Hi everyone, Thank you again for your help last wednesday ;) This is the bug I am working on.I am now playing around with Blare and I am trying really simple cases. However I met problems in several situations. Here is the description of attributes of each file: getinfo file1 : 4 getpolicy file2 : {1 2} I now run the following command : cat file1 >> file2 which is an illegal information flow regard to the policy I chose. Result : the machine is completely frozen, a hard reboot is mandatory. Can you add it to the bugtracker here ? For testing purpose, you can apply the attached patch, but note that there are memory leaks so don't use it in a production environment. I can't read your attachment. Can you send it in plaintext or something?However I tried several times this example and it works twice. I managed to see the Blare flag in dmesg. By the way you can find in attachment the stacktrace of the system I copied right after the succesfull example. -- Guillaume |
diff --git a/Documentation/security/blare.txt b/Documentation/security/blare.txt new file mode 100644 index 0000000..c3d512a --- /dev/null +++ b/Documentation/security/blare.txt @@ -0,0 +1,23 @@ +--- What is Blare ? --- + +Blare is an IDS for the Linux kernel. It implements a policy centered on users, +programs, containers or any combinations of those. It uses *tags* to supervise +information flows within the operating systems. Tags can be attached to data +and programs in userspace : + - information tags (itags) + - policy tags (ptags) + - execute policy tags (xptags) + +Tasks running programs that do not have any policy defined in their xptags are +not supervised. Therefore, no alerts will be raised if they access illegal +information. However, information will continue to be spread through their +*information tags*, and alerts will still be raised if they write to containers +that are supervised (i.e., that have a policy tag defined). + +Blare is implemented as a LSM module and therefore does not avoid the +enforcement of standard DAC security. + +--- How to enable/disable --- + +set CONFIG_SECURITY_BLARE=y + diff --git a/include/linux/lsm_audit.h b/include/linux/lsm_audit.h index 88e78de..0adc456 100644 --- a/include/linux/lsm_audit.h +++ b/include/linux/lsm_audit.h @@ -82,6 +82,14 @@ struct common_audit_data { int result; } smack_audit_data; #endif +#ifdef CONFIG_SECURITY_BLARE + struct blare_audit_data { + char *subject; + char *object; + char *mode; + int legal; + } blare_audit_data; +#endif #ifdef CONFIG_SECURITY_SELINUX /* SELinux data */ struct { diff --git a/include/linux/security.h b/include/linux/security.h index e8c619d..3af1292 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -1189,6 +1189,10 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) * @shmflg contains the operational flags. * Return 0 if permission is granted. * + * @shm_shmdt: + * Cleanup after a process detaches this memory segment + * @shp: contains the detached shared memory structure + * * Security hooks for System V Semaphores * * @sem_alloc_security: @@ -1553,6 +1557,8 @@ struct security_operations { int (*shm_shmctl) (struct shmid_kernel *shp, int cmd); int (*shm_shmat) (struct shmid_kernel *shp, char __user *shmaddr, int shmflg); + void (*shm_shmdt) (struct shmid_kernel *shp); + int (*pipe_create) (struct file *fildes, int flags); int (*sem_alloc_security) (struct sem_array *sma); void (*sem_free_security) (struct sem_array *sma); @@ -1599,6 +1605,7 @@ struct security_operations { int (*socket_setsockopt) (struct socket *sock, int level, int optname); int (*socket_shutdown) (struct socket *sock, int how); int (*socket_sock_rcv_skb) (struct sock *sk, struct sk_buff *skb); + void (*socket_sock_release) (struct sock *sk); int (*socket_getpeersec_stream) (struct socket *sock, char __user *optval, int __user *optlen, unsigned len); int (*socket_getpeersec_dgram) (struct socket *sock, struct sk_buff *skb, u32 *secid); int (*sk_alloc_security) (struct sock *sk, int family, gfp_t priority); @@ -1806,6 +1813,8 @@ void security_shm_free(struct shmid_kernel *shp); int security_shm_associate(struct shmid_kernel *shp, int shmflg); int security_shm_shmctl(struct shmid_kernel *shp, int cmd); int security_shm_shmat(struct shmid_kernel *shp, char __user *shmaddr, int shmflg); +void security_shm_shmdt(struct shmid_kernel *shp); +int security_pipe_create(struct file *fildes, int flags); int security_sem_alloc(struct sem_array *sma); void security_sem_free(struct sem_array *sma); int security_sem_associate(struct sem_array *sma, int semflg); @@ -1820,7 +1829,6 @@ int security_netlink_recv(struct sk_buff *skb, int cap); int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen); int security_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid); void security_release_secctx(char *secdata, u32 seclen); - int security_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen); int security_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen); int security_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen); @@ -1832,6 +1840,7 @@ static inline void security_init_mnt_opts(struct security_mnt_opts *opts) { } + static inline void security_free_mnt_opts(struct security_mnt_opts *opts) { } @@ -2473,6 +2482,9 @@ static inline int security_shm_shmat(struct shmid_kernel *shp, return 0; } +static inline void security_shm_shmdt(struct shmid_kernel *shp) +{ } + static inline int security_sem_alloc(struct sem_array *sma) { return 0; @@ -2571,6 +2583,7 @@ int security_socket_getsockopt(struct socket *sock, int level, int optname); int security_socket_setsockopt(struct socket *sock, int level, int optname); int security_socket_shutdown(struct socket *sock, int how); int security_sock_rcv_skb(struct sock *sk, struct sk_buff *skb); +void security_sock_release(struct sock *sk); int security_socket_getpeersec_stream(struct socket *sock, char __user *optval, int __user *optlen, unsigned len); int security_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *skb, u32 *secid); @@ -2691,6 +2704,10 @@ static inline int security_sock_rcv_skb(struct sock *sk, return 0; } +static inline void security_sock_release(struct sock *sk) +{ +} + static inline int security_socket_getpeersec_stream(struct socket *sock, char __user *optval, int __user *optlen, unsigned len) { diff --git a/ipc/shm.c b/ipc/shm.c index 02ecf2c..dd13bed 100644 --- a/ipc/shm.c +++ b/ipc/shm.c @@ -237,6 +237,7 @@ static void shm_close(struct vm_area_struct *vma) shp->shm_lprid = task_tgid_vnr(current); shp->shm_dtim = get_seconds(); shp->shm_nattch--; + security_shm_shmdt(shp); if (shm_may_destroy(ns, shp)) shm_destroy(ns, shp); else diff --git a/net/socket.c b/net/socket.c index 2877647..8a10822 100644 --- a/net/socket.c +++ b/net/socket.c @@ -511,6 +511,10 @@ const struct file_operations bad_sock_fops = { void sock_release(struct socket *sock) { + /* This hook is used byu Blare-IDS to free the security field of the socket + * (when not using sk_alloc_secuity/sk_free_security*/ + security_sock_release(sock->sk); + if (sock->ops) { struct module *owner = sock->ops->owner; diff --git a/security/Kconfig b/security/Kconfig index 51bd5a0..666beed 100644 --- a/security/Kconfig +++ b/security/Kconfig @@ -186,6 +186,7 @@ config LSM_MMAP_MIN_ADDR source security/selinux/Kconfig source security/smack/Kconfig source security/tomoyo/Kconfig +source security/blare/Kconfig source security/apparmor/Kconfig source security/integrity/Kconfig @@ -195,6 +196,7 @@ choice default DEFAULT_SECURITY_SELINUX if SECURITY_SELINUX default DEFAULT_SECURITY_SMACK if SECURITY_SMACK default DEFAULT_SECURITY_TOMOYO if SECURITY_TOMOYO + default DEFAULT_SECURITY_BLARE if SECURITY_BLARE default DEFAULT_SECURITY_APPARMOR if SECURITY_APPARMOR default DEFAULT_SECURITY_DAC @@ -211,6 +213,9 @@ choice config DEFAULT_SECURITY_TOMOYO bool "TOMOYO" if SECURITY_TOMOYO=y +config DEFAULT_SECURITY_BLARE + bool "BLARE" if SECURITY_BLARE=y + config DEFAULT_SECURITY_APPARMOR bool "AppArmor" if SECURITY_APPARMOR=y @@ -224,6 +229,7 @@ config DEFAULT_SECURITY default "selinux" if DEFAULT_SECURITY_SELINUX default "smack" if DEFAULT_SECURITY_SMACK default "tomoyo" if DEFAULT_SECURITY_TOMOYO + default "BLARE" if DEFAULT_SECURITY_BLARE default "apparmor" if DEFAULT_SECURITY_APPARMOR default "" if DEFAULT_SECURITY_DAC diff --git a/security/Makefile b/security/Makefile index a5e502f..a9adf8f 100644 --- a/security/Makefile +++ b/security/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_KEYS) += keys/ subdir-$(CONFIG_SECURITY_SELINUX) += selinux subdir-$(CONFIG_SECURITY_SMACK) += smack subdir-$(CONFIG_SECURITY_TOMOYO) += tomoyo +subdir-$(CONFIG_SECURITY_BLARE) += blare subdir-$(CONFIG_SECURITY_APPARMOR) += apparmor # always enable default capabilities @@ -20,6 +21,7 @@ obj-$(CONFIG_SECURITY_SELINUX) += selinux/built-in.o obj-$(CONFIG_SECURITY_SMACK) += smack/built-in.o obj-$(CONFIG_AUDIT) += lsm_audit.o obj-$(CONFIG_SECURITY_TOMOYO) += tomoyo/built-in.o +obj-$(CONFIG_SECURITY_BLARE) += blare/built-in.o obj-$(CONFIG_SECURITY_APPARMOR) += apparmor/built-in.o obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o diff --git a/security/blare/Kconfig b/security/blare/Kconfig new file mode 100644 index 0000000..3549ae7 --- /dev/null +++ b/security/blare/Kconfig @@ -0,0 +1,12 @@ +config SECURITY_BLARE + bool "BLARE" + depends on SECURITY + select AUDIT + select SECURITYFS + select SECURITY_NETWORK + select NETLABEL + default n + help + This selects the Blare Intrusion Detection System Kernel. + Blare is IDS configured by a MAC policy, and based on information flow control at the operating system level. + If you are unsure how to answer this question, answer N. diff --git a/security/blare/Makefile b/security/blare/Makefile new file mode 100644 index 0000000..b3e58ea --- /dev/null +++ b/security/blare/Makefile @@ -0,0 +1,5 @@ +# For compiling as part of the linux source tree +obj-$(CONFIG_SECURITY_BLARE) := blare_lsm.o +blare_lsm-objs += lsm_hooks.o xattr.o itag.o itag_rcu.o ptags.o tests.o legality.o blare_securityfs.o network.o shm.o file_ops.o blare.o netlabel.o netfilter_hooks.o audit.o trace.o mmap.o #configfs.o + + diff --git a/security/blare/audit.c b/security/blare/audit.c new file mode 100644 index 0000000..d1d9eff --- /dev/null +++ b/security/blare/audit.c @@ -0,0 +1,98 @@ +/* This file contains the auditing code related to Blare IDS. + * + * Author: + * Christophe Hauser <christophe.hauser@xxxxxxxxxx> + * + * Copyright (C) 2010 University of Rennes 1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2. + * + */ + + +#ifdef CONFIG_AUDIT +#include <linux/lsm_audit.h> +#include <linux/fs.h> +#include "blare.h" +#include "include/audit.h" + +/** Adapted from smack code + * blare_log_callback - Blare specific information + * will be called by generic audit code + * @ab : the audit_buffer + * @a : audit_data + * + */ +static void blare_log_callback(struct audit_buffer *ab, void *a) +{ + struct common_audit_data *ad = a; + struct blare_audit_data *bad = &ad->blare_audit_data; + audit_log_format(ab, "lsm=Blare action=%s", + bad->legal ? "illegal" : "legal"); + audit_log_format(ab, " subject="); + audit_log_untrustedstring(ab, bad->subject); + audit_log_format(ab, " object="); + audit_log_untrustedstring(ab, bad->object); + audit_log_format(ab, " mode=%s", bad->mode); +} + + +// From Smack code +inline void str_from_perm(char *string, int access) +{ + int i = 0; + if (access & MAY_READ) + string[i++] = 'r'; + if (access & MAY_WRITE) + string[i++] = 'w'; + if (access & MAY_EXEC) + string[i++] = 'x'; + if (access & MAY_APPEND) + string[i++] = 'a'; + string[i] = '\0'; +} + + /* blare_log - Audit illegal or legal accesses depending on + * blare_audit_policy, adapted from Smack */ +void blare_log(char *subject_label, char *object_label, int mode, + int legal, struct blare_audit_info *bai) +{ + char mode_buf[NB_ACCESS_MODES + 1]; + struct blare_audit_data *bad; + //struct common_audit_data *a = &bai->a; + struct common_audit_data *a; + + a = kzalloc(sizeof(struct common_audit_data), GFP_KERNEL); + + /* We do not log legal information flows by default */ + if (legal == 1 && (BLARE_AUDIT_LEGAL == 0)) + return; + + bad = &a->blare_audit_data; + + /* Smack has a function to convert an access mode into a string */ + str_from_perm(mode_buf, mode); + + a->type = LSM_AUDIT_DATA_TASK; + a->u.tsk = current; + + //bad->subject = subject_label; + bad->subject = "subject"; + //bad->object = object_label; + bad->object = "object"; + bad->mode = mode_buf; + bad->legal = legal; + a->lsm_pre_audit = blare_log_callback; + + common_lsm_audit(a); + kfree(a); +} +#else /* #ifdef CONFIG_AUDIT */ +void blare_log(char *subject_label, char *object_label, int mode, + int legal) +{ +} +#endif + diff --git a/security/blare/blare.c b/security/blare/blare.c new file mode 100644 index 0000000..a2fb4cf --- /dev/null +++ b/security/blare/blare.c @@ -0,0 +1,409 @@ +/* General functions of Blare IDS. + * Author: + * Christophe Hauser <christophe.hauser@xxxxxxxxxx> + * + * Copyright (C) 2010 University of Rennes 1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2. + */ + +#include <linux/security.h> +#include "blare.h" +#include "include/itag.h" +#include "include/ptags.h" +#include "include/shm.h" +#include "include/legality.h" +#include <linux/list.h> +#include <linux/kmod.h> +#include "include/xattr.h" + + +/* Warnings, always printed */ +void blare_warn(const char* format,...){ + va_list args; + va_start(args,format); + printk(KERN_WARNING "blare_warning _%s(pid %d;cpu %d)_ ", current->comm, current->pid, current->on_cpu); + vprintk(format,args); + va_end(args); +} + +/* Print messages only if Blare was compiled with DEBUG enabled */ +void blare_debug(const char* format,...){ + va_list args; + if (!DEBUG) + return; + va_start(args,format); + printk(KERN_DEBUG "blare_debug: _%s(pid %d;cpu %d)_ ",current->comm, current->pid, current->on_cpu); + vprintk(format,args); + va_end(args); +} + + +/* Before transfering tags, there are some points to check */ +int current_process_check(void){ + const struct cred *cred; + struct blare_task_struct *tstruct; + int err = 0; + cred = get_current_cred(); + if (unlikely(!cred)){ + err = -ENODATA; + goto error; + } + + if (blare_enabled == 0){ + err = -1; + goto error; + } + /* PID 0 is the swapper*/ + if (current->pid == 0){ + err = -2; + goto error; + } + + tstruct = cred->security; + if (unlikely(!tstruct)){ + blare_error(-ECANCELED); // if pid!=0 and !tstruct we're in trouble + err = -ECANCELED; + goto error; + } + + if (tstruct->info_list){ + /* Trusted processes do not propagate information nor xptags */ + if ((blare_check_trusted_process(tstruct->info_list)) == 0){ + err = -3; + goto error; + } + } + + // If we reach this point, it is ok to transfer the tags + err = 0; +error: + put_cred(cred); + return err; +} + +/* Propagation of information tags from an object to a task*/ +int blare_task_read_info(struct list_head *info){ + int rc; + struct list_head *newlist; + //struct blare_task_struct *tstruct = current_security(); + struct blare_task_struct *tstruct; + struct cred *cred; + + /* Nothing to do */ + if (!info || list_empty(info)) + return 0; + + /* Check wether the current process should transfer tags */ + rc = current_process_check(); + if (rc != 0) + return rc; + + cred = prepare_creds(); + tstruct = cred->security; + + if (tstruct->info_list){ + rc = itag_merge(tstruct->info_list,info, GFP_KERNEL); + if (rc< 0) + BLARE_DEBUG("[task_read] Error -1 merging tags"); + if (rc !=0) + tstruct->info_rev++; + } + else{ + rcu_read_lock(); + newlist = itag_copy(info, GFP_ATOMIC); + rcu_read_unlock(); + if (newlist && !IS_ERR(newlist)){ + tstruct->info_list = newlist; + } + else + BLARE_DEBUG("Error %d trying to copy itag list", (int)PTR_ERR(newlist)); + } + + commit_creds(cred); + return 0; +} + +/* Propagation of xptags from an object to a task */ +int blare_task_read_xpolicy(struct list_head *xpolicy){ + int rc; + const struct cred *cred; + struct blare_task_struct *tstruct; + + /* Nothing to do */ + if (!xpolicy) + return 0; + + /* Check wether the current process should transfer tags */ + rc = current_process_check(); + if (rc != 0) + return rc; + + cred = get_current_cred(); + tstruct = cred->security; + + if(tstruct->xpolicy_list) + tstruct->xpolicy_list = common_sets(tstruct->xpolicy_list,xpolicy); + else + tstruct->xpolicy_list = ptag_copy(xpolicy); + + put_cred(cred); + + return 0; +} + +/* Deprecated stub, don't use + */ +int blare_task_read(struct blare_tags *objtags){ + int rc; + + if (!objtags) + return 0; + + rc = blare_task_read_info(objtags->info); + rc = blare_task_read_xpolicy(objtags->xpolicy); + + /* Update shm segments TODO + if (tstruct->shm && !(list_empty(tstruct->shm))) + blare_update_shm_tags(tstruct->shm,tstruct->info_list); + */ + + /* Policy check. Or we can also do it later, as this function won't return 0 + * if this is an illegal flow. + rc = check_policy(tstruct->info_list, tstruct->policy_list); + if (rc < 0) printk(KERN_CRIT "[BLARE_POLICY_VIOLATION] process with pid" + "%d running %s made an illegal READ access to resource.", + current->pid,current->comm); + */ + return 0; +} + +/* When a task writes into an object */ +int blare_task_write(struct blare_tags *objtags){ + struct blare_task_struct *tstruct; + struct list_head *copy = NULL; + int err = 0; + const struct cred *cred; + + if (blare_enabled == 0 || current->pid == 0) + return 0; + + if(unlikely(!objtags)) return -ENODATA; + + cred = get_current_cred(); + if (unlikely(!cred)){ + err = -ENODATA; + goto exit; + } + + tstruct = cred->security; + if (unlikely(!tstruct)){ + err = -ENODATA; + goto exit; + } + + /* Information tags propagation */ + if (tstruct->info_list && !list_empty(tstruct->info_list)){ + if (objtags->info){ + + /* Trusted processes do not propagate information */ + if ((err = blare_check_trusted_process(tstruct->info_list)) == 0){ + err = 0; + goto exit; + } + + /* Make sure we don't screw itags ordering - for debug purpose + err = itag_check_order(tstruct->info_list); + if (err < 0) BLARE_DEBUG("Wrong itag order before merge"); + */ + + err = itag_merge(objtags->info, tstruct->info_list, GFP_KERNEL); + if (err < 0) BLARE_DEBUG("[task_write] Error %d merging tags",err); + + } + else{ + /* BUG: causes exponential memory allocation ! + * Fixed, was caused by itag_merge screwing the ordering of itags + * */ + rcu_read_lock(); + copy = itag_copy(tstruct->info_list, GFP_ATOMIC); + rcu_read_unlock(); + if (!IS_ERR(copy)){ + objtags->info = copy; + } + else{ + err = PTR_ERR(copy); + BLARE_DEBUG("(blare_task_write) Error %d while copying itag",err); + } + } + } + + /* Execute policy tag propagation + */ + if (tstruct->xpolicy_list) + xptag_merge(&objtags->xpolicy, tstruct->xpolicy_list); + + err = 0; +exit: + put_cred (cred); + return err; +} + +int blare_task_write_info(struct list_head *info){ + struct blare_task_struct *tstruct; + struct list_head *copy = NULL; + int err = 0; + const struct cred *cred; + + if (blare_enabled == 0 || current->pid == 0) + return 0; + + //if(unlikely(!info)) return -ENODATA; + + cred = get_current_cred(); + if (unlikely(!cred)){ + err = -ENODATA; + goto exit; + } + + tstruct = cred->security; + if (unlikely(!tstruct)){ + err = -ENODATA; + goto exit; + } + + /* Information tags propagation */ + if (tstruct->info_list && !list_empty(tstruct->info_list)){ + if (info){ + + /* Trusted processes do not propagate information */ + if ((err = blare_check_trusted_process(tstruct->info_list)) == 0){ + err = 0; + goto exit; + } + + err = itag_merge(info, tstruct->info_list, GFP_KERNEL); + if (err < 0) BLARE_DEBUG("[task_write] Error %d merging tags",err); + + } + else{ + rcu_read_lock(); + copy = itag_copy(tstruct->info_list, GFP_ATOMIC); + rcu_read_unlock(); + if (!IS_ERR(copy)){ + info = copy; + } + else{ + err = PTR_ERR(copy); + BLARE_DEBUG("(blare_task_write) Error %d while copying itag",err); + } + } + } +exit: + put_cred(cred); + return 0; +} + + +/* Helper function to pring tags contents */ +void tags_print (struct blare_tags *tags){ + itag_count (tags->info); + policytag_print(tags->xpolicy); +} + +/* Helper function to allocate a blare_tags structure */ +struct blare_tags *blare_tags_alloc(gfp_t flags) +{ + return (struct blare_tags*) kmem_cache_zalloc(blare_tags_cache, flags); +} + +/* Helper function to free a tags structure and its + * allocated fields */ +void tags_free(struct blare_tags **tagsp){ + struct blare_tags *tags; + if (!tagsp || !*tagsp) return; + + tags = *tagsp; + + if (tags->info) + itag_free(tags->info); + if (tags->xpolicy) + policy_tag_free(tags->xpolicy); + + kmem_cache_free(blare_tags_cache,tags); + *tagsp = NULL; +} + + +/* Invoke a userspace program to report a message from blare */ +int blare_call(char *msg){ + char *argv[] = { "/usr/bin/blared", msg, NULL }; + static char *envp[] = { + "HOME=/", + "TERM=linux", + "PATH=/sbin:/bin:/usr/sbin:/usr/bin", NULL }; + + return call_usermodehelper( argv[0], argv, envp, UMH_WAIT_PROC ); +} + + +/* Check whether a process's itag is set to 0 + * which means that we do not propagate anything + * (i.e. trusted process)*/ +int blare_check_trusted_process(struct list_head *itag){ + struct information *info; + + /* Itag can't be null here */ + BUG_ON(!itag); + + rcu_read_lock(); + info = list_first_entry(itag, struct information, node); + rcu_read_unlock(); + if (info->value == 0){ + return 0; + } + else + return -1; +} + +/* Check whether the current process is trusted + * returns 0 if it is trusted, -1 otherwise + * */ +int blare_current_is_trusted(void){ + struct blare_task_struct *tstruct; + int rc; + const struct cred *ro_cred; + + ro_cred = get_current_cred(); + tstruct = ro_cred->security; + + if (tstruct && tstruct->info_list){ + //rcu_read_lock(); + rc = blare_check_trusted_process(tstruct->info_list); + //rcu_read_unlock(); + } + else + rc = -1; + + put_cred(ro_cred); + return rc; +} + +/* The current information tag - D*/ +char *blare_current_info_to_s(void){ + struct blare_task_struct *tstruct; + const struct cred *ro_cred; + char *str = NULL; + + ro_cred = get_current_cred(); + tstruct = ro_cred->security; + + if (tstruct && tstruct->info_list) + str = itag_to_s(tstruct->info_list); + put_cred(ro_cred); + return str; +} + + diff --git a/security/blare/blare.h b/security/blare/blare.h new file mode 100644 index 0000000..124d873 --- /dev/null +++ b/security/blare/blare.h @@ -0,0 +1,212 @@ +/* Check Documentation/blare.txt for documentation on Blare. + * + * Author: + * Christophe Hauser <christophe.hauser@xxxxxxxxxx> + * + * Copyright (C) 2010 University of Rennes 1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2. + */ + +#ifndef __BLARE_H +#define __BLARE_H + +#include <linux/security.h> +#include <net/netlabel.h> +#include <linux/list.h> + +/* Blare stores meta information in the extended attributes of the filesystem. + * Such meta-information are called tags. + * INFO_XATTR store information tags + * POLICY_XATTR store policy tags + * XPOLICY_XATTR store execute policy tags + */ + +#define INFO_XATTR "security.blare.info" + +#define POLICY_XATTR "security.blare.policy" +#define POLICY_COUNT "security.blare.policy_count" +#define POLICY_VERSION "security.blare.policy_version" + +#define XPOLICY_XATTR "security.blare.xpolicy" +#define XPOLICY_COUNT "security.blare.xpolicy_count" +#define XPOLICY_VERSION "security.blare.xpolicy_version" + +#define ROLE_XATTR "security.blare.role" +#define ROLE_COUNT "security.blare.role_count" + +// Tracing binaries +#define TRACE_XATTR "security.blare_trace" + +/* Set this to enable debug mode (printk) */ +#define DEBUG false + +/* Auditing, should we also log legal events ? */ +#define BLARE_AUDIT_LEGAL true + +/* Verbose (printk) POLICY and INFO operations (for debug) */ +#define DEBUG_POLICY false +#define DEBUG_XPOLICY false +#define DEBUG_INFO false +#define DEBUG_NETWORK false +#define DEBUG_SECURITYFS true +#define DEBUG_NET DEBUG_NETWORK +#define DEBUG_MMAP false + +#define BLARE_DEBUG(x...) { if (DEBUG) {printk(KERN_DEBUG "BLARE_DEBUG@%s:%d [%s(pid %d)]",__func__, __LINE__, current->comm,current->pid); printk(KERN_DEBUG x);}} + +#define BLARE_DEBUG_NET(x...) { if (DEBUG_NETWORK) {printk(KERN_DEBUG "BLARE_DEBUG_NET@%s:%d [%s(pid %d)]",__func__, __LINE__, current->comm,current->pid); printk(KERN_DEBUG x);}} + +#define BLARE_WARN_ON(condition,format...){ if (unlikely(condition)){ printk(KERN_WARNING "BLARE: WARNING, [%s]", current->comm) ; printk(KERN_WARNING format);}} + +/* Critical errors */ +#define blare_error(errno){printk(KERN_WARNING "blare: error %d in function %s, line %d _%s:%d_", errno, __func__, __LINE__, current->comm,current->pid);} + +extern int blare_enabled; + +extern struct kmem_cache *blare_info_cache; +extern struct kmem_cache *blare_policy_array_cache; +extern struct kmem_cache *blare_tree_item_cache; +extern struct kmem_cache *blare_policy_tree_cache; +extern struct kmem_cache *blare_cache_slab; +extern struct kmem_cache *blare_file_struct_cache; +extern struct kmem_cache *blare_task_struct_cache; +extern struct kmem_cache *blare_shmptr_cache; +extern struct kmem_cache *blare_tags_cache; +extern struct security_operations blare_ops; +extern struct rb_root blare_cache_root; +extern int blare_initialized __initdata; +extern struct list_head *blare_network_policy; +extern struct rb_root *blare_mmap_tree_root; + +/* Structure attached to processes + * in their credentials + * cred->security */ +struct blare_task_struct{ + struct list_head *info_list; + int info_rev; // revision number + struct list_head *policy_list; + struct list_head *xpolicy_list; + struct list_head *shm; + struct list_head *threads; + struct list_head *mmap_list; + int trace; //Should we trace (log the actions of) this process ? +}; + +/* blare_task_struct has a threads list of blare_thread items, each of them + * embedding the pid of the thread and a pointer to its information tag + */ +struct blare_thread{ + struct list_head *info_list; + struct list_head node; + int pid; +}; + +/* Set of tags to attach to any object */ +struct blare_tags{ + struct list_head *info; + /* Used by softirqs invoking rcv_skb*/ + spinlock_t info_lock; + atomic_t refcount; + int info_rev; //unused + struct list_head *policy; + struct list_head *xpolicy; +}; + + +/* Structure attached to files and binary handlers (binprm) + * in their credentials + * cred->security + * */ +struct blare_file_struct{ + int *info_array; + int info_size; + struct policy_array **policy_arrays; + int policy_count; + struct policy_array **xpolicy_arrays; + int xpolicy_count; + struct blare_tags tags; // used for unnamed pipes +}; + + +/* An information tag is a list of struct information elements + */ +struct information{ + // This is a chained list of integer values + // representing an ordered set + struct list_head node; + int value; +}; + + +/* We attach information tags to shared memory segments. Processes maintain a + * list of pointers (of type blare_shmptr) to currently attached shared memory + * segments. + */ +struct blare_shmptr{ + struct list_head node; + struct blare_tags *ptr; + int shmflg; //shmat() flags, i.e. SHM_RDONLY etc. +}; + + +/* This intermediate structure is used when reading policy tags from the + * filesystem XATTR*/ +struct policy_array{ + int *array; + int size; +}; + + +/* Each set of a policy tag is a binary tree composed of elements of type + * tree_item + */ +struct tree_item{ + struct rb_node node; + int value; +}; + +/* A policy tag is a list of binary trees (a set of sets) + * Each binary tree has the following type: + * */ +struct policy_tree{ + struct list_head list; + struct rb_root *root; + int cardinal; +}; + +/* Unused ?*/ +struct xpolicy{ + struct list_head policy_list; +}; + +/* +struct blare_cipso { + int blare_level; + char blare_catset[BLARE_TAG_LEN]; +}; +*/ + +/* From blare.c */ +int blare_task_read(struct blare_tags *objtags); +int blare_task_read_info(struct list_head *info); +int blare_task_read_xpolicy(struct list_head *xpolicy); +int blare_task_write(struct blare_tags *objtags); +void tags_print (struct blare_tags *tags); +struct blare_tags *tags_alloc(void); +void tags_free(struct blare_tags **tags); +struct blare_tags *blare_tags_alloc(gfp_t flags); +int blare_call(char *msg); +int blare_set_skbuff_netlabel(struct sk_buff *skb, struct list_head *itag); +//void blare_cipso_doi(void); +//void blare_netlabel_audit_set(struct netlbl_audit *nap); +int blare_check_trusted_process(struct list_head *itag); +int blare_current_is_trusted(void); +char *blare_current_info_to_s(void); +unsigned int inet_addr(char *str); +void blare_warn(const char* format,...); +void blare_debug(const char* format,...); +int blare_task_write_info(struct list_head *info); +#endif diff --git a/security/blare/blare_securityfs.c b/security/blare/blare_securityfs.c new file mode 100644 index 0000000..19973cd --- /dev/null +++ b/security/blare/blare_securityfs.c @@ -0,0 +1,258 @@ +/* securityfs is used in blare to do things like setting the network policy, + * setting per user policies etc. + * + * Author: + * Christophe Hauser <christophe.hauser@xxxxxxxxxx> + * + * Copyright (C) 2010 University of Rennes 1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2. + */ + +#include "blare.h" +#include <linux/uaccess.h> +#include "include/network.h" +#include "include/netlabel.h" +#include "include/ptags.h" + +static struct dentry *blare_fs_dir __initdata; +extern int blare_enabled; + +/* +struct blare_securityfs_data{ + int *array; + size_t size; +} +*/ + + +/** + * This is used by blare to read a set of the network policy from its "network" + * entry. The user writes to the virtual file, and we read it from kernelspace. + * @userbuf: user buffer to copy data from (NOT NULL) + * @alloc_size: size of user buffer (REQUIRES: @alloc_size >= @copy_size) + * @copy_size: size of data to copy from user buffer + * @pos: position write is at in the file (NOT NULL) + * + * Returns: kernel buffer containing copy of user buffer data or an + * ERR_PTR on failure. + */ +static char *blare_securityfs_buffer_write(const char __user *buffer, size_t + alloc_size, size_t copy_size, loff_t *pos){ + + char *data; + BUG_ON(copy_size > alloc_size); + + if (*pos != 0) + /* only writes from pos 0, that is complete writes */ + return ERR_PTR(-ESPIPE); + + /* TODO: check permissions */ + + data = kmalloc(alloc_size, GFP_KERNEL); + + if (data == NULL) + return ERR_PTR(-ENOMEM); + + if (copy_from_user(data, buffer, copy_size)) { + kfree(data); + return ERR_PTR(-EFAULT); + } + + // The caller should free it + return data; +} + +/** + * policy_load + * .write file hook function to load a new policy subset via securityfs + */ +static ssize_t policy_load(struct file *f, const char __user *buffer, size_t size, + loff_t *pos){ + ssize_t error; + int *array; + + array = (int*)blare_securityfs_buffer_write(buffer, size, size, pos); + + /* + printk("Got the following array: "); + array_print(array,size/sizeof(int)); + */ + + error = PTR_ERR(array); + if (!IS_ERR(array)) { + error = blare_network_policy_add(array,size); + printk(KERN_INFO "blare: added array @%p with size %zd. Error code was %zd\n",array,size,error); + kfree(array); + return size; + //policytag_print(blare_network_policy); + } + else + printk(KERN_INFO "Blare: error: %zd\n loading the network policy",error); + + return error; +} + +/* + * policy_export + * .read file hook function to export a policy list to userspace + */ +static ssize_t policy_export(struct file *f, char __user *buffer, size_t size, loff_t *pos){ + if (*pos != 0) + return -ESPIPE; + + if (!blare_network_policy){ + printk(KERN_INFO "(empty set)\n"); + return 0; + } + + policytag_print(blare_network_policy); + + /* + if (DEBUG_SECURITYFS) printk("[policy_export] buffersize: %zd\n",size); + if (DEBUG_SECURITYFS && !buffer) printk("[policy_export] buffer is NULL\n"); + */ + + return 0; +} + + +/* Enable / disable blare + * .write hook function */ +static ssize_t activate(struct file *f, const char __user *buffer, size_t size, + loff_t *pos){ + char *data, error; + + data = blare_securityfs_buffer_write(buffer, size, size, pos); + if (IS_ERR(data)){ + error = PTR_ERR(data); + printk(KERN_WARNING "blare_securityfs: error %d\n", error); + return error; + } + + if (strncmp(data, "1", 1) == 0){ + printk(KERN_INFO "*** BlARE ENABLED ***\n"); + blare_enabled = 1; + } + else if (strncmp(data, "0", 1) == 0){ + printk(KERN_INFO "*** BlARE DISABLED ***\n"); + blare_enabled = 0; + } + else + printk(KERN_INFO "Blare: what ? I don't understand what \"%c\" means\n",data[0]); + kfree(data); + return size; +} + + +/* Is blare enabled ? */ +static ssize_t is_enabled(struct file *f, char __user *buffer, size_t size, loff_t *pos){ + if (*pos != 0) + return -ESPIPE; + + if(blare_enabled != 0 && blare_enabled !=1) + printk(KERN_WARNING "blare: blare_enabled should be either 0 or 1, but" + "it is %d !\n", blare_enabled); + else + printk(KERN_INFO "%d\n",blare_enabled); + + return 0; +} + +/** + * File operations attached to /sys/kernel/security/blare/network + */ +static const struct file_operations blare_securityfs_policy_ops = { + .write = policy_load, + .llseek = default_llseek, + .read = policy_export +}; + +/* Blare activation / desactivation */ +static const struct file_operations blare_securityfs_active_ops = { + .write = activate, + .llseek = default_llseek, + .read = is_enabled +}; + +/* Called once for every securityfs entry */ +static int __init blare_securityfs_create(const char *name, int mask, + const struct file_operations *fops) +{ + struct dentry *dentry; + + dentry = securityfs_create_file(name, S_IFREG | mask, blare_fs_dir, + NULL, fops); + + return IS_ERR(dentry) ? PTR_ERR(dentry) : 0; +} + + +/** + * blare_free_securityfs + * + * releases dentries allocated by blare_init_securityfs + */ +void __init blare_free_securityfs(void) +{ + if (blare_fs_dir) { + //TODO: blare_securityfs_remove(".load"); + + securityfs_remove(blare_fs_dir); + blare_fs_dir = NULL; + } +} + +/** + * blare_create_blarefs - create the Blare security filesystem + * + * dentries created here are released by blare_destroy_blarefs + * + * Returns: error on failure + */ +int __init blare_init_securityfs(void) +{ + int rc; + + if (!blare_initialized) + return 0; + + + if (blare_fs_dir) { + if(DEBUG) printk("Blare securityfs already exists\n"); + return -EEXIST; + } + + blare_fs_dir = securityfs_create_dir("blare", NULL); + if (IS_ERR(blare_fs_dir)) { + rc = PTR_ERR(blare_fs_dir); + blare_fs_dir = NULL; + goto error; + } + + rc = blare_securityfs_create("network", 0640, &blare_securityfs_policy_ops); + if (rc) + goto error; + + rc = blare_securityfs_create("enabled", 0640, &blare_securityfs_active_ops); + if (rc) + goto error; + + printk(KERN_INFO "Blare: securityfs enabled :)\n"); + + /* Initialize CIPSO labels*/ + blare_cipso_doi(); + + //blare_enabled = 1; + + return 0; + +error: + blare_free_securityfs(); + if(DEBUG) printk(KERN_INFO "Error creating Blare securityfs entry :(\n"); + return rc; +} + +fs_initcall(blare_init_securityfs); diff --git a/security/blare/cache.c b/security/blare/cache.c new file mode 100644 index 0000000..c057838 --- /dev/null +++ b/security/blare/cache.c @@ -0,0 +1,54 @@ +/* The Blare cache stores information about files currently being opened + * by processes monitored by Blare. It is a binary tree indexed on the inode. + * Well.. that's what it would be if it was implemented :). + * + * TODO: There is a bottleneck somewhere at the filesystem level, which really + * slows down the system. It has to do with Blare updating the extended + * attributes at every file access. Some cache mechanism probably would be the + * answer. + * + * Author: + * Christophe Hauser <christophe.hauser@xxxxxxxxxx> + * + * Copyright (C) 2010 University of Rennes 1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2. + */ + + + +#include "blare.h" + +/* To be implemented */ +int blare_cache_add_entry(); +int blare_cache_del_entry(); + + +/* Search for an entry in the blare cache for inode @inode. + * Returns the corresponding blare_cache_entry if found + * Retunrs NULL otherwise + */ +struct blare_cache_entry *blare_get_cache_entry(struct inode *inode){ + struct rb_node *node; + struct blare_cache_entry *entry; + + node = blare_cache_root->rb_node; + + while (node) + { + entry = rb_entry(node, struct blare_cache_entry, node); + + if (entry->inode->i_ino > inode->i_ino) + node = node->rb_left; + + else if ( entry->inode->i_ino < inode->i_ino) + node = node->rb_right; + + else + return entry; + } + return NULL; +} + diff --git a/security/blare/configfs.c b/security/blare/configfs.c new file mode 100644 index 0000000..c44c33b --- /dev/null +++ b/security/blare/configfs.c @@ -0,0 +1,326 @@ +/* + * vim: noexpandtab ts=8 sts=0 sw=8: + * + * configfs_example_macros.c - This file is a demonstration module + * containing a number of configfs subsystems. It uses the helper + * macros defined by configfs.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 021110-1307, USA. + * + * Based on sysfs: + * sysfs is Copyright (C) 2001, 2002, 2003 Patrick Mochel + * + * configfs Copyright (C) 2005 Oracle. All rights reserved. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/configfs.h> +/* + * 02-simple-children + * + * This example merely has a simple one-attribute child. Note that + * there is no extra attribute structure, as the child's attribute is + * known from the get-go. Also, there is no container for the + * subsystem, as it has no attributes of its own. + */ + +struct simple_child { + struct config_item item; + int storeme; +}; + +inline struct simple_child *to_simple_child(struct config_item *item) +{ + return item ? container_of(item, struct simple_child, item) : NULL; +} + +struct configfs_attribute simple_child_attr_storeme = { + .ca_owner = THIS_MODULE, + .ca_name = "storeme", + .ca_mode = S_IRUGO | S_IWUSR, +}; + +struct configfs_attribute *simple_child_attrs[] = { + &simple_child_attr_storeme, + NULL, +}; + +ssize_t simple_child_attr_show(struct config_item *item, + struct configfs_attribute *attr, + char *page) +{ + ssize_t count; + struct simple_child *simple_child = to_simple_child(item); + + count = sprintf(page, "%d\n", simple_child->storeme); + + return count; +} + +ssize_t simple_child_attr_store(struct config_item *item, + struct configfs_attribute *attr, + const char *page, size_t count) +{ + struct simple_child *simple_child = to_simple_child(item); + unsigned long tmp; + char *p = (char *) page; + + tmp = simple_strtoul(p, &p, 10); + if (!p || (*p && (*p != '\n'))) + return -EINVAL; + + if (tmp > INT_MAX) + return -ERANGE; + + simple_child->storeme = tmp; + + return count; +} + +void simple_child_release(struct config_item *item) +{ + kfree(to_simple_child(item)); +} + +struct configfs_item_operations simple_child_item_ops = { + .release = simple_child_release, + .show_attribute = simple_child_attr_show, + .store_attribute = simple_child_attr_store, +}; + +struct config_item_type simple_child_type = { + .ct_item_ops = &simple_child_item_ops, + .ct_attrs = simple_child_attrs, + .ct_owner = THIS_MODULE, +}; + + +struct simple_children { + struct config_group group; +}; + +inline struct simple_children *to_simple_children(struct config_item *item) +{ + return item ? container_of(to_config_group(item), struct simple_children, group) : NULL; +} + +struct config_item *simple_children_make_item(struct config_group *group, const char *name) +{ + struct simple_child *simple_child; + + simple_child = kzalloc(sizeof(struct simple_child), GFP_KERNEL); + if (!simple_child) + return ERR_PTR(-ENOMEM); + + config_item_init_type_name(&simple_child->item, name, + &simple_child_type); + + simple_child->storeme = 0; + + return &simple_child->item; +} + +struct configfs_attribute simple_children_attr_description = { + .ca_owner = THIS_MODULE, + .ca_name = "description", + .ca_mode = S_IRUGO, +}; + +struct configfs_attribute *simple_children_attrs[] = { + &simple_children_attr_description, + NULL, +}; + +ssize_t simple_children_attr_show(struct config_item *item, + struct configfs_attribute *attr, + char *page) +{ + return sprintf(page, + "[02-simple-children]\n" + "\n" + "This subsystem allows the creation of child config_items. These\n" + "items have only one attribute that is readable and writeable.\n"); +} + +void simple_children_release(struct config_item *item) +{ + kfree(to_simple_children(item)); +} + +struct configfs_item_operations simple_children_item_ops = { + .release = simple_children_release, + .show_attribute = simple_children_attr_show, +}; + +/* + * Note that, since no extra work is required on ->drop_item(), + * no ->drop_item() is provided. + */ +struct configfs_group_operations simple_children_group_ops = { + .make_item = simple_children_make_item, +}; + +struct config_item_type simple_children_type = { + .ct_item_ops = &simple_children_item_ops, + .ct_group_ops = &simple_children_group_ops, + .ct_attrs = simple_children_attrs, + .ct_owner = THIS_MODULE, +}; + +struct configfs_subsystem simple_children_subsys = { + .su_group = { + .cg_item = { + .ci_namebuf = "02-simple-children", + .ci_type = &simple_children_type, + }, + }, +}; + + +/* ----------------------------------------------------------------- */ + +/* + * 03-group-children + * + * This example reuses the simple_children group from above. However, + * the simple_children group is not the subsystem itself, it is a + * child of the subsystem. Creation of a group in the subsystem creates + * a new simple_children group. That group can then have simple_child + * children of its own. + */ + +struct config_group *group_children_make_group(struct config_group *group, const char *name) +{ + struct simple_children *simple_children; + + simple_children = kzalloc(sizeof(struct simple_children), + GFP_KERNEL); + if (!simple_children) + return ERR_PTR(-ENOMEM); + + config_group_init_type_name(&simple_children->group, name, + &simple_children_type); + + return &simple_children->group; +} + +struct configfs_attribute group_children_attr_description = { + .ca_owner = THIS_MODULE, + .ca_name = "description", + .ca_mode = S_IRUGO, +}; + +struct configfs_attribute *group_children_attrs[] = { + &group_children_attr_description, + NULL, +}; + +ssize_t group_children_attr_show(struct config_item *item, + struct configfs_attribute *attr, + char *page) +{ + return sprintf(page, + "[03-group-children]\n" + "\n" + "This subsystem allows the creation of child config_groups. These\n" + "groups are like the subsystem simple-children.\n"); +} + +struct configfs_item_operations group_children_item_ops = { + .show_attribute = group_children_attr_show, +}; + +/* + * Note that, since no extra work is required on ->drop_item(), + * no ->drop_item() is provided. + */ +struct configfs_group_operations group_children_group_ops = { + .make_group = group_children_make_group, +}; + +struct config_item_type group_children_type = { + .ct_item_ops = &group_children_item_ops, + .ct_group_ops = &group_children_group_ops, + .ct_attrs = group_children_attrs, + .ct_owner = THIS_MODULE, +}; + +struct configfs_subsystem group_children_subsys = { + .su_group = { + .cg_item = { + .ci_namebuf = "03-group-children", + .ci_type = &group_children_type, + }, + }, +}; + +/* ----------------------------------------------------------------- */ + +/* + * We're now done with our subsystem definitions. + * For convenience in this module, here's a list of them all. It + * allows the init function to easily register them. Most modules + * will only have one subsystem, and will only call register_subsystem + * on it directly. + */ +struct configfs_subsystem *example_subsys[] = { + &simple_children_subsys, + &group_children_subsys, + NULL, +}; + +int configfs_example_init(void) +{ + int ret; + int i; + struct configfs_subsystem *subsys; + + for (i = 0; example_subsys[i]; i++) { + subsys = example_subsys[i]; + + config_group_init(&subsys->su_group); + mutex_init(&subsys->su_mutex); + ret = configfs_register_subsystem(subsys); + if (ret) { + printk(KERN_ERR "Error %d while registering subsystem %s\n", + ret, + subsys->su_group.cg_item.ci_namebuf); + goto out_unregister; + } + } + + return 0; + +out_unregister: + for (; i >= 0; i--) { + configfs_unregister_subsystem(example_subsys[i]); + } + + return ret; +} + +void configfs_example_exit(void) +{ + int i; + + for (i = 0; example_subsys[i]; i++) { + configfs_unregister_subsystem(example_subsys[i]); + } +} + + diff --git a/security/blare/file_ops.c b/security/blare/file_ops.c new file mode 100644 index 0000000..e0f8b45 --- /dev/null +++ b/security/blare/file_ops.c @@ -0,0 +1,371 @@ +/* Author: + * Christophe Hauser <christophe.hauser@xxxxxxxxxx> + * + * Copyright (C) 2010 University of Rennes 1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2. + */ + + +#include "blare.h" +#include "include/itag.h" +#include "include/ptags.h" +#include "include/xattr.h" +#include "include/legality.h" +#include "include/audit.h" +#include "include/file_ops.h" + +/* The current process may read a file */ +int blare_may_read(struct dentry *dp, struct blare_file_struct *fstruct){ + int rc, icount,xpcount; + struct cred *cred; + struct blare_task_struct *tstruct; + //struct blare_audit_info audit; + + /* Trusted processes do not propagate tags nor does the kernel swapper*/ + if (blare_current_is_trusted() == 0 || (blare_enabled == 0) || (current->pid == 0)) + return 0; + + cred = prepare_creds(); + if (unlikely(!cred || !cred->security)){ + abort_creds(cred); + return -ENODATA; + } + + if (fstruct->info_array) + blare_warn("Allocating a new info array but one already exists\n"); + + tstruct = cred->security; + + /* Information tag propagation + * blare_alloc_file_info is to be used along + * with blare_free_file_info*/ + icount = blare_alloc_file_info(dp,fstruct); + + /* + if (icount < 0) + blare_warn("error %d - couldn't read information tag from file %s", icount, dp->d_name.name); + */ + + if (icount > 0){ + /* Thread + if (current->pid != current->tgid){ + //BLARE_DEBUG("Subthread: current pid [%d] / current tgid" + //"[%d]\n",current->pid,current->tgid); + } + */ + + if (file_is_excluded(fstruct)){ + abort_creds(cred); + blare_free_file_info(fstruct); + printk(KERN_DEBUG"blare: may_read, file %s is excluded (0)\n", dp->d_name.name); + return 0; + } + + rc = itag_insert(fstruct->info_array,fstruct->info_size,&tstruct->info_list, + GFP_KERNEL, DISCARD_EXEC); + /* + if (rc > 0){ // Tag has changed + tstruct->info_rev++; + itag_print_msg(tstruct->info_list,"blare_file_read:"); + } + */ + + blare_free_file_info(fstruct); + + /* Note: here we want to continue with the other tags even if it fails + * except when ENOMEM*/ + if (rc == -ENOMEM){ + abort_creds(cred); + return rc; + } + + /* Now we're done with fstruct->info_array, we need to free it */ + } + + + /* Also update eventual shared memory segments + if (tstruct->shm && !(list_empty(tstruct->shm))) + blare_update_shm(tstruct->shm,tstruct->info_list); + */ + + /* Xptag propagation*/ + xpcount = blare_alloc_file_xpolicy(dp,fstruct); + if (xpcount > 0){ + rc = xptag_insert(fstruct->xpolicy_arrays, fstruct->xpolicy_count, + &tstruct->xpolicy_list); + blare_free_file_xpolicy(fstruct); + if (rc == -ENOMEM){ + blare_error(rc); + abort_creds(cred); + return rc; + } + } + + /*Commit the new creds for the current process if something has changed + */ + if (icount > 0 || xpcount > 0) + commit_creds(cred); + else{ + abort_creds(cred); + return 0; + } + + rc= check_policy(tstruct->info_list, tstruct->policy_list); + if (rc <0) + printk(KERN_CRIT "[BLARE_POLICY_VIOLATION] %s with pid %d " + "made an illegal READ access to file %s.\n" + ,current->comm,current->pid,dp->d_name.name); + + //blare_log(blare_current_info_to_s(), "unknown", MAY_READ, rc, &audit); + + /* Policy check*/ + return rc; +} + +int blare_may_append(struct dentry *dp, struct blare_file_struct *fstruct){ + const struct cred *ro_cred; + struct blare_task_struct *tstruct; + int rc, icount, size; + struct list_head *newtag; + + /* Trusted processes do not propagate tags */ + if (blare_current_is_trusted() == 0 || (blare_enabled == 0) || (current->pid == 0)) + return 0; + + ro_cred = get_current_cred(); + tstruct = ro_cred->security; + + if (unlikely(!tstruct)){ + put_cred(ro_cred); + return -ENODATA; + } + + /* Nothing to write or append */ + if (!tstruct->info_list || list_empty(tstruct->info_list)){ + goto xptag; + } + + + icount = blare_alloc_file_info(dp,fstruct); + if (file_is_excluded(fstruct)){ + put_cred(ro_cred); + blare_free_file_info(fstruct); + printk(KERN_DEBUG"blare: may_append, file %s is excluded (0)\n", dp->d_name.name); + return 0; + } + + /* Is there something to append ? */ + if (icount > 0){ + newtag = itag_copy(tstruct->info_list, GFP_KERNEL); + //itag_print_msg(newtag, "newtag:"); + rc = itag_insert(fstruct->info_array, fstruct->info_size, &newtag, + GFP_KERNEL, TAINT_EXEC); + if (rc < 0){ + blare_error(rc); + itag_free(newtag); + blare_free_file_info(fstruct); + goto xptag; + } + rc = blare_write_info(dp,newtag); + blare_free_file_info(fstruct); + } + else{ + blare_may_write(dp, fstruct); + blare_free_file_info(fstruct); + } + +xptag: + //blare_debug("TODO: xptag append"); + + put_cred(ro_cred); + return 0; +} + +/* The current process may write to the file */ +int blare_may_write(struct dentry *dp, struct blare_file_struct *fstruct){ + const struct cred *ro_cred; + struct blare_task_struct *tstruct; + struct list_head *file_policy; + int rc; + + /* Trusted processes do not propagate tags */ + if (blare_current_is_trusted() == 0 || (blare_enabled == 0) || (current->pid == 0)) + return 0; + + ro_cred = get_current_cred(); + tstruct = ro_cred->security; + //tstruct = current_security(); + + if (unlikely(!tstruct)){ + put_cred(ro_cred); + return -ENODATA; + } + + /* Should we propagate tags to this file ?*/ + rc = blare_alloc_file_info(dp,fstruct); + if (file_is_excluded(fstruct)){ + put_cred(ro_cred); + blare_free_file_info(fstruct); + blare_debug("may_write, file %s is excluded (0)\n", dp->d_name.name); + return 0; + } + else + blare_free_file_info(fstruct); + + /* Information tags propagation + * */ + rc = blare_write_info(dp,tstruct->info_list); + if (rc !=0) + blare_warn("failed to write information tag to file \"%s\", error %d", dp->d_name.name, rc); + + /* Propagate the execute policy */ + rc = blare_write_xpolicy(dp,tstruct->xpolicy_list); + if (rc < 0) + blare_warn("failed to write xpolicy tag to file %s, error %d", dp->d_name.name, rc); + + // Get the file's policy + rc = blare_alloc_file_cpolicy(dp,fstruct); + + /* If there is a policy for the current file AND an information tag for + * the current process*/ + if (rc > 0 && tstruct->info_list){ + file_policy = + arrays2policytag(fstruct->policy_arrays,fstruct->policy_count); + blare_free_file_cpolicy(fstruct); + + rc = check_policy(tstruct->info_list,file_policy); + + if (rc < 0) + printk(KERN_CRIT "[BLARE_POLICY_VIOLATION] process with pid %d" + "running %s wrote illegal information to file" + "%s.\n",current->pid,current->comm,dp->d_name.name); + + // Free the policy list & btrees... + policy_tag_free(file_policy); + } + + put_cred(ro_cred); + + return rc; +} + +/* The current process may read a file */ +int blare_may_exec(struct dentry *dp, struct blare_file_struct *fstruct){ + int rc, icount,xpcount; + struct cred *cred; + struct blare_task_struct *tstruct; + //struct blare_audit_info audit; + struct list_head *fxptag = NULL; + + /* Trusted processes do not propagate tags nor does the kernel swapper*/ + if (blare_current_is_trusted() == 0 || (blare_enabled == 0) || (current->pid == 0)) + return 0; + + cred = prepare_creds(); + if (unlikely(!cred || !cred->security)){ + abort_creds(cred); + return -ENODATA; + } + + tstruct = cred->security; + + /* Information tag propagation */ + icount = blare_alloc_file_info(dp,fstruct); + /* + if (icount < 0) + blare_warn("error %d - couldn't read information tag from file %s", icount, dp->d_name.name); + */ + + if (icount > 0){ + /* Thread + if (current->pid != current->tgid){ + //BLARE_DEBUG("Subthread: current pid [%d] / current tgid" + //"[%d]\n",current->pid,current->tgid); + } + */ + + if (file_is_excluded(fstruct)){ + abort_creds(cred); + blare_free_file_info(fstruct); + printk(KERN_DEBUG"blare: may_exec, file %s is excluded (0)\n", dp->d_name.name); + return 0; + } + + rc = itag_insert(fstruct->info_array,fstruct->info_size,&tstruct->info_list, + GFP_KERNEL, TAINT_EXEC); + if (rc > 0){ // Tag has changed + //tstruct->info_rev++; + itag_print_msg(tstruct->info_list,"blare_may_exec:"); + } + + blare_free_file_info(fstruct); + + /* Note: here we want to continue with the other tags even if it fails + * except when ENOMEM*/ + if (rc == -ENOMEM){ + abort_creds(cred); + return rc; + } + } + + xpcount = blare_alloc_file_xpolicy(dp,fstruct); + if (xpcount > 0){ + fxptag = arrays2policytag(fstruct->xpolicy_arrays, fstruct->xpolicy_count); + /* Intersect the policy tag of the current process with the xptag of the + * executed file */ + xptag_merge(&tstruct->policy_list, fxptag); + blare_free_file_xpolicy(fstruct); + } + + if (icount > 0 || xpcount > 0) + commit_creds(cred); + else + abort_creds(cred); + + if (fxptag) + policy_tag_free(fxptag); + + /* Check for policy violation */ + rc = check_policy(tstruct->info_list, tstruct->policy_list); + if (rc < 0) + printk(KERN_CRIT "[BLARE_POLICY_VIOLATION] process with pid %d" + "running %s wrote illegal information to file" + "%s.\n",current->pid,current->comm,dp->d_name.name); + + + return rc; +} + +/* blare_file_struct has pointers to inner structures. + * This function frees the inner structures, but doesn't free the + * blare_file_struct itself*/ +void blare_free_fstruct (struct blare_file_struct *fstruct){ + int i; + + if (fstruct->info_array) + kfree(fstruct->info_array); + + if (fstruct->policy_arrays){ + for(i=0;i<fstruct->policy_count;i++) + kmem_cache_free(blare_policy_array_cache,fstruct->policy_arrays[i]); + } + + if (fstruct->xpolicy_arrays){ + for(i=0;i<fstruct->xpolicy_count;i++) + kmem_cache_free(blare_policy_array_cache,fstruct->xpolicy_arrays[i]); + } + +} + +inline int file_is_excluded(struct blare_file_struct *fstruct){ + if (!fstruct || !fstruct->info_array || fstruct->info_size < 1) + return false; + + if (fstruct->info_array[0] == 0) + return true; + + return false; +} diff --git a/security/blare/include/audit.h b/security/blare/include/audit.h new file mode 100644 index 0000000..75cb072 --- /dev/null +++ b/security/blare/include/audit.h @@ -0,0 +1,16 @@ +#include <linux/lsm_audit.h> + +#define NB_ACCESS_MODES 5 // rwxat (inspired from Smack) + +struct blare_audit_info{ +#ifdef CONFIG_AUDIT + struct common_audit_data a; +#endif +}; + +void blare_log(char *subject_label, char *object_label, int mode, + int legal, struct blare_audit_info *bai); + +inline void str_from_perm(char *string, int access); + + diff --git a/security/blare/include/file_ops.h b/security/blare/include/file_ops.h new file mode 100644 index 0000000..fea119b --- /dev/null +++ b/security/blare/include/file_ops.h @@ -0,0 +1,14 @@ +#ifndef __BLARE_FOPS_H +#define __BLARE_FOPS_H + +int blare_may_read(struct dentry *dp, struct blare_file_struct *fstruct); +int blare_may_write(struct dentry *dp, struct blare_file_struct *fstruct); +int blare_may_exec(struct dentry *dp, struct blare_file_struct *fstruct); +void blare_free_fstruct (struct blare_file_struct *fstruct); + +/* Excluded files do not propagate taint. This is useful for logfiles and the + * like */ +inline int file_is_excluded(struct blare_file_struct *fstruct); +int blare_may_append(struct dentry *dp, struct blare_file_struct *fstruct); + +#endif diff --git a/security/blare/include/itag.h b/security/blare/include/itag.h new file mode 100644 index 0000000..9daf100 --- /dev/null +++ b/security/blare/include/itag.h @@ -0,0 +1,79 @@ +/* Author: + * Christophe Hauser <christophe.hauser@xxxxxxxxxx> + * + * Copyright (C) 2010 University of Rennes 1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2. + */ + + +#ifndef __BLARE_ITAG_H +#define __BLARE_ITAG_H + +/* When processes write or append to a file, they propagate their information + * tag, which is composed of negative and positive values, corresponding to + * elements in I or X (see ICC 2011 article). + * When processes read a file, they are only tainted by the positive values, + * i.e. negative values are discared, hence READ_MODE */ +#define DISCARD_EXEC 0x01 //Discard negative values in information tags +#define TAINT_EXEC 0x02 + +int itag2array(struct list_head *list, int **buffer); +struct list_head *array2itag(int *array, int size); +inline struct list_head *itag_exec(int *array, int size); +struct list_head *array2itag_generic(int *array, int size, bool exec); + +int itag_insert(int *array, int size, struct list_head **head, gfp_t flags, int exec_flag); +struct list_head *itag_copy(struct list_head *head, gfp_t gfp_flags); +struct list_head *itag_copy_rcu(struct list_head *head); + +void itag_print(struct list_head *head); +int itag_count(struct list_head *head); +void itag_free(struct list_head *head); +void array_print(int*array, int size); +int itag_merge(struct list_head* a, struct list_head *b, gfp_t flags); +int itag_merge_rcu(struct list_head* a, struct list_head *b); +int itag_alloc(struct list_head **security); +struct blare_thread *blare_current_threadinfo(struct list_head *tlist); +char *itag_to_s(struct list_head *head); + +/* Moved to netlabel.h */ +uint8_t *blare_itag2bitmap(struct list_head *itag); +//struct list_head *blare_bitmap2itag(uint8_t *bitmap); +//struct list_head *blare_catmap2itag(struct netlbl_lsm_secattr_catmap *catmap); +//int blare_itag2catmap(struct list_head *itag, struct netlbl_lsm_secattr_catmap *catmap); +// int itag2secattr(struct list_head *itag, struct netlbl_lsm_secattr *secattr); + +void itag_print_msg(struct list_head *head, const char* msg, ...); + +int itag_check_sanity(struct list_head *itag); + +/* Helper functions */ +static inline void itag_debug(struct list_head *itag, const char *msg){ + if(DEBUG_INFO && itag) + itag_print_msg(itag,msg); +} + +/* Returns a reference on the current itag. + * */ +static inline struct list_head* current_itag_copy(gfp_t flags){ + struct blare_task_struct *tstruct; + const struct cred *cred; + struct list_head *itag; + + cred = get_current_cred(); + if(unlikely(!cred || !cred->security)){ + put_cred(cred); + blare_error(-ENODATA); + return NULL; + } + + tstruct = cred->security; + itag = itag_copy(tstruct->info_list, flags); + put_cred(cred); + return itag; +} + +#endif diff --git a/security/blare/include/itag_rcu.h b/security/blare/include/itag_rcu.h new file mode 100644 index 0000000..3f72821 --- /dev/null +++ b/security/blare/include/itag_rcu.h @@ -0,0 +1,33 @@ +/* Author: + * Christophe Hauser <christophe.hauser@xxxxxxxxxx> + * + * Copyright (C) 2010 University of Rennes 1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2. + */ + + +#ifndef __BLARE_ITAG_RCU_H +#define __BLARE_ITAG_RCU_H + +int itag2array_rcu(struct list_head *list, int **buffer); +int itag_insert_rcu(int *array, int size, struct list_head **head, gfp_t flags); +int itag_count_rcu(struct list_head *head); +int itag_merge_rcu(struct list_head *a, struct list_head *b); +int itag_check_sanity_rcu(struct list_head *head); +void itag_free_rcu(struct list_head *head); + +int itag_basic_check_rcu(struct list_head *head); +int itag_check_order_rcu(struct list_head *head); +int itag_check_null_pointer_rcu(struct list_head *head); + +/* +static int itag_basic_check_rcu(struct list_head *head); +static int itag_check_order_rcu(struct list_head *head); +static int itag_check_null_pointer_rcu(struct list_head *head); +*/ + + +#endif diff --git a/security/blare/include/legality.h b/security/blare/include/legality.h new file mode 100644 index 0000000..6ff9684 --- /dev/null +++ b/security/blare/include/legality.h @@ -0,0 +1,19 @@ +/* Author: + * Christophe Hauser <christophe.hauser@xxxxxxxxxx> + * + * Copyright (C) 2010 University of Rennes 1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2. + */ + + +#ifndef __BLARE_LEG_H +#define __BLARE_LEG_H + +/* Functions from legality.c */ +int check_policy(struct list_head* info_list, struct list_head* policy_list); +int check_netpolicy(struct list_head* info_list); + +#endif diff --git a/security/blare/include/lib.h b/security/blare/include/lib.h new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/security/blare/include/lib.h @@ -0,0 +1 @@ + diff --git a/security/blare/include/mmap.h b/security/blare/include/mmap.h new file mode 100644 index 0000000..22fbc82 --- /dev/null +++ b/security/blare/include/mmap.h @@ -0,0 +1,36 @@ +/* Shared memory mappings (mmap) don't have any file descriptor when the MAP_ANONYMOUS flag is + * used. Therefore, we maintain a tree to store tags related to each mapping. + * + * Processes have a list of bound mmaps. A mmap is bound to a process when the + * process calls shmat, until it calls shmdt for a given memory segment. + * */ + +#include <linux/list.h> +#include "../blare.h" +#include <linux/rbtree.h> + +/* This structure is used in the global mmap tree, where mmaps are sorted by + * addresses + * @node: the red black tree node + * @addr: the address of the memory mapping + * @tags: blare tags associated with this mmap + * @refcount: how many processes are currently bound to this mmap + * */ +struct blare_mmap_struct{ + struct rb_node node; + long addr; + struct blare_tags *tags; + atomic_t refcount; +}; + +/* This structure is used by processes in their lists of bound mmaps + * @node is the list node + * @mmap is a pointer to the structure residing in the global tree for a given + * mmap*/ +struct blare_mmap_bound{ + struct list_head node; + struct blare_mmap_struct *mmap; +}; + +struct blare_mmap_struct *blare_mmap(unsigned long addr); + diff --git a/security/blare/include/netlabel.h b/security/blare/include/netlabel.h new file mode 100644 index 0000000..1438f32 --- /dev/null +++ b/security/blare/include/netlabel.h @@ -0,0 +1,36 @@ +/* Author: + * Christophe Hauser <christophe.hauser@xxxxxxxxxx> + * + * Copyright (C) 2010 University of Rennes 1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2. + */ + + +#include <linux/list.h> +#include <net/netlabel.h> +#include "../blare.h" + +#ifndef __BLARE_NETLABEL_H +#define __BLARE_NETLABEL_H + +#define BLARE_BITMAP_SIZE 32 // UNUSED +#define BLARE_BITMAP_MAX 239 // how many tags in the bitmap, see the RFC +#define BLARE_CIPSO_LEVEL 200 +#define BLARE_CIPSO_DOI 3 // Arbitrary +#define BLARE_CIPSO_HOST_LABEL_MAX 255 +#define BLARE_CIPSO_DOMAIN "BLARE_CIPSOv4" +#define BLARE_CIPSO_UNLBL "BLARE_UNLBL" + +#define INADDR_HOSTIP ((unsigned long int) 0x0a000001) +#define INADDR_HOSTMASK ((unsigned long int) 0xffffff00) + +void blare_cipso_doi(void); +struct list_head *blare_catmap2itag(struct netlbl_lsm_secattr_catmap *catmap); +int blare_itag2secattr(struct list_head *itag, struct netlbl_lsm_secattr *secattr); +int blare_label_socket(struct socket *socket); +int blare_netlabel_insert(struct blare_tags *tags, struct netlbl_lsm_secattr_catmap *catmap); + +#endif diff --git a/security/blare/include/network.h b/security/blare/include/network.h new file mode 100644 index 0000000..58f63ec --- /dev/null +++ b/security/blare/include/network.h @@ -0,0 +1,12 @@ +/* Author: + * Christophe Hauser <christophe.hauser@xxxxxxxxxx> + * + * Copyright (C) 2010 University of Rennes 1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2. + */ + + +int blare_network_policy_add(int *data, size_t size); diff --git a/security/blare/include/ptags.h b/security/blare/include/ptags.h new file mode 100644 index 0000000..7932e80 --- /dev/null +++ b/security/blare/include/ptags.h @@ -0,0 +1,34 @@ +/* Author: + * Christophe Hauser <christophe.hauser@xxxxxxxxxx> + * + * Copyright (C) 2010 University of Rennes 1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2. + */ + + +#ifndef __BLARE_PTAGS_H +#define __BLARE_PTAGS_H + +struct rb_root *array2tree(int *array, int size); +int tree_insert(struct rb_root *root, struct tree_item *new); +struct list_head* arrays2policytag(struct policy_array **arrays, int policy_count); +void tree_free(struct rb_root *root); +void policytag_print(struct list_head *head); +void policy_tag_free(struct list_head *head); +struct tree_item *tree_search(struct rb_root *root, int value); +int tree_is_subset(struct policy_tree *a, struct policy_tree *b); +struct policy_tree *intersection(struct policy_tree *a, struct policy_tree *b); +int xptag_insert(struct policy_array **xpolicy_arrays, int xpolicy_count, struct list_head **xpolicy_list); +void tree_print(struct rb_root *root); +struct list_head *ptag_copy(struct list_head *head); +struct rb_root *tree_copy(struct rb_root *root); +int *ptree2array(struct policy_tree *ptree); + + +void xptag_merge(struct list_head **a, struct list_head *b); +struct list_head *common_sets(struct list_head *a, struct list_head *b); + +#endif diff --git a/security/blare/include/shm.h b/security/blare/include/shm.h new file mode 100644 index 0000000..b6e1bb5 --- /dev/null +++ b/security/blare/include/shm.h @@ -0,0 +1,21 @@ +/* Author: + * Christophe Hauser <christophe.hauser@xxxxxxxxxx> + * + * Copyright (C) 2010 University of Rennes 1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2. + */ + + +#ifndef __BLARE_SHM +#define __BLARE_SHM + +struct list_head *shm_list_copy(struct list_head *list); +void shm_list_free(struct list_head *list); +int blare_update_shm_tags(struct list_head *list, struct list_head *info); +int shm_list_add(struct kern_ipc_perm *isp, struct list_head **list, int shmflg); +void shm_list_del(struct kern_ipc_perm *isp, struct list_head *list); + +#endif diff --git a/security/blare/include/test.h b/security/blare/include/test.h new file mode 100644 index 0000000..8cadfd5 --- /dev/null +++ b/security/blare/include/test.h @@ -0,0 +1,20 @@ +/* Author: + * Christophe Hauser <christophe.hauser@xxxxxxxxxx> + * + * Copyright (C) 2010 University of Rennes 1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2. + */ + + +#ifndef __BLARE_TEST_H +#define __BLARE_TEST_H + +/*Functions from blare_test.c */ +void test_alloc(void); +void list_test(void); +int checklist(struct list_head *head); + +#endif diff --git a/security/blare/include/trace.h b/security/blare/include/trace.h new file mode 100644 index 0000000..673d23a --- /dev/null +++ b/security/blare/include/trace.h @@ -0,0 +1,25 @@ + /* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2. + * + * Author: + * Christophe Hauser <christophe.hauser@xxxxxxxxxx> + * + * Copyright (C) 2010 University of Rennes 1 + */ + +void blare_init_trace(struct dentry *dp, struct blare_task_struct *tstruct); +void blare_trace(int type, int mode, int flags, struct file *file, int id); + + +#define TRACE_MMAP 1 +#define TRACE_FILE 2 +#define TRACE_SHM 3 +#define TRACE_INET 4 +#define TRACE_UNIX 5 +#define TRACE_MSGQ 6 +#define TRACE_PIPE 7 +#define TRACE_CLONE 8 + +#define MMAP_FLAGS 21 //strlen("MAP_ANONYNOUS +MAP_SHARED +MAP_PRIVATE"); diff --git a/security/blare/include/xattr.h b/security/blare/include/xattr.h new file mode 100644 index 0000000..450dc75 --- /dev/null +++ b/security/blare/include/xattr.h @@ -0,0 +1,27 @@ +/* Author: + * Christophe Hauser <christophe.hauser@xxxxxxxxxx> + * + * Copyright (C) 2010 University of Rennes 1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2. + */ + + +int blare_check_trace_flag(struct dentry *dp, int **ptr); + +int blare_alloc_file_cpolicy(struct dentry *dp, struct blare_file_struct *fstruct); +void blare_free_file_cpolicy(struct blare_file_struct *fstruct); + +int blare_alloc_file_info(struct dentry *d, struct blare_file_struct *fstruct); +void blare_free_file_info(struct blare_file_struct *fstruct); + +int blare_alloc_file_xpolicy(struct dentry *dp, struct blare_file_struct *fstruct); +void blare_free_file_xpolicy(struct blare_file_struct *fstruct); + +int blare_write_info(struct dentry *d, struct list_head *list); +int blare_write_xpolicy(struct dentry *dp, struct list_head *xpolicy_list); + + + diff --git a/security/blare/itag.c b/security/blare/itag.c new file mode 100644 index 0000000..8b4f864 --- /dev/null +++ b/security/blare/itag.c @@ -0,0 +1,540 @@ +/* Information tag functions - non RCU versions + * + * Author: + * Christophe Hauser <christophe.hauser@xxxxxxxxxx> + * + * Copyright (C) 2010 University of Rennes 1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2. + */ +#include "blare.h" +#include "include/itag.h" + +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/rbtree.h> +#include <linux/errno.h> + +/* Local static functions */ +static int itag_check_order(struct list_head *head); +static int itag_check_null_pointer(struct list_head *head); +static int itag_basic_check(struct list_head *head); + + +/* This function is used to serialize information tags (lists) + * The serialized representation is an array if int and can thus be written + * to the extended attributes of the filesystem. */ + +int itag2array(struct list_head *list, int **buffer){ + struct information *info; + int *array, i=0, size=0, buffersize; + + if (!list) + return 0; + + if (list_empty(list)) + return 0; + + // Count the number of items in the list + list_for_each_entry(info, list, node){ + size ++; + } + + if (size == 0) + return 0; + + buffersize = size * sizeof(int); + array = (int*)kmalloc(buffersize,GFP_KERNEL); + + if (!array) + return -ENOMEM; + + // Fill the array (in reverse order) + list_for_each_entry(info, list, node){ + if (i>=size) break; // just make sure we don't get out of the array bounds + array[i] = info->value; + i++; + } + + *buffer = array; + + if (DEBUG_INFO){ + blare_debug("[itag2array]: corresponding array has size %d: ",size); + array_print(array,size); + } + + return size; +} + + +/* Don't call this function, use array2itag or exec instead + * Create a new (ordered) list of struct information elements (i.e. an information tag) + * from an (ordered) array of int + * Returns : the head of the list*/ +struct list_head *array2itag_generic(int *array, int size, bool exec){ + struct list_head *head; + int i; + struct information *new; + + // If the array is null, there is no information + if (!array || !size || size <=0) + return NULL; + + head = kmalloc(sizeof(struct list_head), GFP_KERNEL); + + if (!head) + return NULL; + + /* Note : head is a standalone list_head element to describe the list + * see the list_add macro from linux/list.h */ + INIT_LIST_HEAD(head); + + //For each element in the array + for(i=0;i<size;i++){ + /* Discard negative tags (which should only be propagated on write + * access. We don't reread them afterwards) + */ + if (array[i] < 0) continue; + + new = kmem_cache_alloc(blare_info_cache,GFP_KERNEL); + if (!new) return NULL; + INIT_LIST_HEAD(&new->node); + + if (exec){ + new->value = -array[i]; + list_add(&new->node,head); + } + else{ + new->value = array[i]; + list_add_tail(&new->node,head); + } + } + + /* If the array only had negative elements, we have an empty list */ + if (list_empty(head)){ + kfree(head); + head = NULL; + } + return head; +} + +/* This stub function is used when reading an information tag + * from the extended attributes of the filesystem. */ + +struct list_head *array2itag(int *array, int size){ + return array2itag_generic(array,size,false); +} + +/* This stub function is used when executing code. The information tag + * is read from the filesystem, and transformed into negatives values + * (running code). */ + +inline struct list_head *itag_exec(int *array, int size){ + return array2itag_generic(array,size,true); +} + +/* Fills an existing (ordered) list of struct information elements (i.e. information tag) + * from an (ordered) array of int (xattr representation). + * Returns : the new head of the list. + * @exec_flag: determines whether we should discard the execution part of the + * itag (i.e. elements of X represented by negative values). + * When reading a regular file, those should be discarded. When executing code, + * the current process should merge its itag with exec(itag_of_file). + * */ +int itag_insert(int *array, int size, struct list_head **head, gfp_t flags, int exec_flag){ + //struct list_head *position; + struct information *info, *new; + int i=0, count=0; + + BLARE_WARN_ON((!head || !array || size<=0),"NULL pointers in itag_insert()" + "parameters, this should NOT happen"); + + if (!head || !array || size <=0) return -1; + + // If there is no list yet, then we we create a new one from the array + if (!*head){ + *head = array2itag(array,size); + return 0; + } + + list_for_each_entry(info, *head, node){ + /* We don't restart at the beginning of the array*/ + for(i=i; i<size; i++){ + // Discard negative tags (those are only propagated on write access) + if (array [i] < 0 && (exec_flag & DISCARD_EXEC)) continue; + + if (array[i] == info->value){ + i++; + break; //break the loop and iterate over the list + } + + if (array[i] > info->value && ! list_is_last(&info->node,*head)) + break; + + new = kmem_cache_alloc(blare_info_cache,flags); + if (!new){ + BLARE_DEBUG("[itag_insert] No mem :(\n"); + return -ENOMEM; + } + new->value = array[i]; + + if (array[i] < info->value) + list_add_tail(&new->node,&info->node); + else + list_add(&new->node,&info->node); + count ++; + } + } + return count; +} + + +/*Print the content of an information tag (list) */ +void itag_print(struct list_head *head){ + char *str; + str = itag_to_s(head); + printk(KERN_INFO "blare: %s (pid %d) %s\n", current->comm, current->pid, str); + kfree(str); +} + +void itag_print_msg(struct list_head *head, const char* msg, ...){ + va_list args; + + char *str = itag_to_s(head); + if (str){ + va_start(args, msg); + //printk(KERN_DEBUG "blare: %s (pid %d) %s %s\n", current->comm, current->pid, msg, str); + printk(KERN_DEBUG "blare: _%s(pid %d)_ %s: ", current->comm, current->pid, str); + vprintk(msg, args); + kfree(str); + va_end(args); + } +} + +/* This function alloates memory - don't forget to free it + * This function also sucks, use it at your own risk :). + * TODO: cleanup all that junk, or just remove it. + */ +char *itag_to_s(struct list_head *head){ + char *str = NULL, *msg; + struct information *infoptr; + int size=0, rc; + + rc = itag_check_sanity(head); + if ( rc != 0){ + blare_error(rc); + msg = "(sanity error)"; + goto alloc; + } + + if (!head){ + msg = "(null)"; + goto alloc; + } + + else if (list_empty(head)){ + msg = "(list_empty)"; + goto alloc; + } + + else{ + msg = "itag["; + size = (strlen(msg)) * sizeof(char); + list_for_each_entry(infoptr, head, node){ + //infoptr = list_entry(position, struct information, node); + if (infoptr){ + size += snprintf(NULL, 0, "%d ", infoptr->value); //just counts size + } + } + size += snprintf(NULL, 0, "]"); + /*sizeof(char) because strlen and sprintf don't count the terminating \0 char */ + str = (char*)kzalloc(size + sizeof(char), GFP_ATOMIC); + if (!str){ + return "ERROR"; + } + sprintf(str, "%s", msg); + list_for_each_entry(infoptr, head, node){ + //infoptr = list_entry(position, struct information, node); + if (infoptr) + sprintf(str + strlen(str), "%d ", infoptr->value); + } + + // strlen - 1 because we overwrite the last space + sprintf(str + strlen(str) - 1, "]"); + } + + return str; + +alloc: // const char* to char* so we can kfree it later. + size = (strlen(msg) + 1) * sizeof(char); + str = (char*)kmalloc(size, GFP_KERNEL); + sprintf(str, "%s", msg); + return str; +} + +int itag_count(struct list_head *head){ + int i=0; + struct information *info; + + if (!head) return -1; + list_for_each_entry(info, head, node) + i++; + return i; +} + +/* Free an itag */ +void itag_free(struct list_head *head){ + struct list_head *position, *tmp; + struct information *info; + + // In case there is no list + if (!head) return; + + + rc = itag_check_sanity(head); + if (rc !=0){ + blare_error(rc); + return; + } + + + list_for_each_safe(position, tmp, head){ + info = list_entry(position, struct information, node); + list_del(position); + if (info) + kmem_cache_free(blare_info_cache,info); + else + blare_warn("itag_free: itag has null entries :("); + } + kfree(head); +} + +/* Allocates a new information tag. + * On success, returns 0 and @security points to the new information tag. + * On failure to allocate memory, returns -ENOMEM + */ +int itag_alloc(struct list_head **security){ + struct list_head *itag; + + itag = kmalloc(sizeof(struct list_head),GFP_KERNEL); + if(!itag) return -ENOMEM; + + INIT_LIST_HEAD(itag); + *security = itag; + return 0; +} + +struct list_head *itag_copy(struct list_head *head, gfp_t gfp_flags){ + struct list_head *newhead; + struct information *infoptr, *new = NULL; + + if (!head){ + BLARE_DEBUG("(itag_copy) Error: NULL head"); + return NULL; + //return ERR_PTR(-ENODATA); + } + + newhead = kmalloc(sizeof(struct list_head), gfp_flags); + if (!newhead) goto nomem; + + INIT_LIST_HEAD(newhead); + + list_for_each_entry(infoptr, head, node){ + new = kmem_cache_alloc(blare_info_cache,gfp_flags); + if (!new){ + goto nomem; + } + + INIT_LIST_HEAD(&new->node); + new->value = infoptr->value; + list_add_tail(&new->node,newhead); + } + return newhead; + +nomem: + BLARE_DEBUG("[itag_copy] ERROR: not enough memory\n"); + itag_free(newhead); + //return ERR_PTR(-ENOMEM); + return NULL; +} + + +/* This is the non-RCU version of itag-merge */ +int itag_merge(struct list_head *a, struct list_head *b, gfp_t flags){ + struct list_head *itema, *itemb; + struct information *infoa, *infob, *new; + int count=0; + + /* We don't like NULL pointers */ + if (!a || !b) return -1; + + /* Nothing to merge */ + if (list_empty(b)) + return 0; + + /* This should not happen, itag_copy has to be used in this case */ + if (list_empty(a)) + return -ENODATA; + + // Pick b's first item (b itself isn't embedded in any structure) + infoa = list_first_entry(a, struct information, node); + infob = list_first_entry(b, struct information, node); + itema = &infoa->node; + itemb = &infob->node; + + do { + infoa = list_entry(itema,struct information, node); + infob = list_entry(itemb,struct information, node); + + if (!infoa || !infob){ + return -2; + } + + /* Avoid deadlooping on equal lists */ + if(infoa->value == infob->value && list_is_last(itema,a) && + list_is_last(itemb,b)) + goto out; + + else if(infoa->value == infob->value){ + itema = itema->next; + itemb = itemb->next; + } + + else if (infoa->value < infob->value && !list_is_last(itema,a)){ + itema = itema->next; + } + + else{ + new = kmem_cache_alloc(blare_info_cache,flags); + if (!new){ + goto nomem; + } + new->value = infob->value; + + // Add smaller values at the head + if (infob->value < infoa->value) + list_add_tail(&new->node,itema); + else + // Add higher values at the tail + list_add(&new->node,itema); + + itemb = itemb->next; + count ++; // How many new items + } + + } while (!list_is_last(itemb->prev,b)); + +out: + return count; +nomem: + BLARE_DEBUG("-ENOMEM in itag_insert :("); + return -ENOMEM; +} + +/* Prints a given array of int */ +void array_print(int *array, int size){ + int i; + BLARE_DEBUG("[array_print] "); + for(i=0;i<size;i++) + printk(KERN_DEBUG "%d, ",array[i]); + printk("\n"); +} + + +/* Check the sanity of an itag (for null pointers and ordering + * errors) */ +int itag_check_sanity(struct list_head *itag){ + int rc; + + if(!itag) + return 0; // Not unsafe + + /* rc in {0, -2, -3, -4}*/ + rc = itag_basic_check(itag); + if (rc != 0){ + return rc; + } + + /* rc in {0, -5} */ + rc = itag_check_null_pointer(itag); + if (rc !=0){ + blare_debug("itag is not safe, it has null pointers (error %d)",rc); + return rc; + } + + /* rc in {0, -1} */ + rc = itag_check_order(itag); + if (rc !=0){ + blare_debug("itag is not safe, its ordering is wrong (error %d)",rc); + return rc; + } + + return 0; +} + + + +/* Static functions - don't call them directly + * *******************************************/ + + +/* For debug purpose + * An itag should always be sorted in ascending order. If it is not the case, it + * is broken and that seriously sucks*/ +static int itag_check_order(struct list_head *head){ + struct information *info; + int x; + + info = list_first_entry(head, struct information, node); + x = info->value; + list_for_each_entry(info, head, node){ + if (info->value == 0) + continue; + if (info->value < x){ + return -1; + } + x = info->value; + } + return 0; +} + +/* Debug, check for NULL pointers or ERR_PTR in an itag*/ +static int itag_basic_check(struct list_head *head){ + if (!head) + return -2; + + if(IS_ERR(head)) + return(PTR_ERR(head)); + + if (!head->next) + return -3; + + if (!head->prev) + return -4; + + return 0; +} + + +/* Debug, check recursively for errors in an itag */ +static int itag_check_null_pointer(struct list_head *head){ + struct list_head *pos; + struct information *info; + int rc; + + list_for_each_entry(info, head, node){ + pos = &info->node; + if (!pos) + return -5; + + rc = itag_basic_check(pos); + + if(rc !=0) + return rc; + } + return 0; +} + + diff --git a/security/blare/itag_rcu.c b/security/blare/itag_rcu.c new file mode 100644 index 0000000..b9983d5 --- /dev/null +++ b/security/blare/itag_rcu.c @@ -0,0 +1,478 @@ +/* Information tag functions, RCU version + * + * Author: + * Christophe Hauser <christophe.hauser@xxxxxxxxxx> + * + * Copyright (C) 2010 University of Rennes 1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2. + */ +#include "blare.h" +#include "include/itag.h" + +#include <linux/list.h> +#include <linux/rculist.h> +#include <linux/slab.h> +#include <linux/rbtree.h> +#include <linux/errno.h> + +/* Local static functions - not exported in itag_rcu.h*/ +static int itag_check_order_rcu(struct list_head *head); +static int itag_check_null_pointer_rcu(struct list_head *head); +static int itag_basic_check_rcu(struct list_head *head); + + +/* This function is used to serialize information tags (lists). The serialized + * representation is an array of int, which is contigous in memory and can thus + * be dumped to the extended attributes of the filesystem straight away. */ + +int itag2array_rcu(struct list_head *list, int **buffer){ + struct information *info; + struct list_head *l; + int *array, i=0, size=0, buffersize; + + rcu_read_lock(); + l = rcu_dereference(list); + if (!l || list_empty(l)){ + rcu_read_unlock(); + return 0; + } + + // Count the number of items in the list + list_for_each_entry_rcu(info, list, node){ + size ++; + } + rcu_read_unlock(); + + if (size == 0) + return 0; + + buffersize = size * sizeof(int); + array = (int*)kmalloc(buffersize,GFP_KERNEL); + + if (!array) + return -ENOMEM; + + // Fill the array (in reverse order) + rcu_read_lock(); + list_for_each_entry_rcu(info, list, node){ + if (i>=size) break; // just make sure we don't get out of the array bounds + //info = list_entry_rcu(item, struct information, node); + array[i] = info->value; + i++; + } + rcu_read_unlock(); + + *buffer = array; + + if (DEBUG_INFO){ + blare_debug("[itag2array]: corresponding array has size %d: ",size); + array_print(array,size); + } + + return size; +} + + + +/* Fills an existing (ordered) list of struct information elements (i.e. information tag) + * from an (ordered) array of int (xattr representation). + * Returns : the new head of the list */ +int itag_insert_rcu(int *array, int size, struct list_head **head, gfp_t flags){ + //struct list_head *position; + struct information *info, *new; + int i=0; + + BLARE_WARN_ON((!head || !array || size<=0),"NULL pointers in itag_insert()" + "parameters, this should NOT happen"); + + if (!head || !array || size <=0) return -1; + + // If there is no list yet, then we we create a new one from the array + if (!*head){ + *head = array2itag(array,size); + return 0; + } + + rcu_read_lock(); + list_for_each_entry_rcu(info, *head, node){ + /* We don't restart at the beginning of the array*/ + for(i=i; i<size; i++){ + // Discard negative tags (those are only propagated on write access) + if (array [i] < 0) continue; + + if (array[i] == info->value){ + i++; + break; //break the loop and iterate over the list + } + + if (array[i] > info->value && ! list_is_last(&info->node,*head)) + break; + + new = kmem_cache_alloc(blare_info_cache,flags); + if (!new){ + BLARE_DEBUG("[itag_insert] No mem :(\n"); + rcu_read_unlock(); + return -ENOMEM; + } + new->value = array[i]; + + if (array[i] < info->value) + list_add_tail_rcu(&new->node,&info->node); + else + list_add_rcu(&new->node,&info->node); + } + } + rcu_read_unlock(); + + return 0; +} + + +char *itag_to_s_rcu(struct list_head *head){ + char *str = NULL, *msg; + struct information *infoptr; + int size=0, rc; + + rc = itag_check_sanity(head); + if ( rc != 0){ + blare_error(rc); + msg = "error"; + goto alloc; + } + + if (!head){ + msg = "itag[null"; + goto alloc; + } + + else if (list_empty(head)){ + msg = "itag[empty list"; + goto alloc; + } + + else{ + msg = "itag["; + size = (strlen(msg)) * sizeof(char); + rcu_read_lock(); + list_for_each_entry_rcu(infoptr, head, node){ + //infoptr = list_entry(position, struct information, node); + if (infoptr){ + size += snprintf(NULL, 0, "%d ", infoptr->value); //just counts size + } + } + size += snprintf(NULL, 0, "]"); + /*sizeof(char) because strlen and sprintf don't count the terminating \0 char */ + str = (char*)kzalloc(size + sizeof(char), GFP_ATOMIC); + if (!str){ + rcu_read_unlock(); + return "ERROR"; + } + sprintf(str, "%s", msg); + list_for_each_entry_rcu(infoptr, head, node){ + //infoptr = list_entry(position, struct information, node); + if (infoptr) + sprintf(str + strlen(str), "%d ", infoptr->value); + } + + // strlen - 1 because we overwrite the last space + sprintf(str + strlen(str) - 1, "]"); + } + + rcu_read_unlock(); + return str; + +alloc: // const char* to char* so we can kfree it later. + size = (strlen(msg) + 1) * sizeof(char); + str = (char*)kmalloc(size, GFP_KERNEL); + sprintf(str, "%s", msg); + return str; +} + + +int itag_count_rcu(struct list_head *head){ + int i=0; + struct information *info; + + if (!head) return -1; + rcu_read_lock(); + list_for_each_entry_rcu(info, head, node) + i++; + rcu_read_unlock(); + return i; +} + +/* This is the RCU-safe version */ +void itag_free_rcu(struct list_head *head){ + struct list_head *position, *tmp; + struct information *info; + int rc; + + // In case there is no list + if (!head) return; + + rc = itag_check_sanity(head); + if (rc !=0){ + blare_error(rc); + return; + } + + rcu_read_lock(); + list_for_each_safe(position, tmp, head){ + info = list_entry_rcu(position, struct information, node); + list_del_rcu(position); + if (info) + kmem_cache_free(blare_info_cache,info); + else + blare_warn("itag_free: itag has null entries :("); + } + rcu_read_unlock(); + kfree(head); +} + +struct list_head *itag_copy_rcu(struct list_head *head){ + struct list_head *newhead; + struct information *infoptr, *new = NULL; + + if (!head){ + BLARE_DEBUG("(itag_copy) Error: NULL head"); + return NULL; + //return ERR_PTR(-ENODATA); + } + + rcu_read_lock(); + newhead = kmalloc(sizeof(struct list_head), GFP_ATOMIC); + if (!newhead) goto nomem; + + INIT_LIST_HEAD(newhead); + + list_for_each_entry_rcu(infoptr, head, node){ + new = kmem_cache_alloc(blare_info_cache, GFP_ATOMIC); + if (!new){ + goto nomem; + } + + INIT_LIST_HEAD(&new->node); + new->value = infoptr->value; + list_add_tail_rcu(&new->node,newhead); + } + rcu_read_unlock(); + + return newhead; + +nomem: + rcu_read_unlock(); + BLARE_DEBUG("[itag_copy] ERROR: not enough memory\n"); + itag_free_rcu(newhead); + //return ERR_PTR(-ENOMEM); + return NULL; +} + +/* Merge list b into list a + * Returns: the number of new items in a, 0 if unchanged + */ +int itag_merge_rcu(struct list_head *a, struct list_head *b){ + struct list_head *itema, *itemb; + struct information *infoa, *infob, *new; + int count=0; + + if (!a || !b) return -1; + + rcu_read_lock(); + + // Pick b's first item (b itself isn't embedded in any structure) + infoa = list_first_entry_rcu(a, struct information, node); + infob = list_first_entry_rcu(b, struct information, node); + itema = &infoa->node; + itemb = &infob->node; + + do { + infoa = list_entry_rcu(itema,struct information, node); + infob = list_entry_rcu(itemb,struct information, node); + + if (!infoa || !infob){ + rcu_read_unlock(); + return -2; + } + + if(infoa->value == infob->value){ + itema = itema->next; + itemb = itemb->next; + } + + else if (infoa->value < infob->value && !list_is_last(itema,a)){ + itema = itema->next; + } + + else{ + new = kmem_cache_alloc(blare_info_cache, GFP_ATOMIC); + if (!new){ + goto nomem; + } + new->value = infob->value; + + // Add smaller values at the head + if (infob->value < infoa->value) + list_add_tail_rcu(&new->node,itema); + else + // Add higher values at the tail + list_add_rcu(&new->node,itema); + + itemb = itemb->next; + count ++; // How many new items + } + + } while (!list_is_last(itemb->prev,b)); + + rcu_read_unlock(); + return count; + +nomem: + rcu_read_unlock(); + BLARE_DEBUG("-ENOMEM in itag_insert :("); + return -ENOMEM; +} + + + +int itag_check_sanity_rcu(struct list_head *head){ + int rc; + + rc = itag_basic_check_rcu(head); + if (rc != 0) + return rc; + + rc = itag_check_null_pointer_rcu(head); + if (rc != 0) + return rc; + + rc = itag_check_order_rcu(head); + if (rc != 0) + return rc; + + return 0; +} + + + +/* With NTPL, threads are not standalone processes anymore, but they all share + * the same address space. We can't safely consider threads as isolated unless + * the code has been audited first, but in the case you want to watch threads + * separately, this is the place to do it. */ +struct blare_thread *blare_current_threadinfo_rcu(struct list_head *tlist){ + struct blare_thread *t; + + // In case there is no list ... TODO: BUG_ON() + if (!tlist) return NULL; + + rcu_read_lock(); + list_for_each_entry_rcu(t, tlist, node){ + if (t->pid == current->pid){ + rcu_read_unlock(); + return t; + } + } + rcu_read_unlock(); + return NULL; + + /* TODO: To be continued */ +} + + /*********************************************************************** +********** Static functions - don't call them directly ****************** +***********************************************************************/ + + +/* Perform preliminary checks before calling other check functions */ +static int itag_basic_check_rcu(struct list_head *head){ + struct list_head *h, *prev, *next; + + rcu_read_lock(); + + h = rcu_dereference(head); + next = rcu_dereference(head->next); + prev = rcu_dereference(head->prev); + + if (!head){ + rcu_read_unlock(); + return -1; + } + + if(IS_ERR(head)){ + rcu_read_unlock(); + return(PTR_ERR(head)); + } + + if (!next){ + rcu_read_unlock(); + return -6; + } + + if (!prev){ + rcu_read_unlock(); + return -7; + } + rcu_read_unlock(); + return 0; +} + + +/* For debug purpose + * An itag should always be sorted in ascending order. If it is not the case, it + * is broken and that seriously sucks*/ +static int itag_check_order_rcu(struct list_head *head){ + struct information *info; + int x=0; + rcu_read_lock(); + list_for_each_entry_rcu(info, head, node){ + //info = list_entry(pos,struct information,node); + if (x==0) + continue; + if (info->value < x){ + rcu_read_unlock(); + return -1; + } + x = info->value; + } + rcu_read_unlock(); + return 0; +} + + +static int itag_check_null_pointer_rcu(struct list_head *head){ + struct list_head *pos; + struct information *info; + int err; + + rcu_read_lock(); + + list_for_each_entry_rcu(info, head, node){ + err = -2; + if (!info) + goto err; + err = -3; + if (!info->value) + goto err; + + pos = &info->node; + + err = -4; + if (!pos->next) + goto err; + + err = -5; + if (!pos->prev) + goto err; + } + + rcu_read_unlock(); + return 0; + +err: + rcu_read_unlock(); + return err; +} + + diff --git a/security/blare/legality.c b/security/blare/legality.c new file mode 100644 index 0000000..5e70bc3 --- /dev/null +++ b/security/blare/legality.c @@ -0,0 +1,92 @@ + /* Author: + * Christophe Hauser <christophe.hauser@xxxxxxxxxx> + * + * Copyright (C) 2010 University of Rennes 1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2. + */ + + +#include "blare.h" +#include "include/itag.h" +#include "include/ptags.h" + + +/* Check whether the information tag of a process is included in its policy tag + * Return codes : + * -EACCESS (permission denied) in case of an invalid information flow + * 0 if there is no information tag or no policy tag + * 0 if the current process is trusted + * 1 if the information flow is legal + */ +int check_policy(struct list_head* info_list, struct list_head* policy_list){ + struct list_head *info_item, *tree_item; + struct information *info; + struct policy_tree *policy; + struct tree_item *tag; + int legal, rc; + + // Nothing to check + if (!info_list || !policy_list) return 0; + + if(list_empty(info_list)){ + if(DEBUG) printk("[check_policy] Process %s has an empty information tag...\n",current->comm); + return 0; + } + + /* Trusted processes have an empty itag (magic number 0) */ + rc = blare_check_trusted_process(info_list); + if (rc == 0) + return 0; + + // Iterate over the policy list + list_for_each(tree_item,policy_list){ + policy = list_entry(tree_item, struct policy_tree, list); + legal=1; + + // Iterate over the information tags + list_for_each(info_item,info_list){ + info = list_entry(info_item, struct information, node); + tag = tree_search(policy->root,info->value); + + if (!tag){ + legal=0; break; + /* + if(DEBUG_POLICY) + printk("[check_policy] %d is not in the tree, skipping\n",info->value); + */ + } + } + + if(legal == 1){ + if(DEBUG_POLICY) + printk("[blare_container_check] found a matching policy set (LEGAL)\n"); + return 1; + } + } + + return -EACCES; +} + + +/* Check an itag against the network policy */ +int check_netpolicy(struct list_head* info_list){ + int legal; + char *itag_s; + + if (!info_list) + return 1; + legal = check_policy(info_list, blare_network_policy); + if (legal < 0){ + itag_s = itag_to_s(info_list); + if (likely(itag_s)){ + printk(KERN_WARNING "blare_NETPOLICY_violation (pid %d - %s): %s\n", current->pid, current->comm, itag_s); + kfree(itag_s); + } + } + + return legal; +} + diff --git a/security/blare/lsm_hooks.c b/security/blare/lsm_hooks.c new file mode 100644 index 0000000..e244720 --- /dev/null +++ b/security/blare/lsm_hooks.c @@ -0,0 +1,1282 @@ +/* This file contains the LSM hooks related to the Blare IDS. + * All the hook always return 0 (permission granted), as we do + * not enforce the policy. + * + * Author: + * Christophe Hauser <christophe.hauser@xxxxxxxxxx> + * + * Copyright (C) 2010 University of Rennes 1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2. + * + */ + +#include "blare.h" +#include "include/shm.h" +//#include "include/test.h" +#include "include/file_ops.h" +#include "include/xattr.h" +#include "include/itag.h" +#include "include/itag_rcu.h" +#include "include/ptags.h" +#include "include/legality.h" +#include "include/netlabel.h" +#include "include/trace.h" +#include "include/mmap.h" + +#include <linux/security.h> +#include <linux/xattr.h> +#include <linux/mutex.h> +#include <linux/slab.h> +#include <linux/fs.h> +#include <linux/pipe_fs_i.h> +#include <linux/net.h> +#include <net/sock.h> +#include <linux/tcp.h> +#include <linux/in.h> +#include <asm/uaccess.h> +#include <linux/file.h> +#include <linux/socket.h> +#include <linux/mman.h> +#include <net/cipso_ipv4.h> +#include <net/netlabel.h> +#include <linux/stat.h> + +struct kmem_cache *blare_info_cache; +struct kmem_cache *blare_policy_array_cache; +struct kmem_cache *blare_tree_item_cache; +struct kmem_cache *blare_policy_tree_cache; +struct kmem_cache *blare_task_struct_cache; +struct kmem_cache *blare_file_struct_cache; +struct kmem_cache *blare_shmptr_cache; +struct kmem_cache *blare_tags_cache; + +struct list_head *blare_network_policy; + +// Keep tracks of memory initialization (for debug) +int inode_tags = 0; + +//struct proc_dir_entry *blare_proc_dir; +//struct proc_dir_entry *blare_net_policy; + +//struct kmem_cache *blare_cache_slab; +//struct rb_root blare_cache_root; + +int blare_initialized __initdata; +int blare_enabled = 1; //__initdata; // see blare_securityfs + +//static DEFINE_SPINLOCK(sk_lock); +rwlock_t sk_lock = __RW_LOCK_UNLOCKED(sk_lock); +unsigned long irqflags; + +static int __init blare_init(void); +security_initcall(blare_init); + +/*------------------------------- + * Blare initialization + * -----------------------------*/ +static int __init blare_init(void) +{ + int rc; + rc = register_security(&blare_ops); + + if (rc){ + printk (KERN_INFO "***** Failed to register Blare :( ***** \n"); + blare_initialized=0; + //blare_enabled = 0; + return rc; + } + + else + printk (KERN_INFO "***** Blare LSM loaded :) *****\n"); + + blare_initialized=1; + //blare_enabled = 1; + + // Initialize slab caches + blare_info_cache = kmem_cache_create("Blare_info", sizeof(struct + information), 0, SLAB_HWCACHE_ALIGN, NULL); + + blare_policy_array_cache = kmem_cache_create("Blare_policy_array", + sizeof(struct policy_array), 0, SLAB_HWCACHE_ALIGN, NULL); + + blare_tree_item_cache = kmem_cache_create("Blare_tree_item", + sizeof(struct tree_item), 0, SLAB_HWCACHE_ALIGN, NULL); + + blare_policy_tree_cache = kmem_cache_create("Blare_policy_tree", + sizeof(struct policy_tree), 0, SLAB_HWCACHE_ALIGN, NULL); + + blare_task_struct_cache = kmem_cache_create("Blare_task_struct", + sizeof(struct blare_task_struct), 0, SLAB_HWCACHE_ALIGN, NULL); + + blare_file_struct_cache = kmem_cache_create("Blare_file_struct", + sizeof(struct blare_file_struct), 0, SLAB_HWCACHE_ALIGN, NULL); + + blare_shmptr_cache = kmem_cache_create("Blare_shmptr", + sizeof(struct blare_shmptr), 0, SLAB_HWCACHE_ALIGN, NULL); + + blare_tags_cache = kmem_cache_create("Blare_tags", + sizeof(struct blare_tags), 0, SLAB_HWCACHE_ALIGN, NULL); + + /* Initlialize the network policy list + * It is a global list of policy tags shared by all the sockets. See blare.h + * for more information on policy tags + * */ + blare_network_policy = kmalloc(sizeof(struct list_head), GFP_KERNEL); + INIT_LIST_HEAD(blare_network_policy); + + blare_mmap_tree_root = kmalloc(sizeof(struct rb_root), GFP_KERNEL); + *blare_mmap_tree_root = RB_ROOT; + + /* Proc stuff + blare_proc_dir = proc_mkdir ("blare", NULL); + blare_uid_dir = proc_mkdir ("uid", blare_proc_dir); + blare_proc_socket = create_proc_entry("net_policy",0666,blare_proc_dir); + */ +#ifndef CONFIG_NETLABEL + printk(KERN_INFO "blare: please enable CONFIG_NETLABEL\n"); +#endif + + return 0; +} + + +/* ---------------------------- + * Blare LSM hooks + * ---------------------------*/ + +static int blare_inode_alloc_security(struct inode *inode){ + struct blare_tags *tags; + if (unlikely(inode->i_security)){ + BLARE_DEBUG("[inode_alloc_security] Pipe inode already has security" + "attached, overlaping with file_permission ? exiting\n"); + return 0; + } + + //BLARE_DEBUG("Allocating tags, inode no %lu",inode->i_ino); + tags = kmem_cache_zalloc(blare_tags_cache,GFP_KERNEL); + tags->info = NULL; + + if (!tags) return -ENOMEM; + inode->i_security = tags; + + inode_tags ++; + return 0; +} + +/* Inode permission: triggered by any operation on + * inodes such as fopen(), pipe_read() and the like + */ +static int blare_inode_permission(struct inode *inode, int mask, unsigned flags){ + struct blare_tags *tags; + int rc; + if (inode->i_pipe){ + tags = inode->i_security; + + if (tags && (mask & MAY_READ)){ + rc = blare_task_read(tags); + } + else if (mask & MAY_WRITE){ + if (!inode->i_security){ + //blare_inode_alloc_security(inode); + //tags = inode->i_security; + BLARE_DEBUG("No security attached to inode..."); + return 0; + } + + rc = blare_task_write(tags); + if (rc < 0){ + BLARE_DEBUG("Error %d writing to pipe",rc); + } + } + } + blare_trace(TRACE_PIPE, mask, flags, NULL, inode->i_ino); + return 0; +} + +static void blare_inode_free_security(struct inode *inode){ + struct blare_tags *tags; + //struct pipe_inode_info *info; + + /* + if (inode->i_pipe){ + info = inode->i_pipe; + while (info->readers >0 || info->writers>0) + schedule(); + } + */ + tags = inode->i_security; + tags_free(&tags); + inode->i_security = NULL; + inode_tags--; + + //BLARE_DEBUG("inode_free_security for inode no %lu - %d overall allocated inode tags",inode->i_ino,inode_tags); +} + +/* Read/write operations on files. + * Note : opening a file won't trigger this hook. + * See inode_permission in this case. + */ +static int blare_file_permission (struct file *file, int mask){ + struct blare_file_struct *fstruct; + struct dentry *dp; + struct inode *inode; + int rc; + struct kstat stat; + + if (!file->f_security){ + BLARE_DEBUG("[blare_file_permission] Error: no structure attached to current" + "file\n"); + return 0; + } + + fstruct = file->f_security; + + /* dcache locking mechanism, see documentation/filesystems/vfs.txt */ + dp = dget(file->f_dentry); + + /* We don't want sockets here */ + rc = vfs_getattr(file->f_vfsmnt, dp, &stat); + + if (S_ISSOCK(stat.mode) && DEBUG){ + //blare_warn("file_permission: IS_SOCK - Todo: handle this properly by calling socket_sendmsg|recvmsg\n"); + goto exit; + } + + /* There is no link between the inode/dentry/file on uninitialized FSs */ + if (!dp){ + if(DEBUG) printk("[blare_file_permission] Warning: filesystem has NOT" + "been initialized yet\n"); + return 0; + } + + inode = dp->d_inode; + if (!inode) + goto exit; + + if ((file->f_flags & O_APPEND) && (mask & MAY_WRITE)) + mask |= MAY_APPEND; + + /* This is for the unnamed pipes. (i.e.: cat | grep) */ + if (inode->i_pipe){ + blare_inode_permission(inode,mask,0); + goto exit; + } + + if((mask & MAY_READ))// || (mask & MAY_APPEND)) + rc = blare_may_read(dp,fstruct); + //rc = blare_shm_update_all(tstruct->shm,tstruct->info_list); + + /* Pure write with no append */ + if (mask & MAY_WRITE && ! (mask & MAY_APPEND)){ + /* + if (mask & MAY_APPEND) + else + */ + rc = blare_may_write(dp, fstruct); + } + + else if(mask & MAY_APPEND) + rc = blare_may_append(dp, fstruct); + + blare_trace(TRACE_FILE, mask, 0, file, 0); + +exit: + dput(dp); + return 0; +} + +/*blare_file_alloc_security: + * Allocate and attach a security structure to the file->f_security field.*/ +static int blare_file_alloc_security(struct file *file){ + struct blare_file_struct *fstruct; + + fstruct = kmem_cache_zalloc(blare_file_struct_cache, GFP_KERNEL); + if (!fstruct) + return -ENOMEM; + fstruct->info_array = NULL; + + file->f_security = fstruct; + return 0; +} + +/* + * blare_file_free_security: + * free the security structure attached to the file->f_security field + */ +static void blare_file_free_security(struct file *file){ + struct blare_file_struct *fstruct; + + if (file->f_security){ + fstruct = file->f_security; + blare_free_fstruct(fstruct); + kmem_cache_free(blare_file_struct_cache,fstruct); + file->f_security = NULL; + } +} + +/* Todo (or already caught by file_permission) */ +static int blare_file_mmap(struct file *file, unsigned long reqprot, + unsigned long prot, unsigned long flags, + unsigned long addr, unsigned long addr_only){ + + struct dentry *dp; + struct blare_file_struct *fstruct; + struct blare_mmap_struct *mstruct; + +#ifdef CONFIG_MMAP_ALLOW_UNINITIALIZED + if (flags & MAP_UNINITIALIZED) + blare_warning("Your kernel is configured with" + "CONFIG_MMAP_ALLOW_UNINITIALIZED, pages mapped with the" + "MAP_UNINITIALIZED won't be cleared beforehands"); +#endif + + /* Pages cannot be accessed */ + if(prot == PROT_NONE) + return 0; + + /* No file descriptor */ + if (flags & MAP_ANONYMOUS){ + blare_trace(TRACE_MMAP, prot, flags, NULL, addr); + } + + /* From here, we should always have a file descriptor */ + else{ + if (!file || !file->f_dentry){ + blare_debug("Hmm, non anonymous mmap with no file descriptor... flags are %lu and prot is %lu - address is %lu\n", flags, prot, addr); + return 0; + } + + if(!file->f_security){ + blare_debug("Hmm, mmaped file has no f_security field\n"); + return 0; + } + + fstruct = file->f_security; + + dp = dget(file->f_dentry); + + if(prot & PROT_READ) + blare_may_read(dp,fstruct); + + /* When MAP_PRIVATE, updates to the mapping are not carried through the + * underlying file */ + if ((prot & PROT_WRITE) && !(flags & MAP_PRIVATE)) + blare_may_write(dp,fstruct); + + /* Code execution : + * - append exec(itag) to the current->security->info_list + * - update policy_tag: restrict it + * TODO: should we check PROT_EXEC before PROT_WRITE instead + * (i.e. also transfer execution tags) ? */ + if(prot & PROT_EXEC) + blare_may_exec(dp, fstruct); + + blare_trace(TRACE_MMAP, prot, flags, file, addr); + dput(dp); + } + + /* Shared data between processes + if (flags & MAP_SHARED){ + mstruct = blare_mmap(addr); + } + */ + + return 0; + +} + +/* After execve, once the new creds have been set. + */ +static void blare_bprm_committed_creds(struct linux_binprm *bprm){ + // Before there was bprm_free_security, but it isn't used anymore + //struct blare_file_struct *fstruct; + struct blare_task_struct *tstruct; + const struct cred *ro_cred; + int rc; + + ro_cred = get_current_cred(); + tstruct = ro_cred->security; + //tstruct = current_security(); + if (!tstruct || ! tstruct->info_list){ + put_cred(ro_cred); + return; + } + + itag_print_msg(tstruct->info_list, "exec(%s)\n", bprm->filename); + + rc = blare_check_trusted_process(tstruct->info_list); + if (rc == 0) + printk(KERN_INFO "blare: %s (pid %d) : trusted process\n",current->comm, current->pid); + put_cred(ro_cred); +} + +/* + * Called during execve() + * - Read tags from the xattr of the file associted with binprm + * - Save security information in the bprm->cred->security field. + * - The process will get those credentials (bprm->cred) after execve(). + */ +static int blare_bprm_set_creds(struct linux_binprm *bprm){ + + int rc; + struct blare_file_struct *fstruct; + struct blare_task_struct *tstruct; + //struct list_head *xpolicy_list; + struct dentry *dp; + + /* Should we disable this ? */ + rc = cap_bprm_set_creds(bprm); + if (rc){ + printk(KERN_WARNING "blare: %s (pid %d) cap_bprm_set_creds returned error %d, this may be an issue...\n", current->comm, current->pid, rc); + return rc; + } + + // It might be called multiple times + if (bprm->cred_prepared || (blare_enabled == 0)) return 0; + + bprm->cred->security = NULL; + + /* There is a locking mechanism with dcache + * So we can't access file->f_dentry directly + * see Documentation/filesystem/dentry-locking.txt */ + dp = dget(bprm->file->f_dentry); + + /* It is possible that the filesystem hasn't been initialized yet + * In this case there is no link between the inode/dentry/file */ + if (!dp){ + printk("[bprm_set_creds] Filesystem hasn't been initialized\ + yet\n"); + return 0; + } + + /* Allocate a temporary security structure for the binary file */ + fstruct = kmem_cache_zalloc(blare_file_struct_cache, GFP_KERNEL); + if (!fstruct) return -ENOMEM; + + // Allocate a security structure for the process's tags + tstruct = kmem_cache_zalloc(blare_task_struct_cache, GFP_KERNEL); + + if (!tstruct){ + kmem_cache_free(blare_file_struct_cache, fstruct); + dput(dp); + BLARE_DEBUG("no memory, could not allocate task struct\n"); + return -ENOMEM; + } + + /* TODO: shm stuff + tstruct->shm = kmalloc(sizeof(struct list_head),GFP_KERNEL); + if (!tstruct->shm){ + dput(dp); + kmem_cache_free(blare_file_struct_cache, fstruct); + kmem_cache_free(blare_task_struct_cache, tstruct); + return -ENOMEM; + } + + INIT_LIST_HEAD(tstruct->shm); + */ + + /* Read the information tag */ + rc = blare_alloc_file_info(dp,fstruct); + if (rc > 0){ + tstruct->info_list = itag_exec(fstruct->info_array,fstruct->info_size); + blare_free_file_info(fstruct); + } + + /* Read the execute policy tag and use it to determine the policy tag of the + current process */ + rc = blare_alloc_file_xpolicy(dp,fstruct); + if (rc > 0){ + tstruct->policy_list = + arrays2policytag(fstruct->xpolicy_arrays,fstruct->xpolicy_count); + blare_free_file_xpolicy(fstruct); + + if (DEBUG_XPOLICY){ + printk("[bprm_set_creds] GOT xptag for %s : ",bprm->filename); + policytag_print(tstruct->policy_list); + } + } + + /* We can mark some binary files to be traceable. In this case, + * Blare will report all the operations those binaries do */ + blare_init_trace(dp, tstruct); + + bprm->cred->security = tstruct; + + blare_free_fstruct(fstruct); + kmem_cache_free(blare_file_struct_cache,fstruct); + + dput(dp); + + return 0; +} + +/* This is an alternative way to copy the process credentials (see + * blare_security_prepare_creds() for the original code. + * The idea behind this new function is that linked lists need only to be + * protected for concurrent writers, but not for concurrent readers. + * A single writer and multiple readers is fine. + * The linked list is safe by design when at most one writer updates at a + * given time. + * To be used along with blare_cred_free_alternate() + */ +static int blare_security_prepare_creds_alternate (struct cred *new, const struct cred + *old, gfp_t gfp) { + struct blare_task_struct *tnew; + const struct blare_task_struct *told; + + if (old && old->security){ + told = old->security; + tnew = kmemdup(told, sizeof(struct blare_task_struct), gfp); + new->security = tnew; + } + return 0; +} + + +/* Replaces security_task_alloc() - from new security hooks included in commit + d84f4f992cbd76e8f39c488cf0c5d123843923b1) + * Note : this is used by prepare_creds() to copy the security attributes + * of the process. The kernel does actually copy the credentials, but not + * the security fields. This part of the code is left to us. + * + * There are two cases + * - either the current task already has a security structure, and + * what we want is to copy it as is ; + * - or there is nothing, and then we create a new empty structure + * with a NULL info_list + */ +static int blare_security_prepare_creds (struct cred *new, const struct cred + *old, gfp_t gfp) { + struct blare_task_struct *tnew; + const struct blare_task_struct *told; + + /*=== The following copies all the structures*/ + + // At this stage, we initialize the new security structure to NULL + new->security = NULL; + + /* If there is no security field attached to the old credentials, + * we create a new empty one */ + + tnew = kmem_cache_zalloc(blare_task_struct_cache, GFP_KERNEL); + if (!tnew) + return -ENOMEM; + + /* If information exists in the old credentials, then we copy it + * to the new ones */ + told = old->security; + + if (told){ + if (told->info_list){ + tnew->info_list = itag_copy(told->info_list, GFP_KERNEL); + if (IS_ERR(tnew->info_list)) + tnew->info_list = NULL; + } + + if (told->policy_list) + tnew->policy_list = ptag_copy(told->policy_list); + + if (told->xpolicy_list) + tnew->xpolicy_list = ptag_copy(told->xpolicy_list); + + tnew->trace = told->trace; + + /* TODO: shm and mmap + if (told->shm) + tnew->shm = shm_list_copy(told->shm); + + if (told->shm_ro) + tnew->shm_ro = shm_list_copy(told->shm_ro); + */ + } + + new->security = tnew; + return 0; +} + +/* Fork() and stuff */ +static int blare_task_create(unsigned long clone_flags){ + + /* Parent & child share the same address space (i.e. copy_mm() does not + * allocate a new address space). + */ + if ((clone_flags & CLONE_VM) && !(clone_flags & CLONE_THREAD)) + blare_debug("CLONE_VM: we do not catch this yet, but parent & child will" + "share the same address space\n"); + + /* Otherwise, only the shared anonymous memory mappings are shared + */ + + blare_trace(TRACE_CLONE, 0, clone_flags, NULL, 0); + + return 0; +} + +/* This is the alternate version of blare_cred_free, see + * blare_prepare_creds_alternate()*/ +static void blare_cred_free_alternate(struct cred *cred){ + struct blare_task_struct *tstruct; + struct list_head *list; + + if (cred->security){ + tstruct = cred->security; + kmem_cache_free(blare_task_struct_cache, tstruct); + cred->security = NULL; + } + return; +} + + +/* @cred_free: + * @cred points to the credentials. + * Deallocate and clear the cred->security field in a set of credentials. + */ +static void blare_cred_free(struct cred *cred){ + struct blare_task_struct *tstruct; + struct list_head *list; + + /* Free everything including the itag, ptag and xptag*/ + + // In case there is nothing to free + if (!cred->security) + return; + + tstruct = cred->security; + list = tstruct->info_list; + + itag_free(tstruct->info_list); + + policy_tag_free(tstruct->policy_list); + policy_tag_free(tstruct->xpolicy_list); + //shm_list_free(tstruct->shm); + + cred->security = NULL; + kmem_cache_free(blare_task_struct_cache, tstruct); +} + +/* Security.h says: @cred_alloc_blank + * Only allocate sufficient memory and attach to @cred such that + * cred_transfer() will not get ENOMEM. + */ +static int blare_cred_alloc_blank(struct cred *cred, gfp_t gfp){ + struct blare_task_struct *tnew; + + tnew = kmem_cache_zalloc(blare_task_struct_cache, gfp); + tnew->info_list = NULL; + cred->security = tnew; + if (DEBUG) printk("Cred allocated a blank structure (kzalloc)\n"); + + return 0; +} + +/* security.h says : @cred_transfer + * Transfer data from original creds to new creds + */ +static void blare_cred_transfer(struct cred *new, const struct cred *old){ + new->security = old->security; + if (DEBUG) printk("[cred_transfer]\n"); +} + + +/* /proc/pid/attr/current gives the information tag of processes */ +static int blare_getprocattr(struct task_struct *task, char *name, + char **value) +{ + int error = -ENOENT; + char *str; + int *array; + int rc = 0; + int i; + int size = 0; + struct blare_task_struct *tstruct; + const struct cred *cred = get_task_cred(task); + + tstruct = cred->security; + //printk("getprocattr begin : %p\n", tstruct); + if (strcmp(name, "current") == 0) + { + rc = itag2array(tstruct->info_list, &array); + + if (rc < 0) + return rc; + + size += snprintf(NULL, 0, "itags : "); + for (i = 0 ; i < rc ; i++) + size += snprintf(NULL, 0, "%d ", array[i]); + + //for '\0' and '\n' + str = kmalloc(size + (2 * sizeof (char)), GFP_KERNEL); + sprintf(str, "itags : "); + + for (i = 0 ; i < rc ; i++) + sprintf(str + strlen(str) * sizeof(char),"%d ", array[i]); + + sprintf(str + strlen(str) * sizeof(char), "\n"); + + *value = str; + error = size + 1; + } + + else + error = -EINVAL; + + put_cred(cred); + kfree(array); + + return error; +} + + + +/* @sk_alloc_security: + * Allocate and attach a security structure to the sk->sk_security field, + * which is used to copy security attributes between local stream sockets. + */ +static int blare_sk_alloc_security (struct sock *sk, + int family, gfp_t priority){ + struct blare_tags *tags; + + tags = kmem_cache_zalloc(blare_tags_cache,GFP_KERNEL); + if (!tags) return -ENOMEM; + tags->info = NULL; + spin_lock_init(&tags->info_lock); + //kref_init(&tags->refcount); + sk->sk_security = tags; + + return 0; +} + + +/* @sk_free_security: + * Deallocate security structure. + */ +static void blare_sk_free_security(struct sock *sk){ + struct blare_tags *tags; + + tags = sk->sk_security; + if (!tags) + return; + + /* TODO: we only check itags here, we should + * call tags_free */ + if (tags->info) + itag_free(tags->info); + + kmem_cache_free(blare_tags_cache,tags); + // tags_free(&tags); + sk->sk_security = NULL; +} + + +/* This hook is used to free sk->sk_security before + * the sock gets released + * UNUSED, to be removed + * */ +void blare_socket_sock_release(struct sock *sk){ +} + +/* Read netlabel options off network packets (sk_buff) + * Extract information tag from the bitmap (catmap) + * Append tags to the current process's information tag + * Note: this code cannot block as the callers have spinlocks + */ +int blare_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb){ + int rc = 0; + struct netlbl_lsm_secattr secattr; + struct netlbl_lsm_secattr_catmap *catmap; + struct list_head *itag = NULL; + struct blare_tags *tags; + //struct sock_common common; + //__be32 daddr; + + if (unlikely((!sk || sk->sk_family != PF_INET || (blare_enabled == 0)))) + return 0; + + netlbl_secattr_init(&secattr); + rc = netlbl_skbuff_getattr(skb, sk->sk_family, &secattr); + + if (rc != 0){ + blare_error(rc); + return 0; + } + + tags = sk->sk_security; + if (unlikely(!tags)){ + blare_error(-ENODATA); + return 0; + } + + catmap = secattr.attr.mls.cat; + if (!catmap) + return 0; + + spin_lock(&tags->info_lock); + + if (!tags->info) + tags->info = blare_catmap2itag(catmap); + else + rc = blare_netlabel_insert(tags, catmap); + + spin_unlock(&tags->info_lock); + + if (rc < 0) + blare_error(rc); + + //itag_print_msg(tags->info, "rcv_skb:"); + + return 0; +} + +static int blare_socket_recvmsg(struct socket *sock, struct msghdr *msg, + int size, int flags){ + struct sock *sk; + struct blare_task_struct *tstruct; + int rc = 0; + struct cred *cred; + struct sock_common common; + __be32 daddr; + struct blare_tags *tags; + int err; + + sk = sock->sk; + if (unlikely(!sk)) + return 0; + + if (!sk->sk_security || blare_enabled == 0) + return 0; + + if(blare_current_is_trusted() == 0) + return 0; + + tags = sk->sk_security; + //kref_get(&tags->refcount); + + if (!tags->info && !tags->xpolicy) + goto release; + + /* Get IP addresses + common = sk->__sk_common; + daddr = common.skc_daddr; + */ + + cred = prepare_creds(); + if (unlikely(!cred)) + goto release; + + tstruct = cred->security; + if (unlikely(!tstruct)){ + abort_creds(cred); + goto release; + } + + if (tstruct->info_list){ + rc = itag_check_sanity(tags->info); + if (rc < 0){ + blare_error(rc); + abort_creds(cred); + goto release; + } + rc = itag_merge(tstruct->info_list, tags->info, GFP_ATOMIC); + //blare_debug("socket_recvmsg: merged %d tags from socket to process\n", rc); + } + else{ + tstruct->info_list = itag_copy(tags->info, GFP_ATOMIC); + } + + //itag_print_msg(tags->info, "(socket)blare_net_in");/* (%d.%d.%d.%d)", daddr + //& 0xff, (daddr << 8) & 0xff, (daddr << 16) & 0xff, (daddr << 24) & 0xff);*/ + + //kref_put (&tags->refcount, blare_sk_free_security); + + commit_creds(cred); + + if (sk->sk_family == AF_INET) + blare_trace(TRACE_INET, MAY_READ, flags, NULL, size); + else + blare_trace(TRACE_UNIX, MAY_READ, flags, NULL, size); + + return 0; + +release: + //kref_put (&tags->refcount, blare_sk_free_security); + return 0; +} + +/* Update the socket's netlabel before allowing outgoing traffic */ +static int blare_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size){ + struct netlbl_lsm_secattr secattr; + int rc; + struct sock *sk; + struct blare_task_struct *tstruct = NULL; + const struct cred *cred; + + sk = sock->sk; + + /* ipv4 only*/ + if (sk->sk_family != PF_INET || blare_enabled == 0) + return 0; + + if(blare_current_is_trusted() == 0) + return 0; + + cred = get_current_cred(); + if (unlikely(!cred)){ + //put_cred(cred); + return 0; + } + + tstruct = cred->security; + + /* No tags = no netlabel*/ + if (unlikely(!tstruct)){ + put_cred(cred); + blare_error(-ENODATA); + return 0; + } + + if (!tstruct->info_list || list_empty(tstruct->info_list)){ + put_cred(cred); + return 0; + } + + //itag_debug(tstruct->info_list,"socket_out"); + + netlbl_secattr_init(&secattr); + rc = blare_itag2secattr(tstruct->info_list, &secattr); + put_cred(cred); + + if (rc !=0){ + blare_error(rc); + return 0; + } + else{ + /* We need to lock the socket before setting netlabel secattr */ + local_bh_disable(); + bh_lock_sock_nested(sk); + + rc = netlbl_sock_setattr(sk, sk->sk_family, &secattr); + + bh_unlock_sock(sk); + local_bh_enable(); + + netlbl_secattr_destroy(&secattr); + } + + if (rc !=0) + blare_warn("error %d setting socket attributes", rc); + + blare_trace(TRACE_INET, MAY_WRITE, 0, NULL, size); + return 0; +} + + +/* This is the right place to set a first netlabel on the socket This is also + * the only place where we do not need a lock nor disabling the bottom halves. + */ +static int blare_socket_post_create(struct socket *sock, int family, + int type, int protocol, int kern){ + struct sock *sk; + sk = sock->sk; + sk->sk_security = NULL; + return 0; +} + +/* Unix sockets hook + * Process sends AF_UNIX packets*/ +static int blare_unix_may_send (struct socket *sock, struct socket *other){ + struct sock *sk; + struct blare_tags *tags; + + sk = other->sk; + + /* tags are allocated in sk_alloc_security */ + tags = sk->sk_security; + if (!tags) return 0; + + //BLARE_DEBUG("[unix_may_send] calls task_write"); + blare_task_write(tags); + + blare_trace(TRACE_UNIX, MAY_WRITE, 0, NULL, 0); + return 0; +} + + +/* We do track message queues too. + */ +static int blare_msg_queue_msgsnd (struct msg_queue *msq, struct msg_msg *msg, int msqflg){ + struct blare_tags *tags; + + tags = msg->security; + blare_task_write(tags); + printk("[blare_msg_queue_msgsnd] (process %s)\n",current->comm); + + blare_trace(TRACE_MSGQ, MAY_WRITE, 0, NULL, 0); + return 0; +} + +/* We receive a message, and we have to wake up the target process in case the + * current process is not the target process (which truely happens). + */ +static int blare_msg_queue_msgrcv (struct msg_queue *msq, struct msg_msg *msg, + struct task_struct *target, long type, int mode){ + struct blare_tags *tags; + int rc; + + if (!msg->security) return 0; + tags = msg->security; + printk("[blare_msg_queue_msgrcv] (target %s )\n", target->comm); + + /* We cannot alter target's credentials unless it is the current process */ + if (target != current) + wake_up_process(target); + + rc = blare_task_read(tags); + blare_trace(TRACE_MSGQ, MAY_READ, 0, NULL, 0); + return 0; +} + +/* Allocate a security structure to single messages in System V message queues */ +static int blare_msg_alloc_security(struct msg_msg *msg){ + struct blare_tags *tags; + tags = kmem_cache_zalloc(blare_tags_cache, GFP_KERNEL); + if (!tags) return -ENOMEM; + tags->info = NULL; + msg->security = tags; + return 0; +} + +static void blare_msg_free_security(struct msg_msg *msg){ + struct blare_tags *tags; + tags = msg->security; + tags_free(&tags); + msg->security = NULL; +} + +/* Allocate a security structure when a new shared memory segment is created +*/ +static int blare_shm_alloc_security(struct shmid_kernel *shp){ + struct blare_tags *shtags; + struct kern_ipc_perm *isp = &shp->shm_perm; + shtags = kzalloc(sizeof(struct blare_tags), GFP_KERNEL); + if (!shtags) return -ENOMEM; + + isp->security = shtags; + /*rc = itag_alloc(&shtags->info); + return rc; + */ + return 0; +} + +/* Current process attaches to a shared memory segment. */ +static int blare_shm_shmat (struct shmid_kernel *shp, char __user *shmaddr, int shmflg){ + struct blare_task_struct *tstruct; + struct kern_ipc_perm *isp = &shp->shm_perm; + int rc; + struct blare_tags *shtags; + const struct cred *cred; + + //TODO + return 0; + + cred = get_current_cred(); + tstruct = cred->security; + if (!tstruct) + goto out; + + if (isp->security) + shtags = isp->security; + else{ + blare_error(-ENODATA); + goto out; + } + /* + else + shtags = kmem_cache_alloc(blare_tags_cache, GFP_KERNEL); + */ + + if (!shtags){ + blare_error(-ENOMEM); + goto out; + } + + rc = shm_list_add(isp, &(tstruct->shm), shmflg); + if (rc < 0){ + blare_error(rc); + goto out; + } + + /* TODO: we should probably move that to shm_list_add: + * --- */ + rc = itag_merge(shtags->info, tstruct->info_list, GFP_KERNEL); + if (rc < 0) + blare_error(rc); + + rc = itag_merge(tstruct->info_list, shtags->info, GFP_KERNEL); + if (rc < 0) + blare_error(rc); + /*---*/ + +out: + put_cred(cred); + + blare_trace(TRACE_SHM, 0, shmflg, NULL, isp->id); + return 0; +} + + +/* Triggered by shmdt() syscall */ +static void blare_shm_shmdt(struct shmid_kernel *shp){ + struct blare_task_struct *tstruct; + struct kern_ipc_perm *isp; + const struct cred *cred; + + //TODO + return; + + isp = &shp->shm_perm; + if (isp && isp->security){ + cred = get_current_cred(); + tstruct = cred->security; + if (tstruct && tstruct->shm) + shm_list_del(isp,tstruct->shm); + + put_cred(cred); + } +} + +/* Free the security structure attached to a memory segments when it is + * destroyed. This hook is called in shm_destroy() (see ipc/shm.c). + */ +static void blare_shm_free_security(struct shmid_kernel *shp){ + struct blare_tags *shtags; + struct kern_ipc_perm *isp = &shp->shm_perm; + shtags = isp->security; + if (shtags){ + itag_free(shtags->info); + kfree(shtags); + isp->security = NULL; + } +} + +// Unused, just for debug purpose +int blare_file_receive(struct file *file){ + struct inode *inode; + struct dentry *dp; + dp = dget(file->f_dentry); + inode = dp->d_inode; + BLARE_DEBUG("received fd:"); + printk(KERN_DEBUG "%lu", inode->i_ino); + dput(dp); + return 0; +} + + +/* Hooks */ +struct security_operations blare_ops = { + .name = "blare", + + /* File hooks*/ + .file_permission = blare_file_permission, + .file_alloc_security = blare_file_alloc_security, + .file_free_security = blare_file_free_security, + .file_mmap = blare_file_mmap, + + .inode_alloc_security = blare_inode_alloc_security, + .inode_free_security = blare_inode_free_security, + + /* Task credentials */ + .cred_alloc_blank = blare_cred_alloc_blank, + .cred_free = blare_cred_free, + .cred_transfer = blare_cred_transfer, + .cred_prepare = blare_security_prepare_creds, + .task_create = blare_task_create, + + /* Proc attributes */ + .getprocattr = blare_getprocattr, + + /* bprm stuff (execution of binaries) */ + .bprm_set_creds = blare_bprm_set_creds, + .bprm_committed_creds = blare_bprm_committed_creds, + + /* Socket and IPC hooks */ + .sk_alloc_security = blare_sk_alloc_security, + .sk_free_security = blare_sk_free_security, + .socket_sock_rcv_skb = blare_socket_sock_rcv_skb, + .socket_recvmsg = blare_socket_recvmsg, + .socket_sendmsg = blare_socket_sendmsg, + //.socket_post_create = blare_socket_post_create, + .socket_sock_release = blare_socket_sock_release, + + .unix_may_send = blare_unix_may_send, + + /* Shared memory segments*/ + + .shm_shmdt = blare_shm_shmdt, + /* + .shm_shmat = blare_shm_shmat, + .shm_alloc_security = blare_shm_alloc_security, + .shm_free_security = blare_shm_free_security, + */ + + .msg_msg_alloc_security = blare_msg_alloc_security, + .msg_msg_free_security = blare_msg_free_security, + .msg_queue_msgrcv = blare_msg_queue_msgrcv, + .msg_queue_msgsnd = blare_msg_queue_msgsnd, + + /* We'll need them later + .netlink_recv = blare_netlink_recv, + .netlink_send = blare_netlink_send + */ +}; + + + +// *** Unused stuff *** +// +/* This extra hook was to make sure we don't create tag structures for + * all inodes but only those linked to a pipe... + * Only used once to debug stuff +int blare_pipe_create(struct file *fildes, int flags){ + struct dentry *dp; + struct inode *inode; + return 0; + + BLARE_DEBUG("pipe_create"); + + if (fildes->f_dentry) + dp = dget(fildes->f_dentry); + + if(dp){ + inode = dp->d_inode; + if (inode->i_pipe) + BLARE_DEBUG("Pipe initialized"); + } + + return 0; +} +*/ + +/* security.h says : @file_mprotect: + * Check permissions before changing memory access permissions. + * @vma contains the memory region to modify. + * @reqprot contains the protection requested by the application. + * @prot contains the protection that will be applied by the kernel. + * Return 0 if permission is granted. + */ + +/* This makes sure processes do not clear the O_APPEND flag that is used when a + * file is opened in append mode. + * But it also breaks stuff :( + * TODO: There are some authentication problems to resolve first. +static int blare_file_fcntl(struct file *file, unsigned int cmd, + unsigned long arg){ + if ((file->f_flags & O_APPEND) && !(arg & O_APPEND)) + return -EPERM; + return 0; +} +*/ + + diff --git a/security/blare/mmap.c b/security/blare/mmap.c new file mode 100644 index 0000000..9a11f49 --- /dev/null +++ b/security/blare/mmap.c @@ -0,0 +1,197 @@ +/* mmap management in Blare IDS. + * See include/mmap.h for more information + * + * Author: + * Christophe Hauser <christophe.hauser@xxxxxxxxxx> + * + * Copyright (C) 2010 University of Rennes 1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2. + * + */ + +#include "include/mmap.h" +#include "blare.h" +//#include <linux/list.h> +#include <linux/slab.h> +#include <linux/rbtree.h> +#include <linux/errno.h> + +struct rb_root *blare_mmap_tree_root; + +/* Local static functions */ +static struct blare_mmap_struct *blare_mmap_add(unsigned long addr); +static struct blare_mmap_struct *blare_mmap_del(unsigned long addr); + +static struct blare_mmap_struct *blare_search_mmap(unsigned long addr); + +static int mmap_bind(struct list_head **list, struct blare_mmap_struct *mstruct, + struct blare_mmap_bound **bound); + +static struct blare_mmap_bound *find_mmap(struct list_head *mmap_list, + struct blare_mmap_struct *mstruct); + + +/* This is the function to call when a mmap occurs + * @addr: the address of the mapping + * Returns: a pointer to the blare_mmap_struct for this mapping + * Note: if no structure exists yet, it will be created by the underlying + * code. + * */ +struct blare_mmap_struct *blare_mmap(unsigned long addr){ + struct blare_mmap_struct *mstruct; + struct blare_mmap_bound *bound; + const struct cred *cred; + struct blare_task_struct *tstruct; + struct list_head *list; + int rc = 0; + + mstruct = blare_mmap_add(addr); + atomic_inc(&mstruct->refcount); + //mmap_list_add(mstruct); //TODO + + cred = get_current_cred(); + tstruct = cred->security; + list = tstruct->mmap_list; + + /* Bind the mmap structure to the current process */ + rc = mmap_bind(&tstruct->mmap_list, mstruct, &bound); + + put_cred(cred); + return mstruct; +} + + +/* === Static === */ + +/* Add a new memory mapping to the global mmap tree */ +static struct blare_mmap_struct *blare_mmap_add(unsigned long addr){ + struct rb_node **new, *parent = NULL; + struct blare_mmap_struct *mstruct, *newstruct; + int value; + struct rb_root *root = blare_mmap_tree_root; + + if (!addr){ + blare_error(-ENODATA); + return NULL; + } + + new = &(root->rb_node); + + /* Go to the bottom of the tree */ + while (*new) + { + mstruct = container_of(*new,struct blare_mmap_struct,node); + value = mstruct->addr; + parent = *new; + if (addr > value) + new = &((*new)->rb_left); + else if (addr < value) + new = &((*new)->rb_right); + else + return mstruct; // If already in the tree + } + + /* Add new node and rebalance tree. */ + newstruct = kmalloc(sizeof(struct blare_mmap_struct), GFP_KERNEL); + rb_link_node(&newstruct->node, parent, new); + rb_insert_color(&newstruct->node, root); + + return mstruct; +} + +/* Return a reference on a blare_mmap_struct if found in the mmap tree + */ +static struct blare_mmap_struct *blare_search_mmap(unsigned long addr){ +struct rb_node *node; + struct blare_mmap_struct *mstruct; + struct rb_root *root = blare_mmap_tree_root; + + if (!root){ + blare_error(-ENODATA); + return NULL; + } + + node = root->rb_node; /* top of the tree */ + + while (node) + { + mstruct = rb_entry(node, struct blare_mmap_struct, node); + + if(DEBUG_MMAP) + printk("[tree_search] search for %lu, current node is %lu\n",addr ,mstruct->addr); + + if (mstruct->addr < addr) + node = node->rb_left; + else if (mstruct->addr > addr ) + node = node->rb_right; + else + return mstruct; /* Found it */ + } + return NULL; /*Found nothing*/ +} + + +/* Bind an entry of the mmap tree to a process */ +static int mmap_bind(struct list_head **list, struct blare_mmap_struct *mstruct, + struct blare_mmap_bound **bound){ + struct blare_mmap_bound *nbound; + struct list_head *newlist; + + if (!list) + return -ENODATA; + + /* The process may have no list at all at this stage if this is its first + * mmap, in this case we need to allocate it*/ +if (!*list){ + newlist = kmalloc(sizeof(struct list_head), GFP_KERNEL); + if (!newlist) return -ENOMEM; + INIT_LIST_HEAD(newlist); + *list = newlist; + } + + /* If the list already exists, we need to ensure the mmap is not (bound) + * there already */ + else{ + nbound = find_mmap(*list, mstruct); + if (nbound){ + *bound = nbound; + return 1; + } + } + + /* Alocate a list node to be added to the current process's shm list*/ + nbound = kmalloc(sizeof(struct blare_mmap_bound),GFP_KERNEL); + if (!nbound) + return -ENOMEM; + + nbound->mmap = mstruct; + list_add(&nbound->node, *list); + + *bound = nbound; + return 0; +} + +/* Find whether a mmap is already bound to the process holding mmap_list. + * This check is necessary to ensure we don't bind it twice in mmap_bind + */ +static struct blare_mmap_bound *find_mmap(struct list_head *mmap_list, struct blare_mmap_struct *mstruct){ + struct list_head *position; + struct blare_mmap_bound *bound; + + if (!mmap_list){ + blare_error(-ENODATA); + return NULL; + } + + list_for_each(position, mmap_list){ + bound = list_entry(position, struct blare_mmap_bound, node); + if (bound && bound->mmap == mstruct) + return bound; + } + return NULL; +} + + diff --git a/security/blare/netfilter_hooks.c b/security/blare/netfilter_hooks.c new file mode 100644 index 0000000..05da188 --- /dev/null +++ b/security/blare/netfilter_hooks.c @@ -0,0 +1,153 @@ +/* Netfilter hooks + * + * Author: + * Christophe Hauser <christophe.hauser@xxxxxxxxxx> + * + * Copyright (C) 2010 University of Rennes 1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2. + */ + + +#if defined (CONFIG_NETFILTER) +#include <linux/netfilter_ipv4.h> +#include "blare.h" +#include "include/netlabel.h" +#include <linux/export.h> +#include "include/itag.h" + +extern int blare_initialized; + +/* We don't use netfilter hooks anymore. ipv4_output was used at first to label + * the packets themselves (for outgoing traffic), as opposed to labelling the + * sockets as we do currently. To enable those hooks, uncomment the __initcall + * at the bottom of this file. + * */ +static unsigned int blare_ipv4_output(unsigned int hooknum, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + struct netlbl_lsm_secattr secattr; + int rc; + struct sock *sk; + struct blare_task_struct *tstruct = NULL; + const struct cred *cred; + + sk = skb->sk; + + /* ipv4 only*/ + if (sk->sk_family != PF_INET || blare_enabled == 0) + return 0; + + cred = get_current_cred(); + if (!cred){ + put_cred(cred); + return 0; + } + + tstruct = cred->security; + + /* No tags = no netlabel*/ + if (unlikely(!tstruct)){ + put_cred(cred); + blare_debug("No task struct"); + return NF_ACCEPT; + } + + if (!tstruct->info_list || list_empty(tstruct->info_list)){ + put_cred(cred); + return NF_ACCEPT; + } + + + netlbl_secattr_init(&secattr); + rc = blare_itag2secattr(tstruct->info_list, &secattr); + put_cred(cred); + + if (rc !=0){ + blare_error(rc); + } + else{ + /* We need to lock the socket before setting netlabel secattr */ + /* + local_bh_disable(); + bh_lock_sock_nested(sk); + */ + + rc = netlbl_skbuff_setattr(skb, sk->sk_family, &secattr); + + /* + bh_unlock_sock(sk); + local_bh_enable(); + */ + + itag_debug(tstruct->info_list,"socket_out"); + netlbl_secattr_destroy(&secattr); + } + + if (rc !=0) + blare_warn("error %d setting socket attributes", rc); + + return NF_ACCEPT; +} + + +/* We use socket_skb_rcv instead */ +static unsigned int blare_ipv4_input(unsigned int hooknum, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + + return NF_ACCEPT; +} + +static struct nf_hook_ops blare_ipv4_ops[] = { + { + .hook = blare_ipv4_output, + .owner = THIS_MODULE, + .pf = PF_INET, + .hooknum = NF_INET_LOCAL_OUT, + .priority = NF_IP_PRI_SECURITY, + .priority = 50 + }, + + { + .hook = blare_ipv4_input, + .owner = THIS_MODULE, + .pf = PF_INET, + .hooknum = NF_INET_LOCAL_IN, + .priority = NF_IP_PRI_SECURITY, + .priority = 50 + } +}; + + +/* Netfilter hooks init */ +static int __init blare_nf_ip_init(void) +{ + int err = 0; + + if (!blare_initialized){ + printk(KERN_DEBUG "Blare: blare is NOT initialized, can't initialize netfilter hooks :(\n"); + return 0; + } + + err = nf_register_hooks(blare_ipv4_ops, ARRAY_SIZE(blare_ipv4_ops)); + if (err) + panic("Blare: nf_register_hooks (IPv4) error %d :(\n", err); + else + printk(KERN_DEBUG "Blare: Registered netfilter hooks :)\n"); + + return 0; +} + +#endif + +//__initcall(blare_nf_ip_init); + diff --git a/security/blare/netlabel.c b/security/blare/netlabel.c new file mode 100644 index 0000000..2f11984 --- /dev/null +++ b/security/blare/netlabel.c @@ -0,0 +1,432 @@ +/* We use netlabel to carry security information on network packets. + * + * Author: + * Christophe Hauser <christophe.hauser@xxxxxxxxxx> + * + * Copyright (C) 2010 University of Rennes 1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2. + */ + + +#include <net/netlabel.h> +#include <net/cipso_ipv4.h> +#include "include/netlabel.h" +#include "blare.h" +#include <linux/in.h> +#include <linux/byteorder/generic.h> +#include "include/itag.h" + + +/** + * blare_netlabel_audit_set - fill a netlbl_audit struct + */ +void blare_netlabel_audit_set(struct netlbl_audit *nap) +{ + u32 secid = 10; //arbitrary static value - may be changed later + nap->secid = secid; + nap->loginuid = audit_get_loginuid(current); + nap->sessionid = audit_get_sessionid(current); +} + + + /* Initialization of Netlabel domain mappings + * Called from blare_init_securityfs() + */ +void blare_cipso_doi(void) +{ + int rc; + struct cipso_v4_doi *doip; + struct netlbl_audit nai; + + /* Netlabel mapping addresses*/ + //const struct in_addr inmask = {0x0}; // /0 + const struct in_addr inmask = {0x0}; + const struct in_addr inaddr = {0x0}; + + /* Localhost only, i.e. only local traffic gets labeled + const struct in_addr inmask_local = {0xff}; // /0 + const struct in_addr inaddr_local = {0x100007f}; //127.0.0.1 + */ + + blare_netlabel_audit_set(&nai); + + /* Remove Netlabel default unlabelled mapping*/ + rc = netlbl_cfg_map_del(NULL, PF_INET, NULL, NULL, &nai); + if (rc != 0) + printk(KERN_WARNING "blare %s:%d remove rc = %d\n", + __func__, __LINE__, rc); + + doip = kmalloc(sizeof(struct cipso_v4_doi), GFP_KERNEL); + if (doip == NULL) + panic("Blare: Failed to initialize cipso DOI.\n"); + doip->map.std = NULL; + doip->doi = BLARE_CIPSO_DOI; + doip->type = CIPSO_V4_MAP_PASS; + doip->tags[0] = CIPSO_V4_TAG_RBITMAP; + for (rc = 1; rc < CIPSO_V4_TAG_MAXCNT; rc++) + doip->tags[rc] = CIPSO_V4_TAG_INVALID; + + /* Add a cipsov4 doi */ + rc = netlbl_cfg_cipsov4_add(doip, &nai); + if (rc != 0) { + printk(KERN_WARNING "blare %s:%d failed to add cipso doi (%d) :(\n", + __func__, __LINE__, rc); + kfree(doip); + return; + } + + /* Blare labelled mapping + * I don't know why: specifying addresses here breaks netlbl_sock_setattr(), + * which always return -EDESTADDRREQ. Setting those to NULL strangely fixes it... + */ + //rc = netlbl_cfg_cipsov4_map_add(doip->doi, BLARE_CIPSO_DOMAIN, &inaddr, &inmask , &nai); + rc = netlbl_cfg_cipsov4_map_add(doip->doi, BLARE_CIPSO_DOMAIN, NULL, NULL , &nai); + + if (rc != 0) { + printk(KERN_WARNING "blare %s:%d failed to add cipsov4 mapping (%d) :(\n", + __func__, __LINE__, rc); + kfree(doip); + return; + } + + printk(KERN_INFO "Blare: CIPSO domain initialized :)\n"); + printk(KERN_INFO "Blare: netlabel mapping for addr=%x, mask=%x\n",inaddr.s_addr,inmask.s_addr); +} + +/* Fills an itag from a Netlabel category bitmap + * @tags: a pointer to the struct blare_tags of the socket (sk->sk_security) + * The caller must ensure tags->info exists before calling this function. + * */ +int blare_netlabel_insert(struct blare_tags *tags, struct netlbl_lsm_secattr_catmap *catmap){ + int pcat = -1, count = 0; + struct list_head *itag; + struct information *item, *info; + + if (unlikely(!catmap || !tags)) + return -1; + + itag = tags->info; + if (!itag) + return -2; + + list_for_each_entry(item, itag, node){ + for(pcat;;){ + pcat = netlbl_secattr_catmap_walk(catmap, pcat); + if (pcat < 0) //returns -ENOENT on failure + break; + + /* This goes to the next list item and to the next pcat */ + if (item->value == pcat){ + pcat ++; + break; + } + + /* This goes to the next list item*/ + if (pcat > item->value && !list_is_last(&item->node,itag)){ + break; + } + + info = kmem_cache_alloc(blare_info_cache,GFP_ATOMIC); + if (!info) goto nomem; + /* Here we substract the offset we added in blare_itag2catmap and + * get back the original values*/ + info->value = (pcat - (BLARE_BITMAP_MAX/2)); + + blare_debug("blare: converting recieved catmap:" + "pcat = %d -> info->value =%d\n", current->comm, + current->pid, pcat, info->value); + + if (pcat < item->value) + list_add_tail(&info->node,itag); + else + list_add(&info->node, itag); + count ++; + } + } + + return count; +nomem: + return -ENOMEM; +} + +/* Generates an itag from a Netlabel category bitmap */ +struct list_head *blare_catmap2itag(struct netlbl_lsm_secattr_catmap *catmap) +{ + int pcat; + struct list_head *itag; + struct information *info; + + if (!catmap) return NULL; + + itag = kmalloc(sizeof(struct list_head),GFP_ATOMIC); + if (!itag) goto nomem; + INIT_LIST_HEAD(itag); + + for(pcat=-1;;){ + pcat = netlbl_secattr_catmap_walk(catmap, pcat + 1); + if (pcat < 0) //returns -ENOENT on failure + break; + else{ + info = kmem_cache_alloc(blare_info_cache,GFP_ATOMIC); + if (!info) goto nomem; + /* Here we substract the offset we added in blare_itag2catmap and + * get back the original values*/ + info->value = (pcat - (BLARE_BITMAP_MAX/2)); + + if(DEBUG_NETWORK) + printk(KERN_DEBUG"blare: %s (pid %d) converting recieved catmap:" + "pcat = %d >> info->value =%d\n", current->comm, + current->pid, pcat, info->value); + + list_add_tail(&info->node,itag); + } + } + + if (list_empty(itag)){ + kfree(itag); + return NULL; + } + return itag; +nomem: + BLARE_DEBUG("ENOMEM :(\n"); + itag_free(itag); + //return ERR_PTR(-ENOMEM); + return NULL; +} + +/* Generates a netlabel category bitmap from an itag */ +int blare_itag2catmap_rcu(struct list_head* itag, struct netlbl_lsm_secattr_catmap *catmap) +{ + struct information *info; + //struct list_head *position; + int rc, val; + + if (!catmap || !itag) + return -ENODATA; + + catmap->startbit = 0; + + rcu_read_lock(); + list_for_each_entry_rcu(info, itag, node){ + //info = list_entry_rcu(position, struct information, node); + /* We do add an offset to the tags before insterting them in the catmap, + * as the catmap only takes positive values. */ + val = info->value + (BLARE_BITMAP_MAX/2); + if ((val > 0) && (val < BLARE_BITMAP_MAX)){ + if (DEBUG_NET) printk(KERN_DEBUG "blare: inserting %d(%d) in category map\n", val, info->value); + rc = netlbl_secattr_catmap_setbit(catmap, val, GFP_ATOMIC); + if (rc < 0) + blare_error(rc); + } + } + rcu_read_unlock(); + return 0; +} + +int blare_itag2catmap(struct list_head* itag, struct netlbl_lsm_secattr_catmap *catmap) +{ + struct information *info; + //struct list_head *position; + int rc, val; + + if (!catmap || !itag) + return -ENODATA; + + catmap->startbit = 0; + + list_for_each_entry(info, itag, node){ + //info = list_entry_rcu(position, struct information, node); + /* We do add an offset to the tags before insterting them in the catmap, + * as the catmap only takes positive values. */ + val = info->value + (BLARE_BITMAP_MAX/2); + if ((val > 0) && (val < BLARE_BITMAP_MAX)){ + if (DEBUG_NET) printk(KERN_DEBUG "blare: inserting %d(%d) in category map\n", val, info->value); + rc = netlbl_secattr_catmap_setbit(catmap, val, GFP_ATOMIC); + if (rc < 0) + blare_error(rc); + } + } + return 0; +} + + +/* Convert an itag into a netlabel secattr structure + * The secattr structure must be allocated and initialized by the caller. + * If the itag is NULL or empty, just set the MLS level and domain + * */ +int blare_itag2secattr(struct list_head *itag, struct netlbl_lsm_secattr *secattr){ + int rc; + + if(!itag) + return -ENODATA; + + if(IS_ERR(itag)) + return PTR_ERR(itag); + + secattr->flags |= (NETLBL_SECATTR_MLS_LVL | NETLBL_SECATTR_DOMAIN | NETLBL_SECATTR_SECID); + secattr->attr.mls.lvl = BLARE_CIPSO_LEVEL; + secattr->domain = BLARE_CIPSO_DOMAIN; + secattr->attr.secid = 10; + secattr->type = 1; + + if (itag && !list_empty(itag)){ + secattr->attr.mls.cat = netlbl_secattr_catmap_alloc(GFP_ATOMIC); + if (!secattr->attr.mls.cat) + return -ENOMEM; + + rc = blare_itag2catmap(itag, secattr->attr.mls.cat); + if (rc < 0) + goto err; + + secattr->flags |= NETLBL_SECATTR_MLS_CAT; + if (DEBUG_NET) + printk(KERN_DEBUG "blare_itag2secattr: %s (pid %d) catmap created\n", current->comm, current->pid); + } + return 0; + +err: + blare_error(rc); + netlbl_secattr_catmap_free(secattr->attr.mls.cat); + return rc; +} + + +/* ############ Unused funcions ############ */ + +/* Create a bitmap from an itag. This bitmap can then used as a network label + * to carry blare tags over the network. + * This function isn't used at the moment. + * */ +uint8_t *blare_itag2bitmap(struct list_head *itag){ + struct information *info; + //struct list_head *position; + int x; + uint8_t *bitmap; + + if (!itag) + return NULL; + + bitmap = kzalloc(sizeof(uint8_t) * BLARE_BITMAP_MAX, GFP_KERNEL); + + list_for_each_entry_rcu(info, itag, node){ + //info = list_entry_rcu(position, struct information, node); + x = info->value; + if (x < 240 && x>0){ + bitmap[x/8] |= (1 << (x%8)); + printk(KERN_INFO "blare: bitmap no %d is %02X\n",x/8,bitmap[x/8]); + } + } + return bitmap; +} + +/* Create an itag from a bitmap. + * * This function is **UNUSED*** + * */ +struct list_head *blare_bitmap2itag(uint8_t *bitmap){ + struct information *info; + struct list_head *itag; + int i,j,k,val; + + if (!bitmap) + return NULL; + + itag = kmalloc(sizeof(struct list_head), GFP_KERNEL); + INIT_LIST_HEAD(itag); + //if (!itag) return ERR_PTR(-ENOMEM); + if (!itag) return NULL; + + for (i=0; i<BLARE_BITMAP_SIZE; i++){ + for (j=0;j<7;j++){ + k = bitmap[i]; + if (((k >> j) & 1) == 1){ + val = 8 * i + j; + info = kmem_cache_alloc(blare_info_cache,GFP_KERNEL); + //if (!info) return ERR_PTR(-ENOMEM); + if (!info) return NULL; + info->value = val; + list_add_tail(&info->node,itag); + } + } + } + return itag; +} + +unsigned int inet_addr(char *str) +{ + int a,b,c,d; + char arr[4]; + sscanf(str,"%d.%d.%d.%d",&a,&b,&c,&d); + arr[0] = a; arr[1] = b; arr[2] = c; arr[3] = d; + return *(unsigned int*)arr; +} + +/* Unused */ +int blare_label_socket(struct socket *sock){ + struct blare_tags *sotags; + struct netlbl_lsm_secattr secattr; + int rc; + struct sock *sk; + struct blare_task_struct *tstruct; + const struct cred *cred; + + sk = sock->sk; + if (sk->sk_family != PF_INET || blare_enabled == 0) + return 0; + + cred = get_current_cred(); + tstruct = cred->security; + if (!tstruct){ + put_cred(cred); + return -ENODATA; + } + + itag_debug(tstruct->info_list,"socket_out"); + put_cred(cred); + + if (!sk->sk_security){ + blare_warn("socket should have tags"); + return 0; + } + + sotags = sk->sk_security; + /* Are we already up to date ? + * This should be in blare_task_write + if (sotags->info_rev == tstruct->info_rev && tstruct->info_rev != 0) + goto exit; + */ + + rc = blare_task_write(sotags); + if (rc !=0){ + blare_error(rc); + return 0; + } + + /* We need to lock the socket before setting netlabel secattr + local_bh_disable(); + bh_lock_sock_nested(sk); + */ + + netlbl_secattr_init(&secattr); + rc = blare_itag2secattr(sotags->info, &secattr); + if (rc !=0){ + blare_error(rc); + } + else + rc = netlbl_sock_setattr(sk, sk->sk_family, &secattr); + + netlbl_secattr_destroy(&secattr); + /* + bh_unlock_sock(sk); + local_bh_enable(); + */ + + if (rc !=0) + blare_warn("error %d setting socket attributes", rc); + return 0; +} + + + diff --git a/security/blare/network.c b/security/blare/network.c new file mode 100644 index 0000000..ebb91b8 --- /dev/null +++ b/security/blare/network.c @@ -0,0 +1,44 @@ +/* Network policy stuff + * + * Author: + * Christophe Hauser <christophe.hauser@xxxxxxxxxx> + * + * Copyright (C) 2010 University of Rennes 1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2. + */ + + +#include "blare.h" +#include "include/itag.h" +#include "include/ptags.h" + +/* + * Adds a new tree to the global network policy tag + */ +int blare_network_policy_add(int *data, size_t size){ + struct rb_root *root; + struct policy_tree *tree; + int count = size/sizeof(int); + + if(!data) return -ENODATA; + + tree = kmem_cache_alloc(blare_policy_tree_cache, GFP_KERNEL); + if (!tree) return -ENOMEM; + + root = array2tree(data,count); + + tree->root = root; + tree->cardinal = count; + + tree_print(root); + + // TODO: if no root ? + + list_add(&tree->list, blare_network_policy); + + return 0; +} + diff --git a/security/blare/network.h b/security/blare/network.h new file mode 100644 index 0000000..469eb91 --- /dev/null +++ b/security/blare/network.h @@ -0,0 +1,17 @@ +/* Author: + * Christophe Hauser <christophe.hauser@xxxxxxxxxx> + * + * Copyright (C) 2010 University of Rennes 1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2. + */ + + +#ifndef __BLARE_NET_H +#define __BLARE_NET_H +/* Functions from network.c*/ +int blare_network_policy_add(int *data, size_t size); + +#endif diff --git a/security/blare/ptags.c b/security/blare/ptags.c new file mode 100644 index 0000000..35ea4f4 --- /dev/null +++ b/security/blare/ptags.c @@ -0,0 +1,619 @@ +/* Policy tags functions. + * This file is splitted into two sections : ptag (policy tag) and xptag (execute policy tag). + * + * Author: + * Christophe Hauser <christophe.hauser@xxxxxxxxxx> + * + * Copyright (C) 2010 University of Rennes 1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2. + */ + +#include "blare.h" +#include "include/ptags.h" +#include "include/itag.h" + +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/rbtree.h> + +/* ######### Policy tag functions ##########*/ + +/* This function is used to convert policy tags from their XATTR linear representation + * into their abstract representation in memory. + * A set of arrays (each one being a subset of the policy) is converted into a list of trees. + */ +struct list_head* arrays2policytag(struct policy_array **arrays, int policy_count){ + int i,size; + int *array; + struct list_head *head; + struct policy_tree *new; + + BUG_ON(policy_count <= 0); + + // Where is this one freed ? + head = kmalloc(sizeof(struct list_head),GFP_KERNEL); + if (!head) return ERR_PTR(-ENOMEM); + + INIT_LIST_HEAD(head); + + for(i=0;i<policy_count;i++){ + new = kmem_cache_alloc(blare_policy_tree_cache,GFP_KERNEL); + if (!new) return ERR_PTR(-ENOMEM); + + array = arrays[i]->array; + size = arrays[i]->size; + + INIT_LIST_HEAD(&new->list); + + /* Skip null arrays, with an error */ + if(!array){ + blare_error(-ENODATA); + kmem_cache_free(blare_policy_tree_cache, new); + continue; + } + + new->root = array2tree(array,size); + /* This probably cannot happen */ + if (!new->root){ + blare_error(-ENODATA); + kmem_cache_free(blare_policy_tree_cache, new); + continue; + } + + new->cardinal = size; + list_add(&new->list,head); + + if(DEBUG_POLICY){ + BLARE_DEBUG("[arrays2policytag] Converted array to tree. Array: "); + array_print(array,size); + BLARE_DEBUG("[arrays2policytag] Tree: "); + tree_print(new->root); + } + } + return head; +} + + +/* Print all the trees of a policy tag */ +void policytag_print(struct list_head *head){ + struct list_head *position; + struct policy_tree *ptr; + + if (!head){ + BLARE_DEBUG("[policytag_print] null \n"); + return; + } + + if (list_empty(head)) + BLARE_DEBUG("[policytag_print] empty list\n"); + + list_for_each(position,head){ + ptr = list_entry(position, struct policy_tree, list); + tree_print(ptr->root); + printk(" - "); + } + printk("\n"); +} + +/* Free the memory of a whole policy tag + * including all its subsets (trees) + */ +void policy_tag_free(struct list_head *head){ + struct list_head *position,*tmp; + struct policy_tree *tree; + + if (!head ) return; + + list_for_each_safe(position,tmp,head){ + tree = list_entry(position, struct policy_tree, list); + tree_free(tree->root); + list_del(position); + kmem_cache_free(blare_policy_tree_cache,tree); + } + +} + +/* Returns a copy of the given policy tag (list of trees)*/ +struct list_head *ptag_copy(struct list_head *head){ + struct list_head *position, *newhead; + struct policy_tree *ptree, *newtree; + + if (!head){ + if (DEBUG) BLARE_DEBUG("[ptag_copy] Error: null head\n"); + return NULL; + } + + newhead = kmalloc(sizeof(struct list_head), GFP_KERNEL); + if (!newhead) return NULL; + INIT_LIST_HEAD(newhead); + + list_for_each(position,head){ + ptree = list_entry(position, struct policy_tree, list); + if (!ptree){ + if (DEBUG) BLARE_DEBUG("[ptag_copy] Error: null tree... abort\n"); + return NULL; // we should free memory here + } + + newtree = kmem_cache_alloc(blare_policy_tree_cache,GFP_KERNEL); + if (!newtree){ + BLARE_DEBUG("[ptag_copy] could not allocate memory\n"); + return NULL; + } + newtree->root = tree_copy(ptree->root); + if (ptree->cardinal) + newtree->cardinal = ptree->cardinal; + list_add(&newtree->list,newhead); + } + return newhead; +} + + +/* ######### Tree functions ##########*/ + +/* This creates a policy tree (binary tree of integers) from an array of integers. + * (A policy tag is a list of policy trees) + */ +struct rb_root *array2tree(int *array, int size){ + struct rb_root *root; + struct tree_item *new; + int i,rc; + + if (!array || size == 0){ + blare_error(-ENODATA); + return NULL; + } + + // Where is this one freed ? + root = kmalloc(sizeof(struct rb_root),GFP_KERNEL); + if(!root && DEBUG) + BLARE_DEBUG("[array2tree] memory allocation failed\n"); + + if (!root) return NULL; + + *root = RB_ROOT; + + for(i=0;i<size;i++){ + new = kmem_cache_alloc(blare_tree_item_cache,GFP_KERNEL); + + // If there is no memory, free the allocated nodes + // and return NULL + if (!new){ + if(DEBUG) + BLARE_DEBUG("[array2tree] couldn't create node\n"); + tree_free(root); + return NULL; + } + + new->value = array[i]; + rc = tree_insert(root,new); + if (rc < 0 && DEBUG) BLARE_DEBUG("[array2tree] Error: null root or tree_item\n"); + } + return root; +} + +int *ptree2array(struct policy_tree *ptree){ + int *array, i=0; + struct rb_node *node; + struct tree_item *tag; + struct rb_root *root; + + if (!ptree || !ptree->root){ + if (DEBUG_XPOLICY) BLARE_DEBUG("[ptree2array] WARNING: null tree. Exiting\n"); + return NULL; + } + + if (ptree->cardinal <= 0){ + if (DEBUG_XPOLICY) BLARE_DEBUG("[ptree2array] WARNING: non positive cardinal. Exiting\n"); + return NULL; + } + + root = ptree->root; + + array = kmalloc(ptree->cardinal * sizeof(int), GFP_KERNEL); + if (!array) return NULL; + + for (node = rb_last(root); node; node = rb_prev(node)){ + tag = rb_entry(node, struct tree_item, node); + array[i] = tag->value; + i++; + } + return array; +} + + +/* Insert new elements in a tree of integers + */ +int tree_insert(struct rb_root *root, struct tree_item *data){ + struct rb_node **new, *parent = NULL; + struct tree_item *tag; + int value; + + if (!root || !data){ + if (DEBUG) BLARE_DEBUG("[tree_insert] null root or data\n"); + return -1; + } + + new = &(root->rb_node); + + /* Go to the bottom of the tree */ + while (*new) + { + tag = container_of(*new,struct tree_item,node); + value = tag->value; + parent = *new; + if (data->value > tag->value) + new = &((*new)->rb_left); + else if (data->value < tag->value) + new = &((*new)->rb_right); + else + return 1; + } + + /* Add new node and rebalance tree. */ + rb_link_node(&data->node, parent, new); + rb_insert_color(&data->node, root); + + return 2; + +} + +/* Returns a copy of the tree */ +struct rb_root *tree_copy(struct rb_root *root){ + struct rb_root *newroot; + struct rb_node *node; + struct tree_item *tag, *newtag; + int rc; + + if (!root){ + if (DEBUG) BLARE_DEBUG("[tree_copy] Error: null root\n"); + return NULL; + } + + newroot = kmalloc(sizeof(struct rb_root), GFP_KERNEL); + if (!newroot) return NULL; + *newroot = RB_ROOT; + + for (node = rb_first(root); node; node = rb_next(node)){ + tag = rb_entry(node, struct tree_item, node); + if (!tag && DEBUG) BLARE_DEBUG("[tree_copy] Error: null tag !\n"); // That should not happen anyway + + newtag = kmem_cache_alloc(blare_tree_item_cache,GFP_KERNEL); + if (!newtag) { + BLARE_DEBUG("[tree_copy] Memory allocation error\n"); + return NULL; + } + + newtag->value = tag->value; + rc = tree_insert(newroot,newtag); + if (rc < 0) BLARE_DEBUG("[tree_copy] Error: null root or tree_item\n"); + } + return newroot; +} + + + +/* Free the memory of a tree (subset of a policy tag) + * */ +void tree_free(struct rb_root *root){ + struct rb_node *node,*tmp; + struct tree_item *tag; + + if (!root) return; + + for (node = rb_first(root); node; node = tmp){ + tmp = rb_next(node); + tag = rb_entry(node, struct tree_item, node); + rb_erase(node,root); + kmem_cache_free(blare_tree_item_cache,tag); + } +} + + +/* Print the given tree of a policy tag */ +void tree_print(struct rb_root *root){ + struct rb_node *node; + struct tree_item *tag; + + BLARE_DEBUG("[tree_print] "); + + if (!root){ + if (DEBUG) BLARE_DEBUG("null root"); + return; + } + + if(RB_EMPTY_ROOT(root)) + BLARE_DEBUG("empty root"); + + for (node = rb_first(root); node; node = rb_next(node)){ + tag = rb_entry(node, struct tree_item, node); + printk("%d ",tag->value); + } +} + + +/* Search in a given tree (subset of a policy tag) for a given value + * Will return the corresponding struct tree_item if found, or NULL if not found + */ +struct tree_item *tree_search(struct rb_root *root, int value){ + struct rb_node *node; + struct tree_item *item; + + if (!root){ + if (DEBUG) BLARE_DEBUG("[tree_search] Error: null root\n"); + return NULL; + } + + node = root->rb_node; /* top of the tree */ + + while (node) + { + item = rb_entry(node, struct tree_item, node); + + if(DEBUG_POLICY) + printk("[tree_search] search for %d, current node is %d\n",value,item->value); + + if (item->value < value) + node = node->rb_left; + else if (item->value > value) + node = node->rb_right; + else + return item; /* Found it */ + } + return NULL; /*Found nothing*/ +} + + +/* Finds out if a is a subset of b, or b is a subset of a (or none of the two) + * returns -1 if none is a subset of the other + * returns 1 if b is a subset of a + * returns 2 if a is a subset of b + */ +int tree_is_subset(struct policy_tree *a, struct policy_tree *b){ + struct policy_tree *big, *small; + struct rb_node *node; + struct tree_item *item_a, *item_b; + + if (a->cardinal > b->cardinal){ + big = a; small = b; + } + else{ + big = b ; small = a; + } + + // If the small tree is a subset of the big tree + // then all of its values are in the big tree ! + for (node = rb_first(small->root); node; node = rb_next(node)){ + item_a = rb_entry(node, struct tree_item, node); + + // Look for values of a inside b + item_b = tree_search(big->root,item_a->value); + + // If a value is not in the big tree, then we don't have a subset + if (!item_b) + return -1; + } + + // If we reach this point, one is the subset of the other... but which one ? + if (a == big){ + tree_print(b->root); + printk(KERN_DEBUG "is a subset of "); + tree_print(a->root); + printk(KERN_DEBUG "\n"); + return 1; + } + else{ + tree_print(a->root); + printk(KERN_DEBUG "is a subset of "); + tree_print(b->root); + printk(KERN_DEBUG "\n"); + return 2; + } +} + + + +/* Intersection of two sets (represented as trees) + * Iterates over the two trees and finds common elements + * The common elements form a new tree. A pointer on this new tree is returned. + * It returns NULL in case of failure + * */ +struct policy_tree *intersection(struct policy_tree *a, struct policy_tree *b){ + struct tree_item *item_a, *item_b, *new; + struct policy_tree *result; + struct rb_node *node; + struct rb_root *root; + int cardinal=0; + + if (!a || !b){ + if(DEBUG_XPOLICY) BLARE_DEBUG("[intersection] Error: one of the trees is NULL\n"); + return NULL; + } + + if (!a->root || !b->root){ + if (DEBUG_XPOLICY) BLARE_DEBUG("[intersection] Error: one of the trees does not have a root\n"); + return NULL; + } + + /* + if (DEBUG_XPOLICY){ + BLARE_DEBUG("[intersection] intersection of trees: "); + tree_print(a->root); + tree_print(b->root); + printk("\n"); + } + */ + + result = kmem_cache_alloc(blare_policy_tree_cache,GFP_KERNEL); + if (!result){ + BLARE_DEBUG("[intersection] ERROR: could not allocate memory\n"); + return NULL; + } + + result->cardinal = 0; + + // Quick check for an easy case of mutual exclusion + /* + if (rb_first(a->root) > rb_last(b->root) || rb_first(b->root) > rb_last(a->root)) + return result; // Empty tree + */ + + root = kmalloc(sizeof(struct rb_root), GFP_KERNEL); + if (!root) return NULL; + + *root = RB_ROOT; + result->root = root; + + // Iterate over a + for (node = rb_first(a->root); node; node = rb_next(node)){ + item_a = rb_entry(node, struct tree_item, node); + if (!item_a){ + if (DEBUG_XPOLICY) BLARE_DEBUG("[intersection] Error: NULL item in tree\n"); + kfree(result); + kfree(root); + return NULL; + } + + //if (DEBUG_XPOLICY) BLARE_DEBUG("[intersection] looking for %d in tree_b\n",item_a->value); + // Look for values of a inside b + item_b = tree_search(b->root,item_a->value); + + // If found, apend them to the result + if (item_b){ + new = kmem_cache_alloc(blare_tree_item_cache,GFP_KERNEL); + if (!new){ + BLARE_DEBUG("[intersection] ERROR: could not allocate memory\n"); + return NULL; + } + cardinal ++; + new->value = item_b->value; + tree_insert(result->root,new); + } + } + + result->cardinal = cardinal; + if (DEBUG_XPOLICY){ + BLARE_DEBUG("[intersection] newtree: "); + tree_print(result->root); + printk("(cardinal: %d)\n",cardinal); + } + return result; +} + + +/* ######### Xptag functions ##########*/ + +/* This function takes two policy tags (or execute policy tags) and looks for + * all the common elements of each of their subsets. This relation is defined + * as \sqcap in the ICC2011 paper */ + +void xptag_merge(struct list_head **a, struct list_head *b){ + struct list_head **old, *new; + if (a && *a){ + old = a; + new = common_sets(*a,b); + policy_tag_free(*old); + *a = new; + } +} + +struct list_head *common_sets(struct list_head *a, struct list_head *b){ + struct list_head *position_a, *position_b, *position_c, *newlist, *tmp; + struct policy_tree *tree_a, *tree_b, *tree_c, *inter; + int rc=0; + + if (!a || !b) return NULL; + + // initialize new list + newlist = kmalloc(sizeof(struct list_head),GFP_KERNEL); + if (! newlist) + return ERR_PTR(-ENOMEM); + + INIT_LIST_HEAD(newlist); + + // Compare all the sets from the two policies + list_for_each(position_a,a){ + tree_a = list_entry(position_a, struct policy_tree, list); + + list_for_each(position_b,b){ + tree_b = list_entry(position_b, struct policy_tree, list); + + inter = intersection(tree_a,tree_b); + if (!inter){ + if(DEBUG_XPOLICY) BLARE_DEBUG("[common_sets] Error: null intersection\n"); + continue; + } + + if (inter->cardinal == 0){ + kmem_cache_free(blare_policy_tree_cache,inter); + continue; + } + + // Is inter a subset of something already in there ? + list_for_each_safe(position_c,tmp,newlist){ + tree_c = list_entry(position_c, struct policy_tree, list); + + rc = tree_is_subset(inter,tree_c); + // If inter \subseteq tree_c + if (rc == 2){ + tree_free(inter->root); + kmem_cache_free(blare_policy_tree_cache,inter); + break; + } + + // If tree_c \subseteq inter + if (rc == 1){ + list_del(&tree_c->list); + tree_free(tree_c->root); + kmem_cache_free(blare_policy_tree_cache,tree_c); + break; + } + } + + // In case we didn't find any matching intersection + if (rc != 2 && inter->cardinal > 0) + list_add(&inter->list,newlist); + } + } + return newlist; +} + +/* This function reads the sets of xpolicy_arrays (that corresponds the xattr + * representation of an execute policy tag), converts it into a list of trees, + * and inserts it into the xptag of the current process. This inclusion + * proceeds to the suppression of similar sets (or sets included in other sets). + */ +int xptag_insert(struct policy_array **xpolicy_arrays, int xpolicy_count,struct list_head **xpolicy_list){ + struct list_head *converted, *newlist; + + //BUG_ON(!xpolicy_list); + if (!xpolicy_list){ + blare_warn("No xpolicy list"); + return -1; + } + + converted = arrays2policytag(xpolicy_arrays,xpolicy_count); + + if (IS_ERR(converted)) + return PTR_ERR(converted); + + if (DEBUG_XPOLICY){ + BLARE_DEBUG("[xptag_insert] Got xpolicy list from xattr: "); + policytag_print(converted); + } + + // No xpolicy_list for the current process + if (!*xpolicy_list){ + *xpolicy_list = converted; + return 1; + } + + // Modify current->xpolicy_list + newlist = common_sets(*xpolicy_list,converted); + policy_tag_free(*xpolicy_list); + *xpolicy_list = newlist; + + return 0; +} diff --git a/security/blare/shm.c b/security/blare/shm.c new file mode 100644 index 0000000..4d31a81 --- /dev/null +++ b/security/blare/shm.c @@ -0,0 +1,157 @@ +/* Shared memory segments + * Author: + * Christophe Hauser <christophe.hauser@xxxxxxxxxx> + * + * Copyright (C) 2010 University of Rennes 1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2. + */ + +#include <linux/list.h> +#include <linux/security.h> +#include <linux/slab.h> + +#include "blare.h" +#include "include/itag.h" +#include "include/shm.h" + +/* blare_update_shm: update information tags of all shared memory segments of + * @shmlist. + * @shmlist contains a linked list of struct blare_shmptr + * @info contains the source information tag used for the update. + */ +int blare_update_shm_tags(struct list_head *shmlist, struct list_head *info){ + struct blare_shmptr *shm; + struct list_head *item; + struct blare_tags *shtags; + + list_for_each(item,shmlist){ + shm = list_entry(item, struct blare_shmptr, node); + /* TODO: mutex on update ! */ + if (!(shm->shmflg & SHM_RDONLY) && shm->ptr){ + shtags = shm->ptr; + itag_merge(shtags->info, info, GFP_KERNEL); + } + } + return 0; +} + + +/* Shm lists attached to processes + * + */ + +/* Free a list */ +void shm_list_free(struct list_head *list){ + struct blare_shmptr *shm; + struct list_head *item, *tmp; + + if (!list) return; + + list_for_each_safe(item,tmp,list){ + shm = list_entry(item, struct blare_shmptr, node); + kmem_cache_free(blare_shmptr_cache,shm); + } + kfree(list); +} + +/* shm_list_add: adds a new shm information tag pointer to the list @list + */ +int shm_list_add(struct kern_ipc_perm *isp, struct list_head **list, int shmflg){ + struct blare_shmptr *bshm; + struct list_head *shm; + + if (!list || !isp) + return -ENODATA; + + /* Alocate a structure to be added to the current process's shm list*/ + bshm = kmem_cache_alloc(blare_shmptr_cache,GFP_ATOMIC); + if (!bshm) return -ENOMEM; + + bshm->ptr = isp->security; + bshm->shmflg = shmflg; + + /* The list might not have been initialized yet*/ + if (!*list){ + shm = kmalloc(sizeof(struct list_head), GFP_ATOMIC); + if (!shm) return -ENOMEM; + INIT_LIST_HEAD(shm); + *list = shm; + } + + list_add(&bshm->node, *list); + + return 0; +} + +/* Removes an item (@shp) from the list @list*/ +void shm_list_del(struct kern_ipc_perm *isp, struct list_head *list){ + struct list_head *item, *tmp; + struct blare_shmptr *bshm; + + if (!isp || !list){ + blare_error(-ENODATA); + return; + } + + if(!isp->security){ + blare_warn("I can't find the corresponding blare_shmptr with no" + "security pointer\n"); + return; + } + + list_for_each_safe(item, tmp, list){ + bshm = list_entry(item, struct blare_shmptr, node); + if (!bshm || !bshm->ptr){ + blare_error(-ENODATA); + break; + } + + if (bshm->ptr == isp->security){ + list_del(&bshm->node); + kmem_cache_free(blare_shmptr_cache, bshm); + } + } + + if (list_empty(list)) + kfree(list); +} + +/* Makes a copy of the shm list of a process. + */ +struct list_head *shm_list_copy(struct list_head *list){ + struct blare_shmptr *shm; + struct blare_shmptr *shm2; + struct list_head *item, *newlist; + + newlist = kmalloc(sizeof(struct list_head),GFP_KERNEL); + if (!newlist) return NULL; + + INIT_LIST_HEAD(newlist); + + list_for_each(item,list){ + shm = list_entry(item, struct blare_shmptr, node); + shm2 = kmem_cache_alloc(blare_shmptr_cache,GFP_KERNEL); + if (!shm2) goto nomem; + + shm2->ptr = shm->ptr; + shm2->shmflg = shm->shmflg; + INIT_LIST_HEAD(&shm2->node); + list_add(&shm2->node,newlist); + } + + return newlist; + +nomem: + /* Free what we have allocated*/ + list_for_each(item,newlist){ + shm = list_entry(item, struct blare_shmptr, node); + kmem_cache_free(blare_shmptr_cache, shm); + } + kfree(newlist); + return NULL; + //return -ENOMEM; +} + diff --git a/security/blare/tests.c b/security/blare/tests.c new file mode 100644 index 0000000..9b68468 --- /dev/null +++ b/security/blare/tests.c @@ -0,0 +1,114 @@ + +/* Some tests. If you got anything to write related to testing, it goes right + * here. + * Author: + * Christophe Hauser <christophe.hauser@xxxxxxxxxx> + * + * Copyright (C) 2010 University of Rennes 1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2. + */ + +#include "blare.h" +#include "include/itag.h" +#include "include/ptags.h" + +/*** Synopsis ***/ +void test_alloc(void); +int checklist(struct list_head *head); +struct list_head *itag_create(int *array, int size); +void list_test(void); + +/******************************************************/ + + +/* Test slab allocation + */ +void test_alloc(){ + char *item; + item = kmem_cache_alloc(blare_info_cache,GFP_KERNEL); + if(item) + printk("Object has been successfully allocated\n"); + else + printk("Error allocating object\n"); + + kmem_cache_free(blare_info_cache,item); + printk("Object freed\n"); + +} + + /* Check stuff + */ +int checklist(struct list_head *head){ + printk("Checklist...\n"); + return 0; +} + +struct list_head *itag_create(int *array, int size){ + struct list_head *head; + int i; + struct information *new; + + // If the array is null, there is no information + if (!array || !size || size <=0) + return NULL; + + head = kmalloc(sizeof(struct list_head), GFP_KERNEL); + + if (!head) + return ERR_PTR(-ENOMEM); + + /* Note : head is a standalone list_head element to describe the list + * see the list_add macro from linux/list.h */ + INIT_LIST_HEAD(head); + + //For each element in the array + for(i=0;i<size;i++){ + new = kmem_cache_alloc(blare_info_cache,GFP_KERNEL); + if (!new) return ERR_PTR(-ENOMEM); + INIT_LIST_HEAD(&new->node); + new->value = array[i]; + list_add_tail(&new->node,head); + } + + return head; +} + + +/* Lists tests + */ +void list_test(){ + struct list_head *list, *list2, *newlist; + int array[] = {-1,2,3,4,7,9}; + int array2[] = {-4,5,6,7,8,9,10}; + + list = itag_create(array,6); + itag_print(list); + + list2 = itag_create(array2,7); + itag_print(list2); + + /* + itag_insert(array2,3,list); + BLARE_DEBUG("New list:"); + itag_print(list); + */ + + BLARE_DEBUG("Copy: "); + newlist = itag_copy(list, GFP_KERNEL); + itag_print(newlist); + + BLARE_DEBUG("Merge: "); + itag_merge(list,list2, GFP_KERNEL); + itag_print(list); + + BLARE_DEBUG("Free: "); + itag_free(newlist); + itag_free(list); + itag_free(list2); + +} + + diff --git a/security/blare/trace.c b/security/blare/trace.c new file mode 100644 index 0000000..ba4d4d6b --- /dev/null +++ b/security/blare/trace.c @@ -0,0 +1,189 @@ + /* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2. + * + * Author: + * Christophe Hauser <christophe.hauser@xxxxxxxxxx> + * + * Copyright (C) 2010 University of Rennes 1 + * + * --- + * By Default, Blare IDS logs illegal information flows (security policy violations). + * It won't log all legal information flows (thought this could be done + * using the audit subsystem like smack does, see audit.c). + * + * However, it is sometimes needed to track all the information flows related + * to a specific binary as well as all the processes executing it. This can be + * useful to determine what a binary program is doing, and how Blare tags are + * propagated. This is a bit similar to what strace can do for one instance of + * a running program, but we generalize it to all the instances of the program + * (or bunch of programs) whenever it is executed. + * --- + */ + +#include "blare.h" +#include "include/audit.h" +#include "include/xattr.h" +#include "include/itag.h" +#include <linux/mman.h> +#include "include/trace.h" + +static inline void str_from_mmap_prot(char *string, int prot) +{ + int i = 0; + if (prot & PROT_READ) + string[i++] = 'r'; + if (prot & PROT_WRITE) + string[i++] = 'w'; + if (prot & PROT_EXEC) + string[i++] = 'x'; + string[i] = '\0'; +} + +static inline void str_from_mmap_flags(char *string, int flags){ + int offset = 0; + char *msg; + + if (flags & MAP_ANONYMOUS){ + msg = "+ANONYMOUS"; + strcat(string, msg); + offset += strlen(msg); + } + + if (flags & MAP_PRIVATE){ + msg = " +PRIVATE"; + strcat(string+offset, msg); + offset += strlen(msg); + } + + if (flags & MAP_SHARED){ + msg = " +SHARED"; + strcat(string+offset, msg); + } +} + + +/* Check wether we should trace a binary execution and + * set tstruct->trace appropriately*/ + +void blare_init_trace(struct dentry *dp, struct blare_task_struct *tstruct){ + int trace = 0, rc; + int *ptr; + + rc = blare_check_trace_flag(dp, &ptr); + if (rc == -ENODATA) + return; + + if (rc < 0) + blare_debug("error %d reading trace xattr\n"); + + trace = *ptr; + if (trace == 1){ + tstruct->trace = 1; + printk("blare: tracing enabled for %s\n", dp->d_name.name); + } + kfree(ptr); +} + +/* @subject: the file, socket or other object that is accessed + * @mode: the access mode [r,w,x,a] or the protection flags for mmaps + * @flags: special flags (MAP_ANONYMOUS etc.) + * @id: optional + */ +void blare_trace(int type, int mode, int flags, struct file *file, int id){ + const struct cred *cred; + char modes[NB_ACCESS_MODES + 1]; + char flags_str[MMAP_FLAGS + 1]; + struct blare_task_struct *tstruct; + char *itag, *typestr = NULL, *path = '\0', *buf = NULL; + // char *dpath = NULL + struct dentry *dp; + + if (!file) + goto next; + + dp = dget(file->f_dentry); + if (!dp) + goto next; + + /* + buf = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!buf){ + blare_error(-ENOMEM); + return; + } + + dpath = dentry_path(dp, buf, PAGE_SIZE); + if (IS_ERR(dpath)){ + blare_error(PTR_ERR(path)); + path = dp->d_name.name; + } + else + path = dpath; + */ + + path = (char*)dp->d_name.name; + dput(dp); + +next: + flags_str[0] = '\0'; + //strcat(modes, "n/a"); //by default, no mode + modes[0] = '\0'; + + cred = get_current_cred(); + tstruct = cred->security; + if (!tstruct || tstruct->trace == 0){ + put_cred(cred); + return; + } + + switch (type){ + case TRACE_MMAP: + typestr = "mmap"; + str_from_mmap_prot(modes, mode); + str_from_mmap_flags(flags_str, flags); + break; + case TRACE_SHM: + typestr = "shm"; + break; + case TRACE_FILE: + typestr = "file"; + str_from_perm(modes, mode); + break; + case TRACE_INET: + typestr = "af_inet"; + str_from_perm(modes, mode); // We use MAY_READ and MAY_WRITE instead of send/recieve + break; + case TRACE_UNIX: + typestr = "af_unix"; + str_from_perm(modes, mode); + break; + case TRACE_MSGQ: + typestr = "msg_queue"; + str_from_perm(modes, mode); + break; + case TRACE_PIPE: + typestr = "sys_pipe"; + str_from_perm(modes, mode); + break; + case TRACE_CLONE: + typestr = "sys_clone"; + //str_from_clone_flags(flags_str, flags); + break; + default: + typestr = ""; + blare_debug("no trace type given\n"); + return; + } + + itag = itag_to_s(tstruct->info_list); + put_cred(cred); + + printk(KERN_DEBUG "blare_trace: _%s(pid %d;cpu %d;parent %d)_ %s %s [mode %s] %s id %d -->" + "%s\n",current->comm, current->pid, current->on_cpu, current->real_parent->pid, typestr, path, modes, flags_str, id, + itag); + kfree(itag); + kfree(buf); +} + diff --git a/security/blare/xattr.c b/security/blare/xattr.c new file mode 100644 index 0000000..3d68014 --- /dev/null +++ b/security/blare/xattr.c @@ -0,0 +1,381 @@ +/* We do store security labels in the extended attributes of the filesystem + * (security namespace). + * + * Author: + * Christophe Hauser <christophe.hauser@xxxxxxxxxx> + * + * Copyright (C) 2010 University of Rennes 1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2. + */ + +#include "blare.h" +#include "include/itag.h" +#include "include/ptags.h" + +#include <linux/xattr.h> +#include <linux/rbtree.h> +#include <linux/errno.h> + + +/* Static functions specific to this module. You should probably not use them + * directly unless you know what you are doing */ +static int blare_read_xattr_size(struct dentry *dp, const char *fieldname); +static int blare_read_xattr(struct dentry *dp, const char *fieldname, int **buffer); +static int blare_write_xattr(struct dentry *dp, const char *fieldname, int *buffer, int buffersize); +static int blare_read_policy_generic(struct dentry *dp, struct blare_file_struct *fstruct, bool exec); + + +/* Check whether we should trace a binary program's execution */ +int blare_check_trace_flag(struct dentry *dp, int **ptr){ + if (!dp) return -ENODATA; + return blare_read_xattr(dp,TRACE_XATTR,ptr); +} + + +/* Reads the information tag of a file from the extended attributes, allocates + * fstruct->info_array and sets fstruct->info_count to the size of the array. + * @dentry : the dentry corresponding to the accessed file + * NB: to be used along with blare_file_free_info + * */ +int blare_alloc_file_info(struct dentry *dp, struct blare_file_struct *fstruct){ + int rc,count; + int *arrayptr; + + rc = blare_read_xattr(dp,INFO_XATTR,&arrayptr); + + /* Error*/ + if (rc<=0) + return rc; + + count = rc/sizeof(int); + if(arrayptr){ + fstruct->info_array = arrayptr; + fstruct->info_size = count; + return count; + } + + // We should never reach this point anyway + return -ENODATA; +} + + +/* Free the information (tag) parf of a fstruct*/ +inline void blare_free_file_info(struct blare_file_struct *fstruct){ + kfree(fstruct->info_array); + fstruct->info_array = NULL; +} + + +/* Get the container's policy from the extended attributes (and allocate the + * buffer to contain it in fstruct->xpolicy_arrays) */ +inline int blare_alloc_file_cpolicy(struct dentry *dp, struct blare_file_struct *fstruct){ + return blare_read_policy_generic(dp,fstruct,false); +} + +inline void blare_free_file_cpolicy(struct blare_file_struct *fstruct){ + int i; + for(i=0;i<fstruct->policy_count;i++) + kmem_cache_free(blare_policy_array_cache,fstruct->policy_arrays[i]); +} + +inline int blare_alloc_file_xpolicy(struct dentry *dp, struct blare_file_struct *fstruct){ + return blare_read_policy_generic(dp,fstruct,true); +} + +inline void blare_free_file_xpolicy(struct blare_file_struct *fstruct){ + int i; + for(i=0;i<fstruct->xpolicy_count;i++) + kmem_cache_free(blare_policy_array_cache,fstruct->xpolicy_arrays[i]); +} + + +/* Write the information tag to the filesystem's extended attributes + * @dp is the struct dentry attached to the file we're writing to (see dcache) + * @list points to the head of the information tag + */ +int blare_write_info(struct dentry *dp, struct list_head *list){ + int *array, buffersize,rc; + + rc = itag2array(list,&array); + if (rc <= 0) // <0 is an error, 0 is no data + return rc; + + buffersize = rc * sizeof(int); + // Write the xattr + rc = blare_write_xattr(dp,INFO_XATTR,array,buffersize); + if (rc < 0){ + if(DEBUG_INFO) + printk(KERN_DEBUG "[blare_write_info] error %d writing XATTR\n",rc); + kfree(array); + return rc; + } + + if (DEBUG_INFO) + itag_print_msg(list,"wrote to file %s ", dp->d_name.name); + // Cleanup + kfree(array); + + // If all is well + return 0; +} + + +/* Write an xptag to the extended attributes of a file + * @xpolicy_list: the xptag to write + * @dp: the dentry pointer + */ +int blare_write_xpolicy(struct dentry *dp, struct list_head *xpolicy_list){ + struct list_head *position; + struct policy_tree *ptree; + int xattr_name_len, rc, *array, *policycount; + char *xattr_name; + + if (!xpolicy_list) return 0; + + if (list_empty(xpolicy_list)){ + if(DEBUG_XPOLICY) printk("[blare_write_policy] attempt to write an EMPTY list\n"); + return 0; + } + + xattr_name_len = sizeof(XPOLICY_XATTR) + sizeof(int); + xattr_name = (char*) kmalloc(xattr_name_len,GFP_KERNEL); + policycount = (int*) kmalloc(sizeof(int), GFP_KERNEL); + *policycount = 0; + + list_for_each(position,xpolicy_list){ + ptree = list_entry(position, struct policy_tree, list); + if (!ptree){ + if (DEBUG_XPOLICY) printk("[blare_write_xpolicy] Got a NULL policy tree (policycount is %d)\n",*policycount); + return -1; + } + + array = ptree2array(ptree); + if (!array){ + if (DEBUG_XPOLICY) printk("[blare_write_xpolicy] Got a NULL array (at index %d)\n",*policycount); + return -2; + } + + snprintf (xattr_name, xattr_name_len,"%s%d", XPOLICY_XATTR,*policycount); + /* ptree->cardinal tells us how many information we need to write */ + rc = blare_write_xattr(dp,xattr_name,array,(ptree->cardinal * sizeof(int))); + if (rc < 0) return rc; + + (*policycount)++; + if (DEBUG_XPOLICY) printk("[blare_write_policy] policycount is %d\n",*policycount); + } + + rc = blare_write_xattr(dp,XPOLICY_COUNT,policycount,sizeof(int)); + kfree(policycount); + kfree(xattr_name); + + if (rc < 0) return rc; + + return 1; +} + + + +/* ########### Static ############# */ + +/* Returns the size of @fieldname from the xattr of a file + * @fieldname is the name of the field in the extended attributes, + * e.g. "security.blare.info". + */ +static int blare_read_xattr_size(struct dentry *dp, const char *fieldname){ + int rc; + struct inode *inode; + + inode = dp->d_inode; + + if (!inode->i_op->getxattr) + return -EOPNOTSUPP; + + // dummy NULL buffer + // vfs_getxattr will then return the size of *fieldname + // See man vfs_getxattr + + // Read the xattr + rc = inode->i_op->getxattr(dp, fieldname, NULL, 0); + //blare_debug("get_xattr_size returned %d - read access to %s", rc, dp->d_name.name); + return rc; +} + +/* Read @fieldname from the xattr of a file corresponding to @dp + * @fieldname is the name of the field in the extended attributes, + * e.g. "security.blare.info". + * @buffer points to the address of a buffer that is next allocated by + * this function (and needs to be freed afterwards). + */ +static int blare_read_xattr(struct dentry *dp, const char *fieldname, int **buffer){ + struct inode *inode; + int size,rc; + int *buff; + + if(!fieldname){ + printk("[blare_read_xattr] No Xattr Fieldname provided...\n"); + return -1; + } + + inode = dp->d_inode; + + // Get the size of the xattr + size = blare_read_xattr_size(dp,fieldname); + if (size<=0) + return size; + + // Allocate memory + buff = (int*)kzalloc(size,GFP_KERNEL); + if (!buff) + return -ENOMEM; + + // Read the xattr + rc = inode->i_op->getxattr(dp, fieldname, buff, size); + if (rc<0){ + kfree(buff); + return rc; + } + + *buffer = buff; + return size; +} + + +/* Write @buffer to the filesystem's extended attributes in the field corresponding to @fieldname + * @dp is the struct dentry attached to the file we're writing to (see dcache) + * @buffersize is the size of the buffer + */ +static int blare_write_xattr(struct dentry *dp, const char *fieldname, int *buffer, int buffersize){ + struct inode *inode; + int rc; + + inode = dp->d_inode; + + // First : is the xattr handler initialized for this file ? + if (!inode->i_op->setxattr){ + if(DEBUG_INFO) + printk("[blare_write_info] setxattr on file %s -EOPNOTSUPP... handler might not be initialized yet\n",dp->d_name.name); + return -EOPNOTSUPP; + } + + rc = inode->i_op->setxattr(dp, fieldname, buffer, buffersize, 0); + return rc; +} + + +/* Read the policy tag from the extended attributes of the filesystem + * @dp is the struct dentry attached to the file we're reading from (see dcache) + * @fstruct is the opaque security structure attached to the opened file + */ +static int blare_read_policy_generic(struct dentry *dp, struct blare_file_struct *fstruct, bool exec){ + + // This is not actually a list + struct policy_array *policy_array, **policy_list; + + //int buffersize, xattr_count, version, *versionptr, count, *buffer, rc,i, xattr_size; + int rc, i, xattr_name_len; + int *policy_count, *buffer; // *xattr_version + char *xattr_name; + const char *PCOUNT, *PATTR; + + /* Are we reading a policy or an execute policy ? */ + if(exec){ + PCOUNT = XPOLICY_COUNT; + PATTR = XPOLICY_XATTR; + xattr_name_len = sizeof(XPOLICY_XATTR) + sizeof(int); + } + else{ + PCOUNT = POLICY_COUNT; + PATTR = POLICY_XATTR; + xattr_name_len = sizeof(POLICY_XATTR) + sizeof(int); + } + + // Version of the policy + /* + rc = blare_read_xattr(dp,POLICY_VERSION,&xattr_version); + if (rc<0) + return rc; + printk("[blare_read_policy] policyversion: %d\n",*xattr_version); + */ + + // Get the number of subsets in the policy tag + rc = blare_read_xattr(dp, PCOUNT, &policy_count); + if (rc < 0) + return rc; + + // In case there is no policy defined for this container + if(!*policy_count > 0) + return -1; + + if(DEBUG_POLICY || DEBUG_XPOLICY) + printk("[blare_read_policy] %s:security.blare.policy_count is %d\n",dp->d_name.name,*policy_count); + + // Allocate memory for the list (note that it is a list of pointers to policy_array structs + policy_list = kmalloc(*policy_count * sizeof(struct policy_array*), GFP_KERNEL); + //policy_list = kmalloc(*policy_count * sizeof(int), GFP_KERNEL); + + //Allocate a buffer to store the names + //xattr_name_len = (sizeof(PATTR) + sizeof(int)); + xattr_name = (char*) kmalloc(xattr_name_len,GFP_KERNEL); + + // Read each array + for (i=0; i<*policy_count ; i++){ + // Each set of the policy tag is written in a separate xattr, with the same + // base name + a number (0-9); + snprintf (xattr_name, xattr_name_len,"%s%d", PATTR,i); + if(DEBUG_POLICY) + printk("[blare_read_policy] xattr name: %s\n",xattr_name); + + rc = blare_read_xattr(dp, xattr_name, &buffer); + if (rc < 0){ + kfree (xattr_name); + return rc; + } + + policy_array = kmem_cache_alloc(blare_policy_array_cache, GFP_KERNEL); + policy_array->array = buffer; + + // The number of integers in the int[] of the xattr + policy_array->size = rc/sizeof(int); + + policy_list[i] = policy_array; + } + + /* Are we reading a policy xattr or an execute policy xattr ? */ + if(exec){ + fstruct->xpolicy_arrays = policy_list; + fstruct->xpolicy_count = *policy_count; + } + else{ + fstruct->policy_arrays = policy_list; + fstruct->policy_count = *policy_count; + } + + /* Only free local stuff, the remaining is freed in : + * - file_free_security when this is called from file_permission + * - anywhere else in the other cases (e.g. after calling this function) + */ + + kfree(policy_count); + kfree(xattr_name); + + // If all is well + //return rc/sizeof(int); + return *policy_count; +} + +/* There is no FPU in kernel mode... + * This computes log10(*policy_count); +int log10(int x){ + int log; + log = 1; + while (x >= 10){ + log ++; + x = x / 10; + } + return log; +} + + * */ diff --git a/security/security.c b/security/security.c index e2f684a..a8c4031 100644 --- a/security/security.c +++ b/security/security.c @@ -946,6 +946,16 @@ int security_shm_shmat(struct shmid_kernel *shp, char __user *shmaddr, int shmfl return security_ops->shm_shmat(shp, shmaddr, shmflg); } +void security_shm_shmdt(struct shmid_kernel *shp) +{ + security_ops->shm_shmdt(shp); +} + +int security_pipe_create(struct file *fildes, int flags) +{ + return security_ops->pipe_create(fildes, flags); +} + int security_sem_alloc(struct sem_array *sma) { return security_ops->sem_alloc_security(sma); @@ -1125,6 +1135,11 @@ int security_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) } EXPORT_SYMBOL(security_sock_rcv_skb); +void security_sock_release(struct sock *sk){ + return security_ops->socket_sock_release(sk); +} +EXPORT_SYMBOL(security_sock_release); + int security_socket_getpeersec_stream(struct socket *sock, char __user *optval, int __user *optlen, unsigned len) {
Mail converted by MHonArc 2.6.19+ | http://listengine.tuxfamily.org/ |