| 1 | |
| 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 |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 210 | return 6;
|
| 211 | }
|
| 212 |
|
| 213 |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 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 | |
| 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 |
|
| 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 | |
| 602 | *
|
| 603 | * Returns: label for transition or ERR_PTR. Does not return NULL
|
| 604 |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 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 | |
| 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 | }
|