diff options
author | John Johansen <john.johansen@canonical.com> | 2017-05-23 06:25:14 -0400 |
---|---|---|
committer | John Johansen <john.johansen@canonical.com> | 2017-06-08 14:29:34 -0400 |
commit | 4227c333f65cddc6c2f048e5b67cfe796b9df9a6 (patch) | |
tree | 4deee8d16246bc879036da19642451b8e7cdcde0 | |
parent | 72c8a768641dc6ee8d1d9dcebd51bbec2817459b (diff) |
apparmor: Move path lookup to using preallocated buffers
Dynamically allocating buffers is problematic and is an extra layer
that is a potntial point of failure and can slow down mediation.
Change path lookup to use the preallocated per cpu buffers.
Signed-off-by: John Johansen <john.johansen@canonical.com>
-rw-r--r-- | security/apparmor/domain.c | 8 | ||||
-rw-r--r-- | security/apparmor/file.c | 13 | ||||
-rw-r--r-- | security/apparmor/include/path.h | 4 | ||||
-rw-r--r-- | security/apparmor/path.c | 114 |
4 files changed, 53 insertions, 86 deletions
diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index c92fd0e7b33c..ab8f23cdccff 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c | |||
@@ -357,6 +357,9 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) | |||
357 | AA_BUG(!ctx); | 357 | AA_BUG(!ctx); |
358 | 358 | ||
359 | profile = aa_get_newest_profile(ctx->profile); | 359 | profile = aa_get_newest_profile(ctx->profile); |
360 | |||
361 | /* buffer freed below, name is pointer into buffer */ | ||
362 | get_buffers(buffer); | ||
360 | /* | 363 | /* |
361 | * get the namespace from the replacement profile as replacement | 364 | * get the namespace from the replacement profile as replacement |
362 | * can change the namespace | 365 | * can change the namespace |
@@ -364,8 +367,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) | |||
364 | ns = profile->ns; | 367 | ns = profile->ns; |
365 | state = profile->file.start; | 368 | state = profile->file.start; |
366 | 369 | ||
367 | /* buffer freed below, name is pointer into buffer */ | 370 | error = aa_path_name(&bprm->file->f_path, profile->path_flags, buffer, |
368 | error = aa_path_name(&bprm->file->f_path, profile->path_flags, &buffer, | ||
369 | &name, &info, profile->disconnected); | 371 | &name, &info, profile->disconnected); |
370 | if (error) { | 372 | if (error) { |
371 | if (unconfined(profile) || | 373 | if (unconfined(profile) || |
@@ -515,7 +517,7 @@ audit: | |||
515 | cleanup: | 517 | cleanup: |
516 | aa_put_profile(new_profile); | 518 | aa_put_profile(new_profile); |
517 | aa_put_profile(profile); | 519 | aa_put_profile(profile); |
518 | kfree(buffer); | 520 | put_buffers(buffer); |
519 | 521 | ||
520 | return error; | 522 | return error; |
521 | } | 523 | } |
diff --git a/security/apparmor/file.c b/security/apparmor/file.c index 83d43ac72134..22be62f0fc73 100644 --- a/security/apparmor/file.c +++ b/security/apparmor/file.c | |||
@@ -285,7 +285,8 @@ int aa_path_perm(const char *op, struct aa_profile *profile, | |||
285 | int error; | 285 | int error; |
286 | 286 | ||
287 | flags |= profile->path_flags | (S_ISDIR(cond->mode) ? PATH_IS_DIR : 0); | 287 | flags |= profile->path_flags | (S_ISDIR(cond->mode) ? PATH_IS_DIR : 0); |
288 | error = aa_path_name(path, flags, &buffer, &name, &info, | 288 | get_buffers(buffer); |
289 | error = aa_path_name(path, flags, buffer, &name, &info, | ||
289 | profile->disconnected); | 290 | profile->disconnected); |
290 | if (error) { | 291 | if (error) { |
291 | if (error == -ENOENT && is_deleted(path->dentry)) { | 292 | if (error == -ENOENT && is_deleted(path->dentry)) { |
@@ -304,7 +305,7 @@ int aa_path_perm(const char *op, struct aa_profile *profile, | |||
304 | } | 305 | } |
305 | error = aa_audit_file(profile, &perms, op, request, name, NULL, | 306 | error = aa_audit_file(profile, &perms, op, request, name, NULL, |
306 | cond->uid, info, error); | 307 | cond->uid, info, error); |
307 | kfree(buffer); | 308 | put_buffers(buffer); |
308 | 309 | ||
309 | return error; | 310 | return error; |
310 | } | 311 | } |
@@ -363,16 +364,17 @@ int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry, | |||
363 | unsigned int state; | 364 | unsigned int state; |
364 | int error; | 365 | int error; |
365 | 366 | ||
367 | get_buffers(buffer, buffer2); | ||
366 | lperms = nullperms; | 368 | lperms = nullperms; |
367 | 369 | ||
368 | /* buffer freed below, lname is pointer in buffer */ | 370 | /* buffer freed below, lname is pointer in buffer */ |
369 | error = aa_path_name(&link, profile->path_flags, &buffer, &lname, | 371 | error = aa_path_name(&link, profile->path_flags, buffer, &lname, |
370 | &info, profile->disconnected); | 372 | &info, profile->disconnected); |
371 | if (error) | 373 | if (error) |
372 | goto audit; | 374 | goto audit; |
373 | 375 | ||
374 | /* buffer2 freed below, tname is pointer in buffer2 */ | 376 | /* buffer2 freed below, tname is pointer in buffer2 */ |
375 | error = aa_path_name(&target, profile->path_flags, &buffer2, &tname, | 377 | error = aa_path_name(&target, profile->path_flags, buffer2, &tname, |
376 | &info, profile->disconnected); | 378 | &info, profile->disconnected); |
377 | if (error) | 379 | if (error) |
378 | goto audit; | 380 | goto audit; |
@@ -432,8 +434,7 @@ done_tests: | |||
432 | audit: | 434 | audit: |
433 | error = aa_audit_file(profile, &lperms, OP_LINK, request, | 435 | error = aa_audit_file(profile, &lperms, OP_LINK, request, |
434 | lname, tname, cond.uid, info, error); | 436 | lname, tname, cond.uid, info, error); |
435 | kfree(buffer); | 437 | put_buffers(buffer, buffer2); |
436 | kfree(buffer2); | ||
437 | 438 | ||
438 | return error; | 439 | return error; |
439 | } | 440 | } |
diff --git a/security/apparmor/include/path.h b/security/apparmor/include/path.h index 78e4909dcc6a..05fb3305671e 100644 --- a/security/apparmor/include/path.h +++ b/security/apparmor/include/path.h | |||
@@ -23,10 +23,10 @@ enum path_flags { | |||
23 | PATH_CHROOT_NSCONNECT = 0x10, /* connect paths that are at ns root */ | 23 | PATH_CHROOT_NSCONNECT = 0x10, /* connect paths that are at ns root */ |
24 | 24 | ||
25 | PATH_DELEGATE_DELETED = 0x08000, /* delegate deleted files */ | 25 | PATH_DELEGATE_DELETED = 0x08000, /* delegate deleted files */ |
26 | PATH_MEDIATE_DELETED = 0x10000, /* mediate deleted paths */ | 26 | PATH_MEDIATE_DELETED = 0x10000, /* mediate deleted paths */ |
27 | }; | 27 | }; |
28 | 28 | ||
29 | int aa_path_name(const struct path *path, int flags, char **buffer, | 29 | int aa_path_name(const struct path *path, int flags, char *buffer, |
30 | const char **name, const char **info, | 30 | const char **name, const char **info, |
31 | const char *disconnected); | 31 | const char *disconnected); |
32 | 32 | ||
diff --git a/security/apparmor/path.c b/security/apparmor/path.c index 9490f8e89630..9d5de1d05be4 100644 --- a/security/apparmor/path.c +++ b/security/apparmor/path.c | |||
@@ -79,7 +79,6 @@ static int disconnect(const struct path *path, char *buf, char **name, | |||
79 | * d_namespace_path - lookup a name associated with a given path | 79 | * d_namespace_path - lookup a name associated with a given path |
80 | * @path: path to lookup (NOT NULL) | 80 | * @path: path to lookup (NOT NULL) |
81 | * @buf: buffer to store path to (NOT NULL) | 81 | * @buf: buffer to store path to (NOT NULL) |
82 | * @buflen: length of @buf | ||
83 | * @name: Returns - pointer for start of path name with in @buf (NOT NULL) | 82 | * @name: Returns - pointer for start of path name with in @buf (NOT NULL) |
84 | * @flags: flags controlling path lookup | 83 | * @flags: flags controlling path lookup |
85 | * @disconnected: string to prefix to disconnected paths | 84 | * @disconnected: string to prefix to disconnected paths |
@@ -90,12 +89,14 @@ static int disconnect(const struct path *path, char *buf, char **name, | |||
90 | * When no error the path name is returned in @name which points to | 89 | * When no error the path name is returned in @name which points to |
91 | * to a position in @buf | 90 | * to a position in @buf |
92 | */ | 91 | */ |
93 | static int d_namespace_path(const struct path *path, char *buf, int buflen, | 92 | static int d_namespace_path(const struct path *path, char *buf, char **name, |
94 | char **name, int flags, const char *disconnected) | 93 | int flags, const char *disconnected) |
95 | { | 94 | { |
96 | char *res; | 95 | char *res; |
97 | int error = 0; | 96 | int error = 0; |
98 | int connected = 1; | 97 | int connected = 1; |
98 | int isdir = (flags & PATH_IS_DIR) ? 1 : 0; | ||
99 | int buflen = aa_g_path_max - isdir; | ||
99 | 100 | ||
100 | if (path->mnt->mnt_flags & MNT_INTERNAL) { | 101 | if (path->mnt->mnt_flags & MNT_INTERNAL) { |
101 | /* it's not mounted anywhere */ | 102 | /* it's not mounted anywhere */ |
@@ -110,10 +111,12 @@ static int d_namespace_path(const struct path *path, char *buf, int buflen, | |||
110 | /* TODO: convert over to using a per namespace | 111 | /* TODO: convert over to using a per namespace |
111 | * control instead of hard coded /proc | 112 | * control instead of hard coded /proc |
112 | */ | 113 | */ |
113 | return prepend(name, *name - buf, "/proc", 5); | 114 | error = prepend(name, *name - buf, "/proc", 5); |
115 | goto out; | ||
114 | } else | 116 | } else |
115 | return disconnect(path, buf, name, flags, | 117 | error = disconnect(path, buf, name, flags, |
116 | disconnected); | 118 | disconnected); |
119 | goto out; | ||
117 | } | 120 | } |
118 | 121 | ||
119 | /* resolve paths relative to chroot?*/ | 122 | /* resolve paths relative to chroot?*/ |
@@ -132,8 +135,11 @@ static int d_namespace_path(const struct path *path, char *buf, int buflen, | |||
132 | * be returned. | 135 | * be returned. |
133 | */ | 136 | */ |
134 | if (!res || IS_ERR(res)) { | 137 | if (!res || IS_ERR(res)) { |
135 | if (PTR_ERR(res) == -ENAMETOOLONG) | 138 | if (PTR_ERR(res) == -ENAMETOOLONG) { |
136 | return -ENAMETOOLONG; | 139 | error = -ENAMETOOLONG; |
140 | *name = buf; | ||
141 | goto out; | ||
142 | } | ||
137 | connected = 0; | 143 | connected = 0; |
138 | res = dentry_path_raw(path->dentry, buf, buflen); | 144 | res = dentry_path_raw(path->dentry, buf, buflen); |
139 | if (IS_ERR(res)) { | 145 | if (IS_ERR(res)) { |
@@ -146,6 +152,9 @@ static int d_namespace_path(const struct path *path, char *buf, int buflen, | |||
146 | 152 | ||
147 | *name = res; | 153 | *name = res; |
148 | 154 | ||
155 | if (!connected) | ||
156 | error = disconnect(path, buf, name, flags, disconnected); | ||
157 | |||
149 | /* Handle two cases: | 158 | /* Handle two cases: |
150 | * 1. A deleted dentry && profile is not allowing mediation of deleted | 159 | * 1. A deleted dentry && profile is not allowing mediation of deleted |
151 | * 2. On some filesystems, newly allocated dentries appear to the | 160 | * 2. On some filesystems, newly allocated dentries appear to the |
@@ -153,62 +162,27 @@ static int d_namespace_path(const struct path *path, char *buf, int buflen, | |||
153 | * allocated. | 162 | * allocated. |
154 | */ | 163 | */ |
155 | if (d_unlinked(path->dentry) && d_is_positive(path->dentry) && | 164 | if (d_unlinked(path->dentry) && d_is_positive(path->dentry) && |
156 | !(flags & PATH_MEDIATE_DELETED)) { | 165 | !(flags & (PATH_MEDIATE_DELETED | PATH_DELEGATE_DELETED))) { |
157 | error = -ENOENT; | 166 | error = -ENOENT; |
158 | goto out; | 167 | goto out; |
159 | } | 168 | } |
160 | 169 | ||
161 | if (!connected) | ||
162 | error = disconnect(path, buf, name, flags, disconnected); | ||
163 | |||
164 | out: | 170 | out: |
165 | return error; | 171 | /* |
166 | } | 172 | * Append "/" to the pathname. The root directory is a special |
167 | 173 | * case; it already ends in slash. | |
168 | /** | 174 | */ |
169 | * get_name_to_buffer - get the pathname to a buffer ensure dir / is appended | 175 | if (!error && isdir && ((*name)[1] != '\0' || (*name)[0] != '/')) |
170 | * @path: path to get name for (NOT NULL) | 176 | strcpy(&buf[aa_g_path_max - 2], "/"); |
171 | * @flags: flags controlling path lookup | ||
172 | * @buffer: buffer to put name in (NOT NULL) | ||
173 | * @size: size of buffer | ||
174 | * @name: Returns - contains position of path name in @buffer (NOT NULL) | ||
175 | * | ||
176 | * Returns: %0 else error on failure | ||
177 | */ | ||
178 | static int get_name_to_buffer(const struct path *path, int flags, char *buffer, | ||
179 | int size, char **name, const char **info, | ||
180 | const char *disconnected) | ||
181 | { | ||
182 | int adjust = (flags & PATH_IS_DIR) ? 1 : 0; | ||
183 | int error = d_namespace_path(path, buffer, size - adjust, name, flags, | ||
184 | disconnected); | ||
185 | |||
186 | if (!error && (flags & PATH_IS_DIR) && (*name)[1] != '\0') | ||
187 | /* | ||
188 | * Append "/" to the pathname. The root directory is a special | ||
189 | * case; it already ends in slash. | ||
190 | */ | ||
191 | strcpy(&buffer[size - 2], "/"); | ||
192 | |||
193 | if (info && error) { | ||
194 | if (error == -ENOENT) | ||
195 | *info = "Failed name lookup - deleted entry"; | ||
196 | else if (error == -EACCES) | ||
197 | *info = "Failed name lookup - disconnected path"; | ||
198 | else if (error == -ENAMETOOLONG) | ||
199 | *info = "Failed name lookup - name too long"; | ||
200 | else | ||
201 | *info = "Failed name lookup"; | ||
202 | } | ||
203 | 177 | ||
204 | return error; | 178 | return error; |
205 | } | 179 | } |
206 | 180 | ||
207 | /** | 181 | /** |
208 | * aa_path_name - compute the pathname of a file | 182 | * aa_path_name - get the pathname to a buffer ensure dir / is appended |
209 | * @path: path the file (NOT NULL) | 183 | * @path: path the file (NOT NULL) |
210 | * @flags: flags controlling path name generation | 184 | * @flags: flags controlling path name generation |
211 | * @buffer: buffer that aa_get_name() allocated (NOT NULL) | 185 | * @buffer: buffer to put name in (NOT NULL) |
212 | * @name: Returns - the generated path name if !error (NOT NULL) | 186 | * @name: Returns - the generated path name if !error (NOT NULL) |
213 | * @info: Returns - information on why the path lookup failed (MAYBE NULL) | 187 | * @info: Returns - information on why the path lookup failed (MAYBE NULL) |
214 | * @disconnected: string to prepend to disconnected paths | 188 | * @disconnected: string to prepend to disconnected paths |
@@ -224,33 +198,23 @@ static int get_name_to_buffer(const struct path *path, int flags, char *buffer, | |||
224 | * | 198 | * |
225 | * Returns: %0 else error code if could retrieve name | 199 | * Returns: %0 else error code if could retrieve name |
226 | */ | 200 | */ |
227 | int aa_path_name(const struct path *path, int flags, char **buffer, | 201 | int aa_path_name(const struct path *path, int flags, char *buffer, |
228 | const char **name, const char **info, const char *disconnected) | 202 | const char **name, const char **info, const char *disconnected) |
229 | { | 203 | { |
230 | char *buf, *str = NULL; | 204 | char *str = NULL; |
231 | int size = 256; | 205 | int error = d_namespace_path(path, buffer, &str, flags, disconnected); |
232 | int error; | ||
233 | |||
234 | *name = NULL; | ||
235 | *buffer = NULL; | ||
236 | for (;;) { | ||
237 | /* freed by caller */ | ||
238 | buf = kmalloc(size, GFP_KERNEL); | ||
239 | if (!buf) | ||
240 | return -ENOMEM; | ||
241 | 206 | ||
242 | error = get_name_to_buffer(path, flags, buf, size, &str, info, | 207 | if (info && error) { |
243 | disconnected); | 208 | if (error == -ENOENT) |
244 | if (error != -ENAMETOOLONG) | 209 | *info = "Failed name lookup - deleted entry"; |
245 | break; | 210 | else if (error == -EACCES) |
246 | 211 | *info = "Failed name lookup - disconnected path"; | |
247 | kfree(buf); | 212 | else if (error == -ENAMETOOLONG) |
248 | size <<= 1; | 213 | *info = "Failed name lookup - name too long"; |
249 | if (size > aa_g_path_max) | 214 | else |
250 | return -ENAMETOOLONG; | 215 | *info = "Failed name lookup"; |
251 | *info = NULL; | ||
252 | } | 216 | } |
253 | *buffer = buf; | 217 | |
254 | *name = str; | 218 | *name = str; |
255 | 219 | ||
256 | return error; | 220 | return error; |