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

Pull security subsystem updates from James Morris:
 "In this update, Smack learns to love IPv6 and to mount a filesystem
  with a transmutable hierarchy (i.e.  security labels are inherited
  from parent directory upon creation rather than creating process).

  The rest of the changes are maintenance"

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security: (37 commits)
  tpm/tpm_i2c_infineon: Remove unused header file
  tpm: tpm_i2c_infinion: Don't modify i2c_client->driver
  evm: audit integrity metadata failures
  integrity: move integrity_audit_msg()
  evm: calculate HMAC after initializing posix acl on tmpfs
  maintainers:  add Dmitry Kasatkin
  Smack: Fix the bug smackcipso can't set CIPSO correctly
  Smack: Fix possible NULL pointer dereference at smk_netlbl_mls()
  Smack: Add smkfstransmute mount option
  Smack: Improve access check performance
  Smack: Local IPv6 port based controls
  tpm: fix regression caused by section type conflict of tpm_dev_release() in ppc builds
  maintainers: Remove Kent from maintainers
  tpm: move TPM_DIGEST_SIZE defintion
  tpm_tis: missing platform_driver_unregister() on error in init_tis()
  security: clarify cap_inode_getsecctx description
  apparmor: no need to delay vfree()
  apparmor: fix fully qualified name parsing
  apparmor: fix setprocattr arg processing for onexec
  apparmor: localize getting the security context to a few macros
  ...
This commit is contained in:
Linus Torvalds 2013-07-03 14:04:58 -07:00
commit f39d420f67
40 changed files with 1012 additions and 637 deletions

View File

@ -1129,11 +1129,6 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
The builtin appraise policy appraises all files The builtin appraise policy appraises all files
owned by uid=0. owned by uid=0.
ima_audit= [IMA]
Format: { "0" | "1" }
0 -- integrity auditing messages. (Default)
1 -- enable informational integrity auditing messages.
ima_hash= [IMA] ima_hash= [IMA]
Format: { "sha1" | "md5" } Format: { "sha1" | "md5" }
default: "sha1" default: "sha1"
@ -1160,6 +1155,11 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
int_pln_enable [x86] Enable power limit notification interrupt int_pln_enable [x86] Enable power limit notification interrupt
integrity_audit=[IMA]
Format: { "0" | "1" }
0 -- basic integrity auditing messages. (Default)
1 -- additional integrity auditing messages.
intel_iommu= [DMAR] Intel IOMMU driver (DMAR) option intel_iommu= [DMAR] Intel IOMMU driver (DMAR) option
on on
Enable intel iommu driver. Enable intel iommu driver.

View File

@ -4001,7 +4001,8 @@ S: Maintained
F: arch/ia64/ F: arch/ia64/
IBM Power in-Nest Crypto Acceleration IBM Power in-Nest Crypto Acceleration
M: Kent Yoder <key@linux.vnet.ibm.com> M: Marcelo Henrique Cerri <mhcerri@linux.vnet.ibm.com>
M: Fionnuala Gunter <fin@linux.vnet.ibm.com>
L: linux-crypto@vger.kernel.org L: linux-crypto@vger.kernel.org
S: Supported S: Supported
F: drivers/crypto/nx/ F: drivers/crypto/nx/
@ -4130,6 +4131,7 @@ F: drivers/ipack/
INTEGRITY MEASUREMENT ARCHITECTURE (IMA) INTEGRITY MEASUREMENT ARCHITECTURE (IMA)
M: Mimi Zohar <zohar@us.ibm.com> M: Mimi Zohar <zohar@us.ibm.com>
M: Dmitry Kasatkin <d.kasatkin@samsung.com>
S: Supported S: Supported
F: security/integrity/ima/ F: security/integrity/ima/
@ -8282,7 +8284,8 @@ S: Odd fixes
F: drivers/media/usb/tm6000/ F: drivers/media/usb/tm6000/
TPM DEVICE DRIVER TPM DEVICE DRIVER
M: Kent Yoder <key@linux.vnet.ibm.com> M: Leonidas Da Silva Barbosa <leosilva@linux.vnet.ibm.com>
M: Ashley Lai <ashley@ashleylai.com>
M: Rajiv Andrade <mail@srajiv.net> M: Rajiv Andrade <mail@srajiv.net>
W: http://tpmdd.sourceforge.net W: http://tpmdd.sourceforge.net
M: Marcel Selhorst <tpmdd@selhorst.net> M: Marcel Selhorst <tpmdd@selhorst.net>

View File

@ -1472,7 +1472,7 @@ EXPORT_SYMBOL_GPL(tpm_dev_vendor_release);
* Once all references to platform device are down to 0, * Once all references to platform device are down to 0,
* release all allocated structures. * release all allocated structures.
*/ */
static void tpm_dev_release(struct device *dev) void tpm_dev_release(struct device *dev)
{ {
struct tpm_chip *chip = dev_get_drvdata(dev); struct tpm_chip *chip = dev_get_drvdata(dev);

View File

@ -272,7 +272,6 @@ typedef union {
struct tpm_output_header out; struct tpm_output_header out;
} tpm_cmd_header; } tpm_cmd_header;
#define TPM_DIGEST_SIZE 20
struct tpm_pcrread_out { struct tpm_pcrread_out {
u8 pcr_result[TPM_DIGEST_SIZE]; u8 pcr_result[TPM_DIGEST_SIZE];
} __packed; } __packed;
@ -333,6 +332,7 @@ extern struct tpm_chip* tpm_register_hardware(struct device *,
const struct tpm_vendor_specific *); const struct tpm_vendor_specific *);
extern int tpm_open(struct inode *, struct file *); extern int tpm_open(struct inode *, struct file *);
extern int tpm_release(struct inode *, struct file *); extern int tpm_release(struct inode *, struct file *);
extern void tpm_dev_release(struct device *dev);
extern void tpm_dev_vendor_release(struct tpm_chip *); extern void tpm_dev_vendor_release(struct tpm_chip *);
extern ssize_t tpm_write(struct file *, const char __user *, size_t, extern ssize_t tpm_write(struct file *, const char __user *, size_t,
loff_t *); loff_t *);

View File

@ -24,7 +24,6 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/wait.h> #include <linux/wait.h>
#include "tpm.h" #include "tpm.h"
@ -74,7 +73,6 @@ struct tpm_inf_dev {
}; };
static struct tpm_inf_dev tpm_dev; static struct tpm_inf_dev tpm_dev;
static struct i2c_driver tpm_tis_i2c_driver;
/* /*
* iic_tpm_read() - read from TPM register * iic_tpm_read() - read from TPM register
@ -744,11 +742,9 @@ static int tpm_tis_i2c_probe(struct i2c_client *client,
return -ENODEV; return -ENODEV;
} }
client->driver = &tpm_tis_i2c_driver;
tpm_dev.client = client; tpm_dev.client = client;
rc = tpm_tis_i2c_init(&client->dev); rc = tpm_tis_i2c_init(&client->dev);
if (rc != 0) { if (rc != 0) {
client->driver = NULL;
tpm_dev.client = NULL; tpm_dev.client = NULL;
rc = -ENODEV; rc = -ENODEV;
} }

View File

@ -884,12 +884,19 @@ static int __init init_tis(void)
rc = platform_driver_register(&tis_drv); rc = platform_driver_register(&tis_drv);
if (rc < 0) if (rc < 0)
return rc; return rc;
if (IS_ERR(pdev=platform_device_register_simple("tpm_tis", -1, NULL, 0))) pdev = platform_device_register_simple("tpm_tis", -1, NULL, 0);
return PTR_ERR(pdev); if (IS_ERR(pdev)) {
if((rc=tpm_tis_init(&pdev->dev, TIS_MEM_BASE, TIS_MEM_LEN, 0)) != 0) { rc = PTR_ERR(pdev);
platform_device_unregister(pdev); goto err_dev;
platform_driver_unregister(&tis_drv);
} }
rc = tpm_tis_init(&pdev->dev, TIS_MEM_BASE, TIS_MEM_LEN, 0);
if (rc)
goto err_init;
return 0;
err_init:
platform_device_unregister(pdev);
err_dev:
platform_driver_unregister(&tis_drv);
return rc; return rc;
} }

View File

@ -1392,7 +1392,8 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
* @ctxlen contains the length of @ctx. * @ctxlen contains the length of @ctx.
* *
* @inode_getsecctx: * @inode_getsecctx:
* Returns a string containing all relevant security context information * On success, returns 0 and fills out @ctx and @ctxlen with the security
* context for the given @inode.
* *
* @inode we wish to get the security context of. * @inode we wish to get the security context of.
* @ctx is a pointer in which to place the allocated security context. * @ctx is a pointer in which to place the allocated security context.

View File

@ -22,6 +22,8 @@
#ifndef __LINUX_TPM_H__ #ifndef __LINUX_TPM_H__
#define __LINUX_TPM_H__ #define __LINUX_TPM_H__
#define TPM_DIGEST_SIZE 20 /* Max TPM v1.2 PCR size */
/* /*
* Chip num is this value or a valid tpm idx * Chip num is this value or a valid tpm idx
*/ */

View File

@ -1936,6 +1936,13 @@ shmem_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
inode = shmem_get_inode(dir->i_sb, dir, mode, dev, VM_NORESERVE); inode = shmem_get_inode(dir->i_sb, dir, mode, dev, VM_NORESERVE);
if (inode) { if (inode) {
#ifdef CONFIG_TMPFS_POSIX_ACL
error = generic_acl_init(inode, dir);
if (error) {
iput(inode);
return error;
}
#endif
error = security_inode_init_security(inode, dir, error = security_inode_init_security(inode, dir,
&dentry->d_name, &dentry->d_name,
shmem_initxattrs, NULL); shmem_initxattrs, NULL);
@ -1945,15 +1952,8 @@ shmem_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
return error; return error;
} }
} }
#ifdef CONFIG_TMPFS_POSIX_ACL
error = generic_acl_init(inode, dir);
if (error) {
iput(inode);
return error;
}
#else
error = 0; error = 0;
#endif
dir->i_size += BOGO_DIRENT_SIZE; dir->i_size += BOGO_DIRENT_SIZE;
dir->i_ctime = dir->i_mtime = CURRENT_TIME; dir->i_ctime = dir->i_mtime = CURRENT_TIME;
d_instantiate(dentry, inode); d_instantiate(dentry, inode);

View File

@ -88,7 +88,7 @@ static const char *const aa_audit_type[] = {
"HINT", "HINT",
"STATUS", "STATUS",
"ERROR", "ERROR",
"KILLED" "KILLED",
"AUTO" "AUTO"
}; };

View File

@ -68,6 +68,23 @@ void aa_dup_task_context(struct aa_task_cxt *new, const struct aa_task_cxt *old)
aa_get_profile(new->onexec); aa_get_profile(new->onexec);
} }
/**
* aa_get_task_profile - Get another task's profile
* @task: task to query (NOT NULL)
*
* Returns: counted reference to @task's profile
*/
struct aa_profile *aa_get_task_profile(struct task_struct *task)
{
struct aa_profile *p;
rcu_read_lock();
p = aa_get_profile(__aa_task_profile(task));
rcu_read_unlock();
return p;
}
/** /**
* aa_replace_current_profile - replace the current tasks profiles * aa_replace_current_profile - replace the current tasks profiles
* @profile: new profile (NOT NULL) * @profile: new profile (NOT NULL)
@ -76,7 +93,7 @@ void aa_dup_task_context(struct aa_task_cxt *new, const struct aa_task_cxt *old)
*/ */
int aa_replace_current_profile(struct aa_profile *profile) int aa_replace_current_profile(struct aa_profile *profile)
{ {
struct aa_task_cxt *cxt = current_cred()->security; struct aa_task_cxt *cxt = current_cxt();
struct cred *new; struct cred *new;
BUG_ON(!profile); BUG_ON(!profile);
@ -87,17 +104,13 @@ int aa_replace_current_profile(struct aa_profile *profile)
if (!new) if (!new)
return -ENOMEM; return -ENOMEM;
cxt = new->security; cxt = cred_cxt(new);
if (unconfined(profile) || (cxt->profile->ns != profile->ns)) { if (unconfined(profile) || (cxt->profile->ns != profile->ns))
/* if switching to unconfined or a different profile namespace /* if switching to unconfined or a different profile namespace
* clear out context state * clear out context state
*/ */
aa_put_profile(cxt->previous); aa_clear_task_cxt_trans(cxt);
aa_put_profile(cxt->onexec);
cxt->previous = NULL;
cxt->onexec = NULL;
cxt->token = 0;
}
/* 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 is the reference keeping
* @profile valid, so make sure to get its reference before dropping * @profile valid, so make sure to get its reference before dropping
@ -123,7 +136,7 @@ int aa_set_current_onexec(struct aa_profile *profile)
if (!new) if (!new)
return -ENOMEM; return -ENOMEM;
cxt = new->security; cxt = cred_cxt(new);
aa_get_profile(profile); aa_get_profile(profile);
aa_put_profile(cxt->onexec); aa_put_profile(cxt->onexec);
cxt->onexec = profile; cxt->onexec = profile;
@ -150,7 +163,7 @@ int aa_set_current_hat(struct aa_profile *profile, u64 token)
return -ENOMEM; return -ENOMEM;
BUG_ON(!profile); BUG_ON(!profile);
cxt = new->security; cxt = cred_cxt(new);
if (!cxt->previous) { if (!cxt->previous) {
/* transfer refcount */ /* transfer refcount */
cxt->previous = cxt->profile; cxt->previous = cxt->profile;
@ -187,7 +200,7 @@ int aa_restore_previous_profile(u64 token)
if (!new) if (!new)
return -ENOMEM; return -ENOMEM;
cxt = new->security; cxt = cred_cxt(new);
if (cxt->token != token) { if (cxt->token != token) {
abort_creds(new); abort_creds(new);
return -EACCES; return -EACCES;
@ -205,11 +218,10 @@ int aa_restore_previous_profile(u64 token)
aa_get_profile(cxt->profile); aa_get_profile(cxt->profile);
aa_put_profile(cxt->previous); aa_put_profile(cxt->previous);
} }
/* clear exec && prev information when restoring to previous context */ /* ref has been transfered so avoid putting ref in clear_task_cxt */
cxt->previous = NULL; cxt->previous = NULL;
cxt->token = 0; /* clear exec && prev information when restoring to previous context */
aa_put_profile(cxt->onexec); aa_clear_task_cxt_trans(cxt);
cxt->onexec = NULL;
commit_creds(new); commit_creds(new);
return 0; return 0;

View File

@ -62,17 +62,14 @@ static int may_change_ptraced_domain(struct task_struct *task,
struct aa_profile *to_profile) struct aa_profile *to_profile)
{ {
struct task_struct *tracer; struct task_struct *tracer;
const struct cred *cred = NULL;
struct aa_profile *tracerp = NULL; struct aa_profile *tracerp = NULL;
int error = 0; int error = 0;
rcu_read_lock(); rcu_read_lock();
tracer = ptrace_parent(task); tracer = ptrace_parent(task);
if (tracer) { if (tracer)
/* released below */ /* released below */
cred = get_task_cred(tracer); tracerp = aa_get_task_profile(tracer);
tracerp = aa_cred_profile(cred);
}
/* not ptraced */ /* not ptraced */
if (!tracer || unconfined(tracerp)) if (!tracer || unconfined(tracerp))
@ -82,8 +79,7 @@ static int may_change_ptraced_domain(struct task_struct *task,
out: out:
rcu_read_unlock(); rcu_read_unlock();
if (cred) aa_put_profile(tracerp);
put_cred(cred);
return error; return error;
} }
@ -360,7 +356,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
if (bprm->cred_prepared) if (bprm->cred_prepared)
return 0; return 0;
cxt = bprm->cred->security; cxt = cred_cxt(bprm->cred);
BUG_ON(!cxt); BUG_ON(!cxt);
profile = aa_get_profile(aa_newest_version(cxt->profile)); profile = aa_get_profile(aa_newest_version(cxt->profile));
@ -443,6 +439,8 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
} else { } else {
error = -ENOENT; error = -ENOENT;
info = "profile not found"; info = "profile not found";
/* remove MAY_EXEC to audit as failure */
perms.allow &= ~MAY_EXEC;
} }
} }
} else if (COMPLAIN_MODE(profile)) { } else if (COMPLAIN_MODE(profile)) {
@ -514,11 +512,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
cxt->profile = new_profile; cxt->profile = new_profile;
/* clear out all temporary/transitional state from the context */ /* clear out all temporary/transitional state from the context */
aa_put_profile(cxt->previous); aa_clear_task_cxt_trans(cxt);
aa_put_profile(cxt->onexec);
cxt->previous = NULL;
cxt->onexec = NULL;
cxt->token = 0;
audit: audit:
error = aa_audit_file(profile, &perms, GFP_KERNEL, OP_EXEC, MAY_EXEC, error = aa_audit_file(profile, &perms, GFP_KERNEL, OP_EXEC, MAY_EXEC,
@ -557,7 +551,7 @@ int apparmor_bprm_secureexec(struct linux_binprm *bprm)
void apparmor_bprm_committing_creds(struct linux_binprm *bprm) void apparmor_bprm_committing_creds(struct linux_binprm *bprm)
{ {
struct aa_profile *profile = __aa_current_profile(); struct aa_profile *profile = __aa_current_profile();
struct aa_task_cxt *new_cxt = bprm->cred->security; struct aa_task_cxt *new_cxt = cred_cxt(bprm->cred);
/* bail out if unconfined or not changing profile */ /* bail out if unconfined or not changing profile */
if ((new_cxt->profile == profile) || if ((new_cxt->profile == profile) ||
@ -634,7 +628,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest)
/* released below */ /* released below */
cred = get_current_cred(); cred = get_current_cred();
cxt = cred->security; cxt = cred_cxt(cred);
profile = aa_cred_profile(cred); profile = aa_cred_profile(cred);
previous_profile = cxt->previous; previous_profile = cxt->previous;
@ -750,7 +744,6 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec,
bool permtest) bool permtest)
{ {
const struct cred *cred; const struct cred *cred;
struct aa_task_cxt *cxt;
struct aa_profile *profile, *target = NULL; struct aa_profile *profile, *target = NULL;
struct aa_namespace *ns = NULL; struct aa_namespace *ns = NULL;
struct file_perms perms = {}; struct file_perms perms = {};
@ -770,7 +763,6 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec,
} }
cred = get_current_cred(); cred = get_current_cred();
cxt = cred->security;
profile = aa_cred_profile(cred); profile = aa_cred_profile(cred);
/* /*

View File

@ -15,6 +15,7 @@
#ifndef __APPARMOR_H #ifndef __APPARMOR_H
#define __APPARMOR_H #define __APPARMOR_H
#include <linux/slab.h>
#include <linux/fs.h> #include <linux/fs.h>
#include "match.h" #include "match.h"
@ -64,9 +65,18 @@ extern int apparmor_initialized __initdata;
/* fn's in lib */ /* fn's in lib */
char *aa_split_fqname(char *args, char **ns_name); char *aa_split_fqname(char *args, char **ns_name);
void aa_info_message(const char *str); void aa_info_message(const char *str);
void *kvmalloc(size_t size); void *__aa_kvmalloc(size_t size, gfp_t flags);
void kvfree(void *buffer); void kvfree(void *buffer);
static inline void *kvmalloc(size_t size)
{
return __aa_kvmalloc(size, 0);
}
static inline void *kvzalloc(size_t size)
{
return __aa_kvmalloc(size, __GFP_ZERO);
}
/** /**
* aa_strneq - compare null terminated @str to a non null terminated substring * aa_strneq - compare null terminated @str to a non null terminated substring

View File

@ -21,6 +21,9 @@
#include "policy.h" #include "policy.h"
#define cred_cxt(X) (X)->security
#define current_cxt() cred_cxt(current_cred())
/* struct aa_file_cxt - the AppArmor context the file was opened in /* struct aa_file_cxt - the AppArmor context the file was opened in
* @perms: the permission the file was opened with * @perms: the permission the file was opened with
* *
@ -80,23 +83,8 @@ int aa_replace_current_profile(struct aa_profile *profile);
int aa_set_current_onexec(struct aa_profile *profile); int aa_set_current_onexec(struct aa_profile *profile);
int aa_set_current_hat(struct aa_profile *profile, u64 token); int aa_set_current_hat(struct aa_profile *profile, u64 token);
int aa_restore_previous_profile(u64 cookie); int aa_restore_previous_profile(u64 cookie);
struct aa_profile *aa_get_task_profile(struct task_struct *task);
/**
* __aa_task_is_confined - determine if @task has any confinement
* @task: task to check confinement of (NOT NULL)
*
* If @task != current needs to be called in RCU safe critical section
*/
static inline bool __aa_task_is_confined(struct task_struct *task)
{
struct aa_task_cxt *cxt = __task_cred(task)->security;
BUG_ON(!cxt || !cxt->profile);
if (unconfined(aa_newest_version(cxt->profile)))
return 0;
return 1;
}
/** /**
* aa_cred_profile - obtain cred's profiles * aa_cred_profile - obtain cred's profiles
@ -108,11 +96,35 @@ static inline bool __aa_task_is_confined(struct task_struct *task)
*/ */
static inline struct aa_profile *aa_cred_profile(const struct cred *cred) static inline struct aa_profile *aa_cred_profile(const struct cred *cred)
{ {
struct aa_task_cxt *cxt = cred->security; 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 aa_newest_version(cxt->profile);
} }
/**
* __aa_task_profile - retrieve another task's profile
* @task: task to query (NOT NULL)
*
* Returns: @task's profile without incrementing its ref count
*
* If @task != current needs to be called in RCU safe critical section
*/
static inline struct aa_profile *__aa_task_profile(struct task_struct *task)
{
return aa_cred_profile(__task_cred(task));
}
/**
* __aa_task_is_confined - determine if @task has any confinement
* @task: task to check confinement of (NOT NULL)
*
* If @task != current needs to be called in RCU safe critical section
*/
static inline bool __aa_task_is_confined(struct task_struct *task)
{
return !unconfined(__aa_task_profile(task));
}
/** /**
* __aa_current_profile - find the current tasks confining profile * __aa_current_profile - find the current tasks confining profile
* *
@ -136,7 +148,7 @@ static inline struct aa_profile *__aa_current_profile(void)
*/ */
static inline struct aa_profile *aa_current_profile(void) static inline struct aa_profile *aa_current_profile(void)
{ {
const struct aa_task_cxt *cxt = current_cred()->security; const struct aa_task_cxt *cxt = current_cxt();
struct aa_profile *profile; struct aa_profile *profile;
BUG_ON(!cxt || !cxt->profile); BUG_ON(!cxt || !cxt->profile);
@ -151,4 +163,17 @@ static inline struct aa_profile *aa_current_profile(void)
return profile; return profile;
} }
/**
* aa_clear_task_cxt_trans - clear transition tracking info from the cxt
* @cxt: task context to clear (NOT NULL)
*/
static inline void aa_clear_task_cxt_trans(struct aa_task_cxt *cxt)
{
aa_put_profile(cxt->previous);
aa_put_profile(cxt->onexec);
cxt->previous = NULL;
cxt->onexec = NULL;
cxt->token = 0;
}
#endif /* __AA_CONTEXT_H */ #endif /* __AA_CONTEXT_H */

View File

@ -186,11 +186,6 @@ static inline void aa_free_file_rules(struct aa_file_rules *rules)
aa_free_domain_entries(&rules->trans); aa_free_domain_entries(&rules->trans);
} }
#define ACC_FMODE(x) (("\000\004\002\006"[(x)&O_ACCMODE]) | (((x) << 1) & 0x40))
/* from namei.c */
#define MAP_OPEN_FLAGS(x) ((((x) + 1) & O_ACCMODE) ? (x) + 1 : (x))
/** /**
* aa_map_file_perms - map file flags to AppArmor permissions * aa_map_file_perms - map file flags to AppArmor permissions
* @file: open file to map flags to AppArmor permissions * @file: open file to map flags to AppArmor permissions
@ -199,8 +194,13 @@ static inline void aa_free_file_rules(struct aa_file_rules *rules)
*/ */
static inline u32 aa_map_file_to_perms(struct file *file) static inline u32 aa_map_file_to_perms(struct file *file)
{ {
int flags = MAP_OPEN_FLAGS(file->f_flags); int flags = file->f_flags;
u32 perms = ACC_FMODE(file->f_mode); u32 perms = 0;
if (file->f_mode & FMODE_WRITE)
perms |= MAY_WRITE;
if (file->f_mode & FMODE_READ)
perms |= MAY_READ;
if ((flags & O_APPEND) && (perms & MAY_WRITE)) if ((flags & O_APPEND) && (perms & MAY_WRITE))
perms = (perms & ~MAY_WRITE) | MAY_APPEND; perms = (perms & ~MAY_WRITE) | MAY_APPEND;

View File

@ -4,7 +4,7 @@
* This file contains AppArmor policy dfa matching engine definitions. * This file contains AppArmor policy dfa matching engine definitions.
* *
* Copyright (C) 1998-2008 Novell/SUSE * Copyright (C) 1998-2008 Novell/SUSE
* Copyright 2009-2010 Canonical Ltd. * Copyright 2009-2012 Canonical Ltd.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as * modify it under the terms of the GNU General Public License as
@ -16,25 +16,30 @@
#define __AA_MATCH_H #define __AA_MATCH_H
#include <linux/kref.h> #include <linux/kref.h>
#include <linux/workqueue.h>
#define DFA_NOMATCH 0 #define DFA_NOMATCH 0
#define DFA_START 1 #define DFA_START 1
#define DFA_VALID_PERM_MASK 0xffffffff
#define DFA_VALID_PERM2_MASK 0xffffffff
/** /**
* The format used for transition tables is based on the GNU flex table * The format used for transition tables is based on the GNU flex table
* file format (--tables-file option; see Table File Format in the flex * file format (--tables-file option; see Table File Format in the flex
* info pages and the flex sources for documentation). The magic number * info pages and the flex sources for documentation). The magic number
* used in the header is 0x1B5E783D instead of 0xF13C57B1 though, because * used in the header is 0x1B5E783D instead of 0xF13C57B1 though, because
* the YY_ID_CHK (check) and YY_ID_DEF (default) tables are used * new tables have been defined and others YY_ID_CHK (check) and YY_ID_DEF
* slightly differently (see the apparmor-parser package). * (default) tables are used slightly differently (see the apparmor-parser
* package).
*
*
* The data in the packed dfa is stored in network byte order, and the tables
* are arranged for flexibility. We convert the table data to host native
* byte order.
*
* The dfa begins with a table set header, and is followed by the actual
* tables.
*/ */
#define YYTH_MAGIC 0x1B5E783D #define YYTH_MAGIC 0x1B5E783D
#define YYTH_DEF_RECURSE 0x1 /* DEF Table is recursive */
struct table_set_header { struct table_set_header {
u32 th_magic; /* YYTH_MAGIC */ u32 th_magic; /* YYTH_MAGIC */
@ -63,7 +68,7 @@ struct table_set_header {
#define YYTD_DATA32 4 #define YYTD_DATA32 4
#define YYTD_DATA64 8 #define YYTD_DATA64 8
/* Each ACCEPT2 table gets 6 dedicated flags, YYTD_DATAX define the /* ACCEPT & ACCEPT2 tables gets 6 dedicated flags, YYTD_DATAX define the
* first flags * first flags
*/ */
#define ACCEPT1_FLAGS(X) ((X) & 0x3f) #define ACCEPT1_FLAGS(X) ((X) & 0x3f)

View File

@ -32,13 +32,13 @@
extern const char *const profile_mode_names[]; extern const char *const profile_mode_names[];
#define APPARMOR_NAMES_MAX_INDEX 3 #define APPARMOR_NAMES_MAX_INDEX 3
#define COMPLAIN_MODE(_profile) \ #define PROFILE_MODE(_profile, _mode) \
((aa_g_profile_mode == APPARMOR_COMPLAIN) || \ ((aa_g_profile_mode == (_mode)) || \
((_profile)->mode == APPARMOR_COMPLAIN)) ((_profile)->mode == (_mode)))
#define KILL_MODE(_profile) \ #define COMPLAIN_MODE(_profile) PROFILE_MODE((_profile), APPARMOR_COMPLAIN)
((aa_g_profile_mode == APPARMOR_KILL) || \
((_profile)->mode == APPARMOR_KILL)) #define KILL_MODE(_profile) PROFILE_MODE((_profile), APPARMOR_KILL)
#define PROFILE_IS_HAT(_profile) ((_profile)->flags & PFLAG_HAT) #define PROFILE_IS_HAT(_profile) ((_profile)->flags & PFLAG_HAT)
@ -105,6 +105,7 @@ struct aa_ns_acct {
* @acct: accounting for the namespace * @acct: accounting for the namespace
* @unconfined: special unconfined profile for the namespace * @unconfined: special unconfined profile for the namespace
* @sub_ns: list of namespaces under the current namespace. * @sub_ns: list of namespaces under the current namespace.
* @uniq_null: uniq value used for null learning profiles
* *
* An aa_namespace defines the set profiles that are searched to determine * An aa_namespace defines the set profiles that are searched to determine
* which profile to attach to a task. Profiles can not be shared between * which profile to attach to a task. Profiles can not be shared between
@ -127,6 +128,7 @@ struct aa_namespace {
struct aa_ns_acct acct; struct aa_ns_acct acct;
struct aa_profile *unconfined; struct aa_profile *unconfined;
struct list_head sub_ns; struct list_head sub_ns;
atomic_t uniq_null;
}; };
/* struct aa_policydb - match engine for a policy /* struct aa_policydb - match engine for a policy
@ -148,7 +150,6 @@ struct aa_policydb {
* @rename: optional profile name that this profile renamed * @rename: optional profile name that this profile renamed
* @xmatch: optional extended matching for unconfined executables names * @xmatch: optional extended matching for unconfined executables names
* @xmatch_len: xmatch prefix len, used to determine xmatch priority * @xmatch_len: xmatch prefix len, used to determine xmatch priority
* @sid: the unique security id number of this profile
* @audit: the auditing mode of the profile * @audit: the auditing mode of the profile
* @mode: the enforcement mode of the profile * @mode: the enforcement mode of the profile
* @flags: flags controlling profile behavior * @flags: flags controlling profile behavior
@ -184,7 +185,6 @@ struct aa_profile {
struct aa_dfa *xmatch; struct aa_dfa *xmatch;
int xmatch_len; int xmatch_len;
u32 sid;
enum audit_mode audit; enum audit_mode audit;
enum profile_mode mode; enum profile_mode mode;
u32 flags; u32 flags;

View File

@ -21,6 +21,5 @@
int aa_getprocattr(struct aa_profile *profile, char **string); int aa_getprocattr(struct aa_profile *profile, char **string);
int aa_setprocattr_changehat(char *args, size_t size, int test); int aa_setprocattr_changehat(char *args, size_t size, int test);
int aa_setprocattr_changeprofile(char *fqname, bool onexec, int test); int aa_setprocattr_changeprofile(char *fqname, bool onexec, int test);
int aa_setprocattr_permipc(char *fqname);
#endif /* __AA_PROCATTR_H */ #endif /* __AA_PROCATTR_H */

View File

@ -16,7 +16,9 @@
#include <linux/types.h> #include <linux/types.h>
struct aa_profile; /* sid value that will not be allocated */
#define AA_SID_INVALID 0
#define AA_SID_ALLOC AA_SID_INVALID
u32 aa_alloc_sid(void); u32 aa_alloc_sid(void);
void aa_free_sid(u32 sid); void aa_free_sid(u32 sid);

View File

@ -95,23 +95,18 @@ int aa_ptrace(struct task_struct *tracer, struct task_struct *tracee,
* - tracer profile has CAP_SYS_PTRACE * - tracer profile has CAP_SYS_PTRACE
*/ */
struct aa_profile *tracer_p; struct aa_profile *tracer_p = aa_get_task_profile(tracer);
/* cred released below */
const struct cred *cred = get_task_cred(tracer);
int error = 0; int error = 0;
tracer_p = aa_cred_profile(cred);
if (!unconfined(tracer_p)) { if (!unconfined(tracer_p)) {
/* lcred released below */ struct aa_profile *tracee_p = aa_get_task_profile(tracee);
const struct cred *lcred = get_task_cred(tracee);
struct aa_profile *tracee_p = aa_cred_profile(lcred);
error = aa_may_ptrace(tracer, tracer_p, tracee_p, mode); error = aa_may_ptrace(tracer, tracer_p, tracee_p, mode);
error = aa_audit_ptrace(tracer_p, tracee_p, error); error = aa_audit_ptrace(tracer_p, tracee_p, error);
put_cred(lcred); aa_put_profile(tracee_p);
} }
put_cred(cred); aa_put_profile(tracer_p);
return error; return error;
} }

View File

@ -45,8 +45,10 @@ char *aa_split_fqname(char *fqname, char **ns_name)
*ns_name = skip_spaces(&name[1]); *ns_name = skip_spaces(&name[1]);
if (split) { if (split) {
/* overwrite ':' with \0 */ /* overwrite ':' with \0 */
*split = 0; *split++ = 0;
name = skip_spaces(split + 1); if (strncmp(split, "//", 2) == 0)
split += 2;
name = skip_spaces(split);
} else } else
/* a ns name without a following profile is allowed */ /* a ns name without a following profile is allowed */
name = NULL; name = NULL;
@ -75,15 +77,16 @@ void aa_info_message(const char *str)
} }
/** /**
* kvmalloc - do allocation preferring kmalloc but falling back to vmalloc * __aa_kvmalloc - do allocation preferring kmalloc but falling back to vmalloc
* @size: size of allocation * @size: how many bytes of memory are required
* @flags: the type of memory to allocate (see kmalloc).
* *
* Return: allocated buffer or NULL if failed * Return: allocated buffer or NULL if failed
* *
* It is possible that policy being loaded from the user is larger than * It is possible that policy being loaded from the user is larger than
* what can be allocated by kmalloc, in those cases fall back to vmalloc. * what can be allocated by kmalloc, in those cases fall back to vmalloc.
*/ */
void *kvmalloc(size_t size) void *__aa_kvmalloc(size_t size, gfp_t flags)
{ {
void *buffer = NULL; void *buffer = NULL;
@ -92,31 +95,21 @@ void *kvmalloc(size_t size)
/* do not attempt kmalloc if we need more than 16 pages at once */ /* do not attempt kmalloc if we need more than 16 pages at once */
if (size <= (16*PAGE_SIZE)) if (size <= (16*PAGE_SIZE))
buffer = kmalloc(size, 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 /* see kvfree for why size must be at least work_struct size
* when allocated via vmalloc * when allocated via vmalloc
*/ */
if (size < sizeof(struct work_struct)) if (size < sizeof(struct work_struct))
size = sizeof(struct work_struct); size = sizeof(struct work_struct);
buffer = vmalloc(size); if (flags & __GFP_ZERO)
buffer = vzalloc(size);
else
buffer = vmalloc(size);
} }
return buffer; return buffer;
} }
/**
* do_vfree - workqueue routine for freeing vmalloced memory
* @work: data to be freed
*
* The work_struct is overlaid to the data being freed, as at the point
* the work is scheduled the data is no longer valid, be its freeing
* needs to be delayed until safe.
*/
static void do_vfree(struct work_struct *work)
{
vfree(work);
}
/** /**
* kvfree - free an allocation do by kvmalloc * kvfree - free an allocation do by kvmalloc
* @buffer: buffer to free (MAYBE_NULL) * @buffer: buffer to free (MAYBE_NULL)
@ -125,13 +118,8 @@ static void do_vfree(struct work_struct *work)
*/ */
void kvfree(void *buffer) void kvfree(void *buffer)
{ {
if (is_vmalloc_addr(buffer)) { if (is_vmalloc_addr(buffer))
/* Data is no longer valid so just use the allocated space vfree(buffer);
* as the work_struct else
*/
struct work_struct *work = (struct work_struct *) buffer;
INIT_WORK(work, do_vfree);
schedule_work(work);
} else
kfree(buffer); kfree(buffer);
} }

View File

@ -48,8 +48,8 @@ int apparmor_initialized __initdata;
*/ */
static void apparmor_cred_free(struct cred *cred) static void apparmor_cred_free(struct cred *cred)
{ {
aa_free_task_context(cred->security); aa_free_task_context(cred_cxt(cred));
cred->security = NULL; cred_cxt(cred) = NULL;
} }
/* /*
@ -62,7 +62,7 @@ static int apparmor_cred_alloc_blank(struct cred *cred, gfp_t gfp)
if (!cxt) if (!cxt)
return -ENOMEM; return -ENOMEM;
cred->security = cxt; cred_cxt(cred) = cxt;
return 0; return 0;
} }
@ -77,8 +77,8 @@ static int apparmor_cred_prepare(struct cred *new, const struct cred *old,
if (!cxt) if (!cxt)
return -ENOMEM; return -ENOMEM;
aa_dup_task_context(cxt, old->security); aa_dup_task_context(cxt, cred_cxt(old));
new->security = cxt; cred_cxt(new) = cxt;
return 0; return 0;
} }
@ -87,8 +87,8 @@ static int apparmor_cred_prepare(struct cred *new, const struct cred *old,
*/ */
static void apparmor_cred_transfer(struct cred *new, const struct cred *old) static void apparmor_cred_transfer(struct cred *new, const struct cred *old)
{ {
const struct aa_task_cxt *old_cxt = old->security; const struct aa_task_cxt *old_cxt = cred_cxt(old);
struct aa_task_cxt *new_cxt = new->security; struct aa_task_cxt *new_cxt = cred_cxt(new);
aa_dup_task_context(new_cxt, old_cxt); aa_dup_task_context(new_cxt, old_cxt);
} }
@ -469,7 +469,6 @@ static int apparmor_file_lock(struct file *file, unsigned int cmd)
static int common_mmap(int op, struct file *file, unsigned long prot, static int common_mmap(int op, struct file *file, unsigned long prot,
unsigned long flags) unsigned long flags)
{ {
struct dentry *dentry;
int mask = 0; int mask = 0;
if (!file || !file->f_security) if (!file || !file->f_security)
@ -486,7 +485,6 @@ static int common_mmap(int op, struct file *file, unsigned long prot,
if (prot & PROT_EXEC) if (prot & PROT_EXEC)
mask |= AA_EXEC_MMAP; mask |= AA_EXEC_MMAP;
dentry = file->f_path.dentry;
return common_file_perm(op, file, mask); return common_file_perm(op, file, mask);
} }
@ -507,11 +505,9 @@ static int apparmor_getprocattr(struct task_struct *task, char *name,
char **value) char **value)
{ {
int error = -ENOENT; int error = -ENOENT;
struct aa_profile *profile;
/* 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->security; struct aa_task_cxt *cxt = cred_cxt(cred);
profile = aa_cred_profile(cred);
if (strcmp(name, "current") == 0) if (strcmp(name, "current") == 0)
error = aa_getprocattr(aa_newest_version(cxt->profile), error = aa_getprocattr(aa_newest_version(cxt->profile),
@ -533,6 +529,8 @@ static int apparmor_getprocattr(struct task_struct *task, char *name,
static int apparmor_setprocattr(struct task_struct *task, char *name, static int apparmor_setprocattr(struct task_struct *task, char *name,
void *value, size_t size) void *value, size_t size)
{ {
struct common_audit_data sa;
struct apparmor_audit_data aad = {0,};
char *command, *args = value; char *command, *args = value;
size_t arg_size; size_t arg_size;
int error; int error;
@ -576,30 +574,31 @@ static int apparmor_setprocattr(struct task_struct *task, char *name,
} else if (strcmp(command, "permprofile") == 0) { } else if (strcmp(command, "permprofile") == 0) {
error = aa_setprocattr_changeprofile(args, !AA_ONEXEC, error = aa_setprocattr_changeprofile(args, !AA_ONEXEC,
AA_DO_TEST); AA_DO_TEST);
} else if (strcmp(command, "permipc") == 0) { } else
error = aa_setprocattr_permipc(args); goto fail;
} else {
struct common_audit_data sa;
struct apparmor_audit_data aad = {0,};
sa.type = LSM_AUDIT_DATA_NONE;
sa.aad = &aad;
aad.op = OP_SETPROCATTR;
aad.info = name;
aad.error = -EINVAL;
return aa_audit(AUDIT_APPARMOR_DENIED,
__aa_current_profile(), GFP_KERNEL,
&sa, NULL);
}
} else if (strcmp(name, "exec") == 0) { } else if (strcmp(name, "exec") == 0) {
error = aa_setprocattr_changeprofile(args, AA_ONEXEC, if (strcmp(command, "exec") == 0)
!AA_DO_TEST); error = aa_setprocattr_changeprofile(args, AA_ONEXEC,
} else { !AA_DO_TEST);
else
goto fail;
} else
/* only support the "current" and "exec" process attributes */ /* only support the "current" and "exec" process attributes */
return -EINVAL; return -EINVAL;
}
if (!error) if (!error)
error = size; error = size;
return error; return error;
fail:
sa.type = LSM_AUDIT_DATA_NONE;
sa.aad = &aad;
aad.profile = aa_current_profile();
aad.op = OP_SETPROCATTR;
aad.info = name;
aad.error = -EINVAL;
aa_audit_msg(AUDIT_APPARMOR_DENIED, &sa, NULL);
return -EINVAL;
} }
static int apparmor_task_setrlimit(struct task_struct *task, static int apparmor_task_setrlimit(struct task_struct *task,
@ -886,7 +885,7 @@ static int __init set_init_cxt(void)
return -ENOMEM; return -ENOMEM;
cxt->profile = aa_get_profile(root_ns->unconfined); cxt->profile = aa_get_profile(root_ns->unconfined);
cred->security = cxt; cred_cxt(cred) = cxt;
return 0; return 0;
} }
@ -915,8 +914,11 @@ static int __init apparmor_init(void)
error = register_security(&apparmor_ops); error = register_security(&apparmor_ops);
if (error) { if (error) {
struct cred *cred = (struct cred *)current->real_cred;
aa_free_task_context(cred_cxt(cred));
cred_cxt(cred) = NULL;
AA_ERROR("Unable to register AppArmor\n"); AA_ERROR("Unable to register AppArmor\n");
goto set_init_cxt_out; goto register_security_out;
} }
/* Report that AppArmor successfully initialized */ /* Report that AppArmor successfully initialized */
@ -930,9 +932,6 @@ static int __init apparmor_init(void)
return error; return error;
set_init_cxt_out:
aa_free_task_context(current->real_cred->security);
register_security_out: register_security_out:
aa_free_root_ns(); aa_free_root_ns();

View File

@ -4,7 +4,7 @@
* This file contains AppArmor dfa based regular expression matching engine * This file contains AppArmor dfa based regular expression matching engine
* *
* Copyright (C) 1998-2008 Novell/SUSE * Copyright (C) 1998-2008 Novell/SUSE
* Copyright 2009-2010 Canonical Ltd. * Copyright 2009-2012 Canonical Ltd.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as * modify it under the terms of the GNU General Public License as
@ -23,6 +23,8 @@
#include "include/apparmor.h" #include "include/apparmor.h"
#include "include/match.h" #include "include/match.h"
#define base_idx(X) ((X) & 0xffffff)
/** /**
* unpack_table - unpack a dfa table (one of accept, default, base, next check) * unpack_table - unpack a dfa table (one of accept, default, base, next check)
* @blob: data to unpack (NOT NULL) * @blob: data to unpack (NOT NULL)
@ -30,7 +32,7 @@
* *
* Returns: pointer to table else NULL on failure * Returns: pointer to table else NULL on failure
* *
* NOTE: must be freed by kvfree (not kmalloc) * NOTE: must be freed by kvfree (not kfree)
*/ */
static struct table_header *unpack_table(char *blob, size_t bsize) static struct table_header *unpack_table(char *blob, size_t bsize)
{ {
@ -57,7 +59,7 @@ static struct table_header *unpack_table(char *blob, size_t bsize)
if (bsize < tsize) if (bsize < tsize)
goto out; goto out;
table = kvmalloc(tsize); table = kvzalloc(tsize);
if (table) { if (table) {
*table = th; *table = th;
if (th.td_flags == YYTD_DATA8) if (th.td_flags == YYTD_DATA8)
@ -137,8 +139,7 @@ static int verify_dfa(struct aa_dfa *dfa, int flags)
for (i = 0; i < state_count; i++) { for (i = 0; i < state_count; i++) {
if (DEFAULT_TABLE(dfa)[i] >= state_count) if (DEFAULT_TABLE(dfa)[i] >= state_count)
goto out; goto out;
/* TODO: do check that DEF state recursion terminates */ if (base_idx(BASE_TABLE(dfa)[i]) + 255 >= trans_count) {
if (BASE_TABLE(dfa)[i] + 255 >= trans_count) {
printk(KERN_ERR "AppArmor DFA next/check upper " printk(KERN_ERR "AppArmor DFA next/check upper "
"bounds error\n"); "bounds error\n");
goto out; goto out;
@ -314,7 +315,7 @@ unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start,
u8 *equiv = EQUIV_TABLE(dfa); u8 *equiv = EQUIV_TABLE(dfa);
/* default is direct to next state */ /* default is direct to next state */
for (; len; len--) { for (; len; len--) {
pos = base[state] + equiv[(u8) *str++]; pos = base_idx(base[state]) + equiv[(u8) *str++];
if (check[pos] == state) if (check[pos] == state)
state = next[pos]; state = next[pos];
else else
@ -323,7 +324,7 @@ unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start,
} else { } else {
/* default is direct to next state */ /* default is direct to next state */
for (; len; len--) { for (; len; len--) {
pos = base[state] + (u8) *str++; pos = base_idx(base[state]) + (u8) *str++;
if (check[pos] == state) if (check[pos] == state)
state = next[pos]; state = next[pos];
else else
@ -364,7 +365,7 @@ unsigned int aa_dfa_match(struct aa_dfa *dfa, unsigned int start,
u8 *equiv = EQUIV_TABLE(dfa); u8 *equiv = EQUIV_TABLE(dfa);
/* default is direct to next state */ /* default is direct to next state */
while (*str) { while (*str) {
pos = base[state] + equiv[(u8) *str++]; pos = base_idx(base[state]) + equiv[(u8) *str++];
if (check[pos] == state) if (check[pos] == state)
state = next[pos]; state = next[pos];
else else
@ -373,7 +374,7 @@ unsigned int aa_dfa_match(struct aa_dfa *dfa, unsigned int start,
} else { } else {
/* default is direct to next state */ /* default is direct to next state */
while (*str) { while (*str) {
pos = base[state] + (u8) *str++; pos = base_idx(base[state]) + (u8) *str++;
if (check[pos] == state) if (check[pos] == state)
state = next[pos]; state = next[pos];
else else
@ -409,14 +410,14 @@ unsigned int aa_dfa_next(struct aa_dfa *dfa, unsigned int state,
u8 *equiv = EQUIV_TABLE(dfa); u8 *equiv = EQUIV_TABLE(dfa);
/* default is direct to next state */ /* default is direct to next state */
pos = base[state] + equiv[(u8) c]; pos = base_idx(base[state]) + equiv[(u8) c];
if (check[pos] == state) if (check[pos] == state)
state = next[pos]; state = next[pos];
else else
state = def[state]; state = def[state];
} else { } else {
/* default is direct to next state */ /* default is direct to next state */
pos = base[state] + (u8) c; pos = base_idx(base[state]) + (u8) c;
if (check[pos] == state) if (check[pos] == state)
state = next[pos]; state = next[pos];
else else

View File

@ -174,7 +174,7 @@ static int get_name_to_buffer(struct path *path, int flags, char *buffer,
if (info && error) { if (info && error) {
if (error == -ENOENT) if (error == -ENOENT)
*info = "Failed name lookup - deleted entry"; *info = "Failed name lookup - deleted entry";
else if (error == -ESTALE) else if (error == -EACCES)
*info = "Failed name lookup - disconnected path"; *info = "Failed name lookup - disconnected path";
else if (error == -ENAMETOOLONG) else if (error == -ENAMETOOLONG)
*info = "Failed name lookup - name too long"; *info = "Failed name lookup - name too long";

View File

@ -87,7 +87,6 @@
#include "include/policy.h" #include "include/policy.h"
#include "include/policy_unpack.h" #include "include/policy_unpack.h"
#include "include/resource.h" #include "include/resource.h"
#include "include/sid.h"
/* root profile namespace */ /* root profile namespace */
@ -292,7 +291,6 @@ static struct aa_namespace *alloc_namespace(const char *prefix,
if (!ns->unconfined) if (!ns->unconfined)
goto fail_unconfined; goto fail_unconfined;
ns->unconfined->sid = aa_alloc_sid();
ns->unconfined->flags = PFLAG_UNCONFINED | PFLAG_IX_ON_NAME_ERROR | ns->unconfined->flags = PFLAG_UNCONFINED | PFLAG_IX_ON_NAME_ERROR |
PFLAG_IMMUTABLE; PFLAG_IMMUTABLE;
@ -303,6 +301,8 @@ static struct aa_namespace *alloc_namespace(const char *prefix,
*/ */
ns->unconfined->ns = aa_get_namespace(ns); ns->unconfined->ns = aa_get_namespace(ns);
atomic_set(&ns->uniq_null, 0);
return ns; return ns;
fail_unconfined: fail_unconfined:
@ -497,7 +497,6 @@ static void __replace_profile(struct aa_profile *old, struct aa_profile *new)
/* released when @new is freed */ /* released when @new is freed */
new->parent = aa_get_profile(old->parent); new->parent = aa_get_profile(old->parent);
new->ns = aa_get_namespace(old->ns); new->ns = aa_get_namespace(old->ns);
new->sid = old->sid;
__list_add_profile(&policy->profiles, new); __list_add_profile(&policy->profiles, new);
/* inherit children */ /* inherit children */
list_for_each_entry_safe(child, tmp, &old->base.profiles, base.list) { list_for_each_entry_safe(child, tmp, &old->base.profiles, base.list) {
@ -635,83 +634,6 @@ void __init aa_free_root_ns(void)
aa_put_namespace(ns); aa_put_namespace(ns);
} }
/**
* aa_alloc_profile - allocate, initialize and return a new profile
* @hname: name of the profile (NOT NULL)
*
* Returns: refcount profile or NULL on failure
*/
struct aa_profile *aa_alloc_profile(const char *hname)
{
struct aa_profile *profile;
/* freed by free_profile - usually through aa_put_profile */
profile = kzalloc(sizeof(*profile), GFP_KERNEL);
if (!profile)
return NULL;
if (!policy_init(&profile->base, NULL, hname)) {
kzfree(profile);
return NULL;
}
/* refcount released by caller */
return profile;
}
/**
* aa_new_null_profile - create a new null-X learning profile
* @parent: profile that caused this profile to be created (NOT NULL)
* @hat: true if the null- learning profile is a hat
*
* Create a null- complain mode profile used in learning mode. The name of
* the profile is unique and follows the format of parent//null-sid.
*
* null profiles are added to the profile list but the list does not
* hold a count on them so that they are automatically released when
* not in use.
*
* Returns: new refcounted profile else NULL on failure
*/
struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat)
{
struct aa_profile *profile = NULL;
char *name;
u32 sid = aa_alloc_sid();
/* freed below */
name = kmalloc(strlen(parent->base.hname) + 2 + 7 + 8, GFP_KERNEL);
if (!name)
goto fail;
sprintf(name, "%s//null-%x", parent->base.hname, sid);
profile = aa_alloc_profile(name);
kfree(name);
if (!profile)
goto fail;
profile->sid = sid;
profile->mode = APPARMOR_COMPLAIN;
profile->flags = PFLAG_NULL;
if (hat)
profile->flags |= PFLAG_HAT;
/* released on free_profile */
profile->parent = aa_get_profile(parent);
profile->ns = aa_get_namespace(parent->ns);
write_lock(&profile->ns->lock);
__list_add_profile(&parent->base.profiles, profile);
write_unlock(&profile->ns->lock);
/* refcount released by caller */
return profile;
fail:
aa_free_sid(sid);
return NULL;
}
/** /**
* free_profile - free a profile * free_profile - free a profile
* @profile: the profile to free (MAYBE NULL) * @profile: the profile to free (MAYBE NULL)
@ -749,7 +671,6 @@ static void free_profile(struct aa_profile *profile)
aa_free_cap_rules(&profile->caps); aa_free_cap_rules(&profile->caps);
aa_free_rlimit_rules(&profile->rlimits); aa_free_rlimit_rules(&profile->rlimits);
aa_free_sid(profile->sid);
aa_put_dfa(profile->xmatch); aa_put_dfa(profile->xmatch);
aa_put_dfa(profile->policy.dfa); aa_put_dfa(profile->policy.dfa);
@ -790,6 +711,81 @@ void aa_free_profile_kref(struct kref *kref)
free_profile(p); free_profile(p);
} }
/**
* aa_alloc_profile - allocate, initialize and return a new profile
* @hname: name of the profile (NOT NULL)
*
* Returns: refcount profile or NULL on failure
*/
struct aa_profile *aa_alloc_profile(const char *hname)
{
struct aa_profile *profile;
/* freed by free_profile - usually through aa_put_profile */
profile = kzalloc(sizeof(*profile), GFP_KERNEL);
if (!profile)
return NULL;
if (!policy_init(&profile->base, NULL, hname)) {
kzfree(profile);
return NULL;
}
/* refcount released by caller */
return profile;
}
/**
* aa_new_null_profile - create a new null-X learning profile
* @parent: profile that caused this profile to be created (NOT NULL)
* @hat: true if the null- learning profile is a hat
*
* Create a null- complain mode profile used in learning mode. The name of
* the profile is unique and follows the format of parent//null-<uniq>.
*
* null profiles are added to the profile list but the list does not
* hold a count on them so that they are automatically released when
* not in use.
*
* Returns: new refcounted profile else NULL on failure
*/
struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat)
{
struct aa_profile *profile = NULL;
char *name;
int uniq = atomic_inc_return(&parent->ns->uniq_null);
/* freed below */
name = kmalloc(strlen(parent->base.hname) + 2 + 7 + 8, GFP_KERNEL);
if (!name)
goto fail;
sprintf(name, "%s//null-%x", parent->base.hname, uniq);
profile = aa_alloc_profile(name);
kfree(name);
if (!profile)
goto fail;
profile->mode = APPARMOR_COMPLAIN;
profile->flags = PFLAG_NULL;
if (hat)
profile->flags |= PFLAG_HAT;
/* released on free_profile */
profile->parent = aa_get_profile(parent);
profile->ns = aa_get_namespace(parent->ns);
write_lock(&profile->ns->lock);
__list_add_profile(&parent->base.profiles, profile);
write_unlock(&profile->ns->lock);
/* refcount released by caller */
return profile;
fail:
return NULL;
}
/* TODO: profile accounting - setup in remove */ /* TODO: profile accounting - setup in remove */
/** /**
@ -972,7 +968,6 @@ static void __add_new_profile(struct aa_namespace *ns, struct aa_policy *policy,
profile->parent = aa_get_profile((struct aa_profile *) policy); profile->parent = aa_get_profile((struct aa_profile *) policy);
__list_add_profile(&policy->profiles, profile); __list_add_profile(&policy->profiles, profile);
/* released on free_profile */ /* released on free_profile */
profile->sid = aa_alloc_sid();
profile->ns = aa_get_namespace(ns); profile->ns = aa_get_namespace(ns);
} }
@ -1110,14 +1105,8 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace)
if (!error) { if (!error) {
if (rename_profile) if (rename_profile)
__replace_profile(rename_profile, new_profile); __replace_profile(rename_profile, new_profile);
if (old_profile) { if (old_profile)
/* when there are both rename and old profiles
* inherit old profiles sid
*/
if (rename_profile)
aa_free_sid(new_profile->sid);
__replace_profile(old_profile, new_profile); __replace_profile(old_profile, new_profile);
}
if (!(old_profile || rename_profile)) if (!(old_profile || rename_profile))
__add_new_profile(ns, policy, new_profile); __add_new_profile(ns, policy, new_profile);
} }
@ -1167,14 +1156,12 @@ ssize_t aa_remove_profiles(char *fqname, size_t size)
if (fqname[0] == ':') { if (fqname[0] == ':') {
char *ns_name; char *ns_name;
name = aa_split_fqname(fqname, &ns_name); name = aa_split_fqname(fqname, &ns_name);
if (ns_name) { /* released below */
/* released below */ ns = aa_find_namespace(root, ns_name);
ns = aa_find_namespace(root, ns_name); if (!ns) {
if (!ns) { info = "namespace does not exist";
info = "namespace does not exist"; error = -ENOENT;
error = -ENOENT; goto fail;
goto fail;
}
} }
} else } else
/* released below */ /* released below */

View File

@ -27,7 +27,6 @@
#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"
#include "include/sid.h"
/* /*
* The AppArmor interface treats data as a type byte followed by the * The AppArmor interface treats data as a type byte followed by the
@ -290,6 +289,9 @@ static int unpack_strdup(struct aa_ext *e, char **string, const char *name)
return res; return res;
} }
#define DFA_VALID_PERM_MASK 0xffffffff
#define DFA_VALID_PERM2_MASK 0xffffffff
/** /**
* verify_accept - verify the accept tables of a dfa * verify_accept - verify the accept tables of a dfa
* @dfa: dfa to verify accept tables of (NOT NULL) * @dfa: dfa to verify accept tables of (NOT NULL)

View File

@ -163,9 +163,3 @@ int aa_setprocattr_changeprofile(char *fqname, bool onexec, int test)
name = aa_split_fqname(fqname, &ns_name); name = aa_split_fqname(fqname, &ns_name);
return aa_change_profile(ns_name, name, onexec, test); return aa_change_profile(ns_name, name, onexec, test);
} }
int aa_setprocattr_permipc(char *fqname)
{
/* TODO: add ipc permission querying */
return -ENOTSUPP;
}

View File

@ -15,6 +15,7 @@
#include <linux/audit.h> #include <linux/audit.h>
#include "include/audit.h" #include "include/audit.h"
#include "include/context.h"
#include "include/resource.h" #include "include/resource.h"
#include "include/policy.h" #include "include/policy.h"
@ -90,17 +91,25 @@ int aa_map_resource(int resource)
int aa_task_setrlimit(struct aa_profile *profile, struct task_struct *task, int aa_task_setrlimit(struct aa_profile *profile, struct task_struct *task,
unsigned int resource, struct rlimit *new_rlim) unsigned int resource, struct rlimit *new_rlim)
{ {
struct aa_profile *task_profile;
int error = 0; int error = 0;
rcu_read_lock();
task_profile = aa_get_profile(aa_cred_profile(__task_cred(task)));
rcu_read_unlock();
/* TODO: extend resource control to handle other (non current) /* TODO: extend resource control to handle other (non current)
* processes. AppArmor rules currently have the implicit assumption * profiles. AppArmor rules currently have the implicit assumption
* that the task is setting the resource of the current process * that the task is setting the resource of a task confined with
* the same profile.
*/ */
if ((task != current->group_leader) || if (profile != task_profile ||
(profile->rlimits.mask & (1 << resource) && (profile->rlimits.mask & (1 << resource) &&
new_rlim->rlim_max > profile->rlimits.limits[resource].rlim_max)) new_rlim->rlim_max > profile->rlimits.limits[resource].rlim_max))
error = -EACCES; error = -EACCES;
aa_put_profile(task_profile);
return audit_resource(profile, resource, new_rlim->rlim_max, error); return audit_resource(profile, resource, new_rlim->rlim_max, error);
} }

View File

@ -17,6 +17,21 @@ config INTEGRITY_SIGNATURE
This is useful for evm and module keyrings, when keys are This is useful for evm and module keyrings, when keys are
usually only added from initramfs. usually only added from initramfs.
config INTEGRITY_AUDIT
bool "Enables integrity auditing support "
depends on INTEGRITY && AUDIT
default y
help
In addition to enabling integrity auditing support, this
option adds a kernel parameter 'integrity_audit', which
controls the level of integrity auditing messages.
0 - basic integrity auditing messages (default)
1 - additional integrity auditing messages
Additional informational integrity auditing messages would
be enabled by specifying 'integrity_audit=1' on the kernel
command line.
config INTEGRITY_ASYMMETRIC_KEYS config INTEGRITY_ASYMMETRIC_KEYS
boolean "Enable asymmetric keys support" boolean "Enable asymmetric keys support"
depends on INTEGRITY_SIGNATURE depends on INTEGRITY_SIGNATURE

View File

@ -3,6 +3,7 @@
# #
obj-$(CONFIG_INTEGRITY) += integrity.o obj-$(CONFIG_INTEGRITY) += integrity.o
obj-$(CONFIG_INTEGRITY_AUDIT) += integrity_audit.o
obj-$(CONFIG_INTEGRITY_SIGNATURE) += digsig.o obj-$(CONFIG_INTEGRITY_SIGNATURE) += digsig.o
obj-$(CONFIG_INTEGRITY_ASYMMETRIC_KEYS) += digsig_asymmetric.o obj-$(CONFIG_INTEGRITY_ASYMMETRIC_KEYS) += digsig_asymmetric.o

View File

@ -16,6 +16,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/crypto.h> #include <linux/crypto.h>
#include <linux/audit.h>
#include <linux/xattr.h> #include <linux/xattr.h>
#include <linux/integrity.h> #include <linux/integrity.h>
#include <linux/evm.h> #include <linux/evm.h>
@ -24,6 +25,9 @@
int evm_initialized; int evm_initialized;
static char *integrity_status_msg[] = {
"pass", "fail", "no_label", "no_xattrs", "unknown"
};
char *evm_hmac = "hmac(sha1)"; char *evm_hmac = "hmac(sha1)";
char *evm_hash = "sha1"; char *evm_hash = "sha1";
int evm_hmac_version = CONFIG_EVM_HMAC_VERSION; int evm_hmac_version = CONFIG_EVM_HMAC_VERSION;
@ -262,9 +266,15 @@ static int evm_protect_xattr(struct dentry *dentry, const char *xattr_name,
if ((evm_status == INTEGRITY_PASS) || if ((evm_status == INTEGRITY_PASS) ||
(evm_status == INTEGRITY_NOXATTRS)) (evm_status == INTEGRITY_NOXATTRS))
return 0; return 0;
return -EPERM; goto out;
} }
evm_status = evm_verify_current_integrity(dentry); evm_status = evm_verify_current_integrity(dentry);
out:
if (evm_status != INTEGRITY_PASS)
integrity_audit_msg(AUDIT_INTEGRITY_METADATA, dentry->d_inode,
dentry->d_name.name, "appraise_metadata",
integrity_status_msg[evm_status],
-EPERM, 0);
return evm_status == INTEGRITY_PASS ? 0 : -EPERM; return evm_status == INTEGRITY_PASS ? 0 : -EPERM;
} }
@ -357,6 +367,9 @@ int evm_inode_setattr(struct dentry *dentry, struct iattr *attr)
if ((evm_status == INTEGRITY_PASS) || if ((evm_status == INTEGRITY_PASS) ||
(evm_status == INTEGRITY_NOXATTRS)) (evm_status == INTEGRITY_NOXATTRS))
return 0; return 0;
integrity_audit_msg(AUDIT_INTEGRITY_METADATA, dentry->d_inode,
dentry->d_name.name, "appraise_metadata",
integrity_status_msg[evm_status], -EPERM, 0);
return -EPERM; return -EPERM;
} }

View File

@ -38,18 +38,6 @@ config IMA_MEASURE_PCR_IDX
that IMA uses to maintain the integrity aggregate of the that IMA uses to maintain the integrity aggregate of the
measurement list. If unsure, use the default 10. measurement list. If unsure, use the default 10.
config IMA_AUDIT
bool "Enables auditing support"
depends on IMA
depends on AUDIT
default y
help
This option adds a kernel parameter 'ima_audit', which
allows informational auditing messages to be enabled
at boot. If this option is selected, informational integrity
auditing messages can be enabled with 'ima_audit=1' on
the kernel command line.
config IMA_LSM_RULES config IMA_LSM_RULES
bool bool
depends on IMA && AUDIT && (SECURITY_SELINUX || SECURITY_SMACK) depends on IMA && AUDIT && (SECURITY_SELINUX || SECURITY_SMACK)

View File

@ -7,5 +7,4 @@ obj-$(CONFIG_IMA) += ima.o
ima-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \ ima-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \
ima_policy.o ima_policy.o
ima-$(CONFIG_IMA_AUDIT) += ima_audit.o
ima-$(CONFIG_IMA_APPRAISE) += ima_appraise.o ima-$(CONFIG_IMA_APPRAISE) += ima_appraise.o

View File

@ -62,20 +62,6 @@ struct ima_queue_entry {
}; };
extern struct list_head ima_measurements; /* list of all measurements */ extern struct list_head ima_measurements; /* list of all measurements */
#ifdef CONFIG_IMA_AUDIT
/* declarations */
void integrity_audit_msg(int audit_msgno, struct inode *inode,
const unsigned char *fname, const char *op,
const char *cause, int result, int info);
#else
static inline void integrity_audit_msg(int audit_msgno, struct inode *inode,
const unsigned char *fname,
const char *op, const char *cause,
int result, int info)
{
}
#endif
/* Internal IMA function definitions */ /* Internal IMA function definitions */
int ima_init(void); int ima_init(void);
void ima_cleanup(void); void ima_cleanup(void);

View File

@ -113,5 +113,19 @@ static inline int asymmetric_verify(struct key *keyring, const char *sig,
} }
#endif #endif
#ifdef CONFIG_INTEGRITY_AUDIT
/* declarations */
void integrity_audit_msg(int audit_msgno, struct inode *inode,
const unsigned char *fname, const char *op,
const char *cause, int result, int info);
#else
static inline void integrity_audit_msg(int audit_msgno, struct inode *inode,
const unsigned char *fname,
const char *op, const char *cause,
int result, int info)
{
}
#endif
/* set during initialization */ /* set during initialization */
extern int iint_initialized; extern int iint_initialized;

View File

@ -13,20 +13,20 @@
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/gfp.h> #include <linux/gfp.h>
#include <linux/audit.h> #include <linux/audit.h>
#include "ima.h" #include "integrity.h"
static int ima_audit; static int integrity_audit_info;
/* ima_audit_setup - enable informational auditing messages */ /* ima_audit_setup - enable informational auditing messages */
static int __init ima_audit_setup(char *str) static int __init integrity_audit_setup(char *str)
{ {
unsigned long audit; unsigned long audit;
if (!strict_strtoul(str, 0, &audit)) if (!strict_strtoul(str, 0, &audit))
ima_audit = audit ? 1 : 0; integrity_audit_info = audit ? 1 : 0;
return 1; return 1;
} }
__setup("ima_audit=", ima_audit_setup); __setup("integrity_audit=", integrity_audit_setup);
void integrity_audit_msg(int audit_msgno, struct inode *inode, void integrity_audit_msg(int audit_msgno, struct inode *inode,
const unsigned char *fname, const char *op, const unsigned char *fname, const char *op,
@ -34,7 +34,7 @@ void integrity_audit_msg(int audit_msgno, struct inode *inode,
{ {
struct audit_buffer *ab; struct audit_buffer *ab;
if (!ima_audit && audit_info == 1) /* Skip informational messages */ if (!integrity_audit_info && audit_info == 1) /* Skip info messages */
return; return;
ab = audit_log_start(current->audit_context, GFP_KERNEL, audit_msgno); ab = audit_log_start(current->audit_context, GFP_KERNEL, audit_msgno);

View File

@ -28,71 +28,6 @@
#define SMK_LABELLEN 24 #define SMK_LABELLEN 24
#define SMK_LONGLABEL 256 #define SMK_LONGLABEL 256
/*
* Maximum number of bytes for the levels in a CIPSO IP option.
* Why 23? CIPSO is constrained to 30, so a 32 byte buffer is
* bigger than can be used, and 24 is the next lower multiple
* of 8, and there are too many issues if there isn't space set
* aside for the terminating null byte.
*/
#define SMK_CIPSOLEN 24
struct superblock_smack {
char *smk_root;
char *smk_floor;
char *smk_hat;
char *smk_default;
int smk_initialized;
};
struct socket_smack {
char *smk_out; /* outbound label */
char *smk_in; /* inbound label */
char *smk_packet; /* TCP peer label */
};
/*
* Inode smack data
*/
struct inode_smack {
char *smk_inode; /* label of the fso */
char *smk_task; /* label of the task */
char *smk_mmap; /* label of the mmap domain */
struct mutex smk_lock; /* initialization lock */
int smk_flags; /* smack inode flags */
};
struct task_smack {
char *smk_task; /* label for access control */
char *smk_forked; /* label when forked */
struct list_head smk_rules; /* per task access rules */
struct mutex smk_rules_lock; /* lock for the rules */
};
#define SMK_INODE_INSTANT 0x01 /* inode is instantiated */
#define SMK_INODE_TRANSMUTE 0x02 /* directory is transmuting */
#define SMK_INODE_CHANGED 0x04 /* smack was transmuted */
/*
* A label access rule.
*/
struct smack_rule {
struct list_head list;
char *smk_subject;
char *smk_object;
int smk_access;
};
/*
* An entry in the table identifying hosts.
*/
struct smk_netlbladdr {
struct list_head list;
struct sockaddr_in smk_host; /* network address */
struct in_addr smk_mask; /* network mask */
char *smk_label; /* label */
};
/* /*
* This is the repository for labels seen so that it is * This is the repository for labels seen so that it is
* not necessary to keep allocating tiny chuncks of memory * not necessary to keep allocating tiny chuncks of memory
@ -125,6 +60,82 @@ struct smack_known {
struct mutex smk_rules_lock; /* lock for rules */ struct mutex smk_rules_lock; /* lock for rules */
}; };
/*
* Maximum number of bytes for the levels in a CIPSO IP option.
* Why 23? CIPSO is constrained to 30, so a 32 byte buffer is
* bigger than can be used, and 24 is the next lower multiple
* of 8, and there are too many issues if there isn't space set
* aside for the terminating null byte.
*/
#define SMK_CIPSOLEN 24
struct superblock_smack {
char *smk_root;
char *smk_floor;
char *smk_hat;
char *smk_default;
int smk_initialized;
};
struct socket_smack {
struct smack_known *smk_out; /* outbound label */
char *smk_in; /* inbound label */
char *smk_packet; /* TCP peer label */
};
/*
* Inode smack data
*/
struct inode_smack {
char *smk_inode; /* label of the fso */
struct smack_known *smk_task; /* label of the task */
struct smack_known *smk_mmap; /* label of the mmap domain */
struct mutex smk_lock; /* initialization lock */
int smk_flags; /* smack inode flags */
};
struct task_smack {
struct smack_known *smk_task; /* label for access control */
struct smack_known *smk_forked; /* label when forked */
struct list_head smk_rules; /* per task access rules */
struct mutex smk_rules_lock; /* lock for the rules */
};
#define SMK_INODE_INSTANT 0x01 /* inode is instantiated */
#define SMK_INODE_TRANSMUTE 0x02 /* directory is transmuting */
#define SMK_INODE_CHANGED 0x04 /* smack was transmuted */
/*
* A label access rule.
*/
struct smack_rule {
struct list_head list;
struct smack_known *smk_subject;
char *smk_object;
int smk_access;
};
/*
* An entry in the table identifying hosts.
*/
struct smk_netlbladdr {
struct list_head list;
struct sockaddr_in smk_host; /* network address */
struct in_addr smk_mask; /* network mask */
char *smk_label; /* label */
};
/*
* An entry in the table identifying ports.
*/
struct smk_port_label {
struct list_head list;
struct sock *smk_sock; /* socket initialized on */
unsigned short smk_port; /* the port number */
char *smk_in; /* incoming label */
struct smack_known *smk_out; /* outgoing label */
};
/* /*
* Mount options * Mount options
*/ */
@ -132,6 +143,7 @@ struct smack_known {
#define SMK_FSFLOOR "smackfsfloor=" #define SMK_FSFLOOR "smackfsfloor="
#define SMK_FSHAT "smackfshat=" #define SMK_FSHAT "smackfshat="
#define SMK_FSROOT "smackfsroot=" #define SMK_FSROOT "smackfsroot="
#define SMK_FSTRANS "smackfstransmute="
#define SMACK_CIPSO_OPTION "-CIPSO" #define SMACK_CIPSO_OPTION "-CIPSO"
@ -203,9 +215,9 @@ struct inode_smack *new_inode_smack(char *);
* These functions are in smack_access.c * These functions are in smack_access.c
*/ */
int smk_access_entry(char *, char *, struct list_head *); int smk_access_entry(char *, char *, struct list_head *);
int smk_access(char *, char *, int, struct smk_audit_info *); int smk_access(struct smack_known *, char *, int, struct smk_audit_info *);
int smk_curacc(char *, u32, struct smk_audit_info *); int smk_curacc(char *, u32, struct smk_audit_info *);
char *smack_from_secid(const u32); struct smack_known *smack_from_secid(const u32);
char *smk_parse_smack(const char *string, int len); 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);
@ -218,7 +230,7 @@ u32 smack_to_secid(const char *);
*/ */
extern int smack_cipso_direct; extern int smack_cipso_direct;
extern int smack_cipso_mapped; extern int smack_cipso_mapped;
extern char *smack_net_ambient; extern struct smack_known *smack_net_ambient;
extern char *smack_onlycap; extern char *smack_onlycap;
extern const char *smack_cipso_option; extern const char *smack_cipso_option;
@ -254,17 +266,17 @@ static inline char *smk_of_inode(const struct inode *isp)
} }
/* /*
* Present a pointer to the smack label in an task blob. * Present a pointer to the smack label entry in an task blob.
*/ */
static inline char *smk_of_task(const struct task_smack *tsp) static inline struct smack_known *smk_of_task(const struct task_smack *tsp)
{ {
return tsp->smk_task; return tsp->smk_task;
} }
/* /*
* Present a pointer to the forked smack label in an task blob. * Present a pointer to the forked smack label entry in an task blob.
*/ */
static inline char *smk_of_forked(const struct task_smack *tsp) static inline struct smack_known *smk_of_forked(const struct task_smack *tsp)
{ {
return tsp->smk_forked; return tsp->smk_forked;
} }
@ -272,7 +284,7 @@ static inline char *smk_of_forked(const struct task_smack *tsp)
/* /*
* Present a pointer to the smack label in the current task blob. * Present a pointer to the smack label in the current task blob.
*/ */
static inline char *smk_of_current(void) static inline struct smack_known *smk_of_current(void)
{ {
return smk_of_task(current_security()); return smk_of_task(current_security());
} }
@ -283,9 +295,11 @@ static inline char *smk_of_current(void)
*/ */
static inline int smack_privileged(int cap) static inline int smack_privileged(int cap)
{ {
struct smack_known *skp = smk_of_current();
if (!capable(cap)) if (!capable(cap))
return 0; return 0;
if (smack_onlycap == NULL || smack_onlycap == smk_of_current()) if (smack_onlycap == NULL || smack_onlycap == skp->smk_known)
return 1; return 1;
return 0; return 0;
} }

View File

@ -93,7 +93,7 @@ int smk_access_entry(char *subject_label, char *object_label,
list_for_each_entry_rcu(srp, rule_list, list) { list_for_each_entry_rcu(srp, rule_list, list) {
if (srp->smk_object == object_label && if (srp->smk_object == object_label &&
srp->smk_subject == subject_label) { srp->smk_subject->smk_known == subject_label) {
may = srp->smk_access; may = srp->smk_access;
break; break;
} }
@ -104,7 +104,7 @@ int smk_access_entry(char *subject_label, char *object_label,
/** /**
* smk_access - determine if a subject has a specific access to an object * smk_access - determine if a subject has a specific access to an object
* @subject_label: a pointer to the subject's Smack label * @subject_known: a pointer to the subject's Smack label entry
* @object_label: a pointer to the object's Smack label * @object_label: a pointer to the object's Smack label
* @request: the access requested, in "MAY" format * @request: the access requested, in "MAY" format
* @a : a pointer to the audit data * @a : a pointer to the audit data
@ -115,10 +115,9 @@ int smk_access_entry(char *subject_label, char *object_label,
* *
* Smack labels are shared on smack_list * Smack labels are shared on smack_list
*/ */
int smk_access(char *subject_label, char *object_label, int request, int smk_access(struct smack_known *subject_known, char *object_label,
struct smk_audit_info *a) int request, struct smk_audit_info *a)
{ {
struct smack_known *skp;
int may = MAY_NOT; int may = MAY_NOT;
int rc = 0; int rc = 0;
@ -127,7 +126,7 @@ int smk_access(char *subject_label, char *object_label, int request,
* *
* A star subject can't access any object. * A star subject can't access any object.
*/ */
if (subject_label == smack_known_star.smk_known) { if (subject_known == &smack_known_star) {
rc = -EACCES; rc = -EACCES;
goto out_audit; goto out_audit;
} }
@ -137,7 +136,7 @@ int smk_access(char *subject_label, char *object_label, int request,
* An internet subject can access any object. * An internet subject can access any object.
*/ */
if (object_label == smack_known_web.smk_known || if (object_label == smack_known_web.smk_known ||
subject_label == smack_known_web.smk_known) subject_known == &smack_known_web)
goto out_audit; goto out_audit;
/* /*
* A star object can be accessed by any subject. * A star object can be accessed by any subject.
@ -148,7 +147,7 @@ int smk_access(char *subject_label, char *object_label, int request,
* An object can be accessed in any way by a subject * An object can be accessed in any way by a subject
* with the same label. * with the same label.
*/ */
if (subject_label == object_label) if (subject_known->smk_known == object_label)
goto out_audit; goto out_audit;
/* /*
* A hat subject can read any object. * A hat subject can read any object.
@ -157,7 +156,7 @@ int smk_access(char *subject_label, char *object_label, int request,
if ((request & MAY_ANYREAD) == request) { if ((request & MAY_ANYREAD) == request) {
if (object_label == smack_known_floor.smk_known) if (object_label == smack_known_floor.smk_known)
goto out_audit; goto out_audit;
if (subject_label == smack_known_hat.smk_known) if (subject_known == &smack_known_hat)
goto out_audit; goto out_audit;
} }
/* /*
@ -167,9 +166,9 @@ int smk_access(char *subject_label, char *object_label, int request,
* good. A negative response from smk_access_entry() * good. A negative response from smk_access_entry()
* indicates there is no entry for this pair. * indicates there is no entry for this pair.
*/ */
skp = smk_find_entry(subject_label);
rcu_read_lock(); rcu_read_lock();
may = smk_access_entry(subject_label, object_label, &skp->smk_rules); may = smk_access_entry(subject_known->smk_known, object_label,
&subject_known->smk_rules);
rcu_read_unlock(); rcu_read_unlock();
if (may > 0 && (request & may) == request) if (may > 0 && (request & may) == request)
@ -179,7 +178,8 @@ int smk_access(char *subject_label, char *object_label, int request,
out_audit: out_audit:
#ifdef CONFIG_AUDIT #ifdef CONFIG_AUDIT
if (a) if (a)
smack_log(subject_label, object_label, request, rc, a); smack_log(subject_known->smk_known, object_label, request,
rc, a);
#endif #endif
return rc; return rc;
} }
@ -198,20 +198,21 @@ int smk_access(char *subject_label, char *object_label, int request,
int smk_curacc(char *obj_label, u32 mode, struct smk_audit_info *a) int smk_curacc(char *obj_label, u32 mode, struct smk_audit_info *a)
{ {
struct task_smack *tsp = current_security(); struct task_smack *tsp = current_security();
char *sp = smk_of_task(tsp); struct smack_known *skp = smk_of_task(tsp);
int may; int may;
int rc; int rc;
/* /*
* Check the global rule list * Check the global rule list
*/ */
rc = smk_access(sp, obj_label, mode, NULL); rc = smk_access(skp, obj_label, mode, NULL);
if (rc == 0) { if (rc == 0) {
/* /*
* If there is an entry in the task's rule list * If there is an entry in the task's rule list
* it can further restrict access. * it can further restrict access.
*/ */
may = smk_access_entry(sp, obj_label, &tsp->smk_rules); may = smk_access_entry(skp->smk_known, obj_label,
&tsp->smk_rules);
if (may < 0) if (may < 0)
goto out_audit; goto out_audit;
if ((mode & may) == mode) if ((mode & may) == mode)
@ -228,7 +229,7 @@ int smk_curacc(char *obj_label, u32 mode, struct smk_audit_info *a)
out_audit: out_audit:
#ifdef CONFIG_AUDIT #ifdef CONFIG_AUDIT
if (a) if (a)
smack_log(sp, obj_label, mode, rc, a); smack_log(skp->smk_known, obj_label, mode, rc, a);
#endif #endif
return rc; return rc;
} }
@ -402,6 +403,8 @@ int smk_netlbl_mls(int level, char *catset, struct netlbl_lsm_secattr *sap,
sap->flags |= NETLBL_SECATTR_MLS_CAT; sap->flags |= NETLBL_SECATTR_MLS_CAT;
sap->attr.mls.lvl = level; sap->attr.mls.lvl = level;
sap->attr.mls.cat = netlbl_secattr_catmap_alloc(GFP_ATOMIC); sap->attr.mls.cat = netlbl_secattr_catmap_alloc(GFP_ATOMIC);
if (!sap->attr.mls.cat)
return -ENOMEM;
sap->attr.mls.cat->startbit = 0; sap->attr.mls.cat->startbit = 0;
for (cat = 1, cp = catset, byte = 0; byte < len; cp++, byte++) for (cat = 1, cp = catset, byte = 0; byte < len; cp++, byte++)
@ -513,10 +516,10 @@ char *smk_import(const char *string, int len)
* smack_from_secid - find the Smack label associated with a secid * smack_from_secid - find the Smack label associated with a secid
* @secid: an integer that might be associated with a Smack label * @secid: an integer that might be associated with a Smack label
* *
* Returns a pointer to the appropriate Smack label if there is one, * Returns a pointer to the appropriate Smack label entry if there is one,
* otherwise a pointer to the invalid Smack label. * otherwise a pointer to the invalid Smack label.
*/ */
char *smack_from_secid(const u32 secid) struct smack_known *smack_from_secid(const u32 secid)
{ {
struct smack_known *skp; struct smack_known *skp;
@ -524,7 +527,7 @@ char *smack_from_secid(const u32 secid)
list_for_each_entry_rcu(skp, &smack_known_list, list) { list_for_each_entry_rcu(skp, &smack_known_list, list) {
if (skp->smk_secid == secid) { if (skp->smk_secid == secid) {
rcu_read_unlock(); rcu_read_unlock();
return skp->smk_known; return skp;
} }
} }
@ -533,7 +536,7 @@ char *smack_from_secid(const u32 secid)
* of a secid that is not on the list. * of a secid that is not on the list.
*/ */
rcu_read_unlock(); rcu_read_unlock();
return smack_known_invalid.smk_known; return &smack_known_invalid;
} }
/** /**

File diff suppressed because it is too large Load Diff

View File

@ -66,7 +66,7 @@ static DEFINE_MUTEX(smk_netlbladdr_lock);
* If it isn't somehow marked, use this. * If it isn't somehow marked, use this.
* It can be reset via smackfs/ambient * It can be reset via smackfs/ambient
*/ */
char *smack_net_ambient; struct smack_known *smack_net_ambient;
/* /*
* This is the level in a CIPSO header that indicates a * This is the level in a CIPSO header that indicates a
@ -112,7 +112,7 @@ struct smack_master_list {
LIST_HEAD(smack_rule_list); LIST_HEAD(smack_rule_list);
struct smack_parsed_rule { struct smack_parsed_rule {
char *smk_subject; struct smack_known *smk_subject;
char *smk_object; char *smk_object;
int smk_access1; int smk_access1;
int smk_access2; int smk_access2;
@ -163,9 +163,11 @@ static inline void smack_catset_bit(unsigned int cat, char *catsetp)
*/ */
static void smk_netlabel_audit_set(struct netlbl_audit *nap) static void smk_netlabel_audit_set(struct netlbl_audit *nap)
{ {
struct smack_known *skp = smk_of_current();
nap->loginuid = audit_get_loginuid(current); nap->loginuid = audit_get_loginuid(current);
nap->sessionid = audit_get_sessionid(current); nap->sessionid = audit_get_sessionid(current);
nap->secid = smack_to_secid(smk_of_current()); nap->secid = skp->smk_secid;
} }
/* /*
@ -306,7 +308,7 @@ static int smk_fill_rule(const char *subject, const char *object,
struct smack_known *skp; struct smack_known *skp;
if (import) { if (import) {
rule->smk_subject = smk_import(subject, len); rule->smk_subject = smk_import_entry(subject, len);
if (rule->smk_subject == NULL) if (rule->smk_subject == NULL)
return -1; return -1;
@ -321,7 +323,7 @@ static int smk_fill_rule(const char *subject, const char *object,
kfree(cp); kfree(cp);
if (skp == NULL) if (skp == NULL)
return -1; return -1;
rule->smk_subject = skp->smk_known; rule->smk_subject = skp;
cp = smk_parse_smack(object, len); cp = smk_parse_smack(object, len);
if (cp == NULL) if (cp == NULL)
@ -445,7 +447,6 @@ 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_known *skp;
struct smack_parsed_rule *rule; struct smack_parsed_rule *rule;
char *data; char *data;
int datalen; int datalen;
@ -505,12 +506,10 @@ static ssize_t smk_write_rules_list(struct file *file, const char __user *buf,
goto out_free_rule; goto out_free_rule;
} }
if (rule_list == NULL) { if (rule_list == NULL) {
load = 1; load = 1;
skp = smk_find_entry(rule->smk_subject); rule_list = &rule->smk_subject->smk_rules;
rule_list = &skp->smk_rules; rule_lock = &rule->smk_subject->smk_rules_lock;
rule_lock = &skp->smk_rules_lock;
} }
rc = smk_set_access(rule, rule_list, rule_lock, load); rc = smk_set_access(rule, rule_list, rule_lock, load);
@ -579,13 +578,14 @@ static void smk_rule_show(struct seq_file *s, struct smack_rule *srp, int max)
* because you should expect to be able to write * because you should expect to be able to write
* anything you read back. * anything you read back.
*/ */
if (strlen(srp->smk_subject) >= max || strlen(srp->smk_object) >= max) if (strlen(srp->smk_subject->smk_known) >= max ||
strlen(srp->smk_object) >= max)
return; return;
if (srp->smk_access == 0) if (srp->smk_access == 0)
return; return;
seq_printf(s, "%s %s", srp->smk_subject, srp->smk_object); seq_printf(s, "%s %s", srp->smk_subject->smk_known, srp->smk_object);
seq_putc(s, ' '); seq_putc(s, ' ');
@ -738,9 +738,9 @@ static void smk_unlbl_ambient(char *oldambient)
__func__, __LINE__, rc); __func__, __LINE__, rc);
} }
if (smack_net_ambient == NULL) if (smack_net_ambient == NULL)
smack_net_ambient = smack_known_floor.smk_known; smack_net_ambient = &smack_known_floor;
rc = netlbl_cfg_unlbl_map_add(smack_net_ambient, PF_INET, rc = netlbl_cfg_unlbl_map_add(smack_net_ambient->smk_known, PF_INET,
NULL, NULL, &nai); NULL, NULL, &nai);
if (rc != 0) if (rc != 0)
printk(KERN_WARNING "%s:%d add rc = %d\n", printk(KERN_WARNING "%s:%d add rc = %d\n",
@ -881,7 +881,7 @@ static ssize_t smk_set_cipso(struct file *file, const char __user *buf,
if (format == SMK_FIXED24_FMT) if (format == SMK_FIXED24_FMT)
rule += SMK_LABELLEN; rule += SMK_LABELLEN;
else else
rule += strlen(skp->smk_known); rule += strlen(skp->smk_known) + 1;
ret = sscanf(rule, "%d", &maplevel); ret = sscanf(rule, "%d", &maplevel);
if (ret != 1 || maplevel > SMACK_CIPSO_MAXLEVEL) if (ret != 1 || maplevel > SMACK_CIPSO_MAXLEVEL)
@ -1535,11 +1535,12 @@ static ssize_t smk_read_ambient(struct file *filp, char __user *buf,
*/ */
mutex_lock(&smack_ambient_lock); mutex_lock(&smack_ambient_lock);
asize = strlen(smack_net_ambient) + 1; asize = strlen(smack_net_ambient->smk_known) + 1;
if (cn >= asize) if (cn >= asize)
rc = simple_read_from_buffer(buf, cn, ppos, rc = simple_read_from_buffer(buf, cn, ppos,
smack_net_ambient, asize); smack_net_ambient->smk_known,
asize);
else else
rc = -EINVAL; rc = -EINVAL;
@ -1560,8 +1561,8 @@ static ssize_t smk_read_ambient(struct file *filp, char __user *buf,
static ssize_t smk_write_ambient(struct file *file, const char __user *buf, static ssize_t smk_write_ambient(struct file *file, const char __user *buf,
size_t count, loff_t *ppos) size_t count, loff_t *ppos)
{ {
struct smack_known *skp;
char *oldambient; char *oldambient;
char *smack = NULL;
char *data; char *data;
int rc = count; int rc = count;
@ -1577,16 +1578,16 @@ static ssize_t smk_write_ambient(struct file *file, const char __user *buf,
goto out; goto out;
} }
smack = smk_import(data, count); skp = smk_import_entry(data, count);
if (smack == NULL) { if (skp == NULL) {
rc = -EINVAL; rc = -EINVAL;
goto out; goto out;
} }
mutex_lock(&smack_ambient_lock); mutex_lock(&smack_ambient_lock);
oldambient = smack_net_ambient; oldambient = smack_net_ambient->smk_known;
smack_net_ambient = smack; smack_net_ambient = skp;
smk_unlbl_ambient(oldambient); smk_unlbl_ambient(oldambient);
mutex_unlock(&smack_ambient_lock); mutex_unlock(&smack_ambient_lock);
@ -1645,7 +1646,7 @@ static ssize_t smk_write_onlycap(struct file *file, const char __user *buf,
size_t count, loff_t *ppos) size_t count, loff_t *ppos)
{ {
char *data; char *data;
char *sp = smk_of_task(current->cred->security); struct smack_known *skp = smk_of_task(current->cred->security);
int rc = count; int rc = count;
if (!smack_privileged(CAP_MAC_ADMIN)) if (!smack_privileged(CAP_MAC_ADMIN))
@ -1656,7 +1657,7 @@ static ssize_t smk_write_onlycap(struct file *file, const char __user *buf,
* explicitly for clarity. The smk_access() implementation * explicitly for clarity. The smk_access() implementation
* would use smk_access(smack_onlycap, MAY_WRITE) * would use smk_access(smack_onlycap, MAY_WRITE)
*/ */
if (smack_onlycap != NULL && smack_onlycap != sp) if (smack_onlycap != NULL && smack_onlycap != skp->smk_known)
return -EPERM; return -EPERM;
data = kzalloc(count, GFP_KERNEL); data = kzalloc(count, GFP_KERNEL);
@ -1866,8 +1867,8 @@ static ssize_t smk_user_access(struct file *file, const char __user *buf,
if (res) if (res)
return -EINVAL; return -EINVAL;
res = smk_access(rule.smk_subject, rule.smk_object, rule.smk_access1, res = smk_access(rule.smk_subject, rule.smk_object,
NULL); rule.smk_access1, NULL);
data[0] = res == 0 ? '1' : '0'; data[0] = res == 0 ? '1' : '0';
data[1] = '\0'; data[1] = '\0';