lkml.org 
[lkml]   [2006]   [Aug]   [17]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
/
Date
From
SubjectRe: [RFC][PATCH 4/8] SLIM main patch
On Thu, Aug 17, 2006 at 12:53:22PM -0700, Kylene Jo Hall wrote:
> --- linux-2.6.18-rc3/security/slim/slm_main.c 1969-12-31 18:00:00.000000000 -0600
> +++ linux-2.6.18-rc3-working/security/slim/slm_main.c 2006-08-08 13:01:26.000000000 -0500
> @@ -0,0 +1,1526 @@
> +/*
> + * SLIM - Simple Linux Integrity Module
> + *
> + * Copyright (C) 2005,2006 IBM Corporation
> + * Author: Mimi Zohar <zohar@us.ibm.com>
> + * Kylene Hall <kjhall@us.ibm.com>
> + *
> + * 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 of the License.
> + */
> +
> +#include <linux/mman.h>
> +#include <linux/config.h>
> +#include <linux/kernel.h>
> +#include <linux/security.h>
> +#include <linux/integrity.h>
> +#include <linux/proc_fs.h>
> +#include <linux/socket.h>
> +#include <linux/fs.h>
> +#include <linux/file.h>
> +#include <linux/namei.h>
> +#include <linux/mm.h>
> +#include <linux/shm.h>
> +#include <linux/ipc.h>
> +#include <linux/errno.h>
> +#include <linux/xattr.h>
> +#include <net/sock.h>
> +
> +#include "slim.h"
> +
> +#define XATTR_NAME "security.slim.level"
> +
> +#define EXEMPT_STR "EXEMPT"
> +#define PUBLIC_STR "PUBLIC"
> +#define SYSTEM_SENSITIVE_STR "SYSTEM-SENSITIVE"
> +#define SYSTEM_STR "SYSTEM"
> +#define UNLIMITED_STR "UNLIMITED"
> +#define UNTRUSTED_STR "UNTRUSTED"
> +#define USER_SENSITIVE_STR "USER-SENSITIVE"
> +#define USER_STR "USER"
> +#define ZERO_STR "0"
> +
> +char *slm_iac_str[] = { ZERO_STR,
> + UNTRUSTED_STR,
> + USER_STR,
> + SYSTEM_STR
> +};
> +static char *slm_sac_str[] = { ZERO_STR,
> + PUBLIC_STR,
> + USER_STR,
> + USER_SENSITIVE_STR,
> + SYSTEM_SENSITIVE_STR
> +};

I'd find these easier to understand if the #defines were done nearer
the array that uses them; some are integrity, some are secrecy, and it
isn't exactly clear to me which ones are which from just the above.

> +static char *get_token(char *buf_start, char *buf_end, char delimiter,
> + int *token_len)
> +{
> + char *bufp = buf_start;
> + char *token = NULL;
> +
> + while (!token && (bufp < buf_end)) { /* Get start of token */
> + switch (*bufp) {
> + case ' ':
> + case '\n':
> + case '\t':
> + bufp++;
> + break;
> + case '#':
> + while ((*bufp != '\n') && (bufp++ < buf_end)) ;
> + bufp++;
> + break;
> + default:
> + token = bufp;
> + break;
> + }
> + }
> + if (!token)
> + return NULL;
> +
> + *token_len = 0;
> + while ((*token_len == 0) && (bufp <= buf_end)) {
> + if ((*bufp == delimiter) || (*bufp == '\n'))
> + *token_len = bufp - token;
> + if (bufp == buf_end)
> + *token_len = bufp - token;
> + bufp++;
> + }
> + if (*token_len == 0)
> + token = NULL;
> + return token;
> +}
> +
> +static int is_guard_integrity(struct slm_file_xattr *level)
> +{
> + if ((level->guard.iac_r != SLM_IAC_NOTDEFINED)
> + && (level->guard.iac_wx != SLM_IAC_NOTDEFINED))
> + return 1;
> + return 0;
> +}
> +
> +static int is_guard_secrecy(struct slm_file_xattr *level)
> +{
> + if ((level->guard.sac_rx != SLM_SAC_NOTDEFINED)
> + && (level->guard.sac_w != SLM_SAC_NOTDEFINED))
> + return 1;
> + return 0;
> +}
> +
> +static int is_lower_integrity(struct slm_file_xattr *task_level,
> + struct slm_file_xattr *obj_level)
> +{
> + if (task_level->iac_level < obj_level->iac_level)
> + return 1;
> + return 0;
> +}
> +static int is_isec_defined(struct slm_isec_data *isec)
> +{
> + if (isec && isec->level.iac_level != SLM_IAC_NOTDEFINED)
> + return 1;
> + return 0;
> +}

All of these booleans could be re-written to simply return the value of
the boolean check. I don't know if those are actually easier to read,
but someone should see them once and decide. :)

> +/*
> + * Called with current->files->file_lock. There is not a great lock to grab
> + * for demotion of this type. The only place f_mode is changed after install
> + * is in mark_files_ro in the filesystem code. That function is also changing
> + * taking away write rights so even if we race the outcome is the same.
> + */
> +static inline void do_revoke_file_wperm(struct file *file,
> + struct slm_file_xattr *cur_level)
> +{
> + struct inode *inode;
> + struct slm_isec_data *isec;
> +
> + inode = file->f_dentry->d_inode;
> + if (!inode)
> + return;

How would the code get to this point? Should this be a BUG_ON instead?

> + if (!S_ISREG(inode->i_mode) || !(file->f_mode && FMODE_WRITE))
> + return;
> +
> + isec = inode->i_security;
> + spin_lock(&isec->lock);
> + if (is_lower_integrity(cur_level, &isec->level))
> + file->f_mode &= ~FMODE_WRITE;
> + spin_unlock(&isec->lock);
> +}
> +
> +/*
> + * Revoke write permission on an open file.
> + */
> +static void revoke_file_wperm(struct slm_file_xattr *cur_level)
> +{
> + int i, j = 0;
> + struct files_struct *files = current->files;
> + unsigned long fd = 0;
> + struct fdtable *fdt;
> + struct file *file;
> +
> + if (!files || !cur_level)
> + return;
> +
> + spin_lock(&files->file_lock);
> + fdt = files_fdtable(files);
> +
> + for (;;) {
> + i = j * __NFDBITS;
> + if (i >= fdt->max_fdset || i >= fdt->max_fds)
> + break;
> + fd = fdt->open_fds->fds_bits[j++];
> + while (fd) {
> + if (fd & 1) {
> + file = fdt->fd[i++];
> + if (file && file->f_dentry)
> + do_revoke_file_wperm(file, cur_level);

Which files wouldn't have dentries?

> + }
> + fd >>= 1;
> + }
> + }
> + spin_unlock(&files->file_lock);
> +}
> +
> +static inline void do_revoke_mmap_wperm(struct vm_area_struct *mpnt,
> + struct slm_isec_data *isec,
> + struct slm_file_xattr *cur_level)
> +{
> + unsigned long start = mpnt->vm_start;
> + unsigned long end = mpnt->vm_end;
> + size_t len = end - start;
> +
> + if ((mpnt->vm_flags & (VM_WRITE | VM_MAYWRITE))
> + && (mpnt->vm_flags & VM_SHARED)
> + && (cur_level->iac_level < isec->level.iac_level))
> + do_mprotect(start, len, PROT_READ);
> +}
> +
> +/*
> + * Revoke write permission to underlying mmap file (MAP_SHARED)
> + */
> +static void revoke_mmap_wperm(struct slm_file_xattr *cur_level)
> +{
> + struct vm_area_struct *mpnt;
> + struct file *file;
> + struct dentry *dentry;
> + struct slm_isec_data *isec;
> +
> + flush_cache_mm(current->mm);

Is it a good idea to flush the cache before making the modifications?
Feels like the wrong order to me.

> + down_write(&current->mm->mmap_sem);
> + for (mpnt = current->mm->mmap; mpnt; mpnt = mpnt->vm_next) {
> + file = mpnt->vm_file;
> + if (!file)
> + continue;
> +
> + dentry = file->f_dentry;
> + if (!dentry || !dentry->d_inode)
> + continue;
> +
> + isec = dentry->d_inode->i_security;
> + do_revoke_mmap_wperm(mpnt, isec, cur_level);
> + }
> + up_write(&current->mm->mmap_sem);
> +}
> +
> +/*
> + * Revoke write permissions and demote threads using shared memory
> + */
> +static void revoke_permissions(struct slm_file_xattr *cur_level)
> +{
> + if (!is_kernel_thread(current)) {
> + revoke_mmap_wperm(cur_level);
> + revoke_file_wperm(cur_level);
> + }
> +}
> +
> +static enum slm_iac_level set_iac(char *token)
> +{
> + int iac;
> +
> + if (strncmp(token, EXEMPT_STR, strlen(EXEMPT_STR)) == 0)
> + return SLM_IAC_EXEMPT;
> + for (iac = 0; iac < sizeof(slm_iac_str) / sizeof(char *); iac++) {
> + if (strncmp(token, slm_iac_str[iac], strlen(slm_iac_str[iac]))
> + == 0)
> + return iac;
> + }
> + return SLM_IAC_ERROR;
> +}
> +
> +static enum slm_sac_level set_sac(char *token)
> +{
> + int sac;
> +
> + if (strncmp(token, EXEMPT_STR, strlen(EXEMPT_STR)) == 0)
> + return SLM_SAC_EXEMPT;
> + for (sac = 0; sac < sizeof(slm_sac_str) / sizeof(char *); sac++) {
> + if (strncmp(token, slm_sac_str[sac], strlen(slm_sac_str[sac]))
> + == 0)
> + return sac;
> + }
> + return SLM_SAC_ERROR;
> +}
> +
> +static inline int set_bounds(char *token)
> +{
> + if (strncmp(token, UNLIMITED_STR, strlen(UNLIMITED_STR)) == 0)
> + return 1;
> + return 0;
> +}
> +
> +/*
> + * Get the 7 access class levels from the extended attribute
> + * Format: TIMESTAMP INTEGRITY SECRECY [INTEGRITY_GUARD INTEGRITY_GUARD] [SECRECY_GUARD SECRECY_GUARD] [GUARD_ TYPE]
> + */
> +static int slm_parse_xattr(char *xattr_value, int xattr_len,
> + struct slm_file_xattr *level)
> +{
> + char *token;
> + int token_len;
> + char *buf, *buf_end;
> + int fieldno = 0;
> + int rc = 0;
> +
> + buf = xattr_value;
> + buf_end = xattr_value + xattr_len;
> +
> + while ((token = get_token(buf, buf_end, ' ', &token_len)) != NULL) {
> + buf = token + token_len;
> + switch (++fieldno) {
> + case 1:
> + level->iac_level = set_iac(token);

set_iac() seems an odd name for a function parsing a string to return an
integer. Same with the other functions here..

> + if (level->iac_level == SLM_IAC_ERROR) {
> + rc = -1;
> + level->iac_level = SLM_IAC_UNTRUSTED;
> + }
> + break;
> + case 2:
> + level->sac_level = set_sac(token);
> + if (level->sac_level == SLM_SAC_ERROR) {
> + rc = -1;
> + level->sac_level = SLM_SAC_PUBLIC;
> + }
> + break;
> + case 3:
> + level->guard.iac_r = set_iac(token);
> + if (level->guard.iac_r == SLM_IAC_ERROR) {
> + rc = -1;
> + level->iac_level = SLM_IAC_UNTRUSTED;
> + }
> + break;
> + case 4:
> + level->guard.iac_wx = set_iac(token);
> + if (level->guard.iac_wx == SLM_IAC_ERROR) {
> + rc = -1;
> + level->iac_level = SLM_IAC_UNTRUSTED;
> + }
> + break;
> + case 5:
> + level->guard.unlimited = set_bounds(token);
> + level->guard.sac_w = set_sac(token);
> + if (level->guard.sac_w == SLM_SAC_ERROR) {
> + rc = -1;
> + level->sac_level = SLM_SAC_PUBLIC;
> + }
> + break;
> + case 6:
> + level->guard.sac_rx = set_sac(token);
> + if (level->guard.sac_rx == SLM_SAC_ERROR) {
> + rc = -1;
> + level->sac_level = SLM_SAC_PUBLIC;
> + }
> + break;
> + case 7:
> + level->guard.unlimited = set_bounds(token);
> + default:
> + break;
> + }
> + }
> + return rc;
> +}
> +
> +/*
> + * Possible return codes: INTEGRITY_PASS, INTEGRITY_FAIL, INTEGRITY_NOLABEL,
> + * or -EINVAL
> + */
> +static int slm_get_xattr(struct dentry *dentry,
> + struct slm_file_xattr *level, int *status)
> +{
> + int xattr_len;
> + char *xattr_value = NULL;
> + int rc, error = -EINVAL;
> +
> + rc = integrity_verify_metadata(dentry, XATTR_NAME,
> + &xattr_value, &xattr_len, status);
> + if (rc < 0) {
> + if (rc != -EOPNOTSUPP) {
> + printk(KERN_INFO
> + "%s integrity_verify_metadata failed "
> + "(rc: %d - status: %d)\n",
> + dentry->d_name.name, rc, *status);
> + }
> + return rc;
> + }
> +
> + if (xattr_value) {
> + memset(level, 0, sizeof(struct slm_file_xattr));
> + error = slm_parse_xattr(xattr_value, xattr_len, level);
> + kfree(xattr_value);
> + }
> +
> + if (level->iac_level != SLM_IAC_UNTRUSTED) {
> + rc = integrity_verify_data(dentry, status);
> + if ((rc < 0) || (*status != INTEGRITY_PASS)) {
> + printk(KERN_INFO "%s integrity_verify_data failed "
> + " (rc: %d status: %d)\n", dentry->d_name.name,
> + rc, *status);
> + return rc;
> + }
> + }
> +
> + if (error < 0)
> + return -EINVAL;
> + return rc;
> +}

slm_get_xattr() seems remarkably subtle given its name: *status can be
updated at two points in the function, a positive 'rc' from
integrity_verify_data() is left to return at the end, but negative 'rc'
values (that aren't -EOPNOTSUPP) get returned immediately, and if an
error variable is negative, a specific value is returned..

> +/* Caller responsible for necessary locking */
> +static inline void set_level(struct slm_file_xattr *level,
> + enum slm_iac_level iac, enum slm_sac_level sac)
> +{
> + level->iac_level = iac;
> + level->sac_level = sac;
> +}
> +static inline void set_level_exempt(struct slm_file_xattr *level)
> +{
> + set_level(level, SLM_IAC_EXEMPT, SLM_SAC_EXEMPT);
> +}
> +
> +static inline void set_level_untrusted(struct slm_file_xattr *level)
> +{
> + set_level(level, SLM_IAC_UNTRUSTED, SLM_SAC_PUBLIC);
> +}
> +
> +static inline void set_level_tsec_write(struct slm_file_xattr *level,
> + struct slm_tsec_data *tsec)
> +{
> + set_level(level, tsec->iac_wx, tsec->sac_w);
> +}
> +
> +static inline void set_level_tsec_read(struct slm_file_xattr *level,
> + struct slm_tsec_data *tsec)
> +{
> + set_level(level, tsec->iac_r, tsec->sac_rx);
> +}
> +
> +static void get_sock_level(struct dentry *dentry, struct slm_file_xattr *level)
> +{
> + struct slm_tsec_data *cur_tsec = current->security;
> + int rc, status = 0;
> +
> + rc = slm_get_xattr(dentry, level, &status);
> + if (rc == -EOPNOTSUPP)
> + set_level_exempt(level);
> + else
> + set_level_tsec_read(level, cur_tsec);
> +}
> +
> +static void get_level(struct dentry *dentry, struct slm_file_xattr *level)
> +{
> + int rc, status = 0;
> +
> + rc = slm_get_xattr(dentry, level, &status);
> + if (rc < 0) {
> + switch (rc) {
> + case -EOPNOTSUPP:
> + set_level_exempt(level);
> + break;
> + case -EINVAL: /* improperly formatted */
> + default:
> + set_level_untrusted(level);
> + break;
> + }
> + } else {
> + switch(status) {
> + case INTEGRITY_FAIL:
> + case INTEGRITY_NOLABEL:
> + set_level_untrusted(level);
> + break;
> + }
> + }
> +}

The complicated set of decision making in get_level() (which sets
levels, heh) might be simplified if slm_get_xattr() internals were
less complicated.

> +static struct slm_isec_data *slm_alloc_security(gfp_t flags)
> +{
> + struct slm_isec_data *isec;
> +
> + isec = kzalloc(sizeof(struct slm_isec_data), flags);
> + if (!isec)
> + return NULL;
> +
> + spin_lock_init(&isec->lock);
> + return isec;
> +}
> +
> +/*
> + * Exempt objects without extended attribute support
> + * for fastpath. Others will be handled generically
> + * by the other functions.
> + */
> +static int is_exempt_fastpath(struct inode *inode)
> +{
> + if ((inode->i_sb->s_magic == PROC_SUPER_MAGIC)
> + || S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
> + return 1;
> + return 0;
> +}
> +
> +/*
> + * All directories with xattr support should be labeled, but just in case
> + * recursively traverse path (dentry->parent) until level info is found.
> + */
> +static void slm_get_level(struct dentry *dentry, struct slm_file_xattr *level)
> +{
> + struct inode *inode = dentry->d_inode;
> + struct slm_isec_data *isec = inode->i_security;
> +
> + if (is_isec_defined(isec)) {
> + memcpy(level, &isec->level, sizeof(struct slm_file_xattr));
> + return;
> + }
> +
> + memset(level, 0, sizeof(struct slm_file_xattr));
> + if (is_exempt_fastpath(inode))
> + set_level_exempt(level);
> + else if (S_ISSOCK(inode->i_mode))
> + get_sock_level(dentry, level);
> + else
> + get_level(dentry, level);
> +
> + spin_lock(&isec->lock);
> + memcpy(&isec->level, level, sizeof(struct slm_file_xattr));
> + spin_unlock(&isec->lock);
> +}

No lock is required to read from isec->level, but a lock is required to
write to it. Does this make sense?

> +/*
> + * new tsk->security inherits from current->security
> + */
> +static struct slm_tsec_data *slm_init_task(struct task_struct *tsk, gfp_t flags)
> +{
> + struct slm_tsec_data *tsec, *cur_tsec = current->security;
> +
> + tsec = kzalloc(sizeof(struct slm_tsec_data), flags);
> + if (!tsec)
> + return NULL;
> + tsec->lock = SPIN_LOCK_UNLOCKED;
> + if (!cur_tsec) {
> + tsec->iac_r = SLM_IAC_HIGHEST - 1;
> + tsec->iac_wx = SLM_IAC_HIGHEST - 1;
> + tsec->sac_w = SLM_SAC_NOTDEFINED + 1;
> + tsec->sac_rx = SLM_SAC_NOTDEFINED + 1;
> + } else
> + memcpy(tsec, cur_tsec, sizeof(struct slm_tsec_data));
> +
> + return tsec;
> +}
> +
> +static int is_iac_level_exempt(struct slm_file_xattr *level)
> +{
> + if (level->iac_level == SLM_IAC_EXEMPT)
> + return 1;
> + return 0;
> +}
> +
> +static int is_sac_level_exempt(struct slm_file_xattr *level)
> +{
> + if (level->sac_level == SLM_SAC_EXEMPT)
> + return 1;
> + return 0;
> +}
> +
> +static int is_iac_less_than_or_exempt(struct slm_file_xattr *level,
> + enum slm_iac_level iac)
> +{
> + if (iac <= level->iac_level)
> + return 1;
> + return is_iac_level_exempt(level);
> +}
> +
> +static int is_iac_greater_than_or_exempt(struct slm_file_xattr *level,
> + enum slm_iac_level iac)
> +{
> + if (iac >= level->iac_level)
> + return 1;
> + return is_iac_level_exempt(level);
> +}
> +
> +static int is_sac_less_than_or_exempt(struct slm_file_xattr *level,
> + enum slm_sac_level sac)
> +{
> + if (sac <= level->sac_level)
> + return 1;
> + return is_sac_level_exempt(level);
> +}
> +
> +static int is_sac_greater_than_or_exempt(struct slm_file_xattr *level,
> + enum slm_sac_level sac)
> +{
> + if (sac >= level->sac_level)
> + return 1;
> + return is_sac_level_exempt(level);
> +}
> +
> +/*
> + * enforce: IRAC(process) <= IAC(object)
> + * Permit process to read file of equal or greater integrity
> + * otherwise, demote the process.
> + */
> +static void enforce_integrity_read(struct slm_file_xattr *level)
> +{
> + struct slm_tsec_data *cur_tsec = current->security;
> +
> + spin_lock(&cur_tsec->lock);
> + if (!is_iac_less_than_or_exempt(level, cur_tsec->iac_r)) {
> + /* Reading lower integrity, demote process */
> + /* Even in the case of a integrity guard process. */
> + cur_tsec->iac_r = level->iac_level;
> + cur_tsec->iac_wx = level->iac_level;
> + spin_unlock(&cur_tsec->lock);
> +
> + revoke_permissions(level);
> + return;
> + }
> + spin_unlock(&cur_tsec->lock);
> +}
> +
> +/*
> + * enforce: SRXAC(process) >= SAC(object)
> + * Permit process to read file of equal or lesser secrecy;
> + * otherwise, promote the process.
> + */
> +static void enforce_secrecy_read(struct slm_file_xattr *level)
> +{
> + struct slm_tsec_data *cur_tsec = current->security;
> +
> + spin_lock(&cur_tsec->lock);
> + if (!is_sac_greater_than_or_exempt(level, cur_tsec->sac_rx)) {
> + /* Reading higher secrecy, promote process */
> + /* Even in the case of a secrecy guard process. */
> + cur_tsec->sac_rx = level->sac_level;
> + cur_tsec->sac_w = level->sac_level;
> + spin_unlock(&cur_tsec->lock);
> +
> + /* Working item: revoke write permission to lower secrecy
> + * files. Prototyped but insufficiently tested for release
> + * current code will only allow authorized user to release
> + * sensitive data */
> + return;
> + }
> + spin_unlock(&cur_tsec->lock);
> +}
> +
> +static void do_task_may_read(struct slm_file_xattr *level)
> +{
> + enforce_integrity_read(level);
> + enforce_secrecy_read(level);
> +}
> +
> +/*
> + * enforce: IWXAC(process) >= IAC(object)
> + * Permit process to write a file of equal or lesser integrity.
> + */
> +static int enforce_integrity_write(struct slm_file_xattr *level)
> +{
> + struct slm_tsec_data *cur_tsec = current->security;
> + int rc = 0;
> +
> + spin_lock(&cur_tsec->lock);
> + if (!(is_iac_greater_than_or_exempt(level, cur_tsec->iac_wx)
> + || (level->iac_level == SLM_IAC_NOTDEFINED)))
> + /* can't write higher integrity */
> + rc = -EACCES;
> + spin_unlock(&cur_tsec->lock);
> + return rc;
> +}
> +
> +/*
> + * enforce: SWAC(process) <= SAC(process)
> + * Permit process to write a file of equal or greater secrecy
> + */
> +static int enforce_secrecy_write(struct slm_file_xattr *level)
> +{
> + struct slm_tsec_data *cur_tsec = current->security;
> + int rc = 0;
> +
> + spin_lock(&cur_tsec->lock);
> + if (!is_sac_less_than_or_exempt(level, cur_tsec->sac_w))
> + /* can't write lower secrecy */
> + rc = -EACCES;
> + spin_unlock(&cur_tsec->lock);
> + return rc;
> +}
> +
> +static int do_task_may_write(struct slm_file_xattr *level)
> +{
> + int rc;
> +
> + rc = enforce_integrity_write(level);
> + if (rc < 0)
> + return rc;
> +
> + return enforce_secrecy_write(level);
> +}
> +
> +static int slm_set_taskperm(int mask, struct slm_file_xattr *level)
> +{
> + int rc = 0;
> +
> + if (mask & MAY_READ)
> + do_task_may_read(level);
> + if ((mask & MAY_WRITE) || (mask & MAY_APPEND))
> + rc = do_task_may_write(level);
> +
> + return rc;
> +}
> +
> +/*
> + * file changes invalidate isec
> + */
> +static int slm_file_permission(struct file *file, int mask)
> +{
> + struct slm_isec_data *isec = file->f_dentry->d_inode->i_security;
> +
> + if (((mask & MAY_WRITE) || (mask & MAY_APPEND)) && isec) {
> + spin_lock(&isec->lock);
> + isec->level.iac_level = SLM_IAC_NOTDEFINED;
> + spin_unlock(&isec->lock);
> + }
> + return 0;
> +}
> +
> +static int is_untrusted_blk_access(struct inode *inode)
> +{
> + struct slm_tsec_data *cur_tsec = current->security;
> + int rc = 0;
> +
> + spin_lock(&cur_tsec->lock);
> + if (cur_tsec && (cur_tsec->iac_wx == SLM_IAC_UNTRUSTED)
> + && S_ISBLK(inode->i_mode))
> + rc = 1;
> + spin_unlock(&cur_tsec->lock);
> + return rc;
> +}
> +
> +/*
> + * Premise:
> + * Can't write or execute higher integrity, can't read lower integrity
> + * Can't read or execute higher secrecy, can't write lower secrecy
> + */
> +static int slm_inode_permission(struct inode *inode, int mask,
> + struct nameidata *nd)
> +{
> + struct dentry *dentry = NULL;
> + struct slm_file_xattr level;
> +
> + if (S_ISDIR(inode->i_mode) && (mask & MAY_WRITE))
> + return 0;
> +
> + dentry = (!nd || !nd->dentry) ? d_find_alias(inode) : nd->dentry;
> + if (!dentry)
> + return 0;
> +
> + if (is_untrusted_blk_access(inode))
> + return -EPERM;
> +
> + slm_get_level(dentry, &level);
> +
> + /* measure all SYSTEM level integrity objects */
> + if (level.iac_level == SLM_IAC_SYSTEM)
> + integrity_measure(dentry, NULL, mask);
> +
> + return slm_set_taskperm(mask, &level);
> +}
> +
> +/*
> + * This hook is called holding the inode mutex.
> + */
> +static int slm_inode_unlink(struct inode *dir, struct dentry *dentry)
> +{
> + struct slm_file_xattr level;
> +
> + if (!dentry || !dentry->d_name.name)
> + return 0;
> +
> + slm_get_level(dentry, &level);
> + return slm_set_taskperm(MAY_WRITE, &level);
> +}
> +
> +static void slm_inode_free_security(struct inode *inode)
> +{
> + struct slm_isec_data *isec = inode->i_security;
> +
> + inode->i_security = NULL;
> + kfree(isec);
> +}
> +
> +/*
> + * Check integrity permission to create a regular file.
> + */
> +static int slm_inode_create(struct inode *parent_dir,
> + struct dentry *dentry, int mask)
> +{
> + struct slm_tsec_data *cur_tsec = current->security;
> + struct slm_isec_data *parent_isec = parent_dir->i_security;
> + struct slm_file_xattr *parent_level = &parent_isec->level;
> + int rc = 0;
> +
> + /*
> + * enforce: IWXAC(process) >= IAC(object)
> + * Permit process to write a file of equal or lesser integrity.
> + */
> + spin_lock(&cur_tsec->lock);
> + spin_lock(&parent_isec->lock);
> + if (!is_iac_greater_than_or_exempt(parent_level, cur_tsec->iac_wx))
> + rc = -EPERM;
> + spin_unlock(&parent_isec->lock);
> + spin_unlock(&cur_tsec->lock);
> + return rc;
> +}
> +
> +#define MAX_XATTR_SIZE 76
> +
> +static int slm_set_xattr(struct slm_file_xattr *level,
> + char **name, void **value, size_t * value_len)
> +{
> + int len;
> + int xattr_len;
> + char buf[MAX_XATTR_SIZE];
> + char *bufp = buf;
> + char *xattr_val = buf;
> + char *xattr_name;
> +
> + if (!level)
> + return 0;
> +
> + memset(buf, 0, sizeof(buf));
> +
> + if (is_iac_level_exempt(level)) {
> + memcpy(bufp, EXEMPT_STR, strlen(EXEMPT_STR));
> + bufp += strlen(EXEMPT_STR);
> + } else {
> + len = strlen(slm_iac_str[level->iac_level]);
> + memcpy(bufp, slm_iac_str[level->iac_level], len);
> + bufp += len;
> + }
> + *bufp++ = ' ';
> + if (is_sac_level_exempt(level)) {
> + memcpy(bufp, EXEMPT_STR, strlen(EXEMPT_STR));
> + bufp += strlen(EXEMPT_STR);
> + } else {
> + len = strlen(slm_sac_str[level->sac_level]);
> + memcpy(bufp, slm_sac_str[level->sac_level], len);
> + bufp += len;
> + }
> + xattr_len = bufp - buf;
> +
> + /* point after 'security.' */
> + xattr_name = strchr(XATTR_NAME, '.');
> + if (xattr_name)
> + *name = kstrdup(xattr_name + 1, GFP_KERNEL);
> + *value = kmalloc(xattr_len + 1, GFP_KERNEL);
> + if (!*value) {
> + kfree(name);
> + return -ENOMEM;
> + }
> + memcpy(*value, xattr_val, xattr_len);
> + *value_len = xattr_len;
> + return 0;
> +}
> +
> +/* Create the security.slim.level extended attribute */
> +static int slm_inode_init_security(struct inode *inode, struct inode *dir,
> + char **name, void **value, size_t * len)
> +{
> + struct slm_isec_data *isec = inode->i_security, *parent_isec =
> + dir->i_security;
> + struct slm_tsec_data *cur_tsec = current->security;
> + struct slm_file_xattr level;
> + struct xattr_data *data;
> + int rc;
> +
> + if (!name || !value || !len)
> + return 0;
> +
> + memset(&level, 0, sizeof(struct slm_file_xattr));
> +
> + if (is_isec_defined(parent_isec)) {
> + spin_lock(&parent_isec->lock);
> + memcpy(&level, &parent_isec->level,
> + sizeof(struct slm_file_xattr));
> + spin_unlock(&parent_isec->lock);
> + }
> +
> + spin_lock(&cur_tsec->lock);
> + /* low integrity process wrote into a higher level directory */
> + if (cur_tsec->iac_wx < level.iac_level)
> + set_level_tsec_write(&level, cur_tsec);
> + /* if directory is exempt, then use process level */
> + if (is_iac_level_exempt(&level)) {
> + /* When a guard process creates a directory */
> + if (S_ISDIR(inode->i_mode)
> + && (cur_tsec->iac_wx != cur_tsec->iac_r))
> + set_level_exempt(&level);
> + else
> + set_level_tsec_write(&level, cur_tsec);
> + }
> +
> + /* if a guard process creates a UNIX socket, then EXEMPT it */
> + if (S_ISSOCK(inode->i_mode)
> + && (cur_tsec->iac_wx != cur_tsec->iac_r))
> + set_level_exempt(&level);
> + spin_unlock(&cur_tsec->lock);
> +
> + spin_lock(&isec->lock);
> + memcpy(&isec->level, &level, sizeof(struct slm_file_xattr));
> + spin_unlock(&isec->lock);
> +
> + data = kmalloc(sizeof(struct xattr_data), GFP_KERNEL);
> + if (!data)
> + return -ENOMEM;
> +
> + /* set levels, based on parent */
> + rc = slm_set_xattr(&level, &data->name, &data->value, &data->len);
> + if (rc < 0)
> + return rc;

if the slm_set_xattr() fails, should data be freed?
>
> + *name = data->name;
> + *value = data->value;
> + *len = data->len;
> + return 0;
> +}
> +
> +static void slm_d_instantiate(struct dentry *dentry, struct inode *inode)
> +{
> + struct slm_isec_data *isec;
> + struct slm_file_xattr level;
> +
> + if (!inode)
> + return;
> +
> + isec = inode->i_security;
> + if (is_exempt_fastpath(inode)) {
> + memset(&level, 0, sizeof(struct slm_file_xattr));
> + set_level_exempt(&level);
> + } else if (S_ISSOCK(inode->i_mode))
> + memset(&level, 0, sizeof(struct slm_file_xattr));
> + else
> + get_level(dentry, &level);
> +
> + spin_lock(&isec->lock);
> + memcpy(&isec->level, &level, sizeof(struct slm_file_xattr));
> + spin_unlock(&isec->lock);
> +}
> +
> +/*
> + * Check permissions to create a new directory in the existing directory
> + * associated with inode structure @dir.
> + */
> +static int slm_inode_mkdir(struct inode *parent_dir,
> + struct dentry *dentry, int mask)
> +{
> + struct slm_tsec_data *cur_tsec = current->security;
> + struct slm_isec_data *parent_isec = parent_dir->i_security;
> + struct slm_file_xattr *parent_level = &parent_isec->level;
> + int rc = 0;
> +
> + spin_lock(&cur_tsec->lock);
> + spin_lock(&parent_isec->lock);
> + if (cur_tsec->iac_wx < parent_level->iac_level
> + && parent_level->iac_level == SLM_IAC_SYSTEM)
> + rc = -EACCES;
> + spin_unlock(&parent_isec->lock);
> + spin_unlock(&cur_tsec->lock);
> + return rc;
> +}
> +
> +static int slm_inode_rename(struct inode *old_dir,
> + struct dentry *old_dentry,
> + struct inode *new_dir, struct dentry *new_dentry)
> +{
> + struct slm_file_xattr old_level, parent_level;
> + struct dentry *parent_dentry;
> +
> + if (old_dir == new_dir)
> + return 0;
> +
> + slm_get_level(old_dentry, &old_level);
> +
> + parent_dentry = dget_parent(new_dentry);
> + slm_get_level(parent_dentry, &parent_level);
> + dput(parent_dentry);
> +
> + if (is_lower_integrity(&old_level, &parent_level))
> + return -EPERM;
> + return 0;
> +}
> +
> +/*
> + * Limit the integrity value of an object to be no greater than that
> + * of the current process. This is especially important for objects
> + * being promoted.
> +*/
> +int slm_inode_setxattr(struct dentry *dentry, char *name, void *value,
> + size_t size, int flags)
> +{
> + struct slm_tsec_data *cur_tsec = current->security;
> + char *data = value;
> + enum slm_iac_level iac;
> +
> + if (strncmp(name, XATTR_NAME, strlen(XATTR_NAME)) != 0)
> + return 0;
> +
> + if (!value)
> + return -EINVAL;
> +
> + spin_lock(&cur_tsec->lock);
> + iac = cur_tsec->iac_wx;
> + spin_unlock(&cur_tsec->lock);
> +
> + switch (iac) {
> + case SLM_IAC_USER:
> + if ((strncmp(data, USER_STR, strlen(USER_STR)) != 0) &&
> + (strncmp(data, UNTRUSTED_STR, strlen(UNTRUSTED_STR)) != 0))
> + return -EPERM;
> + break;
> + case SLM_IAC_SYSTEM:
> + if ((strncmp(data, SYSTEM_STR, strlen(SYSTEM_STR)) != 0) &&
> + (strncmp(data, USER_STR, strlen(USER_STR)) != 0) &&
> + (strncmp(data, UNTRUSTED_STR, strlen(UNTRUSTED_STR)) != 0)
> + && (strncmp(data, EXEMPT_STR, strlen(EXEMPT_STR)) != 0))
> + return -EPERM;
> + break;
> + default:
> + return -EPERM;
> + }
> + return 0;
> +}
> +
> +/*
> + * SLIM extended attribute was modified, update isec.
> + */
> +static void slm_inode_post_setxattr(struct dentry *dentry, char *name,
> + void *value, size_t size, int flags)
> +{
> + struct slm_isec_data *slm_isec;
> + struct slm_file_xattr level;
> + int rc, status = 0;
> +
> + if (strncmp(name, XATTR_NAME, strlen(XATTR_NAME)) != 0)
> + return;
> +
> + rc = slm_get_xattr(dentry, &level, &status);
> + slm_isec = dentry->d_inode->i_security;
> + spin_lock(&slm_isec->lock);
> + memcpy(&slm_isec->level, &level, sizeof(struct slm_file_xattr));
> + spin_unlock(&slm_isec->lock);
> +}
> +
> +static int slm_inode_removexattr(struct dentry *dentry, char *name)
> +{
> + struct slm_isec_data *isec = dentry->d_inode->i_security;
> + struct slm_tsec_data *tsec = current->security;
> + enum slm_iac_level iac;
> + int rc = 0;
> +
> + if (strncmp(name, XATTR_NAME, strlen(XATTR_NAME)) != 0)
> + return 0;
> +
> + if (isec) {
> + spin_lock(&tsec->lock);
> + iac = tsec->iac_wx;
> + spin_unlock(&tsec->lock);
> +
> + spin_lock(&isec->lock);
> + switch(iac) {
> + case SLM_IAC_SYSTEM:
> + isec->level.iac_level = SLM_IAC_NOTDEFINED;
> + break;
> + case SLM_IAC_USER:
> + if (isec->level.iac_level < SLM_IAC_NOTDEFINED ||
> + isec->level.iac_level > SLM_IAC_USER)
> + rc = -EPERM;
> + else
> + isec->level.iac_level = SLM_IAC_NOTDEFINED;
> + break;
> + default:
> + rc = -EPERM;
> + }
> + spin_unlock(&isec->lock);
> + }
> + return rc;
> +}
> +
> +static int slm_inode_alloc_security(struct inode *inode)
> +{
> + struct slm_isec_data *isec = slm_alloc_security(GFP_KERNEL);
> + if (!isec)
> + return -ENOMEM;
> +
> + inode->i_security = isec;
> + return 0;
> +}
> +
> +/*
> + * Opening a socket demotes the integrity of a process to untrusted.
> + */
> +int slm_socket_create(int family, int type, int protocol, int kern)
> +{
> + struct slm_tsec_data *cur_tsec = current->security;
> + struct slm_file_xattr level;
> +
> + /* demoting only internet sockets */
> + if ((family != AF_UNIX) && (family != AF_NETLINK)) {

Comment doesn't really match the test..

> + spin_lock(&cur_tsec->lock);
> + if (cur_tsec->iac_r > SLM_IAC_UNTRUSTED) {
> + cur_tsec->iac_r = SLM_IAC_UNTRUSTED;
> + cur_tsec->iac_wx = SLM_IAC_UNTRUSTED;
> + spin_unlock(&cur_tsec->lock);
> +
> + memset(&level, 0, sizeof(struct slm_file_xattr));
> + level.iac_level = SLM_IAC_UNTRUSTED;
> +
> + revoke_permissions(&level);
> + return 0;
> + }
> + spin_unlock(&cur_tsec->lock);
> + }
> + return 0;
> +}
> +
> +/*
> + * Didn't have the family type previously, so update the inode security now.
> + */
> +static void slm_socket_post_create(struct socket *sock, int family,
> + int type, int protocol, int kern)
> +{
> + struct slm_tsec_data *cur_tsec = current->security;
> + struct inode *inode = SOCK_INODE(sock);
> + struct slm_isec_data *slm_isec = inode->i_security;
> +
> + spin_lock(&slm_isec->lock);
> + if (family == PF_UNIX) {
> + if (cur_tsec->iac_wx != cur_tsec->iac_r) /* guard process */
> + set_level_exempt(&slm_isec->level);
> + else
> + set_level_tsec_write(&slm_isec->level, cur_tsec);
> + } else
> + set_level_untrusted(&slm_isec->level);
> + spin_unlock(&slm_isec->lock);
> +}
> +
> +/*
> + * When a task gets allocated, it inherits the current IAC and SAC.
> + * Set the values and store them in p->security.
> + */
> +static int slm_task_alloc_security(struct task_struct *tsk)
> +{
> + struct slm_tsec_data *tsec = tsk->security;
> +
> + if (!tsec) {
> + tsec = slm_init_task(tsk, GFP_KERNEL);
> + if (!tsec)
> + return -ENOMEM;
> + }
> + tsk->security = tsec;
> + return 0;
> +}
> +
> +static void slm_task_free_security(struct task_struct *tsk)
> +{
> + struct slm_tsec_data *tsec;
> +
> + tsec = tsk->security;
> + tsk->security = NULL;
> + kfree(tsec);
> +}
> +
> +/* init_task is an integrity guard */
> +static int slm_task_init_alloc_security(struct task_struct *tsk)
> +{
> + struct slm_tsec_data *tsec = kzalloc(sizeof(struct slm_tsec_data), GFP_KERNEL);
> +
> + if (!tsec)
> + return -ENOMEM;
> +
> + tsec->lock = SPIN_LOCK_UNLOCKED;
> +
> + tsec->iac_r = SLM_IAC_UNTRUSTED;
> + tsec->iac_wx = SLM_IAC_SYSTEM;
> + tsec->sac_w = SLM_SAC_PUBLIC;
> + tsec->sac_rx = SLM_SAC_PUBLIC;
> +
> + tsec->unlimited = 1;
> +
> + tsk->security = tsec;
> + return 0;
> +}
> +
> +static int slm_task_post_setuid(uid_t old_ruid, uid_t old_euid,
> + uid_t old_suid, int flags)
> +{
> + struct slm_tsec_data *cur_tsec = current->security;
> +
> + if (cur_tsec && flags == LSM_SETID_ID) {
> + /*set process to USER level integrity for everything but root */
> + spin_lock(&cur_tsec->lock);
> + if ((cur_tsec->iac_r == cur_tsec->iac_wx)
> + && (cur_tsec->iac_r == SLM_IAC_UNTRUSTED));
> + else if (current->suid != 0) {
> + cur_tsec->iac_r = SLM_IAC_USER;
> + cur_tsec->iac_wx = SLM_IAC_USER;
> + } else if ((current->uid == 0) && (old_ruid != 0)) {
> + cur_tsec->iac_r = SLM_IAC_SYSTEM;
> + cur_tsec->iac_wx = SLM_IAC_SYSTEM;
> + }
> + spin_unlock(&cur_tsec->lock);
> + }
> + return 0;
> +}
> +
> +static inline int slm_setprocattr(struct task_struct *tsk,
> + char *name, void *value, size_t size)
> +{
> + return -EACCES;
> +
> +}
> +
> +static inline int slm_getprocattr(struct task_struct *tsk,
> + char *name, void *value, size_t size)
> +{
> + struct slm_tsec_data *tsec = tsk->security;
> + size_t len = 0;
> +
> + if (is_kernel_thread(tsk))
> + len = snprintf(value, size, "KERNEL");
> + else {
> + spin_lock(&tsec->lock);
> + if (tsec->iac_wx != tsec->iac_r)
> + len = snprintf(value, size, "GUARD wx:%s r:%s",
> + slm_iac_str[tsec->iac_wx],
> + slm_iac_str[tsec->iac_r]);
> + else
> + len = snprintf(value, size, "%s",
> + slm_iac_str[tsec->iac_wx]);
> + spin_unlock(&tsec->lock);
> + }
> + return min(len, size);
> +}
> +
> +/*
> + * enforce: IWXAC(process) <= IAC(object)
> + * Permit process to execute file of equal or greater integrity
> + */
> +static void enforce_integrity_execute(struct linux_binprm *bprm,
> + struct slm_file_xattr *level,
> + struct slm_tsec_data *cur_tsec)
> +{
> + spin_lock(&cur_tsec->lock);
> + if (is_iac_less_than_or_exempt(level, cur_tsec->iac_wx))
> + /* Being a guard process is not inherited */
> + cur_tsec->iac_r = cur_tsec->iac_wx;
> + else {
> + cur_tsec->iac_r = level->iac_level;
> + cur_tsec->iac_wx = level->iac_level;
> + spin_unlock(&cur_tsec->lock);
> +
> + revoke_permissions(level);
> + return;
> + }
> + spin_unlock(&cur_tsec->lock);
> +}
> +
> +static void enforce_guard_integrity_execute(struct linux_binprm *bprm,
> + struct slm_file_xattr *level,
> + struct slm_tsec_data *cur_tsec)
> +{
> + if ((strcmp(bprm->filename, bprm->interp) != 0)
> + && (level->guard.unlimited))
> + level->guard.unlimited = 0;
> +
> + spin_lock(&cur_tsec->lock);
> + if (level->guard.unlimited) {
> + cur_tsec->iac_r = level->guard.iac_r;
> + cur_tsec->iac_wx = level->guard.iac_wx;
> + } else {
> + if (cur_tsec->iac_r > level->guard.iac_r)
> + cur_tsec->iac_r = level->guard.iac_r;
> + if (cur_tsec->iac_wx > level->guard.iac_wx)
> + cur_tsec->iac_wx = level->guard.iac_wx;
> + }
> + spin_unlock(&cur_tsec->lock);
> +}
> +
> +/*
> + * enforce: SRXAC(process) >= SAC(object)
> + * Permit process to execute file of equal or lesser secrecy
> + */
> +static void enforce_secrecy_execute(struct linux_binprm *bprm,
> + struct slm_file_xattr *level,
> + struct slm_tsec_data *cur_tsec)
> +{
> + spin_lock(&cur_tsec->lock);
> + if (is_sac_greater_than_or_exempt(level, cur_tsec->sac_rx))
> + /* Being a guard process is not inherited */
> + cur_tsec->sac_w = cur_tsec->sac_rx;
> + else {
> + cur_tsec->sac_rx = level->sac_level;
> + cur_tsec->sac_w = level->sac_level;
> +
> + /* Working item: revoke write permission to lower secrecy
> + * files. Prototyped but insufficiently tested for release
> + * current code will only allow authorized user to release
> + * sensitive data */
> + }
> + spin_unlock(&cur_tsec->lock);
> +}
> +
> +static void enforce_guard_secrecy_execute(struct linux_binprm *bprm,
> + struct slm_file_xattr *level,
> + struct slm_tsec_data *cur_tsec)
> +{
> + /*
> + * set low write secrecy range,
> + * not less than current value, prevent leaking data
> + */
> + spin_lock(&cur_tsec->lock);
> + cur_tsec->sac_w = max(cur_tsec->sac_w, level->guard.sac_w);
> + /* limit secrecy range, never demote secrecy */
> + cur_tsec->sac_rx = max(cur_tsec->sac_rx, level->guard.sac_rx);
> + spin_unlock(&cur_tsec->lock);
> +}
> +
> +/*
> + * Enforce process integrity & secrecy levels.
> + * - update integrity process level of integrity guard program
> + * - update secrecy process level of secrecy guard program
> + */
> +static int slm_bprm_check_security(struct linux_binprm *bprm)
> +{
> + struct dentry *dentry;
> + struct slm_tsec_data *cur_tsec = current->security;
> + struct slm_file_xattr level;
> + int rc, status;
> +
> + /* Special case interpreters */
> + spin_lock(&cur_tsec->lock);
> + if (strcmp(bprm->filename, bprm->interp) != 0) {
> + if (!cur_tsec->script_dentry) {
> + spin_unlock(&cur_tsec->lock);
> + return 0;
> + } else
> + dentry = cur_tsec->script_dentry;
> + } else {
> + dentry = bprm->file->f_dentry;
> + cur_tsec->script_dentry = dentry;
> + }
> + spin_unlock(&cur_tsec->lock);
> +
> + slm_get_level(dentry, &level);
> +
> + /* slm_inode_permission measured all SYSTEM level integrity objects */
> + if (level.iac_level != SLM_IAC_SYSTEM)
> + integrity_measure(dentry, bprm->filename, MAY_EXEC);
> +
> + /* Possible return codes: PERMIT, DENY, NOLABEL */
> + rc = integrity_verify_data(dentry, &status);
> + if (rc < 0)
> + return rc;
> +
> + switch(status) {
> + case INTEGRITY_FAIL:
> + if (!is_kernel_thread(current))
> + return -EACCES;
> + case INTEGRITY_NOLABEL:
> + level.iac_level = SLM_IAC_UNTRUSTED;
> + level.sac_level = SLM_SAC_PUBLIC;
> + }
> +
> + enforce_integrity_execute(bprm, &level, cur_tsec);
> + if (is_guard_integrity(&level))
> + enforce_guard_integrity_execute(bprm, &level, cur_tsec);
> +
> + enforce_secrecy_execute(bprm, &level, cur_tsec);
> + if (is_guard_secrecy(&level))
> + enforce_guard_secrecy_execute(bprm, &level, cur_tsec);
> +
> + return 0;
> +}
> +
> +static int slm_inode_setattr(struct dentry *dentry, struct iattr *iattr)
> +{
> + struct slm_tsec_data *cur_tsec = current->security;
> + struct slm_file_xattr level;
> + int rc = 0;
> +
> + slm_get_level(dentry, &level);
> + spin_lock(&cur_tsec->lock);
> + if (cur_tsec->iac_wx < level.iac_level)
> + rc = -EACCES;
> + spin_unlock(&cur_tsec->lock);
> + return rc;
> +}
> +
> +static inline int slm_capable(struct task_struct *tsk, int cap)
> +{
> + struct slm_tsec_data *tsec = tsk->security;
> + int rc = 0;
> +
> + /* Derived from include/linux/sched.h:capable. */
> + if (cap_raised(tsk->cap_effective, cap)) {
> + spin_lock(&tsec->lock);
> + if (tsec->iac_wx == SLM_IAC_UNTRUSTED &&
> + cap == CAP_SYS_ADMIN)
> + rc = -EACCES;

Why is CAP_SYS_ADMIN handled specially?

> + spin_unlock(&tsec->lock);
> + return rc;
> + }
> + return -EPERM;
> +}
> +
> +static int slm_ptrace(struct task_struct *parent, struct task_struct *child)
> +{
> + struct slm_tsec_data *parent_tsec = parent->security,
> + *child_tsec = child->security;
> + int rc = 0;
> +
> + if (is_kernel_thread(parent) || is_kernel_thread(child))
> + return 0;

Why was this added?

> + spin_lock(&parent_tsec->lock);
> + if (parent_tsec->iac_wx < child_tsec->iac_wx)
> + rc = -EPERM;
> + spin_unlock(&parent_tsec->lock);
> + return rc;
> +}
> +
> +static int slm_shm_alloc_security(struct shmid_kernel *shp)
> +{
> + struct slm_tsec_data *cur_tsec = current->security;
> + struct kern_ipc_perm *perm = &shp->shm_perm;
> + struct slm_isec_data *isec = slm_alloc_security(GFP_KERNEL);
> +
> + if (!isec)
> + return -ENOMEM;
> +
> + spin_lock(&cur_tsec->lock);
> + if (cur_tsec->iac_wx != cur_tsec->iac_r) /* guard process */
> + set_level_exempt(&isec->level);
> + else
> + set_level_tsec_write(&isec->level, cur_tsec);
> + spin_unlock(&cur_tsec->lock);
> + perm->security = isec;
> +
> + return 0;
> +}
> +
> +static void slm_shm_free_security(struct shmid_kernel *shp)
> +{
> + struct kern_ipc_perm *perm = &shp->shm_perm;
> + struct slm_isec_data *isec = perm->security;
> +
> + perm->security = NULL;
> + kfree(isec);
> +}
> +
> +/*
> + * When shp exists called holding perm->lock
> + */
> +static int slm_shm_shmctl(struct shmid_kernel *shp, int cmd)
> +{
> + struct kern_ipc_perm *perm;
> + struct slm_isec_data *perm_isec;
> + struct file *file;
> + struct dentry *dentry;
> + struct inode *inode;
> + int rc;
> +
> + if (!shp)
> + return 0;
> +
> + perm = &shp->shm_perm;
> + perm_isec = perm->security;
> + file = shp->shm_file;
> + dentry = file->f_dentry;
> + inode = dentry->d_inode;
> +
> + spin_lock(&perm_isec->lock);
> + rc = slm_set_taskperm(MAY_READ | MAY_WRITE, &perm_isec->level);
> + spin_unlock(&perm_isec->lock);
> + return rc;
> +}
> +
> +/*
> + * Called holding perm->lock
> + */
> +static int slm_shm_shmat(struct shmid_kernel *shp,
> + char __user * shmaddr, int shmflg)
> +{
> + int mask = MAY_READ;
> + int rc;
> + struct kern_ipc_perm *perm = &shp->shm_perm;
> + struct file *file = shp->shm_file;
> + struct dentry *dentry = file->f_dentry;
> + struct inode *inode = dentry->d_inode;
> + struct slm_isec_data *perm_isec = perm->security,
> + *isec = inode->i_security;
> +
> + if (shmflg != SHM_RDONLY)
> + mask |= MAY_WRITE;
> +
> + spin_lock(&perm_isec->lock);
> + rc = slm_set_taskperm(mask, &perm_isec->level);
> +
> + spin_lock(&isec->lock);
> + memcpy(&isec->level, &perm_isec->level, sizeof(struct slm_file_xattr));
> + spin_unlock(&perm_isec->lock);
> + spin_unlock(&isec->lock);
> +
> + return rc;
> +}
> +
> +static struct security_operations slm_security_ops = {
> + .bprm_check_security = slm_bprm_check_security,
> + .file_permission = slm_file_permission,
> + .inode_permission = slm_inode_permission,
> + .inode_unlink = slm_inode_unlink,
> + .inode_create = slm_inode_create,
> + .inode_mkdir = slm_inode_mkdir,
> + .inode_rename = slm_inode_rename,
> + .inode_setattr = slm_inode_setattr,
> + .inode_setxattr = slm_inode_setxattr,
> + .inode_post_setxattr = slm_inode_post_setxattr,
> + .inode_removexattr = slm_inode_removexattr,
> + .inode_alloc_security = slm_inode_alloc_security,
> + .inode_free_security = slm_inode_free_security,
> + .inode_init_security = slm_inode_init_security,
> + .socket_create = slm_socket_create,
> + .socket_post_create = slm_socket_post_create,
> + .task_alloc_security = slm_task_alloc_security,
> + .task_free_security = slm_task_free_security,
> + .task_init_alloc_security = slm_task_init_alloc_security,
> + .task_post_setuid = slm_task_post_setuid,
> + .capable = slm_capable,
> + .ptrace = slm_ptrace,
> + .shm_alloc_security = slm_shm_alloc_security,
> + .shm_free_security = slm_shm_free_security,
> + .shm_shmat = slm_shm_shmat,
> + .shm_shmctl = slm_shm_shmctl,
> + .getprocattr = slm_getprocattr,
> + .setprocattr = slm_setprocattr,
> + .d_instantiate = slm_d_instantiate
> +};
> +
> +static int __init init_slm(void)
> +{
> + current->security = slm_init_task(current, GFP_ATOMIC);
> + return register_security(&slm_security_ops);
> +}
> +security_initcall(init_slm);

Thanks
[unhandled content-type:application/pgp-signature]
\
 
 \ /
  Last update: 2006-08-18 03:19    [W:0.114 / U:0.412 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site