Merge branch 'for-security' of git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor into next
This commit is contained in:
commit
b01d3fb921
|
@ -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 "};" >> $@ ;\
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
/**
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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)) {
|
||||
|
|
Loading…
Reference in New Issue