xref: /linux/security/apparmor/mount.c (revision 8fefe68784ae1606e11a5c65c04167c3b95051a0)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * AppArmor security module
4  *
5  * This file contains AppArmor mediation of files
6  *
7  * Copyright (C) 1998-2008 Novell/SUSE
8  * Copyright 2009-2017 Canonical Ltd.
9  */
10 
11 #include <linux/fs.h>
12 #include <linux/mount.h>
13 #include <linux/namei.h>
14 #include <uapi/linux/mount.h>
15 
16 #include "include/apparmor.h"
17 #include "include/audit.h"
18 #include "include/cred.h"
19 #include "include/domain.h"
20 #include "include/file.h"
21 #include "include/match.h"
22 #include "include/mount.h"
23 #include "include/path.h"
24 #include "include/policy.h"
25 
26 
27 #define DEFINE_AUDIT_MOUNT(NAME, OP, CRED)				\
28 	DEFINE_AUDIT_DATA(NAME, LSM_AUDIT_DATA_NONE, AA_CLASS_MOUNT, OP);\
29 	NAME.subj_cred = (CRED)
30 
audit_mnt_flags(struct audit_buffer * ab,unsigned long flags)31 static void audit_mnt_flags(struct audit_buffer *ab, unsigned long flags)
32 {
33 	if (flags & MS_RDONLY)
34 		audit_log_format(ab, "ro");
35 	else
36 		audit_log_format(ab, "rw");
37 	if (flags & MS_NOSUID)
38 		audit_log_format(ab, ", nosuid");
39 	if (flags & MS_NODEV)
40 		audit_log_format(ab, ", nodev");
41 	if (flags & MS_NOEXEC)
42 		audit_log_format(ab, ", noexec");
43 	if (flags & MS_SYNCHRONOUS)
44 		audit_log_format(ab, ", sync");
45 	if (flags & MS_REMOUNT)
46 		audit_log_format(ab, ", remount");
47 	if (flags & MS_MANDLOCK)
48 		audit_log_format(ab, ", mand");
49 	if (flags & MS_DIRSYNC)
50 		audit_log_format(ab, ", dirsync");
51 	if (flags & MS_NOSYMFOLLOW)
52 		audit_log_format(ab, ", nosymfollow");
53 	if (flags & MS_NOATIME)
54 		audit_log_format(ab, ", noatime");
55 	if (flags & MS_NODIRATIME)
56 		audit_log_format(ab, ", nodiratime");
57 	if (flags & MS_BIND)
58 		audit_log_format(ab, flags & MS_REC ? ", rbind" : ", bind");
59 	if (flags & MS_MOVE)
60 		audit_log_format(ab, ", move");
61 	if (flags & MS_SILENT)
62 		audit_log_format(ab, ", silent");
63 	if (flags & MS_POSIXACL)
64 		audit_log_format(ab, ", acl");
65 	if (flags & MS_UNBINDABLE)
66 		audit_log_format(ab, flags & MS_REC ? ", runbindable" :
67 				 ", unbindable");
68 	if (flags & MS_PRIVATE)
69 		audit_log_format(ab, flags & MS_REC ? ", rprivate" :
70 				 ", private");
71 	if (flags & MS_SLAVE)
72 		audit_log_format(ab, flags & MS_REC ? ", rslave" :
73 				 ", slave");
74 	if (flags & MS_SHARED)
75 		audit_log_format(ab, flags & MS_REC ? ", rshared" :
76 				 ", shared");
77 	if (flags & MS_RELATIME)
78 		audit_log_format(ab, ", relatime");
79 	if (flags & MS_I_VERSION)
80 		audit_log_format(ab, ", iversion");
81 	if (flags & MS_STRICTATIME)
82 		audit_log_format(ab, ", strictatime");
83 	if (flags & MS_NOUSER)
84 		audit_log_format(ab, ", nouser");
85 }
86 
87 /**
88  * audit_cb - call back for mount specific audit fields
89  * @ab: audit_buffer  (NOT NULL)
90  * @va: audit struct to audit values of  (NOT NULL)
91  */
audit_cb(struct audit_buffer * ab,void * va)92 static void audit_cb(struct audit_buffer *ab, void *va)
93 {
94 	struct common_audit_data *sa = va;
95 	struct apparmor_audit_data *ad = aad(sa);
96 
97 	if (ad->mnt.type) {
98 		audit_log_format(ab, " fstype=");
99 		audit_log_untrustedstring(ab, ad->mnt.type);
100 	}
101 	if (ad->mnt.src_name) {
102 		audit_log_format(ab, " srcname=");
103 		audit_log_untrustedstring(ab, ad->mnt.src_name);
104 	}
105 	if (ad->mnt.trans) {
106 		audit_log_format(ab, " trans=");
107 		audit_log_untrustedstring(ab, ad->mnt.trans);
108 	}
109 	if (ad->mnt.flags) {
110 		audit_log_format(ab, " flags=\"");
111 		audit_mnt_flags(ab, ad->mnt.flags);
112 		audit_log_format(ab, "\"");
113 	}
114 	if (ad->mnt.data) {
115 		audit_log_format(ab, " options=");
116 		audit_log_untrustedstring(ab, ad->mnt.data);
117 	}
118 }
119 
120 /**
121  * match_mnt_flags - Do an ordered match on mount flags
122  * @dfa: dfa to match against
123  * @state: state to start in
124  * @flags: mount flags to match against
125  *
126  * Mount flags are encoded as an ordered match. This is done instead of
127  * checking against a simple bitmask, to allow for logical operations
128  * on the flags.
129  *
130  * Returns: next state after flags match
131  */
match_mnt_flags(const struct aa_dfa * dfa,aa_state_t state,unsigned long flags)132 static aa_state_t match_mnt_flags(const struct aa_dfa *dfa, aa_state_t state,
133 				    unsigned long flags)
134 {
135 	unsigned int i;
136 
137 	for (i = 0; i <= 31 ; ++i) {
138 		if ((1 << i) & flags)
139 			state = aa_dfa_next(dfa, state, i + 1);
140 	}
141 
142 	return state;
143 }
144 
145 static const char * const mnt_info_table[] = {
146 	"match succeeded",
147 	"failed mntpnt match",
148 	"failed srcname match",
149 	"failed type match",
150 	"failed flags match",
151 	"failed data match",
152 	"failed perms check"
153 };
154 
155 /*
156  * Returns 0 on success else element that match failed in, this is the
157  * index into the mnt_info_table above
158  */
do_match_mnt(struct aa_policydb * policy,aa_state_t start,const char * mntpnt,const char * devname,const char * type,unsigned long flags,void * data,bool binary,struct aa_perms * perms)159 static int do_match_mnt(struct aa_policydb *policy, aa_state_t start,
160 			const char *mntpnt, const char *devname,
161 			const char *type, unsigned long flags,
162 			void *data, bool binary, struct aa_perms *perms)
163 {
164 	aa_state_t state;
165 
166 	AA_BUG(!policy);
167 	AA_BUG(!policy->dfa);
168 	AA_BUG(!policy->perms);
169 	AA_BUG(!perms);
170 
171 	state = aa_dfa_match(policy->dfa, start, mntpnt);
172 	state = aa_dfa_null_transition(policy->dfa, state);
173 	if (!state)
174 		return 1;
175 
176 	if (devname)
177 		state = aa_dfa_match(policy->dfa, state, devname);
178 	state = aa_dfa_null_transition(policy->dfa, state);
179 	if (!state)
180 		return 2;
181 
182 	if (type)
183 		state = aa_dfa_match(policy->dfa, state, type);
184 	state = aa_dfa_null_transition(policy->dfa, state);
185 	if (!state)
186 		return 3;
187 
188 	state = match_mnt_flags(policy->dfa, state, flags);
189 	if (!state)
190 		return 4;
191 	*perms = *aa_lookup_perms(policy, state);
192 	if (perms->allow & AA_MAY_MOUNT)
193 		return 0;
194 
195 	/* only match data if not binary and the DFA flags data is expected */
196 	if (data && !binary && (perms->allow & AA_MNT_CONT_MATCH)) {
197 		state = aa_dfa_null_transition(policy->dfa, state);
198 		if (!state)
199 			return 4;
200 
201 		state = aa_dfa_match(policy->dfa, state, data);
202 		if (!state)
203 			return 5;
204 		*perms = *aa_lookup_perms(policy, state);
205 		if (perms->allow & AA_MAY_MOUNT)
206 			return 0;
207 	}
208 
209 	/* failed at perms check, don't confuse with flags match */
210 	return 6;
211 }
212 
213 
path_flags(const struct aa_profile * profile,const struct path * path)214 static int path_flags(const struct aa_profile *profile, const struct path *path)
215 {
216 	AA_BUG(!profile);
217 	AA_BUG(!path);
218 
219 	return profile->path_flags |
220 		(S_ISDIR(path->dentry->d_inode->i_mode) ? PATH_IS_DIR : 0);
221 }
222 
223 /**
224  * match_mnt_path_str - handle path matching for mount
225  * @profile: the confining profile
226  * @mntpath: for the mntpnt (NOT NULL)
227  * @buffer: buffer to be used to lookup mntpath
228  * @devname: string for the devname/src_name (MAY BE NULL OR ERRPTR)
229  * @type: string for the dev type (MAYBE NULL)
230  * @flags: mount flags to match
231  * @data: fs mount data (MAYBE NULL)
232  * @binary: whether @data is binary
233  * @devinfo: error str if (IS_ERR(@devname))
234  * @ad: apparmor audit data structure
235  *
236  * Returns: 0 on success else error
237  */
match_mnt_path_str(struct aa_profile * profile,const struct path * mntpath,char * buffer,const char * devname,const char * type,unsigned long flags,void * data,bool binary,const char * devinfo,struct apparmor_audit_data * ad)238 static int match_mnt_path_str(struct aa_profile *profile,
239 			      const struct path *mntpath, char *buffer,
240 			      const char *devname, const char *type,
241 			      unsigned long flags, void *data, bool binary,
242 			      const char *devinfo,
243 			      struct apparmor_audit_data *ad)
244 {
245 	struct aa_perms perms = { };
246 	const char *mntpnt = NULL;
247 	struct aa_ruleset *rules = profile->label.rules[0];
248 	int pos, error;
249 
250 	AA_BUG(!profile);
251 	AA_BUG(!mntpath);
252 	AA_BUG(!buffer);
253 
254 	if (!RULE_MEDIATES(rules, AA_CLASS_MOUNT))
255 		return 0;
256 
257 	ad->mnt.type = type;
258 
259 	error = aa_path_name(mntpath, path_flags(profile, mntpath), buffer,
260 			     &mntpnt, &ad->info, profile->disconnected);
261 	if (error)
262 		return aa_audit_perm_error(&profile->label, AA_MAY_MOUNT,
263 					   error, ad, audit_cb);
264 	ad->name = mntpnt;
265 
266 	if (IS_ERR(devname)) {
267 		error = PTR_ERR(devname);
268 		ad->info = devinfo;
269 		return aa_audit_perm_error(&profile->label, AA_MAY_MOUNT,
270 					   error, ad, audit_cb);
271 	}
272 	ad->mnt.src_name = devname;
273 
274 	pos = do_match_mnt(rules->policy,
275 			   rules->policy->start[AA_CLASS_MOUNT],
276 			   mntpnt, devname, type, flags, data, binary, &perms);
277 	if (pos)
278 		ad->info = mnt_info_table[pos];
279 
280 	aa_apply_modes_to_perms(profile, &perms);
281 	if (data && !binary && (perms.audit & AA_AUDIT_DATA))
282 		ad->mnt.data = data;
283 	return aa_check_perms(profile, &perms, AA_MAY_MOUNT, ad, audit_cb);
284 }
285 
286 /**
287  * match_mnt - handle path matching for mount
288  * @profile: the confining profile
289  * @path: for the mntpnt (NOT NULL)
290  * @buffer: buffer to be used to lookup mntpath
291  * @devpath: path devname/src_name (MAYBE NULL)
292  * @devbuffer: buffer to be used to lookup devname/src_name
293  * @type: string for the dev type (MAYBE NULL)
294  * @flags: mount flags to match
295  * @data: fs mount data (MAYBE NULL)
296  * @binary: whether @data is binary
297  * @ad: apparmor audit data structure
298  *
299  * Returns: 0 on success else error
300  */
match_mnt(struct aa_profile * profile,const struct path * path,char * buffer,const struct path * devpath,char * devbuffer,const char * type,unsigned long flags,void * data,bool binary,struct apparmor_audit_data * ad)301 static int match_mnt(struct aa_profile *profile, const struct path *path,
302 		     char *buffer, const struct path *devpath, char *devbuffer,
303 		     const char *type, unsigned long flags, void *data,
304 		     bool binary, struct apparmor_audit_data *ad)
305 {
306 	const char *devname = NULL, *info = NULL;
307 	struct aa_ruleset *rules = profile->label.rules[0];
308 	int error = -EACCES;
309 
310 	AA_BUG(!profile);
311 	AA_BUG(devpath && !devbuffer);
312 
313 	if (!RULE_MEDIATES(rules, AA_CLASS_MOUNT))
314 		return 0;
315 
316 	if (devpath) {
317 		error = aa_path_name(devpath, path_flags(profile, devpath),
318 				     devbuffer, &devname, &info,
319 				     profile->disconnected);
320 		if (error)
321 			devname = ERR_PTR(error);
322 	}
323 
324 	return match_mnt_path_str(profile, path, buffer, devname,
325 				  type, flags, data, binary, info, ad);
326 }
327 
aa_remount(const struct cred * subj_cred,struct aa_label * label,const struct path * path,unsigned long flags,void * data)328 int aa_remount(const struct cred *subj_cred,
329 	       struct aa_label *label, const struct path *path,
330 	       unsigned long flags, void *data)
331 {
332 	struct aa_profile *profile;
333 	char *buffer = NULL;
334 	bool binary;
335 	int error;
336 	DEFINE_AUDIT_MOUNT(ad, OP_MOUNT, subj_cred);
337 	ad.mnt.flags = flags;
338 
339 	AA_BUG(!label);
340 	AA_BUG(!path);
341 
342 	binary = path->dentry->d_sb->s_type->fs_flags & FS_BINARY_MOUNTDATA;
343 
344 	buffer = aa_get_buffer(false);
345 	if (!buffer)
346 		return -ENOMEM;
347 	error = fn_for_each(label, profile,
348 			match_mnt(profile, path, buffer, NULL, NULL, NULL,
349 				  flags, data, binary, &ad));
350 	aa_put_buffer(buffer);
351 
352 	return error;
353 }
354 
aa_bind_mount(const struct cred * subj_cred,struct aa_label * label,const struct path * path,const char * dev_name,unsigned long flags)355 int aa_bind_mount(const struct cred *subj_cred,
356 		  struct aa_label *label, const struct path *path,
357 		  const char *dev_name, unsigned long flags)
358 {
359 	struct aa_profile *profile;
360 	char *buffer = NULL, *old_buffer = NULL;
361 	struct path old_path;
362 	int error;
363 	DEFINE_AUDIT_MOUNT(ad, OP_MOUNT, subj_cred);
364 
365 	AA_BUG(!label);
366 	AA_BUG(!path);
367 
368 	if (!dev_name || !*dev_name)
369 		return -EINVAL;
370 
371 	flags &= MS_REC | MS_BIND;
372 	ad.mnt.flags = flags;
373 
374 	error = kern_path(dev_name, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &old_path);
375 	if (error)
376 		return aa_audit_perm_error(label, AA_MAY_MOUNT, error, &ad,
377 			audit_cb);
378 
379 	buffer = aa_get_buffer(false);
380 	old_buffer = aa_get_buffer(false);
381 	error = -ENOMEM;
382 	if (!buffer || !old_buffer)
383 		goto out;
384 
385 	error = fn_for_each(label, profile,
386 			match_mnt(profile, path, buffer, &old_path,
387 				  old_buffer, NULL, flags, NULL, false, &ad));
388 out:
389 	aa_put_buffer(buffer);
390 	aa_put_buffer(old_buffer);
391 	path_put(&old_path);
392 
393 	return error;
394 }
395 
aa_mount_change_type(const struct cred * subj_cred,struct aa_label * label,const struct path * path,unsigned long flags)396 int aa_mount_change_type(const struct cred *subj_cred,
397 			 struct aa_label *label, const struct path *path,
398 			 unsigned long flags)
399 {
400 	struct aa_profile *profile;
401 	char *buffer = NULL;
402 	int error;
403 	DEFINE_AUDIT_MOUNT(ad, OP_MOUNT, subj_cred);
404 
405 	AA_BUG(!label);
406 	AA_BUG(!path);
407 
408 	/* These are the flags allowed by do_change_type() */
409 	flags &= (MS_REC | MS_SILENT | MS_SHARED | MS_PRIVATE | MS_SLAVE |
410 		  MS_UNBINDABLE);
411 	ad.mnt.flags = flags;
412 
413 	buffer = aa_get_buffer(false);
414 	if (!buffer)
415 		return -ENOMEM;
416 	error = fn_for_each(label, profile,
417 			match_mnt(profile, path, buffer, NULL, NULL, NULL,
418 				  flags, NULL, false, &ad));
419 	aa_put_buffer(buffer);
420 
421 	return error;
422 }
423 
aa_move_mount(const struct cred * subj_cred,struct aa_label * label,const struct path * from_path,const struct path * to_path)424 int aa_move_mount(const struct cred *subj_cred,
425 		  struct aa_label *label, const struct path *from_path,
426 		  const struct path *to_path)
427 {
428 	struct aa_profile *profile;
429 	char *to_buffer = NULL, *from_buffer = NULL;
430 	int error;
431 	DEFINE_AUDIT_MOUNT(ad, OP_MOUNT, subj_cred);
432 	ad.mnt.flags = MS_MOVE;
433 
434 	AA_BUG(!label);
435 	AA_BUG(!from_path);
436 	AA_BUG(!to_path);
437 
438 	to_buffer = aa_get_buffer(false);
439 	from_buffer = aa_get_buffer(false);
440 	error = -ENOMEM;
441 	if (!to_buffer || !from_buffer)
442 		goto out;
443 
444 	if (!our_mnt(from_path->mnt))
445 		/* moving a mount detached from the namespace */
446 		from_path = NULL;
447 	error = fn_for_each(label, profile,
448 			match_mnt(profile, to_path, to_buffer, from_path,
449 				  from_buffer, NULL, MS_MOVE, NULL, false,
450 				  &ad));
451 out:
452 	aa_put_buffer(to_buffer);
453 	aa_put_buffer(from_buffer);
454 
455 	return error;
456 }
457 
aa_move_mount_old(const struct cred * subj_cred,struct aa_label * label,const struct path * path,const char * orig_name)458 int aa_move_mount_old(const struct cred *subj_cred, struct aa_label *label,
459 		      const struct path *path, const char *orig_name)
460 {
461 	struct path old_path;
462 	int error;
463 
464 	if (!orig_name || !*orig_name)
465 		return -EINVAL;
466 	error = kern_path(orig_name, LOOKUP_FOLLOW, &old_path);
467 	if (error)
468 		return error;
469 
470 	error = aa_move_mount(subj_cred, label, &old_path, path);
471 	path_put(&old_path);
472 
473 	return error;
474 }
475 
aa_new_mount(const struct cred * subj_cred,struct aa_label * label,const char * dev_name,const struct path * path,const char * type,unsigned long flags,void * data)476 int aa_new_mount(const struct cred *subj_cred, struct aa_label *label,
477 		 const char *dev_name, const struct path *path,
478 		 const char *type, unsigned long flags, void *data)
479 {
480 	struct aa_profile *profile;
481 	char *buffer = NULL, *dev_buffer = NULL;
482 	bool binary = true;
483 	int error;
484 	int requires_dev = 0;
485 	struct path tmp_path, *dev_path = NULL;
486 	DEFINE_AUDIT_MOUNT(ad, OP_MOUNT, subj_cred);
487 	ad.mnt.flags = flags;
488 
489 	AA_BUG(!label);
490 	AA_BUG(!path);
491 
492 	if (type) {
493 		struct file_system_type *fstype;
494 
495 		fstype = get_fs_type(type);
496 		if (!fstype)
497 			return -ENODEV;
498 		binary = fstype->fs_flags & FS_BINARY_MOUNTDATA;
499 		requires_dev = fstype->fs_flags & FS_REQUIRES_DEV;
500 		put_filesystem(fstype);
501 
502 		if (requires_dev) {
503 			if (!dev_name || !*dev_name)
504 				return -ENOENT;
505 
506 			error = kern_path(dev_name, LOOKUP_FOLLOW, &tmp_path);
507 			if (error)
508 				return error;
509 			dev_path = &tmp_path;
510 		}
511 	}
512 
513 	buffer = aa_get_buffer(false);
514 	if (!buffer) {
515 		error = -ENOMEM;
516 		goto out;
517 	}
518 	if (dev_path) {
519 		dev_buffer = aa_get_buffer(false);
520 		if (!dev_buffer) {
521 			error = -ENOMEM;
522 			goto out;
523 		}
524 		error = fn_for_each(label, profile,
525 				match_mnt(profile, path, buffer, dev_path,
526 					  dev_buffer, type, flags, data,
527 					  binary, &ad));
528 	} else {
529 		error = fn_for_each(label, profile,
530 				match_mnt_path_str(profile, path, buffer,
531 						   dev_name, type, flags, data,
532 						   binary, NULL, &ad));
533 	}
534 
535 out:
536 	aa_put_buffer(buffer);
537 	aa_put_buffer(dev_buffer);
538 	if (dev_path)
539 		path_put(dev_path);
540 
541 	return error;
542 }
543 
profile_umount(struct aa_profile * profile,const struct path * path,char * buffer,struct apparmor_audit_data * ad)544 static int profile_umount(struct aa_profile *profile, const struct path *path,
545 			  char *buffer, struct apparmor_audit_data *ad)
546 {
547 	struct aa_ruleset *rules = profile->label.rules[0];
548 	struct aa_perms perms = { };
549 	const char *name = NULL;
550 	aa_state_t state;
551 	int error;
552 
553 	AA_BUG(!profile);
554 	AA_BUG(!path);
555 
556 	if (!RULE_MEDIATES(rules, AA_CLASS_MOUNT))
557 		return 0;
558 
559 	/* TODO: lift path_name, need to separate profile path_flags from
560 	 * the lookup
561 	 */
562 	error = aa_path_name(path, path_flags(profile, path), buffer, &name,
563 			     &ad->info, profile->disconnected);
564 	if (error)
565 		return aa_audit_perm_error(&profile->label, AA_MAY_UMOUNT,
566 					   error, ad, audit_cb);
567 
568 	ad->name = name;
569 	state = aa_dfa_match(rules->policy->dfa,
570 			     rules->policy->start[AA_CLASS_MOUNT],
571 			     name);
572 	perms = *aa_lookup_perms(rules->policy, state);
573 
574 	aa_apply_modes_to_perms(profile, &perms);
575 	return aa_check_perms(profile, &perms, AA_MAY_UMOUNT, ad, audit_cb);
576 }
577 
aa_umount(const struct cred * subj_cred,struct aa_label * label,struct vfsmount * mnt,int flags)578 int aa_umount(const struct cred *subj_cred, struct aa_label *label,
579 	      struct vfsmount *mnt, int flags)
580 {
581 	struct aa_profile *profile;
582 	char *buffer = NULL;
583 	int error;
584 	struct path path = { .mnt = mnt, .dentry = mnt->mnt_root };
585 	DEFINE_AUDIT_MOUNT(ad, OP_UMOUNT, subj_cred);
586 
587 	AA_BUG(!label);
588 	AA_BUG(!mnt);
589 
590 	buffer = aa_get_buffer(false);
591 	if (!buffer)
592 		return -ENOMEM;
593 
594 	error = fn_for_each(label, profile,
595 			profile_umount(profile, &path, buffer, &ad));
596 	aa_put_buffer(buffer);
597 
598 	return error;
599 }
600 
601 /* helper fn for transition on pivotroot
602  *
603  * Returns: label for transition or ERR_PTR. Does not return NULL
604  */
build_pivotroot(struct aa_profile * profile,const struct path * new_path,char * new_buffer,const struct path * old_path,char * old_buffer,struct apparmor_audit_data * ad)605 static struct aa_label *build_pivotroot(struct aa_profile *profile,
606 					const struct path *new_path,
607 					char *new_buffer,
608 					const struct path *old_path,
609 					char *old_buffer,
610 					struct apparmor_audit_data *ad)
611 {
612 	struct aa_ruleset *rules = profile->label.rules[0];
613 	const char *old_name, *new_name = NULL;
614 	struct aa_perms perms = { };
615 	aa_state_t state;
616 	int error;
617 
618 	AA_BUG(!profile);
619 	AA_BUG(!new_path);
620 	AA_BUG(!old_path);
621 
622 	if (profile_unconfined(profile) ||
623 	    !RULE_MEDIATES(rules, AA_CLASS_MOUNT))
624 		return aa_get_newest_label(&profile->label);
625 
626 	error = aa_path_name(old_path, path_flags(profile, old_path),
627 			     old_buffer, &old_name, &ad->info,
628 			     profile->disconnected);
629 	if (error)
630 		goto err;
631 	ad->mnt.src_name = old_name;
632 	error = aa_path_name(new_path, path_flags(profile, new_path),
633 			     new_buffer, &new_name, &ad->info,
634 			     profile->disconnected);
635 	if (error)
636 		goto err;
637 	ad->name = new_name;
638 
639 	state = aa_dfa_match(rules->policy->dfa,
640 			     rules->policy->start[AA_CLASS_MOUNT],
641 			     new_name);
642 	state = aa_dfa_null_transition(rules->policy->dfa, state);
643 	state = aa_dfa_match(rules->policy->dfa, state, old_name);
644 	perms = *aa_lookup_perms(rules->policy, state);
645 	/* todo: allow pivotroot to specify a transition other than profile */
646 	ad->mnt.trans = profile->label.hname;
647 
648 	aa_apply_modes_to_perms(profile, &perms);
649 	error = aa_check_perms(profile, &perms, AA_MAY_PIVOTROOT, ad, audit_cb);
650 
651 out:
652 	if (error)
653 		return ERR_PTR(error);
654 
655 	return aa_get_newest_label(&profile->label);
656 
657 err:
658 	error = aa_audit_perm_error(&profile->label, AA_MAY_PIVOTROOT, error,
659 				    ad, audit_cb);
660 	goto out;
661 }
662 
aa_pivotroot(const struct cred * subj_cred,struct aa_label * label,const struct path * old_path,const struct path * new_path)663 int aa_pivotroot(const struct cred *subj_cred, struct aa_label *label,
664 		 const struct path *old_path,
665 		 const struct path *new_path)
666 {
667 	struct aa_profile *profile;
668 	struct aa_label *target = NULL;
669 	char *old_buffer = NULL, *new_buffer = NULL;
670 	int error;
671 	DEFINE_AUDIT_MOUNT(ad, OP_PIVOTROOT, subj_cred);
672 
673 	AA_BUG(!label);
674 	AA_BUG(!old_path);
675 	AA_BUG(!new_path);
676 
677 	old_buffer = aa_get_buffer(false);
678 	new_buffer = aa_get_buffer(false);
679 	error = -ENOMEM;
680 	if (!old_buffer || !new_buffer)
681 		goto out;
682 	target = fn_label_build(label, profile, GFP_KERNEL,
683 			build_pivotroot(profile, new_path, new_buffer,
684 					old_path, old_buffer, &ad));
685 	AA_BUG(!target);
686 	if (!IS_ERR(target)) {
687 		error = aa_replace_current_label(target);
688 		if (error)
689 			goto fail;
690 		aa_put_label(target);
691 	} else
692 		/* already audited error in build_pivotroot */
693 		error = PTR_ERR(target);
694 out:
695 	aa_put_buffer(old_buffer);
696 	aa_put_buffer(new_buffer);
697 
698 	return error;
699 
700 fail:
701 	/* TODO: add back in auditing of new_name and old_name,
702 	 * needs lifting of name lookup out of profile cb
703 	 */
704 	ad.mnt.trans = target->hname;
705 	error = aa_audit_perm_error(label, AA_MAY_PIVOTROOT, error, &ad,
706 				    audit_cb);
707 	aa_put_label(target);
708 	goto out;
709 }
710