* [TOMOYO #16 00/25] Starting TOMOYO 2.3
@ 2009-10-04 12:49 Tetsuo Handa
2009-10-04 12:49 ` [TOMOYO #16 01/25] LSM: Add security_path_chmod() and security_path_chown() Tetsuo Handa
` (25 more replies)
0 siblings, 26 replies; 50+ messages in thread
From: Tetsuo Handa @ 2009-10-04 12:49 UTC (permalink / raw)
To: linux-security-module; +Cc: linux-kernel
Hello.
This is the beginning of TOMOYO 2.3. TOMOYO 2.2 (which is in kernel 2.6.30 and
later) is terribly lacking in functionality (e.g. no audit logs, no network).
I hope TOMOYO 2.3 can provide practically sufficient functionality.
This patchset provides almost all functionality in TOMOYO 1.7.0 except
(1) DAC before MAC checks for directory modification operations.
(2) Incoming UDP/RAW packet filtering.
(3) Signal transmission restriction.
(4) Many of non-posix capabilities support.
Since this patchset is not yet accepted, I haven't written documentation for
TOMOYO 2.3. You can see http://tomoyo.sourceforge.jp/1.7/policy-reference.html
instead.
Conventionally, patches should be submitted in the form of diff file.
But this time, I submit in the form of entire file due to amount of changes.
# diff -u security/tomoyo.2.2/realpath.c security/tomoyo/new-realpath.c | diffstat -f0
new-realpath.c | 609 186 + 423 - 0 !
1 file changed, 186 insertions(+), 423 deletions(-)
# wc -l security/tomoyo/new-realpath.c
251 security/tomoyo/new-realpath.c
# diff -u security/tomoyo.2.2/file.c security/tomoyo/new-file.c | diffstat -f0
new-file.c | 2472 1693 + 779 - 0 !
1 file changed, 1693 insertions(+), 779 deletions(-)
# wc -l security/tomoyo/new-file.c
2249 security/tomoyo/new-file.c
# diff -u security/tomoyo.2.2/domain.c security/tomoyo/new-domain.c | diffstat -f0
new-domain.c | 1322 877 + 445 - 0 !
1 file changed, 877 insertions(+), 445 deletions(-)
# wc -l security/tomoyo/new-domain.c
1354 security/tomoyo/new-domain.c
# diff -u security/tomoyo.2.2/tomoyo.c security/tomoyo/lsm.c | diffstat -f0
lsm.c | 492 350 + 142 - 0 !
1 file changed, 350 insertions(+), 142 deletions(-)
# wc -l security/tomoyo/lsm.c
523 security/tomoyo/lsm.c
# diff -Nur security/tomoyo.2.2/ security/tomoyo/ | diffstat -f0
Kconfig | 67 67 + 0 - 0 !
Makefile | 2 1 + 1 - 0 !
address_group.c | 270 270 + 0 - 0 !
audit.c | 561 561 + 0 - 0 !
capability.c | 141 141 + 0 - 0 !
common.c | 2276 0 + 2276 - 0 !
common.h | 461 0 + 461 - 0 !
condition.c | 1332 1332 + 0 - 0 !
domain.c | 922 0 + 922 - 0 !
environ.c | 232 232 + 0 - 0 !
file.c | 1335 0 + 1335 - 0 !
gc.c | 606 606 + 0 - 0 !
internal.h | 1317 1317 + 0 - 0 !
load_policy.c | 97 97 + 0 - 0 !
lsm.c | 523 523 + 0 - 0 !
memory.c | 391 391 + 0 - 0 !
mount.c | 366 366 + 0 - 0 !
network.c | 757 757 + 0 - 0 !
new-domain.c | 1354 1354 + 0 - 0 !
new-file.c | 2249 2249 + 0 - 0 !
new-realpath.c | 251 251 + 0 - 0 !
number_group.c | 212 212 + 0 - 0 !
path_group.c | 210 210 + 0 - 0 !
policy_io.c | 2734 2734 + 0 - 0 !
realpath.c | 488 0 + 488 - 0 !
realpath.h | 66 0 + 66 - 0 !
securityfs_if.c | 148 148 + 0 - 0 !
tomoyo.c | 315 0 + 315 - 0 !
tomoyo.h | 96 0 + 96 - 0 !
util.c | 1144 1144 + 0 - 0 !
30 files changed, 14963 insertions(+), 5960 deletions(-)
Regards.
--
^ permalink raw reply [flat|nested] 50+ messages in thread
* [TOMOYO #16 01/25] LSM: Add security_path_chmod() and security_path_chown().
2009-10-04 12:49 [TOMOYO #16 00/25] Starting TOMOYO 2.3 Tetsuo Handa
@ 2009-10-04 12:49 ` Tetsuo Handa
2009-10-08 17:10 ` John Johansen
2009-10-29 5:12 ` [TOMOYO #16 01/25] LSM: Add security_path_chmod() and security_path_chown() Serge E. Hallyn
2009-10-04 12:49 ` [TOMOYO #16 02/25] LSM: Add security_path_chroot() Tetsuo Handa
` (24 subsequent siblings)
25 siblings, 2 replies; 50+ messages in thread
From: Tetsuo Handa @ 2009-10-04 12:49 UTC (permalink / raw)
To: linux-security-module; +Cc: linux-kernel, Tetsuo Handa
[-- Attachment #1: lsm-add-chmod-chown-hooks.patch --]
[-- Type: text/plain, Size: 7056 bytes --]
This patch allows pathname based LSM modules to check chmod()/chown()
operations. Since notify_change() does not receive "struct vfsmount *",
we add security_path_chmod() and security_path_chown() to the caller of
notify_change().
These hooks are used by TOMOYO.
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
fs/open.c | 24 ++++++++++++++++++++----
include/linux/security.h | 30 ++++++++++++++++++++++++++++++
security/capability.c | 13 +++++++++++++
security/security.c | 15 +++++++++++++++
4 files changed, 78 insertions(+), 4 deletions(-)
--- security-testing-2.6.orig/fs/open.c
+++ security-testing-2.6/fs/open.c
@@ -616,6 +616,9 @@ SYSCALL_DEFINE2(fchmod, unsigned int, fd
err = mnt_want_write_file(file);
if (err)
goto out_putf;
+ err = security_path_chmod(dentry, file->f_vfsmnt, mode);
+ if (err)
+ goto out_drop_write;
mutex_lock(&inode->i_mutex);
if (mode == (mode_t) -1)
mode = inode->i_mode;
@@ -623,6 +626,7 @@ SYSCALL_DEFINE2(fchmod, unsigned int, fd
newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
err = notify_change(dentry, &newattrs);
mutex_unlock(&inode->i_mutex);
+out_drop_write:
mnt_drop_write(file->f_path.mnt);
out_putf:
fput(file);
@@ -645,6 +649,9 @@ SYSCALL_DEFINE3(fchmodat, int, dfd, cons
error = mnt_want_write(path.mnt);
if (error)
goto dput_and_out;
+ error = security_path_chmod(path.dentry, path.mnt, mode);
+ if (error)
+ goto out_drop_write;
mutex_lock(&inode->i_mutex);
if (mode == (mode_t) -1)
mode = inode->i_mode;
@@ -652,6 +659,7 @@ SYSCALL_DEFINE3(fchmodat, int, dfd, cons
newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
error = notify_change(path.dentry, &newattrs);
mutex_unlock(&inode->i_mutex);
+out_drop_write:
mnt_drop_write(path.mnt);
dput_and_out:
path_put(&path);
@@ -700,7 +708,9 @@ SYSCALL_DEFINE3(chown, const char __user
error = mnt_want_write(path.mnt);
if (error)
goto out_release;
- error = chown_common(path.dentry, user, group);
+ error = security_path_chown(&path, user, group);
+ if (!error)
+ error = chown_common(path.dentry, user, group);
mnt_drop_write(path.mnt);
out_release:
path_put(&path);
@@ -725,7 +735,9 @@ SYSCALL_DEFINE5(fchownat, int, dfd, cons
error = mnt_want_write(path.mnt);
if (error)
goto out_release;
- error = chown_common(path.dentry, user, group);
+ error = security_path_chown(&path, user, group);
+ if (!error)
+ error = chown_common(path.dentry, user, group);
mnt_drop_write(path.mnt);
out_release:
path_put(&path);
@@ -744,7 +756,9 @@ SYSCALL_DEFINE3(lchown, const char __use
error = mnt_want_write(path.mnt);
if (error)
goto out_release;
- error = chown_common(path.dentry, user, group);
+ error = security_path_chown(&path, user, group);
+ if (!error)
+ error = chown_common(path.dentry, user, group);
mnt_drop_write(path.mnt);
out_release:
path_put(&path);
@@ -767,7 +781,9 @@ SYSCALL_DEFINE3(fchown, unsigned int, fd
goto out_fput;
dentry = file->f_path.dentry;
audit_inode(NULL, dentry);
- error = chown_common(dentry, user, group);
+ error = security_path_chown(&file->f_path, user, group);
+ if (!error)
+ error = chown_common(dentry, user, group);
mnt_drop_write(file->f_path.mnt);
out_fput:
fput(file);
--- security-testing-2.6.orig/include/linux/security.h
+++ security-testing-2.6/include/linux/security.h
@@ -447,6 +447,18 @@ static inline void security_free_mnt_opt
* @new_dir contains the path structure for parent of the new link.
* @new_dentry contains the dentry structure of the new link.
* Return 0 if permission is granted.
+ * @path_chmod:
+ * Check for permission to change DAC's permission of a file or directory.
+ * @dentry contains the dentry structure.
+ * @mnt contains the vfsmnt structure.
+ * @mode contains DAC's mode.
+ * Return 0 if permission is granted.
+ * @path_chown:
+ * Check for permission to change owner/group of a file or directory.
+ * @path contains the path structure.
+ * @uid contains new owner's ID.
+ * @gid contains new group's ID.
+ * Return 0 if permission is granted.
* @inode_readlink:
* Check the permission to read the symbolic link.
* @dentry contains the dentry structure for the file link.
@@ -1488,6 +1500,9 @@ struct security_operations {
struct dentry *new_dentry);
int (*path_rename) (struct path *old_dir, struct dentry *old_dentry,
struct path *new_dir, struct dentry *new_dentry);
+ int (*path_chmod) (struct dentry *dentry, struct vfsmount *mnt,
+ mode_t mode);
+ int (*path_chown) (struct path *path, uid_t uid, gid_t gid);
#endif
int (*inode_alloc_security) (struct inode *inode);
@@ -2952,6 +2967,9 @@ int security_path_link(struct dentry *ol
struct dentry *new_dentry);
int security_path_rename(struct path *old_dir, struct dentry *old_dentry,
struct path *new_dir, struct dentry *new_dentry);
+int security_path_chmod(struct dentry *dentry, struct vfsmount *mnt,
+ mode_t mode);
+int security_path_chown(struct path *path, uid_t uid, gid_t gid);
#else /* CONFIG_SECURITY_PATH */
static inline int security_path_unlink(struct path *dir, struct dentry *dentry)
{
@@ -3001,6 +3019,18 @@ static inline int security_path_rename(s
{
return 0;
}
+
+static inline int security_path_chmod(struct dentry *dentry,
+ struct vfsmount *mnt,
+ mode_t mode)
+{
+ return 0;
+}
+
+static inline int security_path_chown(struct path *path, uid_t uid, gid_t gid)
+{
+ return 0;
+}
#endif /* CONFIG_SECURITY_PATH */
#ifdef CONFIG_KEYS
--- security-testing-2.6.orig/security/capability.c
+++ security-testing-2.6/security/capability.c
@@ -308,6 +308,17 @@ static int cap_path_truncate(struct path
{
return 0;
}
+
+static int cap_path_chmod(struct dentry *dentry, struct vfsmount *mnt,
+ mode_t mode)
+{
+ return 0;
+}
+
+static int cap_path_chown(struct path *path, uid_t uid, gid_t gid)
+{
+ return 0;
+}
#endif
static int cap_file_permission(struct file *file, int mask)
@@ -977,6 +988,8 @@ void security_fixup_ops(struct security_
set_to_cap_if_null(ops, path_link);
set_to_cap_if_null(ops, path_rename);
set_to_cap_if_null(ops, path_truncate);
+ set_to_cap_if_null(ops, path_chmod);
+ set_to_cap_if_null(ops, path_chown);
#endif
set_to_cap_if_null(ops, file_permission);
set_to_cap_if_null(ops, file_alloc_security);
--- security-testing-2.6.orig/security/security.c
+++ security-testing-2.6/security/security.c
@@ -434,6 +434,21 @@ int security_path_truncate(struct path *
return 0;
return security_ops->path_truncate(path, length, time_attrs);
}
+
+int security_path_chmod(struct dentry *dentry, struct vfsmount *mnt,
+ mode_t mode)
+{
+ if (unlikely(IS_PRIVATE(dentry->d_inode)))
+ return 0;
+ return security_ops->path_chmod(dentry, mnt, mode);
+}
+
+int security_path_chown(struct path *path, uid_t uid, gid_t gid)
+{
+ if (unlikely(IS_PRIVATE(path->dentry->d_inode)))
+ return 0;
+ return security_ops->path_chown(path, uid, gid);
+}
#endif
int security_inode_create(struct inode *dir, struct dentry *dentry, int mode)
--
^ permalink raw reply [flat|nested] 50+ messages in thread
* [TOMOYO #16 02/25] LSM: Add security_path_chroot().
2009-10-04 12:49 [TOMOYO #16 00/25] Starting TOMOYO 2.3 Tetsuo Handa
2009-10-04 12:49 ` [TOMOYO #16 01/25] LSM: Add security_path_chmod() and security_path_chown() Tetsuo Handa
@ 2009-10-04 12:49 ` Tetsuo Handa
2009-10-08 17:12 ` John Johansen
2009-10-29 5:32 ` Serge E. Hallyn
2009-10-04 12:49 ` [TOMOYO #16 03/25] LSM: Pass original mount flags to security_sb_mount() Tetsuo Handa
` (23 subsequent siblings)
25 siblings, 2 replies; 50+ messages in thread
From: Tetsuo Handa @ 2009-10-04 12:49 UTC (permalink / raw)
To: linux-security-module; +Cc: linux-kernel, Tetsuo Handa
[-- Attachment #1: lsm-add-chroot-hook.patch --]
[-- Type: text/plain, Size: 3216 bytes --]
This patch allows pathname based LSM modules to check chroot() operations.
This hook is used by TOMOYO.
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
fs/open.c | 3 +++
include/linux/security.h | 11 +++++++++++
security/capability.c | 6 ++++++
security/security.c | 5 +++++
4 files changed, 25 insertions(+)
--- security-testing-2.6.orig/fs/open.c
+++ security-testing-2.6/fs/open.c
@@ -587,6 +587,9 @@ SYSCALL_DEFINE1(chroot, const char __use
error = -EPERM;
if (!capable(CAP_SYS_CHROOT))
goto dput_and_out;
+ error = security_path_chroot(&path);
+ if (error)
+ goto dput_and_out;
set_fs_root(current->fs, &path);
error = 0;
--- security-testing-2.6.orig/include/linux/security.h
+++ security-testing-2.6/include/linux/security.h
@@ -459,6 +459,10 @@ static inline void security_free_mnt_opt
* @uid contains new owner's ID.
* @gid contains new group's ID.
* Return 0 if permission is granted.
+ * @path_chroot:
+ * Check for permission to change root directory.
+ * @path contains the path structure.
+ * Return 0 if permission is granted.
* @inode_readlink:
* Check the permission to read the symbolic link.
* @dentry contains the dentry structure for the file link.
@@ -1503,6 +1507,7 @@ struct security_operations {
int (*path_chmod) (struct dentry *dentry, struct vfsmount *mnt,
mode_t mode);
int (*path_chown) (struct path *path, uid_t uid, gid_t gid);
+ int (*path_chroot) (struct path *path);
#endif
int (*inode_alloc_security) (struct inode *inode);
@@ -2970,6 +2975,7 @@ int security_path_rename(struct path *ol
int security_path_chmod(struct dentry *dentry, struct vfsmount *mnt,
mode_t mode);
int security_path_chown(struct path *path, uid_t uid, gid_t gid);
+int security_path_chroot(struct path *path);
#else /* CONFIG_SECURITY_PATH */
static inline int security_path_unlink(struct path *dir, struct dentry *dentry)
{
@@ -3031,6 +3037,11 @@ static inline int security_path_chown(st
{
return 0;
}
+
+static inline int security_path_chroot(struct path *path)
+{
+ return 0;
+}
#endif /* CONFIG_SECURITY_PATH */
#ifdef CONFIG_KEYS
--- security-testing-2.6.orig/security/capability.c
+++ security-testing-2.6/security/capability.c
@@ -319,6 +319,11 @@ static int cap_path_chown(struct path *p
{
return 0;
}
+
+static int cap_path_chroot(struct path *root)
+{
+ return 0;
+}
#endif
static int cap_file_permission(struct file *file, int mask)
@@ -990,6 +995,7 @@ void security_fixup_ops(struct security_
set_to_cap_if_null(ops, path_truncate);
set_to_cap_if_null(ops, path_chmod);
set_to_cap_if_null(ops, path_chown);
+ set_to_cap_if_null(ops, path_chroot);
#endif
set_to_cap_if_null(ops, file_permission);
set_to_cap_if_null(ops, file_alloc_security);
--- security-testing-2.6.orig/security/security.c
+++ security-testing-2.6/security/security.c
@@ -449,6 +449,11 @@ int security_path_chown(struct path *pat
return 0;
return security_ops->path_chown(path, uid, gid);
}
+
+int security_path_chroot(struct path *path)
+{
+ return security_ops->path_chroot(path);
+}
#endif
int security_inode_create(struct inode *dir, struct dentry *dentry, int mode)
--
^ permalink raw reply [flat|nested] 50+ messages in thread
* [TOMOYO #16 03/25] LSM: Pass original mount flags to security_sb_mount().
2009-10-04 12:49 [TOMOYO #16 00/25] Starting TOMOYO 2.3 Tetsuo Handa
2009-10-04 12:49 ` [TOMOYO #16 01/25] LSM: Add security_path_chmod() and security_path_chown() Tetsuo Handa
2009-10-04 12:49 ` [TOMOYO #16 02/25] LSM: Add security_path_chroot() Tetsuo Handa
@ 2009-10-04 12:49 ` Tetsuo Handa
2009-10-08 17:22 ` John Johansen
2009-10-04 12:49 ` [TOMOYO #16 04/25] TOMOYO: Add header file Tetsuo Handa
` (22 subsequent siblings)
25 siblings, 1 reply; 50+ messages in thread
From: Tetsuo Handa @ 2009-10-04 12:49 UTC (permalink / raw)
To: linux-security-module; +Cc: linux-kernel, Tetsuo Handa
[-- Attachment #1: lsm-pass-original-mount-flags.patch --]
[-- Type: text/plain, Size: 1520 bytes --]
This patch allows LSM modules to determine based on original mount flags
passed to mount(). A LSM module can get masked mount flags (if needed) by
flags &= ~(MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_ACTIVE |
MS_NOATIME | MS_NODIRATIME | MS_RELATIME| MS_KERNMOUNT |
MS_STRICTATIME);
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
fs/namespace.c | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
--- security-testing-2.6.orig/fs/namespace.c
+++ security-testing-2.6/fs/namespace.c
@@ -1921,6 +1921,16 @@ long do_mount(char *dev_name, char *dir_
if (data_page)
((char *)data_page)[PAGE_SIZE - 1] = 0;
+ /* ... and get the mountpoint */
+ retval = kern_path(dir_name, LOOKUP_FOLLOW, &path);
+ if (retval)
+ return retval;
+
+ retval = security_sb_mount(dev_name, &path,
+ type_page, flags, data_page);
+ if (retval)
+ goto dput_out;
+
/* Default to relatime unless overriden */
if (!(flags & MS_NOATIME))
mnt_flags |= MNT_RELATIME;
@@ -1945,16 +1955,6 @@ long do_mount(char *dev_name, char *dir_
MS_NOATIME | MS_NODIRATIME | MS_RELATIME| MS_KERNMOUNT |
MS_STRICTATIME);
- /* ... and get the mountpoint */
- retval = kern_path(dir_name, LOOKUP_FOLLOW, &path);
- if (retval)
- return retval;
-
- retval = security_sb_mount(dev_name, &path,
- type_page, flags, data_page);
- if (retval)
- goto dput_out;
-
if (flags & MS_REMOUNT)
retval = do_remount(&path, flags & ~MS_REMOUNT, mnt_flags,
data_page);
--
^ permalink raw reply [flat|nested] 50+ messages in thread
* [TOMOYO #16 04/25] TOMOYO: Add header file.
2009-10-04 12:49 [TOMOYO #16 00/25] Starting TOMOYO 2.3 Tetsuo Handa
` (2 preceding siblings ...)
2009-10-04 12:49 ` [TOMOYO #16 03/25] LSM: Pass original mount flags to security_sb_mount() Tetsuo Handa
@ 2009-10-04 12:49 ` Tetsuo Handa
2009-10-04 12:49 ` [TOMOYO #16 05/25] TOMOYO: Add per task_struct variables Tetsuo Handa
` (21 subsequent siblings)
25 siblings, 0 replies; 50+ messages in thread
From: Tetsuo Handa @ 2009-10-04 12:49 UTC (permalink / raw)
To: linux-security-module; +Cc: linux-kernel, Tetsuo Handa
[-- Attachment #1: tomoyo-add-internal-header.patch --]
[-- Type: text/plain, Size: 50127 bytes --]
This patch contains structure/prototype/variable declarations for TOMOYO.
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
security/tomoyo/internal.h | 1317 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 1317 insertions(+)
--- /dev/null
+++ security-testing-2.6/security/tomoyo/internal.h
@@ -0,0 +1,1317 @@
+/*
+ * security/tomoyo/internal.h
+ *
+ * Copyright (C) 2005-2009 NTT DATA CORPORATION
+ */
+
+#ifndef _SECURITY_TOMOYO_INTERNAL_H
+#define _SECURITY_TOMOYO_INTERNAL_H
+
+#include <linux/security.h>
+#include <linux/poll.h>
+extern asmlinkage long sys_getpid(void);
+extern asmlinkage long sys_getppid(void);
+extern spinlock_t vfsmount_lock;
+
+/* Index numbers for Access Controls. */
+enum tomoyo_acl_entry_type_index {
+ TOMOYO_TYPE_PATH_ACL,
+ TOMOYO_TYPE_PATH2_ACL,
+ TOMOYO_TYPE_PATH_NUMBER_ACL,
+ TOMOYO_TYPE_PATH_NUMBER3_ACL,
+ TOMOYO_TYPE_ENV_ACL,
+ TOMOYO_TYPE_CAPABILITY_ACL,
+ TOMOYO_TYPE_IP_NETWORK_ACL,
+ TOMOYO_TYPE_MOUNT_ACL,
+ TOMOYO_TYPE_EXECUTE_HANDLER,
+ TOMOYO_TYPE_DENIED_EXECUTE_HANDLER
+};
+
+/* Index numbers for Capability Controls. */
+enum tomoyo_capability_acl_index {
+ /* socket(PF_INET or PF_INET6, SOCK_STREAM, *) */
+ TOMOYO_INET_STREAM_SOCKET_CREATE,
+ /* listen() for PF_INET or PF_INET6, SOCK_STREAM */
+ TOMOYO_INET_STREAM_SOCKET_LISTEN,
+ /* connect() for PF_INET or PF_INET6, SOCK_STREAM */
+ TOMOYO_INET_STREAM_SOCKET_CONNECT,
+ /* socket(PF_INET or PF_INET6, SOCK_DGRAM, *) */
+ TOMOYO_USE_INET_DGRAM_SOCKET,
+ /* socket(PF_INET or PF_INET6, SOCK_RAW, *) */
+ TOMOYO_USE_INET_RAW_SOCKET,
+ /* socket(PF_ROUTE, *, *) */
+ TOMOYO_USE_ROUTE_SOCKET,
+ /* socket(PF_PACKET, *, *) */
+ TOMOYO_USE_PACKET_SOCKET,
+ /* sys_mount() */
+ TOMOYO_SYS_MOUNT,
+ /* sys_umount() */
+ TOMOYO_SYS_UMOUNT,
+ /* sys_chroot() */
+ TOMOYO_SYS_CHROOT,
+ /* sys_mknod(S_IFIFO) */
+ TOMOYO_CREATE_FIFO,
+ /* sys_mknod(S_IFBLK) */
+ TOMOYO_CREATE_BLOCK_DEV,
+ /* sys_mknod(S_IFCHR) */
+ TOMOYO_CREATE_CHAR_DEV,
+ /* sys_mknod(S_IFSOCK) */
+ TOMOYO_CREATE_UNIX_SOCKET,
+ /* sys_link() */
+ TOMOYO_SYS_LINK,
+ /* sys_symlink() */
+ TOMOYO_SYS_SYMLINK,
+ /* sys_rename() */
+ TOMOYO_SYS_RENAME,
+ /* sys_unlink() */
+ TOMOYO_SYS_UNLINK,
+ /* sys_chmod(), sys_fchmod() */
+ TOMOYO_SYS_CHMOD,
+ /* sys_chown(), sys_fchown(), sys_lchown() */
+ TOMOYO_SYS_CHOWN,
+ /* sys_ioctl(), compat_sys_ioctl() */
+ TOMOYO_SYS_IOCTL,
+ /* sys_pivot_root() */
+ TOMOYO_SYS_PIVOT_ROOT,
+ TOMOYO_MAX_CAPABILITY_INDEX
+};
+
+/*
+ * TOMOYO_TYPE_READ_WRITE is special. TOMOYO_TYPE_READ_WRITE is automatically
+ * set if both TOMOYO_TYPE_READ and TOMOYO_TYPE_WRITE are set. Both
+ * TOMOYO_TYPE_READ and TOMOYO_TYPE_WRITE are automatically set if
+ * TOMOYO_TYPE_READ_WRITE is set.
+ * TOMOYO_TYPE_READ_WRITE is automatically cleared if either TOMOYO_TYPE_READ
+ * or TOMOYO_TYPE_WRITE is cleared. Both TOMOYO_TYPE_READ and TOMOYO_TYPE_WRITE
+ * are automatically cleared if TOMOYO_TYPE_READ_WRITE is cleared.
+ */
+
+enum tomoyo_path_acl_index {
+ TOMOYO_TYPE_READ_WRITE,
+ TOMOYO_TYPE_EXECUTE,
+ TOMOYO_TYPE_READ,
+ TOMOYO_TYPE_WRITE,
+ TOMOYO_TYPE_UNLINK,
+ TOMOYO_TYPE_RMDIR,
+ TOMOYO_TYPE_TRUNCATE,
+ TOMOYO_TYPE_SYMLINK,
+ TOMOYO_TYPE_REWRITE,
+ TOMOYO_TYPE_CHROOT,
+ TOMOYO_TYPE_UMOUNT,
+ TOMOYO_MAX_PATH_OPERATION
+};
+
+enum tomoyo_path_number3_acl_index {
+ TOMOYO_TYPE_MKBLOCK,
+ TOMOYO_TYPE_MKCHAR,
+ TOMOYO_MAX_PATH_NUMBER3_OPERATION
+};
+
+enum tomoyo_path2_acl_index {
+ TOMOYO_TYPE_LINK,
+ TOMOYO_TYPE_RENAME,
+ TOMOYO_TYPE_PIVOT_ROOT,
+ TOMOYO_MAX_PATH2_OPERATION
+};
+
+enum tomoyo_path_number_acl_index {
+ TOMOYO_TYPE_CREATE,
+ TOMOYO_TYPE_MKDIR,
+ TOMOYO_TYPE_MKFIFO,
+ TOMOYO_TYPE_MKSOCK,
+ TOMOYO_TYPE_IOCTL,
+ TOMOYO_TYPE_CHMOD,
+ TOMOYO_TYPE_CHOWN,
+ TOMOYO_TYPE_CHGRP,
+ TOMOYO_MAX_PATH_NUMBER_OPERATION
+};
+
+enum tomoyo_network_acl_index {
+ /* UDP's bind() operation. */
+ TOMOYO_NETWORK_UDP_BIND,
+ /* UDP's connect()/send()/recv() operation. */
+ TOMOYO_NETWORK_UDP_CONNECT,
+ /* TCP's bind() operation. */
+ TOMOYO_NETWORK_TCP_BIND,
+ /* TCP's listen() operation. */
+ TOMOYO_NETWORK_TCP_LISTEN,
+ /* TCP's connect() operation. */
+ TOMOYO_NETWORK_TCP_CONNECT,
+ /* TCP's accept() operation. */
+ TOMOYO_NETWORK_TCP_ACCEPT,
+ /* IP's bind() operation. */
+ TOMOYO_NETWORK_RAW_BIND,
+ /* IP's connect()/send()/recv() operation. */
+ TOMOYO_NETWORK_RAW_CONNECT,
+ TOMOYO_MAX_NETWORK_OPERATION
+};
+
+enum tomoyo_ip_address_type {
+ TOMOYO_IP_ADDRESS_TYPE_ADDRESS_GROUP,
+ TOMOYO_IP_ADDRESS_TYPE_IPv4,
+ TOMOYO_IP_ADDRESS_TYPE_IPv6
+};
+
+/* Indexes for /sys/kernel/security/tomoyo/ interfaces. */
+enum tomoyo_proc_interface_index {
+ TOMOYO_DOMAINPOLICY,
+ TOMOYO_EXCEPTIONPOLICY,
+ TOMOYO_DOMAIN_STATUS,
+ TOMOYO_PROCESS_STATUS,
+ TOMOYO_MEMINFO,
+ TOMOYO_GRANTLOG,
+ TOMOYO_REJECTLOG,
+ TOMOYO_SELFDOMAIN,
+ TOMOYO_VERSION,
+ TOMOYO_PROFILE,
+ TOMOYO_QUERY,
+ TOMOYO_MANAGER,
+ TOMOYO_EXECUTE_HANDLER
+};
+
+enum tomoyo_mac_index {
+ TOMOYO_MAC_FILE_EXECUTE,
+ TOMOYO_MAC_FILE_OPEN,
+ TOMOYO_MAC_FILE_CREATE,
+ TOMOYO_MAC_FILE_UNLINK,
+ TOMOYO_MAC_FILE_MKDIR,
+ TOMOYO_MAC_FILE_RMDIR,
+ TOMOYO_MAC_FILE_MKFIFO,
+ TOMOYO_MAC_FILE_MKSOCK,
+ TOMOYO_MAC_FILE_TRUNCATE,
+ TOMOYO_MAC_FILE_SYMLINK,
+ TOMOYO_MAC_FILE_REWRITE,
+ TOMOYO_MAC_FILE_MKBLOCK,
+ TOMOYO_MAC_FILE_MKCHAR,
+ TOMOYO_MAC_FILE_LINK,
+ TOMOYO_MAC_FILE_RENAME,
+ TOMOYO_MAC_FILE_CHMOD,
+ TOMOYO_MAC_FILE_CHOWN,
+ TOMOYO_MAC_FILE_CHGRP,
+ TOMOYO_MAC_FILE_IOCTL,
+ TOMOYO_MAC_FILE_CHROOT,
+ TOMOYO_MAC_FILE_MOUNT,
+ TOMOYO_MAC_FILE_UMOUNT,
+ TOMOYO_MAC_FILE_PIVOT_ROOT,
+ TOMOYO_MAC_NETWORK_UDP_BIND,
+ TOMOYO_MAC_NETWORK_UDP_CONNECT,
+ TOMOYO_MAC_NETWORK_TCP_BIND,
+ TOMOYO_MAC_NETWORK_TCP_LISTEN,
+ TOMOYO_MAC_NETWORK_TCP_CONNECT,
+ TOMOYO_MAC_NETWORK_TCP_ACCEPT,
+ TOMOYO_MAC_NETWORK_RAW_BIND,
+ TOMOYO_MAC_NETWORK_RAW_CONNECT,
+ TOMOYO_MAC_ENVIRON,
+ TOMOYO_MAX_MAC_INDEX
+};
+
+enum tomoyo_mac_category_index {
+ TOMOYO_MAC_CATEGORY_FILE,
+ TOMOYO_MAC_CATEGORY_NETWORK,
+ TOMOYO_MAC_CATEGORY_MISC,
+ TOMOYO_MAC_CATEGORY_CAPABILITY,
+ TOMOYO_MAX_MAC_CATEGORY_INDEX
+};
+
+enum tomoyo_conditions_index {
+ TOMOYO_TASK_UID, /* current_uid() */
+ TOMOYO_TASK_EUID, /* current_euid() */
+ TOMOYO_TASK_SUID, /* current_suid() */
+ TOMOYO_TASK_FSUID, /* current_fsuid() */
+ TOMOYO_TASK_GID, /* current_gid() */
+ TOMOYO_TASK_EGID, /* current_egid() */
+ TOMOYO_TASK_SGID, /* current_sgid() */
+ TOMOYO_TASK_FSGID, /* current_fsgid() */
+ TOMOYO_TASK_PID, /* sys_getpid() */
+ TOMOYO_TASK_PPID, /* sys_getppid() */
+ TOMOYO_EXEC_ARGC, /* "struct linux_binprm *"->argc */
+ TOMOYO_EXEC_ENVC, /* "struct linux_binprm *"->envc */
+ TOMOYO_TASK_STATE_0, /* (u8) (current->tomoyo_flags >> 24) */
+ TOMOYO_TASK_STATE_1, /* (u8) (current->tomoyo_flags >> 16) */
+ TOMOYO_TASK_STATE_2, /* (u8) (task->tomoyo_flags >> 8) */
+ TOMOYO_TYPE_IS_SOCKET, /* S_IFSOCK */
+ TOMOYO_TYPE_IS_SYMLINK, /* S_IFLNK */
+ TOMOYO_TYPE_IS_FILE, /* S_IFREG */
+ TOMOYO_TYPE_IS_BLOCK_DEV, /* S_IFBLK */
+ TOMOYO_TYPE_IS_DIRECTORY, /* S_IFDIR */
+ TOMOYO_TYPE_IS_CHAR_DEV, /* S_IFCHR */
+ TOMOYO_TYPE_IS_FIFO, /* S_IFIFO */
+ TOMOYO_MODE_SETUID, /* S_ISUID */
+ TOMOYO_MODE_SETGID, /* S_ISGID */
+ TOMOYO_MODE_STICKY, /* S_ISVTX */
+ TOMOYO_MODE_OWNER_READ, /* S_IRUSR */
+ TOMOYO_MODE_OWNER_WRITE, /* S_IWUSR */
+ TOMOYO_MODE_OWNER_EXECUTE, /* S_IXUSR */
+ TOMOYO_MODE_GROUP_READ, /* S_IRGRP */
+ TOMOYO_MODE_GROUP_WRITE, /* S_IWGRP */
+ TOMOYO_MODE_GROUP_EXECUTE, /* S_IXGRP */
+ TOMOYO_MODE_OTHERS_READ, /* S_IROTH */
+ TOMOYO_MODE_OTHERS_WRITE, /* S_IWOTH */
+ TOMOYO_MODE_OTHERS_EXECUTE, /* S_IXOTH */
+ TOMOYO_TASK_TYPE, /* ((u8) task->tomoyo_flags) &
+ TOMOYO_TASK_IS_EXECUTE_HANDLER */
+ TOMOYO_TASK_EXECUTE_HANDLER, /* TOMOYO_TASK_IS_EXECUTE_HANDLER */
+ TOMOYO_EXEC_REALPATH,
+ TOMOYO_SYMLINK_TARGET,
+ TOMOYO_PATH1_UID,
+ TOMOYO_PATH1_GID,
+ TOMOYO_PATH1_INO,
+ TOMOYO_PATH1_MAJOR,
+ TOMOYO_PATH1_MINOR,
+ TOMOYO_PATH1_PERM,
+ TOMOYO_PATH1_TYPE,
+ TOMOYO_PATH1_DEV_MAJOR,
+ TOMOYO_PATH1_DEV_MINOR,
+ TOMOYO_PATH2_UID,
+ TOMOYO_PATH2_GID,
+ TOMOYO_PATH2_INO,
+ TOMOYO_PATH2_MAJOR,
+ TOMOYO_PATH2_MINOR,
+ TOMOYO_PATH2_PERM,
+ TOMOYO_PATH2_TYPE,
+ TOMOYO_PATH2_DEV_MAJOR,
+ TOMOYO_PATH2_DEV_MINOR,
+ TOMOYO_PATH1_PARENT_UID,
+ TOMOYO_PATH1_PARENT_GID,
+ TOMOYO_PATH1_PARENT_INO,
+ TOMOYO_PATH1_PARENT_PERM,
+ TOMOYO_PATH2_PARENT_UID,
+ TOMOYO_PATH2_PARENT_GID,
+ TOMOYO_PATH2_PARENT_INO,
+ TOMOYO_PATH2_PARENT_PERM,
+ TOMOYO_MAX_CONDITION_KEYWORD,
+ TOMOYO_NUMBER_UNION,
+ TOMOYO_NAME_UNION,
+ TOMOYO_ARGV_ENTRY,
+ TOMOYO_ENVP_ENTRY
+};
+
+/* Keywords for ACLs. */
+#define TOMOYO_KEYWORD_ADDRESS_GROUP "address_group "
+#define TOMOYO_KEYWORD_AGGREGATOR "aggregator "
+#define TOMOYO_KEYWORD_ALLOW_CAPABILITY "allow_capability "
+#define TOMOYO_KEYWORD_ALLOW_ENV "allow_env "
+#define TOMOYO_KEYWORD_ALLOW_IOCTL "allow_ioctl "
+#define TOMOYO_KEYWORD_ALLOW_CHMOD "allow_chmod "
+#define TOMOYO_KEYWORD_ALLOW_CHOWN "allow_chown "
+#define TOMOYO_KEYWORD_ALLOW_CHGRP "allow_chgrp "
+#define TOMOYO_KEYWORD_ALLOW_MOUNT "allow_mount "
+#define TOMOYO_KEYWORD_ALLOW_NETWORK "allow_network "
+#define TOMOYO_KEYWORD_ALLOW_READ "allow_read "
+#define TOMOYO_KEYWORD_DELETE "delete "
+#define TOMOYO_KEYWORD_DENY_REWRITE "deny_rewrite "
+#define TOMOYO_KEYWORD_FILE_PATTERN "file_pattern "
+#define TOMOYO_KEYWORD_INITIALIZE_DOMAIN "initialize_domain "
+#define TOMOYO_KEYWORD_KEEP_DOMAIN "keep_domain "
+#define TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN "no_initialize_domain "
+#define TOMOYO_KEYWORD_NO_KEEP_DOMAIN "no_keep_domain "
+#define TOMOYO_KEYWORD_PATH_GROUP "path_group "
+#define TOMOYO_KEYWORD_NUMBER_GROUP "number_group "
+#define TOMOYO_KEYWORD_SELECT "select "
+#define TOMOYO_KEYWORD_USE_PROFILE "use_profile "
+#define TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ "ignore_global_allow_read"
+#define TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_ENV "ignore_global_allow_env"
+#define TOMOYO_KEYWORD_EXECUTE_HANDLER "execute_handler"
+#define TOMOYO_KEYWORD_DENIED_EXECUTE_HANDLER "denied_execute_handler"
+
+/* A domain definition starts with <kernel>. */
+#define ROOT_NAME "<kernel>"
+#define ROOT_NAME_LEN (sizeof(ROOT_NAME) - 1)
+
+/* Value type definition. */
+#define TOMOYO_VALUE_TYPE_INVALID 0
+#define TOMOYO_VALUE_TYPE_DECIMAL 1
+#define TOMOYO_VALUE_TYPE_OCTAL 2
+#define TOMOYO_VALUE_TYPE_HEXADECIMAL 3
+
+#define TOMOYO_EXEC_TMPSIZE 4096
+
+/* Profile number is an integer between 0 and 255. */
+#define TOMOYO_MAX_PROFILES 256
+
+#define TOMOYO_CONFIG_DISABLED 0
+#define TOMOYO_CONFIG_LEARNING 1
+#define TOMOYO_CONFIG_PERMISSIVE 2
+#define TOMOYO_CONFIG_ENFORCING 3
+#define TOMOYO_CONFIG_WANT_REJECT_LOG 64
+#define TOMOYO_CONFIG_WANT_GRANT_LOG 128
+#define TOMOYO_CONFIG_USE_DEFAULT 255
+
+#define TOMOYO_TASK_IS_IN_EXECVE 16
+#define TOMOYO_DONT_SLEEP_ON_ENFORCE_ERROR 32
+#define TOMOYO_TASK_IS_EXECUTE_HANDLER 64
+#define TOMOYO_TASK_IS_POLICY_MANAGER 128
+/* Highest 24 bits are reserved for task.state[] conditions. */
+
+/**
+ * list_for_each_cookie - iterate over a list with cookie.
+ * @pos: the &struct list_head to use as a loop cursor.
+ * @cookie: the &struct list_head to use as a cookie.
+ * @head: the head for your list.
+ *
+ * Same with list_for_each_rcu() except that this primitive uses @cookie
+ * so that we can continue iteration.
+ * @cookie must be NULL when iteration starts, and @cookie will become
+ * NULL when iteration finishes.
+ */
+#define list_for_each_cookie(pos, cookie, head) \
+ for (({ if (!cookie) \
+ cookie = head; }), \
+ pos = rcu_dereference((cookie)->next); \
+ prefetch(pos->next), pos != (head) || ((cookie) = NULL); \
+ (cookie) = pos, pos = rcu_dereference(pos->next))
+
+struct tomoyo_name_union {
+ const struct tomoyo_path_info *filename;
+ struct tomoyo_path_group *group;
+ u8 is_group;
+};
+
+struct tomoyo_number_union {
+ unsigned long values[2];
+ struct tomoyo_number_group *group;
+ u8 min_type;
+ u8 max_type;
+ u8 is_group;
+};
+
+/* Structure for "path_group" directive. */
+struct tomoyo_path_group {
+ struct list_head list;
+ const struct tomoyo_path_info *group_name;
+ struct list_head member_list;
+ atomic_t users;
+};
+
+/* Structure for "number_group" directive. */
+struct tomoyo_number_group {
+ struct list_head list;
+ const struct tomoyo_path_info *group_name;
+ struct list_head member_list;
+ atomic_t users;
+};
+
+/* Structure for "address_group" directive. */
+struct tomoyo_address_group {
+ struct list_head list;
+ const struct tomoyo_path_info *group_name;
+ struct list_head member_list;
+ atomic_t users;
+};
+
+/* Structure for "path_group" directive. */
+struct tomoyo_path_group_member {
+ struct list_head list;
+ bool is_deleted;
+ const struct tomoyo_path_info *member_name;
+};
+
+/* Structure for "number_group" directive. */
+struct tomoyo_number_group_member {
+ struct list_head list;
+ bool is_deleted;
+ struct tomoyo_number_union number;
+};
+
+/* Structure for "address_group" directive. */
+struct tomoyo_address_group_member {
+ struct list_head list;
+ bool is_deleted;
+ bool is_ipv6;
+ union {
+ u32 ipv4; /* Host byte order */
+ const struct in6_addr *ipv6; /* Network byte order */
+ } min, max;
+};
+
+
+/* Subset of "struct stat". */
+struct tomoyo_mini_stat {
+ uid_t uid;
+ gid_t gid;
+ ino_t ino;
+ mode_t mode;
+ dev_t dev;
+ dev_t rdev;
+};
+
+/* Structure for dumping argv[] and envp[] of "struct linux_binprm". */
+struct tomoyo_page_dump {
+ struct page *page; /* Previously dumped page. */
+ char *data; /* Contents of "page". Size is PAGE_SIZE. */
+};
+
+/* Structure for attribute checks in addition to pathname checks. */
+struct tomoyo_obj_info {
+ bool validate_done;
+ bool path1_valid;
+ bool path1_parent_valid;
+ bool path2_valid;
+ bool path2_parent_valid;
+ struct path path1;
+ struct path path2;
+ struct tomoyo_mini_stat path1_stat;
+ /* I don't handle path2_stat for rename operation. */
+ struct tomoyo_mini_stat path2_stat;
+ struct tomoyo_mini_stat path1_parent_stat;
+ struct tomoyo_mini_stat path2_parent_stat;
+ struct tomoyo_path_info *symlink_target;
+ unsigned int dev;
+};
+
+struct tomoyo_condition_element {
+ /*
+ * Left hand operand. A "struct tomoyo_argv_entry" for
+ * TOMOYO_ARGV_ENTRY, a "struct tomoyo_envp_entry" for
+ * TOMOYO_ENVP_ENTRY is attached to the tail of the array of this
+ * struct.
+ */
+ u8 left;
+ /*
+ * Right hand operand. A "struct tomoyo_number_union" for
+ * TOMOYO_NUMBER_UNION, a "struct tomoyo_name_union" for
+ * TOMOYO_NAME_UNION is attached to the tail of the array of this
+ * struct.
+ */
+ u8 right;
+ /* Equation operator. true if equals or overlaps, false otherwise. */
+ bool equals;
+};
+
+/* Structure for " if " and "; set" part. */
+struct tomoyo_condition {
+ struct list_head list;
+ atomic_t users;
+ u32 size;
+ u16 condc;
+ u16 numbers_count;
+ u16 names_count;
+ u16 argc;
+ u16 envc;
+ u8 post_state[4];
+ /*
+ * struct tomoyo_condition_element condition[condc];
+ * struct tomoyo_number_union values[numbers_count];
+ * struct tomoyo_name_union names[names_count];
+ * struct tomoyo_argv_entry argv[argc];
+ * struct tomoyo_envp_entry envp[envc];
+ */
+};
+
+struct tomoyo_execve_entry;
+
+/* Structure for request info. */
+struct tomoyo_request_info {
+ struct tomoyo_domain_info *domain;
+ struct tomoyo_obj_info *obj;
+ struct tomoyo_execve_entry *ee;
+ struct tomoyo_condition *cond;
+ u8 retry;
+ u8 profile;
+ u8 mode;
+ u8 type;
+};
+
+/*
+ * tomoyo_path_info is a structure which is used for holding a string data
+ * used by TOMOYO.
+ * This structure has several fields for supporting pattern matching.
+ *
+ * (1) "name" is the '\0' terminated string data.
+ * (2) "hash" is full_name_hash(name, strlen(name)).
+ * This allows tomoyo_pathcmp() to compare by hash before actually compare
+ * using strcmp().
+ * (3) "const_len" is the length of the initial segment of "name" which
+ * consists entirely of non wildcard characters. In other words, the length
+ * which we can compare two strings using strncmp().
+ * (4) "is_dir" is a bool which is true if "name" ends with "/",
+ * false otherwise.
+ * TOMOYO distinguishes directory and non-directory. A directory ends with
+ * "/" and non-directory does not end with "/".
+ * (5) "is_patterned" is a bool which is true if "name" contains wildcard
+ * characters, false otherwise. This allows TOMOYO to use "hash" and
+ * strcmp() for string comparison if "is_patterned" is false.
+ */
+struct tomoyo_path_info {
+ const char *name;
+ u32 hash; /* = full_name_hash(name, strlen(name)) */
+ u16 const_len; /* = tomoyo_const_part_length(name) */
+ bool is_dir; /* = tomoyo_strendswith(name, "/") */
+ bool is_patterned; /* = const_len < strlen(name) */
+};
+
+/* Structure for execve() operation. */
+struct tomoyo_execve_entry {
+ struct tomoyo_request_info r;
+ struct tomoyo_obj_info obj;
+ struct linux_binprm *bprm;
+ struct tomoyo_domain_info *previous_domain;
+ int reader_idx;
+ /* For execute_handler */
+ const struct tomoyo_path_info *handler;
+ char *handler_path; /* = kstrdup(handler->name, GFP_KERNEL) */
+ /* For dumping argv[] and envp[]. */
+ struct tomoyo_page_dump dump;
+ /* For temporary use. */
+ char *tmp; /* Size is TOMOYO_EXEC_TMPSIZE bytes */
+ atomic_t cred_users;
+};
+
+/*
+ * tomoyo_acl_info is a structure which is used for holding
+ *
+ * (1) "list" which is linked to the ->acl_info_list of
+ * "struct tomoyo_domain_info"
+ * (2) "type" which tells type of the enrty.
+ * (3) "is_deleted" is a bool which is true if marked as deleted, false
+ * otherwise.
+ * (4) "cond" is condition part ("if" clause and "; set" clause).
+ *
+ * Packing "struct tomoyo_acl_info" allows users of this struct
+ * to embed "u16" or "u8" without enlarging their structure size.
+ */
+struct tomoyo_acl_info {
+ struct list_head list;
+ struct tomoyo_condition *cond;
+ bool is_deleted;
+ u8 type; /* = one of values in "enum tomoyo_acl_entry_type_index" */
+} __attribute__((__packed__));
+
+/*
+ * tomoyo_domain_info is a structure which is used for holding permissions
+ * (e.g. "allow_read /lib/libc-2.5.so") given to each domain.
+ * It has following fields.
+ *
+ * (1) "list" which is linked to tomoyo_domain_list .
+ * (2) "acl_info_list" which is linked to "struct tomoyo_acl_info".
+ * (3) "domainname" which holds the name of the domain.
+ * (4) "profile" which remembers profile number assigned to this domain.
+ * (5) "is_deleted" is a bool which is true if this domain is marked as
+ * "deleted", false otherwise.
+ * (6) "quota_warned" is a bool which is used for suppressing warning message
+ * when learning mode learned too much entries.
+ * (7) "ignore_global_allow_read" is a bool which is true if this domain
+ * ignores tomoyo_globally_readable_list, false otherwise.
+ * (8) "ignore_global_allow_env" is a bool which is true if this domain
+ * ignores tomoyo_globally_usable_env_list, false otherwise.
+ * (9) "domain_transition_failed" is a bool which is true if this domain
+ * has failed to transit domain.
+ *
+ * A domain's lifecycle is an analogy of files on / directory.
+ * Multiple domains with the same domainname cannot be created (as with
+ * creating files with the same filename fails with -EEXIST).
+ * If a process reached a domain, that process can reside in that domain after
+ * that domain is marked as "deleted" (as with a process can access an already
+ * open()ed file after that file was unlink()ed).
+ */
+struct tomoyo_domain_info {
+ struct list_head list;
+ struct list_head acl_info_list;
+ /* Name of this domain. Never NULL. */
+ const struct tomoyo_path_info *domainname;
+ u8 profile; /* Profile number to use. */
+ bool is_deleted; /* Delete flag. */
+ bool quota_warned; /* Quota warnning flag. */
+ /* Ignore "allow_read" directive in exception policy. */
+ bool ignore_global_allow_read;
+ /* Ignore "allow_env" directive in exception policy. */
+ bool ignore_global_allow_env;
+ /*
+ * This domain was unable to create a new domain at
+ * tomoyo_find_next_domain() because the name of the domain to be
+ * created was too long or it could not allocate memory.
+ * More than one process continued execve() without domain transition.
+ */
+ bool domain_transition_failed;
+};
+
+/*
+ * tomoyo_globally_readable_file_entry is a structure which is used for holding
+ * "allow_read" entries.
+ * It has following fields.
+ *
+ * (1) "list" which is linked to tomoyo_globally_readable_list .
+ * (2) "filename" is a pathname which is allowed to open(O_RDONLY).
+ * (3) "is_deleted" is a bool which is true if marked as deleted, false
+ * otherwise.
+ */
+struct tomoyo_globally_readable_file_entry {
+ struct list_head list;
+ bool is_deleted;
+ const struct tomoyo_path_info *filename;
+};
+
+/*
+ * tomoyo_pattern_entry is a structure which is used for holding
+ * "tomoyo_pattern_list" entries.
+ * It has following fields.
+ *
+ * (1) "list" which is linked to tomoyo_pattern_list .
+ * (2) "pattern" is a pathname pattern which is used for converting pathnames
+ * to pathname patterns during learning mode.
+ * (3) "is_deleted" is a bool which is true if marked as deleted, false
+ * otherwise.
+ */
+struct tomoyo_pattern_entry {
+ struct list_head list;
+ bool is_deleted;
+ const struct tomoyo_path_info *pattern;
+};
+
+/*
+ * tomoyo_no_rewrite_entry is a structure which is used for holding
+ * "deny_rewrite" entries.
+ * It has following fields.
+ *
+ * (1) "list" which is linked to tomoyo_no_rewrite_list .
+ * (2) "pattern" is a pathname which is by default not permitted to modify
+ * already existing content.
+ * (3) "is_deleted" is a bool which is true if marked as deleted, false
+ * otherwise.
+ */
+struct tomoyo_no_rewrite_entry {
+ struct list_head list;
+ bool is_deleted;
+ const struct tomoyo_path_info *pattern;
+};
+
+/* Structure for "allow_env" keyword. */
+struct tomoyo_globally_usable_env_entry {
+ struct list_head list;
+ bool is_deleted;
+ const struct tomoyo_path_info *env;
+};
+
+/*
+ * tomoyo_domain_initializer_entry is a structure which is used for holding
+ * "initialize_domain" and "no_initialize_domain" entries.
+ * It has following fields.
+ *
+ * (1) "list" which is linked to tomoyo_domain_initializer_list .
+ * (2) "domainname" which is "a domainname" or "the last component of a
+ * domainname". This field is NULL if "from" clause is not specified.
+ * (3) "program" which is a program's pathname.
+ * (4) "is_deleted" is a bool which is true if marked as deleted, false
+ * otherwise.
+ * (5) "is_not" is a bool which is true if "no_initialize_domain", false
+ * otherwise.
+ * (6) "is_last_name" is a bool which is true if "domainname" is "the last
+ * component of a domainname", false otherwise.
+ */
+struct tomoyo_domain_initializer_entry {
+ struct list_head list;
+ bool is_deleted;
+ bool is_not; /* True if this entry is "no_initialize_domain". */
+ bool is_last_name; /* True if the domainname is tomoyo_last_word(). */
+ const struct tomoyo_path_info *domainname; /* This may be NULL */
+ const struct tomoyo_path_info *program;
+};
+
+/*
+ * tomoyo_domain_keeper_entry is a structure which is used for holding
+ * "keep_domain" and "no_keep_domain" entries.
+ * It has following fields.
+ *
+ * (1) "list" which is linked to tomoyo_domain_keeper_list .
+ * (2) "domainname" which is "a domainname" or "the last component of a
+ * domainname".
+ * (3) "program" which is a program's pathname.
+ * This field is NULL if "from" clause is not specified.
+ * (4) "is_deleted" is a bool which is true if marked as deleted, false
+ * otherwise.
+ * (5) "is_not" is a bool which is true if "no_initialize_domain", false
+ * otherwise.
+ * (6) "is_last_name" is a bool which is true if "domainname" is "the last
+ * component of a domainname", false otherwise.
+ */
+struct tomoyo_domain_keeper_entry {
+ struct list_head list;
+ bool is_deleted;
+ bool is_not; /* True if this entry is "no_keep_domain". */
+ bool is_last_name; /* True if the domainname is tomoyo_last_word(). */
+ const struct tomoyo_path_info *domainname;
+ const struct tomoyo_path_info *program; /* This may be NULL */
+};
+
+/* Structure for "aggregator" keyword. */
+struct tomoyo_aggregator_entry {
+ struct list_head list;
+ bool is_deleted;
+ const struct tomoyo_path_info *original_name;
+ const struct tomoyo_path_info *aggregated_name;
+};
+
+/* Structure for "allow_mount" keyword. */
+struct tomoyo_mount_acl {
+ struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_MOUNT_ACL */
+ struct tomoyo_name_union dev_name;
+ struct tomoyo_name_union dir_name;
+ struct tomoyo_name_union fs_type;
+ struct tomoyo_number_union flags;
+};
+
+/*
+ * tomoyo_policy_manager_entry is a structure which is used for holding list of
+ * domainnames or programs which are permitted to modify configuration via
+ * /sys/kernel/security/tomoyo/ interface.
+ * It has following fields.
+ *
+ * (1) "list" which is linked to tomoyo_policy_manager_list .
+ * (2) "manager" is a domainname or a program's pathname.
+ * (3) "is_domain" is a bool which is true if "manager" is a domainname, false
+ * otherwise.
+ * (4) "is_deleted" is a bool which is true if marked as deleted, false
+ * otherwise.
+ */
+struct tomoyo_policy_manager_entry {
+ struct list_head list;
+ bool is_deleted; /* True if this entry is deleted. */
+ bool is_domain; /* True if manager is a domainname. */
+ /* A path to program or a domainname. */
+ const struct tomoyo_path_info *manager;
+};
+
+/* Structure for argv[]. */
+struct tomoyo_argv_entry {
+ unsigned int index;
+ const struct tomoyo_path_info *value;
+ bool is_not;
+};
+
+/* Structure for envp[]. */
+struct tomoyo_envp_entry {
+ const struct tomoyo_path_info *name;
+ const struct tomoyo_path_info *value;
+ bool is_not;
+};
+
+/*
+ * Structure for "execute_handler" and "denied_execute_handler" directive.
+ * These directives can exist only one entry in a domain.
+ *
+ * If "execute_handler" directive exists and the current process is not
+ * an execute handler, all execve() requests are replaced by execve() requests
+ * of a program specified by "execute_handler" directive.
+ * If the current process is an execute handler,
+ * "execute_handler" and "denied_execute_handler" directives are ignored.
+ * The program specified by "execute_handler" validates execve() parameters
+ * and executes the original execve() requests if appropriate.
+ *
+ * "denied_execute_handler" directive is used only when execve() request was
+ * rejected in enforcing mode (i.e. MAC_FOR_FILE=enforcing).
+ * The program specified by "denied_execute_handler" does whatever it wants
+ * to do (e.g. silently terminate, change firewall settings,
+ * redirect the user to honey pot etc.).
+ */
+struct tomoyo_execute_handler_record {
+ struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_*EXECUTE_HANDLER */
+ /* Pointer to single pathname. */
+ const struct tomoyo_path_info *handler;
+};
+
+/*
+ * tomoyo_path_acl is a structure which is used for holding an entry with
+ * one pathname operation (e.g. open(), unlink()).
+ * It has following fields.
+ *
+ * (1) "head" which is a "struct tomoyo_acl_info".
+ * (2) "perm" which is a bitmask of permitted operations.
+ * (3) "name" is the pathname or pathname group.
+ *
+ * Directives held by this structure are "allow_read/write", "allow_execute",
+ * "allow_read", "allow_write", "allow_unlink", "allow_rmdir",
+ * "allow_truncate", "allow_symlink", "allow_rewrite", "allow_chroot" and
+ * "allow_unmount".
+ */
+struct tomoyo_path_acl {
+ struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_PATH_ACL */
+ u16 perm;
+ struct tomoyo_name_union name;
+};
+
+/* Structure for "allow_mkblock" and "allow_mkchar" directive. */
+struct tomoyo_path_number3_acl {
+ struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_PATH_NUMBER3_ACL */
+ u8 perm;
+ struct tomoyo_name_union name;
+ struct tomoyo_number_union mode;
+ struct tomoyo_number_union major;
+ struct tomoyo_number_union minor;
+};
+
+/*
+ * tomoyo_path2_acl is a structure which is used for holding an entry with
+ * two pathnames operation (i.e. link(), rename(), pivot_root()).
+ * It has following fields.
+ *
+ * (1) "head" which is a "struct tomoyo_acl_info".
+ * (2) "perm" which is a bitmask of permitted operations.
+ * (3) "filename1" is the source/old pathname.
+ * (4) "filename2" is the destination/new pathname.
+ *
+ * Directives held by this structure are "allow_rename", "allow_link" and
+ * "allow_pivot_root".
+ */
+struct tomoyo_path2_acl {
+ struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_PATH2_ACL */
+ u8 perm;
+ struct tomoyo_name_union name1;
+ struct tomoyo_name_union name2;
+};
+
+/*
+ * Structure for "allow_create", "allow_mkdir", "allow_mkfifo", "allow_mksock",
+ * "allow_ioctl", "allow_chmod", "allow_chown" and "allow_chgrp" directive.
+ */
+struct tomoyo_path_number_acl {
+ struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_PATH_NUMBER_ACL */
+ u8 perm;
+ struct tomoyo_name_union name;
+ struct tomoyo_number_union number;
+};
+
+/* Structure for "allow_env" directive in domain policy. */
+struct tomoyo_env_acl {
+ struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_ENV_ACL */
+ const struct tomoyo_path_info *env; /* environment variable */
+};
+
+/* Structure for "allow_capability" directive. */
+struct tomoyo_capability_acl {
+ struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_CAPABILITY_ACL */
+ u8 operation;
+};
+
+struct tomoyo_ipv6addr_entry {
+ struct list_head list;
+ atomic_t users;
+ struct in6_addr addr;
+};
+
+/* Structure for "allow_network" directive. */
+struct tomoyo_ip_network_acl {
+ struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_IP_NETWORK_ACL */
+ u16 perm;
+ /*
+ * address_type takes one of the following constants.
+ * TOMOYO_IP_ADDRESS_TYPE_ADDRESS_GROUP
+ * if address points to "address_group" directive.
+ * TOMOYO_IP_ADDRESS_TYPE_IPv4
+ * if address points to an IPv4 address.
+ * TOMOYO_IP_ADDRESS_TYPE_IPv6
+ * if address points to an IPv6 address.
+ */
+ u8 address_type;
+ union {
+ struct {
+ /* Start of IPv4 address range. Host endian. */
+ u32 min;
+ /* End of IPv4 address range. Host endian. */
+ u32 max;
+ } ipv4;
+ struct {
+ /* Start of IPv6 address range. Big endian. */
+ const struct in6_addr *min;
+ /* End of IPv6 address range. Big endian. */
+ const struct in6_addr *max;
+ } ipv6;
+ /* Pointer to address group. */
+ struct tomoyo_address_group *group;
+ } address;
+ struct tomoyo_number_union port;
+};
+
+
+/*
+ * tomoyo_io_buffer is a structure which is used for reading and modifying
+ * configuration via /sys/kernel/security/tomoyo/ interface.
+ * It has many fields. ->read_var1 , ->read_var2 , ->write_var1 are used as
+ * cursors.
+ *
+ * Since the content of /sys/kernel/security/tomoyo/domain_policy is a list of
+ * "struct tomoyo_domain_info" entries and each "struct tomoyo_domain_info"
+ * entry has a list of "struct tomoyo_acl_info", we need two cursors when
+ * reading (one is for traversing tomoyo_domain_list and the other is for
+ * traversing "struct tomoyo_acl_info"->acl_info_list ).
+ *
+ * If a line written to /sys/kernel/security/tomoyo/domain_policy starts with
+ * "select ", TOMOYO seeks the cursor ->read_var1 and ->write_var1 to the
+ * domain with the domainname specified by the rest of that line (NULL is set
+ * if seek failed).
+ * If a line written to /sys/kernel/security/tomoyo/domain_policy starts with
+ * "delete ", TOMOYO deletes an entry or a domain specified by the rest of that
+ * line (->write_var1 is set to NULL if a domain was deleted).
+ * If a line written to /sys/kernel/security/tomoyo/domain_policy starts with
+ * neither "select " nor "delete ", an entry or a domain specified by that line
+ * is appended.
+ */
+struct tomoyo_io_buffer {
+ void (*read) (struct tomoyo_io_buffer *);
+ int (*write) (struct tomoyo_io_buffer *);
+ int (*poll) (struct file *file, poll_table *wait);
+ /* Exclusive lock for this structure. */
+ struct mutex io_sem;
+ /* Index returned by tomoyo_read_lock(). */
+ int reader_idx;
+ /* The position currently reading from. */
+ struct list_head *read_var1;
+ /* Extra variables for reading. */
+ struct list_head *read_var2;
+ /* The position currently writing to. */
+ struct tomoyo_domain_info *write_var1;
+ /* The step for reading. */
+ int read_step;
+ /* Buffer for reading. */
+ char *read_buf;
+ /* EOF flag for reading. */
+ bool read_eof;
+ /* Read domain ACL of specified PID? */
+ bool read_single_domain;
+ /* Read allow_execute entry only? */
+ bool read_execute_only;
+ /* Extra variable for reading. */
+ u8 read_bit;
+ /* Bytes available for reading. */
+ int read_avail;
+ /* Size of read buffer. */
+ int readbuf_size;
+ /* Buffer for writing. */
+ char *write_buf;
+ /* Bytes available for writing. */
+ int write_avail;
+ /* Size of write buffer. */
+ int writebuf_size;
+ /* Type of this interface. */
+ u8 type;
+};
+
+struct tomoyo_preference {
+#ifdef CONFIG_SECURITY_TOMOYO_AUDIT
+ unsigned int audit_max_grant_log;
+ unsigned int audit_max_reject_log;
+#endif
+ unsigned int learning_max_entry;
+ unsigned int enforcing_penalty;
+ bool audit_task_info;
+ bool audit_path_info;
+ bool enforcing_verbose;
+ bool learning_verbose;
+ bool learning_exec_realpath;
+ bool learning_exec_argv0;
+ bool learning_symlink_target;
+ bool permissive_verbose;
+};
+
+/*
+ * tomoyo_profile is a structure which is used for holding the mode of access
+ * controls. TOMOYO has 4 modes: disabled, learning, permissive, enforcing.
+ * An administrator can define up to 256 profiles.
+ * The ->profile of "struct tomoyo_domain_info" is used for remembering
+ * the profile's number (0 - 255) assigned to that domain.
+ *
+ * audit/learning/permissive/enforcing point to either self->preference or
+ * tomoyo_default_profile->preference.
+ */
+struct tomoyo_profile {
+ const struct tomoyo_path_info *comment;
+ struct tomoyo_preference *audit;
+ struct tomoyo_preference *learning;
+ struct tomoyo_preference *permissive;
+ struct tomoyo_preference *enforcing;
+ struct tomoyo_preference preference;
+ u8 default_config;
+ u8 config[TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_CAPABILITY_INDEX
+ + TOMOYO_MAX_MAC_CATEGORY_INDEX];
+};
+
+/* Prototype definition. */
+
+bool tomoyo_address_matches_group(const bool is_ipv6, const u32 *address,
+ const struct tomoyo_address_group *group);
+bool tomoyo_commit_ok(void *ptr, void *data, const unsigned int size);
+bool tomoyo_compare_name_union(const struct tomoyo_path_info *name,
+ const struct tomoyo_name_union *ptr);
+bool tomoyo_compare_number_union(const unsigned long value,
+ const struct tomoyo_number_union *ptr);
+bool tomoyo_condition(struct tomoyo_request_info *r,
+ const struct tomoyo_acl_info *acl);
+bool tomoyo_domain_quota_ok(struct tomoyo_request_info *r);
+bool tomoyo_dump_page(struct linux_binprm *bprm, unsigned long pos,
+ struct tomoyo_page_dump *dump);
+bool tomoyo_get_audit(const u8 profile, const u8 index, const bool is_granted);
+bool tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...)
+ __attribute__ ((format(printf, 2, 3)));
+bool tomoyo_is_correct_domain(const unsigned char *domainname);
+bool tomoyo_is_correct_path(const char *filename, const s8 start_type,
+ const s8 pattern_type, const s8 end_type);
+bool tomoyo_is_domain_def(const unsigned char *buffer);
+bool tomoyo_memory_ok(const void *ptr, const unsigned int size);
+bool tomoyo_number_matches_group(const unsigned long min,
+ const unsigned long max,
+ const struct tomoyo_number_group *group);
+bool tomoyo_parse_name_union(const char *filename,
+ struct tomoyo_name_union *ptr);
+bool tomoyo_parse_number_union(char *data, struct tomoyo_number_union *num);
+bool tomoyo_path_matches_group(const struct tomoyo_path_info *pathname,
+ const struct tomoyo_path_group *group,
+ const bool may_use_pattern);
+bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename,
+ const struct tomoyo_path_info *pattern);
+bool tomoyo_print_number_union(struct tomoyo_io_buffer *head,
+ const struct tomoyo_number_union *ptr);
+bool tomoyo_read_address_group_policy(struct tomoyo_io_buffer *head);
+bool tomoyo_read_aggregator_policy(struct tomoyo_io_buffer *head);
+bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head);
+bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head);
+bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head);
+bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head);
+bool tomoyo_read_globally_usable_env_policy(struct tomoyo_io_buffer *head);
+bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head);
+bool tomoyo_read_number_group_policy(struct tomoyo_io_buffer *head);
+bool tomoyo_read_path_group_policy(struct tomoyo_io_buffer *head);
+bool tomoyo_str_starts(char **src, const char *find);
+bool tomoyo_tokenize(char *buffer, char *w[], size_t size);
+char *tomoyo_encode(const char *str);
+char *tomoyo_init_audit_log(int *len, struct tomoyo_request_info *r);
+char *tomoyo_realpath_from_path(struct path *path);
+const char *tomoyo_cap2keyword(const u8 operation);
+const char *tomoyo_file_pattern(const struct tomoyo_path_info *filename);
+const char *tomoyo_get_exe(void);
+const char *tomoyo_last_word(const char *name);
+const char *tomoyo_net2keyword(const u8 operation);
+const char *tomoyo_path22keyword(const u8 operation);
+const char *tomoyo_path2keyword(const u8 operation);
+const char *tomoyo_path_number2keyword(const u8 operation);
+const char *tomoyo_path_number32keyword(const u8 operation);
+const struct tomoyo_path_info *tomoyo_get_name(const char *name);
+const struct in6_addr *tomoyo_get_ipv6_address(const struct in6_addr *addr);
+int tomoyo_close_control(struct file *file);
+int tomoyo_delete_domain(char *data);
+int tomoyo_env_perm(struct tomoyo_request_info *r, const char *env);
+int tomoyo_exec_perm(struct tomoyo_request_info *r,
+ const struct tomoyo_path_info *filename);
+int tomoyo_get_mode(const u8 profile, const u8 index);
+int tomoyo_get_path(const char *pathname, struct path *path);
+int tomoyo_init_request_info(struct tomoyo_request_info *r,
+ struct tomoyo_domain_info *domain,
+ const u8 index);
+int tomoyo_open_control(const u8 type, struct file *file);
+int tomoyo_parse_ip_address(char *address, u16 *min, u16 *max);
+int tomoyo_poll_control(struct file *file, poll_table *wait);
+int tomoyo_poll_grant_log(struct file *file, poll_table *wait);
+int tomoyo_poll_reject_log(struct file *file, poll_table *wait);
+int tomoyo_read_control(struct file *file, char __user *buffer,
+ const int buffer_len);
+int tomoyo_read_lock(void) __acquires(tomoyo_ss);
+int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...)
+ __attribute__ ((format(printf, 2, 3)));
+int tomoyo_symlink_path(const char *pathname, struct tomoyo_path_info *name);
+int tomoyo_write_address_group_policy(char *data, const bool is_delete);
+int tomoyo_write_aggregator_policy(char *data, const bool is_delete);
+int tomoyo_write_audit_log(const bool is_granted,
+ struct tomoyo_request_info *r,
+ const char *fmt, ...)
+ __attribute__ ((format(printf, 3, 4)));
+int tomoyo_write_capability_policy(char *data,
+ struct tomoyo_domain_info *domain,
+ struct tomoyo_condition *condition,
+ const bool is_delete);
+int tomoyo_write_control(struct file *file, const char __user *buffer,
+ const int buffer_len);
+int tomoyo_write_domain_initializer_policy(char *data, const bool is_not,
+ const bool is_delete);
+int tomoyo_write_domain_keeper_policy(char *data, const bool is_not,
+ const bool is_delete);
+int tomoyo_write_env_policy(char *data, struct tomoyo_domain_info *domain,
+ struct tomoyo_condition *condition,
+ const bool is_delete);
+int tomoyo_write_file_policy(char *data, struct tomoyo_domain_info *domain,
+ struct tomoyo_condition *condition,
+ const bool is_delete);
+int tomoyo_write_globally_readable_policy(char *data, const bool is_delete);
+int tomoyo_write_globally_usable_env_policy(char *data, const bool is_delete);
+int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head);
+int tomoyo_write_mount_policy(char *data, struct tomoyo_domain_info *domain,
+ struct tomoyo_condition *condition,
+ const bool is_delete);
+int tomoyo_write_network_policy(char *data, struct tomoyo_domain_info *domain,
+ struct tomoyo_condition *condition,
+ const bool is_delete);
+int tomoyo_write_no_rewrite_policy(char *data, const bool is_delete);
+int tomoyo_write_number_group_policy(char *data, const bool is_delete);
+int tomoyo_write_path_group_policy(char *data, const bool is_delete);
+int tomoyo_write_pattern_policy(char *data, const bool is_delete);
+struct tomoyo_address_group *tomoyo_get_address_group(const char *group_name);
+struct tomoyo_condition *tomoyo_get_condition(char * const condition);
+struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname);
+struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain
+(const char *domainname, const u8 profile);
+struct tomoyo_number_group *tomoyo_get_number_group(const char *group_name);
+struct tomoyo_path_group *tomoyo_get_path_group(const char *group_name);
+struct tomoyo_profile *tomoyo_profile(const u8 profile);
+u8 tomoyo_parse_ulong(unsigned long *result, char **str);
+void tomoyo_check_profile(void);
+void tomoyo_fill_path_info(struct tomoyo_path_info *ptr);
+void tomoyo_get_attributes(struct tomoyo_obj_info *obj);
+void tomoyo_load_policy(const char *filename);
+void tomoyo_memory_free(const void *ptr, size_t size);
+void tomoyo_normalize_line(unsigned char *buffer);
+void tomoyo_print_ipv6(char *buffer, const int buffer_len,
+ const struct in6_addr *ip);
+void tomoyo_print_ulong(char *buffer, const int buffer_len,
+ const unsigned long value, const u8 type);
+void tomoyo_put_address_group(struct tomoyo_address_group *group);
+void tomoyo_put_condition(struct tomoyo_condition *cond);
+void tomoyo_put_ipv6_address(const struct in6_addr *addr);
+void tomoyo_put_name(const struct tomoyo_path_info *name);
+void tomoyo_put_name_union(struct tomoyo_name_union *ptr);
+void tomoyo_put_number_group(struct tomoyo_number_group *group);
+void tomoyo_put_number_union(struct tomoyo_number_union *ptr);
+void tomoyo_put_path_group(struct tomoyo_path_group *group);
+void tomoyo_read_grant_log(struct tomoyo_io_buffer *head);
+void tomoyo_read_memory_counter(struct tomoyo_io_buffer *head);
+void tomoyo_read_reject_log(struct tomoyo_io_buffer *head);
+void tomoyo_read_unlock(const int idx) __releases(tomoyo_ss);
+void tomoyo_run_gc(void);
+void tomoyo_warn_log(struct tomoyo_request_info *r, const char *fmt, ...)
+ __attribute__ ((format(printf, 2, 3)));
+void tomoyo_warn_oom(const char *function);
+int tomoyo_start_execve(struct linux_binprm *bprm);
+void tomoyo_finish_execve(struct tomoyo_execve_entry *ee, const bool rollback);
+int tomoyo_file_perm(struct tomoyo_request_info *r,
+ const struct tomoyo_path_info *filename, const u8 mode);
+int tomoyo_chroot_permission(struct path *path);
+int tomoyo_mount_permission(char *dev_name, struct path *dir, char *type,
+ const unsigned long flags);
+int tomoyo_pivot_root_permission(struct path *old_path, struct path *new_path);
+int tomoyo_umount_permission(struct vfsmount *mnt);
+int tomoyo_open_permission(struct dentry *dentry, struct vfsmount *mnt,
+ const int flag);
+int tomoyo_rewrite_permission(struct file *filp);
+int tomoyo_ioctl_permission(struct file *filp, unsigned int cmd,
+ unsigned long arg);
+_Bool tomoyo_capable(const u8 operation);
+int tomoyo_mknod_permission(struct inode *dir, struct dentry *dentry,
+ struct vfsmount *mnt, unsigned int mode,
+ unsigned int dev);
+int tomoyo_mkdir_permission(struct inode *dir, struct dentry *dentry,
+ struct vfsmount *mnt, unsigned int mode);
+int tomoyo_rmdir_permission(struct inode *dir, struct dentry *dentry,
+ struct vfsmount *mnt);
+int tomoyo_unlink_permission(struct inode *dir, struct dentry *dentry,
+ struct vfsmount *mnt);
+int tomoyo_symlink_permission(struct inode *dir, struct dentry *dentry,
+ struct vfsmount *mnt, const char *from);
+int tomoyo_truncate_permission(struct dentry *dentry, struct vfsmount *mnt,
+ loff_t length, unsigned int time_attrs);
+int tomoyo_rename_permission(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry,
+ struct vfsmount *mnt);
+int tomoyo_link_permission(struct dentry *old_dentry, struct inode *new_dir,
+ struct dentry *new_dentry, struct vfsmount *mnt);
+int tomoyo_socket_create_permission(int family, int type, int protocol);
+int tomoyo_socket_listen_permission(struct socket *sock);
+int tomoyo_socket_connect_permission(struct socket *sock,
+ struct sockaddr *addr, int addr_len);
+int tomoyo_socket_bind_permission(struct socket *sock, struct sockaddr *addr,
+ int addr_len);
+int tomoyo_socket_accept_permission(struct socket *sock);
+int tomoyo_socket_sendmsg_permission(struct socket *sock, struct msghdr *msg);
+int tomoyo_chown_permission(struct dentry *dentry, struct vfsmount *mnt,
+ uid_t user, gid_t group);
+int tomoyo_chmod_permission(struct dentry *dentry, struct vfsmount *mnt,
+ mode_t mode);
+
+/* strcmp() for "struct tomoyo_path_info" structure. */
+static inline bool tomoyo_pathcmp(const struct tomoyo_path_info *a,
+ const struct tomoyo_path_info *b)
+{
+ return a->hash != b->hash || strcmp(a->name, b->name);
+}
+
+static inline int tomoyo_memcmp(void *a, void *b, const u8 offset,
+ const u8 size)
+{
+ return memcmp(((char *) a) + offset, ((char *) b) + offset,
+ size - offset);
+}
+
+extern struct mutex tomoyo_policy_lock;
+extern struct list_head tomoyo_domain_list;
+extern struct list_head tomoyo_address_group_list;
+extern struct list_head tomoyo_globally_readable_list;
+extern struct list_head tomoyo_path_group_list;
+extern struct list_head tomoyo_number_group_list;
+extern struct list_head tomoyo_pattern_list;
+extern struct list_head tomoyo_no_rewrite_list;
+extern struct list_head tomoyo_globally_usable_env_list;
+extern struct list_head tomoyo_domain_initializer_list;
+extern struct list_head tomoyo_domain_keeper_list;
+extern struct list_head tomoyo_aggregator_list;
+extern struct list_head tomoyo_policy_manager_list;
+extern __initdata bool tomoyo_registered;
+extern bool tomoyo_policy_loaded;
+extern struct tomoyo_domain_info tomoyo_kernel_domain;
+extern const char *tomoyo_condition_keyword[TOMOYO_MAX_CONDITION_KEYWORD];
+extern unsigned int tomoyo_audit_log_memory_size;
+extern unsigned int tomoyo_quota_for_audit_log;
+extern unsigned int tomoyo_query_memory_size;
+extern unsigned int tomoyo_quota_for_query;
+
+static inline struct tomoyo_domain_info *tomoyo_task_domain(struct task_struct
+ *task)
+{
+ return task->tomoyo_domain_info ?
+ task->tomoyo_domain_info : &tomoyo_kernel_domain;
+}
+
+static inline struct tomoyo_domain_info *tomoyo_current_domain(void)
+{
+ struct task_struct *task = current;
+ if (!task->tomoyo_domain_info)
+ task->tomoyo_domain_info = &tomoyo_kernel_domain;
+ return task->tomoyo_domain_info;
+}
+
+static inline void tomoyo_add_domain_acl(struct tomoyo_domain_info *domain,
+ struct tomoyo_acl_info *acl)
+{
+ if (acl->cond)
+ atomic_inc(&acl->cond->users);
+ list_add_tail_rcu(&acl->list, &domain->acl_info_list);
+}
+
+#if defined(CONFIG_SLOB)
+static inline int tomoyo_round2(size_t size)
+{
+ return size;
+}
+#else
+static inline int tomoyo_round2(size_t size)
+{
+#if PAGE_SIZE == 4096
+ size_t bsize = 32;
+#else
+ size_t bsize = 64;
+#endif
+ if (!size)
+ return 0;
+ while (size > bsize)
+ bsize <<= 1;
+ return bsize;
+}
+#endif
+
+#if defined(__LITTLE_ENDIAN)
+#define HIPQUAD(addr) \
+ ((unsigned char *)&addr)[3], \
+ ((unsigned char *)&addr)[2], \
+ ((unsigned char *)&addr)[1], \
+ ((unsigned char *)&addr)[0]
+#elif defined(__BIG_ENDIAN)
+#define HIPQUAD NIPQUAD
+#else
+#error "Please fix asm/byteorder.h"
+#endif /* __LITTLE_ENDIAN */
+
+#endif
--
^ permalink raw reply [flat|nested] 50+ messages in thread
* [TOMOYO #16 05/25] TOMOYO: Add per task_struct variables.
2009-10-04 12:49 [TOMOYO #16 00/25] Starting TOMOYO 2.3 Tetsuo Handa
` (3 preceding siblings ...)
2009-10-04 12:49 ` [TOMOYO #16 04/25] TOMOYO: Add header file Tetsuo Handa
@ 2009-10-04 12:49 ` Tetsuo Handa
2009-10-04 12:49 ` [TOMOYO #16 06/25] TOMOYO: Add LSM adaptor Tetsuo Handa
` (20 subsequent siblings)
25 siblings, 0 replies; 50+ messages in thread
From: Tetsuo Handa @ 2009-10-04 12:49 UTC (permalink / raw)
To: linux-security-module; +Cc: linux-kernel, Tetsuo Handa
[-- Attachment #1: tomoyo-add-per-task-variable.patch --]
[-- Type: text/plain, Size: 7972 bytes --]
I want to introduce per task_struct variables
struct tomoyo_domain_info *tomoyo_domain_info;
u32 tomoyo_flags;
for holding task state variables used by TOMOYO.
(1) These variables are not shared between threads. Only the current thread
modifies current->tomoyo_domain_info and current->tomoyo_flags.
The current->tomoyo_domain_info and current->tomoyo_flags can be simply
copied upon fork() and discarded upon exit().
So far, below status is stored into the current->tomoyo_flags.
Whether the current thread is allowed to modify policy via
/sys/kernel/security/tomoyo/ interface or not. TOMOYO 2.2.0 checks
whenever write() is requested. This is "slow", and the check will "fail"
if the program's pathname is changed by updating TOMOYO's userland tools.
By caching the result of "whether the current thread is allowed to modify
policy via /sys/kernel/security/tomoyo/ interface or not" into this
variable, I can solve the "slow"/"fail" problems.
Whether the current thread is running as an execute handler program or
not. TOMOYO needs to remember this information in order to avoid infinite
execute handler loop.
Whether the current thread should be carried the sleep() penalty or not
if the current thread violated policy in enforcing mode. TOMOYO can
carry sleep() penalty in order to avoid CPU consumption by repeating
requests which violate policy, but the sleep() penalty should not be
carried in some cases (e.g. connections/packets from unwanted hosts)
in order to avoid falling into almost denial-of-service state.
Whether the current thread might roll back to previous domain or not.
This is used for
telling the garbage collector that "do not delete domains because I'll
roll back to the previous domain if search_binary_handler() failed"
and
telling the file access checker that "check read permission because
open_exec() is called by "struct linux_binfmt *"->load_binary()"
.
This is different from current->in_execve. current->in_execve is used for
telling the file access checker that "check execute permission rather
than read permission because open_exec() is called by do_execve()".
3 variables for splitting permissions within the domain. These variables
are updated when a request is permitted by TOMOYO's policy.
(2) The task state variables must not be reverted outside the LSM hooks.
TOMOYO updates the task state variables inside LSM hooks. But there is the
possibility of credentials being reverted outside the LSM hooks. If I store
the updated task state variables into "struct cred *"->security and
revert_creds() is called, the updated task state variables get lost.
So far, it seems that TOMOYO is not using LSM hooks which are called
between override_creds() and revert_creds() (e.g. inode_permission() in
sys_faccessat()). But in the future, LSM hooks which TOMOYO is using could
be called between override_creds() and revert_creds().
(3) Use of COW credentials introduces the possibility of -ENOMEM failure.
I can omit error paths if I can use the per task_struct variables.
This problem will be solved if current->security and security_task_alloc()
and security_task_free() (which existed in 2.6.24 - 2.6.28 kernels) are
revived.
(4) As noted in Documentation/tomoyo.txt , I want TOMOYO to be able to coexist
with other LSM modules.
Since TOMOYO is not a label based access control, TOMOYO can yield
current->cred->security and current->real_cred->security to access controls
who can utilize better, if I can use the per task_struct variables.
Also, TOMOYO can yield various object's "void *security" field except the
one for socket.
TOMOYO is close to be able to coexist with other LSM modules in a way IMA
proposed at http://marc.info/?l=linux-security-module&m=125259049313359&w=2
TOMOYO 2.3 (this submission) and 1.7 (its non-LSM version) became more
suitable for use with other access controls. For example people can use
SELinux or SMACK as a base access control and use TOMOYO for restricting
only directory modification operations (e.g. create()/mkdir()/rename()/
link()) or auditing complete execve() parameters or SSH login sessions,
if LSM allows TOMOYO to coexist with other modules which use
"struct security_operations". Use of per task variables is one of steps
towards such usage.
Hints for supporting TOMOYO's c/r support:
Basically, only current thread is allowed to modify tomoyo_domain_info and
tomoyo_flags. But in case of restarting, it is safe to allow the process who is
processing the checkpointed image to modify task_to_restart->tomoyo_domain_info
and task_to_restart->tomoyo_flags because task_to_restart is controlled by the
process who is processing the checkpointed image.
Checkpointing a process would look like
write_string_to_image(task_to_checkpoint->tomoyo_domain_info->
domainname->name);
write_u32_to_image(task_to_checkpoint->tomoyo_flags);
Restarting a process would look like
const char *domainname = read_string_from_image();
u32 flags = read_u32_from_image();
struct tomoyo_domain_info *domain;
mutex_lock(&tomoyo_policy_lock);
domain = tomoyo_find_domain(domainname);
if (domain) {
task_to_restart->tomoyo_domain_info = domain;
task_to_restart->tomoyo_flags = flags;
}
mutex_unlock(&tomoyo_policy_lock);
return domain ? 0 : -EINVAL;
The difference between fork() and restarting is that the domain to be assigned
is taken from its parent or checkpointed image.
In case of fork(), the domain to be assigned never be garbage collected because
parent process is using that domain. No lock is needed.
In case of restarting, the domain to be assigned could be garbage collected.
To protect the domain from being garbage collected, the process who is
processing the checkpointed image needs to take tomoyo_policy_lock mutex.
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
include/linux/init_task.h | 9 +++++++++
include/linux/sched.h | 6 ++++++
kernel/kmod.c | 5 +++++
3 files changed, 20 insertions(+)
--- security-testing-2.6.orig/include/linux/init_task.h
+++ security-testing-2.6/include/linux/init_task.h
@@ -115,6 +115,14 @@ extern struct cred init_cred;
# define INIT_PERF_EVENTS(tsk)
#endif
+#ifdef CONFIG_SECURITY_TOMOYO
+#define INIT_SECURITY_TOMOYO \
+ .tomoyo_domain_info = NULL, \
+ .tomoyo_flags = 0,
+#else
+#define INIT_SECURITY_TOMOYO
+#endif
+
/*
* INIT_TASK is used to set up the first task table, touch at
* your own risk!. Base=0, limit=0x1fffff (=2MB)
@@ -184,6 +192,7 @@ extern struct cred init_cred;
INIT_FTRACE_GRAPH \
INIT_TRACE_RECURSION \
INIT_TASK_RCU_PREEMPT(tsk) \
+ INIT_SECURITY_TOMOYO \
}
--- security-testing-2.6.orig/include/linux/sched.h
+++ security-testing-2.6/include/linux/sched.h
@@ -29,6 +29,8 @@
#define CLONE_NEWNET 0x40000000 /* New network namespace */
#define CLONE_IO 0x80000000 /* Clone io context */
+struct tomoyo_domain_info;
+
/*
* Scheduling policies
*/
@@ -1539,6 +1541,10 @@ struct task_struct {
unsigned long trace_recursion;
#endif /* CONFIG_TRACING */
unsigned long stack_start;
+#ifdef CONFIG_SECURITY_TOMOYO
+ struct tomoyo_domain_info *tomoyo_domain_info;
+ u32 tomoyo_flags;
+#endif
};
/* Future-safe accessor for struct task_struct's cpus_allowed. */
--- security-testing-2.6.orig/kernel/kmod.c
+++ security-testing-2.6/kernel/kmod.c
@@ -184,6 +184,11 @@ static int ____call_usermodehelper(void
*/
set_user_nice(current, 0);
+#ifdef CONFIG_SECURITY_TOMOYO
+ current->tomoyo_domain_info = NULL;
+ current->tomoyo_flags = 0;
+#endif
+
retval = kernel_execve(sub_info->path, sub_info->argv, sub_info->envp);
/* Exec failed? */
--
^ permalink raw reply [flat|nested] 50+ messages in thread
* [TOMOYO #16 06/25] TOMOYO: Add LSM adaptor.
2009-10-04 12:49 [TOMOYO #16 00/25] Starting TOMOYO 2.3 Tetsuo Handa
` (4 preceding siblings ...)
2009-10-04 12:49 ` [TOMOYO #16 05/25] TOMOYO: Add per task_struct variables Tetsuo Handa
@ 2009-10-04 12:49 ` Tetsuo Handa
2009-10-04 12:49 ` [TOMOYO #16 07/25] TOMOYO: Add path_group keyword support Tetsuo Handa
` (19 subsequent siblings)
25 siblings, 0 replies; 50+ messages in thread
From: Tetsuo Handa @ 2009-10-04 12:49 UTC (permalink / raw)
To: linux-security-module; +Cc: linux-kernel, Tetsuo Handa
[-- Attachment #1: tomoyo-add-lsm.patch --]
[-- Type: text/plain, Size: 17634 bytes --]
Since TOMOYO uses per task_struct variables, I have nothing to store into
"struct cred *"->security except "struct linux_binprm *"->cred->security.
To support execute handler functionality, TOMOYO needs to keep memory for
holding execute handler's pathname valid until search_binary_handler()
finishes. Therefore, to release memory, TOMOYO needs a hook which is called
after an execve() finished. But there is no such hook. As a workaround,
TOMOYO releases memory when "struct linux_binprm *"->cred is discarded (either
security_bprm_committing_creds() (which means execve() succeeded) or
security_cred_free() (which means execve() failed)).
If I'm permitted to add a hook which is called after an execve() finished,
I can avoid such tricky usage of "struct cred *"->security.
Since TOMOYO is not a label based access control, I have nothing to store into
various object's "void *security" field except the one for socket.
To support incoming TCP connection filtering functionality, TOMOYO needs a hook
which is called after accept(). But such hook is not acceptable.
As a workaround, TOMOYO performs TCP connection filtering after returning from
accept(). To do so, I assign special tag to SOCK_INODE(socket)->i_security if
the socket is an accept()ed socket at security_socket_accept(), and I check
permission inside subsequent system calls if SOCK_INODE(socket)->i_security
has the special value.
As of now, hooks for supporting incoming UDP/RAW packet filtering remain
missing. Unlike TCP connection filtering, it is too late to perform filtering
after returning from recvmsg().
As of now, hooks for supporting signal transmission restriction remain missing.
I can't use security_task_kill() because it is called in a context that is not
permitted to sleep. I want hooks which are called in a context that is
permitted to sleep in order to support interactive enforcement mode.
The interactive enforcement is a mode which allows administrators to
interactively judge whether the request should be permitted or not. This mode
is designed for handling unexpected requests caused by software updates.
As of now, many of hooks for supporting TOMOYO's original (i.e. non-POSIX)
capability remain missing. TOMOYO is ready to support 65536 types of
non-POSIX capability but the caller passes only 32 (or so) types of POSIX
capability. I need to introduce mapping table for converting from non-POSIX
capability to POSIX capability. Also, I can't use security_capable() because it
is called in a context that is not permitted to sleep.
Except the missing hooks listed above, this LSM adaptor can provide almost the
same functionality which non-LSM version of TOMOYO provides.
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
security/tomoyo/lsm.c | 523 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 523 insertions(+)
--- /dev/null
+++ security-testing-2.6/security/tomoyo/lsm.c
@@ -0,0 +1,523 @@
+/*
+ * security/tomoyo/lsm.c
+ *
+ * LSM hooks for TOMOYO Linux.
+ *
+ * Copyright (C) 2005-2009 NTT DATA CORPORATION
+ */
+#include "internal.h"
+#include <net/sock.h>
+
+static int tomoyo_cred_alloc_blank(struct cred *new, gfp_t gfp)
+{
+ new->security = NULL;
+ return 0;
+}
+
+static int tomoyo_cred_prepare(struct cred *new, const struct cred *old,
+ gfp_t gfp)
+{
+ /* Get ref if "struct linux_binprm *"->cred */
+ struct tomoyo_execve_entry *ee = old->security;
+ if (ee)
+ atomic_inc(&ee->cred_users);
+ new->security = ee;
+ return 0;
+}
+
+static void tomoyo_cred_transfer(struct cred *new, const struct cred *old)
+{
+ new->security = old->security;
+}
+
+static void tomoyo_cred_free(struct cred *cred)
+{
+ struct tomoyo_execve_entry *ee = cred->security;
+ /* Put ref if "struct linux_binprm *"->cred */
+ if (ee && atomic_dec_and_test(&ee->cred_users)) {
+ /*
+ * Reaching here means execve() failed, for
+ * "struct linux_binprm *"->cred is not shared between
+ * processes.
+ * Release memory allocated by tomoyo_start_execve().
+ */
+ tomoyo_finish_execve(cred->security, true);
+ cred->security = NULL;
+ }
+}
+
+static int tomoyo_bprm_check_security(struct linux_binprm *bprm)
+{
+ /*
+ * Execute permission is checked against pathname passed to do_execve()
+ * using current domain.
+ */
+ if (!bprm->cred->security)
+ return tomoyo_start_execve(bprm);
+ /*
+ * Read permission is checked against interpreters using next domain.
+ * '1' is the result of open_to_namei_flags(O_RDONLY).
+ */
+ return tomoyo_open_permission(bprm->file->f_path.dentry,
+ bprm->file->f_path.mnt, 1);
+}
+
+static void tomoyo_bprm_committing_creds(struct linux_binprm *bprm)
+{
+ /* Release memory allocated by tomoyo_start_execve(). */
+ tomoyo_finish_execve(bprm->cred->security, false);
+ bprm->cred->security = NULL;
+}
+
+#ifdef CONFIG_SYSCTL
+
+static int tomoyo_prepend(char **buffer, int *buflen, const char *str)
+{
+ int namelen = strlen(str);
+
+ if (*buflen < namelen)
+ return -ENOMEM;
+ *buflen -= namelen;
+ *buffer -= namelen;
+ memcpy(*buffer, str, namelen);
+ return 0;
+}
+
+/**
+ * tomoyo_sysctl_path - return the realpath of a ctl_table.
+ * @table: pointer to "struct ctl_table".
+ *
+ * Returns realpath(3) of the @table on success.
+ * Returns NULL on failure.
+ *
+ * This function uses kzalloc(), so the caller must call kfree()
+ * if this function didn't return NULL.
+ */
+static char *tomoyo_sysctl_path(struct ctl_table *table)
+{
+ int buflen = 4096;
+ char *cp = NULL;
+ char *buf = kmalloc(buflen, GFP_KERNEL);
+ char *end = buf + buflen;
+
+ if (!buf)
+ return NULL;
+
+ *--end = '\0';
+ buflen--;
+ while (table) {
+ char num[32];
+ const char *sp = table->procname;
+
+ if (!sp) {
+ memset(num, 0, sizeof(num));
+ snprintf(num, sizeof(num) - 1, "=%d=",
+ table->ctl_name);
+ sp = num;
+ }
+ if (tomoyo_prepend(&end, &buflen, sp) ||
+ tomoyo_prepend(&end, &buflen, "/"))
+ goto out;
+ table = table->parent;
+ }
+ if (tomoyo_prepend(&end, &buflen, "/proc/sys"))
+ goto out;
+ cp = tomoyo_encode(end);
+ out:
+ kfree(buf);
+ return cp;
+}
+
+static int tomoyo_sysctl(struct ctl_table *table, int op)
+{
+ int error = 0;
+ struct tomoyo_request_info r;
+ struct tomoyo_path_info buf;
+ int idx;
+
+ op &= MAY_READ | MAY_WRITE;
+ if (!op)
+ return 0;
+
+ idx = tomoyo_read_lock();
+ if (tomoyo_init_request_info(&r, NULL, TOMOYO_MAC_FILE_OPEN)
+ == TOMOYO_CONFIG_DISABLED)
+ goto out;
+
+ buf.name = tomoyo_sysctl_path(table);
+ if (buf.name) {
+ tomoyo_fill_path_info(&buf);
+ error = tomoyo_file_perm(&r, &buf, op);
+ kfree(buf.name);
+ } else
+ error = -ENOMEM;
+ out:
+ tomoyo_read_unlock(idx);
+ return error;
+}
+
+#endif
+
+static int tomoyo_path_truncate(struct path *path, loff_t length,
+ unsigned int time_attrs)
+{
+ return tomoyo_truncate_permission(path->dentry, path->mnt,
+ length, time_attrs);
+}
+
+static int tomoyo_path_unlink(struct path *parent, struct dentry *dentry)
+{
+ return tomoyo_unlink_permission(parent->dentry->d_inode, dentry,
+ parent->mnt);
+}
+
+static int tomoyo_path_mkdir(struct path *parent, struct dentry *dentry,
+ int mode)
+{
+ return tomoyo_mkdir_permission(parent->dentry->d_inode, dentry,
+ parent->mnt, mode);
+}
+
+static int tomoyo_path_rmdir(struct path *parent, struct dentry *dentry)
+{
+ return tomoyo_rmdir_permission(parent->dentry->d_inode, dentry,
+ parent->mnt);
+}
+
+static int tomoyo_path_symlink(struct path *parent, struct dentry *dentry,
+ const char *old_name)
+{
+ return tomoyo_symlink_permission(parent->dentry->d_inode, dentry,
+ parent->mnt, old_name);
+}
+
+static int tomoyo_path_mknod(struct path *parent, struct dentry *dentry,
+ int mode, unsigned int dev)
+{
+ return tomoyo_mknod_permission(parent->dentry->d_inode, dentry,
+ parent->mnt, mode, dev);
+}
+
+static int tomoyo_path_link(struct dentry *old_dentry, struct path *new_dir,
+ struct dentry *new_dentry)
+{
+ return tomoyo_link_permission(old_dentry, new_dir->dentry->d_inode,
+ new_dentry, new_dir->mnt);
+}
+
+static int tomoyo_path_rename(struct path *old_parent,
+ struct dentry *old_dentry,
+ struct path *new_parent,
+ struct dentry *new_dentry)
+{
+ return tomoyo_rename_permission(old_parent->dentry->d_inode,
+ old_dentry,
+ new_parent->dentry->d_inode,
+ new_dentry, old_parent->mnt);
+}
+
+static int tomoyo_path_chmod(struct dentry *dentry, struct vfsmount *mnt,
+ mode_t mode)
+{
+ return tomoyo_chmod_permission(dentry, mnt, mode);
+}
+
+static int tomoyo_path_chown(struct path *path, uid_t uid, gid_t gid)
+{
+ return tomoyo_chown_permission(path->dentry, path->mnt, uid, gid);
+}
+
+static int tomoyo_path_chroot(struct path *path)
+{
+ return tomoyo_chroot_permission(path);
+}
+
+static int tomoyo_file_fcntl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ if (cmd == F_SETFL && ((arg ^ file->f_flags) & O_APPEND))
+ return tomoyo_rewrite_permission(file);
+ return 0;
+}
+
+static int tomoyo_dentry_open(struct file *f, const struct cred *cred)
+{
+ /* Don't check read permission here if called from do_execve(). */
+ if (current->in_execve)
+ return 0;
+ /*
+ * TOMOYO does not check "allow_write" if
+ * open(path, O_TRUNC | O_RDONLY) was requested because write() is not
+ * permitted. Instead, TOMOYO checks "allow_truncate" if O_TRUNC is
+ * passed.
+ *
+ * TOMOYO does not check "allow_read/write" if open(path, 3) was
+ * requested because read()/write() are not permitted. Instead, TOMOYO
+ * checks "allow_ioctl" when ioctl() is requested.
+ */
+ return tomoyo_open_permission(f->f_path.dentry, f->f_path.mnt,
+ f->f_flags + 1);
+}
+
+static int tomoyo_sb_mount(char *dev_name, struct path *path,
+ char *type, unsigned long flags, void *data)
+{
+ return tomoyo_mount_permission(dev_name, path, type, flags);
+}
+
+static int tomoyo_sb_umount(struct vfsmount *mnt, int flags)
+{
+ return tomoyo_umount_permission(mnt);
+}
+
+static int tomoyo_sb_pivotroot(struct path *old_path, struct path *new_path)
+{
+ return tomoyo_pivot_root_permission(old_path, new_path);
+}
+
+/* Magic constants for socket. Only address is used. */
+/* This socket is created by kernel. Thus, no permission check. */
+static const bool tomoyo_socket_kernel_socket;
+/*
+ * This socket is an accept()ed socket. Thus, I need to check acccept()
+ * permission after returning from accept().
+ */
+static const bool tomoyo_socket_not_yet_authorized;
+/*
+ * This socket is an accept()ed socket but permission denied. Thus, this socket
+ * should be close()d.
+ */
+static const bool tomoyo_socket_not_yet_aborted;
+
+static inline bool tomoyo_kern_sock(struct socket *sock)
+{
+ return SOCK_INODE(sock)->i_security == &tomoyo_socket_kernel_socket;
+}
+
+static int tomoyo_dead_sock(struct socket *sock)
+{
+ if (!sock || !SOCK_INODE(sock) || !SOCK_INODE(sock)->i_security)
+ return 0;
+ if (SOCK_INODE(sock)->i_security
+ == &tomoyo_socket_not_yet_authorized) {
+ /*
+ * This socket is an accept()ed socket, but not yet
+ * authorized. Check permission for accept() now.
+ */
+ if (tomoyo_socket_accept_permission(sock) == 0) {
+ SOCK_INODE(sock)->i_security = NULL;
+ return 0;
+ }
+ /*
+ * Tell the caller that this socket should be close()ed
+ * immediately. But in case the caller does not close this
+ * socket immediately, I mark special tag so that I can give
+ * sleep() penalty for subsequent socket calls.
+ */
+ SOCK_INODE(sock)->i_security =
+ (void *) &tomoyo_socket_not_yet_aborted;
+ } else {
+ /*
+ * Give sleep() penalty.
+ *
+ * This penalty is intended for avoiding CPU resource
+ * consumption caused by select()/poll() saying "ready" and
+ * socket calls saying "not permitted" loop.
+ */
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ / 10);
+ }
+ return -EPERM;
+}
+
+static int tomoyo_socket_create(int family, int type, int protocol, int kern)
+{
+ if (kern)
+ return 0;
+ return tomoyo_socket_create_permission(family, type, protocol);
+}
+
+static int tomoyo_socket_post_create(struct socket *sock, int family, int type,
+ int protocol, int kern)
+{
+ if (kern)
+ SOCK_INODE(sock)->i_security =
+ (void *) &tomoyo_socket_kernel_socket;
+ return 0;
+}
+
+static int tomoyo_socket_accept(struct socket *sock, struct socket *newsock)
+{
+ if (tomoyo_kern_sock(sock))
+ return 0;
+ if (tomoyo_dead_sock(sock))
+ return -EPERM;
+ /* Check permission for accept() later. */
+ SOCK_INODE(newsock)->i_security =
+ (void *) &tomoyo_socket_not_yet_authorized;
+ return 0;
+}
+
+static int tomoyo_socket_bind(struct socket *sock, struct sockaddr *address,
+ int addrlen)
+{
+ if (tomoyo_kern_sock(sock))
+ return 0;
+ if (tomoyo_dead_sock(sock))
+ return -EPERM;
+ return tomoyo_socket_bind_permission(sock, address, addrlen);
+}
+
+static int tomoyo_socket_connect(struct socket *sock, struct sockaddr *address,
+ int addrlen)
+{
+ if (tomoyo_kern_sock(sock))
+ return 0;
+ if (tomoyo_dead_sock(sock))
+ return -EPERM;
+ return tomoyo_socket_connect_permission(sock, address, addrlen);
+}
+
+static int tomoyo_socket_listen(struct socket *sock, int backlog)
+{
+ if (tomoyo_kern_sock(sock))
+ return 0;
+ if (tomoyo_dead_sock(sock))
+ return -EPERM;
+ return tomoyo_socket_listen_permission(sock);
+}
+
+static int tomoyo_socket_getsockname(struct socket *sock)
+{
+ if (tomoyo_kern_sock(sock))
+ return 0;
+ return tomoyo_dead_sock(sock);
+}
+
+static int tomoyo_socket_getpeername(struct socket *sock)
+{
+ if (tomoyo_kern_sock(sock))
+ return 0;
+ return tomoyo_dead_sock(sock);
+}
+
+static int tomoyo_socket_getsockopt(struct socket *sock, int level,
+ int optname)
+{
+ if (tomoyo_kern_sock(sock))
+ return 0;
+ return tomoyo_dead_sock(sock);
+}
+
+static int tomoyo_socket_setsockopt(struct socket *sock, int level,
+ int optname)
+{
+ if (tomoyo_kern_sock(sock))
+ return 0;
+ return tomoyo_dead_sock(sock);
+}
+
+static int tomoyo_socket_sendmsg(struct socket *sock, struct msghdr *msg,
+ int size)
+{
+ if (tomoyo_kern_sock(sock))
+ return 0;
+ if (tomoyo_dead_sock(sock))
+ return -EPERM;
+ return tomoyo_socket_sendmsg_permission(sock, msg);
+}
+
+static int tomoyo_socket_recvmsg(struct socket *sock, struct msghdr *msg,
+ int size, int flags)
+{
+ if (tomoyo_kern_sock(sock))
+ return 0;
+ if (tomoyo_dead_sock(sock))
+ return -EPERM;
+ return 0;
+}
+
+#define SOCKFS_MAGIC 0x534F434B
+
+static int tomoyo_file_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ /* Check permission if this is a socket. */
+ struct dentry *dentry = file->f_path.dentry;
+ if (dentry && dentry->d_sb && dentry->d_sb->s_magic == SOCKFS_MAGIC) {
+ struct inode *inode = dentry->d_inode;
+ struct socket *sock = inode ? SOCKET_I(inode) : NULL;
+ if (inode) {
+ if (tomoyo_kern_sock(sock))
+ return 0;
+ if (tomoyo_dead_sock(sock))
+ return -EPERM;
+ }
+ }
+ return tomoyo_ioctl_permission(file, cmd, arg);
+}
+
+/*
+ * tomoyo_security_ops is a "struct security_operations" which is used for
+ * registering TOMOYO.
+ */
+static struct security_operations tomoyo_security_ops = {
+ .name = "tomoyo",
+ .cred_alloc_blank = tomoyo_cred_alloc_blank,
+ .cred_prepare = tomoyo_cred_prepare,
+ .cred_transfer = tomoyo_cred_transfer,
+ .bprm_check_security = tomoyo_bprm_check_security,
+ .bprm_committing_creds = tomoyo_bprm_committing_creds,
+ .cred_free = tomoyo_cred_free,
+#ifdef CONFIG_SYSCTL
+ .sysctl = tomoyo_sysctl,
+#endif
+ .file_fcntl = tomoyo_file_fcntl,
+ .dentry_open = tomoyo_dentry_open,
+ .path_truncate = tomoyo_path_truncate,
+ .path_unlink = tomoyo_path_unlink,
+ .path_mkdir = tomoyo_path_mkdir,
+ .path_rmdir = tomoyo_path_rmdir,
+ .path_symlink = tomoyo_path_symlink,
+ .path_mknod = tomoyo_path_mknod,
+ .path_link = tomoyo_path_link,
+ .path_rename = tomoyo_path_rename,
+ .path_chmod = tomoyo_path_chmod,
+ .path_chown = tomoyo_path_chown,
+ .path_chroot = tomoyo_path_chroot,
+ .file_ioctl = tomoyo_file_ioctl,
+ .sb_pivotroot = tomoyo_sb_pivotroot,
+ .sb_mount = tomoyo_sb_mount,
+ .sb_umount = tomoyo_sb_umount,
+ .socket_create = tomoyo_socket_create,
+ .socket_bind = tomoyo_socket_bind,
+ .socket_listen = tomoyo_socket_listen,
+ .socket_connect = tomoyo_socket_connect,
+ .socket_accept = tomoyo_socket_accept,
+ .socket_sendmsg = tomoyo_socket_sendmsg,
+ .socket_recvmsg = tomoyo_socket_recvmsg,
+ .socket_post_create = tomoyo_socket_post_create,
+ .socket_getsockname = tomoyo_socket_getsockname,
+ .socket_getpeername = tomoyo_socket_getpeername,
+ .socket_getsockopt = tomoyo_socket_getsockopt,
+ .socket_setsockopt = tomoyo_socket_setsockopt,
+};
+
+/*
+ * To tell tomoyo_mm_init()/tomoyo_securityfs_init() that TOMOYO was
+ * registered.
+ */
+__initdata bool tomoyo_registered;
+static int __init tomoyo_init(void)
+{
+ if (!security_module_enable(&tomoyo_security_ops))
+ return 0;
+ /* register ourselves with the security framework */
+ if (register_security(&tomoyo_security_ops))
+ panic("Failure registering TOMOYO Linux");
+ printk(KERN_INFO "TOMOYO Linux initialized\n");
+ /* call tomoyo_mm_init() and tomoyo_securityfs_init() later. */
+ tomoyo_registered = true;
+ return 0;
+}
+security_initcall(tomoyo_init);
--
^ permalink raw reply [flat|nested] 50+ messages in thread
* [TOMOYO #16 07/25] TOMOYO: Add path_group keyword support.
2009-10-04 12:49 [TOMOYO #16 00/25] Starting TOMOYO 2.3 Tetsuo Handa
` (5 preceding siblings ...)
2009-10-04 12:49 ` [TOMOYO #16 06/25] TOMOYO: Add LSM adaptor Tetsuo Handa
@ 2009-10-04 12:49 ` Tetsuo Handa
2009-10-04 12:49 ` [TOMOYO #16 08/25] TOMOYO: Add number_group " Tetsuo Handa
` (18 subsequent siblings)
25 siblings, 0 replies; 50+ messages in thread
From: Tetsuo Handa @ 2009-10-04 12:49 UTC (permalink / raw)
To: linux-security-module; +Cc: linux-kernel, Tetsuo Handa
[-- Attachment #1: tomoyo-add-path_group.patch --]
[-- Type: text/plain, Size: 6879 bytes --]
This patch contains code for handling pathname grouping.
To be able to specify pathnames more flexibly, I want to add pathname grouping
support into TOMOYO 2.3.0.
path_group APACHE_READABLE_FILES /var/www/html/\*.html
path_group APACHE_READABLE_FILES /var/www/html/\*.jpg
path_group APACHE_READABLE_FILES /var/www/html/\*.png
path_group APACHE_READABLE_FILES /var/www/html/\{\*\}/\*.html
path_group APACHE_READABLE_FILES /var/www/html/\{\*\}/\*.jpg
path_group APACHE_READABLE_FILES /var/www/html/\{\*\}/\*.png
+
allow_read @APACHE_READABLE_FILES
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
security/tomoyo/path_group.c | 210 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 210 insertions(+)
--- /dev/null
+++ security-testing-2.6/security/tomoyo/path_group.c
@@ -0,0 +1,210 @@
+/*
+ * security/tomoyo/path_group.c
+ *
+ * Copyright (C) 2005-2009 NTT DATA CORPORATION
+ */
+#include "internal.h"
+
+/* The list for "struct tomoyo_path_group". */
+LIST_HEAD(tomoyo_path_group_list);
+
+/**
+ * tomoyo_get_path_group - Allocate memory for "struct tomoyo_path_group".
+ *
+ * @group_name: The name of pathname group.
+ *
+ * Returns pointer to "struct tomoyo_path_group" on success, NULL otherwise.
+ */
+struct tomoyo_path_group *tomoyo_get_path_group(const char *group_name)
+{
+ struct tomoyo_path_group *entry = NULL;
+ struct tomoyo_path_group *group;
+ const struct tomoyo_path_info *saved_group_name;
+ int error = -ENOMEM;
+ if (!tomoyo_is_correct_path(group_name, 0, 0, 0) ||
+ !group_name[0])
+ return NULL;
+ saved_group_name = tomoyo_get_name(group_name);
+ if (!saved_group_name)
+ return NULL;
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ mutex_lock(&tomoyo_policy_lock);
+ list_for_each_entry_rcu(group, &tomoyo_path_group_list, list) {
+ if (saved_group_name != group->group_name)
+ continue;
+ atomic_inc(&group->users);
+ error = 0;
+ break;
+ }
+ if (error && tomoyo_memory_ok(entry, sizeof(*entry))) {
+ INIT_LIST_HEAD(&entry->member_list);
+ entry->group_name = saved_group_name;
+ saved_group_name = NULL;
+ atomic_set(&entry->users, 1);
+ list_add_tail_rcu(&entry->list, &tomoyo_path_group_list);
+ group = entry;
+ entry = NULL;
+ error = 0;
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+ tomoyo_put_name(saved_group_name);
+ kfree(entry);
+ return !error ? group : NULL;
+}
+
+/**
+ * tomoyo_put_path_group - Delete memory for "struct tomoyo_path_group".
+ *
+ * @group: Pointer to "struct tomoyo_path_group".
+ */
+void tomoyo_put_path_group(struct tomoyo_path_group *group)
+{
+ struct tomoyo_path_group_member *member;
+ struct tomoyo_path_group_member *next_member;
+ LIST_HEAD(q);
+ bool can_delete_group = false;
+ if (!group)
+ return;
+ mutex_lock(&tomoyo_policy_lock);
+ if (atomic_dec_and_test(&group->users)) {
+ list_for_each_entry_safe(member, next_member,
+ &group->member_list, list) {
+ if (!member->is_deleted)
+ break;
+ list_del(&member->list);
+ list_add(&member->list, &q);
+ }
+ if (list_empty(&group->member_list)) {
+ list_del(&group->list);
+ can_delete_group = true;
+ }
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+ list_for_each_entry_safe(member, next_member, &q, list) {
+ list_del(&member->list);
+ tomoyo_put_name(member->member_name);
+ tomoyo_memory_free(member, sizeof(*member));
+ }
+ if (can_delete_group) {
+ tomoyo_put_name(group->group_name);
+ tomoyo_memory_free(group, sizeof(*group));
+ }
+}
+
+/**
+ * tomoyo_write_path_group_policy - Write "struct tomoyo_path_group" list.
+ *
+ * @data: String to parse.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, nagative value otherwise.
+ */
+int tomoyo_write_path_group_policy(char *data, const bool is_delete)
+{
+ struct tomoyo_path_group *group;
+ struct tomoyo_path_group_member *entry = NULL;
+ struct tomoyo_path_group_member *member;
+ struct tomoyo_path_group_member e = { };
+ int error = is_delete ? -ENOENT : -ENOMEM;
+ char *w[2];
+ if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[1][0])
+ return -EINVAL;
+ group = tomoyo_get_path_group(w[0]);
+ if (!group)
+ return -ENOMEM;
+ e.member_name = tomoyo_get_name(w[1]);
+ if (!e.member_name)
+ goto out;
+ if (!is_delete)
+ entry = kmalloc(sizeof(e), GFP_KERNEL);
+ mutex_lock(&tomoyo_policy_lock);
+ list_for_each_entry_rcu(member, &group->member_list, list) {
+ if (member->member_name != e.member_name)
+ continue;
+ member->is_deleted = is_delete;
+ error = 0;
+ break;
+ }
+ if (!is_delete && error && tomoyo_commit_ok(entry, &e, sizeof(e))) {
+ list_add_tail_rcu(&entry->list, &group->member_list);
+ entry = NULL;
+ error = 0;
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+ out:
+ tomoyo_put_name(e.member_name);
+ tomoyo_put_path_group(group);
+ kfree(entry);
+ return error;
+}
+
+/**
+ * tomoyo_read_path_group_policy - Read "struct tomoyo_path_group" list.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+bool tomoyo_read_path_group_policy(struct tomoyo_io_buffer *head)
+{
+ struct list_head *gpos;
+ struct list_head *mpos;
+ bool done = true;
+ list_for_each_cookie(gpos, head->read_var1, &tomoyo_path_group_list) {
+ struct tomoyo_path_group *group;
+ group = list_entry(gpos, struct tomoyo_path_group, list);
+ list_for_each_cookie(mpos, head->read_var2,
+ &group->member_list) {
+ struct tomoyo_path_group_member *member;
+ member = list_entry(mpos,
+ struct tomoyo_path_group_member,
+ list);
+ if (member->is_deleted)
+ continue;
+ done = tomoyo_io_printf(head, TOMOYO_KEYWORD_PATH_GROUP
+ "%s %s\n",
+ group->group_name->name,
+ member->member_name->name);
+ if (!done)
+ break;
+ }
+ }
+ return done;
+}
+
+/**
+ * tomoyo_path_matches_group - Check whether the given pathname matches members of the given pathname group.
+ *
+ * @pathname: The name of pathname.
+ * @group: Pointer to "struct tomoyo_path_group".
+ * @may_use_pattern: True if wild card is permitted.
+ *
+ * Returns true if @pathname matches pathnames in @group, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+bool tomoyo_path_matches_group(const struct tomoyo_path_info *pathname,
+ const struct tomoyo_path_group *group,
+ const bool may_use_pattern)
+{
+ struct tomoyo_path_group_member *member;
+ bool matched = false;
+ list_for_each_entry_rcu(member, &group->member_list, list) {
+ if (member->is_deleted)
+ continue;
+ if (!member->member_name->is_patterned) {
+ if (tomoyo_pathcmp(pathname, member->member_name))
+ continue;
+ } else if (may_use_pattern) {
+ if (!tomoyo_path_matches_pattern(pathname,
+ member->member_name))
+ continue;
+ } else
+ continue;
+ matched = true;
+ break;
+ }
+ return matched;
+}
--
^ permalink raw reply [flat|nested] 50+ messages in thread
* [TOMOYO #16 08/25] TOMOYO: Add number_group keyword support.
2009-10-04 12:49 [TOMOYO #16 00/25] Starting TOMOYO 2.3 Tetsuo Handa
` (6 preceding siblings ...)
2009-10-04 12:49 ` [TOMOYO #16 07/25] TOMOYO: Add path_group keyword support Tetsuo Handa
@ 2009-10-04 12:49 ` Tetsuo Handa
2009-10-04 12:49 ` [TOMOYO #16 09/25] TOMOYO: Add address_group " Tetsuo Handa
` (17 subsequent siblings)
25 siblings, 0 replies; 50+ messages in thread
From: Tetsuo Handa @ 2009-10-04 12:49 UTC (permalink / raw)
To: linux-security-module; +Cc: linux-kernel, Tetsuo Handa
[-- Attachment #1: tomoyo-add-number_group.patch --]
[-- Type: text/plain, Size: 6893 bytes --]
This patch contains code for handling numeric values grouping.
To be able to specify value ranges more flexibly, I want to add numeric values
grouping support into TOMOYO 2.3.0.
number_group UIDS_ALLOWED_TO_EXECUTE_SHELL 100-2000
number_group UIDS_ALLOWED_TO_EXECUTE_SHELL 10000-20000
+
allow_execute /bin/sh if task.uid=@UIDS_ALLOWED_TO_EXECUTE_SHELL
number_group PERMISSIONS_FOR_CREATE 0644
number_group PERMISSIONS_FOR_CREATE 0600
+
allow_create /tmp/file @PERMISSIONS_FOR_CREATE
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
security/tomoyo/number_group.c | 212 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 212 insertions(+)
--- /dev/null
+++ security-testing-2.6/security/tomoyo/number_group.c
@@ -0,0 +1,212 @@
+/*
+ * security/tomoyo/number_group.c
+ *
+ * Copyright (C) 2005-2009 NTT DATA CORPORATION
+ */
+#include "internal.h"
+
+/* The list for "struct tomoyo_number_group". */
+LIST_HEAD(tomoyo_number_group_list);
+
+/**
+ * tomoyo_get_number_group - Allocate memory for "struct tomoyo_number_group".
+ *
+ * @group_name: The name of number group.
+ *
+ * Returns pointer to "struct tomoyo_number_group" on success,
+ * NULL otherwise.
+ */
+struct tomoyo_number_group *tomoyo_get_number_group(const char *group_name)
+{
+ struct tomoyo_number_group *entry = NULL;
+ struct tomoyo_number_group *group;
+ const struct tomoyo_path_info *saved_group_name;
+ int error = -ENOMEM;
+ if (!tomoyo_is_correct_path(group_name, 0, 0, 0) ||
+ !group_name[0])
+ return NULL;
+ saved_group_name = tomoyo_get_name(group_name);
+ if (!saved_group_name)
+ return NULL;
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ mutex_lock(&tomoyo_policy_lock);
+ list_for_each_entry_rcu(group, &tomoyo_number_group_list, list) {
+ if (saved_group_name != group->group_name)
+ continue;
+ atomic_inc(&group->users);
+ error = 0;
+ break;
+ }
+ if (error && tomoyo_memory_ok(entry, sizeof(*entry))) {
+ INIT_LIST_HEAD(&entry->member_list);
+ entry->group_name = saved_group_name;
+ saved_group_name = NULL;
+ atomic_set(&entry->users, 1);
+ list_add_tail_rcu(&entry->list, &tomoyo_number_group_list);
+ group = entry;
+ entry = NULL;
+ error = 0;
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+ tomoyo_put_name(saved_group_name);
+ kfree(entry);
+ return !error ? group : NULL;
+}
+
+/**
+ * tomoyo_put_number_group - Delete memory for "struct tomoyo_number_group".
+ *
+ * @group: Pointer to "struct tomoyo_number_group".
+ */
+void tomoyo_put_number_group(struct tomoyo_number_group *group)
+{
+ struct tomoyo_number_group_member *member;
+ struct tomoyo_number_group_member *next_member;
+ LIST_HEAD(q);
+ bool can_delete_group = false;
+ if (!group)
+ return;
+ mutex_lock(&tomoyo_policy_lock);
+ if (atomic_dec_and_test(&group->users)) {
+ list_for_each_entry_safe(member, next_member,
+ &group->member_list, list) {
+ if (!member->is_deleted)
+ break;
+ list_del(&member->list);
+ list_add(&member->list, &q);
+ }
+ if (list_empty(&group->member_list)) {
+ list_del(&group->list);
+ can_delete_group = true;
+ }
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+ list_for_each_entry_safe(member, next_member, &q, list) {
+ list_del(&member->list);
+ tomoyo_memory_free(member, sizeof(*member));
+ }
+ if (can_delete_group) {
+ tomoyo_put_name(group->group_name);
+ tomoyo_memory_free(group, sizeof(*group));
+ }
+}
+
+/**
+ * tomoyo_write_number_group_policy - Write "struct tomoyo_number_group" list.
+ *
+ * @data: String to parse.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, nagative value otherwise.
+ */
+int tomoyo_write_number_group_policy(char *data, const bool is_delete)
+{
+ struct tomoyo_number_group *group;
+ struct tomoyo_number_group_member *entry = NULL;
+ struct tomoyo_number_group_member e = { };
+ struct tomoyo_number_group_member *member;
+ int error = is_delete ? -ENOENT : -ENOMEM;
+ char *w[2];
+ if (!tomoyo_tokenize(data, w, sizeof(w)))
+ return -EINVAL;
+ if (!tomoyo_parse_number_union(w[1], &e.number))
+ return -EINVAL;
+ if (e.number.is_group || e.number.values[0] > e.number.values[1]) {
+ tomoyo_put_number_union(&e.number);
+ return -EINVAL;
+ }
+ group = tomoyo_get_number_group(w[0]);
+ if (!group)
+ return -ENOMEM;
+ if (!is_delete)
+ entry = kmalloc(sizeof(e), GFP_KERNEL);
+ mutex_lock(&tomoyo_policy_lock);
+ list_for_each_entry_rcu(member, &group->member_list, list) {
+ if (memcmp(&member->number, &e.number, sizeof(e.number)))
+ continue;
+ member->is_deleted = is_delete;
+ error = 0;
+ break;
+ }
+ if (!is_delete && error && tomoyo_commit_ok(entry, &e, sizeof(e))) {
+ list_add_tail_rcu(&entry->list, &group->member_list);
+ entry = NULL;
+ error = 0;
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+ tomoyo_put_number_group(group);
+ kfree(entry);
+ return error;
+}
+
+/**
+ * tomoyo_read_number_group_policy - Read "struct tomoyo_number_group" list.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+bool tomoyo_read_number_group_policy(struct tomoyo_io_buffer *head)
+{
+ struct list_head *gpos;
+ struct list_head *mpos;
+ bool done = true;
+ list_for_each_cookie(gpos, head->read_var1,
+ &tomoyo_number_group_list) {
+ struct tomoyo_number_group *group;
+ const char *name;
+ group = list_entry(gpos, struct tomoyo_number_group, list);
+ name = group->group_name->name;
+ list_for_each_cookie(mpos, head->read_var2,
+ &group->member_list) {
+ int pos;
+ const struct tomoyo_number_group_member *member
+ = list_entry(mpos,
+ struct tomoyo_number_group_member,
+ list);
+ if (member->is_deleted)
+ continue;
+ pos = head->read_avail;
+ if (!tomoyo_io_printf(head, TOMOYO_KEYWORD_NUMBER_GROUP
+ "%s", name) ||
+ !tomoyo_print_number_union(head, &member->number)
+ || !tomoyo_io_printf(head, "\n")) {
+ head->read_avail = pos;
+ done = false;
+ break;
+ }
+ }
+ }
+ return done;
+}
+
+/**
+ * tomoyo_number_matches_group - Check whether the given number matches members of the given number group.
+ *
+ * @min: Min number.
+ * @max: Max number.
+ * @group: Pointer to "struct tomoyo_number_group".
+ *
+ * Returns true if @min and @max partially overlaps @group, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+bool tomoyo_number_matches_group(const unsigned long min,
+ const unsigned long max,
+ const struct tomoyo_number_group *group)
+{
+ struct tomoyo_number_group_member *member;
+ bool matched = false;
+ list_for_each_entry_rcu(member, &group->member_list, list) {
+ if (member->is_deleted)
+ continue;
+ if (min > member->number.values[1] ||
+ max < member->number.values[0])
+ continue;
+ matched = true;
+ break;
+ }
+ return matched;
+}
--
^ permalink raw reply [flat|nested] 50+ messages in thread
* [TOMOYO #16 09/25] TOMOYO: Add address_group keyword support.
2009-10-04 12:49 [TOMOYO #16 00/25] Starting TOMOYO 2.3 Tetsuo Handa
` (7 preceding siblings ...)
2009-10-04 12:49 ` [TOMOYO #16 08/25] TOMOYO: Add number_group " Tetsuo Handa
@ 2009-10-04 12:49 ` Tetsuo Handa
2009-10-04 12:49 ` [TOMOYO #16 10/25] TOMOYO: Add conditional ACL support Tetsuo Handa
` (16 subsequent siblings)
25 siblings, 0 replies; 50+ messages in thread
From: Tetsuo Handa @ 2009-10-04 12:49 UTC (permalink / raw)
To: linux-security-module; +Cc: linux-kernel, Tetsuo Handa
[-- Attachment #1: tomoyo-add-address_group.patch --]
[-- Type: text/plain, Size: 8501 bytes --]
This patch contains code for handling IPv4/IPv6 address grouping.
To be able to specify IP addresses more flexibly,
I want to add IP address grouping support into TOMOYO 2.3.0.
address_group TRUSTED_CLIENTS 192.168.1.1
address_group TRUSTED_CLIENTS 127.0.0.1
address_group TRUSTED_CLIENTS 0:0:0:0:0:0:0:1
address_group UNTRUSTED_CLIENTS 192.168.2.1-192.168.2.255
+
allow_network TCP accept @TRUSTED_CLIENTS 1024-65535 ; set task.state[0]=1
allow_network TCP accept @UNTRUSTED_CLIENTS 1024-65535 ; set task.state[0]=0
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
security/tomoyo/address_group.c | 270 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 270 insertions(+)
--- /dev/null
+++ security-testing-2.6/security/tomoyo/address_group.c
@@ -0,0 +1,270 @@
+/*
+ * security/tomoyo/address_group.c
+ *
+ * Copyright (C) 2005-2009 NTT DATA CORPORATION
+ */
+#include "internal.h"
+
+/* The list for "struct tomoyo_address_group". */
+LIST_HEAD(tomoyo_address_group_list);
+
+/**
+ * tomoyo_get_address_group - Allocate memory for "struct tomoyo_address_group".
+ *
+ * @group_name: The name of address group.
+ *
+ * Returns pointer to "struct tomoyo_address_group" on success,
+ * NULL otherwise.
+ */
+struct tomoyo_address_group *tomoyo_get_address_group(const char *group_name)
+{
+ struct tomoyo_address_group *entry = NULL;
+ struct tomoyo_address_group *group;
+ const struct tomoyo_path_info *saved_group_name;
+ int error = -ENOMEM;
+ if (!tomoyo_is_correct_path(group_name, 0, 0, 0) ||
+ !group_name[0])
+ return NULL;
+ saved_group_name = tomoyo_get_name(group_name);
+ if (!saved_group_name)
+ return NULL;
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ mutex_lock(&tomoyo_policy_lock);
+ list_for_each_entry_rcu(group, &tomoyo_address_group_list, list) {
+ if (saved_group_name != group->group_name)
+ continue;
+ atomic_inc(&group->users);
+ error = 0;
+ break;
+ }
+ if (error && tomoyo_memory_ok(entry, sizeof(*entry))) {
+ INIT_LIST_HEAD(&entry->member_list);
+ entry->group_name = saved_group_name;
+ saved_group_name = NULL;
+ atomic_set(&entry->users, 1);
+ list_add_tail_rcu(&entry->list, &tomoyo_address_group_list);
+ group = entry;
+ entry = NULL;
+ error = 0;
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+ tomoyo_put_name(saved_group_name);
+ kfree(entry);
+ return !error ? group : NULL;
+}
+
+/**
+ * tomoyo_put_address_group - Delete memory for "struct tomoyo_address_group".
+ *
+ * @group: Pointer to "struct tomoyo_address_group".
+ */
+void tomoyo_put_address_group(struct tomoyo_address_group *group)
+{
+ struct tomoyo_address_group_member *member;
+ struct tomoyo_address_group_member *next_member;
+ LIST_HEAD(q);
+ bool can_delete_group = false;
+ if (!group)
+ return;
+ mutex_lock(&tomoyo_policy_lock);
+ if (atomic_dec_and_test(&group->users)) {
+ list_for_each_entry_safe(member, next_member,
+ &group->member_list, list) {
+ if (!member->is_deleted)
+ break;
+ list_del(&member->list);
+ list_add(&member->list, &q);
+ }
+ if (list_empty(&group->member_list)) {
+ list_del(&group->list);
+ can_delete_group = true;
+ }
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+ list_for_each_entry_safe(member, next_member, &q, list) {
+ list_del(&member->list);
+ if (member->is_ipv6) {
+ tomoyo_put_ipv6_address(member->min.ipv6);
+ tomoyo_put_ipv6_address(member->max.ipv6);
+ }
+ tomoyo_memory_free(member, sizeof(*member));
+ }
+ if (can_delete_group) {
+ tomoyo_put_name(group->group_name);
+ tomoyo_memory_free(group, sizeof(*group));
+ }
+}
+
+/**
+ * tomoyo_write_address_group_policy - Write "struct tomoyo_address_group" list.
+ *
+ * @data: String to parse.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_write_address_group_policy(char *data, const bool is_delete)
+{
+ struct tomoyo_address_group *group;
+ struct tomoyo_address_group_member *entry = NULL;
+ struct tomoyo_address_group_member *member;
+ struct tomoyo_address_group_member e = { };
+ int error = is_delete ? -ENOENT : -ENOMEM;
+ u16 min_address[8];
+ u16 max_address[8];
+ char *w[2];
+ if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[1][0])
+ return -EINVAL;
+ group = tomoyo_get_address_group(w[0]);
+ if (!group)
+ return -ENOMEM;
+ switch (tomoyo_parse_ip_address(w[1], min_address, max_address)) {
+ case 2:
+ e.is_ipv6 = true;
+ e.min.ipv6 = tomoyo_get_ipv6_address((struct in6_addr *)
+ min_address);
+ e.max.ipv6 = tomoyo_get_ipv6_address((struct in6_addr *)
+ max_address);
+ if (!e.min.ipv6 || !e.max.ipv6)
+ goto out;
+ break;
+ case 1:
+ e.min.ipv4 = ntohl(*(u32 *) min_address);
+ e.max.ipv4 = ntohl(*(u32 *) max_address);
+ break;
+ default:
+ goto out;
+ }
+ if (!is_delete)
+ entry = kmalloc(sizeof(e), GFP_KERNEL);
+ mutex_lock(&tomoyo_policy_lock);
+ list_for_each_entry_rcu(member, &group->member_list, list) {
+ if (tomoyo_memcmp(member, &e, offsetof(typeof(e), is_ipv6),
+ sizeof(e)))
+ continue;
+ member->is_deleted = is_delete;
+ error = 0;
+ break;
+ }
+ if (!is_delete && error && tomoyo_commit_ok(entry, &e, sizeof(e))) {
+ list_add_tail_rcu(&entry->list, &group->member_list);
+ entry = NULL;
+ error = 0;
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+ out:
+ if (e.is_ipv6) {
+ tomoyo_put_ipv6_address(e.min.ipv6);
+ tomoyo_put_ipv6_address(e.max.ipv6);
+ }
+ tomoyo_put_address_group(group);
+ return error;
+}
+
+/**
+ * tomoyo_read_address_group_policy - Read "struct tomoyo_address_group" list.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+bool tomoyo_read_address_group_policy(struct tomoyo_io_buffer *head)
+{
+ struct list_head *gpos;
+ struct list_head *mpos;
+ bool done = true;
+ list_for_each_cookie(gpos, head->read_var1,
+ &tomoyo_address_group_list) {
+ struct tomoyo_address_group *group;
+ group = list_entry(gpos, struct tomoyo_address_group, list);
+ list_for_each_cookie(mpos, head->read_var2,
+ &group->member_list) {
+ char buf[128];
+ struct tomoyo_address_group_member *member;
+ member = list_entry(mpos,
+ struct tomoyo_address_group_member,
+ list);
+ if (member->is_deleted)
+ continue;
+ if (member->is_ipv6) {
+ const struct in6_addr *min_address
+ = member->min.ipv6;
+ const struct in6_addr *max_address
+ = member->max.ipv6;
+ tomoyo_print_ipv6(buf, sizeof(buf),
+ min_address);
+ if (min_address != max_address) {
+ int len;
+ char *cp = buf + strlen(buf);
+ *cp++ = '-';
+ len = strlen(buf);
+ tomoyo_print_ipv6(cp,
+ sizeof(buf) - len,
+ max_address);
+ }
+ } else {
+ const u32 min_address = member->min.ipv4;
+ const u32 max_address = member->max.ipv4;
+ memset(buf, 0, sizeof(buf));
+ snprintf(buf, sizeof(buf) - 1, "%u.%u.%u.%u",
+ HIPQUAD(min_address));
+ if (min_address != max_address) {
+ const int len = strlen(buf);
+ snprintf(buf + len,
+ sizeof(buf) - 1 - len,
+ "-%u.%u.%u.%u",
+ HIPQUAD(max_address));
+ }
+ }
+ done = tomoyo_io_printf(head,
+ TOMOYO_KEYWORD_ADDRESS_GROUP
+ "%s %s\n",
+ group->group_name->name, buf);
+ if (!done)
+ break;
+ }
+ if (!done)
+ break;
+ }
+ return done;
+}
+
+/**
+ * tomoyo_address_matches_group - Check whether the given address matches members of the given address group.
+ *
+ * @is_ipv6: True if @address is an IPv6 address.
+ * @address: An IPv4 or IPv6 address.
+ * @group: Pointer to "struct tomoyo_address_group".
+ *
+ * Returns true if @address matches addresses in @group group, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+bool tomoyo_address_matches_group(const bool is_ipv6, const u32 *address,
+ const struct tomoyo_address_group *group)
+{
+ struct tomoyo_address_group_member *member;
+ const u32 ip = ntohl(*address);
+ bool matched = false;
+ list_for_each_entry_rcu(member, &group->member_list, list) {
+ if (member->is_deleted)
+ continue;
+ if (member->is_ipv6) {
+ if (is_ipv6 &&
+ memcmp(member->min.ipv6, address, 16) <= 0 &&
+ memcmp(address, member->max.ipv6, 16) <= 0) {
+ matched = true;
+ break;
+ }
+ } else {
+ if (!is_ipv6 &&
+ member->min.ipv4 <= ip && ip <= member->max.ipv4) {
+ matched = true;
+ break;
+ }
+ }
+ }
+ return matched;
+}
--
^ permalink raw reply [flat|nested] 50+ messages in thread
* [TOMOYO #16 10/25] TOMOYO: Add conditional ACL support.
2009-10-04 12:49 [TOMOYO #16 00/25] Starting TOMOYO 2.3 Tetsuo Handa
` (8 preceding siblings ...)
2009-10-04 12:49 ` [TOMOYO #16 09/25] TOMOYO: Add address_group " Tetsuo Handa
@ 2009-10-04 12:49 ` Tetsuo Handa
2009-10-04 12:49 ` [TOMOYO #16 11/25] TOMOYO: Add auditing support Tetsuo Handa
` (15 subsequent siblings)
25 siblings, 0 replies; 50+ messages in thread
From: Tetsuo Handa @ 2009-10-04 12:49 UTC (permalink / raw)
To: linux-security-module; +Cc: linux-kernel, Tetsuo Handa
[-- Attachment #1: tomoyo-add-condition.patch --]
[-- Type: text/plain, Size: 35451 bytes --]
This patch contains code for handling "if" clause of a permission.
TOMOYO is a pathname based access control system, but also deals some of non
pathname attributes.
TOMOYO supports conditional permissions based on process's UID/GID etc. and/or
requested pathname's UID/GID.
Regarding symlink(), you can check symlink's contents.
Regarding execve(), you can check argv[]/envp[] conditions.
If you need to examine argv[]/envp[] more deeply, you can use execute handler.
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
security/tomoyo/condition.c | 1332 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 1332 insertions(+)
--- /dev/null
+++ security-testing-2.6/security/tomoyo/condition.c
@@ -0,0 +1,1332 @@
+/*
+ * security/tomoyo/condition.c
+ *
+ * Copyright (C) 2005-2009 NTT DATA CORPORATION
+ */
+#include "internal.h"
+
+/**
+ * tomoyo_argv - Check argv[] in "struct linux_binbrm".
+ *
+ * @index: Index number of @arg_ptr.
+ * @arg_ptr: Contents of argv[@index].
+ * @argc: Length of @argv.
+ * @argv: Pointer to "struct tomoyo_argv_entry".
+ * @checked: Set to true if @argv[@index] was found.
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_argv(const unsigned int index, const char *arg_ptr,
+ const int argc, const struct tomoyo_argv_entry *argv,
+ u8 *checked)
+{
+ int i;
+ struct tomoyo_path_info arg;
+ arg.name = arg_ptr;
+ for (i = 0; i < argc; argv++, checked++, i++) {
+ bool result;
+ if (index != argv->index)
+ continue;
+ *checked = 1;
+ tomoyo_fill_path_info(&arg);
+ result = tomoyo_path_matches_pattern(&arg, argv->value);
+ if (argv->is_not)
+ result = !result;
+ if (!result)
+ return false;
+ }
+ return true;
+}
+
+/**
+ * tomoyo_envp - Check envp[] in "struct linux_binbrm".
+ *
+ * @env_name: The name of environment variable.
+ * @env_value: The value of environment variable.
+ * @envc: Length of @envp.
+ * @envp: Pointer to "struct tomoyo_envp_entry".
+ * @checked: Set to true if @envp[@env_name] was found.
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_envp(const char *env_name, const char *env_value,
+ const int envc, const struct tomoyo_envp_entry *envp,
+ u8 *checked)
+{
+ int i;
+ struct tomoyo_path_info name;
+ struct tomoyo_path_info value;
+ name.name = env_name;
+ tomoyo_fill_path_info(&name);
+ value.name = env_value;
+ tomoyo_fill_path_info(&value);
+ for (i = 0; i < envc; envp++, checked++, i++) {
+ bool result;
+ if (!tomoyo_path_matches_pattern(&name, envp->name))
+ continue;
+ *checked = 1;
+ if (envp->value) {
+ result = tomoyo_path_matches_pattern(&value,
+ envp->value);
+ if (envp->is_not)
+ result = !result;
+ } else {
+ result = true;
+ if (!envp->is_not)
+ result = !result;
+ }
+ if (!result)
+ return false;
+ }
+ return true;
+}
+
+/**
+ * tomoyo_scan_bprm - Scan "struct linux_binprm".
+ *
+ * @ee: Pointer to "struct tomoyo_execve_entry".
+ * @argc: Length of @argc.
+ * @argv: Pointer to "struct tomoyo_argv_entry".
+ * @envc: Length of @envp.
+ * @envp: Poiner to "struct tomoyo_envp_entry".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_scan_bprm(struct tomoyo_execve_entry *ee, const u16 argc,
+ const struct tomoyo_argv_entry *argv,
+ const u16 envc,
+ const struct tomoyo_envp_entry *envp)
+{
+ /*
+ if exec.argc=3
+ if (argc == 3)
+ if exec.argv[1]="-c"
+ if (argc >= 2 && !strcmp(argv[1], "-c"))
+ if exec.argv[1]!="-c"
+ if (argc < 2 || strcmp(argv[1], "-c"))
+ if exec.envc=10-20
+ if (envc >= 10 && envc <= 20)
+ if exec.envc!=10-20
+ if (envc < 10 || envc > 20)
+ if exec.envp["HOME"]!=NULL
+ if (getenv("HOME"))
+ if exec.envp["HOME"]=NULL
+ if (!getenv("HOME"))
+ if exec.envp["HOME"]="/"
+ if (getenv("HOME") && !strcmp(getenv("HOME"), "/"))
+ if exec.envp["HOME"]!="/"
+ if (!getenv("HOME") || strcmp(getenv("HOME", "/"))
+ */
+ struct linux_binprm *bprm = ee->bprm;
+ struct tomoyo_page_dump *dump = &ee->dump;
+ char *arg_ptr = ee->tmp;
+ int arg_len = 0;
+ unsigned long pos = bprm->p;
+ int offset = pos % PAGE_SIZE;
+ int argv_count = bprm->argc;
+ int envp_count = bprm->envc;
+ bool result = true;
+ u8 local_checked[32];
+ u8 *checked;
+ if (argc + envc <= sizeof(local_checked)) {
+ checked = local_checked;
+ memset(local_checked, 0, sizeof(local_checked));
+ } else {
+ checked = kzalloc(argc + envc, GFP_KERNEL);
+ if (!checked)
+ return false;
+ }
+ while (argv_count || envp_count) {
+ if (!tomoyo_dump_page(bprm, pos, dump)) {
+ result = false;
+ goto out;
+ }
+ pos += PAGE_SIZE - offset;
+ while (offset < PAGE_SIZE) {
+ /* Read. */
+ struct tomoyo_path_info arg;
+ const char *kaddr = dump->data;
+ const unsigned char c = kaddr[offset++];
+ arg.name = arg_ptr;
+ if (c && arg_len < TOMOYO_EXEC_TMPSIZE - 10) {
+ if (c == '\\') {
+ arg_ptr[arg_len++] = '\\';
+ arg_ptr[arg_len++] = '\\';
+ } else if (c > ' ' && c < 127) {
+ arg_ptr[arg_len++] = c;
+ } else {
+ arg_ptr[arg_len++] = '\\';
+ arg_ptr[arg_len++] = (c >> 6) + '0';
+ arg_ptr[arg_len++] =
+ ((c >> 3) & 7) + '0';
+ arg_ptr[arg_len++] = (c & 7) + '0';
+ }
+ } else {
+ arg_ptr[arg_len] = '\0';
+ }
+ if (c)
+ continue;
+ /* Check. */
+ if (argv_count) {
+ if (!tomoyo_argv(bprm->argc - argv_count,
+ arg_ptr, argc, argv,
+ checked)) {
+ result = false;
+ break;
+ }
+ argv_count--;
+ } else if (envp_count) {
+ char *cp = strchr(arg_ptr, '=');
+ if (cp) {
+ *cp = '\0';
+ if (!tomoyo_envp(arg_ptr, cp + 1,
+ envc, envp,
+ checked + argc)) {
+ result = false;
+ break;
+ }
+ }
+ envp_count--;
+ } else {
+ break;
+ }
+ arg_len = 0;
+ }
+ offset = 0;
+ if (!result)
+ break;
+ }
+ out:
+ if (result) {
+ int i;
+ /* Check not-yet-checked entries. */
+ for (i = 0; i < argc; i++) {
+ if (checked[i])
+ continue;
+ /*
+ * Return true only if all unchecked indexes in
+ * bprm->argv[] are not matched.
+ */
+ if (argv[i].is_not)
+ continue;
+ result = false;
+ break;
+ }
+ for (i = 0; i < envc; envp++, i++) {
+ if (checked[argc + i])
+ continue;
+ /*
+ * Return true only if all unchecked environ variables
+ * in bprm->envp[] are either undefined or not matched.
+ */
+ if ((!envp->value && !envp->is_not) ||
+ (envp->value && envp->is_not))
+ continue;
+ result = false;
+ break;
+ }
+ }
+ if (checked != local_checked)
+ kfree(checked);
+ return result;
+}
+
+static bool tomoyo_scan_exec_realpath(struct file *file,
+ const struct tomoyo_name_union *ptr,
+ const bool match)
+{
+ bool result;
+ struct tomoyo_path_info exe;
+ if (!file)
+ return false;
+ exe.name = tomoyo_realpath_from_path(&file->f_path);
+ if (!exe.name)
+ return false;
+ tomoyo_fill_path_info(&exe);
+ result = tomoyo_compare_name_union(&exe, ptr);
+ kfree(exe.name);
+ return result == match;
+}
+
+static bool tomoyo_parse_name_union_quoted(char *filename,
+ struct tomoyo_name_union *ptr)
+{
+ bool result;
+ char *cp = NULL;
+ if (*filename == '"') {
+ cp = filename + strlen(filename) - 1;
+ if (*cp != '"')
+ return false;
+ *cp = '\0';
+ filename++;
+ }
+ result = tomoyo_parse_name_union(filename, ptr);
+ if (cp)
+ *cp = '"';
+ return result;
+}
+
+/**
+ * tomoyo_get_dqword - tomoyo_get_name() for a quoted string.
+ *
+ * @start: String to save.
+ *
+ * Returns pointer to "struct tomoyo_path_info" on success, NULL otherwise.
+ */
+static const struct tomoyo_path_info *tomoyo_get_dqword(char *start)
+{
+ char *cp;
+ if (*start++ != '"')
+ return NULL;
+ cp = start;
+ while (1) {
+ const char c = *cp++;
+ if (!c)
+ return NULL;
+ if (c != '"' || *cp)
+ continue;
+ *(cp - 1) = '\0';
+ break;
+ }
+ if (!tomoyo_is_correct_path(start, 0, 0, 0))
+ return NULL;
+ return tomoyo_get_name(start);
+}
+
+/**
+ * tomoyo_parse_argv - Parse an argv[] condition part.
+ *
+ * @start: String to parse.
+ * @argv: Pointer to "struct tomoyo_argv_entry".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_parse_argv(char *start, struct tomoyo_argv_entry *argv)
+{
+ unsigned long index;
+ const struct tomoyo_path_info *value;
+ bool is_not;
+ char c;
+ if (tomoyo_parse_ulong(&index, &start) != TOMOYO_VALUE_TYPE_DECIMAL)
+ goto out;
+ if (*start++ != ']')
+ goto out;
+ c = *start++;
+ if (c == '=')
+ is_not = false;
+ else if (c == '!' && *start++ == '=')
+ is_not = true;
+ else
+ goto out;
+ value = tomoyo_get_dqword(start);
+ if (!value)
+ goto out;
+ argv->index = index;
+ argv->is_not = is_not;
+ argv->value = value;
+ return true;
+ out:
+ return false;
+}
+
+/**
+ * tomoyo_parse_envp - Parse an envp[] condition part.
+ *
+ * @start: String to parse.
+ * @envp: Pointer to "struct tomoyo_envp_entry".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_parse_envp(char *start, struct tomoyo_envp_entry *envp)
+{
+ const struct tomoyo_path_info *name;
+ const struct tomoyo_path_info *value;
+ bool is_not;
+ char *cp = start;
+ /*
+ * Since environment variable names don't
+ * contain '=', I can treat '"]=' and '"]!='
+ * sequences as delimiters.
+ */
+ while (1) {
+ if (!strncmp(start, "\"]=", 3)) {
+ is_not = false;
+ *start = '\0';
+ start += 3;
+ break;
+ } else if (!strncmp(start, "\"]!=", 4)) {
+ is_not = true;
+ *start = '\0';
+ start += 4;
+ break;
+ } else if (!*start++) {
+ goto out;
+ }
+ }
+ if (!*cp || !tomoyo_is_correct_path(cp, 0, 0, 0))
+ goto out;
+ name = tomoyo_get_name(cp);
+ if (!name)
+ goto out;
+ if (!strcmp(start, "NULL")) {
+ value = NULL;
+ } else {
+ value = tomoyo_get_dqword(start);
+ if (!value)
+ goto out;
+ }
+ envp->name = name;
+ envp->is_not = is_not;
+ envp->value = value;
+ return true;
+ out:
+ return false;
+}
+
+/* The list for "struct tomoyo_condition". */
+static LIST_HEAD(tomoyo_condition_list);
+
+const char *tomoyo_condition_keyword[TOMOYO_MAX_CONDITION_KEYWORD] = {
+ [TOMOYO_TASK_UID] = "task.uid",
+ [TOMOYO_TASK_EUID] = "task.euid",
+ [TOMOYO_TASK_SUID] = "task.suid",
+ [TOMOYO_TASK_FSUID] = "task.fsuid",
+ [TOMOYO_TASK_GID] = "task.gid",
+ [TOMOYO_TASK_EGID] = "task.egid",
+ [TOMOYO_TASK_SGID] = "task.sgid",
+ [TOMOYO_TASK_FSGID] = "task.fsgid",
+ [TOMOYO_TASK_PID] = "task.pid",
+ [TOMOYO_TASK_PPID] = "task.ppid",
+ [TOMOYO_EXEC_ARGC] = "exec.argc",
+ [TOMOYO_EXEC_ENVC] = "exec.envc",
+ [TOMOYO_TASK_STATE_0] = "task.state[0]",
+ [TOMOYO_TASK_STATE_1] = "task.state[1]",
+ [TOMOYO_TASK_STATE_2] = "task.state[2]",
+ [TOMOYO_TYPE_IS_SOCKET] = "socket",
+ [TOMOYO_TYPE_IS_SYMLINK] = "symlink",
+ [TOMOYO_TYPE_IS_FILE] = "file",
+ [TOMOYO_TYPE_IS_BLOCK_DEV] = "block",
+ [TOMOYO_TYPE_IS_DIRECTORY] = "directory",
+ [TOMOYO_TYPE_IS_CHAR_DEV] = "char",
+ [TOMOYO_TYPE_IS_FIFO] = "fifo",
+ [TOMOYO_MODE_SETUID] = "setuid",
+ [TOMOYO_MODE_SETGID] = "setgid",
+ [TOMOYO_MODE_STICKY] = "sticky",
+ [TOMOYO_MODE_OWNER_READ] = "owner_read",
+ [TOMOYO_MODE_OWNER_WRITE] = "owner_write",
+ [TOMOYO_MODE_OWNER_EXECUTE] = "owner_execute",
+ [TOMOYO_MODE_GROUP_READ] = "group_read",
+ [TOMOYO_MODE_GROUP_WRITE] = "group_write",
+ [TOMOYO_MODE_GROUP_EXECUTE] = "group_execute",
+ [TOMOYO_MODE_OTHERS_READ] = "others_read",
+ [TOMOYO_MODE_OTHERS_WRITE] = "others_write",
+ [TOMOYO_MODE_OTHERS_EXECUTE] = "others_execute",
+ [TOMOYO_TASK_TYPE] = "task.type",
+ [TOMOYO_TASK_EXECUTE_HANDLER] = "execute_handler",
+ [TOMOYO_EXEC_REALPATH] = "exec.realpath",
+ [TOMOYO_SYMLINK_TARGET] = "symlink.target",
+ [TOMOYO_PATH1_UID] = "path1.uid",
+ [TOMOYO_PATH1_GID] = "path1.gid",
+ [TOMOYO_PATH1_INO] = "path1.ino",
+ [TOMOYO_PATH1_MAJOR] = "path1.major",
+ [TOMOYO_PATH1_MINOR] = "path1.minor",
+ [TOMOYO_PATH1_PERM] = "path1.perm",
+ [TOMOYO_PATH1_TYPE] = "path1.type",
+ [TOMOYO_PATH1_DEV_MAJOR] = "path1.dev_major",
+ [TOMOYO_PATH1_DEV_MINOR] = "path1.dev_minor",
+ [TOMOYO_PATH2_UID] = "path2.uid",
+ [TOMOYO_PATH2_GID] = "path2.gid",
+ [TOMOYO_PATH2_INO] = "path2.ino",
+ [TOMOYO_PATH2_MAJOR] = "path2.major",
+ [TOMOYO_PATH2_MINOR] = "path2.minor",
+ [TOMOYO_PATH2_PERM] = "path2.perm",
+ [TOMOYO_PATH2_TYPE] = "path2.type",
+ [TOMOYO_PATH2_DEV_MAJOR] = "path2.dev_major",
+ [TOMOYO_PATH2_DEV_MINOR] = "path2.dev_minor",
+ [TOMOYO_PATH1_PARENT_UID] = "path1.parent.uid",
+ [TOMOYO_PATH1_PARENT_GID] = "path1.parent.gid",
+ [TOMOYO_PATH1_PARENT_INO] = "path1.parent.ino",
+ [TOMOYO_PATH1_PARENT_PERM] = "path1.parent.perm",
+ [TOMOYO_PATH2_PARENT_UID] = "path2.parent.uid",
+ [TOMOYO_PATH2_PARENT_GID] = "path2.parent.gid",
+ [TOMOYO_PATH2_PARENT_INO] = "path2.parent.ino",
+ [TOMOYO_PATH2_PARENT_PERM] = "path2.parent.perm",
+};
+
+/**
+ * tomoyo_parse_post_condition - Parse post-condition part.
+ *
+ * @condition: String to parse.
+ * @post_state: Buffer to store post-condition part.
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_parse_post_condition(char * const condition,
+ u8 post_state[4])
+{
+ char *start = strstr(condition, "; set ");
+ if (!start)
+ return true;
+ *start = '\0';
+ start += 6;
+ while (1) {
+ int i;
+ unsigned long value;
+ while (*start == ' ')
+ start++;
+ if (!*start)
+ break;
+ if (!strncmp(start, "task.state[0]=", 14))
+ i = 0;
+ else if (!strncmp(start, "task.state[1]=", 14))
+ i = 1;
+ else if (!strncmp(start, "task.state[2]=", 14))
+ i = 2;
+ else
+ goto out;
+ start += 14;
+ if (post_state[3] & (1 << i))
+ goto out;
+ post_state[3] |= 1 << i;
+ if (!tomoyo_parse_ulong(&value, &start) || value > 255)
+ goto out;
+ post_state[i] = (u8) value;
+ }
+ return true;
+ out:
+ return false;
+}
+
+/**
+ * tomoyo_get_condition - Parse condition part.
+ *
+ * @condition: Pointer to string to parse.
+ *
+ * Returns pointer to "struct tomoyo_condition" on success, NULL otherwise.
+ */
+struct tomoyo_condition *tomoyo_get_condition(char * const condition)
+{
+ static const bool debug;
+ char *start = condition;
+ struct tomoyo_condition *entry = NULL;
+ struct tomoyo_condition *ptr;
+ struct tomoyo_condition_element *condp;
+ struct tomoyo_number_union *numbers_p;
+ struct tomoyo_name_union *names_p;
+ struct tomoyo_argv_entry *argv;
+ struct tomoyo_envp_entry *envp;
+ u32 size;
+ u8 i;
+ bool found = false;
+ u16 condc = 0;
+ u16 numbers_count = 0;
+ u16 names_count = 0;
+ u16 argc = 0;
+ u16 envc = 0;
+ u8 post_state[4] = { 0, 0, 0, 0 };
+ char *end_of_string;
+ if (!tomoyo_parse_post_condition(start, post_state))
+ goto out;
+ start = condition;
+ if (!strncmp(start, "if ", 3))
+ start += 3;
+ else if (*start)
+ return NULL;
+ end_of_string = start + strlen(start);
+ while (1) {
+ u8 left;
+ u8 right;
+ char *word = start;
+ char *cp;
+ char *eq;
+ bool is_not = false;
+ if (!*word)
+ break;
+ cp = strchr(start, ' ');
+ if (cp) {
+ *cp = '\0';
+ start = cp + 1;
+ } else {
+ start = "";
+ }
+ if (debug)
+ printk(KERN_WARNING "%u: <%s>\n", __LINE__, word);
+ if (!strncmp(word, "exec.argv[", 10)) {
+ argc++;
+ condc++;
+ continue;
+ } else if (!strncmp(word, "exec.envp[\"", 11)) {
+ envc++;
+ condc++;
+ continue;
+ }
+ eq = strchr(word, '=');
+ if (!eq)
+ goto out;
+ if (eq > word && *(eq - 1) == '!') {
+ is_not = true;
+ eq--;
+ }
+ *eq = '\0';
+ for (left = 0; left < TOMOYO_MAX_CONDITION_KEYWORD; left++) {
+ if (strcmp(word, tomoyo_condition_keyword[left]))
+ continue;
+ break;
+ }
+ if (debug)
+ printk(KERN_WARNING "%u: <%s> left=%u\n", __LINE__,
+ word, left);
+ if (left == TOMOYO_MAX_CONDITION_KEYWORD)
+ numbers_count++;
+ *eq = is_not ? '!' : '=';
+ word = eq + 1;
+ if (is_not)
+ word++;
+ condc++;
+ if (debug)
+ printk(KERN_WARNING "%u: <%s> left=%u\n", __LINE__,
+ word, left);
+ if (left == TOMOYO_EXEC_REALPATH ||
+ left == TOMOYO_SYMLINK_TARGET) {
+ names_count++;
+ continue;
+ }
+ for (right = 0; right < TOMOYO_MAX_CONDITION_KEYWORD;
+ right++) {
+ if (strcmp(word, tomoyo_condition_keyword[right]))
+ continue;
+ break;
+ }
+ if (debug)
+ printk(KERN_WARNING "%u: <%s> right=%u\n", __LINE__,
+ word, right);
+ if (right == TOMOYO_MAX_CONDITION_KEYWORD)
+ numbers_count++;
+ }
+ if (debug)
+ printk(KERN_DEBUG "%u: cond=%u numbers=%u names=%u ac=%u "
+ "ec=%u\n", __LINE__, condc, numbers_count, names_count,
+ argc, envc);
+ size = sizeof(*entry)
+ + condc * sizeof(struct tomoyo_condition_element)
+ + numbers_count * sizeof(struct tomoyo_number_union)
+ + names_count * sizeof(struct tomoyo_name_union)
+ + argc * sizeof(struct tomoyo_argv_entry)
+ + envc * sizeof(struct tomoyo_envp_entry);
+ entry = kzalloc(size, GFP_KERNEL);
+ if (!entry)
+ return NULL;
+ atomic_set(&entry->users, 1);
+ INIT_LIST_HEAD(&entry->list);
+ for (i = 0; i < 4; i++)
+ entry->post_state[i] = post_state[i];
+ entry->condc = condc;
+ entry->numbers_count = numbers_count;
+ entry->names_count = names_count;
+ entry->argc = argc;
+ entry->envc = envc;
+ condp = (struct tomoyo_condition_element *) (entry + 1);
+ numbers_p = (struct tomoyo_number_union *) (condp + condc);
+ names_p = (struct tomoyo_name_union *) (numbers_p + numbers_count);
+ argv = (struct tomoyo_argv_entry *) (names_p + names_count);
+ envp = (struct tomoyo_envp_entry *) (argv + argc);
+ for (start = condition; start < end_of_string; start++)
+ if (!*start)
+ *start = ' ';
+ start = condition;
+ if (!strncmp(start, "if ", 3))
+ start += 3;
+ else if (*start)
+ goto out;
+ while (1) {
+ u8 left;
+ u8 right;
+ char *word = start;
+ char *cp;
+ char *eq;
+ bool is_not = false;
+ if (!*word)
+ break;
+ cp = strchr(start, ' ');
+ if (cp) {
+ *cp = '\0';
+ start = cp + 1;
+ } else {
+ start = "";
+ }
+ if (debug)
+ printk(KERN_WARNING "%u: <%s>\n", __LINE__, word);
+ if (!strncmp(word, "exec.argv[", 10)) {
+ if (!tomoyo_parse_argv(word + 10, argv))
+ goto out;
+ argv++;
+ argc--;
+ condc--;
+ left = TOMOYO_ARGV_ENTRY;
+ right = -1;
+ goto store_value;
+ } else if (!strncmp(word, "exec.envp[\"", 11)) {
+ if (!tomoyo_parse_envp(word + 11, envp))
+ goto out;
+ envp++;
+ envc--;
+ condc--;
+ left = TOMOYO_ENVP_ENTRY;
+ right = -1;
+ goto store_value;
+ }
+ eq = strchr(word, '=');
+ if (!eq) {
+ if (debug)
+ printk(KERN_WARNING "%u: No operator.\n",
+ __LINE__);
+ goto out;
+ }
+ if (eq > word && *(eq - 1) == '!') {
+ is_not = true;
+ eq--;
+ }
+ *eq = '\0';
+ for (left = 0; left < TOMOYO_MAX_CONDITION_KEYWORD; left++) {
+ if (strcmp(word, tomoyo_condition_keyword[left]))
+ continue;
+ break;
+ }
+ if (debug)
+ printk(KERN_WARNING "%u: <%s> left=%u\n", __LINE__,
+ word, left);
+ if (left == TOMOYO_MAX_CONDITION_KEYWORD) {
+ left = TOMOYO_NUMBER_UNION;
+ if (!tomoyo_parse_number_union(word, numbers_p))
+ goto out;
+ if (numbers_p->is_group)
+ goto out;
+ numbers_p++;
+ numbers_count--;
+ }
+ *eq = is_not ? '!' : '=';
+ word = eq + 1;
+ if (is_not)
+ word++;
+ condc--;
+ if (debug)
+ printk(KERN_WARNING "%u: <%s> left=%u\n", __LINE__,
+ word, left);
+ if (left == TOMOYO_EXEC_REALPATH ||
+ left == TOMOYO_SYMLINK_TARGET) {
+ right = TOMOYO_NAME_UNION;
+ if (!tomoyo_parse_name_union_quoted(word, names_p++))
+ goto out;
+ names_count--;
+ goto store_value;
+ }
+ for (right = 0; right < TOMOYO_MAX_CONDITION_KEYWORD;
+ right++) {
+ if (strcmp(word, tomoyo_condition_keyword[right]))
+ continue;
+ break;
+ }
+ if (right == TOMOYO_MAX_CONDITION_KEYWORD) {
+ right = TOMOYO_NUMBER_UNION;
+ if (!tomoyo_parse_number_union(word, numbers_p++))
+ goto out;
+ numbers_count--;
+ }
+ store_value:
+ condp->left = left;
+ condp->right = right;
+ condp->equals = !is_not;
+ if (debug)
+ printk(KERN_WARNING "%u: left=%u right=%u match=%u\n",
+ __LINE__, condp->left, condp->right,
+ condp->equals);
+ condp++;
+ }
+ if (debug) {
+ for (start = condition; start < end_of_string; start++)
+ if (!*start)
+ *start = ' ';
+ printk(KERN_DEBUG "%u: <%s> cond=%u numbers=%u names=%u ac=%u "
+ "ec=%u\n", __LINE__, condition, condc, numbers_count,
+ names_count, argc, envc);
+ }
+ BUG_ON(names_count);
+ BUG_ON(numbers_count);
+ BUG_ON(argc);
+ BUG_ON(envc);
+ BUG_ON(condc);
+ entry->size = size;
+ mutex_lock(&tomoyo_policy_lock);
+ list_for_each_entry_rcu(ptr, &tomoyo_condition_list, list) {
+ if (tomoyo_memcmp(ptr, entry, offsetof(typeof(*entry), size),
+ size))
+ continue;
+ /* Same entry found. Share this entry. */
+ atomic_inc(&ptr->users);
+ found = true;
+ break;
+ }
+ if (!found) {
+ if (tomoyo_memory_ok(entry, size)) {
+ list_add_rcu(&entry->list, &tomoyo_condition_list);
+ } else {
+ found = true;
+ ptr = NULL;
+ }
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+ if (found) {
+ entry->size = 0;
+ tomoyo_put_condition(entry);
+ entry = ptr;
+ }
+ return entry;
+ out:
+ if (debug)
+ printk(KERN_WARNING "%u: %s failed\n", __LINE__, __func__);
+ tomoyo_put_condition(entry);
+ return NULL;
+}
+
+/**
+ * tomoyo_get_attributes - Revalidate "struct inode".
+ *
+ * @obj: Pointer to "struct tomoyo_obj_info".
+ *
+ * Returns nothing.
+ */
+void tomoyo_get_attributes(struct tomoyo_obj_info *obj)
+{
+ struct vfsmount *mnt;
+ struct dentry *dentry;
+ struct inode *inode;
+ struct kstat stat;
+
+ if (!obj->path1.mnt)
+ goto no_path1;
+
+ /* Get information on "path1". */
+ mnt = obj->path1.mnt;
+ dentry = obj->path1.dentry;
+ inode = dentry->d_inode;
+ if (inode) {
+ if (!inode->i_op || vfs_getattr(mnt, dentry, &stat)) {
+ /* Nothing to do. */
+ } else {
+ obj->path1_stat.uid = stat.uid;
+ obj->path1_stat.gid = stat.gid;
+ obj->path1_stat.ino = stat.ino;
+ obj->path1_stat.mode = stat.mode;
+ obj->path1_stat.dev = stat.dev;
+ obj->path1_stat.rdev = stat.rdev;
+ obj->path1_valid = true;
+ }
+ }
+
+ /* Get information on "path1.parent". */
+ dentry = dget_parent(obj->path1.dentry);
+ inode = dentry->d_inode;
+ if (inode) {
+ if (!inode->i_op || vfs_getattr(mnt, dentry, &stat)) {
+ /* Nothing to do. */
+ } else {
+ obj->path1_parent_stat.uid = stat.uid;
+ obj->path1_parent_stat.gid = stat.gid;
+ obj->path1_parent_stat.ino = stat.ino;
+ obj->path1_parent_stat.mode = stat.mode;
+ obj->path1_parent_stat.dev = stat.dev;
+ obj->path1_parent_stat.rdev = stat.rdev;
+ obj->path1_parent_valid = true;
+ }
+ }
+ dput(dentry);
+
+ no_path1:
+ mnt = obj->path2.mnt;
+ if (!mnt)
+ return;
+
+ /* Get information on "path2". */
+ dentry = obj->path2.dentry;
+ inode = dentry->d_inode;
+ if (inode) {
+ if (!inode->i_op || vfs_getattr(mnt, dentry, &stat)) {
+ /* Nothing to do. */
+ } else {
+ obj->path2_stat.uid = stat.uid;
+ obj->path2_stat.gid = stat.gid;
+ obj->path2_stat.ino = stat.ino;
+ obj->path2_stat.mode = stat.mode;
+ obj->path2_stat.dev = stat.dev;
+ obj->path2_stat.rdev = stat.rdev;
+ obj->path2_valid = true;
+ }
+ }
+
+ /* Get information on "path2.parent". */
+ dentry = dget_parent(obj->path2.dentry);
+ inode = dentry->d_inode;
+ if (inode) {
+ if (!inode->i_op || vfs_getattr(mnt, dentry, &stat)) {
+ /* Nothing to do. */
+ } else {
+ obj->path2_parent_stat.uid = stat.uid;
+ obj->path2_parent_stat.gid = stat.gid;
+ obj->path2_parent_stat.ino = stat.ino;
+ obj->path2_parent_stat.mode = stat.mode;
+ obj->path2_parent_stat.dev = stat.dev;
+ obj->path2_parent_stat.rdev = stat.rdev;
+ obj->path2_parent_valid = true;
+ }
+ }
+ dput(dentry);
+}
+
+/**
+ * tomoyo_condition - Check condition part.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ * @acl: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+bool tomoyo_condition(struct tomoyo_request_info *r,
+ const struct tomoyo_acl_info *acl)
+{
+ const struct task_struct *task = current;
+ u32 i;
+ unsigned long left_min = 0;
+ unsigned long left_max = 0;
+ unsigned long right_min = 0;
+ unsigned long right_max = 0;
+ const struct tomoyo_condition_element *condp;
+ const struct tomoyo_number_union *numbers_p;
+ const struct tomoyo_name_union *names_p;
+ const struct tomoyo_argv_entry *argv;
+ const struct tomoyo_envp_entry *envp;
+ struct tomoyo_obj_info *obj;
+ u16 condc;
+ u16 argc;
+ u16 envc;
+ struct linux_binprm *bprm = NULL;
+ const struct tomoyo_condition *cond = acl->cond;
+ if (!cond)
+ return true;
+ condc = cond->condc;
+ argc = cond->argc;
+ envc = cond->envc;
+ obj = r->obj;
+ if (r->ee)
+ bprm = r->ee->bprm;
+ if (!bprm && (argc || envc))
+ return false;
+ condp = (struct tomoyo_condition_element *) (cond + 1);
+ numbers_p = (const struct tomoyo_number_union *) (condp + condc);
+ names_p = (const struct tomoyo_name_union *)
+ (numbers_p + cond->numbers_count);
+ argv = (const struct tomoyo_argv_entry *) (names_p +
+ cond->names_count);
+ envp = (const struct tomoyo_envp_entry *) (argv + argc);
+ for (i = 0; i < condc; i++) {
+ const bool match = condp->equals;
+ const u8 left = condp->left;
+ const u8 right = condp->right;
+ bool left_is_bitop = false;
+ bool right_is_bitop = false;
+ u8 j;
+ condp++;
+ /* Check argv[] and envp[] later. */
+ if (left == TOMOYO_ARGV_ENTRY || left == TOMOYO_ENVP_ENTRY)
+ continue;
+ /* Check string expressions. */
+ if (right == TOMOYO_NAME_UNION) {
+ const struct tomoyo_name_union *ptr = names_p++;
+ switch (left) {
+ struct tomoyo_path_info *symlink;
+ struct tomoyo_execve_entry *ee;
+ struct file *file;
+ case TOMOYO_SYMLINK_TARGET:
+ symlink = obj->symlink_target;
+ if (!symlink ||
+ tomoyo_compare_name_union(symlink, ptr)
+ != match)
+ goto out;
+ break;
+ case TOMOYO_EXEC_REALPATH:
+ ee = r->ee;
+ file = ee ? ee->bprm->file : NULL;
+ if (!tomoyo_scan_exec_realpath(file, ptr,
+ match))
+ goto out;
+ break;
+ }
+ continue;
+ }
+ /* Check numeric or bit-op expressions. */
+ for (j = 0; j < 2; j++) {
+ const u8 index = j ? right : left;
+ unsigned long value = 0;
+ bool is_bitop = false;
+ switch (index) {
+ case TOMOYO_TASK_UID:
+ value = current_uid();
+ break;
+ case TOMOYO_TASK_EUID:
+ value = current_euid();
+ break;
+ case TOMOYO_TASK_SUID:
+ value = current_suid();
+ break;
+ case TOMOYO_TASK_FSUID:
+ value = current_fsuid();
+ break;
+ case TOMOYO_TASK_GID:
+ value = current_gid();
+ break;
+ case TOMOYO_TASK_EGID:
+ value = current_egid();
+ break;
+ case TOMOYO_TASK_SGID:
+ value = current_sgid();
+ break;
+ case TOMOYO_TASK_FSGID:
+ value = current_fsgid();
+ break;
+ case TOMOYO_TASK_PID:
+ value = sys_getpid();
+ break;
+ case TOMOYO_TASK_PPID:
+ value = sys_getppid();
+ break;
+ case TOMOYO_TYPE_IS_SOCKET:
+ value = S_IFSOCK;
+ break;
+ case TOMOYO_TYPE_IS_SYMLINK:
+ value = S_IFLNK;
+ break;
+ case TOMOYO_TYPE_IS_FILE:
+ value = S_IFREG;
+ break;
+ case TOMOYO_TYPE_IS_BLOCK_DEV:
+ value = S_IFBLK;
+ break;
+ case TOMOYO_TYPE_IS_DIRECTORY:
+ value = S_IFDIR;
+ break;
+ case TOMOYO_TYPE_IS_CHAR_DEV:
+ value = S_IFCHR;
+ break;
+ case TOMOYO_TYPE_IS_FIFO:
+ value = S_IFIFO;
+ break;
+ case TOMOYO_MODE_SETUID:
+ value = S_ISUID;
+ is_bitop = true;
+ break;
+ case TOMOYO_MODE_SETGID:
+ value = S_ISGID;
+ is_bitop = true;
+ break;
+ case TOMOYO_MODE_STICKY:
+ value = S_ISVTX;
+ is_bitop = true;
+ break;
+ case TOMOYO_MODE_OWNER_READ:
+ value = S_IRUSR;
+ is_bitop = true;
+ break;
+ case TOMOYO_MODE_OWNER_WRITE:
+ value = S_IWUSR;
+ is_bitop = true;
+ break;
+ case TOMOYO_MODE_OWNER_EXECUTE:
+ value = S_IXUSR;
+ is_bitop = true;
+ break;
+ case TOMOYO_MODE_GROUP_READ:
+ value = S_IRGRP;
+ is_bitop = true;
+ break;
+ case TOMOYO_MODE_GROUP_WRITE:
+ value = S_IWGRP;
+ is_bitop = true;
+ break;
+ case TOMOYO_MODE_GROUP_EXECUTE:
+ value = S_IXGRP;
+ is_bitop = true;
+ break;
+ case TOMOYO_MODE_OTHERS_READ:
+ value = S_IROTH;
+ is_bitop = true;
+ break;
+ case TOMOYO_MODE_OTHERS_WRITE:
+ value = S_IWOTH;
+ is_bitop = true;
+ break;
+ case TOMOYO_MODE_OTHERS_EXECUTE:
+ value = S_IXOTH;
+ is_bitop = true;
+ break;
+ case TOMOYO_EXEC_ARGC:
+ if (!bprm)
+ goto out;
+ value = bprm->argc;
+ break;
+ case TOMOYO_EXEC_ENVC:
+ if (!bprm)
+ goto out;
+ value = bprm->envc;
+ break;
+ case TOMOYO_TASK_STATE_0:
+ value = (u8) (task->tomoyo_flags >> 24);
+ break;
+ case TOMOYO_TASK_STATE_1:
+ value = (u8) (task->tomoyo_flags >> 16);
+ break;
+ case TOMOYO_TASK_STATE_2:
+ value = (u8) (task->tomoyo_flags >> 8);
+ break;
+ case TOMOYO_TASK_TYPE:
+ value = ((u8) task->tomoyo_flags)
+ & TOMOYO_TASK_IS_EXECUTE_HANDLER;
+ break;
+ case TOMOYO_TASK_EXECUTE_HANDLER:
+ value = TOMOYO_TASK_IS_EXECUTE_HANDLER;
+ break;
+ case TOMOYO_NUMBER_UNION:
+ /* Fetch values later. */
+ break;
+ default:
+ if (!obj)
+ goto out;
+ if (!obj->validate_done) {
+ tomoyo_get_attributes(obj);
+ obj->validate_done = true;
+ }
+ switch (index) {
+ case TOMOYO_PATH1_UID:
+ if (!obj->path1_valid)
+ goto out;
+ value = obj->path1_stat.uid;
+ break;
+ case TOMOYO_PATH1_GID:
+ if (!obj->path1_valid)
+ goto out;
+ value = obj->path1_stat.gid;
+ break;
+ case TOMOYO_PATH1_INO:
+ if (!obj->path1_valid)
+ goto out;
+ value = obj->path1_stat.ino;
+ break;
+ case TOMOYO_PATH1_MAJOR:
+ if (!obj->path1_valid)
+ goto out;
+ value = MAJOR(obj->path1_stat.dev);
+ break;
+ case TOMOYO_PATH1_MINOR:
+ if (!obj->path1_valid)
+ goto out;
+ value = MINOR(obj->path1_stat.dev);
+ break;
+ case TOMOYO_PATH1_TYPE:
+ if (!obj->path1_valid)
+ goto out;
+ value = obj->path1_stat.mode & S_IFMT;
+ break;
+ case TOMOYO_PATH1_DEV_MAJOR:
+ if (!obj->path1_valid)
+ goto out;
+ value = MAJOR(obj->path1_stat.rdev);
+ break;
+ case TOMOYO_PATH1_DEV_MINOR:
+ if (!obj->path1_valid)
+ goto out;
+ value = MINOR(obj->path1_stat.rdev);
+ break;
+ case TOMOYO_PATH1_PERM:
+ if (!obj->path1_valid)
+ goto out;
+ value = obj->path1_stat.mode
+ & S_IALLUGO;
+ break;
+ case TOMOYO_PATH2_UID:
+ if (!obj->path2_valid)
+ goto out;
+ value = obj->path2_stat.uid;
+ break;
+ case TOMOYO_PATH2_GID:
+ if (!obj->path2_valid)
+ goto out;
+ value = obj->path2_stat.gid;
+ break;
+ case TOMOYO_PATH2_INO:
+ if (!obj->path2_valid)
+ goto out;
+ value = obj->path2_stat.ino;
+ break;
+ case TOMOYO_PATH2_MAJOR:
+ if (!obj->path2_valid)
+ goto out;
+ value = MAJOR(obj->path2_stat.dev);
+ break;
+ case TOMOYO_PATH2_MINOR:
+ if (!obj->path2_valid)
+ goto out;
+ value = MINOR(obj->path2_stat.dev);
+ break;
+ case TOMOYO_PATH2_TYPE:
+ if (!obj->path2_valid)
+ goto out;
+ value = obj->path2_stat.mode & S_IFMT;
+ break;
+ case TOMOYO_PATH2_DEV_MAJOR:
+ if (!obj->path2_valid)
+ goto out;
+ value = MAJOR(obj->path2_stat.rdev);
+ break;
+ case TOMOYO_PATH2_DEV_MINOR:
+ if (!obj->path2_valid)
+ goto out;
+ value = MINOR(obj->path2_stat.rdev);
+ break;
+ case TOMOYO_PATH2_PERM:
+ if (!obj->path2_valid)
+ goto out;
+ value = obj->path2_stat.mode
+ & S_IALLUGO;
+ break;
+ case TOMOYO_PATH1_PARENT_UID:
+ if (!obj->path1_parent_valid)
+ goto out;
+ value = obj->path1_parent_stat.uid;
+ break;
+ case TOMOYO_PATH1_PARENT_GID:
+ if (!obj->path1_parent_valid)
+ goto out;
+ value = obj->path1_parent_stat.gid;
+ break;
+ case TOMOYO_PATH1_PARENT_INO:
+ if (!obj->path1_parent_valid)
+ goto out;
+ value = obj->path1_parent_stat.ino;
+ break;
+ case TOMOYO_PATH1_PARENT_PERM:
+ if (!obj->path1_parent_valid)
+ goto out;
+ value = obj->path1_parent_stat.mode
+ & S_IALLUGO;
+ break;
+ case TOMOYO_PATH2_PARENT_UID:
+ if (!obj->path2_parent_valid)
+ goto out;
+ value = obj->path2_parent_stat.uid;
+ break;
+ case TOMOYO_PATH2_PARENT_GID:
+ if (!obj->path2_parent_valid)
+ goto out;
+ value = obj->path2_parent_stat.gid;
+ break;
+ case TOMOYO_PATH2_PARENT_INO:
+ if (!obj->path2_parent_valid)
+ goto out;
+ value = obj->path2_parent_stat.ino;
+ break;
+ case TOMOYO_PATH2_PARENT_PERM:
+ if (!obj->path2_parent_valid)
+ goto out;
+ value = obj->path2_parent_stat.mode
+ & S_IALLUGO;
+ break;
+ }
+ break;
+ }
+ value = value;
+ if (j) {
+ right_max = value;
+ right_min = value;
+ right_is_bitop = is_bitop;
+ } else {
+ left_max = value;
+ left_min = value;
+ left_is_bitop = is_bitop;
+ }
+ }
+ if (left == TOMOYO_NUMBER_UNION) {
+ /* Fetch values now. */
+ const struct tomoyo_number_union *ptr = numbers_p++;
+ left_min = ptr->values[0];
+ left_max = ptr->values[1];
+ }
+ if (right == TOMOYO_NUMBER_UNION) {
+ /* Fetch values now. */
+ const struct tomoyo_number_union *ptr = numbers_p++;
+ if (ptr->is_group) {
+ if (tomoyo_number_matches_group(left_min,
+ left_max,
+ ptr->group)
+ == match)
+ continue;
+ } else {
+ if ((left_min <= ptr->values[1] &&
+ left_max >= ptr->values[0]) == match)
+ continue;
+ }
+ goto out;
+ }
+ /*
+ * Bit operation is valid only when counterpart value
+ * represents permission.
+ */
+ if (left_is_bitop && right_is_bitop)
+ goto out;
+ if (left_is_bitop) {
+ switch (right) {
+ case TOMOYO_PATH1_PERM:
+ case TOMOYO_PATH1_PARENT_PERM:
+ case TOMOYO_PATH2_PERM:
+ case TOMOYO_PATH2_PARENT_PERM:
+ if (match) {
+ if ((right_max & left_max))
+ continue;
+ } else {
+ if (!(right_max & left_max))
+ continue;
+ }
+ }
+ goto out;
+ }
+ if (right_is_bitop) {
+ switch (left) {
+ case TOMOYO_PATH1_PERM:
+ case TOMOYO_PATH1_PARENT_PERM:
+ case TOMOYO_PATH2_PERM:
+ case TOMOYO_PATH2_PARENT_PERM:
+ if (match) {
+ if ((left_max & right_max))
+ continue;
+ } else {
+ if (!(left_max & right_max))
+ continue;
+ }
+ }
+ goto out;
+ }
+ /* Normal value range comparison. */
+ if (match) {
+ if (left_min <= right_max && left_max >= right_min)
+ continue;
+ } else {
+ if (left_min > right_max || left_max < right_min)
+ continue;
+ }
+ out:
+ return false;
+ }
+ /* Check argv[] and envp[] now. */
+ if (r->ee && (argc || envc))
+ return tomoyo_scan_bprm(r->ee, argc, argv, envc, envp);
+ return true;
+}
--
^ permalink raw reply [flat|nested] 50+ messages in thread
* [TOMOYO #16 11/25] TOMOYO: Add auditing support.
2009-10-04 12:49 [TOMOYO #16 00/25] Starting TOMOYO 2.3 Tetsuo Handa
` (9 preceding siblings ...)
2009-10-04 12:49 ` [TOMOYO #16 10/25] TOMOYO: Add conditional ACL support Tetsuo Handa
@ 2009-10-04 12:49 ` Tetsuo Handa
2009-10-04 12:49 ` [TOMOYO #16 12/25] TOMOYO: Memory management support Tetsuo Handa
` (14 subsequent siblings)
25 siblings, 0 replies; 50+ messages in thread
From: Tetsuo Handa @ 2009-10-04 12:49 UTC (permalink / raw)
To: linux-security-module; +Cc: linux-kernel, Tetsuo Handa
[-- Attachment #1: tomoyo-add-audit.patch --]
[-- Type: text/plain, Size: 16358 bytes --]
This patch adds auditing code used by TOMOYO.
TOMOYO generates audit logs ( /sys/kernel/security/tomoyo/grant_log and
/sys/kernel/security/tomoyo/reject_log ) in the form of domain policy
( /sys/kernel/security/domain_policy ).
Users can configure whether to generate audit logs or not via profile
( /sys/kernel/security/tomoyo/profile ). By default all information usable for
"if" clause is audited. Users can suppress unneeded information.
Memory quota for audit logs is configurable via profile and meminfo interface
( /sys/kernel/security/tomoyo/meminfo ).
tomoyo_profile()->audit is guaranteed to point to either default configuration
( tomoyo_default_profile ) or per profile configuration.
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
security/tomoyo/audit.c | 561 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 561 insertions(+)
--- /dev/null
+++ security-testing-2.6/security/tomoyo/audit.c
@@ -0,0 +1,561 @@
+/*
+ * security/tomoyo/audit.c
+ *
+ * Copyright (C) 2005-2009 NTT DATA CORPORATION
+ */
+#include "internal.h"
+
+/**
+ * tomoyo_print_bprm - Print "struct linux_binprm" for auditing.
+ *
+ * @bprm: Pointer to "struct linux_binprm".
+ * @dump: Pointer to "struct tomoyo_page_dump".
+ *
+ * Returns the contents of @bprm on success, NULL otherwise.
+ */
+static char *tomoyo_print_bprm(struct linux_binprm *bprm,
+ struct tomoyo_page_dump *dump)
+{
+ static const int tomoyo_buffer_len = 4096 * 2;
+ char *buffer = kzalloc(tomoyo_buffer_len, GFP_KERNEL);
+ char *cp;
+ char *last_start;
+ int len;
+ unsigned long pos = bprm->p;
+ int offset = pos % PAGE_SIZE;
+ int argv_count = bprm->argc;
+ int envp_count = bprm->envc;
+ bool truncated = false;
+ if (!buffer)
+ return NULL;
+ len = snprintf(buffer, tomoyo_buffer_len - 1, "argv[]={ ");
+ cp = buffer + len;
+ if (!argv_count) {
+ memmove(cp, "} envp[]={ ", 11);
+ cp += 11;
+ }
+ last_start = cp;
+ while (argv_count || envp_count) {
+ if (!tomoyo_dump_page(bprm, pos, dump))
+ goto out;
+ pos += PAGE_SIZE - offset;
+ /* Read. */
+ while (offset < PAGE_SIZE) {
+ const char *kaddr = dump->data;
+ const unsigned char c = kaddr[offset++];
+ if (cp == last_start)
+ *cp++ = '"';
+ if (cp >= buffer + tomoyo_buffer_len - 32) {
+ /* Reserve some room for "..." string. */
+ truncated = true;
+ } else if (c == '\\') {
+ *cp++ = '\\';
+ *cp++ = '\\';
+ } else if (c > ' ' && c < 127) {
+ *cp++ = c;
+ } else if (!c) {
+ *cp++ = '"';
+ *cp++ = ' ';
+ last_start = cp;
+ } else {
+ *cp++ = '\\';
+ *cp++ = (c >> 6) + '0';
+ *cp++ = ((c >> 3) & 7) + '0';
+ *cp++ = (c & 7) + '0';
+ }
+ if (c)
+ continue;
+ if (argv_count) {
+ if (--argv_count == 0) {
+ if (truncated) {
+ cp = last_start;
+ memmove(cp, "... ", 4);
+ cp += 4;
+ }
+ memmove(cp, "} envp[]={ ", 11);
+ cp += 11;
+ last_start = cp;
+ truncated = false;
+ }
+ } else if (envp_count) {
+ if (--envp_count == 0) {
+ if (truncated) {
+ cp = last_start;
+ memmove(cp, "... ", 4);
+ cp += 4;
+ }
+ }
+ }
+ if (!argv_count && !envp_count)
+ break;
+ }
+ offset = 0;
+ }
+ *cp++ = '}';
+ *cp = '\0';
+ return buffer;
+ out:
+ snprintf(buffer, tomoyo_buffer_len - 1,
+ "argv[]={ ... } envp[]= { ... }");
+ return buffer;
+}
+
+/**
+ * tomoyo_filetype - Get string representation of file type.
+ *
+ * @mode: Mode value for stat().
+ *
+ * Returns header line on success, NULL otherwise.
+ */
+static const char *tomoyo_filetype(const mode_t mode)
+{
+ switch (mode & S_IFMT) {
+ case S_IFREG:
+ case 0:
+ return "file";
+ case S_IFDIR:
+ return "directory";
+ case S_IFLNK:
+ return "symlink";
+ case S_IFIFO:
+ return "fifo";
+ case S_IFSOCK:
+ return "socket";
+ case S_IFBLK:
+ return "block";
+ case S_IFCHR:
+ return "char";
+ }
+ return "unknown"; /* This should not happen. */
+}
+
+/**
+ * tomoyo_print_header - Get header line of audit log.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ *
+ * Returns string representation.
+ */
+static char *tomoyo_print_header(struct tomoyo_request_info *r)
+{
+ static const char *tomoyo_mode_4[4] = {
+ "disabled", "learning", "permissive", "enforcing"
+ };
+ struct timeval tv;
+ unsigned int dev;
+ mode_t mode;
+ struct tomoyo_obj_info *obj = r->obj;
+ const u32 tomoyo_flags = current->tomoyo_flags;
+ static const int tomoyo_buffer_len = 4096;
+ char *buffer = kmalloc(tomoyo_buffer_len, GFP_KERNEL);
+ int pos;
+ if (!buffer)
+ return NULL;
+ do_gettimeofday(&tv);
+ pos = snprintf(buffer, tomoyo_buffer_len - 1,
+ "#timestamp=%lu profile=%u mode=%s "
+ "(global-pid=%u)", tv.tv_sec, r->profile,
+ tomoyo_mode_4[r->mode], task_pid_nr(current));
+ if (tomoyo_profile(r->profile)->audit->audit_task_info) {
+ pos += snprintf(buffer + pos, tomoyo_buffer_len - 1 - pos,
+ " task={ pid=%u ppid=%u uid=%u gid=%u euid=%u"
+ " egid=%u suid=%u sgid=%u fsuid=%u fsgid=%u"
+ " state[0]=%u state[1]=%u state[2]=%u"
+ " type%s=execute_handler }",
+ (pid_t) sys_getpid(), (pid_t) sys_getppid(),
+ current_uid(), current_gid(), current_euid(),
+ current_egid(), current_suid(), current_sgid(),
+ current_fsuid(), current_fsgid(),
+ (u8) (tomoyo_flags >> 24),
+ (u8) (tomoyo_flags >> 16),
+ (u8) (tomoyo_flags >> 8), tomoyo_flags &
+ TOMOYO_TASK_IS_EXECUTE_HANDLER ? "" : "!");
+ }
+ if (!obj || !tomoyo_profile(r->profile)->audit->audit_path_info)
+ goto no_obj_info;
+ if (!obj->validate_done) {
+ tomoyo_get_attributes(obj);
+ obj->validate_done = true;
+ }
+ if (obj->path1_valid) {
+ dev = obj->path1_stat.dev;
+ mode = obj->path1_stat.mode;
+ pos += snprintf(buffer + pos, tomoyo_buffer_len - 1 - pos,
+ " path1={ uid=%u gid=%u ino=%lu major=%u"
+ " minor=%u perm=0%o type=%s",
+ obj->path1_stat.uid, obj->path1_stat.gid,
+ (unsigned long) obj->path1_stat.ino,
+ MAJOR(dev), MINOR(dev), mode & S_IALLUGO,
+ tomoyo_filetype(mode & S_IFMT));
+ if (S_ISCHR(mode) || S_ISBLK(mode)) {
+ dev = obj->path1_stat.rdev;
+ pos += snprintf(buffer + pos,
+ tomoyo_buffer_len - 1 - pos,
+ " dev_major=%u dev_minor=%u",
+ MAJOR(dev), MINOR(dev));
+ }
+ pos += snprintf(buffer + pos, tomoyo_buffer_len - 1 - pos,
+ " }");
+ }
+ if (obj->path1_parent_valid) {
+ pos += snprintf(buffer + pos, tomoyo_buffer_len - 1 - pos,
+ " path1.parent={ uid=%u gid=%u ino=%lu"
+ " perm=0%o }", obj->path1_parent_stat.uid,
+ obj->path1_parent_stat.gid,
+ obj->path1_parent_stat.ino,
+ obj->path1_parent_stat.mode & S_IALLUGO);
+ }
+ if (obj->path2_valid) {
+ dev = obj->path2_stat.dev;
+ mode = obj->path2_stat.mode;
+ pos += snprintf(buffer + pos, tomoyo_buffer_len - 1 - pos,
+ " path2={ uid=%u gid=%u ino=%lu major=%u"
+ " minor=%u perm=0%o type=%s",
+ obj->path2_stat.uid, obj->path2_stat.gid,
+ (unsigned long) obj->path2_stat.ino,
+ MAJOR(dev), MINOR(dev), mode & S_IALLUGO,
+ tomoyo_filetype(mode & S_IFMT));
+ if (S_ISCHR(mode) || S_ISBLK(mode)) {
+ dev = obj->path2_stat.rdev;
+ pos += snprintf(buffer + pos,
+ tomoyo_buffer_len - 1 - pos,
+ " dev_major=%u dev_minor=%u",
+ MAJOR(dev), MINOR(dev));
+ }
+ pos += snprintf(buffer + pos, tomoyo_buffer_len - 1 - pos,
+ " }");
+ }
+ if (obj->path2_parent_valid) {
+ pos += snprintf(buffer + pos, tomoyo_buffer_len - 1 - pos,
+ " path2.parent={ uid=%u gid=%u ino=%lu"
+ " perm=0%o }", obj->path2_parent_stat.uid,
+ obj->path2_parent_stat.gid,
+ obj->path2_parent_stat.ino,
+ obj->path2_parent_stat.mode & S_IALLUGO);
+ }
+ no_obj_info:
+ if (pos < tomoyo_buffer_len - 1)
+ return buffer;
+ kfree(buffer);
+ return NULL;
+}
+
+/**
+ * tomoyo_init_audit_log - Allocate buffer for audit logs.
+ *
+ * @len: Required size.
+ * @r: Pointer to "struct tomoyo_request_info".
+ *
+ * Returns pointer to allocated memory.
+ *
+ * The @len is updated to add the header lines' size on success.
+ */
+char *tomoyo_init_audit_log(int *len, struct tomoyo_request_info *r)
+{
+ char *buf = NULL;
+ char *bprm_info = NULL;
+ char *realpath = NULL;
+ const char *symlink = NULL;
+ const char *header = NULL;
+ int pos;
+ const char *domainname;
+ if (!r->domain)
+ r->domain = tomoyo_current_domain();
+ domainname = r->domain->domainname->name;
+ header = tomoyo_print_header(r);
+ if (!header)
+ return NULL;
+ *len += strlen(domainname) + strlen(header) + 10;
+ if (r->ee) {
+ struct file *file = r->ee->bprm->file;
+ realpath = tomoyo_realpath_from_path(&file->f_path);
+ bprm_info = tomoyo_print_bprm(r->ee->bprm, &r->ee->dump);
+ if (!realpath || !bprm_info)
+ goto out;
+ *len += strlen(realpath) + 64 + strlen(bprm_info);
+ } else if (r->obj && r->obj->symlink_target) {
+ symlink = r->obj->symlink_target->name;
+ *len += 18 + strlen(symlink);
+ }
+ buf = kzalloc(*len, GFP_KERNEL);
+ if (!buf)
+ goto out;
+ pos = snprintf(buf, (*len) - 1, "%s", header);
+ if (realpath) {
+ struct linux_binprm *bprm = r->ee->bprm;
+ pos += snprintf(buf + pos, (*len) - 1 - pos,
+ " exec={ realpath=\"%s\" argc=%d envc=%d %s }",
+ realpath, bprm->argc, bprm->envc, bprm_info);
+ } else if (symlink)
+ pos += snprintf(buf + pos, (*len) - 1 - pos,
+ " symlink.target=\"%s\"", symlink);
+ snprintf(buf + pos, (*len) - 1 - pos, "\n%s\n", domainname);
+ out:
+ kfree(realpath);
+ kfree(bprm_info);
+ kfree(header);
+ return buf;
+}
+
+/**
+ * tomoyo_update_task_state - Update task's state.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ */
+static void tomoyo_update_task_state(struct tomoyo_request_info *r)
+{
+ /*
+ * Don't change the lowest byte because it is reserved for
+ * TOMOYO_TASK_IS_IN_EXECVE /
+ * TOMOYO_DONT_SLEEP_ON_ENFORCE_ERROR /
+ * TOMOYO_TASK_IS_EXECUTE_HANDLER / TOMOYO_TASK_IS_POLICY_MANAGER .
+ */
+ const struct tomoyo_condition *ptr = r->cond;
+ if (ptr) {
+ struct task_struct *task = current;
+ const u8 flags = ptr->post_state[3];
+ u32 tomoyo_flags = task->tomoyo_flags;
+ if (flags & 1) {
+ tomoyo_flags &= ~0xFF000000;
+ tomoyo_flags |= ptr->post_state[0] << 24;
+ }
+ if (flags & 2) {
+ tomoyo_flags &= ~0x00FF0000;
+ tomoyo_flags |= ptr->post_state[1] << 16;
+ }
+ if (flags & 4) {
+ tomoyo_flags &= ~0x0000FF00;
+ tomoyo_flags |= ptr->post_state[2] << 8;
+ }
+ task->tomoyo_flags = tomoyo_flags;
+ r->cond = NULL;
+ }
+}
+
+#ifndef CONFIG_SECURITY_TOMOYO_AUDIT
+
+/**
+ * tomoyo_write_audit_log - Write audit log.
+ *
+ * @is_granted: True if this is a granted log.
+ * @r: Pointer to "struct tomoyo_request_info".
+ * @fmt: The printf()'s format string, followed by parameters.
+ *
+ * Returns 0 on success, -ENOMEM otherwise.
+ */
+int tomoyo_write_audit_log(const bool is_granted,
+ struct tomoyo_request_info *r, const char *fmt, ...)
+{
+ tomoyo_update_task_state(r);
+ return 0;
+}
+
+#else
+
+static DECLARE_WAIT_QUEUE_HEAD(tomoyo_grant_log_wait);
+static DECLARE_WAIT_QUEUE_HEAD(tomoyo_reject_log_wait);
+
+static DEFINE_SPINLOCK(tomoyo_audit_log_lock);
+
+/* Structure for audit log. */
+struct tomoyo_log_entry {
+ struct list_head list;
+ char *log;
+ int size;
+};
+
+/* The list for "struct tomoyo_log_entry". */
+static LIST_HEAD(tomoyo_grant_log);
+
+/* The list for "struct tomoyo_log_entry". */
+static LIST_HEAD(tomoyo_reject_log);
+
+static unsigned int tomoyo_grant_log_count;
+static unsigned int tomoyo_reject_log_count;
+
+/**
+ * tomoyo_write_audit_log - Write audit log.
+ *
+ * @is_granted: True if this is a granted log.
+ * @r: Pointer to "struct tomoyo_request_info".
+ * @fmt: The printf()'s format string, followed by parameters.
+ *
+ * Returns 0 on success, -ENOMEM otherwise.
+ */
+int tomoyo_write_audit_log(const bool is_granted,
+ struct tomoyo_request_info *r, const char *fmt, ...)
+{
+ va_list args;
+ int error = -ENOMEM;
+ int pos;
+ int len;
+ char *buf;
+ struct tomoyo_log_entry *new_entry;
+ bool quota_exceeded = false;
+ if (!r->domain)
+ r->domain = tomoyo_current_domain();
+ if (is_granted) {
+ if (tomoyo_grant_log_count >=
+ tomoyo_profile(r->domain->profile)->
+ audit->audit_max_grant_log
+ || !tomoyo_get_audit(r->profile, r->type, true))
+ goto out;
+ } else {
+ if (tomoyo_reject_log_count >=
+ tomoyo_profile(r->domain->profile)->
+ audit->audit_max_reject_log
+ || !tomoyo_get_audit(r->profile, r->type, false))
+ goto out;
+ }
+ va_start(args, fmt);
+ len = vsnprintf((char *) &pos, sizeof(pos) - 1, fmt, args) + 32;
+ va_end(args);
+ buf = tomoyo_init_audit_log(&len, r);
+ if (!buf)
+ goto out;
+ pos = strlen(buf);
+ va_start(args, fmt);
+ vsnprintf(buf + pos, len - pos - 1, fmt, args);
+ va_end(args);
+ new_entry = kzalloc(sizeof(*new_entry), GFP_KERNEL);
+ if (!new_entry) {
+ kfree(buf);
+ goto out;
+ }
+ new_entry->log = buf;
+ /*
+ * The new_entry->size is used for memory quota checks.
+ * Don't go beyond strlen(new_entry->log).
+ */
+ new_entry->size = tomoyo_round2(len) +
+ tomoyo_round2(sizeof(*new_entry));
+ spin_lock(&tomoyo_audit_log_lock);
+ if (tomoyo_quota_for_audit_log && tomoyo_audit_log_memory_size
+ + new_entry->size >= tomoyo_quota_for_audit_log) {
+ quota_exceeded = true;
+ } else {
+ tomoyo_audit_log_memory_size += new_entry->size;
+ if (is_granted) {
+ list_add_tail(&new_entry->list, &tomoyo_grant_log);
+ tomoyo_grant_log_count++;
+ } else {
+ list_add_tail(&new_entry->list, &tomoyo_reject_log);
+ tomoyo_reject_log_count++;
+ }
+ }
+ spin_unlock(&tomoyo_audit_log_lock);
+ if (quota_exceeded) {
+ kfree(buf);
+ kfree(new_entry);
+ goto out;
+ }
+ if (is_granted)
+ wake_up(&tomoyo_grant_log_wait);
+ else
+ wake_up(&tomoyo_reject_log_wait);
+ error = 0;
+ out:
+ tomoyo_update_task_state(r);
+ return error;
+}
+
+/**
+ * tomoyo_read_grant_log - Read a grant log.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ */
+void tomoyo_read_grant_log(struct tomoyo_io_buffer *head)
+{
+ struct tomoyo_log_entry *ptr = NULL;
+ if (head->read_avail)
+ return;
+ if (head->read_buf) {
+ kfree(head->read_buf);
+ head->read_buf = NULL;
+ head->readbuf_size = 0;
+ }
+ spin_lock(&tomoyo_audit_log_lock);
+ if (!list_empty(&tomoyo_grant_log)) {
+ ptr = list_entry(tomoyo_grant_log.next,
+ struct tomoyo_log_entry, list);
+ list_del(&ptr->list);
+ tomoyo_grant_log_count--;
+ tomoyo_audit_log_memory_size -= ptr->size;
+ }
+ spin_unlock(&tomoyo_audit_log_lock);
+ if (ptr) {
+ head->read_buf = ptr->log;
+ head->read_avail = strlen(ptr->log) + 1;
+ head->readbuf_size = head->read_avail;
+ kfree(ptr);
+ }
+}
+
+/**
+ * tomoyo_poll_grant_log - Wait for a grant log.
+ *
+ * @file: Pointer to "struct file".
+ * @wait: Pointer to "poll_table".
+ *
+ * Returns POLLIN | POLLRDNORM when ready to read a grant log.
+ */
+int tomoyo_poll_grant_log(struct file *file, poll_table *wait)
+{
+ if (tomoyo_grant_log_count)
+ return POLLIN | POLLRDNORM;
+ poll_wait(file, &tomoyo_grant_log_wait, wait);
+ if (tomoyo_grant_log_count)
+ return POLLIN | POLLRDNORM;
+ return 0;
+}
+
+/**
+ * tomoyo_read_reject_log - Read a reject log.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ */
+void tomoyo_read_reject_log(struct tomoyo_io_buffer *head)
+{
+ struct tomoyo_log_entry *ptr = NULL;
+ if (head->read_avail)
+ return;
+ if (head->read_buf) {
+ kfree(head->read_buf);
+ head->read_buf = NULL;
+ head->readbuf_size = 0;
+ }
+ spin_lock(&tomoyo_audit_log_lock);
+ if (!list_empty(&tomoyo_reject_log)) {
+ ptr = list_entry(tomoyo_reject_log.next,
+ struct tomoyo_log_entry, list);
+ list_del(&ptr->list);
+ tomoyo_reject_log_count--;
+ tomoyo_audit_log_memory_size -= ptr->size;
+ }
+ spin_unlock(&tomoyo_audit_log_lock);
+ if (ptr) {
+ head->read_buf = ptr->log;
+ head->read_avail = strlen(ptr->log) + 1;
+ head->readbuf_size = head->read_avail;
+ kfree(ptr);
+ }
+}
+
+/**
+ * tomoyo_poll_reject_log - Wait for a reject log.
+ *
+ * @file: Pointer to "struct file".
+ * @wait: Pointer to "poll_table".
+ *
+ * Returns POLLIN | POLLRDNORM when ready to read a reject log.
+ */
+int tomoyo_poll_reject_log(struct file *file, poll_table *wait)
+{
+ if (tomoyo_reject_log_count)
+ return POLLIN | POLLRDNORM;
+ poll_wait(file, &tomoyo_reject_log_wait, wait);
+ if (tomoyo_reject_log_count)
+ return POLLIN | POLLRDNORM;
+ return 0;
+}
+#endif
--
^ permalink raw reply [flat|nested] 50+ messages in thread
* [TOMOYO #16 12/25] TOMOYO: Memory management support.
2009-10-04 12:49 [TOMOYO #16 00/25] Starting TOMOYO 2.3 Tetsuo Handa
` (10 preceding siblings ...)
2009-10-04 12:49 ` [TOMOYO #16 11/25] TOMOYO: Add auditing support Tetsuo Handa
@ 2009-10-04 12:49 ` Tetsuo Handa
2009-10-04 12:49 ` [TOMOYO #16 13/25] TOMOYO: Add garbage collector support Tetsuo Handa
` (13 subsequent siblings)
25 siblings, 0 replies; 50+ messages in thread
From: Tetsuo Handa @ 2009-10-04 12:49 UTC (permalink / raw)
To: linux-security-module; +Cc: linux-kernel, Tetsuo Handa
[-- Attachment #1: tomoyo-add-mm.patch --]
[-- Type: text/plain, Size: 11074 bytes --]
This patch contains memory management code for TOMOYO.
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
security/tomoyo/memory.c | 391 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 391 insertions(+)
--- /dev/null
+++ security-testing-2.6/security/tomoyo/memory.c
@@ -0,0 +1,391 @@
+/*
+ * security/tomoyo/memory.c
+ *
+ * Copyright (C) 2005-2009 NTT DATA CORPORATION
+ */
+#include "internal.h"
+
+void tomoyo_warn_oom(const char *function)
+{
+ /* Reduce error messages. */
+ static pid_t tomoyo_last_pid;
+ const pid_t pid = current->pid;
+ if (tomoyo_last_pid != pid) {
+ printk(KERN_WARNING "ERROR: Out of memory at %s.\n",
+ function);
+ tomoyo_last_pid = pid;
+ }
+ if (!tomoyo_policy_loaded)
+ panic("MAC Initialization failed.\n");
+}
+
+static atomic_t tomoyo_policy_memory_size;
+static unsigned int tomoyo_quota_for_policy;
+
+/**
+ * tomoyo_memory_ok - Check memory quota.
+ *
+ * @ptr: Pointer to allocated memory.
+ * @size: Size in byte.
+ *
+ * Returns true if @ptr is not NULL and quota not exceeded, false otehrwise.
+ */
+bool tomoyo_memory_ok(const void *ptr, const unsigned int size)
+{
+ size_t s = tomoyo_round2(size);
+ atomic_add(s, &tomoyo_policy_memory_size);
+ if (ptr && (!tomoyo_quota_for_policy ||
+ atomic_read(&tomoyo_policy_memory_size)
+ <= tomoyo_quota_for_policy))
+ return true;
+ atomic_sub(s, &tomoyo_policy_memory_size);
+ tomoyo_warn_oom(__func__);
+ return false;
+}
+
+/**
+ * tomoyo_commit_ok - Check memory quota.
+ *
+ * @ptr: Pointer to allocated memory.
+ * @data: Data to copy from.
+ * @size: Size in byte.
+ *
+ * Returns true if @ptr is not NULL and quota not exceeded, false otehrwise.
+ */
+bool tomoyo_commit_ok(void *ptr, void *data, const unsigned int size)
+{
+ if (tomoyo_memory_ok(ptr, size)) {
+ memmove(ptr, data, size);
+ memset(data, 0, size);
+ return true;
+ }
+ return false;
+}
+
+
+/**
+ * tomoyo_memory_free - Free memory for elements.
+ *
+ * @ptr: Pointer to allocated memory.
+ * @size: Size in byte.
+ */
+void tomoyo_memory_free(const void *ptr, size_t size)
+{
+ atomic_sub(tomoyo_round2(size), &tomoyo_policy_memory_size);
+ kfree(ptr);
+}
+
+static LIST_HEAD(tomoyo_address_list);
+
+/**
+ * tomoyo_get_ipv6_address - Keep the given IPv6 address on the RAM.
+ *
+ * @addr: Pointer to "struct in6_addr".
+ *
+ * Returns pointer to "struct in6_addr" on success, NULL otherwise.
+ *
+ * The RAM is shared, so NEVER try to modify or kfree() the returned address.
+ */
+const struct in6_addr *tomoyo_get_ipv6_address(const struct in6_addr *addr)
+{
+ struct tomoyo_ipv6addr_entry *entry;
+ struct tomoyo_ipv6addr_entry *ptr;
+ int error = -ENOMEM;
+ if (!addr)
+ return NULL;
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ mutex_lock(&tomoyo_policy_lock);
+ list_for_each_entry(ptr, &tomoyo_address_list, list) {
+ if (memcmp(&ptr->addr, addr, sizeof(*addr)))
+ continue;
+ atomic_inc(&ptr->users);
+ error = 0;
+ break;
+ }
+ if (error && tomoyo_memory_ok(entry, sizeof(*entry))) {
+ ptr = entry;
+ ptr->addr = *addr;
+ atomic_set(&ptr->users, 1);
+ list_add_tail(&ptr->list, &tomoyo_address_list);
+ entry = NULL;
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+ kfree(entry);
+ return ptr ? &ptr->addr : NULL;
+}
+
+/**
+ * tomoyo_put_ipv6_address - Delete the given IPv6 address on the RAM.
+ *
+ * @addr: Pointer to "struct in6_addr".
+ */
+void tomoyo_put_ipv6_address(const struct in6_addr *addr)
+{
+ struct tomoyo_ipv6addr_entry *ptr;
+ bool can_delete = false;
+ if (!addr)
+ return;
+ ptr = container_of(addr, struct tomoyo_ipv6addr_entry, addr);
+ mutex_lock(&tomoyo_policy_lock);
+ if (atomic_dec_and_test(&ptr->users)) {
+ list_del(&ptr->list);
+ can_delete = true;
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+ if (can_delete)
+ tomoyo_memory_free(ptr, sizeof(*ptr));
+}
+
+/**
+ * tomoyo_put_condition - Delete memory for "struct tomoyo_condition".
+ *
+ * @cond: Pointer to "struct tomoyo_condition".
+ */
+void tomoyo_put_condition(struct tomoyo_condition *cond)
+{
+ const struct tomoyo_condition_element *condp;
+ struct tomoyo_number_union *numbers_p;
+ struct tomoyo_name_union *names_p;
+ const struct tomoyo_argv_entry *argv;
+ const struct tomoyo_envp_entry *envp;
+ u16 condc;
+ u16 numbers_count;
+ u16 names_count;
+ u16 argc;
+ u16 envc;
+ u16 i;
+ bool can_delete = false;
+ if (!cond)
+ return;
+ BUG_ON(atomic_read(&cond->users) <= 0);
+ mutex_lock(&tomoyo_policy_lock);
+ if (atomic_dec_and_test(&cond->users)) {
+ list_del(&cond->list);
+ can_delete = true;
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+ if (!can_delete)
+ return;
+ condc = cond->condc;
+ numbers_count = cond->numbers_count;
+ names_count = cond->names_count;
+ argc = cond->argc;
+ envc = cond->envc;
+ condp = (const struct tomoyo_condition_element *) (cond + 1);
+ numbers_p = (struct tomoyo_number_union *) (condp + condc);
+ names_p = (struct tomoyo_name_union *) (numbers_p + numbers_count);
+ argv = (const struct tomoyo_argv_entry *) (names_p + names_count);
+ envp = (const struct tomoyo_envp_entry *) (argv + argc);
+ for (i = 0; i < numbers_count; i++)
+ tomoyo_put_number_union(numbers_p++);
+ for (i = 0; i < names_count; i++)
+ tomoyo_put_name_union(names_p++);
+ for (i = 0; i < argc; argv++, i++)
+ tomoyo_put_name(argv->value);
+ for (i = 0; i < envc; envp++, i++) {
+ tomoyo_put_name(envp->name);
+ tomoyo_put_name(envp->value);
+ }
+ tomoyo_memory_free(cond, cond->size);
+}
+
+/*
+ * TOMOYO uses this hash only when appending a string into the string
+ * table. Frequency of appending strings is very low. So we don't need
+ * large (e.g. 64k) hash size. 256 will be sufficient.
+ */
+#define TOMOYO_MAX_HASH 256
+
+/*
+ * tomoyo_name_entry is a structure which is used for linking
+ * "struct tomoyo_path_info" into tomoyo_name_list .
+ */
+struct tomoyo_name_entry {
+ struct list_head list;
+ atomic_t users;
+ int size;
+ struct tomoyo_path_info entry;
+};
+
+/*
+ * tomoyo_name_list is used for holding string data used by TOMOYO.
+ * Since same string data is likely used for multiple times (e.g.
+ * "/lib/libc-2.5.so"), TOMOYO shares string data in the form of
+ * "const struct tomoyo_path_info *".
+ */
+static struct list_head tomoyo_name_list[TOMOYO_MAX_HASH];
+static DEFINE_MUTEX(tomoyo_name_list_lock);
+
+/**
+ * tomoyo_get_name - Allocate memory for string data.
+ *
+ * @name: The string to store into the permernent memory.
+ *
+ * Returns pointer to "struct tomoyo_path_info" on success, NULL otherwise.
+ */
+const struct tomoyo_path_info *tomoyo_get_name(const char *name)
+{
+ struct tomoyo_name_entry *ptr;
+ unsigned int hash;
+ int len;
+ int allocated_len;
+
+ if (!name)
+ return NULL;
+ len = strlen(name) + 1;
+ hash = full_name_hash((const unsigned char *) name, len - 1);
+ mutex_lock(&tomoyo_name_list_lock);
+ list_for_each_entry(ptr, &tomoyo_name_list[hash % TOMOYO_MAX_HASH],
+ list) {
+ if (hash != ptr->entry.hash || strcmp(name, ptr->entry.name))
+ continue;
+ atomic_inc(&ptr->users);
+ goto out;
+ }
+ allocated_len = tomoyo_round2(sizeof(*ptr) + len);
+ ptr = kzalloc(allocated_len, GFP_KERNEL);
+ if (!ptr || (tomoyo_quota_for_policy &&
+ atomic_read(&tomoyo_policy_memory_size) + allocated_len
+ > tomoyo_quota_for_policy)) {
+ kfree(ptr);
+ ptr = NULL;
+ tomoyo_warn_oom(__func__);
+ goto out;
+ }
+ atomic_add(allocated_len, &tomoyo_policy_memory_size);
+ ptr->entry.name = ((char *) ptr) + sizeof(*ptr);
+ memmove((char *) ptr->entry.name, name, len);
+ atomic_set(&ptr->users, 1);
+ tomoyo_fill_path_info(&ptr->entry);
+ ptr->size = allocated_len;
+ list_add_tail(&ptr->list, &tomoyo_name_list[hash % TOMOYO_MAX_HASH]);
+ out:
+ mutex_unlock(&tomoyo_name_list_lock);
+ return ptr ? &ptr->entry : NULL;
+}
+
+/**
+ * tomoyo_put_name - Delete shared memory for string data.
+ *
+ * @name: Pointer to "struct tomoyo_path_info".
+ */
+void tomoyo_put_name(const struct tomoyo_path_info *name)
+{
+ struct tomoyo_name_entry *ptr;
+ bool can_delete = false;
+ if (!name)
+ return;
+ ptr = container_of(name, struct tomoyo_name_entry, entry);
+ mutex_lock(&tomoyo_name_list_lock);
+ if (atomic_dec_and_test(&ptr->users)) {
+ list_del(&ptr->list);
+ can_delete = true;
+ }
+ mutex_unlock(&tomoyo_name_list_lock);
+ if (can_delete) {
+ atomic_sub(ptr->size, &tomoyo_policy_memory_size);
+ kfree(ptr);
+ }
+}
+
+/**
+ * tomoyo_mm_init - Initialize mm code.
+ *
+ * Returns 0.
+ */
+static int __init tomoyo_mm_init(void)
+{
+ int i;
+ if (!tomoyo_registered)
+ return 0;
+ for (i = 0; i < TOMOYO_MAX_HASH; i++)
+ INIT_LIST_HEAD(&tomoyo_name_list[i]);
+ INIT_LIST_HEAD(&tomoyo_kernel_domain.acl_info_list);
+ tomoyo_kernel_domain.domainname = tomoyo_get_name(ROOT_NAME);
+ list_add_tail_rcu(&tomoyo_kernel_domain.list, &tomoyo_domain_list);
+ if (tomoyo_find_domain(ROOT_NAME) != &tomoyo_kernel_domain)
+ panic("Can't register tomoyo_kernel_domain");
+#ifdef CONFIG_SECURITY_TOMOYO_BUILTIN_INITIALIZERS
+ {
+ /* Load built-in policy. */
+ static char tomoyo_builtin_initializers[] __initdata
+ = CONFIG_SECURITY_TOMOYO_BUILTIN_INITIALIZERS;
+ char *cp = tomoyo_builtin_initializers;
+ tomoyo_normalize_line(cp);
+ while (cp && *cp) {
+ char *cp2 = strchr(cp, ' ');
+ if (cp2)
+ *cp2++ = '\0';
+ tomoyo_write_domain_initializer_policy(cp, false,
+ false);
+ cp = cp2;
+ }
+ }
+#endif
+ return 0;
+}
+core_initcall(tomoyo_mm_init);
+
+unsigned int tomoyo_audit_log_memory_size;
+unsigned int tomoyo_quota_for_audit_log;
+
+unsigned int tomoyo_query_memory_size;
+unsigned int tomoyo_quota_for_query;
+
+/**
+ * tomoyo_read_memory_counter - Check for memory usage.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ */
+void tomoyo_read_memory_counter(struct tomoyo_io_buffer *head)
+{
+ const unsigned int usage[3] = {
+ atomic_read(&tomoyo_policy_memory_size),
+ tomoyo_audit_log_memory_size,
+ tomoyo_query_memory_size
+ };
+ const unsigned int quota[3] = {
+ tomoyo_quota_for_policy,
+ tomoyo_quota_for_audit_log,
+ tomoyo_quota_for_query
+ };
+ static const char *header[4] = {
+ "Policy: ",
+ "Audit logs: ",
+ "Query lists:",
+ "Total: "
+ };
+ unsigned int total = 0;
+ int i;
+ if (head->read_eof)
+ return;
+ for (i = 0; i < 3; i++) {
+ total += usage[i];
+ tomoyo_io_printf(head, "%s %10u", header[i], usage[i]);
+ if (quota[i])
+ tomoyo_io_printf(head, " (Quota: %10u)", quota[i]);
+ tomoyo_io_printf(head, "\n");
+ }
+ tomoyo_io_printf(head, "%s %10u\n", header[3], total);
+ head->read_eof = true;
+}
+
+/**
+ * tomoyo_write_memory_quota - Set memory quota.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0.
+ */
+int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head)
+{
+ char *data = head->write_buf;
+ unsigned int size;
+ if (sscanf(data, "Policy: %u", &size) == 1)
+ tomoyo_quota_for_policy = size;
+ else if (sscanf(data, "Audit logs: %u", &size) == 1)
+ tomoyo_quota_for_audit_log = size;
+ else if (sscanf(data, "Query lists: %u", &size) == 1)
+ tomoyo_quota_for_query = size;
+ return 0;
+}
--
^ permalink raw reply [flat|nested] 50+ messages in thread
* [TOMOYO #16 13/25] TOMOYO: Add garbage collector support.
2009-10-04 12:49 [TOMOYO #16 00/25] Starting TOMOYO 2.3 Tetsuo Handa
` (11 preceding siblings ...)
2009-10-04 12:49 ` [TOMOYO #16 12/25] TOMOYO: Memory management support Tetsuo Handa
@ 2009-10-04 12:49 ` Tetsuo Handa
2009-10-04 12:50 ` [TOMOYO #16 14/25] TOMOYO: Add network restriction Tetsuo Handa
` (12 subsequent siblings)
25 siblings, 0 replies; 50+ messages in thread
From: Tetsuo Handa @ 2009-10-04 12:49 UTC (permalink / raw)
To: linux-security-module; +Cc: linux-kernel, Tetsuo Handa
[-- Attachment #1: tomoyo-add-gc.patch --]
[-- Type: text/plain, Size: 17117 bytes --]
This patch contains code for SRCU based garbage collector.
Both writers and GC process take tomoyo_policy_lock mutex when updating list
elements. All readers use srcu_read_lock()/srcu_read_unlock() to protect list
elements against the GC process. The removed list elements are kfree()d by GC
process after synchronize_srcu().
Special care is taken when garbage collecting "struct tomoyo_domain_info".
When GC process finds that FOO->is_deleted == true, GC process traverses the
tasklist to verify that "struct tomoyo_task_struct *"->tomoyo_domain_info !=
FOO before removing FOO from the tomoyo_domain_list list.
But there is a race window shown below.
(1) Reader process who is doing an execve() operation calls srcu_read_lock().
(2) Reader process reaches FOO because FOO->is_deleted == false.
(3) Writer process changes FOO->is_deleted to true.
(4) GC process finds that FOO->is_deleted == true and also finds that all
thread's "struct tomoyo_task_struct *"->tomoyo_domain_info != FOO , and
removes FOO from the tomoyo_domain_list list.
(5) GC process calls synchronize_srcu().
(6) Reader process sets current->tomoyo_domain_info to FOO .
(7) Reader process calls srcu_read_unlock().
(8) GC process calls kfree(FOO).
Therefore, before doing (8), GC process rechecks that all thread's
"struct tomoyo_task_struct *"->tomoyo_domain_info != FOO by traversing the
tasklist.
Traversing the tasklist might be heavy, but this event seldom happens because
users unlikely delete FOO.
Thus, this approach can avoid use of atomic variables for counting FOO users
and simplify the reader's code.
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
security/tomoyo/gc.c | 606 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 606 insertions(+)
--- /dev/null
+++ security-testing-2.6/security/tomoyo/gc.c
@@ -0,0 +1,606 @@
+/*
+ * security/tomoyo/gc.c
+ *
+ * Copyright (C) 2005-2009 NTT DATA CORPORATION
+ */
+#include "internal.h"
+#include <linux/kthread.h>
+
+enum tomoyo_gc_id {
+ TOMOYO_ID_ADDRESS_GROUP,
+ TOMOYO_ID_ADDRESS_GROUP_MEMBER,
+ TOMOYO_ID_PATH_GROUP,
+ TOMOYO_ID_PATH_GROUP_MEMBER,
+ TOMOYO_ID_NUMBER_GROUP,
+ TOMOYO_ID_NUMBER_GROUP_MEMBER,
+ TOMOYO_ID_GLOBAL_ENV,
+ TOMOYO_ID_AGGREGATOR,
+ TOMOYO_ID_DOMAIN_INITIALIZER,
+ TOMOYO_ID_DOMAIN_KEEPER,
+ TOMOYO_ID_GLOBALLY_READABLE,
+ TOMOYO_ID_PATTERN,
+ TOMOYO_ID_NO_REWRITE,
+ TOMOYO_ID_MANAGER,
+ TOMOYO_ID_ACL,
+ TOMOYO_ID_DOMAIN
+};
+
+struct tomoyo_gc_entry {
+ struct list_head list;
+ int type;
+ void *element;
+};
+static LIST_HEAD(tomoyo_gc_queue);
+static DEFINE_MUTEX(tomoyo_gc_mutex);
+
+/* Caller holds tomoyo_policy_lock mutex. */
+static bool tomoyo_add_to_gc(const int type, void *element)
+{
+ struct tomoyo_gc_entry *entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
+ if (!entry)
+ return false;
+ entry->type = type;
+ entry->element = element;
+ list_add(&entry->list, &tomoyo_gc_queue);
+ return true;
+}
+
+static size_t tomoyo_del_allow_read
+(struct tomoyo_globally_readable_file_entry *ptr)
+{
+ tomoyo_put_name(ptr->filename);
+ return sizeof(*ptr);
+}
+
+static size_t tomoyo_del_allow_env
+(struct tomoyo_globally_usable_env_entry *ptr)
+{
+ tomoyo_put_name(ptr->env);
+ return sizeof(*ptr);
+}
+
+static size_t tomoyo_del_file_pattern(struct tomoyo_pattern_entry *ptr)
+{
+ tomoyo_put_name(ptr->pattern);
+ return sizeof(*ptr);
+}
+
+static size_t tomoyo_del_no_rewrite(struct tomoyo_no_rewrite_entry *ptr)
+{
+ tomoyo_put_name(ptr->pattern);
+ return sizeof(*ptr);
+}
+
+static size_t tomoyo_del_domain_initializer
+(struct tomoyo_domain_initializer_entry *ptr)
+{
+ tomoyo_put_name(ptr->domainname);
+ tomoyo_put_name(ptr->program);
+ return sizeof(*ptr);
+}
+
+static size_t tomoyo_del_domain_keeper(struct tomoyo_domain_keeper_entry *ptr)
+{
+ tomoyo_put_name(ptr->domainname);
+ tomoyo_put_name(ptr->program);
+ return sizeof(*ptr);
+}
+
+static size_t tomoyo_del_aggregator(struct tomoyo_aggregator_entry *ptr)
+{
+ tomoyo_put_name(ptr->original_name);
+ tomoyo_put_name(ptr->aggregated_name);
+ return sizeof(*ptr);
+}
+
+static size_t tomoyo_del_manager(struct tomoyo_policy_manager_entry *ptr)
+{
+ tomoyo_put_name(ptr->manager);
+ return sizeof(*ptr);
+}
+
+/**
+ * tomoyo_used_by_task - Check whether the given pointer is referenced by a task.
+ *
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ *
+ * Returns true if @domain is in use, false otherwise.
+ */
+static bool tomoyo_used_by_task(struct tomoyo_domain_info *domain)
+{
+ bool in_use = false;
+ /*
+ * Don't delete this domain if somebody is doing execve().
+ *
+ * Since tomoyo_finish_execve() first reverts tomoyo_domain_info and
+ * then updates tomoyo_flags , we need smp_mb() to make sure that GC
+ * first checks tomoyo_flags and then checks tomoyo_domain_info .
+ */
+ struct task_struct *g;
+ struct task_struct *t;
+ read_lock(&tasklist_lock);
+ do_each_thread(g, t) {
+ if (!(t->tomoyo_flags & TOMOYO_TASK_IS_IN_EXECVE)) {
+ smp_mb(); /* Avoid out of order execution. */
+ if (t->tomoyo_domain_info != domain)
+ continue;
+ }
+ in_use = true;
+ goto out;
+ } while_each_thread(g, t);
+ out:
+ read_unlock(&tasklist_lock);
+ return in_use;
+}
+
+static size_t tomoyo_del_acl(struct tomoyo_acl_info *acl)
+{
+ size_t size;
+ tomoyo_put_condition(acl->cond);
+ switch (acl->type) {
+ case TOMOYO_TYPE_PATH_ACL:
+ {
+ struct tomoyo_path_acl *entry;
+ size = sizeof(*entry);
+ entry = container_of(acl, typeof(*entry), head);
+ tomoyo_put_name_union(&entry->name);
+ }
+ break;
+ case TOMOYO_TYPE_PATH_NUMBER3_ACL:
+ {
+ struct tomoyo_path_number3_acl *entry;
+ size = sizeof(*entry);
+ entry = container_of(acl, typeof(*entry), head);
+ tomoyo_put_name_union(&entry->name);
+ tomoyo_put_number_union(&entry->mode);
+ tomoyo_put_number_union(&entry->major);
+ tomoyo_put_number_union(&entry->minor);
+ }
+ break;
+ case TOMOYO_TYPE_PATH2_ACL:
+ {
+ struct tomoyo_path2_acl *entry;
+ size = sizeof(*entry);
+ entry = container_of(acl, typeof(*entry), head);
+ tomoyo_put_name_union(&entry->name1);
+ tomoyo_put_name_union(&entry->name2);
+ }
+ break;
+ case TOMOYO_TYPE_IP_NETWORK_ACL:
+ {
+ struct tomoyo_ip_network_acl *entry;
+ size = sizeof(*entry);
+ entry = container_of(acl, typeof(*entry), head);
+ switch (entry->address_type) {
+ case TOMOYO_IP_ADDRESS_TYPE_ADDRESS_GROUP:
+ tomoyo_put_address_group(entry->address.group);
+ break;
+ case TOMOYO_IP_ADDRESS_TYPE_IPv6:
+ tomoyo_put_ipv6_address(entry->
+ address.ipv6.min);
+ tomoyo_put_ipv6_address(entry->
+ address.ipv6.max);
+ break;
+ }
+ tomoyo_put_number_union(&entry->port);
+ }
+ break;
+ case TOMOYO_TYPE_PATH_NUMBER_ACL:
+ {
+ struct tomoyo_path_number_acl *entry;
+ size = sizeof(*entry);
+ entry = container_of(acl, typeof(*entry), head);
+ tomoyo_put_name_union(&entry->name);
+ tomoyo_put_number_union(&entry->number);
+ }
+ break;
+ case TOMOYO_TYPE_ENV_ACL:
+ {
+ struct tomoyo_env_acl *entry;
+ size = sizeof(*entry);
+ entry = container_of(acl, typeof(*entry), head);
+ tomoyo_put_name(entry->env);
+ }
+ break;
+ case TOMOYO_TYPE_CAPABILITY_ACL:
+ {
+ struct tomoyo_capability_acl *entry;
+ size = sizeof(*entry);
+ entry = container_of(acl, typeof(*entry), head);
+ }
+ break;
+ case TOMOYO_TYPE_EXECUTE_HANDLER:
+ case TOMOYO_TYPE_DENIED_EXECUTE_HANDLER:
+ {
+ struct tomoyo_execute_handler_record *entry;
+ size = sizeof(*entry);
+ entry = container_of(acl, typeof(*entry), head);
+ tomoyo_put_name(entry->handler);
+ }
+ break;
+ case TOMOYO_TYPE_MOUNT_ACL:
+ {
+ struct tomoyo_mount_acl *entry;
+ size = sizeof(*entry);
+ entry = container_of(acl, typeof(*entry), head);
+ tomoyo_put_name_union(&entry->dev_name);
+ tomoyo_put_name_union(&entry->dir_name);
+ tomoyo_put_name_union(&entry->fs_type);
+ tomoyo_put_number_union(&entry->flags);
+ }
+ break;
+ default:
+ size = 0;
+ printk(KERN_WARNING "Unknown type\n");
+ break;
+ }
+ return size;
+}
+
+static size_t tomoyo_del_domain(struct tomoyo_domain_info *domain)
+{
+ struct tomoyo_acl_info *acl;
+ struct tomoyo_acl_info *tmp;
+ if (tomoyo_used_by_task(domain))
+ return 0;
+ list_for_each_entry_safe(acl, tmp, &domain->acl_info_list, list) {
+ size_t size = tomoyo_del_acl(acl);
+ tomoyo_memory_free(acl, size);
+ }
+ tomoyo_put_name(domain->domainname);
+ return sizeof(*domain);
+}
+
+static size_t tomoyo_del_path_group_member
+(struct tomoyo_path_group_member *member)
+{
+ tomoyo_put_name(member->member_name);
+ return sizeof(*member);
+}
+
+static size_t tomoyo_del_path_group(struct tomoyo_path_group *group)
+{
+ tomoyo_put_name(group->group_name);
+ return sizeof(*group);
+}
+
+static size_t tomoyo_del_address_group_member
+(struct tomoyo_address_group_member *member)
+{
+ if (member->is_ipv6) {
+ tomoyo_put_ipv6_address(member->min.ipv6);
+ tomoyo_put_ipv6_address(member->max.ipv6);
+ }
+ return sizeof(*member);
+}
+
+static size_t tomoyo_del_address_group(struct tomoyo_address_group *group)
+{
+ tomoyo_put_name(group->group_name);
+ return sizeof(*group);
+}
+
+static size_t tomoyo_del_number_group_member
+(struct tomoyo_number_group_member *member)
+{
+ return sizeof(*member);
+}
+
+static size_t tomoyo_del_number_group(struct tomoyo_number_group *group)
+{
+ tomoyo_put_name(group->group_name);
+ return sizeof(*group);
+}
+
+/* Lock for GC. */
+static struct srcu_struct tomoyo_ss;
+
+/**
+ * tomoyo_gc_init - Initialize garbage collector.
+ *
+ * Returns 0.
+ */
+static int __init tomoyo_gc_init(void)
+{
+ if (init_srcu_struct(&tomoyo_ss))
+ panic("Out of memory.");
+ return 0;
+}
+core_initcall(tomoyo_gc_init);
+
+int tomoyo_read_lock(void)
+{
+ return srcu_read_lock(&tomoyo_ss);
+}
+
+void tomoyo_read_unlock(const int idx)
+{
+ srcu_read_unlock(&tomoyo_ss, idx);
+}
+
+static inline void tomoyo_synchronize_srcu(void)
+{
+ synchronize_srcu(&tomoyo_ss);
+}
+
+static int tomoyo_gc_thread(void *unused)
+{
+ daemonize("GC for TOMOYO");
+ if (!mutex_trylock(&tomoyo_gc_mutex))
+ goto out;
+ mutex_lock(&tomoyo_policy_lock);
+ {
+ struct tomoyo_globally_readable_file_entry *ptr;
+ list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list,
+ list) {
+ if (!ptr->is_deleted)
+ continue;
+ if (tomoyo_add_to_gc(TOMOYO_ID_GLOBALLY_READABLE, ptr))
+ list_del_rcu(&ptr->list);
+ else
+ break;
+ }
+ }
+ {
+ struct tomoyo_globally_usable_env_entry *ptr;
+ list_for_each_entry_rcu(ptr, &tomoyo_globally_usable_env_list,
+ list) {
+ if (!ptr->is_deleted)
+ continue;
+ if (tomoyo_add_to_gc(TOMOYO_ID_GLOBAL_ENV, ptr))
+ list_del_rcu(&ptr->list);
+ else
+ break;
+ }
+ }
+ {
+ struct tomoyo_pattern_entry *ptr;
+ list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) {
+ if (!ptr->is_deleted)
+ continue;
+ if (tomoyo_add_to_gc(TOMOYO_ID_PATTERN, ptr))
+ list_del_rcu(&ptr->list);
+ else
+ break;
+ }
+ }
+ {
+ struct tomoyo_no_rewrite_entry *ptr;
+ list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, list) {
+ if (!ptr->is_deleted)
+ continue;
+ if (tomoyo_add_to_gc(TOMOYO_ID_NO_REWRITE, ptr))
+ list_del_rcu(&ptr->list);
+ else
+ break;
+ }
+ }
+ {
+ struct tomoyo_domain_initializer_entry *ptr;
+ list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list,
+ list) {
+ if (!ptr->is_deleted)
+ continue;
+ if (tomoyo_add_to_gc(TOMOYO_ID_DOMAIN_INITIALIZER,
+ ptr))
+ list_del_rcu(&ptr->list);
+ else
+ break;
+ }
+ }
+ {
+ struct tomoyo_domain_keeper_entry *ptr;
+ list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list,
+ list) {
+ if (!ptr->is_deleted)
+ continue;
+ if (tomoyo_add_to_gc(TOMOYO_ID_DOMAIN_KEEPER, ptr))
+ list_del_rcu(&ptr->list);
+ else
+ break;
+ }
+ }
+ {
+ struct tomoyo_policy_manager_entry *ptr;
+ list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list,
+ list) {
+ if (!ptr->is_deleted)
+ continue;
+ if (tomoyo_add_to_gc(TOMOYO_ID_MANAGER, ptr))
+ list_del_rcu(&ptr->list);
+ else
+ break;
+ }
+ }
+ {
+ struct tomoyo_aggregator_entry *ptr;
+ list_for_each_entry_rcu(ptr, &tomoyo_aggregator_list, list) {
+ if (!ptr->is_deleted)
+ continue;
+ if (tomoyo_add_to_gc(TOMOYO_ID_AGGREGATOR, ptr))
+ list_del_rcu(&ptr->list);
+ else
+ break;
+ }
+ }
+ {
+ struct tomoyo_domain_info *domain;
+ list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
+ struct tomoyo_acl_info *acl;
+ list_for_each_entry_rcu(acl, &domain->acl_info_list,
+ list) {
+ if (!acl->is_deleted)
+ continue;
+ if (tomoyo_add_to_gc(TOMOYO_ID_ACL, acl))
+ list_del_rcu(&acl->list);
+ else
+ break;
+ }
+ if (!domain->is_deleted ||
+ tomoyo_used_by_task(domain))
+ continue;
+ if (tomoyo_add_to_gc(TOMOYO_ID_DOMAIN, domain))
+ list_del_rcu(&domain->list);
+ else
+ break;
+ }
+ }
+ {
+ struct tomoyo_path_group *group;
+ list_for_each_entry_rcu(group, &tomoyo_path_group_list, list) {
+ struct tomoyo_path_group_member *member;
+ list_for_each_entry_rcu(member, &group->member_list,
+ list) {
+ if (!member->is_deleted)
+ continue;
+ if (tomoyo_add_to_gc
+ (TOMOYO_ID_PATH_GROUP_MEMBER, member))
+ list_del_rcu(&member->list);
+ else
+ break;
+ }
+ if (!list_empty(&group->member_list) ||
+ atomic_read(&group->users))
+ continue;
+ if (tomoyo_add_to_gc(TOMOYO_ID_PATH_GROUP, group))
+ list_del_rcu(&group->list);
+ else
+ break;
+ }
+ }
+ {
+ struct tomoyo_address_group *group;
+ list_for_each_entry_rcu(group, &tomoyo_address_group_list,
+ list) {
+ struct tomoyo_address_group_member *member;
+ list_for_each_entry_rcu(member, &group->member_list,
+ list) {
+ if (!member->is_deleted)
+ break;
+ if (tomoyo_add_to_gc
+ (TOMOYO_ID_ADDRESS_GROUP_MEMBER, member))
+ list_del_rcu(&member->list);
+ else
+ break;
+ }
+ if (!list_empty(&group->member_list) ||
+ atomic_read(&group->users))
+ continue;
+ if (tomoyo_add_to_gc(TOMOYO_ID_ADDRESS_GROUP, group))
+ list_del_rcu(&group->list);
+ else
+ break;
+ }
+ }
+ {
+ struct tomoyo_number_group *group;
+ list_for_each_entry_rcu(group, &tomoyo_number_group_list,
+ list) {
+ struct tomoyo_number_group_member *member;
+ list_for_each_entry_rcu(member, &group->member_list,
+ list) {
+ if (!member->is_deleted)
+ continue;
+ if (tomoyo_add_to_gc
+ (TOMOYO_ID_NUMBER_GROUP_MEMBER, member))
+ list_del_rcu(&member->list);
+ else
+ break;
+ }
+ if (!list_empty(&group->member_list) ||
+ atomic_read(&group->users))
+ continue;
+ if (tomoyo_add_to_gc(TOMOYO_ID_NUMBER_GROUP, group))
+ list_del_rcu(&group->list);
+ else
+ break;
+ }
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+ if (list_empty(&tomoyo_gc_queue))
+ goto done;
+ tomoyo_synchronize_srcu();
+ {
+ struct tomoyo_gc_entry *p;
+ struct tomoyo_gc_entry *tmp;
+ size_t size = 0;
+ list_for_each_entry_safe(p, tmp, &tomoyo_gc_queue, list) {
+ switch (p->type) {
+ case TOMOYO_ID_DOMAIN_INITIALIZER:
+ size = tomoyo_del_domain_initializer(p->
+ element);
+ break;
+ case TOMOYO_ID_DOMAIN_KEEPER:
+ size = tomoyo_del_domain_keeper(p->element);
+ break;
+ case TOMOYO_ID_GLOBALLY_READABLE:
+ size = tomoyo_del_allow_read(p->element);
+ break;
+ case TOMOYO_ID_PATTERN:
+ size = tomoyo_del_file_pattern(p->element);
+ break;
+ case TOMOYO_ID_NO_REWRITE:
+ size = tomoyo_del_no_rewrite(p->element);
+ break;
+ case TOMOYO_ID_MANAGER:
+ size = tomoyo_del_manager(p->element);
+ break;
+ case TOMOYO_ID_GLOBAL_ENV:
+ size = tomoyo_del_allow_env(p->element);
+ break;
+ case TOMOYO_ID_AGGREGATOR:
+ size = tomoyo_del_aggregator(p->element);
+ break;
+ case TOMOYO_ID_PATH_GROUP_MEMBER:
+ size = tomoyo_del_path_group_member(p->
+ element);
+ break;
+ case TOMOYO_ID_PATH_GROUP:
+ size = tomoyo_del_path_group(p->element);
+ break;
+ case TOMOYO_ID_ADDRESS_GROUP_MEMBER:
+ size = tomoyo_del_address_group_member(p->
+ element
+ );
+ break;
+ case TOMOYO_ID_ADDRESS_GROUP:
+ size = tomoyo_del_address_group(p->element);
+ break;
+ case TOMOYO_ID_NUMBER_GROUP_MEMBER:
+ size = tomoyo_del_number_group_member(p->
+ element);
+ break;
+ case TOMOYO_ID_NUMBER_GROUP:
+ size = tomoyo_del_number_group(p->element);
+ break;
+ case TOMOYO_ID_ACL:
+ size = tomoyo_del_acl(p->element);
+ break;
+ case TOMOYO_ID_DOMAIN:
+ size = tomoyo_del_domain(p->element);
+ if (!size)
+ continue;
+ break;
+ default:
+ size = 0;
+ printk(KERN_WARNING "Unknown type\n");
+ break;
+ }
+ tomoyo_memory_free(p->element, size);
+ list_del(&p->list);
+ kfree(p);
+ }
+ }
+ done:
+ mutex_unlock(&tomoyo_gc_mutex);
+ out:
+ do_exit(0);
+}
+
+void tomoyo_run_gc(void)
+{
+ struct task_struct *task = kthread_create(tomoyo_gc_thread, NULL,
+ "GC for TOMOYO");
+ if (!IS_ERR(task))
+ wake_up_process(task);
+}
--
^ permalink raw reply [flat|nested] 50+ messages in thread
* [TOMOYO #16 14/25] TOMOYO: Add network restriction.
2009-10-04 12:49 [TOMOYO #16 00/25] Starting TOMOYO 2.3 Tetsuo Handa
` (12 preceding siblings ...)
2009-10-04 12:49 ` [TOMOYO #16 13/25] TOMOYO: Add garbage collector support Tetsuo Handa
@ 2009-10-04 12:50 ` Tetsuo Handa
2009-10-04 12:50 ` [TOMOYO #16 15/25] TOMOYO: Add mount restriction Tetsuo Handa
` (11 subsequent siblings)
25 siblings, 0 replies; 50+ messages in thread
From: Tetsuo Handa @ 2009-10-04 12:50 UTC (permalink / raw)
To: linux-security-module; +Cc: linux-kernel, Tetsuo Handa
[-- Attachment #1: tomoyo-add-network.patch --]
[-- Type: text/plain, Size: 21838 bytes --]
This patch contains code for restricting IPV4/IPv6 network communications.
The way how LSM version performs incoming TCP connections filtering (a.k.a.
post accept() hook) is different from non-LSM version.
LSM version does not add post accept() hook. Instead, do connection filtering
upon first use of an accept()ed socket.
As of now, LSM version does not perform incoming UDP/RAW packets filtering
(a.k.a. post recvmsg() hook). I want to implement it in the future.
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
security/tomoyo/network.c | 757 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 757 insertions(+)
--- /dev/null
+++ security-testing-2.6/security/tomoyo/network.c
@@ -0,0 +1,757 @@
+/*
+ * security/tomoyo/network.c
+ *
+ * Copyright (C) 2005-2009 NTT DATA CORPORATION
+ */
+#include "internal.h"
+#include <net/ipv6.h>
+
+/**
+ * tomoyo_audit_network_log - Audit network log.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ * @operation: The name of operation.
+ * @address: An IPv4 or IPv6 address.
+ * @port: Port number.
+ * @is_granted: True if this is a granted log.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_audit_network_log(struct tomoyo_request_info *r,
+ const char *operation, const char *address,
+ const u16 port, const bool is_granted)
+{
+ if (!is_granted)
+ tomoyo_warn_log(r, "%s %s %u", operation, address, port);
+ return tomoyo_write_audit_log(is_granted, r,
+ TOMOYO_KEYWORD_ALLOW_NETWORK
+ "%s %s %u\n", operation, address, port);
+}
+
+/**
+ * tomoyo_parse_ip_address - Parse an IP address.
+ *
+ * @address: String to parse.
+ * @min: Pointer to store min address.
+ * @max: Pointer to store max address.
+ *
+ * Returns 2 if @address is an IPv6, 1 if @address is an IPv4, 0 otherwise.
+ */
+int tomoyo_parse_ip_address(char *address, u16 *min, u16 *max)
+{
+ int count = sscanf(address, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx"
+ "-%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx",
+ &min[0], &min[1], &min[2], &min[3],
+ &min[4], &min[5], &min[6], &min[7],
+ &max[0], &max[1], &max[2], &max[3],
+ &max[4], &max[5], &max[6], &max[7]);
+ if (count == 8 || count == 16) {
+ u8 i;
+ if (count == 8)
+ memmove(max, min, sizeof(u16) * 8);
+ for (i = 0; i < 8; i++) {
+ min[i] = htons(min[i]);
+ max[i] = htons(max[i]);
+ }
+ return 2;
+ }
+ count = sscanf(address, "%hu.%hu.%hu.%hu-%hu.%hu.%hu.%hu",
+ &min[0], &min[1], &min[2], &min[3],
+ &max[0], &max[1], &max[2], &max[3]);
+ if (count == 4 || count == 8) {
+ u32 ip = htonl((((u8) min[0]) << 24) + (((u8) min[1]) << 16)
+ + (((u8) min[2]) << 8) + (u8) min[3]);
+ memmove(min, &ip, sizeof(ip));
+ if (count == 8)
+ ip = htonl((((u8) max[0]) << 24) +
+ (((u8) max[1]) << 16) + (((u8) max[2]) << 8)
+ + (u8) max[3]);
+ memmove(max, &ip, sizeof(ip));
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * tomoyo_print_ipv6 - Print an IPv6 address.
+ *
+ * @buffer: Buffer to write to.
+ * @buffer_len: Size of @buffer.
+ * @ip: Pointer to "struct in6_addr".
+ *
+ * This is different from "%pI6" and "%pI6c".
+ *
+ * Returns nothing.
+ */
+void tomoyo_print_ipv6(char *buffer, const int buffer_len,
+ const struct in6_addr *ip)
+{
+ memset(buffer, 0, buffer_len);
+ snprintf(buffer, buffer_len - 1, "%x:%x:%x:%x:%x:%x:%x:%x",
+ ntohs(ip->s6_addr16[0]), ntohs(ip->s6_addr16[1]),
+ ntohs(ip->s6_addr16[2]), ntohs(ip->s6_addr16[3]),
+ ntohs(ip->s6_addr16[4]), ntohs(ip->s6_addr16[5]),
+ ntohs(ip->s6_addr16[6]), ntohs(ip->s6_addr16[7]));
+}
+
+/**
+ * tomoyo_net2keyword - Convert network operation index to network operation name.
+ *
+ * @operation: Type of operation.
+ *
+ * Returns the name of operation.
+ */
+const char *tomoyo_net2keyword(const u8 operation)
+{
+ const char *keyword = "unknown";
+ switch (operation) {
+ case TOMOYO_NETWORK_UDP_BIND:
+ keyword = "UDP bind";
+ break;
+ case TOMOYO_NETWORK_UDP_CONNECT:
+ keyword = "UDP connect";
+ break;
+ case TOMOYO_NETWORK_TCP_BIND:
+ keyword = "TCP bind";
+ break;
+ case TOMOYO_NETWORK_TCP_LISTEN:
+ keyword = "TCP listen";
+ break;
+ case TOMOYO_NETWORK_TCP_CONNECT:
+ keyword = "TCP connect";
+ break;
+ case TOMOYO_NETWORK_TCP_ACCEPT:
+ keyword = "TCP accept";
+ break;
+ case TOMOYO_NETWORK_RAW_BIND:
+ keyword = "RAW bind";
+ break;
+ case TOMOYO_NETWORK_RAW_CONNECT:
+ keyword = "RAW connect";
+ break;
+ }
+ return keyword;
+}
+
+/**
+ * tomoyo_network_entry2 - Check permission for network operation.
+ *
+ * @is_ipv6: True if @address is an IPv6 address.
+ * @operation: Type of operation.
+ * @address: An IPv4 or IPv6 address.
+ * @port: Port number.
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static int tomoyo_network_entry2(const bool is_ipv6, const u8 operation,
+ const u32 *address, const u16 port)
+{
+ struct tomoyo_request_info r;
+ struct tomoyo_acl_info *ptr;
+ const char *keyword = tomoyo_net2keyword(operation);
+ const u16 perm = 1 << operation;
+ /* using host byte order to allow u32 comparison than memcmp().*/
+ const u32 ip = ntohl(*address);
+ int error;
+ char buf[64];
+ if (tomoyo_init_request_info(&r, NULL,
+ TOMOYO_MAC_NETWORK_UDP_BIND + operation)
+ == TOMOYO_CONFIG_DISABLED)
+ return 0;
+ memset(buf, 0, sizeof(buf));
+ if (is_ipv6)
+ tomoyo_print_ipv6(buf, sizeof(buf), (const struct in6_addr *)
+ address);
+ else
+ snprintf(buf, sizeof(buf) - 1, "%u.%u.%u.%u", HIPQUAD(ip));
+ do {
+ error = -EPERM;
+ list_for_each_entry_rcu(ptr, &r.domain->acl_info_list, list) {
+ struct tomoyo_ip_network_acl *acl;
+ if (ptr->is_deleted ||
+ ptr->type != TOMOYO_TYPE_IP_NETWORK_ACL)
+ continue;
+ acl = container_of(ptr, struct tomoyo_ip_network_acl,
+ head);
+ if (!(acl->perm & perm))
+ continue;
+ if (!tomoyo_compare_number_union(port, &acl->port) ||
+ !tomoyo_condition(&r, ptr))
+ continue;
+ switch (acl->address_type) {
+ case TOMOYO_IP_ADDRESS_TYPE_ADDRESS_GROUP:
+ if (!tomoyo_address_matches_group(is_ipv6,
+ address,
+ acl->address.
+ group))
+ continue;
+ break;
+ case TOMOYO_IP_ADDRESS_TYPE_IPv4:
+ if (is_ipv6 || ip < acl->address.ipv4.min ||
+ acl->address.ipv4.max < ip)
+ continue;
+ break;
+ default:
+ if (!is_ipv6 ||
+ memcmp(acl->address.ipv6.min, address, 16)
+ > 0 ||
+ memcmp(address, acl->address.ipv6.max, 16)
+ > 0)
+ continue;
+ break;
+ }
+ r.cond = ptr->cond;
+ error = 0;
+ break;
+ }
+ tomoyo_audit_network_log(&r, keyword, buf, port, !error);
+ if (!error)
+ break;
+ error = tomoyo_supervisor(&r, TOMOYO_KEYWORD_ALLOW_NETWORK
+ "%s %s %u\n", keyword, buf, port);
+ } while (error == 1);
+ if (r.mode != TOMOYO_CONFIG_ENFORCING)
+ error = 0;
+ return error;
+}
+
+/**
+ * tomoyo_network_entry - Check permission for network operation.
+ *
+ * @is_ipv6: True if @address is an IPv6 address.
+ * @operation: Type of operation.
+ * @address: An IPv4 or IPv6 address.
+ * @port: Port number.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_network_entry(const bool is_ipv6, const u8 operation,
+ const u32 *address, const u16 port)
+{
+ const int idx = tomoyo_read_lock();
+ const int error = tomoyo_network_entry2(is_ipv6, operation, address,
+ port);
+ tomoyo_read_unlock(idx);
+ return error;
+}
+
+/**
+ * tomoyo_write_network_policy - Write "struct tomoyo_ip_network_acl" list.
+ *
+ * @data: String to parse.
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @condition: Pointer to "struct tomoyo_condition". May be NULL.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_write_network_policy(char *data, struct tomoyo_domain_info *domain,
+ struct tomoyo_condition *condition,
+ const bool is_delete)
+{
+ struct tomoyo_ip_network_acl *entry = NULL;
+ struct tomoyo_acl_info *ptr;
+ struct tomoyo_ip_network_acl e = {
+ .head.type = TOMOYO_TYPE_IP_NETWORK_ACL,
+ .head.cond = condition,
+ };
+ u16 min_address[8];
+ u16 max_address[8];
+ int error = is_delete ? -ENOENT : -ENOMEM;
+ u8 sock_type;
+ char *w[4];
+ if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[3][0])
+ return -EINVAL;
+ if (!strcmp(w[0], "TCP"))
+ sock_type = SOCK_STREAM;
+ else if (!strcmp(w[0], "UDP"))
+ sock_type = SOCK_DGRAM;
+ else if (!strcmp(w[0], "RAW"))
+ sock_type = SOCK_RAW;
+ else
+ return -EINVAL;
+ if (!strcmp(w[1], "bind"))
+ switch (sock_type) {
+ case SOCK_STREAM:
+ e.perm = 1 << TOMOYO_NETWORK_TCP_BIND;
+ break;
+ case SOCK_DGRAM:
+ e.perm = 1 << TOMOYO_NETWORK_UDP_BIND;
+ break;
+ default:
+ e.perm = 1 << TOMOYO_NETWORK_RAW_BIND;
+ break;
+ }
+ else if (!strcmp(w[1], "connect"))
+ switch (sock_type) {
+ case SOCK_STREAM:
+ e.perm = 1 << TOMOYO_NETWORK_TCP_CONNECT;
+ break;
+ case SOCK_DGRAM:
+ e.perm = 1 << TOMOYO_NETWORK_UDP_CONNECT;
+ break;
+ default:
+ e.perm = 1 << TOMOYO_NETWORK_RAW_CONNECT;
+ break;
+ }
+ else if (sock_type == SOCK_STREAM && !strcmp(w[1], "listen"))
+ e.perm = 1 << TOMOYO_NETWORK_TCP_LISTEN;
+ else if (sock_type == SOCK_STREAM && !strcmp(w[1], "accept"))
+ e.perm = 1 << TOMOYO_NETWORK_TCP_ACCEPT;
+ else
+ return -EINVAL;
+ switch (tomoyo_parse_ip_address(w[2], min_address, max_address)) {
+ case 2:
+ e.address_type = TOMOYO_IP_ADDRESS_TYPE_IPv6;
+ e.address.ipv6.min =
+ tomoyo_get_ipv6_address((struct in6_addr *)
+ min_address);
+ e.address.ipv6.max =
+ tomoyo_get_ipv6_address((struct in6_addr *)
+ max_address);
+ if (!e.address.ipv6.min || !e.address.ipv6.max)
+ goto out;
+ break;
+ case 1:
+ e.address_type = TOMOYO_IP_ADDRESS_TYPE_IPv4;
+ /* use host byte order to allow u32 comparison.*/
+ e.address.ipv4.min = ntohl(*(u32 *) min_address);
+ e.address.ipv4.max = ntohl(*(u32 *) max_address);
+ break;
+ default:
+ if (w[2][0] != '@')
+ return -EINVAL;
+ e.address_type = TOMOYO_IP_ADDRESS_TYPE_ADDRESS_GROUP;
+ e.address.group = tomoyo_get_address_group(w[2] + 1);
+ if (!e.address.group)
+ return -ENOMEM;
+ break;
+ }
+ if (!tomoyo_parse_number_union(w[3], &e.port))
+ goto out;
+ if (!is_delete)
+ entry = kmalloc(sizeof(e), GFP_KERNEL);
+ mutex_lock(&tomoyo_policy_lock);
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
+ struct tomoyo_ip_network_acl *acl =
+ container_of(ptr, struct tomoyo_ip_network_acl,
+ head);
+ if (ptr->type != TOMOYO_TYPE_IP_NETWORK_ACL ||
+ ptr->cond != condition ||
+ tomoyo_memcmp(acl, &e, offsetof(typeof(e), address_type),
+ sizeof(e)))
+ continue;
+ if (is_delete) {
+ acl->perm &= ~e.perm;
+ if (!acl->perm)
+ ptr->is_deleted = true;
+ } else {
+ if (ptr->is_deleted)
+ acl->perm = 0;
+ acl->perm |= e.perm;
+ ptr->is_deleted = false;
+ }
+ error = 0;
+ break;
+ }
+ if (!is_delete && error && tomoyo_commit_ok(entry, &e, sizeof(e))) {
+ tomoyo_add_domain_acl(domain, &entry->head);
+ entry = NULL;
+ error = 0;
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+ out:
+ if (w[2][0] == '@')
+ tomoyo_put_address_group(e.address.group);
+ else if (e.address_type == TOMOYO_IP_ADDRESS_TYPE_IPv6) {
+ tomoyo_put_ipv6_address(e.address.ipv6.min);
+ tomoyo_put_ipv6_address(e.address.ipv6.max);
+ }
+ tomoyo_put_number_union(&e.port);
+ kfree(entry);
+ return error;
+}
+
+/**
+ * tomoyo_network_listen_acl - Check permission for listen() operation.
+ *
+ * @is_ipv6: True if @address is an IPv6 address.
+ * @address: An IPv4 or IPv6 address.
+ * @port: Port number.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static inline int tomoyo_network_listen_acl(const bool is_ipv6,
+ const u8 *address, const u16 port)
+{
+ return tomoyo_network_entry(is_ipv6, TOMOYO_NETWORK_TCP_LISTEN,
+ (const u32 *) address, ntohs(port));
+}
+
+/**
+ * tomoyo_network_connect_acl - Check permission for connect() operation.
+ *
+ * @is_ipv6: True if @address is an IPv6 address.
+ * @sock_type: Type of socket. (TCP or UDP or RAW)
+ * @address: An IPv4 or IPv6 address.
+ * @port: Port number.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static inline int tomoyo_network_connect_acl(const bool is_ipv6,
+ const int sock_type,
+ const u8 *address, const u16 port)
+{
+ u8 operation;
+ switch (sock_type) {
+ case SOCK_STREAM:
+ operation = TOMOYO_NETWORK_TCP_CONNECT;
+ break;
+ case SOCK_DGRAM:
+ operation = TOMOYO_NETWORK_UDP_CONNECT;
+ break;
+ default:
+ operation = TOMOYO_NETWORK_RAW_CONNECT;
+ }
+ return tomoyo_network_entry(is_ipv6, operation,
+ (const u32 *) address, ntohs(port));
+}
+
+/**
+ * tomoyo_network_bind_acl - Check permission for bind() operation.
+ *
+ * @is_ipv6: True if @address is an IPv6 address.
+ * @sock_type: Type of socket. (TCP or UDP or RAW)
+ * @address: An IPv4 or IPv6 address.
+ * @port: Port number.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_network_bind_acl(const bool is_ipv6, const int sock_type,
+ const u8 *address, const u16 port)
+{
+ u8 operation;
+ switch (sock_type) {
+ case SOCK_STREAM:
+ operation = TOMOYO_NETWORK_TCP_BIND;
+ break;
+ case SOCK_DGRAM:
+ operation = TOMOYO_NETWORK_UDP_BIND;
+ break;
+ default:
+ operation = TOMOYO_NETWORK_RAW_BIND;
+ }
+ return tomoyo_network_entry(is_ipv6, operation,
+ (const u32 *) address, ntohs(port));
+}
+
+/**
+ * tomoyo_network_accept_acl - Check permission for accept() operation.
+ *
+ * @is_ipv6: True if @address is an IPv6 address.
+ * @address: An IPv4 or IPv6 address.
+ * @port: Port number.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static inline int tomoyo_network_accept_acl(const bool is_ipv6,
+ const u8 *address, const u16 port)
+{
+ int retval;
+ current->tomoyo_flags |= TOMOYO_DONT_SLEEP_ON_ENFORCE_ERROR;
+ retval = tomoyo_network_entry(is_ipv6, TOMOYO_NETWORK_TCP_ACCEPT,
+ (const u32 *) address, ntohs(port));
+ current->tomoyo_flags &= ~TOMOYO_DONT_SLEEP_ON_ENFORCE_ERROR;
+ return retval;
+}
+
+/**
+ * tomoyo_network_sendmsg_acl - Check permission for sendmsg() operation.
+ *
+ * @is_ipv6: True if @address is an IPv6 address.
+ * @sock_type: Type of socket. (UDP or RAW)
+ * @address: An IPv4 or IPv6 address.
+ * @port: Port number.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static inline int tomoyo_network_sendmsg_acl(const bool is_ipv6,
+ const int sock_type,
+ const u8 *address, const u16 port)
+{
+ u8 operation;
+ if (sock_type == SOCK_DGRAM)
+ operation = TOMOYO_NETWORK_UDP_CONNECT;
+ else
+ operation = TOMOYO_NETWORK_RAW_CONNECT;
+ return tomoyo_network_entry(is_ipv6, operation,
+ (const u32 *) address, ntohs(port));
+}
+
+#define MAX_SOCK_ADDR 128 /* net/socket.c */
+
+/* Check permission for creating a socket. */
+int tomoyo_socket_create_permission(int family, int type, int protocol)
+{
+ int error = 0;
+ /* Nothing to do if I am a kernel service. */
+ if (segment_eq(get_fs(), KERNEL_DS))
+ return 0;
+ if (family == PF_PACKET && !tomoyo_capable(TOMOYO_USE_PACKET_SOCKET))
+ return -EPERM;
+ if (family == PF_ROUTE && !tomoyo_capable(TOMOYO_USE_ROUTE_SOCKET))
+ return -EPERM;
+ if (family != PF_INET && family != PF_INET6)
+ return 0;
+ switch (type) {
+ case SOCK_STREAM:
+ if (!tomoyo_capable(TOMOYO_INET_STREAM_SOCKET_CREATE))
+ error = -EPERM;
+ break;
+ case SOCK_DGRAM:
+ if (!tomoyo_capable(TOMOYO_USE_INET_DGRAM_SOCKET))
+ error = -EPERM;
+ break;
+ case SOCK_RAW:
+ if (!tomoyo_capable(TOMOYO_USE_INET_RAW_SOCKET))
+ error = -EPERM;
+ break;
+ }
+ return error;
+}
+
+/* Check permission for listening a TCP socket. */
+int tomoyo_socket_listen_permission(struct socket *sock)
+{
+ int error = 0;
+ char addr[MAX_SOCK_ADDR];
+ int addr_len;
+ /* Nothing to do if I am a kernel service. */
+ if (segment_eq(get_fs(), KERNEL_DS))
+ return 0;
+ if (sock->type != SOCK_STREAM)
+ return 0;
+ switch (sock->sk->sk_family) {
+ case PF_INET:
+ case PF_INET6:
+ break;
+ default:
+ return 0;
+ }
+ if (!tomoyo_capable(TOMOYO_INET_STREAM_SOCKET_LISTEN))
+ return -EPERM;
+ if (sock->ops->getname(sock, (struct sockaddr *) addr, &addr_len, 0))
+ return -EPERM;
+ switch (((struct sockaddr *) addr)->sa_family) {
+ struct sockaddr_in6 *addr6;
+ struct sockaddr_in *addr4;
+ case AF_INET6:
+ addr6 = (struct sockaddr_in6 *) addr;
+ error = tomoyo_network_listen_acl(true,
+ addr6->sin6_addr.s6_addr,
+ addr6->sin6_port);
+ break;
+ case AF_INET:
+ addr4 = (struct sockaddr_in *) addr;
+ error = tomoyo_network_listen_acl(false,
+ (u8 *) &addr4->sin_addr,
+ addr4->sin_port);
+ break;
+ }
+ return error;
+}
+
+/* Check permission for setting the remote IP address/port pair of a socket. */
+int tomoyo_socket_connect_permission(struct socket *sock,
+ struct sockaddr *addr, int addr_len)
+{
+ int error = 0;
+ const unsigned int type = sock->type;
+ /* Nothing to do if I am a kernel service. */
+ if (segment_eq(get_fs(), KERNEL_DS))
+ return 0;
+ switch (type) {
+ case SOCK_STREAM:
+ case SOCK_DGRAM:
+ case SOCK_RAW:
+ break;
+ default:
+ return 0;
+ }
+ switch (addr->sa_family) {
+ struct sockaddr_in6 *addr6;
+ struct sockaddr_in *addr4;
+ u16 port;
+ case AF_INET6:
+ if (addr_len < SIN6_LEN_RFC2133)
+ break;
+ addr6 = (struct sockaddr_in6 *) addr;
+ if (type != SOCK_RAW)
+ port = addr6->sin6_port;
+ else
+ port = htons(sock->sk->sk_protocol);
+ error = tomoyo_network_connect_acl(true, type,
+ addr6->sin6_addr.s6_addr,
+ port);
+ break;
+ case AF_INET:
+ if (addr_len < sizeof(struct sockaddr_in))
+ break;
+ addr4 = (struct sockaddr_in *) addr;
+ if (type != SOCK_RAW)
+ port = addr4->sin_port;
+ else
+ port = htons(sock->sk->sk_protocol);
+ error = tomoyo_network_connect_acl(false, type,
+ (u8 *) &addr4->sin_addr,
+ port);
+ break;
+ }
+ if (type != SOCK_STREAM)
+ return error;
+ switch (sock->sk->sk_family) {
+ case PF_INET:
+ case PF_INET6:
+ if (!tomoyo_capable(TOMOYO_INET_STREAM_SOCKET_CONNECT))
+ error = -EPERM;
+ break;
+ }
+ return error;
+}
+
+/* Check permission for setting the local IP address/port pair of a socket. */
+int tomoyo_socket_bind_permission(struct socket *sock, struct sockaddr *addr,
+ int addr_len)
+{
+ int error = 0;
+ const unsigned int type = sock->type;
+ /* Nothing to do if I am a kernel service. */
+ if (segment_eq(get_fs(), KERNEL_DS))
+ return 0;
+ switch (type) {
+ case SOCK_STREAM:
+ case SOCK_DGRAM:
+ case SOCK_RAW:
+ break;
+ default:
+ return 0;
+ }
+ switch (addr->sa_family) {
+ struct sockaddr_in6 *addr6;
+ struct sockaddr_in *addr4;
+ u16 port;
+ case AF_INET6:
+ if (addr_len < SIN6_LEN_RFC2133)
+ break;
+ addr6 = (struct sockaddr_in6 *) addr;
+ if (type != SOCK_RAW)
+ port = addr6->sin6_port;
+ else
+ port = htons(sock->sk->sk_protocol);
+ error = tomoyo_network_bind_acl(true, type,
+ addr6->sin6_addr.s6_addr,
+ port);
+ break;
+ case AF_INET:
+ if (addr_len < sizeof(struct sockaddr_in))
+ break;
+ addr4 = (struct sockaddr_in *) addr;
+ if (type != SOCK_RAW)
+ port = addr4->sin_port;
+ else
+ port = htons(sock->sk->sk_protocol);
+ error = tomoyo_network_bind_acl(false, type,
+ (u8 *) &addr4->sin_addr, port);
+ break;
+ }
+ return error;
+}
+
+/*
+ * Check permission for accepting a TCP socket.
+ *
+ * This hook is called by socket calls after accept().
+ */
+int tomoyo_socket_accept_permission(struct socket *sock)
+{
+ int error = 0;
+ int addr_len;
+ struct sockaddr_storage addr;
+ /* Nothing to do if I am a kernel service. */
+ if (segment_eq(get_fs(), KERNEL_DS))
+ return 0;
+ switch (sock->sk->sk_family) {
+ case PF_INET:
+ case PF_INET6:
+ break;
+ default:
+ return 0;
+ }
+ error = sock->ops->getname(sock, (struct sockaddr *) &addr, &addr_len,
+ 2);
+ if (error)
+ return error;
+ switch (((struct sockaddr *) &addr)->sa_family) {
+ struct sockaddr_in6 *addr6;
+ struct sockaddr_in *addr4;
+ case AF_INET6:
+ addr6 = (struct sockaddr_in6 *) &addr;
+ error = tomoyo_network_accept_acl(true,
+ addr6->sin6_addr.s6_addr,
+ addr6->sin6_port);
+ break;
+ case AF_INET:
+ addr4 = (struct sockaddr_in *) &addr;
+ error = tomoyo_network_accept_acl(false,
+ (u8 *) &addr4->sin_addr,
+ addr4->sin_port);
+ break;
+ }
+ return error;
+}
+
+/* Check permission for sending a datagram via a UDP or RAW socket. */
+int tomoyo_socket_sendmsg_permission(struct socket *sock, struct msghdr *msg)
+{
+ struct sockaddr *addr = msg->msg_name;
+ const int addr_len = msg->msg_namelen;
+ int error = 0;
+ const int type = sock->type;
+ /* Nothing to do if I am a kernel service. */
+ if (segment_eq(get_fs(), KERNEL_DS))
+ return 0;
+ if (!addr || (type != SOCK_DGRAM && type != SOCK_RAW))
+ return 0;
+ switch (addr->sa_family) {
+ struct sockaddr_in6 *addr6;
+ struct sockaddr_in *addr4;
+ u16 port;
+ case AF_INET6:
+ if (addr_len < SIN6_LEN_RFC2133)
+ break;
+ addr6 = (struct sockaddr_in6 *) addr;
+ if (type == SOCK_DGRAM)
+ port = addr6->sin6_port;
+ else
+ port = htons(sock->sk->sk_protocol);
+ error = tomoyo_network_sendmsg_acl(true, type,
+ addr6->sin6_addr.s6_addr,
+ port);
+ break;
+ case AF_INET:
+ if (addr_len < sizeof(struct sockaddr_in))
+ break;
+ addr4 = (struct sockaddr_in *) addr;
+ if (type == SOCK_DGRAM)
+ port = addr4->sin_port;
+ else
+ port = htons(sock->sk->sk_protocol);
+ error = tomoyo_network_sendmsg_acl(false, type,
+ (u8 *) &addr4->sin_addr,
+ port);
+ break;
+ }
+ return error;
+}
--
^ permalink raw reply [flat|nested] 50+ messages in thread
* [TOMOYO #16 15/25] TOMOYO: Add mount restriction.
2009-10-04 12:49 [TOMOYO #16 00/25] Starting TOMOYO 2.3 Tetsuo Handa
` (13 preceding siblings ...)
2009-10-04 12:50 ` [TOMOYO #16 14/25] TOMOYO: Add network restriction Tetsuo Handa
@ 2009-10-04 12:50 ` Tetsuo Handa
2009-10-04 12:50 ` [TOMOYO #16 16/25] TOMOYO: Add environment variables restriction Tetsuo Handa
` (10 subsequent siblings)
25 siblings, 0 replies; 50+ messages in thread
From: Tetsuo Handa @ 2009-10-04 12:50 UTC (permalink / raw)
To: linux-security-module; +Cc: linux-kernel, Tetsuo Handa
[-- Attachment #1: tomoyo-add-mount.patch --]
[-- Type: text/plain, Size: 12248 bytes --]
This patch contains code for checking mount operations.
Checking mount operations is a part of checking file operations, but the code
is separated from file access because the code is complicated.
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
security/tomoyo/mount.c | 366 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 366 insertions(+)
--- /dev/null
+++ security-testing-2.6/security/tomoyo/mount.c
@@ -0,0 +1,366 @@
+/*
+ * security/tomoyo/mount.c
+ *
+ * Copyright (C) 2005-2009 NTT DATA CORPORATION
+ */
+#include "internal.h"
+
+/* Keywords for mount restrictions. */
+
+/* Allow to call 'mount --bind /source_dir /dest_dir' */
+#define TOMOYO_MOUNT_BIND_KEYWORD "--bind"
+/* Allow to call 'mount --move /old_dir /new_dir ' */
+#define TOMOYO_MOUNT_MOVE_KEYWORD "--move"
+/* Allow to call 'mount -o remount /dir ' */
+#define TOMOYO_MOUNT_REMOUNT_KEYWORD "--remount"
+/* Allow to call 'mount --make-unbindable /dir' */
+#define TOMOYO_MOUNT_MAKE_UNBINDABLE_KEYWORD "--make-unbindable"
+/* Allow to call 'mount --make-private /dir' */
+#define TOMOYO_MOUNT_MAKE_PRIVATE_KEYWORD "--make-private"
+/* Allow to call 'mount --make-slave /dir' */
+#define TOMOYO_MOUNT_MAKE_SLAVE_KEYWORD "--make-slave"
+/* Allow to call 'mount --make-shared /dir' */
+#define TOMOYO_MOUNT_MAKE_SHARED_KEYWORD "--make-shared"
+
+/**
+ * tomoyo_audit_mount_log - Audit mount log.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ * @dev_name: Device file.
+ * @dir_name: Mount point.
+ * @type: Filesystem type.
+ * @flags: Mount flags.
+ * @is_granted: True if this is a granted log.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_audit_mount_log(struct tomoyo_request_info *r,
+ const char *dev_name, const char *dir_name,
+ const char *type, const unsigned long flags,
+ const bool is_granted)
+{
+ if (!is_granted) {
+ if (!strcmp(type, TOMOYO_MOUNT_REMOUNT_KEYWORD))
+ tomoyo_warn_log(r, "mount -o remount %s 0x%lX",
+ dir_name, flags);
+ else if (!strcmp(type, TOMOYO_MOUNT_BIND_KEYWORD)
+ || !strcmp(type, TOMOYO_MOUNT_MOVE_KEYWORD))
+ tomoyo_warn_log(r, "mount %s %s %s 0x%lX", type,
+ dev_name, dir_name, flags);
+ else if (!strcmp(type, TOMOYO_MOUNT_MAKE_UNBINDABLE_KEYWORD) ||
+ !strcmp(type, TOMOYO_MOUNT_MAKE_PRIVATE_KEYWORD) ||
+ !strcmp(type, TOMOYO_MOUNT_MAKE_SLAVE_KEYWORD) ||
+ !strcmp(type, TOMOYO_MOUNT_MAKE_SHARED_KEYWORD))
+ tomoyo_warn_log(r, "mount %s %s 0x%lX", type, dir_name,
+ flags);
+ else
+ tomoyo_warn_log(r, "mount -t %s %s %s 0x%lX", type,
+ dev_name, dir_name, flags);
+ }
+ return tomoyo_write_audit_log(is_granted, r, TOMOYO_KEYWORD_ALLOW_MOUNT
+ "%s %s %s 0x%lu\n", dev_name, dir_name,
+ type, flags);
+}
+
+/**
+ * tomoyo_mount_acl2 - Check permission for mount() operation.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ * @dev_name: Name of device file.
+ * @dir: Pointer to "struct path".
+ * @type: Name of filesystem type.
+ * @flags: Mount options.
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static int tomoyo_mount_acl2(struct tomoyo_request_info *r, char *dev_name,
+ struct path *dir, char *type, unsigned long flags)
+{
+ struct tomoyo_obj_info obj = { };
+ struct path path;
+ struct tomoyo_acl_info *ptr;
+ struct file_system_type *fstype = NULL;
+ const char *requested_type = NULL;
+ const char *requested_dir_name = NULL;
+ const char *requested_dev_name = NULL;
+ struct tomoyo_path_info rtype;
+ struct tomoyo_path_info rdev;
+ struct tomoyo_path_info rdir;
+ int need_dev = 0;
+ int error = -ENOMEM;
+ r->obj = &obj;
+
+ /* Get fstype. */
+ requested_type = tomoyo_encode(type);
+ if (!requested_type)
+ goto out;
+ rtype.name = requested_type;
+ tomoyo_fill_path_info(&rtype);
+
+ /* Get mount point. */
+ obj.path2 = *dir;
+ requested_dir_name = tomoyo_realpath_from_path(dir);
+ if (!requested_dir_name) {
+ error = -ENOMEM;
+ goto out;
+ }
+ rdir.name = requested_dir_name;
+ tomoyo_fill_path_info(&rdir);
+
+ /* Compare fs name. */
+ if (!strcmp(type, TOMOYO_MOUNT_REMOUNT_KEYWORD)) {
+ /* dev_name is ignored. */
+ } else if (!strcmp(type, TOMOYO_MOUNT_MAKE_UNBINDABLE_KEYWORD) ||
+ !strcmp(type, TOMOYO_MOUNT_MAKE_PRIVATE_KEYWORD) ||
+ !strcmp(type, TOMOYO_MOUNT_MAKE_SLAVE_KEYWORD) ||
+ !strcmp(type, TOMOYO_MOUNT_MAKE_SHARED_KEYWORD)) {
+ /* dev_name is ignored. */
+ } else if (!strcmp(type, TOMOYO_MOUNT_BIND_KEYWORD) ||
+ !strcmp(type, TOMOYO_MOUNT_MOVE_KEYWORD)) {
+ need_dev = -1; /* dev_name is a directory */
+ } else {
+ fstype = get_fs_type(type);
+ if (!fstype) {
+ error = -ENODEV;
+ goto out;
+ }
+ if (fstype->fs_flags & FS_REQUIRES_DEV)
+ /* dev_name is a block device file. */
+ need_dev = 1;
+ }
+ if (need_dev) {
+ /* Get mount point or device file. */
+ if (tomoyo_get_path(dev_name, &path)) {
+ error = -ENOENT;
+ goto out;
+ }
+ obj.path1 = path;
+ requested_dev_name = tomoyo_realpath_from_path(&path);
+ if (!requested_dev_name) {
+ error = -ENOENT;
+ goto out;
+ }
+ } else {
+ /* Map dev_name to "<NULL>" if no dev_name given. */
+ if (!dev_name)
+ dev_name = "<NULL>";
+ requested_dev_name = tomoyo_encode(dev_name);
+ if (!requested_dev_name) {
+ error = -ENOMEM;
+ goto out;
+ }
+ }
+ rdev.name = requested_dev_name;
+ tomoyo_fill_path_info(&rdev);
+ list_for_each_entry_rcu(ptr, &r->domain->acl_info_list, list) {
+ struct tomoyo_mount_acl *acl;
+ if (ptr->is_deleted || ptr->type != TOMOYO_TYPE_MOUNT_ACL)
+ continue;
+ acl = container_of(ptr, struct tomoyo_mount_acl,
+ head);
+ if (!tomoyo_compare_number_union(flags, &acl->flags) ||
+ !tomoyo_compare_name_union(&rtype, &acl->fs_type) ||
+ !tomoyo_compare_name_union(&rdir, &acl->dir_name) ||
+ (need_dev &&
+ !tomoyo_compare_name_union(&rdev, &acl->dev_name)) ||
+ !tomoyo_condition(r, ptr))
+ continue;
+ r->cond = ptr->cond;
+ error = 0;
+ break;
+ }
+ tomoyo_audit_mount_log(r, requested_dev_name, requested_dir_name,
+ requested_type, flags, !error);
+ if (error)
+ error = tomoyo_supervisor(r, TOMOYO_KEYWORD_ALLOW_MOUNT
+ "%s %s %s 0x%lX\n",
+ tomoyo_file_pattern(&rdev),
+ tomoyo_file_pattern(&rdir),
+ requested_type, flags);
+ out:
+ kfree(requested_dev_name);
+ kfree(requested_dir_name);
+ if (fstype)
+ put_filesystem(fstype);
+ kfree(requested_type);
+ /* Drop refcount obtained by tomoyo_get_path(). */
+ if (obj.path1.dentry)
+ path_put(&obj.path1);
+ return error;
+}
+
+/**
+ * tomoyo_mount_acl - Check permission for mount() operation.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ * @dev_name: Name of device file.
+ * @dir: Pointer to "struct path".
+ * @type: Name of filesystem type.
+ * @flags: Mount options.
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static int tomoyo_mount_acl(struct tomoyo_request_info *r, char *dev_name,
+ struct path *dir, char *type, unsigned long flags)
+{
+ int error;
+ error = -EPERM;
+ if ((flags & MS_MGC_MSK) == MS_MGC_VAL)
+ flags &= ~MS_MGC_MSK;
+ switch (flags & (MS_REMOUNT | MS_MOVE | MS_BIND)) {
+ case MS_REMOUNT:
+ case MS_MOVE:
+ case MS_BIND:
+ case 0:
+ break;
+ default:
+ printk(KERN_WARNING "ERROR: "
+ "%s%s%sare given for single mount operation.\n",
+ flags & MS_REMOUNT ? "'remount' " : "",
+ flags & MS_MOVE ? "'move' " : "",
+ flags & MS_BIND ? "'bind' " : "");
+ return -EINVAL;
+ }
+ switch (flags & (MS_UNBINDABLE | MS_PRIVATE | MS_SLAVE | MS_SHARED)) {
+ case MS_UNBINDABLE:
+ case MS_PRIVATE:
+ case MS_SLAVE:
+ case MS_SHARED:
+ case 0:
+ break;
+ default:
+ printk(KERN_WARNING "ERROR: "
+ "%s%s%s%sare given for single mount operation.\n",
+ flags & MS_UNBINDABLE ? "'unbindable' " : "",
+ flags & MS_PRIVATE ? "'private' " : "",
+ flags & MS_SLAVE ? "'slave' " : "",
+ flags & MS_SHARED ? "'shared' " : "");
+ return -EINVAL;
+ }
+ if (flags & MS_REMOUNT)
+ error = tomoyo_mount_acl(r, dev_name, dir,
+ TOMOYO_MOUNT_REMOUNT_KEYWORD,
+ flags & ~MS_REMOUNT);
+ else if (flags & MS_MOVE)
+ error = tomoyo_mount_acl(r, dev_name, dir,
+ TOMOYO_MOUNT_MOVE_KEYWORD,
+ flags & ~MS_MOVE);
+ else if (flags & MS_BIND)
+ error = tomoyo_mount_acl(r, dev_name, dir,
+ TOMOYO_MOUNT_BIND_KEYWORD,
+ flags & ~MS_BIND);
+ else if (flags & MS_UNBINDABLE)
+ error = tomoyo_mount_acl(r, dev_name, dir,
+ TOMOYO_MOUNT_MAKE_UNBINDABLE_KEYWORD,
+ flags & ~MS_UNBINDABLE);
+ else if (flags & MS_PRIVATE)
+ error = tomoyo_mount_acl(r, dev_name, dir,
+ TOMOYO_MOUNT_MAKE_PRIVATE_KEYWORD,
+ flags & ~MS_PRIVATE);
+ else if (flags & MS_SLAVE)
+ error = tomoyo_mount_acl(r, dev_name, dir,
+ TOMOYO_MOUNT_MAKE_SLAVE_KEYWORD,
+ flags & ~MS_SLAVE);
+ else if (flags & MS_SHARED)
+ error = tomoyo_mount_acl(r, dev_name, dir,
+ TOMOYO_MOUNT_MAKE_SHARED_KEYWORD,
+ flags & ~MS_SHARED);
+ else
+ do {
+ error = tomoyo_mount_acl2(r, dev_name, dir, type,
+ flags);
+ } while (error == 1);
+ if (r->mode != TOMOYO_CONFIG_ENFORCING)
+ error = 0;
+ return error;
+}
+
+/**
+ * tomoyo_mount_permission - Check permission for mount() operation.
+ *
+ * @dev_name: Name of device file.
+ * @dir: Pointer to "struct path".
+ * @type: Name of filesystem type. May be NULL.
+ * @flags: Mount options.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_mount_permission(char *dev_name, struct path *dir, char *type,
+ const unsigned long flags)
+{
+ struct tomoyo_request_info r;
+ int error;
+ int idx;
+ if (!tomoyo_capable(TOMOYO_SYS_MOUNT))
+ return -EPERM;
+ if (tomoyo_init_request_info(&r, NULL, TOMOYO_MAC_FILE_MOUNT)
+ == TOMOYO_CONFIG_DISABLED)
+ return 0;
+ if (!type)
+ type = "<NULL>";
+ idx = tomoyo_read_lock();
+ error = tomoyo_mount_acl(&r, dev_name, dir, type, flags);
+ tomoyo_read_unlock(idx);
+ return error;
+}
+
+/**
+ * tomoyo_write_mount_policy - Write "struct tomoyo_mount_acl" list.
+ *
+ * @data: String to parse.
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @condition: Pointer to "struct tomoyo_condition". May be NULL.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_write_mount_policy(char *data, struct tomoyo_domain_info *domain,
+ struct tomoyo_condition *condition,
+ const bool is_delete)
+{
+ struct tomoyo_mount_acl *entry = NULL;
+ struct tomoyo_acl_info *ptr;
+ struct tomoyo_mount_acl e = { .head.type = TOMOYO_TYPE_MOUNT_ACL,
+ .head.cond = condition };
+ int error = is_delete ? -ENOENT : -ENOMEM;
+ char *w[4];
+ if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[3][0])
+ return -EINVAL;
+ if (!tomoyo_parse_name_union(w[0], &e.dev_name) ||
+ !tomoyo_parse_name_union(w[1], &e.dir_name) ||
+ !tomoyo_parse_name_union(w[2], &e.fs_type) ||
+ !tomoyo_parse_number_union(w[3], &e.flags))
+ goto out;
+ if (!is_delete)
+ entry = kmalloc(sizeof(e), GFP_KERNEL);
+ mutex_lock(&tomoyo_policy_lock);
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
+ struct tomoyo_mount_acl *acl =
+ container_of(ptr, struct tomoyo_mount_acl, head);
+ if (ptr->type != TOMOYO_TYPE_MOUNT_ACL ||
+ ptr->cond != condition
+ || tomoyo_memcmp(acl, &e, offsetof(typeof(e), dev_name),
+ sizeof(e)))
+ continue;
+ ptr->is_deleted = is_delete;
+ error = 0;
+ break;
+ }
+ if (!is_delete && error && tomoyo_commit_ok(entry, &e, sizeof(e))) {
+ tomoyo_add_domain_acl(domain, &entry->head);
+ entry = NULL;
+ error = 0;
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+ out:
+ tomoyo_put_name_union(&e.dev_name);
+ tomoyo_put_name_union(&e.dir_name);
+ tomoyo_put_name_union(&e.fs_type);
+ tomoyo_put_number_union(&e.flags);
+ kfree(entry);
+ return error;
+}
--
^ permalink raw reply [flat|nested] 50+ messages in thread
* [TOMOYO #16 16/25] TOMOYO: Add environment variables restriction.
2009-10-04 12:49 [TOMOYO #16 00/25] Starting TOMOYO 2.3 Tetsuo Handa
` (14 preceding siblings ...)
2009-10-04 12:50 ` [TOMOYO #16 15/25] TOMOYO: Add mount restriction Tetsuo Handa
@ 2009-10-04 12:50 ` Tetsuo Handa
2009-10-04 12:50 ` [TOMOYO #16 17/25] TOMOYO: Add capability support Tetsuo Handa
` (9 subsequent siblings)
25 siblings, 0 replies; 50+ messages in thread
From: Tetsuo Handa @ 2009-10-04 12:50 UTC (permalink / raw)
To: linux-security-module; +Cc: linux-kernel, Tetsuo Handa
[-- Attachment #1: tomoyo-add-environ.patch --]
[-- Type: text/plain, Size: 7636 bytes --]
This patch contains code for checking environment variable's names passed to
an execve() operation.
This functionality is for protecting programs to be executed from dangerous
environment variables such as LD_PRELOAD.
You can use "if" clause if you want to check not only names but also values.
allow_execute /bin/sh if exec.envp["HOME"]="/"
You can use execute handler if you want to check not only names but also values
and/or add/remove/modify envp[]. The execute handler is executed with envp[]
moved to argv[] so that the execute handler will not be affected by dangerous
environment variables.
execute_handler /bin/check_param_and_exec
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
security/tomoyo/environ.c | 232 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 232 insertions(+)
--- /dev/null
+++ security-testing-2.6/security/tomoyo/environ.c
@@ -0,0 +1,232 @@
+/*
+ * security/tomoyo/environ.c
+ *
+ * Copyright (C) 2005-2009 NTT DATA CORPORATION
+ */
+#include "internal.h"
+
+/**
+ * tomoyo_audit_env_log - Audit environment variable name log.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ * @env: The name of environment variable.
+ * @is_granted: True if this is a granted log.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_audit_env_log(struct tomoyo_request_info *r, const char *env,
+ const bool is_granted)
+{
+ if (!is_granted)
+ tomoyo_warn_log(r, "environ %s", env);
+ return tomoyo_write_audit_log(is_granted, r, TOMOYO_KEYWORD_ALLOW_ENV
+ "%s\n", env);
+}
+
+/* The list for "struct tomoyo_globally_usable_env_entry". */
+LIST_HEAD(tomoyo_globally_usable_env_list);
+
+/**
+ * tomoyo_is_globally_usable_env - Check whether the given environment variable is acceptable for all domains.
+ *
+ * @env: The name of environment variable.
+ *
+ * Returns true if @env is globally permitted environment variable's name,
+ * false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static bool tomoyo_is_globally_usable_env(const struct tomoyo_path_info *env)
+{
+ struct tomoyo_globally_usable_env_entry *ptr;
+ bool found = false;
+ list_for_each_entry_rcu(ptr, &tomoyo_globally_usable_env_list, list) {
+ if (ptr->is_deleted || !tomoyo_path_matches_pattern(env,
+ ptr->env))
+ continue;
+ found = true;
+ break;
+ }
+ return found;
+}
+
+/**
+ * tomoyo_write_globally_usable_env_policy - Write "struct tomoyo_globally_usable_env_entry" list.
+ *
+ * @data: String to parse.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_write_globally_usable_env_policy(char *data, const bool is_delete)
+{
+ struct tomoyo_globally_usable_env_entry *entry = NULL;
+ struct tomoyo_globally_usable_env_entry e = { };
+ struct tomoyo_globally_usable_env_entry *ptr;
+ int error = is_delete ? -ENOENT : -ENOMEM;
+ if (!tomoyo_is_correct_path(data, 0, 0, 0) || strchr(data, '='))
+ return -EINVAL;
+ e.env = tomoyo_get_name(data);
+ if (!e.env)
+ return error;
+ if (!is_delete)
+ entry = kmalloc(sizeof(e), GFP_KERNEL);
+ mutex_lock(&tomoyo_policy_lock);
+ list_for_each_entry_rcu(ptr, &tomoyo_globally_usable_env_list, list) {
+ if (ptr->env != e.env)
+ continue;
+ ptr->is_deleted = is_delete;
+ error = 0;
+ break;
+ }
+ if (!is_delete && error && tomoyo_commit_ok(entry, &e, sizeof(e))) {
+ list_add_tail_rcu(&entry->list,
+ &tomoyo_globally_usable_env_list);
+ entry = NULL;
+ error = 0;
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+ tomoyo_put_name(e.env);
+ kfree(entry);
+ return error;
+}
+
+/**
+ * tomoyo_read_globally_usable_env_policy - Read "struct tomoyo_globally_usable_env_entry" list.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0 on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+bool tomoyo_read_globally_usable_env_policy(struct tomoyo_io_buffer *head)
+{
+ struct list_head *pos;
+ bool done = true;
+ list_for_each_cookie(pos, head->read_var2,
+ &tomoyo_globally_usable_env_list) {
+ struct tomoyo_globally_usable_env_entry *ptr;
+ ptr = list_entry(pos, struct tomoyo_globally_usable_env_entry,
+ list);
+ if (ptr->is_deleted)
+ continue;
+ done = tomoyo_io_printf(head, TOMOYO_KEYWORD_ALLOW_ENV "%s\n",
+ ptr->env->name);
+ if (!done)
+ break;
+ }
+ return done;
+}
+
+/**
+ * tomoyo_env_acl - Check permission for environment variable's name.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ * @environ: The name of environment variable.
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static int tomoyo_env_acl(struct tomoyo_request_info *r, const char *environ)
+{
+ const struct tomoyo_domain_info *domain = r->domain;
+ int error = -EPERM;
+ struct tomoyo_acl_info *ptr;
+ struct tomoyo_path_info env;
+ env.name = environ;
+ tomoyo_fill_path_info(&env);
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
+ struct tomoyo_env_acl *acl;
+ if (ptr->is_deleted || ptr->type != TOMOYO_TYPE_ENV_ACL)
+ continue;
+ acl = container_of(ptr, struct tomoyo_env_acl, head);
+ if (!tomoyo_condition(r, ptr) ||
+ !tomoyo_path_matches_pattern(&env, acl->env))
+ continue;
+ r->cond = ptr->cond;
+ error = 0;
+ break;
+ }
+ if (error && !domain->ignore_global_allow_env &&
+ tomoyo_is_globally_usable_env(&env))
+ error = 0;
+ return error;
+}
+
+/**
+ * tomoyo_env_perm - Check permission for environment variable's name.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ * @env: The name of environment variable.
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+int tomoyo_env_perm(struct tomoyo_request_info *r, const char *env)
+{
+ int error;
+ if (!env || !*env)
+ return 0;
+ do {
+ error = tomoyo_env_acl(r, env);
+ tomoyo_audit_env_log(r, env, !error);
+ if (!error)
+ break;
+ error = tomoyo_supervisor(r, TOMOYO_KEYWORD_ALLOW_ENV "%s\n",
+ env);
+ } while (error == 1);
+ return error;
+}
+
+/**
+ * tomoyo_write_env_policy - Write "struct tomoyo_env_acl" list.
+ *
+ * @data: String to parse.
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @condition: Pointer to "struct tomoyo_condition". May be NULL.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_write_env_policy(char *data, struct tomoyo_domain_info *domain,
+ struct tomoyo_condition *condition,
+ const bool is_delete)
+{
+ struct tomoyo_env_acl *entry = NULL;
+ struct tomoyo_acl_info *ptr;
+ struct tomoyo_env_acl e = {
+ .head.type = TOMOYO_TYPE_ENV_ACL,
+ .head.cond = condition
+ };
+ int error = is_delete ? -ENOENT : -ENOMEM;
+ if (!tomoyo_is_correct_path(data, 0, 0, 0) || strchr(data, '='))
+ return -EINVAL;
+ e.env = tomoyo_get_name(data);
+ if (!e.env)
+ return error;
+ if (!is_delete)
+ entry = kmalloc(sizeof(e), GFP_KERNEL);
+ mutex_lock(&tomoyo_policy_lock);
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
+ struct tomoyo_env_acl *acl =
+ container_of(ptr, struct tomoyo_env_acl, head);
+ if (ptr->type != TOMOYO_TYPE_ENV_ACL || ptr->cond != condition
+ || acl->env != e.env)
+ continue;
+ ptr->is_deleted = is_delete;
+ error = 0;
+ break;
+ }
+ if (!is_delete && error && tomoyo_commit_ok(entry, &e, sizeof(e))) {
+ tomoyo_add_domain_acl(domain, &entry->head);
+ entry = NULL;
+ error = 0;
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+ tomoyo_put_name(e.env);
+ kfree(entry);
+ return error;
+}
--
^ permalink raw reply [flat|nested] 50+ messages in thread
* [TOMOYO #16 17/25] TOMOYO: Add capability support.
2009-10-04 12:49 [TOMOYO #16 00/25] Starting TOMOYO 2.3 Tetsuo Handa
` (15 preceding siblings ...)
2009-10-04 12:50 ` [TOMOYO #16 16/25] TOMOYO: Add environment variables restriction Tetsuo Handa
@ 2009-10-04 12:50 ` Tetsuo Handa
2009-10-29 5:23 ` Serge E. Hallyn
2009-10-04 12:50 ` [TOMOYO #16 18/25] TOMOYO: Add utility functions Tetsuo Handa
` (8 subsequent siblings)
25 siblings, 1 reply; 50+ messages in thread
From: Tetsuo Handa @ 2009-10-04 12:50 UTC (permalink / raw)
To: linux-security-module; +Cc: linux-kernel, Tetsuo Handa
[-- Attachment #1: tomoyo-add-capability.patch --]
[-- Type: text/plain, Size: 5264 bytes --]
This patch contains code for checking non-posix capability.
TOMOYO is ready to support 65536 types of non-posix capabilities.
But I can't utilize TOMOYO's ability because
(1) Hooks are missing.
or
(2) Posix's capability and functionality are not one to one mapping.
Therefore I can't derive functionality the caller wants to use from
posix's capability number (e.g. CAP_SYS_ADMIN).
or
(3) Hooks are provided but it is not permitted to sleep (e.g. CAP_SYS_NICE)
while TOMOYO needs hooks where it is permitted to sleep.
or
(4) System calls and device drivers use the same posix's capability number.
Thus whether MAC's policy suits or not depends on hardware the system
is running. TOMOYO wants to distinguish requests from userland
applications and requests from kernel drivers, but I can't distinguish
it from posix's capability number.
Therefore, LSM version of TOMOYO has very poor support compared to non-LSM
version of TOMOYO. I hope this problem is solved in the future.
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
security/tomoyo/capability.c | 141 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 141 insertions(+)
--- /dev/null
+++ security-testing-2.6/security/tomoyo/capability.c
@@ -0,0 +1,141 @@
+/*
+ * security/tomoyo/capability.c
+ *
+ * Copyright (C) 2005-2009 NTT DATA CORPORATION
+ */
+#include "internal.h"
+
+/**
+ * tomoyo_audit_capability_log - Audit capability log.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ * @operation: Type of operation.
+ * @is_granted: True if this is a granted log.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_audit_capability_log(struct tomoyo_request_info *r,
+ const u8 operation,
+ const bool is_granted)
+{
+ if (!is_granted)
+ tomoyo_warn_log(r, "capability %s",
+ tomoyo_cap2keyword(operation));
+ return tomoyo_write_audit_log(is_granted, r,
+ TOMOYO_KEYWORD_ALLOW_CAPABILITY "%s\n",
+ tomoyo_cap2keyword(operation));
+}
+
+/**
+ * tomoyo_capable - Check permission for capability.
+ *
+ * @operation: Type of operation.
+ *
+ * Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static bool tomoyo_capable2(const u8 operation)
+{
+ struct tomoyo_request_info r;
+ struct tomoyo_acl_info *ptr;
+ int error;
+ if (tomoyo_init_request_info(&r, NULL, TOMOYO_MAX_MAC_INDEX +
+ operation) == TOMOYO_CONFIG_DISABLED)
+ return true;
+ do {
+ error = -EPERM;
+ list_for_each_entry_rcu(ptr, &r.domain->acl_info_list, list) {
+ struct tomoyo_capability_acl *acl;
+ if (ptr->is_deleted ||
+ ptr->type != TOMOYO_TYPE_CAPABILITY_ACL)
+ continue;
+ acl = container_of(ptr, struct tomoyo_capability_acl,
+ head);
+ if (acl->operation != operation ||
+ !tomoyo_condition(&r, ptr))
+ continue;
+ r.cond = ptr->cond;
+ error = 0;
+ break;
+ }
+ tomoyo_audit_capability_log(&r, operation, !error);
+ if (!error)
+ break;
+ error = tomoyo_supervisor(&r, TOMOYO_KEYWORD_ALLOW_CAPABILITY
+ "%s\n",
+ tomoyo_cap2keyword(operation));
+ } while (error == 1);
+ return !error;
+}
+
+/**
+ * tomoyo_capable - Check permission for capability.
+ *
+ * @operation: Type of operation.
+ *
+ * Returns true on success, false otherwise.
+ */
+bool tomoyo_capable(const u8 operation)
+{
+ const int idx = tomoyo_read_lock();
+ const int error = tomoyo_capable2(operation);
+ tomoyo_read_unlock(idx);
+ return error;
+}
+
+/**
+ * tomoyo_write_capability_policy - Write "struct tomoyo_capability_acl" list.
+ *
+ * @data: String to parse.
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @condition: Pointer to "struct tomoyo_condition". May be NULL.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_write_capability_policy(char *data,
+ struct tomoyo_domain_info *domain,
+ struct tomoyo_condition *condition,
+ const bool is_delete)
+{
+ struct tomoyo_capability_acl e = {
+ .head.type = TOMOYO_TYPE_CAPABILITY_ACL,
+ .head.cond = condition,
+ };
+ struct tomoyo_capability_acl *entry = NULL;
+ struct tomoyo_acl_info *ptr;
+ int error = is_delete ? -ENOENT : -ENOMEM;
+ u8 capability;
+ for (capability = 0; capability < TOMOYO_MAX_CAPABILITY_INDEX;
+ capability++) {
+ if (strcmp(data, tomoyo_cap2keyword(capability)))
+ continue;
+ break;
+ }
+ if (capability == TOMOYO_MAX_CAPABILITY_INDEX)
+ return -EINVAL;
+ e.operation = capability;
+ if (!is_delete)
+ entry = kmalloc(sizeof(e), GFP_KERNEL);
+ mutex_lock(&tomoyo_policy_lock);
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
+ struct tomoyo_capability_acl *acl =
+ container_of(ptr, struct tomoyo_capability_acl,
+ head);
+ if (ptr->type != TOMOYO_TYPE_CAPABILITY_ACL ||
+ ptr->cond != condition || acl->operation != capability)
+ continue;
+ ptr->is_deleted = is_delete;
+ error = 0;
+ break;
+ }
+ if (!is_delete && error && tomoyo_commit_ok(entry, &e, sizeof(e))) {
+ tomoyo_add_domain_acl(domain, &entry->head);
+ entry = NULL;
+ error = 0;
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+ kfree(entry);
+ return error;
+}
--
^ permalink raw reply [flat|nested] 50+ messages in thread
* [TOMOYO #16 18/25] TOMOYO: Add utility functions.
2009-10-04 12:49 [TOMOYO #16 00/25] Starting TOMOYO 2.3 Tetsuo Handa
` (16 preceding siblings ...)
2009-10-04 12:50 ` [TOMOYO #16 17/25] TOMOYO: Add capability support Tetsuo Handa
@ 2009-10-04 12:50 ` Tetsuo Handa
2009-10-04 12:50 ` [TOMOYO #16 19/25] TOMOYO: Add policy I/O handler Tetsuo Handa
` (7 subsequent siblings)
25 siblings, 0 replies; 50+ messages in thread
From: Tetsuo Handa @ 2009-10-04 12:50 UTC (permalink / raw)
To: linux-security-module; +Cc: linux-kernel, Tetsuo Handa
[-- Attachment #1: tomoyo-add-util.patch --]
[-- Type: text/plain, Size: 31976 bytes --]
This file contains utility functions.
There are two major changes compared to TOMOYO 2.2.0.
One is that garbage collector support is added.
tomoyo_read_lock()/tomoyo_read_unlock() protects the data against
the garbage collector.
The other is that the wildcard for recursive directory matching support is
added. For example, /var/www/html/\{\*\-.svn\-CVS\}/\*\-.\* matches
/var/www/html/\*\-.svn\-CVS/\*\-.\*
/var/www/html/\*\-.svn\-CVS/\*\-.svn\-CVS/\*\-.\*
/var/www/html/\*\-.svn\-CVS/\*\-.svn\-CVS/\*\-.svn\-CVS/\*\-.\*
etc.
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
security/tomoyo/util.c | 1144 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 1144 insertions(+)
--- /dev/null
+++ security-testing-2.6/security/tomoyo/util.c
@@ -0,0 +1,1144 @@
+/*
+ * security/tomoyo/util.c
+ *
+ * Copyright (C) 2005-2009 NTT DATA CORPORATION
+ */
+#include "internal.h"
+
+/* Lock for protecting policy. */
+DEFINE_MUTEX(tomoyo_policy_lock);
+
+/* Has /sbin/init started? */
+bool tomoyo_policy_loaded;
+
+/* Index table for searching parent category. */
+static const u8 tomoyo_index2category[TOMOYO_MAX_MAC_INDEX +
+ TOMOYO_MAX_CAPABILITY_INDEX] = {
+ [TOMOYO_MAC_FILE_EXECUTE] = TOMOYO_MAC_CATEGORY_FILE,
+ [TOMOYO_MAC_FILE_OPEN] = TOMOYO_MAC_CATEGORY_FILE,
+ [TOMOYO_MAC_FILE_CREATE] = TOMOYO_MAC_CATEGORY_FILE,
+ [TOMOYO_MAC_FILE_UNLINK] = TOMOYO_MAC_CATEGORY_FILE,
+ [TOMOYO_MAC_FILE_MKDIR] = TOMOYO_MAC_CATEGORY_FILE,
+ [TOMOYO_MAC_FILE_RMDIR] = TOMOYO_MAC_CATEGORY_FILE,
+ [TOMOYO_MAC_FILE_MKFIFO] = TOMOYO_MAC_CATEGORY_FILE,
+ [TOMOYO_MAC_FILE_MKSOCK] = TOMOYO_MAC_CATEGORY_FILE,
+ [TOMOYO_MAC_FILE_TRUNCATE] = TOMOYO_MAC_CATEGORY_FILE,
+ [TOMOYO_MAC_FILE_SYMLINK] = TOMOYO_MAC_CATEGORY_FILE,
+ [TOMOYO_MAC_FILE_REWRITE] = TOMOYO_MAC_CATEGORY_FILE,
+ [TOMOYO_MAC_FILE_MKBLOCK] = TOMOYO_MAC_CATEGORY_FILE,
+ [TOMOYO_MAC_FILE_MKCHAR] = TOMOYO_MAC_CATEGORY_FILE,
+ [TOMOYO_MAC_FILE_LINK] = TOMOYO_MAC_CATEGORY_FILE,
+ [TOMOYO_MAC_FILE_RENAME] = TOMOYO_MAC_CATEGORY_FILE,
+ [TOMOYO_MAC_FILE_CHMOD] = TOMOYO_MAC_CATEGORY_FILE,
+ [TOMOYO_MAC_FILE_CHOWN] = TOMOYO_MAC_CATEGORY_FILE,
+ [TOMOYO_MAC_FILE_CHGRP] = TOMOYO_MAC_CATEGORY_FILE,
+ [TOMOYO_MAC_FILE_IOCTL] = TOMOYO_MAC_CATEGORY_FILE,
+ [TOMOYO_MAC_FILE_CHROOT] = TOMOYO_MAC_CATEGORY_FILE,
+ [TOMOYO_MAC_FILE_MOUNT] = TOMOYO_MAC_CATEGORY_FILE,
+ [TOMOYO_MAC_FILE_UMOUNT] = TOMOYO_MAC_CATEGORY_FILE,
+ [TOMOYO_MAC_FILE_PIVOT_ROOT] = TOMOYO_MAC_CATEGORY_FILE,
+ [TOMOYO_MAC_ENVIRON] = TOMOYO_MAC_CATEGORY_MISC,
+ [TOMOYO_MAC_NETWORK_UDP_BIND] = TOMOYO_MAC_CATEGORY_NETWORK,
+ [TOMOYO_MAC_NETWORK_UDP_CONNECT] = TOMOYO_MAC_CATEGORY_NETWORK,
+ [TOMOYO_MAC_NETWORK_TCP_BIND] = TOMOYO_MAC_CATEGORY_NETWORK,
+ [TOMOYO_MAC_NETWORK_TCP_LISTEN] = TOMOYO_MAC_CATEGORY_NETWORK,
+ [TOMOYO_MAC_NETWORK_TCP_CONNECT] = TOMOYO_MAC_CATEGORY_NETWORK,
+ [TOMOYO_MAC_NETWORK_RAW_BIND] = TOMOYO_MAC_CATEGORY_NETWORK,
+ [TOMOYO_MAC_NETWORK_RAW_CONNECT] = TOMOYO_MAC_CATEGORY_NETWORK,
+ [TOMOYO_MAX_MAC_INDEX + TOMOYO_INET_STREAM_SOCKET_CREATE]
+ = TOMOYO_MAC_CATEGORY_CAPABILITY,
+ [TOMOYO_MAX_MAC_INDEX + TOMOYO_INET_STREAM_SOCKET_LISTEN]
+ = TOMOYO_MAC_CATEGORY_CAPABILITY,
+ [TOMOYO_MAX_MAC_INDEX + TOMOYO_INET_STREAM_SOCKET_CONNECT]
+ = TOMOYO_MAC_CATEGORY_CAPABILITY,
+ [TOMOYO_MAX_MAC_INDEX + TOMOYO_USE_INET_DGRAM_SOCKET]
+ = TOMOYO_MAC_CATEGORY_CAPABILITY,
+ [TOMOYO_MAX_MAC_INDEX + TOMOYO_USE_INET_RAW_SOCKET]
+ = TOMOYO_MAC_CATEGORY_CAPABILITY,
+ [TOMOYO_MAX_MAC_INDEX + TOMOYO_USE_ROUTE_SOCKET]
+ = TOMOYO_MAC_CATEGORY_CAPABILITY,
+ [TOMOYO_MAX_MAC_INDEX + TOMOYO_USE_PACKET_SOCKET]
+ = TOMOYO_MAC_CATEGORY_CAPABILITY,
+ [TOMOYO_MAX_MAC_INDEX + TOMOYO_SYS_MOUNT]
+ = TOMOYO_MAC_CATEGORY_CAPABILITY,
+ [TOMOYO_MAX_MAC_INDEX + TOMOYO_SYS_UMOUNT]
+ = TOMOYO_MAC_CATEGORY_CAPABILITY,
+ [TOMOYO_MAX_MAC_INDEX + TOMOYO_SYS_CHROOT]
+ = TOMOYO_MAC_CATEGORY_CAPABILITY,
+ [TOMOYO_MAX_MAC_INDEX + TOMOYO_CREATE_FIFO]
+ = TOMOYO_MAC_CATEGORY_CAPABILITY,
+ [TOMOYO_MAX_MAC_INDEX + TOMOYO_CREATE_BLOCK_DEV]
+ = TOMOYO_MAC_CATEGORY_CAPABILITY,
+ [TOMOYO_MAX_MAC_INDEX + TOMOYO_CREATE_CHAR_DEV]
+ = TOMOYO_MAC_CATEGORY_CAPABILITY,
+ [TOMOYO_MAX_MAC_INDEX + TOMOYO_CREATE_UNIX_SOCKET]
+ = TOMOYO_MAC_CATEGORY_CAPABILITY,
+ [TOMOYO_MAX_MAC_INDEX + TOMOYO_SYS_LINK]
+ = TOMOYO_MAC_CATEGORY_CAPABILITY,
+ [TOMOYO_MAX_MAC_INDEX + TOMOYO_SYS_SYMLINK]
+ = TOMOYO_MAC_CATEGORY_CAPABILITY,
+ [TOMOYO_MAX_MAC_INDEX + TOMOYO_SYS_RENAME]
+ = TOMOYO_MAC_CATEGORY_CAPABILITY,
+ [TOMOYO_MAX_MAC_INDEX + TOMOYO_SYS_UNLINK]
+ = TOMOYO_MAC_CATEGORY_CAPABILITY,
+ [TOMOYO_MAX_MAC_INDEX + TOMOYO_SYS_CHMOD]
+ = TOMOYO_MAC_CATEGORY_CAPABILITY,
+ [TOMOYO_MAX_MAC_INDEX + TOMOYO_SYS_CHOWN]
+ = TOMOYO_MAC_CATEGORY_CAPABILITY,
+ [TOMOYO_MAX_MAC_INDEX + TOMOYO_SYS_IOCTL]
+ = TOMOYO_MAC_CATEGORY_CAPABILITY,
+ [TOMOYO_MAX_MAC_INDEX + TOMOYO_SYS_PIVOT_ROOT]
+ = TOMOYO_MAC_CATEGORY_CAPABILITY,
+};
+
+/* Utility functions. */
+
+/**
+ * tomoyo_parse_ulong - Parse an "unsigned long" value.
+ *
+ * @result: Pointer to "unsigned long".
+ * @str: Pointer to string to parse.
+ *
+ * Returns value type on success, 0 otherwise.
+ *
+ * The @src is updated to point the first character after the value
+ * on success.
+ */
+u8 tomoyo_parse_ulong(unsigned long *result, char **str)
+{
+ const char *cp = *str;
+ char *ep;
+ int base = 10;
+ if (*cp == '0') {
+ char c = *(cp + 1);
+ if (c == 'x' || c == 'X') {
+ base = 16;
+ cp += 2;
+ } else if (c >= '0' && c <= '7') {
+ base = 8;
+ cp++;
+ }
+ }
+ *result = simple_strtoul(cp, &ep, base);
+ if (cp == ep)
+ return 0;
+ *str = ep;
+ switch (base) {
+ case 16:
+ return TOMOYO_VALUE_TYPE_HEXADECIMAL;
+ case 8:
+ return TOMOYO_VALUE_TYPE_OCTAL;
+ default:
+ return TOMOYO_VALUE_TYPE_DECIMAL;
+ }
+}
+
+/**
+ * tomoyo_print_ulong - Print an "unsigned long" value.
+ *
+ * @buffer: Pointer to buffer.
+ * @buffer_len: Size of @buffer.
+ * @value: An "unsigned long" value.
+ * @type: Type of @value.
+ *
+ * Returns nothing.
+ */
+void tomoyo_print_ulong(char *buffer, const int buffer_len,
+ const unsigned long value, const u8 type)
+{
+ if (type == TOMOYO_VALUE_TYPE_DECIMAL)
+ snprintf(buffer, buffer_len, "%lu", value);
+ else if (type == TOMOYO_VALUE_TYPE_OCTAL)
+ snprintf(buffer, buffer_len, "0%lo", value);
+ else if (type == TOMOYO_VALUE_TYPE_HEXADECIMAL)
+ snprintf(buffer, buffer_len, "0x%lX", value);
+ else
+ snprintf(buffer, buffer_len, "type(%u)", type);
+}
+
+/**
+ * tomoyo_parse_name_union - Parse a tomoyo_name_union.
+ *
+ * @filename: Name or name group.
+ * @ptr: Pointer to "struct tomoyo_name_union".
+ *
+ * Returns true on success, false otherwise.
+ */
+bool tomoyo_parse_name_union(const char *filename,
+ struct tomoyo_name_union *ptr)
+{
+ if (!tomoyo_is_correct_path(filename, 0, 0, 0))
+ return false;
+ if (filename[0] == '@') {
+ ptr->group = tomoyo_get_path_group(filename + 1);
+ ptr->is_group = true;
+ return ptr->group != NULL;
+ }
+ ptr->filename = tomoyo_get_name(filename);
+ ptr->is_group = false;
+ return ptr->filename != NULL;
+}
+
+/**
+ * tomoyo_parse_number_union - Parse a tomoyo_number_union.
+ *
+ * @data: Number or number range or number group.
+ * @ptr: Pointer to "struct tomoyo_number_union".
+ *
+ * Returns true on success, false otherwise.
+ */
+bool tomoyo_parse_number_union(char *data, struct tomoyo_number_union *num)
+{
+ u8 type;
+ unsigned long v;
+ memset(num, 0, sizeof(*num));
+ if (data[0] == '@') {
+ if (!tomoyo_is_correct_path(data, 0, 0, 0))
+ return false;
+ num->group = tomoyo_get_number_group(data + 1);
+ num->is_group = true;
+ return num->group != NULL;
+ }
+ type = tomoyo_parse_ulong(&v, &data);
+ if (!type)
+ return false;
+ num->values[0] = v;
+ num->min_type = type;
+ if (!*data) {
+ num->values[1] = v;
+ num->max_type = type;
+ return true;
+ }
+ if (*data++ != '-')
+ return false;
+ type = tomoyo_parse_ulong(&v, &data);
+ if (!type || *data)
+ return false;
+ num->values[1] = v;
+ num->max_type = type;
+ return true;
+}
+
+/**
+ * tomoyo_is_byte_range - Check whether the string isa \ooo style octal value.
+ *
+ * @str: Pointer to the string.
+ *
+ * Returns true if @str is a \ooo style octal value, false otherwise.
+ */
+static inline bool tomoyo_is_byte_range(const char *str)
+{
+ return *str >= '0' && *str++ <= '3' &&
+ *str >= '0' && *str++ <= '7' &&
+ *str >= '0' && *str <= '7';
+}
+
+/**
+ * tomoyo_is_decimal - Check whether the character is a decimal character.
+ *
+ * @c: The character to check.
+ *
+ * Returns true if @c is a decimal character, false otherwise.
+ */
+static inline bool tomoyo_is_decimal(const char c)
+{
+ return c >= '0' && c <= '9';
+}
+
+/**
+ * tomoyo_is_hexadecimal - Check whether the character is a hexadecimal character.
+ *
+ * @c: The character to check.
+ *
+ * Returns true if @c is a hexadecimal character, false otherwise.
+ */
+static inline bool tomoyo_is_hexadecimal(const char c)
+{
+ return (c >= '0' && c <= '9') ||
+ (c >= 'A' && c <= 'F') ||
+ (c >= 'a' && c <= 'f');
+}
+
+/**
+ * tomoyo_is_alphabet_char - Check whether the character is an alphabet.
+ *
+ * @c: The character to check.
+ *
+ * Returns true if @c is an alphabet character, false otherwise.
+ */
+static inline bool tomoyo_is_alphabet_char(const char c)
+{
+ return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
+}
+
+/**
+ * tomoyo_make_byte - Make byte value from three octal characters.
+ *
+ * @c1: The first character.
+ * @c2: The second character.
+ * @c3: The third character.
+ *
+ * Returns byte value.
+ */
+static inline u8 tomoyo_make_byte(const u8 c1, const u8 c2, const u8 c3)
+{
+ return ((c1 - '0') << 6) + ((c2 - '0') << 3) + (c3 - '0');
+}
+
+/**
+ * tomoyo_str_starts - Check whether the given string starts with the given keyword.
+ *
+ * @src: Pointer to pointer to the string.
+ * @find: Pointer to the keyword.
+ *
+ * Returns true if @src starts with @find, false otherwise.
+ *
+ * The @src is updated to point the first character after the @find
+ * if @src starts with @find.
+ */
+bool tomoyo_str_starts(char **src, const char *find)
+{
+ const int len = strlen(find);
+ char *tmp = *src;
+ if (strncmp(tmp, find, len))
+ return false;
+ tmp += len;
+ *src = tmp;
+ return true;
+}
+
+/**
+ * tomoyo_normalize_line - Format string.
+ *
+ * @buffer: The line to normalize.
+ *
+ * Leading and trailing whitespaces are removed.
+ * Multiple whitespaces are packed into single space.
+ *
+ * Returns nothing.
+ */
+void tomoyo_normalize_line(unsigned char *buffer)
+{
+ unsigned char *sp = buffer;
+ unsigned char *dp = buffer;
+ bool first = true;
+ while (*sp && (*sp <= ' ' || *sp >= 127))
+ sp++;
+ while (*sp) {
+ if (!first)
+ *dp++ = ' ';
+ first = false;
+ while (*sp > ' ' && *sp < 127)
+ *dp++ = *sp++;
+ while (*sp && (*sp <= ' ' || *sp >= 127))
+ sp++;
+ }
+ *dp = '\0';
+}
+
+/**
+ * tomoyo_tokenize - Tokenize string.
+ *
+ * @buffer: The line to tokenize.
+ * @w: Pointer to "char *".
+ * @size: Sizeof @w .
+ *
+ * Returns true on success, false otherwise.
+ */
+bool tomoyo_tokenize(char *buffer, char *w[], size_t size)
+{
+ int count = size / sizeof(char *);
+ int i;
+ for (i = 0; i < count; i++)
+ w[i] = "";
+ for (i = 0; i < count; i++) {
+ char *cp = strchr(buffer, ' ');
+ if (cp)
+ *cp = '\0';
+ w[i] = buffer;
+ if (!cp)
+ break;
+ buffer = cp + 1;
+ }
+ return i < count || !*buffer;
+}
+
+/**
+ * tomoyo_is_correct_path - Validate a pathname.
+ *
+ * @filename: The pathname to check.
+ * @start_type: Should the pathname start with '/'?
+ * 1 = must / -1 = must not / 0 = don't care
+ * @pattern_type: Can the pathname contain a wildcard?
+ * 1 = must / -1 = must not / 0 = don't care
+ * @end_type: Should the pathname end with '/'?
+ * 1 = must / -1 = must not / 0 = don't care
+ *
+ * Check whether the given filename follows the naming rules.
+ * Returns true if @filename follows the naming rules, false otherwise.
+ */
+bool tomoyo_is_correct_path(const char *filename, const s8 start_type,
+ const s8 pattern_type, const s8 end_type)
+{
+ const char *const start = filename;
+ bool in_repetition = false;
+ bool contains_pattern = false;
+ unsigned char c;
+ unsigned char d;
+ unsigned char e;
+ if (!filename)
+ goto out;
+ c = *filename;
+ if (start_type == 1) { /* Must start with '/' */
+ if (c != '/')
+ goto out;
+ } else if (start_type == -1) { /* Must not start with '/' */
+ if (c == '/')
+ goto out;
+ }
+ if (c)
+ c = *(filename + strlen(filename) - 1);
+ if (end_type == 1) { /* Must end with '/' */
+ if (c != '/')
+ goto out;
+ } else if (end_type == -1) { /* Must not end with '/' */
+ if (c == '/')
+ goto out;
+ }
+ while (1) {
+ c = *filename++;
+ if (!c)
+ break;
+ if (c == '\\') {
+ c = *filename++;
+ switch (c) {
+ case '\\': /* "\\" */
+ continue;
+ case '$': /* "\$" */
+ case '+': /* "\+" */
+ case '?': /* "\?" */
+ case '*': /* "\*" */
+ case '@': /* "\@" */
+ case 'x': /* "\x" */
+ case 'X': /* "\X" */
+ case 'a': /* "\a" */
+ case 'A': /* "\A" */
+ case '-': /* "\-" */
+ if (pattern_type == -1)
+ break; /* Must not contain pattern */
+ contains_pattern = true;
+ continue;
+ case '{': /* "/\{" */
+ if (filename - 3 < start ||
+ *(filename - 3) != '/')
+ break;
+ if (pattern_type == -1)
+ break; /* Must not contain pattern */
+ contains_pattern = true;
+ in_repetition = true;
+ continue;
+ case '}': /* "\}/" */
+ if (*filename != '/')
+ break;
+ if (!in_repetition)
+ break;
+ in_repetition = false;
+ continue;
+ case '0': /* "\ooo" */
+ case '1':
+ case '2':
+ case '3':
+ d = *filename++;
+ if (d < '0' || d > '7')
+ break;
+ e = *filename++;
+ if (e < '0' || e > '7')
+ break;
+ c = tomoyo_make_byte(c, d, e);
+ if (c && (c <= ' ' || c >= 127))
+ continue; /* pattern is not \000 */
+ }
+ goto out;
+ } else if (in_repetition && c == '/') {
+ goto out;
+ } else if (c <= ' ' || c >= 127) {
+ goto out;
+ }
+ }
+ if (pattern_type == 1) { /* Must contain pattern */
+ if (!contains_pattern)
+ goto out;
+ }
+ if (in_repetition)
+ goto out;
+ return true;
+ out:
+ return false;
+}
+
+/**
+ * tomoyo_is_correct_domain - Check whether the given domainname follows the naming rules.
+ *
+ * @domainname: The domainname to check.
+ *
+ * Returns true if @domainname follows the naming rules, false otherwise.
+ */
+bool tomoyo_is_correct_domain(const unsigned char *domainname)
+{
+ unsigned char c;
+ unsigned char d;
+ unsigned char e;
+ if (!domainname || strncmp(domainname, ROOT_NAME, ROOT_NAME_LEN))
+ goto out;
+ domainname += ROOT_NAME_LEN;
+ if (!*domainname)
+ return true;
+ do {
+ if (*domainname++ != ' ')
+ goto out;
+ if (*domainname++ != '/')
+ goto out;
+ while (1) {
+ c = *domainname;
+ if (!c || c == ' ')
+ break;
+ domainname++;
+ if (c == '\\') {
+ c = *domainname++;
+ switch ((c)) {
+ case '\\': /* "\\" */
+ continue;
+ case '0': /* "\ooo" */
+ case '1':
+ case '2':
+ case '3':
+ d = *domainname++;
+ if (d < '0' || d > '7')
+ break;
+ e = *domainname++;
+ if (e < '0' || e > '7')
+ break;
+ c = tomoyo_make_byte(c, d, e);
+ if (c && (c <= ' ' || c >= 127))
+ /* pattern is not \000 */
+ continue;
+ }
+ goto out;
+ } else if (c < ' ' || c >= 127) {
+ goto out;
+ }
+ }
+ } while (*domainname);
+ return true;
+ out:
+ return false;
+}
+
+/**
+ * tomoyo_is_domain_def - Check whether the given token can be a domainname.
+ *
+ * @buffer: The token to check.
+ *
+ * Returns true if @buffer possibly be a domainname, false otherwise.
+ */
+bool tomoyo_is_domain_def(const unsigned char *buffer)
+{
+ return !strncmp(buffer, ROOT_NAME, ROOT_NAME_LEN);
+}
+
+/**
+ * tomoyo_find_domain - Find a domain by the given name.
+ *
+ * @domainname: The domainname to find.
+ *
+ * Returns pointer to "struct tomoyo_domain_info" if found, NULL otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname)
+{
+ struct tomoyo_domain_info *domain;
+ struct tomoyo_path_info name;
+ name.name = domainname;
+ tomoyo_fill_path_info(&name);
+ list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
+ if (!domain->is_deleted &&
+ !tomoyo_pathcmp(&name, domain->domainname))
+ return domain;
+ }
+ return NULL;
+}
+
+/**
+ * tomoyo_const_part_length - Evaluate the initial length without a pattern in a token.
+ *
+ * @filename: The string to evaluate.
+ *
+ * Returns the initial length without a pattern in @filename.
+ */
+static int tomoyo_const_part_length(const char *filename)
+{
+ char c;
+ int len = 0;
+ if (!filename)
+ return 0;
+ while (1) {
+ c = *filename++;
+ if (!c)
+ break;
+ if (c != '\\') {
+ len++;
+ continue;
+ }
+ c = *filename++;
+ switch (c) {
+ case '\\': /* "\\" */
+ len += 2;
+ continue;
+ case '0': /* "\ooo" */
+ case '1':
+ case '2':
+ case '3':
+ c = *filename++;
+ if (c < '0' || c > '7')
+ break;
+ c = *filename++;
+ if (c < '0' || c > '7')
+ break;
+ len += 4;
+ continue;
+ }
+ break;
+ }
+ return len;
+}
+
+/**
+ * tomoyo_fill_path_info - Fill in "struct tomoyo_path_info" members.
+ *
+ * @ptr: Pointer to "struct tomoyo_path_info" to fill in.
+ *
+ * The caller sets "struct tomoyo_path_info *"->name.
+ */
+void tomoyo_fill_path_info(struct tomoyo_path_info *ptr)
+{
+ const char *name = ptr->name;
+ const int len = strlen(name);
+ ptr->const_len = tomoyo_const_part_length(name);
+ ptr->is_dir = len && (name[len - 1] == '/');
+ ptr->is_patterned = (ptr->const_len < len);
+ ptr->hash = full_name_hash(name, len);
+}
+
+/**
+ * tomoyo_file_matches_pattern2 - Pattern matching without '/' character and "\-" pattern.
+ *
+ * @filename: The start of string to check.
+ * @filename_end: The end of string to check.
+ * @pattern: The start of pattern to compare.
+ * @pattern_end: The end of pattern to compare.
+ *
+ * Returns true if @filename matches @pattern, false otherwise.
+ */
+static bool tomoyo_file_matches_pattern2(const char *filename,
+ const char *filename_end,
+ const char *pattern,
+ const char *pattern_end)
+{
+ while (filename < filename_end && pattern < pattern_end) {
+ char c;
+ if (*pattern != '\\') {
+ if (*filename++ != *pattern++)
+ return false;
+ continue;
+ }
+ c = *filename;
+ pattern++;
+ switch (*pattern) {
+ int i;
+ int j;
+ case '?':
+ if (c == '/') {
+ return false;
+ } else if (c == '\\') {
+ if (filename[1] == '\\')
+ filename++;
+ else if (tomoyo_is_byte_range(filename + 1))
+ filename += 3;
+ else
+ return false;
+ }
+ break;
+ case '\\':
+ if (c != '\\')
+ return false;
+ if (*++filename != '\\')
+ return false;
+ break;
+ case '+':
+ if (!tomoyo_is_decimal(c))
+ return false;
+ break;
+ case 'x':
+ if (!tomoyo_is_hexadecimal(c))
+ return false;
+ break;
+ case 'a':
+ if (!tomoyo_is_alphabet_char(c))
+ return false;
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ if (c == '\\' && tomoyo_is_byte_range(filename + 1)
+ && !strncmp(filename + 1, pattern, 3)) {
+ filename += 3;
+ pattern += 2;
+ break;
+ }
+ return false; /* Not matched. */
+ case '*':
+ case '@':
+ for (i = 0; i <= filename_end - filename; i++) {
+ if (tomoyo_file_matches_pattern2(filename + i,
+ filename_end,
+ pattern + 1,
+ pattern_end))
+ return true;
+ c = filename[i];
+ if (c == '.' && *pattern == '@')
+ break;
+ if (c != '\\')
+ continue;
+ if (filename[i + 1] == '\\')
+ i++;
+ else if (tomoyo_is_byte_range(filename + i
+ + 1))
+ i += 3;
+ else
+ break; /* Bad pattern. */
+ }
+ return false; /* Not matched. */
+ default:
+ j = 0;
+ c = *pattern;
+ if (c == '$') {
+ while (tomoyo_is_decimal(filename[j]))
+ j++;
+ } else if (c == 'X') {
+ while (tomoyo_is_hexadecimal(filename[j]))
+ j++;
+ } else if (c == 'A') {
+ while (tomoyo_is_alphabet_char(filename[j]))
+ j++;
+ }
+ for (i = 1; i <= j; i++) {
+ if (tomoyo_file_matches_pattern2(filename + i,
+ filename_end,
+ pattern + 1,
+ pattern_end))
+ return true;
+ }
+ return false; /* Not matched or bad pattern. */
+ }
+ filename++;
+ pattern++;
+ }
+ while (*pattern == '\\' &&
+ (*(pattern + 1) == '*' || *(pattern + 1) == '@'))
+ pattern += 2;
+ return filename == filename_end && pattern == pattern_end;
+}
+
+/**
+ * tomoyo_file_matches_pattern - Pattern matching without without '/' character.
+ *
+ * @filename: The start of string to check.
+ * @filename_end: The end of string to check.
+ * @pattern: The start of pattern to compare.
+ * @pattern_end: The end of pattern to compare.
+ *
+ * Returns true if @filename matches @pattern, false otherwise.
+ */
+static bool tomoyo_file_matches_pattern(const char *filename,
+ const char *filename_end,
+ const char *pattern,
+ const char *pattern_end)
+{
+ const char *pattern_start = pattern;
+ bool first = true;
+ bool result;
+ while (pattern < pattern_end - 1) {
+ /* Split at "\-" pattern. */
+ if (*pattern++ != '\\' || *pattern++ != '-')
+ continue;
+ result = tomoyo_file_matches_pattern2(filename, filename_end,
+ pattern_start,
+ pattern - 2);
+ if (first)
+ result = !result;
+ if (result)
+ return false;
+ first = false;
+ pattern_start = pattern;
+ }
+ result = tomoyo_file_matches_pattern2(filename, filename_end,
+ pattern_start, pattern_end);
+ return first ? result : !result;
+}
+
+/**
+ * tomoyo_path_matches_pattern2 - Do pathname pattern matching.
+ *
+ * @f: The start of string to check.
+ * @p: The start of pattern to compare.
+ *
+ * Returns true if @f matches @p, false otherwise.
+ */
+static bool tomoyo_path_matches_pattern2(const char *f, const char *p)
+{
+ const char *f_delimiter;
+ const char *p_delimiter;
+ while (*f && *p) {
+ f_delimiter = strchr(f, '/');
+ if (!f_delimiter)
+ f_delimiter = f + strlen(f);
+ p_delimiter = strchr(p, '/');
+ if (!p_delimiter)
+ p_delimiter = p + strlen(p);
+ if (*p == '\\' && *(p + 1) == '{')
+ goto recursive;
+ if (!tomoyo_file_matches_pattern(f, f_delimiter, p,
+ p_delimiter))
+ return false;
+ f = f_delimiter;
+ if (*f)
+ f++;
+ p = p_delimiter;
+ if (*p)
+ p++;
+ }
+ /* Ignore trailing "\*" and "\@" in @pattern. */
+ while (*p == '\\' &&
+ (*(p + 1) == '*' || *(p + 1) == '@'))
+ p += 2;
+ return !*f && !*p;
+ recursive:
+ /*
+ * The "\{" pattern is permitted only after '/' character.
+ * This guarantees that below "*(p - 1)" is safe.
+ * Also, the "\}" pattern is permitted only before '/' character
+ * so that "\{" + "\}" pair will not break the "\-" operator.
+ */
+ if (*(p - 1) != '/' || p_delimiter <= p + 3 || *p_delimiter != '/' ||
+ *(p_delimiter - 1) != '}' || *(p_delimiter - 2) != '\\')
+ return false; /* Bad pattern. */
+ do {
+ /* Compare current component with pattern. */
+ if (!tomoyo_file_matches_pattern(f, f_delimiter, p + 2,
+ p_delimiter - 2))
+ break;
+ /* Proceed to next component. */
+ f = f_delimiter;
+ if (!*f)
+ break;
+ f++;
+ /* Continue comparison. */
+ if (tomoyo_path_matches_pattern2(f, p_delimiter + 1))
+ return true;
+ f_delimiter = strchr(f, '/');
+ } while (f_delimiter);
+ return false; /* Not matched. */
+}
+
+/**
+ * tomoyo_path_matches_pattern - Check whether the given filename matches the given pattern.
+ *
+ * @filename: The filename to check.
+ * @pattern: The pattern to compare.
+ *
+ * Returns true if matches, false otherwise.
+ *
+ * The following patterns are available.
+ * \\ \ itself.
+ * \ooo Octal representation of a byte.
+ * \* Zero or more repetitions of characters other than '/'.
+ * \@ Zero or more repetitions of characters other than '/' or '.'.
+ * \? 1 byte character other than '/'.
+ * \$ One or more repetitions of decimal digits.
+ * \+ 1 decimal digit.
+ * \X One or more repetitions of hexadecimal digits.
+ * \x 1 hexadecimal digit.
+ * \A One or more repetitions of alphabet characters.
+ * \a 1 alphabet character.
+ *
+ * \- Subtraction operator.
+ *
+ * /\{dir\}/ '/' + 'One or more repetitions of dir/' (e.g. /dir/ /dir/dir/
+ * /dir/dir/dir/ ).
+ */
+bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename,
+ const struct tomoyo_path_info *pattern)
+{
+ const char *f = filename->name;
+ const char *p = pattern->name;
+ const int len = pattern->const_len;
+ /* If @pattern doesn't contain pattern, I can use strcmp(). */
+ if (!pattern->is_patterned)
+ return !tomoyo_pathcmp(filename, pattern);
+ /* Don't compare directory and non-directory. */
+ if (filename->is_dir != pattern->is_dir)
+ return false;
+ /* Compare the initial length without patterns. */
+ if (strncmp(f, p, len))
+ return false;
+ f += len;
+ p += len;
+ return tomoyo_path_matches_pattern2(f, p);
+}
+
+/**
+ * tomoyo_get_exe - Get tomoyo_realpath() of current process.
+ *
+ * Returns the tomoyo_realpath() of current process on success, NULL otherwise.
+ *
+ * This function uses kzalloc(), so the caller must kfree()
+ * if this function didn't return NULL.
+ */
+const char *tomoyo_get_exe(void)
+{
+ struct mm_struct *mm = current->mm;
+ struct vm_area_struct *vma;
+ const char *cp = NULL;
+ if (!mm)
+ return NULL;
+ down_read(&mm->mmap_sem);
+ for (vma = mm->mmap; vma; vma = vma->vm_next) {
+ if ((vma->vm_flags & VM_EXECUTABLE) && vma->vm_file) {
+ cp = tomoyo_realpath_from_path(&vma->vm_file->f_path);
+ break;
+ }
+ }
+ up_read(&mm->mmap_sem);
+ return cp;
+}
+
+/**
+ * tomoyo_get_audit - Get audit mode.
+ *
+ * @profile: Profile number.
+ * @index: Index number of functionality.
+ * @is_granted: True if granted log, false otehrwise.
+ *
+ * Returns mode.
+ */
+bool tomoyo_get_audit(const u8 profile, const u8 index, const bool is_granted)
+{
+ u8 mode;
+ const u8 category = tomoyo_index2category[index] + TOMOYO_MAX_MAC_INDEX
+ + TOMOYO_MAX_CAPABILITY_INDEX;
+ if (!tomoyo_policy_loaded)
+ return false;
+ mode = tomoyo_profile(profile)->config[index];
+ if (mode == TOMOYO_CONFIG_USE_DEFAULT)
+ mode = tomoyo_profile(profile)->config[category];
+ if (mode == TOMOYO_CONFIG_USE_DEFAULT)
+ mode = tomoyo_profile(profile)->default_config;
+ if (is_granted)
+ return mode & TOMOYO_CONFIG_WANT_GRANT_LOG;
+ return mode & TOMOYO_CONFIG_WANT_REJECT_LOG;
+}
+
+/**
+ * tomoyo_get_mode - Get MAC mode.
+ *
+ * @profile: Profile number.
+ * @index: Index number of functionality.
+ *
+ * Returns mode.
+ */
+int tomoyo_get_mode(const u8 profile, const u8 index)
+{
+ u8 mode;
+ const u8 category = tomoyo_index2category[index] + TOMOYO_MAX_MAC_INDEX
+ + TOMOYO_MAX_CAPABILITY_INDEX;
+ if (!tomoyo_policy_loaded)
+ return TOMOYO_CONFIG_DISABLED;
+ mode = tomoyo_profile(profile)->config[index];
+ if (mode == TOMOYO_CONFIG_USE_DEFAULT)
+ mode = tomoyo_profile(profile)->config[category];
+ if (mode == TOMOYO_CONFIG_USE_DEFAULT)
+ mode = tomoyo_profile(profile)->default_config;
+ return mode & 3;
+}
+
+/**
+ * tomoyo_init_request_info - Initialize "struct tomoyo_request_info" members.
+ *
+ * @r: Pointer to "struct tomoyo_request_info" to initialize.
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * NULL for tomoyo_current_domain().
+ * @index: Index number of functionality.
+ *
+ * Returns mode.
+ */
+int tomoyo_init_request_info(struct tomoyo_request_info *r,
+ struct tomoyo_domain_info *domain, const u8 index)
+{
+ u8 profile;
+ memset(r, 0, sizeof(*r));
+ if (!domain)
+ domain = tomoyo_current_domain();
+ r->domain = domain;
+ profile = domain->profile;
+ r->profile = profile;
+ r->type = index;
+ r->mode = tomoyo_get_mode(profile, index);
+ return r->mode;
+}
+
+/**
+ * tomoyo_last_word - Get last component of a line.
+ *
+ * @line: A line.
+ *
+ * Returns the last word of a line.
+ */
+const char *tomoyo_last_word(const char *name)
+{
+ const char *cp = strrchr(name, ' ');
+ if (cp)
+ return cp + 1;
+ return name;
+}
+
+/**
+ * tomoyo_warn_log - Print warning or error message on console.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ * @fmt: The printf()'s format string, followed by parameters.
+ */
+void tomoyo_warn_log(struct tomoyo_request_info *r, const char *fmt, ...)
+{
+ int len = PAGE_SIZE;
+ va_list args;
+ char *buffer;
+ const struct tomoyo_profile *profile =
+ tomoyo_profile(r->domain->profile);
+ switch (r->mode) {
+ case TOMOYO_CONFIG_ENFORCING:
+ if (!profile->enforcing->enforcing_verbose)
+ return;
+ break;
+ case TOMOYO_CONFIG_PERMISSIVE:
+ if (!profile->permissive->permissive_verbose)
+ return;
+ break;
+ case TOMOYO_CONFIG_LEARNING:
+ if (!profile->learning->learning_verbose)
+ return;
+ break;
+ }
+ while (1) {
+ int len2;
+ buffer = kmalloc(len, GFP_KERNEL);
+ if (!buffer)
+ return;
+ va_start(args, fmt);
+ len2 = vsnprintf(buffer, len - 1, fmt, args);
+ va_end(args);
+ if (len2 <= len - 1) {
+ buffer[len2] = '\0';
+ break;
+ }
+ len = len2 + 1;
+ kfree(buffer);
+ }
+ printk(KERN_WARNING "%s: Access %s denied for %s\n",
+ r->mode == TOMOYO_CONFIG_ENFORCING ? "ERROR" : "WARNING",
+ buffer, tomoyo_last_word(r->domain->domainname->name));
+ kfree(buffer);
+}
+
+/**
+ * tomoyo_domain_quota_ok - Check for domain's quota.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ *
+ * Returns true if the domain is not exceeded quota, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+bool tomoyo_domain_quota_ok(struct tomoyo_request_info *r)
+{
+ unsigned int count = 0;
+ struct tomoyo_domain_info *domain = r->domain;
+ struct tomoyo_acl_info *ptr;
+ if (r->mode != 1)
+ return false;
+ if (!domain)
+ return true;
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
+ if (ptr->is_deleted)
+ continue;
+ switch (ptr->type) {
+ u16 perm;
+ u8 i;
+ case TOMOYO_TYPE_PATH_ACL:
+ perm = container_of(ptr, struct tomoyo_path_acl,
+ head)->perm;
+ for (i = 0; i < TOMOYO_MAX_PATH_OPERATION; i++)
+ if (perm & (1 << i))
+ count++;
+ if (perm & (1 << TOMOYO_TYPE_READ_WRITE))
+ count -= 2;
+ break;
+ case TOMOYO_TYPE_PATH2_ACL:
+ perm = container_of(ptr, struct tomoyo_path2_acl,
+ head)->perm;
+ for (i = 0; i < TOMOYO_MAX_PATH2_OPERATION; i++)
+ if (perm & (1 << i))
+ count++;
+ break;
+ case TOMOYO_TYPE_EXECUTE_HANDLER:
+ case TOMOYO_TYPE_DENIED_EXECUTE_HANDLER:
+ break;
+ case TOMOYO_TYPE_PATH_NUMBER_ACL:
+ perm = container_of(ptr, struct tomoyo_path_number_acl,
+ head)->perm;
+ for (i = 0; i < TOMOYO_MAX_PATH_NUMBER_OPERATION; i++)
+ if (perm & (1 << i))
+ count++;
+ break;
+ case TOMOYO_TYPE_PATH_NUMBER3_ACL:
+ perm = container_of(ptr,
+ struct tomoyo_path_number3_acl,
+ head)->perm;
+ for (i = 0; i < TOMOYO_MAX_PATH_NUMBER3_OPERATION;
+ i++)
+ if (perm & (1 << i))
+ count++;
+ break;
+ case TOMOYO_TYPE_IP_NETWORK_ACL:
+ perm = container_of(ptr, struct tomoyo_ip_network_acl,
+ head)->perm;
+ for (i = 0; i < TOMOYO_MAX_NETWORK_OPERATION; i++)
+ if (perm & (1 << i))
+ count++;
+ break;
+ default:
+ count++;
+ }
+ }
+ if (count
+ < tomoyo_profile(domain->profile)->learning->learning_max_entry)
+ return true;
+ if (!domain->quota_warned) {
+ domain->quota_warned = true;
+ printk(KERN_WARNING "WARNING: "
+ "Domain '%s' has so many ACLs to hold. "
+ "Stopped learning mode.\n", domain->domainname->name);
+ }
+ return false;
+}
--
^ permalink raw reply [flat|nested] 50+ messages in thread
* [TOMOYO #16 19/25] TOMOYO: Add policy I/O handler.
2009-10-04 12:49 [TOMOYO #16 00/25] Starting TOMOYO 2.3 Tetsuo Handa
` (17 preceding siblings ...)
2009-10-04 12:50 ` [TOMOYO #16 18/25] TOMOYO: Add utility functions Tetsuo Handa
@ 2009-10-04 12:50 ` Tetsuo Handa
2009-10-04 12:50 ` [TOMOYO #16 20/25] TOMOYO: Add policy loader launcher Tetsuo Handa
` (6 subsequent siblings)
25 siblings, 0 replies; 50+ messages in thread
From: Tetsuo Handa @ 2009-10-04 12:50 UTC (permalink / raw)
To: linux-security-module; +Cc: linux-kernel, Tetsuo Handa
[-- Attachment #1: tomoyo-add-policy_io.patch --]
[-- Type: text/plain, Size: 82752 bytes --]
This patch contains code for handling policy I/O.
tomoyo_read_lock()/tomoyo_read_unlock() protects the data against the garbage
collector. I call tomoyo_read_lock() when /sys/kernel/security/tomoyo/
interface is open()ed and call tomoyo_read_unlock() when
/sys/kernel/security/tomoyo/ interface is close()d rather than calling
tomoyo_read_lock()/tomoyo_read_unlock() upon individual read()/write()
requests. In this way, the pointers saved in "struct tomoyo_io_buffer" are
guaranteed to be valid. Please ignore
warning: context imbalance in 'tomoyo_open_control' - wrong count at exit
warning: context imbalance in 'tomoyo_close_control' - unexpected unlock
messages when built with "C=1" option.
I don't call tomoyo_read_lock()/tomoyo_read_unlock() for
/sys/kernel/security/tomoyo/query (for interactive enforcement) and
/sys/kernel/security/tomoyo/grant_log (for access granted logs) and
/sys/kernel/security/tomoyo/reject_log (for access rejected logs) because the
data which these interfaces access is not subjected to the garbage collection.
The "struct tomoyo_profile *"->name is not subjected to GC. It is manually
garbage collected. Thus, to protect it against tomoyo_read_profile(),
tomoyo_write_profile() uses tomoyo_profile_comment_lock spinlock.
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
security/tomoyo/policy_io.c | 2734 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 2734 insertions(+)
--- /dev/null
+++ security-testing-2.6/security/tomoyo/policy_io.c
@@ -0,0 +1,2734 @@
+/*
+ * security/tomoyo/policy_io.c
+ *
+ * Copyright (C) 2005-2009 NTT DATA CORPORATION
+ */
+
+#include "internal.h"
+
+static struct tomoyo_profile tomoyo_default_profile = {
+ .learning = &tomoyo_default_profile.preference,
+ .permissive = &tomoyo_default_profile.preference,
+ .enforcing = &tomoyo_default_profile.preference,
+ .audit = &tomoyo_default_profile.preference,
+#ifdef CONFIG_SECURITY_TOMOYO_AUDIT
+ .preference.audit_max_grant_log = CONFIG_SECURITY_TOMOYO_MAX_GRANT_LOG,
+ .preference.audit_max_reject_log
+ = CONFIG_SECURITY_TOMOYO_MAX_REJECT_LOG,
+#endif
+ .preference.audit_task_info = true,
+ .preference.audit_path_info = true,
+ .preference.enforcing_penalty = 0,
+ .preference.enforcing_verbose = true,
+ .preference.learning_max_entry
+ = CONFIG_SECURITY_TOMOYO_MAX_ACCEPT_ENTRY,
+ .preference.learning_verbose = false,
+ .preference.learning_exec_realpath = true,
+ .preference.learning_exec_argv0 = true,
+ .preference.learning_symlink_target = true,
+ .preference.permissive_verbose = true
+};
+
+/* Profile table. Memory is allocated as needed. */
+static struct tomoyo_profile *tomoyo_profile_ptr[TOMOYO_MAX_PROFILES];
+
+/* Lock for protecting "struct tomoyo_profile *"->comment */
+static DEFINE_SPINLOCK(tomoyo_profile_comment_lock);
+
+/* String table for functionality that takes 4 modes. */
+static const char *tomoyo_mode_4[4] = {
+ "disabled", "learning", "permissive", "enforcing"
+};
+
+/* String table for /sys/kernel/security/tomoyo/profile */
+static const char *tomoyo_mac_keywords[TOMOYO_MAX_MAC_INDEX +
+ TOMOYO_MAX_CAPABILITY_INDEX +
+ TOMOYO_MAX_MAC_CATEGORY_INDEX] = {
+ [TOMOYO_MAC_FILE_EXECUTE]
+ = "file::execute",
+ [TOMOYO_MAC_FILE_OPEN]
+ = "file::open",
+ [TOMOYO_MAC_FILE_CREATE]
+ = "file::create",
+ [TOMOYO_MAC_FILE_UNLINK]
+ = "file::unlink",
+ [TOMOYO_MAC_FILE_MKDIR]
+ = "file::mkdir",
+ [TOMOYO_MAC_FILE_RMDIR]
+ = "file::rmdir",
+ [TOMOYO_MAC_FILE_MKFIFO]
+ = "file::mkfifo",
+ [TOMOYO_MAC_FILE_MKSOCK]
+ = "file::mksock",
+ [TOMOYO_MAC_FILE_TRUNCATE]
+ = "file::truncate",
+ [TOMOYO_MAC_FILE_SYMLINK]
+ = "file::symlink",
+ [TOMOYO_MAC_FILE_REWRITE]
+ = "file::rewrite",
+ [TOMOYO_MAC_FILE_MKBLOCK]
+ = "file::mkblock",
+ [TOMOYO_MAC_FILE_MKCHAR]
+ = "file::mkchar",
+ [TOMOYO_MAC_FILE_LINK]
+ = "file::link",
+ [TOMOYO_MAC_FILE_RENAME]
+ = "file::rename",
+ [TOMOYO_MAC_FILE_CHMOD]
+ = "file::chmod",
+ [TOMOYO_MAC_FILE_CHOWN]
+ = "file::chown",
+ [TOMOYO_MAC_FILE_CHGRP]
+ = "file::chgrp",
+ [TOMOYO_MAC_FILE_IOCTL]
+ = "file::ioctl",
+ [TOMOYO_MAC_FILE_CHROOT]
+ = "file::chroot",
+ [TOMOYO_MAC_FILE_MOUNT]
+ = "file::mount",
+ [TOMOYO_MAC_FILE_UMOUNT]
+ = "file::umount",
+ [TOMOYO_MAC_FILE_PIVOT_ROOT]
+ = "file::pivot_root",
+ [TOMOYO_MAC_ENVIRON]
+ = "misc::env",
+ [TOMOYO_MAC_NETWORK_UDP_BIND]
+ = "network::inet_udp_bind",
+ [TOMOYO_MAC_NETWORK_UDP_CONNECT]
+ = "network::inet_udp_connect",
+ [TOMOYO_MAC_NETWORK_TCP_BIND]
+ = "network::inet_tcp_bind",
+ [TOMOYO_MAC_NETWORK_TCP_LISTEN]
+ = "network::inet_tcp_listen",
+ [TOMOYO_MAC_NETWORK_TCP_CONNECT]
+ = "network::inet_tcp_connect",
+ [TOMOYO_MAC_NETWORK_TCP_ACCEPT]
+ = "network::inet_tcp_accept",
+ [TOMOYO_MAC_NETWORK_RAW_BIND]
+ = "network::inet_raw_bind",
+ [TOMOYO_MAC_NETWORK_RAW_CONNECT]
+ = "network::inet_raw_connect",
+ [TOMOYO_MAX_MAC_INDEX + TOMOYO_INET_STREAM_SOCKET_CREATE]
+ = "capability::inet_tcp_create",
+ [TOMOYO_MAX_MAC_INDEX + TOMOYO_INET_STREAM_SOCKET_LISTEN]
+ = "capability::inet_tcp_listen",
+ [TOMOYO_MAX_MAC_INDEX + TOMOYO_INET_STREAM_SOCKET_CONNECT]
+ = "capability::inet_tcp_connect",
+ [TOMOYO_MAX_MAC_INDEX + TOMOYO_USE_INET_DGRAM_SOCKET]
+ = "capability::use_inet_udp",
+ [TOMOYO_MAX_MAC_INDEX + TOMOYO_USE_INET_RAW_SOCKET]
+ = "capability::use_inet_ip",
+ [TOMOYO_MAX_MAC_INDEX + TOMOYO_USE_ROUTE_SOCKET]
+ = "capability::use_route",
+ [TOMOYO_MAX_MAC_INDEX + TOMOYO_USE_PACKET_SOCKET]
+ = "capability::use_packet",
+ [TOMOYO_MAX_MAC_INDEX + TOMOYO_SYS_MOUNT]
+ = "capability::SYS_MOUNT",
+ [TOMOYO_MAX_MAC_INDEX + TOMOYO_SYS_UMOUNT]
+ = "capability::SYS_UMOUNT",
+ [TOMOYO_MAX_MAC_INDEX + TOMOYO_SYS_CHROOT]
+ = "capability::SYS_CHROOT",
+ [TOMOYO_MAX_MAC_INDEX + TOMOYO_CREATE_FIFO]
+ = "capability::create_fifo",
+ [TOMOYO_MAX_MAC_INDEX + TOMOYO_CREATE_BLOCK_DEV]
+ = "capability::create_block_dev",
+ [TOMOYO_MAX_MAC_INDEX + TOMOYO_CREATE_CHAR_DEV]
+ = "capability::create_char_dev",
+ [TOMOYO_MAX_MAC_INDEX + TOMOYO_CREATE_UNIX_SOCKET]
+ = "capability::create_unix_socket",
+ [TOMOYO_MAX_MAC_INDEX + TOMOYO_SYS_LINK]
+ = "capability::SYS_LINK",
+ [TOMOYO_MAX_MAC_INDEX + TOMOYO_SYS_SYMLINK]
+ = "capability::SYS_SYMLINK",
+ [TOMOYO_MAX_MAC_INDEX + TOMOYO_SYS_RENAME]
+ = "capability::SYS_RENAME",
+ [TOMOYO_MAX_MAC_INDEX + TOMOYO_SYS_UNLINK]
+ = "capability::SYS_UNLINK",
+ [TOMOYO_MAX_MAC_INDEX + TOMOYO_SYS_CHMOD]
+ = "capability::SYS_CHMOD",
+ [TOMOYO_MAX_MAC_INDEX + TOMOYO_SYS_CHOWN]
+ = "capability::SYS_CHOWN",
+ [TOMOYO_MAX_MAC_INDEX + TOMOYO_SYS_IOCTL]
+ = "capability::SYS_IOCTL",
+ [TOMOYO_MAX_MAC_INDEX + TOMOYO_SYS_PIVOT_ROOT]
+ = "capability::SYS_PIVOT_ROOT",
+ [TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_CAPABILITY_INDEX
+ + TOMOYO_MAC_CATEGORY_FILE] = "file",
+ [TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_CAPABILITY_INDEX
+ + TOMOYO_MAC_CATEGORY_NETWORK] = "network",
+ [TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_CAPABILITY_INDEX
+ + TOMOYO_MAC_CATEGORY_MISC] = "misc",
+ [TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_CAPABILITY_INDEX
+ + TOMOYO_MAC_CATEGORY_CAPABILITY] = "capability",
+};
+
+/* Permit policy management by non-root user? */
+static bool tomoyo_manage_by_non_root;
+
+/**
+ * tomoyo_cap2keyword - Convert capability operation to capability name.
+ *
+ * @operation: The capability index.
+ *
+ * Returns the name of the specified capability's name.
+ */
+const char *tomoyo_cap2keyword(const u8 operation)
+{
+ return operation < TOMOYO_MAX_CAPABILITY_INDEX
+ ? tomoyo_mac_keywords[TOMOYO_MAX_MAC_INDEX + operation] + 12 :
+ NULL;
+}
+
+/**
+ * tomoyo_yesno - Return "yes" or "no".
+ *
+ * @value: Bool value.
+ */
+static const char *tomoyo_yesno(const unsigned int value)
+{
+ return value ? "yes" : "no";
+}
+
+/**
+ * tomoyo_io_printf - Transactional printf() to "struct tomoyo_io_buffer" structure.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @fmt: The printf()'s format string, followed by parameters.
+ *
+ * Returns true on success, false otherwise.
+ *
+ * The snprintf() will truncate, but tomoyo_io_printf() won't.
+ */
+bool tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...)
+{
+ va_list args;
+ int len;
+ int pos = head->read_avail;
+ int size = head->readbuf_size - pos;
+ if (size <= 0)
+ return false;
+ va_start(args, fmt);
+ len = vsnprintf(head->read_buf + pos, size, fmt, args);
+ va_end(args);
+ if (pos + len >= head->readbuf_size)
+ return false;
+ head->read_avail += len;
+ return true;
+}
+
+/**
+ * tomoyo_find_or_assign_new_profile - Create a new profile.
+ *
+ * @profile: Profile number to create.
+ *
+ * Returns pointer to "struct tomoyo_profile" on success, NULL otherwise.
+ */
+static struct tomoyo_profile *tomoyo_find_or_assign_new_profile(const unsigned
+ int profile)
+{
+ struct tomoyo_profile *ptr;
+ struct tomoyo_profile *entry;
+ if (profile >= TOMOYO_MAX_PROFILES)
+ return NULL;
+ ptr = tomoyo_profile_ptr[profile];
+ if (ptr)
+ return ptr;
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ mutex_lock(&tomoyo_policy_lock);
+ ptr = tomoyo_profile_ptr[profile];
+ if (!ptr && tomoyo_memory_ok(entry, sizeof(*entry))) {
+ ptr = entry;
+ ptr->audit = &tomoyo_default_profile.preference;
+ ptr->learning = &tomoyo_default_profile.preference;
+ ptr->permissive = &tomoyo_default_profile.preference;
+ ptr->enforcing = &tomoyo_default_profile.preference;
+ ptr->default_config = TOMOYO_CONFIG_DISABLED |
+ TOMOYO_CONFIG_WANT_GRANT_LOG |
+ TOMOYO_CONFIG_WANT_REJECT_LOG;
+ memset(ptr->config, TOMOYO_CONFIG_USE_DEFAULT,
+ sizeof(ptr->config));
+ mb(); /* Avoid out-of-order execution. */
+ tomoyo_profile_ptr[profile] = ptr;
+ entry = NULL;
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+ kfree(entry);
+ return ptr;
+}
+
+/**
+ * tomoyo_check_profile - Check all profiles currently assigned to domains are defined.
+ */
+void tomoyo_check_profile(void)
+{
+ struct tomoyo_domain_info *domain;
+ tomoyo_policy_loaded = true;
+ list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
+ const u8 profile = domain->profile;
+ if (tomoyo_profile_ptr[profile])
+ continue;
+ panic("Profile %u (used by '%s') not defined.\n",
+ profile, domain->domainname->name);
+ }
+}
+
+/**
+ * tomoyo_profile - Find a profile.
+ *
+ * @profile: Profile number to find.
+ *
+ * Returns pointer to "struct tomoyo_profile".
+ */
+struct tomoyo_profile *tomoyo_profile(const u8 profile)
+{
+ struct tomoyo_profile *ptr = tomoyo_profile_ptr[profile];
+ if (!tomoyo_policy_loaded)
+ return &tomoyo_default_profile;
+ BUG_ON(!ptr);
+ return ptr;
+}
+
+/**
+ * tomoyo_write_profile - Write profile table.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_write_profile(struct tomoyo_io_buffer *head)
+{
+ char *data = head->write_buf;
+ unsigned int i;
+ int value;
+ int mode;
+ u8 config;
+ bool use_default = false;
+ char *cp;
+ struct tomoyo_profile *profile;
+ i = simple_strtoul(data, &cp, 10);
+ if (data == cp) {
+ profile = &tomoyo_default_profile;
+ } else {
+ if (*cp != '-')
+ return -EINVAL;
+ data = cp + 1;
+ profile = tomoyo_find_or_assign_new_profile(i);
+ if (!profile)
+ return -EINVAL;
+ }
+ cp = strchr(data, '=');
+ if (!cp)
+ return -EINVAL;
+ *cp++ = '\0';
+ if (profile != &tomoyo_default_profile)
+ use_default = strstr(cp, "use_default") != NULL;
+ if (strstr(cp, "verbose=yes"))
+ value = 1;
+ else if (strstr(cp, "verbose=no"))
+ value = 0;
+ else
+ value = -1;
+ if (!strcmp(data, "PREFERENCE::audit")) {
+#ifdef CONFIG_SECURITY_TOMOYO_AUDIT
+ char *cp2;
+#endif
+ if (use_default) {
+ profile->audit = &tomoyo_default_profile.preference;
+ return 0;
+ }
+ profile->audit = &profile->preference;
+#ifdef CONFIG_SECURITY_TOMOYO_AUDIT
+ cp2 = strstr(cp, "max_grant_log=");
+ if (cp2)
+ sscanf(cp2 + 14, "%u",
+ &profile->preference.audit_max_grant_log);
+ cp2 = strstr(cp, "max_reject_log=");
+ if (cp2)
+ sscanf(cp2 + 15, "%u",
+ &profile->preference.audit_max_reject_log);
+#endif
+ if (strstr(cp, "task_info=yes"))
+ profile->preference.audit_task_info = true;
+ else if (strstr(cp, "task_info=no"))
+ profile->preference.audit_task_info = false;
+ if (strstr(cp, "path_info=yes"))
+ profile->preference.audit_path_info = true;
+ else if (strstr(cp, "path_info=no"))
+ profile->preference.audit_path_info = false;
+ return 0;
+ }
+ if (!strcmp(data, "PREFERENCE::enforcing")) {
+ char *cp2;
+ if (use_default) {
+ profile->enforcing
+ = &tomoyo_default_profile.preference;
+ return 0;
+ }
+ profile->enforcing = &profile->preference;
+ if (value >= 0)
+ profile->preference.enforcing_verbose = value;
+ cp2 = strstr(cp, "penalty=");
+ if (cp2)
+ sscanf(cp2 + 8, "%u",
+ &profile->preference.enforcing_penalty);
+ return 0;
+ }
+ if (!strcmp(data, "PREFERENCE::permissive")) {
+ if (use_default) {
+ profile->permissive
+ = &tomoyo_default_profile.preference;
+ return 0;
+ }
+ profile->permissive = &profile->preference;
+ if (value >= 0)
+ profile->preference.permissive_verbose = value;
+ return 0;
+ }
+ if (!strcmp(data, "PREFERENCE::learning")) {
+ char *cp2;
+ if (use_default) {
+ profile->learning = &tomoyo_default_profile.preference;
+ return 0;
+ }
+ profile->learning = &profile->preference;
+ if (value >= 0)
+ profile->preference.learning_verbose = value;
+ cp2 = strstr(cp, "max_entry=");
+ if (cp2)
+ sscanf(cp2 + 10, "%u",
+ &profile->preference.learning_max_entry);
+ if (strstr(cp, "exec.realpath=yes"))
+ profile->preference.learning_exec_realpath = true;
+ else if (strstr(cp, "exec.realpath=no"))
+ profile->preference.learning_exec_realpath = false;
+ if (strstr(cp, "exec.argv0=yes"))
+ profile->preference.learning_exec_argv0 = true;
+ else if (strstr(cp, "exec.argv0=no"))
+ profile->preference.learning_exec_argv0 = false;
+ if (strstr(cp, "symlink.target=yes"))
+ profile->preference.learning_symlink_target = true;
+ else if (strstr(cp, "symlink.target=no"))
+ profile->preference.learning_symlink_target = false;
+ return 0;
+ }
+ if (profile == &tomoyo_default_profile)
+ return -EINVAL;
+ if (!strcmp(data, "COMMENT")) {
+ const struct tomoyo_path_info *new_comment
+ = tomoyo_get_name(cp);
+ const struct tomoyo_path_info *old_comment;
+ /* Protect reader from tomoyo_put_name(). */
+ spin_lock(&tomoyo_profile_comment_lock);
+ old_comment = profile->comment;
+ profile->comment = new_comment;
+ spin_unlock(&tomoyo_profile_comment_lock);
+ tomoyo_put_name(old_comment);
+ return 0;
+ }
+ if (!strcmp(data, "CONFIG")) {
+ i = TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_CAPABILITY_INDEX
+ + TOMOYO_MAX_MAC_CATEGORY_INDEX;
+ config = profile->default_config;
+ } else if (tomoyo_str_starts(&data, "CONFIG::")) {
+ config = 0;
+ for (i = 0; i < TOMOYO_MAX_MAC_INDEX
+ + TOMOYO_MAX_CAPABILITY_INDEX
+ + TOMOYO_MAX_MAC_CATEGORY_INDEX; i++) {
+ if (strcmp(data, tomoyo_mac_keywords[i]))
+ continue;
+ config = profile->config[i];
+ break;
+ }
+ if (i == TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_CAPABILITY_INDEX
+ + TOMOYO_MAX_MAC_CATEGORY_INDEX)
+ return -EINVAL;
+ } else {
+ return -EINVAL;
+ }
+ if (use_default) {
+ config = TOMOYO_CONFIG_USE_DEFAULT;
+ } else {
+ for (mode = 3; mode >= 0; mode--)
+ if (strstr(cp, tomoyo_mode_4[mode]))
+ /*
+ * Update lower 3 bits in order to distinguish
+ * 'config' from 'TOMOYO_CONFIG_USE_DEAFULT'.
+ */
+ config = (config & ~7) | mode;
+#ifdef CONFIG_SECURITY_TOMOYO_AUDIT
+ if (config != TOMOYO_CONFIG_USE_DEFAULT) {
+ if (strstr(cp, "grant_log=yes"))
+ config |= TOMOYO_CONFIG_WANT_GRANT_LOG;
+ else if (strstr(cp, "grant_log=no"))
+ config &= ~TOMOYO_CONFIG_WANT_GRANT_LOG;
+ if (strstr(cp, "reject_log=yes"))
+ config |= TOMOYO_CONFIG_WANT_REJECT_LOG;
+ else if (strstr(cp, "reject_log=no"))
+ config &= ~TOMOYO_CONFIG_WANT_REJECT_LOG;
+ }
+#endif
+ }
+ if (i < TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_CAPABILITY_INDEX
+ + TOMOYO_MAX_MAC_CATEGORY_INDEX)
+ profile->config[i] = config;
+ else if (config != TOMOYO_CONFIG_USE_DEFAULT)
+ profile->default_config = config;
+ return 0;
+}
+
+/**
+ * tomoyo_read_profile - Read profile table.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ */
+static void tomoyo_read_profile(struct tomoyo_io_buffer *head)
+{
+ int index;
+ if (head->read_eof)
+ return;
+ if (head->read_bit)
+ goto body;
+ tomoyo_io_printf(head, "PROFILE_VERSION=%s\n", "20090903");
+ tomoyo_io_printf(head, "PREFERENCE::audit={ "
+#ifdef CONFIG_SECURITY_TOMOYO_AUDIT
+ "max_grant_log=%u max_reject_log=%u "
+#endif
+ "task_info=%s path_info=%s }\n",
+#ifdef CONFIG_SECURITY_TOMOYO_AUDIT
+ tomoyo_default_profile.preference.audit_max_grant_log,
+ tomoyo_default_profile.preference.
+ audit_max_reject_log,
+#endif
+ tomoyo_yesno(tomoyo_default_profile.preference.
+ audit_task_info),
+ tomoyo_yesno(tomoyo_default_profile.preference.
+ audit_path_info));
+ tomoyo_io_printf(head, "PREFERENCE::learning={ verbose=%s "
+ "max_entry=%u exec.realpath=%s exec.argv0=%s "
+ "symlink.target=%s }\n",
+ tomoyo_yesno(tomoyo_default_profile.preference.
+ learning_verbose),
+ tomoyo_default_profile.preference.learning_max_entry,
+ tomoyo_yesno(tomoyo_default_profile.preference.
+ learning_exec_realpath),
+ tomoyo_yesno(tomoyo_default_profile.preference.
+ learning_exec_argv0),
+ tomoyo_yesno(tomoyo_default_profile.preference.
+ learning_symlink_target));
+ tomoyo_io_printf(head, "PREFERENCE::permissive={ verbose=%s }\n",
+ tomoyo_yesno(tomoyo_default_profile.preference.
+ permissive_verbose));
+ tomoyo_io_printf(head, "PREFERENCE::enforcing={ verbose=%s penalty=%u "
+ "}\n",
+ tomoyo_yesno(tomoyo_default_profile.preference.
+ enforcing_verbose),
+ tomoyo_default_profile.preference.enforcing_penalty);
+ head->read_bit = 1;
+ body:
+ for (index = head->read_step; index < TOMOYO_MAX_PROFILES; index++) {
+ bool done;
+ u8 config;
+ int i;
+ int pos;
+ const struct tomoyo_profile *profile
+ = tomoyo_profile_ptr[index];
+ head->read_step = index;
+ if (!profile)
+ continue;
+ pos = head->read_avail;
+ spin_lock(&tomoyo_profile_comment_lock);
+ done = tomoyo_io_printf(head, "%u-COMMENT=%s\n", index,
+ profile->comment ?
+ profile->comment->name : "");
+ spin_unlock(&tomoyo_profile_comment_lock);
+ if (!done)
+ goto out;
+ config = profile->default_config;
+#ifdef CONFIG_SECURITY_TOMOYO_AUDIT
+ if (!tomoyo_io_printf(head, "%u-CONFIG={ mode=%s grant_log=%s "
+ "reject_log=%s }\n", index,
+ tomoyo_mode_4[config & 3],
+ tomoyo_yesno(config &
+ TOMOYO_CONFIG_WANT_GRANT_LOG),
+ tomoyo_yesno(config &
+ TOMOYO_CONFIG_WANT_REJECT_LOG)))
+ goto out;
+#else
+ if (!tomoyo_io_printf(head, "%u-CONFIG={ mode=%s }\n", index,
+ tomoyo_mode_4[config & 3]))
+ goto out;
+#endif
+ for (i = 0; i < TOMOYO_MAX_MAC_INDEX
+ + TOMOYO_MAX_CAPABILITY_INDEX
+ + TOMOYO_MAX_MAC_CATEGORY_INDEX; i++) {
+#ifdef CONFIG_SECURITY_TOMOYO_AUDIT
+ const char *g;
+ const char *r;
+#endif
+ config = profile->config[i];
+ if (config == TOMOYO_CONFIG_USE_DEFAULT)
+ continue;
+#ifdef CONFIG_SECURITY_TOMOYO_AUDIT
+ g = tomoyo_yesno(config &
+ TOMOYO_CONFIG_WANT_GRANT_LOG);
+ r = tomoyo_yesno(config &
+ TOMOYO_CONFIG_WANT_REJECT_LOG);
+ if (!tomoyo_io_printf(head, "%u-CONFIG::%s={ mode=%s "
+ "grant_log=%s reject_log=%s }\n",
+ index, tomoyo_mac_keywords[i],
+ tomoyo_mode_4[config & 3], g, r))
+ goto out;
+#else
+ if (!tomoyo_io_printf(head,
+ "%u-CONFIG::%s={ mode=%s }\n",
+ index, tomoyo_mac_keywords[i],
+ tomoyo_mode_4[config & 3]))
+ goto out;
+#endif
+ }
+ if (profile->audit != &tomoyo_default_profile.preference &&
+ !tomoyo_io_printf(head, "%u-PREFERENCE::audit={ "
+#ifdef CONFIG_SECURITY_TOMOYO_AUDIT
+ "max_grant_log=%u max_reject_log=%u "
+#endif
+ "task_info=%s path_info=%s }\n", index,
+#ifdef CONFIG_SECURITY_TOMOYO_AUDIT
+ profile->preference.audit_max_grant_log,
+ profile->preference.audit_max_reject_log,
+#endif
+ tomoyo_yesno(profile->preference.
+ audit_task_info),
+ tomoyo_yesno(profile->preference.
+ audit_path_info)))
+ goto out;
+ if (profile->learning != &tomoyo_default_profile.preference &&
+ !tomoyo_io_printf(head, "%u-PREFERENCE::learning={ "
+ "verbose=%s max_entry=%u "
+ "exec.realpath=%s exec.argv0=%s "
+ "symlink.target=%s }\n", index,
+ tomoyo_yesno(profile->preference.
+ learning_verbose),
+ profile->preference.learning_max_entry,
+ tomoyo_yesno(profile->preference.
+ learning_exec_realpath),
+ tomoyo_yesno(profile->preference.
+ learning_exec_argv0),
+ tomoyo_yesno(profile->preference.
+ learning_symlink_target)))
+ goto out;
+ if (profile->permissive != &tomoyo_default_profile.preference
+ && !tomoyo_io_printf(head, "%u-PREFERENCE::permissive={ "
+ "verbose=%s }\n", index,
+ tomoyo_yesno(profile->preference.
+ permissive_verbose)))
+ goto out;
+ if (profile->enforcing != &tomoyo_default_profile.preference &&
+ !tomoyo_io_printf(head, "%u-PREFERENCE::enforcing={ "
+ "verbose=%s penalty=%u }\n", index,
+ tomoyo_yesno(profile->preference.
+ enforcing_verbose),
+ profile->preference.enforcing_penalty))
+ goto out;
+ continue;
+ out:
+ head->read_avail = pos;
+ break;
+ }
+ if (index == TOMOYO_MAX_PROFILES)
+ head->read_eof = true;
+}
+
+/*
+ * tomoyo_policy_manager_list is used for holding list of domainnames or
+ * programs which are permitted to modify configuration via
+ * /sys/kernel/security/tomoyo/ interface.
+ *
+ * An entry is added by
+ *
+ * # echo '<kernel> /sbin/mingetty /bin/login /bin/bash' > \
+ * /sys/kernel/security/tomoyo/manager
+ * (if you want to specify by a domainname)
+ *
+ * or
+ *
+ * # echo '/usr/lib/ccs/editpolicy' > /sys/kernel/security/tomoyo/manager
+ * (if you want to specify by a program's location)
+ *
+ * and is deleted by
+ *
+ * # echo 'delete <kernel> /sbin/mingetty /bin/login /bin/bash' > \
+ * /sys/kernel/security/tomoyo/manager
+ *
+ * or
+ *
+ * # echo 'delete /usr/lib/ccs/editpolicy' > \
+ * /sys/kernel/security/tomoyo/manager
+ *
+ * and all entries are retrieved by
+ *
+ * # cat /sys/kernel/security/tomoyo/manager
+ */
+LIST_HEAD(tomoyo_policy_manager_list);
+
+/**
+ * tomoyo_update_manager_entry - Add a manager entry.
+ *
+ * @manager: The path to manager or the domainnamme.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_update_manager_entry(const char *manager,
+ const bool is_delete)
+{
+ struct tomoyo_policy_manager_entry *entry = NULL;
+ struct tomoyo_policy_manager_entry *ptr;
+ struct tomoyo_policy_manager_entry e = { };
+ int error = is_delete ? -ENOENT : -ENOMEM;
+ if (tomoyo_is_domain_def(manager)) {
+ if (!tomoyo_is_correct_domain(manager))
+ return -EINVAL;
+ e.is_domain = true;
+ } else {
+ if (!tomoyo_is_correct_path(manager, 1, -1, -1))
+ return -EINVAL;
+ }
+ e.manager = tomoyo_get_name(manager);
+ if (!e.manager)
+ return -ENOMEM;
+ if (!is_delete)
+ entry = kmalloc(sizeof(e), GFP_KERNEL);
+ mutex_lock(&tomoyo_policy_lock);
+ list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, list) {
+ if (ptr->manager != e.manager)
+ continue;
+ ptr->is_deleted = is_delete;
+ error = 0;
+ break;
+ }
+ if (!is_delete && error && tomoyo_commit_ok(entry, &e, sizeof(e))) {
+ list_add_tail_rcu(&entry->list, &tomoyo_policy_manager_list);
+ entry = NULL;
+ error = 0;
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+ tomoyo_put_name(e.manager);
+ kfree(entry);
+ return error;
+}
+
+/**
+ * tomoyo_write_manager_policy - Write manager policy.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_write_manager_policy(struct tomoyo_io_buffer *head)
+{
+ char *data = head->write_buf;
+ bool is_delete = tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE);
+ if (!strcmp(data, "manage_by_non_root")) {
+ tomoyo_manage_by_non_root = !is_delete;
+ return 0;
+ }
+ return tomoyo_update_manager_entry(data, is_delete);
+}
+
+/**
+ * tomoyo_read_manager_policy - Read manager policy.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static void tomoyo_read_manager_policy(struct tomoyo_io_buffer *head)
+{
+ struct list_head *pos;
+ if (head->read_eof)
+ return;
+ list_for_each_cookie(pos, head->read_var2,
+ &tomoyo_policy_manager_list) {
+ struct tomoyo_policy_manager_entry *ptr;
+ ptr = list_entry(pos, struct tomoyo_policy_manager_entry,
+ list);
+ if (ptr->is_deleted)
+ continue;
+ if (!tomoyo_io_printf(head, "%s\n", ptr->manager->name))
+ return;
+ }
+ head->read_eof = true;
+}
+
+/**
+ * tomoyo_is_policy_manager - Check whether the current process is a policy manager.
+ *
+ * Returns true if the current process is permitted to modify policy
+ * via /sys/kernel/security/tomoyo/ interface.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static bool tomoyo_is_policy_manager(void)
+{
+ struct tomoyo_policy_manager_entry *ptr;
+ const char *exe;
+ struct task_struct *task = current;
+ const struct tomoyo_path_info *domainname
+ = tomoyo_current_domain()->domainname;
+ bool found = false;
+ if (!tomoyo_policy_loaded)
+ return true;
+ if (task->tomoyo_flags & TOMOYO_TASK_IS_POLICY_MANAGER)
+ return true;
+ if (!tomoyo_manage_by_non_root && (current_uid() || current_euid()))
+ return false;
+ list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, list) {
+ if (!ptr->is_deleted && ptr->is_domain
+ && !tomoyo_pathcmp(domainname, ptr->manager)) {
+ /* Set manager flag. */
+ task->tomoyo_flags |= TOMOYO_TASK_IS_POLICY_MANAGER;
+ return true;
+ }
+ }
+ exe = tomoyo_get_exe();
+ if (!exe)
+ return false;
+ list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, list) {
+ if (!ptr->is_deleted && !ptr->is_domain
+ && !strcmp(exe, ptr->manager->name)) {
+ found = true;
+ /* Set manager flag. */
+ task->tomoyo_flags |= TOMOYO_TASK_IS_POLICY_MANAGER;
+ break;
+ }
+ }
+ if (!found) { /* Reduce error messages. */
+ static pid_t tomoyo_last_pid;
+ const pid_t pid = current->pid;
+ if (tomoyo_last_pid != pid) {
+ printk(KERN_WARNING "%s ( %s ) is not permitted to "
+ "update policies.\n", domainname->name, exe);
+ tomoyo_last_pid = pid;
+ }
+ }
+ kfree(exe);
+ return found;
+}
+
+/**
+ * tomoyo_find_condition_part - Find condition part from the statement.
+ *
+ * @data: String to parse.
+ *
+ * Returns pointer to the condition part if it was found in the statement,
+ * NULL otherwise.
+ */
+static char *tomoyo_find_condition_part(char *data)
+{
+ char *cp = strstr(data, " if ");
+ if (cp) {
+ while (1) {
+ char *cp2 = strstr(cp + 3, " if ");
+ if (!cp2)
+ break;
+ cp = cp2;
+ }
+ *cp++ = '\0';
+ } else {
+ cp = strstr(data, " ; set ");
+ if (cp)
+ *cp++ = '\0';
+ }
+ return cp;
+}
+
+/**
+ * tomoyo_is_select_one - Parse select command.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @data: String to parse.
+ *
+ * Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head,
+ const char *data)
+{
+ unsigned int pid;
+ struct tomoyo_domain_info *domain = NULL;
+ bool global_pid = false;
+ if (!strcmp(data, "allow_execute")) {
+ head->read_execute_only = true;
+ return true;
+ }
+ if (sscanf(data, "pid=%u", &pid) == 1 ||
+ (global_pid = true, sscanf(data, "global-pid=%u", &pid) == 1)) {
+ struct task_struct *p;
+ read_lock(&tasklist_lock);
+ if (global_pid)
+ p = find_task_by_pid_ns(pid, &init_pid_ns);
+ else
+ p = find_task_by_vpid(pid);
+ if (p)
+ domain = tomoyo_task_domain(p);
+ read_unlock(&tasklist_lock);
+ } else if (!strncmp(data, "domain=", 7)) {
+ if (tomoyo_is_domain_def(data + 7))
+ domain = tomoyo_find_domain(data + 7);
+ } else
+ return false;
+ head->write_var1 = domain;
+ /* Accessing read_buf is safe because head->io_sem is held. */
+ if (!head->read_buf)
+ return true; /* Do nothing if open(O_WRONLY). */
+ head->read_avail = 0;
+ tomoyo_io_printf(head, "# select %s\n", data);
+ head->read_single_domain = true;
+ head->read_eof = !domain;
+ if (domain) {
+ struct tomoyo_domain_info *d;
+ head->read_var1 = NULL;
+ list_for_each_entry_rcu(d, &tomoyo_domain_list, list) {
+ if (d == domain)
+ break;
+ head->read_var1 = &d->list;
+ }
+ head->read_var2 = NULL;
+ head->read_bit = 0;
+ head->read_step = 0;
+ if (domain->is_deleted)
+ tomoyo_io_printf(head,
+ "# This is a deleted domain.\n");
+ }
+ return true;
+}
+
+static int tomoyo_write_domain_policy2(char *data,
+ struct tomoyo_domain_info *domain,
+ struct tomoyo_condition *cond,
+ const bool is_delete)
+{
+ if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_ALLOW_CAPABILITY))
+ return tomoyo_write_capability_policy(data, domain, cond,
+ is_delete);
+ if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_ALLOW_NETWORK))
+ return tomoyo_write_network_policy(data, domain, cond,
+ is_delete);
+ if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_ALLOW_ENV))
+ return tomoyo_write_env_policy(data, domain, cond, is_delete);
+ if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_ALLOW_MOUNT))
+ return tomoyo_write_mount_policy(data, domain, cond,
+ is_delete);
+ return tomoyo_write_file_policy(data, domain, cond, is_delete);
+}
+
+/**
+ * tomoyo_write_domain_policy - Write domain policy.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_write_domain_policy(struct tomoyo_io_buffer *head)
+{
+ char *data = head->write_buf;
+ struct tomoyo_domain_info *domain = head->write_var1;
+ bool is_delete = false;
+ bool is_select = false;
+ unsigned int profile;
+ struct tomoyo_condition *cond = NULL;
+ char *cp;
+ int error;
+ if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE))
+ is_delete = true;
+ else if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_SELECT))
+ is_select = true;
+ if (is_select && tomoyo_is_select_one(head, data))
+ return 0;
+ /* Don't allow updating policies by non manager programs. */
+ if (!tomoyo_is_policy_manager())
+ return -EPERM;
+ if (tomoyo_is_domain_def(data)) {
+ domain = NULL;
+ if (is_delete)
+ tomoyo_delete_domain(data);
+ else if (is_select)
+ domain = tomoyo_find_domain(data);
+ else
+ domain = tomoyo_find_or_assign_new_domain(data, 0);
+ head->write_var1 = domain;
+ return 0;
+ }
+ if (!domain)
+ return -EINVAL;
+
+ if (sscanf(data, TOMOYO_KEYWORD_USE_PROFILE "%u", &profile) == 1
+ && profile < TOMOYO_MAX_PROFILES) {
+ if (!tomoyo_policy_loaded || tomoyo_profile_ptr[(u8) profile])
+ domain->profile = (u8) profile;
+ return 0;
+ }
+ if (!strcmp(data, TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ)) {
+ domain->ignore_global_allow_read = !is_delete;
+ return 0;
+ }
+ if (!strcmp(data, TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_ENV)) {
+ domain->ignore_global_allow_env = !is_delete;
+ return 0;
+ }
+ cp = tomoyo_find_condition_part(data);
+ if (cp) {
+ cond = tomoyo_get_condition(cp);
+ if (!cond)
+ return -EINVAL;
+ }
+ error = tomoyo_write_domain_policy2(data, domain, cond, is_delete);
+ if (cond)
+ tomoyo_put_condition(cond);
+ return error;
+}
+
+/**
+ * tomoyo_print_name_union - Print a tomoyo_name_union.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr: Pointer to "struct tomoyo_name_union".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_print_name_union(struct tomoyo_io_buffer *head,
+ const struct tomoyo_name_union *ptr)
+{
+ int pos = head->read_avail;
+ if (pos && head->read_buf[pos - 1] == ' ')
+ head->read_avail--;
+ if (ptr->is_group)
+ return tomoyo_io_printf(head, " @%s",
+ ptr->group->group_name->name);
+ return tomoyo_io_printf(head, " %s", ptr->filename->name);
+}
+
+/**
+ * tomoyo_print_name_union_quoted - Print a tomoyo_name_union with double quotes.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr: Pointer to "struct tomoyo_name_union".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_print_name_union_quoted(struct tomoyo_io_buffer *head,
+ const struct tomoyo_name_union *ptr)
+{
+ if (ptr->is_group)
+ return tomoyo_io_printf(head, "@%s",
+ ptr->group->group_name->name);
+ return tomoyo_io_printf(head, "\"%s\"", ptr->filename->name);
+}
+
+/**
+ * tomoyo_print_number_union_common - Print a tomoyo_number_union.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr: Pointer to "struct tomoyo_number_union".
+ * @need_space: True if a space character is needed.
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_print_number_union_common
+(struct tomoyo_io_buffer *head, const struct tomoyo_number_union *ptr,
+ const bool need_space)
+{
+ unsigned long min;
+ unsigned long max;
+ u8 min_type;
+ u8 max_type;
+ if (need_space && !tomoyo_io_printf(head, " "))
+ return false;
+ if (ptr->is_group)
+ return tomoyo_io_printf(head, "@%s",
+ ptr->group->group_name->name);
+ min_type = ptr->min_type;
+ max_type = ptr->max_type;
+ min = ptr->values[0];
+ max = ptr->values[1];
+ switch (min_type) {
+ case TOMOYO_VALUE_TYPE_HEXADECIMAL:
+ if (!tomoyo_io_printf(head, "0x%lX", min))
+ return false;
+ break;
+ case TOMOYO_VALUE_TYPE_OCTAL:
+ if (!tomoyo_io_printf(head, "0%lo", min))
+ return false;
+ break;
+ default:
+ if (!tomoyo_io_printf(head, "%lu", min))
+ return false;
+ break;
+ }
+ if (min == max && min_type == max_type)
+ return true;
+ switch (max_type) {
+ case TOMOYO_VALUE_TYPE_HEXADECIMAL:
+ return tomoyo_io_printf(head, "-0x%lX", max);
+ case TOMOYO_VALUE_TYPE_OCTAL:
+ return tomoyo_io_printf(head, "-0%lo", max);
+ default:
+ return tomoyo_io_printf(head, "-%lu", max);
+ }
+}
+
+/**
+ * tomoyo_print_number_union - Print a tomoyo_number_union.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr: Pointer to "struct tomoyo_number_union".
+ *
+ * Returns true on success, false otherwise.
+ */
+bool tomoyo_print_number_union(struct tomoyo_io_buffer *head,
+ const struct tomoyo_number_union *ptr)
+{
+ return tomoyo_print_number_union_common(head, ptr, true);
+}
+
+/**
+ * tomoyo_print_number_union_nospace - Print a tomoyo_number_union without a space character.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr: Pointer to "struct tomoyo_number_union".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_print_number_union_nospace
+(struct tomoyo_io_buffer *head, const struct tomoyo_number_union *ptr)
+{
+ return tomoyo_print_number_union_common(head, ptr, false);
+}
+
+/**
+ * tomoyo_print_condition - Print condition part.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @cond: Pointer to "struct tomoyo_condition". May be NULL.
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_print_condition(struct tomoyo_io_buffer *head,
+ const struct tomoyo_condition *cond)
+{
+ const struct tomoyo_condition_element *condp;
+ const struct tomoyo_number_union *numbers_p;
+ const struct tomoyo_name_union *names_p;
+ const struct tomoyo_argv_entry *argv;
+ const struct tomoyo_envp_entry *envp;
+ u16 condc;
+ u16 i;
+ u16 j;
+ char buffer[32];
+ if (!cond)
+ goto no_condition;
+ condc = cond->condc;
+ condp = (const struct tomoyo_condition_element *) (cond + 1);
+ numbers_p = (const struct tomoyo_number_union *) (condp + condc);
+ names_p = (const struct tomoyo_name_union *)
+ (numbers_p + cond->numbers_count);
+ argv = (const struct tomoyo_argv_entry *) (names_p + cond->names_count);
+ envp = (const struct tomoyo_envp_entry *) (argv + cond->argc);
+ memset(buffer, 0, sizeof(buffer));
+ if (condc && !tomoyo_io_printf(head, "%s", " if"))
+ goto out;
+ for (i = 0; i < condc; i++) {
+ const u8 match = condp->equals;
+ const u8 left = condp->left;
+ const u8 right = condp->right;
+ condp++;
+ switch (left) {
+ case TOMOYO_ARGV_ENTRY:
+ if (!tomoyo_io_printf(head, " exec.argv[%u]%s\"%s\"",
+ argv->index, argv->is_not ?
+ "!=" : "=", argv->value->name))
+ goto out;
+ argv++;
+ continue;
+ case TOMOYO_ENVP_ENTRY:
+ if (!tomoyo_io_printf(head, " exec.envp[\"%s\"]%s",
+ envp->name->name, envp->is_not ?
+ "!=" : "="))
+ goto out;
+ if (envp->value) {
+ if (!tomoyo_io_printf(head, "\"%s\"",
+ envp->value->name))
+ goto out;
+ } else {
+ if (!tomoyo_io_printf(head, "NULL"))
+ goto out;
+ }
+ envp++;
+ continue;
+ case TOMOYO_NUMBER_UNION:
+ if (!tomoyo_print_number_union(head, numbers_p++))
+ goto out;
+ break;
+ default:
+ if (left >= TOMOYO_MAX_CONDITION_KEYWORD)
+ goto out;
+ if (!tomoyo_io_printf(head, " %s",
+ tomoyo_condition_keyword[left]))
+ goto out;
+ break;
+ }
+ if (!tomoyo_io_printf(head, "%s", match ? "=" : "!="))
+ goto out;
+ switch (right) {
+ case TOMOYO_NAME_UNION:
+ if (!tomoyo_print_name_union_quoted(head, names_p++))
+ goto out;
+ break;
+ case TOMOYO_NUMBER_UNION:
+ if (!tomoyo_print_number_union_nospace(head,
+ numbers_p++))
+ goto out;
+ break;
+ default:
+ if (right >= TOMOYO_MAX_CONDITION_KEYWORD)
+ goto out;
+ if (!tomoyo_io_printf(head, "%s",
+ tomoyo_condition_keyword[right]))
+ goto out;
+ break;
+ }
+ }
+ i = cond->post_state[3];
+ if (!i)
+ goto no_condition;
+ if (!tomoyo_io_printf(head, " ; set"))
+ goto out;
+ for (j = 0; j < 3; j++) {
+ if (!(i & (1 << j)))
+ continue;
+ if (!tomoyo_io_printf(head, " task.state[%u]=%u", j,
+ cond->post_state[j]))
+ goto out;
+ }
+ no_condition:
+ if (tomoyo_io_printf(head, "\n"))
+ return true;
+ out:
+ return false;
+}
+
+/**
+ * tomoyo_print_path_acl - Print a single path ACL entry.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr: Pointer to "struct tomoyo_path_acl".
+ * @cond: Pointer to "struct tomoyo_condition". May be NULL.
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_print_path_acl(struct tomoyo_io_buffer *head,
+ struct tomoyo_path_acl *ptr,
+ const struct tomoyo_condition *cond)
+{
+ int pos;
+ u8 bit;
+ const u16 perm = ptr->perm;
+ for (bit = head->read_bit; bit < TOMOYO_MAX_PATH_OPERATION; bit++) {
+ if (!(perm & (1 << bit)))
+ continue;
+ if (head->read_execute_only && bit != TOMOYO_TYPE_EXECUTE)
+ continue;
+ /* Print "read/write" instead of "read" and "write". */
+ if ((bit == TOMOYO_TYPE_READ || bit == TOMOYO_TYPE_WRITE)
+ && (perm & (1 << TOMOYO_TYPE_READ_WRITE)))
+ continue;
+ pos = head->read_avail;
+ if (!tomoyo_io_printf(head, "allow_%s",
+ tomoyo_path2keyword(bit)) ||
+ !tomoyo_print_name_union(head, &ptr->name) ||
+ !tomoyo_print_condition(head, cond)) {
+ head->read_bit = bit;
+ head->read_avail = pos;
+ return false;
+ }
+ }
+ head->read_bit = 0;
+ return true;
+}
+
+/**
+ * tomoyo_print_path_number3_acl - Print a path_number3 ACL entry.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr: Pointer to "struct tomoyo_path_number3_acl".
+ * @cond: Pointer to "struct tomoyo_condition". May be NULL.
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_print_path_number3_acl(struct tomoyo_io_buffer *head,
+ struct tomoyo_path_number3_acl *ptr,
+ const struct tomoyo_condition *cond)
+{
+ int pos;
+ u8 bit;
+ const u16 perm = ptr->perm;
+ for (bit = head->read_bit; bit < TOMOYO_MAX_PATH_NUMBER3_OPERATION;
+ bit++) {
+ if (!(perm & (1 << bit)))
+ continue;
+ pos = head->read_avail;
+ if (!tomoyo_io_printf(head, "allow_%s",
+ tomoyo_path_number32keyword(bit)) ||
+ !tomoyo_print_name_union(head, &ptr->name) ||
+ !tomoyo_print_number_union(head, &ptr->mode) ||
+ !tomoyo_print_number_union(head, &ptr->major) ||
+ !tomoyo_print_number_union(head, &ptr->minor) ||
+ !tomoyo_print_condition(head, cond)) {
+ head->read_bit = bit;
+ head->read_avail = pos;
+ return false;
+ }
+ }
+ head->read_bit = 0;
+ return true;
+}
+
+/**
+ * tomoyo_print_path2_acl - Print a path2 ACL entry.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr: Pointer to "struct tomoyo_path2_acl".
+ * @cond: Pointer to "struct tomoyo_condition". May be NULL.
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_print_path2_acl(struct tomoyo_io_buffer *head,
+ struct tomoyo_path2_acl *ptr,
+ const struct tomoyo_condition *cond)
+{
+ int pos;
+ u8 bit;
+ const u8 perm = ptr->perm;
+ for (bit = head->read_bit; bit < TOMOYO_MAX_PATH2_OPERATION; bit++) {
+ if (!(perm & (1 << bit)))
+ continue;
+ pos = head->read_avail;
+ if (!tomoyo_io_printf(head, "allow_%s",
+ tomoyo_path22keyword(bit)) ||
+ !tomoyo_print_name_union(head, &ptr->name1) ||
+ !tomoyo_print_name_union(head, &ptr->name2) ||
+ !tomoyo_print_condition(head, cond)) {
+ head->read_bit = bit;
+ head->read_avail = pos;
+ return false;
+ }
+ }
+ head->read_bit = 0;
+ return true;
+}
+
+/**
+ * tomoyo_print_path_number_acl - Print a path_number ACL entry.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr: Pointer to "struct tomoyo_path_number_acl".
+ * @cond: Pointer to "struct tomoyo_condition". May be NULL.
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_print_path_number_acl(struct tomoyo_io_buffer *head,
+ struct tomoyo_path_number_acl *ptr,
+ const struct tomoyo_condition *cond)
+{
+ int pos;
+ u8 bit;
+ const u8 perm = ptr->perm;
+ for (bit = head->read_bit; bit < TOMOYO_MAX_PATH_NUMBER_OPERATION;
+ bit++) {
+ if (!(perm & (1 << bit)))
+ continue;
+ pos = head->read_avail;
+ if (!tomoyo_io_printf(head, "allow_%s",
+ tomoyo_path_number2keyword(bit)) ||
+ !tomoyo_print_name_union(head, &ptr->name) ||
+ !tomoyo_print_number_union(head, &ptr->number) ||
+ !tomoyo_print_condition(head, cond)) {
+ head->read_bit = bit;
+ head->read_avail = pos;
+ return false;
+ }
+ }
+ head->read_bit = 0;
+ return true;
+}
+
+/**
+ * tomoyo_print_env_acl - Print an evironment variable name's ACL entry.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr: Pointer to "struct tomoyo_env_acl".
+ * @cond: Pointer to "struct tomoyo_condition". May be NULL.
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_print_env_acl(struct tomoyo_io_buffer *head,
+ struct tomoyo_env_acl *ptr,
+ const struct tomoyo_condition *cond)
+{
+ const int pos = head->read_avail;
+ if (!tomoyo_io_printf(head, TOMOYO_KEYWORD_ALLOW_ENV "%s",
+ ptr->env->name) ||
+ !tomoyo_print_condition(head, cond)) {
+ head->read_avail = pos;
+ return false;
+ }
+ return true;
+}
+
+/**
+ * tomoyo_print_capability_acl - Print a capability ACL entry.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr: Pointer to "struct tomoyo_capability_acl".
+ * @cond: Pointer to "struct tomoyo_condition". May be NULL.
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_print_capability_acl(struct tomoyo_io_buffer *head,
+ struct tomoyo_capability_acl *ptr,
+ const struct tomoyo_condition *cond)
+{
+ const int pos = head->read_avail;
+ if (!tomoyo_io_printf(head, TOMOYO_KEYWORD_ALLOW_CAPABILITY "%s",
+ tomoyo_cap2keyword(ptr->operation)) ||
+ !tomoyo_print_condition(head, cond)) {
+ head->read_avail = pos;
+ return false;
+ }
+ return true;
+}
+
+/**
+ * tomoyo_print_ipv4_entry - Print IPv4 address of a network ACL entry.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr: Pointer to "struct tomoyo_ip_network_acl".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_print_ipv4_entry(struct tomoyo_io_buffer *head,
+ struct tomoyo_ip_network_acl *ptr)
+{
+ const u32 min_address = ptr->address.ipv4.min;
+ const u32 max_address = ptr->address.ipv4.max;
+ if (!tomoyo_io_printf(head, "%u.%u.%u.%u", HIPQUAD(min_address)))
+ return false;
+ if (min_address != max_address
+ && !tomoyo_io_printf(head, "-%u.%u.%u.%u", HIPQUAD(max_address)))
+ return false;
+ return true;
+}
+
+/**
+ * tomoyo_print_ipv6_entry - Print IPv6 address of a network ACL entry.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr: Pointer to "struct tomoyo_ip_network_acl".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_print_ipv6_entry(struct tomoyo_io_buffer *head,
+ struct tomoyo_ip_network_acl *ptr)
+{
+ char buf[64];
+ const struct in6_addr *min_address = ptr->address.ipv6.min;
+ const struct in6_addr *max_address = ptr->address.ipv6.max;
+ tomoyo_print_ipv6(buf, sizeof(buf), min_address);
+ if (!tomoyo_io_printf(head, "%s", buf))
+ return false;
+ if (min_address != max_address) {
+ tomoyo_print_ipv6(buf, sizeof(buf), max_address);
+ if (!tomoyo_io_printf(head, "-%s", buf))
+ return false;
+ }
+ return true;
+}
+
+/**
+ * tomoyo_print_network_acl - Print a network ACL entry.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr: Pointer to "struct tomoyo_ip_network_acl".
+ * @cond: Pointer to "struct tomoyo_condition". May be NULL.
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_print_network_acl(struct tomoyo_io_buffer *head,
+ struct tomoyo_ip_network_acl *ptr,
+ const struct tomoyo_condition *cond)
+{
+ int pos;
+ u8 bit;
+ const u16 perm = ptr->perm;
+ for (bit = head->read_bit; bit < TOMOYO_MAX_NETWORK_OPERATION; bit++) {
+ if (!(perm & (1 << bit)))
+ continue;
+ pos = head->read_avail;
+ if (!tomoyo_io_printf(head, TOMOYO_KEYWORD_ALLOW_NETWORK "%s ",
+ tomoyo_net2keyword(bit)))
+ goto out;
+ switch (ptr->address_type) {
+ case TOMOYO_IP_ADDRESS_TYPE_ADDRESS_GROUP:
+ if (!tomoyo_io_printf(head, "@%s", ptr->address.group->
+ group_name->name))
+ goto out;
+ break;
+ case TOMOYO_IP_ADDRESS_TYPE_IPv4:
+ if (!tomoyo_print_ipv4_entry(head, ptr))
+ goto out;
+ break;
+ case TOMOYO_IP_ADDRESS_TYPE_IPv6:
+ if (!tomoyo_print_ipv6_entry(head, ptr))
+ goto out;
+ break;
+ }
+ if (!tomoyo_print_number_union(head, &ptr->port) ||
+ !tomoyo_print_condition(head, cond))
+ goto out;
+ }
+ head->read_bit = 0;
+ return true;
+ out:
+ head->read_bit = bit;
+ head->read_avail = pos;
+ return false;
+}
+
+/**
+ * tomoyo_print_execute_handler_record - Print an execute handler ACL entry.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @keyword: Name of the keyword.
+ * @ptr: Pointer to "struct tomoyo_execute_handler_record".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_print_execute_handler_record
+(struct tomoyo_io_buffer *head, const char *keyword,
+ struct tomoyo_execute_handler_record *ptr)
+{
+ return tomoyo_io_printf(head, "%s %s\n", keyword, ptr->handler->name);
+}
+
+/**
+ * tomoyo_print_mount_acl - Print a mount ACL entry.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr: Pointer to "struct tomoyo_mount_acl".
+ * @cond: Pointer to "struct tomoyo_condition". May be NULL.
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_print_mount_acl(struct tomoyo_io_buffer *head,
+ struct tomoyo_mount_acl *ptr,
+ const struct tomoyo_condition *cond)
+{
+ const int pos = head->read_avail;
+ if (!tomoyo_io_printf(head, TOMOYO_KEYWORD_ALLOW_MOUNT) ||
+ !tomoyo_print_name_union(head, &ptr->dev_name) ||
+ !tomoyo_print_name_union(head, &ptr->dir_name) ||
+ !tomoyo_print_name_union(head, &ptr->fs_type) ||
+ !tomoyo_print_number_union(head, &ptr->flags) ||
+ !tomoyo_print_condition(head, cond)) {
+ head->read_avail = pos;
+ return false;
+ }
+ return true;
+}
+
+/**
+ * tomoyo_print_entry - Print an ACL entry.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr: Pointer to an ACL entry.
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_print_entry(struct tomoyo_io_buffer *head,
+ struct tomoyo_acl_info *ptr)
+{
+ const struct tomoyo_condition *cond = ptr->cond;
+ const u8 acl_type = ptr->type;
+ if (ptr->is_deleted)
+ return true;
+ if (acl_type == TOMOYO_TYPE_PATH_ACL) {
+ struct tomoyo_path_acl *acl
+ = container_of(ptr, struct tomoyo_path_acl, head);
+ return tomoyo_print_path_acl(head, acl, cond);
+ }
+ if (acl_type == TOMOYO_TYPE_EXECUTE_HANDLER) {
+ struct tomoyo_execute_handler_record *acl
+ = container_of(ptr,
+ struct tomoyo_execute_handler_record,
+ head);
+ const char *keyword = TOMOYO_KEYWORD_EXECUTE_HANDLER;
+ return tomoyo_print_execute_handler_record(head, keyword, acl);
+ }
+ if (acl_type == TOMOYO_TYPE_DENIED_EXECUTE_HANDLER) {
+ struct tomoyo_execute_handler_record *acl
+ = container_of(ptr,
+ struct tomoyo_execute_handler_record,
+ head);
+ const char *keyword = TOMOYO_KEYWORD_DENIED_EXECUTE_HANDLER;
+ return tomoyo_print_execute_handler_record(head, keyword, acl);
+ }
+ if (head->read_execute_only)
+ return true;
+ if (acl_type == TOMOYO_TYPE_PATH_NUMBER3_ACL) {
+ struct tomoyo_path_number3_acl *acl
+ = container_of(ptr, struct tomoyo_path_number3_acl,
+ head);
+ return tomoyo_print_path_number3_acl(head, acl, cond);
+ }
+ if (acl_type == TOMOYO_TYPE_PATH2_ACL) {
+ struct tomoyo_path2_acl *acl
+ = container_of(ptr, struct tomoyo_path2_acl,
+ head);
+ return tomoyo_print_path2_acl(head, acl, cond);
+ }
+ if (acl_type == TOMOYO_TYPE_PATH_NUMBER_ACL) {
+ struct tomoyo_path_number_acl *acl
+ = container_of(ptr, struct tomoyo_path_number_acl,
+ head);
+ return tomoyo_print_path_number_acl(head, acl, cond);
+ }
+ if (acl_type == TOMOYO_TYPE_ENV_ACL) {
+ struct tomoyo_env_acl *acl
+ = container_of(ptr, struct tomoyo_env_acl, head);
+ return tomoyo_print_env_acl(head, acl, cond);
+ }
+ if (acl_type == TOMOYO_TYPE_CAPABILITY_ACL) {
+ struct tomoyo_capability_acl *acl
+ = container_of(ptr, struct tomoyo_capability_acl,
+ head);
+ return tomoyo_print_capability_acl(head, acl, cond);
+ }
+ if (acl_type == TOMOYO_TYPE_IP_NETWORK_ACL) {
+ struct tomoyo_ip_network_acl *acl
+ = container_of(ptr, struct tomoyo_ip_network_acl,
+ head);
+ return tomoyo_print_network_acl(head, acl, cond);
+ }
+ if (acl_type == TOMOYO_TYPE_MOUNT_ACL) {
+ struct tomoyo_mount_acl *acl
+ = container_of(ptr, struct tomoyo_mount_acl, head);
+ return tomoyo_print_mount_acl(head, acl, cond);
+ }
+ BUG(); /* This must not happen. */
+ return false;
+}
+
+/**
+ * tomoyo_read_domain_policy - Read domain policy.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static void tomoyo_read_domain_policy(struct tomoyo_io_buffer *head)
+{
+ struct list_head *dpos;
+ struct list_head *apos;
+ if (head->read_eof)
+ return;
+ if (head->read_step == 0)
+ head->read_step = 1;
+ list_for_each_cookie(dpos, head->read_var1, &tomoyo_domain_list) {
+ struct tomoyo_domain_info *domain;
+ const char *quota_exceeded = "";
+ const char *transition_failed = "";
+ const char *ignore_global_allow_read = "";
+ const char *ignore_global_allow_env = "";
+ domain = list_entry(dpos, struct tomoyo_domain_info, list);
+ if (head->read_step != 1)
+ goto acl_loop;
+ if (domain->is_deleted && !head->read_single_domain)
+ continue;
+ /* Print domainname and flags. */
+ if (domain->quota_warned)
+ quota_exceeded = "quota_exceeded\n";
+ if (domain->domain_transition_failed)
+ transition_failed = "transition_failed\n";
+ if (domain->ignore_global_allow_read)
+ ignore_global_allow_read
+ = TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ "\n";
+ if (domain->ignore_global_allow_env)
+ ignore_global_allow_env
+ = TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_ENV "\n";
+ if (!tomoyo_io_printf(head, "%s\n" TOMOYO_KEYWORD_USE_PROFILE
+ "%u\n%s%s%s%s\n",
+ domain->domainname->name,
+ domain->profile, quota_exceeded,
+ transition_failed,
+ ignore_global_allow_read,
+ ignore_global_allow_env))
+ return;
+ head->read_step = 2;
+ acl_loop:
+ if (head->read_step == 3)
+ goto tail_mark;
+ /* Print ACL entries in the domain. */
+ list_for_each_cookie(apos, head->read_var2,
+ &domain->acl_info_list) {
+ struct tomoyo_acl_info *ptr
+ = list_entry(apos, struct tomoyo_acl_info,
+ list);
+ if (!tomoyo_print_entry(head, ptr))
+ return;
+ }
+ head->read_step = 3;
+ tail_mark:
+ if (!tomoyo_io_printf(head, "\n"))
+ return;
+ head->read_step = 1;
+ if (head->read_single_domain)
+ break;
+ }
+ head->read_eof = true;
+}
+
+/**
+ * tomoyo_write_domain_profile - Assign profile for specified domain.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0 on success, -EINVAL otherwise.
+ *
+ * This is equivalent to doing
+ *
+ * ( echo "select " $domainname; echo "use_profile " $profile ) |
+ * /usr/sbin/tomoyo-loadpolicy -d
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static int tomoyo_write_domain_profile(struct tomoyo_io_buffer *head)
+{
+ char *data = head->write_buf;
+ char *cp = strchr(data, ' ');
+ struct tomoyo_domain_info *domain;
+ unsigned int profile;
+ if (!cp)
+ return -EINVAL;
+ *cp = '\0';
+ profile = simple_strtoul(data, NULL, 10);
+ if (profile >= TOMOYO_MAX_PROFILES)
+ return -EINVAL;
+ domain = tomoyo_find_domain(cp + 1);
+ if (domain && (!tomoyo_policy_loaded ||
+ tomoyo_profile_ptr[(u8) profile]))
+ domain->profile = (u8) profile;
+ return 0;
+}
+
+/**
+ * tomoyo_read_domain_profile - Read only domainname and profile.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * This is equivalent to doing
+ *
+ * grep -A 1 '^<kernel>' /sys/kernel/security/domain_policy |
+ * awk ' { if ( domainname == "" ) { if ( $1 == "<kernel>" )
+ * domainname = $0; } else if ( $1 == "use_profile" ) {
+ * print $2 " " domainname; domainname = ""; } } ; '
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static void tomoyo_read_domain_profile(struct tomoyo_io_buffer *head)
+{
+ struct list_head *pos;
+ if (head->read_eof)
+ return;
+ list_for_each_cookie(pos, head->read_var1, &tomoyo_domain_list) {
+ struct tomoyo_domain_info *domain;
+ domain = list_entry(pos, struct tomoyo_domain_info, list);
+ if (domain->is_deleted)
+ continue;
+ if (!tomoyo_io_printf(head, "%u %s\n", domain->profile,
+ domain->domainname->name))
+ return;
+ }
+ head->read_eof = true;
+}
+
+/**
+ * tomoyo_write_pid: Specify PID to obtain domainname.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0.
+ */
+static int tomoyo_write_pid(struct tomoyo_io_buffer *head)
+{
+ head->read_eof = false;
+ return 0;
+}
+
+/**
+ * tomoyo_read_pid - Read information of a process.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns the domainname which the specified PID is in or
+ * process information of the specified PID on success,
+ * empty string otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static void tomoyo_read_pid(struct tomoyo_io_buffer *head)
+{
+ char *buf = head->write_buf;
+ bool task_info = false;
+ bool global_pid = false;
+ unsigned int pid;
+ struct task_struct *p;
+ struct tomoyo_domain_info *domain = NULL;
+ u32 tomoyo_flags = 0;
+ /* Accessing write_buf is safe because head->io_sem is held. */
+ if (!buf)
+ return; /* Do nothing if open(O_RDONLY). */
+ if (head->read_avail || head->read_eof)
+ return;
+ head->read_eof = true;
+ if (tomoyo_str_starts(&buf, "info "))
+ task_info = true;
+ if (tomoyo_str_starts(&buf, "global-pid "))
+ global_pid = true;
+ pid = (unsigned int) simple_strtoul(buf, NULL, 10);
+ read_lock(&tasklist_lock);
+ if (global_pid)
+ p = find_task_by_pid_ns(pid, &init_pid_ns);
+ else
+ p = find_task_by_vpid(pid);
+ if (p) {
+ domain = tomoyo_task_domain(p);
+ tomoyo_flags = p->tomoyo_flags;
+ }
+ read_unlock(&tasklist_lock);
+ if (!domain)
+ return;
+ if (!task_info)
+ tomoyo_io_printf(head, "%u %u %s", pid, domain->profile,
+ domain->domainname->name);
+ else
+ tomoyo_io_printf(head, "%u manager=%s execute_handler=%s "
+ "state[0]=%u state[1]=%u state[2]=%u", pid,
+ tomoyo_yesno(tomoyo_flags &
+ TOMOYO_TASK_IS_POLICY_MANAGER),
+ tomoyo_yesno(tomoyo_flags &
+ TOMOYO_TASK_IS_EXECUTE_HANDLER),
+ (u8) (tomoyo_flags >> 24),
+ (u8) (tomoyo_flags >> 16),
+ (u8) (tomoyo_flags >> 8));
+}
+
+/**
+ * tomoyo_write_exception_policy - Write exception policy.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_write_exception_policy(struct tomoyo_io_buffer *head)
+{
+ char *data = head->write_buf;
+ bool is_delete = tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE);
+ if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_KEEP_DOMAIN))
+ return tomoyo_write_domain_keeper_policy(data, false,
+ is_delete);
+ if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_NO_KEEP_DOMAIN))
+ return tomoyo_write_domain_keeper_policy(data, true,
+ is_delete);
+ if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_INITIALIZE_DOMAIN))
+ return tomoyo_write_domain_initializer_policy(data, false,
+ is_delete);
+ if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN))
+ return tomoyo_write_domain_initializer_policy(data, true,
+ is_delete);
+ if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_AGGREGATOR))
+ return tomoyo_write_aggregator_policy(data, is_delete);
+ if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_ALLOW_READ))
+ return tomoyo_write_globally_readable_policy(data, is_delete);
+ if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_ALLOW_ENV))
+ return tomoyo_write_globally_usable_env_policy(data,
+ is_delete);
+ if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_FILE_PATTERN))
+ return tomoyo_write_pattern_policy(data, is_delete);
+ if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_PATH_GROUP))
+ return tomoyo_write_path_group_policy(data, is_delete);
+ if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_NUMBER_GROUP))
+ return tomoyo_write_number_group_policy(data, is_delete);
+ if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_DENY_REWRITE))
+ return tomoyo_write_no_rewrite_policy(data, is_delete);
+ if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_ADDRESS_GROUP))
+ return tomoyo_write_address_group_policy(data, is_delete);
+ return -EINVAL;
+}
+
+/**
+ * tomoyo_read_exception_policy - Read exception policy.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static void tomoyo_read_exception_policy(struct tomoyo_io_buffer *head)
+{
+ if (head->read_eof)
+ return;
+ switch (head->read_step) {
+ case 0:
+ head->read_var2 = NULL;
+ head->read_step = 1;
+ case 1:
+ if (!tomoyo_read_domain_keeper_policy(head))
+ break;
+ head->read_var2 = NULL;
+ head->read_step = 2;
+ case 2:
+ if (!tomoyo_read_globally_readable_policy(head))
+ break;
+ head->read_var2 = NULL;
+ head->read_step = 3;
+ case 3:
+ if (!tomoyo_read_globally_usable_env_policy(head))
+ break;
+ head->read_var2 = NULL;
+ head->read_step = 4;
+ case 4:
+ if (!tomoyo_read_domain_initializer_policy(head))
+ break;
+ head->read_var2 = NULL;
+ head->read_step = 6;
+ case 6:
+ if (!tomoyo_read_aggregator_policy(head))
+ break;
+ head->read_var2 = NULL;
+ head->read_step = 7;
+ case 7:
+ if (!tomoyo_read_file_pattern(head))
+ break;
+ head->read_var2 = NULL;
+ head->read_step = 8;
+ case 8:
+ if (!tomoyo_read_no_rewrite_policy(head))
+ break;
+ head->read_var2 = NULL;
+ head->read_step = 9;
+ case 9:
+ if (!tomoyo_read_path_group_policy(head))
+ break;
+ head->read_var1 = NULL;
+ head->read_var2 = NULL;
+ head->read_step = 10;
+ case 10:
+ if (!tomoyo_read_number_group_policy(head))
+ break;
+ head->read_var1 = NULL;
+ head->read_var2 = NULL;
+ head->read_step = 11;
+ case 11:
+ if (!tomoyo_read_address_group_policy(head))
+ break;
+ head->read_var2 = NULL;
+ head->read_eof = true;
+ }
+}
+
+/**
+ * tomoyo_get_argv0 - Get argv[0].
+ *
+ * @ee: Pointer to "struct tomoyo_execve_entry".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_get_argv0(struct tomoyo_execve_entry *ee)
+{
+ struct linux_binprm *bprm = ee->bprm;
+ char *arg_ptr = ee->tmp;
+ int arg_len = 0;
+ unsigned long pos = bprm->p;
+ int offset = pos % PAGE_SIZE;
+ bool done = false;
+ if (!bprm->argc)
+ goto out;
+ while (1) {
+ if (!tomoyo_dump_page(bprm, pos, &ee->dump))
+ goto out;
+ pos += PAGE_SIZE - offset;
+ /* Read. */
+ while (offset < PAGE_SIZE) {
+ const char *kaddr = ee->dump.data;
+ const unsigned char c = kaddr[offset++];
+ if (c && arg_len < TOMOYO_EXEC_TMPSIZE - 10) {
+ if (c == '\\') {
+ arg_ptr[arg_len++] = '\\';
+ arg_ptr[arg_len++] = '\\';
+ } else if (c > ' ' && c < 127) {
+ arg_ptr[arg_len++] = c;
+ } else {
+ arg_ptr[arg_len++] = '\\';
+ arg_ptr[arg_len++] = (c >> 6) + '0';
+ arg_ptr[arg_len++]
+ = ((c >> 3) & 7) + '0';
+ arg_ptr[arg_len++] = (c & 7) + '0';
+ }
+ } else {
+ arg_ptr[arg_len] = '\0';
+ done = true;
+ break;
+ }
+ }
+ offset = 0;
+ if (done)
+ break;
+ }
+ return true;
+ out:
+ return false;
+}
+
+/**
+ * tomoyo_get_execute_condition - Get condition part for execute requests.
+ *
+ * @ee: Pointer to "struct tomoyo_execve_entry".
+ *
+ * Returns pointer to "struct tomoyo_condition" on success, NULL otherwise.
+ */
+static struct tomoyo_condition *tomoyo_get_execute_condition
+(struct tomoyo_execve_entry *ee)
+{
+ struct tomoyo_condition *cond;
+ char *buf;
+ int len = 256;
+ char *realpath = NULL;
+ char *argv0 = NULL;
+ const struct tomoyo_profile *profile
+ = tomoyo_profile(ee->r.domain->profile);
+ if (profile->learning->learning_exec_realpath) {
+ struct file *file = ee->bprm->file;
+ realpath = tomoyo_realpath_from_path(&file->f_path);
+ if (realpath)
+ len += strlen(realpath) + 17;
+ }
+ if (profile->learning->learning_exec_argv0) {
+ if (tomoyo_get_argv0(ee)) {
+ argv0 = ee->tmp;
+ len += strlen(argv0) + 16;
+ }
+ }
+ buf = kmalloc(len, GFP_KERNEL);
+ if (!buf) {
+ kfree(realpath);
+ return NULL;
+ }
+ snprintf(buf, len - 1, "if");
+ if (current->tomoyo_flags & TOMOYO_TASK_IS_EXECUTE_HANDLER) {
+ const int pos = strlen(buf);
+ snprintf(buf + pos, len - pos - 1,
+ " task.type=execute_handler");
+ }
+ if (realpath) {
+ const int pos = strlen(buf);
+ snprintf(buf + pos, len - pos - 1, " exec.realpath=\"%s\"",
+ realpath);
+ kfree(realpath);
+ }
+ if (argv0) {
+ const int pos = strlen(buf);
+ snprintf(buf + pos, len - pos - 1, " exec.argv[0]=\"%s\"",
+ argv0);
+ }
+ cond = tomoyo_get_condition(buf);
+ kfree(buf);
+ return cond;
+}
+
+/**
+ * tomoyo_get_symlink_condition - Get condition part for symlink requests.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ *
+ * Returns pointer to "struct tomoyo_condition" on success, NULL otherwise.
+ */
+static struct tomoyo_condition *tomoyo_get_symlink_condition
+(struct tomoyo_request_info *r)
+{
+ struct tomoyo_condition *cond;
+ char *buf;
+ int len = 256;
+ const char *symlink = NULL;
+ const struct tomoyo_profile *profile = tomoyo_profile(r->profile);
+ if (profile->learning->learning_symlink_target) {
+ symlink = r->obj->symlink_target->name;
+ len += strlen(symlink) + 18;
+ }
+ buf = kmalloc(len, GFP_KERNEL);
+ if (!buf)
+ return NULL;
+ snprintf(buf, len - 1, "if");
+ if (current->tomoyo_flags & TOMOYO_TASK_IS_EXECUTE_HANDLER) {
+ const int pos = strlen(buf);
+ snprintf(buf + pos, len - pos - 1,
+ " task.type=execute_handler");
+ }
+ if (symlink) {
+ const int pos = strlen(buf);
+ snprintf(buf + pos, len - pos - 1, " symlink.target=\"%s\"",
+ symlink);
+ }
+ cond = tomoyo_get_condition(buf);
+ kfree(buf);
+ return cond;
+}
+
+/* Wait queue for tomoyo_query_list. */
+static DECLARE_WAIT_QUEUE_HEAD(tomoyo_query_wait);
+
+/* Lock for manipulating tomoyo_query_list. */
+static DEFINE_SPINLOCK(tomoyo_query_list_lock);
+
+/* Structure for query. */
+struct tomoyo_query_entry {
+ struct list_head list;
+ char *query;
+ int query_len;
+ unsigned int serial;
+ int timer;
+ int answer;
+};
+
+/* The list for "struct tomoyo_query_entry". */
+static LIST_HEAD(tomoyo_query_list);
+
+/* Number of "struct file" referring /sys/kernel/security/query interface. */
+static atomic_t tomoyo_query_observers = ATOMIC_INIT(0);
+
+/**
+ * tomoyo_supervisor - Ask for the supervisor's decision.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ * @fmt: The printf()'s format string, followed by parameters.
+ *
+ * Returns 0 if the supervisor decided to permit the access request which
+ * violated the policy in enforcing mode, 1 if the supervisor decided to
+ * retry the access request which violated the policy in enforcing mode,
+ * 0 if it is not in enforcing mode, -EPERM otherwise.
+ */
+int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...)
+{
+ va_list args;
+ int error = -EPERM;
+ int pos;
+ int len;
+ static unsigned int tomoyo_serial;
+ struct tomoyo_query_entry *tomoyo_query_entry = NULL;
+ bool quota_exceeded = false;
+ char *header;
+ if (!r->domain)
+ r->domain = tomoyo_current_domain();
+ switch (r->mode) {
+ char *buffer;
+ struct tomoyo_condition *cond;
+ case TOMOYO_CONFIG_LEARNING:
+ if (!tomoyo_domain_quota_ok(r))
+ return 0;
+ va_start(args, fmt);
+ len = vsnprintf((char *) &pos, sizeof(pos) - 1, fmt, args) + 4;
+ va_end(args);
+ buffer = kmalloc(len, GFP_KERNEL);
+ if (!buffer)
+ return 0;
+ va_start(args, fmt);
+ vsnprintf(buffer, len - 1, fmt, args);
+ va_end(args);
+ tomoyo_normalize_line(buffer);
+ if (r->ee && !strncmp(buffer, "allow_execute ", 14))
+ cond = tomoyo_get_execute_condition(r->ee);
+ else if (r->obj && r->obj->symlink_target)
+ cond = tomoyo_get_symlink_condition(r);
+ else if ((current->tomoyo_flags &
+ TOMOYO_TASK_IS_EXECUTE_HANDLER)) {
+ char str[] = "if task.type=execute_handler";
+ cond = tomoyo_get_condition(str);
+ } else
+ cond = NULL;
+ tomoyo_write_domain_policy2(buffer, r->domain, cond, false);
+ tomoyo_put_condition(cond);
+ kfree(buffer);
+ /* fall through */
+ case TOMOYO_CONFIG_PERMISSIVE:
+ return 0;
+ }
+ if (!atomic_read(&tomoyo_query_observers)) {
+ int i;
+ if (current->tomoyo_flags & TOMOYO_DONT_SLEEP_ON_ENFORCE_ERROR)
+ return -EPERM;
+ for (i = 0; i < tomoyo_profile(r->domain->profile)->enforcing->
+ enforcing_penalty; i++) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ / 10);
+ }
+ return -EPERM;
+ }
+ va_start(args, fmt);
+ len = vsnprintf((char *) &pos, sizeof(pos) - 1, fmt, args) + 32;
+ va_end(args);
+ header = tomoyo_init_audit_log(&len, r);
+ if (!header)
+ goto out;
+ tomoyo_query_entry = kzalloc(sizeof(*tomoyo_query_entry), GFP_KERNEL);
+ if (!tomoyo_query_entry)
+ goto out;
+ len = tomoyo_round2(len);
+ tomoyo_query_entry->query = kzalloc(len, GFP_KERNEL);
+ if (!tomoyo_query_entry->query)
+ goto out;
+ INIT_LIST_HEAD(&tomoyo_query_entry->list);
+ spin_lock(&tomoyo_query_list_lock);
+ if (tomoyo_quota_for_query && tomoyo_query_memory_size + len +
+ sizeof(*tomoyo_query_entry) >= tomoyo_quota_for_query) {
+ quota_exceeded = true;
+ } else {
+ tomoyo_query_memory_size += len + sizeof(*tomoyo_query_entry);
+ tomoyo_query_entry->serial = tomoyo_serial++;
+ }
+ spin_unlock(&tomoyo_query_list_lock);
+ if (quota_exceeded)
+ goto out;
+ pos = snprintf(tomoyo_query_entry->query, len - 1, "Q%u-%hu\n%s",
+ tomoyo_query_entry->serial, r->retry, header);
+ kfree(header);
+ header = NULL;
+ va_start(args, fmt);
+ vsnprintf(tomoyo_query_entry->query + pos, len - 1 - pos, fmt, args);
+ tomoyo_query_entry->query_len = strlen(tomoyo_query_entry->query) + 1;
+ va_end(args);
+ spin_lock(&tomoyo_query_list_lock);
+ list_add_tail(&tomoyo_query_entry->list, &tomoyo_query_list);
+ spin_unlock(&tomoyo_query_list_lock);
+ /* Give 10 seconds for supervisor's opinion. */
+ for (tomoyo_query_entry->timer = 0;
+ atomic_read(&tomoyo_query_observers) &&
+ tomoyo_query_entry->timer < 100;
+ tomoyo_query_entry->timer++) {
+ wake_up(&tomoyo_query_wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ / 10);
+ if (tomoyo_query_entry->answer)
+ break;
+ }
+ spin_lock(&tomoyo_query_list_lock);
+ list_del(&tomoyo_query_entry->list);
+ tomoyo_query_memory_size -= len + sizeof(*tomoyo_query_entry);
+ spin_unlock(&tomoyo_query_list_lock);
+ switch (tomoyo_query_entry->answer) {
+ case 3: /* Asked to retry by administrator. */
+ error = 1;
+ r->retry++;
+ break;
+ case 1:
+ /* Granted by administrator. */
+ error = 0;
+ break;
+ case 0:
+ /* Timed out. */
+ break;
+ default:
+ /* Rejected by administrator. */
+ break;
+ }
+ out:
+ if (tomoyo_query_entry)
+ kfree(tomoyo_query_entry->query);
+ kfree(tomoyo_query_entry);
+ kfree(header);
+ return error;
+}
+
+/**
+ * tomoyo_poll_query - poll() for /sys/kernel/security/query.
+ *
+ * @file: Pointer to "struct file".
+ * @wait: Pointer to "poll_table".
+ *
+ * Returns POLLIN | POLLRDNORM when ready to read, 0 otherwise.
+ *
+ * Waits for access requests which violated policy in enforcing mode.
+ */
+static int tomoyo_poll_query(struct file *file, poll_table *wait)
+{
+ struct list_head *tmp;
+ bool found = false;
+ u8 i;
+ for (i = 0; i < 2; i++) {
+ spin_lock(&tomoyo_query_list_lock);
+ list_for_each(tmp, &tomoyo_query_list) {
+ struct tomoyo_query_entry *ptr
+ = list_entry(tmp, struct tomoyo_query_entry,
+ list);
+ if (ptr->answer)
+ continue;
+ found = true;
+ break;
+ }
+ spin_unlock(&tomoyo_query_list_lock);
+ if (found)
+ return POLLIN | POLLRDNORM;
+ if (i)
+ break;
+ poll_wait(file, &tomoyo_query_wait, wait);
+ }
+ return 0;
+}
+
+/**
+ * tomoyo_read_query - Read access requests which violated policy in enforcing mode.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ */
+static void tomoyo_read_query(struct tomoyo_io_buffer *head)
+{
+ struct list_head *tmp;
+ int pos = 0;
+ int len = 0;
+ char *buf;
+ if (head->read_avail)
+ return;
+ if (head->read_buf) {
+ kfree(head->read_buf);
+ head->read_buf = NULL;
+ head->readbuf_size = 0;
+ }
+ spin_lock(&tomoyo_query_list_lock);
+ list_for_each(tmp, &tomoyo_query_list) {
+ struct tomoyo_query_entry *ptr
+ = list_entry(tmp, struct tomoyo_query_entry, list);
+ if (ptr->answer)
+ continue;
+ if (pos++ != head->read_step)
+ continue;
+ len = ptr->query_len;
+ break;
+ }
+ spin_unlock(&tomoyo_query_list_lock);
+ if (!len) {
+ head->read_step = 0;
+ return;
+ }
+ buf = kzalloc(len, GFP_KERNEL);
+ if (!buf)
+ return;
+ pos = 0;
+ spin_lock(&tomoyo_query_list_lock);
+ list_for_each(tmp, &tomoyo_query_list) {
+ struct tomoyo_query_entry *ptr
+ = list_entry(tmp, struct tomoyo_query_entry, list);
+ if (ptr->answer)
+ continue;
+ if (pos++ != head->read_step)
+ continue;
+ /*
+ * Some query can be skipped because tomoyo_query_list
+ * can change, but I don't care.
+ */
+ if (len == ptr->query_len)
+ memmove(buf, ptr->query, len);
+ break;
+ }
+ spin_unlock(&tomoyo_query_list_lock);
+ if (buf[0]) {
+ head->read_avail = len;
+ head->readbuf_size = head->read_avail;
+ head->read_buf = buf;
+ head->read_step++;
+ } else {
+ kfree(buf);
+ }
+}
+
+/**
+ * tomoyo_write_answer - Write the supervisor's decision.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0 on success, -EINVAL otherwise.
+ */
+static int tomoyo_write_answer(struct tomoyo_io_buffer *head)
+{
+ char *data = head->write_buf;
+ struct list_head *tmp;
+ unsigned int serial;
+ unsigned int answer;
+ spin_lock(&tomoyo_query_list_lock);
+ list_for_each(tmp, &tomoyo_query_list) {
+ struct tomoyo_query_entry *ptr
+ = list_entry(tmp, struct tomoyo_query_entry, list);
+ ptr->timer = 0;
+ }
+ spin_unlock(&tomoyo_query_list_lock);
+ if (sscanf(data, "A%u=%u", &serial, &answer) != 2)
+ return -EINVAL;
+ spin_lock(&tomoyo_query_list_lock);
+ list_for_each(tmp, &tomoyo_query_list) {
+ struct tomoyo_query_entry *ptr
+ = list_entry(tmp, struct tomoyo_query_entry, list);
+ if (ptr->serial != serial)
+ continue;
+ if (!ptr->answer)
+ ptr->answer = answer;
+ break;
+ }
+ spin_unlock(&tomoyo_query_list_lock);
+ return 0;
+}
+
+/**
+ * tomoyo_read_version: Get version.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ */
+static void tomoyo_read_version(struct tomoyo_io_buffer *head)
+{
+ if (head->read_eof)
+ return;
+ tomoyo_io_printf(head, "2.3.0-pre");
+ head->read_eof = true;
+}
+
+/**
+ * tomoyo_read_self_domain - Get the current process's domainname.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ */
+static void tomoyo_read_self_domain(struct tomoyo_io_buffer *head)
+{
+ if (head->read_eof)
+ return;
+ /*
+ * tomoyo_current_domain()->domainname != NULL because every process
+ * belongs to a domain and the domain's name cannot be NULL.
+ */
+ tomoyo_io_printf(head, "%s",
+ tomoyo_current_domain()->domainname->name);
+ head->read_eof = true;
+}
+
+/**
+ * tomoyo_open_control - open() for /sys/kernel/security/ interface.
+ *
+ * @type: Type of interface.
+ * @file: Pointer to "struct file".
+ *
+ * Associates policy handler and returns 0 on success, -ENOMEM otherwise.
+ */
+int tomoyo_open_control(const u8 type, struct file *file)
+{
+ struct tomoyo_io_buffer *head = kzalloc(sizeof(*head), GFP_KERNEL);
+ if (!head)
+ return -ENOMEM;
+ mutex_init(&head->io_sem);
+ head->type = type;
+ switch (type) {
+ case TOMOYO_DOMAINPOLICY:
+ /* /sys/kernel/security/tomoyo/domain_policy */
+ head->write = tomoyo_write_domain_policy;
+ head->read = tomoyo_read_domain_policy;
+ break;
+ case TOMOYO_EXCEPTIONPOLICY:
+ /* /sys/kernel/security/tomoyo/exception_policy */
+ head->write = tomoyo_write_exception_policy;
+ head->read = tomoyo_read_exception_policy;
+ break;
+#ifdef CONFIG_SECURITY_TOMOYO_AUDIT
+ case TOMOYO_GRANTLOG: /* /sys/kernel/security/tomoyo/grant_log */
+ head->poll = tomoyo_poll_grant_log;
+ head->read = tomoyo_read_grant_log;
+ break;
+ case TOMOYO_REJECTLOG: /* /sys/kernel/security/tomoyo/reject_log */
+ head->poll = tomoyo_poll_reject_log;
+ head->read = tomoyo_read_reject_log;
+ break;
+#endif
+ case TOMOYO_SELFDOMAIN: /* /sys/kernel/security/tomoyo/self_domain */
+ head->read = tomoyo_read_self_domain;
+ break;
+ case TOMOYO_DOMAIN_STATUS:
+ /* /sys/kernel/security/tomoyo/.domain_status */
+ head->write = tomoyo_write_domain_profile;
+ head->read = tomoyo_read_domain_profile;
+ break;
+ case TOMOYO_EXECUTE_HANDLER:
+ /* /sys/kernel/security/tomoyo/.execute_handler */
+ /* Allow execute_handler to read process's status. */
+ if (!(current->tomoyo_flags &
+ TOMOYO_TASK_IS_EXECUTE_HANDLER)) {
+ kfree(head);
+ return -EPERM;
+ }
+ /* fall through */
+ case TOMOYO_PROCESS_STATUS:
+ /* /sys/kernel/security/tomoyo/.process_status */
+ head->write = tomoyo_write_pid;
+ head->read = tomoyo_read_pid;
+ break;
+ case TOMOYO_VERSION: /* /sys/kernel/security/tomoyo/version */
+ head->read = tomoyo_read_version;
+ head->readbuf_size = 128;
+ break;
+ case TOMOYO_MEMINFO: /* /sys/kernel/security/tomoyo/meminfo */
+ head->write = tomoyo_write_memory_quota;
+ head->read = tomoyo_read_memory_counter;
+ head->readbuf_size = 512;
+ break;
+ case TOMOYO_PROFILE: /* /sys/kernel/security/tomoyo/profile */
+ head->write = tomoyo_write_profile;
+ head->read = tomoyo_read_profile;
+ break;
+ case TOMOYO_QUERY: /* /sys/kernel/security/tomoyo/query */
+ head->poll = tomoyo_poll_query;
+ head->write = tomoyo_write_answer;
+ head->read = tomoyo_read_query;
+ break;
+ case TOMOYO_MANAGER: /* /sys/kernel/security/tomoyo/manager */
+ head->write = tomoyo_write_manager_policy;
+ head->read = tomoyo_read_manager_policy;
+ break;
+ }
+ if (!(file->f_mode & FMODE_READ)) {
+ /*
+ * No need to allocate read_buf since it is not opened
+ * for reading.
+ */
+ head->read = NULL;
+ head->poll = NULL;
+ } else if (!head->poll) {
+ /* Don't allocate read_buf for poll() access. */
+ if (!head->readbuf_size)
+ head->readbuf_size = 4096;
+ head->read_buf = kzalloc(head->readbuf_size, GFP_KERNEL);
+ if (!head->read_buf) {
+ kfree(head);
+ return -ENOMEM;
+ }
+ }
+ if (!(file->f_mode & FMODE_WRITE)) {
+ /*
+ * No need to allocate write_buf since it is not opened
+ * for writing.
+ */
+ head->write = NULL;
+ } else if (head->write) {
+ head->writebuf_size = 4096;
+ head->write_buf = kzalloc(head->writebuf_size, GFP_KERNEL);
+ if (!head->write_buf) {
+ kfree(head->read_buf);
+ kfree(head);
+ return -ENOMEM;
+ }
+ }
+ /* This lock is released at tomoyo_close_control(). */
+ if (type != TOMOYO_QUERY &&
+ type != TOMOYO_GRANTLOG && type != TOMOYO_REJECTLOG)
+ head->reader_idx = tomoyo_read_lock();
+ file->private_data = head;
+ /*
+ * Call the handler now if the file is
+ * /sys/kernel/security/tomoyo/self_domain so that the user can use
+ * "cat < /sys/kernel/security/tomoyo/self_domain" to
+ * know the current process's domainname.
+ */
+ if (type == TOMOYO_SELFDOMAIN)
+ tomoyo_read_control(file, NULL, 0);
+ /*
+ * If the file is /sys/kernel/security/tomoyo/query ,
+ * increment the observer counter.
+ * The obserber counter is used by tomoyo_supervisor() to see if
+ * there is some process monitoring /sys/kernel/security/tomoyo/query.
+ */
+ else if (type == TOMOYO_QUERY)
+ atomic_inc(&tomoyo_query_observers);
+ return 0;
+}
+
+/**
+ * tomoyo_poll_control - poll() for /sys/kernel/security/tomoyo/ interface.
+ *
+ * @file: Pointer to "struct file".
+ * @wait: Pointer to "poll_table".
+ *
+ * Waits for read readiness.
+ * /sys/kernel/security/tomoyo/query is handled by /usr/sbin/tomoyo-queryd and
+ * /sys/kernel/security/tomoyo/grant_log and
+ * /sys/kernel/security/tomoyo/reject_log are handled by
+ * /usr/sbin/tomoyo-auditd .
+ */
+int tomoyo_poll_control(struct file *file, poll_table *wait)
+{
+ struct tomoyo_io_buffer *head = file->private_data;
+ if (!head->poll)
+ return -ENOSYS;
+ return head->poll(file, wait);
+}
+
+/**
+ * tomoyo_read_control - read() for /sys/kernel/security/tomoyo/ interface.
+ *
+ * @file: Pointer to "struct file".
+ * @buffer: Poiner to buffer to write to.
+ * @buffer_len: Size of @buffer.
+ *
+ * Returns bytes read on success, negative value otherwise.
+ */
+int tomoyo_read_control(struct file *file, char __user *buffer,
+ const int buffer_len)
+{
+ int len = 0;
+ struct tomoyo_io_buffer *head = file->private_data;
+ char *cp;
+ if (!head->read)
+ return -ENOSYS;
+ if (!access_ok(VERIFY_WRITE, buffer, buffer_len))
+ return -EFAULT;
+ if (mutex_lock_interruptible(&head->io_sem))
+ return -EINTR;
+ while (1) {
+ /* Call the policy handler. */
+ head->read(head);
+ /* Write to buffer. */
+ len = head->read_avail;
+ if (len || head->poll || head->read_eof)
+ break;
+ len = head->readbuf_size * 2;
+ cp = kzalloc(len, GFP_KERNEL);
+ if (!cp) {
+ len = -ENOMEM;
+ goto out;
+ }
+ kfree(head->read_buf);
+ head->read_buf = cp;
+ head->readbuf_size = len;
+ }
+ if (len > buffer_len)
+ len = buffer_len;
+ if (!len)
+ goto out;
+ /* head->read_buf changes by some functions. */
+ cp = head->read_buf;
+ if (copy_to_user(buffer, cp, len)) {
+ len = -EFAULT;
+ goto out;
+ }
+ head->read_avail -= len;
+ memmove(cp, cp + len, head->read_avail);
+ out:
+ mutex_unlock(&head->io_sem);
+ return len;
+}
+
+/**
+ * tomoyo_write_control - write() for /sys/kernel/security/tomoyo/ interface.
+ *
+ * @file: Pointer to "struct file".
+ * @buffer: Pointer to buffer to read from.
+ * @buffer_len: Size of @buffer.
+ *
+ * Returns @buffer_len on success, negative value otherwise.
+ */
+int tomoyo_write_control(struct file *file, const char __user *buffer,
+ const int buffer_len)
+{
+ struct tomoyo_io_buffer *head = file->private_data;
+ int error = buffer_len;
+ int avail_len = buffer_len;
+ char *cp0 = head->write_buf;
+ if (!head->write)
+ return -ENOSYS;
+ if (!access_ok(VERIFY_READ, buffer, buffer_len))
+ return -EFAULT;
+ /* Don't allow updating policies by non manager programs. */
+ if (head->write != tomoyo_write_pid &&
+ head->write != tomoyo_write_domain_policy &&
+ !tomoyo_is_policy_manager())
+ return -EPERM;
+ if (mutex_lock_interruptible(&head->io_sem))
+ return -EINTR;
+ /* Read a line and dispatch it to the policy handler. */
+ while (avail_len > 0) {
+ char c;
+ if (head->write_avail >= head->writebuf_size - 1) {
+ const int len = head->writebuf_size * 2;
+ char *cp = kzalloc(len, GFP_KERNEL);
+ if (!cp) {
+ error = -ENOMEM;
+ break;
+ }
+ memmove(cp, cp0, head->write_avail);
+ kfree(cp0);
+ head->write_buf = cp;
+ cp0 = cp;
+ head->writebuf_size = len;
+ }
+ if (get_user(c, buffer)) {
+ error = -EFAULT;
+ break;
+ }
+ buffer++;
+ avail_len--;
+ cp0[head->write_avail++] = c;
+ if (c != '\n')
+ continue;
+ cp0[head->write_avail - 1] = '\0';
+ head->write_avail = 0;
+ tomoyo_normalize_line(cp0);
+ head->write(head);
+ }
+ mutex_unlock(&head->io_sem);
+ return error;
+}
+
+/**
+ * tomoyo_close_control - close() for /sys/kernel/security/tomoyo/ interface.
+ *
+ * @file: Pointer to "struct file".
+ *
+ * Releases memory and returns 0.
+ */
+int tomoyo_close_control(struct file *file)
+{
+ struct tomoyo_io_buffer *head = file->private_data;
+ const bool is_write = head->write_buf != NULL;
+ const u8 type = head->type;
+ /*
+ * If the file is /sys/kernel/security/tomoyo/query ,
+ * decrement the observer counter.
+ */
+ if (type == TOMOYO_QUERY)
+ atomic_dec(&tomoyo_query_observers);
+ /* This lock is acquired at tomoyo_open_control(). */
+ if (type != TOMOYO_QUERY &&
+ type != TOMOYO_GRANTLOG && type != TOMOYO_REJECTLOG)
+ tomoyo_read_unlock(head->reader_idx);
+ /* Release memory used for policy I/O. */
+ kfree(head->read_buf);
+ head->read_buf = NULL;
+ kfree(head->write_buf);
+ head->write_buf = NULL;
+ kfree(head);
+ head = NULL;
+ file->private_data = NULL;
+ if (is_write)
+ tomoyo_run_gc();
+ return 0;
+}
--
^ permalink raw reply [flat|nested] 50+ messages in thread
* [TOMOYO #16 20/25] TOMOYO: Add policy loader launcher.
2009-10-04 12:49 [TOMOYO #16 00/25] Starting TOMOYO 2.3 Tetsuo Handa
` (18 preceding siblings ...)
2009-10-04 12:50 ` [TOMOYO #16 19/25] TOMOYO: Add policy I/O handler Tetsuo Handa
@ 2009-10-04 12:50 ` Tetsuo Handa
2009-10-04 12:50 ` [TOMOYO #16 21/25] TOMOYO: Add securityfs interface Tetsuo Handa
` (5 subsequent siblings)
25 siblings, 0 replies; 50+ messages in thread
From: Tetsuo Handa @ 2009-10-04 12:50 UTC (permalink / raw)
To: linux-security-module; +Cc: linux-kernel, Tetsuo Handa
[-- Attachment #1: tomoyo-add-load_policy.patch --]
[-- Type: text/plain, Size: 3432 bytes --]
This patch contains code for policy loader.
By default, TOMOYO checks for /sbin/tomoyo-init and run it when /sbin/init (or
/sbin/tomoyo-start) is requested. But it is configurable via kernel commandline
parameter. /sbin/tomoyo-start is a dummy name for environments where /sbin/init
is missing (e.g. Android).
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
security/tomoyo/load_policy.c | 97 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 97 insertions(+)
--- /dev/null
+++ security-testing-2.6/security/tomoyo/load_policy.c
@@ -0,0 +1,97 @@
+/*
+ * security/tomoyo/load_policy.c
+ *
+ * Copyright (C) 2005-2009 NTT DATA CORPORATION
+ */
+#include "internal.h"
+#include <linux/module.h>
+#include <linux/namei.h>
+#include <linux/mount.h>
+
+/* Path to the policy loader. The default is /sbin/tomoyo-init. */
+static const char *tomoyo_loader;
+
+/**
+ * tomoyo_loader_setup - Specify the policy loader to use.
+ *
+ * @str: Path to the policy loader.
+ *
+ * Returns 0.
+ */
+static int __init tomoyo_loader_setup(char *str)
+{
+ tomoyo_loader = str;
+ return 0;
+}
+__setup("TOMOYO_loader=", tomoyo_loader_setup);
+
+/**
+ * tomoyo_policy_loader_exists - Check whether /sbin/tomoyo-init exists.
+ *
+ * Returns true if /sbin/tomoyo-init exists, false otherwise.
+ */
+static bool tomoyo_policy_loader_exists(void)
+{
+ /*
+ * Don't activate MAC if the path given by 'TOMOYO_loader=' option
+ * doesn't exist. If the initrd includes /sbin/init but real-root-dev
+ * has not mounted on / yet, activating MAC will block the system since
+ * policies are not loaded yet.
+ * Thus, let do_execve() call this function everytime.
+ */
+ struct path path;
+ if (!tomoyo_loader)
+ tomoyo_loader = "/sbin/tomoyo-init";
+ if (kern_path(tomoyo_loader, LOOKUP_FOLLOW, &path)) {
+ printk(KERN_INFO "Not activating Mandatory Access Control now "
+ "since %s doesn't exist.\n", tomoyo_loader);
+ return false;
+ }
+ path_put(&path);
+ return true;
+}
+
+/**
+ * tomoyo_load_policy - Run external policy loader to load policy.
+ *
+ * @filename: The program about to start.
+ *
+ * This function checks whether @filename is /sbin/init , and if so
+ * invoke /sbin/tomoyo-init and wait for the termination of /sbin/tomoyo-init
+ * and then continues invocation of /sbin/init.
+ * /sbin/tomoyo-init reads policy files in /etc/tomoyo/ directory and
+ * writes to /sys/kernel/security/tomoyo/ interfaces.
+ *
+ * Returns nothing.
+ */
+void tomoyo_load_policy(const char *filename)
+{
+ if (tomoyo_policy_loaded)
+ return;
+ /*
+ * Check filename is /sbin/init or /sbin/tomoyo-start.
+ * /sbin/tomoyo-start is a dummy filename in case where /sbin/init
+ * can't be passed. You can create /sbin/tomoyo-start by
+ * "ln -s /bin/true /sbin/tomoyo-start".
+ */
+ if (strcmp(filename, "/sbin/init") &&
+ strcmp(filename, "/sbin/tomoyo-start"))
+ return;
+ if (!tomoyo_policy_loader_exists())
+ return;
+ {
+ char *argv[2];
+ char *envp[3];
+ printk(KERN_INFO "Calling %s to load policy. Please wait.\n",
+ tomoyo_loader);
+ argv[0] = (char *) tomoyo_loader;
+ argv[1] = NULL;
+ envp[0] = "HOME=/";
+ envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
+ envp[2] = NULL;
+ call_usermodehelper(argv[0], argv, envp, 1);
+ }
+ printk(KERN_INFO "TOMOYO: 2.3.0-pre 2009/10/01\n");
+ printk(KERN_INFO "Mandatory Access Control activated.\n");
+ tomoyo_check_profile();
+}
--
^ permalink raw reply [flat|nested] 50+ messages in thread
* [TOMOYO #16 21/25] TOMOYO: Add securityfs interface.
2009-10-04 12:49 [TOMOYO #16 00/25] Starting TOMOYO 2.3 Tetsuo Handa
` (19 preceding siblings ...)
2009-10-04 12:50 ` [TOMOYO #16 20/25] TOMOYO: Add policy loader launcher Tetsuo Handa
@ 2009-10-04 12:50 ` Tetsuo Handa
2009-10-04 12:50 ` [TOMOYO #16 22/25] TOMOYO: Add pathname calculation functions Tetsuo Handa
` (4 subsequent siblings)
25 siblings, 0 replies; 50+ messages in thread
From: Tetsuo Handa @ 2009-10-04 12:50 UTC (permalink / raw)
To: linux-security-module; +Cc: linux-kernel, Tetsuo Handa
[-- Attachment #1: tomoyo-add-securityfs-interface.patch --]
[-- Type: text/plain, Size: 4720 bytes --]
This patch contains code for creating /sys/kernel/security/tomoyo/ interface.
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
security/tomoyo/securityfs_if.c | 148 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 148 insertions(+)
--- /dev/null
+++ security-testing-2.6/security/tomoyo/securityfs_if.c
@@ -0,0 +1,148 @@
+/*
+ * security/tomoyo/securityfs_if.c
+ *
+ * Copyright (C) 2005-2009 NTT DATA CORPORATION
+ */
+#include "internal.h"
+
+/**
+ * tomoyo_open - open() for /sys/kernel/security/tomoyo/ interface.
+ *
+ * @inode: Pointer to "struct inode".
+ * @file: Pointer to "struct file".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_open(struct inode *inode, struct file *file)
+{
+ const int key = ((u8 *) file->f_path.dentry->d_inode->i_private)
+ - ((u8 *) NULL);
+ return tomoyo_open_control(key, file);
+}
+
+/**
+ * tomoyo_release - close() for /sys/kernel/security/tomoyo/ interface.
+ *
+ * @inode: Pointer to "struct inode".
+ * @file: Pointer to "struct file".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_release(struct inode *inode, struct file *file)
+{
+ return tomoyo_close_control(file);
+}
+
+/**
+ * tomoyo_poll - poll() for /sys/kernel/security/tomoyo/ interface.
+ *
+ * @file: Pointer to "struct file".
+ * @wait: Pointer to "poll_table".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static unsigned int tomoyo_poll(struct file *file, poll_table *wait)
+{
+ return tomoyo_poll_control(file, wait);
+}
+
+/**
+ * tomoyo_read - read() for /sys/kernel/security/tomoyo/ interface.
+ *
+ * @file: Pointer to "struct file".
+ * @buf: Pointer to buffer.
+ * @count: Size of @buf.
+ * @ppos: Unused.
+ *
+ * Returns bytes read on success, negative value otherwise.
+ */
+static ssize_t tomoyo_read(struct file *file, char __user *buf, size_t count,
+ loff_t *ppos)
+{
+ return tomoyo_read_control(file, buf, count);
+}
+
+/**
+ * tomoyo_write - write() for /sys/kernel/security/tomoyo/ interface.
+ *
+ * @file: Pointer to "struct file".
+ * @buf: Pointer to buffer.
+ * @count: Size of @buf.
+ * @ppos: Unused.
+ *
+ * Returns @count on success, negative value otherwise.
+ */
+static ssize_t tomoyo_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ return tomoyo_write_control(file, buf, count);
+}
+
+/* Operations for /sys/kernel/security/tomoyo/interface. */
+static const struct file_operations tomoyo_operations = {
+ .open = tomoyo_open,
+ .release = tomoyo_release,
+ .poll = tomoyo_poll,
+ .read = tomoyo_read,
+ .write = tomoyo_write,
+};
+
+/**
+ * tomoyo_create_entry - Create interface files under /sys/kernel/security/tomoyo/ directory.
+ *
+ * @name: The name of the interface file.
+ * @mode: The permission of the interface file.
+ * @parent: The parent directory.
+ * @key: Type of interface.
+ *
+ * Returns nothing.
+ */
+static void __init tomoyo_create_entry(const char *name, const mode_t mode,
+ struct dentry *parent, const u8 key)
+{
+ securityfs_create_file(name, mode, parent, ((u8 *) NULL) + key,
+ &tomoyo_operations);
+}
+
+/**
+ * tomoyo_securityfs_init - Initialize /sys/kernel/security/tomoyo/ interface.
+ *
+ * Returns 0.
+ */
+static int __init tomoyo_securityfs_init(void)
+{
+ struct dentry *tomoyo_dir;
+ if (!tomoyo_registered)
+ return 0;
+ tomoyo_dir = securityfs_create_dir("tomoyo", NULL);
+ tomoyo_create_entry("query", 0600, tomoyo_dir,
+ TOMOYO_QUERY);
+ tomoyo_create_entry("domain_policy", 0600, tomoyo_dir,
+ TOMOYO_DOMAINPOLICY);
+ tomoyo_create_entry("exception_policy", 0600, tomoyo_dir,
+ TOMOYO_EXCEPTIONPOLICY);
+#ifdef CONFIG_SECURITY_TOMOYO_AUDIT
+ tomoyo_create_entry("grant_log", 0400, tomoyo_dir,
+ TOMOYO_GRANTLOG);
+ tomoyo_create_entry("reject_log", 0400, tomoyo_dir,
+ TOMOYO_REJECTLOG);
+#endif
+ tomoyo_create_entry("self_domain", 0400, tomoyo_dir,
+ TOMOYO_SELFDOMAIN);
+ tomoyo_create_entry(".domain_status", 0600, tomoyo_dir,
+ TOMOYO_DOMAIN_STATUS);
+ tomoyo_create_entry(".process_status", 0600, tomoyo_dir,
+ TOMOYO_PROCESS_STATUS);
+ tomoyo_create_entry("meminfo", 0600, tomoyo_dir,
+ TOMOYO_MEMINFO);
+ tomoyo_create_entry("profile", 0600, tomoyo_dir,
+ TOMOYO_PROFILE);
+ tomoyo_create_entry("manager", 0600, tomoyo_dir,
+ TOMOYO_MANAGER);
+ tomoyo_create_entry("version", 0400, tomoyo_dir,
+ TOMOYO_VERSION);
+ tomoyo_create_entry(".execute_handler", 0666, tomoyo_dir,
+ TOMOYO_EXECUTE_HANDLER);
+ return 0;
+}
+fs_initcall(tomoyo_securityfs_init);
--
^ permalink raw reply [flat|nested] 50+ messages in thread
* [TOMOYO #16 22/25] TOMOYO: Add pathname calculation functions.
2009-10-04 12:49 [TOMOYO #16 00/25] Starting TOMOYO 2.3 Tetsuo Handa
` (20 preceding siblings ...)
2009-10-04 12:50 ` [TOMOYO #16 21/25] TOMOYO: Add securityfs interface Tetsuo Handa
@ 2009-10-04 12:50 ` Tetsuo Handa
2009-10-04 12:50 ` [TOMOYO #16 23/25] TOMOYO: Add file access restriction Tetsuo Handa
` (3 subsequent siblings)
25 siblings, 0 replies; 50+ messages in thread
From: Tetsuo Handa @ 2009-10-04 12:50 UTC (permalink / raw)
To: linux-security-module; +Cc: linux-kernel, Tetsuo Handa
[-- Attachment #1: tomoyo-add-new-realpath.patch --]
[-- Type: text/plain, Size: 8322 bytes --]
This patch contains code for getting canonicalized pathnames used by TOMOYO.
TOMOYO 2.2.0 uses __d_path() for getting canonicalized pathnames, but there are
several issues.
(1) The " (deleted)" is appended to the returned pathname if the pathname was
deleted. This causes TOMOYO to check
allow_truncate /tmp/file
for
int fd = open("/tmp/file", O_WRONLY | O_CREAT, 0600);
ftruncate(fd, 0);
unlink("/tmp/file");
close(fd);
sequence and
allow_truncate /tmp/file\040(deleted)
for
int fd = open("/tmp/file", O_WRONLY | O_CREAT, 0600);
unlink("/tmp/file");
ftruncate(fd, 0);
close(fd);
sequence, although these sequences are doing the same operations in
different order. Since this is annoying for policy developers, I want
to avoid trailing " (deleted)".
(2) __d_path() does not replace /proc/PID/ with /proc/self/ if PID is equals to
the current process's PID. Pathname based access control can prevent
current process from accessing other process's information if we can map
/proc/PID/ to /proc/self/ . The caller of __d_path() can't replace "PID"
with "self" after returning from __d_path() because the caller cannot tell
whether the numeric part of the returned pathname is a PID or not.
To tell whether the numeric part of the returned pathname is a PID or not,
the caller has to do dentry/vfsmount traversal by its own, and that makes
use of __d_path() meaningless.
(3) The pathname for socket is in "socket:[%u]" format. But such pathname is
too useless for pathname based access control.
I want to use pathnames in "socket:[family=%u:type=%u:protocol=%u]" format.
The best pathname might include socket's peer address information, but it
would be difficult to fetch and represent address information.
To solve these issues, I'd like to introduce custom __d_path() functions
(or add some flags to __d_path()).
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
security/tomoyo/new-realpath.c | 251 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 251 insertions(+)
--- /dev/null
+++ security-testing-2.6/security/tomoyo/new-realpath.c
@@ -0,0 +1,251 @@
+/*
+ * security/tomoyo/realpath.c
+ *
+ * Copyright (C) 2005-2009 NTT DATA CORPORATION
+ */
+#include "internal.h"
+#include <net/sock.h>
+#include <linux/magic.h>
+#include <linux/namei.h>
+#include <linux/mount.h>
+
+/**
+ * tomoyo_get_absolute_path - Get the path of a dentry but ignores chroot'ed root.
+ *
+ * @path: Pointer to "struct path".
+ * @buffer: Pointer to buffer to return value in.
+ * @buflen: Sizeof @buffer.
+ *
+ * Returns the buffer on success, an error code otherwise.
+ *
+ * Caller holds the dcache_lock and vfsmount_lock.
+ * Based on __d_path() in fs/dcache.c
+ *
+ * If dentry is a directory, trailing '/' is appended.
+ * /proc/pid is represented as /proc/self if pid is current.
+ */
+static char *tomoyo_get_absolute_path(struct path *path, char * const buffer,
+ const int buflen)
+{
+ char *pos = buffer + buflen - 1;
+ struct dentry *dentry = path->dentry;
+ struct vfsmount *vfsmnt = path->mnt;
+ bool is_dir = (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode));
+ const char *name;
+ int len;
+
+ if (buflen < 256)
+ goto out;
+
+ *pos = '\0';
+ for (;;) {
+ struct dentry *parent;
+ if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {
+ if (vfsmnt->mnt_parent == vfsmnt)
+ break;
+ dentry = vfsmnt->mnt_mountpoint;
+ vfsmnt = vfsmnt->mnt_parent;
+ continue;
+ }
+ if (is_dir) {
+ is_dir = false;
+ *--pos = '/';
+ }
+ parent = dentry->d_parent;
+ name = dentry->d_name.name;
+ len = dentry->d_name.len;
+ if (IS_ROOT(parent) && *name > '0' && *name <= '9' &&
+ parent->d_sb &&
+ parent->d_sb->s_magic == PROC_SUPER_MAGIC) {
+ char *ep;
+ const pid_t pid = (pid_t) simple_strtoul(name, &ep,
+ 10);
+ const pid_t tgid = task_tgid_nr_ns(current,
+ dentry->d_sb->
+ s_fs_info);
+ if (!*ep && pid == tgid && tgid) {
+ name = "self";
+ len = 4;
+ }
+ }
+ pos -= len;
+ if (pos <= buffer)
+ goto out;
+ memmove(pos, name, len);
+ *--pos = '/';
+ dentry = parent;
+ }
+ if (*pos == '/')
+ pos++;
+ len = dentry->d_name.len;
+ pos -= len;
+ if (pos < buffer)
+ goto out;
+ memmove(pos, dentry->d_name.name, len);
+ return pos;
+ out:
+ return ERR_PTR(-ENOMEM);
+}
+
+#define SOCKFS_MAGIC 0x534F434B
+
+/**
+ * tomoyo_realpath_from_path - Returns realpath(3) of the given pathname but ignores chroot'ed root.
+ *
+ * @path: Pointer to "struct path".
+ *
+ * Returns the realpath of the given @path on success, NULL otherwise.
+ *
+ * This function uses kzalloc(), so caller must kfree() if this function
+ * didn't return NULL.
+ */
+char *tomoyo_realpath_from_path(struct path *path)
+{
+ char *buf = NULL;
+ char *name = NULL;
+ unsigned int buf_len = PAGE_SIZE / 2;
+ struct dentry *dentry = path->dentry;
+ if (!dentry)
+ return NULL;
+ while (1) {
+ char *pos;
+ buf_len <<= 1;
+ kfree(buf);
+ buf = kmalloc(buf_len, GFP_KERNEL);
+ if (!buf)
+ break;
+ /* Get better name for socket. */
+ if (dentry->d_sb && dentry->d_sb->s_magic == SOCKFS_MAGIC) {
+ struct inode *inode = dentry->d_inode;
+ struct socket *sock = inode ? SOCKET_I(inode) : NULL;
+ struct sock *sk = sock ? sock->sk : NULL;
+ if (sk) {
+ snprintf(buf, buf_len - 1, "socket:[family=%u:"
+ "type=%u:protocol=%u]", sk->sk_family,
+ sk->sk_type, sk->sk_protocol);
+ } else {
+ snprintf(buf, buf_len - 1, "socket:[unknown]");
+ }
+ name = tomoyo_encode(buf);
+ break;
+ }
+ /* For "socket:[\$]" and "pipe:[\$]". */
+ if (dentry->d_op && dentry->d_op->d_dname) {
+ pos = dentry->d_op->d_dname(dentry, buf, buf_len - 1);
+ if (IS_ERR(pos))
+ continue;
+ name = tomoyo_encode(pos);
+ break;
+ }
+ if (!path->mnt)
+ break;
+ path_get(path);
+ spin_lock(&dcache_lock);
+ spin_lock(&vfsmount_lock);
+ pos = tomoyo_get_absolute_path(path, buf, buf_len - 1);
+ spin_unlock(&vfsmount_lock);
+ spin_unlock(&dcache_lock);
+ path_put(path);
+ if (IS_ERR(pos))
+ continue;
+ name = tomoyo_encode(pos);
+ break;
+ }
+ kfree(buf);
+ if (!name)
+ tomoyo_warn_oom(__func__);
+ return name;
+}
+
+/**
+ * tomoyo_symlink_path - Get symlink's pathname.
+ *
+ * @pathname: The pathname to solve.
+ * @name: Pointer to "struct tomoyo_path_info".
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * This function uses kzalloc(), so caller must kfree() if this function
+ * didn't return NULL.
+ */
+int tomoyo_symlink_path(const char *pathname, struct tomoyo_path_info *name)
+{
+ char *buf;
+ struct path path;
+ if (!pathname || kern_path(pathname, 0, &path))
+ return -ENOENT;
+ buf = tomoyo_realpath_from_path(&path);
+ path_put(&path);
+ if (buf) {
+ name->name = buf;
+ tomoyo_fill_path_info(name);
+ return 0;
+ }
+ return -ENOMEM;
+}
+
+/**
+ * tomoyo_encode: Encode binary string to ascii string.
+ *
+ * @str: String in binary format.
+ *
+ * Returns pointer to @str in ascii format on success, NULL otherwise.
+ *
+ * This function uses kzalloc(), so caller must kfree() if this function
+ * didn't return NULL.
+ */
+char *tomoyo_encode(const char *str)
+{
+ int len = 0;
+ const char *p = str;
+ char *cp;
+ char *cp0;
+ if (!p)
+ return NULL;
+ while (*p) {
+ const unsigned char c = *p++;
+ if (c == '\\')
+ len += 2;
+ else if (c > ' ' && c < 127)
+ len++;
+ else
+ len += 4;
+ }
+ len++;
+ /* Reserve space for appending "/". */
+ cp = kzalloc(len + 10, GFP_KERNEL);
+ if (!cp)
+ return NULL;
+ cp0 = cp;
+ p = str;
+ while (*p) {
+ const unsigned char c = *p++;
+ if (c == '\\') {
+ *cp++ = '\\';
+ *cp++ = '\\';
+ } else if (c > ' ' && c < 127) {
+ *cp++ = c;
+ } else {
+ *cp++ = '\\';
+ *cp++ = (c >> 6) + '0';
+ *cp++ = ((c >> 3) & 7) + '0';
+ *cp++ = (c & 7) + '0';
+ }
+ }
+ return cp0;
+}
+
+/**
+ * tomoyo_get_path - Get dentry/vfsmmount of a pathname.
+ *
+ * @pathname: The pathname to solve.
+ * @path: Pointer to "struct path".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_get_path(const char *pathname, struct path *path)
+{
+ if (!pathname || kern_path(pathname, LOOKUP_FOLLOW, path))
+ return -ENOENT;
+ return 0;
+}
--
^ permalink raw reply [flat|nested] 50+ messages in thread
* [TOMOYO #16 23/25] TOMOYO: Add file access restriction.
2009-10-04 12:49 [TOMOYO #16 00/25] Starting TOMOYO 2.3 Tetsuo Handa
` (21 preceding siblings ...)
2009-10-04 12:50 ` [TOMOYO #16 22/25] TOMOYO: Add pathname calculation functions Tetsuo Handa
@ 2009-10-04 12:50 ` Tetsuo Handa
2009-10-04 12:50 ` [TOMOYO #16 24/25] TOMOYO: Add domain transition handler Tetsuo Handa
` (2 subsequent siblings)
25 siblings, 0 replies; 50+ messages in thread
From: Tetsuo Handa @ 2009-10-04 12:50 UTC (permalink / raw)
To: linux-security-module; +Cc: linux-kernel, Tetsuo Handa
[-- Attachment #1: tomoyo-add-new-file.patch --]
[-- Type: text/plain, Size: 67255 bytes --]
This patch contains code for checking file access.
Functions are separated by type of parameters.
(1) Hooks which take one pathname.
They are read/write/execute/unlink/rmdir/truncate/symlink/rewrite/chroot/
umount.
(2) Hooks which take one pathname and three numeric parameters.
They are mkblock/mkchar.
(3) Hooks which take two pathnames.
They are link/rename/pivot_root.
(4) Hooks which take one pathname and one numeric parameter.
They are create/mkdir/mkfifo/mksock/ioctl/chmod/chown/chgrp.
(5) Hooks which take three pathname and one numeric parameter.
That is mount, and handled by separate patch.
I want to call may_create() and may_delete() for directory modification
operations in order to perform DAC checks before MAC. (For now, I'm not calling
may_create() and may_delete().)
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
security/tomoyo/new-file.c | 2249 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 2249 insertions(+)
--- /dev/null
+++ security-testing-2.6/security/tomoyo/new-file.c
@@ -0,0 +1,2249 @@
+/*
+ * security/tomoyo/file.c
+ *
+ * Copyright (C) 2005-2009 NTT DATA CORPORATION
+ */
+#include "internal.h"
+#include <linux/mount.h>
+#include <asm/ioctls.h>
+#include <linux/falloc.h>
+
+#define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE])
+
+static const char *tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION] = {
+ [TOMOYO_TYPE_READ_WRITE] = "read/write",
+ [TOMOYO_TYPE_EXECUTE] = "execute",
+ [TOMOYO_TYPE_READ] = "read",
+ [TOMOYO_TYPE_WRITE] = "write",
+ [TOMOYO_TYPE_UNLINK] = "unlink",
+ [TOMOYO_TYPE_RMDIR] = "rmdir",
+ [TOMOYO_TYPE_TRUNCATE] = "truncate",
+ [TOMOYO_TYPE_SYMLINK] = "symlink",
+ [TOMOYO_TYPE_REWRITE] = "rewrite",
+ [TOMOYO_TYPE_CHROOT] = "chroot",
+ [TOMOYO_TYPE_UMOUNT] = "unmount",
+};
+
+static const char *
+tomoyo_path_number3_keyword[TOMOYO_MAX_PATH_NUMBER3_OPERATION] = {
+ [TOMOYO_TYPE_MKBLOCK] = "mkblock",
+ [TOMOYO_TYPE_MKCHAR] = "mkchar",
+};
+
+static const char *tomoyo_path2_keyword[TOMOYO_MAX_PATH2_OPERATION] = {
+ [TOMOYO_TYPE_LINK] = "link",
+ [TOMOYO_TYPE_RENAME] = "rename",
+ [TOMOYO_TYPE_PIVOT_ROOT] = "pivot_root",
+};
+
+static const char *
+tomoyo_path_number_keyword[TOMOYO_MAX_PATH_NUMBER_OPERATION] = {
+ [TOMOYO_TYPE_CREATE] = "create",
+ [TOMOYO_TYPE_MKDIR] = "mkdir",
+ [TOMOYO_TYPE_MKFIFO] = "mkfifo",
+ [TOMOYO_TYPE_MKSOCK] = "mksock",
+ [TOMOYO_TYPE_IOCTL] = "ioctl",
+ [TOMOYO_TYPE_CHMOD] = "chmod",
+ [TOMOYO_TYPE_CHOWN] = "chown",
+ [TOMOYO_TYPE_CHGRP] = "chgrp",
+};
+
+static const u8 tomoyo_p2mac[TOMOYO_MAX_PATH_OPERATION] = {
+ [TOMOYO_TYPE_READ_WRITE] = TOMOYO_MAC_FILE_OPEN,
+ [TOMOYO_TYPE_EXECUTE] = TOMOYO_MAC_FILE_EXECUTE,
+ [TOMOYO_TYPE_READ] = TOMOYO_MAC_FILE_OPEN,
+ [TOMOYO_TYPE_WRITE] = TOMOYO_MAC_FILE_OPEN,
+ [TOMOYO_TYPE_UNLINK] = TOMOYO_MAC_FILE_UNLINK,
+ [TOMOYO_TYPE_RMDIR] = TOMOYO_MAC_FILE_RMDIR,
+ [TOMOYO_TYPE_TRUNCATE] = TOMOYO_MAC_FILE_TRUNCATE,
+ [TOMOYO_TYPE_SYMLINK] = TOMOYO_MAC_FILE_SYMLINK,
+ [TOMOYO_TYPE_REWRITE] = TOMOYO_MAC_FILE_REWRITE,
+ [TOMOYO_TYPE_CHROOT] = TOMOYO_MAC_FILE_CHROOT,
+ [TOMOYO_TYPE_UMOUNT] = TOMOYO_MAC_FILE_UMOUNT,
+};
+
+static const u8 tomoyo_pnnn2mac[TOMOYO_MAX_PATH_NUMBER3_OPERATION] = {
+ [TOMOYO_TYPE_MKBLOCK] = TOMOYO_MAC_FILE_MKBLOCK,
+ [TOMOYO_TYPE_MKCHAR] = TOMOYO_MAC_FILE_MKCHAR,
+};
+
+static const u8 tomoyo_pp2mac[TOMOYO_MAX_PATH2_OPERATION] = {
+ [TOMOYO_TYPE_LINK] = TOMOYO_MAC_FILE_LINK,
+ [TOMOYO_TYPE_RENAME] = TOMOYO_MAC_FILE_RENAME,
+ [TOMOYO_TYPE_PIVOT_ROOT] = TOMOYO_MAC_FILE_PIVOT_ROOT,
+};
+
+static const u8 tomoyo_pn2mac[TOMOYO_MAX_PATH_NUMBER_OPERATION] = {
+ [TOMOYO_TYPE_CREATE] = TOMOYO_MAC_FILE_CREATE,
+ [TOMOYO_TYPE_MKDIR] = TOMOYO_MAC_FILE_MKDIR,
+ [TOMOYO_TYPE_MKFIFO] = TOMOYO_MAC_FILE_MKFIFO,
+ [TOMOYO_TYPE_MKSOCK] = TOMOYO_MAC_FILE_MKSOCK,
+ [TOMOYO_TYPE_IOCTL] = TOMOYO_MAC_FILE_IOCTL,
+ [TOMOYO_TYPE_CHMOD] = TOMOYO_MAC_FILE_CHMOD,
+ [TOMOYO_TYPE_CHOWN] = TOMOYO_MAC_FILE_CHOWN,
+ [TOMOYO_TYPE_CHGRP] = TOMOYO_MAC_FILE_CHGRP,
+};
+
+
+void tomoyo_put_name_union(struct tomoyo_name_union *ptr)
+{
+ if (!ptr)
+ return;
+ if (ptr->is_group)
+ tomoyo_put_path_group(ptr->group);
+ else
+ tomoyo_put_name(ptr->filename);
+}
+
+void tomoyo_put_number_union(struct tomoyo_number_union *ptr)
+{
+ if (ptr && ptr->is_group)
+ tomoyo_put_number_group(ptr->group);
+}
+
+bool tomoyo_compare_number_union(const unsigned long value,
+ const struct tomoyo_number_union *ptr)
+{
+ if (ptr->is_group)
+ return tomoyo_number_matches_group(value, value, ptr->group);
+ return value >= ptr->values[0] && value <= ptr->values[1];
+}
+
+bool tomoyo_compare_name_union(const struct tomoyo_path_info *name,
+ const struct tomoyo_name_union *ptr)
+{
+ if (ptr->is_group)
+ return tomoyo_path_matches_group(name, ptr->group, 1);
+ return tomoyo_path_matches_pattern(name, ptr->filename);
+}
+
+static bool tomoyo_compare_name_union_pattern
+(const struct tomoyo_path_info *name, const struct tomoyo_name_union *ptr,
+ const bool may_use_pattern)
+{
+ if (ptr->is_group)
+ return tomoyo_path_matches_group(name, ptr->group,
+ may_use_pattern);
+ if (may_use_pattern || !ptr->filename->is_patterned)
+ return tomoyo_path_matches_pattern(name, ptr->filename);
+ return false;
+}
+
+/**
+ * tomoyo_path2keyword - Get the name of single path operation.
+ *
+ * @operation: Type of operation.
+ *
+ * Returns the name of single path operation.
+ */
+const char *tomoyo_path2keyword(const u8 operation)
+{
+ return (operation < TOMOYO_MAX_PATH_OPERATION)
+ ? tomoyo_path_keyword[operation] : NULL;
+}
+
+/**
+ * tomoyo_path_number32keyword - Get the name of mkdev operation.
+ *
+ * @operation: Type of operation.
+ *
+ * Returns the name of mkdev operation.
+ */
+const char *tomoyo_path_number32keyword(const u8 operation)
+{
+ return (operation < TOMOYO_MAX_PATH_NUMBER3_OPERATION)
+ ? tomoyo_path_number3_keyword[operation] : NULL;
+}
+
+/**
+ * tomoyo_path22keyword - Get the name of double path operation.
+ *
+ * @operation: Type of operation.
+ *
+ * Returns the name of double path operation.
+ */
+const char *tomoyo_path22keyword(const u8 operation)
+{
+ return (operation < TOMOYO_MAX_PATH2_OPERATION)
+ ? tomoyo_path2_keyword[operation] : NULL;
+}
+
+const char *tomoyo_path_number2keyword(const u8 operation)
+{
+ return (operation < TOMOYO_MAX_PATH_NUMBER_OPERATION)
+ ? tomoyo_path_number_keyword[operation] : NULL;
+}
+
+static void tomoyo_add_slash(struct tomoyo_path_info *buf)
+{
+ if (buf->is_dir)
+ return;
+ /*
+ * This is OK because tomoyo_encode() reserves space for appending "/".
+ */
+ strcat((char *) buf->name, "/");
+ tomoyo_fill_path_info(buf);
+}
+
+/**
+ * tomoyo_strendswith - Check whether the token ends with the given token.
+ *
+ * @name: The token to check.
+ * @tail: The token to find.
+ *
+ * Returns true if @name ends with @tail, false otherwise.
+ */
+static bool tomoyo_strendswith(const char *name, const char *tail)
+{
+ int len;
+ if (!name || !tail)
+ return false;
+ len = strlen(name) - strlen(tail);
+ return len >= 0 && !strcmp(name + len, tail);
+}
+
+/**
+ * tomoyo_get_realpath - Get realpath.
+ *
+ * @buf: Pointer to "struct tomoyo_path_info".
+ * @dentry: Pointer to "struct dentry".
+ * @mnt: Pointer to "struct vfsmount".
+ *
+ * Returns true success, false otherwise.
+ */
+static bool tomoyo_get_realpath(struct tomoyo_path_info *buf,
+ struct dentry *dentry, struct vfsmount *mnt)
+{
+ struct path path = { mnt, dentry };
+ buf->name = tomoyo_realpath_from_path(&path);
+ if (buf->name) {
+ tomoyo_fill_path_info(buf);
+ return true;
+ }
+ return false;
+}
+
+static int tomoyo_update_path_acl(const u8 type, const char *filename,
+ struct tomoyo_domain_info * const domain,
+ struct tomoyo_condition *condition,
+ const bool is_delete);
+
+/**
+ * tomoyo_audit_path_log - Audit single path request log.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ * @operation: The name of operation.
+ * @filename: Pathname.
+ * @is_granted: True if this is a granted log.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_audit_path_log(struct tomoyo_request_info *r,
+ const char *operation, const char *filename,
+ const bool is_granted)
+{
+ if (!is_granted)
+ tomoyo_warn_log(r, "%s %s", operation, filename);
+ return tomoyo_write_audit_log(is_granted, r, "allow_%s %s\n",
+ operation, filename);
+}
+
+/**
+ * tomoyo_audit_path2_log - Audit double path request log.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ * @operation: The name of operation.
+ * @filename1: First pathname.
+ * @filename2: Second pathname.
+ * @is_granted: True if this is a granted log.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_audit_path2_log(struct tomoyo_request_info *r,
+ const char *operation, const char *filename1,
+ const char *filename2, const bool is_granted)
+{
+ if (!is_granted)
+ tomoyo_warn_log(r, "%s %s %s", operation, filename1,
+ filename2);
+ return tomoyo_write_audit_log(is_granted, r, "allow_%s %s %s\n",
+ operation, filename1, filename2);
+}
+
+/**
+ * tomoyo_audit_path_number3_log - Audit mkdev request log.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ * @operation: The name of operation.
+ * @filename: First pathname.
+ * @mode: Create mode.
+ * @major: Device major number.
+ * @minor: Device minor number.
+ * @is_granted: True if this is a granted log.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_audit_path_number3_log(struct tomoyo_request_info *r,
+ const char *operation,
+ const char *filename,
+ const unsigned int mode,
+ const unsigned int major,
+ const unsigned int minor,
+ const bool is_granted)
+{
+ if (!is_granted)
+ tomoyo_warn_log(r, "%s %s 0%o %u %u", operation, filename,
+ mode, major, minor);
+ return tomoyo_write_audit_log(is_granted, r, "allow_%s %s 0%o %u %u\n",
+ operation, filename, mode, major, minor);
+}
+
+/**
+ * tomoyo_audit_path_number_log - Audit ioctl/chmod/chown/chgrp related request log.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ * @type: Type of operation.
+ * @filename: Pathname.
+ * @value: Value.
+ * @is_granted: True if this is a granted log.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_audit_path_number_log(struct tomoyo_request_info *r,
+ const char *operation,
+ const char *filename,
+ const char *value,
+ const bool is_granted)
+{
+ if (!is_granted)
+ tomoyo_warn_log(r, "%s %s %s", operation, filename, value);
+ return tomoyo_write_audit_log(is_granted, r, "allow_%s %s %s\n",
+ operation, filename, value);
+}
+
+/*
+ * tomoyo_globally_readable_list is used for holding list of pathnames which
+ * are by default allowed to be open()ed for reading by any process.
+ *
+ * An entry is added by
+ *
+ * # echo 'allow_read /lib/libc-2.5.so' > \
+ * /sys/kernel/security/tomoyo/exception_policy
+ *
+ * and is deleted by
+ *
+ * # echo 'delete allow_read /lib/libc-2.5.so' > \
+ * /sys/kernel/security/tomoyo/exception_policy
+ *
+ * and all entries are retrieved by
+ *
+ * # grep ^allow_read /sys/kernel/security/tomoyo/exception_policy
+ *
+ * In the example above, any process is allowed to
+ * open("/lib/libc-2.5.so", O_RDONLY).
+ * One exception is, if the domain which current process belongs to is marked
+ * as "ignore_global_allow_read", current process can't do so unless explicitly
+ * given "allow_read /lib/libc-2.5.so" to the domain which current process
+ * belongs to.
+ */
+LIST_HEAD(tomoyo_globally_readable_list);
+
+/**
+ * tomoyo_is_globally_readable_file - Check if the file is unconditionnaly permitted to be open()ed for reading.
+ *
+ * @filename: The filename to check.
+ *
+ * Returns true if any domain can open @filename for reading, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static bool tomoyo_is_globally_readable_file
+(const struct tomoyo_path_info *filename)
+{
+ struct tomoyo_globally_readable_file_entry *ptr;
+ bool found = false;
+ list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, list) {
+ if (ptr->is_deleted ||
+ !tomoyo_path_matches_pattern(filename, ptr->filename))
+ continue;
+ found = true;
+ break;
+ }
+ return found;
+}
+
+/**
+ * tomoyo_write_globally_readable_policy - Write "struct tomoyo_globally_readable_file_entry" list.
+ *
+ * @data: String to parse.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_write_globally_readable_policy(char *data, const bool is_delete)
+{
+ struct tomoyo_globally_readable_file_entry *entry = NULL;
+ struct tomoyo_globally_readable_file_entry *ptr;
+ struct tomoyo_globally_readable_file_entry e = { };
+ int error = is_delete ? -ENOENT : -ENOMEM;
+ if (!tomoyo_is_correct_path(data, 1, 0, -1))
+ return -EINVAL;
+ e.filename = tomoyo_get_name(data);
+ if (!e.filename)
+ return -ENOMEM;
+ if (!is_delete)
+ entry = kmalloc(sizeof(e), GFP_KERNEL);
+ mutex_lock(&tomoyo_policy_lock);
+ list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, list) {
+ if (ptr->filename != e.filename)
+ continue;
+ ptr->is_deleted = is_delete;
+ error = 0;
+ break;
+ }
+ if (!is_delete && error && tomoyo_commit_ok(entry, &e, sizeof(e))) {
+ list_add_tail_rcu(&entry->list,
+ &tomoyo_globally_readable_list);
+ entry = NULL;
+ error = 0;
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+ tomoyo_put_name(e.filename);
+ kfree(entry);
+ return error;
+}
+
+/**
+ * tomoyo_read_globally_readable_policy - Read "struct tomoyo_globally_readable_file_entry" list.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head)
+{
+ struct list_head *pos;
+ bool done = true;
+ list_for_each_cookie(pos, head->read_var2,
+ &tomoyo_globally_readable_list) {
+ struct tomoyo_globally_readable_file_entry *ptr;
+ ptr = list_entry(pos,
+ struct tomoyo_globally_readable_file_entry,
+ list);
+ if (ptr->is_deleted)
+ continue;
+ done = tomoyo_io_printf(head, TOMOYO_KEYWORD_ALLOW_READ "%s\n",
+ ptr->filename->name);
+ if (!done)
+ break;
+ }
+ return done;
+}
+
+/*
+ * tomoyo_pattern_list is used for holding list of pathnames which are used for
+ * converting pathnames to pathname patterns during learning mode.
+ *
+ * An entry is added by
+ *
+ * # echo 'file_pattern /proc/\$/mounts' > \
+ * /sys/kernel/security/tomoyo/exception_policy
+ *
+ * and is deleted by
+ *
+ * # echo 'delete file_pattern /proc/\$/mounts' > \
+ * /sys/kernel/security/tomoyo/exception_policy
+ *
+ * and all entries are retrieved by
+ *
+ * # grep ^file_pattern /sys/kernel/security/tomoyo/exception_policy
+ *
+ * In the example above, if a process which belongs to a domain which is in
+ * learning mode requested open("/proc/1/mounts", O_RDONLY),
+ * "allow_read /proc/\$/mounts" is automatically added to the domain which that
+ * process belongs to.
+ */
+LIST_HEAD(tomoyo_pattern_list);
+
+/**
+ * tomoyo_file_pattern - Get patterned pathname.
+ *
+ * @filename: Pointer to "struct tomoyo_path_info".
+ *
+ * Returns pointer to patterned pathname.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+const char *tomoyo_file_pattern(const struct tomoyo_path_info *filename)
+{
+ struct tomoyo_pattern_entry *ptr;
+ const struct tomoyo_path_info *pattern = NULL;
+ list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) {
+ if (ptr->is_deleted)
+ continue;
+ if (!tomoyo_path_matches_pattern(filename, ptr->pattern))
+ continue;
+ pattern = ptr->pattern;
+ if (tomoyo_strendswith(pattern->name, "/\\*")) {
+ /* Do nothing. Try to find the better match. */
+ } else {
+ /* This would be the better match. Use this. */
+ break;
+ }
+ }
+ return pattern ? pattern->name : filename->name;
+}
+
+/**
+ * tomoyo_write_pattern_policy - Write "struct tomoyo_pattern_entry" list.
+ *
+ * @data: String to parse.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_write_pattern_policy(char *data, const bool is_delete)
+{
+ struct tomoyo_pattern_entry *entry = NULL;
+ struct tomoyo_pattern_entry *ptr;
+ struct tomoyo_pattern_entry e = { .pattern = tomoyo_get_name(data) };
+ int error = is_delete ? -ENOENT : -ENOMEM;
+ if (!e.pattern)
+ return error;
+ if (!e.pattern->is_patterned)
+ goto out;
+ if (!is_delete)
+ entry = kmalloc(sizeof(e), GFP_KERNEL);
+ mutex_lock(&tomoyo_policy_lock);
+ list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) {
+ if (e.pattern != ptr->pattern)
+ continue;
+ ptr->is_deleted = is_delete;
+ error = 0;
+ break;
+ }
+ if (!is_delete && error && tomoyo_commit_ok(entry, &e, sizeof(e))) {
+ list_add_tail_rcu(&entry->list, &tomoyo_pattern_list);
+ entry = NULL;
+ error = 0;
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+ out:
+ tomoyo_put_name(e.pattern);
+ kfree(entry);
+ return error;
+}
+
+/**
+ * tomoyo_read_file_pattern - Read "struct tomoyo_pattern_entry" list.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head)
+{
+ struct list_head *pos;
+ bool done = true;
+ list_for_each_cookie(pos, head->read_var2, &tomoyo_pattern_list) {
+ struct tomoyo_pattern_entry *ptr;
+ ptr = list_entry(pos, struct tomoyo_pattern_entry, list);
+ if (ptr->is_deleted)
+ continue;
+ done = tomoyo_io_printf(head, TOMOYO_KEYWORD_FILE_PATTERN
+ "%s\n", ptr->pattern->name);
+ if (!done)
+ break;
+ }
+ return done;
+}
+
+/*
+ * tomoyo_no_rewrite_list is used for holding list of pathnames which are by
+ * default forbidden to modify already written content of a file.
+ *
+ * An entry is added by
+ *
+ * # echo 'deny_rewrite /var/log/messages' > \
+ * /sys/kernel/security/tomoyo/exception_policy
+ *
+ * and is deleted by
+ *
+ * # echo 'delete deny_rewrite /var/log/messages' > \
+ * /sys/kernel/security/tomoyo/exception_policy
+ *
+ * and all entries are retrieved by
+ *
+ * # grep ^deny_rewrite /sys/kernel/security/tomoyo/exception_policy
+ *
+ * In the example above, if a process requested to rewrite /var/log/messages ,
+ * the process can't rewrite unless the domain which that process belongs to
+ * has "allow_rewrite /var/log/messages" entry.
+ */
+LIST_HEAD(tomoyo_no_rewrite_list);
+
+/**
+ * tomoyo_is_no_rewrite_file - Check if the given pathname is not permitted to be rewrited.
+ *
+ * @filename: Filename to check.
+ *
+ * Returns true if @filename is specified by "deny_rewrite" directive,
+ * false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static bool tomoyo_is_no_rewrite_file(const struct tomoyo_path_info *filename)
+{
+ struct tomoyo_no_rewrite_entry *ptr;
+ bool matched = false;
+ list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, list) {
+ if (ptr->is_deleted)
+ continue;
+ if (!tomoyo_path_matches_pattern(filename, ptr->pattern))
+ continue;
+ matched = true;
+ break;
+ }
+ return matched;
+}
+
+/**
+ * tomoyo_write_no_rewrite_policy - Write "struct tomoyo_no_rewrite_entry" list.
+ *
+ * @data: String to parse.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_write_no_rewrite_policy(char *data, const bool is_delete)
+{
+ struct tomoyo_no_rewrite_entry *entry = NULL;
+ struct tomoyo_no_rewrite_entry *ptr;
+ struct tomoyo_no_rewrite_entry e = { };
+ int error = is_delete ? -ENOENT : -ENOMEM;
+ if (!tomoyo_is_correct_path(data, 0, 0, 0))
+ return -EINVAL;
+ e.pattern = tomoyo_get_name(data);
+ if (!e.pattern)
+ return error;
+ if (!is_delete)
+ entry = kmalloc(sizeof(e), GFP_KERNEL);
+ mutex_lock(&tomoyo_policy_lock);
+ list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, list) {
+ if (ptr->pattern != e.pattern)
+ continue;
+ ptr->is_deleted = is_delete;
+ error = 0;
+ break;
+ }
+ if (!is_delete && error && tomoyo_commit_ok(entry, &e, sizeof(e))) {
+ list_add_tail_rcu(&entry->list, &tomoyo_no_rewrite_list);
+ entry = NULL;
+ error = 0;
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+ tomoyo_put_name(e.pattern);
+ kfree(entry);
+ return error;
+}
+
+/**
+ * tomoyo_read_no_rewrite_policy - Read "struct tomoyo_no_rewrite_entry" list.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head)
+{
+ struct list_head *pos;
+ bool done = true;
+ list_for_each_cookie(pos, head->read_var2,
+ &tomoyo_no_rewrite_list) {
+ struct tomoyo_no_rewrite_entry *ptr;
+ ptr = list_entry(pos, struct tomoyo_no_rewrite_entry, list);
+ if (ptr->is_deleted)
+ continue;
+ done = tomoyo_io_printf(head, TOMOYO_KEYWORD_DENY_REWRITE
+ "%s\n", ptr->pattern->name);
+ if (!done)
+ break;
+ }
+ return done;
+}
+
+/**
+ * tomoyo_update_file_acl - Update file's read/write/execute ACL.
+ *
+ * @perm: Permission (between 1 to 7).
+ * @filename: Filename.
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @condition: Pointer to "struct tomoyo_condition". May be NULL.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * This is legacy support interface for older policy syntax.
+ * Current policy syntax uses "allow_read/write" instead of "6",
+ * "allow_read" instead of "4", "allow_write" instead of "2",
+ * "allow_execute" instead of "1".
+ */
+static inline int tomoyo_update_file_acl
+(u8 perm, const char *filename, struct tomoyo_domain_info * const domain,
+ struct tomoyo_condition *condition, const bool is_delete)
+{
+ if (perm > 7 || !perm)
+ return -EINVAL;
+ if (filename[0] != '@' && tomoyo_strendswith(filename, "/"))
+ /*
+ * Only 'allow_mkdir' and 'allow_rmdir' are valid for
+ * directory permissions.
+ */
+ return 0;
+ if (perm & 4)
+ tomoyo_update_path_acl(TOMOYO_TYPE_READ, filename, domain,
+ condition, is_delete);
+ if (perm & 2)
+ tomoyo_update_path_acl(TOMOYO_TYPE_WRITE, filename,
+ domain, condition, is_delete);
+ if (perm & 1)
+ tomoyo_update_path_acl(TOMOYO_TYPE_EXECUTE, filename,
+ domain, condition, is_delete);
+ return 0;
+}
+
+/**
+ * tomoyo_path_acl - Check permission for single path operation.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ * @filename: Filename to check.
+ * @perm: Permission.
+ * @may_use_pattern: True if patterned ACL is permitted.
+ *
+ * Returns 0 on success, -EPERM otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static int tomoyo_path_acl(struct tomoyo_request_info *r,
+ const struct tomoyo_path_info *filename,
+ const u16 perm,
+ const bool may_use_pattern)
+{
+ struct tomoyo_domain_info *domain = r->domain;
+ struct tomoyo_acl_info *ptr;
+ int error = -EPERM;
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
+ struct tomoyo_path_acl *acl;
+ if (ptr->is_deleted || ptr->type != TOMOYO_TYPE_PATH_ACL)
+ continue;
+ acl = container_of(ptr, struct tomoyo_path_acl,
+ head);
+ if (!(acl->perm & perm) || !tomoyo_condition(r, ptr) ||
+ !tomoyo_compare_name_union_pattern(filename, &acl->name,
+ may_use_pattern))
+ continue;
+ r->cond = ptr->cond;
+ error = 0;
+ break;
+ }
+ return error;
+}
+
+/**
+ * tomoyo_path_number3_acl - Check permission for mkdev operation.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ * @filename: Filename to check.
+ * @perm: Permission.
+ * @mode: Create mode.
+ * @major: Device major number.
+ * @minor: Device minor number.
+ *
+ * Returns 0 on success, -EPERM otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static int tomoyo_path_number3_acl(struct tomoyo_request_info *r,
+ const struct tomoyo_path_info *filename,
+ const u16 perm, const unsigned int mode,
+ const unsigned int major,
+ const unsigned int minor)
+{
+ struct tomoyo_domain_info *domain = r->domain;
+ struct tomoyo_acl_info *ptr;
+ int error = -EPERM;
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
+ struct tomoyo_path_number3_acl *acl;
+ if (ptr->is_deleted ||
+ ptr->type != TOMOYO_TYPE_PATH_NUMBER3_ACL)
+ continue;
+ acl = container_of(ptr, struct tomoyo_path_number3_acl, head);
+ if (!tomoyo_compare_number_union(mode, &acl->mode))
+ continue;
+ if (!tomoyo_compare_number_union(major, &acl->major))
+ continue;
+ if (!tomoyo_compare_number_union(minor, &acl->minor))
+ continue;
+ if (!(acl->perm & perm) || !tomoyo_condition(r, ptr))
+ continue;
+ if (!tomoyo_compare_name_union(filename, &acl->name))
+ continue;
+ r->cond = ptr->cond;
+ error = 0;
+ break;
+ }
+ return error;
+}
+
+/**
+ * tomoyo_file_perm - Check permission for opening files.
+ *
+ * @r: Pointer to "strct tomoyo_request_info".
+ * @filename: Filename to check.
+ * @mode: Mode ("read" or "write" or "read/write" or "execute").
+ *
+ * Returns 0 on success, 1 on retry, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+int tomoyo_file_perm(struct tomoyo_request_info *r,
+ const struct tomoyo_path_info *filename, const u8 mode)
+{
+ const char *msg = "<unknown>";
+ int error = 0;
+ u16 perm = 0;
+ if (!filename)
+ return 0;
+ if (mode == 6) {
+ msg = tomoyo_path2keyword(TOMOYO_TYPE_READ_WRITE);
+ perm = 1 << TOMOYO_TYPE_READ_WRITE;
+ } else if (mode == 4) {
+ msg = tomoyo_path2keyword(TOMOYO_TYPE_READ);
+ perm = 1 << TOMOYO_TYPE_READ;
+ } else if (mode == 2) {
+ msg = tomoyo_path2keyword(TOMOYO_TYPE_WRITE);
+ perm = 1 << TOMOYO_TYPE_WRITE;
+ } else if (mode == 1) {
+ msg = tomoyo_path2keyword(TOMOYO_TYPE_EXECUTE);
+ perm = 1 << TOMOYO_TYPE_EXECUTE;
+ } else
+ BUG();
+ do {
+ error = tomoyo_path_acl(r, filename, perm, mode != 1);
+ if (error && mode == 4 && !r->domain->ignore_global_allow_read
+ && tomoyo_is_globally_readable_file(filename))
+ error = 0;
+ tomoyo_audit_path_log(r, msg, filename->name, !error);
+ if (!error)
+ break;
+ error = tomoyo_supervisor(r, "allow_%s %s\n", msg,
+ mode == 1 ? filename->name :
+ tomoyo_file_pattern(filename));
+ /*
+ * Do not retry for execute request, for aggregator may have
+ * changed.
+ */
+ } while (error == 1 && !r->ee);
+ if (r->mode != TOMOYO_CONFIG_ENFORCING)
+ error = 0;
+ return error;
+}
+
+/**
+ * tomoyo_update_execute_handler - Update "struct tomoyo_execute_handler_record" list.
+ *
+ * @type: Type of execute handler.
+ * @filename: Pathname to the execute handler.
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static inline int tomoyo_update_execute_handler
+(const u8 type, const char *filename, struct tomoyo_domain_info * const domain,
+ const bool is_delete)
+{
+ struct tomoyo_acl_info *ptr;
+ struct tomoyo_execute_handler_record e = { .head.type = type };
+ struct tomoyo_execute_handler_record *entry = NULL;
+ int error = is_delete ? -ENOENT : -ENOMEM;
+ if (!domain)
+ return -EINVAL;
+ if (!tomoyo_is_correct_path(filename, 1, -1, -1))
+ return -EINVAL;
+ e.handler = tomoyo_get_name(filename);
+ if (!e.handler)
+ return -ENOMEM;
+ if (!is_delete)
+ entry = kmalloc(sizeof(e), GFP_KERNEL);
+ mutex_lock(&tomoyo_policy_lock);
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
+ struct tomoyo_execute_handler_record *acl;
+ if (ptr->type != type)
+ continue;
+ /* Condition not supported. */
+ acl = container_of(ptr, struct tomoyo_execute_handler_record,
+ head);
+ if (acl->handler != e.handler)
+ continue;
+ if (!is_delete) {
+ /* Only one entry can exist in a domain. */
+ struct tomoyo_acl_info *ptr2;
+ list_for_each_entry_rcu(ptr2, &domain->acl_info_list,
+ list) {
+ if (ptr2->type == type)
+ ptr2->is_deleted = true;
+ }
+ }
+ ptr->is_deleted = is_delete;
+ error = 0;
+ break;
+ }
+ if (!is_delete && error && tomoyo_commit_ok(entry, &e, sizeof(e))) {
+ /* Only one entry can exist in a domain. */
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
+ if (ptr->type == type)
+ ptr->is_deleted = true;
+ }
+ tomoyo_add_domain_acl(domain, &entry->head);
+ entry = NULL;
+ error = 0;
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+ tomoyo_put_name(e.handler);
+ kfree(entry);
+ return error;
+}
+
+/**
+ * tomoyo_update_path_acl - Update "struct tomoyo_path_acl" list.
+ *
+ * @type: Type of operation.
+ * @filename: Filename.
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @condition: Pointer to "struct tomoyo_condition". May be NULL.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_update_path_acl(const u8 type, const char *filename,
+ struct tomoyo_domain_info * const domain,
+ struct tomoyo_condition *condition,
+ const bool is_delete)
+{
+ static const u16 tomoyo_rw_mask =
+ (1 << TOMOYO_TYPE_READ) | (1 << TOMOYO_TYPE_WRITE);
+ const u16 perm = 1 << type;
+ struct tomoyo_acl_info *ptr;
+ struct tomoyo_path_acl e = {
+ .head.type = TOMOYO_TYPE_PATH_ACL,
+ .head.cond = condition,
+ .perm = perm
+ };
+ struct tomoyo_path_acl *entry = NULL;
+ int error = is_delete ? -ENOENT : -ENOMEM;
+ if (type == TOMOYO_TYPE_READ_WRITE)
+ e.perm |= tomoyo_rw_mask;
+ if (!tomoyo_parse_name_union(filename, &e.name))
+ return -EINVAL;
+ if (!is_delete)
+ entry = kmalloc(sizeof(e), GFP_KERNEL);
+ mutex_lock(&tomoyo_policy_lock);
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
+ struct tomoyo_path_acl *acl =
+ container_of(ptr, struct tomoyo_path_acl,
+ head);
+ if (ptr->type != TOMOYO_TYPE_PATH_ACL ||
+ ptr->cond != condition ||
+ tomoyo_memcmp(acl, &e, offsetof(typeof(e), name),
+ sizeof(e)))
+ continue;
+ if (is_delete) {
+ acl->perm &= ~perm;
+ if ((acl->perm & tomoyo_rw_mask) != tomoyo_rw_mask)
+ acl->perm &= ~(1 << TOMOYO_TYPE_READ_WRITE);
+ else if (!(acl->perm & (1 << TOMOYO_TYPE_READ_WRITE)))
+ acl->perm &= ~tomoyo_rw_mask;
+ if (!acl->perm)
+ ptr->is_deleted = true;
+ } else {
+ if (ptr->is_deleted)
+ acl->perm = 0;
+ acl->perm |= perm;
+ if ((acl->perm & tomoyo_rw_mask) == tomoyo_rw_mask)
+ acl->perm |= 1 << TOMOYO_TYPE_READ_WRITE;
+ else if (acl->perm & (1 << TOMOYO_TYPE_READ_WRITE))
+ acl->perm |= tomoyo_rw_mask;
+ ptr->is_deleted = false;
+ }
+ error = 0;
+ break;
+ }
+ if (!is_delete && error && tomoyo_commit_ok(entry, &e, sizeof(e))) {
+ tomoyo_add_domain_acl(domain, &entry->head);
+ entry = NULL;
+ error = 0;
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+ tomoyo_put_name_union(&e.name);
+ kfree(entry);
+ return error;
+}
+
+/**
+ * tomoyo_update_path_number3_acl - Update "struct tomoyo_path_number3_acl" list.
+ *
+ * @type: Type of operation.
+ * @filename: Filename.
+ * @mode: Create mode.
+ * @major: Device major number.
+ * @minor: Device minor number.
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @condition: Pointer to "struct tomoyo_condition". May be NULL.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static inline int tomoyo_update_path_number3_acl
+(const u8 type, const char *filename, char *mode, char *major, char *minor,
+ struct tomoyo_domain_info * const domain, struct tomoyo_condition *condition,
+ const bool is_delete)
+{
+ const u8 perm = 1 << type;
+ struct tomoyo_acl_info *ptr;
+ struct tomoyo_path_number3_acl e = {
+ .head.type = TOMOYO_TYPE_PATH_NUMBER3_ACL,
+ .head.cond = condition,
+ .perm = perm
+ };
+ struct tomoyo_path_number3_acl *entry = NULL;
+ int error = is_delete ? -ENOENT : -ENOMEM;
+ if (!tomoyo_parse_name_union(filename, &e.name) ||
+ !tomoyo_parse_number_union(mode, &e.mode) ||
+ !tomoyo_parse_number_union(major, &e.major) ||
+ !tomoyo_parse_number_union(minor, &e.minor))
+ goto out;
+ if (!is_delete)
+ entry = kmalloc(sizeof(e), GFP_KERNEL);
+ mutex_lock(&tomoyo_policy_lock);
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
+ struct tomoyo_path_number3_acl *acl =
+ container_of(ptr, struct tomoyo_path_number3_acl,
+ head);
+ if (ptr->type != TOMOYO_TYPE_PATH_NUMBER3_ACL ||
+ ptr->cond != condition ||
+ tomoyo_memcmp(acl, &e, offsetof(typeof(e), name),
+ sizeof(e)))
+ continue;
+ if (is_delete) {
+ acl->perm &= ~perm;
+ if (!acl->perm)
+ ptr->is_deleted = true;
+ } else {
+ if (ptr->is_deleted)
+ acl->perm = 0;
+ acl->perm |= perm;
+ ptr->is_deleted = false;
+ }
+ error = 0;
+ break;
+ }
+ if (!is_delete && error && tomoyo_commit_ok(entry, &e, sizeof(e))) {
+ tomoyo_add_domain_acl(domain, &entry->head);
+ entry = NULL;
+ error = 0;
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+ out:
+ tomoyo_put_name_union(&e.name);
+ tomoyo_put_number_union(&e.mode);
+ tomoyo_put_number_union(&e.major);
+ tomoyo_put_number_union(&e.minor);
+ kfree(entry);
+ return error;
+}
+
+/**
+ * tomoyo_update_path2_acl - Update "struct tomoyo_path2_acl" list.
+ *
+ * @type: Type of operation.
+ * @filename1: First filename.
+ * @filename2: Second filename.
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @condition: Pointer to "struct tomoyo_condition". May be NULL.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static inline int tomoyo_update_path2_acl
+(const u8 type, const char *filename1, const char *filename2,
+ struct tomoyo_domain_info * const domain, struct tomoyo_condition *condition,
+ const bool is_delete)
+{
+ const u8 perm = 1 << type;
+ struct tomoyo_acl_info *ptr;
+ struct tomoyo_path2_acl e = {
+ .head.type = TOMOYO_TYPE_PATH2_ACL,
+ .head.cond = condition,
+ .perm = perm
+ };
+ struct tomoyo_path2_acl *entry = NULL;
+ int error = is_delete ? -ENOENT : -ENOMEM;
+ if (!tomoyo_parse_name_union(filename1, &e.name1) ||
+ !tomoyo_parse_name_union(filename2, &e.name2))
+ goto out;
+ if (!is_delete)
+ entry = kmalloc(sizeof(e), GFP_KERNEL);
+ mutex_lock(&tomoyo_policy_lock);
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
+ struct tomoyo_path2_acl *acl =
+ container_of(ptr, struct tomoyo_path2_acl,
+ head);
+ if (ptr->type != TOMOYO_TYPE_PATH2_ACL ||
+ ptr->cond != condition ||
+ tomoyo_memcmp(acl, &e, offsetof(typeof(e), name1),
+ sizeof(e)))
+ continue;
+ if (is_delete) {
+ acl->perm &= ~perm;
+ if (!acl->perm)
+ ptr->is_deleted = true;
+ } else {
+ if (ptr->is_deleted)
+ acl->perm = 0;
+ acl->perm |= perm;
+ ptr->is_deleted = false;
+ }
+ error = 0;
+ break;
+ }
+ if (!is_delete && error && tomoyo_commit_ok(entry, &e, sizeof(e))) {
+ tomoyo_add_domain_acl(domain, &entry->head);
+ entry = NULL;
+ error = 0;
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+ out:
+ tomoyo_put_name_union(&e.name1);
+ tomoyo_put_name_union(&e.name2);
+ kfree(entry);
+ return error;
+}
+
+/**
+ * tomoyo_path2_acl - Check permission for double path operation.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ * @type: Type of operation.
+ * @filename1: First filename to check.
+ * @filename2: Second filename to check.
+ *
+ * Returns 0 on success, -EPERM otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static int tomoyo_path2_acl(struct tomoyo_request_info *r, const u8 type,
+ const struct tomoyo_path_info *filename1,
+ const struct tomoyo_path_info *filename2)
+{
+ const struct tomoyo_domain_info *domain = r->domain;
+ struct tomoyo_acl_info *ptr;
+ const u8 perm = 1 << type;
+ int error = -EPERM;
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
+ struct tomoyo_path2_acl *acl;
+ if (ptr->is_deleted || ptr->type != TOMOYO_TYPE_PATH2_ACL)
+ continue;
+ acl = container_of(ptr, struct tomoyo_path2_acl,
+ head);
+ if (!(acl->perm & perm) || !tomoyo_condition(r, ptr) ||
+ !tomoyo_compare_name_union(filename1, &acl->name1) ||
+ !tomoyo_compare_name_union(filename2, &acl->name2))
+ continue;
+ r->cond = ptr->cond;
+ error = 0;
+ break;
+ }
+ return error;
+}
+
+/**
+ * tomoyo_path_permission - Check permission for single path operation.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ * @operation: Type of operation.
+ * @filename: Filename to check.
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static int tomoyo_path_permission(struct tomoyo_request_info *r,
+ u8 operation,
+ const struct tomoyo_path_info *
+ filename)
+{
+ const char *msg;
+ int error;
+ repeat:
+ r->mode = tomoyo_get_mode(r->profile, tomoyo_p2mac[operation]);
+ if (r->mode == TOMOYO_CONFIG_DISABLED)
+ return 0;
+ do {
+ error = tomoyo_path_acl(r, filename, 1 << operation, 1);
+ msg = tomoyo_path2keyword(operation);
+ tomoyo_audit_path_log(r, msg, filename->name, !error);
+ if (!error)
+ break;
+ error = tomoyo_supervisor(r, "allow_%s %s\n", msg,
+ tomoyo_file_pattern(filename));
+ } while (error == 1);
+ if (r->mode != TOMOYO_CONFIG_ENFORCING)
+ error = 0;
+ /*
+ * Since "allow_truncate" doesn't imply "allow_rewrite" permission,
+ * we need to check "allow_rewrite" permission if the filename is
+ * specified by "deny_rewrite" keyword.
+ */
+ if (!error && operation == TOMOYO_TYPE_TRUNCATE &&
+ tomoyo_is_no_rewrite_file(filename)) {
+ operation = TOMOYO_TYPE_REWRITE;
+ goto repeat;
+ }
+ return error;
+}
+
+/**
+ * tomoyo_path_number3_perm2 - Check permission for mkdev operation.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ * @operation: Type of operation.
+ * @filename: Filename to check.
+ * @mode: Create mode.
+ * @dev: Device number.
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static int tomoyo_path_number3_perm2(struct tomoyo_request_info *r,
+ const u8 operation,
+ const struct tomoyo_path_info *filename,
+ const unsigned int mode,
+ const unsigned int dev)
+{
+ int error;
+ const char *msg = tomoyo_path_number32keyword(operation);
+ const unsigned int major = MAJOR(dev);
+ const unsigned int minor = MINOR(dev);
+ if (!r->mode)
+ return 0;
+ do {
+ error = tomoyo_path_number3_acl(r, filename, 1 << operation,
+ mode, major, minor);
+ tomoyo_audit_path_number3_log(r, msg, filename->name, mode,
+ major, minor, !error);
+ if (!error)
+ break;
+ error = tomoyo_supervisor(r, "allow_%s %s 0%o %u %u\n", msg,
+ tomoyo_file_pattern(filename), mode,
+ major, minor);
+ } while (error == 1);
+ if (r->mode != TOMOYO_CONFIG_ENFORCING)
+ error = 0;
+ return error;
+}
+
+/**
+ * tomoyo_exec_perm - Check permission for "execute".
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ * @filename: Check permission for "execute".
+ *
+ * Returns 0 on success, 1 on retry, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+int tomoyo_exec_perm(struct tomoyo_request_info *r,
+ const struct tomoyo_path_info *filename)
+{
+ if (r->mode == TOMOYO_CONFIG_DISABLED)
+ return 0;
+ return tomoyo_file_perm(r, filename, 1);
+}
+
+/**
+ * tomoyo_open_permission - Check permission for "read" and "write".
+ *
+ * @dentry: Pointer to "struct dentry".
+ * @mnt: Pointer to "struct vfsmount".
+ * @flag: Flags for open().
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_open_permission(struct dentry *dentry, struct vfsmount *mnt,
+ const int flag)
+{
+ struct tomoyo_request_info r;
+ struct tomoyo_obj_info obj = {
+ .path1.dentry = dentry,
+ .path1.mnt = mnt
+ };
+ struct task_struct * const task = current;
+ const u8 acc_mode = ACC_MODE(flag);
+ int error = 0;
+ struct tomoyo_path_info buf;
+ int idx;
+ if (task->in_execve &&
+ !(task->tomoyo_flags & TOMOYO_TASK_IS_IN_EXECVE))
+ return 0;
+ if (!mnt || (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)))
+ return 0;
+ buf.name = NULL;
+ r.mode = 0;
+ idx = tomoyo_read_lock();
+ /*
+ * If the filename is specified by "deny_rewrite" keyword,
+ * we need to check "allow_rewrite" permission when the filename is not
+ * opened for append mode or the filename is truncated at open time.
+ */
+ if ((acc_mode & MAY_WRITE) && !(flag & O_APPEND)
+ && tomoyo_init_request_info(&r, NULL, TOMOYO_MAC_FILE_REWRITE)
+ != TOMOYO_CONFIG_DISABLED) {
+ if (!tomoyo_get_realpath(&buf, dentry, mnt)) {
+ error = -ENOMEM;
+ goto out;
+ }
+ if (tomoyo_is_no_rewrite_file(&buf)) {
+ r.obj = &obj;
+ error = tomoyo_path_permission(&r, TOMOYO_TYPE_REWRITE,
+ &buf);
+ }
+ }
+ if (!error && acc_mode &&
+ tomoyo_init_request_info(&r, NULL, TOMOYO_MAC_FILE_OPEN)
+ != TOMOYO_CONFIG_DISABLED) {
+ if (!buf.name && !tomoyo_get_realpath(&buf, dentry, mnt)) {
+ error = -ENOMEM;
+ goto out;
+ }
+ r.obj = &obj;
+ error = tomoyo_file_perm(&r, &buf, acc_mode);
+ }
+ out:
+ kfree(buf.name);
+ tomoyo_read_unlock(idx);
+ if (r.mode != TOMOYO_CONFIG_ENFORCING)
+ error = 0;
+ return error;
+}
+
+/**
+ * tomoyo_path_perm - Check permission for "unlink", "rmdir", "truncate", "symlink", "chroot" and "unmount".
+ *
+ * @operation: Type of operation.
+ * @dentry: Pointer to "struct dentry".
+ * @mnt: Pointer to "struct vfsmount".
+ * @target: Symlink's target if @operation is TOMOYO_TYPE_SYMLINK.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_path_perm(const u8 operation, struct dentry *dentry,
+ struct vfsmount *mnt, const char *target)
+{
+ struct tomoyo_request_info r;
+ struct tomoyo_obj_info obj = {
+ .path1.dentry = dentry,
+ .path1.mnt = mnt
+ };
+ int error = -ENOMEM;
+ struct tomoyo_path_info buf;
+ bool is_enforce = false;
+ struct tomoyo_path_info symlink_target;
+ int idx;
+ buf.name = NULL;
+ symlink_target.name = NULL;
+ idx = tomoyo_read_lock();
+ if (!mnt || tomoyo_init_request_info(&r, NULL, tomoyo_p2mac[operation])
+ == TOMOYO_CONFIG_DISABLED) {
+ error = 0;
+ goto out;
+ }
+ is_enforce = (r.mode == TOMOYO_CONFIG_ENFORCING);
+ if (!tomoyo_get_realpath(&buf, dentry, mnt))
+ goto out;
+ r.obj = &obj;
+ switch (operation) {
+ case TOMOYO_TYPE_RMDIR:
+ case TOMOYO_TYPE_CHROOT:
+ case TOMOYO_TYPE_UMOUNT:
+ tomoyo_add_slash(&buf);
+ break;
+ case TOMOYO_TYPE_SYMLINK:
+ symlink_target.name = tomoyo_encode(target);
+ if (!symlink_target.name)
+ goto out;
+ tomoyo_fill_path_info(&symlink_target);
+ obj.symlink_target = &symlink_target;
+ break;
+ }
+ error = tomoyo_path_permission(&r, operation, &buf);
+ if (operation == TOMOYO_TYPE_SYMLINK)
+ kfree(symlink_target.name);
+ out:
+ kfree(buf.name);
+ tomoyo_read_unlock(idx);
+ if (!is_enforce)
+ error = 0;
+ return error;
+}
+
+/**
+ * tomoyo_path_number3_perm - Check permission for "mkblock" and "mkchar".
+ *
+ * @operation: Type of operation. (TOMOYO_TYPE_MKCHAR or TOMOYO_TYPE_MKBLOCK)
+ * @dentry: Pointer to "struct dentry".
+ * @mnt: Pointer to "struct vfsmount".
+ ` @mode: Create mode.
+ * @dev: Device number.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_path_number3_perm(const u8 operation, struct dentry *dentry,
+ struct vfsmount *mnt,
+ const unsigned int mode,
+ const unsigned int dev)
+{
+ struct tomoyo_request_info r;
+ struct tomoyo_obj_info obj = {
+ .path1.dentry = dentry,
+ .path1.mnt = mnt,
+ .dev = dev
+ };
+ int error = -ENOMEM;
+ struct tomoyo_path_info buf;
+ int idx;
+ buf.name = NULL;
+ idx = tomoyo_read_lock();
+ if (!mnt || tomoyo_init_request_info(&r, NULL,
+ tomoyo_pnnn2mac[operation])
+ == TOMOYO_CONFIG_DISABLED) {
+ error = 0;
+ goto out;
+ }
+ if (!tomoyo_get_realpath(&buf, dentry, mnt))
+ goto out;
+ r.obj = &obj;
+ error = tomoyo_path_number3_perm2(&r, operation, &buf, mode, dev);
+ out:
+ kfree(buf.name);
+ tomoyo_read_unlock(idx);
+ if (r.mode != TOMOYO_CONFIG_ENFORCING)
+ error = 0;
+ return error;
+}
+
+/**
+ * tomoyo_rewrite_permission - Check permission for "rewrite".
+ *
+ * @filp: Pointer to "struct file".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_rewrite_permission(struct file *filp)
+{
+ struct tomoyo_request_info r;
+ struct tomoyo_obj_info obj = {
+ .path1.dentry = filp->f_dentry,
+ .path1.mnt = filp->f_vfsmnt
+ };
+ int error = -ENOMEM;
+ bool is_enforce = false;
+ struct tomoyo_path_info buf;
+ int idx;
+ buf.name = NULL;
+ idx = tomoyo_read_lock();
+ if (!filp->f_vfsmnt ||
+ tomoyo_init_request_info(&r, NULL, TOMOYO_MAC_FILE_REWRITE)
+ == TOMOYO_CONFIG_DISABLED) {
+ error = 0;
+ goto out;
+ }
+ is_enforce = (r.mode == TOMOYO_CONFIG_ENFORCING);
+ if (!tomoyo_get_realpath(&buf, filp->f_dentry, filp->f_vfsmnt))
+ goto out;
+ if (!tomoyo_is_no_rewrite_file(&buf)) {
+ error = 0;
+ goto out;
+ }
+ r.obj = &obj;
+ error = tomoyo_path_permission(&r, TOMOYO_TYPE_REWRITE, &buf);
+ out:
+ kfree(buf.name);
+ tomoyo_read_unlock(idx);
+ if (!is_enforce)
+ error = 0;
+ return error;
+}
+
+/**
+ * tomoyo_path2_perm - Check permission for "rename", "link" and "pivot_root".
+ *
+ * @operation: Type of operation.
+ * @dentry1: Pointer to "struct dentry".
+ * @mnt1: Pointer to "struct vfsmount".
+ * @dentry2: Pointer to "struct dentry".
+ * @mnt2: Pointer to "struct vfsmount".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_path2_perm(const u8 operation, struct dentry *dentry1,
+ struct vfsmount *mnt1, struct dentry *dentry2,
+ struct vfsmount *mnt2)
+{
+ struct tomoyo_request_info r;
+ int error = -ENOMEM;
+ const char *msg = tomoyo_path22keyword(operation);
+ struct tomoyo_path_info buf1;
+ struct tomoyo_path_info buf2;
+ bool is_enforce = false;
+ struct tomoyo_obj_info obj = {
+ .path1.dentry = dentry1,
+ .path1.mnt = mnt1,
+ .path2.dentry = dentry2,
+ .path2.mnt = mnt2
+ };
+ int idx;
+ buf1.name = NULL;
+ buf2.name = NULL;
+ idx = tomoyo_read_lock();
+ if (!mnt1 || !mnt2 ||
+ tomoyo_init_request_info(&r, NULL, tomoyo_pp2mac[operation])
+ == TOMOYO_CONFIG_DISABLED) {
+ error = 0;
+ goto out;
+ }
+ is_enforce = (r.mode == TOMOYO_CONFIG_ENFORCING);
+ if (!tomoyo_get_realpath(&buf1, dentry1, mnt1) ||
+ !tomoyo_get_realpath(&buf2, dentry2, mnt2))
+ goto out;
+ switch (operation) {
+ case TOMOYO_TYPE_RENAME:
+ case TOMOYO_TYPE_LINK:
+ if (!dentry1->d_inode || !S_ISDIR(dentry1->d_inode->i_mode))
+ break;
+ /* fall through */
+ case TOMOYO_TYPE_PIVOT_ROOT:
+ tomoyo_add_slash(&buf1);
+ tomoyo_add_slash(&buf2);
+ }
+ r.obj = &obj;
+ do {
+ error = tomoyo_path2_acl(&r, operation, &buf1, &buf2);
+ tomoyo_audit_path2_log(&r, msg, buf1.name, buf2.name, !error);
+ if (!error)
+ break;
+ error = tomoyo_supervisor(&r, "allow_%s %s %s\n", msg,
+ tomoyo_file_pattern(&buf1),
+ tomoyo_file_pattern(&buf2));
+ } while (error == 1);
+ out:
+ kfree(buf1.name);
+ kfree(buf2.name);
+ tomoyo_read_unlock(idx);
+ if (!is_enforce)
+ error = 0;
+ return error;
+}
+
+/**
+ * tomoyo_update_path_number_acl - Update ioctl/chmod/chown/chgrp ACL.
+ *
+ * @type: Type of operation.
+ * @filename: Filename.
+ * @number: Number.
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @condition: Pointer to "struct tomoyo_condition". May be NULL.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static inline int tomoyo_update_path_number_acl
+(const u8 type, const char *filename, char *number,
+ struct tomoyo_domain_info * const domain, struct tomoyo_condition *condition,
+ const bool is_delete)
+{
+ const u8 perm = 1 << type;
+ struct tomoyo_acl_info *ptr;
+ struct tomoyo_path_number_acl e = {
+ .head.type = TOMOYO_TYPE_PATH_NUMBER_ACL,
+ .head.cond = condition,
+ .perm = perm
+ };
+ struct tomoyo_path_number_acl *entry = NULL;
+ int error = is_delete ? -ENOENT : -ENOMEM;
+ if (!domain)
+ return -EINVAL;
+ if (!tomoyo_parse_name_union(filename, &e.name))
+ return -EINVAL;
+ if (!tomoyo_parse_number_union(number, &e.number))
+ goto out;
+ if (!is_delete)
+ entry = kmalloc(sizeof(e), GFP_KERNEL);
+ mutex_lock(&tomoyo_policy_lock);
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
+ struct tomoyo_path_number_acl *acl =
+ container_of(ptr, struct tomoyo_path_number_acl,
+ head);
+ if (ptr->type != TOMOYO_TYPE_PATH_NUMBER_ACL ||
+ ptr->cond != condition ||
+ tomoyo_memcmp(acl, &e, offsetof(typeof(e), name),
+ sizeof(e)))
+ continue;
+ if (is_delete) {
+ acl->perm &= ~perm;
+ if (!acl->perm)
+ ptr->is_deleted = true;
+ } else {
+ if (ptr->is_deleted)
+ acl->perm = 0;
+ acl->perm |= perm;
+ ptr->is_deleted = false;
+ }
+ error = 0;
+ break;
+ }
+ if (!is_delete && error && tomoyo_commit_ok(entry, &e, sizeof(e))) {
+ tomoyo_add_domain_acl(domain, &entry->head);
+ entry = NULL;
+ error = 0;
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+ out:
+ tomoyo_put_name_union(&e.name);
+ tomoyo_put_number_union(&e.number);
+ kfree(entry);
+ return error;
+}
+
+/**
+ * tomoyo_path_number_acl - Check permission for ioctl/chmod/chown/chgrp operation.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ * @type: Operation.
+ * @filename: Filename to check.
+ * @number: Number.
+ *
+ * Returns 0 on success, -EPERM otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static int tomoyo_path_number_acl(struct tomoyo_request_info *r, const u8 type,
+ const struct tomoyo_path_info *filename,
+ const unsigned long number)
+{
+ struct tomoyo_domain_info *domain = r->domain;
+ struct tomoyo_acl_info *ptr;
+ const u8 perm = 1 << type;
+ int error = -EPERM;
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
+ struct tomoyo_path_number_acl *acl;
+ if (ptr->is_deleted ||
+ ptr->type != TOMOYO_TYPE_PATH_NUMBER_ACL)
+ continue;
+ acl = container_of(ptr, struct tomoyo_path_number_acl,
+ head);
+ if (!(acl->perm & perm) || !tomoyo_condition(r, ptr) ||
+ !tomoyo_compare_number_union(number, &acl->number) ||
+ !tomoyo_compare_name_union(filename, &acl->name))
+ continue;
+ r->cond = ptr->cond;
+ error = 0;
+ break;
+ }
+ return error;
+}
+
+/**
+ * tomoyo_path_number_perm2 - Check permission for "create", "mkdir", "mkfifo", "mksock", "ioctl", "chmod", "chown", "chgrp".
+ *
+ * @r: Pointer to "strct tomoyo_request_info".
+ * @filename: Filename to check.
+ * @numr: Number.
+ *
+ * Returns 0 on success, 1 on retry, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static int tomoyo_path_number_perm2(struct tomoyo_request_info *r,
+ const u8 type,
+ const struct tomoyo_path_info *filename,
+ const unsigned long number)
+{
+ char buffer[64];
+ int error;
+ u8 radix;
+ const char *msg = tomoyo_path_number2keyword(type);
+ if (!filename)
+ return 0;
+ switch (type) {
+ case TOMOYO_TYPE_CREATE:
+ case TOMOYO_TYPE_MKDIR:
+ case TOMOYO_TYPE_MKFIFO:
+ case TOMOYO_TYPE_MKSOCK:
+ case TOMOYO_TYPE_CHMOD:
+ radix = TOMOYO_VALUE_TYPE_OCTAL;
+ break;
+ case TOMOYO_TYPE_IOCTL:
+ radix = TOMOYO_VALUE_TYPE_HEXADECIMAL;
+ break;
+ default:
+ radix = TOMOYO_VALUE_TYPE_DECIMAL;
+ break;
+ }
+ tomoyo_print_ulong(buffer, sizeof(buffer), number, radix);
+ do {
+ error = tomoyo_path_number_acl(r, type, filename, number);
+ tomoyo_audit_path_number_log(r, msg, filename->name, buffer,
+ !error);
+ if (!error)
+ return 0;
+ error = tomoyo_supervisor(r, "allow_%s %s %s\n", msg,
+ tomoyo_file_pattern(filename),
+ buffer);
+ } while (error == 1);
+ if (r->mode != TOMOYO_CONFIG_ENFORCING)
+ error = 0;
+ return error;
+}
+
+/**
+ * tomoyo_path_number_perm - Check permission for "create", "mkdir", "mkfifo", "mksock", "ioctl", "chmod", "chown", "chgrp".
+ *
+ * @dentry: Pointer to "struct dentry".
+ * @vfsmnt: Pointer to "struct vfsmount".
+ * @number: Number.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_path_number_perm(const u8 type, struct dentry *dentry,
+ struct vfsmount *vfsmnt,
+ unsigned long number)
+{
+ struct tomoyo_request_info r;
+ struct tomoyo_obj_info obj = {
+ .path1.dentry = dentry,
+ .path1.mnt = vfsmnt
+ };
+ int error = -ENOMEM;
+ struct tomoyo_path_info buf;
+ int idx;
+ if (!vfsmnt || !dentry)
+ return 0;
+ buf.name = NULL;
+ idx = tomoyo_read_lock();
+ if (tomoyo_init_request_info(&r, NULL, tomoyo_pn2mac[type])
+ == TOMOYO_CONFIG_DISABLED) {
+ error = 0;
+ goto out;
+ }
+ if (!tomoyo_get_realpath(&buf, dentry, vfsmnt))
+ goto out;
+ r.obj = &obj;
+ if (type == TOMOYO_TYPE_MKDIR)
+ tomoyo_add_slash(&buf);
+ error = tomoyo_path_number_perm2(&r, type, &buf, number);
+ out:
+ kfree(buf.name);
+ tomoyo_read_unlock(idx);
+ if (r.mode != TOMOYO_CONFIG_ENFORCING)
+ error = 0;
+ return error;
+}
+
+/**
+ * tomoyo_ioctl_permission - Check permission for "ioctl".
+ *
+ * @file: Pointer to "struct file".
+ * @cmd: Ioctl command number.
+ * @arg: Param for @cmd .
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_ioctl_permission(struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ int error = tomoyo_path_number_perm(TOMOYO_TYPE_IOCTL, filp->f_dentry,
+ filp->f_vfsmnt, cmd);
+ if (error)
+ goto out;
+ switch (cmd) {
+ case FIOCLEX:
+ case FIONCLEX:
+ case FIONBIO:
+ case FIOASYNC:
+ case FIOQSIZE:
+ case FIFREEZE:
+ case FITHAW:
+ case FS_IOC_FIEMAP:
+ case FIGETBSZ:
+ goto out;
+ }
+ if (S_ISREG(filp->f_path.dentry->d_inode->i_mode)) {
+ switch (cmd) {
+ case FIBMAP:
+ case FIONREAD:
+ case FS_IOC_RESVSP:
+ case FS_IOC_RESVSP64:
+ goto out;
+ }
+ }
+ /* Check permission to call vfs_ioctl(). */
+ if (!tomoyo_capable(TOMOYO_SYS_IOCTL))
+ error = -EPERM;
+ out:
+ return error;
+}
+
+/**
+ * tomoyo_chmod_permission - Check permission for "chmod".
+ *
+ * @dentry: Pointer to "struct dentry".
+ * @vfsmnt: Pointer to "struct vfsmount".
+ * @mode: Mode.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_chmod_permission(struct dentry *dentry, struct vfsmount *vfsmnt,
+ mode_t mode)
+{
+ if (mode == (mode_t) -1)
+ return 0;
+ if (!tomoyo_capable(TOMOYO_SYS_CHMOD))
+ return -EPERM;
+ return tomoyo_path_number_perm(TOMOYO_TYPE_CHMOD, dentry, vfsmnt,
+ mode & S_IALLUGO);
+}
+
+/**
+ * tomoyo_chown_permission - Check permission for "chown/chgrp".
+ *
+ * @dentry: Pointer to "struct dentry".
+ * @vfsmnt: Pointer to "struct vfsmount".
+ * @user: User ID.
+ * @group: Group ID.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_chown_permission(struct dentry *dentry, struct vfsmount *vfsmnt,
+ uid_t user, gid_t group)
+{
+ int error = 0;
+ if (user == (uid_t) -1 && group == (gid_t) -1)
+ return 0;
+ if (!tomoyo_capable(TOMOYO_SYS_CHOWN))
+ return -EPERM;
+ if (user != (uid_t) -1)
+ error = tomoyo_path_number_perm(TOMOYO_TYPE_CHOWN, dentry,
+ vfsmnt, user);
+ if (!error && group != (gid_t) -1)
+ error = tomoyo_path_number_perm(TOMOYO_TYPE_CHGRP, dentry,
+ vfsmnt, group);
+ return error;
+}
+
+/**
+ * tomoyo_pivot_root_permission - Check permission for pivot_root().
+ *
+ * @old_path: Pointer to "struct path".
+ * @new_path: Pointer to "struct path".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_pivot_root_permission(struct path *old_path, struct path*new_path)
+{
+ if (!tomoyo_capable(TOMOYO_SYS_PIVOT_ROOT))
+ return -EPERM;
+ return tomoyo_path2_perm(TOMOYO_TYPE_PIVOT_ROOT, new_path->dentry,
+ new_path->mnt, old_path->dentry,
+ old_path->mnt);
+}
+
+/**
+ * tomoyo_chroot_permission - Check permission for chroot().
+ *
+ * @path: Pointer to "struct path".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_chroot_permission(struct path *path)
+{
+ if (!tomoyo_capable(TOMOYO_SYS_CHROOT))
+ return -EPERM;
+ return tomoyo_path_perm(TOMOYO_TYPE_CHROOT, path->dentry, path->mnt,
+ NULL);
+}
+
+/**
+ * tomoyo_umount_permission - Check permission for unmount.
+ *
+ * @mnt: Pointer to "struct vfsmount".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_umount_permission(struct vfsmount *mnt)
+{
+ if (!tomoyo_capable(TOMOYO_SYS_UMOUNT))
+ return -EPERM;
+ return tomoyo_path_perm(TOMOYO_TYPE_UMOUNT, mnt->mnt_root, mnt, NULL);
+}
+
+/**
+ * tomoyo_write_file_policy - Update file related list.
+ *
+ * @data: String to parse.
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @condition: Pointer to "struct tomoyo_condition". May be NULL.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_write_file_policy(char *data, struct tomoyo_domain_info *domain,
+ struct tomoyo_condition *condition,
+ const bool is_delete)
+{
+ char *w[5];
+ unsigned int perm;
+ u8 type;
+ if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[1][0])
+ return -EINVAL;
+ if (strncmp(w[0], "allow_", 6)) {
+ if (sscanf(w[0], "%u", &perm) == 1)
+ return tomoyo_update_file_acl((u8) perm, w[1], domain,
+ condition, is_delete);
+ if (!strcmp(w[0], TOMOYO_KEYWORD_EXECUTE_HANDLER))
+ type = TOMOYO_TYPE_EXECUTE_HANDLER;
+ else if (!strcmp(w[0], TOMOYO_KEYWORD_DENIED_EXECUTE_HANDLER))
+ type = TOMOYO_TYPE_DENIED_EXECUTE_HANDLER;
+ else
+ goto out;
+ return tomoyo_update_execute_handler(type, w[1], domain,
+ is_delete);
+ }
+ w[0] += 6;
+ for (type = 0; type < TOMOYO_MAX_PATH_OPERATION; type++) {
+ if (strcmp(w[0], tomoyo_path_keyword[type]))
+ continue;
+ return tomoyo_update_path_acl(type, w[1], domain, condition,
+ is_delete);
+ }
+ if (!w[2][0])
+ goto out;
+ for (type = 0; type < TOMOYO_MAX_PATH2_OPERATION; type++) {
+ if (strcmp(w[0], tomoyo_path2_keyword[type]))
+ continue;
+ return tomoyo_update_path2_acl(type, w[1], w[2], domain,
+ condition, is_delete);
+ }
+ for (type = 0; type < TOMOYO_MAX_PATH_NUMBER_OPERATION; type++) {
+ if (strcmp(w[0], tomoyo_path_number_keyword[type]))
+ continue;
+ return tomoyo_update_path_number_acl(type, w[1], w[2], domain,
+ condition, is_delete);
+ }
+ if (!w[3][0] || !w[4][0])
+ goto out;
+ for (type = 0; type < TOMOYO_MAX_PATH_NUMBER3_OPERATION; type++) {
+ if (strcmp(w[0], tomoyo_path_number3_keyword[type]))
+ continue;
+ return tomoyo_update_path_number3_acl(type, w[1], w[2], w[3],
+ w[4], domain, condition,
+ is_delete);
+ }
+ out:
+ return -EINVAL;
+}
+
+/*
+ * Below part contains copy of some of VFS helper functions.
+ *
+ * Since TOMOYO Linux requires "struct vfsmount" parameter to calculate
+ * an absolute pathname of the requested "struct dentry" parameter
+ * but the VFS helper functions don't receive "struct vfsmount" parameter,
+ * TOMOYO Linux checks permission outside VFS helper functions.
+ * To keep the DAC's permission checks are performed before the
+ * TOMOYO Linux's permission checks are performed, I'd like to call
+ * may_create() and may_delete() from LSM.
+ */
+
+static inline int tomoyo_may_create(struct inode *dir, struct dentry *dentry)
+{
+ /* return may_create(dir, dentry); */
+ return 0;
+}
+
+static inline int tomoyo_may_delete(struct inode *dir, struct dentry *dentry,
+ int is_dir)
+{
+ /* return may_delete(dir, dentry, is_dir); */
+ return 0;
+}
+
+/* Permission checks from vfs_create(). */
+static int tomoyo_pre_vfs_create(struct inode *dir, struct dentry *dentry)
+{
+ int error = tomoyo_may_create(dir, dentry);
+ if (error)
+ return error;
+ if (!dir->i_op || !dir->i_op->create)
+ return -EACCES; /* shouldn't it be ENOSYS? */
+ return 0;
+}
+
+/* Permission checks from vfs_mknod(). */
+static int tomoyo_pre_vfs_mknod(struct inode *dir, struct dentry *dentry,
+ int mode)
+{
+ int error = tomoyo_may_create(dir, dentry);
+ if (error)
+ return error;
+ if ((S_ISCHR(mode) || S_ISBLK(mode)) && !capable(CAP_MKNOD))
+ return -EPERM;
+ if (!dir->i_op || !dir->i_op->mknod)
+ return -EPERM;
+ return 0;
+}
+
+/* Permission checks from vfs_mkdir(). */
+static int tomoyo_pre_vfs_mkdir(struct inode *dir, struct dentry *dentry)
+{
+ int error = tomoyo_may_create(dir, dentry);
+ if (error)
+ return error;
+ if (!dir->i_op || !dir->i_op->mkdir)
+ return -EPERM;
+ return 0;
+}
+
+/* Permission checks from vfs_rmdir(). */
+static int tomoyo_pre_vfs_rmdir(struct inode *dir, struct dentry *dentry)
+{
+ int error = tomoyo_may_delete(dir, dentry, 1);
+ if (error)
+ return error;
+ if (!dir->i_op || !dir->i_op->rmdir)
+ return -EPERM;
+ return 0;
+}
+
+/* Permission checks from vfs_unlink(). */
+static int tomoyo_pre_vfs_unlink(struct inode *dir, struct dentry *dentry)
+{
+ int error = tomoyo_may_delete(dir, dentry, 0);
+ if (error)
+ return error;
+ if (!dir->i_op || !dir->i_op->unlink)
+ return -EPERM;
+ return 0;
+}
+
+/* Permission checks from vfs_link(). */
+static int tomoyo_pre_vfs_link(struct dentry *old_dentry, struct inode *dir,
+ struct dentry *new_dentry)
+{
+ struct inode *inode = old_dentry->d_inode;
+ int error;
+ if (!inode)
+ return -ENOENT;
+ error = tomoyo_may_create(dir, new_dentry);
+ if (error)
+ return error;
+ if (dir->i_sb != inode->i_sb)
+ return -EXDEV;
+ if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
+ return -EPERM;
+ if (!dir->i_op || !dir->i_op->link)
+ return -EPERM;
+ if (S_ISDIR(old_dentry->d_inode->i_mode))
+ return -EPERM;
+ return 0;
+}
+
+/* Permission checks from vfs_symlink(). */
+static int tomoyo_pre_vfs_symlink(struct inode *dir, struct dentry *dentry)
+{
+ int error = tomoyo_may_create(dir, dentry);
+ if (error)
+ return error;
+ if (!dir->i_op || !dir->i_op->symlink)
+ return -EPERM;
+ return 0;
+}
+
+/* Permission checks from vfs_rename(). */
+static int tomoyo_pre_vfs_rename(struct inode *old_dir,
+ struct dentry *old_dentry,
+ struct inode *new_dir,
+ struct dentry *new_dentry)
+{
+ int error;
+ const int is_dir = S_ISDIR(old_dentry->d_inode->i_mode);
+ if (old_dentry->d_inode == new_dentry->d_inode)
+ return 0;
+ error = tomoyo_may_delete(old_dir, old_dentry, is_dir);
+ if (error)
+ return error;
+ if (!new_dentry->d_inode)
+ error = tomoyo_may_create(new_dir, new_dentry);
+ else
+ error = tomoyo_may_delete(new_dir, new_dentry, is_dir);
+ if (error)
+ return error;
+ if (!old_dir->i_op || !old_dir->i_op->rename)
+ return -EPERM;
+ if (is_dir && new_dir != old_dir)
+ error = inode_permission(old_dentry->d_inode, MAY_WRITE);
+ return error;
+}
+
+/* Permission checks from vfs_mknod(). */
+int tomoyo_mknod_permission(struct inode *dir, struct dentry *dentry,
+ struct vfsmount *mnt, const unsigned int mode,
+ unsigned int dev)
+{
+ int error;
+ if (S_ISCHR(mode) && !tomoyo_capable(TOMOYO_CREATE_CHAR_DEV))
+ return -EPERM;
+ if (S_ISBLK(mode) && !tomoyo_capable(TOMOYO_CREATE_BLOCK_DEV))
+ return -EPERM;
+ if (S_ISFIFO(mode) && !tomoyo_capable(TOMOYO_CREATE_FIFO))
+ return -EPERM;
+ if (S_ISSOCK(mode) && !tomoyo_capable(TOMOYO_CREATE_UNIX_SOCKET))
+ return -EPERM;
+ switch (mode & S_IFMT) {
+ case 0:
+ case S_IFREG:
+ error = tomoyo_pre_vfs_create(dir, dentry);
+ if (!error)
+ error = tomoyo_path_number_perm(TOMOYO_TYPE_CREATE,
+ dentry, mnt,
+ mode & S_IALLUGO);
+ return error;
+ }
+ error = tomoyo_pre_vfs_mknod(dir, dentry, mode);
+ dev = new_decode_dev(dev);
+ if (error)
+ return error;
+ switch (mode & S_IFMT) {
+ case S_IFCHR:
+ error = tomoyo_path_number3_perm(TOMOYO_TYPE_MKCHAR, dentry,
+ mnt, mode & S_IALLUGO, dev);
+ break;
+ case S_IFBLK:
+ error = tomoyo_path_number3_perm(TOMOYO_TYPE_MKBLOCK, dentry,
+ mnt, mode & S_IALLUGO, dev);
+ break;
+ case S_IFIFO:
+ error = tomoyo_path_number_perm(TOMOYO_TYPE_MKFIFO, dentry,
+ mnt, mode & S_IALLUGO);
+ break;
+ case S_IFSOCK:
+ error = tomoyo_path_number_perm(TOMOYO_TYPE_MKSOCK, dentry,
+ mnt, mode & S_IALLUGO);
+ break;
+ }
+ return error;
+}
+
+/* Permission checks for vfs_mkdir(). */
+int tomoyo_mkdir_permission(struct inode *dir, struct dentry *dentry,
+ struct vfsmount *mnt, unsigned int mode)
+{
+ int error = tomoyo_pre_vfs_mkdir(dir, dentry);
+ if (!error)
+ error = tomoyo_path_number_perm(TOMOYO_TYPE_MKDIR, dentry, mnt,
+ mode);
+ return error;
+}
+
+/* Permission checks for vfs_rmdir(). */
+int tomoyo_rmdir_permission(struct inode *dir, struct dentry *dentry,
+ struct vfsmount *mnt)
+{
+ int error = tomoyo_pre_vfs_rmdir(dir, dentry);
+ if (!error)
+ error = tomoyo_path_perm(TOMOYO_TYPE_RMDIR, dentry, mnt,
+ NULL);
+ return error;
+}
+
+/* Permission checks for vfs_unlink(). */
+int tomoyo_unlink_permission(struct inode *dir, struct dentry *dentry,
+ struct vfsmount *mnt)
+{
+ int error;
+ if (!tomoyo_capable(TOMOYO_SYS_UNLINK))
+ return -EPERM;
+ error = tomoyo_pre_vfs_unlink(dir, dentry);
+ if (!error)
+ error = tomoyo_path_perm(TOMOYO_TYPE_UNLINK, dentry, mnt,
+ NULL);
+ return error;
+}
+
+/* Permission checks for vfs_symlink(). */
+int tomoyo_symlink_permission(struct inode *dir, struct dentry *dentry,
+ struct vfsmount *mnt, const char *from)
+{
+ int error;
+ if (!tomoyo_capable(TOMOYO_SYS_SYMLINK))
+ return -EPERM;
+ error = tomoyo_pre_vfs_symlink(dir, dentry);
+ if (!error)
+ error = tomoyo_path_perm(TOMOYO_TYPE_SYMLINK, dentry, mnt,
+ from);
+ return error;
+}
+
+/* Permission checks for notify_change(). */
+int tomoyo_truncate_permission(struct dentry *dentry, struct vfsmount *mnt,
+ loff_t length, unsigned int time_attrs)
+{
+ return tomoyo_path_perm(TOMOYO_TYPE_TRUNCATE, dentry, mnt, NULL);
+}
+
+/* Permission checks for vfs_rename(). */
+int tomoyo_rename_permission(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry,
+ struct vfsmount *mnt)
+{
+ int error;
+ if (!tomoyo_capable(TOMOYO_SYS_RENAME))
+ return -EPERM;
+ error = tomoyo_pre_vfs_rename(old_dir, old_dentry, new_dir,
+ new_dentry);
+ if (!error)
+ error = tomoyo_path2_perm(TOMOYO_TYPE_RENAME, old_dentry, mnt,
+ new_dentry, mnt);
+ return error;
+}
+
+/* Permission checks for vfs_link(). */
+int tomoyo_link_permission(struct dentry *old_dentry, struct inode *new_dir,
+ struct dentry *new_dentry, struct vfsmount *mnt)
+{
+ int error;
+ if (!tomoyo_capable(TOMOYO_SYS_LINK))
+ return -EPERM;
+ error = tomoyo_pre_vfs_link(old_dentry, new_dir, new_dentry);
+ if (!error)
+ error = tomoyo_path2_perm(TOMOYO_TYPE_LINK, old_dentry, mnt,
+ new_dentry, mnt);
+ return error;
+}
--
^ permalink raw reply [flat|nested] 50+ messages in thread
* [TOMOYO #16 24/25] TOMOYO: Add domain transition handler.
2009-10-04 12:49 [TOMOYO #16 00/25] Starting TOMOYO 2.3 Tetsuo Handa
` (22 preceding siblings ...)
2009-10-04 12:50 ` [TOMOYO #16 23/25] TOMOYO: Add file access restriction Tetsuo Handa
@ 2009-10-04 12:50 ` Tetsuo Handa
2009-10-04 12:50 ` [TOMOYO #16 25/25] TOMOYO: Update Kconfig and Makefile Tetsuo Handa
2009-10-06 9:39 ` [TOMOYO #16 00/25] Starting TOMOYO 2.3 Pavel Machek
25 siblings, 0 replies; 50+ messages in thread
From: Tetsuo Handa @ 2009-10-04 12:50 UTC (permalink / raw)
To: linux-security-module; +Cc: linux-kernel, Tetsuo Handa
[-- Attachment #1: tomoyo-add-new-domain.patch --]
[-- Type: text/plain, Size: 39550 bytes --]
This patch contains code for handling domain transition.
tomoyo_read_lock()/tomoyo_read_unlock() protects the data against the garbage
collector. I call tomoyo_read_lock() when an execve() operation starts and
call tomoyo_read_unlock() when an execve() operation finishes rather than
calling tomoyo_read_lock()/tomoyo_read_unlock() upon individual list traversal.
In this way, the pointer saved in "struct tomoyo_execve_entry" is guaranteed
to be valid. Please ignore
warning: context imbalance in 'tomoyo_start_execve' - wrong count at exit
warning: context imbalance in 'tomoyo_finish_execve' - unexpected unlock
messages when built with "C=1" option.
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
security/tomoyo/new-domain.c | 1354 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 1354 insertions(+)
--- /dev/null
+++ security-testing-2.6/security/tomoyo/new-domain.c
@@ -0,0 +1,1354 @@
+/*
+ * security/tomoyo/domain.c
+ *
+ * Copyright (C) 2005-2009 NTT DATA CORPORATION
+ */
+#include "internal.h"
+#include <linux/mount.h>
+#include <linux/highmem.h>
+#include <linux/fs_struct.h>
+#include <linux/file.h>
+
+/* Variables definitions.*/
+
+/* The initial domain. */
+struct tomoyo_domain_info tomoyo_kernel_domain;
+
+/*
+ * tomoyo_domain_list is used for holding list of domains.
+ * The ->acl_info_list of "struct tomoyo_domain_info" is used for holding
+ * permissions (e.g. "allow_read /lib/libc-2.5.so") given to each domain.
+ *
+ * An entry is added by
+ *
+ * # ( echo "<kernel>"; echo "allow_execute /sbin/init" ) > \
+ * /sys/kernel/security/tomoyo/domain_policy
+ *
+ * and is deleted by
+ *
+ * # ( echo "<kernel>"; echo "delete allow_execute /sbin/init" ) > \
+ * /sys/kernel/security/tomoyo/domain_policy
+ *
+ * and all entries are retrieved by
+ *
+ * # cat /sys/kernel/security/tomoyo/domain_policy
+ *
+ * A domain is added by
+ *
+ * # echo "<kernel>" > /sys/kernel/security/tomoyo/domain_policy
+ *
+ * and is deleted by
+ *
+ * # echo "delete <kernel>" > /sys/kernel/security/tomoyo/domain_policy
+ *
+ * and all domains are retrieved by
+ *
+ * # grep '^<kernel>' /sys/kernel/security/tomoyo/domain_policy
+ *
+ * Normally, a domainname is monotonically getting longer because a domainname
+ * which the process will belong to if an execve() operation succeeds is
+ * defined as a concatenation of "current domainname" + "pathname passed to
+ * execve()".
+ * See tomoyo_domain_initializer_list and tomoyo_domain_keeper_list for
+ * exceptions.
+ */
+LIST_HEAD(tomoyo_domain_list);
+
+/**
+ * tomoyo_audit_execute_handler_log - Audit execute_handler log.
+ *
+ * @ee: Pointer to "struct tomoyo_execve_entry".
+ * @is_default: True if it is "execute_handler" log.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_audit_execute_handler_log(struct tomoyo_execve_entry *ee,
+ const bool is_default)
+{
+ struct tomoyo_request_info *r = &ee->r;
+ const char *handler = ee->handler->name;
+ r->mode = tomoyo_get_mode(r->profile, TOMOYO_MAC_FILE_EXECUTE);
+ return tomoyo_write_audit_log(true, r, "%s %s\n",
+ is_default ?
+ TOMOYO_KEYWORD_EXECUTE_HANDLER :
+ TOMOYO_KEYWORD_DENIED_EXECUTE_HANDLER,
+ handler);
+}
+
+/**
+ * tomoyo_audit_domain_creation_log - Audit domain creation log.
+ *
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_audit_domain_creation_log(struct tomoyo_domain_info *domain)
+{
+ int error;
+ struct tomoyo_request_info r;
+ tomoyo_init_request_info(&r, domain, TOMOYO_MAC_FILE_EXECUTE);
+ error = tomoyo_write_audit_log(false, &r, "use_profile %u\n",
+ r.profile);
+ return error;
+}
+
+/*
+ * tomoyo_domain_initializer_list is used for holding list of programs which
+ * triggers reinitialization of domainname. Normally, a domainname is
+ * monotonically getting longer. But sometimes, we restart daemon programs.
+ * It would be convenient for us that "a daemon started upon system boot" and
+ * "the daemon restarted from console" belong to the same domain. Thus, TOMOYO
+ * provides a way to shorten domainnames.
+ *
+ * An entry is added by
+ *
+ * # echo 'initialize_domain /usr/sbin/httpd' > \
+ * /sys/kernel/security/tomoyo/exception_policy
+ *
+ * and is deleted by
+ *
+ * # echo 'delete initialize_domain /usr/sbin/httpd' > \
+ * /sys/kernel/security/tomoyo/exception_policy
+ *
+ * and all entries are retrieved by
+ *
+ * # grep ^initialize_domain /sys/kernel/security/tomoyo/exception_policy
+ *
+ * In the example above, /usr/sbin/httpd will belong to
+ * "<kernel> /usr/sbin/httpd" domain.
+ *
+ * You may specify a domainname using "from" keyword.
+ * "initialize_domain /usr/sbin/httpd from <kernel> /etc/rc.d/init.d/httpd"
+ * will cause "/usr/sbin/httpd" executed from "<kernel> /etc/rc.d/init.d/httpd"
+ * domain to belong to "<kernel> /usr/sbin/httpd" domain.
+ *
+ * You may add "no_" prefix to "initialize_domain".
+ * "initialize_domain /usr/sbin/httpd" and
+ * "no_initialize_domain /usr/sbin/httpd from <kernel> /etc/rc.d/init.d/httpd"
+ * will cause "/usr/sbin/httpd" to belong to "<kernel> /usr/sbin/httpd" domain
+ * unless executed from "<kernel> /etc/rc.d/init.d/httpd" domain.
+ */
+LIST_HEAD(tomoyo_domain_initializer_list);
+
+/**
+ * tomoyo_update_domain_initializer_entry - Update "struct tomoyo_domain_initializer_entry" list.
+ *
+ * @domainname: The name of domain. May be NULL.
+ * @program: The name of program.
+ * @is_not: True if it is "no_initialize_domain" entry.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_update_domain_initializer_entry(const char *domainname,
+ const char *program,
+ const bool is_not,
+ const bool is_delete)
+{
+ struct tomoyo_domain_initializer_entry *entry = NULL;
+ struct tomoyo_domain_initializer_entry *ptr;
+ struct tomoyo_domain_initializer_entry e = { .is_not = is_not };
+ int error = is_delete ? -ENOENT : -ENOMEM;
+ if (!tomoyo_is_correct_path(program, 1, -1, -1))
+ return -EINVAL; /* No patterns allowed. */
+ if (domainname) {
+ if (!tomoyo_is_domain_def(domainname) &&
+ tomoyo_is_correct_path(domainname, 1, -1, -1))
+ e.is_last_name = true;
+ else if (!tomoyo_is_correct_domain(domainname))
+ return -EINVAL;
+ e.domainname = tomoyo_get_name(domainname);
+ if (!e.domainname)
+ goto out;
+ }
+ e.program = tomoyo_get_name(program);
+ if (!e.program)
+ goto out;
+ if (!is_delete)
+ entry = kmalloc(sizeof(e), GFP_KERNEL);
+ mutex_lock(&tomoyo_policy_lock);
+ list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, list) {
+ if (tomoyo_memcmp(ptr, &e, offsetof(typeof(e), is_not),
+ sizeof(e)))
+ continue;
+ ptr->is_deleted = is_delete;
+ error = 0;
+ break;
+ }
+ if (!is_delete && error && tomoyo_commit_ok(entry, &e, sizeof(e))) {
+ list_add_tail_rcu(&entry->list,
+ &tomoyo_domain_initializer_list);
+ entry = NULL;
+ error = 0;
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+ out:
+ tomoyo_put_name(e.domainname);
+ tomoyo_put_name(e.program);
+ kfree(entry);
+ return error;
+}
+
+/**
+ * tomoyo_read_domain_initializer_policy - Read "struct tomoyo_domain_initializer_entry" list.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head)
+{
+ struct list_head *pos;
+ bool done = true;
+ list_for_each_cookie(pos, head->read_var2,
+ &tomoyo_domain_initializer_list) {
+ const char *no;
+ const char *from = "";
+ const char *domain = "";
+ struct tomoyo_domain_initializer_entry *ptr;
+ ptr = list_entry(pos, struct tomoyo_domain_initializer_entry,
+ list);
+ if (ptr->is_deleted)
+ continue;
+ no = ptr->is_not ? "no_" : "";
+ if (ptr->domainname) {
+ from = " from ";
+ domain = ptr->domainname->name;
+ }
+ done = tomoyo_io_printf(head, "%s"
+ TOMOYO_KEYWORD_INITIALIZE_DOMAIN
+ "%s%s%s\n", no, ptr->program->name,
+ from, domain);
+ if (!done)
+ break;
+ }
+ return done;
+}
+
+/**
+ * tomoyo_write_domain_initializer_policy - Write "struct tomoyo_domain_initializer_entry" list.
+ *
+ * @data: String to parse.
+ * @is_not: True if it is "no_initialize_domain" entry.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_write_domain_initializer_policy(char *data, const bool is_not,
+ const bool is_delete)
+{
+ char *cp = strstr(data, " from ");
+ if (cp) {
+ *cp = '\0';
+ return tomoyo_update_domain_initializer_entry(cp + 6, data,
+ is_not,
+ is_delete);
+ }
+ return tomoyo_update_domain_initializer_entry(NULL, data, is_not,
+ is_delete);
+}
+
+/**
+ * tomoyo_is_domain_initializer - Check whether the given program causes domainname reinitialization.
+ *
+ * @domainname: The name of domain.
+ * @program: The name of program.
+ * @last_name: The last component of @domainname.
+ *
+ * Returns true if executing @program reinitializes domain transition,
+ * false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static bool tomoyo_is_domain_initializer
+(const struct tomoyo_path_info *domainname,
+ const struct tomoyo_path_info *program,
+ const struct tomoyo_path_info *last_name)
+{
+ struct tomoyo_domain_initializer_entry *ptr;
+ bool flag = false;
+ list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, list) {
+ if (ptr->is_deleted)
+ continue;
+ if (ptr->domainname) {
+ if (!ptr->is_last_name) {
+ if (ptr->domainname != domainname)
+ continue;
+ } else {
+ if (tomoyo_pathcmp(ptr->domainname, last_name))
+ continue;
+ }
+ }
+ if (tomoyo_pathcmp(ptr->program, program))
+ continue;
+ if (ptr->is_not) {
+ flag = false;
+ break;
+ }
+ flag = true;
+ }
+ return flag;
+}
+
+/*
+ * tomoyo_domain_keeper_list is used for holding list of domainnames which
+ * suppresses domain transition. Normally, a domainname is monotonically
+ * getting longer. But sometimes, we want to suppress domain transition.
+ * It would be convenient for us that programs executed from a login session
+ * belong to the same domain. Thus, TOMOYO provides a way to suppress domain
+ * transition.
+ *
+ * An entry is added by
+ *
+ * # echo 'keep_domain <kernel> /usr/sbin/sshd /bin/bash' > \
+ * /sys/kernel/security/tomoyo/exception_policy
+ *
+ * and is deleted by
+ *
+ * # echo 'delete keep_domain <kernel> /usr/sbin/sshd /bin/bash' > \
+ * /sys/kernel/security/tomoyo/exception_policy
+ *
+ * and all entries are retrieved by
+ *
+ * # grep ^keep_domain /sys/kernel/security/tomoyo/exception_policy
+ *
+ * In the example above, any process which belongs to
+ * "<kernel> /usr/sbin/sshd /bin/bash" domain will remain in that domain,
+ * unless explicitly specified by "initialize_domain" or "no_keep_domain".
+ *
+ * You may specify a program using "from" keyword.
+ * "keep_domain /bin/pwd from <kernel> /usr/sbin/sshd /bin/bash"
+ * will cause "/bin/pwd" executed from "<kernel> /usr/sbin/sshd /bin/bash"
+ * domain to remain in "<kernel> /usr/sbin/sshd /bin/bash" domain.
+ *
+ * You may add "no_" prefix to "keep_domain".
+ * "keep_domain <kernel> /usr/sbin/sshd /bin/bash" and
+ * "no_keep_domain /usr/bin/passwd from <kernel> /usr/sbin/sshd /bin/bash" will
+ * cause "/usr/bin/passwd" to belong to
+ * "<kernel> /usr/sbin/sshd /bin/bash /usr/bin/passwd" domain, unless
+ * explicitly specified by "initialize_domain".
+ */
+LIST_HEAD(tomoyo_domain_keeper_list);
+
+/**
+ * tomoyo_update_domain_keeper_entry - Update "struct tomoyo_domain_keeper_entry" list.
+ *
+ * @domainname: The name of domain.
+ * @program: The name of program. May be NULL.
+ * @is_not: True if it is "no_keep_domain" entry.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_update_domain_keeper_entry(const char *domainname,
+ const char *program,
+ const bool is_not,
+ const bool is_delete)
+{
+ struct tomoyo_domain_keeper_entry *entry = NULL;
+ struct tomoyo_domain_keeper_entry *ptr;
+ struct tomoyo_domain_keeper_entry e = { .is_not = is_not };
+ int error = is_delete ? -ENOENT : -ENOMEM;
+ if (!tomoyo_is_domain_def(domainname) &&
+ tomoyo_is_correct_path(domainname, 1, -1, -1))
+ e.is_last_name = true;
+ else if (!tomoyo_is_correct_domain(domainname))
+ return -EINVAL;
+ if (program) {
+ if (!tomoyo_is_correct_path(program, 1, -1, -1))
+ return -EINVAL;
+ e.program = tomoyo_get_name(program);
+ if (!e.program)
+ goto out;
+ }
+ e.domainname = tomoyo_get_name(domainname);
+ if (!e.domainname)
+ goto out;
+ if (!is_delete)
+ entry = kmalloc(sizeof(e), GFP_KERNEL);
+ mutex_lock(&tomoyo_policy_lock);
+ list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, list) {
+ if (tomoyo_memcmp(ptr, &e, offsetof(typeof(e), is_not),
+ sizeof(e)))
+ continue;
+ ptr->is_deleted = is_delete;
+ error = 0;
+ break;
+ }
+ if (!is_delete && error && tomoyo_commit_ok(entry, &e, sizeof(e))) {
+ list_add_tail_rcu(&entry->list, &tomoyo_domain_keeper_list);
+ entry = NULL;
+ error = 0;
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+ out:
+ tomoyo_put_name(e.domainname);
+ tomoyo_put_name(e.program);
+ kfree(entry);
+ return error;
+}
+
+/**
+ * tomoyo_write_domain_keeper_policy - Write "struct tomoyo_domain_keeper_entry" list.
+ *
+ * @data: String to parse.
+ * @is_not: True if it is "no_keep_domain" entry.
+ * @is_delete: True if it is a delete request.
+ *
+ */
+int tomoyo_write_domain_keeper_policy(char *data, const bool is_not,
+ const bool is_delete)
+{
+ char *cp = strstr(data, " from ");
+ if (cp) {
+ *cp = '\0';
+ return tomoyo_update_domain_keeper_entry(cp + 6, data,
+ is_not, is_delete);
+ }
+ return tomoyo_update_domain_keeper_entry(data, NULL, is_not,
+ is_delete);
+}
+
+/**
+ * tomoyo_read_domain_keeper_policy - Read "struct tomoyo_domain_keeper_entry" list.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head)
+{
+ struct list_head *pos;
+ bool done = true;
+ list_for_each_cookie(pos, head->read_var2,
+ &tomoyo_domain_keeper_list) {
+ struct tomoyo_domain_keeper_entry *ptr;
+ const char *no;
+ const char *from = "";
+ const char *program = "";
+ ptr = list_entry(pos, struct tomoyo_domain_keeper_entry, list);
+ if (ptr->is_deleted)
+ continue;
+ no = ptr->is_not ? "no_" : "";
+ if (ptr->program) {
+ from = " from ";
+ program = ptr->program->name;
+ }
+ done = tomoyo_io_printf(head, "%s" TOMOYO_KEYWORD_KEEP_DOMAIN
+ "%s%s%s\n", no, program, from,
+ ptr->domainname->name);
+ if (!done)
+ break;
+ }
+ return done;
+}
+
+/**
+ * tomoyo_is_domain_keeper - Check whether the given program causes domain transition suppression.
+ *
+ * @domainname: The name of domain.
+ * @program: The name of program.
+ * @last_name: The last component of @domainname.
+ *
+ * Returns true if executing @program supresses domain transition,
+ * false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static bool tomoyo_is_domain_keeper(const struct tomoyo_path_info *domainname,
+ const struct tomoyo_path_info *program,
+ const struct tomoyo_path_info *last_name)
+{
+ struct tomoyo_domain_keeper_entry *ptr;
+ bool flag = false;
+ list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, list) {
+ if (ptr->is_deleted)
+ continue;
+ if (!ptr->is_last_name) {
+ if (ptr->domainname != domainname)
+ continue;
+ } else {
+ if (tomoyo_pathcmp(ptr->domainname, last_name))
+ continue;
+ }
+ if (ptr->program && tomoyo_pathcmp(ptr->program, program))
+ continue;
+ if (ptr->is_not) {
+ flag = false;
+ break;
+ }
+ flag = true;
+ }
+ return flag;
+}
+
+/* The list for "struct tomoyo_aggregator_entry". */
+LIST_HEAD(tomoyo_aggregator_list);
+
+/**
+ * tomoyo_update_aggregator_entry - Update "struct tomoyo_aggregator_entry" list.
+ *
+ * @original_name: The original program's name.
+ * @aggregated_name: The aggregated program's name.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_update_aggregator_entry(const char *original_name,
+ const char *aggregated_name,
+ const bool is_delete)
+{
+ struct tomoyo_aggregator_entry *entry = NULL;
+ struct tomoyo_aggregator_entry *ptr;
+ struct tomoyo_aggregator_entry e = { };
+ int error = is_delete ? -ENOENT : -ENOMEM;
+ if (!tomoyo_is_correct_path(original_name, 1, 0, -1) ||
+ !tomoyo_is_correct_path(aggregated_name, 1, -1, -1))
+ return -EINVAL;
+ e.original_name = tomoyo_get_name(original_name);
+ e.aggregated_name = tomoyo_get_name(aggregated_name);
+ if (!e.original_name || !e.aggregated_name)
+ goto out;
+ if (!is_delete)
+ entry = kmalloc(sizeof(e), GFP_KERNEL);
+ mutex_lock(&tomoyo_policy_lock);
+ list_for_each_entry_rcu(ptr, &tomoyo_aggregator_list, list) {
+ if (tomoyo_memcmp(ptr, &e, offsetof(typeof(e), original_name),
+ sizeof(e)))
+ continue;
+ ptr->is_deleted = is_delete;
+ error = 0;
+ break;
+ }
+ if (!is_delete && error && tomoyo_commit_ok(entry, &e, sizeof(e))) {
+ list_add_tail_rcu(&entry->list, &tomoyo_aggregator_list);
+ entry = NULL;
+ error = 0;
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+ out:
+ tomoyo_put_name(e.original_name);
+ tomoyo_put_name(e.aggregated_name);
+ kfree(entry);
+ return error;
+}
+
+/**
+ * tomoyo_read_aggregator_policy - Read "struct tomoyo_aggregator_entry" list.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+bool tomoyo_read_aggregator_policy(struct tomoyo_io_buffer *head)
+{
+ struct list_head *pos;
+ bool done = true;
+ list_for_each_cookie(pos, head->read_var2, &tomoyo_aggregator_list) {
+ struct tomoyo_aggregator_entry *ptr;
+ ptr = list_entry(pos, struct tomoyo_aggregator_entry, list);
+ if (ptr->is_deleted)
+ continue;
+ done = tomoyo_io_printf(head, TOMOYO_KEYWORD_AGGREGATOR
+ "%s %s\n", ptr->original_name->name,
+ ptr->aggregated_name->name);
+ if (!done)
+ break;
+ }
+ return done;
+}
+
+/**
+ * tomoyo_write_aggregator_policy - Write "struct tomoyo_aggregator_entry" list.
+ *
+ * @data: String to parse.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_write_aggregator_policy(char *data, const bool is_delete)
+{
+ char *w[2];
+ if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[1][0])
+ return -EINVAL;
+ return tomoyo_update_aggregator_entry(w[0], w[1], is_delete);
+}
+
+/* Domain create/delete handler. */
+
+/**
+ * tomoyo_delete_domain - Delete a domain.
+ *
+ * @domainname: The name of domain.
+ *
+ * Returns 0.
+ */
+int tomoyo_delete_domain(char *domainname)
+{
+ struct tomoyo_domain_info *domain;
+ struct tomoyo_path_info name;
+ name.name = domainname;
+ tomoyo_fill_path_info(&name);
+ mutex_lock(&tomoyo_policy_lock);
+ /* Is there an active domain? */
+ list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
+ /* Never delete tomoyo_kernel_domain */
+ if (domain == &tomoyo_kernel_domain)
+ continue;
+ if (domain->is_deleted ||
+ tomoyo_pathcmp(domain->domainname, &name))
+ continue;
+ domain->is_deleted = true;
+ break;
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+ return 0;
+}
+
+/**
+ * tomoyo_find_or_assign_new_domain - Create a domain.
+ *
+ * @domainname: The name of domain.
+ * @profile: Profile number to assign if the domain was newly created.
+ *
+ * Returns pointer to "struct tomoyo_domain_info" on success, NULL otherwise.
+ */
+struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain
+(const char *domainname, const u8 profile)
+{
+ struct tomoyo_domain_info *entry;
+ struct tomoyo_domain_info *domain;
+ const struct tomoyo_path_info *saved_domainname;
+ bool found = false;
+
+ if (!tomoyo_is_correct_domain(domainname))
+ return NULL;
+ saved_domainname = tomoyo_get_name(domainname);
+ if (!saved_domainname)
+ return NULL;
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ mutex_lock(&tomoyo_policy_lock);
+ list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
+ if (domain->is_deleted ||
+ tomoyo_pathcmp(saved_domainname, domain->domainname))
+ continue;
+ found = true;
+ break;
+ }
+ if (!found && tomoyo_memory_ok(entry, sizeof(*entry))) {
+ INIT_LIST_HEAD(&entry->acl_info_list);
+ entry->domainname = saved_domainname;
+ saved_domainname = NULL;
+ entry->profile = profile;
+ list_add_tail_rcu(&entry->list, &tomoyo_domain_list);
+ domain = entry;
+ entry = NULL;
+ found = true;
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+ tomoyo_put_name(saved_domainname);
+ kfree(entry);
+ return found ? domain : NULL;
+}
+
+/**
+ * tomoyo_find_next_domain - Find a domain.
+ *
+ * @ee: Pointer to "struct tomoyo_execve_entry".
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static int tomoyo_find_next_domain(struct tomoyo_execve_entry *ee)
+{
+ struct tomoyo_request_info *r = &ee->r;
+ const struct tomoyo_path_info *handler = ee->handler;
+ struct tomoyo_domain_info *domain = NULL;
+ const char *old_domain_name = r->domain->domainname->name;
+ struct linux_binprm *bprm = ee->bprm;
+ const u32 tomoyo_flags = current->tomoyo_flags;
+ struct tomoyo_path_info rn = { }; /* real name */
+ struct tomoyo_path_info ln; /* last name */
+ int retval;
+ bool need_kfree = false;
+ ln.name = tomoyo_last_word(old_domain_name);
+ tomoyo_fill_path_info(&ln);
+ retry:
+ current->tomoyo_flags = tomoyo_flags;
+ r->cond = NULL;
+ if (need_kfree) {
+ kfree(rn.name);
+ need_kfree = false;
+ }
+
+ /* Get symlink's pathname of program. */
+ retval = tomoyo_symlink_path(bprm->filename, &rn);
+ if (retval < 0)
+ goto out;
+ need_kfree = true;
+
+ if (handler) {
+ if (tomoyo_pathcmp(&rn, handler)) {
+ /* Failed to verify execute handler. */
+ static u8 counter = 20;
+ if (counter) {
+ counter--;
+ printk(KERN_WARNING "Failed to verify: %s\n",
+ handler->name);
+ }
+ goto out;
+ }
+ } else {
+ struct tomoyo_aggregator_entry *ptr;
+ /* Check 'aggregator' directive. */
+ list_for_each_entry_rcu(ptr, &tomoyo_aggregator_list, list) {
+ if (ptr->is_deleted ||
+ !tomoyo_path_matches_pattern(&rn,
+ ptr->original_name))
+ continue;
+ kfree(rn.name);
+ need_kfree = false;
+ /* This is OK because it is read only. */
+ rn = *ptr->aggregated_name;
+ break;
+ }
+
+ /* Check execute permission. */
+ retval = tomoyo_exec_perm(r, &rn);
+ if (retval == 1)
+ goto retry;
+ if (retval < 0)
+ goto out;
+ }
+
+ /* Calculate domain to transit to. */
+ if (tomoyo_is_domain_initializer(r->domain->domainname, &rn, &ln)) {
+ /* Transit to the child of tomoyo_kernel_domain domain. */
+ snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, ROOT_NAME " " "%s",
+ rn.name);
+ } else if (r->domain == &tomoyo_kernel_domain &&
+ !tomoyo_policy_loaded) {
+ /*
+ * Needn't to transit from kernel domain before starting
+ * /sbin/init. But transit from kernel domain if executing
+ * initializers because they might start before /sbin/init.
+ */
+ domain = r->domain;
+ } else if (tomoyo_is_domain_keeper(r->domain->domainname, &rn, &ln)) {
+ /* Keep current domain. */
+ domain = r->domain;
+ } else {
+ /* Normal domain transition. */
+ snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s",
+ old_domain_name, rn.name);
+ }
+ if (domain || strlen(ee->tmp) >= TOMOYO_EXEC_TMPSIZE - 10)
+ goto done;
+ domain = tomoyo_find_domain(ee->tmp);
+ if (domain)
+ goto done;
+ if (r->mode == TOMOYO_CONFIG_ENFORCING) {
+ int error = tomoyo_supervisor(r, "# wants to create domain\n"
+ "%s\n", ee->tmp);
+ if (error == 1)
+ goto retry;
+ if (error < 0)
+ goto done;
+ }
+ domain = tomoyo_find_or_assign_new_domain(ee->tmp, r->profile);
+ if (domain)
+ tomoyo_audit_domain_creation_log(r->domain);
+ done:
+ if (!domain) {
+ printk(KERN_WARNING "ERROR: Domain '%s' not defined.\n",
+ ee->tmp);
+ if (r->mode == TOMOYO_CONFIG_ENFORCING)
+ retval = -EPERM;
+ else {
+ retval = 0;
+ r->domain->domain_transition_failed = true;
+ }
+ } else {
+ retval = 0;
+ }
+ out:
+ if (domain)
+ r->domain = domain;
+ if (need_kfree)
+ kfree(rn.name);
+ return retval;
+}
+
+/**
+ * tomoyo_environ - Check permission for environment variable names.
+ *
+ * @ee: Pointer to "struct tomoyo_execve_entry".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_environ(struct tomoyo_execve_entry *ee)
+{
+ struct tomoyo_request_info *r = &ee->r;
+ struct linux_binprm *bprm = ee->bprm;
+ char *arg_ptr = ee->tmp;
+ int arg_len = 0;
+ unsigned long pos = bprm->p;
+ int offset = pos % PAGE_SIZE;
+ int argv_count = bprm->argc;
+ int envp_count = bprm->envc;
+ /* printk(KERN_DEBUG "start %d %d\n", argv_count, envp_count); */
+ int error = -ENOMEM;
+ if (!r->mode || !envp_count)
+ return 0;
+ while (error == -ENOMEM) {
+ if (!tomoyo_dump_page(bprm, pos, &ee->dump))
+ goto out;
+ pos += PAGE_SIZE - offset;
+ /* Read. */
+ while (argv_count && offset < PAGE_SIZE) {
+ const char *kaddr = ee->dump.data;
+ if (!kaddr[offset++])
+ argv_count--;
+ }
+ if (argv_count) {
+ offset = 0;
+ continue;
+ }
+ while (offset < PAGE_SIZE) {
+ const char *kaddr = ee->dump.data;
+ const unsigned char c = kaddr[offset++];
+ if (c && arg_len < TOMOYO_EXEC_TMPSIZE - 10) {
+ if (c == '=') {
+ arg_ptr[arg_len++] = '\0';
+ } else if (c == '\\') {
+ arg_ptr[arg_len++] = '\\';
+ arg_ptr[arg_len++] = '\\';
+ } else if (c > ' ' && c < 127) {
+ arg_ptr[arg_len++] = c;
+ } else {
+ arg_ptr[arg_len++] = '\\';
+ arg_ptr[arg_len++] = (c >> 6) + '0';
+ arg_ptr[arg_len++]
+ = ((c >> 3) & 7) + '0';
+ arg_ptr[arg_len++] = (c & 7) + '0';
+ }
+ } else {
+ arg_ptr[arg_len] = '\0';
+ }
+ if (c)
+ continue;
+ if (tomoyo_env_perm(r, arg_ptr)) {
+ error = -EPERM;
+ break;
+ }
+ if (!--envp_count) {
+ error = 0;
+ break;
+ }
+ arg_len = 0;
+ }
+ offset = 0;
+ }
+ out:
+ if (r->mode != 3)
+ error = 0;
+ return error;
+}
+
+/**
+ * tomoyo_unescape - Unescape escaped string.
+ *
+ * @dest: String to unescape.
+ *
+ * Returns nothing.
+ */
+static void tomoyo_unescape(unsigned char *dest)
+{
+ unsigned char *src = dest;
+ unsigned char c;
+ unsigned char d;
+ unsigned char e;
+ while (1) {
+ c = *src++;
+ if (!c)
+ break;
+ if (c != '\\') {
+ *dest++ = c;
+ continue;
+ }
+ c = *src++;
+ if (c == '\\') {
+ *dest++ = c;
+ continue;
+ }
+ if (c < '0' || c > '3')
+ break;
+ d = *src++;
+ if (d < '0' || d > '7')
+ break;
+ e = *src++;
+ if (e < '0' || e > '7')
+ break;
+ *dest++ = ((c - '0') << 6) + ((d - '0') << 3) + (e - '0');
+ }
+ *dest = '\0';
+}
+
+/**
+ * tomoyo_root_depth - Get number of directories to strip.
+ *
+ * @dentry: Pointer to "struct dentry".
+ * @vfsmnt: Pointer to "struct vfsmount".
+ *
+ * Returns number of directories to strip.
+ */
+static inline int tomoyo_root_depth(struct dentry *dentry,
+ struct vfsmount *vfsmnt)
+{
+ int depth = 0;
+ spin_lock(&dcache_lock);
+ spin_lock(&vfsmount_lock);
+ for (;;) {
+ if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {
+ /* Global root? */
+ if (vfsmnt->mnt_parent == vfsmnt)
+ break;
+ dentry = vfsmnt->mnt_mountpoint;
+ vfsmnt = vfsmnt->mnt_parent;
+ continue;
+ }
+ dentry = dentry->d_parent;
+ depth++;
+ }
+ spin_unlock(&vfsmount_lock);
+ spin_unlock(&dcache_lock);
+ return depth;
+}
+
+/**
+ * tomoyo_get_root_depth - return the depth of root directory.
+ *
+ * Returns number of directories to strip.
+ */
+static int tomoyo_get_root_depth(void)
+{
+ int depth;
+ struct dentry *dentry;
+ struct vfsmount *vfsmnt;
+ struct path root;
+ read_lock(¤t->fs->lock);
+ root = current->fs->root;
+ path_get(¤t->fs->root);
+ dentry = root.dentry;
+ vfsmnt = root.mnt;
+ read_unlock(¤t->fs->lock);
+ depth = tomoyo_root_depth(dentry, vfsmnt);
+ path_put(&root);
+ return depth;
+}
+
+/**
+ * tomoyo_try_alt_exec - Try to start execute handler.
+ *
+ * @ee: Pointer to "struct tomoyo_execve_entry".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_try_alt_exec(struct tomoyo_execve_entry *ee)
+{
+ /*
+ * Contents of modified bprm.
+ * The envp[] in original bprm is moved to argv[] so that
+ * the alternatively executed program won't be affected by
+ * some dangerous environment variables like LD_PRELOAD.
+ *
+ * modified bprm->argc
+ * = original bprm->argc + original bprm->envc + 7
+ * modified bprm->envc
+ * = 0
+ *
+ * modified bprm->argv[0]
+ * = the program's name specified by execute_handler
+ * modified bprm->argv[1]
+ * = tomoyo_current_domain()->domainname->name
+ * modified bprm->argv[2]
+ * = the current process's name
+ * modified bprm->argv[3]
+ * = the current process's information (e.g. uid/gid).
+ * modified bprm->argv[4]
+ * = original bprm->filename
+ * modified bprm->argv[5]
+ * = original bprm->argc in string expression
+ * modified bprm->argv[6]
+ * = original bprm->envc in string expression
+ * modified bprm->argv[7]
+ * = original bprm->argv[0]
+ * ...
+ * modified bprm->argv[bprm->argc + 6]
+ * = original bprm->argv[bprm->argc - 1]
+ * modified bprm->argv[bprm->argc + 7]
+ * = original bprm->envp[0]
+ * ...
+ * modified bprm->argv[bprm->envc + bprm->argc + 6]
+ * = original bprm->envp[bprm->envc - 1]
+ */
+ struct linux_binprm *bprm = ee->bprm;
+ struct file *filp;
+ int retval;
+ const int original_argc = bprm->argc;
+ const int original_envc = bprm->envc;
+ struct task_struct *task = current;
+
+ /* Close the requested program's dentry. */
+ ee->obj.path1.dentry = NULL;
+ ee->obj.path1.mnt = NULL;
+ ee->obj.validate_done = false;
+ allow_write_access(bprm->file);
+ fput(bprm->file);
+ bprm->file = NULL;
+
+ /* Invalidate page dump cache. */
+ ee->dump.page = NULL;
+
+ /* Move envp[] to argv[] */
+ bprm->argc += bprm->envc;
+ bprm->envc = 0;
+
+ /* Set argv[6] */
+ {
+ char *cp = ee->tmp;
+ snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%d",
+ original_envc);
+ retval = copy_strings_kernel(1, &cp, bprm);
+ if (retval < 0)
+ goto out;
+ bprm->argc++;
+ }
+
+ /* Set argv[5] */
+ {
+ char *cp = ee->tmp;
+ snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%d",
+ original_argc);
+ retval = copy_strings_kernel(1, &cp, bprm);
+ if (retval < 0)
+ goto out;
+ bprm->argc++;
+ }
+
+ /* Set argv[4] */
+ {
+ retval = copy_strings_kernel(1, &bprm->filename, bprm);
+ if (retval < 0)
+ goto out;
+ bprm->argc++;
+ }
+
+ /* Set argv[3] */
+ {
+ char *cp = ee->tmp;
+ const u32 tomoyo_flags = task->tomoyo_flags;
+ snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1,
+ "pid=%d uid=%d gid=%d euid=%d egid=%d suid=%d "
+ "sgid=%d fsuid=%d fsgid=%d state[0]=%u "
+ "state[1]=%u state[2]=%u",
+ (pid_t) sys_getpid(), current_uid(), current_gid(),
+ current_euid(), current_egid(), current_suid(),
+ current_sgid(), current_fsuid(), current_fsgid(),
+ (u8) (tomoyo_flags >> 24), (u8) (tomoyo_flags >> 16),
+ (u8) (tomoyo_flags >> 8));
+ retval = copy_strings_kernel(1, &cp, bprm);
+ if (retval < 0)
+ goto out;
+ bprm->argc++;
+ }
+
+ /* Set argv[2] */
+ {
+ char *exe = (char *) tomoyo_get_exe();
+ if (exe) {
+ retval = copy_strings_kernel(1, &exe, bprm);
+ kfree(exe);
+ } else {
+ exe = ee->tmp;
+ snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1,
+ "<unknown>");
+ retval = copy_strings_kernel(1, &exe, bprm);
+ }
+ if (retval < 0)
+ goto out;
+ bprm->argc++;
+ }
+
+ /* Set argv[1] */
+ {
+ char *cp = ee->tmp;
+ snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s",
+ tomoyo_current_domain()->domainname->name);
+ retval = copy_strings_kernel(1, &cp, bprm);
+ if (retval < 0)
+ goto out;
+ bprm->argc++;
+ }
+
+ /* Set argv[0] */
+ {
+ int depth = tomoyo_get_root_depth();
+ int len = strlen(ee->handler->name) + 1;
+ char *cp = kmalloc(len, GFP_KERNEL);
+ if (!cp) {
+ retval = -ENOMEM;
+ goto out;
+ }
+ ee->handler_path = cp;
+ memmove(cp, ee->handler->name, len);
+ tomoyo_unescape(cp);
+ retval = -ENOENT;
+ if (!*cp || *cp != '/')
+ goto out;
+ /* Adjust root directory for open_exec(). */
+ while (depth) {
+ cp = strchr(cp + 1, '/');
+ if (!cp)
+ goto out;
+ depth--;
+ }
+ memmove(ee->handler_path, cp, strlen(cp) + 1);
+ cp = ee->handler_path;
+ retval = copy_strings_kernel(1, &cp, bprm);
+ if (retval < 0)
+ goto out;
+ bprm->argc++;
+ }
+
+ /*
+ * OK, now restart the process with execute handler program's dentry.
+ */
+ filp = open_exec(ee->handler_path);
+ if (IS_ERR(filp)) {
+ retval = PTR_ERR(filp);
+ goto out;
+ }
+ ee->obj.path1.dentry = filp->f_dentry;
+ ee->obj.path1.mnt = filp->f_vfsmnt;
+ bprm->file = filp;
+ bprm->filename = ee->handler_path;
+ bprm->interp = bprm->filename;
+ retval = prepare_binprm(bprm);
+ if (retval < 0)
+ goto out;
+ task->tomoyo_flags |= TOMOYO_DONT_SLEEP_ON_ENFORCE_ERROR;
+ retval = tomoyo_find_next_domain(ee);
+ task->tomoyo_flags &= ~TOMOYO_DONT_SLEEP_ON_ENFORCE_ERROR;
+ out:
+ return retval;
+}
+
+/**
+ * tomoyo_find_execute_handler - Find an execute handler.
+ *
+ * @ee: Pointer to "struct tomoyo_execve_entry".
+ * @type: Type of execute handler.
+ *
+ * Returns true if found, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static bool tomoyo_find_execute_handler(struct tomoyo_execve_entry *ee,
+ const u8 type)
+{
+ struct task_struct *task = current;
+ const struct tomoyo_domain_info *domain = tomoyo_current_domain();
+ struct tomoyo_acl_info *ptr;
+ bool found = false;
+ /*
+ * Don't use execute handler if the current process is
+ * marked as execute handler to avoid infinite execute handler loop.
+ */
+ if (task->tomoyo_flags & TOMOYO_TASK_IS_EXECUTE_HANDLER)
+ return false;
+ list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ struct tomoyo_execute_handler_record *acl;
+ if (ptr->type != type)
+ continue;
+ acl = container_of(ptr, struct tomoyo_execute_handler_record,
+ head);
+ ee->handler = acl->handler;
+ found = true;
+ break;
+ }
+ return found;
+}
+
+/**
+ * tomoyo_dump_page - Dump a page to buffer.
+ *
+ * @bprm: Pointer to "struct linux_binprm".
+ * @pos: Location to dump.
+ * @dump: Poiner to "struct tomoyo_page_dump".
+ *
+ * Returns true on success, false otherwise.
+ */
+bool tomoyo_dump_page(struct linux_binprm *bprm, unsigned long pos,
+ struct tomoyo_page_dump *dump)
+{
+ struct page *page;
+ /* dump->data is released by tomoyo_finish_execve(). */
+ if (!dump->data) {
+ dump->data = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!dump->data)
+ return false;
+ }
+ /* Same with get_arg_page(bprm, pos, 0) in fs/exec.c */
+#if defined(CONFIG_MMU)
+ if (get_user_pages(current, bprm->mm, pos, 1, 0, 1, &page, NULL) <= 0)
+ return false;
+#else
+ page = bprm->page[pos / PAGE_SIZE];
+#endif
+ if (page != dump->page) {
+ const unsigned int offset = pos % PAGE_SIZE;
+ /*
+ * Maybe kmap()/kunmap() should be used here.
+ * But remove_arg_zero() uses kmap_atomic()/kunmap_atomic().
+ * So do I.
+ */
+ char *kaddr = kmap_atomic(page, KM_USER0);
+ dump->page = page;
+ memcpy(dump->data + offset, kaddr + offset,
+ PAGE_SIZE - offset);
+ kunmap_atomic(kaddr, KM_USER0);
+ }
+ /* Same with put_arg_page(page) in fs/exec.c */
+#if defined(CONFIG_MMU)
+ put_page(page);
+#endif
+ return true;
+}
+
+/**
+ * tomoyo_start_execve - Prepare for execve() operation.
+ *
+ * @bprm: Pointer to "struct linux_binprm".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_start_execve(struct linux_binprm *bprm)
+{
+ int retval;
+ struct task_struct *task = current;
+ struct tomoyo_execve_entry *ee;
+ if (!tomoyo_policy_loaded)
+ tomoyo_load_policy(bprm->filename);
+ ee = kzalloc(sizeof(*ee), GFP_KERNEL);
+ if (!ee)
+ return -ENOMEM;
+ ee->tmp = kzalloc(TOMOYO_EXEC_TMPSIZE, GFP_KERNEL);
+ if (!ee->tmp) {
+ kfree(ee);
+ return -ENOMEM;
+ }
+ /* This lock is released at tomoyo_finish_execve(). */
+ ee->reader_idx = tomoyo_read_lock();
+ /* ee->dump->data is allocated by tomoyo_dump_page(). */
+ ee->previous_domain = task->tomoyo_domain_info;
+ /* Clear manager flag. */
+ task->tomoyo_flags &= ~TOMOYO_TASK_IS_POLICY_MANAGER;
+ /* Tell GC that I started execve(). */
+ task->tomoyo_flags |= TOMOYO_TASK_IS_IN_EXECVE;
+ /*
+ * Make task->tomoyo_flags visible to GC before changing
+ * task->tomoyo_domain_info .
+ */
+ smp_mb();
+ bprm->cred->security = ee;
+ atomic_set(&ee->cred_users, 1);
+ tomoyo_init_request_info(&ee->r, NULL, TOMOYO_MAC_FILE_EXECUTE);
+ ee->r.ee = ee;
+ ee->bprm = bprm;
+ ee->r.obj = &ee->obj;
+ ee->obj.path1.dentry = bprm->file->f_dentry;
+ ee->obj.path1.mnt = bprm->file->f_vfsmnt;
+ if (tomoyo_find_execute_handler(ee, TOMOYO_TYPE_EXECUTE_HANDLER)) {
+ retval = tomoyo_try_alt_exec(ee);
+ if (!retval)
+ tomoyo_audit_execute_handler_log(ee, true);
+ goto ok;
+ }
+ retval = tomoyo_find_next_domain(ee);
+ if (retval != -EPERM)
+ goto ok;
+ if (tomoyo_find_execute_handler(ee,
+ TOMOYO_TYPE_DENIED_EXECUTE_HANDLER)) {
+ retval = tomoyo_try_alt_exec(ee);
+ if (!retval)
+ tomoyo_audit_execute_handler_log(ee, false);
+ }
+ ok:
+ if (retval < 0)
+ goto out;
+ /*
+ * Proceed to the next domain in order to allow reaching via PID.
+ * It will be reverted if execve() failed. Reverting is not good.
+ * But it is better than being unable to reach via PID in interactive
+ * enforcing mode.
+ */
+ task->tomoyo_domain_info = ee->r.domain;
+ ee->r.mode = tomoyo_get_mode(ee->r.domain->profile,
+ TOMOYO_MAC_ENVIRON);
+ retval = tomoyo_environ(ee);
+ if (retval < 0)
+ goto out;
+ retval = 0;
+ out:
+ if (retval) {
+ tomoyo_finish_execve(ee, true);
+ bprm->cred->security = NULL;
+ }
+ return retval;
+}
+
+/**
+ * tomoyo_finish_execve - Clean up execve() operation.
+ *
+ * @ee: Pointer to "struct tomoyo_execve_entry".
+ * @rollback: True if need to rollback.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+void tomoyo_finish_execve(struct tomoyo_execve_entry *ee, const bool rollback)
+{
+ struct task_struct *task = current;
+ if (!ee)
+ return;
+ if (rollback) {
+ task->tomoyo_domain_info = ee->previous_domain;
+ /*
+ * Make task->tomoyo_domain_info visible to GC before changing
+ * task->tomoyo_flags .
+ */
+ smp_mb();
+ } else {
+ /* Mark the current process as execute handler. */
+ if (ee->handler)
+ task->tomoyo_flags |= TOMOYO_TASK_IS_EXECUTE_HANDLER;
+ /* Mark the current process as normal process. */
+ else
+ task->tomoyo_flags &= ~TOMOYO_TASK_IS_EXECUTE_HANDLER;
+ }
+ /* Tell GC that I finished execve(). */
+ task->tomoyo_flags &= ~TOMOYO_TASK_IS_IN_EXECVE;
+ /* This lock is acquired at tomoyo_start_execve(). */
+ tomoyo_read_unlock(ee->reader_idx);
+ kfree(ee->handler_path);
+ kfree(ee->tmp);
+ kfree(ee->dump.data);
+ kfree(ee);
+}
--
^ permalink raw reply [flat|nested] 50+ messages in thread
* [TOMOYO #16 25/25] TOMOYO: Update Kconfig and Makefile.
2009-10-04 12:49 [TOMOYO #16 00/25] Starting TOMOYO 2.3 Tetsuo Handa
` (23 preceding siblings ...)
2009-10-04 12:50 ` [TOMOYO #16 24/25] TOMOYO: Add domain transition handler Tetsuo Handa
@ 2009-10-04 12:50 ` Tetsuo Handa
2009-10-06 9:39 ` [TOMOYO #16 00/25] Starting TOMOYO 2.3 Pavel Machek
25 siblings, 0 replies; 50+ messages in thread
From: Tetsuo Handa @ 2009-10-04 12:50 UTC (permalink / raw)
To: linux-security-module; +Cc: linux-kernel, Tetsuo Handa
[-- Attachment #1: tomoyo-update-Makefile-and-Kconfig.patch --]
[-- Type: text/plain, Size: 3667 bytes --]
This patch switches from TOMOYO 2.2.0 to TOMOYO 2.3.0 .
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
security/tomoyo/Kconfig | 67 +++++++++++++++++++++++++++++++++++++++++++++++
security/tomoyo/Makefile | 2 -
2 files changed, 68 insertions(+), 1 deletion(-)
--- security-testing-2.6.orig/security/tomoyo/Kconfig
+++ security-testing-2.6/security/tomoyo/Kconfig
@@ -3,9 +3,76 @@ config SECURITY_TOMOYO
depends on SECURITY
select SECURITYFS
select SECURITY_PATH
+ select SECURITY_NETWORK
default n
help
This selects TOMOYO Linux, pathname-based access control.
Required userspace tools and further information may be
found at <http://tomoyo.sourceforge.jp/>.
If you are unsure how to answer this question, answer N.
+
+config SECURITY_TOMOYO_MAX_ACCEPT_ENTRY
+ int "Default maximal count for learning mode"
+ default 2048
+ range 0 2147483647
+ depends on SECURITY_TOMOYO
+ help
+ This is the default value for maximal ACL entries
+ that are automatically appended into policy at "learning mode".
+ Some programs access thousands of objects, so running
+ such programs in "learning mode" dulls the system response
+ and consumes much memory.
+ This is the safeguard for such programs.
+
+config SECURITY_TOMOYO_BUILTIN_INITIALIZERS
+ string "Built-in domain initializer programs"
+ default "/sbin/modprobe /sbin/hotplug"
+ depends on SECURITY_TOMOYO
+ ---help---
+ Some programs are executed from initrd/initramfs before /sbin/init
+ starts.
+
+ Since policy is loaded when /sbin/init starts, it is impossible to
+ run such programs outside the <kernel> domain. Usually it is fine.
+
+ But if such programs continue running when /sbin/init starts, such
+ programs will reside in the <kernel> domain.
+ If such programs executes /bin/sh , you will give the <kernel> domain
+ permission to execute /bin/sh ; I think you don't want to do so.
+
+ This option allows you to chase such programs away from the <kernel>
+ domain so that you won't give the <kernel> domain permission to
+ execute /bin/sh .
+
+config SECURITY_TOMOYO_AUDIT
+ bool "Auditing interface support"
+ default y
+ depends on SECURITY_TOMOYO
+ help
+ This option enables /sys/kernel/security/tomoyo/grant_log and /sys/kernel/security/tomoyo/reject_log
+ interfaces. You may disable these interfaces if you want to apply for
+ HDD-less systems (e.g. embedded systems).
+
+config SECURITY_TOMOYO_MAX_GRANT_LOG
+ int "Default maximal count for grant log"
+ default 1024
+ range 0 2147483647
+ depends on SECURITY_TOMOYO_AUDIT
+ help
+ This is the default value for maximal entries for
+ access grant logs that the kernel can hold on memory.
+ You can read the log via /sys/kernel/security/tomoyo/grant_log.
+ If you don't need access grant logs,
+ you may set this value to 0.
+
+config SECURITY_TOMOYO_MAX_REJECT_LOG
+ int "Default maximal count for reject log"
+ default 1024
+ range 0 2147483647
+ depends on SECURITY_TOMOYO_AUDIT
+ help
+ This is the default value for maximal entries for
+ access reject logs that the kernel can hold on memory.
+ You can read the log via /sys/kernel/security/tomoyo/reject_log.
+ If you don't need access reject logs,
+ you may set this value to 0.
--- security-testing-2.6.orig/security/tomoyo/Makefile
+++ security-testing-2.6/security/tomoyo/Makefile
@@ -1 +1 @@
-obj-y = common.o realpath.o tomoyo.o domain.o file.o
+obj-y = address_group.o gc.o mount.o new-file.o path_group.o audit.o environ.o load_policy.o network.o new-realpath.o securityfs_if.o util.o capability.o condition.o memory.o new-domain.o number_group.o policy_io.o lsm.o
--
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [TOMOYO #16 00/25] Starting TOMOYO 2.3
2009-10-04 12:49 [TOMOYO #16 00/25] Starting TOMOYO 2.3 Tetsuo Handa
` (24 preceding siblings ...)
2009-10-04 12:50 ` [TOMOYO #16 25/25] TOMOYO: Update Kconfig and Makefile Tetsuo Handa
@ 2009-10-06 9:39 ` Pavel Machek
2009-10-07 4:09 ` Tetsuo Handa
25 siblings, 1 reply; 50+ messages in thread
From: Pavel Machek @ 2009-10-06 9:39 UTC (permalink / raw)
To: Tetsuo Handa; +Cc: linux-security-module, linux-kernel
On Sun 2009-10-04 21:49:46, Tetsuo Handa wrote:
> Hello.
>
> This is the beginning of TOMOYO 2.3. TOMOYO 2.2 (which is in kernel 2.6.30 and
> later) is terribly lacking in functionality (e.g. no audit logs, no network).
> I hope TOMOYO 2.3 can provide practically sufficient functionality.
...
> Since this patchset is not yet accepted, I haven't written documentation for
> TOMOYO 2.3. You can see http://tomoyo.sourceforge.jp/1.7/policy-reference.html
> instead.
New, undocumented user/kernel api is no-no.
> Conventionally, patches should be submitted in the form of diff file.
> But this time, I submit in the form of entire file due to amount of changes.
That's also no-no.
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [TOMOYO #16 00/25] Starting TOMOYO 2.3
2009-10-06 9:39 ` [TOMOYO #16 00/25] Starting TOMOYO 2.3 Pavel Machek
@ 2009-10-07 4:09 ` Tetsuo Handa
2009-10-07 7:38 ` Pavel Machek
0 siblings, 1 reply; 50+ messages in thread
From: Tetsuo Handa @ 2009-10-07 4:09 UTC (permalink / raw)
To: pavel; +Cc: linux-security-module, linux-kernel
Hello.
Pavel Machek wrote:
>> Since this patchset is not yet accepted, I haven't written documentation for
>> TOMOYO 2.3. You can see http://tomoyo.sourceforge.jp/1.7/policy-reference.html
>> instead.
>
>New, undocumented user/kernel api is no-no.
I'll update api description by final submission.
Main purpose of this submission is to
(1) know whether 01, 02, 03, 05 and 06 are acceptable or not.
If 05 is not acceptable, the rest of patchset needs to be rewritten.
Please review 01, 02, 03, 05 and 06 before reviewing the rest.
(2) know which features are acceptable.
This submission includes proposal of new features.
Use of customized d_path().
Network filtering including incoming TCP connections.
Audit logs.
Conditional permissions.
Interactive enforcing mode.
Sleep penalty.
Execute handler.
Environment variable name checking.
Non POSIX capability checking.
Unacceptable features will be dropped from next submission.
>> Conventionally, patches should be submitted in the form of diff file.
>> But this time, I submit in the form of entire file due to amount of changes.
>
> That's also no-no.
I have a question.
Is the diff file based on existing files more preferable for reviewers to
review than totally rewritten files, even if "total lines of diff files" is
close to "total lines of rewritten files"?
Amount of rewritten files:
# cat security/tomoyo/* | wc -l
17250
Amount of diff based on existing files:
# diff -Nur security/tomoyo.2.2/ security/tomoyo/ | wc -l
16945
# diff -Nur security/tomoyo.2.2/ security/tomoyo/ | diffstat -f0
24 files changed, 13495 insertions(+), 2216 deletions(-)
I posted rewritten files because I thought reading 17250 insertions is less
difficult than reading 16945 lines of diff file with complicated mixture of
13495 insertions and 2216 deletions.
Regards.
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [TOMOYO #16 00/25] Starting TOMOYO 2.3
2009-10-07 4:09 ` Tetsuo Handa
@ 2009-10-07 7:38 ` Pavel Machek
2009-10-07 13:30 ` Tetsuo Handa
0 siblings, 1 reply; 50+ messages in thread
From: Pavel Machek @ 2009-10-07 7:38 UTC (permalink / raw)
To: Tetsuo Handa; +Cc: linux-security-module, linux-kernel
On Wed 2009-10-07 13:09:24, Tetsuo Handa wrote:
> Hello.
>
> Pavel Machek wrote:
> >> Since this patchset is not yet accepted, I haven't written documentation for
> >> TOMOYO 2.3. You can see http://tomoyo.sourceforge.jp/1.7/policy-reference.html
> >> instead.
> >
> >New, undocumented user/kernel api is no-no.
>
> I'll update api description by final submission.
Well, then you'll get proper review by final submission.
> >> Conventionally, patches should be submitted in the form of diff file.
> >> But this time, I submit in the form of entire file due to amount of changes.
> >
> > That's also no-no.
>
> I have a question.
> Is the diff file based on existing files more preferable for reviewers to
> review than totally rewritten files, even if "total lines of diff files" is
> close to "total lines of rewritten files"?
You are expected to submit diffs in smaller steps, not "here it is,
totally rewritten, take it or leave it".
Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [TOMOYO #16 00/25] Starting TOMOYO 2.3
2009-10-07 7:38 ` Pavel Machek
@ 2009-10-07 13:30 ` Tetsuo Handa
0 siblings, 0 replies; 50+ messages in thread
From: Tetsuo Handa @ 2009-10-07 13:30 UTC (permalink / raw)
To: pavel; +Cc: linux-security-module, linux-kernel
Pavel Machek wrote:
> You are expected to submit diffs in smaller steps, not "here it is,
> totally rewritten, take it or leave it".
I see. I'll try to break this patchset into smaller steps.
Your comment ( http://lkml.org/lkml/2009/5/1/47 ) was the trigger for not only
garbage collector but also many usability and feature enhancements for TOMOYO.
Thank you. :-)
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [TOMOYO #16 01/25] LSM: Add security_path_chmod() and security_path_chown().
2009-10-04 12:49 ` [TOMOYO #16 01/25] LSM: Add security_path_chmod() and security_path_chown() Tetsuo Handa
@ 2009-10-08 17:10 ` John Johansen
2009-10-12 1:04 ` James Morris
2009-10-29 5:12 ` [TOMOYO #16 01/25] LSM: Add security_path_chmod() and security_path_chown() Serge E. Hallyn
1 sibling, 1 reply; 50+ messages in thread
From: John Johansen @ 2009-10-08 17:10 UTC (permalink / raw)
To: Tetsuo Handa; +Cc: linux-security-module, linux-kernel
Tetsuo Handa wrote:
> This patch allows pathname based LSM modules to check chmod()/chown()
> operations. Since notify_change() does not receive "struct vfsmount *",
> we add security_path_chmod() and security_path_chown() to the caller of
> notify_change().
>
> These hooks are used by TOMOYO.
This hooks would be useful for AppArmor as well.
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [TOMOYO #16 02/25] LSM: Add security_path_chroot().
2009-10-04 12:49 ` [TOMOYO #16 02/25] LSM: Add security_path_chroot() Tetsuo Handa
@ 2009-10-08 17:12 ` John Johansen
2009-10-29 5:32 ` Serge E. Hallyn
1 sibling, 0 replies; 50+ messages in thread
From: John Johansen @ 2009-10-08 17:12 UTC (permalink / raw)
To: Tetsuo Handa; +Cc: linux-security-module, linux-kernel
Tetsuo Handa wrote:
> This patch allows pathname based LSM modules to check chroot() operations.
>
> This hook is used by TOMOYO.
Looks good, and AppArmor would use this as well.
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [TOMOYO #16 03/25] LSM: Pass original mount flags to security_sb_mount().
2009-10-04 12:49 ` [TOMOYO #16 03/25] LSM: Pass original mount flags to security_sb_mount() Tetsuo Handa
@ 2009-10-08 17:22 ` John Johansen
0 siblings, 0 replies; 50+ messages in thread
From: John Johansen @ 2009-10-08 17:22 UTC (permalink / raw)
To: Tetsuo Handa; +Cc: linux-security-module, linux-kernel
Tetsuo Handa wrote:
> This patch allows LSM modules to determine based on original mount flags
> passed to mount(). A LSM module can get masked mount flags (if needed) by
>
> flags &= ~(MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_ACTIVE |
> MS_NOATIME | MS_NODIRATIME | MS_RELATIME| MS_KERNMOUNT |
> MS_STRICTATIME);
This looks good, and would also have use in AppArmor.
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [TOMOYO #16 01/25] LSM: Add security_path_chmod() and security_path_chown().
2009-10-08 17:10 ` John Johansen
@ 2009-10-12 1:04 ` James Morris
2009-10-13 11:34 ` [TOMOYO #16 01/25] LSM: Add security_path_chmod() andsecurity_path_chown() Tetsuo Handa
0 siblings, 1 reply; 50+ messages in thread
From: James Morris @ 2009-10-12 1:04 UTC (permalink / raw)
To: John Johansen; +Cc: Tetsuo Handa, linux-security-module, linux-kernel
On Thu, 8 Oct 2009, John Johansen wrote:
> Tetsuo Handa wrote:
> > This patch allows pathname based LSM modules to check chmod()/chown()
> > operations. Since notify_change() does not receive "struct vfsmount *",
> > we add security_path_chmod() and security_path_chown() to the caller of
> > notify_change().
> >
> > These hooks are used by TOMOYO.
>
> This hooks would be useful for AppArmor as well.
I've applied the first three patches to
git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/security-testing-2.6#next
It would be good to see some more evidence of review for the remaining
patches.
--
James Morris
<jmorris@namei.org>
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [TOMOYO #16 01/25] LSM: Add security_path_chmod() andsecurity_path_chown().
2009-10-12 1:04 ` James Morris
@ 2009-10-13 11:34 ` Tetsuo Handa
2009-10-13 11:37 ` [PATCH] TOMOYO: Add recursive directory matching operator support Tetsuo Handa
0 siblings, 1 reply; 50+ messages in thread
From: Tetsuo Handa @ 2009-10-13 11:34 UTC (permalink / raw)
To: jmorris; +Cc: linux-security-module, linux-kernel
James Morris wrote:
> On Thu, 8 Oct 2009, John Johansen wrote:
>
> > Tetsuo Handa wrote:
> > > This patch allows pathname based LSM modules to check chmod()/chown()
> > > operations. Since notify_change() does not receive "struct vfsmount *",
> > > we add security_path_chmod() and security_path_chown() to the caller of
> > > notify_change().
> > >
> > > These hooks are used by TOMOYO.
> >
> > This hooks would be useful for AppArmor as well.
>
>
> I've applied the first three patches to
>
> git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/security-testing-2.6#next
>
Thank you.
> It would be good to see some more evidence of review for the remaining
> patches.
Yes. I'm ready to submit first three patches.
^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH] TOMOYO: Add recursive directory matching operator support.
2009-10-13 11:34 ` [TOMOYO #16 01/25] LSM: Add security_path_chmod() andsecurity_path_chown() Tetsuo Handa
@ 2009-10-13 11:37 ` Tetsuo Handa
2009-10-13 11:39 ` [PATCH] TOMOYO: Use RCU primitives for list operation Tetsuo Handa
0 siblings, 1 reply; 50+ messages in thread
From: Tetsuo Handa @ 2009-10-13 11:37 UTC (permalink / raw)
To: linux-security-module; +Cc: linux-kernel
[PATCH] TOMOYO: Add recursive directory matching operator support.
This patch introduces new operator /\{dir\}/ which matches
'/' + 'One or more repetitions of dir/' (e.g. /dir/ /dir/dir/ /dir/dir/dir/ ).
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
security/tomoyo/common.c | 200 ++++++++++++++++++++++++++++-------------------
security/tomoyo/common.h | 4
2 files changed, 121 insertions(+), 83 deletions(-)
--- security-testing-2.6.orig/security/tomoyo/common.c
+++ security-testing-2.6/security/tomoyo/common.c
@@ -187,6 +187,8 @@ bool tomoyo_is_correct_path(const char *
const s8 pattern_type, const s8 end_type,
const char *function)
{
+ const char *const start = filename;
+ bool in_repetition = false;
bool contains_pattern = false;
unsigned char c;
unsigned char d;
@@ -212,9 +214,13 @@ bool tomoyo_is_correct_path(const char *
if (c == '/')
goto out;
}
- while ((c = *filename++) != '\0') {
+ while (1) {
+ c = *filename++;
+ if (!c)
+ break;
if (c == '\\') {
- switch ((c = *filename++)) {
+ c = *filename++;
+ switch (c) {
case '\\': /* "\\" */
continue;
case '$': /* "\$" */
@@ -231,6 +237,22 @@ bool tomoyo_is_correct_path(const char *
break; /* Must not contain pattern */
contains_pattern = true;
continue;
+ case '{': /* "/\{" */
+ if (filename - 3 < start ||
+ *(filename - 3) != '/')
+ break;
+ if (pattern_type == -1)
+ break; /* Must not contain pattern */
+ contains_pattern = true;
+ in_repetition = true;
+ continue;
+ case '}': /* "\}/" */
+ if (*filename != '/')
+ break;
+ if (!in_repetition)
+ break;
+ in_repetition = false;
+ continue;
case '0': /* "\ooo" */
case '1':
case '2':
@@ -246,6 +268,8 @@ bool tomoyo_is_correct_path(const char *
continue; /* pattern is not \000 */
}
goto out;
+ } else if (in_repetition && c == '/') {
+ goto out;
} else if (tomoyo_is_invalid(c)) {
goto out;
}
@@ -254,6 +278,8 @@ bool tomoyo_is_correct_path(const char *
if (!contains_pattern)
goto out;
}
+ if (in_repetition)
+ goto out;
return true;
out:
printk(KERN_DEBUG "%s: Invalid pathname '%s'\n", function,
@@ -360,33 +386,6 @@ struct tomoyo_domain_info *tomoyo_find_d
}
/**
- * tomoyo_path_depth - Evaluate the number of '/' in a string.
- *
- * @pathname: The string to evaluate.
- *
- * Returns path depth of the string.
- *
- * I score 2 for each of the '/' in the @pathname
- * and score 1 if the @pathname ends with '/'.
- */
-static int tomoyo_path_depth(const char *pathname)
-{
- int i = 0;
-
- if (pathname) {
- const char *ep = pathname + strlen(pathname);
- if (pathname < ep--) {
- if (*ep != '/')
- i++;
- while (pathname <= ep)
- if (*ep-- == '/')
- i += 2;
- }
- }
- return i;
-}
-
-/**
* tomoyo_const_part_length - Evaluate the initial length without a pattern in a token.
*
* @filename: The string to evaluate.
@@ -444,11 +443,10 @@ void tomoyo_fill_path_info(struct tomoyo
ptr->is_dir = len && (name[len - 1] == '/');
ptr->is_patterned = (ptr->const_len < len);
ptr->hash = full_name_hash(name, len);
- ptr->depth = tomoyo_path_depth(name);
}
/**
- * tomoyo_file_matches_to_pattern2 - Pattern matching without '/' character
+ * tomoyo_file_matches_pattern2 - Pattern matching without '/' character
* and "\-" pattern.
*
* @filename: The start of string to check.
@@ -458,10 +456,10 @@ void tomoyo_fill_path_info(struct tomoyo
*
* Returns true if @filename matches @pattern, false otherwise.
*/
-static bool tomoyo_file_matches_to_pattern2(const char *filename,
- const char *filename_end,
- const char *pattern,
- const char *pattern_end)
+static bool tomoyo_file_matches_pattern2(const char *filename,
+ const char *filename_end,
+ const char *pattern,
+ const char *pattern_end)
{
while (filename < filename_end && pattern < pattern_end) {
char c;
@@ -519,7 +517,7 @@ static bool tomoyo_file_matches_to_patte
case '*':
case '@':
for (i = 0; i <= filename_end - filename; i++) {
- if (tomoyo_file_matches_to_pattern2(
+ if (tomoyo_file_matches_pattern2(
filename + i, filename_end,
pattern + 1, pattern_end))
return true;
@@ -550,7 +548,7 @@ static bool tomoyo_file_matches_to_patte
j++;
}
for (i = 1; i <= j; i++) {
- if (tomoyo_file_matches_to_pattern2(
+ if (tomoyo_file_matches_pattern2(
filename + i, filename_end,
pattern + 1, pattern_end))
return true;
@@ -567,7 +565,7 @@ static bool tomoyo_file_matches_to_patte
}
/**
- * tomoyo_file_matches_to_pattern - Pattern matching without without '/' character.
+ * tomoyo_file_matches_pattern - Pattern matching without without '/' character.
*
* @filename: The start of string to check.
* @filename_end: The end of string to check.
@@ -576,7 +574,7 @@ static bool tomoyo_file_matches_to_patte
*
* Returns true if @filename matches @pattern, false otherwise.
*/
-static bool tomoyo_file_matches_to_pattern(const char *filename,
+static bool tomoyo_file_matches_pattern(const char *filename,
const char *filename_end,
const char *pattern,
const char *pattern_end)
@@ -589,10 +587,10 @@ static bool tomoyo_file_matches_to_patte
/* Split at "\-" pattern. */
if (*pattern++ != '\\' || *pattern++ != '-')
continue;
- result = tomoyo_file_matches_to_pattern2(filename,
- filename_end,
- pattern_start,
- pattern - 2);
+ result = tomoyo_file_matches_pattern2(filename,
+ filename_end,
+ pattern_start,
+ pattern - 2);
if (first)
result = !result;
if (result)
@@ -600,13 +598,79 @@ static bool tomoyo_file_matches_to_patte
first = false;
pattern_start = pattern;
}
- result = tomoyo_file_matches_to_pattern2(filename, filename_end,
- pattern_start, pattern_end);
+ result = tomoyo_file_matches_pattern2(filename, filename_end,
+ pattern_start, pattern_end);
return first ? result : !result;
}
/**
+ * tomoyo_path_matches_pattern2 - Do pathname pattern matching.
+ *
+ * @f: The start of string to check.
+ * @p: The start of pattern to compare.
+ *
+ * Returns true if @f matches @p, false otherwise.
+ */
+static bool tomoyo_path_matches_pattern2(const char *f, const char *p)
+{
+ const char *f_delimiter;
+ const char *p_delimiter;
+
+ while (*f && *p) {
+ f_delimiter = strchr(f, '/');
+ if (!f_delimiter)
+ f_delimiter = f + strlen(f);
+ p_delimiter = strchr(p, '/');
+ if (!p_delimiter)
+ p_delimiter = p + strlen(p);
+ if (*p == '\\' && *(p + 1) == '{')
+ goto recursive;
+ if (!tomoyo_file_matches_pattern(f, f_delimiter, p,
+ p_delimiter))
+ return false;
+ f = f_delimiter;
+ if (*f)
+ f++;
+ p = p_delimiter;
+ if (*p)
+ p++;
+ }
+ /* Ignore trailing "\*" and "\@" in @pattern. */
+ while (*p == '\\' &&
+ (*(p + 1) == '*' || *(p + 1) == '@'))
+ p += 2;
+ return !*f && !*p;
+ recursive:
+ /*
+ * The "\{" pattern is permitted only after '/' character.
+ * This guarantees that below "*(p - 1)" is safe.
+ * Also, the "\}" pattern is permitted only before '/' character
+ * so that "\{" + "\}" pair will not break the "\-" operator.
+ */
+ if (*(p - 1) != '/' || p_delimiter <= p + 3 || *p_delimiter != '/' ||
+ *(p_delimiter - 1) != '}' || *(p_delimiter - 2) != '\\')
+ return false; /* Bad pattern. */
+ do {
+ /* Compare current component with pattern. */
+ if (!tomoyo_file_matches_pattern(f, f_delimiter, p + 2,
+ p_delimiter - 2))
+ break;
+ /* Proceed to next component. */
+ f = f_delimiter;
+ if (!*f)
+ break;
+ f++;
+ /* Continue comparison. */
+ if (tomoyo_path_matches_pattern2(f, p_delimiter + 1))
+ return true;
+ f_delimiter = strchr(f, '/');
+ } while (f_delimiter);
+ return false; /* Not matched. */
+}
+
+/**
* tomoyo_path_matches_pattern - Check whether the given filename matches the given pattern.
+ *
* @filename: The filename to check.
* @pattern: The pattern to compare.
*
@@ -615,24 +679,24 @@ static bool tomoyo_file_matches_to_patte
* The following patterns are available.
* \\ \ itself.
* \ooo Octal representation of a byte.
- * \* More than or equals to 0 character other than '/'.
- * \@ More than or equals to 0 character other than '/' or '.'.
+ * \* Zero or more repetitions of characters other than '/'.
+ * \@ Zero or more repetitions of characters other than '/' or '.'.
* \? 1 byte character other than '/'.
- * \$ More than or equals to 1 decimal digit.
+ * \$ One or more repetitions of decimal digits.
* \+ 1 decimal digit.
- * \X More than or equals to 1 hexadecimal digit.
+ * \X One or more repetitions of hexadecimal digits.
* \x 1 hexadecimal digit.
- * \A More than or equals to 1 alphabet character.
+ * \A One or more repetitions of alphabet characters.
* \a 1 alphabet character.
+ *
* \- Subtraction operator.
+ *
+ * /\{dir\}/ '/' + 'One or more repetitions of dir/' (e.g. /dir/ /dir/dir/
+ * /dir/dir/dir/ ).
*/
bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename,
const struct tomoyo_path_info *pattern)
{
- /*
- if (!filename || !pattern)
- return false;
- */
const char *f = filename->name;
const char *p = pattern->name;
const int len = pattern->const_len;
@@ -640,37 +704,15 @@ bool tomoyo_path_matches_pattern(const s
/* If @pattern doesn't contain pattern, I can use strcmp(). */
if (!pattern->is_patterned)
return !tomoyo_pathcmp(filename, pattern);
- /* Dont compare if the number of '/' differs. */
- if (filename->depth != pattern->depth)
+ /* Don't compare directory and non-directory. */
+ if (filename->is_dir != pattern->is_dir)
return false;
/* Compare the initial length without patterns. */
if (strncmp(f, p, len))
return false;
f += len;
p += len;
- /* Main loop. Compare each directory component. */
- while (*f && *p) {
- const char *f_delimiter = strchr(f, '/');
- const char *p_delimiter = strchr(p, '/');
- if (!f_delimiter)
- f_delimiter = f + strlen(f);
- if (!p_delimiter)
- p_delimiter = p + strlen(p);
- if (!tomoyo_file_matches_to_pattern(f, f_delimiter,
- p, p_delimiter))
- return false;
- f = f_delimiter;
- if (*f)
- f++;
- p = p_delimiter;
- if (*p)
- p++;
- }
- /* Ignore trailing "\*" and "\@" in @pattern. */
- while (*p == '\\' &&
- (*(p + 1) == '*' || *(p + 1) == '@'))
- p += 2;
- return !*f && !*p;
+ return tomoyo_path_matches_pattern2(f, p);
}
/**
--- security-testing-2.6.orig/security/tomoyo/common.h
+++ security-testing-2.6/security/tomoyo/common.h
@@ -56,9 +56,6 @@ struct tomoyo_page_buffer {
* (5) "is_patterned" is a bool which is true if "name" contains wildcard
* characters, false otherwise. This allows TOMOYO to use "hash" and
* strcmp() for string comparison if "is_patterned" is false.
- * (6) "depth" is calculated using the number of "/" characters in "name".
- * This allows TOMOYO to avoid comparing two pathnames which never match
- * (e.g. whether "/var/www/html/index.html" matches "/tmp/sh-thd-\$").
*/
struct tomoyo_path_info {
const char *name;
@@ -66,7 +63,6 @@ struct tomoyo_path_info {
u16 const_len; /* = tomoyo_const_part_length(name) */
bool is_dir; /* = tomoyo_strendswith(name, "/") */
bool is_patterned; /* = tomoyo_path_contains_pattern(name) */
- u16 depth; /* = tomoyo_path_depth(name) */
};
/*
^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH] TOMOYO: Use RCU primitives for list operation
2009-10-13 11:37 ` [PATCH] TOMOYO: Add recursive directory matching operator support Tetsuo Handa
@ 2009-10-13 11:39 ` Tetsuo Handa
2009-10-13 11:41 ` [PATCH] TOMOYO: Bring memory allocation to outside semaphore Tetsuo Handa
2009-10-29 5:40 ` [PATCH] TOMOYO: Use RCU primitives for list operation Serge E. Hallyn
0 siblings, 2 replies; 50+ messages in thread
From: Tetsuo Handa @ 2009-10-13 11:39 UTC (permalink / raw)
To: linux-security-module; +Cc: linux-kernel
[PATCH] TOMOYO: Use RCU primitives for list operation
Remove down_read()/up_read() by replacing with RCU primitives.
SRCU based garbage collector will be added in the future.
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
security/tomoyo/common.c | 52 ++++++++++-----------------------------------
security/tomoyo/common.h | 14 ++++++------
security/tomoyo/domain.c | 38 ++++++++++----------------------
security/tomoyo/file.c | 50 ++++++++++++++-----------------------------
security/tomoyo/realpath.c | 4 ---
5 files changed, 49 insertions(+), 109 deletions(-)
--- security-testing-2.6.orig/security/tomoyo/common.c
+++ security-testing-2.6/security/tomoyo/common.c
@@ -365,9 +365,6 @@ bool tomoyo_is_domain_def(const unsigned
*
* @domainname: The domainname to find.
*
- * Caller must call down_read(&tomoyo_domain_list_lock); or
- * down_write(&tomoyo_domain_list_lock); .
- *
* Returns pointer to "struct tomoyo_domain_info" if found, NULL otherwise.
*/
struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname)
@@ -377,7 +374,7 @@ struct tomoyo_domain_info *tomoyo_find_d
name.name = domainname;
tomoyo_fill_path_info(&name);
- list_for_each_entry(domain, &tomoyo_domain_list, list) {
+ list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
if (!domain->is_deleted &&
!tomoyo_pathcmp(&name, domain->domainname))
return domain;
@@ -837,8 +834,7 @@ bool tomoyo_domain_quota_is_ok(struct to
if (!domain)
return true;
- down_read(&tomoyo_domain_acl_info_list_lock);
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
if (ptr->type & TOMOYO_ACL_DELETED)
continue;
switch (tomoyo_acl_type2(ptr)) {
@@ -891,7 +887,6 @@ bool tomoyo_domain_quota_is_ok(struct to
break;
}
}
- up_read(&tomoyo_domain_acl_info_list_lock);
if (count < tomoyo_check_flags(domain, TOMOYO_MAX_ACCEPT_ENTRY))
return true;
if (!domain->quota_warned) {
@@ -1143,7 +1138,7 @@ static int tomoyo_update_manager_entry(c
if (!saved_manager)
return -ENOMEM;
down_write(&tomoyo_policy_manager_list_lock);
- list_for_each_entry(ptr, &tomoyo_policy_manager_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, list) {
if (ptr->manager != saved_manager)
continue;
ptr->is_deleted = is_delete;
@@ -1159,7 +1154,7 @@ static int tomoyo_update_manager_entry(c
goto out;
new_entry->manager = saved_manager;
new_entry->is_domain = is_domain;
- list_add_tail(&new_entry->list, &tomoyo_policy_manager_list);
+ list_add_tail_rcu(&new_entry->list, &tomoyo_policy_manager_list);
error = 0;
out:
up_write(&tomoyo_policy_manager_list_lock);
@@ -1199,7 +1194,6 @@ static int tomoyo_read_manager_policy(st
if (head->read_eof)
return 0;
- down_read(&tomoyo_policy_manager_list_lock);
list_for_each_cookie(pos, head->read_var2,
&tomoyo_policy_manager_list) {
struct tomoyo_policy_manager_entry *ptr;
@@ -1211,7 +1205,6 @@ static int tomoyo_read_manager_policy(st
if (!done)
break;
}
- up_read(&tomoyo_policy_manager_list_lock);
head->read_eof = done;
return 0;
}
@@ -1234,29 +1227,25 @@ static bool tomoyo_is_policy_manager(voi
return true;
if (!tomoyo_manage_by_non_root && (task->cred->uid || task->cred->euid))
return false;
- down_read(&tomoyo_policy_manager_list_lock);
- list_for_each_entry(ptr, &tomoyo_policy_manager_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, list) {
if (!ptr->is_deleted && ptr->is_domain
&& !tomoyo_pathcmp(domainname, ptr->manager)) {
found = true;
break;
}
}
- up_read(&tomoyo_policy_manager_list_lock);
if (found)
return true;
exe = tomoyo_get_exe();
if (!exe)
return false;
- down_read(&tomoyo_policy_manager_list_lock);
- list_for_each_entry(ptr, &tomoyo_policy_manager_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, list) {
if (!ptr->is_deleted && !ptr->is_domain
&& !strcmp(exe, ptr->manager->name)) {
found = true;
break;
}
}
- up_read(&tomoyo_policy_manager_list_lock);
if (!found) { /* Reduce error messages. */
static pid_t last_pid;
const pid_t pid = current->pid;
@@ -1292,11 +1281,8 @@ static bool tomoyo_is_select_one(struct
domain = tomoyo_real_domain(p);
read_unlock(&tasklist_lock);
} else if (!strncmp(data, "domain=", 7)) {
- if (tomoyo_is_domain_def(data + 7)) {
- down_read(&tomoyo_domain_list_lock);
+ if (tomoyo_is_domain_def(data + 7))
domain = tomoyo_find_domain(data + 7);
- up_read(&tomoyo_domain_list_lock);
- }
} else
return false;
head->write_var1 = domain;
@@ -1310,13 +1296,11 @@ static bool tomoyo_is_select_one(struct
if (domain) {
struct tomoyo_domain_info *d;
head->read_var1 = NULL;
- down_read(&tomoyo_domain_list_lock);
- list_for_each_entry(d, &tomoyo_domain_list, list) {
+ list_for_each_entry_rcu(d, &tomoyo_domain_list, list) {
if (d == domain)
break;
head->read_var1 = &d->list;
}
- up_read(&tomoyo_domain_list_lock);
head->read_var2 = NULL;
head->read_bit = 0;
head->read_step = 0;
@@ -1342,7 +1326,7 @@ static int tomoyo_delete_domain(char *do
tomoyo_fill_path_info(&name);
down_write(&tomoyo_domain_list_lock);
/* Is there an active domain? */
- list_for_each_entry(domain, &tomoyo_domain_list, list) {
+ list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
/* Never delete tomoyo_kernel_domain */
if (domain == &tomoyo_kernel_domain)
continue;
@@ -1384,11 +1368,9 @@ static int tomoyo_write_domain_policy(st
domain = NULL;
if (is_delete)
tomoyo_delete_domain(data);
- else if (is_select) {
- down_read(&tomoyo_domain_list_lock);
+ else if (is_select)
domain = tomoyo_find_domain(data);
- up_read(&tomoyo_domain_list_lock);
- } else
+ else
domain = tomoyo_find_or_assign_new_domain(data, 0);
head->write_var1 = domain;
return 0;
@@ -1544,7 +1526,6 @@ static int tomoyo_read_domain_policy(str
return 0;
if (head->read_step == 0)
head->read_step = 1;
- down_read(&tomoyo_domain_list_lock);
list_for_each_cookie(dpos, head->read_var1, &tomoyo_domain_list) {
struct tomoyo_domain_info *domain;
const char *quota_exceeded = "";
@@ -1577,7 +1558,6 @@ acl_loop:
if (head->read_step == 3)
goto tail_mark;
/* Print ACL entries in the domain. */
- down_read(&tomoyo_domain_acl_info_list_lock);
list_for_each_cookie(apos, head->read_var2,
&domain->acl_info_list) {
struct tomoyo_acl_info *ptr
@@ -1587,7 +1567,6 @@ acl_loop:
if (!done)
break;
}
- up_read(&tomoyo_domain_acl_info_list_lock);
if (!done)
break;
head->read_step = 3;
@@ -1599,7 +1578,6 @@ tail_mark:
if (head->read_single_domain)
break;
}
- up_read(&tomoyo_domain_list_lock);
head->read_eof = done;
return 0;
}
@@ -1626,9 +1604,7 @@ static int tomoyo_write_domain_profile(s
if (!cp)
return -EINVAL;
*cp = '\0';
- down_read(&tomoyo_domain_list_lock);
domain = tomoyo_find_domain(cp + 1);
- up_read(&tomoyo_domain_list_lock);
if (strict_strtoul(data, 10, &profile))
return -EINVAL;
if (domain && profile < TOMOYO_MAX_PROFILES
@@ -1658,7 +1634,6 @@ static int tomoyo_read_domain_profile(st
if (head->read_eof)
return 0;
- down_read(&tomoyo_domain_list_lock);
list_for_each_cookie(pos, head->read_var1, &tomoyo_domain_list) {
struct tomoyo_domain_info *domain;
domain = list_entry(pos, struct tomoyo_domain_info, list);
@@ -1669,7 +1644,6 @@ static int tomoyo_read_domain_profile(st
if (!done)
break;
}
- up_read(&tomoyo_domain_list_lock);
head->read_eof = done;
return 0;
}
@@ -1889,15 +1863,13 @@ void tomoyo_load_policy(const char *file
tomoyo_policy_loaded = true;
{ /* Check all profiles currently assigned to domains are defined. */
struct tomoyo_domain_info *domain;
- down_read(&tomoyo_domain_list_lock);
- list_for_each_entry(domain, &tomoyo_domain_list, list) {
+ list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
const u8 profile = domain->profile;
if (tomoyo_profile_ptr[profile])
continue;
panic("Profile %u (used by '%s') not defined.\n",
profile, domain->domainname->name);
}
- up_read(&tomoyo_domain_list_lock);
}
}
--- security-testing-2.6.orig/security/tomoyo/common.h
+++ security-testing-2.6/security/tomoyo/common.h
@@ -442,16 +442,16 @@ extern struct tomoyo_domain_info tomoyo_
* @cookie: the &struct list_head to use as a cookie.
* @head: the head for your list.
*
- * Same with list_for_each() except that this primitive uses @cookie
+ * Same with list_for_each_rcu() except that this primitive uses @cookie
* so that we can continue iteration.
* @cookie must be NULL when iteration starts, and @cookie will become
* NULL when iteration finishes.
*/
-#define list_for_each_cookie(pos, cookie, head) \
- for (({ if (!cookie) \
- cookie = head; }), \
- pos = (cookie)->next; \
- prefetch(pos->next), pos != (head) || ((cookie) = NULL); \
- (cookie) = pos, pos = pos->next)
+#define list_for_each_cookie(pos, cookie, head) \
+ for (({ if (!cookie) \
+ cookie = head; }), \
+ pos = rcu_dereference((cookie)->next); \
+ prefetch(pos->next), pos != (head) || ((cookie) = NULL); \
+ (cookie) = pos, pos = rcu_dereference(pos->next))
#endif /* !defined(_SECURITY_TOMOYO_COMMON_H) */
--- security-testing-2.6.orig/security/tomoyo/domain.c
+++ security-testing-2.6/security/tomoyo/domain.c
@@ -246,7 +246,7 @@ static int tomoyo_update_domain_initiali
if (!saved_program)
return -ENOMEM;
down_write(&tomoyo_domain_initializer_list_lock);
- list_for_each_entry(ptr, &tomoyo_domain_initializer_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, list) {
if (ptr->is_not != is_not ||
ptr->domainname != saved_domainname ||
ptr->program != saved_program)
@@ -266,7 +266,7 @@ static int tomoyo_update_domain_initiali
new_entry->program = saved_program;
new_entry->is_not = is_not;
new_entry->is_last_name = is_last_name;
- list_add_tail(&new_entry->list, &tomoyo_domain_initializer_list);
+ list_add_tail_rcu(&new_entry->list, &tomoyo_domain_initializer_list);
error = 0;
out:
up_write(&tomoyo_domain_initializer_list_lock);
@@ -285,7 +285,6 @@ bool tomoyo_read_domain_initializer_poli
struct list_head *pos;
bool done = true;
- down_read(&tomoyo_domain_initializer_list_lock);
list_for_each_cookie(pos, head->read_var2,
&tomoyo_domain_initializer_list) {
const char *no;
@@ -308,7 +307,6 @@ bool tomoyo_read_domain_initializer_poli
if (!done)
break;
}
- up_read(&tomoyo_domain_initializer_list_lock);
return done;
}
@@ -355,8 +353,7 @@ static bool tomoyo_is_domain_initializer
struct tomoyo_domain_initializer_entry *ptr;
bool flag = false;
- down_read(&tomoyo_domain_initializer_list_lock);
- list_for_each_entry(ptr, &tomoyo_domain_initializer_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, list) {
if (ptr->is_deleted)
continue;
if (ptr->domainname) {
@@ -376,7 +373,6 @@ static bool tomoyo_is_domain_initializer
}
flag = true;
}
- up_read(&tomoyo_domain_initializer_list_lock);
return flag;
}
@@ -459,7 +455,7 @@ static int tomoyo_update_domain_keeper_e
if (!saved_domainname)
return -ENOMEM;
down_write(&tomoyo_domain_keeper_list_lock);
- list_for_each_entry(ptr, &tomoyo_domain_keeper_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, list) {
if (ptr->is_not != is_not ||
ptr->domainname != saved_domainname ||
ptr->program != saved_program)
@@ -479,7 +475,7 @@ static int tomoyo_update_domain_keeper_e
new_entry->program = saved_program;
new_entry->is_not = is_not;
new_entry->is_last_name = is_last_name;
- list_add_tail(&new_entry->list, &tomoyo_domain_keeper_list);
+ list_add_tail_rcu(&new_entry->list, &tomoyo_domain_keeper_list);
error = 0;
out:
up_write(&tomoyo_domain_keeper_list_lock);
@@ -519,7 +515,6 @@ bool tomoyo_read_domain_keeper_policy(st
struct list_head *pos;
bool done = true;
- down_read(&tomoyo_domain_keeper_list_lock);
list_for_each_cookie(pos, head->read_var2,
&tomoyo_domain_keeper_list) {
struct tomoyo_domain_keeper_entry *ptr;
@@ -542,7 +537,6 @@ bool tomoyo_read_domain_keeper_policy(st
if (!done)
break;
}
- up_read(&tomoyo_domain_keeper_list_lock);
return done;
}
@@ -563,8 +557,7 @@ static bool tomoyo_is_domain_keeper(cons
struct tomoyo_domain_keeper_entry *ptr;
bool flag = false;
- down_read(&tomoyo_domain_keeper_list_lock);
- list_for_each_entry(ptr, &tomoyo_domain_keeper_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, list) {
if (ptr->is_deleted)
continue;
if (!ptr->is_last_name) {
@@ -582,7 +575,6 @@ static bool tomoyo_is_domain_keeper(cons
}
flag = true;
}
- up_read(&tomoyo_domain_keeper_list_lock);
return flag;
}
@@ -646,7 +638,7 @@ static int tomoyo_update_alias_entry(con
if (!saved_original_name || !saved_aliased_name)
return -ENOMEM;
down_write(&tomoyo_alias_list_lock);
- list_for_each_entry(ptr, &tomoyo_alias_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_alias_list, list) {
if (ptr->original_name != saved_original_name ||
ptr->aliased_name != saved_aliased_name)
continue;
@@ -663,7 +655,7 @@ static int tomoyo_update_alias_entry(con
goto out;
new_entry->original_name = saved_original_name;
new_entry->aliased_name = saved_aliased_name;
- list_add_tail(&new_entry->list, &tomoyo_alias_list);
+ list_add_tail_rcu(&new_entry->list, &tomoyo_alias_list);
error = 0;
out:
up_write(&tomoyo_alias_list_lock);
@@ -682,7 +674,6 @@ bool tomoyo_read_alias_policy(struct tom
struct list_head *pos;
bool done = true;
- down_read(&tomoyo_alias_list_lock);
list_for_each_cookie(pos, head->read_var2, &tomoyo_alias_list) {
struct tomoyo_alias_entry *ptr;
@@ -695,7 +686,6 @@ bool tomoyo_read_alias_policy(struct tom
if (!done)
break;
}
- up_read(&tomoyo_alias_list_lock);
return done;
}
@@ -742,7 +732,7 @@ struct tomoyo_domain_info *tomoyo_find_o
if (!saved_domainname)
goto out;
/* Can I reuse memory of deleted domain? */
- list_for_each_entry(domain, &tomoyo_domain_list, list) {
+ list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
struct task_struct *p;
struct tomoyo_acl_info *ptr;
bool flag;
@@ -760,7 +750,7 @@ struct tomoyo_domain_info *tomoyo_find_o
read_unlock(&tasklist_lock);
if (flag)
continue;
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
ptr->type |= TOMOYO_ACL_DELETED;
}
tomoyo_set_domain_flag(domain, true, domain->flags);
@@ -776,7 +766,7 @@ struct tomoyo_domain_info *tomoyo_find_o
INIT_LIST_HEAD(&domain->acl_info_list);
domain->domainname = saved_domainname;
domain->profile = profile;
- list_add_tail(&domain->list, &tomoyo_domain_list);
+ list_add_tail_rcu(&domain->list, &tomoyo_domain_list);
}
out:
up_write(&tomoyo_domain_list_lock);
@@ -849,8 +839,7 @@ int tomoyo_find_next_domain(struct linux
if (tomoyo_pathcmp(&r, &s)) {
struct tomoyo_alias_entry *ptr;
/* Is this program allowed to be called via symbolic links? */
- down_read(&tomoyo_alias_list_lock);
- list_for_each_entry(ptr, &tomoyo_alias_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_alias_list, list) {
if (ptr->is_deleted ||
tomoyo_pathcmp(&r, ptr->original_name) ||
tomoyo_pathcmp(&s, ptr->aliased_name))
@@ -861,7 +850,6 @@ int tomoyo_find_next_domain(struct linux
tomoyo_fill_path_info(&r);
break;
}
- up_read(&tomoyo_alias_list_lock);
}
/* Check execute permission. */
@@ -892,9 +880,7 @@ int tomoyo_find_next_domain(struct linux
}
if (domain || strlen(new_domain_name) >= TOMOYO_MAX_PATHNAME_LEN)
goto done;
- down_read(&tomoyo_domain_list_lock);
domain = tomoyo_find_domain(new_domain_name);
- up_read(&tomoyo_domain_list_lock);
if (domain)
goto done;
if (is_enforce)
--- security-testing-2.6.orig/security/tomoyo/file.c
+++ security-testing-2.6/security/tomoyo/file.c
@@ -220,7 +220,7 @@ static int tomoyo_update_globally_readab
if (!saved_filename)
return -ENOMEM;
down_write(&tomoyo_globally_readable_list_lock);
- list_for_each_entry(ptr, &tomoyo_globally_readable_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, list) {
if (ptr->filename != saved_filename)
continue;
ptr->is_deleted = is_delete;
@@ -235,7 +235,7 @@ static int tomoyo_update_globally_readab
if (!new_entry)
goto out;
new_entry->filename = saved_filename;
- list_add_tail(&new_entry->list, &tomoyo_globally_readable_list);
+ list_add_tail_rcu(&new_entry->list, &tomoyo_globally_readable_list);
error = 0;
out:
up_write(&tomoyo_globally_readable_list_lock);
@@ -254,15 +254,13 @@ static bool tomoyo_is_globally_readable_
{
struct tomoyo_globally_readable_file_entry *ptr;
bool found = false;
- down_read(&tomoyo_globally_readable_list_lock);
- list_for_each_entry(ptr, &tomoyo_globally_readable_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, list) {
if (!ptr->is_deleted &&
tomoyo_path_matches_pattern(filename, ptr->filename)) {
found = true;
break;
}
}
- up_read(&tomoyo_globally_readable_list_lock);
return found;
}
@@ -291,7 +289,6 @@ bool tomoyo_read_globally_readable_polic
struct list_head *pos;
bool done = true;
- down_read(&tomoyo_globally_readable_list_lock);
list_for_each_cookie(pos, head->read_var2,
&tomoyo_globally_readable_list) {
struct tomoyo_globally_readable_file_entry *ptr;
@@ -305,7 +302,6 @@ bool tomoyo_read_globally_readable_polic
if (!done)
break;
}
- up_read(&tomoyo_globally_readable_list_lock);
return done;
}
@@ -363,7 +359,7 @@ static int tomoyo_update_file_pattern_en
if (!saved_pattern)
return -ENOMEM;
down_write(&tomoyo_pattern_list_lock);
- list_for_each_entry(ptr, &tomoyo_pattern_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) {
if (saved_pattern != ptr->pattern)
continue;
ptr->is_deleted = is_delete;
@@ -378,7 +374,7 @@ static int tomoyo_update_file_pattern_en
if (!new_entry)
goto out;
new_entry->pattern = saved_pattern;
- list_add_tail(&new_entry->list, &tomoyo_pattern_list);
+ list_add_tail_rcu(&new_entry->list, &tomoyo_pattern_list);
error = 0;
out:
up_write(&tomoyo_pattern_list_lock);
@@ -398,8 +394,7 @@ tomoyo_get_file_pattern(const struct tom
struct tomoyo_pattern_entry *ptr;
const struct tomoyo_path_info *pattern = NULL;
- down_read(&tomoyo_pattern_list_lock);
- list_for_each_entry(ptr, &tomoyo_pattern_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) {
if (ptr->is_deleted)
continue;
if (!tomoyo_path_matches_pattern(filename, ptr->pattern))
@@ -412,7 +407,6 @@ tomoyo_get_file_pattern(const struct tom
break;
}
}
- up_read(&tomoyo_pattern_list_lock);
if (pattern)
filename = pattern;
return filename;
@@ -443,7 +437,6 @@ bool tomoyo_read_file_pattern(struct tom
struct list_head *pos;
bool done = true;
- down_read(&tomoyo_pattern_list_lock);
list_for_each_cookie(pos, head->read_var2, &tomoyo_pattern_list) {
struct tomoyo_pattern_entry *ptr;
ptr = list_entry(pos, struct tomoyo_pattern_entry, list);
@@ -454,7 +447,6 @@ bool tomoyo_read_file_pattern(struct tom
if (!done)
break;
}
- up_read(&tomoyo_pattern_list_lock);
return done;
}
@@ -511,7 +503,7 @@ static int tomoyo_update_no_rewrite_entr
if (!saved_pattern)
return -ENOMEM;
down_write(&tomoyo_no_rewrite_list_lock);
- list_for_each_entry(ptr, &tomoyo_no_rewrite_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, list) {
if (ptr->pattern != saved_pattern)
continue;
ptr->is_deleted = is_delete;
@@ -526,7 +518,7 @@ static int tomoyo_update_no_rewrite_entr
if (!new_entry)
goto out;
new_entry->pattern = saved_pattern;
- list_add_tail(&new_entry->list, &tomoyo_no_rewrite_list);
+ list_add_tail_rcu(&new_entry->list, &tomoyo_no_rewrite_list);
error = 0;
out:
up_write(&tomoyo_no_rewrite_list_lock);
@@ -546,8 +538,7 @@ static bool tomoyo_is_no_rewrite_file(co
struct tomoyo_no_rewrite_entry *ptr;
bool found = false;
- down_read(&tomoyo_no_rewrite_list_lock);
- list_for_each_entry(ptr, &tomoyo_no_rewrite_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, list) {
if (ptr->is_deleted)
continue;
if (!tomoyo_path_matches_pattern(filename, ptr->pattern))
@@ -555,7 +546,6 @@ static bool tomoyo_is_no_rewrite_file(co
found = true;
break;
}
- up_read(&tomoyo_no_rewrite_list_lock);
return found;
}
@@ -584,7 +574,6 @@ bool tomoyo_read_no_rewrite_policy(struc
struct list_head *pos;
bool done = true;
- down_read(&tomoyo_no_rewrite_list_lock);
list_for_each_cookie(pos, head->read_var2, &tomoyo_no_rewrite_list) {
struct tomoyo_no_rewrite_entry *ptr;
ptr = list_entry(pos, struct tomoyo_no_rewrite_entry, list);
@@ -595,7 +584,6 @@ bool tomoyo_read_no_rewrite_policy(struc
if (!done)
break;
}
- up_read(&tomoyo_no_rewrite_list_lock);
return done;
}
@@ -661,8 +649,7 @@ static int tomoyo_check_single_path_acl2
struct tomoyo_acl_info *ptr;
int error = -EPERM;
- down_read(&tomoyo_domain_acl_info_list_lock);
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
struct tomoyo_single_path_acl_record *acl;
if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL)
continue;
@@ -680,7 +667,6 @@ static int tomoyo_check_single_path_acl2
error = 0;
break;
}
- up_read(&tomoyo_domain_acl_info_list_lock);
return error;
}
@@ -848,7 +834,7 @@ static int tomoyo_update_single_path_acl
down_write(&tomoyo_domain_acl_info_list_lock);
if (is_delete)
goto delete;
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
if (tomoyo_acl_type1(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL)
continue;
acl = container_of(ptr, struct tomoyo_single_path_acl_record,
@@ -875,12 +861,12 @@ static int tomoyo_update_single_path_acl
if (perm == (1 << TOMOYO_TYPE_READ_WRITE_ACL))
acl->perm |= rw_mask;
acl->filename = saved_filename;
- list_add_tail(&acl->head.list, &domain->acl_info_list);
+ list_add_tail_rcu(&acl->head.list, &domain->acl_info_list);
error = 0;
goto out;
delete:
error = -ENOENT;
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL)
continue;
acl = container_of(ptr, struct tomoyo_single_path_acl_record,
@@ -937,7 +923,7 @@ static int tomoyo_update_double_path_acl
down_write(&tomoyo_domain_acl_info_list_lock);
if (is_delete)
goto delete;
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
if (tomoyo_acl_type1(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL)
continue;
acl = container_of(ptr, struct tomoyo_double_path_acl_record,
@@ -960,12 +946,12 @@ static int tomoyo_update_double_path_acl
acl->perm = perm;
acl->filename1 = saved_filename1;
acl->filename2 = saved_filename2;
- list_add_tail(&acl->head.list, &domain->acl_info_list);
+ list_add_tail_rcu(&acl->head.list, &domain->acl_info_list);
error = 0;
goto out;
delete:
error = -ENOENT;
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL)
continue;
acl = container_of(ptr, struct tomoyo_double_path_acl_record,
@@ -1025,8 +1011,7 @@ static int tomoyo_check_double_path_acl(
if (!tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE))
return 0;
- down_read(&tomoyo_domain_acl_info_list_lock);
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
struct tomoyo_double_path_acl_record *acl;
if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL)
continue;
@@ -1041,7 +1026,6 @@ static int tomoyo_check_double_path_acl(
error = 0;
break;
}
- up_read(&tomoyo_domain_acl_info_list_lock);
return error;
}
--- security-testing-2.6.orig/security/tomoyo/realpath.c
+++ security-testing-2.6/security/tomoyo/realpath.c
@@ -387,11 +387,9 @@ void __init tomoyo_realpath_init(void)
INIT_LIST_HEAD(&tomoyo_name_list[i]);
INIT_LIST_HEAD(&tomoyo_kernel_domain.acl_info_list);
tomoyo_kernel_domain.domainname = tomoyo_save_name(TOMOYO_ROOT_NAME);
- list_add_tail(&tomoyo_kernel_domain.list, &tomoyo_domain_list);
- down_read(&tomoyo_domain_list_lock);
+ list_add_tail_rcu(&tomoyo_kernel_domain.list, &tomoyo_domain_list);
if (tomoyo_find_domain(TOMOYO_ROOT_NAME) != &tomoyo_kernel_domain)
panic("Can't register tomoyo_kernel_domain");
- up_read(&tomoyo_domain_list_lock);
}
/* Memory allocated for temporary purpose. */
^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH] TOMOYO: Bring memory allocation to outside semaphore
2009-10-13 11:39 ` [PATCH] TOMOYO: Use RCU primitives for list operation Tetsuo Handa
@ 2009-10-13 11:41 ` Tetsuo Handa
2009-10-29 5:40 ` [PATCH] TOMOYO: Use RCU primitives for list operation Serge E. Hallyn
1 sibling, 0 replies; 50+ messages in thread
From: Tetsuo Handa @ 2009-10-13 11:41 UTC (permalink / raw)
To: linux-security-module; +Cc: linux-kernel
[PATCH] TOMOYO: Bring memory allocation to outside semaphore
This patch brings kmalloc(GFP_KERNEL) to outside rw_semaphores.
Since readers no longer need to call down_read()/up_read() and writers no
longer sleep inside down_write()/up_write(), this patch also replaces various
rw_semaphore with single tomoyo_policy_lock mutex.
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
security/tomoyo/common.c | 87 +++++++++----------------
security/tomoyo/common.h | 11 +--
security/tomoyo/domain.c | 137 ++++++++++++++++-----------------------
security/tomoyo/file.c | 124 ++++++++++++++++++++----------------
security/tomoyo/realpath.c | 155 +++++++++++++++------------------------------
security/tomoyo/realpath.h | 6 -
6 files changed, 216 insertions(+), 304 deletions(-)
--- security-testing-2.6.orig/security/tomoyo/common.c
+++ security-testing-2.6/security/tomoyo/common.c
@@ -908,25 +908,28 @@ bool tomoyo_domain_quota_is_ok(struct to
static struct tomoyo_profile *tomoyo_find_or_assign_new_profile(const unsigned
int profile)
{
- static DEFINE_MUTEX(lock);
- struct tomoyo_profile *ptr = NULL;
- int i;
+ struct tomoyo_profile *ptr;
+ struct tomoyo_profile *entry;
if (profile >= TOMOYO_MAX_PROFILES)
return NULL;
- mutex_lock(&lock);
ptr = tomoyo_profile_ptr[profile];
if (ptr)
- goto ok;
- ptr = tomoyo_alloc_element(sizeof(*ptr));
- if (!ptr)
- goto ok;
- for (i = 0; i < TOMOYO_MAX_CONTROL_INDEX; i++)
- ptr->value[i] = tomoyo_control_array[i].current_value;
- mb(); /* Avoid out-of-order execution. */
- tomoyo_profile_ptr[profile] = ptr;
- ok:
- mutex_unlock(&lock);
+ return ptr;
+ entry = kmalloc(sizeof(*ptr), GFP_KERNEL);
+ mutex_lock(&tomoyo_policy_lock);
+ ptr = tomoyo_profile_ptr[profile];
+ if (!ptr && tomoyo_memory_ok(entry)) {
+ int i;
+ ptr = entry;
+ for (i = 0; i < TOMOYO_MAX_CONTROL_INDEX; i++)
+ ptr->value[i] = tomoyo_control_array[i].current_value;
+ mb(); /* Avoid out-of-order execution. */
+ tomoyo_profile_ptr[profile] = ptr;
+ entry = NULL;
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+ kfree(entry);
return ptr;
}
@@ -1107,7 +1110,6 @@ struct tomoyo_policy_manager_entry {
* # cat /sys/kernel/security/tomoyo/manager
*/
static LIST_HEAD(tomoyo_policy_manager_list);
-static DECLARE_RWSEM(tomoyo_policy_manager_list_lock);
/**
* tomoyo_update_manager_entry - Add a manager entry.
@@ -1120,7 +1122,7 @@ static DECLARE_RWSEM(tomoyo_policy_manag
static int tomoyo_update_manager_entry(const char *manager,
const bool is_delete)
{
- struct tomoyo_policy_manager_entry *new_entry;
+ struct tomoyo_policy_manager_entry *entry;
struct tomoyo_policy_manager_entry *ptr;
const struct tomoyo_path_info *saved_manager;
int error = -ENOMEM;
@@ -1137,7 +1139,8 @@ static int tomoyo_update_manager_entry(c
saved_manager = tomoyo_save_name(manager);
if (!saved_manager)
return -ENOMEM;
- down_write(&tomoyo_policy_manager_list_lock);
+ entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+ mutex_lock(&tomoyo_policy_lock);
list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, list) {
if (ptr->manager != saved_manager)
continue;
@@ -1149,15 +1152,16 @@ static int tomoyo_update_manager_entry(c
error = -ENOENT;
goto out;
}
- new_entry = tomoyo_alloc_element(sizeof(*new_entry));
- if (!new_entry)
- goto out;
- new_entry->manager = saved_manager;
- new_entry->is_domain = is_domain;
- list_add_tail_rcu(&new_entry->list, &tomoyo_policy_manager_list);
- error = 0;
+ if (tomoyo_memory_ok(entry)) {
+ entry->manager = saved_manager;
+ entry->is_domain = is_domain;
+ list_add_tail_rcu(&entry->list, &tomoyo_policy_manager_list);
+ entry = NULL;
+ error = 0;
+ }
out:
- up_write(&tomoyo_policy_manager_list_lock);
+ mutex_unlock(&tomoyo_policy_lock);
+ kfree(entry);
return error;
}
@@ -1324,7 +1328,7 @@ static int tomoyo_delete_domain(char *do
name.name = domainname;
tomoyo_fill_path_info(&name);
- down_write(&tomoyo_domain_list_lock);
+ mutex_lock(&tomoyo_policy_lock);
/* Is there an active domain? */
list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
/* Never delete tomoyo_kernel_domain */
@@ -1336,7 +1340,7 @@ static int tomoyo_delete_domain(char *do
domain->is_deleted = true;
break;
}
- up_write(&tomoyo_domain_list_lock);
+ mutex_unlock(&tomoyo_policy_lock);
return 0;
}
@@ -2133,35 +2137,6 @@ static int tomoyo_close_control(struct f
}
/**
- * tomoyo_alloc_acl_element - Allocate permanent memory for ACL entry.
- *
- * @acl_type: Type of ACL entry.
- *
- * Returns pointer to the ACL entry on success, NULL otherwise.
- */
-void *tomoyo_alloc_acl_element(const u8 acl_type)
-{
- int len;
- struct tomoyo_acl_info *ptr;
-
- switch (acl_type) {
- case TOMOYO_TYPE_SINGLE_PATH_ACL:
- len = sizeof(struct tomoyo_single_path_acl_record);
- break;
- case TOMOYO_TYPE_DOUBLE_PATH_ACL:
- len = sizeof(struct tomoyo_double_path_acl_record);
- break;
- default:
- return NULL;
- }
- ptr = tomoyo_alloc_element(len);
- if (!ptr)
- return NULL;
- ptr->type = acl_type;
- return ptr;
-}
-
-/**
* tomoyo_open - open() for /sys/kernel/security/tomoyo/ interface.
*
* @inode: Pointer to "struct inode".
--- security-testing-2.6.orig/security/tomoyo/common.h
+++ security-testing-2.6/security/tomoyo/common.h
@@ -307,6 +307,8 @@ bool tomoyo_is_correct_path(const char *
const char *function);
/* Check whether the token can be a domainname. */
bool tomoyo_is_domain_def(const unsigned char *buffer);
+/* Check memory quota. */
+bool tomoyo_memory_ok(void *ptr);
/* Check whether the given filename matches the given pattern. */
bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename,
const struct tomoyo_path_info *pattern);
@@ -370,8 +372,6 @@ struct tomoyo_domain_info *tomoyo_find_o
/* Check mode for specified functionality. */
unsigned int tomoyo_check_flags(const struct tomoyo_domain_info *domain,
const u8 index);
-/* Allocate memory for structures. */
-void *tomoyo_alloc_acl_element(const u8 acl_type);
/* Fill in "struct tomoyo_path_info" members. */
void tomoyo_fill_path_info(struct tomoyo_path_info *ptr);
/* Run policy loader when /sbin/init starts. */
@@ -423,12 +423,11 @@ static inline bool tomoyo_is_invalid(con
return c && (c <= ' ' || c >= 127);
}
+/* Lock for modifying policy. */
+extern struct mutex tomoyo_policy_lock;
+
/* The list for "struct tomoyo_domain_info". */
extern struct list_head tomoyo_domain_list;
-extern struct rw_semaphore tomoyo_domain_list_lock;
-
-/* Lock for domain->acl_info_list. */
-extern struct rw_semaphore tomoyo_domain_acl_info_list_lock;
/* Has /sbin/init started? */
extern bool tomoyo_policy_loaded;
--- security-testing-2.6.orig/security/tomoyo/domain.c
+++ security-testing-2.6/security/tomoyo/domain.c
@@ -58,7 +58,8 @@ struct tomoyo_domain_info tomoyo_kernel_
* exceptions.
*/
LIST_HEAD(tomoyo_domain_list);
-DECLARE_RWSEM(tomoyo_domain_list_lock);
+/* Lock for modifying policy. */
+DEFINE_MUTEX(tomoyo_policy_lock);
/*
* tomoyo_domain_initializer_entry is a structure which is used for holding
@@ -206,7 +207,6 @@ const char *tomoyo_get_last_name(const s
* unless executed from "<kernel> /etc/rc.d/init.d/httpd" domain.
*/
static LIST_HEAD(tomoyo_domain_initializer_list);
-static DECLARE_RWSEM(tomoyo_domain_initializer_list_lock);
/**
* tomoyo_update_domain_initializer_entry - Update "struct tomoyo_domain_initializer_entry" list.
@@ -223,7 +223,7 @@ static int tomoyo_update_domain_initiali
const bool is_not,
const bool is_delete)
{
- struct tomoyo_domain_initializer_entry *new_entry;
+ struct tomoyo_domain_initializer_entry *entry = NULL;
struct tomoyo_domain_initializer_entry *ptr;
const struct tomoyo_path_info *saved_program;
const struct tomoyo_path_info *saved_domainname = NULL;
@@ -245,7 +245,9 @@ static int tomoyo_update_domain_initiali
saved_program = tomoyo_save_name(program);
if (!saved_program)
return -ENOMEM;
- down_write(&tomoyo_domain_initializer_list_lock);
+ if (!is_delete)
+ entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+ mutex_lock(&tomoyo_policy_lock);
list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, list) {
if (ptr->is_not != is_not ||
ptr->domainname != saved_domainname ||
@@ -259,17 +261,19 @@ static int tomoyo_update_domain_initiali
error = -ENOENT;
goto out;
}
- new_entry = tomoyo_alloc_element(sizeof(*new_entry));
- if (!new_entry)
- goto out;
- new_entry->domainname = saved_domainname;
- new_entry->program = saved_program;
- new_entry->is_not = is_not;
- new_entry->is_last_name = is_last_name;
- list_add_tail_rcu(&new_entry->list, &tomoyo_domain_initializer_list);
- error = 0;
+ if (tomoyo_memory_ok(entry)) {
+ entry->domainname = saved_domainname;
+ entry->program = saved_program;
+ entry->is_not = is_not;
+ entry->is_last_name = is_last_name;
+ list_add_tail_rcu(&entry->list,
+ &tomoyo_domain_initializer_list);
+ entry = NULL;
+ error = 0;
+ }
out:
- up_write(&tomoyo_domain_initializer_list_lock);
+ mutex_unlock(&tomoyo_policy_lock);
+ kfree(entry);
return error;
}
@@ -415,7 +419,6 @@ static bool tomoyo_is_domain_initializer
* explicitly specified by "initialize_domain".
*/
static LIST_HEAD(tomoyo_domain_keeper_list);
-static DECLARE_RWSEM(tomoyo_domain_keeper_list_lock);
/**
* tomoyo_update_domain_keeper_entry - Update "struct tomoyo_domain_keeper_entry" list.
@@ -432,7 +435,7 @@ static int tomoyo_update_domain_keeper_e
const bool is_not,
const bool is_delete)
{
- struct tomoyo_domain_keeper_entry *new_entry;
+ struct tomoyo_domain_keeper_entry *entry = NULL;
struct tomoyo_domain_keeper_entry *ptr;
const struct tomoyo_path_info *saved_domainname;
const struct tomoyo_path_info *saved_program = NULL;
@@ -454,7 +457,9 @@ static int tomoyo_update_domain_keeper_e
saved_domainname = tomoyo_save_name(domainname);
if (!saved_domainname)
return -ENOMEM;
- down_write(&tomoyo_domain_keeper_list_lock);
+ if (!is_delete)
+ entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+ mutex_lock(&tomoyo_policy_lock);
list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, list) {
if (ptr->is_not != is_not ||
ptr->domainname != saved_domainname ||
@@ -468,17 +473,18 @@ static int tomoyo_update_domain_keeper_e
error = -ENOENT;
goto out;
}
- new_entry = tomoyo_alloc_element(sizeof(*new_entry));
- if (!new_entry)
- goto out;
- new_entry->domainname = saved_domainname;
- new_entry->program = saved_program;
- new_entry->is_not = is_not;
- new_entry->is_last_name = is_last_name;
- list_add_tail_rcu(&new_entry->list, &tomoyo_domain_keeper_list);
- error = 0;
+ if (tomoyo_memory_ok(entry)) {
+ entry->domainname = saved_domainname;
+ entry->program = saved_program;
+ entry->is_not = is_not;
+ entry->is_last_name = is_last_name;
+ list_add_tail_rcu(&entry->list, &tomoyo_domain_keeper_list);
+ entry = NULL;
+ error = 0;
+ }
out:
- up_write(&tomoyo_domain_keeper_list_lock);
+ mutex_unlock(&tomoyo_policy_lock);
+ kfree(entry);
return error;
}
@@ -609,7 +615,6 @@ static bool tomoyo_is_domain_keeper(cons
* execve() succeeds is calculated using /bin/cat rather than /bin/busybox .
*/
static LIST_HEAD(tomoyo_alias_list);
-static DECLARE_RWSEM(tomoyo_alias_list_lock);
/**
* tomoyo_update_alias_entry - Update "struct tomoyo_alias_entry" list.
@@ -624,7 +629,7 @@ static int tomoyo_update_alias_entry(con
const char *aliased_name,
const bool is_delete)
{
- struct tomoyo_alias_entry *new_entry;
+ struct tomoyo_alias_entry *entry = NULL;
struct tomoyo_alias_entry *ptr;
const struct tomoyo_path_info *saved_original_name;
const struct tomoyo_path_info *saved_aliased_name;
@@ -637,7 +642,9 @@ static int tomoyo_update_alias_entry(con
saved_aliased_name = tomoyo_save_name(aliased_name);
if (!saved_original_name || !saved_aliased_name)
return -ENOMEM;
- down_write(&tomoyo_alias_list_lock);
+ if (!is_delete)
+ entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+ mutex_lock(&tomoyo_policy_lock);
list_for_each_entry_rcu(ptr, &tomoyo_alias_list, list) {
if (ptr->original_name != saved_original_name ||
ptr->aliased_name != saved_aliased_name)
@@ -650,15 +657,16 @@ static int tomoyo_update_alias_entry(con
error = -ENOENT;
goto out;
}
- new_entry = tomoyo_alloc_element(sizeof(*new_entry));
- if (!new_entry)
- goto out;
- new_entry->original_name = saved_original_name;
- new_entry->aliased_name = saved_aliased_name;
- list_add_tail_rcu(&new_entry->list, &tomoyo_alias_list);
- error = 0;
+ if (tomoyo_memory_ok(entry)) {
+ entry->original_name = saved_original_name;
+ entry->aliased_name = saved_aliased_name;
+ list_add_tail_rcu(&entry->list, &tomoyo_alias_list);
+ entry = NULL;
+ error = 0;
+ }
out:
- up_write(&tomoyo_alias_list_lock);
+ mutex_unlock(&tomoyo_policy_lock);
+ kfree(entry);
return error;
}
@@ -719,57 +727,28 @@ struct tomoyo_domain_info *tomoyo_find_o
domainname,
const u8 profile)
{
- struct tomoyo_domain_info *domain = NULL;
+ struct tomoyo_domain_info *entry;
+ struct tomoyo_domain_info *domain;
const struct tomoyo_path_info *saved_domainname;
- down_write(&tomoyo_domain_list_lock);
- domain = tomoyo_find_domain(domainname);
- if (domain)
- goto out;
if (!tomoyo_is_correct_domain(domainname, __func__))
- goto out;
+ return NULL;
saved_domainname = tomoyo_save_name(domainname);
if (!saved_domainname)
- goto out;
- /* Can I reuse memory of deleted domain? */
- list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
- struct task_struct *p;
- struct tomoyo_acl_info *ptr;
- bool flag;
- if (!domain->is_deleted ||
- domain->domainname != saved_domainname)
- continue;
- flag = false;
- read_lock(&tasklist_lock);
- for_each_process(p) {
- if (tomoyo_real_domain(p) != domain)
- continue;
- flag = true;
- break;
- }
- read_unlock(&tasklist_lock);
- if (flag)
- continue;
- list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
- ptr->type |= TOMOYO_ACL_DELETED;
- }
- tomoyo_set_domain_flag(domain, true, domain->flags);
- domain->profile = profile;
- domain->quota_warned = false;
- mb(); /* Avoid out-of-order execution. */
- domain->is_deleted = false;
- goto out;
- }
- /* No memory reusable. Create using new memory. */
- domain = tomoyo_alloc_element(sizeof(*domain));
- if (domain) {
+ return NULL;
+ entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+ mutex_lock(&tomoyo_policy_lock);
+ domain = tomoyo_find_domain(domainname);
+ if (!domain && tomoyo_memory_ok(entry)) {
+ domain = entry;
INIT_LIST_HEAD(&domain->acl_info_list);
domain->domainname = saved_domainname;
domain->profile = profile;
list_add_tail_rcu(&domain->list, &tomoyo_domain_list);
+ entry = NULL;
}
- out:
- up_write(&tomoyo_domain_list_lock);
+ mutex_unlock(&tomoyo_policy_lock);
+ kfree(entry);
return domain;
}
--- security-testing-2.6.orig/security/tomoyo/file.c
+++ security-testing-2.6/security/tomoyo/file.c
@@ -159,9 +159,6 @@ static struct tomoyo_path_info *tomoyo_g
return NULL;
}
-/* Lock for domain->acl_info_list. */
-DECLARE_RWSEM(tomoyo_domain_acl_info_list_lock);
-
static int tomoyo_update_double_path_acl(const u8 type, const char *filename1,
const char *filename2,
struct tomoyo_domain_info *
@@ -196,7 +193,6 @@ static int tomoyo_update_single_path_acl
* belongs to.
*/
static LIST_HEAD(tomoyo_globally_readable_list);
-static DECLARE_RWSEM(tomoyo_globally_readable_list_lock);
/**
* tomoyo_update_globally_readable_entry - Update "struct tomoyo_globally_readable_file_entry" list.
@@ -209,7 +205,7 @@ static DECLARE_RWSEM(tomoyo_globally_rea
static int tomoyo_update_globally_readable_entry(const char *filename,
const bool is_delete)
{
- struct tomoyo_globally_readable_file_entry *new_entry;
+ struct tomoyo_globally_readable_file_entry *entry = NULL;
struct tomoyo_globally_readable_file_entry *ptr;
const struct tomoyo_path_info *saved_filename;
int error = -ENOMEM;
@@ -219,7 +215,9 @@ static int tomoyo_update_globally_readab
saved_filename = tomoyo_save_name(filename);
if (!saved_filename)
return -ENOMEM;
- down_write(&tomoyo_globally_readable_list_lock);
+ if (!is_delete)
+ entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+ mutex_lock(&tomoyo_policy_lock);
list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, list) {
if (ptr->filename != saved_filename)
continue;
@@ -231,14 +229,15 @@ static int tomoyo_update_globally_readab
error = -ENOENT;
goto out;
}
- new_entry = tomoyo_alloc_element(sizeof(*new_entry));
- if (!new_entry)
- goto out;
- new_entry->filename = saved_filename;
- list_add_tail_rcu(&new_entry->list, &tomoyo_globally_readable_list);
- error = 0;
+ if (tomoyo_memory_ok(entry)) {
+ entry->filename = saved_filename;
+ list_add_tail_rcu(&entry->list, &tomoyo_globally_readable_list);
+ entry = NULL;
+ error = 0;
+ }
out:
- up_write(&tomoyo_globally_readable_list_lock);
+ mutex_unlock(&tomoyo_policy_lock);
+ kfree(entry);
return error;
}
@@ -335,7 +334,6 @@ bool tomoyo_read_globally_readable_polic
* current process from accessing other process's information.
*/
static LIST_HEAD(tomoyo_pattern_list);
-static DECLARE_RWSEM(tomoyo_pattern_list_lock);
/**
* tomoyo_update_file_pattern_entry - Update "struct tomoyo_pattern_entry" list.
@@ -348,7 +346,7 @@ static DECLARE_RWSEM(tomoyo_pattern_list
static int tomoyo_update_file_pattern_entry(const char *pattern,
const bool is_delete)
{
- struct tomoyo_pattern_entry *new_entry;
+ struct tomoyo_pattern_entry *entry = NULL;
struct tomoyo_pattern_entry *ptr;
const struct tomoyo_path_info *saved_pattern;
int error = -ENOMEM;
@@ -358,7 +356,9 @@ static int tomoyo_update_file_pattern_en
saved_pattern = tomoyo_save_name(pattern);
if (!saved_pattern)
return -ENOMEM;
- down_write(&tomoyo_pattern_list_lock);
+ if (!is_delete)
+ entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+ mutex_lock(&tomoyo_policy_lock);
list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) {
if (saved_pattern != ptr->pattern)
continue;
@@ -370,14 +370,15 @@ static int tomoyo_update_file_pattern_en
error = -ENOENT;
goto out;
}
- new_entry = tomoyo_alloc_element(sizeof(*new_entry));
- if (!new_entry)
- goto out;
- new_entry->pattern = saved_pattern;
- list_add_tail_rcu(&new_entry->list, &tomoyo_pattern_list);
- error = 0;
+ if (tomoyo_memory_ok(entry)) {
+ entry->pattern = saved_pattern;
+ list_add_tail_rcu(&entry->list, &tomoyo_pattern_list);
+ entry = NULL;
+ error = 0;
+ }
out:
- up_write(&tomoyo_pattern_list_lock);
+ mutex_unlock(&tomoyo_policy_lock);
+ kfree(entry);
return error;
}
@@ -480,7 +481,6 @@ bool tomoyo_read_file_pattern(struct tom
* need to worry whether the file is already unlink()ed or not.
*/
static LIST_HEAD(tomoyo_no_rewrite_list);
-static DECLARE_RWSEM(tomoyo_no_rewrite_list_lock);
/**
* tomoyo_update_no_rewrite_entry - Update "struct tomoyo_no_rewrite_entry" list.
@@ -493,7 +493,8 @@ static DECLARE_RWSEM(tomoyo_no_rewrite_l
static int tomoyo_update_no_rewrite_entry(const char *pattern,
const bool is_delete)
{
- struct tomoyo_no_rewrite_entry *new_entry, *ptr;
+ struct tomoyo_no_rewrite_entry *entry = NULL;
+ struct tomoyo_no_rewrite_entry *ptr;
const struct tomoyo_path_info *saved_pattern;
int error = -ENOMEM;
@@ -502,7 +503,9 @@ static int tomoyo_update_no_rewrite_entr
saved_pattern = tomoyo_save_name(pattern);
if (!saved_pattern)
return -ENOMEM;
- down_write(&tomoyo_no_rewrite_list_lock);
+ if (!is_delete)
+ entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+ mutex_lock(&tomoyo_policy_lock);
list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, list) {
if (ptr->pattern != saved_pattern)
continue;
@@ -514,14 +517,15 @@ static int tomoyo_update_no_rewrite_entr
error = -ENOENT;
goto out;
}
- new_entry = tomoyo_alloc_element(sizeof(*new_entry));
- if (!new_entry)
- goto out;
- new_entry->pattern = saved_pattern;
- list_add_tail_rcu(&new_entry->list, &tomoyo_no_rewrite_list);
- error = 0;
+ if (tomoyo_memory_ok(entry)) {
+ entry->pattern = saved_pattern;
+ list_add_tail_rcu(&entry->list, &tomoyo_no_rewrite_list);
+ entry = NULL;
+ error = 0;
+ }
out:
- up_write(&tomoyo_no_rewrite_list_lock);
+ mutex_unlock(&tomoyo_policy_lock);
+ kfree(entry);
return error;
}
@@ -821,6 +825,7 @@ static int tomoyo_update_single_path_acl
const struct tomoyo_path_info *saved_filename;
struct tomoyo_acl_info *ptr;
struct tomoyo_single_path_acl_record *acl;
+ struct tomoyo_single_path_acl_record *entry = NULL;
int error = -ENOMEM;
const u16 perm = 1 << type;
@@ -831,7 +836,9 @@ static int tomoyo_update_single_path_acl
saved_filename = tomoyo_save_name(filename);
if (!saved_filename)
return -ENOMEM;
- down_write(&tomoyo_domain_acl_info_list_lock);
+ if (!is_delete)
+ entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+ mutex_lock(&tomoyo_policy_lock);
if (is_delete)
goto delete;
list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
@@ -854,15 +861,17 @@ static int tomoyo_update_single_path_acl
goto out;
}
/* Not found. Append it to the tail. */
- acl = tomoyo_alloc_acl_element(TOMOYO_TYPE_SINGLE_PATH_ACL);
- if (!acl)
- goto out;
- acl->perm = perm;
- if (perm == (1 << TOMOYO_TYPE_READ_WRITE_ACL))
- acl->perm |= rw_mask;
- acl->filename = saved_filename;
- list_add_tail_rcu(&acl->head.list, &domain->acl_info_list);
- error = 0;
+ if (tomoyo_memory_ok(entry)) {
+ acl = entry;
+ acl->head.type = TOMOYO_TYPE_SINGLE_PATH_ACL;
+ acl->perm = perm;
+ if (perm == (1 << TOMOYO_TYPE_READ_WRITE_ACL))
+ acl->perm |= rw_mask;
+ acl->filename = saved_filename;
+ list_add_tail_rcu(&acl->head.list, &domain->acl_info_list);
+ entry = NULL;
+ error = 0;
+ }
goto out;
delete:
error = -ENOENT;
@@ -884,7 +893,8 @@ static int tomoyo_update_single_path_acl
break;
}
out:
- up_write(&tomoyo_domain_acl_info_list_lock);
+ mutex_unlock(&tomoyo_policy_lock);
+ kfree(entry);
return error;
}
@@ -908,6 +918,7 @@ static int tomoyo_update_double_path_acl
const struct tomoyo_path_info *saved_filename2;
struct tomoyo_acl_info *ptr;
struct tomoyo_double_path_acl_record *acl;
+ struct tomoyo_double_path_acl_record *entry = NULL;
int error = -ENOMEM;
const u8 perm = 1 << type;
@@ -920,7 +931,9 @@ static int tomoyo_update_double_path_acl
saved_filename2 = tomoyo_save_name(filename2);
if (!saved_filename1 || !saved_filename2)
return -ENOMEM;
- down_write(&tomoyo_domain_acl_info_list_lock);
+ if (!is_delete)
+ entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+ mutex_lock(&tomoyo_policy_lock);
if (is_delete)
goto delete;
list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
@@ -940,14 +953,16 @@ static int tomoyo_update_double_path_acl
goto out;
}
/* Not found. Append it to the tail. */
- acl = tomoyo_alloc_acl_element(TOMOYO_TYPE_DOUBLE_PATH_ACL);
- if (!acl)
- goto out;
- acl->perm = perm;
- acl->filename1 = saved_filename1;
- acl->filename2 = saved_filename2;
- list_add_tail_rcu(&acl->head.list, &domain->acl_info_list);
- error = 0;
+ if (tomoyo_memory_ok(entry)) {
+ acl = entry;
+ acl->head.type = TOMOYO_TYPE_DOUBLE_PATH_ACL;
+ acl->perm = perm;
+ acl->filename1 = saved_filename1;
+ acl->filename2 = saved_filename2;
+ list_add_tail_rcu(&acl->head.list, &domain->acl_info_list);
+ entry = NULL;
+ error = 0;
+ }
goto out;
delete:
error = -ENOENT;
@@ -966,7 +981,8 @@ static int tomoyo_update_double_path_acl
break;
}
out:
- up_write(&tomoyo_domain_acl_info_list_lock);
+ mutex_unlock(&tomoyo_policy_lock);
+ kfree(entry);
return error;
}
--- security-testing-2.6.orig/security/tomoyo/realpath.c
+++ security-testing-2.6/security/tomoyo/realpath.c
@@ -17,6 +17,25 @@
#include "realpath.h"
/**
+ * tomoyo_warn_oom - Print wraning message if memory allocation failed.
+ *
+ * @function: Function name.
+ */
+static void tomoyo_warn_oom(const char *function)
+{
+ /* Reduce error messages. */
+ static pid_t tomoyo_last_pid;
+ const pid_t pid = current->pid;
+ if (tomoyo_last_pid != pid) {
+ printk(KERN_WARNING "ERROR: Out of memory at %s.\n",
+ function);
+ tomoyo_last_pid = pid;
+ }
+ if (!tomoyo_policy_loaded)
+ panic("MAC Initialization failed.\n");
+}
+
+/**
* tomoyo_encode: Convert binary string to ascii string.
*
* @buffer: Buffer for ASCII string.
@@ -200,57 +219,26 @@ static unsigned int tomoyo_allocated_mem
static unsigned int tomoyo_quota_for_elements;
/**
- * tomoyo_alloc_element - Allocate permanent memory for structures.
+ * tomoyo_memory_ok - Check memory quota.
*
- * @size: Size in bytes.
+ * @ptr: Pointer to allocated memory.
*
- * Returns pointer to allocated memory on success, NULL otherwise.
+ * Returns true if @ptr is not NULL and quota not exceeded, false otherwise.
*
- * Memory has to be zeroed.
- * The RAM is chunked, so NEVER try to kfree() the returned pointer.
+ * Caller holds to tomoyo_policy_lock mutex.
*/
-void *tomoyo_alloc_element(const unsigned int size)
+bool tomoyo_memory_ok(void *ptr)
{
- static char *buf;
- static DEFINE_MUTEX(lock);
- static unsigned int buf_used_len = PATH_MAX;
- char *ptr = NULL;
- /*Assumes sizeof(void *) >= sizeof(long) is true. */
- const unsigned int word_aligned_size
- = roundup(size, max(sizeof(void *), sizeof(long)));
- if (word_aligned_size > PATH_MAX)
- return NULL;
- mutex_lock(&lock);
- if (buf_used_len + word_aligned_size > PATH_MAX) {
- if (!tomoyo_quota_for_elements ||
- tomoyo_allocated_memory_for_elements
- + PATH_MAX <= tomoyo_quota_for_elements)
- ptr = kzalloc(PATH_MAX, GFP_KERNEL);
- if (!ptr) {
- printk(KERN_WARNING "ERROR: Out of memory "
- "for tomoyo_alloc_element().\n");
- if (!tomoyo_policy_loaded)
- panic("MAC Initialization failed.\n");
- } else {
- buf = ptr;
- tomoyo_allocated_memory_for_elements += PATH_MAX;
- buf_used_len = word_aligned_size;
- ptr = buf;
- }
- } else if (word_aligned_size) {
- int i;
- ptr = buf + buf_used_len;
- buf_used_len += word_aligned_size;
- for (i = 0; i < word_aligned_size; i++) {
- if (!ptr[i])
- continue;
- printk(KERN_ERR "WARNING: Reserved memory was tainted! "
- "The system might go wrong.\n");
- ptr[i] = '\0';
- }
+ const unsigned int s = ptr ? ksize(ptr) : 0;
+ if (ptr && (!tomoyo_quota_for_elements ||
+ tomoyo_allocated_memory_for_elements + s
+ <= tomoyo_quota_for_elements)) {
+ tomoyo_allocated_memory_for_elements += s;
+ memset(ptr, 0, s);
+ return true;
}
- mutex_unlock(&lock);
- return ptr;
+ tomoyo_warn_oom(__func__);
+ return false;
}
/* Memory allocated for string data in bytes. */
@@ -280,13 +268,6 @@ struct tomoyo_name_entry {
struct tomoyo_path_info entry;
};
-/* Structure for available memory region. */
-struct tomoyo_free_memory_block_list {
- struct list_head list;
- char *ptr; /* Pointer to a free area. */
- int len; /* Length of the area. */
-};
-
/*
* tomoyo_name_list is used for holding string data used by TOMOYO.
* Since same string data is likely used for multiple times (e.g.
@@ -294,6 +275,7 @@ struct tomoyo_free_memory_block_list {
* "const struct tomoyo_path_info *".
*/
static struct list_head tomoyo_name_list[TOMOYO_MAX_HASH];
+static DEFINE_MUTEX(tomoyo_name_list_lock);
/**
* tomoyo_save_name - Allocate permanent memory for string data.
@@ -306,72 +288,39 @@ static struct list_head tomoyo_name_list
*/
const struct tomoyo_path_info *tomoyo_save_name(const char *name)
{
- static LIST_HEAD(fmb_list);
- static DEFINE_MUTEX(lock);
struct tomoyo_name_entry *ptr;
unsigned int hash;
- /* fmb contains available size in bytes.
- fmb is removed from the fmb_list when fmb->len becomes 0. */
- struct tomoyo_free_memory_block_list *fmb;
int len;
- char *cp;
+ int allocated_len;
if (!name)
return NULL;
len = strlen(name) + 1;
- if (len > TOMOYO_MAX_PATHNAME_LEN) {
- printk(KERN_WARNING "ERROR: Name too long "
- "for tomoyo_save_name().\n");
- return NULL;
- }
hash = full_name_hash((const unsigned char *) name, len - 1);
- mutex_lock(&lock);
+ mutex_lock(&tomoyo_name_list_lock);
list_for_each_entry(ptr, &tomoyo_name_list[hash % TOMOYO_MAX_HASH],
- list) {
- if (hash == ptr->entry.hash && !strcmp(name, ptr->entry.name))
- goto out;
- }
- list_for_each_entry(fmb, &fmb_list, list) {
- if (len <= fmb->len)
- goto ready;
- }
- if (!tomoyo_quota_for_savename ||
- tomoyo_allocated_memory_for_savename + PATH_MAX
- <= tomoyo_quota_for_savename)
- cp = kzalloc(PATH_MAX, GFP_KERNEL);
- else
- cp = NULL;
- fmb = kzalloc(sizeof(*fmb), GFP_KERNEL);
- if (!cp || !fmb) {
- kfree(cp);
- kfree(fmb);
- printk(KERN_WARNING "ERROR: Out of memory "
- "for tomoyo_save_name().\n");
- if (!tomoyo_policy_loaded)
- panic("MAC Initialization failed.\n");
- ptr = NULL;
+ list) {
+ if (hash != ptr->entry.hash || strcmp(name, ptr->entry.name))
+ continue;
goto out;
}
- tomoyo_allocated_memory_for_savename += PATH_MAX;
- list_add(&fmb->list, &fmb_list);
- fmb->ptr = cp;
- fmb->len = PATH_MAX;
- ready:
- ptr = tomoyo_alloc_element(sizeof(*ptr));
- if (!ptr)
+ ptr = kzalloc(sizeof(*ptr) + len, GFP_KERNEL);
+ allocated_len = ptr ? ksize(ptr) : 0;
+ if (!ptr || (tomoyo_quota_for_savename &&
+ tomoyo_allocated_memory_for_savename + allocated_len
+ > tomoyo_quota_for_savename)) {
+ kfree(ptr);
+ ptr = NULL;
+ tomoyo_warn_oom(__func__);
goto out;
- ptr->entry.name = fmb->ptr;
- memmove(fmb->ptr, name, len);
+ }
+ tomoyo_allocated_memory_for_savename += allocated_len;
+ ptr->entry.name = ((char *) ptr) + sizeof(*ptr);
+ memmove((char *) ptr->entry.name, name, len);
tomoyo_fill_path_info(&ptr->entry);
- fmb->ptr += len;
- fmb->len -= len;
list_add_tail(&ptr->list, &tomoyo_name_list[hash % TOMOYO_MAX_HASH]);
- if (fmb->len == 0) {
- list_del(&fmb->list);
- kfree(fmb);
- }
out:
- mutex_unlock(&lock);
+ mutex_unlock(&tomoyo_name_list_lock);
return ptr ? &ptr->entry : NULL;
}
--- security-testing-2.6.orig/security/tomoyo/realpath.h
+++ security-testing-2.6/security/tomoyo/realpath.h
@@ -37,12 +37,6 @@ char *tomoyo_realpath_nofollow(const cha
char *tomoyo_realpath_from_path(struct path *path);
/*
- * Allocate memory for ACL entry.
- * The RAM is chunked, so NEVER try to kfree() the returned pointer.
- */
-void *tomoyo_alloc_element(const unsigned int size);
-
-/*
* Keep the given name on the RAM.
* The RAM is shared, so NEVER try to modify or kfree() the returned name.
*/
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [TOMOYO #16 01/25] LSM: Add security_path_chmod() and security_path_chown().
2009-10-04 12:49 ` [TOMOYO #16 01/25] LSM: Add security_path_chmod() and security_path_chown() Tetsuo Handa
2009-10-08 17:10 ` John Johansen
@ 2009-10-29 5:12 ` Serge E. Hallyn
2009-10-29 15:56 ` [TOMOYO #16 01/25] LSM: Add security_path_chmod() andsecurity_path_chown() Tetsuo Handa
1 sibling, 1 reply; 50+ messages in thread
From: Serge E. Hallyn @ 2009-10-29 5:12 UTC (permalink / raw)
To: Tetsuo Handa; +Cc: linux-security-module, linux-kernel
Quoting Tetsuo Handa (penguin-kernel@I-love.SAKURA.ne.jp):
> This patch allows pathname based LSM modules to check chmod()/chown()
> operations. Since notify_change() does not receive "struct vfsmount *",
> we add security_path_chmod() and security_path_chown() to the caller of
> notify_change().
>
> These hooks are used by TOMOYO.
>
> Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
> ---
> fs/open.c | 24 ++++++++++++++++++++----
> include/linux/security.h | 30 ++++++++++++++++++++++++++++++
> security/capability.c | 13 +++++++++++++
> security/security.c | 15 +++++++++++++++
> 4 files changed, 78 insertions(+), 4 deletions(-)
>
> --- security-testing-2.6.orig/fs/open.c
> +++ security-testing-2.6/fs/open.c
> @@ -616,6 +616,9 @@ SYSCALL_DEFINE2(fchmod, unsigned int, fd
> err = mnt_want_write_file(file);
> if (err)
> goto out_putf;
> + err = security_path_chmod(dentry, file->f_vfsmnt, mode);
> + if (err)
> + goto out_drop_write;
> mutex_lock(&inode->i_mutex);
Isn't doing the security check before the mutex_lock racy?
Any reason not to move it into the lock? (since you had
considered putting a path hook in notify_change() I assume
not?)
-serge
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [TOMOYO #16 17/25] TOMOYO: Add capability support.
2009-10-04 12:50 ` [TOMOYO #16 17/25] TOMOYO: Add capability support Tetsuo Handa
@ 2009-10-29 5:23 ` Serge E. Hallyn
0 siblings, 0 replies; 50+ messages in thread
From: Serge E. Hallyn @ 2009-10-29 5:23 UTC (permalink / raw)
To: Tetsuo Handa; +Cc: linux-security-module, linux-kernel
Quoting Tetsuo Handa (penguin-kernel@I-love.SAKURA.ne.jp):
> This patch contains code for checking non-posix capability.
>
> TOMOYO is ready to support 65536 types of non-posix capabilities.
> But I can't utilize TOMOYO's ability because
>
> (1) Hooks are missing.
please send patches.
> or
> (2) Posix's capability and functionality are not one to one mapping.
> Therefore I can't derive functionality the caller wants to use from
> posix's capability number (e.g. CAP_SYS_ADMIN).
Hmm, it should be possible to address this in a back-compatible way. I.e.,
#define CAP_CHOWN 0
...
#define CAP_SYS_ADMIN 21
...
#define CAP_MAC_ADMIN 33
/* start enhanced capabilities */
#ifdef CONFIG_CAPABILITIES_ENHANCED
#define CAP_SAK_CONFIG 200
#define CAP_RND_ADMIN 201
#define CAP_SYS_HOSTNAME 202
...
#else
#define CAP_SAK_CONFIG CAP_SYS_ADMIN
#define CAP_RND_ADMIN CAP_SYS_ADMIN
#define CAP_SYS_HOSTNAME CAP_SYS_ADMIN
#endif
Plus of course all that is needed (if CONFIG_CAPABILITIES_ENHANCED=y)
to support all those caps.
> or
> (3) Hooks are provided but it is not permitted to sleep (e.g. CAP_SYS_NICE)
> while TOMOYO needs hooks where it is permitted to sleep.
> or
> (4) System calls and device drivers use the same posix's capability number.
> Thus whether MAC's policy suits or not depends on hardware the system
> is running. TOMOYO wants to distinguish requests from userland
> applications and requests from kernel drivers, but I can't distinguish
> it from posix's capability number.
Same thing?
> Therefore, LSM version of TOMOYO has very poor support compared to non-LSM
> version of TOMOYO. I hope this problem is solved in the future.
>
> Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
> ---
> security/tomoyo/capability.c | 141 +++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 141 insertions(+)
>
> --- /dev/null
> +++ security-testing-2.6/security/tomoyo/capability.c
> @@ -0,0 +1,141 @@
> +/*
> + * security/tomoyo/capability.c
> + *
> + * Copyright (C) 2005-2009 NTT DATA CORPORATION
> + */
> +#include "internal.h"
> +
> +/**
> + * tomoyo_audit_capability_log - Audit capability log.
> + *
> + * @r: Pointer to "struct tomoyo_request_info".
> + * @operation: Type of operation.
> + * @is_granted: True if this is a granted log.
> + *
> + * Returns 0 on success, negative value otherwise.
> + */
> +static int tomoyo_audit_capability_log(struct tomoyo_request_info *r,
> + const u8 operation,
> + const bool is_granted)
> +{
> + if (!is_granted)
> + tomoyo_warn_log(r, "capability %s",
> + tomoyo_cap2keyword(operation));
> + return tomoyo_write_audit_log(is_granted, r,
> + TOMOYO_KEYWORD_ALLOW_CAPABILITY "%s\n",
> + tomoyo_cap2keyword(operation));
> +}
> +
> +/**
> + * tomoyo_capable - Check permission for capability.
> + *
> + * @operation: Type of operation.
> + *
> + * Returns true on success, false otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> + */
> +static bool tomoyo_capable2(const u8 operation)
> +{
> + struct tomoyo_request_info r;
> + struct tomoyo_acl_info *ptr;
> + int error;
> + if (tomoyo_init_request_info(&r, NULL, TOMOYO_MAX_MAC_INDEX +
> + operation) == TOMOYO_CONFIG_DISABLED)
> + return true;
> + do {
> + error = -EPERM;
> + list_for_each_entry_rcu(ptr, &r.domain->acl_info_list, list) {
> + struct tomoyo_capability_acl *acl;
> + if (ptr->is_deleted ||
> + ptr->type != TOMOYO_TYPE_CAPABILITY_ACL)
> + continue;
> + acl = container_of(ptr, struct tomoyo_capability_acl,
> + head);
> + if (acl->operation != operation ||
> + !tomoyo_condition(&r, ptr))
> + continue;
> + r.cond = ptr->cond;
> + error = 0;
> + break;
> + }
> + tomoyo_audit_capability_log(&r, operation, !error);
> + if (!error)
> + break;
> + error = tomoyo_supervisor(&r, TOMOYO_KEYWORD_ALLOW_CAPABILITY
> + "%s\n",
> + tomoyo_cap2keyword(operation));
> + } while (error == 1);
> + return !error;
> +}
> +
> +/**
> + * tomoyo_capable - Check permission for capability.
> + *
> + * @operation: Type of operation.
> + *
> + * Returns true on success, false otherwise.
> + */
> +bool tomoyo_capable(const u8 operation)
> +{
> + const int idx = tomoyo_read_lock();
> + const int error = tomoyo_capable2(operation);
> + tomoyo_read_unlock(idx);
> + return error;
> +}
> +
> +/**
> + * tomoyo_write_capability_policy - Write "struct tomoyo_capability_acl" list.
> + *
> + * @data: String to parse.
> + * @domain: Pointer to "struct tomoyo_domain_info".
> + * @condition: Pointer to "struct tomoyo_condition". May be NULL.
> + * @is_delete: True if it is a delete request.
> + *
> + * Returns 0 on success, negative value otherwise.
> + */
> +int tomoyo_write_capability_policy(char *data,
> + struct tomoyo_domain_info *domain,
> + struct tomoyo_condition *condition,
> + const bool is_delete)
> +{
> + struct tomoyo_capability_acl e = {
> + .head.type = TOMOYO_TYPE_CAPABILITY_ACL,
> + .head.cond = condition,
> + };
> + struct tomoyo_capability_acl *entry = NULL;
> + struct tomoyo_acl_info *ptr;
> + int error = is_delete ? -ENOENT : -ENOMEM;
> + u8 capability;
> + for (capability = 0; capability < TOMOYO_MAX_CAPABILITY_INDEX;
> + capability++) {
> + if (strcmp(data, tomoyo_cap2keyword(capability)))
> + continue;
> + break;
> + }
> + if (capability == TOMOYO_MAX_CAPABILITY_INDEX)
> + return -EINVAL;
> + e.operation = capability;
> + if (!is_delete)
> + entry = kmalloc(sizeof(e), GFP_KERNEL);
> + mutex_lock(&tomoyo_policy_lock);
> + list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
> + struct tomoyo_capability_acl *acl =
> + container_of(ptr, struct tomoyo_capability_acl,
> + head);
> + if (ptr->type != TOMOYO_TYPE_CAPABILITY_ACL ||
> + ptr->cond != condition || acl->operation != capability)
> + continue;
> + ptr->is_deleted = is_delete;
> + error = 0;
> + break;
> + }
> + if (!is_delete && error && tomoyo_commit_ok(entry, &e, sizeof(e))) {
> + tomoyo_add_domain_acl(domain, &entry->head);
> + entry = NULL;
> + error = 0;
> + }
> + mutex_unlock(&tomoyo_policy_lock);
> + kfree(entry);
> + return error;
> +}
>
> --
> --
> To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [TOMOYO #16 02/25] LSM: Add security_path_chroot().
2009-10-04 12:49 ` [TOMOYO #16 02/25] LSM: Add security_path_chroot() Tetsuo Handa
2009-10-08 17:12 ` John Johansen
@ 2009-10-29 5:32 ` Serge E. Hallyn
1 sibling, 0 replies; 50+ messages in thread
From: Serge E. Hallyn @ 2009-10-29 5:32 UTC (permalink / raw)
To: Tetsuo Handa; +Cc: linux-security-module, linux-kernel
Quoting Tetsuo Handa (penguin-kernel@I-love.SAKURA.ne.jp):
> This patch allows pathname based LSM modules to check chroot() operations.
>
> This hook is used by TOMOYO.
>
> Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Acked-by: Serge Hallyn <serue@us.ibm.com>
> ---
> fs/open.c | 3 +++
> include/linux/security.h | 11 +++++++++++
> security/capability.c | 6 ++++++
> security/security.c | 5 +++++
> 4 files changed, 25 insertions(+)
>
> --- security-testing-2.6.orig/fs/open.c
> +++ security-testing-2.6/fs/open.c
> @@ -587,6 +587,9 @@ SYSCALL_DEFINE1(chroot, const char __use
> error = -EPERM;
> if (!capable(CAP_SYS_CHROOT))
> goto dput_and_out;
> + error = security_path_chroot(&path);
> + if (error)
> + goto dput_and_out;
>
> set_fs_root(current->fs, &path);
> error = 0;
> --- security-testing-2.6.orig/include/linux/security.h
> +++ security-testing-2.6/include/linux/security.h
> @@ -459,6 +459,10 @@ static inline void security_free_mnt_opt
> * @uid contains new owner's ID.
> * @gid contains new group's ID.
> * Return 0 if permission is granted.
> + * @path_chroot:
> + * Check for permission to change root directory.
> + * @path contains the path structure.
> + * Return 0 if permission is granted.
> * @inode_readlink:
> * Check the permission to read the symbolic link.
> * @dentry contains the dentry structure for the file link.
> @@ -1503,6 +1507,7 @@ struct security_operations {
> int (*path_chmod) (struct dentry *dentry, struct vfsmount *mnt,
> mode_t mode);
> int (*path_chown) (struct path *path, uid_t uid, gid_t gid);
> + int (*path_chroot) (struct path *path);
> #endif
>
> int (*inode_alloc_security) (struct inode *inode);
> @@ -2970,6 +2975,7 @@ int security_path_rename(struct path *ol
> int security_path_chmod(struct dentry *dentry, struct vfsmount *mnt,
> mode_t mode);
> int security_path_chown(struct path *path, uid_t uid, gid_t gid);
> +int security_path_chroot(struct path *path);
> #else /* CONFIG_SECURITY_PATH */
> static inline int security_path_unlink(struct path *dir, struct dentry *dentry)
> {
> @@ -3031,6 +3037,11 @@ static inline int security_path_chown(st
> {
> return 0;
> }
> +
> +static inline int security_path_chroot(struct path *path)
> +{
> + return 0;
> +}
> #endif /* CONFIG_SECURITY_PATH */
>
> #ifdef CONFIG_KEYS
> --- security-testing-2.6.orig/security/capability.c
> +++ security-testing-2.6/security/capability.c
> @@ -319,6 +319,11 @@ static int cap_path_chown(struct path *p
> {
> return 0;
> }
> +
> +static int cap_path_chroot(struct path *root)
> +{
> + return 0;
> +}
> #endif
>
> static int cap_file_permission(struct file *file, int mask)
> @@ -990,6 +995,7 @@ void security_fixup_ops(struct security_
> set_to_cap_if_null(ops, path_truncate);
> set_to_cap_if_null(ops, path_chmod);
> set_to_cap_if_null(ops, path_chown);
> + set_to_cap_if_null(ops, path_chroot);
> #endif
> set_to_cap_if_null(ops, file_permission);
> set_to_cap_if_null(ops, file_alloc_security);
> --- security-testing-2.6.orig/security/security.c
> +++ security-testing-2.6/security/security.c
> @@ -449,6 +449,11 @@ int security_path_chown(struct path *pat
> return 0;
> return security_ops->path_chown(path, uid, gid);
> }
> +
> +int security_path_chroot(struct path *path)
> +{
> + return security_ops->path_chroot(path);
> +}
> #endif
>
> int security_inode_create(struct inode *dir, struct dentry *dentry, int mode)
>
> --
> --
> To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] TOMOYO: Use RCU primitives for list operation
2009-10-13 11:39 ` [PATCH] TOMOYO: Use RCU primitives for list operation Tetsuo Handa
2009-10-13 11:41 ` [PATCH] TOMOYO: Bring memory allocation to outside semaphore Tetsuo Handa
@ 2009-10-29 5:40 ` Serge E. Hallyn
2009-12-04 12:34 ` Tetsuo Handa
1 sibling, 1 reply; 50+ messages in thread
From: Serge E. Hallyn @ 2009-10-29 5:40 UTC (permalink / raw)
To: Tetsuo Handa; +Cc: linux-security-module, linux-kernel
Quoting Tetsuo Handa (penguin-kernel@I-love.SAKURA.ne.jp):
> [PATCH] TOMOYO: Use RCU primitives for list operation
>
> Remove down_read()/up_read() by replacing with RCU primitives.
> SRCU based garbage collector will be added in the future.
>
> Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
> ---
> security/tomoyo/common.c | 52 ++++++++++-----------------------------------
> security/tomoyo/common.h | 14 ++++++------
> security/tomoyo/domain.c | 38 ++++++++++----------------------
> security/tomoyo/file.c | 50 ++++++++++++++-----------------------------
> security/tomoyo/realpath.c | 4 ---
> 5 files changed, 49 insertions(+), 109 deletions(-)
>
> --- security-testing-2.6.orig/security/tomoyo/common.c
> +++ security-testing-2.6/security/tomoyo/common.c
> @@ -365,9 +365,6 @@ bool tomoyo_is_domain_def(const unsigned
> *
> * @domainname: The domainname to find.
> *
> - * Caller must call down_read(&tomoyo_domain_list_lock); or
> - * down_write(&tomoyo_domain_list_lock); .
> - *
> * Returns pointer to "struct tomoyo_domain_info" if found, NULL otherwise.
> */
> struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname)
> @@ -377,7 +374,7 @@ struct tomoyo_domain_info *tomoyo_find_d
>
> name.name = domainname;
> tomoyo_fill_path_info(&name);
> - list_for_each_entry(domain, &tomoyo_domain_list, list) {
> + list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
> if (!domain->is_deleted &&
> !tomoyo_pathcmp(&name, domain->domainname))
> return domain;
> @@ -837,8 +834,7 @@ bool tomoyo_domain_quota_is_ok(struct to
>
> if (!domain)
> return true;
> - down_read(&tomoyo_domain_acl_info_list_lock);
> - list_for_each_entry(ptr, &domain->acl_info_list, list) {
> + list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
> if (ptr->type & TOMOYO_ACL_DELETED)
> continue;
> switch (tomoyo_acl_type2(ptr)) {
You are removing the down_read()s, but not replacing them with
rcu_read_lock()s. I assume this is based on the same discussions
you had with Paul awhile ago about the safety of walking the list
bc you only append to the end (which I trust must have concluded
in your favor)?
If you'll be adding gc eventually anyway, is it really worthwhile
to 'violate the rules' now by calling list_for_each_entry_rcu()
without being inside rcu_read_lock() now? I fear it'll only serve
to confuse readers, especially those looking for rcu users to serve
as examples.
-serge
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [TOMOYO #16 01/25] LSM: Add security_path_chmod() andsecurity_path_chown().
2009-10-29 5:12 ` [TOMOYO #16 01/25] LSM: Add security_path_chmod() and security_path_chown() Serge E. Hallyn
@ 2009-10-29 15:56 ` Tetsuo Handa
2009-11-22 2:49 ` [PATCH] LSM: Move security_path_chmod()/security_path_chown() to after mutex_lock() Tetsuo Handa
0 siblings, 1 reply; 50+ messages in thread
From: Tetsuo Handa @ 2009-10-29 15:56 UTC (permalink / raw)
To: serue, viro; +Cc: linux-fsdevel, linux-security-module, linux-kernel
Hello.
Serge E. Hallyn wrote:
> Quoting Tetsuo Handa (penguin-kernel@I-love.SAKURA.ne.jp):
> > This patch allows pathname based LSM modules to check chmod()/chown()
> > operations. Since notify_change() does not receive "struct vfsmount *",
> > we add security_path_chmod() and security_path_chown() to the caller of
> > notify_change().
> >
> > These hooks are used by TOMOYO.
> >
> > Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
> > ---
> > fs/open.c | 24 ++++++++++++++++++++----
> > include/linux/security.h | 30 ++++++++++++++++++++++++++++++
> > security/capability.c | 13 +++++++++++++
> > security/security.c | 15 +++++++++++++++
> > 4 files changed, 78 insertions(+), 4 deletions(-)
> >
> > --- security-testing-2.6.orig/fs/open.c
> > +++ security-testing-2.6/fs/open.c
> > @@ -616,6 +616,9 @@ SYSCALL_DEFINE2(fchmod, unsigned int, fd
> > err = mnt_want_write_file(file);
> > if (err)
> > goto out_putf;
> > + err = security_path_chmod(dentry, file->f_vfsmnt, mode);
> > + if (err)
> > + goto out_drop_write;
> > mutex_lock(&inode->i_mutex);
>
> Isn't doing the security check before the mutex_lock racy?
>
> Any reason not to move it into the lock? (since you had
> considered putting a path hook in notify_change() I assume
> not?)
>
None for security_path_chmod(). None for security_path_chown() if we are
allowed to pass "struct vfsmount" to chown_common().
> -serge
>
Al, is it OK to pass "struct vfsmount" to chown_common() so that
security_path_chown() is called after mutex_lock()?
----------
[PATCH] LSM: Move security_path_chmod()/security_path_chown() to after mutex_lock().
We should call security_path_chmod()/security_path_chown() after mutex_lock()
in order to avoid races.
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
fs/open.c | 36 +++++++++++++++---------------------
1 file changed, 15 insertions(+), 21 deletions(-)
--- security-testing-2.6.orig/fs/open.c
+++ security-testing-2.6/fs/open.c
@@ -619,17 +619,17 @@ SYSCALL_DEFINE2(fchmod, unsigned int, fd
err = mnt_want_write_file(file);
if (err)
goto out_putf;
+ mutex_lock(&inode->i_mutex);
err = security_path_chmod(dentry, file->f_vfsmnt, mode);
if (err)
- goto out_drop_write;
- mutex_lock(&inode->i_mutex);
+ goto out_unlock;
if (mode == (mode_t) -1)
mode = inode->i_mode;
newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
err = notify_change(dentry, &newattrs);
+out_unlock:
mutex_unlock(&inode->i_mutex);
-out_drop_write:
mnt_drop_write(file->f_path.mnt);
out_putf:
fput(file);
@@ -652,17 +652,17 @@ SYSCALL_DEFINE3(fchmodat, int, dfd, cons
error = mnt_want_write(path.mnt);
if (error)
goto dput_and_out;
+ mutex_lock(&inode->i_mutex);
error = security_path_chmod(path.dentry, path.mnt, mode);
if (error)
- goto out_drop_write;
- mutex_lock(&inode->i_mutex);
+ goto out_unlock;
if (mode == (mode_t) -1)
mode = inode->i_mode;
newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
error = notify_change(path.dentry, &newattrs);
+out_unlock:
mutex_unlock(&inode->i_mutex);
-out_drop_write:
mnt_drop_write(path.mnt);
dput_and_out:
path_put(&path);
@@ -675,9 +675,9 @@ SYSCALL_DEFINE2(chmod, const char __user
return sys_fchmodat(AT_FDCWD, filename, mode);
}
-static int chown_common(struct dentry * dentry, uid_t user, gid_t group)
+static int chown_common(struct path *path, uid_t user, gid_t group)
{
- struct inode *inode = dentry->d_inode;
+ struct inode *inode = path->dentry->d_inode;
int error;
struct iattr newattrs;
@@ -694,7 +694,9 @@ static int chown_common(struct dentry *
newattrs.ia_valid |=
ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV;
mutex_lock(&inode->i_mutex);
- error = notify_change(dentry, &newattrs);
+ error = security_path_chown(path, user, group);
+ if (!error)
+ error = notify_change(path->dentry, &newattrs);
mutex_unlock(&inode->i_mutex);
return error;
@@ -711,9 +713,7 @@ SYSCALL_DEFINE3(chown, const char __user
error = mnt_want_write(path.mnt);
if (error)
goto out_release;
- error = security_path_chown(&path, user, group);
- if (!error)
- error = chown_common(path.dentry, user, group);
+ error = chown_common(&path, user, group);
mnt_drop_write(path.mnt);
out_release:
path_put(&path);
@@ -738,9 +738,7 @@ SYSCALL_DEFINE5(fchownat, int, dfd, cons
error = mnt_want_write(path.mnt);
if (error)
goto out_release;
- error = security_path_chown(&path, user, group);
- if (!error)
- error = chown_common(path.dentry, user, group);
+ error = chown_common(&path, user, group);
mnt_drop_write(path.mnt);
out_release:
path_put(&path);
@@ -759,9 +757,7 @@ SYSCALL_DEFINE3(lchown, const char __use
error = mnt_want_write(path.mnt);
if (error)
goto out_release;
- error = security_path_chown(&path, user, group);
- if (!error)
- error = chown_common(path.dentry, user, group);
+ error = chown_common(&path, user, group);
mnt_drop_write(path.mnt);
out_release:
path_put(&path);
@@ -784,9 +780,7 @@ SYSCALL_DEFINE3(fchown, unsigned int, fd
goto out_fput;
dentry = file->f_path.dentry;
audit_inode(NULL, dentry);
- error = security_path_chown(&file->f_path, user, group);
- if (!error)
- error = chown_common(dentry, user, group);
+ error = chown_common(&file->f_path, user, group);
mnt_drop_write(file->f_path.mnt);
out_fput:
fput(file);
^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH] LSM: Move security_path_chmod()/security_path_chown() to after mutex_lock().
2009-10-29 15:56 ` [TOMOYO #16 01/25] LSM: Add security_path_chmod() andsecurity_path_chown() Tetsuo Handa
@ 2009-11-22 2:49 ` Tetsuo Handa
2009-11-23 10:09 ` John Johansen
0 siblings, 1 reply; 50+ messages in thread
From: Tetsuo Handa @ 2009-11-22 2:49 UTC (permalink / raw)
To: linux-security-module; +Cc: linux-fsdevel, linux-kernel
[PATCH] LSM: Move security_path_chmod()/security_path_chown() to after mutex_lock().
We should call security_path_chmod()/security_path_chown() after mutex_lock()
in order to avoid races.
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
fs/open.c | 36 +++++++++++++++---------------------
1 file changed, 15 insertions(+), 21 deletions(-)
--- security-testing-2.6.orig/fs/open.c
+++ security-testing-2.6/fs/open.c
@@ -619,17 +619,17 @@ SYSCALL_DEFINE2(fchmod, unsigned int, fd
err = mnt_want_write_file(file);
if (err)
goto out_putf;
+ mutex_lock(&inode->i_mutex);
err = security_path_chmod(dentry, file->f_vfsmnt, mode);
if (err)
- goto out_drop_write;
- mutex_lock(&inode->i_mutex);
+ goto out_unlock;
if (mode == (mode_t) -1)
mode = inode->i_mode;
newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
err = notify_change(dentry, &newattrs);
+out_unlock:
mutex_unlock(&inode->i_mutex);
-out_drop_write:
mnt_drop_write(file->f_path.mnt);
out_putf:
fput(file);
@@ -652,17 +652,17 @@ SYSCALL_DEFINE3(fchmodat, int, dfd, cons
error = mnt_want_write(path.mnt);
if (error)
goto dput_and_out;
+ mutex_lock(&inode->i_mutex);
error = security_path_chmod(path.dentry, path.mnt, mode);
if (error)
- goto out_drop_write;
- mutex_lock(&inode->i_mutex);
+ goto out_unlock;
if (mode == (mode_t) -1)
mode = inode->i_mode;
newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
error = notify_change(path.dentry, &newattrs);
+out_unlock:
mutex_unlock(&inode->i_mutex);
-out_drop_write:
mnt_drop_write(path.mnt);
dput_and_out:
path_put(&path);
@@ -675,9 +675,9 @@ SYSCALL_DEFINE2(chmod, const char __user
return sys_fchmodat(AT_FDCWD, filename, mode);
}
-static int chown_common(struct dentry * dentry, uid_t user, gid_t group)
+static int chown_common(struct path *path, uid_t user, gid_t group)
{
- struct inode *inode = dentry->d_inode;
+ struct inode *inode = path->dentry->d_inode;
int error;
struct iattr newattrs;
@@ -694,7 +694,9 @@ static int chown_common(struct dentry *
newattrs.ia_valid |=
ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV;
mutex_lock(&inode->i_mutex);
- error = notify_change(dentry, &newattrs);
+ error = security_path_chown(path, user, group);
+ if (!error)
+ error = notify_change(path->dentry, &newattrs);
mutex_unlock(&inode->i_mutex);
return error;
@@ -711,9 +713,7 @@ SYSCALL_DEFINE3(chown, const char __user
error = mnt_want_write(path.mnt);
if (error)
goto out_release;
- error = security_path_chown(&path, user, group);
- if (!error)
- error = chown_common(path.dentry, user, group);
+ error = chown_common(&path, user, group);
mnt_drop_write(path.mnt);
out_release:
path_put(&path);
@@ -738,9 +738,7 @@ SYSCALL_DEFINE5(fchownat, int, dfd, cons
error = mnt_want_write(path.mnt);
if (error)
goto out_release;
- error = security_path_chown(&path, user, group);
- if (!error)
- error = chown_common(path.dentry, user, group);
+ error = chown_common(&path, user, group);
mnt_drop_write(path.mnt);
out_release:
path_put(&path);
@@ -759,9 +757,7 @@ SYSCALL_DEFINE3(lchown, const char __use
error = mnt_want_write(path.mnt);
if (error)
goto out_release;
- error = security_path_chown(&path, user, group);
- if (!error)
- error = chown_common(path.dentry, user, group);
+ error = chown_common(&path, user, group);
mnt_drop_write(path.mnt);
out_release:
path_put(&path);
@@ -784,9 +780,7 @@ SYSCALL_DEFINE3(fchown, unsigned int, fd
goto out_fput;
dentry = file->f_path.dentry;
audit_inode(NULL, dentry);
- error = security_path_chown(&file->f_path, user, group);
- if (!error)
- error = chown_common(dentry, user, group);
+ error = chown_common(&file->f_path, user, group);
mnt_drop_write(file->f_path.mnt);
out_fput:
fput(file);
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] LSM: Move security_path_chmod()/security_path_chown() to after mutex_lock().
2009-11-22 2:49 ` [PATCH] LSM: Move security_path_chmod()/security_path_chown() to after mutex_lock() Tetsuo Handa
@ 2009-11-23 10:09 ` John Johansen
2009-11-23 21:50 ` James Morris
0 siblings, 1 reply; 50+ messages in thread
From: John Johansen @ 2009-11-23 10:09 UTC (permalink / raw)
To: Tetsuo Handa; +Cc: linux-security-module, linux-fsdevel, linux-kernel
Tetsuo Handa wrote:
> [PATCH] LSM: Move security_path_chmod()/security_path_chown() to after mutex_lock().
>
> We should call security_path_chmod()/security_path_chown() after mutex_lock()
> in order to avoid races.
>
> Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Acked-by: John Johansen <john.johansen@canonical.com>
> ---
> fs/open.c | 36 +++++++++++++++---------------------
> 1 file changed, 15 insertions(+), 21 deletions(-)
>
> --- security-testing-2.6.orig/fs/open.c
> +++ security-testing-2.6/fs/open.c
> @@ -619,17 +619,17 @@ SYSCALL_DEFINE2(fchmod, unsigned int, fd
> err = mnt_want_write_file(file);
> if (err)
> goto out_putf;
> + mutex_lock(&inode->i_mutex);
> err = security_path_chmod(dentry, file->f_vfsmnt, mode);
> if (err)
> - goto out_drop_write;
> - mutex_lock(&inode->i_mutex);
> + goto out_unlock;
> if (mode == (mode_t) -1)
> mode = inode->i_mode;
> newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
> newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
> err = notify_change(dentry, &newattrs);
> +out_unlock:
> mutex_unlock(&inode->i_mutex);
> -out_drop_write:
> mnt_drop_write(file->f_path.mnt);
> out_putf:
> fput(file);
> @@ -652,17 +652,17 @@ SYSCALL_DEFINE3(fchmodat, int, dfd, cons
> error = mnt_want_write(path.mnt);
> if (error)
> goto dput_and_out;
> + mutex_lock(&inode->i_mutex);
> error = security_path_chmod(path.dentry, path.mnt, mode);
> if (error)
> - goto out_drop_write;
> - mutex_lock(&inode->i_mutex);
> + goto out_unlock;
> if (mode == (mode_t) -1)
> mode = inode->i_mode;
> newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
> newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
> error = notify_change(path.dentry, &newattrs);
> +out_unlock:
> mutex_unlock(&inode->i_mutex);
> -out_drop_write:
> mnt_drop_write(path.mnt);
> dput_and_out:
> path_put(&path);
> @@ -675,9 +675,9 @@ SYSCALL_DEFINE2(chmod, const char __user
> return sys_fchmodat(AT_FDCWD, filename, mode);
> }
>
> -static int chown_common(struct dentry * dentry, uid_t user, gid_t group)
> +static int chown_common(struct path *path, uid_t user, gid_t group)
> {
> - struct inode *inode = dentry->d_inode;
> + struct inode *inode = path->dentry->d_inode;
> int error;
> struct iattr newattrs;
>
> @@ -694,7 +694,9 @@ static int chown_common(struct dentry *
> newattrs.ia_valid |=
> ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV;
> mutex_lock(&inode->i_mutex);
> - error = notify_change(dentry, &newattrs);
> + error = security_path_chown(path, user, group);
> + if (!error)
> + error = notify_change(path->dentry, &newattrs);
> mutex_unlock(&inode->i_mutex);
>
> return error;
> @@ -711,9 +713,7 @@ SYSCALL_DEFINE3(chown, const char __user
> error = mnt_want_write(path.mnt);
> if (error)
> goto out_release;
> - error = security_path_chown(&path, user, group);
> - if (!error)
> - error = chown_common(path.dentry, user, group);
> + error = chown_common(&path, user, group);
> mnt_drop_write(path.mnt);
> out_release:
> path_put(&path);
> @@ -738,9 +738,7 @@ SYSCALL_DEFINE5(fchownat, int, dfd, cons
> error = mnt_want_write(path.mnt);
> if (error)
> goto out_release;
> - error = security_path_chown(&path, user, group);
> - if (!error)
> - error = chown_common(path.dentry, user, group);
> + error = chown_common(&path, user, group);
> mnt_drop_write(path.mnt);
> out_release:
> path_put(&path);
> @@ -759,9 +757,7 @@ SYSCALL_DEFINE3(lchown, const char __use
> error = mnt_want_write(path.mnt);
> if (error)
> goto out_release;
> - error = security_path_chown(&path, user, group);
> - if (!error)
> - error = chown_common(path.dentry, user, group);
> + error = chown_common(&path, user, group);
> mnt_drop_write(path.mnt);
> out_release:
> path_put(&path);
> @@ -784,9 +780,7 @@ SYSCALL_DEFINE3(fchown, unsigned int, fd
> goto out_fput;
> dentry = file->f_path.dentry;
> audit_inode(NULL, dentry);
> - error = security_path_chown(&file->f_path, user, group);
> - if (!error)
> - error = chown_common(dentry, user, group);
> + error = chown_common(&file->f_path, user, group);
> mnt_drop_write(file->f_path.mnt);
> out_fput:
> fput(file);
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] LSM: Move security_path_chmod()/security_path_chown() to after mutex_lock().
2009-11-23 10:09 ` John Johansen
@ 2009-11-23 21:50 ` James Morris
0 siblings, 0 replies; 50+ messages in thread
From: James Morris @ 2009-11-23 21:50 UTC (permalink / raw)
To: John Johansen
Cc: Tetsuo Handa, linux-security-module, linux-fsdevel, linux-kernel
On Mon, 23 Nov 2009, John Johansen wrote:
> Tetsuo Handa wrote:
> > [PATCH] LSM: Move security_path_chmod()/security_path_chown() to after mutex_lock().
> >
> > We should call security_path_chmod()/security_path_chown() after mutex_lock()
> > in order to avoid races.
> >
> > Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
>
> Acked-by: John Johansen <john.johansen@canonical.com>
Applied to
git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/security-testing-2.6#next
--
James Morris
<jmorris@namei.org>
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] TOMOYO: Use RCU primitives for list operation
2009-10-29 5:40 ` [PATCH] TOMOYO: Use RCU primitives for list operation Serge E. Hallyn
@ 2009-12-04 12:34 ` Tetsuo Handa
0 siblings, 0 replies; 50+ messages in thread
From: Tetsuo Handa @ 2009-12-04 12:34 UTC (permalink / raw)
To: serue; +Cc: linux-security-module, linux-kernel
Hello.
Serge E. Hallyn wrote:
> If you'll be adding gc eventually anyway, is it really worthwhile
> to 'violate the rules' now by calling list_for_each_entry_rcu()
> without being inside rcu_read_lock() now? I fear it'll only serve
> to confuse readers, especially those looking for rcu users to serve
> as examples.
You are right. I modified to call srcu_read_lock().
I don't modify tomoyo_file_permission() because it will be removed by
applying commit: c656ae95d1c5c8ed5763356263ace2d03087efec
(security/tomoyo: Remove now unnecessary handling of security_sysctl.).
Regards.
----------
[PATCH] TOMOYO: Use RCU primitives for list operation
Replace list operation with RCU primitives and replace
down_read()/up_read() with srcu_read_lock()/srcu_read_unlock().
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
security/tomoyo/common.c | 90 ++++++++++++++++++++----------------
security/tomoyo/common.h | 28 ++++++++---
security/tomoyo/domain.c | 63 +++++++++++++++----------
security/tomoyo/file.c | 110 +++++++++++++++++++++++++++++++--------------
security/tomoyo/realpath.c | 8 ++-
security/tomoyo/tomoyo.c | 20 ++++++--
6 files changed, 207 insertions(+), 112 deletions(-)
--- security-testing-2.6.orig/security/tomoyo/common.c
+++ security-testing-2.6/security/tomoyo/common.c
@@ -365,10 +365,9 @@ bool tomoyo_is_domain_def(const unsigned
*
* @domainname: The domainname to find.
*
- * Caller must call down_read(&tomoyo_domain_list_lock); or
- * down_write(&tomoyo_domain_list_lock); .
- *
* Returns pointer to "struct tomoyo_domain_info" if found, NULL otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname)
{
@@ -377,7 +376,7 @@ struct tomoyo_domain_info *tomoyo_find_d
name.name = domainname;
tomoyo_fill_path_info(&name);
- list_for_each_entry(domain, &tomoyo_domain_list, list) {
+ list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
if (!domain->is_deleted &&
!tomoyo_pathcmp(&name, domain->domainname))
return domain;
@@ -829,6 +828,8 @@ bool tomoyo_verbose_mode(const struct to
* @domain: Pointer to "struct tomoyo_domain_info".
*
* Returns true if the domain is not exceeded quota, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
bool tomoyo_domain_quota_is_ok(struct tomoyo_domain_info * const domain)
{
@@ -837,8 +838,7 @@ bool tomoyo_domain_quota_is_ok(struct to
if (!domain)
return true;
- down_read(&tomoyo_domain_acl_info_list_lock);
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
if (ptr->type & TOMOYO_ACL_DELETED)
continue;
switch (tomoyo_acl_type2(ptr)) {
@@ -891,7 +891,6 @@ bool tomoyo_domain_quota_is_ok(struct to
break;
}
}
- up_read(&tomoyo_domain_acl_info_list_lock);
if (count < tomoyo_check_flags(domain, TOMOYO_MAX_ACCEPT_ENTRY))
return true;
if (!domain->quota_warned) {
@@ -1121,6 +1120,8 @@ static DECLARE_RWSEM(tomoyo_policy_manag
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_update_manager_entry(const char *manager,
const bool is_delete)
@@ -1143,7 +1144,7 @@ static int tomoyo_update_manager_entry(c
if (!saved_manager)
return -ENOMEM;
down_write(&tomoyo_policy_manager_list_lock);
- list_for_each_entry(ptr, &tomoyo_policy_manager_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, list) {
if (ptr->manager != saved_manager)
continue;
ptr->is_deleted = is_delete;
@@ -1159,7 +1160,7 @@ static int tomoyo_update_manager_entry(c
goto out;
new_entry->manager = saved_manager;
new_entry->is_domain = is_domain;
- list_add_tail(&new_entry->list, &tomoyo_policy_manager_list);
+ list_add_tail_rcu(&new_entry->list, &tomoyo_policy_manager_list);
error = 0;
out:
up_write(&tomoyo_policy_manager_list_lock);
@@ -1172,6 +1173,8 @@ static int tomoyo_update_manager_entry(c
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_write_manager_policy(struct tomoyo_io_buffer *head)
{
@@ -1191,6 +1194,8 @@ static int tomoyo_write_manager_policy(s
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns 0.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_read_manager_policy(struct tomoyo_io_buffer *head)
{
@@ -1199,7 +1204,6 @@ static int tomoyo_read_manager_policy(st
if (head->read_eof)
return 0;
- down_read(&tomoyo_policy_manager_list_lock);
list_for_each_cookie(pos, head->read_var2,
&tomoyo_policy_manager_list) {
struct tomoyo_policy_manager_entry *ptr;
@@ -1211,7 +1215,6 @@ static int tomoyo_read_manager_policy(st
if (!done)
break;
}
- up_read(&tomoyo_policy_manager_list_lock);
head->read_eof = done;
return 0;
}
@@ -1221,6 +1224,8 @@ static int tomoyo_read_manager_policy(st
*
* Returns true if the current process is permitted to modify policy
* via /sys/kernel/security/tomoyo/ interface.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static bool tomoyo_is_policy_manager(void)
{
@@ -1234,29 +1239,25 @@ static bool tomoyo_is_policy_manager(voi
return true;
if (!tomoyo_manage_by_non_root && (task->cred->uid || task->cred->euid))
return false;
- down_read(&tomoyo_policy_manager_list_lock);
- list_for_each_entry(ptr, &tomoyo_policy_manager_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, list) {
if (!ptr->is_deleted && ptr->is_domain
&& !tomoyo_pathcmp(domainname, ptr->manager)) {
found = true;
break;
}
}
- up_read(&tomoyo_policy_manager_list_lock);
if (found)
return true;
exe = tomoyo_get_exe();
if (!exe)
return false;
- down_read(&tomoyo_policy_manager_list_lock);
- list_for_each_entry(ptr, &tomoyo_policy_manager_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, list) {
if (!ptr->is_deleted && !ptr->is_domain
&& !strcmp(exe, ptr->manager->name)) {
found = true;
break;
}
}
- up_read(&tomoyo_policy_manager_list_lock);
if (!found) { /* Reduce error messages. */
static pid_t last_pid;
const pid_t pid = current->pid;
@@ -1277,6 +1278,8 @@ static bool tomoyo_is_policy_manager(voi
* @data: String to parse.
*
* Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head,
const char *data)
@@ -1292,11 +1295,8 @@ static bool tomoyo_is_select_one(struct
domain = tomoyo_real_domain(p);
read_unlock(&tasklist_lock);
} else if (!strncmp(data, "domain=", 7)) {
- if (tomoyo_is_domain_def(data + 7)) {
- down_read(&tomoyo_domain_list_lock);
+ if (tomoyo_is_domain_def(data + 7))
domain = tomoyo_find_domain(data + 7);
- up_read(&tomoyo_domain_list_lock);
- }
} else
return false;
head->write_var1 = domain;
@@ -1310,13 +1310,11 @@ static bool tomoyo_is_select_one(struct
if (domain) {
struct tomoyo_domain_info *d;
head->read_var1 = NULL;
- down_read(&tomoyo_domain_list_lock);
- list_for_each_entry(d, &tomoyo_domain_list, list) {
+ list_for_each_entry_rcu(d, &tomoyo_domain_list, list) {
if (d == domain)
break;
head->read_var1 = &d->list;
}
- up_read(&tomoyo_domain_list_lock);
head->read_var2 = NULL;
head->read_bit = 0;
head->read_step = 0;
@@ -1332,6 +1330,8 @@ static bool tomoyo_is_select_one(struct
* @domainname: The name of domain.
*
* Returns 0.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_delete_domain(char *domainname)
{
@@ -1342,7 +1342,7 @@ static int tomoyo_delete_domain(char *do
tomoyo_fill_path_info(&name);
down_write(&tomoyo_domain_list_lock);
/* Is there an active domain? */
- list_for_each_entry(domain, &tomoyo_domain_list, list) {
+ list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
/* Never delete tomoyo_kernel_domain */
if (domain == &tomoyo_kernel_domain)
continue;
@@ -1362,6 +1362,8 @@ static int tomoyo_delete_domain(char *do
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_write_domain_policy(struct tomoyo_io_buffer *head)
{
@@ -1384,11 +1386,9 @@ static int tomoyo_write_domain_policy(st
domain = NULL;
if (is_delete)
tomoyo_delete_domain(data);
- else if (is_select) {
- down_read(&tomoyo_domain_list_lock);
+ else if (is_select)
domain = tomoyo_find_domain(data);
- up_read(&tomoyo_domain_list_lock);
- } else
+ else
domain = tomoyo_find_or_assign_new_domain(data, 0);
head->write_var1 = domain;
return 0;
@@ -1533,6 +1533,8 @@ static bool tomoyo_print_entry(struct to
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns 0.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_read_domain_policy(struct tomoyo_io_buffer *head)
{
@@ -1544,7 +1546,6 @@ static int tomoyo_read_domain_policy(str
return 0;
if (head->read_step == 0)
head->read_step = 1;
- down_read(&tomoyo_domain_list_lock);
list_for_each_cookie(dpos, head->read_var1, &tomoyo_domain_list) {
struct tomoyo_domain_info *domain;
const char *quota_exceeded = "";
@@ -1577,7 +1578,6 @@ acl_loop:
if (head->read_step == 3)
goto tail_mark;
/* Print ACL entries in the domain. */
- down_read(&tomoyo_domain_acl_info_list_lock);
list_for_each_cookie(apos, head->read_var2,
&domain->acl_info_list) {
struct tomoyo_acl_info *ptr
@@ -1587,7 +1587,6 @@ acl_loop:
if (!done)
break;
}
- up_read(&tomoyo_domain_acl_info_list_lock);
if (!done)
break;
head->read_step = 3;
@@ -1599,7 +1598,6 @@ tail_mark:
if (head->read_single_domain)
break;
}
- up_read(&tomoyo_domain_list_lock);
head->read_eof = done;
return 0;
}
@@ -1615,6 +1613,8 @@ tail_mark:
*
* ( echo "select " $domainname; echo "use_profile " $profile ) |
* /usr/lib/ccs/loadpolicy -d
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_write_domain_profile(struct tomoyo_io_buffer *head)
{
@@ -1626,9 +1626,7 @@ static int tomoyo_write_domain_profile(s
if (!cp)
return -EINVAL;
*cp = '\0';
- down_read(&tomoyo_domain_list_lock);
domain = tomoyo_find_domain(cp + 1);
- up_read(&tomoyo_domain_list_lock);
if (strict_strtoul(data, 10, &profile))
return -EINVAL;
if (domain && profile < TOMOYO_MAX_PROFILES
@@ -1650,6 +1648,8 @@ static int tomoyo_write_domain_profile(s
* awk ' { if ( domainname == "" ) { if ( $1 == "<kernel>" )
* domainname = $0; } else if ( $1 == "use_profile" ) {
* print $2 " " domainname; domainname = ""; } } ; '
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_read_domain_profile(struct tomoyo_io_buffer *head)
{
@@ -1658,7 +1658,6 @@ static int tomoyo_read_domain_profile(st
if (head->read_eof)
return 0;
- down_read(&tomoyo_domain_list_lock);
list_for_each_cookie(pos, head->read_var1, &tomoyo_domain_list) {
struct tomoyo_domain_info *domain;
domain = list_entry(pos, struct tomoyo_domain_info, list);
@@ -1669,7 +1668,6 @@ static int tomoyo_read_domain_profile(st
if (!done)
break;
}
- up_read(&tomoyo_domain_list_lock);
head->read_eof = done;
return 0;
}
@@ -1726,6 +1724,8 @@ static int tomoyo_read_pid(struct tomoyo
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_write_exception_policy(struct tomoyo_io_buffer *head)
{
@@ -1760,6 +1760,8 @@ static int tomoyo_write_exception_policy
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns 0 on success, -EINVAL otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_read_exception_policy(struct tomoyo_io_buffer *head)
{
@@ -1889,15 +1891,13 @@ void tomoyo_load_policy(const char *file
tomoyo_policy_loaded = true;
{ /* Check all profiles currently assigned to domains are defined. */
struct tomoyo_domain_info *domain;
- down_read(&tomoyo_domain_list_lock);
- list_for_each_entry(domain, &tomoyo_domain_list, list) {
+ list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
const u8 profile = domain->profile;
if (tomoyo_profile_ptr[profile])
continue;
panic("Profile %u (used by '%s') not defined.\n",
profile, domain->domainname->name);
}
- up_read(&tomoyo_domain_list_lock);
}
}
@@ -1945,6 +1945,8 @@ static int tomoyo_read_self_domain(struc
* @file: Pointer to "struct file".
*
* Associates policy handler and returns 0 on success, -ENOMEM otherwise.
+ *
+ * Caller acquires tomoyo_read_lock().
*/
static int tomoyo_open_control(const u8 type, struct file *file)
{
@@ -2030,6 +2032,7 @@ static int tomoyo_open_control(const u8
return -ENOMEM;
}
}
+ head->reader_idx = tomoyo_read_lock();
file->private_data = head;
/*
* Call the handler now if the file is
@@ -2051,6 +2054,8 @@ static int tomoyo_open_control(const u8
* @buffer_len: Size of @buffer.
*
* Returns bytes read on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_read_control(struct file *file, char __user *buffer,
const int buffer_len)
@@ -2094,6 +2099,8 @@ static int tomoyo_read_control(struct fi
* @buffer_len: Size of @buffer.
*
* Returns @buffer_len on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_write_control(struct file *file, const char __user *buffer,
const int buffer_len)
@@ -2144,11 +2151,14 @@ static int tomoyo_write_control(struct f
* @file: Pointer to "struct file".
*
* Releases memory and returns 0.
+ *
+ * Caller looses tomoyo_read_lock().
*/
static int tomoyo_close_control(struct file *file)
{
struct tomoyo_io_buffer *head = file->private_data;
+ tomoyo_read_unlock(head->reader_idx);
/* Release memory used for policy I/O. */
tomoyo_free(head->read_buf);
head->read_buf = NULL;
--- security-testing-2.6.orig/security/tomoyo/common.h
+++ security-testing-2.6/security/tomoyo/common.h
@@ -265,6 +265,8 @@ struct tomoyo_io_buffer {
int (*write) (struct tomoyo_io_buffer *);
/* Exclusive lock for this structure. */
struct mutex io_sem;
+ /* Index returned by tomoyo_read_lock(). */
+ int reader_idx;
/* The position currently reading from. */
struct list_head *read_var1;
/* Extra variables for reading. */
@@ -442,16 +444,28 @@ extern struct tomoyo_domain_info tomoyo_
* @cookie: the &struct list_head to use as a cookie.
* @head: the head for your list.
*
- * Same with list_for_each() except that this primitive uses @cookie
+ * Same with list_for_each_rcu() except that this primitive uses @cookie
* so that we can continue iteration.
* @cookie must be NULL when iteration starts, and @cookie will become
* NULL when iteration finishes.
*/
-#define list_for_each_cookie(pos, cookie, head) \
- for (({ if (!cookie) \
- cookie = head; }), \
- pos = (cookie)->next; \
- prefetch(pos->next), pos != (head) || ((cookie) = NULL); \
- (cookie) = pos, pos = pos->next)
+#define list_for_each_cookie(pos, cookie, head) \
+ for (({ if (!cookie) \
+ cookie = head; }), \
+ pos = rcu_dereference((cookie)->next); \
+ prefetch(pos->next), pos != (head) || ((cookie) = NULL); \
+ (cookie) = pos, pos = rcu_dereference(pos->next))
+
+extern struct srcu_struct tomoyo_ss;
+
+static inline int tomoyo_read_lock(void)
+{
+ return srcu_read_lock(&tomoyo_ss);
+}
+
+static inline void tomoyo_read_unlock(int idx)
+{
+ srcu_read_unlock(&tomoyo_ss, idx);
+}
#endif /* !defined(_SECURITY_TOMOYO_COMMON_H) */
--- security-testing-2.6.orig/security/tomoyo/domain.c
+++ security-testing-2.6/security/tomoyo/domain.c
@@ -217,6 +217,8 @@ static DECLARE_RWSEM(tomoyo_domain_initi
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_update_domain_initializer_entry(const char *domainname,
const char *program,
@@ -246,7 +248,7 @@ static int tomoyo_update_domain_initiali
if (!saved_program)
return -ENOMEM;
down_write(&tomoyo_domain_initializer_list_lock);
- list_for_each_entry(ptr, &tomoyo_domain_initializer_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, list) {
if (ptr->is_not != is_not ||
ptr->domainname != saved_domainname ||
ptr->program != saved_program)
@@ -266,7 +268,7 @@ static int tomoyo_update_domain_initiali
new_entry->program = saved_program;
new_entry->is_not = is_not;
new_entry->is_last_name = is_last_name;
- list_add_tail(&new_entry->list, &tomoyo_domain_initializer_list);
+ list_add_tail_rcu(&new_entry->list, &tomoyo_domain_initializer_list);
error = 0;
out:
up_write(&tomoyo_domain_initializer_list_lock);
@@ -279,13 +281,14 @@ static int tomoyo_update_domain_initiali
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head)
{
struct list_head *pos;
bool done = true;
- down_read(&tomoyo_domain_initializer_list_lock);
list_for_each_cookie(pos, head->read_var2,
&tomoyo_domain_initializer_list) {
const char *no;
@@ -308,7 +311,6 @@ bool tomoyo_read_domain_initializer_poli
if (!done)
break;
}
- up_read(&tomoyo_domain_initializer_list_lock);
return done;
}
@@ -320,6 +322,8 @@ bool tomoyo_read_domain_initializer_poli
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
int tomoyo_write_domain_initializer_policy(char *data, const bool is_not,
const bool is_delete)
@@ -345,6 +349,8 @@ int tomoyo_write_domain_initializer_poli
*
* Returns true if executing @program reinitializes domain transition,
* false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static bool tomoyo_is_domain_initializer(const struct tomoyo_path_info *
domainname,
@@ -355,8 +361,7 @@ static bool tomoyo_is_domain_initializer
struct tomoyo_domain_initializer_entry *ptr;
bool flag = false;
- down_read(&tomoyo_domain_initializer_list_lock);
- list_for_each_entry(ptr, &tomoyo_domain_initializer_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, list) {
if (ptr->is_deleted)
continue;
if (ptr->domainname) {
@@ -376,7 +381,6 @@ static bool tomoyo_is_domain_initializer
}
flag = true;
}
- up_read(&tomoyo_domain_initializer_list_lock);
return flag;
}
@@ -430,6 +434,8 @@ static DECLARE_RWSEM(tomoyo_domain_keepe
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_update_domain_keeper_entry(const char *domainname,
const char *program,
@@ -459,7 +465,7 @@ static int tomoyo_update_domain_keeper_e
if (!saved_domainname)
return -ENOMEM;
down_write(&tomoyo_domain_keeper_list_lock);
- list_for_each_entry(ptr, &tomoyo_domain_keeper_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, list) {
if (ptr->is_not != is_not ||
ptr->domainname != saved_domainname ||
ptr->program != saved_program)
@@ -479,7 +485,7 @@ static int tomoyo_update_domain_keeper_e
new_entry->program = saved_program;
new_entry->is_not = is_not;
new_entry->is_last_name = is_last_name;
- list_add_tail(&new_entry->list, &tomoyo_domain_keeper_list);
+ list_add_tail_rcu(&new_entry->list, &tomoyo_domain_keeper_list);
error = 0;
out:
up_write(&tomoyo_domain_keeper_list_lock);
@@ -493,6 +499,7 @@ static int tomoyo_update_domain_keeper_e
* @is_not: True if it is "no_keep_domain" entry.
* @is_delete: True if it is a delete request.
*
+ * Caller holds tomoyo_read_lock().
*/
int tomoyo_write_domain_keeper_policy(char *data, const bool is_not,
const bool is_delete)
@@ -513,13 +520,14 @@ int tomoyo_write_domain_keeper_policy(ch
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head)
{
struct list_head *pos;
bool done = true;
- down_read(&tomoyo_domain_keeper_list_lock);
list_for_each_cookie(pos, head->read_var2,
&tomoyo_domain_keeper_list) {
struct tomoyo_domain_keeper_entry *ptr;
@@ -542,7 +550,6 @@ bool tomoyo_read_domain_keeper_policy(st
if (!done)
break;
}
- up_read(&tomoyo_domain_keeper_list_lock);
return done;
}
@@ -555,6 +562,8 @@ bool tomoyo_read_domain_keeper_policy(st
*
* Returns true if executing @program supresses domain transition,
* false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static bool tomoyo_is_domain_keeper(const struct tomoyo_path_info *domainname,
const struct tomoyo_path_info *program,
@@ -563,8 +572,7 @@ static bool tomoyo_is_domain_keeper(cons
struct tomoyo_domain_keeper_entry *ptr;
bool flag = false;
- down_read(&tomoyo_domain_keeper_list_lock);
- list_for_each_entry(ptr, &tomoyo_domain_keeper_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, list) {
if (ptr->is_deleted)
continue;
if (!ptr->is_last_name) {
@@ -582,7 +590,6 @@ static bool tomoyo_is_domain_keeper(cons
}
flag = true;
}
- up_read(&tomoyo_domain_keeper_list_lock);
return flag;
}
@@ -627,6 +634,8 @@ static DECLARE_RWSEM(tomoyo_alias_list_l
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_update_alias_entry(const char *original_name,
const char *aliased_name,
@@ -646,7 +655,7 @@ static int tomoyo_update_alias_entry(con
if (!saved_original_name || !saved_aliased_name)
return -ENOMEM;
down_write(&tomoyo_alias_list_lock);
- list_for_each_entry(ptr, &tomoyo_alias_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_alias_list, list) {
if (ptr->original_name != saved_original_name ||
ptr->aliased_name != saved_aliased_name)
continue;
@@ -663,7 +672,7 @@ static int tomoyo_update_alias_entry(con
goto out;
new_entry->original_name = saved_original_name;
new_entry->aliased_name = saved_aliased_name;
- list_add_tail(&new_entry->list, &tomoyo_alias_list);
+ list_add_tail_rcu(&new_entry->list, &tomoyo_alias_list);
error = 0;
out:
up_write(&tomoyo_alias_list_lock);
@@ -676,13 +685,14 @@ static int tomoyo_update_alias_entry(con
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head)
{
struct list_head *pos;
bool done = true;
- down_read(&tomoyo_alias_list_lock);
list_for_each_cookie(pos, head->read_var2, &tomoyo_alias_list) {
struct tomoyo_alias_entry *ptr;
@@ -695,7 +705,6 @@ bool tomoyo_read_alias_policy(struct tom
if (!done)
break;
}
- up_read(&tomoyo_alias_list_lock);
return done;
}
@@ -706,6 +715,8 @@ bool tomoyo_read_alias_policy(struct tom
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
int tomoyo_write_alias_policy(char *data, const bool is_delete)
{
@@ -724,6 +735,8 @@ int tomoyo_write_alias_policy(char *data
* @profile: Profile number to assign if the domain was newly created.
*
* Returns pointer to "struct tomoyo_domain_info" on success, NULL otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
domainname,
@@ -742,7 +755,7 @@ struct tomoyo_domain_info *tomoyo_find_o
if (!saved_domainname)
goto out;
/* Can I reuse memory of deleted domain? */
- list_for_each_entry(domain, &tomoyo_domain_list, list) {
+ list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
struct task_struct *p;
struct tomoyo_acl_info *ptr;
bool flag;
@@ -760,7 +773,7 @@ struct tomoyo_domain_info *tomoyo_find_o
read_unlock(&tasklist_lock);
if (flag)
continue;
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
ptr->type |= TOMOYO_ACL_DELETED;
}
tomoyo_set_domain_flag(domain, true, domain->flags);
@@ -776,7 +789,7 @@ struct tomoyo_domain_info *tomoyo_find_o
INIT_LIST_HEAD(&domain->acl_info_list);
domain->domainname = saved_domainname;
domain->profile = profile;
- list_add_tail(&domain->list, &tomoyo_domain_list);
+ list_add_tail_rcu(&domain->list, &tomoyo_domain_list);
}
out:
up_write(&tomoyo_domain_list_lock);
@@ -789,6 +802,8 @@ struct tomoyo_domain_info *tomoyo_find_o
* @bprm: Pointer to "struct linux_binprm".
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
int tomoyo_find_next_domain(struct linux_binprm *bprm)
{
@@ -849,8 +864,7 @@ int tomoyo_find_next_domain(struct linux
if (tomoyo_pathcmp(&r, &s)) {
struct tomoyo_alias_entry *ptr;
/* Is this program allowed to be called via symbolic links? */
- down_read(&tomoyo_alias_list_lock);
- list_for_each_entry(ptr, &tomoyo_alias_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_alias_list, list) {
if (ptr->is_deleted ||
tomoyo_pathcmp(&r, ptr->original_name) ||
tomoyo_pathcmp(&s, ptr->aliased_name))
@@ -861,7 +875,6 @@ int tomoyo_find_next_domain(struct linux
tomoyo_fill_path_info(&r);
break;
}
- up_read(&tomoyo_alias_list_lock);
}
/* Check execute permission. */
@@ -892,9 +905,7 @@ int tomoyo_find_next_domain(struct linux
}
if (domain || strlen(new_domain_name) >= TOMOYO_MAX_PATHNAME_LEN)
goto done;
- down_read(&tomoyo_domain_list_lock);
domain = tomoyo_find_domain(new_domain_name);
- up_read(&tomoyo_domain_list_lock);
if (domain)
goto done;
if (is_enforce)
--- security-testing-2.6.orig/security/tomoyo/file.c
+++ security-testing-2.6/security/tomoyo/file.c
@@ -205,6 +205,8 @@ static DECLARE_RWSEM(tomoyo_globally_rea
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_update_globally_readable_entry(const char *filename,
const bool is_delete)
@@ -220,7 +222,7 @@ static int tomoyo_update_globally_readab
if (!saved_filename)
return -ENOMEM;
down_write(&tomoyo_globally_readable_list_lock);
- list_for_each_entry(ptr, &tomoyo_globally_readable_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, list) {
if (ptr->filename != saved_filename)
continue;
ptr->is_deleted = is_delete;
@@ -235,7 +237,7 @@ static int tomoyo_update_globally_readab
if (!new_entry)
goto out;
new_entry->filename = saved_filename;
- list_add_tail(&new_entry->list, &tomoyo_globally_readable_list);
+ list_add_tail_rcu(&new_entry->list, &tomoyo_globally_readable_list);
error = 0;
out:
up_write(&tomoyo_globally_readable_list_lock);
@@ -248,21 +250,22 @@ static int tomoyo_update_globally_readab
* @filename: The filename to check.
*
* Returns true if any domain can open @filename for reading, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static bool tomoyo_is_globally_readable_file(const struct tomoyo_path_info *
filename)
{
struct tomoyo_globally_readable_file_entry *ptr;
bool found = false;
- down_read(&tomoyo_globally_readable_list_lock);
- list_for_each_entry(ptr, &tomoyo_globally_readable_list, list) {
+
+ list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, list) {
if (!ptr->is_deleted &&
tomoyo_path_matches_pattern(filename, ptr->filename)) {
found = true;
break;
}
}
- up_read(&tomoyo_globally_readable_list_lock);
return found;
}
@@ -273,6 +276,8 @@ static bool tomoyo_is_globally_readable_
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
int tomoyo_write_globally_readable_policy(char *data, const bool is_delete)
{
@@ -285,13 +290,14 @@ int tomoyo_write_globally_readable_polic
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head)
{
struct list_head *pos;
bool done = true;
- down_read(&tomoyo_globally_readable_list_lock);
list_for_each_cookie(pos, head->read_var2,
&tomoyo_globally_readable_list) {
struct tomoyo_globally_readable_file_entry *ptr;
@@ -305,7 +311,6 @@ bool tomoyo_read_globally_readable_polic
if (!done)
break;
}
- up_read(&tomoyo_globally_readable_list_lock);
return done;
}
@@ -348,6 +353,8 @@ static DECLARE_RWSEM(tomoyo_pattern_list
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_update_file_pattern_entry(const char *pattern,
const bool is_delete)
@@ -363,7 +370,7 @@ static int tomoyo_update_file_pattern_en
if (!saved_pattern)
return -ENOMEM;
down_write(&tomoyo_pattern_list_lock);
- list_for_each_entry(ptr, &tomoyo_pattern_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) {
if (saved_pattern != ptr->pattern)
continue;
ptr->is_deleted = is_delete;
@@ -378,7 +385,7 @@ static int tomoyo_update_file_pattern_en
if (!new_entry)
goto out;
new_entry->pattern = saved_pattern;
- list_add_tail(&new_entry->list, &tomoyo_pattern_list);
+ list_add_tail_rcu(&new_entry->list, &tomoyo_pattern_list);
error = 0;
out:
up_write(&tomoyo_pattern_list_lock);
@@ -391,6 +398,8 @@ static int tomoyo_update_file_pattern_en
* @filename: The filename to find patterned pathname.
*
* Returns pointer to pathname pattern if matched, @filename otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static const struct tomoyo_path_info *
tomoyo_get_file_pattern(const struct tomoyo_path_info *filename)
@@ -398,8 +407,7 @@ tomoyo_get_file_pattern(const struct tom
struct tomoyo_pattern_entry *ptr;
const struct tomoyo_path_info *pattern = NULL;
- down_read(&tomoyo_pattern_list_lock);
- list_for_each_entry(ptr, &tomoyo_pattern_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) {
if (ptr->is_deleted)
continue;
if (!tomoyo_path_matches_pattern(filename, ptr->pattern))
@@ -412,7 +420,6 @@ tomoyo_get_file_pattern(const struct tom
break;
}
}
- up_read(&tomoyo_pattern_list_lock);
if (pattern)
filename = pattern;
return filename;
@@ -425,6 +432,8 @@ tomoyo_get_file_pattern(const struct tom
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
int tomoyo_write_pattern_policy(char *data, const bool is_delete)
{
@@ -437,13 +446,14 @@ int tomoyo_write_pattern_policy(char *da
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head)
{
struct list_head *pos;
bool done = true;
- down_read(&tomoyo_pattern_list_lock);
list_for_each_cookie(pos, head->read_var2, &tomoyo_pattern_list) {
struct tomoyo_pattern_entry *ptr;
ptr = list_entry(pos, struct tomoyo_pattern_entry, list);
@@ -454,7 +464,6 @@ bool tomoyo_read_file_pattern(struct tom
if (!done)
break;
}
- up_read(&tomoyo_pattern_list_lock);
return done;
}
@@ -497,6 +506,8 @@ static DECLARE_RWSEM(tomoyo_no_rewrite_l
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_update_no_rewrite_entry(const char *pattern,
const bool is_delete)
@@ -511,7 +522,7 @@ static int tomoyo_update_no_rewrite_entr
if (!saved_pattern)
return -ENOMEM;
down_write(&tomoyo_no_rewrite_list_lock);
- list_for_each_entry(ptr, &tomoyo_no_rewrite_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, list) {
if (ptr->pattern != saved_pattern)
continue;
ptr->is_deleted = is_delete;
@@ -526,7 +537,7 @@ static int tomoyo_update_no_rewrite_entr
if (!new_entry)
goto out;
new_entry->pattern = saved_pattern;
- list_add_tail(&new_entry->list, &tomoyo_no_rewrite_list);
+ list_add_tail_rcu(&new_entry->list, &tomoyo_no_rewrite_list);
error = 0;
out:
up_write(&tomoyo_no_rewrite_list_lock);
@@ -540,14 +551,15 @@ static int tomoyo_update_no_rewrite_entr
*
* Returns true if @filename is specified by "deny_rewrite" directive,
* false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static bool tomoyo_is_no_rewrite_file(const struct tomoyo_path_info *filename)
{
struct tomoyo_no_rewrite_entry *ptr;
bool found = false;
- down_read(&tomoyo_no_rewrite_list_lock);
- list_for_each_entry(ptr, &tomoyo_no_rewrite_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, list) {
if (ptr->is_deleted)
continue;
if (!tomoyo_path_matches_pattern(filename, ptr->pattern))
@@ -555,7 +567,6 @@ static bool tomoyo_is_no_rewrite_file(co
found = true;
break;
}
- up_read(&tomoyo_no_rewrite_list_lock);
return found;
}
@@ -566,6 +577,8 @@ static bool tomoyo_is_no_rewrite_file(co
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
int tomoyo_write_no_rewrite_policy(char *data, const bool is_delete)
{
@@ -578,13 +591,14 @@ int tomoyo_write_no_rewrite_policy(char
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head)
{
struct list_head *pos;
bool done = true;
- down_read(&tomoyo_no_rewrite_list_lock);
list_for_each_cookie(pos, head->read_var2, &tomoyo_no_rewrite_list) {
struct tomoyo_no_rewrite_entry *ptr;
ptr = list_entry(pos, struct tomoyo_no_rewrite_entry, list);
@@ -595,7 +609,6 @@ bool tomoyo_read_no_rewrite_policy(struc
if (!done)
break;
}
- up_read(&tomoyo_no_rewrite_list_lock);
return done;
}
@@ -613,6 +626,8 @@ bool tomoyo_read_no_rewrite_policy(struc
* Current policy syntax uses "allow_read/write" instead of "6",
* "allow_read" instead of "4", "allow_write" instead of "2",
* "allow_execute" instead of "1".
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_update_file_acl(const char *filename, u8 perm,
struct tomoyo_domain_info * const domain,
@@ -650,6 +665,8 @@ static int tomoyo_update_file_acl(const
* @may_use_pattern: True if patterned ACL is permitted.
*
* Returns 0 on success, -EPERM otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_check_single_path_acl2(const struct tomoyo_domain_info *
domain,
@@ -661,8 +678,7 @@ static int tomoyo_check_single_path_acl2
struct tomoyo_acl_info *ptr;
int error = -EPERM;
- down_read(&tomoyo_domain_acl_info_list_lock);
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
struct tomoyo_single_path_acl_record *acl;
if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL)
continue;
@@ -680,7 +696,6 @@ static int tomoyo_check_single_path_acl2
error = 0;
break;
}
- up_read(&tomoyo_domain_acl_info_list_lock);
return error;
}
@@ -692,6 +707,8 @@ static int tomoyo_check_single_path_acl2
* @operation: Mode ("read" or "write" or "read/write" or "execute").
*
* Returns 0 on success, -EPERM otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_check_file_acl(const struct tomoyo_domain_info *domain,
const struct tomoyo_path_info *filename,
@@ -725,6 +742,8 @@ static int tomoyo_check_file_acl(const s
* @mode: Access control mode.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_check_file_perm2(struct tomoyo_domain_info * const domain,
const struct tomoyo_path_info *filename,
@@ -778,6 +797,8 @@ static int tomoyo_check_file_perm2(struc
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
int tomoyo_write_file_policy(char *data, struct tomoyo_domain_info *domain,
const bool is_delete)
@@ -825,6 +846,8 @@ int tomoyo_write_file_policy(char *data,
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_update_single_path_acl(const u8 type, const char *filename,
struct tomoyo_domain_info *
@@ -848,7 +871,7 @@ static int tomoyo_update_single_path_acl
down_write(&tomoyo_domain_acl_info_list_lock);
if (is_delete)
goto delete;
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
if (tomoyo_acl_type1(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL)
continue;
acl = container_of(ptr, struct tomoyo_single_path_acl_record,
@@ -875,12 +898,12 @@ static int tomoyo_update_single_path_acl
if (perm == (1 << TOMOYO_TYPE_READ_WRITE_ACL))
acl->perm |= rw_mask;
acl->filename = saved_filename;
- list_add_tail(&acl->head.list, &domain->acl_info_list);
+ list_add_tail_rcu(&acl->head.list, &domain->acl_info_list);
error = 0;
goto out;
delete:
error = -ENOENT;
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL)
continue;
acl = container_of(ptr, struct tomoyo_single_path_acl_record,
@@ -912,6 +935,8 @@ static int tomoyo_update_single_path_acl
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_update_double_path_acl(const u8 type, const char *filename1,
const char *filename2,
@@ -937,7 +962,7 @@ static int tomoyo_update_double_path_acl
down_write(&tomoyo_domain_acl_info_list_lock);
if (is_delete)
goto delete;
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
if (tomoyo_acl_type1(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL)
continue;
acl = container_of(ptr, struct tomoyo_double_path_acl_record,
@@ -960,12 +985,12 @@ static int tomoyo_update_double_path_acl
acl->perm = perm;
acl->filename1 = saved_filename1;
acl->filename2 = saved_filename2;
- list_add_tail(&acl->head.list, &domain->acl_info_list);
+ list_add_tail_rcu(&acl->head.list, &domain->acl_info_list);
error = 0;
goto out;
delete:
error = -ENOENT;
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL)
continue;
acl = container_of(ptr, struct tomoyo_double_path_acl_record,
@@ -992,6 +1017,8 @@ static int tomoyo_update_double_path_acl
* @filename: Filename to check.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_check_single_path_acl(struct tomoyo_domain_info *domain,
const u8 type,
@@ -1011,6 +1038,8 @@ static int tomoyo_check_single_path_acl(
* @filename2: Second filename to check.
*
* Returns 0 on success, -EPERM otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_check_double_path_acl(const struct tomoyo_domain_info *domain,
const u8 type,
@@ -1025,8 +1054,7 @@ static int tomoyo_check_double_path_acl(
if (!tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE))
return 0;
- down_read(&tomoyo_domain_acl_info_list_lock);
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
struct tomoyo_double_path_acl_record *acl;
if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL)
continue;
@@ -1041,7 +1069,6 @@ static int tomoyo_check_double_path_acl(
error = 0;
break;
}
- up_read(&tomoyo_domain_acl_info_list_lock);
return error;
}
@@ -1054,6 +1081,8 @@ static int tomoyo_check_double_path_acl(
* @mode: Access control mode.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_check_single_path_permission2(struct tomoyo_domain_info *
const domain, u8 operation,
@@ -1123,6 +1152,8 @@ int tomoyo_check_file_perm(struct tomoyo
* @filename: Check permission for "execute".
*
* Returns 0 on success, negativevalue otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
int tomoyo_check_exec_perm(struct tomoyo_domain_info *domain,
const struct tomoyo_path_info *filename)
@@ -1151,6 +1182,7 @@ int tomoyo_check_open_permission(struct
struct tomoyo_path_info *buf;
const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
const bool is_enforce = (mode == 3);
+ int idx;
if (!mode || !path->mnt)
return 0;
@@ -1162,6 +1194,7 @@ int tomoyo_check_open_permission(struct
* don't call me.
*/
return 0;
+ idx = tomoyo_read_lock();
buf = tomoyo_get_path(path);
if (!buf)
goto out;
@@ -1187,6 +1220,7 @@ int tomoyo_check_open_permission(struct
buf, mode);
out:
tomoyo_free(buf);
+ tomoyo_read_unlock(idx);
if (!is_enforce)
error = 0;
return error;
@@ -1208,9 +1242,11 @@ int tomoyo_check_1path_perm(struct tomoy
struct tomoyo_path_info *buf;
const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
const bool is_enforce = (mode == 3);
+ int idx;
if (!mode || !path->mnt)
return 0;
+ idx = tomoyo_read_lock();
buf = tomoyo_get_path(path);
if (!buf)
goto out;
@@ -1229,6 +1265,7 @@ int tomoyo_check_1path_perm(struct tomoy
mode);
out:
tomoyo_free(buf);
+ tomoyo_read_unlock(idx);
if (!is_enforce)
error = 0;
return error;
@@ -1249,9 +1286,12 @@ int tomoyo_check_rewrite_permission(stru
const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
const bool is_enforce = (mode == 3);
struct tomoyo_path_info *buf;
+ int idx;
if (!mode || !filp->f_path.mnt)
return 0;
+
+ idx = tomoyo_read_lock();
buf = tomoyo_get_path(&filp->f_path);
if (!buf)
goto out;
@@ -1264,6 +1304,7 @@ int tomoyo_check_rewrite_permission(stru
buf, mode);
out:
tomoyo_free(buf);
+ tomoyo_read_unlock(idx);
if (!is_enforce)
error = 0;
return error;
@@ -1288,9 +1329,11 @@ int tomoyo_check_2path_perm(struct tomoy
const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
const bool is_enforce = (mode == 3);
const char *msg;
+ int idx;
if (!mode || !path1->mnt || !path2->mnt)
return 0;
+ idx = tomoyo_read_lock();
buf1 = tomoyo_get_path(path1);
buf2 = tomoyo_get_path(path2);
if (!buf1 || !buf2)
@@ -1329,6 +1372,7 @@ int tomoyo_check_2path_perm(struct tomoy
out:
tomoyo_free(buf1);
tomoyo_free(buf2);
+ tomoyo_read_unlock(idx);
if (!is_enforce)
error = 0;
return error;
--- security-testing-2.6.orig/security/tomoyo/realpath.c
+++ security-testing-2.6/security/tomoyo/realpath.c
@@ -392,11 +392,13 @@ void __init tomoyo_realpath_init(void)
INIT_LIST_HEAD(&tomoyo_name_list[i]);
INIT_LIST_HEAD(&tomoyo_kernel_domain.acl_info_list);
tomoyo_kernel_domain.domainname = tomoyo_save_name(TOMOYO_ROOT_NAME);
- list_add_tail(&tomoyo_kernel_domain.list, &tomoyo_domain_list);
- down_read(&tomoyo_domain_list_lock);
+ /*
+ * tomoyo_read_lock() is not needed because this function is
+ * called before the first "delete" request.
+ */
+ list_add_tail_rcu(&tomoyo_kernel_domain.list, &tomoyo_domain_list);
if (tomoyo_find_domain(TOMOYO_ROOT_NAME) != &tomoyo_kernel_domain)
panic("Can't register tomoyo_kernel_domain");
- up_read(&tomoyo_domain_list_lock);
}
/* Memory allocated for temporary purpose. */
--- security-testing-2.6.orig/security/tomoyo/tomoyo.c
+++ security-testing-2.6/security/tomoyo/tomoyo.c
@@ -76,8 +76,18 @@ static int tomoyo_bprm_check_security(st
* Execute permission is checked against pathname passed to do_execve()
* using current domain.
*/
- if (!domain)
- return tomoyo_find_next_domain(bprm);
+ if (!domain) {
+ /*
+ * We will need to protect whole execve() operation when GC
+ * starts kfree()ing "struct tomoyo_domain_info" because
+ * bprm->cred->security points to "struct tomoyo_domain_info"
+ * but "struct tomoyo_domain_info" does not have a refcounter.
+ */
+ const int idx = tomoyo_read_lock();
+ const int err = tomoyo_find_next_domain(bprm);
+ tomoyo_read_unlock(idx);
+ return err;
+ }
/*
* Read permission is checked against interpreters using next domain.
* '1' is the result of open_to_namei_flags(O_RDONLY).
@@ -297,6 +307,9 @@ static struct security_operations tomoyo
.path_rename = tomoyo_path_rename,
};
+/* Lock for GC. */
+struct srcu_struct tomoyo_ss;
+
static int __init tomoyo_init(void)
{
struct cred *cred = (struct cred *) current_cred();
@@ -304,7 +317,8 @@ static int __init tomoyo_init(void)
if (!security_module_enable(&tomoyo_security_ops))
return 0;
/* register ourselves with the security framework */
- if (register_security(&tomoyo_security_ops))
+ if (register_security(&tomoyo_security_ops) ||
+ init_srcu_struct(&tomoyo_ss))
panic("Failure registering TOMOYO Linux");
printk(KERN_INFO "TOMOYO Linux initialized\n");
cred->security = &tomoyo_kernel_domain;
^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH] TOMOYO: Use RCU primitives for list operation
@ 2009-12-11 13:53 Tetsuo Handa
2009-12-14 16:39 ` Serge E. Hallyn
0 siblings, 1 reply; 50+ messages in thread
From: Tetsuo Handa @ 2009-12-11 13:53 UTC (permalink / raw)
To: linux-security-module; +Cc: linux-kernel
Can somebody please review?
----------
[PATCH] TOMOYO: Use RCU primitives for list operation
Currently, TOMOYO is using down_read() only for protecting ->prev member from
writers. If we convert list operation using RCU primitives so that readers don't
look at ->prev member, we can replace down_read() by srcu_read_lock().
tomoyo_read_lock()/tomoyo_read_unlock() (these are wrappers for
srcu_read_lock()/srcu_read_unlock()) protects the data against the garbage
collector (which will be added in the future). I call tomoyo_read_lock() when
/sys/kernel/security/tomoyo/ interface is open()ed and call
tomoyo_read_unlock() when /sys/kernel/security/tomoyo/ interface is close()d
rather than calling tomoyo_read_lock()/tomoyo_read_unlock() upon individual
read()/write() requests. In this way, the pointers saved in
"struct tomoyo_io_buffer" are guaranteed to be valid. Please ignore
warning: context imbalance in 'tomoyo_open_control' - wrong count at exit
warning: context imbalance in 'tomoyo_close_control' - unexpected unlock
messages when built with "C=1" option.
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
common.c | 90 +++++++++++++++++++++++++++----------------------
common.h | 28 +++++++++++----
domain.c | 63 ++++++++++++++++++++--------------
file.c | 110 ++++++++++++++++++++++++++++++++++++++++++-------------------
realpath.c | 8 ++--
tomoyo.c | 20 +++++++++--
6 files changed, 207 insertions(+), 112 deletions(-)
diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c
index 6c60616..f01b936 100644
--- a/security/tomoyo/common.c
+++ b/security/tomoyo/common.c
@@ -365,10 +365,9 @@ bool tomoyo_is_domain_def(const unsigned char *buffer)
*
* @domainname: The domainname to find.
*
- * Caller must call down_read(&tomoyo_domain_list_lock); or
- * down_write(&tomoyo_domain_list_lock); .
- *
* Returns pointer to "struct tomoyo_domain_info" if found, NULL otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname)
{
@@ -377,7 +376,7 @@ struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname)
name.name = domainname;
tomoyo_fill_path_info(&name);
- list_for_each_entry(domain, &tomoyo_domain_list, list) {
+ list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
if (!domain->is_deleted &&
!tomoyo_pathcmp(&name, domain->domainname))
return domain;
@@ -829,6 +828,8 @@ bool tomoyo_verbose_mode(const struct tomoyo_domain_info *domain)
* @domain: Pointer to "struct tomoyo_domain_info".
*
* Returns true if the domain is not exceeded quota, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
bool tomoyo_domain_quota_is_ok(struct tomoyo_domain_info * const domain)
{
@@ -837,8 +838,7 @@ bool tomoyo_domain_quota_is_ok(struct tomoyo_domain_info * const domain)
if (!domain)
return true;
- down_read(&tomoyo_domain_acl_info_list_lock);
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
if (ptr->type & TOMOYO_ACL_DELETED)
continue;
switch (tomoyo_acl_type2(ptr)) {
@@ -866,7 +866,6 @@ bool tomoyo_domain_quota_is_ok(struct tomoyo_domain_info * const domain)
break;
}
}
- up_read(&tomoyo_domain_acl_info_list_lock);
if (count < tomoyo_check_flags(domain, TOMOYO_MAX_ACCEPT_ENTRY))
return true;
if (!domain->quota_warned) {
@@ -1096,6 +1095,8 @@ static DECLARE_RWSEM(tomoyo_policy_manager_list_lock);
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_update_manager_entry(const char *manager,
const bool is_delete)
@@ -1118,7 +1119,7 @@ static int tomoyo_update_manager_entry(const char *manager,
if (!saved_manager)
return -ENOMEM;
down_write(&tomoyo_policy_manager_list_lock);
- list_for_each_entry(ptr, &tomoyo_policy_manager_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, list) {
if (ptr->manager != saved_manager)
continue;
ptr->is_deleted = is_delete;
@@ -1134,7 +1135,7 @@ static int tomoyo_update_manager_entry(const char *manager,
goto out;
new_entry->manager = saved_manager;
new_entry->is_domain = is_domain;
- list_add_tail(&new_entry->list, &tomoyo_policy_manager_list);
+ list_add_tail_rcu(&new_entry->list, &tomoyo_policy_manager_list);
error = 0;
out:
up_write(&tomoyo_policy_manager_list_lock);
@@ -1147,6 +1148,8 @@ static int tomoyo_update_manager_entry(const char *manager,
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_write_manager_policy(struct tomoyo_io_buffer *head)
{
@@ -1166,6 +1169,8 @@ static int tomoyo_write_manager_policy(struct tomoyo_io_buffer *head)
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns 0.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_read_manager_policy(struct tomoyo_io_buffer *head)
{
@@ -1174,7 +1179,6 @@ static int tomoyo_read_manager_policy(struct tomoyo_io_buffer *head)
if (head->read_eof)
return 0;
- down_read(&tomoyo_policy_manager_list_lock);
list_for_each_cookie(pos, head->read_var2,
&tomoyo_policy_manager_list) {
struct tomoyo_policy_manager_entry *ptr;
@@ -1186,7 +1190,6 @@ static int tomoyo_read_manager_policy(struct tomoyo_io_buffer *head)
if (!done)
break;
}
- up_read(&tomoyo_policy_manager_list_lock);
head->read_eof = done;
return 0;
}
@@ -1196,6 +1199,8 @@ static int tomoyo_read_manager_policy(struct tomoyo_io_buffer *head)
*
* Returns true if the current process is permitted to modify policy
* via /sys/kernel/security/tomoyo/ interface.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static bool tomoyo_is_policy_manager(void)
{
@@ -1209,29 +1214,25 @@ static bool tomoyo_is_policy_manager(void)
return true;
if (!tomoyo_manage_by_non_root && (task->cred->uid || task->cred->euid))
return false;
- down_read(&tomoyo_policy_manager_list_lock);
- list_for_each_entry(ptr, &tomoyo_policy_manager_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, list) {
if (!ptr->is_deleted && ptr->is_domain
&& !tomoyo_pathcmp(domainname, ptr->manager)) {
found = true;
break;
}
}
- up_read(&tomoyo_policy_manager_list_lock);
if (found)
return true;
exe = tomoyo_get_exe();
if (!exe)
return false;
- down_read(&tomoyo_policy_manager_list_lock);
- list_for_each_entry(ptr, &tomoyo_policy_manager_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, list) {
if (!ptr->is_deleted && !ptr->is_domain
&& !strcmp(exe, ptr->manager->name)) {
found = true;
break;
}
}
- up_read(&tomoyo_policy_manager_list_lock);
if (!found) { /* Reduce error messages. */
static pid_t last_pid;
const pid_t pid = current->pid;
@@ -1252,6 +1253,8 @@ static bool tomoyo_is_policy_manager(void)
* @data: String to parse.
*
* Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head,
const char *data)
@@ -1267,11 +1270,8 @@ static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head,
domain = tomoyo_real_domain(p);
read_unlock(&tasklist_lock);
} else if (!strncmp(data, "domain=", 7)) {
- if (tomoyo_is_domain_def(data + 7)) {
- down_read(&tomoyo_domain_list_lock);
+ if (tomoyo_is_domain_def(data + 7))
domain = tomoyo_find_domain(data + 7);
- up_read(&tomoyo_domain_list_lock);
- }
} else
return false;
head->write_var1 = domain;
@@ -1285,13 +1285,11 @@ static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head,
if (domain) {
struct tomoyo_domain_info *d;
head->read_var1 = NULL;
- down_read(&tomoyo_domain_list_lock);
- list_for_each_entry(d, &tomoyo_domain_list, list) {
+ list_for_each_entry_rcu(d, &tomoyo_domain_list, list) {
if (d == domain)
break;
head->read_var1 = &d->list;
}
- up_read(&tomoyo_domain_list_lock);
head->read_var2 = NULL;
head->read_bit = 0;
head->read_step = 0;
@@ -1307,6 +1305,8 @@ static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head,
* @domainname: The name of domain.
*
* Returns 0.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_delete_domain(char *domainname)
{
@@ -1317,7 +1317,7 @@ static int tomoyo_delete_domain(char *domainname)
tomoyo_fill_path_info(&name);
down_write(&tomoyo_domain_list_lock);
/* Is there an active domain? */
- list_for_each_entry(domain, &tomoyo_domain_list, list) {
+ list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
/* Never delete tomoyo_kernel_domain */
if (domain == &tomoyo_kernel_domain)
continue;
@@ -1337,6 +1337,8 @@ static int tomoyo_delete_domain(char *domainname)
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_write_domain_policy(struct tomoyo_io_buffer *head)
{
@@ -1359,11 +1361,9 @@ static int tomoyo_write_domain_policy(struct tomoyo_io_buffer *head)
domain = NULL;
if (is_delete)
tomoyo_delete_domain(data);
- else if (is_select) {
- down_read(&tomoyo_domain_list_lock);
+ else if (is_select)
domain = tomoyo_find_domain(data);
- up_read(&tomoyo_domain_list_lock);
- } else
+ else
domain = tomoyo_find_or_assign_new_domain(data, 0);
head->write_var1 = domain;
return 0;
@@ -1508,6 +1508,8 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head,
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns 0.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_read_domain_policy(struct tomoyo_io_buffer *head)
{
@@ -1519,7 +1521,6 @@ static int tomoyo_read_domain_policy(struct tomoyo_io_buffer *head)
return 0;
if (head->read_step == 0)
head->read_step = 1;
- down_read(&tomoyo_domain_list_lock);
list_for_each_cookie(dpos, head->read_var1, &tomoyo_domain_list) {
struct tomoyo_domain_info *domain;
const char *quota_exceeded = "";
@@ -1552,7 +1553,6 @@ acl_loop:
if (head->read_step == 3)
goto tail_mark;
/* Print ACL entries in the domain. */
- down_read(&tomoyo_domain_acl_info_list_lock);
list_for_each_cookie(apos, head->read_var2,
&domain->acl_info_list) {
struct tomoyo_acl_info *ptr
@@ -1562,7 +1562,6 @@ acl_loop:
if (!done)
break;
}
- up_read(&tomoyo_domain_acl_info_list_lock);
if (!done)
break;
head->read_step = 3;
@@ -1574,7 +1573,6 @@ tail_mark:
if (head->read_single_domain)
break;
}
- up_read(&tomoyo_domain_list_lock);
head->read_eof = done;
return 0;
}
@@ -1590,6 +1588,8 @@ tail_mark:
*
* ( echo "select " $domainname; echo "use_profile " $profile ) |
* /usr/lib/ccs/loadpolicy -d
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_write_domain_profile(struct tomoyo_io_buffer *head)
{
@@ -1601,9 +1601,7 @@ static int tomoyo_write_domain_profile(struct tomoyo_io_buffer *head)
if (!cp)
return -EINVAL;
*cp = '\0';
- down_read(&tomoyo_domain_list_lock);
domain = tomoyo_find_domain(cp + 1);
- up_read(&tomoyo_domain_list_lock);
if (strict_strtoul(data, 10, &profile))
return -EINVAL;
if (domain && profile < TOMOYO_MAX_PROFILES
@@ -1625,6 +1623,8 @@ static int tomoyo_write_domain_profile(struct tomoyo_io_buffer *head)
* awk ' { if ( domainname == "" ) { if ( $1 == "<kernel>" )
* domainname = $0; } else if ( $1 == "use_profile" ) {
* print $2 " " domainname; domainname = ""; } } ; '
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_read_domain_profile(struct tomoyo_io_buffer *head)
{
@@ -1633,7 +1633,6 @@ static int tomoyo_read_domain_profile(struct tomoyo_io_buffer *head)
if (head->read_eof)
return 0;
- down_read(&tomoyo_domain_list_lock);
list_for_each_cookie(pos, head->read_var1, &tomoyo_domain_list) {
struct tomoyo_domain_info *domain;
domain = list_entry(pos, struct tomoyo_domain_info, list);
@@ -1644,7 +1643,6 @@ static int tomoyo_read_domain_profile(struct tomoyo_io_buffer *head)
if (!done)
break;
}
- up_read(&tomoyo_domain_list_lock);
head->read_eof = done;
return 0;
}
@@ -1701,6 +1699,8 @@ static int tomoyo_read_pid(struct tomoyo_io_buffer *head)
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_write_exception_policy(struct tomoyo_io_buffer *head)
{
@@ -1735,6 +1735,8 @@ static int tomoyo_write_exception_policy(struct tomoyo_io_buffer *head)
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns 0 on success, -EINVAL otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_read_exception_policy(struct tomoyo_io_buffer *head)
{
@@ -1864,15 +1866,13 @@ void tomoyo_load_policy(const char *filename)
tomoyo_policy_loaded = true;
{ /* Check all profiles currently assigned to domains are defined. */
struct tomoyo_domain_info *domain;
- down_read(&tomoyo_domain_list_lock);
- list_for_each_entry(domain, &tomoyo_domain_list, list) {
+ list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
const u8 profile = domain->profile;
if (tomoyo_profile_ptr[profile])
continue;
panic("Profile %u (used by '%s') not defined.\n",
profile, domain->domainname->name);
}
- up_read(&tomoyo_domain_list_lock);
}
}
@@ -1920,6 +1920,8 @@ static int tomoyo_read_self_domain(struct tomoyo_io_buffer *head)
* @file: Pointer to "struct file".
*
* Associates policy handler and returns 0 on success, -ENOMEM otherwise.
+ *
+ * Caller acquires tomoyo_read_lock().
*/
static int tomoyo_open_control(const u8 type, struct file *file)
{
@@ -2005,6 +2007,7 @@ static int tomoyo_open_control(const u8 type, struct file *file)
return -ENOMEM;
}
}
+ head->reader_idx = tomoyo_read_lock();
file->private_data = head;
/*
* Call the handler now if the file is
@@ -2026,6 +2029,8 @@ static int tomoyo_open_control(const u8 type, struct file *file)
* @buffer_len: Size of @buffer.
*
* Returns bytes read on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_read_control(struct file *file, char __user *buffer,
const int buffer_len)
@@ -2069,6 +2074,8 @@ static int tomoyo_read_control(struct file *file, char __user *buffer,
* @buffer_len: Size of @buffer.
*
* Returns @buffer_len on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_write_control(struct file *file, const char __user *buffer,
const int buffer_len)
@@ -2119,11 +2126,14 @@ static int tomoyo_write_control(struct file *file, const char __user *buffer,
* @file: Pointer to "struct file".
*
* Releases memory and returns 0.
+ *
+ * Caller looses tomoyo_read_lock().
*/
static int tomoyo_close_control(struct file *file)
{
struct tomoyo_io_buffer *head = file->private_data;
+ tomoyo_read_unlock(head->reader_idx);
/* Release memory used for policy I/O. */
tomoyo_free(head->read_buf);
head->read_buf = NULL;
diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h
index bd10f9f..c6f1392 100644
--- a/security/tomoyo/common.h
+++ b/security/tomoyo/common.h
@@ -269,6 +269,8 @@ struct tomoyo_io_buffer {
int (*write) (struct tomoyo_io_buffer *);
/* Exclusive lock for this structure. */
struct mutex io_sem;
+ /* Index returned by tomoyo_read_lock(). */
+ int reader_idx;
/* The position currently reading from. */
struct list_head *read_var1;
/* Extra variables for reading. */
@@ -446,16 +448,28 @@ extern struct tomoyo_domain_info tomoyo_kernel_domain;
* @cookie: the &struct list_head to use as a cookie.
* @head: the head for your list.
*
- * Same with list_for_each() except that this primitive uses @cookie
+ * Same with list_for_each_rcu() except that this primitive uses @cookie
* so that we can continue iteration.
* @cookie must be NULL when iteration starts, and @cookie will become
* NULL when iteration finishes.
*/
-#define list_for_each_cookie(pos, cookie, head) \
- for (({ if (!cookie) \
- cookie = head; }), \
- pos = (cookie)->next; \
- prefetch(pos->next), pos != (head) || ((cookie) = NULL); \
- (cookie) = pos, pos = pos->next)
+#define list_for_each_cookie(pos, cookie, head) \
+ for (({ if (!cookie) \
+ cookie = head; }), \
+ pos = rcu_dereference((cookie)->next); \
+ prefetch(pos->next), pos != (head) || ((cookie) = NULL); \
+ (cookie) = pos, pos = rcu_dereference(pos->next))
+
+extern struct srcu_struct tomoyo_ss;
+
+static inline int tomoyo_read_lock(void)
+{
+ return srcu_read_lock(&tomoyo_ss);
+}
+
+static inline void tomoyo_read_unlock(int idx)
+{
+ srcu_read_unlock(&tomoyo_ss, idx);
+}
#endif /* !defined(_SECURITY_TOMOYO_COMMON_H) */
diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c
index fcf52ac..2fd1901 100644
--- a/security/tomoyo/domain.c
+++ b/security/tomoyo/domain.c
@@ -217,6 +217,8 @@ static DECLARE_RWSEM(tomoyo_domain_initializer_list_lock);
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_update_domain_initializer_entry(const char *domainname,
const char *program,
@@ -246,7 +248,7 @@ static int tomoyo_update_domain_initializer_entry(const char *domainname,
if (!saved_program)
return -ENOMEM;
down_write(&tomoyo_domain_initializer_list_lock);
- list_for_each_entry(ptr, &tomoyo_domain_initializer_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, list) {
if (ptr->is_not != is_not ||
ptr->domainname != saved_domainname ||
ptr->program != saved_program)
@@ -266,7 +268,7 @@ static int tomoyo_update_domain_initializer_entry(const char *domainname,
new_entry->program = saved_program;
new_entry->is_not = is_not;
new_entry->is_last_name = is_last_name;
- list_add_tail(&new_entry->list, &tomoyo_domain_initializer_list);
+ list_add_tail_rcu(&new_entry->list, &tomoyo_domain_initializer_list);
error = 0;
out:
up_write(&tomoyo_domain_initializer_list_lock);
@@ -279,13 +281,14 @@ static int tomoyo_update_domain_initializer_entry(const char *domainname,
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head)
{
struct list_head *pos;
bool done = true;
- down_read(&tomoyo_domain_initializer_list_lock);
list_for_each_cookie(pos, head->read_var2,
&tomoyo_domain_initializer_list) {
const char *no;
@@ -308,7 +311,6 @@ bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head)
if (!done)
break;
}
- up_read(&tomoyo_domain_initializer_list_lock);
return done;
}
@@ -320,6 +322,8 @@ bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head)
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
int tomoyo_write_domain_initializer_policy(char *data, const bool is_not,
const bool is_delete)
@@ -345,6 +349,8 @@ int tomoyo_write_domain_initializer_policy(char *data, const bool is_not,
*
* Returns true if executing @program reinitializes domain transition,
* false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static bool tomoyo_is_domain_initializer(const struct tomoyo_path_info *
domainname,
@@ -355,8 +361,7 @@ static bool tomoyo_is_domain_initializer(const struct tomoyo_path_info *
struct tomoyo_domain_initializer_entry *ptr;
bool flag = false;
- down_read(&tomoyo_domain_initializer_list_lock);
- list_for_each_entry(ptr, &tomoyo_domain_initializer_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, list) {
if (ptr->is_deleted)
continue;
if (ptr->domainname) {
@@ -376,7 +381,6 @@ static bool tomoyo_is_domain_initializer(const struct tomoyo_path_info *
}
flag = true;
}
- up_read(&tomoyo_domain_initializer_list_lock);
return flag;
}
@@ -430,6 +434,8 @@ static DECLARE_RWSEM(tomoyo_domain_keeper_list_lock);
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_update_domain_keeper_entry(const char *domainname,
const char *program,
@@ -459,7 +465,7 @@ static int tomoyo_update_domain_keeper_entry(const char *domainname,
if (!saved_domainname)
return -ENOMEM;
down_write(&tomoyo_domain_keeper_list_lock);
- list_for_each_entry(ptr, &tomoyo_domain_keeper_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, list) {
if (ptr->is_not != is_not ||
ptr->domainname != saved_domainname ||
ptr->program != saved_program)
@@ -479,7 +485,7 @@ static int tomoyo_update_domain_keeper_entry(const char *domainname,
new_entry->program = saved_program;
new_entry->is_not = is_not;
new_entry->is_last_name = is_last_name;
- list_add_tail(&new_entry->list, &tomoyo_domain_keeper_list);
+ list_add_tail_rcu(&new_entry->list, &tomoyo_domain_keeper_list);
error = 0;
out:
up_write(&tomoyo_domain_keeper_list_lock);
@@ -493,6 +499,7 @@ static int tomoyo_update_domain_keeper_entry(const char *domainname,
* @is_not: True if it is "no_keep_domain" entry.
* @is_delete: True if it is a delete request.
*
+ * Caller holds tomoyo_read_lock().
*/
int tomoyo_write_domain_keeper_policy(char *data, const bool is_not,
const bool is_delete)
@@ -513,13 +520,14 @@ int tomoyo_write_domain_keeper_policy(char *data, const bool is_not,
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head)
{
struct list_head *pos;
bool done = true;
- down_read(&tomoyo_domain_keeper_list_lock);
list_for_each_cookie(pos, head->read_var2,
&tomoyo_domain_keeper_list) {
struct tomoyo_domain_keeper_entry *ptr;
@@ -542,7 +550,6 @@ bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head)
if (!done)
break;
}
- up_read(&tomoyo_domain_keeper_list_lock);
return done;
}
@@ -555,6 +562,8 @@ bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head)
*
* Returns true if executing @program supresses domain transition,
* false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static bool tomoyo_is_domain_keeper(const struct tomoyo_path_info *domainname,
const struct tomoyo_path_info *program,
@@ -563,8 +572,7 @@ static bool tomoyo_is_domain_keeper(const struct tomoyo_path_info *domainname,
struct tomoyo_domain_keeper_entry *ptr;
bool flag = false;
- down_read(&tomoyo_domain_keeper_list_lock);
- list_for_each_entry(ptr, &tomoyo_domain_keeper_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, list) {
if (ptr->is_deleted)
continue;
if (!ptr->is_last_name) {
@@ -582,7 +590,6 @@ static bool tomoyo_is_domain_keeper(const struct tomoyo_path_info *domainname,
}
flag = true;
}
- up_read(&tomoyo_domain_keeper_list_lock);
return flag;
}
@@ -627,6 +634,8 @@ static DECLARE_RWSEM(tomoyo_alias_list_lock);
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_update_alias_entry(const char *original_name,
const char *aliased_name,
@@ -646,7 +655,7 @@ static int tomoyo_update_alias_entry(const char *original_name,
if (!saved_original_name || !saved_aliased_name)
return -ENOMEM;
down_write(&tomoyo_alias_list_lock);
- list_for_each_entry(ptr, &tomoyo_alias_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_alias_list, list) {
if (ptr->original_name != saved_original_name ||
ptr->aliased_name != saved_aliased_name)
continue;
@@ -663,7 +672,7 @@ static int tomoyo_update_alias_entry(const char *original_name,
goto out;
new_entry->original_name = saved_original_name;
new_entry->aliased_name = saved_aliased_name;
- list_add_tail(&new_entry->list, &tomoyo_alias_list);
+ list_add_tail_rcu(&new_entry->list, &tomoyo_alias_list);
error = 0;
out:
up_write(&tomoyo_alias_list_lock);
@@ -676,13 +685,14 @@ static int tomoyo_update_alias_entry(const char *original_name,
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head)
{
struct list_head *pos;
bool done = true;
- down_read(&tomoyo_alias_list_lock);
list_for_each_cookie(pos, head->read_var2, &tomoyo_alias_list) {
struct tomoyo_alias_entry *ptr;
@@ -695,7 +705,6 @@ bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head)
if (!done)
break;
}
- up_read(&tomoyo_alias_list_lock);
return done;
}
@@ -706,6 +715,8 @@ bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head)
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
int tomoyo_write_alias_policy(char *data, const bool is_delete)
{
@@ -724,6 +735,8 @@ int tomoyo_write_alias_policy(char *data, const bool is_delete)
* @profile: Profile number to assign if the domain was newly created.
*
* Returns pointer to "struct tomoyo_domain_info" on success, NULL otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
domainname,
@@ -742,7 +755,7 @@ struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
if (!saved_domainname)
goto out;
/* Can I reuse memory of deleted domain? */
- list_for_each_entry(domain, &tomoyo_domain_list, list) {
+ list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
struct task_struct *p;
struct tomoyo_acl_info *ptr;
bool flag;
@@ -760,7 +773,7 @@ struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
read_unlock(&tasklist_lock);
if (flag)
continue;
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
ptr->type |= TOMOYO_ACL_DELETED;
}
tomoyo_set_domain_flag(domain, true, domain->flags);
@@ -776,7 +789,7 @@ struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
INIT_LIST_HEAD(&domain->acl_info_list);
domain->domainname = saved_domainname;
domain->profile = profile;
- list_add_tail(&domain->list, &tomoyo_domain_list);
+ list_add_tail_rcu(&domain->list, &tomoyo_domain_list);
}
out:
up_write(&tomoyo_domain_list_lock);
@@ -789,6 +802,8 @@ struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
* @bprm: Pointer to "struct linux_binprm".
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
int tomoyo_find_next_domain(struct linux_binprm *bprm)
{
@@ -849,8 +864,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
if (tomoyo_pathcmp(&r, &s)) {
struct tomoyo_alias_entry *ptr;
/* Is this program allowed to be called via symbolic links? */
- down_read(&tomoyo_alias_list_lock);
- list_for_each_entry(ptr, &tomoyo_alias_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_alias_list, list) {
if (ptr->is_deleted ||
tomoyo_pathcmp(&r, ptr->original_name) ||
tomoyo_pathcmp(&s, ptr->aliased_name))
@@ -861,7 +875,6 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
tomoyo_fill_path_info(&r);
break;
}
- up_read(&tomoyo_alias_list_lock);
}
/* Check execute permission. */
@@ -892,9 +905,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
}
if (domain || strlen(new_domain_name) >= TOMOYO_MAX_PATHNAME_LEN)
goto done;
- down_read(&tomoyo_domain_list_lock);
domain = tomoyo_find_domain(new_domain_name);
- up_read(&tomoyo_domain_list_lock);
if (domain)
goto done;
if (is_enforce)
diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c
index 482f0e7..3c47286 100644
--- a/security/tomoyo/file.c
+++ b/security/tomoyo/file.c
@@ -213,6 +213,8 @@ static DECLARE_RWSEM(tomoyo_globally_readable_list_lock);
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_update_globally_readable_entry(const char *filename,
const bool is_delete)
@@ -228,7 +230,7 @@ static int tomoyo_update_globally_readable_entry(const char *filename,
if (!saved_filename)
return -ENOMEM;
down_write(&tomoyo_globally_readable_list_lock);
- list_for_each_entry(ptr, &tomoyo_globally_readable_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, list) {
if (ptr->filename != saved_filename)
continue;
ptr->is_deleted = is_delete;
@@ -243,7 +245,7 @@ static int tomoyo_update_globally_readable_entry(const char *filename,
if (!new_entry)
goto out;
new_entry->filename = saved_filename;
- list_add_tail(&new_entry->list, &tomoyo_globally_readable_list);
+ list_add_tail_rcu(&new_entry->list, &tomoyo_globally_readable_list);
error = 0;
out:
up_write(&tomoyo_globally_readable_list_lock);
@@ -256,21 +258,22 @@ static int tomoyo_update_globally_readable_entry(const char *filename,
* @filename: The filename to check.
*
* Returns true if any domain can open @filename for reading, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static bool tomoyo_is_globally_readable_file(const struct tomoyo_path_info *
filename)
{
struct tomoyo_globally_readable_file_entry *ptr;
bool found = false;
- down_read(&tomoyo_globally_readable_list_lock);
- list_for_each_entry(ptr, &tomoyo_globally_readable_list, list) {
+
+ list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, list) {
if (!ptr->is_deleted &&
tomoyo_path_matches_pattern(filename, ptr->filename)) {
found = true;
break;
}
}
- up_read(&tomoyo_globally_readable_list_lock);
return found;
}
@@ -281,6 +284,8 @@ static bool tomoyo_is_globally_readable_file(const struct tomoyo_path_info *
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
int tomoyo_write_globally_readable_policy(char *data, const bool is_delete)
{
@@ -293,13 +298,14 @@ int tomoyo_write_globally_readable_policy(char *data, const bool is_delete)
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head)
{
struct list_head *pos;
bool done = true;
- down_read(&tomoyo_globally_readable_list_lock);
list_for_each_cookie(pos, head->read_var2,
&tomoyo_globally_readable_list) {
struct tomoyo_globally_readable_file_entry *ptr;
@@ -313,7 +319,6 @@ bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head)
if (!done)
break;
}
- up_read(&tomoyo_globally_readable_list_lock);
return done;
}
@@ -356,6 +361,8 @@ static DECLARE_RWSEM(tomoyo_pattern_list_lock);
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_update_file_pattern_entry(const char *pattern,
const bool is_delete)
@@ -371,7 +378,7 @@ static int tomoyo_update_file_pattern_entry(const char *pattern,
if (!saved_pattern)
return -ENOMEM;
down_write(&tomoyo_pattern_list_lock);
- list_for_each_entry(ptr, &tomoyo_pattern_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) {
if (saved_pattern != ptr->pattern)
continue;
ptr->is_deleted = is_delete;
@@ -386,7 +393,7 @@ static int tomoyo_update_file_pattern_entry(const char *pattern,
if (!new_entry)
goto out;
new_entry->pattern = saved_pattern;
- list_add_tail(&new_entry->list, &tomoyo_pattern_list);
+ list_add_tail_rcu(&new_entry->list, &tomoyo_pattern_list);
error = 0;
out:
up_write(&tomoyo_pattern_list_lock);
@@ -399,6 +406,8 @@ static int tomoyo_update_file_pattern_entry(const char *pattern,
* @filename: The filename to find patterned pathname.
*
* Returns pointer to pathname pattern if matched, @filename otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static const struct tomoyo_path_info *
tomoyo_get_file_pattern(const struct tomoyo_path_info *filename)
@@ -406,8 +415,7 @@ tomoyo_get_file_pattern(const struct tomoyo_path_info *filename)
struct tomoyo_pattern_entry *ptr;
const struct tomoyo_path_info *pattern = NULL;
- down_read(&tomoyo_pattern_list_lock);
- list_for_each_entry(ptr, &tomoyo_pattern_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) {
if (ptr->is_deleted)
continue;
if (!tomoyo_path_matches_pattern(filename, ptr->pattern))
@@ -420,7 +428,6 @@ tomoyo_get_file_pattern(const struct tomoyo_path_info *filename)
break;
}
}
- up_read(&tomoyo_pattern_list_lock);
if (pattern)
filename = pattern;
return filename;
@@ -433,6 +440,8 @@ tomoyo_get_file_pattern(const struct tomoyo_path_info *filename)
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
int tomoyo_write_pattern_policy(char *data, const bool is_delete)
{
@@ -445,13 +454,14 @@ int tomoyo_write_pattern_policy(char *data, const bool is_delete)
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head)
{
struct list_head *pos;
bool done = true;
- down_read(&tomoyo_pattern_list_lock);
list_for_each_cookie(pos, head->read_var2, &tomoyo_pattern_list) {
struct tomoyo_pattern_entry *ptr;
ptr = list_entry(pos, struct tomoyo_pattern_entry, list);
@@ -462,7 +472,6 @@ bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head)
if (!done)
break;
}
- up_read(&tomoyo_pattern_list_lock);
return done;
}
@@ -505,6 +514,8 @@ static DECLARE_RWSEM(tomoyo_no_rewrite_list_lock);
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_update_no_rewrite_entry(const char *pattern,
const bool is_delete)
@@ -519,7 +530,7 @@ static int tomoyo_update_no_rewrite_entry(const char *pattern,
if (!saved_pattern)
return -ENOMEM;
down_write(&tomoyo_no_rewrite_list_lock);
- list_for_each_entry(ptr, &tomoyo_no_rewrite_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, list) {
if (ptr->pattern != saved_pattern)
continue;
ptr->is_deleted = is_delete;
@@ -534,7 +545,7 @@ static int tomoyo_update_no_rewrite_entry(const char *pattern,
if (!new_entry)
goto out;
new_entry->pattern = saved_pattern;
- list_add_tail(&new_entry->list, &tomoyo_no_rewrite_list);
+ list_add_tail_rcu(&new_entry->list, &tomoyo_no_rewrite_list);
error = 0;
out:
up_write(&tomoyo_no_rewrite_list_lock);
@@ -548,14 +559,15 @@ static int tomoyo_update_no_rewrite_entry(const char *pattern,
*
* Returns true if @filename is specified by "deny_rewrite" directive,
* false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static bool tomoyo_is_no_rewrite_file(const struct tomoyo_path_info *filename)
{
struct tomoyo_no_rewrite_entry *ptr;
bool found = false;
- down_read(&tomoyo_no_rewrite_list_lock);
- list_for_each_entry(ptr, &tomoyo_no_rewrite_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, list) {
if (ptr->is_deleted)
continue;
if (!tomoyo_path_matches_pattern(filename, ptr->pattern))
@@ -563,7 +575,6 @@ static bool tomoyo_is_no_rewrite_file(const struct tomoyo_path_info *filename)
found = true;
break;
}
- up_read(&tomoyo_no_rewrite_list_lock);
return found;
}
@@ -574,6 +585,8 @@ static bool tomoyo_is_no_rewrite_file(const struct tomoyo_path_info *filename)
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
int tomoyo_write_no_rewrite_policy(char *data, const bool is_delete)
{
@@ -586,13 +599,14 @@ int tomoyo_write_no_rewrite_policy(char *data, const bool is_delete)
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head)
{
struct list_head *pos;
bool done = true;
- down_read(&tomoyo_no_rewrite_list_lock);
list_for_each_cookie(pos, head->read_var2, &tomoyo_no_rewrite_list) {
struct tomoyo_no_rewrite_entry *ptr;
ptr = list_entry(pos, struct tomoyo_no_rewrite_entry, list);
@@ -603,7 +617,6 @@ bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head)
if (!done)
break;
}
- up_read(&tomoyo_no_rewrite_list_lock);
return done;
}
@@ -621,6 +634,8 @@ bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head)
* Current policy syntax uses "allow_read/write" instead of "6",
* "allow_read" instead of "4", "allow_write" instead of "2",
* "allow_execute" instead of "1".
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_update_file_acl(const char *filename, u8 perm,
struct tomoyo_domain_info * const domain,
@@ -658,6 +673,8 @@ static int tomoyo_update_file_acl(const char *filename, u8 perm,
* @may_use_pattern: True if patterned ACL is permitted.
*
* Returns 0 on success, -EPERM otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_check_single_path_acl2(const struct tomoyo_domain_info *
domain,
@@ -669,8 +686,7 @@ static int tomoyo_check_single_path_acl2(const struct tomoyo_domain_info *
struct tomoyo_acl_info *ptr;
int error = -EPERM;
- down_read(&tomoyo_domain_acl_info_list_lock);
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
struct tomoyo_single_path_acl_record *acl;
if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL)
continue;
@@ -693,7 +709,6 @@ static int tomoyo_check_single_path_acl2(const struct tomoyo_domain_info *
error = 0;
break;
}
- up_read(&tomoyo_domain_acl_info_list_lock);
return error;
}
@@ -705,6 +720,8 @@ static int tomoyo_check_single_path_acl2(const struct tomoyo_domain_info *
* @operation: Mode ("read" or "write" or "read/write" or "execute").
*
* Returns 0 on success, -EPERM otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_check_file_acl(const struct tomoyo_domain_info *domain,
const struct tomoyo_path_info *filename,
@@ -738,6 +755,8 @@ static int tomoyo_check_file_acl(const struct tomoyo_domain_info *domain,
* @mode: Access control mode.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_check_file_perm2(struct tomoyo_domain_info * const domain,
const struct tomoyo_path_info *filename,
@@ -791,6 +810,8 @@ static int tomoyo_check_file_perm2(struct tomoyo_domain_info * const domain,
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
int tomoyo_write_file_policy(char *data, struct tomoyo_domain_info *domain,
const bool is_delete)
@@ -838,6 +859,8 @@ int tomoyo_write_file_policy(char *data, struct tomoyo_domain_info *domain,
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_update_single_path_acl(const u8 type, const char *filename,
struct tomoyo_domain_info *
@@ -861,7 +884,7 @@ static int tomoyo_update_single_path_acl(const u8 type, const char *filename,
down_write(&tomoyo_domain_acl_info_list_lock);
if (is_delete)
goto delete;
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
if (tomoyo_acl_type1(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL)
continue;
acl = container_of(ptr, struct tomoyo_single_path_acl_record,
@@ -894,12 +917,12 @@ static int tomoyo_update_single_path_acl(const u8 type, const char *filename,
if (perm == (1 << TOMOYO_TYPE_READ_WRITE_ACL))
acl->perm |= rw_mask;
acl->filename = saved_filename;
- list_add_tail(&acl->head.list, &domain->acl_info_list);
+ list_add_tail_rcu(&acl->head.list, &domain->acl_info_list);
error = 0;
goto out;
delete:
error = -ENOENT;
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL)
continue;
acl = container_of(ptr, struct tomoyo_single_path_acl_record,
@@ -934,6 +957,8 @@ static int tomoyo_update_single_path_acl(const u8 type, const char *filename,
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_update_double_path_acl(const u8 type, const char *filename1,
const char *filename2,
@@ -959,7 +984,7 @@ static int tomoyo_update_double_path_acl(const u8 type, const char *filename1,
down_write(&tomoyo_domain_acl_info_list_lock);
if (is_delete)
goto delete;
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
if (tomoyo_acl_type1(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL)
continue;
acl = container_of(ptr, struct tomoyo_double_path_acl_record,
@@ -982,12 +1007,12 @@ static int tomoyo_update_double_path_acl(const u8 type, const char *filename1,
acl->perm = perm;
acl->filename1 = saved_filename1;
acl->filename2 = saved_filename2;
- list_add_tail(&acl->head.list, &domain->acl_info_list);
+ list_add_tail_rcu(&acl->head.list, &domain->acl_info_list);
error = 0;
goto out;
delete:
error = -ENOENT;
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL)
continue;
acl = container_of(ptr, struct tomoyo_double_path_acl_record,
@@ -1014,6 +1039,8 @@ static int tomoyo_update_double_path_acl(const u8 type, const char *filename1,
* @filename: Filename to check.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_check_single_path_acl(struct tomoyo_domain_info *domain,
const u8 type,
@@ -1033,6 +1060,8 @@ static int tomoyo_check_single_path_acl(struct tomoyo_domain_info *domain,
* @filename2: Second filename to check.
*
* Returns 0 on success, -EPERM otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_check_double_path_acl(const struct tomoyo_domain_info *domain,
const u8 type,
@@ -1047,8 +1076,7 @@ static int tomoyo_check_double_path_acl(const struct tomoyo_domain_info *domain,
if (!tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE))
return 0;
- down_read(&tomoyo_domain_acl_info_list_lock);
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
struct tomoyo_double_path_acl_record *acl;
if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL)
continue;
@@ -1063,7 +1091,6 @@ static int tomoyo_check_double_path_acl(const struct tomoyo_domain_info *domain,
error = 0;
break;
}
- up_read(&tomoyo_domain_acl_info_list_lock);
return error;
}
@@ -1076,6 +1103,8 @@ static int tomoyo_check_double_path_acl(const struct tomoyo_domain_info *domain,
* @mode: Access control mode.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_check_single_path_permission2(struct tomoyo_domain_info *
const domain, u8 operation,
@@ -1124,6 +1153,8 @@ static int tomoyo_check_single_path_permission2(struct tomoyo_domain_info *
* @filename: Check permission for "execute".
*
* Returns 0 on success, negativevalue otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
int tomoyo_check_exec_perm(struct tomoyo_domain_info *domain,
const struct tomoyo_path_info *filename)
@@ -1152,6 +1183,7 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
struct tomoyo_path_info *buf;
const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
const bool is_enforce = (mode == 3);
+ int idx;
if (!mode || !path->mnt)
return 0;
@@ -1163,6 +1195,7 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
* don't call me.
*/
return 0;
+ idx = tomoyo_read_lock();
buf = tomoyo_get_path(path);
if (!buf)
goto out;
@@ -1188,6 +1221,7 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
buf, mode);
out:
tomoyo_free(buf);
+ tomoyo_read_unlock(idx);
if (!is_enforce)
error = 0;
return error;
@@ -1209,9 +1243,11 @@ int tomoyo_check_1path_perm(struct tomoyo_domain_info *domain,
struct tomoyo_path_info *buf;
const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
const bool is_enforce = (mode == 3);
+ int idx;
if (!mode || !path->mnt)
return 0;
+ idx = tomoyo_read_lock();
buf = tomoyo_get_path(path);
if (!buf)
goto out;
@@ -1231,6 +1267,7 @@ int tomoyo_check_1path_perm(struct tomoyo_domain_info *domain,
mode);
out:
tomoyo_free(buf);
+ tomoyo_read_unlock(idx);
if (!is_enforce)
error = 0;
return error;
@@ -1251,9 +1288,12 @@ int tomoyo_check_rewrite_permission(struct tomoyo_domain_info *domain,
const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
const bool is_enforce = (mode == 3);
struct tomoyo_path_info *buf;
+ int idx;
if (!mode || !filp->f_path.mnt)
return 0;
+
+ idx = tomoyo_read_lock();
buf = tomoyo_get_path(&filp->f_path);
if (!buf)
goto out;
@@ -1266,6 +1306,7 @@ int tomoyo_check_rewrite_permission(struct tomoyo_domain_info *domain,
buf, mode);
out:
tomoyo_free(buf);
+ tomoyo_read_unlock(idx);
if (!is_enforce)
error = 0;
return error;
@@ -1290,9 +1331,11 @@ int tomoyo_check_2path_perm(struct tomoyo_domain_info * const domain,
const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
const bool is_enforce = (mode == 3);
const char *msg;
+ int idx;
if (!mode || !path1->mnt || !path2->mnt)
return 0;
+ idx = tomoyo_read_lock();
buf1 = tomoyo_get_path(path1);
buf2 = tomoyo_get_path(path2);
if (!buf1 || !buf2)
@@ -1331,6 +1374,7 @@ int tomoyo_check_2path_perm(struct tomoyo_domain_info * const domain,
out:
tomoyo_free(buf1);
tomoyo_free(buf2);
+ tomoyo_read_unlock(idx);
if (!is_enforce)
error = 0;
return error;
diff --git a/security/tomoyo/realpath.c b/security/tomoyo/realpath.c
index e3c7aa0..62363b3 100644
--- a/security/tomoyo/realpath.c
+++ b/security/tomoyo/realpath.c
@@ -402,11 +402,13 @@ void __init tomoyo_realpath_init(void)
INIT_LIST_HEAD(&tomoyo_name_list[i]);
INIT_LIST_HEAD(&tomoyo_kernel_domain.acl_info_list);
tomoyo_kernel_domain.domainname = tomoyo_save_name(TOMOYO_ROOT_NAME);
- list_add_tail(&tomoyo_kernel_domain.list, &tomoyo_domain_list);
- down_read(&tomoyo_domain_list_lock);
+ /*
+ * tomoyo_read_lock() is not needed because this function is
+ * called before the first "delete" request.
+ */
+ list_add_tail_rcu(&tomoyo_kernel_domain.list, &tomoyo_domain_list);
if (tomoyo_find_domain(TOMOYO_ROOT_NAME) != &tomoyo_kernel_domain)
panic("Can't register tomoyo_kernel_domain");
- up_read(&tomoyo_domain_list_lock);
}
/* Memory allocated for temporary purpose. */
diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c
index ad9555f..714daa3 100644
--- a/security/tomoyo/tomoyo.c
+++ b/security/tomoyo/tomoyo.c
@@ -76,8 +76,18 @@ static int tomoyo_bprm_check_security(struct linux_binprm *bprm)
* Execute permission is checked against pathname passed to do_execve()
* using current domain.
*/
- if (!domain)
- return tomoyo_find_next_domain(bprm);
+ if (!domain) {
+ /*
+ * We will need to protect whole execve() operation when GC
+ * starts kfree()ing "struct tomoyo_domain_info" because
+ * bprm->cred->security points to "struct tomoyo_domain_info"
+ * but "struct tomoyo_domain_info" does not have a refcounter.
+ */
+ const int idx = tomoyo_read_lock();
+ const int err = tomoyo_find_next_domain(bprm);
+ tomoyo_read_unlock(idx);
+ return err;
+ }
/*
* Read permission is checked against interpreters using next domain.
* '1' is the result of open_to_namei_flags(O_RDONLY).
@@ -278,6 +288,9 @@ static struct security_operations tomoyo_security_ops = {
.sb_pivotroot = tomoyo_sb_pivotroot,
};
+/* Lock for GC. */
+struct srcu_struct tomoyo_ss;
+
static int __init tomoyo_init(void)
{
struct cred *cred = (struct cred *) current_cred();
@@ -285,7 +298,8 @@ static int __init tomoyo_init(void)
if (!security_module_enable(&tomoyo_security_ops))
return 0;
/* register ourselves with the security framework */
- if (register_security(&tomoyo_security_ops))
+ if (register_security(&tomoyo_security_ops) ||
+ init_srcu_struct(&tomoyo_ss))
panic("Failure registering TOMOYO Linux");
printk(KERN_INFO "TOMOYO Linux initialized\n");
cred->security = &tomoyo_kernel_domain;
^ permalink raw reply related [flat|nested] 50+ messages in thread
* Re: [PATCH] TOMOYO: Use RCU primitives for list operation
2009-12-11 13:53 [PATCH] TOMOYO: Use RCU primitives for list operation Tetsuo Handa
@ 2009-12-14 16:39 ` Serge E. Hallyn
2009-12-15 1:39 ` Tetsuo Handa
0 siblings, 1 reply; 50+ messages in thread
From: Serge E. Hallyn @ 2009-12-14 16:39 UTC (permalink / raw)
To: Tetsuo Handa; +Cc: linux-security-module, linux-kernel
Quoting Tetsuo Handa (penguin-kernel@I-love.SAKURA.ne.jp):
> Can somebody please review?
Thanks, Tetsuo!
Just out of curiosity (and bc I'm personally much more familiar with plain old
rcu), I assume you have a list of places where sleeping under rcu is necessary
or greatly simplifies/cleans up the code?
Acked-by: Serge Hallyn <serue@us.ibm.com>
-serge
> ----------
> [PATCH] TOMOYO: Use RCU primitives for list operation
>
> Currently, TOMOYO is using down_read() only for protecting ->prev member from
> writers. If we convert list operation using RCU primitives so that readers don't
> look at ->prev member, we can replace down_read() by srcu_read_lock().
>
> tomoyo_read_lock()/tomoyo_read_unlock() (these are wrappers for
> srcu_read_lock()/srcu_read_unlock()) protects the data against the garbage
> collector (which will be added in the future). I call tomoyo_read_lock() when
> /sys/kernel/security/tomoyo/ interface is open()ed and call
> tomoyo_read_unlock() when /sys/kernel/security/tomoyo/ interface is close()d
> rather than calling tomoyo_read_lock()/tomoyo_read_unlock() upon individual
> read()/write() requests. In this way, the pointers saved in
> "struct tomoyo_io_buffer" are guaranteed to be valid. Please ignore
>
> warning: context imbalance in 'tomoyo_open_control' - wrong count at exit
> warning: context imbalance in 'tomoyo_close_control' - unexpected unlock
>
> messages when built with "C=1" option.
>
> Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
> ---
> common.c | 90 +++++++++++++++++++++++++++----------------------
> common.h | 28 +++++++++++----
> domain.c | 63 ++++++++++++++++++++--------------
> file.c | 110 ++++++++++++++++++++++++++++++++++++++++++-------------------
> realpath.c | 8 ++--
> tomoyo.c | 20 +++++++++--
> 6 files changed, 207 insertions(+), 112 deletions(-)
>
> diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c
> index 6c60616..f01b936 100644
> --- a/security/tomoyo/common.c
> +++ b/security/tomoyo/common.c
> @@ -365,10 +365,9 @@ bool tomoyo_is_domain_def(const unsigned char *buffer)
> *
> * @domainname: The domainname to find.
> *
> - * Caller must call down_read(&tomoyo_domain_list_lock); or
> - * down_write(&tomoyo_domain_list_lock); .
> - *
> * Returns pointer to "struct tomoyo_domain_info" if found, NULL otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname)
> {
> @@ -377,7 +376,7 @@ struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname)
>
> name.name = domainname;
> tomoyo_fill_path_info(&name);
> - list_for_each_entry(domain, &tomoyo_domain_list, list) {
> + list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
> if (!domain->is_deleted &&
> !tomoyo_pathcmp(&name, domain->domainname))
> return domain;
> @@ -829,6 +828,8 @@ bool tomoyo_verbose_mode(const struct tomoyo_domain_info *domain)
> * @domain: Pointer to "struct tomoyo_domain_info".
> *
> * Returns true if the domain is not exceeded quota, false otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> bool tomoyo_domain_quota_is_ok(struct tomoyo_domain_info * const domain)
> {
> @@ -837,8 +838,7 @@ bool tomoyo_domain_quota_is_ok(struct tomoyo_domain_info * const domain)
>
> if (!domain)
> return true;
> - down_read(&tomoyo_domain_acl_info_list_lock);
> - list_for_each_entry(ptr, &domain->acl_info_list, list) {
> + list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
> if (ptr->type & TOMOYO_ACL_DELETED)
> continue;
> switch (tomoyo_acl_type2(ptr)) {
> @@ -866,7 +866,6 @@ bool tomoyo_domain_quota_is_ok(struct tomoyo_domain_info * const domain)
> break;
> }
> }
> - up_read(&tomoyo_domain_acl_info_list_lock);
> if (count < tomoyo_check_flags(domain, TOMOYO_MAX_ACCEPT_ENTRY))
> return true;
> if (!domain->quota_warned) {
> @@ -1096,6 +1095,8 @@ static DECLARE_RWSEM(tomoyo_policy_manager_list_lock);
> * @is_delete: True if it is a delete request.
> *
> * Returns 0 on success, negative value otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_update_manager_entry(const char *manager,
> const bool is_delete)
> @@ -1118,7 +1119,7 @@ static int tomoyo_update_manager_entry(const char *manager,
> if (!saved_manager)
> return -ENOMEM;
> down_write(&tomoyo_policy_manager_list_lock);
> - list_for_each_entry(ptr, &tomoyo_policy_manager_list, list) {
> + list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, list) {
> if (ptr->manager != saved_manager)
> continue;
> ptr->is_deleted = is_delete;
> @@ -1134,7 +1135,7 @@ static int tomoyo_update_manager_entry(const char *manager,
> goto out;
> new_entry->manager = saved_manager;
> new_entry->is_domain = is_domain;
> - list_add_tail(&new_entry->list, &tomoyo_policy_manager_list);
> + list_add_tail_rcu(&new_entry->list, &tomoyo_policy_manager_list);
> error = 0;
> out:
> up_write(&tomoyo_policy_manager_list_lock);
> @@ -1147,6 +1148,8 @@ static int tomoyo_update_manager_entry(const char *manager,
> * @head: Pointer to "struct tomoyo_io_buffer".
> *
> * Returns 0 on success, negative value otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_write_manager_policy(struct tomoyo_io_buffer *head)
> {
> @@ -1166,6 +1169,8 @@ static int tomoyo_write_manager_policy(struct tomoyo_io_buffer *head)
> * @head: Pointer to "struct tomoyo_io_buffer".
> *
> * Returns 0.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_read_manager_policy(struct tomoyo_io_buffer *head)
> {
> @@ -1174,7 +1179,6 @@ static int tomoyo_read_manager_policy(struct tomoyo_io_buffer *head)
>
> if (head->read_eof)
> return 0;
> - down_read(&tomoyo_policy_manager_list_lock);
> list_for_each_cookie(pos, head->read_var2,
> &tomoyo_policy_manager_list) {
> struct tomoyo_policy_manager_entry *ptr;
> @@ -1186,7 +1190,6 @@ static int tomoyo_read_manager_policy(struct tomoyo_io_buffer *head)
> if (!done)
> break;
> }
> - up_read(&tomoyo_policy_manager_list_lock);
> head->read_eof = done;
> return 0;
> }
> @@ -1196,6 +1199,8 @@ static int tomoyo_read_manager_policy(struct tomoyo_io_buffer *head)
> *
> * Returns true if the current process is permitted to modify policy
> * via /sys/kernel/security/tomoyo/ interface.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static bool tomoyo_is_policy_manager(void)
> {
> @@ -1209,29 +1214,25 @@ static bool tomoyo_is_policy_manager(void)
> return true;
> if (!tomoyo_manage_by_non_root && (task->cred->uid || task->cred->euid))
> return false;
> - down_read(&tomoyo_policy_manager_list_lock);
> - list_for_each_entry(ptr, &tomoyo_policy_manager_list, list) {
> + list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, list) {
> if (!ptr->is_deleted && ptr->is_domain
> && !tomoyo_pathcmp(domainname, ptr->manager)) {
> found = true;
> break;
> }
> }
> - up_read(&tomoyo_policy_manager_list_lock);
> if (found)
> return true;
> exe = tomoyo_get_exe();
> if (!exe)
> return false;
> - down_read(&tomoyo_policy_manager_list_lock);
> - list_for_each_entry(ptr, &tomoyo_policy_manager_list, list) {
> + list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, list) {
> if (!ptr->is_deleted && !ptr->is_domain
> && !strcmp(exe, ptr->manager->name)) {
> found = true;
> break;
> }
> }
> - up_read(&tomoyo_policy_manager_list_lock);
> if (!found) { /* Reduce error messages. */
> static pid_t last_pid;
> const pid_t pid = current->pid;
> @@ -1252,6 +1253,8 @@ static bool tomoyo_is_policy_manager(void)
> * @data: String to parse.
> *
> * Returns true on success, false otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head,
> const char *data)
> @@ -1267,11 +1270,8 @@ static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head,
> domain = tomoyo_real_domain(p);
> read_unlock(&tasklist_lock);
> } else if (!strncmp(data, "domain=", 7)) {
> - if (tomoyo_is_domain_def(data + 7)) {
> - down_read(&tomoyo_domain_list_lock);
> + if (tomoyo_is_domain_def(data + 7))
> domain = tomoyo_find_domain(data + 7);
> - up_read(&tomoyo_domain_list_lock);
> - }
> } else
> return false;
> head->write_var1 = domain;
> @@ -1285,13 +1285,11 @@ static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head,
> if (domain) {
> struct tomoyo_domain_info *d;
> head->read_var1 = NULL;
> - down_read(&tomoyo_domain_list_lock);
> - list_for_each_entry(d, &tomoyo_domain_list, list) {
> + list_for_each_entry_rcu(d, &tomoyo_domain_list, list) {
> if (d == domain)
> break;
> head->read_var1 = &d->list;
> }
> - up_read(&tomoyo_domain_list_lock);
> head->read_var2 = NULL;
> head->read_bit = 0;
> head->read_step = 0;
> @@ -1307,6 +1305,8 @@ static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head,
> * @domainname: The name of domain.
> *
> * Returns 0.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_delete_domain(char *domainname)
> {
> @@ -1317,7 +1317,7 @@ static int tomoyo_delete_domain(char *domainname)
> tomoyo_fill_path_info(&name);
> down_write(&tomoyo_domain_list_lock);
> /* Is there an active domain? */
> - list_for_each_entry(domain, &tomoyo_domain_list, list) {
> + list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
> /* Never delete tomoyo_kernel_domain */
> if (domain == &tomoyo_kernel_domain)
> continue;
> @@ -1337,6 +1337,8 @@ static int tomoyo_delete_domain(char *domainname)
> * @head: Pointer to "struct tomoyo_io_buffer".
> *
> * Returns 0 on success, negative value otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_write_domain_policy(struct tomoyo_io_buffer *head)
> {
> @@ -1359,11 +1361,9 @@ static int tomoyo_write_domain_policy(struct tomoyo_io_buffer *head)
> domain = NULL;
> if (is_delete)
> tomoyo_delete_domain(data);
> - else if (is_select) {
> - down_read(&tomoyo_domain_list_lock);
> + else if (is_select)
> domain = tomoyo_find_domain(data);
> - up_read(&tomoyo_domain_list_lock);
> - } else
> + else
> domain = tomoyo_find_or_assign_new_domain(data, 0);
> head->write_var1 = domain;
> return 0;
> @@ -1508,6 +1508,8 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head,
> * @head: Pointer to "struct tomoyo_io_buffer".
> *
> * Returns 0.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_read_domain_policy(struct tomoyo_io_buffer *head)
> {
> @@ -1519,7 +1521,6 @@ static int tomoyo_read_domain_policy(struct tomoyo_io_buffer *head)
> return 0;
> if (head->read_step == 0)
> head->read_step = 1;
> - down_read(&tomoyo_domain_list_lock);
> list_for_each_cookie(dpos, head->read_var1, &tomoyo_domain_list) {
> struct tomoyo_domain_info *domain;
> const char *quota_exceeded = "";
> @@ -1552,7 +1553,6 @@ acl_loop:
> if (head->read_step == 3)
> goto tail_mark;
> /* Print ACL entries in the domain. */
> - down_read(&tomoyo_domain_acl_info_list_lock);
> list_for_each_cookie(apos, head->read_var2,
> &domain->acl_info_list) {
> struct tomoyo_acl_info *ptr
> @@ -1562,7 +1562,6 @@ acl_loop:
> if (!done)
> break;
> }
> - up_read(&tomoyo_domain_acl_info_list_lock);
> if (!done)
> break;
> head->read_step = 3;
> @@ -1574,7 +1573,6 @@ tail_mark:
> if (head->read_single_domain)
> break;
> }
> - up_read(&tomoyo_domain_list_lock);
> head->read_eof = done;
> return 0;
> }
> @@ -1590,6 +1588,8 @@ tail_mark:
> *
> * ( echo "select " $domainname; echo "use_profile " $profile ) |
> * /usr/lib/ccs/loadpolicy -d
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_write_domain_profile(struct tomoyo_io_buffer *head)
> {
> @@ -1601,9 +1601,7 @@ static int tomoyo_write_domain_profile(struct tomoyo_io_buffer *head)
> if (!cp)
> return -EINVAL;
> *cp = '\0';
> - down_read(&tomoyo_domain_list_lock);
> domain = tomoyo_find_domain(cp + 1);
> - up_read(&tomoyo_domain_list_lock);
> if (strict_strtoul(data, 10, &profile))
> return -EINVAL;
> if (domain && profile < TOMOYO_MAX_PROFILES
> @@ -1625,6 +1623,8 @@ static int tomoyo_write_domain_profile(struct tomoyo_io_buffer *head)
> * awk ' { if ( domainname == "" ) { if ( $1 == "<kernel>" )
> * domainname = $0; } else if ( $1 == "use_profile" ) {
> * print $2 " " domainname; domainname = ""; } } ; '
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_read_domain_profile(struct tomoyo_io_buffer *head)
> {
> @@ -1633,7 +1633,6 @@ static int tomoyo_read_domain_profile(struct tomoyo_io_buffer *head)
>
> if (head->read_eof)
> return 0;
> - down_read(&tomoyo_domain_list_lock);
> list_for_each_cookie(pos, head->read_var1, &tomoyo_domain_list) {
> struct tomoyo_domain_info *domain;
> domain = list_entry(pos, struct tomoyo_domain_info, list);
> @@ -1644,7 +1643,6 @@ static int tomoyo_read_domain_profile(struct tomoyo_io_buffer *head)
> if (!done)
> break;
> }
> - up_read(&tomoyo_domain_list_lock);
> head->read_eof = done;
> return 0;
> }
> @@ -1701,6 +1699,8 @@ static int tomoyo_read_pid(struct tomoyo_io_buffer *head)
> * @head: Pointer to "struct tomoyo_io_buffer".
> *
> * Returns 0 on success, negative value otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_write_exception_policy(struct tomoyo_io_buffer *head)
> {
> @@ -1735,6 +1735,8 @@ static int tomoyo_write_exception_policy(struct tomoyo_io_buffer *head)
> * @head: Pointer to "struct tomoyo_io_buffer".
> *
> * Returns 0 on success, -EINVAL otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_read_exception_policy(struct tomoyo_io_buffer *head)
> {
> @@ -1864,15 +1866,13 @@ void tomoyo_load_policy(const char *filename)
> tomoyo_policy_loaded = true;
> { /* Check all profiles currently assigned to domains are defined. */
> struct tomoyo_domain_info *domain;
> - down_read(&tomoyo_domain_list_lock);
> - list_for_each_entry(domain, &tomoyo_domain_list, list) {
> + list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
> const u8 profile = domain->profile;
> if (tomoyo_profile_ptr[profile])
> continue;
> panic("Profile %u (used by '%s') not defined.\n",
> profile, domain->domainname->name);
> }
> - up_read(&tomoyo_domain_list_lock);
> }
> }
>
> @@ -1920,6 +1920,8 @@ static int tomoyo_read_self_domain(struct tomoyo_io_buffer *head)
> * @file: Pointer to "struct file".
> *
> * Associates policy handler and returns 0 on success, -ENOMEM otherwise.
> + *
> + * Caller acquires tomoyo_read_lock().
> */
> static int tomoyo_open_control(const u8 type, struct file *file)
> {
> @@ -2005,6 +2007,7 @@ static int tomoyo_open_control(const u8 type, struct file *file)
> return -ENOMEM;
> }
> }
> + head->reader_idx = tomoyo_read_lock();
> file->private_data = head;
> /*
> * Call the handler now if the file is
> @@ -2026,6 +2029,8 @@ static int tomoyo_open_control(const u8 type, struct file *file)
> * @buffer_len: Size of @buffer.
> *
> * Returns bytes read on success, negative value otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_read_control(struct file *file, char __user *buffer,
> const int buffer_len)
> @@ -2069,6 +2074,8 @@ static int tomoyo_read_control(struct file *file, char __user *buffer,
> * @buffer_len: Size of @buffer.
> *
> * Returns @buffer_len on success, negative value otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_write_control(struct file *file, const char __user *buffer,
> const int buffer_len)
> @@ -2119,11 +2126,14 @@ static int tomoyo_write_control(struct file *file, const char __user *buffer,
> * @file: Pointer to "struct file".
> *
> * Releases memory and returns 0.
> + *
> + * Caller looses tomoyo_read_lock().
> */
> static int tomoyo_close_control(struct file *file)
> {
> struct tomoyo_io_buffer *head = file->private_data;
>
> + tomoyo_read_unlock(head->reader_idx);
> /* Release memory used for policy I/O. */
> tomoyo_free(head->read_buf);
> head->read_buf = NULL;
> diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h
> index bd10f9f..c6f1392 100644
> --- a/security/tomoyo/common.h
> +++ b/security/tomoyo/common.h
> @@ -269,6 +269,8 @@ struct tomoyo_io_buffer {
> int (*write) (struct tomoyo_io_buffer *);
> /* Exclusive lock for this structure. */
> struct mutex io_sem;
> + /* Index returned by tomoyo_read_lock(). */
> + int reader_idx;
> /* The position currently reading from. */
> struct list_head *read_var1;
> /* Extra variables for reading. */
> @@ -446,16 +448,28 @@ extern struct tomoyo_domain_info tomoyo_kernel_domain;
> * @cookie: the &struct list_head to use as a cookie.
> * @head: the head for your list.
> *
> - * Same with list_for_each() except that this primitive uses @cookie
> + * Same with list_for_each_rcu() except that this primitive uses @cookie
> * so that we can continue iteration.
> * @cookie must be NULL when iteration starts, and @cookie will become
> * NULL when iteration finishes.
> */
> -#define list_for_each_cookie(pos, cookie, head) \
> - for (({ if (!cookie) \
> - cookie = head; }), \
> - pos = (cookie)->next; \
> - prefetch(pos->next), pos != (head) || ((cookie) = NULL); \
> - (cookie) = pos, pos = pos->next)
> +#define list_for_each_cookie(pos, cookie, head) \
> + for (({ if (!cookie) \
> + cookie = head; }), \
> + pos = rcu_dereference((cookie)->next); \
> + prefetch(pos->next), pos != (head) || ((cookie) = NULL); \
> + (cookie) = pos, pos = rcu_dereference(pos->next))
> +
> +extern struct srcu_struct tomoyo_ss;
> +
> +static inline int tomoyo_read_lock(void)
> +{
> + return srcu_read_lock(&tomoyo_ss);
> +}
> +
> +static inline void tomoyo_read_unlock(int idx)
> +{
> + srcu_read_unlock(&tomoyo_ss, idx);
> +}
>
> #endif /* !defined(_SECURITY_TOMOYO_COMMON_H) */
> diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c
> index fcf52ac..2fd1901 100644
> --- a/security/tomoyo/domain.c
> +++ b/security/tomoyo/domain.c
> @@ -217,6 +217,8 @@ static DECLARE_RWSEM(tomoyo_domain_initializer_list_lock);
> * @is_delete: True if it is a delete request.
> *
> * Returns 0 on success, negative value otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_update_domain_initializer_entry(const char *domainname,
> const char *program,
> @@ -246,7 +248,7 @@ static int tomoyo_update_domain_initializer_entry(const char *domainname,
> if (!saved_program)
> return -ENOMEM;
> down_write(&tomoyo_domain_initializer_list_lock);
> - list_for_each_entry(ptr, &tomoyo_domain_initializer_list, list) {
> + list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, list) {
> if (ptr->is_not != is_not ||
> ptr->domainname != saved_domainname ||
> ptr->program != saved_program)
> @@ -266,7 +268,7 @@ static int tomoyo_update_domain_initializer_entry(const char *domainname,
> new_entry->program = saved_program;
> new_entry->is_not = is_not;
> new_entry->is_last_name = is_last_name;
> - list_add_tail(&new_entry->list, &tomoyo_domain_initializer_list);
> + list_add_tail_rcu(&new_entry->list, &tomoyo_domain_initializer_list);
> error = 0;
> out:
> up_write(&tomoyo_domain_initializer_list_lock);
> @@ -279,13 +281,14 @@ static int tomoyo_update_domain_initializer_entry(const char *domainname,
> * @head: Pointer to "struct tomoyo_io_buffer".
> *
> * Returns true on success, false otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head)
> {
> struct list_head *pos;
> bool done = true;
>
> - down_read(&tomoyo_domain_initializer_list_lock);
> list_for_each_cookie(pos, head->read_var2,
> &tomoyo_domain_initializer_list) {
> const char *no;
> @@ -308,7 +311,6 @@ bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head)
> if (!done)
> break;
> }
> - up_read(&tomoyo_domain_initializer_list_lock);
> return done;
> }
>
> @@ -320,6 +322,8 @@ bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head)
> * @is_delete: True if it is a delete request.
> *
> * Returns 0 on success, negative value otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> int tomoyo_write_domain_initializer_policy(char *data, const bool is_not,
> const bool is_delete)
> @@ -345,6 +349,8 @@ int tomoyo_write_domain_initializer_policy(char *data, const bool is_not,
> *
> * Returns true if executing @program reinitializes domain transition,
> * false otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static bool tomoyo_is_domain_initializer(const struct tomoyo_path_info *
> domainname,
> @@ -355,8 +361,7 @@ static bool tomoyo_is_domain_initializer(const struct tomoyo_path_info *
> struct tomoyo_domain_initializer_entry *ptr;
> bool flag = false;
>
> - down_read(&tomoyo_domain_initializer_list_lock);
> - list_for_each_entry(ptr, &tomoyo_domain_initializer_list, list) {
> + list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, list) {
> if (ptr->is_deleted)
> continue;
> if (ptr->domainname) {
> @@ -376,7 +381,6 @@ static bool tomoyo_is_domain_initializer(const struct tomoyo_path_info *
> }
> flag = true;
> }
> - up_read(&tomoyo_domain_initializer_list_lock);
> return flag;
> }
>
> @@ -430,6 +434,8 @@ static DECLARE_RWSEM(tomoyo_domain_keeper_list_lock);
> * @is_delete: True if it is a delete request.
> *
> * Returns 0 on success, negative value otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_update_domain_keeper_entry(const char *domainname,
> const char *program,
> @@ -459,7 +465,7 @@ static int tomoyo_update_domain_keeper_entry(const char *domainname,
> if (!saved_domainname)
> return -ENOMEM;
> down_write(&tomoyo_domain_keeper_list_lock);
> - list_for_each_entry(ptr, &tomoyo_domain_keeper_list, list) {
> + list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, list) {
> if (ptr->is_not != is_not ||
> ptr->domainname != saved_domainname ||
> ptr->program != saved_program)
> @@ -479,7 +485,7 @@ static int tomoyo_update_domain_keeper_entry(const char *domainname,
> new_entry->program = saved_program;
> new_entry->is_not = is_not;
> new_entry->is_last_name = is_last_name;
> - list_add_tail(&new_entry->list, &tomoyo_domain_keeper_list);
> + list_add_tail_rcu(&new_entry->list, &tomoyo_domain_keeper_list);
> error = 0;
> out:
> up_write(&tomoyo_domain_keeper_list_lock);
> @@ -493,6 +499,7 @@ static int tomoyo_update_domain_keeper_entry(const char *domainname,
> * @is_not: True if it is "no_keep_domain" entry.
> * @is_delete: True if it is a delete request.
> *
> + * Caller holds tomoyo_read_lock().
> */
> int tomoyo_write_domain_keeper_policy(char *data, const bool is_not,
> const bool is_delete)
> @@ -513,13 +520,14 @@ int tomoyo_write_domain_keeper_policy(char *data, const bool is_not,
> * @head: Pointer to "struct tomoyo_io_buffer".
> *
> * Returns true on success, false otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head)
> {
> struct list_head *pos;
> bool done = true;
>
> - down_read(&tomoyo_domain_keeper_list_lock);
> list_for_each_cookie(pos, head->read_var2,
> &tomoyo_domain_keeper_list) {
> struct tomoyo_domain_keeper_entry *ptr;
> @@ -542,7 +550,6 @@ bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head)
> if (!done)
> break;
> }
> - up_read(&tomoyo_domain_keeper_list_lock);
> return done;
> }
>
> @@ -555,6 +562,8 @@ bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head)
> *
> * Returns true if executing @program supresses domain transition,
> * false otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static bool tomoyo_is_domain_keeper(const struct tomoyo_path_info *domainname,
> const struct tomoyo_path_info *program,
> @@ -563,8 +572,7 @@ static bool tomoyo_is_domain_keeper(const struct tomoyo_path_info *domainname,
> struct tomoyo_domain_keeper_entry *ptr;
> bool flag = false;
>
> - down_read(&tomoyo_domain_keeper_list_lock);
> - list_for_each_entry(ptr, &tomoyo_domain_keeper_list, list) {
> + list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, list) {
> if (ptr->is_deleted)
> continue;
> if (!ptr->is_last_name) {
> @@ -582,7 +590,6 @@ static bool tomoyo_is_domain_keeper(const struct tomoyo_path_info *domainname,
> }
> flag = true;
> }
> - up_read(&tomoyo_domain_keeper_list_lock);
> return flag;
> }
>
> @@ -627,6 +634,8 @@ static DECLARE_RWSEM(tomoyo_alias_list_lock);
> * @is_delete: True if it is a delete request.
> *
> * Returns 0 on success, negative value otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_update_alias_entry(const char *original_name,
> const char *aliased_name,
> @@ -646,7 +655,7 @@ static int tomoyo_update_alias_entry(const char *original_name,
> if (!saved_original_name || !saved_aliased_name)
> return -ENOMEM;
> down_write(&tomoyo_alias_list_lock);
> - list_for_each_entry(ptr, &tomoyo_alias_list, list) {
> + list_for_each_entry_rcu(ptr, &tomoyo_alias_list, list) {
> if (ptr->original_name != saved_original_name ||
> ptr->aliased_name != saved_aliased_name)
> continue;
> @@ -663,7 +672,7 @@ static int tomoyo_update_alias_entry(const char *original_name,
> goto out;
> new_entry->original_name = saved_original_name;
> new_entry->aliased_name = saved_aliased_name;
> - list_add_tail(&new_entry->list, &tomoyo_alias_list);
> + list_add_tail_rcu(&new_entry->list, &tomoyo_alias_list);
> error = 0;
> out:
> up_write(&tomoyo_alias_list_lock);
> @@ -676,13 +685,14 @@ static int tomoyo_update_alias_entry(const char *original_name,
> * @head: Pointer to "struct tomoyo_io_buffer".
> *
> * Returns true on success, false otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head)
> {
> struct list_head *pos;
> bool done = true;
>
> - down_read(&tomoyo_alias_list_lock);
> list_for_each_cookie(pos, head->read_var2, &tomoyo_alias_list) {
> struct tomoyo_alias_entry *ptr;
>
> @@ -695,7 +705,6 @@ bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head)
> if (!done)
> break;
> }
> - up_read(&tomoyo_alias_list_lock);
> return done;
> }
>
> @@ -706,6 +715,8 @@ bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head)
> * @is_delete: True if it is a delete request.
> *
> * Returns 0 on success, negative value otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> int tomoyo_write_alias_policy(char *data, const bool is_delete)
> {
> @@ -724,6 +735,8 @@ int tomoyo_write_alias_policy(char *data, const bool is_delete)
> * @profile: Profile number to assign if the domain was newly created.
> *
> * Returns pointer to "struct tomoyo_domain_info" on success, NULL otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
> domainname,
> @@ -742,7 +755,7 @@ struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
> if (!saved_domainname)
> goto out;
> /* Can I reuse memory of deleted domain? */
> - list_for_each_entry(domain, &tomoyo_domain_list, list) {
> + list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
> struct task_struct *p;
> struct tomoyo_acl_info *ptr;
> bool flag;
> @@ -760,7 +773,7 @@ struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
> read_unlock(&tasklist_lock);
> if (flag)
> continue;
> - list_for_each_entry(ptr, &domain->acl_info_list, list) {
> + list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
> ptr->type |= TOMOYO_ACL_DELETED;
> }
> tomoyo_set_domain_flag(domain, true, domain->flags);
> @@ -776,7 +789,7 @@ struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
> INIT_LIST_HEAD(&domain->acl_info_list);
> domain->domainname = saved_domainname;
> domain->profile = profile;
> - list_add_tail(&domain->list, &tomoyo_domain_list);
> + list_add_tail_rcu(&domain->list, &tomoyo_domain_list);
> }
> out:
> up_write(&tomoyo_domain_list_lock);
> @@ -789,6 +802,8 @@ struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
> * @bprm: Pointer to "struct linux_binprm".
> *
> * Returns 0 on success, negative value otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> int tomoyo_find_next_domain(struct linux_binprm *bprm)
> {
> @@ -849,8 +864,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
> if (tomoyo_pathcmp(&r, &s)) {
> struct tomoyo_alias_entry *ptr;
> /* Is this program allowed to be called via symbolic links? */
> - down_read(&tomoyo_alias_list_lock);
> - list_for_each_entry(ptr, &tomoyo_alias_list, list) {
> + list_for_each_entry_rcu(ptr, &tomoyo_alias_list, list) {
> if (ptr->is_deleted ||
> tomoyo_pathcmp(&r, ptr->original_name) ||
> tomoyo_pathcmp(&s, ptr->aliased_name))
> @@ -861,7 +875,6 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
> tomoyo_fill_path_info(&r);
> break;
> }
> - up_read(&tomoyo_alias_list_lock);
> }
>
> /* Check execute permission. */
> @@ -892,9 +905,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
> }
> if (domain || strlen(new_domain_name) >= TOMOYO_MAX_PATHNAME_LEN)
> goto done;
> - down_read(&tomoyo_domain_list_lock);
> domain = tomoyo_find_domain(new_domain_name);
> - up_read(&tomoyo_domain_list_lock);
> if (domain)
> goto done;
> if (is_enforce)
> diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c
> index 482f0e7..3c47286 100644
> --- a/security/tomoyo/file.c
> +++ b/security/tomoyo/file.c
> @@ -213,6 +213,8 @@ static DECLARE_RWSEM(tomoyo_globally_readable_list_lock);
> * @is_delete: True if it is a delete request.
> *
> * Returns 0 on success, negative value otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_update_globally_readable_entry(const char *filename,
> const bool is_delete)
> @@ -228,7 +230,7 @@ static int tomoyo_update_globally_readable_entry(const char *filename,
> if (!saved_filename)
> return -ENOMEM;
> down_write(&tomoyo_globally_readable_list_lock);
> - list_for_each_entry(ptr, &tomoyo_globally_readable_list, list) {
> + list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, list) {
> if (ptr->filename != saved_filename)
> continue;
> ptr->is_deleted = is_delete;
> @@ -243,7 +245,7 @@ static int tomoyo_update_globally_readable_entry(const char *filename,
> if (!new_entry)
> goto out;
> new_entry->filename = saved_filename;
> - list_add_tail(&new_entry->list, &tomoyo_globally_readable_list);
> + list_add_tail_rcu(&new_entry->list, &tomoyo_globally_readable_list);
> error = 0;
> out:
> up_write(&tomoyo_globally_readable_list_lock);
> @@ -256,21 +258,22 @@ static int tomoyo_update_globally_readable_entry(const char *filename,
> * @filename: The filename to check.
> *
> * Returns true if any domain can open @filename for reading, false otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static bool tomoyo_is_globally_readable_file(const struct tomoyo_path_info *
> filename)
> {
> struct tomoyo_globally_readable_file_entry *ptr;
> bool found = false;
> - down_read(&tomoyo_globally_readable_list_lock);
> - list_for_each_entry(ptr, &tomoyo_globally_readable_list, list) {
> +
> + list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, list) {
> if (!ptr->is_deleted &&
> tomoyo_path_matches_pattern(filename, ptr->filename)) {
> found = true;
> break;
> }
> }
> - up_read(&tomoyo_globally_readable_list_lock);
> return found;
> }
>
> @@ -281,6 +284,8 @@ static bool tomoyo_is_globally_readable_file(const struct tomoyo_path_info *
> * @is_delete: True if it is a delete request.
> *
> * Returns 0 on success, negative value otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> int tomoyo_write_globally_readable_policy(char *data, const bool is_delete)
> {
> @@ -293,13 +298,14 @@ int tomoyo_write_globally_readable_policy(char *data, const bool is_delete)
> * @head: Pointer to "struct tomoyo_io_buffer".
> *
> * Returns true on success, false otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head)
> {
> struct list_head *pos;
> bool done = true;
>
> - down_read(&tomoyo_globally_readable_list_lock);
> list_for_each_cookie(pos, head->read_var2,
> &tomoyo_globally_readable_list) {
> struct tomoyo_globally_readable_file_entry *ptr;
> @@ -313,7 +319,6 @@ bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head)
> if (!done)
> break;
> }
> - up_read(&tomoyo_globally_readable_list_lock);
> return done;
> }
>
> @@ -356,6 +361,8 @@ static DECLARE_RWSEM(tomoyo_pattern_list_lock);
> * @is_delete: True if it is a delete request.
> *
> * Returns 0 on success, negative value otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_update_file_pattern_entry(const char *pattern,
> const bool is_delete)
> @@ -371,7 +378,7 @@ static int tomoyo_update_file_pattern_entry(const char *pattern,
> if (!saved_pattern)
> return -ENOMEM;
> down_write(&tomoyo_pattern_list_lock);
> - list_for_each_entry(ptr, &tomoyo_pattern_list, list) {
> + list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) {
> if (saved_pattern != ptr->pattern)
> continue;
> ptr->is_deleted = is_delete;
> @@ -386,7 +393,7 @@ static int tomoyo_update_file_pattern_entry(const char *pattern,
> if (!new_entry)
> goto out;
> new_entry->pattern = saved_pattern;
> - list_add_tail(&new_entry->list, &tomoyo_pattern_list);
> + list_add_tail_rcu(&new_entry->list, &tomoyo_pattern_list);
> error = 0;
> out:
> up_write(&tomoyo_pattern_list_lock);
> @@ -399,6 +406,8 @@ static int tomoyo_update_file_pattern_entry(const char *pattern,
> * @filename: The filename to find patterned pathname.
> *
> * Returns pointer to pathname pattern if matched, @filename otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static const struct tomoyo_path_info *
> tomoyo_get_file_pattern(const struct tomoyo_path_info *filename)
> @@ -406,8 +415,7 @@ tomoyo_get_file_pattern(const struct tomoyo_path_info *filename)
> struct tomoyo_pattern_entry *ptr;
> const struct tomoyo_path_info *pattern = NULL;
>
> - down_read(&tomoyo_pattern_list_lock);
> - list_for_each_entry(ptr, &tomoyo_pattern_list, list) {
> + list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) {
> if (ptr->is_deleted)
> continue;
> if (!tomoyo_path_matches_pattern(filename, ptr->pattern))
> @@ -420,7 +428,6 @@ tomoyo_get_file_pattern(const struct tomoyo_path_info *filename)
> break;
> }
> }
> - up_read(&tomoyo_pattern_list_lock);
> if (pattern)
> filename = pattern;
> return filename;
> @@ -433,6 +440,8 @@ tomoyo_get_file_pattern(const struct tomoyo_path_info *filename)
> * @is_delete: True if it is a delete request.
> *
> * Returns 0 on success, negative value otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> int tomoyo_write_pattern_policy(char *data, const bool is_delete)
> {
> @@ -445,13 +454,14 @@ int tomoyo_write_pattern_policy(char *data, const bool is_delete)
> * @head: Pointer to "struct tomoyo_io_buffer".
> *
> * Returns true on success, false otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head)
> {
> struct list_head *pos;
> bool done = true;
>
> - down_read(&tomoyo_pattern_list_lock);
> list_for_each_cookie(pos, head->read_var2, &tomoyo_pattern_list) {
> struct tomoyo_pattern_entry *ptr;
> ptr = list_entry(pos, struct tomoyo_pattern_entry, list);
> @@ -462,7 +472,6 @@ bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head)
> if (!done)
> break;
> }
> - up_read(&tomoyo_pattern_list_lock);
> return done;
> }
>
> @@ -505,6 +514,8 @@ static DECLARE_RWSEM(tomoyo_no_rewrite_list_lock);
> * @is_delete: True if it is a delete request.
> *
> * Returns 0 on success, negative value otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_update_no_rewrite_entry(const char *pattern,
> const bool is_delete)
> @@ -519,7 +530,7 @@ static int tomoyo_update_no_rewrite_entry(const char *pattern,
> if (!saved_pattern)
> return -ENOMEM;
> down_write(&tomoyo_no_rewrite_list_lock);
> - list_for_each_entry(ptr, &tomoyo_no_rewrite_list, list) {
> + list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, list) {
> if (ptr->pattern != saved_pattern)
> continue;
> ptr->is_deleted = is_delete;
> @@ -534,7 +545,7 @@ static int tomoyo_update_no_rewrite_entry(const char *pattern,
> if (!new_entry)
> goto out;
> new_entry->pattern = saved_pattern;
> - list_add_tail(&new_entry->list, &tomoyo_no_rewrite_list);
> + list_add_tail_rcu(&new_entry->list, &tomoyo_no_rewrite_list);
> error = 0;
> out:
> up_write(&tomoyo_no_rewrite_list_lock);
> @@ -548,14 +559,15 @@ static int tomoyo_update_no_rewrite_entry(const char *pattern,
> *
> * Returns true if @filename is specified by "deny_rewrite" directive,
> * false otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static bool tomoyo_is_no_rewrite_file(const struct tomoyo_path_info *filename)
> {
> struct tomoyo_no_rewrite_entry *ptr;
> bool found = false;
>
> - down_read(&tomoyo_no_rewrite_list_lock);
> - list_for_each_entry(ptr, &tomoyo_no_rewrite_list, list) {
> + list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, list) {
> if (ptr->is_deleted)
> continue;
> if (!tomoyo_path_matches_pattern(filename, ptr->pattern))
> @@ -563,7 +575,6 @@ static bool tomoyo_is_no_rewrite_file(const struct tomoyo_path_info *filename)
> found = true;
> break;
> }
> - up_read(&tomoyo_no_rewrite_list_lock);
> return found;
> }
>
> @@ -574,6 +585,8 @@ static bool tomoyo_is_no_rewrite_file(const struct tomoyo_path_info *filename)
> * @is_delete: True if it is a delete request.
> *
> * Returns 0 on success, negative value otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> int tomoyo_write_no_rewrite_policy(char *data, const bool is_delete)
> {
> @@ -586,13 +599,14 @@ int tomoyo_write_no_rewrite_policy(char *data, const bool is_delete)
> * @head: Pointer to "struct tomoyo_io_buffer".
> *
> * Returns true on success, false otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head)
> {
> struct list_head *pos;
> bool done = true;
>
> - down_read(&tomoyo_no_rewrite_list_lock);
> list_for_each_cookie(pos, head->read_var2, &tomoyo_no_rewrite_list) {
> struct tomoyo_no_rewrite_entry *ptr;
> ptr = list_entry(pos, struct tomoyo_no_rewrite_entry, list);
> @@ -603,7 +617,6 @@ bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head)
> if (!done)
> break;
> }
> - up_read(&tomoyo_no_rewrite_list_lock);
> return done;
> }
>
> @@ -621,6 +634,8 @@ bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head)
> * Current policy syntax uses "allow_read/write" instead of "6",
> * "allow_read" instead of "4", "allow_write" instead of "2",
> * "allow_execute" instead of "1".
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_update_file_acl(const char *filename, u8 perm,
> struct tomoyo_domain_info * const domain,
> @@ -658,6 +673,8 @@ static int tomoyo_update_file_acl(const char *filename, u8 perm,
> * @may_use_pattern: True if patterned ACL is permitted.
> *
> * Returns 0 on success, -EPERM otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_check_single_path_acl2(const struct tomoyo_domain_info *
> domain,
> @@ -669,8 +686,7 @@ static int tomoyo_check_single_path_acl2(const struct tomoyo_domain_info *
> struct tomoyo_acl_info *ptr;
> int error = -EPERM;
>
> - down_read(&tomoyo_domain_acl_info_list_lock);
> - list_for_each_entry(ptr, &domain->acl_info_list, list) {
> + list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
> struct tomoyo_single_path_acl_record *acl;
> if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL)
> continue;
> @@ -693,7 +709,6 @@ static int tomoyo_check_single_path_acl2(const struct tomoyo_domain_info *
> error = 0;
> break;
> }
> - up_read(&tomoyo_domain_acl_info_list_lock);
> return error;
> }
>
> @@ -705,6 +720,8 @@ static int tomoyo_check_single_path_acl2(const struct tomoyo_domain_info *
> * @operation: Mode ("read" or "write" or "read/write" or "execute").
> *
> * Returns 0 on success, -EPERM otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_check_file_acl(const struct tomoyo_domain_info *domain,
> const struct tomoyo_path_info *filename,
> @@ -738,6 +755,8 @@ static int tomoyo_check_file_acl(const struct tomoyo_domain_info *domain,
> * @mode: Access control mode.
> *
> * Returns 0 on success, negative value otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_check_file_perm2(struct tomoyo_domain_info * const domain,
> const struct tomoyo_path_info *filename,
> @@ -791,6 +810,8 @@ static int tomoyo_check_file_perm2(struct tomoyo_domain_info * const domain,
> * @is_delete: True if it is a delete request.
> *
> * Returns 0 on success, negative value otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> int tomoyo_write_file_policy(char *data, struct tomoyo_domain_info *domain,
> const bool is_delete)
> @@ -838,6 +859,8 @@ int tomoyo_write_file_policy(char *data, struct tomoyo_domain_info *domain,
> * @is_delete: True if it is a delete request.
> *
> * Returns 0 on success, negative value otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_update_single_path_acl(const u8 type, const char *filename,
> struct tomoyo_domain_info *
> @@ -861,7 +884,7 @@ static int tomoyo_update_single_path_acl(const u8 type, const char *filename,
> down_write(&tomoyo_domain_acl_info_list_lock);
> if (is_delete)
> goto delete;
> - list_for_each_entry(ptr, &domain->acl_info_list, list) {
> + list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
> if (tomoyo_acl_type1(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL)
> continue;
> acl = container_of(ptr, struct tomoyo_single_path_acl_record,
> @@ -894,12 +917,12 @@ static int tomoyo_update_single_path_acl(const u8 type, const char *filename,
> if (perm == (1 << TOMOYO_TYPE_READ_WRITE_ACL))
> acl->perm |= rw_mask;
> acl->filename = saved_filename;
> - list_add_tail(&acl->head.list, &domain->acl_info_list);
> + list_add_tail_rcu(&acl->head.list, &domain->acl_info_list);
> error = 0;
> goto out;
> delete:
> error = -ENOENT;
> - list_for_each_entry(ptr, &domain->acl_info_list, list) {
> + list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
> if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL)
> continue;
> acl = container_of(ptr, struct tomoyo_single_path_acl_record,
> @@ -934,6 +957,8 @@ static int tomoyo_update_single_path_acl(const u8 type, const char *filename,
> * @is_delete: True if it is a delete request.
> *
> * Returns 0 on success, negative value otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_update_double_path_acl(const u8 type, const char *filename1,
> const char *filename2,
> @@ -959,7 +984,7 @@ static int tomoyo_update_double_path_acl(const u8 type, const char *filename1,
> down_write(&tomoyo_domain_acl_info_list_lock);
> if (is_delete)
> goto delete;
> - list_for_each_entry(ptr, &domain->acl_info_list, list) {
> + list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
> if (tomoyo_acl_type1(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL)
> continue;
> acl = container_of(ptr, struct tomoyo_double_path_acl_record,
> @@ -982,12 +1007,12 @@ static int tomoyo_update_double_path_acl(const u8 type, const char *filename1,
> acl->perm = perm;
> acl->filename1 = saved_filename1;
> acl->filename2 = saved_filename2;
> - list_add_tail(&acl->head.list, &domain->acl_info_list);
> + list_add_tail_rcu(&acl->head.list, &domain->acl_info_list);
> error = 0;
> goto out;
> delete:
> error = -ENOENT;
> - list_for_each_entry(ptr, &domain->acl_info_list, list) {
> + list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
> if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL)
> continue;
> acl = container_of(ptr, struct tomoyo_double_path_acl_record,
> @@ -1014,6 +1039,8 @@ static int tomoyo_update_double_path_acl(const u8 type, const char *filename1,
> * @filename: Filename to check.
> *
> * Returns 0 on success, negative value otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_check_single_path_acl(struct tomoyo_domain_info *domain,
> const u8 type,
> @@ -1033,6 +1060,8 @@ static int tomoyo_check_single_path_acl(struct tomoyo_domain_info *domain,
> * @filename2: Second filename to check.
> *
> * Returns 0 on success, -EPERM otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_check_double_path_acl(const struct tomoyo_domain_info *domain,
> const u8 type,
> @@ -1047,8 +1076,7 @@ static int tomoyo_check_double_path_acl(const struct tomoyo_domain_info *domain,
>
> if (!tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE))
> return 0;
> - down_read(&tomoyo_domain_acl_info_list_lock);
> - list_for_each_entry(ptr, &domain->acl_info_list, list) {
> + list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
> struct tomoyo_double_path_acl_record *acl;
> if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL)
> continue;
> @@ -1063,7 +1091,6 @@ static int tomoyo_check_double_path_acl(const struct tomoyo_domain_info *domain,
> error = 0;
> break;
> }
> - up_read(&tomoyo_domain_acl_info_list_lock);
> return error;
> }
>
> @@ -1076,6 +1103,8 @@ static int tomoyo_check_double_path_acl(const struct tomoyo_domain_info *domain,
> * @mode: Access control mode.
> *
> * Returns 0 on success, negative value otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_check_single_path_permission2(struct tomoyo_domain_info *
> const domain, u8 operation,
> @@ -1124,6 +1153,8 @@ static int tomoyo_check_single_path_permission2(struct tomoyo_domain_info *
> * @filename: Check permission for "execute".
> *
> * Returns 0 on success, negativevalue otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> int tomoyo_check_exec_perm(struct tomoyo_domain_info *domain,
> const struct tomoyo_path_info *filename)
> @@ -1152,6 +1183,7 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
> struct tomoyo_path_info *buf;
> const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
> const bool is_enforce = (mode == 3);
> + int idx;
>
> if (!mode || !path->mnt)
> return 0;
> @@ -1163,6 +1195,7 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
> * don't call me.
> */
> return 0;
> + idx = tomoyo_read_lock();
> buf = tomoyo_get_path(path);
> if (!buf)
> goto out;
> @@ -1188,6 +1221,7 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
> buf, mode);
> out:
> tomoyo_free(buf);
> + tomoyo_read_unlock(idx);
> if (!is_enforce)
> error = 0;
> return error;
> @@ -1209,9 +1243,11 @@ int tomoyo_check_1path_perm(struct tomoyo_domain_info *domain,
> struct tomoyo_path_info *buf;
> const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
> const bool is_enforce = (mode == 3);
> + int idx;
>
> if (!mode || !path->mnt)
> return 0;
> + idx = tomoyo_read_lock();
> buf = tomoyo_get_path(path);
> if (!buf)
> goto out;
> @@ -1231,6 +1267,7 @@ int tomoyo_check_1path_perm(struct tomoyo_domain_info *domain,
> mode);
> out:
> tomoyo_free(buf);
> + tomoyo_read_unlock(idx);
> if (!is_enforce)
> error = 0;
> return error;
> @@ -1251,9 +1288,12 @@ int tomoyo_check_rewrite_permission(struct tomoyo_domain_info *domain,
> const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
> const bool is_enforce = (mode == 3);
> struct tomoyo_path_info *buf;
> + int idx;
>
> if (!mode || !filp->f_path.mnt)
> return 0;
> +
> + idx = tomoyo_read_lock();
> buf = tomoyo_get_path(&filp->f_path);
> if (!buf)
> goto out;
> @@ -1266,6 +1306,7 @@ int tomoyo_check_rewrite_permission(struct tomoyo_domain_info *domain,
> buf, mode);
> out:
> tomoyo_free(buf);
> + tomoyo_read_unlock(idx);
> if (!is_enforce)
> error = 0;
> return error;
> @@ -1290,9 +1331,11 @@ int tomoyo_check_2path_perm(struct tomoyo_domain_info * const domain,
> const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
> const bool is_enforce = (mode == 3);
> const char *msg;
> + int idx;
>
> if (!mode || !path1->mnt || !path2->mnt)
> return 0;
> + idx = tomoyo_read_lock();
> buf1 = tomoyo_get_path(path1);
> buf2 = tomoyo_get_path(path2);
> if (!buf1 || !buf2)
> @@ -1331,6 +1374,7 @@ int tomoyo_check_2path_perm(struct tomoyo_domain_info * const domain,
> out:
> tomoyo_free(buf1);
> tomoyo_free(buf2);
> + tomoyo_read_unlock(idx);
> if (!is_enforce)
> error = 0;
> return error;
> diff --git a/security/tomoyo/realpath.c b/security/tomoyo/realpath.c
> index e3c7aa0..62363b3 100644
> --- a/security/tomoyo/realpath.c
> +++ b/security/tomoyo/realpath.c
> @@ -402,11 +402,13 @@ void __init tomoyo_realpath_init(void)
> INIT_LIST_HEAD(&tomoyo_name_list[i]);
> INIT_LIST_HEAD(&tomoyo_kernel_domain.acl_info_list);
> tomoyo_kernel_domain.domainname = tomoyo_save_name(TOMOYO_ROOT_NAME);
> - list_add_tail(&tomoyo_kernel_domain.list, &tomoyo_domain_list);
> - down_read(&tomoyo_domain_list_lock);
> + /*
> + * tomoyo_read_lock() is not needed because this function is
> + * called before the first "delete" request.
> + */
> + list_add_tail_rcu(&tomoyo_kernel_domain.list, &tomoyo_domain_list);
> if (tomoyo_find_domain(TOMOYO_ROOT_NAME) != &tomoyo_kernel_domain)
> panic("Can't register tomoyo_kernel_domain");
> - up_read(&tomoyo_domain_list_lock);
> }
>
> /* Memory allocated for temporary purpose. */
> diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c
> index ad9555f..714daa3 100644
> --- a/security/tomoyo/tomoyo.c
> +++ b/security/tomoyo/tomoyo.c
> @@ -76,8 +76,18 @@ static int tomoyo_bprm_check_security(struct linux_binprm *bprm)
> * Execute permission is checked against pathname passed to do_execve()
> * using current domain.
> */
> - if (!domain)
> - return tomoyo_find_next_domain(bprm);
> + if (!domain) {
> + /*
> + * We will need to protect whole execve() operation when GC
> + * starts kfree()ing "struct tomoyo_domain_info" because
> + * bprm->cred->security points to "struct tomoyo_domain_info"
> + * but "struct tomoyo_domain_info" does not have a refcounter.
> + */
> + const int idx = tomoyo_read_lock();
> + const int err = tomoyo_find_next_domain(bprm);
> + tomoyo_read_unlock(idx);
> + return err;
> + }
> /*
> * Read permission is checked against interpreters using next domain.
> * '1' is the result of open_to_namei_flags(O_RDONLY).
> @@ -278,6 +288,9 @@ static struct security_operations tomoyo_security_ops = {
> .sb_pivotroot = tomoyo_sb_pivotroot,
> };
>
> +/* Lock for GC. */
> +struct srcu_struct tomoyo_ss;
> +
> static int __init tomoyo_init(void)
> {
> struct cred *cred = (struct cred *) current_cred();
> @@ -285,7 +298,8 @@ static int __init tomoyo_init(void)
> if (!security_module_enable(&tomoyo_security_ops))
> return 0;
> /* register ourselves with the security framework */
> - if (register_security(&tomoyo_security_ops))
> + if (register_security(&tomoyo_security_ops) ||
> + init_srcu_struct(&tomoyo_ss))
> panic("Failure registering TOMOYO Linux");
> printk(KERN_INFO "TOMOYO Linux initialized\n");
> cred->security = &tomoyo_kernel_domain;
> --
> To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] TOMOYO: Use RCU primitives for list operation
2009-12-14 16:39 ` Serge E. Hallyn
@ 2009-12-15 1:39 ` Tetsuo Handa
0 siblings, 0 replies; 50+ messages in thread
From: Tetsuo Handa @ 2009-12-15 1:39 UTC (permalink / raw)
To: serue; +Cc: linux-security-module, linux-kernel
Hello.
Serge E. Hallyn wrote:
> Just out of curiosity (and bc I'm personally much more familiar with plain old
> rcu), I assume you have a list of places where sleeping under rcu is necessary
> or greatly simplifies/cleans up the code?
Yes.
TOMOYO checks operations which are permitted to sleep so that we can load
policy like call_usermodehelper("/sbin/modprobe"). I tried to use non-sleeping
RCU while implementing garbage collector support. But that attempt resulted in
messing up the code with full of atomic_inc()/atomic_dec() calls.
Thus, I decided to use sleeping RCU.
I tested garbage collector support using sleeping RCU with non-LSM version of
TOMOYO. It is working well. Thus, I started breaking non-LSM version of TOMOYO
into smaller pieces and proposing for LSM version of TOMOYO.
Thank you for reviewing. I'll send next patches.
^ permalink raw reply [flat|nested] 50+ messages in thread
end of thread, other threads:[~2009-12-15 1:39 UTC | newest]
Thread overview: 50+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-10-04 12:49 [TOMOYO #16 00/25] Starting TOMOYO 2.3 Tetsuo Handa
2009-10-04 12:49 ` [TOMOYO #16 01/25] LSM: Add security_path_chmod() and security_path_chown() Tetsuo Handa
2009-10-08 17:10 ` John Johansen
2009-10-12 1:04 ` James Morris
2009-10-13 11:34 ` [TOMOYO #16 01/25] LSM: Add security_path_chmod() andsecurity_path_chown() Tetsuo Handa
2009-10-13 11:37 ` [PATCH] TOMOYO: Add recursive directory matching operator support Tetsuo Handa
2009-10-13 11:39 ` [PATCH] TOMOYO: Use RCU primitives for list operation Tetsuo Handa
2009-10-13 11:41 ` [PATCH] TOMOYO: Bring memory allocation to outside semaphore Tetsuo Handa
2009-10-29 5:40 ` [PATCH] TOMOYO: Use RCU primitives for list operation Serge E. Hallyn
2009-12-04 12:34 ` Tetsuo Handa
2009-10-29 5:12 ` [TOMOYO #16 01/25] LSM: Add security_path_chmod() and security_path_chown() Serge E. Hallyn
2009-10-29 15:56 ` [TOMOYO #16 01/25] LSM: Add security_path_chmod() andsecurity_path_chown() Tetsuo Handa
2009-11-22 2:49 ` [PATCH] LSM: Move security_path_chmod()/security_path_chown() to after mutex_lock() Tetsuo Handa
2009-11-23 10:09 ` John Johansen
2009-11-23 21:50 ` James Morris
2009-10-04 12:49 ` [TOMOYO #16 02/25] LSM: Add security_path_chroot() Tetsuo Handa
2009-10-08 17:12 ` John Johansen
2009-10-29 5:32 ` Serge E. Hallyn
2009-10-04 12:49 ` [TOMOYO #16 03/25] LSM: Pass original mount flags to security_sb_mount() Tetsuo Handa
2009-10-08 17:22 ` John Johansen
2009-10-04 12:49 ` [TOMOYO #16 04/25] TOMOYO: Add header file Tetsuo Handa
2009-10-04 12:49 ` [TOMOYO #16 05/25] TOMOYO: Add per task_struct variables Tetsuo Handa
2009-10-04 12:49 ` [TOMOYO #16 06/25] TOMOYO: Add LSM adaptor Tetsuo Handa
2009-10-04 12:49 ` [TOMOYO #16 07/25] TOMOYO: Add path_group keyword support Tetsuo Handa
2009-10-04 12:49 ` [TOMOYO #16 08/25] TOMOYO: Add number_group " Tetsuo Handa
2009-10-04 12:49 ` [TOMOYO #16 09/25] TOMOYO: Add address_group " Tetsuo Handa
2009-10-04 12:49 ` [TOMOYO #16 10/25] TOMOYO: Add conditional ACL support Tetsuo Handa
2009-10-04 12:49 ` [TOMOYO #16 11/25] TOMOYO: Add auditing support Tetsuo Handa
2009-10-04 12:49 ` [TOMOYO #16 12/25] TOMOYO: Memory management support Tetsuo Handa
2009-10-04 12:49 ` [TOMOYO #16 13/25] TOMOYO: Add garbage collector support Tetsuo Handa
2009-10-04 12:50 ` [TOMOYO #16 14/25] TOMOYO: Add network restriction Tetsuo Handa
2009-10-04 12:50 ` [TOMOYO #16 15/25] TOMOYO: Add mount restriction Tetsuo Handa
2009-10-04 12:50 ` [TOMOYO #16 16/25] TOMOYO: Add environment variables restriction Tetsuo Handa
2009-10-04 12:50 ` [TOMOYO #16 17/25] TOMOYO: Add capability support Tetsuo Handa
2009-10-29 5:23 ` Serge E. Hallyn
2009-10-04 12:50 ` [TOMOYO #16 18/25] TOMOYO: Add utility functions Tetsuo Handa
2009-10-04 12:50 ` [TOMOYO #16 19/25] TOMOYO: Add policy I/O handler Tetsuo Handa
2009-10-04 12:50 ` [TOMOYO #16 20/25] TOMOYO: Add policy loader launcher Tetsuo Handa
2009-10-04 12:50 ` [TOMOYO #16 21/25] TOMOYO: Add securityfs interface Tetsuo Handa
2009-10-04 12:50 ` [TOMOYO #16 22/25] TOMOYO: Add pathname calculation functions Tetsuo Handa
2009-10-04 12:50 ` [TOMOYO #16 23/25] TOMOYO: Add file access restriction Tetsuo Handa
2009-10-04 12:50 ` [TOMOYO #16 24/25] TOMOYO: Add domain transition handler Tetsuo Handa
2009-10-04 12:50 ` [TOMOYO #16 25/25] TOMOYO: Update Kconfig and Makefile Tetsuo Handa
2009-10-06 9:39 ` [TOMOYO #16 00/25] Starting TOMOYO 2.3 Pavel Machek
2009-10-07 4:09 ` Tetsuo Handa
2009-10-07 7:38 ` Pavel Machek
2009-10-07 13:30 ` Tetsuo Handa
-- strict thread matches above, loose matches on Subject: below --
2009-12-11 13:53 [PATCH] TOMOYO: Use RCU primitives for list operation Tetsuo Handa
2009-12-14 16:39 ` Serge E. Hallyn
2009-12-15 1:39 ` Tetsuo Handa
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox