diff options
Diffstat (limited to 'security')
54 files changed, 3317 insertions, 1848 deletions
diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig index be5e9414a295..b6b68a7750ce 100644 --- a/security/apparmor/Kconfig +++ b/security/apparmor/Kconfig | |||
| @@ -36,7 +36,6 @@ config SECURITY_APPARMOR_HASH | |||
| 36 | select CRYPTO | 36 | select CRYPTO |
| 37 | select CRYPTO_SHA1 | 37 | select CRYPTO_SHA1 |
| 38 | default y | 38 | default y |
| 39 | |||
| 40 | help | 39 | help |
| 41 | This option selects whether introspection of loaded policy | 40 | This option selects whether introspection of loaded policy |
| 42 | is available to userspace via the apparmor filesystem. | 41 | is available to userspace via the apparmor filesystem. |
| @@ -45,7 +44,6 @@ config SECURITY_APPARMOR_HASH_DEFAULT | |||
| 45 | bool "Enable policy hash introspection by default" | 44 | bool "Enable policy hash introspection by default" |
| 46 | depends on SECURITY_APPARMOR_HASH | 45 | depends on SECURITY_APPARMOR_HASH |
| 47 | default y | 46 | default y |
| 48 | |||
| 49 | help | 47 | help |
| 50 | This option selects whether sha1 hashing of loaded policy | 48 | This option selects whether sha1 hashing of loaded policy |
| 51 | is enabled by default. The generation of sha1 hashes for | 49 | is enabled by default. The generation of sha1 hashes for |
| @@ -54,3 +52,32 @@ config SECURITY_APPARMOR_HASH_DEFAULT | |||
| 54 | however it can slow down policy load on some devices. In | 52 | however it can slow down policy load on some devices. In |
| 55 | these cases policy hashing can be disabled by default and | 53 | these cases policy hashing can be disabled by default and |
| 56 | enabled only if needed. | 54 | enabled only if needed. |
| 55 | |||
| 56 | config SECURITY_APPARMOR_DEBUG | ||
| 57 | bool "Build AppArmor with debug code" | ||
| 58 | depends on SECURITY_APPARMOR | ||
| 59 | default n | ||
| 60 | help | ||
| 61 | Build apparmor with debugging logic in apparmor. Not all | ||
| 62 | debugging logic will necessarily be enabled. A submenu will | ||
| 63 | provide fine grained control of the debug options that are | ||
| 64 | available. | ||
| 65 | |||
| 66 | config SECURITY_APPARMOR_DEBUG_ASSERTS | ||
| 67 | bool "Build AppArmor with debugging asserts" | ||
| 68 | depends on SECURITY_APPARMOR_DEBUG | ||
| 69 | default y | ||
| 70 | help | ||
| 71 | Enable code assertions made with AA_BUG. These are primarily | ||
| 72 | function entry preconditions but also exist at other key | ||
| 73 | points. If the assert is triggered it will trigger a WARN | ||
| 74 | message. | ||
| 75 | |||
| 76 | config SECURITY_APPARMOR_DEBUG_MESSAGES | ||
| 77 | bool "Debug messages enabled by default" | ||
| 78 | depends on SECURITY_APPARMOR_DEBUG | ||
| 79 | default n | ||
| 80 | help | ||
| 81 | Set the default value of the apparmor.debug kernel parameter. | ||
| 82 | When enabled, various debug messages will be logged to | ||
| 83 | the kernel message buffer. | ||
diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index d693df874818..ad369a7aac24 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile | |||
| @@ -4,7 +4,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o | |||
| 4 | 4 | ||
| 5 | apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ | 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 \ | 6 | path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ |
| 7 | resource.o sid.o file.o | 7 | resource.o secid.o file.o policy_ns.o |
| 8 | apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o | 8 | apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o |
| 9 | 9 | ||
| 10 | clean-files := capability_names.h rlim_names.h | 10 | clean-files := capability_names.h rlim_names.h |
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 5923d5665209..41073f70eb41 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c | |||
| @@ -18,9 +18,12 @@ | |||
| 18 | #include <linux/module.h> | 18 | #include <linux/module.h> |
| 19 | #include <linux/seq_file.h> | 19 | #include <linux/seq_file.h> |
| 20 | #include <linux/uaccess.h> | 20 | #include <linux/uaccess.h> |
| 21 | #include <linux/mount.h> | ||
| 21 | #include <linux/namei.h> | 22 | #include <linux/namei.h> |
| 22 | #include <linux/capability.h> | 23 | #include <linux/capability.h> |
| 23 | #include <linux/rcupdate.h> | 24 | #include <linux/rcupdate.h> |
| 25 | #include <uapi/linux/major.h> | ||
| 26 | #include <linux/fs.h> | ||
| 24 | 27 | ||
| 25 | #include "include/apparmor.h" | 28 | #include "include/apparmor.h" |
| 26 | #include "include/apparmorfs.h" | 29 | #include "include/apparmorfs.h" |
| @@ -28,7 +31,9 @@ | |||
| 28 | #include "include/context.h" | 31 | #include "include/context.h" |
| 29 | #include "include/crypto.h" | 32 | #include "include/crypto.h" |
| 30 | #include "include/policy.h" | 33 | #include "include/policy.h" |
| 34 | #include "include/policy_ns.h" | ||
| 31 | #include "include/resource.h" | 35 | #include "include/resource.h" |
| 36 | #include "include/policy_unpack.h" | ||
| 32 | 37 | ||
| 33 | /** | 38 | /** |
| 34 | * aa_mangle_name - mangle a profile name to std profile layout form | 39 | * aa_mangle_name - mangle a profile name to std profile layout form |
| @@ -37,7 +42,7 @@ | |||
| 37 | * | 42 | * |
| 38 | * Returns: length of mangled name | 43 | * Returns: length of mangled name |
| 39 | */ | 44 | */ |
| 40 | static int mangle_name(char *name, char *target) | 45 | static int mangle_name(const char *name, char *target) |
| 41 | { | 46 | { |
| 42 | char *t = target; | 47 | char *t = target; |
| 43 | 48 | ||
| @@ -71,7 +76,6 @@ static int mangle_name(char *name, char *target) | |||
| 71 | 76 | ||
| 72 | /** | 77 | /** |
| 73 | * aa_simple_write_to_buffer - common routine for getting policy from user | 78 | * aa_simple_write_to_buffer - common routine for getting policy from user |
| 74 | * @op: operation doing the user buffer copy | ||
| 75 | * @userbuf: user buffer to copy data from (NOT NULL) | 79 | * @userbuf: user buffer to copy data from (NOT NULL) |
| 76 | * @alloc_size: size of user buffer (REQUIRES: @alloc_size >= @copy_size) | 80 | * @alloc_size: size of user buffer (REQUIRES: @alloc_size >= @copy_size) |
| 77 | * @copy_size: size of data to copy from user buffer | 81 | * @copy_size: size of data to copy from user buffer |
| @@ -80,31 +84,29 @@ static int mangle_name(char *name, char *target) | |||
| 80 | * Returns: kernel buffer containing copy of user buffer data or an | 84 | * Returns: kernel buffer containing copy of user buffer data or an |
| 81 | * ERR_PTR on failure. | 85 | * ERR_PTR on failure. |
| 82 | */ | 86 | */ |
| 83 | static char *aa_simple_write_to_buffer(int op, const char __user *userbuf, | 87 | static struct aa_loaddata *aa_simple_write_to_buffer(const char __user *userbuf, |
| 84 | size_t alloc_size, size_t copy_size, | 88 | size_t alloc_size, |
| 85 | loff_t *pos) | 89 | size_t copy_size, |
| 90 | loff_t *pos) | ||
| 86 | { | 91 | { |
| 87 | char *data; | 92 | struct aa_loaddata *data; |
| 88 | 93 | ||
| 89 | BUG_ON(copy_size > alloc_size); | 94 | AA_BUG(copy_size > alloc_size); |
| 90 | 95 | ||
| 91 | if (*pos != 0) | 96 | if (*pos != 0) |
| 92 | /* only writes from pos 0, that is complete writes */ | 97 | /* only writes from pos 0, that is complete writes */ |
| 93 | return ERR_PTR(-ESPIPE); | 98 | return ERR_PTR(-ESPIPE); |
| 94 | 99 | ||
| 95 | /* | ||
| 96 | * Don't allow profile load/replace/remove from profiles that don't | ||
| 97 | * have CAP_MAC_ADMIN | ||
| 98 | */ | ||
| 99 | if (!aa_may_manage_policy(op)) | ||
| 100 | return ERR_PTR(-EACCES); | ||
| 101 | |||
| 102 | /* freed by caller to simple_write_to_buffer */ | 100 | /* freed by caller to simple_write_to_buffer */ |
| 103 | data = kvmalloc(alloc_size); | 101 | data = kvmalloc(sizeof(*data) + alloc_size); |
| 104 | if (data == NULL) | 102 | if (data == NULL) |
| 105 | return ERR_PTR(-ENOMEM); | 103 | return ERR_PTR(-ENOMEM); |
| 104 | kref_init(&data->count); | ||
| 105 | data->size = copy_size; | ||
| 106 | data->hash = NULL; | ||
| 107 | data->abi = 0; | ||
| 106 | 108 | ||
| 107 | if (copy_from_user(data, userbuf, copy_size)) { | 109 | if (copy_from_user(data->data, userbuf, copy_size)) { |
| 108 | kvfree(data); | 110 | kvfree(data); |
| 109 | return ERR_PTR(-EFAULT); | 111 | return ERR_PTR(-EFAULT); |
| 110 | } | 112 | } |
| @@ -112,25 +114,43 @@ static char *aa_simple_write_to_buffer(int op, const char __user *userbuf, | |||
| 112 | return data; | 114 | return data; |
| 113 | } | 115 | } |
| 114 | 116 | ||
| 115 | 117 | static ssize_t policy_update(int binop, const char __user *buf, size_t size, | |
| 116 | /* .load file hook fn to load policy */ | 118 | loff_t *pos, struct aa_ns *ns) |
| 117 | static ssize_t profile_load(struct file *f, const char __user *buf, size_t size, | ||
| 118 | loff_t *pos) | ||
| 119 | { | 119 | { |
| 120 | char *data; | ||
| 121 | ssize_t error; | 120 | ssize_t error; |
| 121 | struct aa_loaddata *data; | ||
| 122 | struct aa_profile *profile = aa_current_profile(); | ||
| 123 | const char *op = binop == PROF_ADD ? OP_PROF_LOAD : OP_PROF_REPL; | ||
| 124 | /* high level check about policy management - fine grained in | ||
| 125 | * below after unpack | ||
| 126 | */ | ||
| 127 | error = aa_may_manage_policy(profile, ns, op); | ||
| 128 | if (error) | ||
| 129 | return error; | ||
| 122 | 130 | ||
| 123 | data = aa_simple_write_to_buffer(OP_PROF_LOAD, buf, size, size, pos); | 131 | data = aa_simple_write_to_buffer(buf, size, size, pos); |
| 124 | |||
| 125 | error = PTR_ERR(data); | 132 | error = PTR_ERR(data); |
| 126 | if (!IS_ERR(data)) { | 133 | if (!IS_ERR(data)) { |
| 127 | error = aa_replace_profiles(data, size, PROF_ADD); | 134 | error = aa_replace_profiles(ns ? ns : profile->ns, profile, |
| 128 | kvfree(data); | 135 | binop, data); |
| 136 | aa_put_loaddata(data); | ||
| 129 | } | 137 | } |
| 130 | 138 | ||
| 131 | return error; | 139 | return error; |
| 132 | } | 140 | } |
| 133 | 141 | ||
| 142 | /* .load file hook fn to load policy */ | ||
| 143 | static ssize_t profile_load(struct file *f, const char __user *buf, size_t size, | ||
| 144 | loff_t *pos) | ||
| 145 | { | ||
| 146 | struct aa_ns *ns = aa_get_ns(f->f_inode->i_private); | ||
| 147 | int error = policy_update(PROF_ADD, buf, size, pos, ns); | ||
| 148 | |||
| 149 | aa_put_ns(ns); | ||
| 150 | |||
| 151 | return error; | ||
| 152 | } | ||
| 153 | |||
| 134 | static const struct file_operations aa_fs_profile_load = { | 154 | static const struct file_operations aa_fs_profile_load = { |
| 135 | .write = profile_load, | 155 | .write = profile_load, |
| 136 | .llseek = default_llseek, | 156 | .llseek = default_llseek, |
| @@ -140,15 +160,10 @@ static const struct file_operations aa_fs_profile_load = { | |||
| 140 | static ssize_t profile_replace(struct file *f, const char __user *buf, | 160 | static ssize_t profile_replace(struct file *f, const char __user *buf, |
| 141 | size_t size, loff_t *pos) | 161 | size_t size, loff_t *pos) |
| 142 | { | 162 | { |
| 143 | char *data; | 163 | struct aa_ns *ns = aa_get_ns(f->f_inode->i_private); |
| 144 | ssize_t error; | 164 | int error = policy_update(PROF_REPLACE, buf, size, pos, ns); |
| 145 | 165 | ||
| 146 | data = aa_simple_write_to_buffer(OP_PROF_REPL, buf, size, size, pos); | 166 | aa_put_ns(ns); |
| 147 | error = PTR_ERR(data); | ||
| 148 | if (!IS_ERR(data)) { | ||
| 149 | error = aa_replace_profiles(data, size, PROF_REPLACE); | ||
| 150 | kvfree(data); | ||
| 151 | } | ||
| 152 | 167 | ||
| 153 | return error; | 168 | return error; |
| 154 | } | 169 | } |
| @@ -162,22 +177,34 @@ static const struct file_operations aa_fs_profile_replace = { | |||
| 162 | static ssize_t profile_remove(struct file *f, const char __user *buf, | 177 | static ssize_t profile_remove(struct file *f, const char __user *buf, |
| 163 | size_t size, loff_t *pos) | 178 | size_t size, loff_t *pos) |
| 164 | { | 179 | { |
| 165 | char *data; | 180 | struct aa_loaddata *data; |
| 181 | struct aa_profile *profile; | ||
| 166 | ssize_t error; | 182 | ssize_t error; |
| 183 | struct aa_ns *ns = aa_get_ns(f->f_inode->i_private); | ||
| 184 | |||
| 185 | profile = aa_current_profile(); | ||
| 186 | /* high level check about policy management - fine grained in | ||
| 187 | * below after unpack | ||
| 188 | */ | ||
| 189 | error = aa_may_manage_policy(profile, ns, OP_PROF_RM); | ||
| 190 | if (error) | ||
| 191 | goto out; | ||
| 167 | 192 | ||
| 168 | /* | 193 | /* |
| 169 | * aa_remove_profile needs a null terminated string so 1 extra | 194 | * aa_remove_profile needs a null terminated string so 1 extra |
| 170 | * byte is allocated and the copied data is null terminated. | 195 | * byte is allocated and the copied data is null terminated. |
| 171 | */ | 196 | */ |
| 172 | data = aa_simple_write_to_buffer(OP_PROF_RM, buf, size + 1, size, pos); | 197 | data = aa_simple_write_to_buffer(buf, size + 1, size, pos); |
| 173 | 198 | ||
| 174 | error = PTR_ERR(data); | 199 | error = PTR_ERR(data); |
| 175 | if (!IS_ERR(data)) { | 200 | if (!IS_ERR(data)) { |
| 176 | data[size] = 0; | 201 | data->data[size] = 0; |
| 177 | error = aa_remove_profiles(data, size); | 202 | error = aa_remove_profiles(ns ? ns : profile->ns, profile, |
| 178 | kvfree(data); | 203 | data->data, size); |
| 204 | aa_put_loaddata(data); | ||
| 179 | } | 205 | } |
| 180 | 206 | out: | |
| 207 | aa_put_ns(ns); | ||
| 181 | return error; | 208 | return error; |
| 182 | } | 209 | } |
| 183 | 210 | ||
| @@ -186,6 +213,144 @@ static const struct file_operations aa_fs_profile_remove = { | |||
| 186 | .llseek = default_llseek, | 213 | .llseek = default_llseek, |
| 187 | }; | 214 | }; |
| 188 | 215 | ||
| 216 | /** | ||
| 217 | * query_data - queries a policy and writes its data to buf | ||
| 218 | * @buf: the resulting data is stored here (NOT NULL) | ||
| 219 | * @buf_len: size of buf | ||
| 220 | * @query: query string used to retrieve data | ||
| 221 | * @query_len: size of query including second NUL byte | ||
| 222 | * | ||
| 223 | * The buffers pointed to by buf and query may overlap. The query buffer is | ||
| 224 | * parsed before buf is written to. | ||
| 225 | * | ||
| 226 | * The query should look like "<LABEL>\0<KEY>\0", where <LABEL> is the name of | ||
| 227 | * the security confinement context and <KEY> is the name of the data to | ||
| 228 | * retrieve. <LABEL> and <KEY> must not be NUL-terminated. | ||
| 229 | * | ||
| 230 | * Don't expect the contents of buf to be preserved on failure. | ||
| 231 | * | ||
| 232 | * Returns: number of characters written to buf or -errno on failure | ||
| 233 | */ | ||
| 234 | static ssize_t query_data(char *buf, size_t buf_len, | ||
| 235 | char *query, size_t query_len) | ||
| 236 | { | ||
| 237 | char *out; | ||
| 238 | const char *key; | ||
| 239 | struct aa_profile *profile; | ||
| 240 | struct aa_data *data; | ||
| 241 | u32 bytes, blocks; | ||
| 242 | __le32 outle32; | ||
| 243 | |||
| 244 | if (!query_len) | ||
| 245 | return -EINVAL; /* need a query */ | ||
| 246 | |||
| 247 | key = query + strnlen(query, query_len) + 1; | ||
| 248 | if (key + 1 >= query + query_len) | ||
| 249 | return -EINVAL; /* not enough space for a non-empty key */ | ||
| 250 | if (key + strnlen(key, query + query_len - key) >= query + query_len) | ||
| 251 | return -EINVAL; /* must end with NUL */ | ||
| 252 | |||
| 253 | if (buf_len < sizeof(bytes) + sizeof(blocks)) | ||
| 254 | return -EINVAL; /* not enough space */ | ||
| 255 | |||
| 256 | profile = aa_current_profile(); | ||
| 257 | |||
| 258 | /* We are going to leave space for two numbers. The first is the total | ||
| 259 | * number of bytes we are writing after the first number. This is so | ||
| 260 | * users can read the full output without reallocation. | ||
| 261 | * | ||
| 262 | * The second number is the number of data blocks we're writing. An | ||
| 263 | * application might be confined by multiple policies having data in | ||
| 264 | * the same key. | ||
| 265 | */ | ||
| 266 | memset(buf, 0, sizeof(bytes) + sizeof(blocks)); | ||
| 267 | out = buf + sizeof(bytes) + sizeof(blocks); | ||
| 268 | |||
| 269 | blocks = 0; | ||
| 270 | if (profile->data) { | ||
| 271 | data = rhashtable_lookup_fast(profile->data, &key, | ||
| 272 | profile->data->p); | ||
| 273 | |||
| 274 | if (data) { | ||
| 275 | if (out + sizeof(outle32) + data->size > buf + buf_len) | ||
| 276 | return -EINVAL; /* not enough space */ | ||
| 277 | outle32 = __cpu_to_le32(data->size); | ||
| 278 | memcpy(out, &outle32, sizeof(outle32)); | ||
| 279 | out += sizeof(outle32); | ||
| 280 | memcpy(out, data->data, data->size); | ||
| 281 | out += data->size; | ||
| 282 | blocks++; | ||
| 283 | } | ||
| 284 | } | ||
| 285 | |||
| 286 | outle32 = __cpu_to_le32(out - buf - sizeof(bytes)); | ||
| 287 | memcpy(buf, &outle32, sizeof(outle32)); | ||
| 288 | outle32 = __cpu_to_le32(blocks); | ||
| 289 | memcpy(buf + sizeof(bytes), &outle32, sizeof(outle32)); | ||
| 290 | |||
| 291 | return out - buf; | ||
| 292 | } | ||
| 293 | |||
| 294 | #define QUERY_CMD_DATA "data\0" | ||
| 295 | #define QUERY_CMD_DATA_LEN 5 | ||
| 296 | |||
| 297 | /** | ||
| 298 | * aa_write_access - generic permissions and data query | ||
| 299 | * @file: pointer to open apparmorfs/access file | ||
| 300 | * @ubuf: user buffer containing the complete query string (NOT NULL) | ||
| 301 | * @count: size of ubuf | ||
| 302 | * @ppos: position in the file (MUST BE ZERO) | ||
| 303 | * | ||
| 304 | * Allows for one permissions or data query per open(), write(), and read() | ||
| 305 | * sequence. The only queries currently supported are label-based queries for | ||
| 306 | * permissions or data. | ||
| 307 | * | ||
| 308 | * For permissions queries, ubuf must begin with "label\0", followed by the | ||
| 309 | * profile query specific format described in the query_label() function | ||
| 310 | * documentation. | ||
| 311 | * | ||
| 312 | * For data queries, ubuf must have the form "data\0<LABEL>\0<KEY>\0", where | ||
| 313 | * <LABEL> is the name of the security confinement context and <KEY> is the | ||
| 314 | * name of the data to retrieve. | ||
| 315 | * | ||
| 316 | * Returns: number of bytes written or -errno on failure | ||
| 317 | */ | ||
| 318 | static ssize_t aa_write_access(struct file *file, const char __user *ubuf, | ||
| 319 | size_t count, loff_t *ppos) | ||
| 320 | { | ||
| 321 | char *buf; | ||
| 322 | ssize_t len; | ||
| 323 | |||
| 324 | if (*ppos) | ||
| 325 | return -ESPIPE; | ||
| 326 | |||
| 327 | buf = simple_transaction_get(file, ubuf, count); | ||
| 328 | if (IS_ERR(buf)) | ||
| 329 | return PTR_ERR(buf); | ||
| 330 | |||
| 331 | if (count > QUERY_CMD_DATA_LEN && | ||
| 332 | !memcmp(buf, QUERY_CMD_DATA, QUERY_CMD_DATA_LEN)) { | ||
| 333 | len = query_data(buf, SIMPLE_TRANSACTION_LIMIT, | ||
| 334 | buf + QUERY_CMD_DATA_LEN, | ||
| 335 | count - QUERY_CMD_DATA_LEN); | ||
| 336 | } else | ||
| 337 | len = -EINVAL; | ||
| 338 | |||
| 339 | if (len < 0) | ||
| 340 | return len; | ||
| 341 | |||
| 342 | simple_transaction_set(file, len); | ||
| 343 | |||
| 344 | return count; | ||
| 345 | } | ||
| 346 | |||
| 347 | static const struct file_operations aa_fs_access = { | ||
| 348 | .write = aa_write_access, | ||
| 349 | .read = simple_transaction_read, | ||
| 350 | .release = simple_transaction_release, | ||
| 351 | .llseek = generic_file_llseek, | ||
| 352 | }; | ||
| 353 | |||
| 189 | static int aa_fs_seq_show(struct seq_file *seq, void *v) | 354 | static int aa_fs_seq_show(struct seq_file *seq, void *v) |
| 190 | { | 355 | { |
| 191 | struct aa_fs_entry *fs_file = seq->private; | 356 | struct aa_fs_entry *fs_file = seq->private; |
| @@ -227,12 +392,12 @@ const struct file_operations aa_fs_seq_file_ops = { | |||
| 227 | static int aa_fs_seq_profile_open(struct inode *inode, struct file *file, | 392 | static int aa_fs_seq_profile_open(struct inode *inode, struct file *file, |
| 228 | int (*show)(struct seq_file *, void *)) | 393 | int (*show)(struct seq_file *, void *)) |
| 229 | { | 394 | { |
| 230 | struct aa_replacedby *r = aa_get_replacedby(inode->i_private); | 395 | struct aa_proxy *proxy = aa_get_proxy(inode->i_private); |
| 231 | int error = single_open(file, show, r); | 396 | int error = single_open(file, show, proxy); |
| 232 | 397 | ||
| 233 | if (error) { | 398 | if (error) { |
| 234 | file->private_data = NULL; | 399 | file->private_data = NULL; |
| 235 | aa_put_replacedby(r); | 400 | aa_put_proxy(proxy); |
| 236 | } | 401 | } |
| 237 | 402 | ||
| 238 | return error; | 403 | return error; |
| @@ -242,14 +407,14 @@ static int aa_fs_seq_profile_release(struct inode *inode, struct file *file) | |||
| 242 | { | 407 | { |
| 243 | struct seq_file *seq = (struct seq_file *) file->private_data; | 408 | struct seq_file *seq = (struct seq_file *) file->private_data; |
| 244 | if (seq) | 409 | if (seq) |
| 245 | aa_put_replacedby(seq->private); | 410 | aa_put_proxy(seq->private); |
| 246 | return single_release(inode, file); | 411 | return single_release(inode, file); |
| 247 | } | 412 | } |
| 248 | 413 | ||
| 249 | static int aa_fs_seq_profname_show(struct seq_file *seq, void *v) | 414 | static int aa_fs_seq_profname_show(struct seq_file *seq, void *v) |
| 250 | { | 415 | { |
| 251 | struct aa_replacedby *r = seq->private; | 416 | struct aa_proxy *proxy = seq->private; |
| 252 | struct aa_profile *profile = aa_get_profile_rcu(&r->profile); | 417 | struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile); |
| 253 | seq_printf(seq, "%s\n", profile->base.name); | 418 | seq_printf(seq, "%s\n", profile->base.name); |
| 254 | aa_put_profile(profile); | 419 | aa_put_profile(profile); |
| 255 | 420 | ||
| @@ -271,8 +436,8 @@ static const struct file_operations aa_fs_profname_fops = { | |||
| 271 | 436 | ||
| 272 | static int aa_fs_seq_profmode_show(struct seq_file *seq, void *v) | 437 | static int aa_fs_seq_profmode_show(struct seq_file *seq, void *v) |
| 273 | { | 438 | { |
| 274 | struct aa_replacedby *r = seq->private; | 439 | struct aa_proxy *proxy = seq->private; |
| 275 | struct aa_profile *profile = aa_get_profile_rcu(&r->profile); | 440 | struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile); |
| 276 | seq_printf(seq, "%s\n", aa_profile_mode_names[profile->mode]); | 441 | seq_printf(seq, "%s\n", aa_profile_mode_names[profile->mode]); |
| 277 | aa_put_profile(profile); | 442 | aa_put_profile(profile); |
| 278 | 443 | ||
| @@ -294,8 +459,8 @@ static const struct file_operations aa_fs_profmode_fops = { | |||
| 294 | 459 | ||
| 295 | static int aa_fs_seq_profattach_show(struct seq_file *seq, void *v) | 460 | static int aa_fs_seq_profattach_show(struct seq_file *seq, void *v) |
| 296 | { | 461 | { |
| 297 | struct aa_replacedby *r = seq->private; | 462 | struct aa_proxy *proxy = seq->private; |
| 298 | struct aa_profile *profile = aa_get_profile_rcu(&r->profile); | 463 | struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile); |
| 299 | if (profile->attach) | 464 | if (profile->attach) |
| 300 | seq_printf(seq, "%s\n", profile->attach); | 465 | seq_printf(seq, "%s\n", profile->attach); |
| 301 | else if (profile->xmatch) | 466 | else if (profile->xmatch) |
| @@ -322,8 +487,8 @@ static const struct file_operations aa_fs_profattach_fops = { | |||
| 322 | 487 | ||
| 323 | static int aa_fs_seq_hash_show(struct seq_file *seq, void *v) | 488 | static int aa_fs_seq_hash_show(struct seq_file *seq, void *v) |
| 324 | { | 489 | { |
| 325 | struct aa_replacedby *r = seq->private; | 490 | struct aa_proxy *proxy = seq->private; |
| 326 | struct aa_profile *profile = aa_get_profile_rcu(&r->profile); | 491 | struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile); |
| 327 | unsigned int i, size = aa_hash_size(); | 492 | unsigned int i, size = aa_hash_size(); |
| 328 | 493 | ||
| 329 | if (profile->hash) { | 494 | if (profile->hash) { |
| @@ -349,6 +514,145 @@ static const struct file_operations aa_fs_seq_hash_fops = { | |||
| 349 | .release = single_release, | 514 | .release = single_release, |
| 350 | }; | 515 | }; |
| 351 | 516 | ||
| 517 | |||
| 518 | static int aa_fs_seq_show_ns_level(struct seq_file *seq, void *v) | ||
| 519 | { | ||
| 520 | struct aa_ns *ns = aa_current_profile()->ns; | ||
| 521 | |||
| 522 | seq_printf(seq, "%d\n", ns->level); | ||
| 523 | |||
| 524 | return 0; | ||
| 525 | } | ||
| 526 | |||
| 527 | static int aa_fs_seq_open_ns_level(struct inode *inode, struct file *file) | ||
| 528 | { | ||
| 529 | return single_open(file, aa_fs_seq_show_ns_level, inode->i_private); | ||
| 530 | } | ||
| 531 | |||
| 532 | static const struct file_operations aa_fs_ns_level = { | ||
| 533 | .owner = THIS_MODULE, | ||
| 534 | .open = aa_fs_seq_open_ns_level, | ||
| 535 | .read = seq_read, | ||
| 536 | .llseek = seq_lseek, | ||
| 537 | .release = single_release, | ||
| 538 | }; | ||
| 539 | |||
| 540 | static int aa_fs_seq_show_ns_name(struct seq_file *seq, void *v) | ||
| 541 | { | ||
| 542 | struct aa_ns *ns = aa_current_profile()->ns; | ||
| 543 | |||
| 544 | seq_printf(seq, "%s\n", ns->base.name); | ||
| 545 | |||
| 546 | return 0; | ||
| 547 | } | ||
| 548 | |||
| 549 | static int aa_fs_seq_open_ns_name(struct inode *inode, struct file *file) | ||
| 550 | { | ||
| 551 | return single_open(file, aa_fs_seq_show_ns_name, inode->i_private); | ||
| 552 | } | ||
| 553 | |||
| 554 | static const struct file_operations aa_fs_ns_name = { | ||
| 555 | .owner = THIS_MODULE, | ||
| 556 | .open = aa_fs_seq_open_ns_name, | ||
| 557 | .read = seq_read, | ||
| 558 | .llseek = seq_lseek, | ||
| 559 | .release = single_release, | ||
| 560 | }; | ||
| 561 | |||
| 562 | static int rawdata_release(struct inode *inode, struct file *file) | ||
| 563 | { | ||
| 564 | /* TODO: switch to loaddata when profile switched to symlink */ | ||
| 565 | aa_put_loaddata(file->private_data); | ||
| 566 | |||
| 567 | return 0; | ||
| 568 | } | ||
| 569 | |||
| 570 | static int aa_fs_seq_raw_abi_show(struct seq_file *seq, void *v) | ||
| 571 | { | ||
| 572 | struct aa_proxy *proxy = seq->private; | ||
| 573 | struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile); | ||
| 574 | |||
| 575 | if (profile->rawdata->abi) { | ||
| 576 | seq_printf(seq, "v%d", profile->rawdata->abi); | ||
| 577 | seq_puts(seq, "\n"); | ||
| 578 | } | ||
| 579 | aa_put_profile(profile); | ||
| 580 | |||
| 581 | return 0; | ||
| 582 | } | ||
| 583 | |||
| 584 | static int aa_fs_seq_raw_abi_open(struct inode *inode, struct file *file) | ||
| 585 | { | ||
| 586 | return aa_fs_seq_profile_open(inode, file, aa_fs_seq_raw_abi_show); | ||
| 587 | } | ||
| 588 | |||
| 589 | static const struct file_operations aa_fs_seq_raw_abi_fops = { | ||
| 590 | .owner = THIS_MODULE, | ||
| 591 | .open = aa_fs_seq_raw_abi_open, | ||
| 592 | .read = seq_read, | ||
| 593 | .llseek = seq_lseek, | ||
| 594 | .release = aa_fs_seq_profile_release, | ||
| 595 | }; | ||
| 596 | |||
| 597 | static int aa_fs_seq_raw_hash_show(struct seq_file *seq, void *v) | ||
| 598 | { | ||
| 599 | struct aa_proxy *proxy = seq->private; | ||
| 600 | struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile); | ||
| 601 | unsigned int i, size = aa_hash_size(); | ||
| 602 | |||
| 603 | if (profile->rawdata->hash) { | ||
| 604 | for (i = 0; i < size; i++) | ||
| 605 | seq_printf(seq, "%.2x", profile->rawdata->hash[i]); | ||
| 606 | seq_puts(seq, "\n"); | ||
| 607 | } | ||
| 608 | aa_put_profile(profile); | ||
| 609 | |||
| 610 | return 0; | ||
| 611 | } | ||
| 612 | |||
| 613 | static int aa_fs_seq_raw_hash_open(struct inode *inode, struct file *file) | ||
| 614 | { | ||
| 615 | return aa_fs_seq_profile_open(inode, file, aa_fs_seq_raw_hash_show); | ||
| 616 | } | ||
| 617 | |||
| 618 | static const struct file_operations aa_fs_seq_raw_hash_fops = { | ||
| 619 | .owner = THIS_MODULE, | ||
| 620 | .open = aa_fs_seq_raw_hash_open, | ||
| 621 | .read = seq_read, | ||
| 622 | .llseek = seq_lseek, | ||
| 623 | .release = aa_fs_seq_profile_release, | ||
| 624 | }; | ||
| 625 | |||
| 626 | static ssize_t rawdata_read(struct file *file, char __user *buf, size_t size, | ||
| 627 | loff_t *ppos) | ||
| 628 | { | ||
| 629 | struct aa_loaddata *rawdata = file->private_data; | ||
| 630 | |||
| 631 | return simple_read_from_buffer(buf, size, ppos, rawdata->data, | ||
| 632 | rawdata->size); | ||
| 633 | } | ||
| 634 | |||
| 635 | static int rawdata_open(struct inode *inode, struct file *file) | ||
| 636 | { | ||
| 637 | struct aa_proxy *proxy = inode->i_private; | ||
| 638 | struct aa_profile *profile; | ||
| 639 | |||
| 640 | if (!policy_view_capable(NULL)) | ||
| 641 | return -EACCES; | ||
| 642 | profile = aa_get_profile_rcu(&proxy->profile); | ||
| 643 | file->private_data = aa_get_loaddata(profile->rawdata); | ||
| 644 | aa_put_profile(profile); | ||
| 645 | |||
| 646 | return 0; | ||
| 647 | } | ||
| 648 | |||
| 649 | static const struct file_operations aa_fs_rawdata_fops = { | ||
| 650 | .open = rawdata_open, | ||
| 651 | .read = rawdata_read, | ||
| 652 | .llseek = generic_file_llseek, | ||
| 653 | .release = rawdata_release, | ||
| 654 | }; | ||
| 655 | |||
| 352 | /** fns to setup dynamic per profile/namespace files **/ | 656 | /** fns to setup dynamic per profile/namespace files **/ |
| 353 | void __aa_fs_profile_rmdir(struct aa_profile *profile) | 657 | void __aa_fs_profile_rmdir(struct aa_profile *profile) |
| 354 | { | 658 | { |
| @@ -362,13 +666,13 @@ void __aa_fs_profile_rmdir(struct aa_profile *profile) | |||
| 362 | __aa_fs_profile_rmdir(child); | 666 | __aa_fs_profile_rmdir(child); |
| 363 | 667 | ||
| 364 | for (i = AAFS_PROF_SIZEOF - 1; i >= 0; --i) { | 668 | for (i = AAFS_PROF_SIZEOF - 1; i >= 0; --i) { |
| 365 | struct aa_replacedby *r; | 669 | struct aa_proxy *proxy; |
| 366 | if (!profile->dents[i]) | 670 | if (!profile->dents[i]) |
| 367 | continue; | 671 | continue; |
| 368 | 672 | ||
| 369 | r = d_inode(profile->dents[i])->i_private; | 673 | proxy = d_inode(profile->dents[i])->i_private; |
| 370 | securityfs_remove(profile->dents[i]); | 674 | securityfs_remove(profile->dents[i]); |
| 371 | aa_put_replacedby(r); | 675 | aa_put_proxy(proxy); |
| 372 | profile->dents[i] = NULL; | 676 | profile->dents[i] = NULL; |
| 373 | } | 677 | } |
| 374 | } | 678 | } |
| @@ -390,12 +694,12 @@ static struct dentry *create_profile_file(struct dentry *dir, const char *name, | |||
| 390 | struct aa_profile *profile, | 694 | struct aa_profile *profile, |
| 391 | const struct file_operations *fops) | 695 | const struct file_operations *fops) |
| 392 | { | 696 | { |
| 393 | struct aa_replacedby *r = aa_get_replacedby(profile->replacedby); | 697 | struct aa_proxy *proxy = aa_get_proxy(profile->proxy); |
| 394 | struct dentry *dent; | 698 | struct dentry *dent; |
| 395 | 699 | ||
| 396 | dent = securityfs_create_file(name, S_IFREG | 0444, dir, r, fops); | 700 | dent = securityfs_create_file(name, S_IFREG | 0444, dir, proxy, fops); |
| 397 | if (IS_ERR(dent)) | 701 | if (IS_ERR(dent)) |
| 398 | aa_put_replacedby(r); | 702 | aa_put_proxy(proxy); |
| 399 | 703 | ||
| 400 | return dent; | 704 | return dent; |
| 401 | } | 705 | } |
| @@ -460,6 +764,29 @@ int __aa_fs_profile_mkdir(struct aa_profile *profile, struct dentry *parent) | |||
| 460 | profile->dents[AAFS_PROF_HASH] = dent; | 764 | profile->dents[AAFS_PROF_HASH] = dent; |
| 461 | } | 765 | } |
| 462 | 766 | ||
| 767 | if (profile->rawdata) { | ||
| 768 | dent = create_profile_file(dir, "raw_sha1", profile, | ||
| 769 | &aa_fs_seq_raw_hash_fops); | ||
| 770 | if (IS_ERR(dent)) | ||
| 771 | goto fail; | ||
| 772 | profile->dents[AAFS_PROF_RAW_HASH] = dent; | ||
| 773 | |||
| 774 | dent = create_profile_file(dir, "raw_abi", profile, | ||
| 775 | &aa_fs_seq_raw_abi_fops); | ||
| 776 | if (IS_ERR(dent)) | ||
| 777 | goto fail; | ||
| 778 | profile->dents[AAFS_PROF_RAW_ABI] = dent; | ||
| 779 | |||
| 780 | dent = securityfs_create_file("raw_data", S_IFREG | 0444, dir, | ||
| 781 | profile->proxy, | ||
| 782 | &aa_fs_rawdata_fops); | ||
| 783 | if (IS_ERR(dent)) | ||
| 784 | goto fail; | ||
| 785 | profile->dents[AAFS_PROF_RAW_DATA] = dent; | ||
| 786 | d_inode(dent)->i_size = profile->rawdata->size; | ||
| 787 | aa_get_proxy(profile->proxy); | ||
| 788 | } | ||
| 789 | |||
| 463 | list_for_each_entry(child, &profile->base.profiles, base.list) { | 790 | list_for_each_entry(child, &profile->base.profiles, base.list) { |
| 464 | error = __aa_fs_profile_mkdir(child, prof_child_dir(profile)); | 791 | error = __aa_fs_profile_mkdir(child, prof_child_dir(profile)); |
| 465 | if (error) | 792 | if (error) |
| @@ -477,9 +804,9 @@ fail2: | |||
| 477 | return error; | 804 | return error; |
| 478 | } | 805 | } |
| 479 | 806 | ||
| 480 | void __aa_fs_namespace_rmdir(struct aa_namespace *ns) | 807 | void __aa_fs_ns_rmdir(struct aa_ns *ns) |
| 481 | { | 808 | { |
| 482 | struct aa_namespace *sub; | 809 | struct aa_ns *sub; |
| 483 | struct aa_profile *child; | 810 | struct aa_profile *child; |
| 484 | int i; | 811 | int i; |
| 485 | 812 | ||
| @@ -491,51 +818,116 @@ void __aa_fs_namespace_rmdir(struct aa_namespace *ns) | |||
| 491 | 818 | ||
| 492 | list_for_each_entry(sub, &ns->sub_ns, base.list) { | 819 | list_for_each_entry(sub, &ns->sub_ns, base.list) { |
| 493 | mutex_lock(&sub->lock); | 820 | mutex_lock(&sub->lock); |
| 494 | __aa_fs_namespace_rmdir(sub); | 821 | __aa_fs_ns_rmdir(sub); |
| 495 | mutex_unlock(&sub->lock); | 822 | mutex_unlock(&sub->lock); |
| 496 | } | 823 | } |
| 497 | 824 | ||
| 825 | if (ns_subns_dir(ns)) { | ||
| 826 | sub = d_inode(ns_subns_dir(ns))->i_private; | ||
| 827 | aa_put_ns(sub); | ||
| 828 | } | ||
| 829 | if (ns_subload(ns)) { | ||
| 830 | sub = d_inode(ns_subload(ns))->i_private; | ||
| 831 | aa_put_ns(sub); | ||
| 832 | } | ||
| 833 | if (ns_subreplace(ns)) { | ||
| 834 | sub = d_inode(ns_subreplace(ns))->i_private; | ||
| 835 | aa_put_ns(sub); | ||
| 836 | } | ||
| 837 | if (ns_subremove(ns)) { | ||
| 838 | sub = d_inode(ns_subremove(ns))->i_private; | ||
| 839 | aa_put_ns(sub); | ||
| 840 | } | ||
| 841 | |||
| 498 | for (i = AAFS_NS_SIZEOF - 1; i >= 0; --i) { | 842 | for (i = AAFS_NS_SIZEOF - 1; i >= 0; --i) { |
| 499 | securityfs_remove(ns->dents[i]); | 843 | securityfs_remove(ns->dents[i]); |
| 500 | ns->dents[i] = NULL; | 844 | ns->dents[i] = NULL; |
| 501 | } | 845 | } |
| 502 | } | 846 | } |
| 503 | 847 | ||
| 504 | int __aa_fs_namespace_mkdir(struct aa_namespace *ns, struct dentry *parent, | 848 | /* assumes cleanup in caller */ |
| 505 | const char *name) | 849 | static int __aa_fs_ns_mkdir_entries(struct aa_ns *ns, struct dentry *dir) |
| 850 | { | ||
| 851 | struct dentry *dent; | ||
| 852 | |||
| 853 | AA_BUG(!ns); | ||
| 854 | AA_BUG(!dir); | ||
| 855 | |||
| 856 | dent = securityfs_create_dir("profiles", dir); | ||
| 857 | if (IS_ERR(dent)) | ||
| 858 | return PTR_ERR(dent); | ||
| 859 | ns_subprofs_dir(ns) = dent; | ||
| 860 | |||
| 861 | dent = securityfs_create_dir("raw_data", dir); | ||
| 862 | if (IS_ERR(dent)) | ||
| 863 | return PTR_ERR(dent); | ||
| 864 | ns_subdata_dir(ns) = dent; | ||
| 865 | |||
| 866 | dent = securityfs_create_file(".load", 0640, dir, ns, | ||
| 867 | &aa_fs_profile_load); | ||
| 868 | if (IS_ERR(dent)) | ||
| 869 | return PTR_ERR(dent); | ||
| 870 | aa_get_ns(ns); | ||
| 871 | ns_subload(ns) = dent; | ||
| 872 | |||
| 873 | dent = securityfs_create_file(".replace", 0640, dir, ns, | ||
| 874 | &aa_fs_profile_replace); | ||
| 875 | if (IS_ERR(dent)) | ||
| 876 | return PTR_ERR(dent); | ||
| 877 | aa_get_ns(ns); | ||
| 878 | ns_subreplace(ns) = dent; | ||
| 879 | |||
| 880 | dent = securityfs_create_file(".remove", 0640, dir, ns, | ||
| 881 | &aa_fs_profile_remove); | ||
| 882 | if (IS_ERR(dent)) | ||
| 883 | return PTR_ERR(dent); | ||
| 884 | aa_get_ns(ns); | ||
| 885 | ns_subremove(ns) = dent; | ||
| 886 | |||
| 887 | dent = securityfs_create_dir("namespaces", dir); | ||
| 888 | if (IS_ERR(dent)) | ||
| 889 | return PTR_ERR(dent); | ||
| 890 | aa_get_ns(ns); | ||
| 891 | ns_subns_dir(ns) = dent; | ||
| 892 | |||
| 893 | return 0; | ||
| 894 | } | ||
| 895 | |||
| 896 | int __aa_fs_ns_mkdir(struct aa_ns *ns, struct dentry *parent, const char *name) | ||
| 506 | { | 897 | { |
| 507 | struct aa_namespace *sub; | 898 | struct aa_ns *sub; |
| 508 | struct aa_profile *child; | 899 | struct aa_profile *child; |
| 509 | struct dentry *dent, *dir; | 900 | struct dentry *dent, *dir; |
| 510 | int error; | 901 | int error; |
| 511 | 902 | ||
| 903 | AA_BUG(!ns); | ||
| 904 | AA_BUG(!parent); | ||
| 905 | AA_BUG(!mutex_is_locked(&ns->lock)); | ||
| 906 | |||
| 512 | if (!name) | 907 | if (!name) |
| 513 | name = ns->base.name; | 908 | name = ns->base.name; |
| 514 | 909 | ||
| 910 | /* create ns dir if it doesn't already exist */ | ||
| 515 | dent = securityfs_create_dir(name, parent); | 911 | dent = securityfs_create_dir(name, parent); |
| 516 | if (IS_ERR(dent)) | 912 | if (IS_ERR(dent)) |
| 517 | goto fail; | 913 | goto fail; |
| 518 | ns_dir(ns) = dir = dent; | ||
| 519 | 914 | ||
| 520 | dent = securityfs_create_dir("profiles", dir); | 915 | ns_dir(ns) = dir = dent; |
| 521 | if (IS_ERR(dent)) | 916 | error = __aa_fs_ns_mkdir_entries(ns, dir); |
| 522 | goto fail; | 917 | if (error) |
| 523 | ns_subprofs_dir(ns) = dent; | 918 | goto fail2; |
| 524 | |||
| 525 | dent = securityfs_create_dir("namespaces", dir); | ||
| 526 | if (IS_ERR(dent)) | ||
| 527 | goto fail; | ||
| 528 | ns_subns_dir(ns) = dent; | ||
| 529 | 919 | ||
| 920 | /* profiles */ | ||
| 530 | list_for_each_entry(child, &ns->base.profiles, base.list) { | 921 | list_for_each_entry(child, &ns->base.profiles, base.list) { |
| 531 | error = __aa_fs_profile_mkdir(child, ns_subprofs_dir(ns)); | 922 | error = __aa_fs_profile_mkdir(child, ns_subprofs_dir(ns)); |
| 532 | if (error) | 923 | if (error) |
| 533 | goto fail2; | 924 | goto fail2; |
| 534 | } | 925 | } |
| 535 | 926 | ||
| 927 | /* subnamespaces */ | ||
| 536 | list_for_each_entry(sub, &ns->sub_ns, base.list) { | 928 | list_for_each_entry(sub, &ns->sub_ns, base.list) { |
| 537 | mutex_lock(&sub->lock); | 929 | mutex_lock(&sub->lock); |
| 538 | error = __aa_fs_namespace_mkdir(sub, ns_subns_dir(ns), NULL); | 930 | error = __aa_fs_ns_mkdir(sub, ns_subns_dir(ns), NULL); |
| 539 | mutex_unlock(&sub->lock); | 931 | mutex_unlock(&sub->lock); |
| 540 | if (error) | 932 | if (error) |
| 541 | goto fail2; | 933 | goto fail2; |
| @@ -547,7 +939,7 @@ fail: | |||
| 547 | error = PTR_ERR(dent); | 939 | error = PTR_ERR(dent); |
| 548 | 940 | ||
| 549 | fail2: | 941 | fail2: |
| 550 | __aa_fs_namespace_rmdir(ns); | 942 | __aa_fs_ns_rmdir(ns); |
| 551 | 943 | ||
| 552 | return error; | 944 | return error; |
| 553 | } | 945 | } |
| @@ -556,7 +948,7 @@ fail2: | |||
| 556 | #define list_entry_is_head(pos, head, member) (&pos->member == (head)) | 948 | #define list_entry_is_head(pos, head, member) (&pos->member == (head)) |
| 557 | 949 | ||
| 558 | /** | 950 | /** |
| 559 | * __next_namespace - find the next namespace to list | 951 | * __next_ns - find the next namespace to list |
| 560 | * @root: root namespace to stop search at (NOT NULL) | 952 | * @root: root namespace to stop search at (NOT NULL) |
| 561 | * @ns: current ns position (NOT NULL) | 953 | * @ns: current ns position (NOT NULL) |
| 562 | * | 954 | * |
| @@ -567,10 +959,9 @@ fail2: | |||
| 567 | * Requires: ns->parent->lock to be held | 959 | * Requires: ns->parent->lock to be held |
| 568 | * NOTE: will not unlock root->lock | 960 | * NOTE: will not unlock root->lock |
| 569 | */ | 961 | */ |
| 570 | static struct aa_namespace *__next_namespace(struct aa_namespace *root, | 962 | static struct aa_ns *__next_ns(struct aa_ns *root, struct aa_ns *ns) |
| 571 | struct aa_namespace *ns) | ||
| 572 | { | 963 | { |
| 573 | struct aa_namespace *parent, *next; | 964 | struct aa_ns *parent, *next; |
| 574 | 965 | ||
| 575 | /* is next namespace a child */ | 966 | /* is next namespace a child */ |
| 576 | if (!list_empty(&ns->sub_ns)) { | 967 | if (!list_empty(&ns->sub_ns)) { |
| @@ -603,10 +994,10 @@ static struct aa_namespace *__next_namespace(struct aa_namespace *root, | |||
| 603 | * Returns: unrefcounted profile or NULL if no profile | 994 | * Returns: unrefcounted profile or NULL if no profile |
| 604 | * Requires: profile->ns.lock to be held | 995 | * Requires: profile->ns.lock to be held |
| 605 | */ | 996 | */ |
| 606 | static struct aa_profile *__first_profile(struct aa_namespace *root, | 997 | static struct aa_profile *__first_profile(struct aa_ns *root, |
| 607 | struct aa_namespace *ns) | 998 | struct aa_ns *ns) |
| 608 | { | 999 | { |
| 609 | for (; ns; ns = __next_namespace(root, ns)) { | 1000 | for (; ns; ns = __next_ns(root, ns)) { |
| 610 | if (!list_empty(&ns->base.profiles)) | 1001 | if (!list_empty(&ns->base.profiles)) |
| 611 | return list_first_entry(&ns->base.profiles, | 1002 | return list_first_entry(&ns->base.profiles, |
| 612 | struct aa_profile, base.list); | 1003 | struct aa_profile, base.list); |
| @@ -626,7 +1017,7 @@ static struct aa_profile *__first_profile(struct aa_namespace *root, | |||
| 626 | static struct aa_profile *__next_profile(struct aa_profile *p) | 1017 | static struct aa_profile *__next_profile(struct aa_profile *p) |
| 627 | { | 1018 | { |
| 628 | struct aa_profile *parent; | 1019 | struct aa_profile *parent; |
| 629 | struct aa_namespace *ns = p->ns; | 1020 | struct aa_ns *ns = p->ns; |
| 630 | 1021 | ||
| 631 | /* is next profile a child */ | 1022 | /* is next profile a child */ |
| 632 | if (!list_empty(&p->base.profiles)) | 1023 | if (!list_empty(&p->base.profiles)) |
| @@ -660,7 +1051,7 @@ static struct aa_profile *__next_profile(struct aa_profile *p) | |||
| 660 | * | 1051 | * |
| 661 | * Returns: next profile or NULL if there isn't one | 1052 | * Returns: next profile or NULL if there isn't one |
| 662 | */ | 1053 | */ |
| 663 | static struct aa_profile *next_profile(struct aa_namespace *root, | 1054 | static struct aa_profile *next_profile(struct aa_ns *root, |
| 664 | struct aa_profile *profile) | 1055 | struct aa_profile *profile) |
| 665 | { | 1056 | { |
| 666 | struct aa_profile *next = __next_profile(profile); | 1057 | struct aa_profile *next = __next_profile(profile); |
| @@ -668,7 +1059,7 @@ static struct aa_profile *next_profile(struct aa_namespace *root, | |||
| 668 | return next; | 1059 | return next; |
| 669 | 1060 | ||
| 670 | /* finished all profiles in namespace move to next namespace */ | 1061 | /* finished all profiles in namespace move to next namespace */ |
| 671 | return __first_profile(root, __next_namespace(root, profile->ns)); | 1062 | return __first_profile(root, __next_ns(root, profile->ns)); |
| 672 | } | 1063 | } |
| 673 | 1064 | ||
| 674 | /** | 1065 | /** |
| @@ -683,9 +1074,9 @@ static struct aa_profile *next_profile(struct aa_namespace *root, | |||
| 683 | static void *p_start(struct seq_file *f, loff_t *pos) | 1074 | static void *p_start(struct seq_file *f, loff_t *pos) |
| 684 | { | 1075 | { |
| 685 | struct aa_profile *profile = NULL; | 1076 | struct aa_profile *profile = NULL; |
| 686 | struct aa_namespace *root = aa_current_profile()->ns; | 1077 | struct aa_ns *root = aa_current_profile()->ns; |
| 687 | loff_t l = *pos; | 1078 | loff_t l = *pos; |
| 688 | f->private = aa_get_namespace(root); | 1079 | f->private = aa_get_ns(root); |
| 689 | 1080 | ||
| 690 | 1081 | ||
| 691 | /* find the first profile */ | 1082 | /* find the first profile */ |
| @@ -712,7 +1103,7 @@ static void *p_start(struct seq_file *f, loff_t *pos) | |||
| 712 | static void *p_next(struct seq_file *f, void *p, loff_t *pos) | 1103 | static void *p_next(struct seq_file *f, void *p, loff_t *pos) |
| 713 | { | 1104 | { |
| 714 | struct aa_profile *profile = p; | 1105 | struct aa_profile *profile = p; |
| 715 | struct aa_namespace *ns = f->private; | 1106 | struct aa_ns *ns = f->private; |
| 716 | (*pos)++; | 1107 | (*pos)++; |
| 717 | 1108 | ||
| 718 | return next_profile(ns, profile); | 1109 | return next_profile(ns, profile); |
| @@ -728,14 +1119,14 @@ static void *p_next(struct seq_file *f, void *p, loff_t *pos) | |||
| 728 | static void p_stop(struct seq_file *f, void *p) | 1119 | static void p_stop(struct seq_file *f, void *p) |
| 729 | { | 1120 | { |
| 730 | struct aa_profile *profile = p; | 1121 | struct aa_profile *profile = p; |
| 731 | struct aa_namespace *root = f->private, *ns; | 1122 | struct aa_ns *root = f->private, *ns; |
| 732 | 1123 | ||
| 733 | if (profile) { | 1124 | if (profile) { |
| 734 | for (ns = profile->ns; ns && ns != root; ns = ns->parent) | 1125 | for (ns = profile->ns; ns && ns != root; ns = ns->parent) |
| 735 | mutex_unlock(&ns->lock); | 1126 | mutex_unlock(&ns->lock); |
| 736 | } | 1127 | } |
| 737 | mutex_unlock(&root->lock); | 1128 | mutex_unlock(&root->lock); |
| 738 | aa_put_namespace(root); | 1129 | aa_put_ns(root); |
| 739 | } | 1130 | } |
| 740 | 1131 | ||
| 741 | /** | 1132 | /** |
| @@ -748,10 +1139,10 @@ static void p_stop(struct seq_file *f, void *p) | |||
| 748 | static int seq_show_profile(struct seq_file *f, void *p) | 1139 | static int seq_show_profile(struct seq_file *f, void *p) |
| 749 | { | 1140 | { |
| 750 | struct aa_profile *profile = (struct aa_profile *)p; | 1141 | struct aa_profile *profile = (struct aa_profile *)p; |
| 751 | struct aa_namespace *root = f->private; | 1142 | struct aa_ns *root = f->private; |
| 752 | 1143 | ||
| 753 | if (profile->ns != root) | 1144 | if (profile->ns != root) |
| 754 | seq_printf(f, ":%s://", aa_ns_name(root, profile->ns)); | 1145 | seq_printf(f, ":%s://", aa_ns_name(root, profile->ns, true)); |
| 755 | seq_printf(f, "%s (%s)\n", profile->base.hname, | 1146 | seq_printf(f, "%s (%s)\n", profile->base.hname, |
| 756 | aa_profile_mode_names[profile->mode]); | 1147 | aa_profile_mode_names[profile->mode]); |
| 757 | 1148 | ||
| @@ -767,6 +1158,9 @@ static const struct seq_operations aa_fs_profiles_op = { | |||
| 767 | 1158 | ||
| 768 | static int profiles_open(struct inode *inode, struct file *file) | 1159 | static int profiles_open(struct inode *inode, struct file *file) |
| 769 | { | 1160 | { |
| 1161 | if (!policy_view_capable(NULL)) | ||
| 1162 | return -EACCES; | ||
| 1163 | |||
| 770 | return seq_open(file, &aa_fs_profiles_op); | 1164 | return seq_open(file, &aa_fs_profiles_op); |
| 771 | } | 1165 | } |
| 772 | 1166 | ||
| @@ -795,12 +1189,20 @@ static struct aa_fs_entry aa_fs_entry_domain[] = { | |||
| 795 | AA_FS_FILE_BOOLEAN("change_hatv", 1), | 1189 | AA_FS_FILE_BOOLEAN("change_hatv", 1), |
| 796 | AA_FS_FILE_BOOLEAN("change_onexec", 1), | 1190 | AA_FS_FILE_BOOLEAN("change_onexec", 1), |
| 797 | AA_FS_FILE_BOOLEAN("change_profile", 1), | 1191 | AA_FS_FILE_BOOLEAN("change_profile", 1), |
| 1192 | AA_FS_FILE_BOOLEAN("fix_binfmt_elf_mmap", 1), | ||
| 1193 | AA_FS_FILE_STRING("version", "1.2"), | ||
| 1194 | { } | ||
| 1195 | }; | ||
| 1196 | |||
| 1197 | static struct aa_fs_entry aa_fs_entry_versions[] = { | ||
| 1198 | AA_FS_FILE_BOOLEAN("v5", 1), | ||
| 798 | { } | 1199 | { } |
| 799 | }; | 1200 | }; |
| 800 | 1201 | ||
| 801 | static struct aa_fs_entry aa_fs_entry_policy[] = { | 1202 | static struct aa_fs_entry aa_fs_entry_policy[] = { |
| 802 | AA_FS_FILE_BOOLEAN("set_load", 1), | 1203 | AA_FS_DIR("versions", aa_fs_entry_versions), |
| 803 | {} | 1204 | AA_FS_FILE_BOOLEAN("set_load", 1), |
| 1205 | { } | ||
| 804 | }; | 1206 | }; |
| 805 | 1207 | ||
| 806 | static struct aa_fs_entry aa_fs_entry_features[] = { | 1208 | static struct aa_fs_entry aa_fs_entry_features[] = { |
| @@ -814,10 +1216,10 @@ static struct aa_fs_entry aa_fs_entry_features[] = { | |||
| 814 | }; | 1216 | }; |
| 815 | 1217 | ||
| 816 | static struct aa_fs_entry aa_fs_entry_apparmor[] = { | 1218 | static struct aa_fs_entry aa_fs_entry_apparmor[] = { |
| 817 | AA_FS_FILE_FOPS(".load", 0640, &aa_fs_profile_load), | 1219 | AA_FS_FILE_FOPS(".access", 0640, &aa_fs_access), |
| 818 | AA_FS_FILE_FOPS(".replace", 0640, &aa_fs_profile_replace), | 1220 | AA_FS_FILE_FOPS(".ns_level", 0666, &aa_fs_ns_level), |
| 819 | AA_FS_FILE_FOPS(".remove", 0640, &aa_fs_profile_remove), | 1221 | AA_FS_FILE_FOPS(".ns_name", 0640, &aa_fs_ns_name), |
| 820 | AA_FS_FILE_FOPS("profiles", 0640, &aa_fs_profiles_fops), | 1222 | AA_FS_FILE_FOPS("profiles", 0440, &aa_fs_profiles_fops), |
| 821 | AA_FS_DIR("features", aa_fs_entry_features), | 1223 | AA_FS_DIR("features", aa_fs_entry_features), |
| 822 | { } | 1224 | { } |
| 823 | }; | 1225 | }; |
| @@ -926,6 +1328,52 @@ void __init aa_destroy_aafs(void) | |||
| 926 | aafs_remove_dir(&aa_fs_entry); | 1328 | aafs_remove_dir(&aa_fs_entry); |
| 927 | } | 1329 | } |
| 928 | 1330 | ||
| 1331 | |||
| 1332 | #define NULL_FILE_NAME ".null" | ||
| 1333 | struct path aa_null; | ||
| 1334 | |||
| 1335 | static int aa_mk_null_file(struct dentry *parent) | ||
| 1336 | { | ||
| 1337 | struct vfsmount *mount = NULL; | ||
| 1338 | struct dentry *dentry; | ||
| 1339 | struct inode *inode; | ||
| 1340 | int count = 0; | ||
| 1341 | int error = simple_pin_fs(parent->d_sb->s_type, &mount, &count); | ||
| 1342 | |||
| 1343 | if (error) | ||
| 1344 | return error; | ||
| 1345 | |||
| 1346 | inode_lock(d_inode(parent)); | ||
| 1347 | dentry = lookup_one_len(NULL_FILE_NAME, parent, strlen(NULL_FILE_NAME)); | ||
| 1348 | if (IS_ERR(dentry)) { | ||
| 1349 | error = PTR_ERR(dentry); | ||
| 1350 | goto out; | ||
| 1351 | } | ||
| 1352 | inode = new_inode(parent->d_inode->i_sb); | ||
| 1353 | if (!inode) { | ||
| 1354 | error = -ENOMEM; | ||
| 1355 | goto out1; | ||
| 1356 | } | ||
| 1357 | |||
| 1358 | inode->i_ino = get_next_ino(); | ||
| 1359 | inode->i_mode = S_IFCHR | S_IRUGO | S_IWUGO; | ||
| 1360 | inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; | ||
| 1361 | init_special_inode(inode, S_IFCHR | S_IRUGO | S_IWUGO, | ||
| 1362 | MKDEV(MEM_MAJOR, 3)); | ||
| 1363 | d_instantiate(dentry, inode); | ||
| 1364 | aa_null.dentry = dget(dentry); | ||
| 1365 | aa_null.mnt = mntget(mount); | ||
| 1366 | |||
| 1367 | error = 0; | ||
| 1368 | |||
| 1369 | out1: | ||
| 1370 | dput(dentry); | ||
| 1371 | out: | ||
| 1372 | inode_unlock(d_inode(parent)); | ||
| 1373 | simple_release_fs(&mount, &count); | ||
| 1374 | return error; | ||
| 1375 | } | ||
| 1376 | |||
| 929 | /** | 1377 | /** |
| 930 | * aa_create_aafs - create the apparmor security filesystem | 1378 | * aa_create_aafs - create the apparmor security filesystem |
| 931 | * | 1379 | * |
| @@ -935,6 +1383,7 @@ void __init aa_destroy_aafs(void) | |||
| 935 | */ | 1383 | */ |
| 936 | static int __init aa_create_aafs(void) | 1384 | static int __init aa_create_aafs(void) |
| 937 | { | 1385 | { |
| 1386 | struct dentry *dent; | ||
| 938 | int error; | 1387 | int error; |
| 939 | 1388 | ||
| 940 | if (!apparmor_initialized) | 1389 | if (!apparmor_initialized) |
| @@ -950,12 +1399,42 @@ static int __init aa_create_aafs(void) | |||
| 950 | if (error) | 1399 | if (error) |
| 951 | goto error; | 1400 | goto error; |
| 952 | 1401 | ||
| 953 | error = __aa_fs_namespace_mkdir(root_ns, aa_fs_entry.dentry, | 1402 | dent = securityfs_create_file(".load", 0666, aa_fs_entry.dentry, |
| 954 | "policy"); | 1403 | NULL, &aa_fs_profile_load); |
| 1404 | if (IS_ERR(dent)) { | ||
| 1405 | error = PTR_ERR(dent); | ||
| 1406 | goto error; | ||
| 1407 | } | ||
| 1408 | ns_subload(root_ns) = dent; | ||
| 1409 | |||
| 1410 | dent = securityfs_create_file(".replace", 0666, aa_fs_entry.dentry, | ||
| 1411 | NULL, &aa_fs_profile_replace); | ||
| 1412 | if (IS_ERR(dent)) { | ||
| 1413 | error = PTR_ERR(dent); | ||
| 1414 | goto error; | ||
| 1415 | } | ||
| 1416 | ns_subreplace(root_ns) = dent; | ||
| 1417 | |||
| 1418 | dent = securityfs_create_file(".remove", 0666, aa_fs_entry.dentry, | ||
| 1419 | NULL, &aa_fs_profile_remove); | ||
| 1420 | if (IS_ERR(dent)) { | ||
| 1421 | error = PTR_ERR(dent); | ||
| 1422 | goto error; | ||
| 1423 | } | ||
| 1424 | ns_subremove(root_ns) = dent; | ||
| 1425 | |||
| 1426 | mutex_lock(&root_ns->lock); | ||
| 1427 | error = __aa_fs_ns_mkdir(root_ns, aa_fs_entry.dentry, "policy"); | ||
| 1428 | mutex_unlock(&root_ns->lock); | ||
| 1429 | |||
| 1430 | if (error) | ||
| 1431 | goto error; | ||
| 1432 | |||
| 1433 | error = aa_mk_null_file(aa_fs_entry.dentry); | ||
| 955 | if (error) | 1434 | if (error) |
| 956 | goto error; | 1435 | goto error; |
| 957 | 1436 | ||
| 958 | /* TODO: add support for apparmorfs_null and apparmorfs_mnt */ | 1437 | /* TODO: add default profile to apparmorfs */ |
| 959 | 1438 | ||
| 960 | /* Report that AppArmor fs is enabled */ | 1439 | /* Report that AppArmor fs is enabled */ |
| 961 | aa_info_message("AppArmor Filesystem Enabled"); | 1440 | aa_info_message("AppArmor Filesystem Enabled"); |
diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c index 3a7f1da1425e..87f40fa8c431 100644 --- a/security/apparmor/audit.c +++ b/security/apparmor/audit.c | |||
| @@ -18,60 +18,8 @@ | |||
| 18 | #include "include/apparmor.h" | 18 | #include "include/apparmor.h" |
| 19 | #include "include/audit.h" | 19 | #include "include/audit.h" |
| 20 | #include "include/policy.h" | 20 | #include "include/policy.h" |
| 21 | #include "include/policy_ns.h" | ||
| 21 | 22 | ||
| 22 | const char *const 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 | 23 | ||
| 76 | const char *const audit_mode_names[] = { | 24 | const char *const audit_mode_names[] = { |
| 77 | "normal", | 25 | "normal", |
| @@ -114,23 +62,23 @@ static void audit_pre(struct audit_buffer *ab, void *ca) | |||
| 114 | 62 | ||
| 115 | if (aa_g_audit_header) { | 63 | if (aa_g_audit_header) { |
| 116 | audit_log_format(ab, "apparmor="); | 64 | audit_log_format(ab, "apparmor="); |
| 117 | audit_log_string(ab, aa_audit_type[sa->aad->type]); | 65 | audit_log_string(ab, aa_audit_type[aad(sa)->type]); |
| 118 | } | 66 | } |
| 119 | 67 | ||
| 120 | if (sa->aad->op) { | 68 | if (aad(sa)->op) { |
| 121 | audit_log_format(ab, " operation="); | 69 | audit_log_format(ab, " operation="); |
| 122 | audit_log_string(ab, op_table[sa->aad->op]); | 70 | audit_log_string(ab, aad(sa)->op); |
| 123 | } | 71 | } |
| 124 | 72 | ||
| 125 | if (sa->aad->info) { | 73 | if (aad(sa)->info) { |
| 126 | audit_log_format(ab, " info="); | 74 | audit_log_format(ab, " info="); |
| 127 | audit_log_string(ab, sa->aad->info); | 75 | audit_log_string(ab, aad(sa)->info); |
| 128 | if (sa->aad->error) | 76 | if (aad(sa)->error) |
| 129 | audit_log_format(ab, " error=%d", sa->aad->error); | 77 | audit_log_format(ab, " error=%d", aad(sa)->error); |
| 130 | } | 78 | } |
| 131 | 79 | ||
| 132 | if (sa->aad->profile) { | 80 | if (aad(sa)->profile) { |
| 133 | struct aa_profile *profile = sa->aad->profile; | 81 | struct aa_profile *profile = aad(sa)->profile; |
| 134 | if (profile->ns != root_ns) { | 82 | if (profile->ns != root_ns) { |
| 135 | audit_log_format(ab, " namespace="); | 83 | audit_log_format(ab, " namespace="); |
| 136 | audit_log_untrustedstring(ab, profile->ns->base.hname); | 84 | audit_log_untrustedstring(ab, profile->ns->base.hname); |
| @@ -139,9 +87,9 @@ static void audit_pre(struct audit_buffer *ab, void *ca) | |||
| 139 | audit_log_untrustedstring(ab, profile->base.hname); | 87 | audit_log_untrustedstring(ab, profile->base.hname); |
| 140 | } | 88 | } |
| 141 | 89 | ||
| 142 | if (sa->aad->name) { | 90 | if (aad(sa)->name) { |
| 143 | audit_log_format(ab, " name="); | 91 | audit_log_format(ab, " name="); |
| 144 | audit_log_untrustedstring(ab, sa->aad->name); | 92 | audit_log_untrustedstring(ab, aad(sa)->name); |
| 145 | } | 93 | } |
| 146 | } | 94 | } |
| 147 | 95 | ||
| @@ -153,7 +101,7 @@ static void audit_pre(struct audit_buffer *ab, void *ca) | |||
| 153 | void aa_audit_msg(int type, struct common_audit_data *sa, | 101 | void aa_audit_msg(int type, struct common_audit_data *sa, |
| 154 | void (*cb) (struct audit_buffer *, void *)) | 102 | void (*cb) (struct audit_buffer *, void *)) |
| 155 | { | 103 | { |
| 156 | sa->aad->type = type; | 104 | aad(sa)->type = type; |
| 157 | common_lsm_audit(sa, audit_pre, cb); | 105 | common_lsm_audit(sa, audit_pre, cb); |
| 158 | } | 106 | } |
| 159 | 107 | ||
| @@ -161,7 +109,6 @@ void aa_audit_msg(int type, struct common_audit_data *sa, | |||
| 161 | * aa_audit - Log a profile based audit event to the audit subsystem | 109 | * aa_audit - Log a profile based audit event to the audit subsystem |
| 162 | * @type: audit type for the message | 110 | * @type: audit type for the message |
| 163 | * @profile: profile to check against (NOT NULL) | 111 | * @profile: profile to check against (NOT NULL) |
| 164 | * @gfp: allocation flags to use | ||
| 165 | * @sa: audit event (NOT NULL) | 112 | * @sa: audit event (NOT NULL) |
| 166 | * @cb: optional callback fn for type specific fields (MAYBE NULL) | 113 | * @cb: optional callback fn for type specific fields (MAYBE NULL) |
| 167 | * | 114 | * |
| @@ -169,14 +116,13 @@ void aa_audit_msg(int type, struct common_audit_data *sa, | |||
| 169 | * | 116 | * |
| 170 | * Returns: error on failure | 117 | * Returns: error on failure |
| 171 | */ | 118 | */ |
| 172 | int aa_audit(int type, struct aa_profile *profile, gfp_t gfp, | 119 | int aa_audit(int type, struct aa_profile *profile, struct common_audit_data *sa, |
| 173 | struct common_audit_data *sa, | ||
| 174 | void (*cb) (struct audit_buffer *, void *)) | 120 | void (*cb) (struct audit_buffer *, void *)) |
| 175 | { | 121 | { |
| 176 | BUG_ON(!profile); | 122 | AA_BUG(!profile); |
| 177 | 123 | ||
| 178 | if (type == AUDIT_APPARMOR_AUTO) { | 124 | if (type == AUDIT_APPARMOR_AUTO) { |
| 179 | if (likely(!sa->aad->error)) { | 125 | if (likely(!aad(sa)->error)) { |
| 180 | if (AUDIT_MODE(profile) != AUDIT_ALL) | 126 | if (AUDIT_MODE(profile) != AUDIT_ALL) |
| 181 | return 0; | 127 | return 0; |
| 182 | type = AUDIT_APPARMOR_AUDIT; | 128 | type = AUDIT_APPARMOR_AUDIT; |
| @@ -188,23 +134,23 @@ int aa_audit(int type, struct aa_profile *profile, gfp_t gfp, | |||
| 188 | if (AUDIT_MODE(profile) == AUDIT_QUIET || | 134 | if (AUDIT_MODE(profile) == AUDIT_QUIET || |
| 189 | (type == AUDIT_APPARMOR_DENIED && | 135 | (type == AUDIT_APPARMOR_DENIED && |
| 190 | AUDIT_MODE(profile) == AUDIT_QUIET)) | 136 | AUDIT_MODE(profile) == AUDIT_QUIET)) |
| 191 | return sa->aad->error; | 137 | return aad(sa)->error; |
| 192 | 138 | ||
| 193 | if (KILL_MODE(profile) && type == AUDIT_APPARMOR_DENIED) | 139 | if (KILL_MODE(profile) && type == AUDIT_APPARMOR_DENIED) |
| 194 | type = AUDIT_APPARMOR_KILL; | 140 | type = AUDIT_APPARMOR_KILL; |
| 195 | 141 | ||
| 196 | if (!unconfined(profile)) | 142 | if (!unconfined(profile)) |
| 197 | sa->aad->profile = profile; | 143 | aad(sa)->profile = profile; |
| 198 | 144 | ||
| 199 | aa_audit_msg(type, sa, cb); | 145 | aa_audit_msg(type, sa, cb); |
| 200 | 146 | ||
| 201 | if (sa->aad->type == AUDIT_APPARMOR_KILL) | 147 | if (aad(sa)->type == AUDIT_APPARMOR_KILL) |
| 202 | (void)send_sig_info(SIGKILL, NULL, | 148 | (void)send_sig_info(SIGKILL, NULL, |
| 203 | sa->type == LSM_AUDIT_DATA_TASK && sa->u.tsk ? | 149 | sa->type == LSM_AUDIT_DATA_TASK && sa->u.tsk ? |
| 204 | sa->u.tsk : current); | 150 | sa->u.tsk : current); |
| 205 | 151 | ||
| 206 | if (sa->aad->type == AUDIT_APPARMOR_ALLOWED) | 152 | if (aad(sa)->type == AUDIT_APPARMOR_ALLOWED) |
| 207 | return complain_error(sa->aad->error); | 153 | return complain_error(aad(sa)->error); |
| 208 | 154 | ||
| 209 | return sa->aad->error; | 155 | return aad(sa)->error; |
| 210 | } | 156 | } |
diff --git a/security/apparmor/capability.c b/security/apparmor/capability.c index 1101c6f64bb7..ed0a3e6b8022 100644 --- a/security/apparmor/capability.c +++ b/security/apparmor/capability.c | |||
| @@ -15,6 +15,7 @@ | |||
| 15 | #include <linux/capability.h> | 15 | #include <linux/capability.h> |
| 16 | #include <linux/errno.h> | 16 | #include <linux/errno.h> |
| 17 | #include <linux/gfp.h> | 17 | #include <linux/gfp.h> |
| 18 | #include <linux/security.h> | ||
| 18 | 19 | ||
| 19 | #include "include/apparmor.h" | 20 | #include "include/apparmor.h" |
| 20 | #include "include/capability.h" | 21 | #include "include/capability.h" |
| @@ -55,6 +56,7 @@ static void audit_cb(struct audit_buffer *ab, void *va) | |||
| 55 | * audit_caps - audit a capability | 56 | * audit_caps - audit a capability |
| 56 | * @profile: profile being tested for confinement (NOT NULL) | 57 | * @profile: profile being tested for confinement (NOT NULL) |
| 57 | * @cap: capability tested | 58 | * @cap: capability tested |
| 59 | @audit: whether an audit record should be generated | ||
| 58 | * @error: error code returned by test | 60 | * @error: error code returned by test |
| 59 | * | 61 | * |
| 60 | * Do auditing of capability and handle, audit/complain/kill modes switching | 62 | * Do auditing of capability and handle, audit/complain/kill modes switching |
| @@ -62,17 +64,16 @@ static void audit_cb(struct audit_buffer *ab, void *va) | |||
| 62 | * | 64 | * |
| 63 | * Returns: 0 or sa->error on success, error code on failure | 65 | * Returns: 0 or sa->error on success, error code on failure |
| 64 | */ | 66 | */ |
| 65 | static int audit_caps(struct aa_profile *profile, int cap, int error) | 67 | static int audit_caps(struct aa_profile *profile, int cap, int audit, |
| 68 | int error) | ||
| 66 | { | 69 | { |
| 67 | struct audit_cache *ent; | 70 | struct audit_cache *ent; |
| 68 | int type = AUDIT_APPARMOR_AUTO; | 71 | int type = AUDIT_APPARMOR_AUTO; |
| 69 | struct common_audit_data sa; | 72 | DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_CAP, OP_CAPABLE); |
| 70 | struct apparmor_audit_data aad = {0,}; | ||
| 71 | sa.type = LSM_AUDIT_DATA_CAP; | ||
| 72 | sa.aad = &aad; | ||
| 73 | sa.u.cap = cap; | 73 | sa.u.cap = cap; |
| 74 | sa.aad->op = OP_CAPABLE; | 74 | aad(&sa)->error = error; |
| 75 | sa.aad->error = error; | 75 | if (audit == SECURITY_CAP_NOAUDIT) |
| 76 | aad(&sa)->info = "optional: no audit"; | ||
| 76 | 77 | ||
| 77 | if (likely(!error)) { | 78 | if (likely(!error)) { |
| 78 | /* test if auditing is being forced */ | 79 | /* test if auditing is being forced */ |
| @@ -104,7 +105,7 @@ static int audit_caps(struct aa_profile *profile, int cap, int error) | |||
| 104 | } | 105 | } |
| 105 | put_cpu_var(audit_cache); | 106 | put_cpu_var(audit_cache); |
| 106 | 107 | ||
| 107 | return aa_audit(type, profile, GFP_ATOMIC, &sa, audit_cb); | 108 | return aa_audit(type, profile, &sa, audit_cb); |
| 108 | } | 109 | } |
| 109 | 110 | ||
| 110 | /** | 111 | /** |
| @@ -133,11 +134,10 @@ int aa_capable(struct aa_profile *profile, int cap, int audit) | |||
| 133 | { | 134 | { |
| 134 | int error = profile_capable(profile, cap); | 135 | int error = profile_capable(profile, cap); |
| 135 | 136 | ||
| 136 | if (!audit) { | 137 | if (audit == SECURITY_CAP_NOAUDIT) { |
| 137 | if (COMPLAIN_MODE(profile)) | 138 | if (!COMPLAIN_MODE(profile)) |
| 138 | return complain_error(error); | 139 | return error; |
| 139 | return error; | ||
| 140 | } | 140 | } |
| 141 | 141 | ||
| 142 | return audit_caps(profile, cap, error); | 142 | return audit_caps(profile, cap, audit, error); |
| 143 | } | 143 | } |
diff --git a/security/apparmor/context.c b/security/apparmor/context.c index 3064c6ced87c..1fc16b88efbf 100644 --- a/security/apparmor/context.c +++ b/security/apparmor/context.c | |||
| @@ -13,11 +13,11 @@ | |||
| 13 | * License. | 13 | * License. |
| 14 | * | 14 | * |
| 15 | * | 15 | * |
| 16 | * AppArmor sets confinement on every task, via the the aa_task_cxt and | 16 | * AppArmor sets confinement on every task, via the the aa_task_ctx and |
| 17 | * the aa_task_cxt.profile, both of which are required and are not allowed | 17 | * the aa_task_ctx.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 | 18 | * to be NULL. The aa_task_ctx is not reference counted and is unique |
| 19 | * to each cred (which is reference count). The profile pointed to by | 19 | * to each cred (which is reference count). The profile pointed to by |
| 20 | * the task_cxt is reference counted. | 20 | * the task_ctx is reference counted. |
| 21 | * | 21 | * |
| 22 | * TODO | 22 | * TODO |
| 23 | * If a task uses change_hat it currently does not return to the old | 23 | * If a task uses change_hat it currently does not return to the old |
| @@ -30,28 +30,28 @@ | |||
| 30 | #include "include/policy.h" | 30 | #include "include/policy.h" |
| 31 | 31 | ||
| 32 | /** | 32 | /** |
| 33 | * aa_alloc_task_context - allocate a new task_cxt | 33 | * aa_alloc_task_context - allocate a new task_ctx |
| 34 | * @flags: gfp flags for allocation | 34 | * @flags: gfp flags for allocation |
| 35 | * | 35 | * |
| 36 | * Returns: allocated buffer or NULL on failure | 36 | * Returns: allocated buffer or NULL on failure |
| 37 | */ | 37 | */ |
| 38 | struct aa_task_cxt *aa_alloc_task_context(gfp_t flags) | 38 | struct aa_task_ctx *aa_alloc_task_context(gfp_t flags) |
| 39 | { | 39 | { |
| 40 | return kzalloc(sizeof(struct aa_task_cxt), flags); | 40 | return kzalloc(sizeof(struct aa_task_ctx), flags); |
| 41 | } | 41 | } |
| 42 | 42 | ||
| 43 | /** | 43 | /** |
| 44 | * aa_free_task_context - free a task_cxt | 44 | * aa_free_task_context - free a task_ctx |
| 45 | * @cxt: task_cxt to free (MAYBE NULL) | 45 | * @ctx: task_ctx to free (MAYBE NULL) |
| 46 | */ | 46 | */ |
| 47 | void aa_free_task_context(struct aa_task_cxt *cxt) | 47 | void aa_free_task_context(struct aa_task_ctx *ctx) |
| 48 | { | 48 | { |
| 49 | if (cxt) { | 49 | if (ctx) { |
| 50 | aa_put_profile(cxt->profile); | 50 | aa_put_profile(ctx->profile); |
| 51 | aa_put_profile(cxt->previous); | 51 | aa_put_profile(ctx->previous); |
| 52 | aa_put_profile(cxt->onexec); | 52 | aa_put_profile(ctx->onexec); |
| 53 | 53 | ||
| 54 | kzfree(cxt); | 54 | kzfree(ctx); |
| 55 | } | 55 | } |
| 56 | } | 56 | } |
| 57 | 57 | ||
| @@ -60,7 +60,7 @@ void aa_free_task_context(struct aa_task_cxt *cxt) | |||
| 60 | * @new: a blank task context (NOT NULL) | 60 | * @new: a blank task context (NOT NULL) |
| 61 | * @old: the task context to copy (NOT NULL) | 61 | * @old: the task context to copy (NOT NULL) |
| 62 | */ | 62 | */ |
| 63 | void aa_dup_task_context(struct aa_task_cxt *new, const struct aa_task_cxt *old) | 63 | void aa_dup_task_context(struct aa_task_ctx *new, const struct aa_task_ctx *old) |
| 64 | { | 64 | { |
| 65 | *new = *old; | 65 | *new = *old; |
| 66 | aa_get_profile(new->profile); | 66 | aa_get_profile(new->profile); |
| @@ -93,31 +93,36 @@ struct aa_profile *aa_get_task_profile(struct task_struct *task) | |||
| 93 | */ | 93 | */ |
| 94 | int aa_replace_current_profile(struct aa_profile *profile) | 94 | int aa_replace_current_profile(struct aa_profile *profile) |
| 95 | { | 95 | { |
| 96 | struct aa_task_cxt *cxt = current_cxt(); | 96 | struct aa_task_ctx *ctx = current_ctx(); |
| 97 | struct cred *new; | 97 | struct cred *new; |
| 98 | BUG_ON(!profile); | 98 | AA_BUG(!profile); |
| 99 | 99 | ||
| 100 | if (cxt->profile == profile) | 100 | if (ctx->profile == profile) |
| 101 | return 0; | 101 | return 0; |
| 102 | 102 | ||
| 103 | if (current_cred() != current_real_cred()) | ||
| 104 | return -EBUSY; | ||
| 105 | |||
| 103 | new = prepare_creds(); | 106 | new = prepare_creds(); |
| 104 | if (!new) | 107 | if (!new) |
| 105 | return -ENOMEM; | 108 | return -ENOMEM; |
| 106 | 109 | ||
| 107 | cxt = cred_cxt(new); | 110 | ctx = cred_ctx(new); |
| 108 | if (unconfined(profile) || (cxt->profile->ns != profile->ns)) | 111 | if (unconfined(profile) || (ctx->profile->ns != profile->ns)) |
| 109 | /* if switching to unconfined or a different profile namespace | 112 | /* if switching to unconfined or a different profile namespace |
| 110 | * clear out context state | 113 | * clear out context state |
| 111 | */ | 114 | */ |
| 112 | aa_clear_task_cxt_trans(cxt); | 115 | aa_clear_task_ctx_trans(ctx); |
| 113 | 116 | ||
| 114 | /* be careful switching cxt->profile, when racing replacement it | 117 | /* |
| 115 | * is possible that cxt->profile->replacedby->profile is the reference | 118 | * be careful switching ctx->profile, when racing replacement it |
| 119 | * is possible that ctx->profile->proxy->profile is the reference | ||
| 116 | * keeping @profile valid, so make sure to get its reference before | 120 | * keeping @profile valid, so make sure to get its reference before |
| 117 | * dropping the reference on cxt->profile */ | 121 | * dropping the reference on ctx->profile |
| 122 | */ | ||
| 118 | aa_get_profile(profile); | 123 | aa_get_profile(profile); |
| 119 | aa_put_profile(cxt->profile); | 124 | aa_put_profile(ctx->profile); |
| 120 | cxt->profile = profile; | 125 | ctx->profile = profile; |
| 121 | 126 | ||
| 122 | commit_creds(new); | 127 | commit_creds(new); |
| 123 | return 0; | 128 | return 0; |
| @@ -131,15 +136,15 @@ int aa_replace_current_profile(struct aa_profile *profile) | |||
| 131 | */ | 136 | */ |
| 132 | int aa_set_current_onexec(struct aa_profile *profile) | 137 | int aa_set_current_onexec(struct aa_profile *profile) |
| 133 | { | 138 | { |
| 134 | struct aa_task_cxt *cxt; | 139 | struct aa_task_ctx *ctx; |
| 135 | struct cred *new = prepare_creds(); | 140 | struct cred *new = prepare_creds(); |
| 136 | if (!new) | 141 | if (!new) |
| 137 | return -ENOMEM; | 142 | return -ENOMEM; |
| 138 | 143 | ||
| 139 | cxt = cred_cxt(new); | 144 | ctx = cred_ctx(new); |
| 140 | aa_get_profile(profile); | 145 | aa_get_profile(profile); |
| 141 | aa_put_profile(cxt->onexec); | 146 | aa_put_profile(ctx->onexec); |
| 142 | cxt->onexec = profile; | 147 | ctx->onexec = profile; |
| 143 | 148 | ||
| 144 | commit_creds(new); | 149 | commit_creds(new); |
| 145 | return 0; | 150 | return 0; |
| @@ -157,28 +162,28 @@ int aa_set_current_onexec(struct aa_profile *profile) | |||
| 157 | */ | 162 | */ |
| 158 | int aa_set_current_hat(struct aa_profile *profile, u64 token) | 163 | int aa_set_current_hat(struct aa_profile *profile, u64 token) |
| 159 | { | 164 | { |
| 160 | struct aa_task_cxt *cxt; | 165 | struct aa_task_ctx *ctx; |
| 161 | struct cred *new = prepare_creds(); | 166 | struct cred *new = prepare_creds(); |
| 162 | if (!new) | 167 | if (!new) |
| 163 | return -ENOMEM; | 168 | return -ENOMEM; |
| 164 | BUG_ON(!profile); | 169 | AA_BUG(!profile); |
| 165 | 170 | ||
| 166 | cxt = cred_cxt(new); | 171 | ctx = cred_ctx(new); |
| 167 | if (!cxt->previous) { | 172 | if (!ctx->previous) { |
| 168 | /* transfer refcount */ | 173 | /* transfer refcount */ |
| 169 | cxt->previous = cxt->profile; | 174 | ctx->previous = ctx->profile; |
| 170 | cxt->token = token; | 175 | ctx->token = token; |
| 171 | } else if (cxt->token == token) { | 176 | } else if (ctx->token == token) { |
| 172 | aa_put_profile(cxt->profile); | 177 | aa_put_profile(ctx->profile); |
| 173 | } else { | 178 | } else { |
| 174 | /* previous_profile && cxt->token != token */ | 179 | /* previous_profile && ctx->token != token */ |
| 175 | abort_creds(new); | 180 | abort_creds(new); |
| 176 | return -EACCES; | 181 | return -EACCES; |
| 177 | } | 182 | } |
| 178 | cxt->profile = aa_get_newest_profile(profile); | 183 | ctx->profile = aa_get_newest_profile(profile); |
| 179 | /* clear exec on switching context */ | 184 | /* clear exec on switching context */ |
| 180 | aa_put_profile(cxt->onexec); | 185 | aa_put_profile(ctx->onexec); |
| 181 | cxt->onexec = NULL; | 186 | ctx->onexec = NULL; |
| 182 | 187 | ||
| 183 | commit_creds(new); | 188 | commit_creds(new); |
| 184 | return 0; | 189 | return 0; |
| @@ -195,27 +200,27 @@ int aa_set_current_hat(struct aa_profile *profile, u64 token) | |||
| 195 | */ | 200 | */ |
| 196 | int aa_restore_previous_profile(u64 token) | 201 | int aa_restore_previous_profile(u64 token) |
| 197 | { | 202 | { |
| 198 | struct aa_task_cxt *cxt; | 203 | struct aa_task_ctx *ctx; |
| 199 | struct cred *new = prepare_creds(); | 204 | struct cred *new = prepare_creds(); |
| 200 | if (!new) | 205 | if (!new) |
| 201 | return -ENOMEM; | 206 | return -ENOMEM; |
| 202 | 207 | ||
| 203 | cxt = cred_cxt(new); | 208 | ctx = cred_ctx(new); |
| 204 | if (cxt->token != token) { | 209 | if (ctx->token != token) { |
| 205 | abort_creds(new); | 210 | abort_creds(new); |
| 206 | return -EACCES; | 211 | return -EACCES; |
| 207 | } | 212 | } |
| 208 | /* ignore restores when there is no saved profile */ | 213 | /* ignore restores when there is no saved profile */ |
| 209 | if (!cxt->previous) { | 214 | if (!ctx->previous) { |
| 210 | abort_creds(new); | 215 | abort_creds(new); |
| 211 | return 0; | 216 | return 0; |
| 212 | } | 217 | } |
| 213 | 218 | ||
| 214 | aa_put_profile(cxt->profile); | 219 | aa_put_profile(ctx->profile); |
| 215 | cxt->profile = aa_get_newest_profile(cxt->previous); | 220 | ctx->profile = aa_get_newest_profile(ctx->previous); |
| 216 | BUG_ON(!cxt->profile); | 221 | AA_BUG(!ctx->profile); |
| 217 | /* clear exec && prev information when restoring to previous context */ | 222 | /* clear exec && prev information when restoring to previous context */ |
| 218 | aa_clear_task_cxt_trans(cxt); | 223 | aa_clear_task_ctx_trans(ctx); |
| 219 | 224 | ||
| 220 | commit_creds(new); | 225 | commit_creds(new); |
| 221 | return 0; | 226 | return 0; |
diff --git a/security/apparmor/crypto.c b/security/apparmor/crypto.c index b75dab0df1cb..de8dc78b6144 100644 --- a/security/apparmor/crypto.c +++ b/security/apparmor/crypto.c | |||
| @@ -29,6 +29,43 @@ unsigned int aa_hash_size(void) | |||
| 29 | return apparmor_hash_size; | 29 | return apparmor_hash_size; |
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | char *aa_calc_hash(void *data, size_t len) | ||
| 33 | { | ||
| 34 | struct { | ||
| 35 | struct shash_desc shash; | ||
| 36 | char ctx[crypto_shash_descsize(apparmor_tfm)]; | ||
| 37 | } desc; | ||
| 38 | char *hash = NULL; | ||
| 39 | int error = -ENOMEM; | ||
| 40 | |||
| 41 | if (!apparmor_tfm) | ||
| 42 | return NULL; | ||
| 43 | |||
| 44 | hash = kzalloc(apparmor_hash_size, GFP_KERNEL); | ||
| 45 | if (!hash) | ||
| 46 | goto fail; | ||
| 47 | |||
| 48 | desc.shash.tfm = apparmor_tfm; | ||
| 49 | desc.shash.flags = 0; | ||
| 50 | |||
| 51 | error = crypto_shash_init(&desc.shash); | ||
| 52 | if (error) | ||
| 53 | goto fail; | ||
| 54 | error = crypto_shash_update(&desc.shash, (u8 *) data, len); | ||
| 55 | if (error) | ||
| 56 | goto fail; | ||
| 57 | error = crypto_shash_final(&desc.shash, hash); | ||
| 58 | if (error) | ||
| 59 | goto fail; | ||
| 60 | |||
| 61 | return hash; | ||
| 62 | |||
| 63 | fail: | ||
| 64 | kfree(hash); | ||
| 65 | |||
| 66 | return ERR_PTR(error); | ||
| 67 | } | ||
| 68 | |||
| 32 | int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start, | 69 | int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start, |
| 33 | size_t len) | 70 | size_t len) |
| 34 | { | 71 | { |
| @@ -37,7 +74,7 @@ int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start, | |||
| 37 | char ctx[crypto_shash_descsize(apparmor_tfm)]; | 74 | char ctx[crypto_shash_descsize(apparmor_tfm)]; |
| 38 | } desc; | 75 | } desc; |
| 39 | int error = -ENOMEM; | 76 | int error = -ENOMEM; |
| 40 | u32 le32_version = cpu_to_le32(version); | 77 | __le32 le32_version = cpu_to_le32(version); |
| 41 | 78 | ||
| 42 | if (!aa_g_hash_policy) | 79 | if (!aa_g_hash_policy) |
| 43 | return 0; | 80 | return 0; |
diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index a4d90aa1045a..ef4beef06e9d 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c | |||
| @@ -29,6 +29,7 @@ | |||
| 29 | #include "include/match.h" | 29 | #include "include/match.h" |
| 30 | #include "include/path.h" | 30 | #include "include/path.h" |
| 31 | #include "include/policy.h" | 31 | #include "include/policy.h" |
| 32 | #include "include/policy_ns.h" | ||
| 32 | 33 | ||
| 33 | /** | 34 | /** |
| 34 | * aa_free_domain_entries - free entries in a domain table | 35 | * aa_free_domain_entries - free entries in a domain table |
| @@ -93,7 +94,7 @@ out: | |||
| 93 | * Returns: permission set | 94 | * Returns: permission set |
| 94 | */ | 95 | */ |
| 95 | static struct file_perms change_profile_perms(struct aa_profile *profile, | 96 | static struct file_perms change_profile_perms(struct aa_profile *profile, |
| 96 | struct aa_namespace *ns, | 97 | struct aa_ns *ns, |
| 97 | const char *name, u32 request, | 98 | const char *name, u32 request, |
| 98 | unsigned int start) | 99 | unsigned int start) |
| 99 | { | 100 | { |
| @@ -170,7 +171,7 @@ static struct aa_profile *__attach_match(const char *name, | |||
| 170 | * | 171 | * |
| 171 | * Returns: profile or NULL if no match found | 172 | * Returns: profile or NULL if no match found |
| 172 | */ | 173 | */ |
| 173 | static struct aa_profile *find_attach(struct aa_namespace *ns, | 174 | static struct aa_profile *find_attach(struct aa_ns *ns, |
| 174 | struct list_head *list, const char *name) | 175 | struct list_head *list, const char *name) |
| 175 | { | 176 | { |
| 176 | struct aa_profile *profile; | 177 | struct aa_profile *profile; |
| @@ -239,7 +240,7 @@ static const char *next_name(int xtype, const char *name) | |||
| 239 | static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) | 240 | static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) |
| 240 | { | 241 | { |
| 241 | struct aa_profile *new_profile = NULL; | 242 | struct aa_profile *new_profile = NULL; |
| 242 | struct aa_namespace *ns = profile->ns; | 243 | struct aa_ns *ns = profile->ns; |
| 243 | u32 xtype = xindex & AA_X_TYPE_MASK; | 244 | u32 xtype = xindex & AA_X_TYPE_MASK; |
| 244 | int index = xindex & AA_X_INDEX_MASK; | 245 | int index = xindex & AA_X_INDEX_MASK; |
| 245 | const char *name; | 246 | const char *name; |
| @@ -247,7 +248,7 @@ static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) | |||
| 247 | /* index is guaranteed to be in range, validated at load time */ | 248 | /* index is guaranteed to be in range, validated at load time */ |
| 248 | for (name = profile->file.trans.table[index]; !new_profile && name; | 249 | for (name = profile->file.trans.table[index]; !new_profile && name; |
| 249 | name = next_name(xtype, name)) { | 250 | name = next_name(xtype, name)) { |
| 250 | struct aa_namespace *new_ns; | 251 | struct aa_ns *new_ns; |
| 251 | const char *xname = NULL; | 252 | const char *xname = NULL; |
| 252 | 253 | ||
| 253 | new_ns = NULL; | 254 | new_ns = NULL; |
| @@ -267,7 +268,7 @@ static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) | |||
| 267 | ; | 268 | ; |
| 268 | } | 269 | } |
| 269 | /* released below */ | 270 | /* released below */ |
| 270 | new_ns = aa_find_namespace(ns, ns_name); | 271 | new_ns = aa_find_ns(ns, ns_name); |
| 271 | if (!new_ns) | 272 | if (!new_ns) |
| 272 | continue; | 273 | continue; |
| 273 | } else if (*name == '@') { | 274 | } else if (*name == '@') { |
| @@ -280,7 +281,7 @@ static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) | |||
| 280 | 281 | ||
| 281 | /* released by caller */ | 282 | /* released by caller */ |
| 282 | new_profile = aa_lookup_profile(new_ns ? new_ns : ns, xname); | 283 | new_profile = aa_lookup_profile(new_ns ? new_ns : ns, xname); |
| 283 | aa_put_namespace(new_ns); | 284 | aa_put_ns(new_ns); |
| 284 | } | 285 | } |
| 285 | 286 | ||
| 286 | /* released by caller */ | 287 | /* released by caller */ |
| @@ -301,7 +302,7 @@ static struct aa_profile *x_to_profile(struct aa_profile *profile, | |||
| 301 | const char *name, u32 xindex) | 302 | const char *name, u32 xindex) |
| 302 | { | 303 | { |
| 303 | struct aa_profile *new_profile = NULL; | 304 | struct aa_profile *new_profile = NULL; |
| 304 | struct aa_namespace *ns = profile->ns; | 305 | struct aa_ns *ns = profile->ns; |
| 305 | u32 xtype = xindex & AA_X_TYPE_MASK; | 306 | u32 xtype = xindex & AA_X_TYPE_MASK; |
| 306 | 307 | ||
| 307 | switch (xtype) { | 308 | switch (xtype) { |
| @@ -336,9 +337,9 @@ static struct aa_profile *x_to_profile(struct aa_profile *profile, | |||
| 336 | */ | 337 | */ |
| 337 | int apparmor_bprm_set_creds(struct linux_binprm *bprm) | 338 | int apparmor_bprm_set_creds(struct linux_binprm *bprm) |
| 338 | { | 339 | { |
| 339 | struct aa_task_cxt *cxt; | 340 | struct aa_task_ctx *ctx; |
| 340 | struct aa_profile *profile, *new_profile = NULL; | 341 | struct aa_profile *profile, *new_profile = NULL; |
| 341 | struct aa_namespace *ns; | 342 | struct aa_ns *ns; |
| 342 | char *buffer = NULL; | 343 | char *buffer = NULL; |
| 343 | unsigned int state; | 344 | unsigned int state; |
| 344 | struct file_perms perms = {}; | 345 | struct file_perms perms = {}; |
| @@ -352,10 +353,10 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) | |||
| 352 | if (bprm->cred_prepared) | 353 | if (bprm->cred_prepared) |
| 353 | return 0; | 354 | return 0; |
| 354 | 355 | ||
| 355 | cxt = cred_cxt(bprm->cred); | 356 | ctx = cred_ctx(bprm->cred); |
| 356 | BUG_ON(!cxt); | 357 | AA_BUG(!ctx); |
| 357 | 358 | ||
| 358 | profile = aa_get_newest_profile(cxt->profile); | 359 | profile = aa_get_newest_profile(ctx->profile); |
| 359 | /* | 360 | /* |
| 360 | * get the namespace from the replacement profile as replacement | 361 | * get the namespace from the replacement profile as replacement |
| 361 | * can change the namespace | 362 | * can change the namespace |
| @@ -379,9 +380,9 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) | |||
| 379 | */ | 380 | */ |
| 380 | if (unconfined(profile)) { | 381 | if (unconfined(profile)) { |
| 381 | /* unconfined task */ | 382 | /* unconfined task */ |
| 382 | if (cxt->onexec) | 383 | if (ctx->onexec) |
| 383 | /* change_profile on exec already been granted */ | 384 | /* change_profile on exec already been granted */ |
| 384 | new_profile = aa_get_profile(cxt->onexec); | 385 | new_profile = aa_get_profile(ctx->onexec); |
| 385 | else | 386 | else |
| 386 | new_profile = find_attach(ns, &ns->base.profiles, name); | 387 | new_profile = find_attach(ns, &ns->base.profiles, name); |
| 387 | if (!new_profile) | 388 | if (!new_profile) |
| @@ -396,10 +397,10 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) | |||
| 396 | 397 | ||
| 397 | /* find exec permissions for name */ | 398 | /* find exec permissions for name */ |
| 398 | state = aa_str_perms(profile->file.dfa, state, name, &cond, &perms); | 399 | state = aa_str_perms(profile->file.dfa, state, name, &cond, &perms); |
| 399 | if (cxt->onexec) { | 400 | if (ctx->onexec) { |
| 400 | struct file_perms cp; | 401 | struct file_perms cp; |
| 401 | info = "change_profile onexec"; | 402 | info = "change_profile onexec"; |
| 402 | new_profile = aa_get_newest_profile(cxt->onexec); | 403 | new_profile = aa_get_newest_profile(ctx->onexec); |
| 403 | if (!(perms.allow & AA_MAY_ONEXEC)) | 404 | if (!(perms.allow & AA_MAY_ONEXEC)) |
| 404 | goto audit; | 405 | goto audit; |
| 405 | 406 | ||
| @@ -408,8 +409,8 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) | |||
| 408 | * exec\0change_profile | 409 | * exec\0change_profile |
| 409 | */ | 410 | */ |
| 410 | state = aa_dfa_null_transition(profile->file.dfa, state); | 411 | state = aa_dfa_null_transition(profile->file.dfa, state); |
| 411 | cp = change_profile_perms(profile, cxt->onexec->ns, | 412 | cp = change_profile_perms(profile, ctx->onexec->ns, |
| 412 | cxt->onexec->base.name, | 413 | ctx->onexec->base.name, |
| 413 | AA_MAY_ONEXEC, state); | 414 | AA_MAY_ONEXEC, state); |
| 414 | 415 | ||
| 415 | if (!(cp.allow & AA_MAY_ONEXEC)) | 416 | if (!(cp.allow & AA_MAY_ONEXEC)) |
| @@ -441,7 +442,8 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) | |||
| 441 | } | 442 | } |
| 442 | } else if (COMPLAIN_MODE(profile)) { | 443 | } else if (COMPLAIN_MODE(profile)) { |
| 443 | /* no exec permission - are we in learning mode */ | 444 | /* no exec permission - are we in learning mode */ |
| 444 | new_profile = aa_new_null_profile(profile, 0); | 445 | new_profile = aa_new_null_profile(profile, false, name, |
| 446 | GFP_ATOMIC); | ||
| 445 | if (!new_profile) { | 447 | if (!new_profile) { |
| 446 | error = -ENOMEM; | 448 | error = -ENOMEM; |
| 447 | info = "could not create null profile"; | 449 | info = "could not create null profile"; |
| @@ -497,17 +499,16 @@ apply: | |||
| 497 | bprm->per_clear |= PER_CLEAR_ON_SETID; | 499 | bprm->per_clear |= PER_CLEAR_ON_SETID; |
| 498 | 500 | ||
| 499 | x_clear: | 501 | x_clear: |
| 500 | aa_put_profile(cxt->profile); | 502 | aa_put_profile(ctx->profile); |
| 501 | /* transfer new profile reference will be released when cxt is freed */ | 503 | /* transfer new profile reference will be released when ctx is freed */ |
| 502 | cxt->profile = new_profile; | 504 | ctx->profile = new_profile; |
| 503 | new_profile = NULL; | 505 | new_profile = NULL; |
| 504 | 506 | ||
| 505 | /* clear out all temporary/transitional state from the context */ | 507 | /* clear out all temporary/transitional state from the context */ |
| 506 | aa_clear_task_cxt_trans(cxt); | 508 | aa_clear_task_ctx_trans(ctx); |
| 507 | 509 | ||
| 508 | audit: | 510 | audit: |
| 509 | error = aa_audit_file(profile, &perms, GFP_KERNEL, OP_EXEC, MAY_EXEC, | 511 | error = aa_audit_file(profile, &perms, OP_EXEC, MAY_EXEC, name, |
| 510 | name, | ||
| 511 | new_profile ? new_profile->base.hname : NULL, | 512 | new_profile ? new_profile->base.hname : NULL, |
| 512 | cond.uid, info, error); | 513 | cond.uid, info, error); |
| 513 | 514 | ||
| @@ -543,17 +544,17 @@ int apparmor_bprm_secureexec(struct linux_binprm *bprm) | |||
| 543 | void apparmor_bprm_committing_creds(struct linux_binprm *bprm) | 544 | void apparmor_bprm_committing_creds(struct linux_binprm *bprm) |
| 544 | { | 545 | { |
| 545 | struct aa_profile *profile = __aa_current_profile(); | 546 | struct aa_profile *profile = __aa_current_profile(); |
| 546 | struct aa_task_cxt *new_cxt = cred_cxt(bprm->cred); | 547 | struct aa_task_ctx *new_ctx = cred_ctx(bprm->cred); |
| 547 | 548 | ||
| 548 | /* bail out if unconfined or not changing profile */ | 549 | /* bail out if unconfined or not changing profile */ |
| 549 | if ((new_cxt->profile == profile) || | 550 | if ((new_ctx->profile == profile) || |
| 550 | (unconfined(new_cxt->profile))) | 551 | (unconfined(new_ctx->profile))) |
| 551 | return; | 552 | return; |
| 552 | 553 | ||
| 553 | current->pdeath_signal = 0; | 554 | current->pdeath_signal = 0; |
| 554 | 555 | ||
| 555 | /* reset soft limits and set hard limits for the new profile */ | 556 | /* reset soft limits and set hard limits for the new profile */ |
| 556 | __aa_transition_rlimits(profile, new_cxt->profile); | 557 | __aa_transition_rlimits(profile, new_ctx->profile); |
| 557 | } | 558 | } |
| 558 | 559 | ||
| 559 | /** | 560 | /** |
| @@ -602,7 +603,7 @@ static char *new_compound_name(const char *n1, const char *n2) | |||
| 602 | int aa_change_hat(const char *hats[], int count, u64 token, bool permtest) | 603 | int aa_change_hat(const char *hats[], int count, u64 token, bool permtest) |
| 603 | { | 604 | { |
| 604 | const struct cred *cred; | 605 | const struct cred *cred; |
| 605 | struct aa_task_cxt *cxt; | 606 | struct aa_task_ctx *ctx; |
| 606 | struct aa_profile *profile, *previous_profile, *hat = NULL; | 607 | struct aa_profile *profile, *previous_profile, *hat = NULL; |
| 607 | char *name = NULL; | 608 | char *name = NULL; |
| 608 | int i; | 609 | int i; |
| @@ -620,9 +621,9 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest) | |||
| 620 | 621 | ||
| 621 | /* released below */ | 622 | /* released below */ |
| 622 | cred = get_current_cred(); | 623 | cred = get_current_cred(); |
| 623 | cxt = cred_cxt(cred); | 624 | ctx = cred_ctx(cred); |
| 624 | profile = aa_get_newest_profile(aa_cred_profile(cred)); | 625 | profile = aa_get_newest_profile(aa_cred_profile(cred)); |
| 625 | previous_profile = aa_get_newest_profile(cxt->previous); | 626 | previous_profile = aa_get_newest_profile(ctx->previous); |
| 626 | 627 | ||
| 627 | if (unconfined(profile)) { | 628 | if (unconfined(profile)) { |
| 628 | info = "unconfined"; | 629 | info = "unconfined"; |
| @@ -666,7 +667,8 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest) | |||
| 666 | aa_put_profile(root); | 667 | aa_put_profile(root); |
| 667 | target = name; | 668 | target = name; |
| 668 | /* released below */ | 669 | /* released below */ |
| 669 | hat = aa_new_null_profile(profile, 1); | 670 | hat = aa_new_null_profile(profile, true, hats[0], |
| 671 | GFP_KERNEL); | ||
| 670 | if (!hat) { | 672 | if (!hat) { |
| 671 | info = "failed null profile create"; | 673 | info = "failed null profile create"; |
| 672 | error = -ENOMEM; | 674 | error = -ENOMEM; |
| @@ -711,9 +713,9 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest) | |||
| 711 | 713 | ||
| 712 | audit: | 714 | audit: |
| 713 | if (!permtest) | 715 | if (!permtest) |
| 714 | error = aa_audit_file(profile, &perms, GFP_KERNEL, | 716 | error = aa_audit_file(profile, &perms, OP_CHANGE_HAT, |
| 715 | OP_CHANGE_HAT, AA_MAY_CHANGEHAT, NULL, | 717 | AA_MAY_CHANGEHAT, NULL, target, |
| 716 | target, GLOBAL_ROOT_UID, info, error); | 718 | GLOBAL_ROOT_UID, info, error); |
| 717 | 719 | ||
| 718 | out: | 720 | out: |
| 719 | aa_put_profile(hat); | 721 | aa_put_profile(hat); |
| @@ -727,8 +729,7 @@ out: | |||
| 727 | 729 | ||
| 728 | /** | 730 | /** |
| 729 | * aa_change_profile - perform a one-way profile transition | 731 | * aa_change_profile - perform a one-way profile transition |
| 730 | * @ns_name: name of the profile namespace to change to (MAYBE NULL) | 732 | * @fqname: name of profile may include namespace (NOT NULL) |
| 731 | * @hname: name of profile to change to (MAYBE NULL) | ||
| 732 | * @onexec: whether this transition is to take place immediately or at exec | 733 | * @onexec: whether this transition is to take place immediately or at exec |
| 733 | * @permtest: true if this is just a permission test | 734 | * @permtest: true if this is just a permission test |
| 734 | * | 735 | * |
| @@ -740,19 +741,20 @@ out: | |||
| 740 | * | 741 | * |
| 741 | * Returns %0 on success, error otherwise. | 742 | * Returns %0 on success, error otherwise. |
| 742 | */ | 743 | */ |
| 743 | int aa_change_profile(const char *ns_name, const char *hname, bool onexec, | 744 | int aa_change_profile(const char *fqname, bool onexec, |
| 744 | bool permtest) | 745 | bool permtest, bool stack) |
| 745 | { | 746 | { |
| 746 | const struct cred *cred; | 747 | const struct cred *cred; |
| 747 | struct aa_profile *profile, *target = NULL; | 748 | struct aa_profile *profile, *target = NULL; |
| 748 | struct aa_namespace *ns = NULL; | ||
| 749 | struct file_perms perms = {}; | 749 | struct file_perms perms = {}; |
| 750 | const char *name = NULL, *info = NULL; | 750 | const char *info = NULL, *op; |
| 751 | int op, error = 0; | 751 | int error = 0; |
| 752 | u32 request; | 752 | u32 request; |
| 753 | 753 | ||
| 754 | if (!hname && !ns_name) | 754 | if (!fqname || !*fqname) { |
| 755 | AA_DEBUG("no profile name"); | ||
| 755 | return -EINVAL; | 756 | return -EINVAL; |
| 757 | } | ||
| 756 | 758 | ||
| 757 | if (onexec) { | 759 | if (onexec) { |
| 758 | request = AA_MAY_ONEXEC; | 760 | request = AA_MAY_ONEXEC; |
| @@ -777,44 +779,15 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec, | |||
| 777 | return -EPERM; | 779 | return -EPERM; |
| 778 | } | 780 | } |
| 779 | 781 | ||
| 780 | if (ns_name) { | 782 | target = aa_fqlookupn_profile(profile, fqname, strlen(fqname)); |
| 781 | /* released below */ | ||
| 782 | ns = aa_find_namespace(profile->ns, ns_name); | ||
| 783 | if (!ns) { | ||
| 784 | /* we don't create new namespace in complain mode */ | ||
| 785 | name = ns_name; | ||
| 786 | info = "namespace not found"; | ||
| 787 | error = -ENOENT; | ||
| 788 | goto audit; | ||
| 789 | } | ||
| 790 | } else | ||
| 791 | /* released below */ | ||
| 792 | ns = aa_get_namespace(profile->ns); | ||
| 793 | |||
| 794 | /* if the name was not specified, use the name of the current profile */ | ||
| 795 | if (!hname) { | ||
| 796 | if (unconfined(profile)) | ||
| 797 | hname = ns->unconfined->base.hname; | ||
| 798 | else | ||
| 799 | hname = profile->base.hname; | ||
| 800 | } | ||
| 801 | |||
| 802 | perms = change_profile_perms(profile, ns, hname, request, | ||
| 803 | profile->file.start); | ||
| 804 | if (!(perms.allow & request)) { | ||
| 805 | error = -EACCES; | ||
| 806 | goto audit; | ||
| 807 | } | ||
| 808 | |||
| 809 | /* released below */ | ||
| 810 | target = aa_lookup_profile(ns, hname); | ||
| 811 | if (!target) { | 783 | if (!target) { |
| 812 | info = "profile not found"; | 784 | info = "profile not found"; |
| 813 | error = -ENOENT; | 785 | error = -ENOENT; |
| 814 | if (permtest || !COMPLAIN_MODE(profile)) | 786 | if (permtest || !COMPLAIN_MODE(profile)) |
| 815 | goto audit; | 787 | goto audit; |
| 816 | /* released below */ | 788 | /* released below */ |
| 817 | target = aa_new_null_profile(profile, 0); | 789 | target = aa_new_null_profile(profile, false, fqname, |
| 790 | GFP_KERNEL); | ||
| 818 | if (!target) { | 791 | if (!target) { |
| 819 | info = "failed null profile create"; | 792 | info = "failed null profile create"; |
| 820 | error = -ENOMEM; | 793 | error = -ENOMEM; |
| @@ -822,6 +795,13 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec, | |||
| 822 | } | 795 | } |
| 823 | } | 796 | } |
| 824 | 797 | ||
| 798 | perms = change_profile_perms(profile, target->ns, target->base.hname, | ||
| 799 | request, profile->file.start); | ||
| 800 | if (!(perms.allow & request)) { | ||
| 801 | error = -EACCES; | ||
| 802 | goto audit; | ||
| 803 | } | ||
| 804 | |||
| 825 | /* check if tracing task is allowed to trace target domain */ | 805 | /* check if tracing task is allowed to trace target domain */ |
| 826 | error = may_change_ptraced_domain(target); | 806 | error = may_change_ptraced_domain(target); |
| 827 | if (error) { | 807 | if (error) { |
| @@ -839,10 +819,9 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec, | |||
| 839 | 819 | ||
| 840 | audit: | 820 | audit: |
| 841 | if (!permtest) | 821 | if (!permtest) |
| 842 | error = aa_audit_file(profile, &perms, GFP_KERNEL, op, request, | 822 | error = aa_audit_file(profile, &perms, op, request, NULL, |
| 843 | name, hname, GLOBAL_ROOT_UID, info, error); | 823 | fqname, GLOBAL_ROOT_UID, info, error); |
| 844 | 824 | ||
| 845 | aa_put_namespace(ns); | ||
| 846 | aa_put_profile(target); | 825 | aa_put_profile(target); |
| 847 | put_cred(cred); | 826 | put_cred(cred); |
| 848 | 827 | ||
diff --git a/security/apparmor/file.c b/security/apparmor/file.c index 4d2af4b01033..750564c3ab71 100644 --- a/security/apparmor/file.c +++ b/security/apparmor/file.c | |||
| @@ -67,24 +67,24 @@ static void file_audit_cb(struct audit_buffer *ab, void *va) | |||
| 67 | struct common_audit_data *sa = va; | 67 | struct common_audit_data *sa = va; |
| 68 | kuid_t fsuid = current_fsuid(); | 68 | kuid_t fsuid = current_fsuid(); |
| 69 | 69 | ||
| 70 | if (sa->aad->fs.request & AA_AUDIT_FILE_MASK) { | 70 | if (aad(sa)->fs.request & AA_AUDIT_FILE_MASK) { |
| 71 | audit_log_format(ab, " requested_mask="); | 71 | audit_log_format(ab, " requested_mask="); |
| 72 | audit_file_mask(ab, sa->aad->fs.request); | 72 | audit_file_mask(ab, aad(sa)->fs.request); |
| 73 | } | 73 | } |
| 74 | if (sa->aad->fs.denied & AA_AUDIT_FILE_MASK) { | 74 | if (aad(sa)->fs.denied & AA_AUDIT_FILE_MASK) { |
| 75 | audit_log_format(ab, " denied_mask="); | 75 | audit_log_format(ab, " denied_mask="); |
| 76 | audit_file_mask(ab, sa->aad->fs.denied); | 76 | audit_file_mask(ab, aad(sa)->fs.denied); |
| 77 | } | 77 | } |
| 78 | if (sa->aad->fs.request & AA_AUDIT_FILE_MASK) { | 78 | if (aad(sa)->fs.request & AA_AUDIT_FILE_MASK) { |
| 79 | audit_log_format(ab, " fsuid=%d", | 79 | audit_log_format(ab, " fsuid=%d", |
| 80 | from_kuid(&init_user_ns, fsuid)); | 80 | from_kuid(&init_user_ns, fsuid)); |
| 81 | audit_log_format(ab, " ouid=%d", | 81 | audit_log_format(ab, " ouid=%d", |
| 82 | from_kuid(&init_user_ns, sa->aad->fs.ouid)); | 82 | from_kuid(&init_user_ns, aad(sa)->fs.ouid)); |
| 83 | } | 83 | } |
| 84 | 84 | ||
| 85 | if (sa->aad->fs.target) { | 85 | if (aad(sa)->fs.target) { |
| 86 | audit_log_format(ab, " target="); | 86 | audit_log_format(ab, " target="); |
| 87 | audit_log_untrustedstring(ab, sa->aad->fs.target); | 87 | audit_log_untrustedstring(ab, aad(sa)->fs.target); |
| 88 | } | 88 | } |
| 89 | } | 89 | } |
| 90 | 90 | ||
| @@ -104,54 +104,53 @@ static void file_audit_cb(struct audit_buffer *ab, void *va) | |||
| 104 | * Returns: %0 or error on failure | 104 | * Returns: %0 or error on failure |
| 105 | */ | 105 | */ |
| 106 | int aa_audit_file(struct aa_profile *profile, struct file_perms *perms, | 106 | int aa_audit_file(struct aa_profile *profile, struct file_perms *perms, |
| 107 | gfp_t gfp, int op, u32 request, const char *name, | 107 | const char *op, u32 request, const char *name, |
| 108 | const char *target, kuid_t ouid, const char *info, int error) | 108 | const char *target, kuid_t ouid, const char *info, int error) |
| 109 | { | 109 | { |
| 110 | int type = AUDIT_APPARMOR_AUTO; | 110 | int type = AUDIT_APPARMOR_AUTO; |
| 111 | struct common_audit_data sa; | 111 | DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_TASK, op); |
| 112 | struct apparmor_audit_data aad = {0,}; | 112 | |
| 113 | sa.type = LSM_AUDIT_DATA_TASK; | 113 | sa.u.tsk = NULL; |
| 114 | aad(&sa)->fs.request = request; | ||
| 115 | aad(&sa)->name = name; | ||
| 116 | aad(&sa)->fs.target = target; | ||
| 117 | aad(&sa)->fs.ouid = ouid; | ||
| 118 | aad(&sa)->info = info; | ||
| 119 | aad(&sa)->error = error; | ||
| 114 | sa.u.tsk = NULL; | 120 | sa.u.tsk = NULL; |
| 115 | sa.aad = &aad; | 121 | |
| 116 | aad.op = op, | 122 | if (likely(!aad(&sa)->error)) { |
| 117 | aad.fs.request = request; | ||
| 118 | aad.name = name; | ||
| 119 | aad.fs.target = target; | ||
| 120 | aad.fs.ouid = ouid; | ||
| 121 | aad.info = info; | ||
| 122 | aad.error = error; | ||
| 123 | |||
| 124 | if (likely(!sa.aad->error)) { | ||
| 125 | u32 mask = perms->audit; | 123 | u32 mask = perms->audit; |
| 126 | 124 | ||
| 127 | if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL)) | 125 | if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL)) |
| 128 | mask = 0xffff; | 126 | mask = 0xffff; |
| 129 | 127 | ||
| 130 | /* mask off perms that are not being force audited */ | 128 | /* mask off perms that are not being force audited */ |
| 131 | sa.aad->fs.request &= mask; | 129 | aad(&sa)->fs.request &= mask; |
| 132 | 130 | ||
| 133 | if (likely(!sa.aad->fs.request)) | 131 | if (likely(!aad(&sa)->fs.request)) |
| 134 | return 0; | 132 | return 0; |
| 135 | type = AUDIT_APPARMOR_AUDIT; | 133 | type = AUDIT_APPARMOR_AUDIT; |
| 136 | } else { | 134 | } else { |
| 137 | /* only report permissions that were denied */ | 135 | /* only report permissions that were denied */ |
| 138 | sa.aad->fs.request = sa.aad->fs.request & ~perms->allow; | 136 | aad(&sa)->fs.request = aad(&sa)->fs.request & ~perms->allow; |
| 137 | AA_BUG(!aad(&sa)->fs.request); | ||
| 139 | 138 | ||
| 140 | if (sa.aad->fs.request & perms->kill) | 139 | if (aad(&sa)->fs.request & perms->kill) |
| 141 | type = AUDIT_APPARMOR_KILL; | 140 | type = AUDIT_APPARMOR_KILL; |
| 142 | 141 | ||
| 143 | /* quiet known rejects, assumes quiet and kill do not overlap */ | 142 | /* quiet known rejects, assumes quiet and kill do not overlap */ |
| 144 | if ((sa.aad->fs.request & perms->quiet) && | 143 | if ((aad(&sa)->fs.request & perms->quiet) && |
| 145 | AUDIT_MODE(profile) != AUDIT_NOQUIET && | 144 | AUDIT_MODE(profile) != AUDIT_NOQUIET && |
| 146 | AUDIT_MODE(profile) != AUDIT_ALL) | 145 | AUDIT_MODE(profile) != AUDIT_ALL) |
| 147 | sa.aad->fs.request &= ~perms->quiet; | 146 | aad(&sa)->fs.request &= ~perms->quiet; |
| 148 | 147 | ||
| 149 | if (!sa.aad->fs.request) | 148 | if (!aad(&sa)->fs.request) |
| 150 | return COMPLAIN_MODE(profile) ? 0 : sa.aad->error; | 149 | return COMPLAIN_MODE(profile) ? 0 : aad(&sa)->error; |
| 151 | } | 150 | } |
| 152 | 151 | ||
| 153 | sa.aad->fs.denied = sa.aad->fs.request & ~perms->allow; | 152 | aad(&sa)->fs.denied = aad(&sa)->fs.request & ~perms->allow; |
| 154 | return aa_audit(type, profile, gfp, &sa, file_audit_cb); | 153 | return aa_audit(type, profile, &sa, file_audit_cb); |
| 155 | } | 154 | } |
| 156 | 155 | ||
| 157 | /** | 156 | /** |
| @@ -276,8 +275,9 @@ static inline bool is_deleted(struct dentry *dentry) | |||
| 276 | * | 275 | * |
| 277 | * Returns: %0 else error if access denied or other error | 276 | * Returns: %0 else error if access denied or other error |
| 278 | */ | 277 | */ |
| 279 | int aa_path_perm(int op, struct aa_profile *profile, const struct path *path, | 278 | int aa_path_perm(const char *op, struct aa_profile *profile, |
| 280 | int flags, u32 request, struct path_cond *cond) | 279 | const struct path *path, int flags, u32 request, |
| 280 | struct path_cond *cond) | ||
| 281 | { | 281 | { |
| 282 | char *buffer = NULL; | 282 | char *buffer = NULL; |
| 283 | struct file_perms perms = {}; | 283 | struct file_perms perms = {}; |
| @@ -301,8 +301,8 @@ int aa_path_perm(int op, struct aa_profile *profile, const struct path *path, | |||
| 301 | if (request & ~perms.allow) | 301 | if (request & ~perms.allow) |
| 302 | error = -EACCES; | 302 | error = -EACCES; |
| 303 | } | 303 | } |
| 304 | error = aa_audit_file(profile, &perms, GFP_KERNEL, op, request, name, | 304 | error = aa_audit_file(profile, &perms, op, request, name, NULL, |
| 305 | NULL, cond->uid, info, error); | 305 | cond->uid, info, error); |
| 306 | kfree(buffer); | 306 | kfree(buffer); |
| 307 | 307 | ||
| 308 | return error; | 308 | return error; |
| @@ -349,8 +349,8 @@ static inline bool xindex_is_subset(u32 link, u32 target) | |||
| 349 | int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry, | 349 | int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry, |
| 350 | const struct path *new_dir, struct dentry *new_dentry) | 350 | const struct path *new_dir, struct dentry *new_dentry) |
| 351 | { | 351 | { |
| 352 | struct path link = { new_dir->mnt, new_dentry }; | 352 | struct path link = { .mnt = new_dir->mnt, .dentry = new_dentry }; |
| 353 | struct path target = { new_dir->mnt, old_dentry }; | 353 | struct path target = { .mnt = new_dir->mnt, .dentry = old_dentry }; |
| 354 | struct path_cond cond = { | 354 | struct path_cond cond = { |
| 355 | d_backing_inode(old_dentry)->i_uid, | 355 | d_backing_inode(old_dentry)->i_uid, |
| 356 | d_backing_inode(old_dentry)->i_mode | 356 | d_backing_inode(old_dentry)->i_mode |
| @@ -429,7 +429,7 @@ done_tests: | |||
| 429 | error = 0; | 429 | error = 0; |
| 430 | 430 | ||
| 431 | audit: | 431 | audit: |
| 432 | error = aa_audit_file(profile, &lperms, GFP_KERNEL, OP_LINK, request, | 432 | error = aa_audit_file(profile, &lperms, OP_LINK, request, |
| 433 | lname, tname, cond.uid, info, error); | 433 | lname, tname, cond.uid, info, error); |
| 434 | kfree(buffer); | 434 | kfree(buffer); |
| 435 | kfree(buffer2); | 435 | kfree(buffer2); |
| @@ -446,7 +446,7 @@ audit: | |||
| 446 | * | 446 | * |
| 447 | * Returns: %0 if access allowed else error | 447 | * Returns: %0 if access allowed else error |
| 448 | */ | 448 | */ |
| 449 | int aa_file_perm(int op, struct aa_profile *profile, struct file *file, | 449 | int aa_file_perm(const char *op, struct aa_profile *profile, struct file *file, |
| 450 | u32 request) | 450 | u32 request) |
| 451 | { | 451 | { |
| 452 | struct path_cond cond = { | 452 | struct path_cond cond = { |
diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h index 5d721e990876..1750cc0721c1 100644 --- a/security/apparmor/include/apparmor.h +++ b/security/apparmor/include/apparmor.h | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * AppArmor security module | 2 | * AppArmor security module |
| 3 | * | 3 | * |
| 4 | * This file contains AppArmor basic global and lib definitions | 4 | * This file contains AppArmor basic global |
| 5 | * | 5 | * |
| 6 | * Copyright (C) 1998-2008 Novell/SUSE | 6 | * Copyright (C) 1998-2008 Novell/SUSE |
| 7 | * Copyright 2009-2010 Canonical Ltd. | 7 | * Copyright 2009-2010 Canonical Ltd. |
| @@ -15,10 +15,7 @@ | |||
| 15 | #ifndef __APPARMOR_H | 15 | #ifndef __APPARMOR_H |
| 16 | #define __APPARMOR_H | 16 | #define __APPARMOR_H |
| 17 | 17 | ||
| 18 | #include <linux/slab.h> | 18 | #include <linux/types.h> |
| 19 | #include <linux/fs.h> | ||
| 20 | |||
| 21 | #include "match.h" | ||
| 22 | 19 | ||
| 23 | /* | 20 | /* |
| 24 | * Class of mediation types in the AppArmor policy db | 21 | * Class of mediation types in the AppArmor policy db |
| @@ -43,79 +40,4 @@ extern bool aa_g_logsyscall; | |||
| 43 | extern bool aa_g_paranoid_load; | 40 | extern bool aa_g_paranoid_load; |
| 44 | extern unsigned int aa_g_path_max; | 41 | extern unsigned int aa_g_path_max; |
| 45 | 42 | ||
| 46 | /* | ||
| 47 | * DEBUG remains global (no per profile flag) since it is mostly used in sysctl | ||
| 48 | * which is not related to profile accesses. | ||
| 49 | */ | ||
| 50 | |||
| 51 | #define AA_DEBUG(fmt, args...) \ | ||
| 52 | do { \ | ||
| 53 | if (aa_g_debug && printk_ratelimit()) \ | ||
| 54 | printk(KERN_DEBUG "AppArmor: " fmt, ##args); \ | ||
| 55 | } while (0) | ||
| 56 | |||
| 57 | #define AA_ERROR(fmt, args...) \ | ||
| 58 | do { \ | ||
| 59 | if (printk_ratelimit()) \ | ||
| 60 | printk(KERN_ERR "AppArmor: " fmt, ##args); \ | ||
| 61 | } while (0) | ||
| 62 | |||
| 63 | /* Flag indicating whether initialization completed */ | ||
| 64 | extern int apparmor_initialized __initdata; | ||
| 65 | |||
| 66 | /* fn's in lib */ | ||
| 67 | char *aa_split_fqname(char *args, char **ns_name); | ||
| 68 | void aa_info_message(const char *str); | ||
| 69 | void *__aa_kvmalloc(size_t size, gfp_t flags); | ||
| 70 | |||
| 71 | static inline void *kvmalloc(size_t size) | ||
| 72 | { | ||
| 73 | return __aa_kvmalloc(size, 0); | ||
| 74 | } | ||
| 75 | |||
| 76 | static inline void *kvzalloc(size_t size) | ||
| 77 | { | ||
| 78 | return __aa_kvmalloc(size, __GFP_ZERO); | ||
| 79 | } | ||
| 80 | |||
| 81 | /* returns 0 if kref not incremented */ | ||
| 82 | static inline int kref_get_not0(struct kref *kref) | ||
| 83 | { | ||
| 84 | return atomic_inc_not_zero(&kref->refcount); | ||
| 85 | } | ||
| 86 | |||
| 87 | /** | ||
| 88 | * aa_strneq - compare null terminated @str to a non null terminated substring | ||
| 89 | * @str: a null terminated string | ||
| 90 | * @sub: a substring, not necessarily null terminated | ||
| 91 | * @len: length of @sub to compare | ||
| 92 | * | ||
| 93 | * The @str string must be full consumed for this to be considered a match | ||
| 94 | */ | ||
| 95 | static inline bool aa_strneq(const char *str, const char *sub, int len) | ||
| 96 | { | ||
| 97 | return !strncmp(str, sub, len) && !str[len]; | ||
| 98 | } | ||
| 99 | |||
| 100 | /** | ||
| 101 | * aa_dfa_null_transition - step to next state after null character | ||
| 102 | * @dfa: the dfa to match against | ||
| 103 | * @start: the state of the dfa to start matching in | ||
| 104 | * | ||
| 105 | * aa_dfa_null_transition transitions to the next state after a null | ||
| 106 | * character which is not used in standard matching and is only | ||
| 107 | * used to separate pairs. | ||
| 108 | */ | ||
| 109 | static inline unsigned int aa_dfa_null_transition(struct aa_dfa *dfa, | ||
| 110 | unsigned int start) | ||
| 111 | { | ||
| 112 | /* the null transition only needs the string's null terminator byte */ | ||
| 113 | return aa_dfa_next(dfa, start, 0); | ||
| 114 | } | ||
| 115 | |||
| 116 | static inline bool mediated_filesystem(struct dentry *dentry) | ||
| 117 | { | ||
| 118 | return !(dentry->d_sb->s_flags & MS_NOUSER); | ||
| 119 | } | ||
| 120 | |||
| 121 | #endif /* __APPARMOR_H */ | 43 | #endif /* __APPARMOR_H */ |
diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h index 414e56878dd0..120a798b5bb0 100644 --- a/security/apparmor/include/apparmorfs.h +++ b/security/apparmor/include/apparmorfs.h | |||
| @@ -15,6 +15,8 @@ | |||
| 15 | #ifndef __AA_APPARMORFS_H | 15 | #ifndef __AA_APPARMORFS_H |
| 16 | #define __AA_APPARMORFS_H | 16 | #define __AA_APPARMORFS_H |
| 17 | 17 | ||
| 18 | extern struct path aa_null; | ||
| 19 | |||
| 18 | enum aa_fs_type { | 20 | enum aa_fs_type { |
| 19 | AA_FS_TYPE_BOOLEAN, | 21 | AA_FS_TYPE_BOOLEAN, |
| 20 | AA_FS_TYPE_STRING, | 22 | AA_FS_TYPE_STRING, |
| @@ -62,12 +64,16 @@ extern const struct file_operations aa_fs_seq_file_ops; | |||
| 62 | extern void __init aa_destroy_aafs(void); | 64 | extern void __init aa_destroy_aafs(void); |
| 63 | 65 | ||
| 64 | struct aa_profile; | 66 | struct aa_profile; |
| 65 | struct aa_namespace; | 67 | struct aa_ns; |
| 66 | 68 | ||
| 67 | enum aafs_ns_type { | 69 | enum aafs_ns_type { |
| 68 | AAFS_NS_DIR, | 70 | AAFS_NS_DIR, |
| 69 | AAFS_NS_PROFS, | 71 | AAFS_NS_PROFS, |
| 70 | AAFS_NS_NS, | 72 | AAFS_NS_NS, |
| 73 | AAFS_NS_RAW_DATA, | ||
| 74 | AAFS_NS_LOAD, | ||
| 75 | AAFS_NS_REPLACE, | ||
| 76 | AAFS_NS_REMOVE, | ||
| 71 | AAFS_NS_COUNT, | 77 | AAFS_NS_COUNT, |
| 72 | AAFS_NS_MAX_COUNT, | 78 | AAFS_NS_MAX_COUNT, |
| 73 | AAFS_NS_SIZE, | 79 | AAFS_NS_SIZE, |
| @@ -83,12 +89,19 @@ enum aafs_prof_type { | |||
| 83 | AAFS_PROF_MODE, | 89 | AAFS_PROF_MODE, |
| 84 | AAFS_PROF_ATTACH, | 90 | AAFS_PROF_ATTACH, |
| 85 | AAFS_PROF_HASH, | 91 | AAFS_PROF_HASH, |
| 92 | AAFS_PROF_RAW_DATA, | ||
| 93 | AAFS_PROF_RAW_HASH, | ||
| 94 | AAFS_PROF_RAW_ABI, | ||
| 86 | AAFS_PROF_SIZEOF, | 95 | AAFS_PROF_SIZEOF, |
| 87 | }; | 96 | }; |
| 88 | 97 | ||
| 89 | #define ns_dir(X) ((X)->dents[AAFS_NS_DIR]) | 98 | #define ns_dir(X) ((X)->dents[AAFS_NS_DIR]) |
| 90 | #define ns_subns_dir(X) ((X)->dents[AAFS_NS_NS]) | 99 | #define ns_subns_dir(X) ((X)->dents[AAFS_NS_NS]) |
| 91 | #define ns_subprofs_dir(X) ((X)->dents[AAFS_NS_PROFS]) | 100 | #define ns_subprofs_dir(X) ((X)->dents[AAFS_NS_PROFS]) |
| 101 | #define ns_subdata_dir(X) ((X)->dents[AAFS_NS_RAW_DATA]) | ||
| 102 | #define ns_subload(X) ((X)->dents[AAFS_NS_LOAD]) | ||
| 103 | #define ns_subreplace(X) ((X)->dents[AAFS_NS_REPLACE]) | ||
| 104 | #define ns_subremove(X) ((X)->dents[AAFS_NS_REMOVE]) | ||
| 92 | 105 | ||
| 93 | #define prof_dir(X) ((X)->dents[AAFS_PROF_DIR]) | 106 | #define prof_dir(X) ((X)->dents[AAFS_PROF_DIR]) |
| 94 | #define prof_child_dir(X) ((X)->dents[AAFS_PROF_PROFS]) | 107 | #define prof_child_dir(X) ((X)->dents[AAFS_PROF_PROFS]) |
| @@ -97,8 +110,8 @@ void __aa_fs_profile_rmdir(struct aa_profile *profile); | |||
| 97 | void __aa_fs_profile_migrate_dents(struct aa_profile *old, | 110 | void __aa_fs_profile_migrate_dents(struct aa_profile *old, |
| 98 | struct aa_profile *new); | 111 | struct aa_profile *new); |
| 99 | int __aa_fs_profile_mkdir(struct aa_profile *profile, struct dentry *parent); | 112 | int __aa_fs_profile_mkdir(struct aa_profile *profile, struct dentry *parent); |
| 100 | void __aa_fs_namespace_rmdir(struct aa_namespace *ns); | 113 | void __aa_fs_ns_rmdir(struct aa_ns *ns); |
| 101 | int __aa_fs_namespace_mkdir(struct aa_namespace *ns, struct dentry *parent, | 114 | int __aa_fs_ns_mkdir(struct aa_ns *ns, struct dentry *parent, |
| 102 | const char *name); | 115 | const char *name); |
| 103 | 116 | ||
| 104 | #endif /* __AA_APPARMORFS_H */ | 117 | #endif /* __AA_APPARMORFS_H */ |
diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index ba3dfd17f23f..fdc4774318ba 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h | |||
| @@ -46,97 +46,115 @@ enum audit_type { | |||
| 46 | AUDIT_APPARMOR_AUTO | 46 | AUDIT_APPARMOR_AUTO |
| 47 | }; | 47 | }; |
| 48 | 48 | ||
| 49 | extern const char *const op_table[]; | 49 | #define OP_NULL NULL |
| 50 | enum aa_ops { | 50 | |
| 51 | OP_NULL, | 51 | #define OP_SYSCTL "sysctl" |
| 52 | 52 | #define OP_CAPABLE "capable" | |
| 53 | OP_SYSCTL, | 53 | |
| 54 | OP_CAPABLE, | 54 | #define OP_UNLINK "unlink" |
| 55 | 55 | #define OP_MKDIR "mkdir" | |
| 56 | OP_UNLINK, | 56 | #define OP_RMDIR "rmdir" |
| 57 | OP_MKDIR, | 57 | #define OP_MKNOD "mknod" |
| 58 | OP_RMDIR, | 58 | #define OP_TRUNC "truncate" |
| 59 | OP_MKNOD, | 59 | #define OP_LINK "link" |
| 60 | OP_TRUNC, | 60 | #define OP_SYMLINK "symlink" |
| 61 | OP_LINK, | 61 | #define OP_RENAME_SRC "rename_src" |
| 62 | OP_SYMLINK, | 62 | #define OP_RENAME_DEST "rename_dest" |
| 63 | OP_RENAME_SRC, | 63 | #define OP_CHMOD "chmod" |
| 64 | OP_RENAME_DEST, | 64 | #define OP_CHOWN "chown" |
| 65 | OP_CHMOD, | 65 | #define OP_GETATTR "getattr" |
| 66 | OP_CHOWN, | 66 | #define OP_OPEN "open" |
| 67 | OP_GETATTR, | 67 | |
| 68 | OP_OPEN, | 68 | #define OP_FPERM "file_perm" |
| 69 | 69 | #define OP_FLOCK "file_lock" | |
| 70 | OP_FPERM, | 70 | #define OP_FMMAP "file_mmap" |
| 71 | OP_FLOCK, | 71 | #define OP_FMPROT "file_mprotect" |
| 72 | OP_FMMAP, | 72 | |
| 73 | OP_FMPROT, | 73 | #define OP_CREATE "create" |
| 74 | 74 | #define OP_POST_CREATE "post_create" | |
| 75 | OP_CREATE, | 75 | #define OP_BIND "bind" |
| 76 | OP_POST_CREATE, | 76 | #define OP_CONNECT "connect" |
| 77 | OP_BIND, | 77 | #define OP_LISTEN "listen" |
| 78 | OP_CONNECT, | 78 | #define OP_ACCEPT "accept" |
| 79 | OP_LISTEN, | 79 | #define OP_SENDMSG "sendmsg" |
| 80 | OP_ACCEPT, | 80 | #define OP_RECVMSG "recvmsg" |
| 81 | OP_SENDMSG, | 81 | #define OP_GETSOCKNAME "getsockname" |
| 82 | OP_RECVMSG, | 82 | #define OP_GETPEERNAME "getpeername" |
| 83 | OP_GETSOCKNAME, | 83 | #define OP_GETSOCKOPT "getsockopt" |
| 84 | OP_GETPEERNAME, | 84 | #define OP_SETSOCKOPT "setsockopt" |
| 85 | OP_GETSOCKOPT, | 85 | #define OP_SHUTDOWN "socket_shutdown" |
| 86 | OP_SETSOCKOPT, | 86 | |
| 87 | OP_SOCK_SHUTDOWN, | 87 | #define OP_PTRACE "ptrace" |
| 88 | 88 | ||
| 89 | OP_PTRACE, | 89 | #define OP_EXEC "exec" |
| 90 | 90 | ||
| 91 | OP_EXEC, | 91 | #define OP_CHANGE_HAT "change_hat" |
| 92 | OP_CHANGE_HAT, | 92 | #define OP_CHANGE_PROFILE "change_profile" |
| 93 | OP_CHANGE_PROFILE, | 93 | #define OP_CHANGE_ONEXEC "change_onexec" |
| 94 | OP_CHANGE_ONEXEC, | 94 | |
| 95 | 95 | #define OP_SETPROCATTR "setprocattr" | |
| 96 | OP_SETPROCATTR, | 96 | #define OP_SETRLIMIT "setrlimit" |
| 97 | OP_SETRLIMIT, | 97 | |
| 98 | 98 | #define OP_PROF_REPL "profile_replace" | |
| 99 | OP_PROF_REPL, | 99 | #define OP_PROF_LOAD "profile_load" |
| 100 | OP_PROF_LOAD, | 100 | #define OP_PROF_RM "profile_remove" |
| 101 | OP_PROF_RM, | ||
| 102 | }; | ||
| 103 | 101 | ||
| 104 | 102 | ||
| 105 | struct apparmor_audit_data { | 103 | struct apparmor_audit_data { |
| 106 | int error; | 104 | int error; |
| 107 | int op; | 105 | const char *op; |
| 108 | int type; | 106 | int type; |
| 109 | void *profile; | 107 | void *profile; |
| 110 | const char *name; | 108 | const char *name; |
| 111 | const char *info; | 109 | const char *info; |
| 112 | union { | 110 | union { |
| 113 | void *target; | 111 | /* these entries require a custom callback fn */ |
| 112 | struct { | ||
| 113 | struct aa_profile *peer; | ||
| 114 | struct { | ||
| 115 | const char *target; | ||
| 116 | u32 request; | ||
| 117 | u32 denied; | ||
| 118 | kuid_t ouid; | ||
| 119 | } fs; | ||
| 120 | }; | ||
| 114 | struct { | 121 | struct { |
| 122 | const char *name; | ||
| 115 | long pos; | 123 | long pos; |
| 116 | void *target; | 124 | const char *ns; |
| 117 | } iface; | 125 | } iface; |
| 118 | struct { | 126 | struct { |
| 119 | int rlim; | 127 | int rlim; |
| 120 | unsigned long max; | 128 | unsigned long max; |
| 121 | } rlim; | 129 | } rlim; |
| 122 | struct { | ||
| 123 | const char *target; | ||
| 124 | u32 request; | ||
| 125 | u32 denied; | ||
| 126 | kuid_t ouid; | ||
| 127 | } fs; | ||
| 128 | }; | 130 | }; |
| 129 | }; | 131 | }; |
| 130 | 132 | ||
| 131 | /* define a short hand for apparmor_audit_data structure */ | 133 | /* macros for dealing with apparmor_audit_data structure */ |
| 132 | #define aad apparmor_audit_data | 134 | #define aad(SA) ((SA)->apparmor_audit_data) |
| 135 | #define DEFINE_AUDIT_DATA(NAME, T, X) \ | ||
| 136 | /* TODO: cleanup audit init so we don't need _aad = {0,} */ \ | ||
| 137 | struct apparmor_audit_data NAME ## _aad = { .op = (X), }; \ | ||
| 138 | struct common_audit_data NAME = \ | ||
| 139 | { \ | ||
| 140 | .type = (T), \ | ||
| 141 | .u.tsk = NULL, \ | ||
| 142 | }; \ | ||
| 143 | NAME.apparmor_audit_data = &(NAME ## _aad) | ||
| 133 | 144 | ||
| 134 | void aa_audit_msg(int type, struct common_audit_data *sa, | 145 | void aa_audit_msg(int type, struct common_audit_data *sa, |
| 135 | void (*cb) (struct audit_buffer *, void *)); | 146 | void (*cb) (struct audit_buffer *, void *)); |
| 136 | int aa_audit(int type, struct aa_profile *profile, gfp_t gfp, | 147 | int aa_audit(int type, struct aa_profile *profile, struct common_audit_data *sa, |
| 137 | struct common_audit_data *sa, | ||
| 138 | void (*cb) (struct audit_buffer *, void *)); | 148 | void (*cb) (struct audit_buffer *, void *)); |
| 139 | 149 | ||
| 150 | #define aa_audit_error(ERROR, SA, CB) \ | ||
| 151 | ({ \ | ||
| 152 | aad((SA))->error = (ERROR); \ | ||
| 153 | aa_audit_msg(AUDIT_APPARMOR_ERROR, (SA), (CB)); \ | ||
| 154 | aad((SA))->error; \ | ||
| 155 | }) | ||
| 156 | |||
| 157 | |||
| 140 | static inline int complain_error(int error) | 158 | static inline int complain_error(int error) |
| 141 | { | 159 | { |
| 142 | if (error == -EPERM || error == -EACCES) | 160 | if (error == -EPERM || error == -EACCES) |
diff --git a/security/apparmor/include/context.h b/security/apparmor/include/context.h index 6bf65798e5d1..5b18fedab4c8 100644 --- a/security/apparmor/include/context.h +++ b/security/apparmor/include/context.h | |||
| @@ -20,44 +20,45 @@ | |||
| 20 | #include <linux/sched.h> | 20 | #include <linux/sched.h> |
| 21 | 21 | ||
| 22 | #include "policy.h" | 22 | #include "policy.h" |
| 23 | #include "policy_ns.h" | ||
| 23 | 24 | ||
| 24 | #define cred_cxt(X) (X)->security | 25 | #define cred_ctx(X) ((X)->security) |
| 25 | #define current_cxt() cred_cxt(current_cred()) | 26 | #define current_ctx() cred_ctx(current_cred()) |
| 26 | 27 | ||
| 27 | /* struct aa_file_cxt - the AppArmor context the file was opened in | 28 | /* struct aa_file_ctx - the AppArmor context the file was opened in |
| 28 | * @perms: the permission the file was opened with | 29 | * @perms: the permission the file was opened with |
| 29 | * | 30 | * |
| 30 | * The file_cxt could currently be directly stored in file->f_security | 31 | * The file_ctx could currently be directly stored in file->f_security |
| 31 | * as the profile reference is now stored in the f_cred. However the | 32 | * as the profile reference is now stored in the f_cred. However the |
| 32 | * cxt struct will expand in the future so we keep the struct. | 33 | * ctx struct will expand in the future so we keep the struct. |
| 33 | */ | 34 | */ |
| 34 | struct aa_file_cxt { | 35 | struct aa_file_ctx { |
| 35 | u16 allow; | 36 | u16 allow; |
| 36 | }; | 37 | }; |
| 37 | 38 | ||
| 38 | /** | 39 | /** |
| 39 | * aa_alloc_file_context - allocate file_cxt | 40 | * aa_alloc_file_context - allocate file_ctx |
| 40 | * @gfp: gfp flags for allocation | 41 | * @gfp: gfp flags for allocation |
| 41 | * | 42 | * |
| 42 | * Returns: file_cxt or NULL on failure | 43 | * Returns: file_ctx or NULL on failure |
| 43 | */ | 44 | */ |
| 44 | static inline struct aa_file_cxt *aa_alloc_file_context(gfp_t gfp) | 45 | static inline struct aa_file_ctx *aa_alloc_file_context(gfp_t gfp) |
| 45 | { | 46 | { |
| 46 | return kzalloc(sizeof(struct aa_file_cxt), gfp); | 47 | return kzalloc(sizeof(struct aa_file_ctx), gfp); |
| 47 | } | 48 | } |
| 48 | 49 | ||
| 49 | /** | 50 | /** |
| 50 | * aa_free_file_context - free a file_cxt | 51 | * aa_free_file_context - free a file_ctx |
| 51 | * @cxt: file_cxt to free (MAYBE_NULL) | 52 | * @ctx: file_ctx to free (MAYBE_NULL) |
| 52 | */ | 53 | */ |
| 53 | static inline void aa_free_file_context(struct aa_file_cxt *cxt) | 54 | static inline void aa_free_file_context(struct aa_file_ctx *ctx) |
| 54 | { | 55 | { |
| 55 | if (cxt) | 56 | if (ctx) |
| 56 | kzfree(cxt); | 57 | kzfree(ctx); |
| 57 | } | 58 | } |
| 58 | 59 | ||
| 59 | /** | 60 | /** |
| 60 | * struct aa_task_cxt - primary label for confined tasks | 61 | * struct aa_task_ctx - primary label for confined tasks |
| 61 | * @profile: the current profile (NOT NULL) | 62 | * @profile: the current profile (NOT NULL) |
| 62 | * @exec: profile to transition to on next exec (MAYBE NULL) | 63 | * @exec: profile to transition to on next exec (MAYBE NULL) |
| 63 | * @previous: profile the task may return to (MAYBE NULL) | 64 | * @previous: profile the task may return to (MAYBE NULL) |
| @@ -68,17 +69,17 @@ static inline void aa_free_file_context(struct aa_file_cxt *cxt) | |||
| 68 | * | 69 | * |
| 69 | * TODO: make so a task can be confined by a stack of contexts | 70 | * TODO: make so a task can be confined by a stack of contexts |
| 70 | */ | 71 | */ |
| 71 | struct aa_task_cxt { | 72 | struct aa_task_ctx { |
| 72 | struct aa_profile *profile; | 73 | struct aa_profile *profile; |
| 73 | struct aa_profile *onexec; | 74 | struct aa_profile *onexec; |
| 74 | struct aa_profile *previous; | 75 | struct aa_profile *previous; |
| 75 | u64 token; | 76 | u64 token; |
| 76 | }; | 77 | }; |
| 77 | 78 | ||
| 78 | struct aa_task_cxt *aa_alloc_task_context(gfp_t flags); | 79 | struct aa_task_ctx *aa_alloc_task_context(gfp_t flags); |
| 79 | void aa_free_task_context(struct aa_task_cxt *cxt); | 80 | void aa_free_task_context(struct aa_task_ctx *ctx); |
| 80 | void aa_dup_task_context(struct aa_task_cxt *new, | 81 | void aa_dup_task_context(struct aa_task_ctx *new, |
| 81 | const struct aa_task_cxt *old); | 82 | const struct aa_task_ctx *old); |
| 82 | int aa_replace_current_profile(struct aa_profile *profile); | 83 | int aa_replace_current_profile(struct aa_profile *profile); |
| 83 | int aa_set_current_onexec(struct aa_profile *profile); | 84 | int aa_set_current_onexec(struct aa_profile *profile); |
| 84 | int aa_set_current_hat(struct aa_profile *profile, u64 token); | 85 | int aa_set_current_hat(struct aa_profile *profile, u64 token); |
| @@ -96,9 +97,10 @@ struct aa_profile *aa_get_task_profile(struct task_struct *task); | |||
| 96 | */ | 97 | */ |
| 97 | static inline struct aa_profile *aa_cred_profile(const struct cred *cred) | 98 | static inline struct aa_profile *aa_cred_profile(const struct cred *cred) |
| 98 | { | 99 | { |
| 99 | struct aa_task_cxt *cxt = cred_cxt(cred); | 100 | struct aa_task_ctx *ctx = cred_ctx(cred); |
| 100 | BUG_ON(!cxt || !cxt->profile); | 101 | |
| 101 | return cxt->profile; | 102 | AA_BUG(!ctx || !ctx->profile); |
| 103 | return ctx->profile; | ||
| 102 | } | 104 | } |
| 103 | 105 | ||
| 104 | /** | 106 | /** |
| @@ -148,31 +150,37 @@ static inline struct aa_profile *__aa_current_profile(void) | |||
| 148 | */ | 150 | */ |
| 149 | static inline struct aa_profile *aa_current_profile(void) | 151 | static inline struct aa_profile *aa_current_profile(void) |
| 150 | { | 152 | { |
| 151 | const struct aa_task_cxt *cxt = current_cxt(); | 153 | const struct aa_task_ctx *ctx = current_ctx(); |
| 152 | struct aa_profile *profile; | 154 | struct aa_profile *profile; |
| 153 | BUG_ON(!cxt || !cxt->profile); | ||
| 154 | 155 | ||
| 155 | if (PROFILE_INVALID(cxt->profile)) { | 156 | AA_BUG(!ctx || !ctx->profile); |
| 156 | profile = aa_get_newest_profile(cxt->profile); | 157 | |
| 158 | if (profile_is_stale(ctx->profile)) { | ||
| 159 | profile = aa_get_newest_profile(ctx->profile); | ||
| 157 | aa_replace_current_profile(profile); | 160 | aa_replace_current_profile(profile); |
| 158 | aa_put_profile(profile); | 161 | aa_put_profile(profile); |
| 159 | cxt = current_cxt(); | 162 | ctx = current_ctx(); |
| 160 | } | 163 | } |
| 161 | 164 | ||
| 162 | return cxt->profile; | 165 | return ctx->profile; |
| 166 | } | ||
| 167 | |||
| 168 | static inline struct aa_ns *aa_get_current_ns(void) | ||
| 169 | { | ||
| 170 | return aa_get_ns(__aa_current_profile()->ns); | ||
| 163 | } | 171 | } |
| 164 | 172 | ||
| 165 | /** | 173 | /** |
| 166 | * aa_clear_task_cxt_trans - clear transition tracking info from the cxt | 174 | * aa_clear_task_ctx_trans - clear transition tracking info from the ctx |
| 167 | * @cxt: task context to clear (NOT NULL) | 175 | * @ctx: task context to clear (NOT NULL) |
| 168 | */ | 176 | */ |
| 169 | static inline void aa_clear_task_cxt_trans(struct aa_task_cxt *cxt) | 177 | static inline void aa_clear_task_ctx_trans(struct aa_task_ctx *ctx) |
| 170 | { | 178 | { |
| 171 | aa_put_profile(cxt->previous); | 179 | aa_put_profile(ctx->previous); |
| 172 | aa_put_profile(cxt->onexec); | 180 | aa_put_profile(ctx->onexec); |
| 173 | cxt->previous = NULL; | 181 | ctx->previous = NULL; |
| 174 | cxt->onexec = NULL; | 182 | ctx->onexec = NULL; |
| 175 | cxt->token = 0; | 183 | ctx->token = 0; |
| 176 | } | 184 | } |
| 177 | 185 | ||
| 178 | #endif /* __AA_CONTEXT_H */ | 186 | #endif /* __AA_CONTEXT_H */ |
diff --git a/security/apparmor/include/crypto.h b/security/apparmor/include/crypto.h index dc418e5024d9..c1469f8db174 100644 --- a/security/apparmor/include/crypto.h +++ b/security/apparmor/include/crypto.h | |||
| @@ -18,9 +18,14 @@ | |||
| 18 | 18 | ||
| 19 | #ifdef CONFIG_SECURITY_APPARMOR_HASH | 19 | #ifdef CONFIG_SECURITY_APPARMOR_HASH |
| 20 | unsigned int aa_hash_size(void); | 20 | unsigned int aa_hash_size(void); |
| 21 | char *aa_calc_hash(void *data, size_t len); | ||
| 21 | int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start, | 22 | int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start, |
| 22 | size_t len); | 23 | size_t len); |
| 23 | #else | 24 | #else |
| 25 | static inline char *aa_calc_hash(void *data, size_t len) | ||
| 26 | { | ||
| 27 | return NULL; | ||
| 28 | } | ||
| 24 | static inline int aa_calc_profile_hash(struct aa_profile *profile, u32 version, | 29 | static inline int aa_calc_profile_hash(struct aa_profile *profile, u32 version, |
| 25 | void *start, size_t len) | 30 | void *start, size_t len) |
| 26 | { | 31 | { |
diff --git a/security/apparmor/include/domain.h b/security/apparmor/include/domain.h index de04464f0a3f..30544729878a 100644 --- a/security/apparmor/include/domain.h +++ b/security/apparmor/include/domain.h | |||
| @@ -30,7 +30,7 @@ void apparmor_bprm_committed_creds(struct linux_binprm *bprm); | |||
| 30 | 30 | ||
| 31 | void aa_free_domain_entries(struct aa_domain *domain); | 31 | void aa_free_domain_entries(struct aa_domain *domain); |
| 32 | int aa_change_hat(const char *hats[], int count, u64 token, bool permtest); | 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, | 33 | int aa_change_profile(const char *fqname, bool onexec, bool permtest, |
| 34 | bool permtest); | 34 | bool stack); |
| 35 | 35 | ||
| 36 | #endif /* __AA_DOMAIN_H */ | 36 | #endif /* __AA_DOMAIN_H */ |
diff --git a/security/apparmor/include/file.h b/security/apparmor/include/file.h index 4803c97d1992..38f821bf49b6 100644 --- a/security/apparmor/include/file.h +++ b/security/apparmor/include/file.h | |||
| @@ -145,7 +145,7 @@ static inline u16 dfa_map_xindex(u16 mask) | |||
| 145 | dfa_map_xindex((ACCEPT_TABLE(dfa)[state] >> 14) & 0x3fff) | 145 | dfa_map_xindex((ACCEPT_TABLE(dfa)[state] >> 14) & 0x3fff) |
| 146 | 146 | ||
| 147 | int aa_audit_file(struct aa_profile *profile, struct file_perms *perms, | 147 | int aa_audit_file(struct aa_profile *profile, struct file_perms *perms, |
| 148 | gfp_t gfp, int op, u32 request, const char *name, | 148 | const char *op, u32 request, const char *name, |
| 149 | const char *target, kuid_t ouid, const char *info, int error); | 149 | const char *target, kuid_t ouid, const char *info, int error); |
| 150 | 150 | ||
| 151 | /** | 151 | /** |
| @@ -171,13 +171,14 @@ unsigned int aa_str_perms(struct aa_dfa *dfa, unsigned int start, | |||
| 171 | const char *name, struct path_cond *cond, | 171 | const char *name, struct path_cond *cond, |
| 172 | struct file_perms *perms); | 172 | struct file_perms *perms); |
| 173 | 173 | ||
| 174 | int aa_path_perm(int op, struct aa_profile *profile, const struct path *path, | 174 | int aa_path_perm(const char *op, struct aa_profile *profile, |
| 175 | int flags, u32 request, struct path_cond *cond); | 175 | const struct path *path, int flags, u32 request, |
| 176 | struct path_cond *cond); | ||
| 176 | 177 | ||
| 177 | int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry, | 178 | int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry, |
| 178 | const struct path *new_dir, struct dentry *new_dentry); | 179 | const struct path *new_dir, struct dentry *new_dentry); |
| 179 | 180 | ||
| 180 | int aa_file_perm(int op, struct aa_profile *profile, struct file *file, | 181 | int aa_file_perm(const char *op, struct aa_profile *profile, struct file *file, |
| 181 | u32 request); | 182 | u32 request); |
| 182 | 183 | ||
| 183 | static inline void aa_free_file_rules(struct aa_file_rules *rules) | 184 | static inline void aa_free_file_rules(struct aa_file_rules *rules) |
diff --git a/security/apparmor/include/lib.h b/security/apparmor/include/lib.h new file mode 100644 index 000000000000..65ff492a9807 --- /dev/null +++ b/security/apparmor/include/lib.h | |||
| @@ -0,0 +1,194 @@ | |||
| 1 | /* | ||
| 2 | * AppArmor security module | ||
| 3 | * | ||
| 4 | * This file contains AppArmor lib definitions | ||
| 5 | * | ||
| 6 | * 2017 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_LIB_H | ||
| 15 | #define __AA_LIB_H | ||
| 16 | |||
| 17 | #include <linux/slab.h> | ||
| 18 | #include <linux/fs.h> | ||
| 19 | |||
| 20 | #include "match.h" | ||
| 21 | |||
| 22 | /* Provide our own test for whether a write lock is held for asserts | ||
| 23 | * this is because on none SMP systems write_can_lock will always | ||
| 24 | * resolve to true, which is what you want for code making decisions | ||
| 25 | * based on it, but wrong for asserts checking that the lock is held | ||
| 26 | */ | ||
| 27 | #ifdef CONFIG_SMP | ||
| 28 | #define write_is_locked(X) !write_can_lock(X) | ||
| 29 | #else | ||
| 30 | #define write_is_locked(X) (1) | ||
| 31 | #endif /* CONFIG_SMP */ | ||
| 32 | |||
| 33 | /* | ||
| 34 | * DEBUG remains global (no per profile flag) since it is mostly used in sysctl | ||
| 35 | * which is not related to profile accesses. | ||
| 36 | */ | ||
| 37 | |||
| 38 | #define DEBUG_ON (aa_g_debug) | ||
| 39 | #define dbg_printk(__fmt, __args...) pr_debug(__fmt, ##__args) | ||
| 40 | #define AA_DEBUG(fmt, args...) \ | ||
| 41 | do { \ | ||
| 42 | if (DEBUG_ON) \ | ||
| 43 | pr_debug_ratelimited("AppArmor: " fmt, ##args); \ | ||
| 44 | } while (0) | ||
| 45 | |||
| 46 | #define AA_WARN(X) WARN((X), "APPARMOR WARN %s: %s\n", __func__, #X) | ||
| 47 | |||
| 48 | #define AA_BUG(X, args...) AA_BUG_FMT((X), "" args) | ||
| 49 | #ifdef CONFIG_SECURITY_APPARMOR_DEBUG_ASSERTS | ||
| 50 | #define AA_BUG_FMT(X, fmt, args...) \ | ||
| 51 | WARN((X), "AppArmor WARN %s: (" #X "): " fmt, __func__, ##args) | ||
| 52 | #else | ||
| 53 | #define AA_BUG_FMT(X, fmt, args...) | ||
| 54 | #endif | ||
| 55 | |||
| 56 | #define AA_ERROR(fmt, args...) \ | ||
| 57 | pr_err_ratelimited("AppArmor: " fmt, ##args) | ||
| 58 | |||
| 59 | /* Flag indicating whether initialization completed */ | ||
| 60 | extern int apparmor_initialized __initdata; | ||
| 61 | |||
| 62 | /* fn's in lib */ | ||
| 63 | char *aa_split_fqname(char *args, char **ns_name); | ||
| 64 | const char *aa_splitn_fqname(const char *fqname, size_t n, const char **ns_name, | ||
| 65 | size_t *ns_len); | ||
| 66 | void aa_info_message(const char *str); | ||
| 67 | void *__aa_kvmalloc(size_t size, gfp_t flags); | ||
| 68 | |||
| 69 | static inline void *kvmalloc(size_t size) | ||
| 70 | { | ||
| 71 | return __aa_kvmalloc(size, 0); | ||
| 72 | } | ||
| 73 | |||
| 74 | static inline void *kvzalloc(size_t size) | ||
| 75 | { | ||
| 76 | return __aa_kvmalloc(size, __GFP_ZERO); | ||
| 77 | } | ||
| 78 | |||
| 79 | /** | ||
| 80 | * aa_strneq - compare null terminated @str to a non null terminated substring | ||
| 81 | * @str: a null terminated string | ||
| 82 | * @sub: a substring, not necessarily null terminated | ||
| 83 | * @len: length of @sub to compare | ||
| 84 | * | ||
| 85 | * The @str string must be full consumed for this to be considered a match | ||
| 86 | */ | ||
| 87 | static inline bool aa_strneq(const char *str, const char *sub, int len) | ||
| 88 | { | ||
| 89 | return !strncmp(str, sub, len) && !str[len]; | ||
| 90 | } | ||
| 91 | |||
| 92 | /** | ||
| 93 | * aa_dfa_null_transition - step to next state after null character | ||
| 94 | * @dfa: the dfa to match against | ||
| 95 | * @start: the state of the dfa to start matching in | ||
| 96 | * | ||
| 97 | * aa_dfa_null_transition transitions to the next state after a null | ||
| 98 | * character which is not used in standard matching and is only | ||
| 99 | * used to separate pairs. | ||
| 100 | */ | ||
| 101 | static inline unsigned int aa_dfa_null_transition(struct aa_dfa *dfa, | ||
| 102 | unsigned int start) | ||
| 103 | { | ||
| 104 | /* the null transition only needs the string's null terminator byte */ | ||
| 105 | return aa_dfa_next(dfa, start, 0); | ||
| 106 | } | ||
| 107 | |||
| 108 | static inline bool path_mediated_fs(struct dentry *dentry) | ||
| 109 | { | ||
| 110 | return !(dentry->d_sb->s_flags & MS_NOUSER); | ||
| 111 | } | ||
| 112 | |||
| 113 | /* struct aa_policy - common part of both namespaces and profiles | ||
| 114 | * @name: name of the object | ||
| 115 | * @hname - The hierarchical name | ||
| 116 | * @list: list policy object is on | ||
| 117 | * @profiles: head of the profiles list contained in the object | ||
| 118 | */ | ||
| 119 | struct aa_policy { | ||
| 120 | const char *name; | ||
| 121 | const char *hname; | ||
| 122 | struct list_head list; | ||
| 123 | struct list_head profiles; | ||
| 124 | }; | ||
| 125 | |||
| 126 | /** | ||
| 127 | * basename - find the last component of an hname | ||
| 128 | * @name: hname to find the base profile name component of (NOT NULL) | ||
| 129 | * | ||
| 130 | * Returns: the tail (base profile name) name component of an hname | ||
| 131 | */ | ||
| 132 | static inline const char *basename(const char *hname) | ||
| 133 | { | ||
| 134 | char *split; | ||
| 135 | |||
| 136 | hname = strim((char *)hname); | ||
| 137 | for (split = strstr(hname, "//"); split; split = strstr(hname, "//")) | ||
| 138 | hname = split + 2; | ||
| 139 | |||
| 140 | return hname; | ||
| 141 | } | ||
| 142 | |||
| 143 | /** | ||
| 144 | * __policy_find - find a policy by @name on a policy list | ||
| 145 | * @head: list to search (NOT NULL) | ||
| 146 | * @name: name to search for (NOT NULL) | ||
| 147 | * | ||
| 148 | * Requires: rcu_read_lock be held | ||
| 149 | * | ||
| 150 | * Returns: unrefcounted policy that match @name or NULL if not found | ||
| 151 | */ | ||
| 152 | static inline struct aa_policy *__policy_find(struct list_head *head, | ||
| 153 | const char *name) | ||
| 154 | { | ||
| 155 | struct aa_policy *policy; | ||
| 156 | |||
| 157 | list_for_each_entry_rcu(policy, head, list) { | ||
| 158 | if (!strcmp(policy->name, name)) | ||
| 159 | return policy; | ||
| 160 | } | ||
| 161 | return NULL; | ||
| 162 | } | ||
| 163 | |||
| 164 | /** | ||
| 165 | * __policy_strn_find - find a policy that's name matches @len chars of @str | ||
| 166 | * @head: list to search (NOT NULL) | ||
| 167 | * @str: string to search for (NOT NULL) | ||
| 168 | * @len: length of match required | ||
| 169 | * | ||
| 170 | * Requires: rcu_read_lock be held | ||
| 171 | * | ||
| 172 | * Returns: unrefcounted policy that match @str or NULL if not found | ||
| 173 | * | ||
| 174 | * if @len == strlen(@strlen) then this is equiv to __policy_find | ||
| 175 | * other wise it allows searching for policy by a partial match of name | ||
| 176 | */ | ||
| 177 | static inline struct aa_policy *__policy_strn_find(struct list_head *head, | ||
| 178 | const char *str, int len) | ||
| 179 | { | ||
| 180 | struct aa_policy *policy; | ||
| 181 | |||
| 182 | list_for_each_entry_rcu(policy, head, list) { | ||
| 183 | if (aa_strneq(policy->name, str, len)) | ||
| 184 | return policy; | ||
| 185 | } | ||
| 186 | |||
| 187 | return NULL; | ||
| 188 | } | ||
| 189 | |||
| 190 | bool aa_policy_init(struct aa_policy *policy, const char *prefix, | ||
| 191 | const char *name, gfp_t gfp); | ||
| 192 | void aa_policy_destroy(struct aa_policy *policy); | ||
| 193 | |||
| 194 | #endif /* AA_LIB_H */ | ||
diff --git a/security/apparmor/include/match.h b/security/apparmor/include/match.h index a1c04fe86790..add4c6726558 100644 --- a/security/apparmor/include/match.h +++ b/security/apparmor/include/match.h | |||
| @@ -100,13 +100,15 @@ struct aa_dfa { | |||
| 100 | struct table_header *tables[YYTD_ID_TSIZE]; | 100 | struct table_header *tables[YYTD_ID_TSIZE]; |
| 101 | }; | 101 | }; |
| 102 | 102 | ||
| 103 | extern struct aa_dfa *nulldfa; | ||
| 104 | |||
| 103 | #define byte_to_byte(X) (X) | 105 | #define byte_to_byte(X) (X) |
| 104 | 106 | ||
| 105 | #define UNPACK_ARRAY(TABLE, BLOB, LEN, TYPE, NTOHX) \ | 107 | #define UNPACK_ARRAY(TABLE, BLOB, LEN, TTYPE, BTYPE, NTOHX) \ |
| 106 | do { \ | 108 | do { \ |
| 107 | typeof(LEN) __i; \ | 109 | typeof(LEN) __i; \ |
| 108 | TYPE *__t = (TYPE *) TABLE; \ | 110 | TTYPE *__t = (TTYPE *) TABLE; \ |
| 109 | TYPE *__b = (TYPE *) BLOB; \ | 111 | BTYPE *__b = (BTYPE *) BLOB; \ |
| 110 | for (__i = 0; __i < LEN; __i++) { \ | 112 | for (__i = 0; __i < LEN; __i++) { \ |
| 111 | __t[__i] = NTOHX(__b[__i]); \ | 113 | __t[__i] = NTOHX(__b[__i]); \ |
| 112 | } \ | 114 | } \ |
| @@ -117,6 +119,9 @@ static inline size_t table_size(size_t len, size_t el_size) | |||
| 117 | return ALIGN(sizeof(struct table_header) + len * el_size, 8); | 119 | return ALIGN(sizeof(struct table_header) + len * el_size, 8); |
| 118 | } | 120 | } |
| 119 | 121 | ||
| 122 | int aa_setup_dfa_engine(void); | ||
| 123 | void aa_teardown_dfa_engine(void); | ||
| 124 | |||
| 120 | struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags); | 125 | struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags); |
| 121 | unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start, | 126 | unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start, |
| 122 | const char *str, int len); | 127 | const char *str, int len); |
| @@ -128,6 +133,21 @@ unsigned int aa_dfa_next(struct aa_dfa *dfa, unsigned int state, | |||
| 128 | void aa_dfa_free_kref(struct kref *kref); | 133 | void aa_dfa_free_kref(struct kref *kref); |
| 129 | 134 | ||
| 130 | /** | 135 | /** |
| 136 | * aa_get_dfa - increment refcount on dfa @p | ||
| 137 | * @dfa: dfa (MAYBE NULL) | ||
| 138 | * | ||
| 139 | * Returns: pointer to @dfa if @dfa is NULL will return NULL | ||
| 140 | * Requires: @dfa must be held with valid refcount when called | ||
| 141 | */ | ||
| 142 | static inline struct aa_dfa *aa_get_dfa(struct aa_dfa *dfa) | ||
| 143 | { | ||
| 144 | if (dfa) | ||
| 145 | kref_get(&(dfa->count)); | ||
| 146 | |||
| 147 | return dfa; | ||
| 148 | } | ||
| 149 | |||
| 150 | /** | ||
| 131 | * aa_put_dfa - put a dfa refcount | 151 | * aa_put_dfa - put a dfa refcount |
| 132 | * @dfa: dfa to put refcount (MAYBE NULL) | 152 | * @dfa: dfa to put refcount (MAYBE NULL) |
| 133 | * | 153 | * |
diff --git a/security/apparmor/include/path.h b/security/apparmor/include/path.h index 73560f258784..0444fdde3918 100644 --- a/security/apparmor/include/path.h +++ b/security/apparmor/include/path.h | |||
| @@ -29,4 +29,57 @@ enum path_flags { | |||
| 29 | int aa_path_name(const struct path *path, int flags, char **buffer, | 29 | int aa_path_name(const struct path *path, int flags, char **buffer, |
| 30 | const char **name, const char **info); | 30 | const char **name, const char **info); |
| 31 | 31 | ||
| 32 | #define MAX_PATH_BUFFERS 2 | ||
| 33 | |||
| 34 | /* Per cpu buffers used during mediation */ | ||
| 35 | /* preallocated buffers to use during path lookups */ | ||
| 36 | struct aa_buffers { | ||
| 37 | char *buf[MAX_PATH_BUFFERS]; | ||
| 38 | }; | ||
| 39 | |||
| 40 | #include <linux/percpu.h> | ||
| 41 | #include <linux/preempt.h> | ||
| 42 | |||
| 43 | DECLARE_PER_CPU(struct aa_buffers, aa_buffers); | ||
| 44 | |||
| 45 | #define COUNT_ARGS(X...) COUNT_ARGS_HELPER(, ##X, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) | ||
| 46 | #define COUNT_ARGS_HELPER(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, n, X...) n | ||
| 47 | #define CONCAT(X, Y) X ## Y | ||
| 48 | #define CONCAT_AFTER(X, Y) CONCAT(X, Y) | ||
| 49 | |||
| 50 | #define ASSIGN(FN, X, N) ((X) = FN(N)) | ||
| 51 | #define EVAL1(FN, X) ASSIGN(FN, X, 0) /*X = FN(0)*/ | ||
| 52 | #define EVAL2(FN, X, Y...) do { ASSIGN(FN, X, 1); EVAL1(FN, Y); } while (0) | ||
| 53 | #define EVAL(FN, X...) CONCAT_AFTER(EVAL, COUNT_ARGS(X))(FN, X) | ||
| 54 | |||
| 55 | #define for_each_cpu_buffer(I) for ((I) = 0; (I) < MAX_PATH_BUFFERS; (I)++) | ||
| 56 | |||
| 57 | #ifdef CONFIG_DEBUG_PREEMPT | ||
| 58 | #define AA_BUG_PREEMPT_ENABLED(X) AA_BUG(preempt_count() <= 0, X) | ||
| 59 | #else | ||
| 60 | #define AA_BUG_PREEMPT_ENABLED(X) /* nop */ | ||
| 61 | #endif | ||
| 62 | |||
| 63 | #define __get_buffer(N) ({ \ | ||
| 64 | struct aa_buffers *__cpu_var; \ | ||
| 65 | AA_BUG_PREEMPT_ENABLED("__get_buffer without preempt disabled"); \ | ||
| 66 | __cpu_var = this_cpu_ptr(&aa_buffers); \ | ||
| 67 | __cpu_var->buf[(N)]; }) | ||
| 68 | |||
| 69 | #define __get_buffers(X...) EVAL(__get_buffer, X) | ||
| 70 | |||
| 71 | #define __put_buffers(X, Y...) ((void)&(X)) | ||
| 72 | |||
| 73 | #define get_buffers(X...) \ | ||
| 74 | do { \ | ||
| 75 | preempt_disable(); \ | ||
| 76 | __get_buffers(X); \ | ||
| 77 | } while (0) | ||
| 78 | |||
| 79 | #define put_buffers(X, Y...) \ | ||
| 80 | do { \ | ||
| 81 | __put_buffers(X, Y); \ | ||
| 82 | preempt_enable(); \ | ||
| 83 | } while (0) | ||
| 84 | |||
| 32 | #endif /* __AA_PATH_H */ | 85 | #endif /* __AA_PATH_H */ |
diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 52275f040a5f..67bc96afe541 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h | |||
| @@ -18,6 +18,7 @@ | |||
| 18 | #include <linux/capability.h> | 18 | #include <linux/capability.h> |
| 19 | #include <linux/cred.h> | 19 | #include <linux/cred.h> |
| 20 | #include <linux/kref.h> | 20 | #include <linux/kref.h> |
| 21 | #include <linux/rhashtable.h> | ||
| 21 | #include <linux/sched.h> | 22 | #include <linux/sched.h> |
| 22 | #include <linux/slab.h> | 23 | #include <linux/slab.h> |
| 23 | #include <linux/socket.h> | 24 | #include <linux/socket.h> |
| @@ -27,8 +28,14 @@ | |||
| 27 | #include "capability.h" | 28 | #include "capability.h" |
| 28 | #include "domain.h" | 29 | #include "domain.h" |
| 29 | #include "file.h" | 30 | #include "file.h" |
| 31 | #include "lib.h" | ||
| 30 | #include "resource.h" | 32 | #include "resource.h" |
| 31 | 33 | ||
| 34 | |||
| 35 | struct aa_ns; | ||
| 36 | |||
| 37 | extern int unprivileged_userns_apparmor_policy; | ||
| 38 | |||
| 32 | extern const char *const aa_profile_mode_names[]; | 39 | extern const char *const aa_profile_mode_names[]; |
| 33 | #define APPARMOR_MODE_NAMES_MAX_INDEX 4 | 40 | #define APPARMOR_MODE_NAMES_MAX_INDEX 4 |
| 34 | 41 | ||
| @@ -42,7 +49,7 @@ extern const char *const aa_profile_mode_names[]; | |||
| 42 | 49 | ||
| 43 | #define PROFILE_IS_HAT(_profile) ((_profile)->flags & PFLAG_HAT) | 50 | #define PROFILE_IS_HAT(_profile) ((_profile)->flags & PFLAG_HAT) |
| 44 | 51 | ||
| 45 | #define PROFILE_INVALID(_profile) ((_profile)->flags & PFLAG_INVALID) | 52 | #define profile_is_stale(_profile) ((_profile)->flags & PFLAG_STALE) |
| 46 | 53 | ||
| 47 | #define on_list_rcu(X) (!list_empty(X) && (X)->prev != LIST_POISON2) | 54 | #define on_list_rcu(X) (!list_empty(X) && (X)->prev != LIST_POISON2) |
| 48 | 55 | ||
| @@ -67,7 +74,7 @@ enum profile_flags { | |||
| 67 | PFLAG_USER_DEFINED = 0x20, /* user based profile - lower privs */ | 74 | PFLAG_USER_DEFINED = 0x20, /* user based profile - lower privs */ |
| 68 | PFLAG_NO_LIST_REF = 0x40, /* list doesn't keep profile ref */ | 75 | PFLAG_NO_LIST_REF = 0x40, /* list doesn't keep profile ref */ |
| 69 | PFLAG_OLD_NULL_TRANS = 0x100, /* use // as the null transition */ | 76 | PFLAG_OLD_NULL_TRANS = 0x100, /* use // as the null transition */ |
| 70 | PFLAG_INVALID = 0x200, /* profile replaced/removed */ | 77 | PFLAG_STALE = 0x200, /* profile replaced/removed */ |
| 71 | PFLAG_NS_COUNT = 0x400, /* carries NS ref count */ | 78 | PFLAG_NS_COUNT = 0x400, /* carries NS ref count */ |
| 72 | 79 | ||
| 73 | /* These flags must correspond with PATH_flags */ | 80 | /* These flags must correspond with PATH_flags */ |
| @@ -76,70 +83,6 @@ enum profile_flags { | |||
| 76 | 83 | ||
| 77 | struct aa_profile; | 84 | struct aa_profile; |
| 78 | 85 | ||
| 79 | /* struct aa_policy - common part of both namespaces and profiles | ||
| 80 | * @name: name of the object | ||
| 81 | * @hname - The hierarchical name | ||
| 82 | * @list: list policy object is on | ||
| 83 | * @profiles: head of the profiles list contained in the object | ||
| 84 | */ | ||
| 85 | struct aa_policy { | ||
| 86 | char *name; | ||
| 87 | char *hname; | ||
| 88 | struct list_head list; | ||
| 89 | struct list_head profiles; | ||
| 90 | }; | ||
| 91 | |||
| 92 | /* struct aa_ns_acct - accounting of profiles in namespace | ||
| 93 | * @max_size: maximum space allowed for all profiles in namespace | ||
| 94 | * @max_count: maximum number of profiles that can be in this namespace | ||
| 95 | * @size: current size of profiles | ||
| 96 | * @count: current count of profiles (includes null profiles) | ||
| 97 | */ | ||
| 98 | struct aa_ns_acct { | ||
| 99 | int max_size; | ||
| 100 | int max_count; | ||
| 101 | int size; | ||
| 102 | int count; | ||
| 103 | }; | ||
| 104 | |||
| 105 | /* struct aa_namespace - namespace for a set of profiles | ||
| 106 | * @base: common policy | ||
| 107 | * @parent: parent of namespace | ||
| 108 | * @lock: lock for modifying the object | ||
| 109 | * @acct: accounting for the namespace | ||
| 110 | * @unconfined: special unconfined profile for the namespace | ||
| 111 | * @sub_ns: list of namespaces under the current namespace. | ||
| 112 | * @uniq_null: uniq value used for null learning profiles | ||
| 113 | * @uniq_id: a unique id count for the profiles in the namespace | ||
| 114 | * @dents: dentries for the namespaces file entries in apparmorfs | ||
| 115 | * | ||
| 116 | * An aa_namespace defines the set profiles that are searched to determine | ||
| 117 | * which profile to attach to a task. Profiles can not be shared between | ||
| 118 | * aa_namespaces and profile names within a namespace are guaranteed to be | ||
| 119 | * unique. When profiles in separate namespaces have the same name they | ||
| 120 | * are NOT considered to be equivalent. | ||
| 121 | * | ||
| 122 | * Namespaces are hierarchical and only namespaces and profiles below the | ||
| 123 | * current namespace are visible. | ||
| 124 | * | ||
| 125 | * Namespace names must be unique and can not contain the characters :/\0 | ||
| 126 | * | ||
| 127 | * FIXME TODO: add vserver support of namespaces (can it all be done in | ||
| 128 | * userspace?) | ||
| 129 | */ | ||
| 130 | struct aa_namespace { | ||
| 131 | struct aa_policy base; | ||
| 132 | struct aa_namespace *parent; | ||
| 133 | struct mutex lock; | ||
| 134 | struct aa_ns_acct acct; | ||
| 135 | struct aa_profile *unconfined; | ||
| 136 | struct list_head sub_ns; | ||
| 137 | atomic_t uniq_null; | ||
| 138 | long uniq_id; | ||
| 139 | |||
| 140 | struct dentry *dents[AAFS_NS_SIZEOF]; | ||
| 141 | }; | ||
| 142 | |||
| 143 | /* struct aa_policydb - match engine for a policy | 86 | /* struct aa_policydb - match engine for a policy |
| 144 | * dfa: dfa pattern match | 87 | * dfa: dfa pattern match |
| 145 | * start: set of start states for the different classes of data | 88 | * start: set of start states for the different classes of data |
| @@ -151,11 +94,24 @@ struct aa_policydb { | |||
| 151 | 94 | ||
| 152 | }; | 95 | }; |
| 153 | 96 | ||
| 154 | struct aa_replacedby { | 97 | struct aa_proxy { |
| 155 | struct kref count; | 98 | struct kref count; |
| 156 | struct aa_profile __rcu *profile; | 99 | struct aa_profile __rcu *profile; |
| 157 | }; | 100 | }; |
| 158 | 101 | ||
| 102 | /* struct aa_data - generic data structure | ||
| 103 | * key: name for retrieving this data | ||
| 104 | * size: size of data in bytes | ||
| 105 | * data: binary data | ||
| 106 | * head: reserved for rhashtable | ||
| 107 | */ | ||
| 108 | struct aa_data { | ||
| 109 | char *key; | ||
| 110 | u32 size; | ||
| 111 | char *data; | ||
| 112 | struct rhash_head head; | ||
| 113 | }; | ||
| 114 | |||
| 159 | 115 | ||
| 160 | /* struct aa_profile - basic confinement data | 116 | /* struct aa_profile - basic confinement data |
| 161 | * @base - base components of the profile (name, refcount, lists, lock ...) | 117 | * @base - base components of the profile (name, refcount, lists, lock ...) |
| @@ -163,7 +119,7 @@ struct aa_replacedby { | |||
| 163 | * @rcu: rcu head used when removing from @list | 119 | * @rcu: rcu head used when removing from @list |
| 164 | * @parent: parent of profile | 120 | * @parent: parent of profile |
| 165 | * @ns: namespace the profile is in | 121 | * @ns: namespace the profile is in |
| 166 | * @replacedby: is set to the profile that replaced this profile | 122 | * @proxy: is set to the profile that replaced this profile |
| 167 | * @rename: optional profile name that this profile renamed | 123 | * @rename: optional profile name that this profile renamed |
| 168 | * @attach: human readable attachment string | 124 | * @attach: human readable attachment string |
| 169 | * @xmatch: optional extended matching for unconfined executables names | 125 | * @xmatch: optional extended matching for unconfined executables names |
| @@ -180,13 +136,14 @@ struct aa_replacedby { | |||
| 180 | * | 136 | * |
| 181 | * @dents: dentries for the profiles file entries in apparmorfs | 137 | * @dents: dentries for the profiles file entries in apparmorfs |
| 182 | * @dirname: name of the profile dir in apparmorfs | 138 | * @dirname: name of the profile dir in apparmorfs |
| 139 | * @data: hashtable for free-form policy aa_data | ||
| 183 | * | 140 | * |
| 184 | * The AppArmor profile contains the basic confinement data. Each profile | 141 | * The AppArmor profile contains the basic confinement data. Each profile |
| 185 | * has a name, and exists in a namespace. The @name and @exec_match are | 142 | * has a name, and exists in a namespace. The @name and @exec_match are |
| 186 | * used to determine profile attachment against unconfined tasks. All other | 143 | * used to determine profile attachment against unconfined tasks. All other |
| 187 | * attachments are determined by profile X transition rules. | 144 | * attachments are determined by profile X transition rules. |
| 188 | * | 145 | * |
| 189 | * The @replacedby struct is write protected by the profile lock. | 146 | * The @proxy struct is write protected by the profile lock. |
| 190 | * | 147 | * |
| 191 | * Profiles have a hierarchy where hats and children profiles keep | 148 | * Profiles have a hierarchy where hats and children profiles keep |
| 192 | * a reference to their parent. | 149 | * a reference to their parent. |
| @@ -201,8 +158,8 @@ struct aa_profile { | |||
| 201 | struct rcu_head rcu; | 158 | struct rcu_head rcu; |
| 202 | struct aa_profile __rcu *parent; | 159 | struct aa_profile __rcu *parent; |
| 203 | 160 | ||
| 204 | struct aa_namespace *ns; | 161 | struct aa_ns *ns; |
| 205 | struct aa_replacedby *replacedby; | 162 | struct aa_proxy *proxy; |
| 206 | const char *rename; | 163 | const char *rename; |
| 207 | 164 | ||
| 208 | const char *attach; | 165 | const char *attach; |
| @@ -219,37 +176,39 @@ struct aa_profile { | |||
| 219 | struct aa_caps caps; | 176 | struct aa_caps caps; |
| 220 | struct aa_rlimit rlimits; | 177 | struct aa_rlimit rlimits; |
| 221 | 178 | ||
| 179 | struct aa_loaddata *rawdata; | ||
| 222 | unsigned char *hash; | 180 | unsigned char *hash; |
| 223 | char *dirname; | 181 | char *dirname; |
| 224 | struct dentry *dents[AAFS_PROF_SIZEOF]; | 182 | struct dentry *dents[AAFS_PROF_SIZEOF]; |
| 183 | struct rhashtable *data; | ||
| 225 | }; | 184 | }; |
| 226 | 185 | ||
| 227 | extern struct aa_namespace *root_ns; | ||
| 228 | extern enum profile_mode aa_g_profile_mode; | 186 | extern enum profile_mode aa_g_profile_mode; |
| 229 | 187 | ||
| 230 | void aa_add_profile(struct aa_policy *common, struct aa_profile *profile); | 188 | void __aa_update_proxy(struct aa_profile *orig, struct aa_profile *new); |
| 231 | |||
| 232 | bool aa_ns_visible(struct aa_namespace *curr, struct aa_namespace *view); | ||
| 233 | const char *aa_ns_name(struct aa_namespace *parent, struct aa_namespace *child); | ||
| 234 | int aa_alloc_root_ns(void); | ||
| 235 | void aa_free_root_ns(void); | ||
| 236 | void aa_free_namespace_kref(struct kref *kref); | ||
| 237 | 189 | ||
| 238 | struct aa_namespace *aa_find_namespace(struct aa_namespace *root, | 190 | void aa_add_profile(struct aa_policy *common, struct aa_profile *profile); |
| 239 | const char *name); | ||
| 240 | 191 | ||
| 241 | 192 | ||
| 242 | void aa_free_replacedby_kref(struct kref *kref); | 193 | void aa_free_proxy_kref(struct kref *kref); |
| 243 | struct aa_profile *aa_alloc_profile(const char *name); | 194 | struct aa_profile *aa_alloc_profile(const char *name, gfp_t gfp); |
| 244 | struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat); | 195 | struct aa_profile *aa_new_null_profile(struct aa_profile *parent, bool hat, |
| 196 | const char *base, gfp_t gfp); | ||
| 245 | void aa_free_profile(struct aa_profile *profile); | 197 | void aa_free_profile(struct aa_profile *profile); |
| 246 | void aa_free_profile_kref(struct kref *kref); | 198 | void aa_free_profile_kref(struct kref *kref); |
| 247 | struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name); | 199 | struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name); |
| 248 | struct aa_profile *aa_lookup_profile(struct aa_namespace *ns, const char *name); | 200 | struct aa_profile *aa_lookupn_profile(struct aa_ns *ns, const char *hname, |
| 249 | struct aa_profile *aa_match_profile(struct aa_namespace *ns, const char *name); | 201 | size_t n); |
| 250 | 202 | struct aa_profile *aa_lookup_profile(struct aa_ns *ns, const char *name); | |
| 251 | ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace); | 203 | struct aa_profile *aa_fqlookupn_profile(struct aa_profile *base, |
| 252 | ssize_t aa_remove_profiles(char *name, size_t size); | 204 | const char *fqname, size_t n); |
| 205 | struct aa_profile *aa_match_profile(struct aa_ns *ns, const char *name); | ||
| 206 | |||
| 207 | ssize_t aa_replace_profiles(struct aa_ns *view, struct aa_profile *profile, | ||
| 208 | bool noreplace, struct aa_loaddata *udata); | ||
| 209 | ssize_t aa_remove_profiles(struct aa_ns *view, struct aa_profile *profile, | ||
| 210 | char *name, size_t size); | ||
| 211 | void __aa_profile_list_release(struct list_head *head); | ||
| 253 | 212 | ||
| 254 | #define PROF_ADD 1 | 213 | #define PROF_ADD 1 |
| 255 | #define PROF_REPLACE 0 | 214 | #define PROF_REPLACE 0 |
| @@ -257,12 +216,6 @@ ssize_t aa_remove_profiles(char *name, size_t size); | |||
| 257 | #define unconfined(X) ((X)->mode == APPARMOR_UNCONFINED) | 216 | #define unconfined(X) ((X)->mode == APPARMOR_UNCONFINED) |
| 258 | 217 | ||
| 259 | 218 | ||
| 260 | static inline struct aa_profile *aa_deref_parent(struct aa_profile *p) | ||
| 261 | { | ||
| 262 | return rcu_dereference_protected(p->parent, | ||
| 263 | mutex_is_locked(&p->ns->lock)); | ||
| 264 | } | ||
| 265 | |||
| 266 | /** | 219 | /** |
| 267 | * aa_get_profile - increment refcount on profile @p | 220 | * aa_get_profile - increment refcount on profile @p |
| 268 | * @p: profile (MAYBE NULL) | 221 | * @p: profile (MAYBE NULL) |
| @@ -287,7 +240,7 @@ static inline struct aa_profile *aa_get_profile(struct aa_profile *p) | |||
| 287 | */ | 240 | */ |
| 288 | static inline struct aa_profile *aa_get_profile_not0(struct aa_profile *p) | 241 | static inline struct aa_profile *aa_get_profile_not0(struct aa_profile *p) |
| 289 | { | 242 | { |
| 290 | if (p && kref_get_not0(&p->count)) | 243 | if (p && kref_get_unless_zero(&p->count)) |
| 291 | return p; | 244 | return p; |
| 292 | 245 | ||
| 293 | return NULL; | 246 | return NULL; |
| @@ -307,7 +260,7 @@ static inline struct aa_profile *aa_get_profile_rcu(struct aa_profile __rcu **p) | |||
| 307 | rcu_read_lock(); | 260 | rcu_read_lock(); |
| 308 | do { | 261 | do { |
| 309 | c = rcu_dereference(*p); | 262 | c = rcu_dereference(*p); |
| 310 | } while (c && !kref_get_not0(&c->count)); | 263 | } while (c && !kref_get_unless_zero(&c->count)); |
| 311 | rcu_read_unlock(); | 264 | rcu_read_unlock(); |
| 312 | 265 | ||
| 313 | return c; | 266 | return c; |
| @@ -326,8 +279,8 @@ static inline struct aa_profile *aa_get_newest_profile(struct aa_profile *p) | |||
| 326 | if (!p) | 279 | if (!p) |
| 327 | return NULL; | 280 | return NULL; |
| 328 | 281 | ||
| 329 | if (PROFILE_INVALID(p)) | 282 | if (profile_is_stale(p)) |
| 330 | return aa_get_profile_rcu(&p->replacedby->profile); | 283 | return aa_get_profile_rcu(&p->proxy->profile); |
| 331 | 284 | ||
| 332 | return aa_get_profile(p); | 285 | return aa_get_profile(p); |
| 333 | } | 286 | } |
| @@ -342,7 +295,7 @@ static inline void aa_put_profile(struct aa_profile *p) | |||
| 342 | kref_put(&p->count, aa_free_profile_kref); | 295 | kref_put(&p->count, aa_free_profile_kref); |
| 343 | } | 296 | } |
| 344 | 297 | ||
| 345 | static inline struct aa_replacedby *aa_get_replacedby(struct aa_replacedby *p) | 298 | static inline struct aa_proxy *aa_get_proxy(struct aa_proxy *p) |
| 346 | { | 299 | { |
| 347 | if (p) | 300 | if (p) |
| 348 | kref_get(&(p->count)); | 301 | kref_get(&(p->count)); |
| @@ -350,49 +303,10 @@ static inline struct aa_replacedby *aa_get_replacedby(struct aa_replacedby *p) | |||
| 350 | return p; | 303 | return p; |
| 351 | } | 304 | } |
| 352 | 305 | ||
| 353 | static inline void aa_put_replacedby(struct aa_replacedby *p) | 306 | static inline void aa_put_proxy(struct aa_proxy *p) |
| 354 | { | 307 | { |
| 355 | if (p) | 308 | if (p) |
| 356 | kref_put(&p->count, aa_free_replacedby_kref); | 309 | kref_put(&p->count, aa_free_proxy_kref); |
| 357 | } | ||
| 358 | |||
| 359 | /* requires profile list write lock held */ | ||
| 360 | static inline void __aa_update_replacedby(struct aa_profile *orig, | ||
| 361 | struct aa_profile *new) | ||
| 362 | { | ||
| 363 | struct aa_profile *tmp; | ||
| 364 | tmp = rcu_dereference_protected(orig->replacedby->profile, | ||
| 365 | mutex_is_locked(&orig->ns->lock)); | ||
| 366 | rcu_assign_pointer(orig->replacedby->profile, aa_get_profile(new)); | ||
| 367 | orig->flags |= PFLAG_INVALID; | ||
| 368 | aa_put_profile(tmp); | ||
| 369 | } | ||
| 370 | |||
| 371 | /** | ||
| 372 | * aa_get_namespace - increment references count on @ns | ||
| 373 | * @ns: namespace to increment reference count of (MAYBE NULL) | ||
| 374 | * | ||
| 375 | * Returns: pointer to @ns, if @ns is NULL returns NULL | ||
| 376 | * Requires: @ns must be held with valid refcount when called | ||
| 377 | */ | ||
| 378 | static inline struct aa_namespace *aa_get_namespace(struct aa_namespace *ns) | ||
| 379 | { | ||
| 380 | if (ns) | ||
| 381 | aa_get_profile(ns->unconfined); | ||
| 382 | |||
| 383 | return ns; | ||
| 384 | } | ||
| 385 | |||
| 386 | /** | ||
| 387 | * aa_put_namespace - decrement refcount on @ns | ||
| 388 | * @ns: namespace to put reference of | ||
| 389 | * | ||
| 390 | * Decrement reference count of @ns and if no longer in use free it | ||
| 391 | */ | ||
| 392 | static inline void aa_put_namespace(struct aa_namespace *ns) | ||
| 393 | { | ||
| 394 | if (ns) | ||
| 395 | aa_put_profile(ns->unconfined); | ||
| 396 | } | 310 | } |
| 397 | 311 | ||
| 398 | static inline int AUDIT_MODE(struct aa_profile *profile) | 312 | static inline int AUDIT_MODE(struct aa_profile *profile) |
| @@ -403,8 +317,9 @@ static inline int AUDIT_MODE(struct aa_profile *profile) | |||
| 403 | return profile->audit; | 317 | return profile->audit; |
| 404 | } | 318 | } |
| 405 | 319 | ||
| 406 | bool policy_view_capable(void); | 320 | bool policy_view_capable(struct aa_ns *ns); |
| 407 | bool policy_admin_capable(void); | 321 | bool policy_admin_capable(struct aa_ns *ns); |
| 408 | bool aa_may_manage_policy(int op); | 322 | int aa_may_manage_policy(struct aa_profile *profile, struct aa_ns *ns, |
| 323 | const char *op); | ||
| 409 | 324 | ||
| 410 | #endif /* __AA_POLICY_H */ | 325 | #endif /* __AA_POLICY_H */ |
diff --git a/security/apparmor/include/policy_ns.h b/security/apparmor/include/policy_ns.h new file mode 100644 index 000000000000..89cffddd7e75 --- /dev/null +++ b/security/apparmor/include/policy_ns.h | |||
| @@ -0,0 +1,147 @@ | |||
| 1 | /* | ||
| 2 | * AppArmor security module | ||
| 3 | * | ||
| 4 | * This file contains AppArmor policy definitions. | ||
| 5 | * | ||
| 6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
| 7 | * Copyright 2009-2017 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_NAMESPACE_H | ||
| 16 | #define __AA_NAMESPACE_H | ||
| 17 | |||
| 18 | #include <linux/kref.h> | ||
| 19 | |||
| 20 | #include "apparmor.h" | ||
| 21 | #include "apparmorfs.h" | ||
| 22 | #include "policy.h" | ||
| 23 | |||
| 24 | |||
| 25 | /* struct aa_ns_acct - accounting of profiles in namespace | ||
| 26 | * @max_size: maximum space allowed for all profiles in namespace | ||
| 27 | * @max_count: maximum number of profiles that can be in this namespace | ||
| 28 | * @size: current size of profiles | ||
| 29 | * @count: current count of profiles (includes null profiles) | ||
| 30 | */ | ||
| 31 | struct aa_ns_acct { | ||
| 32 | int max_size; | ||
| 33 | int max_count; | ||
| 34 | int size; | ||
| 35 | int count; | ||
| 36 | }; | ||
| 37 | |||
| 38 | /* struct aa_ns - namespace for a set of profiles | ||
| 39 | * @base: common policy | ||
| 40 | * @parent: parent of namespace | ||
| 41 | * @lock: lock for modifying the object | ||
| 42 | * @acct: accounting for the namespace | ||
| 43 | * @unconfined: special unconfined profile for the namespace | ||
| 44 | * @sub_ns: list of namespaces under the current namespace. | ||
| 45 | * @uniq_null: uniq value used for null learning profiles | ||
| 46 | * @uniq_id: a unique id count for the profiles in the namespace | ||
| 47 | * @level: level of ns within the tree hierarchy | ||
| 48 | * @dents: dentries for the namespaces file entries in apparmorfs | ||
| 49 | * | ||
| 50 | * An aa_ns defines the set profiles that are searched to determine which | ||
| 51 | * profile to attach to a task. Profiles can not be shared between aa_ns | ||
| 52 | * and profile names within a namespace are guaranteed to be unique. When | ||
| 53 | * profiles in separate namespaces have the same name they are NOT considered | ||
| 54 | * to be equivalent. | ||
| 55 | * | ||
| 56 | * Namespaces are hierarchical and only namespaces and profiles below the | ||
| 57 | * current namespace are visible. | ||
| 58 | * | ||
| 59 | * Namespace names must be unique and can not contain the characters :/\0 | ||
| 60 | */ | ||
| 61 | struct aa_ns { | ||
| 62 | struct aa_policy base; | ||
| 63 | struct aa_ns *parent; | ||
| 64 | struct mutex lock; | ||
| 65 | struct aa_ns_acct acct; | ||
| 66 | struct aa_profile *unconfined; | ||
| 67 | struct list_head sub_ns; | ||
| 68 | atomic_t uniq_null; | ||
| 69 | long uniq_id; | ||
| 70 | int level; | ||
| 71 | |||
| 72 | struct dentry *dents[AAFS_NS_SIZEOF]; | ||
| 73 | }; | ||
| 74 | |||
| 75 | extern struct aa_ns *root_ns; | ||
| 76 | |||
| 77 | extern const char *aa_hidden_ns_name; | ||
| 78 | |||
| 79 | bool aa_ns_visible(struct aa_ns *curr, struct aa_ns *view, bool subns); | ||
| 80 | const char *aa_ns_name(struct aa_ns *parent, struct aa_ns *child, bool subns); | ||
| 81 | void aa_free_ns(struct aa_ns *ns); | ||
| 82 | int aa_alloc_root_ns(void); | ||
| 83 | void aa_free_root_ns(void); | ||
| 84 | void aa_free_ns_kref(struct kref *kref); | ||
| 85 | |||
| 86 | struct aa_ns *aa_find_ns(struct aa_ns *root, const char *name); | ||
| 87 | struct aa_ns *aa_findn_ns(struct aa_ns *root, const char *name, size_t n); | ||
| 88 | struct aa_ns *__aa_find_or_create_ns(struct aa_ns *parent, const char *name, | ||
| 89 | struct dentry *dir); | ||
| 90 | struct aa_ns *aa_prepare_ns(struct aa_ns *root, const char *name); | ||
| 91 | void __aa_remove_ns(struct aa_ns *ns); | ||
| 92 | |||
| 93 | static inline struct aa_profile *aa_deref_parent(struct aa_profile *p) | ||
| 94 | { | ||
| 95 | return rcu_dereference_protected(p->parent, | ||
| 96 | mutex_is_locked(&p->ns->lock)); | ||
| 97 | } | ||
| 98 | |||
| 99 | /** | ||
| 100 | * aa_get_ns - increment references count on @ns | ||
| 101 | * @ns: namespace to increment reference count of (MAYBE NULL) | ||
| 102 | * | ||
| 103 | * Returns: pointer to @ns, if @ns is NULL returns NULL | ||
| 104 | * Requires: @ns must be held with valid refcount when called | ||
| 105 | */ | ||
| 106 | static inline struct aa_ns *aa_get_ns(struct aa_ns *ns) | ||
| 107 | { | ||
| 108 | if (ns) | ||
| 109 | aa_get_profile(ns->unconfined); | ||
| 110 | |||
| 111 | return ns; | ||
| 112 | } | ||
| 113 | |||
| 114 | /** | ||
| 115 | * aa_put_ns - decrement refcount on @ns | ||
| 116 | * @ns: namespace to put reference of | ||
| 117 | * | ||
| 118 | * Decrement reference count of @ns and if no longer in use free it | ||
| 119 | */ | ||
| 120 | static inline void aa_put_ns(struct aa_ns *ns) | ||
| 121 | { | ||
| 122 | if (ns) | ||
| 123 | aa_put_profile(ns->unconfined); | ||
| 124 | } | ||
| 125 | |||
| 126 | /** | ||
| 127 | * __aa_findn_ns - find a namespace on a list by @name | ||
| 128 | * @head: list to search for namespace on (NOT NULL) | ||
| 129 | * @name: name of namespace to look for (NOT NULL) | ||
| 130 | * @n: length of @name | ||
| 131 | * Returns: unrefcounted namespace | ||
| 132 | * | ||
| 133 | * Requires: rcu_read_lock be held | ||
| 134 | */ | ||
| 135 | static inline struct aa_ns *__aa_findn_ns(struct list_head *head, | ||
| 136 | const char *name, size_t n) | ||
| 137 | { | ||
| 138 | return (struct aa_ns *)__policy_strn_find(head, name, n); | ||
| 139 | } | ||
| 140 | |||
| 141 | static inline struct aa_ns *__aa_find_ns(struct list_head *head, | ||
| 142 | const char *name) | ||
| 143 | { | ||
| 144 | return __aa_findn_ns(head, name, strlen(name)); | ||
| 145 | } | ||
| 146 | |||
| 147 | #endif /* AA_NAMESPACE_H */ | ||
diff --git a/security/apparmor/include/policy_unpack.h b/security/apparmor/include/policy_unpack.h index c214fb88b1bc..4c1319eebc42 100644 --- a/security/apparmor/include/policy_unpack.h +++ b/security/apparmor/include/policy_unpack.h | |||
| @@ -16,12 +16,14 @@ | |||
| 16 | #define __POLICY_INTERFACE_H | 16 | #define __POLICY_INTERFACE_H |
| 17 | 17 | ||
| 18 | #include <linux/list.h> | 18 | #include <linux/list.h> |
| 19 | #include <linux/kref.h> | ||
| 19 | 20 | ||
| 20 | struct aa_load_ent { | 21 | struct aa_load_ent { |
| 21 | struct list_head list; | 22 | struct list_head list; |
| 22 | struct aa_profile *new; | 23 | struct aa_profile *new; |
| 23 | struct aa_profile *old; | 24 | struct aa_profile *old; |
| 24 | struct aa_profile *rename; | 25 | struct aa_profile *rename; |
| 26 | const char *ns_name; | ||
| 25 | }; | 27 | }; |
| 26 | 28 | ||
| 27 | void aa_load_ent_free(struct aa_load_ent *ent); | 29 | void aa_load_ent_free(struct aa_load_ent *ent); |
| @@ -34,6 +36,30 @@ struct aa_load_ent *aa_load_ent_alloc(void); | |||
| 34 | #define PACKED_MODE_KILL 2 | 36 | #define PACKED_MODE_KILL 2 |
| 35 | #define PACKED_MODE_UNCONFINED 3 | 37 | #define PACKED_MODE_UNCONFINED 3 |
| 36 | 38 | ||
| 37 | int aa_unpack(void *udata, size_t size, struct list_head *lh, const char **ns); | 39 | /* struct aa_loaddata - buffer of policy load data set */ |
| 40 | struct aa_loaddata { | ||
| 41 | struct kref count; | ||
| 42 | size_t size; | ||
| 43 | int abi; | ||
| 44 | unsigned char *hash; | ||
| 45 | char data[]; | ||
| 46 | }; | ||
| 47 | |||
| 48 | int aa_unpack(struct aa_loaddata *udata, struct list_head *lh, const char **ns); | ||
| 49 | |||
| 50 | static inline struct aa_loaddata * | ||
| 51 | aa_get_loaddata(struct aa_loaddata *data) | ||
| 52 | { | ||
| 53 | if (data) | ||
| 54 | kref_get(&(data->count)); | ||
| 55 | return data; | ||
| 56 | } | ||
| 57 | |||
| 58 | void aa_loaddata_kref(struct kref *kref); | ||
| 59 | static inline void aa_put_loaddata(struct aa_loaddata *data) | ||
| 60 | { | ||
| 61 | if (data) | ||
| 62 | kref_put(&data->count, aa_loaddata_kref); | ||
| 63 | } | ||
| 38 | 64 | ||
| 39 | #endif /* __POLICY_INTERFACE_H */ | 65 | #endif /* __POLICY_INTERFACE_H */ |
diff --git a/security/apparmor/include/sid.h b/security/apparmor/include/secid.h index 513ca0e48965..95ed86a0f1e2 100644 --- a/security/apparmor/include/sid.h +++ b/security/apparmor/include/secid.h | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * AppArmor security module | 2 | * AppArmor security module |
| 3 | * | 3 | * |
| 4 | * This file contains AppArmor security identifier (sid) definitions | 4 | * This file contains AppArmor security identifier (secid) definitions |
| 5 | * | 5 | * |
| 6 | * Copyright 2009-2010 Canonical Ltd. | 6 | * Copyright 2009-2010 Canonical Ltd. |
| 7 | * | 7 | * |
| @@ -11,16 +11,16 @@ | |||
| 11 | * License. | 11 | * License. |
| 12 | */ | 12 | */ |
| 13 | 13 | ||
| 14 | #ifndef __AA_SID_H | 14 | #ifndef __AA_SECID_H |
| 15 | #define __AA_SID_H | 15 | #define __AA_SECID_H |
| 16 | 16 | ||
| 17 | #include <linux/types.h> | 17 | #include <linux/types.h> |
| 18 | 18 | ||
| 19 | /* sid value that will not be allocated */ | 19 | /* secid value that will not be allocated */ |
| 20 | #define AA_SID_INVALID 0 | 20 | #define AA_SECID_INVALID 0 |
| 21 | #define AA_SID_ALLOC AA_SID_INVALID | 21 | #define AA_SECID_ALLOC AA_SECID_INVALID |
| 22 | 22 | ||
| 23 | u32 aa_alloc_sid(void); | 23 | u32 aa_alloc_secid(void); |
| 24 | void aa_free_sid(u32 sid); | 24 | void aa_free_secid(u32 secid); |
| 25 | 25 | ||
| 26 | #endif /* __AA_SID_H */ | 26 | #endif /* __AA_SECID_H */ |
diff --git a/security/apparmor/ipc.c b/security/apparmor/ipc.c index 777ac1c47253..edac790923c3 100644 --- a/security/apparmor/ipc.c +++ b/security/apparmor/ipc.c | |||
| @@ -25,8 +25,8 @@ | |||
| 25 | static void audit_cb(struct audit_buffer *ab, void *va) | 25 | static void audit_cb(struct audit_buffer *ab, void *va) |
| 26 | { | 26 | { |
| 27 | struct common_audit_data *sa = va; | 27 | struct common_audit_data *sa = va; |
| 28 | audit_log_format(ab, " target="); | 28 | audit_log_format(ab, " peer="); |
| 29 | audit_log_untrustedstring(ab, sa->aad->target); | 29 | audit_log_untrustedstring(ab, aad(sa)->peer->base.hname); |
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | /** | 32 | /** |
| @@ -40,16 +40,12 @@ static void audit_cb(struct audit_buffer *ab, void *va) | |||
| 40 | static int aa_audit_ptrace(struct aa_profile *profile, | 40 | static int aa_audit_ptrace(struct aa_profile *profile, |
| 41 | struct aa_profile *target, int error) | 41 | struct aa_profile *target, int error) |
| 42 | { | 42 | { |
| 43 | struct common_audit_data sa; | 43 | DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_PTRACE); |
| 44 | struct apparmor_audit_data aad = {0,}; | ||
| 45 | sa.type = LSM_AUDIT_DATA_NONE; | ||
| 46 | sa.aad = &aad; | ||
| 47 | aad.op = OP_PTRACE; | ||
| 48 | aad.target = target; | ||
| 49 | aad.error = error; | ||
| 50 | 44 | ||
| 51 | return aa_audit(AUDIT_APPARMOR_AUTO, profile, GFP_ATOMIC, &sa, | 45 | aad(&sa)->peer = target; |
| 52 | audit_cb); | 46 | aad(&sa)->error = error; |
| 47 | |||
| 48 | return aa_audit(AUDIT_APPARMOR_AUTO, profile, &sa, audit_cb); | ||
| 53 | } | 49 | } |
| 54 | 50 | ||
| 55 | /** | 51 | /** |
diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c index c1827e068454..66475bda6f72 100644 --- a/security/apparmor/lib.c +++ b/security/apparmor/lib.c | |||
| @@ -12,6 +12,7 @@ | |||
| 12 | * License. | 12 | * License. |
| 13 | */ | 13 | */ |
| 14 | 14 | ||
| 15 | #include <linux/ctype.h> | ||
| 15 | #include <linux/mm.h> | 16 | #include <linux/mm.h> |
| 16 | #include <linux/slab.h> | 17 | #include <linux/slab.h> |
| 17 | #include <linux/string.h> | 18 | #include <linux/string.h> |
| @@ -19,7 +20,8 @@ | |||
| 19 | 20 | ||
| 20 | #include "include/audit.h" | 21 | #include "include/audit.h" |
| 21 | #include "include/apparmor.h" | 22 | #include "include/apparmor.h" |
| 22 | 23 | #include "include/lib.h" | |
| 24 | #include "include/policy.h" | ||
| 23 | 25 | ||
| 24 | /** | 26 | /** |
| 25 | * aa_split_fqname - split a fqname into a profile and namespace name | 27 | * aa_split_fqname - split a fqname into a profile and namespace name |
| @@ -60,17 +62,67 @@ char *aa_split_fqname(char *fqname, char **ns_name) | |||
| 60 | } | 62 | } |
| 61 | 63 | ||
| 62 | /** | 64 | /** |
| 65 | * skipn_spaces - Removes leading whitespace from @str. | ||
| 66 | * @str: The string to be stripped. | ||
| 67 | * | ||
| 68 | * Returns a pointer to the first non-whitespace character in @str. | ||
| 69 | * if all whitespace will return NULL | ||
| 70 | */ | ||
| 71 | |||
| 72 | static const char *skipn_spaces(const char *str, size_t n) | ||
| 73 | { | ||
| 74 | for (; n && isspace(*str); --n) | ||
| 75 | ++str; | ||
| 76 | if (n) | ||
| 77 | return (char *)str; | ||
| 78 | return NULL; | ||
| 79 | } | ||
| 80 | |||
| 81 | const char *aa_splitn_fqname(const char *fqname, size_t n, const char **ns_name, | ||
| 82 | size_t *ns_len) | ||
| 83 | { | ||
| 84 | const char *end = fqname + n; | ||
| 85 | const char *name = skipn_spaces(fqname, n); | ||
| 86 | |||
| 87 | if (!name) | ||
| 88 | return NULL; | ||
| 89 | *ns_name = NULL; | ||
| 90 | *ns_len = 0; | ||
| 91 | if (name[0] == ':') { | ||
| 92 | char *split = strnchr(&name[1], end - &name[1], ':'); | ||
| 93 | *ns_name = skipn_spaces(&name[1], end - &name[1]); | ||
| 94 | if (!*ns_name) | ||
| 95 | return NULL; | ||
| 96 | if (split) { | ||
| 97 | *ns_len = split - *ns_name; | ||
| 98 | if (*ns_len == 0) | ||
| 99 | *ns_name = NULL; | ||
| 100 | split++; | ||
| 101 | if (end - split > 1 && strncmp(split, "//", 2) == 0) | ||
| 102 | split += 2; | ||
| 103 | name = skipn_spaces(split, end - split); | ||
| 104 | } else { | ||
| 105 | /* a ns name without a following profile is allowed */ | ||
| 106 | name = NULL; | ||
| 107 | *ns_len = end - *ns_name; | ||
| 108 | } | ||
| 109 | } | ||
| 110 | if (name && *name == 0) | ||
| 111 | name = NULL; | ||
| 112 | |||
| 113 | return name; | ||
| 114 | } | ||
| 115 | |||
| 116 | /** | ||
| 63 | * aa_info_message - log a none profile related status message | 117 | * aa_info_message - log a none profile related status message |
| 64 | * @str: message to log | 118 | * @str: message to log |
| 65 | */ | 119 | */ |
| 66 | void aa_info_message(const char *str) | 120 | void aa_info_message(const char *str) |
| 67 | { | 121 | { |
| 68 | if (audit_enabled) { | 122 | if (audit_enabled) { |
| 69 | struct common_audit_data sa; | 123 | DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, NULL); |
| 70 | struct apparmor_audit_data aad = {0,}; | 124 | |
| 71 | sa.type = LSM_AUDIT_DATA_NONE; | 125 | aad(&sa)->info = str; |
| 72 | sa.aad = &aad; | ||
| 73 | aad.info = str; | ||
| 74 | aa_audit_msg(AUDIT_APPARMOR_STATUS, &sa, NULL); | 126 | aa_audit_msg(AUDIT_APPARMOR_STATUS, &sa, NULL); |
| 75 | } | 127 | } |
| 76 | printk(KERN_INFO "AppArmor: %s\n", str); | 128 | printk(KERN_INFO "AppArmor: %s\n", str); |
| @@ -95,7 +147,8 @@ void *__aa_kvmalloc(size_t size, gfp_t flags) | |||
| 95 | 147 | ||
| 96 | /* do not attempt kmalloc if we need more than 16 pages at once */ | 148 | /* do not attempt kmalloc if we need more than 16 pages at once */ |
| 97 | if (size <= (16*PAGE_SIZE)) | 149 | if (size <= (16*PAGE_SIZE)) |
| 98 | buffer = kmalloc(size, flags | GFP_NOIO | __GFP_NOWARN); | 150 | buffer = kmalloc(size, flags | GFP_KERNEL | __GFP_NORETRY | |
| 151 | __GFP_NOWARN); | ||
| 99 | if (!buffer) { | 152 | if (!buffer) { |
| 100 | if (flags & __GFP_ZERO) | 153 | if (flags & __GFP_ZERO) |
| 101 | buffer = vzalloc(size); | 154 | buffer = vzalloc(size); |
| @@ -104,3 +157,47 @@ void *__aa_kvmalloc(size_t size, gfp_t flags) | |||
| 104 | } | 157 | } |
| 105 | return buffer; | 158 | return buffer; |
| 106 | } | 159 | } |
| 160 | |||
| 161 | /** | ||
| 162 | * aa_policy_init - initialize a policy structure | ||
| 163 | * @policy: policy to initialize (NOT NULL) | ||
| 164 | * @prefix: prefix name if any is required. (MAYBE NULL) | ||
| 165 | * @name: name of the policy, init will make a copy of it (NOT NULL) | ||
| 166 | * | ||
| 167 | * Note: this fn creates a copy of strings passed in | ||
| 168 | * | ||
| 169 | * Returns: true if policy init successful | ||
| 170 | */ | ||
| 171 | bool aa_policy_init(struct aa_policy *policy, const char *prefix, | ||
| 172 | const char *name, gfp_t gfp) | ||
| 173 | { | ||
| 174 | /* freed by policy_free */ | ||
| 175 | if (prefix) { | ||
| 176 | policy->hname = kmalloc(strlen(prefix) + strlen(name) + 3, | ||
| 177 | gfp); | ||
| 178 | if (policy->hname) | ||
| 179 | sprintf((char *)policy->hname, "%s//%s", prefix, name); | ||
| 180 | } else | ||
| 181 | policy->hname = kstrdup(name, gfp); | ||
| 182 | if (!policy->hname) | ||
| 183 | return 0; | ||
| 184 | /* base.name is a substring of fqname */ | ||
| 185 | policy->name = basename(policy->hname); | ||
| 186 | INIT_LIST_HEAD(&policy->list); | ||
| 187 | INIT_LIST_HEAD(&policy->profiles); | ||
| 188 | |||
| 189 | return 1; | ||
| 190 | } | ||
| 191 | |||
| 192 | /** | ||
| 193 | * aa_policy_destroy - free the elements referenced by @policy | ||
| 194 | * @policy: policy that is to have its elements freed (NOT NULL) | ||
| 195 | */ | ||
| 196 | void aa_policy_destroy(struct aa_policy *policy) | ||
| 197 | { | ||
| 198 | AA_BUG(on_list_rcu(&policy->profiles)); | ||
| 199 | AA_BUG(on_list_rcu(&policy->list)); | ||
| 200 | |||
| 201 | /* don't free name as its a subset of hname */ | ||
| 202 | kzfree(policy->hname); | ||
| 203 | } | ||
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 41b8cb115801..709eacd23909 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c | |||
| @@ -23,6 +23,7 @@ | |||
| 23 | #include <linux/sysctl.h> | 23 | #include <linux/sysctl.h> |
| 24 | #include <linux/audit.h> | 24 | #include <linux/audit.h> |
| 25 | #include <linux/user_namespace.h> | 25 | #include <linux/user_namespace.h> |
| 26 | #include <linux/kmemleak.h> | ||
| 26 | #include <net/sock.h> | 27 | #include <net/sock.h> |
| 27 | 28 | ||
| 28 | #include "include/apparmor.h" | 29 | #include "include/apparmor.h" |
| @@ -34,22 +35,26 @@ | |||
| 34 | #include "include/ipc.h" | 35 | #include "include/ipc.h" |
| 35 | #include "include/path.h" | 36 | #include "include/path.h" |
| 36 | #include "include/policy.h" | 37 | #include "include/policy.h" |
| 38 | #include "include/policy_ns.h" | ||
| 37 | #include "include/procattr.h" | 39 | #include "include/procattr.h" |
| 38 | 40 | ||
| 39 | /* Flag indicating whether initialization completed */ | 41 | /* Flag indicating whether initialization completed */ |
| 40 | int apparmor_initialized __initdata; | 42 | int apparmor_initialized __initdata; |
| 41 | 43 | ||
| 44 | DEFINE_PER_CPU(struct aa_buffers, aa_buffers); | ||
| 45 | |||
| 46 | |||
| 42 | /* | 47 | /* |
| 43 | * LSM hook functions | 48 | * LSM hook functions |
| 44 | */ | 49 | */ |
| 45 | 50 | ||
| 46 | /* | 51 | /* |
| 47 | * free the associated aa_task_cxt and put its profiles | 52 | * free the associated aa_task_ctx and put its profiles |
| 48 | */ | 53 | */ |
| 49 | static void apparmor_cred_free(struct cred *cred) | 54 | static void apparmor_cred_free(struct cred *cred) |
| 50 | { | 55 | { |
| 51 | aa_free_task_context(cred_cxt(cred)); | 56 | aa_free_task_context(cred_ctx(cred)); |
| 52 | cred_cxt(cred) = NULL; | 57 | cred_ctx(cred) = NULL; |
| 53 | } | 58 | } |
| 54 | 59 | ||
| 55 | /* | 60 | /* |
| @@ -58,27 +63,29 @@ static void apparmor_cred_free(struct cred *cred) | |||
| 58 | static int apparmor_cred_alloc_blank(struct cred *cred, gfp_t gfp) | 63 | static int apparmor_cred_alloc_blank(struct cred *cred, gfp_t gfp) |
| 59 | { | 64 | { |
| 60 | /* freed by apparmor_cred_free */ | 65 | /* freed by apparmor_cred_free */ |
| 61 | struct aa_task_cxt *cxt = aa_alloc_task_context(gfp); | 66 | struct aa_task_ctx *ctx = aa_alloc_task_context(gfp); |
| 62 | if (!cxt) | 67 | |
| 68 | if (!ctx) | ||
| 63 | return -ENOMEM; | 69 | return -ENOMEM; |
| 64 | 70 | ||
| 65 | cred_cxt(cred) = cxt; | 71 | cred_ctx(cred) = ctx; |
| 66 | return 0; | 72 | return 0; |
| 67 | } | 73 | } |
| 68 | 74 | ||
| 69 | /* | 75 | /* |
| 70 | * prepare new aa_task_cxt for modification by prepare_cred block | 76 | * prepare new aa_task_ctx for modification by prepare_cred block |
| 71 | */ | 77 | */ |
| 72 | static int apparmor_cred_prepare(struct cred *new, const struct cred *old, | 78 | static int apparmor_cred_prepare(struct cred *new, const struct cred *old, |
| 73 | gfp_t gfp) | 79 | gfp_t gfp) |
| 74 | { | 80 | { |
| 75 | /* freed by apparmor_cred_free */ | 81 | /* freed by apparmor_cred_free */ |
| 76 | struct aa_task_cxt *cxt = aa_alloc_task_context(gfp); | 82 | struct aa_task_ctx *ctx = aa_alloc_task_context(gfp); |
| 77 | if (!cxt) | 83 | |
| 84 | if (!ctx) | ||
| 78 | return -ENOMEM; | 85 | return -ENOMEM; |
| 79 | 86 | ||
| 80 | aa_dup_task_context(cxt, cred_cxt(old)); | 87 | aa_dup_task_context(ctx, cred_ctx(old)); |
| 81 | cred_cxt(new) = cxt; | 88 | cred_ctx(new) = ctx; |
| 82 | return 0; | 89 | return 0; |
| 83 | } | 90 | } |
| 84 | 91 | ||
| @@ -87,10 +94,10 @@ static int apparmor_cred_prepare(struct cred *new, const struct cred *old, | |||
| 87 | */ | 94 | */ |
| 88 | static void apparmor_cred_transfer(struct cred *new, const struct cred *old) | 95 | static void apparmor_cred_transfer(struct cred *new, const struct cred *old) |
| 89 | { | 96 | { |
| 90 | const struct aa_task_cxt *old_cxt = cred_cxt(old); | 97 | const struct aa_task_ctx *old_ctx = cred_ctx(old); |
| 91 | struct aa_task_cxt *new_cxt = cred_cxt(new); | 98 | struct aa_task_ctx *new_ctx = cred_ctx(new); |
| 92 | 99 | ||
| 93 | aa_dup_task_context(new_cxt, old_cxt); | 100 | aa_dup_task_context(new_ctx, old_ctx); |
| 94 | } | 101 | } |
| 95 | 102 | ||
| 96 | static int apparmor_ptrace_access_check(struct task_struct *child, | 103 | static int apparmor_ptrace_access_check(struct task_struct *child, |
| @@ -149,7 +156,7 @@ static int apparmor_capable(const struct cred *cred, struct user_namespace *ns, | |||
| 149 | * | 156 | * |
| 150 | * Returns: %0 else error code if error or permission denied | 157 | * Returns: %0 else error code if error or permission denied |
| 151 | */ | 158 | */ |
| 152 | static int common_perm(int op, const struct path *path, u32 mask, | 159 | static int common_perm(const char *op, const struct path *path, u32 mask, |
| 153 | struct path_cond *cond) | 160 | struct path_cond *cond) |
| 154 | { | 161 | { |
| 155 | struct aa_profile *profile; | 162 | struct aa_profile *profile; |
| @@ -163,41 +170,42 @@ static int common_perm(int op, const struct path *path, u32 mask, | |||
| 163 | } | 170 | } |
| 164 | 171 | ||
| 165 | /** | 172 | /** |
| 166 | * common_perm_dir_dentry - common permission wrapper when path is dir, dentry | 173 | * common_perm_cond - common permission wrapper around inode cond |
| 167 | * @op: operation being checked | 174 | * @op: operation being checked |
| 168 | * @dir: directory of the dentry (NOT NULL) | 175 | * @path: location to check (NOT NULL) |
| 169 | * @dentry: dentry to check (NOT NULL) | ||
| 170 | * @mask: requested permissions mask | 176 | * @mask: requested permissions mask |
| 171 | * @cond: conditional info for the permission request (NOT NULL) | ||
| 172 | * | 177 | * |
| 173 | * Returns: %0 else error code if error or permission denied | 178 | * Returns: %0 else error code if error or permission denied |
| 174 | */ | 179 | */ |
| 175 | static int common_perm_dir_dentry(int op, const struct path *dir, | 180 | static int common_perm_cond(const char *op, const struct path *path, u32 mask) |
| 176 | struct dentry *dentry, u32 mask, | ||
| 177 | struct path_cond *cond) | ||
| 178 | { | 181 | { |
| 179 | struct path path = { dir->mnt, dentry }; | 182 | struct path_cond cond = { d_backing_inode(path->dentry)->i_uid, |
| 183 | d_backing_inode(path->dentry)->i_mode | ||
| 184 | }; | ||
| 180 | 185 | ||
| 181 | return common_perm(op, &path, mask, cond); | 186 | if (!path_mediated_fs(path->dentry)) |
| 187 | return 0; | ||
| 188 | |||
| 189 | return common_perm(op, path, mask, &cond); | ||
| 182 | } | 190 | } |
| 183 | 191 | ||
| 184 | /** | 192 | /** |
| 185 | * common_perm_path - common permission wrapper when mnt, dentry | 193 | * common_perm_dir_dentry - common permission wrapper when path is dir, dentry |
| 186 | * @op: operation being checked | 194 | * @op: operation being checked |
| 187 | * @path: location to check (NOT NULL) | 195 | * @dir: directory of the dentry (NOT NULL) |
| 196 | * @dentry: dentry to check (NOT NULL) | ||
| 188 | * @mask: requested permissions mask | 197 | * @mask: requested permissions mask |
| 198 | * @cond: conditional info for the permission request (NOT NULL) | ||
| 189 | * | 199 | * |
| 190 | * Returns: %0 else error code if error or permission denied | 200 | * Returns: %0 else error code if error or permission denied |
| 191 | */ | 201 | */ |
| 192 | static inline int common_perm_path(int op, const struct path *path, u32 mask) | 202 | static int common_perm_dir_dentry(const char *op, const struct path *dir, |
| 203 | struct dentry *dentry, u32 mask, | ||
| 204 | struct path_cond *cond) | ||
| 193 | { | 205 | { |
| 194 | struct path_cond cond = { d_backing_inode(path->dentry)->i_uid, | 206 | struct path path = { .mnt = dir->mnt, .dentry = dentry }; |
| 195 | d_backing_inode(path->dentry)->i_mode | ||
| 196 | }; | ||
| 197 | if (!mediated_filesystem(path->dentry)) | ||
| 198 | return 0; | ||
| 199 | 207 | ||
| 200 | return common_perm(op, path, mask, &cond); | 208 | return common_perm(op, &path, mask, cond); |
| 201 | } | 209 | } |
| 202 | 210 | ||
| 203 | /** | 211 | /** |
| @@ -209,13 +217,13 @@ static inline int common_perm_path(int op, const struct path *path, u32 mask) | |||
| 209 | * | 217 | * |
| 210 | * Returns: %0 else error code if error or permission denied | 218 | * Returns: %0 else error code if error or permission denied |
| 211 | */ | 219 | */ |
| 212 | static int common_perm_rm(int op, const struct path *dir, | 220 | static int common_perm_rm(const char *op, const struct path *dir, |
| 213 | struct dentry *dentry, u32 mask) | 221 | struct dentry *dentry, u32 mask) |
| 214 | { | 222 | { |
| 215 | struct inode *inode = d_backing_inode(dentry); | 223 | struct inode *inode = d_backing_inode(dentry); |
| 216 | struct path_cond cond = { }; | 224 | struct path_cond cond = { }; |
| 217 | 225 | ||
| 218 | if (!inode || !mediated_filesystem(dentry)) | 226 | if (!inode || !path_mediated_fs(dentry)) |
| 219 | return 0; | 227 | return 0; |
| 220 | 228 | ||
| 221 | cond.uid = inode->i_uid; | 229 | cond.uid = inode->i_uid; |
| @@ -234,12 +242,12 @@ static int common_perm_rm(int op, const struct path *dir, | |||
| 234 | * | 242 | * |
| 235 | * Returns: %0 else error code if error or permission denied | 243 | * Returns: %0 else error code if error or permission denied |
| 236 | */ | 244 | */ |
| 237 | static int common_perm_create(int op, const struct path *dir, | 245 | static int common_perm_create(const char *op, const struct path *dir, |
| 238 | struct dentry *dentry, u32 mask, umode_t mode) | 246 | struct dentry *dentry, u32 mask, umode_t mode) |
| 239 | { | 247 | { |
| 240 | struct path_cond cond = { current_fsuid(), mode }; | 248 | struct path_cond cond = { current_fsuid(), mode }; |
| 241 | 249 | ||
| 242 | if (!mediated_filesystem(dir->dentry)) | 250 | if (!path_mediated_fs(dir->dentry)) |
| 243 | return 0; | 251 | return 0; |
| 244 | 252 | ||
| 245 | return common_perm_dir_dentry(op, dir, dentry, mask, &cond); | 253 | return common_perm_dir_dentry(op, dir, dentry, mask, &cond); |
| @@ -270,7 +278,7 @@ static int apparmor_path_mknod(const struct path *dir, struct dentry *dentry, | |||
| 270 | 278 | ||
| 271 | static int apparmor_path_truncate(const struct path *path) | 279 | static int apparmor_path_truncate(const struct path *path) |
| 272 | { | 280 | { |
| 273 | return common_perm_path(OP_TRUNC, path, MAY_WRITE | AA_MAY_META_WRITE); | 281 | return common_perm_cond(OP_TRUNC, path, MAY_WRITE | AA_MAY_META_WRITE); |
| 274 | } | 282 | } |
| 275 | 283 | ||
| 276 | static int apparmor_path_symlink(const struct path *dir, struct dentry *dentry, | 284 | static int apparmor_path_symlink(const struct path *dir, struct dentry *dentry, |
| @@ -286,7 +294,7 @@ static int apparmor_path_link(struct dentry *old_dentry, const struct path *new_ | |||
| 286 | struct aa_profile *profile; | 294 | struct aa_profile *profile; |
| 287 | int error = 0; | 295 | int error = 0; |
| 288 | 296 | ||
| 289 | if (!mediated_filesystem(old_dentry)) | 297 | if (!path_mediated_fs(old_dentry)) |
| 290 | return 0; | 298 | return 0; |
| 291 | 299 | ||
| 292 | profile = aa_current_profile(); | 300 | profile = aa_current_profile(); |
| @@ -301,13 +309,15 @@ static int apparmor_path_rename(const struct path *old_dir, struct dentry *old_d | |||
| 301 | struct aa_profile *profile; | 309 | struct aa_profile *profile; |
| 302 | int error = 0; | 310 | int error = 0; |
| 303 | 311 | ||
| 304 | if (!mediated_filesystem(old_dentry)) | 312 | if (!path_mediated_fs(old_dentry)) |
| 305 | return 0; | 313 | return 0; |
| 306 | 314 | ||
| 307 | profile = aa_current_profile(); | 315 | profile = aa_current_profile(); |
| 308 | if (!unconfined(profile)) { | 316 | if (!unconfined(profile)) { |
| 309 | struct path old_path = { old_dir->mnt, old_dentry }; | 317 | struct path old_path = { .mnt = old_dir->mnt, |
| 310 | struct path new_path = { new_dir->mnt, new_dentry }; | 318 | .dentry = old_dentry }; |
| 319 | struct path new_path = { .mnt = new_dir->mnt, | ||
| 320 | .dentry = new_dentry }; | ||
| 311 | struct path_cond cond = { d_backing_inode(old_dentry)->i_uid, | 321 | struct path_cond cond = { d_backing_inode(old_dentry)->i_uid, |
| 312 | d_backing_inode(old_dentry)->i_mode | 322 | d_backing_inode(old_dentry)->i_mode |
| 313 | }; | 323 | }; |
| @@ -327,26 +337,26 @@ static int apparmor_path_rename(const struct path *old_dir, struct dentry *old_d | |||
| 327 | 337 | ||
| 328 | static int apparmor_path_chmod(const struct path *path, umode_t mode) | 338 | static int apparmor_path_chmod(const struct path *path, umode_t mode) |
| 329 | { | 339 | { |
| 330 | return common_perm_path(OP_CHMOD, path, AA_MAY_CHMOD); | 340 | return common_perm_cond(OP_CHMOD, path, AA_MAY_CHMOD); |
| 331 | } | 341 | } |
| 332 | 342 | ||
| 333 | static int apparmor_path_chown(const struct path *path, kuid_t uid, kgid_t gid) | 343 | static int apparmor_path_chown(const struct path *path, kuid_t uid, kgid_t gid) |
| 334 | { | 344 | { |
| 335 | return common_perm_path(OP_CHOWN, path, AA_MAY_CHOWN); | 345 | return common_perm_cond(OP_CHOWN, path, AA_MAY_CHOWN); |
| 336 | } | 346 | } |
| 337 | 347 | ||
| 338 | static int apparmor_inode_getattr(const struct path *path) | 348 | static int apparmor_inode_getattr(const struct path *path) |
| 339 | { | 349 | { |
| 340 | return common_perm_path(OP_GETATTR, path, AA_MAY_META_READ); | 350 | return common_perm_cond(OP_GETATTR, path, AA_MAY_META_READ); |
| 341 | } | 351 | } |
| 342 | 352 | ||
| 343 | static int apparmor_file_open(struct file *file, const struct cred *cred) | 353 | static int apparmor_file_open(struct file *file, const struct cred *cred) |
| 344 | { | 354 | { |
| 345 | struct aa_file_cxt *fcxt = file->f_security; | 355 | struct aa_file_ctx *fctx = file->f_security; |
| 346 | struct aa_profile *profile; | 356 | struct aa_profile *profile; |
| 347 | int error = 0; | 357 | int error = 0; |
| 348 | 358 | ||
| 349 | if (!mediated_filesystem(file->f_path.dentry)) | 359 | if (!path_mediated_fs(file->f_path.dentry)) |
| 350 | return 0; | 360 | return 0; |
| 351 | 361 | ||
| 352 | /* If in exec, permission is handled by bprm hooks. | 362 | /* If in exec, permission is handled by bprm hooks. |
| @@ -355,7 +365,7 @@ static int apparmor_file_open(struct file *file, const struct cred *cred) | |||
| 355 | * actually execute the image. | 365 | * actually execute the image. |
| 356 | */ | 366 | */ |
| 357 | if (current->in_execve) { | 367 | if (current->in_execve) { |
| 358 | fcxt->allow = MAY_EXEC | MAY_READ | AA_EXEC_MMAP; | 368 | fctx->allow = MAY_EXEC | MAY_READ | AA_EXEC_MMAP; |
| 359 | return 0; | 369 | return 0; |
| 360 | } | 370 | } |
| 361 | 371 | ||
| @@ -367,7 +377,7 @@ static int apparmor_file_open(struct file *file, const struct cred *cred) | |||
| 367 | error = aa_path_perm(OP_OPEN, profile, &file->f_path, 0, | 377 | error = aa_path_perm(OP_OPEN, profile, &file->f_path, 0, |
| 368 | aa_map_file_to_perms(file), &cond); | 378 | aa_map_file_to_perms(file), &cond); |
| 369 | /* todo cache full allowed permissions set and state */ | 379 | /* todo cache full allowed permissions set and state */ |
| 370 | fcxt->allow = aa_map_file_to_perms(file); | 380 | fctx->allow = aa_map_file_to_perms(file); |
| 371 | } | 381 | } |
| 372 | 382 | ||
| 373 | return error; | 383 | return error; |
| @@ -385,21 +395,21 @@ static int apparmor_file_alloc_security(struct file *file) | |||
| 385 | 395 | ||
| 386 | static void apparmor_file_free_security(struct file *file) | 396 | static void apparmor_file_free_security(struct file *file) |
| 387 | { | 397 | { |
| 388 | struct aa_file_cxt *cxt = file->f_security; | 398 | struct aa_file_ctx *ctx = file->f_security; |
| 389 | 399 | ||
| 390 | aa_free_file_context(cxt); | 400 | aa_free_file_context(ctx); |
| 391 | } | 401 | } |
| 392 | 402 | ||
| 393 | static int common_file_perm(int op, struct file *file, u32 mask) | 403 | static int common_file_perm(const char *op, struct file *file, u32 mask) |
| 394 | { | 404 | { |
| 395 | struct aa_file_cxt *fcxt = file->f_security; | 405 | struct aa_file_ctx *fctx = file->f_security; |
| 396 | struct aa_profile *profile, *fprofile = aa_cred_profile(file->f_cred); | 406 | struct aa_profile *profile, *fprofile = aa_cred_profile(file->f_cred); |
| 397 | int error = 0; | 407 | int error = 0; |
| 398 | 408 | ||
| 399 | BUG_ON(!fprofile); | 409 | AA_BUG(!fprofile); |
| 400 | 410 | ||
| 401 | if (!file->f_path.mnt || | 411 | if (!file->f_path.mnt || |
| 402 | !mediated_filesystem(file->f_path.dentry)) | 412 | !path_mediated_fs(file->f_path.dentry)) |
| 403 | return 0; | 413 | return 0; |
| 404 | 414 | ||
| 405 | profile = __aa_current_profile(); | 415 | profile = __aa_current_profile(); |
| @@ -412,7 +422,7 @@ static int common_file_perm(int op, struct file *file, u32 mask) | |||
| 412 | * delegation from unconfined tasks | 422 | * delegation from unconfined tasks |
| 413 | */ | 423 | */ |
| 414 | if (!unconfined(profile) && !unconfined(fprofile) && | 424 | if (!unconfined(profile) && !unconfined(fprofile) && |
| 415 | ((fprofile != profile) || (mask & ~fcxt->allow))) | 425 | ((fprofile != profile) || (mask & ~fctx->allow))) |
| 416 | error = aa_file_perm(op, profile, file, mask); | 426 | error = aa_file_perm(op, profile, file, mask); |
| 417 | 427 | ||
| 418 | return error; | 428 | return error; |
| @@ -433,7 +443,7 @@ static int apparmor_file_lock(struct file *file, unsigned int cmd) | |||
| 433 | return common_file_perm(OP_FLOCK, file, mask); | 443 | return common_file_perm(OP_FLOCK, file, mask); |
| 434 | } | 444 | } |
| 435 | 445 | ||
| 436 | static int common_mmap(int op, struct file *file, unsigned long prot, | 446 | static int common_mmap(const char *op, struct file *file, unsigned long prot, |
| 437 | unsigned long flags) | 447 | unsigned long flags) |
| 438 | { | 448 | { |
| 439 | int mask = 0; | 449 | int mask = 0; |
| @@ -474,15 +484,15 @@ static int apparmor_getprocattr(struct task_struct *task, char *name, | |||
| 474 | int error = -ENOENT; | 484 | int error = -ENOENT; |
| 475 | /* released below */ | 485 | /* released below */ |
| 476 | const struct cred *cred = get_task_cred(task); | 486 | const struct cred *cred = get_task_cred(task); |
| 477 | struct aa_task_cxt *cxt = cred_cxt(cred); | 487 | struct aa_task_ctx *ctx = cred_ctx(cred); |
| 478 | struct aa_profile *profile = NULL; | 488 | struct aa_profile *profile = NULL; |
| 479 | 489 | ||
| 480 | if (strcmp(name, "current") == 0) | 490 | if (strcmp(name, "current") == 0) |
| 481 | profile = aa_get_newest_profile(cxt->profile); | 491 | profile = aa_get_newest_profile(ctx->profile); |
| 482 | else if (strcmp(name, "prev") == 0 && cxt->previous) | 492 | else if (strcmp(name, "prev") == 0 && ctx->previous) |
| 483 | profile = aa_get_newest_profile(cxt->previous); | 493 | profile = aa_get_newest_profile(ctx->previous); |
| 484 | else if (strcmp(name, "exec") == 0 && cxt->onexec) | 494 | else if (strcmp(name, "exec") == 0 && ctx->onexec) |
| 485 | profile = aa_get_newest_profile(cxt->onexec); | 495 | profile = aa_get_newest_profile(ctx->onexec); |
| 486 | else | 496 | else |
| 487 | error = -EINVAL; | 497 | error = -EINVAL; |
| 488 | 498 | ||
| @@ -495,20 +505,16 @@ static int apparmor_getprocattr(struct task_struct *task, char *name, | |||
| 495 | return error; | 505 | return error; |
| 496 | } | 506 | } |
| 497 | 507 | ||
| 498 | static int apparmor_setprocattr(struct task_struct *task, char *name, | 508 | static int apparmor_setprocattr(const char *name, void *value, |
| 499 | void *value, size_t size) | 509 | size_t size) |
| 500 | { | 510 | { |
| 501 | struct common_audit_data sa; | ||
| 502 | struct apparmor_audit_data aad = {0,}; | ||
| 503 | char *command, *largs = NULL, *args = value; | 511 | char *command, *largs = NULL, *args = value; |
| 504 | size_t arg_size; | 512 | size_t arg_size; |
| 505 | int error; | 513 | int error; |
| 514 | DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_SETPROCATTR); | ||
| 506 | 515 | ||
| 507 | if (size == 0) | 516 | if (size == 0) |
| 508 | return -EINVAL; | 517 | return -EINVAL; |
| 509 | /* task can only write its own attributes */ | ||
| 510 | if (current != task) | ||
| 511 | return -EACCES; | ||
| 512 | 518 | ||
| 513 | /* AppArmor requires that the buffer must be null terminated atm */ | 519 | /* AppArmor requires that the buffer must be null terminated atm */ |
| 514 | if (args[size - 1] != '\0') { | 520 | if (args[size - 1] != '\0') { |
| @@ -538,17 +544,17 @@ static int apparmor_setprocattr(struct task_struct *task, char *name, | |||
| 538 | error = aa_setprocattr_changehat(args, arg_size, | 544 | error = aa_setprocattr_changehat(args, arg_size, |
| 539 | AA_DO_TEST); | 545 | AA_DO_TEST); |
| 540 | } else if (strcmp(command, "changeprofile") == 0) { | 546 | } else if (strcmp(command, "changeprofile") == 0) { |
| 541 | error = aa_setprocattr_changeprofile(args, !AA_ONEXEC, | 547 | error = aa_change_profile(args, !AA_ONEXEC, |
| 542 | !AA_DO_TEST); | 548 | !AA_DO_TEST, false); |
| 543 | } else if (strcmp(command, "permprofile") == 0) { | 549 | } else if (strcmp(command, "permprofile") == 0) { |
| 544 | error = aa_setprocattr_changeprofile(args, !AA_ONEXEC, | 550 | error = aa_change_profile(args, !AA_ONEXEC, AA_DO_TEST, |
| 545 | AA_DO_TEST); | 551 | false); |
| 546 | } else | 552 | } else |
| 547 | goto fail; | 553 | goto fail; |
| 548 | } else if (strcmp(name, "exec") == 0) { | 554 | } else if (strcmp(name, "exec") == 0) { |
| 549 | if (strcmp(command, "exec") == 0) | 555 | if (strcmp(command, "exec") == 0) |
| 550 | error = aa_setprocattr_changeprofile(args, AA_ONEXEC, | 556 | error = aa_change_profile(args, AA_ONEXEC, !AA_DO_TEST, |
| 551 | !AA_DO_TEST); | 557 | false); |
| 552 | else | 558 | else |
| 553 | goto fail; | 559 | goto fail; |
| 554 | } else | 560 | } else |
| @@ -562,12 +568,9 @@ out: | |||
| 562 | return error; | 568 | return error; |
| 563 | 569 | ||
| 564 | fail: | 570 | fail: |
| 565 | sa.type = LSM_AUDIT_DATA_NONE; | 571 | aad(&sa)->profile = aa_current_profile(); |
| 566 | sa.aad = &aad; | 572 | aad(&sa)->info = name; |
| 567 | aad.profile = aa_current_profile(); | 573 | aad(&sa)->error = error = -EINVAL; |
| 568 | aad.op = OP_SETPROCATTR; | ||
| 569 | aad.info = name; | ||
| 570 | aad.error = error = -EINVAL; | ||
| 571 | aa_audit_msg(AUDIT_APPARMOR_DENIED, &sa, NULL); | 574 | aa_audit_msg(AUDIT_APPARMOR_DENIED, &sa, NULL); |
| 572 | goto out; | 575 | goto out; |
| 573 | } | 576 | } |
| @@ -671,14 +674,14 @@ enum profile_mode aa_g_profile_mode = APPARMOR_ENFORCE; | |||
| 671 | module_param_call(mode, param_set_mode, param_get_mode, | 674 | module_param_call(mode, param_set_mode, param_get_mode, |
| 672 | &aa_g_profile_mode, S_IRUSR | S_IWUSR); | 675 | &aa_g_profile_mode, S_IRUSR | S_IWUSR); |
| 673 | 676 | ||
| 674 | #ifdef CONFIG_SECURITY_APPARMOR_HASH | ||
| 675 | /* whether policy verification hashing is enabled */ | 677 | /* whether policy verification hashing is enabled */ |
| 676 | bool aa_g_hash_policy = IS_ENABLED(CONFIG_SECURITY_APPARMOR_HASH_DEFAULT); | 678 | bool aa_g_hash_policy = IS_ENABLED(CONFIG_SECURITY_APPARMOR_HASH_DEFAULT); |
| 679 | #ifdef CONFIG_SECURITY_APPARMOR_HASH | ||
| 677 | module_param_named(hash_policy, aa_g_hash_policy, aabool, S_IRUSR | S_IWUSR); | 680 | module_param_named(hash_policy, aa_g_hash_policy, aabool, S_IRUSR | S_IWUSR); |
| 678 | #endif | 681 | #endif |
| 679 | 682 | ||
| 680 | /* Debug mode */ | 683 | /* Debug mode */ |
| 681 | bool aa_g_debug; | 684 | bool aa_g_debug = IS_ENABLED(CONFIG_SECURITY_DEBUG_MESSAGES); |
| 682 | module_param_named(debug, aa_g_debug, aabool, S_IRUSR | S_IWUSR); | 685 | module_param_named(debug, aa_g_debug, aabool, S_IRUSR | S_IWUSR); |
| 683 | 686 | ||
| 684 | /* Audit mode */ | 687 | /* Audit mode */ |
| @@ -711,10 +714,11 @@ module_param_named(path_max, aa_g_path_max, aauint, S_IRUSR | S_IWUSR); | |||
| 711 | 714 | ||
| 712 | /* Determines how paranoid loading of policy is and how much verification | 715 | /* Determines how paranoid loading of policy is and how much verification |
| 713 | * on the loaded policy is done. | 716 | * on the loaded policy is done. |
| 717 | * DEPRECATED: read only as strict checking of load is always done now | ||
| 718 | * that none root users (user namespaces) can load policy. | ||
| 714 | */ | 719 | */ |
| 715 | bool aa_g_paranoid_load = 1; | 720 | bool aa_g_paranoid_load = 1; |
| 716 | module_param_named(paranoid_load, aa_g_paranoid_load, aabool, | 721 | module_param_named(paranoid_load, aa_g_paranoid_load, aabool, S_IRUGO); |
| 717 | S_IRUSR | S_IWUSR); | ||
| 718 | 722 | ||
| 719 | /* Boot time disable flag */ | 723 | /* Boot time disable flag */ |
| 720 | static bool apparmor_enabled = CONFIG_SECURITY_APPARMOR_BOOTPARAM_VALUE; | 724 | static bool apparmor_enabled = CONFIG_SECURITY_APPARMOR_BOOTPARAM_VALUE; |
| @@ -734,49 +738,59 @@ __setup("apparmor=", apparmor_enabled_setup); | |||
| 734 | /* set global flag turning off the ability to load policy */ | 738 | /* set global flag turning off the ability to load policy */ |
| 735 | static int param_set_aalockpolicy(const char *val, const struct kernel_param *kp) | 739 | static int param_set_aalockpolicy(const char *val, const struct kernel_param *kp) |
| 736 | { | 740 | { |
| 737 | if (!policy_admin_capable()) | 741 | if (!policy_admin_capable(NULL)) |
| 738 | return -EPERM; | 742 | return -EPERM; |
| 739 | return param_set_bool(val, kp); | 743 | return param_set_bool(val, kp); |
| 740 | } | 744 | } |
| 741 | 745 | ||
| 742 | static int param_get_aalockpolicy(char *buffer, const struct kernel_param *kp) | 746 | static int param_get_aalockpolicy(char *buffer, const struct kernel_param *kp) |
| 743 | { | 747 | { |
| 744 | if (!policy_view_capable()) | 748 | if (!policy_view_capable(NULL)) |
| 745 | return -EPERM; | 749 | return -EPERM; |
| 750 | if (!apparmor_enabled) | ||
| 751 | return -EINVAL; | ||
| 746 | return param_get_bool(buffer, kp); | 752 | return param_get_bool(buffer, kp); |
| 747 | } | 753 | } |
| 748 | 754 | ||
| 749 | static int param_set_aabool(const char *val, const struct kernel_param *kp) | 755 | static int param_set_aabool(const char *val, const struct kernel_param *kp) |
| 750 | { | 756 | { |
| 751 | if (!policy_admin_capable()) | 757 | if (!policy_admin_capable(NULL)) |
| 752 | return -EPERM; | 758 | return -EPERM; |
| 759 | if (!apparmor_enabled) | ||
| 760 | return -EINVAL; | ||
| 753 | return param_set_bool(val, kp); | 761 | return param_set_bool(val, kp); |
| 754 | } | 762 | } |
| 755 | 763 | ||
| 756 | static int param_get_aabool(char *buffer, const struct kernel_param *kp) | 764 | static int param_get_aabool(char *buffer, const struct kernel_param *kp) |
| 757 | { | 765 | { |
| 758 | if (!policy_view_capable()) | 766 | if (!policy_view_capable(NULL)) |
| 759 | return -EPERM; | 767 | return -EPERM; |
| 768 | if (!apparmor_enabled) | ||
| 769 | return -EINVAL; | ||
| 760 | return param_get_bool(buffer, kp); | 770 | return param_get_bool(buffer, kp); |
| 761 | } | 771 | } |
| 762 | 772 | ||
| 763 | static int param_set_aauint(const char *val, const struct kernel_param *kp) | 773 | static int param_set_aauint(const char *val, const struct kernel_param *kp) |
| 764 | { | 774 | { |
| 765 | if (!policy_admin_capable()) | 775 | if (!policy_admin_capable(NULL)) |
| 766 | return -EPERM; | 776 | return -EPERM; |
| 777 | if (!apparmor_enabled) | ||
| 778 | return -EINVAL; | ||
| 767 | return param_set_uint(val, kp); | 779 | return param_set_uint(val, kp); |
| 768 | } | 780 | } |
| 769 | 781 | ||
| 770 | static int param_get_aauint(char *buffer, const struct kernel_param *kp) | 782 | static int param_get_aauint(char *buffer, const struct kernel_param *kp) |
| 771 | { | 783 | { |
| 772 | if (!policy_view_capable()) | 784 | if (!policy_view_capable(NULL)) |
| 773 | return -EPERM; | 785 | return -EPERM; |
| 786 | if (!apparmor_enabled) | ||
| 787 | return -EINVAL; | ||
| 774 | return param_get_uint(buffer, kp); | 788 | return param_get_uint(buffer, kp); |
| 775 | } | 789 | } |
| 776 | 790 | ||
| 777 | static int param_get_audit(char *buffer, struct kernel_param *kp) | 791 | static int param_get_audit(char *buffer, struct kernel_param *kp) |
| 778 | { | 792 | { |
| 779 | if (!policy_view_capable()) | 793 | if (!policy_view_capable(NULL)) |
| 780 | return -EPERM; | 794 | return -EPERM; |
| 781 | 795 | ||
| 782 | if (!apparmor_enabled) | 796 | if (!apparmor_enabled) |
| @@ -788,7 +802,7 @@ static int param_get_audit(char *buffer, struct kernel_param *kp) | |||
| 788 | static int param_set_audit(const char *val, struct kernel_param *kp) | 802 | static int param_set_audit(const char *val, struct kernel_param *kp) |
| 789 | { | 803 | { |
| 790 | int i; | 804 | int i; |
| 791 | if (!policy_admin_capable()) | 805 | if (!policy_admin_capable(NULL)) |
| 792 | return -EPERM; | 806 | return -EPERM; |
| 793 | 807 | ||
| 794 | if (!apparmor_enabled) | 808 | if (!apparmor_enabled) |
| @@ -809,7 +823,7 @@ static int param_set_audit(const char *val, struct kernel_param *kp) | |||
| 809 | 823 | ||
| 810 | static int param_get_mode(char *buffer, struct kernel_param *kp) | 824 | static int param_get_mode(char *buffer, struct kernel_param *kp) |
| 811 | { | 825 | { |
| 812 | if (!policy_admin_capable()) | 826 | if (!policy_view_capable(NULL)) |
| 813 | return -EPERM; | 827 | return -EPERM; |
| 814 | 828 | ||
| 815 | if (!apparmor_enabled) | 829 | if (!apparmor_enabled) |
| @@ -821,7 +835,7 @@ static int param_get_mode(char *buffer, struct kernel_param *kp) | |||
| 821 | static int param_set_mode(const char *val, struct kernel_param *kp) | 835 | static int param_set_mode(const char *val, struct kernel_param *kp) |
| 822 | { | 836 | { |
| 823 | int i; | 837 | int i; |
| 824 | if (!policy_admin_capable()) | 838 | if (!policy_admin_capable(NULL)) |
| 825 | return -EPERM; | 839 | return -EPERM; |
| 826 | 840 | ||
| 827 | if (!apparmor_enabled) | 841 | if (!apparmor_enabled) |
| @@ -845,25 +859,102 @@ static int param_set_mode(const char *val, struct kernel_param *kp) | |||
| 845 | */ | 859 | */ |
| 846 | 860 | ||
| 847 | /** | 861 | /** |
| 848 | * set_init_cxt - set a task context and profile on the first task. | 862 | * set_init_ctx - set a task context and profile on the first task. |
| 849 | * | 863 | * |
| 850 | * TODO: allow setting an alternate profile than unconfined | 864 | * TODO: allow setting an alternate profile than unconfined |
| 851 | */ | 865 | */ |
| 852 | static int __init set_init_cxt(void) | 866 | static int __init set_init_ctx(void) |
| 853 | { | 867 | { |
| 854 | struct cred *cred = (struct cred *)current->real_cred; | 868 | struct cred *cred = (struct cred *)current->real_cred; |
| 855 | struct aa_task_cxt *cxt; | 869 | struct aa_task_ctx *ctx; |
| 856 | 870 | ||
| 857 | cxt = aa_alloc_task_context(GFP_KERNEL); | 871 | ctx = aa_alloc_task_context(GFP_KERNEL); |
| 858 | if (!cxt) | 872 | if (!ctx) |
| 859 | return -ENOMEM; | 873 | return -ENOMEM; |
| 860 | 874 | ||
| 861 | cxt->profile = aa_get_profile(root_ns->unconfined); | 875 | ctx->profile = aa_get_profile(root_ns->unconfined); |
| 862 | cred_cxt(cred) = cxt; | 876 | cred_ctx(cred) = ctx; |
| 863 | 877 | ||
| 864 | return 0; | 878 | return 0; |
| 865 | } | 879 | } |
| 866 | 880 | ||
| 881 | static void destroy_buffers(void) | ||
| 882 | { | ||
| 883 | u32 i, j; | ||
| 884 | |||
| 885 | for_each_possible_cpu(i) { | ||
| 886 | for_each_cpu_buffer(j) { | ||
| 887 | kfree(per_cpu(aa_buffers, i).buf[j]); | ||
| 888 | per_cpu(aa_buffers, i).buf[j] = NULL; | ||
| 889 | } | ||
| 890 | } | ||
| 891 | } | ||
| 892 | |||
| 893 | static int __init alloc_buffers(void) | ||
| 894 | { | ||
| 895 | u32 i, j; | ||
| 896 | |||
| 897 | for_each_possible_cpu(i) { | ||
| 898 | for_each_cpu_buffer(j) { | ||
| 899 | char *buffer; | ||
| 900 | |||
| 901 | if (cpu_to_node(i) > num_online_nodes()) | ||
| 902 | /* fallback to kmalloc for offline nodes */ | ||
| 903 | buffer = kmalloc(aa_g_path_max, GFP_KERNEL); | ||
| 904 | else | ||
| 905 | buffer = kmalloc_node(aa_g_path_max, GFP_KERNEL, | ||
| 906 | cpu_to_node(i)); | ||
| 907 | if (!buffer) { | ||
| 908 | destroy_buffers(); | ||
| 909 | return -ENOMEM; | ||
| 910 | } | ||
| 911 | per_cpu(aa_buffers, i).buf[j] = buffer; | ||
| 912 | } | ||
| 913 | } | ||
| 914 | |||
| 915 | return 0; | ||
| 916 | } | ||
| 917 | |||
| 918 | #ifdef CONFIG_SYSCTL | ||
| 919 | static int apparmor_dointvec(struct ctl_table *table, int write, | ||
| 920 | void __user *buffer, size_t *lenp, loff_t *ppos) | ||
| 921 | { | ||
| 922 | if (!policy_admin_capable(NULL)) | ||
| 923 | return -EPERM; | ||
| 924 | if (!apparmor_enabled) | ||
| 925 | return -EINVAL; | ||
| 926 | |||
| 927 | return proc_dointvec(table, write, buffer, lenp, ppos); | ||
| 928 | } | ||
| 929 | |||
| 930 | static struct ctl_path apparmor_sysctl_path[] = { | ||
| 931 | { .procname = "kernel", }, | ||
| 932 | { } | ||
| 933 | }; | ||
| 934 | |||
| 935 | static struct ctl_table apparmor_sysctl_table[] = { | ||
| 936 | { | ||
| 937 | .procname = "unprivileged_userns_apparmor_policy", | ||
| 938 | .data = &unprivileged_userns_apparmor_policy, | ||
| 939 | .maxlen = sizeof(int), | ||
| 940 | .mode = 0600, | ||
| 941 | .proc_handler = apparmor_dointvec, | ||
| 942 | }, | ||
| 943 | { } | ||
| 944 | }; | ||
| 945 | |||
| 946 | static int __init apparmor_init_sysctl(void) | ||
| 947 | { | ||
| 948 | return register_sysctl_paths(apparmor_sysctl_path, | ||
| 949 | apparmor_sysctl_table) ? 0 : -ENOMEM; | ||
| 950 | } | ||
| 951 | #else | ||
| 952 | static inline int apparmor_init_sysctl(void) | ||
| 953 | { | ||
| 954 | return 0; | ||
| 955 | } | ||
| 956 | #endif /* CONFIG_SYSCTL */ | ||
| 957 | |||
| 867 | static int __init apparmor_init(void) | 958 | static int __init apparmor_init(void) |
| 868 | { | 959 | { |
| 869 | int error; | 960 | int error; |
| @@ -874,19 +965,39 @@ static int __init apparmor_init(void) | |||
| 874 | return 0; | 965 | return 0; |
| 875 | } | 966 | } |
| 876 | 967 | ||
| 968 | error = aa_setup_dfa_engine(); | ||
| 969 | if (error) { | ||
| 970 | AA_ERROR("Unable to setup dfa engine\n"); | ||
| 971 | goto alloc_out; | ||
| 972 | } | ||
| 973 | |||
| 877 | error = aa_alloc_root_ns(); | 974 | error = aa_alloc_root_ns(); |
| 878 | if (error) { | 975 | if (error) { |
| 879 | AA_ERROR("Unable to allocate default profile namespace\n"); | 976 | AA_ERROR("Unable to allocate default profile namespace\n"); |
| 880 | goto alloc_out; | 977 | goto alloc_out; |
| 881 | } | 978 | } |
| 882 | 979 | ||
| 883 | error = set_init_cxt(); | 980 | error = apparmor_init_sysctl(); |
| 981 | if (error) { | ||
| 982 | AA_ERROR("Unable to register sysctls\n"); | ||
| 983 | goto alloc_out; | ||
| 984 | |||
| 985 | } | ||
| 986 | |||
| 987 | error = alloc_buffers(); | ||
| 988 | if (error) { | ||
| 989 | AA_ERROR("Unable to allocate work buffers\n"); | ||
| 990 | goto buffers_out; | ||
| 991 | } | ||
| 992 | |||
| 993 | error = set_init_ctx(); | ||
| 884 | if (error) { | 994 | if (error) { |
| 885 | AA_ERROR("Failed to set context on init task\n"); | 995 | AA_ERROR("Failed to set context on init task\n"); |
| 886 | aa_free_root_ns(); | 996 | aa_free_root_ns(); |
| 887 | goto alloc_out; | 997 | goto buffers_out; |
| 888 | } | 998 | } |
| 889 | security_add_hooks(apparmor_hooks, ARRAY_SIZE(apparmor_hooks)); | 999 | security_add_hooks(apparmor_hooks, ARRAY_SIZE(apparmor_hooks), |
| 1000 | "apparmor"); | ||
| 890 | 1001 | ||
| 891 | /* Report that AppArmor successfully initialized */ | 1002 | /* Report that AppArmor successfully initialized */ |
| 892 | apparmor_initialized = 1; | 1003 | apparmor_initialized = 1; |
| @@ -899,8 +1010,12 @@ static int __init apparmor_init(void) | |||
| 899 | 1010 | ||
| 900 | return error; | 1011 | return error; |
| 901 | 1012 | ||
| 1013 | buffers_out: | ||
| 1014 | destroy_buffers(); | ||
| 1015 | |||
| 902 | alloc_out: | 1016 | alloc_out: |
| 903 | aa_destroy_aafs(); | 1017 | aa_destroy_aafs(); |
| 1018 | aa_teardown_dfa_engine(); | ||
| 904 | 1019 | ||
| 905 | apparmor_enabled = 0; | 1020 | apparmor_enabled = 0; |
| 906 | return error; | 1021 | return error; |
diff --git a/security/apparmor/match.c b/security/apparmor/match.c index 3f900fcca8fb..eb0efef746f5 100644 --- a/security/apparmor/match.c +++ b/security/apparmor/match.c | |||
| @@ -20,11 +20,38 @@ | |||
| 20 | #include <linux/err.h> | 20 | #include <linux/err.h> |
| 21 | #include <linux/kref.h> | 21 | #include <linux/kref.h> |
| 22 | 22 | ||
| 23 | #include "include/apparmor.h" | 23 | #include "include/lib.h" |
| 24 | #include "include/match.h" | 24 | #include "include/match.h" |
| 25 | 25 | ||
| 26 | #define base_idx(X) ((X) & 0xffffff) | 26 | #define base_idx(X) ((X) & 0xffffff) |
| 27 | 27 | ||
| 28 | static char nulldfa_src[] = { | ||
| 29 | #include "nulldfa.in" | ||
| 30 | }; | ||
| 31 | struct aa_dfa *nulldfa; | ||
| 32 | |||
| 33 | int aa_setup_dfa_engine(void) | ||
| 34 | { | ||
| 35 | int error; | ||
| 36 | |||
| 37 | nulldfa = aa_dfa_unpack(nulldfa_src, sizeof(nulldfa_src), | ||
| 38 | TO_ACCEPT1_FLAG(YYTD_DATA32) | | ||
| 39 | TO_ACCEPT2_FLAG(YYTD_DATA32)); | ||
| 40 | if (!IS_ERR(nulldfa)) | ||
| 41 | return 0; | ||
| 42 | |||
| 43 | error = PTR_ERR(nulldfa); | ||
| 44 | nulldfa = NULL; | ||
| 45 | |||
| 46 | return error; | ||
| 47 | } | ||
| 48 | |||
| 49 | void aa_teardown_dfa_engine(void) | ||
| 50 | { | ||
| 51 | aa_put_dfa(nulldfa); | ||
| 52 | nulldfa = NULL; | ||
| 53 | } | ||
| 54 | |||
| 28 | /** | 55 | /** |
| 29 | * unpack_table - unpack a dfa table (one of accept, default, base, next check) | 56 | * unpack_table - unpack a dfa table (one of accept, default, base, next check) |
| 30 | * @blob: data to unpack (NOT NULL) | 57 | * @blob: data to unpack (NOT NULL) |
| @@ -46,11 +73,11 @@ static struct table_header *unpack_table(char *blob, size_t bsize) | |||
| 46 | /* loaded td_id's start at 1, subtract 1 now to avoid doing | 73 | /* loaded td_id's start at 1, subtract 1 now to avoid doing |
| 47 | * it every time we use td_id as an index | 74 | * it every time we use td_id as an index |
| 48 | */ | 75 | */ |
| 49 | th.td_id = be16_to_cpu(*(u16 *) (blob)) - 1; | 76 | th.td_id = be16_to_cpu(*(__be16 *) (blob)) - 1; |
| 50 | if (th.td_id > YYTD_ID_MAX) | 77 | if (th.td_id > YYTD_ID_MAX) |
| 51 | goto out; | 78 | goto out; |
| 52 | th.td_flags = be16_to_cpu(*(u16 *) (blob + 2)); | 79 | th.td_flags = be16_to_cpu(*(__be16 *) (blob + 2)); |
| 53 | th.td_lolen = be32_to_cpu(*(u32 *) (blob + 8)); | 80 | th.td_lolen = be32_to_cpu(*(__be32 *) (blob + 8)); |
| 54 | blob += sizeof(struct table_header); | 81 | blob += sizeof(struct table_header); |
| 55 | 82 | ||
| 56 | if (!(th.td_flags == YYTD_DATA16 || th.td_flags == YYTD_DATA32 || | 83 | if (!(th.td_flags == YYTD_DATA16 || th.td_flags == YYTD_DATA32 || |
| @@ -68,13 +95,13 @@ static struct table_header *unpack_table(char *blob, size_t bsize) | |||
| 68 | table->td_lolen = th.td_lolen; | 95 | table->td_lolen = th.td_lolen; |
| 69 | if (th.td_flags == YYTD_DATA8) | 96 | if (th.td_flags == YYTD_DATA8) |
| 70 | UNPACK_ARRAY(table->td_data, blob, th.td_lolen, | 97 | UNPACK_ARRAY(table->td_data, blob, th.td_lolen, |
| 71 | u8, byte_to_byte); | 98 | u8, u8, byte_to_byte); |
| 72 | else if (th.td_flags == YYTD_DATA16) | 99 | else if (th.td_flags == YYTD_DATA16) |
| 73 | UNPACK_ARRAY(table->td_data, blob, th.td_lolen, | 100 | UNPACK_ARRAY(table->td_data, blob, th.td_lolen, |
| 74 | u16, be16_to_cpu); | 101 | u16, __be16, be16_to_cpu); |
| 75 | else if (th.td_flags == YYTD_DATA32) | 102 | else if (th.td_flags == YYTD_DATA32) |
| 76 | UNPACK_ARRAY(table->td_data, blob, th.td_lolen, | 103 | UNPACK_ARRAY(table->td_data, blob, th.td_lolen, |
| 77 | u32, be32_to_cpu); | 104 | u32, __be32, be32_to_cpu); |
| 78 | else | 105 | else |
| 79 | goto fail; | 106 | goto fail; |
| 80 | /* if table was vmalloced make sure the page tables are synced | 107 | /* if table was vmalloced make sure the page tables are synced |
| @@ -222,14 +249,14 @@ struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags) | |||
| 222 | if (size < sizeof(struct table_set_header)) | 249 | if (size < sizeof(struct table_set_header)) |
| 223 | goto fail; | 250 | goto fail; |
| 224 | 251 | ||
| 225 | if (ntohl(*(u32 *) data) != YYTH_MAGIC) | 252 | if (ntohl(*(__be32 *) data) != YYTH_MAGIC) |
| 226 | goto fail; | 253 | goto fail; |
| 227 | 254 | ||
| 228 | hsize = ntohl(*(u32 *) (data + 4)); | 255 | hsize = ntohl(*(__be32 *) (data + 4)); |
| 229 | if (size < hsize) | 256 | if (size < hsize) |
| 230 | goto fail; | 257 | goto fail; |
| 231 | 258 | ||
| 232 | dfa->flags = ntohs(*(u16 *) (data + 12)); | 259 | dfa->flags = ntohs(*(__be16 *) (data + 12)); |
| 233 | data += hsize; | 260 | data += hsize; |
| 234 | size -= hsize; | 261 | size -= hsize; |
| 235 | 262 | ||
diff --git a/security/apparmor/nulldfa.in b/security/apparmor/nulldfa.in new file mode 100644 index 000000000000..3cb38022902e --- /dev/null +++ b/security/apparmor/nulldfa.in | |||
| @@ -0,0 +1 @@ | |||
| 0x1B, 0x5E, 0x78, 0x3D, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x04, 0x90, 0x00, 0x00, 0x6E, 0x6F, 0x74, 0x66, 0x6C, 0x65, 0x78, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 | |||
diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 179e68d7dc5f..f44312a19522 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c | |||
| @@ -76,6 +76,7 @@ | |||
| 76 | #include <linux/slab.h> | 76 | #include <linux/slab.h> |
| 77 | #include <linux/spinlock.h> | 77 | #include <linux/spinlock.h> |
| 78 | #include <linux/string.h> | 78 | #include <linux/string.h> |
| 79 | #include <linux/user_namespace.h> | ||
| 79 | 80 | ||
| 80 | #include "include/apparmor.h" | 81 | #include "include/apparmor.h" |
| 81 | #include "include/capability.h" | 82 | #include "include/capability.h" |
| @@ -85,12 +86,11 @@ | |||
| 85 | #include "include/match.h" | 86 | #include "include/match.h" |
| 86 | #include "include/path.h" | 87 | #include "include/path.h" |
| 87 | #include "include/policy.h" | 88 | #include "include/policy.h" |
| 89 | #include "include/policy_ns.h" | ||
| 88 | #include "include/policy_unpack.h" | 90 | #include "include/policy_unpack.h" |
| 89 | #include "include/resource.h" | 91 | #include "include/resource.h" |
| 90 | 92 | ||
| 91 | 93 | int unprivileged_userns_apparmor_policy = 1; | |
| 92 | /* root profile namespace */ | ||
| 93 | struct aa_namespace *root_ns; | ||
| 94 | 94 | ||
| 95 | const char *const aa_profile_mode_names[] = { | 95 | const char *const aa_profile_mode_names[] = { |
| 96 | "enforce", | 96 | "enforce", |
| @@ -99,318 +99,16 @@ const char *const aa_profile_mode_names[] = { | |||
| 99 | "unconfined", | 99 | "unconfined", |
| 100 | }; | 100 | }; |
| 101 | 101 | ||
| 102 | /** | 102 | /* requires profile list write lock held */ |
| 103 | * hname_tail - find the last component of an hname | 103 | void __aa_update_proxy(struct aa_profile *orig, struct aa_profile *new) |
| 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 | |||
| 146 | return 1; | ||
| 147 | } | ||
| 148 | |||
| 149 | /** | ||
| 150 | * policy_destroy - free the elements referenced by @policy | ||
| 151 | * @policy: policy that is to have its elements freed (NOT NULL) | ||
| 152 | */ | ||
| 153 | static void policy_destroy(struct aa_policy *policy) | ||
| 154 | { | ||
| 155 | /* still contains profiles -- invalid */ | ||
| 156 | if (on_list_rcu(&policy->profiles)) { | ||
| 157 | AA_ERROR("%s: internal error, " | ||
| 158 | "policy '%s' still contains profiles\n", | ||
| 159 | __func__, policy->name); | ||
| 160 | BUG(); | ||
| 161 | } | ||
| 162 | if (on_list_rcu(&policy->list)) { | ||
| 163 | AA_ERROR("%s: internal error, policy '%s' still on list\n", | ||
| 164 | __func__, policy->name); | ||
| 165 | BUG(); | ||
| 166 | } | ||
| 167 | |||
| 168 | /* don't free name as its a subset of hname */ | ||
| 169 | kzfree(policy->hname); | ||
| 170 | } | ||
| 171 | |||
| 172 | /** | ||
| 173 | * __policy_find - find a policy by @name on a policy list | ||
| 174 | * @head: list to search (NOT NULL) | ||
| 175 | * @name: name to search for (NOT NULL) | ||
| 176 | * | ||
| 177 | * Requires: rcu_read_lock be held | ||
| 178 | * | ||
| 179 | * Returns: unrefcounted policy that match @name or NULL if not found | ||
| 180 | */ | ||
| 181 | static struct aa_policy *__policy_find(struct list_head *head, const char *name) | ||
| 182 | { | ||
| 183 | struct aa_policy *policy; | ||
| 184 | |||
| 185 | list_for_each_entry_rcu(policy, head, list) { | ||
| 186 | if (!strcmp(policy->name, name)) | ||
| 187 | return policy; | ||
| 188 | } | ||
| 189 | return NULL; | ||
| 190 | } | ||
| 191 | |||
| 192 | /** | ||
| 193 | * __policy_strn_find - find a policy that's name matches @len chars of @str | ||
| 194 | * @head: list to search (NOT NULL) | ||
| 195 | * @str: string to search for (NOT NULL) | ||
| 196 | * @len: length of match required | ||
| 197 | * | ||
| 198 | * Requires: rcu_read_lock be held | ||
| 199 | * | ||
| 200 | * Returns: unrefcounted policy that match @str or NULL if not found | ||
| 201 | * | ||
| 202 | * if @len == strlen(@strlen) then this is equiv to __policy_find | ||
| 203 | * other wise it allows searching for policy by a partial match of name | ||
| 204 | */ | ||
| 205 | static struct aa_policy *__policy_strn_find(struct list_head *head, | ||
| 206 | const char *str, int len) | ||
| 207 | { | ||
| 208 | struct aa_policy *policy; | ||
| 209 | |||
| 210 | list_for_each_entry_rcu(policy, head, list) { | ||
| 211 | if (aa_strneq(policy->name, str, len)) | ||
| 212 | return policy; | ||
| 213 | } | ||
| 214 | |||
| 215 | return NULL; | ||
| 216 | } | ||
| 217 | |||
| 218 | /* | ||
| 219 | * Routines for AppArmor namespaces | ||
| 220 | */ | ||
| 221 | |||
| 222 | static const char *hidden_ns_name = "---"; | ||
| 223 | /** | ||
| 224 | * aa_ns_visible - test if @view is visible from @curr | ||
| 225 | * @curr: namespace to treat as the parent (NOT NULL) | ||
| 226 | * @view: namespace to test if visible from @curr (NOT NULL) | ||
| 227 | * | ||
| 228 | * Returns: true if @view is visible from @curr else false | ||
| 229 | */ | ||
| 230 | bool aa_ns_visible(struct aa_namespace *curr, struct aa_namespace *view) | ||
| 231 | { | ||
| 232 | if (curr == view) | ||
| 233 | return true; | ||
| 234 | |||
| 235 | for ( ; view; view = view->parent) { | ||
| 236 | if (view->parent == curr) | ||
| 237 | return true; | ||
| 238 | } | ||
| 239 | return false; | ||
| 240 | } | ||
| 241 | |||
| 242 | /** | ||
| 243 | * aa_na_name - Find the ns name to display for @view from @curr | ||
| 244 | * @curr - current namespace (NOT NULL) | ||
| 245 | * @view - namespace attempting to view (NOT NULL) | ||
| 246 | * | ||
| 247 | * Returns: name of @view visible from @curr | ||
| 248 | */ | ||
| 249 | const char *aa_ns_name(struct aa_namespace *curr, struct aa_namespace *view) | ||
| 250 | { | ||
| 251 | /* if view == curr then the namespace name isn't displayed */ | ||
| 252 | if (curr == view) | ||
| 253 | return ""; | ||
| 254 | |||
| 255 | if (aa_ns_visible(curr, view)) { | ||
| 256 | /* at this point if a ns is visible it is in a view ns | ||
| 257 | * thus the curr ns.hname is a prefix of its name. | ||
| 258 | * Only output the virtualized portion of the name | ||
| 259 | * Add + 2 to skip over // separating curr hname prefix | ||
| 260 | * from the visible tail of the views hname | ||
| 261 | */ | ||
| 262 | return view->base.hname + strlen(curr->base.hname) + 2; | ||
| 263 | } else | ||
| 264 | return hidden_ns_name; | ||
| 265 | } | ||
| 266 | |||
| 267 | /** | ||
| 268 | * alloc_namespace - allocate, initialize and return a new namespace | ||
| 269 | * @prefix: parent namespace name (MAYBE NULL) | ||
| 270 | * @name: a preallocated name (NOT NULL) | ||
| 271 | * | ||
| 272 | * Returns: refcounted namespace or NULL on failure. | ||
| 273 | */ | ||
| 274 | static struct aa_namespace *alloc_namespace(const char *prefix, | ||
| 275 | const char *name) | ||
| 276 | { | ||
| 277 | struct aa_namespace *ns; | ||
| 278 | |||
| 279 | ns = kzalloc(sizeof(*ns), GFP_KERNEL); | ||
| 280 | AA_DEBUG("%s(%p)\n", __func__, ns); | ||
| 281 | if (!ns) | ||
| 282 | return NULL; | ||
| 283 | if (!policy_init(&ns->base, prefix, name)) | ||
| 284 | goto fail_ns; | ||
| 285 | |||
| 286 | INIT_LIST_HEAD(&ns->sub_ns); | ||
| 287 | mutex_init(&ns->lock); | ||
| 288 | |||
| 289 | /* released by free_namespace */ | ||
| 290 | ns->unconfined = aa_alloc_profile("unconfined"); | ||
| 291 | if (!ns->unconfined) | ||
| 292 | goto fail_unconfined; | ||
| 293 | |||
| 294 | ns->unconfined->flags = PFLAG_IX_ON_NAME_ERROR | | ||
| 295 | PFLAG_IMMUTABLE | PFLAG_NS_COUNT; | ||
| 296 | ns->unconfined->mode = APPARMOR_UNCONFINED; | ||
| 297 | |||
| 298 | /* ns and ns->unconfined share ns->unconfined refcount */ | ||
| 299 | ns->unconfined->ns = ns; | ||
| 300 | |||
| 301 | atomic_set(&ns->uniq_null, 0); | ||
| 302 | |||
| 303 | return ns; | ||
| 304 | |||
| 305 | fail_unconfined: | ||
| 306 | kzfree(ns->base.hname); | ||
| 307 | fail_ns: | ||
| 308 | kzfree(ns); | ||
| 309 | return NULL; | ||
| 310 | } | ||
| 311 | |||
| 312 | /** | ||
| 313 | * free_namespace - free a profile namespace | ||
| 314 | * @ns: the namespace to free (MAYBE NULL) | ||
| 315 | * | ||
| 316 | * Requires: All references to the namespace must have been put, if the | ||
| 317 | * namespace was referenced by a profile confining a task, | ||
| 318 | */ | ||
| 319 | static void free_namespace(struct aa_namespace *ns) | ||
| 320 | { | 104 | { |
| 321 | if (!ns) | 105 | struct aa_profile *tmp; |
| 322 | return; | ||
| 323 | |||
| 324 | policy_destroy(&ns->base); | ||
| 325 | aa_put_namespace(ns->parent); | ||
| 326 | |||
| 327 | ns->unconfined->ns = NULL; | ||
| 328 | aa_free_profile(ns->unconfined); | ||
| 329 | kzfree(ns); | ||
| 330 | } | ||
| 331 | |||
| 332 | /** | ||
| 333 | * __aa_find_namespace - find a namespace on a list by @name | ||
| 334 | * @head: list to search for namespace on (NOT NULL) | ||
| 335 | * @name: name of namespace to look for (NOT NULL) | ||
| 336 | * | ||
| 337 | * Returns: unrefcounted namespace | ||
| 338 | * | ||
| 339 | * Requires: rcu_read_lock be held | ||
| 340 | */ | ||
| 341 | static struct aa_namespace *__aa_find_namespace(struct list_head *head, | ||
| 342 | const char *name) | ||
| 343 | { | ||
| 344 | return (struct aa_namespace *)__policy_find(head, name); | ||
| 345 | } | ||
| 346 | |||
| 347 | /** | ||
| 348 | * aa_find_namespace - look up a profile namespace on the namespace list | ||
| 349 | * @root: namespace to search in (NOT NULL) | ||
| 350 | * @name: name of namespace to find (NOT NULL) | ||
| 351 | * | ||
| 352 | * Returns: a refcounted namespace on the list, or NULL if no namespace | ||
| 353 | * called @name exists. | ||
| 354 | * | ||
| 355 | * refcount released by caller | ||
| 356 | */ | ||
| 357 | struct aa_namespace *aa_find_namespace(struct aa_namespace *root, | ||
| 358 | const char *name) | ||
| 359 | { | ||
| 360 | struct aa_namespace *ns = NULL; | ||
| 361 | |||
| 362 | rcu_read_lock(); | ||
| 363 | ns = aa_get_namespace(__aa_find_namespace(&root->sub_ns, name)); | ||
| 364 | rcu_read_unlock(); | ||
| 365 | |||
| 366 | return ns; | ||
| 367 | } | ||
| 368 | |||
| 369 | /** | ||
| 370 | * aa_prepare_namespace - find an existing or create a new namespace of @name | ||
| 371 | * @name: the namespace to find or add (MAYBE NULL) | ||
| 372 | * | ||
| 373 | * Returns: refcounted namespace or NULL if failed to create one | ||
| 374 | */ | ||
| 375 | static struct aa_namespace *aa_prepare_namespace(const char *name) | ||
| 376 | { | ||
| 377 | struct aa_namespace *ns, *root; | ||
| 378 | |||
| 379 | root = aa_current_profile()->ns; | ||
| 380 | 106 | ||
| 381 | mutex_lock(&root->lock); | 107 | tmp = rcu_dereference_protected(orig->proxy->profile, |
| 382 | 108 | mutex_is_locked(&orig->ns->lock)); | |
| 383 | /* if name isn't specified the profile is loaded to the current ns */ | 109 | rcu_assign_pointer(orig->proxy->profile, aa_get_profile(new)); |
| 384 | if (!name) { | 110 | orig->flags |= PFLAG_STALE; |
| 385 | /* released by caller */ | 111 | aa_put_profile(tmp); |
| 386 | ns = aa_get_namespace(root); | ||
| 387 | goto out; | ||
| 388 | } | ||
| 389 | |||
| 390 | /* try and find the specified ns and if it doesn't exist create it */ | ||
| 391 | /* released by caller */ | ||
| 392 | ns = aa_get_namespace(__aa_find_namespace(&root->sub_ns, name)); | ||
| 393 | if (!ns) { | ||
| 394 | ns = alloc_namespace(root->base.hname, name); | ||
| 395 | if (!ns) | ||
| 396 | goto out; | ||
| 397 | if (__aa_fs_namespace_mkdir(ns, ns_subns_dir(root), name)) { | ||
| 398 | AA_ERROR("Failed to create interface for ns %s\n", | ||
| 399 | ns->base.name); | ||
| 400 | free_namespace(ns); | ||
| 401 | ns = NULL; | ||
| 402 | goto out; | ||
| 403 | } | ||
| 404 | ns->parent = aa_get_namespace(root); | ||
| 405 | list_add_rcu(&ns->base.list, &root->sub_ns); | ||
| 406 | /* add list ref */ | ||
| 407 | aa_get_namespace(ns); | ||
| 408 | } | ||
| 409 | out: | ||
| 410 | mutex_unlock(&root->lock); | ||
| 411 | |||
| 412 | /* return ref */ | ||
| 413 | return ns; | ||
| 414 | } | 112 | } |
| 415 | 113 | ||
| 416 | /** | 114 | /** |
| @@ -448,8 +146,6 @@ static void __list_remove_profile(struct aa_profile *profile) | |||
| 448 | aa_put_profile(profile); | 146 | aa_put_profile(profile); |
| 449 | } | 147 | } |
| 450 | 148 | ||
| 451 | static void __profile_list_release(struct list_head *head); | ||
| 452 | |||
| 453 | /** | 149 | /** |
| 454 | * __remove_profile - remove old profile, and children | 150 | * __remove_profile - remove old profile, and children |
| 455 | * @profile: profile to be replaced (NOT NULL) | 151 | * @profile: profile to be replaced (NOT NULL) |
| @@ -459,122 +155,56 @@ static void __profile_list_release(struct list_head *head); | |||
| 459 | static void __remove_profile(struct aa_profile *profile) | 155 | static void __remove_profile(struct aa_profile *profile) |
| 460 | { | 156 | { |
| 461 | /* release any children lists first */ | 157 | /* release any children lists first */ |
| 462 | __profile_list_release(&profile->base.profiles); | 158 | __aa_profile_list_release(&profile->base.profiles); |
| 463 | /* released by free_profile */ | 159 | /* released by free_profile */ |
| 464 | __aa_update_replacedby(profile, profile->ns->unconfined); | 160 | __aa_update_proxy(profile, profile->ns->unconfined); |
| 465 | __aa_fs_profile_rmdir(profile); | 161 | __aa_fs_profile_rmdir(profile); |
| 466 | __list_remove_profile(profile); | 162 | __list_remove_profile(profile); |
| 467 | } | 163 | } |
| 468 | 164 | ||
| 469 | /** | 165 | /** |
| 470 | * __profile_list_release - remove all profiles on the list and put refs | 166 | * __aa_profile_list_release - remove all profiles on the list and put refs |
| 471 | * @head: list of profiles (NOT NULL) | 167 | * @head: list of profiles (NOT NULL) |
| 472 | * | 168 | * |
| 473 | * Requires: namespace lock be held | 169 | * Requires: namespace lock be held |
| 474 | */ | 170 | */ |
| 475 | static void __profile_list_release(struct list_head *head) | 171 | void __aa_profile_list_release(struct list_head *head) |
| 476 | { | 172 | { |
| 477 | struct aa_profile *profile, *tmp; | 173 | struct aa_profile *profile, *tmp; |
| 478 | list_for_each_entry_safe(profile, tmp, head, base.list) | 174 | list_for_each_entry_safe(profile, tmp, head, base.list) |
| 479 | __remove_profile(profile); | 175 | __remove_profile(profile); |
| 480 | } | 176 | } |
| 481 | 177 | ||
| 482 | static void __ns_list_release(struct list_head *head); | ||
| 483 | 178 | ||
| 484 | /** | 179 | static void free_proxy(struct aa_proxy *p) |
| 485 | * destroy_namespace - remove everything contained by @ns | ||
| 486 | * @ns: namespace to have it contents removed (NOT NULL) | ||
| 487 | */ | ||
| 488 | static void destroy_namespace(struct aa_namespace *ns) | ||
| 489 | { | 180 | { |
| 490 | if (!ns) | 181 | if (p) { |
| 491 | return; | 182 | /* r->profile will not be updated any more as r is dead */ |
| 492 | 183 | aa_put_profile(rcu_dereference_protected(p->profile, true)); | |
| 493 | mutex_lock(&ns->lock); | 184 | kzfree(p); |
| 494 | /* release all profiles in this namespace */ | 185 | } |
| 495 | __profile_list_release(&ns->base.profiles); | ||
| 496 | |||
| 497 | /* release all sub namespaces */ | ||
| 498 | __ns_list_release(&ns->sub_ns); | ||
| 499 | |||
| 500 | if (ns->parent) | ||
| 501 | __aa_update_replacedby(ns->unconfined, ns->parent->unconfined); | ||
| 502 | __aa_fs_namespace_rmdir(ns); | ||
| 503 | mutex_unlock(&ns->lock); | ||
| 504 | } | 186 | } |
| 505 | 187 | ||
| 506 | /** | ||
| 507 | * __remove_namespace - remove a namespace and all its children | ||
| 508 | * @ns: namespace to be removed (NOT NULL) | ||
| 509 | * | ||
| 510 | * Requires: ns->parent->lock be held and ns removed from parent. | ||
| 511 | */ | ||
| 512 | static void __remove_namespace(struct aa_namespace *ns) | ||
| 513 | { | ||
| 514 | /* remove ns from namespace list */ | ||
| 515 | list_del_rcu(&ns->base.list); | ||
| 516 | destroy_namespace(ns); | ||
| 517 | aa_put_namespace(ns); | ||
| 518 | } | ||
| 519 | 188 | ||
| 520 | /** | 189 | void aa_free_proxy_kref(struct kref *kref) |
| 521 | * __ns_list_release - remove all profile namespaces on the list put refs | ||
| 522 | * @head: list of profile namespaces (NOT NULL) | ||
| 523 | * | ||
| 524 | * Requires: namespace lock be held | ||
| 525 | */ | ||
| 526 | static void __ns_list_release(struct list_head *head) | ||
| 527 | { | 190 | { |
| 528 | struct aa_namespace *ns, *tmp; | 191 | struct aa_proxy *p = container_of(kref, struct aa_proxy, count); |
| 529 | list_for_each_entry_safe(ns, tmp, head, base.list) | ||
| 530 | __remove_namespace(ns); | ||
| 531 | 192 | ||
| 193 | free_proxy(p); | ||
| 532 | } | 194 | } |
| 533 | 195 | ||
| 534 | /** | 196 | /** |
| 535 | * aa_alloc_root_ns - allocate the root profile namespace | 197 | * aa_free_data - free a data blob |
| 536 | * | 198 | * @ptr: data to free |
| 537 | * Returns: %0 on success else error | 199 | * @arg: unused |
| 538 | * | ||
| 539 | */ | 200 | */ |
| 540 | int __init aa_alloc_root_ns(void) | 201 | static void aa_free_data(void *ptr, void *arg) |
| 541 | { | 202 | { |
| 542 | /* released by aa_free_root_ns - used as list ref*/ | 203 | struct aa_data *data = ptr; |
| 543 | root_ns = alloc_namespace(NULL, "root"); | ||
| 544 | if (!root_ns) | ||
| 545 | return -ENOMEM; | ||
| 546 | |||
| 547 | return 0; | ||
| 548 | } | ||
| 549 | |||
| 550 | /** | ||
| 551 | * aa_free_root_ns - free the root profile namespace | ||
| 552 | */ | ||
| 553 | void __init aa_free_root_ns(void) | ||
| 554 | { | ||
| 555 | struct aa_namespace *ns = root_ns; | ||
| 556 | root_ns = NULL; | ||
| 557 | |||
| 558 | destroy_namespace(ns); | ||
| 559 | aa_put_namespace(ns); | ||
| 560 | } | ||
| 561 | |||
| 562 | |||
| 563 | static void free_replacedby(struct aa_replacedby *r) | ||
| 564 | { | ||
| 565 | if (r) { | ||
| 566 | /* r->profile will not be updated any more as r is dead */ | ||
| 567 | aa_put_profile(rcu_dereference_protected(r->profile, true)); | ||
| 568 | kzfree(r); | ||
| 569 | } | ||
| 570 | } | ||
| 571 | 204 | ||
| 572 | 205 | kzfree(data->data); | |
| 573 | void aa_free_replacedby_kref(struct kref *kref) | 206 | kzfree(data->key); |
| 574 | { | 207 | kzfree(data); |
| 575 | struct aa_replacedby *r = container_of(kref, struct aa_replacedby, | ||
| 576 | count); | ||
| 577 | free_replacedby(r); | ||
| 578 | } | 208 | } |
| 579 | 209 | ||
| 580 | /** | 210 | /** |
| @@ -589,16 +219,18 @@ void aa_free_replacedby_kref(struct kref *kref) | |||
| 589 | */ | 219 | */ |
| 590 | void aa_free_profile(struct aa_profile *profile) | 220 | void aa_free_profile(struct aa_profile *profile) |
| 591 | { | 221 | { |
| 222 | struct rhashtable *rht; | ||
| 223 | |||
| 592 | AA_DEBUG("%s(%p)\n", __func__, profile); | 224 | AA_DEBUG("%s(%p)\n", __func__, profile); |
| 593 | 225 | ||
| 594 | if (!profile) | 226 | if (!profile) |
| 595 | return; | 227 | return; |
| 596 | 228 | ||
| 597 | /* free children profiles */ | 229 | /* free children profiles */ |
| 598 | policy_destroy(&profile->base); | 230 | aa_policy_destroy(&profile->base); |
| 599 | aa_put_profile(rcu_access_pointer(profile->parent)); | 231 | aa_put_profile(rcu_access_pointer(profile->parent)); |
| 600 | 232 | ||
| 601 | aa_put_namespace(profile->ns); | 233 | aa_put_ns(profile->ns); |
| 602 | kzfree(profile->rename); | 234 | kzfree(profile->rename); |
| 603 | 235 | ||
| 604 | aa_free_file_rules(&profile->file); | 236 | aa_free_file_rules(&profile->file); |
| @@ -608,9 +240,17 @@ void aa_free_profile(struct aa_profile *profile) | |||
| 608 | kzfree(profile->dirname); | 240 | kzfree(profile->dirname); |
| 609 | aa_put_dfa(profile->xmatch); | 241 | aa_put_dfa(profile->xmatch); |
| 610 | aa_put_dfa(profile->policy.dfa); | 242 | aa_put_dfa(profile->policy.dfa); |
| 611 | aa_put_replacedby(profile->replacedby); | 243 | aa_put_proxy(profile->proxy); |
| 244 | |||
| 245 | if (profile->data) { | ||
| 246 | rht = profile->data; | ||
| 247 | profile->data = NULL; | ||
| 248 | rhashtable_free_and_destroy(rht, aa_free_data, NULL); | ||
| 249 | kzfree(rht); | ||
| 250 | } | ||
| 612 | 251 | ||
| 613 | kzfree(profile->hash); | 252 | kzfree(profile->hash); |
| 253 | aa_put_loaddata(profile->rawdata); | ||
| 614 | kzfree(profile); | 254 | kzfree(profile); |
| 615 | } | 255 | } |
| 616 | 256 | ||
| @@ -622,7 +262,7 @@ static void aa_free_profile_rcu(struct rcu_head *head) | |||
| 622 | { | 262 | { |
| 623 | struct aa_profile *p = container_of(head, struct aa_profile, rcu); | 263 | struct aa_profile *p = container_of(head, struct aa_profile, rcu); |
| 624 | if (p->flags & PFLAG_NS_COUNT) | 264 | if (p->flags & PFLAG_NS_COUNT) |
| 625 | free_namespace(p->ns); | 265 | aa_free_ns(p->ns); |
| 626 | else | 266 | else |
| 627 | aa_free_profile(p); | 267 | aa_free_profile(p); |
| 628 | } | 268 | } |
| @@ -640,24 +280,25 @@ void aa_free_profile_kref(struct kref *kref) | |||
| 640 | /** | 280 | /** |
| 641 | * aa_alloc_profile - allocate, initialize and return a new profile | 281 | * aa_alloc_profile - allocate, initialize and return a new profile |
| 642 | * @hname: name of the profile (NOT NULL) | 282 | * @hname: name of the profile (NOT NULL) |
| 283 | * @gfp: allocation type | ||
| 643 | * | 284 | * |
| 644 | * Returns: refcount profile or NULL on failure | 285 | * Returns: refcount profile or NULL on failure |
| 645 | */ | 286 | */ |
| 646 | struct aa_profile *aa_alloc_profile(const char *hname) | 287 | struct aa_profile *aa_alloc_profile(const char *hname, gfp_t gfp) |
| 647 | { | 288 | { |
| 648 | struct aa_profile *profile; | 289 | struct aa_profile *profile; |
| 649 | 290 | ||
| 650 | /* freed by free_profile - usually through aa_put_profile */ | 291 | /* freed by free_profile - usually through aa_put_profile */ |
| 651 | profile = kzalloc(sizeof(*profile), GFP_KERNEL); | 292 | profile = kzalloc(sizeof(*profile), gfp); |
| 652 | if (!profile) | 293 | if (!profile) |
| 653 | return NULL; | 294 | return NULL; |
| 654 | 295 | ||
| 655 | profile->replacedby = kzalloc(sizeof(struct aa_replacedby), GFP_KERNEL); | 296 | profile->proxy = kzalloc(sizeof(struct aa_proxy), gfp); |
| 656 | if (!profile->replacedby) | 297 | if (!profile->proxy) |
| 657 | goto fail; | 298 | goto fail; |
| 658 | kref_init(&profile->replacedby->count); | 299 | kref_init(&profile->proxy->count); |
| 659 | 300 | ||
| 660 | if (!policy_init(&profile->base, NULL, hname)) | 301 | if (!aa_policy_init(&profile->base, NULL, hname, gfp)) |
| 661 | goto fail; | 302 | goto fail; |
| 662 | kref_init(&profile->count); | 303 | kref_init(&profile->count); |
| 663 | 304 | ||
| @@ -665,19 +306,23 @@ struct aa_profile *aa_alloc_profile(const char *hname) | |||
| 665 | return profile; | 306 | return profile; |
| 666 | 307 | ||
| 667 | fail: | 308 | fail: |
| 668 | kzfree(profile->replacedby); | 309 | kzfree(profile->proxy); |
| 669 | kzfree(profile); | 310 | kzfree(profile); |
| 670 | 311 | ||
| 671 | return NULL; | 312 | return NULL; |
| 672 | } | 313 | } |
| 673 | 314 | ||
| 674 | /** | 315 | /** |
| 675 | * aa_new_null_profile - create a new null-X learning profile | 316 | * aa_new_null_profile - create or find a null-X learning profile |
| 676 | * @parent: profile that caused this profile to be created (NOT NULL) | 317 | * @parent: profile that caused this profile to be created (NOT NULL) |
| 677 | * @hat: true if the null- learning profile is a hat | 318 | * @hat: true if the null- learning profile is a hat |
| 319 | * @base: name to base the null profile off of | ||
| 320 | * @gfp: type of allocation | ||
| 678 | * | 321 | * |
| 679 | * Create a null- complain mode profile used in learning mode. The name of | 322 | * Find/Create a null- complain mode profile used in learning mode. The |
| 680 | * the profile is unique and follows the format of parent//null-<uniq>. | 323 | * name of the profile is unique and follows the format of parent//null-XXX. |
| 324 | * where XXX is based on the @name or if that fails or is not supplied | ||
| 325 | * a unique number | ||
| 681 | * | 326 | * |
| 682 | * null profiles are added to the profile list but the list does not | 327 | * null profiles are added to the profile list but the list does not |
| 683 | * hold a count on them so that they are automatically released when | 328 | * hold a count on them so that they are automatically released when |
| @@ -685,40 +330,65 @@ fail: | |||
| 685 | * | 330 | * |
| 686 | * Returns: new refcounted profile else NULL on failure | 331 | * Returns: new refcounted profile else NULL on failure |
| 687 | */ | 332 | */ |
| 688 | struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat) | 333 | struct aa_profile *aa_new_null_profile(struct aa_profile *parent, bool hat, |
| 334 | const char *base, gfp_t gfp) | ||
| 689 | { | 335 | { |
| 690 | struct aa_profile *profile = NULL; | 336 | struct aa_profile *profile; |
| 691 | char *name; | 337 | char *name; |
| 692 | int uniq = atomic_inc_return(&parent->ns->uniq_null); | ||
| 693 | 338 | ||
| 694 | /* freed below */ | 339 | AA_BUG(!parent); |
| 695 | name = kmalloc(strlen(parent->base.hname) + 2 + 7 + 8, GFP_KERNEL); | 340 | |
| 341 | if (base) { | ||
| 342 | name = kmalloc(strlen(parent->base.hname) + 8 + strlen(base), | ||
| 343 | gfp); | ||
| 344 | if (name) { | ||
| 345 | sprintf(name, "%s//null-%s", parent->base.hname, base); | ||
| 346 | goto name; | ||
| 347 | } | ||
| 348 | /* fall through to try shorter uniq */ | ||
| 349 | } | ||
| 350 | |||
| 351 | name = kmalloc(strlen(parent->base.hname) + 2 + 7 + 8, gfp); | ||
| 696 | if (!name) | 352 | if (!name) |
| 697 | goto fail; | 353 | return NULL; |
| 698 | sprintf(name, "%s//null-%x", parent->base.hname, uniq); | 354 | sprintf(name, "%s//null-%x", parent->base.hname, |
| 355 | atomic_inc_return(&parent->ns->uniq_null)); | ||
| 699 | 356 | ||
| 700 | profile = aa_alloc_profile(name); | 357 | name: |
| 701 | kfree(name); | 358 | /* lookup to see if this is a dup creation */ |
| 359 | profile = aa_find_child(parent, basename(name)); | ||
| 360 | if (profile) | ||
| 361 | goto out; | ||
| 362 | |||
| 363 | profile = aa_alloc_profile(name, gfp); | ||
| 702 | if (!profile) | 364 | if (!profile) |
| 703 | goto fail; | 365 | goto fail; |
| 704 | 366 | ||
| 705 | profile->mode = APPARMOR_COMPLAIN; | 367 | profile->mode = APPARMOR_COMPLAIN; |
| 706 | profile->flags = PFLAG_NULL; | 368 | profile->flags |= PFLAG_NULL; |
| 707 | if (hat) | 369 | if (hat) |
| 708 | profile->flags |= PFLAG_HAT; | 370 | profile->flags |= PFLAG_HAT; |
| 371 | profile->path_flags = parent->path_flags; | ||
| 709 | 372 | ||
| 710 | /* released on free_profile */ | 373 | /* released on free_profile */ |
| 711 | rcu_assign_pointer(profile->parent, aa_get_profile(parent)); | 374 | rcu_assign_pointer(profile->parent, aa_get_profile(parent)); |
| 712 | profile->ns = aa_get_namespace(parent->ns); | 375 | profile->ns = aa_get_ns(parent->ns); |
| 376 | profile->file.dfa = aa_get_dfa(nulldfa); | ||
| 377 | profile->policy.dfa = aa_get_dfa(nulldfa); | ||
| 713 | 378 | ||
| 714 | mutex_lock(&profile->ns->lock); | 379 | mutex_lock(&profile->ns->lock); |
| 715 | __list_add_profile(&parent->base.profiles, profile); | 380 | __list_add_profile(&parent->base.profiles, profile); |
| 716 | mutex_unlock(&profile->ns->lock); | 381 | mutex_unlock(&profile->ns->lock); |
| 717 | 382 | ||
| 718 | /* refcount released by caller */ | 383 | /* refcount released by caller */ |
| 384 | out: | ||
| 385 | kfree(name); | ||
| 386 | |||
| 719 | return profile; | 387 | return profile; |
| 720 | 388 | ||
| 721 | fail: | 389 | fail: |
| 390 | kfree(name); | ||
| 391 | aa_free_profile(profile); | ||
| 722 | return NULL; | 392 | return NULL; |
| 723 | } | 393 | } |
| 724 | 394 | ||
| @@ -788,7 +458,7 @@ struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name) | |||
| 788 | * | 458 | * |
| 789 | * Returns: unrefcounted policy or NULL if not found | 459 | * Returns: unrefcounted policy or NULL if not found |
| 790 | */ | 460 | */ |
| 791 | static struct aa_policy *__lookup_parent(struct aa_namespace *ns, | 461 | static struct aa_policy *__lookup_parent(struct aa_ns *ns, |
| 792 | const char *hname) | 462 | const char *hname) |
| 793 | { | 463 | { |
| 794 | struct aa_policy *policy; | 464 | struct aa_policy *policy; |
| @@ -812,9 +482,10 @@ static struct aa_policy *__lookup_parent(struct aa_namespace *ns, | |||
| 812 | } | 482 | } |
| 813 | 483 | ||
| 814 | /** | 484 | /** |
| 815 | * __lookup_profile - lookup the profile matching @hname | 485 | * __lookupn_profile - lookup the profile matching @hname |
| 816 | * @base: base list to start looking up profile name from (NOT NULL) | 486 | * @base: base list to start looking up profile name from (NOT NULL) |
| 817 | * @hname: hierarchical profile name (NOT NULL) | 487 | * @hname: hierarchical profile name (NOT NULL) |
| 488 | * @n: length of @hname | ||
| 818 | * | 489 | * |
| 819 | * Requires: rcu_read_lock be held | 490 | * Requires: rcu_read_lock be held |
| 820 | * | 491 | * |
| @@ -822,53 +493,95 @@ static struct aa_policy *__lookup_parent(struct aa_namespace *ns, | |||
| 822 | * | 493 | * |
| 823 | * Do a relative name lookup, recursing through profile tree. | 494 | * Do a relative name lookup, recursing through profile tree. |
| 824 | */ | 495 | */ |
| 825 | static struct aa_profile *__lookup_profile(struct aa_policy *base, | 496 | static struct aa_profile *__lookupn_profile(struct aa_policy *base, |
| 826 | const char *hname) | 497 | const char *hname, size_t n) |
| 827 | { | 498 | { |
| 828 | struct aa_profile *profile = NULL; | 499 | struct aa_profile *profile = NULL; |
| 829 | char *split; | 500 | const char *split; |
| 830 | 501 | ||
| 831 | for (split = strstr(hname, "//"); split;) { | 502 | for (split = strnstr(hname, "//", n); split; |
| 503 | split = strnstr(hname, "//", n)) { | ||
| 832 | profile = __strn_find_child(&base->profiles, hname, | 504 | profile = __strn_find_child(&base->profiles, hname, |
| 833 | split - hname); | 505 | split - hname); |
| 834 | if (!profile) | 506 | if (!profile) |
| 835 | return NULL; | 507 | return NULL; |
| 836 | 508 | ||
| 837 | base = &profile->base; | 509 | base = &profile->base; |
| 510 | n -= split + 2 - hname; | ||
| 838 | hname = split + 2; | 511 | hname = split + 2; |
| 839 | split = strstr(hname, "//"); | ||
| 840 | } | 512 | } |
| 841 | 513 | ||
| 842 | profile = __find_child(&base->profiles, hname); | 514 | if (n) |
| 515 | return __strn_find_child(&base->profiles, hname, n); | ||
| 516 | return NULL; | ||
| 517 | } | ||
| 843 | 518 | ||
| 844 | return profile; | 519 | static struct aa_profile *__lookup_profile(struct aa_policy *base, |
| 520 | const char *hname) | ||
| 521 | { | ||
| 522 | return __lookupn_profile(base, hname, strlen(hname)); | ||
| 845 | } | 523 | } |
| 846 | 524 | ||
| 847 | /** | 525 | /** |
| 848 | * aa_lookup_profile - find a profile by its full or partial name | 526 | * aa_lookup_profile - find a profile by its full or partial name |
| 849 | * @ns: the namespace to start from (NOT NULL) | 527 | * @ns: the namespace to start from (NOT NULL) |
| 850 | * @hname: name to do lookup on. Does not contain namespace prefix (NOT NULL) | 528 | * @hname: name to do lookup on. Does not contain namespace prefix (NOT NULL) |
| 529 | * @n: size of @hname | ||
| 851 | * | 530 | * |
| 852 | * Returns: refcounted profile or NULL if not found | 531 | * Returns: refcounted profile or NULL if not found |
| 853 | */ | 532 | */ |
| 854 | struct aa_profile *aa_lookup_profile(struct aa_namespace *ns, const char *hname) | 533 | struct aa_profile *aa_lookupn_profile(struct aa_ns *ns, const char *hname, |
| 534 | size_t n) | ||
| 855 | { | 535 | { |
| 856 | struct aa_profile *profile; | 536 | struct aa_profile *profile; |
| 857 | 537 | ||
| 858 | rcu_read_lock(); | 538 | rcu_read_lock(); |
| 859 | do { | 539 | do { |
| 860 | profile = __lookup_profile(&ns->base, hname); | 540 | profile = __lookupn_profile(&ns->base, hname, n); |
| 861 | } while (profile && !aa_get_profile_not0(profile)); | 541 | } while (profile && !aa_get_profile_not0(profile)); |
| 862 | rcu_read_unlock(); | 542 | rcu_read_unlock(); |
| 863 | 543 | ||
| 864 | /* the unconfined profile is not in the regular profile list */ | 544 | /* the unconfined profile is not in the regular profile list */ |
| 865 | if (!profile && strcmp(hname, "unconfined") == 0) | 545 | if (!profile && strncmp(hname, "unconfined", n) == 0) |
| 866 | profile = aa_get_newest_profile(ns->unconfined); | 546 | profile = aa_get_newest_profile(ns->unconfined); |
| 867 | 547 | ||
| 868 | /* refcount released by caller */ | 548 | /* refcount released by caller */ |
| 869 | return profile; | 549 | return profile; |
| 870 | } | 550 | } |
| 871 | 551 | ||
| 552 | struct aa_profile *aa_lookup_profile(struct aa_ns *ns, const char *hname) | ||
| 553 | { | ||
| 554 | return aa_lookupn_profile(ns, hname, strlen(hname)); | ||
| 555 | } | ||
| 556 | |||
| 557 | struct aa_profile *aa_fqlookupn_profile(struct aa_profile *base, | ||
| 558 | const char *fqname, size_t n) | ||
| 559 | { | ||
| 560 | struct aa_profile *profile; | ||
| 561 | struct aa_ns *ns; | ||
| 562 | const char *name, *ns_name; | ||
| 563 | size_t ns_len; | ||
| 564 | |||
| 565 | name = aa_splitn_fqname(fqname, n, &ns_name, &ns_len); | ||
| 566 | if (ns_name) { | ||
| 567 | ns = aa_findn_ns(base->ns, ns_name, ns_len); | ||
| 568 | if (!ns) | ||
| 569 | return NULL; | ||
| 570 | } else | ||
| 571 | ns = aa_get_ns(base->ns); | ||
| 572 | |||
| 573 | if (name) | ||
| 574 | profile = aa_lookupn_profile(ns, name, n - (name - fqname)); | ||
| 575 | else if (ns) | ||
| 576 | /* default profile for ns, currently unconfined */ | ||
| 577 | profile = aa_get_newest_profile(ns->unconfined); | ||
| 578 | else | ||
| 579 | profile = NULL; | ||
| 580 | aa_put_ns(ns); | ||
| 581 | |||
| 582 | return profile; | ||
| 583 | } | ||
| 584 | |||
| 872 | /** | 585 | /** |
| 873 | * replacement_allowed - test to see if replacement is allowed | 586 | * replacement_allowed - test to see if replacement is allowed |
| 874 | * @profile: profile to test if it can be replaced (MAYBE NULL) | 587 | * @profile: profile to test if it can be replaced (MAYBE NULL) |
| @@ -892,74 +605,109 @@ static int replacement_allowed(struct aa_profile *profile, int noreplace, | |||
| 892 | return 0; | 605 | return 0; |
| 893 | } | 606 | } |
| 894 | 607 | ||
| 608 | /* audit callback for net specific fields */ | ||
| 609 | static void audit_cb(struct audit_buffer *ab, void *va) | ||
| 610 | { | ||
| 611 | struct common_audit_data *sa = va; | ||
| 612 | |||
| 613 | if (aad(sa)->iface.ns) { | ||
| 614 | audit_log_format(ab, " ns="); | ||
| 615 | audit_log_untrustedstring(ab, aad(sa)->iface.ns); | ||
| 616 | } | ||
| 617 | } | ||
| 618 | |||
| 895 | /** | 619 | /** |
| 896 | * aa_audit_policy - Do auditing of policy changes | 620 | * aa_audit_policy - Do auditing of policy changes |
| 621 | * @profile: profile to check if it can manage policy | ||
| 897 | * @op: policy operation being performed | 622 | * @op: policy operation being performed |
| 898 | * @gfp: memory allocation flags | 623 | * @gfp: memory allocation flags |
| 624 | * @nsname: name of the ns being manipulated (MAY BE NULL) | ||
| 899 | * @name: name of profile being manipulated (NOT NULL) | 625 | * @name: name of profile being manipulated (NOT NULL) |
| 900 | * @info: any extra information to be audited (MAYBE NULL) | 626 | * @info: any extra information to be audited (MAYBE NULL) |
| 901 | * @error: error code | 627 | * @error: error code |
| 902 | * | 628 | * |
| 903 | * Returns: the error to be returned after audit is done | 629 | * Returns: the error to be returned after audit is done |
| 904 | */ | 630 | */ |
| 905 | static int audit_policy(int op, gfp_t gfp, const char *name, const char *info, | 631 | static int audit_policy(struct aa_profile *profile, const char *op, |
| 906 | int error) | 632 | const char *nsname, const char *name, |
| 633 | const char *info, int error) | ||
| 907 | { | 634 | { |
| 908 | struct common_audit_data sa; | 635 | DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, op); |
| 909 | struct apparmor_audit_data aad = {0,}; | 636 | |
| 910 | sa.type = LSM_AUDIT_DATA_NONE; | 637 | aad(&sa)->iface.ns = nsname; |
| 911 | sa.aad = &aad; | 638 | aad(&sa)->name = name; |
| 912 | aad.op = op; | 639 | aad(&sa)->info = info; |
| 913 | aad.name = name; | 640 | aad(&sa)->error = error; |
| 914 | aad.info = info; | 641 | |
| 915 | aad.error = error; | 642 | return aa_audit(AUDIT_APPARMOR_STATUS, profile, &sa, audit_cb); |
| 916 | |||
| 917 | return aa_audit(AUDIT_APPARMOR_STATUS, __aa_current_profile(), gfp, | ||
| 918 | &sa, NULL); | ||
| 919 | } | 643 | } |
| 920 | 644 | ||
| 921 | bool policy_view_capable(void) | 645 | /** |
| 646 | * policy_view_capable - check if viewing policy in at @ns is allowed | ||
| 647 | * ns: namespace being viewed by current task (may be NULL) | ||
| 648 | * Returns: true if viewing policy is allowed | ||
| 649 | * | ||
| 650 | * If @ns is NULL then the namespace being viewed is assumed to be the | ||
| 651 | * tasks current namespace. | ||
| 652 | */ | ||
| 653 | bool policy_view_capable(struct aa_ns *ns) | ||
| 922 | { | 654 | { |
| 923 | struct user_namespace *user_ns = current_user_ns(); | 655 | struct user_namespace *user_ns = current_user_ns(); |
| 656 | struct aa_ns *view_ns = aa_get_current_ns(); | ||
| 657 | bool root_in_user_ns = uid_eq(current_euid(), make_kuid(user_ns, 0)) || | ||
| 658 | in_egroup_p(make_kgid(user_ns, 0)); | ||
| 924 | bool response = false; | 659 | bool response = false; |
| 660 | if (!ns) | ||
| 661 | ns = view_ns; | ||
| 925 | 662 | ||
| 926 | if (ns_capable(user_ns, CAP_MAC_ADMIN)) | 663 | if (root_in_user_ns && aa_ns_visible(view_ns, ns, true) && |
| 664 | (user_ns == &init_user_ns || | ||
| 665 | (unprivileged_userns_apparmor_policy != 0 && | ||
| 666 | user_ns->level == view_ns->level))) | ||
| 927 | response = true; | 667 | response = true; |
| 668 | aa_put_ns(view_ns); | ||
| 928 | 669 | ||
| 929 | return response; | 670 | return response; |
| 930 | } | 671 | } |
| 931 | 672 | ||
| 932 | bool policy_admin_capable(void) | 673 | bool policy_admin_capable(struct aa_ns *ns) |
| 933 | { | 674 | { |
| 934 | return policy_view_capable() && !aa_g_lock_policy; | 675 | struct user_namespace *user_ns = current_user_ns(); |
| 676 | bool capable = ns_capable(user_ns, CAP_MAC_ADMIN); | ||
| 677 | |||
| 678 | AA_DEBUG("cap_mac_admin? %d\n", capable); | ||
| 679 | AA_DEBUG("policy locked? %d\n", aa_g_lock_policy); | ||
| 680 | |||
| 681 | return policy_view_capable(ns) && capable && !aa_g_lock_policy; | ||
| 935 | } | 682 | } |
| 936 | 683 | ||
| 937 | /** | 684 | /** |
| 938 | * aa_may_manage_policy - can the current task manage policy | 685 | * aa_may_manage_policy - can the current task manage policy |
| 686 | * @profile: profile to check if it can manage policy | ||
| 939 | * @op: the policy manipulation operation being done | 687 | * @op: the policy manipulation operation being done |
| 940 | * | 688 | * |
| 941 | * Returns: true if the task is allowed to manipulate policy | 689 | * Returns: 0 if the task is allowed to manipulate policy else error |
| 942 | */ | 690 | */ |
| 943 | bool aa_may_manage_policy(int op) | 691 | int aa_may_manage_policy(struct aa_profile *profile, struct aa_ns *ns, |
| 692 | const char *op) | ||
| 944 | { | 693 | { |
| 945 | /* check if loading policy is locked out */ | 694 | /* check if loading policy is locked out */ |
| 946 | if (aa_g_lock_policy) { | 695 | if (aa_g_lock_policy) |
| 947 | audit_policy(op, GFP_KERNEL, NULL, "policy_locked", -EACCES); | 696 | return audit_policy(profile, op, NULL, NULL, |
| 948 | return 0; | 697 | "policy_locked", -EACCES); |
| 949 | } | ||
| 950 | 698 | ||
| 951 | if (!policy_admin_capable()) { | 699 | if (!policy_admin_capable(ns)) |
| 952 | audit_policy(op, GFP_KERNEL, NULL, "not policy admin", -EACCES); | 700 | return audit_policy(profile, op, NULL, NULL, |
| 953 | return 0; | 701 | "not policy admin", -EACCES); |
| 954 | } | ||
| 955 | 702 | ||
| 956 | return 1; | 703 | /* TODO: add fine grained mediation of policy loads */ |
| 704 | return 0; | ||
| 957 | } | 705 | } |
| 958 | 706 | ||
| 959 | static struct aa_profile *__list_lookup_parent(struct list_head *lh, | 707 | static struct aa_profile *__list_lookup_parent(struct list_head *lh, |
| 960 | struct aa_profile *profile) | 708 | struct aa_profile *profile) |
| 961 | { | 709 | { |
| 962 | const char *base = hname_tail(profile->base.hname); | 710 | const char *base = basename(profile->base.hname); |
| 963 | long len = base - profile->base.hname; | 711 | long len = base - profile->base.hname; |
| 964 | struct aa_load_ent *ent; | 712 | struct aa_load_ent *ent; |
| 965 | 713 | ||
| @@ -983,7 +731,7 @@ static struct aa_profile *__list_lookup_parent(struct list_head *lh, | |||
| 983 | * __replace_profile - replace @old with @new on a list | 731 | * __replace_profile - replace @old with @new on a list |
| 984 | * @old: profile to be replaced (NOT NULL) | 732 | * @old: profile to be replaced (NOT NULL) |
| 985 | * @new: profile to replace @old with (NOT NULL) | 733 | * @new: profile to replace @old with (NOT NULL) |
| 986 | * @share_replacedby: transfer @old->replacedby to @new | 734 | * @share_proxy: transfer @old->proxy to @new |
| 987 | * | 735 | * |
| 988 | * Will duplicate and refcount elements that @new inherits from @old | 736 | * Will duplicate and refcount elements that @new inherits from @old |
| 989 | * and will inherit @old children. | 737 | * and will inherit @old children. |
| @@ -993,7 +741,7 @@ static struct aa_profile *__list_lookup_parent(struct list_head *lh, | |||
| 993 | * Requires: namespace list lock be held, or list not be shared | 741 | * Requires: namespace list lock be held, or list not be shared |
| 994 | */ | 742 | */ |
| 995 | static void __replace_profile(struct aa_profile *old, struct aa_profile *new, | 743 | static void __replace_profile(struct aa_profile *old, struct aa_profile *new, |
| 996 | bool share_replacedby) | 744 | bool share_proxy) |
| 997 | { | 745 | { |
| 998 | struct aa_profile *child, *tmp; | 746 | struct aa_profile *child, *tmp; |
| 999 | 747 | ||
| @@ -1008,7 +756,7 @@ static void __replace_profile(struct aa_profile *old, struct aa_profile *new, | |||
| 1008 | p = __find_child(&new->base.profiles, child->base.name); | 756 | p = __find_child(&new->base.profiles, child->base.name); |
| 1009 | if (p) { | 757 | if (p) { |
| 1010 | /* @p replaces @child */ | 758 | /* @p replaces @child */ |
| 1011 | __replace_profile(child, p, share_replacedby); | 759 | __replace_profile(child, p, share_proxy); |
| 1012 | continue; | 760 | continue; |
| 1013 | } | 761 | } |
| 1014 | 762 | ||
| @@ -1026,13 +774,13 @@ static void __replace_profile(struct aa_profile *old, struct aa_profile *new, | |||
| 1026 | struct aa_profile *parent = aa_deref_parent(old); | 774 | struct aa_profile *parent = aa_deref_parent(old); |
| 1027 | rcu_assign_pointer(new->parent, aa_get_profile(parent)); | 775 | rcu_assign_pointer(new->parent, aa_get_profile(parent)); |
| 1028 | } | 776 | } |
| 1029 | __aa_update_replacedby(old, new); | 777 | __aa_update_proxy(old, new); |
| 1030 | if (share_replacedby) { | 778 | if (share_proxy) { |
| 1031 | aa_put_replacedby(new->replacedby); | 779 | aa_put_proxy(new->proxy); |
| 1032 | new->replacedby = aa_get_replacedby(old->replacedby); | 780 | new->proxy = aa_get_proxy(old->proxy); |
| 1033 | } else if (!rcu_access_pointer(new->replacedby->profile)) | 781 | } else if (!rcu_access_pointer(new->proxy->profile)) |
| 1034 | /* aafs interface uses replacedby */ | 782 | /* aafs interface uses proxy */ |
| 1035 | rcu_assign_pointer(new->replacedby->profile, | 783 | rcu_assign_pointer(new->proxy->profile, |
| 1036 | aa_get_profile(new)); | 784 | aa_get_profile(new)); |
| 1037 | __aa_fs_profile_migrate_dents(old, new); | 785 | __aa_fs_profile_migrate_dents(old, new); |
| 1038 | 786 | ||
| @@ -1055,7 +803,7 @@ static void __replace_profile(struct aa_profile *old, struct aa_profile *new, | |||
| 1055 | * | 803 | * |
| 1056 | * Returns: profile to replace (no ref) on success else ptr error | 804 | * Returns: profile to replace (no ref) on success else ptr error |
| 1057 | */ | 805 | */ |
| 1058 | static int __lookup_replace(struct aa_namespace *ns, const char *hname, | 806 | static int __lookup_replace(struct aa_ns *ns, const char *hname, |
| 1059 | bool noreplace, struct aa_profile **p, | 807 | bool noreplace, struct aa_profile **p, |
| 1060 | const char **info) | 808 | const char **info) |
| 1061 | { | 809 | { |
| @@ -1073,42 +821,72 @@ static int __lookup_replace(struct aa_namespace *ns, const char *hname, | |||
| 1073 | 821 | ||
| 1074 | /** | 822 | /** |
| 1075 | * aa_replace_profiles - replace profile(s) on the profile list | 823 | * aa_replace_profiles - replace profile(s) on the profile list |
| 1076 | * @udata: serialized data stream (NOT NULL) | 824 | * @view: namespace load is viewed from |
| 1077 | * @size: size of the serialized data stream | 825 | * @label: label that is attempting to load/replace policy |
| 1078 | * @noreplace: true if only doing addition, no replacement allowed | 826 | * @noreplace: true if only doing addition, no replacement allowed |
| 827 | * @udata: serialized data stream (NOT NULL) | ||
| 1079 | * | 828 | * |
| 1080 | * unpack and replace a profile on the profile list and uses of that profile | 829 | * unpack and replace a profile on the profile list and uses of that profile |
| 1081 | * by any aa_task_cxt. If the profile does not exist on the profile list | 830 | * by any aa_task_ctx. If the profile does not exist on the profile list |
| 1082 | * it is added. | 831 | * it is added. |
| 1083 | * | 832 | * |
| 1084 | * Returns: size of data consumed else error code on failure. | 833 | * Returns: size of data consumed else error code on failure. |
| 1085 | */ | 834 | */ |
| 1086 | ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) | 835 | ssize_t aa_replace_profiles(struct aa_ns *view, struct aa_profile *profile, |
| 836 | bool noreplace, struct aa_loaddata *udata) | ||
| 1087 | { | 837 | { |
| 1088 | const char *ns_name, *info = NULL; | 838 | const char *ns_name, *info = NULL; |
| 1089 | struct aa_namespace *ns = NULL; | 839 | struct aa_ns *ns = NULL; |
| 1090 | struct aa_load_ent *ent, *tmp; | 840 | struct aa_load_ent *ent, *tmp; |
| 1091 | int op = OP_PROF_REPL; | 841 | const char *op = OP_PROF_REPL; |
| 1092 | ssize_t error; | 842 | ssize_t count, error; |
| 1093 | LIST_HEAD(lh); | 843 | LIST_HEAD(lh); |
| 1094 | 844 | ||
| 1095 | /* released below */ | 845 | /* released below */ |
| 1096 | error = aa_unpack(udata, size, &lh, &ns_name); | 846 | error = aa_unpack(udata, &lh, &ns_name); |
| 1097 | if (error) | 847 | if (error) |
| 1098 | goto out; | 848 | goto out; |
| 1099 | 849 | ||
| 1100 | /* released below */ | 850 | /* ensure that profiles are all for the same ns |
| 1101 | ns = aa_prepare_namespace(ns_name); | 851 | * TODO: update locking to remove this constaint. All profiles in |
| 1102 | if (!ns) { | 852 | * the load set must succeed as a set or the load will |
| 1103 | error = audit_policy(op, GFP_KERNEL, ns_name, | 853 | * fail. Sort ent list and take ns locks in hierarchy order |
| 1104 | "failed to prepare namespace", -ENOMEM); | 854 | */ |
| 1105 | goto free; | 855 | count = 0; |
| 856 | list_for_each_entry(ent, &lh, list) { | ||
| 857 | if (ns_name) { | ||
| 858 | if (ent->ns_name && | ||
| 859 | strcmp(ent->ns_name, ns_name) != 0) { | ||
| 860 | info = "policy load has mixed namespaces"; | ||
| 861 | error = -EACCES; | ||
| 862 | goto fail; | ||
| 863 | } | ||
| 864 | } else if (ent->ns_name) { | ||
| 865 | if (count) { | ||
| 866 | info = "policy load has mixed namespaces"; | ||
| 867 | error = -EACCES; | ||
| 868 | goto fail; | ||
| 869 | } | ||
| 870 | ns_name = ent->ns_name; | ||
| 871 | } else | ||
| 872 | count++; | ||
| 1106 | } | 873 | } |
| 874 | if (ns_name) { | ||
| 875 | ns = aa_prepare_ns(view, ns_name); | ||
| 876 | if (IS_ERR(ns)) { | ||
| 877 | info = "failed to prepare namespace"; | ||
| 878 | error = PTR_ERR(ns); | ||
| 879 | ns = NULL; | ||
| 880 | goto fail; | ||
| 881 | } | ||
| 882 | } else | ||
| 883 | ns = aa_get_ns(view); | ||
| 1107 | 884 | ||
| 1108 | mutex_lock(&ns->lock); | 885 | mutex_lock(&ns->lock); |
| 1109 | /* setup parent and ns info */ | 886 | /* setup parent and ns info */ |
| 1110 | list_for_each_entry(ent, &lh, list) { | 887 | list_for_each_entry(ent, &lh, list) { |
| 1111 | struct aa_policy *policy; | 888 | struct aa_policy *policy; |
| 889 | ent->new->rawdata = aa_get_loaddata(udata); | ||
| 1112 | error = __lookup_replace(ns, ent->new->base.hname, noreplace, | 890 | error = __lookup_replace(ns, ent->new->base.hname, noreplace, |
| 1113 | &ent->old, &info); | 891 | &ent->old, &info); |
| 1114 | if (error) | 892 | if (error) |
| @@ -1123,7 +901,7 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) | |||
| 1123 | } | 901 | } |
| 1124 | 902 | ||
| 1125 | /* released when @new is freed */ | 903 | /* released when @new is freed */ |
| 1126 | ent->new->ns = aa_get_namespace(ns); | 904 | ent->new->ns = aa_get_ns(ns); |
| 1127 | 905 | ||
| 1128 | if (ent->old || ent->rename) | 906 | if (ent->old || ent->rename) |
| 1129 | continue; | 907 | continue; |
| @@ -1177,20 +955,21 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) | |||
| 1177 | list_del_init(&ent->list); | 955 | list_del_init(&ent->list); |
| 1178 | op = (!ent->old && !ent->rename) ? OP_PROF_LOAD : OP_PROF_REPL; | 956 | op = (!ent->old && !ent->rename) ? OP_PROF_LOAD : OP_PROF_REPL; |
| 1179 | 957 | ||
| 1180 | audit_policy(op, GFP_ATOMIC, ent->new->base.hname, NULL, error); | 958 | audit_policy(profile, op, NULL, ent->new->base.hname, |
| 959 | NULL, error); | ||
| 1181 | 960 | ||
| 1182 | if (ent->old) { | 961 | if (ent->old) { |
| 1183 | __replace_profile(ent->old, ent->new, 1); | 962 | __replace_profile(ent->old, ent->new, 1); |
| 1184 | if (ent->rename) { | 963 | if (ent->rename) { |
| 1185 | /* aafs interface uses replacedby */ | 964 | /* aafs interface uses proxy */ |
| 1186 | struct aa_replacedby *r = ent->new->replacedby; | 965 | struct aa_proxy *r = ent->new->proxy; |
| 1187 | rcu_assign_pointer(r->profile, | 966 | rcu_assign_pointer(r->profile, |
| 1188 | aa_get_profile(ent->new)); | 967 | aa_get_profile(ent->new)); |
| 1189 | __replace_profile(ent->rename, ent->new, 0); | 968 | __replace_profile(ent->rename, ent->new, 0); |
| 1190 | } | 969 | } |
| 1191 | } else if (ent->rename) { | 970 | } else if (ent->rename) { |
| 1192 | /* aafs interface uses replacedby */ | 971 | /* aafs interface uses proxy */ |
| 1193 | rcu_assign_pointer(ent->new->replacedby->profile, | 972 | rcu_assign_pointer(ent->new->proxy->profile, |
| 1194 | aa_get_profile(ent->new)); | 973 | aa_get_profile(ent->new)); |
| 1195 | __replace_profile(ent->rename, ent->new, 0); | 974 | __replace_profile(ent->rename, ent->new, 0); |
| 1196 | } else if (ent->new->parent) { | 975 | } else if (ent->new->parent) { |
| @@ -1204,14 +983,14 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) | |||
| 1204 | rcu_assign_pointer(ent->new->parent, newest); | 983 | rcu_assign_pointer(ent->new->parent, newest); |
| 1205 | aa_put_profile(parent); | 984 | aa_put_profile(parent); |
| 1206 | } | 985 | } |
| 1207 | /* aafs interface uses replacedby */ | 986 | /* aafs interface uses proxy */ |
| 1208 | rcu_assign_pointer(ent->new->replacedby->profile, | 987 | rcu_assign_pointer(ent->new->proxy->profile, |
| 1209 | aa_get_profile(ent->new)); | 988 | aa_get_profile(ent->new)); |
| 1210 | __list_add_profile(&newest->base.profiles, ent->new); | 989 | __list_add_profile(&newest->base.profiles, ent->new); |
| 1211 | aa_put_profile(newest); | 990 | aa_put_profile(newest); |
| 1212 | } else { | 991 | } else { |
| 1213 | /* aafs interface uses replacedby */ | 992 | /* aafs interface uses proxy */ |
| 1214 | rcu_assign_pointer(ent->new->replacedby->profile, | 993 | rcu_assign_pointer(ent->new->proxy->profile, |
| 1215 | aa_get_profile(ent->new)); | 994 | aa_get_profile(ent->new)); |
| 1216 | __list_add_profile(&ns->base.profiles, ent->new); | 995 | __list_add_profile(&ns->base.profiles, ent->new); |
| 1217 | } | 996 | } |
| @@ -1220,18 +999,20 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) | |||
| 1220 | mutex_unlock(&ns->lock); | 999 | mutex_unlock(&ns->lock); |
| 1221 | 1000 | ||
| 1222 | out: | 1001 | out: |
| 1223 | aa_put_namespace(ns); | 1002 | aa_put_ns(ns); |
| 1224 | 1003 | ||
| 1225 | if (error) | 1004 | if (error) |
| 1226 | return error; | 1005 | return error; |
| 1227 | return size; | 1006 | return udata->size; |
| 1228 | 1007 | ||
| 1229 | fail_lock: | 1008 | fail_lock: |
| 1230 | mutex_unlock(&ns->lock); | 1009 | mutex_unlock(&ns->lock); |
| 1231 | 1010 | ||
| 1232 | /* audit cause of failure */ | 1011 | /* audit cause of failure */ |
| 1233 | op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL; | 1012 | op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL; |
| 1234 | audit_policy(op, GFP_KERNEL, ent->new->base.hname, info, error); | 1013 | fail: |
| 1014 | audit_policy(profile, op, ns_name, ent->new->base.hname, | ||
| 1015 | info, error); | ||
| 1235 | /* audit status that rest of profiles in the atomic set failed too */ | 1016 | /* audit status that rest of profiles in the atomic set failed too */ |
| 1236 | info = "valid profile in failed atomic policy load"; | 1017 | info = "valid profile in failed atomic policy load"; |
| 1237 | list_for_each_entry(tmp, &lh, list) { | 1018 | list_for_each_entry(tmp, &lh, list) { |
| @@ -1241,9 +1022,9 @@ fail_lock: | |||
| 1241 | continue; | 1022 | continue; |
| 1242 | } | 1023 | } |
| 1243 | op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL; | 1024 | op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL; |
| 1244 | audit_policy(op, GFP_KERNEL, tmp->new->base.hname, info, error); | 1025 | audit_policy(profile, op, ns_name, |
| 1026 | tmp->new->base.hname, info, error); | ||
| 1245 | } | 1027 | } |
| 1246 | free: | ||
| 1247 | list_for_each_entry_safe(ent, tmp, &lh, list) { | 1028 | list_for_each_entry_safe(ent, tmp, &lh, list) { |
| 1248 | list_del_init(&ent->list); | 1029 | list_del_init(&ent->list); |
| 1249 | aa_load_ent_free(ent); | 1030 | aa_load_ent_free(ent); |
| @@ -1254,6 +1035,8 @@ free: | |||
| 1254 | 1035 | ||
| 1255 | /** | 1036 | /** |
| 1256 | * aa_remove_profiles - remove profile(s) from the system | 1037 | * aa_remove_profiles - remove profile(s) from the system |
| 1038 | * @view: namespace the remove is being done from | ||
| 1039 | * @subj: profile attempting to remove policy | ||
| 1257 | * @fqname: name of the profile or namespace to remove (NOT NULL) | 1040 | * @fqname: name of the profile or namespace to remove (NOT NULL) |
| 1258 | * @size: size of the name | 1041 | * @size: size of the name |
| 1259 | * | 1042 | * |
| @@ -1264,11 +1047,13 @@ free: | |||
| 1264 | * | 1047 | * |
| 1265 | * Returns: size of data consume else error code if fails | 1048 | * Returns: size of data consume else error code if fails |
| 1266 | */ | 1049 | */ |
| 1267 | ssize_t aa_remove_profiles(char *fqname, size_t size) | 1050 | ssize_t aa_remove_profiles(struct aa_ns *view, struct aa_profile *subj, |
| 1051 | char *fqname, size_t size) | ||
| 1268 | { | 1052 | { |
| 1269 | struct aa_namespace *root, *ns = NULL; | 1053 | struct aa_ns *root = NULL, *ns = NULL; |
| 1270 | struct aa_profile *profile = NULL; | 1054 | struct aa_profile *profile = NULL; |
| 1271 | const char *name = fqname, *info = NULL; | 1055 | const char *name = fqname, *info = NULL; |
| 1056 | char *ns_name = NULL; | ||
| 1272 | ssize_t error = 0; | 1057 | ssize_t error = 0; |
| 1273 | 1058 | ||
| 1274 | if (*fqname == 0) { | 1059 | if (*fqname == 0) { |
| @@ -1277,13 +1062,12 @@ ssize_t aa_remove_profiles(char *fqname, size_t size) | |||
| 1277 | goto fail; | 1062 | goto fail; |
| 1278 | } | 1063 | } |
| 1279 | 1064 | ||
| 1280 | root = aa_current_profile()->ns; | 1065 | root = view; |
| 1281 | 1066 | ||
| 1282 | if (fqname[0] == ':') { | 1067 | if (fqname[0] == ':') { |
| 1283 | char *ns_name; | ||
| 1284 | name = aa_split_fqname(fqname, &ns_name); | 1068 | name = aa_split_fqname(fqname, &ns_name); |
| 1285 | /* released below */ | 1069 | /* released below */ |
| 1286 | ns = aa_find_namespace(root, ns_name); | 1070 | ns = aa_find_ns(root, ns_name); |
| 1287 | if (!ns) { | 1071 | if (!ns) { |
| 1288 | info = "namespace does not exist"; | 1072 | info = "namespace does not exist"; |
| 1289 | error = -ENOENT; | 1073 | error = -ENOENT; |
| @@ -1291,12 +1075,12 @@ ssize_t aa_remove_profiles(char *fqname, size_t size) | |||
| 1291 | } | 1075 | } |
| 1292 | } else | 1076 | } else |
| 1293 | /* released below */ | 1077 | /* released below */ |
| 1294 | ns = aa_get_namespace(root); | 1078 | ns = aa_get_ns(root); |
| 1295 | 1079 | ||
| 1296 | if (!name) { | 1080 | if (!name) { |
| 1297 | /* remove namespace - can only happen if fqname[0] == ':' */ | 1081 | /* remove namespace - can only happen if fqname[0] == ':' */ |
| 1298 | mutex_lock(&ns->parent->lock); | 1082 | mutex_lock(&ns->parent->lock); |
| 1299 | __remove_namespace(ns); | 1083 | __aa_remove_ns(ns); |
| 1300 | mutex_unlock(&ns->parent->lock); | 1084 | mutex_unlock(&ns->parent->lock); |
| 1301 | } else { | 1085 | } else { |
| 1302 | /* remove profile */ | 1086 | /* remove profile */ |
| @@ -1313,16 +1097,18 @@ ssize_t aa_remove_profiles(char *fqname, size_t size) | |||
| 1313 | } | 1097 | } |
| 1314 | 1098 | ||
| 1315 | /* don't fail removal if audit fails */ | 1099 | /* don't fail removal if audit fails */ |
| 1316 | (void) audit_policy(OP_PROF_RM, GFP_KERNEL, name, info, error); | 1100 | (void) audit_policy(subj, OP_PROF_RM, ns_name, name, info, |
| 1317 | aa_put_namespace(ns); | 1101 | error); |
| 1102 | aa_put_ns(ns); | ||
| 1318 | aa_put_profile(profile); | 1103 | aa_put_profile(profile); |
| 1319 | return size; | 1104 | return size; |
| 1320 | 1105 | ||
| 1321 | fail_ns_lock: | 1106 | fail_ns_lock: |
| 1322 | mutex_unlock(&ns->lock); | 1107 | mutex_unlock(&ns->lock); |
| 1323 | aa_put_namespace(ns); | 1108 | aa_put_ns(ns); |
| 1324 | 1109 | ||
| 1325 | fail: | 1110 | fail: |
| 1326 | (void) audit_policy(OP_PROF_RM, GFP_KERNEL, name, info, error); | 1111 | (void) audit_policy(subj, OP_PROF_RM, ns_name, name, info, |
| 1112 | error); | ||
| 1327 | return error; | 1113 | return error; |
| 1328 | } | 1114 | } |
diff --git a/security/apparmor/policy_ns.c b/security/apparmor/policy_ns.c new file mode 100644 index 000000000000..93d1826c4b09 --- /dev/null +++ b/security/apparmor/policy_ns.c | |||
| @@ -0,0 +1,346 @@ | |||
| 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-2017 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 | * AppArmor policy namespaces, allow for different sets of policies | ||
| 15 | * to be loaded for tasks within the namespace. | ||
| 16 | */ | ||
| 17 | |||
| 18 | #include <linux/list.h> | ||
| 19 | #include <linux/mutex.h> | ||
| 20 | #include <linux/slab.h> | ||
| 21 | #include <linux/string.h> | ||
| 22 | |||
| 23 | #include "include/apparmor.h" | ||
| 24 | #include "include/context.h" | ||
| 25 | #include "include/policy_ns.h" | ||
| 26 | #include "include/policy.h" | ||
| 27 | |||
| 28 | /* root profile namespace */ | ||
| 29 | struct aa_ns *root_ns; | ||
| 30 | const char *aa_hidden_ns_name = "---"; | ||
| 31 | |||
| 32 | /** | ||
| 33 | * aa_ns_visible - test if @view is visible from @curr | ||
| 34 | * @curr: namespace to treat as the parent (NOT NULL) | ||
| 35 | * @view: namespace to test if visible from @curr (NOT NULL) | ||
| 36 | * @subns: whether view of a subns is allowed | ||
| 37 | * | ||
| 38 | * Returns: true if @view is visible from @curr else false | ||
| 39 | */ | ||
| 40 | bool aa_ns_visible(struct aa_ns *curr, struct aa_ns *view, bool subns) | ||
| 41 | { | ||
| 42 | if (curr == view) | ||
| 43 | return true; | ||
| 44 | |||
| 45 | if (!subns) | ||
| 46 | return false; | ||
| 47 | |||
| 48 | for ( ; view; view = view->parent) { | ||
| 49 | if (view->parent == curr) | ||
| 50 | return true; | ||
| 51 | } | ||
| 52 | |||
| 53 | return false; | ||
| 54 | } | ||
| 55 | |||
| 56 | /** | ||
| 57 | * aa_na_name - Find the ns name to display for @view from @curr | ||
| 58 | * @curr - current namespace (NOT NULL) | ||
| 59 | * @view - namespace attempting to view (NOT NULL) | ||
| 60 | * @subns - are subns visible | ||
| 61 | * | ||
| 62 | * Returns: name of @view visible from @curr | ||
| 63 | */ | ||
| 64 | const char *aa_ns_name(struct aa_ns *curr, struct aa_ns *view, bool subns) | ||
| 65 | { | ||
| 66 | /* if view == curr then the namespace name isn't displayed */ | ||
| 67 | if (curr == view) | ||
| 68 | return ""; | ||
| 69 | |||
| 70 | if (aa_ns_visible(curr, view, subns)) { | ||
| 71 | /* at this point if a ns is visible it is in a view ns | ||
| 72 | * thus the curr ns.hname is a prefix of its name. | ||
| 73 | * Only output the virtualized portion of the name | ||
| 74 | * Add + 2 to skip over // separating curr hname prefix | ||
| 75 | * from the visible tail of the views hname | ||
| 76 | */ | ||
| 77 | return view->base.hname + strlen(curr->base.hname) + 2; | ||
| 78 | } | ||
| 79 | |||
| 80 | return aa_hidden_ns_name; | ||
| 81 | } | ||
| 82 | |||
| 83 | /** | ||
| 84 | * alloc_ns - allocate, initialize and return a new namespace | ||
| 85 | * @prefix: parent namespace name (MAYBE NULL) | ||
| 86 | * @name: a preallocated name (NOT NULL) | ||
| 87 | * | ||
| 88 | * Returns: refcounted namespace or NULL on failure. | ||
| 89 | */ | ||
| 90 | static struct aa_ns *alloc_ns(const char *prefix, const char *name) | ||
| 91 | { | ||
| 92 | struct aa_ns *ns; | ||
| 93 | |||
| 94 | ns = kzalloc(sizeof(*ns), GFP_KERNEL); | ||
| 95 | AA_DEBUG("%s(%p)\n", __func__, ns); | ||
| 96 | if (!ns) | ||
| 97 | return NULL; | ||
| 98 | if (!aa_policy_init(&ns->base, prefix, name, GFP_KERNEL)) | ||
| 99 | goto fail_ns; | ||
| 100 | |||
| 101 | INIT_LIST_HEAD(&ns->sub_ns); | ||
| 102 | mutex_init(&ns->lock); | ||
| 103 | |||
| 104 | /* released by aa_free_ns() */ | ||
| 105 | ns->unconfined = aa_alloc_profile("unconfined", GFP_KERNEL); | ||
| 106 | if (!ns->unconfined) | ||
| 107 | goto fail_unconfined; | ||
| 108 | |||
| 109 | ns->unconfined->flags = PFLAG_IX_ON_NAME_ERROR | | ||
| 110 | PFLAG_IMMUTABLE | PFLAG_NS_COUNT; | ||
| 111 | ns->unconfined->mode = APPARMOR_UNCONFINED; | ||
| 112 | |||
| 113 | /* ns and ns->unconfined share ns->unconfined refcount */ | ||
| 114 | ns->unconfined->ns = ns; | ||
| 115 | |||
| 116 | atomic_set(&ns->uniq_null, 0); | ||
| 117 | |||
| 118 | return ns; | ||
| 119 | |||
| 120 | fail_unconfined: | ||
| 121 | kzfree(ns->base.hname); | ||
| 122 | fail_ns: | ||
| 123 | kzfree(ns); | ||
| 124 | return NULL; | ||
| 125 | } | ||
| 126 | |||
| 127 | /** | ||
| 128 | * aa_free_ns - free a profile namespace | ||
| 129 | * @ns: the namespace to free (MAYBE NULL) | ||
| 130 | * | ||
| 131 | * Requires: All references to the namespace must have been put, if the | ||
| 132 | * namespace was referenced by a profile confining a task, | ||
| 133 | */ | ||
| 134 | void aa_free_ns(struct aa_ns *ns) | ||
| 135 | { | ||
| 136 | if (!ns) | ||
| 137 | return; | ||
| 138 | |||
| 139 | aa_policy_destroy(&ns->base); | ||
| 140 | aa_put_ns(ns->parent); | ||
| 141 | |||
| 142 | ns->unconfined->ns = NULL; | ||
| 143 | aa_free_profile(ns->unconfined); | ||
| 144 | kzfree(ns); | ||
| 145 | } | ||
| 146 | |||
| 147 | /** | ||
| 148 | * aa_findn_ns - look up a profile namespace on the namespace list | ||
| 149 | * @root: namespace to search in (NOT NULL) | ||
| 150 | * @name: name of namespace to find (NOT NULL) | ||
| 151 | * @n: length of @name | ||
| 152 | * | ||
| 153 | * Returns: a refcounted namespace on the list, or NULL if no namespace | ||
| 154 | * called @name exists. | ||
| 155 | * | ||
| 156 | * refcount released by caller | ||
| 157 | */ | ||
| 158 | struct aa_ns *aa_findn_ns(struct aa_ns *root, const char *name, size_t n) | ||
| 159 | { | ||
| 160 | struct aa_ns *ns = NULL; | ||
| 161 | |||
| 162 | rcu_read_lock(); | ||
| 163 | ns = aa_get_ns(__aa_findn_ns(&root->sub_ns, name, n)); | ||
| 164 | rcu_read_unlock(); | ||
| 165 | |||
| 166 | return ns; | ||
| 167 | } | ||
| 168 | |||
| 169 | /** | ||
| 170 | * aa_find_ns - look up a profile namespace on the namespace list | ||
| 171 | * @root: namespace to search in (NOT NULL) | ||
| 172 | * @name: name of namespace to find (NOT NULL) | ||
| 173 | * | ||
| 174 | * Returns: a refcounted namespace on the list, or NULL if no namespace | ||
| 175 | * called @name exists. | ||
| 176 | * | ||
| 177 | * refcount released by caller | ||
| 178 | */ | ||
| 179 | struct aa_ns *aa_find_ns(struct aa_ns *root, const char *name) | ||
| 180 | { | ||
| 181 | return aa_findn_ns(root, name, strlen(name)); | ||
| 182 | } | ||
| 183 | |||
| 184 | static struct aa_ns *__aa_create_ns(struct aa_ns *parent, const char *name, | ||
| 185 | struct dentry *dir) | ||
| 186 | { | ||
| 187 | struct aa_ns *ns; | ||
| 188 | int error; | ||
| 189 | |||
| 190 | AA_BUG(!parent); | ||
| 191 | AA_BUG(!name); | ||
| 192 | AA_BUG(!mutex_is_locked(&parent->lock)); | ||
| 193 | |||
| 194 | ns = alloc_ns(parent->base.hname, name); | ||
| 195 | if (!ns) | ||
| 196 | return NULL; | ||
| 197 | mutex_lock(&ns->lock); | ||
| 198 | error = __aa_fs_ns_mkdir(ns, ns_subns_dir(parent), name); | ||
| 199 | if (error) { | ||
| 200 | AA_ERROR("Failed to create interface for ns %s\n", | ||
| 201 | ns->base.name); | ||
| 202 | mutex_unlock(&ns->lock); | ||
| 203 | aa_free_ns(ns); | ||
| 204 | return ERR_PTR(error); | ||
| 205 | } | ||
| 206 | ns->parent = aa_get_ns(parent); | ||
| 207 | ns->level = parent->level + 1; | ||
| 208 | list_add_rcu(&ns->base.list, &parent->sub_ns); | ||
| 209 | /* add list ref */ | ||
| 210 | aa_get_ns(ns); | ||
| 211 | mutex_unlock(&ns->lock); | ||
| 212 | |||
| 213 | return ns; | ||
| 214 | } | ||
| 215 | |||
| 216 | /** | ||
| 217 | * aa_create_ns - create an ns, fail if it already exists | ||
| 218 | * @parent: the parent of the namespace being created | ||
| 219 | * @name: the name of the namespace | ||
| 220 | * @dir: if not null the dir to put the ns entries in | ||
| 221 | * | ||
| 222 | * Returns: the a refcounted ns that has been add or an ERR_PTR | ||
| 223 | */ | ||
| 224 | struct aa_ns *__aa_find_or_create_ns(struct aa_ns *parent, const char *name, | ||
| 225 | struct dentry *dir) | ||
| 226 | { | ||
| 227 | struct aa_ns *ns; | ||
| 228 | |||
| 229 | AA_BUG(!mutex_is_locked(&parent->lock)); | ||
| 230 | |||
| 231 | /* try and find the specified ns */ | ||
| 232 | /* released by caller */ | ||
| 233 | ns = aa_get_ns(__aa_find_ns(&parent->sub_ns, name)); | ||
| 234 | if (!ns) | ||
| 235 | ns = __aa_create_ns(parent, name, dir); | ||
| 236 | else | ||
| 237 | ns = ERR_PTR(-EEXIST); | ||
| 238 | |||
| 239 | /* return ref */ | ||
| 240 | return ns; | ||
| 241 | } | ||
| 242 | |||
| 243 | /** | ||
| 244 | * aa_prepare_ns - find an existing or create a new namespace of @name | ||
| 245 | * @parent: ns to treat as parent | ||
| 246 | * @name: the namespace to find or add (NOT NULL) | ||
| 247 | * | ||
| 248 | * Returns: refcounted namespace or PTR_ERR if failed to create one | ||
| 249 | */ | ||
| 250 | struct aa_ns *aa_prepare_ns(struct aa_ns *parent, const char *name) | ||
| 251 | { | ||
| 252 | struct aa_ns *ns; | ||
| 253 | |||
| 254 | mutex_lock(&parent->lock); | ||
| 255 | /* try and find the specified ns and if it doesn't exist create it */ | ||
| 256 | /* released by caller */ | ||
| 257 | ns = aa_get_ns(__aa_find_ns(&parent->sub_ns, name)); | ||
| 258 | if (!ns) | ||
| 259 | ns = __aa_create_ns(parent, name, NULL); | ||
| 260 | mutex_unlock(&parent->lock); | ||
| 261 | |||
| 262 | /* return ref */ | ||
| 263 | return ns; | ||
| 264 | } | ||
| 265 | |||
| 266 | static void __ns_list_release(struct list_head *head); | ||
| 267 | |||
| 268 | /** | ||
| 269 | * destroy_ns - remove everything contained by @ns | ||
| 270 | * @ns: namespace to have it contents removed (NOT NULL) | ||
| 271 | */ | ||
| 272 | static void destroy_ns(struct aa_ns *ns) | ||
| 273 | { | ||
| 274 | if (!ns) | ||
| 275 | return; | ||
| 276 | |||
| 277 | mutex_lock(&ns->lock); | ||
| 278 | /* release all profiles in this namespace */ | ||
| 279 | __aa_profile_list_release(&ns->base.profiles); | ||
| 280 | |||
| 281 | /* release all sub namespaces */ | ||
| 282 | __ns_list_release(&ns->sub_ns); | ||
| 283 | |||
| 284 | if (ns->parent) | ||
| 285 | __aa_update_proxy(ns->unconfined, ns->parent->unconfined); | ||
| 286 | __aa_fs_ns_rmdir(ns); | ||
| 287 | mutex_unlock(&ns->lock); | ||
| 288 | } | ||
| 289 | |||
| 290 | /** | ||
| 291 | * __aa_remove_ns - remove a namespace and all its children | ||
| 292 | * @ns: namespace to be removed (NOT NULL) | ||
| 293 | * | ||
| 294 | * Requires: ns->parent->lock be held and ns removed from parent. | ||
| 295 | */ | ||
| 296 | void __aa_remove_ns(struct aa_ns *ns) | ||
| 297 | { | ||
| 298 | /* remove ns from namespace list */ | ||
| 299 | list_del_rcu(&ns->base.list); | ||
| 300 | destroy_ns(ns); | ||
| 301 | aa_put_ns(ns); | ||
| 302 | } | ||
| 303 | |||
| 304 | /** | ||
| 305 | * __ns_list_release - remove all profile namespaces on the list put refs | ||
| 306 | * @head: list of profile namespaces (NOT NULL) | ||
| 307 | * | ||
| 308 | * Requires: namespace lock be held | ||
| 309 | */ | ||
| 310 | static void __ns_list_release(struct list_head *head) | ||
| 311 | { | ||
| 312 | struct aa_ns *ns, *tmp; | ||
| 313 | |||
| 314 | list_for_each_entry_safe(ns, tmp, head, base.list) | ||
| 315 | __aa_remove_ns(ns); | ||
| 316 | |||
| 317 | } | ||
| 318 | |||
| 319 | /** | ||
| 320 | * aa_alloc_root_ns - allocate the root profile namespace | ||
| 321 | * | ||
| 322 | * Returns: %0 on success else error | ||
| 323 | * | ||
| 324 | */ | ||
| 325 | int __init aa_alloc_root_ns(void) | ||
| 326 | { | ||
| 327 | /* released by aa_free_root_ns - used as list ref*/ | ||
| 328 | root_ns = alloc_ns(NULL, "root"); | ||
| 329 | if (!root_ns) | ||
| 330 | return -ENOMEM; | ||
| 331 | |||
| 332 | return 0; | ||
| 333 | } | ||
| 334 | |||
| 335 | /** | ||
| 336 | * aa_free_root_ns - free the root profile namespace | ||
| 337 | */ | ||
| 338 | void __init aa_free_root_ns(void) | ||
| 339 | { | ||
| 340 | struct aa_ns *ns = root_ns; | ||
| 341 | |||
| 342 | root_ns = NULL; | ||
| 343 | |||
| 344 | destroy_ns(ns); | ||
| 345 | aa_put_ns(ns); | ||
| 346 | } | ||
diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 138120698f83..2e37c9c26bbd 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c | |||
| @@ -29,6 +29,15 @@ | |||
| 29 | #include "include/policy.h" | 29 | #include "include/policy.h" |
| 30 | #include "include/policy_unpack.h" | 30 | #include "include/policy_unpack.h" |
| 31 | 31 | ||
| 32 | #define K_ABI_MASK 0x3ff | ||
| 33 | #define FORCE_COMPLAIN_FLAG 0x800 | ||
| 34 | #define VERSION_LT(X, Y) (((X) & K_ABI_MASK) < ((Y) & K_ABI_MASK)) | ||
| 35 | #define VERSION_GT(X, Y) (((X) & K_ABI_MASK) > ((Y) & K_ABI_MASK)) | ||
| 36 | |||
| 37 | #define v5 5 /* base version */ | ||
| 38 | #define v6 6 /* per entry policydb mediation check */ | ||
| 39 | #define v7 7 /* full network masking */ | ||
| 40 | |||
| 32 | /* | 41 | /* |
| 33 | * The AppArmor interface treats data as a type byte followed by the | 42 | * 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 | 43 | * actual data. The interface has the notion of a a named entry |
| @@ -70,18 +79,23 @@ struct aa_ext { | |||
| 70 | static void audit_cb(struct audit_buffer *ab, void *va) | 79 | static void audit_cb(struct audit_buffer *ab, void *va) |
| 71 | { | 80 | { |
| 72 | struct common_audit_data *sa = va; | 81 | struct common_audit_data *sa = va; |
| 73 | if (sa->aad->iface.target) { | 82 | |
| 74 | struct aa_profile *name = sa->aad->iface.target; | 83 | if (aad(sa)->iface.ns) { |
| 84 | audit_log_format(ab, " ns="); | ||
| 85 | audit_log_untrustedstring(ab, aad(sa)->iface.ns); | ||
| 86 | } | ||
| 87 | if (aad(sa)->iface.name) { | ||
| 75 | audit_log_format(ab, " name="); | 88 | audit_log_format(ab, " name="); |
| 76 | audit_log_untrustedstring(ab, name->base.hname); | 89 | audit_log_untrustedstring(ab, aad(sa)->iface.name); |
| 77 | } | 90 | } |
| 78 | if (sa->aad->iface.pos) | 91 | if (aad(sa)->iface.pos) |
| 79 | audit_log_format(ab, " offset=%ld", sa->aad->iface.pos); | 92 | audit_log_format(ab, " offset=%ld", aad(sa)->iface.pos); |
| 80 | } | 93 | } |
| 81 | 94 | ||
| 82 | /** | 95 | /** |
| 83 | * audit_iface - do audit message for policy unpacking/load/replace/remove | 96 | * audit_iface - do audit message for policy unpacking/load/replace/remove |
| 84 | * @new: profile if it has been allocated (MAYBE NULL) | 97 | * @new: profile if it has been allocated (MAYBE NULL) |
| 98 | * @ns_name: name of the ns the profile is to be loaded to (MAY BE NULL) | ||
| 85 | * @name: name of the profile being manipulated (MAYBE NULL) | 99 | * @name: name of the profile being manipulated (MAYBE NULL) |
| 86 | * @info: any extra info about the failure (MAYBE NULL) | 100 | * @info: any extra info about the failure (MAYBE NULL) |
| 87 | * @e: buffer position info | 101 | * @e: buffer position info |
| @@ -89,23 +103,33 @@ static void audit_cb(struct audit_buffer *ab, void *va) | |||
| 89 | * | 103 | * |
| 90 | * Returns: %0 or error | 104 | * Returns: %0 or error |
| 91 | */ | 105 | */ |
| 92 | static int audit_iface(struct aa_profile *new, const char *name, | 106 | static int audit_iface(struct aa_profile *new, const char *ns_name, |
| 93 | const char *info, struct aa_ext *e, int error) | 107 | const char *name, const char *info, struct aa_ext *e, |
| 108 | int error) | ||
| 94 | { | 109 | { |
| 95 | struct aa_profile *profile = __aa_current_profile(); | 110 | struct aa_profile *profile = __aa_current_profile(); |
| 96 | struct common_audit_data sa; | 111 | DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, NULL); |
| 97 | struct apparmor_audit_data aad = {0,}; | ||
| 98 | sa.type = LSM_AUDIT_DATA_NONE; | ||
| 99 | sa.aad = &aad; | ||
| 100 | if (e) | 112 | if (e) |
| 101 | aad.iface.pos = e->pos - e->start; | 113 | aad(&sa)->iface.pos = e->pos - e->start; |
| 102 | aad.iface.target = new; | 114 | aad(&sa)->iface.ns = ns_name; |
| 103 | aad.name = name; | 115 | if (new) |
| 104 | aad.info = info; | 116 | aad(&sa)->iface.name = new->base.hname; |
| 105 | aad.error = error; | 117 | else |
| 106 | 118 | aad(&sa)->iface.name = name; | |
| 107 | return aa_audit(AUDIT_APPARMOR_STATUS, profile, GFP_KERNEL, &sa, | 119 | aad(&sa)->info = info; |
| 108 | audit_cb); | 120 | aad(&sa)->error = error; |
| 121 | |||
| 122 | return aa_audit(AUDIT_APPARMOR_STATUS, profile, &sa, audit_cb); | ||
| 123 | } | ||
| 124 | |||
| 125 | void aa_loaddata_kref(struct kref *kref) | ||
| 126 | { | ||
| 127 | struct aa_loaddata *d = container_of(kref, struct aa_loaddata, count); | ||
| 128 | |||
| 129 | if (d) { | ||
| 130 | kzfree(d->hash); | ||
| 131 | kvfree(d); | ||
| 132 | } | ||
| 109 | } | 133 | } |
| 110 | 134 | ||
| 111 | /* test if read will be in packed data bounds */ | 135 | /* test if read will be in packed data bounds */ |
| @@ -127,8 +151,8 @@ static size_t unpack_u16_chunk(struct aa_ext *e, char **chunk) | |||
| 127 | 151 | ||
| 128 | if (!inbounds(e, sizeof(u16))) | 152 | if (!inbounds(e, sizeof(u16))) |
| 129 | return 0; | 153 | return 0; |
| 130 | size = le16_to_cpu(get_unaligned((u16 *) e->pos)); | 154 | size = le16_to_cpu(get_unaligned((__le16 *) e->pos)); |
| 131 | e->pos += sizeof(u16); | 155 | e->pos += sizeof(__le16); |
| 132 | if (!inbounds(e, size)) | 156 | if (!inbounds(e, size)) |
| 133 | return 0; | 157 | return 0; |
| 134 | *chunk = e->pos; | 158 | *chunk = e->pos; |
| @@ -199,7 +223,7 @@ static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) | |||
| 199 | if (!inbounds(e, sizeof(u32))) | 223 | if (!inbounds(e, sizeof(u32))) |
| 200 | return 0; | 224 | return 0; |
| 201 | if (data) | 225 | if (data) |
| 202 | *data = le32_to_cpu(get_unaligned((u32 *) e->pos)); | 226 | *data = le32_to_cpu(get_unaligned((__le32 *) e->pos)); |
| 203 | e->pos += sizeof(u32); | 227 | e->pos += sizeof(u32); |
| 204 | return 1; | 228 | return 1; |
| 205 | } | 229 | } |
| @@ -212,7 +236,7 @@ static bool unpack_u64(struct aa_ext *e, u64 *data, const char *name) | |||
| 212 | if (!inbounds(e, sizeof(u64))) | 236 | if (!inbounds(e, sizeof(u64))) |
| 213 | return 0; | 237 | return 0; |
| 214 | if (data) | 238 | if (data) |
| 215 | *data = le64_to_cpu(get_unaligned((u64 *) e->pos)); | 239 | *data = le64_to_cpu(get_unaligned((__le64 *) e->pos)); |
| 216 | e->pos += sizeof(u64); | 240 | e->pos += sizeof(u64); |
| 217 | return 1; | 241 | return 1; |
| 218 | } | 242 | } |
| @@ -225,7 +249,7 @@ static size_t unpack_array(struct aa_ext *e, const char *name) | |||
| 225 | int size; | 249 | int size; |
| 226 | if (!inbounds(e, sizeof(u16))) | 250 | if (!inbounds(e, sizeof(u16))) |
| 227 | return 0; | 251 | return 0; |
| 228 | size = (int)le16_to_cpu(get_unaligned((u16 *) e->pos)); | 252 | size = (int)le16_to_cpu(get_unaligned((__le16 *) e->pos)); |
| 229 | e->pos += sizeof(u16); | 253 | e->pos += sizeof(u16); |
| 230 | return size; | 254 | return size; |
| 231 | } | 255 | } |
| @@ -238,7 +262,7 @@ static size_t unpack_blob(struct aa_ext *e, char **blob, const char *name) | |||
| 238 | u32 size; | 262 | u32 size; |
| 239 | if (!inbounds(e, sizeof(u32))) | 263 | if (!inbounds(e, sizeof(u32))) |
| 240 | return 0; | 264 | return 0; |
| 241 | size = le32_to_cpu(get_unaligned((u32 *) e->pos)); | 265 | size = le32_to_cpu(get_unaligned((__le32 *) e->pos)); |
| 242 | e->pos += sizeof(u32); | 266 | e->pos += sizeof(u32); |
| 243 | if (inbounds(e, (size_t) size)) { | 267 | if (inbounds(e, (size_t) size)) { |
| 244 | *blob = e->pos; | 268 | *blob = e->pos; |
| @@ -340,12 +364,7 @@ static struct aa_dfa *unpack_dfa(struct aa_ext *e) | |||
| 340 | ((e->pos - e->start) & 7); | 364 | ((e->pos - e->start) & 7); |
| 341 | size_t pad = ALIGN(sz, 8) - sz; | 365 | size_t pad = ALIGN(sz, 8) - sz; |
| 342 | int flags = TO_ACCEPT1_FLAG(YYTD_DATA32) | | 366 | int flags = TO_ACCEPT1_FLAG(YYTD_DATA32) | |
| 343 | TO_ACCEPT2_FLAG(YYTD_DATA32); | 367 | TO_ACCEPT2_FLAG(YYTD_DATA32) | DFA_FLAG_VERIFY_STATES; |
| 344 | |||
| 345 | |||
| 346 | if (aa_g_paranoid_load) | ||
| 347 | flags |= DFA_FLAG_VERIFY_STATES; | ||
| 348 | |||
| 349 | dfa = aa_dfa_unpack(blob + pad, size - pad, flags); | 368 | dfa = aa_dfa_unpack(blob + pad, size - pad, flags); |
| 350 | 369 | ||
| 351 | if (IS_ERR(dfa)) | 370 | if (IS_ERR(dfa)) |
| @@ -466,27 +485,67 @@ fail: | |||
| 466 | return 0; | 485 | return 0; |
| 467 | } | 486 | } |
| 468 | 487 | ||
| 488 | static void *kvmemdup(const void *src, size_t len) | ||
| 489 | { | ||
| 490 | void *p = kvmalloc(len); | ||
| 491 | |||
| 492 | if (p) | ||
| 493 | memcpy(p, src, len); | ||
| 494 | return p; | ||
| 495 | } | ||
| 496 | |||
| 497 | static u32 strhash(const void *data, u32 len, u32 seed) | ||
| 498 | { | ||
| 499 | const char * const *key = data; | ||
| 500 | |||
| 501 | return jhash(*key, strlen(*key), seed); | ||
| 502 | } | ||
| 503 | |||
| 504 | static int datacmp(struct rhashtable_compare_arg *arg, const void *obj) | ||
| 505 | { | ||
| 506 | const struct aa_data *data = obj; | ||
| 507 | const char * const *key = arg->key; | ||
| 508 | |||
| 509 | return strcmp(data->key, *key); | ||
| 510 | } | ||
| 511 | |||
| 469 | /** | 512 | /** |
| 470 | * unpack_profile - unpack a serialized profile | 513 | * unpack_profile - unpack a serialized profile |
| 471 | * @e: serialized data extent information (NOT NULL) | 514 | * @e: serialized data extent information (NOT NULL) |
| 472 | * | 515 | * |
| 473 | * NOTE: unpack profile sets audit struct if there is a failure | 516 | * NOTE: unpack profile sets audit struct if there is a failure |
| 474 | */ | 517 | */ |
| 475 | static struct aa_profile *unpack_profile(struct aa_ext *e) | 518 | static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) |
| 476 | { | 519 | { |
| 477 | struct aa_profile *profile = NULL; | 520 | struct aa_profile *profile = NULL; |
| 478 | const char *name = NULL; | 521 | const char *tmpname, *tmpns = NULL, *name = NULL; |
| 522 | size_t ns_len; | ||
| 523 | struct rhashtable_params params = { 0 }; | ||
| 524 | char *key = NULL; | ||
| 525 | struct aa_data *data; | ||
| 479 | int i, error = -EPROTO; | 526 | int i, error = -EPROTO; |
| 480 | kernel_cap_t tmpcap; | 527 | kernel_cap_t tmpcap; |
| 481 | u32 tmp; | 528 | u32 tmp; |
| 482 | 529 | ||
| 530 | *ns_name = NULL; | ||
| 531 | |||
| 483 | /* check that we have the right struct being passed */ | 532 | /* check that we have the right struct being passed */ |
| 484 | if (!unpack_nameX(e, AA_STRUCT, "profile")) | 533 | if (!unpack_nameX(e, AA_STRUCT, "profile")) |
| 485 | goto fail; | 534 | goto fail; |
| 486 | if (!unpack_str(e, &name, NULL)) | 535 | if (!unpack_str(e, &name, NULL)) |
| 487 | goto fail; | 536 | goto fail; |
| 537 | if (*name == '\0') | ||
| 538 | goto fail; | ||
| 539 | |||
| 540 | tmpname = aa_splitn_fqname(name, strlen(name), &tmpns, &ns_len); | ||
| 541 | if (tmpns) { | ||
| 542 | *ns_name = kstrndup(tmpns, ns_len, GFP_KERNEL); | ||
| 543 | if (!*ns_name) | ||
| 544 | goto fail; | ||
| 545 | name = tmpname; | ||
| 546 | } | ||
| 488 | 547 | ||
| 489 | profile = aa_alloc_profile(name); | 548 | profile = aa_alloc_profile(name, GFP_KERNEL); |
| 490 | if (!profile) | 549 | if (!profile) |
| 491 | return ERR_PTR(-ENOMEM); | 550 | return ERR_PTR(-ENOMEM); |
| 492 | 551 | ||
| @@ -519,7 +578,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) | |||
| 519 | profile->flags |= PFLAG_HAT; | 578 | profile->flags |= PFLAG_HAT; |
| 520 | if (!unpack_u32(e, &tmp, NULL)) | 579 | if (!unpack_u32(e, &tmp, NULL)) |
| 521 | goto fail; | 580 | goto fail; |
| 522 | if (tmp == PACKED_MODE_COMPLAIN) | 581 | if (tmp == PACKED_MODE_COMPLAIN || (e->version & FORCE_COMPLAIN_FLAG)) |
| 523 | profile->mode = APPARMOR_COMPLAIN; | 582 | profile->mode = APPARMOR_COMPLAIN; |
| 524 | else if (tmp == PACKED_MODE_KILL) | 583 | else if (tmp == PACKED_MODE_KILL) |
| 525 | profile->mode = APPARMOR_KILL; | 584 | profile->mode = APPARMOR_KILL; |
| @@ -599,7 +658,8 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) | |||
| 599 | } | 658 | } |
| 600 | if (!unpack_nameX(e, AA_STRUCTEND, NULL)) | 659 | if (!unpack_nameX(e, AA_STRUCTEND, NULL)) |
| 601 | goto fail; | 660 | goto fail; |
| 602 | } | 661 | } else |
| 662 | profile->policy.dfa = aa_get_dfa(nulldfa); | ||
| 603 | 663 | ||
| 604 | /* get file rules */ | 664 | /* get file rules */ |
| 605 | profile->file.dfa = unpack_dfa(e); | 665 | profile->file.dfa = unpack_dfa(e); |
| @@ -607,15 +667,59 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) | |||
| 607 | error = PTR_ERR(profile->file.dfa); | 667 | error = PTR_ERR(profile->file.dfa); |
| 608 | profile->file.dfa = NULL; | 668 | profile->file.dfa = NULL; |
| 609 | goto fail; | 669 | goto fail; |
| 610 | } | 670 | } else if (profile->file.dfa) { |
| 611 | 671 | if (!unpack_u32(e, &profile->file.start, "dfa_start")) | |
| 612 | if (!unpack_u32(e, &profile->file.start, "dfa_start")) | 672 | /* default start state */ |
| 613 | /* default start state */ | 673 | profile->file.start = DFA_START; |
| 614 | profile->file.start = DFA_START; | 674 | } else if (profile->policy.dfa && |
| 675 | profile->policy.start[AA_CLASS_FILE]) { | ||
| 676 | profile->file.dfa = aa_get_dfa(profile->policy.dfa); | ||
| 677 | profile->file.start = profile->policy.start[AA_CLASS_FILE]; | ||
| 678 | } else | ||
| 679 | profile->file.dfa = aa_get_dfa(nulldfa); | ||
| 615 | 680 | ||
| 616 | if (!unpack_trans_table(e, profile)) | 681 | if (!unpack_trans_table(e, profile)) |
| 617 | goto fail; | 682 | goto fail; |
| 618 | 683 | ||
| 684 | if (unpack_nameX(e, AA_STRUCT, "data")) { | ||
| 685 | profile->data = kzalloc(sizeof(*profile->data), GFP_KERNEL); | ||
| 686 | if (!profile->data) | ||
| 687 | goto fail; | ||
| 688 | |||
| 689 | params.nelem_hint = 3; | ||
| 690 | params.key_len = sizeof(void *); | ||
| 691 | params.key_offset = offsetof(struct aa_data, key); | ||
| 692 | params.head_offset = offsetof(struct aa_data, head); | ||
| 693 | params.hashfn = strhash; | ||
| 694 | params.obj_cmpfn = datacmp; | ||
| 695 | |||
| 696 | if (rhashtable_init(profile->data, ¶ms)) | ||
| 697 | goto fail; | ||
| 698 | |||
| 699 | while (unpack_strdup(e, &key, NULL)) { | ||
| 700 | data = kzalloc(sizeof(*data), GFP_KERNEL); | ||
| 701 | if (!data) { | ||
| 702 | kzfree(key); | ||
| 703 | goto fail; | ||
| 704 | } | ||
| 705 | |||
| 706 | data->key = key; | ||
| 707 | data->size = unpack_blob(e, &data->data, NULL); | ||
| 708 | data->data = kvmemdup(data->data, data->size); | ||
| 709 | if (data->size && !data->data) { | ||
| 710 | kzfree(data->key); | ||
| 711 | kzfree(data); | ||
| 712 | goto fail; | ||
| 713 | } | ||
| 714 | |||
| 715 | rhashtable_insert_fast(profile->data, &data->head, | ||
| 716 | profile->data->p); | ||
| 717 | } | ||
| 718 | |||
| 719 | if (!unpack_nameX(e, AA_STRUCTEND, NULL)) | ||
| 720 | goto fail; | ||
| 721 | } | ||
| 722 | |||
| 619 | if (!unpack_nameX(e, AA_STRUCTEND, NULL)) | 723 | if (!unpack_nameX(e, AA_STRUCTEND, NULL)) |
| 620 | goto fail; | 724 | goto fail; |
| 621 | 725 | ||
| @@ -626,7 +730,8 @@ fail: | |||
| 626 | name = NULL; | 730 | name = NULL; |
| 627 | else if (!name) | 731 | else if (!name) |
| 628 | name = "unknown"; | 732 | name = "unknown"; |
| 629 | audit_iface(profile, name, "failed to unpack profile", e, error); | 733 | audit_iface(profile, NULL, name, "failed to unpack profile", e, |
| 734 | error); | ||
| 630 | aa_free_profile(profile); | 735 | aa_free_profile(profile); |
| 631 | 736 | ||
| 632 | return ERR_PTR(error); | 737 | return ERR_PTR(error); |
| @@ -649,24 +754,32 @@ static int verify_header(struct aa_ext *e, int required, const char **ns) | |||
| 649 | /* get the interface version */ | 754 | /* get the interface version */ |
| 650 | if (!unpack_u32(e, &e->version, "version")) { | 755 | if (!unpack_u32(e, &e->version, "version")) { |
| 651 | if (required) { | 756 | if (required) { |
| 652 | audit_iface(NULL, NULL, "invalid profile format", e, | 757 | audit_iface(NULL, NULL, NULL, "invalid profile format", |
| 653 | error); | ||
| 654 | return error; | ||
| 655 | } | ||
| 656 | |||
| 657 | /* check that the interface version is currently supported */ | ||
| 658 | if (e->version != 5) { | ||
| 659 | audit_iface(NULL, NULL, "unsupported interface version", | ||
| 660 | e, error); | 758 | e, error); |
| 661 | return error; | 759 | return error; |
| 662 | } | 760 | } |
| 663 | } | 761 | } |
| 664 | 762 | ||
| 763 | /* Check that the interface version is currently supported. | ||
| 764 | * if not specified use previous version | ||
| 765 | * Mask off everything that is not kernel abi version | ||
| 766 | */ | ||
| 767 | if (VERSION_LT(e->version, v5) && VERSION_GT(e->version, v7)) { | ||
| 768 | audit_iface(NULL, NULL, NULL, "unsupported interface version", | ||
| 769 | e, error); | ||
| 770 | return error; | ||
| 771 | } | ||
| 665 | 772 | ||
| 666 | /* read the namespace if present */ | 773 | /* read the namespace if present */ |
| 667 | if (unpack_str(e, &name, "namespace")) { | 774 | if (unpack_str(e, &name, "namespace")) { |
| 775 | if (*name == '\0') { | ||
| 776 | audit_iface(NULL, NULL, NULL, "invalid namespace name", | ||
| 777 | e, error); | ||
| 778 | return error; | ||
| 779 | } | ||
| 668 | if (*ns && strcmp(*ns, name)) | 780 | if (*ns && strcmp(*ns, name)) |
| 669 | audit_iface(NULL, NULL, "invalid ns change", e, error); | 781 | audit_iface(NULL, NULL, NULL, "invalid ns change", e, |
| 782 | error); | ||
| 670 | else if (!*ns) | 783 | else if (!*ns) |
| 671 | *ns = name; | 784 | *ns = name; |
| 672 | } | 785 | } |
| @@ -705,14 +818,12 @@ static bool verify_dfa_xindex(struct aa_dfa *dfa, int table_size) | |||
| 705 | */ | 818 | */ |
| 706 | static int verify_profile(struct aa_profile *profile) | 819 | static int verify_profile(struct aa_profile *profile) |
| 707 | { | 820 | { |
| 708 | if (aa_g_paranoid_load) { | 821 | if (profile->file.dfa && |
| 709 | if (profile->file.dfa && | 822 | !verify_dfa_xindex(profile->file.dfa, |
| 710 | !verify_dfa_xindex(profile->file.dfa, | 823 | profile->file.trans.size)) { |
| 711 | profile->file.trans.size)) { | 824 | audit_iface(profile, NULL, NULL, "Invalid named transition", |
| 712 | audit_iface(profile, NULL, "Invalid named transition", | 825 | NULL, -EPROTO); |
| 713 | NULL, -EPROTO); | 826 | return -EPROTO; |
| 714 | return -EPROTO; | ||
| 715 | } | ||
| 716 | } | 827 | } |
| 717 | 828 | ||
| 718 | return 0; | 829 | return 0; |
| @@ -724,6 +835,7 @@ void aa_load_ent_free(struct aa_load_ent *ent) | |||
| 724 | aa_put_profile(ent->rename); | 835 | aa_put_profile(ent->rename); |
| 725 | aa_put_profile(ent->old); | 836 | aa_put_profile(ent->old); |
| 726 | aa_put_profile(ent->new); | 837 | aa_put_profile(ent->new); |
| 838 | kfree(ent->ns_name); | ||
| 727 | kzfree(ent); | 839 | kzfree(ent); |
| 728 | } | 840 | } |
| 729 | } | 841 | } |
| @@ -739,7 +851,6 @@ struct aa_load_ent *aa_load_ent_alloc(void) | |||
| 739 | /** | 851 | /** |
| 740 | * aa_unpack - unpack packed binary profile(s) data loaded from user space | 852 | * aa_unpack - unpack packed binary profile(s) data loaded from user space |
| 741 | * @udata: user data copied to kmem (NOT NULL) | 853 | * @udata: user data copied to kmem (NOT NULL) |
| 742 | * @size: the size of the user data | ||
| 743 | * @lh: list to place unpacked profiles in a aa_repl_ws | 854 | * @lh: list to place unpacked profiles in a aa_repl_ws |
| 744 | * @ns: Returns namespace profile is in if specified else NULL (NOT NULL) | 855 | * @ns: Returns namespace profile is in if specified else NULL (NOT NULL) |
| 745 | * | 856 | * |
| @@ -749,26 +860,28 @@ struct aa_load_ent *aa_load_ent_alloc(void) | |||
| 749 | * | 860 | * |
| 750 | * Returns: profile(s) on @lh else error pointer if fails to unpack | 861 | * Returns: profile(s) on @lh else error pointer if fails to unpack |
| 751 | */ | 862 | */ |
| 752 | int aa_unpack(void *udata, size_t size, struct list_head *lh, const char **ns) | 863 | int aa_unpack(struct aa_loaddata *udata, struct list_head *lh, |
| 864 | const char **ns) | ||
| 753 | { | 865 | { |
| 754 | struct aa_load_ent *tmp, *ent; | 866 | struct aa_load_ent *tmp, *ent; |
| 755 | struct aa_profile *profile = NULL; | 867 | struct aa_profile *profile = NULL; |
| 756 | int error; | 868 | int error; |
| 757 | struct aa_ext e = { | 869 | struct aa_ext e = { |
| 758 | .start = udata, | 870 | .start = udata->data, |
| 759 | .end = udata + size, | 871 | .end = udata->data + udata->size, |
| 760 | .pos = udata, | 872 | .pos = udata->data, |
| 761 | }; | 873 | }; |
| 762 | 874 | ||
| 763 | *ns = NULL; | 875 | *ns = NULL; |
| 764 | while (e.pos < e.end) { | 876 | while (e.pos < e.end) { |
| 877 | char *ns_name = NULL; | ||
| 765 | void *start; | 878 | void *start; |
| 766 | error = verify_header(&e, e.pos == e.start, ns); | 879 | error = verify_header(&e, e.pos == e.start, ns); |
| 767 | if (error) | 880 | if (error) |
| 768 | goto fail; | 881 | goto fail; |
| 769 | 882 | ||
| 770 | start = e.pos; | 883 | start = e.pos; |
| 771 | profile = unpack_profile(&e); | 884 | profile = unpack_profile(&e, &ns_name); |
| 772 | if (IS_ERR(profile)) { | 885 | if (IS_ERR(profile)) { |
| 773 | error = PTR_ERR(profile); | 886 | error = PTR_ERR(profile); |
| 774 | goto fail; | 887 | goto fail; |
| @@ -778,7 +891,8 @@ int aa_unpack(void *udata, size_t size, struct list_head *lh, const char **ns) | |||
| 778 | if (error) | 891 | if (error) |
| 779 | goto fail_profile; | 892 | goto fail_profile; |
| 780 | 893 | ||
| 781 | error = aa_calc_profile_hash(profile, e.version, start, | 894 | if (aa_g_hash_policy) |
| 895 | error = aa_calc_profile_hash(profile, e.version, start, | ||
| 782 | e.pos - start); | 896 | e.pos - start); |
| 783 | if (error) | 897 | if (error) |
| 784 | goto fail_profile; | 898 | goto fail_profile; |
| @@ -790,9 +904,18 @@ int aa_unpack(void *udata, size_t size, struct list_head *lh, const char **ns) | |||
| 790 | } | 904 | } |
| 791 | 905 | ||
| 792 | ent->new = profile; | 906 | ent->new = profile; |
| 907 | ent->ns_name = ns_name; | ||
| 793 | list_add_tail(&ent->list, lh); | 908 | list_add_tail(&ent->list, lh); |
| 794 | } | 909 | } |
| 795 | 910 | udata->abi = e.version & K_ABI_MASK; | |
| 911 | if (aa_g_hash_policy) { | ||
| 912 | udata->hash = aa_calc_hash(udata->data, udata->size); | ||
| 913 | if (IS_ERR(udata->hash)) { | ||
| 914 | error = PTR_ERR(udata->hash); | ||
| 915 | udata->hash = NULL; | ||
| 916 | goto fail; | ||
| 917 | } | ||
| 918 | } | ||
| 796 | return 0; | 919 | return 0; |
| 797 | 920 | ||
| 798 | fail_profile: | 921 | fail_profile: |
diff --git a/security/apparmor/procattr.c b/security/apparmor/procattr.c index b125acc9aa26..3466a27bca09 100644 --- a/security/apparmor/procattr.c +++ b/security/apparmor/procattr.c | |||
| @@ -15,6 +15,7 @@ | |||
| 15 | #include "include/apparmor.h" | 15 | #include "include/apparmor.h" |
| 16 | #include "include/context.h" | 16 | #include "include/context.h" |
| 17 | #include "include/policy.h" | 17 | #include "include/policy.h" |
| 18 | #include "include/policy_ns.h" | ||
| 18 | #include "include/domain.h" | 19 | #include "include/domain.h" |
| 19 | #include "include/procattr.h" | 20 | #include "include/procattr.h" |
| 20 | 21 | ||
| @@ -39,14 +40,14 @@ int aa_getprocattr(struct aa_profile *profile, char **string) | |||
| 39 | int len = 0, mode_len = 0, ns_len = 0, name_len; | 40 | int len = 0, mode_len = 0, ns_len = 0, name_len; |
| 40 | const char *mode_str = aa_profile_mode_names[profile->mode]; | 41 | const char *mode_str = aa_profile_mode_names[profile->mode]; |
| 41 | const char *ns_name = NULL; | 42 | const char *ns_name = NULL; |
| 42 | struct aa_namespace *ns = profile->ns; | 43 | struct aa_ns *ns = profile->ns; |
| 43 | struct aa_namespace *current_ns = __aa_current_profile()->ns; | 44 | struct aa_ns *current_ns = __aa_current_profile()->ns; |
| 44 | char *s; | 45 | char *s; |
| 45 | 46 | ||
| 46 | if (!aa_ns_visible(current_ns, ns)) | 47 | if (!aa_ns_visible(current_ns, ns, true)) |
| 47 | return -EACCES; | 48 | return -EACCES; |
| 48 | 49 | ||
| 49 | ns_name = aa_ns_name(current_ns, ns); | 50 | ns_name = aa_ns_name(current_ns, ns, true); |
| 50 | ns_len = strlen(ns_name); | 51 | ns_len = strlen(ns_name); |
| 51 | 52 | ||
| 52 | /* if the visible ns_name is > 0 increase size for : :// seperator */ | 53 | /* if the visible ns_name is > 0 increase size for : :// seperator */ |
| @@ -87,13 +88,13 @@ int aa_getprocattr(struct aa_profile *profile, char **string) | |||
| 87 | * | 88 | * |
| 88 | * Returns: start position of name after token else NULL on failure | 89 | * Returns: start position of name after token else NULL on failure |
| 89 | */ | 90 | */ |
| 90 | static char *split_token_from_name(int op, char *args, u64 * token) | 91 | static char *split_token_from_name(const char *op, char *args, u64 *token) |
| 91 | { | 92 | { |
| 92 | char *name; | 93 | char *name; |
| 93 | 94 | ||
| 94 | *token = simple_strtoull(args, &name, 16); | 95 | *token = simple_strtoull(args, &name, 16); |
| 95 | if ((name == args) || *name != '^') { | 96 | if ((name == args) || *name != '^') { |
| 96 | AA_ERROR("%s: Invalid input '%s'", op_table[op], args); | 97 | AA_ERROR("%s: Invalid input '%s'", op, args); |
| 97 | return ERR_PTR(-EINVAL); | 98 | return ERR_PTR(-EINVAL); |
| 98 | } | 99 | } |
| 99 | 100 | ||
| @@ -138,28 +139,13 @@ int aa_setprocattr_changehat(char *args, size_t size, int test) | |||
| 138 | for (count = 0; (hat < end) && count < 16; ++count) { | 139 | for (count = 0; (hat < end) && count < 16; ++count) { |
| 139 | char *next = hat + strlen(hat) + 1; | 140 | char *next = hat + strlen(hat) + 1; |
| 140 | hats[count] = hat; | 141 | hats[count] = hat; |
| 142 | AA_DEBUG("%s: (pid %d) Magic 0x%llx count %d hat '%s'\n" | ||
| 143 | , __func__, current->pid, token, count, hat); | ||
| 141 | hat = next; | 144 | hat = next; |
| 142 | } | 145 | } |
| 143 | } | 146 | } else |
| 144 | 147 | AA_DEBUG("%s: (pid %d) Magic 0x%llx count %d Hat '%s'\n", | |
| 145 | AA_DEBUG("%s: Magic 0x%llx Hat '%s'\n", | 148 | __func__, current->pid, token, count, "<NULL>"); |
| 146 | __func__, token, hat ? hat : NULL); | ||
| 147 | 149 | ||
| 148 | return aa_change_hat(hats, count, token, test); | 150 | return aa_change_hat(hats, count, token, test); |
| 149 | } | 151 | } |
| 150 | |||
| 151 | /** | ||
| 152 | * aa_setprocattr_changeprofile - handle procattr interface to changeprofile | ||
| 153 | * @fqname: args received from writting to /proc/<pid>/attr/current (NOT NULL) | ||
| 154 | * @onexec: true if change_profile should be delayed until exec | ||
| 155 | * @test: true if this is a test of change_profile permissions | ||
| 156 | * | ||
| 157 | * Returns: %0 or error code if change_profile fails | ||
| 158 | */ | ||
| 159 | int aa_setprocattr_changeprofile(char *fqname, bool onexec, int test) | ||
| 160 | { | ||
| 161 | char *name, *ns_name; | ||
| 162 | |||
| 163 | name = aa_split_fqname(fqname, &ns_name); | ||
| 164 | return aa_change_profile(ns_name, name, onexec, test); | ||
| 165 | } | ||
diff --git a/security/apparmor/resource.c b/security/apparmor/resource.c index 67a6072ead4b..86a941afd956 100644 --- a/security/apparmor/resource.c +++ b/security/apparmor/resource.c | |||
| @@ -35,7 +35,7 @@ static void audit_cb(struct audit_buffer *ab, void *va) | |||
| 35 | struct common_audit_data *sa = va; | 35 | struct common_audit_data *sa = va; |
| 36 | 36 | ||
| 37 | audit_log_format(ab, " rlimit=%s value=%lu", | 37 | audit_log_format(ab, " rlimit=%s value=%lu", |
| 38 | rlim_names[sa->aad->rlim.rlim], sa->aad->rlim.max); | 38 | rlim_names[aad(sa)->rlim.rlim], aad(sa)->rlim.max); |
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | /** | 41 | /** |
| @@ -50,17 +50,12 @@ static void audit_cb(struct audit_buffer *ab, void *va) | |||
| 50 | static int audit_resource(struct aa_profile *profile, unsigned int resource, | 50 | static int audit_resource(struct aa_profile *profile, unsigned int resource, |
| 51 | unsigned long value, int error) | 51 | unsigned long value, int error) |
| 52 | { | 52 | { |
| 53 | struct common_audit_data sa; | 53 | DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_SETRLIMIT); |
| 54 | struct apparmor_audit_data aad = {0,}; | 54 | |
| 55 | 55 | aad(&sa)->rlim.rlim = resource; | |
| 56 | sa.type = LSM_AUDIT_DATA_NONE; | 56 | aad(&sa)->rlim.max = value; |
| 57 | sa.aad = &aad; | 57 | aad(&sa)->error = error; |
| 58 | aad.op = OP_SETRLIMIT, | 58 | return aa_audit(AUDIT_APPARMOR_AUTO, profile, &sa, audit_cb); |
| 59 | aad.rlim.rlim = resource; | ||
| 60 | aad.rlim.max = value; | ||
| 61 | aad.error = error; | ||
| 62 | return aa_audit(AUDIT_APPARMOR_AUTO, profile, GFP_KERNEL, &sa, | ||
| 63 | audit_cb); | ||
| 64 | } | 59 | } |
| 65 | 60 | ||
| 66 | /** | 61 | /** |
diff --git a/security/apparmor/secid.c b/security/apparmor/secid.c new file mode 100644 index 000000000000..3a3edbad0b21 --- /dev/null +++ b/security/apparmor/secid.c | |||
| @@ -0,0 +1,55 @@ | |||
| 1 | /* | ||
| 2 | * AppArmor security module | ||
| 3 | * | ||
| 4 | * This file contains AppArmor security identifier (secid) 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 secid for every profile loaded. If a profile | ||
| 15 | * is replaced it receives the secid of the profile it is replacing. | ||
| 16 | * | ||
| 17 | * The secid 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/secid.h" | ||
| 25 | |||
| 26 | /* global counter from which secids are allocated */ | ||
| 27 | static u32 global_secid; | ||
| 28 | static DEFINE_SPINLOCK(secid_lock); | ||
| 29 | |||
| 30 | /* TODO FIXME: add secid to profile mapping, and secid recycling */ | ||
| 31 | |||
| 32 | /** | ||
| 33 | * aa_alloc_secid - allocate a new secid for a profile | ||
| 34 | */ | ||
| 35 | u32 aa_alloc_secid(void) | ||
| 36 | { | ||
| 37 | u32 secid; | ||
| 38 | |||
| 39 | /* | ||
| 40 | * TODO FIXME: secid recycling - part of profile mapping table | ||
| 41 | */ | ||
| 42 | spin_lock(&secid_lock); | ||
| 43 | secid = (++global_secid); | ||
| 44 | spin_unlock(&secid_lock); | ||
| 45 | return secid; | ||
| 46 | } | ||
| 47 | |||
| 48 | /** | ||
| 49 | * aa_free_secid - free a secid | ||
| 50 | * @secid: secid to free | ||
| 51 | */ | ||
| 52 | void aa_free_secid(u32 secid) | ||
| 53 | { | ||
| 54 | ; /* NOP ATM */ | ||
| 55 | } | ||
diff --git a/security/apparmor/sid.c b/security/apparmor/sid.c deleted file mode 100644 index f0b34f76ebef..000000000000 --- a/security/apparmor/sid.c +++ /dev/null | |||
| @@ -1,55 +0,0 @@ | |||
| 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/commoncap.c b/security/commoncap.c index 8df676fbd393..6d4d586b9356 100644 --- a/security/commoncap.c +++ b/security/commoncap.c | |||
| @@ -1093,7 +1093,8 @@ struct security_hook_list capability_hooks[] = { | |||
| 1093 | 1093 | ||
| 1094 | void __init capability_add_hooks(void) | 1094 | void __init capability_add_hooks(void) |
| 1095 | { | 1095 | { |
| 1096 | security_add_hooks(capability_hooks, ARRAY_SIZE(capability_hooks)); | 1096 | security_add_hooks(capability_hooks, ARRAY_SIZE(capability_hooks), |
| 1097 | "capability"); | ||
| 1097 | } | 1098 | } |
| 1098 | 1099 | ||
| 1099 | #endif /* CONFIG_SECURITY */ | 1100 | #endif /* CONFIG_SECURITY */ |
diff --git a/security/inode.c b/security/inode.c index c83db05c15ab..2cb14162ff8d 100644 --- a/security/inode.c +++ b/security/inode.c | |||
| @@ -20,6 +20,7 @@ | |||
| 20 | #include <linux/init.h> | 20 | #include <linux/init.h> |
| 21 | #include <linux/namei.h> | 21 | #include <linux/namei.h> |
| 22 | #include <linux/security.h> | 22 | #include <linux/security.h> |
| 23 | #include <linux/lsm_hooks.h> | ||
| 23 | #include <linux/magic.h> | 24 | #include <linux/magic.h> |
| 24 | 25 | ||
| 25 | static struct vfsmount *mount; | 26 | static struct vfsmount *mount; |
| @@ -204,6 +205,21 @@ void securityfs_remove(struct dentry *dentry) | |||
| 204 | } | 205 | } |
| 205 | EXPORT_SYMBOL_GPL(securityfs_remove); | 206 | EXPORT_SYMBOL_GPL(securityfs_remove); |
| 206 | 207 | ||
| 208 | #ifdef CONFIG_SECURITY | ||
| 209 | static struct dentry *lsm_dentry; | ||
| 210 | static ssize_t lsm_read(struct file *filp, char __user *buf, size_t count, | ||
| 211 | loff_t *ppos) | ||
| 212 | { | ||
| 213 | return simple_read_from_buffer(buf, count, ppos, lsm_names, | ||
| 214 | strlen(lsm_names)); | ||
| 215 | } | ||
| 216 | |||
| 217 | static const struct file_operations lsm_ops = { | ||
| 218 | .read = lsm_read, | ||
| 219 | .llseek = generic_file_llseek, | ||
| 220 | }; | ||
| 221 | #endif | ||
| 222 | |||
| 207 | static int __init securityfs_init(void) | 223 | static int __init securityfs_init(void) |
| 208 | { | 224 | { |
| 209 | int retval; | 225 | int retval; |
| @@ -213,9 +229,15 @@ static int __init securityfs_init(void) | |||
| 213 | return retval; | 229 | return retval; |
| 214 | 230 | ||
| 215 | retval = register_filesystem(&fs_type); | 231 | retval = register_filesystem(&fs_type); |
| 216 | if (retval) | 232 | if (retval) { |
| 217 | sysfs_remove_mount_point(kernel_kobj, "security"); | 233 | sysfs_remove_mount_point(kernel_kobj, "security"); |
| 218 | return retval; | 234 | return retval; |
| 235 | } | ||
| 236 | #ifdef CONFIG_SECURITY | ||
| 237 | lsm_dentry = securityfs_create_file("lsm", 0444, NULL, NULL, | ||
| 238 | &lsm_ops); | ||
| 239 | #endif | ||
| 240 | return 0; | ||
| 219 | } | 241 | } |
| 220 | 242 | ||
| 221 | core_initcall(securityfs_init); | 243 | core_initcall(securityfs_init); |
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 5e6180a4da7d..b563fbd4d122 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h | |||
| @@ -204,7 +204,7 @@ int ima_store_template(struct ima_template_entry *entry, int violation, | |||
| 204 | struct inode *inode, | 204 | struct inode *inode, |
| 205 | const unsigned char *filename, int pcr); | 205 | const unsigned char *filename, int pcr); |
| 206 | void ima_free_template_entry(struct ima_template_entry *entry); | 206 | void ima_free_template_entry(struct ima_template_entry *entry); |
| 207 | const char *ima_d_path(const struct path *path, char **pathbuf); | 207 | const char *ima_d_path(const struct path *path, char **pathbuf, char *filename); |
| 208 | 208 | ||
| 209 | /* IMA policy related functions */ | 209 | /* IMA policy related functions */ |
| 210 | int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask, | 210 | int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask, |
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index 9df26a2b75ba..c2edba8de35e 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c | |||
| @@ -157,7 +157,8 @@ err_out: | |||
| 157 | /** | 157 | /** |
| 158 | * ima_get_action - appraise & measure decision based on policy. | 158 | * ima_get_action - appraise & measure decision based on policy. |
| 159 | * @inode: pointer to inode to measure | 159 | * @inode: pointer to inode to measure |
| 160 | * @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXECUTE) | 160 | * @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXEC, |
| 161 | * MAY_APPEND) | ||
| 161 | * @func: caller identifier | 162 | * @func: caller identifier |
| 162 | * @pcr: pointer filled in if matched measure policy sets pcr= | 163 | * @pcr: pointer filled in if matched measure policy sets pcr= |
| 163 | * | 164 | * |
| @@ -318,7 +319,17 @@ void ima_audit_measurement(struct integrity_iint_cache *iint, | |||
| 318 | iint->flags |= IMA_AUDITED; | 319 | iint->flags |= IMA_AUDITED; |
| 319 | } | 320 | } |
| 320 | 321 | ||
| 321 | const char *ima_d_path(const struct path *path, char **pathbuf) | 322 | /* |
| 323 | * ima_d_path - return a pointer to the full pathname | ||
| 324 | * | ||
| 325 | * Attempt to return a pointer to the full pathname for use in the | ||
| 326 | * IMA measurement list, IMA audit records, and auditing logs. | ||
| 327 | * | ||
| 328 | * On failure, return a pointer to a copy of the filename, not dname. | ||
| 329 | * Returning a pointer to dname, could result in using the pointer | ||
| 330 | * after the memory has been freed. | ||
| 331 | */ | ||
| 332 | const char *ima_d_path(const struct path *path, char **pathbuf, char *namebuf) | ||
| 322 | { | 333 | { |
| 323 | char *pathname = NULL; | 334 | char *pathname = NULL; |
| 324 | 335 | ||
| @@ -331,5 +342,11 @@ const char *ima_d_path(const struct path *path, char **pathbuf) | |||
| 331 | pathname = NULL; | 342 | pathname = NULL; |
| 332 | } | 343 | } |
| 333 | } | 344 | } |
| 334 | return pathname ?: (const char *)path->dentry->d_name.name; | 345 | |
| 346 | if (!pathname) { | ||
| 347 | strlcpy(namebuf, path->dentry->d_name.name, NAME_MAX); | ||
| 348 | pathname = namebuf; | ||
| 349 | } | ||
| 350 | |||
| 351 | return pathname; | ||
| 335 | } | 352 | } |
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 50818c60538b..2aebb7984437 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c | |||
| @@ -83,6 +83,7 @@ static void ima_rdwr_violation_check(struct file *file, | |||
| 83 | const char **pathname) | 83 | const char **pathname) |
| 84 | { | 84 | { |
| 85 | struct inode *inode = file_inode(file); | 85 | struct inode *inode = file_inode(file); |
| 86 | char filename[NAME_MAX]; | ||
| 86 | fmode_t mode = file->f_mode; | 87 | fmode_t mode = file->f_mode; |
| 87 | bool send_tomtou = false, send_writers = false; | 88 | bool send_tomtou = false, send_writers = false; |
| 88 | 89 | ||
| @@ -102,7 +103,7 @@ static void ima_rdwr_violation_check(struct file *file, | |||
| 102 | if (!send_tomtou && !send_writers) | 103 | if (!send_tomtou && !send_writers) |
| 103 | return; | 104 | return; |
| 104 | 105 | ||
| 105 | *pathname = ima_d_path(&file->f_path, pathbuf); | 106 | *pathname = ima_d_path(&file->f_path, pathbuf, filename); |
| 106 | 107 | ||
| 107 | if (send_tomtou) | 108 | if (send_tomtou) |
| 108 | ima_add_violation(file, *pathname, iint, | 109 | ima_add_violation(file, *pathname, iint, |
| @@ -161,6 +162,7 @@ static int process_measurement(struct file *file, char *buf, loff_t size, | |||
| 161 | struct integrity_iint_cache *iint = NULL; | 162 | struct integrity_iint_cache *iint = NULL; |
| 162 | struct ima_template_desc *template_desc; | 163 | struct ima_template_desc *template_desc; |
| 163 | char *pathbuf = NULL; | 164 | char *pathbuf = NULL; |
| 165 | char filename[NAME_MAX]; | ||
| 164 | const char *pathname = NULL; | 166 | const char *pathname = NULL; |
| 165 | int rc = -ENOMEM, action, must_appraise; | 167 | int rc = -ENOMEM, action, must_appraise; |
| 166 | int pcr = CONFIG_IMA_MEASURE_PCR_IDX; | 168 | int pcr = CONFIG_IMA_MEASURE_PCR_IDX; |
| @@ -239,8 +241,8 @@ static int process_measurement(struct file *file, char *buf, loff_t size, | |||
| 239 | goto out_digsig; | 241 | goto out_digsig; |
| 240 | } | 242 | } |
| 241 | 243 | ||
| 242 | if (!pathname) /* ima_rdwr_violation possibly pre-fetched */ | 244 | if (!pathbuf) /* ima_rdwr_violation possibly pre-fetched */ |
| 243 | pathname = ima_d_path(&file->f_path, &pathbuf); | 245 | pathname = ima_d_path(&file->f_path, &pathbuf, filename); |
| 244 | 246 | ||
| 245 | if (action & IMA_MEASURE) | 247 | if (action & IMA_MEASURE) |
| 246 | ima_store_measurement(iint, file, pathname, | 248 | ima_store_measurement(iint, file, pathname, |
| @@ -307,7 +309,7 @@ int ima_bprm_check(struct linux_binprm *bprm) | |||
| 307 | /** | 309 | /** |
| 308 | * ima_path_check - based on policy, collect/store measurement. | 310 | * ima_path_check - based on policy, collect/store measurement. |
| 309 | * @file: pointer to the file to be measured | 311 | * @file: pointer to the file to be measured |
| 310 | * @mask: contains MAY_READ, MAY_WRITE or MAY_EXECUTE | 312 | * @mask: contains MAY_READ, MAY_WRITE, MAY_EXEC or MAY_APPEND |
| 311 | * | 313 | * |
| 312 | * Measure files based on the ima_must_measure() policy decision. | 314 | * Measure files based on the ima_must_measure() policy decision. |
| 313 | * | 315 | * |
| @@ -317,8 +319,8 @@ int ima_bprm_check(struct linux_binprm *bprm) | |||
| 317 | int ima_file_check(struct file *file, int mask, int opened) | 319 | int ima_file_check(struct file *file, int mask, int opened) |
| 318 | { | 320 | { |
| 319 | return process_measurement(file, NULL, 0, | 321 | return process_measurement(file, NULL, 0, |
| 320 | mask & (MAY_READ | MAY_WRITE | MAY_EXEC), | 322 | mask & (MAY_READ | MAY_WRITE | MAY_EXEC | |
| 321 | FILE_CHECK, opened); | 323 | MAY_APPEND), FILE_CHECK, opened); |
| 322 | } | 324 | } |
| 323 | EXPORT_SYMBOL_GPL(ima_file_check); | 325 | EXPORT_SYMBOL_GPL(ima_file_check); |
| 324 | 326 | ||
diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c index 17a06105ccb6..4fb315cddf5b 100644 --- a/security/keys/encrypted-keys/encrypted.c +++ b/security/keys/encrypted-keys/encrypted.c | |||
| @@ -437,7 +437,7 @@ static struct skcipher_request *init_skcipher_req(const u8 *key, | |||
| 437 | static struct key *request_master_key(struct encrypted_key_payload *epayload, | 437 | static struct key *request_master_key(struct encrypted_key_payload *epayload, |
| 438 | const u8 **master_key, size_t *master_keylen) | 438 | const u8 **master_key, size_t *master_keylen) |
| 439 | { | 439 | { |
| 440 | struct key *mkey = NULL; | 440 | struct key *mkey = ERR_PTR(-EINVAL); |
| 441 | 441 | ||
| 442 | if (!strncmp(epayload->master_desc, KEY_TRUSTED_PREFIX, | 442 | if (!strncmp(epayload->master_desc, KEY_TRUSTED_PREFIX, |
| 443 | KEY_TRUSTED_PREFIX_LEN)) { | 443 | KEY_TRUSTED_PREFIX_LEN)) { |
| @@ -985,7 +985,7 @@ static void encrypted_destroy(struct key *key) | |||
| 985 | if (!epayload) | 985 | if (!epayload) |
| 986 | return; | 986 | return; |
| 987 | 987 | ||
| 988 | memset(epayload->decrypted_data, 0, epayload->decrypted_datalen); | 988 | memzero_explicit(epayload->decrypted_data, epayload->decrypted_datalen); |
| 989 | kfree(key->payload.data[0]); | 989 | kfree(key->payload.data[0]); |
| 990 | } | 990 | } |
| 991 | 991 | ||
diff --git a/security/loadpin/loadpin.c b/security/loadpin/loadpin.c index 89a46f10b8a7..1d82eae3a5b8 100644 --- a/security/loadpin/loadpin.c +++ b/security/loadpin/loadpin.c | |||
| @@ -182,7 +182,7 @@ static struct security_hook_list loadpin_hooks[] = { | |||
| 182 | void __init loadpin_add_hooks(void) | 182 | void __init loadpin_add_hooks(void) |
| 183 | { | 183 | { |
| 184 | pr_info("ready to pin (currently %sabled)", enabled ? "en" : "dis"); | 184 | pr_info("ready to pin (currently %sabled)", enabled ? "en" : "dis"); |
| 185 | security_add_hooks(loadpin_hooks, ARRAY_SIZE(loadpin_hooks)); | 185 | security_add_hooks(loadpin_hooks, ARRAY_SIZE(loadpin_hooks), "loadpin"); |
| 186 | } | 186 | } |
| 187 | 187 | ||
| 188 | /* Should not be mutable after boot, so not listed in sysfs (perm == 0). */ | 188 | /* Should not be mutable after boot, so not listed in sysfs (perm == 0). */ |
diff --git a/security/security.c b/security/security.c index f825304f04a7..d0e07f269b2d 100644 --- a/security/security.c +++ b/security/security.c | |||
| @@ -32,6 +32,7 @@ | |||
| 32 | /* Maximum number of letters for an LSM name string */ | 32 | /* Maximum number of letters for an LSM name string */ |
| 33 | #define SECURITY_NAME_MAX 10 | 33 | #define SECURITY_NAME_MAX 10 |
| 34 | 34 | ||
| 35 | char *lsm_names; | ||
| 35 | /* Boot-time LSM user choice */ | 36 | /* Boot-time LSM user choice */ |
| 36 | static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] = | 37 | static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] = |
| 37 | CONFIG_DEFAULT_SECURITY; | 38 | CONFIG_DEFAULT_SECURITY; |
| @@ -78,6 +79,22 @@ static int __init choose_lsm(char *str) | |||
| 78 | } | 79 | } |
| 79 | __setup("security=", choose_lsm); | 80 | __setup("security=", choose_lsm); |
| 80 | 81 | ||
| 82 | static int lsm_append(char *new, char **result) | ||
| 83 | { | ||
| 84 | char *cp; | ||
| 85 | |||
| 86 | if (*result == NULL) { | ||
| 87 | *result = kstrdup(new, GFP_KERNEL); | ||
| 88 | } else { | ||
| 89 | cp = kasprintf(GFP_KERNEL, "%s,%s", *result, new); | ||
| 90 | if (cp == NULL) | ||
| 91 | return -ENOMEM; | ||
| 92 | kfree(*result); | ||
| 93 | *result = cp; | ||
| 94 | } | ||
| 95 | return 0; | ||
| 96 | } | ||
| 97 | |||
| 81 | /** | 98 | /** |
| 82 | * security_module_enable - Load given security module on boot ? | 99 | * security_module_enable - Load given security module on boot ? |
| 83 | * @module: the name of the module | 100 | * @module: the name of the module |
| @@ -97,6 +114,27 @@ int __init security_module_enable(const char *module) | |||
| 97 | return !strcmp(module, chosen_lsm); | 114 | return !strcmp(module, chosen_lsm); |
| 98 | } | 115 | } |
| 99 | 116 | ||
| 117 | /** | ||
| 118 | * security_add_hooks - Add a modules hooks to the hook lists. | ||
| 119 | * @hooks: the hooks to add | ||
| 120 | * @count: the number of hooks to add | ||
| 121 | * @lsm: the name of the security module | ||
| 122 | * | ||
| 123 | * Each LSM has to register its hooks with the infrastructure. | ||
| 124 | */ | ||
| 125 | void __init security_add_hooks(struct security_hook_list *hooks, int count, | ||
| 126 | char *lsm) | ||
| 127 | { | ||
| 128 | int i; | ||
| 129 | |||
| 130 | for (i = 0; i < count; i++) { | ||
| 131 | hooks[i].lsm = lsm; | ||
| 132 | list_add_tail_rcu(&hooks[i].list, hooks[i].head); | ||
| 133 | } | ||
| 134 | if (lsm_append(lsm, &lsm_names) < 0) | ||
| 135 | panic("%s - Cannot get early memory.\n", __func__); | ||
| 136 | } | ||
| 137 | |||
| 100 | /* | 138 | /* |
| 101 | * Hook list operation macros. | 139 | * Hook list operation macros. |
| 102 | * | 140 | * |
| @@ -1025,11 +1063,6 @@ int security_task_kill(struct task_struct *p, struct siginfo *info, | |||
| 1025 | return call_int_hook(task_kill, 0, p, info, sig, secid); | 1063 | return call_int_hook(task_kill, 0, p, info, sig, secid); |
| 1026 | } | 1064 | } |
| 1027 | 1065 | ||
| 1028 | int security_task_wait(struct task_struct *p) | ||
| 1029 | { | ||
| 1030 | return call_int_hook(task_wait, 0, p); | ||
| 1031 | } | ||
| 1032 | |||
| 1033 | int security_task_prctl(int option, unsigned long arg2, unsigned long arg3, | 1066 | int security_task_prctl(int option, unsigned long arg2, unsigned long arg3, |
| 1034 | unsigned long arg4, unsigned long arg5) | 1067 | unsigned long arg4, unsigned long arg5) |
| 1035 | { | 1068 | { |
| @@ -1170,9 +1203,9 @@ int security_getprocattr(struct task_struct *p, char *name, char **value) | |||
| 1170 | return call_int_hook(getprocattr, -EINVAL, p, name, value); | 1203 | return call_int_hook(getprocattr, -EINVAL, p, name, value); |
| 1171 | } | 1204 | } |
| 1172 | 1205 | ||
| 1173 | int security_setprocattr(struct task_struct *p, char *name, void *value, size_t size) | 1206 | int security_setprocattr(const char *name, void *value, size_t size) |
| 1174 | { | 1207 | { |
| 1175 | return call_int_hook(setprocattr, -EINVAL, p, name, value, size); | 1208 | return call_int_hook(setprocattr, -EINVAL, name, value, size); |
| 1176 | } | 1209 | } |
| 1177 | 1210 | ||
| 1178 | int security_netlink_send(struct sock *sk, struct sk_buff *skb) | 1211 | int security_netlink_send(struct sock *sk, struct sk_buff *skb) |
| @@ -1769,7 +1802,6 @@ struct security_hook_heads security_hook_heads = { | |||
| 1769 | .task_movememory = | 1802 | .task_movememory = |
| 1770 | LIST_HEAD_INIT(security_hook_heads.task_movememory), | 1803 | LIST_HEAD_INIT(security_hook_heads.task_movememory), |
| 1771 | .task_kill = LIST_HEAD_INIT(security_hook_heads.task_kill), | 1804 | .task_kill = LIST_HEAD_INIT(security_hook_heads.task_kill), |
| 1772 | .task_wait = LIST_HEAD_INIT(security_hook_heads.task_wait), | ||
| 1773 | .task_prctl = LIST_HEAD_INIT(security_hook_heads.task_prctl), | 1805 | .task_prctl = LIST_HEAD_INIT(security_hook_heads.task_prctl), |
| 1774 | .task_to_inode = | 1806 | .task_to_inode = |
| 1775 | LIST_HEAD_INIT(security_hook_heads.task_to_inode), | 1807 | LIST_HEAD_INIT(security_hook_heads.task_to_inode), |
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index c7c6619431d5..9bc12bcddc2c 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c | |||
| @@ -210,16 +210,6 @@ static inline u32 task_sid(const struct task_struct *task) | |||
| 210 | return sid; | 210 | return sid; |
| 211 | } | 211 | } |
| 212 | 212 | ||
| 213 | /* | ||
| 214 | * get the subjective security ID of the current task | ||
| 215 | */ | ||
| 216 | static inline u32 current_sid(void) | ||
| 217 | { | ||
| 218 | const struct task_security_struct *tsec = current_security(); | ||
| 219 | |||
| 220 | return tsec->sid; | ||
| 221 | } | ||
| 222 | |||
| 223 | /* Allocate and free functions for each kind of security blob. */ | 213 | /* Allocate and free functions for each kind of security blob. */ |
| 224 | 214 | ||
| 225 | static int inode_alloc_security(struct inode *inode) | 215 | static int inode_alloc_security(struct inode *inode) |
| @@ -490,8 +480,11 @@ static int selinux_is_sblabel_mnt(struct super_block *sb) | |||
| 490 | sbsec->behavior == SECURITY_FS_USE_NATIVE || | 480 | sbsec->behavior == SECURITY_FS_USE_NATIVE || |
| 491 | /* Special handling. Genfs but also in-core setxattr handler */ | 481 | /* Special handling. Genfs but also in-core setxattr handler */ |
| 492 | !strcmp(sb->s_type->name, "sysfs") || | 482 | !strcmp(sb->s_type->name, "sysfs") || |
| 483 | !strcmp(sb->s_type->name, "cgroup") || | ||
| 484 | !strcmp(sb->s_type->name, "cgroup2") || | ||
| 493 | !strcmp(sb->s_type->name, "pstore") || | 485 | !strcmp(sb->s_type->name, "pstore") || |
| 494 | !strcmp(sb->s_type->name, "debugfs") || | 486 | !strcmp(sb->s_type->name, "debugfs") || |
| 487 | !strcmp(sb->s_type->name, "tracefs") || | ||
| 495 | !strcmp(sb->s_type->name, "rootfs"); | 488 | !strcmp(sb->s_type->name, "rootfs"); |
| 496 | } | 489 | } |
| 497 | 490 | ||
| @@ -833,10 +826,14 @@ static int selinux_set_mnt_opts(struct super_block *sb, | |||
| 833 | } | 826 | } |
| 834 | 827 | ||
| 835 | /* | 828 | /* |
| 836 | * If this is a user namespace mount, no contexts are allowed | 829 | * If this is a user namespace mount and the filesystem type is not |
| 837 | * on the command line and security labels must be ignored. | 830 | * explicitly whitelisted, then no contexts are allowed on the command |
| 831 | * line and security labels must be ignored. | ||
| 838 | */ | 832 | */ |
| 839 | if (sb->s_user_ns != &init_user_ns) { | 833 | if (sb->s_user_ns != &init_user_ns && |
| 834 | strcmp(sb->s_type->name, "tmpfs") && | ||
| 835 | strcmp(sb->s_type->name, "ramfs") && | ||
| 836 | strcmp(sb->s_type->name, "devpts")) { | ||
| 840 | if (context_sid || fscontext_sid || rootcontext_sid || | 837 | if (context_sid || fscontext_sid || rootcontext_sid || |
| 841 | defcontext_sid) { | 838 | defcontext_sid) { |
| 842 | rc = -EACCES; | 839 | rc = -EACCES; |
| @@ -1268,6 +1265,8 @@ static inline int default_protocol_dgram(int protocol) | |||
| 1268 | 1265 | ||
| 1269 | static inline u16 socket_type_to_security_class(int family, int type, int protocol) | 1266 | static inline u16 socket_type_to_security_class(int family, int type, int protocol) |
| 1270 | { | 1267 | { |
| 1268 | int extsockclass = selinux_policycap_extsockclass; | ||
| 1269 | |||
| 1271 | switch (family) { | 1270 | switch (family) { |
| 1272 | case PF_UNIX: | 1271 | case PF_UNIX: |
| 1273 | switch (type) { | 1272 | switch (type) { |
| @@ -1282,13 +1281,19 @@ static inline u16 socket_type_to_security_class(int family, int type, int protoc | |||
| 1282 | case PF_INET6: | 1281 | case PF_INET6: |
| 1283 | switch (type) { | 1282 | switch (type) { |
| 1284 | case SOCK_STREAM: | 1283 | case SOCK_STREAM: |
| 1284 | case SOCK_SEQPACKET: | ||
| 1285 | if (default_protocol_stream(protocol)) | 1285 | if (default_protocol_stream(protocol)) |
| 1286 | return SECCLASS_TCP_SOCKET; | 1286 | return SECCLASS_TCP_SOCKET; |
| 1287 | else if (extsockclass && protocol == IPPROTO_SCTP) | ||
| 1288 | return SECCLASS_SCTP_SOCKET; | ||
| 1287 | else | 1289 | else |
| 1288 | return SECCLASS_RAWIP_SOCKET; | 1290 | return SECCLASS_RAWIP_SOCKET; |
| 1289 | case SOCK_DGRAM: | 1291 | case SOCK_DGRAM: |
| 1290 | if (default_protocol_dgram(protocol)) | 1292 | if (default_protocol_dgram(protocol)) |
| 1291 | return SECCLASS_UDP_SOCKET; | 1293 | return SECCLASS_UDP_SOCKET; |
| 1294 | else if (extsockclass && (protocol == IPPROTO_ICMP || | ||
| 1295 | protocol == IPPROTO_ICMPV6)) | ||
| 1296 | return SECCLASS_ICMP_SOCKET; | ||
| 1292 | else | 1297 | else |
| 1293 | return SECCLASS_RAWIP_SOCKET; | 1298 | return SECCLASS_RAWIP_SOCKET; |
| 1294 | case SOCK_DCCP: | 1299 | case SOCK_DCCP: |
| @@ -1342,6 +1347,66 @@ static inline u16 socket_type_to_security_class(int family, int type, int protoc | |||
| 1342 | return SECCLASS_APPLETALK_SOCKET; | 1347 | return SECCLASS_APPLETALK_SOCKET; |
| 1343 | } | 1348 | } |
| 1344 | 1349 | ||
| 1350 | if (extsockclass) { | ||
| 1351 | switch (family) { | ||
| 1352 | case PF_AX25: | ||
| 1353 | return SECCLASS_AX25_SOCKET; | ||
| 1354 | case PF_IPX: | ||
| 1355 | return SECCLASS_IPX_SOCKET; | ||
| 1356 | case PF_NETROM: | ||
| 1357 | return SECCLASS_NETROM_SOCKET; | ||
| 1358 | case PF_ATMPVC: | ||
| 1359 | return SECCLASS_ATMPVC_SOCKET; | ||
| 1360 | case PF_X25: | ||
| 1361 | return SECCLASS_X25_SOCKET; | ||
| 1362 | case PF_ROSE: | ||
| 1363 | return SECCLASS_ROSE_SOCKET; | ||
| 1364 | case PF_DECnet: | ||
| 1365 | return SECCLASS_DECNET_SOCKET; | ||
| 1366 | case PF_ATMSVC: | ||
| 1367 | return SECCLASS_ATMSVC_SOCKET; | ||
| 1368 | case PF_RDS: | ||
| 1369 | return SECCLASS_RDS_SOCKET; | ||
| 1370 | case PF_IRDA: | ||
| 1371 | return SECCLASS_IRDA_SOCKET; | ||
| 1372 | case PF_PPPOX: | ||
| 1373 | return SECCLASS_PPPOX_SOCKET; | ||
| 1374 | case PF_LLC: | ||
| 1375 | return SECCLASS_LLC_SOCKET; | ||
| 1376 | case PF_CAN: | ||
| 1377 | return SECCLASS_CAN_SOCKET; | ||
| 1378 | case PF_TIPC: | ||
| 1379 | return SECCLASS_TIPC_SOCKET; | ||
| 1380 | case PF_BLUETOOTH: | ||
| 1381 | return SECCLASS_BLUETOOTH_SOCKET; | ||
| 1382 | case PF_IUCV: | ||
| 1383 | return SECCLASS_IUCV_SOCKET; | ||
| 1384 | case PF_RXRPC: | ||
| 1385 | return SECCLASS_RXRPC_SOCKET; | ||
| 1386 | case PF_ISDN: | ||
| 1387 | return SECCLASS_ISDN_SOCKET; | ||
| 1388 | case PF_PHONET: | ||
| 1389 | return SECCLASS_PHONET_SOCKET; | ||
| 1390 | case PF_IEEE802154: | ||
| 1391 | return SECCLASS_IEEE802154_SOCKET; | ||
| 1392 | case PF_CAIF: | ||
| 1393 | return SECCLASS_CAIF_SOCKET; | ||
| 1394 | case PF_ALG: | ||
| 1395 | return SECCLASS_ALG_SOCKET; | ||
| 1396 | case PF_NFC: | ||
| 1397 | return SECCLASS_NFC_SOCKET; | ||
| 1398 | case PF_VSOCK: | ||
| 1399 | return SECCLASS_VSOCK_SOCKET; | ||
| 1400 | case PF_KCM: | ||
| 1401 | return SECCLASS_KCM_SOCKET; | ||
| 1402 | case PF_QIPCRTR: | ||
| 1403 | return SECCLASS_QIPCRTR_SOCKET; | ||
| 1404 | #if PF_MAX > 43 | ||
| 1405 | #error New address family defined, please update this function. | ||
| 1406 | #endif | ||
| 1407 | } | ||
| 1408 | } | ||
| 1409 | |||
| 1345 | return SECCLASS_SOCKET; | 1410 | return SECCLASS_SOCKET; |
| 1346 | } | 1411 | } |
| 1347 | 1412 | ||
| @@ -1608,55 +1673,6 @@ static inline u32 signal_to_av(int sig) | |||
| 1608 | return perm; | 1673 | return perm; |
| 1609 | } | 1674 | } |
| 1610 | 1675 | ||
| 1611 | /* | ||
| 1612 | * Check permission between a pair of credentials | ||
| 1613 | * fork check, ptrace check, etc. | ||
| 1614 | */ | ||
| 1615 | static int cred_has_perm(const struct cred *actor, | ||
| 1616 | const struct cred *target, | ||
| 1617 | u32 perms) | ||
| 1618 | { | ||
| 1619 | u32 asid = cred_sid(actor), tsid = cred_sid(target); | ||
| 1620 | |||
| 1621 | return avc_has_perm(asid, tsid, SECCLASS_PROCESS, perms, NULL); | ||
| 1622 | } | ||
| 1623 | |||
| 1624 | /* | ||
| 1625 | * Check permission between a pair of tasks, e.g. signal checks, | ||
| 1626 | * fork check, ptrace check, etc. | ||
| 1627 | * tsk1 is the actor and tsk2 is the target | ||
| 1628 | * - this uses the default subjective creds of tsk1 | ||
| 1629 | */ | ||
| 1630 | static int task_has_perm(const struct task_struct *tsk1, | ||
| 1631 | const struct task_struct *tsk2, | ||
| 1632 | u32 perms) | ||
| 1633 | { | ||
| 1634 | const struct task_security_struct *__tsec1, *__tsec2; | ||
| 1635 | u32 sid1, sid2; | ||
| 1636 | |||
| 1637 | rcu_read_lock(); | ||
| 1638 | __tsec1 = __task_cred(tsk1)->security; sid1 = __tsec1->sid; | ||
| 1639 | __tsec2 = __task_cred(tsk2)->security; sid2 = __tsec2->sid; | ||
| 1640 | rcu_read_unlock(); | ||
| 1641 | return avc_has_perm(sid1, sid2, SECCLASS_PROCESS, perms, NULL); | ||
| 1642 | } | ||
| 1643 | |||
| 1644 | /* | ||
| 1645 | * Check permission between current and another task, e.g. signal checks, | ||
| 1646 | * fork check, ptrace check, etc. | ||
| 1647 | * current is the actor and tsk2 is the target | ||
| 1648 | * - this uses current's subjective creds | ||
| 1649 | */ | ||
| 1650 | static int current_has_perm(const struct task_struct *tsk, | ||
| 1651 | u32 perms) | ||
| 1652 | { | ||
| 1653 | u32 sid, tsid; | ||
| 1654 | |||
| 1655 | sid = current_sid(); | ||
| 1656 | tsid = task_sid(tsk); | ||
| 1657 | return avc_has_perm(sid, tsid, SECCLASS_PROCESS, perms, NULL); | ||
| 1658 | } | ||
| 1659 | |||
| 1660 | #if CAP_LAST_CAP > 63 | 1676 | #if CAP_LAST_CAP > 63 |
| 1661 | #error Fix SELinux to handle capabilities > 63. | 1677 | #error Fix SELinux to handle capabilities > 63. |
| 1662 | #endif | 1678 | #endif |
| @@ -1698,16 +1714,6 @@ static int cred_has_capability(const struct cred *cred, | |||
| 1698 | return rc; | 1714 | return rc; |
| 1699 | } | 1715 | } |
| 1700 | 1716 | ||
| 1701 | /* Check whether a task is allowed to use a system operation. */ | ||
| 1702 | static int task_has_system(struct task_struct *tsk, | ||
| 1703 | u32 perms) | ||
| 1704 | { | ||
| 1705 | u32 sid = task_sid(tsk); | ||
| 1706 | |||
| 1707 | return avc_has_perm(sid, SECINITSID_KERNEL, | ||
| 1708 | SECCLASS_SYSTEM, perms, NULL); | ||
| 1709 | } | ||
| 1710 | |||
| 1711 | /* Check whether a task has a particular permission to an inode. | 1717 | /* Check whether a task has a particular permission to an inode. |
| 1712 | The 'adp' parameter is optional and allows other audit | 1718 | The 'adp' parameter is optional and allows other audit |
| 1713 | data to be passed (e.g. the dentry). */ | 1719 | data to be passed (e.g. the dentry). */ |
| @@ -1879,15 +1885,6 @@ static int may_create(struct inode *dir, | |||
| 1879 | FILESYSTEM__ASSOCIATE, &ad); | 1885 | FILESYSTEM__ASSOCIATE, &ad); |
| 1880 | } | 1886 | } |
| 1881 | 1887 | ||
| 1882 | /* Check whether a task can create a key. */ | ||
| 1883 | static int may_create_key(u32 ksid, | ||
| 1884 | struct task_struct *ctx) | ||
| 1885 | { | ||
| 1886 | u32 sid = task_sid(ctx); | ||
| 1887 | |||
| 1888 | return avc_has_perm(sid, ksid, SECCLASS_KEY, KEY__CREATE, NULL); | ||
| 1889 | } | ||
| 1890 | |||
| 1891 | #define MAY_LINK 0 | 1888 | #define MAY_LINK 0 |
| 1892 | #define MAY_UNLINK 1 | 1889 | #define MAY_UNLINK 1 |
| 1893 | #define MAY_RMDIR 2 | 1890 | #define MAY_RMDIR 2 |
| @@ -2143,24 +2140,26 @@ static int selinux_binder_transfer_file(struct task_struct *from, | |||
| 2143 | static int selinux_ptrace_access_check(struct task_struct *child, | 2140 | static int selinux_ptrace_access_check(struct task_struct *child, |
| 2144 | unsigned int mode) | 2141 | unsigned int mode) |
| 2145 | { | 2142 | { |
| 2146 | if (mode & PTRACE_MODE_READ) { | 2143 | u32 sid = current_sid(); |
| 2147 | u32 sid = current_sid(); | 2144 | u32 csid = task_sid(child); |
| 2148 | u32 csid = task_sid(child); | 2145 | |
| 2146 | if (mode & PTRACE_MODE_READ) | ||
| 2149 | return avc_has_perm(sid, csid, SECCLASS_FILE, FILE__READ, NULL); | 2147 | return avc_has_perm(sid, csid, SECCLASS_FILE, FILE__READ, NULL); |
| 2150 | } | ||
| 2151 | 2148 | ||
| 2152 | return current_has_perm(child, PROCESS__PTRACE); | 2149 | return avc_has_perm(sid, csid, SECCLASS_PROCESS, PROCESS__PTRACE, NULL); |
| 2153 | } | 2150 | } |
| 2154 | 2151 | ||
| 2155 | static int selinux_ptrace_traceme(struct task_struct *parent) | 2152 | static int selinux_ptrace_traceme(struct task_struct *parent) |
| 2156 | { | 2153 | { |
| 2157 | return task_has_perm(parent, current, PROCESS__PTRACE); | 2154 | return avc_has_perm(task_sid(parent), current_sid(), SECCLASS_PROCESS, |
| 2155 | PROCESS__PTRACE, NULL); | ||
| 2158 | } | 2156 | } |
| 2159 | 2157 | ||
| 2160 | static int selinux_capget(struct task_struct *target, kernel_cap_t *effective, | 2158 | static int selinux_capget(struct task_struct *target, kernel_cap_t *effective, |
| 2161 | kernel_cap_t *inheritable, kernel_cap_t *permitted) | 2159 | kernel_cap_t *inheritable, kernel_cap_t *permitted) |
| 2162 | { | 2160 | { |
| 2163 | return current_has_perm(target, PROCESS__GETCAP); | 2161 | return avc_has_perm(current_sid(), task_sid(target), SECCLASS_PROCESS, |
| 2162 | PROCESS__GETCAP, NULL); | ||
| 2164 | } | 2163 | } |
| 2165 | 2164 | ||
| 2166 | static int selinux_capset(struct cred *new, const struct cred *old, | 2165 | static int selinux_capset(struct cred *new, const struct cred *old, |
| @@ -2168,7 +2167,8 @@ static int selinux_capset(struct cred *new, const struct cred *old, | |||
| 2168 | const kernel_cap_t *inheritable, | 2167 | const kernel_cap_t *inheritable, |
| 2169 | const kernel_cap_t *permitted) | 2168 | const kernel_cap_t *permitted) |
| 2170 | { | 2169 | { |
| 2171 | return cred_has_perm(old, new, PROCESS__SETCAP); | 2170 | return avc_has_perm(cred_sid(old), cred_sid(new), SECCLASS_PROCESS, |
| 2171 | PROCESS__SETCAP, NULL); | ||
| 2172 | } | 2172 | } |
| 2173 | 2173 | ||
| 2174 | /* | 2174 | /* |
| @@ -2224,29 +2224,22 @@ static int selinux_quota_on(struct dentry *dentry) | |||
| 2224 | 2224 | ||
| 2225 | static int selinux_syslog(int type) | 2225 | static int selinux_syslog(int type) |
| 2226 | { | 2226 | { |
| 2227 | int rc; | ||
| 2228 | |||
| 2229 | switch (type) { | 2227 | switch (type) { |
| 2230 | case SYSLOG_ACTION_READ_ALL: /* Read last kernel messages */ | 2228 | case SYSLOG_ACTION_READ_ALL: /* Read last kernel messages */ |
| 2231 | case SYSLOG_ACTION_SIZE_BUFFER: /* Return size of the log buffer */ | 2229 | case SYSLOG_ACTION_SIZE_BUFFER: /* Return size of the log buffer */ |
| 2232 | rc = task_has_system(current, SYSTEM__SYSLOG_READ); | 2230 | return avc_has_perm(current_sid(), SECINITSID_KERNEL, |
| 2233 | break; | 2231 | SECCLASS_SYSTEM, SYSTEM__SYSLOG_READ, NULL); |
| 2234 | case SYSLOG_ACTION_CONSOLE_OFF: /* Disable logging to console */ | 2232 | case SYSLOG_ACTION_CONSOLE_OFF: /* Disable logging to console */ |
| 2235 | case SYSLOG_ACTION_CONSOLE_ON: /* Enable logging to console */ | 2233 | case SYSLOG_ACTION_CONSOLE_ON: /* Enable logging to console */ |
| 2236 | /* Set level of messages printed to console */ | 2234 | /* Set level of messages printed to console */ |
| 2237 | case SYSLOG_ACTION_CONSOLE_LEVEL: | 2235 | case SYSLOG_ACTION_CONSOLE_LEVEL: |
| 2238 | rc = task_has_system(current, SYSTEM__SYSLOG_CONSOLE); | 2236 | return avc_has_perm(current_sid(), SECINITSID_KERNEL, |
| 2239 | break; | 2237 | SECCLASS_SYSTEM, SYSTEM__SYSLOG_CONSOLE, |
| 2240 | case SYSLOG_ACTION_CLOSE: /* Close log */ | 2238 | NULL); |
| 2241 | case SYSLOG_ACTION_OPEN: /* Open log */ | ||
| 2242 | case SYSLOG_ACTION_READ: /* Read from log */ | ||
| 2243 | case SYSLOG_ACTION_READ_CLEAR: /* Read/clear last kernel messages */ | ||
| 2244 | case SYSLOG_ACTION_CLEAR: /* Clear ring buffer */ | ||
| 2245 | default: | ||
| 2246 | rc = task_has_system(current, SYSTEM__SYSLOG_MOD); | ||
| 2247 | break; | ||
| 2248 | } | 2239 | } |
| 2249 | return rc; | 2240 | /* All other syslog types */ |
| 2241 | return avc_has_perm(current_sid(), SECINITSID_KERNEL, | ||
| 2242 | SECCLASS_SYSTEM, SYSTEM__SYSLOG_MOD, NULL); | ||
| 2250 | } | 2243 | } |
| 2251 | 2244 | ||
| 2252 | /* | 2245 | /* |
| @@ -2271,13 +2264,13 @@ static int selinux_vm_enough_memory(struct mm_struct *mm, long pages) | |||
| 2271 | 2264 | ||
| 2272 | /* binprm security operations */ | 2265 | /* binprm security operations */ |
| 2273 | 2266 | ||
| 2274 | static u32 ptrace_parent_sid(struct task_struct *task) | 2267 | static u32 ptrace_parent_sid(void) |
| 2275 | { | 2268 | { |
| 2276 | u32 sid = 0; | 2269 | u32 sid = 0; |
| 2277 | struct task_struct *tracer; | 2270 | struct task_struct *tracer; |
| 2278 | 2271 | ||
| 2279 | rcu_read_lock(); | 2272 | rcu_read_lock(); |
| 2280 | tracer = ptrace_parent(task); | 2273 | tracer = ptrace_parent(current); |
| 2281 | if (tracer) | 2274 | if (tracer) |
| 2282 | sid = task_sid(tracer); | 2275 | sid = task_sid(tracer); |
| 2283 | rcu_read_unlock(); | 2276 | rcu_read_unlock(); |
| @@ -2406,7 +2399,7 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm) | |||
| 2406 | * changes its SID has the appropriate permit */ | 2399 | * changes its SID has the appropriate permit */ |
| 2407 | if (bprm->unsafe & | 2400 | if (bprm->unsafe & |
| 2408 | (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) { | 2401 | (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) { |
| 2409 | u32 ptsid = ptrace_parent_sid(current); | 2402 | u32 ptsid = ptrace_parent_sid(); |
| 2410 | if (ptsid != 0) { | 2403 | if (ptsid != 0) { |
| 2411 | rc = avc_has_perm(ptsid, new_tsec->sid, | 2404 | rc = avc_has_perm(ptsid, new_tsec->sid, |
| 2412 | SECCLASS_PROCESS, | 2405 | SECCLASS_PROCESS, |
| @@ -3503,6 +3496,7 @@ static int default_noexec; | |||
| 3503 | static int file_map_prot_check(struct file *file, unsigned long prot, int shared) | 3496 | static int file_map_prot_check(struct file *file, unsigned long prot, int shared) |
| 3504 | { | 3497 | { |
| 3505 | const struct cred *cred = current_cred(); | 3498 | const struct cred *cred = current_cred(); |
| 3499 | u32 sid = cred_sid(cred); | ||
| 3506 | int rc = 0; | 3500 | int rc = 0; |
| 3507 | 3501 | ||
| 3508 | if (default_noexec && | 3502 | if (default_noexec && |
| @@ -3513,7 +3507,8 @@ static int file_map_prot_check(struct file *file, unsigned long prot, int shared | |||
| 3513 | * private file mapping that will also be writable. | 3507 | * private file mapping that will also be writable. |
| 3514 | * This has an additional check. | 3508 | * This has an additional check. |
| 3515 | */ | 3509 | */ |
| 3516 | rc = cred_has_perm(cred, cred, PROCESS__EXECMEM); | 3510 | rc = avc_has_perm(sid, sid, SECCLASS_PROCESS, |
| 3511 | PROCESS__EXECMEM, NULL); | ||
| 3517 | if (rc) | 3512 | if (rc) |
| 3518 | goto error; | 3513 | goto error; |
| 3519 | } | 3514 | } |
| @@ -3564,6 +3559,7 @@ static int selinux_file_mprotect(struct vm_area_struct *vma, | |||
| 3564 | unsigned long prot) | 3559 | unsigned long prot) |
| 3565 | { | 3560 | { |
| 3566 | const struct cred *cred = current_cred(); | 3561 | const struct cred *cred = current_cred(); |
| 3562 | u32 sid = cred_sid(cred); | ||
| 3567 | 3563 | ||
| 3568 | if (selinux_checkreqprot) | 3564 | if (selinux_checkreqprot) |
| 3569 | prot = reqprot; | 3565 | prot = reqprot; |
| @@ -3573,12 +3569,14 @@ static int selinux_file_mprotect(struct vm_area_struct *vma, | |||
| 3573 | int rc = 0; | 3569 | int rc = 0; |
| 3574 | if (vma->vm_start >= vma->vm_mm->start_brk && | 3570 | if (vma->vm_start >= vma->vm_mm->start_brk && |
| 3575 | vma->vm_end <= vma->vm_mm->brk) { | 3571 | vma->vm_end <= vma->vm_mm->brk) { |
| 3576 | rc = cred_has_perm(cred, cred, PROCESS__EXECHEAP); | 3572 | rc = avc_has_perm(sid, sid, SECCLASS_PROCESS, |
| 3573 | PROCESS__EXECHEAP, NULL); | ||
| 3577 | } else if (!vma->vm_file && | 3574 | } else if (!vma->vm_file && |
| 3578 | ((vma->vm_start <= vma->vm_mm->start_stack && | 3575 | ((vma->vm_start <= vma->vm_mm->start_stack && |
| 3579 | vma->vm_end >= vma->vm_mm->start_stack) || | 3576 | vma->vm_end >= vma->vm_mm->start_stack) || |
| 3580 | vma_is_stack_for_current(vma))) { | 3577 | vma_is_stack_for_current(vma))) { |
| 3581 | rc = current_has_perm(current, PROCESS__EXECSTACK); | 3578 | rc = avc_has_perm(sid, sid, SECCLASS_PROCESS, |
| 3579 | PROCESS__EXECSTACK, NULL); | ||
| 3582 | } else if (vma->vm_file && vma->anon_vma) { | 3580 | } else if (vma->vm_file && vma->anon_vma) { |
| 3583 | /* | 3581 | /* |
| 3584 | * We are making executable a file mapping that has | 3582 | * We are making executable a file mapping that has |
| @@ -3711,7 +3709,9 @@ static int selinux_file_open(struct file *file, const struct cred *cred) | |||
| 3711 | 3709 | ||
| 3712 | static int selinux_task_create(unsigned long clone_flags) | 3710 | static int selinux_task_create(unsigned long clone_flags) |
| 3713 | { | 3711 | { |
| 3714 | return current_has_perm(current, PROCESS__FORK); | 3712 | u32 sid = current_sid(); |
| 3713 | |||
| 3714 | return avc_has_perm(sid, sid, SECCLASS_PROCESS, PROCESS__FORK, NULL); | ||
| 3715 | } | 3715 | } |
| 3716 | 3716 | ||
| 3717 | /* | 3717 | /* |
| @@ -3821,15 +3821,12 @@ static int selinux_kernel_create_files_as(struct cred *new, struct inode *inode) | |||
| 3821 | 3821 | ||
| 3822 | static int selinux_kernel_module_request(char *kmod_name) | 3822 | static int selinux_kernel_module_request(char *kmod_name) |
| 3823 | { | 3823 | { |
| 3824 | u32 sid; | ||
| 3825 | struct common_audit_data ad; | 3824 | struct common_audit_data ad; |
| 3826 | 3825 | ||
| 3827 | sid = task_sid(current); | ||
| 3828 | |||
| 3829 | ad.type = LSM_AUDIT_DATA_KMOD; | 3826 | ad.type = LSM_AUDIT_DATA_KMOD; |
| 3830 | ad.u.kmod_name = kmod_name; | 3827 | ad.u.kmod_name = kmod_name; |
| 3831 | 3828 | ||
| 3832 | return avc_has_perm(sid, SECINITSID_KERNEL, SECCLASS_SYSTEM, | 3829 | return avc_has_perm(current_sid(), SECINITSID_KERNEL, SECCLASS_SYSTEM, |
| 3833 | SYSTEM__MODULE_REQUEST, &ad); | 3830 | SYSTEM__MODULE_REQUEST, &ad); |
| 3834 | } | 3831 | } |
| 3835 | 3832 | ||
| @@ -3881,17 +3878,20 @@ static int selinux_kernel_read_file(struct file *file, | |||
| 3881 | 3878 | ||
| 3882 | static int selinux_task_setpgid(struct task_struct *p, pid_t pgid) | 3879 | static int selinux_task_setpgid(struct task_struct *p, pid_t pgid) |
| 3883 | { | 3880 | { |
| 3884 | return current_has_perm(p, PROCESS__SETPGID); | 3881 | return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS, |
| 3882 | PROCESS__SETPGID, NULL); | ||
| 3885 | } | 3883 | } |
| 3886 | 3884 | ||
| 3887 | static int selinux_task_getpgid(struct task_struct *p) | 3885 | static int selinux_task_getpgid(struct task_struct *p) |
| 3888 | { | 3886 | { |
| 3889 | return current_has_perm(p, PROCESS__GETPGID); | 3887 | return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS, |
| 3888 | PROCESS__GETPGID, NULL); | ||
| 3890 | } | 3889 | } |
| 3891 | 3890 | ||
| 3892 | static int selinux_task_getsid(struct task_struct *p) | 3891 | static int selinux_task_getsid(struct task_struct *p) |
| 3893 | { | 3892 | { |
| 3894 | return current_has_perm(p, PROCESS__GETSESSION); | 3893 | return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS, |
| 3894 | PROCESS__GETSESSION, NULL); | ||
| 3895 | } | 3895 | } |
| 3896 | 3896 | ||
| 3897 | static void selinux_task_getsecid(struct task_struct *p, u32 *secid) | 3897 | static void selinux_task_getsecid(struct task_struct *p, u32 *secid) |
| @@ -3901,17 +3901,20 @@ static void selinux_task_getsecid(struct task_struct *p, u32 *secid) | |||
| 3901 | 3901 | ||
| 3902 | static int selinux_task_setnice(struct task_struct *p, int nice) | 3902 | static int selinux_task_setnice(struct task_struct *p, int nice) |
| 3903 | { | 3903 | { |
| 3904 | return current_has_perm(p, PROCESS__SETSCHED); | 3904 | return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS, |
| 3905 | PROCESS__SETSCHED, NULL); | ||
| 3905 | } | 3906 | } |
| 3906 | 3907 | ||
| 3907 | static int selinux_task_setioprio(struct task_struct *p, int ioprio) | 3908 | static int selinux_task_setioprio(struct task_struct *p, int ioprio) |
| 3908 | { | 3909 | { |
| 3909 | return current_has_perm(p, PROCESS__SETSCHED); | 3910 | return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS, |
| 3911 | PROCESS__SETSCHED, NULL); | ||
| 3910 | } | 3912 | } |
| 3911 | 3913 | ||
| 3912 | static int selinux_task_getioprio(struct task_struct *p) | 3914 | static int selinux_task_getioprio(struct task_struct *p) |
| 3913 | { | 3915 | { |
| 3914 | return current_has_perm(p, PROCESS__GETSCHED); | 3916 | return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS, |
| 3917 | PROCESS__GETSCHED, NULL); | ||
| 3915 | } | 3918 | } |
| 3916 | 3919 | ||
| 3917 | static int selinux_task_setrlimit(struct task_struct *p, unsigned int resource, | 3920 | static int selinux_task_setrlimit(struct task_struct *p, unsigned int resource, |
| @@ -3924,47 +3927,42 @@ static int selinux_task_setrlimit(struct task_struct *p, unsigned int resource, | |||
| 3924 | later be used as a safe reset point for the soft limit | 3927 | later be used as a safe reset point for the soft limit |
| 3925 | upon context transitions. See selinux_bprm_committing_creds. */ | 3928 | upon context transitions. See selinux_bprm_committing_creds. */ |
| 3926 | if (old_rlim->rlim_max != new_rlim->rlim_max) | 3929 | if (old_rlim->rlim_max != new_rlim->rlim_max) |
| 3927 | return current_has_perm(p, PROCESS__SETRLIMIT); | 3930 | return avc_has_perm(current_sid(), task_sid(p), |
| 3931 | SECCLASS_PROCESS, PROCESS__SETRLIMIT, NULL); | ||
| 3928 | 3932 | ||
| 3929 | return 0; | 3933 | return 0; |
| 3930 | } | 3934 | } |
| 3931 | 3935 | ||
| 3932 | static int selinux_task_setscheduler(struct task_struct *p) | 3936 | static int selinux_task_setscheduler(struct task_struct *p) |
| 3933 | { | 3937 | { |
| 3934 | return current_has_perm(p, PROCESS__SETSCHED); | 3938 | return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS, |
| 3939 | PROCESS__SETSCHED, NULL); | ||
| 3935 | } | 3940 | } |
| 3936 | 3941 | ||
| 3937 | static int selinux_task_getscheduler(struct task_struct *p) | 3942 | static int selinux_task_getscheduler(struct task_struct *p) |
| 3938 | { | 3943 | { |
| 3939 | return current_has_perm(p, PROCESS__GETSCHED); | 3944 | return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS, |
| 3945 | PROCESS__GETSCHED, NULL); | ||
| 3940 | } | 3946 | } |
| 3941 | 3947 | ||
| 3942 | static int selinux_task_movememory(struct task_struct *p) | 3948 | static int selinux_task_movememory(struct task_struct *p) |
| 3943 | { | 3949 | { |
| 3944 | return current_has_perm(p, PROCESS__SETSCHED); | 3950 | return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS, |
| 3951 | PROCESS__SETSCHED, NULL); | ||
| 3945 | } | 3952 | } |
| 3946 | 3953 | ||
| 3947 | static int selinux_task_kill(struct task_struct *p, struct siginfo *info, | 3954 | static int selinux_task_kill(struct task_struct *p, struct siginfo *info, |
| 3948 | int sig, u32 secid) | 3955 | int sig, u32 secid) |
| 3949 | { | 3956 | { |
| 3950 | u32 perm; | 3957 | u32 perm; |
| 3951 | int rc; | ||
| 3952 | 3958 | ||
| 3953 | if (!sig) | 3959 | if (!sig) |
| 3954 | perm = PROCESS__SIGNULL; /* null signal; existence test */ | 3960 | perm = PROCESS__SIGNULL; /* null signal; existence test */ |
| 3955 | else | 3961 | else |
| 3956 | perm = signal_to_av(sig); | 3962 | perm = signal_to_av(sig); |
| 3957 | if (secid) | 3963 | if (!secid) |
| 3958 | rc = avc_has_perm(secid, task_sid(p), | 3964 | secid = current_sid(); |
| 3959 | SECCLASS_PROCESS, perm, NULL); | 3965 | return avc_has_perm(secid, task_sid(p), SECCLASS_PROCESS, perm, NULL); |
| 3960 | else | ||
| 3961 | rc = current_has_perm(p, perm); | ||
| 3962 | return rc; | ||
| 3963 | } | ||
| 3964 | |||
| 3965 | static int selinux_task_wait(struct task_struct *p) | ||
| 3966 | { | ||
| 3967 | return task_has_perm(p, current, PROCESS__SIGCHLD); | ||
| 3968 | } | 3966 | } |
| 3969 | 3967 | ||
| 3970 | static void selinux_task_to_inode(struct task_struct *p, | 3968 | static void selinux_task_to_inode(struct task_struct *p, |
| @@ -4254,12 +4252,11 @@ static int socket_sockcreate_sid(const struct task_security_struct *tsec, | |||
| 4254 | socksid); | 4252 | socksid); |
| 4255 | } | 4253 | } |
| 4256 | 4254 | ||
| 4257 | static int sock_has_perm(struct task_struct *task, struct sock *sk, u32 perms) | 4255 | static int sock_has_perm(struct sock *sk, u32 perms) |
| 4258 | { | 4256 | { |
| 4259 | struct sk_security_struct *sksec = sk->sk_security; | 4257 | struct sk_security_struct *sksec = sk->sk_security; |
| 4260 | struct common_audit_data ad; | 4258 | struct common_audit_data ad; |
| 4261 | struct lsm_network_audit net = {0,}; | 4259 | struct lsm_network_audit net = {0,}; |
| 4262 | u32 tsid = task_sid(task); | ||
| 4263 | 4260 | ||
| 4264 | if (sksec->sid == SECINITSID_KERNEL) | 4261 | if (sksec->sid == SECINITSID_KERNEL) |
| 4265 | return 0; | 4262 | return 0; |
| @@ -4268,7 +4265,8 @@ static int sock_has_perm(struct task_struct *task, struct sock *sk, u32 perms) | |||
| 4268 | ad.u.net = &net; | 4265 | ad.u.net = &net; |
| 4269 | ad.u.net->sk = sk; | 4266 | ad.u.net->sk = sk; |
| 4270 | 4267 | ||
| 4271 | return avc_has_perm(tsid, sksec->sid, sksec->sclass, perms, &ad); | 4268 | return avc_has_perm(current_sid(), sksec->sid, sksec->sclass, perms, |
| 4269 | &ad); | ||
| 4272 | } | 4270 | } |
| 4273 | 4271 | ||
| 4274 | static int selinux_socket_create(int family, int type, | 4272 | static int selinux_socket_create(int family, int type, |
| @@ -4330,7 +4328,7 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in | |||
| 4330 | u16 family; | 4328 | u16 family; |
| 4331 | int err; | 4329 | int err; |
| 4332 | 4330 | ||
| 4333 | err = sock_has_perm(current, sk, SOCKET__BIND); | 4331 | err = sock_has_perm(sk, SOCKET__BIND); |
| 4334 | if (err) | 4332 | if (err) |
| 4335 | goto out; | 4333 | goto out; |
| 4336 | 4334 | ||
| @@ -4429,7 +4427,7 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, | |||
| 4429 | struct sk_security_struct *sksec = sk->sk_security; | 4427 | struct sk_security_struct *sksec = sk->sk_security; |
| 4430 | int err; | 4428 | int err; |
| 4431 | 4429 | ||
| 4432 | err = sock_has_perm(current, sk, SOCKET__CONNECT); | 4430 | err = sock_has_perm(sk, SOCKET__CONNECT); |
| 4433 | if (err) | 4431 | if (err) |
| 4434 | return err; | 4432 | return err; |
| 4435 | 4433 | ||
| @@ -4481,7 +4479,7 @@ out: | |||
| 4481 | 4479 | ||
| 4482 | static int selinux_socket_listen(struct socket *sock, int backlog) | 4480 | static int selinux_socket_listen(struct socket *sock, int backlog) |
| 4483 | { | 4481 | { |
| 4484 | return sock_has_perm(current, sock->sk, SOCKET__LISTEN); | 4482 | return sock_has_perm(sock->sk, SOCKET__LISTEN); |
| 4485 | } | 4483 | } |
| 4486 | 4484 | ||
| 4487 | static int selinux_socket_accept(struct socket *sock, struct socket *newsock) | 4485 | static int selinux_socket_accept(struct socket *sock, struct socket *newsock) |
| @@ -4492,7 +4490,7 @@ static int selinux_socket_accept(struct socket *sock, struct socket *newsock) | |||
| 4492 | u16 sclass; | 4490 | u16 sclass; |
| 4493 | u32 sid; | 4491 | u32 sid; |
| 4494 | 4492 | ||
| 4495 | err = sock_has_perm(current, sock->sk, SOCKET__ACCEPT); | 4493 | err = sock_has_perm(sock->sk, SOCKET__ACCEPT); |
| 4496 | if (err) | 4494 | if (err) |
| 4497 | return err; | 4495 | return err; |
| 4498 | 4496 | ||
| @@ -4513,30 +4511,30 @@ static int selinux_socket_accept(struct socket *sock, struct socket *newsock) | |||
| 4513 | static int selinux_socket_sendmsg(struct socket *sock, struct msghdr *msg, | 4511 | static int selinux_socket_sendmsg(struct socket *sock, struct msghdr *msg, |
| 4514 | int size) | 4512 | int size) |
| 4515 | { | 4513 | { |
| 4516 | return sock_has_perm(current, sock->sk, SOCKET__WRITE); | 4514 | return sock_has_perm(sock->sk, SOCKET__WRITE); |
| 4517 | } | 4515 | } |
| 4518 | 4516 | ||
| 4519 | static int selinux_socket_recvmsg(struct socket *sock, struct msghdr *msg, | 4517 | static int selinux_socket_recvmsg(struct socket *sock, struct msghdr *msg, |
| 4520 | int size, int flags) | 4518 | int size, int flags) |
| 4521 | { | 4519 | { |
| 4522 | return sock_has_perm(current, sock->sk, SOCKET__READ); | 4520 | return sock_has_perm(sock->sk, SOCKET__READ); |
| 4523 | } | 4521 | } |
| 4524 | 4522 | ||
| 4525 | static int selinux_socket_getsockname(struct socket *sock) | 4523 | static int selinux_socket_getsockname(struct socket *sock) |
| 4526 | { | 4524 | { |
| 4527 | return sock_has_perm(current, sock->sk, SOCKET__GETATTR); | 4525 | return sock_has_perm(sock->sk, SOCKET__GETATTR); |
| 4528 | } | 4526 | } |
| 4529 | 4527 | ||
| 4530 | static int selinux_socket_getpeername(struct socket *sock) | 4528 | static int selinux_socket_getpeername(struct socket *sock) |
| 4531 | { | 4529 | { |
| 4532 | return sock_has_perm(current, sock->sk, SOCKET__GETATTR); | 4530 | return sock_has_perm(sock->sk, SOCKET__GETATTR); |
| 4533 | } | 4531 | } |
| 4534 | 4532 | ||
| 4535 | static int selinux_socket_setsockopt(struct socket *sock, int level, int optname) | 4533 | static int selinux_socket_setsockopt(struct socket *sock, int level, int optname) |
| 4536 | { | 4534 | { |
| 4537 | int err; | 4535 | int err; |
| 4538 | 4536 | ||
| 4539 | err = sock_has_perm(current, sock->sk, SOCKET__SETOPT); | 4537 | err = sock_has_perm(sock->sk, SOCKET__SETOPT); |
| 4540 | if (err) | 4538 | if (err) |
| 4541 | return err; | 4539 | return err; |
| 4542 | 4540 | ||
| @@ -4546,12 +4544,12 @@ static int selinux_socket_setsockopt(struct socket *sock, int level, int optname | |||
| 4546 | static int selinux_socket_getsockopt(struct socket *sock, int level, | 4544 | static int selinux_socket_getsockopt(struct socket *sock, int level, |
| 4547 | int optname) | 4545 | int optname) |
| 4548 | { | 4546 | { |
| 4549 | return sock_has_perm(current, sock->sk, SOCKET__GETOPT); | 4547 | return sock_has_perm(sock->sk, SOCKET__GETOPT); |
| 4550 | } | 4548 | } |
| 4551 | 4549 | ||
| 4552 | static int selinux_socket_shutdown(struct socket *sock, int how) | 4550 | static int selinux_socket_shutdown(struct socket *sock, int how) |
| 4553 | { | 4551 | { |
| 4554 | return sock_has_perm(current, sock->sk, SOCKET__SHUTDOWN); | 4552 | return sock_has_perm(sock->sk, SOCKET__SHUTDOWN); |
| 4555 | } | 4553 | } |
| 4556 | 4554 | ||
| 4557 | static int selinux_socket_unix_stream_connect(struct sock *sock, | 4555 | static int selinux_socket_unix_stream_connect(struct sock *sock, |
| @@ -5039,7 +5037,7 @@ static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb) | |||
| 5039 | goto out; | 5037 | goto out; |
| 5040 | } | 5038 | } |
| 5041 | 5039 | ||
| 5042 | err = sock_has_perm(current, sk, perm); | 5040 | err = sock_has_perm(sk, perm); |
| 5043 | out: | 5041 | out: |
| 5044 | return err; | 5042 | return err; |
| 5045 | } | 5043 | } |
| @@ -5370,20 +5368,17 @@ static int selinux_netlink_send(struct sock *sk, struct sk_buff *skb) | |||
| 5370 | return selinux_nlmsg_perm(sk, skb); | 5368 | return selinux_nlmsg_perm(sk, skb); |
| 5371 | } | 5369 | } |
| 5372 | 5370 | ||
| 5373 | static int ipc_alloc_security(struct task_struct *task, | 5371 | static int ipc_alloc_security(struct kern_ipc_perm *perm, |
| 5374 | struct kern_ipc_perm *perm, | ||
| 5375 | u16 sclass) | 5372 | u16 sclass) |
| 5376 | { | 5373 | { |
| 5377 | struct ipc_security_struct *isec; | 5374 | struct ipc_security_struct *isec; |
| 5378 | u32 sid; | ||
| 5379 | 5375 | ||
| 5380 | isec = kzalloc(sizeof(struct ipc_security_struct), GFP_KERNEL); | 5376 | isec = kzalloc(sizeof(struct ipc_security_struct), GFP_KERNEL); |
| 5381 | if (!isec) | 5377 | if (!isec) |
| 5382 | return -ENOMEM; | 5378 | return -ENOMEM; |
| 5383 | 5379 | ||
| 5384 | sid = task_sid(task); | ||
| 5385 | isec->sclass = sclass; | 5380 | isec->sclass = sclass; |
| 5386 | isec->sid = sid; | 5381 | isec->sid = current_sid(); |
| 5387 | perm->security = isec; | 5382 | perm->security = isec; |
| 5388 | 5383 | ||
| 5389 | return 0; | 5384 | return 0; |
| @@ -5451,7 +5446,7 @@ static int selinux_msg_queue_alloc_security(struct msg_queue *msq) | |||
| 5451 | u32 sid = current_sid(); | 5446 | u32 sid = current_sid(); |
| 5452 | int rc; | 5447 | int rc; |
| 5453 | 5448 | ||
| 5454 | rc = ipc_alloc_security(current, &msq->q_perm, SECCLASS_MSGQ); | 5449 | rc = ipc_alloc_security(&msq->q_perm, SECCLASS_MSGQ); |
| 5455 | if (rc) | 5450 | if (rc) |
| 5456 | return rc; | 5451 | return rc; |
| 5457 | 5452 | ||
| @@ -5498,7 +5493,8 @@ static int selinux_msg_queue_msgctl(struct msg_queue *msq, int cmd) | |||
| 5498 | case IPC_INFO: | 5493 | case IPC_INFO: |
| 5499 | case MSG_INFO: | 5494 | case MSG_INFO: |
| 5500 | /* No specific object, just general system-wide information. */ | 5495 | /* No specific object, just general system-wide information. */ |
| 5501 | return task_has_system(current, SYSTEM__IPC_INFO); | 5496 | return avc_has_perm(current_sid(), SECINITSID_KERNEL, |
| 5497 | SECCLASS_SYSTEM, SYSTEM__IPC_INFO, NULL); | ||
| 5502 | case IPC_STAT: | 5498 | case IPC_STAT: |
| 5503 | case MSG_STAT: | 5499 | case MSG_STAT: |
| 5504 | perms = MSGQ__GETATTR | MSGQ__ASSOCIATE; | 5500 | perms = MSGQ__GETATTR | MSGQ__ASSOCIATE; |
| @@ -5592,7 +5588,7 @@ static int selinux_shm_alloc_security(struct shmid_kernel *shp) | |||
| 5592 | u32 sid = current_sid(); | 5588 | u32 sid = current_sid(); |
| 5593 | int rc; | 5589 | int rc; |
| 5594 | 5590 | ||
| 5595 | rc = ipc_alloc_security(current, &shp->shm_perm, SECCLASS_SHM); | 5591 | rc = ipc_alloc_security(&shp->shm_perm, SECCLASS_SHM); |
| 5596 | if (rc) | 5592 | if (rc) |
| 5597 | return rc; | 5593 | return rc; |
| 5598 | 5594 | ||
| @@ -5640,7 +5636,8 @@ static int selinux_shm_shmctl(struct shmid_kernel *shp, int cmd) | |||
| 5640 | case IPC_INFO: | 5636 | case IPC_INFO: |
| 5641 | case SHM_INFO: | 5637 | case SHM_INFO: |
| 5642 | /* No specific object, just general system-wide information. */ | 5638 | /* No specific object, just general system-wide information. */ |
| 5643 | return task_has_system(current, SYSTEM__IPC_INFO); | 5639 | return avc_has_perm(current_sid(), SECINITSID_KERNEL, |
| 5640 | SECCLASS_SYSTEM, SYSTEM__IPC_INFO, NULL); | ||
| 5644 | case IPC_STAT: | 5641 | case IPC_STAT: |
| 5645 | case SHM_STAT: | 5642 | case SHM_STAT: |
| 5646 | perms = SHM__GETATTR | SHM__ASSOCIATE; | 5643 | perms = SHM__GETATTR | SHM__ASSOCIATE; |
| @@ -5684,7 +5681,7 @@ static int selinux_sem_alloc_security(struct sem_array *sma) | |||
| 5684 | u32 sid = current_sid(); | 5681 | u32 sid = current_sid(); |
| 5685 | int rc; | 5682 | int rc; |
| 5686 | 5683 | ||
| 5687 | rc = ipc_alloc_security(current, &sma->sem_perm, SECCLASS_SEM); | 5684 | rc = ipc_alloc_security(&sma->sem_perm, SECCLASS_SEM); |
| 5688 | if (rc) | 5685 | if (rc) |
| 5689 | return rc; | 5686 | return rc; |
| 5690 | 5687 | ||
| @@ -5732,7 +5729,8 @@ static int selinux_sem_semctl(struct sem_array *sma, int cmd) | |||
| 5732 | case IPC_INFO: | 5729 | case IPC_INFO: |
| 5733 | case SEM_INFO: | 5730 | case SEM_INFO: |
| 5734 | /* No specific object, just general system-wide information. */ | 5731 | /* No specific object, just general system-wide information. */ |
| 5735 | return task_has_system(current, SYSTEM__IPC_INFO); | 5732 | return avc_has_perm(current_sid(), SECINITSID_KERNEL, |
| 5733 | SECCLASS_SYSTEM, SYSTEM__IPC_INFO, NULL); | ||
| 5736 | case GETPID: | 5734 | case GETPID: |
| 5737 | case GETNCNT: | 5735 | case GETNCNT: |
| 5738 | case GETZCNT: | 5736 | case GETZCNT: |
| @@ -5813,15 +5811,16 @@ static int selinux_getprocattr(struct task_struct *p, | |||
| 5813 | int error; | 5811 | int error; |
| 5814 | unsigned len; | 5812 | unsigned len; |
| 5815 | 5813 | ||
| 5814 | rcu_read_lock(); | ||
| 5815 | __tsec = __task_cred(p)->security; | ||
| 5816 | |||
| 5816 | if (current != p) { | 5817 | if (current != p) { |
| 5817 | error = current_has_perm(p, PROCESS__GETATTR); | 5818 | error = avc_has_perm(current_sid(), __tsec->sid, |
| 5819 | SECCLASS_PROCESS, PROCESS__GETATTR, NULL); | ||
| 5818 | if (error) | 5820 | if (error) |
| 5819 | return error; | 5821 | goto bad; |
| 5820 | } | 5822 | } |
| 5821 | 5823 | ||
| 5822 | rcu_read_lock(); | ||
| 5823 | __tsec = __task_cred(p)->security; | ||
| 5824 | |||
| 5825 | if (!strcmp(name, "current")) | 5824 | if (!strcmp(name, "current")) |
| 5826 | sid = __tsec->sid; | 5825 | sid = __tsec->sid; |
| 5827 | else if (!strcmp(name, "prev")) | 5826 | else if (!strcmp(name, "prev")) |
| @@ -5834,8 +5833,10 @@ static int selinux_getprocattr(struct task_struct *p, | |||
| 5834 | sid = __tsec->keycreate_sid; | 5833 | sid = __tsec->keycreate_sid; |
| 5835 | else if (!strcmp(name, "sockcreate")) | 5834 | else if (!strcmp(name, "sockcreate")) |
| 5836 | sid = __tsec->sockcreate_sid; | 5835 | sid = __tsec->sockcreate_sid; |
| 5837 | else | 5836 | else { |
| 5838 | goto invalid; | 5837 | error = -EINVAL; |
| 5838 | goto bad; | ||
| 5839 | } | ||
| 5839 | rcu_read_unlock(); | 5840 | rcu_read_unlock(); |
| 5840 | 5841 | ||
| 5841 | if (!sid) | 5842 | if (!sid) |
| @@ -5846,48 +5847,44 @@ static int selinux_getprocattr(struct task_struct *p, | |||
| 5846 | return error; | 5847 | return error; |
| 5847 | return len; | 5848 | return len; |
| 5848 | 5849 | ||
| 5849 | invalid: | 5850 | bad: |
| 5850 | rcu_read_unlock(); | 5851 | rcu_read_unlock(); |
| 5851 | return -EINVAL; | 5852 | return error; |
| 5852 | } | 5853 | } |
| 5853 | 5854 | ||
| 5854 | static int selinux_setprocattr(struct task_struct *p, | 5855 | static int selinux_setprocattr(const char *name, void *value, size_t size) |
| 5855 | char *name, void *value, size_t size) | ||
| 5856 | { | 5856 | { |
| 5857 | struct task_security_struct *tsec; | 5857 | struct task_security_struct *tsec; |
| 5858 | struct cred *new; | 5858 | struct cred *new; |
| 5859 | u32 sid = 0, ptsid; | 5859 | u32 mysid = current_sid(), sid = 0, ptsid; |
| 5860 | int error; | 5860 | int error; |
| 5861 | char *str = value; | 5861 | char *str = value; |
| 5862 | 5862 | ||
| 5863 | if (current != p) { | ||
| 5864 | /* SELinux only allows a process to change its own | ||
| 5865 | security attributes. */ | ||
| 5866 | return -EACCES; | ||
| 5867 | } | ||
| 5868 | |||
| 5869 | /* | 5863 | /* |
| 5870 | * Basic control over ability to set these attributes at all. | 5864 | * Basic control over ability to set these attributes at all. |
| 5871 | * current == p, but we'll pass them separately in case the | ||
| 5872 | * above restriction is ever removed. | ||
| 5873 | */ | 5865 | */ |
| 5874 | if (!strcmp(name, "exec")) | 5866 | if (!strcmp(name, "exec")) |
| 5875 | error = current_has_perm(p, PROCESS__SETEXEC); | 5867 | error = avc_has_perm(mysid, mysid, SECCLASS_PROCESS, |
| 5868 | PROCESS__SETEXEC, NULL); | ||
| 5876 | else if (!strcmp(name, "fscreate")) | 5869 | else if (!strcmp(name, "fscreate")) |
| 5877 | error = current_has_perm(p, PROCESS__SETFSCREATE); | 5870 | error = avc_has_perm(mysid, mysid, SECCLASS_PROCESS, |
| 5871 | PROCESS__SETFSCREATE, NULL); | ||
| 5878 | else if (!strcmp(name, "keycreate")) | 5872 | else if (!strcmp(name, "keycreate")) |
| 5879 | error = current_has_perm(p, PROCESS__SETKEYCREATE); | 5873 | error = avc_has_perm(mysid, mysid, SECCLASS_PROCESS, |
| 5874 | PROCESS__SETKEYCREATE, NULL); | ||
| 5880 | else if (!strcmp(name, "sockcreate")) | 5875 | else if (!strcmp(name, "sockcreate")) |
| 5881 | error = current_has_perm(p, PROCESS__SETSOCKCREATE); | 5876 | error = avc_has_perm(mysid, mysid, SECCLASS_PROCESS, |
| 5877 | PROCESS__SETSOCKCREATE, NULL); | ||
| 5882 | else if (!strcmp(name, "current")) | 5878 | else if (!strcmp(name, "current")) |
| 5883 | error = current_has_perm(p, PROCESS__SETCURRENT); | 5879 | error = avc_has_perm(mysid, mysid, SECCLASS_PROCESS, |
| 5880 | PROCESS__SETCURRENT, NULL); | ||
| 5884 | else | 5881 | else |
| 5885 | error = -EINVAL; | 5882 | error = -EINVAL; |
| 5886 | if (error) | 5883 | if (error) |
| 5887 | return error; | 5884 | return error; |
| 5888 | 5885 | ||
| 5889 | /* Obtain a SID for the context, if one was specified. */ | 5886 | /* Obtain a SID for the context, if one was specified. */ |
| 5890 | if (size && str[1] && str[1] != '\n') { | 5887 | if (size && str[0] && str[0] != '\n') { |
| 5891 | if (str[size-1] == '\n') { | 5888 | if (str[size-1] == '\n') { |
| 5892 | str[size-1] = 0; | 5889 | str[size-1] = 0; |
| 5893 | size--; | 5890 | size--; |
| @@ -5934,7 +5931,8 @@ static int selinux_setprocattr(struct task_struct *p, | |||
| 5934 | } else if (!strcmp(name, "fscreate")) { | 5931 | } else if (!strcmp(name, "fscreate")) { |
| 5935 | tsec->create_sid = sid; | 5932 | tsec->create_sid = sid; |
| 5936 | } else if (!strcmp(name, "keycreate")) { | 5933 | } else if (!strcmp(name, "keycreate")) { |
| 5937 | error = may_create_key(sid, p); | 5934 | error = avc_has_perm(mysid, sid, SECCLASS_KEY, KEY__CREATE, |
| 5935 | NULL); | ||
| 5938 | if (error) | 5936 | if (error) |
| 5939 | goto abort_change; | 5937 | goto abort_change; |
| 5940 | tsec->keycreate_sid = sid; | 5938 | tsec->keycreate_sid = sid; |
| @@ -5961,7 +5959,7 @@ static int selinux_setprocattr(struct task_struct *p, | |||
| 5961 | 5959 | ||
| 5962 | /* Check for ptracing, and update the task SID if ok. | 5960 | /* Check for ptracing, and update the task SID if ok. |
| 5963 | Otherwise, leave SID unchanged and fail. */ | 5961 | Otherwise, leave SID unchanged and fail. */ |
| 5964 | ptsid = ptrace_parent_sid(p); | 5962 | ptsid = ptrace_parent_sid(); |
| 5965 | if (ptsid != 0) { | 5963 | if (ptsid != 0) { |
| 5966 | error = avc_has_perm(ptsid, sid, SECCLASS_PROCESS, | 5964 | error = avc_has_perm(ptsid, sid, SECCLASS_PROCESS, |
| 5967 | PROCESS__PTRACE, NULL); | 5965 | PROCESS__PTRACE, NULL); |
| @@ -6209,7 +6207,6 @@ static struct security_hook_list selinux_hooks[] = { | |||
| 6209 | LSM_HOOK_INIT(task_getscheduler, selinux_task_getscheduler), | 6207 | LSM_HOOK_INIT(task_getscheduler, selinux_task_getscheduler), |
| 6210 | LSM_HOOK_INIT(task_movememory, selinux_task_movememory), | 6208 | LSM_HOOK_INIT(task_movememory, selinux_task_movememory), |
| 6211 | LSM_HOOK_INIT(task_kill, selinux_task_kill), | 6209 | LSM_HOOK_INIT(task_kill, selinux_task_kill), |
| 6212 | LSM_HOOK_INIT(task_wait, selinux_task_wait), | ||
| 6213 | LSM_HOOK_INIT(task_to_inode, selinux_task_to_inode), | 6210 | LSM_HOOK_INIT(task_to_inode, selinux_task_to_inode), |
| 6214 | 6211 | ||
| 6215 | LSM_HOOK_INIT(ipc_permission, selinux_ipc_permission), | 6212 | LSM_HOOK_INIT(ipc_permission, selinux_ipc_permission), |
| @@ -6349,7 +6346,7 @@ static __init int selinux_init(void) | |||
| 6349 | 0, SLAB_PANIC, NULL); | 6346 | 0, SLAB_PANIC, NULL); |
| 6350 | avc_init(); | 6347 | avc_init(); |
| 6351 | 6348 | ||
| 6352 | security_add_hooks(selinux_hooks, ARRAY_SIZE(selinux_hooks)); | 6349 | security_add_hooks(selinux_hooks, ARRAY_SIZE(selinux_hooks), "selinux"); |
| 6353 | 6350 | ||
| 6354 | if (avc_add_callback(selinux_netcache_avc_callback, AVC_CALLBACK_RESET)) | 6351 | if (avc_add_callback(selinux_netcache_avc_callback, AVC_CALLBACK_RESET)) |
| 6355 | panic("SELinux: Unable to register AVC netcache callback\n"); | 6352 | panic("SELinux: Unable to register AVC netcache callback\n"); |
diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h index 13ae49b0baa0..7898ffa6d3e6 100644 --- a/security/selinux/include/classmap.h +++ b/security/selinux/include/classmap.h | |||
| @@ -171,5 +171,67 @@ struct security_class_mapping secclass_map[] = { | |||
| 171 | { COMMON_CAP_PERMS, NULL } }, | 171 | { COMMON_CAP_PERMS, NULL } }, |
| 172 | { "cap2_userns", | 172 | { "cap2_userns", |
| 173 | { COMMON_CAP2_PERMS, NULL } }, | 173 | { COMMON_CAP2_PERMS, NULL } }, |
| 174 | { "sctp_socket", | ||
| 175 | { COMMON_SOCK_PERMS, | ||
| 176 | "node_bind", NULL } }, | ||
| 177 | { "icmp_socket", | ||
| 178 | { COMMON_SOCK_PERMS, | ||
| 179 | "node_bind", NULL } }, | ||
| 180 | { "ax25_socket", | ||
| 181 | { COMMON_SOCK_PERMS, NULL } }, | ||
| 182 | { "ipx_socket", | ||
| 183 | { COMMON_SOCK_PERMS, NULL } }, | ||
| 184 | { "netrom_socket", | ||
| 185 | { COMMON_SOCK_PERMS, NULL } }, | ||
| 186 | { "atmpvc_socket", | ||
| 187 | { COMMON_SOCK_PERMS, NULL } }, | ||
| 188 | { "x25_socket", | ||
| 189 | { COMMON_SOCK_PERMS, NULL } }, | ||
| 190 | { "rose_socket", | ||
| 191 | { COMMON_SOCK_PERMS, NULL } }, | ||
| 192 | { "decnet_socket", | ||
| 193 | { COMMON_SOCK_PERMS, NULL } }, | ||
| 194 | { "atmsvc_socket", | ||
| 195 | { COMMON_SOCK_PERMS, NULL } }, | ||
| 196 | { "rds_socket", | ||
| 197 | { COMMON_SOCK_PERMS, NULL } }, | ||
| 198 | { "irda_socket", | ||
| 199 | { COMMON_SOCK_PERMS, NULL } }, | ||
| 200 | { "pppox_socket", | ||
| 201 | { COMMON_SOCK_PERMS, NULL } }, | ||
| 202 | { "llc_socket", | ||
| 203 | { COMMON_SOCK_PERMS, NULL } }, | ||
| 204 | { "can_socket", | ||
| 205 | { COMMON_SOCK_PERMS, NULL } }, | ||
| 206 | { "tipc_socket", | ||
| 207 | { COMMON_SOCK_PERMS, NULL } }, | ||
| 208 | { "bluetooth_socket", | ||
| 209 | { COMMON_SOCK_PERMS, NULL } }, | ||
| 210 | { "iucv_socket", | ||
| 211 | { COMMON_SOCK_PERMS, NULL } }, | ||
| 212 | { "rxrpc_socket", | ||
| 213 | { COMMON_SOCK_PERMS, NULL } }, | ||
| 214 | { "isdn_socket", | ||
| 215 | { COMMON_SOCK_PERMS, NULL } }, | ||
| 216 | { "phonet_socket", | ||
| 217 | { COMMON_SOCK_PERMS, NULL } }, | ||
| 218 | { "ieee802154_socket", | ||
| 219 | { COMMON_SOCK_PERMS, NULL } }, | ||
| 220 | { "caif_socket", | ||
| 221 | { COMMON_SOCK_PERMS, NULL } }, | ||
| 222 | { "alg_socket", | ||
| 223 | { COMMON_SOCK_PERMS, NULL } }, | ||
| 224 | { "nfc_socket", | ||
| 225 | { COMMON_SOCK_PERMS, NULL } }, | ||
| 226 | { "vsock_socket", | ||
| 227 | { COMMON_SOCK_PERMS, NULL } }, | ||
| 228 | { "kcm_socket", | ||
| 229 | { COMMON_SOCK_PERMS, NULL } }, | ||
| 230 | { "qipcrtr_socket", | ||
| 231 | { COMMON_SOCK_PERMS, NULL } }, | ||
| 174 | { NULL } | 232 | { NULL } |
| 175 | }; | 233 | }; |
| 234 | |||
| 235 | #if PF_MAX > 43 | ||
| 236 | #error New address family defined, please update secclass_map. | ||
| 237 | #endif | ||
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h index e8dab0f02c72..c03cdcd12a3b 100644 --- a/security/selinux/include/objsec.h +++ b/security/selinux/include/objsec.h | |||
| @@ -37,6 +37,16 @@ struct task_security_struct { | |||
| 37 | u32 sockcreate_sid; /* fscreate SID */ | 37 | u32 sockcreate_sid; /* fscreate SID */ |
| 38 | }; | 38 | }; |
| 39 | 39 | ||
| 40 | /* | ||
| 41 | * get the subjective security ID of the current task | ||
| 42 | */ | ||
| 43 | static inline u32 current_sid(void) | ||
| 44 | { | ||
| 45 | const struct task_security_struct *tsec = current_security(); | ||
| 46 | |||
| 47 | return tsec->sid; | ||
| 48 | } | ||
| 49 | |||
| 40 | enum label_initialized { | 50 | enum label_initialized { |
| 41 | LABEL_INVALID, /* invalid or not initialized */ | 51 | LABEL_INVALID, /* invalid or not initialized */ |
| 42 | LABEL_INITIALIZED, /* initialized */ | 52 | LABEL_INITIALIZED, /* initialized */ |
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index 308a286c6cbe..beaa14b8b6cf 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h | |||
| @@ -69,7 +69,7 @@ extern int selinux_enabled; | |||
| 69 | enum { | 69 | enum { |
| 70 | POLICYDB_CAPABILITY_NETPEER, | 70 | POLICYDB_CAPABILITY_NETPEER, |
| 71 | POLICYDB_CAPABILITY_OPENPERM, | 71 | POLICYDB_CAPABILITY_OPENPERM, |
| 72 | POLICYDB_CAPABILITY_REDHAT1, | 72 | POLICYDB_CAPABILITY_EXTSOCKCLASS, |
| 73 | POLICYDB_CAPABILITY_ALWAYSNETWORK, | 73 | POLICYDB_CAPABILITY_ALWAYSNETWORK, |
| 74 | __POLICYDB_CAPABILITY_MAX | 74 | __POLICYDB_CAPABILITY_MAX |
| 75 | }; | 75 | }; |
| @@ -77,6 +77,7 @@ enum { | |||
| 77 | 77 | ||
| 78 | extern int selinux_policycap_netpeer; | 78 | extern int selinux_policycap_netpeer; |
| 79 | extern int selinux_policycap_openperm; | 79 | extern int selinux_policycap_openperm; |
| 80 | extern int selinux_policycap_extsockclass; | ||
| 80 | extern int selinux_policycap_alwaysnetwork; | 81 | extern int selinux_policycap_alwaysnetwork; |
| 81 | 82 | ||
| 82 | /* | 83 | /* |
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index cf9293e01fc1..c354807381c1 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c | |||
| @@ -45,7 +45,7 @@ | |||
| 45 | static char *policycap_names[] = { | 45 | static char *policycap_names[] = { |
| 46 | "network_peer_controls", | 46 | "network_peer_controls", |
| 47 | "open_perms", | 47 | "open_perms", |
| 48 | "redhat1", | 48 | "extended_socket_class", |
| 49 | "always_check_network" | 49 | "always_check_network" |
| 50 | }; | 50 | }; |
| 51 | 51 | ||
| @@ -77,25 +77,6 @@ static char policy_opened; | |||
| 77 | /* global data for policy capabilities */ | 77 | /* global data for policy capabilities */ |
| 78 | static struct dentry *policycap_dir; | 78 | static struct dentry *policycap_dir; |
| 79 | 79 | ||
| 80 | /* Check whether a task is allowed to use a security operation. */ | ||
| 81 | static int task_has_security(struct task_struct *tsk, | ||
| 82 | u32 perms) | ||
| 83 | { | ||
| 84 | const struct task_security_struct *tsec; | ||
| 85 | u32 sid = 0; | ||
| 86 | |||
| 87 | rcu_read_lock(); | ||
| 88 | tsec = __task_cred(tsk)->security; | ||
| 89 | if (tsec) | ||
| 90 | sid = tsec->sid; | ||
| 91 | rcu_read_unlock(); | ||
| 92 | if (!tsec) | ||
| 93 | return -EACCES; | ||
| 94 | |||
| 95 | return avc_has_perm(sid, SECINITSID_SECURITY, | ||
| 96 | SECCLASS_SECURITY, perms, NULL); | ||
| 97 | } | ||
| 98 | |||
| 99 | enum sel_inos { | 80 | enum sel_inos { |
| 100 | SEL_ROOT_INO = 2, | 81 | SEL_ROOT_INO = 2, |
| 101 | SEL_LOAD, /* load policy */ | 82 | SEL_LOAD, /* load policy */ |
| @@ -166,7 +147,9 @@ static ssize_t sel_write_enforce(struct file *file, const char __user *buf, | |||
| 166 | new_value = !!new_value; | 147 | new_value = !!new_value; |
| 167 | 148 | ||
| 168 | if (new_value != selinux_enforcing) { | 149 | if (new_value != selinux_enforcing) { |
| 169 | length = task_has_security(current, SECURITY__SETENFORCE); | 150 | length = avc_has_perm(current_sid(), SECINITSID_SECURITY, |
| 151 | SECCLASS_SECURITY, SECURITY__SETENFORCE, | ||
| 152 | NULL); | ||
| 170 | if (length) | 153 | if (length) |
| 171 | goto out; | 154 | goto out; |
| 172 | audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_STATUS, | 155 | audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_STATUS, |
| @@ -368,7 +351,8 @@ static int sel_open_policy(struct inode *inode, struct file *filp) | |||
| 368 | 351 | ||
| 369 | mutex_lock(&sel_mutex); | 352 | mutex_lock(&sel_mutex); |
| 370 | 353 | ||
| 371 | rc = task_has_security(current, SECURITY__READ_POLICY); | 354 | rc = avc_has_perm(current_sid(), SECINITSID_SECURITY, |
| 355 | SECCLASS_SECURITY, SECURITY__READ_POLICY, NULL); | ||
| 372 | if (rc) | 356 | if (rc) |
| 373 | goto err; | 357 | goto err; |
| 374 | 358 | ||
| @@ -429,7 +413,8 @@ static ssize_t sel_read_policy(struct file *filp, char __user *buf, | |||
| 429 | 413 | ||
| 430 | mutex_lock(&sel_mutex); | 414 | mutex_lock(&sel_mutex); |
| 431 | 415 | ||
| 432 | ret = task_has_security(current, SECURITY__READ_POLICY); | 416 | ret = avc_has_perm(current_sid(), SECINITSID_SECURITY, |
| 417 | SECCLASS_SECURITY, SECURITY__READ_POLICY, NULL); | ||
| 433 | if (ret) | 418 | if (ret) |
| 434 | goto out; | 419 | goto out; |
| 435 | 420 | ||
| @@ -499,7 +484,8 @@ static ssize_t sel_write_load(struct file *file, const char __user *buf, | |||
| 499 | 484 | ||
| 500 | mutex_lock(&sel_mutex); | 485 | mutex_lock(&sel_mutex); |
| 501 | 486 | ||
| 502 | length = task_has_security(current, SECURITY__LOAD_POLICY); | 487 | length = avc_has_perm(current_sid(), SECINITSID_SECURITY, |
| 488 | SECCLASS_SECURITY, SECURITY__LOAD_POLICY, NULL); | ||
| 503 | if (length) | 489 | if (length) |
| 504 | goto out; | 490 | goto out; |
| 505 | 491 | ||
| @@ -522,20 +508,28 @@ static ssize_t sel_write_load(struct file *file, const char __user *buf, | |||
| 522 | goto out; | 508 | goto out; |
| 523 | 509 | ||
| 524 | length = security_load_policy(data, count); | 510 | length = security_load_policy(data, count); |
| 525 | if (length) | 511 | if (length) { |
| 512 | pr_warn_ratelimited("SELinux: failed to load policy\n"); | ||
| 526 | goto out; | 513 | goto out; |
| 514 | } | ||
| 527 | 515 | ||
| 528 | length = sel_make_bools(); | 516 | length = sel_make_bools(); |
| 529 | if (length) | 517 | if (length) { |
| 518 | pr_err("SELinux: failed to load policy booleans\n"); | ||
| 530 | goto out1; | 519 | goto out1; |
| 520 | } | ||
| 531 | 521 | ||
| 532 | length = sel_make_classes(); | 522 | length = sel_make_classes(); |
| 533 | if (length) | 523 | if (length) { |
| 524 | pr_err("SELinux: failed to load policy classes\n"); | ||
| 534 | goto out1; | 525 | goto out1; |
| 526 | } | ||
| 535 | 527 | ||
| 536 | length = sel_make_policycap(); | 528 | length = sel_make_policycap(); |
| 537 | if (length) | 529 | if (length) { |
| 530 | pr_err("SELinux: failed to load policy capabilities\n"); | ||
| 538 | goto out1; | 531 | goto out1; |
| 532 | } | ||
| 539 | 533 | ||
| 540 | length = count; | 534 | length = count; |
| 541 | 535 | ||
| @@ -561,7 +555,8 @@ static ssize_t sel_write_context(struct file *file, char *buf, size_t size) | |||
| 561 | u32 sid, len; | 555 | u32 sid, len; |
| 562 | ssize_t length; | 556 | ssize_t length; |
| 563 | 557 | ||
| 564 | length = task_has_security(current, SECURITY__CHECK_CONTEXT); | 558 | length = avc_has_perm(current_sid(), SECINITSID_SECURITY, |
| 559 | SECCLASS_SECURITY, SECURITY__CHECK_CONTEXT, NULL); | ||
| 565 | if (length) | 560 | if (length) |
| 566 | goto out; | 561 | goto out; |
| 567 | 562 | ||
| @@ -604,7 +599,9 @@ static ssize_t sel_write_checkreqprot(struct file *file, const char __user *buf, | |||
| 604 | ssize_t length; | 599 | ssize_t length; |
| 605 | unsigned int new_value; | 600 | unsigned int new_value; |
| 606 | 601 | ||
| 607 | length = task_has_security(current, SECURITY__SETCHECKREQPROT); | 602 | length = avc_has_perm(current_sid(), SECINITSID_SECURITY, |
| 603 | SECCLASS_SECURITY, SECURITY__SETCHECKREQPROT, | ||
| 604 | NULL); | ||
| 608 | if (length) | 605 | if (length) |
| 609 | return length; | 606 | return length; |
| 610 | 607 | ||
| @@ -645,7 +642,8 @@ static ssize_t sel_write_validatetrans(struct file *file, | |||
| 645 | u16 tclass; | 642 | u16 tclass; |
| 646 | int rc; | 643 | int rc; |
| 647 | 644 | ||
| 648 | rc = task_has_security(current, SECURITY__VALIDATE_TRANS); | 645 | rc = avc_has_perm(current_sid(), SECINITSID_SECURITY, |
| 646 | SECCLASS_SECURITY, SECURITY__VALIDATE_TRANS, NULL); | ||
| 649 | if (rc) | 647 | if (rc) |
| 650 | goto out; | 648 | goto out; |
| 651 | 649 | ||
| @@ -772,7 +770,8 @@ static ssize_t sel_write_access(struct file *file, char *buf, size_t size) | |||
| 772 | struct av_decision avd; | 770 | struct av_decision avd; |
| 773 | ssize_t length; | 771 | ssize_t length; |
| 774 | 772 | ||
| 775 | length = task_has_security(current, SECURITY__COMPUTE_AV); | 773 | length = avc_has_perm(current_sid(), SECINITSID_SECURITY, |
| 774 | SECCLASS_SECURITY, SECURITY__COMPUTE_AV, NULL); | ||
| 776 | if (length) | 775 | if (length) |
| 777 | goto out; | 776 | goto out; |
| 778 | 777 | ||
| @@ -822,7 +821,9 @@ static ssize_t sel_write_create(struct file *file, char *buf, size_t size) | |||
| 822 | u32 len; | 821 | u32 len; |
| 823 | int nargs; | 822 | int nargs; |
| 824 | 823 | ||
| 825 | length = task_has_security(current, SECURITY__COMPUTE_CREATE); | 824 | length = avc_has_perm(current_sid(), SECINITSID_SECURITY, |
| 825 | SECCLASS_SECURITY, SECURITY__COMPUTE_CREATE, | ||
| 826 | NULL); | ||
| 826 | if (length) | 827 | if (length) |
| 827 | goto out; | 828 | goto out; |
| 828 | 829 | ||
| @@ -919,7 +920,9 @@ static ssize_t sel_write_relabel(struct file *file, char *buf, size_t size) | |||
| 919 | char *newcon = NULL; | 920 | char *newcon = NULL; |
| 920 | u32 len; | 921 | u32 len; |
| 921 | 922 | ||
| 922 | length = task_has_security(current, SECURITY__COMPUTE_RELABEL); | 923 | length = avc_has_perm(current_sid(), SECINITSID_SECURITY, |
| 924 | SECCLASS_SECURITY, SECURITY__COMPUTE_RELABEL, | ||
| 925 | NULL); | ||
| 923 | if (length) | 926 | if (length) |
| 924 | goto out; | 927 | goto out; |
| 925 | 928 | ||
| @@ -975,7 +978,9 @@ static ssize_t sel_write_user(struct file *file, char *buf, size_t size) | |||
| 975 | int i, rc; | 978 | int i, rc; |
| 976 | u32 len, nsids; | 979 | u32 len, nsids; |
| 977 | 980 | ||
| 978 | length = task_has_security(current, SECURITY__COMPUTE_USER); | 981 | length = avc_has_perm(current_sid(), SECINITSID_SECURITY, |
| 982 | SECCLASS_SECURITY, SECURITY__COMPUTE_USER, | ||
| 983 | NULL); | ||
| 979 | if (length) | 984 | if (length) |
| 980 | goto out; | 985 | goto out; |
| 981 | 986 | ||
| @@ -1035,7 +1040,9 @@ static ssize_t sel_write_member(struct file *file, char *buf, size_t size) | |||
| 1035 | char *newcon = NULL; | 1040 | char *newcon = NULL; |
| 1036 | u32 len; | 1041 | u32 len; |
| 1037 | 1042 | ||
| 1038 | length = task_has_security(current, SECURITY__COMPUTE_MEMBER); | 1043 | length = avc_has_perm(current_sid(), SECINITSID_SECURITY, |
| 1044 | SECCLASS_SECURITY, SECURITY__COMPUTE_MEMBER, | ||
| 1045 | NULL); | ||
| 1039 | if (length) | 1046 | if (length) |
| 1040 | goto out; | 1047 | goto out; |
| 1041 | 1048 | ||
| @@ -1142,7 +1149,9 @@ static ssize_t sel_write_bool(struct file *filep, const char __user *buf, | |||
| 1142 | 1149 | ||
| 1143 | mutex_lock(&sel_mutex); | 1150 | mutex_lock(&sel_mutex); |
| 1144 | 1151 | ||
| 1145 | length = task_has_security(current, SECURITY__SETBOOL); | 1152 | length = avc_has_perm(current_sid(), SECINITSID_SECURITY, |
| 1153 | SECCLASS_SECURITY, SECURITY__SETBOOL, | ||
| 1154 | NULL); | ||
| 1146 | if (length) | 1155 | if (length) |
| 1147 | goto out; | 1156 | goto out; |
| 1148 | 1157 | ||
| @@ -1198,7 +1207,9 @@ static ssize_t sel_commit_bools_write(struct file *filep, | |||
| 1198 | 1207 | ||
| 1199 | mutex_lock(&sel_mutex); | 1208 | mutex_lock(&sel_mutex); |
| 1200 | 1209 | ||
| 1201 | length = task_has_security(current, SECURITY__SETBOOL); | 1210 | length = avc_has_perm(current_sid(), SECINITSID_SECURITY, |
| 1211 | SECCLASS_SECURITY, SECURITY__SETBOOL, | ||
| 1212 | NULL); | ||
| 1202 | if (length) | 1213 | if (length) |
| 1203 | goto out; | 1214 | goto out; |
| 1204 | 1215 | ||
| @@ -1299,8 +1310,11 @@ static int sel_make_bools(void) | |||
| 1299 | 1310 | ||
| 1300 | isec = (struct inode_security_struct *)inode->i_security; | 1311 | isec = (struct inode_security_struct *)inode->i_security; |
| 1301 | ret = security_genfs_sid("selinuxfs", page, SECCLASS_FILE, &sid); | 1312 | ret = security_genfs_sid("selinuxfs", page, SECCLASS_FILE, &sid); |
| 1302 | if (ret) | 1313 | if (ret) { |
| 1303 | goto out; | 1314 | pr_warn_ratelimited("SELinux: no sid found, defaulting to security isid for %s\n", |
| 1315 | page); | ||
| 1316 | sid = SECINITSID_SECURITY; | ||
| 1317 | } | ||
| 1304 | 1318 | ||
| 1305 | isec->sid = sid; | 1319 | isec->sid = sid; |
| 1306 | isec->initialized = LABEL_INITIALIZED; | 1320 | isec->initialized = LABEL_INITIALIZED; |
| @@ -1351,7 +1365,9 @@ static ssize_t sel_write_avc_cache_threshold(struct file *file, | |||
| 1351 | ssize_t ret; | 1365 | ssize_t ret; |
| 1352 | unsigned int new_value; | 1366 | unsigned int new_value; |
| 1353 | 1367 | ||
| 1354 | ret = task_has_security(current, SECURITY__SETSECPARAM); | 1368 | ret = avc_has_perm(current_sid(), SECINITSID_SECURITY, |
| 1369 | SECCLASS_SECURITY, SECURITY__SETSECPARAM, | ||
| 1370 | NULL); | ||
| 1355 | if (ret) | 1371 | if (ret) |
| 1356 | return ret; | 1372 | return ret; |
| 1357 | 1373 | ||
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 082b20c78363..a70fcee9824b 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c | |||
| @@ -72,6 +72,7 @@ | |||
| 72 | 72 | ||
| 73 | int selinux_policycap_netpeer; | 73 | int selinux_policycap_netpeer; |
| 74 | int selinux_policycap_openperm; | 74 | int selinux_policycap_openperm; |
| 75 | int selinux_policycap_extsockclass; | ||
| 75 | int selinux_policycap_alwaysnetwork; | 76 | int selinux_policycap_alwaysnetwork; |
| 76 | 77 | ||
| 77 | static DEFINE_RWLOCK(policy_rwlock); | 78 | static DEFINE_RWLOCK(policy_rwlock); |
| @@ -1988,6 +1989,8 @@ static void security_load_policycaps(void) | |||
| 1988 | POLICYDB_CAPABILITY_NETPEER); | 1989 | POLICYDB_CAPABILITY_NETPEER); |
| 1989 | selinux_policycap_openperm = ebitmap_get_bit(&policydb.policycaps, | 1990 | selinux_policycap_openperm = ebitmap_get_bit(&policydb.policycaps, |
| 1990 | POLICYDB_CAPABILITY_OPENPERM); | 1991 | POLICYDB_CAPABILITY_OPENPERM); |
| 1992 | selinux_policycap_extsockclass = ebitmap_get_bit(&policydb.policycaps, | ||
| 1993 | POLICYDB_CAPABILITY_EXTSOCKCLASS); | ||
| 1991 | selinux_policycap_alwaysnetwork = ebitmap_get_bit(&policydb.policycaps, | 1994 | selinux_policycap_alwaysnetwork = ebitmap_get_bit(&policydb.policycaps, |
| 1992 | POLICYDB_CAPABILITY_ALWAYSNETWORK); | 1995 | POLICYDB_CAPABILITY_ALWAYSNETWORK); |
| 1993 | } | 1996 | } |
diff --git a/security/smack/smack.h b/security/smack/smack.h index 77abe2efacae..612b810fbbc6 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h | |||
| @@ -114,6 +114,7 @@ struct inode_smack { | |||
| 114 | struct smack_known *smk_mmap; /* label of the mmap domain */ | 114 | struct smack_known *smk_mmap; /* label of the mmap domain */ |
| 115 | struct mutex smk_lock; /* initialization lock */ | 115 | struct mutex smk_lock; /* initialization lock */ |
| 116 | int smk_flags; /* smack inode flags */ | 116 | int smk_flags; /* smack inode flags */ |
| 117 | struct rcu_head smk_rcu; /* for freeing inode_smack */ | ||
| 117 | }; | 118 | }; |
| 118 | 119 | ||
| 119 | struct task_smack { | 120 | struct task_smack { |
| @@ -173,6 +174,8 @@ struct smk_port_label { | |||
| 173 | unsigned short smk_port; /* the port number */ | 174 | unsigned short smk_port; /* the port number */ |
| 174 | struct smack_known *smk_in; /* inbound label */ | 175 | struct smack_known *smk_in; /* inbound label */ |
| 175 | struct smack_known *smk_out; /* outgoing label */ | 176 | struct smack_known *smk_out; /* outgoing label */ |
| 177 | short smk_sock_type; /* Socket type */ | ||
| 178 | short smk_can_reuse; | ||
| 176 | }; | 179 | }; |
| 177 | #endif /* SMACK_IPV6_PORT_LABELING */ | 180 | #endif /* SMACK_IPV6_PORT_LABELING */ |
| 178 | 181 | ||
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 94dc9d406ce3..60b4217b9b68 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c | |||
| @@ -52,6 +52,7 @@ | |||
| 52 | #define SMK_SENDING 2 | 52 | #define SMK_SENDING 2 |
| 53 | 53 | ||
| 54 | #ifdef SMACK_IPV6_PORT_LABELING | 54 | #ifdef SMACK_IPV6_PORT_LABELING |
| 55 | DEFINE_MUTEX(smack_ipv6_lock); | ||
| 55 | static LIST_HEAD(smk_ipv6_port_list); | 56 | static LIST_HEAD(smk_ipv6_port_list); |
| 56 | #endif | 57 | #endif |
| 57 | static struct kmem_cache *smack_inode_cache; | 58 | static struct kmem_cache *smack_inode_cache; |
| @@ -347,8 +348,6 @@ static int smk_copy_rules(struct list_head *nhead, struct list_head *ohead, | |||
| 347 | struct smack_rule *orp; | 348 | struct smack_rule *orp; |
| 348 | int rc = 0; | 349 | int rc = 0; |
| 349 | 350 | ||
| 350 | INIT_LIST_HEAD(nhead); | ||
| 351 | |||
| 352 | list_for_each_entry_rcu(orp, ohead, list) { | 351 | list_for_each_entry_rcu(orp, ohead, list) { |
| 353 | nrp = kzalloc(sizeof(struct smack_rule), gfp); | 352 | nrp = kzalloc(sizeof(struct smack_rule), gfp); |
| 354 | if (nrp == NULL) { | 353 | if (nrp == NULL) { |
| @@ -375,8 +374,6 @@ static int smk_copy_relabel(struct list_head *nhead, struct list_head *ohead, | |||
| 375 | struct smack_known_list_elem *nklep; | 374 | struct smack_known_list_elem *nklep; |
| 376 | struct smack_known_list_elem *oklep; | 375 | struct smack_known_list_elem *oklep; |
| 377 | 376 | ||
| 378 | INIT_LIST_HEAD(nhead); | ||
| 379 | |||
| 380 | list_for_each_entry(oklep, ohead, list) { | 377 | list_for_each_entry(oklep, ohead, list) { |
| 381 | nklep = kzalloc(sizeof(struct smack_known_list_elem), gfp); | 378 | nklep = kzalloc(sizeof(struct smack_known_list_elem), gfp); |
| 382 | if (nklep == NULL) { | 379 | if (nklep == NULL) { |
| @@ -1009,15 +1006,39 @@ static int smack_inode_alloc_security(struct inode *inode) | |||
| 1009 | } | 1006 | } |
| 1010 | 1007 | ||
| 1011 | /** | 1008 | /** |
| 1012 | * smack_inode_free_security - free an inode blob | 1009 | * smack_inode_free_rcu - Free inode_smack blob from cache |
| 1010 | * @head: the rcu_head for getting inode_smack pointer | ||
| 1011 | * | ||
| 1012 | * Call back function called from call_rcu() to free | ||
| 1013 | * the i_security blob pointer in inode | ||
| 1014 | */ | ||
| 1015 | static void smack_inode_free_rcu(struct rcu_head *head) | ||
| 1016 | { | ||
| 1017 | struct inode_smack *issp; | ||
| 1018 | |||
| 1019 | issp = container_of(head, struct inode_smack, smk_rcu); | ||
| 1020 | kmem_cache_free(smack_inode_cache, issp); | ||
| 1021 | } | ||
| 1022 | |||
| 1023 | /** | ||
| 1024 | * smack_inode_free_security - free an inode blob using call_rcu() | ||
| 1013 | * @inode: the inode with a blob | 1025 | * @inode: the inode with a blob |
| 1014 | * | 1026 | * |
| 1015 | * Clears the blob pointer in inode | 1027 | * Clears the blob pointer in inode using RCU |
| 1016 | */ | 1028 | */ |
| 1017 | static void smack_inode_free_security(struct inode *inode) | 1029 | static void smack_inode_free_security(struct inode *inode) |
| 1018 | { | 1030 | { |
| 1019 | kmem_cache_free(smack_inode_cache, inode->i_security); | 1031 | struct inode_smack *issp = inode->i_security; |
| 1020 | inode->i_security = NULL; | 1032 | |
| 1033 | /* | ||
| 1034 | * The inode may still be referenced in a path walk and | ||
| 1035 | * a call to smack_inode_permission() can be made | ||
| 1036 | * after smack_inode_free_security() is called. | ||
| 1037 | * To avoid race condition free the i_security via RCU | ||
| 1038 | * and leave the current inode->i_security pointer intact. | ||
| 1039 | * The inode will be freed after the RCU grace period too. | ||
| 1040 | */ | ||
| 1041 | call_rcu(&issp->smk_rcu, smack_inode_free_rcu); | ||
| 1021 | } | 1042 | } |
| 1022 | 1043 | ||
| 1023 | /** | 1044 | /** |
| @@ -1626,6 +1647,9 @@ static int smack_file_ioctl(struct file *file, unsigned int cmd, | |||
| 1626 | struct smk_audit_info ad; | 1647 | struct smk_audit_info ad; |
| 1627 | struct inode *inode = file_inode(file); | 1648 | struct inode *inode = file_inode(file); |
| 1628 | 1649 | ||
| 1650 | if (unlikely(IS_PRIVATE(inode))) | ||
| 1651 | return 0; | ||
| 1652 | |||
| 1629 | smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); | 1653 | smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); |
| 1630 | smk_ad_setfield_u_fs_path(&ad, file->f_path); | 1654 | smk_ad_setfield_u_fs_path(&ad, file->f_path); |
| 1631 | 1655 | ||
| @@ -1655,6 +1679,9 @@ static int smack_file_lock(struct file *file, unsigned int cmd) | |||
| 1655 | int rc; | 1679 | int rc; |
| 1656 | struct inode *inode = file_inode(file); | 1680 | struct inode *inode = file_inode(file); |
| 1657 | 1681 | ||
| 1682 | if (unlikely(IS_PRIVATE(inode))) | ||
| 1683 | return 0; | ||
| 1684 | |||
| 1658 | smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); | 1685 | smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); |
| 1659 | smk_ad_setfield_u_fs_path(&ad, file->f_path); | 1686 | smk_ad_setfield_u_fs_path(&ad, file->f_path); |
| 1660 | rc = smk_curacc(smk_of_inode(inode), MAY_LOCK, &ad); | 1687 | rc = smk_curacc(smk_of_inode(inode), MAY_LOCK, &ad); |
| @@ -1681,6 +1708,9 @@ static int smack_file_fcntl(struct file *file, unsigned int cmd, | |||
| 1681 | int rc = 0; | 1708 | int rc = 0; |
| 1682 | struct inode *inode = file_inode(file); | 1709 | struct inode *inode = file_inode(file); |
| 1683 | 1710 | ||
| 1711 | if (unlikely(IS_PRIVATE(inode))) | ||
| 1712 | return 0; | ||
| 1713 | |||
| 1684 | switch (cmd) { | 1714 | switch (cmd) { |
| 1685 | case F_GETLK: | 1715 | case F_GETLK: |
| 1686 | break; | 1716 | break; |
| @@ -1734,6 +1764,9 @@ static int smack_mmap_file(struct file *file, | |||
| 1734 | if (file == NULL) | 1764 | if (file == NULL) |
| 1735 | return 0; | 1765 | return 0; |
| 1736 | 1766 | ||
| 1767 | if (unlikely(IS_PRIVATE(file_inode(file)))) | ||
| 1768 | return 0; | ||
| 1769 | |||
| 1737 | isp = file_inode(file)->i_security; | 1770 | isp = file_inode(file)->i_security; |
| 1738 | if (isp->smk_mmap == NULL) | 1771 | if (isp->smk_mmap == NULL) |
| 1739 | return 0; | 1772 | return 0; |
| @@ -1934,12 +1967,9 @@ static int smack_file_open(struct file *file, const struct cred *cred) | |||
| 1934 | struct smk_audit_info ad; | 1967 | struct smk_audit_info ad; |
| 1935 | int rc; | 1968 | int rc; |
| 1936 | 1969 | ||
| 1937 | if (smack_privileged(CAP_MAC_OVERRIDE)) | ||
| 1938 | return 0; | ||
| 1939 | |||
| 1940 | smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); | 1970 | smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); |
| 1941 | smk_ad_setfield_u_fs_path(&ad, file->f_path); | 1971 | smk_ad_setfield_u_fs_path(&ad, file->f_path); |
| 1942 | rc = smk_access(tsp->smk_task, smk_of_inode(inode), MAY_READ, &ad); | 1972 | rc = smk_tskacc(tsp, smk_of_inode(inode), MAY_READ, &ad); |
| 1943 | rc = smk_bu_credfile(cred, file, MAY_READ, rc); | 1973 | rc = smk_bu_credfile(cred, file, MAY_READ, rc); |
| 1944 | 1974 | ||
| 1945 | return rc; | 1975 | return rc; |
| @@ -2272,25 +2302,6 @@ static int smack_task_kill(struct task_struct *p, struct siginfo *info, | |||
| 2272 | } | 2302 | } |
| 2273 | 2303 | ||
| 2274 | /** | 2304 | /** |
| 2275 | * smack_task_wait - Smack access check for waiting | ||
| 2276 | * @p: task to wait for | ||
| 2277 | * | ||
| 2278 | * Returns 0 | ||
| 2279 | */ | ||
| 2280 | static int smack_task_wait(struct task_struct *p) | ||
| 2281 | { | ||
| 2282 | /* | ||
| 2283 | * Allow the operation to succeed. | ||
| 2284 | * Zombies are bad. | ||
| 2285 | * In userless environments (e.g. phones) programs | ||
| 2286 | * get marked with SMACK64EXEC and even if the parent | ||
| 2287 | * and child shouldn't be talking the parent still | ||
| 2288 | * may expect to know when the child exits. | ||
| 2289 | */ | ||
| 2290 | return 0; | ||
| 2291 | } | ||
| 2292 | |||
| 2293 | /** | ||
| 2294 | * smack_task_to_inode - copy task smack into the inode blob | 2305 | * smack_task_to_inode - copy task smack into the inode blob |
| 2295 | * @p: task to copy from | 2306 | * @p: task to copy from |
| 2296 | * @inode: inode to copy to | 2307 | * @inode: inode to copy to |
| @@ -2353,6 +2364,20 @@ static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags) | |||
| 2353 | */ | 2364 | */ |
| 2354 | static void smack_sk_free_security(struct sock *sk) | 2365 | static void smack_sk_free_security(struct sock *sk) |
| 2355 | { | 2366 | { |
| 2367 | #ifdef SMACK_IPV6_PORT_LABELING | ||
| 2368 | struct smk_port_label *spp; | ||
| 2369 | |||
| 2370 | if (sk->sk_family == PF_INET6) { | ||
| 2371 | rcu_read_lock(); | ||
| 2372 | list_for_each_entry_rcu(spp, &smk_ipv6_port_list, list) { | ||
| 2373 | if (spp->smk_sock != sk) | ||
| 2374 | continue; | ||
| 2375 | spp->smk_can_reuse = 1; | ||
| 2376 | break; | ||
| 2377 | } | ||
| 2378 | rcu_read_unlock(); | ||
| 2379 | } | ||
| 2380 | #endif | ||
| 2356 | kfree(sk->sk_security); | 2381 | kfree(sk->sk_security); |
| 2357 | } | 2382 | } |
| 2358 | 2383 | ||
| @@ -2603,17 +2628,20 @@ static void smk_ipv6_port_label(struct socket *sock, struct sockaddr *address) | |||
| 2603 | * on the bound socket. Take the changes to the port | 2628 | * on the bound socket. Take the changes to the port |
| 2604 | * as well. | 2629 | * as well. |
| 2605 | */ | 2630 | */ |
| 2606 | list_for_each_entry(spp, &smk_ipv6_port_list, list) { | 2631 | rcu_read_lock(); |
| 2632 | list_for_each_entry_rcu(spp, &smk_ipv6_port_list, list) { | ||
| 2607 | if (sk != spp->smk_sock) | 2633 | if (sk != spp->smk_sock) |
| 2608 | continue; | 2634 | continue; |
| 2609 | spp->smk_in = ssp->smk_in; | 2635 | spp->smk_in = ssp->smk_in; |
| 2610 | spp->smk_out = ssp->smk_out; | 2636 | spp->smk_out = ssp->smk_out; |
| 2637 | rcu_read_unlock(); | ||
| 2611 | return; | 2638 | return; |
| 2612 | } | 2639 | } |
| 2613 | /* | 2640 | /* |
| 2614 | * A NULL address is only used for updating existing | 2641 | * A NULL address is only used for updating existing |
| 2615 | * bound entries. If there isn't one, it's OK. | 2642 | * bound entries. If there isn't one, it's OK. |
| 2616 | */ | 2643 | */ |
| 2644 | rcu_read_unlock(); | ||
| 2617 | return; | 2645 | return; |
| 2618 | } | 2646 | } |
| 2619 | 2647 | ||
| @@ -2629,16 +2657,23 @@ static void smk_ipv6_port_label(struct socket *sock, struct sockaddr *address) | |||
| 2629 | * Look for an existing port list entry. | 2657 | * Look for an existing port list entry. |
| 2630 | * This is an indication that a port is getting reused. | 2658 | * This is an indication that a port is getting reused. |
| 2631 | */ | 2659 | */ |
| 2632 | list_for_each_entry(spp, &smk_ipv6_port_list, list) { | 2660 | rcu_read_lock(); |
| 2633 | if (spp->smk_port != port) | 2661 | list_for_each_entry_rcu(spp, &smk_ipv6_port_list, list) { |
| 2662 | if (spp->smk_port != port || spp->smk_sock_type != sock->type) | ||
| 2634 | continue; | 2663 | continue; |
| 2664 | if (spp->smk_can_reuse != 1) { | ||
| 2665 | rcu_read_unlock(); | ||
| 2666 | return; | ||
| 2667 | } | ||
| 2635 | spp->smk_port = port; | 2668 | spp->smk_port = port; |
| 2636 | spp->smk_sock = sk; | 2669 | spp->smk_sock = sk; |
| 2637 | spp->smk_in = ssp->smk_in; | 2670 | spp->smk_in = ssp->smk_in; |
| 2638 | spp->smk_out = ssp->smk_out; | 2671 | spp->smk_out = ssp->smk_out; |
| 2672 | spp->smk_can_reuse = 0; | ||
| 2673 | rcu_read_unlock(); | ||
| 2639 | return; | 2674 | return; |
| 2640 | } | 2675 | } |
| 2641 | 2676 | rcu_read_unlock(); | |
| 2642 | /* | 2677 | /* |
| 2643 | * A new port entry is required. | 2678 | * A new port entry is required. |
| 2644 | */ | 2679 | */ |
| @@ -2650,8 +2685,12 @@ static void smk_ipv6_port_label(struct socket *sock, struct sockaddr *address) | |||
| 2650 | spp->smk_sock = sk; | 2685 | spp->smk_sock = sk; |
| 2651 | spp->smk_in = ssp->smk_in; | 2686 | spp->smk_in = ssp->smk_in; |
| 2652 | spp->smk_out = ssp->smk_out; | 2687 | spp->smk_out = ssp->smk_out; |
| 2688 | spp->smk_sock_type = sock->type; | ||
| 2689 | spp->smk_can_reuse = 0; | ||
| 2653 | 2690 | ||
| 2654 | list_add(&spp->list, &smk_ipv6_port_list); | 2691 | mutex_lock(&smack_ipv6_lock); |
| 2692 | list_add_rcu(&spp->list, &smk_ipv6_port_list); | ||
| 2693 | mutex_unlock(&smack_ipv6_lock); | ||
| 2655 | return; | 2694 | return; |
| 2656 | } | 2695 | } |
| 2657 | 2696 | ||
| @@ -2702,14 +2741,16 @@ static int smk_ipv6_port_check(struct sock *sk, struct sockaddr_in6 *address, | |||
| 2702 | return 0; | 2741 | return 0; |
| 2703 | 2742 | ||
| 2704 | port = ntohs(address->sin6_port); | 2743 | port = ntohs(address->sin6_port); |
| 2705 | list_for_each_entry(spp, &smk_ipv6_port_list, list) { | 2744 | rcu_read_lock(); |
| 2706 | if (spp->smk_port != port) | 2745 | list_for_each_entry_rcu(spp, &smk_ipv6_port_list, list) { |
| 2746 | if (spp->smk_port != port || spp->smk_sock_type != sk->sk_type) | ||
| 2707 | continue; | 2747 | continue; |
| 2708 | object = spp->smk_in; | 2748 | object = spp->smk_in; |
| 2709 | if (act == SMK_CONNECTING) | 2749 | if (act == SMK_CONNECTING) |
| 2710 | ssp->smk_packet = spp->smk_out; | 2750 | ssp->smk_packet = spp->smk_out; |
| 2711 | break; | 2751 | break; |
| 2712 | } | 2752 | } |
| 2753 | rcu_read_unlock(); | ||
| 2713 | 2754 | ||
| 2714 | return smk_ipv6_check(skp, object, address, act); | 2755 | return smk_ipv6_check(skp, object, address, act); |
| 2715 | } | 2756 | } |
| @@ -3438,6 +3479,13 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) | |||
| 3438 | case PIPEFS_MAGIC: | 3479 | case PIPEFS_MAGIC: |
| 3439 | isp->smk_inode = smk_of_current(); | 3480 | isp->smk_inode = smk_of_current(); |
| 3440 | break; | 3481 | break; |
| 3482 | case SOCKFS_MAGIC: | ||
| 3483 | /* | ||
| 3484 | * Socket access is controlled by the socket | ||
| 3485 | * structures associated with the task involved. | ||
| 3486 | */ | ||
| 3487 | isp->smk_inode = &smack_known_star; | ||
| 3488 | break; | ||
| 3441 | default: | 3489 | default: |
| 3442 | isp->smk_inode = sbsp->smk_root; | 3490 | isp->smk_inode = sbsp->smk_root; |
| 3443 | break; | 3491 | break; |
| @@ -3454,19 +3502,12 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) | |||
| 3454 | */ | 3502 | */ |
| 3455 | switch (sbp->s_magic) { | 3503 | switch (sbp->s_magic) { |
| 3456 | case SMACK_MAGIC: | 3504 | case SMACK_MAGIC: |
| 3457 | case PIPEFS_MAGIC: | ||
| 3458 | case SOCKFS_MAGIC: | ||
| 3459 | case CGROUP_SUPER_MAGIC: | 3505 | case CGROUP_SUPER_MAGIC: |
| 3460 | /* | 3506 | /* |
| 3461 | * Casey says that it's a little embarrassing | 3507 | * Casey says that it's a little embarrassing |
| 3462 | * that the smack file system doesn't do | 3508 | * that the smack file system doesn't do |
| 3463 | * extended attributes. | 3509 | * extended attributes. |
| 3464 | * | 3510 | * |
| 3465 | * Casey says pipes are easy (?) | ||
| 3466 | * | ||
| 3467 | * Socket access is controlled by the socket | ||
| 3468 | * structures associated with the task involved. | ||
| 3469 | * | ||
| 3470 | * Cgroupfs is special | 3511 | * Cgroupfs is special |
| 3471 | */ | 3512 | */ |
| 3472 | final = &smack_known_star; | 3513 | final = &smack_known_star; |
| @@ -3620,7 +3661,6 @@ static int smack_getprocattr(struct task_struct *p, char *name, char **value) | |||
| 3620 | 3661 | ||
| 3621 | /** | 3662 | /** |
| 3622 | * smack_setprocattr - Smack process attribute setting | 3663 | * smack_setprocattr - Smack process attribute setting |
| 3623 | * @p: the object task | ||
| 3624 | * @name: the name of the attribute in /proc/.../attr | 3664 | * @name: the name of the attribute in /proc/.../attr |
| 3625 | * @value: the value to set | 3665 | * @value: the value to set |
| 3626 | * @size: the size of the value | 3666 | * @size: the size of the value |
| @@ -3630,8 +3670,7 @@ static int smack_getprocattr(struct task_struct *p, char *name, char **value) | |||
| 3630 | * | 3670 | * |
| 3631 | * Returns the length of the smack label or an error code | 3671 | * Returns the length of the smack label or an error code |
| 3632 | */ | 3672 | */ |
| 3633 | static int smack_setprocattr(struct task_struct *p, char *name, | 3673 | static int smack_setprocattr(const char *name, void *value, size_t size) |
| 3634 | void *value, size_t size) | ||
| 3635 | { | 3674 | { |
| 3636 | struct task_smack *tsp = current_security(); | 3675 | struct task_smack *tsp = current_security(); |
| 3637 | struct cred *new; | 3676 | struct cred *new; |
| @@ -3639,13 +3678,6 @@ static int smack_setprocattr(struct task_struct *p, char *name, | |||
| 3639 | struct smack_known_list_elem *sklep; | 3678 | struct smack_known_list_elem *sklep; |
| 3640 | int rc; | 3679 | int rc; |
| 3641 | 3680 | ||
| 3642 | /* | ||
| 3643 | * Changing another process' Smack value is too dangerous | ||
| 3644 | * and supports no sane use case. | ||
| 3645 | */ | ||
| 3646 | if (p != current) | ||
| 3647 | return -EPERM; | ||
| 3648 | |||
| 3649 | if (!smack_privileged(CAP_MAC_ADMIN) && list_empty(&tsp->smk_relabel)) | 3681 | if (!smack_privileged(CAP_MAC_ADMIN) && list_empty(&tsp->smk_relabel)) |
| 3650 | return -EPERM; | 3682 | return -EPERM; |
| 3651 | 3683 | ||
| @@ -3849,7 +3881,7 @@ static struct smack_known *smack_from_secattr(struct netlbl_lsm_secattr *sap, | |||
| 3849 | * ambient value. | 3881 | * ambient value. |
| 3850 | */ | 3882 | */ |
| 3851 | rcu_read_lock(); | 3883 | rcu_read_lock(); |
| 3852 | list_for_each_entry(skp, &smack_known_list, list) { | 3884 | list_for_each_entry_rcu(skp, &smack_known_list, list) { |
| 3853 | if (sap->attr.mls.lvl != skp->smk_netlabel.attr.mls.lvl) | 3885 | if (sap->attr.mls.lvl != skp->smk_netlabel.attr.mls.lvl) |
| 3854 | continue; | 3886 | continue; |
| 3855 | /* | 3887 | /* |
| @@ -4667,7 +4699,6 @@ static struct security_hook_list smack_hooks[] = { | |||
| 4667 | LSM_HOOK_INIT(task_getscheduler, smack_task_getscheduler), | 4699 | LSM_HOOK_INIT(task_getscheduler, smack_task_getscheduler), |
| 4668 | LSM_HOOK_INIT(task_movememory, smack_task_movememory), | 4700 | LSM_HOOK_INIT(task_movememory, smack_task_movememory), |
| 4669 | LSM_HOOK_INIT(task_kill, smack_task_kill), | 4701 | LSM_HOOK_INIT(task_kill, smack_task_kill), |
| 4670 | LSM_HOOK_INIT(task_wait, smack_task_wait), | ||
| 4671 | LSM_HOOK_INIT(task_to_inode, smack_task_to_inode), | 4702 | LSM_HOOK_INIT(task_to_inode, smack_task_to_inode), |
| 4672 | 4703 | ||
| 4673 | LSM_HOOK_INIT(ipc_permission, smack_ipc_permission), | 4704 | LSM_HOOK_INIT(ipc_permission, smack_ipc_permission), |
| @@ -4819,7 +4850,7 @@ static __init int smack_init(void) | |||
| 4819 | /* | 4850 | /* |
| 4820 | * Register with LSM | 4851 | * Register with LSM |
| 4821 | */ | 4852 | */ |
| 4822 | security_add_hooks(smack_hooks, ARRAY_SIZE(smack_hooks)); | 4853 | security_add_hooks(smack_hooks, ARRAY_SIZE(smack_hooks), "smack"); |
| 4823 | 4854 | ||
| 4824 | return 0; | 4855 | return 0; |
| 4825 | } | 4856 | } |
diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index 13743a01b35b..366b8356f75b 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c | |||
| @@ -67,6 +67,7 @@ enum smk_inos { | |||
| 67 | /* | 67 | /* |
| 68 | * List locks | 68 | * List locks |
| 69 | */ | 69 | */ |
| 70 | static DEFINE_MUTEX(smack_master_list_lock); | ||
| 70 | static DEFINE_MUTEX(smack_cipso_lock); | 71 | static DEFINE_MUTEX(smack_cipso_lock); |
| 71 | static DEFINE_MUTEX(smack_ambient_lock); | 72 | static DEFINE_MUTEX(smack_ambient_lock); |
| 72 | static DEFINE_MUTEX(smk_net4addr_lock); | 73 | static DEFINE_MUTEX(smk_net4addr_lock); |
| @@ -262,12 +263,16 @@ static int smk_set_access(struct smack_parsed_rule *srp, | |||
| 262 | * it needs to get added for reporting. | 263 | * it needs to get added for reporting. |
| 263 | */ | 264 | */ |
| 264 | if (global) { | 265 | if (global) { |
| 266 | mutex_unlock(rule_lock); | ||
| 265 | smlp = kzalloc(sizeof(*smlp), GFP_KERNEL); | 267 | smlp = kzalloc(sizeof(*smlp), GFP_KERNEL); |
| 266 | if (smlp != NULL) { | 268 | if (smlp != NULL) { |
| 267 | smlp->smk_rule = sp; | 269 | smlp->smk_rule = sp; |
| 270 | mutex_lock(&smack_master_list_lock); | ||
| 268 | list_add_rcu(&smlp->list, &smack_rule_list); | 271 | list_add_rcu(&smlp->list, &smack_rule_list); |
| 272 | mutex_unlock(&smack_master_list_lock); | ||
| 269 | } else | 273 | } else |
| 270 | rc = -ENOMEM; | 274 | rc = -ENOMEM; |
| 275 | return rc; | ||
| 271 | } | 276 | } |
| 272 | } | 277 | } |
| 273 | 278 | ||
diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c index 75c998700190..edc52d620f29 100644 --- a/security/tomoyo/tomoyo.c +++ b/security/tomoyo/tomoyo.c | |||
| @@ -542,7 +542,7 @@ static int __init tomoyo_init(void) | |||
| 542 | if (!security_module_enable("tomoyo")) | 542 | if (!security_module_enable("tomoyo")) |
| 543 | return 0; | 543 | return 0; |
| 544 | /* register ourselves with the security framework */ | 544 | /* register ourselves with the security framework */ |
| 545 | security_add_hooks(tomoyo_hooks, ARRAY_SIZE(tomoyo_hooks)); | 545 | security_add_hooks(tomoyo_hooks, ARRAY_SIZE(tomoyo_hooks), "tomoyo"); |
| 546 | printk(KERN_INFO "TOMOYO Linux initialized\n"); | 546 | printk(KERN_INFO "TOMOYO Linux initialized\n"); |
| 547 | cred->security = &tomoyo_kernel_domain; | 547 | cred->security = &tomoyo_kernel_domain; |
| 548 | tomoyo_mm_init(); | 548 | tomoyo_mm_init(); |
diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c index 968e5e0a3f81..88271a3bf37f 100644 --- a/security/yama/yama_lsm.c +++ b/security/yama/yama_lsm.c | |||
| @@ -485,6 +485,6 @@ static inline void yama_init_sysctl(void) { } | |||
| 485 | void __init yama_add_hooks(void) | 485 | void __init yama_add_hooks(void) |
| 486 | { | 486 | { |
| 487 | pr_info("Yama: becoming mindful.\n"); | 487 | pr_info("Yama: becoming mindful.\n"); |
| 488 | security_add_hooks(yama_hooks, ARRAY_SIZE(yama_hooks)); | 488 | security_add_hooks(yama_hooks, ARRAY_SIZE(yama_hooks), "yama"); |
| 489 | yama_init_sysctl(); | 489 | yama_init_sysctl(); |
| 490 | } | 490 | } |
