On 6/2/2015 2:23 AM, Rafal Krypa wrote: > Smack onlycap allows limiting of CAP_MAC_ADMIN and CAP_MAC_OVERRIDE to > processes running with the configured label. But having single privileged > label is not enough in some real use cases. On a complex system like Tizen, > there maybe few programs that need to configure Smack policy in run-time > and running them all with a single label is not always practical. > This patch extends onlycap feature for multiple labels. They are configured > in the same smackfs "onlycap" interface, separated by spaces. > > Signed-off-by: Rafal Krypa <r.kr...@samsung.com>
Applied to https://github.com/cschaufler/smack-next.git#smack-for-4.2-stacked > --- > Documentation/security/Smack.txt | 6 +- > security/smack/smack.h | 25 +++--- > security/smack/smack_access.c | 41 ++++++++++ > security/smack/smackfs.c | 163 > ++++++++++++++++++++++++++------------- > 4 files changed, 163 insertions(+), 72 deletions(-) > > diff --git a/Documentation/security/Smack.txt > b/Documentation/security/Smack.txt > index abc82f8..de5e1ae 100644 > --- a/Documentation/security/Smack.txt > +++ b/Documentation/security/Smack.txt > @@ -206,11 +206,11 @@ netlabel > label. The format accepted on write is: > "%d.%d.%d.%d label" or "%d.%d.%d.%d/%d label". > onlycap > - This contains the label processes must have for CAP_MAC_ADMIN > + This contains labels processes must have for CAP_MAC_ADMIN > and CAP_MAC_OVERRIDE to be effective. If this file is empty > these capabilities are effective at for processes with any > - label. The value is set by writing the desired label to the > - file or cleared by writing "-" to the file. > + label. The values are set by writing the desired labels, separated > + by spaces, to the file or cleared by writing "-" to the file. > ptrace > This is used to define the current ptrace policy > 0 - default: this is the policy that relies on Smack access rules. > diff --git a/security/smack/smack.h b/security/smack/smack.h > index b8c1a86..244e035 100644 > --- a/security/smack/smack.h > +++ b/security/smack/smack.h > @@ -138,6 +138,11 @@ struct smk_port_label { > struct smack_known *smk_out; /* outgoing label */ > }; > > +struct smack_onlycap { > + struct list_head list; > + struct smack_known *smk_label; > +}; > + > /* > * Mount options > */ > @@ -249,6 +254,7 @@ int smk_netlbl_mls(int, char *, struct netlbl_lsm_secattr > *, int); > struct smack_known *smk_import_entry(const char *, int); > void smk_insert_entry(struct smack_known *skp); > struct smack_known *smk_find_entry(const char *); > +int smack_privileged(int cap); > > /* > * Shared data. > @@ -257,7 +263,6 @@ extern int smack_enabled; > extern int smack_cipso_direct; > extern int smack_cipso_mapped; > extern struct smack_known *smack_net_ambient; > -extern struct smack_known *smack_onlycap; > extern struct smack_known *smack_syslog_label; > #ifdef CONFIG_SECURITY_SMACK_BRINGUP > extern struct smack_known *smack_unconfined; > @@ -276,6 +281,9 @@ extern struct mutex smack_known_lock; > extern struct list_head smack_known_list; > extern struct list_head smk_netlbladdr_list; > > +extern struct mutex smack_onlycap_lock; > +extern struct list_head smack_onlycap_list; > + > #define SMACK_HASH_SLOTS 16 > extern struct hlist_head smack_known_hash[SMACK_HASH_SLOTS]; > > @@ -332,21 +340,6 @@ static inline struct smack_known *smk_of_current(void) > } > > /* > - * Is the task privileged and allowed to be privileged > - * by the onlycap rule. > - */ > -static inline int smack_privileged(int cap) > -{ > - struct smack_known *skp = smk_of_current(); > - > - if (!capable(cap)) > - return 0; > - if (smack_onlycap == NULL || smack_onlycap == skp) > - return 1; > - return 0; > -} > - > -/* > * logging functions > */ > #define SMACK_AUDIT_DENIED 0x1 > diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c > index 408e20b..00f6b38 100644 > --- a/security/smack/smack_access.c > +++ b/security/smack/smack_access.c > @@ -617,3 +617,44 @@ struct smack_known *smack_from_secid(const u32 secid) > rcu_read_unlock(); > return &smack_known_invalid; > } > + > +/* > + * Unless a process is running with one of these labels > + * even having CAP_MAC_OVERRIDE isn't enough to grant > + * privilege to violate MAC policy. If no labels are > + * designated (the empty list case) capabilities apply to > + * everyone. > + */ > +LIST_HEAD(smack_onlycap_list); > +DEFINE_MUTEX(smack_onlycap_lock); > + > +/* > + * Is the task privileged and allowed to be privileged > + * by the onlycap rule. > + * > + * Returns 1 if the task is allowed to be privileged, 0 if it's not. > + */ > +int smack_privileged(int cap) > +{ > + struct smack_known *skp = smk_of_current(); > + struct smack_onlycap *sop; > + > + if (!capable(cap)) > + return 0; > + > + rcu_read_lock(); > + if (list_empty(&smack_onlycap_list)) { > + rcu_read_unlock(); > + return 1; > + } > + > + list_for_each_entry_rcu(sop, &smack_onlycap_list, list) { > + if (sop->smk_label == skp) { > + rcu_read_unlock(); > + return 1; > + } > + } > + rcu_read_unlock(); > + > + return 0; > +} > diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c > index e40dc48..f1c22a8 100644 > --- a/security/smack/smackfs.c > +++ b/security/smack/smackfs.c > @@ -87,16 +87,6 @@ int smack_cipso_direct = SMACK_CIPSO_DIRECT_DEFAULT; > */ > int smack_cipso_mapped = SMACK_CIPSO_MAPPED_DEFAULT; > > -/* > - * Unless a process is running with this label even > - * having CAP_MAC_OVERRIDE isn't enough to grant > - * privilege to violate MAC policy. If no label is > - * designated (the NULL case) capabilities apply to > - * everyone. It is expected that the hat (^) label > - * will be used if any label is used. > - */ > -struct smack_known *smack_onlycap; > - > #ifdef CONFIG_SECURITY_SMACK_BRINGUP > /* > * Allow one label to be unconfined. This is for > @@ -1636,34 +1626,79 @@ static const struct file_operations smk_ambient_ops = > { > .llseek = default_llseek, > }; > > -/** > - * smk_read_onlycap - read() for smackfs/onlycap > - * @filp: file pointer, not actually used > - * @buf: where to put the result > - * @cn: maximum to send along > - * @ppos: where to start > - * > - * Returns number of bytes read or error code, as appropriate > +/* > + * Seq_file operations for /smack/onlycap > */ > -static ssize_t smk_read_onlycap(struct file *filp, char __user *buf, > - size_t cn, loff_t *ppos) > +static void *onlycap_seq_start(struct seq_file *s, loff_t *pos) > { > - char *smack = ""; > - ssize_t rc = -EINVAL; > - int asize; > + return smk_seq_start(s, pos, &smack_onlycap_list); > +} > > - if (*ppos != 0) > - return 0; > +static void *onlycap_seq_next(struct seq_file *s, void *v, loff_t *pos) > +{ > + return smk_seq_next(s, v, pos, &smack_onlycap_list); > +} > > - if (smack_onlycap != NULL) > - smack = smack_onlycap->smk_known; > +static int onlycap_seq_show(struct seq_file *s, void *v) > +{ > + struct list_head *list = v; > + struct smack_onlycap *sop = > + list_entry_rcu(list, struct smack_onlycap, list); > > - asize = strlen(smack) + 1; > + seq_puts(s, sop->smk_label->smk_known); > + seq_putc(s, ' '); > > - if (cn >= asize) > - rc = simple_read_from_buffer(buf, cn, ppos, smack, asize); > + return 0; > +} > > - return rc; > +static const struct seq_operations onlycap_seq_ops = { > + .start = onlycap_seq_start, > + .next = onlycap_seq_next, > + .show = onlycap_seq_show, > + .stop = smk_seq_stop, > +}; > + > +static int smk_open_onlycap(struct inode *inode, struct file *file) > +{ > + return seq_open(file, &onlycap_seq_ops); > +} > + > +/** > + * smk_list_swap_rcu - swap public list with a private one in RCU-safe way > + * The caller must hold appropriate mutex to prevent concurrent modifications > + * to the public list. > + * Private list is assumed to be not accessible to other threads yet. > + * > + * @public: public list > + * @private: private list > + */ > +static void smk_list_swap_rcu(struct list_head *public, > + struct list_head *private) > +{ > + struct list_head *first, *last; > + > + if (list_empty(public)) { > + list_splice_init_rcu(private, public, synchronize_rcu); > + } else { > + /* Remember public list before replacing it */ > + first = public->next; > + last = public->prev; > + > + /* Publish private list in place of public in RCU-safe way */ > + private->prev->next = public; > + private->next->prev = public; > + rcu_assign_pointer(public->next, private->next); > + public->prev = private->prev; > + > + synchronize_rcu(); > + > + /* When all readers are done with the old public list, > + * attach it in place of private */ > + private->next = first; > + private->prev = last; > + first->prev = private; > + last->next = private; > + } > } > > /** > @@ -1679,28 +1714,47 @@ static ssize_t smk_write_onlycap(struct file *file, > const char __user *buf, > size_t count, loff_t *ppos) > { > char *data; > - struct smack_known *skp = smk_of_task(current->cred->security); > + char *data_parse; > + char *tok; > + struct smack_known *skp; > + struct smack_onlycap *sop; > + struct smack_onlycap *sop2; > + LIST_HEAD(list_tmp); > int rc = count; > > if (!smack_privileged(CAP_MAC_ADMIN)) > return -EPERM; > > - /* > - * This can be done using smk_access() but is done > - * explicitly for clarity. The smk_access() implementation > - * would use smk_access(smack_onlycap, MAY_WRITE) > - */ > - if (smack_onlycap != NULL && smack_onlycap != skp) > - return -EPERM; > - > data = kzalloc(count + 1, GFP_KERNEL); > if (data == NULL) > return -ENOMEM; > > if (copy_from_user(data, buf, count) != 0) { > - rc = -EFAULT; > - goto freeout; > + kfree(data); > + return -EFAULT; > + } > + > + data_parse = data; > + while ((tok = strsep(&data_parse, " ")) != NULL) { > + if (!*tok) > + continue; > + > + skp = smk_import_entry(tok, 0); > + if (IS_ERR(skp)) { > + rc = PTR_ERR(skp); > + break; > + } > + > + sop = kzalloc(sizeof(*sop), GFP_KERNEL); > + if (sop == NULL) { > + rc = -ENOMEM; > + break; > + } > + > + sop->smk_label = skp; > + list_add_rcu(&sop->list, &list_tmp); > } > + kfree(data); > > /* > * Clear the smack_onlycap on invalid label errors. This means > @@ -1710,26 +1764,29 @@ static ssize_t smk_write_onlycap(struct file *file, > const char __user *buf, > * so "-usecapabilities" will also work. > * > * But do so only on invalid label, not on system errors. > + * The invalid label must be first to count as clearing attempt. > */ > - skp = smk_import_entry(data, count); > - if (PTR_ERR(skp) == -EINVAL) > - skp = NULL; > - else if (IS_ERR(skp)) { > - rc = PTR_ERR(skp); > - goto freeout; > + if (rc == -EINVAL && list_empty(&list_tmp)) > + rc = count; > + > + if (rc >= 0) { > + mutex_lock(&smack_onlycap_lock); > + smk_list_swap_rcu(&smack_onlycap_list, &list_tmp); > + mutex_unlock(&smack_onlycap_lock); > } > > - smack_onlycap = skp; > + list_for_each_entry_safe(sop, sop2, &list_tmp, list) > + kfree(sop); > > -freeout: > - kfree(data); > return rc; > } > > static const struct file_operations smk_onlycap_ops = { > - .read = smk_read_onlycap, > + .open = smk_open_onlycap, > + .read = seq_read, > .write = smk_write_onlycap, > - .llseek = default_llseek, > + .llseek = seq_lseek, > + .release = seq_release, > }; > > #ifdef CONFIG_SECURITY_SMACK_BRINGUP -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/