/*
* 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 <linux/ctype.h>
#include <linux/security.h>
#include <linux/vmalloc.h>
#include <linux/module.h>
#include <linux/seq_file.h>
#include <linux/uaccess.h>
#include <linux/namei.h>
#include <linux/capability.h>
#include <linux/rcupdate.h>
#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/resource.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(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
* @op: operation doing the user buffer copy
* @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 char *aa_simple_write_to_buffer(int op, const char __user *userbuf,
size_t alloc_size, size_t copy_size,
loff_t *pos)
{
char *data;
BUG_ON(copy_size > alloc_size);
if (*pos != 0)
/* only writes from pos 0, that is complete writes */
return ERR_PTR(-ESPIPE);
/*
* Don't allow profile load/replace/remove from profiles that don't
* have CAP_MAC_ADMIN
*/
if (!aa_may_manage_policy(op))
return ERR_PTR(-EACCES);
/* freed by caller to simple_write_to_buffer */
data = kvmalloc(alloc_size);
if (data == NULL)
return ERR_PTR(-ENOMEM);
if (copy_from_user(data, userbuf, copy_size)) {
kvfree(data);
return ERR_PTR(-EFAULT);
}
return data;
}
/* .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)
{
char *data;
ssize_t error;
data = aa_simple_write_to_buffer(OP_PROF_LOAD, buf, size, size, pos);
error = PTR_ERR(data);
if (!IS_ERR(data)) {
error = aa_replace_profiles(data, size, PROF_ADD);
kvfree(data);
}
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)
{
char *data;
ssize_t error;
data = aa_simple_write_to_buffer(OP_PROF_REPL, buf, size, size, pos);
error = PTR_ERR(data);
if (!IS_ERR(data)) {
error = aa_replace_profiles(data, size, PROF_REPLACE);
kvfree(data);
}
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)
{
char *data;
ssize_t error;
/*
* 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(OP_PROF_RM, buf, size + 1,