aboutsummaryrefslogtreecommitdiffstats
path: root/security
diff options
context:
space:
mode:
authorRafal Krypa <r.krypa@samsung.com>2013-01-10 13:42:00 -0500
committerCasey Schaufler <casey@schaufler-ca.com>2013-03-19 17:16:42 -0400
commite05b6f982a049113a88a1750e13fdb15298cbed4 (patch)
tree2f59b25edb54ff44f743423268e934f87c60a359 /security
parentcee7e443344a3845e5b9111614b41e0b1afb60ce (diff)
Smack: add support for modification of existing rules
Rule modifications are enabled via /smack/change-rule. Format is as follows: "Subject Object rwaxt rwaxt" First two strings are subject and object labels up to 255 characters. Third string contains permissions to enable. Fourth string contains permissions to disable. All unmentioned permissions will be left unchanged. If no rule previously existed, it will be created. Targeted for git://git.gitorious.org/smack-next/kernel.git Signed-off-by: Rafal Krypa <r.krypa@samsung.com>
Diffstat (limited to 'security')
-rw-r--r--security/smack/smackfs.c249
1 files changed, 170 insertions, 79 deletions
diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c
index 337e32c551da..2479a41a7dff 100644
--- a/security/smack/smackfs.c
+++ b/security/smack/smackfs.c
@@ -50,12 +50,12 @@ enum smk_inos {
50 SMK_ACCESS2 = 16, /* make an access check with long labels */ 50 SMK_ACCESS2 = 16, /* make an access check with long labels */
51 SMK_CIPSO2 = 17, /* load long label -> CIPSO mapping */ 51 SMK_CIPSO2 = 17, /* load long label -> CIPSO mapping */
52 SMK_REVOKE_SUBJ = 18, /* set rules with subject label to '-' */ 52 SMK_REVOKE_SUBJ = 18, /* set rules with subject label to '-' */
53 SMK_CHANGE_RULE = 19, /* change or add rules (long labels) */
53}; 54};
54 55
55/* 56/*
56 * List locks 57 * List locks
57 */ 58 */
58static DEFINE_MUTEX(smack_list_lock);
59static DEFINE_MUTEX(smack_cipso_lock); 59static DEFINE_MUTEX(smack_cipso_lock);
60static DEFINE_MUTEX(smack_ambient_lock); 60static DEFINE_MUTEX(smack_ambient_lock);
61static DEFINE_MUTEX(smk_netlbladdr_lock); 61static DEFINE_MUTEX(smk_netlbladdr_lock);
@@ -110,6 +110,13 @@ struct smack_master_list {
110 110
111LIST_HEAD(smack_rule_list); 111LIST_HEAD(smack_rule_list);
112 112
113struct smack_parsed_rule {
114 char *smk_subject;
115 char *smk_object;
116 int smk_access1;
117 int smk_access2;
118};
119
113static int smk_cipso_doi_value = SMACK_CIPSO_DOI_DEFAULT; 120static int smk_cipso_doi_value = SMACK_CIPSO_DOI_DEFAULT;
114 121
115const char *smack_cipso_option = SMACK_CIPSO_OPTION; 122const char *smack_cipso_option = SMACK_CIPSO_OPTION;
@@ -167,25 +174,28 @@ static void smk_netlabel_audit_set(struct netlbl_audit *nap)
167#define SMK_NETLBLADDRMIN 9 174#define SMK_NETLBLADDRMIN 9
168 175
169/** 176/**
170 * smk_set_access - add a rule to the rule list 177 * smk_set_access - add a rule to the rule list or replace an old rule
171 * @srp: the new rule to add 178 * @srp: the rule to add or replace
172 * @rule_list: the list of rules 179 * @rule_list: the list of rules
173 * @rule_lock: the rule list lock 180 * @rule_lock: the rule list lock
181 * @global: if non-zero, indicates a global rule
174 * 182 *
175 * Looks through the current subject/object/access list for 183 * Looks through the current subject/object/access list for
176 * the subject/object pair and replaces the access that was 184 * the subject/object pair and replaces the access that was
177 * there. If the pair isn't found add it with the specified 185 * there. If the pair isn't found add it with the specified
178 * access. 186 * access.
179 * 187 *
180 * Returns 1 if a rule was found to exist already, 0 if it is new
181 * Returns 0 if nothing goes wrong or -ENOMEM if it fails 188 * Returns 0 if nothing goes wrong or -ENOMEM if it fails
182 * during the allocation of the new pair to add. 189 * during the allocation of the new pair to add.
183 */ 190 */
184static int smk_set_access(struct smack_rule *srp, struct list_head *rule_list, 191static int smk_set_access(struct smack_parsed_rule *srp,
185 struct mutex *rule_lock) 192 struct list_head *rule_list,
193 struct mutex *rule_lock, int global)
186{ 194{
187 struct smack_rule *sp; 195 struct smack_rule *sp;
196 struct smack_master_list *smlp;
188 int found = 0; 197 int found = 0;
198 int rc = 0;
189 199
190 mutex_lock(rule_lock); 200 mutex_lock(rule_lock);
191 201
@@ -197,23 +207,89 @@ static int smk_set_access(struct smack_rule *srp, struct list_head *rule_list,
197 if (sp->smk_object == srp->smk_object && 207 if (sp->smk_object == srp->smk_object &&
198 sp->smk_subject == srp->smk_subject) { 208 sp->smk_subject == srp->smk_subject) {
199 found = 1; 209 found = 1;
200 sp->smk_access = srp->smk_access; 210 sp->smk_access |= srp->smk_access1;
211 sp->smk_access &= ~srp->smk_access2;
201 break; 212 break;
202 } 213 }
203 } 214 }
204 if (found == 0)
205 list_add_rcu(&srp->list, rule_list);
206 215
216 if (found == 0) {
217 sp = kzalloc(sizeof(*sp), GFP_KERNEL);
218 if (sp == NULL) {
219 rc = -ENOMEM;
220 goto out;
221 }
222
223 sp->smk_subject = srp->smk_subject;
224 sp->smk_object = srp->smk_object;
225 sp->smk_access = srp->smk_access1 & ~srp->smk_access2;
226
227 list_add_rcu(&sp->list, rule_list);
228 /*
229 * If this is a global as opposed to self and a new rule
230 * it needs to get added for reporting.
231 */
232 if (global) {
233 smlp = kzalloc(sizeof(*smlp), GFP_KERNEL);
234 if (smlp != NULL) {
235 smlp->smk_rule = sp;
236 list_add_rcu(&smlp->list, &smack_rule_list);
237 } else
238 rc = -ENOMEM;
239 }
240 }
241
242out:
207 mutex_unlock(rule_lock); 243 mutex_unlock(rule_lock);
244 return rc;
245}
246
247/**
248 * smk_perm_from_str - parse smack accesses from a text string
249 * @string: a text string that contains a Smack accesses code
250 *
251 * Returns an integer with respective bits set for specified accesses.
252 */
253static int smk_perm_from_str(const char *string)
254{
255 int perm = 0;
256 const char *cp;
208 257
209 return found; 258 for (cp = string; ; cp++)
259 switch (*cp) {
260 case '-':
261 break;
262 case 'r':
263 case 'R':
264 perm |= MAY_READ;
265 break;
266 case 'w':
267 case 'W':
268 perm |= MAY_WRITE;
269 break;
270 case 'x':
271 case 'X':
272 perm |= MAY_EXEC;
273 break;
274 case 'a':
275 case 'A':
276 perm |= MAY_APPEND;
277 break;
278 case 't':
279 case 'T':
280 perm |= MAY_TRANSMUTE;
281 break;
282 default:
283 return perm;
284 }
210} 285}
211 286
212/** 287/**
213 * smk_fill_rule - Fill Smack rule from strings 288 * smk_fill_rule - Fill Smack rule from strings
214 * @subject: subject label string 289 * @subject: subject label string
215 * @object: object label string 290 * @object: object label string
216 * @access: access string 291 * @access1: access string
292 * @access2: string with permissions to be removed
217 * @rule: Smack rule 293 * @rule: Smack rule
218 * @import: if non-zero, import labels 294 * @import: if non-zero, import labels
219 * @len: label length limit 295 * @len: label length limit
@@ -221,8 +297,9 @@ static int smk_set_access(struct smack_rule *srp, struct list_head *rule_list,
221 * Returns 0 on success, -1 on failure 297 * Returns 0 on success, -1 on failure
222 */ 298 */
223static int smk_fill_rule(const char *subject, const char *object, 299static int smk_fill_rule(const char *subject, const char *object,
224 const char *access, struct smack_rule *rule, 300 const char *access1, const char *access2,
225 int import, int len) 301 struct smack_parsed_rule *rule, int import,
302 int len)
226{ 303{
227 const char *cp; 304 const char *cp;
228 struct smack_known *skp; 305 struct smack_known *skp;
@@ -255,36 +332,11 @@ static int smk_fill_rule(const char *subject, const char *object,
255 rule->smk_object = skp->smk_known; 332 rule->smk_object = skp->smk_known;
256 } 333 }
257 334
258 rule->smk_access = 0; 335 rule->smk_access1 = smk_perm_from_str(access1);
259 336 if (access2)
260 for (cp = access; *cp != '\0'; cp++) { 337 rule->smk_access2 = smk_perm_from_str(access2);
261 switch (*cp) { 338 else
262 case '-': 339 rule->smk_access2 = ~rule->smk_access1;
263 break;
264 case 'r':
265 case 'R':
266 rule->smk_access |= MAY_READ;
267 break;
268 case 'w':
269 case 'W':
270 rule->smk_access |= MAY_WRITE;
271 break;
272 case 'x':
273 case 'X':
274 rule->smk_access |= MAY_EXEC;
275 break;
276 case 'a':
277 case 'A':
278 rule->smk_access |= MAY_APPEND;
279 break;
280 case 't':
281 case 'T':
282 rule->smk_access |= MAY_TRANSMUTE;
283 break;
284 default:
285 return 0;
286 }
287 }
288 340
289 return 0; 341 return 0;
290} 342}
@@ -297,30 +349,33 @@ static int smk_fill_rule(const char *subject, const char *object,
297 * 349 *
298 * Returns 0 on success, -1 on errors. 350 * Returns 0 on success, -1 on errors.
299 */ 351 */
300static int smk_parse_rule(const char *data, struct smack_rule *rule, int import) 352static int smk_parse_rule(const char *data, struct smack_parsed_rule *rule,
353 int import)
301{ 354{
302 int rc; 355 int rc;
303 356
304 rc = smk_fill_rule(data, data + SMK_LABELLEN, 357 rc = smk_fill_rule(data, data + SMK_LABELLEN,
305 data + SMK_LABELLEN + SMK_LABELLEN, rule, import, 358 data + SMK_LABELLEN + SMK_LABELLEN, NULL, rule,
306 SMK_LABELLEN); 359 import, SMK_LABELLEN);
307 return rc; 360 return rc;
308} 361}
309 362
310/** 363/**
311 * smk_parse_long_rule - parse Smack rule from rule string 364 * smk_parse_long_rule - parse Smack rule from rule string
312 * @data: string to be parsed, null terminated 365 * @data: string to be parsed, null terminated
313 * @rule: Smack rule 366 * @rule: Will be filled with Smack parsed rule
314 * @import: if non-zero, import labels 367 * @import: if non-zero, import labels
368 * @change: if non-zero, data is from /smack/change-rule
315 * 369 *
316 * Returns 0 on success, -1 on failure 370 * Returns 0 on success, -1 on failure
317 */ 371 */
318static int smk_parse_long_rule(const char *data, struct smack_rule *rule, 372static int smk_parse_long_rule(const char *data, struct smack_parsed_rule *rule,
319 int import) 373 int import, int change)
320{ 374{
321 char *subject; 375 char *subject;
322 char *object; 376 char *object;
323 char *access; 377 char *access1;
378 char *access2;
324 int datalen; 379 int datalen;
325 int rc = -1; 380 int rc = -1;
326 381
@@ -334,14 +389,27 @@ static int smk_parse_long_rule(const char *data, struct smack_rule *rule,
334 object = kzalloc(datalen, GFP_KERNEL); 389 object = kzalloc(datalen, GFP_KERNEL);
335 if (object == NULL) 390 if (object == NULL)
336 goto free_out_s; 391 goto free_out_s;
337 access = kzalloc(datalen, GFP_KERNEL); 392 access1 = kzalloc(datalen, GFP_KERNEL);
338 if (access == NULL) 393 if (access1 == NULL)
339 goto free_out_o; 394 goto free_out_o;
395 access2 = kzalloc(datalen, GFP_KERNEL);
396 if (access2 == NULL)
397 goto free_out_a;
398
399 if (change) {
400 if (sscanf(data, "%s %s %s %s",
401 subject, object, access1, access2) == 4)
402 rc = smk_fill_rule(subject, object, access1, access2,
403 rule, import, 0);
404 } else {
405 if (sscanf(data, "%s %s %s", subject, object, access1) == 3)
406 rc = smk_fill_rule(subject, object, access1, NULL,
407 rule, import, 0);
408 }
340 409
341 if (sscanf(data, "%s %s %s", subject, object, access) == 3) 410 kfree(access2);
342 rc = smk_fill_rule(subject, object, access, rule, import, 0); 411free_out_a:
343 412 kfree(access1);
344 kfree(access);
345free_out_o: 413free_out_o:
346 kfree(object); 414 kfree(object);
347free_out_s: 415free_out_s:
@@ -351,6 +419,7 @@ free_out_s:
351 419
352#define SMK_FIXED24_FMT 0 /* Fixed 24byte label format */ 420#define SMK_FIXED24_FMT 0 /* Fixed 24byte label format */
353#define SMK_LONG_FMT 1 /* Variable long label format */ 421#define SMK_LONG_FMT 1 /* Variable long label format */
422#define SMK_CHANGE_FMT 2 /* Rule modification format */
354/** 423/**
355 * smk_write_rules_list - write() for any /smack rule file 424 * smk_write_rules_list - write() for any /smack rule file
356 * @file: file pointer, not actually used 425 * @file: file pointer, not actually used
@@ -359,22 +428,24 @@ free_out_s:
359 * @ppos: where to start - must be 0 428 * @ppos: where to start - must be 0
360 * @rule_list: the list of rules to write to 429 * @rule_list: the list of rules to write to
361 * @rule_lock: lock for the rule list 430 * @rule_lock: lock for the rule list
362 * @format: /smack/load or /smack/load2 format. 431 * @format: /smack/load or /smack/load2 or /smack/change-rule format.
363 * 432 *
364 * Get one smack access rule from above. 433 * Get one smack access rule from above.
365 * The format for SMK_LONG_FMT is: 434 * The format for SMK_LONG_FMT is:
366 * "subject<whitespace>object<whitespace>access[<whitespace>...]" 435 * "subject<whitespace>object<whitespace>access[<whitespace>...]"
367 * The format for SMK_FIXED24_FMT is exactly: 436 * The format for SMK_FIXED24_FMT is exactly:
368 * "subject object rwxat" 437 * "subject object rwxat"
438 * The format for SMK_CHANGE_FMT is:
439 * "subject<whitespace>object<whitespace>
440 * acc_enable<whitespace>acc_disable[<whitespace>...]"
369 */ 441 */
370static ssize_t smk_write_rules_list(struct file *file, const char __user *buf, 442static ssize_t smk_write_rules_list(struct file *file, const char __user *buf,
371 size_t count, loff_t *ppos, 443 size_t count, loff_t *ppos,
372 struct list_head *rule_list, 444 struct list_head *rule_list,
373 struct mutex *rule_lock, int format) 445 struct mutex *rule_lock, int format)
374{ 446{
375 struct smack_master_list *smlp;
376 struct smack_known *skp; 447 struct smack_known *skp;
377 struct smack_rule *rule; 448 struct smack_parsed_rule *rule;
378 char *data; 449 char *data;
379 int datalen; 450 int datalen;
380 int rc = -EINVAL; 451 int rc = -EINVAL;
@@ -417,7 +488,11 @@ static ssize_t smk_write_rules_list(struct file *file, const char __user *buf,
417 * Be sure the data string is terminated. 488 * Be sure the data string is terminated.
418 */ 489 */
419 data[count] = '\0'; 490 data[count] = '\0';
420 if (smk_parse_long_rule(data, rule, 1)) 491 if (smk_parse_long_rule(data, rule, 1, 0))
492 goto out_free_rule;
493 } else if (format == SMK_CHANGE_FMT) {
494 data[count] = '\0';
495 if (smk_parse_long_rule(data, rule, 1, 1))
421 goto out_free_rule; 496 goto out_free_rule;
422 } else { 497 } else {
423 /* 498 /*
@@ -437,22 +512,9 @@ static ssize_t smk_write_rules_list(struct file *file, const char __user *buf,
437 rule_lock = &skp->smk_rules_lock; 512 rule_lock = &skp->smk_rules_lock;
438 } 513 }
439 514
440 rc = count; 515 rc = smk_set_access(rule, rule_list, rule_lock, load);
441 /* 516 if (rc == 0) {
442 * If this is a global as opposed to self and a new rule 517 rc = count;
443 * it needs to get added for reporting.
444 * smk_set_access returns true if there was already a rule
445 * for the subject/object pair, and false if it was new.
446 */
447 if (!smk_set_access(rule, rule_list, rule_lock)) {
448 if (load) {
449 smlp = kzalloc(sizeof(*smlp), GFP_KERNEL);
450 if (smlp != NULL) {
451 smlp->smk_rule = rule;
452 list_add_rcu(&smlp->list, &smack_rule_list);
453 } else
454 rc = -ENOMEM;
455 }
456 goto out; 518 goto out;
457 } 519 }
458 520
@@ -1774,7 +1836,7 @@ static const struct file_operations smk_load_self_ops = {
1774static ssize_t smk_user_access(struct file *file, const char __user *buf, 1836static ssize_t smk_user_access(struct file *file, const char __user *buf,
1775 size_t count, loff_t *ppos, int format) 1837 size_t count, loff_t *ppos, int format)
1776{ 1838{
1777 struct smack_rule rule; 1839 struct smack_parsed_rule rule;
1778 char *data; 1840 char *data;
1779 char *cod; 1841 char *cod;
1780 int res; 1842 int res;
@@ -1796,14 +1858,14 @@ static ssize_t smk_user_access(struct file *file, const char __user *buf,
1796 return -ENOMEM; 1858 return -ENOMEM;
1797 memcpy(cod, data, count); 1859 memcpy(cod, data, count);
1798 cod[count] = '\0'; 1860 cod[count] = '\0';
1799 res = smk_parse_long_rule(cod, &rule, 0); 1861 res = smk_parse_long_rule(cod, &rule, 0, 0);
1800 kfree(cod); 1862 kfree(cod);
1801 } 1863 }
1802 1864
1803 if (res) 1865 if (res)
1804 return -EINVAL; 1866 return -EINVAL;
1805 1867
1806 res = smk_access(rule.smk_subject, rule.smk_object, rule.smk_access, 1868 res = smk_access(rule.smk_subject, rule.smk_object, rule.smk_access1,
1807 NULL); 1869 NULL);
1808 data[0] = res == 0 ? '1' : '0'; 1870 data[0] = res == 0 ? '1' : '0';
1809 data[1] = '\0'; 1871 data[1] = '\0';
@@ -2075,6 +2137,33 @@ static int smk_init_sysfs(void)
2075} 2137}
2076 2138
2077/** 2139/**
2140 * smk_write_change_rule - write() for /smack/change-rule
2141 * @file: file pointer
2142 * @buf: data from user space
2143 * @count: bytes sent
2144 * @ppos: where to start - must be 0
2145 */
2146static ssize_t smk_write_change_rule(struct file *file, const char __user *buf,
2147 size_t count, loff_t *ppos)
2148{
2149 /*
2150 * Must have privilege.
2151 */
2152 if (!capable(CAP_MAC_ADMIN))
2153 return -EPERM;
2154
2155 return smk_write_rules_list(file, buf, count, ppos, NULL, NULL,
2156 SMK_CHANGE_FMT);
2157}
2158
2159static const struct file_operations smk_change_rule_ops = {
2160 .write = smk_write_change_rule,
2161 .read = simple_transaction_read,
2162 .release = simple_transaction_release,
2163 .llseek = generic_file_llseek,
2164};
2165
2166/**
2078 * smk_fill_super - fill the /smackfs superblock 2167 * smk_fill_super - fill the /smackfs superblock
2079 * @sb: the empty superblock 2168 * @sb: the empty superblock
2080 * @data: unused 2169 * @data: unused
@@ -2123,6 +2212,8 @@ static int smk_fill_super(struct super_block *sb, void *data, int silent)
2123 [SMK_REVOKE_SUBJ] = { 2212 [SMK_REVOKE_SUBJ] = {
2124 "revoke-subject", &smk_revoke_subj_ops, 2213 "revoke-subject", &smk_revoke_subj_ops,
2125 S_IRUGO|S_IWUSR}, 2214 S_IRUGO|S_IWUSR},
2215 [SMK_CHANGE_RULE] = {
2216 "change-rule", &smk_change_rule_ops, S_IRUGO|S_IWUSR},
2126 /* last one */ 2217 /* last one */
2127 {""} 2218 {""}
2128 }; 2219 };