Merge branch 'for-security' of git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor into next

This commit is contained in:
James Morris 2012-03-15 14:43:02 +11:00
commit b01d3fb921
13 changed files with 189 additions and 52 deletions

View File

@ -15,7 +15,7 @@ clean-files := capability_names.h rlim_names.h
# to
# [1] = "dac_override",
quiet_cmd_make-caps = GEN $@
cmd_make-caps = echo "static const char *capability_names[] = {" > $@ ;\
cmd_make-caps = echo "static const char const *capability_names[] = {" > $@ ;\
sed $< >>$@ -r -n -e '/CAP_FS_MASK/d' \
-e 's/^\#define[ \t]+CAP_([A-Z0-9_]+)[ \t]+([0-9]+)/[\2] = "\L\1",/p';\
echo "};" >> $@
@ -43,7 +43,8 @@ cmd_make-caps = echo "static const char *capability_names[] = {" > $@ ;\
# to
# #define AA_FS_RLIMIT_MASK "fsize stack"
quiet_cmd_make-rlim = GEN $@
cmd_make-rlim = echo "static const char *rlim_names[RLIM_NLIMITS] = {" > $@ ;\
cmd_make-rlim = echo "static const char const *rlim_names[RLIM_NLIMITS] = {" \
> $@ ;\
sed $< >> $@ -r -n \
-e 's/^\# ?define[ \t]+(RLIMIT_([A-Z0-9_]+)).*/[\1] = "\L\2",/p';\
echo "};" >> $@ ;\

View File

@ -19,7 +19,7 @@
#include "include/audit.h"
#include "include/policy.h"
const char *op_table[] = {
const char *const op_table[] = {
"null",
"sysctl",
@ -73,7 +73,7 @@ const char *op_table[] = {
"profile_remove"
};
const char *audit_mode_names[] = {
const char *const audit_mode_names[] = {
"normal",
"quiet_denied",
"quiet",
@ -81,7 +81,7 @@ const char *audit_mode_names[] = {
"all"
};
static char *aa_audit_type[] = {
static const char *const aa_audit_type[] = {
"AUDIT",
"ALLOWED",
"DENIED",

View File

@ -372,13 +372,12 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
state = profile->file.start;
/* buffer freed below, name is pointer into buffer */
error = aa_get_name(&bprm->file->f_path, profile->path_flags, &buffer,
&name);
error = aa_path_name(&bprm->file->f_path, profile->path_flags, &buffer,
&name, &info);
if (error) {
if (profile->flags &
(PFLAG_IX_ON_NAME_ERROR | PFLAG_UNCONFINED))
error = 0;
info = "Exec failed name resolution";
name = bprm->filename;
goto audit;
}

View File

@ -278,22 +278,16 @@ int aa_path_perm(int op, struct aa_profile *profile, struct path *path,
int error;
flags |= profile->path_flags | (S_ISDIR(cond->mode) ? PATH_IS_DIR : 0);
error = aa_get_name(path, flags, &buffer, &name);
error = aa_path_name(path, flags, &buffer, &name, &info);
if (error) {
if (error == -ENOENT && is_deleted(path->dentry)) {
/* Access to open files that are deleted are
* give a pass (implicit delegation)
*/
error = 0;
info = NULL;
perms.allow = request;
} else if (error == -ENOENT)
info = "Failed name lookup - deleted entry";
else if (error == -ESTALE)
info = "Failed name lookup - disconnected path";
else if (error == -ENAMETOOLONG)
info = "Failed name lookup - name too long";
else
info = "Failed name lookup";
}
} else {
aa_str_perms(profile->file.dfa, profile->file.start, name, cond,
&perms);
@ -364,12 +358,14 @@ int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry,
lperms = nullperms;
/* buffer freed below, lname is pointer in buffer */
error = aa_get_name(&link, profile->path_flags, &buffer, &lname);
error = aa_path_name(&link, profile->path_flags, &buffer, &lname,
&info);
if (error)
goto audit;
/* buffer2 freed below, tname is pointer in buffer2 */
error = aa_get_name(&target, profile->path_flags, &buffer2, &tname);
error = aa_path_name(&target, profile->path_flags, &buffer2, &tname,
&info);
if (error)
goto audit;

View File

@ -19,6 +19,19 @@
#include "match.h"
/*
* Class of mediation types in the AppArmor policy db
*/
#define AA_CLASS_ENTRY 0
#define AA_CLASS_UNKNOWN 1
#define AA_CLASS_FILE 2
#define AA_CLASS_CAP 3
#define AA_CLASS_NET 4
#define AA_CLASS_RLIMITS 5
#define AA_CLASS_DOMAIN 6
#define AA_CLASS_LAST AA_CLASS_DOMAIN
/* Control parameters settable through module/boot flags */
extern enum audit_mode aa_g_audit;
extern bool aa_g_audit_header;
@ -81,7 +94,7 @@ static inline unsigned int aa_dfa_null_transition(struct aa_dfa *dfa,
unsigned int start)
{
/* the null transition only needs the string's null terminator byte */
return aa_dfa_match_len(dfa, start, "", 1);
return aa_dfa_next(dfa, start, 0);
}
static inline bool mediated_filesystem(struct inode *inode)

View File

@ -25,7 +25,7 @@
struct aa_profile;
extern const char *audit_mode_names[];
extern const char *const audit_mode_names[];
#define AUDIT_MAX_INDEX 5
enum audit_mode {
@ -47,7 +47,7 @@ enum audit_type {
AUDIT_APPARMOR_AUTO
};
extern const char *op_table[];
extern const char *const op_table[];
enum aa_ops {
OP_NULL,

View File

@ -116,6 +116,9 @@ unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start,
const char *str, int len);
unsigned int aa_dfa_match(struct aa_dfa *dfa, unsigned int start,
const char *str);
unsigned int aa_dfa_next(struct aa_dfa *dfa, unsigned int state,
const char c);
void aa_dfa_free_kref(struct kref *kref);
/**

View File

@ -26,6 +26,7 @@ enum path_flags {
PATH_MEDIATE_DELETED = 0x10000, /* mediate deleted paths */
};
int aa_get_name(struct path *path, int flags, char **buffer, const char **name);
int aa_path_name(struct path *path, int flags, char **buffer,
const char **name, const char **info);
#endif /* __AA_PATH_H */

View File

@ -29,7 +29,7 @@
#include "file.h"
#include "resource.h"
extern const char *profile_mode_names[];
extern const char *const profile_mode_names[];
#define APPARMOR_NAMES_MAX_INDEX 3
#define COMPLAIN_MODE(_profile) \
@ -129,6 +129,17 @@ struct aa_namespace {
struct list_head sub_ns;
};
/* struct aa_policydb - match engine for a policy
* dfa: dfa pattern match
* start: set of start states for the different classes of data
*/
struct aa_policydb {
/* Generic policy DFA specific rule types will be subsections of it */
struct aa_dfa *dfa;
unsigned int start[AA_CLASS_LAST + 1];
};
/* struct aa_profile - basic confinement data
* @base - base components of the profile (name, refcount, lists, lock ...)
* @parent: parent of profile
@ -143,6 +154,7 @@ struct aa_namespace {
* @flags: flags controlling profile behavior
* @path_flags: flags controlling path generation behavior
* @size: the memory consumed by this profiles rules
* @policy: general match rules governing policy
* @file: The set of rules governing basic file access and domain transitions
* @caps: capabilities for the profile
* @rlimits: rlimits for the profile
@ -179,6 +191,7 @@ struct aa_profile {
u32 path_flags;
int size;
struct aa_policydb policy;
struct aa_file_rules file;
struct aa_caps caps;
struct aa_rlimit rlimits;

View File

@ -335,12 +335,12 @@ unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start,
}
/**
* aa_dfa_next_state - traverse @dfa to find state @str stops at
* aa_dfa_match - traverse @dfa to find state @str stops at
* @dfa: the dfa to match @str against (NOT NULL)
* @start: the state of the dfa to start matching in
* @str: the null terminated string of bytes to match against the dfa (NOT NULL)
*
* aa_dfa_next_state will match @str against the dfa and return the state it
* aa_dfa_match will match @str against the dfa and return the state it
* finished matching in. The final state can be used to look up the accepting
* label, or as the start state of a continuing match.
*
@ -349,5 +349,79 @@ unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start,
unsigned int aa_dfa_match(struct aa_dfa *dfa, unsigned int start,
const char *str)
{
return aa_dfa_match_len(dfa, start, str, strlen(str));
u16 *def = DEFAULT_TABLE(dfa);
u32 *base = BASE_TABLE(dfa);
u16 *next = NEXT_TABLE(dfa);
u16 *check = CHECK_TABLE(dfa);
unsigned int state = start, pos;
if (state == 0)
return 0;
/* current state is <state>, matching character *str */
if (dfa->tables[YYTD_ID_EC]) {
/* Equivalence class table defined */
u8 *equiv = EQUIV_TABLE(dfa);
/* default is direct to next state */
while (*str) {
pos = base[state] + equiv[(u8) *str++];
if (check[pos] == state)
state = next[pos];
else
state = def[state];
}
} else {
/* default is direct to next state */
while (*str) {
pos = base[state] + (u8) *str++;
if (check[pos] == state)
state = next[pos];
else
state = def[state];
}
}
return state;
}
/**
* aa_dfa_next - step one character to the next state in the dfa
* @dfa: the dfa to tranverse (NOT NULL)
* @state: the state to start in
* @c: the input character to transition on
*
* aa_dfa_match will step through the dfa by one input character @c
*
* Returns: state reach after input @c
*/
unsigned int aa_dfa_next(struct aa_dfa *dfa, unsigned int state,
const char c)
{
u16 *def = DEFAULT_TABLE(dfa);
u32 *base = BASE_TABLE(dfa);
u16 *next = NEXT_TABLE(dfa);
u16 *check = CHECK_TABLE(dfa);
unsigned int pos;
/* current state is <state>, matching character *str */
if (dfa->tables[YYTD_ID_EC]) {
/* Equivalence class table defined */
u8 *equiv = EQUIV_TABLE(dfa);
/* default is direct to next state */
pos = base[state] + equiv[(u8) c];
if (check[pos] == state)
state = next[pos];
else
state = def[state];
} else {
/* default is direct to next state */
pos = base[state] + (u8) c;
if (check[pos] == state)
state = next[pos];
else
state = def[state];
}
return state;
}

View File

@ -83,30 +83,29 @@ static int d_namespace_path(struct path *path, char *buf, int buflen,
struct path root;
get_fs_root(current->fs, &root);
res = __d_path(path, &root, buf, buflen);
if (res && !IS_ERR(res)) {
/* everything's fine */
*name = res;
path_put(&root);
goto ok;
}
path_put(&root);
connected = 0;
} else
} else {
res = d_absolute_path(path, buf, buflen);
if (!our_mnt(path->mnt))
connected = 0;
}
*name = res;
/* handle error conditions - and still allow a partial path to
* be returned.
*/
if (IS_ERR(res)) {
error = PTR_ERR(res);
*name = buf;
goto out;
}
if (!our_mnt(path->mnt))
if (!res || IS_ERR(res)) {
connected = 0;
res = dentry_path_raw(path->dentry, buf, buflen);
if (IS_ERR(res)) {
error = PTR_ERR(res);
*name = buf;
goto out;
};
} else if (!our_mnt(path->mnt))
connected = 0;
ok:
*name = res;
/* Handle two cases:
* 1. A deleted dentry && profile is not allowing mediation of deleted
* 2. On some filesystems, newly allocated dentries appear to the
@ -137,7 +136,7 @@ ok:
/* disconnected path, don't return pathname starting
* with '/'
*/
error = -ESTALE;
error = -EACCES;
if (*res == '/')
*name = res + 1;
}
@ -158,7 +157,7 @@ out:
* Returns: %0 else error on failure
*/
static int get_name_to_buffer(struct path *path, int flags, char *buffer,
int size, char **name)
int size, char **name, const char **info)
{
int adjust = (flags & PATH_IS_DIR) ? 1 : 0;
int error = d_namespace_path(path, buffer, size - adjust, name, flags);
@ -170,15 +169,27 @@ static int get_name_to_buffer(struct path *path, int flags, char *buffer,
*/
strcpy(&buffer[size - 2], "/");
if (info && error) {
if (error == -ENOENT)
*info = "Failed name lookup - deleted entry";
else if (error == -ESTALE)
*info = "Failed name lookup - disconnected path";
else if (error == -ENAMETOOLONG)
*info = "Failed name lookup - name too long";
else
*info = "Failed name lookup";
}
return error;
}
/**
* aa_get_name - compute the pathname of a file
* aa_path_name - compute the pathname of a file
* @path: path the file (NOT NULL)
* @flags: flags controlling path name generation
* @buffer: buffer that aa_get_name() allocated (NOT NULL)
* @name: Returns - the generated path name if !error (NOT NULL)
* @info: Returns - information on why the path lookup failed (MAYBE NULL)
*
* @name is a pointer to the beginning of the pathname (which usually differs
* from the beginning of the buffer), or NULL. If there is an error @name
@ -191,7 +202,8 @@ static int get_name_to_buffer(struct path *path, int flags, char *buffer,
*
* Returns: %0 else error code if could retrieve name
*/
int aa_get_name(struct path *path, int flags, char **buffer, const char **name)
int aa_path_name(struct path *path, int flags, char **buffer, const char **name,
const char **info)
{
char *buf, *str = NULL;
int size = 256;
@ -205,7 +217,7 @@ int aa_get_name(struct path *path, int flags, char **buffer, const char **name)
if (!buf)
return -ENOMEM;
error = get_name_to_buffer(path, flags, buf, size, &str);
error = get_name_to_buffer(path, flags, buf, size, &str, info);
if (error != -ENAMETOOLONG)
break;
@ -213,6 +225,7 @@ int aa_get_name(struct path *path, int flags, char **buffer, const char **name)
size <<= 1;
if (size > aa_g_path_max)
return -ENAMETOOLONG;
*info = NULL;
}
*buffer = buf;
*name = str;

View File

@ -93,7 +93,7 @@
/* root profile namespace */
struct aa_namespace *root_ns;
const char *profile_mode_names[] = {
const char *const profile_mode_names[] = {
"enforce",
"complain",
"kill",
@ -749,6 +749,7 @@ static void free_profile(struct aa_profile *profile)
aa_free_sid(profile->sid);
aa_put_dfa(profile->xmatch);
aa_put_dfa(profile->policy.dfa);
aa_put_profile(profile->replacedby);

View File

@ -84,7 +84,7 @@ static void audit_cb(struct audit_buffer *ab, void *va)
* @new: profile if it has been allocated (MAYBE NULL)
* @name: name of the profile being manipulated (MAYBE NULL)
* @info: any extra info about the failure (MAYBE NULL)
* @e: buffer position info (NOT NULL)
* @e: buffer position info
* @error: error code
*
* Returns: %0 or error
@ -95,7 +95,8 @@ static int audit_iface(struct aa_profile *new, const char *name,
struct aa_profile *profile = __aa_current_profile();
struct common_audit_data sa;
COMMON_AUDIT_DATA_INIT(&sa, NONE);
sa.aad.iface.pos = e->pos - e->start;
if (e)
sa.aad.iface.pos = e->pos - e->start;
sa.aad.iface.target = new;
sa.aad.name = name;
sa.aad.info = info;
@ -468,7 +469,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e)
{
struct aa_profile *profile = NULL;
const char *name = NULL;
int error = -EPROTO;
int i, error = -EPROTO;
kernel_cap_t tmpcap;
u32 tmp;
@ -561,6 +562,28 @@ static struct aa_profile *unpack_profile(struct aa_ext *e)
if (!unpack_rlimits(e, profile))
goto fail;
if (unpack_nameX(e, AA_STRUCT, "policydb")) {
/* generic policy dfa - optional and may be NULL */
profile->policy.dfa = unpack_dfa(e);
if (IS_ERR(profile->policy.dfa)) {
error = PTR_ERR(profile->policy.dfa);
profile->policy.dfa = NULL;
goto fail;
}
if (!unpack_u32(e, &profile->policy.start[0], "start"))
/* default start state */
profile->policy.start[0] = DFA_START;
/* setup class index */
for (i = AA_CLASS_FILE; i <= AA_CLASS_LAST; i++) {
profile->policy.start[i] =
aa_dfa_next(profile->policy.dfa,
profile->policy.start[0],
i);
}
if (!unpack_nameX(e, AA_STRUCTEND, NULL))
goto fail;
}
/* get file rules */
profile->file.dfa = unpack_dfa(e);
if (IS_ERR(profile->file.dfa)) {