SMACK: Fix handling value==NULL in post setxattr
[cascardo/linux.git] / security / smack / smack_lsm.c
index b0be893..a5d86ff 100644 (file)
@@ -157,6 +157,74 @@ static int smk_copy_rules(struct list_head *nhead, struct list_head *ohead,
        return rc;
 }
 
+/**
+ * smk_ptrace_mode - helper function for converting PTRACE_MODE_* into MAY_*
+ * @mode - input mode in form of PTRACE_MODE_*
+ *
+ * Returns a converted MAY_* mode usable by smack rules
+ */
+static inline unsigned int smk_ptrace_mode(unsigned int mode)
+{
+       switch (mode) {
+       case PTRACE_MODE_READ:
+               return MAY_READ;
+       case PTRACE_MODE_ATTACH:
+               return MAY_READWRITE;
+       }
+
+       return 0;
+}
+
+/**
+ * smk_ptrace_rule_check - helper for ptrace access
+ * @tracer: tracer process
+ * @tracee_label: label of the process that's about to be traced,
+ *                the pointer must originate from smack structures
+ * @mode: ptrace attachment mode (PTRACE_MODE_*)
+ * @func: name of the function that called us, used for audit
+ *
+ * Returns 0 on access granted, -error on error
+ */
+static int smk_ptrace_rule_check(struct task_struct *tracer, char *tracee_label,
+                                unsigned int mode, const char *func)
+{
+       int rc;
+       struct smk_audit_info ad, *saip = NULL;
+       struct task_smack *tsp;
+       struct smack_known *skp;
+
+       if ((mode & PTRACE_MODE_NOAUDIT) == 0) {
+               smk_ad_init(&ad, func, LSM_AUDIT_DATA_TASK);
+               smk_ad_setfield_u_tsk(&ad, tracer);
+               saip = &ad;
+       }
+
+       tsp = task_security(tracer);
+       skp = smk_of_task(tsp);
+
+       if ((mode & PTRACE_MODE_ATTACH) &&
+           (smack_ptrace_rule == SMACK_PTRACE_EXACT ||
+            smack_ptrace_rule == SMACK_PTRACE_DRACONIAN)) {
+               if (skp->smk_known == tracee_label)
+                       rc = 0;
+               else if (smack_ptrace_rule == SMACK_PTRACE_DRACONIAN)
+                       rc = -EACCES;
+               else if (capable(CAP_SYS_PTRACE))
+                       rc = 0;
+               else
+                       rc = -EACCES;
+
+               if (saip)
+                       smack_log(skp->smk_known, tracee_label, 0, rc, saip);
+
+               return rc;
+       }
+
+       /* In case of rule==SMACK_PTRACE_DEFAULT or mode==PTRACE_MODE_READ */
+       rc = smk_tskacc(tsp, tracee_label, smk_ptrace_mode(mode), saip);
+       return rc;
+}
+
 /*
  * LSM hooks.
  * We he, that is fun!
@@ -165,16 +233,15 @@ static int smk_copy_rules(struct list_head *nhead, struct list_head *ohead,
 /**
  * smack_ptrace_access_check - Smack approval on PTRACE_ATTACH
  * @ctp: child task pointer
- * @mode: ptrace attachment mode
+ * @mode: ptrace attachment mode (PTRACE_MODE_*)
  *
  * Returns 0 if access is OK, an error code otherwise
  *
- * Do the capability checks, and require read and write.
+ * Do the capability checks.
  */
 static int smack_ptrace_access_check(struct task_struct *ctp, unsigned int mode)
 {
        int rc;
-       struct smk_audit_info ad;
        struct smack_known *skp;
 
        rc = cap_ptrace_access_check(ctp, mode);
@@ -182,10 +249,8 @@ static int smack_ptrace_access_check(struct task_struct *ctp, unsigned int mode)
                return rc;
 
        skp = smk_of_task(task_security(ctp));
-       smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK);
-       smk_ad_setfield_u_tsk(&ad, ctp);
 
-       rc = smk_curacc(skp->smk_known, mode, &ad);
+       rc = smk_ptrace_rule_check(current, skp->smk_known, mode, __func__);
        return rc;
 }
 
@@ -195,23 +260,21 @@ static int smack_ptrace_access_check(struct task_struct *ctp, unsigned int mode)
  *
  * Returns 0 if access is OK, an error code otherwise
  *
- * Do the capability checks, and require read and write.
+ * Do the capability checks, and require PTRACE_MODE_ATTACH.
  */
 static int smack_ptrace_traceme(struct task_struct *ptp)
 {
        int rc;
-       struct smk_audit_info ad;
        struct smack_known *skp;
 
        rc = cap_ptrace_traceme(ptp);
        if (rc != 0)
                return rc;
 
-       skp = smk_of_task(task_security(ptp));
-       smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK);
-       smk_ad_setfield_u_tsk(&ad, ptp);
+       skp = smk_of_task(current_security());
 
-       rc = smk_curacc(skp->smk_known, MAY_READWRITE, &ad);
+       rc = smk_ptrace_rule_check(ptp, skp->smk_known,
+                                  PTRACE_MODE_ATTACH, __func__);
        return rc;
 }
 
@@ -219,8 +282,6 @@ static int smack_ptrace_traceme(struct task_struct *ptp)
  * smack_syslog - Smack approval on syslog
  * @type: message type
  *
- * Require that the task has the floor label
- *
  * Returns 0 on success, error code otherwise.
  */
 static int smack_syslog(int typefrom_file)
@@ -231,7 +292,7 @@ static int smack_syslog(int typefrom_file)
        if (smack_privileged(CAP_MAC_OVERRIDE))
                return 0;
 
-        if (skp != &smack_known_floor)
+       if (smack_syslog_label != NULL && smack_syslog_label != skp)
                rc = -EACCES;
 
        return rc;
@@ -341,10 +402,12 @@ static int smack_sb_kern_mount(struct super_block *sb, int flags, void *data)
        struct inode *inode = root->d_inode;
        struct superblock_smack *sp = sb->s_security;
        struct inode_smack *isp;
+       struct smack_known *skp;
        char *op;
        char *commap;
        char *nsp;
        int transmute = 0;
+       int specified = 0;
 
        if (sp->smk_initialized)
                return 0;
@@ -359,41 +422,65 @@ static int smack_sb_kern_mount(struct super_block *sb, int flags, void *data)
                if (strncmp(op, SMK_FSHAT, strlen(SMK_FSHAT)) == 0) {
                        op += strlen(SMK_FSHAT);
                        nsp = smk_import(op, 0);
-                       if (nsp != NULL)
+                       if (nsp != NULL) {
                                sp->smk_hat = nsp;
+                               specified = 1;
+                       }
                } else if (strncmp(op, SMK_FSFLOOR, strlen(SMK_FSFLOOR)) == 0) {
                        op += strlen(SMK_FSFLOOR);
                        nsp = smk_import(op, 0);
-                       if (nsp != NULL)
+                       if (nsp != NULL) {
                                sp->smk_floor = nsp;
+                               specified = 1;
+                       }
                } else if (strncmp(op, SMK_FSDEFAULT,
                                   strlen(SMK_FSDEFAULT)) == 0) {
                        op += strlen(SMK_FSDEFAULT);
                        nsp = smk_import(op, 0);
-                       if (nsp != NULL)
+                       if (nsp != NULL) {
                                sp->smk_default = nsp;
+                               specified = 1;
+                       }
                } else if (strncmp(op, SMK_FSROOT, strlen(SMK_FSROOT)) == 0) {
                        op += strlen(SMK_FSROOT);
                        nsp = smk_import(op, 0);
-                       if (nsp != NULL)
+                       if (nsp != NULL) {
                                sp->smk_root = nsp;
+                               specified = 1;
+                       }
                } else if (strncmp(op, SMK_FSTRANS, strlen(SMK_FSTRANS)) == 0) {
                        op += strlen(SMK_FSTRANS);
                        nsp = smk_import(op, 0);
                        if (nsp != NULL) {
                                sp->smk_root = nsp;
                                transmute = 1;
+                               specified = 1;
                        }
                }
        }
 
+       if (!smack_privileged(CAP_MAC_ADMIN)) {
+               /*
+                * Unprivileged mounts don't get to specify Smack values.
+                */
+               if (specified)
+                       return -EPERM;
+               /*
+                * Unprivileged mounts get root and default from the caller.
+                */
+               skp = smk_of_current();
+               sp->smk_root = skp->smk_known;
+               sp->smk_default = skp->smk_known;
+       }
        /*
         * Initialize the root inode.
         */
        isp = inode->i_security;
-       if (inode->i_security == NULL) {
-               inode->i_security = new_inode_smack(sp->smk_root);
-               isp = inode->i_security;
+       if (isp == NULL) {
+               isp = new_inode_smack(sp->smk_root);
+               if (isp == NULL)
+                       return -ENOMEM;
+               inode->i_security = isp;
        } else
                isp->smk_inode = sp->smk_root;
 
@@ -423,53 +510,6 @@ static int smack_sb_statfs(struct dentry *dentry)
        return rc;
 }
 
-/**
- * smack_sb_mount - Smack check for mounting
- * @dev_name: unused
- * @path: mount point
- * @type: unused
- * @flags: unused
- * @data: unused
- *
- * Returns 0 if current can write the floor of the filesystem
- * being mounted on, an error code otherwise.
- */
-static int smack_sb_mount(const char *dev_name, struct path *path,
-                         const char *type, unsigned long flags, void *data)
-{
-       struct superblock_smack *sbp = path->dentry->d_sb->s_security;
-       struct smk_audit_info ad;
-
-       smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH);
-       smk_ad_setfield_u_fs_path(&ad, *path);
-
-       return smk_curacc(sbp->smk_floor, MAY_WRITE, &ad);
-}
-
-/**
- * smack_sb_umount - Smack check for unmounting
- * @mnt: file system to unmount
- * @flags: unused
- *
- * Returns 0 if current can write the floor of the filesystem
- * being unmounted, an error code otherwise.
- */
-static int smack_sb_umount(struct vfsmount *mnt, int flags)
-{
-       struct superblock_smack *sbp;
-       struct smk_audit_info ad;
-       struct path path;
-
-       path.dentry = mnt->mnt_root;
-       path.mnt = mnt;
-
-       smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH);
-       smk_ad_setfield_u_fs_path(&ad, path);
-
-       sbp = path.dentry->d_sb->s_security;
-       return smk_curacc(sbp->smk_floor, MAY_WRITE, &ad);
-}
-
 /*
  * BPRM hooks
  */
@@ -478,7 +518,7 @@ static int smack_sb_umount(struct vfsmount *mnt, int flags)
  * smack_bprm_set_creds - set creds for exec
  * @bprm: the exec information
  *
- * Returns 0 if it gets a blob, -ENOMEM otherwise
+ * Returns 0 if it gets a blob, -EPERM if exec forbidden and -ENOMEM otherwise
  */
 static int smack_bprm_set_creds(struct linux_binprm *bprm)
 {
@@ -498,7 +538,22 @@ static int smack_bprm_set_creds(struct linux_binprm *bprm)
        if (isp->smk_task == NULL || isp->smk_task == bsp->smk_task)
                return 0;
 
-       if (bprm->unsafe)
+       if (bprm->unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) {
+               struct task_struct *tracer;
+               rc = 0;
+
+               rcu_read_lock();
+               tracer = ptrace_parent(current);
+               if (likely(tracer != NULL))
+                       rc = smk_ptrace_rule_check(tracer,
+                                                  isp->smk_task->smk_known,
+                                                  PTRACE_MODE_ATTACH,
+                                                  __func__);
+               rcu_read_unlock();
+
+               if (rc != 0)
+                       return rc;
+       } else if (bprm->unsafe)
                return -EPERM;
 
        bsp->smk_task = isp->smk_task;
@@ -837,31 +892,43 @@ static int smack_inode_setxattr(struct dentry *dentry, const char *name,
                                const void *value, size_t size, int flags)
 {
        struct smk_audit_info ad;
+       struct smack_known *skp;
+       int check_priv = 0;
+       int check_import = 0;
+       int check_star = 0;
        int rc = 0;
 
+       /*
+        * Check label validity here so import won't fail in post_setxattr
+        */
        if (strcmp(name, XATTR_NAME_SMACK) == 0 ||
            strcmp(name, XATTR_NAME_SMACKIPIN) == 0 ||
-           strcmp(name, XATTR_NAME_SMACKIPOUT) == 0 ||
-           strcmp(name, XATTR_NAME_SMACKEXEC) == 0 ||
-           strcmp(name, XATTR_NAME_SMACKMMAP) == 0) {
-               if (!smack_privileged(CAP_MAC_ADMIN))
-                       rc = -EPERM;
-               /*
-                * check label validity here so import wont fail on
-                * post_setxattr
-                */
-               if (size == 0 || size >= SMK_LONGLABEL ||
-                   smk_import(value, size) == NULL)
-                       rc = -EINVAL;
+           strcmp(name, XATTR_NAME_SMACKIPOUT) == 0) {
+               check_priv = 1;
+               check_import = 1;
+       } else if (strcmp(name, XATTR_NAME_SMACKEXEC) == 0 ||
+                  strcmp(name, XATTR_NAME_SMACKMMAP) == 0) {
+               check_priv = 1;
+               check_import = 1;
+               check_star = 1;
        } else if (strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0) {
-               if (!smack_privileged(CAP_MAC_ADMIN))
-                       rc = -EPERM;
+               check_priv = 1;
                if (size != TRANS_TRUE_SIZE ||
                    strncmp(value, TRANS_TRUE, TRANS_TRUE_SIZE) != 0)
                        rc = -EINVAL;
        } else
                rc = cap_inode_setxattr(dentry, name, value, size, flags);
 
+       if (check_priv && !smack_privileged(CAP_MAC_ADMIN))
+               rc = -EPERM;
+
+       if (rc == 0 && check_import) {
+               skp = smk_import_entry(value, size);
+               if (skp == NULL || (check_star &&
+                   (skp == &smack_known_star || skp == &smack_known_web)))
+                       rc = -EINVAL;
+       }
+
        smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY);
        smk_ad_setfield_u_fs_path_dentry(&ad, dentry);
 
@@ -893,18 +960,20 @@ static void smack_inode_post_setxattr(struct dentry *dentry, const char *name,
                return;
        }
 
-       skp = smk_import_entry(value, size);
        if (strcmp(name, XATTR_NAME_SMACK) == 0) {
+               skp = smk_import_entry(value, size);
                if (skp != NULL)
                        isp->smk_inode = skp->smk_known;
                else
                        isp->smk_inode = smack_known_invalid.smk_known;
        } else if (strcmp(name, XATTR_NAME_SMACKEXEC) == 0) {
+               skp = smk_import_entry(value, size);
                if (skp != NULL)
                        isp->smk_task = skp;
                else
                        isp->smk_task = &smack_known_invalid;
        } else if (strcmp(name, XATTR_NAME_SMACKMMAP) == 0) {
+               skp = smk_import_entry(value, size);
                if (skp != NULL)
                        isp->smk_mmap = skp;
                else
@@ -951,7 +1020,7 @@ static int smack_inode_removexattr(struct dentry *dentry, const char *name)
            strcmp(name, XATTR_NAME_SMACKIPOUT) == 0 ||
            strcmp(name, XATTR_NAME_SMACKEXEC) == 0 ||
            strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0 ||
-           strcmp(name, XATTR_NAME_SMACKMMAP)) {
+           strcmp(name, XATTR_NAME_SMACKMMAP) == 0) {
                if (!smack_privileged(CAP_MAC_ADMIN))
                        rc = -EPERM;
        } else
@@ -1364,7 +1433,7 @@ static int smack_file_receive(struct file *file)
        int may = 0;
        struct smk_audit_info ad;
 
-       smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK);
+       smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH);
        smk_ad_setfield_u_fs_path(&ad, file->f_path);
        /*
         * This code relies on bitmasks.
@@ -2089,7 +2158,7 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name,
        int rc = 0;
 
        if (value == NULL || size > SMK_LONGLABEL || size == 0)
-               return -EACCES;
+               return -EINVAL;
 
        skp = smk_import_entry(value, size);
        if (skp == NULL)
@@ -2847,8 +2916,17 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
                        if (rc >= 0)
                                transflag = SMK_INODE_TRANSMUTE;
                }
-               isp->smk_task = smk_fetch(XATTR_NAME_SMACKEXEC, inode, dp);
-               isp->smk_mmap = smk_fetch(XATTR_NAME_SMACKMMAP, inode, dp);
+               /*
+                * Don't let the exec or mmap label be "*" or "@".
+                */
+               skp = smk_fetch(XATTR_NAME_SMACKEXEC, inode, dp);
+               if (skp == &smack_known_star || skp == &smack_known_web)
+                       skp = NULL;
+               isp->smk_task = skp;
+               skp = smk_fetch(XATTR_NAME_SMACKMMAP, inode, dp);
+               if (skp == &smack_known_star || skp == &smack_known_web)
+                       skp = NULL;
+               isp->smk_mmap = skp;
 
                dput(dp);
                break;
@@ -3743,8 +3821,6 @@ struct security_operations smack_ops = {
        .sb_copy_data =                 smack_sb_copy_data,
        .sb_kern_mount =                smack_sb_kern_mount,
        .sb_statfs =                    smack_sb_statfs,
-       .sb_mount =                     smack_sb_mount,
-       .sb_umount =                    smack_sb_umount,
 
        .bprm_set_creds =               smack_bprm_set_creds,
        .bprm_committing_creds =        smack_bprm_committing_creds,