linux/security/apparmor/apparmorfs.c

// SPDX-License-Identifier: GPL-2.0-only
/*
 * AppArmor security module
 *
 * This file contains AppArmor /sys/kernel/security/apparmor interface functions
 *
 * Copyright (C) 1998-2008 Novell/SUSE
 * Copyright 2009-2010 Canonical Ltd.
 */

#include <linux/ctype.h>
#include <linux/security.h>
#include <linux/vmalloc.h>
#include <linux/init.h>
#include <linux/seq_file.h>
#include <linux/uaccess.h>
#include <linux/mount.h>
#include <linux/namei.h>
#include <linux/capability.h>
#include <linux/rcupdate.h>
#include <linux/fs.h>
#include <linux/fs_context.h>
#include <linux/poll.h>
#include <linux/zstd.h>
#include <uapi/linux/major.h>
#include <uapi/linux/magic.h>

#include "include/apparmor.h"
#include "include/apparmorfs.h"
#include "include/audit.h"
#include "include/cred.h"
#include "include/crypto.h"
#include "include/ipc.h"
#include "include/label.h"
#include "include/policy.h"
#include "include/policy_ns.h"
#include "include/resource.h"
#include "include/policy_unpack.h"
#include "include/task.h"

/*
 * The apparmor filesystem interface used for policy load and introspection
 * The interface is split into two main components based on their function
 * a securityfs component:
 *   used for static files that are always available, and which allows
 *   userspace to specificy the location of the security filesystem.
 *
 *   fns and data are prefixed with
 *      aa_sfs_
 *
 * an apparmorfs component:
 *   used loaded policy content and introspection. It is not part of  a
 *   regular mounted filesystem and is available only through the magic
 *   policy symlink in the root of the securityfs apparmor/ directory.
 *   Tasks queries will be magically redirected to the correct portion
 *   of the policy tree based on their confinement.
 *
 *   fns and data are prefixed with
 *      aafs_
 *
 * The aa_fs_ prefix is used to indicate the fn is used by both the
 * securityfs and apparmorfs filesystems.
 */


/*
 * support fns
 */

struct rawdata_f_data {};

#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY
#define RAWDATA_F_DATA_BUF(p)

static void rawdata_f_data_free(struct rawdata_f_data *private)
{}

static struct rawdata_f_data *rawdata_f_data_alloc(size_t size)
{}
#endif

/**
 * mangle_name - mangle a profile name to std profile layout form
 * @name: profile name to mangle  (NOT NULL)
 * @target: buffer to store mangled name, same length as @name (MAYBE NULL)
 *
 * Returns: length of mangled name
 */
static int mangle_name(const char *name, char *target)
{}


/*
 * aafs - core fns and data for the policy tree
 */

#define AAFS_NAME
static struct vfsmount *aafs_mnt;
static int aafs_count;


static int aafs_show_path(struct seq_file *seq, struct dentry *dentry)
{}

static void aafs_free_inode(struct inode *inode)
{}

static const struct super_operations aafs_super_ops =;

static int apparmorfs_fill_super(struct super_block *sb, struct fs_context *fc)
{}

static int apparmorfs_get_tree(struct fs_context *fc)
{}

static const struct fs_context_operations apparmorfs_context_ops =;

static int apparmorfs_init_fs_context(struct fs_context *fc)
{}

static struct file_system_type aafs_ops =;

/**
 * __aafs_setup_d_inode - basic inode setup for apparmorfs
 * @dir: parent directory for the dentry
 * @dentry: dentry we are seting the inode up for
 * @mode: permissions the file should have
 * @data: data to store on inode.i_private, available in open()
 * @link: if symlink, symlink target string
 * @fops: struct file_operations that should be used
 * @iops: struct of inode_operations that should be used
 */
static int __aafs_setup_d_inode(struct inode *dir, struct dentry *dentry,
			       umode_t mode, void *data, char *link,
			       const struct file_operations *fops,
			       const struct inode_operations *iops)
{}

/**
 * aafs_create - create a dentry in the apparmorfs filesystem
 *
 * @name: name of dentry to create
 * @mode: permissions the file should have
 * @parent: parent directory for this dentry
 * @data: data to store on inode.i_private, available in open()
 * @link: if symlink, symlink target string
 * @fops: struct file_operations that should be used for
 * @iops: struct of inode_operations that should be used
 *
 * This is the basic "create a xxx" function for apparmorfs.
 *
 * Returns a pointer to a dentry if it succeeds, that must be free with
 * aafs_remove(). Will return ERR_PTR on failure.
 */
static struct dentry *aafs_create(const char *name, umode_t mode,
				  struct dentry *parent, void *data, void *link,
				  const struct file_operations *fops,
				  const struct inode_operations *iops)
{}

/**
 * aafs_create_file - create a file in the apparmorfs filesystem
 *
 * @name: name of dentry to create
 * @mode: permissions the file should have
 * @parent: parent directory for this dentry
 * @data: data to store on inode.i_private, available in open()
 * @fops: struct file_operations that should be used for
 *
 * see aafs_create
 */
static struct dentry *aafs_create_file(const char *name, umode_t mode,
				       struct dentry *parent, void *data,
				       const struct file_operations *fops)
{}

/**
 * aafs_create_dir - create a directory in the apparmorfs filesystem
 *
 * @name: name of dentry to create
 * @parent: parent directory for this dentry
 *
 * see aafs_create
 */
static struct dentry *aafs_create_dir(const char *name, struct dentry *parent)
{}

/**
 * aafs_remove - removes a file or directory from the apparmorfs filesystem
 *
 * @dentry: dentry of the file/directory/symlink to removed.
 */
static void aafs_remove(struct dentry *dentry)
{}


/*
 * aa_fs - policy load/replace/remove
 */

/**
 * aa_simple_write_to_buffer - common routine for getting policy from user
 * @userbuf: user buffer to copy data from  (NOT NULL)
 * @alloc_size: size of user buffer (REQUIRES: @alloc_size >= @copy_size)
 * @copy_size: size of data to copy from user buffer
 * @pos: position write is at in the file (NOT NULL)
 *
 * Returns: kernel buffer containing copy of user buffer data or an
 *          ERR_PTR on failure.
 */
static struct aa_loaddata *aa_simple_write_to_buffer(const char __user *userbuf,
						     size_t alloc_size,
						     size_t copy_size,
						     loff_t *pos)
{}

static ssize_t policy_update(u32 mask, const char __user *buf, size_t size,
			     loff_t *pos, struct aa_ns *ns)
{}

/* .load file hook fn to load policy */
static ssize_t profile_load(struct file *f, const char __user *buf, size_t size,
			    loff_t *pos)
{}

static const struct file_operations aa_fs_profile_load =;

/* .replace file hook fn to load and/or replace policy */
static ssize_t profile_replace(struct file *f, const char __user *buf,
			       size_t size, loff_t *pos)
{}

static const struct file_operations aa_fs_profile_replace =;

/* .remove file hook fn to remove loaded policy */
static ssize_t profile_remove(struct file *f, const char __user *buf,
			      size_t size, loff_t *pos)
{}

static const struct file_operations aa_fs_profile_remove =;

struct aa_revision {};

/* revision file hook fn for policy loads */
static int ns_revision_release(struct inode *inode, struct file *file)
{}

static ssize_t ns_revision_read(struct file *file, char __user *buf,
				size_t size, loff_t *ppos)
{}

static int ns_revision_open(struct inode *inode, struct file *file)
{}

static __poll_t ns_revision_poll(struct file *file, poll_table *pt)
{}

void __aa_bump_ns_revision(struct aa_ns *ns)
{}

static const struct file_operations aa_fs_ns_revision_fops =;

static void profile_query_cb(struct aa_profile *profile, struct aa_perms *perms,
			     const char *match_str, size_t match_len)
{}


/**
 * query_data - queries a policy and writes its data to buf
 * @buf: the resulting data is stored here (NOT NULL)
 * @buf_len: size of buf
 * @query: query string used to retrieve data
 * @query_len: size of query including second NUL byte
 *
 * The buffers pointed to by buf and query may overlap. The query buffer is
 * parsed before buf is written to.
 *
 * The query should look like "<LABEL>\0<KEY>\0", where <LABEL> is the name of
 * the security confinement context and <KEY> is the name of the data to
 * retrieve. <LABEL> and <KEY> must not be NUL-terminated.
 *
 * Don't expect the contents of buf to be preserved on failure.
 *
 * Returns: number of characters written to buf or -errno on failure
 */
static ssize_t query_data(char *buf, size_t buf_len,
			  char *query, size_t query_len)
{}

/**
 * query_label - queries a label and writes permissions to buf
 * @buf: the resulting permissions string is stored here (NOT NULL)
 * @buf_len: size of buf
 * @query: binary query string to match against the dfa
 * @query_len: size of query
 * @view_only: only compute for querier's view
 *
 * The buffers pointed to by buf and query may overlap. The query buffer is
 * parsed before buf is written to.
 *
 * The query should look like "LABEL_NAME\0DFA_STRING" where LABEL_NAME is
 * the name of the label, in the current namespace, that is to be queried and
 * DFA_STRING is a binary string to match against the label(s)'s DFA.
 *
 * LABEL_NAME must be NUL terminated. DFA_STRING may contain NUL characters
 * but must *not* be NUL terminated.
 *
 * Returns: number of characters written to buf or -errno on failure
 */
static ssize_t query_label(char *buf, size_t buf_len,
			   char *query, size_t query_len, bool view_only)
{}

/*
 * Transaction based IO.
 * The file expects a write which triggers the transaction, and then
 * possibly a read(s) which collects the result - which is stored in a
 * file-local buffer. Once a new write is performed, a new set of results
 * are stored in the file-local buffer.
 */
struct multi_transaction {};

#define MULTI_TRANSACTION_LIMIT

static void multi_transaction_kref(struct kref *kref)
{}

static struct multi_transaction *
get_multi_transaction(struct multi_transaction *t)
{}

static void put_multi_transaction(struct multi_transaction *t)
{}

/* does not increment @new's count */
static void multi_transaction_set(struct file *file,
				  struct multi_transaction *new, size_t n)
{}

static struct multi_transaction *multi_transaction_new(struct file *file,
						       const char __user *buf,
						       size_t size)
{}

static ssize_t multi_transaction_read(struct file *file, char __user *buf,
				       size_t size, loff_t *pos)
{}

static int multi_transaction_release(struct inode *inode, struct file *file)
{}

#define QUERY_CMD_LABEL
#define QUERY_CMD_LABEL_LEN
#define QUERY_CMD_PROFILE
#define QUERY_CMD_PROFILE_LEN
#define QUERY_CMD_LABELALL
#define QUERY_CMD_LABELALL_LEN
#define QUERY_CMD_DATA
#define QUERY_CMD_DATA_LEN

/**
 * aa_write_access - generic permissions and data query
 * @file: pointer to open apparmorfs/access file
 * @ubuf: user buffer containing the complete query string (NOT NULL)
 * @count: size of ubuf
 * @ppos: position in the file (MUST BE ZERO)
 *
 * Allows for one permissions or data query per open(), write(), and read()
 * sequence. The only queries currently supported are label-based queries for
 * permissions or data.
 *
 * For permissions queries, ubuf must begin with "label\0", followed by the
 * profile query specific format described in the query_label() function
 * documentation.
 *
 * For data queries, ubuf must have the form "data\0<LABEL>\0<KEY>\0", where
 * <LABEL> is the name of the security confinement context and <KEY> is the
 * name of the data to retrieve.
 *
 * Returns: number of bytes written or -errno on failure
 */
static ssize_t aa_write_access(struct file *file, const char __user *ubuf,
			       size_t count, loff_t *ppos)
{}

static const struct file_operations aa_sfs_access =;

static int aa_sfs_seq_show(struct seq_file *seq, void *v)
{}

static int aa_sfs_seq_open(struct inode *inode, struct file *file)
{}

const struct file_operations aa_sfs_seq_file_ops =;

/*
 * profile based file operations
 *     policy/profiles/XXXX/profiles/ *
 */

#define SEQ_PROFILE_FOPS(NAME)									      \

static int seq_profile_open(struct inode *inode, struct file *file,
			    int (*show)(struct seq_file *, void *))
{}

static int seq_profile_release(struct inode *inode, struct file *file)
{}

static int seq_profile_name_show(struct seq_file *seq, void *v)
{}

static int seq_profile_mode_show(struct seq_file *seq, void *v)
{}

static int seq_profile_attach_show(struct seq_file *seq, void *v)
{}

static int seq_profile_hash_show(struct seq_file *seq, void *v)
{}

SEQ_PROFILE_FOPS();
SEQ_PROFILE_FOPS();
SEQ_PROFILE_FOPS();
SEQ_PROFILE_FOPS();

/*
 * namespace based files
 *     several root files and
 *     policy/ *
 */

#define SEQ_NS_FOPS(NAME)									      \

static int seq_ns_stacked_show(struct seq_file *seq, void *v)
{}

static int seq_ns_nsstacked_show(struct seq_file *seq, void *v)
{}

static int seq_ns_level_show(struct seq_file *seq, void *v)
{}

static int seq_ns_name_show(struct seq_file *seq, void *v)
{}

static int seq_ns_compress_min_show(struct seq_file *seq, void *v)
{}

static int seq_ns_compress_max_show(struct seq_file *seq, void *v)
{}

SEQ_NS_FOPS();
SEQ_NS_FOPS();
SEQ_NS_FOPS();
SEQ_NS_FOPS();
SEQ_NS_FOPS();
SEQ_NS_FOPS();


/* policy/raw_data/ * file ops */
#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY
#define SEQ_RAWDATA_FOPS(NAME)									      \

static int seq_rawdata_open(struct inode *inode, struct file *file,
			    int (*show)(struct seq_file *, void *))
{}

static int seq_rawdata_release(struct inode *inode, struct file *file)
{}

static int seq_rawdata_abi_show(struct seq_file *seq, void *v)
{}

static int seq_rawdata_revision_show(struct seq_file *seq, void *v)
{}

static int seq_rawdata_hash_show(struct seq_file *seq, void *v)
{}

static int seq_rawdata_compressed_size_show(struct seq_file *seq, void *v)
{}

SEQ_RAWDATA_FOPS();
SEQ_RAWDATA_FOPS();
SEQ_RAWDATA_FOPS();
SEQ_RAWDATA_FOPS();

static int decompress_zstd(char *src, size_t slen, char *dst, size_t dlen)
{}

static ssize_t rawdata_read(struct file *file, char __user *buf, size_t size,
			    loff_t *ppos)
{}

static int rawdata_release(struct inode *inode, struct file *file)
{}

static int rawdata_open(struct inode *inode, struct file *file)
{}

static const struct file_operations rawdata_fops =;

static void remove_rawdata_dents(struct aa_loaddata *rawdata)
{}

void __aa_fs_remove_rawdata(struct aa_loaddata *rawdata)
{}

int __aa_fs_create_rawdata(struct aa_ns *ns, struct aa_loaddata *rawdata)
{}
#endif /* CONFIG_SECURITY_APPARMOR_EXPORT_BINARY */


/** fns to setup dynamic per profile/namespace files **/

/*
 *
 * Requires: @profile->ns->lock held
 */
void __aafs_profile_rmdir(struct aa_profile *profile)
{}

/*
 *
 * Requires: @old->ns->lock held
 */
void __aafs_profile_migrate_dents(struct aa_profile *old,
				  struct aa_profile *new)
{}

static struct dentry *create_profile_file(struct dentry *dir, const char *name,
					  struct aa_profile *profile,
					  const struct file_operations *fops)
{}

#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY
static int profile_depth(struct aa_profile *profile)
{}

static char *gen_symlink_name(int depth, const char *dirname, const char *fname)
{}

static const char *rawdata_get_link_base(struct dentry *dentry,
					 struct inode *inode,
					 struct delayed_call *done,
					 const char *name)
{}

static const char *rawdata_get_link_sha256(struct dentry *dentry,
					 struct inode *inode,
					 struct delayed_call *done)
{}

static const char *rawdata_get_link_abi(struct dentry *dentry,
					struct inode *inode,
					struct delayed_call *done)
{}

static const char *rawdata_get_link_data(struct dentry *dentry,
					 struct inode *inode,
					 struct delayed_call *done)
{}

static const struct inode_operations rawdata_link_sha256_iops =;

static const struct inode_operations rawdata_link_abi_iops =;
static const struct inode_operations rawdata_link_data_iops =;
#endif /* CONFIG_SECURITY_APPARMOR_EXPORT_BINARY */

/*
 * Requires: @profile->ns->lock held
 */
int __aafs_profile_mkdir(struct aa_profile *profile, struct dentry *parent)
{}

static int ns_mkdir_op(struct mnt_idmap *idmap, struct inode *dir,
		       struct dentry *dentry, umode_t mode)
{}

static int ns_rmdir_op(struct inode *dir, struct dentry *dentry)
{}

static const struct inode_operations ns_dir_inode_operations =;

static void __aa_fs_list_remove_rawdata(struct aa_ns *ns)
{}

/*
 *
 * Requires: @ns->lock held
 */
void __aafs_ns_rmdir(struct aa_ns *ns)
{}

/* assumes cleanup in caller */
static int __aafs_ns_mkdir_entries(struct aa_ns *ns, struct dentry *dir)
{}

/*
 * Requires: @ns->lock held
 */
int __aafs_ns_mkdir(struct aa_ns *ns, struct dentry *parent, const char *name,
		    struct dentry *dent)
{}

/**
 * __next_ns - find the next namespace to list
 * @root: root namespace to stop search at (NOT NULL)
 * @ns: current ns position (NOT NULL)
 *
 * Find the next namespace from @ns under @root and handle all locking needed
 * while switching current namespace.
 *
 * Returns: next namespace or NULL if at last namespace under @root
 * Requires: ns->parent->lock to be held
 * NOTE: will not unlock root->lock
 */
static struct aa_ns *__next_ns(struct aa_ns *root, struct aa_ns *ns)
{}

/**
 * __first_profile - find the first profile in a namespace
 * @root: namespace that is root of profiles being displayed (NOT NULL)
 * @ns: namespace to start in   (NOT NULL)
 *
 * Returns: unrefcounted profile or NULL if no profile
 * Requires: profile->ns.lock to be held
 */
static struct aa_profile *__first_profile(struct aa_ns *root,
					  struct aa_ns *ns)
{}

/**
 * __next_profile - step to the next profile in a profile tree
 * @p: current profile in tree (NOT NULL)
 *
 * Perform a depth first traversal on the profile tree in a namespace
 *
 * Returns: next profile or NULL if done
 * Requires: profile->ns.lock to be held
 */
static struct aa_profile *__next_profile(struct aa_profile *p)
{}

/**
 * next_profile - step to the next profile in where ever it may be
 * @root: root namespace  (NOT NULL)
 * @profile: current profile  (NOT NULL)
 *
 * Returns: next profile or NULL if there isn't one
 */
static struct aa_profile *next_profile(struct aa_ns *root,
				       struct aa_profile *profile)
{}

/**
 * p_start - start a depth first traversal of profile tree
 * @f: seq_file to fill
 * @pos: current position
 *
 * Returns: first profile under current namespace or NULL if none found
 *
 * acquires first ns->lock
 */
static void *p_start(struct seq_file *f, loff_t *pos)
{}

/**
 * p_next - read the next profile entry
 * @f: seq_file to fill
 * @p: profile previously returned
 * @pos: current position
 *
 * Returns: next profile after @p or NULL if none
 *
 * may acquire/release locks in namespace tree as necessary
 */
static void *p_next(struct seq_file *f, void *p, loff_t *pos)
{}

/**
 * p_stop - stop depth first traversal
 * @f: seq_file we are filling
 * @p: the last profile writen
 *
 * Release all locking done by p_start/p_next on namespace tree
 */
static void p_stop(struct seq_file *f, void *p)
{}

/**
 * seq_show_profile - show a profile entry
 * @f: seq_file to file
 * @p: current position (profile)    (NOT NULL)
 *
 * Returns: error on failure
 */
static int seq_show_profile(struct seq_file *f, void *p)
{}

static const struct seq_operations aa_sfs_profiles_op =;

static int profiles_open(struct inode *inode, struct file *file)
{}

static int profiles_release(struct inode *inode, struct file *file)
{}

static const struct file_operations aa_sfs_profiles_fops =;


/** Base file system setup **/
static struct aa_sfs_entry aa_sfs_entry_file[] =;

static struct aa_sfs_entry aa_sfs_entry_ptrace[] =;

static struct aa_sfs_entry aa_sfs_entry_signal[] =;

static struct aa_sfs_entry aa_sfs_entry_attach[] =;
static struct aa_sfs_entry aa_sfs_entry_domain[] =;

static struct aa_sfs_entry aa_sfs_entry_unconfined[] =;

static struct aa_sfs_entry aa_sfs_entry_versions[] =;

#define PERMS32STR
static struct aa_sfs_entry aa_sfs_entry_policy[] =;

static struct aa_sfs_entry aa_sfs_entry_mount[] =;

static struct aa_sfs_entry aa_sfs_entry_ns[] =;

static struct aa_sfs_entry aa_sfs_entry_query_label[] =;

static struct aa_sfs_entry aa_sfs_entry_query[] =;

static struct aa_sfs_entry aa_sfs_entry_io_uring[] =;

static struct aa_sfs_entry aa_sfs_entry_features[] =;

static struct aa_sfs_entry aa_sfs_entry_apparmor[] =;

static struct aa_sfs_entry aa_sfs_entry =;

/**
 * entry_create_file - create a file entry in the apparmor securityfs
 * @fs_file: aa_sfs_entry to build an entry for (NOT NULL)
 * @parent: the parent dentry in the securityfs
 *
 * Use entry_remove_file to remove entries created with this fn.
 */
static int __init entry_create_file(struct aa_sfs_entry *fs_file,
				    struct dentry *parent)
{}

static void __init entry_remove_dir(struct aa_sfs_entry *fs_dir);
/**
 * entry_create_dir - recursively create a directory entry in the securityfs
 * @fs_dir: aa_sfs_entry (and all child entries) to build (NOT NULL)
 * @parent: the parent dentry in the securityfs
 *
 * Use entry_remove_dir to remove entries created with this fn.
 */
static int __init entry_create_dir(struct aa_sfs_entry *fs_dir,
				   struct dentry *parent)
{}

/**
 * entry_remove_file - drop a single file entry in the apparmor securityfs
 * @fs_file: aa_sfs_entry to detach from the securityfs (NOT NULL)
 */
static void __init entry_remove_file(struct aa_sfs_entry *fs_file)
{}

/**
 * entry_remove_dir - recursively drop a directory entry from the securityfs
 * @fs_dir: aa_sfs_entry (and all child entries) to detach (NOT NULL)
 */
static void __init entry_remove_dir(struct aa_sfs_entry *fs_dir)
{}

/**
 * aa_destroy_aafs - cleanup and free aafs
 *
 * releases dentries allocated by aa_create_aafs
 */
void __init aa_destroy_aafs(void)
{}


#define NULL_FILE_NAME
struct path aa_null;

static int aa_mk_null_file(struct dentry *parent)
{}



static const char *policy_get_link(struct dentry *dentry,
				   struct inode *inode,
				   struct delayed_call *done)
{}

static int policy_readlink(struct dentry *dentry, char __user *buffer,
			   int buflen)
{}

static const struct inode_operations policy_link_iops =;


/**
 * aa_create_aafs - create the apparmor security filesystem
 *
 * dentries created here are released by aa_destroy_aafs
 *
 * Returns: error on failure
 */
static int __init aa_create_aafs(void)
{}

fs_initcall(aa_create_aafs);