From 02125a826459a6ad142f8d91c5b6357562f96615 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 5 Dec 2011 08:43:34 -0500 Subject: fix apparmor dereferencing potentially freed dentry, sanitize __d_path() API __d_path() API is asking for trouble and in case of apparmor d_namespace_path() getting just that. The root cause is that when __d_path() misses the root it had been told to look for, it stores the location of the most remote ancestor in *root. Without grabbing references. Sure, at the moment of call it had been pinned down by what we have in *path. And if we raced with umount -l, we could have very well stopped at vfsmount/dentry that got freed as soon as prepend_path() dropped vfsmount_lock. It is safe to compare these pointers with pre-existing (and known to be still alive) vfsmount and dentry, as long as all we are asking is "is it the same address?". Dereferencing is not safe and apparmor ended up stepping into that. d_namespace_path() really wants to examine the place where we stopped, even if it's not connected to our namespace. As the result, it looked at ->d_sb->s_magic of a dentry that might've been already freed by that point. All other callers had been careful enough to avoid that, but it's really a bad interface - it invites that kind of trouble. The fix is fairly straightforward, even though it's bigger than I'd like: * prepend_path() root argument becomes const. * __d_path() is never called with NULL/NULL root. It was a kludge to start with. Instead, we have an explicit function - d_absolute_root(). Same as __d_path(), except that it doesn't get root passed and stops where it stops. apparmor and tomoyo are using it. * __d_path() returns NULL on path outside of root. The main caller is show_mountinfo() and that's precisely what we pass root for - to skip those outside chroot jail. Those who don't want that can (and do) use d_path(). * __d_path() root argument becomes const. Everyone agrees, I hope. * apparmor does *NOT* try to use __d_path() or any of its variants when it sees that path->mnt is an internal vfsmount. In that case it's definitely not mounted anywhere and dentry_path() is exactly what we want there. Handling of sysctl()-triggered weirdness is moved to that place. * if apparmor is asked to do pathname relative to chroot jail and __d_path() tells it we it's not in that jail, the sucker just calls d_absolute_path() instead. That's the other remaining caller of __d_path(), BTW. * seq_path_root() does _NOT_ return -ENAMETOOLONG (it's stupid anyway - the normal seq_file logics will take care of growing the buffer and redoing the call of ->show() just fine). However, if it gets path not reachable from root, it returns SEQ_SKIP. The only caller adjusted (i.e. stopped ignoring the return value as it used to do). Reviewed-by: John Johansen ACKed-by: John Johansen Signed-off-by: Al Viro Cc: stable@vger.kernel.org --- security/apparmor/path.c | 65 +++++++++++++++++++++++++++------------------- security/tomoyo/realpath.c | 3 +-- 2 files changed, 39 insertions(+), 29 deletions(-) (limited to 'security') diff --git a/security/apparmor/path.c b/security/apparmor/path.c index 36cc0cc39e78..b566eba4a65c 100644 --- a/security/apparmor/path.c +++ b/security/apparmor/path.c @@ -57,23 +57,44 @@ static int prepend(char **buffer, int buflen, const char *str, int namelen) static int d_namespace_path(struct path *path, char *buf, int buflen, char **name, int flags) { - struct path root, tmp; char *res; - int connected, error = 0; + int error = 0; + int connected = 1; + + if (path->mnt->mnt_flags & MNT_INTERNAL) { + /* it's not mounted anywhere */ + res = dentry_path(path->dentry, buf, buflen); + *name = res; + if (IS_ERR(res)) { + *name = buf; + return PTR_ERR(res); + } + if (path->dentry->d_sb->s_magic == PROC_SUPER_MAGIC && + strncmp(*name, "/sys/", 5) == 0) { + /* TODO: convert over to using a per namespace + * control instead of hard coded /proc + */ + return prepend(name, *name - buf, "/proc", 5); + } + return 0; + } - /* Get the root we want to resolve too, released below */ + /* resolve paths relative to chroot?*/ if (flags & PATH_CHROOT_REL) { - /* resolve paths relative to chroot */ + struct path root; get_fs_root(current->fs, &root); - } else { - /* resolve paths relative to namespace */ - root.mnt = current->nsproxy->mnt_ns->root; - root.dentry = root.mnt->mnt_root; - path_get(&root); + res = __d_path(path, &root, buf, buflen); + if (res && !IS_ERR(res)) { + /* everything's fine */ + *name = res; + path_put(&root); + goto ok; + } + path_put(&root); + connected = 0; } - tmp = root; - res = __d_path(path, &tmp, buf, buflen); + res = d_absolute_path(path, buf, buflen); *name = res; /* handle error conditions - and still allow a partial path to @@ -84,7 +105,10 @@ static int d_namespace_path(struct path *path, char *buf, int buflen, *name = buf; goto out; } + if (!our_mnt(path->mnt)) + connected = 0; +ok: /* Handle two cases: * 1. A deleted dentry && profile is not allowing mediation of deleted * 2. On some filesystems, newly allocated dentries appear to the @@ -97,10 +121,7 @@ static int d_namespace_path(struct path *path, char *buf, int buflen, goto out; } - /* Determine if the path is connected to the expected root */ - connected = tmp.dentry == root.dentry && tmp.mnt == root.mnt; - - /* If the path is not connected, + /* If the path is not connected to the expected root, * check if it is a sysctl and handle specially else remove any * leading / that __d_path may have returned. * Unless @@ -112,17 +133,9 @@ static int d_namespace_path(struct path *path, char *buf, int buflen, * namespace root. */ if (!connected) { - /* is the disconnect path a sysctl? */ - if (tmp.dentry->d_sb->s_magic == PROC_SUPER_MAGIC && - strncmp(*name, "/sys/", 5) == 0) { - /* TODO: convert over to using a per namespace - * control instead of hard coded /proc - */ - error = prepend(name, *name - buf, "/proc", 5); - } else if (!(flags & PATH_CONNECT_PATH) && + if (!(flags & PATH_CONNECT_PATH) && !(((flags & CHROOT_NSCONNECT) == CHROOT_NSCONNECT) && - (tmp.mnt == current->nsproxy->mnt_ns->root && - tmp.dentry == tmp.mnt->mnt_root))) { + our_mnt(path->mnt))) { /* disconnected path, don't return pathname starting * with '/' */ @@ -133,8 +146,6 @@ static int d_namespace_path(struct path *path, char *buf, int buflen, } out: - path_put(&root); - return error; } diff --git a/security/tomoyo/realpath.c b/security/tomoyo/realpath.c index 738bbdf8d4c7..36fa7c9bedc4 100644 --- a/security/tomoyo/realpath.c +++ b/security/tomoyo/realpath.c @@ -101,9 +101,8 @@ static char *tomoyo_get_absolute_path(struct path *path, char * const buffer, { char *pos = ERR_PTR(-ENOMEM); if (buflen >= 256) { - struct path ns_root = { }; /* go to whatever namespace root we are under */ - pos = __d_path(path, &ns_root, buffer, buflen - 1); + pos = d_absolute_path(path, buffer, buflen - 1); if (!IS_ERR(pos) && *pos == '/' && pos[1]) { struct inode *inode = path->dentry->d_inode; if (inode && S_ISDIR(inode->i_mode)) { -- cgit v1.2.1 From 1418a3e5ad4d01b1d4abf2c479c50b0cedd59e3f Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Thu, 8 Dec 2011 21:24:06 +0900 Subject: TOMOYO: Fix pathname handling of disconnected paths. Current tomoyo_realpath_from_path() implementation returns strange pathname when calculating pathname of a file which belongs to lazy unmounted tree. Use local pathname rather than strange absolute pathname in that case. Also, this patch fixes a regression by commit 02125a82 "fix apparmor dereferencing potentially freed dentry, sanitize __d_path() API". Signed-off-by: Tetsuo Handa Acked-by: Al Viro Cc: stable@vger.kernel.org Signed-off-by: Linus Torvalds --- security/tomoyo/realpath.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'security') diff --git a/security/tomoyo/realpath.c b/security/tomoyo/realpath.c index 36fa7c9bedc4..d9f3ced8756e 100644 --- a/security/tomoyo/realpath.c +++ b/security/tomoyo/realpath.c @@ -293,8 +293,16 @@ char *tomoyo_realpath_from_path(struct path *path) pos = tomoyo_get_local_path(path->dentry, buf, buf_len - 1); /* Get absolute name for the rest. */ - else + else { pos = tomoyo_get_absolute_path(path, buf, buf_len - 1); + /* + * Fall back to local name if absolute name is not + * available. + */ + if (pos == ERR_PTR(-EINVAL)) + pos = tomoyo_get_local_path(path->dentry, buf, + buf_len - 1); + } encode: if (IS_ERR(pos)) continue; -- cgit v1.2.1 From d21b59451886cb82448302f8d6f9ac87c3bd56cf Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Mon, 5 Dec 2011 13:17:41 +0200 Subject: evm: key must be set once during initialization On multi-core systems, setting of the key before every caclculation, causes invalid HMAC calculation for other tfm users, because internal state (ipad, opad) can be invalid before set key call returns. It needs to be set only once during initialization. Signed-off-by: Dmitry Kasatkin Acked-by: Mimi Zohar Signed-off-by: James Morris --- security/integrity/evm/evm_crypto.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'security') diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c index 5dd5b140242c..4ad657d88097 100644 --- a/security/integrity/evm/evm_crypto.c +++ b/security/integrity/evm/evm_crypto.c @@ -41,6 +41,12 @@ static struct shash_desc *init_desc(void) hmac_tfm = NULL; return ERR_PTR(rc); } + rc = crypto_shash_setkey(hmac_tfm, evmkey, evmkey_len); + if (rc) { + crypto_free_shash(hmac_tfm); + hmac_tfm = NULL; + return ERR_PTR(rc); + } } desc = kmalloc(sizeof(*desc) + crypto_shash_descsize(hmac_tfm), @@ -51,11 +57,7 @@ static struct shash_desc *init_desc(void) desc->tfm = hmac_tfm; desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP; - rc = crypto_shash_setkey(hmac_tfm, evmkey, evmkey_len); - if (rc) - goto out; rc = crypto_shash_init(desc); -out: if (rc) { kfree(desc); return ERR_PTR(rc); -- cgit v1.2.1 From 97426f985729573cea06e82e271cc3929f1f5f8e Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Mon, 5 Dec 2011 13:17:42 +0200 Subject: evm: prevent racing during tfm allocation There is a small chance of racing during tfm allocation. This patch fixes it. Signed-off-by: Dmitry Kasatkin Acked-by: Mimi Zohar Signed-off-by: James Morris --- security/integrity/evm/evm_crypto.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'security') diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c index 4ad657d88097..8738deff26fa 100644 --- a/security/integrity/evm/evm_crypto.c +++ b/security/integrity/evm/evm_crypto.c @@ -27,26 +27,35 @@ static int evmkey_len = MAX_KEY_SIZE; struct crypto_shash *hmac_tfm; +static DEFINE_MUTEX(mutex); + static struct shash_desc *init_desc(void) { int rc; struct shash_desc *desc; if (hmac_tfm == NULL) { + mutex_lock(&mutex); + if (hmac_tfm) + goto out; hmac_tfm = crypto_alloc_shash(evm_hmac, 0, CRYPTO_ALG_ASYNC); if (IS_ERR(hmac_tfm)) { pr_err("Can not allocate %s (reason: %ld)\n", evm_hmac, PTR_ERR(hmac_tfm)); rc = PTR_ERR(hmac_tfm); hmac_tfm = NULL; + mutex_unlock(&mutex); return ERR_PTR(rc); } rc = crypto_shash_setkey(hmac_tfm, evmkey, evmkey_len); if (rc) { crypto_free_shash(hmac_tfm); hmac_tfm = NULL; + mutex_unlock(&mutex); return ERR_PTR(rc); } +out: + mutex_unlock(&mutex); } desc = kmalloc(sizeof(*desc) + crypto_shash_descsize(hmac_tfm), -- cgit v1.2.1 From 50345f1ea9cda4618d9c26e590a97ecd4bc7ac75 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 13 Dec 2011 14:49:04 +0000 Subject: SELinux: Fix RCU deref check warning in sel_netport_insert() Fix the following bug in sel_netport_insert() where rcu_dereference() should be rcu_dereference_protected() as sel_netport_lock is held. =================================================== [ INFO: suspicious rcu_dereference_check() usage. ] --------------------------------------------------- security/selinux/netport.c:127 invoked rcu_dereference_check() without protection! other info that might help us debug this: rcu_scheduler_active = 1, debug_locks = 0 1 lock held by ossec-rootcheck/3323: #0: (sel_netport_lock){+.....}, at: [] sel_netport_sid+0xbb/0x226 stack backtrace: Pid: 3323, comm: ossec-rootcheck Not tainted 3.1.0-rc8-fsdevel+ #1095 Call Trace: [] lockdep_rcu_dereference+0xa7/0xb0 [] sel_netport_sid+0x1b7/0x226 [] ? sel_netport_avc_callback+0xbc/0xbc [] selinux_socket_bind+0x115/0x230 [] ? might_fault+0x4e/0x9e [] ? might_fault+0x97/0x9e [] security_socket_bind+0x11/0x13 [] sys_bind+0x56/0x95 [] ? sysret_check+0x27/0x62 [] ? trace_hardirqs_on_caller+0x11e/0x155 [] ? audit_syscall_entry+0x17b/0x1ae [] ? trace_hardirqs_on_thunk+0x3a/0x3f [] system_call_fastpath+0x16/0x1b Signed-off-by: David Howells Acked-by: Paul Moore Acked-by: Eric Dumazet Cc: stable@kernel.org Signed-off-by: James Morris --- security/selinux/netport.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'security') diff --git a/security/selinux/netport.c b/security/selinux/netport.c index 0b62bd112461..7b9eb1faf68b 100644 --- a/security/selinux/netport.c +++ b/security/selinux/netport.c @@ -123,7 +123,9 @@ static void sel_netport_insert(struct sel_netport *port) if (sel_netport_hash[idx].size == SEL_NETPORT_HASH_BKT_LIMIT) { struct sel_netport *tail; tail = list_entry( - rcu_dereference(sel_netport_hash[idx].list.prev), + rcu_dereference_protected( + sel_netport_hash[idx].list.prev, + lockdep_is_held(&sel_netport_lock)), struct sel_netport, list); list_del_rcu(&tail->list); kfree_rcu(tail, rcu); -- cgit v1.2.1