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