diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-09-08 15:41:25 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-09-08 15:41:25 -0400 |
commit | b793c005ceabf6db0b17494b0ec67ade6796bb34 (patch) | |
tree | 080c884f04254403ec9564742f591a9fd9b7e95a /security/selinux/avc.c | |
parent | 6f0a2fc1feb19bd142961a39dc118e7e55418b3f (diff) | |
parent | 07f081fb5057b2ea98baeca3a47bf0eb33e94aa1 (diff) |
Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security
Pull security subsystem updates from James Morris:
"Highlights:
- PKCS#7 support added to support signed kexec, also utilized for
module signing. See comments in 3f1e1bea.
** NOTE: this requires linking against the OpenSSL library, which
must be installed, e.g. the openssl-devel on Fedora **
- Smack
- add IPv6 host labeling; ignore labels on kernel threads
- support smack labeling mounts which use binary mount data
- SELinux:
- add ioctl whitelisting (see
http://kernsec.org/files/lss2015/vanderstoep.pdf)
- fix mprotect PROT_EXEC regression caused by mm change
- Seccomp:
- add ptrace options for suspend/resume"
* 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security: (57 commits)
PKCS#7: Add OIDs for sha224, sha284 and sha512 hash algos and use them
Documentation/Changes: Now need OpenSSL devel packages for module signing
scripts: add extract-cert and sign-file to .gitignore
modsign: Handle signing key in source tree
modsign: Use if_changed rule for extracting cert from module signing key
Move certificate handling to its own directory
sign-file: Fix warning about BIO_reset() return value
PKCS#7: Add MODULE_LICENSE() to test module
Smack - Fix build error with bringup unconfigured
sign-file: Document dependency on OpenSSL devel libraries
PKCS#7: Appropriately restrict authenticated attributes and content type
KEYS: Add a name for PKEY_ID_PKCS7
PKCS#7: Improve and export the X.509 ASN.1 time object decoder
modsign: Use extract-cert to process CONFIG_SYSTEM_TRUSTED_KEYS
extract-cert: Cope with multiple X.509 certificates in a single file
sign-file: Generate CMS message as signature instead of PKCS#7
PKCS#7: Support CMS messages also [RFC5652]
X.509: Change recorded SKID & AKID to not include Subject or Issuer
PKCS#7: Check content type and versions
MAINTAINERS: The keyrings mailing list has moved
...
Diffstat (limited to 'security/selinux/avc.c')
-rw-r--r-- | security/selinux/avc.c | 418 |
1 files changed, 402 insertions, 16 deletions
diff --git a/security/selinux/avc.c b/security/selinux/avc.c index 0b122b1421a9..e60c79de13e1 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include <linux/init.h> | 22 | #include <linux/init.h> |
23 | #include <linux/skbuff.h> | 23 | #include <linux/skbuff.h> |
24 | #include <linux/percpu.h> | 24 | #include <linux/percpu.h> |
25 | #include <linux/list.h> | ||
25 | #include <net/sock.h> | 26 | #include <net/sock.h> |
26 | #include <linux/un.h> | 27 | #include <linux/un.h> |
27 | #include <net/af_unix.h> | 28 | #include <net/af_unix.h> |
@@ -48,6 +49,7 @@ struct avc_entry { | |||
48 | u32 tsid; | 49 | u32 tsid; |
49 | u16 tclass; | 50 | u16 tclass; |
50 | struct av_decision avd; | 51 | struct av_decision avd; |
52 | struct avc_xperms_node *xp_node; | ||
51 | }; | 53 | }; |
52 | 54 | ||
53 | struct avc_node { | 55 | struct avc_node { |
@@ -56,6 +58,16 @@ struct avc_node { | |||
56 | struct rcu_head rhead; | 58 | struct rcu_head rhead; |
57 | }; | 59 | }; |
58 | 60 | ||
61 | struct avc_xperms_decision_node { | ||
62 | struct extended_perms_decision xpd; | ||
63 | struct list_head xpd_list; /* list of extended_perms_decision */ | ||
64 | }; | ||
65 | |||
66 | struct avc_xperms_node { | ||
67 | struct extended_perms xp; | ||
68 | struct list_head xpd_head; /* list head of extended_perms_decision */ | ||
69 | }; | ||
70 | |||
59 | struct avc_cache { | 71 | struct avc_cache { |
60 | struct hlist_head slots[AVC_CACHE_SLOTS]; /* head for avc_node->list */ | 72 | struct hlist_head slots[AVC_CACHE_SLOTS]; /* head for avc_node->list */ |
61 | spinlock_t slots_lock[AVC_CACHE_SLOTS]; /* lock for writes */ | 73 | spinlock_t slots_lock[AVC_CACHE_SLOTS]; /* lock for writes */ |
@@ -80,6 +92,9 @@ DEFINE_PER_CPU(struct avc_cache_stats, avc_cache_stats) = { 0 }; | |||
80 | static struct avc_cache avc_cache; | 92 | static struct avc_cache avc_cache; |
81 | static struct avc_callback_node *avc_callbacks; | 93 | static struct avc_callback_node *avc_callbacks; |
82 | static struct kmem_cache *avc_node_cachep; | 94 | static struct kmem_cache *avc_node_cachep; |
95 | static struct kmem_cache *avc_xperms_data_cachep; | ||
96 | static struct kmem_cache *avc_xperms_decision_cachep; | ||
97 | static struct kmem_cache *avc_xperms_cachep; | ||
83 | 98 | ||
84 | static inline int avc_hash(u32 ssid, u32 tsid, u16 tclass) | 99 | static inline int avc_hash(u32 ssid, u32 tsid, u16 tclass) |
85 | { | 100 | { |
@@ -101,6 +116,7 @@ static void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av) | |||
101 | return; | 116 | return; |
102 | } | 117 | } |
103 | 118 | ||
119 | BUG_ON(!tclass || tclass >= ARRAY_SIZE(secclass_map)); | ||
104 | perms = secclass_map[tclass-1].perms; | 120 | perms = secclass_map[tclass-1].perms; |
105 | 121 | ||
106 | audit_log_format(ab, " {"); | 122 | audit_log_format(ab, " {"); |
@@ -149,7 +165,7 @@ static void avc_dump_query(struct audit_buffer *ab, u32 ssid, u32 tsid, u16 tcla | |||
149 | kfree(scontext); | 165 | kfree(scontext); |
150 | } | 166 | } |
151 | 167 | ||
152 | BUG_ON(tclass >= ARRAY_SIZE(secclass_map)); | 168 | BUG_ON(!tclass || tclass >= ARRAY_SIZE(secclass_map)); |
153 | audit_log_format(ab, " tclass=%s", secclass_map[tclass-1].name); | 169 | audit_log_format(ab, " tclass=%s", secclass_map[tclass-1].name); |
154 | } | 170 | } |
155 | 171 | ||
@@ -170,7 +186,17 @@ void __init avc_init(void) | |||
170 | atomic_set(&avc_cache.lru_hint, 0); | 186 | atomic_set(&avc_cache.lru_hint, 0); |
171 | 187 | ||
172 | avc_node_cachep = kmem_cache_create("avc_node", sizeof(struct avc_node), | 188 | avc_node_cachep = kmem_cache_create("avc_node", sizeof(struct avc_node), |
173 | 0, SLAB_PANIC, NULL); | 189 | 0, SLAB_PANIC, NULL); |
190 | avc_xperms_cachep = kmem_cache_create("avc_xperms_node", | ||
191 | sizeof(struct avc_xperms_node), | ||
192 | 0, SLAB_PANIC, NULL); | ||
193 | avc_xperms_decision_cachep = kmem_cache_create( | ||
194 | "avc_xperms_decision_node", | ||
195 | sizeof(struct avc_xperms_decision_node), | ||
196 | 0, SLAB_PANIC, NULL); | ||
197 | avc_xperms_data_cachep = kmem_cache_create("avc_xperms_data", | ||
198 | sizeof(struct extended_perms_data), | ||
199 | 0, SLAB_PANIC, NULL); | ||
174 | 200 | ||
175 | audit_log(current->audit_context, GFP_KERNEL, AUDIT_KERNEL, "AVC INITIALIZED\n"); | 201 | audit_log(current->audit_context, GFP_KERNEL, AUDIT_KERNEL, "AVC INITIALIZED\n"); |
176 | } | 202 | } |
@@ -205,9 +231,261 @@ int avc_get_hash_stats(char *page) | |||
205 | slots_used, AVC_CACHE_SLOTS, max_chain_len); | 231 | slots_used, AVC_CACHE_SLOTS, max_chain_len); |
206 | } | 232 | } |
207 | 233 | ||
234 | /* | ||
235 | * using a linked list for extended_perms_decision lookup because the list is | ||
236 | * always small. i.e. less than 5, typically 1 | ||
237 | */ | ||
238 | static struct extended_perms_decision *avc_xperms_decision_lookup(u8 driver, | ||
239 | struct avc_xperms_node *xp_node) | ||
240 | { | ||
241 | struct avc_xperms_decision_node *xpd_node; | ||
242 | |||
243 | list_for_each_entry(xpd_node, &xp_node->xpd_head, xpd_list) { | ||
244 | if (xpd_node->xpd.driver == driver) | ||
245 | return &xpd_node->xpd; | ||
246 | } | ||
247 | return NULL; | ||
248 | } | ||
249 | |||
250 | static inline unsigned int | ||
251 | avc_xperms_has_perm(struct extended_perms_decision *xpd, | ||
252 | u8 perm, u8 which) | ||
253 | { | ||
254 | unsigned int rc = 0; | ||
255 | |||
256 | if ((which == XPERMS_ALLOWED) && | ||
257 | (xpd->used & XPERMS_ALLOWED)) | ||
258 | rc = security_xperm_test(xpd->allowed->p, perm); | ||
259 | else if ((which == XPERMS_AUDITALLOW) && | ||
260 | (xpd->used & XPERMS_AUDITALLOW)) | ||
261 | rc = security_xperm_test(xpd->auditallow->p, perm); | ||
262 | else if ((which == XPERMS_DONTAUDIT) && | ||
263 | (xpd->used & XPERMS_DONTAUDIT)) | ||
264 | rc = security_xperm_test(xpd->dontaudit->p, perm); | ||
265 | return rc; | ||
266 | } | ||
267 | |||
268 | static void avc_xperms_allow_perm(struct avc_xperms_node *xp_node, | ||
269 | u8 driver, u8 perm) | ||
270 | { | ||
271 | struct extended_perms_decision *xpd; | ||
272 | security_xperm_set(xp_node->xp.drivers.p, driver); | ||
273 | xpd = avc_xperms_decision_lookup(driver, xp_node); | ||
274 | if (xpd && xpd->allowed) | ||
275 | security_xperm_set(xpd->allowed->p, perm); | ||
276 | } | ||
277 | |||
278 | static void avc_xperms_decision_free(struct avc_xperms_decision_node *xpd_node) | ||
279 | { | ||
280 | struct extended_perms_decision *xpd; | ||
281 | |||
282 | xpd = &xpd_node->xpd; | ||
283 | if (xpd->allowed) | ||
284 | kmem_cache_free(avc_xperms_data_cachep, xpd->allowed); | ||
285 | if (xpd->auditallow) | ||
286 | kmem_cache_free(avc_xperms_data_cachep, xpd->auditallow); | ||
287 | if (xpd->dontaudit) | ||
288 | kmem_cache_free(avc_xperms_data_cachep, xpd->dontaudit); | ||
289 | kmem_cache_free(avc_xperms_decision_cachep, xpd_node); | ||
290 | } | ||
291 | |||
292 | static void avc_xperms_free(struct avc_xperms_node *xp_node) | ||
293 | { | ||
294 | struct avc_xperms_decision_node *xpd_node, *tmp; | ||
295 | |||
296 | if (!xp_node) | ||
297 | return; | ||
298 | |||
299 | list_for_each_entry_safe(xpd_node, tmp, &xp_node->xpd_head, xpd_list) { | ||
300 | list_del(&xpd_node->xpd_list); | ||
301 | avc_xperms_decision_free(xpd_node); | ||
302 | } | ||
303 | kmem_cache_free(avc_xperms_cachep, xp_node); | ||
304 | } | ||
305 | |||
306 | static void avc_copy_xperms_decision(struct extended_perms_decision *dest, | ||
307 | struct extended_perms_decision *src) | ||
308 | { | ||
309 | dest->driver = src->driver; | ||
310 | dest->used = src->used; | ||
311 | if (dest->used & XPERMS_ALLOWED) | ||
312 | memcpy(dest->allowed->p, src->allowed->p, | ||
313 | sizeof(src->allowed->p)); | ||
314 | if (dest->used & XPERMS_AUDITALLOW) | ||
315 | memcpy(dest->auditallow->p, src->auditallow->p, | ||
316 | sizeof(src->auditallow->p)); | ||
317 | if (dest->used & XPERMS_DONTAUDIT) | ||
318 | memcpy(dest->dontaudit->p, src->dontaudit->p, | ||
319 | sizeof(src->dontaudit->p)); | ||
320 | } | ||
321 | |||
322 | /* | ||
323 | * similar to avc_copy_xperms_decision, but only copy decision | ||
324 | * information relevant to this perm | ||
325 | */ | ||
326 | static inline void avc_quick_copy_xperms_decision(u8 perm, | ||
327 | struct extended_perms_decision *dest, | ||
328 | struct extended_perms_decision *src) | ||
329 | { | ||
330 | /* | ||
331 | * compute index of the u32 of the 256 bits (8 u32s) that contain this | ||
332 | * command permission | ||
333 | */ | ||
334 | u8 i = perm >> 5; | ||
335 | |||
336 | dest->used = src->used; | ||
337 | if (dest->used & XPERMS_ALLOWED) | ||
338 | dest->allowed->p[i] = src->allowed->p[i]; | ||
339 | if (dest->used & XPERMS_AUDITALLOW) | ||
340 | dest->auditallow->p[i] = src->auditallow->p[i]; | ||
341 | if (dest->used & XPERMS_DONTAUDIT) | ||
342 | dest->dontaudit->p[i] = src->dontaudit->p[i]; | ||
343 | } | ||
344 | |||
345 | static struct avc_xperms_decision_node | ||
346 | *avc_xperms_decision_alloc(u8 which) | ||
347 | { | ||
348 | struct avc_xperms_decision_node *xpd_node; | ||
349 | struct extended_perms_decision *xpd; | ||
350 | |||
351 | xpd_node = kmem_cache_zalloc(avc_xperms_decision_cachep, | ||
352 | GFP_ATOMIC | __GFP_NOMEMALLOC); | ||
353 | if (!xpd_node) | ||
354 | return NULL; | ||
355 | |||
356 | xpd = &xpd_node->xpd; | ||
357 | if (which & XPERMS_ALLOWED) { | ||
358 | xpd->allowed = kmem_cache_zalloc(avc_xperms_data_cachep, | ||
359 | GFP_ATOMIC | __GFP_NOMEMALLOC); | ||
360 | if (!xpd->allowed) | ||
361 | goto error; | ||
362 | } | ||
363 | if (which & XPERMS_AUDITALLOW) { | ||
364 | xpd->auditallow = kmem_cache_zalloc(avc_xperms_data_cachep, | ||
365 | GFP_ATOMIC | __GFP_NOMEMALLOC); | ||
366 | if (!xpd->auditallow) | ||
367 | goto error; | ||
368 | } | ||
369 | if (which & XPERMS_DONTAUDIT) { | ||
370 | xpd->dontaudit = kmem_cache_zalloc(avc_xperms_data_cachep, | ||
371 | GFP_ATOMIC | __GFP_NOMEMALLOC); | ||
372 | if (!xpd->dontaudit) | ||
373 | goto error; | ||
374 | } | ||
375 | return xpd_node; | ||
376 | error: | ||
377 | avc_xperms_decision_free(xpd_node); | ||
378 | return NULL; | ||
379 | } | ||
380 | |||
381 | static int avc_add_xperms_decision(struct avc_node *node, | ||
382 | struct extended_perms_decision *src) | ||
383 | { | ||
384 | struct avc_xperms_decision_node *dest_xpd; | ||
385 | |||
386 | node->ae.xp_node->xp.len++; | ||
387 | dest_xpd = avc_xperms_decision_alloc(src->used); | ||
388 | if (!dest_xpd) | ||
389 | return -ENOMEM; | ||
390 | avc_copy_xperms_decision(&dest_xpd->xpd, src); | ||
391 | list_add(&dest_xpd->xpd_list, &node->ae.xp_node->xpd_head); | ||
392 | return 0; | ||
393 | } | ||
394 | |||
395 | static struct avc_xperms_node *avc_xperms_alloc(void) | ||
396 | { | ||
397 | struct avc_xperms_node *xp_node; | ||
398 | |||
399 | xp_node = kmem_cache_zalloc(avc_xperms_cachep, | ||
400 | GFP_ATOMIC|__GFP_NOMEMALLOC); | ||
401 | if (!xp_node) | ||
402 | return xp_node; | ||
403 | INIT_LIST_HEAD(&xp_node->xpd_head); | ||
404 | return xp_node; | ||
405 | } | ||
406 | |||
407 | static int avc_xperms_populate(struct avc_node *node, | ||
408 | struct avc_xperms_node *src) | ||
409 | { | ||
410 | struct avc_xperms_node *dest; | ||
411 | struct avc_xperms_decision_node *dest_xpd; | ||
412 | struct avc_xperms_decision_node *src_xpd; | ||
413 | |||
414 | if (src->xp.len == 0) | ||
415 | return 0; | ||
416 | dest = avc_xperms_alloc(); | ||
417 | if (!dest) | ||
418 | return -ENOMEM; | ||
419 | |||
420 | memcpy(dest->xp.drivers.p, src->xp.drivers.p, sizeof(dest->xp.drivers.p)); | ||
421 | dest->xp.len = src->xp.len; | ||
422 | |||
423 | /* for each source xpd allocate a destination xpd and copy */ | ||
424 | list_for_each_entry(src_xpd, &src->xpd_head, xpd_list) { | ||
425 | dest_xpd = avc_xperms_decision_alloc(src_xpd->xpd.used); | ||
426 | if (!dest_xpd) | ||
427 | goto error; | ||
428 | avc_copy_xperms_decision(&dest_xpd->xpd, &src_xpd->xpd); | ||
429 | list_add(&dest_xpd->xpd_list, &dest->xpd_head); | ||
430 | } | ||
431 | node->ae.xp_node = dest; | ||
432 | return 0; | ||
433 | error: | ||
434 | avc_xperms_free(dest); | ||
435 | return -ENOMEM; | ||
436 | |||
437 | } | ||
438 | |||
439 | static inline u32 avc_xperms_audit_required(u32 requested, | ||
440 | struct av_decision *avd, | ||
441 | struct extended_perms_decision *xpd, | ||
442 | u8 perm, | ||
443 | int result, | ||
444 | u32 *deniedp) | ||
445 | { | ||
446 | u32 denied, audited; | ||
447 | |||
448 | denied = requested & ~avd->allowed; | ||
449 | if (unlikely(denied)) { | ||
450 | audited = denied & avd->auditdeny; | ||
451 | if (audited && xpd) { | ||
452 | if (avc_xperms_has_perm(xpd, perm, XPERMS_DONTAUDIT)) | ||
453 | audited &= ~requested; | ||
454 | } | ||
455 | } else if (result) { | ||
456 | audited = denied = requested; | ||
457 | } else { | ||
458 | audited = requested & avd->auditallow; | ||
459 | if (audited && xpd) { | ||
460 | if (!avc_xperms_has_perm(xpd, perm, XPERMS_AUDITALLOW)) | ||
461 | audited &= ~requested; | ||
462 | } | ||
463 | } | ||
464 | |||
465 | *deniedp = denied; | ||
466 | return audited; | ||
467 | } | ||
468 | |||
469 | static inline int avc_xperms_audit(u32 ssid, u32 tsid, u16 tclass, | ||
470 | u32 requested, struct av_decision *avd, | ||
471 | struct extended_perms_decision *xpd, | ||
472 | u8 perm, int result, | ||
473 | struct common_audit_data *ad) | ||
474 | { | ||
475 | u32 audited, denied; | ||
476 | |||
477 | audited = avc_xperms_audit_required( | ||
478 | requested, avd, xpd, perm, result, &denied); | ||
479 | if (likely(!audited)) | ||
480 | return 0; | ||
481 | return slow_avc_audit(ssid, tsid, tclass, requested, | ||
482 | audited, denied, result, ad, 0); | ||
483 | } | ||
484 | |||
208 | static void avc_node_free(struct rcu_head *rhead) | 485 | static void avc_node_free(struct rcu_head *rhead) |
209 | { | 486 | { |
210 | struct avc_node *node = container_of(rhead, struct avc_node, rhead); | 487 | struct avc_node *node = container_of(rhead, struct avc_node, rhead); |
488 | avc_xperms_free(node->ae.xp_node); | ||
211 | kmem_cache_free(avc_node_cachep, node); | 489 | kmem_cache_free(avc_node_cachep, node); |
212 | avc_cache_stats_incr(frees); | 490 | avc_cache_stats_incr(frees); |
213 | } | 491 | } |
@@ -221,6 +499,7 @@ static void avc_node_delete(struct avc_node *node) | |||
221 | 499 | ||
222 | static void avc_node_kill(struct avc_node *node) | 500 | static void avc_node_kill(struct avc_node *node) |
223 | { | 501 | { |
502 | avc_xperms_free(node->ae.xp_node); | ||
224 | kmem_cache_free(avc_node_cachep, node); | 503 | kmem_cache_free(avc_node_cachep, node); |
225 | avc_cache_stats_incr(frees); | 504 | avc_cache_stats_incr(frees); |
226 | atomic_dec(&avc_cache.active_nodes); | 505 | atomic_dec(&avc_cache.active_nodes); |
@@ -367,6 +646,7 @@ static int avc_latest_notif_update(int seqno, int is_insert) | |||
367 | * @tsid: target security identifier | 646 | * @tsid: target security identifier |
368 | * @tclass: target security class | 647 | * @tclass: target security class |
369 | * @avd: resulting av decision | 648 | * @avd: resulting av decision |
649 | * @xp_node: resulting extended permissions | ||
370 | * | 650 | * |
371 | * Insert an AVC entry for the SID pair | 651 | * Insert an AVC entry for the SID pair |
372 | * (@ssid, @tsid) and class @tclass. | 652 | * (@ssid, @tsid) and class @tclass. |
@@ -378,7 +658,9 @@ static int avc_latest_notif_update(int seqno, int is_insert) | |||
378 | * the access vectors into a cache entry, returns | 658 | * the access vectors into a cache entry, returns |
379 | * avc_node inserted. Otherwise, this function returns NULL. | 659 | * avc_node inserted. Otherwise, this function returns NULL. |
380 | */ | 660 | */ |
381 | static struct avc_node *avc_insert(u32 ssid, u32 tsid, u16 tclass, struct av_decision *avd) | 661 | static struct avc_node *avc_insert(u32 ssid, u32 tsid, u16 tclass, |
662 | struct av_decision *avd, | ||
663 | struct avc_xperms_node *xp_node) | ||
382 | { | 664 | { |
383 | struct avc_node *pos, *node = NULL; | 665 | struct avc_node *pos, *node = NULL; |
384 | int hvalue; | 666 | int hvalue; |
@@ -391,10 +673,15 @@ static struct avc_node *avc_insert(u32 ssid, u32 tsid, u16 tclass, struct av_dec | |||
391 | if (node) { | 673 | if (node) { |
392 | struct hlist_head *head; | 674 | struct hlist_head *head; |
393 | spinlock_t *lock; | 675 | spinlock_t *lock; |
676 | int rc = 0; | ||
394 | 677 | ||
395 | hvalue = avc_hash(ssid, tsid, tclass); | 678 | hvalue = avc_hash(ssid, tsid, tclass); |
396 | avc_node_populate(node, ssid, tsid, tclass, avd); | 679 | avc_node_populate(node, ssid, tsid, tclass, avd); |
397 | 680 | rc = avc_xperms_populate(node, xp_node); | |
681 | if (rc) { | ||
682 | kmem_cache_free(avc_node_cachep, node); | ||
683 | return NULL; | ||
684 | } | ||
398 | head = &avc_cache.slots[hvalue]; | 685 | head = &avc_cache.slots[hvalue]; |
399 | lock = &avc_cache.slots_lock[hvalue]; | 686 | lock = &avc_cache.slots_lock[hvalue]; |
400 | 687 | ||
@@ -523,14 +810,17 @@ out: | |||
523 | * @perms : Permission mask bits | 810 | * @perms : Permission mask bits |
524 | * @ssid,@tsid,@tclass : identifier of an AVC entry | 811 | * @ssid,@tsid,@tclass : identifier of an AVC entry |
525 | * @seqno : sequence number when decision was made | 812 | * @seqno : sequence number when decision was made |
813 | * @xpd: extended_perms_decision to be added to the node | ||
526 | * | 814 | * |
527 | * if a valid AVC entry doesn't exist,this function returns -ENOENT. | 815 | * if a valid AVC entry doesn't exist,this function returns -ENOENT. |
528 | * if kmalloc() called internal returns NULL, this function returns -ENOMEM. | 816 | * if kmalloc() called internal returns NULL, this function returns -ENOMEM. |
529 | * otherwise, this function updates the AVC entry. The original AVC-entry object | 817 | * otherwise, this function updates the AVC entry. The original AVC-entry object |
530 | * will release later by RCU. | 818 | * will release later by RCU. |
531 | */ | 819 | */ |
532 | static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass, | 820 | static int avc_update_node(u32 event, u32 perms, u8 driver, u8 xperm, u32 ssid, |
533 | u32 seqno) | 821 | u32 tsid, u16 tclass, u32 seqno, |
822 | struct extended_perms_decision *xpd, | ||
823 | u32 flags) | ||
534 | { | 824 | { |
535 | int hvalue, rc = 0; | 825 | int hvalue, rc = 0; |
536 | unsigned long flag; | 826 | unsigned long flag; |
@@ -574,9 +864,19 @@ static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass, | |||
574 | 864 | ||
575 | avc_node_populate(node, ssid, tsid, tclass, &orig->ae.avd); | 865 | avc_node_populate(node, ssid, tsid, tclass, &orig->ae.avd); |
576 | 866 | ||
867 | if (orig->ae.xp_node) { | ||
868 | rc = avc_xperms_populate(node, orig->ae.xp_node); | ||
869 | if (rc) { | ||
870 | kmem_cache_free(avc_node_cachep, node); | ||
871 | goto out_unlock; | ||
872 | } | ||
873 | } | ||
874 | |||
577 | switch (event) { | 875 | switch (event) { |
578 | case AVC_CALLBACK_GRANT: | 876 | case AVC_CALLBACK_GRANT: |
579 | node->ae.avd.allowed |= perms; | 877 | node->ae.avd.allowed |= perms; |
878 | if (node->ae.xp_node && (flags & AVC_EXTENDED_PERMS)) | ||
879 | avc_xperms_allow_perm(node->ae.xp_node, driver, xperm); | ||
580 | break; | 880 | break; |
581 | case AVC_CALLBACK_TRY_REVOKE: | 881 | case AVC_CALLBACK_TRY_REVOKE: |
582 | case AVC_CALLBACK_REVOKE: | 882 | case AVC_CALLBACK_REVOKE: |
@@ -594,6 +894,9 @@ static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass, | |||
594 | case AVC_CALLBACK_AUDITDENY_DISABLE: | 894 | case AVC_CALLBACK_AUDITDENY_DISABLE: |
595 | node->ae.avd.auditdeny &= ~perms; | 895 | node->ae.avd.auditdeny &= ~perms; |
596 | break; | 896 | break; |
897 | case AVC_CALLBACK_ADD_XPERMS: | ||
898 | avc_add_xperms_decision(node, xpd); | ||
899 | break; | ||
597 | } | 900 | } |
598 | avc_node_replace(node, orig); | 901 | avc_node_replace(node, orig); |
599 | out_unlock: | 902 | out_unlock: |
@@ -665,18 +968,20 @@ int avc_ss_reset(u32 seqno) | |||
665 | * results in a bigger stack frame. | 968 | * results in a bigger stack frame. |
666 | */ | 969 | */ |
667 | static noinline struct avc_node *avc_compute_av(u32 ssid, u32 tsid, | 970 | static noinline struct avc_node *avc_compute_av(u32 ssid, u32 tsid, |
668 | u16 tclass, struct av_decision *avd) | 971 | u16 tclass, struct av_decision *avd, |
972 | struct avc_xperms_node *xp_node) | ||
669 | { | 973 | { |
670 | rcu_read_unlock(); | 974 | rcu_read_unlock(); |
671 | security_compute_av(ssid, tsid, tclass, avd); | 975 | INIT_LIST_HEAD(&xp_node->xpd_head); |
976 | security_compute_av(ssid, tsid, tclass, avd, &xp_node->xp); | ||
672 | rcu_read_lock(); | 977 | rcu_read_lock(); |
673 | return avc_insert(ssid, tsid, tclass, avd); | 978 | return avc_insert(ssid, tsid, tclass, avd, xp_node); |
674 | } | 979 | } |
675 | 980 | ||
676 | static noinline int avc_denied(u32 ssid, u32 tsid, | 981 | static noinline int avc_denied(u32 ssid, u32 tsid, |
677 | u16 tclass, u32 requested, | 982 | u16 tclass, u32 requested, |
678 | unsigned flags, | 983 | u8 driver, u8 xperm, unsigned flags, |
679 | struct av_decision *avd) | 984 | struct av_decision *avd) |
680 | { | 985 | { |
681 | if (flags & AVC_STRICT) | 986 | if (flags & AVC_STRICT) |
682 | return -EACCES; | 987 | return -EACCES; |
@@ -684,11 +989,91 @@ static noinline int avc_denied(u32 ssid, u32 tsid, | |||
684 | if (selinux_enforcing && !(avd->flags & AVD_FLAGS_PERMISSIVE)) | 989 | if (selinux_enforcing && !(avd->flags & AVD_FLAGS_PERMISSIVE)) |
685 | return -EACCES; | 990 | return -EACCES; |
686 | 991 | ||
687 | avc_update_node(AVC_CALLBACK_GRANT, requested, ssid, | 992 | avc_update_node(AVC_CALLBACK_GRANT, requested, driver, xperm, ssid, |
688 | tsid, tclass, avd->seqno); | 993 | tsid, tclass, avd->seqno, NULL, flags); |
689 | return 0; | 994 | return 0; |
690 | } | 995 | } |
691 | 996 | ||
997 | /* | ||
998 | * The avc extended permissions logic adds an additional 256 bits of | ||
999 | * permissions to an avc node when extended permissions for that node are | ||
1000 | * specified in the avtab. If the additional 256 permissions is not adequate, | ||
1001 | * as-is the case with ioctls, then multiple may be chained together and the | ||
1002 | * driver field is used to specify which set contains the permission. | ||
1003 | */ | ||
1004 | int avc_has_extended_perms(u32 ssid, u32 tsid, u16 tclass, u32 requested, | ||
1005 | u8 driver, u8 xperm, struct common_audit_data *ad) | ||
1006 | { | ||
1007 | struct avc_node *node; | ||
1008 | struct av_decision avd; | ||
1009 | u32 denied; | ||
1010 | struct extended_perms_decision local_xpd; | ||
1011 | struct extended_perms_decision *xpd = NULL; | ||
1012 | struct extended_perms_data allowed; | ||
1013 | struct extended_perms_data auditallow; | ||
1014 | struct extended_perms_data dontaudit; | ||
1015 | struct avc_xperms_node local_xp_node; | ||
1016 | struct avc_xperms_node *xp_node; | ||
1017 | int rc = 0, rc2; | ||
1018 | |||
1019 | xp_node = &local_xp_node; | ||
1020 | BUG_ON(!requested); | ||
1021 | |||
1022 | rcu_read_lock(); | ||
1023 | |||
1024 | node = avc_lookup(ssid, tsid, tclass); | ||
1025 | if (unlikely(!node)) { | ||
1026 | node = avc_compute_av(ssid, tsid, tclass, &avd, xp_node); | ||
1027 | } else { | ||
1028 | memcpy(&avd, &node->ae.avd, sizeof(avd)); | ||
1029 | xp_node = node->ae.xp_node; | ||
1030 | } | ||
1031 | /* if extended permissions are not defined, only consider av_decision */ | ||
1032 | if (!xp_node || !xp_node->xp.len) | ||
1033 | goto decision; | ||
1034 | |||
1035 | local_xpd.allowed = &allowed; | ||
1036 | local_xpd.auditallow = &auditallow; | ||
1037 | local_xpd.dontaudit = &dontaudit; | ||
1038 | |||
1039 | xpd = avc_xperms_decision_lookup(driver, xp_node); | ||
1040 | if (unlikely(!xpd)) { | ||
1041 | /* | ||
1042 | * Compute the extended_perms_decision only if the driver | ||
1043 | * is flagged | ||
1044 | */ | ||
1045 | if (!security_xperm_test(xp_node->xp.drivers.p, driver)) { | ||
1046 | avd.allowed &= ~requested; | ||
1047 | goto decision; | ||
1048 | } | ||
1049 | rcu_read_unlock(); | ||
1050 | security_compute_xperms_decision(ssid, tsid, tclass, driver, | ||
1051 | &local_xpd); | ||
1052 | rcu_read_lock(); | ||
1053 | avc_update_node(AVC_CALLBACK_ADD_XPERMS, requested, driver, xperm, | ||
1054 | ssid, tsid, tclass, avd.seqno, &local_xpd, 0); | ||
1055 | } else { | ||
1056 | avc_quick_copy_xperms_decision(xperm, &local_xpd, xpd); | ||
1057 | } | ||
1058 | xpd = &local_xpd; | ||
1059 | |||
1060 | if (!avc_xperms_has_perm(xpd, xperm, XPERMS_ALLOWED)) | ||
1061 | avd.allowed &= ~requested; | ||
1062 | |||
1063 | decision: | ||
1064 | denied = requested & ~(avd.allowed); | ||
1065 | if (unlikely(denied)) | ||
1066 | rc = avc_denied(ssid, tsid, tclass, requested, driver, xperm, | ||
1067 | AVC_EXTENDED_PERMS, &avd); | ||
1068 | |||
1069 | rcu_read_unlock(); | ||
1070 | |||
1071 | rc2 = avc_xperms_audit(ssid, tsid, tclass, requested, | ||
1072 | &avd, xpd, xperm, rc, ad); | ||
1073 | if (rc2) | ||
1074 | return rc2; | ||
1075 | return rc; | ||
1076 | } | ||
692 | 1077 | ||
693 | /** | 1078 | /** |
694 | * avc_has_perm_noaudit - Check permissions but perform no auditing. | 1079 | * avc_has_perm_noaudit - Check permissions but perform no auditing. |
@@ -716,6 +1101,7 @@ inline int avc_has_perm_noaudit(u32 ssid, u32 tsid, | |||
716 | struct av_decision *avd) | 1101 | struct av_decision *avd) |
717 | { | 1102 | { |
718 | struct avc_node *node; | 1103 | struct avc_node *node; |
1104 | struct avc_xperms_node xp_node; | ||
719 | int rc = 0; | 1105 | int rc = 0; |
720 | u32 denied; | 1106 | u32 denied; |
721 | 1107 | ||
@@ -725,13 +1111,13 @@ inline int avc_has_perm_noaudit(u32 ssid, u32 tsid, | |||
725 | 1111 | ||
726 | node = avc_lookup(ssid, tsid, tclass); | 1112 | node = avc_lookup(ssid, tsid, tclass); |
727 | if (unlikely(!node)) | 1113 | if (unlikely(!node)) |
728 | node = avc_compute_av(ssid, tsid, tclass, avd); | 1114 | node = avc_compute_av(ssid, tsid, tclass, avd, &xp_node); |
729 | else | 1115 | else |
730 | memcpy(avd, &node->ae.avd, sizeof(*avd)); | 1116 | memcpy(avd, &node->ae.avd, sizeof(*avd)); |
731 | 1117 | ||
732 | denied = requested & ~(avd->allowed); | 1118 | denied = requested & ~(avd->allowed); |
733 | if (unlikely(denied)) | 1119 | if (unlikely(denied)) |
734 | rc = avc_denied(ssid, tsid, tclass, requested, flags, avd); | 1120 | rc = avc_denied(ssid, tsid, tclass, requested, 0, 0, flags, avd); |
735 | 1121 | ||
736 | rcu_read_unlock(); | 1122 | rcu_read_unlock(); |
737 | return rc; | 1123 | return rc; |