123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707 |
- /*
- * AppArmor security module
- *
- * This file contains AppArmor mediation of files
- *
- * Copyright (C) 1998-2008 Novell/SUSE
- * Copyright 2009-2017 Canonical Ltd.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, version 2 of the
- * License.
- */
- #include <linux/fs.h>
- #include <linux/mount.h>
- #include <linux/namei.h>
- #include "include/apparmor.h"
- #include "include/audit.h"
- #include "include/context.h"
- #include "include/domain.h"
- #include "include/file.h"
- #include "include/match.h"
- #include "include/mount.h"
- #include "include/path.h"
- #include "include/policy.h"
- static void audit_mnt_flags(struct audit_buffer *ab, unsigned long flags)
- {
- if (flags & MS_RDONLY)
- audit_log_format(ab, "ro");
- else
- audit_log_format(ab, "rw");
- if (flags & MS_NOSUID)
- audit_log_format(ab, ", nosuid");
- if (flags & MS_NODEV)
- audit_log_format(ab, ", nodev");
- if (flags & MS_NOEXEC)
- audit_log_format(ab, ", noexec");
- if (flags & MS_SYNCHRONOUS)
- audit_log_format(ab, ", sync");
- if (flags & MS_REMOUNT)
- audit_log_format(ab, ", remount");
- if (flags & MS_MANDLOCK)
- audit_log_format(ab, ", mand");
- if (flags & MS_DIRSYNC)
- audit_log_format(ab, ", dirsync");
- if (flags & MS_NOATIME)
- audit_log_format(ab, ", noatime");
- if (flags & MS_NODIRATIME)
- audit_log_format(ab, ", nodiratime");
- if (flags & MS_BIND)
- audit_log_format(ab, flags & MS_REC ? ", rbind" : ", bind");
- if (flags & MS_MOVE)
- audit_log_format(ab, ", move");
- if (flags & MS_SILENT)
- audit_log_format(ab, ", silent");
- if (flags & MS_POSIXACL)
- audit_log_format(ab, ", acl");
- if (flags & MS_UNBINDABLE)
- audit_log_format(ab, flags & MS_REC ? ", runbindable" :
- ", unbindable");
- if (flags & MS_PRIVATE)
- audit_log_format(ab, flags & MS_REC ? ", rprivate" :
- ", private");
- if (flags & MS_SLAVE)
- audit_log_format(ab, flags & MS_REC ? ", rslave" :
- ", slave");
- if (flags & MS_SHARED)
- audit_log_format(ab, flags & MS_REC ? ", rshared" :
- ", shared");
- if (flags & MS_RELATIME)
- audit_log_format(ab, ", relatime");
- if (flags & MS_I_VERSION)
- audit_log_format(ab, ", iversion");
- if (flags & MS_STRICTATIME)
- audit_log_format(ab, ", strictatime");
- if (flags & MS_NOUSER)
- audit_log_format(ab, ", nouser");
- }
- /**
- * audit_cb - call back for mount specific audit fields
- * @ab: audit_buffer (NOT NULL)
- * @va: audit struct to audit values of (NOT NULL)
- */
- static void audit_cb(struct audit_buffer *ab, void *va)
- {
- struct common_audit_data *sa = va;
- if (aad(sa)->mnt.type) {
- audit_log_format(ab, " fstype=");
- audit_log_untrustedstring(ab, aad(sa)->mnt.type);
- }
- if (aad(sa)->mnt.src_name) {
- audit_log_format(ab, " srcname=");
- audit_log_untrustedstring(ab, aad(sa)->mnt.src_name);
- }
- if (aad(sa)->mnt.trans) {
- audit_log_format(ab, " trans=");
- audit_log_untrustedstring(ab, aad(sa)->mnt.trans);
- }
- if (aad(sa)->mnt.flags) {
- audit_log_format(ab, " flags=\"");
- audit_mnt_flags(ab, aad(sa)->mnt.flags);
- audit_log_format(ab, "\"");
- }
- if (aad(sa)->mnt.data) {
- audit_log_format(ab, " options=");
- audit_log_untrustedstring(ab, aad(sa)->mnt.data);
- }
- }
- /**
- * audit_mount - handle the auditing of mount operations
- * @profile: the profile being enforced (NOT NULL)
- * @op: operation being mediated (NOT NULL)
- * @name: name of object being mediated (MAYBE NULL)
- * @src_name: src_name of object being mediated (MAYBE_NULL)
- * @type: type of filesystem (MAYBE_NULL)
- * @trans: name of trans (MAYBE NULL)
- * @flags: filesystem idependent mount flags
- * @data: filesystem mount flags
- * @request: permissions requested
- * @perms: the permissions computed for the request (NOT NULL)
- * @info: extra information message (MAYBE NULL)
- * @error: 0 if operation allowed else failure error code
- *
- * Returns: %0 or error on failure
- */
- static int audit_mount(struct aa_profile *profile, const char *op,
- const char *name, const char *src_name,
- const char *type, const char *trans,
- unsigned long flags, const void *data, u32 request,
- struct aa_perms *perms, const char *info, int error)
- {
- int audit_type = AUDIT_APPARMOR_AUTO;
- DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, op);
- if (likely(!error)) {
- u32 mask = perms->audit;
- if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL))
- mask = 0xffff;
- /* mask off perms that are not being force audited */
- request &= mask;
- if (likely(!request))
- return 0;
- audit_type = AUDIT_APPARMOR_AUDIT;
- } else {
- /* only report permissions that were denied */
- request = request & ~perms->allow;
- if (request & perms->kill)
- audit_type = AUDIT_APPARMOR_KILL;
- /* quiet known rejects, assumes quiet and kill do not overlap */
- if ((request & perms->quiet) &&
- AUDIT_MODE(profile) != AUDIT_NOQUIET &&
- AUDIT_MODE(profile) != AUDIT_ALL)
- request &= ~perms->quiet;
- if (!request)
- return error;
- }
- aad(&sa)->name = name;
- aad(&sa)->mnt.src_name = src_name;
- aad(&sa)->mnt.type = type;
- aad(&sa)->mnt.trans = trans;
- aad(&sa)->mnt.flags = flags;
- if (data && (perms->audit & AA_AUDIT_DATA))
- aad(&sa)->mnt.data = data;
- aad(&sa)->info = info;
- aad(&sa)->error = error;
- return aa_audit(audit_type, profile, &sa, audit_cb);
- }
- /**
- * match_mnt_flags - Do an ordered match on mount flags
- * @dfa: dfa to match against
- * @state: state to start in
- * @flags: mount flags to match against
- *
- * Mount flags are encoded as an ordered match. This is done instead of
- * checking against a simple bitmask, to allow for logical operations
- * on the flags.
- *
- * Returns: next state after flags match
- */
- static unsigned int match_mnt_flags(struct aa_dfa *dfa, unsigned int state,
- unsigned long flags)
- {
- unsigned int i;
- for (i = 0; i <= 31 ; ++i) {
- if ((1 << i) & flags)
- state = aa_dfa_next(dfa, state, i + 1);
- }
- return state;
- }
- /**
- * compute_mnt_perms - compute mount permission associated with @state
- * @dfa: dfa to match against (NOT NULL)
- * @state: state match finished in
- *
- * Returns: mount permissions
- */
- static struct aa_perms compute_mnt_perms(struct aa_dfa *dfa,
- unsigned int state)
- {
- struct aa_perms perms;
- perms.kill = 0;
- perms.allow = dfa_user_allow(dfa, state);
- perms.audit = dfa_user_audit(dfa, state);
- perms.quiet = dfa_user_quiet(dfa, state);
- perms.xindex = dfa_user_xindex(dfa, state);
- return perms;
- }
- static const char * const mnt_info_table[] = {
- "match succeeded",
- "failed mntpnt match",
- "failed srcname match",
- "failed type match",
- "failed flags match",
- "failed data match"
- };
- /*
- * Returns 0 on success else element that match failed in, this is the
- * index into the mnt_info_table above
- */
- static int do_match_mnt(struct aa_dfa *dfa, unsigned int start,
- const char *mntpnt, const char *devname,
- const char *type, unsigned long flags,
- void *data, bool binary, struct aa_perms *perms)
- {
- unsigned int state;
- AA_BUG(!dfa);
- AA_BUG(!perms);
- state = aa_dfa_match(dfa, start, mntpnt);
- state = aa_dfa_null_transition(dfa, state);
- if (!state)
- return 1;
- if (devname)
- state = aa_dfa_match(dfa, state, devname);
- state = aa_dfa_null_transition(dfa, state);
- if (!state)
- return 2;
- if (type)
- state = aa_dfa_match(dfa, state, type);
- state = aa_dfa_null_transition(dfa, state);
- if (!state)
- return 3;
- state = match_mnt_flags(dfa, state, flags);
- if (!state)
- return 4;
- *perms = compute_mnt_perms(dfa, state);
- if (perms->allow & AA_MAY_MOUNT)
- return 0;
- /* only match data if not binary and the DFA flags data is expected */
- if (data && !binary && (perms->allow & AA_MNT_CONT_MATCH)) {
- state = aa_dfa_null_transition(dfa, state);
- if (!state)
- return 4;
- state = aa_dfa_match(dfa, state, data);
- if (!state)
- return 5;
- *perms = compute_mnt_perms(dfa, state);
- if (perms->allow & AA_MAY_MOUNT)
- return 0;
- }
- /* failed at end of flags match */
- return 4;
- }
- static int path_flags(struct aa_profile *profile, const struct path *path)
- {
- AA_BUG(!profile);
- AA_BUG(!path);
- return profile->path_flags |
- (S_ISDIR(path->dentry->d_inode->i_mode) ? PATH_IS_DIR : 0);
- }
- /**
- * match_mnt_path_str - handle path matching for mount
- * @profile: the confining profile
- * @mntpath: for the mntpnt (NOT NULL)
- * @buffer: buffer to be used to lookup mntpath
- * @devnme: string for the devname/src_name (MAY BE NULL OR ERRPTR)
- * @type: string for the dev type (MAYBE NULL)
- * @flags: mount flags to match
- * @data: fs mount data (MAYBE NULL)
- * @binary: whether @data is binary
- * @devinfo: error str if (IS_ERR(@devname))
- *
- * Returns: 0 on success else error
- */
- static int 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 aa_perms perms = { };
- const char *mntpnt = NULL, *info = NULL;
- int pos, error;
- AA_BUG(!profile);
- AA_BUG(!mntpath);
- AA_BUG(!buffer);
- if (!PROFILE_MEDIATES(profile, AA_CLASS_MOUNT))
- return 0;
- error = aa_path_name(mntpath, path_flags(profile, mntpath), buffer,
- &mntpnt, &info, profile->disconnected);
- if (error)
- goto audit;
- if (IS_ERR(devname)) {
- error = PTR_ERR(devname);
- devname = NULL;
- info = devinfo;
- goto audit;
- }
- error = -EACCES;
- pos = do_match_mnt(profile->policy.dfa,
- profile->policy.start[AA_CLASS_MOUNT],
- mntpnt, devname, type, flags, data, binary, &perms);
- if (pos) {
- info = mnt_info_table[pos];
- goto audit;
- }
- error = 0;
- audit:
- return audit_mount(profile, OP_MOUNT, mntpnt, devname, type, NULL,
- flags, data, AA_MAY_MOUNT, &perms, info, error);
- }
- /**
- * match_mnt - handle path matching for mount
- * @profile: the confining profile
- * @mntpath: for the mntpnt (NOT NULL)
- * @buffer: buffer to be used to lookup mntpath
- * @devpath: path devname/src_name (MAYBE NULL)
- * @devbuffer: buffer to be used to lookup devname/src_name
- * @type: string for the dev type (MAYBE NULL)
- * @flags: mount flags to match
- * @data: fs mount data (MAYBE NULL)
- * @binary: whether @data is binary
- *
- * Returns: 0 on success else error
- */
- static int match_mnt(struct aa_profile *profile, const struct path *path,
- char *buffer, struct path *devpath, char *devbuffer,
- const char *type, unsigned long flags, void *data,
- bool binary)
- {
- const char *devname = NULL, *info = NULL;
- int error = -EACCES;
- AA_BUG(!profile);
- AA_BUG(devpath && !devbuffer);
- if (!PROFILE_MEDIATES(profile, AA_CLASS_MOUNT))
- return 0;
- if (devpath) {
- error = aa_path_name(devpath, path_flags(profile, devpath),
- devbuffer, &devname, &info,
- profile->disconnected);
- if (error)
- devname = ERR_PTR(error);
- }
- return match_mnt_path_str(profile, path, buffer, devname, type, flags,
- data, binary, info);
- }
- int aa_remount(struct aa_label *label, const struct path *path,
- unsigned long flags, void *data)
- {
- struct aa_profile *profile;
- char *buffer = NULL;
- bool binary;
- int error;
- AA_BUG(!label);
- AA_BUG(!path);
- binary = path->dentry->d_sb->s_type->fs_flags & FS_BINARY_MOUNTDATA;
- get_buffers(buffer);
- error = fn_for_each_confined(label, profile,
- match_mnt(profile, path, buffer, NULL, NULL, NULL,
- flags, data, binary));
- put_buffers(buffer);
- return error;
- }
- int aa_bind_mount(struct aa_label *label, const struct path *path,
- const char *dev_name, unsigned long flags)
- {
- struct aa_profile *profile;
- char *buffer = NULL, *old_buffer = NULL;
- struct path old_path;
- int error;
- AA_BUG(!label);
- AA_BUG(!path);
- if (!dev_name || !*dev_name)
- return -EINVAL;
- flags &= MS_REC | MS_BIND;
- error = kern_path(dev_name, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &old_path);
- if (error)
- return error;
- get_buffers(buffer, old_buffer);
- error = fn_for_each_confined(label, profile,
- match_mnt(profile, path, buffer, &old_path, old_buffer,
- NULL, flags, NULL, false));
- put_buffers(buffer, old_buffer);
- path_put(&old_path);
- return error;
- }
- int aa_mount_change_type(struct aa_label *label, const struct path *path,
- unsigned long flags)
- {
- struct aa_profile *profile;
- char *buffer = NULL;
- int error;
- AA_BUG(!label);
- AA_BUG(!path);
- /* These are the flags allowed by do_change_type() */
- flags &= (MS_REC | MS_SILENT | MS_SHARED | MS_PRIVATE | MS_SLAVE |
- MS_UNBINDABLE);
- get_buffers(buffer);
- error = fn_for_each_confined(label, profile,
- match_mnt(profile, path, buffer, NULL, NULL, NULL,
- flags, NULL, false));
- put_buffers(buffer);
- return error;
- }
- int aa_move_mount(struct aa_label *label, const struct path *path,
- const char *orig_name)
- {
- struct aa_profile *profile;
- char *buffer = NULL, *old_buffer = NULL;
- struct path old_path;
- int error;
- AA_BUG(!label);
- AA_BUG(!path);
- if (!orig_name || !*orig_name)
- return -EINVAL;
- error = kern_path(orig_name, LOOKUP_FOLLOW, &old_path);
- if (error)
- return error;
- get_buffers(buffer, old_buffer);
- error = fn_for_each_confined(label, profile,
- match_mnt(profile, path, buffer, &old_path, old_buffer,
- NULL, MS_MOVE, NULL, false));
- put_buffers(buffer, old_buffer);
- path_put(&old_path);
- return error;
- }
- int aa_new_mount(struct aa_label *label, const char *dev_name,
- const struct path *path, const char *type, unsigned long flags,
- void *data)
- {
- struct aa_profile *profile;
- char *buffer = NULL, *dev_buffer = NULL;
- bool binary = true;
- int error;
- int requires_dev = 0;
- struct path tmp_path, *dev_path = NULL;
- AA_BUG(!label);
- AA_BUG(!path);
- if (type) {
- struct file_system_type *fstype;
- fstype = get_fs_type(type);
- if (!fstype)
- return -ENODEV;
- binary = fstype->fs_flags & FS_BINARY_MOUNTDATA;
- requires_dev = fstype->fs_flags & FS_REQUIRES_DEV;
- put_filesystem(fstype);
- if (requires_dev) {
- if (!dev_name || !*dev_name)
- return -ENOENT;
- error = kern_path(dev_name, LOOKUP_FOLLOW, &tmp_path);
- if (error)
- return error;
- dev_path = &tmp_path;
- }
- }
- get_buffers(buffer, dev_buffer);
- if (dev_path) {
- error = fn_for_each_confined(label, profile,
- match_mnt(profile, path, buffer, dev_path, dev_buffer,
- type, flags, data, binary));
- } else {
- error = fn_for_each_confined(label, profile,
- match_mnt_path_str(profile, path, buffer, dev_name,
- type, flags, data, binary, NULL));
- }
- put_buffers(buffer, dev_buffer);
- if (dev_path)
- path_put(dev_path);
- return error;
- }
- static int profile_umount(struct aa_profile *profile, struct path *path,
- char *buffer)
- {
- struct aa_perms perms = { };
- const char *name = NULL, *info = NULL;
- unsigned int state;
- int error;
- AA_BUG(!profile);
- AA_BUG(!path);
- if (!PROFILE_MEDIATES(profile, AA_CLASS_MOUNT))
- return 0;
- error = aa_path_name(path, path_flags(profile, path), buffer, &name,
- &info, profile->disconnected);
- if (error)
- goto audit;
- state = aa_dfa_match(profile->policy.dfa,
- profile->policy.start[AA_CLASS_MOUNT],
- name);
- perms = compute_mnt_perms(profile->policy.dfa, state);
- if (AA_MAY_UMOUNT & ~perms.allow)
- error = -EACCES;
- audit:
- return audit_mount(profile, OP_UMOUNT, name, NULL, NULL, NULL, 0, NULL,
- AA_MAY_UMOUNT, &perms, info, error);
- }
- int aa_umount(struct aa_label *label, struct vfsmount *mnt, int flags)
- {
- struct aa_profile *profile;
- char *buffer = NULL;
- int error;
- struct path path = { .mnt = mnt, .dentry = mnt->mnt_root };
- AA_BUG(!label);
- AA_BUG(!mnt);
- get_buffers(buffer);
- error = fn_for_each_confined(label, profile,
- profile_umount(profile, &path, buffer));
- put_buffers(buffer);
- return error;
- }
- /* helper fn for transition on pivotroot
- *
- * Returns: label for transition or ERR_PTR. Does not return NULL
- */
- static struct aa_label *build_pivotroot(struct aa_profile *profile,
- const struct path *new_path,
- char *new_buffer,
- const struct path *old_path,
- char *old_buffer)
- {
- const char *old_name, *new_name = NULL, *info = NULL;
- const char *trans_name = NULL;
- struct aa_perms perms = { };
- unsigned int state;
- int error;
- AA_BUG(!profile);
- AA_BUG(!new_path);
- AA_BUG(!old_path);
- if (profile_unconfined(profile) ||
- !PROFILE_MEDIATES(profile, AA_CLASS_MOUNT))
- return aa_get_newest_label(&profile->label);
- error = aa_path_name(old_path, path_flags(profile, old_path),
- old_buffer, &old_name, &info,
- profile->disconnected);
- if (error)
- goto audit;
- error = aa_path_name(new_path, path_flags(profile, new_path),
- new_buffer, &new_name, &info,
- profile->disconnected);
- if (error)
- goto audit;
- error = -EACCES;
- state = aa_dfa_match(profile->policy.dfa,
- profile->policy.start[AA_CLASS_MOUNT],
- new_name);
- state = aa_dfa_null_transition(profile->policy.dfa, state);
- state = aa_dfa_match(profile->policy.dfa, state, old_name);
- perms = compute_mnt_perms(profile->policy.dfa, state);
- if (AA_MAY_PIVOTROOT & perms.allow)
- error = 0;
- audit:
- error = audit_mount(profile, OP_PIVOTROOT, new_name, old_name,
- NULL, trans_name, 0, NULL, AA_MAY_PIVOTROOT,
- &perms, info, error);
- if (error)
- return ERR_PTR(error);
- return aa_get_newest_label(&profile->label);
- }
- int aa_pivotroot(struct aa_label *label, const struct path *old_path,
- const struct path *new_path)
- {
- struct aa_profile *profile;
- struct aa_label *target = NULL;
- char *old_buffer = NULL, *new_buffer = NULL, *info = NULL;
- int error;
- AA_BUG(!label);
- AA_BUG(!old_path);
- AA_BUG(!new_path);
- get_buffers(old_buffer, new_buffer);
- target = fn_label_build(label, profile, GFP_ATOMIC,
- build_pivotroot(profile, new_path, new_buffer,
- old_path, old_buffer));
- if (!target) {
- info = "label build failed";
- error = -ENOMEM;
- goto fail;
- } else if (!IS_ERR(target)) {
- error = aa_replace_current_label(target);
- if (error) {
- /* TODO: audit target */
- aa_put_label(target);
- goto out;
- }
- } else
- /* already audited error */
- error = PTR_ERR(target);
- out:
- put_buffers(old_buffer, new_buffer);
- return error;
- fail:
- /* TODO: add back in auditing of new_name and old_name */
- error = fn_for_each(label, profile,
- audit_mount(profile, OP_PIVOTROOT, NULL /*new_name */,
- NULL /* old_name */,
- NULL, NULL,
- 0, NULL, AA_MAY_PIVOTROOT, &nullperms, info,
- error));
- goto out;
- }
|