diff options
Diffstat (limited to 'security/apparmor')
34 files changed, 7469 insertions, 0 deletions
diff --git a/security/apparmor/.gitignore b/security/apparmor/.gitignore new file mode 100644 index 00000000000..0a0a99f3b08 --- /dev/null +++ b/security/apparmor/.gitignore | |||
@@ -0,0 +1,5 @@ | |||
1 | # | ||
2 | # Generated include files | ||
3 | # | ||
4 | af_names.h | ||
5 | capability_names.h | ||
diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig new file mode 100644 index 00000000000..9b9013b2e32 --- /dev/null +++ b/security/apparmor/Kconfig | |||
@@ -0,0 +1,31 @@ | |||
1 | config SECURITY_APPARMOR | ||
2 | bool "AppArmor support" | ||
3 | depends on SECURITY && NET | ||
4 | select AUDIT | ||
5 | select SECURITY_PATH | ||
6 | select SECURITYFS | ||
7 | select SECURITY_NETWORK | ||
8 | default n | ||
9 | help | ||
10 | This enables the AppArmor security module. | ||
11 | Required userspace tools (if they are not included in your | ||
12 | distribution) and further information may be found at | ||
13 | http://apparmor.wiki.kernel.org | ||
14 | |||
15 | If you are unsure how to answer this question, answer N. | ||
16 | |||
17 | config SECURITY_APPARMOR_BOOTPARAM_VALUE | ||
18 | int "AppArmor boot parameter default value" | ||
19 | depends on SECURITY_APPARMOR | ||
20 | range 0 1 | ||
21 | default 1 | ||
22 | help | ||
23 | This option sets the default value for the kernel parameter | ||
24 | 'apparmor', which allows AppArmor to be enabled or disabled | ||
25 | at boot. If this option is set to 0 (zero), the AppArmor | ||
26 | kernel parameter will default to 0, disabling AppArmor at | ||
27 | boot. If this option is set to 1 (one), the AppArmor | ||
28 | kernel parameter will default to 1, enabling AppArmor at | ||
29 | boot. | ||
30 | |||
31 | If you are unsure how to answer this question, answer 1. | ||
diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile new file mode 100644 index 00000000000..f204869399e --- /dev/null +++ b/security/apparmor/Makefile | |||
@@ -0,0 +1,24 @@ | |||
1 | # Makefile for AppArmor Linux Security Module | ||
2 | # | ||
3 | obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o | ||
4 | |||
5 | apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ | ||
6 | path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ | ||
7 | resource.o sid.o file.o | ||
8 | |||
9 | clean-files: capability_names.h af_names.h | ||
10 | |||
11 | quiet_cmd_make-caps = GEN $@ | ||
12 | cmd_make-caps = echo "static const char *capability_names[] = {" > $@ ; sed -n -e "/CAP_FS_MASK/d" -e "s/^\#define[ \\t]\\+CAP_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\$$/[\\2] = \"\\1\",/p" $< | tr A-Z a-z >> $@ ; echo "};" >> $@ | ||
13 | |||
14 | quiet_cmd_make-rlim = GEN $@ | ||
15 | cmd_make-rlim = echo "static const char *rlim_names[] = {" > $@ ; sed -n --e "/AF_MAX/d" -e "s/^\# \\?define[ \\t]\\+RLIMIT_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\\(.*\\)\$$/[\\2] = \"\\1\",/p" $< | tr A-Z a-z >> $@ ; echo "};" >> $@ ; echo "static const int rlim_map[] = {" >> $@ ; sed -n -e "/AF_MAX/d" -e "s/^\# \\?define[ \\t]\\+\\(RLIMIT_[A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\\(.*\\)\$$/\\1,/p" $< >> $@ ; echo "};" >> $@ | ||
16 | |||
17 | $(obj)/capability.o : $(obj)/capability_names.h | ||
18 | $(obj)/resource.o : $(obj)/rlim_names.h | ||
19 | $(obj)/capability_names.h : $(srctree)/include/linux/capability.h | ||
20 | $(call cmd,make-caps) | ||
21 | $(obj)/af_names.h : $(srctree)/include/linux/socket.h | ||
22 | $(call cmd,make-af) | ||
23 | $(obj)/rlim_names.h : $(srctree)/include/asm-generic/resource.h | ||
24 | $(call cmd,make-rlim) | ||
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c new file mode 100644 index 00000000000..7320331b44a --- /dev/null +++ b/security/apparmor/apparmorfs.c | |||
@@ -0,0 +1,239 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor /sys/kernel/security/apparmor interface functions | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #include <linux/security.h> | ||
16 | #include <linux/vmalloc.h> | ||
17 | #include <linux/module.h> | ||
18 | #include <linux/seq_file.h> | ||
19 | #include <linux/uaccess.h> | ||
20 | #include <linux/namei.h> | ||
21 | |||
22 | #include "include/apparmor.h" | ||
23 | #include "include/apparmorfs.h" | ||
24 | #include "include/audit.h" | ||
25 | #include "include/context.h" | ||
26 | #include "include/policy.h" | ||
27 | |||
28 | /** | ||
29 | * aa_simple_write_to_buffer - common routine for getting policy from user | ||
30 | * @op: operation doing the user buffer copy | ||
31 | * @userbuf: user buffer to copy data from (NOT NULL) | ||
32 | * @alloc_size: size of user buffer | ||
33 | * @copy_size: size of data to copy from user buffer | ||
34 | * @pos: position write is at in the file (NOT NULL) | ||
35 | * | ||
36 | * Returns: kernel buffer containing copy of user buffer data or an | ||
37 | * ERR_PTR on failure. | ||
38 | */ | ||
39 | static char *aa_simple_write_to_buffer(int op, const char __user *userbuf, | ||
40 | size_t alloc_size, size_t copy_size, | ||
41 | loff_t *pos) | ||
42 | { | ||
43 | char *data; | ||
44 | |||
45 | if (*pos != 0) | ||
46 | /* only writes from pos 0, that is complete writes */ | ||
47 | return ERR_PTR(-ESPIPE); | ||
48 | |||
49 | /* | ||
50 | * Don't allow profile load/replace/remove from profiles that don't | ||
51 | * have CAP_MAC_ADMIN | ||
52 | */ | ||
53 | if (!aa_may_manage_policy(op)) | ||
54 | return ERR_PTR(-EACCES); | ||
55 | |||
56 | /* freed by caller to simple_write_to_buffer */ | ||
57 | data = kvmalloc(alloc_size); | ||
58 | if (data == NULL) | ||
59 | return ERR_PTR(-ENOMEM); | ||
60 | |||
61 | if (copy_from_user(data, userbuf, copy_size)) { | ||
62 | kvfree(data); | ||
63 | return ERR_PTR(-EFAULT); | ||
64 | } | ||
65 | |||
66 | return data; | ||
67 | } | ||
68 | |||
69 | |||
70 | /* .load file hook fn to load policy */ | ||
71 | static ssize_t profile_load(struct file *f, const char __user *buf, size_t size, | ||
72 | loff_t *pos) | ||
73 | { | ||
74 | char *data; | ||
75 | ssize_t error; | ||
76 | |||
77 | data = aa_simple_write_to_buffer(OP_PROF_LOAD, buf, size, size, pos); | ||
78 | |||
79 | error = PTR_ERR(data); | ||
80 | if (!IS_ERR(data)) { | ||
81 | error = aa_replace_profiles(data, size, PROF_ADD); | ||
82 | kvfree(data); | ||
83 | } | ||
84 | |||
85 | return error; | ||
86 | } | ||
87 | |||
88 | static const struct file_operations aa_fs_profile_load = { | ||
89 | .write = profile_load | ||
90 | }; | ||
91 | |||
92 | /* .replace file hook fn to load and/or replace policy */ | ||
93 | static ssize_t profile_replace(struct file *f, const char __user *buf, | ||
94 | size_t size, loff_t *pos) | ||
95 | { | ||
96 | char *data; | ||
97 | ssize_t error; | ||
98 | |||
99 | data = aa_simple_write_to_buffer(OP_PROF_REPL, buf, size, size, pos); | ||
100 | error = PTR_ERR(data); | ||
101 | if (!IS_ERR(data)) { | ||
102 | error = aa_replace_profiles(data, size, PROF_REPLACE); | ||
103 | kvfree(data); | ||
104 | } | ||
105 | |||
106 | return error; | ||
107 | } | ||
108 | |||
109 | static const struct file_operations aa_fs_profile_replace = { | ||
110 | .write = profile_replace | ||
111 | }; | ||
112 | |||
113 | /* .remove file hook fn to remove loaded policy */ | ||
114 | static ssize_t profile_remove(struct file *f, const char __user *buf, | ||
115 | size_t size, loff_t *pos) | ||
116 | { | ||
117 | char *data; | ||
118 | ssize_t error; | ||
119 | |||
120 | /* | ||
121 | * aa_remove_profile needs a null terminated string so 1 extra | ||
122 | * byte is allocated and the copied data is null terminated. | ||
123 | */ | ||
124 | data = aa_simple_write_to_buffer(OP_PROF_RM, buf, size + 1, size, pos); | ||
125 | |||
126 | error = PTR_ERR(data); | ||
127 | if (!IS_ERR(data)) { | ||
128 | data[size] = 0; | ||
129 | error = aa_remove_profiles(data, size); | ||
130 | kvfree(data); | ||
131 | } | ||
132 | |||
133 | return error; | ||
134 | } | ||
135 | |||
136 | static const struct file_operations aa_fs_profile_remove = { | ||
137 | .write = profile_remove | ||
138 | }; | ||
139 | |||
140 | /** Base file system setup **/ | ||
141 | |||
142 | static struct dentry *aa_fs_dentry __initdata; | ||
143 | |||
144 | static void __init aafs_remove(const char *name) | ||
145 | { | ||
146 | struct dentry *dentry; | ||
147 | |||
148 | dentry = lookup_one_len(name, aa_fs_dentry, strlen(name)); | ||
149 | if (!IS_ERR(dentry)) { | ||
150 | securityfs_remove(dentry); | ||
151 | dput(dentry); | ||
152 | } | ||
153 | } | ||
154 | |||
155 | /** | ||
156 | * aafs_create - create an entry in the apparmor filesystem | ||
157 | * @name: name of the entry (NOT NULL) | ||
158 | * @mask: file permission mask of the file | ||
159 | * @fops: file operations for the file (NOT NULL) | ||
160 | * | ||
161 | * Used aafs_remove to remove entries created with this fn. | ||
162 | */ | ||
163 | static int __init aafs_create(const char *name, int mask, | ||
164 | const struct file_operations *fops) | ||
165 | { | ||
166 | struct dentry *dentry; | ||
167 | |||
168 | dentry = securityfs_create_file(name, S_IFREG | mask, aa_fs_dentry, | ||
169 | NULL, fops); | ||
170 | |||
171 | return IS_ERR(dentry) ? PTR_ERR(dentry) : 0; | ||
172 | } | ||
173 | |||
174 | /** | ||
175 | * aa_destroy_aafs - cleanup and free aafs | ||
176 | * | ||
177 | * releases dentries allocated by aa_create_aafs | ||
178 | */ | ||
179 | void __init aa_destroy_aafs(void) | ||
180 | { | ||
181 | if (aa_fs_dentry) { | ||
182 | aafs_remove(".remove"); | ||
183 | aafs_remove(".replace"); | ||
184 | aafs_remove(".load"); | ||
185 | |||
186 | securityfs_remove(aa_fs_dentry); | ||
187 | aa_fs_dentry = NULL; | ||
188 | } | ||
189 | } | ||
190 | |||
191 | /** | ||
192 | * aa_create_aafs - create the apparmor security filesystem | ||
193 | * | ||
194 | * dentries created here are released by aa_destroy_aafs | ||
195 | * | ||
196 | * Returns: error on failure | ||
197 | */ | ||
198 | int __init aa_create_aafs(void) | ||
199 | { | ||
200 | int error; | ||
201 | |||
202 | if (!apparmor_initialized) | ||
203 | return 0; | ||
204 | |||
205 | if (aa_fs_dentry) { | ||
206 | AA_ERROR("%s: AppArmor securityfs already exists\n", __func__); | ||
207 | return -EEXIST; | ||
208 | } | ||
209 | |||
210 | aa_fs_dentry = securityfs_create_dir("apparmor", NULL); | ||
211 | if (IS_ERR(aa_fs_dentry)) { | ||
212 | error = PTR_ERR(aa_fs_dentry); | ||
213 | aa_fs_dentry = NULL; | ||
214 | goto error; | ||
215 | } | ||
216 | |||
217 | error = aafs_create(".load", 0640, &aa_fs_profile_load); | ||
218 | if (error) | ||
219 | goto error; | ||
220 | error = aafs_create(".replace", 0640, &aa_fs_profile_replace); | ||
221 | if (error) | ||
222 | goto error; | ||
223 | error = aafs_create(".remove", 0640, &aa_fs_profile_remove); | ||
224 | if (error) | ||
225 | goto error; | ||
226 | |||
227 | /* TODO: add support for apparmorfs_null and apparmorfs_mnt */ | ||
228 | |||
229 | /* Report that AppArmor fs is enabled */ | ||
230 | aa_info_message("AppArmor Filesystem Enabled"); | ||
231 | return 0; | ||
232 | |||
233 | error: | ||
234 | aa_destroy_aafs(); | ||
235 | AA_ERROR("Error creating AppArmor securityfs\n"); | ||
236 | return error; | ||
237 | } | ||
238 | |||
239 | fs_initcall(aa_create_aafs); | ||
diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c new file mode 100644 index 00000000000..96502b22b26 --- /dev/null +++ b/security/apparmor/audit.c | |||
@@ -0,0 +1,215 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor auditing functions | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #include <linux/audit.h> | ||
16 | #include <linux/socket.h> | ||
17 | |||
18 | #include "include/apparmor.h" | ||
19 | #include "include/audit.h" | ||
20 | #include "include/policy.h" | ||
21 | |||
22 | const char *op_table[] = { | ||
23 | "null", | ||
24 | |||
25 | "sysctl", | ||
26 | "capable", | ||
27 | |||
28 | "unlink", | ||
29 | "mkdir", | ||
30 | "rmdir", | ||
31 | "mknod", | ||
32 | "truncate", | ||
33 | "link", | ||
34 | "symlink", | ||
35 | "rename_src", | ||
36 | "rename_dest", | ||
37 | "chmod", | ||
38 | "chown", | ||
39 | "getattr", | ||
40 | "open", | ||
41 | |||
42 | "file_perm", | ||
43 | "file_lock", | ||
44 | "file_mmap", | ||
45 | "file_mprotect", | ||
46 | |||
47 | "create", | ||
48 | "post_create", | ||
49 | "bind", | ||
50 | "connect", | ||
51 | "listen", | ||
52 | "accept", | ||
53 | "sendmsg", | ||
54 | "recvmsg", | ||
55 | "getsockname", | ||
56 | "getpeername", | ||
57 | "getsockopt", | ||
58 | "setsockopt", | ||
59 | "socket_shutdown", | ||
60 | |||
61 | "ptrace", | ||
62 | |||
63 | "exec", | ||
64 | "change_hat", | ||
65 | "change_profile", | ||
66 | "change_onexec", | ||
67 | |||
68 | "setprocattr", | ||
69 | "setrlimit", | ||
70 | |||
71 | "profile_replace", | ||
72 | "profile_load", | ||
73 | "profile_remove" | ||
74 | }; | ||
75 | |||
76 | const char *audit_mode_names[] = { | ||
77 | "normal", | ||
78 | "quiet_denied", | ||
79 | "quiet", | ||
80 | "noquiet", | ||
81 | "all" | ||
82 | }; | ||
83 | |||
84 | static char *aa_audit_type[] = { | ||
85 | "AUDIT", | ||
86 | "ALLOWED", | ||
87 | "DENIED", | ||
88 | "HINT", | ||
89 | "STATUS", | ||
90 | "ERROR", | ||
91 | "KILLED" | ||
92 | }; | ||
93 | |||
94 | /* | ||
95 | * Currently AppArmor auditing is fed straight into the audit framework. | ||
96 | * | ||
97 | * TODO: | ||
98 | * netlink interface for complain mode | ||
99 | * user auditing, - send user auditing to netlink interface | ||
100 | * system control of whether user audit messages go to system log | ||
101 | */ | ||
102 | |||
103 | /** | ||
104 | * audit_base - core AppArmor function. | ||
105 | * @ab: audit buffer to fill (NOT NULL) | ||
106 | * @ca: audit structure containing data to audit (NOT NULL) | ||
107 | * | ||
108 | * Record common AppArmor audit data from @sa | ||
109 | */ | ||
110 | static void audit_pre(struct audit_buffer *ab, void *ca) | ||
111 | { | ||
112 | struct common_audit_data *sa = ca; | ||
113 | struct task_struct *tsk = sa->tsk ? sa->tsk : current; | ||
114 | |||
115 | if (aa_g_audit_header) { | ||
116 | audit_log_format(ab, "apparmor="); | ||
117 | audit_log_string(ab, aa_audit_type[sa->aad.type]); | ||
118 | } | ||
119 | |||
120 | if (sa->aad.op) { | ||
121 | audit_log_format(ab, " operation="); | ||
122 | audit_log_string(ab, op_table[sa->aad.op]); | ||
123 | } | ||
124 | |||
125 | if (sa->aad.info) { | ||
126 | audit_log_format(ab, " info="); | ||
127 | audit_log_string(ab, sa->aad.info); | ||
128 | if (sa->aad.error) | ||
129 | audit_log_format(ab, " error=%d", sa->aad.error); | ||
130 | } | ||
131 | |||
132 | if (sa->aad.profile) { | ||
133 | struct aa_profile *profile = sa->aad.profile; | ||
134 | pid_t pid; | ||
135 | rcu_read_lock(); | ||
136 | pid = tsk->real_parent->pid; | ||
137 | rcu_read_unlock(); | ||
138 | audit_log_format(ab, " parent=%d", pid); | ||
139 | if (profile->ns != root_ns) { | ||
140 | audit_log_format(ab, " namespace="); | ||
141 | audit_log_untrustedstring(ab, profile->ns->base.hname); | ||
142 | } | ||
143 | audit_log_format(ab, " profile="); | ||
144 | audit_log_untrustedstring(ab, profile->base.hname); | ||
145 | } | ||
146 | |||
147 | if (sa->aad.name) { | ||
148 | audit_log_format(ab, " name="); | ||
149 | audit_log_untrustedstring(ab, sa->aad.name); | ||
150 | } | ||
151 | } | ||
152 | |||
153 | /** | ||
154 | * aa_audit_msg - Log a message to the audit subsystem | ||
155 | * @sa: audit event structure (NOT NULL) | ||
156 | * @cb: optional callback fn for type specific fields (MAYBE NULL) | ||
157 | */ | ||
158 | void aa_audit_msg(int type, struct common_audit_data *sa, | ||
159 | void (*cb) (struct audit_buffer *, void *)) | ||
160 | { | ||
161 | sa->aad.type = type; | ||
162 | sa->lsm_pre_audit = audit_pre; | ||
163 | sa->lsm_post_audit = cb; | ||
164 | common_lsm_audit(sa); | ||
165 | } | ||
166 | |||
167 | /** | ||
168 | * aa_audit - Log a profile based audit event to the audit subsystem | ||
169 | * @type: audit type for the message | ||
170 | * @profile: profile to check against (NOT NULL) | ||
171 | * @gfp: allocation flags to use | ||
172 | * @sa: audit event (NOT NULL) | ||
173 | * @cb: optional callback fn for type specific fields (MAYBE NULL) | ||
174 | * | ||
175 | * Handle default message switching based off of audit mode flags | ||
176 | * | ||
177 | * Returns: error on failure | ||
178 | */ | ||
179 | int aa_audit(int type, struct aa_profile *profile, gfp_t gfp, | ||
180 | struct common_audit_data *sa, | ||
181 | void (*cb) (struct audit_buffer *, void *)) | ||
182 | { | ||
183 | BUG_ON(!profile); | ||
184 | |||
185 | if (type == AUDIT_APPARMOR_AUTO) { | ||
186 | if (likely(!sa->aad.error)) { | ||
187 | if (AUDIT_MODE(profile) != AUDIT_ALL) | ||
188 | return 0; | ||
189 | type = AUDIT_APPARMOR_AUDIT; | ||
190 | } else if (COMPLAIN_MODE(profile)) | ||
191 | type = AUDIT_APPARMOR_ALLOWED; | ||
192 | else | ||
193 | type = AUDIT_APPARMOR_DENIED; | ||
194 | } | ||
195 | if (AUDIT_MODE(profile) == AUDIT_QUIET || | ||
196 | (type == AUDIT_APPARMOR_DENIED && | ||
197 | AUDIT_MODE(profile) == AUDIT_QUIET)) | ||
198 | return sa->aad.error; | ||
199 | |||
200 | if (KILL_MODE(profile) && type == AUDIT_APPARMOR_DENIED) | ||
201 | type = AUDIT_APPARMOR_KILL; | ||
202 | |||
203 | if (!unconfined(profile)) | ||
204 | sa->aad.profile = profile; | ||
205 | |||
206 | aa_audit_msg(type, sa, cb); | ||
207 | |||
208 | if (sa->aad.type == AUDIT_APPARMOR_KILL) | ||
209 | (void)send_sig_info(SIGKILL, NULL, sa->tsk ? sa->tsk : current); | ||
210 | |||
211 | if (sa->aad.type == AUDIT_APPARMOR_ALLOWED) | ||
212 | return complain_error(sa->aad.error); | ||
213 | |||
214 | return sa->aad.error; | ||
215 | } | ||
diff --git a/security/apparmor/capability.c b/security/apparmor/capability.c new file mode 100644 index 00000000000..9982c48def4 --- /dev/null +++ b/security/apparmor/capability.c | |||
@@ -0,0 +1,141 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor capability mediation functions | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #include <linux/capability.h> | ||
16 | #include <linux/errno.h> | ||
17 | #include <linux/gfp.h> | ||
18 | |||
19 | #include "include/apparmor.h" | ||
20 | #include "include/capability.h" | ||
21 | #include "include/context.h" | ||
22 | #include "include/policy.h" | ||
23 | #include "include/audit.h" | ||
24 | |||
25 | /* | ||
26 | * Table of capability names: we generate it from capabilities.h. | ||
27 | */ | ||
28 | #include "capability_names.h" | ||
29 | |||
30 | struct audit_cache { | ||
31 | struct aa_profile *profile; | ||
32 | kernel_cap_t caps; | ||
33 | }; | ||
34 | |||
35 | static DEFINE_PER_CPU(struct audit_cache, audit_cache); | ||
36 | |||
37 | /** | ||
38 | * audit_cb - call back for capability components of audit struct | ||
39 | * @ab - audit buffer (NOT NULL) | ||
40 | * @va - audit struct to audit data from (NOT NULL) | ||
41 | */ | ||
42 | static void audit_cb(struct audit_buffer *ab, void *va) | ||
43 | { | ||
44 | struct common_audit_data *sa = va; | ||
45 | audit_log_format(ab, " capname="); | ||
46 | audit_log_untrustedstring(ab, capability_names[sa->u.cap]); | ||
47 | } | ||
48 | |||
49 | /** | ||
50 | * audit_caps - audit a capability | ||
51 | * @profile: profile confining task (NOT NULL) | ||
52 | * @task: task capability test was performed against (NOT NULL) | ||
53 | * @cap: capability tested | ||
54 | * @error: error code returned by test | ||
55 | * | ||
56 | * Do auditing of capability and handle, audit/complain/kill modes switching | ||
57 | * and duplicate message elimination. | ||
58 | * | ||
59 | * Returns: 0 or sa->error on success, error code on failure | ||
60 | */ | ||
61 | static int audit_caps(struct aa_profile *profile, struct task_struct *task, | ||
62 | int cap, int error) | ||
63 | { | ||
64 | struct audit_cache *ent; | ||
65 | int type = AUDIT_APPARMOR_AUTO; | ||
66 | struct common_audit_data sa; | ||
67 | COMMON_AUDIT_DATA_INIT(&sa, CAP); | ||
68 | sa.tsk = task; | ||
69 | sa.u.cap = cap; | ||
70 | sa.aad.op = OP_CAPABLE; | ||
71 | sa.aad.error = error; | ||
72 | |||
73 | if (likely(!error)) { | ||
74 | /* test if auditing is being forced */ | ||
75 | if (likely((AUDIT_MODE(profile) != AUDIT_ALL) && | ||
76 | !cap_raised(profile->caps.audit, cap))) | ||
77 | return 0; | ||
78 | type = AUDIT_APPARMOR_AUDIT; | ||
79 | } else if (KILL_MODE(profile) || | ||
80 | cap_raised(profile->caps.kill, cap)) { | ||
81 | type = AUDIT_APPARMOR_KILL; | ||
82 | } else if (cap_raised(profile->caps.quiet, cap) && | ||
83 | AUDIT_MODE(profile) != AUDIT_NOQUIET && | ||
84 | AUDIT_MODE(profile) != AUDIT_ALL) { | ||
85 | /* quiet auditing */ | ||
86 | return error; | ||
87 | } | ||
88 | |||
89 | /* Do simple duplicate message elimination */ | ||
90 | ent = &get_cpu_var(audit_cache); | ||
91 | if (profile == ent->profile && cap_raised(ent->caps, cap)) { | ||
92 | put_cpu_var(audit_cache); | ||
93 | if (COMPLAIN_MODE(profile)) | ||
94 | return complain_error(error); | ||
95 | return error; | ||
96 | } else { | ||
97 | aa_put_profile(ent->profile); | ||
98 | ent->profile = aa_get_profile(profile); | ||
99 | cap_raise(ent->caps, cap); | ||
100 | } | ||
101 | put_cpu_var(audit_cache); | ||
102 | |||
103 | return aa_audit(type, profile, GFP_ATOMIC, &sa, audit_cb); | ||
104 | } | ||
105 | |||
106 | /** | ||
107 | * profile_capable - test if profile allows use of capability @cap | ||
108 | * @profile: profile being enforced (NOT NULL, NOT unconfined) | ||
109 | * @cap: capability to test if allowed | ||
110 | * | ||
111 | * Returns: 0 if allowed else -EPERM | ||
112 | */ | ||
113 | static int profile_capable(struct aa_profile *profile, int cap) | ||
114 | { | ||
115 | return cap_raised(profile->caps.allow, cap) ? 0 : -EPERM; | ||
116 | } | ||
117 | |||
118 | /** | ||
119 | * aa_capable - test permission to use capability | ||
120 | * @task: task doing capability test against (NOT NULL) | ||
121 | * @profile: profile confining @task (NOT NULL) | ||
122 | * @cap: capability to be tested | ||
123 | * @audit: whether an audit record should be generated | ||
124 | * | ||
125 | * Look up capability in profile capability set. | ||
126 | * | ||
127 | * Returns: 0 on success, or else an error code. | ||
128 | */ | ||
129 | int aa_capable(struct task_struct *task, struct aa_profile *profile, int cap, | ||
130 | int audit) | ||
131 | { | ||
132 | int error = profile_capable(profile, cap); | ||
133 | |||
134 | if (!audit) { | ||
135 | if (COMPLAIN_MODE(profile)) | ||
136 | return complain_error(error); | ||
137 | return error; | ||
138 | } | ||
139 | |||
140 | return audit_caps(profile, task, cap, error); | ||
141 | } | ||
diff --git a/security/apparmor/context.c b/security/apparmor/context.c new file mode 100644 index 00000000000..8a9b5027c81 --- /dev/null +++ b/security/apparmor/context.c | |||
@@ -0,0 +1,216 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor functions used to manipulate object security | ||
5 | * contexts. | ||
6 | * | ||
7 | * Copyright (C) 1998-2008 Novell/SUSE | ||
8 | * Copyright 2009-2010 Canonical Ltd. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or | ||
11 | * modify it under the terms of the GNU General Public License as | ||
12 | * published by the Free Software Foundation, version 2 of the | ||
13 | * License. | ||
14 | * | ||
15 | * | ||
16 | * AppArmor sets confinement on every task, via the the aa_task_cxt and | ||
17 | * the aa_task_cxt.profile, both of which are required and are not allowed | ||
18 | * to be NULL. The aa_task_cxt is not reference counted and is unique | ||
19 | * to each cred (which is reference count). The profile pointed to by | ||
20 | * the task_cxt is reference counted. | ||
21 | * | ||
22 | * TODO | ||
23 | * If a task uses change_hat it currently does not return to the old | ||
24 | * cred or task context but instead creates a new one. Ideally the task | ||
25 | * should return to the previous cred if it has not been modified. | ||
26 | * | ||
27 | */ | ||
28 | |||
29 | #include "include/context.h" | ||
30 | #include "include/policy.h" | ||
31 | |||
32 | /** | ||
33 | * aa_alloc_task_context - allocate a new task_cxt | ||
34 | * @flags: gfp flags for allocation | ||
35 | * | ||
36 | * Returns: allocated buffer or NULL on failure | ||
37 | */ | ||
38 | struct aa_task_cxt *aa_alloc_task_context(gfp_t flags) | ||
39 | { | ||
40 | return kzalloc(sizeof(struct aa_task_cxt), flags); | ||
41 | } | ||
42 | |||
43 | /** | ||
44 | * aa_free_task_context - free a task_cxt | ||
45 | * @cxt: task_cxt to free (MAYBE NULL) | ||
46 | */ | ||
47 | void aa_free_task_context(struct aa_task_cxt *cxt) | ||
48 | { | ||
49 | if (cxt) { | ||
50 | aa_put_profile(cxt->profile); | ||
51 | aa_put_profile(cxt->previous); | ||
52 | aa_put_profile(cxt->onexec); | ||
53 | |||
54 | kzfree(cxt); | ||
55 | } | ||
56 | } | ||
57 | |||
58 | /** | ||
59 | * aa_dup_task_context - duplicate a task context, incrementing reference counts | ||
60 | * @new: a blank task context (NOT NULL) | ||
61 | * @old: the task context to copy (NOT NULL) | ||
62 | */ | ||
63 | void aa_dup_task_context(struct aa_task_cxt *new, const struct aa_task_cxt *old) | ||
64 | { | ||
65 | *new = *old; | ||
66 | aa_get_profile(new->profile); | ||
67 | aa_get_profile(new->previous); | ||
68 | aa_get_profile(new->onexec); | ||
69 | } | ||
70 | |||
71 | /** | ||
72 | * aa_replace_current_profile - replace the current tasks profiles | ||
73 | * @profile: new profile (NOT NULL) | ||
74 | * | ||
75 | * Returns: 0 or error on failure | ||
76 | */ | ||
77 | int aa_replace_current_profile(struct aa_profile *profile) | ||
78 | { | ||
79 | struct aa_task_cxt *cxt = current_cred()->security; | ||
80 | struct cred *new; | ||
81 | BUG_ON(!profile); | ||
82 | |||
83 | if (cxt->profile == profile) | ||
84 | return 0; | ||
85 | |||
86 | new = prepare_creds(); | ||
87 | if (!new) | ||
88 | return -ENOMEM; | ||
89 | |||
90 | cxt = new->security; | ||
91 | if (unconfined(profile) || (cxt->profile->ns != profile->ns)) { | ||
92 | /* if switching to unconfined or a different profile namespace | ||
93 | * clear out context state | ||
94 | */ | ||
95 | aa_put_profile(cxt->previous); | ||
96 | aa_put_profile(cxt->onexec); | ||
97 | cxt->previous = NULL; | ||
98 | cxt->onexec = NULL; | ||
99 | cxt->token = 0; | ||
100 | } | ||
101 | /* be careful switching cxt->profile, when racing replacement it | ||
102 | * is possible that cxt->profile->replacedby is the reference keeping | ||
103 | * @profile valid, so make sure to get its reference before dropping | ||
104 | * the reference on cxt->profile */ | ||
105 | aa_get_profile(profile); | ||
106 | aa_put_profile(cxt->profile); | ||
107 | cxt->profile = profile; | ||
108 | |||
109 | commit_creds(new); | ||
110 | return 0; | ||
111 | } | ||
112 | |||
113 | /** | ||
114 | * aa_set_current_onexec - set the tasks change_profile to happen onexec | ||
115 | * @profile: system profile to set at exec (MAYBE NULL to clear value) | ||
116 | * | ||
117 | * Returns: 0 or error on failure | ||
118 | */ | ||
119 | int aa_set_current_onexec(struct aa_profile *profile) | ||
120 | { | ||
121 | struct aa_task_cxt *cxt; | ||
122 | struct cred *new = prepare_creds(); | ||
123 | if (!new) | ||
124 | return -ENOMEM; | ||
125 | |||
126 | cxt = new->security; | ||
127 | aa_get_profile(profile); | ||
128 | aa_put_profile(cxt->onexec); | ||
129 | cxt->onexec = profile; | ||
130 | |||
131 | commit_creds(new); | ||
132 | return 0; | ||
133 | } | ||
134 | |||
135 | /** | ||
136 | * aa_set_current_hat - set the current tasks hat | ||
137 | * @profile: profile to set as the current hat (NOT NULL) | ||
138 | * @token: token value that must be specified to change from the hat | ||
139 | * | ||
140 | * Do switch of tasks hat. If the task is currently in a hat | ||
141 | * validate the token to match. | ||
142 | * | ||
143 | * Returns: 0 or error on failure | ||
144 | */ | ||
145 | int aa_set_current_hat(struct aa_profile *profile, u64 token) | ||
146 | { | ||
147 | struct aa_task_cxt *cxt; | ||
148 | struct cred *new = prepare_creds(); | ||
149 | if (!new) | ||
150 | return -ENOMEM; | ||
151 | BUG_ON(!profile); | ||
152 | |||
153 | cxt = new->security; | ||
154 | if (!cxt->previous) { | ||
155 | /* transfer refcount */ | ||
156 | cxt->previous = cxt->profile; | ||
157 | cxt->token = token; | ||
158 | } else if (cxt->token == token) { | ||
159 | aa_put_profile(cxt->profile); | ||
160 | } else { | ||
161 | /* previous_profile && cxt->token != token */ | ||
162 | abort_creds(new); | ||
163 | return -EACCES; | ||
164 | } | ||
165 | cxt->profile = aa_get_profile(aa_newest_version(profile)); | ||
166 | /* clear exec on switching context */ | ||
167 | aa_put_profile(cxt->onexec); | ||
168 | cxt->onexec = NULL; | ||
169 | |||
170 | commit_creds(new); | ||
171 | return 0; | ||
172 | } | ||
173 | |||
174 | /** | ||
175 | * aa_restore_previous_profile - exit from hat context restoring the profile | ||
176 | * @token: the token that must be matched to exit hat context | ||
177 | * | ||
178 | * Attempt to return out of a hat to the previous profile. The token | ||
179 | * must match the stored token value. | ||
180 | * | ||
181 | * Returns: 0 or error of failure | ||
182 | */ | ||
183 | int aa_restore_previous_profile(u64 token) | ||
184 | { | ||
185 | struct aa_task_cxt *cxt; | ||
186 | struct cred *new = prepare_creds(); | ||
187 | if (!new) | ||
188 | return -ENOMEM; | ||
189 | |||
190 | cxt = new->security; | ||
191 | if (cxt->token != token) { | ||
192 | abort_creds(new); | ||
193 | return -EACCES; | ||
194 | } | ||
195 | /* ignore restores when there is no saved profile */ | ||
196 | if (!cxt->previous) { | ||
197 | abort_creds(new); | ||
198 | return 0; | ||
199 | } | ||
200 | |||
201 | aa_put_profile(cxt->profile); | ||
202 | cxt->profile = aa_newest_version(cxt->previous); | ||
203 | BUG_ON(!cxt->profile); | ||
204 | if (unlikely(cxt->profile != cxt->previous)) { | ||
205 | aa_get_profile(cxt->profile); | ||
206 | aa_put_profile(cxt->previous); | ||
207 | } | ||
208 | /* clear exec && prev information when restoring to previous context */ | ||
209 | cxt->previous = NULL; | ||
210 | cxt->token = 0; | ||
211 | aa_put_profile(cxt->onexec); | ||
212 | cxt->onexec = NULL; | ||
213 | |||
214 | commit_creds(new); | ||
215 | return 0; | ||
216 | } | ||
diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c new file mode 100644 index 00000000000..c825c6e0b63 --- /dev/null +++ b/security/apparmor/domain.c | |||
@@ -0,0 +1,823 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor policy attachment and domain transitions | ||
5 | * | ||
6 | * Copyright (C) 2002-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #include <linux/errno.h> | ||
16 | #include <linux/fdtable.h> | ||
17 | #include <linux/file.h> | ||
18 | #include <linux/mount.h> | ||
19 | #include <linux/syscalls.h> | ||
20 | #include <linux/tracehook.h> | ||
21 | #include <linux/personality.h> | ||
22 | |||
23 | #include "include/audit.h" | ||
24 | #include "include/apparmorfs.h" | ||
25 | #include "include/context.h" | ||
26 | #include "include/domain.h" | ||
27 | #include "include/file.h" | ||
28 | #include "include/ipc.h" | ||
29 | #include "include/match.h" | ||
30 | #include "include/path.h" | ||
31 | #include "include/policy.h" | ||
32 | |||
33 | /** | ||
34 | * aa_free_domain_entries - free entries in a domain table | ||
35 | * @domain: the domain table to free (MAYBE NULL) | ||
36 | */ | ||
37 | void aa_free_domain_entries(struct aa_domain *domain) | ||
38 | { | ||
39 | int i; | ||
40 | if (domain) { | ||
41 | if (!domain->table) | ||
42 | return; | ||
43 | |||
44 | for (i = 0; i < domain->size; i++) | ||
45 | kzfree(domain->table[i]); | ||
46 | kzfree(domain->table); | ||
47 | domain->table = NULL; | ||
48 | } | ||
49 | } | ||
50 | |||
51 | /** | ||
52 | * may_change_ptraced_domain - check if can change profile on ptraced task | ||
53 | * @task: task we want to change profile of (NOT NULL) | ||
54 | * @to_profile: profile to change to (NOT NULL) | ||
55 | * | ||
56 | * Check if the task is ptraced and if so if the tracing task is allowed | ||
57 | * to trace the new domain | ||
58 | * | ||
59 | * Returns: %0 or error if change not allowed | ||
60 | */ | ||
61 | static int may_change_ptraced_domain(struct task_struct *task, | ||
62 | struct aa_profile *to_profile) | ||
63 | { | ||
64 | struct task_struct *tracer; | ||
65 | const struct cred *cred = NULL; | ||
66 | struct aa_profile *tracerp = NULL; | ||
67 | int error = 0; | ||
68 | |||
69 | rcu_read_lock(); | ||
70 | tracer = tracehook_tracer_task(task); | ||
71 | if (tracer) { | ||
72 | /* released below */ | ||
73 | cred = get_task_cred(tracer); | ||
74 | tracerp = aa_cred_profile(cred); | ||
75 | } | ||
76 | rcu_read_unlock(); | ||
77 | |||
78 | /* not ptraced */ | ||
79 | if (!tracer || unconfined(tracerp)) | ||
80 | goto out; | ||
81 | |||
82 | error = aa_may_ptrace(tracer, tracerp, to_profile, PTRACE_MODE_ATTACH); | ||
83 | |||
84 | out: | ||
85 | if (cred) | ||
86 | put_cred(cred); | ||
87 | |||
88 | return error; | ||
89 | } | ||
90 | |||
91 | /** | ||
92 | * change_profile_perms - find permissions for change_profile | ||
93 | * @profile: the current profile (NOT NULL) | ||
94 | * @ns: the namespace being switched to (NOT NULL) | ||
95 | * @name: the name of the profile to change to (NOT NULL) | ||
96 | * @request: requested perms | ||
97 | * @start: state to start matching in | ||
98 | * | ||
99 | * Returns: permission set | ||
100 | */ | ||
101 | static struct file_perms change_profile_perms(struct aa_profile *profile, | ||
102 | struct aa_namespace *ns, | ||
103 | const char *name, u32 request, | ||
104 | unsigned int start) | ||
105 | { | ||
106 | struct file_perms perms; | ||
107 | struct path_cond cond = { }; | ||
108 | unsigned int state; | ||
109 | |||
110 | if (unconfined(profile)) { | ||
111 | perms.allow = AA_MAY_CHANGE_PROFILE | AA_MAY_ONEXEC; | ||
112 | perms.audit = perms.quiet = perms.kill = 0; | ||
113 | return perms; | ||
114 | } else if (!profile->file.dfa) { | ||
115 | return nullperms; | ||
116 | } else if ((ns == profile->ns)) { | ||
117 | /* try matching against rules with out namespace prepended */ | ||
118 | aa_str_perms(profile->file.dfa, start, name, &cond, &perms); | ||
119 | if (COMBINED_PERM_MASK(perms) & request) | ||
120 | return perms; | ||
121 | } | ||
122 | |||
123 | /* try matching with namespace name and then profile */ | ||
124 | state = aa_dfa_match(profile->file.dfa, start, ns->base.name); | ||
125 | state = aa_dfa_match_len(profile->file.dfa, state, ":", 1); | ||
126 | aa_str_perms(profile->file.dfa, state, name, &cond, &perms); | ||
127 | |||
128 | return perms; | ||
129 | } | ||
130 | |||
131 | /** | ||
132 | * __attach_match_ - find an attachment match | ||
133 | * @name - to match against (NOT NULL) | ||
134 | * @head - profile list to walk (NOT NULL) | ||
135 | * | ||
136 | * Do a linear search on the profiles in the list. There is a matching | ||
137 | * preference where an exact match is preferred over a name which uses | ||
138 | * expressions to match, and matching expressions with the greatest | ||
139 | * xmatch_len are preferred. | ||
140 | * | ||
141 | * Requires: @head not be shared or have appropriate locks held | ||
142 | * | ||
143 | * Returns: profile or NULL if no match found | ||
144 | */ | ||
145 | static struct aa_profile *__attach_match(const char *name, | ||
146 | struct list_head *head) | ||
147 | { | ||
148 | int len = 0; | ||
149 | struct aa_profile *profile, *candidate = NULL; | ||
150 | |||
151 | list_for_each_entry(profile, head, base.list) { | ||
152 | if (profile->flags & PFLAG_NULL) | ||
153 | continue; | ||
154 | if (profile->xmatch && profile->xmatch_len > len) { | ||
155 | unsigned int state = aa_dfa_match(profile->xmatch, | ||
156 | DFA_START, name); | ||
157 | u32 perm = dfa_user_allow(profile->xmatch, state); | ||
158 | /* any accepting state means a valid match. */ | ||
159 | if (perm & MAY_EXEC) { | ||
160 | candidate = profile; | ||
161 | len = profile->xmatch_len; | ||
162 | } | ||
163 | } else if (!strcmp(profile->base.name, name)) | ||
164 | /* exact non-re match, no more searching required */ | ||
165 | return profile; | ||
166 | } | ||
167 | |||
168 | return candidate; | ||
169 | } | ||
170 | |||
171 | /** | ||
172 | * find_attach - do attachment search for unconfined processes | ||
173 | * @ns: the current namespace (NOT NULL) | ||
174 | * @list: list to search (NOT NULL) | ||
175 | * @name: the executable name to match against (NOT NULL) | ||
176 | * | ||
177 | * Returns: profile or NULL if no match found | ||
178 | */ | ||
179 | static struct aa_profile *find_attach(struct aa_namespace *ns, | ||
180 | struct list_head *list, const char *name) | ||
181 | { | ||
182 | struct aa_profile *profile; | ||
183 | |||
184 | read_lock(&ns->lock); | ||
185 | profile = aa_get_profile(__attach_match(name, list)); | ||
186 | read_unlock(&ns->lock); | ||
187 | |||
188 | return profile; | ||
189 | } | ||
190 | |||
191 | /** | ||
192 | * separate_fqname - separate the namespace and profile names | ||
193 | * @fqname: the fqname name to split (NOT NULL) | ||
194 | * @ns_name: the namespace name if it exists (NOT NULL) | ||
195 | * | ||
196 | * This is the xtable equivalent routine of aa_split_fqname. It finds the | ||
197 | * split in an xtable fqname which contains an embedded \0 instead of a : | ||
198 | * if a namespace is specified. This is done so the xtable is constant and | ||
199 | * isn't re-split on every lookup. | ||
200 | * | ||
201 | * Either the profile or namespace name may be optional but if the namespace | ||
202 | * is specified the profile name termination must be present. This results | ||
203 | * in the following possible encodings: | ||
204 | * profile_name\0 | ||
205 | * :ns_name\0profile_name\0 | ||
206 | * :ns_name\0\0 | ||
207 | * | ||
208 | * NOTE: the xtable fqname is pre-validated at load time in unpack_trans_table | ||
209 | * | ||
210 | * Returns: profile name if it is specified else NULL | ||
211 | */ | ||
212 | static const char *separate_fqname(const char *fqname, const char **ns_name) | ||
213 | { | ||
214 | const char *name; | ||
215 | |||
216 | if (fqname[0] == ':') { | ||
217 | /* In this case there is guaranteed to be two \0 terminators | ||
218 | * in the string. They are verified at load time by | ||
219 | * by unpack_trans_table | ||
220 | */ | ||
221 | *ns_name = fqname + 1; /* skip : */ | ||
222 | name = *ns_name + strlen(*ns_name) + 1; | ||
223 | if (!*name) | ||
224 | name = NULL; | ||
225 | } else { | ||
226 | *ns_name = NULL; | ||
227 | name = fqname; | ||
228 | } | ||
229 | |||
230 | return name; | ||
231 | } | ||
232 | |||
233 | static const char *next_name(int xtype, const char *name) | ||
234 | { | ||
235 | return NULL; | ||
236 | } | ||
237 | |||
238 | /** | ||
239 | * x_table_lookup - lookup an x transition name via transition table | ||
240 | * @profile: current profile (NOT NULL) | ||
241 | * @xindex: index into x transition table | ||
242 | * | ||
243 | * Returns: refcounted profile, or NULL on failure (MAYBE NULL) | ||
244 | */ | ||
245 | static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) | ||
246 | { | ||
247 | struct aa_profile *new_profile = NULL; | ||
248 | struct aa_namespace *ns = profile->ns; | ||
249 | u32 xtype = xindex & AA_X_TYPE_MASK; | ||
250 | int index = xindex & AA_X_INDEX_MASK; | ||
251 | const char *name; | ||
252 | |||
253 | /* index is guaranteed to be in range, validated at load time */ | ||
254 | for (name = profile->file.trans.table[index]; !new_profile && name; | ||
255 | name = next_name(xtype, name)) { | ||
256 | struct aa_namespace *new_ns; | ||
257 | const char *xname = NULL; | ||
258 | |||
259 | new_ns = NULL; | ||
260 | if (xindex & AA_X_CHILD) { | ||
261 | /* release by caller */ | ||
262 | new_profile = aa_find_child(profile, name); | ||
263 | continue; | ||
264 | } else if (*name == ':') { | ||
265 | /* switching namespace */ | ||
266 | const char *ns_name; | ||
267 | xname = name = separate_fqname(name, &ns_name); | ||
268 | if (!xname) | ||
269 | /* no name so use profile name */ | ||
270 | xname = profile->base.hname; | ||
271 | if (*ns_name == '@') { | ||
272 | /* TODO: variable support */ | ||
273 | ; | ||
274 | } | ||
275 | /* released below */ | ||
276 | new_ns = aa_find_namespace(ns, ns_name); | ||
277 | if (!new_ns) | ||
278 | continue; | ||
279 | } else if (*name == '@') { | ||
280 | /* TODO: variable support */ | ||
281 | continue; | ||
282 | } else { | ||
283 | /* basic namespace lookup */ | ||
284 | xname = name; | ||
285 | } | ||
286 | |||
287 | /* released by caller */ | ||
288 | new_profile = aa_lookup_profile(new_ns ? new_ns : ns, xname); | ||
289 | aa_put_namespace(new_ns); | ||
290 | } | ||
291 | |||
292 | /* released by caller */ | ||
293 | return new_profile; | ||
294 | } | ||
295 | |||
296 | /** | ||
297 | * x_to_profile - get target profile for a given xindex | ||
298 | * @profile: current profile (NOT NULL) | ||
299 | * @name: name to lookup (NOT NULL) | ||
300 | * @xindex: index into x transition table | ||
301 | * | ||
302 | * find profile for a transition index | ||
303 | * | ||
304 | * Returns: refcounted profile or NULL if not found available | ||
305 | */ | ||
306 | static struct aa_profile *x_to_profile(struct aa_profile *profile, | ||
307 | const char *name, u32 xindex) | ||
308 | { | ||
309 | struct aa_profile *new_profile = NULL; | ||
310 | struct aa_namespace *ns = profile->ns; | ||
311 | u32 xtype = xindex & AA_X_TYPE_MASK; | ||
312 | |||
313 | switch (xtype) { | ||
314 | case AA_X_NONE: | ||
315 | /* fail exec unless ix || ux fallback - handled by caller */ | ||
316 | return NULL; | ||
317 | case AA_X_NAME: | ||
318 | if (xindex & AA_X_CHILD) | ||
319 | /* released by caller */ | ||
320 | new_profile = find_attach(ns, &profile->base.profiles, | ||
321 | name); | ||
322 | else | ||
323 | /* released by caller */ | ||
324 | new_profile = find_attach(ns, &ns->base.profiles, | ||
325 | name); | ||
326 | break; | ||
327 | case AA_X_TABLE: | ||
328 | /* released by caller */ | ||
329 | new_profile = x_table_lookup(profile, xindex); | ||
330 | break; | ||
331 | } | ||
332 | |||
333 | /* released by caller */ | ||
334 | return new_profile; | ||
335 | } | ||
336 | |||
337 | /** | ||
338 | * apparmor_bprm_set_creds - set the new creds on the bprm struct | ||
339 | * @bprm: binprm for the exec (NOT NULL) | ||
340 | * | ||
341 | * Returns: %0 or error on failure | ||
342 | */ | ||
343 | int apparmor_bprm_set_creds(struct linux_binprm *bprm) | ||
344 | { | ||
345 | struct aa_task_cxt *cxt; | ||
346 | struct aa_profile *profile, *new_profile = NULL; | ||
347 | struct aa_namespace *ns; | ||
348 | char *buffer = NULL; | ||
349 | unsigned int state; | ||
350 | struct file_perms perms = {}; | ||
351 | struct path_cond cond = { | ||
352 | bprm->file->f_path.dentry->d_inode->i_uid, | ||
353 | bprm->file->f_path.dentry->d_inode->i_mode | ||
354 | }; | ||
355 | const char *name = NULL, *target = NULL, *info = NULL; | ||
356 | int error = cap_bprm_set_creds(bprm); | ||
357 | if (error) | ||
358 | return error; | ||
359 | |||
360 | if (bprm->cred_prepared) | ||
361 | return 0; | ||
362 | |||
363 | cxt = bprm->cred->security; | ||
364 | BUG_ON(!cxt); | ||
365 | |||
366 | profile = aa_get_profile(aa_newest_version(cxt->profile)); | ||
367 | /* | ||
368 | * get the namespace from the replacement profile as replacement | ||
369 | * can change the namespace | ||
370 | */ | ||
371 | ns = profile->ns; | ||
372 | state = profile->file.start; | ||
373 | |||
374 | /* buffer freed below, name is pointer into buffer */ | ||
375 | error = aa_get_name(&bprm->file->f_path, profile->path_flags, &buffer, | ||
376 | &name); | ||
377 | if (error) { | ||
378 | if (profile->flags & | ||
379 | (PFLAG_IX_ON_NAME_ERROR | PFLAG_UNCONFINED)) | ||
380 | error = 0; | ||
381 | info = "Exec failed name resolution"; | ||
382 | name = bprm->filename; | ||
383 | goto audit; | ||
384 | } | ||
385 | |||
386 | /* Test for onexec first as onexec directives override other | ||
387 | * x transitions. | ||
388 | */ | ||
389 | if (unconfined(profile)) { | ||
390 | /* unconfined task */ | ||
391 | if (cxt->onexec) | ||
392 | /* change_profile on exec already been granted */ | ||
393 | new_profile = aa_get_profile(cxt->onexec); | ||
394 | else | ||
395 | new_profile = find_attach(ns, &ns->base.profiles, name); | ||
396 | if (!new_profile) | ||
397 | goto cleanup; | ||
398 | goto apply; | ||
399 | } | ||
400 | |||
401 | /* find exec permissions for name */ | ||
402 | state = aa_str_perms(profile->file.dfa, state, name, &cond, &perms); | ||
403 | if (cxt->onexec) { | ||
404 | struct file_perms cp; | ||
405 | info = "change_profile onexec"; | ||
406 | if (!(perms.allow & AA_MAY_ONEXEC)) | ||
407 | goto audit; | ||
408 | |||
409 | /* test if this exec can be paired with change_profile onexec. | ||
410 | * onexec permission is linked to exec with a standard pairing | ||
411 | * exec\0change_profile | ||
412 | */ | ||
413 | state = aa_dfa_null_transition(profile->file.dfa, state); | ||
414 | cp = change_profile_perms(profile, cxt->onexec->ns, name, | ||
415 | AA_MAY_ONEXEC, state); | ||
416 | |||
417 | if (!(cp.allow & AA_MAY_ONEXEC)) | ||
418 | goto audit; | ||
419 | new_profile = aa_get_profile(aa_newest_version(cxt->onexec)); | ||
420 | goto apply; | ||
421 | } | ||
422 | |||
423 | if (perms.allow & MAY_EXEC) { | ||
424 | /* exec permission determine how to transition */ | ||
425 | new_profile = x_to_profile(profile, name, perms.xindex); | ||
426 | if (!new_profile) { | ||
427 | if (perms.xindex & AA_X_INHERIT) { | ||
428 | /* (p|c|n)ix - don't change profile but do | ||
429 | * use the newest version, which was picked | ||
430 | * up above when getting profile | ||
431 | */ | ||
432 | info = "ix fallback"; | ||
433 | new_profile = aa_get_profile(profile); | ||
434 | goto x_clear; | ||
435 | } else if (perms.xindex & AA_X_UNCONFINED) { | ||
436 | new_profile = aa_get_profile(ns->unconfined); | ||
437 | info = "ux fallback"; | ||
438 | } else { | ||
439 | error = -ENOENT; | ||
440 | info = "profile not found"; | ||
441 | } | ||
442 | } | ||
443 | } else if (COMPLAIN_MODE(profile)) { | ||
444 | /* no exec permission - are we in learning mode */ | ||
445 | new_profile = aa_new_null_profile(profile, 0); | ||
446 | if (!new_profile) { | ||
447 | error = -ENOMEM; | ||
448 | info = "could not create null profile"; | ||
449 | } else { | ||
450 | error = -EACCES; | ||
451 | target = new_profile->base.hname; | ||
452 | } | ||
453 | perms.xindex |= AA_X_UNSAFE; | ||
454 | } else | ||
455 | /* fail exec */ | ||
456 | error = -EACCES; | ||
457 | |||
458 | if (!new_profile) | ||
459 | goto audit; | ||
460 | |||
461 | if (bprm->unsafe & LSM_UNSAFE_SHARE) { | ||
462 | /* FIXME: currently don't mediate shared state */ | ||
463 | ; | ||
464 | } | ||
465 | |||
466 | if (bprm->unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) { | ||
467 | error = may_change_ptraced_domain(current, new_profile); | ||
468 | if (error) { | ||
469 | aa_put_profile(new_profile); | ||
470 | goto audit; | ||
471 | } | ||
472 | } | ||
473 | |||
474 | /* Determine if secure exec is needed. | ||
475 | * Can be at this point for the following reasons: | ||
476 | * 1. unconfined switching to confined | ||
477 | * 2. confined switching to different confinement | ||
478 | * 3. confined switching to unconfined | ||
479 | * | ||
480 | * Cases 2 and 3 are marked as requiring secure exec | ||
481 | * (unless policy specified "unsafe exec") | ||
482 | * | ||
483 | * bprm->unsafe is used to cache the AA_X_UNSAFE permission | ||
484 | * to avoid having to recompute in secureexec | ||
485 | */ | ||
486 | if (!(perms.xindex & AA_X_UNSAFE)) { | ||
487 | AA_DEBUG("scrubbing environment variables for %s profile=%s\n", | ||
488 | name, new_profile->base.hname); | ||
489 | bprm->unsafe |= AA_SECURE_X_NEEDED; | ||
490 | } | ||
491 | apply: | ||
492 | target = new_profile->base.hname; | ||
493 | /* when transitioning profiles clear unsafe personality bits */ | ||
494 | bprm->per_clear |= PER_CLEAR_ON_SETID; | ||
495 | |||
496 | x_clear: | ||
497 | aa_put_profile(cxt->profile); | ||
498 | /* transfer new profile reference will be released when cxt is freed */ | ||
499 | cxt->profile = new_profile; | ||
500 | |||
501 | /* clear out all temporary/transitional state from the context */ | ||
502 | aa_put_profile(cxt->previous); | ||
503 | aa_put_profile(cxt->onexec); | ||
504 | cxt->previous = NULL; | ||
505 | cxt->onexec = NULL; | ||
506 | cxt->token = 0; | ||
507 | |||
508 | audit: | ||
509 | error = aa_audit_file(profile, &perms, GFP_KERNEL, OP_EXEC, MAY_EXEC, | ||
510 | name, target, cond.uid, info, error); | ||
511 | |||
512 | cleanup: | ||
513 | aa_put_profile(profile); | ||
514 | kfree(buffer); | ||
515 | |||
516 | return error; | ||
517 | } | ||
518 | |||
519 | /** | ||
520 | * apparmor_bprm_secureexec - determine if secureexec is needed | ||
521 | * @bprm: binprm for exec (NOT NULL) | ||
522 | * | ||
523 | * Returns: %1 if secureexec is needed else %0 | ||
524 | */ | ||
525 | int apparmor_bprm_secureexec(struct linux_binprm *bprm) | ||
526 | { | ||
527 | int ret = cap_bprm_secureexec(bprm); | ||
528 | |||
529 | /* the decision to use secure exec is computed in set_creds | ||
530 | * and stored in bprm->unsafe. | ||
531 | */ | ||
532 | if (!ret && (bprm->unsafe & AA_SECURE_X_NEEDED)) | ||
533 | ret = 1; | ||
534 | |||
535 | return ret; | ||
536 | } | ||
537 | |||
538 | /** | ||
539 | * apparmor_bprm_committing_creds - do task cleanup on committing new creds | ||
540 | * @bprm: binprm for the exec (NOT NULL) | ||
541 | */ | ||
542 | void apparmor_bprm_committing_creds(struct linux_binprm *bprm) | ||
543 | { | ||
544 | struct aa_profile *profile = __aa_current_profile(); | ||
545 | struct aa_task_cxt *new_cxt = bprm->cred->security; | ||
546 | |||
547 | /* bail out if unconfined or not changing profile */ | ||
548 | if ((new_cxt->profile == profile) || | ||
549 | (unconfined(new_cxt->profile))) | ||
550 | return; | ||
551 | |||
552 | current->pdeath_signal = 0; | ||
553 | |||
554 | /* reset soft limits and set hard limits for the new profile */ | ||
555 | __aa_transition_rlimits(profile, new_cxt->profile); | ||
556 | } | ||
557 | |||
558 | /** | ||
559 | * apparmor_bprm_commited_cred - do cleanup after new creds committed | ||
560 | * @bprm: binprm for the exec (NOT NULL) | ||
561 | */ | ||
562 | void apparmor_bprm_committed_creds(struct linux_binprm *bprm) | ||
563 | { | ||
564 | /* TODO: cleanup signals - ipc mediation */ | ||
565 | return; | ||
566 | } | ||
567 | |||
568 | /* | ||
569 | * Functions for self directed profile change | ||
570 | */ | ||
571 | |||
572 | /** | ||
573 | * new_compound_name - create an hname with @n2 appended to @n1 | ||
574 | * @n1: base of hname (NOT NULL) | ||
575 | * @n2: name to append (NOT NULL) | ||
576 | * | ||
577 | * Returns: new name or NULL on error | ||
578 | */ | ||
579 | static char *new_compound_name(const char *n1, const char *n2) | ||
580 | { | ||
581 | char *name = kmalloc(strlen(n1) + strlen(n2) + 3, GFP_KERNEL); | ||
582 | if (name) | ||
583 | sprintf(name, "%s//%s", n1, n2); | ||
584 | return name; | ||
585 | } | ||
586 | |||
587 | /** | ||
588 | * aa_change_hat - change hat to/from subprofile | ||
589 | * @hats: vector of hat names to try changing into (MAYBE NULL if @count == 0) | ||
590 | * @count: number of hat names in @hats | ||
591 | * @token: magic value to validate the hat change | ||
592 | * @permtest: true if this is just a permission test | ||
593 | * | ||
594 | * Change to the first profile specified in @hats that exists, and store | ||
595 | * the @hat_magic in the current task context. If the count == 0 and the | ||
596 | * @token matches that stored in the current task context, return to the | ||
597 | * top level profile. | ||
598 | * | ||
599 | * Returns %0 on success, error otherwise. | ||
600 | */ | ||
601 | int aa_change_hat(const char *hats[], int count, u64 token, bool permtest) | ||
602 | { | ||
603 | const struct cred *cred; | ||
604 | struct aa_task_cxt *cxt; | ||
605 | struct aa_profile *profile, *previous_profile, *hat = NULL; | ||
606 | char *name = NULL; | ||
607 | int i; | ||
608 | struct file_perms perms = {}; | ||
609 | const char *target = NULL, *info = NULL; | ||
610 | int error = 0; | ||
611 | |||
612 | /* released below */ | ||
613 | cred = get_current_cred(); | ||
614 | cxt = cred->security; | ||
615 | profile = aa_cred_profile(cred); | ||
616 | previous_profile = cxt->previous; | ||
617 | |||
618 | if (unconfined(profile)) { | ||
619 | info = "unconfined"; | ||
620 | error = -EPERM; | ||
621 | goto audit; | ||
622 | } | ||
623 | |||
624 | if (count) { | ||
625 | /* attempting to change into a new hat or switch to a sibling */ | ||
626 | struct aa_profile *root; | ||
627 | root = PROFILE_IS_HAT(profile) ? profile->parent : profile; | ||
628 | |||
629 | /* find first matching hat */ | ||
630 | for (i = 0; i < count && !hat; i++) | ||
631 | /* released below */ | ||
632 | hat = aa_find_child(root, hats[i]); | ||
633 | if (!hat) { | ||
634 | if (!COMPLAIN_MODE(root) || permtest) { | ||
635 | if (list_empty(&root->base.profiles)) | ||
636 | error = -ECHILD; | ||
637 | else | ||
638 | error = -ENOENT; | ||
639 | goto out; | ||
640 | } | ||
641 | |||
642 | /* | ||
643 | * In complain mode and failed to match any hats. | ||
644 | * Audit the failure is based off of the first hat | ||
645 | * supplied. This is done due how userspace | ||
646 | * interacts with change_hat. | ||
647 | * | ||
648 | * TODO: Add logging of all failed hats | ||
649 | */ | ||
650 | |||
651 | /* freed below */ | ||
652 | name = new_compound_name(root->base.hname, hats[0]); | ||
653 | target = name; | ||
654 | /* released below */ | ||
655 | hat = aa_new_null_profile(profile, 1); | ||
656 | if (!hat) { | ||
657 | info = "failed null profile create"; | ||
658 | error = -ENOMEM; | ||
659 | goto audit; | ||
660 | } | ||
661 | } else { | ||
662 | target = hat->base.hname; | ||
663 | if (!PROFILE_IS_HAT(hat)) { | ||
664 | info = "target not hat"; | ||
665 | error = -EPERM; | ||
666 | goto audit; | ||
667 | } | ||
668 | } | ||
669 | |||
670 | error = may_change_ptraced_domain(current, hat); | ||
671 | if (error) { | ||
672 | info = "ptraced"; | ||
673 | error = -EPERM; | ||
674 | goto audit; | ||
675 | } | ||
676 | |||
677 | if (!permtest) { | ||
678 | error = aa_set_current_hat(hat, token); | ||
679 | if (error == -EACCES) | ||
680 | /* kill task in case of brute force attacks */ | ||
681 | perms.kill = AA_MAY_CHANGEHAT; | ||
682 | else if (name && !error) | ||
683 | /* reset error for learning of new hats */ | ||
684 | error = -ENOENT; | ||
685 | } | ||
686 | } else if (previous_profile) { | ||
687 | /* Return to saved profile. Kill task if restore fails | ||
688 | * to avoid brute force attacks | ||
689 | */ | ||
690 | target = previous_profile->base.hname; | ||
691 | error = aa_restore_previous_profile(token); | ||
692 | perms.kill = AA_MAY_CHANGEHAT; | ||
693 | } else | ||
694 | /* ignore restores when there is no saved profile */ | ||
695 | goto out; | ||
696 | |||
697 | audit: | ||
698 | if (!permtest) | ||
699 | error = aa_audit_file(profile, &perms, GFP_KERNEL, | ||
700 | OP_CHANGE_HAT, AA_MAY_CHANGEHAT, NULL, | ||
701 | target, 0, info, error); | ||
702 | |||
703 | out: | ||
704 | aa_put_profile(hat); | ||
705 | kfree(name); | ||
706 | put_cred(cred); | ||
707 | |||
708 | return error; | ||
709 | } | ||
710 | |||
711 | /** | ||
712 | * aa_change_profile - perform a one-way profile transition | ||
713 | * @ns_name: name of the profile namespace to change to (MAYBE NULL) | ||
714 | * @hname: name of profile to change to (MAYBE NULL) | ||
715 | * @onexec: whether this transition is to take place immediately or at exec | ||
716 | * @permtest: true if this is just a permission test | ||
717 | * | ||
718 | * Change to new profile @name. Unlike with hats, there is no way | ||
719 | * to change back. If @name isn't specified the current profile name is | ||
720 | * used. | ||
721 | * If @onexec then the transition is delayed until | ||
722 | * the next exec. | ||
723 | * | ||
724 | * Returns %0 on success, error otherwise. | ||
725 | */ | ||
726 | int aa_change_profile(const char *ns_name, const char *hname, bool onexec, | ||
727 | bool permtest) | ||
728 | { | ||
729 | const struct cred *cred; | ||
730 | struct aa_task_cxt *cxt; | ||
731 | struct aa_profile *profile, *target = NULL; | ||
732 | struct aa_namespace *ns = NULL; | ||
733 | struct file_perms perms = {}; | ||
734 | const char *name = NULL, *info = NULL; | ||
735 | int op, error = 0; | ||
736 | u32 request; | ||
737 | |||
738 | if (!hname && !ns_name) | ||
739 | return -EINVAL; | ||
740 | |||
741 | if (onexec) { | ||
742 | request = AA_MAY_ONEXEC; | ||
743 | op = OP_CHANGE_ONEXEC; | ||
744 | } else { | ||
745 | request = AA_MAY_CHANGE_PROFILE; | ||
746 | op = OP_CHANGE_PROFILE; | ||
747 | } | ||
748 | |||
749 | cred = get_current_cred(); | ||
750 | cxt = cred->security; | ||
751 | profile = aa_cred_profile(cred); | ||
752 | |||
753 | if (ns_name) { | ||
754 | /* released below */ | ||
755 | ns = aa_find_namespace(profile->ns, ns_name); | ||
756 | if (!ns) { | ||
757 | /* we don't create new namespace in complain mode */ | ||
758 | name = ns_name; | ||
759 | info = "namespace not found"; | ||
760 | error = -ENOENT; | ||
761 | goto audit; | ||
762 | } | ||
763 | } else | ||
764 | /* released below */ | ||
765 | ns = aa_get_namespace(profile->ns); | ||
766 | |||
767 | /* if the name was not specified, use the name of the current profile */ | ||
768 | if (!hname) { | ||
769 | if (unconfined(profile)) | ||
770 | hname = ns->unconfined->base.hname; | ||
771 | else | ||
772 | hname = profile->base.hname; | ||
773 | } | ||
774 | |||
775 | perms = change_profile_perms(profile, ns, hname, request, | ||
776 | profile->file.start); | ||
777 | if (!(perms.allow & request)) { | ||
778 | error = -EACCES; | ||
779 | goto audit; | ||
780 | } | ||
781 | |||
782 | /* released below */ | ||
783 | target = aa_lookup_profile(ns, hname); | ||
784 | if (!target) { | ||
785 | info = "profile not found"; | ||
786 | error = -ENOENT; | ||
787 | if (permtest || !COMPLAIN_MODE(profile)) | ||
788 | goto audit; | ||
789 | /* released below */ | ||
790 | target = aa_new_null_profile(profile, 0); | ||
791 | if (!target) { | ||
792 | info = "failed null profile create"; | ||
793 | error = -ENOMEM; | ||
794 | goto audit; | ||
795 | } | ||
796 | } | ||
797 | |||
798 | /* check if tracing task is allowed to trace target domain */ | ||
799 | error = may_change_ptraced_domain(current, target); | ||
800 | if (error) { | ||
801 | info = "ptrace prevents transition"; | ||
802 | goto audit; | ||
803 | } | ||
804 | |||
805 | if (permtest) | ||
806 | goto audit; | ||
807 | |||
808 | if (onexec) | ||
809 | error = aa_set_current_onexec(target); | ||
810 | else | ||
811 | error = aa_replace_current_profile(target); | ||
812 | |||
813 | audit: | ||
814 | if (!permtest) | ||
815 | error = aa_audit_file(profile, &perms, GFP_KERNEL, op, request, | ||
816 | name, hname, 0, info, error); | ||
817 | |||
818 | aa_put_namespace(ns); | ||
819 | aa_put_profile(target); | ||
820 | put_cred(cred); | ||
821 | |||
822 | return error; | ||
823 | } | ||
diff --git a/security/apparmor/file.c b/security/apparmor/file.c new file mode 100644 index 00000000000..7312db74121 --- /dev/null +++ b/security/apparmor/file.c | |||
@@ -0,0 +1,457 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor mediation of files | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #include "include/apparmor.h" | ||
16 | #include "include/audit.h" | ||
17 | #include "include/file.h" | ||
18 | #include "include/match.h" | ||
19 | #include "include/path.h" | ||
20 | #include "include/policy.h" | ||
21 | |||
22 | struct file_perms nullperms; | ||
23 | |||
24 | |||
25 | /** | ||
26 | * audit_file_mask - convert mask to permission string | ||
27 | * @buffer: buffer to write string to (NOT NULL) | ||
28 | * @mask: permission mask to convert | ||
29 | */ | ||
30 | static void audit_file_mask(struct audit_buffer *ab, u32 mask) | ||
31 | { | ||
32 | char str[10]; | ||
33 | |||
34 | char *m = str; | ||
35 | |||
36 | if (mask & AA_EXEC_MMAP) | ||
37 | *m++ = 'm'; | ||
38 | if (mask & (MAY_READ | AA_MAY_META_READ)) | ||
39 | *m++ = 'r'; | ||
40 | if (mask & (MAY_WRITE | AA_MAY_META_WRITE | AA_MAY_CHMOD | | ||
41 | AA_MAY_CHOWN)) | ||
42 | *m++ = 'w'; | ||
43 | else if (mask & MAY_APPEND) | ||
44 | *m++ = 'a'; | ||
45 | if (mask & AA_MAY_CREATE) | ||
46 | *m++ = 'c'; | ||
47 | if (mask & AA_MAY_DELETE) | ||
48 | *m++ = 'd'; | ||
49 | if (mask & AA_MAY_LINK) | ||
50 | *m++ = 'l'; | ||
51 | if (mask & AA_MAY_LOCK) | ||
52 | *m++ = 'k'; | ||
53 | if (mask & MAY_EXEC) | ||
54 | *m++ = 'x'; | ||
55 | *m = '\0'; | ||
56 | |||
57 | audit_log_string(ab, str); | ||
58 | } | ||
59 | |||
60 | /** | ||
61 | * file_audit_cb - call back for file specific audit fields | ||
62 | * @ab: audit_buffer (NOT NULL) | ||
63 | * @va: audit struct to audit values of (NOT NULL) | ||
64 | */ | ||
65 | static void file_audit_cb(struct audit_buffer *ab, void *va) | ||
66 | { | ||
67 | struct common_audit_data *sa = va; | ||
68 | uid_t fsuid = current_fsuid(); | ||
69 | |||
70 | if (sa->aad.fs.request & AA_AUDIT_FILE_MASK) { | ||
71 | audit_log_format(ab, " requested_mask="); | ||
72 | audit_file_mask(ab, sa->aad.fs.request); | ||
73 | } | ||
74 | if (sa->aad.fs.denied & AA_AUDIT_FILE_MASK) { | ||
75 | audit_log_format(ab, " denied_mask="); | ||
76 | audit_file_mask(ab, sa->aad.fs.denied); | ||
77 | } | ||
78 | if (sa->aad.fs.request & AA_AUDIT_FILE_MASK) { | ||
79 | audit_log_format(ab, " fsuid=%d", fsuid); | ||
80 | audit_log_format(ab, " ouid=%d", sa->aad.fs.ouid); | ||
81 | } | ||
82 | |||
83 | if (sa->aad.fs.target) { | ||
84 | audit_log_format(ab, " target="); | ||
85 | audit_log_untrustedstring(ab, sa->aad.fs.target); | ||
86 | } | ||
87 | } | ||
88 | |||
89 | /** | ||
90 | * aa_audit_file - handle the auditing of file operations | ||
91 | * @profile: the profile being enforced (NOT NULL) | ||
92 | * @perms: the permissions computed for the request (NOT NULL) | ||
93 | * @gfp: allocation flags | ||
94 | * @op: operation being mediated | ||
95 | * @request: permissions requested | ||
96 | * @name: name of object being mediated (MAYBE NULL) | ||
97 | * @target: name of target (MAYBE NULL) | ||
98 | * @ouid: object uid | ||
99 | * @info: extra information message (MAYBE NULL) | ||
100 | * @error: 0 if operation allowed else failure error code | ||
101 | * | ||
102 | * Returns: %0 or error on failure | ||
103 | */ | ||
104 | int aa_audit_file(struct aa_profile *profile, struct file_perms *perms, | ||
105 | gfp_t gfp, int op, u32 request, const char *name, | ||
106 | const char *target, uid_t ouid, const char *info, int error) | ||
107 | { | ||
108 | int type = AUDIT_APPARMOR_AUTO; | ||
109 | struct common_audit_data sa; | ||
110 | COMMON_AUDIT_DATA_INIT(&sa, NONE); | ||
111 | sa.aad.op = op, | ||
112 | sa.aad.fs.request = request; | ||
113 | sa.aad.name = name; | ||
114 | sa.aad.fs.target = target; | ||
115 | sa.aad.fs.ouid = ouid; | ||
116 | sa.aad.info = info; | ||
117 | sa.aad.error = error; | ||
118 | |||
119 | if (likely(!sa.aad.error)) { | ||
120 | u32 mask = perms->audit; | ||
121 | |||
122 | if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL)) | ||
123 | mask = 0xffff; | ||
124 | |||
125 | /* mask off perms that are not being force audited */ | ||
126 | sa.aad.fs.request &= mask; | ||
127 | |||
128 | if (likely(!sa.aad.fs.request)) | ||
129 | return 0; | ||
130 | type = AUDIT_APPARMOR_AUDIT; | ||
131 | } else { | ||
132 | /* only report permissions that were denied */ | ||
133 | sa.aad.fs.request = sa.aad.fs.request & ~perms->allow; | ||
134 | |||
135 | if (sa.aad.fs.request & perms->kill) | ||
136 | type = AUDIT_APPARMOR_KILL; | ||
137 | |||
138 | /* quiet known rejects, assumes quiet and kill do not overlap */ | ||
139 | if ((sa.aad.fs.request & perms->quiet) && | ||
140 | AUDIT_MODE(profile) != AUDIT_NOQUIET && | ||
141 | AUDIT_MODE(profile) != AUDIT_ALL) | ||
142 | sa.aad.fs.request &= ~perms->quiet; | ||
143 | |||
144 | if (!sa.aad.fs.request) | ||
145 | return COMPLAIN_MODE(profile) ? 0 : sa.aad.error; | ||
146 | } | ||
147 | |||
148 | sa.aad.fs.denied = sa.aad.fs.request & ~perms->allow; | ||
149 | return aa_audit(type, profile, gfp, &sa, file_audit_cb); | ||
150 | } | ||
151 | |||
152 | /** | ||
153 | * map_old_perms - map old file perms layout to the new layout | ||
154 | * @old: permission set in old mapping | ||
155 | * | ||
156 | * Returns: new permission mapping | ||
157 | */ | ||
158 | static u32 map_old_perms(u32 old) | ||
159 | { | ||
160 | u32 new = old & 0xf; | ||
161 | if (old & MAY_READ) | ||
162 | new |= AA_MAY_META_READ; | ||
163 | if (old & MAY_WRITE) | ||
164 | new |= AA_MAY_META_WRITE | AA_MAY_CREATE | AA_MAY_DELETE | | ||
165 | AA_MAY_CHMOD | AA_MAY_CHOWN; | ||
166 | if (old & 0x10) | ||
167 | new |= AA_MAY_LINK; | ||
168 | /* the old mapping lock and link_subset flags where overlaid | ||
169 | * and use was determined by part of a pair that they were in | ||
170 | */ | ||
171 | if (old & 0x20) | ||
172 | new |= AA_MAY_LOCK | AA_LINK_SUBSET; | ||
173 | if (old & 0x40) /* AA_EXEC_MMAP */ | ||
174 | new |= AA_EXEC_MMAP; | ||
175 | |||
176 | new |= AA_MAY_META_READ; | ||
177 | |||
178 | return new; | ||
179 | } | ||
180 | |||
181 | /** | ||
182 | * compute_perms - convert dfa compressed perms to internal perms | ||
183 | * @dfa: dfa to compute perms for (NOT NULL) | ||
184 | * @state: state in dfa | ||
185 | * @cond: conditions to consider (NOT NULL) | ||
186 | * | ||
187 | * TODO: convert from dfa + state to permission entry, do computation conversion | ||
188 | * at load time. | ||
189 | * | ||
190 | * Returns: computed permission set | ||
191 | */ | ||
192 | static struct file_perms compute_perms(struct aa_dfa *dfa, unsigned int state, | ||
193 | struct path_cond *cond) | ||
194 | { | ||
195 | struct file_perms perms; | ||
196 | |||
197 | /* FIXME: change over to new dfa format | ||
198 | * currently file perms are encoded in the dfa, new format | ||
199 | * splits the permissions from the dfa. This mapping can be | ||
200 | * done at profile load | ||
201 | */ | ||
202 | perms.kill = 0; | ||
203 | |||
204 | if (current_fsuid() == cond->uid) { | ||
205 | perms.allow = map_old_perms(dfa_user_allow(dfa, state)); | ||
206 | perms.audit = map_old_perms(dfa_user_audit(dfa, state)); | ||
207 | perms.quiet = map_old_perms(dfa_user_quiet(dfa, state)); | ||
208 | perms.xindex = dfa_user_xindex(dfa, state); | ||
209 | } else { | ||
210 | perms.allow = map_old_perms(dfa_other_allow(dfa, state)); | ||
211 | perms.audit = map_old_perms(dfa_other_audit(dfa, state)); | ||
212 | perms.quiet = map_old_perms(dfa_other_quiet(dfa, state)); | ||
213 | perms.xindex = dfa_other_xindex(dfa, state); | ||
214 | } | ||
215 | |||
216 | /* change_profile wasn't determined by ownership in old mapping */ | ||
217 | if (ACCEPT_TABLE(dfa)[state] & 0x80000000) | ||
218 | perms.allow |= AA_MAY_CHANGE_PROFILE; | ||
219 | |||
220 | return perms; | ||
221 | } | ||
222 | |||
223 | /** | ||
224 | * aa_str_perms - find permission that match @name | ||
225 | * @dfa: to match against (MAYBE NULL) | ||
226 | * @state: state to start matching in | ||
227 | * @name: string to match against dfa (NOT NULL) | ||
228 | * @cond: conditions to consider for permission set computation (NOT NULL) | ||
229 | * @perms: Returns - the permissions found when matching @name | ||
230 | * | ||
231 | * Returns: the final state in @dfa when beginning @start and walking @name | ||
232 | */ | ||
233 | unsigned int aa_str_perms(struct aa_dfa *dfa, unsigned int start, | ||
234 | const char *name, struct path_cond *cond, | ||
235 | struct file_perms *perms) | ||
236 | { | ||
237 | unsigned int state; | ||
238 | if (!dfa) { | ||
239 | *perms = nullperms; | ||
240 | return DFA_NOMATCH; | ||
241 | } | ||
242 | |||
243 | state = aa_dfa_match(dfa, start, name); | ||
244 | *perms = compute_perms(dfa, state, cond); | ||
245 | |||
246 | return state; | ||
247 | } | ||
248 | |||
249 | /** | ||
250 | * is_deleted - test if a file has been completely unlinked | ||
251 | * @dentry: dentry of file to test for deletion (NOT NULL) | ||
252 | * | ||
253 | * Returns: %1 if deleted else %0 | ||
254 | */ | ||
255 | static inline bool is_deleted(struct dentry *dentry) | ||
256 | { | ||
257 | if (d_unlinked(dentry) && dentry->d_inode->i_nlink == 0) | ||
258 | return 1; | ||
259 | return 0; | ||
260 | } | ||
261 | |||
262 | /** | ||
263 | * aa_path_perm - do permissions check & audit for @path | ||
264 | * @op: operation being checked | ||
265 | * @profile: profile being enforced (NOT NULL) | ||
266 | * @path: path to check permissions of (NOT NULL) | ||
267 | * @flags: any additional path flags beyond what the profile specifies | ||
268 | * @request: requested permissions | ||
269 | * @cond: conditional info for this request (NOT NULL) | ||
270 | * | ||
271 | * Returns: %0 else error if access denied or other error | ||
272 | */ | ||
273 | int aa_path_perm(int op, struct aa_profile *profile, struct path *path, | ||
274 | int flags, u32 request, struct path_cond *cond) | ||
275 | { | ||
276 | char *buffer = NULL; | ||
277 | struct file_perms perms = {}; | ||
278 | const char *name, *info = NULL; | ||
279 | int error; | ||
280 | |||
281 | flags |= profile->path_flags | (S_ISDIR(cond->mode) ? PATH_IS_DIR : 0); | ||
282 | error = aa_get_name(path, flags, &buffer, &name); | ||
283 | if (error) { | ||
284 | if (error == -ENOENT && is_deleted(path->dentry)) { | ||
285 | /* Access to open files that are deleted are | ||
286 | * give a pass (implicit delegation) | ||
287 | */ | ||
288 | error = 0; | ||
289 | perms.allow = request; | ||
290 | } else if (error == -ENOENT) | ||
291 | info = "Failed name lookup - deleted entry"; | ||
292 | else if (error == -ESTALE) | ||
293 | info = "Failed name lookup - disconnected path"; | ||
294 | else if (error == -ENAMETOOLONG) | ||
295 | info = "Failed name lookup - name too long"; | ||
296 | else | ||
297 | info = "Failed name lookup"; | ||
298 | } else { | ||
299 | aa_str_perms(profile->file.dfa, profile->file.start, name, cond, | ||
300 | &perms); | ||
301 | if (request & ~perms.allow) | ||
302 | error = -EACCES; | ||
303 | } | ||
304 | error = aa_audit_file(profile, &perms, GFP_KERNEL, op, request, name, | ||
305 | NULL, cond->uid, info, error); | ||
306 | kfree(buffer); | ||
307 | |||
308 | return error; | ||
309 | } | ||
310 | |||
311 | /** | ||
312 | * xindex_is_subset - helper for aa_path_link | ||
313 | * @link: link permission set | ||
314 | * @target: target permission set | ||
315 | * | ||
316 | * test target x permissions are equal OR a subset of link x permissions | ||
317 | * this is done as part of the subset test, where a hardlink must have | ||
318 | * a subset of permissions that the target has. | ||
319 | * | ||
320 | * Returns: %1 if subset else %0 | ||
321 | */ | ||
322 | static inline bool xindex_is_subset(u32 link, u32 target) | ||
323 | { | ||
324 | if (((link & ~AA_X_UNSAFE) != (target & ~AA_X_UNSAFE)) || | ||
325 | ((link & AA_X_UNSAFE) && !(target & AA_X_UNSAFE))) | ||
326 | return 0; | ||
327 | |||
328 | return 1; | ||
329 | } | ||
330 | |||
331 | /** | ||
332 | * aa_path_link - Handle hard link permission check | ||
333 | * @profile: the profile being enforced (NOT NULL) | ||
334 | * @old_dentry: the target dentry (NOT NULL) | ||
335 | * @new_dir: directory the new link will be created in (NOT NULL) | ||
336 | * @new_dentry: the link being created (NOT NULL) | ||
337 | * | ||
338 | * Handle the permission test for a link & target pair. Permission | ||
339 | * is encoded as a pair where the link permission is determined | ||
340 | * first, and if allowed, the target is tested. The target test | ||
341 | * is done from the point of the link match (not start of DFA) | ||
342 | * making the target permission dependent on the link permission match. | ||
343 | * | ||
344 | * The subset test if required forces that permissions granted | ||
345 | * on link are a subset of the permission granted to target. | ||
346 | * | ||
347 | * Returns: %0 if allowed else error | ||
348 | */ | ||
349 | int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry, | ||
350 | struct path *new_dir, struct dentry *new_dentry) | ||
351 | { | ||
352 | struct path link = { new_dir->mnt, new_dentry }; | ||
353 | struct path target = { new_dir->mnt, old_dentry }; | ||
354 | struct path_cond cond = { | ||
355 | old_dentry->d_inode->i_uid, | ||
356 | old_dentry->d_inode->i_mode | ||
357 | }; | ||
358 | char *buffer = NULL, *buffer2 = NULL; | ||
359 | const char *lname, *tname = NULL, *info = NULL; | ||
360 | struct file_perms lperms, perms; | ||
361 | u32 request = AA_MAY_LINK; | ||
362 | unsigned int state; | ||
363 | int error; | ||
364 | |||
365 | lperms = nullperms; | ||
366 | |||
367 | /* buffer freed below, lname is pointer in buffer */ | ||
368 | error = aa_get_name(&link, profile->path_flags, &buffer, &lname); | ||
369 | if (error) | ||
370 | goto audit; | ||
371 | |||
372 | /* buffer2 freed below, tname is pointer in buffer2 */ | ||
373 | error = aa_get_name(&target, profile->path_flags, &buffer2, &tname); | ||
374 | if (error) | ||
375 | goto audit; | ||
376 | |||
377 | error = -EACCES; | ||
378 | /* aa_str_perms - handles the case of the dfa being NULL */ | ||
379 | state = aa_str_perms(profile->file.dfa, profile->file.start, lname, | ||
380 | &cond, &lperms); | ||
381 | |||
382 | if (!(lperms.allow & AA_MAY_LINK)) | ||
383 | goto audit; | ||
384 | |||
385 | /* test to see if target can be paired with link */ | ||
386 | state = aa_dfa_null_transition(profile->file.dfa, state); | ||
387 | aa_str_perms(profile->file.dfa, state, tname, &cond, &perms); | ||
388 | |||
389 | /* force audit/quiet masks for link are stored in the second entry | ||
390 | * in the link pair. | ||
391 | */ | ||
392 | lperms.audit = perms.audit; | ||
393 | lperms.quiet = perms.quiet; | ||
394 | lperms.kill = perms.kill; | ||
395 | |||
396 | if (!(perms.allow & AA_MAY_LINK)) { | ||
397 | info = "target restricted"; | ||
398 | goto audit; | ||
399 | } | ||
400 | |||
401 | /* done if link subset test is not required */ | ||
402 | if (!(perms.allow & AA_LINK_SUBSET)) | ||
403 | goto done_tests; | ||
404 | |||
405 | /* Do link perm subset test requiring allowed permission on link are a | ||
406 | * subset of the allowed permissions on target. | ||
407 | */ | ||
408 | aa_str_perms(profile->file.dfa, profile->file.start, tname, &cond, | ||
409 | &perms); | ||
410 | |||
411 | /* AA_MAY_LINK is not considered in the subset test */ | ||
412 | request = lperms.allow & ~AA_MAY_LINK; | ||
413 | lperms.allow &= perms.allow | AA_MAY_LINK; | ||
414 | |||
415 | request |= AA_AUDIT_FILE_MASK & (lperms.allow & ~perms.allow); | ||
416 | if (request & ~lperms.allow) { | ||
417 | goto audit; | ||
418 | } else if ((lperms.allow & MAY_EXEC) && | ||
419 | !xindex_is_subset(lperms.xindex, perms.xindex)) { | ||
420 | lperms.allow &= ~MAY_EXEC; | ||
421 | request |= MAY_EXEC; | ||
422 | info = "link not subset of target"; | ||
423 | goto audit; | ||
424 | } | ||
425 | |||
426 | done_tests: | ||
427 | error = 0; | ||
428 | |||
429 | audit: | ||
430 | error = aa_audit_file(profile, &lperms, GFP_KERNEL, OP_LINK, request, | ||
431 | lname, tname, cond.uid, info, error); | ||
432 | kfree(buffer); | ||
433 | kfree(buffer2); | ||
434 | |||
435 | return error; | ||
436 | } | ||
437 | |||
438 | /** | ||
439 | * aa_file_perm - do permission revalidation check & audit for @file | ||
440 | * @op: operation being checked | ||
441 | * @profile: profile being enforced (NOT NULL) | ||
442 | * @file: file to revalidate access permissions on (NOT NULL) | ||
443 | * @request: requested permissions | ||
444 | * | ||
445 | * Returns: %0 if access allowed else error | ||
446 | */ | ||
447 | int aa_file_perm(int op, struct aa_profile *profile, struct file *file, | ||
448 | u32 request) | ||
449 | { | ||
450 | struct path_cond cond = { | ||
451 | .uid = file->f_path.dentry->d_inode->i_uid, | ||
452 | .mode = file->f_path.dentry->d_inode->i_mode | ||
453 | }; | ||
454 | |||
455 | return aa_path_perm(op, profile, &file->f_path, PATH_DELEGATE_DELETED, | ||
456 | request, &cond); | ||
457 | } | ||
diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h new file mode 100644 index 00000000000..38ccaea0820 --- /dev/null +++ b/security/apparmor/include/apparmor.h | |||
@@ -0,0 +1,92 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor basic global and lib definitions | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #ifndef __APPARMOR_H | ||
16 | #define __APPARMOR_H | ||
17 | |||
18 | #include <linux/fs.h> | ||
19 | |||
20 | #include "match.h" | ||
21 | |||
22 | /* Control parameters settable through module/boot flags */ | ||
23 | extern enum audit_mode aa_g_audit; | ||
24 | extern int aa_g_audit_header; | ||
25 | extern int aa_g_debug; | ||
26 | extern int aa_g_lock_policy; | ||
27 | extern int aa_g_logsyscall; | ||
28 | extern int aa_g_paranoid_load; | ||
29 | extern unsigned int aa_g_path_max; | ||
30 | |||
31 | /* | ||
32 | * DEBUG remains global (no per profile flag) since it is mostly used in sysctl | ||
33 | * which is not related to profile accesses. | ||
34 | */ | ||
35 | |||
36 | #define AA_DEBUG(fmt, args...) \ | ||
37 | do { \ | ||
38 | if (aa_g_debug && printk_ratelimit()) \ | ||
39 | printk(KERN_DEBUG "AppArmor: " fmt, ##args); \ | ||
40 | } while (0) | ||
41 | |||
42 | #define AA_ERROR(fmt, args...) \ | ||
43 | do { \ | ||
44 | if (printk_ratelimit()) \ | ||
45 | printk(KERN_ERR "AppArmor: " fmt, ##args); \ | ||
46 | } while (0) | ||
47 | |||
48 | /* Flag indicating whether initialization completed */ | ||
49 | extern int apparmor_initialized __initdata; | ||
50 | |||
51 | /* fn's in lib */ | ||
52 | char *aa_split_fqname(char *args, char **ns_name); | ||
53 | void aa_info_message(const char *str); | ||
54 | void *kvmalloc(size_t size); | ||
55 | void kvfree(void *buffer); | ||
56 | |||
57 | |||
58 | /** | ||
59 | * aa_strneq - compare null terminated @str to a non null terminated substring | ||
60 | * @str: a null terminated string | ||
61 | * @sub: a substring, not necessarily null terminated | ||
62 | * @len: length of @sub to compare | ||
63 | * | ||
64 | * The @str string must be full consumed for this to be considered a match | ||
65 | */ | ||
66 | static inline bool aa_strneq(const char *str, const char *sub, int len) | ||
67 | { | ||
68 | return !strncmp(str, sub, len) && !str[len]; | ||
69 | } | ||
70 | |||
71 | /** | ||
72 | * aa_dfa_null_transition - step to next state after null character | ||
73 | * @dfa: the dfa to match against | ||
74 | * @start: the state of the dfa to start matching in | ||
75 | * | ||
76 | * aa_dfa_null_transition transitions to the next state after a null | ||
77 | * character which is not used in standard matching and is only | ||
78 | * used to separate pairs. | ||
79 | */ | ||
80 | static inline unsigned int aa_dfa_null_transition(struct aa_dfa *dfa, | ||
81 | unsigned int start) | ||
82 | { | ||
83 | /* the null transition only needs the string's null terminator byte */ | ||
84 | return aa_dfa_match_len(dfa, start, "", 1); | ||
85 | } | ||
86 | |||
87 | static inline bool mediated_filesystem(struct inode *inode) | ||
88 | { | ||
89 | return !(inode->i_sb->s_flags & MS_NOUSER); | ||
90 | } | ||
91 | |||
92 | #endif /* __APPARMOR_H */ | ||
diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h new file mode 100644 index 00000000000..cb1e93a114d --- /dev/null +++ b/security/apparmor/include/apparmorfs.h | |||
@@ -0,0 +1,20 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor filesystem definitions. | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #ifndef __AA_APPARMORFS_H | ||
16 | #define __AA_APPARMORFS_H | ||
17 | |||
18 | extern void __init aa_destroy_aafs(void); | ||
19 | |||
20 | #endif /* __AA_APPARMORFS_H */ | ||
diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h new file mode 100644 index 00000000000..1951786d32e --- /dev/null +++ b/security/apparmor/include/audit.h | |||
@@ -0,0 +1,123 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor auditing function definitions. | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #ifndef __AA_AUDIT_H | ||
16 | #define __AA_AUDIT_H | ||
17 | |||
18 | #include <linux/audit.h> | ||
19 | #include <linux/fs.h> | ||
20 | #include <linux/lsm_audit.h> | ||
21 | #include <linux/sched.h> | ||
22 | #include <linux/slab.h> | ||
23 | |||
24 | #include "file.h" | ||
25 | |||
26 | struct aa_profile; | ||
27 | |||
28 | extern const char *audit_mode_names[]; | ||
29 | #define AUDIT_MAX_INDEX 5 | ||
30 | |||
31 | #define AUDIT_APPARMOR_AUTO 0 /* auto choose audit message type */ | ||
32 | |||
33 | enum audit_mode { | ||
34 | AUDIT_NORMAL, /* follow normal auditing of accesses */ | ||
35 | AUDIT_QUIET_DENIED, /* quiet all denied access messages */ | ||
36 | AUDIT_QUIET, /* quiet all messages */ | ||
37 | AUDIT_NOQUIET, /* do not quiet audit messages */ | ||
38 | AUDIT_ALL /* audit all accesses */ | ||
39 | }; | ||
40 | |||
41 | enum audit_type { | ||
42 | AUDIT_APPARMOR_AUDIT, | ||
43 | AUDIT_APPARMOR_ALLOWED, | ||
44 | AUDIT_APPARMOR_DENIED, | ||
45 | AUDIT_APPARMOR_HINT, | ||
46 | AUDIT_APPARMOR_STATUS, | ||
47 | AUDIT_APPARMOR_ERROR, | ||
48 | AUDIT_APPARMOR_KILL | ||
49 | }; | ||
50 | |||
51 | extern const char *op_table[]; | ||
52 | enum aa_ops { | ||
53 | OP_NULL, | ||
54 | |||
55 | OP_SYSCTL, | ||
56 | OP_CAPABLE, | ||
57 | |||
58 | OP_UNLINK, | ||
59 | OP_MKDIR, | ||
60 | OP_RMDIR, | ||
61 | OP_MKNOD, | ||
62 | OP_TRUNC, | ||
63 | OP_LINK, | ||
64 | OP_SYMLINK, | ||
65 | OP_RENAME_SRC, | ||
66 | OP_RENAME_DEST, | ||
67 | OP_CHMOD, | ||
68 | OP_CHOWN, | ||
69 | OP_GETATTR, | ||
70 | OP_OPEN, | ||
71 | |||
72 | OP_FPERM, | ||
73 | OP_FLOCK, | ||
74 | OP_FMMAP, | ||
75 | OP_FMPROT, | ||
76 | |||
77 | OP_CREATE, | ||
78 | OP_POST_CREATE, | ||
79 | OP_BIND, | ||
80 | OP_CONNECT, | ||
81 | OP_LISTEN, | ||
82 | OP_ACCEPT, | ||
83 | OP_SENDMSG, | ||
84 | OP_RECVMSG, | ||
85 | OP_GETSOCKNAME, | ||
86 | OP_GETPEERNAME, | ||
87 | OP_GETSOCKOPT, | ||
88 | OP_SETSOCKOPT, | ||
89 | OP_SOCK_SHUTDOWN, | ||
90 | |||
91 | OP_PTRACE, | ||
92 | |||
93 | OP_EXEC, | ||
94 | OP_CHANGE_HAT, | ||
95 | OP_CHANGE_PROFILE, | ||
96 | OP_CHANGE_ONEXEC, | ||
97 | |||
98 | OP_SETPROCATTR, | ||
99 | OP_SETRLIMIT, | ||
100 | |||
101 | OP_PROF_REPL, | ||
102 | OP_PROF_LOAD, | ||
103 | OP_PROF_RM, | ||
104 | }; | ||
105 | |||
106 | |||
107 | /* define a short hand for apparmor_audit_data portion of common_audit_data */ | ||
108 | #define aad apparmor_audit_data | ||
109 | |||
110 | void aa_audit_msg(int type, struct common_audit_data *sa, | ||
111 | void (*cb) (struct audit_buffer *, void *)); | ||
112 | int aa_audit(int type, struct aa_profile *profile, gfp_t gfp, | ||
113 | struct common_audit_data *sa, | ||
114 | void (*cb) (struct audit_buffer *, void *)); | ||
115 | |||
116 | static inline int complain_error(int error) | ||
117 | { | ||
118 | if (error == -EPERM || error == -EACCES) | ||
119 | return 0; | ||
120 | return error; | ||
121 | } | ||
122 | |||
123 | #endif /* __AA_AUDIT_H */ | ||
diff --git a/security/apparmor/include/capability.h b/security/apparmor/include/capability.h new file mode 100644 index 00000000000..c24d2959ea0 --- /dev/null +++ b/security/apparmor/include/capability.h | |||
@@ -0,0 +1,45 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor capability mediation definitions. | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #ifndef __AA_CAPABILITY_H | ||
16 | #define __AA_CAPABILITY_H | ||
17 | |||
18 | #include <linux/sched.h> | ||
19 | |||
20 | struct aa_profile; | ||
21 | |||
22 | /* aa_caps - confinement data for capabilities | ||
23 | * @allowed: capabilities mask | ||
24 | * @audit: caps that are to be audited | ||
25 | * @quiet: caps that should not be audited | ||
26 | * @kill: caps that when requested will result in the task being killed | ||
27 | * @extended: caps that are subject finer grained mediation | ||
28 | */ | ||
29 | struct aa_caps { | ||
30 | kernel_cap_t allow; | ||
31 | kernel_cap_t audit; | ||
32 | kernel_cap_t quiet; | ||
33 | kernel_cap_t kill; | ||
34 | kernel_cap_t extended; | ||
35 | }; | ||
36 | |||
37 | int aa_capable(struct task_struct *task, struct aa_profile *profile, int cap, | ||
38 | int audit); | ||
39 | |||
40 | static inline void aa_free_cap_rules(struct aa_caps *caps) | ||
41 | { | ||
42 | /* NOP */ | ||
43 | } | ||
44 | |||
45 | #endif /* __AA_CAPBILITY_H */ | ||
diff --git a/security/apparmor/include/context.h b/security/apparmor/include/context.h new file mode 100644 index 00000000000..a9cbee4d9e4 --- /dev/null +++ b/security/apparmor/include/context.h | |||
@@ -0,0 +1,154 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor contexts used to associate "labels" to objects. | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #ifndef __AA_CONTEXT_H | ||
16 | #define __AA_CONTEXT_H | ||
17 | |||
18 | #include <linux/cred.h> | ||
19 | #include <linux/slab.h> | ||
20 | #include <linux/sched.h> | ||
21 | |||
22 | #include "policy.h" | ||
23 | |||
24 | /* struct aa_file_cxt - the AppArmor context the file was opened in | ||
25 | * @perms: the permission the file was opened with | ||
26 | * | ||
27 | * The file_cxt could currently be directly stored in file->f_security | ||
28 | * as the profile reference is now stored in the f_cred. However the | ||
29 | * cxt struct will expand in the future so we keep the struct. | ||
30 | */ | ||
31 | struct aa_file_cxt { | ||
32 | u16 allow; | ||
33 | }; | ||
34 | |||
35 | /** | ||
36 | * aa_alloc_file_context - allocate file_cxt | ||
37 | * @gfp: gfp flags for allocation | ||
38 | * | ||
39 | * Returns: file_cxt or NULL on failure | ||
40 | */ | ||
41 | static inline struct aa_file_cxt *aa_alloc_file_context(gfp_t gfp) | ||
42 | { | ||
43 | return kzalloc(sizeof(struct aa_file_cxt), gfp); | ||
44 | } | ||
45 | |||
46 | /** | ||
47 | * aa_free_file_context - free a file_cxt | ||
48 | * @cxt: file_cxt to free (MAYBE_NULL) | ||
49 | */ | ||
50 | static inline void aa_free_file_context(struct aa_file_cxt *cxt) | ||
51 | { | ||
52 | if (cxt) | ||
53 | kzfree(cxt); | ||
54 | } | ||
55 | |||
56 | /** | ||
57 | * struct aa_task_cxt - primary label for confined tasks | ||
58 | * @profile: the current profile (NOT NULL) | ||
59 | * @exec: profile to transition to on next exec (MAYBE NULL) | ||
60 | * @previous: profile the task may return to (MAYBE NULL) | ||
61 | * @token: magic value the task must know for returning to @previous_profile | ||
62 | * | ||
63 | * Contains the task's current profile (which could change due to | ||
64 | * change_hat). Plus the hat_magic needed during change_hat. | ||
65 | * | ||
66 | * TODO: make so a task can be confined by a stack of contexts | ||
67 | */ | ||
68 | struct aa_task_cxt { | ||
69 | struct aa_profile *profile; | ||
70 | struct aa_profile *onexec; | ||
71 | struct aa_profile *previous; | ||
72 | u64 token; | ||
73 | }; | ||
74 | |||
75 | struct aa_task_cxt *aa_alloc_task_context(gfp_t flags); | ||
76 | void aa_free_task_context(struct aa_task_cxt *cxt); | ||
77 | void aa_dup_task_context(struct aa_task_cxt *new, | ||
78 | const struct aa_task_cxt *old); | ||
79 | int aa_replace_current_profile(struct aa_profile *profile); | ||
80 | int aa_set_current_onexec(struct aa_profile *profile); | ||
81 | int aa_set_current_hat(struct aa_profile *profile, u64 token); | ||
82 | int aa_restore_previous_profile(u64 cookie); | ||
83 | |||
84 | /** | ||
85 | * __aa_task_is_confined - determine if @task has any confinement | ||
86 | * @task: task to check confinement of (NOT NULL) | ||
87 | * | ||
88 | * If @task != current needs to be called in RCU safe critical section | ||
89 | */ | ||
90 | static inline bool __aa_task_is_confined(struct task_struct *task) | ||
91 | { | ||
92 | struct aa_task_cxt *cxt = __task_cred(task)->security; | ||
93 | |||
94 | BUG_ON(!cxt || !cxt->profile); | ||
95 | if (unconfined(aa_newest_version(cxt->profile))) | ||
96 | return 0; | ||
97 | |||
98 | return 1; | ||
99 | } | ||
100 | |||
101 | /** | ||
102 | * aa_cred_profile - obtain cred's profiles | ||
103 | * @cred: cred to obtain profiles from (NOT NULL) | ||
104 | * | ||
105 | * Returns: confining profile | ||
106 | * | ||
107 | * does NOT increment reference count | ||
108 | */ | ||
109 | static inline struct aa_profile *aa_cred_profile(const struct cred *cred) | ||
110 | { | ||
111 | struct aa_task_cxt *cxt = cred->security; | ||
112 | BUG_ON(!cxt || !cxt->profile); | ||
113 | return aa_newest_version(cxt->profile); | ||
114 | } | ||
115 | |||
116 | /** | ||
117 | * __aa_current_profile - find the current tasks confining profile | ||
118 | * | ||
119 | * Returns: up to date confining profile or the ns unconfined profile (NOT NULL) | ||
120 | * | ||
121 | * This fn will not update the tasks cred to the most up to date version | ||
122 | * of the profile so it is safe to call when inside of locks. | ||
123 | */ | ||
124 | static inline struct aa_profile *__aa_current_profile(void) | ||
125 | { | ||
126 | return aa_cred_profile(current_cred()); | ||
127 | } | ||
128 | |||
129 | /** | ||
130 | * aa_current_profile - find the current tasks confining profile and do updates | ||
131 | * | ||
132 | * Returns: up to date confining profile or the ns unconfined profile (NOT NULL) | ||
133 | * | ||
134 | * This fn will update the tasks cred structure if the profile has been | ||
135 | * replaced. Not safe to call inside locks | ||
136 | */ | ||
137 | static inline struct aa_profile *aa_current_profile(void) | ||
138 | { | ||
139 | const struct aa_task_cxt *cxt = current_cred()->security; | ||
140 | struct aa_profile *profile; | ||
141 | BUG_ON(!cxt || !cxt->profile); | ||
142 | |||
143 | profile = aa_newest_version(cxt->profile); | ||
144 | /* | ||
145 | * Whether or not replacement succeeds, use newest profile so | ||
146 | * there is no need to update it after replacement. | ||
147 | */ | ||
148 | if (unlikely((cxt->profile != profile))) | ||
149 | aa_replace_current_profile(profile); | ||
150 | |||
151 | return profile; | ||
152 | } | ||
153 | |||
154 | #endif /* __AA_CONTEXT_H */ | ||
diff --git a/security/apparmor/include/domain.h b/security/apparmor/include/domain.h new file mode 100644 index 00000000000..de04464f0a3 --- /dev/null +++ b/security/apparmor/include/domain.h | |||
@@ -0,0 +1,36 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor security domain transition function definitions. | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #include <linux/binfmts.h> | ||
16 | #include <linux/types.h> | ||
17 | |||
18 | #ifndef __AA_DOMAIN_H | ||
19 | #define __AA_DOMAIN_H | ||
20 | |||
21 | struct aa_domain { | ||
22 | int size; | ||
23 | char **table; | ||
24 | }; | ||
25 | |||
26 | int apparmor_bprm_set_creds(struct linux_binprm *bprm); | ||
27 | int apparmor_bprm_secureexec(struct linux_binprm *bprm); | ||
28 | void apparmor_bprm_committing_creds(struct linux_binprm *bprm); | ||
29 | void apparmor_bprm_committed_creds(struct linux_binprm *bprm); | ||
30 | |||
31 | void aa_free_domain_entries(struct aa_domain *domain); | ||
32 | int aa_change_hat(const char *hats[], int count, u64 token, bool permtest); | ||
33 | int aa_change_profile(const char *ns_name, const char *name, bool onexec, | ||
34 | bool permtest); | ||
35 | |||
36 | #endif /* __AA_DOMAIN_H */ | ||
diff --git a/security/apparmor/include/file.h b/security/apparmor/include/file.h new file mode 100644 index 00000000000..be36feabb16 --- /dev/null +++ b/security/apparmor/include/file.h | |||
@@ -0,0 +1,217 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor file mediation function definitions. | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #ifndef __AA_FILE_H | ||
16 | #define __AA_FILE_H | ||
17 | |||
18 | #include <linux/path.h> | ||
19 | |||
20 | #include "domain.h" | ||
21 | #include "match.h" | ||
22 | |||
23 | struct aa_profile; | ||
24 | |||
25 | /* | ||
26 | * We use MAY_EXEC, MAY_WRITE, MAY_READ, MAY_APPEND and the following flags | ||
27 | * for profile permissions | ||
28 | */ | ||
29 | #define AA_MAY_CREATE 0x0010 | ||
30 | #define AA_MAY_DELETE 0x0020 | ||
31 | #define AA_MAY_META_WRITE 0x0040 | ||
32 | #define AA_MAY_META_READ 0x0080 | ||
33 | |||
34 | #define AA_MAY_CHMOD 0x0100 | ||
35 | #define AA_MAY_CHOWN 0x0200 | ||
36 | #define AA_MAY_LOCK 0x0400 | ||
37 | #define AA_EXEC_MMAP 0x0800 | ||
38 | |||
39 | #define AA_MAY_LINK 0x1000 | ||
40 | #define AA_LINK_SUBSET AA_MAY_LOCK /* overlaid */ | ||
41 | #define AA_MAY_ONEXEC 0x40000000 /* exec allows onexec */ | ||
42 | #define AA_MAY_CHANGE_PROFILE 0x80000000 | ||
43 | #define AA_MAY_CHANGEHAT 0x80000000 /* ctrl auditing only */ | ||
44 | |||
45 | #define AA_AUDIT_FILE_MASK (MAY_READ | MAY_WRITE | MAY_EXEC | MAY_APPEND |\ | ||
46 | AA_MAY_CREATE | AA_MAY_DELETE | \ | ||
47 | AA_MAY_META_READ | AA_MAY_META_WRITE | \ | ||
48 | AA_MAY_CHMOD | AA_MAY_CHOWN | AA_MAY_LOCK | \ | ||
49 | AA_EXEC_MMAP | AA_MAY_LINK) | ||
50 | |||
51 | /* | ||
52 | * The xindex is broken into 3 parts | ||
53 | * - index - an index into either the exec name table or the variable table | ||
54 | * - exec type - which determines how the executable name and index are used | ||
55 | * - flags - which modify how the destination name is applied | ||
56 | */ | ||
57 | #define AA_X_INDEX_MASK 0x03ff | ||
58 | |||
59 | #define AA_X_TYPE_MASK 0x0c00 | ||
60 | #define AA_X_TYPE_SHIFT 10 | ||
61 | #define AA_X_NONE 0x0000 | ||
62 | #define AA_X_NAME 0x0400 /* use executable name px */ | ||
63 | #define AA_X_TABLE 0x0800 /* use a specified name ->n# */ | ||
64 | |||
65 | #define AA_X_UNSAFE 0x1000 | ||
66 | #define AA_X_CHILD 0x2000 /* make >AA_X_NONE apply to children */ | ||
67 | #define AA_X_INHERIT 0x4000 | ||
68 | #define AA_X_UNCONFINED 0x8000 | ||
69 | |||
70 | /* AA_SECURE_X_NEEDED - is passed in the bprm->unsafe field */ | ||
71 | #define AA_SECURE_X_NEEDED 0x8000 | ||
72 | |||
73 | /* need to make conditional which ones are being set */ | ||
74 | struct path_cond { | ||
75 | uid_t uid; | ||
76 | umode_t mode; | ||
77 | }; | ||
78 | |||
79 | /* struct file_perms - file permission | ||
80 | * @allow: mask of permissions that are allowed | ||
81 | * @audit: mask of permissions to force an audit message for | ||
82 | * @quiet: mask of permissions to quiet audit messages for | ||
83 | * @kill: mask of permissions that when matched will kill the task | ||
84 | * @xindex: exec transition index if @allow contains MAY_EXEC | ||
85 | * | ||
86 | * The @audit and @queit mask should be mutually exclusive. | ||
87 | */ | ||
88 | struct file_perms { | ||
89 | u32 allow; | ||
90 | u32 audit; | ||
91 | u32 quiet; | ||
92 | u32 kill; | ||
93 | u16 xindex; | ||
94 | }; | ||
95 | |||
96 | extern struct file_perms nullperms; | ||
97 | |||
98 | #define COMBINED_PERM_MASK(X) ((X).allow | (X).audit | (X).quiet | (X).kill) | ||
99 | |||
100 | /* FIXME: split perms from dfa and match this to description | ||
101 | * also add delegation info. | ||
102 | */ | ||
103 | static inline u16 dfa_map_xindex(u16 mask) | ||
104 | { | ||
105 | u16 old_index = (mask >> 10) & 0xf; | ||
106 | u16 index = 0; | ||
107 | |||
108 | if (mask & 0x100) | ||
109 | index |= AA_X_UNSAFE; | ||
110 | if (mask & 0x200) | ||
111 | index |= AA_X_INHERIT; | ||
112 | if (mask & 0x80) | ||
113 | index |= AA_X_UNCONFINED; | ||
114 | |||
115 | if (old_index == 1) { | ||
116 | index |= AA_X_UNCONFINED; | ||
117 | } else if (old_index == 2) { | ||
118 | index |= AA_X_NAME; | ||
119 | } else if (old_index == 3) { | ||
120 | index |= AA_X_NAME | AA_X_CHILD; | ||
121 | } else { | ||
122 | index |= AA_X_TABLE; | ||
123 | index |= old_index - 4; | ||
124 | } | ||
125 | |||
126 | return index; | ||
127 | } | ||
128 | |||
129 | /* | ||
130 | * map old dfa inline permissions to new format | ||
131 | */ | ||
132 | #define dfa_user_allow(dfa, state) (((ACCEPT_TABLE(dfa)[state]) & 0x7f) | \ | ||
133 | ((ACCEPT_TABLE(dfa)[state]) & 0x80000000)) | ||
134 | #define dfa_user_audit(dfa, state) ((ACCEPT_TABLE2(dfa)[state]) & 0x7f) | ||
135 | #define dfa_user_quiet(dfa, state) (((ACCEPT_TABLE2(dfa)[state]) >> 7) & 0x7f) | ||
136 | #define dfa_user_xindex(dfa, state) \ | ||
137 | (dfa_map_xindex(ACCEPT_TABLE(dfa)[state] & 0x3fff)) | ||
138 | |||
139 | #define dfa_other_allow(dfa, state) ((((ACCEPT_TABLE(dfa)[state]) >> 14) & \ | ||
140 | 0x7f) | \ | ||
141 | ((ACCEPT_TABLE(dfa)[state]) & 0x80000000)) | ||
142 | #define dfa_other_audit(dfa, state) (((ACCEPT_TABLE2(dfa)[state]) >> 14) & 0x7f) | ||
143 | #define dfa_other_quiet(dfa, state) \ | ||
144 | ((((ACCEPT_TABLE2(dfa)[state]) >> 7) >> 14) & 0x7f) | ||
145 | #define dfa_other_xindex(dfa, state) \ | ||
146 | dfa_map_xindex((ACCEPT_TABLE(dfa)[state] >> 14) & 0x3fff) | ||
147 | |||
148 | int aa_audit_file(struct aa_profile *profile, struct file_perms *perms, | ||
149 | gfp_t gfp, int op, u32 request, const char *name, | ||
150 | const char *target, uid_t ouid, const char *info, int error); | ||
151 | |||
152 | /** | ||
153 | * struct aa_file_rules - components used for file rule permissions | ||
154 | * @dfa: dfa to match path names and conditionals against | ||
155 | * @perms: permission table indexed by the matched state accept entry of @dfa | ||
156 | * @trans: transition table for indexed by named x transitions | ||
157 | * | ||
158 | * File permission are determined by matching a path against @dfa and then | ||
159 | * then using the value of the accept entry for the matching state as | ||
160 | * an index into @perms. If a named exec transition is required it is | ||
161 | * looked up in the transition table. | ||
162 | */ | ||
163 | struct aa_file_rules { | ||
164 | unsigned int start; | ||
165 | struct aa_dfa *dfa; | ||
166 | /* struct perms perms; */ | ||
167 | struct aa_domain trans; | ||
168 | /* TODO: add delegate table */ | ||
169 | }; | ||
170 | |||
171 | unsigned int aa_str_perms(struct aa_dfa *dfa, unsigned int start, | ||
172 | const char *name, struct path_cond *cond, | ||
173 | struct file_perms *perms); | ||
174 | |||
175 | int aa_path_perm(int op, struct aa_profile *profile, struct path *path, | ||
176 | int flags, u32 request, struct path_cond *cond); | ||
177 | |||
178 | int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry, | ||
179 | struct path *new_dir, struct dentry *new_dentry); | ||
180 | |||
181 | int aa_file_perm(int op, struct aa_profile *profile, struct file *file, | ||
182 | u32 request); | ||
183 | |||
184 | static inline void aa_free_file_rules(struct aa_file_rules *rules) | ||
185 | { | ||
186 | aa_put_dfa(rules->dfa); | ||
187 | aa_free_domain_entries(&rules->trans); | ||
188 | } | ||
189 | |||
190 | #define ACC_FMODE(x) (("\000\004\002\006"[(x)&O_ACCMODE]) | (((x) << 1) & 0x40)) | ||
191 | |||
192 | /* from namei.c */ | ||
193 | #define MAP_OPEN_FLAGS(x) ((((x) + 1) & O_ACCMODE) ? (x) + 1 : (x)) | ||
194 | |||
195 | /** | ||
196 | * aa_map_file_perms - map file flags to AppArmor permissions | ||
197 | * @file: open file to map flags to AppArmor permissions | ||
198 | * | ||
199 | * Returns: apparmor permission set for the file | ||
200 | */ | ||
201 | static inline u32 aa_map_file_to_perms(struct file *file) | ||
202 | { | ||
203 | int flags = MAP_OPEN_FLAGS(file->f_flags); | ||
204 | u32 perms = ACC_FMODE(file->f_mode); | ||
205 | |||
206 | if ((flags & O_APPEND) && (perms & MAY_WRITE)) | ||
207 | perms = (perms & ~MAY_WRITE) | MAY_APPEND; | ||
208 | /* trunc implies write permission */ | ||
209 | if (flags & O_TRUNC) | ||
210 | perms |= MAY_WRITE; | ||
211 | if (flags & O_CREAT) | ||
212 | perms |= AA_MAY_CREATE; | ||
213 | |||
214 | return perms; | ||
215 | } | ||
216 | |||
217 | #endif /* __AA_FILE_H */ | ||
diff --git a/security/apparmor/include/ipc.h b/security/apparmor/include/ipc.h new file mode 100644 index 00000000000..aeda0fbc8b2 --- /dev/null +++ b/security/apparmor/include/ipc.h | |||
@@ -0,0 +1,28 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor ipc mediation function definitions. | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #ifndef __AA_IPC_H | ||
16 | #define __AA_IPC_H | ||
17 | |||
18 | #include <linux/sched.h> | ||
19 | |||
20 | struct aa_profile; | ||
21 | |||
22 | int aa_may_ptrace(struct task_struct *tracer_task, struct aa_profile *tracer, | ||
23 | struct aa_profile *tracee, unsigned int mode); | ||
24 | |||
25 | int aa_ptrace(struct task_struct *tracer, struct task_struct *tracee, | ||
26 | unsigned int mode); | ||
27 | |||
28 | #endif /* __AA_IPC_H */ | ||
diff --git a/security/apparmor/include/match.h b/security/apparmor/include/match.h new file mode 100644 index 00000000000..734a6d35112 --- /dev/null +++ b/security/apparmor/include/match.h | |||
@@ -0,0 +1,132 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor policy dfa matching engine definitions. | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #ifndef __AA_MATCH_H | ||
16 | #define __AA_MATCH_H | ||
17 | |||
18 | #include <linux/workqueue.h> | ||
19 | |||
20 | #define DFA_NOMATCH 0 | ||
21 | #define DFA_START 1 | ||
22 | |||
23 | #define DFA_VALID_PERM_MASK 0xffffffff | ||
24 | #define DFA_VALID_PERM2_MASK 0xffffffff | ||
25 | |||
26 | /** | ||
27 | * The format used for transition tables is based on the GNU flex table | ||
28 | * file format (--tables-file option; see Table File Format in the flex | ||
29 | * info pages and the flex sources for documentation). The magic number | ||
30 | * used in the header is 0x1B5E783D insted of 0xF13C57B1 though, because | ||
31 | * the YY_ID_CHK (check) and YY_ID_DEF (default) tables are used | ||
32 | * slightly differently (see the apparmor-parser package). | ||
33 | */ | ||
34 | |||
35 | #define YYTH_MAGIC 0x1B5E783D | ||
36 | #define YYTH_DEF_RECURSE 0x1 /* DEF Table is recursive */ | ||
37 | |||
38 | struct table_set_header { | ||
39 | u32 th_magic; /* YYTH_MAGIC */ | ||
40 | u32 th_hsize; | ||
41 | u32 th_ssize; | ||
42 | u16 th_flags; | ||
43 | char th_version[]; | ||
44 | }; | ||
45 | |||
46 | /* The YYTD_ID are one less than flex table mappings. The flex id | ||
47 | * has 1 subtracted at table load time, this allows us to directly use the | ||
48 | * ID's as indexes. | ||
49 | */ | ||
50 | #define YYTD_ID_ACCEPT 0 | ||
51 | #define YYTD_ID_BASE 1 | ||
52 | #define YYTD_ID_CHK 2 | ||
53 | #define YYTD_ID_DEF 3 | ||
54 | #define YYTD_ID_EC 4 | ||
55 | #define YYTD_ID_META 5 | ||
56 | #define YYTD_ID_ACCEPT2 6 | ||
57 | #define YYTD_ID_NXT 7 | ||
58 | #define YYTD_ID_TSIZE 8 | ||
59 | |||
60 | #define YYTD_DATA8 1 | ||
61 | #define YYTD_DATA16 2 | ||
62 | #define YYTD_DATA32 4 | ||
63 | #define YYTD_DATA64 8 | ||
64 | |||
65 | /* Each ACCEPT2 table gets 6 dedicated flags, YYTD_DATAX define the | ||
66 | * first flags | ||
67 | */ | ||
68 | #define ACCEPT1_FLAGS(X) ((X) & 0x3f) | ||
69 | #define ACCEPT2_FLAGS(X) ACCEPT1_FLAGS((X) >> YYTD_ID_ACCEPT2) | ||
70 | #define TO_ACCEPT1_FLAG(X) ACCEPT1_FLAGS(X) | ||
71 | #define TO_ACCEPT2_FLAG(X) (ACCEPT1_FLAGS(X) << YYTD_ID_ACCEPT2) | ||
72 | #define DFA_FLAG_VERIFY_STATES 0x1000 | ||
73 | |||
74 | struct table_header { | ||
75 | u16 td_id; | ||
76 | u16 td_flags; | ||
77 | u32 td_hilen; | ||
78 | u32 td_lolen; | ||
79 | char td_data[]; | ||
80 | }; | ||
81 | |||
82 | #define DEFAULT_TABLE(DFA) ((u16 *)((DFA)->tables[YYTD_ID_DEF]->td_data)) | ||
83 | #define BASE_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_BASE]->td_data)) | ||
84 | #define NEXT_TABLE(DFA) ((u16 *)((DFA)->tables[YYTD_ID_NXT]->td_data)) | ||
85 | #define CHECK_TABLE(DFA) ((u16 *)((DFA)->tables[YYTD_ID_CHK]->td_data)) | ||
86 | #define EQUIV_TABLE(DFA) ((u8 *)((DFA)->tables[YYTD_ID_EC]->td_data)) | ||
87 | #define ACCEPT_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_ACCEPT]->td_data)) | ||
88 | #define ACCEPT_TABLE2(DFA) ((u32 *)((DFA)->tables[YYTD_ID_ACCEPT2]->td_data)) | ||
89 | |||
90 | struct aa_dfa { | ||
91 | struct kref count; | ||
92 | u16 flags; | ||
93 | struct table_header *tables[YYTD_ID_TSIZE]; | ||
94 | }; | ||
95 | |||
96 | #define byte_to_byte(X) (X) | ||
97 | |||
98 | #define UNPACK_ARRAY(TABLE, BLOB, LEN, TYPE, NTOHX) \ | ||
99 | do { \ | ||
100 | typeof(LEN) __i; \ | ||
101 | TYPE *__t = (TYPE *) TABLE; \ | ||
102 | TYPE *__b = (TYPE *) BLOB; \ | ||
103 | for (__i = 0; __i < LEN; __i++) { \ | ||
104 | __t[__i] = NTOHX(__b[__i]); \ | ||
105 | } \ | ||
106 | } while (0) | ||
107 | |||
108 | static inline size_t table_size(size_t len, size_t el_size) | ||
109 | { | ||
110 | return ALIGN(sizeof(struct table_header) + len * el_size, 8); | ||
111 | } | ||
112 | |||
113 | struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags); | ||
114 | unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start, | ||
115 | const char *str, int len); | ||
116 | unsigned int aa_dfa_match(struct aa_dfa *dfa, unsigned int start, | ||
117 | const char *str); | ||
118 | void aa_dfa_free_kref(struct kref *kref); | ||
119 | |||
120 | /** | ||
121 | * aa_put_dfa - put a dfa refcount | ||
122 | * @dfa: dfa to put refcount (MAYBE NULL) | ||
123 | * | ||
124 | * Requires: if @dfa != NULL that a valid refcount be held | ||
125 | */ | ||
126 | static inline void aa_put_dfa(struct aa_dfa *dfa) | ||
127 | { | ||
128 | if (dfa) | ||
129 | kref_put(&dfa->count, aa_dfa_free_kref); | ||
130 | } | ||
131 | |||
132 | #endif /* __AA_MATCH_H */ | ||
diff --git a/security/apparmor/include/path.h b/security/apparmor/include/path.h new file mode 100644 index 00000000000..27b327a7fae --- /dev/null +++ b/security/apparmor/include/path.h | |||
@@ -0,0 +1,31 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor basic path manipulation function definitions. | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #ifndef __AA_PATH_H | ||
16 | #define __AA_PATH_H | ||
17 | |||
18 | |||
19 | enum path_flags { | ||
20 | PATH_IS_DIR = 0x1, /* path is a directory */ | ||
21 | PATH_CONNECT_PATH = 0x4, /* connect disconnected paths to / */ | ||
22 | PATH_CHROOT_REL = 0x8, /* do path lookup relative to chroot */ | ||
23 | PATH_CHROOT_NSCONNECT = 0x10, /* connect paths that are at ns root */ | ||
24 | |||
25 | PATH_DELEGATE_DELETED = 0x08000, /* delegate deleted files */ | ||
26 | PATH_MEDIATE_DELETED = 0x10000, /* mediate deleted paths */ | ||
27 | }; | ||
28 | |||
29 | int aa_get_name(struct path *path, int flags, char **buffer, const char **name); | ||
30 | |||
31 | #endif /* __AA_PATH_H */ | ||
diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h new file mode 100644 index 00000000000..aeda5cf5690 --- /dev/null +++ b/security/apparmor/include/policy.h | |||
@@ -0,0 +1,305 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor policy definitions. | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #ifndef __AA_POLICY_H | ||
16 | #define __AA_POLICY_H | ||
17 | |||
18 | #include <linux/capability.h> | ||
19 | #include <linux/cred.h> | ||
20 | #include <linux/kref.h> | ||
21 | #include <linux/sched.h> | ||
22 | #include <linux/slab.h> | ||
23 | #include <linux/socket.h> | ||
24 | |||
25 | #include "apparmor.h" | ||
26 | #include "audit.h" | ||
27 | #include "capability.h" | ||
28 | #include "domain.h" | ||
29 | #include "file.h" | ||
30 | #include "resource.h" | ||
31 | |||
32 | extern const char *profile_mode_names[]; | ||
33 | #define APPARMOR_NAMES_MAX_INDEX 3 | ||
34 | |||
35 | #define COMPLAIN_MODE(_profile) \ | ||
36 | ((aa_g_profile_mode == APPARMOR_COMPLAIN) || \ | ||
37 | ((_profile)->mode == APPARMOR_COMPLAIN)) | ||
38 | |||
39 | #define KILL_MODE(_profile) \ | ||
40 | ((aa_g_profile_mode == APPARMOR_KILL) || \ | ||
41 | ((_profile)->mode == APPARMOR_KILL)) | ||
42 | |||
43 | #define PROFILE_IS_HAT(_profile) ((_profile)->flags & PFLAG_HAT) | ||
44 | |||
45 | /* | ||
46 | * FIXME: currently need a clean way to replace and remove profiles as a | ||
47 | * set. It should be done at the namespace level. | ||
48 | * Either, with a set of profiles loaded at the namespace level or via | ||
49 | * a mark and remove marked interface. | ||
50 | */ | ||
51 | enum profile_mode { | ||
52 | APPARMOR_ENFORCE, /* enforce access rules */ | ||
53 | APPARMOR_COMPLAIN, /* allow and log access violations */ | ||
54 | APPARMOR_KILL, /* kill task on access violation */ | ||
55 | }; | ||
56 | |||
57 | enum profile_flags { | ||
58 | PFLAG_HAT = 1, /* profile is a hat */ | ||
59 | PFLAG_UNCONFINED = 2, /* profile is an unconfined profile */ | ||
60 | PFLAG_NULL = 4, /* profile is null learning profile */ | ||
61 | PFLAG_IX_ON_NAME_ERROR = 8, /* fallback to ix on name lookup fail */ | ||
62 | PFLAG_IMMUTABLE = 0x10, /* don't allow changes/replacement */ | ||
63 | PFLAG_USER_DEFINED = 0x20, /* user based profile - lower privs */ | ||
64 | PFLAG_NO_LIST_REF = 0x40, /* list doesn't keep profile ref */ | ||
65 | PFLAG_OLD_NULL_TRANS = 0x100, /* use // as the null transition */ | ||
66 | |||
67 | /* These flags must correspond with PATH_flags */ | ||
68 | PFLAG_MEDIATE_DELETED = 0x10000, /* mediate instead delegate deleted */ | ||
69 | }; | ||
70 | |||
71 | struct aa_profile; | ||
72 | |||
73 | /* struct aa_policy - common part of both namespaces and profiles | ||
74 | * @name: name of the object | ||
75 | * @hname - The hierarchical name | ||
76 | * @count: reference count of the obj | ||
77 | * @list: list policy object is on | ||
78 | * @profiles: head of the profiles list contained in the object | ||
79 | */ | ||
80 | struct aa_policy { | ||
81 | char *name; | ||
82 | char *hname; | ||
83 | struct kref count; | ||
84 | struct list_head list; | ||
85 | struct list_head profiles; | ||
86 | }; | ||
87 | |||
88 | /* struct aa_ns_acct - accounting of profiles in namespace | ||
89 | * @max_size: maximum space allowed for all profiles in namespace | ||
90 | * @max_count: maximum number of profiles that can be in this namespace | ||
91 | * @size: current size of profiles | ||
92 | * @count: current count of profiles (includes null profiles) | ||
93 | */ | ||
94 | struct aa_ns_acct { | ||
95 | int max_size; | ||
96 | int max_count; | ||
97 | int size; | ||
98 | int count; | ||
99 | }; | ||
100 | |||
101 | /* struct aa_namespace - namespace for a set of profiles | ||
102 | * @base: common policy | ||
103 | * @parent: parent of namespace | ||
104 | * @lock: lock for modifying the object | ||
105 | * @acct: accounting for the namespace | ||
106 | * @unconfined: special unconfined profile for the namespace | ||
107 | * @sub_ns: list of namespaces under the current namespace. | ||
108 | * | ||
109 | * An aa_namespace defines the set profiles that are searched to determine | ||
110 | * which profile to attach to a task. Profiles can not be shared between | ||
111 | * aa_namespaces and profile names within a namespace are guaranteed to be | ||
112 | * unique. When profiles in separate namespaces have the same name they | ||
113 | * are NOT considered to be equivalent. | ||
114 | * | ||
115 | * Namespaces are hierarchical and only namespaces and profiles below the | ||
116 | * current namespace are visible. | ||
117 | * | ||
118 | * Namespace names must be unique and can not contain the characters :/\0 | ||
119 | * | ||
120 | * FIXME TODO: add vserver support of namespaces (can it all be done in | ||
121 | * userspace?) | ||
122 | */ | ||
123 | struct aa_namespace { | ||
124 | struct aa_policy base; | ||
125 | struct aa_namespace *parent; | ||
126 | rwlock_t lock; | ||
127 | struct aa_ns_acct acct; | ||
128 | struct aa_profile *unconfined; | ||
129 | struct list_head sub_ns; | ||
130 | }; | ||
131 | |||
132 | /* struct aa_profile - basic confinement data | ||
133 | * @base - base components of the profile (name, refcount, lists, lock ...) | ||
134 | * @parent: parent of profile | ||
135 | * @ns: namespace the profile is in | ||
136 | * @replacedby: is set to the profile that replaced this profile | ||
137 | * @rename: optional profile name that this profile renamed | ||
138 | * @xmatch: optional extended matching for unconfined executables names | ||
139 | * @xmatch_len: xmatch prefix len, used to determine xmatch priority | ||
140 | * @sid: the unique security id number of this profile | ||
141 | * @audit: the auditing mode of the profile | ||
142 | * @mode: the enforcement mode of the profile | ||
143 | * @flags: flags controlling profile behavior | ||
144 | * @path_flags: flags controlling path generation behavior | ||
145 | * @size: the memory consumed by this profiles rules | ||
146 | * @file: The set of rules governing basic file access and domain transitions | ||
147 | * @caps: capabilities for the profile | ||
148 | * @rlimits: rlimits for the profile | ||
149 | * | ||
150 | * The AppArmor profile contains the basic confinement data. Each profile | ||
151 | * has a name, and exists in a namespace. The @name and @exec_match are | ||
152 | * used to determine profile attachment against unconfined tasks. All other | ||
153 | * attachments are determined by profile X transition rules. | ||
154 | * | ||
155 | * The @replacedby field is write protected by the profile lock. Reads | ||
156 | * are assumed to be atomic, and are done without locking. | ||
157 | * | ||
158 | * Profiles have a hierarchy where hats and children profiles keep | ||
159 | * a reference to their parent. | ||
160 | * | ||
161 | * Profile names can not begin with a : and can not contain the \0 | ||
162 | * character. If a profile name begins with / it will be considered when | ||
163 | * determining profile attachment on "unconfined" tasks. | ||
164 | */ | ||
165 | struct aa_profile { | ||
166 | struct aa_policy base; | ||
167 | struct aa_profile *parent; | ||
168 | |||
169 | struct aa_namespace *ns; | ||
170 | struct aa_profile *replacedby; | ||
171 | const char *rename; | ||
172 | |||
173 | struct aa_dfa *xmatch; | ||
174 | int xmatch_len; | ||
175 | u32 sid; | ||
176 | enum audit_mode audit; | ||
177 | enum profile_mode mode; | ||
178 | u32 flags; | ||
179 | u32 path_flags; | ||
180 | int size; | ||
181 | |||
182 | struct aa_file_rules file; | ||
183 | struct aa_caps caps; | ||
184 | struct aa_rlimit rlimits; | ||
185 | }; | ||
186 | |||
187 | extern struct aa_namespace *root_ns; | ||
188 | extern enum profile_mode aa_g_profile_mode; | ||
189 | |||
190 | void aa_add_profile(struct aa_policy *common, struct aa_profile *profile); | ||
191 | |||
192 | bool aa_ns_visible(struct aa_namespace *curr, struct aa_namespace *view); | ||
193 | const char *aa_ns_name(struct aa_namespace *parent, struct aa_namespace *child); | ||
194 | int aa_alloc_root_ns(void); | ||
195 | void aa_free_root_ns(void); | ||
196 | void aa_free_namespace_kref(struct kref *kref); | ||
197 | |||
198 | struct aa_namespace *aa_find_namespace(struct aa_namespace *root, | ||
199 | const char *name); | ||
200 | |||
201 | static inline struct aa_policy *aa_get_common(struct aa_policy *c) | ||
202 | { | ||
203 | if (c) | ||
204 | kref_get(&c->count); | ||
205 | |||
206 | return c; | ||
207 | } | ||
208 | |||
209 | /** | ||
210 | * aa_get_namespace - increment references count on @ns | ||
211 | * @ns: namespace to increment reference count of (MAYBE NULL) | ||
212 | * | ||
213 | * Returns: pointer to @ns, if @ns is NULL returns NULL | ||
214 | * Requires: @ns must be held with valid refcount when called | ||
215 | */ | ||
216 | static inline struct aa_namespace *aa_get_namespace(struct aa_namespace *ns) | ||
217 | { | ||
218 | if (ns) | ||
219 | kref_get(&(ns->base.count)); | ||
220 | |||
221 | return ns; | ||
222 | } | ||
223 | |||
224 | /** | ||
225 | * aa_put_namespace - decrement refcount on @ns | ||
226 | * @ns: namespace to put reference of | ||
227 | * | ||
228 | * Decrement reference count of @ns and if no longer in use free it | ||
229 | */ | ||
230 | static inline void aa_put_namespace(struct aa_namespace *ns) | ||
231 | { | ||
232 | if (ns) | ||
233 | kref_put(&ns->base.count, aa_free_namespace_kref); | ||
234 | } | ||
235 | |||
236 | struct aa_profile *aa_alloc_profile(const char *name); | ||
237 | struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat); | ||
238 | void aa_free_profile_kref(struct kref *kref); | ||
239 | struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name); | ||
240 | struct aa_profile *aa_lookup_profile(struct aa_namespace *ns, const char *name); | ||
241 | struct aa_profile *aa_match_profile(struct aa_namespace *ns, const char *name); | ||
242 | |||
243 | ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace); | ||
244 | ssize_t aa_remove_profiles(char *name, size_t size); | ||
245 | |||
246 | #define PROF_ADD 1 | ||
247 | #define PROF_REPLACE 0 | ||
248 | |||
249 | #define unconfined(X) ((X)->flags & PFLAG_UNCONFINED) | ||
250 | |||
251 | /** | ||
252 | * aa_newest_version - find the newest version of @profile | ||
253 | * @profile: the profile to check for newer versions of (NOT NULL) | ||
254 | * | ||
255 | * Returns: newest version of @profile, if @profile is the newest version | ||
256 | * return @profile. | ||
257 | * | ||
258 | * NOTE: the profile returned is not refcounted, The refcount on @profile | ||
259 | * must be held until the caller decides what to do with the returned newest | ||
260 | * version. | ||
261 | */ | ||
262 | static inline struct aa_profile *aa_newest_version(struct aa_profile *profile) | ||
263 | { | ||
264 | while (profile->replacedby) | ||
265 | profile = profile->replacedby; | ||
266 | |||
267 | return profile; | ||
268 | } | ||
269 | |||
270 | /** | ||
271 | * aa_get_profile - increment refcount on profile @p | ||
272 | * @p: profile (MAYBE NULL) | ||
273 | * | ||
274 | * Returns: pointer to @p if @p is NULL will return NULL | ||
275 | * Requires: @p must be held with valid refcount when called | ||
276 | */ | ||
277 | static inline struct aa_profile *aa_get_profile(struct aa_profile *p) | ||
278 | { | ||
279 | if (p) | ||
280 | kref_get(&(p->base.count)); | ||
281 | |||
282 | return p; | ||
283 | } | ||
284 | |||
285 | /** | ||
286 | * aa_put_profile - decrement refcount on profile @p | ||
287 | * @p: profile (MAYBE NULL) | ||
288 | */ | ||
289 | static inline void aa_put_profile(struct aa_profile *p) | ||
290 | { | ||
291 | if (p) | ||
292 | kref_put(&p->base.count, aa_free_profile_kref); | ||
293 | } | ||
294 | |||
295 | static inline int AUDIT_MODE(struct aa_profile *profile) | ||
296 | { | ||
297 | if (aa_g_audit != AUDIT_NORMAL) | ||
298 | return aa_g_audit; | ||
299 | |||
300 | return profile->audit; | ||
301 | } | ||
302 | |||
303 | bool aa_may_manage_policy(int op); | ||
304 | |||
305 | #endif /* __AA_POLICY_H */ | ||
diff --git a/security/apparmor/include/policy_unpack.h b/security/apparmor/include/policy_unpack.h new file mode 100644 index 00000000000..a2dcccac45a --- /dev/null +++ b/security/apparmor/include/policy_unpack.h | |||
@@ -0,0 +1,20 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor policy loading interface function definitions. | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #ifndef __POLICY_INTERFACE_H | ||
16 | #define __POLICY_INTERFACE_H | ||
17 | |||
18 | struct aa_profile *aa_unpack(void *udata, size_t size, const char **ns); | ||
19 | |||
20 | #endif /* __POLICY_INTERFACE_H */ | ||
diff --git a/security/apparmor/include/procattr.h b/security/apparmor/include/procattr.h new file mode 100644 index 00000000000..544aa6b766a --- /dev/null +++ b/security/apparmor/include/procattr.h | |||
@@ -0,0 +1,26 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor /proc/<pid>/attr/ interface function definitions. | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #ifndef __AA_PROCATTR_H | ||
16 | #define __AA_PROCATTR_H | ||
17 | |||
18 | #define AA_DO_TEST 1 | ||
19 | #define AA_ONEXEC 1 | ||
20 | |||
21 | int aa_getprocattr(struct aa_profile *profile, char **string); | ||
22 | int aa_setprocattr_changehat(char *args, size_t size, int test); | ||
23 | int aa_setprocattr_changeprofile(char *fqname, bool onexec, int test); | ||
24 | int aa_setprocattr_permipc(char *fqname); | ||
25 | |||
26 | #endif /* __AA_PROCATTR_H */ | ||
diff --git a/security/apparmor/include/resource.h b/security/apparmor/include/resource.h new file mode 100644 index 00000000000..3c88be94649 --- /dev/null +++ b/security/apparmor/include/resource.h | |||
@@ -0,0 +1,46 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor resource limits function definitions. | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #ifndef __AA_RESOURCE_H | ||
16 | #define __AA_RESOURCE_H | ||
17 | |||
18 | #include <linux/resource.h> | ||
19 | #include <linux/sched.h> | ||
20 | |||
21 | struct aa_profile; | ||
22 | |||
23 | /* struct aa_rlimit - rlimit settings for the profile | ||
24 | * @mask: which hard limits to set | ||
25 | * @limits: rlimit values that override task limits | ||
26 | * | ||
27 | * AppArmor rlimits are used to set confined task rlimits. Only the | ||
28 | * limits specified in @mask will be controlled by apparmor. | ||
29 | */ | ||
30 | struct aa_rlimit { | ||
31 | unsigned int mask; | ||
32 | struct rlimit limits[RLIM_NLIMITS]; | ||
33 | }; | ||
34 | |||
35 | int aa_map_resource(int resource); | ||
36 | int aa_task_setrlimit(struct aa_profile *profile, unsigned int resource, | ||
37 | struct rlimit *new_rlim); | ||
38 | |||
39 | void __aa_transition_rlimits(struct aa_profile *old, struct aa_profile *new); | ||
40 | |||
41 | static inline void aa_free_rlimit_rules(struct aa_rlimit *rlims) | ||
42 | { | ||
43 | /* NOP */ | ||
44 | } | ||
45 | |||
46 | #endif /* __AA_RESOURCE_H */ | ||
diff --git a/security/apparmor/include/sid.h b/security/apparmor/include/sid.h new file mode 100644 index 00000000000..020db35c301 --- /dev/null +++ b/security/apparmor/include/sid.h | |||
@@ -0,0 +1,24 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor security identifier (sid) definitions | ||
5 | * | ||
6 | * Copyright 2009-2010 Canonical Ltd. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License as | ||
10 | * published by the Free Software Foundation, version 2 of the | ||
11 | * License. | ||
12 | */ | ||
13 | |||
14 | #ifndef __AA_SID_H | ||
15 | #define __AA_SID_H | ||
16 | |||
17 | #include <linux/types.h> | ||
18 | |||
19 | struct aa_profile; | ||
20 | |||
21 | u32 aa_alloc_sid(void); | ||
22 | void aa_free_sid(u32 sid); | ||
23 | |||
24 | #endif /* __AA_SID_H */ | ||
diff --git a/security/apparmor/ipc.c b/security/apparmor/ipc.c new file mode 100644 index 00000000000..649fad88869 --- /dev/null +++ b/security/apparmor/ipc.c | |||
@@ -0,0 +1,114 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor ipc mediation | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #include <linux/gfp.h> | ||
16 | #include <linux/ptrace.h> | ||
17 | |||
18 | #include "include/audit.h" | ||
19 | #include "include/capability.h" | ||
20 | #include "include/context.h" | ||
21 | #include "include/policy.h" | ||
22 | |||
23 | /* call back to audit ptrace fields */ | ||
24 | static void audit_cb(struct audit_buffer *ab, void *va) | ||
25 | { | ||
26 | struct common_audit_data *sa = va; | ||
27 | audit_log_format(ab, " target="); | ||
28 | audit_log_untrustedstring(ab, sa->aad.target); | ||
29 | } | ||
30 | |||
31 | /** | ||
32 | * aa_audit_ptrace - do auditing for ptrace | ||
33 | * @profile: profile being enforced (NOT NULL) | ||
34 | * @target: profile being traced (NOT NULL) | ||
35 | * @error: error condition | ||
36 | * | ||
37 | * Returns: %0 or error code | ||
38 | */ | ||
39 | static int aa_audit_ptrace(struct aa_profile *profile, | ||
40 | struct aa_profile *target, int error) | ||
41 | { | ||
42 | struct common_audit_data sa; | ||
43 | COMMON_AUDIT_DATA_INIT(&sa, NONE); | ||
44 | sa.aad.op = OP_PTRACE; | ||
45 | sa.aad.target = target; | ||
46 | sa.aad.error = error; | ||
47 | |||
48 | return aa_audit(AUDIT_APPARMOR_AUTO, profile, GFP_ATOMIC, &sa, | ||
49 | audit_cb); | ||
50 | } | ||
51 | |||
52 | /** | ||
53 | * aa_may_ptrace - test if tracer task can trace the tracee | ||
54 | * @tracer_task: task who will do the tracing (NOT NULL) | ||
55 | * @tracer: profile of the task doing the tracing (NOT NULL) | ||
56 | * @tracee: task to be traced | ||
57 | * @mode: whether PTRACE_MODE_READ || PTRACE_MODE_ATTACH | ||
58 | * | ||
59 | * Returns: %0 else error code if permission denied or error | ||
60 | */ | ||
61 | int aa_may_ptrace(struct task_struct *tracer_task, struct aa_profile *tracer, | ||
62 | struct aa_profile *tracee, unsigned int mode) | ||
63 | { | ||
64 | /* TODO: currently only based on capability, not extended ptrace | ||
65 | * rules, | ||
66 | * Test mode for PTRACE_MODE_READ || PTRACE_MODE_ATTACH | ||
67 | */ | ||
68 | |||
69 | if (unconfined(tracer) || tracer == tracee) | ||
70 | return 0; | ||
71 | /* log this capability request */ | ||
72 | return aa_capable(tracer_task, tracer, CAP_SYS_PTRACE, 1); | ||
73 | } | ||
74 | |||
75 | /** | ||
76 | * aa_ptrace - do ptrace permission check and auditing | ||
77 | * @tracer: task doing the tracing (NOT NULL) | ||
78 | * @tracee: task being traced (NOT NULL) | ||
79 | * @mode: ptrace mode either PTRACE_MODE_READ || PTRACE_MODE_ATTACH | ||
80 | * | ||
81 | * Returns: %0 else error code if permission denied or error | ||
82 | */ | ||
83 | int aa_ptrace(struct task_struct *tracer, struct task_struct *tracee, | ||
84 | unsigned int mode) | ||
85 | { | ||
86 | /* | ||
87 | * tracer can ptrace tracee when | ||
88 | * - tracer is unconfined || | ||
89 | * - tracer is in complain mode | ||
90 | * - tracer has rules allowing it to trace tracee currently this is: | ||
91 | * - confined by the same profile || | ||
92 | * - tracer profile has CAP_SYS_PTRACE | ||
93 | */ | ||
94 | |||
95 | struct aa_profile *tracer_p; | ||
96 | /* cred released below */ | ||
97 | const struct cred *cred = get_task_cred(tracer); | ||
98 | int error = 0; | ||
99 | tracer_p = aa_cred_profile(cred); | ||
100 | |||
101 | if (!unconfined(tracer_p)) { | ||
102 | /* lcred released below */ | ||
103 | const struct cred *lcred = get_task_cred(tracee); | ||
104 | struct aa_profile *tracee_p = aa_cred_profile(lcred); | ||
105 | |||
106 | error = aa_may_ptrace(tracer, tracer_p, tracee_p, mode); | ||
107 | error = aa_audit_ptrace(tracer_p, tracee_p, error); | ||
108 | |||
109 | put_cred(lcred); | ||
110 | } | ||
111 | put_cred(cred); | ||
112 | |||
113 | return error; | ||
114 | } | ||
diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c new file mode 100644 index 00000000000..6e85cdb4303 --- /dev/null +++ b/security/apparmor/lib.c | |||
@@ -0,0 +1,133 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains basic common functions used in AppArmor | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #include <linux/slab.h> | ||
16 | #include <linux/string.h> | ||
17 | #include <linux/vmalloc.h> | ||
18 | |||
19 | #include "include/audit.h" | ||
20 | |||
21 | |||
22 | /** | ||
23 | * aa_split_fqname - split a fqname into a profile and namespace name | ||
24 | * @fqname: a full qualified name in namespace profile format (NOT NULL) | ||
25 | * @ns_name: pointer to portion of the string containing the ns name (NOT NULL) | ||
26 | * | ||
27 | * Returns: profile name or NULL if one is not specified | ||
28 | * | ||
29 | * Split a namespace name from a profile name (see policy.c for naming | ||
30 | * description). If a portion of the name is missing it returns NULL for | ||
31 | * that portion. | ||
32 | * | ||
33 | * NOTE: may modify the @fqname string. The pointers returned point | ||
34 | * into the @fqname string. | ||
35 | */ | ||
36 | char *aa_split_fqname(char *fqname, char **ns_name) | ||
37 | { | ||
38 | char *name = strim(fqname); | ||
39 | |||
40 | *ns_name = NULL; | ||
41 | if (name[0] == ':') { | ||
42 | char *split = strchr(&name[1], ':'); | ||
43 | if (split) { | ||
44 | /* overwrite ':' with \0 */ | ||
45 | *split = 0; | ||
46 | name = skip_spaces(split + 1); | ||
47 | } else | ||
48 | /* a ns name without a following profile is allowed */ | ||
49 | name = NULL; | ||
50 | *ns_name = &name[1]; | ||
51 | } | ||
52 | if (name && *name == 0) | ||
53 | name = NULL; | ||
54 | |||
55 | return name; | ||
56 | } | ||
57 | |||
58 | /** | ||
59 | * aa_info_message - log a none profile related status message | ||
60 | * @str: message to log | ||
61 | */ | ||
62 | void aa_info_message(const char *str) | ||
63 | { | ||
64 | if (audit_enabled) { | ||
65 | struct common_audit_data sa; | ||
66 | COMMON_AUDIT_DATA_INIT(&sa, NONE); | ||
67 | sa.aad.info = str; | ||
68 | aa_audit_msg(AUDIT_APPARMOR_STATUS, &sa, NULL); | ||
69 | } | ||
70 | printk(KERN_INFO "AppArmor: %s\n", str); | ||
71 | } | ||
72 | |||
73 | /** | ||
74 | * kvmalloc - do allocation preferring kmalloc but falling back to vmalloc | ||
75 | * @size: size of allocation | ||
76 | * | ||
77 | * Return: allocated buffer or NULL if failed | ||
78 | * | ||
79 | * It is possible that policy being loaded from the user is larger than | ||
80 | * what can be allocated by kmalloc, in those cases fall back to vmalloc. | ||
81 | */ | ||
82 | void *kvmalloc(size_t size) | ||
83 | { | ||
84 | void *buffer = NULL; | ||
85 | |||
86 | if (size == 0) | ||
87 | return NULL; | ||
88 | |||
89 | /* do not attempt kmalloc if we need more than 16 pages at once */ | ||
90 | if (size <= (16*PAGE_SIZE)) | ||
91 | buffer = kmalloc(size, GFP_NOIO | __GFP_NOWARN); | ||
92 | if (!buffer) { | ||
93 | /* see kvfree for why size must be at least work_struct size | ||
94 | * when allocated via vmalloc | ||
95 | */ | ||
96 | if (size < sizeof(struct work_struct)) | ||
97 | size = sizeof(struct work_struct); | ||
98 | buffer = vmalloc(size); | ||
99 | } | ||
100 | return buffer; | ||
101 | } | ||
102 | |||
103 | /** | ||
104 | * do_vfree - workqueue routine for freeing vmalloced memory | ||
105 | * @work: data to be freed | ||
106 | * | ||
107 | * The work_struct is overlaid to the data being freed, as at the point | ||
108 | * the work is scheduled the data is no longer valid, be its freeing | ||
109 | * needs to be delayed until safe. | ||
110 | */ | ||
111 | static void do_vfree(struct work_struct *work) | ||
112 | { | ||
113 | vfree(work); | ||
114 | } | ||
115 | |||
116 | /** | ||
117 | * kvfree - free an allocation do by kvmalloc | ||
118 | * @buffer: buffer to free (MAYBE_NULL) | ||
119 | * | ||
120 | * Free a buffer allocated by kvmalloc | ||
121 | */ | ||
122 | void kvfree(void *buffer) | ||
123 | { | ||
124 | if (is_vmalloc_addr(buffer)) { | ||
125 | /* Data is no longer valid so just use the allocated space | ||
126 | * as the work_struct | ||
127 | */ | ||
128 | struct work_struct *work = (struct work_struct *) buffer; | ||
129 | INIT_WORK(work, do_vfree); | ||
130 | schedule_work(work); | ||
131 | } else | ||
132 | kfree(buffer); | ||
133 | } | ||
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c new file mode 100644 index 00000000000..8db33a8b50c --- /dev/null +++ b/security/apparmor/lsm.c | |||
@@ -0,0 +1,938 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor LSM hooks. | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #include <linux/security.h> | ||
16 | #include <linux/moduleparam.h> | ||
17 | #include <linux/mm.h> | ||
18 | #include <linux/mman.h> | ||
19 | #include <linux/mount.h> | ||
20 | #include <linux/namei.h> | ||
21 | #include <linux/ptrace.h> | ||
22 | #include <linux/ctype.h> | ||
23 | #include <linux/sysctl.h> | ||
24 | #include <linux/audit.h> | ||
25 | #include <net/sock.h> | ||
26 | |||
27 | #include "include/apparmor.h" | ||
28 | #include "include/apparmorfs.h" | ||
29 | #include "include/audit.h" | ||
30 | #include "include/capability.h" | ||
31 | #include "include/context.h" | ||
32 | #include "include/file.h" | ||
33 | #include "include/ipc.h" | ||
34 | #include "include/path.h" | ||
35 | #include "include/policy.h" | ||
36 | #include "include/procattr.h" | ||
37 | |||
38 | /* Flag indicating whether initialization completed */ | ||
39 | int apparmor_initialized __initdata; | ||
40 | |||
41 | /* | ||
42 | * LSM hook functions | ||
43 | */ | ||
44 | |||
45 | /* | ||
46 | * free the associated aa_task_cxt and put its profiles | ||
47 | */ | ||
48 | static void apparmor_cred_free(struct cred *cred) | ||
49 | { | ||
50 | aa_free_task_context(cred->security); | ||
51 | cred->security = NULL; | ||
52 | } | ||
53 | |||
54 | /* | ||
55 | * allocate the apparmor part of blank credentials | ||
56 | */ | ||
57 | static int apparmor_cred_alloc_blank(struct cred *cred, gfp_t gfp) | ||
58 | { | ||
59 | /* freed by apparmor_cred_free */ | ||
60 | struct aa_task_cxt *cxt = aa_alloc_task_context(gfp); | ||
61 | if (!cxt) | ||
62 | return -ENOMEM; | ||
63 | |||
64 | cred->security = cxt; | ||
65 | return 0; | ||
66 | } | ||
67 | |||
68 | /* | ||
69 | * prepare new aa_task_cxt for modification by prepare_cred block | ||
70 | */ | ||
71 | static int apparmor_cred_prepare(struct cred *new, const struct cred *old, | ||
72 | gfp_t gfp) | ||
73 | { | ||
74 | /* freed by apparmor_cred_free */ | ||
75 | struct aa_task_cxt *cxt = aa_alloc_task_context(gfp); | ||
76 | if (!cxt) | ||
77 | return -ENOMEM; | ||
78 | |||
79 | aa_dup_task_context(cxt, old->security); | ||
80 | new->security = cxt; | ||
81 | return 0; | ||
82 | } | ||
83 | |||
84 | /* | ||
85 | * transfer the apparmor data to a blank set of creds | ||
86 | */ | ||
87 | static void apparmor_cred_transfer(struct cred *new, const struct cred *old) | ||
88 | { | ||
89 | const struct aa_task_cxt *old_cxt = old->security; | ||
90 | struct aa_task_cxt *new_cxt = new->security; | ||
91 | |||
92 | aa_dup_task_context(new_cxt, old_cxt); | ||
93 | } | ||
94 | |||
95 | static int apparmor_ptrace_access_check(struct task_struct *child, | ||
96 | unsigned int mode) | ||
97 | { | ||
98 | int error = cap_ptrace_access_check(child, mode); | ||
99 | if (error) | ||
100 | return error; | ||
101 | |||
102 | return aa_ptrace(current, child, mode); | ||
103 | } | ||
104 | |||
105 | static int apparmor_ptrace_traceme(struct task_struct *parent) | ||
106 | { | ||
107 | int error = cap_ptrace_traceme(parent); | ||
108 | if (error) | ||
109 | return error; | ||
110 | |||
111 | return aa_ptrace(parent, current, PTRACE_MODE_ATTACH); | ||
112 | } | ||
113 | |||
114 | /* Derived from security/commoncap.c:cap_capget */ | ||
115 | static int apparmor_capget(struct task_struct *target, kernel_cap_t *effective, | ||
116 | kernel_cap_t *inheritable, kernel_cap_t *permitted) | ||
117 | { | ||
118 | struct aa_profile *profile; | ||
119 | const struct cred *cred; | ||
120 | |||
121 | rcu_read_lock(); | ||
122 | cred = __task_cred(target); | ||
123 | profile = aa_cred_profile(cred); | ||
124 | |||
125 | *effective = cred->cap_effective; | ||
126 | *inheritable = cred->cap_inheritable; | ||
127 | *permitted = cred->cap_permitted; | ||
128 | |||
129 | if (!unconfined(profile)) { | ||
130 | *effective = cap_intersect(*effective, profile->caps.allow); | ||
131 | *permitted = cap_intersect(*permitted, profile->caps.allow); | ||
132 | } | ||
133 | rcu_read_unlock(); | ||
134 | |||
135 | return 0; | ||
136 | } | ||
137 | |||
138 | static int apparmor_capable(struct task_struct *task, const struct cred *cred, | ||
139 | int cap, int audit) | ||
140 | { | ||
141 | struct aa_profile *profile; | ||
142 | /* cap_capable returns 0 on success, else -EPERM */ | ||
143 | int error = cap_capable(task, cred, cap, audit); | ||
144 | if (!error) { | ||
145 | profile = aa_cred_profile(cred); | ||
146 | if (!unconfined(profile)) | ||
147 | error = aa_capable(task, profile, cap, audit); | ||
148 | } | ||
149 | return error; | ||
150 | } | ||
151 | |||
152 | /** | ||
153 | * common_perm - basic common permission check wrapper fn for paths | ||
154 | * @op: operation being checked | ||
155 | * @path: path to check permission of (NOT NULL) | ||
156 | * @mask: requested permissions mask | ||
157 | * @cond: conditional info for the permission request (NOT NULL) | ||
158 | * | ||
159 | * Returns: %0 else error code if error or permission denied | ||
160 | */ | ||
161 | static int common_perm(int op, struct path *path, u32 mask, | ||
162 | struct path_cond *cond) | ||
163 | { | ||
164 | struct aa_profile *profile; | ||
165 | int error = 0; | ||
166 | |||
167 | profile = __aa_current_profile(); | ||
168 | if (!unconfined(profile)) | ||
169 | error = aa_path_perm(op, profile, path, 0, mask, cond); | ||
170 | |||
171 | return error; | ||
172 | } | ||
173 | |||
174 | /** | ||
175 | * common_perm_dir_dentry - common permission wrapper when path is dir, dentry | ||
176 | * @op: operation being checked | ||
177 | * @dir: directory of the dentry (NOT NULL) | ||
178 | * @dentry: dentry to check (NOT NULL) | ||
179 | * @mask: requested permissions mask | ||
180 | * @cond: conditional info for the permission request (NOT NULL) | ||
181 | * | ||
182 | * Returns: %0 else error code if error or permission denied | ||
183 | */ | ||
184 | static int common_perm_dir_dentry(int op, struct path *dir, | ||
185 | struct dentry *dentry, u32 mask, | ||
186 | struct path_cond *cond) | ||
187 | { | ||
188 | struct path path = { dir->mnt, dentry }; | ||
189 | |||
190 | return common_perm(op, &path, mask, cond); | ||
191 | } | ||
192 | |||
193 | /** | ||
194 | * common_perm_mnt_dentry - common permission wrapper when mnt, dentry | ||
195 | * @op: operation being checked | ||
196 | * @mnt: mount point of dentry (NOT NULL) | ||
197 | * @dentry: dentry to check (NOT NULL) | ||
198 | * @mask: requested permissions mask | ||
199 | * | ||
200 | * Returns: %0 else error code if error or permission denied | ||
201 | */ | ||
202 | static int common_perm_mnt_dentry(int op, struct vfsmount *mnt, | ||
203 | struct dentry *dentry, u32 mask) | ||
204 | { | ||
205 | struct path path = { mnt, dentry }; | ||
206 | struct path_cond cond = { dentry->d_inode->i_uid, | ||
207 | dentry->d_inode->i_mode | ||
208 | }; | ||
209 | |||
210 | return common_perm(op, &path, mask, &cond); | ||
211 | } | ||
212 | |||
213 | /** | ||
214 | * common_perm_rm - common permission wrapper for operations doing rm | ||
215 | * @op: operation being checked | ||
216 | * @dir: directory that the dentry is in (NOT NULL) | ||
217 | * @dentry: dentry being rm'd (NOT NULL) | ||
218 | * @mask: requested permission mask | ||
219 | * | ||
220 | * Returns: %0 else error code if error or permission denied | ||
221 | */ | ||
222 | static int common_perm_rm(int op, struct path *dir, | ||
223 | struct dentry *dentry, u32 mask) | ||
224 | { | ||
225 | struct inode *inode = dentry->d_inode; | ||
226 | struct path_cond cond = { }; | ||
227 | |||
228 | if (!inode || !dir->mnt || !mediated_filesystem(inode)) | ||
229 | return 0; | ||
230 | |||
231 | cond.uid = inode->i_uid; | ||
232 | cond.mode = inode->i_mode; | ||
233 | |||
234 | return common_perm_dir_dentry(op, dir, dentry, mask, &cond); | ||
235 | } | ||
236 | |||
237 | /** | ||
238 | * common_perm_create - common permission wrapper for operations doing create | ||
239 | * @op: operation being checked | ||
240 | * @dir: directory that dentry will be created in (NOT NULL) | ||
241 | * @dentry: dentry to create (NOT NULL) | ||
242 | * @mask: request permission mask | ||
243 | * @mode: created file mode | ||
244 | * | ||
245 | * Returns: %0 else error code if error or permission denied | ||
246 | */ | ||
247 | static int common_perm_create(int op, struct path *dir, struct dentry *dentry, | ||
248 | u32 mask, umode_t mode) | ||
249 | { | ||
250 | struct path_cond cond = { current_fsuid(), mode }; | ||
251 | |||
252 | if (!dir->mnt || !mediated_filesystem(dir->dentry->d_inode)) | ||
253 | return 0; | ||
254 | |||
255 | return common_perm_dir_dentry(op, dir, dentry, mask, &cond); | ||
256 | } | ||
257 | |||
258 | static int apparmor_path_unlink(struct path *dir, struct dentry *dentry) | ||
259 | { | ||
260 | return common_perm_rm(OP_UNLINK, dir, dentry, AA_MAY_DELETE); | ||
261 | } | ||
262 | |||
263 | static int apparmor_path_mkdir(struct path *dir, struct dentry *dentry, | ||
264 | int mode) | ||
265 | { | ||
266 | return common_perm_create(OP_MKDIR, dir, dentry, AA_MAY_CREATE, | ||
267 | S_IFDIR); | ||
268 | } | ||
269 | |||
270 | static int apparmor_path_rmdir(struct path *dir, struct dentry *dentry) | ||
271 | { | ||
272 | return common_perm_rm(OP_RMDIR, dir, dentry, AA_MAY_DELETE); | ||
273 | } | ||
274 | |||
275 | static int apparmor_path_mknod(struct path *dir, struct dentry *dentry, | ||
276 | int mode, unsigned int dev) | ||
277 | { | ||
278 | return common_perm_create(OP_MKNOD, dir, dentry, AA_MAY_CREATE, mode); | ||
279 | } | ||
280 | |||
281 | static int apparmor_path_truncate(struct path *path) | ||
282 | { | ||
283 | struct path_cond cond = { path->dentry->d_inode->i_uid, | ||
284 | path->dentry->d_inode->i_mode | ||
285 | }; | ||
286 | |||
287 | if (!path->mnt || !mediated_filesystem(path->dentry->d_inode)) | ||
288 | return 0; | ||
289 | |||
290 | return common_perm(OP_TRUNC, path, MAY_WRITE | AA_MAY_META_WRITE, | ||
291 | &cond); | ||
292 | } | ||
293 | |||
294 | static int apparmor_path_symlink(struct path *dir, struct dentry *dentry, | ||
295 | const char *old_name) | ||
296 | { | ||
297 | return common_perm_create(OP_SYMLINK, dir, dentry, AA_MAY_CREATE, | ||
298 | S_IFLNK); | ||
299 | } | ||
300 | |||
301 | static int apparmor_path_link(struct dentry *old_dentry, struct path *new_dir, | ||
302 | struct dentry *new_dentry) | ||
303 | { | ||
304 | struct aa_profile *profile; | ||
305 | int error = 0; | ||
306 | |||
307 | if (!mediated_filesystem(old_dentry->d_inode)) | ||
308 | return 0; | ||
309 | |||
310 | profile = aa_current_profile(); | ||
311 | if (!unconfined(profile)) | ||
312 | error = aa_path_link(profile, old_dentry, new_dir, new_dentry); | ||
313 | return error; | ||
314 | } | ||
315 | |||
316 | static int apparmor_path_rename(struct path *old_dir, struct dentry *old_dentry, | ||
317 | struct path *new_dir, struct dentry *new_dentry) | ||
318 | { | ||
319 | struct aa_profile *profile; | ||
320 | int error = 0; | ||
321 | |||
322 | if (!mediated_filesystem(old_dentry->d_inode)) | ||
323 | return 0; | ||
324 | |||
325 | profile = aa_current_profile(); | ||
326 | if (!unconfined(profile)) { | ||
327 | struct path old_path = { old_dir->mnt, old_dentry }; | ||
328 | struct path new_path = { new_dir->mnt, new_dentry }; | ||
329 | struct path_cond cond = { old_dentry->d_inode->i_uid, | ||
330 | old_dentry->d_inode->i_mode | ||
331 | }; | ||
332 | |||
333 | error = aa_path_perm(OP_RENAME_SRC, profile, &old_path, 0, | ||
334 | MAY_READ | AA_MAY_META_READ | MAY_WRITE | | ||
335 | AA_MAY_META_WRITE | AA_MAY_DELETE, | ||
336 | &cond); | ||
337 | if (!error) | ||
338 | error = aa_path_perm(OP_RENAME_DEST, profile, &new_path, | ||
339 | 0, MAY_WRITE | AA_MAY_META_WRITE | | ||
340 | AA_MAY_CREATE, &cond); | ||
341 | |||
342 | } | ||
343 | return error; | ||
344 | } | ||
345 | |||
346 | static int apparmor_path_chmod(struct dentry *dentry, struct vfsmount *mnt, | ||
347 | mode_t mode) | ||
348 | { | ||
349 | if (!mediated_filesystem(dentry->d_inode)) | ||
350 | return 0; | ||
351 | |||
352 | return common_perm_mnt_dentry(OP_CHMOD, mnt, dentry, AA_MAY_CHMOD); | ||
353 | } | ||
354 | |||
355 | static int apparmor_path_chown(struct path *path, uid_t uid, gid_t gid) | ||
356 | { | ||
357 | struct path_cond cond = { path->dentry->d_inode->i_uid, | ||
358 | path->dentry->d_inode->i_mode | ||
359 | }; | ||
360 | |||
361 | if (!mediated_filesystem(path->dentry->d_inode)) | ||
362 | return 0; | ||
363 | |||
364 | return common_perm(OP_CHOWN, path, AA_MAY_CHOWN, &cond); | ||
365 | } | ||
366 | |||
367 | static int apparmor_inode_getattr(struct vfsmount *mnt, struct dentry *dentry) | ||
368 | { | ||
369 | if (!mediated_filesystem(dentry->d_inode)) | ||
370 | return 0; | ||
371 | |||
372 | return common_perm_mnt_dentry(OP_GETATTR, mnt, dentry, | ||
373 | AA_MAY_META_READ); | ||
374 | } | ||
375 | |||
376 | static int apparmor_dentry_open(struct file *file, const struct cred *cred) | ||
377 | { | ||
378 | struct aa_file_cxt *fcxt = file->f_security; | ||
379 | struct aa_profile *profile; | ||
380 | int error = 0; | ||
381 | |||
382 | if (!mediated_filesystem(file->f_path.dentry->d_inode)) | ||
383 | return 0; | ||
384 | |||
385 | /* If in exec, permission is handled by bprm hooks. | ||
386 | * Cache permissions granted by the previous exec check, with | ||
387 | * implicit read and executable mmap which are required to | ||
388 | * actually execute the image. | ||
389 | */ | ||
390 | if (current->in_execve) { | ||
391 | fcxt->allow = MAY_EXEC | MAY_READ | AA_EXEC_MMAP; | ||
392 | return 0; | ||
393 | } | ||
394 | |||
395 | profile = aa_cred_profile(cred); | ||
396 | if (!unconfined(profile)) { | ||
397 | struct inode *inode = file->f_path.dentry->d_inode; | ||
398 | struct path_cond cond = { inode->i_uid, inode->i_mode }; | ||
399 | |||
400 | error = aa_path_perm(OP_OPEN, profile, &file->f_path, 0, | ||
401 | aa_map_file_to_perms(file), &cond); | ||
402 | /* todo cache full allowed permissions set and state */ | ||
403 | fcxt->allow = aa_map_file_to_perms(file); | ||
404 | } | ||
405 | |||
406 | return error; | ||
407 | } | ||
408 | |||
409 | static int apparmor_file_alloc_security(struct file *file) | ||
410 | { | ||
411 | /* freed by apparmor_file_free_security */ | ||
412 | file->f_security = aa_alloc_file_context(GFP_KERNEL); | ||
413 | if (!file->f_security) | ||
414 | return -ENOMEM; | ||
415 | return 0; | ||
416 | |||
417 | } | ||
418 | |||
419 | static void apparmor_file_free_security(struct file *file) | ||
420 | { | ||
421 | struct aa_file_cxt *cxt = file->f_security; | ||
422 | |||
423 | aa_free_file_context(cxt); | ||
424 | } | ||
425 | |||
426 | static int common_file_perm(int op, struct file *file, u32 mask) | ||
427 | { | ||
428 | struct aa_file_cxt *fcxt = file->f_security; | ||
429 | struct aa_profile *profile, *fprofile = aa_cred_profile(file->f_cred); | ||
430 | int error = 0; | ||
431 | |||
432 | BUG_ON(!fprofile); | ||
433 | |||
434 | if (!file->f_path.mnt || | ||
435 | !mediated_filesystem(file->f_path.dentry->d_inode)) | ||
436 | return 0; | ||
437 | |||
438 | profile = __aa_current_profile(); | ||
439 | |||
440 | /* revalidate access, if task is unconfined, or the cached cred | ||
441 | * doesn't match or if the request is for more permissions than | ||
442 | * was granted. | ||
443 | * | ||
444 | * Note: the test for !unconfined(fprofile) is to handle file | ||
445 | * delegation from unconfined tasks | ||
446 | */ | ||
447 | if (!unconfined(profile) && !unconfined(fprofile) && | ||
448 | ((fprofile != profile) || (mask & ~fcxt->allow))) | ||
449 | error = aa_file_perm(op, profile, file, mask); | ||
450 | |||
451 | return error; | ||
452 | } | ||
453 | |||
454 | static int apparmor_file_permission(struct file *file, int mask) | ||
455 | { | ||
456 | return common_file_perm(OP_FPERM, file, mask); | ||
457 | } | ||
458 | |||
459 | static int apparmor_file_lock(struct file *file, unsigned int cmd) | ||
460 | { | ||
461 | u32 mask = AA_MAY_LOCK; | ||
462 | |||
463 | if (cmd == F_WRLCK) | ||
464 | mask |= MAY_WRITE; | ||
465 | |||
466 | return common_file_perm(OP_FLOCK, file, mask); | ||
467 | } | ||
468 | |||
469 | static int common_mmap(int op, struct file *file, unsigned long prot, | ||
470 | unsigned long flags) | ||
471 | { | ||
472 | struct dentry *dentry; | ||
473 | int mask = 0; | ||
474 | |||
475 | if (!file || !file->f_security) | ||
476 | return 0; | ||
477 | |||
478 | if (prot & PROT_READ) | ||
479 | mask |= MAY_READ; | ||
480 | /* | ||
481 | * Private mappings don't require write perms since they don't | ||
482 | * write back to the files | ||
483 | */ | ||
484 | if ((prot & PROT_WRITE) && !(flags & MAP_PRIVATE)) | ||
485 | mask |= MAY_WRITE; | ||
486 | if (prot & PROT_EXEC) | ||
487 | mask |= AA_EXEC_MMAP; | ||
488 | |||
489 | dentry = file->f_path.dentry; | ||
490 | return common_file_perm(op, file, mask); | ||
491 | } | ||
492 | |||
493 | static int apparmor_file_mmap(struct file *file, unsigned long reqprot, | ||
494 | unsigned long prot, unsigned long flags, | ||
495 | unsigned long addr, unsigned long addr_only) | ||
496 | { | ||
497 | int rc = 0; | ||
498 | |||
499 | /* do DAC check */ | ||
500 | rc = cap_file_mmap(file, reqprot, prot, flags, addr, addr_only); | ||
501 | if (rc || addr_only) | ||
502 | return rc; | ||
503 | |||
504 | return common_mmap(OP_FMMAP, file, prot, flags); | ||
505 | } | ||
506 | |||
507 | static int apparmor_file_mprotect(struct vm_area_struct *vma, | ||
508 | unsigned long reqprot, unsigned long prot) | ||
509 | { | ||
510 | return common_mmap(OP_FMPROT, vma->vm_file, prot, | ||
511 | !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0); | ||
512 | } | ||
513 | |||
514 | static int apparmor_getprocattr(struct task_struct *task, char *name, | ||
515 | char **value) | ||
516 | { | ||
517 | int error = -ENOENT; | ||
518 | struct aa_profile *profile; | ||
519 | /* released below */ | ||
520 | const struct cred *cred = get_task_cred(task); | ||
521 | struct aa_task_cxt *cxt = cred->security; | ||
522 | profile = aa_cred_profile(cred); | ||
523 | |||
524 | if (strcmp(name, "current") == 0) | ||
525 | error = aa_getprocattr(aa_newest_version(cxt->profile), | ||
526 | value); | ||
527 | else if (strcmp(name, "prev") == 0 && cxt->previous) | ||
528 | error = aa_getprocattr(aa_newest_version(cxt->previous), | ||
529 | value); | ||
530 | else if (strcmp(name, "exec") == 0 && cxt->onexec) | ||
531 | error = aa_getprocattr(aa_newest_version(cxt->onexec), | ||
532 | value); | ||
533 | else | ||
534 | error = -EINVAL; | ||
535 | |||
536 | put_cred(cred); | ||
537 | |||
538 | return error; | ||
539 | } | ||
540 | |||
541 | static int apparmor_setprocattr(struct task_struct *task, char *name, | ||
542 | void *value, size_t size) | ||
543 | { | ||
544 | char *command, *args = value; | ||
545 | size_t arg_size; | ||
546 | int error; | ||
547 | |||
548 | if (size == 0) | ||
549 | return -EINVAL; | ||
550 | /* args points to a PAGE_SIZE buffer, AppArmor requires that | ||
551 | * the buffer must be null terminated or have size <= PAGE_SIZE -1 | ||
552 | * so that AppArmor can null terminate them | ||
553 | */ | ||
554 | if (args[size - 1] != '\0') { | ||
555 | if (size == PAGE_SIZE) | ||
556 | return -EINVAL; | ||
557 | args[size] = '\0'; | ||
558 | } | ||
559 | |||
560 | /* task can only write its own attributes */ | ||
561 | if (current != task) | ||
562 | return -EACCES; | ||
563 | |||
564 | args = value; | ||
565 | args = strim(args); | ||
566 | command = strsep(&args, " "); | ||
567 | if (!args) | ||
568 | return -EINVAL; | ||
569 | args = skip_spaces(args); | ||
570 | if (!*args) | ||
571 | return -EINVAL; | ||
572 | |||
573 | arg_size = size - (args - (char *) value); | ||
574 | if (strcmp(name, "current") == 0) { | ||
575 | if (strcmp(command, "changehat") == 0) { | ||
576 | error = aa_setprocattr_changehat(args, arg_size, | ||
577 | !AA_DO_TEST); | ||
578 | } else if (strcmp(command, "permhat") == 0) { | ||
579 | error = aa_setprocattr_changehat(args, arg_size, | ||
580 | AA_DO_TEST); | ||
581 | } else if (strcmp(command, "changeprofile") == 0) { | ||
582 | error = aa_setprocattr_changeprofile(args, !AA_ONEXEC, | ||
583 | !AA_DO_TEST); | ||
584 | } else if (strcmp(command, "permprofile") == 0) { | ||
585 | error = aa_setprocattr_changeprofile(args, !AA_ONEXEC, | ||
586 | AA_DO_TEST); | ||
587 | } else if (strcmp(command, "permipc") == 0) { | ||
588 | error = aa_setprocattr_permipc(args); | ||
589 | } else { | ||
590 | struct common_audit_data sa; | ||
591 | COMMON_AUDIT_DATA_INIT(&sa, NONE); | ||
592 | sa.aad.op = OP_SETPROCATTR; | ||
593 | sa.aad.info = name; | ||
594 | sa.aad.error = -EINVAL; | ||
595 | return aa_audit(AUDIT_APPARMOR_DENIED, NULL, GFP_KERNEL, | ||
596 | &sa, NULL); | ||
597 | } | ||
598 | } else if (strcmp(name, "exec") == 0) { | ||
599 | error = aa_setprocattr_changeprofile(args, AA_ONEXEC, | ||
600 | !AA_DO_TEST); | ||
601 | } else { | ||
602 | /* only support the "current" and "exec" process attributes */ | ||
603 | return -EINVAL; | ||
604 | } | ||
605 | if (!error) | ||
606 | error = size; | ||
607 | return error; | ||
608 | } | ||
609 | |||
610 | static int apparmor_task_setrlimit(unsigned int resource, | ||
611 | struct rlimit *new_rlim) | ||
612 | { | ||
613 | struct aa_profile *profile = aa_current_profile(); | ||
614 | int error = 0; | ||
615 | |||
616 | if (!unconfined(profile)) | ||
617 | error = aa_task_setrlimit(profile, resource, new_rlim); | ||
618 | |||
619 | return error; | ||
620 | } | ||
621 | |||
622 | static struct security_operations apparmor_ops = { | ||
623 | .name = "apparmor", | ||
624 | |||
625 | .ptrace_access_check = apparmor_ptrace_access_check, | ||
626 | .ptrace_traceme = apparmor_ptrace_traceme, | ||
627 | .capget = apparmor_capget, | ||
628 | .capable = apparmor_capable, | ||
629 | |||
630 | .path_link = apparmor_path_link, | ||
631 | .path_unlink = apparmor_path_unlink, | ||
632 | .path_symlink = apparmor_path_symlink, | ||
633 | .path_mkdir = apparmor_path_mkdir, | ||
634 | .path_rmdir = apparmor_path_rmdir, | ||
635 | .path_mknod = apparmor_path_mknod, | ||
636 | .path_rename = apparmor_path_rename, | ||
637 | .path_chmod = apparmor_path_chmod, | ||
638 | .path_chown = apparmor_path_chown, | ||
639 | .path_truncate = apparmor_path_truncate, | ||
640 | .dentry_open = apparmor_dentry_open, | ||
641 | .inode_getattr = apparmor_inode_getattr, | ||
642 | |||
643 | .file_permission = apparmor_file_permission, | ||
644 | .file_alloc_security = apparmor_file_alloc_security, | ||
645 | .file_free_security = apparmor_file_free_security, | ||
646 | .file_mmap = apparmor_file_mmap, | ||
647 | .file_mprotect = apparmor_file_mprotect, | ||
648 | .file_lock = apparmor_file_lock, | ||
649 | |||
650 | .getprocattr = apparmor_getprocattr, | ||
651 | .setprocattr = apparmor_setprocattr, | ||
652 | |||
653 | .cred_alloc_blank = apparmor_cred_alloc_blank, | ||
654 | .cred_free = apparmor_cred_free, | ||
655 | .cred_prepare = apparmor_cred_prepare, | ||
656 | .cred_transfer = apparmor_cred_transfer, | ||
657 | |||
658 | .bprm_set_creds = apparmor_bprm_set_creds, | ||
659 | .bprm_committing_creds = apparmor_bprm_committing_creds, | ||
660 | .bprm_committed_creds = apparmor_bprm_committed_creds, | ||
661 | .bprm_secureexec = apparmor_bprm_secureexec, | ||
662 | |||
663 | .task_setrlimit = apparmor_task_setrlimit, | ||
664 | }; | ||
665 | |||
666 | /* | ||
667 | * AppArmor sysfs module parameters | ||
668 | */ | ||
669 | |||
670 | static int param_set_aabool(const char *val, struct kernel_param *kp); | ||
671 | static int param_get_aabool(char *buffer, struct kernel_param *kp); | ||
672 | #define param_check_aabool(name, p) __param_check(name, p, int) | ||
673 | |||
674 | static int param_set_aauint(const char *val, struct kernel_param *kp); | ||
675 | static int param_get_aauint(char *buffer, struct kernel_param *kp); | ||
676 | #define param_check_aauint(name, p) __param_check(name, p, int) | ||
677 | |||
678 | static int param_set_aalockpolicy(const char *val, struct kernel_param *kp); | ||
679 | static int param_get_aalockpolicy(char *buffer, struct kernel_param *kp); | ||
680 | #define param_check_aalockpolicy(name, p) __param_check(name, p, int) | ||
681 | |||
682 | static int param_set_audit(const char *val, struct kernel_param *kp); | ||
683 | static int param_get_audit(char *buffer, struct kernel_param *kp); | ||
684 | #define param_check_audit(name, p) __param_check(name, p, int) | ||
685 | |||
686 | static int param_set_mode(const char *val, struct kernel_param *kp); | ||
687 | static int param_get_mode(char *buffer, struct kernel_param *kp); | ||
688 | #define param_check_mode(name, p) __param_check(name, p, int) | ||
689 | |||
690 | /* Flag values, also controllable via /sys/module/apparmor/parameters | ||
691 | * We define special types as we want to do additional mediation. | ||
692 | */ | ||
693 | |||
694 | /* AppArmor global enforcement switch - complain, enforce, kill */ | ||
695 | enum profile_mode aa_g_profile_mode = APPARMOR_ENFORCE; | ||
696 | module_param_call(mode, param_set_mode, param_get_mode, | ||
697 | &aa_g_profile_mode, S_IRUSR | S_IWUSR); | ||
698 | |||
699 | /* Debug mode */ | ||
700 | int aa_g_debug; | ||
701 | module_param_named(debug, aa_g_debug, aabool, S_IRUSR | S_IWUSR); | ||
702 | |||
703 | /* Audit mode */ | ||
704 | enum audit_mode aa_g_audit; | ||
705 | module_param_call(audit, param_set_audit, param_get_audit, | ||
706 | &aa_g_audit, S_IRUSR | S_IWUSR); | ||
707 | |||
708 | /* Determines if audit header is included in audited messages. This | ||
709 | * provides more context if the audit daemon is not running | ||
710 | */ | ||
711 | int aa_g_audit_header = 1; | ||
712 | module_param_named(audit_header, aa_g_audit_header, aabool, | ||
713 | S_IRUSR | S_IWUSR); | ||
714 | |||
715 | /* lock out loading/removal of policy | ||
716 | * TODO: add in at boot loading of policy, which is the only way to | ||
717 | * load policy, if lock_policy is set | ||
718 | */ | ||
719 | int aa_g_lock_policy; | ||
720 | module_param_named(lock_policy, aa_g_lock_policy, aalockpolicy, | ||
721 | S_IRUSR | S_IWUSR); | ||
722 | |||
723 | /* Syscall logging mode */ | ||
724 | int aa_g_logsyscall; | ||
725 | module_param_named(logsyscall, aa_g_logsyscall, aabool, S_IRUSR | S_IWUSR); | ||
726 | |||
727 | /* Maximum pathname length before accesses will start getting rejected */ | ||
728 | unsigned int aa_g_path_max = 2 * PATH_MAX; | ||
729 | module_param_named(path_max, aa_g_path_max, aauint, S_IRUSR | S_IWUSR); | ||
730 | |||
731 | /* Determines how paranoid loading of policy is and how much verification | ||
732 | * on the loaded policy is done. | ||
733 | */ | ||
734 | int aa_g_paranoid_load = 1; | ||
735 | module_param_named(paranoid_load, aa_g_paranoid_load, aabool, | ||
736 | S_IRUSR | S_IWUSR); | ||
737 | |||
738 | /* Boot time disable flag */ | ||
739 | static unsigned int apparmor_enabled = CONFIG_SECURITY_APPARMOR_BOOTPARAM_VALUE; | ||
740 | module_param_named(enabled, apparmor_enabled, aabool, S_IRUSR); | ||
741 | |||
742 | static int __init apparmor_enabled_setup(char *str) | ||
743 | { | ||
744 | unsigned long enabled; | ||
745 | int error = strict_strtoul(str, 0, &enabled); | ||
746 | if (!error) | ||
747 | apparmor_enabled = enabled ? 1 : 0; | ||
748 | return 1; | ||
749 | } | ||
750 | |||
751 | __setup("apparmor=", apparmor_enabled_setup); | ||
752 | |||
753 | /* set global flag turning off the ability to load policy */ | ||
754 | static int param_set_aalockpolicy(const char *val, struct kernel_param *kp) | ||
755 | { | ||
756 | if (!capable(CAP_MAC_ADMIN)) | ||
757 | return -EPERM; | ||
758 | if (aa_g_lock_policy) | ||
759 | return -EACCES; | ||
760 | return param_set_bool(val, kp); | ||
761 | } | ||
762 | |||
763 | static int param_get_aalockpolicy(char *buffer, struct kernel_param *kp) | ||
764 | { | ||
765 | if (!capable(CAP_MAC_ADMIN)) | ||
766 | return -EPERM; | ||
767 | return param_get_bool(buffer, kp); | ||
768 | } | ||
769 | |||
770 | static int param_set_aabool(const char *val, struct kernel_param *kp) | ||
771 | { | ||
772 | if (!capable(CAP_MAC_ADMIN)) | ||
773 | return -EPERM; | ||
774 | return param_set_bool(val, kp); | ||
775 | } | ||
776 | |||
777 | static int param_get_aabool(char *buffer, struct kernel_param *kp) | ||
778 | { | ||
779 | if (!capable(CAP_MAC_ADMIN)) | ||
780 | return -EPERM; | ||
781 | return param_get_bool(buffer, kp); | ||
782 | } | ||
783 | |||
784 | static int param_set_aauint(const char *val, struct kernel_param *kp) | ||
785 | { | ||
786 | if (!capable(CAP_MAC_ADMIN)) | ||
787 | return -EPERM; | ||
788 | return param_set_uint(val, kp); | ||
789 | } | ||
790 | |||
791 | static int param_get_aauint(char *buffer, struct kernel_param *kp) | ||
792 | { | ||
793 | if (!capable(CAP_MAC_ADMIN)) | ||
794 | return -EPERM; | ||
795 | return param_get_uint(buffer, kp); | ||
796 | } | ||
797 | |||
798 | static int param_get_audit(char *buffer, struct kernel_param *kp) | ||
799 | { | ||
800 | if (!capable(CAP_MAC_ADMIN)) | ||
801 | return -EPERM; | ||
802 | |||
803 | if (!apparmor_enabled) | ||
804 | return -EINVAL; | ||
805 | |||
806 | return sprintf(buffer, "%s", audit_mode_names[aa_g_audit]); | ||
807 | } | ||
808 | |||
809 | static int param_set_audit(const char *val, struct kernel_param *kp) | ||
810 | { | ||
811 | int i; | ||
812 | if (!capable(CAP_MAC_ADMIN)) | ||
813 | return -EPERM; | ||
814 | |||
815 | if (!apparmor_enabled) | ||
816 | return -EINVAL; | ||
817 | |||
818 | if (!val) | ||
819 | return -EINVAL; | ||
820 | |||
821 | for (i = 0; i < AUDIT_MAX_INDEX; i++) { | ||
822 | if (strcmp(val, audit_mode_names[i]) == 0) { | ||
823 | aa_g_audit = i; | ||
824 | return 0; | ||
825 | } | ||
826 | } | ||
827 | |||
828 | return -EINVAL; | ||
829 | } | ||
830 | |||
831 | static int param_get_mode(char *buffer, struct kernel_param *kp) | ||
832 | { | ||
833 | if (!capable(CAP_MAC_ADMIN)) | ||
834 | return -EPERM; | ||
835 | |||
836 | if (!apparmor_enabled) | ||
837 | return -EINVAL; | ||
838 | |||
839 | return sprintf(buffer, "%s", profile_mode_names[aa_g_profile_mode]); | ||
840 | } | ||
841 | |||
842 | static int param_set_mode(const char *val, struct kernel_param *kp) | ||
843 | { | ||
844 | int i; | ||
845 | if (!capable(CAP_MAC_ADMIN)) | ||
846 | return -EPERM; | ||
847 | |||
848 | if (!apparmor_enabled) | ||
849 | return -EINVAL; | ||
850 | |||
851 | if (!val) | ||
852 | return -EINVAL; | ||
853 | |||
854 | for (i = 0; i < APPARMOR_NAMES_MAX_INDEX; i++) { | ||
855 | if (strcmp(val, profile_mode_names[i]) == 0) { | ||
856 | aa_g_profile_mode = i; | ||
857 | return 0; | ||
858 | } | ||
859 | } | ||
860 | |||
861 | return -EINVAL; | ||
862 | } | ||
863 | |||
864 | /* | ||
865 | * AppArmor init functions | ||
866 | */ | ||
867 | |||
868 | /** | ||
869 | * set_init_cxt - set a task context and profile on the first task. | ||
870 | * | ||
871 | * TODO: allow setting an alternate profile than unconfined | ||
872 | */ | ||
873 | static int __init set_init_cxt(void) | ||
874 | { | ||
875 | struct cred *cred = (struct cred *)current->real_cred; | ||
876 | struct aa_task_cxt *cxt; | ||
877 | |||
878 | cxt = aa_alloc_task_context(GFP_KERNEL); | ||
879 | if (!cxt) | ||
880 | return -ENOMEM; | ||
881 | |||
882 | cxt->profile = aa_get_profile(root_ns->unconfined); | ||
883 | cred->security = cxt; | ||
884 | |||
885 | return 0; | ||
886 | } | ||
887 | |||
888 | static int __init apparmor_init(void) | ||
889 | { | ||
890 | int error; | ||
891 | |||
892 | if (!apparmor_enabled || !security_module_enable(&apparmor_ops)) { | ||
893 | aa_info_message("AppArmor disabled by boot time parameter"); | ||
894 | apparmor_enabled = 0; | ||
895 | return 0; | ||
896 | } | ||
897 | |||
898 | error = aa_alloc_root_ns(); | ||
899 | if (error) { | ||
900 | AA_ERROR("Unable to allocate default profile namespace\n"); | ||
901 | goto alloc_out; | ||
902 | } | ||
903 | |||
904 | error = set_init_cxt(); | ||
905 | if (error) { | ||
906 | AA_ERROR("Failed to set context on init task\n"); | ||
907 | goto register_security_out; | ||
908 | } | ||
909 | |||
910 | error = register_security(&apparmor_ops); | ||
911 | if (error) { | ||
912 | AA_ERROR("Unable to register AppArmor\n"); | ||
913 | goto register_security_out; | ||
914 | } | ||
915 | |||
916 | /* Report that AppArmor successfully initialized */ | ||
917 | apparmor_initialized = 1; | ||
918 | if (aa_g_profile_mode == APPARMOR_COMPLAIN) | ||
919 | aa_info_message("AppArmor initialized: complain mode enabled"); | ||
920 | else if (aa_g_profile_mode == APPARMOR_KILL) | ||
921 | aa_info_message("AppArmor initialized: kill mode enabled"); | ||
922 | else | ||
923 | aa_info_message("AppArmor initialized"); | ||
924 | |||
925 | return error; | ||
926 | |||
927 | register_security_out: | ||
928 | aa_free_root_ns(); | ||
929 | |||
930 | alloc_out: | ||
931 | aa_destroy_aafs(); | ||
932 | |||
933 | apparmor_enabled = 0; | ||
934 | return error; | ||
935 | |||
936 | } | ||
937 | |||
938 | security_initcall(apparmor_init); | ||
diff --git a/security/apparmor/match.c b/security/apparmor/match.c new file mode 100644 index 00000000000..5cb4dc1f699 --- /dev/null +++ b/security/apparmor/match.c | |||
@@ -0,0 +1,353 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor dfa based regular expression matching engine | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #include <linux/errno.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/mm.h> | ||
18 | #include <linux/slab.h> | ||
19 | #include <linux/vmalloc.h> | ||
20 | #include <linux/err.h> | ||
21 | #include <linux/kref.h> | ||
22 | |||
23 | #include "include/apparmor.h" | ||
24 | #include "include/match.h" | ||
25 | |||
26 | /** | ||
27 | * unpack_table - unpack a dfa table (one of accept, default, base, next check) | ||
28 | * @blob: data to unpack (NOT NULL) | ||
29 | * @bsize: size of blob | ||
30 | * | ||
31 | * Returns: pointer to table else NULL on failure | ||
32 | * | ||
33 | * NOTE: must be freed by kvfree (not kmalloc) | ||
34 | */ | ||
35 | static struct table_header *unpack_table(char *blob, size_t bsize) | ||
36 | { | ||
37 | struct table_header *table = NULL; | ||
38 | struct table_header th; | ||
39 | size_t tsize; | ||
40 | |||
41 | if (bsize < sizeof(struct table_header)) | ||
42 | goto out; | ||
43 | |||
44 | /* loaded td_id's start at 1, subtract 1 now to avoid doing | ||
45 | * it every time we use td_id as an index | ||
46 | */ | ||
47 | th.td_id = be16_to_cpu(*(u16 *) (blob)) - 1; | ||
48 | th.td_flags = be16_to_cpu(*(u16 *) (blob + 2)); | ||
49 | th.td_lolen = be32_to_cpu(*(u32 *) (blob + 8)); | ||
50 | blob += sizeof(struct table_header); | ||
51 | |||
52 | if (!(th.td_flags == YYTD_DATA16 || th.td_flags == YYTD_DATA32 || | ||
53 | th.td_flags == YYTD_DATA8)) | ||
54 | goto out; | ||
55 | |||
56 | tsize = table_size(th.td_lolen, th.td_flags); | ||
57 | if (bsize < tsize) | ||
58 | goto out; | ||
59 | |||
60 | table = kvmalloc(tsize); | ||
61 | if (table) { | ||
62 | *table = th; | ||
63 | if (th.td_flags == YYTD_DATA8) | ||
64 | UNPACK_ARRAY(table->td_data, blob, th.td_lolen, | ||
65 | u8, byte_to_byte); | ||
66 | else if (th.td_flags == YYTD_DATA16) | ||
67 | UNPACK_ARRAY(table->td_data, blob, th.td_lolen, | ||
68 | u16, be16_to_cpu); | ||
69 | else if (th.td_flags == YYTD_DATA32) | ||
70 | UNPACK_ARRAY(table->td_data, blob, th.td_lolen, | ||
71 | u32, be32_to_cpu); | ||
72 | else | ||
73 | goto fail; | ||
74 | } | ||
75 | |||
76 | out: | ||
77 | /* if table was vmalloced make sure the page tables are synced | ||
78 | * before it is used, as it goes live to all cpus. | ||
79 | */ | ||
80 | if (is_vmalloc_addr(table)) | ||
81 | vm_unmap_aliases(); | ||
82 | return table; | ||
83 | fail: | ||
84 | kvfree(table); | ||
85 | return NULL; | ||
86 | } | ||
87 | |||
88 | /** | ||
89 | * verify_dfa - verify that transitions and states in the tables are in bounds. | ||
90 | * @dfa: dfa to test (NOT NULL) | ||
91 | * @flags: flags controlling what type of accept table are acceptable | ||
92 | * | ||
93 | * Assumes dfa has gone through the first pass verification done by unpacking | ||
94 | * NOTE: this does not valid accept table values | ||
95 | * | ||
96 | * Returns: %0 else error code on failure to verify | ||
97 | */ | ||
98 | static int verify_dfa(struct aa_dfa *dfa, int flags) | ||
99 | { | ||
100 | size_t i, state_count, trans_count; | ||
101 | int error = -EPROTO; | ||
102 | |||
103 | /* check that required tables exist */ | ||
104 | if (!(dfa->tables[YYTD_ID_DEF] && | ||
105 | dfa->tables[YYTD_ID_BASE] && | ||
106 | dfa->tables[YYTD_ID_NXT] && dfa->tables[YYTD_ID_CHK])) | ||
107 | goto out; | ||
108 | |||
109 | /* accept.size == default.size == base.size */ | ||
110 | state_count = dfa->tables[YYTD_ID_BASE]->td_lolen; | ||
111 | if (ACCEPT1_FLAGS(flags)) { | ||
112 | if (!dfa->tables[YYTD_ID_ACCEPT]) | ||
113 | goto out; | ||
114 | if (state_count != dfa->tables[YYTD_ID_ACCEPT]->td_lolen) | ||
115 | goto out; | ||
116 | } | ||
117 | if (ACCEPT2_FLAGS(flags)) { | ||
118 | if (!dfa->tables[YYTD_ID_ACCEPT2]) | ||
119 | goto out; | ||
120 | if (state_count != dfa->tables[YYTD_ID_ACCEPT2]->td_lolen) | ||
121 | goto out; | ||
122 | } | ||
123 | if (state_count != dfa->tables[YYTD_ID_DEF]->td_lolen) | ||
124 | goto out; | ||
125 | |||
126 | /* next.size == chk.size */ | ||
127 | trans_count = dfa->tables[YYTD_ID_NXT]->td_lolen; | ||
128 | if (trans_count != dfa->tables[YYTD_ID_CHK]->td_lolen) | ||
129 | goto out; | ||
130 | |||
131 | /* if equivalence classes then its table size must be 256 */ | ||
132 | if (dfa->tables[YYTD_ID_EC] && | ||
133 | dfa->tables[YYTD_ID_EC]->td_lolen != 256) | ||
134 | goto out; | ||
135 | |||
136 | if (flags & DFA_FLAG_VERIFY_STATES) { | ||
137 | for (i = 0; i < state_count; i++) { | ||
138 | if (DEFAULT_TABLE(dfa)[i] >= state_count) | ||
139 | goto out; | ||
140 | /* TODO: do check that DEF state recursion terminates */ | ||
141 | if (BASE_TABLE(dfa)[i] + 255 >= trans_count) { | ||
142 | printk(KERN_ERR "AppArmor DFA next/check upper " | ||
143 | "bounds error\n"); | ||
144 | goto out; | ||
145 | } | ||
146 | } | ||
147 | |||
148 | for (i = 0; i < trans_count; i++) { | ||
149 | if (NEXT_TABLE(dfa)[i] >= state_count) | ||
150 | goto out; | ||
151 | if (CHECK_TABLE(dfa)[i] >= state_count) | ||
152 | goto out; | ||
153 | } | ||
154 | } | ||
155 | |||
156 | error = 0; | ||
157 | out: | ||
158 | return error; | ||
159 | } | ||
160 | |||
161 | /** | ||
162 | * dfa_free - free a dfa allocated by aa_dfa_unpack | ||
163 | * @dfa: the dfa to free (MAYBE NULL) | ||
164 | * | ||
165 | * Requires: reference count to dfa == 0 | ||
166 | */ | ||
167 | static void dfa_free(struct aa_dfa *dfa) | ||
168 | { | ||
169 | if (dfa) { | ||
170 | int i; | ||
171 | |||
172 | for (i = 0; i < ARRAY_SIZE(dfa->tables); i++) { | ||
173 | kvfree(dfa->tables[i]); | ||
174 | dfa->tables[i] = NULL; | ||
175 | } | ||
176 | kfree(dfa); | ||
177 | } | ||
178 | } | ||
179 | |||
180 | /** | ||
181 | * aa_dfa_free_kref - free aa_dfa by kref (called by aa_put_dfa) | ||
182 | * @kr: kref callback for freeing of a dfa (NOT NULL) | ||
183 | */ | ||
184 | void aa_dfa_free_kref(struct kref *kref) | ||
185 | { | ||
186 | struct aa_dfa *dfa = container_of(kref, struct aa_dfa, count); | ||
187 | dfa_free(dfa); | ||
188 | } | ||
189 | |||
190 | /** | ||
191 | * aa_dfa_unpack - unpack the binary tables of a serialized dfa | ||
192 | * @blob: aligned serialized stream of data to unpack (NOT NULL) | ||
193 | * @size: size of data to unpack | ||
194 | * @flags: flags controlling what type of accept tables are acceptable | ||
195 | * | ||
196 | * Unpack a dfa that has been serialized. To find information on the dfa | ||
197 | * format look in Documentation/apparmor.txt | ||
198 | * Assumes the dfa @blob stream has been aligned on a 8 byte boundry | ||
199 | * | ||
200 | * Returns: an unpacked dfa ready for matching or ERR_PTR on failure | ||
201 | */ | ||
202 | struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags) | ||
203 | { | ||
204 | int hsize; | ||
205 | int error = -ENOMEM; | ||
206 | char *data = blob; | ||
207 | struct table_header *table = NULL; | ||
208 | struct aa_dfa *dfa = kzalloc(sizeof(struct aa_dfa), GFP_KERNEL); | ||
209 | if (!dfa) | ||
210 | goto fail; | ||
211 | |||
212 | kref_init(&dfa->count); | ||
213 | |||
214 | error = -EPROTO; | ||
215 | |||
216 | /* get dfa table set header */ | ||
217 | if (size < sizeof(struct table_set_header)) | ||
218 | goto fail; | ||
219 | |||
220 | if (ntohl(*(u32 *) data) != YYTH_MAGIC) | ||
221 | goto fail; | ||
222 | |||
223 | hsize = ntohl(*(u32 *) (data + 4)); | ||
224 | if (size < hsize) | ||
225 | goto fail; | ||
226 | |||
227 | dfa->flags = ntohs(*(u16 *) (data + 12)); | ||
228 | data += hsize; | ||
229 | size -= hsize; | ||
230 | |||
231 | while (size > 0) { | ||
232 | table = unpack_table(data, size); | ||
233 | if (!table) | ||
234 | goto fail; | ||
235 | |||
236 | switch (table->td_id) { | ||
237 | case YYTD_ID_ACCEPT: | ||
238 | if (!(table->td_flags & ACCEPT1_FLAGS(flags))) | ||
239 | goto fail; | ||
240 | break; | ||
241 | case YYTD_ID_ACCEPT2: | ||
242 | if (!(table->td_flags & ACCEPT2_FLAGS(flags))) | ||
243 | goto fail; | ||
244 | break; | ||
245 | case YYTD_ID_BASE: | ||
246 | if (table->td_flags != YYTD_DATA32) | ||
247 | goto fail; | ||
248 | break; | ||
249 | case YYTD_ID_DEF: | ||
250 | case YYTD_ID_NXT: | ||
251 | case YYTD_ID_CHK: | ||
252 | if (table->td_flags != YYTD_DATA16) | ||
253 | goto fail; | ||
254 | break; | ||
255 | case YYTD_ID_EC: | ||
256 | if (table->td_flags != YYTD_DATA8) | ||
257 | goto fail; | ||
258 | break; | ||
259 | default: | ||
260 | goto fail; | ||
261 | } | ||
262 | /* check for duplicate table entry */ | ||
263 | if (dfa->tables[table->td_id]) | ||
264 | goto fail; | ||
265 | dfa->tables[table->td_id] = table; | ||
266 | data += table_size(table->td_lolen, table->td_flags); | ||
267 | size -= table_size(table->td_lolen, table->td_flags); | ||
268 | table = NULL; | ||
269 | } | ||
270 | |||
271 | error = verify_dfa(dfa, flags); | ||
272 | if (error) | ||
273 | goto fail; | ||
274 | |||
275 | return dfa; | ||
276 | |||
277 | fail: | ||
278 | kvfree(table); | ||
279 | dfa_free(dfa); | ||
280 | return ERR_PTR(error); | ||
281 | } | ||
282 | |||
283 | /** | ||
284 | * aa_dfa_match_len - traverse @dfa to find state @str stops at | ||
285 | * @dfa: the dfa to match @str against (NOT NULL) | ||
286 | * @start: the state of the dfa to start matching in | ||
287 | * @str: the string of bytes to match against the dfa (NOT NULL) | ||
288 | * @len: length of the string of bytes to match | ||
289 | * | ||
290 | * aa_dfa_match_len will match @str against the dfa and return the state it | ||
291 | * finished matching in. The final state can be used to look up the accepting | ||
292 | * label, or as the start state of a continuing match. | ||
293 | * | ||
294 | * This function will happily match again the 0 byte and only finishes | ||
295 | * when @len input is consumed. | ||
296 | * | ||
297 | * Returns: final state reached after input is consumed | ||
298 | */ | ||
299 | unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start, | ||
300 | const char *str, int len) | ||
301 | { | ||
302 | u16 *def = DEFAULT_TABLE(dfa); | ||
303 | u32 *base = BASE_TABLE(dfa); | ||
304 | u16 *next = NEXT_TABLE(dfa); | ||
305 | u16 *check = CHECK_TABLE(dfa); | ||
306 | unsigned int state = start, pos; | ||
307 | |||
308 | if (state == 0) | ||
309 | return 0; | ||
310 | |||
311 | /* current state is <state>, matching character *str */ | ||
312 | if (dfa->tables[YYTD_ID_EC]) { | ||
313 | /* Equivalence class table defined */ | ||
314 | u8 *equiv = EQUIV_TABLE(dfa); | ||
315 | /* default is direct to next state */ | ||
316 | for (; len; len--) { | ||
317 | pos = base[state] + equiv[(u8) *str++]; | ||
318 | if (check[pos] == state) | ||
319 | state = next[pos]; | ||
320 | else | ||
321 | state = def[state]; | ||
322 | } | ||
323 | } else { | ||
324 | /* default is direct to next state */ | ||
325 | for (; len; len--) { | ||
326 | pos = base[state] + (u8) *str++; | ||
327 | if (check[pos] == state) | ||
328 | state = next[pos]; | ||
329 | else | ||
330 | state = def[state]; | ||
331 | } | ||
332 | } | ||
333 | |||
334 | return state; | ||
335 | } | ||
336 | |||
337 | /** | ||
338 | * aa_dfa_next_state - traverse @dfa to find state @str stops at | ||
339 | * @dfa: the dfa to match @str against (NOT NULL) | ||
340 | * @start: the state of the dfa to start matching in | ||
341 | * @str: the null terminated string of bytes to match against the dfa (NOT NULL) | ||
342 | * | ||
343 | * aa_dfa_next_state will match @str against the dfa and return the state it | ||
344 | * finished matching in. The final state can be used to look up the accepting | ||
345 | * label, or as the start state of a continuing match. | ||
346 | * | ||
347 | * Returns: final state reached after input is consumed | ||
348 | */ | ||
349 | unsigned int aa_dfa_match(struct aa_dfa *dfa, unsigned int start, | ||
350 | const char *str) | ||
351 | { | ||
352 | return aa_dfa_match_len(dfa, start, str, strlen(str)); | ||
353 | } | ||
diff --git a/security/apparmor/path.c b/security/apparmor/path.c new file mode 100644 index 00000000000..96bab9469d4 --- /dev/null +++ b/security/apparmor/path.c | |||
@@ -0,0 +1,235 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor function for pathnames | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #include <linux/magic.h> | ||
16 | #include <linux/mnt_namespace.h> | ||
17 | #include <linux/mount.h> | ||
18 | #include <linux/namei.h> | ||
19 | #include <linux/nsproxy.h> | ||
20 | #include <linux/path.h> | ||
21 | #include <linux/sched.h> | ||
22 | #include <linux/slab.h> | ||
23 | #include <linux/fs_struct.h> | ||
24 | |||
25 | #include "include/apparmor.h" | ||
26 | #include "include/path.h" | ||
27 | #include "include/policy.h" | ||
28 | |||
29 | |||
30 | /* modified from dcache.c */ | ||
31 | static int prepend(char **buffer, int buflen, const char *str, int namelen) | ||
32 | { | ||
33 | buflen -= namelen; | ||
34 | if (buflen < 0) | ||
35 | return -ENAMETOOLONG; | ||
36 | *buffer -= namelen; | ||
37 | memcpy(*buffer, str, namelen); | ||
38 | return 0; | ||
39 | } | ||
40 | |||
41 | #define CHROOT_NSCONNECT (PATH_CHROOT_REL | PATH_CHROOT_NSCONNECT) | ||
42 | |||
43 | /** | ||
44 | * d_namespace_path - lookup a name associated with a given path | ||
45 | * @path: path to lookup (NOT NULL) | ||
46 | * @buf: buffer to store path to (NOT NULL) | ||
47 | * @buflen: length of @buf | ||
48 | * @name: Returns - pointer for start of path name with in @buf (NOT NULL) | ||
49 | * @flags: flags controlling path lookup | ||
50 | * | ||
51 | * Handle path name lookup. | ||
52 | * | ||
53 | * Returns: %0 else error code if path lookup fails | ||
54 | * When no error the path name is returned in @name which points to | ||
55 | * to a position in @buf | ||
56 | */ | ||
57 | static int d_namespace_path(struct path *path, char *buf, int buflen, | ||
58 | char **name, int flags) | ||
59 | { | ||
60 | struct path root, tmp; | ||
61 | char *res; | ||
62 | int deleted, connected; | ||
63 | int error = 0; | ||
64 | |||
65 | /* Get the root we want to resolve too */ | ||
66 | if (flags & PATH_CHROOT_REL) { | ||
67 | /* resolve paths relative to chroot */ | ||
68 | read_lock(¤t->fs->lock); | ||
69 | root = current->fs->root; | ||
70 | /* released below */ | ||
71 | path_get(&root); | ||
72 | read_unlock(¤t->fs->lock); | ||
73 | } else { | ||
74 | /* resolve paths relative to namespace */ | ||
75 | root.mnt = current->nsproxy->mnt_ns->root; | ||
76 | root.dentry = root.mnt->mnt_root; | ||
77 | /* released below */ | ||
78 | path_get(&root); | ||
79 | } | ||
80 | |||
81 | spin_lock(&dcache_lock); | ||
82 | /* There is a race window between path lookup here and the | ||
83 | * need to strip the " (deleted) string that __d_path applies | ||
84 | * Detect the race and relookup the path | ||
85 | * | ||
86 | * The stripping of (deleted) is a hack that could be removed | ||
87 | * with an updated __d_path | ||
88 | */ | ||
89 | do { | ||
90 | tmp = root; | ||
91 | deleted = d_unlinked(path->dentry); | ||
92 | res = __d_path(path, &tmp, buf, buflen); | ||
93 | |||
94 | } while (deleted != d_unlinked(path->dentry)); | ||
95 | spin_unlock(&dcache_lock); | ||
96 | |||
97 | *name = res; | ||
98 | /* handle error conditions - and still allow a partial path to | ||
99 | * be returned. | ||
100 | */ | ||
101 | if (IS_ERR(res)) { | ||
102 | error = PTR_ERR(res); | ||
103 | *name = buf; | ||
104 | goto out; | ||
105 | } | ||
106 | if (deleted) { | ||
107 | /* On some filesystems, newly allocated dentries appear to the | ||
108 | * security_path hooks as a deleted dentry except without an | ||
109 | * inode allocated. | ||
110 | * | ||
111 | * Remove the appended deleted text and return as string for | ||
112 | * normal mediation, or auditing. The (deleted) string is | ||
113 | * guaranteed to be added in this case, so just strip it. | ||
114 | */ | ||
115 | buf[buflen - 11] = 0; /* - (len(" (deleted)") +\0) */ | ||
116 | |||
117 | if (path->dentry->d_inode && !(flags & PATH_MEDIATE_DELETED)) { | ||
118 | error = -ENOENT; | ||
119 | goto out; | ||
120 | } | ||
121 | } | ||
122 | |||
123 | /* Determine if the path is connected to the expected root */ | ||
124 | connected = tmp.dentry == root.dentry && tmp.mnt == root.mnt; | ||
125 | |||
126 | /* If the path is not connected, | ||
127 | * check if it is a sysctl and handle specially else remove any | ||
128 | * leading / that __d_path may have returned. | ||
129 | * Unless | ||
130 | * specifically directed to connect the path, | ||
131 | * OR | ||
132 | * if in a chroot and doing chroot relative paths and the path | ||
133 | * resolves to the namespace root (would be connected outside | ||
134 | * of chroot) and specifically directed to connect paths to | ||
135 | * namespace root. | ||
136 | */ | ||
137 | if (!connected) { | ||
138 | /* is the disconnect path a sysctl? */ | ||
139 | if (tmp.dentry->d_sb->s_magic == PROC_SUPER_MAGIC && | ||
140 | strncmp(*name, "/sys/", 5) == 0) { | ||
141 | /* TODO: convert over to using a per namespace | ||
142 | * control instead of hard coded /proc | ||
143 | */ | ||
144 | error = prepend(name, *name - buf, "/proc", 5); | ||
145 | } else if (!(flags & PATH_CONNECT_PATH) && | ||
146 | !(((flags & CHROOT_NSCONNECT) == CHROOT_NSCONNECT) && | ||
147 | (tmp.mnt == current->nsproxy->mnt_ns->root && | ||
148 | tmp.dentry == tmp.mnt->mnt_root))) { | ||
149 | /* disconnected path, don't return pathname starting | ||
150 | * with '/' | ||
151 | */ | ||
152 | error = -ESTALE; | ||
153 | if (*res == '/') | ||
154 | *name = res + 1; | ||
155 | } | ||
156 | } | ||
157 | |||
158 | out: | ||
159 | path_put(&root); | ||
160 | |||
161 | return error; | ||
162 | } | ||
163 | |||
164 | /** | ||
165 | * get_name_to_buffer - get the pathname to a buffer ensure dir / is appended | ||
166 | * @path: path to get name for (NOT NULL) | ||
167 | * @flags: flags controlling path lookup | ||
168 | * @buffer: buffer to put name in (NOT NULL) | ||
169 | * @size: size of buffer | ||
170 | * @name: Returns - contains position of path name in @buffer (NOT NULL) | ||
171 | * | ||
172 | * Returns: %0 else error on failure | ||
173 | */ | ||
174 | static int get_name_to_buffer(struct path *path, int flags, char *buffer, | ||
175 | int size, char **name) | ||
176 | { | ||
177 | int adjust = (flags & PATH_IS_DIR) ? 1 : 0; | ||
178 | int error = d_namespace_path(path, buffer, size - adjust, name, flags); | ||
179 | |||
180 | if (!error && (flags & PATH_IS_DIR) && (*name)[1] != '\0') | ||
181 | /* | ||
182 | * Append "/" to the pathname. The root directory is a special | ||
183 | * case; it already ends in slash. | ||
184 | */ | ||
185 | strcpy(&buffer[size - 2], "/"); | ||
186 | |||
187 | return error; | ||
188 | } | ||
189 | |||
190 | /** | ||
191 | * aa_get_name - compute the pathname of a file | ||
192 | * @path: path the file (NOT NULL) | ||
193 | * @flags: flags controlling path name generation | ||
194 | * @buffer: buffer that aa_get_name() allocated (NOT NULL) | ||
195 | * @name: Returns - the generated path name if !error (NOT NULL) | ||
196 | * | ||
197 | * @name is a pointer to the beginning of the pathname (which usually differs | ||
198 | * from the beginning of the buffer), or NULL. If there is an error @name | ||
199 | * may contain a partial or invalid name that can be used for audit purposes, | ||
200 | * but it can not be used for mediation. | ||
201 | * | ||
202 | * We need PATH_IS_DIR to indicate whether the file is a directory or not | ||
203 | * because the file may not yet exist, and so we cannot check the inode's | ||
204 | * file type. | ||
205 | * | ||
206 | * Returns: %0 else error code if could retrieve name | ||
207 | */ | ||
208 | int aa_get_name(struct path *path, int flags, char **buffer, const char **name) | ||
209 | { | ||
210 | char *buf, *str = NULL; | ||
211 | int size = 256; | ||
212 | int error; | ||
213 | |||
214 | *name = NULL; | ||
215 | *buffer = NULL; | ||
216 | for (;;) { | ||
217 | /* freed by caller */ | ||
218 | buf = kmalloc(size, GFP_KERNEL); | ||
219 | if (!buf) | ||
220 | return -ENOMEM; | ||
221 | |||
222 | error = get_name_to_buffer(path, flags, buf, size, &str); | ||
223 | if (error != -ENAMETOOLONG) | ||
224 | break; | ||
225 | |||
226 | kfree(buf); | ||
227 | size <<= 1; | ||
228 | if (size > aa_g_path_max) | ||
229 | return -ENAMETOOLONG; | ||
230 | } | ||
231 | *buffer = buf; | ||
232 | *name = str; | ||
233 | |||
234 | return error; | ||
235 | } | ||
diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c new file mode 100644 index 00000000000..3cdc1ad0787 --- /dev/null +++ b/security/apparmor/policy.c | |||
@@ -0,0 +1,1184 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor policy manipulation functions | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | * | ||
14 | * | ||
15 | * AppArmor policy is based around profiles, which contain the rules a | ||
16 | * task is confined by. Every task in the system has a profile attached | ||
17 | * to it determined either by matching "unconfined" tasks against the | ||
18 | * visible set of profiles or by following a profiles attachment rules. | ||
19 | * | ||
20 | * Each profile exists in a profile namespace which is a container of | ||
21 | * visible profiles. Each namespace contains a special "unconfined" profile, | ||
22 | * which doesn't enforce any confinement on a task beyond DAC. | ||
23 | * | ||
24 | * Namespace and profile names can be written together in either | ||
25 | * of two syntaxes. | ||
26 | * :namespace:profile - used by kernel interfaces for easy detection | ||
27 | * namespace://profile - used by policy | ||
28 | * | ||
29 | * Profile names can not start with : or @ or ^ and may not contain \0 | ||
30 | * | ||
31 | * Reserved profile names | ||
32 | * unconfined - special automatically generated unconfined profile | ||
33 | * inherit - special name to indicate profile inheritance | ||
34 | * null-XXXX-YYYY - special automatically generated learning profiles | ||
35 | * | ||
36 | * Namespace names may not start with / or @ and may not contain \0 or : | ||
37 | * Reserved namespace names | ||
38 | * user-XXXX - user defined profiles | ||
39 | * | ||
40 | * a // in a profile or namespace name indicates a hierarchical name with the | ||
41 | * name before the // being the parent and the name after the child. | ||
42 | * | ||
43 | * Profile and namespace hierarchies serve two different but similar purposes. | ||
44 | * The namespace contains the set of visible profiles that are considered | ||
45 | * for attachment. The hierarchy of namespaces allows for virtualizing | ||
46 | * the namespace so that for example a chroot can have its own set of profiles | ||
47 | * which may define some local user namespaces. | ||
48 | * The profile hierarchy severs two distinct purposes, | ||
49 | * - it allows for sub profiles or hats, which allows an application to run | ||
50 | * subprograms under its own profile with different restriction than it | ||
51 | * self, and not have it use the system profile. | ||
52 | * eg. if a mail program starts an editor, the policy might make the | ||
53 | * restrictions tighter on the editor tighter than the mail program, | ||
54 | * and definitely different than general editor restrictions | ||
55 | * - it allows for binary hierarchy of profiles, so that execution history | ||
56 | * is preserved. This feature isn't exploited by AppArmor reference policy | ||
57 | * but is allowed. NOTE: this is currently suboptimal because profile | ||
58 | * aliasing is not currently implemented so that a profile for each | ||
59 | * level must be defined. | ||
60 | * eg. /bin/bash///bin/ls as a name would indicate /bin/ls was started | ||
61 | * from /bin/bash | ||
62 | * | ||
63 | * A profile or namespace name that can contain one or more // separators | ||
64 | * is referred to as an hname (hierarchical). | ||
65 | * eg. /bin/bash//bin/ls | ||
66 | * | ||
67 | * An fqname is a name that may contain both namespace and profile hnames. | ||
68 | * eg. :ns:/bin/bash//bin/ls | ||
69 | * | ||
70 | * NOTES: | ||
71 | * - locking of profile lists is currently fairly coarse. All profile | ||
72 | * lists within a namespace use the namespace lock. | ||
73 | * FIXME: move profile lists to using rcu_lists | ||
74 | */ | ||
75 | |||
76 | #include <linux/slab.h> | ||
77 | #include <linux/spinlock.h> | ||
78 | #include <linux/string.h> | ||
79 | |||
80 | #include "include/apparmor.h" | ||
81 | #include "include/capability.h" | ||
82 | #include "include/context.h" | ||
83 | #include "include/file.h" | ||
84 | #include "include/ipc.h" | ||
85 | #include "include/match.h" | ||
86 | #include "include/path.h" | ||
87 | #include "include/policy.h" | ||
88 | #include "include/policy_unpack.h" | ||
89 | #include "include/resource.h" | ||
90 | #include "include/sid.h" | ||
91 | |||
92 | |||
93 | /* root profile namespace */ | ||
94 | struct aa_namespace *root_ns; | ||
95 | |||
96 | const char *profile_mode_names[] = { | ||
97 | "enforce", | ||
98 | "complain", | ||
99 | "kill", | ||
100 | }; | ||
101 | |||
102 | /** | ||
103 | * hname_tail - find the last component of an hname | ||
104 | * @name: hname to find the base profile name component of (NOT NULL) | ||
105 | * | ||
106 | * Returns: the tail (base profile name) name component of an hname | ||
107 | */ | ||
108 | static const char *hname_tail(const char *hname) | ||
109 | { | ||
110 | char *split; | ||
111 | hname = strim((char *)hname); | ||
112 | for (split = strstr(hname, "//"); split; split = strstr(hname, "//")) | ||
113 | hname = split + 2; | ||
114 | |||
115 | return hname; | ||
116 | } | ||
117 | |||
118 | /** | ||
119 | * policy_init - initialize a policy structure | ||
120 | * @policy: policy to initialize (NOT NULL) | ||
121 | * @prefix: prefix name if any is required. (MAYBE NULL) | ||
122 | * @name: name of the policy, init will make a copy of it (NOT NULL) | ||
123 | * | ||
124 | * Note: this fn creates a copy of strings passed in | ||
125 | * | ||
126 | * Returns: true if policy init successful | ||
127 | */ | ||
128 | static bool policy_init(struct aa_policy *policy, const char *prefix, | ||
129 | const char *name) | ||
130 | { | ||
131 | /* freed by policy_free */ | ||
132 | if (prefix) { | ||
133 | policy->hname = kmalloc(strlen(prefix) + strlen(name) + 3, | ||
134 | GFP_KERNEL); | ||
135 | if (policy->hname) | ||
136 | sprintf(policy->hname, "%s//%s", prefix, name); | ||
137 | } else | ||
138 | policy->hname = kstrdup(name, GFP_KERNEL); | ||
139 | if (!policy->hname) | ||
140 | return 0; | ||
141 | /* base.name is a substring of fqname */ | ||
142 | policy->name = (char *)hname_tail(policy->hname); | ||
143 | INIT_LIST_HEAD(&policy->list); | ||
144 | INIT_LIST_HEAD(&policy->profiles); | ||
145 | kref_init(&policy->count); | ||
146 | |||
147 | return 1; | ||
148 | } | ||
149 | |||
150 | /** | ||
151 | * policy_destroy - free the elements referenced by @policy | ||
152 | * @policy: policy that is to have its elements freed (NOT NULL) | ||
153 | */ | ||
154 | static void policy_destroy(struct aa_policy *policy) | ||
155 | { | ||
156 | /* still contains profiles -- invalid */ | ||
157 | if (!list_empty(&policy->profiles)) { | ||
158 | AA_ERROR("%s: internal error, " | ||
159 | "policy '%s' still contains profiles\n", | ||
160 | __func__, policy->name); | ||
161 | BUG(); | ||
162 | } | ||
163 | if (!list_empty(&policy->list)) { | ||
164 | AA_ERROR("%s: internal error, policy '%s' still on list\n", | ||
165 | __func__, policy->name); | ||
166 | BUG(); | ||
167 | } | ||
168 | |||
169 | /* don't free name as its a subset of hname */ | ||
170 | kzfree(policy->hname); | ||
171 | } | ||
172 | |||
173 | /** | ||
174 | * __policy_find - find a policy by @name on a policy list | ||
175 | * @head: list to search (NOT NULL) | ||
176 | * @name: name to search for (NOT NULL) | ||
177 | * | ||
178 | * Requires: correct locks for the @head list be held | ||
179 | * | ||
180 | * Returns: unrefcounted policy that match @name or NULL if not found | ||
181 | */ | ||
182 | static struct aa_policy *__policy_find(struct list_head *head, const char *name) | ||
183 | { | ||
184 | struct aa_policy *policy; | ||
185 | |||
186 | list_for_each_entry(policy, head, list) { | ||
187 | if (!strcmp(policy->name, name)) | ||
188 | return policy; | ||
189 | } | ||
190 | return NULL; | ||
191 | } | ||
192 | |||
193 | /** | ||
194 | * __policy_strn_find - find a policy that's name matches @len chars of @str | ||
195 | * @head: list to search (NOT NULL) | ||
196 | * @str: string to search for (NOT NULL) | ||
197 | * @len: length of match required | ||
198 | * | ||
199 | * Requires: correct locks for the @head list be held | ||
200 | * | ||
201 | * Returns: unrefcounted policy that match @str or NULL if not found | ||
202 | * | ||
203 | * if @len == strlen(@strlen) then this is equiv to __policy_find | ||
204 | * other wise it allows searching for policy by a partial match of name | ||
205 | */ | ||
206 | static struct aa_policy *__policy_strn_find(struct list_head *head, | ||
207 | const char *str, int len) | ||
208 | { | ||
209 | struct aa_policy *policy; | ||
210 | |||
211 | list_for_each_entry(policy, head, list) { | ||
212 | if (aa_strneq(policy->name, str, len)) | ||
213 | return policy; | ||
214 | } | ||
215 | |||
216 | return NULL; | ||
217 | } | ||
218 | |||
219 | /* | ||
220 | * Routines for AppArmor namespaces | ||
221 | */ | ||
222 | |||
223 | static const char *hidden_ns_name = "---"; | ||
224 | /** | ||
225 | * aa_ns_visible - test if @view is visible from @curr | ||
226 | * @curr: namespace to treat as the parent (NOT NULL) | ||
227 | * @view: namespace to test if visible from @curr (NOT NULL) | ||
228 | * | ||
229 | * Returns: true if @view is visible from @curr else false | ||
230 | */ | ||
231 | bool aa_ns_visible(struct aa_namespace *curr, struct aa_namespace *view) | ||
232 | { | ||
233 | if (curr == view) | ||
234 | return true; | ||
235 | |||
236 | for ( ; view; view = view->parent) { | ||
237 | if (view->parent == curr) | ||
238 | return true; | ||
239 | } | ||
240 | return false; | ||
241 | } | ||
242 | |||
243 | /** | ||
244 | * aa_na_name - Find the ns name to display for @view from @curr | ||
245 | * @curr - current namespace (NOT NULL) | ||
246 | * @view - namespace attempting to view (NOT NULL) | ||
247 | * | ||
248 | * Returns: name of @view visible from @curr | ||
249 | */ | ||
250 | const char *aa_ns_name(struct aa_namespace *curr, struct aa_namespace *view) | ||
251 | { | ||
252 | /* if view == curr then the namespace name isn't displayed */ | ||
253 | if (curr == view) | ||
254 | return ""; | ||
255 | |||
256 | if (aa_ns_visible(curr, view)) { | ||
257 | /* at this point if a ns is visible it is in a view ns | ||
258 | * thus the curr ns.hname is a prefix of its name. | ||
259 | * Only output the virtualized portion of the name | ||
260 | * Add + 2 to skip over // separating curr hname prefix | ||
261 | * from the visible tail of the views hname | ||
262 | */ | ||
263 | return view->base.hname + strlen(curr->base.hname) + 2; | ||
264 | } else | ||
265 | return hidden_ns_name; | ||
266 | } | ||
267 | |||
268 | /** | ||
269 | * alloc_namespace - allocate, initialize and return a new namespace | ||
270 | * @prefix: parent namespace name (MAYBE NULL) | ||
271 | * @name: a preallocated name (NOT NULL) | ||
272 | * | ||
273 | * Returns: refcounted namespace or NULL on failure. | ||
274 | */ | ||
275 | static struct aa_namespace *alloc_namespace(const char *prefix, | ||
276 | const char *name) | ||
277 | { | ||
278 | struct aa_namespace *ns; | ||
279 | |||
280 | ns = kzalloc(sizeof(*ns), GFP_KERNEL); | ||
281 | AA_DEBUG("%s(%p)\n", __func__, ns); | ||
282 | if (!ns) | ||
283 | return NULL; | ||
284 | if (!policy_init(&ns->base, prefix, name)) | ||
285 | goto fail_ns; | ||
286 | |||
287 | INIT_LIST_HEAD(&ns->sub_ns); | ||
288 | rwlock_init(&ns->lock); | ||
289 | |||
290 | /* released by free_namespace */ | ||
291 | ns->unconfined = aa_alloc_profile("unconfined"); | ||
292 | if (!ns->unconfined) | ||
293 | goto fail_unconfined; | ||
294 | |||
295 | ns->unconfined->sid = aa_alloc_sid(); | ||
296 | ns->unconfined->flags = PFLAG_UNCONFINED | PFLAG_IX_ON_NAME_ERROR | | ||
297 | PFLAG_IMMUTABLE; | ||
298 | |||
299 | /* | ||
300 | * released by free_namespace, however __remove_namespace breaks | ||
301 | * the cyclic references (ns->unconfined, and unconfined->ns) and | ||
302 | * replaces with refs to parent namespace unconfined | ||
303 | */ | ||
304 | ns->unconfined->ns = aa_get_namespace(ns); | ||
305 | |||
306 | return ns; | ||
307 | |||
308 | fail_unconfined: | ||
309 | kzfree(ns->base.name); | ||
310 | fail_ns: | ||
311 | kzfree(ns); | ||
312 | return NULL; | ||
313 | } | ||
314 | |||
315 | /** | ||
316 | * free_namespace - free a profile namespace | ||
317 | * @ns: the namespace to free (MAYBE NULL) | ||
318 | * | ||
319 | * Requires: All references to the namespace must have been put, if the | ||
320 | * namespace was referenced by a profile confining a task, | ||
321 | */ | ||
322 | static void free_namespace(struct aa_namespace *ns) | ||
323 | { | ||
324 | if (!ns) | ||
325 | return; | ||
326 | |||
327 | policy_destroy(&ns->base); | ||
328 | aa_put_namespace(ns->parent); | ||
329 | |||
330 | if (ns->unconfined && ns->unconfined->ns == ns) | ||
331 | ns->unconfined->ns = NULL; | ||
332 | |||
333 | aa_put_profile(ns->unconfined); | ||
334 | kzfree(ns); | ||
335 | } | ||
336 | |||
337 | /** | ||
338 | * aa_free_namespace_kref - free aa_namespace by kref (see aa_put_namespace) | ||
339 | * @kr: kref callback for freeing of a namespace (NOT NULL) | ||
340 | */ | ||
341 | void aa_free_namespace_kref(struct kref *kref) | ||
342 | { | ||
343 | free_namespace(container_of(kref, struct aa_namespace, base.count)); | ||
344 | } | ||
345 | |||
346 | /** | ||
347 | * __aa_find_namespace - find a namespace on a list by @name | ||
348 | * @head: list to search for namespace on (NOT NULL) | ||
349 | * @name: name of namespace to look for (NOT NULL) | ||
350 | * | ||
351 | * Returns: unrefcounted namespace | ||
352 | * | ||
353 | * Requires: ns lock be held | ||
354 | */ | ||
355 | static struct aa_namespace *__aa_find_namespace(struct list_head *head, | ||
356 | const char *name) | ||
357 | { | ||
358 | return (struct aa_namespace *)__policy_find(head, name); | ||
359 | } | ||
360 | |||
361 | /** | ||
362 | * aa_find_namespace - look up a profile namespace on the namespace list | ||
363 | * @root: namespace to search in (NOT NULL) | ||
364 | * @name: name of namespace to find (NOT NULL) | ||
365 | * | ||
366 | * Returns: a refcounted namespace on the list, or NULL if no namespace | ||
367 | * called @name exists. | ||
368 | * | ||
369 | * refcount released by caller | ||
370 | */ | ||
371 | struct aa_namespace *aa_find_namespace(struct aa_namespace *root, | ||
372 | const char *name) | ||
373 | { | ||
374 | struct aa_namespace *ns = NULL; | ||
375 | |||
376 | read_lock(&root->lock); | ||
377 | ns = aa_get_namespace(__aa_find_namespace(&root->sub_ns, name)); | ||
378 | read_unlock(&root->lock); | ||
379 | |||
380 | return ns; | ||
381 | } | ||
382 | |||
383 | /** | ||
384 | * aa_prepare_namespace - find an existing or create a new namespace of @name | ||
385 | * @name: the namespace to find or add (MAYBE NULL) | ||
386 | * | ||
387 | * Returns: refcounted namespace or NULL if failed to create one | ||
388 | */ | ||
389 | static struct aa_namespace *aa_prepare_namespace(const char *name) | ||
390 | { | ||
391 | struct aa_namespace *ns, *root; | ||
392 | |||
393 | root = aa_current_profile()->ns; | ||
394 | |||
395 | write_lock(&root->lock); | ||
396 | |||
397 | /* if name isn't specified the profile is loaded to the current ns */ | ||
398 | if (!name) { | ||
399 | /* released by caller */ | ||
400 | ns = aa_get_namespace(root); | ||
401 | goto out; | ||
402 | } | ||
403 | |||
404 | /* try and find the specified ns and if it doesn't exist create it */ | ||
405 | /* released by caller */ | ||
406 | ns = aa_get_namespace(__aa_find_namespace(&root->sub_ns, name)); | ||
407 | if (!ns) { | ||
408 | /* namespace not found */ | ||
409 | struct aa_namespace *new_ns; | ||
410 | write_unlock(&root->lock); | ||
411 | new_ns = alloc_namespace(root->base.hname, name); | ||
412 | if (!new_ns) | ||
413 | return NULL; | ||
414 | write_lock(&root->lock); | ||
415 | /* test for race when new_ns was allocated */ | ||
416 | ns = __aa_find_namespace(&root->sub_ns, name); | ||
417 | if (!ns) { | ||
418 | /* add parent ref */ | ||
419 | new_ns->parent = aa_get_namespace(root); | ||
420 | |||
421 | list_add(&new_ns->base.list, &root->sub_ns); | ||
422 | /* add list ref */ | ||
423 | ns = aa_get_namespace(new_ns); | ||
424 | } else { | ||
425 | /* raced so free the new one */ | ||
426 | free_namespace(new_ns); | ||
427 | /* get reference on namespace */ | ||
428 | aa_get_namespace(ns); | ||
429 | } | ||
430 | } | ||
431 | out: | ||
432 | write_unlock(&root->lock); | ||
433 | |||
434 | /* return ref */ | ||
435 | return ns; | ||
436 | } | ||
437 | |||
438 | /** | ||
439 | * __list_add_profile - add a profile to a list | ||
440 | * @list: list to add it to (NOT NULL) | ||
441 | * @profile: the profile to add (NOT NULL) | ||
442 | * | ||
443 | * refcount @profile, should be put by __list_remove_profile | ||
444 | * | ||
445 | * Requires: namespace lock be held, or list not be shared | ||
446 | */ | ||
447 | static void __list_add_profile(struct list_head *list, | ||
448 | struct aa_profile *profile) | ||
449 | { | ||
450 | list_add(&profile->base.list, list); | ||
451 | /* get list reference */ | ||
452 | aa_get_profile(profile); | ||
453 | } | ||
454 | |||
455 | /** | ||
456 | * __list_remove_profile - remove a profile from the list it is on | ||
457 | * @profile: the profile to remove (NOT NULL) | ||
458 | * | ||
459 | * remove a profile from the list, warning generally removal should | ||
460 | * be done with __replace_profile as most profile removals are | ||
461 | * replacements to the unconfined profile. | ||
462 | * | ||
463 | * put @profile list refcount | ||
464 | * | ||
465 | * Requires: namespace lock be held, or list not have been live | ||
466 | */ | ||
467 | static void __list_remove_profile(struct aa_profile *profile) | ||
468 | { | ||
469 | list_del_init(&profile->base.list); | ||
470 | if (!(profile->flags & PFLAG_NO_LIST_REF)) | ||
471 | /* release list reference */ | ||
472 | aa_put_profile(profile); | ||
473 | } | ||
474 | |||
475 | /** | ||
476 | * __replace_profile - replace @old with @new on a list | ||
477 | * @old: profile to be replaced (NOT NULL) | ||
478 | * @new: profile to replace @old with (NOT NULL) | ||
479 | * | ||
480 | * Will duplicate and refcount elements that @new inherits from @old | ||
481 | * and will inherit @old children. | ||
482 | * | ||
483 | * refcount @new for list, put @old list refcount | ||
484 | * | ||
485 | * Requires: namespace list lock be held, or list not be shared | ||
486 | */ | ||
487 | static void __replace_profile(struct aa_profile *old, struct aa_profile *new) | ||
488 | { | ||
489 | struct aa_policy *policy; | ||
490 | struct aa_profile *child, *tmp; | ||
491 | |||
492 | if (old->parent) | ||
493 | policy = &old->parent->base; | ||
494 | else | ||
495 | policy = &old->ns->base; | ||
496 | |||
497 | /* released when @new is freed */ | ||
498 | new->parent = aa_get_profile(old->parent); | ||
499 | new->ns = aa_get_namespace(old->ns); | ||
500 | new->sid = old->sid; | ||
501 | __list_add_profile(&policy->profiles, new); | ||
502 | /* inherit children */ | ||
503 | list_for_each_entry_safe(child, tmp, &old->base.profiles, base.list) { | ||
504 | aa_put_profile(child->parent); | ||
505 | child->parent = aa_get_profile(new); | ||
506 | /* list refcount transferred to @new*/ | ||
507 | list_move(&child->base.list, &new->base.profiles); | ||
508 | } | ||
509 | |||
510 | /* released by free_profile */ | ||
511 | old->replacedby = aa_get_profile(new); | ||
512 | __list_remove_profile(old); | ||
513 | } | ||
514 | |||
515 | static void __profile_list_release(struct list_head *head); | ||
516 | |||
517 | /** | ||
518 | * __remove_profile - remove old profile, and children | ||
519 | * @profile: profile to be replaced (NOT NULL) | ||
520 | * | ||
521 | * Requires: namespace list lock be held, or list not be shared | ||
522 | */ | ||
523 | static void __remove_profile(struct aa_profile *profile) | ||
524 | { | ||
525 | /* release any children lists first */ | ||
526 | __profile_list_release(&profile->base.profiles); | ||
527 | /* released by free_profile */ | ||
528 | profile->replacedby = aa_get_profile(profile->ns->unconfined); | ||
529 | __list_remove_profile(profile); | ||
530 | } | ||
531 | |||
532 | /** | ||
533 | * __profile_list_release - remove all profiles on the list and put refs | ||
534 | * @head: list of profiles (NOT NULL) | ||
535 | * | ||
536 | * Requires: namespace lock be held | ||
537 | */ | ||
538 | static void __profile_list_release(struct list_head *head) | ||
539 | { | ||
540 | struct aa_profile *profile, *tmp; | ||
541 | list_for_each_entry_safe(profile, tmp, head, base.list) | ||
542 | __remove_profile(profile); | ||
543 | } | ||
544 | |||
545 | static void __ns_list_release(struct list_head *head); | ||
546 | |||
547 | /** | ||
548 | * destroy_namespace - remove everything contained by @ns | ||
549 | * @ns: namespace to have it contents removed (NOT NULL) | ||
550 | */ | ||
551 | static void destroy_namespace(struct aa_namespace *ns) | ||
552 | { | ||
553 | if (!ns) | ||
554 | return; | ||
555 | |||
556 | write_lock(&ns->lock); | ||
557 | /* release all profiles in this namespace */ | ||
558 | __profile_list_release(&ns->base.profiles); | ||
559 | |||
560 | /* release all sub namespaces */ | ||
561 | __ns_list_release(&ns->sub_ns); | ||
562 | |||
563 | write_unlock(&ns->lock); | ||
564 | } | ||
565 | |||
566 | /** | ||
567 | * __remove_namespace - remove a namespace and all its children | ||
568 | * @ns: namespace to be removed (NOT NULL) | ||
569 | * | ||
570 | * Requires: ns->parent->lock be held and ns removed from parent. | ||
571 | */ | ||
572 | static void __remove_namespace(struct aa_namespace *ns) | ||
573 | { | ||
574 | struct aa_profile *unconfined = ns->unconfined; | ||
575 | |||
576 | /* remove ns from namespace list */ | ||
577 | list_del_init(&ns->base.list); | ||
578 | |||
579 | /* | ||
580 | * break the ns, unconfined profile cyclic reference and forward | ||
581 | * all new unconfined profiles requests to the parent namespace | ||
582 | * This will result in all confined tasks that have a profile | ||
583 | * being removed, inheriting the parent->unconfined profile. | ||
584 | */ | ||
585 | if (ns->parent) | ||
586 | ns->unconfined = aa_get_profile(ns->parent->unconfined); | ||
587 | |||
588 | destroy_namespace(ns); | ||
589 | |||
590 | /* release original ns->unconfined ref */ | ||
591 | aa_put_profile(unconfined); | ||
592 | /* release ns->base.list ref, from removal above */ | ||
593 | aa_put_namespace(ns); | ||
594 | } | ||
595 | |||
596 | /** | ||
597 | * __ns_list_release - remove all profile namespaces on the list put refs | ||
598 | * @head: list of profile namespaces (NOT NULL) | ||
599 | * | ||
600 | * Requires: namespace lock be held | ||
601 | */ | ||
602 | static void __ns_list_release(struct list_head *head) | ||
603 | { | ||
604 | struct aa_namespace *ns, *tmp; | ||
605 | list_for_each_entry_safe(ns, tmp, head, base.list) | ||
606 | __remove_namespace(ns); | ||
607 | |||
608 | } | ||
609 | |||
610 | /** | ||
611 | * aa_alloc_root_ns - allocate the root profile namespace | ||
612 | * | ||
613 | * Returns: %0 on success else error | ||
614 | * | ||
615 | */ | ||
616 | int __init aa_alloc_root_ns(void) | ||
617 | { | ||
618 | /* released by aa_free_root_ns - used as list ref*/ | ||
619 | root_ns = alloc_namespace(NULL, "root"); | ||
620 | if (!root_ns) | ||
621 | return -ENOMEM; | ||
622 | |||
623 | return 0; | ||
624 | } | ||
625 | |||
626 | /** | ||
627 | * aa_free_root_ns - free the root profile namespace | ||
628 | */ | ||
629 | void __init aa_free_root_ns(void) | ||
630 | { | ||
631 | struct aa_namespace *ns = root_ns; | ||
632 | root_ns = NULL; | ||
633 | |||
634 | destroy_namespace(ns); | ||
635 | aa_put_namespace(ns); | ||
636 | } | ||
637 | |||
638 | /** | ||
639 | * aa_alloc_profile - allocate, initialize and return a new profile | ||
640 | * @hname: name of the profile (NOT NULL) | ||
641 | * | ||
642 | * Returns: refcount profile or NULL on failure | ||
643 | */ | ||
644 | struct aa_profile *aa_alloc_profile(const char *hname) | ||
645 | { | ||
646 | struct aa_profile *profile; | ||
647 | |||
648 | /* freed by free_profile - usually through aa_put_profile */ | ||
649 | profile = kzalloc(sizeof(*profile), GFP_KERNEL); | ||
650 | if (!profile) | ||
651 | return NULL; | ||
652 | |||
653 | if (!policy_init(&profile->base, NULL, hname)) { | ||
654 | kzfree(profile); | ||
655 | return NULL; | ||
656 | } | ||
657 | |||
658 | /* refcount released by caller */ | ||
659 | return profile; | ||
660 | } | ||
661 | |||
662 | /** | ||
663 | * aa_new_null_profile - create a new null-X learning profile | ||
664 | * @parent: profile that caused this profile to be created (NOT NULL) | ||
665 | * @hat: true if the null- learning profile is a hat | ||
666 | * | ||
667 | * Create a null- complain mode profile used in learning mode. The name of | ||
668 | * the profile is unique and follows the format of parent//null-sid. | ||
669 | * | ||
670 | * null profiles are added to the profile list but the list does not | ||
671 | * hold a count on them so that they are automatically released when | ||
672 | * not in use. | ||
673 | * | ||
674 | * Returns: new refcounted profile else NULL on failure | ||
675 | */ | ||
676 | struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat) | ||
677 | { | ||
678 | struct aa_profile *profile = NULL; | ||
679 | char *name; | ||
680 | u32 sid = aa_alloc_sid(); | ||
681 | |||
682 | /* freed below */ | ||
683 | name = kmalloc(strlen(parent->base.hname) + 2 + 7 + 8, GFP_KERNEL); | ||
684 | if (!name) | ||
685 | goto fail; | ||
686 | sprintf(name, "%s//null-%x", parent->base.hname, sid); | ||
687 | |||
688 | profile = aa_alloc_profile(name); | ||
689 | kfree(name); | ||
690 | if (!profile) | ||
691 | goto fail; | ||
692 | |||
693 | profile->sid = sid; | ||
694 | profile->mode = APPARMOR_COMPLAIN; | ||
695 | profile->flags = PFLAG_NULL; | ||
696 | if (hat) | ||
697 | profile->flags |= PFLAG_HAT; | ||
698 | |||
699 | /* released on free_profile */ | ||
700 | profile->parent = aa_get_profile(parent); | ||
701 | profile->ns = aa_get_namespace(parent->ns); | ||
702 | |||
703 | write_lock(&profile->ns->lock); | ||
704 | __list_add_profile(&parent->base.profiles, profile); | ||
705 | write_unlock(&profile->ns->lock); | ||
706 | |||
707 | /* refcount released by caller */ | ||
708 | return profile; | ||
709 | |||
710 | fail: | ||
711 | aa_free_sid(sid); | ||
712 | return NULL; | ||
713 | } | ||
714 | |||
715 | /** | ||
716 | * free_profile - free a profile | ||
717 | * @profile: the profile to free (MAYBE NULL) | ||
718 | * | ||
719 | * Free a profile, its hats and null_profile. All references to the profile, | ||
720 | * its hats and null_profile must have been put. | ||
721 | * | ||
722 | * If the profile was referenced from a task context, free_profile() will | ||
723 | * be called from an rcu callback routine, so we must not sleep here. | ||
724 | */ | ||
725 | static void free_profile(struct aa_profile *profile) | ||
726 | { | ||
727 | AA_DEBUG("%s(%p)\n", __func__, profile); | ||
728 | |||
729 | if (!profile) | ||
730 | return; | ||
731 | |||
732 | if (!list_empty(&profile->base.list)) { | ||
733 | AA_ERROR("%s: internal error, " | ||
734 | "profile '%s' still on ns list\n", | ||
735 | __func__, profile->base.name); | ||
736 | BUG(); | ||
737 | } | ||
738 | |||
739 | /* free children profiles */ | ||
740 | policy_destroy(&profile->base); | ||
741 | aa_put_profile(profile->parent); | ||
742 | |||
743 | aa_put_namespace(profile->ns); | ||
744 | kzfree(profile->rename); | ||
745 | |||
746 | aa_free_file_rules(&profile->file); | ||
747 | aa_free_cap_rules(&profile->caps); | ||
748 | aa_free_rlimit_rules(&profile->rlimits); | ||
749 | |||
750 | aa_free_sid(profile->sid); | ||
751 | aa_put_dfa(profile->xmatch); | ||
752 | |||
753 | aa_put_profile(profile->replacedby); | ||
754 | |||
755 | kzfree(profile); | ||
756 | } | ||
757 | |||
758 | /** | ||
759 | * aa_free_profile_kref - free aa_profile by kref (called by aa_put_profile) | ||
760 | * @kr: kref callback for freeing of a profile (NOT NULL) | ||
761 | */ | ||
762 | void aa_free_profile_kref(struct kref *kref) | ||
763 | { | ||
764 | struct aa_profile *p = container_of(kref, struct aa_profile, | ||
765 | base.count); | ||
766 | |||
767 | free_profile(p); | ||
768 | } | ||
769 | |||
770 | /* TODO: profile accounting - setup in remove */ | ||
771 | |||
772 | /** | ||
773 | * __find_child - find a profile on @head list with a name matching @name | ||
774 | * @head: list to search (NOT NULL) | ||
775 | * @name: name of profile (NOT NULL) | ||
776 | * | ||
777 | * Requires: ns lock protecting list be held | ||
778 | * | ||
779 | * Returns: unrefcounted profile ptr, or NULL if not found | ||
780 | */ | ||
781 | static struct aa_profile *__find_child(struct list_head *head, const char *name) | ||
782 | { | ||
783 | return (struct aa_profile *)__policy_find(head, name); | ||
784 | } | ||
785 | |||
786 | /** | ||
787 | * __strn_find_child - find a profile on @head list using substring of @name | ||
788 | * @head: list to search (NOT NULL) | ||
789 | * @name: name of profile (NOT NULL) | ||
790 | * @len: length of @name substring to match | ||
791 | * | ||
792 | * Requires: ns lock protecting list be held | ||
793 | * | ||
794 | * Returns: unrefcounted profile ptr, or NULL if not found | ||
795 | */ | ||
796 | static struct aa_profile *__strn_find_child(struct list_head *head, | ||
797 | const char *name, int len) | ||
798 | { | ||
799 | return (struct aa_profile *)__policy_strn_find(head, name, len); | ||
800 | } | ||
801 | |||
802 | /** | ||
803 | * aa_find_child - find a profile by @name in @parent | ||
804 | * @parent: profile to search (NOT NULL) | ||
805 | * @name: profile name to search for (NOT NULL) | ||
806 | * | ||
807 | * Returns: a refcounted profile or NULL if not found | ||
808 | */ | ||
809 | struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name) | ||
810 | { | ||
811 | struct aa_profile *profile; | ||
812 | |||
813 | read_lock(&parent->ns->lock); | ||
814 | profile = aa_get_profile(__find_child(&parent->base.profiles, name)); | ||
815 | read_unlock(&parent->ns->lock); | ||
816 | |||
817 | /* refcount released by caller */ | ||
818 | return profile; | ||
819 | } | ||
820 | |||
821 | /** | ||
822 | * __lookup_parent - lookup the parent of a profile of name @hname | ||
823 | * @ns: namespace to lookup profile in (NOT NULL) | ||
824 | * @hname: hierarchical profile name to find parent of (NOT NULL) | ||
825 | * | ||
826 | * Lookups up the parent of a fully qualified profile name, the profile | ||
827 | * that matches hname does not need to exist, in general this | ||
828 | * is used to load a new profile. | ||
829 | * | ||
830 | * Requires: ns->lock be held | ||
831 | * | ||
832 | * Returns: unrefcounted policy or NULL if not found | ||
833 | */ | ||
834 | static struct aa_policy *__lookup_parent(struct aa_namespace *ns, | ||
835 | const char *hname) | ||
836 | { | ||
837 | struct aa_policy *policy; | ||
838 | struct aa_profile *profile = NULL; | ||
839 | char *split; | ||
840 | |||
841 | policy = &ns->base; | ||
842 | |||
843 | for (split = strstr(hname, "//"); split;) { | ||
844 | profile = __strn_find_child(&policy->profiles, hname, | ||
845 | split - hname); | ||
846 | if (!profile) | ||
847 | return NULL; | ||
848 | policy = &profile->base; | ||
849 | hname = split + 2; | ||
850 | split = strstr(hname, "//"); | ||
851 | } | ||
852 | if (!profile) | ||
853 | return &ns->base; | ||
854 | return &profile->base; | ||
855 | } | ||
856 | |||
857 | /** | ||
858 | * __lookup_profile - lookup the profile matching @hname | ||
859 | * @base: base list to start looking up profile name from (NOT NULL) | ||
860 | * @hname: hierarchical profile name (NOT NULL) | ||
861 | * | ||
862 | * Requires: ns->lock be held | ||
863 | * | ||
864 | * Returns: unrefcounted profile pointer or NULL if not found | ||
865 | * | ||
866 | * Do a relative name lookup, recursing through profile tree. | ||
867 | */ | ||
868 | static struct aa_profile *__lookup_profile(struct aa_policy *base, | ||
869 | const char *hname) | ||
870 | { | ||
871 | struct aa_profile *profile = NULL; | ||
872 | char *split; | ||
873 | |||
874 | for (split = strstr(hname, "//"); split;) { | ||
875 | profile = __strn_find_child(&base->profiles, hname, | ||
876 | split - hname); | ||
877 | if (!profile) | ||
878 | return NULL; | ||
879 | |||
880 | base = &profile->base; | ||
881 | hname = split + 2; | ||
882 | split = strstr(hname, "//"); | ||
883 | } | ||
884 | |||
885 | profile = __find_child(&base->profiles, hname); | ||
886 | |||
887 | return profile; | ||
888 | } | ||
889 | |||
890 | /** | ||
891 | * aa_lookup_profile - find a profile by its full or partial name | ||
892 | * @ns: the namespace to start from (NOT NULL) | ||
893 | * @hname: name to do lookup on. Does not contain namespace prefix (NOT NULL) | ||
894 | * | ||
895 | * Returns: refcounted profile or NULL if not found | ||
896 | */ | ||
897 | struct aa_profile *aa_lookup_profile(struct aa_namespace *ns, const char *hname) | ||
898 | { | ||
899 | struct aa_profile *profile; | ||
900 | |||
901 | read_lock(&ns->lock); | ||
902 | profile = aa_get_profile(__lookup_profile(&ns->base, hname)); | ||
903 | read_unlock(&ns->lock); | ||
904 | |||
905 | /* refcount released by caller */ | ||
906 | return profile; | ||
907 | } | ||
908 | |||
909 | /** | ||
910 | * replacement_allowed - test to see if replacement is allowed | ||
911 | * @profile: profile to test if it can be replaced (MAYBE NULL) | ||
912 | * @noreplace: true if replacement shouldn't be allowed but addition is okay | ||
913 | * @info: Returns - info about why replacement failed (NOT NULL) | ||
914 | * | ||
915 | * Returns: %0 if replacement allowed else error code | ||
916 | */ | ||
917 | static int replacement_allowed(struct aa_profile *profile, int noreplace, | ||
918 | const char **info) | ||
919 | { | ||
920 | if (profile) { | ||
921 | if (profile->flags & PFLAG_IMMUTABLE) { | ||
922 | *info = "cannot replace immutible profile"; | ||
923 | return -EPERM; | ||
924 | } else if (noreplace) { | ||
925 | *info = "profile already exists"; | ||
926 | return -EEXIST; | ||
927 | } | ||
928 | } | ||
929 | return 0; | ||
930 | } | ||
931 | |||
932 | /** | ||
933 | * __add_new_profile - simple wrapper around __list_add_profile | ||
934 | * @ns: namespace that profile is being added to (NOT NULL) | ||
935 | * @policy: the policy container to add the profile to (NOT NULL) | ||
936 | * @profile: profile to add (NOT NULL) | ||
937 | * | ||
938 | * add a profile to a list and do other required basic allocations | ||
939 | */ | ||
940 | static void __add_new_profile(struct aa_namespace *ns, struct aa_policy *policy, | ||
941 | struct aa_profile *profile) | ||
942 | { | ||
943 | if (policy != &ns->base) | ||
944 | /* released on profile replacement or free_profile */ | ||
945 | profile->parent = aa_get_profile((struct aa_profile *) policy); | ||
946 | __list_add_profile(&policy->profiles, profile); | ||
947 | /* released on free_profile */ | ||
948 | profile->sid = aa_alloc_sid(); | ||
949 | profile->ns = aa_get_namespace(ns); | ||
950 | } | ||
951 | |||
952 | /** | ||
953 | * aa_audit_policy - Do auditing of policy changes | ||
954 | * @op: policy operation being performed | ||
955 | * @gfp: memory allocation flags | ||
956 | * @name: name of profile being manipulated (NOT NULL) | ||
957 | * @info: any extra information to be audited (MAYBE NULL) | ||
958 | * @error: error code | ||
959 | * | ||
960 | * Returns: the error to be returned after audit is done | ||
961 | */ | ||
962 | static int audit_policy(int op, gfp_t gfp, const char *name, const char *info, | ||
963 | int error) | ||
964 | { | ||
965 | struct common_audit_data sa; | ||
966 | COMMON_AUDIT_DATA_INIT(&sa, NONE); | ||
967 | sa.aad.op = op; | ||
968 | sa.aad.name = name; | ||
969 | sa.aad.info = info; | ||
970 | sa.aad.error = error; | ||
971 | |||
972 | return aa_audit(AUDIT_APPARMOR_STATUS, __aa_current_profile(), gfp, | ||
973 | &sa, NULL); | ||
974 | } | ||
975 | |||
976 | /** | ||
977 | * aa_may_manage_policy - can the current task manage policy | ||
978 | * @op: the policy manipulation operation being done | ||
979 | * | ||
980 | * Returns: true if the task is allowed to manipulate policy | ||
981 | */ | ||
982 | bool aa_may_manage_policy(int op) | ||
983 | { | ||
984 | /* check if loading policy is locked out */ | ||
985 | if (aa_g_lock_policy) { | ||
986 | audit_policy(op, GFP_KERNEL, NULL, "policy_locked", -EACCES); | ||
987 | return 0; | ||
988 | } | ||
989 | |||
990 | if (!capable(CAP_MAC_ADMIN)) { | ||
991 | audit_policy(op, GFP_KERNEL, NULL, "not policy admin", -EACCES); | ||
992 | return 0; | ||
993 | } | ||
994 | |||
995 | return 1; | ||
996 | } | ||
997 | |||
998 | /** | ||
999 | * aa_replace_profiles - replace profile(s) on the profile list | ||
1000 | * @udata: serialized data stream (NOT NULL) | ||
1001 | * @size: size of the serialized data stream | ||
1002 | * @noreplace: true if only doing addition, no replacement allowed | ||
1003 | * | ||
1004 | * unpack and replace a profile on the profile list and uses of that profile | ||
1005 | * by any aa_task_cxt. If the profile does not exist on the profile list | ||
1006 | * it is added. | ||
1007 | * | ||
1008 | * Returns: size of data consumed else error code on failure. | ||
1009 | */ | ||
1010 | ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) | ||
1011 | { | ||
1012 | struct aa_policy *policy; | ||
1013 | struct aa_profile *old_profile = NULL, *new_profile = NULL; | ||
1014 | struct aa_profile *rename_profile = NULL; | ||
1015 | struct aa_namespace *ns = NULL; | ||
1016 | const char *ns_name, *name = NULL, *info = NULL; | ||
1017 | int op = OP_PROF_REPL; | ||
1018 | ssize_t error; | ||
1019 | |||
1020 | /* released below */ | ||
1021 | new_profile = aa_unpack(udata, size, &ns_name); | ||
1022 | if (IS_ERR(new_profile)) { | ||
1023 | error = PTR_ERR(new_profile); | ||
1024 | new_profile = NULL; | ||
1025 | goto fail; | ||
1026 | } | ||
1027 | |||
1028 | /* released below */ | ||
1029 | ns = aa_prepare_namespace(ns_name); | ||
1030 | if (!ns) { | ||
1031 | info = "failed to prepare namespace"; | ||
1032 | error = -ENOMEM; | ||
1033 | name = ns_name; | ||
1034 | goto fail; | ||
1035 | } | ||
1036 | |||
1037 | name = new_profile->base.hname; | ||
1038 | |||
1039 | write_lock(&ns->lock); | ||
1040 | /* no ref on policy only use inside lock */ | ||
1041 | policy = __lookup_parent(ns, new_profile->base.hname); | ||
1042 | |||
1043 | if (!policy) { | ||
1044 | info = "parent does not exist"; | ||
1045 | error = -ENOENT; | ||
1046 | goto audit; | ||
1047 | } | ||
1048 | |||
1049 | old_profile = __find_child(&policy->profiles, new_profile->base.name); | ||
1050 | /* released below */ | ||
1051 | aa_get_profile(old_profile); | ||
1052 | |||
1053 | if (new_profile->rename) { | ||
1054 | rename_profile = __lookup_profile(&ns->base, | ||
1055 | new_profile->rename); | ||
1056 | /* released below */ | ||
1057 | aa_get_profile(rename_profile); | ||
1058 | |||
1059 | if (!rename_profile) { | ||
1060 | info = "profile to rename does not exist"; | ||
1061 | name = new_profile->rename; | ||
1062 | error = -ENOENT; | ||
1063 | goto audit; | ||
1064 | } | ||
1065 | } | ||
1066 | |||
1067 | error = replacement_allowed(old_profile, noreplace, &info); | ||
1068 | if (error) | ||
1069 | goto audit; | ||
1070 | |||
1071 | error = replacement_allowed(rename_profile, noreplace, &info); | ||
1072 | if (error) | ||
1073 | goto audit; | ||
1074 | |||
1075 | audit: | ||
1076 | if (!old_profile && !rename_profile) | ||
1077 | op = OP_PROF_LOAD; | ||
1078 | |||
1079 | error = audit_policy(op, GFP_ATOMIC, name, info, error); | ||
1080 | |||
1081 | if (!error) { | ||
1082 | if (rename_profile) | ||
1083 | __replace_profile(rename_profile, new_profile); | ||
1084 | if (old_profile) { | ||
1085 | /* when there are both rename and old profiles | ||
1086 | * inherit old profiles sid | ||
1087 | */ | ||
1088 | if (rename_profile) | ||
1089 | aa_free_sid(new_profile->sid); | ||
1090 | __replace_profile(old_profile, new_profile); | ||
1091 | } | ||
1092 | if (!(old_profile || rename_profile)) | ||
1093 | __add_new_profile(ns, policy, new_profile); | ||
1094 | } | ||
1095 | write_unlock(&ns->lock); | ||
1096 | |||
1097 | out: | ||
1098 | aa_put_namespace(ns); | ||
1099 | aa_put_profile(rename_profile); | ||
1100 | aa_put_profile(old_profile); | ||
1101 | aa_put_profile(new_profile); | ||
1102 | if (error) | ||
1103 | return error; | ||
1104 | return size; | ||
1105 | |||
1106 | fail: | ||
1107 | error = audit_policy(op, GFP_KERNEL, name, info, error); | ||
1108 | goto out; | ||
1109 | } | ||
1110 | |||
1111 | /** | ||
1112 | * aa_remove_profiles - remove profile(s) from the system | ||
1113 | * @fqname: name of the profile or namespace to remove (NOT NULL) | ||
1114 | * @size: size of the name | ||
1115 | * | ||
1116 | * Remove a profile or sub namespace from the current namespace, so that | ||
1117 | * they can not be found anymore and mark them as replaced by unconfined | ||
1118 | * | ||
1119 | * NOTE: removing confinement does not restore rlimits to preconfinemnet values | ||
1120 | * | ||
1121 | * Returns: size of data consume else error code if fails | ||
1122 | */ | ||
1123 | ssize_t aa_remove_profiles(char *fqname, size_t size) | ||
1124 | { | ||
1125 | struct aa_namespace *root, *ns = NULL; | ||
1126 | struct aa_profile *profile = NULL; | ||
1127 | const char *name = fqname, *info = NULL; | ||
1128 | ssize_t error = 0; | ||
1129 | |||
1130 | if (*fqname == 0) { | ||
1131 | info = "no profile specified"; | ||
1132 | error = -ENOENT; | ||
1133 | goto fail; | ||
1134 | } | ||
1135 | |||
1136 | root = aa_current_profile()->ns; | ||
1137 | |||
1138 | if (fqname[0] == ':') { | ||
1139 | char *ns_name; | ||
1140 | name = aa_split_fqname(fqname, &ns_name); | ||
1141 | if (ns_name) { | ||
1142 | /* released below */ | ||
1143 | ns = aa_find_namespace(root, ns_name); | ||
1144 | if (!ns) { | ||
1145 | info = "namespace does not exist"; | ||
1146 | error = -ENOENT; | ||
1147 | goto fail; | ||
1148 | } | ||
1149 | } | ||
1150 | } else | ||
1151 | /* released below */ | ||
1152 | ns = aa_get_namespace(root); | ||
1153 | |||
1154 | write_lock(&ns->lock); | ||
1155 | if (!name) { | ||
1156 | /* remove namespace - can only happen if fqname[0] == ':' */ | ||
1157 | __remove_namespace(ns); | ||
1158 | } else { | ||
1159 | /* remove profile */ | ||
1160 | profile = aa_get_profile(__lookup_profile(&ns->base, name)); | ||
1161 | if (!profile) { | ||
1162 | error = -ENOENT; | ||
1163 | info = "profile does not exist"; | ||
1164 | goto fail_ns_lock; | ||
1165 | } | ||
1166 | name = profile->base.hname; | ||
1167 | __remove_profile(profile); | ||
1168 | } | ||
1169 | write_unlock(&ns->lock); | ||
1170 | |||
1171 | /* don't fail removal if audit fails */ | ||
1172 | (void) audit_policy(OP_PROF_RM, GFP_KERNEL, name, info, error); | ||
1173 | aa_put_namespace(ns); | ||
1174 | aa_put_profile(profile); | ||
1175 | return size; | ||
1176 | |||
1177 | fail_ns_lock: | ||
1178 | write_unlock(&ns->lock); | ||
1179 | aa_put_namespace(ns); | ||
1180 | |||
1181 | fail: | ||
1182 | (void) audit_policy(OP_PROF_RM, GFP_KERNEL, name, info, error); | ||
1183 | return error; | ||
1184 | } | ||
diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c new file mode 100644 index 00000000000..eb3700e9fd3 --- /dev/null +++ b/security/apparmor/policy_unpack.c | |||
@@ -0,0 +1,703 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor functions for unpacking policy loaded from | ||
5 | * userspace. | ||
6 | * | ||
7 | * Copyright (C) 1998-2008 Novell/SUSE | ||
8 | * Copyright 2009-2010 Canonical Ltd. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or | ||
11 | * modify it under the terms of the GNU General Public License as | ||
12 | * published by the Free Software Foundation, version 2 of the | ||
13 | * License. | ||
14 | * | ||
15 | * AppArmor uses a serialized binary format for loading policy. | ||
16 | * To find policy format documentation look in Documentation/apparmor.txt | ||
17 | * All policy is validated before it is used. | ||
18 | */ | ||
19 | |||
20 | #include <asm/unaligned.h> | ||
21 | #include <linux/ctype.h> | ||
22 | #include <linux/errno.h> | ||
23 | |||
24 | #include "include/apparmor.h" | ||
25 | #include "include/audit.h" | ||
26 | #include "include/context.h" | ||
27 | #include "include/match.h" | ||
28 | #include "include/policy.h" | ||
29 | #include "include/policy_unpack.h" | ||
30 | #include "include/sid.h" | ||
31 | |||
32 | /* | ||
33 | * The AppArmor interface treats data as a type byte followed by the | ||
34 | * actual data. The interface has the notion of a a named entry | ||
35 | * which has a name (AA_NAME typecode followed by name string) followed by | ||
36 | * the entries typecode and data. Named types allow for optional | ||
37 | * elements and extensions to be added and tested for without breaking | ||
38 | * backwards compatibility. | ||
39 | */ | ||
40 | |||
41 | enum aa_code { | ||
42 | AA_U8, | ||
43 | AA_U16, | ||
44 | AA_U32, | ||
45 | AA_U64, | ||
46 | AA_NAME, /* same as string except it is items name */ | ||
47 | AA_STRING, | ||
48 | AA_BLOB, | ||
49 | AA_STRUCT, | ||
50 | AA_STRUCTEND, | ||
51 | AA_LIST, | ||
52 | AA_LISTEND, | ||
53 | AA_ARRAY, | ||
54 | AA_ARRAYEND, | ||
55 | }; | ||
56 | |||
57 | /* | ||
58 | * aa_ext is the read of the buffer containing the serialized profile. The | ||
59 | * data is copied into a kernel buffer in apparmorfs and then handed off to | ||
60 | * the unpack routines. | ||
61 | */ | ||
62 | struct aa_ext { | ||
63 | void *start; | ||
64 | void *end; | ||
65 | void *pos; /* pointer to current position in the buffer */ | ||
66 | u32 version; | ||
67 | }; | ||
68 | |||
69 | /* audit callback for unpack fields */ | ||
70 | static void audit_cb(struct audit_buffer *ab, void *va) | ||
71 | { | ||
72 | struct common_audit_data *sa = va; | ||
73 | if (sa->aad.iface.target) { | ||
74 | struct aa_profile *name = sa->aad.iface.target; | ||
75 | audit_log_format(ab, " name="); | ||
76 | audit_log_untrustedstring(ab, name->base.hname); | ||
77 | } | ||
78 | if (sa->aad.iface.pos) | ||
79 | audit_log_format(ab, " offset=%ld", sa->aad.iface.pos); | ||
80 | } | ||
81 | |||
82 | /** | ||
83 | * audit_iface - do audit message for policy unpacking/load/replace/remove | ||
84 | * @new: profile if it has been allocated (MAYBE NULL) | ||
85 | * @name: name of the profile being manipulated (MAYBE NULL) | ||
86 | * @info: any extra info about the failure (MAYBE NULL) | ||
87 | * @e: buffer position info (NOT NULL) | ||
88 | * @error: error code | ||
89 | * | ||
90 | * Returns: %0 or error | ||
91 | */ | ||
92 | static int audit_iface(struct aa_profile *new, const char *name, | ||
93 | const char *info, struct aa_ext *e, int error) | ||
94 | { | ||
95 | struct aa_profile *profile = __aa_current_profile(); | ||
96 | struct common_audit_data sa; | ||
97 | COMMON_AUDIT_DATA_INIT(&sa, NONE); | ||
98 | sa.aad.iface.pos = e->pos - e->start; | ||
99 | sa.aad.iface.target = new; | ||
100 | sa.aad.name = name; | ||
101 | sa.aad.info = info; | ||
102 | sa.aad.error = error; | ||
103 | |||
104 | return aa_audit(AUDIT_APPARMOR_STATUS, profile, GFP_KERNEL, &sa, | ||
105 | audit_cb); | ||
106 | } | ||
107 | |||
108 | /* test if read will be in packed data bounds */ | ||
109 | static bool inbounds(struct aa_ext *e, size_t size) | ||
110 | { | ||
111 | return (size <= e->end - e->pos); | ||
112 | } | ||
113 | |||
114 | /** | ||
115 | * aa_u16_chunck - test and do bounds checking for a u16 size based chunk | ||
116 | * @e: serialized data read head (NOT NULL) | ||
117 | * @chunk: start address for chunk of data (NOT NULL) | ||
118 | * | ||
119 | * Returns: the size of chunk found with the read head at the end of the chunk. | ||
120 | */ | ||
121 | static size_t unpack_u16_chunk(struct aa_ext *e, char **chunk) | ||
122 | { | ||
123 | size_t size = 0; | ||
124 | |||
125 | if (!inbounds(e, sizeof(u16))) | ||
126 | return 0; | ||
127 | size = le16_to_cpu(get_unaligned((u16 *) e->pos)); | ||
128 | e->pos += sizeof(u16); | ||
129 | if (!inbounds(e, size)) | ||
130 | return 0; | ||
131 | *chunk = e->pos; | ||
132 | e->pos += size; | ||
133 | return size; | ||
134 | } | ||
135 | |||
136 | /* unpack control byte */ | ||
137 | static bool unpack_X(struct aa_ext *e, enum aa_code code) | ||
138 | { | ||
139 | if (!inbounds(e, 1)) | ||
140 | return 0; | ||
141 | if (*(u8 *) e->pos != code) | ||
142 | return 0; | ||
143 | e->pos++; | ||
144 | return 1; | ||
145 | } | ||
146 | |||
147 | /** | ||
148 | * unpack_nameX - check is the next element is of type X with a name of @name | ||
149 | * @e: serialized data extent information (NOT NULL) | ||
150 | * @code: type code | ||
151 | * @name: name to match to the serialized element. (MAYBE NULL) | ||
152 | * | ||
153 | * check that the next serialized data element is of type X and has a tag | ||
154 | * name @name. If @name is specified then there must be a matching | ||
155 | * name element in the stream. If @name is NULL any name element will be | ||
156 | * skipped and only the typecode will be tested. | ||
157 | * | ||
158 | * Returns 1 on success (both type code and name tests match) and the read | ||
159 | * head is advanced past the headers | ||
160 | * | ||
161 | * Returns: 0 if either match fails, the read head does not move | ||
162 | */ | ||
163 | static bool unpack_nameX(struct aa_ext *e, enum aa_code code, const char *name) | ||
164 | { | ||
165 | /* | ||
166 | * May need to reset pos if name or type doesn't match | ||
167 | */ | ||
168 | void *pos = e->pos; | ||
169 | /* | ||
170 | * Check for presence of a tagname, and if present name size | ||
171 | * AA_NAME tag value is a u16. | ||
172 | */ | ||
173 | if (unpack_X(e, AA_NAME)) { | ||
174 | char *tag = NULL; | ||
175 | size_t size = unpack_u16_chunk(e, &tag); | ||
176 | /* if a name is specified it must match. otherwise skip tag */ | ||
177 | if (name && (!size || strcmp(name, tag))) | ||
178 | goto fail; | ||
179 | } else if (name) { | ||
180 | /* if a name is specified and there is no name tag fail */ | ||
181 | goto fail; | ||
182 | } | ||
183 | |||
184 | /* now check if type code matches */ | ||
185 | if (unpack_X(e, code)) | ||
186 | return 1; | ||
187 | |||
188 | fail: | ||
189 | e->pos = pos; | ||
190 | return 0; | ||
191 | } | ||
192 | |||
193 | static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) | ||
194 | { | ||
195 | if (unpack_nameX(e, AA_U32, name)) { | ||
196 | if (!inbounds(e, sizeof(u32))) | ||
197 | return 0; | ||
198 | if (data) | ||
199 | *data = le32_to_cpu(get_unaligned((u32 *) e->pos)); | ||
200 | e->pos += sizeof(u32); | ||
201 | return 1; | ||
202 | } | ||
203 | return 0; | ||
204 | } | ||
205 | |||
206 | static bool unpack_u64(struct aa_ext *e, u64 *data, const char *name) | ||
207 | { | ||
208 | if (unpack_nameX(e, AA_U64, name)) { | ||
209 | if (!inbounds(e, sizeof(u64))) | ||
210 | return 0; | ||
211 | if (data) | ||
212 | *data = le64_to_cpu(get_unaligned((u64 *) e->pos)); | ||
213 | e->pos += sizeof(u64); | ||
214 | return 1; | ||
215 | } | ||
216 | return 0; | ||
217 | } | ||
218 | |||
219 | static size_t unpack_array(struct aa_ext *e, const char *name) | ||
220 | { | ||
221 | if (unpack_nameX(e, AA_ARRAY, name)) { | ||
222 | int size; | ||
223 | if (!inbounds(e, sizeof(u16))) | ||
224 | return 0; | ||
225 | size = (int)le16_to_cpu(get_unaligned((u16 *) e->pos)); | ||
226 | e->pos += sizeof(u16); | ||
227 | return size; | ||
228 | } | ||
229 | return 0; | ||
230 | } | ||
231 | |||
232 | static size_t unpack_blob(struct aa_ext *e, char **blob, const char *name) | ||
233 | { | ||
234 | if (unpack_nameX(e, AA_BLOB, name)) { | ||
235 | u32 size; | ||
236 | if (!inbounds(e, sizeof(u32))) | ||
237 | return 0; | ||
238 | size = le32_to_cpu(get_unaligned((u32 *) e->pos)); | ||
239 | e->pos += sizeof(u32); | ||
240 | if (inbounds(e, (size_t) size)) { | ||
241 | *blob = e->pos; | ||
242 | e->pos += size; | ||
243 | return size; | ||
244 | } | ||
245 | } | ||
246 | return 0; | ||
247 | } | ||
248 | |||
249 | static int unpack_str(struct aa_ext *e, const char **string, const char *name) | ||
250 | { | ||
251 | char *src_str; | ||
252 | size_t size = 0; | ||
253 | void *pos = e->pos; | ||
254 | *string = NULL; | ||
255 | if (unpack_nameX(e, AA_STRING, name)) { | ||
256 | size = unpack_u16_chunk(e, &src_str); | ||
257 | if (size) { | ||
258 | /* strings are null terminated, length is size - 1 */ | ||
259 | if (src_str[size - 1] != 0) | ||
260 | goto fail; | ||
261 | *string = src_str; | ||
262 | } | ||
263 | } | ||
264 | return size; | ||
265 | |||
266 | fail: | ||
267 | e->pos = pos; | ||
268 | return 0; | ||
269 | } | ||
270 | |||
271 | static int unpack_strdup(struct aa_ext *e, char **string, const char *name) | ||
272 | { | ||
273 | const char *tmp; | ||
274 | void *pos = e->pos; | ||
275 | int res = unpack_str(e, &tmp, name); | ||
276 | *string = NULL; | ||
277 | |||
278 | if (!res) | ||
279 | return 0; | ||
280 | |||
281 | *string = kmemdup(tmp, res, GFP_KERNEL); | ||
282 | if (!*string) { | ||
283 | e->pos = pos; | ||
284 | return 0; | ||
285 | } | ||
286 | |||
287 | return res; | ||
288 | } | ||
289 | |||
290 | /** | ||
291 | * verify_accept - verify the accept tables of a dfa | ||
292 | * @dfa: dfa to verify accept tables of (NOT NULL) | ||
293 | * @flags: flags governing dfa | ||
294 | * | ||
295 | * Returns: 1 if valid accept tables else 0 if error | ||
296 | */ | ||
297 | static bool verify_accept(struct aa_dfa *dfa, int flags) | ||
298 | { | ||
299 | int i; | ||
300 | |||
301 | /* verify accept permissions */ | ||
302 | for (i = 0; i < dfa->tables[YYTD_ID_ACCEPT]->td_lolen; i++) { | ||
303 | int mode = ACCEPT_TABLE(dfa)[i]; | ||
304 | |||
305 | if (mode & ~DFA_VALID_PERM_MASK) | ||
306 | return 0; | ||
307 | |||
308 | if (ACCEPT_TABLE2(dfa)[i] & ~DFA_VALID_PERM2_MASK) | ||
309 | return 0; | ||
310 | } | ||
311 | return 1; | ||
312 | } | ||
313 | |||
314 | /** | ||
315 | * unpack_dfa - unpack a file rule dfa | ||
316 | * @e: serialized data extent information (NOT NULL) | ||
317 | * | ||
318 | * returns dfa or ERR_PTR or NULL if no dfa | ||
319 | */ | ||
320 | static struct aa_dfa *unpack_dfa(struct aa_ext *e) | ||
321 | { | ||
322 | char *blob = NULL; | ||
323 | size_t size; | ||
324 | struct aa_dfa *dfa = NULL; | ||
325 | |||
326 | size = unpack_blob(e, &blob, "aadfa"); | ||
327 | if (size) { | ||
328 | /* | ||
329 | * The dfa is aligned with in the blob to 8 bytes | ||
330 | * from the beginning of the stream. | ||
331 | */ | ||
332 | size_t sz = blob - (char *)e->start; | ||
333 | size_t pad = ALIGN(sz, 8) - sz; | ||
334 | int flags = TO_ACCEPT1_FLAG(YYTD_DATA32) | | ||
335 | TO_ACCEPT2_FLAG(YYTD_DATA32); | ||
336 | |||
337 | |||
338 | if (aa_g_paranoid_load) | ||
339 | flags |= DFA_FLAG_VERIFY_STATES; | ||
340 | |||
341 | dfa = aa_dfa_unpack(blob + pad, size - pad, flags); | ||
342 | |||
343 | if (IS_ERR(dfa)) | ||
344 | return dfa; | ||
345 | |||
346 | if (!verify_accept(dfa, flags)) | ||
347 | goto fail; | ||
348 | } | ||
349 | |||
350 | return dfa; | ||
351 | |||
352 | fail: | ||
353 | aa_put_dfa(dfa); | ||
354 | return ERR_PTR(-EPROTO); | ||
355 | } | ||
356 | |||
357 | /** | ||
358 | * unpack_trans_table - unpack a profile transition table | ||
359 | * @e: serialized data extent information (NOT NULL) | ||
360 | * @profile: profile to add the accept table to (NOT NULL) | ||
361 | * | ||
362 | * Returns: 1 if table succesfully unpacked | ||
363 | */ | ||
364 | static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile) | ||
365 | { | ||
366 | void *pos = e->pos; | ||
367 | |||
368 | /* exec table is optional */ | ||
369 | if (unpack_nameX(e, AA_STRUCT, "xtable")) { | ||
370 | int i, size; | ||
371 | |||
372 | size = unpack_array(e, NULL); | ||
373 | /* currently 4 exec bits and entries 0-3 are reserved iupcx */ | ||
374 | if (size > 16 - 4) | ||
375 | goto fail; | ||
376 | profile->file.trans.table = kzalloc(sizeof(char *) * size, | ||
377 | GFP_KERNEL); | ||
378 | if (!profile->file.trans.table) | ||
379 | goto fail; | ||
380 | |||
381 | profile->file.trans.size = size; | ||
382 | for (i = 0; i < size; i++) { | ||
383 | char *str; | ||
384 | int c, j, size = unpack_strdup(e, &str, NULL); | ||
385 | /* unpack_strdup verifies that the last character is | ||
386 | * null termination byte. | ||
387 | */ | ||
388 | if (!size) | ||
389 | goto fail; | ||
390 | profile->file.trans.table[i] = str; | ||
391 | /* verify that name doesn't start with space */ | ||
392 | if (isspace(*str)) | ||
393 | goto fail; | ||
394 | |||
395 | /* count internal # of internal \0 */ | ||
396 | for (c = j = 0; j < size - 2; j++) { | ||
397 | if (!str[j]) | ||
398 | c++; | ||
399 | } | ||
400 | if (*str == ':') { | ||
401 | /* beginning with : requires an embedded \0, | ||
402 | * verify that exactly 1 internal \0 exists | ||
403 | * trailing \0 already verified by unpack_strdup | ||
404 | */ | ||
405 | if (c != 1) | ||
406 | goto fail; | ||
407 | /* first character after : must be valid */ | ||
408 | if (!str[1]) | ||
409 | goto fail; | ||
410 | } else if (c) | ||
411 | /* fail - all other cases with embedded \0 */ | ||
412 | goto fail; | ||
413 | } | ||
414 | if (!unpack_nameX(e, AA_ARRAYEND, NULL)) | ||
415 | goto fail; | ||
416 | if (!unpack_nameX(e, AA_STRUCTEND, NULL)) | ||
417 | goto fail; | ||
418 | } | ||
419 | return 1; | ||
420 | |||
421 | fail: | ||
422 | aa_free_domain_entries(&profile->file.trans); | ||
423 | e->pos = pos; | ||
424 | return 0; | ||
425 | } | ||
426 | |||
427 | static bool unpack_rlimits(struct aa_ext *e, struct aa_profile *profile) | ||
428 | { | ||
429 | void *pos = e->pos; | ||
430 | |||
431 | /* rlimits are optional */ | ||
432 | if (unpack_nameX(e, AA_STRUCT, "rlimits")) { | ||
433 | int i, size; | ||
434 | u32 tmp = 0; | ||
435 | if (!unpack_u32(e, &tmp, NULL)) | ||
436 | goto fail; | ||
437 | profile->rlimits.mask = tmp; | ||
438 | |||
439 | size = unpack_array(e, NULL); | ||
440 | if (size > RLIM_NLIMITS) | ||
441 | goto fail; | ||
442 | for (i = 0; i < size; i++) { | ||
443 | u64 tmp = 0; | ||
444 | int a = aa_map_resource(i); | ||
445 | if (!unpack_u64(e, &tmp, NULL)) | ||
446 | goto fail; | ||
447 | profile->rlimits.limits[a].rlim_max = tmp; | ||
448 | } | ||
449 | if (!unpack_nameX(e, AA_ARRAYEND, NULL)) | ||
450 | goto fail; | ||
451 | if (!unpack_nameX(e, AA_STRUCTEND, NULL)) | ||
452 | goto fail; | ||
453 | } | ||
454 | return 1; | ||
455 | |||
456 | fail: | ||
457 | e->pos = pos; | ||
458 | return 0; | ||
459 | } | ||
460 | |||
461 | /** | ||
462 | * unpack_profile - unpack a serialized profile | ||
463 | * @e: serialized data extent information (NOT NULL) | ||
464 | * | ||
465 | * NOTE: unpack profile sets audit struct if there is a failure | ||
466 | */ | ||
467 | static struct aa_profile *unpack_profile(struct aa_ext *e) | ||
468 | { | ||
469 | struct aa_profile *profile = NULL; | ||
470 | const char *name = NULL; | ||
471 | int error = -EPROTO; | ||
472 | kernel_cap_t tmpcap; | ||
473 | u32 tmp; | ||
474 | |||
475 | /* check that we have the right struct being passed */ | ||
476 | if (!unpack_nameX(e, AA_STRUCT, "profile")) | ||
477 | goto fail; | ||
478 | if (!unpack_str(e, &name, NULL)) | ||
479 | goto fail; | ||
480 | |||
481 | profile = aa_alloc_profile(name); | ||
482 | if (!profile) | ||
483 | return ERR_PTR(-ENOMEM); | ||
484 | |||
485 | /* profile renaming is optional */ | ||
486 | (void) unpack_str(e, &profile->rename, "rename"); | ||
487 | |||
488 | /* xmatch is optional and may be NULL */ | ||
489 | profile->xmatch = unpack_dfa(e); | ||
490 | if (IS_ERR(profile->xmatch)) { | ||
491 | error = PTR_ERR(profile->xmatch); | ||
492 | profile->xmatch = NULL; | ||
493 | goto fail; | ||
494 | } | ||
495 | /* xmatch_len is not optional if xmatch is set */ | ||
496 | if (profile->xmatch) { | ||
497 | if (!unpack_u32(e, &tmp, NULL)) | ||
498 | goto fail; | ||
499 | profile->xmatch_len = tmp; | ||
500 | } | ||
501 | |||
502 | /* per profile debug flags (complain, audit) */ | ||
503 | if (!unpack_nameX(e, AA_STRUCT, "flags")) | ||
504 | goto fail; | ||
505 | if (!unpack_u32(e, &tmp, NULL)) | ||
506 | goto fail; | ||
507 | if (tmp) | ||
508 | profile->flags |= PFLAG_HAT; | ||
509 | if (!unpack_u32(e, &tmp, NULL)) | ||
510 | goto fail; | ||
511 | if (tmp) | ||
512 | profile->mode = APPARMOR_COMPLAIN; | ||
513 | if (!unpack_u32(e, &tmp, NULL)) | ||
514 | goto fail; | ||
515 | if (tmp) | ||
516 | profile->audit = AUDIT_ALL; | ||
517 | |||
518 | if (!unpack_nameX(e, AA_STRUCTEND, NULL)) | ||
519 | goto fail; | ||
520 | |||
521 | /* path_flags is optional */ | ||
522 | if (unpack_u32(e, &profile->path_flags, "path_flags")) | ||
523 | profile->path_flags |= profile->flags & PFLAG_MEDIATE_DELETED; | ||
524 | else | ||
525 | /* set a default value if path_flags field is not present */ | ||
526 | profile->path_flags = PFLAG_MEDIATE_DELETED; | ||
527 | |||
528 | if (!unpack_u32(e, &(profile->caps.allow.cap[0]), NULL)) | ||
529 | goto fail; | ||
530 | if (!unpack_u32(e, &(profile->caps.audit.cap[0]), NULL)) | ||
531 | goto fail; | ||
532 | if (!unpack_u32(e, &(profile->caps.quiet.cap[0]), NULL)) | ||
533 | goto fail; | ||
534 | if (!unpack_u32(e, &tmpcap.cap[0], NULL)) | ||
535 | goto fail; | ||
536 | |||
537 | if (unpack_nameX(e, AA_STRUCT, "caps64")) { | ||
538 | /* optional upper half of 64 bit caps */ | ||
539 | if (!unpack_u32(e, &(profile->caps.allow.cap[1]), NULL)) | ||
540 | goto fail; | ||
541 | if (!unpack_u32(e, &(profile->caps.audit.cap[1]), NULL)) | ||
542 | goto fail; | ||
543 | if (!unpack_u32(e, &(profile->caps.quiet.cap[1]), NULL)) | ||
544 | goto fail; | ||
545 | if (!unpack_u32(e, &(tmpcap.cap[1]), NULL)) | ||
546 | goto fail; | ||
547 | if (!unpack_nameX(e, AA_STRUCTEND, NULL)) | ||
548 | goto fail; | ||
549 | } | ||
550 | |||
551 | if (unpack_nameX(e, AA_STRUCT, "capsx")) { | ||
552 | /* optional extended caps mediation mask */ | ||
553 | if (!unpack_u32(e, &(profile->caps.extended.cap[0]), NULL)) | ||
554 | goto fail; | ||
555 | if (!unpack_u32(e, &(profile->caps.extended.cap[1]), NULL)) | ||
556 | goto fail; | ||
557 | } | ||
558 | |||
559 | if (!unpack_rlimits(e, profile)) | ||
560 | goto fail; | ||
561 | |||
562 | /* get file rules */ | ||
563 | profile->file.dfa = unpack_dfa(e); | ||
564 | if (IS_ERR(profile->file.dfa)) { | ||
565 | error = PTR_ERR(profile->file.dfa); | ||
566 | profile->file.dfa = NULL; | ||
567 | goto fail; | ||
568 | } | ||
569 | |||
570 | if (!unpack_u32(e, &profile->file.start, "dfa_start")) | ||
571 | /* default start state */ | ||
572 | profile->file.start = DFA_START; | ||
573 | |||
574 | if (!unpack_trans_table(e, profile)) | ||
575 | goto fail; | ||
576 | |||
577 | if (!unpack_nameX(e, AA_STRUCTEND, NULL)) | ||
578 | goto fail; | ||
579 | |||
580 | return profile; | ||
581 | |||
582 | fail: | ||
583 | if (profile) | ||
584 | name = NULL; | ||
585 | else if (!name) | ||
586 | name = "unknown"; | ||
587 | audit_iface(profile, name, "failed to unpack profile", e, error); | ||
588 | aa_put_profile(profile); | ||
589 | |||
590 | return ERR_PTR(error); | ||
591 | } | ||
592 | |||
593 | /** | ||
594 | * verify_head - unpack serialized stream header | ||
595 | * @e: serialized data read head (NOT NULL) | ||
596 | * @ns: Returns - namespace if one is specified else NULL (NOT NULL) | ||
597 | * | ||
598 | * Returns: error or 0 if header is good | ||
599 | */ | ||
600 | static int verify_header(struct aa_ext *e, const char **ns) | ||
601 | { | ||
602 | int error = -EPROTONOSUPPORT; | ||
603 | /* get the interface version */ | ||
604 | if (!unpack_u32(e, &e->version, "version")) { | ||
605 | audit_iface(NULL, NULL, "invalid profile format", e, error); | ||
606 | return error; | ||
607 | } | ||
608 | |||
609 | /* check that the interface version is currently supported */ | ||
610 | if (e->version != 5) { | ||
611 | audit_iface(NULL, NULL, "unsupported interface version", e, | ||
612 | error); | ||
613 | return error; | ||
614 | } | ||
615 | |||
616 | /* read the namespace if present */ | ||
617 | if (!unpack_str(e, ns, "namespace")) | ||
618 | *ns = NULL; | ||
619 | |||
620 | return 0; | ||
621 | } | ||
622 | |||
623 | static bool verify_xindex(int xindex, int table_size) | ||
624 | { | ||
625 | int index, xtype; | ||
626 | xtype = xindex & AA_X_TYPE_MASK; | ||
627 | index = xindex & AA_X_INDEX_MASK; | ||
628 | if (xtype == AA_X_TABLE && index > table_size) | ||
629 | return 0; | ||
630 | return 1; | ||
631 | } | ||
632 | |||
633 | /* verify dfa xindexes are in range of transition tables */ | ||
634 | static bool verify_dfa_xindex(struct aa_dfa *dfa, int table_size) | ||
635 | { | ||
636 | int i; | ||
637 | for (i = 0; i < dfa->tables[YYTD_ID_ACCEPT]->td_lolen; i++) { | ||
638 | if (!verify_xindex(dfa_user_xindex(dfa, i), table_size)) | ||
639 | return 0; | ||
640 | if (!verify_xindex(dfa_other_xindex(dfa, i), table_size)) | ||
641 | return 0; | ||
642 | } | ||
643 | return 1; | ||
644 | } | ||
645 | |||
646 | /** | ||
647 | * verify_profile - Do post unpack analysis to verify profile consistency | ||
648 | * @profile: profile to verify (NOT NULL) | ||
649 | * | ||
650 | * Returns: 0 if passes verification else error | ||
651 | */ | ||
652 | static int verify_profile(struct aa_profile *profile) | ||
653 | { | ||
654 | if (aa_g_paranoid_load) { | ||
655 | if (profile->file.dfa && | ||
656 | !verify_dfa_xindex(profile->file.dfa, | ||
657 | profile->file.trans.size)) { | ||
658 | audit_iface(profile, NULL, "Invalid named transition", | ||
659 | NULL, -EPROTO); | ||
660 | return -EPROTO; | ||
661 | } | ||
662 | } | ||
663 | |||
664 | return 0; | ||
665 | } | ||
666 | |||
667 | /** | ||
668 | * aa_unpack - unpack packed binary profile data loaded from user space | ||
669 | * @udata: user data copied to kmem (NOT NULL) | ||
670 | * @size: the size of the user data | ||
671 | * @ns: Returns namespace profile is in if specified else NULL (NOT NULL) | ||
672 | * | ||
673 | * Unpack user data and return refcounted allocated profile or ERR_PTR | ||
674 | * | ||
675 | * Returns: profile else error pointer if fails to unpack | ||
676 | */ | ||
677 | struct aa_profile *aa_unpack(void *udata, size_t size, const char **ns) | ||
678 | { | ||
679 | struct aa_profile *profile = NULL; | ||
680 | int error; | ||
681 | struct aa_ext e = { | ||
682 | .start = udata, | ||
683 | .end = udata + size, | ||
684 | .pos = udata, | ||
685 | }; | ||
686 | |||
687 | error = verify_header(&e, ns); | ||
688 | if (error) | ||
689 | return ERR_PTR(error); | ||
690 | |||
691 | profile = unpack_profile(&e); | ||
692 | if (IS_ERR(profile)) | ||
693 | return profile; | ||
694 | |||
695 | error = verify_profile(profile); | ||
696 | if (error) { | ||
697 | aa_put_profile(profile); | ||
698 | profile = ERR_PTR(error); | ||
699 | } | ||
700 | |||
701 | /* return refcount */ | ||
702 | return profile; | ||
703 | } | ||
diff --git a/security/apparmor/procattr.c b/security/apparmor/procattr.c new file mode 100644 index 00000000000..04a2cf8d1b6 --- /dev/null +++ b/security/apparmor/procattr.c | |||
@@ -0,0 +1,170 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor /proc/<pid>/attr/ interface functions | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #include "include/apparmor.h" | ||
16 | #include "include/context.h" | ||
17 | #include "include/policy.h" | ||
18 | #include "include/domain.h" | ||
19 | |||
20 | |||
21 | /** | ||
22 | * aa_getprocattr - Return the profile information for @profile | ||
23 | * @profile: the profile to print profile info about (NOT NULL) | ||
24 | * @string: Returns - string containing the profile info (NOT NULL) | ||
25 | * | ||
26 | * Returns: length of @string on success else error on failure | ||
27 | * | ||
28 | * Requires: profile != NULL | ||
29 | * | ||
30 | * Creates a string containing the namespace_name://profile_name for | ||
31 | * @profile. | ||
32 | * | ||
33 | * Returns: size of string placed in @string else error code on failure | ||
34 | */ | ||
35 | int aa_getprocattr(struct aa_profile *profile, char **string) | ||
36 | { | ||
37 | char *str; | ||
38 | int len = 0, mode_len = 0, ns_len = 0, name_len; | ||
39 | const char *mode_str = profile_mode_names[profile->mode]; | ||
40 | const char *ns_name = NULL; | ||
41 | struct aa_namespace *ns = profile->ns; | ||
42 | struct aa_namespace *current_ns = __aa_current_profile()->ns; | ||
43 | char *s; | ||
44 | |||
45 | if (!aa_ns_visible(current_ns, ns)) | ||
46 | return -EACCES; | ||
47 | |||
48 | ns_name = aa_ns_name(current_ns, ns); | ||
49 | ns_len = strlen(ns_name); | ||
50 | |||
51 | /* if the visible ns_name is > 0 increase size for : :// seperator */ | ||
52 | if (ns_len) | ||
53 | ns_len += 4; | ||
54 | |||
55 | /* unconfined profiles don't have a mode string appended */ | ||
56 | if (!unconfined(profile)) | ||
57 | mode_len = strlen(mode_str) + 3; /* + 3 for _() */ | ||
58 | |||
59 | name_len = strlen(profile->base.hname); | ||
60 | len = mode_len + ns_len + name_len + 1; /* + 1 for \n */ | ||
61 | s = str = kmalloc(len + 1, GFP_KERNEL); /* + 1 \0 */ | ||
62 | if (!str) | ||
63 | return -ENOMEM; | ||
64 | |||
65 | if (ns_len) { | ||
66 | /* skip over prefix current_ns->base.hname and separating // */ | ||
67 | sprintf(s, ":%s://", ns_name); | ||
68 | s += ns_len; | ||
69 | } | ||
70 | if (unconfined(profile)) | ||
71 | /* mode string not being appended */ | ||
72 | sprintf(s, "%s\n", profile->base.hname); | ||
73 | else | ||
74 | sprintf(s, "%s (%s)\n", profile->base.hname, mode_str); | ||
75 | *string = str; | ||
76 | |||
77 | /* NOTE: len does not include \0 of string, not saved as part of file */ | ||
78 | return len; | ||
79 | } | ||
80 | |||
81 | /** | ||
82 | * split_token_from_name - separate a string of form <token>^<name> | ||
83 | * @op: operation being checked | ||
84 | * @args: string to parse (NOT NULL) | ||
85 | * @token: stores returned parsed token value (NOT NULL) | ||
86 | * | ||
87 | * Returns: start position of name after token else NULL on failure | ||
88 | */ | ||
89 | static char *split_token_from_name(int op, char *args, u64 * token) | ||
90 | { | ||
91 | char *name; | ||
92 | |||
93 | *token = simple_strtoull(args, &name, 16); | ||
94 | if ((name == args) || *name != '^') { | ||
95 | AA_ERROR("%s: Invalid input '%s'", op_table[op], args); | ||
96 | return ERR_PTR(-EINVAL); | ||
97 | } | ||
98 | |||
99 | name++; /* skip ^ */ | ||
100 | if (!*name) | ||
101 | name = NULL; | ||
102 | return name; | ||
103 | } | ||
104 | |||
105 | /** | ||
106 | * aa_setprocattr_chagnehat - handle procattr interface to change_hat | ||
107 | * @args: args received from writing to /proc/<pid>/attr/current (NOT NULL) | ||
108 | * @size: size of the args | ||
109 | * @test: true if this is a test of change_hat permissions | ||
110 | * | ||
111 | * Returns: %0 or error code if change_hat fails | ||
112 | */ | ||
113 | int aa_setprocattr_changehat(char *args, size_t size, int test) | ||
114 | { | ||
115 | char *hat; | ||
116 | u64 token; | ||
117 | const char *hats[16]; /* current hard limit on # of names */ | ||
118 | int count = 0; | ||
119 | |||
120 | hat = split_token_from_name(OP_CHANGE_HAT, args, &token); | ||
121 | if (IS_ERR(hat)) | ||
122 | return PTR_ERR(hat); | ||
123 | |||
124 | if (!hat && !token) { | ||
125 | AA_ERROR("change_hat: Invalid input, NULL hat and NULL magic"); | ||
126 | return -EINVAL; | ||
127 | } | ||
128 | |||
129 | if (hat) { | ||
130 | /* set up hat name vector, args guaranteed null terminated | ||
131 | * at args[size] by setprocattr. | ||
132 | * | ||
133 | * If there are multiple hat names in the buffer each is | ||
134 | * separated by a \0. Ie. userspace writes them pre tokenized | ||
135 | */ | ||
136 | char *end = args + size; | ||
137 | for (count = 0; (hat < end) && count < 16; ++count) { | ||
138 | char *next = hat + strlen(hat) + 1; | ||
139 | hats[count] = hat; | ||
140 | hat = next; | ||
141 | } | ||
142 | } | ||
143 | |||
144 | AA_DEBUG("%s: Magic 0x%llx Hat '%s'\n", | ||
145 | __func__, token, hat ? hat : NULL); | ||
146 | |||
147 | return aa_change_hat(hats, count, token, test); | ||
148 | } | ||
149 | |||
150 | /** | ||
151 | * aa_setprocattr_changeprofile - handle procattr interface to changeprofile | ||
152 | * @fqname: args received from writting to /proc/<pid>/attr/current (NOT NULL) | ||
153 | * @onexec: true if change_profile should be delayed until exec | ||
154 | * @test: true if this is a test of change_profile permissions | ||
155 | * | ||
156 | * Returns: %0 or error code if change_profile fails | ||
157 | */ | ||
158 | int aa_setprocattr_changeprofile(char *fqname, bool onexec, int test) | ||
159 | { | ||
160 | char *name, *ns_name; | ||
161 | |||
162 | name = aa_split_fqname(fqname, &ns_name); | ||
163 | return aa_change_profile(ns_name, name, onexec, test); | ||
164 | } | ||
165 | |||
166 | int aa_setprocattr_permipc(char *fqname) | ||
167 | { | ||
168 | /* TODO: add ipc permission querying */ | ||
169 | return -ENOTSUPP; | ||
170 | } | ||
diff --git a/security/apparmor/resource.c b/security/apparmor/resource.c new file mode 100644 index 00000000000..4a368f1fd36 --- /dev/null +++ b/security/apparmor/resource.c | |||
@@ -0,0 +1,134 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor resource mediation and attachment | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #include <linux/audit.h> | ||
16 | |||
17 | #include "include/audit.h" | ||
18 | #include "include/resource.h" | ||
19 | #include "include/policy.h" | ||
20 | |||
21 | /* | ||
22 | * Table of rlimit names: we generate it from resource.h. | ||
23 | */ | ||
24 | #include "rlim_names.h" | ||
25 | |||
26 | /* audit callback for resource specific fields */ | ||
27 | static void audit_cb(struct audit_buffer *ab, void *va) | ||
28 | { | ||
29 | struct common_audit_data *sa = va; | ||
30 | |||
31 | audit_log_format(ab, " rlimit=%s value=%lu", | ||
32 | rlim_names[sa->aad.rlim.rlim], sa->aad.rlim.max); | ||
33 | } | ||
34 | |||
35 | /** | ||
36 | * audit_resource - audit setting resource limit | ||
37 | * @profile: profile being enforced (NOT NULL) | ||
38 | * @resoure: rlimit being auditing | ||
39 | * @value: value being set | ||
40 | * @error: error value | ||
41 | * | ||
42 | * Returns: 0 or sa->error else other error code on failure | ||
43 | */ | ||
44 | static int audit_resource(struct aa_profile *profile, unsigned int resource, | ||
45 | unsigned long value, int error) | ||
46 | { | ||
47 | struct common_audit_data sa; | ||
48 | |||
49 | COMMON_AUDIT_DATA_INIT(&sa, NONE); | ||
50 | sa.aad.op = OP_SETRLIMIT, | ||
51 | sa.aad.rlim.rlim = resource; | ||
52 | sa.aad.rlim.max = value; | ||
53 | sa.aad.error = error; | ||
54 | return aa_audit(AUDIT_APPARMOR_AUTO, profile, GFP_KERNEL, &sa, | ||
55 | audit_cb); | ||
56 | } | ||
57 | |||
58 | /** | ||
59 | * aa_map_resouce - map compiled policy resource to internal # | ||
60 | * @resource: flattened policy resource number | ||
61 | * | ||
62 | * Returns: resource # for the current architecture. | ||
63 | * | ||
64 | * rlimit resource can vary based on architecture, map the compiled policy | ||
65 | * resource # to the internal representation for the architecture. | ||
66 | */ | ||
67 | int aa_map_resource(int resource) | ||
68 | { | ||
69 | return rlim_map[resource]; | ||
70 | } | ||
71 | |||
72 | /** | ||
73 | * aa_task_setrlimit - test permission to set an rlimit | ||
74 | * @profile - profile confining the task (NOT NULL) | ||
75 | * @resource - the resource being set | ||
76 | * @new_rlim - the new resource limit (NOT NULL) | ||
77 | * | ||
78 | * Control raising the processes hard limit. | ||
79 | * | ||
80 | * Returns: 0 or error code if setting resource failed | ||
81 | */ | ||
82 | int aa_task_setrlimit(struct aa_profile *profile, unsigned int resource, | ||
83 | struct rlimit *new_rlim) | ||
84 | { | ||
85 | int error = 0; | ||
86 | |||
87 | if (profile->rlimits.mask & (1 << resource) && | ||
88 | new_rlim->rlim_max > profile->rlimits.limits[resource].rlim_max) | ||
89 | |||
90 | error = audit_resource(profile, resource, new_rlim->rlim_max, | ||
91 | -EACCES); | ||
92 | |||
93 | return error; | ||
94 | } | ||
95 | |||
96 | /** | ||
97 | * __aa_transition_rlimits - apply new profile rlimits | ||
98 | * @old: old profile on task (NOT NULL) | ||
99 | * @new: new profile with rlimits to apply (NOT NULL) | ||
100 | */ | ||
101 | void __aa_transition_rlimits(struct aa_profile *old, struct aa_profile *new) | ||
102 | { | ||
103 | unsigned int mask = 0; | ||
104 | struct rlimit *rlim, *initrlim; | ||
105 | int i; | ||
106 | |||
107 | /* for any rlimits the profile controlled reset the soft limit | ||
108 | * to the less of the tasks hard limit and the init tasks soft limit | ||
109 | */ | ||
110 | if (old->rlimits.mask) { | ||
111 | for (i = 0, mask = 1; i < RLIM_NLIMITS; i++, mask <<= 1) { | ||
112 | if (old->rlimits.mask & mask) { | ||
113 | rlim = current->signal->rlim + i; | ||
114 | initrlim = init_task.signal->rlim + i; | ||
115 | rlim->rlim_cur = min(rlim->rlim_max, | ||
116 | initrlim->rlim_cur); | ||
117 | } | ||
118 | } | ||
119 | } | ||
120 | |||
121 | /* set any new hard limits as dictated by the new profile */ | ||
122 | if (!new->rlimits.mask) | ||
123 | return; | ||
124 | for (i = 0, mask = 1; i < RLIM_NLIMITS; i++, mask <<= 1) { | ||
125 | if (!(new->rlimits.mask & mask)) | ||
126 | continue; | ||
127 | |||
128 | rlim = current->signal->rlim + i; | ||
129 | rlim->rlim_max = min(rlim->rlim_max, | ||
130 | new->rlimits.limits[i].rlim_max); | ||
131 | /* soft limit should not exceed hard limit */ | ||
132 | rlim->rlim_cur = min(rlim->rlim_cur, rlim->rlim_max); | ||
133 | } | ||
134 | } | ||
diff --git a/security/apparmor/sid.c b/security/apparmor/sid.c new file mode 100644 index 00000000000..f0b34f76ebe --- /dev/null +++ b/security/apparmor/sid.c | |||
@@ -0,0 +1,55 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor security identifier (sid) manipulation fns | ||
5 | * | ||
6 | * Copyright 2009-2010 Canonical Ltd. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License as | ||
10 | * published by the Free Software Foundation, version 2 of the | ||
11 | * License. | ||
12 | * | ||
13 | * | ||
14 | * AppArmor allocates a unique sid for every profile loaded. If a profile | ||
15 | * is replaced it receives the sid of the profile it is replacing. | ||
16 | * | ||
17 | * The sid value of 0 is invalid. | ||
18 | */ | ||
19 | |||
20 | #include <linux/spinlock.h> | ||
21 | #include <linux/errno.h> | ||
22 | #include <linux/err.h> | ||
23 | |||
24 | #include "include/sid.h" | ||
25 | |||
26 | /* global counter from which sids are allocated */ | ||
27 | static u32 global_sid; | ||
28 | static DEFINE_SPINLOCK(sid_lock); | ||
29 | |||
30 | /* TODO FIXME: add sid to profile mapping, and sid recycling */ | ||
31 | |||
32 | /** | ||
33 | * aa_alloc_sid - allocate a new sid for a profile | ||
34 | */ | ||
35 | u32 aa_alloc_sid(void) | ||
36 | { | ||
37 | u32 sid; | ||
38 | |||
39 | /* | ||
40 | * TODO FIXME: sid recycling - part of profile mapping table | ||
41 | */ | ||
42 | spin_lock(&sid_lock); | ||
43 | sid = (++global_sid); | ||
44 | spin_unlock(&sid_lock); | ||
45 | return sid; | ||
46 | } | ||
47 | |||
48 | /** | ||
49 | * aa_free_sid - free a sid | ||
50 | * @sid: sid to free | ||
51 | */ | ||
52 | void aa_free_sid(u32 sid) | ||
53 | { | ||
54 | ; /* NOP ATM */ | ||
55 | } | ||