Sample SELinux hook function implementations

From: Stephen Smalley (sdsat_private)
Date: Thu May 03 2001 - 13:45:47 PDT

  • Next message: Chris Wright: "Re: Sample SELinux hook function implementations"

    I've implemented hook functions (for the current set of
    defined hooks) that manage the security fields for the 
    SELinux security attributes and that make the appropriate calls to the
    other kernel components of SELinux (the security server, the access vector
    cache, and the persistent label mapping).  I've attached the hook
    function implementation for reference.  Naturally, this code is
    still in a very early stage and undoubtedly has bugs.  
    
    I had to add a s_security field to struct super_block in
    include/linux/fs.h t for per-filesystem security information.
    Otherwise, I haven't made any changes to the existing LSM
    framework.
    
    I'll try to provide some observations and feedback tomorrow 
    based on my experience with implementing these hooks for SELinux.
    
    --
    Stephen D. Smalley, NAI Labs
    ssmalleyat_private
    
    
    
    
    
    /*
     *  NSA Security-Enhanced Linux plug
     *
     *  Copyright (C) 2001 Stephen Smalley <sdsat_private>
     * 
     *	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.
     */ 
    
    #include <linux/config.h>
    #include <linux/module.h>
    #include <linux/init.h>
    #include <linux/kernel.h>
    #include <linux/errno.h>
    #include <linux/security.h>
    #include <linux/capability.h>
    #include <linux/flask/avc.h>
    #include <linux/flask/psid.h>
    #include <linux/mm.h>
    #include <linux/slab.h>
    #include <linux/smp_lock.h>
    #include <linux/spinlock.h>
    #include <asm/uaccess.h>
    #include "selinux_plug.h"
    
    /* The security server must be initialized before
       any labeling or access decisions can be provided. */
    static int ss_precondition(void) 
    {
    	static int ss_initializing = 0, ss_initialized = 0;
    
    	if (ss_initialized) 
    		return 0; /* ready for service */
    
    	if (ss_initializing)
    		return -1; /* currently initializing */
    
    	if (!current->fs->rootmnt)
    		return -1; /* waiting for root file system */
    
    	ss_initializing = 1; /* start initialization */
    	security_init(); 
    	ss_initialized = 1;  /* done initialization */
    	return 0; /* ready for service */
    }
    
    /* The file system's label must be initialized prior to use.
       If the file system is persistent, then its persistent label mapping 
       must be initialized before labels for files in it can be obtained. */
    static int superblock_precondition(struct super_block *sb) 
    {
    	struct superblock_security_struct *sbsec = sb->s_security;
    	int rc;
    
    	if (ss_precondition())
    		return -1;
    
    	if (sbsec && sbsec->magic == SELINUX_MAGIC) {
    		if (sbsec->initialized) 
    			return 0;  /* ready for service */
    		else
    			return -1; /* currently initializing */
    	}
    
    	sbsec = kmalloc(sizeof(struct superblock_security_struct), GFP_KERNEL);
    	if (!sbsec)
    		return -1; 
    	memset(sbsec, 0, sizeof(struct superblock_security_struct));
    
    	sbsec->sid = SECINITSID_UNLABELED;
    	sbsec->magic = SELINUX_MAGIC;
    	sb->s_security = sbsec;
    	
    	if ((strcmp(sb->s_type->name, "ext2") == 0) ||
    	    (strcmp(sb->s_type->name, "ufs") == 0) ||
    	    (strcmp(sb->s_type->name, "reiserfs") == 0)) {
    		/* PSIDs only work for persistent file systems with
    		   persistent inode numbers. */
    		rc = psid_init(sb);
    		if (rc) {
    			printk("superblock_precondition: psid_init returned %d\n", -rc);
    			sb->s_security = NULL;
    			kfree(sbsec);
    			return rc;
    		}
    	} else if (strcmp(sb->s_type->name, "proc") == 0)
    		sbsec->sid = SECINITSID_PROC;
    	else if (strcmp(sb->s_type->name, "devpts") == 0)
    		sbsec->sid = SECINITSID_DEVPTS;
    	else if (strcmp(sb->s_type->name, "nfs") == 0)
    		sbsec->sid = SECINITSID_NFS;
    	else
    		/* Nothing to do here. */
    		;
    
    	sbsec->initialized = 1;
    
    	return 0;
    }
    
    /* The inode's security attributes must be initialized before first use. */
    static int inode_precondition(struct inode *inode) 
    {
            struct task_security_struct *tsec = current->security;
    	struct superblock_security_struct *sbsec = NULL; 
    	struct inode_security_struct *isec = inode->i_security;
    	int rc;
    
    	if (ss_precondition())
    		return -1;
    
    	if (isec && isec->magic == SELINUX_MAGIC) 
    		return 0;
    
    	if (inode->i_sb) {
    		rc = superblock_precondition(inode->i_sb);
    		if (rc)
    			return rc;
    		sbsec = inode->i_sb->s_security;
    	}
    
    	isec = kmalloc(sizeof(struct inode_security_struct), GFP_KERNEL);
    	if (!isec)
    		return -1; 
    	memset(isec, 0, sizeof(struct inode_security_struct));
    	isec->sid = SECINITSID_UNLABELED;
    	isec->sclass = file_mode_to_security_class(inode->i_mode);
    	isec->magic = SELINUX_MAGIC;
    	inode->i_security = isec;
    
    	if (sbsec) {
    		if (sbsec->uses_psids) {
    			rc = psid_to_sid(inode, &isec->sid);
    			if (rc) {
    				printk("inode_precondition:  psid_to_sid returned %d\n", -rc);
    				inode->i_security = NULL;
    				kfree(isec);
    			}
    		}
    		else if (sbsec->sid != SECINITSID_UNLABELED) {
    		        /* inherit from file system */
    			isec->sid = sbsec->sid;
    		} else if (tsec && tsec->magic == SELINUX_MAGIC) {
    			/* inherit from task */
    			isec->sid = tsec->sid;
    		}
    /* XXX:  Still need to provide equivalents for SELinux handling of
       special file systems like devpts and procfs. */
    	}
    
    	if (isec->sid == SECINITSID_UNLABELED)
    		printk("inode_precondition:  leaving unlabeled\n");
    
    	return 0;
    }
    
    /* The task's security attributes must be initialized before first use. */
    static int task_precondition(struct task_struct *task) 
    {
    	struct task_security_struct *tsec = task->security, *psec;
    	struct task_struct *parent = task->p_pptr;
    	struct vm_area_struct *vma = NULL;
    	struct inode *inode = NULL;
    	struct inode_security_struct *isec;
    	security_id_t newsid;
    	security_context_t context;
    	char *buffer = NULL, *path = NULL;
    	__u32 len;
    	int rc;
    
    	if (ss_precondition())
    		return -1;
    	     
    	if (tsec && tsec->magic == SELINUX_MAGIC)
    		return 0;
    
    	parent = task->p_pptr;
    	if (parent == task) {
    		tsec = kmalloc(sizeof(struct task_security_struct), GFP_KERNEL);
    		if (!tsec)
    			return -1; 
    		memset(tsec, 0, sizeof(struct task_security_struct));
    		tsec->osid = tsec->sid = SECINITSID_KERNEL;
    		tsec->magic = SELINUX_MAGIC;
    		task->security = tsec;
    		goto out;
    	}
    		
    	/* XXX:  Need to make this iterative rather than recursive. */
    	rc = task_precondition(parent);
    	if (rc)
    		return rc; 
    	psec = parent->security;
    
    	tsec = kmalloc(sizeof(struct task_security_struct), GFP_KERNEL);
    	if (!tsec)
    		return -1; 
    	memset(tsec, 0, sizeof(struct task_security_struct));
    
    	/* Default to the attributes of my parent. */
    	tsec->osid = psec->osid;
    	tsec->sid = psec->sid;
    	tsec->magic = SELINUX_MAGIC;
    	task->security = tsec;
    
    	/* Try to determine the executable. */
    	if (!task->mm) 
    		goto out;
    	for (vma = task->mm->mmap; vma; vma = vma->vm_next) {
    		if ((vma->vm_flags & VM_EXECUTABLE) && 
    		    vma->vm_file) {
    			break;
    		}
    	}
    	if (!vma) 
    		goto out;
    
    	/* Try to obtain the executable's security attributes. */
    	inode = vma->vm_file->f_dentry->d_inode;
    	rc = inode_precondition(inode);
    	if (rc) {
    		/* May be in the midst of initializing the PSID mapping. */
    		task->security = NULL;
    		kfree(tsec);
    		return rc;
    	}
    	isec = inode->i_security;
    
    	/* Compute my attributes based on the attributes of my
    	   parent and my executable. */
    	rc = security_transition_sid(psec->sid, 
    				     isec->sid,
    				     SECCLASS_PROCESS,
    				     &newsid);
    	if (rc) {
    		printk("task_precondition:  security_transition_sid returned %d\n", -rc);
    		goto out;
    	}
    
    	tsec->sid = newsid;
    
    
    out:
    	/* Typically, a task's attributes are initially assigned
    	   by task_alloc_security and changed upon program execution
    	   by bprm_compute_creds.  So task_precondition should
    	   only determine a task's attributes if the task
    	   was created prior to the initialization of this module.
    	   Show all such assignments until we are sure that they
    	   occur correctly both in the static case and the dynamically
    	   loaded case. */
    	
    	if (vma) {
    		buffer = (char*)__get_free_page(GFP_KERNEL);
    		if (buffer) {
    			path = d_path(vma->vm_file->f_dentry, 
    				      vma->vm_file->f_vfsmnt,
    				      buffer, PAGE_SIZE);
    		}
    	}
    
    	rc = security_sid_to_context(tsec->sid, &context, &len);
    	if (rc) {
    		printk("task_precondition:  assigning SID %d to pid %d exe=%s\n", tsec->sid, task->pid, path ? path : "none");
    	} else {
    		printk("task_precondition:  assigning context %s to pid %d exe=%s\n", context, task->pid, path ? path : "none");		
    		kfree(context);
    	}
    	if (buffer)
    		free_page((unsigned long)buffer);
    
    	return 0;
    }
    
    /* The file's security attributes must be initialized before first use. */
    static int file_precondition(struct file *file) 
    {
            struct task_security_struct *tsec;
    	struct file_security_struct *fsec = file->f_security;
    
    	if (ss_precondition())
    		return -1;
    
    	if (fsec && fsec->magic == SELINUX_MAGIC) 
    		return 0;
    
    	if (task_precondition(current))
    		return -1;
    	tsec = current->security;
    
    	fsec = kmalloc(sizeof(struct file_security_struct), GFP_KERNEL);
    	if (!fsec)
    		return -1; 
    	memset(fsec, 0, sizeof(struct file_security_struct));
    
            /* The SID of the open file descriptor state (i.e. the
    	   seek pointer and the flags), not the file. */
    	fsec->sid = tsec->sid; 
    	fsec->magic = SELINUX_MAGIC;
    	file->f_security = fsec;
    
    	return 0;
    }
    
    /* Check permission betweeen a pair of tasks, e.g. signal checks,
       fork check, ptrace check, etc. */
    int task_has_perm(struct task_struct *tsk1,
    		  struct task_struct *tsk2,
    		  access_vector_t perms)
    {
    	struct task_security_struct *tsec1, *tsec2;
    
    	if (task_precondition(tsk1))
    		return 0;
    	if (task_precondition(tsk2))
    		return 0;
    	tsec1 = tsk1->security;
    	tsec2 = tsk2->security;
    	return avc_has_perm_ref(tsec1->sid, tsec2->sid, 
    				SECCLASS_PROCESS, perms, &tsec2->avcr);
    }
    
    /* Check whether a task is allowed to use a capability. */
    int task_has_capability(struct task_struct *tsk,
    			int cap)
    {
    	struct task_security_struct *tsec;
    	avc_audit_data_t ad;
    
    	if (task_precondition(tsk))
    		return 0;
    
    	tsec = tsk->security;
    
    	AVC_AUDIT_DATA_INIT(&ad,CAP);
    	ad.u.cap = cap;
    	return avc_has_perm_audit(tsec->sid, tsec->sid, 
    				  SECCLASS_CAPABILITY, CAP_TO_MASK(cap), &ad);
    }
    
    /* Check whether a task is allowed to use a system operation. */
    int task_has_system(struct task_struct *tsk,
    		    access_vector_t perms)
    {
    	struct task_security_struct *tsec;
    
    	if (task_precondition(tsk))
    		return 0;
    
    	tsec = tsk->security;
    
    	return avc_has_perm(tsec->sid, SECINITSID_KERNEL, 
    			    SECCLASS_SYSTEM, perms);
    }
    
    /* Check whether a task has a particular permission to an inode.
       The 'aeref' parameter is optional and allows other AVC
       entry references to be passed (e.g. the one in the struct file).
       The 'adp' parameter is optional and allows other audit
       data to be passed (e.g. the dentry). */
    int inode_has_perm(struct task_struct *tsk,
    		   struct inode *inode,
    		   access_vector_t perms,
    		   avc_entry_ref_t *aeref,
    		   avc_audit_data_t *adp)
    {
    	struct task_security_struct *tsec;
    	struct inode_security_struct *isec;
    	avc_audit_data_t ad;
    
    	if (task_precondition(tsk))
    		return 0;
    	if (inode_precondition(inode))
    		return 0;
    	tsec = tsk->security;
    	isec = inode->i_security;
    
    	if (!adp) {
    		adp = &ad;
    		AVC_AUDIT_DATA_INIT(&ad, FS);
    		ad.u.fs.inode = inode;
    	}
    
    	return avc_has_perm_ref_audit(tsec->sid, isec->sid, isec->sclass, 
    				      perms, aeref ? aeref : &isec->avcr, adp);
    }
    
    /* Same as inode_has_perm, but pass explicit audit data containing 
       the dentry to help the auditing code to more easily generate the 
       pathname if needed. */
    static inline int dentry_has_perm(struct task_struct *tsk,
    				  struct dentry *dentry, 
    				  access_vector_t av,
    				  avc_entry_ref_t *aeref) 
    {
    	struct inode *inode = dentry->d_inode;
    	avc_audit_data_t ad;
    	AVC_AUDIT_DATA_INIT(&ad,FS);
    	ad.u.fs.dentry = dentry;
    	return inode_has_perm(tsk, inode, av, aeref, &ad);
    }
    
    /* Same as dentry_has_perm, but also pass the AVC entry reference in
       the struct file, since it should have a higher hit rate. */
    static inline int file_has_perm(struct task_struct *tsk,
    				struct file *file, 
    				access_vector_t av)
    {
    	struct file_security_struct *fsec;
    	struct dentry *dentry = file->f_dentry;
    	
    	if (file_precondition(file))
    		return dentry_has_perm(tsk, dentry, av, NULL);
    		
    	fsec = file->f_security;
    	return dentry_has_perm(tsk, dentry, av, &fsec->inode_avcr);
    }
    
    /* Check whether a task can create a file. */
    static int may_create(struct inode *dir, 
    		      struct dentry *dentry, 
    		      security_class_t tclass)
    {
    	struct task_security_struct *tsec;
    	struct inode_security_struct *dsec;
    	struct superblock_security_struct *sbsec;
    	security_id_t newsid;
    	avc_audit_data_t ad;
    	int rc;
    
    	if (task_precondition(current))
    		return 0;
    	if (inode_precondition(dir))
    		return 0;
    
    	tsec = current->security;
    	dsec = dir->i_security;
    
    	AVC_AUDIT_DATA_INIT(&ad, FS);
    	ad.u.fs.dentry = dentry;
    
    	rc = avc_has_perm_ref_audit(tsec->sid, dsec->sid, SECCLASS_DIR, 
    				    DIR__ADD_NAME | DIR__SEARCH, 
    				    &dsec->avcr, &ad);
    	if (rc)
    		return rc;
    
    	rc = security_transition_sid(tsec->sid, dsec->sid, tclass, 
    				     &newsid);
    	if (rc)
    		return rc;
    
    	rc = avc_has_perm_audit(tsec->sid, newsid, tclass, FILE__CREATE, &ad);
    	if (rc)
    		return rc;
    
    	if (dir->i_sb) {
    		sbsec = dir->i_sb->s_security;
    
    		rc = avc_has_perm_audit(newsid, sbsec->sid, 
    					SECCLASS_FILESYSTEM, 
    					FILESYSTEM__ASSOCIATE, &ad);
    		if (rc)
    			return rc;
    	}
    
    	return 0;
    }
    
    #define MAY_LINK   0
    #define MAY_UNLINK 1
    #define MAY_RMDIR  2
    
    /* Check whether a task can link, unlink, or rmdir a file/directory. */
    static int may_link(struct inode *dir, 
    		    struct dentry *dentry,
    		    int kind)
    				 
    {
    	struct task_security_struct *tsec;
    	struct inode_security_struct *dsec, *isec;
    	avc_audit_data_t ad;
    	access_vector_t av;
    	int rc;
    
    	if (task_precondition(current))
    		return 0;
    	if (inode_precondition(dir))
    		return 0;
    	if (inode_precondition(dentry->d_inode))
    		return 0;
    
    	tsec = current->security;
    	dsec = dir->i_security;
    	isec = dentry->d_inode->i_security;
    
    	AVC_AUDIT_DATA_INIT(&ad, FS);
    	ad.u.fs.dentry = dentry;
    
    	av = DIR__SEARCH;
    	av |= (kind ? DIR__REMOVE_NAME : DIR__ADD_NAME);
    	rc = avc_has_perm_ref_audit(tsec->sid, dsec->sid, SECCLASS_DIR, 
    				    av, &dsec->avcr, &ad);
    	if (rc)
    		return rc;
    
    	switch (kind) {
    	case MAY_LINK:
    		av = FILE__LINK;
    		break;
    	case MAY_UNLINK:
    		av = FILE__UNLINK;
    		break;
    	case MAY_RMDIR:
    		av = DIR__RMDIR;
    		break;
    	default:
    		printk("may_link:  unrecognized kind %d\n", kind);
    		return 0;
    	}
    
    	rc = avc_has_perm_ref_audit(tsec->sid, isec->sid, isec->sclass, 
    				    av, &isec->avcr, &ad);
    	return rc;
    }
    
    static inline int may_rename(struct inode *old_dir, 
    			     struct dentry *old_dentry, 
    			     struct inode *new_dir, 
    			     struct dentry *new_dentry)
    {
    	struct task_security_struct *tsec;
    	struct inode_security_struct *old_dsec, *new_dsec, *old_isec, *new_isec;
    	avc_audit_data_t ad;
    	access_vector_t av;
    	int old_is_dir, new_is_dir;
    	int rc;
    
    	if (task_precondition(current))
    		return 0;
    	if (inode_precondition(old_dir))
    		return 0;
    	if (inode_precondition(new_dir))
    		return 0;
    	if (inode_precondition(old_dentry->d_inode))
    		return 0;
    	if (new_dentry->d_inode &&
    	    inode_precondition(new_dentry->d_inode))
    		return 0;
    
    	tsec = current->security;
    	old_dsec = old_dir->i_security;
    	old_isec = old_dentry->d_inode->i_security;
    	old_is_dir = S_ISDIR(old_dentry->d_inode->i_mode);
    	new_dsec = new_dir->i_security;
    
    	AVC_AUDIT_DATA_INIT(&ad, FS);
    
    	ad.u.fs.dentry = old_dentry;
    	rc = avc_has_perm_ref_audit(tsec->sid, old_dsec->sid, SECCLASS_DIR, 
    				    DIR__REMOVE_NAME | DIR__SEARCH, 
    				    &old_dsec->avcr, &ad);
    	if (rc)
    		return rc;
    	rc = avc_has_perm_ref_audit(tsec->sid, old_isec->sid, 
    				    old_isec->sclass, 
    				    FILE__RENAME, 
    				    &old_isec->avcr, &ad);
    	if (rc)
    		return rc;
    	if (old_is_dir && new_dir != old_dir) {
    		rc = avc_has_perm_ref_audit(tsec->sid, old_isec->sid, 
    					    old_isec->sclass, 
    					    DIR__REPARENT, 
    					    &old_isec->avcr, &ad);
    		if (rc)
    			return rc;
    	}
    
    	ad.u.fs.dentry = new_dentry;
    	av = DIR__ADD_NAME | DIR__SEARCH;
    	if (new_dentry->d_inode)
    		av |= DIR__REMOVE_NAME;
    	rc = avc_has_perm_ref_audit(tsec->sid, new_dsec->sid, SECCLASS_DIR, 
    				    av,&new_dsec->avcr, &ad);
    	if (rc)
    		return rc;
    	if (new_dentry->d_inode) {
    		new_isec = new_dentry->d_inode->i_security;
    		new_is_dir = S_ISDIR(new_dentry->d_inode->i_mode);
    		rc = avc_has_perm_ref_audit(tsec->sid, new_isec->sid, 
    					    new_isec->sclass,
    					    (new_is_dir ? DIR__RMDIR : FILE__UNLINK),
    					    &new_isec->avcr, &ad);
    		if (rc)
    			return rc;
    	}
    
    	return 0;
    }
    
    /* Check whether a task can perform a filesystem operation. */
    int superblock_has_perm(struct task_struct *tsk,
    			struct super_block *sb,
    			access_vector_t perms,
    			avc_audit_data_t *ad)
    {
    	struct task_security_struct *tsec;
    	struct superblock_security_struct *sbsec;
    
    	if (task_precondition(tsk))
    		return 0;
    	if (superblock_precondition(sb))
    		return 0;
    	tsec = tsk->security;
    	sbsec = sb->s_security;
    	return avc_has_perm_audit(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM, 
    				  perms, ad);
    }
    
    /* Convert a Linux file mask to an access vector. */
    static inline access_vector_t file_mask_to_av(int mode, int mask) 
    {
    	access_vector_t av = 0;
    
    	if ((mode & S_IFMT) != S_IFDIR) {
    		if (mask & MAY_EXEC) 
    			av |= FILE__EXECUTE;
    		if (mask & MAY_WRITE) 
    			/* XXX: no way to distinguish append vs. write here. */
    			av |= FILE__WRITE;
    		if (mask & MAY_READ) 
    			av |= FILE__READ;
    	} else {
    		if (mask & MAY_EXEC) 
    			av |= DIR__SEARCH;
    		if (mask & MAY_WRITE) 
    			/* Directory write permission checking is
    			   handled by the individual directory 
    			   inode operations, since only they can determine
    			   the appropriate permission (add_name,
    			   remove_name, reparent). */
    			;
    		if (mask & MAY_READ) 
    			av |= DIR__READ;
    	}
    
    	return av;
    }
    
    /* Set an inode's SID, where the inode may or may not already
       have a security structure. */
    int inode_security_set_sid(struct inode *inode, security_id_t sid)
    {
    	struct inode_security_struct *isec = inode->i_security;
    
    	if (!isec) {
    		isec = kmalloc(sizeof(struct inode_security_struct), GFP_KERNEL);
    		if (!isec)
    			return -1; 
    		memset(isec, 0, sizeof(struct inode_security_struct));
    		inode->i_security = isec;
    		isec->sclass = file_mode_to_security_class(inode->i_mode);
    		isec->magic = SELINUX_MAGIC;
    	}
    	isec->sid = sid;
    	return 0;
    }
    
    /* Set the security attributes on a newly created file. */
    static int post_create(struct inode *dir, 
    		       struct dentry *dentry)
    {
    
    	struct task_security_struct *tsec;
    	struct inode_security_struct *dsec;
    	struct superblock_security_struct *sbsec;
    	security_id_t newsid;
    	int rc;
    
    	if (task_precondition(current))
    		return 0;
    	if (inode_precondition(dir))
    		return 0;
    
    	tsec = current->security;
    	dsec = dir->i_security;
    
    	rc = security_transition_sid(tsec->sid, dsec->sid, 
    				     file_mode_to_security_class(dentry->d_inode->i_mode), 
    				     &newsid);
    	if (rc) {
    		printk("post_create:  unable to obtain new SID, rc=%d\n",-rc);
    		return rc;
    	}
    	
    	rc = inode_security_set_sid(dentry->d_inode, newsid);
    	if (rc) {
    		printk("post_create:  unable to set new SID, rc=%d\n",-rc);
    		return rc;
    	}
    
    	if (dir->i_sb) {
    		sbsec = dir->i_sb->s_security;
    		if (sbsec && sbsec->uses_psids) {
    			rc = sid_to_psid(dentry->d_inode, newsid);
    			if (rc) {
    				printk("post_create:  unable to set new PSID, rc=%d\n",-rc);
    				return rc;
    			}
    		}
    	}
    
    	return 0;
    }	
    
    /* Hook functions begin here. */
    
    /* assorted security operations  (mostly syscall interposition) */
    
    static int selinux_sethostname(void)
    {
    	return task_has_capability(current,CAP_SYS_ADMIN);
    }
    
    static int selinux_setdomainname(void)
    {
    	return task_has_capability(current,CAP_SYS_ADMIN);
    }
    
    static int selinux_reboot(unsigned int cmd)
    {
    	return task_has_capability(current,CAP_SYS_BOOT);
    }
    
    static int selinux_mount(char * dev_name, 
                             struct nameidata *nd, 
                             char * type, 
                             unsigned long flags, 
                             void * data)	
    {
    	return dentry_has_perm(current, nd->dentry, DIR__MOUNTON, NULL);
    }
    
    static int selinux_add_vfsmnt(struct nameidata *nd, 
    			      struct super_block *sb, 
    			      char * dev_name)
    {
    	struct superblock_security_struct *sbsec;
    	struct inode_security_struct *isec;
    	avc_audit_data_t ad;
    	int rc;
    
    	AVC_AUDIT_DATA_INIT(&ad,FS);
    	ad.u.fs.dentry = nd->dentry;
    	rc = superblock_has_perm(current, sb, FILESYSTEM__MOUNT, &ad);
    	if (rc) 
    		return rc;
    
    	if (superblock_precondition(sb))
    		return 0;
    	sbsec = sb->s_security;
    
    	if (inode_precondition(nd->dentry->d_inode))
    		return 0;
    	isec = nd->dentry->d_inode->i_security;
    
    	return avc_has_perm_audit(sbsec->sid, isec->sid, 
    				  SECCLASS_DIR, DIR__MOUNTASSOCIATE, &ad);
    }
    
    static int selinux_umount(struct vfsmount *mnt, int umount_root, int flags)
    {
    	return superblock_has_perm(current,mnt->mnt_sb, FILESYSTEM__UNMOUNT,NULL);
    }
    
    static void selinux_umount_close(struct vfsmount *mnt)
    {
    	struct super_block *sb = mnt->mnt_sb;
    	struct superblock_security_struct *sbsec = sb->s_security;
    
    	if (sbsec && sbsec->uses_psids)
    		psid_release(sb);
    	return;
    }
    
    static void selinux_umount_busy	(struct vfsmount *mnt)
    {
    	struct super_block *sb = mnt->mnt_sb;
    	struct superblock_security_struct *sbsec = sb->s_security;
    
    	if (sbsec && sbsec->uses_psids)
    		psid_init(sb);
    
    	return;
    }
    
    static int selinux_remount(struct vfsmount *mnt, unsigned long flags, void *data)
    {
    	return superblock_has_perm(current, mnt->mnt_sb, FILESYSTEM__REMOUNT, NULL);
    }
    
    static void selinux_post_remount(struct vfsmount *mnt, unsigned long flags, void *data)
    {
    	struct super_block *sb = mnt->mnt_sb;
    	struct superblock_security_struct *sbsec = sb->s_security;
    
    	if (sbsec && sbsec->uses_psids)
    		psid_remount(sb);
    }
    
    static int selinux_ioperm(void)
    {
    	return task_has_capability(current,CAP_SYS_RAWIO);
    }
    
    static int selinux_iopl(void)
    {
    	return task_has_capability(current,CAP_SYS_RAWIO);
    }
    
    static int selinux_ptrace(struct task_struct *parent, struct task_struct *child)
    {
    	return task_has_perm(parent, child, PROCESS__PTRACE);
    }
    
    static int selinux_setcapablity(void)
    {
    	return task_has_capability(current,CAP_SETPCAP);
    }
    
    static int selinux_acct(void)
    {
    	return task_has_capability(current,CAP_SYS_PACCT);
    }
    
    /* binprm security operations */
    
    static void *selinux_bprm_alloc_security(struct linux_binprm *bprm)
    {
    	struct task_security_struct *tsec;
    	struct inode *inode = bprm->file->f_dentry->d_inode;
    	struct inode_security_struct *isec;
    	security_id_t newsid;
    	avc_audit_data_t ad;
    	int rc;
    
    	if (bprm->sh_bang)
    		/* The security field should already be set properly. */
    		return bprm->security;
    
    	/* Clear the field since it may contain garbage. */
    	bprm->security = NULL;
    
    	/* Preconditions */
    	if (task_precondition(current))
    		goto out;
    	if (inode_precondition(inode))
    		goto out;
    
    	tsec = current->security;
    	isec = inode->i_security;
    
    	/* Default to the current task SID. */
    	bprm->security = (void *)tsec->sid;
    
    	/* Check for a default transition on this program. */
    	rc = security_transition_sid(tsec->sid, isec->sid, SECCLASS_PROCESS, 
    				     &newsid);
    	if (rc)
    		goto out;
    
            if (tsec->sid != newsid) {
    		/* Check permissions for the transition. */
    		AVC_AUDIT_DATA_INIT(&ad, FS);
    		ad.u.fs.dentry = bprm->file->f_dentry;
    
    		rc = avc_has_perm_audit(tsec->sid, newsid, 
    					SECCLASS_PROCESS, PROCESS__TRANSITION, 
    					&ad);
    		if (rc)
    			goto out;
    
    		rc = avc_has_perm_audit(newsid, isec->sid, 
    					SECCLASS_PROCESS, PROCESS__ENTRYPOINT, 
    					&ad);
    		if (rc)
    			goto out;
    
    		/* Set the security field to the new SID. */
    		bprm->security = (void*) newsid;
    	}
    
    out:
    	return bprm->security;
    }
    
    static void selinux_bprm_free_security(void *b_security)
    {
    	/* Nothing to do - not dynamically allocated. */
    	return;
    }
    
    static inline int must_not_trace_exec(struct task_struct * p,
    					    struct linux_binprm *bprm)
    {
    	struct task_security_struct *tsec;
    	security_id_t newsid;
    
    	if (task_precondition(p->p_pptr))
    		return 0;
    	tsec = p->p_pptr->security;
    
    	if (bprm->security)
    		newsid = (security_id_t)bprm->security;
    	else
    		newsid = tsec->sid;
    
    	return (p->flags & PT_PTRACED) && 
    		avc_has_perm(tsec->sid, newsid, 
    			     SECCLASS_PROCESS, PROCESS__PTRACE);
    }
    
    static void selinux_bprm_compute_creds(struct linux_binprm *bprm)
    {
    	struct task_security_struct *tsec;
    	security_id_t sid;
    	int do_unlock = 0;
    
    	if (task_precondition(current))
    		return;
    	tsec = current->security;
    	sid = (security_id_t)bprm->security;
    
    	if (current->pid != 1 && sid && tsec->sid != sid) {
                    current->dumpable = 0;
    		lock_kernel();
    		if (must_not_trace_exec(current, bprm) 
    		    || atomic_read(&current->fs->count) > 1
    		    || atomic_read(&current->files->count) > 1
    		    || atomic_read(&current->sig->count) > 1) {
    			sid = tsec->sid;
    		}
    		do_unlock = 1;
    	}
    
    	if (tsec->sid != sid) {
    		tsec->sid = sid;
    
    		/* need to force wait permission check if parent is waiting */
    		wake_up_interruptible(&current->p_pptr->wait_chldexit);
    	}
    
    	if(do_unlock)
    		unlock_kernel();
    }
    
    /* inode security operations */
    
    static void *selinux_inode_alloc_security(struct inode *inode)
    {
    	/* Allocation is deferred to the first use of the inode
    	   and handled by inode_precondition. */
    	return NULL;
    }
    
    static void selinux_inode_free_security(void *i_security)
    {
    	struct inode_security_struct *isec;
    
    	isec = i_security;
    	if (isec && isec->magic == SELINUX_MAGIC) {
    		/* XXX need inode number to remove ino2sid entry from PSID table */
    		kfree(isec);
    	}
    }
    
    static int selinux_inode_create(struct inode *dir, struct dentry *dentry, int mask)
    {
    	return may_create(dir, dentry, SECCLASS_FILE);
    }
    
    static void selinux_inode_post_create(struct inode *dir, struct dentry *dentry, int mask)
    {
    	post_create(dir, dentry);
    }
    
    static int selinux_inode_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry)
    {
    	return may_link(dir, old_dentry, MAY_LINK);
    }
    
    static void selinux_inode_post_link(struct dentry *old_dentry, struct inode *inode, struct dentry *new_dentry)
    {
    	return;
    }
    
    static int selinux_inode_unlink(struct inode *dir, struct dentry *dentry)
    {
    	return may_link(dir, dentry, MAY_UNLINK);
    }
    
    static int selinux_inode_symlink(struct inode *dir, struct dentry *dentry, const char *name)
    {
    	return may_create(dir, dentry, SECCLASS_LNK_FILE);
    }
    
    static void selinux_inode_post_symlink(struct inode *dir, struct dentry *dentry, const char *name)
    {
    	post_create(dir, dentry);
    }
    
    static int selinux_inode_mkdir(struct inode *dir, struct dentry *dentry, int mask)
    {
    	return may_create(dir, dentry, SECCLASS_DIR);
    }
    
    static void selinux_inode_post_mkdir(struct inode *dir, struct dentry *dentry, int mask)
    {
    	post_create(dir, dentry);
    }
    
    static int selinux_inode_rmdir(struct inode *dir, struct dentry *dentry)
    {
    	return may_link(dir, dentry, MAY_RMDIR);
    }
    
    static int selinux_inode_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
    {
    	return may_create(dir, dentry, file_mode_to_security_class(mode));
    }
    
    static void selinux_inode_post_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
    {
    	post_create(dir, dentry);
    }
    
    static int selinux_inode_rename(struct inode *old_inode, struct dentry *old_dentry, struct inode *new_inode, struct dentry *new_dentry)
    {
    	return may_rename(old_inode, old_dentry, new_inode, new_dentry);
    }
    
    static void selinux_inode_post_rename(struct inode *old_inode, struct dentry *old_dentry, struct inode *new_inode, struct dentry *new_dentry)
    {
    	return;
    }
    
    static int selinux_inode_readlink(struct dentry *dentry, char *name, int mask)
    {
    	return dentry_has_perm(current, dentry, FILE__READ, NULL);
    }
    
    static int selinux_inode_follow_link(struct dentry *dentry, struct nameidata *nameidata)
    {
    	return dentry_has_perm(current, dentry, FILE__READ, NULL);
    }
    
    static int selinux_inode_truncate(struct inode *inode)
    {
    	return inode_has_perm(current, inode, FILE__SETATTR | FILE__WRITE, NULL, NULL);
    }
    
    static int selinux_inode_permission(struct inode *inode, int mask)
    {
    	return inode_has_perm(current, inode, 
    			       file_mask_to_av(inode->i_mode, mask), NULL, NULL);
    }
    
    static int selinux_inode_revalidate(struct dentry *inode)
    {
    	return 0;
    }
    
    static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr)
    {
    	return dentry_has_perm(current, dentry, FILE__SETATTR, NULL);
    }
    
    static void selinux_inode_attach_pathlabel(struct dentry *dentry, struct vfsmount *mnt)
    {
    	if (!dentry->d_inode) 
    		/* May be NULL since attach_pathlabel is always called,
    		   even after failed lookup/create operations. */
    		return;
    
    	inode_precondition(dentry->d_inode);
    }
    
    /* file security operations */
    
    static int selinux_file_permission(struct file *file, int mask)
    {
    	struct inode *inode = file->f_dentry->d_inode;
    	return dentry_has_perm(current, file->f_dentry, 
    			       file_mask_to_av(inode->i_mode, mask), NULL);
    }
    
    static void * selinux_file_alloc_security(struct file *file)
    {
    	file->f_security = NULL;
    
    	if (file_precondition(file))
    		return NULL;
    
    	return file->f_security;
    }
    
    static void selinux_file_free_security(struct file *file)
    {
    	if (file->f_security)
    		kfree(file->f_security);
    	return;
    }
    
    static int selinux_file_llseek(struct file *file)
    {
    	/* XXX:  Need to check access to seek pointer */
    	return 0;
    }
    
    static int selinux_file_read(struct file *file)
    {
    	return file_has_perm(current, file, FILE__READ);
    }
    
    static int selinux_file_write(struct file *file)
    {
    	/* XXX:  append vs. write */
    	return file_has_perm(current, file, FILE__WRITE);
    }
    
    static int selinux_file_ioctl(struct file *file)
    {
    	return file_has_perm(current, file, FILE__IOCTL);
    }
    
    static int selinux_file_mmap(struct file *file)
    {
    	/* XXX:  Need the flags (is it shared or private?) and protection */
    	return 0;
    }
    
    static int selinux_file_lock(struct file *file)
    {
    	return file_has_perm(current, file, FILE__LOCK);
    }
    
    static int selinux_file_readv(struct file *file)
    {
    	return file_has_perm(current, file, FILE__READ);
    }
    
    static int selinux_file_writev(struct file *file)
    {
    	return file_has_perm(current, file, FILE__WRITE);
    }
    
    /* task security operations */
    
    static int selinux_task_create(void)
    {
    	return task_has_perm(current, current, PROCESS__FORK);
    }
    
    static void * selinux_task_alloc_security(void)
    {
    	struct task_security_struct *tsec1, *tsec2;
    
    	if (task_precondition(current))
    		return NULL;
    	tsec1 = current->security;
    
    	tsec2 = kmalloc(sizeof(struct task_security_struct), GFP_KERNEL);
    	if (!tsec2)
    		return NULL; 
    	memset(tsec2, 0, sizeof(struct task_security_struct));
    	tsec2->osid = tsec1->osid;
    	tsec2->sid = tsec1->sid;;
    	tsec2->magic = SELINUX_MAGIC;
    	return tsec2;
    }
    
    static void selinux_task_free_security(void *data)
    {
    	if (data)
    		kfree(data);
    }
    static int selinux_task_setuid(void)
    {
    	return task_has_capability(current,CAP_SETUID);
    }
    static int selinux_task_setgid(void)
    {
    	return task_has_capability(current,CAP_SETGID);
    }
    static int selinux_task_setgroups(void)
    {
    	return task_has_capability(current,CAP_SETGID);
    }
    static int selinux_task_setnice(struct task_struct *p, int nice)
    {
    	return task_has_perm(current,p, PROCESS__SETSCHED);
    }
    
    static int selinux_task_setrlimit(unsigned int resource)
    {
    	return task_has_capability(current,CAP_SYS_RESOURCE);
    }
    
    static int selinux_task_setscheduler(struct task_struct *p, int policy)
    {
    	return task_has_perm(current, p, PROCESS__SETSCHED);
    }
    
    static int selinux_task_kill(void)
    {
    	/* XXX:  need target process, signal, info */
    	return task_has_capability(current,CAP_KILL);
    }
    
    static int selinux_task_set_label(char *filename)
    {
    	return 0;
    }
    
    static void selinux_task_reset_label(void)
    {
    	return;
    }
    
    /* module security operations */
    
    static int selinux_module_create_module(const char *name_user, size_t size)
    {
    	return task_has_capability(current,CAP_SYS_MODULE);
    }
    
    static int selinux_module_init_module(const char *name_user, struct module *mod_user)
    {
    	return task_has_capability(current,CAP_SYS_MODULE);
    }
    
    static int selinux_module_delete_module(const char *name_user)
    {
    	return task_has_capability(current,CAP_SYS_MODULE);
    }
    
    /* message queue security operations */
    
    static int selinux_msg_queue_create(key_t key)
    {
    	return 0;
    }
    
    static int selinux_msg_queue_permission(void)
    {
    	return 0;
    }
    
    static int selinux_msg_queue_setmaxqbytes(void)
    {
    	return 0;
    }
    
    static int selinux_msg_queue_setattr(void)
    {
    	return 0;
    }
    static int selinux_msg_queue_delete(void)
    {
    	return 0;
    }
    
    /* shared memory security operations */
    static int selinux_shm_create(key_t key)
    {
    	return 0;
    }
    static int selinux_shm_permission(void)
    {
    	return 0;
    }
    static int selinux_shm_setattr(void)
    {
    	return 0;
    }
    static int selinux_shm_delete(void)
    {
    	return 0;
    }
    
    
    static struct binprm_security_ops selinux_bprm_ops = {
    	alloc_security:	selinux_bprm_alloc_security,
    	free_security:	selinux_bprm_free_security,
    	compute_creds:	selinux_bprm_compute_creds,
    };
    static struct inode_security_ops selinux_inode_ops = {
    	alloc_security:	selinux_inode_alloc_security,
    	free_security:	selinux_inode_free_security,
    	create:		selinux_inode_create,
    	post_create:	selinux_inode_post_create,
    	link:		selinux_inode_link,
    	post_link:	selinux_inode_post_link,
    	unlink:		selinux_inode_unlink,
    	symlink:	selinux_inode_symlink,
    	post_symlink:	selinux_inode_post_symlink,
    	mkdir:		selinux_inode_mkdir,
    	post_mkdir:	selinux_inode_post_mkdir,
    	rmdir:		selinux_inode_rmdir,
    	mknod:		selinux_inode_mknod,
    	post_mknod:	selinux_inode_post_mknod,
    	rename:		selinux_inode_rename,
    	post_rename:	selinux_inode_post_rename,
    	readlink:	selinux_inode_readlink,
    	follow_link:	selinux_inode_follow_link,
    	truncate:	selinux_inode_truncate,
    	permission:	selinux_inode_permission,
    	revalidate:	selinux_inode_revalidate,
    	setattr:	selinux_inode_setattr,
    	attach_pathlabel:selinux_inode_attach_pathlabel,
    };
    
    static struct file_security_ops	selinux_file_ops = {
    	permission:	selinux_file_permission,
    	alloc_security:	selinux_file_alloc_security,
    	free_security:	selinux_file_free_security,
    	llseek:		selinux_file_llseek,
    	read:		selinux_file_read,
    	write:		selinux_file_write,
    	ioctl:		selinux_file_ioctl,
    	mmap:		selinux_file_mmap,
    	lock:		selinux_file_lock,
    	readv:		selinux_file_readv,
    	writev:		selinux_file_writev,
    };
    
    static struct task_security_ops	selinux_task_ops = {
    	create:		selinux_task_create,
    	alloc_security:	selinux_task_alloc_security,
    	free_security:	selinux_task_free_security,
    	setuid:		selinux_task_setuid,
    	setgid:		selinux_task_setgid,
    	setgroups:	selinux_task_setgroups,
    	setnice:	selinux_task_setnice,
    	setrlimit:	selinux_task_setrlimit,
    	setscheduler:	selinux_task_setscheduler,
    	kill:		selinux_task_kill,
    	set_label:	selinux_task_set_label,
    	reset_label:	selinux_task_reset_label,
    };
    
    static struct socket_security_ops selinux_socket_ops = {};
    
    static struct module_security_ops selinux_module_ops = {
    	create_module:	selinux_module_create_module,
    	init_module:	selinux_module_init_module,
    	delete_module:	selinux_module_delete_module,
    
    };
    
    static struct msg_queue_security_ops selinux_msg_queue_ops = {
    	create:		selinux_msg_queue_create,
    	permission:	selinux_msg_queue_permission,
    	setmaxqbytes:	selinux_msg_queue_setmaxqbytes,
    	setattr:	selinux_msg_queue_setattr,
    	delete:		selinux_msg_queue_delete,
    };
    
    static struct shm_security_ops selinux_shm_ops = {
    	create:		selinux_shm_create,
    	permission:	selinux_shm_permission,
    	setattr:	selinux_shm_setattr,
    	delete:		selinux_shm_delete,
    };
    
    struct security_operations selinux_ops = {
            version:                SECURITY_INTERFACE_VERSION,
    
    	sethostname:		selinux_sethostname,
    	setdomainname:		selinux_setdomainname,
    	reboot:			selinux_reboot,
    	mount:			selinux_mount,
    	add_vfsmnt:             selinux_add_vfsmnt,
    	umount:			selinux_umount,
    	umount_close:		selinux_umount_close,
    	umount_busy:		selinux_umount_busy,
    	remount:		selinux_remount,
    	post_remount:		selinux_post_remount,
    	ioperm:			selinux_ioperm,
    	iopl:			selinux_iopl,
    	ptrace:			selinux_ptrace,
    	setcapability:		selinux_setcapablity,
    	acct:			selinux_acct,
    
    	bprm_ops:		&selinux_bprm_ops,
    	inode_ops:		&selinux_inode_ops,
    	file_ops:		&selinux_file_ops,
    	task_ops:		&selinux_task_ops,
    	socket_ops:		&selinux_socket_ops,
    	module_ops:		&selinux_module_ops,
    	msg_queue_ops:		&selinux_msg_queue_ops,
    	shm_ops:		&selinux_shm_ops,
    };
    
    static int __init selinux_plug_init (void)
    {
    	avc_init();
    
    	if (register_security (&selinux_ops)) {
    		printk (KERN_INFO "Failure registering SELinux with the kernel\n");
    		return -EINVAL;
    	}
    	return 0;
    }
    
    
    static void __exit selinux_plug_exit (void)
    {
    	/* remove ourselves from the security framework */
    	if (unregister_security (&selinux_ops)) {
    		printk (KERN_INFO "Failure unregistering SELinux with the kernel\n");
    	}
    }
    
    module_init (selinux_plug_init);
    module_exit (selinux_plug_exit);
    
    EXPORT_SYMBOL(selinux_ops);
    
    
    _______________________________________________
    linux-security-module mailing list
    linux-security-moduleat_private
    http://mail.wirex.com/mailman/listinfo/linux-security-module
    



    This archive was generated by hypermail 2b30 : Thu May 03 2001 - 13:56:28 PDT