Commit 11c7b03d authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security

Pull security subsystem updates from James Morris:
 "Nothing major for this kernel, just maintenance updates"

* 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security: (21 commits)
  apparmor: add the ability to report a sha1 hash of loaded policy
  apparmor: export set of capabilities supported by the apparmor module
  apparmor: add the profile introspection file to interface
  apparmor: add an optional profile attachment string for profiles
  apparmor: add interface files for profiles and namespaces
  apparmor: allow setting any profile into the unconfined state
  apparmor: make free_profile available outside of policy.c
  apparmor: rework namespace free path
  apparmor: update how unconfined is handled
  apparmor: change how profile replacement update is done
  apparmor: convert profile lists to RCU based locking
  apparmor: provide base for multiple profiles to be replaced at once
  apparmor: add a features/policy dir to interface
  apparmor: enable users to query whether apparmor is enabled
  apparmor: remove minimum size check for vmalloc()
  Smack: parse multiple rules per write to load2, up to PAGE_SIZE-1 bytes
  Smack: network label match fix
  security: smack: add a hash table to quicken smk_find_entry()
  security: smack: fix memleak in smk_write_rules_list()
  xattr: Constify ->name member of "struct xattr".
  ...
parents 6be48f29 73203361
...@@ -32,7 +32,7 @@ enum ocfs2_xattr_type { ...@@ -32,7 +32,7 @@ enum ocfs2_xattr_type {
struct ocfs2_security_xattr_info { struct ocfs2_security_xattr_info {
int enable; int enable;
char *name; const char *name;
void *value; void *value;
size_t value_len; size_t value_len;
}; };
......
...@@ -1492,7 +1492,7 @@ struct security_operations { ...@@ -1492,7 +1492,7 @@ struct security_operations {
int (*inode_alloc_security) (struct inode *inode); int (*inode_alloc_security) (struct inode *inode);
void (*inode_free_security) (struct inode *inode); void (*inode_free_security) (struct inode *inode);
int (*inode_init_security) (struct inode *inode, struct inode *dir, int (*inode_init_security) (struct inode *inode, struct inode *dir,
const struct qstr *qstr, char **name, const struct qstr *qstr, const char **name,
void **value, size_t *len); void **value, size_t *len);
int (*inode_create) (struct inode *dir, int (*inode_create) (struct inode *dir,
struct dentry *dentry, umode_t mode); struct dentry *dentry, umode_t mode);
...@@ -1770,7 +1770,7 @@ int security_inode_init_security(struct inode *inode, struct inode *dir, ...@@ -1770,7 +1770,7 @@ int security_inode_init_security(struct inode *inode, struct inode *dir,
const struct qstr *qstr, const struct qstr *qstr,
initxattrs initxattrs, void *fs_data); initxattrs initxattrs, void *fs_data);
int security_old_inode_init_security(struct inode *inode, struct inode *dir, int security_old_inode_init_security(struct inode *inode, struct inode *dir,
const struct qstr *qstr, char **name, const struct qstr *qstr, const char **name,
void **value, size_t *len); void **value, size_t *len);
int security_inode_create(struct inode *dir, struct dentry *dentry, umode_t mode); int security_inode_create(struct inode *dir, struct dentry *dentry, umode_t mode);
int security_inode_link(struct dentry *old_dentry, struct inode *dir, int security_inode_link(struct dentry *old_dentry, struct inode *dir,
...@@ -2094,8 +2094,8 @@ static inline int security_inode_init_security(struct inode *inode, ...@@ -2094,8 +2094,8 @@ static inline int security_inode_init_security(struct inode *inode,
static inline int security_old_inode_init_security(struct inode *inode, static inline int security_old_inode_init_security(struct inode *inode,
struct inode *dir, struct inode *dir,
const struct qstr *qstr, const struct qstr *qstr,
char **name, void **value, const char **name,
size_t *len) void **value, size_t *len)
{ {
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
......
...@@ -31,7 +31,7 @@ struct xattr_handler { ...@@ -31,7 +31,7 @@ struct xattr_handler {
}; };
struct xattr { struct xattr {
char *name; const char *name;
void *value; void *value;
size_t value_len; size_t value_len;
}; };
......
...@@ -16,7 +16,7 @@ struct reiserfs_xattr_header { ...@@ -16,7 +16,7 @@ struct reiserfs_xattr_header {
}; };
struct reiserfs_security_handle { struct reiserfs_security_handle {
char *name; const char *name;
void *value; void *value;
size_t length; size_t length;
}; };
......
...@@ -29,3 +29,15 @@ config SECURITY_APPARMOR_BOOTPARAM_VALUE ...@@ -29,3 +29,15 @@ config SECURITY_APPARMOR_BOOTPARAM_VALUE
boot. boot.
If you are unsure how to answer this question, answer 1. If you are unsure how to answer this question, answer 1.
config SECURITY_APPARMOR_HASH
bool "SHA1 hash of loaded profiles"
depends on SECURITY_APPARMOR
depends on CRYPTO
select CRYPTO_SHA1
default y
help
This option selects whether sha1 hashing is done against loaded
profiles and exported for inspection to user space via the apparmor
filesystem.
...@@ -5,6 +5,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o ...@@ -5,6 +5,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o
apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \
path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \
resource.o sid.o file.o resource.o sid.o file.o
apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o
clean-files := capability_names.h rlim_names.h clean-files := capability_names.h rlim_names.h
...@@ -18,7 +19,11 @@ quiet_cmd_make-caps = GEN $@ ...@@ -18,7 +19,11 @@ quiet_cmd_make-caps = GEN $@
cmd_make-caps = echo "static const char *const capability_names[] = {" > $@ ;\ cmd_make-caps = echo "static const char *const capability_names[] = {" > $@ ;\
sed $< >>$@ -r -n -e '/CAP_FS_MASK/d' \ sed $< >>$@ -r -n -e '/CAP_FS_MASK/d' \
-e 's/^\#define[ \t]+CAP_([A-Z0-9_]+)[ \t]+([0-9]+)/[\2] = "\L\1",/p';\ -e 's/^\#define[ \t]+CAP_([A-Z0-9_]+)[ \t]+([0-9]+)/[\2] = "\L\1",/p';\
echo "};" >> $@ echo "};" >> $@ ;\
echo -n '\#define AA_FS_CAPS_MASK "' >> $@ ;\
sed $< -r -n -e '/CAP_FS_MASK/d' \
-e 's/^\#define[ \t]+CAP_([A-Z0-9_]+)[ \t]+([0-9]+)/\L\1/p' | \
tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@
# Build a lower case string table of rlimit names. # Build a lower case string table of rlimit names.
......
This diff is collapsed.
...@@ -27,6 +27,11 @@ ...@@ -27,6 +27,11 @@
*/ */
#include "capability_names.h" #include "capability_names.h"
struct aa_fs_entry aa_fs_entry_caps[] = {
AA_FS_FILE_STRING("mask", AA_FS_CAPS_MASK),
{ }
};
struct audit_cache { struct audit_cache {
struct aa_profile *profile; struct aa_profile *profile;
kernel_cap_t caps; kernel_cap_t caps;
......
...@@ -112,9 +112,9 @@ int aa_replace_current_profile(struct aa_profile *profile) ...@@ -112,9 +112,9 @@ int aa_replace_current_profile(struct aa_profile *profile)
aa_clear_task_cxt_trans(cxt); aa_clear_task_cxt_trans(cxt);
/* be careful switching cxt->profile, when racing replacement it /* be careful switching cxt->profile, when racing replacement it
* is possible that cxt->profile->replacedby is the reference keeping * is possible that cxt->profile->replacedby->profile is the reference
* @profile valid, so make sure to get its reference before dropping * keeping @profile valid, so make sure to get its reference before
* the reference on cxt->profile */ * dropping the reference on cxt->profile */
aa_get_profile(profile); aa_get_profile(profile);
aa_put_profile(cxt->profile); aa_put_profile(cxt->profile);
cxt->profile = profile; cxt->profile = profile;
...@@ -175,7 +175,7 @@ int aa_set_current_hat(struct aa_profile *profile, u64 token) ...@@ -175,7 +175,7 @@ int aa_set_current_hat(struct aa_profile *profile, u64 token)
abort_creds(new); abort_creds(new);
return -EACCES; return -EACCES;
} }
cxt->profile = aa_get_profile(aa_newest_version(profile)); cxt->profile = aa_get_newest_profile(profile);
/* clear exec on switching context */ /* clear exec on switching context */
aa_put_profile(cxt->onexec); aa_put_profile(cxt->onexec);
cxt->onexec = NULL; cxt->onexec = NULL;
...@@ -212,14 +212,8 @@ int aa_restore_previous_profile(u64 token) ...@@ -212,14 +212,8 @@ int aa_restore_previous_profile(u64 token)
} }
aa_put_profile(cxt->profile); aa_put_profile(cxt->profile);
cxt->profile = aa_newest_version(cxt->previous); cxt->profile = aa_get_newest_profile(cxt->previous);
BUG_ON(!cxt->profile); BUG_ON(!cxt->profile);
if (unlikely(cxt->profile != cxt->previous)) {
aa_get_profile(cxt->profile);
aa_put_profile(cxt->previous);
}
/* ref has been transfered so avoid putting ref in clear_task_cxt */
cxt->previous = NULL;
/* clear exec && prev information when restoring to previous context */ /* clear exec && prev information when restoring to previous context */
aa_clear_task_cxt_trans(cxt); aa_clear_task_cxt_trans(cxt);
......
/*
* AppArmor security module
*
* This file contains AppArmor policy loading interface function definitions.
*
* Copyright 2013 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.
*
* Fns to provide a checksum of policy that has been loaded this can be
* compared to userspace policy compiles to check loaded policy is what
* it should be.
*/
#include <linux/crypto.h>
#include "include/apparmor.h"
#include "include/crypto.h"
static unsigned int apparmor_hash_size;
static struct crypto_hash *apparmor_tfm;
unsigned int aa_hash_size(void)
{
return apparmor_hash_size;
}
int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start,
size_t len)
{
struct scatterlist sg[2];
struct hash_desc desc = {
.tfm = apparmor_tfm,
.flags = 0
};
int error = -ENOMEM;
u32 le32_version = cpu_to_le32(version);
if (!apparmor_tfm)
return 0;
sg_init_table(sg, 2);
sg_set_buf(&sg[0], &le32_version, 4);
sg_set_buf(&sg[1], (u8 *) start, len);
profile->hash = kzalloc(apparmor_hash_size, GFP_KERNEL);
if (!profile->hash)
goto fail;
error = crypto_hash_init(&desc);
if (error)
goto fail;
error = crypto_hash_update(&desc, &sg[0], 4);
if (error)
goto fail;
error = crypto_hash_update(&desc, &sg[1], len);
if (error)
goto fail;
error = crypto_hash_final(&desc, profile->hash);
if (error)
goto fail;
return 0;
fail:
kfree(profile->hash);
profile->hash = NULL;
return error;
}
static int __init init_profile_hash(void)
{
struct crypto_hash *tfm;
if (!apparmor_initialized)
return 0;
tfm = crypto_alloc_hash("sha1", 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(tfm)) {
int error = PTR_ERR(tfm);
AA_ERROR("failed to setup profile sha1 hashing: %d\n", error);
return error;
}
apparmor_tfm = tfm;
apparmor_hash_size = crypto_hash_digestsize(apparmor_tfm);
aa_info_message("AppArmor sha1 policy hashing enabled");
return 0;
}
late_initcall(init_profile_hash);
...@@ -144,7 +144,7 @@ static struct aa_profile *__attach_match(const char *name, ...@@ -144,7 +144,7 @@ static struct aa_profile *__attach_match(const char *name,
int len = 0; int len = 0;
struct aa_profile *profile, *candidate = NULL; struct aa_profile *profile, *candidate = NULL;
list_for_each_entry(profile, head, base.list) { list_for_each_entry_rcu(profile, head, base.list) {
if (profile->flags & PFLAG_NULL) if (profile->flags & PFLAG_NULL)
continue; continue;
if (profile->xmatch && profile->xmatch_len > len) { if (profile->xmatch && profile->xmatch_len > len) {
...@@ -177,9 +177,9 @@ static struct aa_profile *find_attach(struct aa_namespace *ns, ...@@ -177,9 +177,9 @@ static struct aa_profile *find_attach(struct aa_namespace *ns,
{ {
struct aa_profile *profile; struct aa_profile *profile;
read_lock(&ns->lock); rcu_read_lock();
profile = aa_get_profile(__attach_match(name, list)); profile = aa_get_profile(__attach_match(name, list));
read_unlock(&ns->lock); rcu_read_unlock();
return profile; return profile;
} }
...@@ -359,7 +359,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) ...@@ -359,7 +359,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
cxt = cred_cxt(bprm->cred); cxt = cred_cxt(bprm->cred);
BUG_ON(!cxt); BUG_ON(!cxt);
profile = aa_get_profile(aa_newest_version(cxt->profile)); profile = aa_get_newest_profile(cxt->profile);
/* /*
* get the namespace from the replacement profile as replacement * get the namespace from the replacement profile as replacement
* can change the namespace * can change the namespace
...@@ -371,8 +371,8 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) ...@@ -371,8 +371,8 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
error = aa_path_name(&bprm->file->f_path, profile->path_flags, &buffer, error = aa_path_name(&bprm->file->f_path, profile->path_flags, &buffer,
&name, &info); &name, &info);
if (error) { if (error) {
if (profile->flags & if (unconfined(profile) ||
(PFLAG_IX_ON_NAME_ERROR | PFLAG_UNCONFINED)) (profile->flags & PFLAG_IX_ON_NAME_ERROR))
error = 0; error = 0;
name = bprm->filename; name = bprm->filename;
goto audit; goto audit;
...@@ -417,7 +417,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) ...@@ -417,7 +417,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
if (!(cp.allow & AA_MAY_ONEXEC)) if (!(cp.allow & AA_MAY_ONEXEC))
goto audit; goto audit;
new_profile = aa_get_profile(aa_newest_version(cxt->onexec)); new_profile = aa_get_newest_profile(cxt->onexec);
goto apply; goto apply;
} }
...@@ -434,7 +434,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) ...@@ -434,7 +434,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
new_profile = aa_get_profile(profile); new_profile = aa_get_profile(profile);
goto x_clear; goto x_clear;
} else if (perms.xindex & AA_X_UNCONFINED) { } else if (perms.xindex & AA_X_UNCONFINED) {
new_profile = aa_get_profile(ns->unconfined); new_profile = aa_get_newest_profile(ns->unconfined);
info = "ux fallback"; info = "ux fallback";
} else { } else {
error = -ENOENT; error = -ENOENT;
...@@ -641,7 +641,10 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest) ...@@ -641,7 +641,10 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest)
if (count) { if (count) {
/* attempting to change into a new hat or switch to a sibling */ /* attempting to change into a new hat or switch to a sibling */
struct aa_profile *root; struct aa_profile *root;
root = PROFILE_IS_HAT(profile) ? profile->parent : profile; if (PROFILE_IS_HAT(profile))
root = aa_get_profile_rcu(&profile->parent);
else
root = aa_get_profile(profile);
/* find first matching hat */ /* find first matching hat */
for (i = 0; i < count && !hat; i++) for (i = 0; i < count && !hat; i++)
...@@ -653,6 +656,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest) ...@@ -653,6 +656,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest)
error = -ECHILD; error = -ECHILD;
else else
error = -ENOENT; error = -ENOENT;
aa_put_profile(root);
goto out; goto out;
} }
...@@ -667,6 +671,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest) ...@@ -667,6 +671,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest)
/* freed below */ /* freed below */
name = new_compound_name(root->base.hname, hats[0]); name = new_compound_name(root->base.hname, hats[0]);
aa_put_profile(root);
target = name; target = name;
/* released below */ /* released below */
hat = aa_new_null_profile(profile, 1); hat = aa_new_null_profile(profile, 1);
...@@ -676,6 +681,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest) ...@@ -676,6 +681,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest)
goto audit; goto audit;
} }
} else { } else {
aa_put_profile(root);
target = hat->base.hname; target = hat->base.hname;
if (!PROFILE_IS_HAT(hat)) { if (!PROFILE_IS_HAT(hat)) {
info = "target not hat"; info = "target not hat";
......
...@@ -78,6 +78,12 @@ static inline void *kvzalloc(size_t size) ...@@ -78,6 +78,12 @@ static inline void *kvzalloc(size_t size)
return __aa_kvmalloc(size, __GFP_ZERO); return __aa_kvmalloc(size, __GFP_ZERO);
} }
/* returns 0 if kref not incremented */
static inline int kref_get_not0(struct kref *kref)
{
return atomic_inc_not_zero(&kref->refcount);
}
/** /**
* aa_strneq - compare null terminated @str to a non null terminated substring * aa_strneq - compare null terminated @str to a non null terminated substring
* @str: a null terminated string * @str: a null terminated string
......
...@@ -61,4 +61,44 @@ extern const struct file_operations aa_fs_seq_file_ops; ...@@ -61,4 +61,44 @@ extern const struct file_operations aa_fs_seq_file_ops;
extern void __init aa_destroy_aafs(void); extern void __init aa_destroy_aafs(void);
struct aa_profile;
struct aa_namespace;
enum aafs_ns_type {
AAFS_NS_DIR,
AAFS_NS_PROFS,
AAFS_NS_NS,
AAFS_NS_COUNT,
AAFS_NS_MAX_COUNT,
AAFS_NS_SIZE,
AAFS_NS_MAX_SIZE,
AAFS_NS_OWNER,
AAFS_NS_SIZEOF,
};
enum aafs_prof_type {
AAFS_PROF_DIR,
AAFS_PROF_PROFS,
AAFS_PROF_NAME,
AAFS_PROF_MODE,
AAFS_PROF_ATTACH,
AAFS_PROF_HASH,
AAFS_PROF_SIZEOF,
};
#define ns_dir(X) ((X)->dents[AAFS_NS_DIR])
#define ns_subns_dir(X) ((X)->dents[AAFS_NS_NS])
#define ns_subprofs_dir(X) ((X)->dents[AAFS_NS_PROFS])
#define prof_dir(X) ((X)->dents[AAFS_PROF_DIR])
#define prof_child_dir(X) ((X)->dents[AAFS_PROF_PROFS])
void __aa_fs_profile_rmdir(struct aa_profile *profile);
void __aa_fs_profile_migrate_dents(struct aa_profile *old,
struct aa_profile *new);
int __aa_fs_profile_mkdir(struct aa_profile *profile, struct dentry *parent);
void __aa_fs_namespace_rmdir(struct aa_namespace *ns);
int __aa_fs_namespace_mkdir(struct aa_namespace *ns, struct dentry *parent,
const char *name);
#endif /* __AA_APPARMORFS_H */ #endif /* __AA_APPARMORFS_H */
...@@ -27,7 +27,6 @@ struct aa_profile; ...@@ -27,7 +27,6 @@ struct aa_profile;
extern const char *const audit_mode_names[]; extern const char *const audit_mode_names[];
#define AUDIT_MAX_INDEX 5 #define AUDIT_MAX_INDEX 5
enum audit_mode { enum audit_mode {
AUDIT_NORMAL, /* follow normal auditing of accesses */ AUDIT_NORMAL, /* follow normal auditing of accesses */
AUDIT_QUIET_DENIED, /* quiet all denied access messages */ AUDIT_QUIET_DENIED, /* quiet all denied access messages */
......
...@@ -17,6 +17,8 @@ ...@@ -17,6 +17,8 @@
#include <linux/sched.h> #include <linux/sched.h>
#include "apparmorfs.h"
struct aa_profile; struct aa_profile;
/* aa_caps - confinement data for capabilities /* aa_caps - confinement data for capabilities
...@@ -34,6 +36,8 @@ struct aa_caps { ...@@ -34,6 +36,8 @@ struct aa_caps {
kernel_cap_t extended; kernel_cap_t extended;
}; };
extern struct aa_fs_entry aa_fs_entry_caps[];
int aa_capable(struct task_struct *task, struct aa_profile *profile, int cap, int aa_capable(struct task_struct *task, struct aa_profile *profile, int cap,
int audit); int audit);
......
...@@ -98,7 +98,7 @@ static inline struct aa_profile *aa_cred_profile(const struct cred *cred) ...@@ -98,7 +98,7 @@ static inline struct aa_profile *aa_cred_profile(const struct cred *cred)
{ {
struct aa_task_cxt *cxt = cred_cxt(cred); struct aa_task_cxt *cxt = cred_cxt(cred);
BUG_ON(!cxt || !cxt->profile); BUG_ON(!cxt || !cxt->profile);
return aa_newest_version(cxt->profile); return cxt->profile;
} }
/** /**
...@@ -152,15 +152,14 @@ static inline struct aa_profile *aa_current_profile(void) ...@@ -152,15 +152,14 @@ static inline struct aa_profile *aa_current_profile(void)
struct aa_profile *profile; struct aa_profile *profile;
BUG_ON(!cxt || !cxt->profile); BUG_ON(!cxt || !cxt->profile);
profile = aa_newest_version(cxt->profile); if (PROFILE_INVALID(cxt->profile)) {
/* profile = aa_get_newest_profile(cxt->profile);
* Whether or not replacement succeeds, use newest profile so
* there is no need to update it after replacement.
*/
if (unlikely((cxt->profile != profile)))
aa_replace_current_profile(profile); aa_replace_current_profile(profile);
aa_put_profile(profile);
cxt = current_cxt();
}
return profile; return cxt->profile;
} }
/** /**
......
/*
* AppArmor security module
*
* This file contains AppArmor policy loading interface function definitions.
*
* Copyright 2013 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.
*/
#ifndef __APPARMOR_CRYPTO_H
#define __APPARMOR_CRYPTO_H
#include "policy.h"
#ifdef CONFIG_SECURITY_APPARMOR_HASH
unsigned int aa_hash_size(void);
int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start,
size_t len);
#else
static inline int aa_calc_profile_hash(struct aa_profile *profile, u32 version,
void *start, size_t len)
{
return 0;
}
static inline unsigned int aa_hash_size(void)
{
return 0;
}
#endif
#endif /* __APPARMOR_CRYPTO_H */
This diff is collapsed.
...@@ -15,6 +15,25 @@ ...@@ -15,6 +15,25 @@
#ifndef __POLICY_INTERFACE_H #ifndef __POLICY_INTERFACE_H
#define __POLICY_INTERFACE_H #define __POLICY_INTERFACE_H
struct aa_profile *aa_unpack(void *udata, size_t size, const char **ns); #include <linux/list.h>
struct aa_load_ent {
struct list_head list;
struct aa_profile *new;
struct aa_profile *old;
struct aa_profile *rename;
};
void aa_load_ent_free(struct aa_load_ent *ent);
struct aa_load_ent *aa_load_ent_alloc(void);
#define PACKED_FLAG_HAT 1
#define PACKED_MODE_ENFORCE 0
#define PACKED_MODE_COMPLAIN 1
#define PACKED_MODE_KILL 2
#define PACKED_MODE_UNCONFINED 3
int aa_unpack(void *udata, size_t size, struct list_head *lh, const char **ns);
#endif /* __POLICY_INTERFACE_H */ #endif /* __POLICY_INTERFACE_H */
...@@ -97,11 +97,6 @@ void *__aa_kvmalloc(size_t size, gfp_t flags) ...@@ -97,11 +97,6 @@ void *__aa_kvmalloc(size_t size, gfp_t flags)
if (size <= (16*PAGE_SIZE)) if (size <= (16*PAGE_SIZE))
buffer = kmalloc(size, flags | GFP_NOIO | __GFP_NOWARN); buffer = kmalloc(size, flags | GFP_NOIO | __GFP_NOWARN);
if (!buffer) { if (!buffer) {
/* see kvfree for why size must be at least work_struct size
* when allocated via vmalloc
*/
if (size < sizeof(struct work_struct))
size = sizeof(struct work_struct);
if (flags & __GFP_ZERO) if (flags & __GFP_ZERO)
buffer = vzalloc(size); buffer = vzalloc(size);
else else
......
...@@ -508,19 +508,21 @@ static int apparmor_getprocattr(struct task_struct *task, char *name, ...@@ -508,19 +508,21 @@ static int apparmor_getprocattr(struct task_struct *task, char *name,
/* released below */ /* released below */
const struct cred *cred = get_task_cred(task); const struct cred *cred = get_task_cred(task);
struct aa_task_cxt *cxt = cred_cxt(cred); struct aa_task_cxt *cxt = cred_cxt(cred);
struct aa_profile *profile = NULL;
if (strcmp(name, "current") == 0) if (strcmp(name, "current") == 0)
error = aa_getprocattr(aa_newest_version(cxt->profile), profile = aa_get_newest_profile(cxt->profile);
value);
else if (strcmp(name, "prev") == 0 && cxt->previous) else if (strcmp(name, "prev") == 0 && cxt->previous)
error = aa_getprocattr(aa_newest_version(cxt->previous), profile = aa_get_newest_profile(cxt->previous);
value);
else if (strcmp(name, "exec") == 0 && cxt->onexec) else if (strcmp(name, "exec") == 0 && cxt->onexec)
error = aa_getprocattr(aa_newest_version(cxt->onexec), profile = aa_get_newest_profile(cxt->onexec);
value);
else else
error = -EINVAL; error = -EINVAL;
if (profile)
error = aa_getprocattr(profile, value);
aa_put_profile(profile);
put_cred(cred); put_cred(cred);
return error; return error;
...@@ -744,7 +746,7 @@ module_param_named(paranoid_load, aa_g_paranoid_load, aabool, ...@@ -744,7 +746,7 @@ module_param_named(paranoid_load, aa_g_paranoid_load, aabool,
/* Boot time disable flag */ /* Boot time disable flag */
static bool apparmor_enabled = CONFIG_SECURITY_APPARMOR_BOOTPARAM_VALUE; static bool apparmor_enabled = CONFIG_SECURITY_APPARMOR_BOOTPARAM_VALUE;
module_param_named(enabled, apparmor_enabled, aabool, S_IRUSR); module_param_named(enabled, apparmor_enabled, bool, S_IRUGO);
static int __init apparmor_enabled_setup(char *str) static int __init apparmor_enabled_setup(char *str)
{ {
...@@ -843,7 +845,7 @@ static int param_get_mode(char *buffer, struct kernel_param *kp) ...@@ -843,7 +845,7 @@ static int param_get_mode(char *buffer, struct kernel_param *kp)
if (!apparmor_enabled) if (!apparmor_enabled)
return -EINVAL; return -EINVAL;
return sprintf(buffer, "%s", profile_mode_names[aa_g_profile_mode]); return sprintf(buffer, "%s", aa_profile_mode_names[aa_g_profile_mode]);
} }
static int param_set_mode(const char *val, struct kernel_param *kp) static int param_set_mode(const char *val, struct kernel_param *kp)
...@@ -858,8 +860,8 @@ static int param_set_mode(const char *val, struct kernel_param *kp) ...@@ -858,8 +860,8 @@ static int param_set_mode(const char *val, struct kernel_param *kp)
if (!val) if (!val)
return -EINVAL; return -EINVAL;
for (i = 0; i < APPARMOR_NAMES_MAX_INDEX; i++) { for (i = 0; i < APPARMOR_MODE_NAMES_MAX_INDEX; i++) {
if (strcmp(val, profile_mode_names[i]) == 0) { if (strcmp(val, aa_profile_mode_names[i]) == 0) {
aa_g_profile_mode = i; aa_g_profile_mode = i;
return 0; return 0;
} }
......
This diff is collapsed.
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include "include/apparmor.h" #include "include/apparmor.h"
#include "include/audit.h" #include "include/audit.h"
#include "include/context.h" #include "include/context.h"
#include "include/crypto.h"
#include "include/match.h" #include "include/match.h"
#include "include/policy.h" #include "include/policy.h"
#include "include/policy_unpack.h" #include "include/policy_unpack.h"
...@@ -333,8 +334,10 @@ static struct aa_dfa *unpack_dfa(struct aa_ext *e) ...@@ -333,8 +334,10 @@ static struct aa_dfa *unpack_dfa(struct aa_ext *e)
/* /*
* The dfa is aligned with in the blob to 8 bytes * The dfa is aligned with in the blob to 8 bytes
* from the beginning of the stream. * from the beginning of the stream.
* alignment adjust needed by dfa unpack
*/ */
size_t sz = blob - (char *)e->start; size_t sz = blob - (char *) e->start -
((e->pos - e->start) & 7);
size_t pad = ALIGN(sz, 8) - sz; size_t pad = ALIGN(sz, 8) - sz;
int flags = TO_ACCEPT1_FLAG(YYTD_DATA32) | int flags = TO_ACCEPT1_FLAG(YYTD_DATA32) |
TO_ACCEPT2_FLAG(YYTD_DATA32); TO_ACCEPT2_FLAG(YYTD_DATA32);
...@@ -490,6 +493,9 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) ...@@ -490,6 +493,9 @@ static struct aa_profile *unpack_profile(struct aa_ext *e)
/* profile renaming is optional */ /* profile renaming is optional */
(void) unpack_str(e, &profile->rename, "rename"); (void) unpack_str(e, &profile->rename, "rename");
/* attachment string is optional */
(void) unpack_str(e, &profile->attach, "attach");
/* xmatch is optional and may be NULL */ /* xmatch is optional and may be NULL */
profile->xmatch = unpack_dfa(e); profile->xmatch = unpack_dfa(e);
if (IS_ERR(profile->xmatch)) { if (IS_ERR(profile->xmatch)) {
...@@ -509,12 +515,16 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) ...@@ -509,12 +515,16 @@ static struct aa_profile *unpack_profile(struct aa_ext *e)
goto fail; goto fail;
if (!unpack_u32(e, &tmp, NULL)) if (!unpack_u32(e, &tmp, NULL))
goto fail; goto fail;
if (tmp) if (tmp & PACKED_FLAG_HAT)
profile->flags |= PFLAG_HAT; profile->flags |= PFLAG_HAT;
if (!unpack_u32(e, &tmp, NULL)) if (!unpack_u32(e, &tmp, NULL))
goto fail; goto fail;
if (tmp) if (tmp == PACKED_MODE_COMPLAIN)
profile->mode = APPARMOR_COMPLAIN; profile->mode = APPARMOR_COMPLAIN;
else if (tmp == PACKED_MODE_KILL)
profile->mode = APPARMOR_KILL;
else if (tmp == PACKED_MODE_UNCONFINED)
profile->mode = APPARMOR_UNCONFINED;
if (!unpack_u32(e, &tmp, NULL)) if (!unpack_u32(e, &tmp, NULL))
goto fail; goto fail;
if (tmp) if (tmp)
...@@ -614,7 +624,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) ...@@ -614,7 +624,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e)
else if (!name) else if (!name)
name = "unknown"; name = "unknown";
audit_iface(profile, name, "failed to unpack profile", e, error); audit_iface(profile, name, "failed to unpack profile", e, error);
aa_put_profile(profile); aa_free_profile(profile);
return ERR_PTR(error); return ERR_PTR(error);
} }
...@@ -622,29 +632,41 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) ...@@ -622,29 +632,41 @@ static struct aa_profile *unpack_profile(struct aa_ext *e)
/** /**
* verify_head - unpack serialized stream header * verify_head - unpack serialized stream header
* @e: serialized data read head (NOT NULL) * @e: serialized data read head (NOT NULL)
* @required: whether the header is required or optional
* @ns: Returns - namespace if one is specified else NULL (NOT NULL) * @ns: Returns - namespace if one is specified else NULL (NOT NULL)
* *
* Returns: error or 0 if header is good * Returns: error or 0 if header is good
*/ */
static int verify_header(struct aa_ext *e, const char **ns) static int verify_header(struct aa_ext *e, int required, const char **ns)
{ {
int error = -EPROTONOSUPPORT; int error = -EPROTONOSUPPORT;
const char *name = NULL;
*ns = NULL;
/* get the interface version */ /* get the interface version */
if (!unpack_u32(e, &e->version, "version")) { if (!unpack_u32(e, &e->version, "version")) {
audit_iface(NULL, NULL, "invalid profile format", e, error); if (required) {
audit_iface(NULL, NULL, "invalid profile format", e,
error);
return error; return error;
} }
/* check that the interface version is currently supported */ /* check that the interface version is currently supported */
if (e->version != 5) { if (e->version != 5) {
audit_iface(NULL, NULL, "unsupported interface version", e, audit_iface(NULL, NULL, "unsupported interface version",
error); e, error);
return error; return error;
} }
}
/* read the namespace if present */ /* read the namespace if present */
if (!unpack_str(e, ns, "namespace")) if (unpack_str(e, &name, "namespace")) {
*ns = NULL; if (*ns && strcmp(*ns, name))
audit_iface(NULL, NULL, "invalid ns change", e, error);
else if (!*ns)
*ns = name;
}
return 0; return 0;
} }
...@@ -693,18 +715,40 @@ static int verify_profile(struct aa_profile *profile) ...@@ -693,18 +715,40 @@ static int verify_profile(struct aa_profile *profile)
return 0; return 0;
} }
void aa_load_ent_free(struct aa_load_ent *ent)
{
if (ent) {
aa_put_profile(ent->rename);
aa_put_profile(ent->old);
aa_put_profile(ent->new);
kzfree(ent);
}
}
struct aa_load_ent *aa_load_ent_alloc(void)
{
struct aa_load_ent *ent = kzalloc(sizeof(*ent), GFP_KERNEL);
if (ent)
INIT_LIST_HEAD(&ent->list);
return ent;
}
/** /**
* aa_unpack - unpack packed binary profile data loaded from user space * aa_unpack - unpack packed binary profile(s) data loaded from user space
* @udata: user data copied to kmem (NOT NULL) * @udata: user data copied to kmem (NOT NULL)
* @size: the size of the user data * @size: the size of the user data
* @lh: list to place unpacked profiles in a aa_repl_ws
* @ns: Returns namespace profile is in if specified else NULL (NOT NULL) * @ns: Returns namespace profile is in if specified else NULL (NOT NULL)
* *
* Unpack user data and return refcounted allocated profile or ERR_PTR * Unpack user data and return refcounted allocated profile(s) stored in
* @lh in order of discovery, with the list chain stored in base.list
* or error
* *
* Returns: profile else error pointer if fails to unpack * Returns: profile(s) on @lh else error pointer if fails to unpack
*/ */
struct aa_profile *aa_unpack(void *udata, size_t size, const char **ns) int aa_unpack(void *udata, size_t size, struct list_head *lh, const char **ns)
{ {
struct aa_load_ent *tmp, *ent;
struct aa_profile *profile = NULL; struct aa_profile *profile = NULL;
int error; int error;
struct aa_ext e = { struct aa_ext e = {
...@@ -713,20 +757,49 @@ struct aa_profile *aa_unpack(void *udata, size_t size, const char **ns) ...@@ -713,20 +757,49 @@ struct aa_profile *aa_unpack(void *udata, size_t size, const char **ns)
.pos = udata, .pos = udata,
}; };
error = verify_header(&e, ns); *ns = NULL;
while (e.pos < e.end) {
void *start;
error = verify_header(&e, e.pos == e.start, ns);
if (error) if (error)
return ERR_PTR(error); goto fail;
start = e.pos;
profile = unpack_profile(&e); profile = unpack_profile(&e);
if (IS_ERR(profile)) if (IS_ERR(profile)) {
return profile; error = PTR_ERR(profile);
goto fail;
}
error = verify_profile(profile); error = verify_profile(profile);
if (error) { if (error)
goto fail_profile;
error = aa_calc_profile_hash(profile, e.version, start,
e.pos - start);
if (error)
goto fail_profile;
ent = aa_load_ent_alloc();
if (!ent) {
error = -ENOMEM;
goto fail_profile;
}
ent->new = profile;
list_add_tail(&ent->list, lh);
}
return 0;
fail_profile:
aa_put_profile(profile); aa_put_profile(profile);
profile = ERR_PTR(error);
fail:
list_for_each_entry_safe(ent, tmp, lh, list) {
list_del_init(&ent->list);
aa_load_ent_free(ent);
} }
/* return refcount */ return error;
return profile;
} }
...@@ -37,7 +37,7 @@ int aa_getprocattr(struct aa_profile *profile, char **string) ...@@ -37,7 +37,7 @@ int aa_getprocattr(struct aa_profile *profile, char **string)
{ {
char *str; char *str;
int len = 0, mode_len = 0, ns_len = 0, name_len; int len = 0, mode_len = 0, ns_len = 0, name_len;
const char *mode_str = profile_mode_names[profile->mode]; const char *mode_str = aa_profile_mode_names[profile->mode];
const char *ns_name = NULL; const char *ns_name = NULL;
struct aa_namespace *ns = profile->ns; struct aa_namespace *ns = profile->ns;
struct aa_namespace *current_ns = __aa_current_profile()->ns; struct aa_namespace *current_ns = __aa_current_profile()->ns;
......
...@@ -129,7 +129,7 @@ static void cap_inode_free_security(struct inode *inode) ...@@ -129,7 +129,7 @@ static void cap_inode_free_security(struct inode *inode)
} }
static int cap_inode_init_security(struct inode *inode, struct inode *dir, static int cap_inode_init_security(struct inode *inode, struct inode *dir,
const struct qstr *qstr, char **name, const struct qstr *qstr, const char **name,
void **value, size_t *len) void **value, size_t *len)
{ {
return -EOPNOTSUPP; return -EOPNOTSUPP;
......
...@@ -418,7 +418,7 @@ int evm_inode_init_security(struct inode *inode, ...@@ -418,7 +418,7 @@ int evm_inode_init_security(struct inode *inode,
evm_xattr->value = xattr_data; evm_xattr->value = xattr_data;
evm_xattr->value_len = sizeof(*xattr_data); evm_xattr->value_len = sizeof(*xattr_data);
evm_xattr->name = kstrdup(XATTR_EVM_SUFFIX, GFP_NOFS); evm_xattr->name = XATTR_EVM_SUFFIX;
return 0; return 0;
out: out:
kfree(xattr_data); kfree(xattr_data);
......
...@@ -348,10 +348,10 @@ int security_inode_init_security(struct inode *inode, struct inode *dir, ...@@ -348,10 +348,10 @@ int security_inode_init_security(struct inode *inode, struct inode *dir,
if (unlikely(IS_PRIVATE(inode))) if (unlikely(IS_PRIVATE(inode)))
return 0; return 0;
memset(new_xattrs, 0, sizeof new_xattrs);
if (!initxattrs) if (!initxattrs)
return security_ops->inode_init_security(inode, dir, qstr, return security_ops->inode_init_security(inode, dir, qstr,
NULL, NULL, NULL); NULL, NULL, NULL);
memset(new_xattrs, 0, sizeof(new_xattrs));
lsm_xattr = new_xattrs; lsm_xattr = new_xattrs;
ret = security_ops->inode_init_security(inode, dir, qstr, ret = security_ops->inode_init_security(inode, dir, qstr,
&lsm_xattr->name, &lsm_xattr->name,
...@@ -366,16 +366,14 @@ int security_inode_init_security(struct inode *inode, struct inode *dir, ...@@ -366,16 +366,14 @@ int security_inode_init_security(struct inode *inode, struct inode *dir,
goto out; goto out;
ret = initxattrs(inode, new_xattrs, fs_data); ret = initxattrs(inode, new_xattrs, fs_data);
out: out:
for (xattr = new_xattrs; xattr->name != NULL; xattr++) { for (xattr = new_xattrs; xattr->value != NULL; xattr++)
kfree(xattr->name);
kfree(xattr->value); kfree(xattr->value);
}
return (ret == -EOPNOTSUPP) ? 0 : ret; return (ret == -EOPNOTSUPP) ? 0 : ret;
} }
EXPORT_SYMBOL(security_inode_init_security); EXPORT_SYMBOL(security_inode_init_security);
int security_old_inode_init_security(struct inode *inode, struct inode *dir, int security_old_inode_init_security(struct inode *inode, struct inode *dir,
const struct qstr *qstr, char **name, const struct qstr *qstr, const char **name,
void **value, size_t *len) void **value, size_t *len)
{ {
if (unlikely(IS_PRIVATE(inode))) if (unlikely(IS_PRIVATE(inode)))
......
...@@ -2587,7 +2587,8 @@ static int selinux_dentry_init_security(struct dentry *dentry, int mode, ...@@ -2587,7 +2587,8 @@ static int selinux_dentry_init_security(struct dentry *dentry, int mode,
} }
static int selinux_inode_init_security(struct inode *inode, struct inode *dir, static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
const struct qstr *qstr, char **name, const struct qstr *qstr,
const char **name,
void **value, size_t *len) void **value, size_t *len)
{ {
const struct task_security_struct *tsec = current_security(); const struct task_security_struct *tsec = current_security();
...@@ -2595,7 +2596,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir, ...@@ -2595,7 +2596,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
struct superblock_security_struct *sbsec; struct superblock_security_struct *sbsec;
u32 sid, newsid, clen; u32 sid, newsid, clen;
int rc; int rc;
char *namep = NULL, *context; char *context;
dsec = dir->i_security; dsec = dir->i_security;
sbsec = dir->i_sb->s_security; sbsec = dir->i_sb->s_security;
...@@ -2631,19 +2632,13 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir, ...@@ -2631,19 +2632,13 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
if (!ss_initialized || !(sbsec->flags & SE_SBLABELSUPP)) if (!ss_initialized || !(sbsec->flags & SE_SBLABELSUPP))
return -EOPNOTSUPP; return -EOPNOTSUPP;
if (name) { if (name)
namep = kstrdup(XATTR_SELINUX_SUFFIX, GFP_NOFS); *name = XATTR_SELINUX_SUFFIX;
if (!namep)
return -ENOMEM;
*name = namep;
}
if (value && len) { if (value && len) {
rc = security_sid_to_context_force(newsid, &context, &clen); rc = security_sid_to_context_force(newsid, &context, &clen);
if (rc) { if (rc)
kfree(namep);
return rc; return rc;
}
*value = context; *value = context;
*len = clen; *len = clen;
} }
......
...@@ -53,6 +53,7 @@ ...@@ -53,6 +53,7 @@
*/ */
struct smack_known { struct smack_known {
struct list_head list; struct list_head list;
struct hlist_node smk_hashed;
char *smk_known; char *smk_known;
u32 smk_secid; u32 smk_secid;
struct netlbl_lsm_secattr smk_netlabel; /* on wire labels */ struct netlbl_lsm_secattr smk_netlabel; /* on wire labels */
...@@ -167,9 +168,13 @@ struct smk_port_label { ...@@ -167,9 +168,13 @@ struct smk_port_label {
#define SMACK_CIPSO_DOI_INVALID -1 /* Not a DOI */ #define SMACK_CIPSO_DOI_INVALID -1 /* Not a DOI */
#define SMACK_CIPSO_DIRECT_DEFAULT 250 /* Arbitrary */ #define SMACK_CIPSO_DIRECT_DEFAULT 250 /* Arbitrary */
#define SMACK_CIPSO_MAPPED_DEFAULT 251 /* Also arbitrary */ #define SMACK_CIPSO_MAPPED_DEFAULT 251 /* Also arbitrary */
#define SMACK_CIPSO_MAXCATVAL 63 /* Bigger gets harder */
#define SMACK_CIPSO_MAXLEVEL 255 /* CIPSO 2.2 standard */ #define SMACK_CIPSO_MAXLEVEL 255 /* CIPSO 2.2 standard */
#define SMACK_CIPSO_MAXCATNUM 239 /* CIPSO 2.2 standard */ /*
* CIPSO 2.2 standard is 239, but Smack wants to use the
* categories in a structured way that limits the value to
* the bits in 23 bytes, hence the unusual number.
*/
#define SMACK_CIPSO_MAXCATNUM 184 /* 23 * 8 */
/* /*
* Flag for transmute access * Flag for transmute access
...@@ -222,6 +227,7 @@ char *smk_parse_smack(const char *string, int len); ...@@ -222,6 +227,7 @@ char *smk_parse_smack(const char *string, int len);
int smk_netlbl_mls(int, char *, struct netlbl_lsm_secattr *, int); int smk_netlbl_mls(int, char *, struct netlbl_lsm_secattr *, int);
char *smk_import(const char *, int); char *smk_import(const char *, int);
struct smack_known *smk_import_entry(const char *, int); struct smack_known *smk_import_entry(const char *, int);
void smk_insert_entry(struct smack_known *skp);
struct smack_known *smk_find_entry(const char *); struct smack_known *smk_find_entry(const char *);
u32 smack_to_secid(const char *); u32 smack_to_secid(const char *);
...@@ -247,6 +253,9 @@ extern struct list_head smk_netlbladdr_list; ...@@ -247,6 +253,9 @@ extern struct list_head smk_netlbladdr_list;
extern struct security_operations smack_ops; extern struct security_operations smack_ops;
#define SMACK_HASH_SLOTS 16
extern struct hlist_head smack_known_hash[SMACK_HASH_SLOTS];
/* /*
* Is the directory transmuting? * Is the directory transmuting?
*/ */
......
...@@ -325,6 +325,25 @@ void smack_log(char *subject_label, char *object_label, int request, ...@@ -325,6 +325,25 @@ void smack_log(char *subject_label, char *object_label, int request,
DEFINE_MUTEX(smack_known_lock); DEFINE_MUTEX(smack_known_lock);
struct hlist_head smack_known_hash[SMACK_HASH_SLOTS];
/**
* smk_insert_entry - insert a smack label into a hash map,
*
* this function must be called under smack_known_lock
*/
void smk_insert_entry(struct smack_known *skp)
{
unsigned int hash;
struct hlist_head *head;
hash = full_name_hash(skp->smk_known, strlen(skp->smk_known));
head = &smack_known_hash[hash & (SMACK_HASH_SLOTS - 1)];
hlist_add_head_rcu(&skp->smk_hashed, head);
list_add_rcu(&skp->list, &smack_known_list);
}
/** /**
* smk_find_entry - find a label on the list, return the list entry * smk_find_entry - find a label on the list, return the list entry
* @string: a text string that might be a Smack label * @string: a text string that might be a Smack label
...@@ -334,12 +353,16 @@ DEFINE_MUTEX(smack_known_lock); ...@@ -334,12 +353,16 @@ DEFINE_MUTEX(smack_known_lock);
*/ */
struct smack_known *smk_find_entry(const char *string) struct smack_known *smk_find_entry(const char *string)
{ {
unsigned int hash;
struct hlist_head *head;
struct smack_known *skp; struct smack_known *skp;
list_for_each_entry_rcu(skp, &smack_known_list, list) { hash = full_name_hash(string, strlen(string));
head = &smack_known_hash[hash & (SMACK_HASH_SLOTS - 1)];
hlist_for_each_entry_rcu(skp, head, smk_hashed)
if (strcmp(skp->smk_known, string) == 0) if (strcmp(skp->smk_known, string) == 0)
return skp; return skp;
}
return NULL; return NULL;
} }
...@@ -475,7 +498,7 @@ struct smack_known *smk_import_entry(const char *string, int len) ...@@ -475,7 +498,7 @@ struct smack_known *smk_import_entry(const char *string, int len)
* Make sure that the entry is actually * Make sure that the entry is actually
* filled before putting it on the list. * filled before putting it on the list.
*/ */
list_add_rcu(&skp->list, &smack_known_list); smk_insert_entry(skp);
goto unlockout; goto unlockout;
} }
/* /*
......
...@@ -582,7 +582,7 @@ static void smack_inode_free_security(struct inode *inode) ...@@ -582,7 +582,7 @@ static void smack_inode_free_security(struct inode *inode)
* Returns 0 if it all works out, -ENOMEM if there's no memory * Returns 0 if it all works out, -ENOMEM if there's no memory
*/ */
static int smack_inode_init_security(struct inode *inode, struct inode *dir, static int smack_inode_init_security(struct inode *inode, struct inode *dir,
const struct qstr *qstr, char **name, const struct qstr *qstr, const char **name,
void **value, size_t *len) void **value, size_t *len)
{ {
struct inode_smack *issp = inode->i_security; struct inode_smack *issp = inode->i_security;
...@@ -591,11 +591,8 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir, ...@@ -591,11 +591,8 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir,
char *dsp = smk_of_inode(dir); char *dsp = smk_of_inode(dir);
int may; int may;
if (name) { if (name)
*name = kstrdup(XATTR_SMACK_SUFFIX, GFP_NOFS); *name = XATTR_SMACK_SUFFIX;
if (*name == NULL)
return -ENOMEM;
}
if (value) { if (value) {
rcu_read_lock(); rcu_read_lock();
...@@ -3065,6 +3062,8 @@ static struct smack_known *smack_from_secattr(struct netlbl_lsm_secattr *sap, ...@@ -3065,6 +3062,8 @@ static struct smack_known *smack_from_secattr(struct netlbl_lsm_secattr *sap,
{ {
struct smack_known *skp; struct smack_known *skp;
int found = 0; int found = 0;
int acat;
int kcat;
if ((sap->flags & NETLBL_SECATTR_MLS_LVL) != 0) { if ((sap->flags & NETLBL_SECATTR_MLS_LVL) != 0) {
/* /*
...@@ -3081,13 +3080,29 @@ static struct smack_known *smack_from_secattr(struct netlbl_lsm_secattr *sap, ...@@ -3081,13 +3080,29 @@ static struct smack_known *smack_from_secattr(struct netlbl_lsm_secattr *sap,
list_for_each_entry(skp, &smack_known_list, list) { list_for_each_entry(skp, &smack_known_list, list) {
if (sap->attr.mls.lvl != skp->smk_netlabel.attr.mls.lvl) if (sap->attr.mls.lvl != skp->smk_netlabel.attr.mls.lvl)
continue; continue;
if (memcmp(sap->attr.mls.cat, /*
* Compare the catsets. Use the netlbl APIs.
*/
if ((sap->flags & NETLBL_SECATTR_MLS_CAT) == 0) {
if ((skp->smk_netlabel.flags &
NETLBL_SECATTR_MLS_CAT) == 0)
found = 1;
break;
}
for (acat = -1, kcat = -1; acat == kcat; ) {
acat = netlbl_secattr_catmap_walk(
sap->attr.mls.cat, acat + 1);
kcat = netlbl_secattr_catmap_walk(
skp->smk_netlabel.attr.mls.cat, skp->smk_netlabel.attr.mls.cat,
SMK_CIPSOLEN) != 0) kcat + 1);
continue; if (acat < 0 || kcat < 0)
break;
}
if (acat == kcat) {
found = 1; found = 1;
break; break;
} }
}
rcu_read_unlock(); rcu_read_unlock();
if (found) if (found)
...@@ -3877,12 +3892,12 @@ static __init void init_smack_known_list(void) ...@@ -3877,12 +3892,12 @@ static __init void init_smack_known_list(void)
/* /*
* Create the known labels list * Create the known labels list
*/ */
list_add(&smack_known_huh.list, &smack_known_list); smk_insert_entry(&smack_known_huh);
list_add(&smack_known_hat.list, &smack_known_list); smk_insert_entry(&smack_known_hat);
list_add(&smack_known_star.list, &smack_known_list); smk_insert_entry(&smack_known_star);
list_add(&smack_known_floor.list, &smack_known_list); smk_insert_entry(&smack_known_floor);
list_add(&smack_known_invalid.list, &smack_known_list); smk_insert_entry(&smack_known_invalid);
list_add(&smack_known_web.list, &smack_known_list); smk_insert_entry(&smack_known_web);
} }
/** /**
......
...@@ -368,56 +368,43 @@ static int smk_parse_rule(const char *data, struct smack_parsed_rule *rule, ...@@ -368,56 +368,43 @@ static int smk_parse_rule(const char *data, struct smack_parsed_rule *rule,
* @data: string to be parsed, null terminated * @data: string to be parsed, null terminated
* @rule: Will be filled with Smack parsed rule * @rule: Will be filled with Smack parsed rule
* @import: if non-zero, import labels * @import: if non-zero, import labels
* @change: if non-zero, data is from /smack/change-rule * @tokens: numer of substrings expected in data
* *
* Returns 0 on success, -1 on failure * Returns number of processed bytes on success, -1 on failure.
*/ */
static int smk_parse_long_rule(const char *data, struct smack_parsed_rule *rule, static ssize_t smk_parse_long_rule(char *data, struct smack_parsed_rule *rule,
int import, int change) int import, int tokens)
{ {
char *subject; ssize_t cnt = 0;
char *object; char *tok[4];
char *access1; int i;
char *access2;
int datalen;
int rc = -1;
/* This is inefficient */ /*
datalen = strlen(data); * Parsing the rule in-place, filling all white-spaces with '\0'
*/
for (i = 0; i < tokens; ++i) {
while (isspace(data[cnt]))
data[cnt++] = '\0';
/* Our first element can be 64 + \0 with no spaces */ if (data[cnt] == '\0')
subject = kzalloc(datalen + 1, GFP_KERNEL); /* Unexpected end of data */
if (subject == NULL)
return -1; return -1;
object = kzalloc(datalen, GFP_KERNEL);
if (object == NULL) tok[i] = data + cnt;
goto free_out_s;
access1 = kzalloc(datalen, GFP_KERNEL); while (data[cnt] && !isspace(data[cnt]))
if (access1 == NULL) ++cnt;
goto free_out_o;
access2 = kzalloc(datalen, GFP_KERNEL);
if (access2 == NULL)
goto free_out_a;
if (change) {
if (sscanf(data, "%s %s %s %s",
subject, object, access1, access2) == 4)
rc = smk_fill_rule(subject, object, access1, access2,
rule, import, 0);
} else {
if (sscanf(data, "%s %s %s", subject, object, access1) == 3)
rc = smk_fill_rule(subject, object, access1, NULL,
rule, import, 0);
} }
while (isspace(data[cnt]))
data[cnt++] = '\0';
kfree(access2); while (i < 4)
free_out_a: tok[i++] = NULL;
kfree(access1);
free_out_o: if (smk_fill_rule(tok[0], tok[1], tok[2], tok[3], rule, import, 0))
kfree(object); return -1;
free_out_s:
kfree(subject); return cnt;
return rc;
} }
#define SMK_FIXED24_FMT 0 /* Fixed 24byte label format */ #define SMK_FIXED24_FMT 0 /* Fixed 24byte label format */
...@@ -447,11 +434,12 @@ static ssize_t smk_write_rules_list(struct file *file, const char __user *buf, ...@@ -447,11 +434,12 @@ static ssize_t smk_write_rules_list(struct file *file, const char __user *buf,
struct list_head *rule_list, struct list_head *rule_list,
struct mutex *rule_lock, int format) struct mutex *rule_lock, int format)
{ {
struct smack_parsed_rule *rule; struct smack_parsed_rule rule;
char *data; char *data;
int datalen; int rc;
int rc = -EINVAL; int trunc = 0;
int load = 0; int tokens;
ssize_t cnt = 0;
/* /*
* No partial writes. * No partial writes.
...@@ -466,11 +454,14 @@ static ssize_t smk_write_rules_list(struct file *file, const char __user *buf, ...@@ -466,11 +454,14 @@ static ssize_t smk_write_rules_list(struct file *file, const char __user *buf,
*/ */
if (count != SMK_OLOADLEN && count != SMK_LOADLEN) if (count != SMK_OLOADLEN && count != SMK_LOADLEN)
return -EINVAL; return -EINVAL;
datalen = SMK_LOADLEN; } else {
} else if (count >= PAGE_SIZE) {
datalen = count + 1; count = PAGE_SIZE - 1;
trunc = 1;
}
}
data = kzalloc(datalen, GFP_KERNEL); data = kmalloc(count + 1, GFP_KERNEL);
if (data == NULL) if (data == NULL)
return -ENOMEM; return -ENOMEM;
...@@ -479,47 +470,49 @@ static ssize_t smk_write_rules_list(struct file *file, const char __user *buf, ...@@ -479,47 +470,49 @@ static ssize_t smk_write_rules_list(struct file *file, const char __user *buf,
goto out; goto out;
} }
rule = kzalloc(sizeof(*rule), GFP_KERNEL); /*
if (rule == NULL) { * In case of parsing only part of user buf,
rc = -ENOMEM; * avoid having partial rule at the data buffer
*/
if (trunc) {
while (count > 0 && (data[count - 1] != '\n'))
--count;
if (count == 0) {
rc = -EINVAL;
goto out; goto out;
} }
}
if (format == SMK_LONG_FMT) {
/*
* Be sure the data string is terminated.
*/
data[count] = '\0';
if (smk_parse_long_rule(data, rule, 1, 0))
goto out_free_rule;
} else if (format == SMK_CHANGE_FMT) {
data[count] = '\0'; data[count] = '\0';
if (smk_parse_long_rule(data, rule, 1, 1)) tokens = (format == SMK_CHANGE_FMT ? 4 : 3);
goto out_free_rule; while (cnt < count) {
if (format == SMK_FIXED24_FMT) {
rc = smk_parse_rule(data, &rule, 1);
if (rc != 0) {
rc = -EINVAL;
goto out;
}
cnt = count;
} else { } else {
/* rc = smk_parse_long_rule(data + cnt, &rule, 1, tokens);
* More on the minor hack for backward compatibility if (rc <= 0) {
*/ rc = -EINVAL;
if (count == (SMK_OLOADLEN)) goto out;
data[SMK_OLOADLEN] = '-';
if (smk_parse_rule(data, rule, 1))
goto out_free_rule;
} }
cnt += rc;
if (rule_list == NULL) {
load = 1;
rule_list = &rule->smk_subject->smk_rules;
rule_lock = &rule->smk_subject->smk_rules_lock;
} }
rc = smk_set_access(rule, rule_list, rule_lock, load); if (rule_list == NULL)
if (rc == 0) { rc = smk_set_access(&rule, &rule.smk_subject->smk_rules,
rc = count; &rule.smk_subject->smk_rules_lock, 1);
else
rc = smk_set_access(&rule, rule_list, rule_lock, 0);
if (rc)
goto out; goto out;
} }
out_free_rule: rc = cnt;
kfree(rule);
out: out:
kfree(data); kfree(data);
return rc; return rc;
...@@ -901,7 +894,7 @@ static ssize_t smk_set_cipso(struct file *file, const char __user *buf, ...@@ -901,7 +894,7 @@ static ssize_t smk_set_cipso(struct file *file, const char __user *buf,
for (i = 0; i < catlen; i++) { for (i = 0; i < catlen; i++) {
rule += SMK_DIGITLEN; rule += SMK_DIGITLEN;
ret = sscanf(rule, "%u", &cat); ret = sscanf(rule, "%u", &cat);
if (ret != 1 || cat > SMACK_CIPSO_MAXCATVAL) if (ret != 1 || cat > SMACK_CIPSO_MAXCATNUM)
goto out; goto out;
smack_catset_bit(cat, mapcatset); smack_catset_bit(cat, mapcatset);
...@@ -1840,7 +1833,6 @@ static ssize_t smk_user_access(struct file *file, const char __user *buf, ...@@ -1840,7 +1833,6 @@ static ssize_t smk_user_access(struct file *file, const char __user *buf,
{ {
struct smack_parsed_rule rule; struct smack_parsed_rule rule;
char *data; char *data;
char *cod;
int res; int res;
data = simple_transaction_get(file, buf, count); data = simple_transaction_get(file, buf, count);
...@@ -1853,18 +1845,12 @@ static ssize_t smk_user_access(struct file *file, const char __user *buf, ...@@ -1853,18 +1845,12 @@ static ssize_t smk_user_access(struct file *file, const char __user *buf,
res = smk_parse_rule(data, &rule, 0); res = smk_parse_rule(data, &rule, 0);
} else { } else {
/* /*
* Copy the data to make sure the string is terminated. * simple_transaction_get() returns null-terminated data
*/ */
cod = kzalloc(count + 1, GFP_KERNEL); res = smk_parse_long_rule(data, &rule, 0, 3);
if (cod == NULL)
return -ENOMEM;
memcpy(cod, data, count);
cod[count] = '\0';
res = smk_parse_long_rule(cod, &rule, 0, 0);
kfree(cod);
} }
if (res) if (res < 0)
return -EINVAL; return -EINVAL;
res = smk_access(rule.smk_subject, rule.smk_object, res = smk_access(rule.smk_subject, rule.smk_object,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment