Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

apparmor: add policy revision file interface

Add a policy revision file to find the current revision of a ns's policy.
There is a revision file per ns, as well as a virtualized global revision
file in the base apparmor fs directory. The global revision file when
opened will provide the revision of the opening task namespace.

The revision file can be waited on via select/poll to detect apparmor
policy changes from the last read revision of the opened file. This
means that the revision file must be read after the select/poll other
wise update data will remain ready for reading.

Signed-off-by: John Johansen <john.johansen@canonical.com>

+116 -1
+112 -1
security/apparmor/apparmorfs.c
··· 23 23 #include <linux/capability.h> 24 24 #include <linux/rcupdate.h> 25 25 #include <linux/fs.h> 26 + #include <linux/poll.h> 26 27 #include <uapi/linux/major.h> 27 28 #include <uapi/linux/magic.h> 28 29 ··· 32 31 #include "include/audit.h" 33 32 #include "include/context.h" 34 33 #include "include/crypto.h" 34 + #include "include/policy_ns.h" 35 35 #include "include/policy.h" 36 36 #include "include/policy_ns.h" 37 37 #include "include/resource.h" ··· 500 498 .llseek = default_llseek, 501 499 }; 502 500 501 + struct aa_revision { 502 + struct aa_ns *ns; 503 + long last_read; 504 + }; 505 + 506 + /* revision file hook fn for policy loads */ 507 + static int ns_revision_release(struct inode *inode, struct file *file) 508 + { 509 + struct aa_revision *rev = file->private_data; 510 + 511 + if (rev) { 512 + aa_put_ns(rev->ns); 513 + kfree(rev); 514 + } 515 + 516 + return 0; 517 + } 518 + 519 + static ssize_t ns_revision_read(struct file *file, char __user *buf, 520 + size_t size, loff_t *ppos) 521 + { 522 + struct aa_revision *rev = file->private_data; 523 + char buffer[32]; 524 + long last_read; 525 + int avail; 526 + 527 + mutex_lock(&rev->ns->lock); 528 + last_read = rev->last_read; 529 + if (last_read == rev->ns->revision) { 530 + mutex_unlock(&rev->ns->lock); 531 + if (file->f_flags & O_NONBLOCK) 532 + return -EAGAIN; 533 + if (wait_event_interruptible(rev->ns->wait, 534 + last_read != 535 + READ_ONCE(rev->ns->revision))) 536 + return -ERESTARTSYS; 537 + mutex_lock(&rev->ns->lock); 538 + } 539 + 540 + avail = sprintf(buffer, "%ld\n", rev->ns->revision); 541 + if (*ppos + size > avail) { 542 + rev->last_read = rev->ns->revision; 543 + *ppos = 0; 544 + } 545 + mutex_unlock(&rev->ns->lock); 546 + 547 + return simple_read_from_buffer(buf, size, ppos, buffer, avail); 548 + } 549 + 550 + static int ns_revision_open(struct inode *inode, struct file *file) 551 + { 552 + struct aa_revision *rev = kzalloc(sizeof(*rev), GFP_KERNEL); 553 + 554 + if (!rev) 555 + return -ENOMEM; 556 + 557 + rev->ns = aa_get_ns(inode->i_private); 558 + if (!rev->ns) 559 + rev->ns = aa_get_current_ns(); 560 + file->private_data = rev; 561 + 562 + return 0; 563 + } 564 + 565 + static unsigned int ns_revision_poll(struct file *file, poll_table *pt) 566 + { 567 + struct aa_revision *rev = file->private_data; 568 + unsigned int mask = 0; 569 + 570 + if (rev) { 571 + mutex_lock(&rev->ns->lock); 572 + poll_wait(file, &rev->ns->wait, pt); 573 + if (rev->last_read < rev->ns->revision) 574 + mask |= POLLIN | POLLRDNORM; 575 + mutex_unlock(&rev->ns->lock); 576 + } 577 + 578 + return mask; 579 + } 580 + 503 581 void __aa_bump_ns_revision(struct aa_ns *ns) 504 582 { 505 583 ns->revision++; 584 + wake_up_interruptible(&ns->wait); 506 585 } 586 + 587 + static const struct file_operations aa_fs_ns_revision_fops = { 588 + .owner = THIS_MODULE, 589 + .open = ns_revision_open, 590 + .poll = ns_revision_poll, 591 + .read = ns_revision_read, 592 + .llseek = generic_file_llseek, 593 + .release = ns_revision_release, 594 + }; 507 595 508 596 /** 509 597 * query_data - queries a policy and writes its data to buf ··· 1372 1280 sub = d_inode(ns_subremove(ns))->i_private; 1373 1281 aa_put_ns(sub); 1374 1282 } 1283 + if (ns_subrevision(ns)) { 1284 + sub = d_inode(ns_subrevision(ns))->i_private; 1285 + aa_put_ns(sub); 1286 + } 1375 1287 1376 1288 for (i = AAFS_NS_SIZEOF - 1; i >= 0; --i) { 1377 1289 aafs_remove(ns->dents[i]); ··· 1400 1304 if (IS_ERR(dent)) 1401 1305 return PTR_ERR(dent); 1402 1306 ns_subdata_dir(ns) = dent; 1307 + 1308 + dent = aafs_create_file("revision", 0444, dir, ns, 1309 + &aa_fs_ns_revision_fops); 1310 + if (IS_ERR(dent)) 1311 + return PTR_ERR(dent); 1312 + aa_get_ns(ns); 1313 + ns_subrevision(ns) = dent; 1403 1314 1404 1315 dent = aafs_create_file(".load", 0640, dir, ns, 1405 1316 &aa_fs_profile_load); ··· 2033 1930 } 2034 1931 ns_subremove(root_ns) = dent; 2035 1932 1933 + dent = securityfs_create_file("revision", 0444, aa_sfs_entry.dentry, 1934 + NULL, &aa_fs_ns_revision_fops); 1935 + if (IS_ERR(dent)) { 1936 + error = PTR_ERR(dent); 1937 + goto error; 1938 + } 1939 + ns_subrevision(root_ns) = dent; 1940 + 1941 + /* policy tree referenced by magic policy symlink */ 2036 1942 mutex_lock(&root_ns->lock); 2037 1943 error = __aafs_ns_mkdir(root_ns, aafs_mnt->mnt_root, ".policy", 2038 1944 aafs_mnt->mnt_root); 2039 1945 mutex_unlock(&root_ns->lock); 2040 - 2041 1946 if (error) 2042 1947 goto error; 2043 1948
+2
security/apparmor/include/apparmorfs.h
··· 74 74 AAFS_NS_LOAD, 75 75 AAFS_NS_REPLACE, 76 76 AAFS_NS_REMOVE, 77 + AAFS_NS_REVISION, 77 78 AAFS_NS_COUNT, 78 79 AAFS_NS_MAX_COUNT, 79 80 AAFS_NS_SIZE, ··· 103 102 #define ns_subload(X) ((X)->dents[AAFS_NS_LOAD]) 104 103 #define ns_subreplace(X) ((X)->dents[AAFS_NS_REPLACE]) 105 104 #define ns_subremove(X) ((X)->dents[AAFS_NS_REMOVE]) 105 + #define ns_subrevision(X) ((X)->dents[AAFS_NS_REVISION]) 106 106 107 107 #define prof_dir(X) ((X)->dents[AAFS_PROF_DIR]) 108 108 #define prof_child_dir(X) ((X)->dents[AAFS_PROF_PROFS])
+1
security/apparmor/include/policy_ns.h
··· 69 69 long uniq_id; 70 70 int level; 71 71 long revision; 72 + wait_queue_head_t wait; 72 73 73 74 struct list_head rawdata_list; 74 75
+1
security/apparmor/policy_ns.c
··· 101 101 INIT_LIST_HEAD(&ns->sub_ns); 102 102 INIT_LIST_HEAD(&ns->rawdata_list); 103 103 mutex_init(&ns->lock); 104 + init_waitqueue_head(&ns->wait); 104 105 105 106 /* released by aa_free_ns() */ 106 107 ns->unconfined = aa_alloc_profile("unconfined", GFP_KERNEL);