diff options
author | Casey Schaufler <casey@schaufler-ca.com> | 2011-01-17 11:05:27 -0500 |
---|---|---|
committer | Casey Schaufler <casey@schaufler-ca.com> | 2011-01-17 11:05:27 -0500 |
commit | 7898e1f8e9eb1bee88c92d636e0ab93f2cbe31c6 (patch) | |
tree | d4aaa367bb42d0ff9d1e4ba227f248b5b9cd7687 /security/smack/smackfs.c | |
parent | aeda4ac3efc29e4d55989abd0a73530453aa69ba (diff) |
Subject: [PATCH] Smack: mmap controls for library containment
In the embedded world there are often situations
where libraries are updated from a variety of sources,
for a variety of reasons, and with any number of
security characteristics. These differences
might include privilege required for a given library
provided interface to function properly, as occurs
from time to time in graphics libraries. There are
also cases where it is important to limit use of
libraries based on the provider of the library and
the security aware application may make choices
based on that criteria.
These issues are addressed by providing an additional
Smack label that may optionally be assigned to an object,
the SMACK64MMAP attribute. An mmap operation is allowed
if there is no such attribute.
If there is a SMACK64MMAP attribute the mmap is permitted
only if a subject with that label has all of the access
permitted a subject with the current task label.
Security aware applications may from time to time
wish to reduce their "privilege" to avoid accidental use
of privilege. One case where this arises is the
environment in which multiple sources provide libraries
to perform the same functions. An application may know
that it should eschew services made available from a
particular vendor, or of a particular version.
In support of this a secondary list of Smack rules has
been added that is local to the task. This list is
consulted only in the case where the global list has
approved access. It can only further restrict access.
Unlike the global last, if no entry is found on the
local list access is granted. An application can add
entries to its own list by writing to /smack/load-self.
The changes appear large as they involve refactoring
the list handling to accomodate there being more
than one rule list.
Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
Diffstat (limited to 'security/smack/smackfs.c')
-rw-r--r-- | security/smack/smackfs.c | 370 |
1 files changed, 255 insertions, 115 deletions
diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index 362d5eda948b..90d1bbaaa6f3 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c | |||
@@ -43,6 +43,7 @@ enum smk_inos { | |||
43 | SMK_NETLBLADDR = 8, /* single label hosts */ | 43 | SMK_NETLBLADDR = 8, /* single label hosts */ |
44 | SMK_ONLYCAP = 9, /* the only "capable" label */ | 44 | SMK_ONLYCAP = 9, /* the only "capable" label */ |
45 | SMK_LOGGING = 10, /* logging */ | 45 | SMK_LOGGING = 10, /* logging */ |
46 | SMK_LOAD_SELF = 11, /* task specific rules */ | ||
46 | }; | 47 | }; |
47 | 48 | ||
48 | /* | 49 | /* |
@@ -135,104 +136,30 @@ static void smk_netlabel_audit_set(struct netlbl_audit *nap) | |||
135 | #define SMK_NETLBLADDRMIN 9 | 136 | #define SMK_NETLBLADDRMIN 9 |
136 | #define SMK_NETLBLADDRMAX 42 | 137 | #define SMK_NETLBLADDRMAX 42 |
137 | 138 | ||
138 | /* | ||
139 | * Seq_file read operations for /smack/load | ||
140 | */ | ||
141 | |||
142 | static void *load_seq_start(struct seq_file *s, loff_t *pos) | ||
143 | { | ||
144 | if (*pos == SEQ_READ_FINISHED) | ||
145 | return NULL; | ||
146 | if (list_empty(&smack_rule_list)) | ||
147 | return NULL; | ||
148 | return smack_rule_list.next; | ||
149 | } | ||
150 | |||
151 | static void *load_seq_next(struct seq_file *s, void *v, loff_t *pos) | ||
152 | { | ||
153 | struct list_head *list = v; | ||
154 | |||
155 | if (list_is_last(list, &smack_rule_list)) { | ||
156 | *pos = SEQ_READ_FINISHED; | ||
157 | return NULL; | ||
158 | } | ||
159 | return list->next; | ||
160 | } | ||
161 | |||
162 | static int load_seq_show(struct seq_file *s, void *v) | ||
163 | { | ||
164 | struct list_head *list = v; | ||
165 | struct smack_rule *srp = | ||
166 | list_entry(list, struct smack_rule, list); | ||
167 | |||
168 | seq_printf(s, "%s %s", (char *)srp->smk_subject, | ||
169 | (char *)srp->smk_object); | ||
170 | |||
171 | seq_putc(s, ' '); | ||
172 | |||
173 | if (srp->smk_access & MAY_READ) | ||
174 | seq_putc(s, 'r'); | ||
175 | if (srp->smk_access & MAY_WRITE) | ||
176 | seq_putc(s, 'w'); | ||
177 | if (srp->smk_access & MAY_EXEC) | ||
178 | seq_putc(s, 'x'); | ||
179 | if (srp->smk_access & MAY_APPEND) | ||
180 | seq_putc(s, 'a'); | ||
181 | if (srp->smk_access & MAY_TRANSMUTE) | ||
182 | seq_putc(s, 't'); | ||
183 | if (srp->smk_access == 0) | ||
184 | seq_putc(s, '-'); | ||
185 | |||
186 | seq_putc(s, '\n'); | ||
187 | |||
188 | return 0; | ||
189 | } | ||
190 | |||
191 | static void load_seq_stop(struct seq_file *s, void *v) | ||
192 | { | ||
193 | /* No-op */ | ||
194 | } | ||
195 | |||
196 | static const struct seq_operations load_seq_ops = { | ||
197 | .start = load_seq_start, | ||
198 | .next = load_seq_next, | ||
199 | .show = load_seq_show, | ||
200 | .stop = load_seq_stop, | ||
201 | }; | ||
202 | |||
203 | /** | ||
204 | * smk_open_load - open() for /smack/load | ||
205 | * @inode: inode structure representing file | ||
206 | * @file: "load" file pointer | ||
207 | * | ||
208 | * For reading, use load_seq_* seq_file reading operations. | ||
209 | */ | ||
210 | static int smk_open_load(struct inode *inode, struct file *file) | ||
211 | { | ||
212 | return seq_open(file, &load_seq_ops); | ||
213 | } | ||
214 | |||
215 | /** | 139 | /** |
216 | * smk_set_access - add a rule to the rule list | 140 | * smk_set_access - add a rule to the rule list |
217 | * @srp: the new rule to add | 141 | * @srp: the new rule to add |
142 | * @rule_list: the list of rules | ||
143 | * @rule_lock: the rule list lock | ||
218 | * | 144 | * |
219 | * Looks through the current subject/object/access list for | 145 | * Looks through the current subject/object/access list for |
220 | * the subject/object pair and replaces the access that was | 146 | * the subject/object pair and replaces the access that was |
221 | * there. If the pair isn't found add it with the specified | 147 | * there. If the pair isn't found add it with the specified |
222 | * access. | 148 | * access. |
223 | * | 149 | * |
150 | * Returns 1 if a rule was found to exist already, 0 if it is new | ||
224 | * Returns 0 if nothing goes wrong or -ENOMEM if it fails | 151 | * Returns 0 if nothing goes wrong or -ENOMEM if it fails |
225 | * during the allocation of the new pair to add. | 152 | * during the allocation of the new pair to add. |
226 | */ | 153 | */ |
227 | static int smk_set_access(struct smack_rule *srp) | 154 | static int smk_set_access(struct smack_rule *srp, struct list_head *rule_list, |
155 | struct mutex *rule_lock) | ||
228 | { | 156 | { |
229 | struct smack_rule *sp; | 157 | struct smack_rule *sp; |
230 | int ret = 0; | 158 | int found = 0; |
231 | int found; | ||
232 | mutex_lock(&smack_list_lock); | ||
233 | 159 | ||
234 | found = 0; | 160 | mutex_lock(rule_lock); |
235 | list_for_each_entry_rcu(sp, &smack_rule_list, list) { | 161 | |
162 | list_for_each_entry_rcu(sp, rule_list, list) { | ||
236 | if (sp->smk_subject == srp->smk_subject && | 163 | if (sp->smk_subject == srp->smk_subject && |
237 | sp->smk_object == srp->smk_object) { | 164 | sp->smk_object == srp->smk_object) { |
238 | found = 1; | 165 | found = 1; |
@@ -241,19 +168,21 @@ static int smk_set_access(struct smack_rule *srp) | |||
241 | } | 168 | } |
242 | } | 169 | } |
243 | if (found == 0) | 170 | if (found == 0) |
244 | list_add_rcu(&srp->list, &smack_rule_list); | 171 | list_add_rcu(&srp->list, rule_list); |
245 | 172 | ||
246 | mutex_unlock(&smack_list_lock); | 173 | mutex_unlock(rule_lock); |
247 | 174 | ||
248 | return ret; | 175 | return found; |
249 | } | 176 | } |
250 | 177 | ||
251 | /** | 178 | /** |
252 | * smk_write_load - write() for /smack/load | 179 | * smk_write_load_list - write() for any /smack/load |
253 | * @file: file pointer, not actually used | 180 | * @file: file pointer, not actually used |
254 | * @buf: where to get the data from | 181 | * @buf: where to get the data from |
255 | * @count: bytes sent | 182 | * @count: bytes sent |
256 | * @ppos: where to start - must be 0 | 183 | * @ppos: where to start - must be 0 |
184 | * @rule_list: the list of rules to write to | ||
185 | * @rule_lock: lock for the rule list | ||
257 | * | 186 | * |
258 | * Get one smack access rule from above. | 187 | * Get one smack access rule from above. |
259 | * The format is exactly: | 188 | * The format is exactly: |
@@ -263,21 +192,19 @@ static int smk_set_access(struct smack_rule *srp) | |||
263 | * | 192 | * |
264 | * writes must be SMK_LABELLEN+SMK_LABELLEN+SMK_ACCESSLEN bytes. | 193 | * writes must be SMK_LABELLEN+SMK_LABELLEN+SMK_ACCESSLEN bytes. |
265 | */ | 194 | */ |
266 | static ssize_t smk_write_load(struct file *file, const char __user *buf, | 195 | static ssize_t smk_write_load_list(struct file *file, const char __user *buf, |
267 | size_t count, loff_t *ppos) | 196 | size_t count, loff_t *ppos, |
197 | struct list_head *rule_list, | ||
198 | struct mutex *rule_lock) | ||
268 | { | 199 | { |
269 | struct smack_rule *rule; | 200 | struct smack_rule *rule; |
270 | char *data; | 201 | char *data; |
271 | int rc = -EINVAL; | 202 | int rc = -EINVAL; |
272 | 203 | ||
273 | /* | 204 | /* |
274 | * Must have privilege. | ||
275 | * No partial writes. | 205 | * No partial writes. |
276 | * Enough data must be present. | 206 | * Enough data must be present. |
277 | */ | 207 | */ |
278 | if (!capable(CAP_MAC_ADMIN)) | ||
279 | return -EPERM; | ||
280 | |||
281 | if (*ppos != 0) | 208 | if (*ppos != 0) |
282 | return -EINVAL; | 209 | return -EINVAL; |
283 | /* | 210 | /* |
@@ -372,11 +299,13 @@ static ssize_t smk_write_load(struct file *file, const char __user *buf, | |||
372 | goto out_free_rule; | 299 | goto out_free_rule; |
373 | } | 300 | } |
374 | 301 | ||
375 | rc = smk_set_access(rule); | 302 | rc = count; |
376 | 303 | /* | |
377 | if (!rc) | 304 | * smk_set_access returns true if there was already a rule |
378 | rc = count; | 305 | * for the subject/object pair, and false if it was new. |
379 | goto out; | 306 | */ |
307 | if (!smk_set_access(rule, rule_list, rule_lock)) | ||
308 | goto out; | ||
380 | 309 | ||
381 | out_free_rule: | 310 | out_free_rule: |
382 | kfree(rule); | 311 | kfree(rule); |
@@ -385,6 +314,108 @@ out: | |||
385 | return rc; | 314 | return rc; |
386 | } | 315 | } |
387 | 316 | ||
317 | |||
318 | /* | ||
319 | * Seq_file read operations for /smack/load | ||
320 | */ | ||
321 | |||
322 | static void *load_seq_start(struct seq_file *s, loff_t *pos) | ||
323 | { | ||
324 | if (*pos == SEQ_READ_FINISHED) | ||
325 | return NULL; | ||
326 | if (list_empty(&smack_rule_list)) | ||
327 | return NULL; | ||
328 | return smack_rule_list.next; | ||
329 | } | ||
330 | |||
331 | static void *load_seq_next(struct seq_file *s, void *v, loff_t *pos) | ||
332 | { | ||
333 | struct list_head *list = v; | ||
334 | |||
335 | if (list_is_last(list, &smack_rule_list)) { | ||
336 | *pos = SEQ_READ_FINISHED; | ||
337 | return NULL; | ||
338 | } | ||
339 | return list->next; | ||
340 | } | ||
341 | |||
342 | static int load_seq_show(struct seq_file *s, void *v) | ||
343 | { | ||
344 | struct list_head *list = v; | ||
345 | struct smack_rule *srp = | ||
346 | list_entry(list, struct smack_rule, list); | ||
347 | |||
348 | seq_printf(s, "%s %s", (char *)srp->smk_subject, | ||
349 | (char *)srp->smk_object); | ||
350 | |||
351 | seq_putc(s, ' '); | ||
352 | |||
353 | if (srp->smk_access & MAY_READ) | ||
354 | seq_putc(s, 'r'); | ||
355 | if (srp->smk_access & MAY_WRITE) | ||
356 | seq_putc(s, 'w'); | ||
357 | if (srp->smk_access & MAY_EXEC) | ||
358 | seq_putc(s, 'x'); | ||
359 | if (srp->smk_access & MAY_APPEND) | ||
360 | seq_putc(s, 'a'); | ||
361 | if (srp->smk_access & MAY_TRANSMUTE) | ||
362 | seq_putc(s, 't'); | ||
363 | if (srp->smk_access == 0) | ||
364 | seq_putc(s, '-'); | ||
365 | |||
366 | seq_putc(s, '\n'); | ||
367 | |||
368 | return 0; | ||
369 | } | ||
370 | |||
371 | static void load_seq_stop(struct seq_file *s, void *v) | ||
372 | { | ||
373 | /* No-op */ | ||
374 | } | ||
375 | |||
376 | static const struct seq_operations load_seq_ops = { | ||
377 | .start = load_seq_start, | ||
378 | .next = load_seq_next, | ||
379 | .show = load_seq_show, | ||
380 | .stop = load_seq_stop, | ||
381 | }; | ||
382 | |||
383 | /** | ||
384 | * smk_open_load - open() for /smack/load | ||
385 | * @inode: inode structure representing file | ||
386 | * @file: "load" file pointer | ||
387 | * | ||
388 | * For reading, use load_seq_* seq_file reading operations. | ||
389 | */ | ||
390 | static int smk_open_load(struct inode *inode, struct file *file) | ||
391 | { | ||
392 | return seq_open(file, &load_seq_ops); | ||
393 | } | ||
394 | |||
395 | /** | ||
396 | * smk_write_load - write() for /smack/load | ||
397 | * @file: file pointer, not actually used | ||
398 | * @buf: where to get the data from | ||
399 | * @count: bytes sent | ||
400 | * @ppos: where to start - must be 0 | ||
401 | * | ||
402 | */ | ||
403 | static ssize_t smk_write_load(struct file *file, const char __user *buf, | ||
404 | size_t count, loff_t *ppos) | ||
405 | { | ||
406 | |||
407 | /* | ||
408 | * Must have privilege. | ||
409 | * No partial writes. | ||
410 | * Enough data must be present. | ||
411 | */ | ||
412 | if (!capable(CAP_MAC_ADMIN)) | ||
413 | return -EPERM; | ||
414 | |||
415 | return smk_write_load_list(file, buf, count, ppos, &smack_rule_list, | ||
416 | &smack_list_lock); | ||
417 | } | ||
418 | |||
388 | static const struct file_operations smk_load_ops = { | 419 | static const struct file_operations smk_load_ops = { |
389 | .open = smk_open_load, | 420 | .open = smk_open_load, |
390 | .read = seq_read, | 421 | .read = seq_read, |
@@ -1288,6 +1319,112 @@ static const struct file_operations smk_logging_ops = { | |||
1288 | .write = smk_write_logging, | 1319 | .write = smk_write_logging, |
1289 | .llseek = default_llseek, | 1320 | .llseek = default_llseek, |
1290 | }; | 1321 | }; |
1322 | |||
1323 | /* | ||
1324 | * Seq_file read operations for /smack/load-self | ||
1325 | */ | ||
1326 | |||
1327 | static void *load_self_seq_start(struct seq_file *s, loff_t *pos) | ||
1328 | { | ||
1329 | struct task_smack *tsp = current_security(); | ||
1330 | |||
1331 | if (*pos == SEQ_READ_FINISHED) | ||
1332 | return NULL; | ||
1333 | if (list_empty(&tsp->smk_rules)) | ||
1334 | return NULL; | ||
1335 | return tsp->smk_rules.next; | ||
1336 | } | ||
1337 | |||
1338 | static void *load_self_seq_next(struct seq_file *s, void *v, loff_t *pos) | ||
1339 | { | ||
1340 | struct task_smack *tsp = current_security(); | ||
1341 | struct list_head *list = v; | ||
1342 | |||
1343 | if (list_is_last(list, &tsp->smk_rules)) { | ||
1344 | *pos = SEQ_READ_FINISHED; | ||
1345 | return NULL; | ||
1346 | } | ||
1347 | return list->next; | ||
1348 | } | ||
1349 | |||
1350 | static int load_self_seq_show(struct seq_file *s, void *v) | ||
1351 | { | ||
1352 | struct list_head *list = v; | ||
1353 | struct smack_rule *srp = | ||
1354 | list_entry(list, struct smack_rule, list); | ||
1355 | |||
1356 | seq_printf(s, "%s %s", (char *)srp->smk_subject, | ||
1357 | (char *)srp->smk_object); | ||
1358 | |||
1359 | seq_putc(s, ' '); | ||
1360 | |||
1361 | if (srp->smk_access & MAY_READ) | ||
1362 | seq_putc(s, 'r'); | ||
1363 | if (srp->smk_access & MAY_WRITE) | ||
1364 | seq_putc(s, 'w'); | ||
1365 | if (srp->smk_access & MAY_EXEC) | ||
1366 | seq_putc(s, 'x'); | ||
1367 | if (srp->smk_access & MAY_APPEND) | ||
1368 | seq_putc(s, 'a'); | ||
1369 | if (srp->smk_access & MAY_TRANSMUTE) | ||
1370 | seq_putc(s, 't'); | ||
1371 | if (srp->smk_access == 0) | ||
1372 | seq_putc(s, '-'); | ||
1373 | |||
1374 | seq_putc(s, '\n'); | ||
1375 | |||
1376 | return 0; | ||
1377 | } | ||
1378 | |||
1379 | static void load_self_seq_stop(struct seq_file *s, void *v) | ||
1380 | { | ||
1381 | /* No-op */ | ||
1382 | } | ||
1383 | |||
1384 | static const struct seq_operations load_self_seq_ops = { | ||
1385 | .start = load_self_seq_start, | ||
1386 | .next = load_self_seq_next, | ||
1387 | .show = load_self_seq_show, | ||
1388 | .stop = load_self_seq_stop, | ||
1389 | }; | ||
1390 | |||
1391 | |||
1392 | /** | ||
1393 | * smk_open_load_self - open() for /smack/load-self | ||
1394 | * @inode: inode structure representing file | ||
1395 | * @file: "load" file pointer | ||
1396 | * | ||
1397 | * For reading, use load_seq_* seq_file reading operations. | ||
1398 | */ | ||
1399 | static int smk_open_load_self(struct inode *inode, struct file *file) | ||
1400 | { | ||
1401 | return seq_open(file, &load_self_seq_ops); | ||
1402 | } | ||
1403 | |||
1404 | /** | ||
1405 | * smk_write_load_self - write() for /smack/load-self | ||
1406 | * @file: file pointer, not actually used | ||
1407 | * @buf: where to get the data from | ||
1408 | * @count: bytes sent | ||
1409 | * @ppos: where to start - must be 0 | ||
1410 | * | ||
1411 | */ | ||
1412 | static ssize_t smk_write_load_self(struct file *file, const char __user *buf, | ||
1413 | size_t count, loff_t *ppos) | ||
1414 | { | ||
1415 | struct task_smack *tsp = current_security(); | ||
1416 | |||
1417 | return smk_write_load_list(file, buf, count, ppos, &tsp->smk_rules, | ||
1418 | &tsp->smk_rules_lock); | ||
1419 | } | ||
1420 | |||
1421 | static const struct file_operations smk_load_self_ops = { | ||
1422 | .open = smk_open_load_self, | ||
1423 | .read = seq_read, | ||
1424 | .llseek = seq_lseek, | ||
1425 | .write = smk_write_load_self, | ||
1426 | .release = seq_release, | ||
1427 | }; | ||
1291 | /** | 1428 | /** |
1292 | * smk_fill_super - fill the /smackfs superblock | 1429 | * smk_fill_super - fill the /smackfs superblock |
1293 | * @sb: the empty superblock | 1430 | * @sb: the empty superblock |
@@ -1304,23 +1441,26 @@ static int smk_fill_super(struct super_block *sb, void *data, int silent) | |||
1304 | struct inode *root_inode; | 1441 | struct inode *root_inode; |
1305 | 1442 | ||
1306 | static struct tree_descr smack_files[] = { | 1443 | static struct tree_descr smack_files[] = { |
1307 | [SMK_LOAD] = | 1444 | [SMK_LOAD] = { |
1308 | {"load", &smk_load_ops, S_IRUGO|S_IWUSR}, | 1445 | "load", &smk_load_ops, S_IRUGO|S_IWUSR}, |
1309 | [SMK_CIPSO] = | 1446 | [SMK_CIPSO] = { |
1310 | {"cipso", &smk_cipso_ops, S_IRUGO|S_IWUSR}, | 1447 | "cipso", &smk_cipso_ops, S_IRUGO|S_IWUSR}, |
1311 | [SMK_DOI] = | 1448 | [SMK_DOI] = { |
1312 | {"doi", &smk_doi_ops, S_IRUGO|S_IWUSR}, | 1449 | "doi", &smk_doi_ops, S_IRUGO|S_IWUSR}, |
1313 | [SMK_DIRECT] = | 1450 | [SMK_DIRECT] = { |
1314 | {"direct", &smk_direct_ops, S_IRUGO|S_IWUSR}, | 1451 | "direct", &smk_direct_ops, S_IRUGO|S_IWUSR}, |
1315 | [SMK_AMBIENT] = | 1452 | [SMK_AMBIENT] = { |
1316 | {"ambient", &smk_ambient_ops, S_IRUGO|S_IWUSR}, | 1453 | "ambient", &smk_ambient_ops, S_IRUGO|S_IWUSR}, |
1317 | [SMK_NETLBLADDR] = | 1454 | [SMK_NETLBLADDR] = { |
1318 | {"netlabel", &smk_netlbladdr_ops, S_IRUGO|S_IWUSR}, | 1455 | "netlabel", &smk_netlbladdr_ops, S_IRUGO|S_IWUSR}, |
1319 | [SMK_ONLYCAP] = | 1456 | [SMK_ONLYCAP] = { |
1320 | {"onlycap", &smk_onlycap_ops, S_IRUGO|S_IWUSR}, | 1457 | "onlycap", &smk_onlycap_ops, S_IRUGO|S_IWUSR}, |
1321 | [SMK_LOGGING] = | 1458 | [SMK_LOGGING] = { |
1322 | {"logging", &smk_logging_ops, S_IRUGO|S_IWUSR}, | 1459 | "logging", &smk_logging_ops, S_IRUGO|S_IWUSR}, |
1323 | /* last one */ {""} | 1460 | [SMK_LOAD_SELF] = { |
1461 | "load-self", &smk_load_self_ops, S_IRUGO|S_IWUGO}, | ||
1462 | /* last one */ | ||
1463 | {""} | ||
1324 | }; | 1464 | }; |
1325 | 1465 | ||
1326 | rc = simple_fill_super(sb, SMACK_MAGIC, smack_files); | 1466 | rc = simple_fill_super(sb, SMACK_MAGIC, smack_files); |