/* * AppArmor security module * * This file contains AppArmor /sys/kernel/security/apparmor interface functions * * Copyright (C) 1998-2008 Novell/SUSE * Copyright 2009-2010 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 #include #include #include #include #include #include #include #include #include #include #include #include "include/apparmor.h" #include "include/apparmorfs.h" #include "include/audit.h" #include "include/context.h" #include "include/crypto.h" #include "include/policy.h" #include "include/policy_ns.h" #include "include/resource.h" #include "include/policy_unpack.h" /** * aa_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) { char *t = target; while (*name == '/' || *name == '.') name++; if (target) { for (; *name; name++) { if (*name == '/') *(t)++ = '.'; else if (isspace(*name)) *(t)++ = '_'; else if (isalnum(*name) || strchr("._-", *name)) *(t)++ = *name; } *t = 0; } else { int len = 0; for (; *name; name++) { if (isalnum(*name) || isspace(*name) || strchr("/._-", *name)) len++; } return len; } return t - target; } /** * 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) { struct aa_loaddata *data; AA_BUG(copy_size > alloc_size); if (*pos != 0) /* only writes from pos 0, that is complete writes */ return ERR_PTR(-ESPIPE); /* freed by caller to simple_write_to_buffer */ data = kvmalloc(sizeof(*data) + alloc_size); if (data == NULL) return ERR_PTR(-ENOMEM); kref_init(&data->count); data->size = copy_size; data->hash = NULL; data->abi = 0; if (copy_from_user(data->data, userbuf, copy_size)) { kvfree(data); return ERR_PTR(-EFAULT); } return data; } static ssize_t policy_update(int binop, const char __user *buf, size_t size, loff_t *pos, struct aa_ns *ns) { ssize_t error; struct aa_loaddata *data; struct aa_profile *profile = aa_current_profile(); const char *op = binop == PROF_ADD ? OP_PROF_LOAD : OP_PROF_REPL; /* high level check about policy management - fine grained in * below after unpack */ error = aa_may_manage_policy(profile, ns, op); if (error) return error; data = aa_simple_write_to_buffer(buf, size, size, pos); error = PTR_ERR(data); if (!IS_ERR(data)) { error = aa_replace_profiles(ns ? ns : profile->ns, profile, binop, data); aa_put_loaddata(data); } return error; } /* .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) { struct aa_ns *ns = aa_get_ns(f->f_inode->i_private); int error = policy_update(PROF_ADD, buf, size, pos, ns); aa_put_ns(ns); return error; } static const struct file_operations aa_fs_profile_load = { .write = profile_load, .llseek = default_llseek, }; /* .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) { struct aa_ns *ns = aa_get_ns(f->f_inode->i_private); int error = policy_update(PROF_REPLACE, buf, size, pos, ns); aa_put_ns(ns); return error; } static const struct file_operations aa_fs_profile_replace = { .write = profile_replace, .llseek = default_llseek, }; /* .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) { struct aa_loaddata *data; struct aa_profile *profile; ssize_t error; struct aa_ns *ns = aa_get_ns(f->f_inode->i_private); profile = aa_current_profile(); /* high level check about policy management - fine grained in * below after unpack */ error = aa_may_manage_policy(profile, ns, OP_PROF_RM); if (error) goto out; /* * aa_remove_profile needs a null terminated string so 1 extra * byte is allocated and the copied data is null terminated. */ data = aa_simple_write_to_buffer(buf, size + 1, size, pos); error = PTR_ERR(data); if (!IS_ERR(data)) { data->data[size] = 0; error = aa_remove_profiles(ns ? ns : profile->ns, profile, data->data, size); aa_put_loaddata(data); } out: aa_put_ns(ns); return error; } static const struct file_operations aa_fs_profile_remove = { .write = profile_remove, .llseek = default_llseek, }; /** * 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 "