Backport SMACK changes from 3.3 to 3.0
authorElena Reshetova <elena.reshetova@intel.com>
Fri, 4 May 2012 06:09:19 +0000 (09:09 +0300)
committerMarkus Lehtonen <markus.lehtonen@linux.intel.com>
Tue, 3 Jul 2012 09:31:08 +0000 (12:31 +0300)
Signed-off-by: Elena Reshetova <elena.reshetova@intel.com>
Signed-off-by: Markus Lehtonen <markus.lehtonen@linux.intel.com>
security/smack/smack.h
security/smack/smack_access.c
security/smack/smack_lsm.c
security/smack/smackfs.c

index 2b6c6a5..2ad0065 100644 (file)
@@ -41,9 +41,9 @@ struct superblock_smack {
 };
 
 struct socket_smack {
-       char            *smk_out;                       /* outbound label */
-       char            *smk_in;                        /* inbound label */
-       char            smk_packet[SMK_LABELLEN];       /* TCP peer label */
+       char            *smk_out;       /* outbound label */
+       char            *smk_in;        /* inbound label */
+       char            *smk_packet;    /* TCP peer label */
 };
 
 /*
@@ -116,13 +116,19 @@ struct smk_netlbladdr {
  * If there is a cipso value associated with the label it
  * gets stored here, too. This will most likely be rare as
  * the cipso direct mapping in used internally.
+ *
+ * Keep the access rules for this subject label here so that
+ * the entire set of rules does not need to be examined every
+ * time.
  */
 struct smack_known {
        struct list_head        list;
        char                    smk_known[SMK_LABELLEN];
        u32                     smk_secid;
        struct smack_cipso      *smk_cipso;
-       spinlock_t              smk_cipsolock; /* for changing cipso map */
+       spinlock_t              smk_cipsolock;  /* for changing cipso map */
+       struct list_head        smk_rules;      /* access rules */
+       struct mutex            smk_rules_lock; /* lock for the rules */
 };
 
 /*
@@ -150,7 +156,6 @@ struct smack_known {
 
 /*
  * smackfs magic number
- * smackfs macic number
  */
 #define SMACK_MAGIC    0x43415d53 /* "SMAC" */
 
@@ -176,9 +181,9 @@ struct smack_known {
 #define MAY_NOT                0
 
 /*
- * Number of access types used by Smack (rwxa)
+ * Number of access types used by Smack (rwxat)
  */
-#define SMK_NUM_ACCESS_TYPE 4
+#define SMK_NUM_ACCESS_TYPE 5
 
 /*
  * Smack audit data; is empty if CONFIG_AUDIT not set
@@ -201,10 +206,12 @@ int smk_access_entry(char *, char *, struct list_head *);
 int smk_access(char *, char *, int, struct smk_audit_info *);
 int smk_curacc(char *, u32, struct smk_audit_info *);
 int smack_to_cipso(const char *, struct smack_cipso *);
-void smack_from_cipso(u32, char *, char *);
+char *smack_from_cipso(u32, char *);
 char *smack_from_secid(const u32);
+void smk_parse_smack(const char *string, int len, char *smack);
 char *smk_import(const char *, int);
 struct smack_known *smk_import_entry(const char *, int);
+struct smack_known *smk_find_entry(const char *);
 u32 smack_to_secid(const char *);
 
 /*
@@ -223,7 +230,6 @@ extern struct smack_known smack_known_star;
 extern struct smack_known smack_known_web;
 
 extern struct list_head smack_known_list;
-extern struct list_head smack_rule_list;
 extern struct list_head smk_netlbladdr_list;
 
 extern struct security_operations smack_ops;
index 9637e10..cc7cb6e 100644 (file)
@@ -77,14 +77,19 @@ int log_policy = SMACK_AUDIT_DENIED;
  * entry is found returns -ENOENT.
  *
  * NOTE:
- * Even though Smack labels are usually shared on smack_list
- * labels that come in off the network can't be imported
- * and added to the list for locking reasons.
  *
- * Therefore, it is necessary to check the contents of the labels,
- * not just the pointer values. Of course, in most cases the labels
- * will be on the list, so checking the pointers may be a worthwhile
- * optimization.
+ * Earlier versions of this function allowed for labels that
+ * were not on the label list. This was done to allow for
+ * labels to come over the network that had never been seen
+ * before on this host. Unless the receiving socket has the
+ * star label this will always result in a failure check. The
+ * star labeled socket case is now handled in the networking
+ * hooks so there is no case where the label is not on the
+ * label list. Checking to see if the address of two labels
+ * is the same is now a reliable test.
+ *
+ * Do the object check first because that is more
+ * likely to differ.
  */
 int smk_access_entry(char *subject_label, char *object_label,
                        struct list_head *rule_list)
@@ -93,13 +98,10 @@ int smk_access_entry(char *subject_label, char *object_label,
        struct smack_rule *srp;
 
        list_for_each_entry_rcu(srp, rule_list, list) {
-               if (srp->smk_subject == subject_label ||
-                   strcmp(srp->smk_subject, subject_label) == 0) {
-                       if (srp->smk_object == object_label ||
-                           strcmp(srp->smk_object, object_label) == 0) {
-                               may = srp->smk_access;
-                               break;
-                       }
+               if (srp->smk_object == object_label &&
+                   srp->smk_subject == subject_label) {
+                       may = srp->smk_access;
+                       break;
                }
        }
 
@@ -117,18 +119,12 @@ int smk_access_entry(char *subject_label, char *object_label,
  * access rule list and returns 0 if the access is permitted,
  * non zero otherwise.
  *
- * Even though Smack labels are usually shared on smack_list
- * labels that come in off the network can't be imported
- * and added to the list for locking reasons.
- *
- * Therefore, it is necessary to check the contents of the labels,
- * not just the pointer values. Of course, in most cases the labels
- * will be on the list, so checking the pointers may be a worthwhile
- * optimization.
+ * Smack labels are shared on smack_list
  */
 int smk_access(char *subject_label, char *object_label, int request,
               struct smk_audit_info *a)
 {
+       struct smack_known *skp;
        int may = MAY_NOT;
        int rc = 0;
 
@@ -137,8 +133,7 @@ int smk_access(char *subject_label, char *object_label, int request,
         *
         * A star subject can't access any object.
         */
-       if (subject_label == smack_known_star.smk_known ||
-           strcmp(subject_label, smack_known_star.smk_known) == 0) {
+       if (subject_label == smack_known_star.smk_known) {
                rc = -EACCES;
                goto out_audit;
        }
@@ -148,33 +143,27 @@ int smk_access(char *subject_label, char *object_label, int request,
         * An internet subject can access any object.
         */
        if (object_label == smack_known_web.smk_known ||
-           subject_label == smack_known_web.smk_known ||
-           strcmp(object_label, smack_known_web.smk_known) == 0 ||
-           strcmp(subject_label, smack_known_web.smk_known) == 0)
+           subject_label == smack_known_web.smk_known)
                goto out_audit;
        /*
         * A star object can be accessed by any subject.
         */
-       if (object_label == smack_known_star.smk_known ||
-           strcmp(object_label, smack_known_star.smk_known) == 0)
+       if (object_label == smack_known_star.smk_known)
                goto out_audit;
        /*
         * An object can be accessed in any way by a subject
         * with the same label.
         */
-       if (subject_label == object_label ||
-           strcmp(subject_label, object_label) == 0)
+       if (subject_label == object_label)
                goto out_audit;
        /*
         * A hat subject can read any object.
         * A floor object can be read by any subject.
         */
        if ((request & MAY_ANYREAD) == request) {
-               if (object_label == smack_known_floor.smk_known ||
-                   strcmp(object_label, smack_known_floor.smk_known) == 0)
+               if (object_label == smack_known_floor.smk_known)
                        goto out_audit;
-               if (subject_label == smack_known_hat.smk_known ||
-                   strcmp(subject_label, smack_known_hat.smk_known) == 0)
+               if (subject_label == smack_known_hat.smk_known)
                        goto out_audit;
        }
        /*
@@ -184,8 +173,9 @@ int smk_access(char *subject_label, char *object_label, int request,
         * good. A negative response from smk_access_entry()
         * indicates there is no entry for this pair.
         */
+       skp = smk_find_entry(subject_label);
        rcu_read_lock();
-       may = smk_access_entry(subject_label, object_label, &smack_rule_list);
+       may = smk_access_entry(subject_label, object_label, &skp->smk_rules);
        rcu_read_unlock();
 
        if (may > 0 && (request & may) == request)
@@ -344,17 +334,32 @@ void smack_log(char *subject_label, char *object_label, int request,
 static DEFINE_MUTEX(smack_known_lock);
 
 /**
- * smk_import_entry - import a label, return the list entry
+ * smk_find_entry - find a label on the list, return the list entry
  * @string: a text string that might be a Smack label
- * @len: the maximum size, or zero if it is NULL terminated.
  *
  * Returns a pointer to the entry in the label list that
- * matches the passed string, adding it if necessary.
+ * matches the passed string.
  */
-struct smack_known *smk_import_entry(const char *string, int len)
+struct smack_known *smk_find_entry(const char *string)
 {
        struct smack_known *skp;
-       char smack[SMK_LABELLEN];
+
+       list_for_each_entry_rcu(skp, &smack_known_list, list) {
+               if (strncmp(skp->smk_known, string, SMK_MAXLEN) == 0)
+                       return skp;
+       }
+
+       return NULL;
+}
+
+/**
+ * smk_parse_smack - parse smack label from a text string
+ * @string: a text string that might contain a Smack label
+ * @len: the maximum size, or zero if it is NULL terminated.
+ * @smack: parsed smack label, or NULL if parse error
+ */
+void smk_parse_smack(const char *string, int len, char *smack)
+{
        int found;
        int i;
 
@@ -372,27 +377,38 @@ struct smack_known *smk_import_entry(const char *string, int len)
                } else
                        smack[i] = string[i];
        }
+}
+
+/**
+ * smk_import_entry - import a label, return the list entry
+ * @string: a text string that might be a Smack label
+ * @len: the maximum size, or zero if it is NULL terminated.
+ *
+ * Returns a pointer to the entry in the label list that
+ * matches the passed string, adding it if necessary.
+ */
+struct smack_known *smk_import_entry(const char *string, int len)
+{
+       struct smack_known *skp;
+       char smack[SMK_LABELLEN];
 
+       smk_parse_smack(string, len, smack);
        if (smack[0] == '\0')
                return NULL;
 
        mutex_lock(&smack_known_lock);
 
-       found = 0;
-       list_for_each_entry_rcu(skp, &smack_known_list, list) {
-               if (strncmp(skp->smk_known, smack, SMK_MAXLEN) == 0) {
-                       found = 1;
-                       break;
-               }
-       }
+       skp = smk_find_entry(smack);
 
-       if (found == 0) {
+       if (skp == NULL) {
                skp = kzalloc(sizeof(struct smack_known), GFP_KERNEL);
                if (skp != NULL) {
                        strncpy(skp->smk_known, smack, SMK_MAXLEN);
                        skp->smk_secid = smack_next_secid++;
                        skp->smk_cipso = NULL;
+                       INIT_LIST_HEAD(&skp->smk_rules);
                        spin_lock_init(&skp->smk_cipsolock);
+                       mutex_init(&skp->smk_rules_lock);
                        /*
                         * Make sure that the entry is actually
                         * filled before putting it on the list.
@@ -480,19 +496,12 @@ u32 smack_to_secid(const char *smack)
  * smack_from_cipso - find the Smack label associated with a CIPSO option
  * @level: Bell & LaPadula level from the network
  * @cp: Bell & LaPadula categories from the network
- * @result: where to put the Smack value
  *
  * This is a simple lookup in the label table.
  *
- * This is an odd duck as far as smack handling goes in that
- * it sends back a copy of the smack label rather than a pointer
- * to the master list. This is done because it is possible for
- * a foreign host to send a smack label that is new to this
- * machine and hence not on the list. That would not be an
- * issue except that adding an entry to the master list can't
- * be done at that point.
+ * Return the matching label from the label list or NULL.
  */
-void smack_from_cipso(u32 level, char *cp, char *result)
+char *smack_from_cipso(u32 level, char *cp)
 {
        struct smack_known *kp;
        char *final = NULL;
@@ -509,12 +518,13 @@ void smack_from_cipso(u32 level, char *cp, char *result)
                        final = kp->smk_known;
 
                spin_unlock_bh(&kp->smk_cipsolock);
+
+               if (final != NULL)
+                       break;
        }
        rcu_read_unlock();
-       if (final == NULL)
-               final = smack_known_huh.smk_known;
-       strncpy(result, final, SMK_MAXLEN);
-       return;
+
+       return final;
 }
 
 /**
index 9831a39..dcc3a7c 100644 (file)
@@ -5,12 +5,13 @@
  *
  *  Authors:
  *     Casey Schaufler <casey@schaufler-ca.com>
- *     Jarkko Sakkinen <ext-jarkko.2.sakkinen@nokia.com>
+ *     Jarkko Sakkinen <jarkko.sakkinen@intel.com>
  *
  *  Copyright (C) 2007 Casey Schaufler <casey@schaufler-ca.com>
  *  Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
- *                Paul Moore <paul.moore@hp.com>
+ *                Paul Moore <paul@paul-moore.com>
  *  Copyright (C) 2010 Nokia Corporation
+ *  Copyright (C) 2011 Intel Corporation.
  *
  *     This program is free software; you can redistribute it and/or modify
  *     it under the terms of the GNU General Public License version 2,
@@ -34,6 +35,7 @@
 #include <linux/audit.h>
 #include <linux/magic.h>
 #include <linux/dcache.h>
+#include <linux/personality.h>
 #include "smack.h"
 
 #define task_security(task)    (task_cred_xxx((task), security))
@@ -441,11 +443,17 @@ static int smack_sb_umount(struct vfsmount *mnt, int flags)
  * BPRM hooks
  */
 
+/**
+ * smack_bprm_set_creds - set creds for exec
+ * @bprm: the exec information
+ *
+ * Returns 0 if it gets a blob, -ENOMEM otherwise
+ */
 static int smack_bprm_set_creds(struct linux_binprm *bprm)
 {
-       struct task_smack *tsp = bprm->cred->security;
+       struct inode *inode = bprm->file->f_path.dentry->d_inode;
+       struct task_smack *bsp = bprm->cred->security;
        struct inode_smack *isp;
-       struct dentry *dp;
        int rc;
 
        rc = cap_bprm_set_creds(bprm);
@@ -455,20 +463,48 @@ static int smack_bprm_set_creds(struct linux_binprm *bprm)
        if (bprm->cred_prepared)
                return 0;
 
-       if (bprm->file == NULL || bprm->file->f_dentry == NULL)
+       isp = inode->i_security;
+       if (isp->smk_task == NULL || isp->smk_task == bsp->smk_task)
                return 0;
 
-       dp = bprm->file->f_dentry;
+       if (bprm->unsafe)
+               return -EPERM;
 
-       if (dp->d_inode == NULL)
-               return 0;
+       bsp->smk_task = isp->smk_task;
+       bprm->per_clear |= PER_CLEAR_ON_SETID;
 
-       isp = dp->d_inode->i_security;
+       return 0;
+}
 
-       if (isp->smk_task != NULL)
-               tsp->smk_task = isp->smk_task;
+/**
+ * smack_bprm_committing_creds - Prepare to install the new credentials
+ * from bprm.
+ *
+ * @bprm: binprm for exec
+ */
+static void smack_bprm_committing_creds(struct linux_binprm *bprm)
+{
+       struct task_smack *bsp = bprm->cred->security;
 
-       return 0;
+       if (bsp->smk_task != bsp->smk_forked)
+               current->pdeath_signal = 0;
+}
+
+/**
+ * smack_bprm_secureexec - Return the decision to use secureexec.
+ * @bprm: binprm for exec
+ *
+ * Returns 0 on success.
+ */
+static int smack_bprm_secureexec(struct linux_binprm *bprm)
+{
+       struct task_smack *tsp = current_security();
+       int ret = cap_bprm_secureexec(bprm);
+
+       if (!ret && (tsp->smk_task != tsp->smk_forked))
+               ret = 1;
+
+       return ret;
 }
 
 /*
@@ -516,6 +552,8 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir,
                                     const struct qstr *qstr, char **name,
                                     void **value, size_t *len)
 {
+       struct smack_known *skp;
+       char *csp = smk_of_current();
        char *isp = smk_of_inode(inode);
        char *dsp = smk_of_inode(dir);
        int may;
@@ -527,8 +565,9 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir,
        }
 
        if (value) {
+               skp = smk_find_entry(csp);
                rcu_read_lock();
-               may = smk_access_entry(smk_of_current(), dsp, &smack_rule_list);
+               may = smk_access_entry(csp, dsp, &skp->smk_rules);
                rcu_read_unlock();
 
                /*
@@ -684,6 +723,7 @@ static int smack_inode_rename(struct inode *old_inode,
  * smack_inode_permission - Smack version of permission()
  * @inode: the inode in question
  * @mask: the access requested
+ * @flags: special case
  *
  * This is the important Smack hook.
  *
@@ -703,6 +743,7 @@ static int smack_inode_permission(struct inode *inode, int mask, unsigned flags)
        /* May be droppable after audit */
        if (flags & IPERM_FLAG_RCU)
                return -ECHILD;
+
        smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_INODE);
        smk_ad_setfield_u_fs_inode(&ad, inode);
        return smk_curacc(smk_of_inode(inode), mask, &ad);
@@ -840,7 +881,7 @@ static void smack_inode_post_setxattr(struct dentry *dentry, const char *name,
        return;
 }
 
-/*
+/**
  * smack_inode_getxattr - Smack check on getxattr
  * @dentry: the object
  * @name: unused
@@ -857,7 +898,7 @@ static int smack_inode_getxattr(struct dentry *dentry, const char *name)
        return smk_curacc(smk_of_inode(dentry->d_inode), MAY_READ, &ad);
 }
 
-/*
+/**
  * smack_inode_removexattr - Smack check on removexattr
  * @dentry: the object
  * @name: name of the attribute
@@ -1087,36 +1128,31 @@ static int smack_file_lock(struct file *file, unsigned int cmd)
  * @cmd: what action to check
  * @arg: unused
  *
+ * Generally these operations are harmless.
+ * File locking operations present an obvious mechanism
+ * for passing information, so they require write access.
+ *
  * Returns 0 if current has access, error code otherwise
  */
 static int smack_file_fcntl(struct file *file, unsigned int cmd,
                            unsigned long arg)
 {
        struct smk_audit_info ad;
-       int rc;
+       int rc = 0;
 
-       smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH);
-       smk_ad_setfield_u_fs_path(&ad, file->f_path);
 
        switch (cmd) {
-       case F_DUPFD:
-       case F_GETFD:
-       case F_GETFL:
        case F_GETLK:
-       case F_GETOWN:
-       case F_GETSIG:
-               rc = smk_curacc(file->f_security, MAY_READ, &ad);
-               break;
-       case F_SETFD:
-       case F_SETFL:
        case F_SETLK:
        case F_SETLKW:
        case F_SETOWN:
        case F_SETSIG:
+               smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH);
+               smk_ad_setfield_u_fs_path(&ad, file->f_path);
                rc = smk_curacc(file->f_security, MAY_WRITE, &ad);
                break;
        default:
-               rc = smk_curacc(file->f_security, MAY_READWRITE, &ad);
+               break;
        }
 
        return rc;
@@ -1137,6 +1173,7 @@ static int smack_file_mmap(struct file *file,
                           unsigned long flags, unsigned long addr,
                           unsigned long addr_only)
 {
+       struct smack_known *skp;
        struct smack_rule *srp;
        struct task_smack *tsp;
        char *sp;
@@ -1169,6 +1206,7 @@ static int smack_file_mmap(struct file *file,
 
        tsp = current_security();
        sp = smk_of_current();
+       skp = smk_find_entry(sp);
        rc = 0;
 
        rcu_read_lock();
@@ -1176,15 +1214,8 @@ static int smack_file_mmap(struct file *file,
         * For each Smack rule associated with the subject
         * label verify that the SMACK64MMAP also has access
         * to that rule's object label.
-        *
-        * Because neither of the labels comes
-        * from the networking code it is sufficient
-        * to compare pointers.
         */
-       list_for_each_entry_rcu(srp, &smack_rule_list, list) {
-               if (srp->smk_subject != sp)
-                       continue;
-
+       list_for_each_entry_rcu(srp, &skp->smk_rules, list) {
                osmack = srp->smk_object;
                /*
                 * Matching labels always allows access.
@@ -1213,7 +1244,8 @@ static int smack_file_mmap(struct file *file,
                 * If there isn't one a SMACK64MMAP subject
                 * can't have as much access as current.
                 */
-               mmay = smk_access_entry(msmack, osmack, &smack_rule_list);
+               skp = smk_find_entry(msmack);
+               mmay = smk_access_entry(msmack, osmack, &skp->smk_rules);
                if (mmay == -ENOENT) {
                        rc = -EACCES;
                        break;
@@ -1314,6 +1346,24 @@ static int smack_file_receive(struct file *file)
        return smk_curacc(file->f_security, may, &ad);
 }
 
+/**
+ * smack_dentry_open - Smack dentry open processing
+ * @file: the object
+ * @cred: unused
+ *
+ * Set the security blob in the file structure.
+ *
+ * Returns 0
+ */
+static int smack_dentry_open(struct file *file, const struct cred *cred)
+{
+       struct inode_smack *isp = file->f_path.dentry->d_inode->i_security;
+
+       file->f_security = isp->smk_inode;
+
+       return 0;
+}
+
 /*
  * Task hooks
  */
@@ -1454,15 +1504,17 @@ static int smack_kernel_create_files_as(struct cred *new,
 /**
  * smk_curacc_on_task - helper to log task related access
  * @p: the task object
- * @access : the access requested
+ * @access: the access requested
+ * @caller: name of the calling function for audit
  *
  * Return 0 if access is permitted
  */
-static int smk_curacc_on_task(struct task_struct *p, int access)
+static int smk_curacc_on_task(struct task_struct *p, int access,
+                               const char *caller)
 {
        struct smk_audit_info ad;
 
-       smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK);
+       smk_ad_init(&ad, caller, LSM_AUDIT_DATA_TASK);
        smk_ad_setfield_u_tsk(&ad, p);
        return smk_curacc(smk_of_task(task_security(p)), access, &ad);
 }
@@ -1476,7 +1528,7 @@ static int smk_curacc_on_task(struct task_struct *p, int access)
  */
 static int smack_task_setpgid(struct task_struct *p, pid_t pgid)
 {
-       return smk_curacc_on_task(p, MAY_WRITE);
+       return smk_curacc_on_task(p, MAY_WRITE, __func__);
 }
 
 /**
@@ -1487,7 +1539,7 @@ static int smack_task_setpgid(struct task_struct *p, pid_t pgid)
  */
 static int smack_task_getpgid(struct task_struct *p)
 {
-       return smk_curacc_on_task(p, MAY_READ);
+       return smk_curacc_on_task(p, MAY_READ, __func__);
 }
 
 /**
@@ -1498,7 +1550,7 @@ static int smack_task_getpgid(struct task_struct *p)
  */
 static int smack_task_getsid(struct task_struct *p)
 {
-       return smk_curacc_on_task(p, MAY_READ);
+       return smk_curacc_on_task(p, MAY_READ, __func__);
 }
 
 /**
@@ -1526,7 +1578,7 @@ static int smack_task_setnice(struct task_struct *p, int nice)
 
        rc = cap_task_setnice(p, nice);
        if (rc == 0)
-               rc = smk_curacc_on_task(p, MAY_WRITE);
+               rc = smk_curacc_on_task(p, MAY_WRITE, __func__);
        return rc;
 }
 
@@ -1543,7 +1595,7 @@ static int smack_task_setioprio(struct task_struct *p, int ioprio)
 
        rc = cap_task_setioprio(p, ioprio);
        if (rc == 0)
-               rc = smk_curacc_on_task(p, MAY_WRITE);
+               rc = smk_curacc_on_task(p, MAY_WRITE, __func__);
        return rc;
 }
 
@@ -1555,7 +1607,7 @@ static int smack_task_setioprio(struct task_struct *p, int ioprio)
  */
 static int smack_task_getioprio(struct task_struct *p)
 {
-       return smk_curacc_on_task(p, MAY_READ);
+       return smk_curacc_on_task(p, MAY_READ, __func__);
 }
 
 /**
@@ -1572,7 +1624,7 @@ static int smack_task_setscheduler(struct task_struct *p)
 
        rc = cap_task_setscheduler(p);
        if (rc == 0)
-               rc = smk_curacc_on_task(p, MAY_WRITE);
+               rc = smk_curacc_on_task(p, MAY_WRITE, __func__);
        return rc;
 }
 
@@ -1584,7 +1636,7 @@ static int smack_task_setscheduler(struct task_struct *p)
  */
 static int smack_task_getscheduler(struct task_struct *p)
 {
-       return smk_curacc_on_task(p, MAY_READ);
+       return smk_curacc_on_task(p, MAY_READ, __func__);
 }
 
 /**
@@ -1595,7 +1647,7 @@ static int smack_task_getscheduler(struct task_struct *p)
  */
 static int smack_task_movememory(struct task_struct *p)
 {
-       return smk_curacc_on_task(p, MAY_WRITE);
+       return smk_curacc_on_task(p, MAY_WRITE, __func__);
 }
 
 /**
@@ -1710,7 +1762,7 @@ static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags)
 
        ssp->smk_in = csp;
        ssp->smk_out = csp;
-       ssp->smk_packet[0] = '\0';
+       ssp->smk_packet = NULL;
 
        sk->sk_security = ssp;
 
@@ -2752,6 +2804,7 @@ static int smack_unix_stream_connect(struct sock *sock,
 {
        struct socket_smack *ssp = sock->sk_security;
        struct socket_smack *osp = other->sk_security;
+       struct socket_smack *nsp = newsk->sk_security;
        struct smk_audit_info ad;
        int rc = 0;
 
@@ -2761,6 +2814,14 @@ static int smack_unix_stream_connect(struct sock *sock,
        if (!capable(CAP_MAC_OVERRIDE))
                rc = smk_access(ssp->smk_out, osp->smk_in, MAY_WRITE, &ad);
 
+       /*
+        * Cross reference the peer labels for SO_PEERSEC.
+        */
+       if (rc == 0) {
+               nsp->smk_packet = ssp->smk_out;
+               ssp->smk_packet = osp->smk_out;
+       }
+
        return rc;
 }
 
@@ -2812,16 +2873,17 @@ static int smack_socket_sendmsg(struct socket *sock, struct msghdr *msg,
        return smack_netlabel_send(sock->sk, sip);
 }
 
-
 /**
  * smack_from_secattr - Convert a netlabel attr.mls.lvl/attr.mls.cat pair to smack
  * @sap: netlabel secattr
- * @sip: where to put the result
+ * @ssp: socket security information
  *
- * Copies a smack label into sip
+ * Returns a pointer to a Smack label found on the label list.
  */
-static void smack_from_secattr(struct netlbl_lsm_secattr *sap, char *sip)
+static char *smack_from_secattr(struct netlbl_lsm_secattr *sap,
+                               struct socket_smack *ssp)
 {
+       struct smack_known *skp;
        char smack[SMK_LABELLEN];
        char *sp;
        int pcat;
@@ -2851,15 +2913,43 @@ static void smack_from_secattr(struct netlbl_lsm_secattr *sap, char *sip)
                 * we are already done. WeeHee.
                 */
                if (sap->attr.mls.lvl == smack_cipso_direct) {
-                       memcpy(sip, smack, SMK_MAXLEN);
-                       return;
+                       /*
+                        * The label sent is usually on the label list.
+                        *
+                        * If it is not we may still want to allow the
+                        * delivery.
+                        *
+                        * If the recipient is accepting all packets
+                        * because it is using the star ("*") label
+                        * for SMACK64IPIN provide the web ("@") label
+                        * so that a directed response will succeed.
+                        * This is not very correct from a MAC point
+                        * of view, but gets around the problem that
+                        * locking prevents adding the newly discovered
+                        * label to the list.
+                        * The case where the recipient is not using
+                        * the star label should obviously fail.
+                        * The easy way to do this is to provide the
+                        * star label as the subject label.
+                        */
+                       skp = smk_find_entry(smack);
+                       if (skp != NULL)
+                               return skp->smk_known;
+                       if (ssp != NULL &&
+                           ssp->smk_in == smack_known_star.smk_known)
+                               return smack_known_web.smk_known;
+                       return smack_known_star.smk_known;
                }
                /*
                 * Look it up in the supplied table if it is not
                 * a direct mapping.
                 */
-               smack_from_cipso(sap->attr.mls.lvl, smack, sip);
-               return;
+               sp = smack_from_cipso(sap->attr.mls.lvl, smack);
+               if (sp != NULL)
+                       return sp;
+               if (ssp != NULL && ssp->smk_in == smack_known_star.smk_known)
+                       return smack_known_web.smk_known;
+               return smack_known_star.smk_known;
        }
        if ((sap->flags & NETLBL_SECATTR_SECID) != 0) {
                /*
@@ -2874,16 +2964,14 @@ static void smack_from_secattr(struct netlbl_lsm_secattr *sap, char *sip)
                 * secid is from a fallback.
                 */
                BUG_ON(sp == NULL);
-               strncpy(sip, sp, SMK_MAXLEN);
-               return;
+               return sp;
        }
        /*
         * Without guidance regarding the smack value
         * for the packet fall back on the network
         * ambient value.
         */
-       strncpy(sip, smack_net_ambient, SMK_MAXLEN);
-       return;
+       return smack_net_ambient;
 }
 
 /**
@@ -2897,7 +2985,6 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
 {
        struct netlbl_lsm_secattr secattr;
        struct socket_smack *ssp = sk->sk_security;
-       char smack[SMK_LABELLEN];
        char *csp;
        int rc;
        struct smk_audit_info ad;
@@ -2910,10 +2997,9 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
        netlbl_secattr_init(&secattr);
 
        rc = netlbl_skbuff_getattr(skb, sk->sk_family, &secattr);
-       if (rc == 0) {
-               smack_from_secattr(&secattr, smack);
-               csp = smack;
-       } else
+       if (rc == 0)
+               csp = smack_from_secattr(&secattr, ssp);
+       else
                csp = smack_net_ambient;
 
        netlbl_secattr_destroy(&secattr);
@@ -2950,15 +3036,19 @@ static int smack_socket_getpeersec_stream(struct socket *sock,
                                          int __user *optlen, unsigned len)
 {
        struct socket_smack *ssp;
-       int slen;
+       char *rcp = "";
+       int slen = 1;
        int rc = 0;
 
        ssp = sock->sk->sk_security;
-       slen = strlen(ssp->smk_packet) + 1;
+       if (ssp->smk_packet != NULL) {
+               rcp = ssp->smk_packet;
+               slen = strlen(rcp) + 1;
+       }
 
        if (slen > len)
                rc = -ERANGE;
-       else if (copy_to_user(optval, ssp->smk_packet, slen) != 0)
+       else if (copy_to_user(optval, rcp, slen) != 0)
                rc = -EFAULT;
 
        if (put_user(slen, optlen) != 0)
@@ -2981,8 +3071,8 @@ static int smack_socket_getpeersec_dgram(struct socket *sock,
 
 {
        struct netlbl_lsm_secattr secattr;
-       struct socket_smack *sp;
-       char smack[SMK_LABELLEN];
+       struct socket_smack *ssp = NULL;
+       char *sp;
        int family = PF_UNSPEC;
        u32 s = 0;      /* 0 is the invalid secid */
        int rc;
@@ -2997,17 +3087,19 @@ static int smack_socket_getpeersec_dgram(struct socket *sock,
                family = sock->sk->sk_family;
 
        if (family == PF_UNIX) {
-               sp = sock->sk->sk_security;
-               s = smack_to_secid(sp->smk_out);
+               ssp = sock->sk->sk_security;
+               s = smack_to_secid(ssp->smk_out);
        } else if (family == PF_INET || family == PF_INET6) {
                /*
                 * Translate what netlabel gave us.
                 */
+               if (sock != NULL && sock->sk != NULL)
+                       ssp = sock->sk->sk_security;
                netlbl_secattr_init(&secattr);
                rc = netlbl_skbuff_getattr(skb, family, &secattr);
                if (rc == 0) {
-                       smack_from_secattr(&secattr, smack);
-                       s = smack_to_secid(smack);
+                       sp = smack_from_secattr(&secattr, ssp);
+                       s = smack_to_secid(sp);
                }
                netlbl_secattr_destroy(&secattr);
        }
@@ -3055,7 +3147,7 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb,
        struct netlbl_lsm_secattr secattr;
        struct sockaddr_in addr;
        struct iphdr *hdr;
-       char smack[SMK_LABELLEN];
+       char *sp;
        int rc;
        struct smk_audit_info ad;
 
@@ -3066,9 +3158,9 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb,
        netlbl_secattr_init(&secattr);
        rc = netlbl_skbuff_getattr(skb, family, &secattr);
        if (rc == 0)
-               smack_from_secattr(&secattr, smack);
+               sp = smack_from_secattr(&secattr, ssp);
        else
-               strncpy(smack, smack_known_huh.smk_known, SMK_MAXLEN);
+               sp = smack_known_huh.smk_known;
        netlbl_secattr_destroy(&secattr);
 
 #ifdef CONFIG_AUDIT
@@ -3081,7 +3173,7 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb,
         * Receiving a packet requires that the other end be able to write
         * here. Read access is not required.
         */
-       rc = smk_access(smack, ssp->smk_in, MAY_WRITE, &ad);
+       rc = smk_access(sp, ssp->smk_in, MAY_WRITE, &ad);
        if (rc != 0)
                return rc;
 
@@ -3089,7 +3181,7 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb,
         * Save the peer's label in the request_sock so we can later setup
         * smk_packet in the child socket so that SO_PEERCRED can report it.
         */
-       req->peer_secid = smack_to_secid(smack);
+       req->peer_secid = smack_to_secid(sp);
 
        /*
         * We need to decide if we want to label the incoming connection here
@@ -3102,7 +3194,7 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb,
        if (smack_host_label(&addr) == NULL) {
                rcu_read_unlock();
                netlbl_secattr_init(&secattr);
-               smack_to_secattr(smack, &secattr);
+               smack_to_secattr(sp, &secattr);
                rc = netlbl_req_setattr(req, &secattr);
                netlbl_secattr_destroy(&secattr);
        } else {
@@ -3124,13 +3216,11 @@ static void smack_inet_csk_clone(struct sock *sk,
                                 const struct request_sock *req)
 {
        struct socket_smack *ssp = sk->sk_security;
-       char *smack;
 
-       if (req->peer_secid != 0) {
-               smack = smack_from_secid(req->peer_secid);
-               strncpy(ssp->smk_packet, smack, SMK_MAXLEN);
-       } else
-               ssp->smk_packet[0] = '\0';
+       if (req->peer_secid != 0)
+               ssp->smk_packet = smack_from_secid(req->peer_secid);
+       else
+               ssp->smk_packet = NULL;
 }
 
 /*
@@ -3408,6 +3498,8 @@ struct security_operations smack_ops = {
        .sb_umount =                    smack_sb_umount,
 
        .bprm_set_creds =               smack_bprm_set_creds,
+       .bprm_committing_creds =        smack_bprm_committing_creds,
+       .bprm_secureexec =              smack_bprm_secureexec,
 
        .inode_alloc_security =         smack_inode_alloc_security,
        .inode_free_security =          smack_inode_free_security,
@@ -3439,6 +3531,8 @@ struct security_operations smack_ops = {
        .file_send_sigiotask =          smack_file_send_sigiotask,
        .file_receive =                 smack_file_receive,
 
+       .dentry_open =                  smack_dentry_open,
+
        .cred_alloc_blank =             smack_cred_alloc_blank,
        .cred_free =                    smack_cred_free,
        .cred_prepare =                 smack_cred_prepare,
@@ -3528,8 +3622,38 @@ struct security_operations smack_ops = {
 };
 
 
-static __init void init_smack_know_list(void)
+static __init void init_smack_known_list(void)
 {
+       /*
+        * Initialize CIPSO locks
+        */
+       spin_lock_init(&smack_known_huh.smk_cipsolock);
+       spin_lock_init(&smack_known_hat.smk_cipsolock);
+       spin_lock_init(&smack_known_star.smk_cipsolock);
+       spin_lock_init(&smack_known_floor.smk_cipsolock);
+       spin_lock_init(&smack_known_invalid.smk_cipsolock);
+       spin_lock_init(&smack_known_web.smk_cipsolock);
+       /*
+        * Initialize rule list locks
+        */
+       mutex_init(&smack_known_huh.smk_rules_lock);
+       mutex_init(&smack_known_hat.smk_rules_lock);
+       mutex_init(&smack_known_floor.smk_rules_lock);
+       mutex_init(&smack_known_star.smk_rules_lock);
+       mutex_init(&smack_known_invalid.smk_rules_lock);
+       mutex_init(&smack_known_web.smk_rules_lock);
+       /*
+        * Initialize rule lists
+        */
+       INIT_LIST_HEAD(&smack_known_huh.smk_rules);
+       INIT_LIST_HEAD(&smack_known_hat.smk_rules);
+       INIT_LIST_HEAD(&smack_known_star.smk_rules);
+       INIT_LIST_HEAD(&smack_known_floor.smk_rules);
+       INIT_LIST_HEAD(&smack_known_invalid.smk_rules);
+       INIT_LIST_HEAD(&smack_known_web.smk_rules);
+       /*
+        * Create the known labels list
+        */
        list_add(&smack_known_huh.list, &smack_known_list);
        list_add(&smack_known_hat.list, &smack_known_list);
        list_add(&smack_known_star.list, &smack_known_list);
@@ -3564,16 +3688,8 @@ static __init int smack_init(void)
        cred = (struct cred *) current->cred;
        cred->security = tsp;
 
-       /* initialize the smack_know_list */
-       init_smack_know_list();
-       /*
-        * Initialize locks
-        */
-       spin_lock_init(&smack_known_huh.smk_cipsolock);
-       spin_lock_init(&smack_known_hat.smk_cipsolock);
-       spin_lock_init(&smack_known_star.smk_cipsolock);
-       spin_lock_init(&smack_known_floor.smk_cipsolock);
-       spin_lock_init(&smack_known_invalid.smk_cipsolock);
+       /* initialize the smack_known_list */
+       init_smack_known_list();
 
        /*
         * Register with LSM
index f934601..038811c 100644 (file)
@@ -44,6 +44,7 @@ enum smk_inos {
        SMK_ONLYCAP     = 9,    /* the only "capable" label */
        SMK_LOGGING     = 10,   /* logging */
        SMK_LOAD_SELF   = 11,   /* task specific rules */
+       SMK_ACCESSES    = 12,   /* access policy */
 };
 
 /*
@@ -85,15 +86,22 @@ char *smack_onlycap;
  */
 
 LIST_HEAD(smk_netlbladdr_list);
+
+/*
+ * Rule lists are maintained for each label.
+ * This master list is just for reading /smack/load.
+ */
+struct smack_master_list {
+       struct list_head        list;
+       struct smack_rule       *smk_rule;
+};
+
 LIST_HEAD(smack_rule_list);
 
 static int smk_cipso_doi_value = SMACK_CIPSO_DOI_DEFAULT;
 
 const char *smack_cipso_option = SMACK_CIPSO_OPTION;
 
-
-#define        SEQ_READ_FINISHED       1
-
 /*
  * Values for parsing cipso rules
  * SMK_DIGITLEN: Length of a digit field in a rule.
@@ -159,9 +167,13 @@ static int smk_set_access(struct smack_rule *srp, struct list_head *rule_list,
 
        mutex_lock(rule_lock);
 
+       /*
+        * Because the object label is less likely to match
+        * than the subject label check it first
+        */
        list_for_each_entry_rcu(sp, rule_list, list) {
-               if (sp->smk_subject == srp->smk_subject &&
-                   sp->smk_object == srp->smk_object) {
+               if (sp->smk_object == srp->smk_object &&
+                   sp->smk_subject == srp->smk_subject) {
                        found = 1;
                        sp->smk_access = srp->smk_access;
                        break;
@@ -176,6 +188,99 @@ static int smk_set_access(struct smack_rule *srp, struct list_head *rule_list,
 }
 
 /**
+ * smk_parse_rule - parse Smack rule from load string
+ * @data: string to be parsed whose size is SMK_LOADLEN
+ * @rule: Smack rule
+ * @import: if non-zero, import labels
+ */
+static int smk_parse_rule(const char *data, struct smack_rule *rule, int import)
+{
+       char smack[SMK_LABELLEN];
+       struct smack_known *skp;
+
+       if (import) {
+               rule->smk_subject = smk_import(data, 0);
+               if (rule->smk_subject == NULL)
+                       return -1;
+
+               rule->smk_object = smk_import(data + SMK_LABELLEN, 0);
+               if (rule->smk_object == NULL)
+                       return -1;
+       } else {
+               smk_parse_smack(data, 0, smack);
+               skp = smk_find_entry(smack);
+               if (skp == NULL)
+                       return -1;
+               rule->smk_subject = skp->smk_known;
+
+               smk_parse_smack(data + SMK_LABELLEN, 0, smack);
+               skp = smk_find_entry(smack);
+               if (skp == NULL)
+                       return -1;
+               rule->smk_object = skp->smk_known;
+       }
+
+       rule->smk_access = 0;
+
+       switch (data[SMK_LABELLEN + SMK_LABELLEN]) {
+       case '-':
+               break;
+       case 'r':
+       case 'R':
+               rule->smk_access |= MAY_READ;
+               break;
+       default:
+               return -1;
+       }
+
+       switch (data[SMK_LABELLEN + SMK_LABELLEN + 1]) {
+       case '-':
+               break;
+       case 'w':
+       case 'W':
+               rule->smk_access |= MAY_WRITE;
+               break;
+       default:
+               return -1;
+       }
+
+       switch (data[SMK_LABELLEN + SMK_LABELLEN + 2]) {
+       case '-':
+               break;
+       case 'x':
+       case 'X':
+               rule->smk_access |= MAY_EXEC;
+               break;
+       default:
+               return -1;
+       }
+
+       switch (data[SMK_LABELLEN + SMK_LABELLEN + 3]) {
+       case '-':
+               break;
+       case 'a':
+       case 'A':
+               rule->smk_access |= MAY_APPEND;
+               break;
+       default:
+               return -1;
+       }
+
+       switch (data[SMK_LABELLEN + SMK_LABELLEN + 4]) {
+       case '-':
+               break;
+       case 't':
+       case 'T':
+               rule->smk_access |= MAY_TRANSMUTE;
+               break;
+       default:
+               return -1;
+       }
+
+       return 0;
+}
+
+/**
  * smk_write_load_list - write() for any /smack/load
  * @file: file pointer, not actually used
  * @buf: where to get the data from
@@ -197,9 +302,12 @@ static ssize_t smk_write_load_list(struct file *file, const char __user *buf,
                                struct list_head *rule_list,
                                struct mutex *rule_lock)
 {
+       struct smack_master_list *smlp;
+       struct smack_known *skp;
        struct smack_rule *rule;
        char *data;
        int rc = -EINVAL;
+       int load = 0;
 
        /*
         * No partial writes.
@@ -234,78 +342,32 @@ static ssize_t smk_write_load_list(struct file *file, const char __user *buf,
                goto out;
        }
 
-       rule->smk_subject = smk_import(data, 0);
-       if (rule->smk_subject == NULL)
-               goto out_free_rule;
-
-       rule->smk_object = smk_import(data + SMK_LABELLEN, 0);
-       if (rule->smk_object == NULL)
-               goto out_free_rule;
-
-       rule->smk_access = 0;
-
-       switch (data[SMK_LABELLEN + SMK_LABELLEN]) {
-       case '-':
-               break;
-       case 'r':
-       case 'R':
-               rule->smk_access |= MAY_READ;
-               break;
-       default:
+       if (smk_parse_rule(data, rule, 1))
                goto out_free_rule;
-       }
 
-       switch (data[SMK_LABELLEN + SMK_LABELLEN + 1]) {
-       case '-':
-               break;
-       case 'w':
-       case 'W':
-               rule->smk_access |= MAY_WRITE;
-               break;
-       default:
-               goto out_free_rule;
-       }
-
-       switch (data[SMK_LABELLEN + SMK_LABELLEN + 2]) {
-       case '-':
-               break;
-       case 'x':
-       case 'X':
-               rule->smk_access |= MAY_EXEC;
-               break;
-       default:
-               goto out_free_rule;
-       }
-
-       switch (data[SMK_LABELLEN + SMK_LABELLEN + 3]) {
-       case '-':
-               break;
-       case 'a':
-       case 'A':
-               rule->smk_access |= MAY_APPEND;
-               break;
-       default:
-               goto out_free_rule;
-       }
-
-       switch (data[SMK_LABELLEN + SMK_LABELLEN + 4]) {
-       case '-':
-               break;
-       case 't':
-       case 'T':
-               rule->smk_access |= MAY_TRANSMUTE;
-               break;
-       default:
-               goto out_free_rule;
+       if (rule_list == NULL) {
+               load = 1;
+               skp = smk_find_entry(rule->smk_subject);
+               rule_list = &skp->smk_rules;
+               rule_lock = &skp->smk_rules_lock;
        }
 
        rc = count;
        /*
+        * If this is "load" as opposed to "load-self" and a new rule
+        * it needs to get added for reporting.
         * smk_set_access returns true if there was already a rule
         * for the subject/object pair, and false if it was new.
         */
-       if (!smk_set_access(rule, rule_list, rule_lock))
+       if (load && !smk_set_access(rule, rule_list, rule_lock)) {
+               smlp = kzalloc(sizeof(*smlp), GFP_KERNEL);
+               if (smlp != NULL) {
+                       smlp->smk_rule = rule;
+                       list_add_rcu(&smlp->list, &smack_rule_list);
+               } else
+                       rc = -ENOMEM;
                goto out;
+       }
 
 out_free_rule:
        kfree(rule);
@@ -314,36 +376,71 @@ out:
        return rc;
 }
 
-
 /*
- * Seq_file read operations for /smack/load
+ * Core logic for smackfs seq list operations.
  */
 
-static void *load_seq_start(struct seq_file *s, loff_t *pos)
+static void *smk_seq_start(struct seq_file *s, loff_t *pos,
+                               struct list_head *head)
 {
-       if (*pos == SEQ_READ_FINISHED)
+       struct list_head *list;
+
+       /*
+        * This is 0 the first time through.
+        */
+       if (s->index == 0)
+               s->private = head;
+
+       if (s->private == NULL)
                return NULL;
-       if (list_empty(&smack_rule_list))
+
+       list = s->private;
+       if (list_empty(list))
                return NULL;
-       return smack_rule_list.next;
+
+       if (s->index == 0)
+               return list->next;
+       return list;
 }
 
-static void *load_seq_next(struct seq_file *s, void *v, loff_t *pos)
+static void *smk_seq_next(struct seq_file *s, void *v, loff_t *pos,
+                               struct list_head *head)
 {
        struct list_head *list = v;
 
-       if (list_is_last(list, &smack_rule_list)) {
-               *pos = SEQ_READ_FINISHED;
+       if (list_is_last(list, head)) {
+               s->private = NULL;
                return NULL;
        }
+       s->private = list->next;
        return list->next;
 }
 
+static void smk_seq_stop(struct seq_file *s, void *v)
+{
+       /* No-op */
+}
+
+/*
+ * Seq_file read operations for /smack/load
+ */
+
+static void *load_seq_start(struct seq_file *s, loff_t *pos)
+{
+       return smk_seq_start(s, pos, &smack_rule_list);
+}
+
+static void *load_seq_next(struct seq_file *s, void *v, loff_t *pos)
+{
+       return smk_seq_next(s, v, pos, &smack_rule_list);
+}
+
 static int load_seq_show(struct seq_file *s, void *v)
 {
        struct list_head *list = v;
-       struct smack_rule *srp =
-                list_entry(list, struct smack_rule, list);
+       struct smack_master_list *smlp =
+                list_entry(list, struct smack_master_list, list);
+       struct smack_rule *srp = smlp->smk_rule;
 
        seq_printf(s, "%s %s", (char *)srp->smk_subject,
                   (char *)srp->smk_object);
@@ -368,16 +465,11 @@ static int load_seq_show(struct seq_file *s, void *v)
        return 0;
 }
 
-static void load_seq_stop(struct seq_file *s, void *v)
-{
-       /* No-op */
-}
-
 static const struct seq_operations load_seq_ops = {
        .start = load_seq_start,
        .next  = load_seq_next,
        .show  = load_seq_show,
-       .stop  = load_seq_stop,
+       .stop  = smk_seq_stop,
 };
 
 /**
@@ -412,8 +504,7 @@ static ssize_t smk_write_load(struct file *file, const char __user *buf,
        if (!capable(CAP_MAC_ADMIN))
                return -EPERM;
 
-       return smk_write_load_list(file, buf, count, ppos, &smack_rule_list,
-                                       &smack_list_lock);
+       return smk_write_load_list(file, buf, count, ppos, NULL, NULL);
 }
 
 static const struct file_operations smk_load_ops = {
@@ -497,28 +588,12 @@ static void smk_unlbl_ambient(char *oldambient)
 
 static void *cipso_seq_start(struct seq_file *s, loff_t *pos)
 {
-       if (*pos == SEQ_READ_FINISHED)
-               return NULL;
-       if (list_empty(&smack_known_list))
-               return NULL;
-
-       return smack_known_list.next;
+       return smk_seq_start(s, pos, &smack_known_list);
 }
 
 static void *cipso_seq_next(struct seq_file *s, void *v, loff_t *pos)
 {
-       struct list_head  *list = v;
-
-       /*
-        * labels with no associated cipso value wont be printed
-        * in cipso_seq_show
-        */
-       if (list_is_last(list, &smack_known_list)) {
-               *pos = SEQ_READ_FINISHED;
-               return NULL;
-       }
-
-       return list->next;
+       return smk_seq_next(s, v, pos, &smack_known_list);
 }
 
 /*
@@ -557,16 +632,11 @@ static int cipso_seq_show(struct seq_file *s, void *v)
        return 0;
 }
 
-static void cipso_seq_stop(struct seq_file *s, void *v)
-{
-       /* No-op */
-}
-
 static const struct seq_operations cipso_seq_ops = {
        .start = cipso_seq_start,
-       .stop  = cipso_seq_stop,
        .next  = cipso_seq_next,
        .show  = cipso_seq_show,
+       .stop  = smk_seq_stop,
 };
 
 /**
@@ -711,23 +781,12 @@ static const struct file_operations smk_cipso_ops = {
 
 static void *netlbladdr_seq_start(struct seq_file *s, loff_t *pos)
 {
-       if (*pos == SEQ_READ_FINISHED)
-               return NULL;
-       if (list_empty(&smk_netlbladdr_list))
-               return NULL;
-       return smk_netlbladdr_list.next;
+       return smk_seq_start(s, pos, &smk_netlbladdr_list);
 }
 
 static void *netlbladdr_seq_next(struct seq_file *s, void *v, loff_t *pos)
 {
-       struct list_head *list = v;
-
-       if (list_is_last(list, &smk_netlbladdr_list)) {
-               *pos = SEQ_READ_FINISHED;
-               return NULL;
-       }
-
-       return list->next;
+       return smk_seq_next(s, v, pos, &smk_netlbladdr_list);
 }
 #define BEBITS (sizeof(__be32) * 8)
 
@@ -751,16 +810,11 @@ static int netlbladdr_seq_show(struct seq_file *s, void *v)
        return 0;
 }
 
-static void netlbladdr_seq_stop(struct seq_file *s, void *v)
-{
-       /* No-op */
-}
-
 static const struct seq_operations netlbladdr_seq_ops = {
        .start = netlbladdr_seq_start,
-       .stop  = netlbladdr_seq_stop,
        .next  = netlbladdr_seq_next,
        .show  = netlbladdr_seq_show,
+       .stop  = smk_seq_stop,
 };
 
 /**
@@ -1328,23 +1382,14 @@ static void *load_self_seq_start(struct seq_file *s, loff_t *pos)
 {
        struct task_smack *tsp = current_security();
 
-       if (*pos == SEQ_READ_FINISHED)
-               return NULL;
-       if (list_empty(&tsp->smk_rules))
-               return NULL;
-       return tsp->smk_rules.next;
+       return smk_seq_start(s, pos, &tsp->smk_rules);
 }
 
 static void *load_self_seq_next(struct seq_file *s, void *v, loff_t *pos)
 {
        struct task_smack *tsp = current_security();
-       struct list_head *list = v;
 
-       if (list_is_last(list, &tsp->smk_rules)) {
-               *pos = SEQ_READ_FINISHED;
-               return NULL;
-       }
-       return list->next;
+       return smk_seq_next(s, v, pos, &tsp->smk_rules);
 }
 
 static int load_self_seq_show(struct seq_file *s, void *v)
@@ -1376,16 +1421,11 @@ static int load_self_seq_show(struct seq_file *s, void *v)
        return 0;
 }
 
-static void load_self_seq_stop(struct seq_file *s, void *v)
-{
-       /* No-op */
-}
-
 static const struct seq_operations load_self_seq_ops = {
        .start = load_self_seq_start,
        .next  = load_self_seq_next,
        .show  = load_self_seq_show,
-       .stop  = load_self_seq_stop,
+       .stop  = smk_seq_stop,
 };
 
 
@@ -1425,6 +1465,44 @@ static const struct file_operations smk_load_self_ops = {
        .write          = smk_write_load_self,
        .release        = seq_release,
 };
+
+/**
+ * smk_write_access - handle access check transaction
+ * @file: file pointer
+ * @buf: data from user space
+ * @count: bytes sent
+ * @ppos: where to start - must be 0
+ */
+static ssize_t smk_write_access(struct file *file, const char __user *buf,
+                               size_t count, loff_t *ppos)
+{
+       struct smack_rule rule;
+       char *data;
+       int res;
+
+       data = simple_transaction_get(file, buf, count);
+       if (IS_ERR(data))
+               return PTR_ERR(data);
+
+       if (count < SMK_LOADLEN || smk_parse_rule(data, &rule, 0))
+               return -EINVAL;
+
+       res = smk_access(rule.smk_subject, rule.smk_object, rule.smk_access,
+                         NULL);
+       data[0] = res == 0 ? '1' : '0';
+       data[1] = '\0';
+
+       simple_transaction_set(file, 2);
+       return SMK_LOADLEN;
+}
+
+static const struct file_operations smk_access_ops = {
+       .write          = smk_write_access,
+       .read           = simple_transaction_read,
+       .release        = simple_transaction_release,
+       .llseek         = generic_file_llseek,
+};
+
 /**
  * smk_fill_super - fill the /smackfs superblock
  * @sb: the empty superblock
@@ -1459,6 +1537,8 @@ static int smk_fill_super(struct super_block *sb, void *data, int silent)
                        "logging", &smk_logging_ops, S_IRUGO|S_IWUSR},
                [SMK_LOAD_SELF] = {
                        "load-self", &smk_load_self_ops, S_IRUGO|S_IWUGO},
+               [SMK_ACCESSES] = {
+                       "access", &smk_access_ops, S_IRUGO|S_IWUGO},
                /* last one */
                        {""}
        };