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