Commit 5c6d1125f8dbd1bfef39e38fbc2837003be78a59

Authored by Jarkko Sakkinen
Committed by Casey Schaufler
1 parent fe27d4b012

Smack: Transmute labels on specified directories

In a situation where Smack access rules allow processes
with multiple labels to write to a directory it is easy
to get into a situation where the directory gets cluttered
with files that the owner can't deal with because while
they could be written to the directory a process at the
label of the directory can't write them. This is generally
the desired behavior, but when it isn't it is a real
issue.

This patch introduces a new attribute SMACK64TRANSMUTE that
instructs Smack to create the file with the label of the directory
under certain circumstances.

A new access mode, "t" for transmute, is made available to
Smack access rules, which are expanded from "rwxa" to "rwxat".
If a file is created in a directory marked as transmutable
and if access was granted to perform the operation by a rule
that included the transmute mode, then the file gets the
Smack label of the directory instead of the Smack label of the
creating process.

Note that this is equivalent to creating an empty file at the
label of the directory and then having the other process write
to it. The transmute scheme requires that both the access rule
allows transmutation and that the directory be explicitly marked.

Signed-off-by: Jarkko Sakkinen <ext-jarkko.2.sakkinen@nokia.com>
Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>

Showing 5 changed files with 141 additions and 43 deletions Side-by-side Diff

include/linux/xattr.h
... ... @@ -41,10 +41,12 @@
41 41 #define XATTR_SMACK_IPIN "SMACK64IPIN"
42 42 #define XATTR_SMACK_IPOUT "SMACK64IPOUT"
43 43 #define XATTR_SMACK_EXEC "SMACK64EXEC"
  44 +#define XATTR_SMACK_TRANSMUTE "SMACK64TRANSMUTE"
44 45 #define XATTR_NAME_SMACK XATTR_SECURITY_PREFIX XATTR_SMACK_SUFFIX
45 46 #define XATTR_NAME_SMACKIPIN XATTR_SECURITY_PREFIX XATTR_SMACK_IPIN
46 47 #define XATTR_NAME_SMACKIPOUT XATTR_SECURITY_PREFIX XATTR_SMACK_IPOUT
47 48 #define XATTR_NAME_SMACKEXEC XATTR_SECURITY_PREFIX XATTR_SMACK_EXEC
  49 +#define XATTR_NAME_SMACKTRANSMUTE XATTR_SECURITY_PREFIX XATTR_SMACK_TRANSMUTE
48 50  
49 51 #define XATTR_CAPS_SUFFIX "capability"
50 52 #define XATTR_NAME_CAPS XATTR_SECURITY_PREFIX XATTR_CAPS_SUFFIX
security/smack/smack.h
... ... @@ -62,6 +62,7 @@
62 62 };
63 63  
64 64 #define SMK_INODE_INSTANT 0x01 /* inode is instantiated */
  65 +#define SMK_INODE_TRANSMUTE 0x02 /* directory is transmuting */
65 66  
66 67 /*
67 68 * A label access rule.
... ... @@ -167,6 +168,10 @@
167 168 #define SMACK_CIPSO_MAXCATNUM 239 /* CIPSO 2.2 standard */
168 169  
169 170 /*
  171 + * Flag for transmute access
  172 + */
  173 +#define MAY_TRANSMUTE 64
  174 +/*
170 175 * Just to make the common cases easier to deal with
171 176 */
172 177 #define MAY_ANY (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC)
... ... @@ -197,6 +202,7 @@
197 202 /*
198 203 * These functions are in smack_access.c
199 204 */
  205 +int smk_access_entry(char *, char *);
200 206 int smk_access(char *, char *, int, struct smk_audit_info *);
201 207 int smk_curacc(char *, u32, struct smk_audit_info *);
202 208 int smack_to_cipso(const char *, struct smack_cipso *);
... ... @@ -240,6 +246,15 @@
240 246 }
241 247  
242 248 /*
  249 + * Is the directory transmuting?
  250 + */
  251 +static inline int smk_inode_transmutable(const struct inode *isp)
  252 +{
  253 + struct inode_smack *sip = isp->i_security;
  254 + return (sip->smk_flags & SMK_INODE_TRANSMUTE) != 0;
  255 +}
  256 +
  257 +/*
243 258 * Present a pointer to the smack label in an inode blob.
244 259 */
245 260 static inline char *smk_of_inode(const struct inode *isp)
... ... @@ -265,7 +280,7 @@
265 280 }
266 281  
267 282 /*
268   - * Present a pointer to the smack label in the curren task blob.
  283 + * Present a pointer to the smack label in the current task blob.
269 284 */
270 285 static inline char *smk_of_current(void)
271 286 {
security/smack/smack_access.c
... ... @@ -67,6 +67,46 @@
67 67 int log_policy = SMACK_AUDIT_DENIED;
68 68  
69 69 /**
  70 + * smk_access_entry - look up matching access rule
  71 + * @subject_label: a pointer to the subject's Smack label
  72 + * @object_label: a pointer to the object's Smack label
  73 + *
  74 + * This function looks up the subject/object pair in the
  75 + * access rule list and returns pointer to the matching rule if found,
  76 + * NULL otherwise.
  77 + *
  78 + * NOTE:
  79 + * Even though Smack labels are usually shared on smack_list
  80 + * labels that come in off the network can't be imported
  81 + * and added to the list for locking reasons.
  82 + *
  83 + * Therefore, it is necessary to check the contents of the labels,
  84 + * not just the pointer values. Of course, in most cases the labels
  85 + * will be on the list, so checking the pointers may be a worthwhile
  86 + * optimization.
  87 + */
  88 +int smk_access_entry(char *subject_label, char *object_label)
  89 +{
  90 + u32 may = MAY_NOT;
  91 + struct smack_rule *srp;
  92 +
  93 + rcu_read_lock();
  94 + list_for_each_entry_rcu(srp, &smack_rule_list, list) {
  95 + if (srp->smk_subject == subject_label ||
  96 + strcmp(srp->smk_subject, subject_label) == 0) {
  97 + if (srp->smk_object == object_label ||
  98 + strcmp(srp->smk_object, object_label) == 0) {
  99 + may = srp->smk_access;
  100 + break;
  101 + }
  102 + }
  103 + }
  104 + rcu_read_unlock();
  105 +
  106 + return may;
  107 +}
  108 +
  109 +/**
70 110 * smk_access - determine if a subject has a specific access to an object
71 111 * @subject_label: a pointer to the subject's Smack label
72 112 * @object_label: a pointer to the object's Smack label
... ... @@ -90,7 +130,6 @@
90 130 struct smk_audit_info *a)
91 131 {
92 132 u32 may = MAY_NOT;
93   - struct smack_rule *srp;
94 133 int rc = 0;
95 134  
96 135 /*
... ... @@ -144,18 +183,7 @@
144 183 * access (e.g. read is included in readwrite) it's
145 184 * good.
146 185 */
147   - rcu_read_lock();
148   - list_for_each_entry_rcu(srp, &smack_rule_list, list) {
149   - if (srp->smk_subject == subject_label ||
150   - strcmp(srp->smk_subject, subject_label) == 0) {
151   - if (srp->smk_object == object_label ||
152   - strcmp(srp->smk_object, object_label) == 0) {
153   - may = srp->smk_access;
154   - break;
155   - }
156   - }
157   - }
158   - rcu_read_unlock();
  186 + may = smk_access_entry(subject_label, object_label);
159 187 /*
160 188 * This is a bit map operation.
161 189 */
security/smack/smack_lsm.c
... ... @@ -3,12 +3,14 @@
3 3 *
4 4 * This file contains the smack hook function implementations.
5 5 *
6   - * Author:
  6 + * Authors:
7 7 * Casey Schaufler <casey@schaufler-ca.com>
  8 + * Jarkko Sakkinen <ext-jarkko.2.sakkinen@nokia.com>
8 9 *
9 10 * Copyright (C) 2007 Casey Schaufler <casey@schaufler-ca.com>
10 11 * Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
11 12 * Paul Moore <paul.moore@hp.com>
  13 + * Copyright (C) 2010 Nokia Corporation
12 14 *
13 15 * This program is free software; you can redistribute it and/or modify
14 16 * it under the terms of the GNU General Public License version 2,
... ... @@ -35,6 +37,9 @@
35 37  
36 38 #define task_security(task) (task_cred_xxx((task), security))
37 39  
  40 +#define TRANS_TRUE "TRUE"
  41 +#define TRANS_TRUE_SIZE 4
  42 +
38 43 /**
39 44 * smk_fetch - Fetch the smack label from a file.
40 45 * @ip: a pointer to the inode
... ... @@ -468,6 +473,8 @@
468 473 char **name, void **value, size_t *len)
469 474 {
470 475 char *isp = smk_of_inode(inode);
  476 + char *dsp = smk_of_inode(dir);
  477 + u32 may;
471 478  
472 479 if (name) {
473 480 *name = kstrdup(XATTR_SMACK_SUFFIX, GFP_KERNEL);
... ... @@ -476,6 +483,16 @@
476 483 }
477 484  
478 485 if (value) {
  486 + may = smk_access_entry(smk_of_current(), dsp);
  487 +
  488 + /*
  489 + * If the access rule allows transmutation and
  490 + * the directory requests transmutation then
  491 + * by all means transmute.
  492 + */
  493 + if (((may & MAY_TRANSMUTE) != 0) && smk_inode_transmutable(dir))
  494 + isp = dsp;
  495 +
479 496 *value = kstrdup(isp, GFP_KERNEL);
480 497 if (*value == NULL)
481 498 return -ENOMEM;
... ... @@ -709,6 +726,12 @@
709 726 if (size == 0 || size >= SMK_LABELLEN ||
710 727 smk_import(value, size) == NULL)
711 728 rc = -EINVAL;
  729 + } else if (strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0) {
  730 + if (!capable(CAP_MAC_ADMIN))
  731 + rc = -EPERM;
  732 + if (size != TRANS_TRUE_SIZE ||
  733 + strncmp(value, TRANS_TRUE, TRANS_TRUE_SIZE) != 0)
  734 + rc = -EINVAL;
712 735 } else
713 736 rc = cap_inode_setxattr(dentry, name, value, size, flags);
714 737  
715 738  
716 739  
717 740  
718 741  
719 742  
... ... @@ -735,35 +758,23 @@
735 758 static void smack_inode_post_setxattr(struct dentry *dentry, const char *name,
736 759 const void *value, size_t size, int flags)
737 760 {
738   - struct inode_smack *isp;
739 761 char *nsp;
  762 + struct inode_smack *isp = dentry->d_inode->i_security;
740 763  
741   - /*
742   - * Not SMACK or SMACKEXEC
743   - */
744   - if (strcmp(name, XATTR_NAME_SMACK) &&
745   - strcmp(name, XATTR_NAME_SMACKEXEC))
746   - return;
747   -
748   - isp = dentry->d_inode->i_security;
749   -
750   - /*
751   - * No locking is done here. This is a pointer
752   - * assignment.
753   - */
754   - nsp = smk_import(value, size);
755   -
756 764 if (strcmp(name, XATTR_NAME_SMACK) == 0) {
  765 + nsp = smk_import(value, size);
757 766 if (nsp != NULL)
758 767 isp->smk_inode = nsp;
759 768 else
760 769 isp->smk_inode = smack_known_invalid.smk_known;
761   - } else {
  770 + } else if (strcmp(name, XATTR_NAME_SMACKEXEC) == 0) {
  771 + nsp = smk_import(value, size);
762 772 if (nsp != NULL)
763 773 isp->smk_task = nsp;
764 774 else
765 775 isp->smk_task = smack_known_invalid.smk_known;
766   - }
  776 + } else if (strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0)
  777 + isp->smk_flags |= SMK_INODE_TRANSMUTE;
767 778  
768 779 return;
769 780 }
... ... @@ -803,7 +814,8 @@
803 814 if (strcmp(name, XATTR_NAME_SMACK) == 0 ||
804 815 strcmp(name, XATTR_NAME_SMACKIPIN) == 0 ||
805 816 strcmp(name, XATTR_NAME_SMACKIPOUT) == 0 ||
806   - strcmp(name, XATTR_NAME_SMACKEXEC) == 0) {
  817 + strcmp(name, XATTR_NAME_SMACKEXEC) == 0 ||
  818 + strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0) {
807 819 if (!capable(CAP_MAC_ADMIN))
808 820 rc = -EPERM;
809 821 } else
... ... @@ -2274,6 +2286,8 @@
2274 2286 char *csp = smk_of_current();
2275 2287 char *fetched;
2276 2288 char *final;
  2289 + char trattr[TRANS_TRUE_SIZE];
  2290 + int transflag = 0;
2277 2291 struct dentry *dp;
2278 2292  
2279 2293 if (inode == NULL)
2280 2294  
... ... @@ -2392,10 +2406,19 @@
2392 2406 */
2393 2407 dp = dget(opt_dentry);
2394 2408 fetched = smk_fetch(XATTR_NAME_SMACK, inode, dp);
2395   - if (fetched != NULL)
  2409 + if (fetched != NULL) {
2396 2410 final = fetched;
2397   - isp->smk_task = smk_fetch(XATTR_NAME_SMACKEXEC, inode,
2398   - dp);
  2411 + if (S_ISDIR(inode->i_mode)) {
  2412 + trattr[0] = '\0';
  2413 + inode->i_op->getxattr(dp,
  2414 + XATTR_NAME_SMACKTRANSMUTE,
  2415 + trattr, TRANS_TRUE_SIZE);
  2416 + if (strncmp(trattr, TRANS_TRUE,
  2417 + TRANS_TRUE_SIZE) == 0)
  2418 + transflag = SMK_INODE_TRANSMUTE;
  2419 + }
  2420 + }
  2421 + isp->smk_task = smk_fetch(XATTR_NAME_SMACKEXEC, inode, dp);
2399 2422  
2400 2423 dput(dp);
2401 2424 break;
... ... @@ -2406,7 +2429,7 @@
2406 2429 else
2407 2430 isp->smk_inode = final;
2408 2431  
2409   - isp->smk_flags |= SMK_INODE_INSTANT;
  2432 + isp->smk_flags |= (SMK_INODE_INSTANT | transflag);
2410 2433  
2411 2434 unlockandout:
2412 2435 mutex_unlock(&isp->smk_lock);
... ... @@ -2456,6 +2479,7 @@
2456 2479 void *value, size_t size)
2457 2480 {
2458 2481 struct task_smack *tsp;
  2482 + struct task_smack *oldtsp;
2459 2483 struct cred *new;
2460 2484 char *newsmack;
2461 2485  
... ... @@ -2485,6 +2509,7 @@
2485 2509 if (newsmack == smack_known_web.smk_known)
2486 2510 return -EPERM;
2487 2511  
  2512 + oldtsp = p->cred->security;
2488 2513 new = prepare_creds();
2489 2514 if (new == NULL)
2490 2515 return -ENOMEM;
... ... @@ -2494,6 +2519,7 @@
2494 2519 return -ENOMEM;
2495 2520 }
2496 2521 tsp->smk_task = newsmack;
  2522 + tsp->smk_forked = oldtsp->smk_forked;
2497 2523 new->security = tsp;
2498 2524 commit_creds(new);
2499 2525 return size;
security/smack/smackfs.c
... ... @@ -109,9 +109,12 @@
109 109 * SMK_ACCESSLEN: Maximum length for a rule access field
110 110 * SMK_LOADLEN: Smack rule length
111 111 */
112   -#define SMK_ACCESS "rwxa"
113   -#define SMK_ACCESSLEN (sizeof(SMK_ACCESS) - 1)
114   -#define SMK_LOADLEN (SMK_LABELLEN + SMK_LABELLEN + SMK_ACCESSLEN)
  112 +#define SMK_OACCESS "rwxa"
  113 +#define SMK_ACCESS "rwxat"
  114 +#define SMK_OACCESSLEN (sizeof(SMK_OACCESS) - 1)
  115 +#define SMK_ACCESSLEN (sizeof(SMK_ACCESS) - 1)
  116 +#define SMK_OLOADLEN (SMK_LABELLEN + SMK_LABELLEN + SMK_OACCESSLEN)
  117 +#define SMK_LOADLEN (SMK_LABELLEN + SMK_LABELLEN + SMK_ACCESSLEN)
115 118  
116 119 /**
117 120 * smk_netlabel_audit_set - fill a netlbl_audit struct
... ... @@ -175,6 +178,8 @@
175 178 seq_putc(s, 'x');
176 179 if (srp->smk_access & MAY_APPEND)
177 180 seq_putc(s, 'a');
  181 + if (srp->smk_access & MAY_TRANSMUTE)
  182 + seq_putc(s, 't');
178 183 if (srp->smk_access == 0)
179 184 seq_putc(s, '-');
180 185  
181 186  
182 187  
... ... @@ -273,10 +278,15 @@
273 278 if (!capable(CAP_MAC_ADMIN))
274 279 return -EPERM;
275 280  
276   - if (*ppos != 0 || count != SMK_LOADLEN)
  281 + if (*ppos != 0)
277 282 return -EINVAL;
  283 + /*
  284 + * Minor hack for backward compatability
  285 + */
  286 + if (count < (SMK_OLOADLEN) || count > SMK_LOADLEN)
  287 + return -EINVAL;
278 288  
279   - data = kzalloc(count, GFP_KERNEL);
  289 + data = kzalloc(SMK_LOADLEN, GFP_KERNEL);
280 290 if (data == NULL)
281 291 return -ENOMEM;
282 292  
... ... @@ -285,6 +295,12 @@
285 295 goto out;
286 296 }
287 297  
  298 + /*
  299 + * More on the minor hack for backward compatability
  300 + */
  301 + if (count == (SMK_OLOADLEN))
  302 + data[SMK_OLOADLEN] = '-';
  303 +
288 304 rule = kzalloc(sizeof(*rule), GFP_KERNEL);
289 305 if (rule == NULL) {
290 306 rc = -ENOMEM;
... ... @@ -340,6 +356,17 @@
340 356 case 'a':
341 357 case 'A':
342 358 rule->smk_access |= MAY_APPEND;
  359 + break;
  360 + default:
  361 + goto out_free_rule;
  362 + }
  363 +
  364 + switch (data[SMK_LABELLEN + SMK_LABELLEN + 4]) {
  365 + case '-':
  366 + break;
  367 + case 't':
  368 + case 'T':
  369 + rule->smk_access |= MAY_TRANSMUTE;
343 370 break;
344 371 default:
345 372 goto out_free_rule;