diff options
Diffstat (limited to 'security')
73 files changed, 13205 insertions, 4572 deletions
diff --git a/security/Kconfig b/security/Kconfig index 226b9556b25f..bd72ae623494 100644 --- a/security/Kconfig +++ b/security/Kconfig | |||
| @@ -140,6 +140,7 @@ config LSM_MMAP_MIN_ADDR | |||
| 140 | source security/selinux/Kconfig | 140 | source security/selinux/Kconfig |
| 141 | source security/smack/Kconfig | 141 | source security/smack/Kconfig |
| 142 | source security/tomoyo/Kconfig | 142 | source security/tomoyo/Kconfig |
| 143 | source security/apparmor/Kconfig | ||
| 143 | 144 | ||
| 144 | source security/integrity/ima/Kconfig | 145 | source security/integrity/ima/Kconfig |
| 145 | 146 | ||
| @@ -148,6 +149,7 @@ choice | |||
| 148 | default DEFAULT_SECURITY_SELINUX if SECURITY_SELINUX | 149 | default DEFAULT_SECURITY_SELINUX if SECURITY_SELINUX |
| 149 | default DEFAULT_SECURITY_SMACK if SECURITY_SMACK | 150 | default DEFAULT_SECURITY_SMACK if SECURITY_SMACK |
| 150 | default DEFAULT_SECURITY_TOMOYO if SECURITY_TOMOYO | 151 | default DEFAULT_SECURITY_TOMOYO if SECURITY_TOMOYO |
| 152 | default DEFAULT_SECURITY_APPARMOR if SECURITY_APPARMOR | ||
| 151 | default DEFAULT_SECURITY_DAC | 153 | default DEFAULT_SECURITY_DAC |
| 152 | 154 | ||
| 153 | help | 155 | help |
| @@ -163,6 +165,9 @@ choice | |||
| 163 | config DEFAULT_SECURITY_TOMOYO | 165 | config DEFAULT_SECURITY_TOMOYO |
| 164 | bool "TOMOYO" if SECURITY_TOMOYO=y | 166 | bool "TOMOYO" if SECURITY_TOMOYO=y |
| 165 | 167 | ||
| 168 | config DEFAULT_SECURITY_APPARMOR | ||
| 169 | bool "AppArmor" if SECURITY_APPARMOR=y | ||
| 170 | |||
| 166 | config DEFAULT_SECURITY_DAC | 171 | config DEFAULT_SECURITY_DAC |
| 167 | bool "Unix Discretionary Access Controls" | 172 | bool "Unix Discretionary Access Controls" |
| 168 | 173 | ||
| @@ -173,6 +178,7 @@ config DEFAULT_SECURITY | |||
| 173 | default "selinux" if DEFAULT_SECURITY_SELINUX | 178 | default "selinux" if DEFAULT_SECURITY_SELINUX |
| 174 | default "smack" if DEFAULT_SECURITY_SMACK | 179 | default "smack" if DEFAULT_SECURITY_SMACK |
| 175 | default "tomoyo" if DEFAULT_SECURITY_TOMOYO | 180 | default "tomoyo" if DEFAULT_SECURITY_TOMOYO |
| 181 | default "apparmor" if DEFAULT_SECURITY_APPARMOR | ||
| 176 | default "" if DEFAULT_SECURITY_DAC | 182 | default "" if DEFAULT_SECURITY_DAC |
| 177 | 183 | ||
| 178 | endmenu | 184 | endmenu |
diff --git a/security/Makefile b/security/Makefile index da20a193c8dd..8bb0fe9e1ca9 100644 --- a/security/Makefile +++ b/security/Makefile | |||
| @@ -6,6 +6,7 @@ obj-$(CONFIG_KEYS) += keys/ | |||
| 6 | subdir-$(CONFIG_SECURITY_SELINUX) += selinux | 6 | subdir-$(CONFIG_SECURITY_SELINUX) += selinux |
| 7 | subdir-$(CONFIG_SECURITY_SMACK) += smack | 7 | subdir-$(CONFIG_SECURITY_SMACK) += smack |
| 8 | subdir-$(CONFIG_SECURITY_TOMOYO) += tomoyo | 8 | subdir-$(CONFIG_SECURITY_TOMOYO) += tomoyo |
| 9 | subdir-$(CONFIG_SECURITY_APPARMOR) += apparmor | ||
| 9 | 10 | ||
| 10 | # always enable default capabilities | 11 | # always enable default capabilities |
| 11 | obj-y += commoncap.o | 12 | obj-y += commoncap.o |
| @@ -19,6 +20,7 @@ obj-$(CONFIG_SECURITY_SELINUX) += selinux/built-in.o | |||
| 19 | obj-$(CONFIG_SECURITY_SMACK) += smack/built-in.o | 20 | obj-$(CONFIG_SECURITY_SMACK) += smack/built-in.o |
| 20 | obj-$(CONFIG_AUDIT) += lsm_audit.o | 21 | obj-$(CONFIG_AUDIT) += lsm_audit.o |
| 21 | obj-$(CONFIG_SECURITY_TOMOYO) += tomoyo/built-in.o | 22 | obj-$(CONFIG_SECURITY_TOMOYO) += tomoyo/built-in.o |
| 23 | obj-$(CONFIG_SECURITY_APPARMOR) += apparmor/built-in.o | ||
| 22 | obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o | 24 | obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o |
| 23 | 25 | ||
| 24 | # Object integrity file lists | 26 | # Object integrity file lists |
diff --git a/security/apparmor/.gitignore b/security/apparmor/.gitignore new file mode 100644 index 000000000000..0a0a99f3b083 --- /dev/null +++ b/security/apparmor/.gitignore | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | # | ||
| 2 | # Generated include files | ||
| 3 | # | ||
| 4 | af_names.h | ||
| 5 | capability_names.h | ||
diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig new file mode 100644 index 000000000000..72555b9ca7d6 --- /dev/null +++ b/security/apparmor/Kconfig | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | config SECURITY_APPARMOR | ||
| 2 | bool "AppArmor support" | ||
| 3 | depends on SECURITY | ||
| 4 | select AUDIT | ||
| 5 | select SECURITY_PATH | ||
| 6 | select SECURITYFS | ||
| 7 | select SECURITY_NETWORK | ||
| 8 | default n | ||
| 9 | help | ||
| 10 | This enables the AppArmor security module. | ||
| 11 | Required userspace tools (if they are not included in your | ||
| 12 | distribution) and further information may be found at | ||
| 13 | http://apparmor.wiki.kernel.org | ||
| 14 | |||
| 15 | If you are unsure how to answer this question, answer N. | ||
| 16 | |||
| 17 | config SECURITY_APPARMOR_BOOTPARAM_VALUE | ||
| 18 | int "AppArmor boot parameter default value" | ||
| 19 | depends on SECURITY_APPARMOR | ||
| 20 | range 0 1 | ||
| 21 | default 1 | ||
| 22 | help | ||
| 23 | This option sets the default value for the kernel parameter | ||
| 24 | 'apparmor', which allows AppArmor to be enabled or disabled | ||
| 25 | at boot. If this option is set to 0 (zero), the AppArmor | ||
| 26 | kernel parameter will default to 0, disabling AppArmor at | ||
| 27 | boot. If this option is set to 1 (one), the AppArmor | ||
| 28 | kernel parameter will default to 1, enabling AppArmor at | ||
| 29 | boot. | ||
| 30 | |||
| 31 | If you are unsure how to answer this question, answer 1. | ||
diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile new file mode 100644 index 000000000000..f204869399ea --- /dev/null +++ b/security/apparmor/Makefile | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | # Makefile for AppArmor Linux Security Module | ||
| 2 | # | ||
| 3 | obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o | ||
| 4 | |||
| 5 | apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ | ||
| 6 | path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ | ||
| 7 | resource.o sid.o file.o | ||
| 8 | |||
| 9 | clean-files: capability_names.h af_names.h | ||
| 10 | |||
| 11 | quiet_cmd_make-caps = GEN $@ | ||
| 12 | cmd_make-caps = echo "static const char *capability_names[] = {" > $@ ; sed -n -e "/CAP_FS_MASK/d" -e "s/^\#define[ \\t]\\+CAP_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\$$/[\\2] = \"\\1\",/p" $< | tr A-Z a-z >> $@ ; echo "};" >> $@ | ||
| 13 | |||
| 14 | quiet_cmd_make-rlim = GEN $@ | ||
| 15 | cmd_make-rlim = echo "static const char *rlim_names[] = {" > $@ ; sed -n --e "/AF_MAX/d" -e "s/^\# \\?define[ \\t]\\+RLIMIT_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\\(.*\\)\$$/[\\2] = \"\\1\",/p" $< | tr A-Z a-z >> $@ ; echo "};" >> $@ ; echo "static const int rlim_map[] = {" >> $@ ; sed -n -e "/AF_MAX/d" -e "s/^\# \\?define[ \\t]\\+\\(RLIMIT_[A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\\(.*\\)\$$/\\1,/p" $< >> $@ ; echo "};" >> $@ | ||
| 16 | |||
| 17 | $(obj)/capability.o : $(obj)/capability_names.h | ||
| 18 | $(obj)/resource.o : $(obj)/rlim_names.h | ||
| 19 | $(obj)/capability_names.h : $(srctree)/include/linux/capability.h | ||
| 20 | $(call cmd,make-caps) | ||
| 21 | $(obj)/af_names.h : $(srctree)/include/linux/socket.h | ||
| 22 | $(call cmd,make-af) | ||
| 23 | $(obj)/rlim_names.h : $(srctree)/include/asm-generic/resource.h | ||
| 24 | $(call cmd,make-rlim) | ||
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c new file mode 100644 index 000000000000..7320331b44ab --- /dev/null +++ b/security/apparmor/apparmorfs.c | |||
| @@ -0,0 +1,239 @@ | |||
| 1 | /* | ||
| 2 | * AppArmor security module | ||
| 3 | * | ||
| 4 | * This file contains AppArmor /sys/kernel/security/apparmor interface functions | ||
| 5 | * | ||
| 6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
| 7 | * Copyright 2009-2010 Canonical Ltd. | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or | ||
| 10 | * modify it under the terms of the GNU General Public License as | ||
| 11 | * published by the Free Software Foundation, version 2 of the | ||
| 12 | * License. | ||
| 13 | */ | ||
| 14 | |||
| 15 | #include <linux/security.h> | ||
| 16 | #include <linux/vmalloc.h> | ||
| 17 | #include <linux/module.h> | ||
| 18 | #include <linux/seq_file.h> | ||
| 19 | #include <linux/uaccess.h> | ||
| 20 | #include <linux/namei.h> | ||
| 21 | |||
| 22 | #include "include/apparmor.h" | ||
| 23 | #include "include/apparmorfs.h" | ||
| 24 | #include "include/audit.h" | ||
| 25 | #include "include/context.h" | ||
| 26 | #include "include/policy.h" | ||
| 27 | |||
| 28 | /** | ||
| 29 | * aa_simple_write_to_buffer - common routine for getting policy from user | ||
| 30 | * @op: operation doing the user buffer copy | ||
| 31 | * @userbuf: user buffer to copy data from (NOT NULL) | ||
| 32 | * @alloc_size: size of user buffer | ||
| 33 | * @copy_size: size of data to copy from user buffer | ||
| 34 | * @pos: position write is at in the file (NOT NULL) | ||
| 35 | * | ||
| 36 | * Returns: kernel buffer containing copy of user buffer data or an | ||
| 37 | * ERR_PTR on failure. | ||
| 38 | */ | ||
| 39 | static char *aa_simple_write_to_buffer(int op, const char __user *userbuf, | ||
| 40 | size_t alloc_size, size_t copy_size, | ||
| 41 | loff_t *pos) | ||
| 42 | { | ||
| 43 | char *data; | ||
| 44 | |||
| 45 | if (*pos != 0) | ||
| 46 | /* only writes from pos 0, that is complete writes */ | ||
| 47 | return ERR_PTR(-ESPIPE); | ||
| 48 | |||
| 49 | /* | ||
| 50 | * Don't allow profile load/replace/remove from profiles that don't | ||
| 51 | * have CAP_MAC_ADMIN | ||
| 52 | */ | ||
| 53 | if (!aa_may_manage_policy(op)) | ||
| 54 | return ERR_PTR(-EACCES); | ||
| 55 | |||
| 56 | /* freed by caller to simple_write_to_buffer */ | ||
| 57 | data = kvmalloc(alloc_size); | ||
| 58 | if (data == NULL) | ||
| 59 | return ERR_PTR(-ENOMEM); | ||
| 60 | |||
| 61 | if (copy_from_user(data, userbuf, copy_size)) { | ||
| 62 | kvfree(data); | ||
| 63 | return ERR_PTR(-EFAULT); | ||
| 64 | } | ||
| 65 | |||
| 66 | return data; | ||
| 67 | } | ||
| 68 | |||
| 69 | |||
| 70 | /* .load file hook fn to load policy */ | ||
| 71 | static ssize_t profile_load(struct file *f, const char __user *buf, size_t size, | ||
| 72 | loff_t *pos) | ||
| 73 | { | ||
| 74 | char *data; | ||
| 75 | ssize_t error; | ||
| 76 | |||
| 77 | data = aa_simple_write_to_buffer(OP_PROF_LOAD, buf, size, size, pos); | ||
| 78 | |||
| 79 | error = PTR_ERR(data); | ||
| 80 | if (!IS_ERR(data)) { | ||
| 81 | error = aa_replace_profiles(data, size, PROF_ADD); | ||
| 82 | kvfree(data); | ||
| 83 | } | ||
| 84 | |||
| 85 | return error; | ||
| 86 | } | ||
| 87 | |||
| 88 | static const struct file_operations aa_fs_profile_load = { | ||
| 89 | .write = profile_load | ||
| 90 | }; | ||
| 91 | |||
| 92 | /* .replace file hook fn to load and/or replace policy */ | ||
| 93 | static ssize_t profile_replace(struct file *f, const char __user *buf, | ||
| 94 | size_t size, loff_t *pos) | ||
| 95 | { | ||
| 96 | char *data; | ||
| 97 | ssize_t error; | ||
| 98 | |||
| 99 | data = aa_simple_write_to_buffer(OP_PROF_REPL, buf, size, size, pos); | ||
| 100 | error = PTR_ERR(data); | ||
| 101 | if (!IS_ERR(data)) { | ||
| 102 | error = aa_replace_profiles(data, size, PROF_REPLACE); | ||
| 103 | kvfree(data); | ||
| 104 | } | ||
| 105 | |||
| 106 | return error; | ||
| 107 | } | ||
| 108 | |||
| 109 | static const struct file_operations aa_fs_profile_replace = { | ||
| 110 | .write = profile_replace | ||
| 111 | }; | ||
| 112 | |||
| 113 | /* .remove file hook fn to remove loaded policy */ | ||
| 114 | static ssize_t profile_remove(struct file *f, const char __user *buf, | ||
| 115 | size_t size, loff_t *pos) | ||
| 116 | { | ||
| 117 | char *data; | ||
| 118 | ssize_t error; | ||
| 119 | |||
| 120 | /* | ||
| 121 | * aa_remove_profile needs a null terminated string so 1 extra | ||
| 122 | * byte is allocated and the copied data is null terminated. | ||
| 123 | */ | ||
| 124 | data = aa_simple_write_to_buffer(OP_PROF_RM, buf, size + 1, size, pos); | ||
| 125 | |||
| 126 | error = PTR_ERR(data); | ||
| 127 | if (!IS_ERR(data)) { | ||
| 128 | data[size] = 0; | ||
| 129 | error = aa_remove_profiles(data, size); | ||
| 130 | kvfree(data); | ||
| 131 | } | ||
| 132 | |||
| 133 | return error; | ||
| 134 | } | ||
| 135 | |||
| 136 | static const struct file_operations aa_fs_profile_remove = { | ||
| 137 | .write = profile_remove | ||
| 138 | }; | ||
| 139 | |||
| 140 | /** Base file system setup **/ | ||
| 141 | |||
| 142 | static struct dentry *aa_fs_dentry __initdata; | ||
| 143 | |||
| 144 | static void __init aafs_remove(const char *name) | ||
| 145 | { | ||
| 146 | struct dentry *dentry; | ||
| 147 | |||
| 148 | dentry = lookup_one_len(name, aa_fs_dentry, strlen(name)); | ||
| 149 | if (!IS_ERR(dentry)) { | ||
| 150 | securityfs_remove(dentry); | ||
| 151 | dput(dentry); | ||
| 152 | } | ||
| 153 | } | ||
| 154 | |||
| 155 | /** | ||
| 156 | * aafs_create - create an entry in the apparmor filesystem | ||
| 157 | * @name: name of the entry (NOT NULL) | ||
| 158 | * @mask: file permission mask of the file | ||
| 159 | * @fops: file operations for the file (NOT NULL) | ||
| 160 | * | ||
| 161 | * Used aafs_remove to remove entries created with this fn. | ||
| 162 | */ | ||
| 163 | static int __init aafs_create(const char *name, int mask, | ||
| 164 | const struct file_operations *fops) | ||
| 165 | { | ||
| 166 | struct dentry *dentry; | ||
| 167 | |||
| 168 | dentry = securityfs_create_file(name, S_IFREG | mask, aa_fs_dentry, | ||
| 169 | NULL, fops); | ||
| 170 | |||
| 171 | return IS_ERR(dentry) ? PTR_ERR(dentry) : 0; | ||
| 172 | } | ||
| 173 | |||
| 174 | /** | ||
| 175 | * aa_destroy_aafs - cleanup and free aafs | ||
| 176 | * | ||
| 177 | * releases dentries allocated by aa_create_aafs | ||
| 178 | */ | ||
| 179 | void __init aa_destroy_aafs(void) | ||
| 180 | { | ||
| 181 | if (aa_fs_dentry) { | ||
| 182 | aafs_remove(".remove"); | ||
| 183 | aafs_remove(".replace"); | ||
| 184 | aafs_remove(".load"); | ||
| 185 | |||
| 186 | securityfs_remove(aa_fs_dentry); | ||
| 187 | aa_fs_dentry = NULL; | ||
| 188 | } | ||
| 189 | } | ||
| 190 | |||
| 191 | /** | ||
| 192 | * aa_create_aafs - create the apparmor security filesystem | ||
| 193 | * | ||
| 194 | * dentries created here are released by aa_destroy_aafs | ||
| 195 | * | ||
| 196 | * Returns: error on failure | ||
| 197 | */ | ||
| 198 | int __init aa_create_aafs(void) | ||
| 199 | { | ||
| 200 | int error; | ||
| 201 | |||
| 202 | if (!apparmor_initialized) | ||
| 203 | return 0; | ||
| 204 | |||
| 205 | if (aa_fs_dentry) { | ||
| 206 | AA_ERROR("%s: AppArmor securityfs already exists\n", __func__); | ||
| 207 | return -EEXIST; | ||
| 208 | } | ||
| 209 | |||
| 210 | aa_fs_dentry = securityfs_create_dir("apparmor", NULL); | ||
| 211 | if (IS_ERR(aa_fs_dentry)) { | ||
| 212 | error = PTR_ERR(aa_fs_dentry); | ||
| 213 | aa_fs_dentry = NULL; | ||
| 214 | goto error; | ||
| 215 | } | ||
| 216 | |||
| 217 | error = aafs_create(".load", 0640, &aa_fs_profile_load); | ||
| 218 | if (error) | ||
| 219 | goto error; | ||
| 220 | error = aafs_create(".replace", 0640, &aa_fs_profile_replace); | ||
| 221 | if (error) | ||
| 222 | goto error; | ||
| 223 | error = aafs_create(".remove", 0640, &aa_fs_profile_remove); | ||
| 224 | if (error) | ||
| 225 | goto error; | ||
| 226 | |||
| 227 | /* TODO: add support for apparmorfs_null and apparmorfs_mnt */ | ||
| 228 | |||
| 229 | /* Report that AppArmor fs is enabled */ | ||
| 230 | aa_info_message("AppArmor Filesystem Enabled"); | ||
| 231 | return 0; | ||
| 232 | |||
| 233 | error: | ||
| 234 | aa_destroy_aafs(); | ||
| 235 | AA_ERROR("Error creating AppArmor securityfs\n"); | ||
| 236 | return error; | ||
| 237 | } | ||
| 238 | |||
| 239 | fs_initcall(aa_create_aafs); | ||
diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c new file mode 100644 index 000000000000..96502b22b268 --- /dev/null +++ b/security/apparmor/audit.c | |||
| @@ -0,0 +1,215 @@ | |||
| 1 | /* | ||
| 2 | * AppArmor security module | ||
| 3 | * | ||
| 4 | * This file contains AppArmor auditing functions | ||
| 5 | * | ||
| 6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
| 7 | * Copyright 2009-2010 Canonical Ltd. | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or | ||
| 10 | * modify it under the terms of the GNU General Public License as | ||
| 11 | * published by the Free Software Foundation, version 2 of the | ||
| 12 | * License. | ||
| 13 | */ | ||
| 14 | |||
| 15 | #include <linux/audit.h> | ||
| 16 | #include <linux/socket.h> | ||
| 17 | |||
| 18 | #include "include/apparmor.h" | ||
| 19 | #include "include/audit.h" | ||
| 20 | #include "include/policy.h" | ||
| 21 | |||
| 22 | const char *op_table[] = { | ||
| 23 | "null", | ||
| 24 | |||
| 25 | "sysctl", | ||
| 26 | "capable", | ||
| 27 | |||
| 28 | "unlink", | ||
| 29 | "mkdir", | ||
| 30 | "rmdir", | ||
| 31 | "mknod", | ||
| 32 | "truncate", | ||
| 33 | "link", | ||
| 34 | "symlink", | ||
| 35 | "rename_src", | ||
| 36 | "rename_dest", | ||
| 37 | "chmod", | ||
| 38 | "chown", | ||
| 39 | "getattr", | ||
| 40 | "open", | ||
| 41 | |||
| 42 | "file_perm", | ||
| 43 | "file_lock", | ||
| 44 | "file_mmap", | ||
| 45 | "file_mprotect", | ||
| 46 | |||
| 47 | "create", | ||
| 48 | "post_create", | ||
| 49 | "bind", | ||
| 50 | "connect", | ||
| 51 | "listen", | ||
| 52 | "accept", | ||
| 53 | "sendmsg", | ||
| 54 | "recvmsg", | ||
| 55 | "getsockname", | ||
| 56 | "getpeername", | ||
| 57 | "getsockopt", | ||
| 58 | "setsockopt", | ||
| 59 | "socket_shutdown", | ||
| 60 | |||
| 61 | "ptrace", | ||
| 62 | |||
| 63 | "exec", | ||
| 64 | "change_hat", | ||
| 65 | "change_profile", | ||
| 66 | "change_onexec", | ||
| 67 | |||
| 68 | "setprocattr", | ||
| 69 | "setrlimit", | ||
| 70 | |||
| 71 | "profile_replace", | ||
| 72 | "profile_load", | ||
| 73 | "profile_remove" | ||
| 74 | }; | ||
| 75 | |||
| 76 | const char *audit_mode_names[] = { | ||
| 77 | "normal", | ||
| 78 | "quiet_denied", | ||
| 79 | "quiet", | ||
| 80 | "noquiet", | ||
| 81 | "all" | ||
| 82 | }; | ||
| 83 | |||
| 84 | static char *aa_audit_type[] = { | ||
| 85 | "AUDIT", | ||
| 86 | "ALLOWED", | ||
| 87 | "DENIED", | ||
| 88 | "HINT", | ||
| 89 | "STATUS", | ||
| 90 | "ERROR", | ||
| 91 | "KILLED" | ||
| 92 | }; | ||
| 93 | |||
| 94 | /* | ||
| 95 | * Currently AppArmor auditing is fed straight into the audit framework. | ||
| 96 | * | ||
| 97 | * TODO: | ||
| 98 | * netlink interface for complain mode | ||
| 99 | * user auditing, - send user auditing to netlink interface | ||
| 100 | * system control of whether user audit messages go to system log | ||
| 101 | */ | ||
| 102 | |||
| 103 | /** | ||
| 104 | * audit_base - core AppArmor function. | ||
| 105 | * @ab: audit buffer to fill (NOT NULL) | ||
| 106 | * @ca: audit structure containing data to audit (NOT NULL) | ||
| 107 | * | ||
| 108 | * Record common AppArmor audit data from @sa | ||
| 109 | */ | ||
| 110 | static void audit_pre(struct audit_buffer *ab, void *ca) | ||
| 111 | { | ||
| 112 | struct common_audit_data *sa = ca; | ||
| 113 | struct task_struct *tsk = sa->tsk ? sa->tsk : current; | ||
| 114 | |||
| 115 | if (aa_g_audit_header) { | ||
| 116 | audit_log_format(ab, "apparmor="); | ||
| 117 | audit_log_string(ab, aa_audit_type[sa->aad.type]); | ||
| 118 | } | ||
| 119 | |||
| 120 | if (sa->aad.op) { | ||
| 121 | audit_log_format(ab, " operation="); | ||
| 122 | audit_log_string(ab, op_table[sa->aad.op]); | ||
| 123 | } | ||
| 124 | |||
| 125 | if (sa->aad.info) { | ||
| 126 | audit_log_format(ab, " info="); | ||
| 127 | audit_log_string(ab, sa->aad.info); | ||
| 128 | if (sa->aad.error) | ||
| 129 | audit_log_format(ab, " error=%d", sa->aad.error); | ||
| 130 | } | ||
| 131 | |||
| 132 | if (sa->aad.profile) { | ||
| 133 | struct aa_profile *profile = sa->aad.profile; | ||
| 134 | pid_t pid; | ||
| 135 | rcu_read_lock(); | ||
| 136 | pid = tsk->real_parent->pid; | ||
| 137 | rcu_read_unlock(); | ||
| 138 | audit_log_format(ab, " parent=%d", pid); | ||
| 139 | if (profile->ns != root_ns) { | ||
| 140 | audit_log_format(ab, " namespace="); | ||
| 141 | audit_log_untrustedstring(ab, profile->ns->base.hname); | ||
| 142 | } | ||
| 143 | audit_log_format(ab, " profile="); | ||
| 144 | audit_log_untrustedstring(ab, profile->base.hname); | ||
| 145 | } | ||
| 146 | |||
| 147 | if (sa->aad.name) { | ||
| 148 | audit_log_format(ab, " name="); | ||
| 149 | audit_log_untrustedstring(ab, sa->aad.name); | ||
| 150 | } | ||
| 151 | } | ||
| 152 | |||
| 153 | /** | ||
| 154 | * aa_audit_msg - Log a message to the audit subsystem | ||
| 155 | * @sa: audit event structure (NOT NULL) | ||
| 156 | * @cb: optional callback fn for type specific fields (MAYBE NULL) | ||
| 157 | */ | ||
| 158 | void aa_audit_msg(int type, struct common_audit_data *sa, | ||
| 159 | void (*cb) (struct audit_buffer *, void *)) | ||
| 160 | { | ||
| 161 | sa->aad.type = type; | ||
| 162 | sa->lsm_pre_audit = audit_pre; | ||
| 163 | sa->lsm_post_audit = cb; | ||
| 164 | common_lsm_audit(sa); | ||
| 165 | } | ||
| 166 | |||
| 167 | /** | ||
| 168 | * aa_audit - Log a profile based audit event to the audit subsystem | ||
| 169 | * @type: audit type for the message | ||
| 170 | * @profile: profile to check against (NOT NULL) | ||
| 171 | * @gfp: allocation flags to use | ||
| 172 | * @sa: audit event (NOT NULL) | ||
| 173 | * @cb: optional callback fn for type specific fields (MAYBE NULL) | ||
| 174 | * | ||
| 175 | * Handle default message switching based off of audit mode flags | ||
| 176 | * | ||
| 177 | * Returns: error on failure | ||
| 178 | */ | ||
| 179 | int aa_audit(int type, struct aa_profile *profile, gfp_t gfp, | ||
| 180 | struct common_audit_data *sa, | ||
| 181 | void (*cb) (struct audit_buffer *, void *)) | ||
| 182 | { | ||
| 183 | BUG_ON(!profile); | ||
| 184 | |||
| 185 | if (type == AUDIT_APPARMOR_AUTO) { | ||
| 186 | if (likely(!sa->aad.error)) { | ||
| 187 | if (AUDIT_MODE(profile) != AUDIT_ALL) | ||
| 188 | return 0; | ||
| 189 | type = AUDIT_APPARMOR_AUDIT; | ||
| 190 | } else if (COMPLAIN_MODE(profile)) | ||
| 191 | type = AUDIT_APPARMOR_ALLOWED; | ||
| 192 | else | ||
| 193 | type = AUDIT_APPARMOR_DENIED; | ||
| 194 | } | ||
| 195 | if (AUDIT_MODE(profile) == AUDIT_QUIET || | ||
| 196 | (type == AUDIT_APPARMOR_DENIED && | ||
| 197 | AUDIT_MODE(profile) == AUDIT_QUIET)) | ||
| 198 | return sa->aad.error; | ||
| 199 | |||
| 200 | if (KILL_MODE(profile) && type == AUDIT_APPARMOR_DENIED) | ||
| 201 | type = AUDIT_APPARMOR_KILL; | ||
| 202 | |||
| 203 | if (!unconfined(profile)) | ||
| 204 | sa->aad.profile = profile; | ||
| 205 | |||
| 206 | aa_audit_msg(type, sa, cb); | ||
| 207 | |||
| 208 | if (sa->aad.type == AUDIT_APPARMOR_KILL) | ||
| 209 | (void)send_sig_info(SIGKILL, NULL, sa->tsk ? sa->tsk : current); | ||
| 210 | |||
| 211 | if (sa->aad.type == AUDIT_APPARMOR_ALLOWED) | ||
| 212 | return complain_error(sa->aad.error); | ||
| 213 | |||
| 214 | return sa->aad.error; | ||
| 215 | } | ||
diff --git a/security/apparmor/capability.c b/security/apparmor/capability.c new file mode 100644 index 000000000000..9982c48def4e --- /dev/null +++ b/security/apparmor/capability.c | |||
| @@ -0,0 +1,141 @@ | |||
| 1 | /* | ||
| 2 | * AppArmor security module | ||
| 3 | * | ||
| 4 | * This file contains AppArmor capability mediation functions | ||
| 5 | * | ||
| 6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
| 7 | * Copyright 2009-2010 Canonical Ltd. | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or | ||
| 10 | * modify it under the terms of the GNU General Public License as | ||
| 11 | * published by the Free Software Foundation, version 2 of the | ||
| 12 | * License. | ||
| 13 | */ | ||
| 14 | |||
| 15 | #include <linux/capability.h> | ||
| 16 | #include <linux/errno.h> | ||
| 17 | #include <linux/gfp.h> | ||
| 18 | |||
| 19 | #include "include/apparmor.h" | ||
| 20 | #include "include/capability.h" | ||
| 21 | #include "include/context.h" | ||
| 22 | #include "include/policy.h" | ||
| 23 | #include "include/audit.h" | ||
| 24 | |||
| 25 | /* | ||
| 26 | * Table of capability names: we generate it from capabilities.h. | ||
| 27 | */ | ||
| 28 | #include "capability_names.h" | ||
| 29 | |||
| 30 | struct audit_cache { | ||
| 31 | struct aa_profile *profile; | ||
| 32 | kernel_cap_t caps; | ||
| 33 | }; | ||
| 34 | |||
| 35 | static DEFINE_PER_CPU(struct audit_cache, audit_cache); | ||
| 36 | |||
| 37 | /** | ||
| 38 | * audit_cb - call back for capability components of audit struct | ||
| 39 | * @ab - audit buffer (NOT NULL) | ||
| 40 | * @va - audit struct to audit data from (NOT NULL) | ||
| 41 | */ | ||
| 42 | static void audit_cb(struct audit_buffer *ab, void *va) | ||
| 43 | { | ||
| 44 | struct common_audit_data *sa = va; | ||
| 45 | audit_log_format(ab, " capname="); | ||
| 46 | audit_log_untrustedstring(ab, capability_names[sa->u.cap]); | ||
| 47 | } | ||
| 48 | |||
| 49 | /** | ||
| 50 | * audit_caps - audit a capability | ||
| 51 | * @profile: profile confining task (NOT NULL) | ||
| 52 | * @task: task capability test was performed against (NOT NULL) | ||
| 53 | * @cap: capability tested | ||
| 54 | * @error: error code returned by test | ||
| 55 | * | ||
| 56 | * Do auditing of capability and handle, audit/complain/kill modes switching | ||
| 57 | * and duplicate message elimination. | ||
| 58 | * | ||
| 59 | * Returns: 0 or sa->error on success, error code on failure | ||
| 60 | */ | ||
| 61 | static int audit_caps(struct aa_profile *profile, struct task_struct *task, | ||
| 62 | int cap, int error) | ||
| 63 | { | ||
| 64 | struct audit_cache *ent; | ||
| 65 | int type = AUDIT_APPARMOR_AUTO; | ||
| 66 | struct common_audit_data sa; | ||
| 67 | COMMON_AUDIT_DATA_INIT(&sa, CAP); | ||
| 68 | sa.tsk = task; | ||
| 69 | sa.u.cap = cap; | ||
| 70 | sa.aad.op = OP_CAPABLE; | ||
| 71 | sa.aad.error = error; | ||
| 72 | |||
| 73 | if (likely(!error)) { | ||
| 74 | /* test if auditing is being forced */ | ||
| 75 | if (likely((AUDIT_MODE(profile) != AUDIT_ALL) && | ||
| 76 | !cap_raised(profile->caps.audit, cap))) | ||
| 77 | return 0; | ||
| 78 | type = AUDIT_APPARMOR_AUDIT; | ||
| 79 | } else if (KILL_MODE(profile) || | ||
| 80 | cap_raised(profile->caps.kill, cap)) { | ||
| 81 | type = AUDIT_APPARMOR_KILL; | ||
| 82 | } else if (cap_raised(profile->caps.quiet, cap) && | ||
| 83 | AUDIT_MODE(profile) != AUDIT_NOQUIET && | ||
| 84 | AUDIT_MODE(profile) != AUDIT_ALL) { | ||
| 85 | /* quiet auditing */ | ||
| 86 | return error; | ||
| 87 | } | ||
| 88 | |||
| 89 | /* Do simple duplicate message elimination */ | ||
| 90 | ent = &get_cpu_var(audit_cache); | ||
| 91 | if (profile == ent->profile && cap_raised(ent->caps, cap)) { | ||
| 92 | put_cpu_var(audit_cache); | ||
| 93 | if (COMPLAIN_MODE(profile)) | ||
| 94 | return complain_error(error); | ||
| 95 | return error; | ||
| 96 | } else { | ||
| 97 | aa_put_profile(ent->profile); | ||
| 98 | ent->profile = aa_get_profile(profile); | ||
| 99 | cap_raise(ent->caps, cap); | ||
| 100 | } | ||
| 101 | put_cpu_var(audit_cache); | ||
| 102 | |||
| 103 | return aa_audit(type, profile, GFP_ATOMIC, &sa, audit_cb); | ||
| 104 | } | ||
| 105 | |||
| 106 | /** | ||
| 107 | * profile_capable - test if profile allows use of capability @cap | ||
| 108 | * @profile: profile being enforced (NOT NULL, NOT unconfined) | ||
| 109 | * @cap: capability to test if allowed | ||
| 110 | * | ||
| 111 | * Returns: 0 if allowed else -EPERM | ||
| 112 | */ | ||
| 113 | static int profile_capable(struct aa_profile *profile, int cap) | ||
| 114 | { | ||
| 115 | return cap_raised(profile->caps.allow, cap) ? 0 : -EPERM; | ||
| 116 | } | ||
| 117 | |||
| 118 | /** | ||
| 119 | * aa_capable - test permission to use capability | ||
| 120 | * @task: task doing capability test against (NOT NULL) | ||
| 121 | * @profile: profile confining @task (NOT NULL) | ||
| 122 | * @cap: capability to be tested | ||
| 123 | * @audit: whether an audit record should be generated | ||
| 124 | * | ||
| 125 | * Look up capability in profile capability set. | ||
| 126 | * | ||
| 127 | * Returns: 0 on success, or else an error code. | ||
| 128 | */ | ||
| 129 | int aa_capable(struct task_struct *task, struct aa_profile *profile, int cap, | ||
| 130 | int audit) | ||
| 131 | { | ||
| 132 | int error = profile_capable(profile, cap); | ||
| 133 | |||
| 134 | if (!audit) { | ||
| 135 | if (COMPLAIN_MODE(profile)) | ||
| 136 | return complain_error(error); | ||
| 137 | return error; | ||
| 138 | } | ||
| 139 | |||
| 140 | return audit_caps(profile, task, cap, error); | ||
| 141 | } | ||
diff --git a/security/apparmor/context.c b/security/apparmor/context.c new file mode 100644 index 000000000000..8a9b5027c813 --- /dev/null +++ b/security/apparmor/context.c | |||
| @@ -0,0 +1,216 @@ | |||
| 1 | /* | ||
| 2 | * AppArmor security module | ||
| 3 | * | ||
| 4 | * This file contains AppArmor functions used to manipulate object security | ||
| 5 | * contexts. | ||
| 6 | * | ||
| 7 | * Copyright (C) 1998-2008 Novell/SUSE | ||
| 8 | * Copyright 2009-2010 Canonical Ltd. | ||
| 9 | * | ||
| 10 | * This program is free software; you can redistribute it and/or | ||
| 11 | * modify it under the terms of the GNU General Public License as | ||
| 12 | * published by the Free Software Foundation, version 2 of the | ||
| 13 | * License. | ||
| 14 | * | ||
| 15 | * | ||
| 16 | * AppArmor sets confinement on every task, via the the aa_task_cxt and | ||
| 17 | * the aa_task_cxt.profile, both of which are required and are not allowed | ||
| 18 | * to be NULL. The aa_task_cxt is not reference counted and is unique | ||
| 19 | * to each cred (which is reference count). The profile pointed to by | ||
| 20 | * the task_cxt is reference counted. | ||
| 21 | * | ||
| 22 | * TODO | ||
| 23 | * If a task uses change_hat it currently does not return to the old | ||
| 24 | * cred or task context but instead creates a new one. Ideally the task | ||
| 25 | * should return to the previous cred if it has not been modified. | ||
| 26 | * | ||
| 27 | */ | ||
| 28 | |||
| 29 | #include "include/context.h" | ||
| 30 | #include "include/policy.h" | ||
| 31 | |||
| 32 | /** | ||
| 33 | * aa_alloc_task_context - allocate a new task_cxt | ||
| 34 | * @flags: gfp flags for allocation | ||
| 35 | * | ||
| 36 | * Returns: allocated buffer or NULL on failure | ||
| 37 | */ | ||
| 38 | struct aa_task_cxt *aa_alloc_task_context(gfp_t flags) | ||
| 39 | { | ||
| 40 | return kzalloc(sizeof(struct aa_task_cxt), flags); | ||
| 41 | } | ||
| 42 | |||
| 43 | /** | ||
| 44 | * aa_free_task_context - free a task_cxt | ||
| 45 | * @cxt: task_cxt to free (MAYBE NULL) | ||
| 46 | */ | ||
| 47 | void aa_free_task_context(struct aa_task_cxt *cxt) | ||
| 48 | { | ||
| 49 | if (cxt) { | ||
| 50 | aa_put_profile(cxt->profile); | ||
| 51 | aa_put_profile(cxt->previous); | ||
| 52 | aa_put_profile(cxt->onexec); | ||
| 53 | |||
| 54 | kzfree(cxt); | ||
| 55 | } | ||
| 56 | } | ||
| 57 | |||
| 58 | /** | ||
| 59 | * aa_dup_task_context - duplicate a task context, incrementing reference counts | ||
| 60 | * @new: a blank task context (NOT NULL) | ||
| 61 | * @old: the task context to copy (NOT NULL) | ||
| 62 | */ | ||
| 63 | void aa_dup_task_context(struct aa_task_cxt *new, const struct aa_task_cxt *old) | ||
| 64 | { | ||
| 65 | *new = *old; | ||
| 66 | aa_get_profile(new->profile); | ||
| 67 | aa_get_profile(new->previous); | ||
| 68 | aa_get_profile(new->onexec); | ||
| 69 | } | ||
| 70 | |||
| 71 | /** | ||
| 72 | * aa_replace_current_profile - replace the current tasks profiles | ||
| 73 | * @profile: new profile (NOT NULL) | ||
| 74 | * | ||
| 75 | * Returns: 0 or error on failure | ||
| 76 | */ | ||
| 77 | int aa_replace_current_profile(struct aa_profile *profile) | ||
| 78 | { | ||
| 79 | struct aa_task_cxt *cxt = current_cred()->security; | ||
| 80 | struct cred *new; | ||
| 81 | BUG_ON(!profile); | ||
| 82 | |||
| 83 | if (cxt->profile == profile) | ||
| 84 | return 0; | ||
| 85 | |||
| 86 | new = prepare_creds(); | ||
| 87 | if (!new) | ||
| 88 | return -ENOMEM; | ||
| 89 | |||
| 90 | cxt = new->security; | ||
| 91 | if (unconfined(profile) || (cxt->profile->ns != profile->ns)) { | ||
| 92 | /* if switching to unconfined or a different profile namespace | ||
| 93 | * clear out context state | ||
| 94 | */ | ||
| 95 | aa_put_profile(cxt->previous); | ||
| 96 | aa_put_profile(cxt->onexec); | ||
| 97 | cxt->previous = NULL; | ||
| 98 | cxt->onexec = NULL; | ||
| 99 | cxt->token = 0; | ||
| 100 | } | ||
| 101 | /* be careful switching cxt->profile, when racing replacement it | ||
| 102 | * is possible that cxt->profile->replacedby is the reference keeping | ||
| 103 | * @profile valid, so make sure to get its reference before dropping | ||
| 104 | * the reference on cxt->profile */ | ||
| 105 | aa_get_profile(profile); | ||
| 106 | aa_put_profile(cxt->profile); | ||
| 107 | cxt->profile = profile; | ||
| 108 | |||
| 109 | commit_creds(new); | ||
| 110 | return 0; | ||
| 111 | } | ||
| 112 | |||
| 113 | /** | ||
| 114 | * aa_set_current_onexec - set the tasks change_profile to happen onexec | ||
| 115 | * @profile: system profile to set at exec (MAYBE NULL to clear value) | ||
| 116 | * | ||
| 117 | * Returns: 0 or error on failure | ||
| 118 | */ | ||
| 119 | int aa_set_current_onexec(struct aa_profile *profile) | ||
| 120 | { | ||
| 121 | struct aa_task_cxt *cxt; | ||
| 122 | struct cred *new = prepare_creds(); | ||
| 123 | if (!new) | ||
| 124 | return -ENOMEM; | ||
| 125 | |||
| 126 | cxt = new->security; | ||
| 127 | aa_get_profile(profile); | ||
| 128 | aa_put_profile(cxt->onexec); | ||
| 129 | cxt->onexec = profile; | ||
| 130 | |||
| 131 | commit_creds(new); | ||
| 132 | return 0; | ||
| 133 | } | ||
| 134 | |||
| 135 | /** | ||
| 136 | * aa_set_current_hat - set the current tasks hat | ||
| 137 | * @profile: profile to set as the current hat (NOT NULL) | ||
| 138 | * @token: token value that must be specified to change from the hat | ||
| 139 | * | ||
| 140 | * Do switch of tasks hat. If the task is currently in a hat | ||
| 141 | * validate the token to match. | ||
| 142 | * | ||
| 143 | * Returns: 0 or error on failure | ||
| 144 | */ | ||
| 145 | int aa_set_current_hat(struct aa_profile *profile, u64 token) | ||
| 146 | { | ||
| 147 | struct aa_task_cxt *cxt; | ||
| 148 | struct cred *new = prepare_creds(); | ||
| 149 | if (!new) | ||
| 150 | return -ENOMEM; | ||
| 151 | BUG_ON(!profile); | ||
| 152 | |||
| 153 | cxt = new->security; | ||
| 154 | if (!cxt->previous) { | ||
| 155 | /* transfer refcount */ | ||
| 156 | cxt->previous = cxt->profile; | ||
| 157 | cxt->token = token; | ||
| 158 | } else if (cxt->token == token) { | ||
| 159 | aa_put_profile(cxt->profile); | ||
| 160 | } else { | ||
| 161 | /* previous_profile && cxt->token != token */ | ||
| 162 | abort_creds(new); | ||
| 163 | return -EACCES; | ||
| 164 | } | ||
| 165 | cxt->profile = aa_get_profile(aa_newest_version(profile)); | ||
| 166 | /* clear exec on switching context */ | ||
| 167 | aa_put_profile(cxt->onexec); | ||
| 168 | cxt->onexec = NULL; | ||
| 169 | |||
| 170 | commit_creds(new); | ||
| 171 | return 0; | ||
| 172 | } | ||
| 173 | |||
| 174 | /** | ||
| 175 | * aa_restore_previous_profile - exit from hat context restoring the profile | ||
| 176 | * @token: the token that must be matched to exit hat context | ||
| 177 | * | ||
| 178 | * Attempt to return out of a hat to the previous profile. The token | ||
| 179 | * must match the stored token value. | ||
| 180 | * | ||
| 181 | * Returns: 0 or error of failure | ||
| 182 | */ | ||
| 183 | int aa_restore_previous_profile(u64 token) | ||
| 184 | { | ||
| 185 | struct aa_task_cxt *cxt; | ||
| 186 | struct cred *new = prepare_creds(); | ||
| 187 | if (!new) | ||
| 188 | return -ENOMEM; | ||
| 189 | |||
| 190 | cxt = new->security; | ||
| 191 | if (cxt->token != token) { | ||
| 192 | abort_creds(new); | ||
| 193 | return -EACCES; | ||
| 194 | } | ||
| 195 | /* ignore restores when there is no saved profile */ | ||
| 196 | if (!cxt->previous) { | ||
| 197 | abort_creds(new); | ||
| 198 | return 0; | ||
| 199 | } | ||
| 200 | |||
| 201 | aa_put_profile(cxt->profile); | ||
| 202 | cxt->profile = aa_newest_version(cxt->previous); | ||
| 203 | BUG_ON(!cxt->profile); | ||
| 204 | if (unlikely(cxt->profile != cxt->previous)) { | ||
| 205 | aa_get_profile(cxt->profile); | ||
| 206 | aa_put_profile(cxt->previous); | ||
| 207 | } | ||
| 208 | /* clear exec && prev information when restoring to previous context */ | ||
| 209 | cxt->previous = NULL; | ||
| 210 | cxt->token = 0; | ||
| 211 | aa_put_profile(cxt->onexec); | ||
| 212 | cxt->onexec = NULL; | ||
| 213 | |||
| 214 | commit_creds(new); | ||
| 215 | return 0; | ||
| 216 | } | ||
diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c new file mode 100644 index 000000000000..c825c6e0b636 --- /dev/null +++ b/security/apparmor/domain.c | |||
| @@ -0,0 +1,823 @@ | |||
| 1 | /* | ||
| 2 | * AppArmor security module | ||
| 3 | * | ||
| 4 | * This file contains AppArmor policy attachment and domain transitions | ||
| 5 | * | ||
| 6 | * Copyright (C) 2002-2008 Novell/SUSE | ||
| 7 | * Copyright 2009-2010 Canonical Ltd. | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or | ||
| 10 | * modify it under the terms of the GNU General Public License as | ||
| 11 | * published by the Free Software Foundation, version 2 of the | ||
| 12 | * License. | ||
| 13 | */ | ||
| 14 | |||
| 15 | #include <linux/errno.h> | ||
| 16 | #include <linux/fdtable.h> | ||
| 17 | #include <linux/file.h> | ||
| 18 | #include <linux/mount.h> | ||
| 19 | #include <linux/syscalls.h> | ||
| 20 | #include <linux/tracehook.h> | ||
| 21 | #include <linux/personality.h> | ||
| 22 | |||
| 23 | #include "include/audit.h" | ||
| 24 | #include "include/apparmorfs.h" | ||
| 25 | #include "include/context.h" | ||
| 26 | #include "include/domain.h" | ||
| 27 | #include "include/file.h" | ||
| 28 | #include "include/ipc.h" | ||
| 29 | #include "include/match.h" | ||
| 30 | #include "include/path.h" | ||
| 31 | #include "include/policy.h" | ||
| 32 | |||
| 33 | /** | ||
| 34 | * aa_free_domain_entries - free entries in a domain table | ||
| 35 | * @domain: the domain table to free (MAYBE NULL) | ||
| 36 | */ | ||
| 37 | void aa_free_domain_entries(struct aa_domain *domain) | ||
| 38 | { | ||
| 39 | int i; | ||
| 40 | if (domain) { | ||
| 41 | if (!domain->table) | ||
| 42 | return; | ||
| 43 | |||
| 44 | for (i = 0; i < domain->size; i++) | ||
| 45 | kzfree(domain->table[i]); | ||
| 46 | kzfree(domain->table); | ||
| 47 | domain->table = NULL; | ||
| 48 | } | ||
| 49 | } | ||
| 50 | |||
| 51 | /** | ||
| 52 | * may_change_ptraced_domain - check if can change profile on ptraced task | ||
| 53 | * @task: task we want to change profile of (NOT NULL) | ||
| 54 | * @to_profile: profile to change to (NOT NULL) | ||
| 55 | * | ||
| 56 | * Check if the task is ptraced and if so if the tracing task is allowed | ||
| 57 | * to trace the new domain | ||
| 58 | * | ||
| 59 | * Returns: %0 or error if change not allowed | ||
| 60 | */ | ||
| 61 | static int may_change_ptraced_domain(struct task_struct *task, | ||
| 62 | struct aa_profile *to_profile) | ||
| 63 | { | ||
| 64 | struct task_struct *tracer; | ||
| 65 | const struct cred *cred = NULL; | ||
| 66 | struct aa_profile *tracerp = NULL; | ||
| 67 | int error = 0; | ||
| 68 | |||
| 69 | rcu_read_lock(); | ||
| 70 | tracer = tracehook_tracer_task(task); | ||
| 71 | if (tracer) { | ||
| 72 | /* released below */ | ||
| 73 | cred = get_task_cred(tracer); | ||
| 74 | tracerp = aa_cred_profile(cred); | ||
| 75 | } | ||
| 76 | rcu_read_unlock(); | ||
| 77 | |||
| 78 | /* not ptraced */ | ||
| 79 | if (!tracer || unconfined(tracerp)) | ||
| 80 | goto out; | ||
| 81 | |||
| 82 | error = aa_may_ptrace(tracer, tracerp, to_profile, PTRACE_MODE_ATTACH); | ||
| 83 | |||
| 84 | out: | ||
| 85 | if (cred) | ||
| 86 | put_cred(cred); | ||
| 87 | |||
| 88 | return error; | ||
| 89 | } | ||
| 90 | |||
| 91 | /** | ||
| 92 | * change_profile_perms - find permissions for change_profile | ||
| 93 | * @profile: the current profile (NOT NULL) | ||
| 94 | * @ns: the namespace being switched to (NOT NULL) | ||
| 95 | * @name: the name of the profile to change to (NOT NULL) | ||
| 96 | * @request: requested perms | ||
| 97 | * @start: state to start matching in | ||
| 98 | * | ||
| 99 | * Returns: permission set | ||
| 100 | */ | ||
| 101 | static struct file_perms change_profile_perms(struct aa_profile *profile, | ||
| 102 | struct aa_namespace *ns, | ||
| 103 | const char *name, u32 request, | ||
| 104 | unsigned int start) | ||
| 105 | { | ||
| 106 | struct file_perms perms; | ||
| 107 | struct path_cond cond = { }; | ||
| 108 | unsigned int state; | ||
| 109 | |||
| 110 | if (unconfined(profile)) { | ||
| 111 | perms.allow = AA_MAY_CHANGE_PROFILE | AA_MAY_ONEXEC; | ||
| 112 | perms.audit = perms.quiet = perms.kill = 0; | ||
| 113 | return perms; | ||
| 114 | } else if (!profile->file.dfa) { | ||
| 115 | return nullperms; | ||
| 116 | } else if ((ns == profile->ns)) { | ||
| 117 | /* try matching against rules with out namespace prepended */ | ||
| 118 | aa_str_perms(profile->file.dfa, start, name, &cond, &perms); | ||
| 119 | if (COMBINED_PERM_MASK(perms) & request) | ||
| 120 | return perms; | ||
| 121 | } | ||
| 122 | |||
| 123 | /* try matching with namespace name and then profile */ | ||
| 124 | state = aa_dfa_match(profile->file.dfa, start, ns->base.name); | ||
| 125 | state = aa_dfa_match_len(profile->file.dfa, state, ":", 1); | ||
| 126 | aa_str_perms(profile->file.dfa, state, name, &cond, &perms); | ||
| 127 | |||
| 128 | return perms; | ||
| 129 | } | ||
| 130 | |||
| 131 | /** | ||
| 132 | * __attach_match_ - find an attachment match | ||
| 133 | * @name - to match against (NOT NULL) | ||
| 134 | * @head - profile list to walk (NOT NULL) | ||
| 135 | * | ||
| 136 | * Do a linear search on the profiles in the list. There is a matching | ||
| 137 | * preference where an exact match is preferred over a name which uses | ||
| 138 | * expressions to match, and matching expressions with the greatest | ||
| 139 | * xmatch_len are preferred. | ||
| 140 | * | ||
| 141 | * Requires: @head not be shared or have appropriate locks held | ||
| 142 | * | ||
| 143 | * Returns: profile or NULL if no match found | ||
| 144 | */ | ||
| 145 | static struct aa_profile *__attach_match(const char *name, | ||
| 146 | struct list_head *head) | ||
| 147 | { | ||
| 148 | int len = 0; | ||
| 149 | struct aa_profile *profile, *candidate = NULL; | ||
| 150 | |||
| 151 | list_for_each_entry(profile, head, base.list) { | ||
| 152 | if (profile->flags & PFLAG_NULL) | ||
| 153 | continue; | ||
| 154 | if (profile->xmatch && profile->xmatch_len > len) { | ||
| 155 | unsigned int state = aa_dfa_match(profile->xmatch, | ||
| 156 | DFA_START, name); | ||
| 157 | u32 perm = dfa_user_allow(profile->xmatch, state); | ||
| 158 | /* any accepting state means a valid match. */ | ||
| 159 | if (perm & MAY_EXEC) { | ||
| 160 | candidate = profile; | ||
| 161 | len = profile->xmatch_len; | ||
| 162 | } | ||
| 163 | } else if (!strcmp(profile->base.name, name)) | ||
| 164 | /* exact non-re match, no more searching required */ | ||
| 165 | return profile; | ||
| 166 | } | ||
| 167 | |||
| 168 | return candidate; | ||
| 169 | } | ||
| 170 | |||
| 171 | /** | ||
| 172 | * find_attach - do attachment search for unconfined processes | ||
| 173 | * @ns: the current namespace (NOT NULL) | ||
| 174 | * @list: list to search (NOT NULL) | ||
| 175 | * @name: the executable name to match against (NOT NULL) | ||
| 176 | * | ||
| 177 | * Returns: profile or NULL if no match found | ||
| 178 | */ | ||
| 179 | static struct aa_profile *find_attach(struct aa_namespace *ns, | ||
| 180 | struct list_head *list, const char *name) | ||
| 181 | { | ||
| 182 | struct aa_profile *profile; | ||
| 183 | |||
| 184 | read_lock(&ns->lock); | ||
| 185 | profile = aa_get_profile(__attach_match(name, list)); | ||
| 186 | read_unlock(&ns->lock); | ||
| 187 | |||
| 188 | return profile; | ||
| 189 | } | ||
| 190 | |||
| 191 | /** | ||
| 192 | * separate_fqname - separate the namespace and profile names | ||
| 193 | * @fqname: the fqname name to split (NOT NULL) | ||
| 194 | * @ns_name: the namespace name if it exists (NOT NULL) | ||
| 195 | * | ||
| 196 | * This is the xtable equivalent routine of aa_split_fqname. It finds the | ||
| 197 | * split in an xtable fqname which contains an embedded \0 instead of a : | ||
| 198 | * if a namespace is specified. This is done so the xtable is constant and | ||
| 199 | * isn't re-split on every lookup. | ||
| 200 | * | ||
| 201 | * Either the profile or namespace name may be optional but if the namespace | ||
| 202 | * is specified the profile name termination must be present. This results | ||
| 203 | * in the following possible encodings: | ||
| 204 | * profile_name\0 | ||
| 205 | * :ns_name\0profile_name\0 | ||
| 206 | * :ns_name\0\0 | ||
| 207 | * | ||
| 208 | * NOTE: the xtable fqname is pre-validated at load time in unpack_trans_table | ||
| 209 | * | ||
| 210 | * Returns: profile name if it is specified else NULL | ||
| 211 | */ | ||
| 212 | static const char *separate_fqname(const char *fqname, const char **ns_name) | ||
| 213 | { | ||
| 214 | const char *name; | ||
| 215 | |||
| 216 | if (fqname[0] == ':') { | ||
| 217 | /* In this case there is guaranteed to be two \0 terminators | ||
| 218 | * in the string. They are verified at load time by | ||
| 219 | * by unpack_trans_table | ||
| 220 | */ | ||
| 221 | *ns_name = fqname + 1; /* skip : */ | ||
| 222 | name = *ns_name + strlen(*ns_name) + 1; | ||
| 223 | if (!*name) | ||
| 224 | name = NULL; | ||
| 225 | } else { | ||
| 226 | *ns_name = NULL; | ||
| 227 | name = fqname; | ||
| 228 | } | ||
| 229 | |||
| 230 | return name; | ||
| 231 | } | ||
| 232 | |||
| 233 | static const char *next_name(int xtype, const char *name) | ||
| 234 | { | ||
| 235 | return NULL; | ||
| 236 | } | ||
| 237 | |||
| 238 | /** | ||
| 239 | * x_table_lookup - lookup an x transition name via transition table | ||
| 240 | * @profile: current profile (NOT NULL) | ||
| 241 | * @xindex: index into x transition table | ||
| 242 | * | ||
| 243 | * Returns: refcounted profile, or NULL on failure (MAYBE NULL) | ||
| 244 | */ | ||
| 245 | static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) | ||
| 246 | { | ||
| 247 | struct aa_profile *new_profile = NULL; | ||
| 248 | struct aa_namespace *ns = profile->ns; | ||
| 249 | u32 xtype = xindex & AA_X_TYPE_MASK; | ||
| 250 | int index = xindex & AA_X_INDEX_MASK; | ||
| 251 | const char *name; | ||
| 252 | |||
| 253 | /* index is guaranteed to be in range, validated at load time */ | ||
| 254 | for (name = profile->file.trans.table[index]; !new_profile && name; | ||
| 255 | name = next_name(xtype, name)) { | ||
| 256 | struct aa_namespace *new_ns; | ||
| 257 | const char *xname = NULL; | ||
| 258 | |||
| 259 | new_ns = NULL; | ||
| 260 | if (xindex & AA_X_CHILD) { | ||
| 261 | /* release by caller */ | ||
| 262 | new_profile = aa_find_child(profile, name); | ||
| 263 | continue; | ||
| 264 | } else if (*name == ':') { | ||
| 265 | /* switching namespace */ | ||
| 266 | const char *ns_name; | ||
| 267 | xname = name = separate_fqname(name, &ns_name); | ||
| 268 | if (!xname) | ||
| 269 | /* no name so use profile name */ | ||
| 270 | xname = profile->base.hname; | ||
| 271 | if (*ns_name == '@') { | ||
| 272 | /* TODO: variable support */ | ||
| 273 | ; | ||
| 274 | } | ||
| 275 | /* released below */ | ||
| 276 | new_ns = aa_find_namespace(ns, ns_name); | ||
| 277 | if (!new_ns) | ||
| 278 | continue; | ||
| 279 | } else if (*name == '@') { | ||
| 280 | /* TODO: variable support */ | ||
| 281 | continue; | ||
| 282 | } else { | ||
| 283 | /* basic namespace lookup */ | ||
| 284 | xname = name; | ||
| 285 | } | ||
| 286 | |||
| 287 | /* released by caller */ | ||
| 288 | new_profile = aa_lookup_profile(new_ns ? new_ns : ns, xname); | ||
| 289 | aa_put_namespace(new_ns); | ||
| 290 | } | ||
| 291 | |||
| 292 | /* released by caller */ | ||
| 293 | return new_profile; | ||
| 294 | } | ||
| 295 | |||
| 296 | /** | ||
| 297 | * x_to_profile - get target profile for a given xindex | ||
| 298 | * @profile: current profile (NOT NULL) | ||
| 299 | * @name: name to lookup (NOT NULL) | ||
| 300 | * @xindex: index into x transition table | ||
| 301 | * | ||
| 302 | * find profile for a transition index | ||
| 303 | * | ||
| 304 | * Returns: refcounted profile or NULL if not found available | ||
| 305 | */ | ||
| 306 | static struct aa_profile *x_to_profile(struct aa_profile *profile, | ||
| 307 | const char *name, u32 xindex) | ||
| 308 | { | ||
| 309 | struct aa_profile *new_profile = NULL; | ||
| 310 | struct aa_namespace *ns = profile->ns; | ||
| 311 | u32 xtype = xindex & AA_X_TYPE_MASK; | ||
| 312 | |||
| 313 | switch (xtype) { | ||
| 314 | case AA_X_NONE: | ||
| 315 | /* fail exec unless ix || ux fallback - handled by caller */ | ||
| 316 | return NULL; | ||
| 317 | case AA_X_NAME: | ||
| 318 | if (xindex & AA_X_CHILD) | ||
| 319 | /* released by caller */ | ||
| 320 | new_profile = find_attach(ns, &profile->base.profiles, | ||
| 321 | name); | ||
| 322 | else | ||
| 323 | /* released by caller */ | ||
| 324 | new_profile = find_attach(ns, &ns->base.profiles, | ||
| 325 | name); | ||
| 326 | break; | ||
| 327 | case AA_X_TABLE: | ||
| 328 | /* released by caller */ | ||
| 329 | new_profile = x_table_lookup(profile, xindex); | ||
| 330 | break; | ||
| 331 | } | ||
| 332 | |||
| 333 | /* released by caller */ | ||
| 334 | return new_profile; | ||
| 335 | } | ||
| 336 | |||
| 337 | /** | ||
| 338 | * apparmor_bprm_set_creds - set the new creds on the bprm struct | ||
| 339 | * @bprm: binprm for the exec (NOT NULL) | ||
| 340 | * | ||
| 341 | * Returns: %0 or error on failure | ||
| 342 | */ | ||
| 343 | int apparmor_bprm_set_creds(struct linux_binprm *bprm) | ||
| 344 | { | ||
| 345 | struct aa_task_cxt *cxt; | ||
| 346 | struct aa_profile *profile, *new_profile = NULL; | ||
| 347 | struct aa_namespace *ns; | ||
| 348 | char *buffer = NULL; | ||
| 349 | unsigned int state; | ||
| 350 | struct file_perms perms = {}; | ||
| 351 | struct path_cond cond = { | ||
| 352 | bprm->file->f_path.dentry->d_inode->i_uid, | ||
| 353 | bprm->file->f_path.dentry->d_inode->i_mode | ||
| 354 | }; | ||
| 355 | const char *name = NULL, *target = NULL, *info = NULL; | ||
| 356 | int error = cap_bprm_set_creds(bprm); | ||
| 357 | if (error) | ||
| 358 | return error; | ||
| 359 | |||
| 360 | if (bprm->cred_prepared) | ||
| 361 | return 0; | ||
| 362 | |||
| 363 | cxt = bprm->cred->security; | ||
| 364 | BUG_ON(!cxt); | ||
| 365 | |||
| 366 | profile = aa_get_profile(aa_newest_version(cxt->profile)); | ||
| 367 | /* | ||
| 368 | * get the namespace from the replacement profile as replacement | ||
| 369 | * can change the namespace | ||
| 370 | */ | ||
| 371 | ns = profile->ns; | ||
| 372 | state = profile->file.start; | ||
| 373 | |||
| 374 | /* buffer freed below, name is pointer into buffer */ | ||
| 375 | error = aa_get_name(&bprm->file->f_path, profile->path_flags, &buffer, | ||
| 376 | &name); | ||
| 377 | if (error) { | ||
| 378 | if (profile->flags & | ||
| 379 | (PFLAG_IX_ON_NAME_ERROR | PFLAG_UNCONFINED)) | ||
| 380 | error = 0; | ||
| 381 | info = "Exec failed name resolution"; | ||
| 382 | name = bprm->filename; | ||
| 383 | goto audit; | ||
| 384 | } | ||
| 385 | |||
| 386 | /* Test for onexec first as onexec directives override other | ||
| 387 | * x transitions. | ||
| 388 | */ | ||
| 389 | if (unconfined(profile)) { | ||
| 390 | /* unconfined task */ | ||
| 391 | if (cxt->onexec) | ||
| 392 | /* change_profile on exec already been granted */ | ||
| 393 | new_profile = aa_get_profile(cxt->onexec); | ||
| 394 | else | ||
| 395 | new_profile = find_attach(ns, &ns->base.profiles, name); | ||
| 396 | if (!new_profile) | ||
| 397 | goto cleanup; | ||
| 398 | goto apply; | ||
| 399 | } | ||
| 400 | |||
| 401 | /* find exec permissions for name */ | ||
| 402 | state = aa_str_perms(profile->file.dfa, state, name, &cond, &perms); | ||
| 403 | if (cxt->onexec) { | ||
| 404 | struct file_perms cp; | ||
| 405 | info = "change_profile onexec"; | ||
| 406 | if (!(perms.allow & AA_MAY_ONEXEC)) | ||
| 407 | goto audit; | ||
| 408 | |||
| 409 | /* test if this exec can be paired with change_profile onexec. | ||
| 410 | * onexec permission is linked to exec with a standard pairing | ||
| 411 | * exec\0change_profile | ||
| 412 | */ | ||
| 413 | state = aa_dfa_null_transition(profile->file.dfa, state); | ||
| 414 | cp = change_profile_perms(profile, cxt->onexec->ns, name, | ||
| 415 | AA_MAY_ONEXEC, state); | ||
| 416 | |||
| 417 | if (!(cp.allow & AA_MAY_ONEXEC)) | ||
| 418 | goto audit; | ||
| 419 | new_profile = aa_get_profile(aa_newest_version(cxt->onexec)); | ||
| 420 | goto apply; | ||
| 421 | } | ||
| 422 | |||
| 423 | if (perms.allow & MAY_EXEC) { | ||
| 424 | /* exec permission determine how to transition */ | ||
| 425 | new_profile = x_to_profile(profile, name, perms.xindex); | ||
| 426 | if (!new_profile) { | ||
| 427 | if (perms.xindex & AA_X_INHERIT) { | ||
| 428 | /* (p|c|n)ix - don't change profile but do | ||
| 429 | * use the newest version, which was picked | ||
| 430 | * up above when getting profile | ||
| 431 | */ | ||
| 432 | info = "ix fallback"; | ||
| 433 | new_profile = aa_get_profile(profile); | ||
| 434 | goto x_clear; | ||
| 435 | } else if (perms.xindex & AA_X_UNCONFINED) { | ||
| 436 | new_profile = aa_get_profile(ns->unconfined); | ||
| 437 | info = "ux fallback"; | ||
| 438 | } else { | ||
| 439 | error = -ENOENT; | ||
| 440 | info = "profile not found"; | ||
| 441 | } | ||
| 442 | } | ||
| 443 | } else if (COMPLAIN_MODE(profile)) { | ||
| 444 | /* no exec permission - are we in learning mode */ | ||
| 445 | new_profile = aa_new_null_profile(profile, 0); | ||
| 446 | if (!new_profile) { | ||
| 447 | error = -ENOMEM; | ||
| 448 | info = "could not create null profile"; | ||
| 449 | } else { | ||
| 450 | error = -EACCES; | ||
| 451 | target = new_profile->base.hname; | ||
| 452 | } | ||
| 453 | perms.xindex |= AA_X_UNSAFE; | ||
| 454 | } else | ||
| 455 | /* fail exec */ | ||
| 456 | error = -EACCES; | ||
| 457 | |||
| 458 | if (!new_profile) | ||
| 459 | goto audit; | ||
| 460 | |||
| 461 | if (bprm->unsafe & LSM_UNSAFE_SHARE) { | ||
| 462 | /* FIXME: currently don't mediate shared state */ | ||
| 463 | ; | ||
| 464 | } | ||
| 465 | |||
| 466 | if (bprm->unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) { | ||
| 467 | error = may_change_ptraced_domain(current, new_profile); | ||
| 468 | if (error) { | ||
| 469 | aa_put_profile(new_profile); | ||
| 470 | goto audit; | ||
| 471 | } | ||
| 472 | } | ||
| 473 | |||
| 474 | /* Determine if secure exec is needed. | ||
| 475 | * Can be at this point for the following reasons: | ||
| 476 | * 1. unconfined switching to confined | ||
| 477 | * 2. confined switching to different confinement | ||
| 478 | * 3. confined switching to unconfined | ||
| 479 | * | ||
| 480 | * Cases 2 and 3 are marked as requiring secure exec | ||
| 481 | * (unless policy specified "unsafe exec") | ||
| 482 | * | ||
| 483 | * bprm->unsafe is used to cache the AA_X_UNSAFE permission | ||
| 484 | * to avoid having to recompute in secureexec | ||
| 485 | */ | ||
| 486 | if (!(perms.xindex & AA_X_UNSAFE)) { | ||
| 487 | AA_DEBUG("scrubbing environment variables for %s profile=%s\n", | ||
| 488 | name, new_profile->base.hname); | ||
| 489 | bprm->unsafe |= AA_SECURE_X_NEEDED; | ||
| 490 | } | ||
| 491 | apply: | ||
| 492 | target = new_profile->base.hname; | ||
| 493 | /* when transitioning profiles clear unsafe personality bits */ | ||
| 494 | bprm->per_clear |= PER_CLEAR_ON_SETID; | ||
| 495 | |||
| 496 | x_clear: | ||
| 497 | aa_put_profile(cxt->profile); | ||
| 498 | /* transfer new profile reference will be released when cxt is freed */ | ||
| 499 | cxt->profile = new_profile; | ||
| 500 | |||
| 501 | /* clear out all temporary/transitional state from the context */ | ||
| 502 | aa_put_profile(cxt->previous); | ||
| 503 | aa_put_profile(cxt->onexec); | ||
| 504 | cxt->previous = NULL; | ||
| 505 | cxt->onexec = NULL; | ||
| 506 | cxt->token = 0; | ||
| 507 | |||
| 508 | audit: | ||
| 509 | error = aa_audit_file(profile, &perms, GFP_KERNEL, OP_EXEC, MAY_EXEC, | ||
| 510 | name, target, cond.uid, info, error); | ||
| 511 | |||
| 512 | cleanup: | ||
| 513 | aa_put_profile(profile); | ||
| 514 | kfree(buffer); | ||
| 515 | |||
| 516 | return error; | ||
| 517 | } | ||
| 518 | |||
| 519 | /** | ||
| 520 | * apparmor_bprm_secureexec - determine if secureexec is needed | ||
| 521 | * @bprm: binprm for exec (NOT NULL) | ||
| 522 | * | ||
| 523 | * Returns: %1 if secureexec is needed else %0 | ||
| 524 | */ | ||
| 525 | int apparmor_bprm_secureexec(struct linux_binprm *bprm) | ||
| 526 | { | ||
| 527 | int ret = cap_bprm_secureexec(bprm); | ||
| 528 | |||
| 529 | /* the decision to use secure exec is computed in set_creds | ||
| 530 | * and stored in bprm->unsafe. | ||
| 531 | */ | ||
| 532 | if (!ret && (bprm->unsafe & AA_SECURE_X_NEEDED)) | ||
| 533 | ret = 1; | ||
| 534 | |||
| 535 | return ret; | ||
| 536 | } | ||
| 537 | |||
| 538 | /** | ||
| 539 | * apparmor_bprm_committing_creds - do task cleanup on committing new creds | ||
| 540 | * @bprm: binprm for the exec (NOT NULL) | ||
| 541 | */ | ||
| 542 | void apparmor_bprm_committing_creds(struct linux_binprm *bprm) | ||
| 543 | { | ||
| 544 | struct aa_profile *profile = __aa_current_profile(); | ||
| 545 | struct aa_task_cxt *new_cxt = bprm->cred->security; | ||
| 546 | |||
| 547 | /* bail out if unconfined or not changing profile */ | ||
| 548 | if ((new_cxt->profile == profile) || | ||
| 549 | (unconfined(new_cxt->profile))) | ||
| 550 | return; | ||
| 551 | |||
| 552 | current->pdeath_signal = 0; | ||
| 553 | |||
| 554 | /* reset soft limits and set hard limits for the new profile */ | ||
| 555 | __aa_transition_rlimits(profile, new_cxt->profile); | ||
| 556 | } | ||
| 557 | |||
| 558 | /** | ||
| 559 | * apparmor_bprm_commited_cred - do cleanup after new creds committed | ||
| 560 | * @bprm: binprm for the exec (NOT NULL) | ||
| 561 | */ | ||
| 562 | void apparmor_bprm_committed_creds(struct linux_binprm *bprm) | ||
| 563 | { | ||
| 564 | /* TODO: cleanup signals - ipc mediation */ | ||
| 565 | return; | ||
| 566 | } | ||
| 567 | |||
| 568 | /* | ||
| 569 | * Functions for self directed profile change | ||
| 570 | */ | ||
| 571 | |||
| 572 | /** | ||
| 573 | * new_compound_name - create an hname with @n2 appended to @n1 | ||
| 574 | * @n1: base of hname (NOT NULL) | ||
| 575 | * @n2: name to append (NOT NULL) | ||
| 576 | * | ||
| 577 | * Returns: new name or NULL on error | ||
| 578 | */ | ||
| 579 | static char *new_compound_name(const char *n1, const char *n2) | ||
| 580 | { | ||
| 581 | char *name = kmalloc(strlen(n1) + strlen(n2) + 3, GFP_KERNEL); | ||
| 582 | if (name) | ||
| 583 | sprintf(name, "%s//%s", n1, n2); | ||
| 584 | return name; | ||
| 585 | } | ||
| 586 | |||
| 587 | /** | ||
| 588 | * aa_change_hat - change hat to/from subprofile | ||
| 589 | * @hats: vector of hat names to try changing into (MAYBE NULL if @count == 0) | ||
| 590 | * @count: number of hat names in @hats | ||
| 591 | * @token: magic value to validate the hat change | ||
| 592 | * @permtest: true if this is just a permission test | ||
| 593 | * | ||
| 594 | * Change to the first profile specified in @hats that exists, and store | ||
| 595 | * the @hat_magic in the current task context. If the count == 0 and the | ||
| 596 | * @token matches that stored in the current task context, return to the | ||
| 597 | * top level profile. | ||
| 598 | * | ||
| 599 | * Returns %0 on success, error otherwise. | ||
| 600 | */ | ||
| 601 | int aa_change_hat(const char *hats[], int count, u64 token, bool permtest) | ||
| 602 | { | ||
| 603 | const struct cred *cred; | ||
| 604 | struct aa_task_cxt *cxt; | ||
| 605 | struct aa_profile *profile, *previous_profile, *hat = NULL; | ||
| 606 | char *name = NULL; | ||
| 607 | int i; | ||
| 608 | struct file_perms perms = {}; | ||
| 609 | const char *target = NULL, *info = NULL; | ||
| 610 | int error = 0; | ||
| 611 | |||
| 612 | /* released below */ | ||
| 613 | cred = get_current_cred(); | ||
| 614 | cxt = cred->security; | ||
| 615 | profile = aa_cred_profile(cred); | ||
| 616 | previous_profile = cxt->previous; | ||
| 617 | |||
| 618 | if (unconfined(profile)) { | ||
| 619 | info = "unconfined"; | ||
| 620 | error = -EPERM; | ||
| 621 | goto audit; | ||
| 622 | } | ||
| 623 | |||
| 624 | if (count) { | ||
| 625 | /* attempting to change into a new hat or switch to a sibling */ | ||
| 626 | struct aa_profile *root; | ||
| 627 | root = PROFILE_IS_HAT(profile) ? profile->parent : profile; | ||
| 628 | |||
| 629 | /* find first matching hat */ | ||
| 630 | for (i = 0; i < count && !hat; i++) | ||
| 631 | /* released below */ | ||
| 632 | hat = aa_find_child(root, hats[i]); | ||
| 633 | if (!hat) { | ||
| 634 | if (!COMPLAIN_MODE(root) || permtest) { | ||
| 635 | if (list_empty(&root->base.profiles)) | ||
| 636 | error = -ECHILD; | ||
| 637 | else | ||
| 638 | error = -ENOENT; | ||
| 639 | goto out; | ||
| 640 | } | ||
| 641 | |||
| 642 | /* | ||
| 643 | * In complain mode and failed to match any hats. | ||
| 644 | * Audit the failure is based off of the first hat | ||
| 645 | * supplied. This is done due how userspace | ||
| 646 | * interacts with change_hat. | ||
| 647 | * | ||
| 648 | * TODO: Add logging of all failed hats | ||
| 649 | */ | ||
| 650 | |||
| 651 | /* freed below */ | ||
| 652 | name = new_compound_name(root->base.hname, hats[0]); | ||
| 653 | target = name; | ||
| 654 | /* released below */ | ||
| 655 | hat = aa_new_null_profile(profile, 1); | ||
| 656 | if (!hat) { | ||
| 657 | info = "failed null profile create"; | ||
| 658 | error = -ENOMEM; | ||
| 659 | goto audit; | ||
| 660 | } | ||
| 661 | } else { | ||
| 662 | target = hat->base.hname; | ||
| 663 | if (!PROFILE_IS_HAT(hat)) { | ||
| 664 | info = "target not hat"; | ||
| 665 | error = -EPERM; | ||
| 666 | goto audit; | ||
| 667 | } | ||
| 668 | } | ||
| 669 | |||
| 670 | error = may_change_ptraced_domain(current, hat); | ||
| 671 | if (error) { | ||
| 672 | info = "ptraced"; | ||
| 673 | error = -EPERM; | ||
| 674 | goto audit; | ||
| 675 | } | ||
| 676 | |||
| 677 | if (!permtest) { | ||
| 678 | error = aa_set_current_hat(hat, token); | ||
| 679 | if (error == -EACCES) | ||
| 680 | /* kill task in case of brute force attacks */ | ||
| 681 | perms.kill = AA_MAY_CHANGEHAT; | ||
| 682 | else if (name && !error) | ||
| 683 | /* reset error for learning of new hats */ | ||
| 684 | error = -ENOENT; | ||
| 685 | } | ||
| 686 | } else if (previous_profile) { | ||
| 687 | /* Return to saved profile. Kill task if restore fails | ||
| 688 | * to avoid brute force attacks | ||
| 689 | */ | ||
| 690 | target = previous_profile->base.hname; | ||
| 691 | error = aa_restore_previous_profile(token); | ||
| 692 | perms.kill = AA_MAY_CHANGEHAT; | ||
| 693 | } else | ||
| 694 | /* ignore restores when there is no saved profile */ | ||
| 695 | goto out; | ||
| 696 | |||
| 697 | audit: | ||
| 698 | if (!permtest) | ||
| 699 | error = aa_audit_file(profile, &perms, GFP_KERNEL, | ||
| 700 | OP_CHANGE_HAT, AA_MAY_CHANGEHAT, NULL, | ||
| 701 | target, 0, info, error); | ||
| 702 | |||
| 703 | out: | ||
| 704 | aa_put_profile(hat); | ||
| 705 | kfree(name); | ||
| 706 | put_cred(cred); | ||
| 707 | |||
| 708 | return error; | ||
| 709 | } | ||
| 710 | |||
| 711 | /** | ||
| 712 | * aa_change_profile - perform a one-way profile transition | ||
| 713 | * @ns_name: name of the profile namespace to change to (MAYBE NULL) | ||
| 714 | * @hname: name of profile to change to (MAYBE NULL) | ||
| 715 | * @onexec: whether this transition is to take place immediately or at exec | ||
| 716 | * @permtest: true if this is just a permission test | ||
| 717 | * | ||
| 718 | * Change to new profile @name. Unlike with hats, there is no way | ||
| 719 | * to change back. If @name isn't specified the current profile name is | ||
| 720 | * used. | ||
| 721 | * If @onexec then the transition is delayed until | ||
| 722 | * the next exec. | ||
| 723 | * | ||
| 724 | * Returns %0 on success, error otherwise. | ||
| 725 | */ | ||
| 726 | int aa_change_profile(const char *ns_name, const char *hname, bool onexec, | ||
| 727 | bool permtest) | ||
| 728 | { | ||
| 729 | const struct cred *cred; | ||
| 730 | struct aa_task_cxt *cxt; | ||
| 731 | struct aa_profile *profile, *target = NULL; | ||
| 732 | struct aa_namespace *ns = NULL; | ||
| 733 | struct file_perms perms = {}; | ||
| 734 | const char *name = NULL, *info = NULL; | ||
| 735 | int op, error = 0; | ||
| 736 | u32 request; | ||
| 737 | |||
| 738 | if (!hname && !ns_name) | ||
| 739 | return -EINVAL; | ||
| 740 | |||
| 741 | if (onexec) { | ||
| 742 | request = AA_MAY_ONEXEC; | ||
| 743 | op = OP_CHANGE_ONEXEC; | ||
| 744 | } else { | ||
| 745 | request = AA_MAY_CHANGE_PROFILE; | ||
| 746 | op = OP_CHANGE_PROFILE; | ||
| 747 | } | ||
| 748 | |||
| 749 | cred = get_current_cred(); | ||
| 750 | cxt = cred->security; | ||
| 751 | profile = aa_cred_profile(cred); | ||
| 752 | |||
| 753 | if (ns_name) { | ||
| 754 | /* released below */ | ||
| 755 | ns = aa_find_namespace(profile->ns, ns_name); | ||
| 756 | if (!ns) { | ||
| 757 | /* we don't create new namespace in complain mode */ | ||
| 758 | name = ns_name; | ||
| 759 | info = "namespace not found"; | ||
| 760 | error = -ENOENT; | ||
| 761 | goto audit; | ||
| 762 | } | ||
| 763 | } else | ||
| 764 | /* released below */ | ||
| 765 | ns = aa_get_namespace(profile->ns); | ||
| 766 | |||
| 767 | /* if the name was not specified, use the name of the current profile */ | ||
| 768 | if (!hname) { | ||
| 769 | if (unconfined(profile)) | ||
| 770 | hname = ns->unconfined->base.hname; | ||
| 771 | else | ||
| 772 | hname = profile->base.hname; | ||
| 773 | } | ||
| 774 | |||
| 775 | perms = change_profile_perms(profile, ns, hname, request, | ||
| 776 | profile->file.start); | ||
| 777 | if (!(perms.allow & request)) { | ||
| 778 | error = -EACCES; | ||
| 779 | goto audit; | ||
| 780 | } | ||
| 781 | |||
| 782 | /* released below */ | ||
| 783 | target = aa_lookup_profile(ns, hname); | ||
| 784 | if (!target) { | ||
| 785 | info = "profile not found"; | ||
| 786 | error = -ENOENT; | ||
| 787 | if (permtest || !COMPLAIN_MODE(profile)) | ||
| 788 | goto audit; | ||
| 789 | /* released below */ | ||
| 790 | target = aa_new_null_profile(profile, 0); | ||
| 791 | if (!target) { | ||
| 792 | info = "failed null profile create"; | ||
| 793 | error = -ENOMEM; | ||
| 794 | goto audit; | ||
| 795 | } | ||
| 796 | } | ||
| 797 | |||
| 798 | /* check if tracing task is allowed to trace target domain */ | ||
| 799 | error = may_change_ptraced_domain(current, target); | ||
| 800 | if (error) { | ||
| 801 | info = "ptrace prevents transition"; | ||
| 802 | goto audit; | ||
| 803 | } | ||
| 804 | |||
| 805 | if (permtest) | ||
| 806 | goto audit; | ||
| 807 | |||
| 808 | if (onexec) | ||
| 809 | error = aa_set_current_onexec(target); | ||
| 810 | else | ||
| 811 | error = aa_replace_current_profile(target); | ||
| 812 | |||
| 813 | audit: | ||
| 814 | if (!permtest) | ||
| 815 | error = aa_audit_file(profile, &perms, GFP_KERNEL, op, request, | ||
| 816 | name, hname, 0, info, error); | ||
| 817 | |||
| 818 | aa_put_namespace(ns); | ||
| 819 | aa_put_profile(target); | ||
| 820 | put_cred(cred); | ||
| 821 | |||
| 822 | return error; | ||
| 823 | } | ||
diff --git a/security/apparmor/file.c b/security/apparmor/file.c new file mode 100644 index 000000000000..7312db741219 --- /dev/null +++ b/security/apparmor/file.c | |||
| @@ -0,0 +1,457 @@ | |||
| 1 | /* | ||
| 2 | * AppArmor security module | ||
| 3 | * | ||
| 4 | * This file contains AppArmor mediation of files | ||
| 5 | * | ||
| 6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
| 7 | * Copyright 2009-2010 Canonical Ltd. | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or | ||
| 10 | * modify it under the terms of the GNU General Public License as | ||
| 11 | * published by the Free Software Foundation, version 2 of the | ||
| 12 | * License. | ||
| 13 | */ | ||
| 14 | |||
| 15 | #include "include/apparmor.h" | ||
| 16 | #include "include/audit.h" | ||
| 17 | #include "include/file.h" | ||
| 18 | #include "include/match.h" | ||
| 19 | #include "include/path.h" | ||
| 20 | #include "include/policy.h" | ||
| 21 | |||
| 22 | struct file_perms nullperms; | ||
| 23 | |||
| 24 | |||
| 25 | /** | ||
| 26 | * audit_file_mask - convert mask to permission string | ||
| 27 | * @buffer: buffer to write string to (NOT NULL) | ||
| 28 | * @mask: permission mask to convert | ||
| 29 | */ | ||
| 30 | static void audit_file_mask(struct audit_buffer *ab, u32 mask) | ||
| 31 | { | ||
| 32 | char str[10]; | ||
| 33 | |||
| 34 | char *m = str; | ||
| 35 | |||
| 36 | if (mask & AA_EXEC_MMAP) | ||
| 37 | *m++ = 'm'; | ||
| 38 | if (mask & (MAY_READ | AA_MAY_META_READ)) | ||
| 39 | *m++ = 'r'; | ||
| 40 | if (mask & (MAY_WRITE | AA_MAY_META_WRITE | AA_MAY_CHMOD | | ||
| 41 | AA_MAY_CHOWN)) | ||
| 42 | *m++ = 'w'; | ||
| 43 | else if (mask & MAY_APPEND) | ||
| 44 | *m++ = 'a'; | ||
| 45 | if (mask & AA_MAY_CREATE) | ||
| 46 | *m++ = 'c'; | ||
| 47 | if (mask & AA_MAY_DELETE) | ||
| 48 | *m++ = 'd'; | ||
| 49 | if (mask & AA_MAY_LINK) | ||
| 50 | *m++ = 'l'; | ||
| 51 | if (mask & AA_MAY_LOCK) | ||
| 52 | *m++ = 'k'; | ||
| 53 | if (mask & MAY_EXEC) | ||
| 54 | *m++ = 'x'; | ||
| 55 | *m = '\0'; | ||
| 56 | |||
| 57 | audit_log_string(ab, str); | ||
| 58 | } | ||
| 59 | |||
| 60 | /** | ||
| 61 | * file_audit_cb - call back for file specific audit fields | ||
| 62 | * @ab: audit_buffer (NOT NULL) | ||
| 63 | * @va: audit struct to audit values of (NOT NULL) | ||
| 64 | */ | ||
| 65 | static void file_audit_cb(struct audit_buffer *ab, void *va) | ||
| 66 | { | ||
| 67 | struct common_audit_data *sa = va; | ||
| 68 | uid_t fsuid = current_fsuid(); | ||
| 69 | |||
| 70 | if (sa->aad.fs.request & AA_AUDIT_FILE_MASK) { | ||
| 71 | audit_log_format(ab, " requested_mask="); | ||
| 72 | audit_file_mask(ab, sa->aad.fs.request); | ||
| 73 | } | ||
| 74 | if (sa->aad.fs.denied & AA_AUDIT_FILE_MASK) { | ||
| 75 | audit_log_format(ab, " denied_mask="); | ||
| 76 | audit_file_mask(ab, sa->aad.fs.denied); | ||
| 77 | } | ||
| 78 | if (sa->aad.fs.request & AA_AUDIT_FILE_MASK) { | ||
| 79 | audit_log_format(ab, " fsuid=%d", fsuid); | ||
| 80 | audit_log_format(ab, " ouid=%d", sa->aad.fs.ouid); | ||
| 81 | } | ||
| 82 | |||
| 83 | if (sa->aad.fs.target) { | ||
| 84 | audit_log_format(ab, " target="); | ||
| 85 | audit_log_untrustedstring(ab, sa->aad.fs.target); | ||
| 86 | } | ||
| 87 | } | ||
| 88 | |||
| 89 | /** | ||
| 90 | * aa_audit_file - handle the auditing of file operations | ||
| 91 | * @profile: the profile being enforced (NOT NULL) | ||
| 92 | * @perms: the permissions computed for the request (NOT NULL) | ||
| 93 | * @gfp: allocation flags | ||
| 94 | * @op: operation being mediated | ||
| 95 | * @request: permissions requested | ||
| 96 | * @name: name of object being mediated (MAYBE NULL) | ||
| 97 | * @target: name of target (MAYBE NULL) | ||
| 98 | * @ouid: object uid | ||
| 99 | * @info: extra information message (MAYBE NULL) | ||
| 100 | * @error: 0 if operation allowed else failure error code | ||
| 101 | * | ||
| 102 | * Returns: %0 or error on failure | ||
| 103 | */ | ||
| 104 | int aa_audit_file(struct aa_profile *profile, struct file_perms *perms, | ||
| 105 | gfp_t gfp, int op, u32 request, const char *name, | ||
| 106 | const char *target, uid_t ouid, const char *info, int error) | ||
| 107 | { | ||
| 108 | int type = AUDIT_APPARMOR_AUTO; | ||
| 109 | struct common_audit_data sa; | ||
| 110 | COMMON_AUDIT_DATA_INIT(&sa, NONE); | ||
| 111 | sa.aad.op = op, | ||
| 112 | sa.aad.fs.request = request; | ||
| 113 | sa.aad.name = name; | ||
| 114 | sa.aad.fs.target = target; | ||
| 115 | sa.aad.fs.ouid = ouid; | ||
| 116 | sa.aad.info = info; | ||
| 117 | sa.aad.error = error; | ||
| 118 | |||
| 119 | if (likely(!sa.aad.error)) { | ||
| 120 | u32 mask = perms->audit; | ||
| 121 | |||
| 122 | if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL)) | ||
| 123 | mask = 0xffff; | ||
| 124 | |||
| 125 | /* mask off perms that are not being force audited */ | ||
| 126 | sa.aad.fs.request &= mask; | ||
| 127 | |||
| 128 | if (likely(!sa.aad.fs.request)) | ||
| 129 | return 0; | ||
| 130 | type = AUDIT_APPARMOR_AUDIT; | ||
| 131 | } else { | ||
| 132 | /* only report permissions that were denied */ | ||
| 133 | sa.aad.fs.request = sa.aad.fs.request & ~perms->allow; | ||
| 134 | |||
| 135 | if (sa.aad.fs.request & perms->kill) | ||
| 136 | type = AUDIT_APPARMOR_KILL; | ||
| 137 | |||
| 138 | /* quiet known rejects, assumes quiet and kill do not overlap */ | ||
| 139 | if ((sa.aad.fs.request & perms->quiet) && | ||
| 140 | AUDIT_MODE(profile) != AUDIT_NOQUIET && | ||
| 141 | AUDIT_MODE(profile) != AUDIT_ALL) | ||
| 142 | sa.aad.fs.request &= ~perms->quiet; | ||
| 143 | |||
| 144 | if (!sa.aad.fs.request) | ||
| 145 | return COMPLAIN_MODE(profile) ? 0 : sa.aad.error; | ||
| 146 | } | ||
| 147 | |||
| 148 | sa.aad.fs.denied = sa.aad.fs.request & ~perms->allow; | ||
| 149 | return aa_audit(type, profile, gfp, &sa, file_audit_cb); | ||
| 150 | } | ||
| 151 | |||
| 152 | /** | ||
| 153 | * map_old_perms - map old file perms layout to the new layout | ||
| 154 | * @old: permission set in old mapping | ||
| 155 | * | ||
| 156 | * Returns: new permission mapping | ||
| 157 | */ | ||
| 158 | static u32 map_old_perms(u32 old) | ||
| 159 | { | ||
| 160 | u32 new = old & 0xf; | ||
| 161 | if (old & MAY_READ) | ||
| 162 | new |= AA_MAY_META_READ; | ||
| 163 | if (old & MAY_WRITE) | ||
| 164 | new |= AA_MAY_META_WRITE | AA_MAY_CREATE | AA_MAY_DELETE | | ||
| 165 | AA_MAY_CHMOD | AA_MAY_CHOWN; | ||
| 166 | if (old & 0x10) | ||
| 167 | new |= AA_MAY_LINK; | ||
| 168 | /* the old mapping lock and link_subset flags where overlaid | ||
| 169 | * and use was determined by part of a pair that they were in | ||
| 170 | */ | ||
| 171 | if (old & 0x20) | ||
| 172 | new |= AA_MAY_LOCK | AA_LINK_SUBSET; | ||
| 173 | if (old & 0x40) /* AA_EXEC_MMAP */ | ||
| 174 | new |= AA_EXEC_MMAP; | ||
| 175 | |||
| 176 | new |= AA_MAY_META_READ; | ||
| 177 | |||
| 178 | return new; | ||
| 179 | } | ||
| 180 | |||
| 181 | /** | ||
| 182 | * compute_perms - convert dfa compressed perms to internal perms | ||
| 183 | * @dfa: dfa to compute perms for (NOT NULL) | ||
| 184 | * @state: state in dfa | ||
| 185 | * @cond: conditions to consider (NOT NULL) | ||
| 186 | * | ||
| 187 | * TODO: convert from dfa + state to permission entry, do computation conversion | ||
| 188 | * at load time. | ||
| 189 | * | ||
| 190 | * Returns: computed permission set | ||
| 191 | */ | ||
| 192 | static struct file_perms compute_perms(struct aa_dfa *dfa, unsigned int state, | ||
| 193 | struct path_cond *cond) | ||
| 194 | { | ||
| 195 | struct file_perms perms; | ||
| 196 | |||
| 197 | /* FIXME: change over to new dfa format | ||
| 198 | * currently file perms are encoded in the dfa, new format | ||
| 199 | * splits the permissions from the dfa. This mapping can be | ||
| 200 | * done at profile load | ||
| 201 | */ | ||
| 202 | perms.kill = 0; | ||
| 203 | |||
| 204 | if (current_fsuid() == cond->uid) { | ||
| 205 | perms.allow = map_old_perms(dfa_user_allow(dfa, state)); | ||
| 206 | perms.audit = map_old_perms(dfa_user_audit(dfa, state)); | ||
| 207 | perms.quiet = map_old_perms(dfa_user_quiet(dfa, state)); | ||
| 208 | perms.xindex = dfa_user_xindex(dfa, state); | ||
| 209 | } else { | ||
| 210 | perms.allow = map_old_perms(dfa_other_allow(dfa, state)); | ||
| 211 | perms.audit = map_old_perms(dfa_other_audit(dfa, state)); | ||
| 212 | perms.quiet = map_old_perms(dfa_other_quiet(dfa, state)); | ||
| 213 | perms.xindex = dfa_other_xindex(dfa, state); | ||
| 214 | } | ||
| 215 | |||
| 216 | /* change_profile wasn't determined by ownership in old mapping */ | ||
| 217 | if (ACCEPT_TABLE(dfa)[state] & 0x80000000) | ||
| 218 | perms.allow |= AA_MAY_CHANGE_PROFILE; | ||
| 219 | |||
| 220 | return perms; | ||
| 221 | } | ||
| 222 | |||
| 223 | /** | ||
| 224 | * aa_str_perms - find permission that match @name | ||
| 225 | * @dfa: to match against (MAYBE NULL) | ||
| 226 | * @state: state to start matching in | ||
| 227 | * @name: string to match against dfa (NOT NULL) | ||
| 228 | * @cond: conditions to consider for permission set computation (NOT NULL) | ||
| 229 | * @perms: Returns - the permissions found when matching @name | ||
| 230 | * | ||
| 231 | * Returns: the final state in @dfa when beginning @start and walking @name | ||
| 232 | */ | ||
| 233 | unsigned int aa_str_perms(struct aa_dfa *dfa, unsigned int start, | ||
| 234 | const char *name, struct path_cond *cond, | ||
| 235 | struct file_perms *perms) | ||
| 236 | { | ||
| 237 | unsigned int state; | ||
| 238 | if (!dfa) { | ||
| 239 | *perms = nullperms; | ||
| 240 | return DFA_NOMATCH; | ||
| 241 | } | ||
| 242 | |||
| 243 | state = aa_dfa_match(dfa, start, name); | ||
| 244 | *perms = compute_perms(dfa, state, cond); | ||
| 245 | |||
| 246 | return state; | ||
| 247 | } | ||
| 248 | |||
| 249 | /** | ||
| 250 | * is_deleted - test if a file has been completely unlinked | ||
| 251 | * @dentry: dentry of file to test for deletion (NOT NULL) | ||
| 252 | * | ||
| 253 | * Returns: %1 if deleted else %0 | ||
| 254 | */ | ||
| 255 | static inline bool is_deleted(struct dentry *dentry) | ||
| 256 | { | ||
| 257 | if (d_unlinked(dentry) && dentry->d_inode->i_nlink == 0) | ||
| 258 | return 1; | ||
| 259 | return 0; | ||
| 260 | } | ||
| 261 | |||
| 262 | /** | ||
| 263 | * aa_path_perm - do permissions check & audit for @path | ||
| 264 | * @op: operation being checked | ||
| 265 | * @profile: profile being enforced (NOT NULL) | ||
| 266 | * @path: path to check permissions of (NOT NULL) | ||
| 267 | * @flags: any additional path flags beyond what the profile specifies | ||
| 268 | * @request: requested permissions | ||
| 269 | * @cond: conditional info for this request (NOT NULL) | ||
| 270 | * | ||
| 271 | * Returns: %0 else error if access denied or other error | ||
| 272 | */ | ||
| 273 | int aa_path_perm(int op, struct aa_profile *profile, struct path *path, | ||
| 274 | int flags, u32 request, struct path_cond *cond) | ||
| 275 | { | ||
| 276 | char *buffer = NULL; | ||
| 277 | struct file_perms perms = {}; | ||
| 278 | const char *name, *info = NULL; | ||
| 279 | int error; | ||
| 280 | |||
| 281 | flags |= profile->path_flags | (S_ISDIR(cond->mode) ? PATH_IS_DIR : 0); | ||
| 282 | error = aa_get_name(path, flags, &buffer, &name); | ||
| 283 | if (error) { | ||
| 284 | if (error == -ENOENT && is_deleted(path->dentry)) { | ||
| 285 | /* Access to open files that are deleted are | ||
| 286 | * give a pass (implicit delegation) | ||
| 287 | */ | ||
| 288 | error = 0; | ||
| 289 | perms.allow = request; | ||
| 290 | } else if (error == -ENOENT) | ||
| 291 | info = "Failed name lookup - deleted entry"; | ||
| 292 | else if (error == -ESTALE) | ||
| 293 | info = "Failed name lookup - disconnected path"; | ||
| 294 | else if (error == -ENAMETOOLONG) | ||
| 295 | info = "Failed name lookup - name too long"; | ||
| 296 | else | ||
| 297 | info = "Failed name lookup"; | ||
| 298 | } else { | ||
| 299 | aa_str_perms(profile->file.dfa, profile->file.start, name, cond, | ||
| 300 | &perms); | ||
| 301 | if (request & ~perms.allow) | ||
| 302 | error = -EACCES; | ||
| 303 | } | ||
| 304 | error = aa_audit_file(profile, &perms, GFP_KERNEL, op, request, name, | ||
| 305 | NULL, cond->uid, info, error); | ||
| 306 | kfree(buffer); | ||
| 307 | |||
| 308 | return error; | ||
| 309 | } | ||
| 310 | |||
| 311 | /** | ||
| 312 | * xindex_is_subset - helper for aa_path_link | ||
| 313 | * @link: link permission set | ||
| 314 | * @target: target permission set | ||
| 315 | * | ||
| 316 | * test target x permissions are equal OR a subset of link x permissions | ||
| 317 | * this is done as part of the subset test, where a hardlink must have | ||
| 318 | * a subset of permissions that the target has. | ||
| 319 | * | ||
| 320 | * Returns: %1 if subset else %0 | ||
| 321 | */ | ||
| 322 | static inline bool xindex_is_subset(u32 link, u32 target) | ||
| 323 | { | ||
| 324 | if (((link & ~AA_X_UNSAFE) != (target & ~AA_X_UNSAFE)) || | ||
| 325 | ((link & AA_X_UNSAFE) && !(target & AA_X_UNSAFE))) | ||
| 326 | return 0; | ||
| 327 | |||
| 328 | return 1; | ||
| 329 | } | ||
| 330 | |||
| 331 | /** | ||
| 332 | * aa_path_link - Handle hard link permission check | ||
| 333 | * @profile: the profile being enforced (NOT NULL) | ||
| 334 | * @old_dentry: the target dentry (NOT NULL) | ||
| 335 | * @new_dir: directory the new link will be created in (NOT NULL) | ||
| 336 | * @new_dentry: the link being created (NOT NULL) | ||
| 337 | * | ||
| 338 | * Handle the permission test for a link & target pair. Permission | ||
| 339 | * is encoded as a pair where the link permission is determined | ||
| 340 | * first, and if allowed, the target is tested. The target test | ||
| 341 | * is done from the point of the link match (not start of DFA) | ||
| 342 | * making the target permission dependent on the link permission match. | ||
| 343 | * | ||
| 344 | * The subset test if required forces that permissions granted | ||
| 345 | * on link are a subset of the permission granted to target. | ||
| 346 | * | ||
| 347 | * Returns: %0 if allowed else error | ||
| 348 | */ | ||
| 349 | int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry, | ||
| 350 | struct path *new_dir, struct dentry *new_dentry) | ||
| 351 | { | ||
| 352 | struct path link = { new_dir->mnt, new_dentry }; | ||
| 353 | struct path target = { new_dir->mnt, old_dentry }; | ||
| 354 | struct path_cond cond = { | ||
| 355 | old_dentry->d_inode->i_uid, | ||
| 356 | old_dentry->d_inode->i_mode | ||
| 357 | }; | ||
| 358 | char *buffer = NULL, *buffer2 = NULL; | ||
| 359 | const char *lname, *tname = NULL, *info = NULL; | ||
| 360 | struct file_perms lperms, perms; | ||
| 361 | u32 request = AA_MAY_LINK; | ||
| 362 | unsigned int state; | ||
| 363 | int error; | ||
| 364 | |||
| 365 | lperms = nullperms; | ||
| 366 | |||
| 367 | /* buffer freed below, lname is pointer in buffer */ | ||
| 368 | error = aa_get_name(&link, profile->path_flags, &buffer, &lname); | ||
| 369 | if (error) | ||
| 370 | goto audit; | ||
| 371 | |||
| 372 | /* buffer2 freed below, tname is pointer in buffer2 */ | ||
| 373 | error = aa_get_name(&target, profile->path_flags, &buffer2, &tname); | ||
| 374 | if (error) | ||
| 375 | goto audit; | ||
| 376 | |||
| 377 | error = -EACCES; | ||
| 378 | /* aa_str_perms - handles the case of the dfa being NULL */ | ||
| 379 | state = aa_str_perms(profile->file.dfa, profile->file.start, lname, | ||
| 380 | &cond, &lperms); | ||
| 381 | |||
| 382 | if (!(lperms.allow & AA_MAY_LINK)) | ||
| 383 | goto audit; | ||
| 384 | |||
| 385 | /* test to see if target can be paired with link */ | ||
| 386 | state = aa_dfa_null_transition(profile->file.dfa, state); | ||
| 387 | aa_str_perms(profile->file.dfa, state, tname, &cond, &perms); | ||
| 388 | |||
| 389 | /* force audit/quiet masks for link are stored in the second entry | ||
| 390 | * in the link pair. | ||
| 391 | */ | ||
| 392 | lperms.audit = perms.audit; | ||
| 393 | lperms.quiet = perms.quiet; | ||
| 394 | lperms.kill = perms.kill; | ||
| 395 | |||
| 396 | if (!(perms.allow & AA_MAY_LINK)) { | ||
| 397 | info = "target restricted"; | ||
| 398 | goto audit; | ||
| 399 | } | ||
| 400 | |||
| 401 | /* done if link subset test is not required */ | ||
| 402 | if (!(perms.allow & AA_LINK_SUBSET)) | ||
| 403 | goto done_tests; | ||
| 404 | |||
| 405 | /* Do link perm subset test requiring allowed permission on link are a | ||
| 406 | * subset of the allowed permissions on target. | ||
| 407 | */ | ||
| 408 | aa_str_perms(profile->file.dfa, profile->file.start, tname, &cond, | ||
| 409 | &perms); | ||
| 410 | |||
| 411 | /* AA_MAY_LINK is not considered in the subset test */ | ||
| 412 | request = lperms.allow & ~AA_MAY_LINK; | ||
| 413 | lperms.allow &= perms.allow | AA_MAY_LINK; | ||
| 414 | |||
| 415 | request |= AA_AUDIT_FILE_MASK & (lperms.allow & ~perms.allow); | ||
| 416 | if (request & ~lperms.allow) { | ||
| 417 | goto audit; | ||
| 418 | } else if ((lperms.allow & MAY_EXEC) && | ||
| 419 | !xindex_is_subset(lperms.xindex, perms.xindex)) { | ||
| 420 | lperms.allow &= ~MAY_EXEC; | ||
| 421 | request |= MAY_EXEC; | ||
| 422 | info = "link not subset of target"; | ||
| 423 | goto audit; | ||
| 424 | } | ||
| 425 | |||
| 426 | done_tests: | ||
| 427 | error = 0; | ||
| 428 | |||
| 429 | audit: | ||
| 430 | error = aa_audit_file(profile, &lperms, GFP_KERNEL, OP_LINK, request, | ||
| 431 | lname, tname, cond.uid, info, error); | ||
| 432 | kfree(buffer); | ||
| 433 | kfree(buffer2); | ||
| 434 | |||
| 435 | return error; | ||
| 436 | } | ||
| 437 | |||
| 438 | /** | ||
| 439 | * aa_file_perm - do permission revalidation check & audit for @file | ||
| 440 | * @op: operation being checked | ||
| 441 | * @profile: profile being enforced (NOT NULL) | ||
| 442 | * @file: file to revalidate access permissions on (NOT NULL) | ||
| 443 | * @request: requested permissions | ||
| 444 | * | ||
| 445 | * Returns: %0 if access allowed else error | ||
| 446 | */ | ||
| 447 | int aa_file_perm(int op, struct aa_profile *profile, struct file *file, | ||
| 448 | u32 request) | ||
| 449 | { | ||
| 450 | struct path_cond cond = { | ||
| 451 | .uid = file->f_path.dentry->d_inode->i_uid, | ||
| 452 | .mode = file->f_path.dentry->d_inode->i_mode | ||
| 453 | }; | ||
| 454 | |||
| 455 | return aa_path_perm(op, profile, &file->f_path, PATH_DELEGATE_DELETED, | ||
| 456 | request, &cond); | ||
| 457 | } | ||
diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h new file mode 100644 index 000000000000..38ccaea08204 --- /dev/null +++ b/security/apparmor/include/apparmor.h | |||
| @@ -0,0 +1,92 @@ | |||
| 1 | /* | ||
| 2 | * AppArmor security module | ||
| 3 | * | ||
| 4 | * This file contains AppArmor basic global and lib definitions | ||
| 5 | * | ||
| 6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
| 7 | * Copyright 2009-2010 Canonical Ltd. | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or | ||
| 10 | * modify it under the terms of the GNU General Public License as | ||
| 11 | * published by the Free Software Foundation, version 2 of the | ||
| 12 | * License. | ||
| 13 | */ | ||
| 14 | |||
| 15 | #ifndef __APPARMOR_H | ||
| 16 | #define __APPARMOR_H | ||
| 17 | |||
| 18 | #include <linux/fs.h> | ||
| 19 | |||
| 20 | #include "match.h" | ||
| 21 | |||
| 22 | /* Control parameters settable through module/boot flags */ | ||
| 23 | extern enum audit_mode aa_g_audit; | ||
| 24 | extern int aa_g_audit_header; | ||
| 25 | extern int aa_g_debug; | ||
| 26 | extern int aa_g_lock_policy; | ||
| 27 | extern int aa_g_logsyscall; | ||
| 28 | extern int aa_g_paranoid_load; | ||
| 29 | extern unsigned int aa_g_path_max; | ||
| 30 | |||
| 31 | /* | ||
| 32 | * DEBUG remains global (no per profile flag) since it is mostly used in sysctl | ||
| 33 | * which is not related to profile accesses. | ||
| 34 | */ | ||
| 35 | |||
| 36 | #define AA_DEBUG(fmt, args...) \ | ||
| 37 | do { \ | ||
| 38 | if (aa_g_debug && printk_ratelimit()) \ | ||
| 39 | printk(KERN_DEBUG "AppArmor: " fmt, ##args); \ | ||
| 40 | } while (0) | ||
| 41 | |||
| 42 | #define AA_ERROR(fmt, args...) \ | ||
| 43 | do { \ | ||
| 44 | if (printk_ratelimit()) \ | ||
| 45 | printk(KERN_ERR "AppArmor: " fmt, ##args); \ | ||
| 46 | } while (0) | ||
| 47 | |||
| 48 | /* Flag indicating whether initialization completed */ | ||
| 49 | extern int apparmor_initialized __initdata; | ||
| 50 | |||
| 51 | /* fn's in lib */ | ||
| 52 | char *aa_split_fqname(char *args, char **ns_name); | ||
| 53 | void aa_info_message(const char *str); | ||
| 54 | void *kvmalloc(size_t size); | ||
| 55 | void kvfree(void *buffer); | ||
| 56 | |||
| 57 | |||
| 58 | /** | ||
| 59 | * aa_strneq - compare null terminated @str to a non null terminated substring | ||
| 60 | * @str: a null terminated string | ||
| 61 | * @sub: a substring, not necessarily null terminated | ||
| 62 | * @len: length of @sub to compare | ||
| 63 | * | ||
| 64 | * The @str string must be full consumed for this to be considered a match | ||
| 65 | */ | ||
| 66 | static inline bool aa_strneq(const char *str, const char *sub, int len) | ||
| 67 | { | ||
| 68 | return !strncmp(str, sub, len) && !str[len]; | ||
| 69 | } | ||
| 70 | |||
| 71 | /** | ||
| 72 | * aa_dfa_null_transition - step to next state after null character | ||
| 73 | * @dfa: the dfa to match against | ||
| 74 | * @start: the state of the dfa to start matching in | ||
| 75 | * | ||
| 76 | * aa_dfa_null_transition transitions to the next state after a null | ||
| 77 | * character which is not used in standard matching and is only | ||
| 78 | * used to separate pairs. | ||
| 79 | */ | ||
| 80 | static inline unsigned int aa_dfa_null_transition(struct aa_dfa *dfa, | ||
| 81 | unsigned int start) | ||
| 82 | { | ||
| 83 | /* the null transition only needs the string's null terminator byte */ | ||
| 84 | return aa_dfa_match_len(dfa, start, "", 1); | ||
| 85 | } | ||
| 86 | |||
| 87 | static inline bool mediated_filesystem(struct inode *inode) | ||
| 88 | { | ||
| 89 | return !(inode->i_sb->s_flags & MS_NOUSER); | ||
| 90 | } | ||
| 91 | |||
| 92 | #endif /* __APPARMOR_H */ | ||
diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h new file mode 100644 index 000000000000..cb1e93a114d7 --- /dev/null +++ b/security/apparmor/include/apparmorfs.h | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | /* | ||
| 2 | * AppArmor security module | ||
| 3 | * | ||
| 4 | * This file contains AppArmor filesystem definitions. | ||
| 5 | * | ||
| 6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
| 7 | * Copyright 2009-2010 Canonical Ltd. | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or | ||
| 10 | * modify it under the terms of the GNU General Public License as | ||
| 11 | * published by the Free Software Foundation, version 2 of the | ||
| 12 | * License. | ||
| 13 | */ | ||
| 14 | |||
| 15 | #ifndef __AA_APPARMORFS_H | ||
| 16 | #define __AA_APPARMORFS_H | ||
| 17 | |||
| 18 | extern void __init aa_destroy_aafs(void); | ||
| 19 | |||
| 20 | #endif /* __AA_APPARMORFS_H */ | ||
diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h new file mode 100644 index 000000000000..1951786d32e9 --- /dev/null +++ b/security/apparmor/include/audit.h | |||
| @@ -0,0 +1,123 @@ | |||
| 1 | /* | ||
| 2 | * AppArmor security module | ||
| 3 | * | ||
| 4 | * This file contains AppArmor auditing function definitions. | ||
| 5 | * | ||
| 6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
| 7 | * Copyright 2009-2010 Canonical Ltd. | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or | ||
| 10 | * modify it under the terms of the GNU General Public License as | ||
| 11 | * published by the Free Software Foundation, version 2 of the | ||
| 12 | * License. | ||
| 13 | */ | ||
| 14 | |||
| 15 | #ifndef __AA_AUDIT_H | ||
| 16 | #define __AA_AUDIT_H | ||
| 17 | |||
| 18 | #include <linux/audit.h> | ||
| 19 | #include <linux/fs.h> | ||
| 20 | #include <linux/lsm_audit.h> | ||
| 21 | #include <linux/sched.h> | ||
| 22 | #include <linux/slab.h> | ||
| 23 | |||
| 24 | #include "file.h" | ||
| 25 | |||
| 26 | struct aa_profile; | ||
| 27 | |||
| 28 | extern const char *audit_mode_names[]; | ||
| 29 | #define AUDIT_MAX_INDEX 5 | ||
| 30 | |||
| 31 | #define AUDIT_APPARMOR_AUTO 0 /* auto choose audit message type */ | ||
| 32 | |||
| 33 | enum audit_mode { | ||
| 34 | AUDIT_NORMAL, /* follow normal auditing of accesses */ | ||
| 35 | AUDIT_QUIET_DENIED, /* quiet all denied access messages */ | ||
| 36 | AUDIT_QUIET, /* quiet all messages */ | ||
| 37 | AUDIT_NOQUIET, /* do not quiet audit messages */ | ||
| 38 | AUDIT_ALL /* audit all accesses */ | ||
| 39 | }; | ||
| 40 | |||
| 41 | enum audit_type { | ||
| 42 | AUDIT_APPARMOR_AUDIT, | ||
| 43 | AUDIT_APPARMOR_ALLOWED, | ||
| 44 | AUDIT_APPARMOR_DENIED, | ||
| 45 | AUDIT_APPARMOR_HINT, | ||
| 46 | AUDIT_APPARMOR_STATUS, | ||
| 47 | AUDIT_APPARMOR_ERROR, | ||
| 48 | AUDIT_APPARMOR_KILL | ||
| 49 | }; | ||
| 50 | |||
| 51 | extern const char *op_table[]; | ||
| 52 | enum aa_ops { | ||
| 53 | OP_NULL, | ||
| 54 | |||
| 55 | OP_SYSCTL, | ||
| 56 | OP_CAPABLE, | ||
| 57 | |||
| 58 | OP_UNLINK, | ||
| 59 | OP_MKDIR, | ||
| 60 | OP_RMDIR, | ||
| 61 | OP_MKNOD, | ||
| 62 | OP_TRUNC, | ||
| 63 | OP_LINK, | ||
| 64 | OP_SYMLINK, | ||
| 65 | OP_RENAME_SRC, | ||
| 66 | OP_RENAME_DEST, | ||
| 67 | OP_CHMOD, | ||
| 68 | OP_CHOWN, | ||
| 69 | OP_GETATTR, | ||
| 70 | OP_OPEN, | ||
| 71 | |||
| 72 | OP_FPERM, | ||
| 73 | OP_FLOCK, | ||
| 74 | OP_FMMAP, | ||
| 75 | OP_FMPROT, | ||
| 76 | |||
| 77 | OP_CREATE, | ||
| 78 | OP_POST_CREATE, | ||
| 79 | OP_BIND, | ||
| 80 | OP_CONNECT, | ||
| 81 | OP_LISTEN, | ||
| 82 | OP_ACCEPT, | ||
| 83 | OP_SENDMSG, | ||
| 84 | OP_RECVMSG, | ||
| 85 | OP_GETSOCKNAME, | ||
| 86 | OP_GETPEERNAME, | ||
| 87 | OP_GETSOCKOPT, | ||
| 88 | OP_SETSOCKOPT, | ||
| 89 | OP_SOCK_SHUTDOWN, | ||
| 90 | |||
| 91 | OP_PTRACE, | ||
| 92 | |||
| 93 | OP_EXEC, | ||
| 94 | OP_CHANGE_HAT, | ||
| 95 | OP_CHANGE_PROFILE, | ||
| 96 | OP_CHANGE_ONEXEC, | ||
| 97 | |||
| 98 | OP_SETPROCATTR, | ||
| 99 | OP_SETRLIMIT, | ||
| 100 | |||
| 101 | OP_PROF_REPL, | ||
| 102 | OP_PROF_LOAD, | ||
| 103 | OP_PROF_RM, | ||
| 104 | }; | ||
| 105 | |||
| 106 | |||
| 107 | /* define a short hand for apparmor_audit_data portion of common_audit_data */ | ||
| 108 | #define aad apparmor_audit_data | ||
| 109 | |||
| 110 | void aa_audit_msg(int type, struct common_audit_data *sa, | ||
| 111 | void (*cb) (struct audit_buffer *, void *)); | ||
| 112 | int aa_audit(int type, struct aa_profile *profile, gfp_t gfp, | ||
| 113 | struct common_audit_data *sa, | ||
| 114 | void (*cb) (struct audit_buffer *, void *)); | ||
| 115 | |||
| 116 | static inline int complain_error(int error) | ||
| 117 | { | ||
| 118 | if (error == -EPERM || error == -EACCES) | ||
| 119 | return 0; | ||
| 120 | return error; | ||
| 121 | } | ||
| 122 | |||
| 123 | #endif /* __AA_AUDIT_H */ | ||
diff --git a/security/apparmor/include/capability.h b/security/apparmor/include/capability.h new file mode 100644 index 000000000000..c24d2959ea02 --- /dev/null +++ b/security/apparmor/include/capability.h | |||
| @@ -0,0 +1,45 @@ | |||
| 1 | /* | ||
| 2 | * AppArmor security module | ||
| 3 | * | ||
| 4 | * This file contains AppArmor capability mediation definitions. | ||
| 5 | * | ||
| 6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
| 7 | * Copyright 2009-2010 Canonical Ltd. | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or | ||
| 10 | * modify it under the terms of the GNU General Public License as | ||
| 11 | * published by the Free Software Foundation, version 2 of the | ||
| 12 | * License. | ||
| 13 | */ | ||
| 14 | |||
| 15 | #ifndef __AA_CAPABILITY_H | ||
| 16 | #define __AA_CAPABILITY_H | ||
| 17 | |||
| 18 | #include <linux/sched.h> | ||
| 19 | |||
| 20 | struct aa_profile; | ||
| 21 | |||
| 22 | /* aa_caps - confinement data for capabilities | ||
| 23 | * @allowed: capabilities mask | ||
| 24 | * @audit: caps that are to be audited | ||
| 25 | * @quiet: caps that should not be audited | ||
| 26 | * @kill: caps that when requested will result in the task being killed | ||
| 27 | * @extended: caps that are subject finer grained mediation | ||
| 28 | */ | ||
| 29 | struct aa_caps { | ||
| 30 | kernel_cap_t allow; | ||
| 31 | kernel_cap_t audit; | ||
| 32 | kernel_cap_t quiet; | ||
| 33 | kernel_cap_t kill; | ||
| 34 | kernel_cap_t extended; | ||
| 35 | }; | ||
| 36 | |||
| 37 | int aa_capable(struct task_struct *task, struct aa_profile *profile, int cap, | ||
| 38 | int audit); | ||
| 39 | |||
| 40 | static inline void aa_free_cap_rules(struct aa_caps *caps) | ||
| 41 | { | ||
| 42 | /* NOP */ | ||
| 43 | } | ||
| 44 | |||
| 45 | #endif /* __AA_CAPBILITY_H */ | ||
diff --git a/security/apparmor/include/context.h b/security/apparmor/include/context.h new file mode 100644 index 000000000000..a9cbee4d9e48 --- /dev/null +++ b/security/apparmor/include/context.h | |||
| @@ -0,0 +1,154 @@ | |||
| 1 | /* | ||
| 2 | * AppArmor security module | ||
| 3 | * | ||
| 4 | * This file contains AppArmor contexts used to associate "labels" to objects. | ||
| 5 | * | ||
| 6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
| 7 | * Copyright 2009-2010 Canonical Ltd. | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or | ||
| 10 | * modify it under the terms of the GNU General Public License as | ||
| 11 | * published by the Free Software Foundation, version 2 of the | ||
| 12 | * License. | ||
| 13 | */ | ||
| 14 | |||
| 15 | #ifndef __AA_CONTEXT_H | ||
| 16 | #define __AA_CONTEXT_H | ||
| 17 | |||
| 18 | #include <linux/cred.h> | ||
| 19 | #include <linux/slab.h> | ||
| 20 | #include <linux/sched.h> | ||
| 21 | |||
| 22 | #include "policy.h" | ||
| 23 | |||
| 24 | /* struct aa_file_cxt - the AppArmor context the file was opened in | ||
| 25 | * @perms: the permission the file was opened with | ||
| 26 | * | ||
| 27 | * The file_cxt could currently be directly stored in file->f_security | ||
| 28 | * as the profile reference is now stored in the f_cred. However the | ||
| 29 | * cxt struct will expand in the future so we keep the struct. | ||
| 30 | */ | ||
| 31 | struct aa_file_cxt { | ||
| 32 | u16 allow; | ||
| 33 | }; | ||
| 34 | |||
| 35 | /** | ||
| 36 | * aa_alloc_file_context - allocate file_cxt | ||
| 37 | * @gfp: gfp flags for allocation | ||
| 38 | * | ||
| 39 | * Returns: file_cxt or NULL on failure | ||
| 40 | */ | ||
| 41 | static inline struct aa_file_cxt *aa_alloc_file_context(gfp_t gfp) | ||
| 42 | { | ||
| 43 | return kzalloc(sizeof(struct aa_file_cxt), gfp); | ||
| 44 | } | ||
| 45 | |||
| 46 | /** | ||
| 47 | * aa_free_file_context - free a file_cxt | ||
| 48 | * @cxt: file_cxt to free (MAYBE_NULL) | ||
| 49 | */ | ||
| 50 | static inline void aa_free_file_context(struct aa_file_cxt *cxt) | ||
| 51 | { | ||
| 52 | if (cxt) | ||
| 53 | kzfree(cxt); | ||
| 54 | } | ||
| 55 | |||
| 56 | /** | ||
| 57 | * struct aa_task_cxt - primary label for confined tasks | ||
| 58 | * @profile: the current profile (NOT NULL) | ||
| 59 | * @exec: profile to transition to on next exec (MAYBE NULL) | ||
| 60 | * @previous: profile the task may return to (MAYBE NULL) | ||
| 61 | * @token: magic value the task must know for returning to @previous_profile | ||
| 62 | * | ||
| 63 | * Contains the task's current profile (which could change due to | ||
| 64 | * change_hat). Plus the hat_magic needed during change_hat. | ||
| 65 | * | ||
| 66 | * TODO: make so a task can be confined by a stack of contexts | ||
| 67 | */ | ||
| 68 | struct aa_task_cxt { | ||
| 69 | struct aa_profile *profile; | ||
| 70 | struct aa_profile *onexec; | ||
| 71 | struct aa_profile *previous; | ||
| 72 | u64 token; | ||
| 73 | }; | ||
| 74 | |||
| 75 | struct aa_task_cxt *aa_alloc_task_context(gfp_t flags); | ||
| 76 | void aa_free_task_context(struct aa_task_cxt *cxt); | ||
| 77 | void aa_dup_task_context(struct aa_task_cxt *new, | ||
| 78 | const struct aa_task_cxt *old); | ||
| 79 | int aa_replace_current_profile(struct aa_profile *profile); | ||
| 80 | int aa_set_current_onexec(struct aa_profile *profile); | ||
| 81 | int aa_set_current_hat(struct aa_profile *profile, u64 token); | ||
| 82 | int aa_restore_previous_profile(u64 cookie); | ||
| 83 | |||
| 84 | /** | ||
| 85 | * __aa_task_is_confined - determine if @task has any confinement | ||
| 86 | * @task: task to check confinement of (NOT NULL) | ||
| 87 | * | ||
| 88 | * If @task != current needs to be called in RCU safe critical section | ||
| 89 | */ | ||
| 90 | static inline bool __aa_task_is_confined(struct task_struct *task) | ||
| 91 | { | ||
| 92 | struct aa_task_cxt *cxt = __task_cred(task)->security; | ||
| 93 | |||
| 94 | BUG_ON(!cxt || !cxt->profile); | ||
| 95 | if (unconfined(aa_newest_version(cxt->profile))) | ||
| 96 | return 0; | ||
| 97 | |||
| 98 | return 1; | ||
| 99 | } | ||
| 100 | |||
| 101 | /** | ||
| 102 | * aa_cred_profile - obtain cred's profiles | ||
| 103 | * @cred: cred to obtain profiles from (NOT NULL) | ||
| 104 | * | ||
| 105 | * Returns: confining profile | ||
| 106 | * | ||
| 107 | * does NOT increment reference count | ||
| 108 | */ | ||
| 109 | static inline struct aa_profile *aa_cred_profile(const struct cred *cred) | ||
| 110 | { | ||
| 111 | struct aa_task_cxt *cxt = cred->security; | ||
| 112 | BUG_ON(!cxt || !cxt->profile); | ||
| 113 | return aa_newest_version(cxt->profile); | ||
| 114 | } | ||
| 115 | |||
| 116 | /** | ||
| 117 | * __aa_current_profile - find the current tasks confining profile | ||
| 118 | * | ||
| 119 | * Returns: up to date confining profile or the ns unconfined profile (NOT NULL) | ||
| 120 | * | ||
| 121 | * This fn will not update the tasks cred to the most up to date version | ||
| 122 | * of the profile so it is safe to call when inside of locks. | ||
| 123 | */ | ||
| 124 | static inline struct aa_profile *__aa_current_profile(void) | ||
| 125 | { | ||
| 126 | return aa_cred_profile(current_cred()); | ||
| 127 | } | ||
| 128 | |||
| 129 | /** | ||
| 130 | * aa_current_profile - find the current tasks confining profile and do updates | ||
| 131 | * | ||
| 132 | * Returns: up to date confining profile or the ns unconfined profile (NOT NULL) | ||
| 133 | * | ||
| 134 | * This fn will update the tasks cred structure if the profile has been | ||
| 135 | * replaced. Not safe to call inside locks | ||
| 136 | */ | ||
| 137 | static inline struct aa_profile *aa_current_profile(void) | ||
| 138 | { | ||
| 139 | const struct aa_task_cxt *cxt = current_cred()->security; | ||
| 140 | struct aa_profile *profile; | ||
| 141 | BUG_ON(!cxt || !cxt->profile); | ||
| 142 | |||
| 143 | profile = aa_newest_version(cxt->profile); | ||
| 144 | /* | ||
| 145 | * Whether or not replacement succeeds, use newest profile so | ||
| 146 | * there is no need to update it after replacement. | ||
| 147 | */ | ||
| 148 | if (unlikely((cxt->profile != profile))) | ||
| 149 | aa_replace_current_profile(profile); | ||
| 150 | |||
| 151 | return profile; | ||
| 152 | } | ||
| 153 | |||
| 154 | #endif /* __AA_CONTEXT_H */ | ||
diff --git a/security/apparmor/include/domain.h b/security/apparmor/include/domain.h new file mode 100644 index 000000000000..de04464f0a3f --- /dev/null +++ b/security/apparmor/include/domain.h | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | /* | ||
| 2 | * AppArmor security module | ||
| 3 | * | ||
| 4 | * This file contains AppArmor security domain transition function definitions. | ||
| 5 | * | ||
| 6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
| 7 | * Copyright 2009-2010 Canonical Ltd. | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or | ||
| 10 | * modify it under the terms of the GNU General Public License as | ||
| 11 | * published by the Free Software Foundation, version 2 of the | ||
| 12 | * License. | ||
| 13 | */ | ||
| 14 | |||
| 15 | #include <linux/binfmts.h> | ||
| 16 | #include <linux/types.h> | ||
| 17 | |||
| 18 | #ifndef __AA_DOMAIN_H | ||
| 19 | #define __AA_DOMAIN_H | ||
| 20 | |||
| 21 | struct aa_domain { | ||
| 22 | int size; | ||
| 23 | char **table; | ||
| 24 | }; | ||
| 25 | |||
| 26 | int apparmor_bprm_set_creds(struct linux_binprm *bprm); | ||
| 27 | int apparmor_bprm_secureexec(struct linux_binprm *bprm); | ||
| 28 | void apparmor_bprm_committing_creds(struct linux_binprm *bprm); | ||
| 29 | void apparmor_bprm_committed_creds(struct linux_binprm *bprm); | ||
| 30 | |||
| 31 | void aa_free_domain_entries(struct aa_domain *domain); | ||
| 32 | int aa_change_hat(const char *hats[], int count, u64 token, bool permtest); | ||
| 33 | int aa_change_profile(const char *ns_name, const char *name, bool onexec, | ||
| 34 | bool permtest); | ||
| 35 | |||
| 36 | #endif /* __AA_DOMAIN_H */ | ||
diff --git a/security/apparmor/include/file.h b/security/apparmor/include/file.h new file mode 100644 index 000000000000..be36feabb16a --- /dev/null +++ b/security/apparmor/include/file.h | |||
| @@ -0,0 +1,217 @@ | |||
| 1 | /* | ||
| 2 | * AppArmor security module | ||
| 3 | * | ||
| 4 | * This file contains AppArmor file mediation function definitions. | ||
| 5 | * | ||
| 6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
| 7 | * Copyright 2009-2010 Canonical Ltd. | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or | ||
| 10 | * modify it under the terms of the GNU General Public License as | ||
| 11 | * published by the Free Software Foundation, version 2 of the | ||
| 12 | * License. | ||
| 13 | */ | ||
| 14 | |||
| 15 | #ifndef __AA_FILE_H | ||
| 16 | #define __AA_FILE_H | ||
| 17 | |||
| 18 | #include <linux/path.h> | ||
| 19 | |||
| 20 | #include "domain.h" | ||
| 21 | #include "match.h" | ||
| 22 | |||
| 23 | struct aa_profile; | ||
| 24 | |||
| 25 | /* | ||
| 26 | * We use MAY_EXEC, MAY_WRITE, MAY_READ, MAY_APPEND and the following flags | ||
| 27 | * for profile permissions | ||
| 28 | */ | ||
| 29 | #define AA_MAY_CREATE 0x0010 | ||
| 30 | #define AA_MAY_DELETE 0x0020 | ||
| 31 | #define AA_MAY_META_WRITE 0x0040 | ||
| 32 | #define AA_MAY_META_READ 0x0080 | ||
| 33 | |||
| 34 | #define AA_MAY_CHMOD 0x0100 | ||
| 35 | #define AA_MAY_CHOWN 0x0200 | ||
| 36 | #define AA_MAY_LOCK 0x0400 | ||
| 37 | #define AA_EXEC_MMAP 0x0800 | ||
| 38 | |||
| 39 | #define AA_MAY_LINK 0x1000 | ||
| 40 | #define AA_LINK_SUBSET AA_MAY_LOCK /* overlaid */ | ||
| 41 | #define AA_MAY_ONEXEC 0x40000000 /* exec allows onexec */ | ||
| 42 | #define AA_MAY_CHANGE_PROFILE 0x80000000 | ||
| 43 | #define AA_MAY_CHANGEHAT 0x80000000 /* ctrl auditing only */ | ||
| 44 | |||
| 45 | #define AA_AUDIT_FILE_MASK (MAY_READ | MAY_WRITE | MAY_EXEC | MAY_APPEND |\ | ||
| 46 | AA_MAY_CREATE | AA_MAY_DELETE | \ | ||
| 47 | AA_MAY_META_READ | AA_MAY_META_WRITE | \ | ||
| 48 | AA_MAY_CHMOD | AA_MAY_CHOWN | AA_MAY_LOCK | \ | ||
| 49 | AA_EXEC_MMAP | AA_MAY_LINK) | ||
| 50 | |||
| 51 | /* | ||
| 52 | * The xindex is broken into 3 parts | ||
| 53 | * - index - an index into either the exec name table or the variable table | ||
| 54 | * - exec type - which determines how the executable name and index are used | ||
| 55 | * - flags - which modify how the destination name is applied | ||
| 56 | */ | ||
| 57 | #define AA_X_INDEX_MASK 0x03ff | ||
| 58 | |||
| 59 | #define AA_X_TYPE_MASK 0x0c00 | ||
| 60 | #define AA_X_TYPE_SHIFT 10 | ||
| 61 | #define AA_X_NONE 0x0000 | ||
| 62 | #define AA_X_NAME 0x0400 /* use executable name px */ | ||
| 63 | #define AA_X_TABLE 0x0800 /* use a specified name ->n# */ | ||
| 64 | |||
| 65 | #define AA_X_UNSAFE 0x1000 | ||
| 66 | #define AA_X_CHILD 0x2000 /* make >AA_X_NONE apply to children */ | ||
| 67 | #define AA_X_INHERIT 0x4000 | ||
| 68 | #define AA_X_UNCONFINED 0x8000 | ||
| 69 | |||
| 70 | /* AA_SECURE_X_NEEDED - is passed in the bprm->unsafe field */ | ||
| 71 | #define AA_SECURE_X_NEEDED 0x8000 | ||
| 72 | |||
| 73 | /* need to make conditional which ones are being set */ | ||
| 74 | struct path_cond { | ||
| 75 | uid_t uid; | ||
| 76 | umode_t mode; | ||
| 77 | }; | ||
| 78 | |||
| 79 | /* struct file_perms - file permission | ||
| 80 | * @allow: mask of permissions that are allowed | ||
| 81 | * @audit: mask of permissions to force an audit message for | ||
| 82 | * @quiet: mask of permissions to quiet audit messages for | ||
| 83 | * @kill: mask of permissions that when matched will kill the task | ||
| 84 | * @xindex: exec transition index if @allow contains MAY_EXEC | ||
| 85 | * | ||
| 86 | * The @audit and @queit mask should be mutually exclusive. | ||
| 87 | */ | ||
| 88 | struct file_perms { | ||
| 89 | u32 allow; | ||
| 90 | u32 audit; | ||
| 91 | u32 quiet; | ||
| 92 | u32 kill; | ||
| 93 | u16 xindex; | ||
| 94 | }; | ||
| 95 | |||
| 96 | extern struct file_perms nullperms; | ||
| 97 | |||
| 98 | #define COMBINED_PERM_MASK(X) ((X).allow | (X).audit | (X).quiet | (X).kill) | ||
| 99 | |||
| 100 | /* FIXME: split perms from dfa and match this to description | ||
| 101 | * also add delegation info. | ||
| 102 | */ | ||
| 103 | static inline u16 dfa_map_xindex(u16 mask) | ||
| 104 | { | ||
| 105 | u16 old_index = (mask >> 10) & 0xf; | ||
| 106 | u16 index = 0; | ||
| 107 | |||
| 108 | if (mask & 0x100) | ||
| 109 | index |= AA_X_UNSAFE; | ||
| 110 | if (mask & 0x200) | ||
| 111 | index |= AA_X_INHERIT; | ||
| 112 | if (mask & 0x80) | ||
| 113 | index |= AA_X_UNCONFINED; | ||
| 114 | |||
| 115 | if (old_index == 1) { | ||
| 116 | index |= AA_X_UNCONFINED; | ||
| 117 | } else if (old_index == 2) { | ||
| 118 | index |= AA_X_NAME; | ||
| 119 | } else if (old_index == 3) { | ||
| 120 | index |= AA_X_NAME | AA_X_CHILD; | ||
| 121 | } else { | ||
| 122 | index |= AA_X_TABLE; | ||
| 123 | index |= old_index - 4; | ||
| 124 | } | ||
| 125 | |||
| 126 | return index; | ||
| 127 | } | ||
| 128 | |||
| 129 | /* | ||
| 130 | * map old dfa inline permissions to new format | ||
| 131 | */ | ||
| 132 | #define dfa_user_allow(dfa, state) (((ACCEPT_TABLE(dfa)[state]) & 0x7f) | \ | ||
| 133 | ((ACCEPT_TABLE(dfa)[state]) & 0x80000000)) | ||
| 134 | #define dfa_user_audit(dfa, state) ((ACCEPT_TABLE2(dfa)[state]) & 0x7f) | ||
| 135 | #define dfa_user_quiet(dfa, state) (((ACCEPT_TABLE2(dfa)[state]) >> 7) & 0x7f) | ||
| 136 | #define dfa_user_xindex(dfa, state) \ | ||
| 137 | (dfa_map_xindex(ACCEPT_TABLE(dfa)[state] & 0x3fff)) | ||
| 138 | |||
| 139 | #define dfa_other_allow(dfa, state) ((((ACCEPT_TABLE(dfa)[state]) >> 14) & \ | ||
| 140 | 0x7f) | \ | ||
| 141 | ((ACCEPT_TABLE(dfa)[state]) & 0x80000000)) | ||
| 142 | #define dfa_other_audit(dfa, state) (((ACCEPT_TABLE2(dfa)[state]) >> 14) & 0x7f) | ||
| 143 | #define dfa_other_quiet(dfa, state) \ | ||
| 144 | ((((ACCEPT_TABLE2(dfa)[state]) >> 7) >> 14) & 0x7f) | ||
| 145 | #define dfa_other_xindex(dfa, state) \ | ||
| 146 | dfa_map_xindex((ACCEPT_TABLE(dfa)[state] >> 14) & 0x3fff) | ||
| 147 | |||
| 148 | int aa_audit_file(struct aa_profile *profile, struct file_perms *perms, | ||
| 149 | gfp_t gfp, int op, u32 request, const char *name, | ||
| 150 | const char *target, uid_t ouid, const char *info, int error); | ||
| 151 | |||
| 152 | /** | ||
| 153 | * struct aa_file_rules - components used for file rule permissions | ||
| 154 | * @dfa: dfa to match path names and conditionals against | ||
| 155 | * @perms: permission table indexed by the matched state accept entry of @dfa | ||
| 156 | * @trans: transition table for indexed by named x transitions | ||
| 157 | * | ||
| 158 | * File permission are determined by matching a path against @dfa and then | ||
| 159 | * then using the value of the accept entry for the matching state as | ||
| 160 | * an index into @perms. If a named exec transition is required it is | ||
| 161 | * looked up in the transition table. | ||
| 162 | */ | ||
| 163 | struct aa_file_rules { | ||
| 164 | unsigned int start; | ||
| 165 | struct aa_dfa *dfa; | ||
| 166 | /* struct perms perms; */ | ||
| 167 | struct aa_domain trans; | ||
| 168 | /* TODO: add delegate table */ | ||
| 169 | }; | ||
| 170 | |||
| 171 | unsigned int aa_str_perms(struct aa_dfa *dfa, unsigned int start, | ||
| 172 | const char *name, struct path_cond *cond, | ||
| 173 | struct file_perms *perms); | ||
| 174 | |||
| 175 | int aa_path_perm(int op, struct aa_profile *profile, struct path *path, | ||
| 176 | int flags, u32 request, struct path_cond *cond); | ||
| 177 | |||
| 178 | int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry, | ||
| 179 | struct path *new_dir, struct dentry *new_dentry); | ||
| 180 | |||
| 181 | int aa_file_perm(int op, struct aa_profile *profile, struct file *file, | ||
| 182 | u32 request); | ||
| 183 | |||
| 184 | static inline void aa_free_file_rules(struct aa_file_rules *rules) | ||
| 185 | { | ||
| 186 | aa_put_dfa(rules->dfa); | ||
| 187 | aa_free_domain_entries(&rules->trans); | ||
| 188 | } | ||
| 189 | |||
| 190 | #define ACC_FMODE(x) (("\000\004\002\006"[(x)&O_ACCMODE]) | (((x) << 1) & 0x40)) | ||
| 191 | |||
| 192 | /* from namei.c */ | ||
| 193 | #define MAP_OPEN_FLAGS(x) ((((x) + 1) & O_ACCMODE) ? (x) + 1 : (x)) | ||
| 194 | |||
| 195 | /** | ||
| 196 | * aa_map_file_perms - map file flags to AppArmor permissions | ||
| 197 | * @file: open file to map flags to AppArmor permissions | ||
| 198 | * | ||
| 199 | * Returns: apparmor permission set for the file | ||
| 200 | */ | ||
| 201 | static inline u32 aa_map_file_to_perms(struct file *file) | ||
| 202 | { | ||
| 203 | int flags = MAP_OPEN_FLAGS(file->f_flags); | ||
| 204 | u32 perms = ACC_FMODE(file->f_mode); | ||
| 205 | |||
| 206 | if ((flags & O_APPEND) && (perms & MAY_WRITE)) | ||
| 207 | perms = (perms & ~MAY_WRITE) | MAY_APPEND; | ||
| 208 | /* trunc implies write permission */ | ||
| 209 | if (flags & O_TRUNC) | ||
| 210 | perms |= MAY_WRITE; | ||
| 211 | if (flags & O_CREAT) | ||
| 212 | perms |= AA_MAY_CREATE; | ||
| 213 | |||
| 214 | return perms; | ||
| 215 | } | ||
| 216 | |||
| 217 | #endif /* __AA_FILE_H */ | ||
diff --git a/security/apparmor/include/ipc.h b/security/apparmor/include/ipc.h new file mode 100644 index 000000000000..aeda0fbc8b2f --- /dev/null +++ b/security/apparmor/include/ipc.h | |||
| @@ -0,0 +1,28 @@ | |||
| 1 | /* | ||
| 2 | * AppArmor security module | ||
| 3 | * | ||
| 4 | * This file contains AppArmor ipc mediation function definitions. | ||
| 5 | * | ||
| 6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
| 7 | * Copyright 2009-2010 Canonical Ltd. | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or | ||
| 10 | * modify it under the terms of the GNU General Public License as | ||
| 11 | * published by the Free Software Foundation, version 2 of the | ||
| 12 | * License. | ||
| 13 | */ | ||
| 14 | |||
| 15 | #ifndef __AA_IPC_H | ||
| 16 | #define __AA_IPC_H | ||
| 17 | |||
| 18 | #include <linux/sched.h> | ||
| 19 | |||
| 20 | struct aa_profile; | ||
| 21 | |||
| 22 | int aa_may_ptrace(struct task_struct *tracer_task, struct aa_profile *tracer, | ||
| 23 | struct aa_profile *tracee, unsigned int mode); | ||
| 24 | |||
| 25 | int aa_ptrace(struct task_struct *tracer, struct task_struct *tracee, | ||
| 26 | unsigned int mode); | ||
| 27 | |||
| 28 | #endif /* __AA_IPC_H */ | ||
diff --git a/security/apparmor/include/match.h b/security/apparmor/include/match.h new file mode 100644 index 000000000000..734a6d35112c --- /dev/null +++ b/security/apparmor/include/match.h | |||
| @@ -0,0 +1,132 @@ | |||
| 1 | /* | ||
| 2 | * AppArmor security module | ||
| 3 | * | ||
| 4 | * This file contains AppArmor policy dfa matching engine definitions. | ||
| 5 | * | ||
| 6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
| 7 | * Copyright 2009-2010 Canonical Ltd. | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or | ||
| 10 | * modify it under the terms of the GNU General Public License as | ||
| 11 | * published by the Free Software Foundation, version 2 of the | ||
| 12 | * License. | ||
| 13 | */ | ||
| 14 | |||
| 15 | #ifndef __AA_MATCH_H | ||
| 16 | #define __AA_MATCH_H | ||
| 17 | |||
| 18 | #include <linux/workqueue.h> | ||
| 19 | |||
| 20 | #define DFA_NOMATCH 0 | ||
| 21 | #define DFA_START 1 | ||
| 22 | |||
| 23 | #define DFA_VALID_PERM_MASK 0xffffffff | ||
| 24 | #define DFA_VALID_PERM2_MASK 0xffffffff | ||
| 25 | |||
| 26 | /** | ||
| 27 | * The format used for transition tables is based on the GNU flex table | ||
| 28 | * file format (--tables-file option; see Table File Format in the flex | ||
| 29 | * info pages and the flex sources for documentation). The magic number | ||
| 30 | * used in the header is 0x1B5E783D insted of 0xF13C57B1 though, because | ||
| 31 | * the YY_ID_CHK (check) and YY_ID_DEF (default) tables are used | ||
| 32 | * slightly differently (see the apparmor-parser package). | ||
| 33 | */ | ||
| 34 | |||
| 35 | #define YYTH_MAGIC 0x1B5E783D | ||
| 36 | #define YYTH_DEF_RECURSE 0x1 /* DEF Table is recursive */ | ||
| 37 | |||
| 38 | struct table_set_header { | ||
| 39 | u32 th_magic; /* YYTH_MAGIC */ | ||
| 40 | u32 th_hsize; | ||
| 41 | u32 th_ssize; | ||
| 42 | u16 th_flags; | ||
| 43 | char th_version[]; | ||
| 44 | }; | ||
| 45 | |||
| 46 | /* The YYTD_ID are one less than flex table mappings. The flex id | ||
| 47 | * has 1 subtracted at table load time, this allows us to directly use the | ||
| 48 | * ID's as indexes. | ||
| 49 | */ | ||
| 50 | #define YYTD_ID_ACCEPT 0 | ||
| 51 | #define YYTD_ID_BASE 1 | ||
| 52 | #define YYTD_ID_CHK 2 | ||
| 53 | #define YYTD_ID_DEF 3 | ||
| 54 | #define YYTD_ID_EC 4 | ||
| 55 | #define YYTD_ID_META 5 | ||
| 56 | #define YYTD_ID_ACCEPT2 6 | ||
| 57 | #define YYTD_ID_NXT 7 | ||
| 58 | #define YYTD_ID_TSIZE 8 | ||
| 59 | |||
| 60 | #define YYTD_DATA8 1 | ||
| 61 | #define YYTD_DATA16 2 | ||
| 62 | #define YYTD_DATA32 4 | ||
| 63 | #define YYTD_DATA64 8 | ||
| 64 | |||
| 65 | /* Each ACCEPT2 table gets 6 dedicated flags, YYTD_DATAX define the | ||
| 66 | * first flags | ||
| 67 | */ | ||
| 68 | #define ACCEPT1_FLAGS(X) ((X) & 0x3f) | ||
| 69 | #define ACCEPT2_FLAGS(X) ACCEPT1_FLAGS((X) >> YYTD_ID_ACCEPT2) | ||
| 70 | #define TO_ACCEPT1_FLAG(X) ACCEPT1_FLAGS(X) | ||
| 71 | #define TO_ACCEPT2_FLAG(X) (ACCEPT1_FLAGS(X) << YYTD_ID_ACCEPT2) | ||
| 72 | #define DFA_FLAG_VERIFY_STATES 0x1000 | ||
| 73 | |||
| 74 | struct table_header { | ||
| 75 | u16 td_id; | ||
| 76 | u16 td_flags; | ||
| 77 | u32 td_hilen; | ||
| 78 | u32 td_lolen; | ||
| 79 | char td_data[]; | ||
| 80 | }; | ||
| 81 | |||
| 82 | #define DEFAULT_TABLE(DFA) ((u16 *)((DFA)->tables[YYTD_ID_DEF]->td_data)) | ||
| 83 | #define BASE_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_BASE]->td_data)) | ||
| 84 | #define NEXT_TABLE(DFA) ((u16 *)((DFA)->tables[YYTD_ID_NXT]->td_data)) | ||
| 85 | #define CHECK_TABLE(DFA) ((u16 *)((DFA)->tables[YYTD_ID_CHK]->td_data)) | ||
| 86 | #define EQUIV_TABLE(DFA) ((u8 *)((DFA)->tables[YYTD_ID_EC]->td_data)) | ||
| 87 | #define ACCEPT_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_ACCEPT]->td_data)) | ||
| 88 | #define ACCEPT_TABLE2(DFA) ((u32 *)((DFA)->tables[YYTD_ID_ACCEPT2]->td_data)) | ||
| 89 | |||
| 90 | struct aa_dfa { | ||
| 91 | struct kref count; | ||
| 92 | u16 flags; | ||
| 93 | struct table_header *tables[YYTD_ID_TSIZE]; | ||
| 94 | }; | ||
| 95 | |||
| 96 | #define byte_to_byte(X) (X) | ||
| 97 | |||
| 98 | #define UNPACK_ARRAY(TABLE, BLOB, LEN, TYPE, NTOHX) \ | ||
| 99 | do { \ | ||
| 100 | typeof(LEN) __i; \ | ||
| 101 | TYPE *__t = (TYPE *) TABLE; \ | ||
| 102 | TYPE *__b = (TYPE *) BLOB; \ | ||
| 103 | for (__i = 0; __i < LEN; __i++) { \ | ||
| 104 | __t[__i] = NTOHX(__b[__i]); \ | ||
| 105 | } \ | ||
| 106 | } while (0) | ||
| 107 | |||
| 108 | static inline size_t table_size(size_t len, size_t el_size) | ||
| 109 | { | ||
| 110 | return ALIGN(sizeof(struct table_header) + len * el_size, 8); | ||
| 111 | } | ||
| 112 | |||
| 113 | struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags); | ||
| 114 | unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start, | ||
| 115 | const char *str, int len); | ||
| 116 | unsigned int aa_dfa_match(struct aa_dfa *dfa, unsigned int start, | ||
| 117 | const char *str); | ||
| 118 | void aa_dfa_free_kref(struct kref *kref); | ||
| 119 | |||
| 120 | /** | ||
| 121 | * aa_put_dfa - put a dfa refcount | ||
| 122 | * @dfa: dfa to put refcount (MAYBE NULL) | ||
| 123 | * | ||
| 124 | * Requires: if @dfa != NULL that a valid refcount be held | ||
| 125 | */ | ||
| 126 | static inline void aa_put_dfa(struct aa_dfa *dfa) | ||
| 127 | { | ||
| 128 | if (dfa) | ||
| 129 | kref_put(&dfa->count, aa_dfa_free_kref); | ||
| 130 | } | ||
| 131 | |||
| 132 | #endif /* __AA_MATCH_H */ | ||
diff --git a/security/apparmor/include/path.h b/security/apparmor/include/path.h new file mode 100644 index 000000000000..27b327a7fae5 --- /dev/null +++ b/security/apparmor/include/path.h | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | /* | ||
| 2 | * AppArmor security module | ||
| 3 | * | ||
| 4 | * This file contains AppArmor basic path manipulation function definitions. | ||
| 5 | * | ||
| 6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
| 7 | * Copyright 2009-2010 Canonical Ltd. | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or | ||
| 10 | * modify it under the terms of the GNU General Public License as | ||
| 11 | * published by the Free Software Foundation, version 2 of the | ||
| 12 | * License. | ||
| 13 | */ | ||
| 14 | |||
| 15 | #ifndef __AA_PATH_H | ||
| 16 | #define __AA_PATH_H | ||
| 17 | |||
| 18 | |||
| 19 | enum path_flags { | ||
| 20 | PATH_IS_DIR = 0x1, /* path is a directory */ | ||
| 21 | PATH_CONNECT_PATH = 0x4, /* connect disconnected paths to / */ | ||
| 22 | PATH_CHROOT_REL = 0x8, /* do path lookup relative to chroot */ | ||
| 23 | PATH_CHROOT_NSCONNECT = 0x10, /* connect paths that are at ns root */ | ||
| 24 | |||
| 25 | PATH_DELEGATE_DELETED = 0x08000, /* delegate deleted files */ | ||
| 26 | PATH_MEDIATE_DELETED = 0x10000, /* mediate deleted paths */ | ||
| 27 | }; | ||
| 28 | |||
| 29 | int aa_get_name(struct path *path, int flags, char **buffer, const char **name); | ||
| 30 | |||
| 31 | #endif /* __AA_PATH_H */ | ||
diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h new file mode 100644 index 000000000000..aeda5cf56904 --- /dev/null +++ b/security/apparmor/include/policy.h | |||
| @@ -0,0 +1,305 @@ | |||
| 1 | /* | ||
| 2 | * AppArmor security module | ||
| 3 | * | ||
| 4 | * This file contains AppArmor policy definitions. | ||
| 5 | * | ||
| 6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
| 7 | * Copyright 2009-2010 Canonical Ltd. | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or | ||
| 10 | * modify it under the terms of the GNU General Public License as | ||
| 11 | * published by the Free Software Foundation, version 2 of the | ||
| 12 | * License. | ||
| 13 | */ | ||
| 14 | |||
| 15 | #ifndef __AA_POLICY_H | ||
| 16 | #define __AA_POLICY_H | ||
| 17 | |||
| 18 | #include <linux/capability.h> | ||
| 19 | #include <linux/cred.h> | ||
| 20 | #include <linux/kref.h> | ||
| 21 | #include <linux/sched.h> | ||
| 22 | #include <linux/slab.h> | ||
| 23 | #include <linux/socket.h> | ||
| 24 | |||
| 25 | #include "apparmor.h" | ||
| 26 | #include "audit.h" | ||
| 27 | #include "capability.h" | ||
| 28 | #include "domain.h" | ||
| 29 | #include "file.h" | ||
| 30 | #include "resource.h" | ||
| 31 | |||
| 32 | extern const char *profile_mode_names[]; | ||
| 33 | #define APPARMOR_NAMES_MAX_INDEX 3 | ||
| 34 | |||
| 35 | #define COMPLAIN_MODE(_profile) \ | ||
| 36 | ((aa_g_profile_mode == APPARMOR_COMPLAIN) || \ | ||
| 37 | ((_profile)->mode == APPARMOR_COMPLAIN)) | ||
| 38 | |||
| 39 | #define KILL_MODE(_profile) \ | ||
| 40 | ((aa_g_profile_mode == APPARMOR_KILL) || \ | ||
| 41 | ((_profile)->mode == APPARMOR_KILL)) | ||
| 42 | |||
| 43 | #define PROFILE_IS_HAT(_profile) ((_profile)->flags & PFLAG_HAT) | ||
| 44 | |||
| 45 | /* | ||
| 46 | * FIXME: currently need a clean way to replace and remove profiles as a | ||
| 47 | * set. It should be done at the namespace level. | ||
| 48 | * Either, with a set of profiles loaded at the namespace level or via | ||
| 49 | * a mark and remove marked interface. | ||
| 50 | */ | ||
| 51 | enum profile_mode { | ||
| 52 | APPARMOR_ENFORCE, /* enforce access rules */ | ||
| 53 | APPARMOR_COMPLAIN, /* allow and log access violations */ | ||
| 54 | APPARMOR_KILL, /* kill task on access violation */ | ||
| 55 | }; | ||
| 56 | |||
| 57 | enum profile_flags { | ||
| 58 | PFLAG_HAT = 1, /* profile is a hat */ | ||
| 59 | PFLAG_UNCONFINED = 2, /* profile is an unconfined profile */ | ||
| 60 | PFLAG_NULL = 4, /* profile is null learning profile */ | ||
| 61 | PFLAG_IX_ON_NAME_ERROR = 8, /* fallback to ix on name lookup fail */ | ||
| 62 | PFLAG_IMMUTABLE = 0x10, /* don't allow changes/replacement */ | ||
| 63 | PFLAG_USER_DEFINED = 0x20, /* user based profile - lower privs */ | ||
| 64 | PFLAG_NO_LIST_REF = 0x40, /* list doesn't keep profile ref */ | ||
| 65 | PFLAG_OLD_NULL_TRANS = 0x100, /* use // as the null transition */ | ||
| 66 | |||
| 67 | /* These flags must correspond with PATH_flags */ | ||
| 68 | PFLAG_MEDIATE_DELETED = 0x10000, /* mediate instead delegate deleted */ | ||
| 69 | }; | ||
| 70 | |||
| 71 | struct aa_profile; | ||
| 72 | |||
| 73 | /* struct aa_policy - common part of both namespaces and profiles | ||
| 74 | * @name: name of the object | ||
| 75 | * @hname - The hierarchical name | ||
| 76 | * @count: reference count of the obj | ||
| 77 | * @list: list policy object is on | ||
| 78 | * @profiles: head of the profiles list contained in the object | ||
| 79 | */ | ||
| 80 | struct aa_policy { | ||
| 81 | char *name; | ||
| 82 | char *hname; | ||
| 83 | struct kref count; | ||
| 84 | struct list_head list; | ||
| 85 | struct list_head profiles; | ||
| 86 | }; | ||
| 87 | |||
| 88 | /* struct aa_ns_acct - accounting of profiles in namespace | ||
| 89 | * @max_size: maximum space allowed for all profiles in namespace | ||
| 90 | * @max_count: maximum number of profiles that can be in this namespace | ||
| 91 | * @size: current size of profiles | ||
| 92 | * @count: current count of profiles (includes null profiles) | ||
| 93 | */ | ||
| 94 | struct aa_ns_acct { | ||
| 95 | int max_size; | ||
| 96 | int max_count; | ||
| 97 | int size; | ||
| 98 | int count; | ||
| 99 | }; | ||
| 100 | |||
| 101 | /* struct aa_namespace - namespace for a set of profiles | ||
| 102 | * @base: common policy | ||
| 103 | * @parent: parent of namespace | ||
| 104 | * @lock: lock for modifying the object | ||
| 105 | * @acct: accounting for the namespace | ||
| 106 | * @unconfined: special unconfined profile for the namespace | ||
| 107 | * @sub_ns: list of namespaces under the current namespace. | ||
| 108 | * | ||
| 109 | * An aa_namespace defines the set profiles that are searched to determine | ||
| 110 | * which profile to attach to a task. Profiles can not be shared between | ||
| 111 | * aa_namespaces and profile names within a namespace are guaranteed to be | ||
| 112 | * unique. When profiles in separate namespaces have the same name they | ||
| 113 | * are NOT considered to be equivalent. | ||
| 114 | * | ||
| 115 | * Namespaces are hierarchical and only namespaces and profiles below the | ||
| 116 | * current namespace are visible. | ||
| 117 | * | ||
| 118 | * Namespace names must be unique and can not contain the characters :/\0 | ||
| 119 | * | ||
| 120 | * FIXME TODO: add vserver support of namespaces (can it all be done in | ||
| 121 | * userspace?) | ||
| 122 | */ | ||
| 123 | struct aa_namespace { | ||
| 124 | struct aa_policy base; | ||
| 125 | struct aa_namespace *parent; | ||
| 126 | rwlock_t lock; | ||
| 127 | struct aa_ns_acct acct; | ||
| 128 | struct aa_profile *unconfined; | ||
| 129 | struct list_head sub_ns; | ||
| 130 | }; | ||
| 131 | |||
| 132 | /* struct aa_profile - basic confinement data | ||
| 133 | * @base - base components of the profile (name, refcount, lists, lock ...) | ||
| 134 | * @parent: parent of profile | ||
| 135 | * @ns: namespace the profile is in | ||
| 136 | * @replacedby: is set to the profile that replaced this profile | ||
| 137 | * @rename: optional profile name that this profile renamed | ||
| 138 | * @xmatch: optional extended matching for unconfined executables names | ||
| 139 | * @xmatch_len: xmatch prefix len, used to determine xmatch priority | ||
| 140 | * @sid: the unique security id number of this profile | ||
| 141 | * @audit: the auditing mode of the profile | ||
| 142 | * @mode: the enforcement mode of the profile | ||
| 143 | * @flags: flags controlling profile behavior | ||
| 144 | * @path_flags: flags controlling path generation behavior | ||
| 145 | * @size: the memory consumed by this profiles rules | ||
| 146 | * @file: The set of rules governing basic file access and domain transitions | ||
| 147 | * @caps: capabilities for the profile | ||
| 148 | * @rlimits: rlimits for the profile | ||
| 149 | * | ||
| 150 | * The AppArmor profile contains the basic confinement data. Each profile | ||
| 151 | * has a name, and exists in a namespace. The @name and @exec_match are | ||
| 152 | * used to determine profile attachment against unconfined tasks. All other | ||
| 153 | * attachments are determined by profile X transition rules. | ||
| 154 | * | ||
| 155 | * The @replacedby field is write protected by the profile lock. Reads | ||
| 156 | * are assumed to be atomic, and are done without locking. | ||
| 157 | * | ||
| 158 | * Profiles have a hierarchy where hats and children profiles keep | ||
| 159 | * a reference to their parent. | ||
| 160 | * | ||
| 161 | * Profile names can not begin with a : and can not contain the \0 | ||
| 162 | * character. If a profile name begins with / it will be considered when | ||
| 163 | * determining profile attachment on "unconfined" tasks. | ||
| 164 | */ | ||
| 165 | struct aa_profile { | ||
| 166 | struct aa_policy base; | ||
| 167 | struct aa_profile *parent; | ||
| 168 | |||
| 169 | struct aa_namespace *ns; | ||
| 170 | struct aa_profile *replacedby; | ||
| 171 | const char *rename; | ||
| 172 | |||
| 173 | struct aa_dfa *xmatch; | ||
| 174 | int xmatch_len; | ||
| 175 | u32 sid; | ||
| 176 | enum audit_mode audit; | ||
| 177 | enum profile_mode mode; | ||
| 178 | u32 flags; | ||
| 179 | u32 path_flags; | ||
| 180 | int size; | ||
| 181 | |||
| 182 | struct aa_file_rules file; | ||
| 183 | struct aa_caps caps; | ||
| 184 | struct aa_rlimit rlimits; | ||
| 185 | }; | ||
| 186 | |||
| 187 | extern struct aa_namespace *root_ns; | ||
| 188 | extern enum profile_mode aa_g_profile_mode; | ||
| 189 | |||
| 190 | void aa_add_profile(struct aa_policy *common, struct aa_profile *profile); | ||
| 191 | |||
| 192 | bool aa_ns_visible(struct aa_namespace *curr, struct aa_namespace *view); | ||
| 193 | const char *aa_ns_name(struct aa_namespace *parent, struct aa_namespace *child); | ||
| 194 | int aa_alloc_root_ns(void); | ||
| 195 | void aa_free_root_ns(void); | ||
| 196 | void aa_free_namespace_kref(struct kref *kref); | ||
| 197 | |||
| 198 | struct aa_namespace *aa_find_namespace(struct aa_namespace *root, | ||
| 199 | const char *name); | ||
| 200 | |||
| 201 | static inline struct aa_policy *aa_get_common(struct aa_policy *c) | ||
| 202 | { | ||
| 203 | if (c) | ||
| 204 | kref_get(&c->count); | ||
| 205 | |||
| 206 | return c; | ||
| 207 | } | ||
| 208 | |||
| 209 | /** | ||
| 210 | * aa_get_namespace - increment references count on @ns | ||
| 211 | * @ns: namespace to increment reference count of (MAYBE NULL) | ||
| 212 | * | ||
| 213 | * Returns: pointer to @ns, if @ns is NULL returns NULL | ||
| 214 | * Requires: @ns must be held with valid refcount when called | ||
| 215 | */ | ||
| 216 | static inline struct aa_namespace *aa_get_namespace(struct aa_namespace *ns) | ||
| 217 | { | ||
| 218 | if (ns) | ||
| 219 | kref_get(&(ns->base.count)); | ||
| 220 | |||
| 221 | return ns; | ||
| 222 | } | ||
| 223 | |||
| 224 | /** | ||
| 225 | * aa_put_namespace - decrement refcount on @ns | ||
| 226 | * @ns: namespace to put reference of | ||
| 227 | * | ||
| 228 | * Decrement reference count of @ns and if no longer in use free it | ||
| 229 | */ | ||
| 230 | static inline void aa_put_namespace(struct aa_namespace *ns) | ||
| 231 | { | ||
| 232 | if (ns) | ||
| 233 | kref_put(&ns->base.count, aa_free_namespace_kref); | ||
| 234 | } | ||
| 235 | |||
| 236 | struct aa_profile *aa_alloc_profile(const char *name); | ||
| 237 | struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat); | ||
| 238 | void aa_free_profile_kref(struct kref *kref); | ||
| 239 | struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name); | ||
| 240 | struct aa_profile *aa_lookup_profile(struct aa_namespace *ns, const char *name); | ||
| 241 | struct aa_profile *aa_match_profile(struct aa_namespace *ns, const char *name); | ||
| 242 | |||
| 243 | ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace); | ||
| 244 | ssize_t aa_remove_profiles(char *name, size_t size); | ||
| 245 | |||
| 246 | #define PROF_ADD 1 | ||
| 247 | #define PROF_REPLACE 0 | ||
| 248 | |||
| 249 | #define unconfined(X) ((X)->flags & PFLAG_UNCONFINED) | ||
| 250 | |||
| 251 | /** | ||
| 252 | * aa_newest_version - find the newest version of @profile | ||
| 253 | * @profile: the profile to check for newer versions of (NOT NULL) | ||
| 254 | * | ||
| 255 | * Returns: newest version of @profile, if @profile is the newest version | ||
| 256 | * return @profile. | ||
| 257 | * | ||
| 258 | * NOTE: the profile returned is not refcounted, The refcount on @profile | ||
| 259 | * must be held until the caller decides what to do with the returned newest | ||
| 260 | * version. | ||
| 261 | */ | ||
| 262 | static inline struct aa_profile *aa_newest_version(struct aa_profile *profile) | ||
| 263 | { | ||
| 264 | while (profile->replacedby) | ||
| 265 | profile = profile->replacedby; | ||
| 266 | |||
| 267 | return profile; | ||
| 268 | } | ||
| 269 | |||
| 270 | /** | ||
| 271 | * aa_get_profile - increment refcount on profile @p | ||
| 272 | * @p: profile (MAYBE NULL) | ||
| 273 | * | ||
| 274 | * Returns: pointer to @p if @p is NULL will return NULL | ||
| 275 | * Requires: @p must be held with valid refcount when called | ||
| 276 | */ | ||
| 277 | static inline struct aa_profile *aa_get_profile(struct aa_profile *p) | ||
| 278 | { | ||
| 279 | if (p) | ||
| 280 | kref_get(&(p->base.count)); | ||
| 281 | |||
| 282 | return p; | ||
| 283 | } | ||
| 284 | |||
| 285 | /** | ||
| 286 | * aa_put_profile - decrement refcount on profile @p | ||
| 287 | * @p: profile (MAYBE NULL) | ||
| 288 | */ | ||
| 289 | static inline void aa_put_profile(struct aa_profile *p) | ||
| 290 | { | ||
| 291 | if (p) | ||
| 292 | kref_put(&p->base.count, aa_free_profile_kref); | ||
| 293 | } | ||
| 294 | |||
| 295 | static inline int AUDIT_MODE(struct aa_profile *profile) | ||
| 296 | { | ||
| 297 | if (aa_g_audit != AUDIT_NORMAL) | ||
| 298 | return aa_g_audit; | ||
| 299 | |||
| 300 | return profile->audit; | ||
| 301 | } | ||
| 302 | |||
| 303 | bool aa_may_manage_policy(int op); | ||
| 304 | |||
| 305 | #endif /* __AA_POLICY_H */ | ||
diff --git a/security/apparmor/include/policy_unpack.h b/security/apparmor/include/policy_unpack.h new file mode 100644 index 000000000000..a2dcccac45aa --- /dev/null +++ b/security/apparmor/include/policy_unpack.h | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | /* | ||
| 2 | * AppArmor security module | ||
| 3 | * | ||
| 4 | * This file contains AppArmor policy loading interface function definitions. | ||
| 5 | * | ||
| 6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
| 7 | * Copyright 2009-2010 Canonical Ltd. | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or | ||
| 10 | * modify it under the terms of the GNU General Public License as | ||
| 11 | * published by the Free Software Foundation, version 2 of the | ||
| 12 | * License. | ||
| 13 | */ | ||
| 14 | |||
| 15 | #ifndef __POLICY_INTERFACE_H | ||
| 16 | #define __POLICY_INTERFACE_H | ||
| 17 | |||
| 18 | struct aa_profile *aa_unpack(void *udata, size_t size, const char **ns); | ||
| 19 | |||
| 20 | #endif /* __POLICY_INTERFACE_H */ | ||
diff --git a/security/apparmor/include/procattr.h b/security/apparmor/include/procattr.h new file mode 100644 index 000000000000..544aa6b766a4 --- /dev/null +++ b/security/apparmor/include/procattr.h | |||
| @@ -0,0 +1,26 @@ | |||
| 1 | /* | ||
| 2 | * AppArmor security module | ||
| 3 | * | ||
| 4 | * This file contains AppArmor /proc/<pid>/attr/ interface function definitions. | ||
| 5 | * | ||
| 6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
| 7 | * Copyright 2009-2010 Canonical Ltd. | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or | ||
| 10 | * modify it under the terms of the GNU General Public License as | ||
| 11 | * published by the Free Software Foundation, version 2 of the | ||
| 12 | * License. | ||
| 13 | */ | ||
| 14 | |||
| 15 | #ifndef __AA_PROCATTR_H | ||
| 16 | #define __AA_PROCATTR_H | ||
| 17 | |||
| 18 | #define AA_DO_TEST 1 | ||
| 19 | #define AA_ONEXEC 1 | ||
| 20 | |||
| 21 | int aa_getprocattr(struct aa_profile *profile, char **string); | ||
| 22 | int aa_setprocattr_changehat(char *args, size_t size, int test); | ||
| 23 | int aa_setprocattr_changeprofile(char *fqname, bool onexec, int test); | ||
| 24 | int aa_setprocattr_permipc(char *fqname); | ||
| 25 | |||
| 26 | #endif /* __AA_PROCATTR_H */ | ||
diff --git a/security/apparmor/include/resource.h b/security/apparmor/include/resource.h new file mode 100644 index 000000000000..3c88be946494 --- /dev/null +++ b/security/apparmor/include/resource.h | |||
| @@ -0,0 +1,46 @@ | |||
| 1 | /* | ||
| 2 | * AppArmor security module | ||
| 3 | * | ||
| 4 | * This file contains AppArmor resource limits function definitions. | ||
| 5 | * | ||
| 6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
| 7 | * Copyright 2009-2010 Canonical Ltd. | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or | ||
| 10 | * modify it under the terms of the GNU General Public License as | ||
| 11 | * published by the Free Software Foundation, version 2 of the | ||
| 12 | * License. | ||
| 13 | */ | ||
| 14 | |||
| 15 | #ifndef __AA_RESOURCE_H | ||
| 16 | #define __AA_RESOURCE_H | ||
| 17 | |||
| 18 | #include <linux/resource.h> | ||
| 19 | #include <linux/sched.h> | ||
| 20 | |||
| 21 | struct aa_profile; | ||
| 22 | |||
| 23 | /* struct aa_rlimit - rlimit settings for the profile | ||
| 24 | * @mask: which hard limits to set | ||
| 25 | * @limits: rlimit values that override task limits | ||
| 26 | * | ||
| 27 | * AppArmor rlimits are used to set confined task rlimits. Only the | ||
| 28 | * limits specified in @mask will be controlled by apparmor. | ||
| 29 | */ | ||
| 30 | struct aa_rlimit { | ||
| 31 | unsigned int mask; | ||
| 32 | struct rlimit limits[RLIM_NLIMITS]; | ||
| 33 | }; | ||
| 34 | |||
| 35 | int aa_map_resource(int resource); | ||
| 36 | int aa_task_setrlimit(struct aa_profile *profile, unsigned int resource, | ||
| 37 | struct rlimit *new_rlim); | ||
| 38 | |||
| 39 | void __aa_transition_rlimits(struct aa_profile *old, struct aa_profile *new); | ||
| 40 | |||
| 41 | static inline void aa_free_rlimit_rules(struct aa_rlimit *rlims) | ||
| 42 | { | ||
| 43 | /* NOP */ | ||
| 44 | } | ||
| 45 | |||
| 46 | #endif /* __AA_RESOURCE_H */ | ||
diff --git a/security/apparmor/include/sid.h b/security/apparmor/include/sid.h new file mode 100644 index 000000000000..020db35c3010 --- /dev/null +++ b/security/apparmor/include/sid.h | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | /* | ||
| 2 | * AppArmor security module | ||
| 3 | * | ||
| 4 | * This file contains AppArmor security identifier (sid) definitions | ||
| 5 | * | ||
| 6 | * Copyright 2009-2010 Canonical Ltd. | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or | ||
| 9 | * modify it under the terms of the GNU General Public License as | ||
| 10 | * published by the Free Software Foundation, version 2 of the | ||
| 11 | * License. | ||
| 12 | */ | ||
| 13 | |||
| 14 | #ifndef __AA_SID_H | ||
| 15 | #define __AA_SID_H | ||
| 16 | |||
| 17 | #include <linux/types.h> | ||
| 18 | |||
| 19 | struct aa_profile; | ||
| 20 | |||
| 21 | u32 aa_alloc_sid(void); | ||
| 22 | void aa_free_sid(u32 sid); | ||
| 23 | |||
| 24 | #endif /* __AA_SID_H */ | ||
diff --git a/security/apparmor/ipc.c b/security/apparmor/ipc.c new file mode 100644 index 000000000000..649fad88869b --- /dev/null +++ b/security/apparmor/ipc.c | |||
| @@ -0,0 +1,114 @@ | |||
| 1 | /* | ||
| 2 | * AppArmor security module | ||
| 3 | * | ||
| 4 | * This file contains AppArmor ipc mediation | ||
| 5 | * | ||
| 6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
| 7 | * Copyright 2009-2010 Canonical Ltd. | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or | ||
| 10 | * modify it under the terms of the GNU General Public License as | ||
| 11 | * published by the Free Software Foundation, version 2 of the | ||
| 12 | * License. | ||
| 13 | */ | ||
| 14 | |||
| 15 | #include <linux/gfp.h> | ||
| 16 | #include <linux/ptrace.h> | ||
| 17 | |||
| 18 | #include "include/audit.h" | ||
| 19 | #include "include/capability.h" | ||
| 20 | #include "include/context.h" | ||
| 21 | #include "include/policy.h" | ||
| 22 | |||
| 23 | /* call back to audit ptrace fields */ | ||
| 24 | static void audit_cb(struct audit_buffer *ab, void *va) | ||
| 25 | { | ||
| 26 | struct common_audit_data *sa = va; | ||
| 27 | audit_log_format(ab, " target="); | ||
| 28 | audit_log_untrustedstring(ab, sa->aad.target); | ||
| 29 | } | ||
| 30 | |||
| 31 | /** | ||
| 32 | * aa_audit_ptrace - do auditing for ptrace | ||
| 33 | * @profile: profile being enforced (NOT NULL) | ||
| 34 | * @target: profile being traced (NOT NULL) | ||
| 35 | * @error: error condition | ||
| 36 | * | ||
| 37 | * Returns: %0 or error code | ||
| 38 | */ | ||
| 39 | static int aa_audit_ptrace(struct aa_profile *profile, | ||
| 40 | struct aa_profile *target, int error) | ||
| 41 | { | ||
| 42 | struct common_audit_data sa; | ||
| 43 | COMMON_AUDIT_DATA_INIT(&sa, NONE); | ||
| 44 | sa.aad.op = OP_PTRACE; | ||
| 45 | sa.aad.target = target; | ||
| 46 | sa.aad.error = error; | ||
| 47 | |||
| 48 | return aa_audit(AUDIT_APPARMOR_AUTO, profile, GFP_ATOMIC, &sa, | ||
| 49 | audit_cb); | ||
| 50 | } | ||
| 51 | |||
| 52 | /** | ||
| 53 | * aa_may_ptrace - test if tracer task can trace the tracee | ||
| 54 | * @tracer_task: task who will do the tracing (NOT NULL) | ||
| 55 | * @tracer: profile of the task doing the tracing (NOT NULL) | ||
| 56 | * @tracee: task to be traced | ||
| 57 | * @mode: whether PTRACE_MODE_READ || PTRACE_MODE_ATTACH | ||
| 58 | * | ||
| 59 | * Returns: %0 else error code if permission denied or error | ||
| 60 | */ | ||
| 61 | int aa_may_ptrace(struct task_struct *tracer_task, struct aa_profile *tracer, | ||
| 62 | struct aa_profile *tracee, unsigned int mode) | ||
| 63 | { | ||
| 64 | /* TODO: currently only based on capability, not extended ptrace | ||
| 65 | * rules, | ||
| 66 | * Test mode for PTRACE_MODE_READ || PTRACE_MODE_ATTACH | ||
| 67 | */ | ||
| 68 | |||
| 69 | if (unconfined(tracer) || tracer == tracee) | ||
| 70 | return 0; | ||
| 71 | /* log this capability request */ | ||
| 72 | return aa_capable(tracer_task, tracer, CAP_SYS_PTRACE, 1); | ||
| 73 | } | ||
| 74 | |||
| 75 | /** | ||
| 76 | * aa_ptrace - do ptrace permission check and auditing | ||
| 77 | * @tracer: task doing the tracing (NOT NULL) | ||
| 78 | * @tracee: task being traced (NOT NULL) | ||
| 79 | * @mode: ptrace mode either PTRACE_MODE_READ || PTRACE_MODE_ATTACH | ||
| 80 | * | ||
| 81 | * Returns: %0 else error code if permission denied or error | ||
| 82 | */ | ||
| 83 | int aa_ptrace(struct task_struct *tracer, struct task_struct *tracee, | ||
| 84 | unsigned int mode) | ||
| 85 | { | ||
| 86 | /* | ||
| 87 | * tracer can ptrace tracee when | ||
| 88 | * - tracer is unconfined || | ||
| 89 | * - tracer is in complain mode | ||
| 90 | * - tracer has rules allowing it to trace tracee currently this is: | ||
| 91 | * - confined by the same profile || | ||
| 92 | * - tracer profile has CAP_SYS_PTRACE | ||
| 93 | */ | ||
| 94 | |||
| 95 | struct aa_profile *tracer_p; | ||
| 96 | /* cred released below */ | ||
| 97 | const struct cred *cred = get_task_cred(tracer); | ||
| 98 | int error = 0; | ||
| 99 | tracer_p = aa_cred_profile(cred); | ||
| 100 | |||
| 101 | if (!unconfined(tracer_p)) { | ||
| 102 | /* lcred released below */ | ||
| 103 | const struct cred *lcred = get_task_cred(tracee); | ||
| 104 | struct aa_profile *tracee_p = aa_cred_profile(lcred); | ||
| 105 | |||
| 106 | error = aa_may_ptrace(tracer, tracer_p, tracee_p, mode); | ||
| 107 | error = aa_audit_ptrace(tracer_p, tracee_p, error); | ||
| 108 | |||
| 109 | put_cred(lcred); | ||
| 110 | } | ||
| 111 | put_cred(cred); | ||
| 112 | |||
| 113 | return error; | ||
| 114 | } | ||
diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c new file mode 100644 index 000000000000..6e85cdb4303f --- /dev/null +++ b/security/apparmor/lib.c | |||
| @@ -0,0 +1,133 @@ | |||
| 1 | /* | ||
| 2 | * AppArmor security module | ||
| 3 | * | ||
| 4 | * This file contains basic common functions used in AppArmor | ||
| 5 | * | ||
| 6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
| 7 | * Copyright 2009-2010 Canonical Ltd. | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or | ||
| 10 | * modify it under the terms of the GNU General Public License as | ||
| 11 | * published by the Free Software Foundation, version 2 of the | ||
| 12 | * License. | ||
| 13 | */ | ||
| 14 | |||
| 15 | #include <linux/slab.h> | ||
| 16 | #include <linux/string.h> | ||
| 17 | #include <linux/vmalloc.h> | ||
| 18 | |||
| 19 | #include "include/audit.h" | ||
| 20 | |||
| 21 | |||
| 22 | /** | ||
| 23 | * aa_split_fqname - split a fqname into a profile and namespace name | ||
| 24 | * @fqname: a full qualified name in namespace profile format (NOT NULL) | ||
| 25 | * @ns_name: pointer to portion of the string containing the ns name (NOT NULL) | ||
| 26 | * | ||
| 27 | * Returns: profile name or NULL if one is not specified | ||
| 28 | * | ||
| 29 | * Split a namespace name from a profile name (see policy.c for naming | ||
| 30 | * description). If a portion of the name is missing it returns NULL for | ||
| 31 | * that portion. | ||
| 32 | * | ||
| 33 | * NOTE: may modify the @fqname string. The pointers returned point | ||
| 34 | * into the @fqname string. | ||
| 35 | */ | ||
| 36 | char *aa_split_fqname(char *fqname, char **ns_name) | ||
| 37 | { | ||
| 38 | char *name = strim(fqname); | ||
| 39 | |||
| 40 | *ns_name = NULL; | ||
| 41 | if (name[0] == ':') { | ||
| 42 | char *split = strchr(&name[1], ':'); | ||
| 43 | if (split) { | ||
| 44 | /* overwrite ':' with \0 */ | ||
| 45 | *split = 0; | ||
| 46 | name = skip_spaces(split + 1); | ||
| 47 | } else | ||
| 48 | /* a ns name without a following profile is allowed */ | ||
| 49 | name = NULL; | ||
| 50 | *ns_name = &name[1]; | ||
| 51 | } | ||
| 52 | if (name && *name == 0) | ||
| 53 | name = NULL; | ||
| 54 | |||
| 55 | return name; | ||
| 56 | } | ||
| 57 | |||
| 58 | /** | ||
| 59 | * aa_info_message - log a none profile related status message | ||
| 60 | * @str: message to log | ||
| 61 | */ | ||
| 62 | void aa_info_message(const char *str) | ||
| 63 | { | ||
| 64 | if (audit_enabled) { | ||
| 65 | struct common_audit_data sa; | ||
| 66 | COMMON_AUDIT_DATA_INIT(&sa, NONE); | ||
| 67 | sa.aad.info = str; | ||
| 68 | aa_audit_msg(AUDIT_APPARMOR_STATUS, &sa, NULL); | ||
| 69 | } | ||
| 70 | printk(KERN_INFO "AppArmor: %s\n", str); | ||
| 71 | } | ||
| 72 | |||
| 73 | /** | ||
| 74 | * kvmalloc - do allocation preferring kmalloc but falling back to vmalloc | ||
| 75 | * @size: size of allocation | ||
| 76 | * | ||
| 77 | * Return: allocated buffer or NULL if failed | ||
| 78 | * | ||
| 79 | * It is possible that policy being loaded from the user is larger than | ||
| 80 | * what can be allocated by kmalloc, in those cases fall back to vmalloc. | ||
| 81 | */ | ||
| 82 | void *kvmalloc(size_t size) | ||
| 83 | { | ||
| 84 | void *buffer = NULL; | ||
| 85 | |||
| 86 | if (size == 0) | ||
| 87 | return NULL; | ||
| 88 | |||
| 89 | /* do not attempt kmalloc if we need more than 16 pages at once */ | ||
| 90 | if (size <= (16*PAGE_SIZE)) | ||
| 91 | buffer = kmalloc(size, GFP_NOIO | __GFP_NOWARN); | ||
| 92 | if (!buffer) { | ||
| 93 | /* see kvfree for why size must be at least work_struct size | ||
| 94 | * when allocated via vmalloc | ||
| 95 | */ | ||
| 96 | if (size < sizeof(struct work_struct)) | ||
| 97 | size = sizeof(struct work_struct); | ||
| 98 | buffer = vmalloc(size); | ||
| 99 | } | ||
| 100 | return buffer; | ||
| 101 | } | ||
| 102 | |||
| 103 | /** | ||
| 104 | * do_vfree - workqueue routine for freeing vmalloced memory | ||
| 105 | * @work: data to be freed | ||
| 106 | * | ||
| 107 | * The work_struct is overlaid to the data being freed, as at the point | ||
| 108 | * the work is scheduled the data is no longer valid, be its freeing | ||
| 109 | * needs to be delayed until safe. | ||
| 110 | */ | ||
| 111 | static void do_vfree(struct work_struct *work) | ||
| 112 | { | ||
| 113 | vfree(work); | ||
| 114 | } | ||
| 115 | |||
| 116 | /** | ||
| 117 | * kvfree - free an allocation do by kvmalloc | ||
| 118 | * @buffer: buffer to free (MAYBE_NULL) | ||
| 119 | * | ||
| 120 | * Free a buffer allocated by kvmalloc | ||
| 121 | */ | ||
| 122 | void kvfree(void *buffer) | ||
| 123 | { | ||
| 124 | if (is_vmalloc_addr(buffer)) { | ||
| 125 | /* Data is no longer valid so just use the allocated space | ||
| 126 | * as the work_struct | ||
| 127 | */ | ||
| 128 | struct work_struct *work = (struct work_struct *) buffer; | ||
| 129 | INIT_WORK(work, do_vfree); | ||
| 130 | schedule_work(work); | ||
| 131 | } else | ||
| 132 | kfree(buffer); | ||
| 133 | } | ||
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c new file mode 100644 index 000000000000..8db33a8b50c4 --- /dev/null +++ b/security/apparmor/lsm.c | |||
| @@ -0,0 +1,938 @@ | |||
| 1 | /* | ||
| 2 | * AppArmor security module | ||
| 3 | * | ||
| 4 | * This file contains AppArmor LSM hooks. | ||
| 5 | * | ||
| 6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
| 7 | * Copyright 2009-2010 Canonical Ltd. | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or | ||
| 10 | * modify it under the terms of the GNU General Public License as | ||
| 11 | * published by the Free Software Foundation, version 2 of the | ||
| 12 | * License. | ||
| 13 | */ | ||
| 14 | |||
| 15 | #include <linux/security.h> | ||
| 16 | #include <linux/moduleparam.h> | ||
| 17 | #include <linux/mm.h> | ||
| 18 | #include <linux/mman.h> | ||
| 19 | #include <linux/mount.h> | ||
| 20 | #include <linux/namei.h> | ||
| 21 | #include <linux/ptrace.h> | ||
| 22 | #include <linux/ctype.h> | ||
| 23 | #include <linux/sysctl.h> | ||
| 24 | #include <linux/audit.h> | ||
| 25 | #include <net/sock.h> | ||
| 26 | |||
| 27 | #include "include/apparmor.h" | ||
| 28 | #include "include/apparmorfs.h" | ||
| 29 | #include "include/audit.h" | ||
| 30 | #include "include/capability.h" | ||
| 31 | #include "include/context.h" | ||
| 32 | #include "include/file.h" | ||
| 33 | #include "include/ipc.h" | ||
| 34 | #include "include/path.h" | ||
| 35 | #include "include/policy.h" | ||
| 36 | #include "include/procattr.h" | ||
| 37 | |||
| 38 | /* Flag indicating whether initialization completed */ | ||
| 39 | int apparmor_initialized __initdata; | ||
| 40 | |||
| 41 | /* | ||
| 42 | * LSM hook functions | ||
| 43 | */ | ||
| 44 | |||
| 45 | /* | ||
| 46 | * free the associated aa_task_cxt and put its profiles | ||
| 47 | */ | ||
| 48 | static void apparmor_cred_free(struct cred *cred) | ||
| 49 | { | ||
| 50 | aa_free_task_context(cred->security); | ||
| 51 | cred->security = NULL; | ||
| 52 | } | ||
| 53 | |||
| 54 | /* | ||
| 55 | * allocate the apparmor part of blank credentials | ||
| 56 | */ | ||
| 57 | static int apparmor_cred_alloc_blank(struct cred *cred, gfp_t gfp) | ||
| 58 | { | ||
| 59 | /* freed by apparmor_cred_free */ | ||
| 60 | struct aa_task_cxt *cxt = aa_alloc_task_context(gfp); | ||
| 61 | if (!cxt) | ||
| 62 | return -ENOMEM; | ||
| 63 | |||
| 64 | cred->security = cxt; | ||
| 65 | return 0; | ||
| 66 | } | ||
| 67 | |||
| 68 | /* | ||
| 69 | * prepare new aa_task_cxt for modification by prepare_cred block | ||
| 70 | */ | ||
| 71 | static int apparmor_cred_prepare(struct cred *new, const struct cred *old, | ||
| 72 | gfp_t gfp) | ||
| 73 | { | ||
| 74 | /* freed by apparmor_cred_free */ | ||
| 75 | struct aa_task_cxt *cxt = aa_alloc_task_context(gfp); | ||
| 76 | if (!cxt) | ||
| 77 | return -ENOMEM; | ||
| 78 | |||
| 79 | aa_dup_task_context(cxt, old->security); | ||
| 80 | new->security = cxt; | ||
| 81 | return 0; | ||
| 82 | } | ||
| 83 | |||
| 84 | /* | ||
| 85 | * transfer the apparmor data to a blank set of creds | ||
| 86 | */ | ||
| 87 | static void apparmor_cred_transfer(struct cred *new, const struct cred *old) | ||
| 88 | { | ||
| 89 | const struct aa_task_cxt *old_cxt = old->security; | ||
| 90 | struct aa_task_cxt *new_cxt = new->security; | ||
| 91 | |||
| 92 | aa_dup_task_context(new_cxt, old_cxt); | ||
| 93 | } | ||
| 94 | |||
| 95 | static int apparmor_ptrace_access_check(struct task_struct *child, | ||
| 96 | unsigned int mode) | ||
| 97 | { | ||
| 98 | int error = cap_ptrace_access_check(child, mode); | ||
| 99 | if (error) | ||
| 100 | return error; | ||
| 101 | |||
| 102 | return aa_ptrace(current, child, mode); | ||
| 103 | } | ||
| 104 | |||
| 105 | static int apparmor_ptrace_traceme(struct task_struct *parent) | ||
| 106 | { | ||
| 107 | int error = cap_ptrace_traceme(parent); | ||
| 108 | if (error) | ||
| 109 | return error; | ||
| 110 | |||
| 111 | return aa_ptrace(parent, current, PTRACE_MODE_ATTACH); | ||
| 112 | } | ||
| 113 | |||
| 114 | /* Derived from security/commoncap.c:cap_capget */ | ||
| 115 | static int apparmor_capget(struct task_struct *target, kernel_cap_t *effective, | ||
| 116 | kernel_cap_t *inheritable, kernel_cap_t *permitted) | ||
| 117 | { | ||
| 118 | struct aa_profile *profile; | ||
| 119 | const struct cred *cred; | ||
| 120 | |||
| 121 | rcu_read_lock(); | ||
| 122 | cred = __task_cred(target); | ||
| 123 | profile = aa_cred_profile(cred); | ||
| 124 | |||
| 125 | *effective = cred->cap_effective; | ||
| 126 | *inheritable = cred->cap_inheritable; | ||
| 127 | *permitted = cred->cap_permitted; | ||
| 128 | |||
| 129 | if (!unconfined(profile)) { | ||
| 130 | *effective = cap_intersect(*effective, profile->caps.allow); | ||
| 131 | *permitted = cap_intersect(*permitted, profile->caps.allow); | ||
| 132 | } | ||
| 133 | rcu_read_unlock(); | ||
| 134 | |||
| 135 | return 0; | ||
| 136 | } | ||
| 137 | |||
| 138 | static int apparmor_capable(struct task_struct *task, const struct cred *cred, | ||
| 139 | int cap, int audit) | ||
| 140 | { | ||
| 141 | struct aa_profile *profile; | ||
| 142 | /* cap_capable returns 0 on success, else -EPERM */ | ||
| 143 | int error = cap_capable(task, cred, cap, audit); | ||
| 144 | if (!error) { | ||
| 145 | profile = aa_cred_profile(cred); | ||
| 146 | if (!unconfined(profile)) | ||
| 147 | error = aa_capable(task, profile, cap, audit); | ||
| 148 | } | ||
| 149 | return error; | ||
| 150 | } | ||
| 151 | |||
| 152 | /** | ||
| 153 | * common_perm - basic common permission check wrapper fn for paths | ||
| 154 | * @op: operation being checked | ||
| 155 | * @path: path to check permission of (NOT NULL) | ||
| 156 | * @mask: requested permissions mask | ||
| 157 | * @cond: conditional info for the permission request (NOT NULL) | ||
| 158 | * | ||
| 159 | * Returns: %0 else error code if error or permission denied | ||
| 160 | */ | ||
| 161 | static int common_perm(int op, struct path *path, u32 mask, | ||
| 162 | struct path_cond *cond) | ||
| 163 | { | ||
| 164 | struct aa_profile *profile; | ||
| 165 | int error = 0; | ||
| 166 | |||
| 167 | profile = __aa_current_profile(); | ||
| 168 | if (!unconfined(profile)) | ||
| 169 | error = aa_path_perm(op, profile, path, 0, mask, cond); | ||
| 170 | |||
| 171 | return error; | ||
| 172 | } | ||
| 173 | |||
| 174 | /** | ||
| 175 | * common_perm_dir_dentry - common permission wrapper when path is dir, dentry | ||
| 176 | * @op: operation being checked | ||
| 177 | * @dir: directory of the dentry (NOT NULL) | ||
| 178 | * @dentry: dentry to check (NOT NULL) | ||
| 179 | * @mask: requested permissions mask | ||
| 180 | * @cond: conditional info for the permission request (NOT NULL) | ||
| 181 | * | ||
| 182 | * Returns: %0 else error code if error or permission denied | ||
| 183 | */ | ||
| 184 | static int common_perm_dir_dentry(int op, struct path *dir, | ||
| 185 | struct dentry *dentry, u32 mask, | ||
| 186 | struct path_cond *cond) | ||
| 187 | { | ||
| 188 | struct path path = { dir->mnt, dentry }; | ||
| 189 | |||
| 190 | return common_perm(op, &path, mask, cond); | ||
| 191 | } | ||
| 192 | |||
| 193 | /** | ||
| 194 | * common_perm_mnt_dentry - common permission wrapper when mnt, dentry | ||
| 195 | * @op: operation being checked | ||
| 196 | * @mnt: mount point of dentry (NOT NULL) | ||
| 197 | * @dentry: dentry to check (NOT NULL) | ||
| 198 | * @mask: requested permissions mask | ||
| 199 | * | ||
| 200 | * Returns: %0 else error code if error or permission denied | ||
| 201 | */ | ||
| 202 | static int common_perm_mnt_dentry(int op, struct vfsmount *mnt, | ||
| 203 | struct dentry *dentry, u32 mask) | ||
| 204 | { | ||
| 205 | struct path path = { mnt, dentry }; | ||
| 206 | struct path_cond cond = { dentry->d_inode->i_uid, | ||
| 207 | dentry->d_inode->i_mode | ||
| 208 | }; | ||
| 209 | |||
| 210 | return common_perm(op, &path, mask, &cond); | ||
| 211 | } | ||
| 212 | |||
| 213 | /** | ||
| 214 | * common_perm_rm - common permission wrapper for operations doing rm | ||
| 215 | * @op: operation being checked | ||
| 216 | * @dir: directory that the dentry is in (NOT NULL) | ||
| 217 | * @dentry: dentry being rm'd (NOT NULL) | ||
| 218 | * @mask: requested permission mask | ||
| 219 | * | ||
| 220 | * Returns: %0 else error code if error or permission denied | ||
| 221 | */ | ||
| 222 | static int common_perm_rm(int op, struct path *dir, | ||
| 223 | struct dentry *dentry, u32 mask) | ||
| 224 | { | ||
| 225 | struct inode *inode = dentry->d_inode; | ||
| 226 | struct path_cond cond = { }; | ||
| 227 | |||
| 228 | if (!inode || !dir->mnt || !mediated_filesystem(inode)) | ||
| 229 | return 0; | ||
| 230 | |||
| 231 | cond.uid = inode->i_uid; | ||
| 232 | cond.mode = inode->i_mode; | ||
| 233 | |||
| 234 | return common_perm_dir_dentry(op, dir, dentry, mask, &cond); | ||
| 235 | } | ||
| 236 | |||
| 237 | /** | ||
| 238 | * common_perm_create - common permission wrapper for operations doing create | ||
| 239 | * @op: operation being checked | ||
| 240 | * @dir: directory that dentry will be created in (NOT NULL) | ||
| 241 | * @dentry: dentry to create (NOT NULL) | ||
| 242 | * @mask: request permission mask | ||
| 243 | * @mode: created file mode | ||
| 244 | * | ||
| 245 | * Returns: %0 else error code if error or permission denied | ||
| 246 | */ | ||
| 247 | static int common_perm_create(int op, struct path *dir, struct dentry *dentry, | ||
| 248 | u32 mask, umode_t mode) | ||
| 249 | { | ||
| 250 | struct path_cond cond = { current_fsuid(), mode }; | ||
| 251 | |||
| 252 | if (!dir->mnt || !mediated_filesystem(dir->dentry->d_inode)) | ||
| 253 | return 0; | ||
| 254 | |||
| 255 | return common_perm_dir_dentry(op, dir, dentry, mask, &cond); | ||
| 256 | } | ||
| 257 | |||
| 258 | static int apparmor_path_unlink(struct path *dir, struct dentry *dentry) | ||
| 259 | { | ||
| 260 | return common_perm_rm(OP_UNLINK, dir, dentry, AA_MAY_DELETE); | ||
| 261 | } | ||
| 262 | |||
| 263 | static int apparmor_path_mkdir(struct path *dir, struct dentry *dentry, | ||
| 264 | int mode) | ||
| 265 | { | ||
| 266 | return common_perm_create(OP_MKDIR, dir, dentry, AA_MAY_CREATE, | ||
| 267 | S_IFDIR); | ||
| 268 | } | ||
| 269 | |||
| 270 | static int apparmor_path_rmdir(struct path *dir, struct dentry *dentry) | ||
| 271 | { | ||
| 272 | return common_perm_rm(OP_RMDIR, dir, dentry, AA_MAY_DELETE); | ||
| 273 | } | ||
| 274 | |||
| 275 | static int apparmor_path_mknod(struct path *dir, struct dentry *dentry, | ||
| 276 | int mode, unsigned int dev) | ||
| 277 | { | ||
| 278 | return common_perm_create(OP_MKNOD, dir, dentry, AA_MAY_CREATE, mode); | ||
| 279 | } | ||
| 280 | |||
| 281 | static int apparmor_path_truncate(struct path *path) | ||
| 282 | { | ||
| 283 | struct path_cond cond = { path->dentry->d_inode->i_uid, | ||
| 284 | path->dentry->d_inode->i_mode | ||
| 285 | }; | ||
| 286 | |||
| 287 | if (!path->mnt || !mediated_filesystem(path->dentry->d_inode)) | ||
| 288 | return 0; | ||
| 289 | |||
| 290 | return common_perm(OP_TRUNC, path, MAY_WRITE | AA_MAY_META_WRITE, | ||
| 291 | &cond); | ||
| 292 | } | ||
| 293 | |||
| 294 | static int apparmor_path_symlink(struct path *dir, struct dentry *dentry, | ||
| 295 | const char *old_name) | ||
| 296 | { | ||
| 297 | return common_perm_create(OP_SYMLINK, dir, dentry, AA_MAY_CREATE, | ||
| 298 | S_IFLNK); | ||
| 299 | } | ||
| 300 | |||
| 301 | static int apparmor_path_link(struct dentry *old_dentry, struct path *new_dir, | ||
| 302 | struct dentry *new_dentry) | ||
| 303 | { | ||
| 304 | struct aa_profile *profile; | ||
| 305 | int error = 0; | ||
| 306 | |||
| 307 | if (!mediated_filesystem(old_dentry->d_inode)) | ||
| 308 | return 0; | ||
| 309 | |||
| 310 | profile = aa_current_profile(); | ||
| 311 | if (!unconfined(profile)) | ||
| 312 | error = aa_path_link(profile, old_dentry, new_dir, new_dentry); | ||
| 313 | return error; | ||
| 314 | } | ||
| 315 | |||
| 316 | static int apparmor_path_rename(struct path *old_dir, struct dentry *old_dentry, | ||
| 317 | struct path *new_dir, struct dentry *new_dentry) | ||
| 318 | { | ||
| 319 | struct aa_profile *profile; | ||
| 320 | int error = 0; | ||
| 321 | |||
| 322 | if (!mediated_filesystem(old_dentry->d_inode)) | ||
| 323 | return 0; | ||
| 324 | |||
| 325 | profile = aa_current_profile(); | ||
| 326 | if (!unconfined(profile)) { | ||
| 327 | struct path old_path = { old_dir->mnt, old_dentry }; | ||
| 328 | struct path new_path = { new_dir->mnt, new_dentry }; | ||
| 329 | struct path_cond cond = { old_dentry->d_inode->i_uid, | ||
| 330 | old_dentry->d_inode->i_mode | ||
| 331 | }; | ||
| 332 | |||
| 333 | error = aa_path_perm(OP_RENAME_SRC, profile, &old_path, 0, | ||
| 334 | MAY_READ | AA_MAY_META_READ | MAY_WRITE | | ||
| 335 | AA_MAY_META_WRITE | AA_MAY_DELETE, | ||
| 336 | &cond); | ||
| 337 | if (!error) | ||
| 338 | error = aa_path_perm(OP_RENAME_DEST, profile, &new_path, | ||
| 339 | 0, MAY_WRITE | AA_MAY_META_WRITE | | ||
| 340 | AA_MAY_CREATE, &cond); | ||
| 341 | |||
| 342 | } | ||
| 343 | return error; | ||
| 344 | } | ||
| 345 | |||
| 346 | static int apparmor_path_chmod(struct dentry *dentry, struct vfsmount *mnt, | ||
| 347 | mode_t mode) | ||
| 348 | { | ||
| 349 | if (!mediated_filesystem(dentry->d_inode)) | ||
| 350 | return 0; | ||
| 351 | |||
| 352 | return common_perm_mnt_dentry(OP_CHMOD, mnt, dentry, AA_MAY_CHMOD); | ||
| 353 | } | ||
| 354 | |||
| 355 | static int apparmor_path_chown(struct path *path, uid_t uid, gid_t gid) | ||
| 356 | { | ||
| 357 | struct path_cond cond = { path->dentry->d_inode->i_uid, | ||
| 358 | path->dentry->d_inode->i_mode | ||
| 359 | }; | ||
| 360 | |||
| 361 | if (!mediated_filesystem(path->dentry->d_inode)) | ||
| 362 | return 0; | ||
| 363 | |||
| 364 | return common_perm(OP_CHOWN, path, AA_MAY_CHOWN, &cond); | ||
| 365 | } | ||
| 366 | |||
| 367 | static int apparmor_inode_getattr(struct vfsmount *mnt, struct dentry *dentry) | ||
| 368 | { | ||
| 369 | if (!mediated_filesystem(dentry->d_inode)) | ||
| 370 | return 0; | ||
| 371 | |||
| 372 | return common_perm_mnt_dentry(OP_GETATTR, mnt, dentry, | ||
| 373 | AA_MAY_META_READ); | ||
| 374 | } | ||
| 375 | |||
| 376 | static int apparmor_dentry_open(struct file *file, const struct cred *cred) | ||
| 377 | { | ||
| 378 | struct aa_file_cxt *fcxt = file->f_security; | ||
| 379 | struct aa_profile *profile; | ||
| 380 | int error = 0; | ||
| 381 | |||
| 382 | if (!mediated_filesystem(file->f_path.dentry->d_inode)) | ||
| 383 | return 0; | ||
| 384 | |||
| 385 | /* If in exec, permission is handled by bprm hooks. | ||
| 386 | * Cache permissions granted by the previous exec check, with | ||
| 387 | * implicit read and executable mmap which are required to | ||
| 388 | * actually execute the image. | ||
| 389 | */ | ||
| 390 | if (current->in_execve) { | ||
| 391 | fcxt->allow = MAY_EXEC | MAY_READ | AA_EXEC_MMAP; | ||
| 392 | return 0; | ||
| 393 | } | ||
| 394 | |||
| 395 | profile = aa_cred_profile(cred); | ||
| 396 | if (!unconfined(profile)) { | ||
| 397 | struct inode *inode = file->f_path.dentry->d_inode; | ||
| 398 | struct path_cond cond = { inode->i_uid, inode->i_mode }; | ||
| 399 | |||
| 400 | error = aa_path_perm(OP_OPEN, profile, &file->f_path, 0, | ||
| 401 | aa_map_file_to_perms(file), &cond); | ||
| 402 | /* todo cache full allowed permissions set and state */ | ||
| 403 | fcxt->allow = aa_map_file_to_perms(file); | ||
| 404 | } | ||
| 405 | |||
| 406 | return error; | ||
| 407 | } | ||
| 408 | |||
| 409 | static int apparmor_file_alloc_security(struct file *file) | ||
| 410 | { | ||
| 411 | /* freed by apparmor_file_free_security */ | ||
| 412 | file->f_security = aa_alloc_file_context(GFP_KERNEL); | ||
| 413 | if (!file->f_security) | ||
| 414 | return -ENOMEM; | ||
| 415 | return 0; | ||
| 416 | |||
| 417 | } | ||
| 418 | |||
| 419 | static void apparmor_file_free_security(struct file *file) | ||
| 420 | { | ||
| 421 | struct aa_file_cxt *cxt = file->f_security; | ||
| 422 | |||
| 423 | aa_free_file_context(cxt); | ||
| 424 | } | ||
| 425 | |||
| 426 | static int common_file_perm(int op, struct file *file, u32 mask) | ||
| 427 | { | ||
| 428 | struct aa_file_cxt *fcxt = file->f_security; | ||
| 429 | struct aa_profile *profile, *fprofile = aa_cred_profile(file->f_cred); | ||
| 430 | int error = 0; | ||
| 431 | |||
| 432 | BUG_ON(!fprofile); | ||
| 433 | |||
| 434 | if (!file->f_path.mnt || | ||
| 435 | !mediated_filesystem(file->f_path.dentry->d_inode)) | ||
| 436 | return 0; | ||
| 437 | |||
| 438 | profile = __aa_current_profile(); | ||
| 439 | |||
| 440 | /* revalidate access, if task is unconfined, or the cached cred | ||
| 441 | * doesn't match or if the request is for more permissions than | ||
| 442 | * was granted. | ||
| 443 | * | ||
| 444 | * Note: the test for !unconfined(fprofile) is to handle file | ||
| 445 | * delegation from unconfined tasks | ||
| 446 | */ | ||
| 447 | if (!unconfined(profile) && !unconfined(fprofile) && | ||
| 448 | ((fprofile != profile) || (mask & ~fcxt->allow))) | ||
| 449 | error = aa_file_perm(op, profile, file, mask); | ||
| 450 | |||
| 451 | return error; | ||
| 452 | } | ||
| 453 | |||
| 454 | static int apparmor_file_permission(struct file *file, int mask) | ||
| 455 | { | ||
| 456 | return common_file_perm(OP_FPERM, file, mask); | ||
| 457 | } | ||
| 458 | |||
| 459 | static int apparmor_file_lock(struct file *file, unsigned int cmd) | ||
| 460 | { | ||
| 461 | u32 mask = AA_MAY_LOCK; | ||
| 462 | |||
| 463 | if (cmd == F_WRLCK) | ||
| 464 | mask |= MAY_WRITE; | ||
| 465 | |||
| 466 | return common_file_perm(OP_FLOCK, file, mask); | ||
| 467 | } | ||
| 468 | |||
| 469 | static int common_mmap(int op, struct file *file, unsigned long prot, | ||
| 470 | unsigned long flags) | ||
| 471 | { | ||
| 472 | struct dentry *dentry; | ||
| 473 | int mask = 0; | ||
| 474 | |||
| 475 | if (!file || !file->f_security) | ||
| 476 | return 0; | ||
| 477 | |||
| 478 | if (prot & PROT_READ) | ||
| 479 | mask |= MAY_READ; | ||
| 480 | /* | ||
| 481 | * Private mappings don't require write perms since they don't | ||
| 482 | * write back to the files | ||
| 483 | */ | ||
| 484 | if ((prot & PROT_WRITE) && !(flags & MAP_PRIVATE)) | ||
| 485 | mask |= MAY_WRITE; | ||
| 486 | if (prot & PROT_EXEC) | ||
| 487 | mask |= AA_EXEC_MMAP; | ||
| 488 | |||
| 489 | dentry = file->f_path.dentry; | ||
| 490 | return common_file_perm(op, file, mask); | ||
| 491 | } | ||
| 492 | |||
| 493 | static int apparmor_file_mmap(struct file *file, unsigned long reqprot, | ||
| 494 | unsigned long prot, unsigned long flags, | ||
| 495 | unsigned long addr, unsigned long addr_only) | ||
| 496 | { | ||
| 497 | int rc = 0; | ||
| 498 | |||
| 499 | /* do DAC check */ | ||
| 500 | rc = cap_file_mmap(file, reqprot, prot, flags, addr, addr_only); | ||
| 501 | if (rc || addr_only) | ||
| 502 | return rc; | ||
| 503 | |||
| 504 | return common_mmap(OP_FMMAP, file, prot, flags); | ||
| 505 | } | ||
| 506 | |||
| 507 | static int apparmor_file_mprotect(struct vm_area_struct *vma, | ||
| 508 | unsigned long reqprot, unsigned long prot) | ||
| 509 | { | ||
| 510 | return common_mmap(OP_FMPROT, vma->vm_file, prot, | ||
| 511 | !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0); | ||
| 512 | } | ||
| 513 | |||
| 514 | static int apparmor_getprocattr(struct task_struct *task, char *name, | ||
| 515 | char **value) | ||
| 516 | { | ||
| 517 | int error = -ENOENT; | ||
| 518 | struct aa_profile *profile; | ||
| 519 | /* released below */ | ||
| 520 | const struct cred *cred = get_task_cred(task); | ||
| 521 | struct aa_task_cxt *cxt = cred->security; | ||
| 522 | profile = aa_cred_profile(cred); | ||
| 523 | |||
| 524 | if (strcmp(name, "current") == 0) | ||
| 525 | error = aa_getprocattr(aa_newest_version(cxt->profile), | ||
| 526 | value); | ||
| 527 | else if (strcmp(name, "prev") == 0 && cxt->previous) | ||
| 528 | error = aa_getprocattr(aa_newest_version(cxt->previous), | ||
| 529 | value); | ||
| 530 | else if (strcmp(name, "exec") == 0 && cxt->onexec) | ||
| 531 | error = aa_getprocattr(aa_newest_version(cxt->onexec), | ||
| 532 | value); | ||
| 533 | else | ||
| 534 | error = -EINVAL; | ||
| 535 | |||
| 536 | put_cred(cred); | ||
| 537 | |||
| 538 | return error; | ||
| 539 | } | ||
| 540 | |||
| 541 | static int apparmor_setprocattr(struct task_struct *task, char *name, | ||
| 542 | void *value, size_t size) | ||
| 543 | { | ||
| 544 | char *command, *args = value; | ||
| 545 | size_t arg_size; | ||
| 546 | int error; | ||
| 547 | |||
| 548 | if (size == 0) | ||
| 549 | return -EINVAL; | ||
| 550 | /* args points to a PAGE_SIZE buffer, AppArmor requires that | ||
| 551 | * the buffer must be null terminated or have size <= PAGE_SIZE -1 | ||
| 552 | * so that AppArmor can null terminate them | ||
| 553 | */ | ||
| 554 | if (args[size - 1] != '\0') { | ||
| 555 | if (size == PAGE_SIZE) | ||
| 556 | return -EINVAL; | ||
| 557 | args[size] = '\0'; | ||
| 558 | } | ||
| 559 | |||
| 560 | /* task can only write its own attributes */ | ||
| 561 | if (current != task) | ||
| 562 | return -EACCES; | ||
| 563 | |||
| 564 | args = value; | ||
| 565 | args = strim(args); | ||
| 566 | command = strsep(&args, " "); | ||
| 567 | if (!args) | ||
| 568 | return -EINVAL; | ||
| 569 | args = skip_spaces(args); | ||
| 570 | if (!*args) | ||
| 571 | return -EINVAL; | ||
| 572 | |||
| 573 | arg_size = size - (args - (char *) value); | ||
| 574 | if (strcmp(name, "current") == 0) { | ||
| 575 | if (strcmp(command, "changehat") == 0) { | ||
| 576 | error = aa_setprocattr_changehat(args, arg_size, | ||
| 577 | !AA_DO_TEST); | ||
| 578 | } else if (strcmp(command, "permhat") == 0) { | ||
| 579 | error = aa_setprocattr_changehat(args, arg_size, | ||
| 580 | AA_DO_TEST); | ||
| 581 | } else if (strcmp(command, "changeprofile") == 0) { | ||
| 582 | error = aa_setprocattr_changeprofile(args, !AA_ONEXEC, | ||
| 583 | !AA_DO_TEST); | ||
| 584 | } else if (strcmp(command, "permprofile") == 0) { | ||
| 585 | error = aa_setprocattr_changeprofile(args, !AA_ONEXEC, | ||
| 586 | AA_DO_TEST); | ||
| 587 | } else if (strcmp(command, "permipc") == 0) { | ||
| 588 | error = aa_setprocattr_permipc(args); | ||
| 589 | } else { | ||
| 590 | struct common_audit_data sa; | ||
| 591 | COMMON_AUDIT_DATA_INIT(&sa, NONE); | ||
| 592 | sa.aad.op = OP_SETPROCATTR; | ||
| 593 | sa.aad.info = name; | ||
| 594 | sa.aad.error = -EINVAL; | ||
| 595 | return aa_audit(AUDIT_APPARMOR_DENIED, NULL, GFP_KERNEL, | ||
| 596 | &sa, NULL); | ||
| 597 | } | ||
| 598 | } else if (strcmp(name, "exec") == 0) { | ||
| 599 | error = aa_setprocattr_changeprofile(args, AA_ONEXEC, | ||
| 600 | !AA_DO_TEST); | ||
| 601 | } else { | ||
| 602 | /* only support the "current" and "exec" process attributes */ | ||
| 603 | return -EINVAL; | ||
| 604 | } | ||
| 605 | if (!error) | ||
| 606 | error = size; | ||
| 607 | return error; | ||
| 608 | } | ||
| 609 | |||
| 610 | static int apparmor_task_setrlimit(unsigned int resource, | ||
| 611 | struct rlimit *new_rlim) | ||
| 612 | { | ||
| 613 | struct aa_profile *profile = aa_current_profile(); | ||
| 614 | int error = 0; | ||
| 615 | |||
| 616 | if (!unconfined(profile)) | ||
| 617 | error = aa_task_setrlimit(profile, resource, new_rlim); | ||
| 618 | |||
| 619 | return error; | ||
| 620 | } | ||
| 621 | |||
| 622 | static struct security_operations apparmor_ops = { | ||
| 623 | .name = "apparmor", | ||
| 624 | |||
| 625 | .ptrace_access_check = apparmor_ptrace_access_check, | ||
| 626 | .ptrace_traceme = apparmor_ptrace_traceme, | ||
| 627 | .capget = apparmor_capget, | ||
| 628 | .capable = apparmor_capable, | ||
| 629 | |||
| 630 | .path_link = apparmor_path_link, | ||
| 631 | .path_unlink = apparmor_path_unlink, | ||
| 632 | .path_symlink = apparmor_path_symlink, | ||
| 633 | .path_mkdir = apparmor_path_mkdir, | ||
| 634 | .path_rmdir = apparmor_path_rmdir, | ||
| 635 | .path_mknod = apparmor_path_mknod, | ||
| 636 | .path_rename = apparmor_path_rename, | ||
| 637 | .path_chmod = apparmor_path_chmod, | ||
| 638 | .path_chown = apparmor_path_chown, | ||
| 639 | .path_truncate = apparmor_path_truncate, | ||
| 640 | .dentry_open = apparmor_dentry_open, | ||
| 641 | .inode_getattr = apparmor_inode_getattr, | ||
| 642 | |||
| 643 | .file_permission = apparmor_file_permission, | ||
| 644 | .file_alloc_security = apparmor_file_alloc_security, | ||
| 645 | .file_free_security = apparmor_file_free_security, | ||
| 646 | .file_mmap = apparmor_file_mmap, | ||
| 647 | .file_mprotect = apparmor_file_mprotect, | ||
| 648 | .file_lock = apparmor_file_lock, | ||
| 649 | |||
| 650 | .getprocattr = apparmor_getprocattr, | ||
| 651 | .setprocattr = apparmor_setprocattr, | ||
| 652 | |||
| 653 | .cred_alloc_blank = apparmor_cred_alloc_blank, | ||
| 654 | .cred_free = apparmor_cred_free, | ||
| 655 | .cred_prepare = apparmor_cred_prepare, | ||
| 656 | .cred_transfer = apparmor_cred_transfer, | ||
| 657 | |||
| 658 | .bprm_set_creds = apparmor_bprm_set_creds, | ||
| 659 | .bprm_committing_creds = apparmor_bprm_committing_creds, | ||
| 660 | .bprm_committed_creds = apparmor_bprm_committed_creds, | ||
| 661 | .bprm_secureexec = apparmor_bprm_secureexec, | ||
| 662 | |||
| 663 | .task_setrlimit = apparmor_task_setrlimit, | ||
| 664 | }; | ||
| 665 | |||
| 666 | /* | ||
| 667 | * AppArmor sysfs module parameters | ||
| 668 | */ | ||
| 669 | |||
| 670 | static int param_set_aabool(const char *val, struct kernel_param *kp); | ||
| 671 | static int param_get_aabool(char *buffer, struct kernel_param *kp); | ||
| 672 | #define param_check_aabool(name, p) __param_check(name, p, int) | ||
| 673 | |||
| 674 | static int param_set_aauint(const char *val, struct kernel_param *kp); | ||
| 675 | static int param_get_aauint(char *buffer, struct kernel_param *kp); | ||
| 676 | #define param_check_aauint(name, p) __param_check(name, p, int) | ||
| 677 | |||
| 678 | static int param_set_aalockpolicy(const char *val, struct kernel_param *kp); | ||
| 679 | static int param_get_aalockpolicy(char *buffer, struct kernel_param *kp); | ||
| 680 | #define param_check_aalockpolicy(name, p) __param_check(name, p, int) | ||
| 681 | |||
| 682 | static int param_set_audit(const char *val, struct kernel_param *kp); | ||
| 683 | static int param_get_audit(char *buffer, struct kernel_param *kp); | ||
| 684 | #define param_check_audit(name, p) __param_check(name, p, int) | ||
| 685 | |||
| 686 | static int param_set_mode(const char *val, struct kernel_param *kp); | ||
| 687 | static int param_get_mode(char *buffer, struct kernel_param *kp); | ||
| 688 | #define param_check_mode(name, p) __param_check(name, p, int) | ||
| 689 | |||
| 690 | /* Flag values, also controllable via /sys/module/apparmor/parameters | ||
| 691 | * We define special types as we want to do additional mediation. | ||
| 692 | */ | ||
| 693 | |||
| 694 | /* AppArmor global enforcement switch - complain, enforce, kill */ | ||
| 695 | enum profile_mode aa_g_profile_mode = APPARMOR_ENFORCE; | ||
| 696 | module_param_call(mode, param_set_mode, param_get_mode, | ||
| 697 | &aa_g_profile_mode, S_IRUSR | S_IWUSR); | ||
| 698 | |||
| 699 | /* Debug mode */ | ||
| 700 | int aa_g_debug; | ||
| 701 | module_param_named(debug, aa_g_debug, aabool, S_IRUSR | S_IWUSR); | ||
| 702 | |||
| 703 | /* Audit mode */ | ||
| 704 | enum audit_mode aa_g_audit; | ||
| 705 | module_param_call(audit, param_set_audit, param_get_audit, | ||
| 706 | &aa_g_audit, S_IRUSR | S_IWUSR); | ||
| 707 | |||
| 708 | /* Determines if audit header is included in audited messages. This | ||
| 709 | * provides more context if the audit daemon is not running | ||
| 710 | */ | ||
| 711 | int aa_g_audit_header = 1; | ||
| 712 | module_param_named(audit_header, aa_g_audit_header, aabool, | ||
| 713 | S_IRUSR | S_IWUSR); | ||
| 714 | |||
| 715 | /* lock out loading/removal of policy | ||
| 716 | * TODO: add in at boot loading of policy, which is the only way to | ||
| 717 | * load policy, if lock_policy is set | ||
| 718 | */ | ||
| 719 | int aa_g_lock_policy; | ||
| 720 | module_param_named(lock_policy, aa_g_lock_policy, aalockpolicy, | ||
| 721 | S_IRUSR | S_IWUSR); | ||
| 722 | |||
| 723 | /* Syscall logging mode */ | ||
| 724 | int aa_g_logsyscall; | ||
| 725 | module_param_named(logsyscall, aa_g_logsyscall, aabool, S_IRUSR | S_IWUSR); | ||
| 726 | |||
| 727 | /* Maximum pathname length before accesses will start getting rejected */ | ||
| 728 | unsigned int aa_g_path_max = 2 * PATH_MAX; | ||
| 729 | module_param_named(path_max, aa_g_path_max, aauint, S_IRUSR | S_IWUSR); | ||
| 730 | |||
| 731 | /* Determines how paranoid loading of policy is and how much verification | ||
| 732 | * on the loaded policy is done. | ||
| 733 | */ | ||
| 734 | int aa_g_paranoid_load = 1; | ||
| 735 | module_param_named(paranoid_load, aa_g_paranoid_load, aabool, | ||
| 736 | S_IRUSR | S_IWUSR); | ||
| 737 | |||
| 738 | /* Boot time disable flag */ | ||
| 739 | static unsigned int apparmor_enabled = CONFIG_SECURITY_APPARMOR_BOOTPARAM_VALUE; | ||
| 740 | module_param_named(enabled, apparmor_enabled, aabool, S_IRUSR); | ||
| 741 | |||
| 742 | static int __init apparmor_enabled_setup(char *str) | ||
| 743 | { | ||
| 744 | unsigned long enabled; | ||
| 745 | int error = strict_strtoul(str, 0, &enabled); | ||
| 746 | if (!error) | ||
| 747 | apparmor_enabled = enabled ? 1 : 0; | ||
| 748 | return 1; | ||
| 749 | } | ||
| 750 | |||
| 751 | __setup("apparmor=", apparmor_enabled_setup); | ||
| 752 | |||
| 753 | /* set global flag turning off the ability to load policy */ | ||
| 754 | static int param_set_aalockpolicy(const char *val, struct kernel_param *kp) | ||
| 755 | { | ||
| 756 | if (!capable(CAP_MAC_ADMIN)) | ||
| 757 | return -EPERM; | ||
| 758 | if (aa_g_lock_policy) | ||
| 759 | return -EACCES; | ||
| 760 | return param_set_bool(val, kp); | ||
| 761 | } | ||
| 762 | |||
| 763 | static int param_get_aalockpolicy(char *buffer, struct kernel_param *kp) | ||
| 764 | { | ||
| 765 | if (!capable(CAP_MAC_ADMIN)) | ||
| 766 | return -EPERM; | ||
| 767 | return param_get_bool(buffer, kp); | ||
| 768 | } | ||
| 769 | |||
| 770 | static int param_set_aabool(const char *val, struct kernel_param *kp) | ||
| 771 | { | ||
| 772 | if (!capable(CAP_MAC_ADMIN)) | ||
| 773 | return -EPERM; | ||
| 774 | return param_set_bool(val, kp); | ||
| 775 | } | ||
| 776 | |||
| 777 | static int param_get_aabool(char *buffer, struct kernel_param *kp) | ||
| 778 | { | ||
| 779 | if (!capable(CAP_MAC_ADMIN)) | ||
| 780 | return -EPERM; | ||
| 781 | return param_get_bool(buffer, kp); | ||
| 782 | } | ||
| 783 | |||
| 784 | static int param_set_aauint(const char *val, struct kernel_param *kp) | ||
| 785 | { | ||
| 786 | if (!capable(CAP_MAC_ADMIN)) | ||
| 787 | return -EPERM; | ||
| 788 | return param_set_uint(val, kp); | ||
| 789 | } | ||
| 790 | |||
| 791 | static int param_get_aauint(char *buffer, struct kernel_param *kp) | ||
| 792 | { | ||
| 793 | if (!capable(CAP_MAC_ADMIN)) | ||
| 794 | return -EPERM; | ||
| 795 | return param_get_uint(buffer, kp); | ||
| 796 | } | ||
| 797 | |||
| 798 | static int param_get_audit(char *buffer, struct kernel_param *kp) | ||
| 799 | { | ||
| 800 | if (!capable(CAP_MAC_ADMIN)) | ||
| 801 | return -EPERM; | ||
| 802 | |||
| 803 | if (!apparmor_enabled) | ||
| 804 | return -EINVAL; | ||
| 805 | |||
| 806 | return sprintf(buffer, "%s", audit_mode_names[aa_g_audit]); | ||
| 807 | } | ||
| 808 | |||
| 809 | static int param_set_audit(const char *val, struct kernel_param *kp) | ||
| 810 | { | ||
| 811 | int i; | ||
| 812 | if (!capable(CAP_MAC_ADMIN)) | ||
| 813 | return -EPERM; | ||
| 814 | |||
| 815 | if (!apparmor_enabled) | ||
| 816 | return -EINVAL; | ||
| 817 | |||
| 818 | if (!val) | ||
| 819 | return -EINVAL; | ||
| 820 | |||
| 821 | for (i = 0; i < AUDIT_MAX_INDEX; i++) { | ||
| 822 | if (strcmp(val, audit_mode_names[i]) == 0) { | ||
| 823 | aa_g_audit = i; | ||
| 824 | return 0; | ||
| 825 | } | ||
| 826 | } | ||
| 827 | |||
| 828 | return -EINVAL; | ||
| 829 | } | ||
| 830 | |||
| 831 | static int param_get_mode(char *buffer, struct kernel_param *kp) | ||
| 832 | { | ||
| 833 | if (!capable(CAP_MAC_ADMIN)) | ||
| 834 | return -EPERM; | ||
| 835 | |||
| 836 | if (!apparmor_enabled) | ||
| 837 | return -EINVAL; | ||
| 838 | |||
| 839 | return sprintf(buffer, "%s", profile_mode_names[aa_g_profile_mode]); | ||
| 840 | } | ||
| 841 | |||
| 842 | static int param_set_mode(const char *val, struct kernel_param *kp) | ||
| 843 | { | ||
| 844 | int i; | ||
| 845 | if (!capable(CAP_MAC_ADMIN)) | ||
| 846 | return -EPERM; | ||
| 847 | |||
| 848 | if (!apparmor_enabled) | ||
| 849 | return -EINVAL; | ||
| 850 | |||
| 851 | if (!val) | ||
| 852 | return -EINVAL; | ||
| 853 | |||
| 854 | for (i = 0; i < APPARMOR_NAMES_MAX_INDEX; i++) { | ||
| 855 | if (strcmp(val, profile_mode_names[i]) == 0) { | ||
| 856 | aa_g_profile_mode = i; | ||
| 857 | return 0; | ||
| 858 | } | ||
| 859 | } | ||
| 860 | |||
| 861 | return -EINVAL; | ||
| 862 | } | ||
| 863 | |||
| 864 | /* | ||
| 865 | * AppArmor init functions | ||
| 866 | */ | ||
| 867 | |||
| 868 | /** | ||
| 869 | * set_init_cxt - set a task context and profile on the first task. | ||
| 870 | * | ||
| 871 | * TODO: allow setting an alternate profile than unconfined | ||
| 872 | */ | ||
| 873 | static int __init set_init_cxt(void) | ||
| 874 | { | ||
| 875 | struct cred *cred = (struct cred *)current->real_cred; | ||
| 876 | struct aa_task_cxt *cxt; | ||
| 877 | |||
| 878 | cxt = aa_alloc_task_context(GFP_KERNEL); | ||
| 879 | if (!cxt) | ||
| 880 | return -ENOMEM; | ||
| 881 | |||
| 882 | cxt->profile = aa_get_profile(root_ns->unconfined); | ||
| 883 | cred->security = cxt; | ||
| 884 | |||
| 885 | return 0; | ||
| 886 | } | ||
| 887 | |||
| 888 | static int __init apparmor_init(void) | ||
| 889 | { | ||
| 890 | int error; | ||
| 891 | |||
| 892 | if (!apparmor_enabled || !security_module_enable(&apparmor_ops)) { | ||
| 893 | aa_info_message("AppArmor disabled by boot time parameter"); | ||
| 894 | apparmor_enabled = 0; | ||
| 895 | return 0; | ||
| 896 | } | ||
| 897 | |||
| 898 | error = aa_alloc_root_ns(); | ||
| 899 | if (error) { | ||
| 900 | AA_ERROR("Unable to allocate default profile namespace\n"); | ||
| 901 | goto alloc_out; | ||
| 902 | } | ||
| 903 | |||
| 904 | error = set_init_cxt(); | ||
| 905 | if (error) { | ||
| 906 | AA_ERROR("Failed to set context on init task\n"); | ||
| 907 | goto register_security_out; | ||
| 908 | } | ||
| 909 | |||
| 910 | error = register_security(&apparmor_ops); | ||
| 911 | if (error) { | ||
| 912 | AA_ERROR("Unable to register AppArmor\n"); | ||
| 913 | goto register_security_out; | ||
| 914 | } | ||
| 915 | |||
| 916 | /* Report that AppArmor successfully initialized */ | ||
| 917 | apparmor_initialized = 1; | ||
| 918 | if (aa_g_profile_mode == APPARMOR_COMPLAIN) | ||
| 919 | aa_info_message("AppArmor initialized: complain mode enabled"); | ||
| 920 | else if (aa_g_profile_mode == APPARMOR_KILL) | ||
| 921 | aa_info_message("AppArmor initialized: kill mode enabled"); | ||
| 922 | else | ||
| 923 | aa_info_message("AppArmor initialized"); | ||
| 924 | |||
| 925 | return error; | ||
| 926 | |||
| 927 | register_security_out: | ||
| 928 | aa_free_root_ns(); | ||
| 929 | |||
| 930 | alloc_out: | ||
| 931 | aa_destroy_aafs(); | ||
| 932 | |||
| 933 | apparmor_enabled = 0; | ||
| 934 | return error; | ||
| 935 | |||
| 936 | } | ||
| 937 | |||
| 938 | security_initcall(apparmor_init); | ||
diff --git a/security/apparmor/match.c b/security/apparmor/match.c new file mode 100644 index 000000000000..5cb4dc1f6992 --- /dev/null +++ b/security/apparmor/match.c | |||
| @@ -0,0 +1,353 @@ | |||
| 1 | /* | ||
| 2 | * AppArmor security module | ||
| 3 | * | ||
| 4 | * This file contains AppArmor dfa based regular expression matching engine | ||
| 5 | * | ||
| 6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
| 7 | * Copyright 2009-2010 Canonical Ltd. | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or | ||
| 10 | * modify it under the terms of the GNU General Public License as | ||
| 11 | * published by the Free Software Foundation, version 2 of the | ||
| 12 | * License. | ||
| 13 | */ | ||
| 14 | |||
| 15 | #include <linux/errno.h> | ||
| 16 | #include <linux/kernel.h> | ||
| 17 | #include <linux/mm.h> | ||
| 18 | #include <linux/slab.h> | ||
| 19 | #include <linux/vmalloc.h> | ||
| 20 | #include <linux/err.h> | ||
| 21 | #include <linux/kref.h> | ||
| 22 | |||
| 23 | #include "include/apparmor.h" | ||
| 24 | #include "include/match.h" | ||
| 25 | |||
| 26 | /** | ||
| 27 | * unpack_table - unpack a dfa table (one of accept, default, base, next check) | ||
| 28 | * @blob: data to unpack (NOT NULL) | ||
| 29 | * @bsize: size of blob | ||
| 30 | * | ||
| 31 | * Returns: pointer to table else NULL on failure | ||
| 32 | * | ||
| 33 | * NOTE: must be freed by kvfree (not kmalloc) | ||
| 34 | */ | ||
| 35 | static struct table_header *unpack_table(char *blob, size_t bsize) | ||
| 36 | { | ||
| 37 | struct table_header *table = NULL; | ||
| 38 | struct table_header th; | ||
| 39 | size_t tsize; | ||
| 40 | |||
| 41 | if (bsize < sizeof(struct table_header)) | ||
| 42 | goto out; | ||
| 43 | |||
| 44 | /* loaded td_id's start at 1, subtract 1 now to avoid doing | ||
| 45 | * it every time we use td_id as an index | ||
| 46 | */ | ||
| 47 | th.td_id = be16_to_cpu(*(u16 *) (blob)) - 1; | ||
| 48 | th.td_flags = be16_to_cpu(*(u16 *) (blob + 2)); | ||
| 49 | th.td_lolen = be32_to_cpu(*(u32 *) (blob + 8)); | ||
| 50 | blob += sizeof(struct table_header); | ||
| 51 | |||
| 52 | if (!(th.td_flags == YYTD_DATA16 || th.td_flags == YYTD_DATA32 || | ||
| 53 | th.td_flags == YYTD_DATA8)) | ||
| 54 | goto out; | ||
| 55 | |||
| 56 | tsize = table_size(th.td_lolen, th.td_flags); | ||
| 57 | if (bsize < tsize) | ||
| 58 | goto out; | ||
| 59 | |||
| 60 | table = kvmalloc(tsize); | ||
| 61 | if (table) { | ||
| 62 | *table = th; | ||
| 63 | if (th.td_flags == YYTD_DATA8) | ||
| 64 | UNPACK_ARRAY(table->td_data, blob, th.td_lolen, | ||
| 65 | u8, byte_to_byte); | ||
| 66 | else if (th.td_flags == YYTD_DATA16) | ||
| 67 | UNPACK_ARRAY(table->td_data, blob, th.td_lolen, | ||
| 68 | u16, be16_to_cpu); | ||
| 69 | else if (th.td_flags == YYTD_DATA32) | ||
| 70 | UNPACK_ARRAY(table->td_data, blob, th.td_lolen, | ||
| 71 | u32, be32_to_cpu); | ||
| 72 | else | ||
| 73 | goto fail; | ||
| 74 | } | ||
| 75 | |||
| 76 | out: | ||
| 77 | /* if table was vmalloced make sure the page tables are synced | ||
| 78 | * before it is used, as it goes live to all cpus. | ||
| 79 | */ | ||
| 80 | if (is_vmalloc_addr(table)) | ||
| 81 | vm_unmap_aliases(); | ||
| 82 | return table; | ||
| 83 | fail: | ||
| 84 | kvfree(table); | ||
| 85 | return NULL; | ||
| 86 | } | ||
| 87 | |||
| 88 | /** | ||
| 89 | * verify_dfa - verify that transitions and states in the tables are in bounds. | ||
| 90 | * @dfa: dfa to test (NOT NULL) | ||
| 91 | * @flags: flags controlling what type of accept table are acceptable | ||
| 92 | * | ||
| 93 | * Assumes dfa has gone through the first pass verification done by unpacking | ||
| 94 | * NOTE: this does not valid accept table values | ||
| 95 | * | ||
| 96 | * Returns: %0 else error code on failure to verify | ||
| 97 | */ | ||
| 98 | static int verify_dfa(struct aa_dfa *dfa, int flags) | ||
| 99 | { | ||
| 100 | size_t i, state_count, trans_count; | ||
| 101 | int error = -EPROTO; | ||
| 102 | |||
| 103 | /* check that required tables exist */ | ||
| 104 | if (!(dfa->tables[YYTD_ID_DEF] && | ||
| 105 | dfa->tables[YYTD_ID_BASE] && | ||
| 106 | dfa->tables[YYTD_ID_NXT] && dfa->tables[YYTD_ID_CHK])) | ||
| 107 | goto out; | ||
| 108 | |||
| 109 | /* accept.size == default.size == base.size */ | ||
| 110 | state_count = dfa->tables[YYTD_ID_BASE]->td_lolen; | ||
| 111 | if (ACCEPT1_FLAGS(flags)) { | ||
| 112 | if (!dfa->tables[YYTD_ID_ACCEPT]) | ||
| 113 | goto out; | ||
| 114 | if (state_count != dfa->tables[YYTD_ID_ACCEPT]->td_lolen) | ||
| 115 | goto out; | ||
| 116 | } | ||
| 117 | if (ACCEPT2_FLAGS(flags)) { | ||
| 118 | if (!dfa->tables[YYTD_ID_ACCEPT2]) | ||
| 119 | goto out; | ||
| 120 | if (state_count != dfa->tables[YYTD_ID_ACCEPT2]->td_lolen) | ||
| 121 | goto out; | ||
| 122 | } | ||
| 123 | if (state_count != dfa->tables[YYTD_ID_DEF]->td_lolen) | ||
| 124 | goto out; | ||
| 125 | |||
| 126 | /* next.size == chk.size */ | ||
| 127 | trans_count = dfa->tables[YYTD_ID_NXT]->td_lolen; | ||
| 128 | if (trans_count != dfa->tables[YYTD_ID_CHK]->td_lolen) | ||
| 129 | goto out; | ||
| 130 | |||
| 131 | /* if equivalence classes then its table size must be 256 */ | ||
| 132 | if (dfa->tables[YYTD_ID_EC] && | ||
| 133 | dfa->tables[YYTD_ID_EC]->td_lolen != 256) | ||
| 134 | goto out; | ||
| 135 | |||
| 136 | if (flags & DFA_FLAG_VERIFY_STATES) { | ||
| 137 | for (i = 0; i < state_count; i++) { | ||
| 138 | if (DEFAULT_TABLE(dfa)[i] >= state_count) | ||
| 139 | goto out; | ||
| 140 | /* TODO: do check that DEF state recursion terminates */ | ||
| 141 | if (BASE_TABLE(dfa)[i] + 255 >= trans_count) { | ||
| 142 | printk(KERN_ERR "AppArmor DFA next/check upper " | ||
| 143 | "bounds error\n"); | ||
| 144 | goto out; | ||
| 145 | } | ||
| 146 | } | ||
| 147 | |||
| 148 | for (i = 0; i < trans_count; i++) { | ||
| 149 | if (NEXT_TABLE(dfa)[i] >= state_count) | ||
| 150 | goto out; | ||
| 151 | if (CHECK_TABLE(dfa)[i] >= state_count) | ||
| 152 | goto out; | ||
| 153 | } | ||
| 154 | } | ||
| 155 | |||
| 156 | error = 0; | ||
| 157 | out: | ||
| 158 | return error; | ||
| 159 | } | ||
| 160 | |||
| 161 | /** | ||
| 162 | * dfa_free - free a dfa allocated by aa_dfa_unpack | ||
| 163 | * @dfa: the dfa to free (MAYBE NULL) | ||
| 164 | * | ||
| 165 | * Requires: reference count to dfa == 0 | ||
| 166 | */ | ||
| 167 | static void dfa_free(struct aa_dfa *dfa) | ||
| 168 | { | ||
| 169 | if (dfa) { | ||
| 170 | int i; | ||
| 171 | |||
| 172 | for (i = 0; i < ARRAY_SIZE(dfa->tables); i++) { | ||
| 173 | kvfree(dfa->tables[i]); | ||
| 174 | dfa->tables[i] = NULL; | ||
| 175 | } | ||
| 176 | kfree(dfa); | ||
| 177 | } | ||
| 178 | } | ||
| 179 | |||
| 180 | /** | ||
| 181 | * aa_dfa_free_kref - free aa_dfa by kref (called by aa_put_dfa) | ||
| 182 | * @kr: kref callback for freeing of a dfa (NOT NULL) | ||
| 183 | */ | ||
| 184 | void aa_dfa_free_kref(struct kref *kref) | ||
| 185 | { | ||
| 186 | struct aa_dfa *dfa = container_of(kref, struct aa_dfa, count); | ||
| 187 | dfa_free(dfa); | ||
| 188 | } | ||
| 189 | |||
| 190 | /** | ||
| 191 | * aa_dfa_unpack - unpack the binary tables of a serialized dfa | ||
| 192 | * @blob: aligned serialized stream of data to unpack (NOT NULL) | ||
| 193 | * @size: size of data to unpack | ||
| 194 | * @flags: flags controlling what type of accept tables are acceptable | ||
| 195 | * | ||
| 196 | * Unpack a dfa that has been serialized. To find information on the dfa | ||
| 197 | * format look in Documentation/apparmor.txt | ||
| 198 | * Assumes the dfa @blob stream has been aligned on a 8 byte boundry | ||
| 199 | * | ||
| 200 | * Returns: an unpacked dfa ready for matching or ERR_PTR on failure | ||
| 201 | */ | ||
| 202 | struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags) | ||
| 203 | { | ||
| 204 | int hsize; | ||
| 205 | int error = -ENOMEM; | ||
| 206 | char *data = blob; | ||
| 207 | struct table_header *table = NULL; | ||
| 208 | struct aa_dfa *dfa = kzalloc(sizeof(struct aa_dfa), GFP_KERNEL); | ||
| 209 | if (!dfa) | ||
| 210 | goto fail; | ||
| 211 | |||
| 212 | kref_init(&dfa->count); | ||
| 213 | |||
| 214 | error = -EPROTO; | ||
| 215 | |||
| 216 | /* get dfa table set header */ | ||
| 217 | if (size < sizeof(struct table_set_header)) | ||
| 218 | goto fail; | ||
| 219 | |||
| 220 | if (ntohl(*(u32 *) data) != YYTH_MAGIC) | ||
| 221 | goto fail; | ||
| 222 | |||
| 223 | hsize = ntohl(*(u32 *) (data + 4)); | ||
| 224 | if (size < hsize) | ||
| 225 | goto fail; | ||
| 226 | |||
| 227 | dfa->flags = ntohs(*(u16 *) (data + 12)); | ||
| 228 | data += hsize; | ||
| 229 | size -= hsize; | ||
| 230 | |||
| 231 | while (size > 0) { | ||
| 232 | table = unpack_table(data, size); | ||
| 233 | if (!table) | ||
| 234 | goto fail; | ||
| 235 | |||
| 236 | switch (table->td_id) { | ||
| 237 | case YYTD_ID_ACCEPT: | ||
| 238 | if (!(table->td_flags & ACCEPT1_FLAGS(flags))) | ||
| 239 | goto fail; | ||
| 240 | break; | ||
| 241 | case YYTD_ID_ACCEPT2: | ||
| 242 | if (!(table->td_flags & ACCEPT2_FLAGS(flags))) | ||
| 243 | goto fail; | ||
| 244 | break; | ||
| 245 | case YYTD_ID_BASE: | ||
| 246 | if (table->td_flags != YYTD_DATA32) | ||
| 247 | goto fail; | ||
| 248 | break; | ||
| 249 | case YYTD_ID_DEF: | ||
| 250 | case YYTD_ID_NXT: | ||
| 251 | case YYTD_ID_CHK: | ||
| 252 | if (table->td_flags != YYTD_DATA16) | ||
| 253 | goto fail; | ||
| 254 | break; | ||
| 255 | case YYTD_ID_EC: | ||
| 256 | if (table->td_flags != YYTD_DATA8) | ||
| 257 | goto fail; | ||
| 258 | break; | ||
| 259 | default: | ||
| 260 | goto fail; | ||
| 261 | } | ||
| 262 | /* check for duplicate table entry */ | ||
| 263 | if (dfa->tables[table->td_id]) | ||
| 264 | goto fail; | ||
| 265 | dfa->tables[table->td_id] = table; | ||
| 266 | data += table_size(table->td_lolen, table->td_flags); | ||
| 267 | size -= table_size(table->td_lolen, table->td_flags); | ||
| 268 | table = NULL; | ||
| 269 | } | ||
| 270 | |||
| 271 | error = verify_dfa(dfa, flags); | ||
| 272 | if (error) | ||
| 273 | goto fail; | ||
| 274 | |||
| 275 | return dfa; | ||
| 276 | |||
| 277 | fail: | ||
| 278 | kvfree(table); | ||
| 279 | dfa_free(dfa); | ||
| 280 | return ERR_PTR(error); | ||
| 281 | } | ||
| 282 | |||
| 283 | /** | ||
| 284 | * aa_dfa_match_len - traverse @dfa to find state @str stops at | ||
| 285 | * @dfa: the dfa to match @str against (NOT NULL) | ||
| 286 | * @start: the state of the dfa to start matching in | ||
| 287 | * @str: the string of bytes to match against the dfa (NOT NULL) | ||
| 288 | * @len: length of the string of bytes to match | ||
| 289 | * | ||
| 290 | * aa_dfa_match_len will match @str against the dfa and return the state it | ||
| 291 | * finished matching in. The final state can be used to look up the accepting | ||
| 292 | * label, or as the start state of a continuing match. | ||
| 293 | * | ||
| 294 | * This function will happily match again the 0 byte and only finishes | ||
| 295 | * when @len input is consumed. | ||
| 296 | * | ||
| 297 | * Returns: final state reached after input is consumed | ||
| 298 | */ | ||
| 299 | unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start, | ||
| 300 | const char *str, int len) | ||
| 301 | { | ||
| 302 | u16 *def = DEFAULT_TABLE(dfa); | ||
| 303 | u32 *base = BASE_TABLE(dfa); | ||
| 304 | u16 *next = NEXT_TABLE(dfa); | ||
| 305 | u16 *check = CHECK_TABLE(dfa); | ||
| 306 | unsigned int state = start, pos; | ||
| 307 | |||
| 308 | if (state == 0) | ||
| 309 | return 0; | ||
| 310 | |||
| 311 | /* current state is <state>, matching character *str */ | ||
| 312 | if (dfa->tables[YYTD_ID_EC]) { | ||
| 313 | /* Equivalence class table defined */ | ||
| 314 | u8 *equiv = EQUIV_TABLE(dfa); | ||
| 315 | /* default is direct to next state */ | ||
| 316 | for (; len; len--) { | ||
| 317 | pos = base[state] + equiv[(u8) *str++]; | ||
| 318 | if (check[pos] == state) | ||
| 319 | state = next[pos]; | ||
| 320 | else | ||
| 321 | state = def[state]; | ||
| 322 | } | ||
| 323 | } else { | ||
| 324 | /* default is direct to next state */ | ||
| 325 | for (; len; len--) { | ||
| 326 | pos = base[state] + (u8) *str++; | ||
| 327 | if (check[pos] == state) | ||
| 328 | state = next[pos]; | ||
| 329 | else | ||
| 330 | state = def[state]; | ||
| 331 | } | ||
| 332 | } | ||
| 333 | |||
| 334 | return state; | ||
| 335 | } | ||
| 336 | |||
| 337 | /** | ||
| 338 | * aa_dfa_next_state - traverse @dfa to find state @str stops at | ||
| 339 | * @dfa: the dfa to match @str against (NOT NULL) | ||
| 340 | * @start: the state of the dfa to start matching in | ||
| 341 | * @str: the null terminated string of bytes to match against the dfa (NOT NULL) | ||
| 342 | * | ||
| 343 | * aa_dfa_next_state will match @str against the dfa and return the state it | ||
| 344 | * finished matching in. The final state can be used to look up the accepting | ||
| 345 | * label, or as the start state of a continuing match. | ||
| 346 | * | ||
| 347 | * Returns: final state reached after input is consumed | ||
| 348 | */ | ||
| 349 | unsigned int aa_dfa_match(struct aa_dfa *dfa, unsigned int start, | ||
| 350 | const char *str) | ||
| 351 | { | ||
| 352 | return aa_dfa_match_len(dfa, start, str, strlen(str)); | ||
| 353 | } | ||
diff --git a/security/apparmor/path.c b/security/apparmor/path.c new file mode 100644 index 000000000000..96bab9469d48 --- /dev/null +++ b/security/apparmor/path.c | |||
| @@ -0,0 +1,235 @@ | |||
| 1 | /* | ||
| 2 | * AppArmor security module | ||
| 3 | * | ||
| 4 | * This file contains AppArmor function for pathnames | ||
| 5 | * | ||
| 6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
| 7 | * Copyright 2009-2010 Canonical Ltd. | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or | ||
| 10 | * modify it under the terms of the GNU General Public License as | ||
| 11 | * published by the Free Software Foundation, version 2 of the | ||
| 12 | * License. | ||
| 13 | */ | ||
| 14 | |||
| 15 | #include <linux/magic.h> | ||
| 16 | #include <linux/mnt_namespace.h> | ||
| 17 | #include <linux/mount.h> | ||
| 18 | #include <linux/namei.h> | ||
| 19 | #include <linux/nsproxy.h> | ||
| 20 | #include <linux/path.h> | ||
| 21 | #include <linux/sched.h> | ||
| 22 | #include <linux/slab.h> | ||
| 23 | #include <linux/fs_struct.h> | ||
| 24 | |||
| 25 | #include "include/apparmor.h" | ||
| 26 | #include "include/path.h" | ||
| 27 | #include "include/policy.h" | ||
| 28 | |||
| 29 | |||
| 30 | /* modified from dcache.c */ | ||
| 31 | static int prepend(char **buffer, int buflen, const char *str, int namelen) | ||
| 32 | { | ||
| 33 | buflen -= namelen; | ||
| 34 | if (buflen < 0) | ||
| 35 | return -ENAMETOOLONG; | ||
| 36 | *buffer -= namelen; | ||
| 37 | memcpy(*buffer, str, namelen); | ||
| 38 | return 0; | ||
| 39 | } | ||
| 40 | |||
| 41 | #define CHROOT_NSCONNECT (PATH_CHROOT_REL | PATH_CHROOT_NSCONNECT) | ||
| 42 | |||
| 43 | /** | ||
| 44 | * d_namespace_path - lookup a name associated with a given path | ||
| 45 | * @path: path to lookup (NOT NULL) | ||
| 46 | * @buf: buffer to store path to (NOT NULL) | ||
| 47 | * @buflen: length of @buf | ||
| 48 | * @name: Returns - pointer for start of path name with in @buf (NOT NULL) | ||
| 49 | * @flags: flags controlling path lookup | ||
| 50 | * | ||
| 51 | * Handle path name lookup. | ||
| 52 | * | ||
| 53 | * Returns: %0 else error code if path lookup fails | ||
| 54 | * When no error the path name is returned in @name which points to | ||
| 55 | * to a position in @buf | ||
| 56 | */ | ||
| 57 | static int d_namespace_path(struct path *path, char *buf, int buflen, | ||
| 58 | char **name, int flags) | ||
| 59 | { | ||
| 60 | struct path root, tmp; | ||
| 61 | char *res; | ||
| 62 | int deleted, connected; | ||
| 63 | int error = 0; | ||
| 64 | |||
| 65 | /* Get the root we want to resolve too */ | ||
| 66 | if (flags & PATH_CHROOT_REL) { | ||
| 67 | /* resolve paths relative to chroot */ | ||
| 68 | read_lock(¤t->fs->lock); | ||
| 69 | root = current->fs->root; | ||
| 70 | /* released below */ | ||
| 71 | path_get(&root); | ||
| 72 | read_unlock(¤t->fs->lock); | ||
| 73 | } else { | ||
| 74 | /* resolve paths relative to namespace */ | ||
| 75 | root.mnt = current->nsproxy->mnt_ns->root; | ||
| 76 | root.dentry = root.mnt->mnt_root; | ||
| 77 | /* released below */ | ||
| 78 | path_get(&root); | ||
| 79 | } | ||
| 80 | |||
| 81 | spin_lock(&dcache_lock); | ||
| 82 | /* There is a race window between path lookup here and the | ||
| 83 | * need to strip the " (deleted) string that __d_path applies | ||
| 84 | * Detect the race and relookup the path | ||
| 85 | * | ||
| 86 | * The stripping of (deleted) is a hack that could be removed | ||
| 87 | * with an updated __d_path | ||
| 88 | */ | ||
| 89 | do { | ||
| 90 | tmp = root; | ||
| 91 | deleted = d_unlinked(path->dentry); | ||
| 92 | res = __d_path(path, &tmp, buf, buflen); | ||
| 93 | |||
| 94 | } while (deleted != d_unlinked(path->dentry)); | ||
| 95 | spin_unlock(&dcache_lock); | ||
| 96 | |||
| 97 | *name = res; | ||
| 98 | /* handle error conditions - and still allow a partial path to | ||
| 99 | * be returned. | ||
| 100 | */ | ||
| 101 | if (IS_ERR(res)) { | ||
| 102 | error = PTR_ERR(res); | ||
| 103 | *name = buf; | ||
| 104 | goto out; | ||
| 105 | } | ||
| 106 | if (deleted) { | ||
| 107 | /* On some filesystems, newly allocated dentries appear to the | ||
| 108 | * security_path hooks as a deleted dentry except without an | ||
| 109 | * inode allocated. | ||
| 110 | * | ||
| 111 | * Remove the appended deleted text and return as string for | ||
| 112 | * normal mediation, or auditing. The (deleted) string is | ||
| 113 | * guaranteed to be added in this case, so just strip it. | ||
| 114 | */ | ||
| 115 | buf[buflen - 11] = 0; /* - (len(" (deleted)") +\0) */ | ||
| 116 | |||
| 117 | if (path->dentry->d_inode && !(flags & PATH_MEDIATE_DELETED)) { | ||
| 118 | error = -ENOENT; | ||
| 119 | goto out; | ||
| 120 | } | ||
| 121 | } | ||
| 122 | |||
| 123 | /* Determine if the path is connected to the expected root */ | ||
| 124 | connected = tmp.dentry == root.dentry && tmp.mnt == root.mnt; | ||
| 125 | |||
| 126 | /* If the path is not connected, | ||
| 127 | * check if it is a sysctl and handle specially else remove any | ||
| 128 | * leading / that __d_path may have returned. | ||
| 129 | * Unless | ||
| 130 | * specifically directed to connect the path, | ||
| 131 | * OR | ||
| 132 | * if in a chroot and doing chroot relative paths and the path | ||
| 133 | * resolves to the namespace root (would be connected outside | ||
| 134 | * of chroot) and specifically directed to connect paths to | ||
| 135 | * namespace root. | ||
| 136 | */ | ||
| 137 | if (!connected) { | ||
| 138 | /* is the disconnect path a sysctl? */ | ||
| 139 | if (tmp.dentry->d_sb->s_magic == PROC_SUPER_MAGIC && | ||
| 140 | strncmp(*name, "/sys/", 5) == 0) { | ||
| 141 | /* TODO: convert over to using a per namespace | ||
| 142 | * control instead of hard coded /proc | ||
| 143 | */ | ||
| 144 | error = prepend(name, *name - buf, "/proc", 5); | ||
| 145 | } else if (!(flags & PATH_CONNECT_PATH) && | ||
| 146 | !(((flags & CHROOT_NSCONNECT) == CHROOT_NSCONNECT) && | ||
| 147 | (tmp.mnt == current->nsproxy->mnt_ns->root && | ||
| 148 | tmp.dentry == tmp.mnt->mnt_root))) { | ||
| 149 | /* disconnected path, don't return pathname starting | ||
| 150 | * with '/' | ||
| 151 | */ | ||
| 152 | error = -ESTALE; | ||
| 153 | if (*res == '/') | ||
| 154 | *name = res + 1; | ||
| 155 | } | ||
| 156 | } | ||
| 157 | |||
| 158 | out: | ||
| 159 | path_put(&root); | ||
| 160 | |||
| 161 | return error; | ||
| 162 | } | ||
| 163 | |||
| 164 | /** | ||
| 165 | * get_name_to_buffer - get the pathname to a buffer ensure dir / is appended | ||
| 166 | * @path: path to get name for (NOT NULL) | ||
| 167 | * @flags: flags controlling path lookup | ||
| 168 | * @buffer: buffer to put name in (NOT NULL) | ||
| 169 | * @size: size of buffer | ||
| 170 | * @name: Returns - contains position of path name in @buffer (NOT NULL) | ||
| 171 | * | ||
| 172 | * Returns: %0 else error on failure | ||
| 173 | */ | ||
| 174 | static int get_name_to_buffer(struct path *path, int flags, char *buffer, | ||
| 175 | int size, char **name) | ||
| 176 | { | ||
| 177 | int adjust = (flags & PATH_IS_DIR) ? 1 : 0; | ||
| 178 | int error = d_namespace_path(path, buffer, size - adjust, name, flags); | ||
| 179 | |||
| 180 | if (!error && (flags & PATH_IS_DIR) && (*name)[1] != '\0') | ||
| 181 | /* | ||
| 182 | * Append "/" to the pathname. The root directory is a special | ||
| 183 | * case; it already ends in slash. | ||
| 184 | */ | ||
| 185 | strcpy(&buffer[size - 2], "/"); | ||
| 186 | |||
| 187 | return error; | ||
| 188 | } | ||
| 189 | |||
| 190 | /** | ||
| 191 | * aa_get_name - compute the pathname of a file | ||
| 192 | * @path: path the file (NOT NULL) | ||
| 193 | * @flags: flags controlling path name generation | ||
| 194 | * @buffer: buffer that aa_get_name() allocated (NOT NULL) | ||
| 195 | * @name: Returns - the generated path name if !error (NOT NULL) | ||
| 196 | * | ||
| 197 | * @name is a pointer to the beginning of the pathname (which usually differs | ||
| 198 | * from the beginning of the buffer), or NULL. If there is an error @name | ||
| 199 | * may contain a partial or invalid name that can be used for audit purposes, | ||
| 200 | * but it can not be used for mediation. | ||
| 201 | * | ||
| 202 | * We need PATH_IS_DIR to indicate whether the file is a directory or not | ||
| 203 | * because the file may not yet exist, and so we cannot check the inode's | ||
| 204 | * file type. | ||
| 205 | * | ||
| 206 | * Returns: %0 else error code if could retrieve name | ||
| 207 | */ | ||
| 208 | int aa_get_name(struct path *path, int flags, char **buffer, const char **name) | ||
| 209 | { | ||
| 210 | char *buf, *str = NULL; | ||
| 211 | int size = 256; | ||
| 212 | int error; | ||
| 213 | |||
| 214 | *name = NULL; | ||
| 215 | *buffer = NULL; | ||
| 216 | for (;;) { | ||
| 217 | /* freed by caller */ | ||
| 218 | buf = kmalloc(size, GFP_KERNEL); | ||
| 219 | if (!buf) | ||
| 220 | return -ENOMEM; | ||
| 221 | |||
| 222 | error = get_name_to_buffer(path, flags, buf, size, &str); | ||
| 223 | if (error != -ENAMETOOLONG) | ||
| 224 | break; | ||
| 225 | |||
| 226 | kfree(buf); | ||
| 227 | size <<= 1; | ||
| 228 | if (size > aa_g_path_max) | ||
| 229 | return -ENAMETOOLONG; | ||
| 230 | } | ||
| 231 | *buffer = buf; | ||
| 232 | *name = str; | ||
| 233 | |||
| 234 | return error; | ||
| 235 | } | ||
diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c new file mode 100644 index 000000000000..3cdc1ad0787e --- /dev/null +++ b/security/apparmor/policy.c | |||
| @@ -0,0 +1,1184 @@ | |||
| 1 | /* | ||
| 2 | * AppArmor security module | ||
| 3 | * | ||
| 4 | * This file contains AppArmor policy manipulation functions | ||
| 5 | * | ||
| 6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
| 7 | * Copyright 2009-2010 Canonical Ltd. | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or | ||
| 10 | * modify it under the terms of the GNU General Public License as | ||
| 11 | * published by the Free Software Foundation, version 2 of the | ||
| 12 | * License. | ||
| 13 | * | ||
| 14 | * | ||
| 15 | * AppArmor policy is based around profiles, which contain the rules a | ||
| 16 | * task is confined by. Every task in the system has a profile attached | ||
| 17 | * to it determined either by matching "unconfined" tasks against the | ||
| 18 | * visible set of profiles or by following a profiles attachment rules. | ||
| 19 | * | ||
| 20 | * Each profile exists in a profile namespace which is a container of | ||
| 21 | * visible profiles. Each namespace contains a special "unconfined" profile, | ||
| 22 | * which doesn't enforce any confinement on a task beyond DAC. | ||
| 23 | * | ||
| 24 | * Namespace and profile names can be written together in either | ||
| 25 | * of two syntaxes. | ||
| 26 | * :namespace:profile - used by kernel interfaces for easy detection | ||
| 27 | * namespace://profile - used by policy | ||
| 28 | * | ||
| 29 | * Profile names can not start with : or @ or ^ and may not contain \0 | ||
| 30 | * | ||
| 31 | * Reserved profile names | ||
| 32 | * unconfined - special automatically generated unconfined profile | ||
| 33 | * inherit - special name to indicate profile inheritance | ||
| 34 | * null-XXXX-YYYY - special automatically generated learning profiles | ||
| 35 | * | ||
| 36 | * Namespace names may not start with / or @ and may not contain \0 or : | ||
| 37 | * Reserved namespace names | ||
| 38 | * user-XXXX - user defined profiles | ||
| 39 | * | ||
| 40 | * a // in a profile or namespace name indicates a hierarchical name with the | ||
| 41 | * name before the // being the parent and the name after the child. | ||
| 42 | * | ||
| 43 | * Profile and namespace hierarchies serve two different but similar purposes. | ||
| 44 | * The namespace contains the set of visible profiles that are considered | ||
| 45 | * for attachment. The hierarchy of namespaces allows for virtualizing | ||
| 46 | * the namespace so that for example a chroot can have its own set of profiles | ||
| 47 | * which may define some local user namespaces. | ||
| 48 | * The profile hierarchy severs two distinct purposes, | ||
| 49 | * - it allows for sub profiles or hats, which allows an application to run | ||
| 50 | * subprograms under its own profile with different restriction than it | ||
| 51 | * self, and not have it use the system profile. | ||
| 52 | * eg. if a mail program starts an editor, the policy might make the | ||
| 53 | * restrictions tighter on the editor tighter than the mail program, | ||
| 54 | * and definitely different than general editor restrictions | ||
| 55 | * - it allows for binary hierarchy of profiles, so that execution history | ||
| 56 | * is preserved. This feature isn't exploited by AppArmor reference policy | ||
| 57 | * but is allowed. NOTE: this is currently suboptimal because profile | ||
| 58 | * aliasing is not currently implemented so that a profile for each | ||
| 59 | * level must be defined. | ||
| 60 | * eg. /bin/bash///bin/ls as a name would indicate /bin/ls was started | ||
| 61 | * from /bin/bash | ||
| 62 | * | ||
| 63 | * A profile or namespace name that can contain one or more // separators | ||
| 64 | * is referred to as an hname (hierarchical). | ||
| 65 | * eg. /bin/bash//bin/ls | ||
| 66 | * | ||
| 67 | * An fqname is a name that may contain both namespace and profile hnames. | ||
| 68 | * eg. :ns:/bin/bash//bin/ls | ||
| 69 | * | ||
| 70 | * NOTES: | ||
| 71 | * - locking of profile lists is currently fairly coarse. All profile | ||
| 72 | * lists within a namespace use the namespace lock. | ||
| 73 | * FIXME: move profile lists to using rcu_lists | ||
| 74 | */ | ||
| 75 | |||
| 76 | #include <linux/slab.h> | ||
| 77 | #include <linux/spinlock.h> | ||
| 78 | #include <linux/string.h> | ||
| 79 | |||
| 80 | #include "include/apparmor.h" | ||
| 81 | #include "include/capability.h" | ||
| 82 | #include "include/context.h" | ||
| 83 | #include "include/file.h" | ||
| 84 | #include "include/ipc.h" | ||
| 85 | #include "include/match.h" | ||
| 86 | #include "include/path.h" | ||
| 87 | #include "include/policy.h" | ||
| 88 | #include "include/policy_unpack.h" | ||
| 89 | #include "include/resource.h" | ||
| 90 | #include "include/sid.h" | ||
| 91 | |||
| 92 | |||
| 93 | /* root profile namespace */ | ||
| 94 | struct aa_namespace *root_ns; | ||
| 95 | |||
| 96 | const char *profile_mode_names[] = { | ||
| 97 | "enforce", | ||
| 98 | "complain", | ||
| 99 | "kill", | ||
| 100 | }; | ||
| 101 | |||
| 102 | /** | ||
| 103 | * hname_tail - find the last component of an hname | ||
| 104 | * @name: hname to find the base profile name component of (NOT NULL) | ||
| 105 | * | ||
| 106 | * Returns: the tail (base profile name) name component of an hname | ||
| 107 | */ | ||
| 108 | static const char *hname_tail(const char *hname) | ||
| 109 | { | ||
| 110 | char *split; | ||
| 111 | hname = strim((char *)hname); | ||
| 112 | for (split = strstr(hname, "//"); split; split = strstr(hname, "//")) | ||
| 113 | hname = split + 2; | ||
| 114 | |||
| 115 | return hname; | ||
| 116 | } | ||
| 117 | |||
| 118 | /** | ||
| 119 | * policy_init - initialize a policy structure | ||
| 120 | * @policy: policy to initialize (NOT NULL) | ||
| 121 | * @prefix: prefix name if any is required. (MAYBE NULL) | ||
| 122 | * @name: name of the policy, init will make a copy of it (NOT NULL) | ||
| 123 | * | ||
| 124 | * Note: this fn creates a copy of strings passed in | ||
| 125 | * | ||
| 126 | * Returns: true if policy init successful | ||
| 127 | */ | ||
| 128 | static bool policy_init(struct aa_policy *policy, const char *prefix, | ||
| 129 | const char *name) | ||
| 130 | { | ||
| 131 | /* freed by policy_free */ | ||
| 132 | if (prefix) { | ||
| 133 | policy->hname = kmalloc(strlen(prefix) + strlen(name) + 3, | ||
| 134 | GFP_KERNEL); | ||
| 135 | if (policy->hname) | ||
| 136 | sprintf(policy->hname, "%s//%s", prefix, name); | ||
| 137 | } else | ||
| 138 | policy->hname = kstrdup(name, GFP_KERNEL); | ||
| 139 | if (!policy->hname) | ||
| 140 | return 0; | ||
| 141 | /* base.name is a substring of fqname */ | ||
| 142 | policy->name = (char *)hname_tail(policy->hname); | ||
| 143 | INIT_LIST_HEAD(&policy->list); | ||
| 144 | INIT_LIST_HEAD(&policy->profiles); | ||
| 145 | kref_init(&policy->count); | ||
| 146 | |||
| 147 | return 1; | ||
| 148 | } | ||
| 149 | |||
| 150 | /** | ||
| 151 | * policy_destroy - free the elements referenced by @policy | ||
| 152 | * @policy: policy that is to have its elements freed (NOT NULL) | ||
| 153 | */ | ||
| 154 | static void policy_destroy(struct aa_policy *policy) | ||
| 155 | { | ||
| 156 | /* still contains profiles -- invalid */ | ||
| 157 | if (!list_empty(&policy->profiles)) { | ||
| 158 | AA_ERROR("%s: internal error, " | ||
| 159 | "policy '%s' still contains profiles\n", | ||
| 160 | __func__, policy->name); | ||
| 161 | BUG(); | ||
| 162 | } | ||
| 163 | if (!list_empty(&policy->list)) { | ||
| 164 | AA_ERROR("%s: internal error, policy '%s' still on list\n", | ||
| 165 | __func__, policy->name); | ||
| 166 | BUG(); | ||
| 167 | } | ||
| 168 | |||
| 169 | /* don't free name as its a subset of hname */ | ||
| 170 | kzfree(policy->hname); | ||
| 171 | } | ||
| 172 | |||
| 173 | /** | ||
| 174 | * __policy_find - find a policy by @name on a policy list | ||
| 175 | * @head: list to search (NOT NULL) | ||
| 176 | * @name: name to search for (NOT NULL) | ||
| 177 | * | ||
| 178 | * Requires: correct locks for the @head list be held | ||
| 179 | * | ||
| 180 | * Returns: unrefcounted policy that match @name or NULL if not found | ||
| 181 | */ | ||
| 182 | static struct aa_policy *__policy_find(struct list_head *head, const char *name) | ||
| 183 | { | ||
| 184 | struct aa_policy *policy; | ||
| 185 | |||
| 186 | list_for_each_entry(policy, head, list) { | ||
| 187 | if (!strcmp(policy->name, name)) | ||
| 188 | return policy; | ||
| 189 | } | ||
| 190 | return NULL; | ||
| 191 | } | ||
| 192 | |||
| 193 | /** | ||
| 194 | * __policy_strn_find - find a policy that's name matches @len chars of @str | ||
| 195 | * @head: list to search (NOT NULL) | ||
| 196 | * @str: string to search for (NOT NULL) | ||
| 197 | * @len: length of match required | ||
| 198 | * | ||
| 199 | * Requires: correct locks for the @head list be held | ||
| 200 | * | ||
| 201 | * Returns: unrefcounted policy that match @str or NULL if not found | ||
| 202 | * | ||
| 203 | * if @len == strlen(@strlen) then this is equiv to __policy_find | ||
| 204 | * other wise it allows searching for policy by a partial match of name | ||
| 205 | */ | ||
| 206 | static struct aa_policy *__policy_strn_find(struct list_head *head, | ||
| 207 | const char *str, int len) | ||
| 208 | { | ||
| 209 | struct aa_policy *policy; | ||
| 210 | |||
| 211 | list_for_each_entry(policy, head, list) { | ||
| 212 | if (aa_strneq(policy->name, str, len)) | ||
| 213 | return policy; | ||
| 214 | } | ||
| 215 | |||
| 216 | return NULL; | ||
| 217 | } | ||
| 218 | |||
| 219 | /* | ||
| 220 | * Routines for AppArmor namespaces | ||
| 221 | */ | ||
| 222 | |||
| 223 | static const char *hidden_ns_name = "---"; | ||
| 224 | /** | ||
| 225 | * aa_ns_visible - test if @view is visible from @curr | ||
| 226 | * @curr: namespace to treat as the parent (NOT NULL) | ||
| 227 | * @view: namespace to test if visible from @curr (NOT NULL) | ||
| 228 | * | ||
| 229 | * Returns: true if @view is visible from @curr else false | ||
| 230 | */ | ||
| 231 | bool aa_ns_visible(struct aa_namespace *curr, struct aa_namespace *view) | ||
| 232 | { | ||
| 233 | if (curr == view) | ||
| 234 | return true; | ||
| 235 | |||
| 236 | for ( ; view; view = view->parent) { | ||
| 237 | if (view->parent == curr) | ||
| 238 | return true; | ||
| 239 | } | ||
| 240 | return false; | ||
| 241 | } | ||
| 242 | |||
| 243 | /** | ||
| 244 | * aa_na_name - Find the ns name to display for @view from @curr | ||
| 245 | * @curr - current namespace (NOT NULL) | ||
| 246 | * @view - namespace attempting to view (NOT NULL) | ||
| 247 | * | ||
| 248 | * Returns: name of @view visible from @curr | ||
| 249 | */ | ||
| 250 | const char *aa_ns_name(struct aa_namespace *curr, struct aa_namespace *view) | ||
| 251 | { | ||
| 252 | /* if view == curr then the namespace name isn't displayed */ | ||
| 253 | if (curr == view) | ||
| 254 | return ""; | ||
| 255 | |||
| 256 | if (aa_ns_visible(curr, view)) { | ||
| 257 | /* at this point if a ns is visible it is in a view ns | ||
| 258 | * thus the curr ns.hname is a prefix of its name. | ||
| 259 | * Only output the virtualized portion of the name | ||
| 260 | * Add + 2 to skip over // separating curr hname prefix | ||
| 261 | * from the visible tail of the views hname | ||
| 262 | */ | ||
| 263 | return view->base.hname + strlen(curr->base.hname) + 2; | ||
| 264 | } else | ||
| 265 | return hidden_ns_name; | ||
| 266 | } | ||
| 267 | |||
| 268 | /** | ||
| 269 | * alloc_namespace - allocate, initialize and return a new namespace | ||
| 270 | * @prefix: parent namespace name (MAYBE NULL) | ||
| 271 | * @name: a preallocated name (NOT NULL) | ||
| 272 | * | ||
| 273 | * Returns: refcounted namespace or NULL on failure. | ||
| 274 | */ | ||
| 275 | static struct aa_namespace *alloc_namespace(const char *prefix, | ||
| 276 | const char *name) | ||
| 277 | { | ||
| 278 | struct aa_namespace *ns; | ||
| 279 | |||
| 280 | ns = kzalloc(sizeof(*ns), GFP_KERNEL); | ||
| 281 | AA_DEBUG("%s(%p)\n", __func__, ns); | ||
| 282 | if (!ns) | ||
| 283 | return NULL; | ||
| 284 | if (!policy_init(&ns->base, prefix, name)) | ||
| 285 | goto fail_ns; | ||
| 286 | |||
| 287 | INIT_LIST_HEAD(&ns->sub_ns); | ||
| 288 | rwlock_init(&ns->lock); | ||
| 289 | |||
| 290 | /* released by free_namespace */ | ||
| 291 | ns->unconfined = aa_alloc_profile("unconfined"); | ||
| 292 | if (!ns->unconfined) | ||
| 293 | goto fail_unconfined; | ||
| 294 | |||
| 295 | ns->unconfined->sid = aa_alloc_sid(); | ||
| 296 | ns->unconfined->flags = PFLAG_UNCONFINED | PFLAG_IX_ON_NAME_ERROR | | ||
| 297 | PFLAG_IMMUTABLE; | ||
| 298 | |||
| 299 | /* | ||
| 300 | * released by free_namespace, however __remove_namespace breaks | ||
| 301 | * the cyclic references (ns->unconfined, and unconfined->ns) and | ||
| 302 | * replaces with refs to parent namespace unconfined | ||
| 303 | */ | ||
| 304 | ns->unconfined->ns = aa_get_namespace(ns); | ||
| 305 | |||
| 306 | return ns; | ||
| 307 | |||
| 308 | fail_unconfined: | ||
| 309 | kzfree(ns->base.name); | ||
| 310 | fail_ns: | ||
| 311 | kzfree(ns); | ||
| 312 | return NULL; | ||
| 313 | } | ||
| 314 | |||
| 315 | /** | ||
| 316 | * free_namespace - free a profile namespace | ||
| 317 | * @ns: the namespace to free (MAYBE NULL) | ||
| 318 | * | ||
| 319 | * Requires: All references to the namespace must have been put, if the | ||
| 320 | * namespace was referenced by a profile confining a task, | ||
| 321 | */ | ||
| 322 | static void free_namespace(struct aa_namespace *ns) | ||
| 323 | { | ||
| 324 | if (!ns) | ||
| 325 | return; | ||
| 326 | |||
| 327 | policy_destroy(&ns->base); | ||
| 328 | aa_put_namespace(ns->parent); | ||
| 329 | |||
| 330 | if (ns->unconfined && ns->unconfined->ns == ns) | ||
| 331 | ns->unconfined->ns = NULL; | ||
| 332 | |||
| 333 | aa_put_profile(ns->unconfined); | ||
| 334 | kzfree(ns); | ||
| 335 | } | ||
| 336 | |||
| 337 | /** | ||
| 338 | * aa_free_namespace_kref - free aa_namespace by kref (see aa_put_namespace) | ||
| 339 | * @kr: kref callback for freeing of a namespace (NOT NULL) | ||
| 340 | */ | ||
| 341 | void aa_free_namespace_kref(struct kref *kref) | ||
| 342 | { | ||
| 343 | free_namespace(container_of(kref, struct aa_namespace, base.count)); | ||
| 344 | } | ||
| 345 | |||
| 346 | /** | ||
| 347 | * __aa_find_namespace - find a namespace on a list by @name | ||
| 348 | * @head: list to search for namespace on (NOT NULL) | ||
| 349 | * @name: name of namespace to look for (NOT NULL) | ||
| 350 | * | ||
| 351 | * Returns: unrefcounted namespace | ||
| 352 | * | ||
| 353 | * Requires: ns lock be held | ||
| 354 | */ | ||
| 355 | static struct aa_namespace *__aa_find_namespace(struct list_head *head, | ||
| 356 | const char *name) | ||
| 357 | { | ||
| 358 | return (struct aa_namespace *)__policy_find(head, name); | ||
| 359 | } | ||
| 360 | |||
| 361 | /** | ||
| 362 | * aa_find_namespace - look up a profile namespace on the namespace list | ||
| 363 | * @root: namespace to search in (NOT NULL) | ||
| 364 | * @name: name of namespace to find (NOT NULL) | ||
| 365 | * | ||
| 366 | * Returns: a refcounted namespace on the list, or NULL if no namespace | ||
| 367 | * called @name exists. | ||
| 368 | * | ||
| 369 | * refcount released by caller | ||
| 370 | */ | ||
| 371 | struct aa_namespace *aa_find_namespace(struct aa_namespace *root, | ||
| 372 | const char *name) | ||
| 373 | { | ||
| 374 | struct aa_namespace *ns = NULL; | ||
| 375 | |||
| 376 | read_lock(&root->lock); | ||
| 377 | ns = aa_get_namespace(__aa_find_namespace(&root->sub_ns, name)); | ||
| 378 | read_unlock(&root->lock); | ||
| 379 | |||
| 380 | return ns; | ||
| 381 | } | ||
| 382 | |||
| 383 | /** | ||
| 384 | * aa_prepare_namespace - find an existing or create a new namespace of @name | ||
| 385 | * @name: the namespace to find or add (MAYBE NULL) | ||
| 386 | * | ||
| 387 | * Returns: refcounted namespace or NULL if failed to create one | ||
| 388 | */ | ||
| 389 | static struct aa_namespace *aa_prepare_namespace(const char *name) | ||
| 390 | { | ||
| 391 | struct aa_namespace *ns, *root; | ||
| 392 | |||
| 393 | root = aa_current_profile()->ns; | ||
| 394 | |||
| 395 | write_lock(&root->lock); | ||
| 396 | |||
| 397 | /* if name isn't specified the profile is loaded to the current ns */ | ||
| 398 | if (!name) { | ||
| 399 | /* released by caller */ | ||
| 400 | ns = aa_get_namespace(root); | ||
| 401 | goto out; | ||
| 402 | } | ||
| 403 | |||
| 404 | /* try and find the specified ns and if it doesn't exist create it */ | ||
| 405 | /* released by caller */ | ||
| 406 | ns = aa_get_namespace(__aa_find_namespace(&root->sub_ns, name)); | ||
| 407 | if (!ns) { | ||
| 408 | /* namespace not found */ | ||
| 409 | struct aa_namespace *new_ns; | ||
| 410 | write_unlock(&root->lock); | ||
| 411 | new_ns = alloc_namespace(root->base.hname, name); | ||
| 412 | if (!new_ns) | ||
| 413 | return NULL; | ||
| 414 | write_lock(&root->lock); | ||
| 415 | /* test for race when new_ns was allocated */ | ||
| 416 | ns = __aa_find_namespace(&root->sub_ns, name); | ||
| 417 | if (!ns) { | ||
| 418 | /* add parent ref */ | ||
| 419 | new_ns->parent = aa_get_namespace(root); | ||
| 420 | |||
| 421 | list_add(&new_ns->base.list, &root->sub_ns); | ||
| 422 | /* add list ref */ | ||
| 423 | ns = aa_get_namespace(new_ns); | ||
| 424 | } else { | ||
| 425 | /* raced so free the new one */ | ||
| 426 | free_namespace(new_ns); | ||
| 427 | /* get reference on namespace */ | ||
| 428 | aa_get_namespace(ns); | ||
| 429 | } | ||
| 430 | } | ||
| 431 | out: | ||
| 432 | write_unlock(&root->lock); | ||
| 433 | |||
| 434 | /* return ref */ | ||
| 435 | return ns; | ||
| 436 | } | ||
| 437 | |||
| 438 | /** | ||
| 439 | * __list_add_profile - add a profile to a list | ||
| 440 | * @list: list to add it to (NOT NULL) | ||
| 441 | * @profile: the profile to add (NOT NULL) | ||
| 442 | * | ||
| 443 | * refcount @profile, should be put by __list_remove_profile | ||
| 444 | * | ||
| 445 | * Requires: namespace lock be held, or list not be shared | ||
| 446 | */ | ||
| 447 | static void __list_add_profile(struct list_head *list, | ||
| 448 | struct aa_profile *profile) | ||
| 449 | { | ||
| 450 | list_add(&profile->base.list, list); | ||
| 451 | /* get list reference */ | ||
| 452 | aa_get_profile(profile); | ||
| 453 | } | ||
| 454 | |||
| 455 | /** | ||
| 456 | * __list_remove_profile - remove a profile from the list it is on | ||
| 457 | * @profile: the profile to remove (NOT NULL) | ||
| 458 | * | ||
| 459 | * remove a profile from the list, warning generally removal should | ||
| 460 | * be done with __replace_profile as most profile removals are | ||
| 461 | * replacements to the unconfined profile. | ||
| 462 | * | ||
| 463 | * put @profile list refcount | ||
| 464 | * | ||
| 465 | * Requires: namespace lock be held, or list not have been live | ||
| 466 | */ | ||
| 467 | static void __list_remove_profile(struct aa_profile *profile) | ||
| 468 | { | ||
| 469 | list_del_init(&profile->base.list); | ||
| 470 | if (!(profile->flags & PFLAG_NO_LIST_REF)) | ||
| 471 | /* release list reference */ | ||
| 472 | aa_put_profile(profile); | ||
| 473 | } | ||
| 474 | |||
| 475 | /** | ||
| 476 | * __replace_profile - replace @old with @new on a list | ||
| 477 | * @old: profile to be replaced (NOT NULL) | ||
| 478 | * @new: profile to replace @old with (NOT NULL) | ||
| 479 | * | ||
| 480 | * Will duplicate and refcount elements that @new inherits from @old | ||
| 481 | * and will inherit @old children. | ||
| 482 | * | ||
| 483 | * refcount @new for list, put @old list refcount | ||
| 484 | * | ||
| 485 | * Requires: namespace list lock be held, or list not be shared | ||
| 486 | */ | ||
| 487 | static void __replace_profile(struct aa_profile *old, struct aa_profile *new) | ||
| 488 | { | ||
| 489 | struct aa_policy *policy; | ||
| 490 | struct aa_profile *child, *tmp; | ||
| 491 | |||
| 492 | if (old->parent) | ||
| 493 | policy = &old->parent->base; | ||
| 494 | else | ||
| 495 | policy = &old->ns->base; | ||
| 496 | |||
| 497 | /* released when @new is freed */ | ||
| 498 | new->parent = aa_get_profile(old->parent); | ||
| 499 | new->ns = aa_get_namespace(old->ns); | ||
| 500 | new->sid = old->sid; | ||
| 501 | __list_add_profile(&policy->profiles, new); | ||
| 502 | /* inherit children */ | ||
| 503 | list_for_each_entry_safe(child, tmp, &old->base.profiles, base.list) { | ||
| 504 | aa_put_profile(child->parent); | ||
| 505 | child->parent = aa_get_profile(new); | ||
| 506 | /* list refcount transferred to @new*/ | ||
| 507 | list_move(&child->base.list, &new->base.profiles); | ||
| 508 | } | ||
| 509 | |||
| 510 | /* released by free_profile */ | ||
| 511 | old->replacedby = aa_get_profile(new); | ||
| 512 | __list_remove_profile(old); | ||
| 513 | } | ||
| 514 | |||
| 515 | static void __profile_list_release(struct list_head *head); | ||
| 516 | |||
| 517 | /** | ||
| 518 | * __remove_profile - remove old profile, and children | ||
| 519 | * @profile: profile to be replaced (NOT NULL) | ||
| 520 | * | ||
| 521 | * Requires: namespace list lock be held, or list not be shared | ||
| 522 | */ | ||
| 523 | static void __remove_profile(struct aa_profile *profile) | ||
| 524 | { | ||
| 525 | /* release any children lists first */ | ||
| 526 | __profile_list_release(&profile->base.profiles); | ||
| 527 | /* released by free_profile */ | ||
| 528 | profile->replacedby = aa_get_profile(profile->ns->unconfined); | ||
| 529 | __list_remove_profile(profile); | ||
| 530 | } | ||
| 531 | |||
| 532 | /** | ||
| 533 | * __profile_list_release - remove all profiles on the list and put refs | ||
| 534 | * @head: list of profiles (NOT NULL) | ||
| 535 | * | ||
| 536 | * Requires: namespace lock be held | ||
| 537 | */ | ||
| 538 | static void __profile_list_release(struct list_head *head) | ||
| 539 | { | ||
| 540 | struct aa_profile *profile, *tmp; | ||
| 541 | list_for_each_entry_safe(profile, tmp, head, base.list) | ||
| 542 | __remove_profile(profile); | ||
| 543 | } | ||
| 544 | |||
| 545 | static void __ns_list_release(struct list_head *head); | ||
| 546 | |||
| 547 | /** | ||
| 548 | * destroy_namespace - remove everything contained by @ns | ||
| 549 | * @ns: namespace to have it contents removed (NOT NULL) | ||
| 550 | */ | ||
| 551 | static void destroy_namespace(struct aa_namespace *ns) | ||
| 552 | { | ||
| 553 | if (!ns) | ||
| 554 | return; | ||
| 555 | |||
| 556 | write_lock(&ns->lock); | ||
| 557 | /* release all profiles in this namespace */ | ||
| 558 | __profile_list_release(&ns->base.profiles); | ||
| 559 | |||
| 560 | /* release all sub namespaces */ | ||
| 561 | __ns_list_release(&ns->sub_ns); | ||
| 562 | |||
| 563 | write_unlock(&ns->lock); | ||
| 564 | } | ||
| 565 | |||
| 566 | /** | ||
| 567 | * __remove_namespace - remove a namespace and all its children | ||
| 568 | * @ns: namespace to be removed (NOT NULL) | ||
| 569 | * | ||
| 570 | * Requires: ns->parent->lock be held and ns removed from parent. | ||
| 571 | */ | ||
| 572 | static void __remove_namespace(struct aa_namespace *ns) | ||
| 573 | { | ||
| 574 | struct aa_profile *unconfined = ns->unconfined; | ||
| 575 | |||
| 576 | /* remove ns from namespace list */ | ||
| 577 | list_del_init(&ns->base.list); | ||
| 578 | |||
| 579 | /* | ||
| 580 | * break the ns, unconfined profile cyclic reference and forward | ||
| 581 | * all new unconfined profiles requests to the parent namespace | ||
| 582 | * This will result in all confined tasks that have a profile | ||
| 583 | * being removed, inheriting the parent->unconfined profile. | ||
| 584 | */ | ||
| 585 | if (ns->parent) | ||
| 586 | ns->unconfined = aa_get_profile(ns->parent->unconfined); | ||
| 587 | |||
| 588 | destroy_namespace(ns); | ||
| 589 | |||
| 590 | /* release original ns->unconfined ref */ | ||
| 591 | aa_put_profile(unconfined); | ||
| 592 | /* release ns->base.list ref, from removal above */ | ||
| 593 | aa_put_namespace(ns); | ||
| 594 | } | ||
| 595 | |||
| 596 | /** | ||
| 597 | * __ns_list_release - remove all profile namespaces on the list put refs | ||
| 598 | * @head: list of profile namespaces (NOT NULL) | ||
| 599 | * | ||
| 600 | * Requires: namespace lock be held | ||
| 601 | */ | ||
| 602 | static void __ns_list_release(struct list_head *head) | ||
| 603 | { | ||
| 604 | struct aa_namespace *ns, *tmp; | ||
| 605 | list_for_each_entry_safe(ns, tmp, head, base.list) | ||
| 606 | __remove_namespace(ns); | ||
| 607 | |||
| 608 | } | ||
| 609 | |||
| 610 | /** | ||
| 611 | * aa_alloc_root_ns - allocate the root profile namespace | ||
| 612 | * | ||
| 613 | * Returns: %0 on success else error | ||
| 614 | * | ||
| 615 | */ | ||
| 616 | int __init aa_alloc_root_ns(void) | ||
| 617 | { | ||
| 618 | /* released by aa_free_root_ns - used as list ref*/ | ||
| 619 | root_ns = alloc_namespace(NULL, "root"); | ||
| 620 | if (!root_ns) | ||
| 621 | return -ENOMEM; | ||
| 622 | |||
| 623 | return 0; | ||
| 624 | } | ||
| 625 | |||
| 626 | /** | ||
| 627 | * aa_free_root_ns - free the root profile namespace | ||
| 628 | */ | ||
| 629 | void __init aa_free_root_ns(void) | ||
| 630 | { | ||
| 631 | struct aa_namespace *ns = root_ns; | ||
| 632 | root_ns = NULL; | ||
| 633 | |||
| 634 | destroy_namespace(ns); | ||
| 635 | aa_put_namespace(ns); | ||
| 636 | } | ||
| 637 | |||
| 638 | /** | ||
| 639 | * aa_alloc_profile - allocate, initialize and return a new profile | ||
| 640 | * @hname: name of the profile (NOT NULL) | ||
| 641 | * | ||
| 642 | * Returns: refcount profile or NULL on failure | ||
| 643 | */ | ||
| 644 | struct aa_profile *aa_alloc_profile(const char *hname) | ||
| 645 | { | ||
| 646 | struct aa_profile *profile; | ||
| 647 | |||
| 648 | /* freed by free_profile - usually through aa_put_profile */ | ||
| 649 | profile = kzalloc(sizeof(*profile), GFP_KERNEL); | ||
| 650 | if (!profile) | ||
| 651 | return NULL; | ||
| 652 | |||
| 653 | if (!policy_init(&profile->base, NULL, hname)) { | ||
| 654 | kzfree(profile); | ||
| 655 | return NULL; | ||
| 656 | } | ||
| 657 | |||
| 658 | /* refcount released by caller */ | ||
| 659 | return profile; | ||
| 660 | } | ||
| 661 | |||
| 662 | /** | ||
| 663 | * aa_new_null_profile - create a new null-X learning profile | ||
| 664 | * @parent: profile that caused this profile to be created (NOT NULL) | ||
| 665 | * @hat: true if the null- learning profile is a hat | ||
| 666 | * | ||
| 667 | * Create a null- complain mode profile used in learning mode. The name of | ||
| 668 | * the profile is unique and follows the format of parent//null-sid. | ||
| 669 | * | ||
| 670 | * null profiles are added to the profile list but the list does not | ||
| 671 | * hold a count on them so that they are automatically released when | ||
| 672 | * not in use. | ||
| 673 | * | ||
| 674 | * Returns: new refcounted profile else NULL on failure | ||
| 675 | */ | ||
| 676 | struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat) | ||
| 677 | { | ||
| 678 | struct aa_profile *profile = NULL; | ||
| 679 | char *name; | ||
| 680 | u32 sid = aa_alloc_sid(); | ||
| 681 | |||
| 682 | /* freed below */ | ||
| 683 | name = kmalloc(strlen(parent->base.hname) + 2 + 7 + 8, GFP_KERNEL); | ||
| 684 | if (!name) | ||
| 685 | goto fail; | ||
| 686 | sprintf(name, "%s//null-%x", parent->base.hname, sid); | ||
| 687 | |||
| 688 | profile = aa_alloc_profile(name); | ||
| 689 | kfree(name); | ||
| 690 | if (!profile) | ||
| 691 | goto fail; | ||
| 692 | |||
| 693 | profile->sid = sid; | ||
| 694 | profile->mode = APPARMOR_COMPLAIN; | ||
| 695 | profile->flags = PFLAG_NULL; | ||
| 696 | if (hat) | ||
| 697 | profile->flags |= PFLAG_HAT; | ||
| 698 | |||
| 699 | /* released on free_profile */ | ||
| 700 | profile->parent = aa_get_profile(parent); | ||
| 701 | profile->ns = aa_get_namespace(parent->ns); | ||
| 702 | |||
| 703 | write_lock(&profile->ns->lock); | ||
| 704 | __list_add_profile(&parent->base.profiles, profile); | ||
| 705 | write_unlock(&profile->ns->lock); | ||
| 706 | |||
| 707 | /* refcount released by caller */ | ||
| 708 | return profile; | ||
| 709 | |||
| 710 | fail: | ||
| 711 | aa_free_sid(sid); | ||
| 712 | return NULL; | ||
| 713 | } | ||
| 714 | |||
| 715 | /** | ||
| 716 | * free_profile - free a profile | ||
| 717 | * @profile: the profile to free (MAYBE NULL) | ||
| 718 | * | ||
| 719 | * Free a profile, its hats and null_profile. All references to the profile, | ||
| 720 | * its hats and null_profile must have been put. | ||
| 721 | * | ||
| 722 | * If the profile was referenced from a task context, free_profile() will | ||
| 723 | * be called from an rcu callback routine, so we must not sleep here. | ||
| 724 | */ | ||
| 725 | static void free_profile(struct aa_profile *profile) | ||
| 726 | { | ||
| 727 | AA_DEBUG("%s(%p)\n", __func__, profile); | ||
| 728 | |||
| 729 | if (!profile) | ||
| 730 | return; | ||
| 731 | |||
| 732 | if (!list_empty(&profile->base.list)) { | ||
| 733 | AA_ERROR("%s: internal error, " | ||
| 734 | "profile '%s' still on ns list\n", | ||
| 735 | __func__, profile->base.name); | ||
| 736 | BUG(); | ||
| 737 | } | ||
| 738 | |||
| 739 | /* free children profiles */ | ||
| 740 | policy_destroy(&profile->base); | ||
| 741 | aa_put_profile(profile->parent); | ||
| 742 | |||
| 743 | aa_put_namespace(profile->ns); | ||
| 744 | kzfree(profile->rename); | ||
| 745 | |||
| 746 | aa_free_file_rules(&profile->file); | ||
| 747 | aa_free_cap_rules(&profile->caps); | ||
| 748 | aa_free_rlimit_rules(&profile->rlimits); | ||
| 749 | |||
| 750 | aa_free_sid(profile->sid); | ||
| 751 | aa_put_dfa(profile->xmatch); | ||
| 752 | |||
| 753 | aa_put_profile(profile->replacedby); | ||
| 754 | |||
| 755 | kzfree(profile); | ||
| 756 | } | ||
| 757 | |||
| 758 | /** | ||
| 759 | * aa_free_profile_kref - free aa_profile by kref (called by aa_put_profile) | ||
| 760 | * @kr: kref callback for freeing of a profile (NOT NULL) | ||
| 761 | */ | ||
| 762 | void aa_free_profile_kref(struct kref *kref) | ||
| 763 | { | ||
| 764 | struct aa_profile *p = container_of(kref, struct aa_profile, | ||
| 765 | base.count); | ||
| 766 | |||
| 767 | free_profile(p); | ||
| 768 | } | ||
| 769 | |||
| 770 | /* TODO: profile accounting - setup in remove */ | ||
| 771 | |||
| 772 | /** | ||
| 773 | * __find_child - find a profile on @head list with a name matching @name | ||
| 774 | * @head: list to search (NOT NULL) | ||
| 775 | * @name: name of profile (NOT NULL) | ||
| 776 | * | ||
| 777 | * Requires: ns lock protecting list be held | ||
| 778 | * | ||
| 779 | * Returns: unrefcounted profile ptr, or NULL if not found | ||
| 780 | */ | ||
| 781 | static struct aa_profile *__find_child(struct list_head *head, const char *name) | ||
| 782 | { | ||
| 783 | return (struct aa_profile *)__policy_find(head, name); | ||
| 784 | } | ||
| 785 | |||
| 786 | /** | ||
| 787 | * __strn_find_child - find a profile on @head list using substring of @name | ||
| 788 | * @head: list to search (NOT NULL) | ||
| 789 | * @name: name of profile (NOT NULL) | ||
| 790 | * @len: length of @name substring to match | ||
| 791 | * | ||
| 792 | * Requires: ns lock protecting list be held | ||
| 793 | * | ||
| 794 | * Returns: unrefcounted profile ptr, or NULL if not found | ||
| 795 | */ | ||
| 796 | static struct aa_profile *__strn_find_child(struct list_head *head, | ||
| 797 | const char *name, int len) | ||
| 798 | { | ||
| 799 | return (struct aa_profile *)__policy_strn_find(head, name, len); | ||
| 800 | } | ||
| 801 | |||
| 802 | /** | ||
| 803 | * aa_find_child - find a profile by @name in @parent | ||
| 804 | * @parent: profile to search (NOT NULL) | ||
| 805 | * @name: profile name to search for (NOT NULL) | ||
| 806 | * | ||
| 807 | * Returns: a refcounted profile or NULL if not found | ||
| 808 | */ | ||
| 809 | struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name) | ||
| 810 | { | ||
| 811 | struct aa_profile *profile; | ||
| 812 | |||
| 813 | read_lock(&parent->ns->lock); | ||
| 814 | profile = aa_get_profile(__find_child(&parent->base.profiles, name)); | ||
| 815 | read_unlock(&parent->ns->lock); | ||
| 816 | |||
| 817 | /* refcount released by caller */ | ||
| 818 | return profile; | ||
| 819 | } | ||
| 820 | |||
| 821 | /** | ||
| 822 | * __lookup_parent - lookup the parent of a profile of name @hname | ||
| 823 | * @ns: namespace to lookup profile in (NOT NULL) | ||
| 824 | * @hname: hierarchical profile name to find parent of (NOT NULL) | ||
| 825 | * | ||
| 826 | * Lookups up the parent of a fully qualified profile name, the profile | ||
| 827 | * that matches hname does not need to exist, in general this | ||
| 828 | * is used to load a new profile. | ||
| 829 | * | ||
| 830 | * Requires: ns->lock be held | ||
| 831 | * | ||
| 832 | * Returns: unrefcounted policy or NULL if not found | ||
| 833 | */ | ||
| 834 | static struct aa_policy *__lookup_parent(struct aa_namespace *ns, | ||
| 835 | const char *hname) | ||
| 836 | { | ||
| 837 | struct aa_policy *policy; | ||
| 838 | struct aa_profile *profile = NULL; | ||
| 839 | char *split; | ||
| 840 | |||
| 841 | policy = &ns->base; | ||
| 842 | |||
| 843 | for (split = strstr(hname, "//"); split;) { | ||
| 844 | profile = __strn_find_child(&policy->profiles, hname, | ||
| 845 | split - hname); | ||
| 846 | if (!profile) | ||
| 847 | return NULL; | ||
| 848 | policy = &profile->base; | ||
| 849 | hname = split + 2; | ||
| 850 | split = strstr(hname, "//"); | ||
| 851 | } | ||
| 852 | if (!profile) | ||
| 853 | return &ns->base; | ||
| 854 | return &profile->base; | ||
| 855 | } | ||
| 856 | |||
| 857 | /** | ||
| 858 | * __lookup_profile - lookup the profile matching @hname | ||
| 859 | * @base: base list to start looking up profile name from (NOT NULL) | ||
| 860 | * @hname: hierarchical profile name (NOT NULL) | ||
| 861 | * | ||
| 862 | * Requires: ns->lock be held | ||
| 863 | * | ||
| 864 | * Returns: unrefcounted profile pointer or NULL if not found | ||
| 865 | * | ||
| 866 | * Do a relative name lookup, recursing through profile tree. | ||
| 867 | */ | ||
| 868 | static struct aa_profile *__lookup_profile(struct aa_policy *base, | ||
| 869 | const char *hname) | ||
| 870 | { | ||
| 871 | struct aa_profile *profile = NULL; | ||
| 872 | char *split; | ||
| 873 | |||
| 874 | for (split = strstr(hname, "//"); split;) { | ||
| 875 | profile = __strn_find_child(&base->profiles, hname, | ||
| 876 | split - hname); | ||
| 877 | if (!profile) | ||
| 878 | return NULL; | ||
| 879 | |||
| 880 | base = &profile->base; | ||
| 881 | hname = split + 2; | ||
| 882 | split = strstr(hname, "//"); | ||
| 883 | } | ||
| 884 | |||
| 885 | profile = __find_child(&base->profiles, hname); | ||
| 886 | |||
| 887 | return profile; | ||
| 888 | } | ||
| 889 | |||
| 890 | /** | ||
| 891 | * aa_lookup_profile - find a profile by its full or partial name | ||
| 892 | * @ns: the namespace to start from (NOT NULL) | ||
| 893 | * @hname: name to do lookup on. Does not contain namespace prefix (NOT NULL) | ||
| 894 | * | ||
| 895 | * Returns: refcounted profile or NULL if not found | ||
| 896 | */ | ||
| 897 | struct aa_profile *aa_lookup_profile(struct aa_namespace *ns, const char *hname) | ||
| 898 | { | ||
| 899 | struct aa_profile *profile; | ||
| 900 | |||
| 901 | read_lock(&ns->lock); | ||
| 902 | profile = aa_get_profile(__lookup_profile(&ns->base, hname)); | ||
| 903 | read_unlock(&ns->lock); | ||
| 904 | |||
| 905 | /* refcount released by caller */ | ||
| 906 | return profile; | ||
| 907 | } | ||
| 908 | |||
| 909 | /** | ||
| 910 | * replacement_allowed - test to see if replacement is allowed | ||
| 911 | * @profile: profile to test if it can be replaced (MAYBE NULL) | ||
| 912 | * @noreplace: true if replacement shouldn't be allowed but addition is okay | ||
| 913 | * @info: Returns - info about why replacement failed (NOT NULL) | ||
| 914 | * | ||
| 915 | * Returns: %0 if replacement allowed else error code | ||
| 916 | */ | ||
| 917 | static int replacement_allowed(struct aa_profile *profile, int noreplace, | ||
| 918 | const char **info) | ||
| 919 | { | ||
| 920 | if (profile) { | ||
| 921 | if (profile->flags & PFLAG_IMMUTABLE) { | ||
| 922 | *info = "cannot replace immutible profile"; | ||
| 923 | return -EPERM; | ||
| 924 | } else if (noreplace) { | ||
| 925 | *info = "profile already exists"; | ||
| 926 | return -EEXIST; | ||
| 927 | } | ||
| 928 | } | ||
| 929 | return 0; | ||
| 930 | } | ||
| 931 | |||
| 932 | /** | ||
| 933 | * __add_new_profile - simple wrapper around __list_add_profile | ||
| 934 | * @ns: namespace that profile is being added to (NOT NULL) | ||
| 935 | * @policy: the policy container to add the profile to (NOT NULL) | ||
| 936 | * @profile: profile to add (NOT NULL) | ||
| 937 | * | ||
| 938 | * add a profile to a list and do other required basic allocations | ||
| 939 | */ | ||
| 940 | static void __add_new_profile(struct aa_namespace *ns, struct aa_policy *policy, | ||
| 941 | struct aa_profile *profile) | ||
| 942 | { | ||
| 943 | if (policy != &ns->base) | ||
| 944 | /* released on profile replacement or free_profile */ | ||
| 945 | profile->parent = aa_get_profile((struct aa_profile *) policy); | ||
| 946 | __list_add_profile(&policy->profiles, profile); | ||
| 947 | /* released on free_profile */ | ||
| 948 | profile->sid = aa_alloc_sid(); | ||
| 949 | profile->ns = aa_get_namespace(ns); | ||
| 950 | } | ||
| 951 | |||
| 952 | /** | ||
| 953 | * aa_audit_policy - Do auditing of policy changes | ||
| 954 | * @op: policy operation being performed | ||
| 955 | * @gfp: memory allocation flags | ||
| 956 | * @name: name of profile being manipulated (NOT NULL) | ||
| 957 | * @info: any extra information to be audited (MAYBE NULL) | ||
| 958 | * @error: error code | ||
| 959 | * | ||
| 960 | * Returns: the error to be returned after audit is done | ||
| 961 | */ | ||
| 962 | static int audit_policy(int op, gfp_t gfp, const char *name, const char *info, | ||
| 963 | int error) | ||
| 964 | { | ||
| 965 | struct common_audit_data sa; | ||
| 966 | COMMON_AUDIT_DATA_INIT(&sa, NONE); | ||
| 967 | sa.aad.op = op; | ||
| 968 | sa.aad.name = name; | ||
| 969 | sa.aad.info = info; | ||
| 970 | sa.aad.error = error; | ||
| 971 | |||
| 972 | return aa_audit(AUDIT_APPARMOR_STATUS, __aa_current_profile(), gfp, | ||
| 973 | &sa, NULL); | ||
| 974 | } | ||
| 975 | |||
| 976 | /** | ||
| 977 | * aa_may_manage_policy - can the current task manage policy | ||
| 978 | * @op: the policy manipulation operation being done | ||
| 979 | * | ||
| 980 | * Returns: true if the task is allowed to manipulate policy | ||
| 981 | */ | ||
| 982 | bool aa_may_manage_policy(int op) | ||
| 983 | { | ||
| 984 | /* check if loading policy is locked out */ | ||
| 985 | if (aa_g_lock_policy) { | ||
| 986 | audit_policy(op, GFP_KERNEL, NULL, "policy_locked", -EACCES); | ||
| 987 | return 0; | ||
| 988 | } | ||
| 989 | |||
| 990 | if (!capable(CAP_MAC_ADMIN)) { | ||
| 991 | audit_policy(op, GFP_KERNEL, NULL, "not policy admin", -EACCES); | ||
| 992 | return 0; | ||
| 993 | } | ||
| 994 | |||
| 995 | return 1; | ||
| 996 | } | ||
| 997 | |||
| 998 | /** | ||
| 999 | * aa_replace_profiles - replace profile(s) on the profile list | ||
| 1000 | * @udata: serialized data stream (NOT NULL) | ||
| 1001 | * @size: size of the serialized data stream | ||
| 1002 | * @noreplace: true if only doing addition, no replacement allowed | ||
| 1003 | * | ||
| 1004 | * unpack and replace a profile on the profile list and uses of that profile | ||
| 1005 | * by any aa_task_cxt. If the profile does not exist on the profile list | ||
| 1006 | * it is added. | ||
| 1007 | * | ||
| 1008 | * Returns: size of data consumed else error code on failure. | ||
| 1009 | */ | ||
| 1010 | ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) | ||
| 1011 | { | ||
| 1012 | struct aa_policy *policy; | ||
| 1013 | struct aa_profile *old_profile = NULL, *new_profile = NULL; | ||
| 1014 | struct aa_profile *rename_profile = NULL; | ||
| 1015 | struct aa_namespace *ns = NULL; | ||
| 1016 | const char *ns_name, *name = NULL, *info = NULL; | ||
| 1017 | int op = OP_PROF_REPL; | ||
| 1018 | ssize_t error; | ||
| 1019 | |||
| 1020 | /* released below */ | ||
| 1021 | new_profile = aa_unpack(udata, size, &ns_name); | ||
| 1022 | if (IS_ERR(new_profile)) { | ||
| 1023 | error = PTR_ERR(new_profile); | ||
| 1024 | new_profile = NULL; | ||
| 1025 | goto fail; | ||
| 1026 | } | ||
| 1027 | |||
| 1028 | /* released below */ | ||
| 1029 | ns = aa_prepare_namespace(ns_name); | ||
| 1030 | if (!ns) { | ||
| 1031 | info = "failed to prepare namespace"; | ||
| 1032 | error = -ENOMEM; | ||
| 1033 | name = ns_name; | ||
| 1034 | goto fail; | ||
| 1035 | } | ||
| 1036 | |||
| 1037 | name = new_profile->base.hname; | ||
| 1038 | |||
| 1039 | write_lock(&ns->lock); | ||
| 1040 | /* no ref on policy only use inside lock */ | ||
| 1041 | policy = __lookup_parent(ns, new_profile->base.hname); | ||
| 1042 | |||
| 1043 | if (!policy) { | ||
| 1044 | info = "parent does not exist"; | ||
| 1045 | error = -ENOENT; | ||
| 1046 | goto audit; | ||
| 1047 | } | ||
| 1048 | |||
| 1049 | old_profile = __find_child(&policy->profiles, new_profile->base.name); | ||
| 1050 | /* released below */ | ||
| 1051 | aa_get_profile(old_profile); | ||
| 1052 | |||
| 1053 | if (new_profile->rename) { | ||
| 1054 | rename_profile = __lookup_profile(&ns->base, | ||
| 1055 | new_profile->rename); | ||
| 1056 | /* released below */ | ||
| 1057 | aa_get_profile(rename_profile); | ||
| 1058 | |||
| 1059 | if (!rename_profile) { | ||
| 1060 | info = "profile to rename does not exist"; | ||
| 1061 | name = new_profile->rename; | ||
| 1062 | error = -ENOENT; | ||
| 1063 | goto audit; | ||
| 1064 | } | ||
| 1065 | } | ||
| 1066 | |||
| 1067 | error = replacement_allowed(old_profile, noreplace, &info); | ||
| 1068 | if (error) | ||
| 1069 | goto audit; | ||
| 1070 | |||
| 1071 | error = replacement_allowed(rename_profile, noreplace, &info); | ||
| 1072 | if (error) | ||
| 1073 | goto audit; | ||
| 1074 | |||
| 1075 | audit: | ||
| 1076 | if (!old_profile && !rename_profile) | ||
| 1077 | op = OP_PROF_LOAD; | ||
| 1078 | |||
| 1079 | error = audit_policy(op, GFP_ATOMIC, name, info, error); | ||
| 1080 | |||
| 1081 | if (!error) { | ||
| 1082 | if (rename_profile) | ||
| 1083 | __replace_profile(rename_profile, new_profile); | ||
| 1084 | if (old_profile) { | ||
| 1085 | /* when there are both rename and old profiles | ||
| 1086 | * inherit old profiles sid | ||
| 1087 | */ | ||
| 1088 | if (rename_profile) | ||
| 1089 | aa_free_sid(new_profile->sid); | ||
| 1090 | __replace_profile(old_profile, new_profile); | ||
| 1091 | } | ||
| 1092 | if (!(old_profile || rename_profile)) | ||
| 1093 | __add_new_profile(ns, policy, new_profile); | ||
| 1094 | } | ||
| 1095 | write_unlock(&ns->lock); | ||
| 1096 | |||
| 1097 | out: | ||
| 1098 | aa_put_namespace(ns); | ||
| 1099 | aa_put_profile(rename_profile); | ||
| 1100 | aa_put_profile(old_profile); | ||
| 1101 | aa_put_profile(new_profile); | ||
| 1102 | if (error) | ||
| 1103 | return error; | ||
| 1104 | return size; | ||
| 1105 | |||
| 1106 | fail: | ||
| 1107 | error = audit_policy(op, GFP_KERNEL, name, info, error); | ||
| 1108 | goto out; | ||
| 1109 | } | ||
| 1110 | |||
| 1111 | /** | ||
| 1112 | * aa_remove_profiles - remove profile(s) from the system | ||
| 1113 | * @fqname: name of the profile or namespace to remove (NOT NULL) | ||
| 1114 | * @size: size of the name | ||
| 1115 | * | ||
| 1116 | * Remove a profile or sub namespace from the current namespace, so that | ||
| 1117 | * they can not be found anymore and mark them as replaced by unconfined | ||
| 1118 | * | ||
| 1119 | * NOTE: removing confinement does not restore rlimits to preconfinemnet values | ||
| 1120 | * | ||
| 1121 | * Returns: size of data consume else error code if fails | ||
| 1122 | */ | ||
| 1123 | ssize_t aa_remove_profiles(char *fqname, size_t size) | ||
| 1124 | { | ||
| 1125 | struct aa_namespace *root, *ns = NULL; | ||
| 1126 | struct aa_profile *profile = NULL; | ||
| 1127 | const char *name = fqname, *info = NULL; | ||
| 1128 | ssize_t error = 0; | ||
| 1129 | |||
| 1130 | if (*fqname == 0) { | ||
| 1131 | info = "no profile specified"; | ||
| 1132 | error = -ENOENT; | ||
| 1133 | goto fail; | ||
| 1134 | } | ||
| 1135 | |||
| 1136 | root = aa_current_profile()->ns; | ||
| 1137 | |||
| 1138 | if (fqname[0] == ':') { | ||
| 1139 | char *ns_name; | ||
| 1140 | name = aa_split_fqname(fqname, &ns_name); | ||
| 1141 | if (ns_name) { | ||
| 1142 | /* released below */ | ||
| 1143 | ns = aa_find_namespace(root, ns_name); | ||
| 1144 | if (!ns) { | ||
| 1145 | info = "namespace does not exist"; | ||
| 1146 | error = -ENOENT; | ||
| 1147 | goto fail; | ||
| 1148 | } | ||
| 1149 | } | ||
| 1150 | } else | ||
| 1151 | /* released below */ | ||
| 1152 | ns = aa_get_namespace(root); | ||
| 1153 | |||
| 1154 | write_lock(&ns->lock); | ||
| 1155 | if (!name) { | ||
| 1156 | /* remove namespace - can only happen if fqname[0] == ':' */ | ||
| 1157 | __remove_namespace(ns); | ||
| 1158 | } else { | ||
| 1159 | /* remove profile */ | ||
| 1160 | profile = aa_get_profile(__lookup_profile(&ns->base, name)); | ||
| 1161 | if (!profile) { | ||
| 1162 | error = -ENOENT; | ||
| 1163 | info = "profile does not exist"; | ||
| 1164 | goto fail_ns_lock; | ||
| 1165 | } | ||
| 1166 | name = profile->base.hname; | ||
| 1167 | __remove_profile(profile); | ||
| 1168 | } | ||
| 1169 | write_unlock(&ns->lock); | ||
| 1170 | |||
| 1171 | /* don't fail removal if audit fails */ | ||
| 1172 | (void) audit_policy(OP_PROF_RM, GFP_KERNEL, name, info, error); | ||
| 1173 | aa_put_namespace(ns); | ||
| 1174 | aa_put_profile(profile); | ||
| 1175 | return size; | ||
| 1176 | |||
| 1177 | fail_ns_lock: | ||
| 1178 | write_unlock(&ns->lock); | ||
| 1179 | aa_put_namespace(ns); | ||
| 1180 | |||
| 1181 | fail: | ||
| 1182 | (void) audit_policy(OP_PROF_RM, GFP_KERNEL, name, info, error); | ||
| 1183 | return error; | ||
| 1184 | } | ||
diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c new file mode 100644 index 000000000000..eb3700e9fd37 --- /dev/null +++ b/security/apparmor/policy_unpack.c | |||
| @@ -0,0 +1,703 @@ | |||
| 1 | /* | ||
| 2 | * AppArmor security module | ||
| 3 | * | ||
| 4 | * This file contains AppArmor functions for unpacking policy loaded from | ||
| 5 | * userspace. | ||
| 6 | * | ||
| 7 | * Copyright (C) 1998-2008 Novell/SUSE | ||
| 8 | * Copyright 2009-2010 Canonical Ltd. | ||
| 9 | * | ||
| 10 | * This program is free software; you can redistribute it and/or | ||
| 11 | * modify it under the terms of the GNU General Public License as | ||
| 12 | * published by the Free Software Foundation, version 2 of the | ||
| 13 | * License. | ||
| 14 | * | ||
| 15 | * AppArmor uses a serialized binary format for loading policy. | ||
| 16 | * To find policy format documentation look in Documentation/apparmor.txt | ||
| 17 | * All policy is validated before it is used. | ||
| 18 | */ | ||
| 19 | |||
| 20 | #include <asm/unaligned.h> | ||
| 21 | #include <linux/ctype.h> | ||
| 22 | #include <linux/errno.h> | ||
| 23 | |||
| 24 | #include "include/apparmor.h" | ||
| 25 | #include "include/audit.h" | ||
| 26 | #include "include/context.h" | ||
| 27 | #include "include/match.h" | ||
| 28 | #include "include/policy.h" | ||
| 29 | #include "include/policy_unpack.h" | ||
| 30 | #include "include/sid.h" | ||
| 31 | |||
| 32 | /* | ||
| 33 | * The AppArmor interface treats data as a type byte followed by the | ||
| 34 | * actual data. The interface has the notion of a a named entry | ||
| 35 | * which has a name (AA_NAME typecode followed by name string) followed by | ||
| 36 | * the entries typecode and data. Named types allow for optional | ||
| 37 | * elements and extensions to be added and tested for without breaking | ||
| 38 | * backwards compatibility. | ||
| 39 | */ | ||
| 40 | |||
| 41 | enum aa_code { | ||
| 42 | AA_U8, | ||
| 43 | AA_U16, | ||
| 44 | AA_U32, | ||
| 45 | AA_U64, | ||
| 46 | AA_NAME, /* same as string except it is items name */ | ||
| 47 | AA_STRING, | ||
| 48 | AA_BLOB, | ||
| 49 | AA_STRUCT, | ||
| 50 | AA_STRUCTEND, | ||
| 51 | AA_LIST, | ||
| 52 | AA_LISTEND, | ||
| 53 | AA_ARRAY, | ||
| 54 | AA_ARRAYEND, | ||
| 55 | }; | ||
| 56 | |||
| 57 | /* | ||
| 58 | * aa_ext is the read of the buffer containing the serialized profile. The | ||
| 59 | * data is copied into a kernel buffer in apparmorfs and then handed off to | ||
| 60 | * the unpack routines. | ||
| 61 | */ | ||
| 62 | struct aa_ext { | ||
| 63 | void *start; | ||
| 64 | void *end; | ||
| 65 | void *pos; /* pointer to current position in the buffer */ | ||
| 66 | u32 version; | ||
| 67 | }; | ||
| 68 | |||
| 69 | /* audit callback for unpack fields */ | ||
| 70 | static void audit_cb(struct audit_buffer *ab, void *va) | ||
| 71 | { | ||
| 72 | struct common_audit_data *sa = va; | ||
| 73 | if (sa->aad.iface.target) { | ||
| 74 | struct aa_profile *name = sa->aad.iface.target; | ||
| 75 | audit_log_format(ab, " name="); | ||
| 76 | audit_log_untrustedstring(ab, name->base.hname); | ||
| 77 | } | ||
| 78 | if (sa->aad.iface.pos) | ||
| 79 | audit_log_format(ab, " offset=%ld", sa->aad.iface.pos); | ||
| 80 | } | ||
| 81 | |||
| 82 | /** | ||
| 83 | * audit_iface - do audit message for policy unpacking/load/replace/remove | ||
| 84 | * @new: profile if it has been allocated (MAYBE NULL) | ||
| 85 | * @name: name of the profile being manipulated (MAYBE NULL) | ||
| 86 | * @info: any extra info about the failure (MAYBE NULL) | ||
| 87 | * @e: buffer position info (NOT NULL) | ||
| 88 | * @error: error code | ||
| 89 | * | ||
| 90 | * Returns: %0 or error | ||
| 91 | */ | ||
| 92 | static int audit_iface(struct aa_profile *new, const char *name, | ||
| 93 | const char *info, struct aa_ext *e, int error) | ||
| 94 | { | ||
| 95 | struct aa_profile *profile = __aa_current_profile(); | ||
| 96 | struct common_audit_data sa; | ||
| 97 | COMMON_AUDIT_DATA_INIT(&sa, NONE); | ||
| 98 | sa.aad.iface.pos = e->pos - e->start; | ||
| 99 | sa.aad.iface.target = new; | ||
| 100 | sa.aad.name = name; | ||
| 101 | sa.aad.info = info; | ||
| 102 | sa.aad.error = error; | ||
| 103 | |||
| 104 | return aa_audit(AUDIT_APPARMOR_STATUS, profile, GFP_KERNEL, &sa, | ||
| 105 | audit_cb); | ||
| 106 | } | ||
| 107 | |||
| 108 | /* test if read will be in packed data bounds */ | ||
| 109 | static bool inbounds(struct aa_ext *e, size_t size) | ||
| 110 | { | ||
| 111 | return (size <= e->end - e->pos); | ||
| 112 | } | ||
| 113 | |||
| 114 | /** | ||
| 115 | * aa_u16_chunck - test and do bounds checking for a u16 size based chunk | ||
| 116 | * @e: serialized data read head (NOT NULL) | ||
| 117 | * @chunk: start address for chunk of data (NOT NULL) | ||
| 118 | * | ||
| 119 | * Returns: the size of chunk found with the read head at the end of the chunk. | ||
| 120 | */ | ||
| 121 | static size_t unpack_u16_chunk(struct aa_ext *e, char **chunk) | ||
| 122 | { | ||
| 123 | size_t size = 0; | ||
| 124 | |||
| 125 | if (!inbounds(e, sizeof(u16))) | ||
| 126 | return 0; | ||
| 127 | size = le16_to_cpu(get_unaligned((u16 *) e->pos)); | ||
| 128 | e->pos += sizeof(u16); | ||
| 129 | if (!inbounds(e, size)) | ||
| 130 | return 0; | ||
| 131 | *chunk = e->pos; | ||
| 132 | e->pos += size; | ||
| 133 | return size; | ||
| 134 | } | ||
| 135 | |||
| 136 | /* unpack control byte */ | ||
| 137 | static bool unpack_X(struct aa_ext *e, enum aa_code code) | ||
| 138 | { | ||
| 139 | if (!inbounds(e, 1)) | ||
| 140 | return 0; | ||
| 141 | if (*(u8 *) e->pos != code) | ||
| 142 | return 0; | ||
| 143 | e->pos++; | ||
| 144 | return 1; | ||
| 145 | } | ||
| 146 | |||
| 147 | /** | ||
| 148 | * unpack_nameX - check is the next element is of type X with a name of @name | ||
| 149 | * @e: serialized data extent information (NOT NULL) | ||
| 150 | * @code: type code | ||
| 151 | * @name: name to match to the serialized element. (MAYBE NULL) | ||
| 152 | * | ||
| 153 | * check that the next serialized data element is of type X and has a tag | ||
| 154 | * name @name. If @name is specified then there must be a matching | ||
| 155 | * name element in the stream. If @name is NULL any name element will be | ||
| 156 | * skipped and only the typecode will be tested. | ||
| 157 | * | ||
| 158 | * Returns 1 on success (both type code and name tests match) and the read | ||
| 159 | * head is advanced past the headers | ||
| 160 | * | ||
| 161 | * Returns: 0 if either match fails, the read head does not move | ||
| 162 | */ | ||
| 163 | static bool unpack_nameX(struct aa_ext *e, enum aa_code code, const char *name) | ||
| 164 | { | ||
| 165 | /* | ||
| 166 | * May need to reset pos if name or type doesn't match | ||
| 167 | */ | ||
| 168 | void *pos = e->pos; | ||
| 169 | /* | ||
| 170 | * Check for presence of a tagname, and if present name size | ||
| 171 | * AA_NAME tag value is a u16. | ||
| 172 | */ | ||
| 173 | if (unpack_X(e, AA_NAME)) { | ||
| 174 | char *tag = NULL; | ||
| 175 | size_t size = unpack_u16_chunk(e, &tag); | ||
| 176 | /* if a name is specified it must match. otherwise skip tag */ | ||
| 177 | if (name && (!size || strcmp(name, tag))) | ||
| 178 | goto fail; | ||
| 179 | } else if (name) { | ||
| 180 | /* if a name is specified and there is no name tag fail */ | ||
| 181 | goto fail; | ||
| 182 | } | ||
| 183 | |||
| 184 | /* now check if type code matches */ | ||
| 185 | if (unpack_X(e, code)) | ||
| 186 | return 1; | ||
| 187 | |||
| 188 | fail: | ||
| 189 | e->pos = pos; | ||
| 190 | return 0; | ||
| 191 | } | ||
| 192 | |||
| 193 | static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) | ||
| 194 | { | ||
| 195 | if (unpack_nameX(e, AA_U32, name)) { | ||
| 196 | if (!inbounds(e, sizeof(u32))) | ||
| 197 | return 0; | ||
| 198 | if (data) | ||
| 199 | *data = le32_to_cpu(get_unaligned((u32 *) e->pos)); | ||
| 200 | e->pos += sizeof(u32); | ||
| 201 | return 1; | ||
| 202 | } | ||
| 203 | return 0; | ||
| 204 | } | ||
| 205 | |||
| 206 | static bool unpack_u64(struct aa_ext *e, u64 *data, const char *name) | ||
| 207 | { | ||
| 208 | if (unpack_nameX(e, AA_U64, name)) { | ||
| 209 | if (!inbounds(e, sizeof(u64))) | ||
| 210 | return 0; | ||
| 211 | if (data) | ||
| 212 | *data = le64_to_cpu(get_unaligned((u64 *) e->pos)); | ||
| 213 | e->pos += sizeof(u64); | ||
| 214 | return 1; | ||
| 215 | } | ||
| 216 | return 0; | ||
| 217 | } | ||
| 218 | |||
| 219 | static size_t unpack_array(struct aa_ext *e, const char *name) | ||
| 220 | { | ||
| 221 | if (unpack_nameX(e, AA_ARRAY, name)) { | ||
| 222 | int size; | ||
| 223 | if (!inbounds(e, sizeof(u16))) | ||
| 224 | return 0; | ||
| 225 | size = (int)le16_to_cpu(get_unaligned((u16 *) e->pos)); | ||
| 226 | e->pos += sizeof(u16); | ||
| 227 | return size; | ||
| 228 | } | ||
| 229 | return 0; | ||
| 230 | } | ||
| 231 | |||
| 232 | static size_t unpack_blob(struct aa_ext *e, char **blob, const char *name) | ||
| 233 | { | ||
| 234 | if (unpack_nameX(e, AA_BLOB, name)) { | ||
| 235 | u32 size; | ||
| 236 | if (!inbounds(e, sizeof(u32))) | ||
| 237 | return 0; | ||
| 238 | size = le32_to_cpu(get_unaligned((u32 *) e->pos)); | ||
| 239 | e->pos += sizeof(u32); | ||
| 240 | if (inbounds(e, (size_t) size)) { | ||
| 241 | *blob = e->pos; | ||
| 242 | e->pos += size; | ||
| 243 | return size; | ||
| 244 | } | ||
| 245 | } | ||
| 246 | return 0; | ||
| 247 | } | ||
| 248 | |||
| 249 | static int unpack_str(struct aa_ext *e, const char **string, const char *name) | ||
| 250 | { | ||
| 251 | char *src_str; | ||
| 252 | size_t size = 0; | ||
| 253 | void *pos = e->pos; | ||
| 254 | *string = NULL; | ||
| 255 | if (unpack_nameX(e, AA_STRING, name)) { | ||
| 256 | size = unpack_u16_chunk(e, &src_str); | ||
| 257 | if (size) { | ||
| 258 | /* strings are null terminated, length is size - 1 */ | ||
| 259 | if (src_str[size - 1] != 0) | ||
| 260 | goto fail; | ||
| 261 | *string = src_str; | ||
| 262 | } | ||
| 263 | } | ||
| 264 | return size; | ||
| 265 | |||
| 266 | fail: | ||
| 267 | e->pos = pos; | ||
| 268 | return 0; | ||
| 269 | } | ||
| 270 | |||
| 271 | static int unpack_strdup(struct aa_ext *e, char **string, const char *name) | ||
| 272 | { | ||
| 273 | const char *tmp; | ||
| 274 | void *pos = e->pos; | ||
| 275 | int res = unpack_str(e, &tmp, name); | ||
| 276 | *string = NULL; | ||
| 277 | |||
| 278 | if (!res) | ||
| 279 | return 0; | ||
| 280 | |||
| 281 | *string = kmemdup(tmp, res, GFP_KERNEL); | ||
| 282 | if (!*string) { | ||
| 283 | e->pos = pos; | ||
| 284 | return 0; | ||
| 285 | } | ||
| 286 | |||
| 287 | return res; | ||
| 288 | } | ||
| 289 | |||
| 290 | /** | ||
| 291 | * verify_accept - verify the accept tables of a dfa | ||
| 292 | * @dfa: dfa to verify accept tables of (NOT NULL) | ||
| 293 | * @flags: flags governing dfa | ||
| 294 | * | ||
| 295 | * Returns: 1 if valid accept tables else 0 if error | ||
| 296 | */ | ||
| 297 | static bool verify_accept(struct aa_dfa *dfa, int flags) | ||
| 298 | { | ||
| 299 | int i; | ||
| 300 | |||
| 301 | /* verify accept permissions */ | ||
| 302 | for (i = 0; i < dfa->tables[YYTD_ID_ACCEPT]->td_lolen; i++) { | ||
| 303 | int mode = ACCEPT_TABLE(dfa)[i]; | ||
| 304 | |||
| 305 | if (mode & ~DFA_VALID_PERM_MASK) | ||
| 306 | return 0; | ||
| 307 | |||
| 308 | if (ACCEPT_TABLE2(dfa)[i] & ~DFA_VALID_PERM2_MASK) | ||
| 309 | return 0; | ||
| 310 | } | ||
| 311 | return 1; | ||
| 312 | } | ||
| 313 | |||
| 314 | /** | ||
| 315 | * unpack_dfa - unpack a file rule dfa | ||
| 316 | * @e: serialized data extent information (NOT NULL) | ||
| 317 | * | ||
| 318 | * returns dfa or ERR_PTR or NULL if no dfa | ||
| 319 | */ | ||
| 320 | static struct aa_dfa *unpack_dfa(struct aa_ext *e) | ||
| 321 | { | ||
| 322 | char *blob = NULL; | ||
| 323 | size_t size; | ||
| 324 | struct aa_dfa *dfa = NULL; | ||
| 325 | |||
| 326 | size = unpack_blob(e, &blob, "aadfa"); | ||
| 327 | if (size) { | ||
| 328 | /* | ||
| 329 | * The dfa is aligned with in the blob to 8 bytes | ||
| 330 | * from the beginning of the stream. | ||
| 331 | */ | ||
| 332 | size_t sz = blob - (char *)e->start; | ||
| 333 | size_t pad = ALIGN(sz, 8) - sz; | ||
| 334 | int flags = TO_ACCEPT1_FLAG(YYTD_DATA32) | | ||
| 335 | TO_ACCEPT2_FLAG(YYTD_DATA32); | ||
| 336 | |||
| 337 | |||
| 338 | if (aa_g_paranoid_load) | ||
| 339 | flags |= DFA_FLAG_VERIFY_STATES; | ||
| 340 | |||
| 341 | dfa = aa_dfa_unpack(blob + pad, size - pad, flags); | ||
| 342 | |||
| 343 | if (IS_ERR(dfa)) | ||
| 344 | return dfa; | ||
| 345 | |||
| 346 | if (!verify_accept(dfa, flags)) | ||
| 347 | goto fail; | ||
| 348 | } | ||
| 349 | |||
| 350 | return dfa; | ||
| 351 | |||
| 352 | fail: | ||
| 353 | aa_put_dfa(dfa); | ||
| 354 | return ERR_PTR(-EPROTO); | ||
| 355 | } | ||
| 356 | |||
| 357 | /** | ||
| 358 | * unpack_trans_table - unpack a profile transition table | ||
| 359 | * @e: serialized data extent information (NOT NULL) | ||
| 360 | * @profile: profile to add the accept table to (NOT NULL) | ||
| 361 | * | ||
| 362 | * Returns: 1 if table succesfully unpacked | ||
| 363 | */ | ||
| 364 | static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile) | ||
| 365 | { | ||
| 366 | void *pos = e->pos; | ||
| 367 | |||
| 368 | /* exec table is optional */ | ||
| 369 | if (unpack_nameX(e, AA_STRUCT, "xtable")) { | ||
| 370 | int i, size; | ||
| 371 | |||
| 372 | size = unpack_array(e, NULL); | ||
| 373 | /* currently 4 exec bits and entries 0-3 are reserved iupcx */ | ||
| 374 | if (size > 16 - 4) | ||
| 375 | goto fail; | ||
| 376 | profile->file.trans.table = kzalloc(sizeof(char *) * size, | ||
| 377 | GFP_KERNEL); | ||
| 378 | if (!profile->file.trans.table) | ||
| 379 | goto fail; | ||
| 380 | |||
| 381 | profile->file.trans.size = size; | ||
| 382 | for (i = 0; i < size; i++) { | ||
| 383 | char *str; | ||
| 384 | int c, j, size = unpack_strdup(e, &str, NULL); | ||
| 385 | /* unpack_strdup verifies that the last character is | ||
| 386 | * null termination byte. | ||
| 387 | */ | ||
| 388 | if (!size) | ||
| 389 | goto fail; | ||
| 390 | profile->file.trans.table[i] = str; | ||
| 391 | /* verify that name doesn't start with space */ | ||
| 392 | if (isspace(*str)) | ||
| 393 | goto fail; | ||
| 394 | |||
| 395 | /* count internal # of internal \0 */ | ||
| 396 | for (c = j = 0; j < size - 2; j++) { | ||
| 397 | if (!str[j]) | ||
| 398 | c++; | ||
| 399 | } | ||
| 400 | if (*str == ':') { | ||
| 401 | /* beginning with : requires an embedded \0, | ||
| 402 | * verify that exactly 1 internal \0 exists | ||
| 403 | * trailing \0 already verified by unpack_strdup | ||
| 404 | */ | ||
| 405 | if (c != 1) | ||
| 406 | goto fail; | ||
| 407 | /* first character after : must be valid */ | ||
| 408 | if (!str[1]) | ||
| 409 | goto fail; | ||
| 410 | } else if (c) | ||
| 411 | /* fail - all other cases with embedded \0 */ | ||
| 412 | goto fail; | ||
| 413 | } | ||
| 414 | if (!unpack_nameX(e, AA_ARRAYEND, NULL)) | ||
| 415 | goto fail; | ||
| 416 | if (!unpack_nameX(e, AA_STRUCTEND, NULL)) | ||
| 417 | goto fail; | ||
| 418 | } | ||
| 419 | return 1; | ||
| 420 | |||
| 421 | fail: | ||
| 422 | aa_free_domain_entries(&profile->file.trans); | ||
| 423 | e->pos = pos; | ||
| 424 | return 0; | ||
| 425 | } | ||
| 426 | |||
| 427 | static bool unpack_rlimits(struct aa_ext *e, struct aa_profile *profile) | ||
| 428 | { | ||
| 429 | void *pos = e->pos; | ||
| 430 | |||
| 431 | /* rlimits are optional */ | ||
| 432 | if (unpack_nameX(e, AA_STRUCT, "rlimits")) { | ||
| 433 | int i, size; | ||
| 434 | u32 tmp = 0; | ||
| 435 | if (!unpack_u32(e, &tmp, NULL)) | ||
| 436 | goto fail; | ||
| 437 | profile->rlimits.mask = tmp; | ||
| 438 | |||
| 439 | size = unpack_array(e, NULL); | ||
| 440 | if (size > RLIM_NLIMITS) | ||
| 441 | goto fail; | ||
| 442 | for (i = 0; i < size; i++) { | ||
| 443 | u64 tmp = 0; | ||
| 444 | int a = aa_map_resource(i); | ||
| 445 | if (!unpack_u64(e, &tmp, NULL)) | ||
| 446 | goto fail; | ||
| 447 | profile->rlimits.limits[a].rlim_max = tmp; | ||
| 448 | } | ||
| 449 | if (!unpack_nameX(e, AA_ARRAYEND, NULL)) | ||
| 450 | goto fail; | ||
| 451 | if (!unpack_nameX(e, AA_STRUCTEND, NULL)) | ||
| 452 | goto fail; | ||
| 453 | } | ||
| 454 | return 1; | ||
| 455 | |||
| 456 | fail: | ||
| 457 | e->pos = pos; | ||
| 458 | return 0; | ||
| 459 | } | ||
| 460 | |||
| 461 | /** | ||
| 462 | * unpack_profile - unpack a serialized profile | ||
| 463 | * @e: serialized data extent information (NOT NULL) | ||
| 464 | * | ||
| 465 | * NOTE: unpack profile sets audit struct if there is a failure | ||
| 466 | */ | ||
| 467 | static struct aa_profile *unpack_profile(struct aa_ext *e) | ||
| 468 | { | ||
| 469 | struct aa_profile *profile = NULL; | ||
| 470 | const char *name = NULL; | ||
| 471 | int error = -EPROTO; | ||
| 472 | kernel_cap_t tmpcap; | ||
| 473 | u32 tmp; | ||
| 474 | |||
| 475 | /* check that we have the right struct being passed */ | ||
| 476 | if (!unpack_nameX(e, AA_STRUCT, "profile")) | ||
| 477 | goto fail; | ||
| 478 | if (!unpack_str(e, &name, NULL)) | ||
| 479 | goto fail; | ||
| 480 | |||
| 481 | profile = aa_alloc_profile(name); | ||
| 482 | if (!profile) | ||
| 483 | return ERR_PTR(-ENOMEM); | ||
| 484 | |||
| 485 | /* profile renaming is optional */ | ||
| 486 | (void) unpack_str(e, &profile->rename, "rename"); | ||
| 487 | |||
| 488 | /* xmatch is optional and may be NULL */ | ||
| 489 | profile->xmatch = unpack_dfa(e); | ||
| 490 | if (IS_ERR(profile->xmatch)) { | ||
| 491 | error = PTR_ERR(profile->xmatch); | ||
| 492 | profile->xmatch = NULL; | ||
| 493 | goto fail; | ||
| 494 | } | ||
| 495 | /* xmatch_len is not optional if xmatch is set */ | ||
| 496 | if (profile->xmatch) { | ||
| 497 | if (!unpack_u32(e, &tmp, NULL)) | ||
| 498 | goto fail; | ||
| 499 | profile->xmatch_len = tmp; | ||
| 500 | } | ||
| 501 | |||
| 502 | /* per profile debug flags (complain, audit) */ | ||
| 503 | if (!unpack_nameX(e, AA_STRUCT, "flags")) | ||
| 504 | goto fail; | ||
| 505 | if (!unpack_u32(e, &tmp, NULL)) | ||
| 506 | goto fail; | ||
| 507 | if (tmp) | ||
| 508 | profile->flags |= PFLAG_HAT; | ||
| 509 | if (!unpack_u32(e, &tmp, NULL)) | ||
| 510 | goto fail; | ||
| 511 | if (tmp) | ||
| 512 | profile->mode = APPARMOR_COMPLAIN; | ||
| 513 | if (!unpack_u32(e, &tmp, NULL)) | ||
| 514 | goto fail; | ||
| 515 | if (tmp) | ||
| 516 | profile->audit = AUDIT_ALL; | ||
| 517 | |||
| 518 | if (!unpack_nameX(e, AA_STRUCTEND, NULL)) | ||
| 519 | goto fail; | ||
| 520 | |||
| 521 | /* path_flags is optional */ | ||
| 522 | if (unpack_u32(e, &profile->path_flags, "path_flags")) | ||
| 523 | profile->path_flags |= profile->flags & PFLAG_MEDIATE_DELETED; | ||
| 524 | else | ||
| 525 | /* set a default value if path_flags field is not present */ | ||
| 526 | profile->path_flags = PFLAG_MEDIATE_DELETED; | ||
| 527 | |||
| 528 | if (!unpack_u32(e, &(profile->caps.allow.cap[0]), NULL)) | ||
| 529 | goto fail; | ||
| 530 | if (!unpack_u32(e, &(profile->caps.audit.cap[0]), NULL)) | ||
| 531 | goto fail; | ||
| 532 | if (!unpack_u32(e, &(profile->caps.quiet.cap[0]), NULL)) | ||
| 533 | goto fail; | ||
| 534 | if (!unpack_u32(e, &tmpcap.cap[0], NULL)) | ||
| 535 | goto fail; | ||
| 536 | |||
| 537 | if (unpack_nameX(e, AA_STRUCT, "caps64")) { | ||
| 538 | /* optional upper half of 64 bit caps */ | ||
| 539 | if (!unpack_u32(e, &(profile->caps.allow.cap[1]), NULL)) | ||
| 540 | goto fail; | ||
| 541 | if (!unpack_u32(e, &(profile->caps.audit.cap[1]), NULL)) | ||
| 542 | goto fail; | ||
| 543 | if (!unpack_u32(e, &(profile->caps.quiet.cap[1]), NULL)) | ||
| 544 | goto fail; | ||
| 545 | if (!unpack_u32(e, &(tmpcap.cap[1]), NULL)) | ||
| 546 | goto fail; | ||
| 547 | if (!unpack_nameX(e, AA_STRUCTEND, NULL)) | ||
| 548 | goto fail; | ||
| 549 | } | ||
| 550 | |||
| 551 | if (unpack_nameX(e, AA_STRUCT, "capsx")) { | ||
| 552 | /* optional extended caps mediation mask */ | ||
| 553 | if (!unpack_u32(e, &(profile->caps.extended.cap[0]), NULL)) | ||
| 554 | goto fail; | ||
| 555 | if (!unpack_u32(e, &(profile->caps.extended.cap[1]), NULL)) | ||
| 556 | goto fail; | ||
| 557 | } | ||
| 558 | |||
| 559 | if (!unpack_rlimits(e, profile)) | ||
| 560 | goto fail; | ||
| 561 | |||
| 562 | /* get file rules */ | ||
| 563 | profile->file.dfa = unpack_dfa(e); | ||
| 564 | if (IS_ERR(profile->file.dfa)) { | ||
| 565 | error = PTR_ERR(profile->file.dfa); | ||
| 566 | profile->file.dfa = NULL; | ||
| 567 | goto fail; | ||
| 568 | } | ||
| 569 | |||
| 570 | if (!unpack_u32(e, &profile->file.start, "dfa_start")) | ||
| 571 | /* default start state */ | ||
| 572 | profile->file.start = DFA_START; | ||
| 573 | |||
| 574 | if (!unpack_trans_table(e, profile)) | ||
| 575 | goto fail; | ||
| 576 | |||
| 577 | if (!unpack_nameX(e, AA_STRUCTEND, NULL)) | ||
| 578 | goto fail; | ||
| 579 | |||
| 580 | return profile; | ||
| 581 | |||
| 582 | fail: | ||
| 583 | if (profile) | ||
| 584 | name = NULL; | ||
| 585 | else if (!name) | ||
| 586 | name = "unknown"; | ||
| 587 | audit_iface(profile, name, "failed to unpack profile", e, error); | ||
| 588 | aa_put_profile(profile); | ||
| 589 | |||
| 590 | return ERR_PTR(error); | ||
| 591 | } | ||
| 592 | |||
| 593 | /** | ||
| 594 | * verify_head - unpack serialized stream header | ||
| 595 | * @e: serialized data read head (NOT NULL) | ||
| 596 | * @ns: Returns - namespace if one is specified else NULL (NOT NULL) | ||
| 597 | * | ||
| 598 | * Returns: error or 0 if header is good | ||
| 599 | */ | ||
| 600 | static int verify_header(struct aa_ext *e, const char **ns) | ||
| 601 | { | ||
| 602 | int error = -EPROTONOSUPPORT; | ||
| 603 | /* get the interface version */ | ||
| 604 | if (!unpack_u32(e, &e->version, "version")) { | ||
| 605 | audit_iface(NULL, NULL, "invalid profile format", e, error); | ||
| 606 | return error; | ||
| 607 | } | ||
| 608 | |||
| 609 | /* check that the interface version is currently supported */ | ||
| 610 | if (e->version != 5) { | ||
| 611 | audit_iface(NULL, NULL, "unsupported interface version", e, | ||
| 612 | error); | ||
| 613 | return error; | ||
| 614 | } | ||
| 615 | |||
| 616 | /* read the namespace if present */ | ||
| 617 | if (!unpack_str(e, ns, "namespace")) | ||
| 618 | *ns = NULL; | ||
| 619 | |||
| 620 | return 0; | ||
| 621 | } | ||
| 622 | |||
| 623 | static bool verify_xindex(int xindex, int table_size) | ||
| 624 | { | ||
| 625 | int index, xtype; | ||
| 626 | xtype = xindex & AA_X_TYPE_MASK; | ||
| 627 | index = xindex & AA_X_INDEX_MASK; | ||
| 628 | if (xtype == AA_X_TABLE && index > table_size) | ||
| 629 | return 0; | ||
| 630 | return 1; | ||
| 631 | } | ||
| 632 | |||
| 633 | /* verify dfa xindexes are in range of transition tables */ | ||
| 634 | static bool verify_dfa_xindex(struct aa_dfa *dfa, int table_size) | ||
| 635 | { | ||
| 636 | int i; | ||
| 637 | for (i = 0; i < dfa->tables[YYTD_ID_ACCEPT]->td_lolen; i++) { | ||
| 638 | if (!verify_xindex(dfa_user_xindex(dfa, i), table_size)) | ||
| 639 | return 0; | ||
| 640 | if (!verify_xindex(dfa_other_xindex(dfa, i), table_size)) | ||
| 641 | return 0; | ||
| 642 | } | ||
| 643 | return 1; | ||
| 644 | } | ||
| 645 | |||
| 646 | /** | ||
| 647 | * verify_profile - Do post unpack analysis to verify profile consistency | ||
| 648 | * @profile: profile to verify (NOT NULL) | ||
| 649 | * | ||
| 650 | * Returns: 0 if passes verification else error | ||
| 651 | */ | ||
| 652 | static int verify_profile(struct aa_profile *profile) | ||
| 653 | { | ||
| 654 | if (aa_g_paranoid_load) { | ||
| 655 | if (profile->file.dfa && | ||
| 656 | !verify_dfa_xindex(profile->file.dfa, | ||
| 657 | profile->file.trans.size)) { | ||
| 658 | audit_iface(profile, NULL, "Invalid named transition", | ||
| 659 | NULL, -EPROTO); | ||
| 660 | return -EPROTO; | ||
| 661 | } | ||
| 662 | } | ||
| 663 | |||
| 664 | return 0; | ||
| 665 | } | ||
| 666 | |||
| 667 | /** | ||
| 668 | * aa_unpack - unpack packed binary profile data loaded from user space | ||
| 669 | * @udata: user data copied to kmem (NOT NULL) | ||
| 670 | * @size: the size of the user data | ||
| 671 | * @ns: Returns namespace profile is in if specified else NULL (NOT NULL) | ||
| 672 | * | ||
| 673 | * Unpack user data and return refcounted allocated profile or ERR_PTR | ||
| 674 | * | ||
| 675 | * Returns: profile else error pointer if fails to unpack | ||
| 676 | */ | ||
| 677 | struct aa_profile *aa_unpack(void *udata, size_t size, const char **ns) | ||
| 678 | { | ||
| 679 | struct aa_profile *profile = NULL; | ||
| 680 | int error; | ||
| 681 | struct aa_ext e = { | ||
| 682 | .start = udata, | ||
| 683 | .end = udata + size, | ||
| 684 | .pos = udata, | ||
| 685 | }; | ||
| 686 | |||
| 687 | error = verify_header(&e, ns); | ||
| 688 | if (error) | ||
| 689 | return ERR_PTR(error); | ||
| 690 | |||
| 691 | profile = unpack_profile(&e); | ||
| 692 | if (IS_ERR(profile)) | ||
| 693 | return profile; | ||
| 694 | |||
| 695 | error = verify_profile(profile); | ||
| 696 | if (error) { | ||
| 697 | aa_put_profile(profile); | ||
| 698 | profile = ERR_PTR(error); | ||
| 699 | } | ||
| 700 | |||
| 701 | /* return refcount */ | ||
| 702 | return profile; | ||
| 703 | } | ||
diff --git a/security/apparmor/procattr.c b/security/apparmor/procattr.c new file mode 100644 index 000000000000..04a2cf8d1b65 --- /dev/null +++ b/security/apparmor/procattr.c | |||
| @@ -0,0 +1,170 @@ | |||
| 1 | /* | ||
| 2 | * AppArmor security module | ||
| 3 | * | ||
| 4 | * This file contains AppArmor /proc/<pid>/attr/ interface functions | ||
| 5 | * | ||
| 6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
| 7 | * Copyright 2009-2010 Canonical Ltd. | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or | ||
| 10 | * modify it under the terms of the GNU General Public License as | ||
| 11 | * published by the Free Software Foundation, version 2 of the | ||
| 12 | * License. | ||
| 13 | */ | ||
| 14 | |||
| 15 | #include "include/apparmor.h" | ||
| 16 | #include "include/context.h" | ||
| 17 | #include "include/policy.h" | ||
| 18 | #include "include/domain.h" | ||
| 19 | |||
| 20 | |||
| 21 | /** | ||
| 22 | * aa_getprocattr - Return the profile information for @profile | ||
| 23 | * @profile: the profile to print profile info about (NOT NULL) | ||
| 24 | * @string: Returns - string containing the profile info (NOT NULL) | ||
| 25 | * | ||
| 26 | * Returns: length of @string on success else error on failure | ||
| 27 | * | ||
| 28 | * Requires: profile != NULL | ||
| 29 | * | ||
| 30 | * Creates a string containing the namespace_name://profile_name for | ||
| 31 | * @profile. | ||
| 32 | * | ||
| 33 | * Returns: size of string placed in @string else error code on failure | ||
| 34 | */ | ||
| 35 | int aa_getprocattr(struct aa_profile *profile, char **string) | ||
| 36 | { | ||
| 37 | char *str; | ||
| 38 | int len = 0, mode_len = 0, ns_len = 0, name_len; | ||
| 39 | const char *mode_str = profile_mode_names[profile->mode]; | ||
| 40 | const char *ns_name = NULL; | ||
| 41 | struct aa_namespace *ns = profile->ns; | ||
| 42 | struct aa_namespace *current_ns = __aa_current_profile()->ns; | ||
| 43 | char *s; | ||
| 44 | |||
| 45 | if (!aa_ns_visible(current_ns, ns)) | ||
| 46 | return -EACCES; | ||
| 47 | |||
| 48 | ns_name = aa_ns_name(current_ns, ns); | ||
| 49 | ns_len = strlen(ns_name); | ||
| 50 | |||
| 51 | /* if the visible ns_name is > 0 increase size for : :// seperator */ | ||
| 52 | if (ns_len) | ||
| 53 | ns_len += 4; | ||
| 54 | |||
| 55 | /* unconfined profiles don't have a mode string appended */ | ||
| 56 | if (!unconfined(profile)) | ||
| 57 | mode_len = strlen(mode_str) + 3; /* + 3 for _() */ | ||
| 58 | |||
| 59 | name_len = strlen(profile->base.hname); | ||
| 60 | len = mode_len + ns_len + name_len + 1; /* + 1 for \n */ | ||
| 61 | s = str = kmalloc(len + 1, GFP_KERNEL); /* + 1 \0 */ | ||
| 62 | if (!str) | ||
| 63 | return -ENOMEM; | ||
| 64 | |||
| 65 | if (ns_len) { | ||
| 66 | /* skip over prefix current_ns->base.hname and separating // */ | ||
| 67 | sprintf(s, ":%s://", ns_name); | ||
| 68 | s += ns_len; | ||
| 69 | } | ||
| 70 | if (unconfined(profile)) | ||
| 71 | /* mode string not being appended */ | ||
| 72 | sprintf(s, "%s\n", profile->base.hname); | ||
| 73 | else | ||
| 74 | sprintf(s, "%s (%s)\n", profile->base.hname, mode_str); | ||
| 75 | *string = str; | ||
| 76 | |||
| 77 | /* NOTE: len does not include \0 of string, not saved as part of file */ | ||
| 78 | return len; | ||
| 79 | } | ||
| 80 | |||
| 81 | /** | ||
| 82 | * split_token_from_name - separate a string of form <token>^<name> | ||
| 83 | * @op: operation being checked | ||
| 84 | * @args: string to parse (NOT NULL) | ||
| 85 | * @token: stores returned parsed token value (NOT NULL) | ||
| 86 | * | ||
| 87 | * Returns: start position of name after token else NULL on failure | ||
| 88 | */ | ||
| 89 | static char *split_token_from_name(int op, char *args, u64 * token) | ||
| 90 | { | ||
| 91 | char *name; | ||
| 92 | |||
| 93 | *token = simple_strtoull(args, &name, 16); | ||
| 94 | if ((name == args) || *name != '^') { | ||
| 95 | AA_ERROR("%s: Invalid input '%s'", op_table[op], args); | ||
| 96 | return ERR_PTR(-EINVAL); | ||
| 97 | } | ||
| 98 | |||
| 99 | name++; /* skip ^ */ | ||
| 100 | if (!*name) | ||
| 101 | name = NULL; | ||
| 102 | return name; | ||
| 103 | } | ||
| 104 | |||
| 105 | /** | ||
| 106 | * aa_setprocattr_chagnehat - handle procattr interface to change_hat | ||
| 107 | * @args: args received from writing to /proc/<pid>/attr/current (NOT NULL) | ||
| 108 | * @size: size of the args | ||
| 109 | * @test: true if this is a test of change_hat permissions | ||
| 110 | * | ||
| 111 | * Returns: %0 or error code if change_hat fails | ||
| 112 | */ | ||
| 113 | int aa_setprocattr_changehat(char *args, size_t size, int test) | ||
| 114 | { | ||
| 115 | char *hat; | ||
| 116 | u64 token; | ||
| 117 | const char *hats[16]; /* current hard limit on # of names */ | ||
| 118 | int count = 0; | ||
| 119 | |||
| 120 | hat = split_token_from_name(OP_CHANGE_HAT, args, &token); | ||
| 121 | if (IS_ERR(hat)) | ||
| 122 | return PTR_ERR(hat); | ||
| 123 | |||
| 124 | if (!hat && !token) { | ||
| 125 | AA_ERROR("change_hat: Invalid input, NULL hat and NULL magic"); | ||
| 126 | return -EINVAL; | ||
| 127 | } | ||
| 128 | |||
| 129 | if (hat) { | ||
| 130 | /* set up hat name vector, args guaranteed null terminated | ||
| 131 | * at args[size] by setprocattr. | ||
| 132 | * | ||
| 133 | * If there are multiple hat names in the buffer each is | ||
| 134 | * separated by a \0. Ie. userspace writes them pre tokenized | ||
| 135 | */ | ||
| 136 | char *end = args + size; | ||
| 137 | for (count = 0; (hat < end) && count < 16; ++count) { | ||
| 138 | char *next = hat + strlen(hat) + 1; | ||
| 139 | hats[count] = hat; | ||
| 140 | hat = next; | ||
| 141 | } | ||
| 142 | } | ||
| 143 | |||
| 144 | AA_DEBUG("%s: Magic 0x%llx Hat '%s'\n", | ||
| 145 | __func__, token, hat ? hat : NULL); | ||
| 146 | |||
| 147 | return aa_change_hat(hats, count, token, test); | ||
| 148 | } | ||
| 149 | |||
| 150 | /** | ||
| 151 | * aa_setprocattr_changeprofile - handle procattr interface to changeprofile | ||
| 152 | * @fqname: args received from writting to /proc/<pid>/attr/current (NOT NULL) | ||
| 153 | * @onexec: true if change_profile should be delayed until exec | ||
| 154 | * @test: true if this is a test of change_profile permissions | ||
| 155 | * | ||
| 156 | * Returns: %0 or error code if change_profile fails | ||
| 157 | */ | ||
| 158 | int aa_setprocattr_changeprofile(char *fqname, bool onexec, int test) | ||
| 159 | { | ||
| 160 | char *name, *ns_name; | ||
| 161 | |||
| 162 | name = aa_split_fqname(fqname, &ns_name); | ||
| 163 | return aa_change_profile(ns_name, name, onexec, test); | ||
| 164 | } | ||
| 165 | |||
| 166 | int aa_setprocattr_permipc(char *fqname) | ||
| 167 | { | ||
| 168 | /* TODO: add ipc permission querying */ | ||
| 169 | return -ENOTSUPP; | ||
| 170 | } | ||
diff --git a/security/apparmor/resource.c b/security/apparmor/resource.c new file mode 100644 index 000000000000..4a368f1fd36d --- /dev/null +++ b/security/apparmor/resource.c | |||
| @@ -0,0 +1,134 @@ | |||
| 1 | /* | ||
| 2 | * AppArmor security module | ||
| 3 | * | ||
| 4 | * This file contains AppArmor resource mediation and attachment | ||
| 5 | * | ||
| 6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
| 7 | * Copyright 2009-2010 Canonical Ltd. | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or | ||
| 10 | * modify it under the terms of the GNU General Public License as | ||
| 11 | * published by the Free Software Foundation, version 2 of the | ||
| 12 | * License. | ||
| 13 | */ | ||
| 14 | |||
| 15 | #include <linux/audit.h> | ||
| 16 | |||
| 17 | #include "include/audit.h" | ||
| 18 | #include "include/resource.h" | ||
| 19 | #include "include/policy.h" | ||
| 20 | |||
| 21 | /* | ||
| 22 | * Table of rlimit names: we generate it from resource.h. | ||
| 23 | */ | ||
| 24 | #include "rlim_names.h" | ||
| 25 | |||
| 26 | /* audit callback for resource specific fields */ | ||
| 27 | static void audit_cb(struct audit_buffer *ab, void *va) | ||
| 28 | { | ||
| 29 | struct common_audit_data *sa = va; | ||
| 30 | |||
| 31 | audit_log_format(ab, " rlimit=%s value=%lu", | ||
| 32 | rlim_names[sa->aad.rlim.rlim], sa->aad.rlim.max); | ||
| 33 | } | ||
| 34 | |||
| 35 | /** | ||
| 36 | * audit_resource - audit setting resource limit | ||
| 37 | * @profile: profile being enforced (NOT NULL) | ||
| 38 | * @resoure: rlimit being auditing | ||
| 39 | * @value: value being set | ||
| 40 | * @error: error value | ||
| 41 | * | ||
| 42 | * Returns: 0 or sa->error else other error code on failure | ||
| 43 | */ | ||
| 44 | static int audit_resource(struct aa_profile *profile, unsigned int resource, | ||
| 45 | unsigned long value, int error) | ||
| 46 | { | ||
| 47 | struct common_audit_data sa; | ||
| 48 | |||
| 49 | COMMON_AUDIT_DATA_INIT(&sa, NONE); | ||
| 50 | sa.aad.op = OP_SETRLIMIT, | ||
| 51 | sa.aad.rlim.rlim = resource; | ||
| 52 | sa.aad.rlim.max = value; | ||
| 53 | sa.aad.error = error; | ||
| 54 | return aa_audit(AUDIT_APPARMOR_AUTO, profile, GFP_KERNEL, &sa, | ||
| 55 | audit_cb); | ||
| 56 | } | ||
| 57 | |||
| 58 | /** | ||
| 59 | * aa_map_resouce - map compiled policy resource to internal # | ||
| 60 | * @resource: flattened policy resource number | ||
| 61 | * | ||
| 62 | * Returns: resource # for the current architecture. | ||
| 63 | * | ||
| 64 | * rlimit resource can vary based on architecture, map the compiled policy | ||
| 65 | * resource # to the internal representation for the architecture. | ||
| 66 | */ | ||
| 67 | int aa_map_resource(int resource) | ||
| 68 | { | ||
| 69 | return rlim_map[resource]; | ||
| 70 | } | ||
| 71 | |||
| 72 | /** | ||
| 73 | * aa_task_setrlimit - test permission to set an rlimit | ||
| 74 | * @profile - profile confining the task (NOT NULL) | ||
| 75 | * @resource - the resource being set | ||
| 76 | * @new_rlim - the new resource limit (NOT NULL) | ||
| 77 | * | ||
| 78 | * Control raising the processes hard limit. | ||
| 79 | * | ||
| 80 | * Returns: 0 or error code if setting resource failed | ||
| 81 | */ | ||
| 82 | int aa_task_setrlimit(struct aa_profile *profile, unsigned int resource, | ||
| 83 | struct rlimit *new_rlim) | ||
| 84 | { | ||
| 85 | int error = 0; | ||
| 86 | |||
| 87 | if (profile->rlimits.mask & (1 << resource) && | ||
| 88 | new_rlim->rlim_max > profile->rlimits.limits[resource].rlim_max) | ||
| 89 | |||
| 90 | error = audit_resource(profile, resource, new_rlim->rlim_max, | ||
| 91 | -EACCES); | ||
| 92 | |||
| 93 | return error; | ||
| 94 | } | ||
| 95 | |||
| 96 | /** | ||
| 97 | * __aa_transition_rlimits - apply new profile rlimits | ||
| 98 | * @old: old profile on task (NOT NULL) | ||
| 99 | * @new: new profile with rlimits to apply (NOT NULL) | ||
| 100 | */ | ||
| 101 | void __aa_transition_rlimits(struct aa_profile *old, struct aa_profile *new) | ||
| 102 | { | ||
| 103 | unsigned int mask = 0; | ||
| 104 | struct rlimit *rlim, *initrlim; | ||
| 105 | int i; | ||
| 106 | |||
| 107 | /* for any rlimits the profile controlled reset the soft limit | ||
| 108 | * to the less of the tasks hard limit and the init tasks soft limit | ||
| 109 | */ | ||
| 110 | if (old->rlimits.mask) { | ||
| 111 | for (i = 0, mask = 1; i < RLIM_NLIMITS; i++, mask <<= 1) { | ||
| 112 | if (old->rlimits.mask & mask) { | ||
| 113 | rlim = current->signal->rlim + i; | ||
| 114 | initrlim = init_task.signal->rlim + i; | ||
| 115 | rlim->rlim_cur = min(rlim->rlim_max, | ||
| 116 | initrlim->rlim_cur); | ||
| 117 | } | ||
| 118 | } | ||
| 119 | } | ||
| 120 | |||
| 121 | /* set any new hard limits as dictated by the new profile */ | ||
| 122 | if (!new->rlimits.mask) | ||
| 123 | return; | ||
| 124 | for (i = 0, mask = 1; i < RLIM_NLIMITS; i++, mask <<= 1) { | ||
| 125 | if (!(new->rlimits.mask & mask)) | ||
| 126 | continue; | ||
| 127 | |||
| 128 | rlim = current->signal->rlim + i; | ||
| 129 | rlim->rlim_max = min(rlim->rlim_max, | ||
| 130 | new->rlimits.limits[i].rlim_max); | ||
| 131 | /* soft limit should not exceed hard limit */ | ||
| 132 | rlim->rlim_cur = min(rlim->rlim_cur, rlim->rlim_max); | ||
| 133 | } | ||
| 134 | } | ||
diff --git a/security/apparmor/sid.c b/security/apparmor/sid.c new file mode 100644 index 000000000000..f0b34f76ebef --- /dev/null +++ b/security/apparmor/sid.c | |||
| @@ -0,0 +1,55 @@ | |||
| 1 | /* | ||
| 2 | * AppArmor security module | ||
| 3 | * | ||
| 4 | * This file contains AppArmor security identifier (sid) manipulation fns | ||
| 5 | * | ||
| 6 | * Copyright 2009-2010 Canonical Ltd. | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or | ||
| 9 | * modify it under the terms of the GNU General Public License as | ||
| 10 | * published by the Free Software Foundation, version 2 of the | ||
| 11 | * License. | ||
| 12 | * | ||
| 13 | * | ||
| 14 | * AppArmor allocates a unique sid for every profile loaded. If a profile | ||
| 15 | * is replaced it receives the sid of the profile it is replacing. | ||
| 16 | * | ||
| 17 | * The sid value of 0 is invalid. | ||
| 18 | */ | ||
| 19 | |||
| 20 | #include <linux/spinlock.h> | ||
| 21 | #include <linux/errno.h> | ||
| 22 | #include <linux/err.h> | ||
| 23 | |||
| 24 | #include "include/sid.h" | ||
| 25 | |||
| 26 | /* global counter from which sids are allocated */ | ||
| 27 | static u32 global_sid; | ||
| 28 | static DEFINE_SPINLOCK(sid_lock); | ||
| 29 | |||
| 30 | /* TODO FIXME: add sid to profile mapping, and sid recycling */ | ||
| 31 | |||
| 32 | /** | ||
| 33 | * aa_alloc_sid - allocate a new sid for a profile | ||
| 34 | */ | ||
| 35 | u32 aa_alloc_sid(void) | ||
| 36 | { | ||
| 37 | u32 sid; | ||
| 38 | |||
| 39 | /* | ||
| 40 | * TODO FIXME: sid recycling - part of profile mapping table | ||
| 41 | */ | ||
| 42 | spin_lock(&sid_lock); | ||
| 43 | sid = (++global_sid); | ||
| 44 | spin_unlock(&sid_lock); | ||
| 45 | return sid; | ||
| 46 | } | ||
| 47 | |||
| 48 | /** | ||
| 49 | * aa_free_sid - free a sid | ||
| 50 | * @sid: sid to free | ||
| 51 | */ | ||
| 52 | void aa_free_sid(u32 sid) | ||
| 53 | { | ||
| 54 | ; /* NOP ATM */ | ||
| 55 | } | ||
diff --git a/security/capability.c b/security/capability.c index 8168e3ecd5bf..a0bbf30fb6dc 100644 --- a/security/capability.c +++ b/security/capability.c | |||
| @@ -27,7 +27,7 @@ static int cap_quota_on(struct dentry *dentry) | |||
| 27 | return 0; | 27 | return 0; |
| 28 | } | 28 | } |
| 29 | 29 | ||
| 30 | static int cap_bprm_check_security (struct linux_binprm *bprm) | 30 | static int cap_bprm_check_security(struct linux_binprm *bprm) |
| 31 | { | 31 | { |
| 32 | return 0; | 32 | return 0; |
| 33 | } | 33 | } |
| @@ -268,8 +268,7 @@ static int cap_path_rename(struct path *old_path, struct dentry *old_dentry, | |||
| 268 | return 0; | 268 | return 0; |
| 269 | } | 269 | } |
| 270 | 270 | ||
| 271 | static int cap_path_truncate(struct path *path, loff_t length, | 271 | static int cap_path_truncate(struct path *path) |
| 272 | unsigned int time_attrs) | ||
| 273 | { | 272 | { |
| 274 | return 0; | 273 | return 0; |
| 275 | } | 274 | } |
diff --git a/security/inode.c b/security/inode.c index 1c812e874504..8c777f022ad1 100644 --- a/security/inode.c +++ b/security/inode.c | |||
| @@ -86,7 +86,7 @@ static int mknod(struct inode *dir, struct dentry *dentry, | |||
| 86 | int mode, dev_t dev) | 86 | int mode, dev_t dev) |
| 87 | { | 87 | { |
| 88 | struct inode *inode; | 88 | struct inode *inode; |
| 89 | int error = -EPERM; | 89 | int error = -ENOMEM; |
| 90 | 90 | ||
| 91 | if (dentry->d_inode) | 91 | if (dentry->d_inode) |
| 92 | return -EEXIST; | 92 | return -EEXIST; |
| @@ -166,6 +166,8 @@ static int create_by_name(const char *name, mode_t mode, | |||
| 166 | error = mkdir(parent->d_inode, *dentry, mode); | 166 | error = mkdir(parent->d_inode, *dentry, mode); |
| 167 | else | 167 | else |
| 168 | error = create(parent->d_inode, *dentry, mode); | 168 | error = create(parent->d_inode, *dentry, mode); |
| 169 | if (error) | ||
| 170 | dput(*dentry); | ||
| 169 | } else | 171 | } else |
| 170 | error = PTR_ERR(*dentry); | 172 | error = PTR_ERR(*dentry); |
| 171 | mutex_unlock(&parent->d_inode->i_mutex); | 173 | mutex_unlock(&parent->d_inode->i_mutex); |
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index 8fe736aabe71..ef21b96a0b42 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c | |||
| @@ -45,7 +45,8 @@ static ssize_t ima_show_htable_violations(struct file *filp, | |||
| 45 | } | 45 | } |
| 46 | 46 | ||
| 47 | static const struct file_operations ima_htable_violations_ops = { | 47 | static const struct file_operations ima_htable_violations_ops = { |
| 48 | .read = ima_show_htable_violations | 48 | .read = ima_show_htable_violations, |
| 49 | .llseek = generic_file_llseek, | ||
| 49 | }; | 50 | }; |
| 50 | 51 | ||
| 51 | static ssize_t ima_show_measurements_count(struct file *filp, | 52 | static ssize_t ima_show_measurements_count(struct file *filp, |
| @@ -57,7 +58,8 @@ static ssize_t ima_show_measurements_count(struct file *filp, | |||
| 57 | } | 58 | } |
| 58 | 59 | ||
| 59 | static const struct file_operations ima_measurements_count_ops = { | 60 | static const struct file_operations ima_measurements_count_ops = { |
| 60 | .read = ima_show_measurements_count | 61 | .read = ima_show_measurements_count, |
| 62 | .llseek = generic_file_llseek, | ||
| 61 | }; | 63 | }; |
| 62 | 64 | ||
| 63 | /* returns pointer to hlist_node */ | 65 | /* returns pointer to hlist_node */ |
| @@ -319,7 +321,8 @@ static int ima_release_policy(struct inode *inode, struct file *file) | |||
| 319 | static const struct file_operations ima_measure_policy_ops = { | 321 | static const struct file_operations ima_measure_policy_ops = { |
| 320 | .open = ima_open_policy, | 322 | .open = ima_open_policy, |
| 321 | .write = ima_write_policy, | 323 | .write = ima_write_policy, |
| 322 | .release = ima_release_policy | 324 | .release = ima_release_policy, |
| 325 | .llseek = generic_file_llseek, | ||
| 323 | }; | 326 | }; |
| 324 | 327 | ||
| 325 | int __init ima_fs_init(void) | 328 | int __init ima_fs_init(void) |
diff --git a/security/keys/internal.h b/security/keys/internal.h index 5d4402a1161a..addb67b169f4 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h | |||
| @@ -114,6 +114,10 @@ extern key_ref_t keyring_search_aux(key_ref_t keyring_ref, | |||
| 114 | const void *description, | 114 | const void *description, |
| 115 | key_match_func_t match); | 115 | key_match_func_t match); |
| 116 | 116 | ||
| 117 | extern key_ref_t search_my_process_keyrings(struct key_type *type, | ||
| 118 | const void *description, | ||
| 119 | key_match_func_t match, | ||
| 120 | const struct cred *cred); | ||
| 117 | extern key_ref_t search_process_keyrings(struct key_type *type, | 121 | extern key_ref_t search_process_keyrings(struct key_type *type, |
| 118 | const void *description, | 122 | const void *description, |
| 119 | key_match_func_t match, | 123 | key_match_func_t match, |
| @@ -124,6 +128,7 @@ extern struct key *find_keyring_by_name(const char *name, bool skip_perm_check); | |||
| 124 | extern int install_user_keyrings(void); | 128 | extern int install_user_keyrings(void); |
| 125 | extern int install_thread_keyring_to_cred(struct cred *); | 129 | extern int install_thread_keyring_to_cred(struct cred *); |
| 126 | extern int install_process_keyring_to_cred(struct cred *); | 130 | extern int install_process_keyring_to_cred(struct cred *); |
| 131 | extern int install_session_keyring_to_cred(struct cred *, struct key *); | ||
| 127 | 132 | ||
| 128 | extern struct key *request_key_and_link(struct key_type *type, | 133 | extern struct key *request_key_and_link(struct key_type *type, |
| 129 | const char *description, | 134 | const char *description, |
| @@ -133,6 +138,7 @@ extern struct key *request_key_and_link(struct key_type *type, | |||
| 133 | struct key *dest_keyring, | 138 | struct key *dest_keyring, |
| 134 | unsigned long flags); | 139 | unsigned long flags); |
| 135 | 140 | ||
| 141 | extern int lookup_user_key_possessed(const struct key *key, const void *target); | ||
| 136 | extern key_ref_t lookup_user_key(key_serial_t id, unsigned long flags, | 142 | extern key_ref_t lookup_user_key(key_serial_t id, unsigned long flags, |
| 137 | key_perm_t perm); | 143 | key_perm_t perm); |
| 138 | #define KEY_LOOKUP_CREATE 0x01 | 144 | #define KEY_LOOKUP_CREATE 0x01 |
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index 8f4dce1987c4..b2b0998d6abd 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c | |||
| @@ -33,7 +33,7 @@ static int key_get_type_from_user(char *type, | |||
| 33 | ret = strncpy_from_user(type, _type, len); | 33 | ret = strncpy_from_user(type, _type, len); |
| 34 | 34 | ||
| 35 | if (ret < 0) | 35 | if (ret < 0) |
| 36 | return -EFAULT; | 36 | return ret; |
| 37 | 37 | ||
| 38 | if (ret == 0 || ret >= len) | 38 | if (ret == 0 || ret >= len) |
| 39 | return -EINVAL; | 39 | return -EINVAL; |
| @@ -505,13 +505,11 @@ okay: | |||
| 505 | 505 | ||
| 506 | ret = snprintf(tmpbuf, PAGE_SIZE - 1, | 506 | ret = snprintf(tmpbuf, PAGE_SIZE - 1, |
| 507 | "%s;%d;%d;%08x;%s", | 507 | "%s;%d;%d;%08x;%s", |
| 508 | key_ref_to_ptr(key_ref)->type->name, | 508 | key->type->name, |
| 509 | key_ref_to_ptr(key_ref)->uid, | 509 | key->uid, |
| 510 | key_ref_to_ptr(key_ref)->gid, | 510 | key->gid, |
| 511 | key_ref_to_ptr(key_ref)->perm, | 511 | key->perm, |
| 512 | key_ref_to_ptr(key_ref)->description ? | 512 | key->description ?: ""); |
| 513 | key_ref_to_ptr(key_ref)->description : "" | ||
| 514 | ); | ||
| 515 | 513 | ||
| 516 | /* include a NUL char at the end of the data */ | 514 | /* include a NUL char at the end of the data */ |
| 517 | if (ret > PAGE_SIZE - 1) | 515 | if (ret > PAGE_SIZE - 1) |
| @@ -1080,7 +1078,7 @@ set: | |||
| 1080 | return old_setting; | 1078 | return old_setting; |
| 1081 | error: | 1079 | error: |
| 1082 | abort_creds(new); | 1080 | abort_creds(new); |
| 1083 | return -EINVAL; | 1081 | return ret; |
| 1084 | 1082 | ||
| 1085 | } /* end keyctl_set_reqkey_keyring() */ | 1083 | } /* end keyctl_set_reqkey_keyring() */ |
| 1086 | 1084 | ||
| @@ -1091,7 +1089,7 @@ error: | |||
| 1091 | long keyctl_set_timeout(key_serial_t id, unsigned timeout) | 1089 | long keyctl_set_timeout(key_serial_t id, unsigned timeout) |
| 1092 | { | 1090 | { |
| 1093 | struct timespec now; | 1091 | struct timespec now; |
| 1094 | struct key *key; | 1092 | struct key *key, *instkey; |
| 1095 | key_ref_t key_ref; | 1093 | key_ref_t key_ref; |
| 1096 | time_t expiry; | 1094 | time_t expiry; |
| 1097 | long ret; | 1095 | long ret; |
| @@ -1099,10 +1097,25 @@ long keyctl_set_timeout(key_serial_t id, unsigned timeout) | |||
| 1099 | key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE | KEY_LOOKUP_PARTIAL, | 1097 | key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE | KEY_LOOKUP_PARTIAL, |
| 1100 | KEY_SETATTR); | 1098 | KEY_SETATTR); |
| 1101 | if (IS_ERR(key_ref)) { | 1099 | if (IS_ERR(key_ref)) { |
| 1100 | /* setting the timeout on a key under construction is permitted | ||
| 1101 | * if we have the authorisation token handy */ | ||
| 1102 | if (PTR_ERR(key_ref) == -EACCES) { | ||
| 1103 | instkey = key_get_instantiation_authkey(id); | ||
| 1104 | if (!IS_ERR(instkey)) { | ||
| 1105 | key_put(instkey); | ||
| 1106 | key_ref = lookup_user_key(id, | ||
| 1107 | KEY_LOOKUP_PARTIAL, | ||
| 1108 | 0); | ||
| 1109 | if (!IS_ERR(key_ref)) | ||
| 1110 | goto okay; | ||
| 1111 | } | ||
| 1112 | } | ||
| 1113 | |||
| 1102 | ret = PTR_ERR(key_ref); | 1114 | ret = PTR_ERR(key_ref); |
| 1103 | goto error; | 1115 | goto error; |
| 1104 | } | 1116 | } |
| 1105 | 1117 | ||
| 1118 | okay: | ||
| 1106 | key = key_ref_to_ptr(key_ref); | 1119 | key = key_ref_to_ptr(key_ref); |
| 1107 | 1120 | ||
| 1108 | /* make the changes with the locks held to prevent races */ | 1121 | /* make the changes with the locks held to prevent races */ |
| @@ -1269,7 +1282,7 @@ long keyctl_session_to_parent(void) | |||
| 1269 | goto not_permitted; | 1282 | goto not_permitted; |
| 1270 | 1283 | ||
| 1271 | /* the parent must be single threaded */ | 1284 | /* the parent must be single threaded */ |
| 1272 | if (atomic_read(&parent->signal->count) != 1) | 1285 | if (!thread_group_empty(parent)) |
| 1273 | goto not_permitted; | 1286 | goto not_permitted; |
| 1274 | 1287 | ||
| 1275 | /* the parent and the child must have different session keyrings or | 1288 | /* the parent and the child must have different session keyrings or |
diff --git a/security/keys/proc.c b/security/keys/proc.c index 068b66ea2f1b..70373966816e 100644 --- a/security/keys/proc.c +++ b/security/keys/proc.c | |||
| @@ -184,20 +184,36 @@ static void proc_keys_stop(struct seq_file *p, void *v) | |||
| 184 | 184 | ||
| 185 | static int proc_keys_show(struct seq_file *m, void *v) | 185 | static int proc_keys_show(struct seq_file *m, void *v) |
| 186 | { | 186 | { |
| 187 | const struct cred *cred = current_cred(); | ||
| 187 | struct rb_node *_p = v; | 188 | struct rb_node *_p = v; |
| 188 | struct key *key = rb_entry(_p, struct key, serial_node); | 189 | struct key *key = rb_entry(_p, struct key, serial_node); |
| 189 | struct timespec now; | 190 | struct timespec now; |
| 190 | unsigned long timo; | 191 | unsigned long timo; |
| 192 | key_ref_t key_ref, skey_ref; | ||
| 191 | char xbuf[12]; | 193 | char xbuf[12]; |
| 192 | int rc; | 194 | int rc; |
| 193 | 195 | ||
| 196 | key_ref = make_key_ref(key, 0); | ||
| 197 | |||
| 198 | /* determine if the key is possessed by this process (a test we can | ||
| 199 | * skip if the key does not indicate the possessor can view it | ||
| 200 | */ | ||
| 201 | if (key->perm & KEY_POS_VIEW) { | ||
| 202 | skey_ref = search_my_process_keyrings(key->type, key, | ||
| 203 | lookup_user_key_possessed, | ||
| 204 | cred); | ||
| 205 | if (!IS_ERR(skey_ref)) { | ||
| 206 | key_ref_put(skey_ref); | ||
| 207 | key_ref = make_key_ref(key, 1); | ||
| 208 | } | ||
| 209 | } | ||
| 210 | |||
| 194 | /* check whether the current task is allowed to view the key (assuming | 211 | /* check whether the current task is allowed to view the key (assuming |
| 195 | * non-possession) | 212 | * non-possession) |
| 196 | * - the caller holds a spinlock, and thus the RCU read lock, making our | 213 | * - the caller holds a spinlock, and thus the RCU read lock, making our |
| 197 | * access to __current_cred() safe | 214 | * access to __current_cred() safe |
| 198 | */ | 215 | */ |
| 199 | rc = key_task_permission(make_key_ref(key, 0), current_cred(), | 216 | rc = key_task_permission(key_ref, cred, KEY_VIEW); |
| 200 | KEY_VIEW); | ||
| 201 | if (rc < 0) | 217 | if (rc < 0) |
| 202 | return 0; | 218 | return 0; |
| 203 | 219 | ||
diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index 20a38fed61b1..f8e7251ae2c8 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c | |||
| @@ -216,8 +216,7 @@ static int install_process_keyring(void) | |||
| 216 | /* | 216 | /* |
| 217 | * install a session keyring directly to a credentials struct | 217 | * install a session keyring directly to a credentials struct |
| 218 | */ | 218 | */ |
| 219 | static int install_session_keyring_to_cred(struct cred *cred, | 219 | int install_session_keyring_to_cred(struct cred *cred, struct key *keyring) |
| 220 | struct key *keyring) | ||
| 221 | { | 220 | { |
| 222 | unsigned long flags; | 221 | unsigned long flags; |
| 223 | struct key *old; | 222 | struct key *old; |
| @@ -310,22 +309,19 @@ void key_fsgid_changed(struct task_struct *tsk) | |||
| 310 | 309 | ||
| 311 | /*****************************************************************************/ | 310 | /*****************************************************************************/ |
| 312 | /* | 311 | /* |
| 313 | * search the process keyrings for the first matching key | 312 | * search only my process keyrings for the first matching key |
| 314 | * - we use the supplied match function to see if the description (or other | 313 | * - we use the supplied match function to see if the description (or other |
| 315 | * feature of interest) matches | 314 | * feature of interest) matches |
| 316 | * - we return -EAGAIN if we didn't find any matching key | 315 | * - we return -EAGAIN if we didn't find any matching key |
| 317 | * - we return -ENOKEY if we found only negative matching keys | 316 | * - we return -ENOKEY if we found only negative matching keys |
| 318 | */ | 317 | */ |
| 319 | key_ref_t search_process_keyrings(struct key_type *type, | 318 | key_ref_t search_my_process_keyrings(struct key_type *type, |
| 320 | const void *description, | 319 | const void *description, |
| 321 | key_match_func_t match, | 320 | key_match_func_t match, |
| 322 | const struct cred *cred) | 321 | const struct cred *cred) |
| 323 | { | 322 | { |
| 324 | struct request_key_auth *rka; | ||
| 325 | key_ref_t key_ref, ret, err; | 323 | key_ref_t key_ref, ret, err; |
| 326 | 324 | ||
| 327 | might_sleep(); | ||
| 328 | |||
| 329 | /* we want to return -EAGAIN or -ENOKEY if any of the keyrings were | 325 | /* we want to return -EAGAIN or -ENOKEY if any of the keyrings were |
| 330 | * searchable, but we failed to find a key or we found a negative key; | 326 | * searchable, but we failed to find a key or we found a negative key; |
| 331 | * otherwise we want to return a sample error (probably -EACCES) if | 327 | * otherwise we want to return a sample error (probably -EACCES) if |
| @@ -425,6 +421,36 @@ key_ref_t search_process_keyrings(struct key_type *type, | |||
| 425 | } | 421 | } |
| 426 | } | 422 | } |
| 427 | 423 | ||
| 424 | /* no key - decide on the error we're going to go for */ | ||
| 425 | key_ref = ret ? ret : err; | ||
| 426 | |||
| 427 | found: | ||
| 428 | return key_ref; | ||
| 429 | } | ||
| 430 | |||
| 431 | /*****************************************************************************/ | ||
| 432 | /* | ||
| 433 | * search the process keyrings for the first matching key | ||
| 434 | * - we use the supplied match function to see if the description (or other | ||
| 435 | * feature of interest) matches | ||
| 436 | * - we return -EAGAIN if we didn't find any matching key | ||
| 437 | * - we return -ENOKEY if we found only negative matching keys | ||
| 438 | */ | ||
| 439 | key_ref_t search_process_keyrings(struct key_type *type, | ||
| 440 | const void *description, | ||
| 441 | key_match_func_t match, | ||
| 442 | const struct cred *cred) | ||
| 443 | { | ||
| 444 | struct request_key_auth *rka; | ||
| 445 | key_ref_t key_ref, ret = ERR_PTR(-EACCES), err; | ||
| 446 | |||
| 447 | might_sleep(); | ||
| 448 | |||
| 449 | key_ref = search_my_process_keyrings(type, description, match, cred); | ||
| 450 | if (!IS_ERR(key_ref)) | ||
| 451 | goto found; | ||
| 452 | err = key_ref; | ||
| 453 | |||
| 428 | /* if this process has an instantiation authorisation key, then we also | 454 | /* if this process has an instantiation authorisation key, then we also |
| 429 | * search the keyrings of the process mentioned there | 455 | * search the keyrings of the process mentioned there |
| 430 | * - we don't permit access to request_key auth keys via this method | 456 | * - we don't permit access to request_key auth keys via this method |
| @@ -447,24 +473,19 @@ key_ref_t search_process_keyrings(struct key_type *type, | |||
| 447 | if (!IS_ERR(key_ref)) | 473 | if (!IS_ERR(key_ref)) |
| 448 | goto found; | 474 | goto found; |
| 449 | 475 | ||
| 450 | switch (PTR_ERR(key_ref)) { | 476 | ret = key_ref; |
| 451 | case -EAGAIN: /* no key */ | ||
| 452 | if (ret) | ||
| 453 | break; | ||
| 454 | case -ENOKEY: /* negative key */ | ||
| 455 | ret = key_ref; | ||
| 456 | break; | ||
| 457 | default: | ||
| 458 | err = key_ref; | ||
| 459 | break; | ||
| 460 | } | ||
| 461 | } else { | 477 | } else { |
| 462 | up_read(&cred->request_key_auth->sem); | 478 | up_read(&cred->request_key_auth->sem); |
| 463 | } | 479 | } |
| 464 | } | 480 | } |
| 465 | 481 | ||
| 466 | /* no key - decide on the error we're going to go for */ | 482 | /* no key - decide on the error we're going to go for */ |
| 467 | key_ref = ret ? ret : err; | 483 | if (err == ERR_PTR(-ENOKEY) || ret == ERR_PTR(-ENOKEY)) |
| 484 | key_ref = ERR_PTR(-ENOKEY); | ||
| 485 | else if (err == ERR_PTR(-EACCES)) | ||
| 486 | key_ref = ret; | ||
| 487 | else | ||
| 488 | key_ref = err; | ||
| 468 | 489 | ||
| 469 | found: | 490 | found: |
| 470 | return key_ref; | 491 | return key_ref; |
| @@ -475,7 +496,7 @@ found: | |||
| 475 | /* | 496 | /* |
| 476 | * see if the key we're looking at is the target key | 497 | * see if the key we're looking at is the target key |
| 477 | */ | 498 | */ |
| 478 | static int lookup_user_key_possessed(const struct key *key, const void *target) | 499 | int lookup_user_key_possessed(const struct key *key, const void *target) |
| 479 | { | 500 | { |
| 480 | return key == target; | 501 | return key == target; |
| 481 | 502 | ||
diff --git a/security/keys/request_key.c b/security/keys/request_key.c index f656e9c069e3..0d26f689bd77 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c | |||
| @@ -58,6 +58,38 @@ void complete_request_key(struct key_construction *cons, int error) | |||
| 58 | } | 58 | } |
| 59 | EXPORT_SYMBOL(complete_request_key); | 59 | EXPORT_SYMBOL(complete_request_key); |
| 60 | 60 | ||
| 61 | static int umh_keys_init(struct subprocess_info *info) | ||
| 62 | { | ||
| 63 | struct cred *cred = (struct cred*)current_cred(); | ||
| 64 | struct key *keyring = info->data; | ||
| 65 | /* | ||
| 66 | * This is called in context of freshly forked kthread before | ||
| 67 | * kernel_execve(), we can just change our ->session_keyring. | ||
| 68 | */ | ||
| 69 | return install_session_keyring_to_cred(cred, keyring); | ||
| 70 | } | ||
| 71 | |||
| 72 | static void umh_keys_cleanup(struct subprocess_info *info) | ||
| 73 | { | ||
| 74 | struct key *keyring = info->data; | ||
| 75 | key_put(keyring); | ||
| 76 | } | ||
| 77 | |||
| 78 | static int call_usermodehelper_keys(char *path, char **argv, char **envp, | ||
| 79 | struct key *session_keyring, enum umh_wait wait) | ||
| 80 | { | ||
| 81 | gfp_t gfp_mask = (wait == UMH_NO_WAIT) ? GFP_ATOMIC : GFP_KERNEL; | ||
| 82 | struct subprocess_info *info = | ||
| 83 | call_usermodehelper_setup(path, argv, envp, gfp_mask); | ||
| 84 | |||
| 85 | if (!info) | ||
| 86 | return -ENOMEM; | ||
| 87 | |||
| 88 | call_usermodehelper_setfns(info, umh_keys_init, umh_keys_cleanup, | ||
| 89 | key_get(session_keyring)); | ||
| 90 | return call_usermodehelper_exec(info, wait); | ||
| 91 | } | ||
| 92 | |||
| 61 | /* | 93 | /* |
| 62 | * request userspace finish the construction of a key | 94 | * request userspace finish the construction of a key |
| 63 | * - execute "/sbin/request-key <op> <key> <uid> <gid> <keyring> <keyring> <keyring>" | 95 | * - execute "/sbin/request-key <op> <key> <uid> <gid> <keyring> <keyring> <keyring>" |
| @@ -112,6 +144,7 @@ static int call_sbin_request_key(struct key_construction *cons, | |||
| 112 | prkey = 0; | 144 | prkey = 0; |
| 113 | if (cred->tgcred->process_keyring) | 145 | if (cred->tgcred->process_keyring) |
| 114 | prkey = cred->tgcred->process_keyring->serial; | 146 | prkey = cred->tgcred->process_keyring->serial; |
| 147 | sprintf(keyring_str[1], "%d", prkey); | ||
| 115 | 148 | ||
| 116 | rcu_read_lock(); | 149 | rcu_read_lock(); |
| 117 | session = rcu_dereference(cred->tgcred->session_keyring); | 150 | session = rcu_dereference(cred->tgcred->session_keyring); |
diff --git a/security/security.c b/security/security.c index 351942a4ca0e..e8c87b8601b4 100644 --- a/security/security.c +++ b/security/security.c | |||
| @@ -417,12 +417,11 @@ int security_path_rename(struct path *old_dir, struct dentry *old_dentry, | |||
| 417 | new_dentry); | 417 | new_dentry); |
| 418 | } | 418 | } |
| 419 | 419 | ||
| 420 | int security_path_truncate(struct path *path, loff_t length, | 420 | int security_path_truncate(struct path *path) |
| 421 | unsigned int time_attrs) | ||
| 422 | { | 421 | { |
| 423 | if (unlikely(IS_PRIVATE(path->dentry->d_inode))) | 422 | if (unlikely(IS_PRIVATE(path->dentry->d_inode))) |
| 424 | return 0; | 423 | return 0; |
| 425 | return security_ops->path_truncate(path, length, time_attrs); | 424 | return security_ops->path_truncate(path); |
| 426 | } | 425 | } |
| 427 | 426 | ||
| 428 | int security_path_chmod(struct dentry *dentry, struct vfsmount *mnt, | 427 | int security_path_chmod(struct dentry *dentry, struct vfsmount *mnt, |
diff --git a/security/selinux/avc.c b/security/selinux/avc.c index 7f1a304712a9..9da6420e2056 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c | |||
| @@ -288,7 +288,6 @@ static struct avc_node *avc_alloc_node(void) | |||
| 288 | if (!node) | 288 | if (!node) |
| 289 | goto out; | 289 | goto out; |
| 290 | 290 | ||
| 291 | INIT_RCU_HEAD(&node->rhead); | ||
| 292 | INIT_HLIST_NODE(&node->list); | 291 | INIT_HLIST_NODE(&node->list); |
| 293 | avc_cache_stats_incr(allocations); | 292 | avc_cache_stats_incr(allocations); |
| 294 | 293 | ||
| @@ -489,9 +488,29 @@ void avc_audit(u32 ssid, u32 tsid, | |||
| 489 | struct common_audit_data stack_data; | 488 | struct common_audit_data stack_data; |
| 490 | u32 denied, audited; | 489 | u32 denied, audited; |
| 491 | denied = requested & ~avd->allowed; | 490 | denied = requested & ~avd->allowed; |
| 492 | if (denied) | 491 | if (denied) { |
| 493 | audited = denied & avd->auditdeny; | 492 | audited = denied & avd->auditdeny; |
| 494 | else if (result) | 493 | /* |
| 494 | * a->selinux_audit_data.auditdeny is TRICKY! Setting a bit in | ||
| 495 | * this field means that ANY denials should NOT be audited if | ||
| 496 | * the policy contains an explicit dontaudit rule for that | ||
| 497 | * permission. Take notice that this is unrelated to the | ||
| 498 | * actual permissions that were denied. As an example lets | ||
| 499 | * assume: | ||
| 500 | * | ||
| 501 | * denied == READ | ||
| 502 | * avd.auditdeny & ACCESS == 0 (not set means explicit rule) | ||
| 503 | * selinux_audit_data.auditdeny & ACCESS == 1 | ||
| 504 | * | ||
| 505 | * We will NOT audit the denial even though the denied | ||
| 506 | * permission was READ and the auditdeny checks were for | ||
| 507 | * ACCESS | ||
| 508 | */ | ||
| 509 | if (a && | ||
| 510 | a->selinux_audit_data.auditdeny && | ||
| 511 | !(a->selinux_audit_data.auditdeny & avd->auditdeny)) | ||
| 512 | audited = 0; | ||
| 513 | } else if (result) | ||
| 495 | audited = denied = requested; | 514 | audited = denied = requested; |
| 496 | else | 515 | else |
| 497 | audited = requested & avd->auditallow; | 516 | audited = requested & avd->auditallow; |
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 5c9f25ba1c95..9b40f4c0ac70 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c | |||
| @@ -87,9 +87,6 @@ | |||
| 87 | #include "netlabel.h" | 87 | #include "netlabel.h" |
| 88 | #include "audit.h" | 88 | #include "audit.h" |
| 89 | 89 | ||
| 90 | #define XATTR_SELINUX_SUFFIX "selinux" | ||
| 91 | #define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX | ||
| 92 | |||
| 93 | #define NUM_SEL_MNT_OPTS 5 | 90 | #define NUM_SEL_MNT_OPTS 5 |
| 94 | 91 | ||
| 95 | extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm); | 92 | extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm); |
| @@ -188,7 +185,7 @@ static inline u32 task_sid(const struct task_struct *task) | |||
| 188 | */ | 185 | */ |
| 189 | static inline u32 current_sid(void) | 186 | static inline u32 current_sid(void) |
| 190 | { | 187 | { |
| 191 | const struct task_security_struct *tsec = current_cred()->security; | 188 | const struct task_security_struct *tsec = current_security(); |
| 192 | 189 | ||
| 193 | return tsec->sid; | 190 | return tsec->sid; |
| 194 | } | 191 | } |
| @@ -279,32 +276,6 @@ static void superblock_free_security(struct super_block *sb) | |||
| 279 | kfree(sbsec); | 276 | kfree(sbsec); |
| 280 | } | 277 | } |
| 281 | 278 | ||
| 282 | static int sk_alloc_security(struct sock *sk, int family, gfp_t priority) | ||
| 283 | { | ||
| 284 | struct sk_security_struct *sksec; | ||
| 285 | |||
| 286 | sksec = kzalloc(sizeof(*sksec), priority); | ||
| 287 | if (!sksec) | ||
| 288 | return -ENOMEM; | ||
| 289 | |||
| 290 | sksec->peer_sid = SECINITSID_UNLABELED; | ||
| 291 | sksec->sid = SECINITSID_UNLABELED; | ||
| 292 | sk->sk_security = sksec; | ||
| 293 | |||
| 294 | selinux_netlbl_sk_security_reset(sksec); | ||
| 295 | |||
| 296 | return 0; | ||
| 297 | } | ||
| 298 | |||
| 299 | static void sk_free_security(struct sock *sk) | ||
| 300 | { | ||
| 301 | struct sk_security_struct *sksec = sk->sk_security; | ||
| 302 | |||
| 303 | sk->sk_security = NULL; | ||
| 304 | selinux_netlbl_sk_security_free(sksec); | ||
| 305 | kfree(sksec); | ||
| 306 | } | ||
| 307 | |||
| 308 | /* The security server must be initialized before | 279 | /* The security server must be initialized before |
| 309 | any labeling or access decisions can be provided. */ | 280 | any labeling or access decisions can be provided. */ |
| 310 | extern int ss_initialized; | 281 | extern int ss_initialized; |
| @@ -1584,8 +1555,7 @@ static int may_create(struct inode *dir, | |||
| 1584 | struct dentry *dentry, | 1555 | struct dentry *dentry, |
| 1585 | u16 tclass) | 1556 | u16 tclass) |
| 1586 | { | 1557 | { |
| 1587 | const struct cred *cred = current_cred(); | 1558 | const struct task_security_struct *tsec = current_security(); |
| 1588 | const struct task_security_struct *tsec = cred->security; | ||
| 1589 | struct inode_security_struct *dsec; | 1559 | struct inode_security_struct *dsec; |
| 1590 | struct superblock_security_struct *sbsec; | 1560 | struct superblock_security_struct *sbsec; |
| 1591 | u32 sid, newsid; | 1561 | u32 sid, newsid; |
| @@ -1806,27 +1776,9 @@ static inline u32 open_file_to_av(struct file *file) | |||
| 1806 | { | 1776 | { |
| 1807 | u32 av = file_to_av(file); | 1777 | u32 av = file_to_av(file); |
| 1808 | 1778 | ||
| 1809 | if (selinux_policycap_openperm) { | 1779 | if (selinux_policycap_openperm) |
| 1810 | mode_t mode = file->f_path.dentry->d_inode->i_mode; | 1780 | av |= FILE__OPEN; |
| 1811 | /* | 1781 | |
| 1812 | * lnk files and socks do not really have an 'open' | ||
| 1813 | */ | ||
| 1814 | if (S_ISREG(mode)) | ||
| 1815 | av |= FILE__OPEN; | ||
| 1816 | else if (S_ISCHR(mode)) | ||
| 1817 | av |= CHR_FILE__OPEN; | ||
| 1818 | else if (S_ISBLK(mode)) | ||
| 1819 | av |= BLK_FILE__OPEN; | ||
| 1820 | else if (S_ISFIFO(mode)) | ||
| 1821 | av |= FIFO_FILE__OPEN; | ||
| 1822 | else if (S_ISDIR(mode)) | ||
| 1823 | av |= DIR__OPEN; | ||
| 1824 | else if (S_ISSOCK(mode)) | ||
| 1825 | av |= SOCK_FILE__OPEN; | ||
| 1826 | else | ||
| 1827 | printk(KERN_ERR "SELinux: WARNING: inside %s with " | ||
| 1828 | "unknown mode:%o\n", __func__, mode); | ||
| 1829 | } | ||
| 1830 | return av; | 1782 | return av; |
| 1831 | } | 1783 | } |
| 1832 | 1784 | ||
| @@ -2183,8 +2135,7 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm) | |||
| 2183 | 2135 | ||
| 2184 | static int selinux_bprm_secureexec(struct linux_binprm *bprm) | 2136 | static int selinux_bprm_secureexec(struct linux_binprm *bprm) |
| 2185 | { | 2137 | { |
| 2186 | const struct cred *cred = current_cred(); | 2138 | const struct task_security_struct *tsec = current_security(); |
| 2187 | const struct task_security_struct *tsec = cred->security; | ||
| 2188 | u32 sid, osid; | 2139 | u32 sid, osid; |
| 2189 | int atsecure = 0; | 2140 | int atsecure = 0; |
| 2190 | 2141 | ||
| @@ -2559,8 +2510,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir, | |||
| 2559 | char **name, void **value, | 2510 | char **name, void **value, |
| 2560 | size_t *len) | 2511 | size_t *len) |
| 2561 | { | 2512 | { |
| 2562 | const struct cred *cred = current_cred(); | 2513 | const struct task_security_struct *tsec = current_security(); |
| 2563 | const struct task_security_struct *tsec = cred->security; | ||
| 2564 | struct inode_security_struct *dsec; | 2514 | struct inode_security_struct *dsec; |
| 2565 | struct superblock_security_struct *sbsec; | 2515 | struct superblock_security_struct *sbsec; |
| 2566 | u32 sid, newsid, clen; | 2516 | u32 sid, newsid, clen; |
| @@ -2676,14 +2626,26 @@ static int selinux_inode_follow_link(struct dentry *dentry, struct nameidata *na | |||
| 2676 | static int selinux_inode_permission(struct inode *inode, int mask) | 2626 | static int selinux_inode_permission(struct inode *inode, int mask) |
| 2677 | { | 2627 | { |
| 2678 | const struct cred *cred = current_cred(); | 2628 | const struct cred *cred = current_cred(); |
| 2629 | struct common_audit_data ad; | ||
| 2630 | u32 perms; | ||
| 2631 | bool from_access; | ||
| 2679 | 2632 | ||
| 2680 | if (!mask) { | 2633 | from_access = mask & MAY_ACCESS; |
| 2681 | /* No permission to check. Existence test. */ | 2634 | mask &= (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND); |
| 2635 | |||
| 2636 | /* No permission to check. Existence test. */ | ||
| 2637 | if (!mask) | ||
| 2682 | return 0; | 2638 | return 0; |
| 2683 | } | ||
| 2684 | 2639 | ||
| 2685 | return inode_has_perm(cred, inode, | 2640 | COMMON_AUDIT_DATA_INIT(&ad, FS); |
| 2686 | file_mask_to_av(inode->i_mode, mask), NULL); | 2641 | ad.u.fs.inode = inode; |
| 2642 | |||
| 2643 | if (from_access) | ||
| 2644 | ad.selinux_audit_data.auditdeny |= FILE__AUDIT_ACCESS; | ||
| 2645 | |||
| 2646 | perms = file_mask_to_av(inode->i_mode, mask); | ||
| 2647 | |||
| 2648 | return inode_has_perm(cred, inode, perms, &ad); | ||
| 2687 | } | 2649 | } |
| 2688 | 2650 | ||
| 2689 | static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr) | 2651 | static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr) |
| @@ -3671,71 +3633,54 @@ static int selinux_skb_peerlbl_sid(struct sk_buff *skb, u16 family, u32 *sid) | |||
| 3671 | } | 3633 | } |
| 3672 | 3634 | ||
| 3673 | /* socket security operations */ | 3635 | /* socket security operations */ |
| 3674 | static int socket_has_perm(struct task_struct *task, struct socket *sock, | 3636 | |
| 3675 | u32 perms) | 3637 | static u32 socket_sockcreate_sid(const struct task_security_struct *tsec) |
| 3676 | { | 3638 | { |
| 3677 | struct inode_security_struct *isec; | 3639 | return tsec->sockcreate_sid ? : tsec->sid; |
| 3678 | struct common_audit_data ad; | 3640 | } |
| 3679 | u32 sid; | ||
| 3680 | int err = 0; | ||
| 3681 | 3641 | ||
| 3682 | isec = SOCK_INODE(sock)->i_security; | 3642 | static int sock_has_perm(struct task_struct *task, struct sock *sk, u32 perms) |
| 3643 | { | ||
| 3644 | struct sk_security_struct *sksec = sk->sk_security; | ||
| 3645 | struct common_audit_data ad; | ||
| 3646 | u32 tsid = task_sid(task); | ||
| 3683 | 3647 | ||
| 3684 | if (isec->sid == SECINITSID_KERNEL) | 3648 | if (sksec->sid == SECINITSID_KERNEL) |
| 3685 | goto out; | 3649 | return 0; |
| 3686 | sid = task_sid(task); | ||
| 3687 | 3650 | ||
| 3688 | COMMON_AUDIT_DATA_INIT(&ad, NET); | 3651 | COMMON_AUDIT_DATA_INIT(&ad, NET); |
| 3689 | ad.u.net.sk = sock->sk; | 3652 | ad.u.net.sk = sk; |
| 3690 | err = avc_has_perm(sid, isec->sid, isec->sclass, perms, &ad); | ||
| 3691 | 3653 | ||
| 3692 | out: | 3654 | return avc_has_perm(tsid, sksec->sid, sksec->sclass, perms, &ad); |
| 3693 | return err; | ||
| 3694 | } | 3655 | } |
| 3695 | 3656 | ||
| 3696 | static int selinux_socket_create(int family, int type, | 3657 | static int selinux_socket_create(int family, int type, |
| 3697 | int protocol, int kern) | 3658 | int protocol, int kern) |
| 3698 | { | 3659 | { |
| 3699 | const struct cred *cred = current_cred(); | 3660 | const struct task_security_struct *tsec = current_security(); |
| 3700 | const struct task_security_struct *tsec = cred->security; | 3661 | u32 newsid; |
| 3701 | u32 sid, newsid; | ||
| 3702 | u16 secclass; | 3662 | u16 secclass; |
| 3703 | int err = 0; | ||
| 3704 | 3663 | ||
| 3705 | if (kern) | 3664 | if (kern) |
| 3706 | goto out; | 3665 | return 0; |
| 3707 | |||
| 3708 | sid = tsec->sid; | ||
| 3709 | newsid = tsec->sockcreate_sid ?: sid; | ||
| 3710 | 3666 | ||
| 3667 | newsid = socket_sockcreate_sid(tsec); | ||
| 3711 | secclass = socket_type_to_security_class(family, type, protocol); | 3668 | secclass = socket_type_to_security_class(family, type, protocol); |
| 3712 | err = avc_has_perm(sid, newsid, secclass, SOCKET__CREATE, NULL); | 3669 | return avc_has_perm(tsec->sid, newsid, secclass, SOCKET__CREATE, NULL); |
| 3713 | |||
| 3714 | out: | ||
| 3715 | return err; | ||
| 3716 | } | 3670 | } |
| 3717 | 3671 | ||
| 3718 | static int selinux_socket_post_create(struct socket *sock, int family, | 3672 | static int selinux_socket_post_create(struct socket *sock, int family, |
| 3719 | int type, int protocol, int kern) | 3673 | int type, int protocol, int kern) |
| 3720 | { | 3674 | { |
| 3721 | const struct cred *cred = current_cred(); | 3675 | const struct task_security_struct *tsec = current_security(); |
| 3722 | const struct task_security_struct *tsec = cred->security; | 3676 | struct inode_security_struct *isec = SOCK_INODE(sock)->i_security; |
| 3723 | struct inode_security_struct *isec; | ||
| 3724 | struct sk_security_struct *sksec; | 3677 | struct sk_security_struct *sksec; |
| 3725 | u32 sid, newsid; | ||
| 3726 | int err = 0; | 3678 | int err = 0; |
| 3727 | 3679 | ||
| 3728 | sid = tsec->sid; | ||
| 3729 | newsid = tsec->sockcreate_sid; | ||
| 3730 | |||
| 3731 | isec = SOCK_INODE(sock)->i_security; | ||
| 3732 | |||
| 3733 | if (kern) | 3680 | if (kern) |
| 3734 | isec->sid = SECINITSID_KERNEL; | 3681 | isec->sid = SECINITSID_KERNEL; |
| 3735 | else if (newsid) | ||
| 3736 | isec->sid = newsid; | ||
| 3737 | else | 3682 | else |
| 3738 | isec->sid = sid; | 3683 | isec->sid = socket_sockcreate_sid(tsec); |
| 3739 | 3684 | ||
| 3740 | isec->sclass = socket_type_to_security_class(family, type, protocol); | 3685 | isec->sclass = socket_type_to_security_class(family, type, protocol); |
| 3741 | isec->initialized = 1; | 3686 | isec->initialized = 1; |
| @@ -3756,10 +3701,11 @@ static int selinux_socket_post_create(struct socket *sock, int family, | |||
| 3756 | 3701 | ||
| 3757 | static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen) | 3702 | static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen) |
| 3758 | { | 3703 | { |
| 3704 | struct sock *sk = sock->sk; | ||
| 3759 | u16 family; | 3705 | u16 family; |
| 3760 | int err; | 3706 | int err; |
| 3761 | 3707 | ||
| 3762 | err = socket_has_perm(current, sock, SOCKET__BIND); | 3708 | err = sock_has_perm(current, sk, SOCKET__BIND); |
| 3763 | if (err) | 3709 | if (err) |
| 3764 | goto out; | 3710 | goto out; |
| 3765 | 3711 | ||
| @@ -3768,19 +3714,16 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in | |||
| 3768 | * Multiple address binding for SCTP is not supported yet: we just | 3714 | * Multiple address binding for SCTP is not supported yet: we just |
| 3769 | * check the first address now. | 3715 | * check the first address now. |
| 3770 | */ | 3716 | */ |
| 3771 | family = sock->sk->sk_family; | 3717 | family = sk->sk_family; |
| 3772 | if (family == PF_INET || family == PF_INET6) { | 3718 | if (family == PF_INET || family == PF_INET6) { |
| 3773 | char *addrp; | 3719 | char *addrp; |
| 3774 | struct inode_security_struct *isec; | 3720 | struct sk_security_struct *sksec = sk->sk_security; |
| 3775 | struct common_audit_data ad; | 3721 | struct common_audit_data ad; |
| 3776 | struct sockaddr_in *addr4 = NULL; | 3722 | struct sockaddr_in *addr4 = NULL; |
| 3777 | struct sockaddr_in6 *addr6 = NULL; | 3723 | struct sockaddr_in6 *addr6 = NULL; |
| 3778 | unsigned short snum; | 3724 | unsigned short snum; |
| 3779 | struct sock *sk = sock->sk; | ||
| 3780 | u32 sid, node_perm; | 3725 | u32 sid, node_perm; |
| 3781 | 3726 | ||
| 3782 | isec = SOCK_INODE(sock)->i_security; | ||
| 3783 | |||
| 3784 | if (family == PF_INET) { | 3727 | if (family == PF_INET) { |
| 3785 | addr4 = (struct sockaddr_in *)address; | 3728 | addr4 = (struct sockaddr_in *)address; |
| 3786 | snum = ntohs(addr4->sin_port); | 3729 | snum = ntohs(addr4->sin_port); |
| @@ -3804,15 +3747,15 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in | |||
| 3804 | COMMON_AUDIT_DATA_INIT(&ad, NET); | 3747 | COMMON_AUDIT_DATA_INIT(&ad, NET); |
| 3805 | ad.u.net.sport = htons(snum); | 3748 | ad.u.net.sport = htons(snum); |
| 3806 | ad.u.net.family = family; | 3749 | ad.u.net.family = family; |
| 3807 | err = avc_has_perm(isec->sid, sid, | 3750 | err = avc_has_perm(sksec->sid, sid, |
| 3808 | isec->sclass, | 3751 | sksec->sclass, |
| 3809 | SOCKET__NAME_BIND, &ad); | 3752 | SOCKET__NAME_BIND, &ad); |
| 3810 | if (err) | 3753 | if (err) |
| 3811 | goto out; | 3754 | goto out; |
| 3812 | } | 3755 | } |
| 3813 | } | 3756 | } |
| 3814 | 3757 | ||
| 3815 | switch (isec->sclass) { | 3758 | switch (sksec->sclass) { |
| 3816 | case SECCLASS_TCP_SOCKET: | 3759 | case SECCLASS_TCP_SOCKET: |
| 3817 | node_perm = TCP_SOCKET__NODE_BIND; | 3760 | node_perm = TCP_SOCKET__NODE_BIND; |
| 3818 | break; | 3761 | break; |
| @@ -3843,8 +3786,8 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in | |||
| 3843 | else | 3786 | else |
| 3844 | ipv6_addr_copy(&ad.u.net.v6info.saddr, &addr6->sin6_addr); | 3787 | ipv6_addr_copy(&ad.u.net.v6info.saddr, &addr6->sin6_addr); |
| 3845 | 3788 | ||
| 3846 | err = avc_has_perm(isec->sid, sid, | 3789 | err = avc_has_perm(sksec->sid, sid, |
| 3847 | isec->sclass, node_perm, &ad); | 3790 | sksec->sclass, node_perm, &ad); |
| 3848 | if (err) | 3791 | if (err) |
| 3849 | goto out; | 3792 | goto out; |
| 3850 | } | 3793 | } |
| @@ -3855,19 +3798,18 @@ out: | |||
| 3855 | static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen) | 3798 | static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen) |
| 3856 | { | 3799 | { |
| 3857 | struct sock *sk = sock->sk; | 3800 | struct sock *sk = sock->sk; |
| 3858 | struct inode_security_struct *isec; | 3801 | struct sk_security_struct *sksec = sk->sk_security; |
| 3859 | int err; | 3802 | int err; |
| 3860 | 3803 | ||
| 3861 | err = socket_has_perm(current, sock, SOCKET__CONNECT); | 3804 | err = sock_has_perm(current, sk, SOCKET__CONNECT); |
| 3862 | if (err) | 3805 | if (err) |
| 3863 | return err; | 3806 | return err; |
| 3864 | 3807 | ||
| 3865 | /* | 3808 | /* |
| 3866 | * If a TCP or DCCP socket, check name_connect permission for the port. | 3809 | * If a TCP or DCCP socket, check name_connect permission for the port. |
| 3867 | */ | 3810 | */ |
| 3868 | isec = SOCK_INODE(sock)->i_security; | 3811 | if (sksec->sclass == SECCLASS_TCP_SOCKET || |
| 3869 | if (isec->sclass == SECCLASS_TCP_SOCKET || | 3812 | sksec->sclass == SECCLASS_DCCP_SOCKET) { |
| 3870 | isec->sclass == SECCLASS_DCCP_SOCKET) { | ||
| 3871 | struct common_audit_data ad; | 3813 | struct common_audit_data ad; |
| 3872 | struct sockaddr_in *addr4 = NULL; | 3814 | struct sockaddr_in *addr4 = NULL; |
| 3873 | struct sockaddr_in6 *addr6 = NULL; | 3815 | struct sockaddr_in6 *addr6 = NULL; |
| @@ -3890,13 +3832,13 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, | |||
| 3890 | if (err) | 3832 | if (err) |
| 3891 | goto out; | 3833 | goto out; |
| 3892 | 3834 | ||
| 3893 | perm = (isec->sclass == SECCLASS_TCP_SOCKET) ? | 3835 | perm = (sksec->sclass == SECCLASS_TCP_SOCKET) ? |
| 3894 | TCP_SOCKET__NAME_CONNECT : DCCP_SOCKET__NAME_CONNECT; | 3836 | TCP_SOCKET__NAME_CONNECT : DCCP_SOCKET__NAME_CONNECT; |
| 3895 | 3837 | ||
| 3896 | COMMON_AUDIT_DATA_INIT(&ad, NET); | 3838 | COMMON_AUDIT_DATA_INIT(&ad, NET); |
| 3897 | ad.u.net.dport = htons(snum); | 3839 | ad.u.net.dport = htons(snum); |
| 3898 | ad.u.net.family = sk->sk_family; | 3840 | ad.u.net.family = sk->sk_family; |
| 3899 | err = avc_has_perm(isec->sid, sid, isec->sclass, perm, &ad); | 3841 | err = avc_has_perm(sksec->sid, sid, sksec->sclass, perm, &ad); |
| 3900 | if (err) | 3842 | if (err) |
| 3901 | goto out; | 3843 | goto out; |
| 3902 | } | 3844 | } |
| @@ -3909,7 +3851,7 @@ out: | |||
| 3909 | 3851 | ||
| 3910 | static int selinux_socket_listen(struct socket *sock, int backlog) | 3852 | static int selinux_socket_listen(struct socket *sock, int backlog) |
| 3911 | { | 3853 | { |
| 3912 | return socket_has_perm(current, sock, SOCKET__LISTEN); | 3854 | return sock_has_perm(current, sock->sk, SOCKET__LISTEN); |
| 3913 | } | 3855 | } |
| 3914 | 3856 | ||
| 3915 | static int selinux_socket_accept(struct socket *sock, struct socket *newsock) | 3857 | static int selinux_socket_accept(struct socket *sock, struct socket *newsock) |
| @@ -3918,7 +3860,7 @@ static int selinux_socket_accept(struct socket *sock, struct socket *newsock) | |||
| 3918 | struct inode_security_struct *isec; | 3860 | struct inode_security_struct *isec; |
| 3919 | struct inode_security_struct *newisec; | 3861 | struct inode_security_struct *newisec; |
| 3920 | 3862 | ||
| 3921 | err = socket_has_perm(current, sock, SOCKET__ACCEPT); | 3863 | err = sock_has_perm(current, sock->sk, SOCKET__ACCEPT); |
| 3922 | if (err) | 3864 | if (err) |
| 3923 | return err; | 3865 | return err; |
| 3924 | 3866 | ||
| @@ -3935,30 +3877,30 @@ static int selinux_socket_accept(struct socket *sock, struct socket *newsock) | |||
| 3935 | static int selinux_socket_sendmsg(struct socket *sock, struct msghdr *msg, | 3877 | static int selinux_socket_sendmsg(struct socket *sock, struct msghdr *msg, |
| 3936 | int size) | 3878 | int size) |
| 3937 | { | 3879 | { |
| 3938 | return socket_has_perm(current, sock, SOCKET__WRITE); | 3880 | return sock_has_perm(current, sock->sk, SOCKET__WRITE); |
| 3939 | } | 3881 | } |
| 3940 | 3882 | ||
| 3941 | static int selinux_socket_recvmsg(struct socket *sock, struct msghdr *msg, | 3883 | static int selinux_socket_recvmsg(struct socket *sock, struct msghdr *msg, |
| 3942 | int size, int flags) | 3884 | int size, int flags) |
| 3943 | { | 3885 | { |
| 3944 | return socket_has_perm(current, sock, SOCKET__READ); | 3886 | return sock_has_perm(current, sock->sk, SOCKET__READ); |
| 3945 | } | 3887 | } |
| 3946 | 3888 | ||
| 3947 | static int selinux_socket_getsockname(struct socket *sock) | 3889 | static int selinux_socket_getsockname(struct socket *sock) |
| 3948 | { | 3890 | { |
| 3949 | return socket_has_perm(current, sock, SOCKET__GETATTR); | 3891 | return sock_has_perm(current, sock->sk, SOCKET__GETATTR); |
| 3950 | } | 3892 | } |
| 3951 | 3893 | ||
| 3952 | static int selinux_socket_getpeername(struct socket *sock) | 3894 | static int selinux_socket_getpeername(struct socket *sock) |
| 3953 | { | 3895 | { |
| 3954 | return socket_has_perm(current, sock, SOCKET__GETATTR); | 3896 | return sock_has_perm(current, sock->sk, SOCKET__GETATTR); |
| 3955 | } | 3897 | } |
| 3956 | 3898 | ||
| 3957 | static int selinux_socket_setsockopt(struct socket *sock, int level, int optname) | 3899 | static int selinux_socket_setsockopt(struct socket *sock, int level, int optname) |
| 3958 | { | 3900 | { |
| 3959 | int err; | 3901 | int err; |
| 3960 | 3902 | ||
| 3961 | err = socket_has_perm(current, sock, SOCKET__SETOPT); | 3903 | err = sock_has_perm(current, sock->sk, SOCKET__SETOPT); |
| 3962 | if (err) | 3904 | if (err) |
| 3963 | return err; | 3905 | return err; |
| 3964 | 3906 | ||
| @@ -3968,68 +3910,58 @@ static int selinux_socket_setsockopt(struct socket *sock, int level, int optname | |||
| 3968 | static int selinux_socket_getsockopt(struct socket *sock, int level, | 3910 | static int selinux_socket_getsockopt(struct socket *sock, int level, |
| 3969 | int optname) | 3911 | int optname) |
| 3970 | { | 3912 | { |
| 3971 | return socket_has_perm(current, sock, SOCKET__GETOPT); | 3913 | return sock_has_perm(current, sock->sk, SOCKET__GETOPT); |
| 3972 | } | 3914 | } |
| 3973 | 3915 | ||
| 3974 | static int selinux_socket_shutdown(struct socket *sock, int how) | 3916 | static int selinux_socket_shutdown(struct socket *sock, int how) |
| 3975 | { | 3917 | { |
| 3976 | return socket_has_perm(current, sock, SOCKET__SHUTDOWN); | 3918 | return sock_has_perm(current, sock->sk, SOCKET__SHUTDOWN); |
| 3977 | } | 3919 | } |
| 3978 | 3920 | ||
| 3979 | static int selinux_socket_unix_stream_connect(struct socket *sock, | 3921 | static int selinux_socket_unix_stream_connect(struct socket *sock, |
| 3980 | struct socket *other, | 3922 | struct socket *other, |
| 3981 | struct sock *newsk) | 3923 | struct sock *newsk) |
| 3982 | { | 3924 | { |
| 3983 | struct sk_security_struct *sksec; | 3925 | struct sk_security_struct *sksec_sock = sock->sk->sk_security; |
| 3984 | struct inode_security_struct *isec; | 3926 | struct sk_security_struct *sksec_other = other->sk->sk_security; |
| 3985 | struct inode_security_struct *other_isec; | 3927 | struct sk_security_struct *sksec_new = newsk->sk_security; |
| 3986 | struct common_audit_data ad; | 3928 | struct common_audit_data ad; |
| 3987 | int err; | 3929 | int err; |
| 3988 | 3930 | ||
| 3989 | isec = SOCK_INODE(sock)->i_security; | ||
| 3990 | other_isec = SOCK_INODE(other)->i_security; | ||
| 3991 | |||
| 3992 | COMMON_AUDIT_DATA_INIT(&ad, NET); | 3931 | COMMON_AUDIT_DATA_INIT(&ad, NET); |
| 3993 | ad.u.net.sk = other->sk; | 3932 | ad.u.net.sk = other->sk; |
| 3994 | 3933 | ||
| 3995 | err = avc_has_perm(isec->sid, other_isec->sid, | 3934 | err = avc_has_perm(sksec_sock->sid, sksec_other->sid, |
| 3996 | isec->sclass, | 3935 | sksec_other->sclass, |
| 3997 | UNIX_STREAM_SOCKET__CONNECTTO, &ad); | 3936 | UNIX_STREAM_SOCKET__CONNECTTO, &ad); |
| 3998 | if (err) | 3937 | if (err) |
| 3999 | return err; | 3938 | return err; |
| 4000 | 3939 | ||
| 4001 | /* connecting socket */ | ||
| 4002 | sksec = sock->sk->sk_security; | ||
| 4003 | sksec->peer_sid = other_isec->sid; | ||
| 4004 | |||
| 4005 | /* server child socket */ | 3940 | /* server child socket */ |
| 4006 | sksec = newsk->sk_security; | 3941 | sksec_new->peer_sid = sksec_sock->sid; |
| 4007 | sksec->peer_sid = isec->sid; | 3942 | err = security_sid_mls_copy(sksec_other->sid, sksec_sock->sid, |
| 4008 | err = security_sid_mls_copy(other_isec->sid, sksec->peer_sid, &sksec->sid); | 3943 | &sksec_new->sid); |
| 3944 | if (err) | ||
| 3945 | return err; | ||
| 4009 | 3946 | ||
| 4010 | return err; | 3947 | /* connecting socket */ |
| 3948 | sksec_sock->peer_sid = sksec_new->sid; | ||
| 3949 | |||
| 3950 | return 0; | ||
| 4011 | } | 3951 | } |
| 4012 | 3952 | ||
| 4013 | static int selinux_socket_unix_may_send(struct socket *sock, | 3953 | static int selinux_socket_unix_may_send(struct socket *sock, |
| 4014 | struct socket *other) | 3954 | struct socket *other) |
| 4015 | { | 3955 | { |
| 4016 | struct inode_security_struct *isec; | 3956 | struct sk_security_struct *ssec = sock->sk->sk_security; |
| 4017 | struct inode_security_struct *other_isec; | 3957 | struct sk_security_struct *osec = other->sk->sk_security; |
| 4018 | struct common_audit_data ad; | 3958 | struct common_audit_data ad; |
| 4019 | int err; | ||
| 4020 | |||
| 4021 | isec = SOCK_INODE(sock)->i_security; | ||
| 4022 | other_isec = SOCK_INODE(other)->i_security; | ||
| 4023 | 3959 | ||
| 4024 | COMMON_AUDIT_DATA_INIT(&ad, NET); | 3960 | COMMON_AUDIT_DATA_INIT(&ad, NET); |
| 4025 | ad.u.net.sk = other->sk; | 3961 | ad.u.net.sk = other->sk; |
| 4026 | 3962 | ||
| 4027 | err = avc_has_perm(isec->sid, other_isec->sid, | 3963 | return avc_has_perm(ssec->sid, osec->sid, osec->sclass, SOCKET__SENDTO, |
| 4028 | isec->sclass, SOCKET__SENDTO, &ad); | 3964 | &ad); |
| 4029 | if (err) | ||
| 4030 | return err; | ||
| 4031 | |||
| 4032 | return 0; | ||
| 4033 | } | 3965 | } |
| 4034 | 3966 | ||
| 4035 | static int selinux_inet_sys_rcv_skb(int ifindex, char *addrp, u16 family, | 3967 | static int selinux_inet_sys_rcv_skb(int ifindex, char *addrp, u16 family, |
| @@ -4168,26 +4100,18 @@ static int selinux_socket_getpeersec_stream(struct socket *sock, char __user *op | |||
| 4168 | int err = 0; | 4100 | int err = 0; |
| 4169 | char *scontext; | 4101 | char *scontext; |
| 4170 | u32 scontext_len; | 4102 | u32 scontext_len; |
| 4171 | struct sk_security_struct *sksec; | 4103 | struct sk_security_struct *sksec = sock->sk->sk_security; |
| 4172 | struct inode_security_struct *isec; | ||
| 4173 | u32 peer_sid = SECSID_NULL; | 4104 | u32 peer_sid = SECSID_NULL; |
| 4174 | 4105 | ||
| 4175 | isec = SOCK_INODE(sock)->i_security; | 4106 | if (sksec->sclass == SECCLASS_UNIX_STREAM_SOCKET || |
| 4176 | 4107 | sksec->sclass == SECCLASS_TCP_SOCKET) | |
| 4177 | if (isec->sclass == SECCLASS_UNIX_STREAM_SOCKET || | ||
| 4178 | isec->sclass == SECCLASS_TCP_SOCKET) { | ||
| 4179 | sksec = sock->sk->sk_security; | ||
| 4180 | peer_sid = sksec->peer_sid; | 4108 | peer_sid = sksec->peer_sid; |
| 4181 | } | 4109 | if (peer_sid == SECSID_NULL) |
| 4182 | if (peer_sid == SECSID_NULL) { | 4110 | return -ENOPROTOOPT; |
| 4183 | err = -ENOPROTOOPT; | ||
| 4184 | goto out; | ||
| 4185 | } | ||
| 4186 | 4111 | ||
| 4187 | err = security_sid_to_context(peer_sid, &scontext, &scontext_len); | 4112 | err = security_sid_to_context(peer_sid, &scontext, &scontext_len); |
| 4188 | |||
| 4189 | if (err) | 4113 | if (err) |
| 4190 | goto out; | 4114 | return err; |
| 4191 | 4115 | ||
| 4192 | if (scontext_len > len) { | 4116 | if (scontext_len > len) { |
| 4193 | err = -ERANGE; | 4117 | err = -ERANGE; |
| @@ -4200,9 +4124,7 @@ static int selinux_socket_getpeersec_stream(struct socket *sock, char __user *op | |||
| 4200 | out_len: | 4124 | out_len: |
| 4201 | if (put_user(scontext_len, optlen)) | 4125 | if (put_user(scontext_len, optlen)) |
| 4202 | err = -EFAULT; | 4126 | err = -EFAULT; |
| 4203 | |||
| 4204 | kfree(scontext); | 4127 | kfree(scontext); |
| 4205 | out: | ||
| 4206 | return err; | 4128 | return err; |
| 4207 | } | 4129 | } |
| 4208 | 4130 | ||
| @@ -4234,12 +4156,27 @@ out: | |||
| 4234 | 4156 | ||
| 4235 | static int selinux_sk_alloc_security(struct sock *sk, int family, gfp_t priority) | 4157 | static int selinux_sk_alloc_security(struct sock *sk, int family, gfp_t priority) |
| 4236 | { | 4158 | { |
| 4237 | return sk_alloc_security(sk, family, priority); | 4159 | struct sk_security_struct *sksec; |
| 4160 | |||
| 4161 | sksec = kzalloc(sizeof(*sksec), priority); | ||
| 4162 | if (!sksec) | ||
| 4163 | return -ENOMEM; | ||
| 4164 | |||
| 4165 | sksec->peer_sid = SECINITSID_UNLABELED; | ||
| 4166 | sksec->sid = SECINITSID_UNLABELED; | ||
| 4167 | selinux_netlbl_sk_security_reset(sksec); | ||
| 4168 | sk->sk_security = sksec; | ||
| 4169 | |||
| 4170 | return 0; | ||
| 4238 | } | 4171 | } |
| 4239 | 4172 | ||
| 4240 | static void selinux_sk_free_security(struct sock *sk) | 4173 | static void selinux_sk_free_security(struct sock *sk) |
| 4241 | { | 4174 | { |
| 4242 | sk_free_security(sk); | 4175 | struct sk_security_struct *sksec = sk->sk_security; |
| 4176 | |||
| 4177 | sk->sk_security = NULL; | ||
| 4178 | selinux_netlbl_sk_security_free(sksec); | ||
| 4179 | kfree(sksec); | ||
| 4243 | } | 4180 | } |
| 4244 | 4181 | ||
| 4245 | static void selinux_sk_clone_security(const struct sock *sk, struct sock *newsk) | 4182 | static void selinux_sk_clone_security(const struct sock *sk, struct sock *newsk) |
| @@ -4399,8 +4336,7 @@ static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb) | |||
| 4399 | int err = 0; | 4336 | int err = 0; |
| 4400 | u32 perm; | 4337 | u32 perm; |
| 4401 | struct nlmsghdr *nlh; | 4338 | struct nlmsghdr *nlh; |
| 4402 | struct socket *sock = sk->sk_socket; | 4339 | struct sk_security_struct *sksec = sk->sk_security; |
| 4403 | struct inode_security_struct *isec = SOCK_INODE(sock)->i_security; | ||
| 4404 | 4340 | ||
| 4405 | if (skb->len < NLMSG_SPACE(0)) { | 4341 | if (skb->len < NLMSG_SPACE(0)) { |
| 4406 | err = -EINVAL; | 4342 | err = -EINVAL; |
| @@ -4408,13 +4344,13 @@ static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb) | |||
| 4408 | } | 4344 | } |
| 4409 | nlh = nlmsg_hdr(skb); | 4345 | nlh = nlmsg_hdr(skb); |
| 4410 | 4346 | ||
| 4411 | err = selinux_nlmsg_lookup(isec->sclass, nlh->nlmsg_type, &perm); | 4347 | err = selinux_nlmsg_lookup(sksec->sclass, nlh->nlmsg_type, &perm); |
| 4412 | if (err) { | 4348 | if (err) { |
| 4413 | if (err == -EINVAL) { | 4349 | if (err == -EINVAL) { |
| 4414 | audit_log(current->audit_context, GFP_KERNEL, AUDIT_SELINUX_ERR, | 4350 | audit_log(current->audit_context, GFP_KERNEL, AUDIT_SELINUX_ERR, |
| 4415 | "SELinux: unrecognized netlink message" | 4351 | "SELinux: unrecognized netlink message" |
| 4416 | " type=%hu for sclass=%hu\n", | 4352 | " type=%hu for sclass=%hu\n", |
| 4417 | nlh->nlmsg_type, isec->sclass); | 4353 | nlh->nlmsg_type, sksec->sclass); |
| 4418 | if (!selinux_enforcing || security_get_allow_unknown()) | 4354 | if (!selinux_enforcing || security_get_allow_unknown()) |
| 4419 | err = 0; | 4355 | err = 0; |
| 4420 | } | 4356 | } |
| @@ -4425,7 +4361,7 @@ static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb) | |||
| 4425 | goto out; | 4361 | goto out; |
| 4426 | } | 4362 | } |
| 4427 | 4363 | ||
| 4428 | err = socket_has_perm(current, sock, perm); | 4364 | err = sock_has_perm(current, sk, perm); |
| 4429 | out: | 4365 | out: |
| 4430 | return err; | 4366 | return err; |
| 4431 | } | 4367 | } |
diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h index 8b32e959bb2e..b4c9eb4bd6f9 100644 --- a/security/selinux/include/classmap.h +++ b/security/selinux/include/classmap.h | |||
| @@ -2,7 +2,8 @@ | |||
| 2 | "getattr", "setattr", "lock", "relabelfrom", "relabelto", "append" | 2 | "getattr", "setattr", "lock", "relabelfrom", "relabelto", "append" |
| 3 | 3 | ||
| 4 | #define COMMON_FILE_PERMS COMMON_FILE_SOCK_PERMS, "unlink", "link", \ | 4 | #define COMMON_FILE_PERMS COMMON_FILE_SOCK_PERMS, "unlink", "link", \ |
| 5 | "rename", "execute", "swapon", "quotaon", "mounton" | 5 | "rename", "execute", "swapon", "quotaon", "mounton", "audit_access", \ |
| 6 | "open", "execmod" | ||
| 6 | 7 | ||
| 7 | #define COMMON_SOCK_PERMS COMMON_FILE_SOCK_PERMS, "bind", "connect", \ | 8 | #define COMMON_SOCK_PERMS COMMON_FILE_SOCK_PERMS, "bind", "connect", \ |
| 8 | "listen", "accept", "getopt", "setopt", "shutdown", "recvfrom", \ | 9 | "listen", "accept", "getopt", "setopt", "shutdown", "recvfrom", \ |
| @@ -43,22 +44,21 @@ struct security_class_mapping secclass_map[] = { | |||
| 43 | "quotaget", NULL } }, | 44 | "quotaget", NULL } }, |
| 44 | { "file", | 45 | { "file", |
| 45 | { COMMON_FILE_PERMS, | 46 | { COMMON_FILE_PERMS, |
| 46 | "execute_no_trans", "entrypoint", "execmod", "open", NULL } }, | 47 | "execute_no_trans", "entrypoint", NULL } }, |
| 47 | { "dir", | 48 | { "dir", |
| 48 | { COMMON_FILE_PERMS, "add_name", "remove_name", | 49 | { COMMON_FILE_PERMS, "add_name", "remove_name", |
| 49 | "reparent", "search", "rmdir", "open", NULL } }, | 50 | "reparent", "search", "rmdir", NULL } }, |
| 50 | { "fd", { "use", NULL } }, | 51 | { "fd", { "use", NULL } }, |
| 51 | { "lnk_file", | 52 | { "lnk_file", |
| 52 | { COMMON_FILE_PERMS, NULL } }, | 53 | { COMMON_FILE_PERMS, NULL } }, |
| 53 | { "chr_file", | 54 | { "chr_file", |
| 54 | { COMMON_FILE_PERMS, | 55 | { COMMON_FILE_PERMS, NULL } }, |
| 55 | "execute_no_trans", "entrypoint", "execmod", "open", NULL } }, | ||
| 56 | { "blk_file", | 56 | { "blk_file", |
| 57 | { COMMON_FILE_PERMS, "open", NULL } }, | 57 | { COMMON_FILE_PERMS, NULL } }, |
| 58 | { "sock_file", | 58 | { "sock_file", |
| 59 | { COMMON_FILE_PERMS, "open", NULL } }, | 59 | { COMMON_FILE_PERMS, NULL } }, |
| 60 | { "fifo_file", | 60 | { "fifo_file", |
| 61 | { COMMON_FILE_PERMS, "open", NULL } }, | 61 | { COMMON_FILE_PERMS, NULL } }, |
| 62 | { "socket", | 62 | { "socket", |
| 63 | { COMMON_SOCK_PERMS, NULL } }, | 63 | { COMMON_SOCK_PERMS, NULL } }, |
| 64 | { "tcp_socket", | 64 | { "tcp_socket", |
diff --git a/security/selinux/netnode.c b/security/selinux/netnode.c index dc92792271f1..65ebfe954f85 100644 --- a/security/selinux/netnode.c +++ b/security/selinux/netnode.c | |||
| @@ -183,8 +183,6 @@ static void sel_netnode_insert(struct sel_netnode *node) | |||
| 183 | BUG(); | 183 | BUG(); |
| 184 | } | 184 | } |
| 185 | 185 | ||
| 186 | INIT_RCU_HEAD(&node->rcu); | ||
| 187 | |||
| 188 | /* we need to impose a limit on the growth of the hash table so check | 186 | /* we need to impose a limit on the growth of the hash table so check |
| 189 | * this bucket to make sure it is within the specified bounds */ | 187 | * this bucket to make sure it is within the specified bounds */ |
| 190 | list_add_rcu(&node->list, &sel_netnode_hash[idx].list); | 188 | list_add_rcu(&node->list, &sel_netnode_hash[idx].list); |
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index 0293843f7eda..79a1bb635662 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c | |||
| @@ -184,6 +184,7 @@ out: | |||
| 184 | static const struct file_operations sel_enforce_ops = { | 184 | static const struct file_operations sel_enforce_ops = { |
| 185 | .read = sel_read_enforce, | 185 | .read = sel_read_enforce, |
| 186 | .write = sel_write_enforce, | 186 | .write = sel_write_enforce, |
| 187 | .llseek = generic_file_llseek, | ||
| 187 | }; | 188 | }; |
| 188 | 189 | ||
| 189 | static ssize_t sel_read_handle_unknown(struct file *filp, char __user *buf, | 190 | static ssize_t sel_read_handle_unknown(struct file *filp, char __user *buf, |
| @@ -201,6 +202,7 @@ static ssize_t sel_read_handle_unknown(struct file *filp, char __user *buf, | |||
| 201 | 202 | ||
| 202 | static const struct file_operations sel_handle_unknown_ops = { | 203 | static const struct file_operations sel_handle_unknown_ops = { |
| 203 | .read = sel_read_handle_unknown, | 204 | .read = sel_read_handle_unknown, |
| 205 | .llseek = generic_file_llseek, | ||
| 204 | }; | 206 | }; |
| 205 | 207 | ||
| 206 | #ifdef CONFIG_SECURITY_SELINUX_DISABLE | 208 | #ifdef CONFIG_SECURITY_SELINUX_DISABLE |
| @@ -251,6 +253,7 @@ out: | |||
| 251 | 253 | ||
| 252 | static const struct file_operations sel_disable_ops = { | 254 | static const struct file_operations sel_disable_ops = { |
| 253 | .write = sel_write_disable, | 255 | .write = sel_write_disable, |
| 256 | .llseek = generic_file_llseek, | ||
| 254 | }; | 257 | }; |
| 255 | 258 | ||
| 256 | static ssize_t sel_read_policyvers(struct file *filp, char __user *buf, | 259 | static ssize_t sel_read_policyvers(struct file *filp, char __user *buf, |
| @@ -265,6 +268,7 @@ static ssize_t sel_read_policyvers(struct file *filp, char __user *buf, | |||
| 265 | 268 | ||
| 266 | static const struct file_operations sel_policyvers_ops = { | 269 | static const struct file_operations sel_policyvers_ops = { |
| 267 | .read = sel_read_policyvers, | 270 | .read = sel_read_policyvers, |
| 271 | .llseek = generic_file_llseek, | ||
| 268 | }; | 272 | }; |
| 269 | 273 | ||
| 270 | /* declaration for sel_write_load */ | 274 | /* declaration for sel_write_load */ |
| @@ -289,6 +293,7 @@ static ssize_t sel_read_mls(struct file *filp, char __user *buf, | |||
| 289 | 293 | ||
| 290 | static const struct file_operations sel_mls_ops = { | 294 | static const struct file_operations sel_mls_ops = { |
| 291 | .read = sel_read_mls, | 295 | .read = sel_read_mls, |
| 296 | .llseek = generic_file_llseek, | ||
| 292 | }; | 297 | }; |
| 293 | 298 | ||
| 294 | static ssize_t sel_write_load(struct file *file, const char __user *buf, | 299 | static ssize_t sel_write_load(struct file *file, const char __user *buf, |
| @@ -356,6 +361,7 @@ out: | |||
| 356 | 361 | ||
| 357 | static const struct file_operations sel_load_ops = { | 362 | static const struct file_operations sel_load_ops = { |
| 358 | .write = sel_write_load, | 363 | .write = sel_write_load, |
| 364 | .llseek = generic_file_llseek, | ||
| 359 | }; | 365 | }; |
| 360 | 366 | ||
| 361 | static ssize_t sel_write_context(struct file *file, char *buf, size_t size) | 367 | static ssize_t sel_write_context(struct file *file, char *buf, size_t size) |
| @@ -437,6 +443,7 @@ out: | |||
| 437 | static const struct file_operations sel_checkreqprot_ops = { | 443 | static const struct file_operations sel_checkreqprot_ops = { |
| 438 | .read = sel_read_checkreqprot, | 444 | .read = sel_read_checkreqprot, |
| 439 | .write = sel_write_checkreqprot, | 445 | .write = sel_write_checkreqprot, |
| 446 | .llseek = generic_file_llseek, | ||
| 440 | }; | 447 | }; |
| 441 | 448 | ||
| 442 | /* | 449 | /* |
| @@ -482,6 +489,7 @@ static const struct file_operations transaction_ops = { | |||
| 482 | .write = selinux_transaction_write, | 489 | .write = selinux_transaction_write, |
| 483 | .read = simple_transaction_read, | 490 | .read = simple_transaction_read, |
| 484 | .release = simple_transaction_release, | 491 | .release = simple_transaction_release, |
| 492 | .llseek = generic_file_llseek, | ||
| 485 | }; | 493 | }; |
| 486 | 494 | ||
| 487 | /* | 495 | /* |
| @@ -883,6 +891,7 @@ out: | |||
| 883 | static const struct file_operations sel_bool_ops = { | 891 | static const struct file_operations sel_bool_ops = { |
| 884 | .read = sel_read_bool, | 892 | .read = sel_read_bool, |
| 885 | .write = sel_write_bool, | 893 | .write = sel_write_bool, |
| 894 | .llseek = generic_file_llseek, | ||
| 886 | }; | 895 | }; |
| 887 | 896 | ||
| 888 | static ssize_t sel_commit_bools_write(struct file *filep, | 897 | static ssize_t sel_commit_bools_write(struct file *filep, |
| @@ -935,6 +944,7 @@ out: | |||
| 935 | 944 | ||
| 936 | static const struct file_operations sel_commit_bools_ops = { | 945 | static const struct file_operations sel_commit_bools_ops = { |
| 937 | .write = sel_commit_bools_write, | 946 | .write = sel_commit_bools_write, |
| 947 | .llseek = generic_file_llseek, | ||
| 938 | }; | 948 | }; |
| 939 | 949 | ||
| 940 | static void sel_remove_entries(struct dentry *de) | 950 | static void sel_remove_entries(struct dentry *de) |
| @@ -1127,10 +1137,12 @@ out: | |||
| 1127 | static const struct file_operations sel_avc_cache_threshold_ops = { | 1137 | static const struct file_operations sel_avc_cache_threshold_ops = { |
| 1128 | .read = sel_read_avc_cache_threshold, | 1138 | .read = sel_read_avc_cache_threshold, |
| 1129 | .write = sel_write_avc_cache_threshold, | 1139 | .write = sel_write_avc_cache_threshold, |
| 1140 | .llseek = generic_file_llseek, | ||
| 1130 | }; | 1141 | }; |
| 1131 | 1142 | ||
| 1132 | static const struct file_operations sel_avc_hash_stats_ops = { | 1143 | static const struct file_operations sel_avc_hash_stats_ops = { |
| 1133 | .read = sel_read_avc_hash_stats, | 1144 | .read = sel_read_avc_hash_stats, |
| 1145 | .llseek = generic_file_llseek, | ||
| 1134 | }; | 1146 | }; |
| 1135 | 1147 | ||
| 1136 | #ifdef CONFIG_SECURITY_SELINUX_AVC_STATS | 1148 | #ifdef CONFIG_SECURITY_SELINUX_AVC_STATS |
| @@ -1255,6 +1267,7 @@ static ssize_t sel_read_initcon(struct file *file, char __user *buf, | |||
| 1255 | 1267 | ||
| 1256 | static const struct file_operations sel_initcon_ops = { | 1268 | static const struct file_operations sel_initcon_ops = { |
| 1257 | .read = sel_read_initcon, | 1269 | .read = sel_read_initcon, |
| 1270 | .llseek = generic_file_llseek, | ||
| 1258 | }; | 1271 | }; |
| 1259 | 1272 | ||
| 1260 | static int sel_make_initcon_files(struct dentry *dir) | 1273 | static int sel_make_initcon_files(struct dentry *dir) |
| @@ -1330,6 +1343,7 @@ out: | |||
| 1330 | 1343 | ||
| 1331 | static const struct file_operations sel_class_ops = { | 1344 | static const struct file_operations sel_class_ops = { |
| 1332 | .read = sel_read_class, | 1345 | .read = sel_read_class, |
| 1346 | .llseek = generic_file_llseek, | ||
| 1333 | }; | 1347 | }; |
| 1334 | 1348 | ||
| 1335 | static ssize_t sel_read_perm(struct file *file, char __user *buf, | 1349 | static ssize_t sel_read_perm(struct file *file, char __user *buf, |
| @@ -1354,6 +1368,7 @@ out: | |||
| 1354 | 1368 | ||
| 1355 | static const struct file_operations sel_perm_ops = { | 1369 | static const struct file_operations sel_perm_ops = { |
| 1356 | .read = sel_read_perm, | 1370 | .read = sel_read_perm, |
| 1371 | .llseek = generic_file_llseek, | ||
| 1357 | }; | 1372 | }; |
| 1358 | 1373 | ||
| 1359 | static ssize_t sel_read_policycap(struct file *file, char __user *buf, | 1374 | static ssize_t sel_read_policycap(struct file *file, char __user *buf, |
| @@ -1372,6 +1387,7 @@ static ssize_t sel_read_policycap(struct file *file, char __user *buf, | |||
| 1372 | 1387 | ||
| 1373 | static const struct file_operations sel_policycap_ops = { | 1388 | static const struct file_operations sel_policycap_ops = { |
| 1374 | .read = sel_read_policycap, | 1389 | .read = sel_read_policycap, |
| 1390 | .llseek = generic_file_llseek, | ||
| 1375 | }; | 1391 | }; |
| 1376 | 1392 | ||
| 1377 | static int sel_make_perm_files(char *objclass, int classvalue, | 1393 | static int sel_make_perm_files(char *objclass, int classvalue, |
diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c index 1215b8e47dba..929480c6c430 100644 --- a/security/selinux/ss/avtab.c +++ b/security/selinux/ss/avtab.c | |||
| @@ -342,20 +342,20 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, | |||
| 342 | 342 | ||
| 343 | if (vers < POLICYDB_VERSION_AVTAB) { | 343 | if (vers < POLICYDB_VERSION_AVTAB) { |
| 344 | rc = next_entry(buf32, fp, sizeof(u32)); | 344 | rc = next_entry(buf32, fp, sizeof(u32)); |
| 345 | if (rc < 0) { | 345 | if (rc) { |
| 346 | printk(KERN_ERR "SELinux: avtab: truncated entry\n"); | 346 | printk(KERN_ERR "SELinux: avtab: truncated entry\n"); |
| 347 | return -1; | 347 | return rc; |
| 348 | } | 348 | } |
| 349 | items2 = le32_to_cpu(buf32[0]); | 349 | items2 = le32_to_cpu(buf32[0]); |
| 350 | if (items2 > ARRAY_SIZE(buf32)) { | 350 | if (items2 > ARRAY_SIZE(buf32)) { |
| 351 | printk(KERN_ERR "SELinux: avtab: entry overflow\n"); | 351 | printk(KERN_ERR "SELinux: avtab: entry overflow\n"); |
| 352 | return -1; | 352 | return -EINVAL; |
| 353 | 353 | ||
| 354 | } | 354 | } |
| 355 | rc = next_entry(buf32, fp, sizeof(u32)*items2); | 355 | rc = next_entry(buf32, fp, sizeof(u32)*items2); |
| 356 | if (rc < 0) { | 356 | if (rc) { |
| 357 | printk(KERN_ERR "SELinux: avtab: truncated entry\n"); | 357 | printk(KERN_ERR "SELinux: avtab: truncated entry\n"); |
| 358 | return -1; | 358 | return rc; |
| 359 | } | 359 | } |
| 360 | items = 0; | 360 | items = 0; |
| 361 | 361 | ||
| @@ -363,19 +363,19 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, | |||
| 363 | key.source_type = (u16)val; | 363 | key.source_type = (u16)val; |
| 364 | if (key.source_type != val) { | 364 | if (key.source_type != val) { |
| 365 | printk(KERN_ERR "SELinux: avtab: truncated source type\n"); | 365 | printk(KERN_ERR "SELinux: avtab: truncated source type\n"); |
| 366 | return -1; | 366 | return -EINVAL; |
| 367 | } | 367 | } |
| 368 | val = le32_to_cpu(buf32[items++]); | 368 | val = le32_to_cpu(buf32[items++]); |
| 369 | key.target_type = (u16)val; | 369 | key.target_type = (u16)val; |
| 370 | if (key.target_type != val) { | 370 | if (key.target_type != val) { |
| 371 | printk(KERN_ERR "SELinux: avtab: truncated target type\n"); | 371 | printk(KERN_ERR "SELinux: avtab: truncated target type\n"); |
| 372 | return -1; | 372 | return -EINVAL; |
| 373 | } | 373 | } |
| 374 | val = le32_to_cpu(buf32[items++]); | 374 | val = le32_to_cpu(buf32[items++]); |
| 375 | key.target_class = (u16)val; | 375 | key.target_class = (u16)val; |
| 376 | if (key.target_class != val) { | 376 | if (key.target_class != val) { |
| 377 | printk(KERN_ERR "SELinux: avtab: truncated target class\n"); | 377 | printk(KERN_ERR "SELinux: avtab: truncated target class\n"); |
| 378 | return -1; | 378 | return -EINVAL; |
| 379 | } | 379 | } |
| 380 | 380 | ||
| 381 | val = le32_to_cpu(buf32[items++]); | 381 | val = le32_to_cpu(buf32[items++]); |
| @@ -383,12 +383,12 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, | |||
| 383 | 383 | ||
| 384 | if (!(val & (AVTAB_AV | AVTAB_TYPE))) { | 384 | if (!(val & (AVTAB_AV | AVTAB_TYPE))) { |
| 385 | printk(KERN_ERR "SELinux: avtab: null entry\n"); | 385 | printk(KERN_ERR "SELinux: avtab: null entry\n"); |
| 386 | return -1; | 386 | return -EINVAL; |
| 387 | } | 387 | } |
| 388 | if ((val & AVTAB_AV) && | 388 | if ((val & AVTAB_AV) && |
| 389 | (val & AVTAB_TYPE)) { | 389 | (val & AVTAB_TYPE)) { |
| 390 | printk(KERN_ERR "SELinux: avtab: entry has both access vectors and types\n"); | 390 | printk(KERN_ERR "SELinux: avtab: entry has both access vectors and types\n"); |
| 391 | return -1; | 391 | return -EINVAL; |
| 392 | } | 392 | } |
| 393 | 393 | ||
| 394 | for (i = 0; i < ARRAY_SIZE(spec_order); i++) { | 394 | for (i = 0; i < ARRAY_SIZE(spec_order); i++) { |
| @@ -403,15 +403,15 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, | |||
| 403 | 403 | ||
| 404 | if (items != items2) { | 404 | if (items != items2) { |
| 405 | printk(KERN_ERR "SELinux: avtab: entry only had %d items, expected %d\n", items2, items); | 405 | printk(KERN_ERR "SELinux: avtab: entry only had %d items, expected %d\n", items2, items); |
| 406 | return -1; | 406 | return -EINVAL; |
| 407 | } | 407 | } |
| 408 | return 0; | 408 | return 0; |
| 409 | } | 409 | } |
| 410 | 410 | ||
| 411 | rc = next_entry(buf16, fp, sizeof(u16)*4); | 411 | rc = next_entry(buf16, fp, sizeof(u16)*4); |
| 412 | if (rc < 0) { | 412 | if (rc) { |
| 413 | printk(KERN_ERR "SELinux: avtab: truncated entry\n"); | 413 | printk(KERN_ERR "SELinux: avtab: truncated entry\n"); |
| 414 | return -1; | 414 | return rc; |
| 415 | } | 415 | } |
| 416 | 416 | ||
| 417 | items = 0; | 417 | items = 0; |
| @@ -424,7 +424,7 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, | |||
| 424 | !policydb_type_isvalid(pol, key.target_type) || | 424 | !policydb_type_isvalid(pol, key.target_type) || |
| 425 | !policydb_class_isvalid(pol, key.target_class)) { | 425 | !policydb_class_isvalid(pol, key.target_class)) { |
| 426 | printk(KERN_ERR "SELinux: avtab: invalid type or class\n"); | 426 | printk(KERN_ERR "SELinux: avtab: invalid type or class\n"); |
| 427 | return -1; | 427 | return -EINVAL; |
| 428 | } | 428 | } |
| 429 | 429 | ||
| 430 | set = 0; | 430 | set = 0; |
| @@ -434,19 +434,19 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, | |||
| 434 | } | 434 | } |
| 435 | if (!set || set > 1) { | 435 | if (!set || set > 1) { |
| 436 | printk(KERN_ERR "SELinux: avtab: more than one specifier\n"); | 436 | printk(KERN_ERR "SELinux: avtab: more than one specifier\n"); |
| 437 | return -1; | 437 | return -EINVAL; |
| 438 | } | 438 | } |
| 439 | 439 | ||
| 440 | rc = next_entry(buf32, fp, sizeof(u32)); | 440 | rc = next_entry(buf32, fp, sizeof(u32)); |
| 441 | if (rc < 0) { | 441 | if (rc) { |
| 442 | printk(KERN_ERR "SELinux: avtab: truncated entry\n"); | 442 | printk(KERN_ERR "SELinux: avtab: truncated entry\n"); |
| 443 | return -1; | 443 | return rc; |
| 444 | } | 444 | } |
| 445 | datum.data = le32_to_cpu(*buf32); | 445 | datum.data = le32_to_cpu(*buf32); |
| 446 | if ((key.specified & AVTAB_TYPE) && | 446 | if ((key.specified & AVTAB_TYPE) && |
| 447 | !policydb_type_isvalid(pol, datum.data)) { | 447 | !policydb_type_isvalid(pol, datum.data)) { |
| 448 | printk(KERN_ERR "SELinux: avtab: invalid type\n"); | 448 | printk(KERN_ERR "SELinux: avtab: invalid type\n"); |
| 449 | return -1; | 449 | return -EINVAL; |
| 450 | } | 450 | } |
| 451 | return insertf(a, &key, &datum, p); | 451 | return insertf(a, &key, &datum, p); |
| 452 | } | 452 | } |
| @@ -487,8 +487,7 @@ int avtab_read(struct avtab *a, void *fp, struct policydb *pol) | |||
| 487 | printk(KERN_ERR "SELinux: avtab: out of memory\n"); | 487 | printk(KERN_ERR "SELinux: avtab: out of memory\n"); |
| 488 | else if (rc == -EEXIST) | 488 | else if (rc == -EEXIST) |
| 489 | printk(KERN_ERR "SELinux: avtab: duplicate entry\n"); | 489 | printk(KERN_ERR "SELinux: avtab: duplicate entry\n"); |
| 490 | else | 490 | |
| 491 | rc = -EINVAL; | ||
| 492 | goto bad; | 491 | goto bad; |
| 493 | } | 492 | } |
| 494 | } | 493 | } |
diff --git a/security/selinux/ss/conditional.c b/security/selinux/ss/conditional.c index 4a4e35cac22b..c91e150c3087 100644 --- a/security/selinux/ss/conditional.c +++ b/security/selinux/ss/conditional.c | |||
| @@ -117,10 +117,14 @@ int evaluate_cond_node(struct policydb *p, struct cond_node *node) | |||
| 117 | 117 | ||
| 118 | int cond_policydb_init(struct policydb *p) | 118 | int cond_policydb_init(struct policydb *p) |
| 119 | { | 119 | { |
| 120 | int rc; | ||
| 121 | |||
| 120 | p->bool_val_to_struct = NULL; | 122 | p->bool_val_to_struct = NULL; |
| 121 | p->cond_list = NULL; | 123 | p->cond_list = NULL; |
| 122 | if (avtab_init(&p->te_cond_avtab)) | 124 | |
| 123 | return -1; | 125 | rc = avtab_init(&p->te_cond_avtab); |
| 126 | if (rc) | ||
| 127 | return rc; | ||
| 124 | 128 | ||
| 125 | return 0; | 129 | return 0; |
| 126 | } | 130 | } |
| @@ -219,34 +223,37 @@ int cond_read_bool(struct policydb *p, struct hashtab *h, void *fp) | |||
| 219 | 223 | ||
| 220 | booldatum = kzalloc(sizeof(struct cond_bool_datum), GFP_KERNEL); | 224 | booldatum = kzalloc(sizeof(struct cond_bool_datum), GFP_KERNEL); |
| 221 | if (!booldatum) | 225 | if (!booldatum) |
| 222 | return -1; | 226 | return -ENOMEM; |
| 223 | 227 | ||
| 224 | rc = next_entry(buf, fp, sizeof buf); | 228 | rc = next_entry(buf, fp, sizeof buf); |
| 225 | if (rc < 0) | 229 | if (rc) |
| 226 | goto err; | 230 | goto err; |
| 227 | 231 | ||
| 228 | booldatum->value = le32_to_cpu(buf[0]); | 232 | booldatum->value = le32_to_cpu(buf[0]); |
| 229 | booldatum->state = le32_to_cpu(buf[1]); | 233 | booldatum->state = le32_to_cpu(buf[1]); |
| 230 | 234 | ||
| 235 | rc = -EINVAL; | ||
| 231 | if (!bool_isvalid(booldatum)) | 236 | if (!bool_isvalid(booldatum)) |
| 232 | goto err; | 237 | goto err; |
| 233 | 238 | ||
| 234 | len = le32_to_cpu(buf[2]); | 239 | len = le32_to_cpu(buf[2]); |
| 235 | 240 | ||
| 241 | rc = -ENOMEM; | ||
| 236 | key = kmalloc(len + 1, GFP_KERNEL); | 242 | key = kmalloc(len + 1, GFP_KERNEL); |
| 237 | if (!key) | 243 | if (!key) |
| 238 | goto err; | 244 | goto err; |
| 239 | rc = next_entry(key, fp, len); | 245 | rc = next_entry(key, fp, len); |
| 240 | if (rc < 0) | 246 | if (rc) |
| 241 | goto err; | 247 | goto err; |
| 242 | key[len] = '\0'; | 248 | key[len] = '\0'; |
| 243 | if (hashtab_insert(h, key, booldatum)) | 249 | rc = hashtab_insert(h, key, booldatum); |
| 250 | if (rc) | ||
| 244 | goto err; | 251 | goto err; |
| 245 | 252 | ||
| 246 | return 0; | 253 | return 0; |
| 247 | err: | 254 | err: |
| 248 | cond_destroy_bool(key, booldatum, NULL); | 255 | cond_destroy_bool(key, booldatum, NULL); |
| 249 | return -1; | 256 | return rc; |
| 250 | } | 257 | } |
| 251 | 258 | ||
| 252 | struct cond_insertf_data { | 259 | struct cond_insertf_data { |
| @@ -263,7 +270,7 @@ static int cond_insertf(struct avtab *a, struct avtab_key *k, struct avtab_datum | |||
| 263 | struct cond_av_list *other = data->other, *list, *cur; | 270 | struct cond_av_list *other = data->other, *list, *cur; |
| 264 | struct avtab_node *node_ptr; | 271 | struct avtab_node *node_ptr; |
| 265 | u8 found; | 272 | u8 found; |
| 266 | 273 | int rc = -EINVAL; | |
| 267 | 274 | ||
| 268 | /* | 275 | /* |
| 269 | * For type rules we have to make certain there aren't any | 276 | * For type rules we have to make certain there aren't any |
| @@ -313,12 +320,15 @@ static int cond_insertf(struct avtab *a, struct avtab_key *k, struct avtab_datum | |||
| 313 | node_ptr = avtab_insert_nonunique(&p->te_cond_avtab, k, d); | 320 | node_ptr = avtab_insert_nonunique(&p->te_cond_avtab, k, d); |
| 314 | if (!node_ptr) { | 321 | if (!node_ptr) { |
| 315 | printk(KERN_ERR "SELinux: could not insert rule.\n"); | 322 | printk(KERN_ERR "SELinux: could not insert rule.\n"); |
| 323 | rc = -ENOMEM; | ||
| 316 | goto err; | 324 | goto err; |
| 317 | } | 325 | } |
| 318 | 326 | ||
| 319 | list = kzalloc(sizeof(struct cond_av_list), GFP_KERNEL); | 327 | list = kzalloc(sizeof(struct cond_av_list), GFP_KERNEL); |
| 320 | if (!list) | 328 | if (!list) { |
| 329 | rc = -ENOMEM; | ||
| 321 | goto err; | 330 | goto err; |
| 331 | } | ||
| 322 | 332 | ||
| 323 | list->node = node_ptr; | 333 | list->node = node_ptr; |
| 324 | if (!data->head) | 334 | if (!data->head) |
| @@ -331,7 +341,7 @@ static int cond_insertf(struct avtab *a, struct avtab_key *k, struct avtab_datum | |||
| 331 | err: | 341 | err: |
| 332 | cond_av_list_destroy(data->head); | 342 | cond_av_list_destroy(data->head); |
| 333 | data->head = NULL; | 343 | data->head = NULL; |
| 334 | return -1; | 344 | return rc; |
| 335 | } | 345 | } |
| 336 | 346 | ||
| 337 | static int cond_read_av_list(struct policydb *p, void *fp, struct cond_av_list **ret_list, struct cond_av_list *other) | 347 | static int cond_read_av_list(struct policydb *p, void *fp, struct cond_av_list **ret_list, struct cond_av_list *other) |
| @@ -345,8 +355,8 @@ static int cond_read_av_list(struct policydb *p, void *fp, struct cond_av_list * | |||
| 345 | 355 | ||
| 346 | len = 0; | 356 | len = 0; |
| 347 | rc = next_entry(buf, fp, sizeof(u32)); | 357 | rc = next_entry(buf, fp, sizeof(u32)); |
| 348 | if (rc < 0) | 358 | if (rc) |
| 349 | return -1; | 359 | return rc; |
| 350 | 360 | ||
| 351 | len = le32_to_cpu(buf[0]); | 361 | len = le32_to_cpu(buf[0]); |
| 352 | if (len == 0) | 362 | if (len == 0) |
| @@ -361,7 +371,6 @@ static int cond_read_av_list(struct policydb *p, void *fp, struct cond_av_list * | |||
| 361 | &data); | 371 | &data); |
| 362 | if (rc) | 372 | if (rc) |
| 363 | return rc; | 373 | return rc; |
| 364 | |||
| 365 | } | 374 | } |
| 366 | 375 | ||
| 367 | *ret_list = data.head; | 376 | *ret_list = data.head; |
| @@ -390,24 +399,25 @@ static int cond_read_node(struct policydb *p, struct cond_node *node, void *fp) | |||
| 390 | struct cond_expr *expr = NULL, *last = NULL; | 399 | struct cond_expr *expr = NULL, *last = NULL; |
| 391 | 400 | ||
| 392 | rc = next_entry(buf, fp, sizeof(u32)); | 401 | rc = next_entry(buf, fp, sizeof(u32)); |
| 393 | if (rc < 0) | 402 | if (rc) |
| 394 | return -1; | 403 | return rc; |
| 395 | 404 | ||
| 396 | node->cur_state = le32_to_cpu(buf[0]); | 405 | node->cur_state = le32_to_cpu(buf[0]); |
| 397 | 406 | ||
| 398 | len = 0; | 407 | len = 0; |
| 399 | rc = next_entry(buf, fp, sizeof(u32)); | 408 | rc = next_entry(buf, fp, sizeof(u32)); |
| 400 | if (rc < 0) | 409 | if (rc) |
| 401 | return -1; | 410 | return rc; |
| 402 | 411 | ||
| 403 | /* expr */ | 412 | /* expr */ |
| 404 | len = le32_to_cpu(buf[0]); | 413 | len = le32_to_cpu(buf[0]); |
| 405 | 414 | ||
| 406 | for (i = 0; i < len; i++) { | 415 | for (i = 0; i < len; i++) { |
| 407 | rc = next_entry(buf, fp, sizeof(u32) * 2); | 416 | rc = next_entry(buf, fp, sizeof(u32) * 2); |
| 408 | if (rc < 0) | 417 | if (rc) |
| 409 | goto err; | 418 | goto err; |
| 410 | 419 | ||
| 420 | rc = -ENOMEM; | ||
| 411 | expr = kzalloc(sizeof(struct cond_expr), GFP_KERNEL); | 421 | expr = kzalloc(sizeof(struct cond_expr), GFP_KERNEL); |
| 412 | if (!expr) | 422 | if (!expr) |
| 413 | goto err; | 423 | goto err; |
| @@ -416,6 +426,7 @@ static int cond_read_node(struct policydb *p, struct cond_node *node, void *fp) | |||
| 416 | expr->bool = le32_to_cpu(buf[1]); | 426 | expr->bool = le32_to_cpu(buf[1]); |
| 417 | 427 | ||
| 418 | if (!expr_isvalid(p, expr)) { | 428 | if (!expr_isvalid(p, expr)) { |
| 429 | rc = -EINVAL; | ||
| 419 | kfree(expr); | 430 | kfree(expr); |
| 420 | goto err; | 431 | goto err; |
| 421 | } | 432 | } |
| @@ -427,14 +438,16 @@ static int cond_read_node(struct policydb *p, struct cond_node *node, void *fp) | |||
| 427 | last = expr; | 438 | last = expr; |
| 428 | } | 439 | } |
| 429 | 440 | ||
| 430 | if (cond_read_av_list(p, fp, &node->true_list, NULL) != 0) | 441 | rc = cond_read_av_list(p, fp, &node->true_list, NULL); |
| 442 | if (rc) | ||
| 431 | goto err; | 443 | goto err; |
| 432 | if (cond_read_av_list(p, fp, &node->false_list, node->true_list) != 0) | 444 | rc = cond_read_av_list(p, fp, &node->false_list, node->true_list); |
| 445 | if (rc) | ||
| 433 | goto err; | 446 | goto err; |
| 434 | return 0; | 447 | return 0; |
| 435 | err: | 448 | err: |
| 436 | cond_node_destroy(node); | 449 | cond_node_destroy(node); |
| 437 | return -1; | 450 | return rc; |
| 438 | } | 451 | } |
| 439 | 452 | ||
| 440 | int cond_read_list(struct policydb *p, void *fp) | 453 | int cond_read_list(struct policydb *p, void *fp) |
| @@ -445,8 +458,8 @@ int cond_read_list(struct policydb *p, void *fp) | |||
| 445 | int rc; | 458 | int rc; |
| 446 | 459 | ||
| 447 | rc = next_entry(buf, fp, sizeof buf); | 460 | rc = next_entry(buf, fp, sizeof buf); |
| 448 | if (rc < 0) | 461 | if (rc) |
| 449 | return -1; | 462 | return rc; |
| 450 | 463 | ||
| 451 | len = le32_to_cpu(buf[0]); | 464 | len = le32_to_cpu(buf[0]); |
| 452 | 465 | ||
| @@ -455,11 +468,13 @@ int cond_read_list(struct policydb *p, void *fp) | |||
| 455 | goto err; | 468 | goto err; |
| 456 | 469 | ||
| 457 | for (i = 0; i < len; i++) { | 470 | for (i = 0; i < len; i++) { |
| 471 | rc = -ENOMEM; | ||
| 458 | node = kzalloc(sizeof(struct cond_node), GFP_KERNEL); | 472 | node = kzalloc(sizeof(struct cond_node), GFP_KERNEL); |
| 459 | if (!node) | 473 | if (!node) |
| 460 | goto err; | 474 | goto err; |
| 461 | 475 | ||
| 462 | if (cond_read_node(p, node, fp) != 0) | 476 | rc = cond_read_node(p, node, fp); |
| 477 | if (rc) | ||
| 463 | goto err; | 478 | goto err; |
| 464 | 479 | ||
| 465 | if (i == 0) | 480 | if (i == 0) |
| @@ -472,7 +487,7 @@ int cond_read_list(struct policydb *p, void *fp) | |||
| 472 | err: | 487 | err: |
| 473 | cond_list_destroy(p->cond_list); | 488 | cond_list_destroy(p->cond_list); |
| 474 | p->cond_list = NULL; | 489 | p->cond_list = NULL; |
| 475 | return -1; | 490 | return rc; |
| 476 | } | 491 | } |
| 477 | 492 | ||
| 478 | /* Determine whether additional permissions are granted by the conditional | 493 | /* Determine whether additional permissions are granted by the conditional |
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index c57802a164d5..3a29704be8ce 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c | |||
| @@ -31,6 +31,7 @@ | |||
| 31 | #include <linux/string.h> | 31 | #include <linux/string.h> |
| 32 | #include <linux/errno.h> | 32 | #include <linux/errno.h> |
| 33 | #include <linux/audit.h> | 33 | #include <linux/audit.h> |
| 34 | #include <linux/flex_array.h> | ||
| 34 | #include "security.h" | 35 | #include "security.h" |
| 35 | 36 | ||
| 36 | #include "policydb.h" | 37 | #include "policydb.h" |
| @@ -655,6 +656,9 @@ static int range_tr_destroy(void *key, void *datum, void *p) | |||
| 655 | 656 | ||
| 656 | static void ocontext_destroy(struct ocontext *c, int i) | 657 | static void ocontext_destroy(struct ocontext *c, int i) |
| 657 | { | 658 | { |
| 659 | if (!c) | ||
| 660 | return; | ||
| 661 | |||
| 658 | context_destroy(&c->context[0]); | 662 | context_destroy(&c->context[0]); |
| 659 | context_destroy(&c->context[1]); | 663 | context_destroy(&c->context[1]); |
| 660 | if (i == OCON_ISID || i == OCON_FS || | 664 | if (i == OCON_ISID || i == OCON_FS || |
| @@ -736,11 +740,17 @@ void policydb_destroy(struct policydb *p) | |||
| 736 | hashtab_map(p->range_tr, range_tr_destroy, NULL); | 740 | hashtab_map(p->range_tr, range_tr_destroy, NULL); |
| 737 | hashtab_destroy(p->range_tr); | 741 | hashtab_destroy(p->range_tr); |
| 738 | 742 | ||
| 739 | if (p->type_attr_map) { | 743 | if (p->type_attr_map_array) { |
| 740 | for (i = 0; i < p->p_types.nprim; i++) | 744 | for (i = 0; i < p->p_types.nprim; i++) { |
| 741 | ebitmap_destroy(&p->type_attr_map[i]); | 745 | struct ebitmap *e; |
| 746 | |||
| 747 | e = flex_array_get(p->type_attr_map_array, i); | ||
| 748 | if (!e) | ||
| 749 | continue; | ||
| 750 | ebitmap_destroy(e); | ||
| 751 | } | ||
| 752 | flex_array_free(p->type_attr_map_array); | ||
| 742 | } | 753 | } |
| 743 | kfree(p->type_attr_map); | ||
| 744 | ebitmap_destroy(&p->policycaps); | 754 | ebitmap_destroy(&p->policycaps); |
| 745 | ebitmap_destroy(&p->permissive_map); | 755 | ebitmap_destroy(&p->permissive_map); |
| 746 | 756 | ||
| @@ -1701,6 +1711,333 @@ u32 string_to_av_perm(struct policydb *p, u16 tclass, const char *name) | |||
| 1701 | return 1U << (perdatum->value-1); | 1711 | return 1U << (perdatum->value-1); |
| 1702 | } | 1712 | } |
| 1703 | 1713 | ||
| 1714 | static int range_read(struct policydb *p, void *fp) | ||
| 1715 | { | ||
| 1716 | struct range_trans *rt = NULL; | ||
| 1717 | struct mls_range *r = NULL; | ||
| 1718 | int i, rc; | ||
| 1719 | __le32 buf[2]; | ||
| 1720 | u32 nel; | ||
| 1721 | |||
| 1722 | if (p->policyvers < POLICYDB_VERSION_MLS) | ||
| 1723 | return 0; | ||
| 1724 | |||
| 1725 | rc = next_entry(buf, fp, sizeof(u32)); | ||
| 1726 | if (rc) | ||
| 1727 | goto out; | ||
| 1728 | |||
| 1729 | nel = le32_to_cpu(buf[0]); | ||
| 1730 | for (i = 0; i < nel; i++) { | ||
| 1731 | rc = -ENOMEM; | ||
| 1732 | rt = kzalloc(sizeof(*rt), GFP_KERNEL); | ||
| 1733 | if (!rt) | ||
| 1734 | goto out; | ||
| 1735 | |||
| 1736 | rc = next_entry(buf, fp, (sizeof(u32) * 2)); | ||
| 1737 | if (rc) | ||
| 1738 | goto out; | ||
| 1739 | |||
| 1740 | rt->source_type = le32_to_cpu(buf[0]); | ||
| 1741 | rt->target_type = le32_to_cpu(buf[1]); | ||
| 1742 | if (p->policyvers >= POLICYDB_VERSION_RANGETRANS) { | ||
| 1743 | rc = next_entry(buf, fp, sizeof(u32)); | ||
| 1744 | if (rc) | ||
| 1745 | goto out; | ||
| 1746 | rt->target_class = le32_to_cpu(buf[0]); | ||
| 1747 | } else | ||
| 1748 | rt->target_class = p->process_class; | ||
| 1749 | |||
| 1750 | rc = -EINVAL; | ||
| 1751 | if (!policydb_type_isvalid(p, rt->source_type) || | ||
| 1752 | !policydb_type_isvalid(p, rt->target_type) || | ||
| 1753 | !policydb_class_isvalid(p, rt->target_class)) | ||
| 1754 | goto out; | ||
| 1755 | |||
| 1756 | rc = -ENOMEM; | ||
| 1757 | r = kzalloc(sizeof(*r), GFP_KERNEL); | ||
| 1758 | if (!r) | ||
| 1759 | goto out; | ||
| 1760 | |||
| 1761 | rc = mls_read_range_helper(r, fp); | ||
| 1762 | if (rc) | ||
| 1763 | goto out; | ||
| 1764 | |||
| 1765 | rc = -EINVAL; | ||
| 1766 | if (!mls_range_isvalid(p, r)) { | ||
| 1767 | printk(KERN_WARNING "SELinux: rangetrans: invalid range\n"); | ||
| 1768 | goto out; | ||
| 1769 | } | ||
| 1770 | |||
| 1771 | rc = hashtab_insert(p->range_tr, rt, r); | ||
| 1772 | if (rc) | ||
| 1773 | goto out; | ||
| 1774 | |||
| 1775 | rt = NULL; | ||
| 1776 | r = NULL; | ||
| 1777 | } | ||
| 1778 | rangetr_hash_eval(p->range_tr); | ||
| 1779 | rc = 0; | ||
| 1780 | out: | ||
| 1781 | kfree(rt); | ||
| 1782 | kfree(r); | ||
| 1783 | return rc; | ||
| 1784 | } | ||
| 1785 | |||
| 1786 | static int genfs_read(struct policydb *p, void *fp) | ||
| 1787 | { | ||
| 1788 | int i, j, rc; | ||
| 1789 | u32 nel, nel2, len, len2; | ||
| 1790 | __le32 buf[1]; | ||
| 1791 | struct ocontext *l, *c; | ||
| 1792 | struct ocontext *newc = NULL; | ||
| 1793 | struct genfs *genfs_p, *genfs; | ||
| 1794 | struct genfs *newgenfs = NULL; | ||
| 1795 | |||
| 1796 | rc = next_entry(buf, fp, sizeof(u32)); | ||
| 1797 | if (rc) | ||
| 1798 | goto out; | ||
| 1799 | nel = le32_to_cpu(buf[0]); | ||
| 1800 | |||
| 1801 | for (i = 0; i < nel; i++) { | ||
| 1802 | rc = next_entry(buf, fp, sizeof(u32)); | ||
| 1803 | if (rc) | ||
| 1804 | goto out; | ||
| 1805 | len = le32_to_cpu(buf[0]); | ||
| 1806 | |||
| 1807 | rc = -ENOMEM; | ||
| 1808 | newgenfs = kzalloc(sizeof(*newgenfs), GFP_KERNEL); | ||
| 1809 | if (!newgenfs) | ||
| 1810 | goto out; | ||
| 1811 | |||
| 1812 | rc = -ENOMEM; | ||
| 1813 | newgenfs->fstype = kmalloc(len + 1, GFP_KERNEL); | ||
| 1814 | if (!newgenfs->fstype) | ||
| 1815 | goto out; | ||
| 1816 | |||
| 1817 | rc = next_entry(newgenfs->fstype, fp, len); | ||
| 1818 | if (rc) | ||
| 1819 | goto out; | ||
| 1820 | |||
| 1821 | newgenfs->fstype[len] = 0; | ||
| 1822 | |||
| 1823 | for (genfs_p = NULL, genfs = p->genfs; genfs; | ||
| 1824 | genfs_p = genfs, genfs = genfs->next) { | ||
| 1825 | rc = -EINVAL; | ||
| 1826 | if (strcmp(newgenfs->fstype, genfs->fstype) == 0) { | ||
| 1827 | printk(KERN_ERR "SELinux: dup genfs fstype %s\n", | ||
| 1828 | newgenfs->fstype); | ||
| 1829 | goto out; | ||
| 1830 | } | ||
| 1831 | if (strcmp(newgenfs->fstype, genfs->fstype) < 0) | ||
| 1832 | break; | ||
| 1833 | } | ||
| 1834 | newgenfs->next = genfs; | ||
| 1835 | if (genfs_p) | ||
| 1836 | genfs_p->next = newgenfs; | ||
| 1837 | else | ||
| 1838 | p->genfs = newgenfs; | ||
| 1839 | genfs = newgenfs; | ||
| 1840 | newgenfs = NULL; | ||
| 1841 | |||
| 1842 | rc = next_entry(buf, fp, sizeof(u32)); | ||
| 1843 | if (rc) | ||
| 1844 | goto out; | ||
| 1845 | |||
| 1846 | nel2 = le32_to_cpu(buf[0]); | ||
| 1847 | for (j = 0; j < nel2; j++) { | ||
| 1848 | rc = next_entry(buf, fp, sizeof(u32)); | ||
| 1849 | if (rc) | ||
| 1850 | goto out; | ||
| 1851 | len = le32_to_cpu(buf[0]); | ||
| 1852 | |||
| 1853 | rc = -ENOMEM; | ||
| 1854 | newc = kzalloc(sizeof(*newc), GFP_KERNEL); | ||
| 1855 | if (!newc) | ||
| 1856 | goto out; | ||
| 1857 | |||
| 1858 | rc = -ENOMEM; | ||
| 1859 | newc->u.name = kmalloc(len + 1, GFP_KERNEL); | ||
| 1860 | if (!newc->u.name) | ||
| 1861 | goto out; | ||
| 1862 | |||
| 1863 | rc = next_entry(newc->u.name, fp, len); | ||
| 1864 | if (rc) | ||
| 1865 | goto out; | ||
| 1866 | newc->u.name[len] = 0; | ||
| 1867 | |||
| 1868 | rc = next_entry(buf, fp, sizeof(u32)); | ||
| 1869 | if (rc) | ||
| 1870 | goto out; | ||
| 1871 | |||
| 1872 | newc->v.sclass = le32_to_cpu(buf[0]); | ||
| 1873 | rc = context_read_and_validate(&newc->context[0], p, fp); | ||
| 1874 | if (rc) | ||
| 1875 | goto out; | ||
| 1876 | |||
| 1877 | for (l = NULL, c = genfs->head; c; | ||
| 1878 | l = c, c = c->next) { | ||
| 1879 | rc = -EINVAL; | ||
| 1880 | if (!strcmp(newc->u.name, c->u.name) && | ||
| 1881 | (!c->v.sclass || !newc->v.sclass || | ||
| 1882 | newc->v.sclass == c->v.sclass)) { | ||
| 1883 | printk(KERN_ERR "SELinux: dup genfs entry (%s,%s)\n", | ||
| 1884 | genfs->fstype, c->u.name); | ||
| 1885 | goto out; | ||
| 1886 | } | ||
| 1887 | len = strlen(newc->u.name); | ||
| 1888 | len2 = strlen(c->u.name); | ||
| 1889 | if (len > len2) | ||
| 1890 | break; | ||
| 1891 | } | ||
| 1892 | |||
| 1893 | newc->next = c; | ||
| 1894 | if (l) | ||
| 1895 | l->next = newc; | ||
| 1896 | else | ||
| 1897 | genfs->head = newc; | ||
| 1898 | newc = NULL; | ||
| 1899 | } | ||
| 1900 | } | ||
| 1901 | rc = 0; | ||
| 1902 | out: | ||
| 1903 | if (newgenfs) | ||
| 1904 | kfree(newgenfs->fstype); | ||
| 1905 | kfree(newgenfs); | ||
| 1906 | ocontext_destroy(newc, OCON_FSUSE); | ||
| 1907 | |||
| 1908 | return rc; | ||
| 1909 | } | ||
| 1910 | |||
| 1911 | static int ocontext_read(struct policydb *p, struct policydb_compat_info *info, | ||
| 1912 | void *fp) | ||
| 1913 | { | ||
| 1914 | int i, j, rc; | ||
| 1915 | u32 nel, len; | ||
| 1916 | __le32 buf[3]; | ||
| 1917 | struct ocontext *l, *c; | ||
| 1918 | u32 nodebuf[8]; | ||
| 1919 | |||
| 1920 | for (i = 0; i < info->ocon_num; i++) { | ||
| 1921 | rc = next_entry(buf, fp, sizeof(u32)); | ||
| 1922 | if (rc) | ||
| 1923 | goto out; | ||
| 1924 | nel = le32_to_cpu(buf[0]); | ||
| 1925 | |||
| 1926 | l = NULL; | ||
| 1927 | for (j = 0; j < nel; j++) { | ||
| 1928 | rc = -ENOMEM; | ||
| 1929 | c = kzalloc(sizeof(*c), GFP_KERNEL); | ||
| 1930 | if (!c) | ||
| 1931 | goto out; | ||
| 1932 | if (l) | ||
| 1933 | l->next = c; | ||
| 1934 | else | ||
| 1935 | p->ocontexts[i] = c; | ||
| 1936 | l = c; | ||
| 1937 | |||
| 1938 | switch (i) { | ||
| 1939 | case OCON_ISID: | ||
| 1940 | rc = next_entry(buf, fp, sizeof(u32)); | ||
| 1941 | if (rc) | ||
| 1942 | goto out; | ||
| 1943 | |||
| 1944 | c->sid[0] = le32_to_cpu(buf[0]); | ||
| 1945 | rc = context_read_and_validate(&c->context[0], p, fp); | ||
| 1946 | if (rc) | ||
| 1947 | goto out; | ||
| 1948 | break; | ||
| 1949 | case OCON_FS: | ||
| 1950 | case OCON_NETIF: | ||
| 1951 | rc = next_entry(buf, fp, sizeof(u32)); | ||
| 1952 | if (rc) | ||
| 1953 | goto out; | ||
| 1954 | len = le32_to_cpu(buf[0]); | ||
| 1955 | |||
| 1956 | rc = -ENOMEM; | ||
| 1957 | c->u.name = kmalloc(len + 1, GFP_KERNEL); | ||
| 1958 | if (!c->u.name) | ||
| 1959 | goto out; | ||
| 1960 | |||
| 1961 | rc = next_entry(c->u.name, fp, len); | ||
| 1962 | if (rc) | ||
| 1963 | goto out; | ||
| 1964 | |||
| 1965 | c->u.name[len] = 0; | ||
| 1966 | rc = context_read_and_validate(&c->context[0], p, fp); | ||
| 1967 | if (rc) | ||
| 1968 | goto out; | ||
| 1969 | rc = context_read_and_validate(&c->context[1], p, fp); | ||
| 1970 | if (rc) | ||
| 1971 | goto out; | ||
| 1972 | break; | ||
| 1973 | case OCON_PORT: | ||
| 1974 | rc = next_entry(buf, fp, sizeof(u32)*3); | ||
| 1975 | if (rc) | ||
| 1976 | goto out; | ||
| 1977 | c->u.port.protocol = le32_to_cpu(buf[0]); | ||
| 1978 | c->u.port.low_port = le32_to_cpu(buf[1]); | ||
| 1979 | c->u.port.high_port = le32_to_cpu(buf[2]); | ||
| 1980 | rc = context_read_and_validate(&c->context[0], p, fp); | ||
| 1981 | if (rc) | ||
| 1982 | goto out; | ||
| 1983 | break; | ||
| 1984 | case OCON_NODE: | ||
| 1985 | rc = next_entry(nodebuf, fp, sizeof(u32) * 2); | ||
| 1986 | if (rc) | ||
| 1987 | goto out; | ||
| 1988 | c->u.node.addr = nodebuf[0]; /* network order */ | ||
| 1989 | c->u.node.mask = nodebuf[1]; /* network order */ | ||
| 1990 | rc = context_read_and_validate(&c->context[0], p, fp); | ||
| 1991 | if (rc) | ||
| 1992 | goto out; | ||
| 1993 | break; | ||
| 1994 | case OCON_FSUSE: | ||
| 1995 | rc = next_entry(buf, fp, sizeof(u32)*2); | ||
| 1996 | if (rc) | ||
| 1997 | goto out; | ||
| 1998 | |||
| 1999 | rc = -EINVAL; | ||
| 2000 | c->v.behavior = le32_to_cpu(buf[0]); | ||
| 2001 | if (c->v.behavior > SECURITY_FS_USE_NONE) | ||
| 2002 | goto out; | ||
| 2003 | |||
| 2004 | rc = -ENOMEM; | ||
| 2005 | len = le32_to_cpu(buf[1]); | ||
| 2006 | c->u.name = kmalloc(len + 1, GFP_KERNEL); | ||
| 2007 | if (!c->u.name) | ||
| 2008 | goto out; | ||
| 2009 | |||
| 2010 | rc = next_entry(c->u.name, fp, len); | ||
| 2011 | if (rc) | ||
| 2012 | goto out; | ||
| 2013 | c->u.name[len] = 0; | ||
| 2014 | rc = context_read_and_validate(&c->context[0], p, fp); | ||
| 2015 | if (rc) | ||
| 2016 | goto out; | ||
| 2017 | break; | ||
| 2018 | case OCON_NODE6: { | ||
| 2019 | int k; | ||
| 2020 | |||
| 2021 | rc = next_entry(nodebuf, fp, sizeof(u32) * 8); | ||
| 2022 | if (rc) | ||
| 2023 | goto out; | ||
| 2024 | for (k = 0; k < 4; k++) | ||
| 2025 | c->u.node6.addr[k] = nodebuf[k]; | ||
| 2026 | for (k = 0; k < 4; k++) | ||
| 2027 | c->u.node6.mask[k] = nodebuf[k+4]; | ||
| 2028 | rc = context_read_and_validate(&c->context[0], p, fp); | ||
| 2029 | if (rc) | ||
| 2030 | goto out; | ||
| 2031 | break; | ||
| 2032 | } | ||
| 2033 | } | ||
| 2034 | } | ||
| 2035 | } | ||
| 2036 | rc = 0; | ||
| 2037 | out: | ||
| 2038 | return rc; | ||
| 2039 | } | ||
| 2040 | |||
| 1704 | /* | 2041 | /* |
| 1705 | * Read the configuration data from a policy database binary | 2042 | * Read the configuration data from a policy database binary |
| 1706 | * representation file into a policy database structure. | 2043 | * representation file into a policy database structure. |
| @@ -1709,16 +2046,12 @@ int policydb_read(struct policydb *p, void *fp) | |||
| 1709 | { | 2046 | { |
| 1710 | struct role_allow *ra, *lra; | 2047 | struct role_allow *ra, *lra; |
| 1711 | struct role_trans *tr, *ltr; | 2048 | struct role_trans *tr, *ltr; |
| 1712 | struct ocontext *l, *c, *newc; | ||
| 1713 | struct genfs *genfs_p, *genfs, *newgenfs; | ||
| 1714 | int i, j, rc; | 2049 | int i, j, rc; |
| 1715 | __le32 buf[4]; | 2050 | __le32 buf[4]; |
| 1716 | u32 nodebuf[8]; | 2051 | u32 len, nprim, nel; |
| 1717 | u32 len, len2, nprim, nel, nel2; | 2052 | |
| 1718 | char *policydb_str; | 2053 | char *policydb_str; |
| 1719 | struct policydb_compat_info *info; | 2054 | struct policydb_compat_info *info; |
| 1720 | struct range_trans *rt; | ||
| 1721 | struct mls_range *r; | ||
| 1722 | 2055 | ||
| 1723 | rc = policydb_init(p); | 2056 | rc = policydb_init(p); |
| 1724 | if (rc) | 2057 | if (rc) |
| @@ -1919,294 +2252,45 @@ int policydb_read(struct policydb *p, void *fp) | |||
| 1919 | if (!p->process_trans_perms) | 2252 | if (!p->process_trans_perms) |
| 1920 | goto bad; | 2253 | goto bad; |
| 1921 | 2254 | ||
| 1922 | for (i = 0; i < info->ocon_num; i++) { | 2255 | rc = ocontext_read(p, info, fp); |
| 1923 | rc = next_entry(buf, fp, sizeof(u32)); | 2256 | if (rc) |
| 1924 | if (rc < 0) | ||
| 1925 | goto bad; | ||
| 1926 | nel = le32_to_cpu(buf[0]); | ||
| 1927 | l = NULL; | ||
| 1928 | for (j = 0; j < nel; j++) { | ||
| 1929 | c = kzalloc(sizeof(*c), GFP_KERNEL); | ||
| 1930 | if (!c) { | ||
| 1931 | rc = -ENOMEM; | ||
| 1932 | goto bad; | ||
| 1933 | } | ||
| 1934 | if (l) | ||
| 1935 | l->next = c; | ||
| 1936 | else | ||
| 1937 | p->ocontexts[i] = c; | ||
| 1938 | l = c; | ||
| 1939 | rc = -EINVAL; | ||
| 1940 | switch (i) { | ||
| 1941 | case OCON_ISID: | ||
| 1942 | rc = next_entry(buf, fp, sizeof(u32)); | ||
| 1943 | if (rc < 0) | ||
| 1944 | goto bad; | ||
| 1945 | c->sid[0] = le32_to_cpu(buf[0]); | ||
| 1946 | rc = context_read_and_validate(&c->context[0], p, fp); | ||
| 1947 | if (rc) | ||
| 1948 | goto bad; | ||
| 1949 | break; | ||
| 1950 | case OCON_FS: | ||
| 1951 | case OCON_NETIF: | ||
| 1952 | rc = next_entry(buf, fp, sizeof(u32)); | ||
| 1953 | if (rc < 0) | ||
| 1954 | goto bad; | ||
| 1955 | len = le32_to_cpu(buf[0]); | ||
| 1956 | c->u.name = kmalloc(len + 1, GFP_KERNEL); | ||
| 1957 | if (!c->u.name) { | ||
| 1958 | rc = -ENOMEM; | ||
| 1959 | goto bad; | ||
| 1960 | } | ||
| 1961 | rc = next_entry(c->u.name, fp, len); | ||
| 1962 | if (rc < 0) | ||
| 1963 | goto bad; | ||
| 1964 | c->u.name[len] = 0; | ||
| 1965 | rc = context_read_and_validate(&c->context[0], p, fp); | ||
| 1966 | if (rc) | ||
| 1967 | goto bad; | ||
| 1968 | rc = context_read_and_validate(&c->context[1], p, fp); | ||
| 1969 | if (rc) | ||
| 1970 | goto bad; | ||
| 1971 | break; | ||
| 1972 | case OCON_PORT: | ||
| 1973 | rc = next_entry(buf, fp, sizeof(u32)*3); | ||
| 1974 | if (rc < 0) | ||
| 1975 | goto bad; | ||
| 1976 | c->u.port.protocol = le32_to_cpu(buf[0]); | ||
| 1977 | c->u.port.low_port = le32_to_cpu(buf[1]); | ||
| 1978 | c->u.port.high_port = le32_to_cpu(buf[2]); | ||
| 1979 | rc = context_read_and_validate(&c->context[0], p, fp); | ||
| 1980 | if (rc) | ||
| 1981 | goto bad; | ||
| 1982 | break; | ||
| 1983 | case OCON_NODE: | ||
| 1984 | rc = next_entry(nodebuf, fp, sizeof(u32) * 2); | ||
| 1985 | if (rc < 0) | ||
| 1986 | goto bad; | ||
| 1987 | c->u.node.addr = nodebuf[0]; /* network order */ | ||
| 1988 | c->u.node.mask = nodebuf[1]; /* network order */ | ||
| 1989 | rc = context_read_and_validate(&c->context[0], p, fp); | ||
| 1990 | if (rc) | ||
| 1991 | goto bad; | ||
| 1992 | break; | ||
| 1993 | case OCON_FSUSE: | ||
| 1994 | rc = next_entry(buf, fp, sizeof(u32)*2); | ||
| 1995 | if (rc < 0) | ||
| 1996 | goto bad; | ||
| 1997 | c->v.behavior = le32_to_cpu(buf[0]); | ||
| 1998 | if (c->v.behavior > SECURITY_FS_USE_NONE) | ||
| 1999 | goto bad; | ||
| 2000 | len = le32_to_cpu(buf[1]); | ||
| 2001 | c->u.name = kmalloc(len + 1, GFP_KERNEL); | ||
| 2002 | if (!c->u.name) { | ||
| 2003 | rc = -ENOMEM; | ||
| 2004 | goto bad; | ||
| 2005 | } | ||
| 2006 | rc = next_entry(c->u.name, fp, len); | ||
| 2007 | if (rc < 0) | ||
| 2008 | goto bad; | ||
| 2009 | c->u.name[len] = 0; | ||
| 2010 | rc = context_read_and_validate(&c->context[0], p, fp); | ||
| 2011 | if (rc) | ||
| 2012 | goto bad; | ||
| 2013 | break; | ||
| 2014 | case OCON_NODE6: { | ||
| 2015 | int k; | ||
| 2016 | |||
| 2017 | rc = next_entry(nodebuf, fp, sizeof(u32) * 8); | ||
| 2018 | if (rc < 0) | ||
| 2019 | goto bad; | ||
| 2020 | for (k = 0; k < 4; k++) | ||
| 2021 | c->u.node6.addr[k] = nodebuf[k]; | ||
| 2022 | for (k = 0; k < 4; k++) | ||
| 2023 | c->u.node6.mask[k] = nodebuf[k+4]; | ||
| 2024 | if (context_read_and_validate(&c->context[0], p, fp)) | ||
| 2025 | goto bad; | ||
| 2026 | break; | ||
| 2027 | } | ||
| 2028 | } | ||
| 2029 | } | ||
| 2030 | } | ||
| 2031 | |||
| 2032 | rc = next_entry(buf, fp, sizeof(u32)); | ||
| 2033 | if (rc < 0) | ||
| 2034 | goto bad; | 2257 | goto bad; |
| 2035 | nel = le32_to_cpu(buf[0]); | ||
| 2036 | genfs_p = NULL; | ||
| 2037 | rc = -EINVAL; | ||
| 2038 | for (i = 0; i < nel; i++) { | ||
| 2039 | rc = next_entry(buf, fp, sizeof(u32)); | ||
| 2040 | if (rc < 0) | ||
| 2041 | goto bad; | ||
| 2042 | len = le32_to_cpu(buf[0]); | ||
| 2043 | newgenfs = kzalloc(sizeof(*newgenfs), GFP_KERNEL); | ||
| 2044 | if (!newgenfs) { | ||
| 2045 | rc = -ENOMEM; | ||
| 2046 | goto bad; | ||
| 2047 | } | ||
| 2048 | 2258 | ||
| 2049 | newgenfs->fstype = kmalloc(len + 1, GFP_KERNEL); | 2259 | rc = genfs_read(p, fp); |
| 2050 | if (!newgenfs->fstype) { | 2260 | if (rc) |
| 2051 | rc = -ENOMEM; | 2261 | goto bad; |
| 2052 | kfree(newgenfs); | ||
| 2053 | goto bad; | ||
| 2054 | } | ||
| 2055 | rc = next_entry(newgenfs->fstype, fp, len); | ||
| 2056 | if (rc < 0) { | ||
| 2057 | kfree(newgenfs->fstype); | ||
| 2058 | kfree(newgenfs); | ||
| 2059 | goto bad; | ||
| 2060 | } | ||
| 2061 | newgenfs->fstype[len] = 0; | ||
| 2062 | for (genfs_p = NULL, genfs = p->genfs; genfs; | ||
| 2063 | genfs_p = genfs, genfs = genfs->next) { | ||
| 2064 | if (strcmp(newgenfs->fstype, genfs->fstype) == 0) { | ||
| 2065 | printk(KERN_ERR "SELinux: dup genfs " | ||
| 2066 | "fstype %s\n", newgenfs->fstype); | ||
| 2067 | kfree(newgenfs->fstype); | ||
| 2068 | kfree(newgenfs); | ||
| 2069 | goto bad; | ||
| 2070 | } | ||
| 2071 | if (strcmp(newgenfs->fstype, genfs->fstype) < 0) | ||
| 2072 | break; | ||
| 2073 | } | ||
| 2074 | newgenfs->next = genfs; | ||
| 2075 | if (genfs_p) | ||
| 2076 | genfs_p->next = newgenfs; | ||
| 2077 | else | ||
| 2078 | p->genfs = newgenfs; | ||
| 2079 | rc = next_entry(buf, fp, sizeof(u32)); | ||
| 2080 | if (rc < 0) | ||
| 2081 | goto bad; | ||
| 2082 | nel2 = le32_to_cpu(buf[0]); | ||
| 2083 | for (j = 0; j < nel2; j++) { | ||
| 2084 | rc = next_entry(buf, fp, sizeof(u32)); | ||
| 2085 | if (rc < 0) | ||
| 2086 | goto bad; | ||
| 2087 | len = le32_to_cpu(buf[0]); | ||
| 2088 | |||
| 2089 | newc = kzalloc(sizeof(*newc), GFP_KERNEL); | ||
| 2090 | if (!newc) { | ||
| 2091 | rc = -ENOMEM; | ||
| 2092 | goto bad; | ||
| 2093 | } | ||
| 2094 | |||
| 2095 | newc->u.name = kmalloc(len + 1, GFP_KERNEL); | ||
| 2096 | if (!newc->u.name) { | ||
| 2097 | rc = -ENOMEM; | ||
| 2098 | goto bad_newc; | ||
| 2099 | } | ||
| 2100 | rc = next_entry(newc->u.name, fp, len); | ||
| 2101 | if (rc < 0) | ||
| 2102 | goto bad_newc; | ||
| 2103 | newc->u.name[len] = 0; | ||
| 2104 | rc = next_entry(buf, fp, sizeof(u32)); | ||
| 2105 | if (rc < 0) | ||
| 2106 | goto bad_newc; | ||
| 2107 | newc->v.sclass = le32_to_cpu(buf[0]); | ||
| 2108 | if (context_read_and_validate(&newc->context[0], p, fp)) | ||
| 2109 | goto bad_newc; | ||
| 2110 | for (l = NULL, c = newgenfs->head; c; | ||
| 2111 | l = c, c = c->next) { | ||
| 2112 | if (!strcmp(newc->u.name, c->u.name) && | ||
| 2113 | (!c->v.sclass || !newc->v.sclass || | ||
| 2114 | newc->v.sclass == c->v.sclass)) { | ||
| 2115 | printk(KERN_ERR "SELinux: dup genfs " | ||
| 2116 | "entry (%s,%s)\n", | ||
| 2117 | newgenfs->fstype, c->u.name); | ||
| 2118 | goto bad_newc; | ||
| 2119 | } | ||
| 2120 | len = strlen(newc->u.name); | ||
| 2121 | len2 = strlen(c->u.name); | ||
| 2122 | if (len > len2) | ||
| 2123 | break; | ||
| 2124 | } | ||
| 2125 | 2262 | ||
| 2126 | newc->next = c; | 2263 | rc = range_read(p, fp); |
| 2127 | if (l) | 2264 | if (rc) |
| 2128 | l->next = newc; | 2265 | goto bad; |
| 2129 | else | ||
| 2130 | newgenfs->head = newc; | ||
| 2131 | } | ||
| 2132 | } | ||
| 2133 | 2266 | ||
| 2134 | if (p->policyvers >= POLICYDB_VERSION_MLS) { | 2267 | rc = -ENOMEM; |
| 2135 | int new_rangetr = p->policyvers >= POLICYDB_VERSION_RANGETRANS; | 2268 | p->type_attr_map_array = flex_array_alloc(sizeof(struct ebitmap), |
| 2136 | rc = next_entry(buf, fp, sizeof(u32)); | 2269 | p->p_types.nprim, |
| 2137 | if (rc < 0) | 2270 | GFP_KERNEL | __GFP_ZERO); |
| 2138 | goto bad; | 2271 | if (!p->type_attr_map_array) |
| 2139 | nel = le32_to_cpu(buf[0]); | 2272 | goto bad; |
| 2140 | for (i = 0; i < nel; i++) { | ||
| 2141 | rt = kzalloc(sizeof(*rt), GFP_KERNEL); | ||
| 2142 | if (!rt) { | ||
| 2143 | rc = -ENOMEM; | ||
| 2144 | goto bad; | ||
| 2145 | } | ||
| 2146 | rc = next_entry(buf, fp, (sizeof(u32) * 2)); | ||
| 2147 | if (rc < 0) { | ||
| 2148 | kfree(rt); | ||
| 2149 | goto bad; | ||
| 2150 | } | ||
| 2151 | rt->source_type = le32_to_cpu(buf[0]); | ||
| 2152 | rt->target_type = le32_to_cpu(buf[1]); | ||
| 2153 | if (new_rangetr) { | ||
| 2154 | rc = next_entry(buf, fp, sizeof(u32)); | ||
| 2155 | if (rc < 0) { | ||
| 2156 | kfree(rt); | ||
| 2157 | goto bad; | ||
| 2158 | } | ||
| 2159 | rt->target_class = le32_to_cpu(buf[0]); | ||
| 2160 | } else | ||
| 2161 | rt->target_class = p->process_class; | ||
| 2162 | if (!policydb_type_isvalid(p, rt->source_type) || | ||
| 2163 | !policydb_type_isvalid(p, rt->target_type) || | ||
| 2164 | !policydb_class_isvalid(p, rt->target_class)) { | ||
| 2165 | kfree(rt); | ||
| 2166 | rc = -EINVAL; | ||
| 2167 | goto bad; | ||
| 2168 | } | ||
| 2169 | r = kzalloc(sizeof(*r), GFP_KERNEL); | ||
| 2170 | if (!r) { | ||
| 2171 | kfree(rt); | ||
| 2172 | rc = -ENOMEM; | ||
| 2173 | goto bad; | ||
| 2174 | } | ||
| 2175 | rc = mls_read_range_helper(r, fp); | ||
| 2176 | if (rc) { | ||
| 2177 | kfree(rt); | ||
| 2178 | kfree(r); | ||
| 2179 | goto bad; | ||
| 2180 | } | ||
| 2181 | if (!mls_range_isvalid(p, r)) { | ||
| 2182 | printk(KERN_WARNING "SELinux: rangetrans: invalid range\n"); | ||
| 2183 | kfree(rt); | ||
| 2184 | kfree(r); | ||
| 2185 | goto bad; | ||
| 2186 | } | ||
| 2187 | rc = hashtab_insert(p->range_tr, rt, r); | ||
| 2188 | if (rc) { | ||
| 2189 | kfree(rt); | ||
| 2190 | kfree(r); | ||
| 2191 | goto bad; | ||
| 2192 | } | ||
| 2193 | } | ||
| 2194 | rangetr_hash_eval(p->range_tr); | ||
| 2195 | } | ||
| 2196 | 2273 | ||
| 2197 | p->type_attr_map = kmalloc(p->p_types.nprim * sizeof(struct ebitmap), GFP_KERNEL); | 2274 | /* preallocate so we don't have to worry about the put ever failing */ |
| 2198 | if (!p->type_attr_map) | 2275 | rc = flex_array_prealloc(p->type_attr_map_array, 0, p->p_types.nprim - 1, |
| 2276 | GFP_KERNEL | __GFP_ZERO); | ||
| 2277 | if (rc) | ||
| 2199 | goto bad; | 2278 | goto bad; |
| 2200 | 2279 | ||
| 2201 | for (i = 0; i < p->p_types.nprim; i++) { | 2280 | for (i = 0; i < p->p_types.nprim; i++) { |
| 2202 | ebitmap_init(&p->type_attr_map[i]); | 2281 | struct ebitmap *e = flex_array_get(p->type_attr_map_array, i); |
| 2282 | |||
| 2283 | BUG_ON(!e); | ||
| 2284 | ebitmap_init(e); | ||
| 2203 | if (p->policyvers >= POLICYDB_VERSION_AVTAB) { | 2285 | if (p->policyvers >= POLICYDB_VERSION_AVTAB) { |
| 2204 | if (ebitmap_read(&p->type_attr_map[i], fp)) | 2286 | rc = ebitmap_read(e, fp); |
| 2287 | if (rc) | ||
| 2205 | goto bad; | 2288 | goto bad; |
| 2206 | } | 2289 | } |
| 2207 | /* add the type itself as the degenerate case */ | 2290 | /* add the type itself as the degenerate case */ |
| 2208 | if (ebitmap_set_bit(&p->type_attr_map[i], i, 1)) | 2291 | rc = ebitmap_set_bit(e, i, 1); |
| 2209 | goto bad; | 2292 | if (rc) |
| 2293 | goto bad; | ||
| 2210 | } | 2294 | } |
| 2211 | 2295 | ||
| 2212 | rc = policydb_bounds_sanity_check(p); | 2296 | rc = policydb_bounds_sanity_check(p); |
| @@ -2216,8 +2300,6 @@ int policydb_read(struct policydb *p, void *fp) | |||
| 2216 | rc = 0; | 2300 | rc = 0; |
| 2217 | out: | 2301 | out: |
| 2218 | return rc; | 2302 | return rc; |
| 2219 | bad_newc: | ||
| 2220 | ocontext_destroy(newc, OCON_FSUSE); | ||
| 2221 | bad: | 2303 | bad: |
| 2222 | if (!rc) | 2304 | if (!rc) |
| 2223 | rc = -EINVAL; | 2305 | rc = -EINVAL; |
diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h index 26d9adf8542b..310e94442cb8 100644 --- a/security/selinux/ss/policydb.h +++ b/security/selinux/ss/policydb.h | |||
| @@ -24,6 +24,8 @@ | |||
| 24 | #ifndef _SS_POLICYDB_H_ | 24 | #ifndef _SS_POLICYDB_H_ |
| 25 | #define _SS_POLICYDB_H_ | 25 | #define _SS_POLICYDB_H_ |
| 26 | 26 | ||
| 27 | #include <linux/flex_array.h> | ||
| 28 | |||
| 27 | #include "symtab.h" | 29 | #include "symtab.h" |
| 28 | #include "avtab.h" | 30 | #include "avtab.h" |
| 29 | #include "sidtab.h" | 31 | #include "sidtab.h" |
| @@ -246,7 +248,7 @@ struct policydb { | |||
| 246 | struct hashtab *range_tr; | 248 | struct hashtab *range_tr; |
| 247 | 249 | ||
| 248 | /* type -> attribute reverse mapping */ | 250 | /* type -> attribute reverse mapping */ |
| 249 | struct ebitmap *type_attr_map; | 251 | struct flex_array *type_attr_map_array; |
| 250 | 252 | ||
| 251 | struct ebitmap policycaps; | 253 | struct ebitmap policycaps; |
| 252 | 254 | ||
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 1de60ce90d9a..9ea2feca3cd4 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c | |||
| @@ -50,6 +50,7 @@ | |||
| 50 | #include <linux/audit.h> | 50 | #include <linux/audit.h> |
| 51 | #include <linux/mutex.h> | 51 | #include <linux/mutex.h> |
| 52 | #include <linux/selinux.h> | 52 | #include <linux/selinux.h> |
| 53 | #include <linux/flex_array.h> | ||
| 53 | #include <net/netlabel.h> | 54 | #include <net/netlabel.h> |
| 54 | 55 | ||
| 55 | #include "flask.h" | 56 | #include "flask.h" |
| @@ -626,8 +627,10 @@ static void context_struct_compute_av(struct context *scontext, | |||
| 626 | */ | 627 | */ |
| 627 | avkey.target_class = tclass; | 628 | avkey.target_class = tclass; |
| 628 | avkey.specified = AVTAB_AV; | 629 | avkey.specified = AVTAB_AV; |
| 629 | sattr = &policydb.type_attr_map[scontext->type - 1]; | 630 | sattr = flex_array_get(policydb.type_attr_map_array, scontext->type - 1); |
| 630 | tattr = &policydb.type_attr_map[tcontext->type - 1]; | 631 | BUG_ON(!sattr); |
| 632 | tattr = flex_array_get(policydb.type_attr_map_array, tcontext->type - 1); | ||
| 633 | BUG_ON(!tattr); | ||
| 631 | ebitmap_for_each_positive_bit(sattr, snode, i) { | 634 | ebitmap_for_each_positive_bit(sattr, snode, i) { |
| 632 | ebitmap_for_each_positive_bit(tattr, tnode, j) { | 635 | ebitmap_for_each_positive_bit(tattr, tnode, j) { |
| 633 | avkey.source_type = i + 1; | 636 | avkey.source_type = i + 1; |
diff --git a/security/selinux/ss/symtab.c b/security/selinux/ss/symtab.c index bcf9f620426e..160326ee99e5 100644 --- a/security/selinux/ss/symtab.c +++ b/security/selinux/ss/symtab.c | |||
| @@ -36,7 +36,7 @@ int symtab_init(struct symtab *s, unsigned int size) | |||
| 36 | { | 36 | { |
| 37 | s->table = hashtab_create(symhash, symcmp, size); | 37 | s->table = hashtab_create(symhash, symcmp, size); |
| 38 | if (!s->table) | 38 | if (!s->table) |
| 39 | return -1; | 39 | return -ENOMEM; |
| 40 | s->nprim = 0; | 40 | s->nprim = 0; |
| 41 | return 0; | 41 | return 0; |
| 42 | } | 42 | } |
diff --git a/security/smack/smack.h b/security/smack/smack.h index c6e9acae72e4..43ae747a5aa4 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h | |||
| @@ -123,16 +123,6 @@ struct smack_known { | |||
| 123 | #define SMK_FSHAT "smackfshat=" | 123 | #define SMK_FSHAT "smackfshat=" |
| 124 | #define SMK_FSROOT "smackfsroot=" | 124 | #define SMK_FSROOT "smackfsroot=" |
| 125 | 125 | ||
| 126 | /* | ||
| 127 | * xattr names | ||
| 128 | */ | ||
| 129 | #define XATTR_SMACK_SUFFIX "SMACK64" | ||
| 130 | #define XATTR_SMACK_IPIN "SMACK64IPIN" | ||
| 131 | #define XATTR_SMACK_IPOUT "SMACK64IPOUT" | ||
| 132 | #define XATTR_NAME_SMACK XATTR_SECURITY_PREFIX XATTR_SMACK_SUFFIX | ||
| 133 | #define XATTR_NAME_SMACKIPIN XATTR_SECURITY_PREFIX XATTR_SMACK_IPIN | ||
| 134 | #define XATTR_NAME_SMACKIPOUT XATTR_SECURITY_PREFIX XATTR_SMACK_IPOUT | ||
| 135 | |||
| 136 | #define SMACK_CIPSO_OPTION "-CIPSO" | 126 | #define SMACK_CIPSO_OPTION "-CIPSO" |
| 137 | 127 | ||
| 138 | /* | 128 | /* |
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 0f2fc480fc61..9192ba366a4c 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c | |||
| @@ -598,6 +598,8 @@ static int smack_inode_rename(struct inode *old_inode, | |||
| 598 | static int smack_inode_permission(struct inode *inode, int mask) | 598 | static int smack_inode_permission(struct inode *inode, int mask) |
| 599 | { | 599 | { |
| 600 | struct smk_audit_info ad; | 600 | struct smk_audit_info ad; |
| 601 | |||
| 602 | mask &= (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND); | ||
| 601 | /* | 603 | /* |
| 602 | * No permission to check. Existence test. Yup, it's there. | 604 | * No permission to check. Existence test. Yup, it's there. |
| 603 | */ | 605 | */ |
| @@ -2191,7 +2193,7 @@ static void smack_ipc_getsecid(struct kern_ipc_perm *ipp, u32 *secid) | |||
| 2191 | 2193 | ||
| 2192 | /** | 2194 | /** |
| 2193 | * smack_d_instantiate - Make sure the blob is correct on an inode | 2195 | * smack_d_instantiate - Make sure the blob is correct on an inode |
| 2194 | * @opt_dentry: unused | 2196 | * @opt_dentry: dentry where inode will be attached |
| 2195 | * @inode: the object | 2197 | * @inode: the object |
| 2196 | * | 2198 | * |
| 2197 | * Set the inode's security blob if it hasn't been done already. | 2199 | * Set the inode's security blob if it hasn't been done already. |
| @@ -2310,20 +2312,10 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) | |||
| 2310 | /* | 2312 | /* |
| 2311 | * Get the dentry for xattr. | 2313 | * Get the dentry for xattr. |
| 2312 | */ | 2314 | */ |
| 2313 | if (opt_dentry == NULL) { | 2315 | dp = dget(opt_dentry); |
| 2314 | dp = d_find_alias(inode); | ||
| 2315 | if (dp == NULL) | ||
| 2316 | break; | ||
| 2317 | } else { | ||
| 2318 | dp = dget(opt_dentry); | ||
| 2319 | if (dp == NULL) | ||
| 2320 | break; | ||
| 2321 | } | ||
| 2322 | |||
| 2323 | fetched = smk_fetch(inode, dp); | 2316 | fetched = smk_fetch(inode, dp); |
| 2324 | if (fetched != NULL) | 2317 | if (fetched != NULL) |
| 2325 | final = fetched; | 2318 | final = fetched; |
| 2326 | |||
| 2327 | dput(dp); | 2319 | dput(dp); |
| 2328 | break; | 2320 | break; |
| 2329 | } | 2321 | } |
diff --git a/security/tomoyo/Makefile b/security/tomoyo/Makefile index 4fb39030f6bd..91640e96bd06 100644 --- a/security/tomoyo/Makefile +++ b/security/tomoyo/Makefile | |||
| @@ -1 +1 @@ | |||
| obj-y = common.o realpath.o tomoyo.o domain.o file.o gc.o path_group.o | obj-y = common.o domain.o file.o gc.o group.o load_policy.o memory.o mount.o realpath.o securityfs_if.o tomoyo.o util.o | ||
diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index b5dbdc9ff73c..ef43995119a4 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c | |||
| @@ -3,974 +3,424 @@ | |||
| 3 | * | 3 | * |
| 4 | * Common functions for TOMOYO. | 4 | * Common functions for TOMOYO. |
| 5 | * | 5 | * |
| 6 | * Copyright (C) 2005-2009 NTT DATA CORPORATION | 6 | * Copyright (C) 2005-2010 NTT DATA CORPORATION |
| 7 | * | ||
| 8 | * Version: 2.2.0 2009/04/01 | ||
| 9 | * | ||
| 10 | */ | 7 | */ |
| 11 | 8 | ||
| 12 | #include <linux/uaccess.h> | 9 | #include <linux/uaccess.h> |
| 13 | #include <linux/slab.h> | 10 | #include <linux/slab.h> |
| 14 | #include <linux/security.h> | 11 | #include <linux/security.h> |
| 15 | #include <linux/hardirq.h> | ||
| 16 | #include "common.h" | 12 | #include "common.h" |
| 17 | 13 | ||
| 18 | /* Lock for protecting policy. */ | 14 | static struct tomoyo_profile tomoyo_default_profile = { |
| 19 | DEFINE_MUTEX(tomoyo_policy_lock); | 15 | .learning = &tomoyo_default_profile.preference, |
| 16 | .permissive = &tomoyo_default_profile.preference, | ||
| 17 | .enforcing = &tomoyo_default_profile.preference, | ||
| 18 | .preference.enforcing_verbose = true, | ||
| 19 | .preference.learning_max_entry = 2048, | ||
| 20 | .preference.learning_verbose = false, | ||
| 21 | .preference.permissive_verbose = true | ||
| 22 | }; | ||
| 23 | |||
| 24 | /* Profile version. Currently only 20090903 is defined. */ | ||
| 25 | static unsigned int tomoyo_profile_version; | ||
| 20 | 26 | ||
| 21 | /* Has loading policy done? */ | 27 | /* Profile table. Memory is allocated as needed. */ |
| 22 | bool tomoyo_policy_loaded; | 28 | static struct tomoyo_profile *tomoyo_profile_ptr[TOMOYO_MAX_PROFILES]; |
| 23 | 29 | ||
| 24 | /* String table for functionality that takes 4 modes. */ | 30 | /* String table for functionality that takes 4 modes. */ |
| 25 | static const char *tomoyo_mode_4[4] = { | 31 | static const char *tomoyo_mode[4] = { |
| 26 | "disabled", "learning", "permissive", "enforcing" | 32 | "disabled", "learning", "permissive", "enforcing" |
| 27 | }; | 33 | }; |
| 28 | /* String table for functionality that takes 2 modes. */ | ||
| 29 | static const char *tomoyo_mode_2[4] = { | ||
| 30 | "disabled", "enabled", "enabled", "enabled" | ||
| 31 | }; | ||
| 32 | 34 | ||
| 33 | /* | 35 | /* String table for /sys/kernel/security/tomoyo/profile */ |
| 34 | * tomoyo_control_array is a static data which contains | 36 | static const char *tomoyo_mac_keywords[TOMOYO_MAX_MAC_INDEX |
| 35 | * | 37 | + TOMOYO_MAX_MAC_CATEGORY_INDEX] = { |
| 36 | * (1) functionality name used by /sys/kernel/security/tomoyo/profile . | 38 | [TOMOYO_MAC_FILE_EXECUTE] = "file::execute", |
| 37 | * (2) initial values for "struct tomoyo_profile". | 39 | [TOMOYO_MAC_FILE_OPEN] = "file::open", |
| 38 | * (3) max values for "struct tomoyo_profile". | 40 | [TOMOYO_MAC_FILE_CREATE] = "file::create", |
| 39 | */ | 41 | [TOMOYO_MAC_FILE_UNLINK] = "file::unlink", |
| 40 | static struct { | 42 | [TOMOYO_MAC_FILE_MKDIR] = "file::mkdir", |
| 41 | const char *keyword; | 43 | [TOMOYO_MAC_FILE_RMDIR] = "file::rmdir", |
| 42 | unsigned int current_value; | 44 | [TOMOYO_MAC_FILE_MKFIFO] = "file::mkfifo", |
| 43 | const unsigned int max_value; | 45 | [TOMOYO_MAC_FILE_MKSOCK] = "file::mksock", |
| 44 | } tomoyo_control_array[TOMOYO_MAX_CONTROL_INDEX] = { | 46 | [TOMOYO_MAC_FILE_TRUNCATE] = "file::truncate", |
| 45 | [TOMOYO_MAC_FOR_FILE] = { "MAC_FOR_FILE", 0, 3 }, | 47 | [TOMOYO_MAC_FILE_SYMLINK] = "file::symlink", |
| 46 | [TOMOYO_MAX_ACCEPT_ENTRY] = { "MAX_ACCEPT_ENTRY", 2048, INT_MAX }, | 48 | [TOMOYO_MAC_FILE_REWRITE] = "file::rewrite", |
| 47 | [TOMOYO_VERBOSE] = { "TOMOYO_VERBOSE", 1, 1 }, | 49 | [TOMOYO_MAC_FILE_MKBLOCK] = "file::mkblock", |
| 50 | [TOMOYO_MAC_FILE_MKCHAR] = "file::mkchar", | ||
| 51 | [TOMOYO_MAC_FILE_LINK] = "file::link", | ||
| 52 | [TOMOYO_MAC_FILE_RENAME] = "file::rename", | ||
| 53 | [TOMOYO_MAC_FILE_CHMOD] = "file::chmod", | ||
| 54 | [TOMOYO_MAC_FILE_CHOWN] = "file::chown", | ||
| 55 | [TOMOYO_MAC_FILE_CHGRP] = "file::chgrp", | ||
| 56 | [TOMOYO_MAC_FILE_IOCTL] = "file::ioctl", | ||
| 57 | [TOMOYO_MAC_FILE_CHROOT] = "file::chroot", | ||
| 58 | [TOMOYO_MAC_FILE_MOUNT] = "file::mount", | ||
| 59 | [TOMOYO_MAC_FILE_UMOUNT] = "file::umount", | ||
| 60 | [TOMOYO_MAC_FILE_PIVOT_ROOT] = "file::pivot_root", | ||
| 61 | [TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_FILE] = "file", | ||
| 48 | }; | 62 | }; |
| 49 | 63 | ||
| 50 | /* | ||
| 51 | * tomoyo_profile is a structure which is used for holding the mode of access | ||
| 52 | * controls. TOMOYO has 4 modes: disabled, learning, permissive, enforcing. | ||
| 53 | * An administrator can define up to 256 profiles. | ||
| 54 | * The ->profile of "struct tomoyo_domain_info" is used for remembering | ||
| 55 | * the profile's number (0 - 255) assigned to that domain. | ||
| 56 | */ | ||
| 57 | static struct tomoyo_profile { | ||
| 58 | unsigned int value[TOMOYO_MAX_CONTROL_INDEX]; | ||
| 59 | const struct tomoyo_path_info *comment; | ||
| 60 | } *tomoyo_profile_ptr[TOMOYO_MAX_PROFILES]; | ||
| 61 | |||
| 62 | /* Permit policy management by non-root user? */ | 64 | /* Permit policy management by non-root user? */ |
| 63 | static bool tomoyo_manage_by_non_root; | 65 | static bool tomoyo_manage_by_non_root; |
| 64 | 66 | ||
| 65 | /* Utility functions. */ | 67 | /* Utility functions. */ |
| 66 | 68 | ||
| 67 | /* Open operation for /sys/kernel/security/tomoyo/ interface. */ | ||
| 68 | static int tomoyo_open_control(const u8 type, struct file *file); | ||
| 69 | /* Close /sys/kernel/security/tomoyo/ interface. */ | ||
| 70 | static int tomoyo_close_control(struct file *file); | ||
| 71 | /* Read operation for /sys/kernel/security/tomoyo/ interface. */ | ||
| 72 | static int tomoyo_read_control(struct file *file, char __user *buffer, | ||
| 73 | const int buffer_len); | ||
| 74 | /* Write operation for /sys/kernel/security/tomoyo/ interface. */ | ||
| 75 | static int tomoyo_write_control(struct file *file, const char __user *buffer, | ||
| 76 | const int buffer_len); | ||
| 77 | |||
| 78 | /** | 69 | /** |
| 79 | * tomoyo_parse_name_union - Parse a tomoyo_name_union. | 70 | * tomoyo_yesno - Return "yes" or "no". |
| 80 | * | 71 | * |
| 81 | * @filename: Name or name group. | 72 | * @value: Bool value. |
| 82 | * @ptr: Pointer to "struct tomoyo_name_union". | ||
| 83 | * | ||
| 84 | * Returns true on success, false otherwise. | ||
| 85 | */ | 73 | */ |
| 86 | bool tomoyo_parse_name_union(const char *filename, | 74 | static const char *tomoyo_yesno(const unsigned int value) |
| 87 | struct tomoyo_name_union *ptr) | ||
| 88 | { | 75 | { |
| 89 | if (!tomoyo_is_correct_path(filename, 0, 0, 0)) | 76 | return value ? "yes" : "no"; |
| 90 | return false; | ||
| 91 | if (filename[0] == '@') { | ||
| 92 | ptr->group = tomoyo_get_path_group(filename + 1); | ||
| 93 | ptr->is_group = true; | ||
| 94 | return ptr->group != NULL; | ||
| 95 | } | ||
| 96 | ptr->filename = tomoyo_get_name(filename); | ||
| 97 | ptr->is_group = false; | ||
| 98 | return ptr->filename != NULL; | ||
| 99 | } | 77 | } |
| 100 | 78 | ||
| 101 | /** | 79 | static void tomoyo_addprintf(char *buffer, int len, const char *fmt, ...) |
| 102 | * tomoyo_print_name_union - Print a tomoyo_name_union. | ||
| 103 | * | ||
| 104 | * @head: Pointer to "struct tomoyo_io_buffer". | ||
| 105 | * @ptr: Pointer to "struct tomoyo_name_union". | ||
| 106 | * | ||
| 107 | * Returns true on success, false otherwise. | ||
| 108 | */ | ||
| 109 | static bool tomoyo_print_name_union(struct tomoyo_io_buffer *head, | ||
| 110 | const struct tomoyo_name_union *ptr) | ||
| 111 | { | 80 | { |
| 112 | int pos = head->read_avail; | 81 | va_list args; |
| 113 | if (pos && head->read_buf[pos - 1] == ' ') | 82 | const int pos = strlen(buffer); |
| 114 | head->read_avail--; | 83 | va_start(args, fmt); |
| 115 | if (ptr->is_group) | 84 | vsnprintf(buffer + pos, len - pos - 1, fmt, args); |
| 116 | return tomoyo_io_printf(head, " @%s", | 85 | va_end(args); |
| 117 | ptr->group->group_name->name); | ||
| 118 | return tomoyo_io_printf(head, " %s", ptr->filename->name); | ||
| 119 | } | 86 | } |
| 120 | 87 | ||
| 121 | /** | 88 | /** |
| 122 | * tomoyo_is_byte_range - Check whether the string isa \ooo style octal value. | 89 | * tomoyo_flush - Flush queued string to userspace's buffer. |
| 123 | * | ||
| 124 | * @str: Pointer to the string. | ||
| 125 | * | 90 | * |
| 126 | * Returns true if @str is a \ooo style octal value, false otherwise. | 91 | * @head: Pointer to "struct tomoyo_io_buffer". |
| 127 | * | 92 | * |
| 128 | * TOMOYO uses \ooo style representation for 0x01 - 0x20 and 0x7F - 0xFF. | 93 | * Returns true if all data was flushed, false otherwise. |
| 129 | * This function verifies that \ooo is in valid range. | ||
| 130 | */ | 94 | */ |
| 131 | static inline bool tomoyo_is_byte_range(const char *str) | 95 | static bool tomoyo_flush(struct tomoyo_io_buffer *head) |
| 132 | { | 96 | { |
| 133 | return *str >= '0' && *str++ <= '3' && | 97 | while (head->r.w_pos) { |
| 134 | *str >= '0' && *str++ <= '7' && | 98 | const char *w = head->r.w[0]; |
| 135 | *str >= '0' && *str <= '7'; | 99 | int len = strlen(w); |
| 100 | if (len) { | ||
| 101 | if (len > head->read_user_buf_avail) | ||
| 102 | len = head->read_user_buf_avail; | ||
| 103 | if (!len) | ||
| 104 | return false; | ||
| 105 | if (copy_to_user(head->read_user_buf, w, len)) | ||
| 106 | return false; | ||
| 107 | head->read_user_buf_avail -= len; | ||
| 108 | head->read_user_buf += len; | ||
| 109 | w += len; | ||
| 110 | } | ||
| 111 | if (*w) { | ||
| 112 | head->r.w[0] = w; | ||
| 113 | return false; | ||
| 114 | } | ||
| 115 | /* Add '\0' for query. */ | ||
| 116 | if (head->poll) { | ||
| 117 | if (!head->read_user_buf_avail || | ||
| 118 | copy_to_user(head->read_user_buf, "", 1)) | ||
| 119 | return false; | ||
| 120 | head->read_user_buf_avail--; | ||
| 121 | head->read_user_buf++; | ||
| 122 | } | ||
| 123 | head->r.w_pos--; | ||
| 124 | for (len = 0; len < head->r.w_pos; len++) | ||
| 125 | head->r.w[len] = head->r.w[len + 1]; | ||
| 126 | } | ||
| 127 | head->r.avail = 0; | ||
| 128 | return true; | ||
| 136 | } | 129 | } |
| 137 | 130 | ||
| 138 | /** | 131 | /** |
| 139 | * tomoyo_is_alphabet_char - Check whether the character is an alphabet. | 132 | * tomoyo_set_string - Queue string to "struct tomoyo_io_buffer" structure. |
| 140 | * | 133 | * |
| 141 | * @c: The character to check. | 134 | * @head: Pointer to "struct tomoyo_io_buffer". |
| 135 | * @string: String to print. | ||
| 142 | * | 136 | * |
| 143 | * Returns true if @c is an alphabet character, false otherwise. | 137 | * Note that @string has to be kept valid until @head is kfree()d. |
| 138 | * This means that char[] allocated on stack memory cannot be passed to | ||
| 139 | * this function. Use tomoyo_io_printf() for char[] allocated on stack memory. | ||
| 144 | */ | 140 | */ |
| 145 | static inline bool tomoyo_is_alphabet_char(const char c) | 141 | static void tomoyo_set_string(struct tomoyo_io_buffer *head, const char *string) |
| 146 | { | 142 | { |
| 147 | return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); | 143 | if (head->r.w_pos < TOMOYO_MAX_IO_READ_QUEUE) { |
| 144 | head->r.w[head->r.w_pos++] = string; | ||
| 145 | tomoyo_flush(head); | ||
| 146 | } else | ||
| 147 | WARN_ON(1); | ||
| 148 | } | 148 | } |
| 149 | 149 | ||
| 150 | /** | 150 | /** |
| 151 | * tomoyo_make_byte - Make byte value from three octal characters. | 151 | * tomoyo_io_printf - printf() to "struct tomoyo_io_buffer" structure. |
| 152 | * | 152 | * |
| 153 | * @c1: The first character. | 153 | * @head: Pointer to "struct tomoyo_io_buffer". |
| 154 | * @c2: The second character. | 154 | * @fmt: The printf()'s format string, followed by parameters. |
| 155 | * @c3: The third character. | ||
| 156 | * | ||
| 157 | * Returns byte value. | ||
| 158 | */ | 155 | */ |
| 159 | static inline u8 tomoyo_make_byte(const u8 c1, const u8 c2, const u8 c3) | 156 | void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...) |
| 160 | { | 157 | { |
| 161 | return ((c1 - '0') << 6) + ((c2 - '0') << 3) + (c3 - '0'); | 158 | va_list args; |
| 159 | int len; | ||
| 160 | int pos = head->r.avail; | ||
| 161 | int size = head->readbuf_size - pos; | ||
| 162 | if (size <= 0) | ||
| 163 | return; | ||
| 164 | va_start(args, fmt); | ||
| 165 | len = vsnprintf(head->read_buf + pos, size, fmt, args) + 1; | ||
| 166 | va_end(args); | ||
| 167 | if (pos + len >= head->readbuf_size) { | ||
| 168 | WARN_ON(1); | ||
| 169 | return; | ||
| 170 | } | ||
| 171 | head->r.avail += len; | ||
| 172 | tomoyo_set_string(head, head->read_buf + pos); | ||
| 162 | } | 173 | } |
| 163 | 174 | ||
| 164 | /** | 175 | static void tomoyo_set_space(struct tomoyo_io_buffer *head) |
| 165 | * tomoyo_str_starts - Check whether the given string starts with the given keyword. | ||
| 166 | * | ||
| 167 | * @src: Pointer to pointer to the string. | ||
| 168 | * @find: Pointer to the keyword. | ||
| 169 | * | ||
| 170 | * Returns true if @src starts with @find, false otherwise. | ||
| 171 | * | ||
| 172 | * The @src is updated to point the first character after the @find | ||
| 173 | * if @src starts with @find. | ||
| 174 | */ | ||
| 175 | static bool tomoyo_str_starts(char **src, const char *find) | ||
| 176 | { | 176 | { |
| 177 | const int len = strlen(find); | 177 | tomoyo_set_string(head, " "); |
| 178 | char *tmp = *src; | ||
| 179 | |||
| 180 | if (strncmp(tmp, find, len)) | ||
| 181 | return false; | ||
| 182 | tmp += len; | ||
| 183 | *src = tmp; | ||
| 184 | return true; | ||
| 185 | } | 178 | } |
| 186 | 179 | ||
| 187 | /** | 180 | static bool tomoyo_set_lf(struct tomoyo_io_buffer *head) |
| 188 | * tomoyo_normalize_line - Format string. | ||
| 189 | * | ||
| 190 | * @buffer: The line to normalize. | ||
| 191 | * | ||
| 192 | * Leading and trailing whitespaces are removed. | ||
| 193 | * Multiple whitespaces are packed into single space. | ||
| 194 | * | ||
| 195 | * Returns nothing. | ||
| 196 | */ | ||
| 197 | static void tomoyo_normalize_line(unsigned char *buffer) | ||
| 198 | { | 181 | { |
| 199 | unsigned char *sp = buffer; | 182 | tomoyo_set_string(head, "\n"); |
| 200 | unsigned char *dp = buffer; | 183 | return !head->r.w_pos; |
| 201 | bool first = true; | ||
| 202 | |||
| 203 | while (tomoyo_is_invalid(*sp)) | ||
| 204 | sp++; | ||
| 205 | while (*sp) { | ||
| 206 | if (!first) | ||
| 207 | *dp++ = ' '; | ||
| 208 | first = false; | ||
| 209 | while (tomoyo_is_valid(*sp)) | ||
| 210 | *dp++ = *sp++; | ||
| 211 | while (tomoyo_is_invalid(*sp)) | ||
| 212 | sp++; | ||
| 213 | } | ||
| 214 | *dp = '\0'; | ||
| 215 | } | 184 | } |
| 216 | 185 | ||
| 217 | /** | 186 | /** |
| 218 | * tomoyo_tokenize - Tokenize string. | 187 | * tomoyo_print_name_union - Print a tomoyo_name_union. |
| 219 | * | ||
| 220 | * @buffer: The line to tokenize. | ||
| 221 | * @w: Pointer to "char *". | ||
| 222 | * @size: Sizeof @w . | ||
| 223 | * | 188 | * |
| 224 | * Returns true on success, false otherwise. | 189 | * @head: Pointer to "struct tomoyo_io_buffer". |
| 190 | * @ptr: Pointer to "struct tomoyo_name_union". | ||
| 225 | */ | 191 | */ |
| 226 | bool tomoyo_tokenize(char *buffer, char *w[], size_t size) | 192 | static void tomoyo_print_name_union(struct tomoyo_io_buffer *head, |
| 193 | const struct tomoyo_name_union *ptr) | ||
| 227 | { | 194 | { |
| 228 | int count = size / sizeof(char *); | 195 | tomoyo_set_space(head); |
| 229 | int i; | 196 | if (ptr->is_group) { |
| 230 | for (i = 0; i < count; i++) | 197 | tomoyo_set_string(head, "@"); |
| 231 | w[i] = ""; | 198 | tomoyo_set_string(head, ptr->group->group_name->name); |
| 232 | for (i = 0; i < count; i++) { | 199 | } else { |
| 233 | char *cp = strchr(buffer, ' '); | 200 | tomoyo_set_string(head, ptr->filename->name); |
| 234 | if (cp) | ||
| 235 | *cp = '\0'; | ||
| 236 | w[i] = buffer; | ||
| 237 | if (!cp) | ||
| 238 | break; | ||
| 239 | buffer = cp + 1; | ||
| 240 | } | 201 | } |
| 241 | return i < count || !*buffer; | ||
| 242 | } | 202 | } |
| 243 | 203 | ||
| 244 | /** | 204 | /** |
| 245 | * tomoyo_is_correct_path - Validate a pathname. | 205 | * tomoyo_print_number_union - Print a tomoyo_number_union. |
| 246 | * @filename: The pathname to check. | 206 | * |
| 247 | * @start_type: Should the pathname start with '/'? | 207 | * @head: Pointer to "struct tomoyo_io_buffer". |
| 248 | * 1 = must / -1 = must not / 0 = don't care | 208 | * @ptr: Pointer to "struct tomoyo_number_union". |
| 249 | * @pattern_type: Can the pathname contain a wildcard? | ||
| 250 | * 1 = must / -1 = must not / 0 = don't care | ||
| 251 | * @end_type: Should the pathname end with '/'? | ||
| 252 | * 1 = must / -1 = must not / 0 = don't care | ||
| 253 | * | ||
| 254 | * Check whether the given filename follows the naming rules. | ||
| 255 | * Returns true if @filename follows the naming rules, false otherwise. | ||
| 256 | */ | 209 | */ |
| 257 | bool tomoyo_is_correct_path(const char *filename, const s8 start_type, | 210 | static void tomoyo_print_number_union(struct tomoyo_io_buffer *head, |
| 258 | const s8 pattern_type, const s8 end_type) | 211 | const struct tomoyo_number_union *ptr) |
| 259 | { | 212 | { |
| 260 | const char *const start = filename; | 213 | tomoyo_set_space(head); |
| 261 | bool in_repetition = false; | 214 | if (ptr->is_group) { |
| 262 | bool contains_pattern = false; | 215 | tomoyo_set_string(head, "@"); |
| 263 | unsigned char c; | 216 | tomoyo_set_string(head, ptr->group->group_name->name); |
| 264 | unsigned char d; | 217 | } else { |
| 265 | unsigned char e; | 218 | int i; |
| 266 | 219 | unsigned long min = ptr->values[0]; | |
| 267 | if (!filename) | 220 | const unsigned long max = ptr->values[1]; |
| 268 | goto out; | 221 | u8 min_type = ptr->min_type; |
| 269 | c = *filename; | 222 | const u8 max_type = ptr->max_type; |
| 270 | if (start_type == 1) { /* Must start with '/' */ | 223 | char buffer[128]; |
| 271 | if (c != '/') | 224 | buffer[0] = '\0'; |
| 272 | goto out; | 225 | for (i = 0; i < 2; i++) { |
| 273 | } else if (start_type == -1) { /* Must not start with '/' */ | 226 | switch (min_type) { |
| 274 | if (c == '/') | 227 | case TOMOYO_VALUE_TYPE_HEXADECIMAL: |
| 275 | goto out; | 228 | tomoyo_addprintf(buffer, sizeof(buffer), |
| 276 | } | 229 | "0x%lX", min); |
| 277 | if (c) | 230 | break; |
| 278 | c = *(filename + strlen(filename) - 1); | 231 | case TOMOYO_VALUE_TYPE_OCTAL: |
| 279 | if (end_type == 1) { /* Must end with '/' */ | 232 | tomoyo_addprintf(buffer, sizeof(buffer), |
| 280 | if (c != '/') | 233 | "0%lo", min); |
| 281 | goto out; | 234 | break; |
| 282 | } else if (end_type == -1) { /* Must not end with '/' */ | 235 | default: |
| 283 | if (c == '/') | 236 | tomoyo_addprintf(buffer, sizeof(buffer), |
| 284 | goto out; | 237 | "%lu", min); |
| 285 | } | 238 | break; |
| 286 | while (1) { | ||
| 287 | c = *filename++; | ||
| 288 | if (!c) | ||
| 289 | break; | ||
| 290 | if (c == '\\') { | ||
| 291 | c = *filename++; | ||
| 292 | switch (c) { | ||
| 293 | case '\\': /* "\\" */ | ||
| 294 | continue; | ||
| 295 | case '$': /* "\$" */ | ||
| 296 | case '+': /* "\+" */ | ||
| 297 | case '?': /* "\?" */ | ||
| 298 | case '*': /* "\*" */ | ||
| 299 | case '@': /* "\@" */ | ||
| 300 | case 'x': /* "\x" */ | ||
| 301 | case 'X': /* "\X" */ | ||
| 302 | case 'a': /* "\a" */ | ||
| 303 | case 'A': /* "\A" */ | ||
| 304 | case '-': /* "\-" */ | ||
| 305 | if (pattern_type == -1) | ||
| 306 | break; /* Must not contain pattern */ | ||
| 307 | contains_pattern = true; | ||
| 308 | continue; | ||
| 309 | case '{': /* "/\{" */ | ||
| 310 | if (filename - 3 < start || | ||
| 311 | *(filename - 3) != '/') | ||
| 312 | break; | ||
| 313 | if (pattern_type == -1) | ||
| 314 | break; /* Must not contain pattern */ | ||
| 315 | contains_pattern = true; | ||
| 316 | in_repetition = true; | ||
| 317 | continue; | ||
| 318 | case '}': /* "\}/" */ | ||
| 319 | if (*filename != '/') | ||
| 320 | break; | ||
| 321 | if (!in_repetition) | ||
| 322 | break; | ||
| 323 | in_repetition = false; | ||
| 324 | continue; | ||
| 325 | case '0': /* "\ooo" */ | ||
| 326 | case '1': | ||
| 327 | case '2': | ||
| 328 | case '3': | ||
| 329 | d = *filename++; | ||
| 330 | if (d < '0' || d > '7') | ||
| 331 | break; | ||
| 332 | e = *filename++; | ||
| 333 | if (e < '0' || e > '7') | ||
| 334 | break; | ||
| 335 | c = tomoyo_make_byte(c, d, e); | ||
| 336 | if (tomoyo_is_invalid(c)) | ||
| 337 | continue; /* pattern is not \000 */ | ||
| 338 | } | 239 | } |
| 339 | goto out; | 240 | if (min == max && min_type == max_type) |
| 340 | } else if (in_repetition && c == '/') { | 241 | break; |
| 341 | goto out; | 242 | tomoyo_addprintf(buffer, sizeof(buffer), "-"); |
| 342 | } else if (tomoyo_is_invalid(c)) { | 243 | min_type = max_type; |
| 343 | goto out; | 244 | min = max; |
| 344 | } | 245 | } |
| 246 | tomoyo_io_printf(head, "%s", buffer); | ||
| 345 | } | 247 | } |
| 346 | if (pattern_type == 1) { /* Must contain pattern */ | ||
| 347 | if (!contains_pattern) | ||
| 348 | goto out; | ||
| 349 | } | ||
| 350 | if (in_repetition) | ||
| 351 | goto out; | ||
| 352 | return true; | ||
| 353 | out: | ||
| 354 | return false; | ||
| 355 | } | 248 | } |
| 356 | 249 | ||
| 357 | /** | 250 | /** |
| 358 | * tomoyo_is_correct_domain - Check whether the given domainname follows the naming rules. | 251 | * tomoyo_assign_profile - Create a new profile. |
| 359 | * @domainname: The domainname to check. | ||
| 360 | * | 252 | * |
| 361 | * Returns true if @domainname follows the naming rules, false otherwise. | 253 | * @profile: Profile number to create. |
| 254 | * | ||
| 255 | * Returns pointer to "struct tomoyo_profile" on success, NULL otherwise. | ||
| 362 | */ | 256 | */ |
| 363 | bool tomoyo_is_correct_domain(const unsigned char *domainname) | 257 | static struct tomoyo_profile *tomoyo_assign_profile(const unsigned int profile) |
| 364 | { | 258 | { |
| 365 | unsigned char c; | 259 | struct tomoyo_profile *ptr; |
| 366 | unsigned char d; | 260 | struct tomoyo_profile *entry; |
| 367 | unsigned char e; | 261 | if (profile >= TOMOYO_MAX_PROFILES) |
| 368 | 262 | return NULL; | |
| 369 | if (!domainname || strncmp(domainname, TOMOYO_ROOT_NAME, | 263 | ptr = tomoyo_profile_ptr[profile]; |
| 370 | TOMOYO_ROOT_NAME_LEN)) | 264 | if (ptr) |
| 265 | return ptr; | ||
| 266 | entry = kzalloc(sizeof(*entry), GFP_NOFS); | ||
| 267 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) | ||
| 371 | goto out; | 268 | goto out; |
| 372 | domainname += TOMOYO_ROOT_NAME_LEN; | 269 | ptr = tomoyo_profile_ptr[profile]; |
| 373 | if (!*domainname) | 270 | if (!ptr && tomoyo_memory_ok(entry)) { |
| 374 | return true; | 271 | ptr = entry; |
| 375 | do { | 272 | ptr->learning = &tomoyo_default_profile.preference; |
| 376 | if (*domainname++ != ' ') | 273 | ptr->permissive = &tomoyo_default_profile.preference; |
| 377 | goto out; | 274 | ptr->enforcing = &tomoyo_default_profile.preference; |
| 378 | if (*domainname++ != '/') | 275 | ptr->default_config = TOMOYO_CONFIG_DISABLED; |
| 379 | goto out; | 276 | memset(ptr->config, TOMOYO_CONFIG_USE_DEFAULT, |
| 380 | while ((c = *domainname) != '\0' && c != ' ') { | 277 | sizeof(ptr->config)); |
| 381 | domainname++; | 278 | mb(); /* Avoid out-of-order execution. */ |
| 382 | if (c == '\\') { | 279 | tomoyo_profile_ptr[profile] = ptr; |
| 383 | c = *domainname++; | 280 | entry = NULL; |
| 384 | switch ((c)) { | 281 | } |
| 385 | case '\\': /* "\\" */ | 282 | mutex_unlock(&tomoyo_policy_lock); |
| 386 | continue; | ||
| 387 | case '0': /* "\ooo" */ | ||
| 388 | case '1': | ||
| 389 | case '2': | ||
| 390 | case '3': | ||
| 391 | d = *domainname++; | ||
| 392 | if (d < '0' || d > '7') | ||
| 393 | break; | ||
| 394 | e = *domainname++; | ||
| 395 | if (e < '0' || e > '7') | ||
| 396 | break; | ||
| 397 | c = tomoyo_make_byte(c, d, e); | ||
| 398 | if (tomoyo_is_invalid(c)) | ||
| 399 | /* pattern is not \000 */ | ||
| 400 | continue; | ||
| 401 | } | ||
| 402 | goto out; | ||
| 403 | } else if (tomoyo_is_invalid(c)) { | ||
| 404 | goto out; | ||
| 405 | } | ||
| 406 | } | ||
| 407 | } while (*domainname); | ||
| 408 | return true; | ||
| 409 | out: | 283 | out: |
| 410 | return false; | 284 | kfree(entry); |
| 285 | return ptr; | ||
| 411 | } | 286 | } |
| 412 | 287 | ||
| 413 | /** | 288 | /** |
| 414 | * tomoyo_is_domain_def - Check whether the given token can be a domainname. | 289 | * tomoyo_profile - Find a profile. |
| 415 | * | 290 | * |
| 416 | * @buffer: The token to check. | 291 | * @profile: Profile number to find. |
| 417 | * | 292 | * |
| 418 | * Returns true if @buffer possibly be a domainname, false otherwise. | 293 | * Returns pointer to "struct tomoyo_profile". |
| 419 | */ | 294 | */ |
| 420 | bool tomoyo_is_domain_def(const unsigned char *buffer) | 295 | struct tomoyo_profile *tomoyo_profile(const u8 profile) |
| 421 | { | 296 | { |
| 422 | return !strncmp(buffer, TOMOYO_ROOT_NAME, TOMOYO_ROOT_NAME_LEN); | 297 | struct tomoyo_profile *ptr = tomoyo_profile_ptr[profile]; |
| 298 | if (!tomoyo_policy_loaded) | ||
| 299 | return &tomoyo_default_profile; | ||
| 300 | BUG_ON(!ptr); | ||
| 301 | return ptr; | ||
| 423 | } | 302 | } |
| 424 | 303 | ||
| 425 | /** | 304 | static s8 tomoyo_find_yesno(const char *string, const char *find) |
| 426 | * tomoyo_find_domain - Find a domain by the given name. | ||
| 427 | * | ||
| 428 | * @domainname: The domainname to find. | ||
| 429 | * | ||
| 430 | * Returns pointer to "struct tomoyo_domain_info" if found, NULL otherwise. | ||
| 431 | * | ||
| 432 | * Caller holds tomoyo_read_lock(). | ||
| 433 | */ | ||
| 434 | struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname) | ||
| 435 | { | 305 | { |
| 436 | struct tomoyo_domain_info *domain; | 306 | const char *cp = strstr(string, find); |
| 437 | struct tomoyo_path_info name; | 307 | if (cp) { |
| 438 | 308 | cp += strlen(find); | |
| 439 | name.name = domainname; | 309 | if (!strncmp(cp, "=yes", 4)) |
| 440 | tomoyo_fill_path_info(&name); | 310 | return 1; |
| 441 | list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { | 311 | else if (!strncmp(cp, "=no", 3)) |
| 442 | if (!domain->is_deleted && | 312 | return 0; |
| 443 | !tomoyo_pathcmp(&name, domain->domainname)) | ||
| 444 | return domain; | ||
| 445 | } | 313 | } |
| 446 | return NULL; | 314 | return -1; |
| 447 | } | 315 | } |
| 448 | 316 | ||
| 449 | /** | 317 | static void tomoyo_set_bool(bool *b, const char *string, const char *find) |
| 450 | * tomoyo_const_part_length - Evaluate the initial length without a pattern in a token. | ||
| 451 | * | ||
| 452 | * @filename: The string to evaluate. | ||
| 453 | * | ||
| 454 | * Returns the initial length without a pattern in @filename. | ||
| 455 | */ | ||
| 456 | static int tomoyo_const_part_length(const char *filename) | ||
| 457 | { | 318 | { |
| 458 | char c; | 319 | switch (tomoyo_find_yesno(string, find)) { |
| 459 | int len = 0; | 320 | case 1: |
| 460 | 321 | *b = true; | |
| 461 | if (!filename) | 322 | break; |
| 462 | return 0; | 323 | case 0: |
| 463 | while ((c = *filename++) != '\0') { | 324 | *b = false; |
| 464 | if (c != '\\') { | ||
| 465 | len++; | ||
| 466 | continue; | ||
| 467 | } | ||
| 468 | c = *filename++; | ||
| 469 | switch (c) { | ||
| 470 | case '\\': /* "\\" */ | ||
| 471 | len += 2; | ||
| 472 | continue; | ||
| 473 | case '0': /* "\ooo" */ | ||
| 474 | case '1': | ||
| 475 | case '2': | ||
| 476 | case '3': | ||
| 477 | c = *filename++; | ||
| 478 | if (c < '0' || c > '7') | ||
| 479 | break; | ||
| 480 | c = *filename++; | ||
| 481 | if (c < '0' || c > '7') | ||
| 482 | break; | ||
| 483 | len += 4; | ||
| 484 | continue; | ||
| 485 | } | ||
| 486 | break; | 325 | break; |
| 487 | } | 326 | } |
| 488 | return len; | ||
| 489 | } | 327 | } |
| 490 | 328 | ||
| 491 | /** | 329 | static void tomoyo_set_uint(unsigned int *i, const char *string, |
| 492 | * tomoyo_fill_path_info - Fill in "struct tomoyo_path_info" members. | 330 | const char *find) |
| 493 | * | ||
| 494 | * @ptr: Pointer to "struct tomoyo_path_info" to fill in. | ||
| 495 | * | ||
| 496 | * The caller sets "struct tomoyo_path_info"->name. | ||
| 497 | */ | ||
| 498 | void tomoyo_fill_path_info(struct tomoyo_path_info *ptr) | ||
| 499 | { | 331 | { |
| 500 | const char *name = ptr->name; | 332 | const char *cp = strstr(string, find); |
| 501 | const int len = strlen(name); | 333 | if (cp) |
| 502 | 334 | sscanf(cp + strlen(find), "=%u", i); | |
| 503 | ptr->const_len = tomoyo_const_part_length(name); | ||
| 504 | ptr->is_dir = len && (name[len - 1] == '/'); | ||
| 505 | ptr->is_patterned = (ptr->const_len < len); | ||
| 506 | ptr->hash = full_name_hash(name, len); | ||
| 507 | } | 335 | } |
| 508 | 336 | ||
| 509 | /** | 337 | static void tomoyo_set_pref(const char *name, const char *value, |
| 510 | * tomoyo_file_matches_pattern2 - Pattern matching without '/' character | 338 | const bool use_default, |
| 511 | * and "\-" pattern. | 339 | struct tomoyo_profile *profile) |
| 512 | * | ||
| 513 | * @filename: The start of string to check. | ||
| 514 | * @filename_end: The end of string to check. | ||
| 515 | * @pattern: The start of pattern to compare. | ||
| 516 | * @pattern_end: The end of pattern to compare. | ||
| 517 | * | ||
| 518 | * Returns true if @filename matches @pattern, false otherwise. | ||
| 519 | */ | ||
| 520 | static bool tomoyo_file_matches_pattern2(const char *filename, | ||
| 521 | const char *filename_end, | ||
| 522 | const char *pattern, | ||
| 523 | const char *pattern_end) | ||
| 524 | { | 340 | { |
| 525 | while (filename < filename_end && pattern < pattern_end) { | 341 | struct tomoyo_preference **pref; |
| 526 | char c; | 342 | bool *verbose; |
| 527 | if (*pattern != '\\') { | 343 | if (!strcmp(name, "enforcing")) { |
| 528 | if (*filename++ != *pattern++) | 344 | if (use_default) { |
| 529 | return false; | 345 | pref = &profile->enforcing; |
| 530 | continue; | 346 | goto set_default; |
| 531 | } | 347 | } |
| 532 | c = *filename; | 348 | profile->enforcing = &profile->preference; |
| 533 | pattern++; | 349 | verbose = &profile->preference.enforcing_verbose; |
| 534 | switch (*pattern) { | 350 | goto set_verbose; |
| 535 | int i; | ||
| 536 | int j; | ||
| 537 | case '?': | ||
| 538 | if (c == '/') { | ||
| 539 | return false; | ||
| 540 | } else if (c == '\\') { | ||
| 541 | if (filename[1] == '\\') | ||
| 542 | filename++; | ||
| 543 | else if (tomoyo_is_byte_range(filename + 1)) | ||
| 544 | filename += 3; | ||
| 545 | else | ||
| 546 | return false; | ||
| 547 | } | ||
| 548 | break; | ||
| 549 | case '\\': | ||
| 550 | if (c != '\\') | ||
| 551 | return false; | ||
| 552 | if (*++filename != '\\') | ||
| 553 | return false; | ||
| 554 | break; | ||
| 555 | case '+': | ||
| 556 | if (!isdigit(c)) | ||
| 557 | return false; | ||
| 558 | break; | ||
| 559 | case 'x': | ||
| 560 | if (!isxdigit(c)) | ||
| 561 | return false; | ||
| 562 | break; | ||
| 563 | case 'a': | ||
| 564 | if (!tomoyo_is_alphabet_char(c)) | ||
| 565 | return false; | ||
| 566 | break; | ||
| 567 | case '0': | ||
| 568 | case '1': | ||
| 569 | case '2': | ||
| 570 | case '3': | ||
| 571 | if (c == '\\' && tomoyo_is_byte_range(filename + 1) | ||
| 572 | && strncmp(filename + 1, pattern, 3) == 0) { | ||
| 573 | filename += 3; | ||
| 574 | pattern += 2; | ||
| 575 | break; | ||
| 576 | } | ||
| 577 | return false; /* Not matched. */ | ||
| 578 | case '*': | ||
| 579 | case '@': | ||
| 580 | for (i = 0; i <= filename_end - filename; i++) { | ||
| 581 | if (tomoyo_file_matches_pattern2( | ||
| 582 | filename + i, filename_end, | ||
| 583 | pattern + 1, pattern_end)) | ||
| 584 | return true; | ||
| 585 | c = filename[i]; | ||
| 586 | if (c == '.' && *pattern == '@') | ||
| 587 | break; | ||
| 588 | if (c != '\\') | ||
| 589 | continue; | ||
| 590 | if (filename[i + 1] == '\\') | ||
| 591 | i++; | ||
| 592 | else if (tomoyo_is_byte_range(filename + i + 1)) | ||
| 593 | i += 3; | ||
| 594 | else | ||
| 595 | break; /* Bad pattern. */ | ||
| 596 | } | ||
| 597 | return false; /* Not matched. */ | ||
| 598 | default: | ||
| 599 | j = 0; | ||
| 600 | c = *pattern; | ||
| 601 | if (c == '$') { | ||
| 602 | while (isdigit(filename[j])) | ||
| 603 | j++; | ||
| 604 | } else if (c == 'X') { | ||
| 605 | while (isxdigit(filename[j])) | ||
| 606 | j++; | ||
| 607 | } else if (c == 'A') { | ||
| 608 | while (tomoyo_is_alphabet_char(filename[j])) | ||
| 609 | j++; | ||
| 610 | } | ||
| 611 | for (i = 1; i <= j; i++) { | ||
| 612 | if (tomoyo_file_matches_pattern2( | ||
| 613 | filename + i, filename_end, | ||
| 614 | pattern + 1, pattern_end)) | ||
| 615 | return true; | ||
| 616 | } | ||
| 617 | return false; /* Not matched or bad pattern. */ | ||
| 618 | } | ||
| 619 | filename++; | ||
| 620 | pattern++; | ||
| 621 | } | ||
| 622 | while (*pattern == '\\' && | ||
| 623 | (*(pattern + 1) == '*' || *(pattern + 1) == '@')) | ||
| 624 | pattern += 2; | ||
| 625 | return filename == filename_end && pattern == pattern_end; | ||
| 626 | } | ||
| 627 | |||
| 628 | /** | ||
| 629 | * tomoyo_file_matches_pattern - Pattern matching without without '/' character. | ||
| 630 | * | ||
| 631 | * @filename: The start of string to check. | ||
| 632 | * @filename_end: The end of string to check. | ||
| 633 | * @pattern: The start of pattern to compare. | ||
| 634 | * @pattern_end: The end of pattern to compare. | ||
| 635 | * | ||
| 636 | * Returns true if @filename matches @pattern, false otherwise. | ||
| 637 | */ | ||
| 638 | static bool tomoyo_file_matches_pattern(const char *filename, | ||
| 639 | const char *filename_end, | ||
| 640 | const char *pattern, | ||
| 641 | const char *pattern_end) | ||
| 642 | { | ||
| 643 | const char *pattern_start = pattern; | ||
| 644 | bool first = true; | ||
| 645 | bool result; | ||
| 646 | |||
| 647 | while (pattern < pattern_end - 1) { | ||
| 648 | /* Split at "\-" pattern. */ | ||
| 649 | if (*pattern++ != '\\' || *pattern++ != '-') | ||
| 650 | continue; | ||
| 651 | result = tomoyo_file_matches_pattern2(filename, | ||
| 652 | filename_end, | ||
| 653 | pattern_start, | ||
| 654 | pattern - 2); | ||
| 655 | if (first) | ||
| 656 | result = !result; | ||
| 657 | if (result) | ||
| 658 | return false; | ||
| 659 | first = false; | ||
| 660 | pattern_start = pattern; | ||
| 661 | } | 351 | } |
| 662 | result = tomoyo_file_matches_pattern2(filename, filename_end, | 352 | if (!strcmp(name, "permissive")) { |
| 663 | pattern_start, pattern_end); | 353 | if (use_default) { |
| 664 | return first ? result : !result; | 354 | pref = &profile->permissive; |
| 665 | } | 355 | goto set_default; |
| 666 | 356 | } | |
| 667 | /** | 357 | profile->permissive = &profile->preference; |
| 668 | * tomoyo_path_matches_pattern2 - Do pathname pattern matching. | 358 | verbose = &profile->preference.permissive_verbose; |
| 669 | * | 359 | goto set_verbose; |
| 670 | * @f: The start of string to check. | ||
| 671 | * @p: The start of pattern to compare. | ||
| 672 | * | ||
| 673 | * Returns true if @f matches @p, false otherwise. | ||
| 674 | */ | ||
| 675 | static bool tomoyo_path_matches_pattern2(const char *f, const char *p) | ||
| 676 | { | ||
| 677 | const char *f_delimiter; | ||
| 678 | const char *p_delimiter; | ||
| 679 | |||
| 680 | while (*f && *p) { | ||
| 681 | f_delimiter = strchr(f, '/'); | ||
| 682 | if (!f_delimiter) | ||
| 683 | f_delimiter = f + strlen(f); | ||
| 684 | p_delimiter = strchr(p, '/'); | ||
| 685 | if (!p_delimiter) | ||
| 686 | p_delimiter = p + strlen(p); | ||
| 687 | if (*p == '\\' && *(p + 1) == '{') | ||
| 688 | goto recursive; | ||
| 689 | if (!tomoyo_file_matches_pattern(f, f_delimiter, p, | ||
| 690 | p_delimiter)) | ||
| 691 | return false; | ||
| 692 | f = f_delimiter; | ||
| 693 | if (*f) | ||
| 694 | f++; | ||
| 695 | p = p_delimiter; | ||
| 696 | if (*p) | ||
| 697 | p++; | ||
| 698 | } | 360 | } |
| 699 | /* Ignore trailing "\*" and "\@" in @pattern. */ | 361 | if (!strcmp(name, "learning")) { |
| 700 | while (*p == '\\' && | 362 | if (use_default) { |
| 701 | (*(p + 1) == '*' || *(p + 1) == '@')) | 363 | pref = &profile->learning; |
| 702 | p += 2; | 364 | goto set_default; |
| 703 | return !*f && !*p; | ||
| 704 | recursive: | ||
| 705 | /* | ||
| 706 | * The "\{" pattern is permitted only after '/' character. | ||
| 707 | * This guarantees that below "*(p - 1)" is safe. | ||
| 708 | * Also, the "\}" pattern is permitted only before '/' character | ||
| 709 | * so that "\{" + "\}" pair will not break the "\-" operator. | ||
| 710 | */ | ||
| 711 | if (*(p - 1) != '/' || p_delimiter <= p + 3 || *p_delimiter != '/' || | ||
| 712 | *(p_delimiter - 1) != '}' || *(p_delimiter - 2) != '\\') | ||
| 713 | return false; /* Bad pattern. */ | ||
| 714 | do { | ||
| 715 | /* Compare current component with pattern. */ | ||
| 716 | if (!tomoyo_file_matches_pattern(f, f_delimiter, p + 2, | ||
| 717 | p_delimiter - 2)) | ||
| 718 | break; | ||
| 719 | /* Proceed to next component. */ | ||
| 720 | f = f_delimiter; | ||
| 721 | if (!*f) | ||
| 722 | break; | ||
| 723 | f++; | ||
| 724 | /* Continue comparison. */ | ||
| 725 | if (tomoyo_path_matches_pattern2(f, p_delimiter + 1)) | ||
| 726 | return true; | ||
| 727 | f_delimiter = strchr(f, '/'); | ||
| 728 | } while (f_delimiter); | ||
| 729 | return false; /* Not matched. */ | ||
| 730 | } | ||
| 731 | |||
| 732 | /** | ||
| 733 | * tomoyo_path_matches_pattern - Check whether the given filename matches the given pattern. | ||
| 734 | * | ||
| 735 | * @filename: The filename to check. | ||
| 736 | * @pattern: The pattern to compare. | ||
| 737 | * | ||
| 738 | * Returns true if matches, false otherwise. | ||
| 739 | * | ||
| 740 | * The following patterns are available. | ||
| 741 | * \\ \ itself. | ||
| 742 | * \ooo Octal representation of a byte. | ||
| 743 | * \* Zero or more repetitions of characters other than '/'. | ||
| 744 | * \@ Zero or more repetitions of characters other than '/' or '.'. | ||
| 745 | * \? 1 byte character other than '/'. | ||
| 746 | * \$ One or more repetitions of decimal digits. | ||
| 747 | * \+ 1 decimal digit. | ||
| 748 | * \X One or more repetitions of hexadecimal digits. | ||
| 749 | * \x 1 hexadecimal digit. | ||
| 750 | * \A One or more repetitions of alphabet characters. | ||
| 751 | * \a 1 alphabet character. | ||
| 752 | * | ||
| 753 | * \- Subtraction operator. | ||
| 754 | * | ||
| 755 | * /\{dir\}/ '/' + 'One or more repetitions of dir/' (e.g. /dir/ /dir/dir/ | ||
| 756 | * /dir/dir/dir/ ). | ||
| 757 | */ | ||
| 758 | bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename, | ||
| 759 | const struct tomoyo_path_info *pattern) | ||
| 760 | { | ||
| 761 | const char *f = filename->name; | ||
| 762 | const char *p = pattern->name; | ||
| 763 | const int len = pattern->const_len; | ||
| 764 | |||
| 765 | /* If @pattern doesn't contain pattern, I can use strcmp(). */ | ||
| 766 | if (!pattern->is_patterned) | ||
| 767 | return !tomoyo_pathcmp(filename, pattern); | ||
| 768 | /* Don't compare directory and non-directory. */ | ||
| 769 | if (filename->is_dir != pattern->is_dir) | ||
| 770 | return false; | ||
| 771 | /* Compare the initial length without patterns. */ | ||
| 772 | if (strncmp(f, p, len)) | ||
| 773 | return false; | ||
| 774 | f += len; | ||
| 775 | p += len; | ||
| 776 | return tomoyo_path_matches_pattern2(f, p); | ||
| 777 | } | ||
| 778 | |||
| 779 | /** | ||
| 780 | * tomoyo_io_printf - Transactional printf() to "struct tomoyo_io_buffer" structure. | ||
| 781 | * | ||
| 782 | * @head: Pointer to "struct tomoyo_io_buffer". | ||
| 783 | * @fmt: The printf()'s format string, followed by parameters. | ||
| 784 | * | ||
| 785 | * Returns true if output was written, false otherwise. | ||
| 786 | * | ||
| 787 | * The snprintf() will truncate, but tomoyo_io_printf() won't. | ||
| 788 | */ | ||
| 789 | bool tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...) | ||
| 790 | { | ||
| 791 | va_list args; | ||
| 792 | int len; | ||
| 793 | int pos = head->read_avail; | ||
| 794 | int size = head->readbuf_size - pos; | ||
| 795 | |||
| 796 | if (size <= 0) | ||
| 797 | return false; | ||
| 798 | va_start(args, fmt); | ||
| 799 | len = vsnprintf(head->read_buf + pos, size, fmt, args); | ||
| 800 | va_end(args); | ||
| 801 | if (pos + len >= head->readbuf_size) | ||
| 802 | return false; | ||
| 803 | head->read_avail += len; | ||
| 804 | return true; | ||
| 805 | } | ||
| 806 | |||
| 807 | /** | ||
| 808 | * tomoyo_get_exe - Get tomoyo_realpath() of current process. | ||
| 809 | * | ||
| 810 | * Returns the tomoyo_realpath() of current process on success, NULL otherwise. | ||
| 811 | * | ||
| 812 | * This function uses kzalloc(), so the caller must call kfree() | ||
| 813 | * if this function didn't return NULL. | ||
| 814 | */ | ||
| 815 | static const char *tomoyo_get_exe(void) | ||
| 816 | { | ||
| 817 | struct mm_struct *mm = current->mm; | ||
| 818 | struct vm_area_struct *vma; | ||
| 819 | const char *cp = NULL; | ||
| 820 | |||
| 821 | if (!mm) | ||
| 822 | return NULL; | ||
| 823 | down_read(&mm->mmap_sem); | ||
| 824 | for (vma = mm->mmap; vma; vma = vma->vm_next) { | ||
| 825 | if ((vma->vm_flags & VM_EXECUTABLE) && vma->vm_file) { | ||
| 826 | cp = tomoyo_realpath_from_path(&vma->vm_file->f_path); | ||
| 827 | break; | ||
| 828 | } | 365 | } |
| 366 | profile->learning = &profile->preference; | ||
| 367 | tomoyo_set_uint(&profile->preference.learning_max_entry, value, | ||
| 368 | "max_entry"); | ||
| 369 | verbose = &profile->preference.learning_verbose; | ||
| 370 | goto set_verbose; | ||
| 829 | } | 371 | } |
| 830 | up_read(&mm->mmap_sem); | 372 | return; |
| 831 | return cp; | 373 | set_default: |
| 374 | *pref = &tomoyo_default_profile.preference; | ||
| 375 | return; | ||
| 376 | set_verbose: | ||
| 377 | tomoyo_set_bool(verbose, value, "verbose"); | ||
| 832 | } | 378 | } |
| 833 | 379 | ||
| 834 | /** | 380 | static int tomoyo_set_mode(char *name, const char *value, |
| 835 | * tomoyo_get_msg - Get warning message. | 381 | const bool use_default, |
| 836 | * | 382 | struct tomoyo_profile *profile) |
| 837 | * @is_enforce: Is it enforcing mode? | ||
| 838 | * | ||
| 839 | * Returns "ERROR" or "WARNING". | ||
| 840 | */ | ||
| 841 | const char *tomoyo_get_msg(const bool is_enforce) | ||
| 842 | { | 383 | { |
| 843 | if (is_enforce) | 384 | u8 i; |
| 844 | return "ERROR"; | 385 | u8 config; |
| 845 | else | 386 | if (!strcmp(name, "CONFIG")) { |
| 846 | return "WARNING"; | 387 | i = TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX; |
| 847 | } | 388 | config = profile->default_config; |
| 848 | 389 | } else if (tomoyo_str_starts(&name, "CONFIG::")) { | |
| 849 | /** | 390 | config = 0; |
| 850 | * tomoyo_check_flags - Check mode for specified functionality. | 391 | for (i = 0; i < TOMOYO_MAX_MAC_INDEX |
| 851 | * | 392 | + TOMOYO_MAX_MAC_CATEGORY_INDEX; i++) { |
| 852 | * @domain: Pointer to "struct tomoyo_domain_info". | 393 | if (strcmp(name, tomoyo_mac_keywords[i])) |
| 853 | * @index: The functionality to check mode. | 394 | continue; |
| 854 | * | 395 | config = profile->config[i]; |
| 855 | * TOMOYO checks only process context. | ||
| 856 | * This code disables TOMOYO's enforcement in case the function is called from | ||
| 857 | * interrupt context. | ||
| 858 | */ | ||
| 859 | unsigned int tomoyo_check_flags(const struct tomoyo_domain_info *domain, | ||
| 860 | const u8 index) | ||
| 861 | { | ||
| 862 | const u8 profile = domain->profile; | ||
| 863 | |||
| 864 | if (WARN_ON(in_interrupt())) | ||
| 865 | return 0; | ||
| 866 | return tomoyo_policy_loaded && index < TOMOYO_MAX_CONTROL_INDEX | ||
| 867 | #if TOMOYO_MAX_PROFILES != 256 | ||
| 868 | && profile < TOMOYO_MAX_PROFILES | ||
| 869 | #endif | ||
| 870 | && tomoyo_profile_ptr[profile] ? | ||
| 871 | tomoyo_profile_ptr[profile]->value[index] : 0; | ||
| 872 | } | ||
| 873 | |||
| 874 | /** | ||
| 875 | * tomoyo_verbose_mode - Check whether TOMOYO is verbose mode. | ||
| 876 | * | ||
| 877 | * @domain: Pointer to "struct tomoyo_domain_info". | ||
| 878 | * | ||
| 879 | * Returns true if domain policy violation warning should be printed to | ||
| 880 | * console. | ||
| 881 | */ | ||
| 882 | bool tomoyo_verbose_mode(const struct tomoyo_domain_info *domain) | ||
| 883 | { | ||
| 884 | return tomoyo_check_flags(domain, TOMOYO_VERBOSE) != 0; | ||
| 885 | } | ||
| 886 | |||
| 887 | /** | ||
| 888 | * tomoyo_domain_quota_is_ok - Check for domain's quota. | ||
| 889 | * | ||
| 890 | * @domain: Pointer to "struct tomoyo_domain_info". | ||
| 891 | * | ||
| 892 | * Returns true if the domain is not exceeded quota, false otherwise. | ||
| 893 | * | ||
| 894 | * Caller holds tomoyo_read_lock(). | ||
| 895 | */ | ||
| 896 | bool tomoyo_domain_quota_is_ok(struct tomoyo_domain_info * const domain) | ||
| 897 | { | ||
| 898 | unsigned int count = 0; | ||
| 899 | struct tomoyo_acl_info *ptr; | ||
| 900 | |||
| 901 | if (!domain) | ||
| 902 | return true; | ||
| 903 | list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { | ||
| 904 | switch (ptr->type) { | ||
| 905 | struct tomoyo_path_acl *acl; | ||
| 906 | u32 perm; | ||
| 907 | u8 i; | ||
| 908 | case TOMOYO_TYPE_PATH_ACL: | ||
| 909 | acl = container_of(ptr, struct tomoyo_path_acl, head); | ||
| 910 | perm = acl->perm | (((u32) acl->perm_high) << 16); | ||
| 911 | for (i = 0; i < TOMOYO_MAX_PATH_OPERATION; i++) | ||
| 912 | if (perm & (1 << i)) | ||
| 913 | count++; | ||
| 914 | if (perm & (1 << TOMOYO_TYPE_READ_WRITE)) | ||
| 915 | count -= 2; | ||
| 916 | break; | ||
| 917 | case TOMOYO_TYPE_PATH2_ACL: | ||
| 918 | perm = container_of(ptr, struct tomoyo_path2_acl, head) | ||
| 919 | ->perm; | ||
| 920 | for (i = 0; i < TOMOYO_MAX_PATH2_OPERATION; i++) | ||
| 921 | if (perm & (1 << i)) | ||
| 922 | count++; | ||
| 923 | break; | 396 | break; |
| 924 | } | 397 | } |
| 398 | if (i == TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX) | ||
| 399 | return -EINVAL; | ||
| 400 | } else { | ||
| 401 | return -EINVAL; | ||
| 925 | } | 402 | } |
| 926 | if (count < tomoyo_check_flags(domain, TOMOYO_MAX_ACCEPT_ENTRY)) | 403 | if (use_default) { |
| 927 | return true; | 404 | config = TOMOYO_CONFIG_USE_DEFAULT; |
| 928 | if (!domain->quota_warned) { | 405 | } else { |
| 929 | domain->quota_warned = true; | 406 | u8 mode; |
| 930 | printk(KERN_WARNING "TOMOYO-WARNING: " | 407 | for (mode = 0; mode < 4; mode++) |
| 931 | "Domain '%s' has so many ACLs to hold. " | 408 | if (strstr(value, tomoyo_mode[mode])) |
| 932 | "Stopped learning mode.\n", domain->domainname->name); | 409 | /* |
| 933 | } | 410 | * Update lower 3 bits in order to distinguish |
| 934 | return false; | 411 | * 'config' from 'TOMOYO_CONFIG_USE_DEAFULT'. |
| 935 | } | 412 | */ |
| 936 | 413 | config = (config & ~7) | mode; | |
| 937 | /** | ||
| 938 | * tomoyo_find_or_assign_new_profile - Create a new profile. | ||
| 939 | * | ||
| 940 | * @profile: Profile number to create. | ||
| 941 | * | ||
| 942 | * Returns pointer to "struct tomoyo_profile" on success, NULL otherwise. | ||
| 943 | */ | ||
| 944 | static struct tomoyo_profile *tomoyo_find_or_assign_new_profile(const unsigned | ||
| 945 | int profile) | ||
| 946 | { | ||
| 947 | struct tomoyo_profile *ptr = NULL; | ||
| 948 | int i; | ||
| 949 | |||
| 950 | if (profile >= TOMOYO_MAX_PROFILES) | ||
| 951 | return NULL; | ||
| 952 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) | ||
| 953 | return NULL; | ||
| 954 | ptr = tomoyo_profile_ptr[profile]; | ||
| 955 | if (ptr) | ||
| 956 | goto ok; | ||
| 957 | ptr = kmalloc(sizeof(*ptr), GFP_NOFS); | ||
| 958 | if (!tomoyo_memory_ok(ptr)) { | ||
| 959 | kfree(ptr); | ||
| 960 | ptr = NULL; | ||
| 961 | goto ok; | ||
| 962 | } | 414 | } |
| 963 | for (i = 0; i < TOMOYO_MAX_CONTROL_INDEX; i++) | 415 | if (i < TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX) |
| 964 | ptr->value[i] = tomoyo_control_array[i].current_value; | 416 | profile->config[i] = config; |
| 965 | mb(); /* Avoid out-of-order execution. */ | 417 | else if (config != TOMOYO_CONFIG_USE_DEFAULT) |
| 966 | tomoyo_profile_ptr[profile] = ptr; | 418 | profile->default_config = config; |
| 967 | ok: | 419 | return 0; |
| 968 | mutex_unlock(&tomoyo_policy_lock); | ||
| 969 | return ptr; | ||
| 970 | } | 420 | } |
| 971 | 421 | ||
| 972 | /** | 422 | /** |
| 973 | * tomoyo_write_profile - Write to profile table. | 423 | * tomoyo_write_profile - Write profile table. |
| 974 | * | 424 | * |
| 975 | * @head: Pointer to "struct tomoyo_io_buffer". | 425 | * @head: Pointer to "struct tomoyo_io_buffer". |
| 976 | * | 426 | * |
| @@ -980,153 +430,165 @@ static int tomoyo_write_profile(struct tomoyo_io_buffer *head) | |||
| 980 | { | 430 | { |
| 981 | char *data = head->write_buf; | 431 | char *data = head->write_buf; |
| 982 | unsigned int i; | 432 | unsigned int i; |
| 983 | unsigned int value; | 433 | bool use_default = false; |
| 984 | char *cp; | 434 | char *cp; |
| 985 | struct tomoyo_profile *profile; | 435 | struct tomoyo_profile *profile; |
| 986 | unsigned long num; | 436 | if (sscanf(data, "PROFILE_VERSION=%u", &tomoyo_profile_version) == 1) |
| 987 | 437 | return 0; | |
| 988 | cp = strchr(data, '-'); | 438 | i = simple_strtoul(data, &cp, 10); |
| 989 | if (cp) | 439 | if (data == cp) { |
| 990 | *cp = '\0'; | 440 | profile = &tomoyo_default_profile; |
| 991 | if (strict_strtoul(data, 10, &num)) | 441 | } else { |
| 992 | return -EINVAL; | 442 | if (*cp != '-') |
| 993 | if (cp) | 443 | return -EINVAL; |
| 994 | data = cp + 1; | 444 | data = cp + 1; |
| 995 | profile = tomoyo_find_or_assign_new_profile(num); | 445 | profile = tomoyo_assign_profile(i); |
| 996 | if (!profile) | 446 | if (!profile) |
| 997 | return -EINVAL; | 447 | return -EINVAL; |
| 448 | } | ||
| 998 | cp = strchr(data, '='); | 449 | cp = strchr(data, '='); |
| 999 | if (!cp) | 450 | if (!cp) |
| 1000 | return -EINVAL; | 451 | return -EINVAL; |
| 1001 | *cp = '\0'; | 452 | *cp++ = '\0'; |
| 453 | if (profile != &tomoyo_default_profile) | ||
| 454 | use_default = strstr(cp, "use_default") != NULL; | ||
| 455 | if (tomoyo_str_starts(&data, "PREFERENCE::")) { | ||
| 456 | tomoyo_set_pref(data, cp, use_default, profile); | ||
| 457 | return 0; | ||
| 458 | } | ||
| 459 | if (profile == &tomoyo_default_profile) | ||
| 460 | return -EINVAL; | ||
| 1002 | if (!strcmp(data, "COMMENT")) { | 461 | if (!strcmp(data, "COMMENT")) { |
| 1003 | const struct tomoyo_path_info *old_comment = profile->comment; | 462 | const struct tomoyo_path_info *old_comment = profile->comment; |
| 1004 | profile->comment = tomoyo_get_name(cp + 1); | 463 | profile->comment = tomoyo_get_name(cp); |
| 1005 | tomoyo_put_name(old_comment); | 464 | tomoyo_put_name(old_comment); |
| 1006 | return 0; | 465 | return 0; |
| 1007 | } | 466 | } |
| 1008 | for (i = 0; i < TOMOYO_MAX_CONTROL_INDEX; i++) { | 467 | return tomoyo_set_mode(data, cp, use_default, profile); |
| 1009 | if (strcmp(data, tomoyo_control_array[i].keyword)) | 468 | } |
| 1010 | continue; | 469 | |
| 1011 | if (sscanf(cp + 1, "%u", &value) != 1) { | 470 | static void tomoyo_print_preference(struct tomoyo_io_buffer *head, |
| 1012 | int j; | 471 | const int idx) |
| 1013 | const char **modes; | 472 | { |
| 1014 | switch (i) { | 473 | struct tomoyo_preference *pref = &tomoyo_default_profile.preference; |
| 1015 | case TOMOYO_VERBOSE: | 474 | const struct tomoyo_profile *profile = idx >= 0 ? |
| 1016 | modes = tomoyo_mode_2; | 475 | tomoyo_profile_ptr[idx] : NULL; |
| 1017 | break; | 476 | char buffer[16] = ""; |
| 1018 | default: | 477 | if (profile) { |
| 1019 | modes = tomoyo_mode_4; | 478 | buffer[sizeof(buffer) - 1] = '\0'; |
| 1020 | break; | 479 | snprintf(buffer, sizeof(buffer) - 1, "%u-", idx); |
| 1021 | } | ||
| 1022 | for (j = 0; j < 4; j++) { | ||
| 1023 | if (strcmp(cp + 1, modes[j])) | ||
| 1024 | continue; | ||
| 1025 | value = j; | ||
| 1026 | break; | ||
| 1027 | } | ||
| 1028 | if (j == 4) | ||
| 1029 | return -EINVAL; | ||
| 1030 | } else if (value > tomoyo_control_array[i].max_value) { | ||
| 1031 | value = tomoyo_control_array[i].max_value; | ||
| 1032 | } | ||
| 1033 | profile->value[i] = value; | ||
| 1034 | return 0; | ||
| 1035 | } | 480 | } |
| 1036 | return -EINVAL; | 481 | if (profile) { |
| 482 | pref = profile->learning; | ||
| 483 | if (pref == &tomoyo_default_profile.preference) | ||
| 484 | goto skip1; | ||
| 485 | } | ||
| 486 | tomoyo_io_printf(head, "%sPREFERENCE::%s={ " | ||
| 487 | "verbose=%s max_entry=%u }\n", | ||
| 488 | buffer, "learning", | ||
| 489 | tomoyo_yesno(pref->learning_verbose), | ||
| 490 | pref->learning_max_entry); | ||
| 491 | skip1: | ||
| 492 | if (profile) { | ||
| 493 | pref = profile->permissive; | ||
| 494 | if (pref == &tomoyo_default_profile.preference) | ||
| 495 | goto skip2; | ||
| 496 | } | ||
| 497 | tomoyo_io_printf(head, "%sPREFERENCE::%s={ verbose=%s }\n", | ||
| 498 | buffer, "permissive", | ||
| 499 | tomoyo_yesno(pref->permissive_verbose)); | ||
| 500 | skip2: | ||
| 501 | if (profile) { | ||
| 502 | pref = profile->enforcing; | ||
| 503 | if (pref == &tomoyo_default_profile.preference) | ||
| 504 | return; | ||
| 505 | } | ||
| 506 | tomoyo_io_printf(head, "%sPREFERENCE::%s={ verbose=%s }\n", | ||
| 507 | buffer, "enforcing", | ||
| 508 | tomoyo_yesno(pref->enforcing_verbose)); | ||
| 509 | } | ||
| 510 | |||
| 511 | static void tomoyo_print_config(struct tomoyo_io_buffer *head, const u8 config) | ||
| 512 | { | ||
| 513 | tomoyo_io_printf(head, "={ mode=%s }\n", tomoyo_mode[config & 3]); | ||
| 1037 | } | 514 | } |
| 1038 | 515 | ||
| 1039 | /** | 516 | /** |
| 1040 | * tomoyo_read_profile - Read from profile table. | 517 | * tomoyo_read_profile - Read profile table. |
| 1041 | * | 518 | * |
| 1042 | * @head: Pointer to "struct tomoyo_io_buffer". | 519 | * @head: Pointer to "struct tomoyo_io_buffer". |
| 1043 | * | ||
| 1044 | * Returns 0. | ||
| 1045 | */ | 520 | */ |
| 1046 | static int tomoyo_read_profile(struct tomoyo_io_buffer *head) | 521 | static void tomoyo_read_profile(struct tomoyo_io_buffer *head) |
| 1047 | { | 522 | { |
| 1048 | static const int total = TOMOYO_MAX_CONTROL_INDEX + 1; | 523 | u8 index; |
| 1049 | int step; | 524 | const struct tomoyo_profile *profile; |
| 1050 | 525 | next: | |
| 1051 | if (head->read_eof) | 526 | index = head->r.index; |
| 1052 | return 0; | 527 | profile = tomoyo_profile_ptr[index]; |
| 1053 | for (step = head->read_step; step < TOMOYO_MAX_PROFILES * total; | 528 | switch (head->r.step) { |
| 1054 | step++) { | 529 | case 0: |
| 1055 | const u8 index = step / total; | 530 | tomoyo_io_printf(head, "PROFILE_VERSION=%s\n", "20090903"); |
| 1056 | u8 type = step % total; | 531 | tomoyo_print_preference(head, -1); |
| 1057 | const struct tomoyo_profile *profile | 532 | head->r.step++; |
| 1058 | = tomoyo_profile_ptr[index]; | 533 | break; |
| 1059 | head->read_step = step; | 534 | case 1: |
| 1060 | if (!profile) | 535 | for ( ; head->r.index < TOMOYO_MAX_PROFILES; |
| 1061 | continue; | 536 | head->r.index++) |
| 1062 | if (!type) { /* Print profile' comment tag. */ | 537 | if (tomoyo_profile_ptr[head->r.index]) |
| 1063 | if (!tomoyo_io_printf(head, "%u-COMMENT=%s\n", | ||
| 1064 | index, profile->comment ? | ||
| 1065 | profile->comment->name : "")) | ||
| 1066 | break; | 538 | break; |
| 1067 | continue; | 539 | if (head->r.index == TOMOYO_MAX_PROFILES) |
| 540 | return; | ||
| 541 | head->r.step++; | ||
| 542 | break; | ||
| 543 | case 2: | ||
| 544 | { | ||
| 545 | const struct tomoyo_path_info *comment = | ||
| 546 | profile->comment; | ||
| 547 | tomoyo_io_printf(head, "%u-COMMENT=", index); | ||
| 548 | tomoyo_set_string(head, comment ? comment->name : ""); | ||
| 549 | tomoyo_set_lf(head); | ||
| 550 | head->r.step++; | ||
| 1068 | } | 551 | } |
| 1069 | type--; | 552 | break; |
| 1070 | if (type < TOMOYO_MAX_CONTROL_INDEX) { | 553 | case 3: |
| 1071 | const unsigned int value = profile->value[type]; | 554 | { |
| 1072 | const char **modes = NULL; | 555 | tomoyo_io_printf(head, "%u-%s", index, "CONFIG"); |
| 1073 | const char *keyword | 556 | tomoyo_print_config(head, profile->default_config); |
| 1074 | = tomoyo_control_array[type].keyword; | 557 | head->r.bit = 0; |
| 1075 | switch (tomoyo_control_array[type].max_value) { | 558 | head->r.step++; |
| 1076 | case 3: | 559 | } |
| 1077 | modes = tomoyo_mode_4; | 560 | break; |
| 1078 | break; | 561 | case 4: |
| 1079 | case 1: | 562 | for ( ; head->r.bit < TOMOYO_MAX_MAC_INDEX |
| 1080 | modes = tomoyo_mode_2; | 563 | + TOMOYO_MAX_MAC_CATEGORY_INDEX; head->r.bit++) { |
| 1081 | break; | 564 | const u8 i = head->r.bit; |
| 1082 | } | 565 | const u8 config = profile->config[i]; |
| 1083 | if (modes) { | 566 | if (config == TOMOYO_CONFIG_USE_DEFAULT) |
| 1084 | if (!tomoyo_io_printf(head, "%u-%s=%s\n", index, | 567 | continue; |
| 1085 | keyword, modes[value])) | 568 | tomoyo_io_printf(head, "%u-%s%s", index, "CONFIG::", |
| 1086 | break; | 569 | tomoyo_mac_keywords[i]); |
| 1087 | } else { | 570 | tomoyo_print_config(head, config); |
| 1088 | if (!tomoyo_io_printf(head, "%u-%s=%u\n", index, | 571 | head->r.bit++; |
| 1089 | keyword, value)) | 572 | break; |
| 1090 | break; | 573 | } |
| 1091 | } | 574 | if (head->r.bit == TOMOYO_MAX_MAC_INDEX |
| 575 | + TOMOYO_MAX_MAC_CATEGORY_INDEX) { | ||
| 576 | tomoyo_print_preference(head, index); | ||
| 577 | head->r.index++; | ||
| 578 | head->r.step = 1; | ||
| 1092 | } | 579 | } |
| 580 | break; | ||
| 1093 | } | 581 | } |
| 1094 | if (step == TOMOYO_MAX_PROFILES * total) | 582 | if (tomoyo_flush(head)) |
| 1095 | head->read_eof = true; | 583 | goto next; |
| 1096 | return 0; | ||
| 1097 | } | 584 | } |
| 1098 | 585 | ||
| 1099 | /* | 586 | static bool tomoyo_same_manager(const struct tomoyo_acl_head *a, |
| 1100 | * tomoyo_policy_manager_list is used for holding list of domainnames or | 587 | const struct tomoyo_acl_head *b) |
| 1101 | * programs which are permitted to modify configuration via | 588 | { |
| 1102 | * /sys/kernel/security/tomoyo/ interface. | 589 | return container_of(a, struct tomoyo_manager, head)->manager == |
| 1103 | * | 590 | container_of(b, struct tomoyo_manager, head)->manager; |
| 1104 | * An entry is added by | 591 | } |
| 1105 | * | ||
| 1106 | * # echo '<kernel> /sbin/mingetty /bin/login /bin/bash' > \ | ||
| 1107 | * /sys/kernel/security/tomoyo/manager | ||
| 1108 | * (if you want to specify by a domainname) | ||
| 1109 | * | ||
| 1110 | * or | ||
| 1111 | * | ||
| 1112 | * # echo '/usr/lib/ccs/editpolicy' > /sys/kernel/security/tomoyo/manager | ||
| 1113 | * (if you want to specify by a program's location) | ||
| 1114 | * | ||
| 1115 | * and is deleted by | ||
| 1116 | * | ||
| 1117 | * # echo 'delete <kernel> /sbin/mingetty /bin/login /bin/bash' > \ | ||
| 1118 | * /sys/kernel/security/tomoyo/manager | ||
| 1119 | * | ||
| 1120 | * or | ||
| 1121 | * | ||
| 1122 | * # echo 'delete /usr/lib/ccs/editpolicy' > \ | ||
| 1123 | * /sys/kernel/security/tomoyo/manager | ||
| 1124 | * | ||
| 1125 | * and all entries are retrieved by | ||
| 1126 | * | ||
| 1127 | * # cat /sys/kernel/security/tomoyo/manager | ||
| 1128 | */ | ||
| 1129 | LIST_HEAD(tomoyo_policy_manager_list); | ||
| 1130 | 592 | ||
| 1131 | /** | 593 | /** |
| 1132 | * tomoyo_update_manager_entry - Add a manager entry. | 594 | * tomoyo_update_manager_entry - Add a manager entry. |
| @@ -1141,47 +603,29 @@ LIST_HEAD(tomoyo_policy_manager_list); | |||
| 1141 | static int tomoyo_update_manager_entry(const char *manager, | 603 | static int tomoyo_update_manager_entry(const char *manager, |
| 1142 | const bool is_delete) | 604 | const bool is_delete) |
| 1143 | { | 605 | { |
| 1144 | struct tomoyo_policy_manager_entry *ptr; | 606 | struct tomoyo_manager e = { }; |
| 1145 | struct tomoyo_policy_manager_entry e = { }; | 607 | int error; |
| 1146 | int error = is_delete ? -ENOENT : -ENOMEM; | ||
| 1147 | 608 | ||
| 1148 | if (tomoyo_is_domain_def(manager)) { | 609 | if (tomoyo_domain_def(manager)) { |
| 1149 | if (!tomoyo_is_correct_domain(manager)) | 610 | if (!tomoyo_correct_domain(manager)) |
| 1150 | return -EINVAL; | 611 | return -EINVAL; |
| 1151 | e.is_domain = true; | 612 | e.is_domain = true; |
| 1152 | } else { | 613 | } else { |
| 1153 | if (!tomoyo_is_correct_path(manager, 1, -1, -1)) | 614 | if (!tomoyo_correct_path(manager)) |
| 1154 | return -EINVAL; | 615 | return -EINVAL; |
| 1155 | } | 616 | } |
| 1156 | e.manager = tomoyo_get_name(manager); | 617 | e.manager = tomoyo_get_name(manager); |
| 1157 | if (!e.manager) | 618 | if (!e.manager) |
| 1158 | return -ENOMEM; | 619 | return -ENOMEM; |
| 1159 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) | 620 | error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, |
| 1160 | goto out; | 621 | &tomoyo_policy_list[TOMOYO_ID_MANAGER], |
| 1161 | list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, list) { | 622 | tomoyo_same_manager); |
| 1162 | if (ptr->manager != e.manager) | ||
| 1163 | continue; | ||
| 1164 | ptr->is_deleted = is_delete; | ||
| 1165 | error = 0; | ||
| 1166 | break; | ||
| 1167 | } | ||
| 1168 | if (!is_delete && error) { | ||
| 1169 | struct tomoyo_policy_manager_entry *entry = | ||
| 1170 | tomoyo_commit_ok(&e, sizeof(e)); | ||
| 1171 | if (entry) { | ||
| 1172 | list_add_tail_rcu(&entry->list, | ||
| 1173 | &tomoyo_policy_manager_list); | ||
| 1174 | error = 0; | ||
| 1175 | } | ||
| 1176 | } | ||
| 1177 | mutex_unlock(&tomoyo_policy_lock); | ||
| 1178 | out: | ||
| 1179 | tomoyo_put_name(e.manager); | 623 | tomoyo_put_name(e.manager); |
| 1180 | return error; | 624 | return error; |
| 1181 | } | 625 | } |
| 1182 | 626 | ||
| 1183 | /** | 627 | /** |
| 1184 | * tomoyo_write_manager_policy - Write manager policy. | 628 | * tomoyo_write_manager - Write manager policy. |
| 1185 | * | 629 | * |
| 1186 | * @head: Pointer to "struct tomoyo_io_buffer". | 630 | * @head: Pointer to "struct tomoyo_io_buffer". |
| 1187 | * | 631 | * |
| @@ -1189,7 +633,7 @@ static int tomoyo_update_manager_entry(const char *manager, | |||
| 1189 | * | 633 | * |
| 1190 | * Caller holds tomoyo_read_lock(). | 634 | * Caller holds tomoyo_read_lock(). |
| 1191 | */ | 635 | */ |
| 1192 | static int tomoyo_write_manager_policy(struct tomoyo_io_buffer *head) | 636 | static int tomoyo_write_manager(struct tomoyo_io_buffer *head) |
| 1193 | { | 637 | { |
| 1194 | char *data = head->write_buf; | 638 | char *data = head->write_buf; |
| 1195 | bool is_delete = tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE); | 639 | bool is_delete = tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE); |
| @@ -1202,47 +646,41 @@ static int tomoyo_write_manager_policy(struct tomoyo_io_buffer *head) | |||
| 1202 | } | 646 | } |
| 1203 | 647 | ||
| 1204 | /** | 648 | /** |
| 1205 | * tomoyo_read_manager_policy - Read manager policy. | 649 | * tomoyo_read_manager - Read manager policy. |
| 1206 | * | 650 | * |
| 1207 | * @head: Pointer to "struct tomoyo_io_buffer". | 651 | * @head: Pointer to "struct tomoyo_io_buffer". |
| 1208 | * | 652 | * |
| 1209 | * Returns 0. | ||
| 1210 | * | ||
| 1211 | * Caller holds tomoyo_read_lock(). | 653 | * Caller holds tomoyo_read_lock(). |
| 1212 | */ | 654 | */ |
| 1213 | static int tomoyo_read_manager_policy(struct tomoyo_io_buffer *head) | 655 | static void tomoyo_read_manager(struct tomoyo_io_buffer *head) |
| 1214 | { | 656 | { |
| 1215 | struct list_head *pos; | 657 | if (head->r.eof) |
| 1216 | bool done = true; | 658 | return; |
| 1217 | 659 | list_for_each_cookie(head->r.acl, | |
| 1218 | if (head->read_eof) | 660 | &tomoyo_policy_list[TOMOYO_ID_MANAGER]) { |
| 1219 | return 0; | 661 | struct tomoyo_manager *ptr = |
| 1220 | list_for_each_cookie(pos, head->read_var2, | 662 | list_entry(head->r.acl, typeof(*ptr), head.list); |
| 1221 | &tomoyo_policy_manager_list) { | 663 | if (ptr->head.is_deleted) |
| 1222 | struct tomoyo_policy_manager_entry *ptr; | ||
| 1223 | ptr = list_entry(pos, struct tomoyo_policy_manager_entry, | ||
| 1224 | list); | ||
| 1225 | if (ptr->is_deleted) | ||
| 1226 | continue; | 664 | continue; |
| 1227 | done = tomoyo_io_printf(head, "%s\n", ptr->manager->name); | 665 | if (!tomoyo_flush(head)) |
| 1228 | if (!done) | 666 | return; |
| 1229 | break; | 667 | tomoyo_set_string(head, ptr->manager->name); |
| 668 | tomoyo_set_lf(head); | ||
| 1230 | } | 669 | } |
| 1231 | head->read_eof = done; | 670 | head->r.eof = true; |
| 1232 | return 0; | ||
| 1233 | } | 671 | } |
| 1234 | 672 | ||
| 1235 | /** | 673 | /** |
| 1236 | * tomoyo_is_policy_manager - Check whether the current process is a policy manager. | 674 | * tomoyo_manager - Check whether the current process is a policy manager. |
| 1237 | * | 675 | * |
| 1238 | * Returns true if the current process is permitted to modify policy | 676 | * Returns true if the current process is permitted to modify policy |
| 1239 | * via /sys/kernel/security/tomoyo/ interface. | 677 | * via /sys/kernel/security/tomoyo/ interface. |
| 1240 | * | 678 | * |
| 1241 | * Caller holds tomoyo_read_lock(). | 679 | * Caller holds tomoyo_read_lock(). |
| 1242 | */ | 680 | */ |
| 1243 | static bool tomoyo_is_policy_manager(void) | 681 | static bool tomoyo_manager(void) |
| 1244 | { | 682 | { |
| 1245 | struct tomoyo_policy_manager_entry *ptr; | 683 | struct tomoyo_manager *ptr; |
| 1246 | const char *exe; | 684 | const char *exe; |
| 1247 | const struct task_struct *task = current; | 685 | const struct task_struct *task = current; |
| 1248 | const struct tomoyo_path_info *domainname = tomoyo_domain()->domainname; | 686 | const struct tomoyo_path_info *domainname = tomoyo_domain()->domainname; |
| @@ -1252,8 +690,9 @@ static bool tomoyo_is_policy_manager(void) | |||
| 1252 | return true; | 690 | return true; |
| 1253 | if (!tomoyo_manage_by_non_root && (task->cred->uid || task->cred->euid)) | 691 | if (!tomoyo_manage_by_non_root && (task->cred->uid || task->cred->euid)) |
| 1254 | return false; | 692 | return false; |
| 1255 | list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, list) { | 693 | list_for_each_entry_rcu(ptr, &tomoyo_policy_list[TOMOYO_ID_MANAGER], |
| 1256 | if (!ptr->is_deleted && ptr->is_domain | 694 | head.list) { |
| 695 | if (!ptr->head.is_deleted && ptr->is_domain | ||
| 1257 | && !tomoyo_pathcmp(domainname, ptr->manager)) { | 696 | && !tomoyo_pathcmp(domainname, ptr->manager)) { |
| 1258 | found = true; | 697 | found = true; |
| 1259 | break; | 698 | break; |
| @@ -1264,8 +703,9 @@ static bool tomoyo_is_policy_manager(void) | |||
| 1264 | exe = tomoyo_get_exe(); | 703 | exe = tomoyo_get_exe(); |
| 1265 | if (!exe) | 704 | if (!exe) |
| 1266 | return false; | 705 | return false; |
| 1267 | list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, list) { | 706 | list_for_each_entry_rcu(ptr, &tomoyo_policy_list[TOMOYO_ID_MANAGER], |
| 1268 | if (!ptr->is_deleted && !ptr->is_domain | 707 | head.list) { |
| 708 | if (!ptr->head.is_deleted && !ptr->is_domain | ||
| 1269 | && !strcmp(exe, ptr->manager->name)) { | 709 | && !strcmp(exe, ptr->manager->name)) { |
| 1270 | found = true; | 710 | found = true; |
| 1271 | break; | 711 | break; |
| @@ -1285,7 +725,7 @@ static bool tomoyo_is_policy_manager(void) | |||
| 1285 | } | 725 | } |
| 1286 | 726 | ||
| 1287 | /** | 727 | /** |
| 1288 | * tomoyo_is_select_one - Parse select command. | 728 | * tomoyo_select_one - Parse select command. |
| 1289 | * | 729 | * |
| 1290 | * @head: Pointer to "struct tomoyo_io_buffer". | 730 | * @head: Pointer to "struct tomoyo_io_buffer". |
| 1291 | * @data: String to parse. | 731 | * @data: String to parse. |
| @@ -1294,23 +734,31 @@ static bool tomoyo_is_policy_manager(void) | |||
| 1294 | * | 734 | * |
| 1295 | * Caller holds tomoyo_read_lock(). | 735 | * Caller holds tomoyo_read_lock(). |
| 1296 | */ | 736 | */ |
| 1297 | static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head, | 737 | static bool tomoyo_select_one(struct tomoyo_io_buffer *head, const char *data) |
| 1298 | const char *data) | ||
| 1299 | { | 738 | { |
| 1300 | unsigned int pid; | 739 | unsigned int pid; |
| 1301 | struct tomoyo_domain_info *domain = NULL; | 740 | struct tomoyo_domain_info *domain = NULL; |
| 741 | bool global_pid = false; | ||
| 1302 | 742 | ||
| 1303 | if (sscanf(data, "pid=%u", &pid) == 1) { | 743 | if (!strcmp(data, "allow_execute")) { |
| 744 | head->r.print_execute_only = true; | ||
| 745 | return true; | ||
| 746 | } | ||
| 747 | if (sscanf(data, "pid=%u", &pid) == 1 || | ||
| 748 | (global_pid = true, sscanf(data, "global-pid=%u", &pid) == 1)) { | ||
| 1304 | struct task_struct *p; | 749 | struct task_struct *p; |
| 1305 | rcu_read_lock(); | 750 | rcu_read_lock(); |
| 1306 | read_lock(&tasklist_lock); | 751 | read_lock(&tasklist_lock); |
| 1307 | p = find_task_by_vpid(pid); | 752 | if (global_pid) |
| 753 | p = find_task_by_pid_ns(pid, &init_pid_ns); | ||
| 754 | else | ||
| 755 | p = find_task_by_vpid(pid); | ||
| 1308 | if (p) | 756 | if (p) |
| 1309 | domain = tomoyo_real_domain(p); | 757 | domain = tomoyo_real_domain(p); |
| 1310 | read_unlock(&tasklist_lock); | 758 | read_unlock(&tasklist_lock); |
| 1311 | rcu_read_unlock(); | 759 | rcu_read_unlock(); |
| 1312 | } else if (!strncmp(data, "domain=", 7)) { | 760 | } else if (!strncmp(data, "domain=", 7)) { |
| 1313 | if (tomoyo_is_domain_def(data + 7)) | 761 | if (tomoyo_domain_def(data + 7)) |
| 1314 | domain = tomoyo_find_domain(data + 7); | 762 | domain = tomoyo_find_domain(data + 7); |
| 1315 | } else | 763 | } else |
| 1316 | return false; | 764 | return false; |
| @@ -1318,24 +766,13 @@ static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head, | |||
| 1318 | /* Accessing read_buf is safe because head->io_sem is held. */ | 766 | /* Accessing read_buf is safe because head->io_sem is held. */ |
| 1319 | if (!head->read_buf) | 767 | if (!head->read_buf) |
| 1320 | return true; /* Do nothing if open(O_WRONLY). */ | 768 | return true; /* Do nothing if open(O_WRONLY). */ |
| 1321 | head->read_avail = 0; | 769 | memset(&head->r, 0, sizeof(head->r)); |
| 770 | head->r.print_this_domain_only = true; | ||
| 771 | head->r.eof = !domain; | ||
| 772 | head->r.domain = &domain->list; | ||
| 1322 | tomoyo_io_printf(head, "# select %s\n", data); | 773 | tomoyo_io_printf(head, "# select %s\n", data); |
| 1323 | head->read_single_domain = true; | 774 | if (domain && domain->is_deleted) |
| 1324 | head->read_eof = !domain; | 775 | tomoyo_io_printf(head, "# This is a deleted domain.\n"); |
| 1325 | if (domain) { | ||
| 1326 | struct tomoyo_domain_info *d; | ||
| 1327 | head->read_var1 = NULL; | ||
| 1328 | list_for_each_entry_rcu(d, &tomoyo_domain_list, list) { | ||
| 1329 | if (d == domain) | ||
| 1330 | break; | ||
| 1331 | head->read_var1 = &d->list; | ||
| 1332 | } | ||
| 1333 | head->read_var2 = NULL; | ||
| 1334 | head->read_bit = 0; | ||
| 1335 | head->read_step = 0; | ||
| 1336 | if (domain->is_deleted) | ||
| 1337 | tomoyo_io_printf(head, "# This is a deleted domain.\n"); | ||
| 1338 | } | ||
| 1339 | return true; | 776 | return true; |
| 1340 | } | 777 | } |
| 1341 | 778 | ||
| @@ -1373,7 +810,7 @@ static int tomoyo_delete_domain(char *domainname) | |||
| 1373 | } | 810 | } |
| 1374 | 811 | ||
| 1375 | /** | 812 | /** |
| 1376 | * tomoyo_write_domain_policy - Write domain policy. | 813 | * tomoyo_write_domain2 - Write domain policy. |
| 1377 | * | 814 | * |
| 1378 | * @head: Pointer to "struct tomoyo_io_buffer". | 815 | * @head: Pointer to "struct tomoyo_io_buffer". |
| 1379 | * | 816 | * |
| @@ -1381,7 +818,24 @@ static int tomoyo_delete_domain(char *domainname) | |||
| 1381 | * | 818 | * |
| 1382 | * Caller holds tomoyo_read_lock(). | 819 | * Caller holds tomoyo_read_lock(). |
| 1383 | */ | 820 | */ |
| 1384 | static int tomoyo_write_domain_policy(struct tomoyo_io_buffer *head) | 821 | static int tomoyo_write_domain2(char *data, struct tomoyo_domain_info *domain, |
| 822 | const bool is_delete) | ||
| 823 | { | ||
| 824 | if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_ALLOW_MOUNT)) | ||
| 825 | return tomoyo_write_mount(data, domain, is_delete); | ||
| 826 | return tomoyo_write_file(data, domain, is_delete); | ||
| 827 | } | ||
| 828 | |||
| 829 | /** | ||
| 830 | * tomoyo_write_domain - Write domain policy. | ||
| 831 | * | ||
| 832 | * @head: Pointer to "struct tomoyo_io_buffer". | ||
| 833 | * | ||
| 834 | * Returns 0 on success, negative value otherwise. | ||
| 835 | * | ||
| 836 | * Caller holds tomoyo_read_lock(). | ||
| 837 | */ | ||
| 838 | static int tomoyo_write_domain(struct tomoyo_io_buffer *head) | ||
| 1385 | { | 839 | { |
| 1386 | char *data = head->write_buf; | 840 | char *data = head->write_buf; |
| 1387 | struct tomoyo_domain_info *domain = head->write_var1; | 841 | struct tomoyo_domain_info *domain = head->write_var1; |
| @@ -1393,19 +847,19 @@ static int tomoyo_write_domain_policy(struct tomoyo_io_buffer *head) | |||
| 1393 | is_delete = true; | 847 | is_delete = true; |
| 1394 | else if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_SELECT)) | 848 | else if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_SELECT)) |
| 1395 | is_select = true; | 849 | is_select = true; |
| 1396 | if (is_select && tomoyo_is_select_one(head, data)) | 850 | if (is_select && tomoyo_select_one(head, data)) |
| 1397 | return 0; | 851 | return 0; |
| 1398 | /* Don't allow updating policies by non manager programs. */ | 852 | /* Don't allow updating policies by non manager programs. */ |
| 1399 | if (!tomoyo_is_policy_manager()) | 853 | if (!tomoyo_manager()) |
| 1400 | return -EPERM; | 854 | return -EPERM; |
| 1401 | if (tomoyo_is_domain_def(data)) { | 855 | if (tomoyo_domain_def(data)) { |
| 1402 | domain = NULL; | 856 | domain = NULL; |
| 1403 | if (is_delete) | 857 | if (is_delete) |
| 1404 | tomoyo_delete_domain(data); | 858 | tomoyo_delete_domain(data); |
| 1405 | else if (is_select) | 859 | else if (is_select) |
| 1406 | domain = tomoyo_find_domain(data); | 860 | domain = tomoyo_find_domain(data); |
| 1407 | else | 861 | else |
| 1408 | domain = tomoyo_find_or_assign_new_domain(data, 0); | 862 | domain = tomoyo_assign_domain(data, 0); |
| 1409 | head->write_var1 = domain; | 863 | head->write_var1 = domain; |
| 1410 | return 0; | 864 | return 0; |
| 1411 | } | 865 | } |
| @@ -1422,179 +876,198 @@ static int tomoyo_write_domain_policy(struct tomoyo_io_buffer *head) | |||
| 1422 | domain->ignore_global_allow_read = !is_delete; | 876 | domain->ignore_global_allow_read = !is_delete; |
| 1423 | return 0; | 877 | return 0; |
| 1424 | } | 878 | } |
| 1425 | return tomoyo_write_file_policy(data, domain, is_delete); | 879 | if (!strcmp(data, TOMOYO_KEYWORD_QUOTA_EXCEEDED)) { |
| 880 | domain->quota_warned = !is_delete; | ||
| 881 | return 0; | ||
| 882 | } | ||
| 883 | if (!strcmp(data, TOMOYO_KEYWORD_TRANSITION_FAILED)) { | ||
| 884 | domain->transition_failed = !is_delete; | ||
| 885 | return 0; | ||
| 886 | } | ||
| 887 | return tomoyo_write_domain2(data, domain, is_delete); | ||
| 1426 | } | 888 | } |
| 1427 | 889 | ||
| 1428 | /** | 890 | /** |
| 1429 | * tomoyo_print_path_acl - Print a single path ACL entry. | 891 | * tomoyo_fns - Find next set bit. |
| 1430 | * | 892 | * |
| 1431 | * @head: Pointer to "struct tomoyo_io_buffer". | 893 | * @perm: 8 bits value. |
| 1432 | * @ptr: Pointer to "struct tomoyo_path_acl". | 894 | * @bit: First bit to find. |
| 1433 | * | 895 | * |
| 1434 | * Returns true on success, false otherwise. | 896 | * Returns next on-bit on success, 8 otherwise. |
| 1435 | */ | 897 | */ |
| 1436 | static bool tomoyo_print_path_acl(struct tomoyo_io_buffer *head, | 898 | static u8 tomoyo_fns(const u8 perm, u8 bit) |
| 1437 | struct tomoyo_path_acl *ptr) | ||
| 1438 | { | 899 | { |
| 1439 | int pos; | 900 | for ( ; bit < 8; bit++) |
| 1440 | u8 bit; | 901 | if (perm & (1 << bit)) |
| 1441 | const u32 perm = ptr->perm | (((u32) ptr->perm_high) << 16); | 902 | break; |
| 1442 | 903 | return bit; | |
| 1443 | for (bit = head->read_bit; bit < TOMOYO_MAX_PATH_OPERATION; bit++) { | ||
| 1444 | if (!(perm & (1 << bit))) | ||
| 1445 | continue; | ||
| 1446 | /* Print "read/write" instead of "read" and "write". */ | ||
| 1447 | if ((bit == TOMOYO_TYPE_READ || bit == TOMOYO_TYPE_WRITE) | ||
| 1448 | && (perm & (1 << TOMOYO_TYPE_READ_WRITE))) | ||
| 1449 | continue; | ||
| 1450 | pos = head->read_avail; | ||
| 1451 | if (!tomoyo_io_printf(head, "allow_%s ", | ||
| 1452 | tomoyo_path2keyword(bit)) || | ||
| 1453 | !tomoyo_print_name_union(head, &ptr->name) || | ||
| 1454 | !tomoyo_io_printf(head, "\n")) | ||
| 1455 | goto out; | ||
| 1456 | } | ||
| 1457 | head->read_bit = 0; | ||
| 1458 | return true; | ||
| 1459 | out: | ||
| 1460 | head->read_bit = bit; | ||
| 1461 | head->read_avail = pos; | ||
| 1462 | return false; | ||
| 1463 | } | 904 | } |
| 1464 | 905 | ||
| 1465 | /** | 906 | /** |
| 1466 | * tomoyo_print_path2_acl - Print a double path ACL entry. | 907 | * tomoyo_print_entry - Print an ACL entry. |
| 1467 | * | 908 | * |
| 1468 | * @head: Pointer to "struct tomoyo_io_buffer". | 909 | * @head: Pointer to "struct tomoyo_io_buffer". |
| 1469 | * @ptr: Pointer to "struct tomoyo_path2_acl". | 910 | * @acl: Pointer to an ACL entry. |
| 1470 | * | 911 | * |
| 1471 | * Returns true on success, false otherwise. | 912 | * Returns true on success, false otherwise. |
| 1472 | */ | 913 | */ |
| 1473 | static bool tomoyo_print_path2_acl(struct tomoyo_io_buffer *head, | 914 | static bool tomoyo_print_entry(struct tomoyo_io_buffer *head, |
| 1474 | struct tomoyo_path2_acl *ptr) | 915 | struct tomoyo_acl_info *acl) |
| 1475 | { | 916 | { |
| 1476 | int pos; | 917 | const u8 acl_type = acl->type; |
| 1477 | const u8 perm = ptr->perm; | ||
| 1478 | u8 bit; | 918 | u8 bit; |
| 1479 | 919 | ||
| 1480 | for (bit = head->read_bit; bit < TOMOYO_MAX_PATH2_OPERATION; bit++) { | 920 | if (acl->is_deleted) |
| 1481 | if (!(perm & (1 << bit))) | 921 | return true; |
| 1482 | continue; | 922 | next: |
| 1483 | pos = head->read_avail; | 923 | bit = head->r.bit; |
| 1484 | if (!tomoyo_io_printf(head, "allow_%s ", | 924 | if (!tomoyo_flush(head)) |
| 1485 | tomoyo_path22keyword(bit)) || | 925 | return false; |
| 1486 | !tomoyo_print_name_union(head, &ptr->name1) || | 926 | else if (acl_type == TOMOYO_TYPE_PATH_ACL) { |
| 1487 | !tomoyo_print_name_union(head, &ptr->name2) || | 927 | struct tomoyo_path_acl *ptr = |
| 1488 | !tomoyo_io_printf(head, "\n")) | 928 | container_of(acl, typeof(*ptr), head); |
| 1489 | goto out; | 929 | const u16 perm = ptr->perm; |
| 930 | for ( ; bit < TOMOYO_MAX_PATH_OPERATION; bit++) { | ||
| 931 | if (!(perm & (1 << bit))) | ||
| 932 | continue; | ||
| 933 | if (head->r.print_execute_only && | ||
| 934 | bit != TOMOYO_TYPE_EXECUTE) | ||
| 935 | continue; | ||
| 936 | /* Print "read/write" instead of "read" and "write". */ | ||
| 937 | if ((bit == TOMOYO_TYPE_READ || | ||
| 938 | bit == TOMOYO_TYPE_WRITE) | ||
| 939 | && (perm & (1 << TOMOYO_TYPE_READ_WRITE))) | ||
| 940 | continue; | ||
| 941 | break; | ||
| 942 | } | ||
| 943 | if (bit >= TOMOYO_MAX_PATH_OPERATION) | ||
| 944 | goto done; | ||
| 945 | tomoyo_io_printf(head, "allow_%s", tomoyo_path_keyword[bit]); | ||
| 946 | tomoyo_print_name_union(head, &ptr->name); | ||
| 947 | } else if (head->r.print_execute_only) { | ||
| 948 | return true; | ||
| 949 | } else if (acl_type == TOMOYO_TYPE_PATH2_ACL) { | ||
| 950 | struct tomoyo_path2_acl *ptr = | ||
| 951 | container_of(acl, typeof(*ptr), head); | ||
| 952 | bit = tomoyo_fns(ptr->perm, bit); | ||
| 953 | if (bit >= TOMOYO_MAX_PATH2_OPERATION) | ||
| 954 | goto done; | ||
| 955 | tomoyo_io_printf(head, "allow_%s", tomoyo_path2_keyword[bit]); | ||
| 956 | tomoyo_print_name_union(head, &ptr->name1); | ||
| 957 | tomoyo_print_name_union(head, &ptr->name2); | ||
| 958 | } else if (acl_type == TOMOYO_TYPE_PATH_NUMBER_ACL) { | ||
| 959 | struct tomoyo_path_number_acl *ptr = | ||
| 960 | container_of(acl, typeof(*ptr), head); | ||
| 961 | bit = tomoyo_fns(ptr->perm, bit); | ||
| 962 | if (bit >= TOMOYO_MAX_PATH_NUMBER_OPERATION) | ||
| 963 | goto done; | ||
| 964 | tomoyo_io_printf(head, "allow_%s", | ||
| 965 | tomoyo_path_number_keyword[bit]); | ||
| 966 | tomoyo_print_name_union(head, &ptr->name); | ||
| 967 | tomoyo_print_number_union(head, &ptr->number); | ||
| 968 | } else if (acl_type == TOMOYO_TYPE_MKDEV_ACL) { | ||
| 969 | struct tomoyo_mkdev_acl *ptr = | ||
| 970 | container_of(acl, typeof(*ptr), head); | ||
| 971 | bit = tomoyo_fns(ptr->perm, bit); | ||
| 972 | if (bit >= TOMOYO_MAX_MKDEV_OPERATION) | ||
| 973 | goto done; | ||
| 974 | tomoyo_io_printf(head, "allow_%s", tomoyo_mkdev_keyword[bit]); | ||
| 975 | tomoyo_print_name_union(head, &ptr->name); | ||
| 976 | tomoyo_print_number_union(head, &ptr->mode); | ||
| 977 | tomoyo_print_number_union(head, &ptr->major); | ||
| 978 | tomoyo_print_number_union(head, &ptr->minor); | ||
| 979 | } else if (acl_type == TOMOYO_TYPE_MOUNT_ACL) { | ||
| 980 | struct tomoyo_mount_acl *ptr = | ||
| 981 | container_of(acl, typeof(*ptr), head); | ||
| 982 | tomoyo_io_printf(head, "allow_mount"); | ||
| 983 | tomoyo_print_name_union(head, &ptr->dev_name); | ||
| 984 | tomoyo_print_name_union(head, &ptr->dir_name); | ||
| 985 | tomoyo_print_name_union(head, &ptr->fs_type); | ||
| 986 | tomoyo_print_number_union(head, &ptr->flags); | ||
| 1490 | } | 987 | } |
| 1491 | head->read_bit = 0; | 988 | head->r.bit = bit + 1; |
| 989 | tomoyo_io_printf(head, "\n"); | ||
| 990 | if (acl_type != TOMOYO_TYPE_MOUNT_ACL) | ||
| 991 | goto next; | ||
| 992 | done: | ||
| 993 | head->r.bit = 0; | ||
| 1492 | return true; | 994 | return true; |
| 1493 | out: | ||
| 1494 | head->read_bit = bit; | ||
| 1495 | head->read_avail = pos; | ||
| 1496 | return false; | ||
| 1497 | } | 995 | } |
| 1498 | 996 | ||
| 1499 | /** | 997 | /** |
| 1500 | * tomoyo_print_entry - Print an ACL entry. | 998 | * tomoyo_read_domain2 - Read domain policy. |
| 1501 | * | 999 | * |
| 1502 | * @head: Pointer to "struct tomoyo_io_buffer". | 1000 | * @head: Pointer to "struct tomoyo_io_buffer". |
| 1503 | * @ptr: Pointer to an ACL entry. | 1001 | * @domain: Pointer to "struct tomoyo_domain_info". |
| 1002 | * | ||
| 1003 | * Caller holds tomoyo_read_lock(). | ||
| 1504 | * | 1004 | * |
| 1505 | * Returns true on success, false otherwise. | 1005 | * Returns true on success, false otherwise. |
| 1506 | */ | 1006 | */ |
| 1507 | static bool tomoyo_print_entry(struct tomoyo_io_buffer *head, | 1007 | static bool tomoyo_read_domain2(struct tomoyo_io_buffer *head, |
| 1508 | struct tomoyo_acl_info *ptr) | 1008 | struct tomoyo_domain_info *domain) |
| 1509 | { | 1009 | { |
| 1510 | const u8 acl_type = ptr->type; | 1010 | list_for_each_cookie(head->r.acl, &domain->acl_info_list) { |
| 1511 | 1011 | struct tomoyo_acl_info *ptr = | |
| 1512 | if (acl_type == TOMOYO_TYPE_PATH_ACL) { | 1012 | list_entry(head->r.acl, typeof(*ptr), list); |
| 1513 | struct tomoyo_path_acl *acl | 1013 | if (!tomoyo_print_entry(head, ptr)) |
| 1514 | = container_of(ptr, struct tomoyo_path_acl, head); | 1014 | return false; |
| 1515 | return tomoyo_print_path_acl(head, acl); | ||
| 1516 | } | ||
| 1517 | if (acl_type == TOMOYO_TYPE_PATH2_ACL) { | ||
| 1518 | struct tomoyo_path2_acl *acl | ||
| 1519 | = container_of(ptr, struct tomoyo_path2_acl, head); | ||
| 1520 | return tomoyo_print_path2_acl(head, acl); | ||
| 1521 | } | 1015 | } |
| 1522 | BUG(); /* This must not happen. */ | 1016 | head->r.acl = NULL; |
| 1523 | return false; | 1017 | return true; |
| 1524 | } | 1018 | } |
| 1525 | 1019 | ||
| 1526 | /** | 1020 | /** |
| 1527 | * tomoyo_read_domain_policy - Read domain policy. | 1021 | * tomoyo_read_domain - Read domain policy. |
| 1528 | * | 1022 | * |
| 1529 | * @head: Pointer to "struct tomoyo_io_buffer". | 1023 | * @head: Pointer to "struct tomoyo_io_buffer". |
| 1530 | * | 1024 | * |
| 1531 | * Returns 0. | ||
| 1532 | * | ||
| 1533 | * Caller holds tomoyo_read_lock(). | 1025 | * Caller holds tomoyo_read_lock(). |
| 1534 | */ | 1026 | */ |
| 1535 | static int tomoyo_read_domain_policy(struct tomoyo_io_buffer *head) | 1027 | static void tomoyo_read_domain(struct tomoyo_io_buffer *head) |
| 1536 | { | 1028 | { |
| 1537 | struct list_head *dpos; | 1029 | if (head->r.eof) |
| 1538 | struct list_head *apos; | 1030 | return; |
| 1539 | bool done = true; | 1031 | list_for_each_cookie(head->r.domain, &tomoyo_domain_list) { |
| 1540 | 1032 | struct tomoyo_domain_info *domain = | |
| 1541 | if (head->read_eof) | 1033 | list_entry(head->r.domain, typeof(*domain), list); |
| 1542 | return 0; | 1034 | switch (head->r.step) { |
| 1543 | if (head->read_step == 0) | 1035 | case 0: |
| 1544 | head->read_step = 1; | 1036 | if (domain->is_deleted && |
| 1545 | list_for_each_cookie(dpos, head->read_var1, &tomoyo_domain_list) { | 1037 | !head->r.print_this_domain_only) |
| 1546 | struct tomoyo_domain_info *domain; | 1038 | continue; |
| 1547 | const char *quota_exceeded = ""; | 1039 | /* Print domainname and flags. */ |
| 1548 | const char *transition_failed = ""; | 1040 | tomoyo_set_string(head, domain->domainname->name); |
| 1549 | const char *ignore_global_allow_read = ""; | 1041 | tomoyo_set_lf(head); |
| 1550 | domain = list_entry(dpos, struct tomoyo_domain_info, list); | 1042 | tomoyo_io_printf(head, |
| 1551 | if (head->read_step != 1) | 1043 | TOMOYO_KEYWORD_USE_PROFILE "%u\n", |
| 1552 | goto acl_loop; | 1044 | domain->profile); |
| 1553 | if (domain->is_deleted && !head->read_single_domain) | 1045 | if (domain->quota_warned) |
| 1554 | continue; | 1046 | tomoyo_set_string(head, "quota_exceeded\n"); |
| 1555 | /* Print domainname and flags. */ | 1047 | if (domain->transition_failed) |
| 1556 | if (domain->quota_warned) | 1048 | tomoyo_set_string(head, "transition_failed\n"); |
| 1557 | quota_exceeded = "quota_exceeded\n"; | 1049 | if (domain->ignore_global_allow_read) |
| 1558 | if (domain->transition_failed) | 1050 | tomoyo_set_string(head, |
| 1559 | transition_failed = "transition_failed\n"; | 1051 | TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ |
| 1560 | if (domain->ignore_global_allow_read) | 1052 | "\n"); |
| 1561 | ignore_global_allow_read | 1053 | head->r.step++; |
| 1562 | = TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ "\n"; | 1054 | tomoyo_set_lf(head); |
| 1563 | done = tomoyo_io_printf(head, "%s\n" TOMOYO_KEYWORD_USE_PROFILE | 1055 | /* fall through */ |
| 1564 | "%u\n%s%s%s\n", | 1056 | case 1: |
| 1565 | domain->domainname->name, | 1057 | if (!tomoyo_read_domain2(head, domain)) |
| 1566 | domain->profile, quota_exceeded, | 1058 | return; |
| 1567 | transition_failed, | 1059 | head->r.step++; |
| 1568 | ignore_global_allow_read); | 1060 | if (!tomoyo_set_lf(head)) |
| 1569 | if (!done) | 1061 | return; |
| 1570 | break; | 1062 | /* fall through */ |
| 1571 | head->read_step = 2; | 1063 | case 2: |
| 1572 | acl_loop: | 1064 | head->r.step = 0; |
| 1573 | if (head->read_step == 3) | 1065 | if (head->r.print_this_domain_only) |
| 1574 | goto tail_mark; | 1066 | goto done; |
| 1575 | /* Print ACL entries in the domain. */ | ||
| 1576 | list_for_each_cookie(apos, head->read_var2, | ||
| 1577 | &domain->acl_info_list) { | ||
| 1578 | struct tomoyo_acl_info *ptr | ||
| 1579 | = list_entry(apos, struct tomoyo_acl_info, | ||
| 1580 | list); | ||
| 1581 | done = tomoyo_print_entry(head, ptr); | ||
| 1582 | if (!done) | ||
| 1583 | break; | ||
| 1584 | } | 1067 | } |
| 1585 | if (!done) | ||
| 1586 | break; | ||
| 1587 | head->read_step = 3; | ||
| 1588 | tail_mark: | ||
| 1589 | done = tomoyo_io_printf(head, "\n"); | ||
| 1590 | if (!done) | ||
| 1591 | break; | ||
| 1592 | head->read_step = 1; | ||
| 1593 | if (head->read_single_domain) | ||
| 1594 | break; | ||
| 1595 | } | 1068 | } |
| 1596 | head->read_eof = done; | 1069 | done: |
| 1597 | return 0; | 1070 | head->r.eof = true; |
| 1598 | } | 1071 | } |
| 1599 | 1072 | ||
| 1600 | /** | 1073 | /** |
| @@ -1607,7 +1080,7 @@ tail_mark: | |||
| 1607 | * This is equivalent to doing | 1080 | * This is equivalent to doing |
| 1608 | * | 1081 | * |
| 1609 | * ( echo "select " $domainname; echo "use_profile " $profile ) | | 1082 | * ( echo "select " $domainname; echo "use_profile " $profile ) | |
| 1610 | * /usr/lib/ccs/loadpolicy -d | 1083 | * /usr/sbin/tomoyo-loadpolicy -d |
| 1611 | * | 1084 | * |
| 1612 | * Caller holds tomoyo_read_lock(). | 1085 | * Caller holds tomoyo_read_lock(). |
| 1613 | */ | 1086 | */ |
| @@ -1646,25 +1119,22 @@ static int tomoyo_write_domain_profile(struct tomoyo_io_buffer *head) | |||
| 1646 | * | 1119 | * |
| 1647 | * Caller holds tomoyo_read_lock(). | 1120 | * Caller holds tomoyo_read_lock(). |
| 1648 | */ | 1121 | */ |
| 1649 | static int tomoyo_read_domain_profile(struct tomoyo_io_buffer *head) | 1122 | static void tomoyo_read_domain_profile(struct tomoyo_io_buffer *head) |
| 1650 | { | 1123 | { |
| 1651 | struct list_head *pos; | 1124 | if (head->r.eof) |
| 1652 | bool done = true; | 1125 | return; |
| 1653 | 1126 | list_for_each_cookie(head->r.domain, &tomoyo_domain_list) { | |
| 1654 | if (head->read_eof) | 1127 | struct tomoyo_domain_info *domain = |
| 1655 | return 0; | 1128 | list_entry(head->r.domain, typeof(*domain), list); |
| 1656 | list_for_each_cookie(pos, head->read_var1, &tomoyo_domain_list) { | ||
| 1657 | struct tomoyo_domain_info *domain; | ||
| 1658 | domain = list_entry(pos, struct tomoyo_domain_info, list); | ||
| 1659 | if (domain->is_deleted) | 1129 | if (domain->is_deleted) |
| 1660 | continue; | 1130 | continue; |
| 1661 | done = tomoyo_io_printf(head, "%u %s\n", domain->profile, | 1131 | if (!tomoyo_flush(head)) |
| 1662 | domain->domainname->name); | 1132 | return; |
| 1663 | if (!done) | 1133 | tomoyo_io_printf(head, "%u ", domain->profile); |
| 1664 | break; | 1134 | tomoyo_set_string(head, domain->domainname->name); |
| 1135 | tomoyo_set_lf(head); | ||
| 1665 | } | 1136 | } |
| 1666 | head->read_eof = done; | 1137 | head->r.eof = true; |
| 1667 | return 0; | ||
| 1668 | } | 1138 | } |
| 1669 | 1139 | ||
| 1670 | /** | 1140 | /** |
| @@ -1676,11 +1146,7 @@ static int tomoyo_read_domain_profile(struct tomoyo_io_buffer *head) | |||
| 1676 | */ | 1146 | */ |
| 1677 | static int tomoyo_write_pid(struct tomoyo_io_buffer *head) | 1147 | static int tomoyo_write_pid(struct tomoyo_io_buffer *head) |
| 1678 | { | 1148 | { |
| 1679 | unsigned long pid; | 1149 | head->r.eof = false; |
| 1680 | /* No error check. */ | ||
| 1681 | strict_strtoul(head->write_buf, 10, &pid); | ||
| 1682 | head->read_step = (int) pid; | ||
| 1683 | head->read_eof = false; | ||
| 1684 | return 0; | 1150 | return 0; |
| 1685 | } | 1151 | } |
| 1686 | 1152 | ||
| @@ -1694,29 +1160,57 @@ static int tomoyo_write_pid(struct tomoyo_io_buffer *head) | |||
| 1694 | * The PID is specified by tomoyo_write_pid() so that the user can obtain | 1160 | * The PID is specified by tomoyo_write_pid() so that the user can obtain |
| 1695 | * using read()/write() interface rather than sysctl() interface. | 1161 | * using read()/write() interface rather than sysctl() interface. |
| 1696 | */ | 1162 | */ |
| 1697 | static int tomoyo_read_pid(struct tomoyo_io_buffer *head) | 1163 | static void tomoyo_read_pid(struct tomoyo_io_buffer *head) |
| 1698 | { | 1164 | { |
| 1699 | if (head->read_avail == 0 && !head->read_eof) { | 1165 | char *buf = head->write_buf; |
| 1700 | const int pid = head->read_step; | 1166 | bool global_pid = false; |
| 1701 | struct task_struct *p; | 1167 | unsigned int pid; |
| 1702 | struct tomoyo_domain_info *domain = NULL; | 1168 | struct task_struct *p; |
| 1703 | rcu_read_lock(); | 1169 | struct tomoyo_domain_info *domain = NULL; |
| 1704 | read_lock(&tasklist_lock); | 1170 | |
| 1705 | p = find_task_by_vpid(pid); | 1171 | /* Accessing write_buf is safe because head->io_sem is held. */ |
| 1706 | if (p) | 1172 | if (!buf) { |
| 1707 | domain = tomoyo_real_domain(p); | 1173 | head->r.eof = true; |
| 1708 | read_unlock(&tasklist_lock); | 1174 | return; /* Do nothing if open(O_RDONLY). */ |
| 1709 | rcu_read_unlock(); | ||
| 1710 | if (domain) | ||
| 1711 | tomoyo_io_printf(head, "%d %u %s", pid, domain->profile, | ||
| 1712 | domain->domainname->name); | ||
| 1713 | head->read_eof = true; | ||
| 1714 | } | 1175 | } |
| 1715 | return 0; | 1176 | if (head->r.w_pos || head->r.eof) |
| 1177 | return; | ||
| 1178 | head->r.eof = true; | ||
| 1179 | if (tomoyo_str_starts(&buf, "global-pid ")) | ||
| 1180 | global_pid = true; | ||
| 1181 | pid = (unsigned int) simple_strtoul(buf, NULL, 10); | ||
| 1182 | rcu_read_lock(); | ||
| 1183 | read_lock(&tasklist_lock); | ||
| 1184 | if (global_pid) | ||
| 1185 | p = find_task_by_pid_ns(pid, &init_pid_ns); | ||
| 1186 | else | ||
| 1187 | p = find_task_by_vpid(pid); | ||
| 1188 | if (p) | ||
| 1189 | domain = tomoyo_real_domain(p); | ||
| 1190 | read_unlock(&tasklist_lock); | ||
| 1191 | rcu_read_unlock(); | ||
| 1192 | if (!domain) | ||
| 1193 | return; | ||
| 1194 | tomoyo_io_printf(head, "%u %u ", pid, domain->profile); | ||
| 1195 | tomoyo_set_string(head, domain->domainname->name); | ||
| 1716 | } | 1196 | } |
| 1717 | 1197 | ||
| 1198 | static const char *tomoyo_transition_type[TOMOYO_MAX_TRANSITION_TYPE] = { | ||
| 1199 | [TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE] | ||
| 1200 | = TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN, | ||
| 1201 | [TOMOYO_TRANSITION_CONTROL_INITIALIZE] | ||
| 1202 | = TOMOYO_KEYWORD_INITIALIZE_DOMAIN, | ||
| 1203 | [TOMOYO_TRANSITION_CONTROL_NO_KEEP] = TOMOYO_KEYWORD_NO_KEEP_DOMAIN, | ||
| 1204 | [TOMOYO_TRANSITION_CONTROL_KEEP] = TOMOYO_KEYWORD_KEEP_DOMAIN | ||
| 1205 | }; | ||
| 1206 | |||
| 1207 | static const char *tomoyo_group_name[TOMOYO_MAX_GROUP] = { | ||
| 1208 | [TOMOYO_PATH_GROUP] = TOMOYO_KEYWORD_PATH_GROUP, | ||
| 1209 | [TOMOYO_NUMBER_GROUP] = TOMOYO_KEYWORD_NUMBER_GROUP | ||
| 1210 | }; | ||
| 1211 | |||
| 1718 | /** | 1212 | /** |
| 1719 | * tomoyo_write_exception_policy - Write exception policy. | 1213 | * tomoyo_write_exception - Write exception policy. |
| 1720 | * | 1214 | * |
| 1721 | * @head: Pointer to "struct tomoyo_io_buffer". | 1215 | * @head: Pointer to "struct tomoyo_io_buffer". |
| 1722 | * | 1216 | * |
| @@ -1724,186 +1218,523 @@ static int tomoyo_read_pid(struct tomoyo_io_buffer *head) | |||
| 1724 | * | 1218 | * |
| 1725 | * Caller holds tomoyo_read_lock(). | 1219 | * Caller holds tomoyo_read_lock(). |
| 1726 | */ | 1220 | */ |
| 1727 | static int tomoyo_write_exception_policy(struct tomoyo_io_buffer *head) | 1221 | static int tomoyo_write_exception(struct tomoyo_io_buffer *head) |
| 1728 | { | 1222 | { |
| 1729 | char *data = head->write_buf; | 1223 | char *data = head->write_buf; |
| 1730 | bool is_delete = tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE); | 1224 | bool is_delete = tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE); |
| 1731 | 1225 | u8 i; | |
| 1732 | if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_KEEP_DOMAIN)) | 1226 | static const struct { |
| 1733 | return tomoyo_write_domain_keeper_policy(data, false, | 1227 | const char *keyword; |
| 1734 | is_delete); | 1228 | int (*write) (char *, const bool); |
| 1735 | if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_NO_KEEP_DOMAIN)) | 1229 | } tomoyo_callback[4] = { |
| 1736 | return tomoyo_write_domain_keeper_policy(data, true, is_delete); | 1230 | { TOMOYO_KEYWORD_AGGREGATOR, tomoyo_write_aggregator }, |
| 1737 | if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_INITIALIZE_DOMAIN)) | 1231 | { TOMOYO_KEYWORD_FILE_PATTERN, tomoyo_write_pattern }, |
| 1738 | return tomoyo_write_domain_initializer_policy(data, false, | 1232 | { TOMOYO_KEYWORD_DENY_REWRITE, tomoyo_write_no_rewrite }, |
| 1739 | is_delete); | 1233 | { TOMOYO_KEYWORD_ALLOW_READ, tomoyo_write_globally_readable }, |
| 1740 | if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN)) | 1234 | }; |
| 1741 | return tomoyo_write_domain_initializer_policy(data, true, | 1235 | |
| 1742 | is_delete); | 1236 | for (i = 0; i < TOMOYO_MAX_TRANSITION_TYPE; i++) |
| 1743 | if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_ALIAS)) | 1237 | if (tomoyo_str_starts(&data, tomoyo_transition_type[i])) |
| 1744 | return tomoyo_write_alias_policy(data, is_delete); | 1238 | return tomoyo_write_transition_control(data, is_delete, |
| 1745 | if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_ALLOW_READ)) | 1239 | i); |
| 1746 | return tomoyo_write_globally_readable_policy(data, is_delete); | 1240 | for (i = 0; i < 4; i++) |
| 1747 | if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_FILE_PATTERN)) | 1241 | if (tomoyo_str_starts(&data, tomoyo_callback[i].keyword)) |
| 1748 | return tomoyo_write_pattern_policy(data, is_delete); | 1242 | return tomoyo_callback[i].write(data, is_delete); |
| 1749 | if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_DENY_REWRITE)) | 1243 | for (i = 0; i < TOMOYO_MAX_GROUP; i++) |
| 1750 | return tomoyo_write_no_rewrite_policy(data, is_delete); | 1244 | if (tomoyo_str_starts(&data, tomoyo_group_name[i])) |
| 1751 | if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_PATH_GROUP)) | 1245 | return tomoyo_write_group(data, is_delete, i); |
| 1752 | return tomoyo_write_path_group_policy(data, is_delete); | ||
| 1753 | return -EINVAL; | 1246 | return -EINVAL; |
| 1754 | } | 1247 | } |
| 1755 | 1248 | ||
| 1756 | /** | 1249 | /** |
| 1757 | * tomoyo_read_exception_policy - Read exception policy. | 1250 | * tomoyo_read_group - Read "struct tomoyo_path_group"/"struct tomoyo_number_group" list. |
| 1758 | * | 1251 | * |
| 1759 | * @head: Pointer to "struct tomoyo_io_buffer". | 1252 | * @head: Pointer to "struct tomoyo_io_buffer". |
| 1253 | * @idx: Index number. | ||
| 1760 | * | 1254 | * |
| 1761 | * Returns 0 on success, -EINVAL otherwise. | 1255 | * Returns true on success, false otherwise. |
| 1762 | * | 1256 | * |
| 1763 | * Caller holds tomoyo_read_lock(). | 1257 | * Caller holds tomoyo_read_lock(). |
| 1764 | */ | 1258 | */ |
| 1765 | static int tomoyo_read_exception_policy(struct tomoyo_io_buffer *head) | 1259 | static bool tomoyo_read_group(struct tomoyo_io_buffer *head, const int idx) |
| 1766 | { | 1260 | { |
| 1767 | if (!head->read_eof) { | 1261 | list_for_each_cookie(head->r.group, &tomoyo_group_list[idx]) { |
| 1768 | switch (head->read_step) { | 1262 | struct tomoyo_group *group = |
| 1769 | case 0: | 1263 | list_entry(head->r.group, typeof(*group), list); |
| 1770 | head->read_var2 = NULL; | 1264 | list_for_each_cookie(head->r.acl, &group->member_list) { |
| 1771 | head->read_step = 1; | 1265 | struct tomoyo_acl_head *ptr = |
| 1772 | case 1: | 1266 | list_entry(head->r.acl, typeof(*ptr), list); |
| 1773 | if (!tomoyo_read_domain_keeper_policy(head)) | 1267 | if (ptr->is_deleted) |
| 1774 | break; | 1268 | continue; |
| 1775 | head->read_var2 = NULL; | 1269 | if (!tomoyo_flush(head)) |
| 1776 | head->read_step = 2; | 1270 | return false; |
| 1777 | case 2: | 1271 | tomoyo_set_string(head, tomoyo_group_name[idx]); |
| 1778 | if (!tomoyo_read_globally_readable_policy(head)) | 1272 | tomoyo_set_string(head, group->group_name->name); |
| 1779 | break; | 1273 | if (idx == TOMOYO_PATH_GROUP) { |
| 1780 | head->read_var2 = NULL; | 1274 | tomoyo_set_space(head); |
| 1781 | head->read_step = 3; | 1275 | tomoyo_set_string(head, container_of |
| 1782 | case 3: | 1276 | (ptr, struct tomoyo_path_group, |
| 1783 | head->read_var2 = NULL; | 1277 | head)->member_name->name); |
| 1784 | head->read_step = 4; | 1278 | } else if (idx == TOMOYO_NUMBER_GROUP) { |
| 1785 | case 4: | 1279 | tomoyo_print_number_union(head, &container_of |
| 1786 | if (!tomoyo_read_domain_initializer_policy(head)) | 1280 | (ptr, |
| 1787 | break; | 1281 | struct tomoyo_number_group, |
| 1788 | head->read_var2 = NULL; | 1282 | head)->number); |
| 1789 | head->read_step = 5; | 1283 | } |
| 1790 | case 5: | 1284 | tomoyo_set_lf(head); |
| 1791 | if (!tomoyo_read_alias_policy(head)) | 1285 | } |
| 1792 | break; | 1286 | head->r.acl = NULL; |
| 1793 | head->read_var2 = NULL; | 1287 | } |
| 1794 | head->read_step = 6; | 1288 | head->r.group = NULL; |
| 1795 | case 6: | 1289 | return true; |
| 1796 | head->read_var2 = NULL; | 1290 | } |
| 1797 | head->read_step = 7; | 1291 | |
| 1798 | case 7: | 1292 | /** |
| 1799 | if (!tomoyo_read_file_pattern(head)) | 1293 | * tomoyo_read_policy - Read "struct tomoyo_..._entry" list. |
| 1800 | break; | 1294 | * |
| 1801 | head->read_var2 = NULL; | 1295 | * @head: Pointer to "struct tomoyo_io_buffer". |
| 1802 | head->read_step = 8; | 1296 | * @idx: Index number. |
| 1803 | case 8: | 1297 | * |
| 1804 | if (!tomoyo_read_no_rewrite_policy(head)) | 1298 | * Returns true on success, false otherwise. |
| 1805 | break; | 1299 | * |
| 1806 | head->read_var2 = NULL; | 1300 | * Caller holds tomoyo_read_lock(). |
| 1807 | head->read_step = 9; | 1301 | */ |
| 1808 | case 9: | 1302 | static bool tomoyo_read_policy(struct tomoyo_io_buffer *head, const int idx) |
| 1809 | if (!tomoyo_read_path_group_policy(head)) | 1303 | { |
| 1810 | break; | 1304 | list_for_each_cookie(head->r.acl, &tomoyo_policy_list[idx]) { |
| 1811 | head->read_var1 = NULL; | 1305 | struct tomoyo_acl_head *acl = |
| 1812 | head->read_var2 = NULL; | 1306 | container_of(head->r.acl, typeof(*acl), list); |
| 1813 | head->read_step = 10; | 1307 | if (acl->is_deleted) |
| 1814 | case 10: | 1308 | continue; |
| 1815 | head->read_eof = true; | 1309 | if (!tomoyo_flush(head)) |
| 1310 | return false; | ||
| 1311 | switch (idx) { | ||
| 1312 | case TOMOYO_ID_TRANSITION_CONTROL: | ||
| 1313 | { | ||
| 1314 | struct tomoyo_transition_control *ptr = | ||
| 1315 | container_of(acl, typeof(*ptr), head); | ||
| 1316 | tomoyo_set_string(head, | ||
| 1317 | tomoyo_transition_type | ||
| 1318 | [ptr->type]); | ||
| 1319 | if (ptr->program) | ||
| 1320 | tomoyo_set_string(head, | ||
| 1321 | ptr->program->name); | ||
| 1322 | if (ptr->program && ptr->domainname) | ||
| 1323 | tomoyo_set_string(head, " from "); | ||
| 1324 | if (ptr->domainname) | ||
| 1325 | tomoyo_set_string(head, | ||
| 1326 | ptr->domainname-> | ||
| 1327 | name); | ||
| 1328 | } | ||
| 1329 | break; | ||
| 1330 | case TOMOYO_ID_GLOBALLY_READABLE: | ||
| 1331 | { | ||
| 1332 | struct tomoyo_readable_file *ptr = | ||
| 1333 | container_of(acl, typeof(*ptr), head); | ||
| 1334 | tomoyo_set_string(head, | ||
| 1335 | TOMOYO_KEYWORD_ALLOW_READ); | ||
| 1336 | tomoyo_set_string(head, ptr->filename->name); | ||
| 1337 | } | ||
| 1338 | break; | ||
| 1339 | case TOMOYO_ID_AGGREGATOR: | ||
| 1340 | { | ||
| 1341 | struct tomoyo_aggregator *ptr = | ||
| 1342 | container_of(acl, typeof(*ptr), head); | ||
| 1343 | tomoyo_set_string(head, | ||
| 1344 | TOMOYO_KEYWORD_AGGREGATOR); | ||
| 1345 | tomoyo_set_string(head, | ||
| 1346 | ptr->original_name->name); | ||
| 1347 | tomoyo_set_space(head); | ||
| 1348 | tomoyo_set_string(head, | ||
| 1349 | ptr->aggregated_name->name); | ||
| 1350 | } | ||
| 1351 | break; | ||
| 1352 | case TOMOYO_ID_PATTERN: | ||
| 1353 | { | ||
| 1354 | struct tomoyo_no_pattern *ptr = | ||
| 1355 | container_of(acl, typeof(*ptr), head); | ||
| 1356 | tomoyo_set_string(head, | ||
| 1357 | TOMOYO_KEYWORD_FILE_PATTERN); | ||
| 1358 | tomoyo_set_string(head, ptr->pattern->name); | ||
| 1359 | } | ||
| 1360 | break; | ||
| 1361 | case TOMOYO_ID_NO_REWRITE: | ||
| 1362 | { | ||
| 1363 | struct tomoyo_no_rewrite *ptr = | ||
| 1364 | container_of(acl, typeof(*ptr), head); | ||
| 1365 | tomoyo_set_string(head, | ||
| 1366 | TOMOYO_KEYWORD_DENY_REWRITE); | ||
| 1367 | tomoyo_set_string(head, ptr->pattern->name); | ||
| 1368 | } | ||
| 1816 | break; | 1369 | break; |
| 1817 | default: | 1370 | default: |
| 1818 | return -EINVAL; | 1371 | continue; |
| 1819 | } | 1372 | } |
| 1373 | tomoyo_set_lf(head); | ||
| 1820 | } | 1374 | } |
| 1821 | return 0; | 1375 | head->r.acl = NULL; |
| 1376 | return true; | ||
| 1822 | } | 1377 | } |
| 1823 | 1378 | ||
| 1824 | /* path to policy loader */ | 1379 | /** |
| 1825 | static const char *tomoyo_loader = "/sbin/tomoyo-init"; | 1380 | * tomoyo_read_exception - Read exception policy. |
| 1381 | * | ||
| 1382 | * @head: Pointer to "struct tomoyo_io_buffer". | ||
| 1383 | * | ||
| 1384 | * Caller holds tomoyo_read_lock(). | ||
| 1385 | */ | ||
| 1386 | static void tomoyo_read_exception(struct tomoyo_io_buffer *head) | ||
| 1387 | { | ||
| 1388 | if (head->r.eof) | ||
| 1389 | return; | ||
| 1390 | while (head->r.step < TOMOYO_MAX_POLICY && | ||
| 1391 | tomoyo_read_policy(head, head->r.step)) | ||
| 1392 | head->r.step++; | ||
| 1393 | if (head->r.step < TOMOYO_MAX_POLICY) | ||
| 1394 | return; | ||
| 1395 | while (head->r.step < TOMOYO_MAX_POLICY + TOMOYO_MAX_GROUP && | ||
| 1396 | tomoyo_read_group(head, head->r.step - TOMOYO_MAX_POLICY)) | ||
| 1397 | head->r.step++; | ||
| 1398 | if (head->r.step < TOMOYO_MAX_POLICY + TOMOYO_MAX_GROUP) | ||
| 1399 | return; | ||
| 1400 | head->r.eof = true; | ||
| 1401 | } | ||
| 1826 | 1402 | ||
| 1827 | /** | 1403 | /** |
| 1828 | * tomoyo_policy_loader_exists - Check whether /sbin/tomoyo-init exists. | 1404 | * tomoyo_print_header - Get header line of audit log. |
| 1405 | * | ||
| 1406 | * @r: Pointer to "struct tomoyo_request_info". | ||
| 1829 | * | 1407 | * |
| 1830 | * Returns true if /sbin/tomoyo-init exists, false otherwise. | 1408 | * Returns string representation. |
| 1409 | * | ||
| 1410 | * This function uses kmalloc(), so caller must kfree() if this function | ||
| 1411 | * didn't return NULL. | ||
| 1831 | */ | 1412 | */ |
| 1832 | static bool tomoyo_policy_loader_exists(void) | 1413 | static char *tomoyo_print_header(struct tomoyo_request_info *r) |
| 1833 | { | 1414 | { |
| 1834 | /* | 1415 | struct timeval tv; |
| 1835 | * Don't activate MAC if the policy loader doesn't exist. | 1416 | const pid_t gpid = task_pid_nr(current); |
| 1836 | * If the initrd includes /sbin/init but real-root-dev has not | 1417 | static const int tomoyo_buffer_len = 4096; |
| 1837 | * mounted on / yet, activating MAC will block the system since | 1418 | char *buffer = kmalloc(tomoyo_buffer_len, GFP_NOFS); |
| 1838 | * policies are not loaded yet. | 1419 | if (!buffer) |
| 1839 | * Thus, let do_execve() call this function everytime. | 1420 | return NULL; |
| 1840 | */ | 1421 | do_gettimeofday(&tv); |
| 1841 | struct path path; | 1422 | snprintf(buffer, tomoyo_buffer_len - 1, |
| 1423 | "#timestamp=%lu profile=%u mode=%s (global-pid=%u)" | ||
| 1424 | " task={ pid=%u ppid=%u uid=%u gid=%u euid=%u" | ||
| 1425 | " egid=%u suid=%u sgid=%u fsuid=%u fsgid=%u }", | ||
| 1426 | tv.tv_sec, r->profile, tomoyo_mode[r->mode], gpid, | ||
| 1427 | (pid_t) sys_getpid(), (pid_t) sys_getppid(), | ||
| 1428 | current_uid(), current_gid(), current_euid(), | ||
| 1429 | current_egid(), current_suid(), current_sgid(), | ||
| 1430 | current_fsuid(), current_fsgid()); | ||
| 1431 | return buffer; | ||
| 1432 | } | ||
| 1842 | 1433 | ||
| 1843 | if (kern_path(tomoyo_loader, LOOKUP_FOLLOW, &path)) { | 1434 | /** |
| 1844 | printk(KERN_INFO "Not activating Mandatory Access Control now " | 1435 | * tomoyo_init_audit_log - Allocate buffer for audit logs. |
| 1845 | "since %s doesn't exist.\n", tomoyo_loader); | 1436 | * |
| 1846 | return false; | 1437 | * @len: Required size. |
| 1438 | * @r: Pointer to "struct tomoyo_request_info". | ||
| 1439 | * | ||
| 1440 | * Returns pointer to allocated memory. | ||
| 1441 | * | ||
| 1442 | * The @len is updated to add the header lines' size on success. | ||
| 1443 | * | ||
| 1444 | * This function uses kzalloc(), so caller must kfree() if this function | ||
| 1445 | * didn't return NULL. | ||
| 1446 | */ | ||
| 1447 | static char *tomoyo_init_audit_log(int *len, struct tomoyo_request_info *r) | ||
| 1448 | { | ||
| 1449 | char *buf = NULL; | ||
| 1450 | const char *header; | ||
| 1451 | const char *domainname; | ||
| 1452 | if (!r->domain) | ||
| 1453 | r->domain = tomoyo_domain(); | ||
| 1454 | domainname = r->domain->domainname->name; | ||
| 1455 | header = tomoyo_print_header(r); | ||
| 1456 | if (!header) | ||
| 1457 | return NULL; | ||
| 1458 | *len += strlen(domainname) + strlen(header) + 10; | ||
| 1459 | buf = kzalloc(*len, GFP_NOFS); | ||
| 1460 | if (buf) | ||
| 1461 | snprintf(buf, (*len) - 1, "%s\n%s\n", header, domainname); | ||
| 1462 | kfree(header); | ||
| 1463 | return buf; | ||
| 1464 | } | ||
| 1465 | |||
| 1466 | /* Wait queue for tomoyo_query_list. */ | ||
| 1467 | static DECLARE_WAIT_QUEUE_HEAD(tomoyo_query_wait); | ||
| 1468 | |||
| 1469 | /* Lock for manipulating tomoyo_query_list. */ | ||
| 1470 | static DEFINE_SPINLOCK(tomoyo_query_list_lock); | ||
| 1471 | |||
| 1472 | /* Structure for query. */ | ||
| 1473 | struct tomoyo_query { | ||
| 1474 | struct list_head list; | ||
| 1475 | char *query; | ||
| 1476 | int query_len; | ||
| 1477 | unsigned int serial; | ||
| 1478 | int timer; | ||
| 1479 | int answer; | ||
| 1480 | }; | ||
| 1481 | |||
| 1482 | /* The list for "struct tomoyo_query". */ | ||
| 1483 | static LIST_HEAD(tomoyo_query_list); | ||
| 1484 | |||
| 1485 | /* | ||
| 1486 | * Number of "struct file" referring /sys/kernel/security/tomoyo/query | ||
| 1487 | * interface. | ||
| 1488 | */ | ||
| 1489 | static atomic_t tomoyo_query_observers = ATOMIC_INIT(0); | ||
| 1490 | |||
| 1491 | /** | ||
| 1492 | * tomoyo_supervisor - Ask for the supervisor's decision. | ||
| 1493 | * | ||
| 1494 | * @r: Pointer to "struct tomoyo_request_info". | ||
| 1495 | * @fmt: The printf()'s format string, followed by parameters. | ||
| 1496 | * | ||
| 1497 | * Returns 0 if the supervisor decided to permit the access request which | ||
| 1498 | * violated the policy in enforcing mode, TOMOYO_RETRY_REQUEST if the | ||
| 1499 | * supervisor decided to retry the access request which violated the policy in | ||
| 1500 | * enforcing mode, 0 if it is not in enforcing mode, -EPERM otherwise. | ||
| 1501 | */ | ||
| 1502 | int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...) | ||
| 1503 | { | ||
| 1504 | va_list args; | ||
| 1505 | int error = -EPERM; | ||
| 1506 | int pos; | ||
| 1507 | int len; | ||
| 1508 | static unsigned int tomoyo_serial; | ||
| 1509 | struct tomoyo_query *entry = NULL; | ||
| 1510 | bool quota_exceeded = false; | ||
| 1511 | char *header; | ||
| 1512 | switch (r->mode) { | ||
| 1513 | char *buffer; | ||
| 1514 | case TOMOYO_CONFIG_LEARNING: | ||
| 1515 | if (!tomoyo_domain_quota_is_ok(r)) | ||
| 1516 | return 0; | ||
| 1517 | va_start(args, fmt); | ||
| 1518 | len = vsnprintf((char *) &pos, sizeof(pos) - 1, fmt, args) + 4; | ||
| 1519 | va_end(args); | ||
| 1520 | buffer = kmalloc(len, GFP_NOFS); | ||
| 1521 | if (!buffer) | ||
| 1522 | return 0; | ||
| 1523 | va_start(args, fmt); | ||
| 1524 | vsnprintf(buffer, len - 1, fmt, args); | ||
| 1525 | va_end(args); | ||
| 1526 | tomoyo_normalize_line(buffer); | ||
| 1527 | tomoyo_write_domain2(buffer, r->domain, false); | ||
| 1528 | kfree(buffer); | ||
| 1529 | /* fall through */ | ||
| 1530 | case TOMOYO_CONFIG_PERMISSIVE: | ||
| 1531 | return 0; | ||
| 1847 | } | 1532 | } |
| 1848 | path_put(&path); | 1533 | if (!r->domain) |
| 1849 | return true; | 1534 | r->domain = tomoyo_domain(); |
| 1535 | if (!atomic_read(&tomoyo_query_observers)) | ||
| 1536 | return -EPERM; | ||
| 1537 | va_start(args, fmt); | ||
| 1538 | len = vsnprintf((char *) &pos, sizeof(pos) - 1, fmt, args) + 32; | ||
| 1539 | va_end(args); | ||
| 1540 | header = tomoyo_init_audit_log(&len, r); | ||
| 1541 | if (!header) | ||
| 1542 | goto out; | ||
| 1543 | entry = kzalloc(sizeof(*entry), GFP_NOFS); | ||
| 1544 | if (!entry) | ||
| 1545 | goto out; | ||
| 1546 | entry->query = kzalloc(len, GFP_NOFS); | ||
| 1547 | if (!entry->query) | ||
| 1548 | goto out; | ||
| 1549 | len = ksize(entry->query); | ||
| 1550 | spin_lock(&tomoyo_query_list_lock); | ||
| 1551 | if (tomoyo_quota_for_query && tomoyo_query_memory_size + len + | ||
| 1552 | sizeof(*entry) >= tomoyo_quota_for_query) { | ||
| 1553 | quota_exceeded = true; | ||
| 1554 | } else { | ||
| 1555 | tomoyo_query_memory_size += len + sizeof(*entry); | ||
| 1556 | entry->serial = tomoyo_serial++; | ||
| 1557 | } | ||
| 1558 | spin_unlock(&tomoyo_query_list_lock); | ||
| 1559 | if (quota_exceeded) | ||
| 1560 | goto out; | ||
| 1561 | pos = snprintf(entry->query, len - 1, "Q%u-%hu\n%s", | ||
| 1562 | entry->serial, r->retry, header); | ||
| 1563 | kfree(header); | ||
| 1564 | header = NULL; | ||
| 1565 | va_start(args, fmt); | ||
| 1566 | vsnprintf(entry->query + pos, len - 1 - pos, fmt, args); | ||
| 1567 | entry->query_len = strlen(entry->query) + 1; | ||
| 1568 | va_end(args); | ||
| 1569 | spin_lock(&tomoyo_query_list_lock); | ||
| 1570 | list_add_tail(&entry->list, &tomoyo_query_list); | ||
| 1571 | spin_unlock(&tomoyo_query_list_lock); | ||
| 1572 | /* Give 10 seconds for supervisor's opinion. */ | ||
| 1573 | for (entry->timer = 0; | ||
| 1574 | atomic_read(&tomoyo_query_observers) && entry->timer < 100; | ||
| 1575 | entry->timer++) { | ||
| 1576 | wake_up(&tomoyo_query_wait); | ||
| 1577 | set_current_state(TASK_INTERRUPTIBLE); | ||
| 1578 | schedule_timeout(HZ / 10); | ||
| 1579 | if (entry->answer) | ||
| 1580 | break; | ||
| 1581 | } | ||
| 1582 | spin_lock(&tomoyo_query_list_lock); | ||
| 1583 | list_del(&entry->list); | ||
| 1584 | tomoyo_query_memory_size -= len + sizeof(*entry); | ||
| 1585 | spin_unlock(&tomoyo_query_list_lock); | ||
| 1586 | switch (entry->answer) { | ||
| 1587 | case 3: /* Asked to retry by administrator. */ | ||
| 1588 | error = TOMOYO_RETRY_REQUEST; | ||
| 1589 | r->retry++; | ||
| 1590 | break; | ||
| 1591 | case 1: | ||
| 1592 | /* Granted by administrator. */ | ||
| 1593 | error = 0; | ||
| 1594 | break; | ||
| 1595 | case 0: | ||
| 1596 | /* Timed out. */ | ||
| 1597 | break; | ||
| 1598 | default: | ||
| 1599 | /* Rejected by administrator. */ | ||
| 1600 | break; | ||
| 1601 | } | ||
| 1602 | out: | ||
| 1603 | if (entry) | ||
| 1604 | kfree(entry->query); | ||
| 1605 | kfree(entry); | ||
| 1606 | kfree(header); | ||
| 1607 | return error; | ||
| 1850 | } | 1608 | } |
| 1851 | 1609 | ||
| 1852 | /** | 1610 | /** |
| 1853 | * tomoyo_load_policy - Run external policy loader to load policy. | 1611 | * tomoyo_poll_query - poll() for /sys/kernel/security/tomoyo/query. |
| 1854 | * | 1612 | * |
| 1855 | * @filename: The program about to start. | 1613 | * @file: Pointer to "struct file". |
| 1614 | * @wait: Pointer to "poll_table". | ||
| 1856 | * | 1615 | * |
| 1857 | * This function checks whether @filename is /sbin/init , and if so | 1616 | * Returns POLLIN | POLLRDNORM when ready to read, 0 otherwise. |
| 1858 | * invoke /sbin/tomoyo-init and wait for the termination of /sbin/tomoyo-init | ||
| 1859 | * and then continues invocation of /sbin/init. | ||
| 1860 | * /sbin/tomoyo-init reads policy files in /etc/tomoyo/ directory and | ||
| 1861 | * writes to /sys/kernel/security/tomoyo/ interfaces. | ||
| 1862 | * | 1617 | * |
| 1863 | * Returns nothing. | 1618 | * Waits for access requests which violated policy in enforcing mode. |
| 1864 | */ | 1619 | */ |
| 1865 | void tomoyo_load_policy(const char *filename) | 1620 | static int tomoyo_poll_query(struct file *file, poll_table *wait) |
| 1866 | { | 1621 | { |
| 1867 | char *argv[2]; | 1622 | struct list_head *tmp; |
| 1868 | char *envp[3]; | 1623 | bool found = false; |
| 1624 | u8 i; | ||
| 1625 | for (i = 0; i < 2; i++) { | ||
| 1626 | spin_lock(&tomoyo_query_list_lock); | ||
| 1627 | list_for_each(tmp, &tomoyo_query_list) { | ||
| 1628 | struct tomoyo_query *ptr = | ||
| 1629 | list_entry(tmp, typeof(*ptr), list); | ||
| 1630 | if (ptr->answer) | ||
| 1631 | continue; | ||
| 1632 | found = true; | ||
| 1633 | break; | ||
| 1634 | } | ||
| 1635 | spin_unlock(&tomoyo_query_list_lock); | ||
| 1636 | if (found) | ||
| 1637 | return POLLIN | POLLRDNORM; | ||
| 1638 | if (i) | ||
| 1639 | break; | ||
| 1640 | poll_wait(file, &tomoyo_query_wait, wait); | ||
| 1641 | } | ||
| 1642 | return 0; | ||
| 1643 | } | ||
| 1869 | 1644 | ||
| 1870 | if (tomoyo_policy_loaded) | 1645 | /** |
| 1646 | * tomoyo_read_query - Read access requests which violated policy in enforcing mode. | ||
| 1647 | * | ||
| 1648 | * @head: Pointer to "struct tomoyo_io_buffer". | ||
| 1649 | */ | ||
| 1650 | static void tomoyo_read_query(struct tomoyo_io_buffer *head) | ||
| 1651 | { | ||
| 1652 | struct list_head *tmp; | ||
| 1653 | int pos = 0; | ||
| 1654 | int len = 0; | ||
| 1655 | char *buf; | ||
| 1656 | if (head->r.w_pos) | ||
| 1871 | return; | 1657 | return; |
| 1872 | /* | 1658 | if (head->read_buf) { |
| 1873 | * Check filename is /sbin/init or /sbin/tomoyo-start. | 1659 | kfree(head->read_buf); |
| 1874 | * /sbin/tomoyo-start is a dummy filename in case where /sbin/init can't | 1660 | head->read_buf = NULL; |
| 1875 | * be passed. | 1661 | } |
| 1876 | * You can create /sbin/tomoyo-start by | 1662 | spin_lock(&tomoyo_query_list_lock); |
| 1877 | * "ln -s /bin/true /sbin/tomoyo-start". | 1663 | list_for_each(tmp, &tomoyo_query_list) { |
| 1878 | */ | 1664 | struct tomoyo_query *ptr = list_entry(tmp, typeof(*ptr), list); |
| 1879 | if (strcmp(filename, "/sbin/init") && | 1665 | if (ptr->answer) |
| 1880 | strcmp(filename, "/sbin/tomoyo-start")) | 1666 | continue; |
| 1667 | if (pos++ != head->r.query_index) | ||
| 1668 | continue; | ||
| 1669 | len = ptr->query_len; | ||
| 1670 | break; | ||
| 1671 | } | ||
| 1672 | spin_unlock(&tomoyo_query_list_lock); | ||
| 1673 | if (!len) { | ||
| 1674 | head->r.query_index = 0; | ||
| 1881 | return; | 1675 | return; |
| 1882 | if (!tomoyo_policy_loader_exists()) | 1676 | } |
| 1677 | buf = kzalloc(len, GFP_NOFS); | ||
| 1678 | if (!buf) | ||
| 1883 | return; | 1679 | return; |
| 1680 | pos = 0; | ||
| 1681 | spin_lock(&tomoyo_query_list_lock); | ||
| 1682 | list_for_each(tmp, &tomoyo_query_list) { | ||
| 1683 | struct tomoyo_query *ptr = list_entry(tmp, typeof(*ptr), list); | ||
| 1684 | if (ptr->answer) | ||
| 1685 | continue; | ||
| 1686 | if (pos++ != head->r.query_index) | ||
| 1687 | continue; | ||
| 1688 | /* | ||
| 1689 | * Some query can be skipped because tomoyo_query_list | ||
| 1690 | * can change, but I don't care. | ||
| 1691 | */ | ||
| 1692 | if (len == ptr->query_len) | ||
| 1693 | memmove(buf, ptr->query, len); | ||
| 1694 | break; | ||
| 1695 | } | ||
| 1696 | spin_unlock(&tomoyo_query_list_lock); | ||
| 1697 | if (buf[0]) { | ||
| 1698 | head->read_buf = buf; | ||
| 1699 | head->r.w[head->r.w_pos++] = buf; | ||
| 1700 | head->r.query_index++; | ||
| 1701 | } else { | ||
| 1702 | kfree(buf); | ||
| 1703 | } | ||
| 1704 | } | ||
| 1884 | 1705 | ||
| 1885 | printk(KERN_INFO "Calling %s to load policy. Please wait.\n", | 1706 | /** |
| 1886 | tomoyo_loader); | 1707 | * tomoyo_write_answer - Write the supervisor's decision. |
| 1887 | argv[0] = (char *) tomoyo_loader; | 1708 | * |
| 1888 | argv[1] = NULL; | 1709 | * @head: Pointer to "struct tomoyo_io_buffer". |
| 1889 | envp[0] = "HOME=/"; | 1710 | * |
| 1890 | envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; | 1711 | * Returns 0 on success, -EINVAL otherwise. |
| 1891 | envp[2] = NULL; | 1712 | */ |
| 1892 | call_usermodehelper(argv[0], argv, envp, 1); | 1713 | static int tomoyo_write_answer(struct tomoyo_io_buffer *head) |
| 1893 | 1714 | { | |
| 1894 | printk(KERN_INFO "TOMOYO: 2.2.0 2009/04/01\n"); | 1715 | char *data = head->write_buf; |
| 1895 | printk(KERN_INFO "Mandatory Access Control activated.\n"); | 1716 | struct list_head *tmp; |
| 1896 | tomoyo_policy_loaded = true; | 1717 | unsigned int serial; |
| 1897 | { /* Check all profiles currently assigned to domains are defined. */ | 1718 | unsigned int answer; |
| 1898 | struct tomoyo_domain_info *domain; | 1719 | spin_lock(&tomoyo_query_list_lock); |
| 1899 | list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { | 1720 | list_for_each(tmp, &tomoyo_query_list) { |
| 1900 | const u8 profile = domain->profile; | 1721 | struct tomoyo_query *ptr = list_entry(tmp, typeof(*ptr), list); |
| 1901 | if (tomoyo_profile_ptr[profile]) | 1722 | ptr->timer = 0; |
| 1902 | continue; | 1723 | } |
| 1903 | panic("Profile %u (used by '%s') not defined.\n", | 1724 | spin_unlock(&tomoyo_query_list_lock); |
| 1904 | profile, domain->domainname->name); | 1725 | if (sscanf(data, "A%u=%u", &serial, &answer) != 2) |
| 1905 | } | 1726 | return -EINVAL; |
| 1727 | spin_lock(&tomoyo_query_list_lock); | ||
| 1728 | list_for_each(tmp, &tomoyo_query_list) { | ||
| 1729 | struct tomoyo_query *ptr = list_entry(tmp, typeof(*ptr), list); | ||
| 1730 | if (ptr->serial != serial) | ||
| 1731 | continue; | ||
| 1732 | if (!ptr->answer) | ||
| 1733 | ptr->answer = answer; | ||
| 1734 | break; | ||
| 1906 | } | 1735 | } |
| 1736 | spin_unlock(&tomoyo_query_list_lock); | ||
| 1737 | return 0; | ||
| 1907 | } | 1738 | } |
| 1908 | 1739 | ||
| 1909 | /** | 1740 | /** |
| @@ -1913,13 +1744,12 @@ void tomoyo_load_policy(const char *filename) | |||
| 1913 | * | 1744 | * |
| 1914 | * Returns version information. | 1745 | * Returns version information. |
| 1915 | */ | 1746 | */ |
| 1916 | static int tomoyo_read_version(struct tomoyo_io_buffer *head) | 1747 | static void tomoyo_read_version(struct tomoyo_io_buffer *head) |
| 1917 | { | 1748 | { |
| 1918 | if (!head->read_eof) { | 1749 | if (!head->r.eof) { |
| 1919 | tomoyo_io_printf(head, "2.2.0"); | 1750 | tomoyo_io_printf(head, "2.3.0"); |
| 1920 | head->read_eof = true; | 1751 | head->r.eof = true; |
| 1921 | } | 1752 | } |
| 1922 | return 0; | ||
| 1923 | } | 1753 | } |
| 1924 | 1754 | ||
| 1925 | /** | 1755 | /** |
| @@ -1929,18 +1759,17 @@ static int tomoyo_read_version(struct tomoyo_io_buffer *head) | |||
| 1929 | * | 1759 | * |
| 1930 | * Returns the current process's domainname. | 1760 | * Returns the current process's domainname. |
| 1931 | */ | 1761 | */ |
| 1932 | static int tomoyo_read_self_domain(struct tomoyo_io_buffer *head) | 1762 | static void tomoyo_read_self_domain(struct tomoyo_io_buffer *head) |
| 1933 | { | 1763 | { |
| 1934 | if (!head->read_eof) { | 1764 | if (!head->r.eof) { |
| 1935 | /* | 1765 | /* |
| 1936 | * tomoyo_domain()->domainname != NULL | 1766 | * tomoyo_domain()->domainname != NULL |
| 1937 | * because every process belongs to a domain and | 1767 | * because every process belongs to a domain and |
| 1938 | * the domain's name cannot be NULL. | 1768 | * the domain's name cannot be NULL. |
| 1939 | */ | 1769 | */ |
| 1940 | tomoyo_io_printf(head, "%s", tomoyo_domain()->domainname->name); | 1770 | tomoyo_io_printf(head, "%s", tomoyo_domain()->domainname->name); |
| 1941 | head->read_eof = true; | 1771 | head->r.eof = true; |
| 1942 | } | 1772 | } |
| 1943 | return 0; | ||
| 1944 | } | 1773 | } |
| 1945 | 1774 | ||
| 1946 | /** | 1775 | /** |
| @@ -1953,23 +1782,24 @@ static int tomoyo_read_self_domain(struct tomoyo_io_buffer *head) | |||
| 1953 | * | 1782 | * |
| 1954 | * Caller acquires tomoyo_read_lock(). | 1783 | * Caller acquires tomoyo_read_lock(). |
| 1955 | */ | 1784 | */ |
| 1956 | static int tomoyo_open_control(const u8 type, struct file *file) | 1785 | int tomoyo_open_control(const u8 type, struct file *file) |
| 1957 | { | 1786 | { |
| 1958 | struct tomoyo_io_buffer *head = kzalloc(sizeof(*head), GFP_NOFS); | 1787 | struct tomoyo_io_buffer *head = kzalloc(sizeof(*head), GFP_NOFS); |
| 1959 | 1788 | ||
| 1960 | if (!head) | 1789 | if (!head) |
| 1961 | return -ENOMEM; | 1790 | return -ENOMEM; |
| 1962 | mutex_init(&head->io_sem); | 1791 | mutex_init(&head->io_sem); |
| 1792 | head->type = type; | ||
| 1963 | switch (type) { | 1793 | switch (type) { |
| 1964 | case TOMOYO_DOMAINPOLICY: | 1794 | case TOMOYO_DOMAINPOLICY: |
| 1965 | /* /sys/kernel/security/tomoyo/domain_policy */ | 1795 | /* /sys/kernel/security/tomoyo/domain_policy */ |
| 1966 | head->write = tomoyo_write_domain_policy; | 1796 | head->write = tomoyo_write_domain; |
| 1967 | head->read = tomoyo_read_domain_policy; | 1797 | head->read = tomoyo_read_domain; |
| 1968 | break; | 1798 | break; |
| 1969 | case TOMOYO_EXCEPTIONPOLICY: | 1799 | case TOMOYO_EXCEPTIONPOLICY: |
| 1970 | /* /sys/kernel/security/tomoyo/exception_policy */ | 1800 | /* /sys/kernel/security/tomoyo/exception_policy */ |
| 1971 | head->write = tomoyo_write_exception_policy; | 1801 | head->write = tomoyo_write_exception; |
| 1972 | head->read = tomoyo_read_exception_policy; | 1802 | head->read = tomoyo_read_exception; |
| 1973 | break; | 1803 | break; |
| 1974 | case TOMOYO_SELFDOMAIN: | 1804 | case TOMOYO_SELFDOMAIN: |
| 1975 | /* /sys/kernel/security/tomoyo/self_domain */ | 1805 | /* /sys/kernel/security/tomoyo/self_domain */ |
| @@ -2001,10 +1831,15 @@ static int tomoyo_open_control(const u8 type, struct file *file) | |||
| 2001 | head->write = tomoyo_write_profile; | 1831 | head->write = tomoyo_write_profile; |
| 2002 | head->read = tomoyo_read_profile; | 1832 | head->read = tomoyo_read_profile; |
| 2003 | break; | 1833 | break; |
| 1834 | case TOMOYO_QUERY: /* /sys/kernel/security/tomoyo/query */ | ||
| 1835 | head->poll = tomoyo_poll_query; | ||
| 1836 | head->write = tomoyo_write_answer; | ||
| 1837 | head->read = tomoyo_read_query; | ||
| 1838 | break; | ||
| 2004 | case TOMOYO_MANAGER: | 1839 | case TOMOYO_MANAGER: |
| 2005 | /* /sys/kernel/security/tomoyo/manager */ | 1840 | /* /sys/kernel/security/tomoyo/manager */ |
| 2006 | head->write = tomoyo_write_manager_policy; | 1841 | head->write = tomoyo_write_manager; |
| 2007 | head->read = tomoyo_read_manager_policy; | 1842 | head->read = tomoyo_read_manager; |
| 2008 | break; | 1843 | break; |
| 2009 | } | 1844 | } |
| 2010 | if (!(file->f_mode & FMODE_READ)) { | 1845 | if (!(file->f_mode & FMODE_READ)) { |
| @@ -2013,7 +1848,9 @@ static int tomoyo_open_control(const u8 type, struct file *file) | |||
| 2013 | * for reading. | 1848 | * for reading. |
| 2014 | */ | 1849 | */ |
| 2015 | head->read = NULL; | 1850 | head->read = NULL; |
| 2016 | } else { | 1851 | head->poll = NULL; |
| 1852 | } else if (!head->poll) { | ||
| 1853 | /* Don't allocate read_buf for poll() access. */ | ||
| 2017 | if (!head->readbuf_size) | 1854 | if (!head->readbuf_size) |
| 2018 | head->readbuf_size = 4096 * 2; | 1855 | head->readbuf_size = 4096 * 2; |
| 2019 | head->read_buf = kzalloc(head->readbuf_size, GFP_NOFS); | 1856 | head->read_buf = kzalloc(head->readbuf_size, GFP_NOFS); |
| @@ -2037,7 +1874,8 @@ static int tomoyo_open_control(const u8 type, struct file *file) | |||
| 2037 | return -ENOMEM; | 1874 | return -ENOMEM; |
| 2038 | } | 1875 | } |
| 2039 | } | 1876 | } |
| 2040 | head->reader_idx = tomoyo_read_lock(); | 1877 | if (type != TOMOYO_QUERY) |
| 1878 | head->reader_idx = tomoyo_read_lock(); | ||
| 2041 | file->private_data = head; | 1879 | file->private_data = head; |
| 2042 | /* | 1880 | /* |
| 2043 | * Call the handler now if the file is | 1881 | * Call the handler now if the file is |
| @@ -2048,10 +1886,35 @@ static int tomoyo_open_control(const u8 type, struct file *file) | |||
| 2048 | */ | 1886 | */ |
| 2049 | if (type == TOMOYO_SELFDOMAIN) | 1887 | if (type == TOMOYO_SELFDOMAIN) |
| 2050 | tomoyo_read_control(file, NULL, 0); | 1888 | tomoyo_read_control(file, NULL, 0); |
| 1889 | /* | ||
| 1890 | * If the file is /sys/kernel/security/tomoyo/query , increment the | ||
| 1891 | * observer counter. | ||
| 1892 | * The obserber counter is used by tomoyo_supervisor() to see if | ||
| 1893 | * there is some process monitoring /sys/kernel/security/tomoyo/query. | ||
| 1894 | */ | ||
| 1895 | else if (type == TOMOYO_QUERY) | ||
| 1896 | atomic_inc(&tomoyo_query_observers); | ||
| 2051 | return 0; | 1897 | return 0; |
| 2052 | } | 1898 | } |
| 2053 | 1899 | ||
| 2054 | /** | 1900 | /** |
| 1901 | * tomoyo_poll_control - poll() for /sys/kernel/security/tomoyo/ interface. | ||
| 1902 | * | ||
| 1903 | * @file: Pointer to "struct file". | ||
| 1904 | * @wait: Pointer to "poll_table". | ||
| 1905 | * | ||
| 1906 | * Waits for read readiness. | ||
| 1907 | * /sys/kernel/security/tomoyo/query is handled by /usr/sbin/tomoyo-queryd . | ||
| 1908 | */ | ||
| 1909 | int tomoyo_poll_control(struct file *file, poll_table *wait) | ||
| 1910 | { | ||
| 1911 | struct tomoyo_io_buffer *head = file->private_data; | ||
| 1912 | if (!head->poll) | ||
| 1913 | return -ENOSYS; | ||
| 1914 | return head->poll(file, wait); | ||
| 1915 | } | ||
| 1916 | |||
| 1917 | /** | ||
| 2055 | * tomoyo_read_control - read() for /sys/kernel/security/tomoyo/ interface. | 1918 | * tomoyo_read_control - read() for /sys/kernel/security/tomoyo/ interface. |
| 2056 | * | 1919 | * |
| 2057 | * @file: Pointer to "struct file". | 1920 | * @file: Pointer to "struct file". |
| @@ -2062,36 +1925,23 @@ static int tomoyo_open_control(const u8 type, struct file *file) | |||
| 2062 | * | 1925 | * |
| 2063 | * Caller holds tomoyo_read_lock(). | 1926 | * Caller holds tomoyo_read_lock(). |
| 2064 | */ | 1927 | */ |
| 2065 | static int tomoyo_read_control(struct file *file, char __user *buffer, | 1928 | int tomoyo_read_control(struct file *file, char __user *buffer, |
| 2066 | const int buffer_len) | 1929 | const int buffer_len) |
| 2067 | { | 1930 | { |
| 2068 | int len = 0; | 1931 | int len; |
| 2069 | struct tomoyo_io_buffer *head = file->private_data; | 1932 | struct tomoyo_io_buffer *head = file->private_data; |
| 2070 | char *cp; | ||
| 2071 | 1933 | ||
| 2072 | if (!head->read) | 1934 | if (!head->read) |
| 2073 | return -ENOSYS; | 1935 | return -ENOSYS; |
| 2074 | if (mutex_lock_interruptible(&head->io_sem)) | 1936 | if (mutex_lock_interruptible(&head->io_sem)) |
| 2075 | return -EINTR; | 1937 | return -EINTR; |
| 2076 | /* Call the policy handler. */ | 1938 | head->read_user_buf = buffer; |
| 2077 | len = head->read(head); | 1939 | head->read_user_buf_avail = buffer_len; |
| 2078 | if (len < 0) | 1940 | if (tomoyo_flush(head)) |
| 2079 | goto out; | 1941 | /* Call the policy handler. */ |
| 2080 | /* Write to buffer. */ | 1942 | head->read(head); |
| 2081 | len = head->read_avail; | 1943 | tomoyo_flush(head); |
| 2082 | if (len > buffer_len) | 1944 | len = head->read_user_buf - buffer; |
| 2083 | len = buffer_len; | ||
| 2084 | if (!len) | ||
| 2085 | goto out; | ||
| 2086 | /* head->read_buf changes by some functions. */ | ||
| 2087 | cp = head->read_buf; | ||
| 2088 | if (copy_to_user(buffer, cp, len)) { | ||
| 2089 | len = -EFAULT; | ||
| 2090 | goto out; | ||
| 2091 | } | ||
| 2092 | head->read_avail -= len; | ||
| 2093 | memmove(cp, cp + len, head->read_avail); | ||
| 2094 | out: | ||
| 2095 | mutex_unlock(&head->io_sem); | 1945 | mutex_unlock(&head->io_sem); |
| 2096 | return len; | 1946 | return len; |
| 2097 | } | 1947 | } |
| @@ -2107,8 +1957,8 @@ static int tomoyo_read_control(struct file *file, char __user *buffer, | |||
| 2107 | * | 1957 | * |
| 2108 | * Caller holds tomoyo_read_lock(). | 1958 | * Caller holds tomoyo_read_lock(). |
| 2109 | */ | 1959 | */ |
| 2110 | static int tomoyo_write_control(struct file *file, const char __user *buffer, | 1960 | int tomoyo_write_control(struct file *file, const char __user *buffer, |
| 2111 | const int buffer_len) | 1961 | const int buffer_len) |
| 2112 | { | 1962 | { |
| 2113 | struct tomoyo_io_buffer *head = file->private_data; | 1963 | struct tomoyo_io_buffer *head = file->private_data; |
| 2114 | int error = buffer_len; | 1964 | int error = buffer_len; |
| @@ -2121,8 +1971,7 @@ static int tomoyo_write_control(struct file *file, const char __user *buffer, | |||
| 2121 | return -EFAULT; | 1971 | return -EFAULT; |
| 2122 | /* Don't allow updating policies by non manager programs. */ | 1972 | /* Don't allow updating policies by non manager programs. */ |
| 2123 | if (head->write != tomoyo_write_pid && | 1973 | if (head->write != tomoyo_write_pid && |
| 2124 | head->write != tomoyo_write_domain_policy && | 1974 | head->write != tomoyo_write_domain && !tomoyo_manager()) |
| 2125 | !tomoyo_is_policy_manager()) | ||
| 2126 | return -EPERM; | 1975 | return -EPERM; |
| 2127 | if (mutex_lock_interruptible(&head->io_sem)) | 1976 | if (mutex_lock_interruptible(&head->io_sem)) |
| 2128 | return -EINTR; | 1977 | return -EINTR; |
| @@ -2159,12 +2008,19 @@ static int tomoyo_write_control(struct file *file, const char __user *buffer, | |||
| 2159 | * | 2008 | * |
| 2160 | * Caller looses tomoyo_read_lock(). | 2009 | * Caller looses tomoyo_read_lock(). |
| 2161 | */ | 2010 | */ |
| 2162 | static int tomoyo_close_control(struct file *file) | 2011 | int tomoyo_close_control(struct file *file) |
| 2163 | { | 2012 | { |
| 2164 | struct tomoyo_io_buffer *head = file->private_data; | 2013 | struct tomoyo_io_buffer *head = file->private_data; |
| 2165 | const bool is_write = !!head->write_buf; | 2014 | const bool is_write = !!head->write_buf; |
| 2166 | 2015 | ||
| 2167 | tomoyo_read_unlock(head->reader_idx); | 2016 | /* |
| 2017 | * If the file is /sys/kernel/security/tomoyo/query , decrement the | ||
| 2018 | * observer counter. | ||
| 2019 | */ | ||
| 2020 | if (head->type == TOMOYO_QUERY) | ||
| 2021 | atomic_dec(&tomoyo_query_observers); | ||
| 2022 | else | ||
| 2023 | tomoyo_read_unlock(head->reader_idx); | ||
| 2168 | /* Release memory used for policy I/O. */ | 2024 | /* Release memory used for policy I/O. */ |
| 2169 | kfree(head->read_buf); | 2025 | kfree(head->read_buf); |
| 2170 | head->read_buf = NULL; | 2026 | head->read_buf = NULL; |
| @@ -2179,129 +2035,25 @@ static int tomoyo_close_control(struct file *file) | |||
| 2179 | } | 2035 | } |
| 2180 | 2036 | ||
| 2181 | /** | 2037 | /** |
| 2182 | * tomoyo_open - open() for /sys/kernel/security/tomoyo/ interface. | 2038 | * tomoyo_check_profile - Check all profiles currently assigned to domains are defined. |
| 2183 | * | ||
| 2184 | * @inode: Pointer to "struct inode". | ||
| 2185 | * @file: Pointer to "struct file". | ||
| 2186 | * | ||
| 2187 | * Returns 0 on success, negative value otherwise. | ||
| 2188 | */ | ||
| 2189 | static int tomoyo_open(struct inode *inode, struct file *file) | ||
| 2190 | { | ||
| 2191 | const int key = ((u8 *) file->f_path.dentry->d_inode->i_private) | ||
| 2192 | - ((u8 *) NULL); | ||
| 2193 | return tomoyo_open_control(key, file); | ||
| 2194 | } | ||
| 2195 | |||
| 2196 | /** | ||
| 2197 | * tomoyo_release - close() for /sys/kernel/security/tomoyo/ interface. | ||
| 2198 | * | ||
| 2199 | * @inode: Pointer to "struct inode". | ||
| 2200 | * @file: Pointer to "struct file". | ||
| 2201 | * | ||
| 2202 | * Returns 0 on success, negative value otherwise. | ||
| 2203 | */ | ||
| 2204 | static int tomoyo_release(struct inode *inode, struct file *file) | ||
| 2205 | { | ||
| 2206 | return tomoyo_close_control(file); | ||
| 2207 | } | ||
| 2208 | |||
| 2209 | /** | ||
| 2210 | * tomoyo_read - read() for /sys/kernel/security/tomoyo/ interface. | ||
| 2211 | * | ||
| 2212 | * @file: Pointer to "struct file". | ||
| 2213 | * @buf: Pointer to buffer. | ||
| 2214 | * @count: Size of @buf. | ||
| 2215 | * @ppos: Unused. | ||
| 2216 | * | ||
| 2217 | * Returns bytes read on success, negative value otherwise. | ||
| 2218 | */ | 2039 | */ |
| 2219 | static ssize_t tomoyo_read(struct file *file, char __user *buf, size_t count, | 2040 | void tomoyo_check_profile(void) |
| 2220 | loff_t *ppos) | ||
| 2221 | { | 2041 | { |
| 2222 | return tomoyo_read_control(file, buf, count); | 2042 | struct tomoyo_domain_info *domain; |
| 2223 | } | 2043 | const int idx = tomoyo_read_lock(); |
| 2224 | 2044 | tomoyo_policy_loaded = true; | |
| 2225 | /** | 2045 | /* Check all profiles currently assigned to domains are defined. */ |
| 2226 | * tomoyo_write - write() for /sys/kernel/security/tomoyo/ interface. | 2046 | list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { |
| 2227 | * | 2047 | const u8 profile = domain->profile; |
| 2228 | * @file: Pointer to "struct file". | 2048 | if (tomoyo_profile_ptr[profile]) |
| 2229 | * @buf: Pointer to buffer. | 2049 | continue; |
| 2230 | * @count: Size of @buf. | 2050 | panic("Profile %u (used by '%s') not defined.\n", |
| 2231 | * @ppos: Unused. | 2051 | profile, domain->domainname->name); |
| 2232 | * | 2052 | } |
| 2233 | * Returns @count on success, negative value otherwise. | 2053 | tomoyo_read_unlock(idx); |
| 2234 | */ | 2054 | if (tomoyo_profile_version != 20090903) |
| 2235 | static ssize_t tomoyo_write(struct file *file, const char __user *buf, | 2055 | panic("Profile version %u is not supported.\n", |
| 2236 | size_t count, loff_t *ppos) | 2056 | tomoyo_profile_version); |
| 2237 | { | 2057 | printk(KERN_INFO "TOMOYO: 2.3.0\n"); |
| 2238 | return tomoyo_write_control(file, buf, count); | 2058 | printk(KERN_INFO "Mandatory Access Control activated.\n"); |
| 2239 | } | ||
| 2240 | |||
| 2241 | /* | ||
| 2242 | * tomoyo_operations is a "struct file_operations" which is used for handling | ||
| 2243 | * /sys/kernel/security/tomoyo/ interface. | ||
| 2244 | * | ||
| 2245 | * Some files under /sys/kernel/security/tomoyo/ directory accept open(O_RDWR). | ||
| 2246 | * See tomoyo_io_buffer for internals. | ||
| 2247 | */ | ||
| 2248 | static const struct file_operations tomoyo_operations = { | ||
| 2249 | .open = tomoyo_open, | ||
| 2250 | .release = tomoyo_release, | ||
| 2251 | .read = tomoyo_read, | ||
| 2252 | .write = tomoyo_write, | ||
| 2253 | }; | ||
| 2254 | |||
| 2255 | /** | ||
| 2256 | * tomoyo_create_entry - Create interface files under /sys/kernel/security/tomoyo/ directory. | ||
| 2257 | * | ||
| 2258 | * @name: The name of the interface file. | ||
| 2259 | * @mode: The permission of the interface file. | ||
| 2260 | * @parent: The parent directory. | ||
| 2261 | * @key: Type of interface. | ||
| 2262 | * | ||
| 2263 | * Returns nothing. | ||
| 2264 | */ | ||
| 2265 | static void __init tomoyo_create_entry(const char *name, const mode_t mode, | ||
| 2266 | struct dentry *parent, const u8 key) | ||
| 2267 | { | ||
| 2268 | securityfs_create_file(name, mode, parent, ((u8 *) NULL) + key, | ||
| 2269 | &tomoyo_operations); | ||
| 2270 | } | ||
| 2271 | |||
| 2272 | /** | ||
| 2273 | * tomoyo_initerface_init - Initialize /sys/kernel/security/tomoyo/ interface. | ||
| 2274 | * | ||
| 2275 | * Returns 0. | ||
| 2276 | */ | ||
| 2277 | static int __init tomoyo_initerface_init(void) | ||
| 2278 | { | ||
| 2279 | struct dentry *tomoyo_dir; | ||
| 2280 | |||
| 2281 | /* Don't create securityfs entries unless registered. */ | ||
| 2282 | if (current_cred()->security != &tomoyo_kernel_domain) | ||
| 2283 | return 0; | ||
| 2284 | |||
| 2285 | tomoyo_dir = securityfs_create_dir("tomoyo", NULL); | ||
| 2286 | tomoyo_create_entry("domain_policy", 0600, tomoyo_dir, | ||
| 2287 | TOMOYO_DOMAINPOLICY); | ||
| 2288 | tomoyo_create_entry("exception_policy", 0600, tomoyo_dir, | ||
| 2289 | TOMOYO_EXCEPTIONPOLICY); | ||
| 2290 | tomoyo_create_entry("self_domain", 0400, tomoyo_dir, | ||
| 2291 | TOMOYO_SELFDOMAIN); | ||
| 2292 | tomoyo_create_entry(".domain_status", 0600, tomoyo_dir, | ||
| 2293 | TOMOYO_DOMAIN_STATUS); | ||
| 2294 | tomoyo_create_entry(".process_status", 0600, tomoyo_dir, | ||
| 2295 | TOMOYO_PROCESS_STATUS); | ||
| 2296 | tomoyo_create_entry("meminfo", 0600, tomoyo_dir, | ||
| 2297 | TOMOYO_MEMINFO); | ||
| 2298 | tomoyo_create_entry("profile", 0600, tomoyo_dir, | ||
| 2299 | TOMOYO_PROFILE); | ||
| 2300 | tomoyo_create_entry("manager", 0600, tomoyo_dir, | ||
| 2301 | TOMOYO_MANAGER); | ||
| 2302 | tomoyo_create_entry("version", 0400, tomoyo_dir, | ||
| 2303 | TOMOYO_VERSION); | ||
| 2304 | return 0; | ||
| 2305 | } | 2059 | } |
| 2306 | |||
| 2307 | fs_initcall(tomoyo_initerface_init); | ||
diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index 9f1ae5e3ba51..04454cb7b24a 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h | |||
| @@ -20,6 +20,7 @@ | |||
| 20 | #include <linux/mount.h> | 20 | #include <linux/mount.h> |
| 21 | #include <linux/list.h> | 21 | #include <linux/list.h> |
| 22 | #include <linux/cred.h> | 22 | #include <linux/cred.h> |
| 23 | #include <linux/poll.h> | ||
| 23 | struct linux_binprm; | 24 | struct linux_binprm; |
| 24 | 25 | ||
| 25 | /********** Constants definitions. **********/ | 26 | /********** Constants definitions. **********/ |
| @@ -32,20 +33,44 @@ struct linux_binprm; | |||
| 32 | #define TOMOYO_HASH_BITS 8 | 33 | #define TOMOYO_HASH_BITS 8 |
| 33 | #define TOMOYO_MAX_HASH (1u<<TOMOYO_HASH_BITS) | 34 | #define TOMOYO_MAX_HASH (1u<<TOMOYO_HASH_BITS) |
| 34 | 35 | ||
| 35 | /* | 36 | #define TOMOYO_EXEC_TMPSIZE 4096 |
| 36 | * This is the max length of a token. | ||
| 37 | * | ||
| 38 | * A token consists of only ASCII printable characters. | ||
| 39 | * Non printable characters in a token is represented in \ooo style | ||
| 40 | * octal string. Thus, \ itself is represented as \\. | ||
| 41 | */ | ||
| 42 | #define TOMOYO_MAX_PATHNAME_LEN 4000 | ||
| 43 | 37 | ||
| 44 | /* Profile number is an integer between 0 and 255. */ | 38 | /* Profile number is an integer between 0 and 255. */ |
| 45 | #define TOMOYO_MAX_PROFILES 256 | 39 | #define TOMOYO_MAX_PROFILES 256 |
| 46 | 40 | ||
| 41 | enum tomoyo_mode_index { | ||
| 42 | TOMOYO_CONFIG_DISABLED, | ||
| 43 | TOMOYO_CONFIG_LEARNING, | ||
| 44 | TOMOYO_CONFIG_PERMISSIVE, | ||
| 45 | TOMOYO_CONFIG_ENFORCING, | ||
| 46 | TOMOYO_CONFIG_USE_DEFAULT = 255 | ||
| 47 | }; | ||
| 48 | |||
| 49 | enum tomoyo_policy_id { | ||
| 50 | TOMOYO_ID_GROUP, | ||
| 51 | TOMOYO_ID_PATH_GROUP, | ||
| 52 | TOMOYO_ID_NUMBER_GROUP, | ||
| 53 | TOMOYO_ID_TRANSITION_CONTROL, | ||
| 54 | TOMOYO_ID_AGGREGATOR, | ||
| 55 | TOMOYO_ID_GLOBALLY_READABLE, | ||
| 56 | TOMOYO_ID_PATTERN, | ||
| 57 | TOMOYO_ID_NO_REWRITE, | ||
| 58 | TOMOYO_ID_MANAGER, | ||
| 59 | TOMOYO_ID_NAME, | ||
| 60 | TOMOYO_ID_ACL, | ||
| 61 | TOMOYO_ID_DOMAIN, | ||
| 62 | TOMOYO_MAX_POLICY | ||
| 63 | }; | ||
| 64 | |||
| 65 | enum tomoyo_group_id { | ||
| 66 | TOMOYO_PATH_GROUP, | ||
| 67 | TOMOYO_NUMBER_GROUP, | ||
| 68 | TOMOYO_MAX_GROUP | ||
| 69 | }; | ||
| 70 | |||
| 47 | /* Keywords for ACLs. */ | 71 | /* Keywords for ACLs. */ |
| 48 | #define TOMOYO_KEYWORD_ALIAS "alias " | 72 | #define TOMOYO_KEYWORD_AGGREGATOR "aggregator " |
| 73 | #define TOMOYO_KEYWORD_ALLOW_MOUNT "allow_mount " | ||
| 49 | #define TOMOYO_KEYWORD_ALLOW_READ "allow_read " | 74 | #define TOMOYO_KEYWORD_ALLOW_READ "allow_read " |
| 50 | #define TOMOYO_KEYWORD_DELETE "delete " | 75 | #define TOMOYO_KEYWORD_DELETE "delete " |
| 51 | #define TOMOYO_KEYWORD_DENY_REWRITE "deny_rewrite " | 76 | #define TOMOYO_KEYWORD_DENY_REWRITE "deny_rewrite " |
| @@ -55,36 +80,51 @@ struct linux_binprm; | |||
| 55 | #define TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN "no_initialize_domain " | 80 | #define TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN "no_initialize_domain " |
| 56 | #define TOMOYO_KEYWORD_NO_KEEP_DOMAIN "no_keep_domain " | 81 | #define TOMOYO_KEYWORD_NO_KEEP_DOMAIN "no_keep_domain " |
| 57 | #define TOMOYO_KEYWORD_PATH_GROUP "path_group " | 82 | #define TOMOYO_KEYWORD_PATH_GROUP "path_group " |
| 83 | #define TOMOYO_KEYWORD_NUMBER_GROUP "number_group " | ||
| 58 | #define TOMOYO_KEYWORD_SELECT "select " | 84 | #define TOMOYO_KEYWORD_SELECT "select " |
| 59 | #define TOMOYO_KEYWORD_USE_PROFILE "use_profile " | 85 | #define TOMOYO_KEYWORD_USE_PROFILE "use_profile " |
| 60 | #define TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ "ignore_global_allow_read" | 86 | #define TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ "ignore_global_allow_read" |
| 87 | #define TOMOYO_KEYWORD_QUOTA_EXCEEDED "quota_exceeded" | ||
| 88 | #define TOMOYO_KEYWORD_TRANSITION_FAILED "transition_failed" | ||
| 61 | /* A domain definition starts with <kernel>. */ | 89 | /* A domain definition starts with <kernel>. */ |
| 62 | #define TOMOYO_ROOT_NAME "<kernel>" | 90 | #define TOMOYO_ROOT_NAME "<kernel>" |
| 63 | #define TOMOYO_ROOT_NAME_LEN (sizeof(TOMOYO_ROOT_NAME) - 1) | 91 | #define TOMOYO_ROOT_NAME_LEN (sizeof(TOMOYO_ROOT_NAME) - 1) |
| 64 | 92 | ||
| 65 | /* Index numbers for Access Controls. */ | 93 | /* Value type definition. */ |
| 66 | enum tomoyo_mac_index { | 94 | #define TOMOYO_VALUE_TYPE_INVALID 0 |
| 67 | TOMOYO_MAC_FOR_FILE, /* domain_policy.conf */ | 95 | #define TOMOYO_VALUE_TYPE_DECIMAL 1 |
| 68 | TOMOYO_MAX_ACCEPT_ENTRY, | 96 | #define TOMOYO_VALUE_TYPE_OCTAL 2 |
| 69 | TOMOYO_VERBOSE, | 97 | #define TOMOYO_VALUE_TYPE_HEXADECIMAL 3 |
| 70 | TOMOYO_MAX_CONTROL_INDEX | 98 | |
| 99 | enum tomoyo_transition_type { | ||
| 100 | /* Do not change this order, */ | ||
| 101 | TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE, | ||
| 102 | TOMOYO_TRANSITION_CONTROL_INITIALIZE, | ||
| 103 | TOMOYO_TRANSITION_CONTROL_NO_KEEP, | ||
| 104 | TOMOYO_TRANSITION_CONTROL_KEEP, | ||
| 105 | TOMOYO_MAX_TRANSITION_TYPE | ||
| 71 | }; | 106 | }; |
| 72 | 107 | ||
| 73 | /* Index numbers for Access Controls. */ | 108 | /* Index numbers for Access Controls. */ |
| 74 | enum tomoyo_acl_entry_type_index { | 109 | enum tomoyo_acl_entry_type_index { |
| 75 | TOMOYO_TYPE_PATH_ACL, | 110 | TOMOYO_TYPE_PATH_ACL, |
| 76 | TOMOYO_TYPE_PATH2_ACL, | 111 | TOMOYO_TYPE_PATH2_ACL, |
| 112 | TOMOYO_TYPE_PATH_NUMBER_ACL, | ||
| 113 | TOMOYO_TYPE_MKDEV_ACL, | ||
| 114 | TOMOYO_TYPE_MOUNT_ACL, | ||
| 77 | }; | 115 | }; |
| 78 | 116 | ||
| 79 | /* Index numbers for File Controls. */ | 117 | /* Index numbers for File Controls. */ |
| 80 | 118 | ||
| 81 | /* | 119 | /* |
| 82 | * TYPE_READ_WRITE_ACL is special. TYPE_READ_WRITE_ACL is automatically set | 120 | * TOMOYO_TYPE_READ_WRITE is special. TOMOYO_TYPE_READ_WRITE is automatically |
| 83 | * if both TYPE_READ_ACL and TYPE_WRITE_ACL are set. Both TYPE_READ_ACL and | 121 | * set if both TOMOYO_TYPE_READ and TOMOYO_TYPE_WRITE are set. |
| 84 | * TYPE_WRITE_ACL are automatically set if TYPE_READ_WRITE_ACL is set. | 122 | * Both TOMOYO_TYPE_READ and TOMOYO_TYPE_WRITE are automatically set if |
| 85 | * TYPE_READ_WRITE_ACL is automatically cleared if either TYPE_READ_ACL or | 123 | * TOMOYO_TYPE_READ_WRITE is set. |
| 86 | * TYPE_WRITE_ACL is cleared. Both TYPE_READ_ACL and TYPE_WRITE_ACL are | 124 | * TOMOYO_TYPE_READ_WRITE is automatically cleared if either TOMOYO_TYPE_READ |
| 87 | * automatically cleared if TYPE_READ_WRITE_ACL is cleared. | 125 | * or TOMOYO_TYPE_WRITE is cleared. |
| 126 | * Both TOMOYO_TYPE_READ and TOMOYO_TYPE_WRITE are automatically cleared if | ||
| 127 | * TOMOYO_TYPE_READ_WRITE is cleared. | ||
| 88 | */ | 128 | */ |
| 89 | 129 | ||
| 90 | enum tomoyo_path_acl_index { | 130 | enum tomoyo_path_acl_index { |
| @@ -92,27 +132,24 @@ enum tomoyo_path_acl_index { | |||
| 92 | TOMOYO_TYPE_EXECUTE, | 132 | TOMOYO_TYPE_EXECUTE, |
| 93 | TOMOYO_TYPE_READ, | 133 | TOMOYO_TYPE_READ, |
| 94 | TOMOYO_TYPE_WRITE, | 134 | TOMOYO_TYPE_WRITE, |
| 95 | TOMOYO_TYPE_CREATE, | ||
| 96 | TOMOYO_TYPE_UNLINK, | 135 | TOMOYO_TYPE_UNLINK, |
| 97 | TOMOYO_TYPE_MKDIR, | ||
| 98 | TOMOYO_TYPE_RMDIR, | 136 | TOMOYO_TYPE_RMDIR, |
| 99 | TOMOYO_TYPE_MKFIFO, | ||
| 100 | TOMOYO_TYPE_MKSOCK, | ||
| 101 | TOMOYO_TYPE_MKBLOCK, | ||
| 102 | TOMOYO_TYPE_MKCHAR, | ||
| 103 | TOMOYO_TYPE_TRUNCATE, | 137 | TOMOYO_TYPE_TRUNCATE, |
| 104 | TOMOYO_TYPE_SYMLINK, | 138 | TOMOYO_TYPE_SYMLINK, |
| 105 | TOMOYO_TYPE_REWRITE, | 139 | TOMOYO_TYPE_REWRITE, |
| 106 | TOMOYO_TYPE_IOCTL, | ||
| 107 | TOMOYO_TYPE_CHMOD, | ||
| 108 | TOMOYO_TYPE_CHOWN, | ||
| 109 | TOMOYO_TYPE_CHGRP, | ||
| 110 | TOMOYO_TYPE_CHROOT, | 140 | TOMOYO_TYPE_CHROOT, |
| 111 | TOMOYO_TYPE_MOUNT, | ||
| 112 | TOMOYO_TYPE_UMOUNT, | 141 | TOMOYO_TYPE_UMOUNT, |
| 113 | TOMOYO_MAX_PATH_OPERATION | 142 | TOMOYO_MAX_PATH_OPERATION |
| 114 | }; | 143 | }; |
| 115 | 144 | ||
| 145 | #define TOMOYO_RW_MASK ((1 << TOMOYO_TYPE_READ) | (1 << TOMOYO_TYPE_WRITE)) | ||
| 146 | |||
| 147 | enum tomoyo_mkdev_acl_index { | ||
| 148 | TOMOYO_TYPE_MKBLOCK, | ||
| 149 | TOMOYO_TYPE_MKCHAR, | ||
| 150 | TOMOYO_MAX_MKDEV_OPERATION | ||
| 151 | }; | ||
| 152 | |||
| 116 | enum tomoyo_path2_acl_index { | 153 | enum tomoyo_path2_acl_index { |
| 117 | TOMOYO_TYPE_LINK, | 154 | TOMOYO_TYPE_LINK, |
| 118 | TOMOYO_TYPE_RENAME, | 155 | TOMOYO_TYPE_RENAME, |
| @@ -120,6 +157,18 @@ enum tomoyo_path2_acl_index { | |||
| 120 | TOMOYO_MAX_PATH2_OPERATION | 157 | TOMOYO_MAX_PATH2_OPERATION |
| 121 | }; | 158 | }; |
| 122 | 159 | ||
| 160 | enum tomoyo_path_number_acl_index { | ||
| 161 | TOMOYO_TYPE_CREATE, | ||
| 162 | TOMOYO_TYPE_MKDIR, | ||
| 163 | TOMOYO_TYPE_MKFIFO, | ||
| 164 | TOMOYO_TYPE_MKSOCK, | ||
| 165 | TOMOYO_TYPE_IOCTL, | ||
| 166 | TOMOYO_TYPE_CHMOD, | ||
| 167 | TOMOYO_TYPE_CHOWN, | ||
| 168 | TOMOYO_TYPE_CHGRP, | ||
| 169 | TOMOYO_MAX_PATH_NUMBER_OPERATION | ||
| 170 | }; | ||
| 171 | |||
| 123 | enum tomoyo_securityfs_interface_index { | 172 | enum tomoyo_securityfs_interface_index { |
| 124 | TOMOYO_DOMAINPOLICY, | 173 | TOMOYO_DOMAINPOLICY, |
| 125 | TOMOYO_EXCEPTIONPOLICY, | 174 | TOMOYO_EXCEPTIONPOLICY, |
| @@ -129,20 +178,109 @@ enum tomoyo_securityfs_interface_index { | |||
| 129 | TOMOYO_SELFDOMAIN, | 178 | TOMOYO_SELFDOMAIN, |
| 130 | TOMOYO_VERSION, | 179 | TOMOYO_VERSION, |
| 131 | TOMOYO_PROFILE, | 180 | TOMOYO_PROFILE, |
| 181 | TOMOYO_QUERY, | ||
| 132 | TOMOYO_MANAGER | 182 | TOMOYO_MANAGER |
| 133 | }; | 183 | }; |
| 134 | 184 | ||
| 185 | enum tomoyo_mac_index { | ||
| 186 | TOMOYO_MAC_FILE_EXECUTE, | ||
| 187 | TOMOYO_MAC_FILE_OPEN, | ||
| 188 | TOMOYO_MAC_FILE_CREATE, | ||
| 189 | TOMOYO_MAC_FILE_UNLINK, | ||
| 190 | TOMOYO_MAC_FILE_MKDIR, | ||
| 191 | TOMOYO_MAC_FILE_RMDIR, | ||
| 192 | TOMOYO_MAC_FILE_MKFIFO, | ||
| 193 | TOMOYO_MAC_FILE_MKSOCK, | ||
| 194 | TOMOYO_MAC_FILE_TRUNCATE, | ||
| 195 | TOMOYO_MAC_FILE_SYMLINK, | ||
| 196 | TOMOYO_MAC_FILE_REWRITE, | ||
| 197 | TOMOYO_MAC_FILE_MKBLOCK, | ||
| 198 | TOMOYO_MAC_FILE_MKCHAR, | ||
| 199 | TOMOYO_MAC_FILE_LINK, | ||
| 200 | TOMOYO_MAC_FILE_RENAME, | ||
| 201 | TOMOYO_MAC_FILE_CHMOD, | ||
| 202 | TOMOYO_MAC_FILE_CHOWN, | ||
| 203 | TOMOYO_MAC_FILE_CHGRP, | ||
| 204 | TOMOYO_MAC_FILE_IOCTL, | ||
| 205 | TOMOYO_MAC_FILE_CHROOT, | ||
| 206 | TOMOYO_MAC_FILE_MOUNT, | ||
| 207 | TOMOYO_MAC_FILE_UMOUNT, | ||
| 208 | TOMOYO_MAC_FILE_PIVOT_ROOT, | ||
| 209 | TOMOYO_MAX_MAC_INDEX | ||
| 210 | }; | ||
| 211 | |||
| 212 | enum tomoyo_mac_category_index { | ||
| 213 | TOMOYO_MAC_CATEGORY_FILE, | ||
| 214 | TOMOYO_MAX_MAC_CATEGORY_INDEX | ||
| 215 | }; | ||
| 216 | |||
| 217 | #define TOMOYO_RETRY_REQUEST 1 /* Retry this request. */ | ||
| 218 | |||
| 135 | /********** Structure definitions. **********/ | 219 | /********** Structure definitions. **********/ |
| 136 | 220 | ||
| 137 | /* | 221 | /* |
| 138 | * tomoyo_page_buffer is a structure which is used for holding a pathname | 222 | * tomoyo_acl_head is a structure which is used for holding elements not in |
| 139 | * obtained from "struct dentry" and "struct vfsmount" pair. | 223 | * domain policy. |
| 140 | * As of now, it is 4096 bytes. If users complain that 4096 bytes is too small | 224 | * It has following fields. |
| 141 | * (because TOMOYO escapes non ASCII printable characters using \ooo format), | 225 | * |
| 142 | * we will make the buffer larger. | 226 | * (1) "list" which is linked to tomoyo_policy_list[] . |
| 227 | * (2) "is_deleted" is a bool which is true if marked as deleted, false | ||
| 228 | * otherwise. | ||
| 143 | */ | 229 | */ |
| 144 | struct tomoyo_page_buffer { | 230 | struct tomoyo_acl_head { |
| 145 | char buffer[4096]; | 231 | struct list_head list; |
| 232 | bool is_deleted; | ||
| 233 | } __packed; | ||
| 234 | |||
| 235 | /* | ||
| 236 | * tomoyo_request_info is a structure which is used for holding | ||
| 237 | * | ||
| 238 | * (1) Domain information of current process. | ||
| 239 | * (2) How many retries are made for this request. | ||
| 240 | * (3) Profile number used for this request. | ||
| 241 | * (4) Access control mode of the profile. | ||
| 242 | */ | ||
| 243 | struct tomoyo_request_info { | ||
| 244 | struct tomoyo_domain_info *domain; | ||
| 245 | /* For holding parameters. */ | ||
| 246 | union { | ||
| 247 | struct { | ||
| 248 | const struct tomoyo_path_info *filename; | ||
| 249 | /* For using wildcards at tomoyo_find_next_domain(). */ | ||
| 250 | const struct tomoyo_path_info *matched_path; | ||
| 251 | u8 operation; | ||
| 252 | } path; | ||
| 253 | struct { | ||
| 254 | const struct tomoyo_path_info *filename1; | ||
| 255 | const struct tomoyo_path_info *filename2; | ||
| 256 | u8 operation; | ||
| 257 | } path2; | ||
| 258 | struct { | ||
| 259 | const struct tomoyo_path_info *filename; | ||
| 260 | unsigned int mode; | ||
| 261 | unsigned int major; | ||
| 262 | unsigned int minor; | ||
| 263 | u8 operation; | ||
| 264 | } mkdev; | ||
| 265 | struct { | ||
| 266 | const struct tomoyo_path_info *filename; | ||
| 267 | unsigned long number; | ||
| 268 | u8 operation; | ||
| 269 | } path_number; | ||
| 270 | struct { | ||
| 271 | const struct tomoyo_path_info *type; | ||
| 272 | const struct tomoyo_path_info *dir; | ||
| 273 | const struct tomoyo_path_info *dev; | ||
| 274 | unsigned long flags; | ||
| 275 | int need_dev; | ||
| 276 | } mount; | ||
| 277 | } param; | ||
| 278 | u8 param_type; | ||
| 279 | bool granted; | ||
| 280 | u8 retry; | ||
| 281 | u8 profile; | ||
| 282 | u8 mode; /* One of tomoyo_mode_index . */ | ||
| 283 | u8 type; | ||
| 146 | }; | 284 | }; |
| 147 | 285 | ||
| 148 | /* | 286 | /* |
| @@ -174,45 +312,31 @@ struct tomoyo_path_info { | |||
| 174 | }; | 312 | }; |
| 175 | 313 | ||
| 176 | /* | 314 | /* |
| 177 | * tomoyo_name_entry is a structure which is used for linking | 315 | * tomoyo_name is a structure which is used for linking |
| 178 | * "struct tomoyo_path_info" into tomoyo_name_list . | 316 | * "struct tomoyo_path_info" into tomoyo_name_list . |
| 179 | */ | 317 | */ |
| 180 | struct tomoyo_name_entry { | 318 | struct tomoyo_name { |
| 181 | struct list_head list; | 319 | struct list_head list; |
| 182 | atomic_t users; | 320 | atomic_t users; |
| 183 | struct tomoyo_path_info entry; | 321 | struct tomoyo_path_info entry; |
| 184 | }; | 322 | }; |
| 185 | 323 | ||
| 186 | /* | ||
| 187 | * tomoyo_path_info_with_data is a structure which is used for holding a | ||
| 188 | * pathname obtained from "struct dentry" and "struct vfsmount" pair. | ||
| 189 | * | ||
| 190 | * "struct tomoyo_path_info_with_data" consists of "struct tomoyo_path_info" | ||
| 191 | * and buffer for the pathname, while "struct tomoyo_page_buffer" consists of | ||
| 192 | * buffer for the pathname only. | ||
| 193 | * | ||
| 194 | * "struct tomoyo_path_info_with_data" is intended to allow TOMOYO to release | ||
| 195 | * both "struct tomoyo_path_info" and buffer for the pathname by single kfree() | ||
| 196 | * so that we don't need to return two pointers to the caller. If the caller | ||
| 197 | * puts "struct tomoyo_path_info" on stack memory, we will be able to remove | ||
| 198 | * "struct tomoyo_path_info_with_data". | ||
| 199 | */ | ||
| 200 | struct tomoyo_path_info_with_data { | ||
| 201 | /* Keep "head" first, for this pointer is passed to kfree(). */ | ||
| 202 | struct tomoyo_path_info head; | ||
| 203 | char barrier1[16]; /* Safeguard for overrun. */ | ||
| 204 | char body[TOMOYO_MAX_PATHNAME_LEN]; | ||
| 205 | char barrier2[16]; /* Safeguard for overrun. */ | ||
| 206 | }; | ||
| 207 | |||
| 208 | struct tomoyo_name_union { | 324 | struct tomoyo_name_union { |
| 209 | const struct tomoyo_path_info *filename; | 325 | const struct tomoyo_path_info *filename; |
| 210 | struct tomoyo_path_group *group; | 326 | struct tomoyo_group *group; |
| 211 | u8 is_group; | 327 | u8 is_group; |
| 212 | }; | 328 | }; |
| 213 | 329 | ||
| 214 | /* Structure for "path_group" directive. */ | 330 | struct tomoyo_number_union { |
| 215 | struct tomoyo_path_group { | 331 | unsigned long values[2]; |
| 332 | struct tomoyo_group *group; | ||
| 333 | u8 min_type; | ||
| 334 | u8 max_type; | ||
| 335 | u8 is_group; | ||
| 336 | }; | ||
| 337 | |||
| 338 | /* Structure for "path_group"/"number_group" directive. */ | ||
| 339 | struct tomoyo_group { | ||
| 216 | struct list_head list; | 340 | struct list_head list; |
| 217 | const struct tomoyo_path_info *group_name; | 341 | const struct tomoyo_path_info *group_name; |
| 218 | struct list_head member_list; | 342 | struct list_head member_list; |
| @@ -220,28 +344,35 @@ struct tomoyo_path_group { | |||
| 220 | }; | 344 | }; |
| 221 | 345 | ||
| 222 | /* Structure for "path_group" directive. */ | 346 | /* Structure for "path_group" directive. */ |
| 223 | struct tomoyo_path_group_member { | 347 | struct tomoyo_path_group { |
| 224 | struct list_head list; | 348 | struct tomoyo_acl_head head; |
| 225 | bool is_deleted; | ||
| 226 | const struct tomoyo_path_info *member_name; | 349 | const struct tomoyo_path_info *member_name; |
| 227 | }; | 350 | }; |
| 228 | 351 | ||
| 352 | /* Structure for "number_group" directive. */ | ||
| 353 | struct tomoyo_number_group { | ||
| 354 | struct tomoyo_acl_head head; | ||
| 355 | struct tomoyo_number_union number; | ||
| 356 | }; | ||
| 357 | |||
| 229 | /* | 358 | /* |
| 230 | * tomoyo_acl_info is a structure which is used for holding | 359 | * tomoyo_acl_info is a structure which is used for holding |
| 231 | * | 360 | * |
| 232 | * (1) "list" which is linked to the ->acl_info_list of | 361 | * (1) "list" which is linked to the ->acl_info_list of |
| 233 | * "struct tomoyo_domain_info" | 362 | * "struct tomoyo_domain_info" |
| 234 | * (2) "type" which tells type of the entry (either | 363 | * (2) "is_deleted" is a bool which is true if this domain is marked as |
| 235 | * "struct tomoyo_path_acl" or "struct tomoyo_path2_acl"). | 364 | * "deleted", false otherwise. |
| 365 | * (3) "type" which tells type of the entry. | ||
| 236 | * | 366 | * |
| 237 | * Packing "struct tomoyo_acl_info" allows | 367 | * Packing "struct tomoyo_acl_info" allows |
| 238 | * "struct tomoyo_path_acl" to embed "u8" + "u16" and | 368 | * "struct tomoyo_path_acl" to embed "u16" and "struct tomoyo_path2_acl" |
| 239 | * "struct tomoyo_path2_acl" to embed "u8" | 369 | * "struct tomoyo_path_number_acl" "struct tomoyo_mkdev_acl" to embed |
| 240 | * without enlarging their structure size. | 370 | * "u8" without enlarging their structure size. |
| 241 | */ | 371 | */ |
| 242 | struct tomoyo_acl_info { | 372 | struct tomoyo_acl_info { |
| 243 | struct list_head list; | 373 | struct list_head list; |
| 244 | u8 type; | 374 | bool is_deleted; |
| 375 | u8 type; /* = one of values in "enum tomoyo_acl_entry_type_index". */ | ||
| 245 | } __packed; | 376 | } __packed; |
| 246 | 377 | ||
| 247 | /* | 378 | /* |
| @@ -299,20 +430,62 @@ struct tomoyo_domain_info { | |||
| 299 | * (3) "name" is the pathname. | 430 | * (3) "name" is the pathname. |
| 300 | * | 431 | * |
| 301 | * Directives held by this structure are "allow_read/write", "allow_execute", | 432 | * Directives held by this structure are "allow_read/write", "allow_execute", |
| 302 | * "allow_read", "allow_write", "allow_create", "allow_unlink", "allow_mkdir", | 433 | * "allow_read", "allow_write", "allow_unlink", "allow_rmdir", |
| 303 | * "allow_rmdir", "allow_mkfifo", "allow_mksock", "allow_mkblock", | 434 | * "allow_truncate", "allow_symlink", "allow_rewrite", "allow_chroot" and |
| 304 | * "allow_mkchar", "allow_truncate", "allow_symlink", "allow_rewrite", | 435 | * "allow_unmount". |
| 305 | * "allow_chmod", "allow_chown", "allow_chgrp", "allow_chroot", "allow_mount" | ||
| 306 | * and "allow_unmount". | ||
| 307 | */ | 436 | */ |
| 308 | struct tomoyo_path_acl { | 437 | struct tomoyo_path_acl { |
| 309 | struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_PATH_ACL */ | 438 | struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_PATH_ACL */ |
| 310 | u8 perm_high; | ||
| 311 | u16 perm; | 439 | u16 perm; |
| 312 | struct tomoyo_name_union name; | 440 | struct tomoyo_name_union name; |
| 313 | }; | 441 | }; |
| 314 | 442 | ||
| 315 | /* | 443 | /* |
| 444 | * tomoyo_path_number_acl is a structure which is used for holding an | ||
| 445 | * entry with one pathname and one number operation. | ||
| 446 | * It has following fields. | ||
| 447 | * | ||
| 448 | * (1) "head" which is a "struct tomoyo_acl_info". | ||
| 449 | * (2) "perm" which is a bitmask of permitted operations. | ||
| 450 | * (3) "name" is the pathname. | ||
| 451 | * (4) "number" is the numeric value. | ||
| 452 | * | ||
| 453 | * Directives held by this structure are "allow_create", "allow_mkdir", | ||
| 454 | * "allow_ioctl", "allow_mkfifo", "allow_mksock", "allow_chmod", "allow_chown" | ||
| 455 | * and "allow_chgrp". | ||
| 456 | * | ||
| 457 | */ | ||
| 458 | struct tomoyo_path_number_acl { | ||
| 459 | struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_PATH_NUMBER_ACL */ | ||
| 460 | u8 perm; | ||
| 461 | struct tomoyo_name_union name; | ||
| 462 | struct tomoyo_number_union number; | ||
| 463 | }; | ||
| 464 | |||
| 465 | /* | ||
| 466 | * tomoyo_mkdev_acl is a structure which is used for holding an | ||
| 467 | * entry with one pathname and three numbers operation. | ||
| 468 | * It has following fields. | ||
| 469 | * | ||
| 470 | * (1) "head" which is a "struct tomoyo_acl_info". | ||
| 471 | * (2) "perm" which is a bitmask of permitted operations. | ||
| 472 | * (3) "mode" is the create mode. | ||
| 473 | * (4) "major" is the major number of device node. | ||
| 474 | * (5) "minor" is the minor number of device node. | ||
| 475 | * | ||
| 476 | * Directives held by this structure are "allow_mkchar", "allow_mkblock". | ||
| 477 | * | ||
| 478 | */ | ||
| 479 | struct tomoyo_mkdev_acl { | ||
| 480 | struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_MKDEV_ACL */ | ||
| 481 | u8 perm; | ||
| 482 | struct tomoyo_name_union name; | ||
| 483 | struct tomoyo_number_union mode; | ||
| 484 | struct tomoyo_number_union major; | ||
| 485 | struct tomoyo_number_union minor; | ||
| 486 | }; | ||
| 487 | |||
| 488 | /* | ||
| 316 | * tomoyo_path2_acl is a structure which is used for holding an | 489 | * tomoyo_path2_acl is a structure which is used for holding an |
| 317 | * entry with two pathnames operation (i.e. link(), rename() and pivot_root()). | 490 | * entry with two pathnames operation (i.e. link(), rename() and pivot_root()). |
| 318 | * It has following fields. | 491 | * It has following fields. |
| @@ -333,53 +506,61 @@ struct tomoyo_path2_acl { | |||
| 333 | }; | 506 | }; |
| 334 | 507 | ||
| 335 | /* | 508 | /* |
| 336 | * tomoyo_io_buffer is a structure which is used for reading and modifying | 509 | * tomoyo_mount_acl is a structure which is used for holding an |
| 337 | * configuration via /sys/kernel/security/tomoyo/ interface. | 510 | * entry for mount operation. |
| 338 | * It has many fields. ->read_var1 , ->read_var2 , ->write_var1 are used as | 511 | * It has following fields. |
| 339 | * cursors. | ||
| 340 | * | 512 | * |
| 341 | * Since the content of /sys/kernel/security/tomoyo/domain_policy is a list of | 513 | * (1) "head" which is a "struct tomoyo_acl_info". |
| 342 | * "struct tomoyo_domain_info" entries and each "struct tomoyo_domain_info" | 514 | * (2) "dev_name" is the device name. |
| 343 | * entry has a list of "struct tomoyo_acl_info", we need two cursors when | 515 | * (3) "dir_name" is the mount point. |
| 344 | * reading (one is for traversing tomoyo_domain_list and the other is for | 516 | * (4) "fs_type" is the filesystem type. |
| 345 | * traversing "struct tomoyo_acl_info"->acl_info_list ). | 517 | * (5) "flags" is the mount flags. |
| 346 | * | 518 | * |
| 347 | * If a line written to /sys/kernel/security/tomoyo/domain_policy starts with | 519 | * Directive held by this structure is "allow_mount". |
| 348 | * "select ", TOMOYO seeks the cursor ->read_var1 and ->write_var1 to the | 520 | */ |
| 349 | * domain with the domainname specified by the rest of that line (NULL is set | 521 | struct tomoyo_mount_acl { |
| 350 | * if seek failed). | 522 | struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_MOUNT_ACL */ |
| 351 | * If a line written to /sys/kernel/security/tomoyo/domain_policy starts with | 523 | struct tomoyo_name_union dev_name; |
| 352 | * "delete ", TOMOYO deletes an entry or a domain specified by the rest of that | 524 | struct tomoyo_name_union dir_name; |
| 353 | * line (->write_var1 is set to NULL if a domain was deleted). | 525 | struct tomoyo_name_union fs_type; |
| 354 | * If a line written to /sys/kernel/security/tomoyo/domain_policy starts with | 526 | struct tomoyo_number_union flags; |
| 355 | * neither "select " nor "delete ", an entry or a domain specified by that line | 527 | }; |
| 356 | * is appended. | 528 | |
| 529 | #define TOMOYO_MAX_IO_READ_QUEUE 32 | ||
| 530 | |||
| 531 | /* | ||
| 532 | * Structure for reading/writing policy via /sys/kernel/security/tomoyo | ||
| 533 | * interfaces. | ||
| 357 | */ | 534 | */ |
| 358 | struct tomoyo_io_buffer { | 535 | struct tomoyo_io_buffer { |
| 359 | int (*read) (struct tomoyo_io_buffer *); | 536 | void (*read) (struct tomoyo_io_buffer *); |
| 360 | int (*write) (struct tomoyo_io_buffer *); | 537 | int (*write) (struct tomoyo_io_buffer *); |
| 538 | int (*poll) (struct file *file, poll_table *wait); | ||
| 361 | /* Exclusive lock for this structure. */ | 539 | /* Exclusive lock for this structure. */ |
| 362 | struct mutex io_sem; | 540 | struct mutex io_sem; |
| 363 | /* Index returned by tomoyo_read_lock(). */ | 541 | /* Index returned by tomoyo_read_lock(). */ |
| 364 | int reader_idx; | 542 | int reader_idx; |
| 365 | /* The position currently reading from. */ | 543 | char __user *read_user_buf; |
| 366 | struct list_head *read_var1; | 544 | int read_user_buf_avail; |
| 367 | /* Extra variables for reading. */ | 545 | struct { |
| 368 | struct list_head *read_var2; | 546 | struct list_head *domain; |
| 547 | struct list_head *group; | ||
| 548 | struct list_head *acl; | ||
| 549 | int avail; | ||
| 550 | int step; | ||
| 551 | int query_index; | ||
| 552 | u16 index; | ||
| 553 | u8 bit; | ||
| 554 | u8 w_pos; | ||
| 555 | bool eof; | ||
| 556 | bool print_this_domain_only; | ||
| 557 | bool print_execute_only; | ||
| 558 | const char *w[TOMOYO_MAX_IO_READ_QUEUE]; | ||
| 559 | } r; | ||
| 369 | /* The position currently writing to. */ | 560 | /* The position currently writing to. */ |
| 370 | struct tomoyo_domain_info *write_var1; | 561 | struct tomoyo_domain_info *write_var1; |
| 371 | /* The step for reading. */ | ||
| 372 | int read_step; | ||
| 373 | /* Buffer for reading. */ | 562 | /* Buffer for reading. */ |
| 374 | char *read_buf; | 563 | char *read_buf; |
| 375 | /* EOF flag for reading. */ | ||
| 376 | bool read_eof; | ||
| 377 | /* Read domain ACL of specified PID? */ | ||
| 378 | bool read_single_domain; | ||
| 379 | /* Extra variable for reading. */ | ||
| 380 | u8 read_bit; | ||
| 381 | /* Bytes available for reading. */ | ||
| 382 | int read_avail; | ||
| 383 | /* Size of read buffer. */ | 564 | /* Size of read buffer. */ |
| 384 | int readbuf_size; | 565 | int readbuf_size; |
| 385 | /* Buffer for writing. */ | 566 | /* Buffer for writing. */ |
| @@ -388,215 +569,203 @@ struct tomoyo_io_buffer { | |||
| 388 | int write_avail; | 569 | int write_avail; |
| 389 | /* Size of write buffer. */ | 570 | /* Size of write buffer. */ |
| 390 | int writebuf_size; | 571 | int writebuf_size; |
| 572 | /* Type of this interface. */ | ||
| 573 | u8 type; | ||
| 391 | }; | 574 | }; |
| 392 | 575 | ||
| 393 | /* | 576 | /* |
| 394 | * tomoyo_globally_readable_file_entry is a structure which is used for holding | 577 | * tomoyo_readable_file is a structure which is used for holding |
| 395 | * "allow_read" entries. | 578 | * "allow_read" entries. |
| 396 | * It has following fields. | 579 | * It has following fields. |
| 397 | * | 580 | * |
| 398 | * (1) "list" which is linked to tomoyo_globally_readable_list . | 581 | * (1) "head" is "struct tomoyo_acl_head". |
| 399 | * (2) "filename" is a pathname which is allowed to open(O_RDONLY). | 582 | * (2) "filename" is a pathname which is allowed to open(O_RDONLY). |
| 400 | * (3) "is_deleted" is a bool which is true if marked as deleted, false | ||
| 401 | * otherwise. | ||
| 402 | */ | 583 | */ |
| 403 | struct tomoyo_globally_readable_file_entry { | 584 | struct tomoyo_readable_file { |
| 404 | struct list_head list; | 585 | struct tomoyo_acl_head head; |
| 405 | const struct tomoyo_path_info *filename; | 586 | const struct tomoyo_path_info *filename; |
| 406 | bool is_deleted; | ||
| 407 | }; | 587 | }; |
| 408 | 588 | ||
| 409 | /* | 589 | /* |
| 410 | * tomoyo_pattern_entry is a structure which is used for holding | 590 | * tomoyo_no_pattern is a structure which is used for holding |
| 411 | * "tomoyo_pattern_list" entries. | 591 | * "file_pattern" entries. |
| 412 | * It has following fields. | 592 | * It has following fields. |
| 413 | * | 593 | * |
| 414 | * (1) "list" which is linked to tomoyo_pattern_list . | 594 | * (1) "head" is "struct tomoyo_acl_head". |
| 415 | * (2) "pattern" is a pathname pattern which is used for converting pathnames | 595 | * (2) "pattern" is a pathname pattern which is used for converting pathnames |
| 416 | * to pathname patterns during learning mode. | 596 | * to pathname patterns during learning mode. |
| 417 | * (3) "is_deleted" is a bool which is true if marked as deleted, false | ||
| 418 | * otherwise. | ||
| 419 | */ | 597 | */ |
| 420 | struct tomoyo_pattern_entry { | 598 | struct tomoyo_no_pattern { |
| 421 | struct list_head list; | 599 | struct tomoyo_acl_head head; |
| 422 | const struct tomoyo_path_info *pattern; | 600 | const struct tomoyo_path_info *pattern; |
| 423 | bool is_deleted; | ||
| 424 | }; | 601 | }; |
| 425 | 602 | ||
| 426 | /* | 603 | /* |
| 427 | * tomoyo_no_rewrite_entry is a structure which is used for holding | 604 | * tomoyo_no_rewrite is a structure which is used for holding |
| 428 | * "deny_rewrite" entries. | 605 | * "deny_rewrite" entries. |
| 429 | * It has following fields. | 606 | * It has following fields. |
| 430 | * | 607 | * |
| 431 | * (1) "list" which is linked to tomoyo_no_rewrite_list . | 608 | * (1) "head" is "struct tomoyo_acl_head". |
| 432 | * (2) "pattern" is a pathname which is by default not permitted to modify | 609 | * (2) "pattern" is a pathname which is by default not permitted to modify |
| 433 | * already existing content. | 610 | * already existing content. |
| 434 | * (3) "is_deleted" is a bool which is true if marked as deleted, false | ||
| 435 | * otherwise. | ||
| 436 | */ | 611 | */ |
| 437 | struct tomoyo_no_rewrite_entry { | 612 | struct tomoyo_no_rewrite { |
| 438 | struct list_head list; | 613 | struct tomoyo_acl_head head; |
| 439 | const struct tomoyo_path_info *pattern; | 614 | const struct tomoyo_path_info *pattern; |
| 440 | bool is_deleted; | ||
| 441 | }; | 615 | }; |
| 442 | 616 | ||
| 443 | /* | 617 | /* |
| 444 | * tomoyo_domain_initializer_entry is a structure which is used for holding | 618 | * tomoyo_transition_control is a structure which is used for holding |
| 445 | * "initialize_domain" and "no_initialize_domain" entries. | 619 | * "initialize_domain"/"no_initialize_domain"/"keep_domain"/"no_keep_domain" |
| 620 | * entries. | ||
| 446 | * It has following fields. | 621 | * It has following fields. |
| 447 | * | 622 | * |
| 448 | * (1) "list" which is linked to tomoyo_domain_initializer_list . | 623 | * (1) "head" is "struct tomoyo_acl_head". |
| 449 | * (2) "domainname" which is "a domainname" or "the last component of a | 624 | * (2) "type" is type of this entry. |
| 450 | * domainname". This field is NULL if "from" clause is not specified. | 625 | * (3) "is_last_name" is a bool which is true if "domainname" is "the last |
| 451 | * (3) "program" which is a program's pathname. | ||
| 452 | * (4) "is_deleted" is a bool which is true if marked as deleted, false | ||
| 453 | * otherwise. | ||
| 454 | * (5) "is_not" is a bool which is true if "no_initialize_domain", false | ||
| 455 | * otherwise. | ||
| 456 | * (6) "is_last_name" is a bool which is true if "domainname" is "the last | ||
| 457 | * component of a domainname", false otherwise. | 626 | * component of a domainname", false otherwise. |
| 458 | */ | 627 | * (4) "domainname" which is "a domainname" or "the last component of a |
| 459 | struct tomoyo_domain_initializer_entry { | ||
| 460 | struct list_head list; | ||
| 461 | const struct tomoyo_path_info *domainname; /* This may be NULL */ | ||
| 462 | const struct tomoyo_path_info *program; | ||
| 463 | bool is_deleted; | ||
| 464 | bool is_not; /* True if this entry is "no_initialize_domain". */ | ||
| 465 | /* True if the domainname is tomoyo_get_last_name(). */ | ||
| 466 | bool is_last_name; | ||
| 467 | }; | ||
| 468 | |||
| 469 | /* | ||
| 470 | * tomoyo_domain_keeper_entry is a structure which is used for holding | ||
| 471 | * "keep_domain" and "no_keep_domain" entries. | ||
| 472 | * It has following fields. | ||
| 473 | * | ||
| 474 | * (1) "list" which is linked to tomoyo_domain_keeper_list . | ||
| 475 | * (2) "domainname" which is "a domainname" or "the last component of a | ||
| 476 | * domainname". | 628 | * domainname". |
| 477 | * (3) "program" which is a program's pathname. | 629 | * (5) "program" which is a program's pathname. |
| 478 | * This field is NULL if "from" clause is not specified. | ||
| 479 | * (4) "is_deleted" is a bool which is true if marked as deleted, false | ||
| 480 | * otherwise. | ||
| 481 | * (5) "is_not" is a bool which is true if "no_initialize_domain", false | ||
| 482 | * otherwise. | ||
| 483 | * (6) "is_last_name" is a bool which is true if "domainname" is "the last | ||
| 484 | * component of a domainname", false otherwise. | ||
| 485 | */ | 630 | */ |
| 486 | struct tomoyo_domain_keeper_entry { | 631 | struct tomoyo_transition_control { |
| 487 | struct list_head list; | 632 | struct tomoyo_acl_head head; |
| 488 | const struct tomoyo_path_info *domainname; | 633 | u8 type; /* One of values in "enum tomoyo_transition_type". */ |
| 489 | const struct tomoyo_path_info *program; /* This may be NULL */ | ||
| 490 | bool is_deleted; | ||
| 491 | bool is_not; /* True if this entry is "no_keep_domain". */ | ||
| 492 | /* True if the domainname is tomoyo_get_last_name(). */ | 634 | /* True if the domainname is tomoyo_get_last_name(). */ |
| 493 | bool is_last_name; | 635 | bool is_last_name; |
| 636 | const struct tomoyo_path_info *domainname; /* Maybe NULL */ | ||
| 637 | const struct tomoyo_path_info *program; /* Maybe NULL */ | ||
| 494 | }; | 638 | }; |
| 495 | 639 | ||
| 496 | /* | 640 | /* |
| 497 | * tomoyo_alias_entry is a structure which is used for holding "alias" entries. | 641 | * tomoyo_aggregator is a structure which is used for holding |
| 642 | * "aggregator" entries. | ||
| 498 | * It has following fields. | 643 | * It has following fields. |
| 499 | * | 644 | * |
| 500 | * (1) "list" which is linked to tomoyo_alias_list . | 645 | * (1) "head" is "struct tomoyo_acl_head". |
| 501 | * (2) "original_name" which is a dereferenced pathname. | 646 | * (2) "original_name" which is originally requested name. |
| 502 | * (3) "aliased_name" which is a symlink's pathname. | 647 | * (3) "aggregated_name" which is name to rewrite. |
| 503 | * (4) "is_deleted" is a bool which is true if marked as deleted, false | ||
| 504 | * otherwise. | ||
| 505 | */ | 648 | */ |
| 506 | struct tomoyo_alias_entry { | 649 | struct tomoyo_aggregator { |
| 507 | struct list_head list; | 650 | struct tomoyo_acl_head head; |
| 508 | const struct tomoyo_path_info *original_name; | 651 | const struct tomoyo_path_info *original_name; |
| 509 | const struct tomoyo_path_info *aliased_name; | 652 | const struct tomoyo_path_info *aggregated_name; |
| 510 | bool is_deleted; | ||
| 511 | }; | 653 | }; |
| 512 | 654 | ||
| 513 | /* | 655 | /* |
| 514 | * tomoyo_policy_manager_entry is a structure which is used for holding list of | 656 | * tomoyo_manager is a structure which is used for holding list of |
| 515 | * domainnames or programs which are permitted to modify configuration via | 657 | * domainnames or programs which are permitted to modify configuration via |
| 516 | * /sys/kernel/security/tomoyo/ interface. | 658 | * /sys/kernel/security/tomoyo/ interface. |
| 517 | * It has following fields. | 659 | * It has following fields. |
| 518 | * | 660 | * |
| 519 | * (1) "list" which is linked to tomoyo_policy_manager_list . | 661 | * (1) "head" is "struct tomoyo_acl_head". |
| 520 | * (2) "manager" is a domainname or a program's pathname. | 662 | * (2) "is_domain" is a bool which is true if "manager" is a domainname, false |
| 521 | * (3) "is_domain" is a bool which is true if "manager" is a domainname, false | ||
| 522 | * otherwise. | ||
| 523 | * (4) "is_deleted" is a bool which is true if marked as deleted, false | ||
| 524 | * otherwise. | 663 | * otherwise. |
| 664 | * (3) "manager" is a domainname or a program's pathname. | ||
| 525 | */ | 665 | */ |
| 526 | struct tomoyo_policy_manager_entry { | 666 | struct tomoyo_manager { |
| 527 | struct list_head list; | 667 | struct tomoyo_acl_head head; |
| 668 | bool is_domain; /* True if manager is a domainname. */ | ||
| 528 | /* A path to program or a domainname. */ | 669 | /* A path to program or a domainname. */ |
| 529 | const struct tomoyo_path_info *manager; | 670 | const struct tomoyo_path_info *manager; |
| 530 | bool is_domain; /* True if manager is a domainname. */ | 671 | }; |
| 531 | bool is_deleted; /* True if this entry is deleted. */ | 672 | |
| 673 | struct tomoyo_preference { | ||
| 674 | unsigned int learning_max_entry; | ||
| 675 | bool enforcing_verbose; | ||
| 676 | bool learning_verbose; | ||
| 677 | bool permissive_verbose; | ||
| 678 | }; | ||
| 679 | |||
| 680 | struct tomoyo_profile { | ||
| 681 | const struct tomoyo_path_info *comment; | ||
| 682 | struct tomoyo_preference *learning; | ||
| 683 | struct tomoyo_preference *permissive; | ||
| 684 | struct tomoyo_preference *enforcing; | ||
| 685 | struct tomoyo_preference preference; | ||
| 686 | u8 default_config; | ||
| 687 | u8 config[TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX]; | ||
| 532 | }; | 688 | }; |
| 533 | 689 | ||
| 534 | /********** Function prototypes. **********/ | 690 | /********** Function prototypes. **********/ |
| 535 | 691 | ||
| 536 | /* Check whether the given name matches the given name_union. */ | 692 | extern asmlinkage long sys_getpid(void); |
| 537 | bool tomoyo_compare_name_union(const struct tomoyo_path_info *name, | 693 | extern asmlinkage long sys_getppid(void); |
| 538 | const struct tomoyo_name_union *ptr); | 694 | |
| 695 | /* Check whether the given string starts with the given keyword. */ | ||
| 696 | bool tomoyo_str_starts(char **src, const char *find); | ||
| 697 | /* Get tomoyo_realpath() of current process. */ | ||
| 698 | const char *tomoyo_get_exe(void); | ||
| 699 | /* Format string. */ | ||
| 700 | void tomoyo_normalize_line(unsigned char *buffer); | ||
| 701 | /* Print warning or error message on console. */ | ||
| 702 | void tomoyo_warn_log(struct tomoyo_request_info *r, const char *fmt, ...) | ||
| 703 | __attribute__ ((format(printf, 2, 3))); | ||
| 704 | /* Check all profiles currently assigned to domains are defined. */ | ||
| 705 | void tomoyo_check_profile(void); | ||
| 706 | /* Open operation for /sys/kernel/security/tomoyo/ interface. */ | ||
| 707 | int tomoyo_open_control(const u8 type, struct file *file); | ||
| 708 | /* Close /sys/kernel/security/tomoyo/ interface. */ | ||
| 709 | int tomoyo_close_control(struct file *file); | ||
| 710 | /* Poll operation for /sys/kernel/security/tomoyo/ interface. */ | ||
| 711 | int tomoyo_poll_control(struct file *file, poll_table *wait); | ||
| 712 | /* Read operation for /sys/kernel/security/tomoyo/ interface. */ | ||
| 713 | int tomoyo_read_control(struct file *file, char __user *buffer, | ||
| 714 | const int buffer_len); | ||
| 715 | /* Write operation for /sys/kernel/security/tomoyo/ interface. */ | ||
| 716 | int tomoyo_write_control(struct file *file, const char __user *buffer, | ||
| 717 | const int buffer_len); | ||
| 539 | /* Check whether the domain has too many ACL entries to hold. */ | 718 | /* Check whether the domain has too many ACL entries to hold. */ |
| 540 | bool tomoyo_domain_quota_is_ok(struct tomoyo_domain_info * const domain); | 719 | bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r); |
| 541 | /* Transactional sprintf() for policy dump. */ | 720 | /* Print out of memory warning message. */ |
| 542 | bool tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...) | 721 | void tomoyo_warn_oom(const char *function); |
| 722 | /* Check whether the given name matches the given name_union. */ | ||
| 723 | const struct tomoyo_path_info * | ||
| 724 | tomoyo_compare_name_union(const struct tomoyo_path_info *name, | ||
| 725 | const struct tomoyo_name_union *ptr); | ||
| 726 | /* Check whether the given number matches the given number_union. */ | ||
| 727 | bool tomoyo_compare_number_union(const unsigned long value, | ||
| 728 | const struct tomoyo_number_union *ptr); | ||
| 729 | int tomoyo_get_mode(const u8 profile, const u8 index); | ||
| 730 | void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...) | ||
| 543 | __attribute__ ((format(printf, 2, 3))); | 731 | __attribute__ ((format(printf, 2, 3))); |
| 544 | /* Check whether the domainname is correct. */ | 732 | /* Check whether the domainname is correct. */ |
| 545 | bool tomoyo_is_correct_domain(const unsigned char *domainname); | 733 | bool tomoyo_correct_domain(const unsigned char *domainname); |
| 546 | /* Check whether the token is correct. */ | 734 | /* Check whether the token is correct. */ |
| 547 | bool tomoyo_is_correct_path(const char *filename, const s8 start_type, | 735 | bool tomoyo_correct_path(const char *filename); |
| 548 | const s8 pattern_type, const s8 end_type); | 736 | bool tomoyo_correct_word(const char *string); |
| 549 | /* Check whether the token can be a domainname. */ | 737 | /* Check whether the token can be a domainname. */ |
| 550 | bool tomoyo_is_domain_def(const unsigned char *buffer); | 738 | bool tomoyo_domain_def(const unsigned char *buffer); |
| 551 | bool tomoyo_parse_name_union(const char *filename, | 739 | bool tomoyo_parse_name_union(const char *filename, |
| 552 | struct tomoyo_name_union *ptr); | 740 | struct tomoyo_name_union *ptr); |
| 553 | /* Check whether the given filename matches the given path_group. */ | 741 | /* Check whether the given filename matches the given path_group. */ |
| 554 | bool tomoyo_path_matches_group(const struct tomoyo_path_info *pathname, | 742 | const struct tomoyo_path_info * |
| 555 | const struct tomoyo_path_group *group, | 743 | tomoyo_path_matches_group(const struct tomoyo_path_info *pathname, |
| 556 | const bool may_use_pattern); | 744 | const struct tomoyo_group *group); |
| 745 | /* Check whether the given value matches the given number_group. */ | ||
| 746 | bool tomoyo_number_matches_group(const unsigned long min, | ||
| 747 | const unsigned long max, | ||
| 748 | const struct tomoyo_group *group); | ||
| 557 | /* Check whether the given filename matches the given pattern. */ | 749 | /* Check whether the given filename matches the given pattern. */ |
| 558 | bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename, | 750 | bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename, |
| 559 | const struct tomoyo_path_info *pattern); | 751 | const struct tomoyo_path_info *pattern); |
| 560 | /* Read "alias" entry in exception policy. */ | 752 | |
| 561 | bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head); | 753 | bool tomoyo_parse_number_union(char *data, struct tomoyo_number_union *num); |
| 562 | /* | ||
| 563 | * Read "initialize_domain" and "no_initialize_domain" entry | ||
| 564 | * in exception policy. | ||
| 565 | */ | ||
| 566 | bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head); | ||
| 567 | /* Read "keep_domain" and "no_keep_domain" entry in exception policy. */ | ||
| 568 | bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head); | ||
| 569 | /* Read "file_pattern" entry in exception policy. */ | ||
| 570 | bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head); | ||
| 571 | /* Read "path_group" entry in exception policy. */ | ||
| 572 | bool tomoyo_read_path_group_policy(struct tomoyo_io_buffer *head); | ||
| 573 | /* Read "allow_read" entry in exception policy. */ | ||
| 574 | bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head); | ||
| 575 | /* Read "deny_rewrite" entry in exception policy. */ | ||
| 576 | bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head); | ||
| 577 | /* Tokenize a line. */ | 754 | /* Tokenize a line. */ |
| 578 | bool tomoyo_tokenize(char *buffer, char *w[], size_t size); | 755 | bool tomoyo_tokenize(char *buffer, char *w[], size_t size); |
| 579 | /* Write domain policy violation warning message to console? */ | 756 | /* Write domain policy violation warning message to console? */ |
| 580 | bool tomoyo_verbose_mode(const struct tomoyo_domain_info *domain); | 757 | bool tomoyo_verbose_mode(const struct tomoyo_domain_info *domain); |
| 581 | /* Convert double path operation to operation name. */ | 758 | /* Fill "struct tomoyo_request_info". */ |
| 582 | const char *tomoyo_path22keyword(const u8 operation); | 759 | int tomoyo_init_request_info(struct tomoyo_request_info *r, |
| 583 | /* Get the last component of the given domainname. */ | 760 | struct tomoyo_domain_info *domain, |
| 584 | const char *tomoyo_get_last_name(const struct tomoyo_domain_info *domain); | 761 | const u8 index); |
| 585 | /* Get warning message. */ | 762 | /* Check permission for mount operation. */ |
| 586 | const char *tomoyo_get_msg(const bool is_enforce); | 763 | int tomoyo_mount_permission(char *dev_name, struct path *path, char *type, |
| 587 | /* Convert single path operation to operation name. */ | 764 | unsigned long flags, void *data_page); |
| 588 | const char *tomoyo_path2keyword(const u8 operation); | 765 | /* Create "aggregator" entry in exception policy. */ |
| 589 | /* Create "alias" entry in exception policy. */ | 766 | int tomoyo_write_aggregator(char *data, const bool is_delete); |
| 590 | int tomoyo_write_alias_policy(char *data, const bool is_delete); | 767 | int tomoyo_write_transition_control(char *data, const bool is_delete, |
| 591 | /* | 768 | const u8 type); |
| 592 | * Create "initialize_domain" and "no_initialize_domain" entry | ||
| 593 | * in exception policy. | ||
| 594 | */ | ||
| 595 | int tomoyo_write_domain_initializer_policy(char *data, const bool is_not, | ||
| 596 | const bool is_delete); | ||
| 597 | /* Create "keep_domain" and "no_keep_domain" entry in exception policy. */ | ||
| 598 | int tomoyo_write_domain_keeper_policy(char *data, const bool is_not, | ||
| 599 | const bool is_delete); | ||
| 600 | /* | 769 | /* |
| 601 | * Create "allow_read/write", "allow_execute", "allow_read", "allow_write", | 770 | * Create "allow_read/write", "allow_execute", "allow_read", "allow_write", |
| 602 | * "allow_create", "allow_unlink", "allow_mkdir", "allow_rmdir", | 771 | * "allow_create", "allow_unlink", "allow_mkdir", "allow_rmdir", |
| @@ -604,25 +773,31 @@ int tomoyo_write_domain_keeper_policy(char *data, const bool is_not, | |||
| 604 | * "allow_truncate", "allow_symlink", "allow_rewrite", "allow_rename" and | 773 | * "allow_truncate", "allow_symlink", "allow_rewrite", "allow_rename" and |
| 605 | * "allow_link" entry in domain policy. | 774 | * "allow_link" entry in domain policy. |
| 606 | */ | 775 | */ |
| 607 | int tomoyo_write_file_policy(char *data, struct tomoyo_domain_info *domain, | 776 | int tomoyo_write_file(char *data, struct tomoyo_domain_info *domain, |
| 608 | const bool is_delete); | 777 | const bool is_delete); |
| 609 | /* Create "allow_read" entry in exception policy. */ | 778 | /* Create "allow_read" entry in exception policy. */ |
| 610 | int tomoyo_write_globally_readable_policy(char *data, const bool is_delete); | 779 | int tomoyo_write_globally_readable(char *data, const bool is_delete); |
| 780 | /* Create "allow_mount" entry in domain policy. */ | ||
| 781 | int tomoyo_write_mount(char *data, struct tomoyo_domain_info *domain, | ||
| 782 | const bool is_delete); | ||
| 611 | /* Create "deny_rewrite" entry in exception policy. */ | 783 | /* Create "deny_rewrite" entry in exception policy. */ |
| 612 | int tomoyo_write_no_rewrite_policy(char *data, const bool is_delete); | 784 | int tomoyo_write_no_rewrite(char *data, const bool is_delete); |
| 613 | /* Create "file_pattern" entry in exception policy. */ | 785 | /* Create "file_pattern" entry in exception policy. */ |
| 614 | int tomoyo_write_pattern_policy(char *data, const bool is_delete); | 786 | int tomoyo_write_pattern(char *data, const bool is_delete); |
| 615 | /* Create "path_group" entry in exception policy. */ | 787 | /* Create "path_group"/"number_group" entry in exception policy. */ |
| 616 | int tomoyo_write_path_group_policy(char *data, const bool is_delete); | 788 | int tomoyo_write_group(char *data, const bool is_delete, const u8 type); |
| 789 | int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...) | ||
| 790 | __attribute__ ((format(printf, 2, 3))); | ||
| 617 | /* Find a domain by the given name. */ | 791 | /* Find a domain by the given name. */ |
| 618 | struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname); | 792 | struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname); |
| 619 | /* Find or create a domain by the given name. */ | 793 | /* Find or create a domain by the given name. */ |
| 620 | struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char * | 794 | struct tomoyo_domain_info *tomoyo_assign_domain(const char *domainname, |
| 621 | domainname, | 795 | const u8 profile); |
| 622 | const u8 profile); | 796 | struct tomoyo_profile *tomoyo_profile(const u8 profile); |
| 623 | 797 | /* | |
| 624 | /* Allocate memory for "struct tomoyo_path_group". */ | 798 | * Allocate memory for "struct tomoyo_path_group"/"struct tomoyo_number_group". |
| 625 | struct tomoyo_path_group *tomoyo_get_path_group(const char *group_name); | 799 | */ |
| 800 | struct tomoyo_group *tomoyo_get_group(const char *group_name, const u8 type); | ||
| 626 | 801 | ||
| 627 | /* Check mode for specified functionality. */ | 802 | /* Check mode for specified functionality. */ |
| 628 | unsigned int tomoyo_check_flags(const struct tomoyo_domain_info *domain, | 803 | unsigned int tomoyo_check_flags(const struct tomoyo_domain_info *domain, |
| @@ -632,25 +807,23 @@ void tomoyo_fill_path_info(struct tomoyo_path_info *ptr); | |||
| 632 | /* Run policy loader when /sbin/init starts. */ | 807 | /* Run policy loader when /sbin/init starts. */ |
| 633 | void tomoyo_load_policy(const char *filename); | 808 | void tomoyo_load_policy(const char *filename); |
| 634 | 809 | ||
| 635 | /* Convert binary string to ascii string. */ | 810 | void tomoyo_put_number_union(struct tomoyo_number_union *ptr); |
| 636 | int tomoyo_encode(char *buffer, int buflen, const char *str); | ||
| 637 | 811 | ||
| 638 | /* Returns realpath(3) of the given pathname but ignores chroot'ed root. */ | 812 | /* Convert binary string to ascii string. */ |
| 639 | int tomoyo_realpath_from_path2(struct path *path, char *newname, | 813 | char *tomoyo_encode(const char *str); |
| 640 | int newname_len); | ||
| 641 | 814 | ||
| 642 | /* | 815 | /* |
| 643 | * Returns realpath(3) of the given pathname but ignores chroot'ed root. | 816 | * Returns realpath(3) of the given pathname except that |
| 644 | * These functions use kzalloc(), so the caller must call kfree() | 817 | * ignores chroot'ed root and does not follow the final symlink. |
| 645 | * if these functions didn't return NULL. | ||
| 646 | */ | 818 | */ |
| 647 | char *tomoyo_realpath(const char *pathname); | 819 | char *tomoyo_realpath_nofollow(const char *pathname); |
| 648 | /* | 820 | /* |
| 649 | * Same with tomoyo_realpath() except that it doesn't follow the final symlink. | 821 | * Returns realpath(3) of the given pathname except that |
| 822 | * ignores chroot'ed root and the pathname is already solved. | ||
| 650 | */ | 823 | */ |
| 651 | char *tomoyo_realpath_nofollow(const char *pathname); | ||
| 652 | /* Same with tomoyo_realpath() except that the pathname is already solved. */ | ||
| 653 | char *tomoyo_realpath_from_path(struct path *path); | 824 | char *tomoyo_realpath_from_path(struct path *path); |
| 825 | /* Get patterned pathname. */ | ||
| 826 | const char *tomoyo_pattern(const struct tomoyo_path_info *filename); | ||
| 654 | 827 | ||
| 655 | /* Check memory quota. */ | 828 | /* Check memory quota. */ |
| 656 | bool tomoyo_memory_ok(void *ptr); | 829 | bool tomoyo_memory_ok(void *ptr); |
| @@ -663,23 +836,29 @@ void *tomoyo_commit_ok(void *data, const unsigned int size); | |||
| 663 | const struct tomoyo_path_info *tomoyo_get_name(const char *name); | 836 | const struct tomoyo_path_info *tomoyo_get_name(const char *name); |
| 664 | 837 | ||
| 665 | /* Check for memory usage. */ | 838 | /* Check for memory usage. */ |
| 666 | int tomoyo_read_memory_counter(struct tomoyo_io_buffer *head); | 839 | void tomoyo_read_memory_counter(struct tomoyo_io_buffer *head); |
| 667 | 840 | ||
| 668 | /* Set memory quota. */ | 841 | /* Set memory quota. */ |
| 669 | int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head); | 842 | int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head); |
| 670 | 843 | ||
| 671 | /* Initialize realpath related code. */ | 844 | /* Initialize mm related code. */ |
| 672 | void __init tomoyo_realpath_init(void); | 845 | void __init tomoyo_mm_init(void); |
| 673 | int tomoyo_check_exec_perm(struct tomoyo_domain_info *domain, | 846 | int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation, |
| 674 | const struct tomoyo_path_info *filename); | 847 | const struct tomoyo_path_info *filename); |
| 675 | int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, | 848 | int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, |
| 676 | struct path *path, const int flag); | 849 | struct path *path, const int flag); |
| 850 | int tomoyo_path_number_perm(const u8 operation, struct path *path, | ||
| 851 | unsigned long number); | ||
| 852 | int tomoyo_mkdev_perm(const u8 operation, struct path *path, | ||
| 853 | const unsigned int mode, unsigned int dev); | ||
| 677 | int tomoyo_path_perm(const u8 operation, struct path *path); | 854 | int tomoyo_path_perm(const u8 operation, struct path *path); |
| 678 | int tomoyo_path2_perm(const u8 operation, struct path *path1, | 855 | int tomoyo_path2_perm(const u8 operation, struct path *path1, |
| 679 | struct path *path2); | 856 | struct path *path2); |
| 680 | int tomoyo_check_rewrite_permission(struct file *filp); | ||
| 681 | int tomoyo_find_next_domain(struct linux_binprm *bprm); | 857 | int tomoyo_find_next_domain(struct linux_binprm *bprm); |
| 682 | 858 | ||
| 859 | void tomoyo_print_ulong(char *buffer, const int buffer_len, | ||
| 860 | const unsigned long value, const u8 type); | ||
| 861 | |||
| 683 | /* Drop refcount on tomoyo_name_union. */ | 862 | /* Drop refcount on tomoyo_name_union. */ |
| 684 | void tomoyo_put_name_union(struct tomoyo_name_union *ptr); | 863 | void tomoyo_put_name_union(struct tomoyo_name_union *ptr); |
| 685 | 864 | ||
| @@ -688,6 +867,25 @@ void tomoyo_run_gc(void); | |||
| 688 | 867 | ||
| 689 | void tomoyo_memory_free(void *ptr); | 868 | void tomoyo_memory_free(void *ptr); |
| 690 | 869 | ||
| 870 | int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size, | ||
| 871 | bool is_delete, struct tomoyo_domain_info *domain, | ||
| 872 | bool (*check_duplicate) (const struct tomoyo_acl_info | ||
| 873 | *, | ||
| 874 | const struct tomoyo_acl_info | ||
| 875 | *), | ||
| 876 | bool (*merge_duplicate) (struct tomoyo_acl_info *, | ||
| 877 | struct tomoyo_acl_info *, | ||
| 878 | const bool)); | ||
| 879 | int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size, | ||
| 880 | bool is_delete, struct list_head *list, | ||
| 881 | bool (*check_duplicate) (const struct tomoyo_acl_head | ||
| 882 | *, | ||
| 883 | const struct tomoyo_acl_head | ||
| 884 | *)); | ||
| 885 | void tomoyo_check_acl(struct tomoyo_request_info *r, | ||
| 886 | bool (*check_entry) (struct tomoyo_request_info *, | ||
| 887 | const struct tomoyo_acl_info *)); | ||
| 888 | |||
| 691 | /********** External variable definitions. **********/ | 889 | /********** External variable definitions. **********/ |
| 692 | 890 | ||
| 693 | /* Lock for GC. */ | 891 | /* Lock for GC. */ |
| @@ -696,14 +894,8 @@ extern struct srcu_struct tomoyo_ss; | |||
| 696 | /* The list for "struct tomoyo_domain_info". */ | 894 | /* The list for "struct tomoyo_domain_info". */ |
| 697 | extern struct list_head tomoyo_domain_list; | 895 | extern struct list_head tomoyo_domain_list; |
| 698 | 896 | ||
| 699 | extern struct list_head tomoyo_path_group_list; | 897 | extern struct list_head tomoyo_policy_list[TOMOYO_MAX_POLICY]; |
| 700 | extern struct list_head tomoyo_domain_initializer_list; | 898 | extern struct list_head tomoyo_group_list[TOMOYO_MAX_GROUP]; |
| 701 | extern struct list_head tomoyo_domain_keeper_list; | ||
| 702 | extern struct list_head tomoyo_alias_list; | ||
| 703 | extern struct list_head tomoyo_globally_readable_list; | ||
| 704 | extern struct list_head tomoyo_pattern_list; | ||
| 705 | extern struct list_head tomoyo_no_rewrite_list; | ||
| 706 | extern struct list_head tomoyo_policy_manager_list; | ||
| 707 | extern struct list_head tomoyo_name_list[TOMOYO_MAX_HASH]; | 899 | extern struct list_head tomoyo_name_list[TOMOYO_MAX_HASH]; |
| 708 | 900 | ||
| 709 | /* Lock for protecting policy. */ | 901 | /* Lock for protecting policy. */ |
| @@ -715,6 +907,14 @@ extern bool tomoyo_policy_loaded; | |||
| 715 | /* The kernel's domain. */ | 907 | /* The kernel's domain. */ |
| 716 | extern struct tomoyo_domain_info tomoyo_kernel_domain; | 908 | extern struct tomoyo_domain_info tomoyo_kernel_domain; |
| 717 | 909 | ||
| 910 | extern const char *tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION]; | ||
| 911 | extern const char *tomoyo_mkdev_keyword[TOMOYO_MAX_MKDEV_OPERATION]; | ||
| 912 | extern const char *tomoyo_path2_keyword[TOMOYO_MAX_PATH2_OPERATION]; | ||
| 913 | extern const char *tomoyo_path_number_keyword[TOMOYO_MAX_PATH_NUMBER_OPERATION]; | ||
| 914 | |||
| 915 | extern unsigned int tomoyo_quota_for_query; | ||
| 916 | extern unsigned int tomoyo_query_memory_size; | ||
| 917 | |||
| 718 | /********** Inlined functions. **********/ | 918 | /********** Inlined functions. **********/ |
| 719 | 919 | ||
| 720 | static inline int tomoyo_read_lock(void) | 920 | static inline int tomoyo_read_lock(void) |
| @@ -735,25 +935,25 @@ static inline bool tomoyo_pathcmp(const struct tomoyo_path_info *a, | |||
| 735 | } | 935 | } |
| 736 | 936 | ||
| 737 | /** | 937 | /** |
| 738 | * tomoyo_is_valid - Check whether the character is a valid char. | 938 | * tomoyo_valid - Check whether the character is a valid char. |
| 739 | * | 939 | * |
| 740 | * @c: The character to check. | 940 | * @c: The character to check. |
| 741 | * | 941 | * |
| 742 | * Returns true if @c is a valid character, false otherwise. | 942 | * Returns true if @c is a valid character, false otherwise. |
| 743 | */ | 943 | */ |
| 744 | static inline bool tomoyo_is_valid(const unsigned char c) | 944 | static inline bool tomoyo_valid(const unsigned char c) |
| 745 | { | 945 | { |
| 746 | return c > ' ' && c < 127; | 946 | return c > ' ' && c < 127; |
| 747 | } | 947 | } |
| 748 | 948 | ||
| 749 | /** | 949 | /** |
| 750 | * tomoyo_is_invalid - Check whether the character is an invalid char. | 950 | * tomoyo_invalid - Check whether the character is an invalid char. |
| 751 | * | 951 | * |
| 752 | * @c: The character to check. | 952 | * @c: The character to check. |
| 753 | * | 953 | * |
| 754 | * Returns true if @c is an invalid character, false otherwise. | 954 | * Returns true if @c is an invalid character, false otherwise. |
| 755 | */ | 955 | */ |
| 756 | static inline bool tomoyo_is_invalid(const unsigned char c) | 956 | static inline bool tomoyo_invalid(const unsigned char c) |
| 757 | { | 957 | { |
| 758 | return c && (c <= ' ' || c >= 127); | 958 | return c && (c <= ' ' || c >= 127); |
| 759 | } | 959 | } |
| @@ -761,13 +961,13 @@ static inline bool tomoyo_is_invalid(const unsigned char c) | |||
| 761 | static inline void tomoyo_put_name(const struct tomoyo_path_info *name) | 961 | static inline void tomoyo_put_name(const struct tomoyo_path_info *name) |
| 762 | { | 962 | { |
| 763 | if (name) { | 963 | if (name) { |
| 764 | struct tomoyo_name_entry *ptr = | 964 | struct tomoyo_name *ptr = |
| 765 | container_of(name, struct tomoyo_name_entry, entry); | 965 | container_of(name, typeof(*ptr), entry); |
| 766 | atomic_dec(&ptr->users); | 966 | atomic_dec(&ptr->users); |
| 767 | } | 967 | } |
| 768 | } | 968 | } |
| 769 | 969 | ||
| 770 | static inline void tomoyo_put_path_group(struct tomoyo_path_group *group) | 970 | static inline void tomoyo_put_group(struct tomoyo_group *group) |
| 771 | { | 971 | { |
| 772 | if (group) | 972 | if (group) |
| 773 | atomic_dec(&group->users); | 973 | atomic_dec(&group->users); |
| @@ -784,75 +984,35 @@ static inline struct tomoyo_domain_info *tomoyo_real_domain(struct task_struct | |||
| 784 | return task_cred_xxx(task, security); | 984 | return task_cred_xxx(task, security); |
| 785 | } | 985 | } |
| 786 | 986 | ||
| 787 | static inline bool tomoyo_is_same_acl_head(const struct tomoyo_acl_info *p1, | 987 | static inline bool tomoyo_same_acl_head(const struct tomoyo_acl_info *p1, |
| 788 | const struct tomoyo_acl_info *p2) | 988 | const struct tomoyo_acl_info *p2) |
| 789 | { | 989 | { |
| 790 | return p1->type == p2->type; | 990 | return p1->type == p2->type; |
| 791 | } | 991 | } |
| 792 | 992 | ||
| 793 | static inline bool tomoyo_is_same_name_union | 993 | static inline bool tomoyo_same_name_union |
| 794 | (const struct tomoyo_name_union *p1, const struct tomoyo_name_union *p2) | 994 | (const struct tomoyo_name_union *p1, const struct tomoyo_name_union *p2) |
| 795 | { | 995 | { |
| 796 | return p1->filename == p2->filename && p1->group == p2->group && | 996 | return p1->filename == p2->filename && p1->group == p2->group && |
| 797 | p1->is_group == p2->is_group; | 997 | p1->is_group == p2->is_group; |
| 798 | } | 998 | } |
| 799 | 999 | ||
| 800 | static inline bool tomoyo_is_same_path_acl(const struct tomoyo_path_acl *p1, | 1000 | static inline bool tomoyo_same_number_union |
| 801 | const struct tomoyo_path_acl *p2) | 1001 | (const struct tomoyo_number_union *p1, const struct tomoyo_number_union *p2) |
| 802 | { | ||
| 803 | return tomoyo_is_same_acl_head(&p1->head, &p2->head) && | ||
| 804 | tomoyo_is_same_name_union(&p1->name, &p2->name); | ||
| 805 | } | ||
| 806 | |||
| 807 | static inline bool tomoyo_is_same_path2_acl(const struct tomoyo_path2_acl *p1, | ||
| 808 | const struct tomoyo_path2_acl *p2) | ||
| 809 | { | 1002 | { |
| 810 | return tomoyo_is_same_acl_head(&p1->head, &p2->head) && | 1003 | return p1->values[0] == p2->values[0] && p1->values[1] == p2->values[1] |
| 811 | tomoyo_is_same_name_union(&p1->name1, &p2->name1) && | 1004 | && p1->group == p2->group && p1->min_type == p2->min_type && |
| 812 | tomoyo_is_same_name_union(&p1->name2, &p2->name2); | 1005 | p1->max_type == p2->max_type && p1->is_group == p2->is_group; |
| 813 | } | ||
| 814 | |||
| 815 | static inline bool tomoyo_is_same_domain_initializer_entry | ||
| 816 | (const struct tomoyo_domain_initializer_entry *p1, | ||
| 817 | const struct tomoyo_domain_initializer_entry *p2) | ||
| 818 | { | ||
| 819 | return p1->is_not == p2->is_not && p1->is_last_name == p2->is_last_name | ||
| 820 | && p1->domainname == p2->domainname | ||
| 821 | && p1->program == p2->program; | ||
| 822 | } | ||
| 823 | |||
| 824 | static inline bool tomoyo_is_same_domain_keeper_entry | ||
| 825 | (const struct tomoyo_domain_keeper_entry *p1, | ||
| 826 | const struct tomoyo_domain_keeper_entry *p2) | ||
| 827 | { | ||
| 828 | return p1->is_not == p2->is_not && p1->is_last_name == p2->is_last_name | ||
| 829 | && p1->domainname == p2->domainname | ||
| 830 | && p1->program == p2->program; | ||
| 831 | } | ||
| 832 | |||
| 833 | static inline bool tomoyo_is_same_alias_entry | ||
| 834 | (const struct tomoyo_alias_entry *p1, const struct tomoyo_alias_entry *p2) | ||
| 835 | { | ||
| 836 | return p1->original_name == p2->original_name && | ||
| 837 | p1->aliased_name == p2->aliased_name; | ||
| 838 | } | 1006 | } |
| 839 | 1007 | ||
| 840 | /** | 1008 | /** |
| 841 | * list_for_each_cookie - iterate over a list with cookie. | 1009 | * list_for_each_cookie - iterate over a list with cookie. |
| 842 | * @pos: the &struct list_head to use as a loop cursor. | 1010 | * @pos: the &struct list_head to use as a loop cursor. |
| 843 | * @cookie: the &struct list_head to use as a cookie. | ||
| 844 | * @head: the head for your list. | 1011 | * @head: the head for your list. |
| 845 | * | ||
| 846 | * Same with list_for_each_rcu() except that this primitive uses @cookie | ||
| 847 | * so that we can continue iteration. | ||
| 848 | * @cookie must be NULL when iteration starts, and @cookie will become | ||
| 849 | * NULL when iteration finishes. | ||
| 850 | */ | 1012 | */ |
| 851 | #define list_for_each_cookie(pos, cookie, head) \ | 1013 | #define list_for_each_cookie(pos, head) \ |
| 852 | for (({ if (!cookie) \ | 1014 | if (!pos) \ |
| 853 | cookie = head; }), \ | 1015 | pos = srcu_dereference((head)->next, &tomoyo_ss); \ |
| 854 | pos = rcu_dereference((cookie)->next); \ | 1016 | for ( ; pos != (head); pos = srcu_dereference(pos->next, &tomoyo_ss)) |
| 855 | prefetch(pos->next), pos != (head) || ((cookie) = NULL); \ | ||
| 856 | (cookie) = pos, pos = rcu_dereference(pos->next)) | ||
| 857 | 1017 | ||
| 858 | #endif /* !defined(_SECURITY_TOMOYO_COMMON_H) */ | 1018 | #endif /* !defined(_SECURITY_TOMOYO_COMMON_H) */ |
diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c index cd8ba4446763..35388408e475 100644 --- a/security/tomoyo/domain.c +++ b/security/tomoyo/domain.c | |||
| @@ -1,12 +1,9 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * security/tomoyo/domain.c | 2 | * security/tomoyo/domain.c |
| 3 | * | 3 | * |
| 4 | * Implementation of the Domain-Based Mandatory Access Control. | 4 | * Domain transition functions for TOMOYO. |
| 5 | * | ||
| 6 | * Copyright (C) 2005-2009 NTT DATA CORPORATION | ||
| 7 | * | ||
| 8 | * Version: 2.2.0 2009/04/01 | ||
| 9 | * | 5 | * |
| 6 | * Copyright (C) 2005-2010 NTT DATA CORPORATION | ||
| 10 | */ | 7 | */ |
| 11 | 8 | ||
| 12 | #include "common.h" | 9 | #include "common.h" |
| @@ -18,366 +15,191 @@ | |||
| 18 | /* The initial domain. */ | 15 | /* The initial domain. */ |
| 19 | struct tomoyo_domain_info tomoyo_kernel_domain; | 16 | struct tomoyo_domain_info tomoyo_kernel_domain; |
| 20 | 17 | ||
| 21 | /* | ||
| 22 | * tomoyo_domain_list is used for holding list of domains. | ||
| 23 | * The ->acl_info_list of "struct tomoyo_domain_info" is used for holding | ||
| 24 | * permissions (e.g. "allow_read /lib/libc-2.5.so") given to each domain. | ||
| 25 | * | ||
| 26 | * An entry is added by | ||
| 27 | * | ||
| 28 | * # ( echo "<kernel>"; echo "allow_execute /sbin/init" ) > \ | ||
| 29 | * /sys/kernel/security/tomoyo/domain_policy | ||
| 30 | * | ||
| 31 | * and is deleted by | ||
| 32 | * | ||
| 33 | * # ( echo "<kernel>"; echo "delete allow_execute /sbin/init" ) > \ | ||
| 34 | * /sys/kernel/security/tomoyo/domain_policy | ||
| 35 | * | ||
| 36 | * and all entries are retrieved by | ||
| 37 | * | ||
| 38 | * # cat /sys/kernel/security/tomoyo/domain_policy | ||
| 39 | * | ||
| 40 | * A domain is added by | ||
| 41 | * | ||
| 42 | * # echo "<kernel>" > /sys/kernel/security/tomoyo/domain_policy | ||
| 43 | * | ||
| 44 | * and is deleted by | ||
| 45 | * | ||
| 46 | * # echo "delete <kernel>" > /sys/kernel/security/tomoyo/domain_policy | ||
| 47 | * | ||
| 48 | * and all domains are retrieved by | ||
| 49 | * | ||
| 50 | * # grep '^<kernel>' /sys/kernel/security/tomoyo/domain_policy | ||
| 51 | * | ||
| 52 | * Normally, a domainname is monotonically getting longer because a domainname | ||
| 53 | * which the process will belong to if an execve() operation succeeds is | ||
| 54 | * defined as a concatenation of "current domainname" + "pathname passed to | ||
| 55 | * execve()". | ||
| 56 | * See tomoyo_domain_initializer_list and tomoyo_domain_keeper_list for | ||
| 57 | * exceptions. | ||
| 58 | */ | ||
| 59 | LIST_HEAD(tomoyo_domain_list); | ||
| 60 | |||
| 61 | /** | 18 | /** |
| 62 | * tomoyo_get_last_name - Get last component of a domainname. | 19 | * tomoyo_update_policy - Update an entry for exception policy. |
| 63 | * | ||
| 64 | * @domain: Pointer to "struct tomoyo_domain_info". | ||
| 65 | * | ||
| 66 | * Returns the last component of the domainname. | ||
| 67 | */ | ||
| 68 | const char *tomoyo_get_last_name(const struct tomoyo_domain_info *domain) | ||
| 69 | { | ||
| 70 | const char *cp0 = domain->domainname->name; | ||
| 71 | const char *cp1 = strrchr(cp0, ' '); | ||
| 72 | |||
| 73 | if (cp1) | ||
| 74 | return cp1 + 1; | ||
| 75 | return cp0; | ||
| 76 | } | ||
| 77 | |||
| 78 | /* | ||
| 79 | * tomoyo_domain_initializer_list is used for holding list of programs which | ||
| 80 | * triggers reinitialization of domainname. Normally, a domainname is | ||
| 81 | * monotonically getting longer. But sometimes, we restart daemon programs. | ||
| 82 | * It would be convenient for us that "a daemon started upon system boot" and | ||
| 83 | * "the daemon restarted from console" belong to the same domain. Thus, TOMOYO | ||
| 84 | * provides a way to shorten domainnames. | ||
| 85 | * | 20 | * |
| 86 | * An entry is added by | 21 | * @new_entry: Pointer to "struct tomoyo_acl_info". |
| 87 | * | 22 | * @size: Size of @new_entry in bytes. |
| 88 | * # echo 'initialize_domain /usr/sbin/httpd' > \ | 23 | * @is_delete: True if it is a delete request. |
| 89 | * /sys/kernel/security/tomoyo/exception_policy | 24 | * @list: Pointer to "struct list_head". |
| 90 | * | 25 | * @check_duplicate: Callback function to find duplicated entry. |
| 91 | * and is deleted by | ||
| 92 | * | ||
| 93 | * # echo 'delete initialize_domain /usr/sbin/httpd' > \ | ||
| 94 | * /sys/kernel/security/tomoyo/exception_policy | ||
| 95 | * | ||
| 96 | * and all entries are retrieved by | ||
| 97 | * | ||
| 98 | * # grep ^initialize_domain /sys/kernel/security/tomoyo/exception_policy | ||
| 99 | * | ||
| 100 | * In the example above, /usr/sbin/httpd will belong to | ||
| 101 | * "<kernel> /usr/sbin/httpd" domain. | ||
| 102 | * | ||
| 103 | * You may specify a domainname using "from" keyword. | ||
| 104 | * "initialize_domain /usr/sbin/httpd from <kernel> /etc/rc.d/init.d/httpd" | ||
| 105 | * will cause "/usr/sbin/httpd" executed from "<kernel> /etc/rc.d/init.d/httpd" | ||
| 106 | * domain to belong to "<kernel> /usr/sbin/httpd" domain. | ||
| 107 | * | ||
| 108 | * You may add "no_" prefix to "initialize_domain". | ||
| 109 | * "initialize_domain /usr/sbin/httpd" and | ||
| 110 | * "no_initialize_domain /usr/sbin/httpd from <kernel> /etc/rc.d/init.d/httpd" | ||
| 111 | * will cause "/usr/sbin/httpd" to belong to "<kernel> /usr/sbin/httpd" domain | ||
| 112 | * unless executed from "<kernel> /etc/rc.d/init.d/httpd" domain. | ||
| 113 | */ | ||
| 114 | LIST_HEAD(tomoyo_domain_initializer_list); | ||
| 115 | |||
| 116 | /** | ||
| 117 | * tomoyo_update_domain_initializer_entry - Update "struct tomoyo_domain_initializer_entry" list. | ||
| 118 | * | ||
| 119 | * @domainname: The name of domain. May be NULL. | ||
| 120 | * @program: The name of program. | ||
| 121 | * @is_not: True if it is "no_initialize_domain" entry. | ||
| 122 | * @is_delete: True if it is a delete request. | ||
| 123 | * | 26 | * |
| 124 | * Returns 0 on success, negative value otherwise. | 27 | * Returns 0 on success, negative value otherwise. |
| 125 | * | 28 | * |
| 126 | * Caller holds tomoyo_read_lock(). | 29 | * Caller holds tomoyo_read_lock(). |
| 127 | */ | 30 | */ |
| 128 | static int tomoyo_update_domain_initializer_entry(const char *domainname, | 31 | int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size, |
| 129 | const char *program, | 32 | bool is_delete, struct list_head *list, |
| 130 | const bool is_not, | 33 | bool (*check_duplicate) (const struct tomoyo_acl_head |
| 131 | const bool is_delete) | 34 | *, |
| 35 | const struct tomoyo_acl_head | ||
| 36 | *)) | ||
| 132 | { | 37 | { |
| 133 | struct tomoyo_domain_initializer_entry *ptr; | ||
| 134 | struct tomoyo_domain_initializer_entry e = { .is_not = is_not }; | ||
| 135 | int error = is_delete ? -ENOENT : -ENOMEM; | 38 | int error = is_delete ? -ENOENT : -ENOMEM; |
| 39 | struct tomoyo_acl_head *entry; | ||
| 136 | 40 | ||
| 137 | if (!tomoyo_is_correct_path(program, 1, -1, -1)) | ||
| 138 | return -EINVAL; /* No patterns allowed. */ | ||
| 139 | if (domainname) { | ||
| 140 | if (!tomoyo_is_domain_def(domainname) && | ||
| 141 | tomoyo_is_correct_path(domainname, 1, -1, -1)) | ||
| 142 | e.is_last_name = true; | ||
| 143 | else if (!tomoyo_is_correct_domain(domainname)) | ||
| 144 | return -EINVAL; | ||
| 145 | e.domainname = tomoyo_get_name(domainname); | ||
| 146 | if (!e.domainname) | ||
| 147 | goto out; | ||
| 148 | } | ||
| 149 | e.program = tomoyo_get_name(program); | ||
| 150 | if (!e.program) | ||
| 151 | goto out; | ||
| 152 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) | 41 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) |
| 153 | goto out; | 42 | return -ENOMEM; |
| 154 | list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, list) { | 43 | list_for_each_entry_rcu(entry, list, list) { |
| 155 | if (!tomoyo_is_same_domain_initializer_entry(ptr, &e)) | 44 | if (!check_duplicate(entry, new_entry)) |
| 156 | continue; | 45 | continue; |
| 157 | ptr->is_deleted = is_delete; | 46 | entry->is_deleted = is_delete; |
| 158 | error = 0; | 47 | error = 0; |
| 159 | break; | 48 | break; |
| 160 | } | 49 | } |
| 161 | if (!is_delete && error) { | 50 | if (error && !is_delete) { |
| 162 | struct tomoyo_domain_initializer_entry *entry = | 51 | entry = tomoyo_commit_ok(new_entry, size); |
| 163 | tomoyo_commit_ok(&e, sizeof(e)); | ||
| 164 | if (entry) { | 52 | if (entry) { |
| 165 | list_add_tail_rcu(&entry->list, | 53 | list_add_tail_rcu(&entry->list, list); |
| 166 | &tomoyo_domain_initializer_list); | ||
| 167 | error = 0; | 54 | error = 0; |
| 168 | } | 55 | } |
| 169 | } | 56 | } |
| 170 | mutex_unlock(&tomoyo_policy_lock); | 57 | mutex_unlock(&tomoyo_policy_lock); |
| 171 | out: | ||
| 172 | tomoyo_put_name(e.domainname); | ||
| 173 | tomoyo_put_name(e.program); | ||
| 174 | return error; | 58 | return error; |
| 175 | } | 59 | } |
| 176 | 60 | ||
| 177 | /** | 61 | /** |
| 178 | * tomoyo_read_domain_initializer_policy - Read "struct tomoyo_domain_initializer_entry" list. | 62 | * tomoyo_update_domain - Update an entry for domain policy. |
| 179 | * | 63 | * |
| 180 | * @head: Pointer to "struct tomoyo_io_buffer". | 64 | * @new_entry: Pointer to "struct tomoyo_acl_info". |
| 65 | * @size: Size of @new_entry in bytes. | ||
| 66 | * @is_delete: True if it is a delete request. | ||
| 67 | * @domain: Pointer to "struct tomoyo_domain_info". | ||
| 68 | * @check_duplicate: Callback function to find duplicated entry. | ||
| 69 | * @merge_duplicate: Callback function to merge duplicated entry. | ||
| 181 | * | 70 | * |
| 182 | * Returns true on success, false otherwise. | 71 | * Returns 0 on success, negative value otherwise. |
| 183 | * | 72 | * |
| 184 | * Caller holds tomoyo_read_lock(). | 73 | * Caller holds tomoyo_read_lock(). |
| 185 | */ | 74 | */ |
| 186 | bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head) | 75 | int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size, |
| 76 | bool is_delete, struct tomoyo_domain_info *domain, | ||
| 77 | bool (*check_duplicate) (const struct tomoyo_acl_info | ||
| 78 | *, | ||
| 79 | const struct tomoyo_acl_info | ||
| 80 | *), | ||
| 81 | bool (*merge_duplicate) (struct tomoyo_acl_info *, | ||
| 82 | struct tomoyo_acl_info *, | ||
| 83 | const bool)) | ||
| 187 | { | 84 | { |
| 188 | struct list_head *pos; | 85 | int error = is_delete ? -ENOENT : -ENOMEM; |
| 189 | bool done = true; | 86 | struct tomoyo_acl_info *entry; |
| 190 | 87 | ||
| 191 | list_for_each_cookie(pos, head->read_var2, | 88 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) |
| 192 | &tomoyo_domain_initializer_list) { | 89 | return error; |
| 193 | const char *no; | 90 | list_for_each_entry_rcu(entry, &domain->acl_info_list, list) { |
| 194 | const char *from = ""; | 91 | if (!check_duplicate(entry, new_entry)) |
| 195 | const char *domain = ""; | ||
| 196 | struct tomoyo_domain_initializer_entry *ptr; | ||
| 197 | ptr = list_entry(pos, struct tomoyo_domain_initializer_entry, | ||
| 198 | list); | ||
| 199 | if (ptr->is_deleted) | ||
| 200 | continue; | 92 | continue; |
| 201 | no = ptr->is_not ? "no_" : ""; | 93 | if (merge_duplicate) |
| 202 | if (ptr->domainname) { | 94 | entry->is_deleted = merge_duplicate(entry, new_entry, |
| 203 | from = " from "; | 95 | is_delete); |
| 204 | domain = ptr->domainname->name; | 96 | else |
| 97 | entry->is_deleted = is_delete; | ||
| 98 | error = 0; | ||
| 99 | break; | ||
| 100 | } | ||
| 101 | if (error && !is_delete) { | ||
| 102 | entry = tomoyo_commit_ok(new_entry, size); | ||
| 103 | if (entry) { | ||
| 104 | list_add_tail_rcu(&entry->list, &domain->acl_info_list); | ||
| 105 | error = 0; | ||
| 205 | } | 106 | } |
| 206 | done = tomoyo_io_printf(head, | ||
| 207 | "%s" TOMOYO_KEYWORD_INITIALIZE_DOMAIN | ||
| 208 | "%s%s%s\n", no, ptr->program->name, | ||
| 209 | from, domain); | ||
| 210 | if (!done) | ||
| 211 | break; | ||
| 212 | } | 107 | } |
| 213 | return done; | 108 | mutex_unlock(&tomoyo_policy_lock); |
| 109 | return error; | ||
| 214 | } | 110 | } |
| 215 | 111 | ||
| 216 | /** | 112 | void tomoyo_check_acl(struct tomoyo_request_info *r, |
| 217 | * tomoyo_write_domain_initializer_policy - Write "struct tomoyo_domain_initializer_entry" list. | 113 | bool (*check_entry) (struct tomoyo_request_info *, |
| 218 | * | 114 | const struct tomoyo_acl_info *)) |
| 219 | * @data: String to parse. | ||
| 220 | * @is_not: True if it is "no_initialize_domain" entry. | ||
| 221 | * @is_delete: True if it is a delete request. | ||
| 222 | * | ||
| 223 | * Returns 0 on success, negative value otherwise. | ||
| 224 | * | ||
| 225 | * Caller holds tomoyo_read_lock(). | ||
| 226 | */ | ||
| 227 | int tomoyo_write_domain_initializer_policy(char *data, const bool is_not, | ||
| 228 | const bool is_delete) | ||
| 229 | { | 115 | { |
| 230 | char *cp = strstr(data, " from "); | 116 | const struct tomoyo_domain_info *domain = r->domain; |
| 117 | struct tomoyo_acl_info *ptr; | ||
| 231 | 118 | ||
| 232 | if (cp) { | 119 | list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { |
| 233 | *cp = '\0'; | 120 | if (ptr->is_deleted || ptr->type != r->param_type) |
| 234 | return tomoyo_update_domain_initializer_entry(cp + 6, data, | 121 | continue; |
| 235 | is_not, | 122 | if (check_entry(r, ptr)) { |
| 236 | is_delete); | 123 | r->granted = true; |
| 124 | return; | ||
| 125 | } | ||
| 237 | } | 126 | } |
| 238 | return tomoyo_update_domain_initializer_entry(NULL, data, is_not, | 127 | r->granted = false; |
| 239 | is_delete); | ||
| 240 | } | 128 | } |
| 241 | 129 | ||
| 130 | /* The list for "struct tomoyo_domain_info". */ | ||
| 131 | LIST_HEAD(tomoyo_domain_list); | ||
| 132 | |||
| 133 | struct list_head tomoyo_policy_list[TOMOYO_MAX_POLICY]; | ||
| 134 | struct list_head tomoyo_group_list[TOMOYO_MAX_GROUP]; | ||
| 135 | |||
| 242 | /** | 136 | /** |
| 243 | * tomoyo_is_domain_initializer - Check whether the given program causes domainname reinitialization. | 137 | * tomoyo_last_word - Get last component of a domainname. |
| 244 | * | ||
| 245 | * @domainname: The name of domain. | ||
| 246 | * @program: The name of program. | ||
| 247 | * @last_name: The last component of @domainname. | ||
| 248 | * | 138 | * |
| 249 | * Returns true if executing @program reinitializes domain transition, | 139 | * @domainname: Domainname to check. |
| 250 | * false otherwise. | ||
| 251 | * | 140 | * |
| 252 | * Caller holds tomoyo_read_lock(). | 141 | * Returns the last word of @domainname. |
| 253 | */ | 142 | */ |
| 254 | static bool tomoyo_is_domain_initializer(const struct tomoyo_path_info * | 143 | static const char *tomoyo_last_word(const char *name) |
| 255 | domainname, | ||
| 256 | const struct tomoyo_path_info *program, | ||
| 257 | const struct tomoyo_path_info * | ||
| 258 | last_name) | ||
| 259 | { | 144 | { |
| 260 | struct tomoyo_domain_initializer_entry *ptr; | 145 | const char *cp = strrchr(name, ' '); |
| 261 | bool flag = false; | 146 | if (cp) |
| 262 | 147 | return cp + 1; | |
| 263 | list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, list) { | 148 | return name; |
| 264 | if (ptr->is_deleted) | ||
| 265 | continue; | ||
| 266 | if (ptr->domainname) { | ||
| 267 | if (!ptr->is_last_name) { | ||
| 268 | if (ptr->domainname != domainname) | ||
| 269 | continue; | ||
| 270 | } else { | ||
| 271 | if (tomoyo_pathcmp(ptr->domainname, last_name)) | ||
| 272 | continue; | ||
| 273 | } | ||
| 274 | } | ||
| 275 | if (tomoyo_pathcmp(ptr->program, program)) | ||
| 276 | continue; | ||
| 277 | if (ptr->is_not) { | ||
| 278 | flag = false; | ||
| 279 | break; | ||
| 280 | } | ||
| 281 | flag = true; | ||
| 282 | } | ||
| 283 | return flag; | ||
| 284 | } | 149 | } |
| 285 | 150 | ||
| 286 | /* | 151 | static bool tomoyo_same_transition_control(const struct tomoyo_acl_head *a, |
| 287 | * tomoyo_domain_keeper_list is used for holding list of domainnames which | 152 | const struct tomoyo_acl_head *b) |
| 288 | * suppresses domain transition. Normally, a domainname is monotonically | 153 | { |
| 289 | * getting longer. But sometimes, we want to suppress domain transition. | 154 | const struct tomoyo_transition_control *p1 = container_of(a, |
| 290 | * It would be convenient for us that programs executed from a login session | 155 | typeof(*p1), |
| 291 | * belong to the same domain. Thus, TOMOYO provides a way to suppress domain | 156 | head); |
| 292 | * transition. | 157 | const struct tomoyo_transition_control *p2 = container_of(b, |
| 293 | * | 158 | typeof(*p2), |
| 294 | * An entry is added by | 159 | head); |
| 295 | * | 160 | return p1->type == p2->type && p1->is_last_name == p2->is_last_name |
| 296 | * # echo 'keep_domain <kernel> /usr/sbin/sshd /bin/bash' > \ | 161 | && p1->domainname == p2->domainname |
| 297 | * /sys/kernel/security/tomoyo/exception_policy | 162 | && p1->program == p2->program; |
| 298 | * | 163 | } |
| 299 | * and is deleted by | ||
| 300 | * | ||
| 301 | * # echo 'delete keep_domain <kernel> /usr/sbin/sshd /bin/bash' > \ | ||
| 302 | * /sys/kernel/security/tomoyo/exception_policy | ||
| 303 | * | ||
| 304 | * and all entries are retrieved by | ||
| 305 | * | ||
| 306 | * # grep ^keep_domain /sys/kernel/security/tomoyo/exception_policy | ||
| 307 | * | ||
| 308 | * In the example above, any process which belongs to | ||
| 309 | * "<kernel> /usr/sbin/sshd /bin/bash" domain will remain in that domain, | ||
| 310 | * unless explicitly specified by "initialize_domain" or "no_keep_domain". | ||
| 311 | * | ||
| 312 | * You may specify a program using "from" keyword. | ||
| 313 | * "keep_domain /bin/pwd from <kernel> /usr/sbin/sshd /bin/bash" | ||
| 314 | * will cause "/bin/pwd" executed from "<kernel> /usr/sbin/sshd /bin/bash" | ||
| 315 | * domain to remain in "<kernel> /usr/sbin/sshd /bin/bash" domain. | ||
| 316 | * | ||
| 317 | * You may add "no_" prefix to "keep_domain". | ||
| 318 | * "keep_domain <kernel> /usr/sbin/sshd /bin/bash" and | ||
| 319 | * "no_keep_domain /usr/bin/passwd from <kernel> /usr/sbin/sshd /bin/bash" will | ||
| 320 | * cause "/usr/bin/passwd" to belong to | ||
| 321 | * "<kernel> /usr/sbin/sshd /bin/bash /usr/bin/passwd" domain, unless | ||
| 322 | * explicitly specified by "initialize_domain". | ||
| 323 | */ | ||
| 324 | LIST_HEAD(tomoyo_domain_keeper_list); | ||
| 325 | 164 | ||
| 326 | /** | 165 | /** |
| 327 | * tomoyo_update_domain_keeper_entry - Update "struct tomoyo_domain_keeper_entry" list. | 166 | * tomoyo_update_transition_control_entry - Update "struct tomoyo_transition_control" list. |
| 328 | * | 167 | * |
| 329 | * @domainname: The name of domain. | 168 | * @domainname: The name of domain. Maybe NULL. |
| 330 | * @program: The name of program. May be NULL. | 169 | * @program: The name of program. Maybe NULL. |
| 331 | * @is_not: True if it is "no_keep_domain" entry. | 170 | * @type: Type of transition. |
| 332 | * @is_delete: True if it is a delete request. | 171 | * @is_delete: True if it is a delete request. |
| 333 | * | 172 | * |
| 334 | * Returns 0 on success, negative value otherwise. | 173 | * Returns 0 on success, negative value otherwise. |
| 335 | * | ||
| 336 | * Caller holds tomoyo_read_lock(). | ||
| 337 | */ | 174 | */ |
| 338 | static int tomoyo_update_domain_keeper_entry(const char *domainname, | 175 | static int tomoyo_update_transition_control_entry(const char *domainname, |
| 339 | const char *program, | 176 | const char *program, |
| 340 | const bool is_not, | 177 | const u8 type, |
| 341 | const bool is_delete) | 178 | const bool is_delete) |
| 342 | { | 179 | { |
| 343 | struct tomoyo_domain_keeper_entry *ptr; | 180 | struct tomoyo_transition_control e = { .type = type }; |
| 344 | struct tomoyo_domain_keeper_entry e = { .is_not = is_not }; | ||
| 345 | int error = is_delete ? -ENOENT : -ENOMEM; | 181 | int error = is_delete ? -ENOENT : -ENOMEM; |
| 346 | |||
| 347 | if (!tomoyo_is_domain_def(domainname) && | ||
| 348 | tomoyo_is_correct_path(domainname, 1, -1, -1)) | ||
| 349 | e.is_last_name = true; | ||
| 350 | else if (!tomoyo_is_correct_domain(domainname)) | ||
| 351 | return -EINVAL; | ||
| 352 | if (program) { | 182 | if (program) { |
| 353 | if (!tomoyo_is_correct_path(program, 1, -1, -1)) | 183 | if (!tomoyo_correct_path(program)) |
| 354 | return -EINVAL; | 184 | return -EINVAL; |
| 355 | e.program = tomoyo_get_name(program); | 185 | e.program = tomoyo_get_name(program); |
| 356 | if (!e.program) | 186 | if (!e.program) |
| 357 | goto out; | 187 | goto out; |
| 358 | } | 188 | } |
| 359 | e.domainname = tomoyo_get_name(domainname); | 189 | if (domainname) { |
| 360 | if (!e.domainname) | 190 | if (!tomoyo_correct_domain(domainname)) { |
| 361 | goto out; | 191 | if (!tomoyo_correct_path(domainname)) |
| 362 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) | 192 | goto out; |
| 363 | goto out; | 193 | e.is_last_name = true; |
| 364 | list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, list) { | ||
| 365 | if (!tomoyo_is_same_domain_keeper_entry(ptr, &e)) | ||
| 366 | continue; | ||
| 367 | ptr->is_deleted = is_delete; | ||
| 368 | error = 0; | ||
| 369 | break; | ||
| 370 | } | ||
| 371 | if (!is_delete && error) { | ||
| 372 | struct tomoyo_domain_keeper_entry *entry = | ||
| 373 | tomoyo_commit_ok(&e, sizeof(e)); | ||
| 374 | if (entry) { | ||
| 375 | list_add_tail_rcu(&entry->list, | ||
| 376 | &tomoyo_domain_keeper_list); | ||
| 377 | error = 0; | ||
| 378 | } | 194 | } |
| 195 | e.domainname = tomoyo_get_name(domainname); | ||
| 196 | if (!e.domainname) | ||
| 197 | goto out; | ||
| 379 | } | 198 | } |
| 380 | mutex_unlock(&tomoyo_policy_lock); | 199 | error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, |
| 200 | &tomoyo_policy_list | ||
| 201 | [TOMOYO_ID_TRANSITION_CONTROL], | ||
| 202 | tomoyo_same_transition_control); | ||
| 381 | out: | 203 | out: |
| 382 | tomoyo_put_name(e.domainname); | 204 | tomoyo_put_name(e.domainname); |
| 383 | tomoyo_put_name(e.program); | 205 | tomoyo_put_name(e.program); |
| @@ -385,219 +207,133 @@ static int tomoyo_update_domain_keeper_entry(const char *domainname, | |||
| 385 | } | 207 | } |
| 386 | 208 | ||
| 387 | /** | 209 | /** |
| 388 | * tomoyo_write_domain_keeper_policy - Write "struct tomoyo_domain_keeper_entry" list. | 210 | * tomoyo_write_transition_control - Write "struct tomoyo_transition_control" list. |
| 389 | * | 211 | * |
| 390 | * @data: String to parse. | 212 | * @data: String to parse. |
| 391 | * @is_not: True if it is "no_keep_domain" entry. | ||
| 392 | * @is_delete: True if it is a delete request. | 213 | * @is_delete: True if it is a delete request. |
| 214 | * @type: Type of this entry. | ||
| 393 | * | 215 | * |
| 394 | * Caller holds tomoyo_read_lock(). | 216 | * Returns 0 on success, negative value otherwise. |
| 395 | */ | ||
| 396 | int tomoyo_write_domain_keeper_policy(char *data, const bool is_not, | ||
| 397 | const bool is_delete) | ||
| 398 | { | ||
| 399 | char *cp = strstr(data, " from "); | ||
| 400 | |||
| 401 | if (cp) { | ||
| 402 | *cp = '\0'; | ||
| 403 | return tomoyo_update_domain_keeper_entry(cp + 6, data, is_not, | ||
| 404 | is_delete); | ||
| 405 | } | ||
| 406 | return tomoyo_update_domain_keeper_entry(data, NULL, is_not, is_delete); | ||
| 407 | } | ||
| 408 | |||
| 409 | /** | ||
| 410 | * tomoyo_read_domain_keeper_policy - Read "struct tomoyo_domain_keeper_entry" list. | ||
| 411 | * | ||
| 412 | * @head: Pointer to "struct tomoyo_io_buffer". | ||
| 413 | * | ||
| 414 | * Returns true on success, false otherwise. | ||
| 415 | * | ||
| 416 | * Caller holds tomoyo_read_lock(). | ||
| 417 | */ | 217 | */ |
| 418 | bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head) | 218 | int tomoyo_write_transition_control(char *data, const bool is_delete, |
| 219 | const u8 type) | ||
| 419 | { | 220 | { |
| 420 | struct list_head *pos; | 221 | char *domainname = strstr(data, " from "); |
| 421 | bool done = true; | 222 | if (domainname) { |
| 422 | 223 | *domainname = '\0'; | |
| 423 | list_for_each_cookie(pos, head->read_var2, | 224 | domainname += 6; |
| 424 | &tomoyo_domain_keeper_list) { | 225 | } else if (type == TOMOYO_TRANSITION_CONTROL_NO_KEEP || |
| 425 | struct tomoyo_domain_keeper_entry *ptr; | 226 | type == TOMOYO_TRANSITION_CONTROL_KEEP) { |
| 426 | const char *no; | 227 | domainname = data; |
| 427 | const char *from = ""; | 228 | data = NULL; |
| 428 | const char *program = ""; | ||
| 429 | |||
| 430 | ptr = list_entry(pos, struct tomoyo_domain_keeper_entry, list); | ||
| 431 | if (ptr->is_deleted) | ||
| 432 | continue; | ||
| 433 | no = ptr->is_not ? "no_" : ""; | ||
| 434 | if (ptr->program) { | ||
| 435 | from = " from "; | ||
| 436 | program = ptr->program->name; | ||
| 437 | } | ||
| 438 | done = tomoyo_io_printf(head, | ||
| 439 | "%s" TOMOYO_KEYWORD_KEEP_DOMAIN | ||
| 440 | "%s%s%s\n", no, program, from, | ||
| 441 | ptr->domainname->name); | ||
| 442 | if (!done) | ||
| 443 | break; | ||
| 444 | } | 229 | } |
| 445 | return done; | 230 | return tomoyo_update_transition_control_entry(domainname, data, type, |
| 231 | is_delete); | ||
| 446 | } | 232 | } |
| 447 | 233 | ||
| 448 | /** | 234 | /** |
| 449 | * tomoyo_is_domain_keeper - Check whether the given program causes domain transition suppression. | 235 | * tomoyo_transition_type - Get domain transition type. |
| 450 | * | 236 | * |
| 451 | * @domainname: The name of domain. | 237 | * @domainname: The name of domain. |
| 452 | * @program: The name of program. | 238 | * @program: The name of program. |
| 453 | * @last_name: The last component of @domainname. | ||
| 454 | * | 239 | * |
| 455 | * Returns true if executing @program supresses domain transition, | 240 | * Returns TOMOYO_TRANSITION_CONTROL_INITIALIZE if executing @program |
| 456 | * false otherwise. | 241 | * reinitializes domain transition, TOMOYO_TRANSITION_CONTROL_KEEP if executing |
| 242 | * @program suppresses domain transition, others otherwise. | ||
| 457 | * | 243 | * |
| 458 | * Caller holds tomoyo_read_lock(). | 244 | * Caller holds tomoyo_read_lock(). |
| 459 | */ | 245 | */ |
| 460 | static bool tomoyo_is_domain_keeper(const struct tomoyo_path_info *domainname, | 246 | static u8 tomoyo_transition_type(const struct tomoyo_path_info *domainname, |
| 461 | const struct tomoyo_path_info *program, | 247 | const struct tomoyo_path_info *program) |
| 462 | const struct tomoyo_path_info *last_name) | ||
| 463 | { | 248 | { |
| 464 | struct tomoyo_domain_keeper_entry *ptr; | 249 | const struct tomoyo_transition_control *ptr; |
| 465 | bool flag = false; | 250 | const char *last_name = tomoyo_last_word(domainname->name); |
| 466 | 251 | u8 type; | |
| 467 | list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, list) { | 252 | for (type = 0; type < TOMOYO_MAX_TRANSITION_TYPE; type++) { |
| 468 | if (ptr->is_deleted) | 253 | next: |
| 469 | continue; | 254 | list_for_each_entry_rcu(ptr, &tomoyo_policy_list |
| 470 | if (!ptr->is_last_name) { | 255 | [TOMOYO_ID_TRANSITION_CONTROL], |
| 471 | if (ptr->domainname != domainname) | 256 | head.list) { |
| 257 | if (ptr->head.is_deleted || ptr->type != type) | ||
| 472 | continue; | 258 | continue; |
| 473 | } else { | 259 | if (ptr->domainname) { |
| 474 | if (tomoyo_pathcmp(ptr->domainname, last_name)) | 260 | if (!ptr->is_last_name) { |
| 261 | if (ptr->domainname != domainname) | ||
| 262 | continue; | ||
| 263 | } else { | ||
| 264 | /* | ||
| 265 | * Use direct strcmp() since this is | ||
| 266 | * unlikely used. | ||
| 267 | */ | ||
| 268 | if (strcmp(ptr->domainname->name, | ||
| 269 | last_name)) | ||
| 270 | continue; | ||
| 271 | } | ||
| 272 | } | ||
| 273 | if (ptr->program && | ||
| 274 | tomoyo_pathcmp(ptr->program, program)) | ||
| 475 | continue; | 275 | continue; |
| 276 | if (type == TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE) { | ||
| 277 | /* | ||
| 278 | * Do not check for initialize_domain if | ||
| 279 | * no_initialize_domain matched. | ||
| 280 | */ | ||
| 281 | type = TOMOYO_TRANSITION_CONTROL_NO_KEEP; | ||
| 282 | goto next; | ||
| 283 | } | ||
| 284 | goto done; | ||
| 476 | } | 285 | } |
| 477 | if (ptr->program && tomoyo_pathcmp(ptr->program, program)) | ||
| 478 | continue; | ||
| 479 | if (ptr->is_not) { | ||
| 480 | flag = false; | ||
| 481 | break; | ||
| 482 | } | ||
| 483 | flag = true; | ||
| 484 | } | 286 | } |
| 485 | return flag; | 287 | done: |
| 288 | return type; | ||
| 486 | } | 289 | } |
| 487 | 290 | ||
| 488 | /* | 291 | static bool tomoyo_same_aggregator(const struct tomoyo_acl_head *a, |
| 489 | * tomoyo_alias_list is used for holding list of symlink's pathnames which are | 292 | const struct tomoyo_acl_head *b) |
| 490 | * allowed to be passed to an execve() request. Normally, the domainname which | 293 | { |
| 491 | * the current process will belong to after execve() succeeds is calculated | 294 | const struct tomoyo_aggregator *p1 = container_of(a, typeof(*p1), head); |
| 492 | * using dereferenced pathnames. But some programs behave differently depending | 295 | const struct tomoyo_aggregator *p2 = container_of(b, typeof(*p2), head); |
| 493 | * on the name passed to argv[0]. For busybox, calculating domainname using | 296 | return p1->original_name == p2->original_name && |
| 494 | * dereferenced pathnames will cause all programs in the busybox to belong to | 297 | p1->aggregated_name == p2->aggregated_name; |
| 495 | * the same domain. Thus, TOMOYO provides a way to allow use of symlink's | 298 | } |
| 496 | * pathname for checking execve()'s permission and calculating domainname which | ||
| 497 | * the current process will belong to after execve() succeeds. | ||
| 498 | * | ||
| 499 | * An entry is added by | ||
| 500 | * | ||
| 501 | * # echo 'alias /bin/busybox /bin/cat' > \ | ||
| 502 | * /sys/kernel/security/tomoyo/exception_policy | ||
| 503 | * | ||
| 504 | * and is deleted by | ||
| 505 | * | ||
| 506 | * # echo 'delete alias /bin/busybox /bin/cat' > \ | ||
| 507 | * /sys/kernel/security/tomoyo/exception_policy | ||
| 508 | * | ||
| 509 | * and all entries are retrieved by | ||
| 510 | * | ||
| 511 | * # grep ^alias /sys/kernel/security/tomoyo/exception_policy | ||
| 512 | * | ||
| 513 | * In the example above, if /bin/cat is a symlink to /bin/busybox and execution | ||
| 514 | * of /bin/cat is requested, permission is checked for /bin/cat rather than | ||
| 515 | * /bin/busybox and domainname which the current process will belong to after | ||
| 516 | * execve() succeeds is calculated using /bin/cat rather than /bin/busybox . | ||
| 517 | */ | ||
| 518 | LIST_HEAD(tomoyo_alias_list); | ||
| 519 | 299 | ||
| 520 | /** | 300 | /** |
| 521 | * tomoyo_update_alias_entry - Update "struct tomoyo_alias_entry" list. | 301 | * tomoyo_update_aggregator_entry - Update "struct tomoyo_aggregator" list. |
| 522 | * | 302 | * |
| 523 | * @original_name: The original program's real name. | 303 | * @original_name: The original program's name. |
| 524 | * @aliased_name: The symbolic program's symbolic link's name. | 304 | * @aggregated_name: The program name to use. |
| 525 | * @is_delete: True if it is a delete request. | 305 | * @is_delete: True if it is a delete request. |
| 526 | * | 306 | * |
| 527 | * Returns 0 on success, negative value otherwise. | 307 | * Returns 0 on success, negative value otherwise. |
| 528 | * | 308 | * |
| 529 | * Caller holds tomoyo_read_lock(). | 309 | * Caller holds tomoyo_read_lock(). |
| 530 | */ | 310 | */ |
| 531 | static int tomoyo_update_alias_entry(const char *original_name, | 311 | static int tomoyo_update_aggregator_entry(const char *original_name, |
| 532 | const char *aliased_name, | 312 | const char *aggregated_name, |
| 533 | const bool is_delete) | 313 | const bool is_delete) |
| 534 | { | 314 | { |
| 535 | struct tomoyo_alias_entry *ptr; | 315 | struct tomoyo_aggregator e = { }; |
| 536 | struct tomoyo_alias_entry e = { }; | ||
| 537 | int error = is_delete ? -ENOENT : -ENOMEM; | 316 | int error = is_delete ? -ENOENT : -ENOMEM; |
| 538 | 317 | ||
| 539 | if (!tomoyo_is_correct_path(original_name, 1, -1, -1) || | 318 | if (!tomoyo_correct_path(original_name) || |
| 540 | !tomoyo_is_correct_path(aliased_name, 1, -1, -1)) | 319 | !tomoyo_correct_path(aggregated_name)) |
| 541 | return -EINVAL; /* No patterns allowed. */ | 320 | return -EINVAL; |
| 542 | e.original_name = tomoyo_get_name(original_name); | 321 | e.original_name = tomoyo_get_name(original_name); |
| 543 | e.aliased_name = tomoyo_get_name(aliased_name); | 322 | e.aggregated_name = tomoyo_get_name(aggregated_name); |
| 544 | if (!e.original_name || !e.aliased_name) | 323 | if (!e.original_name || !e.aggregated_name || |
| 324 | e.aggregated_name->is_patterned) /* No patterns allowed. */ | ||
| 545 | goto out; | 325 | goto out; |
| 546 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) | 326 | error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, |
| 547 | goto out; | 327 | &tomoyo_policy_list[TOMOYO_ID_AGGREGATOR], |
| 548 | list_for_each_entry_rcu(ptr, &tomoyo_alias_list, list) { | 328 | tomoyo_same_aggregator); |
| 549 | if (!tomoyo_is_same_alias_entry(ptr, &e)) | ||
| 550 | continue; | ||
| 551 | ptr->is_deleted = is_delete; | ||
| 552 | error = 0; | ||
| 553 | break; | ||
| 554 | } | ||
| 555 | if (!is_delete && error) { | ||
| 556 | struct tomoyo_alias_entry *entry = | ||
| 557 | tomoyo_commit_ok(&e, sizeof(e)); | ||
| 558 | if (entry) { | ||
| 559 | list_add_tail_rcu(&entry->list, &tomoyo_alias_list); | ||
| 560 | error = 0; | ||
| 561 | } | ||
| 562 | } | ||
| 563 | mutex_unlock(&tomoyo_policy_lock); | ||
| 564 | out: | 329 | out: |
| 565 | tomoyo_put_name(e.original_name); | 330 | tomoyo_put_name(e.original_name); |
| 566 | tomoyo_put_name(e.aliased_name); | 331 | tomoyo_put_name(e.aggregated_name); |
| 567 | return error; | 332 | return error; |
| 568 | } | 333 | } |
| 569 | 334 | ||
| 570 | /** | 335 | /** |
| 571 | * tomoyo_read_alias_policy - Read "struct tomoyo_alias_entry" list. | 336 | * tomoyo_write_aggregator - Write "struct tomoyo_aggregator" list. |
| 572 | * | ||
| 573 | * @head: Pointer to "struct tomoyo_io_buffer". | ||
| 574 | * | ||
| 575 | * Returns true on success, false otherwise. | ||
| 576 | * | ||
| 577 | * Caller holds tomoyo_read_lock(). | ||
| 578 | */ | ||
| 579 | bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head) | ||
| 580 | { | ||
| 581 | struct list_head *pos; | ||
| 582 | bool done = true; | ||
| 583 | |||
| 584 | list_for_each_cookie(pos, head->read_var2, &tomoyo_alias_list) { | ||
| 585 | struct tomoyo_alias_entry *ptr; | ||
| 586 | |||
| 587 | ptr = list_entry(pos, struct tomoyo_alias_entry, list); | ||
| 588 | if (ptr->is_deleted) | ||
| 589 | continue; | ||
| 590 | done = tomoyo_io_printf(head, TOMOYO_KEYWORD_ALIAS "%s %s\n", | ||
| 591 | ptr->original_name->name, | ||
| 592 | ptr->aliased_name->name); | ||
| 593 | if (!done) | ||
| 594 | break; | ||
| 595 | } | ||
| 596 | return done; | ||
| 597 | } | ||
| 598 | |||
| 599 | /** | ||
| 600 | * tomoyo_write_alias_policy - Write "struct tomoyo_alias_entry" list. | ||
| 601 | * | 337 | * |
| 602 | * @data: String to parse. | 338 | * @data: String to parse. |
| 603 | * @is_delete: True if it is a delete request. | 339 | * @is_delete: True if it is a delete request. |
| @@ -606,18 +342,18 @@ bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head) | |||
| 606 | * | 342 | * |
| 607 | * Caller holds tomoyo_read_lock(). | 343 | * Caller holds tomoyo_read_lock(). |
| 608 | */ | 344 | */ |
| 609 | int tomoyo_write_alias_policy(char *data, const bool is_delete) | 345 | int tomoyo_write_aggregator(char *data, const bool is_delete) |
| 610 | { | 346 | { |
| 611 | char *cp = strchr(data, ' '); | 347 | char *cp = strchr(data, ' '); |
| 612 | 348 | ||
| 613 | if (!cp) | 349 | if (!cp) |
| 614 | return -EINVAL; | 350 | return -EINVAL; |
| 615 | *cp++ = '\0'; | 351 | *cp++ = '\0'; |
| 616 | return tomoyo_update_alias_entry(data, cp, is_delete); | 352 | return tomoyo_update_aggregator_entry(data, cp, is_delete); |
| 617 | } | 353 | } |
| 618 | 354 | ||
| 619 | /** | 355 | /** |
| 620 | * tomoyo_find_or_assign_new_domain - Create a domain. | 356 | * tomoyo_assign_domain - Create a domain. |
| 621 | * | 357 | * |
| 622 | * @domainname: The name of domain. | 358 | * @domainname: The name of domain. |
| 623 | * @profile: Profile number to assign if the domain was newly created. | 359 | * @profile: Profile number to assign if the domain was newly created. |
| @@ -626,16 +362,15 @@ int tomoyo_write_alias_policy(char *data, const bool is_delete) | |||
| 626 | * | 362 | * |
| 627 | * Caller holds tomoyo_read_lock(). | 363 | * Caller holds tomoyo_read_lock(). |
| 628 | */ | 364 | */ |
| 629 | struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char * | 365 | struct tomoyo_domain_info *tomoyo_assign_domain(const char *domainname, |
| 630 | domainname, | 366 | const u8 profile) |
| 631 | const u8 profile) | ||
| 632 | { | 367 | { |
| 633 | struct tomoyo_domain_info *entry; | 368 | struct tomoyo_domain_info *entry; |
| 634 | struct tomoyo_domain_info *domain = NULL; | 369 | struct tomoyo_domain_info *domain = NULL; |
| 635 | const struct tomoyo_path_info *saved_domainname; | 370 | const struct tomoyo_path_info *saved_domainname; |
| 636 | bool found = false; | 371 | bool found = false; |
| 637 | 372 | ||
| 638 | if (!tomoyo_is_correct_domain(domainname)) | 373 | if (!tomoyo_correct_domain(domainname)) |
| 639 | return NULL; | 374 | return NULL; |
| 640 | saved_domainname = tomoyo_get_name(domainname); | 375 | saved_domainname = tomoyo_get_name(domainname); |
| 641 | if (!saved_domainname) | 376 | if (!saved_domainname) |
| @@ -678,116 +413,118 @@ struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char * | |||
| 678 | */ | 413 | */ |
| 679 | int tomoyo_find_next_domain(struct linux_binprm *bprm) | 414 | int tomoyo_find_next_domain(struct linux_binprm *bprm) |
| 680 | { | 415 | { |
| 681 | /* | 416 | struct tomoyo_request_info r; |
| 682 | * This function assumes that the size of buffer returned by | 417 | char *tmp = kzalloc(TOMOYO_EXEC_TMPSIZE, GFP_NOFS); |
| 683 | * tomoyo_realpath() = TOMOYO_MAX_PATHNAME_LEN. | ||
| 684 | */ | ||
| 685 | struct tomoyo_page_buffer *tmp = kzalloc(sizeof(*tmp), GFP_NOFS); | ||
| 686 | struct tomoyo_domain_info *old_domain = tomoyo_domain(); | 418 | struct tomoyo_domain_info *old_domain = tomoyo_domain(); |
| 687 | struct tomoyo_domain_info *domain = NULL; | 419 | struct tomoyo_domain_info *domain = NULL; |
| 688 | const char *old_domain_name = old_domain->domainname->name; | ||
| 689 | const char *original_name = bprm->filename; | 420 | const char *original_name = bprm->filename; |
| 690 | char *new_domain_name = NULL; | 421 | u8 mode; |
| 691 | char *real_program_name = NULL; | 422 | bool is_enforce; |
| 692 | char *symlink_program_name = NULL; | ||
| 693 | const u8 mode = tomoyo_check_flags(old_domain, TOMOYO_MAC_FOR_FILE); | ||
| 694 | const bool is_enforce = (mode == 3); | ||
| 695 | int retval = -ENOMEM; | 423 | int retval = -ENOMEM; |
| 696 | struct tomoyo_path_info r; /* real name */ | 424 | bool need_kfree = false; |
| 697 | struct tomoyo_path_info s; /* symlink name */ | 425 | struct tomoyo_path_info rn = { }; /* real name */ |
| 698 | struct tomoyo_path_info l; /* last name */ | ||
| 699 | static bool initialized; | ||
| 700 | 426 | ||
| 427 | mode = tomoyo_init_request_info(&r, NULL, TOMOYO_MAC_FILE_EXECUTE); | ||
| 428 | is_enforce = (mode == TOMOYO_CONFIG_ENFORCING); | ||
| 701 | if (!tmp) | 429 | if (!tmp) |
| 702 | goto out; | 430 | goto out; |
| 703 | 431 | ||
| 704 | if (!initialized) { | 432 | retry: |
| 705 | /* | 433 | if (need_kfree) { |
| 706 | * Built-in initializers. This is needed because policies are | 434 | kfree(rn.name); |
| 707 | * not loaded until starting /sbin/init. | 435 | need_kfree = false; |
| 708 | */ | ||
| 709 | tomoyo_update_domain_initializer_entry(NULL, "/sbin/hotplug", | ||
| 710 | false, false); | ||
| 711 | tomoyo_update_domain_initializer_entry(NULL, "/sbin/modprobe", | ||
| 712 | false, false); | ||
| 713 | initialized = true; | ||
| 714 | } | 436 | } |
| 715 | 437 | /* Get symlink's pathname of program. */ | |
| 716 | /* Get tomoyo_realpath of program. */ | ||
| 717 | retval = -ENOENT; | 438 | retval = -ENOENT; |
| 718 | /* I hope tomoyo_realpath() won't fail with -ENOMEM. */ | 439 | rn.name = tomoyo_realpath_nofollow(original_name); |
| 719 | real_program_name = tomoyo_realpath(original_name); | 440 | if (!rn.name) |
| 720 | if (!real_program_name) | ||
| 721 | goto out; | ||
| 722 | /* Get tomoyo_realpath of symbolic link. */ | ||
| 723 | symlink_program_name = tomoyo_realpath_nofollow(original_name); | ||
| 724 | if (!symlink_program_name) | ||
| 725 | goto out; | 441 | goto out; |
| 726 | 442 | tomoyo_fill_path_info(&rn); | |
| 727 | r.name = real_program_name; | 443 | need_kfree = true; |
| 728 | tomoyo_fill_path_info(&r); | 444 | |
| 729 | s.name = symlink_program_name; | 445 | /* Check 'aggregator' directive. */ |
| 730 | tomoyo_fill_path_info(&s); | 446 | { |
| 731 | l.name = tomoyo_get_last_name(old_domain); | 447 | struct tomoyo_aggregator *ptr; |
| 732 | tomoyo_fill_path_info(&l); | 448 | list_for_each_entry_rcu(ptr, &tomoyo_policy_list |
| 733 | 449 | [TOMOYO_ID_AGGREGATOR], head.list) { | |
| 734 | /* Check 'alias' directive. */ | 450 | if (ptr->head.is_deleted || |
| 735 | if (tomoyo_pathcmp(&r, &s)) { | 451 | !tomoyo_path_matches_pattern(&rn, |
| 736 | struct tomoyo_alias_entry *ptr; | 452 | ptr->original_name)) |
| 737 | /* Is this program allowed to be called via symbolic links? */ | ||
| 738 | list_for_each_entry_rcu(ptr, &tomoyo_alias_list, list) { | ||
| 739 | if (ptr->is_deleted || | ||
| 740 | tomoyo_pathcmp(&r, ptr->original_name) || | ||
| 741 | tomoyo_pathcmp(&s, ptr->aliased_name)) | ||
| 742 | continue; | 453 | continue; |
| 743 | memset(real_program_name, 0, TOMOYO_MAX_PATHNAME_LEN); | 454 | kfree(rn.name); |
| 744 | strncpy(real_program_name, ptr->aliased_name->name, | 455 | need_kfree = false; |
| 745 | TOMOYO_MAX_PATHNAME_LEN - 1); | 456 | /* This is OK because it is read only. */ |
| 746 | tomoyo_fill_path_info(&r); | 457 | rn = *ptr->aggregated_name; |
| 747 | break; | 458 | break; |
| 748 | } | 459 | } |
| 749 | } | 460 | } |
| 750 | 461 | ||
| 751 | /* Check execute permission. */ | 462 | /* Check execute permission. */ |
| 752 | retval = tomoyo_check_exec_perm(old_domain, &r); | 463 | retval = tomoyo_path_permission(&r, TOMOYO_TYPE_EXECUTE, &rn); |
| 464 | if (retval == TOMOYO_RETRY_REQUEST) | ||
| 465 | goto retry; | ||
| 753 | if (retval < 0) | 466 | if (retval < 0) |
| 754 | goto out; | 467 | goto out; |
| 468 | /* | ||
| 469 | * To be able to specify domainnames with wildcards, use the | ||
| 470 | * pathname specified in the policy (which may contain | ||
| 471 | * wildcard) rather than the pathname passed to execve() | ||
| 472 | * (which never contains wildcard). | ||
| 473 | */ | ||
| 474 | if (r.param.path.matched_path) { | ||
| 475 | if (need_kfree) | ||
| 476 | kfree(rn.name); | ||
| 477 | need_kfree = false; | ||
| 478 | /* This is OK because it is read only. */ | ||
| 479 | rn = *r.param.path.matched_path; | ||
| 480 | } | ||
| 755 | 481 | ||
| 756 | new_domain_name = tmp->buffer; | 482 | /* Calculate domain to transit to. */ |
| 757 | if (tomoyo_is_domain_initializer(old_domain->domainname, &r, &l)) { | 483 | switch (tomoyo_transition_type(old_domain->domainname, &rn)) { |
| 484 | case TOMOYO_TRANSITION_CONTROL_INITIALIZE: | ||
| 758 | /* Transit to the child of tomoyo_kernel_domain domain. */ | 485 | /* Transit to the child of tomoyo_kernel_domain domain. */ |
| 759 | snprintf(new_domain_name, TOMOYO_MAX_PATHNAME_LEN + 1, | 486 | snprintf(tmp, TOMOYO_EXEC_TMPSIZE - 1, TOMOYO_ROOT_NAME " " |
| 760 | TOMOYO_ROOT_NAME " " "%s", real_program_name); | 487 | "%s", rn.name); |
| 761 | } else if (old_domain == &tomoyo_kernel_domain && | 488 | break; |
| 762 | !tomoyo_policy_loaded) { | 489 | case TOMOYO_TRANSITION_CONTROL_KEEP: |
| 763 | /* | ||
| 764 | * Needn't to transit from kernel domain before starting | ||
| 765 | * /sbin/init. But transit from kernel domain if executing | ||
| 766 | * initializers because they might start before /sbin/init. | ||
| 767 | */ | ||
| 768 | domain = old_domain; | ||
| 769 | } else if (tomoyo_is_domain_keeper(old_domain->domainname, &r, &l)) { | ||
| 770 | /* Keep current domain. */ | 490 | /* Keep current domain. */ |
| 771 | domain = old_domain; | 491 | domain = old_domain; |
| 772 | } else { | 492 | break; |
| 773 | /* Normal domain transition. */ | 493 | default: |
| 774 | snprintf(new_domain_name, TOMOYO_MAX_PATHNAME_LEN + 1, | 494 | if (old_domain == &tomoyo_kernel_domain && |
| 775 | "%s %s", old_domain_name, real_program_name); | 495 | !tomoyo_policy_loaded) { |
| 496 | /* | ||
| 497 | * Needn't to transit from kernel domain before | ||
| 498 | * starting /sbin/init. But transit from kernel domain | ||
| 499 | * if executing initializers because they might start | ||
| 500 | * before /sbin/init. | ||
| 501 | */ | ||
| 502 | domain = old_domain; | ||
| 503 | } else { | ||
| 504 | /* Normal domain transition. */ | ||
| 505 | snprintf(tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s", | ||
| 506 | old_domain->domainname->name, rn.name); | ||
| 507 | } | ||
| 508 | break; | ||
| 776 | } | 509 | } |
| 777 | if (domain || strlen(new_domain_name) >= TOMOYO_MAX_PATHNAME_LEN) | 510 | if (domain || strlen(tmp) >= TOMOYO_EXEC_TMPSIZE - 10) |
| 778 | goto done; | 511 | goto done; |
| 779 | domain = tomoyo_find_domain(new_domain_name); | 512 | domain = tomoyo_find_domain(tmp); |
| 780 | if (domain) | 513 | if (domain) |
| 781 | goto done; | 514 | goto done; |
| 782 | if (is_enforce) | 515 | if (is_enforce) { |
| 783 | goto done; | 516 | int error = tomoyo_supervisor(&r, "# wants to create domain\n" |
| 784 | domain = tomoyo_find_or_assign_new_domain(new_domain_name, | 517 | "%s\n", tmp); |
| 785 | old_domain->profile); | 518 | if (error == TOMOYO_RETRY_REQUEST) |
| 519 | goto retry; | ||
| 520 | if (error < 0) | ||
| 521 | goto done; | ||
| 522 | } | ||
| 523 | domain = tomoyo_assign_domain(tmp, old_domain->profile); | ||
| 786 | done: | 524 | done: |
| 787 | if (domain) | 525 | if (domain) |
| 788 | goto out; | 526 | goto out; |
| 789 | printk(KERN_WARNING "TOMOYO-ERROR: Domain '%s' not defined.\n", | 527 | printk(KERN_WARNING "TOMOYO-ERROR: Domain '%s' not defined.\n", tmp); |
| 790 | new_domain_name); | ||
| 791 | if (is_enforce) | 528 | if (is_enforce) |
| 792 | retval = -EPERM; | 529 | retval = -EPERM; |
| 793 | else | 530 | else |
| @@ -798,8 +535,8 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) | |||
| 798 | /* Update reference count on "struct tomoyo_domain_info". */ | 535 | /* Update reference count on "struct tomoyo_domain_info". */ |
| 799 | atomic_inc(&domain->users); | 536 | atomic_inc(&domain->users); |
| 800 | bprm->cred->security = domain; | 537 | bprm->cred->security = domain; |
| 801 | kfree(real_program_name); | 538 | if (need_kfree) |
| 802 | kfree(symlink_program_name); | 539 | kfree(rn.name); |
| 803 | kfree(tmp); | 540 | kfree(tmp); |
| 804 | return retval; | 541 | return retval; |
| 805 | } | 542 | } |
diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c index 1c6f8238ec47..9d32f182301e 100644 --- a/security/tomoyo/file.c +++ b/security/tomoyo/file.c | |||
| @@ -1,48 +1,88 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * security/tomoyo/file.c | 2 | * security/tomoyo/file.c |
| 3 | * | 3 | * |
| 4 | * Implementation of the Domain-Based Mandatory Access Control. | 4 | * Pathname restriction functions. |
| 5 | * | ||
| 6 | * Copyright (C) 2005-2009 NTT DATA CORPORATION | ||
| 7 | * | ||
| 8 | * Version: 2.2.0 2009/04/01 | ||
| 9 | * | 5 | * |
| 6 | * Copyright (C) 2005-2010 NTT DATA CORPORATION | ||
| 10 | */ | 7 | */ |
| 11 | 8 | ||
| 12 | #include "common.h" | 9 | #include "common.h" |
| 13 | #include <linux/slab.h> | 10 | #include <linux/slab.h> |
| 14 | 11 | ||
| 15 | /* Keyword array for single path operations. */ | 12 | /* Keyword array for operations with one pathname. */ |
| 16 | static const char *tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION] = { | 13 | const char *tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION] = { |
| 17 | [TOMOYO_TYPE_READ_WRITE] = "read/write", | 14 | [TOMOYO_TYPE_READ_WRITE] = "read/write", |
| 18 | [TOMOYO_TYPE_EXECUTE] = "execute", | 15 | [TOMOYO_TYPE_EXECUTE] = "execute", |
| 19 | [TOMOYO_TYPE_READ] = "read", | 16 | [TOMOYO_TYPE_READ] = "read", |
| 20 | [TOMOYO_TYPE_WRITE] = "write", | 17 | [TOMOYO_TYPE_WRITE] = "write", |
| 21 | [TOMOYO_TYPE_CREATE] = "create", | ||
| 22 | [TOMOYO_TYPE_UNLINK] = "unlink", | 18 | [TOMOYO_TYPE_UNLINK] = "unlink", |
| 23 | [TOMOYO_TYPE_MKDIR] = "mkdir", | ||
| 24 | [TOMOYO_TYPE_RMDIR] = "rmdir", | 19 | [TOMOYO_TYPE_RMDIR] = "rmdir", |
| 25 | [TOMOYO_TYPE_MKFIFO] = "mkfifo", | ||
| 26 | [TOMOYO_TYPE_MKSOCK] = "mksock", | ||
| 27 | [TOMOYO_TYPE_MKBLOCK] = "mkblock", | ||
| 28 | [TOMOYO_TYPE_MKCHAR] = "mkchar", | ||
| 29 | [TOMOYO_TYPE_TRUNCATE] = "truncate", | 20 | [TOMOYO_TYPE_TRUNCATE] = "truncate", |
| 30 | [TOMOYO_TYPE_SYMLINK] = "symlink", | 21 | [TOMOYO_TYPE_SYMLINK] = "symlink", |
| 31 | [TOMOYO_TYPE_REWRITE] = "rewrite", | 22 | [TOMOYO_TYPE_REWRITE] = "rewrite", |
| 23 | [TOMOYO_TYPE_CHROOT] = "chroot", | ||
| 24 | [TOMOYO_TYPE_UMOUNT] = "unmount", | ||
| 25 | }; | ||
| 26 | |||
| 27 | /* Keyword array for operations with one pathname and three numbers. */ | ||
| 28 | const char *tomoyo_mkdev_keyword[TOMOYO_MAX_MKDEV_OPERATION] = { | ||
| 29 | [TOMOYO_TYPE_MKBLOCK] = "mkblock", | ||
| 30 | [TOMOYO_TYPE_MKCHAR] = "mkchar", | ||
| 31 | }; | ||
| 32 | |||
| 33 | /* Keyword array for operations with two pathnames. */ | ||
| 34 | const char *tomoyo_path2_keyword[TOMOYO_MAX_PATH2_OPERATION] = { | ||
| 35 | [TOMOYO_TYPE_LINK] = "link", | ||
| 36 | [TOMOYO_TYPE_RENAME] = "rename", | ||
| 37 | [TOMOYO_TYPE_PIVOT_ROOT] = "pivot_root", | ||
| 38 | }; | ||
| 39 | |||
| 40 | /* Keyword array for operations with one pathname and one number. */ | ||
| 41 | const char *tomoyo_path_number_keyword[TOMOYO_MAX_PATH_NUMBER_OPERATION] = { | ||
| 42 | [TOMOYO_TYPE_CREATE] = "create", | ||
| 43 | [TOMOYO_TYPE_MKDIR] = "mkdir", | ||
| 44 | [TOMOYO_TYPE_MKFIFO] = "mkfifo", | ||
| 45 | [TOMOYO_TYPE_MKSOCK] = "mksock", | ||
| 32 | [TOMOYO_TYPE_IOCTL] = "ioctl", | 46 | [TOMOYO_TYPE_IOCTL] = "ioctl", |
| 33 | [TOMOYO_TYPE_CHMOD] = "chmod", | 47 | [TOMOYO_TYPE_CHMOD] = "chmod", |
| 34 | [TOMOYO_TYPE_CHOWN] = "chown", | 48 | [TOMOYO_TYPE_CHOWN] = "chown", |
| 35 | [TOMOYO_TYPE_CHGRP] = "chgrp", | 49 | [TOMOYO_TYPE_CHGRP] = "chgrp", |
| 36 | [TOMOYO_TYPE_CHROOT] = "chroot", | ||
| 37 | [TOMOYO_TYPE_MOUNT] = "mount", | ||
| 38 | [TOMOYO_TYPE_UMOUNT] = "unmount", | ||
| 39 | }; | 50 | }; |
| 40 | 51 | ||
| 41 | /* Keyword array for double path operations. */ | 52 | static const u8 tomoyo_p2mac[TOMOYO_MAX_PATH_OPERATION] = { |
| 42 | static const char *tomoyo_path2_keyword[TOMOYO_MAX_PATH2_OPERATION] = { | 53 | [TOMOYO_TYPE_READ_WRITE] = TOMOYO_MAC_FILE_OPEN, |
| 43 | [TOMOYO_TYPE_LINK] = "link", | 54 | [TOMOYO_TYPE_EXECUTE] = TOMOYO_MAC_FILE_EXECUTE, |
| 44 | [TOMOYO_TYPE_RENAME] = "rename", | 55 | [TOMOYO_TYPE_READ] = TOMOYO_MAC_FILE_OPEN, |
| 45 | [TOMOYO_TYPE_PIVOT_ROOT] = "pivot_root", | 56 | [TOMOYO_TYPE_WRITE] = TOMOYO_MAC_FILE_OPEN, |
| 57 | [TOMOYO_TYPE_UNLINK] = TOMOYO_MAC_FILE_UNLINK, | ||
| 58 | [TOMOYO_TYPE_RMDIR] = TOMOYO_MAC_FILE_RMDIR, | ||
| 59 | [TOMOYO_TYPE_TRUNCATE] = TOMOYO_MAC_FILE_TRUNCATE, | ||
| 60 | [TOMOYO_TYPE_SYMLINK] = TOMOYO_MAC_FILE_SYMLINK, | ||
| 61 | [TOMOYO_TYPE_REWRITE] = TOMOYO_MAC_FILE_REWRITE, | ||
| 62 | [TOMOYO_TYPE_CHROOT] = TOMOYO_MAC_FILE_CHROOT, | ||
| 63 | [TOMOYO_TYPE_UMOUNT] = TOMOYO_MAC_FILE_UMOUNT, | ||
| 64 | }; | ||
| 65 | |||
| 66 | static const u8 tomoyo_pnnn2mac[TOMOYO_MAX_MKDEV_OPERATION] = { | ||
| 67 | [TOMOYO_TYPE_MKBLOCK] = TOMOYO_MAC_FILE_MKBLOCK, | ||
| 68 | [TOMOYO_TYPE_MKCHAR] = TOMOYO_MAC_FILE_MKCHAR, | ||
| 69 | }; | ||
| 70 | |||
| 71 | static const u8 tomoyo_pp2mac[TOMOYO_MAX_PATH2_OPERATION] = { | ||
| 72 | [TOMOYO_TYPE_LINK] = TOMOYO_MAC_FILE_LINK, | ||
| 73 | [TOMOYO_TYPE_RENAME] = TOMOYO_MAC_FILE_RENAME, | ||
| 74 | [TOMOYO_TYPE_PIVOT_ROOT] = TOMOYO_MAC_FILE_PIVOT_ROOT, | ||
| 75 | }; | ||
| 76 | |||
| 77 | static const u8 tomoyo_pn2mac[TOMOYO_MAX_PATH_NUMBER_OPERATION] = { | ||
| 78 | [TOMOYO_TYPE_CREATE] = TOMOYO_MAC_FILE_CREATE, | ||
| 79 | [TOMOYO_TYPE_MKDIR] = TOMOYO_MAC_FILE_MKDIR, | ||
| 80 | [TOMOYO_TYPE_MKFIFO] = TOMOYO_MAC_FILE_MKFIFO, | ||
| 81 | [TOMOYO_TYPE_MKSOCK] = TOMOYO_MAC_FILE_MKSOCK, | ||
| 82 | [TOMOYO_TYPE_IOCTL] = TOMOYO_MAC_FILE_IOCTL, | ||
| 83 | [TOMOYO_TYPE_CHMOD] = TOMOYO_MAC_FILE_CHMOD, | ||
| 84 | [TOMOYO_TYPE_CHOWN] = TOMOYO_MAC_FILE_CHOWN, | ||
| 85 | [TOMOYO_TYPE_CHGRP] = TOMOYO_MAC_FILE_CHGRP, | ||
| 46 | }; | 86 | }; |
| 47 | 87 | ||
| 48 | void tomoyo_put_name_union(struct tomoyo_name_union *ptr) | 88 | void tomoyo_put_name_union(struct tomoyo_name_union *ptr) |
| @@ -50,56 +90,45 @@ void tomoyo_put_name_union(struct tomoyo_name_union *ptr) | |||
| 50 | if (!ptr) | 90 | if (!ptr) |
| 51 | return; | 91 | return; |
| 52 | if (ptr->is_group) | 92 | if (ptr->is_group) |
| 53 | tomoyo_put_path_group(ptr->group); | 93 | tomoyo_put_group(ptr->group); |
| 54 | else | 94 | else |
| 55 | tomoyo_put_name(ptr->filename); | 95 | tomoyo_put_name(ptr->filename); |
| 56 | } | 96 | } |
| 57 | 97 | ||
| 58 | bool tomoyo_compare_name_union(const struct tomoyo_path_info *name, | 98 | const struct tomoyo_path_info * |
| 59 | const struct tomoyo_name_union *ptr) | 99 | tomoyo_compare_name_union(const struct tomoyo_path_info *name, |
| 100 | const struct tomoyo_name_union *ptr) | ||
| 60 | { | 101 | { |
| 61 | if (ptr->is_group) | 102 | if (ptr->is_group) |
| 62 | return tomoyo_path_matches_group(name, ptr->group, 1); | 103 | return tomoyo_path_matches_group(name, ptr->group); |
| 63 | return tomoyo_path_matches_pattern(name, ptr->filename); | 104 | if (tomoyo_path_matches_pattern(name, ptr->filename)) |
| 105 | return ptr->filename; | ||
| 106 | return NULL; | ||
| 64 | } | 107 | } |
| 65 | 108 | ||
| 66 | static bool tomoyo_compare_name_union_pattern(const struct tomoyo_path_info | 109 | void tomoyo_put_number_union(struct tomoyo_number_union *ptr) |
| 67 | *name, | ||
| 68 | const struct tomoyo_name_union | ||
| 69 | *ptr, const bool may_use_pattern) | ||
| 70 | { | 110 | { |
| 71 | if (ptr->is_group) | 111 | if (ptr && ptr->is_group) |
| 72 | return tomoyo_path_matches_group(name, ptr->group, | 112 | tomoyo_put_group(ptr->group); |
| 73 | may_use_pattern); | ||
| 74 | if (may_use_pattern || !ptr->filename->is_patterned) | ||
| 75 | return tomoyo_path_matches_pattern(name, ptr->filename); | ||
| 76 | return false; | ||
| 77 | } | 113 | } |
| 78 | 114 | ||
| 79 | /** | 115 | bool tomoyo_compare_number_union(const unsigned long value, |
| 80 | * tomoyo_path2keyword - Get the name of single path operation. | 116 | const struct tomoyo_number_union *ptr) |
| 81 | * | ||
| 82 | * @operation: Type of operation. | ||
| 83 | * | ||
| 84 | * Returns the name of single path operation. | ||
| 85 | */ | ||
| 86 | const char *tomoyo_path2keyword(const u8 operation) | ||
| 87 | { | 117 | { |
| 88 | return (operation < TOMOYO_MAX_PATH_OPERATION) | 118 | if (ptr->is_group) |
| 89 | ? tomoyo_path_keyword[operation] : NULL; | 119 | return tomoyo_number_matches_group(value, value, ptr->group); |
| 120 | return value >= ptr->values[0] && value <= ptr->values[1]; | ||
| 90 | } | 121 | } |
| 91 | 122 | ||
| 92 | /** | 123 | static void tomoyo_add_slash(struct tomoyo_path_info *buf) |
| 93 | * tomoyo_path22keyword - Get the name of double path operation. | ||
| 94 | * | ||
| 95 | * @operation: Type of operation. | ||
| 96 | * | ||
| 97 | * Returns the name of double path operation. | ||
| 98 | */ | ||
| 99 | const char *tomoyo_path22keyword(const u8 operation) | ||
| 100 | { | 124 | { |
| 101 | return (operation < TOMOYO_MAX_PATH2_OPERATION) | 125 | if (buf->is_dir) |
| 102 | ? tomoyo_path2_keyword[operation] : NULL; | 126 | return; |
| 127 | /* | ||
| 128 | * This is OK because tomoyo_encode() reserves space for appending "/". | ||
| 129 | */ | ||
| 130 | strcat((char *) buf->name, "/"); | ||
| 131 | tomoyo_fill_path_info(buf); | ||
| 103 | } | 132 | } |
| 104 | 133 | ||
| 105 | /** | 134 | /** |
| @@ -121,69 +150,134 @@ static bool tomoyo_strendswith(const char *name, const char *tail) | |||
| 121 | } | 150 | } |
| 122 | 151 | ||
| 123 | /** | 152 | /** |
| 124 | * tomoyo_get_path - Get realpath. | 153 | * tomoyo_get_realpath - Get realpath. |
| 125 | * | 154 | * |
| 155 | * @buf: Pointer to "struct tomoyo_path_info". | ||
| 126 | * @path: Pointer to "struct path". | 156 | * @path: Pointer to "struct path". |
| 127 | * | 157 | * |
| 128 | * Returns pointer to "struct tomoyo_path_info" on success, NULL otherwise. | 158 | * Returns true on success, false otherwise. |
| 129 | */ | 159 | */ |
| 130 | static struct tomoyo_path_info *tomoyo_get_path(struct path *path) | 160 | static bool tomoyo_get_realpath(struct tomoyo_path_info *buf, struct path *path) |
| 131 | { | 161 | { |
| 132 | int error; | 162 | buf->name = tomoyo_realpath_from_path(path); |
| 133 | struct tomoyo_path_info_with_data *buf = kzalloc(sizeof(*buf), | 163 | if (buf->name) { |
| 134 | GFP_NOFS); | 164 | tomoyo_fill_path_info(buf); |
| 135 | 165 | return true; | |
| 136 | if (!buf) | ||
| 137 | return NULL; | ||
| 138 | /* Reserve one byte for appending "/". */ | ||
| 139 | error = tomoyo_realpath_from_path2(path, buf->body, | ||
| 140 | sizeof(buf->body) - 2); | ||
| 141 | if (!error) { | ||
| 142 | buf->head.name = buf->body; | ||
| 143 | tomoyo_fill_path_info(&buf->head); | ||
| 144 | return &buf->head; | ||
| 145 | } | 166 | } |
| 146 | kfree(buf); | 167 | return false; |
| 147 | return NULL; | ||
| 148 | } | 168 | } |
| 149 | 169 | ||
| 150 | static int tomoyo_update_path2_acl(const u8 type, const char *filename1, | 170 | /** |
| 151 | const char *filename2, | 171 | * tomoyo_audit_path_log - Audit path request log. |
| 152 | struct tomoyo_domain_info *const domain, | 172 | * |
| 153 | const bool is_delete); | 173 | * @r: Pointer to "struct tomoyo_request_info". |
| 154 | static int tomoyo_update_path_acl(const u8 type, const char *filename, | ||
| 155 | struct tomoyo_domain_info *const domain, | ||
| 156 | const bool is_delete); | ||
| 157 | |||
| 158 | /* | ||
| 159 | * tomoyo_globally_readable_list is used for holding list of pathnames which | ||
| 160 | * are by default allowed to be open()ed for reading by any process. | ||
| 161 | * | 174 | * |
| 162 | * An entry is added by | 175 | * Returns 0 on success, negative value otherwise. |
| 176 | */ | ||
| 177 | static int tomoyo_audit_path_log(struct tomoyo_request_info *r) | ||
| 178 | { | ||
| 179 | const char *operation = tomoyo_path_keyword[r->param.path.operation]; | ||
| 180 | const struct tomoyo_path_info *filename = r->param.path.filename; | ||
| 181 | if (r->granted) | ||
| 182 | return 0; | ||
| 183 | tomoyo_warn_log(r, "%s %s", operation, filename->name); | ||
| 184 | return tomoyo_supervisor(r, "allow_%s %s\n", operation, | ||
| 185 | tomoyo_pattern(filename)); | ||
| 186 | } | ||
| 187 | |||
| 188 | /** | ||
| 189 | * tomoyo_audit_path2_log - Audit path/path request log. | ||
| 163 | * | 190 | * |
| 164 | * # echo 'allow_read /lib/libc-2.5.so' > \ | 191 | * @r: Pointer to "struct tomoyo_request_info". |
| 165 | * /sys/kernel/security/tomoyo/exception_policy | ||
| 166 | * | 192 | * |
| 167 | * and is deleted by | 193 | * Returns 0 on success, negative value otherwise. |
| 194 | */ | ||
| 195 | static int tomoyo_audit_path2_log(struct tomoyo_request_info *r) | ||
| 196 | { | ||
| 197 | const char *operation = tomoyo_path2_keyword[r->param.path2.operation]; | ||
| 198 | const struct tomoyo_path_info *filename1 = r->param.path2.filename1; | ||
| 199 | const struct tomoyo_path_info *filename2 = r->param.path2.filename2; | ||
| 200 | if (r->granted) | ||
| 201 | return 0; | ||
| 202 | tomoyo_warn_log(r, "%s %s %s", operation, filename1->name, | ||
| 203 | filename2->name); | ||
| 204 | return tomoyo_supervisor(r, "allow_%s %s %s\n", operation, | ||
| 205 | tomoyo_pattern(filename1), | ||
| 206 | tomoyo_pattern(filename2)); | ||
| 207 | } | ||
| 208 | |||
| 209 | /** | ||
| 210 | * tomoyo_audit_mkdev_log - Audit path/number/number/number request log. | ||
| 168 | * | 211 | * |
| 169 | * # echo 'delete allow_read /lib/libc-2.5.so' > \ | 212 | * @r: Pointer to "struct tomoyo_request_info". |
| 170 | * /sys/kernel/security/tomoyo/exception_policy | ||
| 171 | * | 213 | * |
| 172 | * and all entries are retrieved by | 214 | * Returns 0 on success, negative value otherwise. |
| 215 | */ | ||
| 216 | static int tomoyo_audit_mkdev_log(struct tomoyo_request_info *r) | ||
| 217 | { | ||
| 218 | const char *operation = tomoyo_mkdev_keyword[r->param.mkdev.operation]; | ||
| 219 | const struct tomoyo_path_info *filename = r->param.mkdev.filename; | ||
| 220 | const unsigned int major = r->param.mkdev.major; | ||
| 221 | const unsigned int minor = r->param.mkdev.minor; | ||
| 222 | const unsigned int mode = r->param.mkdev.mode; | ||
| 223 | if (r->granted) | ||
| 224 | return 0; | ||
| 225 | tomoyo_warn_log(r, "%s %s 0%o %u %u", operation, filename->name, mode, | ||
| 226 | major, minor); | ||
| 227 | return tomoyo_supervisor(r, "allow_%s %s 0%o %u %u\n", operation, | ||
| 228 | tomoyo_pattern(filename), mode, major, minor); | ||
| 229 | } | ||
| 230 | |||
| 231 | /** | ||
| 232 | * tomoyo_audit_path_number_log - Audit path/number request log. | ||
| 173 | * | 233 | * |
| 174 | * # grep ^allow_read /sys/kernel/security/tomoyo/exception_policy | 234 | * @r: Pointer to "struct tomoyo_request_info". |
| 235 | * @error: Error code. | ||
| 175 | * | 236 | * |
| 176 | * In the example above, any process is allowed to | 237 | * Returns 0 on success, negative value otherwise. |
| 177 | * open("/lib/libc-2.5.so", O_RDONLY). | ||
| 178 | * One exception is, if the domain which current process belongs to is marked | ||
| 179 | * as "ignore_global_allow_read", current process can't do so unless explicitly | ||
| 180 | * given "allow_read /lib/libc-2.5.so" to the domain which current process | ||
| 181 | * belongs to. | ||
| 182 | */ | 238 | */ |
| 183 | LIST_HEAD(tomoyo_globally_readable_list); | 239 | static int tomoyo_audit_path_number_log(struct tomoyo_request_info *r) |
| 240 | { | ||
| 241 | const u8 type = r->param.path_number.operation; | ||
| 242 | u8 radix; | ||
| 243 | const struct tomoyo_path_info *filename = r->param.path_number.filename; | ||
| 244 | const char *operation = tomoyo_path_number_keyword[type]; | ||
| 245 | char buffer[64]; | ||
| 246 | if (r->granted) | ||
| 247 | return 0; | ||
| 248 | switch (type) { | ||
| 249 | case TOMOYO_TYPE_CREATE: | ||
| 250 | case TOMOYO_TYPE_MKDIR: | ||
| 251 | case TOMOYO_TYPE_MKFIFO: | ||
| 252 | case TOMOYO_TYPE_MKSOCK: | ||
| 253 | case TOMOYO_TYPE_CHMOD: | ||
| 254 | radix = TOMOYO_VALUE_TYPE_OCTAL; | ||
| 255 | break; | ||
| 256 | case TOMOYO_TYPE_IOCTL: | ||
| 257 | radix = TOMOYO_VALUE_TYPE_HEXADECIMAL; | ||
| 258 | break; | ||
| 259 | default: | ||
| 260 | radix = TOMOYO_VALUE_TYPE_DECIMAL; | ||
| 261 | break; | ||
| 262 | } | ||
| 263 | tomoyo_print_ulong(buffer, sizeof(buffer), r->param.path_number.number, | ||
| 264 | radix); | ||
| 265 | tomoyo_warn_log(r, "%s %s %s", operation, filename->name, buffer); | ||
| 266 | return tomoyo_supervisor(r, "allow_%s %s %s\n", operation, | ||
| 267 | tomoyo_pattern(filename), buffer); | ||
| 268 | } | ||
| 269 | |||
| 270 | static bool tomoyo_same_globally_readable(const struct tomoyo_acl_head *a, | ||
| 271 | const struct tomoyo_acl_head *b) | ||
| 272 | { | ||
| 273 | return container_of(a, struct tomoyo_readable_file, | ||
| 274 | head)->filename == | ||
| 275 | container_of(b, struct tomoyo_readable_file, | ||
| 276 | head)->filename; | ||
| 277 | } | ||
| 184 | 278 | ||
| 185 | /** | 279 | /** |
| 186 | * tomoyo_update_globally_readable_entry - Update "struct tomoyo_globally_readable_file_entry" list. | 280 | * tomoyo_update_globally_readable_entry - Update "struct tomoyo_readable_file" list. |
| 187 | * | 281 | * |
| 188 | * @filename: Filename unconditionally permitted to open() for reading. | 282 | * @filename: Filename unconditionally permitted to open() for reading. |
| 189 | * @is_delete: True if it is a delete request. | 283 | * @is_delete: True if it is a delete request. |
| @@ -195,41 +289,24 @@ LIST_HEAD(tomoyo_globally_readable_list); | |||
| 195 | static int tomoyo_update_globally_readable_entry(const char *filename, | 289 | static int tomoyo_update_globally_readable_entry(const char *filename, |
| 196 | const bool is_delete) | 290 | const bool is_delete) |
| 197 | { | 291 | { |
| 198 | struct tomoyo_globally_readable_file_entry *ptr; | 292 | struct tomoyo_readable_file e = { }; |
| 199 | struct tomoyo_globally_readable_file_entry e = { }; | 293 | int error; |
| 200 | int error = is_delete ? -ENOENT : -ENOMEM; | ||
| 201 | 294 | ||
| 202 | if (!tomoyo_is_correct_path(filename, 1, 0, -1)) | 295 | if (!tomoyo_correct_word(filename)) |
| 203 | return -EINVAL; | 296 | return -EINVAL; |
| 204 | e.filename = tomoyo_get_name(filename); | 297 | e.filename = tomoyo_get_name(filename); |
| 205 | if (!e.filename) | 298 | if (!e.filename) |
| 206 | return -ENOMEM; | 299 | return -ENOMEM; |
| 207 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) | 300 | error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, |
| 208 | goto out; | 301 | &tomoyo_policy_list |
| 209 | list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, list) { | 302 | [TOMOYO_ID_GLOBALLY_READABLE], |
| 210 | if (ptr->filename != e.filename) | 303 | tomoyo_same_globally_readable); |
| 211 | continue; | ||
| 212 | ptr->is_deleted = is_delete; | ||
| 213 | error = 0; | ||
| 214 | break; | ||
| 215 | } | ||
| 216 | if (!is_delete && error) { | ||
| 217 | struct tomoyo_globally_readable_file_entry *entry = | ||
| 218 | tomoyo_commit_ok(&e, sizeof(e)); | ||
| 219 | if (entry) { | ||
| 220 | list_add_tail_rcu(&entry->list, | ||
| 221 | &tomoyo_globally_readable_list); | ||
| 222 | error = 0; | ||
| 223 | } | ||
| 224 | } | ||
| 225 | mutex_unlock(&tomoyo_policy_lock); | ||
| 226 | out: | ||
| 227 | tomoyo_put_name(e.filename); | 304 | tomoyo_put_name(e.filename); |
| 228 | return error; | 305 | return error; |
| 229 | } | 306 | } |
| 230 | 307 | ||
| 231 | /** | 308 | /** |
| 232 | * tomoyo_is_globally_readable_file - Check if the file is unconditionnaly permitted to be open()ed for reading. | 309 | * tomoyo_globally_readable_file - Check if the file is unconditionnaly permitted to be open()ed for reading. |
| 233 | * | 310 | * |
| 234 | * @filename: The filename to check. | 311 | * @filename: The filename to check. |
| 235 | * | 312 | * |
| @@ -237,14 +314,15 @@ static int tomoyo_update_globally_readable_entry(const char *filename, | |||
| 237 | * | 314 | * |
| 238 | * Caller holds tomoyo_read_lock(). | 315 | * Caller holds tomoyo_read_lock(). |
| 239 | */ | 316 | */ |
| 240 | static bool tomoyo_is_globally_readable_file(const struct tomoyo_path_info * | 317 | static bool tomoyo_globally_readable_file(const struct tomoyo_path_info * |
| 241 | filename) | 318 | filename) |
| 242 | { | 319 | { |
| 243 | struct tomoyo_globally_readable_file_entry *ptr; | 320 | struct tomoyo_readable_file *ptr; |
| 244 | bool found = false; | 321 | bool found = false; |
| 245 | 322 | ||
| 246 | list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, list) { | 323 | list_for_each_entry_rcu(ptr, &tomoyo_policy_list |
| 247 | if (!ptr->is_deleted && | 324 | [TOMOYO_ID_GLOBALLY_READABLE], head.list) { |
| 325 | if (!ptr->head.is_deleted && | ||
| 248 | tomoyo_path_matches_pattern(filename, ptr->filename)) { | 326 | tomoyo_path_matches_pattern(filename, ptr->filename)) { |
| 249 | found = true; | 327 | found = true; |
| 250 | break; | 328 | break; |
| @@ -254,7 +332,7 @@ static bool tomoyo_is_globally_readable_file(const struct tomoyo_path_info * | |||
| 254 | } | 332 | } |
| 255 | 333 | ||
| 256 | /** | 334 | /** |
| 257 | * tomoyo_write_globally_readable_policy - Write "struct tomoyo_globally_readable_file_entry" list. | 335 | * tomoyo_write_globally_readable - Write "struct tomoyo_readable_file" list. |
| 258 | * | 336 | * |
| 259 | * @data: String to parse. | 337 | * @data: String to parse. |
| 260 | * @is_delete: True if it is a delete request. | 338 | * @is_delete: True if it is a delete request. |
| @@ -263,74 +341,20 @@ static bool tomoyo_is_globally_readable_file(const struct tomoyo_path_info * | |||
| 263 | * | 341 | * |
| 264 | * Caller holds tomoyo_read_lock(). | 342 | * Caller holds tomoyo_read_lock(). |
| 265 | */ | 343 | */ |
| 266 | int tomoyo_write_globally_readable_policy(char *data, const bool is_delete) | 344 | int tomoyo_write_globally_readable(char *data, const bool is_delete) |
| 267 | { | 345 | { |
| 268 | return tomoyo_update_globally_readable_entry(data, is_delete); | 346 | return tomoyo_update_globally_readable_entry(data, is_delete); |
| 269 | } | 347 | } |
| 270 | 348 | ||
| 271 | /** | 349 | static bool tomoyo_same_pattern(const struct tomoyo_acl_head *a, |
| 272 | * tomoyo_read_globally_readable_policy - Read "struct tomoyo_globally_readable_file_entry" list. | 350 | const struct tomoyo_acl_head *b) |
| 273 | * | ||
| 274 | * @head: Pointer to "struct tomoyo_io_buffer". | ||
| 275 | * | ||
| 276 | * Returns true on success, false otherwise. | ||
| 277 | * | ||
| 278 | * Caller holds tomoyo_read_lock(). | ||
| 279 | */ | ||
| 280 | bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head) | ||
| 281 | { | 351 | { |
| 282 | struct list_head *pos; | 352 | return container_of(a, struct tomoyo_no_pattern, head)->pattern == |
| 283 | bool done = true; | 353 | container_of(b, struct tomoyo_no_pattern, head)->pattern; |
| 284 | |||
| 285 | list_for_each_cookie(pos, head->read_var2, | ||
| 286 | &tomoyo_globally_readable_list) { | ||
| 287 | struct tomoyo_globally_readable_file_entry *ptr; | ||
| 288 | ptr = list_entry(pos, | ||
| 289 | struct tomoyo_globally_readable_file_entry, | ||
| 290 | list); | ||
| 291 | if (ptr->is_deleted) | ||
| 292 | continue; | ||
| 293 | done = tomoyo_io_printf(head, TOMOYO_KEYWORD_ALLOW_READ "%s\n", | ||
| 294 | ptr->filename->name); | ||
| 295 | if (!done) | ||
| 296 | break; | ||
| 297 | } | ||
| 298 | return done; | ||
| 299 | } | 354 | } |
| 300 | 355 | ||
| 301 | /* tomoyo_pattern_list is used for holding list of pathnames which are used for | ||
| 302 | * converting pathnames to pathname patterns during learning mode. | ||
| 303 | * | ||
| 304 | * An entry is added by | ||
| 305 | * | ||
| 306 | * # echo 'file_pattern /proc/\$/mounts' > \ | ||
| 307 | * /sys/kernel/security/tomoyo/exception_policy | ||
| 308 | * | ||
| 309 | * and is deleted by | ||
| 310 | * | ||
| 311 | * # echo 'delete file_pattern /proc/\$/mounts' > \ | ||
| 312 | * /sys/kernel/security/tomoyo/exception_policy | ||
| 313 | * | ||
| 314 | * and all entries are retrieved by | ||
| 315 | * | ||
| 316 | * # grep ^file_pattern /sys/kernel/security/tomoyo/exception_policy | ||
| 317 | * | ||
| 318 | * In the example above, if a process which belongs to a domain which is in | ||
| 319 | * learning mode requested open("/proc/1/mounts", O_RDONLY), | ||
| 320 | * "allow_read /proc/\$/mounts" is automatically added to the domain which that | ||
| 321 | * process belongs to. | ||
| 322 | * | ||
| 323 | * It is not a desirable behavior that we have to use /proc/\$/ instead of | ||
| 324 | * /proc/self/ when current process needs to access only current process's | ||
| 325 | * information. As of now, LSM version of TOMOYO is using __d_path() for | ||
| 326 | * calculating pathname. Non LSM version of TOMOYO is using its own function | ||
| 327 | * which pretends as if /proc/self/ is not a symlink; so that we can forbid | ||
| 328 | * current process from accessing other process's information. | ||
| 329 | */ | ||
| 330 | LIST_HEAD(tomoyo_pattern_list); | ||
| 331 | |||
| 332 | /** | 356 | /** |
| 333 | * tomoyo_update_file_pattern_entry - Update "struct tomoyo_pattern_entry" list. | 357 | * tomoyo_update_file_pattern_entry - Update "struct tomoyo_no_pattern" list. |
| 334 | * | 358 | * |
| 335 | * @pattern: Pathname pattern. | 359 | * @pattern: Pathname pattern. |
| 336 | * @is_delete: True if it is a delete request. | 360 | * @is_delete: True if it is a delete request. |
| @@ -342,39 +366,23 @@ LIST_HEAD(tomoyo_pattern_list); | |||
| 342 | static int tomoyo_update_file_pattern_entry(const char *pattern, | 366 | static int tomoyo_update_file_pattern_entry(const char *pattern, |
| 343 | const bool is_delete) | 367 | const bool is_delete) |
| 344 | { | 368 | { |
| 345 | struct tomoyo_pattern_entry *ptr; | 369 | struct tomoyo_no_pattern e = { }; |
| 346 | struct tomoyo_pattern_entry e = { .pattern = tomoyo_get_name(pattern) }; | 370 | int error; |
| 347 | int error = is_delete ? -ENOENT : -ENOMEM; | ||
| 348 | 371 | ||
| 372 | if (!tomoyo_correct_word(pattern)) | ||
| 373 | return -EINVAL; | ||
| 374 | e.pattern = tomoyo_get_name(pattern); | ||
| 349 | if (!e.pattern) | 375 | if (!e.pattern) |
| 350 | return error; | 376 | return -ENOMEM; |
| 351 | if (!e.pattern->is_patterned) | 377 | error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, |
| 352 | goto out; | 378 | &tomoyo_policy_list[TOMOYO_ID_PATTERN], |
| 353 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) | 379 | tomoyo_same_pattern); |
| 354 | goto out; | ||
| 355 | list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) { | ||
| 356 | if (e.pattern != ptr->pattern) | ||
| 357 | continue; | ||
| 358 | ptr->is_deleted = is_delete; | ||
| 359 | error = 0; | ||
| 360 | break; | ||
| 361 | } | ||
| 362 | if (!is_delete && error) { | ||
| 363 | struct tomoyo_pattern_entry *entry = | ||
| 364 | tomoyo_commit_ok(&e, sizeof(e)); | ||
| 365 | if (entry) { | ||
| 366 | list_add_tail_rcu(&entry->list, &tomoyo_pattern_list); | ||
| 367 | error = 0; | ||
| 368 | } | ||
| 369 | } | ||
| 370 | mutex_unlock(&tomoyo_policy_lock); | ||
| 371 | out: | ||
| 372 | tomoyo_put_name(e.pattern); | 380 | tomoyo_put_name(e.pattern); |
| 373 | return error; | 381 | return error; |
| 374 | } | 382 | } |
| 375 | 383 | ||
| 376 | /** | 384 | /** |
| 377 | * tomoyo_get_file_pattern - Get patterned pathname. | 385 | * tomoyo_pattern - Get patterned pathname. |
| 378 | * | 386 | * |
| 379 | * @filename: The filename to find patterned pathname. | 387 | * @filename: The filename to find patterned pathname. |
| 380 | * | 388 | * |
| @@ -382,14 +390,14 @@ static int tomoyo_update_file_pattern_entry(const char *pattern, | |||
| 382 | * | 390 | * |
| 383 | * Caller holds tomoyo_read_lock(). | 391 | * Caller holds tomoyo_read_lock(). |
| 384 | */ | 392 | */ |
| 385 | static const struct tomoyo_path_info * | 393 | const char *tomoyo_pattern(const struct tomoyo_path_info *filename) |
| 386 | tomoyo_get_file_pattern(const struct tomoyo_path_info *filename) | ||
| 387 | { | 394 | { |
| 388 | struct tomoyo_pattern_entry *ptr; | 395 | struct tomoyo_no_pattern *ptr; |
| 389 | const struct tomoyo_path_info *pattern = NULL; | 396 | const struct tomoyo_path_info *pattern = NULL; |
| 390 | 397 | ||
| 391 | list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) { | 398 | list_for_each_entry_rcu(ptr, &tomoyo_policy_list[TOMOYO_ID_PATTERN], |
| 392 | if (ptr->is_deleted) | 399 | head.list) { |
| 400 | if (ptr->head.is_deleted) | ||
| 393 | continue; | 401 | continue; |
| 394 | if (!tomoyo_path_matches_pattern(filename, ptr->pattern)) | 402 | if (!tomoyo_path_matches_pattern(filename, ptr->pattern)) |
| 395 | continue; | 403 | continue; |
| @@ -403,11 +411,11 @@ tomoyo_get_file_pattern(const struct tomoyo_path_info *filename) | |||
| 403 | } | 411 | } |
| 404 | if (pattern) | 412 | if (pattern) |
| 405 | filename = pattern; | 413 | filename = pattern; |
| 406 | return filename; | 414 | return filename->name; |
| 407 | } | 415 | } |
| 408 | 416 | ||
| 409 | /** | 417 | /** |
| 410 | * tomoyo_write_pattern_policy - Write "struct tomoyo_pattern_entry" list. | 418 | * tomoyo_write_pattern - Write "struct tomoyo_no_pattern" list. |
| 411 | * | 419 | * |
| 412 | * @data: String to parse. | 420 | * @data: String to parse. |
| 413 | * @is_delete: True if it is a delete request. | 421 | * @is_delete: True if it is a delete request. |
| @@ -416,71 +424,21 @@ tomoyo_get_file_pattern(const struct tomoyo_path_info *filename) | |||
| 416 | * | 424 | * |
| 417 | * Caller holds tomoyo_read_lock(). | 425 | * Caller holds tomoyo_read_lock(). |
| 418 | */ | 426 | */ |
| 419 | int tomoyo_write_pattern_policy(char *data, const bool is_delete) | 427 | int tomoyo_write_pattern(char *data, const bool is_delete) |
| 420 | { | 428 | { |
| 421 | return tomoyo_update_file_pattern_entry(data, is_delete); | 429 | return tomoyo_update_file_pattern_entry(data, is_delete); |
| 422 | } | 430 | } |
| 423 | 431 | ||
| 424 | /** | 432 | static bool tomoyo_same_no_rewrite(const struct tomoyo_acl_head *a, |
| 425 | * tomoyo_read_file_pattern - Read "struct tomoyo_pattern_entry" list. | 433 | const struct tomoyo_acl_head *b) |
| 426 | * | ||
| 427 | * @head: Pointer to "struct tomoyo_io_buffer". | ||
| 428 | * | ||
| 429 | * Returns true on success, false otherwise. | ||
| 430 | * | ||
| 431 | * Caller holds tomoyo_read_lock(). | ||
| 432 | */ | ||
| 433 | bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head) | ||
| 434 | { | 434 | { |
| 435 | struct list_head *pos; | 435 | return container_of(a, struct tomoyo_no_rewrite, head)->pattern |
| 436 | bool done = true; | 436 | == container_of(b, struct tomoyo_no_rewrite, head) |
| 437 | 437 | ->pattern; | |
| 438 | list_for_each_cookie(pos, head->read_var2, &tomoyo_pattern_list) { | ||
| 439 | struct tomoyo_pattern_entry *ptr; | ||
| 440 | ptr = list_entry(pos, struct tomoyo_pattern_entry, list); | ||
| 441 | if (ptr->is_deleted) | ||
| 442 | continue; | ||
| 443 | done = tomoyo_io_printf(head, TOMOYO_KEYWORD_FILE_PATTERN | ||
| 444 | "%s\n", ptr->pattern->name); | ||
| 445 | if (!done) | ||
| 446 | break; | ||
| 447 | } | ||
| 448 | return done; | ||
| 449 | } | 438 | } |
| 450 | 439 | ||
| 451 | /* | ||
| 452 | * tomoyo_no_rewrite_list is used for holding list of pathnames which are by | ||
| 453 | * default forbidden to modify already written content of a file. | ||
| 454 | * | ||
| 455 | * An entry is added by | ||
| 456 | * | ||
| 457 | * # echo 'deny_rewrite /var/log/messages' > \ | ||
| 458 | * /sys/kernel/security/tomoyo/exception_policy | ||
| 459 | * | ||
| 460 | * and is deleted by | ||
| 461 | * | ||
| 462 | * # echo 'delete deny_rewrite /var/log/messages' > \ | ||
| 463 | * /sys/kernel/security/tomoyo/exception_policy | ||
| 464 | * | ||
| 465 | * and all entries are retrieved by | ||
| 466 | * | ||
| 467 | * # grep ^deny_rewrite /sys/kernel/security/tomoyo/exception_policy | ||
| 468 | * | ||
| 469 | * In the example above, if a process requested to rewrite /var/log/messages , | ||
| 470 | * the process can't rewrite unless the domain which that process belongs to | ||
| 471 | * has "allow_rewrite /var/log/messages" entry. | ||
| 472 | * | ||
| 473 | * It is not a desirable behavior that we have to add "\040(deleted)" suffix | ||
| 474 | * when we want to allow rewriting already unlink()ed file. As of now, | ||
| 475 | * LSM version of TOMOYO is using __d_path() for calculating pathname. | ||
| 476 | * Non LSM version of TOMOYO is using its own function which doesn't append | ||
| 477 | * " (deleted)" suffix if the file is already unlink()ed; so that we don't | ||
| 478 | * need to worry whether the file is already unlink()ed or not. | ||
| 479 | */ | ||
| 480 | LIST_HEAD(tomoyo_no_rewrite_list); | ||
| 481 | |||
| 482 | /** | 440 | /** |
| 483 | * tomoyo_update_no_rewrite_entry - Update "struct tomoyo_no_rewrite_entry" list. | 441 | * tomoyo_update_no_rewrite_entry - Update "struct tomoyo_no_rewrite" list. |
| 484 | * | 442 | * |
| 485 | * @pattern: Pathname pattern that are not rewritable by default. | 443 | * @pattern: Pathname pattern that are not rewritable by default. |
| 486 | * @is_delete: True if it is a delete request. | 444 | * @is_delete: True if it is a delete request. |
| @@ -492,41 +450,23 @@ LIST_HEAD(tomoyo_no_rewrite_list); | |||
| 492 | static int tomoyo_update_no_rewrite_entry(const char *pattern, | 450 | static int tomoyo_update_no_rewrite_entry(const char *pattern, |
| 493 | const bool is_delete) | 451 | const bool is_delete) |
| 494 | { | 452 | { |
| 495 | struct tomoyo_no_rewrite_entry *ptr; | 453 | struct tomoyo_no_rewrite e = { }; |
| 496 | struct tomoyo_no_rewrite_entry e = { }; | 454 | int error; |
| 497 | int error = is_delete ? -ENOENT : -ENOMEM; | ||
| 498 | 455 | ||
| 499 | if (!tomoyo_is_correct_path(pattern, 0, 0, 0)) | 456 | if (!tomoyo_correct_word(pattern)) |
| 500 | return -EINVAL; | 457 | return -EINVAL; |
| 501 | e.pattern = tomoyo_get_name(pattern); | 458 | e.pattern = tomoyo_get_name(pattern); |
| 502 | if (!e.pattern) | 459 | if (!e.pattern) |
| 503 | return error; | 460 | return -ENOMEM; |
| 504 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) | 461 | error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, |
| 505 | goto out; | 462 | &tomoyo_policy_list[TOMOYO_ID_NO_REWRITE], |
| 506 | list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, list) { | 463 | tomoyo_same_no_rewrite); |
| 507 | if (ptr->pattern != e.pattern) | ||
| 508 | continue; | ||
| 509 | ptr->is_deleted = is_delete; | ||
| 510 | error = 0; | ||
| 511 | break; | ||
| 512 | } | ||
| 513 | if (!is_delete && error) { | ||
| 514 | struct tomoyo_no_rewrite_entry *entry = | ||
| 515 | tomoyo_commit_ok(&e, sizeof(e)); | ||
| 516 | if (entry) { | ||
| 517 | list_add_tail_rcu(&entry->list, | ||
| 518 | &tomoyo_no_rewrite_list); | ||
| 519 | error = 0; | ||
| 520 | } | ||
| 521 | } | ||
| 522 | mutex_unlock(&tomoyo_policy_lock); | ||
| 523 | out: | ||
| 524 | tomoyo_put_name(e.pattern); | 464 | tomoyo_put_name(e.pattern); |
| 525 | return error; | 465 | return error; |
| 526 | } | 466 | } |
| 527 | 467 | ||
| 528 | /** | 468 | /** |
| 529 | * tomoyo_is_no_rewrite_file - Check if the given pathname is not permitted to be rewrited. | 469 | * tomoyo_no_rewrite_file - Check if the given pathname is not permitted to be rewrited. |
| 530 | * | 470 | * |
| 531 | * @filename: Filename to check. | 471 | * @filename: Filename to check. |
| 532 | * | 472 | * |
| @@ -535,13 +475,14 @@ static int tomoyo_update_no_rewrite_entry(const char *pattern, | |||
| 535 | * | 475 | * |
| 536 | * Caller holds tomoyo_read_lock(). | 476 | * Caller holds tomoyo_read_lock(). |
| 537 | */ | 477 | */ |
| 538 | static bool tomoyo_is_no_rewrite_file(const struct tomoyo_path_info *filename) | 478 | static bool tomoyo_no_rewrite_file(const struct tomoyo_path_info *filename) |
| 539 | { | 479 | { |
| 540 | struct tomoyo_no_rewrite_entry *ptr; | 480 | struct tomoyo_no_rewrite *ptr; |
| 541 | bool found = false; | 481 | bool found = false; |
| 542 | 482 | ||
| 543 | list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, list) { | 483 | list_for_each_entry_rcu(ptr, &tomoyo_policy_list[TOMOYO_ID_NO_REWRITE], |
| 544 | if (ptr->is_deleted) | 484 | head.list) { |
| 485 | if (ptr->head.is_deleted) | ||
| 545 | continue; | 486 | continue; |
| 546 | if (!tomoyo_path_matches_pattern(filename, ptr->pattern)) | 487 | if (!tomoyo_path_matches_pattern(filename, ptr->pattern)) |
| 547 | continue; | 488 | continue; |
| @@ -552,7 +493,7 @@ static bool tomoyo_is_no_rewrite_file(const struct tomoyo_path_info *filename) | |||
| 552 | } | 493 | } |
| 553 | 494 | ||
| 554 | /** | 495 | /** |
| 555 | * tomoyo_write_no_rewrite_policy - Write "struct tomoyo_no_rewrite_entry" list. | 496 | * tomoyo_write_no_rewrite - Write "struct tomoyo_no_rewrite" list. |
| 556 | * | 497 | * |
| 557 | * @data: String to parse. | 498 | * @data: String to parse. |
| 558 | * @is_delete: True if it is a delete request. | 499 | * @is_delete: True if it is a delete request. |
| @@ -561,214 +502,103 @@ static bool tomoyo_is_no_rewrite_file(const struct tomoyo_path_info *filename) | |||
| 561 | * | 502 | * |
| 562 | * Caller holds tomoyo_read_lock(). | 503 | * Caller holds tomoyo_read_lock(). |
| 563 | */ | 504 | */ |
| 564 | int tomoyo_write_no_rewrite_policy(char *data, const bool is_delete) | 505 | int tomoyo_write_no_rewrite(char *data, const bool is_delete) |
| 565 | { | 506 | { |
| 566 | return tomoyo_update_no_rewrite_entry(data, is_delete); | 507 | return tomoyo_update_no_rewrite_entry(data, is_delete); |
| 567 | } | 508 | } |
| 568 | 509 | ||
| 569 | /** | 510 | static bool tomoyo_check_path_acl(struct tomoyo_request_info *r, |
| 570 | * tomoyo_read_no_rewrite_policy - Read "struct tomoyo_no_rewrite_entry" list. | 511 | const struct tomoyo_acl_info *ptr) |
| 571 | * | ||
| 572 | * @head: Pointer to "struct tomoyo_io_buffer". | ||
| 573 | * | ||
| 574 | * Returns true on success, false otherwise. | ||
| 575 | * | ||
| 576 | * Caller holds tomoyo_read_lock(). | ||
| 577 | */ | ||
| 578 | bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head) | ||
| 579 | { | 512 | { |
| 580 | struct list_head *pos; | 513 | const struct tomoyo_path_acl *acl = container_of(ptr, typeof(*acl), |
| 581 | bool done = true; | 514 | head); |
| 582 | 515 | if (acl->perm & (1 << r->param.path.operation)) { | |
| 583 | list_for_each_cookie(pos, head->read_var2, &tomoyo_no_rewrite_list) { | 516 | r->param.path.matched_path = |
| 584 | struct tomoyo_no_rewrite_entry *ptr; | 517 | tomoyo_compare_name_union(r->param.path.filename, |
| 585 | ptr = list_entry(pos, struct tomoyo_no_rewrite_entry, list); | 518 | &acl->name); |
| 586 | if (ptr->is_deleted) | 519 | return r->param.path.matched_path != NULL; |
| 587 | continue; | ||
| 588 | done = tomoyo_io_printf(head, TOMOYO_KEYWORD_DENY_REWRITE | ||
| 589 | "%s\n", ptr->pattern->name); | ||
| 590 | if (!done) | ||
| 591 | break; | ||
| 592 | } | 520 | } |
| 593 | return done; | 521 | return false; |
| 594 | } | 522 | } |
| 595 | 523 | ||
| 596 | /** | 524 | static bool tomoyo_check_path_number_acl(struct tomoyo_request_info *r, |
| 597 | * tomoyo_update_file_acl - Update file's read/write/execute ACL. | 525 | const struct tomoyo_acl_info *ptr) |
| 598 | * | ||
| 599 | * @filename: Filename. | ||
| 600 | * @perm: Permission (between 1 to 7). | ||
| 601 | * @domain: Pointer to "struct tomoyo_domain_info". | ||
| 602 | * @is_delete: True if it is a delete request. | ||
| 603 | * | ||
| 604 | * Returns 0 on success, negative value otherwise. | ||
| 605 | * | ||
| 606 | * This is legacy support interface for older policy syntax. | ||
| 607 | * Current policy syntax uses "allow_read/write" instead of "6", | ||
| 608 | * "allow_read" instead of "4", "allow_write" instead of "2", | ||
| 609 | * "allow_execute" instead of "1". | ||
| 610 | * | ||
| 611 | * Caller holds tomoyo_read_lock(). | ||
| 612 | */ | ||
| 613 | static int tomoyo_update_file_acl(const char *filename, u8 perm, | ||
| 614 | struct tomoyo_domain_info * const domain, | ||
| 615 | const bool is_delete) | ||
| 616 | { | 526 | { |
| 617 | if (perm > 7 || !perm) { | 527 | const struct tomoyo_path_number_acl *acl = |
| 618 | printk(KERN_DEBUG "%s: Invalid permission '%d %s'\n", | 528 | container_of(ptr, typeof(*acl), head); |
| 619 | __func__, perm, filename); | 529 | return (acl->perm & (1 << r->param.path_number.operation)) && |
| 620 | return -EINVAL; | 530 | tomoyo_compare_number_union(r->param.path_number.number, |
| 621 | } | 531 | &acl->number) && |
| 622 | if (filename[0] != '@' && tomoyo_strendswith(filename, "/")) | 532 | tomoyo_compare_name_union(r->param.path_number.filename, |
| 623 | /* | 533 | &acl->name); |
| 624 | * Only 'allow_mkdir' and 'allow_rmdir' are valid for | ||
| 625 | * directory permissions. | ||
| 626 | */ | ||
| 627 | return 0; | ||
| 628 | if (perm & 4) | ||
| 629 | tomoyo_update_path_acl(TOMOYO_TYPE_READ, filename, domain, | ||
| 630 | is_delete); | ||
| 631 | if (perm & 2) | ||
| 632 | tomoyo_update_path_acl(TOMOYO_TYPE_WRITE, filename, domain, | ||
| 633 | is_delete); | ||
| 634 | if (perm & 1) | ||
| 635 | tomoyo_update_path_acl(TOMOYO_TYPE_EXECUTE, filename, domain, | ||
| 636 | is_delete); | ||
| 637 | return 0; | ||
| 638 | } | 534 | } |
| 639 | 535 | ||
| 640 | /** | 536 | static bool tomoyo_check_path2_acl(struct tomoyo_request_info *r, |
| 641 | * tomoyo_path_acl2 - Check permission for single path operation. | 537 | const struct tomoyo_acl_info *ptr) |
| 642 | * | ||
| 643 | * @domain: Pointer to "struct tomoyo_domain_info". | ||
| 644 | * @filename: Filename to check. | ||
| 645 | * @perm: Permission. | ||
| 646 | * @may_use_pattern: True if patterned ACL is permitted. | ||
| 647 | * | ||
| 648 | * Returns 0 on success, -EPERM otherwise. | ||
| 649 | * | ||
| 650 | * Caller holds tomoyo_read_lock(). | ||
| 651 | */ | ||
| 652 | static int tomoyo_path_acl2(const struct tomoyo_domain_info *domain, | ||
| 653 | const struct tomoyo_path_info *filename, | ||
| 654 | const u32 perm, const bool may_use_pattern) | ||
| 655 | { | 538 | { |
| 656 | struct tomoyo_acl_info *ptr; | 539 | const struct tomoyo_path2_acl *acl = |
| 657 | int error = -EPERM; | 540 | container_of(ptr, typeof(*acl), head); |
| 658 | 541 | return (acl->perm & (1 << r->param.path2.operation)) && | |
| 659 | list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { | 542 | tomoyo_compare_name_union(r->param.path2.filename1, &acl->name1) |
| 660 | struct tomoyo_path_acl *acl; | 543 | && tomoyo_compare_name_union(r->param.path2.filename2, |
| 661 | if (ptr->type != TOMOYO_TYPE_PATH_ACL) | 544 | &acl->name2); |
| 662 | continue; | ||
| 663 | acl = container_of(ptr, struct tomoyo_path_acl, head); | ||
| 664 | if (perm <= 0xFFFF) { | ||
| 665 | if (!(acl->perm & perm)) | ||
| 666 | continue; | ||
| 667 | } else { | ||
| 668 | if (!(acl->perm_high & (perm >> 16))) | ||
| 669 | continue; | ||
| 670 | } | ||
| 671 | if (!tomoyo_compare_name_union_pattern(filename, &acl->name, | ||
| 672 | may_use_pattern)) | ||
| 673 | continue; | ||
| 674 | error = 0; | ||
| 675 | break; | ||
| 676 | } | ||
| 677 | return error; | ||
| 678 | } | 545 | } |
| 679 | 546 | ||
| 680 | /** | 547 | static bool tomoyo_check_mkdev_acl(struct tomoyo_request_info *r, |
| 681 | * tomoyo_check_file_acl - Check permission for opening files. | 548 | const struct tomoyo_acl_info *ptr) |
| 682 | * | ||
| 683 | * @domain: Pointer to "struct tomoyo_domain_info". | ||
| 684 | * @filename: Filename to check. | ||
| 685 | * @operation: Mode ("read" or "write" or "read/write" or "execute"). | ||
| 686 | * | ||
| 687 | * Returns 0 on success, -EPERM otherwise. | ||
| 688 | * | ||
| 689 | * Caller holds tomoyo_read_lock(). | ||
| 690 | */ | ||
| 691 | static int tomoyo_check_file_acl(const struct tomoyo_domain_info *domain, | ||
| 692 | const struct tomoyo_path_info *filename, | ||
| 693 | const u8 operation) | ||
| 694 | { | 549 | { |
| 695 | u32 perm = 0; | 550 | const struct tomoyo_mkdev_acl *acl = |
| 696 | 551 | container_of(ptr, typeof(*acl), head); | |
| 697 | if (!tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE)) | 552 | return (acl->perm & (1 << r->param.mkdev.operation)) && |
| 698 | return 0; | 553 | tomoyo_compare_number_union(r->param.mkdev.mode, |
| 699 | if (operation == 6) | 554 | &acl->mode) && |
| 700 | perm = 1 << TOMOYO_TYPE_READ_WRITE; | 555 | tomoyo_compare_number_union(r->param.mkdev.major, |
| 701 | else if (operation == 4) | 556 | &acl->major) && |
| 702 | perm = 1 << TOMOYO_TYPE_READ; | 557 | tomoyo_compare_number_union(r->param.mkdev.minor, |
| 703 | else if (operation == 2) | 558 | &acl->minor) && |
| 704 | perm = 1 << TOMOYO_TYPE_WRITE; | 559 | tomoyo_compare_name_union(r->param.mkdev.filename, |
| 705 | else if (operation == 1) | 560 | &acl->name); |
| 706 | perm = 1 << TOMOYO_TYPE_EXECUTE; | ||
| 707 | else | ||
| 708 | BUG(); | ||
| 709 | return tomoyo_path_acl2(domain, filename, perm, operation != 1); | ||
| 710 | } | 561 | } |
| 711 | 562 | ||
| 712 | /** | 563 | static bool tomoyo_same_path_acl(const struct tomoyo_acl_info *a, |
| 713 | * tomoyo_check_file_perm2 - Check permission for opening files. | 564 | const struct tomoyo_acl_info *b) |
| 714 | * | ||
| 715 | * @domain: Pointer to "struct tomoyo_domain_info". | ||
| 716 | * @filename: Filename to check. | ||
| 717 | * @perm: Mode ("read" or "write" or "read/write" or "execute"). | ||
| 718 | * @operation: Operation name passed used for verbose mode. | ||
| 719 | * @mode: Access control mode. | ||
| 720 | * | ||
| 721 | * Returns 0 on success, negative value otherwise. | ||
| 722 | * | ||
| 723 | * Caller holds tomoyo_read_lock(). | ||
| 724 | */ | ||
| 725 | static int tomoyo_check_file_perm2(struct tomoyo_domain_info * const domain, | ||
| 726 | const struct tomoyo_path_info *filename, | ||
| 727 | const u8 perm, const char *operation, | ||
| 728 | const u8 mode) | ||
| 729 | { | 565 | { |
| 730 | const bool is_enforce = (mode == 3); | 566 | const struct tomoyo_path_acl *p1 = container_of(a, typeof(*p1), head); |
| 731 | const char *msg = "<unknown>"; | 567 | const struct tomoyo_path_acl *p2 = container_of(b, typeof(*p2), head); |
| 732 | int error = 0; | 568 | return tomoyo_same_acl_head(&p1->head, &p2->head) && |
| 569 | tomoyo_same_name_union(&p1->name, &p2->name); | ||
| 570 | } | ||
| 733 | 571 | ||
| 734 | if (!filename) | 572 | static bool tomoyo_merge_path_acl(struct tomoyo_acl_info *a, |
| 735 | return 0; | 573 | struct tomoyo_acl_info *b, |
| 736 | error = tomoyo_check_file_acl(domain, filename, perm); | 574 | const bool is_delete) |
| 737 | if (error && perm == 4 && !domain->ignore_global_allow_read | 575 | { |
| 738 | && tomoyo_is_globally_readable_file(filename)) | 576 | u16 * const a_perm = &container_of(a, struct tomoyo_path_acl, head) |
| 739 | error = 0; | 577 | ->perm; |
| 740 | if (perm == 6) | 578 | u16 perm = *a_perm; |
| 741 | msg = tomoyo_path2keyword(TOMOYO_TYPE_READ_WRITE); | 579 | const u16 b_perm = container_of(b, struct tomoyo_path_acl, head)->perm; |
| 742 | else if (perm == 4) | 580 | if (is_delete) { |
| 743 | msg = tomoyo_path2keyword(TOMOYO_TYPE_READ); | 581 | perm &= ~b_perm; |
| 744 | else if (perm == 2) | 582 | if ((perm & TOMOYO_RW_MASK) != TOMOYO_RW_MASK) |
| 745 | msg = tomoyo_path2keyword(TOMOYO_TYPE_WRITE); | 583 | perm &= ~(1 << TOMOYO_TYPE_READ_WRITE); |
| 746 | else if (perm == 1) | 584 | else if (!(perm & (1 << TOMOYO_TYPE_READ_WRITE))) |
| 747 | msg = tomoyo_path2keyword(TOMOYO_TYPE_EXECUTE); | 585 | perm &= ~TOMOYO_RW_MASK; |
| 748 | else | 586 | } else { |
| 749 | BUG(); | 587 | perm |= b_perm; |
| 750 | if (!error) | 588 | if ((perm & TOMOYO_RW_MASK) == TOMOYO_RW_MASK) |
| 751 | return 0; | 589 | perm |= (1 << TOMOYO_TYPE_READ_WRITE); |
| 752 | if (tomoyo_verbose_mode(domain)) | 590 | else if (perm & (1 << TOMOYO_TYPE_READ_WRITE)) |
| 753 | printk(KERN_WARNING "TOMOYO-%s: Access '%s(%s) %s' denied " | 591 | perm |= TOMOYO_RW_MASK; |
| 754 | "for %s\n", tomoyo_get_msg(is_enforce), msg, operation, | ||
| 755 | filename->name, tomoyo_get_last_name(domain)); | ||
| 756 | if (is_enforce) | ||
| 757 | return error; | ||
| 758 | if (mode == 1 && tomoyo_domain_quota_is_ok(domain)) { | ||
| 759 | /* Don't use patterns for execute permission. */ | ||
| 760 | const struct tomoyo_path_info *patterned_file = (perm != 1) ? | ||
| 761 | tomoyo_get_file_pattern(filename) : filename; | ||
| 762 | tomoyo_update_file_acl(patterned_file->name, perm, | ||
| 763 | domain, false); | ||
| 764 | } | 592 | } |
| 765 | return 0; | 593 | *a_perm = perm; |
| 594 | return !perm; | ||
| 766 | } | 595 | } |
| 767 | 596 | ||
| 768 | /** | 597 | /** |
| 769 | * tomoyo_write_file_policy - Update file related list. | 598 | * tomoyo_update_path_acl - Update "struct tomoyo_path_acl" list. |
| 770 | * | 599 | * |
| 771 | * @data: String to parse. | 600 | * @type: Type of operation. |
| 601 | * @filename: Filename. | ||
| 772 | * @domain: Pointer to "struct tomoyo_domain_info". | 602 | * @domain: Pointer to "struct tomoyo_domain_info". |
| 773 | * @is_delete: True if it is a delete request. | 603 | * @is_delete: True if it is a delete request. |
| 774 | * | 604 | * |
| @@ -776,48 +606,65 @@ static int tomoyo_check_file_perm2(struct tomoyo_domain_info * const domain, | |||
| 776 | * | 606 | * |
| 777 | * Caller holds tomoyo_read_lock(). | 607 | * Caller holds tomoyo_read_lock(). |
| 778 | */ | 608 | */ |
| 779 | int tomoyo_write_file_policy(char *data, struct tomoyo_domain_info *domain, | 609 | static int tomoyo_update_path_acl(const u8 type, const char *filename, |
| 780 | const bool is_delete) | 610 | struct tomoyo_domain_info * const domain, |
| 611 | const bool is_delete) | ||
| 781 | { | 612 | { |
| 782 | char *filename = strchr(data, ' '); | 613 | struct tomoyo_path_acl e = { |
| 783 | char *filename2; | 614 | .head.type = TOMOYO_TYPE_PATH_ACL, |
| 784 | unsigned int perm; | 615 | .perm = 1 << type |
| 785 | u8 type; | 616 | }; |
| 786 | 617 | int error; | |
| 787 | if (!filename) | 618 | if (e.perm == (1 << TOMOYO_TYPE_READ_WRITE)) |
| 619 | e.perm |= TOMOYO_RW_MASK; | ||
| 620 | if (!tomoyo_parse_name_union(filename, &e.name)) | ||
| 788 | return -EINVAL; | 621 | return -EINVAL; |
| 789 | *filename++ = '\0'; | 622 | error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain, |
| 790 | if (sscanf(data, "%u", &perm) == 1) | 623 | tomoyo_same_path_acl, |
| 791 | return tomoyo_update_file_acl(filename, (u8) perm, domain, | 624 | tomoyo_merge_path_acl); |
| 792 | is_delete); | 625 | tomoyo_put_name_union(&e.name); |
| 793 | if (strncmp(data, "allow_", 6)) | 626 | return error; |
| 794 | goto out; | 627 | } |
| 795 | data += 6; | 628 | |
| 796 | for (type = 0; type < TOMOYO_MAX_PATH_OPERATION; type++) { | 629 | static bool tomoyo_same_mkdev_acl(const struct tomoyo_acl_info *a, |
| 797 | if (strcmp(data, tomoyo_path_keyword[type])) | 630 | const struct tomoyo_acl_info *b) |
| 798 | continue; | 631 | { |
| 799 | return tomoyo_update_path_acl(type, filename, domain, | 632 | const struct tomoyo_mkdev_acl *p1 = container_of(a, typeof(*p1), |
| 800 | is_delete); | 633 | head); |
| 801 | } | 634 | const struct tomoyo_mkdev_acl *p2 = container_of(b, typeof(*p2), |
| 802 | filename2 = strchr(filename, ' '); | 635 | head); |
| 803 | if (!filename2) | 636 | return tomoyo_same_acl_head(&p1->head, &p2->head) |
| 804 | goto out; | 637 | && tomoyo_same_name_union(&p1->name, &p2->name) |
| 805 | *filename2++ = '\0'; | 638 | && tomoyo_same_number_union(&p1->mode, &p2->mode) |
| 806 | for (type = 0; type < TOMOYO_MAX_PATH2_OPERATION; type++) { | 639 | && tomoyo_same_number_union(&p1->major, &p2->major) |
| 807 | if (strcmp(data, tomoyo_path2_keyword[type])) | 640 | && tomoyo_same_number_union(&p1->minor, &p2->minor); |
| 808 | continue; | 641 | } |
| 809 | return tomoyo_update_path2_acl(type, filename, filename2, | 642 | |
| 810 | domain, is_delete); | 643 | static bool tomoyo_merge_mkdev_acl(struct tomoyo_acl_info *a, |
| 811 | } | 644 | struct tomoyo_acl_info *b, |
| 812 | out: | 645 | const bool is_delete) |
| 813 | return -EINVAL; | 646 | { |
| 647 | u8 *const a_perm = &container_of(a, struct tomoyo_mkdev_acl, | ||
| 648 | head)->perm; | ||
| 649 | u8 perm = *a_perm; | ||
| 650 | const u8 b_perm = container_of(b, struct tomoyo_mkdev_acl, head) | ||
| 651 | ->perm; | ||
| 652 | if (is_delete) | ||
| 653 | perm &= ~b_perm; | ||
| 654 | else | ||
| 655 | perm |= b_perm; | ||
| 656 | *a_perm = perm; | ||
| 657 | return !perm; | ||
| 814 | } | 658 | } |
| 815 | 659 | ||
| 816 | /** | 660 | /** |
| 817 | * tomoyo_update_path_acl - Update "struct tomoyo_path_acl" list. | 661 | * tomoyo_update_mkdev_acl - Update "struct tomoyo_mkdev_acl" list. |
| 818 | * | 662 | * |
| 819 | * @type: Type of operation. | 663 | * @type: Type of operation. |
| 820 | * @filename: Filename. | 664 | * @filename: Filename. |
| 665 | * @mode: Create mode. | ||
| 666 | * @major: Device major number. | ||
| 667 | * @minor: Device minor number. | ||
| 821 | * @domain: Pointer to "struct tomoyo_domain_info". | 668 | * @domain: Pointer to "struct tomoyo_domain_info". |
| 822 | * @is_delete: True if it is a delete request. | 669 | * @is_delete: True if it is a delete request. |
| 823 | * | 670 | * |
| @@ -825,71 +672,58 @@ int tomoyo_write_file_policy(char *data, struct tomoyo_domain_info *domain, | |||
| 825 | * | 672 | * |
| 826 | * Caller holds tomoyo_read_lock(). | 673 | * Caller holds tomoyo_read_lock(). |
| 827 | */ | 674 | */ |
| 828 | static int tomoyo_update_path_acl(const u8 type, const char *filename, | 675 | static int tomoyo_update_mkdev_acl(const u8 type, const char *filename, |
| 829 | struct tomoyo_domain_info *const domain, | 676 | char *mode, char *major, char *minor, |
| 830 | const bool is_delete) | 677 | struct tomoyo_domain_info * const |
| 678 | domain, const bool is_delete) | ||
| 831 | { | 679 | { |
| 832 | static const u32 tomoyo_rw_mask = | 680 | struct tomoyo_mkdev_acl e = { |
| 833 | (1 << TOMOYO_TYPE_READ) | (1 << TOMOYO_TYPE_WRITE); | 681 | .head.type = TOMOYO_TYPE_MKDEV_ACL, |
| 834 | const u32 perm = 1 << type; | 682 | .perm = 1 << type |
| 835 | struct tomoyo_acl_info *ptr; | ||
| 836 | struct tomoyo_path_acl e = { | ||
| 837 | .head.type = TOMOYO_TYPE_PATH_ACL, | ||
| 838 | .perm_high = perm >> 16, | ||
| 839 | .perm = perm | ||
| 840 | }; | 683 | }; |
| 841 | int error = is_delete ? -ENOENT : -ENOMEM; | 684 | int error = is_delete ? -ENOENT : -ENOMEM; |
| 842 | 685 | if (!tomoyo_parse_name_union(filename, &e.name) || | |
| 843 | if (type == TOMOYO_TYPE_READ_WRITE) | 686 | !tomoyo_parse_number_union(mode, &e.mode) || |
| 844 | e.perm |= tomoyo_rw_mask; | 687 | !tomoyo_parse_number_union(major, &e.major) || |
| 845 | if (!domain) | 688 | !tomoyo_parse_number_union(minor, &e.minor)) |
| 846 | return -EINVAL; | ||
| 847 | if (!tomoyo_parse_name_union(filename, &e.name)) | ||
| 848 | return -EINVAL; | ||
| 849 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) | ||
| 850 | goto out; | 689 | goto out; |
| 851 | list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { | 690 | error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain, |
| 852 | struct tomoyo_path_acl *acl = | 691 | tomoyo_same_mkdev_acl, |
| 853 | container_of(ptr, struct tomoyo_path_acl, head); | 692 | tomoyo_merge_mkdev_acl); |
| 854 | if (!tomoyo_is_same_path_acl(acl, &e)) | ||
| 855 | continue; | ||
| 856 | if (is_delete) { | ||
| 857 | if (perm <= 0xFFFF) | ||
| 858 | acl->perm &= ~perm; | ||
| 859 | else | ||
| 860 | acl->perm_high &= ~(perm >> 16); | ||
| 861 | if ((acl->perm & tomoyo_rw_mask) != tomoyo_rw_mask) | ||
| 862 | acl->perm &= ~(1 << TOMOYO_TYPE_READ_WRITE); | ||
| 863 | else if (!(acl->perm & (1 << TOMOYO_TYPE_READ_WRITE))) | ||
| 864 | acl->perm &= ~tomoyo_rw_mask; | ||
| 865 | } else { | ||
| 866 | if (perm <= 0xFFFF) | ||
| 867 | acl->perm |= perm; | ||
| 868 | else | ||
| 869 | acl->perm_high |= (perm >> 16); | ||
| 870 | if ((acl->perm & tomoyo_rw_mask) == tomoyo_rw_mask) | ||
| 871 | acl->perm |= 1 << TOMOYO_TYPE_READ_WRITE; | ||
| 872 | else if (acl->perm & (1 << TOMOYO_TYPE_READ_WRITE)) | ||
| 873 | acl->perm |= tomoyo_rw_mask; | ||
| 874 | } | ||
| 875 | error = 0; | ||
| 876 | break; | ||
| 877 | } | ||
| 878 | if (!is_delete && error) { | ||
| 879 | struct tomoyo_path_acl *entry = | ||
| 880 | tomoyo_commit_ok(&e, sizeof(e)); | ||
| 881 | if (entry) { | ||
| 882 | list_add_tail_rcu(&entry->head.list, | ||
| 883 | &domain->acl_info_list); | ||
| 884 | error = 0; | ||
| 885 | } | ||
| 886 | } | ||
| 887 | mutex_unlock(&tomoyo_policy_lock); | ||
| 888 | out: | 693 | out: |
| 889 | tomoyo_put_name_union(&e.name); | 694 | tomoyo_put_name_union(&e.name); |
| 695 | tomoyo_put_number_union(&e.mode); | ||
| 696 | tomoyo_put_number_union(&e.major); | ||
| 697 | tomoyo_put_number_union(&e.minor); | ||
| 890 | return error; | 698 | return error; |
| 891 | } | 699 | } |
| 892 | 700 | ||
| 701 | static bool tomoyo_same_path2_acl(const struct tomoyo_acl_info *a, | ||
| 702 | const struct tomoyo_acl_info *b) | ||
| 703 | { | ||
| 704 | const struct tomoyo_path2_acl *p1 = container_of(a, typeof(*p1), head); | ||
| 705 | const struct tomoyo_path2_acl *p2 = container_of(b, typeof(*p2), head); | ||
| 706 | return tomoyo_same_acl_head(&p1->head, &p2->head) | ||
| 707 | && tomoyo_same_name_union(&p1->name1, &p2->name1) | ||
| 708 | && tomoyo_same_name_union(&p1->name2, &p2->name2); | ||
| 709 | } | ||
| 710 | |||
| 711 | static bool tomoyo_merge_path2_acl(struct tomoyo_acl_info *a, | ||
| 712 | struct tomoyo_acl_info *b, | ||
| 713 | const bool is_delete) | ||
| 714 | { | ||
| 715 | u8 * const a_perm = &container_of(a, struct tomoyo_path2_acl, head) | ||
| 716 | ->perm; | ||
| 717 | u8 perm = *a_perm; | ||
| 718 | const u8 b_perm = container_of(b, struct tomoyo_path2_acl, head)->perm; | ||
| 719 | if (is_delete) | ||
| 720 | perm &= ~b_perm; | ||
| 721 | else | ||
| 722 | perm |= b_perm; | ||
| 723 | *a_perm = perm; | ||
| 724 | return !perm; | ||
| 725 | } | ||
| 726 | |||
| 893 | /** | 727 | /** |
| 894 | * tomoyo_update_path2_acl - Update "struct tomoyo_path2_acl" list. | 728 | * tomoyo_update_path2_acl - Update "struct tomoyo_path2_acl" list. |
| 895 | * | 729 | * |
| @@ -905,46 +739,20 @@ static int tomoyo_update_path_acl(const u8 type, const char *filename, | |||
| 905 | */ | 739 | */ |
| 906 | static int tomoyo_update_path2_acl(const u8 type, const char *filename1, | 740 | static int tomoyo_update_path2_acl(const u8 type, const char *filename1, |
| 907 | const char *filename2, | 741 | const char *filename2, |
| 908 | struct tomoyo_domain_info *const domain, | 742 | struct tomoyo_domain_info * const domain, |
| 909 | const bool is_delete) | 743 | const bool is_delete) |
| 910 | { | 744 | { |
| 911 | const u8 perm = 1 << type; | ||
| 912 | struct tomoyo_path2_acl e = { | 745 | struct tomoyo_path2_acl e = { |
| 913 | .head.type = TOMOYO_TYPE_PATH2_ACL, | 746 | .head.type = TOMOYO_TYPE_PATH2_ACL, |
| 914 | .perm = perm | 747 | .perm = 1 << type |
| 915 | }; | 748 | }; |
| 916 | struct tomoyo_acl_info *ptr; | ||
| 917 | int error = is_delete ? -ENOENT : -ENOMEM; | 749 | int error = is_delete ? -ENOENT : -ENOMEM; |
| 918 | |||
| 919 | if (!domain) | ||
| 920 | return -EINVAL; | ||
| 921 | if (!tomoyo_parse_name_union(filename1, &e.name1) || | 750 | if (!tomoyo_parse_name_union(filename1, &e.name1) || |
| 922 | !tomoyo_parse_name_union(filename2, &e.name2)) | 751 | !tomoyo_parse_name_union(filename2, &e.name2)) |
| 923 | goto out; | 752 | goto out; |
| 924 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) | 753 | error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain, |
| 925 | goto out; | 754 | tomoyo_same_path2_acl, |
| 926 | list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { | 755 | tomoyo_merge_path2_acl); |
| 927 | struct tomoyo_path2_acl *acl = | ||
| 928 | container_of(ptr, struct tomoyo_path2_acl, head); | ||
| 929 | if (!tomoyo_is_same_path2_acl(acl, &e)) | ||
| 930 | continue; | ||
| 931 | if (is_delete) | ||
| 932 | acl->perm &= ~perm; | ||
| 933 | else | ||
| 934 | acl->perm |= perm; | ||
| 935 | error = 0; | ||
| 936 | break; | ||
| 937 | } | ||
| 938 | if (!is_delete && error) { | ||
| 939 | struct tomoyo_path2_acl *entry = | ||
| 940 | tomoyo_commit_ok(&e, sizeof(e)); | ||
| 941 | if (entry) { | ||
| 942 | list_add_tail_rcu(&entry->head.list, | ||
| 943 | &domain->acl_info_list); | ||
| 944 | error = 0; | ||
| 945 | } | ||
| 946 | } | ||
| 947 | mutex_unlock(&tomoyo_policy_lock); | ||
| 948 | out: | 756 | out: |
| 949 | tomoyo_put_name_union(&e.name1); | 757 | tomoyo_put_name_union(&e.name1); |
| 950 | tomoyo_put_name_union(&e.name2); | 758 | tomoyo_put_name_union(&e.name2); |
| @@ -952,134 +760,158 @@ static int tomoyo_update_path2_acl(const u8 type, const char *filename1, | |||
| 952 | } | 760 | } |
| 953 | 761 | ||
| 954 | /** | 762 | /** |
| 955 | * tomoyo_path_acl - Check permission for single path operation. | 763 | * tomoyo_path_permission - Check permission for single path operation. |
| 956 | * | ||
| 957 | * @domain: Pointer to "struct tomoyo_domain_info". | ||
| 958 | * @type: Type of operation. | ||
| 959 | * @filename: Filename to check. | ||
| 960 | * | ||
| 961 | * Returns 0 on success, negative value otherwise. | ||
| 962 | * | ||
| 963 | * Caller holds tomoyo_read_lock(). | ||
| 964 | */ | ||
| 965 | static int tomoyo_path_acl(struct tomoyo_domain_info *domain, const u8 type, | ||
| 966 | const struct tomoyo_path_info *filename) | ||
| 967 | { | ||
| 968 | if (!tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE)) | ||
| 969 | return 0; | ||
| 970 | return tomoyo_path_acl2(domain, filename, 1 << type, 1); | ||
| 971 | } | ||
| 972 | |||
| 973 | /** | ||
| 974 | * tomoyo_path2_acl - Check permission for double path operation. | ||
| 975 | * | 764 | * |
| 976 | * @domain: Pointer to "struct tomoyo_domain_info". | 765 | * @r: Pointer to "struct tomoyo_request_info". |
| 977 | * @type: Type of operation. | ||
| 978 | * @filename1: First filename to check. | ||
| 979 | * @filename2: Second filename to check. | ||
| 980 | * | ||
| 981 | * Returns 0 on success, -EPERM otherwise. | ||
| 982 | * | ||
| 983 | * Caller holds tomoyo_read_lock(). | ||
| 984 | */ | ||
| 985 | static int tomoyo_path2_acl(const struct tomoyo_domain_info *domain, | ||
| 986 | const u8 type, | ||
| 987 | const struct tomoyo_path_info *filename1, | ||
| 988 | const struct tomoyo_path_info *filename2) | ||
| 989 | { | ||
| 990 | struct tomoyo_acl_info *ptr; | ||
| 991 | const u8 perm = 1 << type; | ||
| 992 | int error = -EPERM; | ||
| 993 | |||
| 994 | if (!tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE)) | ||
| 995 | return 0; | ||
| 996 | list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { | ||
| 997 | struct tomoyo_path2_acl *acl; | ||
| 998 | if (ptr->type != TOMOYO_TYPE_PATH2_ACL) | ||
| 999 | continue; | ||
| 1000 | acl = container_of(ptr, struct tomoyo_path2_acl, head); | ||
| 1001 | if (!(acl->perm & perm)) | ||
| 1002 | continue; | ||
| 1003 | if (!tomoyo_compare_name_union(filename1, &acl->name1)) | ||
| 1004 | continue; | ||
| 1005 | if (!tomoyo_compare_name_union(filename2, &acl->name2)) | ||
| 1006 | continue; | ||
| 1007 | error = 0; | ||
| 1008 | break; | ||
| 1009 | } | ||
| 1010 | return error; | ||
| 1011 | } | ||
| 1012 | |||
| 1013 | /** | ||
| 1014 | * tomoyo_path_permission2 - Check permission for single path operation. | ||
| 1015 | * | ||
| 1016 | * @domain: Pointer to "struct tomoyo_domain_info". | ||
| 1017 | * @operation: Type of operation. | 766 | * @operation: Type of operation. |
| 1018 | * @filename: Filename to check. | 767 | * @filename: Filename to check. |
| 1019 | * @mode: Access control mode. | ||
| 1020 | * | 768 | * |
| 1021 | * Returns 0 on success, negative value otherwise. | 769 | * Returns 0 on success, negative value otherwise. |
| 1022 | * | 770 | * |
| 1023 | * Caller holds tomoyo_read_lock(). | 771 | * Caller holds tomoyo_read_lock(). |
| 1024 | */ | 772 | */ |
| 1025 | static int tomoyo_path_permission2(struct tomoyo_domain_info *const domain, | 773 | int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation, |
| 1026 | u8 operation, | 774 | const struct tomoyo_path_info *filename) |
| 1027 | const struct tomoyo_path_info *filename, | ||
| 1028 | const u8 mode) | ||
| 1029 | { | 775 | { |
| 1030 | const char *msg; | ||
| 1031 | int error; | 776 | int error; |
| 1032 | const bool is_enforce = (mode == 3); | ||
| 1033 | 777 | ||
| 1034 | if (!mode) | ||
| 1035 | return 0; | ||
| 1036 | next: | 778 | next: |
| 1037 | error = tomoyo_path_acl(domain, operation, filename); | 779 | r->type = tomoyo_p2mac[operation]; |
| 1038 | msg = tomoyo_path2keyword(operation); | 780 | r->mode = tomoyo_get_mode(r->profile, r->type); |
| 1039 | if (!error) | 781 | if (r->mode == TOMOYO_CONFIG_DISABLED) |
| 1040 | goto ok; | 782 | return 0; |
| 1041 | if (tomoyo_verbose_mode(domain)) | 783 | r->param_type = TOMOYO_TYPE_PATH_ACL; |
| 1042 | printk(KERN_WARNING "TOMOYO-%s: Access '%s %s' denied for %s\n", | 784 | r->param.path.filename = filename; |
| 1043 | tomoyo_get_msg(is_enforce), msg, filename->name, | 785 | r->param.path.operation = operation; |
| 1044 | tomoyo_get_last_name(domain)); | 786 | do { |
| 1045 | if (mode == 1 && tomoyo_domain_quota_is_ok(domain)) { | 787 | tomoyo_check_acl(r, tomoyo_check_path_acl); |
| 1046 | const char *name = tomoyo_get_file_pattern(filename)->name; | 788 | if (!r->granted && operation == TOMOYO_TYPE_READ && |
| 1047 | tomoyo_update_path_acl(operation, name, domain, false); | 789 | !r->domain->ignore_global_allow_read && |
| 1048 | } | 790 | tomoyo_globally_readable_file(filename)) |
| 1049 | if (!is_enforce) | 791 | r->granted = true; |
| 1050 | error = 0; | 792 | error = tomoyo_audit_path_log(r); |
| 1051 | ok: | 793 | /* |
| 794 | * Do not retry for execute request, for alias may have | ||
| 795 | * changed. | ||
| 796 | */ | ||
| 797 | } while (error == TOMOYO_RETRY_REQUEST && | ||
| 798 | operation != TOMOYO_TYPE_EXECUTE); | ||
| 1052 | /* | 799 | /* |
| 1053 | * Since "allow_truncate" doesn't imply "allow_rewrite" permission, | 800 | * Since "allow_truncate" doesn't imply "allow_rewrite" permission, |
| 1054 | * we need to check "allow_rewrite" permission if the filename is | 801 | * we need to check "allow_rewrite" permission if the filename is |
| 1055 | * specified by "deny_rewrite" keyword. | 802 | * specified by "deny_rewrite" keyword. |
| 1056 | */ | 803 | */ |
| 1057 | if (!error && operation == TOMOYO_TYPE_TRUNCATE && | 804 | if (!error && operation == TOMOYO_TYPE_TRUNCATE && |
| 1058 | tomoyo_is_no_rewrite_file(filename)) { | 805 | tomoyo_no_rewrite_file(filename)) { |
| 1059 | operation = TOMOYO_TYPE_REWRITE; | 806 | operation = TOMOYO_TYPE_REWRITE; |
| 1060 | goto next; | 807 | goto next; |
| 1061 | } | 808 | } |
| 1062 | return error; | 809 | return error; |
| 1063 | } | 810 | } |
| 1064 | 811 | ||
| 812 | static bool tomoyo_same_path_number_acl(const struct tomoyo_acl_info *a, | ||
| 813 | const struct tomoyo_acl_info *b) | ||
| 814 | { | ||
| 815 | const struct tomoyo_path_number_acl *p1 = container_of(a, typeof(*p1), | ||
| 816 | head); | ||
| 817 | const struct tomoyo_path_number_acl *p2 = container_of(b, typeof(*p2), | ||
| 818 | head); | ||
| 819 | return tomoyo_same_acl_head(&p1->head, &p2->head) | ||
| 820 | && tomoyo_same_name_union(&p1->name, &p2->name) | ||
| 821 | && tomoyo_same_number_union(&p1->number, &p2->number); | ||
| 822 | } | ||
| 823 | |||
| 824 | static bool tomoyo_merge_path_number_acl(struct tomoyo_acl_info *a, | ||
| 825 | struct tomoyo_acl_info *b, | ||
| 826 | const bool is_delete) | ||
| 827 | { | ||
| 828 | u8 * const a_perm = &container_of(a, struct tomoyo_path_number_acl, | ||
| 829 | head)->perm; | ||
| 830 | u8 perm = *a_perm; | ||
| 831 | const u8 b_perm = container_of(b, struct tomoyo_path_number_acl, head) | ||
| 832 | ->perm; | ||
| 833 | if (is_delete) | ||
| 834 | perm &= ~b_perm; | ||
| 835 | else | ||
| 836 | perm |= b_perm; | ||
| 837 | *a_perm = perm; | ||
| 838 | return !perm; | ||
| 839 | } | ||
| 840 | |||
| 1065 | /** | 841 | /** |
| 1066 | * tomoyo_check_exec_perm - Check permission for "execute". | 842 | * tomoyo_update_path_number_acl - Update ioctl/chmod/chown/chgrp ACL. |
| 843 | * | ||
| 844 | * @type: Type of operation. | ||
| 845 | * @filename: Filename. | ||
| 846 | * @number: Number. | ||
| 847 | * @domain: Pointer to "struct tomoyo_domain_info". | ||
| 848 | * @is_delete: True if it is a delete request. | ||
| 1067 | * | 849 | * |
| 1068 | * @domain: Pointer to "struct tomoyo_domain_info". | 850 | * Returns 0 on success, negative value otherwise. |
| 1069 | * @filename: Check permission for "execute". | 851 | */ |
| 852 | static int tomoyo_update_path_number_acl(const u8 type, const char *filename, | ||
| 853 | char *number, | ||
| 854 | struct tomoyo_domain_info * const | ||
| 855 | domain, | ||
| 856 | const bool is_delete) | ||
| 857 | { | ||
| 858 | struct tomoyo_path_number_acl e = { | ||
| 859 | .head.type = TOMOYO_TYPE_PATH_NUMBER_ACL, | ||
| 860 | .perm = 1 << type | ||
| 861 | }; | ||
| 862 | int error = is_delete ? -ENOENT : -ENOMEM; | ||
| 863 | if (!tomoyo_parse_name_union(filename, &e.name)) | ||
| 864 | return -EINVAL; | ||
| 865 | if (!tomoyo_parse_number_union(number, &e.number)) | ||
| 866 | goto out; | ||
| 867 | error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain, | ||
| 868 | tomoyo_same_path_number_acl, | ||
| 869 | tomoyo_merge_path_number_acl); | ||
| 870 | out: | ||
| 871 | tomoyo_put_name_union(&e.name); | ||
| 872 | tomoyo_put_number_union(&e.number); | ||
| 873 | return error; | ||
| 874 | } | ||
| 875 | |||
| 876 | /** | ||
| 877 | * tomoyo_path_number_perm - Check permission for "create", "mkdir", "mkfifo", "mksock", "ioctl", "chmod", "chown", "chgrp". | ||
| 1070 | * | 878 | * |
| 1071 | * Returns 0 on success, negativevalue otherwise. | 879 | * @type: Type of operation. |
| 880 | * @path: Pointer to "struct path". | ||
| 881 | * @number: Number. | ||
| 1072 | * | 882 | * |
| 1073 | * Caller holds tomoyo_read_lock(). | 883 | * Returns 0 on success, negative value otherwise. |
| 1074 | */ | 884 | */ |
| 1075 | int tomoyo_check_exec_perm(struct tomoyo_domain_info *domain, | 885 | int tomoyo_path_number_perm(const u8 type, struct path *path, |
| 1076 | const struct tomoyo_path_info *filename) | 886 | unsigned long number) |
| 1077 | { | 887 | { |
| 1078 | const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE); | 888 | struct tomoyo_request_info r; |
| 889 | int error = -ENOMEM; | ||
| 890 | struct tomoyo_path_info buf; | ||
| 891 | int idx; | ||
| 1079 | 892 | ||
| 1080 | if (!mode) | 893 | if (tomoyo_init_request_info(&r, NULL, tomoyo_pn2mac[type]) |
| 894 | == TOMOYO_CONFIG_DISABLED || !path->mnt || !path->dentry) | ||
| 1081 | return 0; | 895 | return 0; |
| 1082 | return tomoyo_check_file_perm2(domain, filename, 1, "do_execve", mode); | 896 | idx = tomoyo_read_lock(); |
| 897 | if (!tomoyo_get_realpath(&buf, path)) | ||
| 898 | goto out; | ||
| 899 | if (type == TOMOYO_TYPE_MKDIR) | ||
| 900 | tomoyo_add_slash(&buf); | ||
| 901 | r.param_type = TOMOYO_TYPE_PATH_NUMBER_ACL; | ||
| 902 | r.param.path_number.operation = type; | ||
| 903 | r.param.path_number.filename = &buf; | ||
| 904 | r.param.path_number.number = number; | ||
| 905 | do { | ||
| 906 | tomoyo_check_acl(&r, tomoyo_check_path_number_acl); | ||
| 907 | error = tomoyo_audit_path_number_log(&r); | ||
| 908 | } while (error == TOMOYO_RETRY_REQUEST); | ||
| 909 | kfree(buf.name); | ||
| 910 | out: | ||
| 911 | tomoyo_read_unlock(idx); | ||
| 912 | if (r.mode != TOMOYO_CONFIG_ENFORCING) | ||
| 913 | error = 0; | ||
| 914 | return error; | ||
| 1083 | } | 915 | } |
| 1084 | 916 | ||
| 1085 | /** | 917 | /** |
| @@ -1096,24 +928,17 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, | |||
| 1096 | { | 928 | { |
| 1097 | const u8 acc_mode = ACC_MODE(flag); | 929 | const u8 acc_mode = ACC_MODE(flag); |
| 1098 | int error = -ENOMEM; | 930 | int error = -ENOMEM; |
| 1099 | struct tomoyo_path_info *buf; | 931 | struct tomoyo_path_info buf; |
| 1100 | const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE); | 932 | struct tomoyo_request_info r; |
| 1101 | const bool is_enforce = (mode == 3); | ||
| 1102 | int idx; | 933 | int idx; |
| 1103 | 934 | ||
| 1104 | if (!mode || !path->mnt) | 935 | if (!path->mnt || |
| 1105 | return 0; | 936 | (path->dentry->d_inode && S_ISDIR(path->dentry->d_inode->i_mode))) |
| 1106 | if (acc_mode == 0) | ||
| 1107 | return 0; | ||
| 1108 | if (path->dentry->d_inode && S_ISDIR(path->dentry->d_inode->i_mode)) | ||
| 1109 | /* | ||
| 1110 | * I don't check directories here because mkdir() and rmdir() | ||
| 1111 | * don't call me. | ||
| 1112 | */ | ||
| 1113 | return 0; | 937 | return 0; |
| 938 | buf.name = NULL; | ||
| 939 | r.mode = TOMOYO_CONFIG_DISABLED; | ||
| 1114 | idx = tomoyo_read_lock(); | 940 | idx = tomoyo_read_lock(); |
| 1115 | buf = tomoyo_get_path(path); | 941 | if (!tomoyo_get_realpath(&buf, path)) |
| 1116 | if (!buf) | ||
| 1117 | goto out; | 942 | goto out; |
| 1118 | error = 0; | 943 | error = 0; |
| 1119 | /* | 944 | /* |
| @@ -1121,28 +946,43 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, | |||
| 1121 | * we need to check "allow_rewrite" permission when the filename is not | 946 | * we need to check "allow_rewrite" permission when the filename is not |
| 1122 | * opened for append mode or the filename is truncated at open time. | 947 | * opened for append mode or the filename is truncated at open time. |
| 1123 | */ | 948 | */ |
| 1124 | if ((acc_mode & MAY_WRITE) && | 949 | if ((acc_mode & MAY_WRITE) && !(flag & O_APPEND) |
| 1125 | ((flag & O_TRUNC) || !(flag & O_APPEND)) && | 950 | && tomoyo_init_request_info(&r, domain, TOMOYO_MAC_FILE_REWRITE) |
| 1126 | (tomoyo_is_no_rewrite_file(buf))) { | 951 | != TOMOYO_CONFIG_DISABLED) { |
| 1127 | error = tomoyo_path_permission2(domain, TOMOYO_TYPE_REWRITE, | 952 | if (!tomoyo_get_realpath(&buf, path)) { |
| 1128 | buf, mode); | 953 | error = -ENOMEM; |
| 954 | goto out; | ||
| 955 | } | ||
| 956 | if (tomoyo_no_rewrite_file(&buf)) | ||
| 957 | error = tomoyo_path_permission(&r, TOMOYO_TYPE_REWRITE, | ||
| 958 | &buf); | ||
| 959 | } | ||
| 960 | if (!error && acc_mode && | ||
| 961 | tomoyo_init_request_info(&r, domain, TOMOYO_MAC_FILE_OPEN) | ||
| 962 | != TOMOYO_CONFIG_DISABLED) { | ||
| 963 | u8 operation; | ||
| 964 | if (!buf.name && !tomoyo_get_realpath(&buf, path)) { | ||
| 965 | error = -ENOMEM; | ||
| 966 | goto out; | ||
| 967 | } | ||
| 968 | if (acc_mode == (MAY_READ | MAY_WRITE)) | ||
| 969 | operation = TOMOYO_TYPE_READ_WRITE; | ||
| 970 | else if (acc_mode == MAY_READ) | ||
| 971 | operation = TOMOYO_TYPE_READ; | ||
| 972 | else | ||
| 973 | operation = TOMOYO_TYPE_WRITE; | ||
| 974 | error = tomoyo_path_permission(&r, operation, &buf); | ||
| 1129 | } | 975 | } |
| 1130 | if (!error) | ||
| 1131 | error = tomoyo_check_file_perm2(domain, buf, acc_mode, "open", | ||
| 1132 | mode); | ||
| 1133 | if (!error && (flag & O_TRUNC)) | ||
| 1134 | error = tomoyo_path_permission2(domain, TOMOYO_TYPE_TRUNCATE, | ||
| 1135 | buf, mode); | ||
| 1136 | out: | 976 | out: |
| 1137 | kfree(buf); | 977 | kfree(buf.name); |
| 1138 | tomoyo_read_unlock(idx); | 978 | tomoyo_read_unlock(idx); |
| 1139 | if (!is_enforce) | 979 | if (r.mode != TOMOYO_CONFIG_ENFORCING) |
| 1140 | error = 0; | 980 | error = 0; |
| 1141 | return error; | 981 | return error; |
| 1142 | } | 982 | } |
| 1143 | 983 | ||
| 1144 | /** | 984 | /** |
| 1145 | * tomoyo_path_perm - Check permission for "create", "unlink", "mkdir", "rmdir", "mkfifo", "mksock", "mkblock", "mkchar", "truncate", "symlink", "ioctl", "chmod", "chown", "chgrp", "chroot", "mount" and "unmount". | 985 | * tomoyo_path_perm - Check permission for "unlink", "rmdir", "truncate", "symlink", "rewrite", "chroot" and "unmount". |
| 1146 | * | 986 | * |
| 1147 | * @operation: Type of operation. | 987 | * @operation: Type of operation. |
| 1148 | * @path: Pointer to "struct path". | 988 | * @path: Pointer to "struct path". |
| @@ -1152,71 +992,79 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, | |||
| 1152 | int tomoyo_path_perm(const u8 operation, struct path *path) | 992 | int tomoyo_path_perm(const u8 operation, struct path *path) |
| 1153 | { | 993 | { |
| 1154 | int error = -ENOMEM; | 994 | int error = -ENOMEM; |
| 1155 | struct tomoyo_path_info *buf; | 995 | struct tomoyo_path_info buf; |
| 1156 | struct tomoyo_domain_info *domain = tomoyo_domain(); | 996 | struct tomoyo_request_info r; |
| 1157 | const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE); | ||
| 1158 | const bool is_enforce = (mode == 3); | ||
| 1159 | int idx; | 997 | int idx; |
| 1160 | 998 | ||
| 1161 | if (!mode || !path->mnt) | 999 | if (!path->mnt) |
| 1162 | return 0; | 1000 | return 0; |
| 1001 | if (tomoyo_init_request_info(&r, NULL, tomoyo_p2mac[operation]) | ||
| 1002 | == TOMOYO_CONFIG_DISABLED) | ||
| 1003 | return 0; | ||
| 1004 | buf.name = NULL; | ||
| 1163 | idx = tomoyo_read_lock(); | 1005 | idx = tomoyo_read_lock(); |
| 1164 | buf = tomoyo_get_path(path); | 1006 | if (!tomoyo_get_realpath(&buf, path)) |
| 1165 | if (!buf) | ||
| 1166 | goto out; | 1007 | goto out; |
| 1167 | switch (operation) { | 1008 | switch (operation) { |
| 1168 | case TOMOYO_TYPE_MKDIR: | 1009 | case TOMOYO_TYPE_REWRITE: |
| 1010 | if (!tomoyo_no_rewrite_file(&buf)) { | ||
| 1011 | error = 0; | ||
| 1012 | goto out; | ||
| 1013 | } | ||
| 1014 | break; | ||
| 1169 | case TOMOYO_TYPE_RMDIR: | 1015 | case TOMOYO_TYPE_RMDIR: |
| 1170 | case TOMOYO_TYPE_CHROOT: | 1016 | case TOMOYO_TYPE_CHROOT: |
| 1171 | if (!buf->is_dir) { | 1017 | case TOMOYO_TYPE_UMOUNT: |
| 1172 | /* | 1018 | tomoyo_add_slash(&buf); |
| 1173 | * tomoyo_get_path() reserves space for appending "/." | 1019 | break; |
| 1174 | */ | ||
| 1175 | strcat((char *) buf->name, "/"); | ||
| 1176 | tomoyo_fill_path_info(buf); | ||
| 1177 | } | ||
| 1178 | } | 1020 | } |
| 1179 | error = tomoyo_path_permission2(domain, operation, buf, mode); | 1021 | error = tomoyo_path_permission(&r, operation, &buf); |
| 1180 | out: | 1022 | out: |
| 1181 | kfree(buf); | 1023 | kfree(buf.name); |
| 1182 | tomoyo_read_unlock(idx); | 1024 | tomoyo_read_unlock(idx); |
| 1183 | if (!is_enforce) | 1025 | if (r.mode != TOMOYO_CONFIG_ENFORCING) |
| 1184 | error = 0; | 1026 | error = 0; |
| 1185 | return error; | 1027 | return error; |
| 1186 | } | 1028 | } |
| 1187 | 1029 | ||
| 1188 | /** | 1030 | /** |
| 1189 | * tomoyo_check_rewrite_permission - Check permission for "rewrite". | 1031 | * tomoyo_mkdev_perm - Check permission for "mkblock" and "mkchar". |
| 1190 | * | 1032 | * |
| 1191 | * @filp: Pointer to "struct file". | 1033 | * @operation: Type of operation. (TOMOYO_TYPE_MKCHAR or TOMOYO_TYPE_MKBLOCK) |
| 1034 | * @path: Pointer to "struct path". | ||
| 1035 | * @mode: Create mode. | ||
| 1036 | * @dev: Device number. | ||
| 1192 | * | 1037 | * |
| 1193 | * Returns 0 on success, negative value otherwise. | 1038 | * Returns 0 on success, negative value otherwise. |
| 1194 | */ | 1039 | */ |
| 1195 | int tomoyo_check_rewrite_permission(struct file *filp) | 1040 | int tomoyo_mkdev_perm(const u8 operation, struct path *path, |
| 1041 | const unsigned int mode, unsigned int dev) | ||
| 1196 | { | 1042 | { |
| 1043 | struct tomoyo_request_info r; | ||
| 1197 | int error = -ENOMEM; | 1044 | int error = -ENOMEM; |
| 1198 | struct tomoyo_domain_info *domain = tomoyo_domain(); | 1045 | struct tomoyo_path_info buf; |
| 1199 | const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE); | ||
| 1200 | const bool is_enforce = (mode == 3); | ||
| 1201 | struct tomoyo_path_info *buf; | ||
| 1202 | int idx; | 1046 | int idx; |
| 1203 | 1047 | ||
| 1204 | if (!mode || !filp->f_path.mnt) | 1048 | if (!path->mnt || |
| 1049 | tomoyo_init_request_info(&r, NULL, tomoyo_pnnn2mac[operation]) | ||
| 1050 | == TOMOYO_CONFIG_DISABLED) | ||
| 1205 | return 0; | 1051 | return 0; |
| 1206 | |||
| 1207 | idx = tomoyo_read_lock(); | 1052 | idx = tomoyo_read_lock(); |
| 1208 | buf = tomoyo_get_path(&filp->f_path); | 1053 | error = -ENOMEM; |
| 1209 | if (!buf) | 1054 | if (tomoyo_get_realpath(&buf, path)) { |
| 1210 | goto out; | 1055 | dev = new_decode_dev(dev); |
| 1211 | if (!tomoyo_is_no_rewrite_file(buf)) { | 1056 | r.param_type = TOMOYO_TYPE_MKDEV_ACL; |
| 1212 | error = 0; | 1057 | r.param.mkdev.filename = &buf; |
| 1213 | goto out; | 1058 | r.param.mkdev.operation = operation; |
| 1059 | r.param.mkdev.mode = mode; | ||
| 1060 | r.param.mkdev.major = MAJOR(dev); | ||
| 1061 | r.param.mkdev.minor = MINOR(dev); | ||
| 1062 | tomoyo_check_acl(&r, tomoyo_check_mkdev_acl); | ||
| 1063 | error = tomoyo_audit_mkdev_log(&r); | ||
| 1064 | kfree(buf.name); | ||
| 1214 | } | 1065 | } |
| 1215 | error = tomoyo_path_permission2(domain, TOMOYO_TYPE_REWRITE, buf, mode); | ||
| 1216 | out: | ||
| 1217 | kfree(buf); | ||
| 1218 | tomoyo_read_unlock(idx); | 1066 | tomoyo_read_unlock(idx); |
| 1219 | if (!is_enforce) | 1067 | if (r.mode != TOMOYO_CONFIG_ENFORCING) |
| 1220 | error = 0; | 1068 | error = 0; |
| 1221 | return error; | 1069 | return error; |
| 1222 | } | 1070 | } |
| @@ -1234,56 +1082,99 @@ int tomoyo_path2_perm(const u8 operation, struct path *path1, | |||
| 1234 | struct path *path2) | 1082 | struct path *path2) |
| 1235 | { | 1083 | { |
| 1236 | int error = -ENOMEM; | 1084 | int error = -ENOMEM; |
| 1237 | struct tomoyo_path_info *buf1, *buf2; | 1085 | struct tomoyo_path_info buf1; |
| 1238 | struct tomoyo_domain_info *domain = tomoyo_domain(); | 1086 | struct tomoyo_path_info buf2; |
| 1239 | const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE); | 1087 | struct tomoyo_request_info r; |
| 1240 | const bool is_enforce = (mode == 3); | ||
| 1241 | const char *msg; | ||
| 1242 | int idx; | 1088 | int idx; |
| 1243 | 1089 | ||
| 1244 | if (!mode || !path1->mnt || !path2->mnt) | 1090 | if (!path1->mnt || !path2->mnt || |
| 1091 | tomoyo_init_request_info(&r, NULL, tomoyo_pp2mac[operation]) | ||
| 1092 | == TOMOYO_CONFIG_DISABLED) | ||
| 1245 | return 0; | 1093 | return 0; |
| 1094 | buf1.name = NULL; | ||
| 1095 | buf2.name = NULL; | ||
| 1246 | idx = tomoyo_read_lock(); | 1096 | idx = tomoyo_read_lock(); |
| 1247 | buf1 = tomoyo_get_path(path1); | 1097 | if (!tomoyo_get_realpath(&buf1, path1) || |
| 1248 | buf2 = tomoyo_get_path(path2); | 1098 | !tomoyo_get_realpath(&buf2, path2)) |
| 1249 | if (!buf1 || !buf2) | ||
| 1250 | goto out; | ||
| 1251 | { | ||
| 1252 | struct dentry *dentry = path1->dentry; | ||
| 1253 | if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)) { | ||
| 1254 | /* | ||
| 1255 | * tomoyo_get_path() reserves space for appending "/." | ||
| 1256 | */ | ||
| 1257 | if (!buf1->is_dir) { | ||
| 1258 | strcat((char *) buf1->name, "/"); | ||
| 1259 | tomoyo_fill_path_info(buf1); | ||
| 1260 | } | ||
| 1261 | if (!buf2->is_dir) { | ||
| 1262 | strcat((char *) buf2->name, "/"); | ||
| 1263 | tomoyo_fill_path_info(buf2); | ||
| 1264 | } | ||
| 1265 | } | ||
| 1266 | } | ||
| 1267 | error = tomoyo_path2_acl(domain, operation, buf1, buf2); | ||
| 1268 | msg = tomoyo_path22keyword(operation); | ||
| 1269 | if (!error) | ||
| 1270 | goto out; | 1099 | goto out; |
| 1271 | if (tomoyo_verbose_mode(domain)) | 1100 | switch (operation) { |
| 1272 | printk(KERN_WARNING "TOMOYO-%s: Access '%s %s %s' " | 1101 | struct dentry *dentry; |
| 1273 | "denied for %s\n", tomoyo_get_msg(is_enforce), | 1102 | case TOMOYO_TYPE_RENAME: |
| 1274 | msg, buf1->name, buf2->name, | 1103 | case TOMOYO_TYPE_LINK: |
| 1275 | tomoyo_get_last_name(domain)); | 1104 | dentry = path1->dentry; |
| 1276 | if (mode == 1 && tomoyo_domain_quota_is_ok(domain)) { | 1105 | if (!dentry->d_inode || !S_ISDIR(dentry->d_inode->i_mode)) |
| 1277 | const char *name1 = tomoyo_get_file_pattern(buf1)->name; | 1106 | break; |
| 1278 | const char *name2 = tomoyo_get_file_pattern(buf2)->name; | 1107 | /* fall through */ |
| 1279 | tomoyo_update_path2_acl(operation, name1, name2, domain, | 1108 | case TOMOYO_TYPE_PIVOT_ROOT: |
| 1280 | false); | 1109 | tomoyo_add_slash(&buf1); |
| 1281 | } | 1110 | tomoyo_add_slash(&buf2); |
| 1111 | break; | ||
| 1112 | } | ||
| 1113 | r.param_type = TOMOYO_TYPE_PATH2_ACL; | ||
| 1114 | r.param.path2.operation = operation; | ||
| 1115 | r.param.path2.filename1 = &buf1; | ||
| 1116 | r.param.path2.filename2 = &buf2; | ||
| 1117 | do { | ||
| 1118 | tomoyo_check_acl(&r, tomoyo_check_path2_acl); | ||
| 1119 | error = tomoyo_audit_path2_log(&r); | ||
| 1120 | } while (error == TOMOYO_RETRY_REQUEST); | ||
| 1282 | out: | 1121 | out: |
| 1283 | kfree(buf1); | 1122 | kfree(buf1.name); |
| 1284 | kfree(buf2); | 1123 | kfree(buf2.name); |
| 1285 | tomoyo_read_unlock(idx); | 1124 | tomoyo_read_unlock(idx); |
| 1286 | if (!is_enforce) | 1125 | if (r.mode != TOMOYO_CONFIG_ENFORCING) |
| 1287 | error = 0; | 1126 | error = 0; |
| 1288 | return error; | 1127 | return error; |
| 1289 | } | 1128 | } |
| 1129 | |||
| 1130 | /** | ||
| 1131 | * tomoyo_write_file - Update file related list. | ||
| 1132 | * | ||
| 1133 | * @data: String to parse. | ||
| 1134 | * @domain: Pointer to "struct tomoyo_domain_info". | ||
| 1135 | * @is_delete: True if it is a delete request. | ||
| 1136 | * | ||
| 1137 | * Returns 0 on success, negative value otherwise. | ||
| 1138 | * | ||
| 1139 | * Caller holds tomoyo_read_lock(). | ||
| 1140 | */ | ||
| 1141 | int tomoyo_write_file(char *data, struct tomoyo_domain_info *domain, | ||
| 1142 | const bool is_delete) | ||
| 1143 | { | ||
| 1144 | char *w[5]; | ||
| 1145 | u8 type; | ||
| 1146 | if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[1][0]) | ||
| 1147 | return -EINVAL; | ||
| 1148 | if (strncmp(w[0], "allow_", 6)) | ||
| 1149 | goto out; | ||
| 1150 | w[0] += 6; | ||
| 1151 | for (type = 0; type < TOMOYO_MAX_PATH_OPERATION; type++) { | ||
| 1152 | if (strcmp(w[0], tomoyo_path_keyword[type])) | ||
| 1153 | continue; | ||
| 1154 | return tomoyo_update_path_acl(type, w[1], domain, is_delete); | ||
| 1155 | } | ||
| 1156 | if (!w[2][0]) | ||
| 1157 | goto out; | ||
| 1158 | for (type = 0; type < TOMOYO_MAX_PATH2_OPERATION; type++) { | ||
| 1159 | if (strcmp(w[0], tomoyo_path2_keyword[type])) | ||
| 1160 | continue; | ||
| 1161 | return tomoyo_update_path2_acl(type, w[1], w[2], domain, | ||
| 1162 | is_delete); | ||
| 1163 | } | ||
| 1164 | for (type = 0; type < TOMOYO_MAX_PATH_NUMBER_OPERATION; type++) { | ||
| 1165 | if (strcmp(w[0], tomoyo_path_number_keyword[type])) | ||
| 1166 | continue; | ||
| 1167 | return tomoyo_update_path_number_acl(type, w[1], w[2], domain, | ||
| 1168 | is_delete); | ||
| 1169 | } | ||
| 1170 | if (!w[3][0] || !w[4][0]) | ||
| 1171 | goto out; | ||
| 1172 | for (type = 0; type < TOMOYO_MAX_MKDEV_OPERATION; type++) { | ||
| 1173 | if (strcmp(w[0], tomoyo_mkdev_keyword[type])) | ||
| 1174 | continue; | ||
| 1175 | return tomoyo_update_mkdev_acl(type, w[1], w[2], w[3], | ||
| 1176 | w[4], domain, is_delete); | ||
| 1177 | } | ||
| 1178 | out: | ||
| 1179 | return -EINVAL; | ||
| 1180 | } | ||
diff --git a/security/tomoyo/gc.c b/security/tomoyo/gc.c index b9cc71b04314..a877e4c3b101 100644 --- a/security/tomoyo/gc.c +++ b/security/tomoyo/gc.c | |||
| @@ -11,83 +11,75 @@ | |||
| 11 | #include <linux/kthread.h> | 11 | #include <linux/kthread.h> |
| 12 | #include <linux/slab.h> | 12 | #include <linux/slab.h> |
| 13 | 13 | ||
| 14 | enum tomoyo_gc_id { | 14 | struct tomoyo_gc { |
| 15 | TOMOYO_ID_PATH_GROUP, | ||
| 16 | TOMOYO_ID_PATH_GROUP_MEMBER, | ||
| 17 | TOMOYO_ID_DOMAIN_INITIALIZER, | ||
| 18 | TOMOYO_ID_DOMAIN_KEEPER, | ||
| 19 | TOMOYO_ID_ALIAS, | ||
| 20 | TOMOYO_ID_GLOBALLY_READABLE, | ||
| 21 | TOMOYO_ID_PATTERN, | ||
| 22 | TOMOYO_ID_NO_REWRITE, | ||
| 23 | TOMOYO_ID_MANAGER, | ||
| 24 | TOMOYO_ID_NAME, | ||
| 25 | TOMOYO_ID_ACL, | ||
| 26 | TOMOYO_ID_DOMAIN | ||
| 27 | }; | ||
| 28 | |||
| 29 | struct tomoyo_gc_entry { | ||
| 30 | struct list_head list; | 15 | struct list_head list; |
| 31 | int type; | 16 | int type; |
| 32 | void *element; | 17 | struct list_head *element; |
| 33 | }; | 18 | }; |
| 34 | static LIST_HEAD(tomoyo_gc_queue); | 19 | static LIST_HEAD(tomoyo_gc_queue); |
| 35 | static DEFINE_MUTEX(tomoyo_gc_mutex); | 20 | static DEFINE_MUTEX(tomoyo_gc_mutex); |
| 36 | 21 | ||
| 37 | /* Caller holds tomoyo_policy_lock mutex. */ | 22 | /* Caller holds tomoyo_policy_lock mutex. */ |
| 38 | static bool tomoyo_add_to_gc(const int type, void *element) | 23 | static bool tomoyo_add_to_gc(const int type, struct list_head *element) |
| 39 | { | 24 | { |
| 40 | struct tomoyo_gc_entry *entry = kzalloc(sizeof(*entry), GFP_ATOMIC); | 25 | struct tomoyo_gc *entry = kzalloc(sizeof(*entry), GFP_ATOMIC); |
| 41 | if (!entry) | 26 | if (!entry) |
| 42 | return false; | 27 | return false; |
| 43 | entry->type = type; | 28 | entry->type = type; |
| 44 | entry->element = element; | 29 | entry->element = element; |
| 45 | list_add(&entry->list, &tomoyo_gc_queue); | 30 | list_add(&entry->list, &tomoyo_gc_queue); |
| 31 | list_del_rcu(element); | ||
| 46 | return true; | 32 | return true; |
| 47 | } | 33 | } |
| 48 | 34 | ||
| 49 | static void tomoyo_del_allow_read | 35 | static void tomoyo_del_allow_read(struct list_head *element) |
| 50 | (struct tomoyo_globally_readable_file_entry *ptr) | ||
| 51 | { | 36 | { |
| 37 | struct tomoyo_readable_file *ptr = | ||
| 38 | container_of(element, typeof(*ptr), head.list); | ||
| 52 | tomoyo_put_name(ptr->filename); | 39 | tomoyo_put_name(ptr->filename); |
| 53 | } | 40 | } |
| 54 | 41 | ||
| 55 | static void tomoyo_del_file_pattern(struct tomoyo_pattern_entry *ptr) | 42 | static void tomoyo_del_file_pattern(struct list_head *element) |
| 56 | { | 43 | { |
| 44 | struct tomoyo_no_pattern *ptr = | ||
| 45 | container_of(element, typeof(*ptr), head.list); | ||
| 57 | tomoyo_put_name(ptr->pattern); | 46 | tomoyo_put_name(ptr->pattern); |
| 58 | } | 47 | } |
| 59 | 48 | ||
| 60 | static void tomoyo_del_no_rewrite(struct tomoyo_no_rewrite_entry *ptr) | 49 | static void tomoyo_del_no_rewrite(struct list_head *element) |
| 61 | { | 50 | { |
| 51 | struct tomoyo_no_rewrite *ptr = | ||
| 52 | container_of(element, typeof(*ptr), head.list); | ||
| 62 | tomoyo_put_name(ptr->pattern); | 53 | tomoyo_put_name(ptr->pattern); |
| 63 | } | 54 | } |
| 64 | 55 | ||
| 65 | static void tomoyo_del_domain_initializer | 56 | static void tomoyo_del_transition_control(struct list_head *element) |
| 66 | (struct tomoyo_domain_initializer_entry *ptr) | ||
| 67 | { | 57 | { |
| 58 | struct tomoyo_transition_control *ptr = | ||
| 59 | container_of(element, typeof(*ptr), head.list); | ||
| 68 | tomoyo_put_name(ptr->domainname); | 60 | tomoyo_put_name(ptr->domainname); |
| 69 | tomoyo_put_name(ptr->program); | 61 | tomoyo_put_name(ptr->program); |
| 70 | } | 62 | } |
| 71 | 63 | ||
| 72 | static void tomoyo_del_domain_keeper(struct tomoyo_domain_keeper_entry *ptr) | 64 | static void tomoyo_del_aggregator(struct list_head *element) |
| 73 | { | ||
| 74 | tomoyo_put_name(ptr->domainname); | ||
| 75 | tomoyo_put_name(ptr->program); | ||
| 76 | } | ||
| 77 | |||
| 78 | static void tomoyo_del_alias(struct tomoyo_alias_entry *ptr) | ||
| 79 | { | 65 | { |
| 66 | struct tomoyo_aggregator *ptr = | ||
| 67 | container_of(element, typeof(*ptr), head.list); | ||
| 80 | tomoyo_put_name(ptr->original_name); | 68 | tomoyo_put_name(ptr->original_name); |
| 81 | tomoyo_put_name(ptr->aliased_name); | 69 | tomoyo_put_name(ptr->aggregated_name); |
| 82 | } | 70 | } |
| 83 | 71 | ||
| 84 | static void tomoyo_del_manager(struct tomoyo_policy_manager_entry *ptr) | 72 | static void tomoyo_del_manager(struct list_head *element) |
| 85 | { | 73 | { |
| 74 | struct tomoyo_manager *ptr = | ||
| 75 | container_of(element, typeof(*ptr), head.list); | ||
| 86 | tomoyo_put_name(ptr->manager); | 76 | tomoyo_put_name(ptr->manager); |
| 87 | } | 77 | } |
| 88 | 78 | ||
| 89 | static void tomoyo_del_acl(struct tomoyo_acl_info *acl) | 79 | static void tomoyo_del_acl(struct list_head *element) |
| 90 | { | 80 | { |
| 81 | struct tomoyo_acl_info *acl = | ||
| 82 | container_of(element, typeof(*acl), list); | ||
| 91 | switch (acl->type) { | 83 | switch (acl->type) { |
| 92 | case TOMOYO_TYPE_PATH_ACL: | 84 | case TOMOYO_TYPE_PATH_ACL: |
| 93 | { | 85 | { |
| @@ -104,14 +96,41 @@ static void tomoyo_del_acl(struct tomoyo_acl_info *acl) | |||
| 104 | tomoyo_put_name_union(&entry->name2); | 96 | tomoyo_put_name_union(&entry->name2); |
| 105 | } | 97 | } |
| 106 | break; | 98 | break; |
| 107 | default: | 99 | case TOMOYO_TYPE_PATH_NUMBER_ACL: |
| 108 | printk(KERN_WARNING "Unknown type\n"); | 100 | { |
| 101 | struct tomoyo_path_number_acl *entry | ||
| 102 | = container_of(acl, typeof(*entry), head); | ||
| 103 | tomoyo_put_name_union(&entry->name); | ||
| 104 | tomoyo_put_number_union(&entry->number); | ||
| 105 | } | ||
| 106 | break; | ||
| 107 | case TOMOYO_TYPE_MKDEV_ACL: | ||
| 108 | { | ||
| 109 | struct tomoyo_mkdev_acl *entry | ||
| 110 | = container_of(acl, typeof(*entry), head); | ||
| 111 | tomoyo_put_name_union(&entry->name); | ||
| 112 | tomoyo_put_number_union(&entry->mode); | ||
| 113 | tomoyo_put_number_union(&entry->major); | ||
| 114 | tomoyo_put_number_union(&entry->minor); | ||
| 115 | } | ||
| 116 | break; | ||
| 117 | case TOMOYO_TYPE_MOUNT_ACL: | ||
| 118 | { | ||
| 119 | struct tomoyo_mount_acl *entry | ||
| 120 | = container_of(acl, typeof(*entry), head); | ||
| 121 | tomoyo_put_name_union(&entry->dev_name); | ||
| 122 | tomoyo_put_name_union(&entry->dir_name); | ||
| 123 | tomoyo_put_name_union(&entry->fs_type); | ||
| 124 | tomoyo_put_number_union(&entry->flags); | ||
| 125 | } | ||
| 109 | break; | 126 | break; |
| 110 | } | 127 | } |
| 111 | } | 128 | } |
| 112 | 129 | ||
| 113 | static bool tomoyo_del_domain(struct tomoyo_domain_info *domain) | 130 | static bool tomoyo_del_domain(struct list_head *element) |
| 114 | { | 131 | { |
| 132 | struct tomoyo_domain_info *domain = | ||
| 133 | container_of(element, typeof(*domain), list); | ||
| 115 | struct tomoyo_acl_info *acl; | 134 | struct tomoyo_acl_info *acl; |
| 116 | struct tomoyo_acl_info *tmp; | 135 | struct tomoyo_acl_info *tmp; |
| 117 | /* | 136 | /* |
| @@ -139,7 +158,7 @@ static bool tomoyo_del_domain(struct tomoyo_domain_info *domain) | |||
| 139 | if (atomic_read(&domain->users)) | 158 | if (atomic_read(&domain->users)) |
| 140 | return false; | 159 | return false; |
| 141 | list_for_each_entry_safe(acl, tmp, &domain->acl_info_list, list) { | 160 | list_for_each_entry_safe(acl, tmp, &domain->acl_info_list, list) { |
| 142 | tomoyo_del_acl(acl); | 161 | tomoyo_del_acl(&acl->list); |
| 143 | tomoyo_memory_free(acl); | 162 | tomoyo_memory_free(acl); |
| 144 | } | 163 | } |
| 145 | tomoyo_put_name(domain->domainname); | 164 | tomoyo_put_name(domain->domainname); |
| @@ -147,135 +166,70 @@ static bool tomoyo_del_domain(struct tomoyo_domain_info *domain) | |||
| 147 | } | 166 | } |
| 148 | 167 | ||
| 149 | 168 | ||
| 150 | static void tomoyo_del_name(const struct tomoyo_name_entry *ptr) | 169 | static void tomoyo_del_name(struct list_head *element) |
| 151 | { | 170 | { |
| 171 | const struct tomoyo_name *ptr = | ||
| 172 | container_of(element, typeof(*ptr), list); | ||
| 152 | } | 173 | } |
| 153 | 174 | ||
| 154 | static void tomoyo_del_path_group_member(struct tomoyo_path_group_member | 175 | static void tomoyo_del_path_group(struct list_head *element) |
| 155 | *member) | ||
| 156 | { | 176 | { |
| 177 | struct tomoyo_path_group *member = | ||
| 178 | container_of(element, typeof(*member), head.list); | ||
| 157 | tomoyo_put_name(member->member_name); | 179 | tomoyo_put_name(member->member_name); |
| 158 | } | 180 | } |
| 159 | 181 | ||
| 160 | static void tomoyo_del_path_group(struct tomoyo_path_group *group) | 182 | static void tomoyo_del_group(struct list_head *element) |
| 161 | { | 183 | { |
| 184 | struct tomoyo_group *group = | ||
| 185 | container_of(element, typeof(*group), list); | ||
| 162 | tomoyo_put_name(group->group_name); | 186 | tomoyo_put_name(group->group_name); |
| 163 | } | 187 | } |
| 164 | 188 | ||
| 189 | static void tomoyo_del_number_group(struct list_head *element) | ||
| 190 | { | ||
| 191 | struct tomoyo_number_group *member = | ||
| 192 | container_of(element, typeof(*member), head.list); | ||
| 193 | } | ||
| 194 | |||
| 195 | static bool tomoyo_collect_member(struct list_head *member_list, int id) | ||
| 196 | { | ||
| 197 | struct tomoyo_acl_head *member; | ||
| 198 | list_for_each_entry(member, member_list, list) { | ||
| 199 | if (!member->is_deleted) | ||
| 200 | continue; | ||
| 201 | if (!tomoyo_add_to_gc(id, &member->list)) | ||
| 202 | return false; | ||
| 203 | } | ||
| 204 | return true; | ||
| 205 | } | ||
| 206 | |||
| 207 | static bool tomoyo_collect_acl(struct tomoyo_domain_info *domain) | ||
| 208 | { | ||
| 209 | struct tomoyo_acl_info *acl; | ||
| 210 | list_for_each_entry(acl, &domain->acl_info_list, list) { | ||
| 211 | if (!acl->is_deleted) | ||
| 212 | continue; | ||
| 213 | if (!tomoyo_add_to_gc(TOMOYO_ID_ACL, &acl->list)) | ||
| 214 | return false; | ||
| 215 | } | ||
| 216 | return true; | ||
| 217 | } | ||
| 218 | |||
| 165 | static void tomoyo_collect_entry(void) | 219 | static void tomoyo_collect_entry(void) |
| 166 | { | 220 | { |
| 221 | int i; | ||
| 167 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) | 222 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) |
| 168 | return; | 223 | return; |
| 169 | { | 224 | for (i = 0; i < TOMOYO_MAX_POLICY; i++) { |
| 170 | struct tomoyo_globally_readable_file_entry *ptr; | 225 | if (!tomoyo_collect_member(&tomoyo_policy_list[i], i)) |
| 171 | list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, | 226 | goto unlock; |
| 172 | list) { | ||
| 173 | if (!ptr->is_deleted) | ||
| 174 | continue; | ||
| 175 | if (tomoyo_add_to_gc(TOMOYO_ID_GLOBALLY_READABLE, ptr)) | ||
| 176 | list_del_rcu(&ptr->list); | ||
| 177 | else | ||
| 178 | break; | ||
| 179 | } | ||
| 180 | } | ||
| 181 | { | ||
| 182 | struct tomoyo_pattern_entry *ptr; | ||
| 183 | list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) { | ||
| 184 | if (!ptr->is_deleted) | ||
| 185 | continue; | ||
| 186 | if (tomoyo_add_to_gc(TOMOYO_ID_PATTERN, ptr)) | ||
| 187 | list_del_rcu(&ptr->list); | ||
| 188 | else | ||
| 189 | break; | ||
| 190 | } | ||
| 191 | } | ||
| 192 | { | ||
| 193 | struct tomoyo_no_rewrite_entry *ptr; | ||
| 194 | list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, list) { | ||
| 195 | if (!ptr->is_deleted) | ||
| 196 | continue; | ||
| 197 | if (tomoyo_add_to_gc(TOMOYO_ID_NO_REWRITE, ptr)) | ||
| 198 | list_del_rcu(&ptr->list); | ||
| 199 | else | ||
| 200 | break; | ||
| 201 | } | ||
| 202 | } | ||
| 203 | { | ||
| 204 | struct tomoyo_domain_initializer_entry *ptr; | ||
| 205 | list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, | ||
| 206 | list) { | ||
| 207 | if (!ptr->is_deleted) | ||
| 208 | continue; | ||
| 209 | if (tomoyo_add_to_gc(TOMOYO_ID_DOMAIN_INITIALIZER, ptr)) | ||
| 210 | list_del_rcu(&ptr->list); | ||
| 211 | else | ||
| 212 | break; | ||
| 213 | } | ||
| 214 | } | ||
| 215 | { | ||
| 216 | struct tomoyo_domain_keeper_entry *ptr; | ||
| 217 | list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, list) { | ||
| 218 | if (!ptr->is_deleted) | ||
| 219 | continue; | ||
| 220 | if (tomoyo_add_to_gc(TOMOYO_ID_DOMAIN_KEEPER, ptr)) | ||
| 221 | list_del_rcu(&ptr->list); | ||
| 222 | else | ||
| 223 | break; | ||
| 224 | } | ||
| 225 | } | ||
| 226 | { | ||
| 227 | struct tomoyo_alias_entry *ptr; | ||
| 228 | list_for_each_entry_rcu(ptr, &tomoyo_alias_list, list) { | ||
| 229 | if (!ptr->is_deleted) | ||
| 230 | continue; | ||
| 231 | if (tomoyo_add_to_gc(TOMOYO_ID_ALIAS, ptr)) | ||
| 232 | list_del_rcu(&ptr->list); | ||
| 233 | else | ||
| 234 | break; | ||
| 235 | } | ||
| 236 | } | ||
| 237 | { | ||
| 238 | struct tomoyo_policy_manager_entry *ptr; | ||
| 239 | list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, | ||
| 240 | list) { | ||
| 241 | if (!ptr->is_deleted) | ||
| 242 | continue; | ||
| 243 | if (tomoyo_add_to_gc(TOMOYO_ID_MANAGER, ptr)) | ||
| 244 | list_del_rcu(&ptr->list); | ||
| 245 | else | ||
| 246 | break; | ||
| 247 | } | ||
| 248 | } | 227 | } |
| 249 | { | 228 | { |
| 250 | struct tomoyo_domain_info *domain; | 229 | struct tomoyo_domain_info *domain; |
| 251 | list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { | 230 | list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { |
| 252 | struct tomoyo_acl_info *acl; | 231 | if (!tomoyo_collect_acl(domain)) |
| 253 | list_for_each_entry_rcu(acl, &domain->acl_info_list, | 232 | goto unlock; |
| 254 | list) { | ||
| 255 | switch (acl->type) { | ||
| 256 | case TOMOYO_TYPE_PATH_ACL: | ||
| 257 | if (container_of(acl, | ||
| 258 | struct tomoyo_path_acl, | ||
| 259 | head)->perm || | ||
| 260 | container_of(acl, | ||
| 261 | struct tomoyo_path_acl, | ||
| 262 | head)->perm_high) | ||
| 263 | continue; | ||
| 264 | break; | ||
| 265 | case TOMOYO_TYPE_PATH2_ACL: | ||
| 266 | if (container_of(acl, | ||
| 267 | struct tomoyo_path2_acl, | ||
| 268 | head)->perm) | ||
| 269 | continue; | ||
| 270 | break; | ||
| 271 | default: | ||
| 272 | continue; | ||
| 273 | } | ||
| 274 | if (tomoyo_add_to_gc(TOMOYO_ID_ACL, acl)) | ||
| 275 | list_del_rcu(&acl->list); | ||
| 276 | else | ||
| 277 | break; | ||
| 278 | } | ||
| 279 | if (!domain->is_deleted || atomic_read(&domain->users)) | 233 | if (!domain->is_deleted || atomic_read(&domain->users)) |
| 280 | continue; | 234 | continue; |
| 281 | /* | 235 | /* |
| @@ -283,104 +237,92 @@ static void tomoyo_collect_entry(void) | |||
| 283 | * refer this domain after successful execve(). | 237 | * refer this domain after successful execve(). |
| 284 | * We recheck domain->users after SRCU synchronization. | 238 | * We recheck domain->users after SRCU synchronization. |
| 285 | */ | 239 | */ |
| 286 | if (tomoyo_add_to_gc(TOMOYO_ID_DOMAIN, domain)) | 240 | if (!tomoyo_add_to_gc(TOMOYO_ID_DOMAIN, &domain->list)) |
| 287 | list_del_rcu(&domain->list); | 241 | goto unlock; |
| 288 | else | ||
| 289 | break; | ||
| 290 | } | 242 | } |
| 291 | } | 243 | } |
| 292 | { | 244 | for (i = 0; i < TOMOYO_MAX_HASH; i++) { |
| 293 | int i; | 245 | struct tomoyo_name *ptr; |
| 294 | for (i = 0; i < TOMOYO_MAX_HASH; i++) { | 246 | list_for_each_entry_rcu(ptr, &tomoyo_name_list[i], list) { |
| 295 | struct tomoyo_name_entry *ptr; | 247 | if (atomic_read(&ptr->users)) |
| 296 | list_for_each_entry_rcu(ptr, &tomoyo_name_list[i], | 248 | continue; |
| 297 | list) { | 249 | if (!tomoyo_add_to_gc(TOMOYO_ID_NAME, &ptr->list)) |
| 298 | if (atomic_read(&ptr->users)) | 250 | goto unlock; |
| 299 | continue; | ||
| 300 | if (tomoyo_add_to_gc(TOMOYO_ID_NAME, ptr)) | ||
| 301 | list_del_rcu(&ptr->list); | ||
| 302 | else { | ||
| 303 | i = TOMOYO_MAX_HASH; | ||
| 304 | break; | ||
| 305 | } | ||
| 306 | } | ||
| 307 | } | 251 | } |
| 308 | } | 252 | } |
| 309 | { | 253 | for (i = 0; i < TOMOYO_MAX_GROUP; i++) { |
| 310 | struct tomoyo_path_group *group; | 254 | struct list_head *list = &tomoyo_group_list[i]; |
| 311 | list_for_each_entry_rcu(group, &tomoyo_path_group_list, list) { | 255 | int id; |
| 312 | struct tomoyo_path_group_member *member; | 256 | struct tomoyo_group *group; |
| 313 | list_for_each_entry_rcu(member, &group->member_list, | 257 | switch (i) { |
| 314 | list) { | 258 | case 0: |
| 315 | if (!member->is_deleted) | 259 | id = TOMOYO_ID_PATH_GROUP; |
| 316 | continue; | 260 | break; |
| 317 | if (tomoyo_add_to_gc(TOMOYO_ID_PATH_GROUP_MEMBER, | 261 | default: |
| 318 | member)) | 262 | id = TOMOYO_ID_NUMBER_GROUP; |
| 319 | list_del_rcu(&member->list); | 263 | break; |
| 320 | else | 264 | } |
| 321 | break; | 265 | list_for_each_entry(group, list, list) { |
| 322 | } | 266 | if (!tomoyo_collect_member(&group->member_list, id)) |
| 267 | goto unlock; | ||
| 323 | if (!list_empty(&group->member_list) || | 268 | if (!list_empty(&group->member_list) || |
| 324 | atomic_read(&group->users)) | 269 | atomic_read(&group->users)) |
| 325 | continue; | 270 | continue; |
| 326 | if (tomoyo_add_to_gc(TOMOYO_ID_PATH_GROUP, group)) | 271 | if (!tomoyo_add_to_gc(TOMOYO_ID_GROUP, &group->list)) |
| 327 | list_del_rcu(&group->list); | 272 | goto unlock; |
| 328 | else | ||
| 329 | break; | ||
| 330 | } | 273 | } |
| 331 | } | 274 | } |
| 275 | unlock: | ||
| 332 | mutex_unlock(&tomoyo_policy_lock); | 276 | mutex_unlock(&tomoyo_policy_lock); |
| 333 | } | 277 | } |
| 334 | 278 | ||
| 335 | static void tomoyo_kfree_entry(void) | 279 | static void tomoyo_kfree_entry(void) |
| 336 | { | 280 | { |
| 337 | struct tomoyo_gc_entry *p; | 281 | struct tomoyo_gc *p; |
| 338 | struct tomoyo_gc_entry *tmp; | 282 | struct tomoyo_gc *tmp; |
| 339 | 283 | ||
| 340 | list_for_each_entry_safe(p, tmp, &tomoyo_gc_queue, list) { | 284 | list_for_each_entry_safe(p, tmp, &tomoyo_gc_queue, list) { |
| 285 | struct list_head *element = p->element; | ||
| 341 | switch (p->type) { | 286 | switch (p->type) { |
| 342 | case TOMOYO_ID_DOMAIN_INITIALIZER: | 287 | case TOMOYO_ID_TRANSITION_CONTROL: |
| 343 | tomoyo_del_domain_initializer(p->element); | 288 | tomoyo_del_transition_control(element); |
| 344 | break; | ||
| 345 | case TOMOYO_ID_DOMAIN_KEEPER: | ||
| 346 | tomoyo_del_domain_keeper(p->element); | ||
| 347 | break; | 289 | break; |
| 348 | case TOMOYO_ID_ALIAS: | 290 | case TOMOYO_ID_AGGREGATOR: |
| 349 | tomoyo_del_alias(p->element); | 291 | tomoyo_del_aggregator(element); |
| 350 | break; | 292 | break; |
| 351 | case TOMOYO_ID_GLOBALLY_READABLE: | 293 | case TOMOYO_ID_GLOBALLY_READABLE: |
| 352 | tomoyo_del_allow_read(p->element); | 294 | tomoyo_del_allow_read(element); |
| 353 | break; | 295 | break; |
| 354 | case TOMOYO_ID_PATTERN: | 296 | case TOMOYO_ID_PATTERN: |
| 355 | tomoyo_del_file_pattern(p->element); | 297 | tomoyo_del_file_pattern(element); |
| 356 | break; | 298 | break; |
| 357 | case TOMOYO_ID_NO_REWRITE: | 299 | case TOMOYO_ID_NO_REWRITE: |
| 358 | tomoyo_del_no_rewrite(p->element); | 300 | tomoyo_del_no_rewrite(element); |
| 359 | break; | 301 | break; |
| 360 | case TOMOYO_ID_MANAGER: | 302 | case TOMOYO_ID_MANAGER: |
| 361 | tomoyo_del_manager(p->element); | 303 | tomoyo_del_manager(element); |
| 362 | break; | 304 | break; |
| 363 | case TOMOYO_ID_NAME: | 305 | case TOMOYO_ID_NAME: |
| 364 | tomoyo_del_name(p->element); | 306 | tomoyo_del_name(element); |
| 365 | break; | 307 | break; |
| 366 | case TOMOYO_ID_ACL: | 308 | case TOMOYO_ID_ACL: |
| 367 | tomoyo_del_acl(p->element); | 309 | tomoyo_del_acl(element); |
| 368 | break; | 310 | break; |
| 369 | case TOMOYO_ID_DOMAIN: | 311 | case TOMOYO_ID_DOMAIN: |
| 370 | if (!tomoyo_del_domain(p->element)) | 312 | if (!tomoyo_del_domain(element)) |
| 371 | continue; | 313 | continue; |
| 372 | break; | 314 | break; |
| 373 | case TOMOYO_ID_PATH_GROUP_MEMBER: | ||
| 374 | tomoyo_del_path_group_member(p->element); | ||
| 375 | break; | ||
| 376 | case TOMOYO_ID_PATH_GROUP: | 315 | case TOMOYO_ID_PATH_GROUP: |
| 377 | tomoyo_del_path_group(p->element); | 316 | tomoyo_del_path_group(element); |
| 378 | break; | 317 | break; |
| 379 | default: | 318 | case TOMOYO_ID_GROUP: |
| 380 | printk(KERN_WARNING "Unknown type\n"); | 319 | tomoyo_del_group(element); |
| 320 | break; | ||
| 321 | case TOMOYO_ID_NUMBER_GROUP: | ||
| 322 | tomoyo_del_number_group(element); | ||
| 381 | break; | 323 | break; |
| 382 | } | 324 | } |
| 383 | tomoyo_memory_free(p->element); | 325 | tomoyo_memory_free(element); |
| 384 | list_del(&p->list); | 326 | list_del(&p->list); |
| 385 | kfree(p); | 327 | kfree(p); |
| 386 | } | 328 | } |
diff --git a/security/tomoyo/group.c b/security/tomoyo/group.c new file mode 100644 index 000000000000..e94352ce723f --- /dev/null +++ b/security/tomoyo/group.c | |||
| @@ -0,0 +1,130 @@ | |||
| 1 | /* | ||
| 2 | * security/tomoyo/group.c | ||
| 3 | * | ||
| 4 | * Copyright (C) 2005-2010 NTT DATA CORPORATION | ||
| 5 | */ | ||
| 6 | |||
| 7 | #include <linux/slab.h> | ||
| 8 | #include "common.h" | ||
| 9 | |||
| 10 | static bool tomoyo_same_path_group(const struct tomoyo_acl_head *a, | ||
| 11 | const struct tomoyo_acl_head *b) | ||
| 12 | { | ||
| 13 | return container_of(a, struct tomoyo_path_group, head)->member_name == | ||
| 14 | container_of(b, struct tomoyo_path_group, head)->member_name; | ||
| 15 | } | ||
| 16 | |||
| 17 | static bool tomoyo_same_number_group(const struct tomoyo_acl_head *a, | ||
| 18 | const struct tomoyo_acl_head *b) | ||
| 19 | { | ||
| 20 | return !memcmp(&container_of(a, struct tomoyo_number_group, head) | ||
| 21 | ->number, | ||
| 22 | &container_of(b, struct tomoyo_number_group, head) | ||
| 23 | ->number, | ||
| 24 | sizeof(container_of(a, struct tomoyo_number_group, head) | ||
| 25 | ->number)); | ||
| 26 | } | ||
| 27 | |||
| 28 | /** | ||
| 29 | * tomoyo_write_group - Write "struct tomoyo_path_group"/"struct tomoyo_number_group" list. | ||
| 30 | * | ||
| 31 | * @data: String to parse. | ||
| 32 | * @is_delete: True if it is a delete request. | ||
| 33 | * @type: Type of this group. | ||
| 34 | * | ||
| 35 | * Returns 0 on success, negative value otherwise. | ||
| 36 | */ | ||
| 37 | int tomoyo_write_group(char *data, const bool is_delete, const u8 type) | ||
| 38 | { | ||
| 39 | struct tomoyo_group *group; | ||
| 40 | struct list_head *member; | ||
| 41 | char *w[2]; | ||
| 42 | int error = -EINVAL; | ||
| 43 | if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[1][0]) | ||
| 44 | return -EINVAL; | ||
| 45 | group = tomoyo_get_group(w[0], type); | ||
| 46 | if (!group) | ||
| 47 | return -ENOMEM; | ||
| 48 | member = &group->member_list; | ||
| 49 | if (type == TOMOYO_PATH_GROUP) { | ||
| 50 | struct tomoyo_path_group e = { }; | ||
| 51 | e.member_name = tomoyo_get_name(w[1]); | ||
| 52 | if (!e.member_name) { | ||
| 53 | error = -ENOMEM; | ||
| 54 | goto out; | ||
| 55 | } | ||
| 56 | error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, | ||
| 57 | member, tomoyo_same_path_group); | ||
| 58 | tomoyo_put_name(e.member_name); | ||
| 59 | } else if (type == TOMOYO_NUMBER_GROUP) { | ||
| 60 | struct tomoyo_number_group e = { }; | ||
| 61 | if (w[1][0] == '@' | ||
| 62 | || !tomoyo_parse_number_union(w[1], &e.number) | ||
| 63 | || e.number.values[0] > e.number.values[1]) | ||
| 64 | goto out; | ||
| 65 | error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, | ||
| 66 | member, tomoyo_same_number_group); | ||
| 67 | /* | ||
| 68 | * tomoyo_put_number_union() is not needed because | ||
| 69 | * w[1][0] != '@'. | ||
| 70 | */ | ||
| 71 | } | ||
| 72 | out: | ||
| 73 | tomoyo_put_group(group); | ||
| 74 | return error; | ||
| 75 | } | ||
| 76 | |||
| 77 | /** | ||
| 78 | * tomoyo_path_matches_group - Check whether the given pathname matches members of the given pathname group. | ||
| 79 | * | ||
| 80 | * @pathname: The name of pathname. | ||
| 81 | * @group: Pointer to "struct tomoyo_path_group". | ||
| 82 | * | ||
| 83 | * Returns matched member's pathname if @pathname matches pathnames in @group, | ||
| 84 | * NULL otherwise. | ||
| 85 | * | ||
| 86 | * Caller holds tomoyo_read_lock(). | ||
| 87 | */ | ||
| 88 | const struct tomoyo_path_info * | ||
| 89 | tomoyo_path_matches_group(const struct tomoyo_path_info *pathname, | ||
| 90 | const struct tomoyo_group *group) | ||
| 91 | { | ||
| 92 | struct tomoyo_path_group *member; | ||
| 93 | list_for_each_entry_rcu(member, &group->member_list, head.list) { | ||
| 94 | if (member->head.is_deleted) | ||
| 95 | continue; | ||
| 96 | if (!tomoyo_path_matches_pattern(pathname, member->member_name)) | ||
| 97 | continue; | ||
| 98 | return member->member_name; | ||
| 99 | } | ||
| 100 | return NULL; | ||
| 101 | } | ||
| 102 | |||
| 103 | /** | ||
| 104 | * tomoyo_number_matches_group - Check whether the given number matches members of the given number group. | ||
| 105 | * | ||
| 106 | * @min: Min number. | ||
| 107 | * @max: Max number. | ||
| 108 | * @group: Pointer to "struct tomoyo_number_group". | ||
| 109 | * | ||
| 110 | * Returns true if @min and @max partially overlaps @group, false otherwise. | ||
| 111 | * | ||
| 112 | * Caller holds tomoyo_read_lock(). | ||
| 113 | */ | ||
| 114 | bool tomoyo_number_matches_group(const unsigned long min, | ||
| 115 | const unsigned long max, | ||
| 116 | const struct tomoyo_group *group) | ||
| 117 | { | ||
| 118 | struct tomoyo_number_group *member; | ||
| 119 | bool matched = false; | ||
| 120 | list_for_each_entry_rcu(member, &group->member_list, head.list) { | ||
| 121 | if (member->head.is_deleted) | ||
| 122 | continue; | ||
| 123 | if (min > member->number.values[1] || | ||
| 124 | max < member->number.values[0]) | ||
| 125 | continue; | ||
| 126 | matched = true; | ||
| 127 | break; | ||
| 128 | } | ||
| 129 | return matched; | ||
| 130 | } | ||
diff --git a/security/tomoyo/load_policy.c b/security/tomoyo/load_policy.c new file mode 100644 index 000000000000..bbada7ca1b91 --- /dev/null +++ b/security/tomoyo/load_policy.c | |||
| @@ -0,0 +1,81 @@ | |||
| 1 | /* | ||
| 2 | * security/tomoyo/load_policy.c | ||
| 3 | * | ||
| 4 | * Policy loader launcher for TOMOYO. | ||
| 5 | * | ||
| 6 | * Copyright (C) 2005-2010 NTT DATA CORPORATION | ||
| 7 | */ | ||
| 8 | |||
| 9 | #include "common.h" | ||
| 10 | |||
| 11 | /* path to policy loader */ | ||
| 12 | static const char *tomoyo_loader = "/sbin/tomoyo-init"; | ||
| 13 | |||
| 14 | /** | ||
| 15 | * tomoyo_policy_loader_exists - Check whether /sbin/tomoyo-init exists. | ||
| 16 | * | ||
| 17 | * Returns true if /sbin/tomoyo-init exists, false otherwise. | ||
| 18 | */ | ||
| 19 | static bool tomoyo_policy_loader_exists(void) | ||
| 20 | { | ||
| 21 | /* | ||
| 22 | * Don't activate MAC if the policy loader doesn't exist. | ||
| 23 | * If the initrd includes /sbin/init but real-root-dev has not | ||
| 24 | * mounted on / yet, activating MAC will block the system since | ||
| 25 | * policies are not loaded yet. | ||
| 26 | * Thus, let do_execve() call this function everytime. | ||
| 27 | */ | ||
| 28 | struct path path; | ||
| 29 | |||
| 30 | if (kern_path(tomoyo_loader, LOOKUP_FOLLOW, &path)) { | ||
| 31 | printk(KERN_INFO "Not activating Mandatory Access Control now " | ||
| 32 | "since %s doesn't exist.\n", tomoyo_loader); | ||
| 33 | return false; | ||
| 34 | } | ||
| 35 | path_put(&path); | ||
| 36 | return true; | ||
| 37 | } | ||
| 38 | |||
| 39 | /** | ||
| 40 | * tomoyo_load_policy - Run external policy loader to load policy. | ||
| 41 | * | ||
| 42 | * @filename: The program about to start. | ||
| 43 | * | ||
| 44 | * This function checks whether @filename is /sbin/init , and if so | ||
| 45 | * invoke /sbin/tomoyo-init and wait for the termination of /sbin/tomoyo-init | ||
| 46 | * and then continues invocation of /sbin/init. | ||
| 47 | * /sbin/tomoyo-init reads policy files in /etc/tomoyo/ directory and | ||
| 48 | * writes to /sys/kernel/security/tomoyo/ interfaces. | ||
| 49 | * | ||
| 50 | * Returns nothing. | ||
| 51 | */ | ||
| 52 | void tomoyo_load_policy(const char *filename) | ||
| 53 | { | ||
| 54 | char *argv[2]; | ||
| 55 | char *envp[3]; | ||
| 56 | |||
| 57 | if (tomoyo_policy_loaded) | ||
| 58 | return; | ||
| 59 | /* | ||
| 60 | * Check filename is /sbin/init or /sbin/tomoyo-start. | ||
| 61 | * /sbin/tomoyo-start is a dummy filename in case where /sbin/init can't | ||
| 62 | * be passed. | ||
| 63 | * You can create /sbin/tomoyo-start by | ||
| 64 | * "ln -s /bin/true /sbin/tomoyo-start". | ||
| 65 | */ | ||
| 66 | if (strcmp(filename, "/sbin/init") && | ||
| 67 | strcmp(filename, "/sbin/tomoyo-start")) | ||
| 68 | return; | ||
| 69 | if (!tomoyo_policy_loader_exists()) | ||
| 70 | return; | ||
| 71 | |||
| 72 | printk(KERN_INFO "Calling %s to load policy. Please wait.\n", | ||
| 73 | tomoyo_loader); | ||
| 74 | argv[0] = (char *) tomoyo_loader; | ||
| 75 | argv[1] = NULL; | ||
| 76 | envp[0] = "HOME=/"; | ||
| 77 | envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; | ||
| 78 | envp[2] = NULL; | ||
| 79 | call_usermodehelper(argv[0], argv, envp, 1); | ||
| 80 | tomoyo_check_profile(); | ||
| 81 | } | ||
diff --git a/security/tomoyo/memory.c b/security/tomoyo/memory.c new file mode 100644 index 000000000000..297612669c74 --- /dev/null +++ b/security/tomoyo/memory.c | |||
| @@ -0,0 +1,282 @@ | |||
| 1 | /* | ||
| 2 | * security/tomoyo/memory.c | ||
| 3 | * | ||
| 4 | * Memory management functions for TOMOYO. | ||
| 5 | * | ||
| 6 | * Copyright (C) 2005-2010 NTT DATA CORPORATION | ||
| 7 | */ | ||
| 8 | |||
| 9 | #include <linux/hash.h> | ||
| 10 | #include <linux/slab.h> | ||
| 11 | #include "common.h" | ||
| 12 | |||
| 13 | /** | ||
| 14 | * tomoyo_warn_oom - Print out of memory warning message. | ||
| 15 | * | ||
| 16 | * @function: Function's name. | ||
| 17 | */ | ||
| 18 | void tomoyo_warn_oom(const char *function) | ||
| 19 | { | ||
| 20 | /* Reduce error messages. */ | ||
| 21 | static pid_t tomoyo_last_pid; | ||
| 22 | const pid_t pid = current->pid; | ||
| 23 | if (tomoyo_last_pid != pid) { | ||
| 24 | printk(KERN_WARNING "ERROR: Out of memory at %s.\n", | ||
| 25 | function); | ||
| 26 | tomoyo_last_pid = pid; | ||
| 27 | } | ||
| 28 | if (!tomoyo_policy_loaded) | ||
| 29 | panic("MAC Initialization failed.\n"); | ||
| 30 | } | ||
| 31 | |||
| 32 | /* Memory allocated for policy. */ | ||
| 33 | static atomic_t tomoyo_policy_memory_size; | ||
| 34 | /* Quota for holding policy. */ | ||
| 35 | static unsigned int tomoyo_quota_for_policy; | ||
| 36 | |||
| 37 | /** | ||
| 38 | * tomoyo_memory_ok - Check memory quota. | ||
| 39 | * | ||
| 40 | * @ptr: Pointer to allocated memory. | ||
| 41 | * | ||
| 42 | * Returns true on success, false otherwise. | ||
| 43 | * | ||
| 44 | * Returns true if @ptr is not NULL and quota not exceeded, false otherwise. | ||
| 45 | */ | ||
| 46 | bool tomoyo_memory_ok(void *ptr) | ||
| 47 | { | ||
| 48 | size_t s = ptr ? ksize(ptr) : 0; | ||
| 49 | atomic_add(s, &tomoyo_policy_memory_size); | ||
| 50 | if (ptr && (!tomoyo_quota_for_policy || | ||
| 51 | atomic_read(&tomoyo_policy_memory_size) | ||
| 52 | <= tomoyo_quota_for_policy)) { | ||
| 53 | memset(ptr, 0, s); | ||
| 54 | return true; | ||
| 55 | } | ||
| 56 | atomic_sub(s, &tomoyo_policy_memory_size); | ||
| 57 | tomoyo_warn_oom(__func__); | ||
| 58 | return false; | ||
| 59 | } | ||
| 60 | |||
| 61 | /** | ||
| 62 | * tomoyo_commit_ok - Check memory quota. | ||
| 63 | * | ||
| 64 | * @data: Data to copy from. | ||
| 65 | * @size: Size in byte. | ||
| 66 | * | ||
| 67 | * Returns pointer to allocated memory on success, NULL otherwise. | ||
| 68 | * @data is zero-cleared on success. | ||
| 69 | */ | ||
| 70 | void *tomoyo_commit_ok(void *data, const unsigned int size) | ||
| 71 | { | ||
| 72 | void *ptr = kzalloc(size, GFP_NOFS); | ||
| 73 | if (tomoyo_memory_ok(ptr)) { | ||
| 74 | memmove(ptr, data, size); | ||
| 75 | memset(data, 0, size); | ||
| 76 | return ptr; | ||
| 77 | } | ||
| 78 | return NULL; | ||
| 79 | } | ||
| 80 | |||
| 81 | /** | ||
| 82 | * tomoyo_memory_free - Free memory for elements. | ||
| 83 | * | ||
| 84 | * @ptr: Pointer to allocated memory. | ||
| 85 | */ | ||
| 86 | void tomoyo_memory_free(void *ptr) | ||
| 87 | { | ||
| 88 | atomic_sub(ksize(ptr), &tomoyo_policy_memory_size); | ||
| 89 | kfree(ptr); | ||
| 90 | } | ||
| 91 | |||
| 92 | /** | ||
| 93 | * tomoyo_get_group - Allocate memory for "struct tomoyo_path_group"/"struct tomoyo_number_group". | ||
| 94 | * | ||
| 95 | * @group_name: The name of address group. | ||
| 96 | * @idx: Index number. | ||
| 97 | * | ||
| 98 | * Returns pointer to "struct tomoyo_group" on success, NULL otherwise. | ||
| 99 | */ | ||
| 100 | struct tomoyo_group *tomoyo_get_group(const char *group_name, const u8 idx) | ||
| 101 | { | ||
| 102 | struct tomoyo_group e = { }; | ||
| 103 | struct tomoyo_group *group = NULL; | ||
| 104 | bool found = false; | ||
| 105 | if (!tomoyo_correct_word(group_name) || idx >= TOMOYO_MAX_GROUP) | ||
| 106 | return NULL; | ||
| 107 | e.group_name = tomoyo_get_name(group_name); | ||
| 108 | if (!e.group_name) | ||
| 109 | return NULL; | ||
| 110 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) | ||
| 111 | goto out; | ||
| 112 | list_for_each_entry(group, &tomoyo_group_list[idx], list) { | ||
| 113 | if (e.group_name != group->group_name) | ||
| 114 | continue; | ||
| 115 | atomic_inc(&group->users); | ||
| 116 | found = true; | ||
| 117 | break; | ||
| 118 | } | ||
| 119 | if (!found) { | ||
| 120 | struct tomoyo_group *entry = tomoyo_commit_ok(&e, sizeof(e)); | ||
| 121 | if (entry) { | ||
| 122 | INIT_LIST_HEAD(&entry->member_list); | ||
| 123 | atomic_set(&entry->users, 1); | ||
| 124 | list_add_tail_rcu(&entry->list, | ||
| 125 | &tomoyo_group_list[idx]); | ||
| 126 | group = entry; | ||
| 127 | found = true; | ||
| 128 | } | ||
| 129 | } | ||
| 130 | mutex_unlock(&tomoyo_policy_lock); | ||
| 131 | out: | ||
| 132 | tomoyo_put_name(e.group_name); | ||
| 133 | return found ? group : NULL; | ||
| 134 | } | ||
| 135 | |||
| 136 | /* | ||
| 137 | * tomoyo_name_list is used for holding string data used by TOMOYO. | ||
| 138 | * Since same string data is likely used for multiple times (e.g. | ||
| 139 | * "/lib/libc-2.5.so"), TOMOYO shares string data in the form of | ||
| 140 | * "const struct tomoyo_path_info *". | ||
| 141 | */ | ||
| 142 | struct list_head tomoyo_name_list[TOMOYO_MAX_HASH]; | ||
| 143 | |||
| 144 | /** | ||
| 145 | * tomoyo_get_name - Allocate permanent memory for string data. | ||
| 146 | * | ||
| 147 | * @name: The string to store into the permernent memory. | ||
| 148 | * | ||
| 149 | * Returns pointer to "struct tomoyo_path_info" on success, NULL otherwise. | ||
| 150 | */ | ||
| 151 | const struct tomoyo_path_info *tomoyo_get_name(const char *name) | ||
| 152 | { | ||
| 153 | struct tomoyo_name *ptr; | ||
| 154 | unsigned int hash; | ||
| 155 | int len; | ||
| 156 | int allocated_len; | ||
| 157 | struct list_head *head; | ||
| 158 | |||
| 159 | if (!name) | ||
| 160 | return NULL; | ||
| 161 | len = strlen(name) + 1; | ||
| 162 | hash = full_name_hash((const unsigned char *) name, len - 1); | ||
| 163 | head = &tomoyo_name_list[hash_long(hash, TOMOYO_HASH_BITS)]; | ||
| 164 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) | ||
| 165 | return NULL; | ||
| 166 | list_for_each_entry(ptr, head, list) { | ||
| 167 | if (hash != ptr->entry.hash || strcmp(name, ptr->entry.name)) | ||
| 168 | continue; | ||
| 169 | atomic_inc(&ptr->users); | ||
| 170 | goto out; | ||
| 171 | } | ||
| 172 | ptr = kzalloc(sizeof(*ptr) + len, GFP_NOFS); | ||
| 173 | allocated_len = ptr ? ksize(ptr) : 0; | ||
| 174 | if (!ptr || (tomoyo_quota_for_policy && | ||
| 175 | atomic_read(&tomoyo_policy_memory_size) + allocated_len | ||
| 176 | > tomoyo_quota_for_policy)) { | ||
| 177 | kfree(ptr); | ||
| 178 | ptr = NULL; | ||
| 179 | tomoyo_warn_oom(__func__); | ||
| 180 | goto out; | ||
| 181 | } | ||
| 182 | atomic_add(allocated_len, &tomoyo_policy_memory_size); | ||
| 183 | ptr->entry.name = ((char *) ptr) + sizeof(*ptr); | ||
| 184 | memmove((char *) ptr->entry.name, name, len); | ||
| 185 | atomic_set(&ptr->users, 1); | ||
| 186 | tomoyo_fill_path_info(&ptr->entry); | ||
| 187 | list_add_tail(&ptr->list, head); | ||
| 188 | out: | ||
| 189 | mutex_unlock(&tomoyo_policy_lock); | ||
| 190 | return ptr ? &ptr->entry : NULL; | ||
| 191 | } | ||
| 192 | |||
| 193 | /** | ||
| 194 | * tomoyo_mm_init - Initialize mm related code. | ||
| 195 | */ | ||
| 196 | void __init tomoyo_mm_init(void) | ||
| 197 | { | ||
| 198 | int idx; | ||
| 199 | |||
| 200 | for (idx = 0; idx < TOMOYO_MAX_POLICY; idx++) | ||
| 201 | INIT_LIST_HEAD(&tomoyo_policy_list[idx]); | ||
| 202 | for (idx = 0; idx < TOMOYO_MAX_GROUP; idx++) | ||
| 203 | INIT_LIST_HEAD(&tomoyo_group_list[idx]); | ||
| 204 | for (idx = 0; idx < TOMOYO_MAX_HASH; idx++) | ||
| 205 | INIT_LIST_HEAD(&tomoyo_name_list[idx]); | ||
| 206 | INIT_LIST_HEAD(&tomoyo_kernel_domain.acl_info_list); | ||
| 207 | tomoyo_kernel_domain.domainname = tomoyo_get_name(TOMOYO_ROOT_NAME); | ||
| 208 | list_add_tail_rcu(&tomoyo_kernel_domain.list, &tomoyo_domain_list); | ||
| 209 | idx = tomoyo_read_lock(); | ||
| 210 | if (tomoyo_find_domain(TOMOYO_ROOT_NAME) != &tomoyo_kernel_domain) | ||
| 211 | panic("Can't register tomoyo_kernel_domain"); | ||
| 212 | { | ||
| 213 | /* Load built-in policy. */ | ||
| 214 | tomoyo_write_transition_control("/sbin/hotplug", false, | ||
| 215 | TOMOYO_TRANSITION_CONTROL_INITIALIZE); | ||
| 216 | tomoyo_write_transition_control("/sbin/modprobe", false, | ||
| 217 | TOMOYO_TRANSITION_CONTROL_INITIALIZE); | ||
| 218 | } | ||
| 219 | tomoyo_read_unlock(idx); | ||
| 220 | } | ||
| 221 | |||
| 222 | |||
| 223 | /* Memory allocated for query lists. */ | ||
| 224 | unsigned int tomoyo_query_memory_size; | ||
| 225 | /* Quota for holding query lists. */ | ||
| 226 | unsigned int tomoyo_quota_for_query; | ||
| 227 | |||
| 228 | /** | ||
| 229 | * tomoyo_read_memory_counter - Check for memory usage in bytes. | ||
| 230 | * | ||
| 231 | * @head: Pointer to "struct tomoyo_io_buffer". | ||
| 232 | * | ||
| 233 | * Returns memory usage. | ||
| 234 | */ | ||
| 235 | void tomoyo_read_memory_counter(struct tomoyo_io_buffer *head) | ||
| 236 | { | ||
| 237 | if (!head->r.eof) { | ||
| 238 | const unsigned int policy | ||
| 239 | = atomic_read(&tomoyo_policy_memory_size); | ||
| 240 | const unsigned int query = tomoyo_query_memory_size; | ||
| 241 | char buffer[64]; | ||
| 242 | |||
| 243 | memset(buffer, 0, sizeof(buffer)); | ||
| 244 | if (tomoyo_quota_for_policy) | ||
| 245 | snprintf(buffer, sizeof(buffer) - 1, | ||
| 246 | " (Quota: %10u)", | ||
| 247 | tomoyo_quota_for_policy); | ||
| 248 | else | ||
| 249 | buffer[0] = '\0'; | ||
| 250 | tomoyo_io_printf(head, "Policy: %10u%s\n", policy, | ||
| 251 | buffer); | ||
| 252 | if (tomoyo_quota_for_query) | ||
| 253 | snprintf(buffer, sizeof(buffer) - 1, | ||
| 254 | " (Quota: %10u)", | ||
| 255 | tomoyo_quota_for_query); | ||
| 256 | else | ||
| 257 | buffer[0] = '\0'; | ||
| 258 | tomoyo_io_printf(head, "Query lists: %10u%s\n", query, | ||
| 259 | buffer); | ||
| 260 | tomoyo_io_printf(head, "Total: %10u\n", policy + query); | ||
| 261 | head->r.eof = true; | ||
| 262 | } | ||
| 263 | } | ||
| 264 | |||
| 265 | /** | ||
| 266 | * tomoyo_write_memory_quota - Set memory quota. | ||
| 267 | * | ||
| 268 | * @head: Pointer to "struct tomoyo_io_buffer". | ||
| 269 | * | ||
| 270 | * Returns 0. | ||
| 271 | */ | ||
| 272 | int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head) | ||
| 273 | { | ||
| 274 | char *data = head->write_buf; | ||
| 275 | unsigned int size; | ||
| 276 | |||
| 277 | if (sscanf(data, "Policy: %u", &size) == 1) | ||
| 278 | tomoyo_quota_for_policy = size; | ||
| 279 | else if (sscanf(data, "Query lists: %u", &size) == 1) | ||
| 280 | tomoyo_quota_for_query = size; | ||
| 281 | return 0; | ||
| 282 | } | ||
diff --git a/security/tomoyo/mount.c b/security/tomoyo/mount.c new file mode 100644 index 000000000000..82bf8c2390bc --- /dev/null +++ b/security/tomoyo/mount.c | |||
| @@ -0,0 +1,284 @@ | |||
| 1 | /* | ||
| 2 | * security/tomoyo/mount.c | ||
| 3 | * | ||
| 4 | * Copyright (C) 2005-2010 NTT DATA CORPORATION | ||
| 5 | */ | ||
| 6 | |||
| 7 | #include <linux/slab.h> | ||
| 8 | #include "common.h" | ||
| 9 | |||
| 10 | /* Keywords for mount restrictions. */ | ||
| 11 | |||
| 12 | /* Allow to call 'mount --bind /source_dir /dest_dir' */ | ||
| 13 | #define TOMOYO_MOUNT_BIND_KEYWORD "--bind" | ||
| 14 | /* Allow to call 'mount --move /old_dir /new_dir ' */ | ||
| 15 | #define TOMOYO_MOUNT_MOVE_KEYWORD "--move" | ||
| 16 | /* Allow to call 'mount -o remount /dir ' */ | ||
| 17 | #define TOMOYO_MOUNT_REMOUNT_KEYWORD "--remount" | ||
| 18 | /* Allow to call 'mount --make-unbindable /dir' */ | ||
| 19 | #define TOMOYO_MOUNT_MAKE_UNBINDABLE_KEYWORD "--make-unbindable" | ||
| 20 | /* Allow to call 'mount --make-private /dir' */ | ||
| 21 | #define TOMOYO_MOUNT_MAKE_PRIVATE_KEYWORD "--make-private" | ||
| 22 | /* Allow to call 'mount --make-slave /dir' */ | ||
| 23 | #define TOMOYO_MOUNT_MAKE_SLAVE_KEYWORD "--make-slave" | ||
| 24 | /* Allow to call 'mount --make-shared /dir' */ | ||
| 25 | #define TOMOYO_MOUNT_MAKE_SHARED_KEYWORD "--make-shared" | ||
| 26 | |||
| 27 | /** | ||
| 28 | * tomoyo_audit_mount_log - Audit mount log. | ||
| 29 | * | ||
| 30 | * @r: Pointer to "struct tomoyo_request_info". | ||
| 31 | * | ||
| 32 | * Returns 0 on success, negative value otherwise. | ||
| 33 | */ | ||
| 34 | static int tomoyo_audit_mount_log(struct tomoyo_request_info *r) | ||
| 35 | { | ||
| 36 | const char *dev = r->param.mount.dev->name; | ||
| 37 | const char *dir = r->param.mount.dir->name; | ||
| 38 | const char *type = r->param.mount.type->name; | ||
| 39 | const unsigned long flags = r->param.mount.flags; | ||
| 40 | if (r->granted) | ||
| 41 | return 0; | ||
| 42 | if (!strcmp(type, TOMOYO_MOUNT_REMOUNT_KEYWORD)) | ||
| 43 | tomoyo_warn_log(r, "mount -o remount %s 0x%lX", dir, flags); | ||
| 44 | else if (!strcmp(type, TOMOYO_MOUNT_BIND_KEYWORD) | ||
| 45 | || !strcmp(type, TOMOYO_MOUNT_MOVE_KEYWORD)) | ||
| 46 | tomoyo_warn_log(r, "mount %s %s %s 0x%lX", type, dev, dir, | ||
| 47 | flags); | ||
| 48 | else if (!strcmp(type, TOMOYO_MOUNT_MAKE_UNBINDABLE_KEYWORD) || | ||
| 49 | !strcmp(type, TOMOYO_MOUNT_MAKE_PRIVATE_KEYWORD) || | ||
| 50 | !strcmp(type, TOMOYO_MOUNT_MAKE_SLAVE_KEYWORD) || | ||
| 51 | !strcmp(type, TOMOYO_MOUNT_MAKE_SHARED_KEYWORD)) | ||
| 52 | tomoyo_warn_log(r, "mount %s %s 0x%lX", type, dir, flags); | ||
| 53 | else | ||
| 54 | tomoyo_warn_log(r, "mount -t %s %s %s 0x%lX", type, dev, dir, | ||
| 55 | flags); | ||
| 56 | return tomoyo_supervisor(r, | ||
| 57 | TOMOYO_KEYWORD_ALLOW_MOUNT "%s %s %s 0x%lX\n", | ||
| 58 | tomoyo_pattern(r->param.mount.dev), | ||
| 59 | tomoyo_pattern(r->param.mount.dir), type, | ||
| 60 | flags); | ||
| 61 | } | ||
| 62 | |||
| 63 | static bool tomoyo_check_mount_acl(struct tomoyo_request_info *r, | ||
| 64 | const struct tomoyo_acl_info *ptr) | ||
| 65 | { | ||
| 66 | const struct tomoyo_mount_acl *acl = | ||
| 67 | container_of(ptr, typeof(*acl), head); | ||
| 68 | return tomoyo_compare_number_union(r->param.mount.flags, &acl->flags) && | ||
| 69 | tomoyo_compare_name_union(r->param.mount.type, &acl->fs_type) && | ||
| 70 | tomoyo_compare_name_union(r->param.mount.dir, &acl->dir_name) && | ||
| 71 | (!r->param.mount.need_dev || | ||
| 72 | tomoyo_compare_name_union(r->param.mount.dev, &acl->dev_name)); | ||
| 73 | } | ||
| 74 | |||
| 75 | /** | ||
| 76 | * tomoyo_mount_acl - Check permission for mount() operation. | ||
| 77 | * | ||
| 78 | * @r: Pointer to "struct tomoyo_request_info". | ||
| 79 | * @dev_name: Name of device file. | ||
| 80 | * @dir: Pointer to "struct path". | ||
| 81 | * @type: Name of filesystem type. | ||
| 82 | * @flags: Mount options. | ||
| 83 | * | ||
| 84 | * Returns 0 on success, negative value otherwise. | ||
| 85 | * | ||
| 86 | * Caller holds tomoyo_read_lock(). | ||
| 87 | */ | ||
| 88 | static int tomoyo_mount_acl(struct tomoyo_request_info *r, char *dev_name, | ||
| 89 | struct path *dir, char *type, unsigned long flags) | ||
| 90 | { | ||
| 91 | struct path path; | ||
| 92 | struct file_system_type *fstype = NULL; | ||
| 93 | const char *requested_type = NULL; | ||
| 94 | const char *requested_dir_name = NULL; | ||
| 95 | const char *requested_dev_name = NULL; | ||
| 96 | struct tomoyo_path_info rtype; | ||
| 97 | struct tomoyo_path_info rdev; | ||
| 98 | struct tomoyo_path_info rdir; | ||
| 99 | int need_dev = 0; | ||
| 100 | int error = -ENOMEM; | ||
| 101 | |||
| 102 | /* Get fstype. */ | ||
| 103 | requested_type = tomoyo_encode(type); | ||
| 104 | if (!requested_type) | ||
| 105 | goto out; | ||
| 106 | rtype.name = requested_type; | ||
| 107 | tomoyo_fill_path_info(&rtype); | ||
| 108 | |||
| 109 | /* Get mount point. */ | ||
| 110 | requested_dir_name = tomoyo_realpath_from_path(dir); | ||
| 111 | if (!requested_dir_name) { | ||
| 112 | error = -ENOMEM; | ||
| 113 | goto out; | ||
| 114 | } | ||
| 115 | rdir.name = requested_dir_name; | ||
| 116 | tomoyo_fill_path_info(&rdir); | ||
| 117 | |||
| 118 | /* Compare fs name. */ | ||
| 119 | if (!strcmp(type, TOMOYO_MOUNT_REMOUNT_KEYWORD)) { | ||
| 120 | /* dev_name is ignored. */ | ||
| 121 | } else if (!strcmp(type, TOMOYO_MOUNT_MAKE_UNBINDABLE_KEYWORD) || | ||
| 122 | !strcmp(type, TOMOYO_MOUNT_MAKE_PRIVATE_KEYWORD) || | ||
| 123 | !strcmp(type, TOMOYO_MOUNT_MAKE_SLAVE_KEYWORD) || | ||
| 124 | !strcmp(type, TOMOYO_MOUNT_MAKE_SHARED_KEYWORD)) { | ||
| 125 | /* dev_name is ignored. */ | ||
| 126 | } else if (!strcmp(type, TOMOYO_MOUNT_BIND_KEYWORD) || | ||
| 127 | !strcmp(type, TOMOYO_MOUNT_MOVE_KEYWORD)) { | ||
| 128 | need_dev = -1; /* dev_name is a directory */ | ||
| 129 | } else { | ||
| 130 | fstype = get_fs_type(type); | ||
| 131 | if (!fstype) { | ||
| 132 | error = -ENODEV; | ||
| 133 | goto out; | ||
| 134 | } | ||
| 135 | if (fstype->fs_flags & FS_REQUIRES_DEV) | ||
| 136 | /* dev_name is a block device file. */ | ||
| 137 | need_dev = 1; | ||
| 138 | } | ||
| 139 | if (need_dev) { | ||
| 140 | /* Get mount point or device file. */ | ||
| 141 | if (kern_path(dev_name, LOOKUP_FOLLOW, &path)) { | ||
| 142 | error = -ENOENT; | ||
| 143 | goto out; | ||
| 144 | } | ||
| 145 | requested_dev_name = tomoyo_realpath_from_path(&path); | ||
| 146 | if (!requested_dev_name) { | ||
| 147 | error = -ENOENT; | ||
| 148 | goto out; | ||
| 149 | } | ||
| 150 | } else { | ||
| 151 | /* Map dev_name to "<NULL>" if no dev_name given. */ | ||
| 152 | if (!dev_name) | ||
| 153 | dev_name = "<NULL>"; | ||
| 154 | requested_dev_name = tomoyo_encode(dev_name); | ||
| 155 | if (!requested_dev_name) { | ||
| 156 | error = -ENOMEM; | ||
| 157 | goto out; | ||
| 158 | } | ||
| 159 | } | ||
| 160 | rdev.name = requested_dev_name; | ||
| 161 | tomoyo_fill_path_info(&rdev); | ||
| 162 | r->param_type = TOMOYO_TYPE_MOUNT_ACL; | ||
| 163 | r->param.mount.need_dev = need_dev; | ||
| 164 | r->param.mount.dev = &rdev; | ||
| 165 | r->param.mount.dir = &rdir; | ||
| 166 | r->param.mount.type = &rtype; | ||
| 167 | r->param.mount.flags = flags; | ||
| 168 | do { | ||
| 169 | tomoyo_check_acl(r, tomoyo_check_mount_acl); | ||
| 170 | error = tomoyo_audit_mount_log(r); | ||
| 171 | } while (error == TOMOYO_RETRY_REQUEST); | ||
| 172 | out: | ||
| 173 | kfree(requested_dev_name); | ||
| 174 | kfree(requested_dir_name); | ||
| 175 | if (fstype) | ||
| 176 | put_filesystem(fstype); | ||
| 177 | kfree(requested_type); | ||
| 178 | return error; | ||
| 179 | } | ||
| 180 | |||
| 181 | /** | ||
| 182 | * tomoyo_mount_permission - Check permission for mount() operation. | ||
| 183 | * | ||
| 184 | * @dev_name: Name of device file. | ||
| 185 | * @path: Pointer to "struct path". | ||
| 186 | * @type: Name of filesystem type. May be NULL. | ||
| 187 | * @flags: Mount options. | ||
| 188 | * @data_page: Optional data. May be NULL. | ||
| 189 | * | ||
| 190 | * Returns 0 on success, negative value otherwise. | ||
| 191 | */ | ||
| 192 | int tomoyo_mount_permission(char *dev_name, struct path *path, char *type, | ||
| 193 | unsigned long flags, void *data_page) | ||
| 194 | { | ||
| 195 | struct tomoyo_request_info r; | ||
| 196 | int error; | ||
| 197 | int idx; | ||
| 198 | |||
| 199 | if (tomoyo_init_request_info(&r, NULL, TOMOYO_MAC_FILE_MOUNT) | ||
| 200 | == TOMOYO_CONFIG_DISABLED) | ||
| 201 | return 0; | ||
| 202 | if ((flags & MS_MGC_MSK) == MS_MGC_VAL) | ||
| 203 | flags &= ~MS_MGC_MSK; | ||
| 204 | if (flags & MS_REMOUNT) { | ||
| 205 | type = TOMOYO_MOUNT_REMOUNT_KEYWORD; | ||
| 206 | flags &= ~MS_REMOUNT; | ||
| 207 | } | ||
| 208 | if (flags & MS_MOVE) { | ||
| 209 | type = TOMOYO_MOUNT_MOVE_KEYWORD; | ||
| 210 | flags &= ~MS_MOVE; | ||
| 211 | } | ||
| 212 | if (flags & MS_BIND) { | ||
| 213 | type = TOMOYO_MOUNT_BIND_KEYWORD; | ||
| 214 | flags &= ~MS_BIND; | ||
| 215 | } | ||
| 216 | if (flags & MS_UNBINDABLE) { | ||
| 217 | type = TOMOYO_MOUNT_MAKE_UNBINDABLE_KEYWORD; | ||
| 218 | flags &= ~MS_UNBINDABLE; | ||
| 219 | } | ||
| 220 | if (flags & MS_PRIVATE) { | ||
| 221 | type = TOMOYO_MOUNT_MAKE_PRIVATE_KEYWORD; | ||
| 222 | flags &= ~MS_PRIVATE; | ||
| 223 | } | ||
| 224 | if (flags & MS_SLAVE) { | ||
| 225 | type = TOMOYO_MOUNT_MAKE_SLAVE_KEYWORD; | ||
| 226 | flags &= ~MS_SLAVE; | ||
| 227 | } | ||
| 228 | if (flags & MS_SHARED) { | ||
| 229 | type = TOMOYO_MOUNT_MAKE_SHARED_KEYWORD; | ||
| 230 | flags &= ~MS_SHARED; | ||
| 231 | } | ||
| 232 | if (!type) | ||
| 233 | type = "<NULL>"; | ||
| 234 | idx = tomoyo_read_lock(); | ||
| 235 | error = tomoyo_mount_acl(&r, dev_name, path, type, flags); | ||
| 236 | tomoyo_read_unlock(idx); | ||
| 237 | return error; | ||
| 238 | } | ||
| 239 | |||
| 240 | static bool tomoyo_same_mount_acl(const struct tomoyo_acl_info *a, | ||
| 241 | const struct tomoyo_acl_info *b) | ||
| 242 | { | ||
| 243 | const struct tomoyo_mount_acl *p1 = container_of(a, typeof(*p1), head); | ||
| 244 | const struct tomoyo_mount_acl *p2 = container_of(b, typeof(*p2), head); | ||
| 245 | return tomoyo_same_acl_head(&p1->head, &p2->head) && | ||
| 246 | tomoyo_same_name_union(&p1->dev_name, &p2->dev_name) && | ||
| 247 | tomoyo_same_name_union(&p1->dir_name, &p2->dir_name) && | ||
| 248 | tomoyo_same_name_union(&p1->fs_type, &p2->fs_type) && | ||
| 249 | tomoyo_same_number_union(&p1->flags, &p2->flags); | ||
| 250 | } | ||
| 251 | |||
| 252 | /** | ||
| 253 | * tomoyo_write_mount - Write "struct tomoyo_mount_acl" list. | ||
| 254 | * | ||
| 255 | * @data: String to parse. | ||
| 256 | * @domain: Pointer to "struct tomoyo_domain_info". | ||
| 257 | * @is_delete: True if it is a delete request. | ||
| 258 | * | ||
| 259 | * Returns 0 on success, negative value otherwise. | ||
| 260 | * | ||
| 261 | * Caller holds tomoyo_read_lock(). | ||
| 262 | */ | ||
| 263 | int tomoyo_write_mount(char *data, struct tomoyo_domain_info *domain, | ||
| 264 | const bool is_delete) | ||
| 265 | { | ||
| 266 | struct tomoyo_mount_acl e = { .head.type = TOMOYO_TYPE_MOUNT_ACL }; | ||
| 267 | int error = is_delete ? -ENOENT : -ENOMEM; | ||
| 268 | char *w[4]; | ||
| 269 | if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[3][0]) | ||
| 270 | return -EINVAL; | ||
| 271 | if (!tomoyo_parse_name_union(w[0], &e.dev_name) || | ||
| 272 | !tomoyo_parse_name_union(w[1], &e.dir_name) || | ||
| 273 | !tomoyo_parse_name_union(w[2], &e.fs_type) || | ||
| 274 | !tomoyo_parse_number_union(w[3], &e.flags)) | ||
| 275 | goto out; | ||
| 276 | error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain, | ||
| 277 | tomoyo_same_mount_acl, NULL); | ||
| 278 | out: | ||
| 279 | tomoyo_put_name_union(&e.dev_name); | ||
| 280 | tomoyo_put_name_union(&e.dir_name); | ||
| 281 | tomoyo_put_name_union(&e.fs_type); | ||
| 282 | tomoyo_put_number_union(&e.flags); | ||
| 283 | return error; | ||
| 284 | } | ||
diff --git a/security/tomoyo/path_group.c b/security/tomoyo/path_group.c deleted file mode 100644 index c988041c8e1c..000000000000 --- a/security/tomoyo/path_group.c +++ /dev/null | |||
| @@ -1,172 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * security/tomoyo/path_group.c | ||
| 3 | * | ||
| 4 | * Copyright (C) 2005-2009 NTT DATA CORPORATION | ||
| 5 | */ | ||
| 6 | |||
| 7 | #include <linux/slab.h> | ||
| 8 | #include "common.h" | ||
| 9 | /* The list for "struct ccs_path_group". */ | ||
| 10 | LIST_HEAD(tomoyo_path_group_list); | ||
| 11 | |||
| 12 | /** | ||
| 13 | * tomoyo_get_path_group - Allocate memory for "struct tomoyo_path_group". | ||
| 14 | * | ||
| 15 | * @group_name: The name of pathname group. | ||
| 16 | * | ||
| 17 | * Returns pointer to "struct tomoyo_path_group" on success, NULL otherwise. | ||
| 18 | */ | ||
| 19 | struct tomoyo_path_group *tomoyo_get_path_group(const char *group_name) | ||
| 20 | { | ||
| 21 | struct tomoyo_path_group *entry = NULL; | ||
| 22 | struct tomoyo_path_group *group = NULL; | ||
| 23 | const struct tomoyo_path_info *saved_group_name; | ||
| 24 | int error = -ENOMEM; | ||
| 25 | if (!tomoyo_is_correct_path(group_name, 0, 0, 0) || | ||
| 26 | !group_name[0]) | ||
| 27 | return NULL; | ||
| 28 | saved_group_name = tomoyo_get_name(group_name); | ||
| 29 | if (!saved_group_name) | ||
| 30 | return NULL; | ||
| 31 | entry = kzalloc(sizeof(*entry), GFP_NOFS); | ||
| 32 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) | ||
| 33 | goto out; | ||
| 34 | list_for_each_entry_rcu(group, &tomoyo_path_group_list, list) { | ||
| 35 | if (saved_group_name != group->group_name) | ||
| 36 | continue; | ||
| 37 | atomic_inc(&group->users); | ||
| 38 | error = 0; | ||
| 39 | break; | ||
| 40 | } | ||
| 41 | if (error && tomoyo_memory_ok(entry)) { | ||
| 42 | INIT_LIST_HEAD(&entry->member_list); | ||
| 43 | entry->group_name = saved_group_name; | ||
| 44 | saved_group_name = NULL; | ||
| 45 | atomic_set(&entry->users, 1); | ||
| 46 | list_add_tail_rcu(&entry->list, &tomoyo_path_group_list); | ||
| 47 | group = entry; | ||
| 48 | entry = NULL; | ||
| 49 | error = 0; | ||
| 50 | } | ||
| 51 | mutex_unlock(&tomoyo_policy_lock); | ||
| 52 | out: | ||
| 53 | tomoyo_put_name(saved_group_name); | ||
| 54 | kfree(entry); | ||
| 55 | return !error ? group : NULL; | ||
| 56 | } | ||
| 57 | |||
| 58 | /** | ||
| 59 | * tomoyo_write_path_group_policy - Write "struct tomoyo_path_group" list. | ||
| 60 | * | ||
| 61 | * @data: String to parse. | ||
| 62 | * @is_delete: True if it is a delete request. | ||
| 63 | * | ||
| 64 | * Returns 0 on success, nagative value otherwise. | ||
| 65 | */ | ||
| 66 | int tomoyo_write_path_group_policy(char *data, const bool is_delete) | ||
| 67 | { | ||
| 68 | struct tomoyo_path_group *group; | ||
| 69 | struct tomoyo_path_group_member *member; | ||
| 70 | struct tomoyo_path_group_member e = { }; | ||
| 71 | int error = is_delete ? -ENOENT : -ENOMEM; | ||
| 72 | char *w[2]; | ||
| 73 | if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[1][0]) | ||
| 74 | return -EINVAL; | ||
| 75 | group = tomoyo_get_path_group(w[0]); | ||
| 76 | if (!group) | ||
| 77 | return -ENOMEM; | ||
| 78 | e.member_name = tomoyo_get_name(w[1]); | ||
| 79 | if (!e.member_name) | ||
| 80 | goto out; | ||
| 81 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) | ||
| 82 | goto out; | ||
| 83 | list_for_each_entry_rcu(member, &group->member_list, list) { | ||
| 84 | if (member->member_name != e.member_name) | ||
| 85 | continue; | ||
| 86 | member->is_deleted = is_delete; | ||
| 87 | error = 0; | ||
| 88 | break; | ||
| 89 | } | ||
| 90 | if (!is_delete && error) { | ||
| 91 | struct tomoyo_path_group_member *entry = | ||
| 92 | tomoyo_commit_ok(&e, sizeof(e)); | ||
| 93 | if (entry) { | ||
| 94 | list_add_tail_rcu(&entry->list, &group->member_list); | ||
| 95 | error = 0; | ||
| 96 | } | ||
| 97 | } | ||
| 98 | mutex_unlock(&tomoyo_policy_lock); | ||
| 99 | out: | ||
| 100 | tomoyo_put_name(e.member_name); | ||
| 101 | tomoyo_put_path_group(group); | ||
| 102 | return error; | ||
| 103 | } | ||
| 104 | |||
| 105 | /** | ||
| 106 | * tomoyo_read_path_group_policy - Read "struct tomoyo_path_group" list. | ||
| 107 | * | ||
| 108 | * @head: Pointer to "struct tomoyo_io_buffer". | ||
| 109 | * | ||
| 110 | * Returns true on success, false otherwise. | ||
| 111 | * | ||
| 112 | * Caller holds tomoyo_read_lock(). | ||
| 113 | */ | ||
| 114 | bool tomoyo_read_path_group_policy(struct tomoyo_io_buffer *head) | ||
| 115 | { | ||
| 116 | struct list_head *gpos; | ||
| 117 | struct list_head *mpos; | ||
| 118 | list_for_each_cookie(gpos, head->read_var1, &tomoyo_path_group_list) { | ||
| 119 | struct tomoyo_path_group *group; | ||
| 120 | group = list_entry(gpos, struct tomoyo_path_group, list); | ||
| 121 | list_for_each_cookie(mpos, head->read_var2, | ||
| 122 | &group->member_list) { | ||
| 123 | struct tomoyo_path_group_member *member; | ||
| 124 | member = list_entry(mpos, | ||
| 125 | struct tomoyo_path_group_member, | ||
| 126 | list); | ||
| 127 | if (member->is_deleted) | ||
| 128 | continue; | ||
| 129 | if (!tomoyo_io_printf(head, TOMOYO_KEYWORD_PATH_GROUP | ||
| 130 | "%s %s\n", | ||
| 131 | group->group_name->name, | ||
| 132 | member->member_name->name)) | ||
| 133 | return false; | ||
| 134 | } | ||
| 135 | } | ||
| 136 | return true; | ||
| 137 | } | ||
| 138 | |||
| 139 | /** | ||
| 140 | * tomoyo_path_matches_group - Check whether the given pathname matches members of the given pathname group. | ||
| 141 | * | ||
| 142 | * @pathname: The name of pathname. | ||
| 143 | * @group: Pointer to "struct tomoyo_path_group". | ||
| 144 | * @may_use_pattern: True if wild card is permitted. | ||
| 145 | * | ||
| 146 | * Returns true if @pathname matches pathnames in @group, false otherwise. | ||
| 147 | * | ||
| 148 | * Caller holds tomoyo_read_lock(). | ||
| 149 | */ | ||
| 150 | bool tomoyo_path_matches_group(const struct tomoyo_path_info *pathname, | ||
| 151 | const struct tomoyo_path_group *group, | ||
| 152 | const bool may_use_pattern) | ||
| 153 | { | ||
| 154 | struct tomoyo_path_group_member *member; | ||
| 155 | bool matched = false; | ||
| 156 | list_for_each_entry_rcu(member, &group->member_list, list) { | ||
| 157 | if (member->is_deleted) | ||
| 158 | continue; | ||
| 159 | if (!member->member_name->is_patterned) { | ||
| 160 | if (tomoyo_pathcmp(pathname, member->member_name)) | ||
| 161 | continue; | ||
| 162 | } else if (may_use_pattern) { | ||
| 163 | if (!tomoyo_path_matches_pattern(pathname, | ||
| 164 | member->member_name)) | ||
| 165 | continue; | ||
| 166 | } else | ||
| 167 | continue; | ||
| 168 | matched = true; | ||
| 169 | break; | ||
| 170 | } | ||
| 171 | return matched; | ||
| 172 | } | ||
diff --git a/security/tomoyo/realpath.c b/security/tomoyo/realpath.c index d1b96f019621..ed8ccd680102 100644 --- a/security/tomoyo/realpath.c +++ b/security/tomoyo/realpath.c | |||
| @@ -1,174 +1,164 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * security/tomoyo/realpath.c | 2 | * security/tomoyo/realpath.c |
| 3 | * | 3 | * |
| 4 | * Get the canonicalized absolute pathnames. The basis for TOMOYO. | 4 | * Pathname calculation functions for TOMOYO. |
| 5 | * | ||
| 6 | * Copyright (C) 2005-2009 NTT DATA CORPORATION | ||
| 7 | * | ||
| 8 | * Version: 2.2.0 2009/04/01 | ||
| 9 | * | 5 | * |
| 6 | * Copyright (C) 2005-2010 NTT DATA CORPORATION | ||
| 10 | */ | 7 | */ |
| 11 | 8 | ||
| 12 | #include <linux/types.h> | 9 | #include <linux/types.h> |
| 13 | #include <linux/mount.h> | 10 | #include <linux/mount.h> |
| 14 | #include <linux/mnt_namespace.h> | 11 | #include <linux/mnt_namespace.h> |
| 15 | #include <linux/fs_struct.h> | 12 | #include <linux/fs_struct.h> |
| 16 | #include <linux/hash.h> | ||
| 17 | #include <linux/magic.h> | 13 | #include <linux/magic.h> |
| 18 | #include <linux/slab.h> | 14 | #include <linux/slab.h> |
| 15 | #include <net/sock.h> | ||
| 19 | #include "common.h" | 16 | #include "common.h" |
| 20 | 17 | ||
| 21 | /** | 18 | /** |
| 22 | * tomoyo_encode: Convert binary string to ascii string. | 19 | * tomoyo_encode: Convert binary string to ascii string. |
| 23 | * | 20 | * |
| 24 | * @buffer: Buffer for ASCII string. | 21 | * @str: String in binary format. |
| 25 | * @buflen: Size of @buffer. | 22 | * |
| 26 | * @str: Binary string. | 23 | * Returns pointer to @str in ascii format on success, NULL otherwise. |
| 27 | * | 24 | * |
| 28 | * Returns 0 on success, -ENOMEM otherwise. | 25 | * This function uses kzalloc(), so caller must kfree() if this function |
| 26 | * didn't return NULL. | ||
| 29 | */ | 27 | */ |
| 30 | int tomoyo_encode(char *buffer, int buflen, const char *str) | 28 | char *tomoyo_encode(const char *str) |
| 31 | { | 29 | { |
| 32 | while (1) { | 30 | int len = 0; |
| 33 | const unsigned char c = *(unsigned char *) str++; | 31 | const char *p = str; |
| 32 | char *cp; | ||
| 33 | char *cp0; | ||
| 34 | 34 | ||
| 35 | if (tomoyo_is_valid(c)) { | 35 | if (!p) |
| 36 | if (--buflen <= 0) | 36 | return NULL; |
| 37 | break; | 37 | while (*p) { |
| 38 | *buffer++ = (char) c; | 38 | const unsigned char c = *p++; |
| 39 | if (c != '\\') | 39 | if (c == '\\') |
| 40 | continue; | 40 | len += 2; |
| 41 | if (--buflen <= 0) | 41 | else if (c > ' ' && c < 127) |
| 42 | break; | 42 | len++; |
| 43 | *buffer++ = (char) c; | 43 | else |
| 44 | continue; | 44 | len += 4; |
| 45 | } | 45 | } |
| 46 | if (!c) { | 46 | len++; |
| 47 | if (--buflen <= 0) | 47 | /* Reserve space for appending "/". */ |
| 48 | break; | 48 | cp = kzalloc(len + 10, GFP_NOFS); |
| 49 | *buffer = '\0'; | 49 | if (!cp) |
| 50 | return 0; | 50 | return NULL; |
| 51 | cp0 = cp; | ||
| 52 | p = str; | ||
| 53 | while (*p) { | ||
| 54 | const unsigned char c = *p++; | ||
| 55 | |||
| 56 | if (c == '\\') { | ||
| 57 | *cp++ = '\\'; | ||
| 58 | *cp++ = '\\'; | ||
| 59 | } else if (c > ' ' && c < 127) { | ||
| 60 | *cp++ = c; | ||
| 61 | } else { | ||
| 62 | *cp++ = '\\'; | ||
| 63 | *cp++ = (c >> 6) + '0'; | ||
| 64 | *cp++ = ((c >> 3) & 7) + '0'; | ||
| 65 | *cp++ = (c & 7) + '0'; | ||
| 51 | } | 66 | } |
| 52 | buflen -= 4; | ||
| 53 | if (buflen <= 0) | ||
| 54 | break; | ||
| 55 | *buffer++ = '\\'; | ||
| 56 | *buffer++ = (c >> 6) + '0'; | ||
| 57 | *buffer++ = ((c >> 3) & 7) + '0'; | ||
| 58 | *buffer++ = (c & 7) + '0'; | ||
| 59 | } | 67 | } |
| 60 | return -ENOMEM; | 68 | return cp0; |
| 61 | } | 69 | } |
| 62 | 70 | ||
| 63 | /** | 71 | /** |
| 64 | * tomoyo_realpath_from_path2 - Returns realpath(3) of the given dentry but ignores chroot'ed root. | 72 | * tomoyo_realpath_from_path - Returns realpath(3) of the given pathname but ignores chroot'ed root. |
| 65 | * | 73 | * |
| 66 | * @path: Pointer to "struct path". | 74 | * @path: Pointer to "struct path". |
| 67 | * @newname: Pointer to buffer to return value in. | ||
| 68 | * @newname_len: Size of @newname. | ||
| 69 | * | 75 | * |
| 70 | * Returns 0 on success, negative value otherwise. | 76 | * Returns the realpath of the given @path on success, NULL otherwise. |
| 71 | * | 77 | * |
| 72 | * If dentry is a directory, trailing '/' is appended. | 78 | * If dentry is a directory, trailing '/' is appended. |
| 73 | * Characters out of 0x20 < c < 0x7F range are converted to | 79 | * Characters out of 0x20 < c < 0x7F range are converted to |
| 74 | * \ooo style octal string. | 80 | * \ooo style octal string. |
| 75 | * Character \ is converted to \\ string. | 81 | * Character \ is converted to \\ string. |
| 82 | * | ||
| 83 | * These functions use kzalloc(), so the caller must call kfree() | ||
| 84 | * if these functions didn't return NULL. | ||
| 76 | */ | 85 | */ |
| 77 | int tomoyo_realpath_from_path2(struct path *path, char *newname, | 86 | char *tomoyo_realpath_from_path(struct path *path) |
| 78 | int newname_len) | ||
| 79 | { | 87 | { |
| 80 | int error = -ENOMEM; | 88 | char *buf = NULL; |
| 89 | char *name = NULL; | ||
| 90 | unsigned int buf_len = PAGE_SIZE / 2; | ||
| 81 | struct dentry *dentry = path->dentry; | 91 | struct dentry *dentry = path->dentry; |
| 82 | char *sp; | 92 | bool is_dir; |
| 83 | 93 | if (!dentry) | |
| 84 | if (!dentry || !path->mnt || !newname || newname_len <= 2048) | 94 | return NULL; |
| 85 | return -EINVAL; | 95 | is_dir = dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode); |
| 86 | if (dentry->d_op && dentry->d_op->d_dname) { | 96 | while (1) { |
| 97 | struct path ns_root = { .mnt = NULL, .dentry = NULL }; | ||
| 98 | char *pos; | ||
| 99 | buf_len <<= 1; | ||
| 100 | kfree(buf); | ||
| 101 | buf = kmalloc(buf_len, GFP_NOFS); | ||
| 102 | if (!buf) | ||
| 103 | break; | ||
| 104 | /* Get better name for socket. */ | ||
| 105 | if (dentry->d_sb && dentry->d_sb->s_magic == SOCKFS_MAGIC) { | ||
| 106 | struct inode *inode = dentry->d_inode; | ||
| 107 | struct socket *sock = inode ? SOCKET_I(inode) : NULL; | ||
| 108 | struct sock *sk = sock ? sock->sk : NULL; | ||
| 109 | if (sk) { | ||
| 110 | snprintf(buf, buf_len - 1, "socket:[family=%u:" | ||
| 111 | "type=%u:protocol=%u]", sk->sk_family, | ||
| 112 | sk->sk_type, sk->sk_protocol); | ||
| 113 | } else { | ||
| 114 | snprintf(buf, buf_len - 1, "socket:[unknown]"); | ||
| 115 | } | ||
| 116 | name = tomoyo_encode(buf); | ||
| 117 | break; | ||
| 118 | } | ||
| 87 | /* For "socket:[\$]" and "pipe:[\$]". */ | 119 | /* For "socket:[\$]" and "pipe:[\$]". */ |
| 88 | static const int offset = 1536; | 120 | if (dentry->d_op && dentry->d_op->d_dname) { |
| 89 | sp = dentry->d_op->d_dname(dentry, newname + offset, | 121 | pos = dentry->d_op->d_dname(dentry, buf, buf_len - 1); |
| 90 | newname_len - offset); | 122 | if (IS_ERR(pos)) |
| 91 | } else { | 123 | continue; |
| 92 | struct path ns_root = {.mnt = NULL, .dentry = NULL}; | 124 | name = tomoyo_encode(pos); |
| 93 | 125 | break; | |
| 126 | } | ||
| 127 | /* If we don't have a vfsmount, we can't calculate. */ | ||
| 128 | if (!path->mnt) | ||
| 129 | break; | ||
| 94 | spin_lock(&dcache_lock); | 130 | spin_lock(&dcache_lock); |
| 95 | /* go to whatever namespace root we are under */ | 131 | /* go to whatever namespace root we are under */ |
| 96 | sp = __d_path(path, &ns_root, newname, newname_len); | 132 | pos = __d_path(path, &ns_root, buf, buf_len); |
| 97 | spin_unlock(&dcache_lock); | 133 | spin_unlock(&dcache_lock); |
| 98 | /* Prepend "/proc" prefix if using internal proc vfs mount. */ | 134 | /* Prepend "/proc" prefix if using internal proc vfs mount. */ |
| 99 | if (!IS_ERR(sp) && (path->mnt->mnt_flags & MNT_INTERNAL) && | 135 | if (!IS_ERR(pos) && (path->mnt->mnt_flags & MNT_INTERNAL) && |
| 100 | (path->mnt->mnt_sb->s_magic == PROC_SUPER_MAGIC)) { | 136 | (path->mnt->mnt_sb->s_magic == PROC_SUPER_MAGIC)) { |
| 101 | sp -= 5; | 137 | pos -= 5; |
| 102 | if (sp >= newname) | 138 | if (pos >= buf) |
| 103 | memcpy(sp, "/proc", 5); | 139 | memcpy(pos, "/proc", 5); |
| 104 | else | 140 | else |
| 105 | sp = ERR_PTR(-ENOMEM); | 141 | pos = ERR_PTR(-ENOMEM); |
| 106 | } | ||
| 107 | } | ||
| 108 | if (IS_ERR(sp)) | ||
| 109 | error = PTR_ERR(sp); | ||
| 110 | else | ||
| 111 | error = tomoyo_encode(newname, sp - newname, sp); | ||
| 112 | /* Append trailing '/' if dentry is a directory. */ | ||
| 113 | if (!error && dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode) | ||
| 114 | && *newname) { | ||
| 115 | sp = newname + strlen(newname); | ||
| 116 | if (*(sp - 1) != '/') { | ||
| 117 | if (sp < newname + newname_len - 4) { | ||
| 118 | *sp++ = '/'; | ||
| 119 | *sp = '\0'; | ||
| 120 | } else { | ||
| 121 | error = -ENOMEM; | ||
| 122 | } | ||
| 123 | } | 142 | } |
| 143 | if (IS_ERR(pos)) | ||
| 144 | continue; | ||
| 145 | name = tomoyo_encode(pos); | ||
| 146 | break; | ||
| 124 | } | 147 | } |
| 125 | if (error) | ||
| 126 | printk(KERN_WARNING "tomoyo_realpath: Pathname too long.\n"); | ||
| 127 | return error; | ||
| 128 | } | ||
| 129 | |||
| 130 | /** | ||
| 131 | * tomoyo_realpath_from_path - Returns realpath(3) of the given pathname but ignores chroot'ed root. | ||
| 132 | * | ||
| 133 | * @path: Pointer to "struct path". | ||
| 134 | * | ||
| 135 | * Returns the realpath of the given @path on success, NULL otherwise. | ||
| 136 | * | ||
| 137 | * These functions use kzalloc(), so the caller must call kfree() | ||
| 138 | * if these functions didn't return NULL. | ||
| 139 | */ | ||
| 140 | char *tomoyo_realpath_from_path(struct path *path) | ||
| 141 | { | ||
| 142 | char *buf = kzalloc(sizeof(struct tomoyo_page_buffer), GFP_NOFS); | ||
| 143 | |||
| 144 | BUILD_BUG_ON(sizeof(struct tomoyo_page_buffer) | ||
| 145 | <= TOMOYO_MAX_PATHNAME_LEN - 1); | ||
| 146 | if (!buf) | ||
| 147 | return NULL; | ||
| 148 | if (tomoyo_realpath_from_path2(path, buf, | ||
| 149 | TOMOYO_MAX_PATHNAME_LEN - 1) == 0) | ||
| 150 | return buf; | ||
| 151 | kfree(buf); | 148 | kfree(buf); |
| 152 | return NULL; | 149 | if (!name) |
| 153 | } | 150 | tomoyo_warn_oom(__func__); |
| 154 | 151 | else if (is_dir && *name) { | |
| 155 | /** | 152 | /* Append trailing '/' if dentry is a directory. */ |
| 156 | * tomoyo_realpath - Get realpath of a pathname. | 153 | char *pos = name + strlen(name) - 1; |
| 157 | * | 154 | if (*pos != '/') |
| 158 | * @pathname: The pathname to solve. | 155 | /* |
| 159 | * | 156 | * This is OK because tomoyo_encode() reserves space |
| 160 | * Returns the realpath of @pathname on success, NULL otherwise. | 157 | * for appending "/". |
| 161 | */ | 158 | */ |
| 162 | char *tomoyo_realpath(const char *pathname) | 159 | *++pos = '/'; |
| 163 | { | ||
| 164 | struct path path; | ||
| 165 | |||
| 166 | if (pathname && kern_path(pathname, LOOKUP_FOLLOW, &path) == 0) { | ||
| 167 | char *buf = tomoyo_realpath_from_path(&path); | ||
| 168 | path_put(&path); | ||
| 169 | return buf; | ||
| 170 | } | 160 | } |
| 171 | return NULL; | 161 | return name; |
| 172 | } | 162 | } |
| 173 | 163 | ||
| 174 | /** | 164 | /** |
| @@ -189,191 +179,3 @@ char *tomoyo_realpath_nofollow(const char *pathname) | |||
| 189 | } | 179 | } |
| 190 | return NULL; | 180 | return NULL; |
| 191 | } | 181 | } |
| 192 | |||
| 193 | /* Memory allocated for non-string data. */ | ||
| 194 | static atomic_t tomoyo_policy_memory_size; | ||
| 195 | /* Quota for holding policy. */ | ||
| 196 | static unsigned int tomoyo_quota_for_policy; | ||
| 197 | |||
| 198 | /** | ||
| 199 | * tomoyo_memory_ok - Check memory quota. | ||
| 200 | * | ||
| 201 | * @ptr: Pointer to allocated memory. | ||
| 202 | * | ||
| 203 | * Returns true on success, false otherwise. | ||
| 204 | * | ||
| 205 | * Caller holds tomoyo_policy_lock. | ||
| 206 | * Memory pointed by @ptr will be zeroed on success. | ||
| 207 | */ | ||
| 208 | bool tomoyo_memory_ok(void *ptr) | ||
| 209 | { | ||
| 210 | int allocated_len = ptr ? ksize(ptr) : 0; | ||
| 211 | atomic_add(allocated_len, &tomoyo_policy_memory_size); | ||
| 212 | if (ptr && (!tomoyo_quota_for_policy || | ||
| 213 | atomic_read(&tomoyo_policy_memory_size) | ||
| 214 | <= tomoyo_quota_for_policy)) { | ||
| 215 | memset(ptr, 0, allocated_len); | ||
| 216 | return true; | ||
| 217 | } | ||
| 218 | printk(KERN_WARNING "ERROR: Out of memory " | ||
| 219 | "for tomoyo_alloc_element().\n"); | ||
| 220 | if (!tomoyo_policy_loaded) | ||
| 221 | panic("MAC Initialization failed.\n"); | ||
| 222 | return false; | ||
| 223 | } | ||
| 224 | |||
| 225 | /** | ||
| 226 | * tomoyo_commit_ok - Check memory quota. | ||
| 227 | * | ||
| 228 | * @data: Data to copy from. | ||
| 229 | * @size: Size in byte. | ||
| 230 | * | ||
| 231 | * Returns pointer to allocated memory on success, NULL otherwise. | ||
| 232 | */ | ||
| 233 | void *tomoyo_commit_ok(void *data, const unsigned int size) | ||
| 234 | { | ||
| 235 | void *ptr = kzalloc(size, GFP_NOFS); | ||
| 236 | if (tomoyo_memory_ok(ptr)) { | ||
| 237 | memmove(ptr, data, size); | ||
| 238 | memset(data, 0, size); | ||
| 239 | return ptr; | ||
| 240 | } | ||
| 241 | return NULL; | ||
| 242 | } | ||
| 243 | |||
| 244 | /** | ||
| 245 | * tomoyo_memory_free - Free memory for elements. | ||
| 246 | * | ||
| 247 | * @ptr: Pointer to allocated memory. | ||
| 248 | */ | ||
| 249 | void tomoyo_memory_free(void *ptr) | ||
| 250 | { | ||
| 251 | atomic_sub(ksize(ptr), &tomoyo_policy_memory_size); | ||
| 252 | kfree(ptr); | ||
| 253 | } | ||
| 254 | |||
| 255 | /* | ||
| 256 | * tomoyo_name_list is used for holding string data used by TOMOYO. | ||
| 257 | * Since same string data is likely used for multiple times (e.g. | ||
| 258 | * "/lib/libc-2.5.so"), TOMOYO shares string data in the form of | ||
| 259 | * "const struct tomoyo_path_info *". | ||
| 260 | */ | ||
| 261 | struct list_head tomoyo_name_list[TOMOYO_MAX_HASH]; | ||
| 262 | |||
| 263 | /** | ||
| 264 | * tomoyo_get_name - Allocate permanent memory for string data. | ||
| 265 | * | ||
| 266 | * @name: The string to store into the permernent memory. | ||
| 267 | * | ||
| 268 | * Returns pointer to "struct tomoyo_path_info" on success, NULL otherwise. | ||
| 269 | */ | ||
| 270 | const struct tomoyo_path_info *tomoyo_get_name(const char *name) | ||
| 271 | { | ||
| 272 | struct tomoyo_name_entry *ptr; | ||
| 273 | unsigned int hash; | ||
| 274 | int len; | ||
| 275 | int allocated_len; | ||
| 276 | struct list_head *head; | ||
| 277 | |||
| 278 | if (!name) | ||
| 279 | return NULL; | ||
| 280 | len = strlen(name) + 1; | ||
| 281 | hash = full_name_hash((const unsigned char *) name, len - 1); | ||
| 282 | head = &tomoyo_name_list[hash_long(hash, TOMOYO_HASH_BITS)]; | ||
| 283 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) | ||
| 284 | return NULL; | ||
| 285 | list_for_each_entry(ptr, head, list) { | ||
| 286 | if (hash != ptr->entry.hash || strcmp(name, ptr->entry.name)) | ||
| 287 | continue; | ||
| 288 | atomic_inc(&ptr->users); | ||
| 289 | goto out; | ||
| 290 | } | ||
| 291 | ptr = kzalloc(sizeof(*ptr) + len, GFP_NOFS); | ||
| 292 | allocated_len = ptr ? ksize(ptr) : 0; | ||
| 293 | if (!ptr || (tomoyo_quota_for_policy && | ||
| 294 | atomic_read(&tomoyo_policy_memory_size) + allocated_len | ||
| 295 | > tomoyo_quota_for_policy)) { | ||
| 296 | kfree(ptr); | ||
| 297 | printk(KERN_WARNING "ERROR: Out of memory " | ||
| 298 | "for tomoyo_get_name().\n"); | ||
| 299 | if (!tomoyo_policy_loaded) | ||
| 300 | panic("MAC Initialization failed.\n"); | ||
| 301 | ptr = NULL; | ||
| 302 | goto out; | ||
| 303 | } | ||
| 304 | atomic_add(allocated_len, &tomoyo_policy_memory_size); | ||
| 305 | ptr->entry.name = ((char *) ptr) + sizeof(*ptr); | ||
| 306 | memmove((char *) ptr->entry.name, name, len); | ||
| 307 | atomic_set(&ptr->users, 1); | ||
| 308 | tomoyo_fill_path_info(&ptr->entry); | ||
| 309 | list_add_tail(&ptr->list, head); | ||
| 310 | out: | ||
| 311 | mutex_unlock(&tomoyo_policy_lock); | ||
| 312 | return ptr ? &ptr->entry : NULL; | ||
| 313 | } | ||
| 314 | |||
| 315 | /** | ||
| 316 | * tomoyo_realpath_init - Initialize realpath related code. | ||
| 317 | */ | ||
| 318 | void __init tomoyo_realpath_init(void) | ||
| 319 | { | ||
| 320 | int i; | ||
| 321 | |||
| 322 | BUILD_BUG_ON(TOMOYO_MAX_PATHNAME_LEN > PATH_MAX); | ||
| 323 | for (i = 0; i < TOMOYO_MAX_HASH; i++) | ||
| 324 | INIT_LIST_HEAD(&tomoyo_name_list[i]); | ||
| 325 | INIT_LIST_HEAD(&tomoyo_kernel_domain.acl_info_list); | ||
| 326 | tomoyo_kernel_domain.domainname = tomoyo_get_name(TOMOYO_ROOT_NAME); | ||
| 327 | /* | ||
| 328 | * tomoyo_read_lock() is not needed because this function is | ||
| 329 | * called before the first "delete" request. | ||
| 330 | */ | ||
| 331 | list_add_tail_rcu(&tomoyo_kernel_domain.list, &tomoyo_domain_list); | ||
| 332 | if (tomoyo_find_domain(TOMOYO_ROOT_NAME) != &tomoyo_kernel_domain) | ||
| 333 | panic("Can't register tomoyo_kernel_domain"); | ||
| 334 | } | ||
| 335 | |||
| 336 | /** | ||
| 337 | * tomoyo_read_memory_counter - Check for memory usage in bytes. | ||
| 338 | * | ||
| 339 | * @head: Pointer to "struct tomoyo_io_buffer". | ||
| 340 | * | ||
| 341 | * Returns memory usage. | ||
| 342 | */ | ||
| 343 | int tomoyo_read_memory_counter(struct tomoyo_io_buffer *head) | ||
| 344 | { | ||
| 345 | if (!head->read_eof) { | ||
| 346 | const unsigned int policy | ||
| 347 | = atomic_read(&tomoyo_policy_memory_size); | ||
| 348 | char buffer[64]; | ||
| 349 | |||
| 350 | memset(buffer, 0, sizeof(buffer)); | ||
| 351 | if (tomoyo_quota_for_policy) | ||
| 352 | snprintf(buffer, sizeof(buffer) - 1, | ||
| 353 | " (Quota: %10u)", | ||
| 354 | tomoyo_quota_for_policy); | ||
| 355 | else | ||
| 356 | buffer[0] = '\0'; | ||
| 357 | tomoyo_io_printf(head, "Policy: %10u%s\n", policy, buffer); | ||
| 358 | tomoyo_io_printf(head, "Total: %10u\n", policy); | ||
| 359 | head->read_eof = true; | ||
| 360 | } | ||
| 361 | return 0; | ||
| 362 | } | ||
| 363 | |||
| 364 | /** | ||
| 365 | * tomoyo_write_memory_quota - Set memory quota. | ||
| 366 | * | ||
| 367 | * @head: Pointer to "struct tomoyo_io_buffer". | ||
| 368 | * | ||
| 369 | * Returns 0. | ||
| 370 | */ | ||
| 371 | int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head) | ||
| 372 | { | ||
| 373 | char *data = head->write_buf; | ||
| 374 | unsigned int size; | ||
| 375 | |||
| 376 | if (sscanf(data, "Policy: %u", &size) == 1) | ||
| 377 | tomoyo_quota_for_policy = size; | ||
| 378 | return 0; | ||
| 379 | } | ||
diff --git a/security/tomoyo/securityfs_if.c b/security/tomoyo/securityfs_if.c new file mode 100644 index 000000000000..e43d5554b506 --- /dev/null +++ b/security/tomoyo/securityfs_if.c | |||
| @@ -0,0 +1,155 @@ | |||
| 1 | /* | ||
| 2 | * security/tomoyo/common.c | ||
| 3 | * | ||
| 4 | * Securityfs interface for TOMOYO. | ||
| 5 | * | ||
| 6 | * Copyright (C) 2005-2010 NTT DATA CORPORATION | ||
| 7 | */ | ||
| 8 | |||
| 9 | #include <linux/security.h> | ||
| 10 | #include "common.h" | ||
| 11 | |||
| 12 | /** | ||
| 13 | * tomoyo_open - open() for /sys/kernel/security/tomoyo/ interface. | ||
| 14 | * | ||
| 15 | * @inode: Pointer to "struct inode". | ||
| 16 | * @file: Pointer to "struct file". | ||
| 17 | * | ||
| 18 | * Returns 0 on success, negative value otherwise. | ||
| 19 | */ | ||
| 20 | static int tomoyo_open(struct inode *inode, struct file *file) | ||
| 21 | { | ||
| 22 | const int key = ((u8 *) file->f_path.dentry->d_inode->i_private) | ||
| 23 | - ((u8 *) NULL); | ||
| 24 | return tomoyo_open_control(key, file); | ||
| 25 | } | ||
| 26 | |||
| 27 | /** | ||
| 28 | * tomoyo_release - close() for /sys/kernel/security/tomoyo/ interface. | ||
| 29 | * | ||
| 30 | * @inode: Pointer to "struct inode". | ||
| 31 | * @file: Pointer to "struct file". | ||
| 32 | * | ||
| 33 | * Returns 0 on success, negative value otherwise. | ||
| 34 | */ | ||
| 35 | static int tomoyo_release(struct inode *inode, struct file *file) | ||
| 36 | { | ||
| 37 | return tomoyo_close_control(file); | ||
| 38 | } | ||
| 39 | |||
| 40 | /** | ||
| 41 | * tomoyo_poll - poll() for /proc/ccs/ interface. | ||
| 42 | * | ||
| 43 | * @file: Pointer to "struct file". | ||
| 44 | * @wait: Pointer to "poll_table". | ||
| 45 | * | ||
| 46 | * Returns 0 on success, negative value otherwise. | ||
| 47 | */ | ||
| 48 | static unsigned int tomoyo_poll(struct file *file, poll_table *wait) | ||
| 49 | { | ||
| 50 | return tomoyo_poll_control(file, wait); | ||
| 51 | } | ||
| 52 | |||
| 53 | /** | ||
| 54 | * tomoyo_read - read() for /sys/kernel/security/tomoyo/ interface. | ||
| 55 | * | ||
| 56 | * @file: Pointer to "struct file". | ||
| 57 | * @buf: Pointer to buffer. | ||
| 58 | * @count: Size of @buf. | ||
| 59 | * @ppos: Unused. | ||
| 60 | * | ||
| 61 | * Returns bytes read on success, negative value otherwise. | ||
| 62 | */ | ||
| 63 | static ssize_t tomoyo_read(struct file *file, char __user *buf, size_t count, | ||
| 64 | loff_t *ppos) | ||
| 65 | { | ||
| 66 | return tomoyo_read_control(file, buf, count); | ||
| 67 | } | ||
| 68 | |||
| 69 | /** | ||
| 70 | * tomoyo_write - write() for /sys/kernel/security/tomoyo/ interface. | ||
| 71 | * | ||
| 72 | * @file: Pointer to "struct file". | ||
| 73 | * @buf: Pointer to buffer. | ||
| 74 | * @count: Size of @buf. | ||
| 75 | * @ppos: Unused. | ||
| 76 | * | ||
| 77 | * Returns @count on success, negative value otherwise. | ||
| 78 | */ | ||
| 79 | static ssize_t tomoyo_write(struct file *file, const char __user *buf, | ||
| 80 | size_t count, loff_t *ppos) | ||
| 81 | { | ||
| 82 | return tomoyo_write_control(file, buf, count); | ||
| 83 | } | ||
| 84 | |||
| 85 | /* | ||
| 86 | * tomoyo_operations is a "struct file_operations" which is used for handling | ||
| 87 | * /sys/kernel/security/tomoyo/ interface. | ||
| 88 | * | ||
| 89 | * Some files under /sys/kernel/security/tomoyo/ directory accept open(O_RDWR). | ||
| 90 | * See tomoyo_io_buffer for internals. | ||
| 91 | */ | ||
| 92 | static const struct file_operations tomoyo_operations = { | ||
| 93 | .open = tomoyo_open, | ||
| 94 | .release = tomoyo_release, | ||
| 95 | .poll = tomoyo_poll, | ||
| 96 | .read = tomoyo_read, | ||
| 97 | .write = tomoyo_write, | ||
| 98 | .llseek = noop_llseek, | ||
| 99 | }; | ||
| 100 | |||
| 101 | /** | ||
| 102 | * tomoyo_create_entry - Create interface files under /sys/kernel/security/tomoyo/ directory. | ||
| 103 | * | ||
| 104 | * @name: The name of the interface file. | ||
| 105 | * @mode: The permission of the interface file. | ||
| 106 | * @parent: The parent directory. | ||
| 107 | * @key: Type of interface. | ||
| 108 | * | ||
| 109 | * Returns nothing. | ||
| 110 | */ | ||
| 111 | static void __init tomoyo_create_entry(const char *name, const mode_t mode, | ||
| 112 | struct dentry *parent, const u8 key) | ||
| 113 | { | ||
| 114 | securityfs_create_file(name, mode, parent, ((u8 *) NULL) + key, | ||
| 115 | &tomoyo_operations); | ||
| 116 | } | ||
| 117 | |||
| 118 | /** | ||
| 119 | * tomoyo_initerface_init - Initialize /sys/kernel/security/tomoyo/ interface. | ||
| 120 | * | ||
| 121 | * Returns 0. | ||
| 122 | */ | ||
| 123 | static int __init tomoyo_initerface_init(void) | ||
| 124 | { | ||
| 125 | struct dentry *tomoyo_dir; | ||
| 126 | |||
| 127 | /* Don't create securityfs entries unless registered. */ | ||
| 128 | if (current_cred()->security != &tomoyo_kernel_domain) | ||
| 129 | return 0; | ||
| 130 | |||
| 131 | tomoyo_dir = securityfs_create_dir("tomoyo", NULL); | ||
| 132 | tomoyo_create_entry("query", 0600, tomoyo_dir, | ||
| 133 | TOMOYO_QUERY); | ||
| 134 | tomoyo_create_entry("domain_policy", 0600, tomoyo_dir, | ||
| 135 | TOMOYO_DOMAINPOLICY); | ||
| 136 | tomoyo_create_entry("exception_policy", 0600, tomoyo_dir, | ||
| 137 | TOMOYO_EXCEPTIONPOLICY); | ||
| 138 | tomoyo_create_entry("self_domain", 0400, tomoyo_dir, | ||
| 139 | TOMOYO_SELFDOMAIN); | ||
| 140 | tomoyo_create_entry(".domain_status", 0600, tomoyo_dir, | ||
| 141 | TOMOYO_DOMAIN_STATUS); | ||
| 142 | tomoyo_create_entry(".process_status", 0600, tomoyo_dir, | ||
| 143 | TOMOYO_PROCESS_STATUS); | ||
| 144 | tomoyo_create_entry("meminfo", 0600, tomoyo_dir, | ||
| 145 | TOMOYO_MEMINFO); | ||
| 146 | tomoyo_create_entry("profile", 0600, tomoyo_dir, | ||
| 147 | TOMOYO_PROFILE); | ||
| 148 | tomoyo_create_entry("manager", 0600, tomoyo_dir, | ||
| 149 | TOMOYO_MANAGER); | ||
| 150 | tomoyo_create_entry("version", 0400, tomoyo_dir, | ||
| 151 | TOMOYO_VERSION); | ||
| 152 | return 0; | ||
| 153 | } | ||
| 154 | |||
| 155 | fs_initcall(tomoyo_initerface_init); | ||
diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c index dedd97d0c163..95d3f9572237 100644 --- a/security/tomoyo/tomoyo.c +++ b/security/tomoyo/tomoyo.c | |||
| @@ -3,10 +3,7 @@ | |||
| 3 | * | 3 | * |
| 4 | * LSM hooks for TOMOYO Linux. | 4 | * LSM hooks for TOMOYO Linux. |
| 5 | * | 5 | * |
| 6 | * Copyright (C) 2005-2009 NTT DATA CORPORATION | 6 | * Copyright (C) 2005-2010 NTT DATA CORPORATION |
| 7 | * | ||
| 8 | * Version: 2.2.0 2009/04/01 | ||
| 9 | * | ||
| 10 | */ | 7 | */ |
| 11 | 8 | ||
| 12 | #include <linux/security.h> | 9 | #include <linux/security.h> |
| @@ -96,8 +93,7 @@ static int tomoyo_bprm_check_security(struct linux_binprm *bprm) | |||
| 96 | return tomoyo_check_open_permission(domain, &bprm->file->f_path, O_RDONLY); | 93 | return tomoyo_check_open_permission(domain, &bprm->file->f_path, O_RDONLY); |
| 97 | } | 94 | } |
| 98 | 95 | ||
| 99 | static int tomoyo_path_truncate(struct path *path, loff_t length, | 96 | static int tomoyo_path_truncate(struct path *path) |
| 100 | unsigned int time_attrs) | ||
| 101 | { | 97 | { |
| 102 | return tomoyo_path_perm(TOMOYO_TYPE_TRUNCATE, path); | 98 | return tomoyo_path_perm(TOMOYO_TYPE_TRUNCATE, path); |
| 103 | } | 99 | } |
| @@ -112,7 +108,8 @@ static int tomoyo_path_mkdir(struct path *parent, struct dentry *dentry, | |||
| 112 | int mode) | 108 | int mode) |
| 113 | { | 109 | { |
| 114 | struct path path = { parent->mnt, dentry }; | 110 | struct path path = { parent->mnt, dentry }; |
| 115 | return tomoyo_path_perm(TOMOYO_TYPE_MKDIR, &path); | 111 | return tomoyo_path_number_perm(TOMOYO_TYPE_MKDIR, &path, |
| 112 | mode & S_IALLUGO); | ||
| 116 | } | 113 | } |
| 117 | 114 | ||
| 118 | static int tomoyo_path_rmdir(struct path *parent, struct dentry *dentry) | 115 | static int tomoyo_path_rmdir(struct path *parent, struct dentry *dentry) |
| @@ -133,6 +130,7 @@ static int tomoyo_path_mknod(struct path *parent, struct dentry *dentry, | |||
| 133 | { | 130 | { |
| 134 | struct path path = { parent->mnt, dentry }; | 131 | struct path path = { parent->mnt, dentry }; |
| 135 | int type = TOMOYO_TYPE_CREATE; | 132 | int type = TOMOYO_TYPE_CREATE; |
| 133 | const unsigned int perm = mode & S_IALLUGO; | ||
| 136 | 134 | ||
| 137 | switch (mode & S_IFMT) { | 135 | switch (mode & S_IFMT) { |
| 138 | case S_IFCHR: | 136 | case S_IFCHR: |
| @@ -141,6 +139,12 @@ static int tomoyo_path_mknod(struct path *parent, struct dentry *dentry, | |||
| 141 | case S_IFBLK: | 139 | case S_IFBLK: |
| 142 | type = TOMOYO_TYPE_MKBLOCK; | 140 | type = TOMOYO_TYPE_MKBLOCK; |
| 143 | break; | 141 | break; |
| 142 | default: | ||
| 143 | goto no_dev; | ||
| 144 | } | ||
| 145 | return tomoyo_mkdev_perm(type, &path, perm, dev); | ||
| 146 | no_dev: | ||
| 147 | switch (mode & S_IFMT) { | ||
| 144 | case S_IFIFO: | 148 | case S_IFIFO: |
| 145 | type = TOMOYO_TYPE_MKFIFO; | 149 | type = TOMOYO_TYPE_MKFIFO; |
| 146 | break; | 150 | break; |
| @@ -148,7 +152,7 @@ static int tomoyo_path_mknod(struct path *parent, struct dentry *dentry, | |||
| 148 | type = TOMOYO_TYPE_MKSOCK; | 152 | type = TOMOYO_TYPE_MKSOCK; |
| 149 | break; | 153 | break; |
| 150 | } | 154 | } |
| 151 | return tomoyo_path_perm(type, &path); | 155 | return tomoyo_path_number_perm(type, &path, perm); |
| 152 | } | 156 | } |
| 153 | 157 | ||
| 154 | static int tomoyo_path_link(struct dentry *old_dentry, struct path *new_dir, | 158 | static int tomoyo_path_link(struct dentry *old_dentry, struct path *new_dir, |
| @@ -173,7 +177,7 @@ static int tomoyo_file_fcntl(struct file *file, unsigned int cmd, | |||
| 173 | unsigned long arg) | 177 | unsigned long arg) |
| 174 | { | 178 | { |
| 175 | if (cmd == F_SETFL && ((arg ^ file->f_flags) & O_APPEND)) | 179 | if (cmd == F_SETFL && ((arg ^ file->f_flags) & O_APPEND)) |
| 176 | return tomoyo_check_rewrite_permission(file); | 180 | return tomoyo_path_perm(TOMOYO_TYPE_REWRITE, &file->f_path); |
| 177 | return 0; | 181 | return 0; |
| 178 | } | 182 | } |
| 179 | 183 | ||
| @@ -189,23 +193,24 @@ static int tomoyo_dentry_open(struct file *f, const struct cred *cred) | |||
| 189 | static int tomoyo_file_ioctl(struct file *file, unsigned int cmd, | 193 | static int tomoyo_file_ioctl(struct file *file, unsigned int cmd, |
| 190 | unsigned long arg) | 194 | unsigned long arg) |
| 191 | { | 195 | { |
| 192 | return tomoyo_path_perm(TOMOYO_TYPE_IOCTL, &file->f_path); | 196 | return tomoyo_path_number_perm(TOMOYO_TYPE_IOCTL, &file->f_path, cmd); |
| 193 | } | 197 | } |
| 194 | 198 | ||
| 195 | static int tomoyo_path_chmod(struct dentry *dentry, struct vfsmount *mnt, | 199 | static int tomoyo_path_chmod(struct dentry *dentry, struct vfsmount *mnt, |
| 196 | mode_t mode) | 200 | mode_t mode) |
| 197 | { | 201 | { |
| 198 | struct path path = { mnt, dentry }; | 202 | struct path path = { mnt, dentry }; |
| 199 | return tomoyo_path_perm(TOMOYO_TYPE_CHMOD, &path); | 203 | return tomoyo_path_number_perm(TOMOYO_TYPE_CHMOD, &path, |
| 204 | mode & S_IALLUGO); | ||
| 200 | } | 205 | } |
| 201 | 206 | ||
| 202 | static int tomoyo_path_chown(struct path *path, uid_t uid, gid_t gid) | 207 | static int tomoyo_path_chown(struct path *path, uid_t uid, gid_t gid) |
| 203 | { | 208 | { |
| 204 | int error = 0; | 209 | int error = 0; |
| 205 | if (uid != (uid_t) -1) | 210 | if (uid != (uid_t) -1) |
| 206 | error = tomoyo_path_perm(TOMOYO_TYPE_CHOWN, path); | 211 | error = tomoyo_path_number_perm(TOMOYO_TYPE_CHOWN, path, uid); |
| 207 | if (!error && gid != (gid_t) -1) | 212 | if (!error && gid != (gid_t) -1) |
| 208 | error = tomoyo_path_perm(TOMOYO_TYPE_CHGRP, path); | 213 | error = tomoyo_path_number_perm(TOMOYO_TYPE_CHGRP, path, gid); |
| 209 | return error; | 214 | return error; |
| 210 | } | 215 | } |
| 211 | 216 | ||
| @@ -217,7 +222,7 @@ static int tomoyo_path_chroot(struct path *path) | |||
| 217 | static int tomoyo_sb_mount(char *dev_name, struct path *path, | 222 | static int tomoyo_sb_mount(char *dev_name, struct path *path, |
| 218 | char *type, unsigned long flags, void *data) | 223 | char *type, unsigned long flags, void *data) |
| 219 | { | 224 | { |
| 220 | return tomoyo_path_perm(TOMOYO_TYPE_MOUNT, path); | 225 | return tomoyo_mount_permission(dev_name, path, type, flags, data); |
| 221 | } | 226 | } |
| 222 | 227 | ||
| 223 | static int tomoyo_sb_umount(struct vfsmount *mnt, int flags) | 228 | static int tomoyo_sb_umount(struct vfsmount *mnt, int flags) |
| @@ -277,7 +282,7 @@ static int __init tomoyo_init(void) | |||
| 277 | panic("Failure registering TOMOYO Linux"); | 282 | panic("Failure registering TOMOYO Linux"); |
| 278 | printk(KERN_INFO "TOMOYO Linux initialized\n"); | 283 | printk(KERN_INFO "TOMOYO Linux initialized\n"); |
| 279 | cred->security = &tomoyo_kernel_domain; | 284 | cred->security = &tomoyo_kernel_domain; |
| 280 | tomoyo_realpath_init(); | 285 | tomoyo_mm_init(); |
| 281 | return 0; | 286 | return 0; |
| 282 | } | 287 | } |
| 283 | 288 | ||
diff --git a/security/tomoyo/util.c b/security/tomoyo/util.c new file mode 100644 index 000000000000..9bfc1ee8222d --- /dev/null +++ b/security/tomoyo/util.c | |||
| @@ -0,0 +1,963 @@ | |||
| 1 | /* | ||
| 2 | * security/tomoyo/util.c | ||
| 3 | * | ||
| 4 | * Utility functions for TOMOYO. | ||
| 5 | * | ||
| 6 | * Copyright (C) 2005-2010 NTT DATA CORPORATION | ||
| 7 | */ | ||
| 8 | |||
| 9 | #include <linux/slab.h> | ||
| 10 | #include "common.h" | ||
| 11 | |||
| 12 | /* Lock for protecting policy. */ | ||
| 13 | DEFINE_MUTEX(tomoyo_policy_lock); | ||
| 14 | |||
| 15 | /* Has /sbin/init started? */ | ||
| 16 | bool tomoyo_policy_loaded; | ||
| 17 | |||
| 18 | /** | ||
| 19 | * tomoyo_parse_ulong - Parse an "unsigned long" value. | ||
| 20 | * | ||
| 21 | * @result: Pointer to "unsigned long". | ||
| 22 | * @str: Pointer to string to parse. | ||
| 23 | * | ||
| 24 | * Returns value type on success, 0 otherwise. | ||
| 25 | * | ||
| 26 | * The @src is updated to point the first character after the value | ||
| 27 | * on success. | ||
| 28 | */ | ||
| 29 | static u8 tomoyo_parse_ulong(unsigned long *result, char **str) | ||
| 30 | { | ||
| 31 | const char *cp = *str; | ||
| 32 | char *ep; | ||
| 33 | int base = 10; | ||
| 34 | if (*cp == '0') { | ||
| 35 | char c = *(cp + 1); | ||
| 36 | if (c == 'x' || c == 'X') { | ||
| 37 | base = 16; | ||
| 38 | cp += 2; | ||
| 39 | } else if (c >= '0' && c <= '7') { | ||
| 40 | base = 8; | ||
| 41 | cp++; | ||
| 42 | } | ||
| 43 | } | ||
| 44 | *result = simple_strtoul(cp, &ep, base); | ||
| 45 | if (cp == ep) | ||
| 46 | return 0; | ||
| 47 | *str = ep; | ||
| 48 | switch (base) { | ||
| 49 | case 16: | ||
| 50 | return TOMOYO_VALUE_TYPE_HEXADECIMAL; | ||
| 51 | case 8: | ||
| 52 | return TOMOYO_VALUE_TYPE_OCTAL; | ||
| 53 | default: | ||
| 54 | return TOMOYO_VALUE_TYPE_DECIMAL; | ||
| 55 | } | ||
| 56 | } | ||
| 57 | |||
| 58 | /** | ||
| 59 | * tomoyo_print_ulong - Print an "unsigned long" value. | ||
| 60 | * | ||
| 61 | * @buffer: Pointer to buffer. | ||
| 62 | * @buffer_len: Size of @buffer. | ||
| 63 | * @value: An "unsigned long" value. | ||
| 64 | * @type: Type of @value. | ||
| 65 | * | ||
| 66 | * Returns nothing. | ||
| 67 | */ | ||
| 68 | void tomoyo_print_ulong(char *buffer, const int buffer_len, | ||
| 69 | const unsigned long value, const u8 type) | ||
| 70 | { | ||
| 71 | if (type == TOMOYO_VALUE_TYPE_DECIMAL) | ||
| 72 | snprintf(buffer, buffer_len, "%lu", value); | ||
| 73 | else if (type == TOMOYO_VALUE_TYPE_OCTAL) | ||
| 74 | snprintf(buffer, buffer_len, "0%lo", value); | ||
| 75 | else if (type == TOMOYO_VALUE_TYPE_HEXADECIMAL) | ||
| 76 | snprintf(buffer, buffer_len, "0x%lX", value); | ||
| 77 | else | ||
| 78 | snprintf(buffer, buffer_len, "type(%u)", type); | ||
| 79 | } | ||
| 80 | |||
| 81 | /** | ||
| 82 | * tomoyo_parse_name_union - Parse a tomoyo_name_union. | ||
| 83 | * | ||
| 84 | * @filename: Name or name group. | ||
| 85 | * @ptr: Pointer to "struct tomoyo_name_union". | ||
| 86 | * | ||
| 87 | * Returns true on success, false otherwise. | ||
| 88 | */ | ||
| 89 | bool tomoyo_parse_name_union(const char *filename, | ||
| 90 | struct tomoyo_name_union *ptr) | ||
| 91 | { | ||
| 92 | if (!tomoyo_correct_word(filename)) | ||
| 93 | return false; | ||
| 94 | if (filename[0] == '@') { | ||
| 95 | ptr->group = tomoyo_get_group(filename + 1, TOMOYO_PATH_GROUP); | ||
| 96 | ptr->is_group = true; | ||
| 97 | return ptr->group != NULL; | ||
| 98 | } | ||
| 99 | ptr->filename = tomoyo_get_name(filename); | ||
| 100 | ptr->is_group = false; | ||
| 101 | return ptr->filename != NULL; | ||
| 102 | } | ||
| 103 | |||
| 104 | /** | ||
| 105 | * tomoyo_parse_number_union - Parse a tomoyo_number_union. | ||
| 106 | * | ||
| 107 | * @data: Number or number range or number group. | ||
| 108 | * @ptr: Pointer to "struct tomoyo_number_union". | ||
| 109 | * | ||
| 110 | * Returns true on success, false otherwise. | ||
| 111 | */ | ||
| 112 | bool tomoyo_parse_number_union(char *data, struct tomoyo_number_union *num) | ||
| 113 | { | ||
| 114 | u8 type; | ||
| 115 | unsigned long v; | ||
| 116 | memset(num, 0, sizeof(*num)); | ||
| 117 | if (data[0] == '@') { | ||
| 118 | if (!tomoyo_correct_word(data)) | ||
| 119 | return false; | ||
| 120 | num->group = tomoyo_get_group(data + 1, TOMOYO_NUMBER_GROUP); | ||
| 121 | num->is_group = true; | ||
| 122 | return num->group != NULL; | ||
| 123 | } | ||
| 124 | type = tomoyo_parse_ulong(&v, &data); | ||
| 125 | if (!type) | ||
| 126 | return false; | ||
| 127 | num->values[0] = v; | ||
| 128 | num->min_type = type; | ||
| 129 | if (!*data) { | ||
| 130 | num->values[1] = v; | ||
| 131 | num->max_type = type; | ||
| 132 | return true; | ||
| 133 | } | ||
| 134 | if (*data++ != '-') | ||
| 135 | return false; | ||
| 136 | type = tomoyo_parse_ulong(&v, &data); | ||
| 137 | if (!type || *data) | ||
| 138 | return false; | ||
| 139 | num->values[1] = v; | ||
| 140 | num->max_type = type; | ||
| 141 | return true; | ||
| 142 | } | ||
| 143 | |||
| 144 | /** | ||
| 145 | * tomoyo_byte_range - Check whether the string is a \ooo style octal value. | ||
| 146 | * | ||
| 147 | * @str: Pointer to the string. | ||
| 148 | * | ||
| 149 | * Returns true if @str is a \ooo style octal value, false otherwise. | ||
| 150 | * | ||
| 151 | * TOMOYO uses \ooo style representation for 0x01 - 0x20 and 0x7F - 0xFF. | ||
| 152 | * This function verifies that \ooo is in valid range. | ||
| 153 | */ | ||
| 154 | static inline bool tomoyo_byte_range(const char *str) | ||
| 155 | { | ||
| 156 | return *str >= '0' && *str++ <= '3' && | ||
| 157 | *str >= '0' && *str++ <= '7' && | ||
| 158 | *str >= '0' && *str <= '7'; | ||
| 159 | } | ||
| 160 | |||
| 161 | /** | ||
| 162 | * tomoyo_alphabet_char - Check whether the character is an alphabet. | ||
| 163 | * | ||
| 164 | * @c: The character to check. | ||
| 165 | * | ||
| 166 | * Returns true if @c is an alphabet character, false otherwise. | ||
| 167 | */ | ||
| 168 | static inline bool tomoyo_alphabet_char(const char c) | ||
| 169 | { | ||
| 170 | return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); | ||
| 171 | } | ||
| 172 | |||
| 173 | /** | ||
| 174 | * tomoyo_make_byte - Make byte value from three octal characters. | ||
| 175 | * | ||
| 176 | * @c1: The first character. | ||
| 177 | * @c2: The second character. | ||
| 178 | * @c3: The third character. | ||
| 179 | * | ||
| 180 | * Returns byte value. | ||
| 181 | */ | ||
| 182 | static inline u8 tomoyo_make_byte(const u8 c1, const u8 c2, const u8 c3) | ||
| 183 | { | ||
| 184 | return ((c1 - '0') << 6) + ((c2 - '0') << 3) + (c3 - '0'); | ||
| 185 | } | ||
| 186 | |||
| 187 | /** | ||
| 188 | * tomoyo_str_starts - Check whether the given string starts with the given keyword. | ||
| 189 | * | ||
| 190 | * @src: Pointer to pointer to the string. | ||
| 191 | * @find: Pointer to the keyword. | ||
| 192 | * | ||
| 193 | * Returns true if @src starts with @find, false otherwise. | ||
| 194 | * | ||
| 195 | * The @src is updated to point the first character after the @find | ||
| 196 | * if @src starts with @find. | ||
| 197 | */ | ||
| 198 | bool tomoyo_str_starts(char **src, const char *find) | ||
| 199 | { | ||
| 200 | const int len = strlen(find); | ||
| 201 | char *tmp = *src; | ||
| 202 | |||
| 203 | if (strncmp(tmp, find, len)) | ||
| 204 | return false; | ||
| 205 | tmp += len; | ||
| 206 | *src = tmp; | ||
| 207 | return true; | ||
| 208 | } | ||
| 209 | |||
| 210 | /** | ||
| 211 | * tomoyo_normalize_line - Format string. | ||
| 212 | * | ||
| 213 | * @buffer: The line to normalize. | ||
| 214 | * | ||
| 215 | * Leading and trailing whitespaces are removed. | ||
| 216 | * Multiple whitespaces are packed into single space. | ||
| 217 | * | ||
| 218 | * Returns nothing. | ||
| 219 | */ | ||
| 220 | void tomoyo_normalize_line(unsigned char *buffer) | ||
| 221 | { | ||
| 222 | unsigned char *sp = buffer; | ||
| 223 | unsigned char *dp = buffer; | ||
| 224 | bool first = true; | ||
| 225 | |||
| 226 | while (tomoyo_invalid(*sp)) | ||
| 227 | sp++; | ||
| 228 | while (*sp) { | ||
| 229 | if (!first) | ||
| 230 | *dp++ = ' '; | ||
| 231 | first = false; | ||
| 232 | while (tomoyo_valid(*sp)) | ||
| 233 | *dp++ = *sp++; | ||
| 234 | while (tomoyo_invalid(*sp)) | ||
| 235 | sp++; | ||
| 236 | } | ||
| 237 | *dp = '\0'; | ||
| 238 | } | ||
| 239 | |||
| 240 | /** | ||
| 241 | * tomoyo_tokenize - Tokenize string. | ||
| 242 | * | ||
| 243 | * @buffer: The line to tokenize. | ||
| 244 | * @w: Pointer to "char *". | ||
| 245 | * @size: Sizeof @w . | ||
| 246 | * | ||
| 247 | * Returns true on success, false otherwise. | ||
| 248 | */ | ||
| 249 | bool tomoyo_tokenize(char *buffer, char *w[], size_t size) | ||
| 250 | { | ||
| 251 | int count = size / sizeof(char *); | ||
| 252 | int i; | ||
| 253 | for (i = 0; i < count; i++) | ||
| 254 | w[i] = ""; | ||
| 255 | for (i = 0; i < count; i++) { | ||
| 256 | char *cp = strchr(buffer, ' '); | ||
| 257 | if (cp) | ||
| 258 | *cp = '\0'; | ||
| 259 | w[i] = buffer; | ||
| 260 | if (!cp) | ||
| 261 | break; | ||
| 262 | buffer = cp + 1; | ||
| 263 | } | ||
| 264 | return i < count || !*buffer; | ||
| 265 | } | ||
| 266 | |||
| 267 | /** | ||
| 268 | * tomoyo_correct_word2 - Validate a string. | ||
| 269 | * | ||
| 270 | * @string: The string to check. May be non-'\0'-terminated. | ||
| 271 | * @len: Length of @string. | ||
| 272 | * | ||
| 273 | * Check whether the given string follows the naming rules. | ||
| 274 | * Returns true if @string follows the naming rules, false otherwise. | ||
| 275 | */ | ||
| 276 | static bool tomoyo_correct_word2(const char *string, size_t len) | ||
| 277 | { | ||
| 278 | const char *const start = string; | ||
| 279 | bool in_repetition = false; | ||
| 280 | unsigned char c; | ||
| 281 | unsigned char d; | ||
| 282 | unsigned char e; | ||
| 283 | if (!len) | ||
| 284 | goto out; | ||
| 285 | while (len--) { | ||
| 286 | c = *string++; | ||
| 287 | if (c == '\\') { | ||
| 288 | if (!len--) | ||
| 289 | goto out; | ||
| 290 | c = *string++; | ||
| 291 | switch (c) { | ||
| 292 | case '\\': /* "\\" */ | ||
| 293 | continue; | ||
| 294 | case '$': /* "\$" */ | ||
| 295 | case '+': /* "\+" */ | ||
| 296 | case '?': /* "\?" */ | ||
| 297 | case '*': /* "\*" */ | ||
| 298 | case '@': /* "\@" */ | ||
| 299 | case 'x': /* "\x" */ | ||
| 300 | case 'X': /* "\X" */ | ||
| 301 | case 'a': /* "\a" */ | ||
| 302 | case 'A': /* "\A" */ | ||
| 303 | case '-': /* "\-" */ | ||
| 304 | continue; | ||
| 305 | case '{': /* "/\{" */ | ||
| 306 | if (string - 3 < start || *(string - 3) != '/') | ||
| 307 | break; | ||
| 308 | in_repetition = true; | ||
| 309 | continue; | ||
| 310 | case '}': /* "\}/" */ | ||
| 311 | if (*string != '/') | ||
| 312 | break; | ||
| 313 | if (!in_repetition) | ||
| 314 | break; | ||
| 315 | in_repetition = false; | ||
| 316 | continue; | ||
| 317 | case '0': /* "\ooo" */ | ||
| 318 | case '1': | ||
| 319 | case '2': | ||
| 320 | case '3': | ||
| 321 | if (!len-- || !len--) | ||
| 322 | break; | ||
| 323 | d = *string++; | ||
| 324 | e = *string++; | ||
| 325 | if (d < '0' || d > '7' || e < '0' || e > '7') | ||
| 326 | break; | ||
| 327 | c = tomoyo_make_byte(c, d, e); | ||
| 328 | if (tomoyo_invalid(c)) | ||
| 329 | continue; /* pattern is not \000 */ | ||
| 330 | } | ||
| 331 | goto out; | ||
| 332 | } else if (in_repetition && c == '/') { | ||
| 333 | goto out; | ||
| 334 | } else if (tomoyo_invalid(c)) { | ||
| 335 | goto out; | ||
| 336 | } | ||
| 337 | } | ||
| 338 | if (in_repetition) | ||
| 339 | goto out; | ||
| 340 | return true; | ||
| 341 | out: | ||
| 342 | return false; | ||
| 343 | } | ||
| 344 | |||
| 345 | /** | ||
| 346 | * tomoyo_correct_word - Validate a string. | ||
| 347 | * | ||
| 348 | * @string: The string to check. | ||
| 349 | * | ||
| 350 | * Check whether the given string follows the naming rules. | ||
| 351 | * Returns true if @string follows the naming rules, false otherwise. | ||
| 352 | */ | ||
| 353 | bool tomoyo_correct_word(const char *string) | ||
| 354 | { | ||
| 355 | return tomoyo_correct_word2(string, strlen(string)); | ||
| 356 | } | ||
| 357 | |||
| 358 | /** | ||
| 359 | * tomoyo_correct_path - Validate a pathname. | ||
| 360 | * | ||
| 361 | * @filename: The pathname to check. | ||
| 362 | * | ||
| 363 | * Check whether the given pathname follows the naming rules. | ||
| 364 | * Returns true if @filename follows the naming rules, false otherwise. | ||
| 365 | */ | ||
| 366 | bool tomoyo_correct_path(const char *filename) | ||
| 367 | { | ||
| 368 | return *filename == '/' && tomoyo_correct_word(filename); | ||
| 369 | } | ||
| 370 | |||
| 371 | /** | ||
| 372 | * tomoyo_correct_domain - Check whether the given domainname follows the naming rules. | ||
| 373 | * | ||
| 374 | * @domainname: The domainname to check. | ||
| 375 | * | ||
| 376 | * Returns true if @domainname follows the naming rules, false otherwise. | ||
| 377 | */ | ||
| 378 | bool tomoyo_correct_domain(const unsigned char *domainname) | ||
| 379 | { | ||
| 380 | if (!domainname || strncmp(domainname, TOMOYO_ROOT_NAME, | ||
| 381 | TOMOYO_ROOT_NAME_LEN)) | ||
| 382 | goto out; | ||
| 383 | domainname += TOMOYO_ROOT_NAME_LEN; | ||
| 384 | if (!*domainname) | ||
| 385 | return true; | ||
| 386 | if (*domainname++ != ' ') | ||
| 387 | goto out; | ||
| 388 | while (1) { | ||
| 389 | const unsigned char *cp = strchr(domainname, ' '); | ||
| 390 | if (!cp) | ||
| 391 | break; | ||
| 392 | if (*domainname != '/' || | ||
| 393 | !tomoyo_correct_word2(domainname, cp - domainname - 1)) | ||
| 394 | goto out; | ||
| 395 | domainname = cp + 1; | ||
| 396 | } | ||
| 397 | return tomoyo_correct_path(domainname); | ||
| 398 | out: | ||
| 399 | return false; | ||
| 400 | } | ||
| 401 | |||
| 402 | /** | ||
| 403 | * tomoyo_domain_def - Check whether the given token can be a domainname. | ||
| 404 | * | ||
| 405 | * @buffer: The token to check. | ||
| 406 | * | ||
| 407 | * Returns true if @buffer possibly be a domainname, false otherwise. | ||
| 408 | */ | ||
| 409 | bool tomoyo_domain_def(const unsigned char *buffer) | ||
| 410 | { | ||
| 411 | return !strncmp(buffer, TOMOYO_ROOT_NAME, TOMOYO_ROOT_NAME_LEN); | ||
| 412 | } | ||
| 413 | |||
| 414 | /** | ||
| 415 | * tomoyo_find_domain - Find a domain by the given name. | ||
| 416 | * | ||
| 417 | * @domainname: The domainname to find. | ||
| 418 | * | ||
| 419 | * Returns pointer to "struct tomoyo_domain_info" if found, NULL otherwise. | ||
| 420 | * | ||
| 421 | * Caller holds tomoyo_read_lock(). | ||
| 422 | */ | ||
| 423 | struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname) | ||
| 424 | { | ||
| 425 | struct tomoyo_domain_info *domain; | ||
| 426 | struct tomoyo_path_info name; | ||
| 427 | |||
| 428 | name.name = domainname; | ||
| 429 | tomoyo_fill_path_info(&name); | ||
| 430 | list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { | ||
| 431 | if (!domain->is_deleted && | ||
| 432 | !tomoyo_pathcmp(&name, domain->domainname)) | ||
| 433 | return domain; | ||
| 434 | } | ||
| 435 | return NULL; | ||
| 436 | } | ||
| 437 | |||
| 438 | /** | ||
| 439 | * tomoyo_const_part_length - Evaluate the initial length without a pattern in a token. | ||
| 440 | * | ||
| 441 | * @filename: The string to evaluate. | ||
| 442 | * | ||
| 443 | * Returns the initial length without a pattern in @filename. | ||
| 444 | */ | ||
| 445 | static int tomoyo_const_part_length(const char *filename) | ||
| 446 | { | ||
| 447 | char c; | ||
| 448 | int len = 0; | ||
| 449 | |||
| 450 | if (!filename) | ||
| 451 | return 0; | ||
| 452 | while ((c = *filename++) != '\0') { | ||
| 453 | if (c != '\\') { | ||
| 454 | len++; | ||
| 455 | continue; | ||
| 456 | } | ||
| 457 | c = *filename++; | ||
| 458 | switch (c) { | ||
| 459 | case '\\': /* "\\" */ | ||
| 460 | len += 2; | ||
| 461 | continue; | ||
| 462 | case '0': /* "\ooo" */ | ||
| 463 | case '1': | ||
| 464 | case '2': | ||
| 465 | case '3': | ||
| 466 | c = *filename++; | ||
| 467 | if (c < '0' || c > '7') | ||
| 468 | break; | ||
| 469 | c = *filename++; | ||
| 470 | if (c < '0' || c > '7') | ||
| 471 | break; | ||
| 472 | len += 4; | ||
| 473 | continue; | ||
| 474 | } | ||
| 475 | break; | ||
| 476 | } | ||
| 477 | return len; | ||
| 478 | } | ||
| 479 | |||
| 480 | /** | ||
| 481 | * tomoyo_fill_path_info - Fill in "struct tomoyo_path_info" members. | ||
| 482 | * | ||
| 483 | * @ptr: Pointer to "struct tomoyo_path_info" to fill in. | ||
| 484 | * | ||
| 485 | * The caller sets "struct tomoyo_path_info"->name. | ||
| 486 | */ | ||
| 487 | void tomoyo_fill_path_info(struct tomoyo_path_info *ptr) | ||
| 488 | { | ||
| 489 | const char *name = ptr->name; | ||
| 490 | const int len = strlen(name); | ||
| 491 | |||
| 492 | ptr->const_len = tomoyo_const_part_length(name); | ||
| 493 | ptr->is_dir = len && (name[len - 1] == '/'); | ||
| 494 | ptr->is_patterned = (ptr->const_len < len); | ||
| 495 | ptr->hash = full_name_hash(name, len); | ||
| 496 | } | ||
| 497 | |||
| 498 | /** | ||
| 499 | * tomoyo_file_matches_pattern2 - Pattern matching without '/' character and "\-" pattern. | ||
| 500 | * | ||
| 501 | * @filename: The start of string to check. | ||
| 502 | * @filename_end: The end of string to check. | ||
| 503 | * @pattern: The start of pattern to compare. | ||
| 504 | * @pattern_end: The end of pattern to compare. | ||
| 505 | * | ||
| 506 | * Returns true if @filename matches @pattern, false otherwise. | ||
| 507 | */ | ||
| 508 | static bool tomoyo_file_matches_pattern2(const char *filename, | ||
| 509 | const char *filename_end, | ||
| 510 | const char *pattern, | ||
| 511 | const char *pattern_end) | ||
| 512 | { | ||
| 513 | while (filename < filename_end && pattern < pattern_end) { | ||
| 514 | char c; | ||
| 515 | if (*pattern != '\\') { | ||
| 516 | if (*filename++ != *pattern++) | ||
| 517 | return false; | ||
| 518 | continue; | ||
| 519 | } | ||
| 520 | c = *filename; | ||
| 521 | pattern++; | ||
| 522 | switch (*pattern) { | ||
| 523 | int i; | ||
| 524 | int j; | ||
| 525 | case '?': | ||
| 526 | if (c == '/') { | ||
| 527 | return false; | ||
| 528 | } else if (c == '\\') { | ||
| 529 | if (filename[1] == '\\') | ||
| 530 | filename++; | ||
| 531 | else if (tomoyo_byte_range(filename + 1)) | ||
| 532 | filename += 3; | ||
| 533 | else | ||
| 534 | return false; | ||
| 535 | } | ||
| 536 | break; | ||
| 537 | case '\\': | ||
| 538 | if (c != '\\') | ||
| 539 | return false; | ||
| 540 | if (*++filename != '\\') | ||
| 541 | return false; | ||
| 542 | break; | ||
| 543 | case '+': | ||
| 544 | if (!isdigit(c)) | ||
| 545 | return false; | ||
| 546 | break; | ||
| 547 | case 'x': | ||
| 548 | if (!isxdigit(c)) | ||
| 549 | return false; | ||
| 550 | break; | ||
| 551 | case 'a': | ||
| 552 | if (!tomoyo_alphabet_char(c)) | ||
| 553 | return false; | ||
| 554 | break; | ||
| 555 | case '0': | ||
| 556 | case '1': | ||
| 557 | case '2': | ||
| 558 | case '3': | ||
| 559 | if (c == '\\' && tomoyo_byte_range(filename + 1) | ||
| 560 | && strncmp(filename + 1, pattern, 3) == 0) { | ||
| 561 | filename += 3; | ||
| 562 | pattern += 2; | ||
| 563 | break; | ||
| 564 | } | ||
| 565 | return false; /* Not matched. */ | ||
| 566 | case '*': | ||
| 567 | case '@': | ||
| 568 | for (i = 0; i <= filename_end - filename; i++) { | ||
| 569 | if (tomoyo_file_matches_pattern2( | ||
| 570 | filename + i, filename_end, | ||
| 571 | pattern + 1, pattern_end)) | ||
| 572 | return true; | ||
| 573 | c = filename[i]; | ||
| 574 | if (c == '.' && *pattern == '@') | ||
| 575 | break; | ||
| 576 | if (c != '\\') | ||
| 577 | continue; | ||
| 578 | if (filename[i + 1] == '\\') | ||
| 579 | i++; | ||
| 580 | else if (tomoyo_byte_range(filename + i + 1)) | ||
| 581 | i += 3; | ||
| 582 | else | ||
| 583 | break; /* Bad pattern. */ | ||
| 584 | } | ||
| 585 | return false; /* Not matched. */ | ||
| 586 | default: | ||
| 587 | j = 0; | ||
| 588 | c = *pattern; | ||
| 589 | if (c == '$') { | ||
| 590 | while (isdigit(filename[j])) | ||
| 591 | j++; | ||
| 592 | } else if (c == 'X') { | ||
| 593 | while (isxdigit(filename[j])) | ||
| 594 | j++; | ||
| 595 | } else if (c == 'A') { | ||
| 596 | while (tomoyo_alphabet_char(filename[j])) | ||
| 597 | j++; | ||
| 598 | } | ||
| 599 | for (i = 1; i <= j; i++) { | ||
| 600 | if (tomoyo_file_matches_pattern2( | ||
| 601 | filename + i, filename_end, | ||
| 602 | pattern + 1, pattern_end)) | ||
| 603 | return true; | ||
| 604 | } | ||
| 605 | return false; /* Not matched or bad pattern. */ | ||
| 606 | } | ||
| 607 | filename++; | ||
| 608 | pattern++; | ||
| 609 | } | ||
| 610 | while (*pattern == '\\' && | ||
| 611 | (*(pattern + 1) == '*' || *(pattern + 1) == '@')) | ||
| 612 | pattern += 2; | ||
| 613 | return filename == filename_end && pattern == pattern_end; | ||
| 614 | } | ||
| 615 | |||
| 616 | /** | ||
| 617 | * tomoyo_file_matches_pattern - Pattern matching without '/' character. | ||
| 618 | * | ||
| 619 | * @filename: The start of string to check. | ||
| 620 | * @filename_end: The end of string to check. | ||
| 621 | * @pattern: The start of pattern to compare. | ||
| 622 | * @pattern_end: The end of pattern to compare. | ||
| 623 | * | ||
| 624 | * Returns true if @filename matches @pattern, false otherwise. | ||
| 625 | */ | ||
| 626 | static bool tomoyo_file_matches_pattern(const char *filename, | ||
| 627 | const char *filename_end, | ||
| 628 | const char *pattern, | ||
| 629 | const char *pattern_end) | ||
| 630 | { | ||
| 631 | const char *pattern_start = pattern; | ||
| 632 | bool first = true; | ||
| 633 | bool result; | ||
| 634 | |||
| 635 | while (pattern < pattern_end - 1) { | ||
| 636 | /* Split at "\-" pattern. */ | ||
| 637 | if (*pattern++ != '\\' || *pattern++ != '-') | ||
| 638 | continue; | ||
| 639 | result = tomoyo_file_matches_pattern2(filename, | ||
| 640 | filename_end, | ||
| 641 | pattern_start, | ||
| 642 | pattern - 2); | ||
| 643 | if (first) | ||
| 644 | result = !result; | ||
| 645 | if (result) | ||
| 646 | return false; | ||
| 647 | first = false; | ||
| 648 | pattern_start = pattern; | ||
| 649 | } | ||
| 650 | result = tomoyo_file_matches_pattern2(filename, filename_end, | ||
| 651 | pattern_start, pattern_end); | ||
| 652 | return first ? result : !result; | ||
| 653 | } | ||
| 654 | |||
| 655 | /** | ||
| 656 | * tomoyo_path_matches_pattern2 - Do pathname pattern matching. | ||
| 657 | * | ||
| 658 | * @f: The start of string to check. | ||
| 659 | * @p: The start of pattern to compare. | ||
| 660 | * | ||
| 661 | * Returns true if @f matches @p, false otherwise. | ||
| 662 | */ | ||
| 663 | static bool tomoyo_path_matches_pattern2(const char *f, const char *p) | ||
| 664 | { | ||
| 665 | const char *f_delimiter; | ||
| 666 | const char *p_delimiter; | ||
| 667 | |||
| 668 | while (*f && *p) { | ||
| 669 | f_delimiter = strchr(f, '/'); | ||
| 670 | if (!f_delimiter) | ||
| 671 | f_delimiter = f + strlen(f); | ||
| 672 | p_delimiter = strchr(p, '/'); | ||
| 673 | if (!p_delimiter) | ||
| 674 | p_delimiter = p + strlen(p); | ||
| 675 | if (*p == '\\' && *(p + 1) == '{') | ||
| 676 | goto recursive; | ||
| 677 | if (!tomoyo_file_matches_pattern(f, f_delimiter, p, | ||
| 678 | p_delimiter)) | ||
| 679 | return false; | ||
| 680 | f = f_delimiter; | ||
| 681 | if (*f) | ||
| 682 | f++; | ||
| 683 | p = p_delimiter; | ||
| 684 | if (*p) | ||
| 685 | p++; | ||
| 686 | } | ||
| 687 | /* Ignore trailing "\*" and "\@" in @pattern. */ | ||
| 688 | while (*p == '\\' && | ||
| 689 | (*(p + 1) == '*' || *(p + 1) == '@')) | ||
| 690 | p += 2; | ||
| 691 | return !*f && !*p; | ||
| 692 | recursive: | ||
| 693 | /* | ||
| 694 | * The "\{" pattern is permitted only after '/' character. | ||
| 695 | * This guarantees that below "*(p - 1)" is safe. | ||
| 696 | * Also, the "\}" pattern is permitted only before '/' character | ||
| 697 | * so that "\{" + "\}" pair will not break the "\-" operator. | ||
| 698 | */ | ||
| 699 | if (*(p - 1) != '/' || p_delimiter <= p + 3 || *p_delimiter != '/' || | ||
| 700 | *(p_delimiter - 1) != '}' || *(p_delimiter - 2) != '\\') | ||
| 701 | return false; /* Bad pattern. */ | ||
| 702 | do { | ||
| 703 | /* Compare current component with pattern. */ | ||
| 704 | if (!tomoyo_file_matches_pattern(f, f_delimiter, p + 2, | ||
| 705 | p_delimiter - 2)) | ||
| 706 | break; | ||
| 707 | /* Proceed to next component. */ | ||
| 708 | f = f_delimiter; | ||
| 709 | if (!*f) | ||
| 710 | break; | ||
| 711 | f++; | ||
| 712 | /* Continue comparison. */ | ||
| 713 | if (tomoyo_path_matches_pattern2(f, p_delimiter + 1)) | ||
| 714 | return true; | ||
| 715 | f_delimiter = strchr(f, '/'); | ||
| 716 | } while (f_delimiter); | ||
| 717 | return false; /* Not matched. */ | ||
| 718 | } | ||
| 719 | |||
| 720 | /** | ||
| 721 | * tomoyo_path_matches_pattern - Check whether the given filename matches the given pattern. | ||
| 722 | * | ||
| 723 | * @filename: The filename to check. | ||
| 724 | * @pattern: The pattern to compare. | ||
| 725 | * | ||
| 726 | * Returns true if matches, false otherwise. | ||
| 727 | * | ||
| 728 | * The following patterns are available. | ||
| 729 | * \\ \ itself. | ||
| 730 | * \ooo Octal representation of a byte. | ||
| 731 | * \* Zero or more repetitions of characters other than '/'. | ||
| 732 | * \@ Zero or more repetitions of characters other than '/' or '.'. | ||
| 733 | * \? 1 byte character other than '/'. | ||
| 734 | * \$ One or more repetitions of decimal digits. | ||
| 735 | * \+ 1 decimal digit. | ||
| 736 | * \X One or more repetitions of hexadecimal digits. | ||
| 737 | * \x 1 hexadecimal digit. | ||
| 738 | * \A One or more repetitions of alphabet characters. | ||
| 739 | * \a 1 alphabet character. | ||
| 740 | * | ||
| 741 | * \- Subtraction operator. | ||
| 742 | * | ||
| 743 | * /\{dir\}/ '/' + 'One or more repetitions of dir/' (e.g. /dir/ /dir/dir/ | ||
| 744 | * /dir/dir/dir/ ). | ||
| 745 | */ | ||
| 746 | bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename, | ||
| 747 | const struct tomoyo_path_info *pattern) | ||
| 748 | { | ||
| 749 | const char *f = filename->name; | ||
| 750 | const char *p = pattern->name; | ||
| 751 | const int len = pattern->const_len; | ||
| 752 | |||
| 753 | /* If @pattern doesn't contain pattern, I can use strcmp(). */ | ||
| 754 | if (!pattern->is_patterned) | ||
| 755 | return !tomoyo_pathcmp(filename, pattern); | ||
| 756 | /* Don't compare directory and non-directory. */ | ||
| 757 | if (filename->is_dir != pattern->is_dir) | ||
| 758 | return false; | ||
| 759 | /* Compare the initial length without patterns. */ | ||
| 760 | if (strncmp(f, p, len)) | ||
| 761 | return false; | ||
| 762 | f += len; | ||
| 763 | p += len; | ||
| 764 | return tomoyo_path_matches_pattern2(f, p); | ||
| 765 | } | ||
| 766 | |||
| 767 | /** | ||
| 768 | * tomoyo_get_exe - Get tomoyo_realpath() of current process. | ||
| 769 | * | ||
| 770 | * Returns the tomoyo_realpath() of current process on success, NULL otherwise. | ||
| 771 | * | ||
| 772 | * This function uses kzalloc(), so the caller must call kfree() | ||
| 773 | * if this function didn't return NULL. | ||
| 774 | */ | ||
| 775 | const char *tomoyo_get_exe(void) | ||
| 776 | { | ||
| 777 | struct mm_struct *mm = current->mm; | ||
| 778 | struct vm_area_struct *vma; | ||
| 779 | const char *cp = NULL; | ||
| 780 | |||
| 781 | if (!mm) | ||
| 782 | return NULL; | ||
| 783 | down_read(&mm->mmap_sem); | ||
| 784 | for (vma = mm->mmap; vma; vma = vma->vm_next) { | ||
| 785 | if ((vma->vm_flags & VM_EXECUTABLE) && vma->vm_file) { | ||
| 786 | cp = tomoyo_realpath_from_path(&vma->vm_file->f_path); | ||
| 787 | break; | ||
| 788 | } | ||
| 789 | } | ||
| 790 | up_read(&mm->mmap_sem); | ||
| 791 | return cp; | ||
| 792 | } | ||
| 793 | |||
| 794 | /** | ||
| 795 | * tomoyo_get_mode - Get MAC mode. | ||
| 796 | * | ||
| 797 | * @profile: Profile number. | ||
| 798 | * @index: Index number of functionality. | ||
| 799 | * | ||
| 800 | * Returns mode. | ||
| 801 | */ | ||
| 802 | int tomoyo_get_mode(const u8 profile, const u8 index) | ||
| 803 | { | ||
| 804 | u8 mode; | ||
| 805 | const u8 category = TOMOYO_MAC_CATEGORY_FILE; | ||
| 806 | if (!tomoyo_policy_loaded) | ||
| 807 | return TOMOYO_CONFIG_DISABLED; | ||
| 808 | mode = tomoyo_profile(profile)->config[index]; | ||
| 809 | if (mode == TOMOYO_CONFIG_USE_DEFAULT) | ||
| 810 | mode = tomoyo_profile(profile)->config[category]; | ||
| 811 | if (mode == TOMOYO_CONFIG_USE_DEFAULT) | ||
| 812 | mode = tomoyo_profile(profile)->default_config; | ||
| 813 | return mode & 3; | ||
| 814 | } | ||
| 815 | |||
| 816 | /** | ||
| 817 | * tomoyo_init_request_info - Initialize "struct tomoyo_request_info" members. | ||
| 818 | * | ||
| 819 | * @r: Pointer to "struct tomoyo_request_info" to initialize. | ||
| 820 | * @domain: Pointer to "struct tomoyo_domain_info". NULL for tomoyo_domain(). | ||
| 821 | * @index: Index number of functionality. | ||
| 822 | * | ||
| 823 | * Returns mode. | ||
| 824 | */ | ||
| 825 | int tomoyo_init_request_info(struct tomoyo_request_info *r, | ||
| 826 | struct tomoyo_domain_info *domain, const u8 index) | ||
| 827 | { | ||
| 828 | u8 profile; | ||
| 829 | memset(r, 0, sizeof(*r)); | ||
| 830 | if (!domain) | ||
| 831 | domain = tomoyo_domain(); | ||
| 832 | r->domain = domain; | ||
| 833 | profile = domain->profile; | ||
| 834 | r->profile = profile; | ||
| 835 | r->type = index; | ||
| 836 | r->mode = tomoyo_get_mode(profile, index); | ||
| 837 | return r->mode; | ||
| 838 | } | ||
| 839 | |||
| 840 | /** | ||
| 841 | * tomoyo_last_word - Get last component of a line. | ||
| 842 | * | ||
| 843 | * @line: A line. | ||
| 844 | * | ||
| 845 | * Returns the last word of a line. | ||
| 846 | */ | ||
| 847 | const char *tomoyo_last_word(const char *name) | ||
| 848 | { | ||
| 849 | const char *cp = strrchr(name, ' '); | ||
| 850 | if (cp) | ||
| 851 | return cp + 1; | ||
| 852 | return name; | ||
| 853 | } | ||
| 854 | |||
| 855 | /** | ||
| 856 | * tomoyo_warn_log - Print warning or error message on console. | ||
| 857 | * | ||
| 858 | * @r: Pointer to "struct tomoyo_request_info". | ||
| 859 | * @fmt: The printf()'s format string, followed by parameters. | ||
| 860 | */ | ||
| 861 | void tomoyo_warn_log(struct tomoyo_request_info *r, const char *fmt, ...) | ||
| 862 | { | ||
| 863 | va_list args; | ||
| 864 | char *buffer; | ||
| 865 | const struct tomoyo_domain_info * const domain = r->domain; | ||
| 866 | const struct tomoyo_profile *profile = tomoyo_profile(domain->profile); | ||
| 867 | switch (r->mode) { | ||
| 868 | case TOMOYO_CONFIG_ENFORCING: | ||
| 869 | if (!profile->enforcing->enforcing_verbose) | ||
| 870 | return; | ||
| 871 | break; | ||
| 872 | case TOMOYO_CONFIG_PERMISSIVE: | ||
| 873 | if (!profile->permissive->permissive_verbose) | ||
| 874 | return; | ||
| 875 | break; | ||
| 876 | case TOMOYO_CONFIG_LEARNING: | ||
| 877 | if (!profile->learning->learning_verbose) | ||
| 878 | return; | ||
| 879 | break; | ||
| 880 | } | ||
| 881 | buffer = kmalloc(4096, GFP_NOFS); | ||
| 882 | if (!buffer) | ||
| 883 | return; | ||
| 884 | va_start(args, fmt); | ||
| 885 | vsnprintf(buffer, 4095, fmt, args); | ||
| 886 | va_end(args); | ||
| 887 | buffer[4095] = '\0'; | ||
| 888 | printk(KERN_WARNING "%s: Access %s denied for %s\n", | ||
| 889 | r->mode == TOMOYO_CONFIG_ENFORCING ? "ERROR" : "WARNING", buffer, | ||
| 890 | tomoyo_last_word(domain->domainname->name)); | ||
| 891 | kfree(buffer); | ||
| 892 | } | ||
| 893 | |||
| 894 | /** | ||
| 895 | * tomoyo_domain_quota_is_ok - Check for domain's quota. | ||
| 896 | * | ||
| 897 | * @r: Pointer to "struct tomoyo_request_info". | ||
| 898 | * | ||
| 899 | * Returns true if the domain is not exceeded quota, false otherwise. | ||
| 900 | * | ||
| 901 | * Caller holds tomoyo_read_lock(). | ||
| 902 | */ | ||
| 903 | bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r) | ||
| 904 | { | ||
| 905 | unsigned int count = 0; | ||
| 906 | struct tomoyo_domain_info *domain = r->domain; | ||
| 907 | struct tomoyo_acl_info *ptr; | ||
| 908 | |||
| 909 | if (r->mode != TOMOYO_CONFIG_LEARNING) | ||
| 910 | return false; | ||
| 911 | if (!domain) | ||
| 912 | return true; | ||
| 913 | list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { | ||
| 914 | if (ptr->is_deleted) | ||
| 915 | continue; | ||
| 916 | switch (ptr->type) { | ||
| 917 | u16 perm; | ||
| 918 | u8 i; | ||
| 919 | case TOMOYO_TYPE_PATH_ACL: | ||
| 920 | perm = container_of(ptr, struct tomoyo_path_acl, head) | ||
| 921 | ->perm; | ||
| 922 | for (i = 0; i < TOMOYO_MAX_PATH_OPERATION; i++) | ||
| 923 | if (perm & (1 << i)) | ||
| 924 | count++; | ||
| 925 | if (perm & (1 << TOMOYO_TYPE_READ_WRITE)) | ||
| 926 | count -= 2; | ||
| 927 | break; | ||
| 928 | case TOMOYO_TYPE_PATH2_ACL: | ||
| 929 | perm = container_of(ptr, struct tomoyo_path2_acl, head) | ||
| 930 | ->perm; | ||
| 931 | for (i = 0; i < TOMOYO_MAX_PATH2_OPERATION; i++) | ||
| 932 | if (perm & (1 << i)) | ||
| 933 | count++; | ||
| 934 | break; | ||
| 935 | case TOMOYO_TYPE_PATH_NUMBER_ACL: | ||
| 936 | perm = container_of(ptr, struct tomoyo_path_number_acl, | ||
| 937 | head)->perm; | ||
| 938 | for (i = 0; i < TOMOYO_MAX_PATH_NUMBER_OPERATION; i++) | ||
| 939 | if (perm & (1 << i)) | ||
| 940 | count++; | ||
| 941 | break; | ||
| 942 | case TOMOYO_TYPE_MKDEV_ACL: | ||
| 943 | perm = container_of(ptr, struct tomoyo_mkdev_acl, | ||
| 944 | head)->perm; | ||
| 945 | for (i = 0; i < TOMOYO_MAX_MKDEV_OPERATION; i++) | ||
| 946 | if (perm & (1 << i)) | ||
| 947 | count++; | ||
| 948 | break; | ||
| 949 | default: | ||
| 950 | count++; | ||
| 951 | } | ||
| 952 | } | ||
| 953 | if (count < tomoyo_profile(domain->profile)->learning-> | ||
| 954 | learning_max_entry) | ||
| 955 | return true; | ||
| 956 | if (!domain->quota_warned) { | ||
| 957 | domain->quota_warned = true; | ||
| 958 | printk(KERN_WARNING "TOMOYO-WARNING: " | ||
| 959 | "Domain '%s' has so many ACLs to hold. " | ||
| 960 | "Stopped learning mode.\n", domain->domainname->name); | ||
| 961 | } | ||
| 962 | return false; | ||
| 963 | } | ||
