summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohn Johansen <john.johansen@canonical.com>2017-05-23 06:25:14 -0400
committerJohn Johansen <john.johansen@canonical.com>2017-06-08 14:29:34 -0400
commit4227c333f65cddc6c2f048e5b67cfe796b9df9a6 (patch)
tree4deee8d16246bc879036da19642451b8e7cdcde0
parent72c8a768641dc6ee8d1d9dcebd51bbec2817459b (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.c8
-rw-r--r--security/apparmor/file.c13
-rw-r--r--security/apparmor/include/path.h4
-rw-r--r--security/apparmor/path.c114
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:
515cleanup: 517cleanup:
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:
432audit: 434audit:
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
29int aa_path_name(const struct path *path, int flags, char **buffer, 29int 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 */
93static int d_namespace_path(const struct path *path, char *buf, int buflen, 92static 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
164out: 170out:
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 */
178static 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 */
227int aa_path_name(const struct path *path, int flags, char **buffer, 201int 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;