summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Morris <james.l.morris@oracle.com>2012-03-15 14:43:02 +1100
committerJames Morris <james.l.morris@oracle.com>2012-03-15 14:43:02 +1100
commitb01d3fb921df9baef1ecd13704f4b1e269b58b6b (patch)
tree1ca714b40774cd56c0194abee5c6577b2ba6aad2
parent6041e8346f2165679c2184cab60db768d6a26a1d (diff)
parent2d4cee7e3a2b9f9c3237672cc136e20dbad0e2ce (diff)
Merge branch 'for-security' of git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor into next
-rw-r--r--security/apparmor/Makefile5
-rw-r--r--security/apparmor/audit.c6
-rw-r--r--security/apparmor/domain.c5
-rw-r--r--security/apparmor/file.c18
-rw-r--r--security/apparmor/include/apparmor.h15
-rw-r--r--security/apparmor/include/audit.h4
-rw-r--r--security/apparmor/include/match.h3
-rw-r--r--security/apparmor/include/path.h3
-rw-r--r--security/apparmor/include/policy.h15
-rw-r--r--security/apparmor/match.c80
-rw-r--r--security/apparmor/path.c55
-rw-r--r--security/apparmor/policy.c3
-rw-r--r--security/apparmor/policy_unpack.c29
13 files changed, 189 insertions, 52 deletions
diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile
index 86103ce5b7a7..7e14edd9ec60 100644
--- a/security/apparmor/Makefile
+++ b/security/apparmor/Makefile
@@ -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 "};" >> $@ ;\
diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c
index 61344b56722e..5ff67776a5ad 100644
--- a/security/apparmor/audit.c
+++ b/security/apparmor/audit.c
@@ -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",
diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c
index c1e18ba5bdc0..7c69599a69e1 100644
--- a/security/apparmor/domain.c
+++ b/security/apparmor/domain.c
@@ -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;
}
diff --git a/security/apparmor/file.c b/security/apparmor/file.c
index bba875c4d068..3022c0f4f0db 100644
--- a/security/apparmor/file.c
+++ b/security/apparmor/file.c
@@ -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;
diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h
index df3649560818..40aedd9f73ea 100644
--- a/security/apparmor/include/apparmor.h
+++ b/security/apparmor/include/apparmor.h
@@ -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)
diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h
index 9317cd81416c..4ba78c203af1 100644
--- a/security/apparmor/include/audit.h
+++ b/security/apparmor/include/audit.h
@@ -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,
diff --git a/security/apparmor/include/match.h b/security/apparmor/include/match.h
index a4a863997bd5..775843e7f984 100644
--- a/security/apparmor/include/match.h
+++ b/security/apparmor/include/match.h
@@ -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);
/**
diff --git a/security/apparmor/include/path.h b/security/apparmor/include/path.h
index 27b327a7fae5..286ac75dc88b 100644
--- a/security/apparmor/include/path.h
+++ b/security/apparmor/include/path.h
@@ -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 */
diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h
index aeda5cf56904..bda4569fdd83 100644
--- a/security/apparmor/include/policy.h
+++ b/security/apparmor/include/policy.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;
diff --git a/security/apparmor/match.c b/security/apparmor/match.c
index 94de6b4907c8..90971a8c3789 100644
--- a/security/apparmor/match.c
+++ b/security/apparmor/match.c
@@ -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;
}
diff --git a/security/apparmor/path.c b/security/apparmor/path.c
index c31ce837fef4..2daeea4f9266 100644
--- a/security/apparmor/path.c
+++ b/security/apparmor/path.c
@@ -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;
+
+ *name = res;
-ok:
/* 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;
diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c
index 4f0eadee78b8..906414383022 100644
--- a/security/apparmor/policy.c
+++ b/security/apparmor/policy.c
@@ -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);
diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c
index 5c46acf5aa65..25fd51edc8da 100644
--- a/security/apparmor/policy_unpack.c
+++ b/security/apparmor/policy_unpack.c
@@ -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)) {