diff --git a/include/linux/xattr.h b/include/linux/xattr.h index 351c7901d74abe3ac18c28fa64c19d5661f66ec5..e6131ef98d8fb94de08edb0db70ad39055e47cec 100644 --- a/include/linux/xattr.h +++ b/include/linux/xattr.h @@ -41,10 +41,12 @@ #define XATTR_SMACK_IPIN "SMACK64IPIN" #define XATTR_SMACK_IPOUT "SMACK64IPOUT" #define XATTR_SMACK_EXEC "SMACK64EXEC" +#define XATTR_SMACK_TRANSMUTE "SMACK64TRANSMUTE" #define XATTR_NAME_SMACK XATTR_SECURITY_PREFIX XATTR_SMACK_SUFFIX #define XATTR_NAME_SMACKIPIN XATTR_SECURITY_PREFIX XATTR_SMACK_IPIN #define XATTR_NAME_SMACKIPOUT XATTR_SECURITY_PREFIX XATTR_SMACK_IPOUT #define XATTR_NAME_SMACKEXEC XATTR_SECURITY_PREFIX XATTR_SMACK_EXEC +#define XATTR_NAME_SMACKTRANSMUTE XATTR_SECURITY_PREFIX XATTR_SMACK_TRANSMUTE #define XATTR_CAPS_SUFFIX "capability" #define XATTR_NAME_CAPS XATTR_SECURITY_PREFIX XATTR_CAPS_SUFFIX diff --git a/security/smack/smack.h b/security/smack/smack.h index a2e2cdfab4ef2004c4e2820ba79f4a438abc8194..129c4eb8ffb10cbba9dce59ef4e5761624d19f7e 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -62,6 +62,7 @@ struct task_smack { }; #define SMK_INODE_INSTANT 0x01 /* inode is instantiated */ +#define SMK_INODE_TRANSMUTE 0x02 /* directory is transmuting */ /* * A label access rule. @@ -166,6 +167,10 @@ struct smack_known { #define SMACK_CIPSO_MAXLEVEL 255 /* CIPSO 2.2 standard */ #define SMACK_CIPSO_MAXCATNUM 239 /* CIPSO 2.2 standard */ +/* + * Flag for transmute access + */ +#define MAY_TRANSMUTE 64 /* * Just to make the common cases easier to deal with */ @@ -197,6 +202,7 @@ struct inode_smack *new_inode_smack(char *); /* * These functions are in smack_access.c */ +int smk_access_entry(char *, char *); 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 *); @@ -239,6 +245,15 @@ static inline void smack_catset_bit(int cat, char *catsetp) catsetp[(cat - 1) / 8] |= 0x80 >> ((cat - 1) % 8); } +/* + * Is the directory transmuting? + */ +static inline int smk_inode_transmutable(const struct inode *isp) +{ + struct inode_smack *sip = isp->i_security; + return (sip->smk_flags & SMK_INODE_TRANSMUTE) != 0; +} + /* * Present a pointer to the smack label in an inode blob. */ @@ -265,7 +280,7 @@ static inline char *smk_of_forked(const struct task_smack *tsp) } /* - * Present a pointer to the smack label in the curren task blob. + * Present a pointer to the smack label in the current task blob. */ static inline char *smk_of_current(void) { diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c index 42becbc1ce3322943e5f65e45928ae059db6fcbe..7ba8478f599e9c0b2e6ae01c88fa4dd0340c2c27 100644 --- a/security/smack/smack_access.c +++ b/security/smack/smack_access.c @@ -66,6 +66,46 @@ static u32 smack_next_secid = 10; */ int log_policy = SMACK_AUDIT_DENIED; +/** + * smk_access_entry - look up matching access rule + * @subject_label: a pointer to the subject's Smack label + * @object_label: a pointer to the object's Smack label + * + * This function looks up the subject/object pair in the + * access rule list and returns pointer to the matching rule if found, + * NULL otherwise. + * + * 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. + */ +int smk_access_entry(char *subject_label, char *object_label) +{ + u32 may = MAY_NOT; + struct smack_rule *srp; + + rcu_read_lock(); + list_for_each_entry_rcu(srp, &smack_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; + } + } + } + rcu_read_unlock(); + + return may; +} + /** * smk_access - determine if a subject has a specific access to an object * @subject_label: a pointer to the subject's Smack label @@ -90,7 +130,6 @@ int smk_access(char *subject_label, char *object_label, int request, struct smk_audit_info *a) { u32 may = MAY_NOT; - struct smack_rule *srp; int rc = 0; /* @@ -144,18 +183,7 @@ int smk_access(char *subject_label, char *object_label, int request, * access (e.g. read is included in readwrite) it's * good. */ - rcu_read_lock(); - list_for_each_entry_rcu(srp, &smack_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; - } - } - } - rcu_read_unlock(); + may = smk_access_entry(subject_label, object_label); /* * This is a bit map operation. */ diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 7e19afe0e7388203368e1950cd04cbefab907eac..05dc4da2e25f435e90527c15bf19e7c4d7b41c4d 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -3,12 +3,14 @@ * * This file contains the smack hook function implementations. * - * Author: + * Authors: * Casey Schaufler + * Jarkko Sakkinen * * Copyright (C) 2007 Casey Schaufler * Copyright (C) 2009 Hewlett-Packard Development Company, L.P. * Paul Moore + * Copyright (C) 2010 Nokia 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, @@ -35,6 +37,9 @@ #define task_security(task) (task_cred_xxx((task), security)) +#define TRANS_TRUE "TRUE" +#define TRANS_TRUE_SIZE 4 + /** * smk_fetch - Fetch the smack label from a file. * @ip: a pointer to the inode @@ -468,6 +473,8 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir, char **name, void **value, size_t *len) { char *isp = smk_of_inode(inode); + char *dsp = smk_of_inode(dir); + u32 may; if (name) { *name = kstrdup(XATTR_SMACK_SUFFIX, GFP_KERNEL); @@ -476,6 +483,16 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir, } if (value) { + may = smk_access_entry(smk_of_current(), dsp); + + /* + * If the access rule allows transmutation and + * the directory requests transmutation then + * by all means transmute. + */ + if (((may & MAY_TRANSMUTE) != 0) && smk_inode_transmutable(dir)) + isp = dsp; + *value = kstrdup(isp, GFP_KERNEL); if (*value == NULL) return -ENOMEM; @@ -709,6 +726,12 @@ static int smack_inode_setxattr(struct dentry *dentry, const char *name, if (size == 0 || size >= SMK_LABELLEN || smk_import(value, size) == NULL) rc = -EINVAL; + } else if (strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0) { + if (!capable(CAP_MAC_ADMIN)) + rc = -EPERM; + 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); @@ -735,35 +758,23 @@ static int smack_inode_setxattr(struct dentry *dentry, const char *name, static void smack_inode_post_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags) { - struct inode_smack *isp; char *nsp; - - /* - * Not SMACK or SMACKEXEC - */ - if (strcmp(name, XATTR_NAME_SMACK) && - strcmp(name, XATTR_NAME_SMACKEXEC)) - return; - - isp = dentry->d_inode->i_security; - - /* - * No locking is done here. This is a pointer - * assignment. - */ - nsp = smk_import(value, size); + struct inode_smack *isp = dentry->d_inode->i_security; if (strcmp(name, XATTR_NAME_SMACK) == 0) { + nsp = smk_import(value, size); if (nsp != NULL) isp->smk_inode = nsp; else isp->smk_inode = smack_known_invalid.smk_known; - } else { + } else if (strcmp(name, XATTR_NAME_SMACKEXEC) == 0) { + nsp = smk_import(value, size); if (nsp != NULL) isp->smk_task = nsp; else isp->smk_task = smack_known_invalid.smk_known; - } + } else if (strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0) + isp->smk_flags |= SMK_INODE_TRANSMUTE; return; } @@ -803,7 +814,8 @@ static int smack_inode_removexattr(struct dentry *dentry, const char *name) 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_SMACKEXEC) == 0 || + strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0) { if (!capable(CAP_MAC_ADMIN)) rc = -EPERM; } else @@ -2274,6 +2286,8 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) char *csp = smk_of_current(); char *fetched; char *final; + char trattr[TRANS_TRUE_SIZE]; + int transflag = 0; struct dentry *dp; if (inode == NULL) @@ -2392,10 +2406,19 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) */ dp = dget(opt_dentry); fetched = smk_fetch(XATTR_NAME_SMACK, inode, dp); - if (fetched != NULL) + if (fetched != NULL) { final = fetched; - isp->smk_task = smk_fetch(XATTR_NAME_SMACKEXEC, inode, - dp); + if (S_ISDIR(inode->i_mode)) { + trattr[0] = '\0'; + inode->i_op->getxattr(dp, + XATTR_NAME_SMACKTRANSMUTE, + trattr, TRANS_TRUE_SIZE); + if (strncmp(trattr, TRANS_TRUE, + TRANS_TRUE_SIZE) == 0) + transflag = SMK_INODE_TRANSMUTE; + } + } + isp->smk_task = smk_fetch(XATTR_NAME_SMACKEXEC, inode, dp); dput(dp); break; @@ -2406,7 +2429,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) else isp->smk_inode = final; - isp->smk_flags |= SMK_INODE_INSTANT; + isp->smk_flags |= (SMK_INODE_INSTANT | transflag); unlockandout: mutex_unlock(&isp->smk_lock); @@ -2456,6 +2479,7 @@ static int smack_setprocattr(struct task_struct *p, char *name, void *value, size_t size) { struct task_smack *tsp; + struct task_smack *oldtsp; struct cred *new; char *newsmack; @@ -2485,6 +2509,7 @@ static int smack_setprocattr(struct task_struct *p, char *name, if (newsmack == smack_known_web.smk_known) return -EPERM; + oldtsp = p->cred->security; new = prepare_creds(); if (new == NULL) return -ENOMEM; @@ -2494,6 +2519,7 @@ static int smack_setprocattr(struct task_struct *p, char *name, return -ENOMEM; } tsp->smk_task = newsmack; + tsp->smk_forked = oldtsp->smk_forked; new->security = tsp; commit_creds(new); return size; diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index 01a0be93d8d08934888c6e6eceec2f68a6f29f79..362d5eda948be93a0671127b85dc55f8e2a04c95 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -109,9 +109,12 @@ const char *smack_cipso_option = SMACK_CIPSO_OPTION; * SMK_ACCESSLEN: Maximum length for a rule access field * SMK_LOADLEN: Smack rule length */ -#define SMK_ACCESS "rwxa" -#define SMK_ACCESSLEN (sizeof(SMK_ACCESS) - 1) -#define SMK_LOADLEN (SMK_LABELLEN + SMK_LABELLEN + SMK_ACCESSLEN) +#define SMK_OACCESS "rwxa" +#define SMK_ACCESS "rwxat" +#define SMK_OACCESSLEN (sizeof(SMK_OACCESS) - 1) +#define SMK_ACCESSLEN (sizeof(SMK_ACCESS) - 1) +#define SMK_OLOADLEN (SMK_LABELLEN + SMK_LABELLEN + SMK_OACCESSLEN) +#define SMK_LOADLEN (SMK_LABELLEN + SMK_LABELLEN + SMK_ACCESSLEN) /** * smk_netlabel_audit_set - fill a netlbl_audit struct @@ -175,6 +178,8 @@ static int load_seq_show(struct seq_file *s, void *v) seq_putc(s, 'x'); if (srp->smk_access & MAY_APPEND) seq_putc(s, 'a'); + if (srp->smk_access & MAY_TRANSMUTE) + seq_putc(s, 't'); if (srp->smk_access == 0) seq_putc(s, '-'); @@ -273,10 +278,15 @@ static ssize_t smk_write_load(struct file *file, const char __user *buf, if (!capable(CAP_MAC_ADMIN)) return -EPERM; - if (*ppos != 0 || count != SMK_LOADLEN) + if (*ppos != 0) + return -EINVAL; + /* + * Minor hack for backward compatability + */ + if (count < (SMK_OLOADLEN) || count > SMK_LOADLEN) return -EINVAL; - data = kzalloc(count, GFP_KERNEL); + data = kzalloc(SMK_LOADLEN, GFP_KERNEL); if (data == NULL) return -ENOMEM; @@ -285,6 +295,12 @@ static ssize_t smk_write_load(struct file *file, const char __user *buf, goto out; } + /* + * More on the minor hack for backward compatability + */ + if (count == (SMK_OLOADLEN)) + data[SMK_OLOADLEN] = '-'; + rule = kzalloc(sizeof(*rule), GFP_KERNEL); if (rule == NULL) { rc = -ENOMEM; @@ -345,6 +361,17 @@ static ssize_t smk_write_load(struct file *file, const char __user *buf, 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; + } + rc = smk_set_access(rule); if (!rc)