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

Pull security layer updates from James Morris:
 "Highlights:

   - major AppArmor update: policy namespaces & lots of fixes

   - add /sys/kernel/security/lsm node for easy detection of loaded LSMs

   - SELinux cgroupfs labeling support

   - SELinux context mounts on tmpfs, ramfs, devpts within user
     namespaces

   - improved TPM 2.0 support"

* 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security: (117 commits)
  tpm: declare tpm2_get_pcr_allocation() as static
  tpm: Fix expected number of response bytes of TPM1.2 PCR Extend
  tpm xen: drop unneeded chip variable
  tpm: fix misspelled "facilitate" in module parameter description
  tpm_tis: fix the error handling of init_tis()
  KEYS: Use memzero_explicit() for secret data
  KEYS: Fix an error code in request_master_key()
  sign-file: fix build error in sign-file.c with libressl
  selinux: allow changing labels for cgroupfs
  selinux: fix off-by-one in setprocattr
  tpm: silence an array overflow warning
  tpm: fix the type of owned field in cap_t
  tpm: add securityfs support for TPM 2.0 firmware event log
  tpm: enhance read_log_of() to support Physical TPM event log
  tpm: enhance TPM 2.0 PCR extend to support multiple banks
  tpm: implement TPM 2.0 capability to get active PCR banks
  tpm: fix RC value check in tpm2_seal_trusted
  tpm_tis: fix iTPM probe via probe_itpm() function
  tpm: Begin the process to deprecate user_read_timer
  tpm: remove tpm_read_index and tpm_write_index from tpm.h
  ...
This commit is contained in:
Linus Torvalds 2017-02-21 12:49:56 -08:00
commit c9341ee0af
86 changed files with 4281 additions and 2255 deletions

View File

@ -22,6 +22,13 @@ system, building their checks on top of the defined capability hooks.
For more details on capabilities, see capabilities(7) in the Linux For more details on capabilities, see capabilities(7) in the Linux
man-pages project. man-pages project.
A list of the active security modules can be found by reading
/sys/kernel/security/lsm. This is a comma separated list, and
will always include the capability module. The list reflects the
order in which checks are made. The capability module will always
be first, followed by any "minor" modules (e.g. Yama) and then
the one "major" module (e.g. SELinux) if there is one configured.
Based on https://lkml.org/lkml/2007/10/26/215, Based on https://lkml.org/lkml/2007/10/26/215,
a new LSM is accepted into the kernel when its intent (a description of a new LSM is accepted into the kernel when its intent (a description of
what it tries to protect against and in what cases one would expect to what it tries to protect against and in what cases one would expect to

View File

@ -6,6 +6,7 @@ menuconfig TCG_TPM
tristate "TPM Hardware Support" tristate "TPM Hardware Support"
depends on HAS_IOMEM depends on HAS_IOMEM
select SECURITYFS select SECURITYFS
select CRYPTO_HASH_INFO
---help--- ---help---
If you have a TPM security chip in your system, which If you have a TPM security chip in your system, which
implements the Trusted Computing Group's specification, implements the Trusted Computing Group's specification,

View File

@ -3,7 +3,7 @@
# #
obj-$(CONFIG_TCG_TPM) += tpm.o obj-$(CONFIG_TCG_TPM) += tpm.o
tpm-y := tpm-interface.o tpm-dev.o tpm-sysfs.o tpm-chip.o tpm2-cmd.o \ tpm-y := tpm-interface.o tpm-dev.o tpm-sysfs.o tpm-chip.o tpm2-cmd.o \
tpm_eventlog.o tpm1_eventlog.o tpm2_eventlog.o
tpm-$(CONFIG_ACPI) += tpm_ppi.o tpm_acpi.o tpm-$(CONFIG_ACPI) += tpm_ppi.o tpm_acpi.o
tpm-$(CONFIG_OF) += tpm_of.o tpm-$(CONFIG_OF) += tpm_of.o
obj-$(CONFIG_TCG_TIS_CORE) += tpm_tis_core.o obj-$(CONFIG_TCG_TIS_CORE) += tpm_tis_core.o

View File

@ -18,7 +18,6 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/wait.h> #include <linux/wait.h>

View File

@ -141,7 +141,7 @@ static void tpm_dev_release(struct device *dev)
* Allocates a new struct tpm_chip instance and assigns a free * Allocates a new struct tpm_chip instance and assigns a free
* device number for it. Must be paired with put_device(&chip->dev). * device number for it. Must be paired with put_device(&chip->dev).
*/ */
struct tpm_chip *tpm_chip_alloc(struct device *dev, struct tpm_chip *tpm_chip_alloc(struct device *pdev,
const struct tpm_class_ops *ops) const struct tpm_class_ops *ops)
{ {
struct tpm_chip *chip; struct tpm_chip *chip;
@ -160,7 +160,7 @@ struct tpm_chip *tpm_chip_alloc(struct device *dev,
rc = idr_alloc(&dev_nums_idr, NULL, 0, TPM_NUM_DEVICES, GFP_KERNEL); rc = idr_alloc(&dev_nums_idr, NULL, 0, TPM_NUM_DEVICES, GFP_KERNEL);
mutex_unlock(&idr_lock); mutex_unlock(&idr_lock);
if (rc < 0) { if (rc < 0) {
dev_err(dev, "No available tpm device numbers\n"); dev_err(pdev, "No available tpm device numbers\n");
kfree(chip); kfree(chip);
return ERR_PTR(rc); return ERR_PTR(rc);
} }
@ -170,7 +170,7 @@ struct tpm_chip *tpm_chip_alloc(struct device *dev,
chip->dev.class = tpm_class; chip->dev.class = tpm_class;
chip->dev.release = tpm_dev_release; chip->dev.release = tpm_dev_release;
chip->dev.parent = dev; chip->dev.parent = pdev;
chip->dev.groups = chip->groups; chip->dev.groups = chip->groups;
if (chip->dev_num == 0) if (chip->dev_num == 0)
@ -182,7 +182,7 @@ struct tpm_chip *tpm_chip_alloc(struct device *dev,
if (rc) if (rc)
goto out; goto out;
if (!dev) if (!pdev)
chip->flags |= TPM_CHIP_FLAG_VIRTUAL; chip->flags |= TPM_CHIP_FLAG_VIRTUAL;
cdev_init(&chip->cdev, &tpm_fops); cdev_init(&chip->cdev, &tpm_fops);

View File

@ -38,6 +38,9 @@ static void user_reader_timeout(unsigned long ptr)
{ {
struct file_priv *priv = (struct file_priv *)ptr; struct file_priv *priv = (struct file_priv *)ptr;
pr_warn("TPM user space timeout is deprecated (pid=%d)\n",
task_tgid_nr(current));
schedule_work(&priv->work); schedule_work(&priv->work);
} }
@ -157,7 +160,7 @@ static ssize_t tpm_write(struct file *file, const char __user *buf,
mutex_unlock(&priv->buffer_mutex); mutex_unlock(&priv->buffer_mutex);
/* Set a timeout by which the reader must come claim the result */ /* Set a timeout by which the reader must come claim the result */
mod_timer(&priv->user_read_timer, jiffies + (60 * HZ)); mod_timer(&priv->user_read_timer, jiffies + (120 * HZ));
return in_size; return in_size;
} }

View File

@ -47,7 +47,7 @@
static int tpm_suspend_pcr; static int tpm_suspend_pcr;
module_param_named(suspend_pcr, tpm_suspend_pcr, uint, 0644); module_param_named(suspend_pcr, tpm_suspend_pcr, uint, 0644);
MODULE_PARM_DESC(suspend_pcr, MODULE_PARM_DESC(suspend_pcr,
"PCR to use for dummy writes to faciltate flush on suspend."); "PCR to use for dummy writes to facilitate flush on suspend.");
/* /*
* Array with one entry per ordinal defining the maximum amount * Array with one entry per ordinal defining the maximum amount
@ -328,8 +328,17 @@ unsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip,
} }
EXPORT_SYMBOL_GPL(tpm_calc_ordinal_duration); EXPORT_SYMBOL_GPL(tpm_calc_ordinal_duration);
/* /**
* Internal kernel interface to transmit TPM commands * tmp_transmit - Internal kernel interface to transmit TPM commands.
*
* @chip: TPM chip to use
* @buf: TPM command buffer
* @bufsiz: length of the TPM command buffer
* @flags: tpm transmit flags - bitmap
*
* Return:
* 0 when the operation is successful.
* A negative number for system errors (errno).
*/ */
ssize_t tpm_transmit(struct tpm_chip *chip, const u8 *buf, size_t bufsiz, ssize_t tpm_transmit(struct tpm_chip *chip, const u8 *buf, size_t bufsiz,
unsigned int flags) unsigned int flags)
@ -409,31 +418,55 @@ out:
return rc; return rc;
} }
#define TPM_DIGEST_SIZE 20 /**
#define TPM_RET_CODE_IDX 6 * tmp_transmit_cmd - send a tpm command to the device
* The function extracts tpm out header return code
ssize_t tpm_transmit_cmd(struct tpm_chip *chip, const void *cmd, *
int len, unsigned int flags, const char *desc) * @chip: TPM chip to use
* @buf: TPM command buffer
* @bufsiz: length of the buffer
* @min_rsp_body_length: minimum expected length of response body
* @flags: tpm transmit flags - bitmap
* @desc: command description used in the error message
*
* Return:
* 0 when the operation is successful.
* A negative number for system errors (errno).
* A positive number for a TPM error.
*/
ssize_t tpm_transmit_cmd(struct tpm_chip *chip, const void *buf,
size_t bufsiz, size_t min_rsp_body_length,
unsigned int flags, const char *desc)
{ {
const struct tpm_output_header *header; const struct tpm_output_header *header;
int err; int err;
ssize_t len;
len = tpm_transmit(chip, (const u8 *)cmd, len, flags); len = tpm_transmit(chip, (const u8 *)buf, bufsiz, flags);
if (len < 0) if (len < 0)
return len; return len;
else if (len < TPM_HEADER_SIZE) else if (len < TPM_HEADER_SIZE)
return -EFAULT; return -EFAULT;
header = cmd; header = buf;
if (len != be32_to_cpu(header->length))
return -EFAULT;
err = be32_to_cpu(header->return_code); err = be32_to_cpu(header->return_code);
if (err != 0 && desc) if (err != 0 && desc)
dev_err(&chip->dev, "A TPM error (%d) occurred %s\n", err, dev_err(&chip->dev, "A TPM error (%d) occurred %s\n", err,
desc); desc);
if (err)
return err;
return err; if (len < min_rsp_body_length + TPM_HEADER_SIZE)
return -EFAULT;
return 0;
} }
#define TPM_DIGEST_SIZE 20
#define TPM_RET_CODE_IDX 6
#define TPM_INTERNAL_RESULT_SIZE 200 #define TPM_INTERNAL_RESULT_SIZE 200
#define TPM_ORD_GET_CAP cpu_to_be32(101) #define TPM_ORD_GET_CAP cpu_to_be32(101)
#define TPM_ORD_GET_RANDOM cpu_to_be32(70) #define TPM_ORD_GET_RANDOM cpu_to_be32(70)
@ -445,7 +478,7 @@ static const struct tpm_input_header tpm_getcap_header = {
}; };
ssize_t tpm_getcap(struct tpm_chip *chip, u32 subcap_id, cap_t *cap, ssize_t tpm_getcap(struct tpm_chip *chip, u32 subcap_id, cap_t *cap,
const char *desc) const char *desc, size_t min_cap_length)
{ {
struct tpm_cmd_t tpm_cmd; struct tpm_cmd_t tpm_cmd;
int rc; int rc;
@ -468,8 +501,8 @@ ssize_t tpm_getcap(struct tpm_chip *chip, u32 subcap_id, cap_t *cap,
tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4); tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
tpm_cmd.params.getcap_in.subcap = cpu_to_be32(subcap_id); tpm_cmd.params.getcap_in.subcap = cpu_to_be32(subcap_id);
} }
rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, 0, rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
desc); min_cap_length, 0, desc);
if (!rc) if (!rc)
*cap = tpm_cmd.params.getcap_out.cap; *cap = tpm_cmd.params.getcap_out.cap;
return rc; return rc;
@ -493,14 +526,13 @@ static int tpm_startup(struct tpm_chip *chip, __be16 startup_type)
start_cmd.params.startup_in.startup_type = startup_type; start_cmd.params.startup_in.startup_type = startup_type;
return tpm_transmit_cmd(chip, &start_cmd, TPM_INTERNAL_RESULT_SIZE, 0, return tpm_transmit_cmd(chip, &start_cmd, TPM_INTERNAL_RESULT_SIZE, 0,
"attempting to start the TPM"); 0, "attempting to start the TPM");
} }
int tpm_get_timeouts(struct tpm_chip *chip) int tpm_get_timeouts(struct tpm_chip *chip)
{ {
cap_t cap; cap_t cap;
unsigned long new_timeout[4]; unsigned long timeout_old[4], timeout_chip[4], timeout_eff[4];
unsigned long old_timeout[4];
ssize_t rc; ssize_t rc;
if (chip->flags & TPM_CHIP_FLAG_HAVE_TIMEOUTS) if (chip->flags & TPM_CHIP_FLAG_HAVE_TIMEOUTS)
@ -523,8 +555,8 @@ int tpm_get_timeouts(struct tpm_chip *chip)
return 0; return 0;
} }
rc = tpm_getcap(chip, TPM_CAP_PROP_TIS_TIMEOUT, &cap, rc = tpm_getcap(chip, TPM_CAP_PROP_TIS_TIMEOUT, &cap, NULL,
"attempting to determine the timeouts"); sizeof(cap.timeout));
if (rc == TPM_ERR_INVALID_POSTINIT) { if (rc == TPM_ERR_INVALID_POSTINIT) {
/* The TPM is not started, we are the first to talk to it. /* The TPM is not started, we are the first to talk to it.
Execute a startup command. */ Execute a startup command. */
@ -533,16 +565,26 @@ int tpm_get_timeouts(struct tpm_chip *chip)
return rc; return rc;
rc = tpm_getcap(chip, TPM_CAP_PROP_TIS_TIMEOUT, &cap, rc = tpm_getcap(chip, TPM_CAP_PROP_TIS_TIMEOUT, &cap,
"attempting to determine the timeouts"); "attempting to determine the timeouts",
sizeof(cap.timeout));
} }
if (rc)
return rc;
old_timeout[0] = be32_to_cpu(cap.timeout.a); if (rc) {
old_timeout[1] = be32_to_cpu(cap.timeout.b); dev_err(&chip->dev,
old_timeout[2] = be32_to_cpu(cap.timeout.c); "A TPM error (%zd) occurred attempting to determine the timeouts\n",
old_timeout[3] = be32_to_cpu(cap.timeout.d); rc);
memcpy(new_timeout, old_timeout, sizeof(new_timeout)); return rc;
}
timeout_old[0] = jiffies_to_usecs(chip->timeout_a);
timeout_old[1] = jiffies_to_usecs(chip->timeout_b);
timeout_old[2] = jiffies_to_usecs(chip->timeout_c);
timeout_old[3] = jiffies_to_usecs(chip->timeout_d);
timeout_chip[0] = be32_to_cpu(cap.timeout.a);
timeout_chip[1] = be32_to_cpu(cap.timeout.b);
timeout_chip[2] = be32_to_cpu(cap.timeout.c);
timeout_chip[3] = be32_to_cpu(cap.timeout.d);
memcpy(timeout_eff, timeout_chip, sizeof(timeout_eff));
/* /*
* Provide ability for vendor overrides of timeout values in case * Provide ability for vendor overrides of timeout values in case
@ -550,16 +592,24 @@ int tpm_get_timeouts(struct tpm_chip *chip)
*/ */
if (chip->ops->update_timeouts != NULL) if (chip->ops->update_timeouts != NULL)
chip->timeout_adjusted = chip->timeout_adjusted =
chip->ops->update_timeouts(chip, new_timeout); chip->ops->update_timeouts(chip, timeout_eff);
if (!chip->timeout_adjusted) { if (!chip->timeout_adjusted) {
/* Don't overwrite default if value is 0 */ /* Restore default if chip reported 0 */
if (new_timeout[0] != 0 && new_timeout[0] < 1000) { int i;
int i;
for (i = 0; i < ARRAY_SIZE(timeout_eff); i++) {
if (timeout_eff[i])
continue;
timeout_eff[i] = timeout_old[i];
chip->timeout_adjusted = true;
}
if (timeout_eff[0] != 0 && timeout_eff[0] < 1000) {
/* timeouts in msec rather usec */ /* timeouts in msec rather usec */
for (i = 0; i != ARRAY_SIZE(new_timeout); i++) for (i = 0; i != ARRAY_SIZE(timeout_eff); i++)
new_timeout[i] *= 1000; timeout_eff[i] *= 1000;
chip->timeout_adjusted = true; chip->timeout_adjusted = true;
} }
} }
@ -568,19 +618,20 @@ int tpm_get_timeouts(struct tpm_chip *chip)
if (chip->timeout_adjusted) { if (chip->timeout_adjusted) {
dev_info(&chip->dev, dev_info(&chip->dev,
HW_ERR "Adjusting reported timeouts: A %lu->%luus B %lu->%luus C %lu->%luus D %lu->%luus\n", HW_ERR "Adjusting reported timeouts: A %lu->%luus B %lu->%luus C %lu->%luus D %lu->%luus\n",
old_timeout[0], new_timeout[0], timeout_chip[0], timeout_eff[0],
old_timeout[1], new_timeout[1], timeout_chip[1], timeout_eff[1],
old_timeout[2], new_timeout[2], timeout_chip[2], timeout_eff[2],
old_timeout[3], new_timeout[3]); timeout_chip[3], timeout_eff[3]);
} }
chip->timeout_a = usecs_to_jiffies(new_timeout[0]); chip->timeout_a = usecs_to_jiffies(timeout_eff[0]);
chip->timeout_b = usecs_to_jiffies(new_timeout[1]); chip->timeout_b = usecs_to_jiffies(timeout_eff[1]);
chip->timeout_c = usecs_to_jiffies(new_timeout[2]); chip->timeout_c = usecs_to_jiffies(timeout_eff[2]);
chip->timeout_d = usecs_to_jiffies(new_timeout[3]); chip->timeout_d = usecs_to_jiffies(timeout_eff[3]);
rc = tpm_getcap(chip, TPM_CAP_PROP_TIS_DURATION, &cap, rc = tpm_getcap(chip, TPM_CAP_PROP_TIS_DURATION, &cap,
"attempting to determine the durations"); "attempting to determine the durations",
sizeof(cap.duration));
if (rc) if (rc)
return rc; return rc;
@ -631,13 +682,14 @@ static int tpm_continue_selftest(struct tpm_chip *chip)
struct tpm_cmd_t cmd; struct tpm_cmd_t cmd;
cmd.header.in = continue_selftest_header; cmd.header.in = continue_selftest_header;
rc = tpm_transmit_cmd(chip, &cmd, CONTINUE_SELFTEST_RESULT_SIZE, 0, rc = tpm_transmit_cmd(chip, &cmd, CONTINUE_SELFTEST_RESULT_SIZE, 0, 0,
"continue selftest"); "continue selftest");
return rc; return rc;
} }
#define TPM_ORDINAL_PCRREAD cpu_to_be32(21) #define TPM_ORDINAL_PCRREAD cpu_to_be32(21)
#define READ_PCR_RESULT_SIZE 30 #define READ_PCR_RESULT_SIZE 30
#define READ_PCR_RESULT_BODY_SIZE 20
static const struct tpm_input_header pcrread_header = { static const struct tpm_input_header pcrread_header = {
.tag = TPM_TAG_RQU_COMMAND, .tag = TPM_TAG_RQU_COMMAND,
.length = cpu_to_be32(14), .length = cpu_to_be32(14),
@ -651,7 +703,8 @@ int tpm_pcr_read_dev(struct tpm_chip *chip, int pcr_idx, u8 *res_buf)
cmd.header.in = pcrread_header; cmd.header.in = pcrread_header;
cmd.params.pcrread_in.pcr_idx = cpu_to_be32(pcr_idx); cmd.params.pcrread_in.pcr_idx = cpu_to_be32(pcr_idx);
rc = tpm_transmit_cmd(chip, &cmd, READ_PCR_RESULT_SIZE, 0, rc = tpm_transmit_cmd(chip, &cmd, READ_PCR_RESULT_SIZE,
READ_PCR_RESULT_BODY_SIZE, 0,
"attempting to read a pcr value"); "attempting to read a pcr value");
if (rc == 0) if (rc == 0)
@ -714,6 +767,7 @@ EXPORT_SYMBOL_GPL(tpm_pcr_read);
#define TPM_ORD_PCR_EXTEND cpu_to_be32(20) #define TPM_ORD_PCR_EXTEND cpu_to_be32(20)
#define EXTEND_PCR_RESULT_SIZE 34 #define EXTEND_PCR_RESULT_SIZE 34
#define EXTEND_PCR_RESULT_BODY_SIZE 20
static const struct tpm_input_header pcrextend_header = { static const struct tpm_input_header pcrextend_header = {
.tag = TPM_TAG_RQU_COMMAND, .tag = TPM_TAG_RQU_COMMAND,
.length = cpu_to_be32(34), .length = cpu_to_be32(34),
@ -735,13 +789,25 @@ int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash)
struct tpm_cmd_t cmd; struct tpm_cmd_t cmd;
int rc; int rc;
struct tpm_chip *chip; struct tpm_chip *chip;
struct tpm2_digest digest_list[ARRAY_SIZE(chip->active_banks)];
u32 count = 0;
int i;
chip = tpm_chip_find_get(chip_num); chip = tpm_chip_find_get(chip_num);
if (chip == NULL) if (chip == NULL)
return -ENODEV; return -ENODEV;
if (chip->flags & TPM_CHIP_FLAG_TPM2) { if (chip->flags & TPM_CHIP_FLAG_TPM2) {
rc = tpm2_pcr_extend(chip, pcr_idx, hash); memset(digest_list, 0, sizeof(digest_list));
for (i = 0; i < ARRAY_SIZE(chip->active_banks) &&
chip->active_banks[i] != TPM2_ALG_ERROR; i++) {
digest_list[i].alg_id = chip->active_banks[i];
memcpy(digest_list[i].digest, hash, TPM_DIGEST_SIZE);
count++;
}
rc = tpm2_pcr_extend(chip, pcr_idx, count, digest_list);
tpm_put_ops(chip); tpm_put_ops(chip);
return rc; return rc;
} }
@ -749,7 +815,8 @@ int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash)
cmd.header.in = pcrextend_header; cmd.header.in = pcrextend_header;
cmd.params.pcrextend_in.pcr_idx = cpu_to_be32(pcr_idx); cmd.params.pcrextend_in.pcr_idx = cpu_to_be32(pcr_idx);
memcpy(cmd.params.pcrextend_in.hash, hash, TPM_DIGEST_SIZE); memcpy(cmd.params.pcrextend_in.hash, hash, TPM_DIGEST_SIZE);
rc = tpm_transmit_cmd(chip, &cmd, EXTEND_PCR_RESULT_SIZE, 0, rc = tpm_transmit_cmd(chip, &cmd, EXTEND_PCR_RESULT_SIZE,
EXTEND_PCR_RESULT_BODY_SIZE, 0,
"attempting extend a PCR value"); "attempting extend a PCR value");
tpm_put_ops(chip); tpm_put_ops(chip);
@ -853,7 +920,7 @@ int tpm_send(u32 chip_num, void *cmd, size_t buflen)
if (chip == NULL) if (chip == NULL)
return -ENODEV; return -ENODEV;
rc = tpm_transmit_cmd(chip, cmd, buflen, 0, "attempting tpm_cmd"); rc = tpm_transmit_cmd(chip, cmd, buflen, 0, 0, "attempting tpm_cmd");
tpm_put_ops(chip); tpm_put_ops(chip);
return rc; return rc;
@ -955,7 +1022,8 @@ int tpm_pm_suspend(struct device *dev)
cmd.params.pcrextend_in.pcr_idx = cpu_to_be32(tpm_suspend_pcr); cmd.params.pcrextend_in.pcr_idx = cpu_to_be32(tpm_suspend_pcr);
memcpy(cmd.params.pcrextend_in.hash, dummy_hash, memcpy(cmd.params.pcrextend_in.hash, dummy_hash,
TPM_DIGEST_SIZE); TPM_DIGEST_SIZE);
rc = tpm_transmit_cmd(chip, &cmd, EXTEND_PCR_RESULT_SIZE, 0, rc = tpm_transmit_cmd(chip, &cmd, EXTEND_PCR_RESULT_SIZE,
EXTEND_PCR_RESULT_BODY_SIZE, 0,
"extending dummy pcr before suspend"); "extending dummy pcr before suspend");
} }
@ -963,7 +1031,7 @@ int tpm_pm_suspend(struct device *dev)
for (try = 0; try < TPM_RETRY; try++) { for (try = 0; try < TPM_RETRY; try++) {
cmd.header.in = savestate_header; cmd.header.in = savestate_header;
rc = tpm_transmit_cmd(chip, &cmd, SAVESTATE_RESULT_SIZE, 0, rc = tpm_transmit_cmd(chip, &cmd, SAVESTATE_RESULT_SIZE, 0,
NULL); 0, NULL);
/* /*
* If the TPM indicates that it is too busy to respond to * If the TPM indicates that it is too busy to respond to
@ -1025,7 +1093,7 @@ int tpm_get_random(u32 chip_num, u8 *out, size_t max)
{ {
struct tpm_chip *chip; struct tpm_chip *chip;
struct tpm_cmd_t tpm_cmd; struct tpm_cmd_t tpm_cmd;
u32 recd, num_bytes = min_t(u32, max, TPM_MAX_RNG_DATA); u32 recd, num_bytes = min_t(u32, max, TPM_MAX_RNG_DATA), rlength;
int err, total = 0, retries = 5; int err, total = 0, retries = 5;
u8 *dest = out; u8 *dest = out;
@ -1048,11 +1116,20 @@ int tpm_get_random(u32 chip_num, u8 *out, size_t max)
err = tpm_transmit_cmd(chip, &tpm_cmd, err = tpm_transmit_cmd(chip, &tpm_cmd,
TPM_GETRANDOM_RESULT_SIZE + num_bytes, TPM_GETRANDOM_RESULT_SIZE + num_bytes,
offsetof(struct tpm_getrandom_out,
rng_data),
0, "attempting get random"); 0, "attempting get random");
if (err) if (err)
break; break;
recd = be32_to_cpu(tpm_cmd.params.getrandom_out.rng_data_len); recd = be32_to_cpu(tpm_cmd.params.getrandom_out.rng_data_len);
rlength = be32_to_cpu(tpm_cmd.header.out.length);
if (rlength < offsetof(struct tpm_getrandom_out, rng_data) +
recd) {
total = -EFAULT;
break;
}
memcpy(dest, tpm_cmd.params.getrandom_out.rng_data, recd); memcpy(dest, tpm_cmd.params.getrandom_out.rng_data, recd);
dest += recd; dest += recd;

View File

@ -21,6 +21,7 @@
#include "tpm.h" #include "tpm.h"
#define READ_PUBEK_RESULT_SIZE 314 #define READ_PUBEK_RESULT_SIZE 314
#define READ_PUBEK_RESULT_MIN_BODY_SIZE (28 + 256)
#define TPM_ORD_READPUBEK cpu_to_be32(124) #define TPM_ORD_READPUBEK cpu_to_be32(124)
static const struct tpm_input_header tpm_readpubek_header = { static const struct tpm_input_header tpm_readpubek_header = {
.tag = TPM_TAG_RQU_COMMAND, .tag = TPM_TAG_RQU_COMMAND,
@ -39,7 +40,8 @@ static ssize_t pubek_show(struct device *dev, struct device_attribute *attr,
struct tpm_chip *chip = to_tpm_chip(dev); struct tpm_chip *chip = to_tpm_chip(dev);
tpm_cmd.header.in = tpm_readpubek_header; tpm_cmd.header.in = tpm_readpubek_header;
err = tpm_transmit_cmd(chip, &tpm_cmd, READ_PUBEK_RESULT_SIZE, 0, err = tpm_transmit_cmd(chip, &tpm_cmd, READ_PUBEK_RESULT_SIZE,
READ_PUBEK_RESULT_MIN_BODY_SIZE, 0,
"attempting to read the PUBEK"); "attempting to read the PUBEK");
if (err) if (err)
goto out; goto out;
@ -95,7 +97,8 @@ static ssize_t pcrs_show(struct device *dev, struct device_attribute *attr,
struct tpm_chip *chip = to_tpm_chip(dev); struct tpm_chip *chip = to_tpm_chip(dev);
rc = tpm_getcap(chip, TPM_CAP_PROP_PCR, &cap, rc = tpm_getcap(chip, TPM_CAP_PROP_PCR, &cap,
"attempting to determine the number of PCRS"); "attempting to determine the number of PCRS",
sizeof(cap.num_pcrs));
if (rc) if (rc)
return 0; return 0;
@ -120,7 +123,8 @@ static ssize_t enabled_show(struct device *dev, struct device_attribute *attr,
ssize_t rc; ssize_t rc;
rc = tpm_getcap(to_tpm_chip(dev), TPM_CAP_FLAG_PERM, &cap, rc = tpm_getcap(to_tpm_chip(dev), TPM_CAP_FLAG_PERM, &cap,
"attempting to determine the permanent enabled state"); "attempting to determine the permanent enabled state",
sizeof(cap.perm_flags));
if (rc) if (rc)
return 0; return 0;
@ -136,7 +140,8 @@ static ssize_t active_show(struct device *dev, struct device_attribute *attr,
ssize_t rc; ssize_t rc;
rc = tpm_getcap(to_tpm_chip(dev), TPM_CAP_FLAG_PERM, &cap, rc = tpm_getcap(to_tpm_chip(dev), TPM_CAP_FLAG_PERM, &cap,
"attempting to determine the permanent active state"); "attempting to determine the permanent active state",
sizeof(cap.perm_flags));
if (rc) if (rc)
return 0; return 0;
@ -152,7 +157,8 @@ static ssize_t owned_show(struct device *dev, struct device_attribute *attr,
ssize_t rc; ssize_t rc;
rc = tpm_getcap(to_tpm_chip(dev), TPM_CAP_PROP_OWNER, &cap, rc = tpm_getcap(to_tpm_chip(dev), TPM_CAP_PROP_OWNER, &cap,
"attempting to determine the owner state"); "attempting to determine the owner state",
sizeof(cap.owned));
if (rc) if (rc)
return 0; return 0;
@ -168,7 +174,8 @@ static ssize_t temp_deactivated_show(struct device *dev,
ssize_t rc; ssize_t rc;
rc = tpm_getcap(to_tpm_chip(dev), TPM_CAP_FLAG_VOL, &cap, rc = tpm_getcap(to_tpm_chip(dev), TPM_CAP_FLAG_VOL, &cap,
"attempting to determine the temporary state"); "attempting to determine the temporary state",
sizeof(cap.stclear_flags));
if (rc) if (rc)
return 0; return 0;
@ -186,7 +193,8 @@ static ssize_t caps_show(struct device *dev, struct device_attribute *attr,
char *str = buf; char *str = buf;
rc = tpm_getcap(chip, TPM_CAP_PROP_MANUFACTURER, &cap, rc = tpm_getcap(chip, TPM_CAP_PROP_MANUFACTURER, &cap,
"attempting to determine the manufacturer"); "attempting to determine the manufacturer",
sizeof(cap.manufacturer_id));
if (rc) if (rc)
return 0; return 0;
str += sprintf(str, "Manufacturer: 0x%x\n", str += sprintf(str, "Manufacturer: 0x%x\n",
@ -194,7 +202,8 @@ static ssize_t caps_show(struct device *dev, struct device_attribute *attr,
/* Try to get a TPM version 1.2 TPM_CAP_VERSION_INFO */ /* Try to get a TPM version 1.2 TPM_CAP_VERSION_INFO */
rc = tpm_getcap(chip, TPM_CAP_VERSION_1_2, &cap, rc = tpm_getcap(chip, TPM_CAP_VERSION_1_2, &cap,
"attempting to determine the 1.2 version"); "attempting to determine the 1.2 version",
sizeof(cap.tpm_version_1_2));
if (!rc) { if (!rc) {
str += sprintf(str, str += sprintf(str,
"TCG version: %d.%d\nFirmware version: %d.%d\n", "TCG version: %d.%d\nFirmware version: %d.%d\n",
@ -205,7 +214,8 @@ static ssize_t caps_show(struct device *dev, struct device_attribute *attr,
} else { } else {
/* Otherwise just use TPM_STRUCT_VER */ /* Otherwise just use TPM_STRUCT_VER */
rc = tpm_getcap(chip, TPM_CAP_VERSION_1_1, &cap, rc = tpm_getcap(chip, TPM_CAP_VERSION_1_1, &cap,
"attempting to determine the 1.1 version"); "attempting to determine the 1.1 version",
sizeof(cap.tpm_version));
if (rc) if (rc)
return 0; return 0;
str += sprintf(str, str += sprintf(str,

View File

@ -34,8 +34,7 @@
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/cdev.h> #include <linux/cdev.h>
#include <linux/highmem.h> #include <linux/highmem.h>
#include <crypto/hash_info.h>
#include "tpm_eventlog.h"
enum tpm_const { enum tpm_const {
TPM_MINOR = 224, /* officially assigned */ TPM_MINOR = 224, /* officially assigned */
@ -97,6 +96,7 @@ enum tpm2_return_codes {
}; };
enum tpm2_algorithms { enum tpm2_algorithms {
TPM2_ALG_ERROR = 0x0000,
TPM2_ALG_SHA1 = 0x0004, TPM2_ALG_SHA1 = 0x0004,
TPM2_ALG_KEYEDHASH = 0x0008, TPM2_ALG_KEYEDHASH = 0x0008,
TPM2_ALG_SHA256 = 0x000B, TPM2_ALG_SHA256 = 0x000B,
@ -127,6 +127,7 @@ enum tpm2_permanent_handles {
}; };
enum tpm2_capabilities { enum tpm2_capabilities {
TPM2_CAP_PCRS = 5,
TPM2_CAP_TPM_PROPERTIES = 6, TPM2_CAP_TPM_PROPERTIES = 6,
}; };
@ -148,6 +149,11 @@ enum tpm_chip_flags {
TPM_CHIP_FLAG_HAVE_TIMEOUTS = BIT(4), TPM_CHIP_FLAG_HAVE_TIMEOUTS = BIT(4),
}; };
struct tpm_bios_log {
void *bios_event_log;
void *bios_event_log_end;
};
struct tpm_chip_seqops { struct tpm_chip_seqops {
struct tpm_chip *chip; struct tpm_chip *chip;
const struct seq_operations *seqops; const struct seq_operations *seqops;
@ -187,6 +193,8 @@ struct tpm_chip {
const struct attribute_group *groups[3]; const struct attribute_group *groups[3];
unsigned int groups_cnt; unsigned int groups_cnt;
u16 active_banks[7];
#ifdef CONFIG_ACPI #ifdef CONFIG_ACPI
acpi_handle acpi_dev_handle; acpi_handle acpi_dev_handle;
char ppi_version[TPM_PPI_VERSION_LEN + 1]; char ppi_version[TPM_PPI_VERSION_LEN + 1];
@ -195,17 +203,6 @@ struct tpm_chip {
#define to_tpm_chip(d) container_of(d, struct tpm_chip, dev) #define to_tpm_chip(d) container_of(d, struct tpm_chip, dev)
static inline int tpm_read_index(int base, int index)
{
outb(index, base);
return inb(base+1) & 0xFF;
}
static inline void tpm_write_index(int base, int index, int value)
{
outb(index, base);
outb(value & 0xFF, base+1);
}
struct tpm_input_header { struct tpm_input_header {
__be16 tag; __be16 tag;
__be32 length; __be32 length;
@ -284,7 +281,7 @@ struct permanent_flags_t {
typedef union { typedef union {
struct permanent_flags_t perm_flags; struct permanent_flags_t perm_flags;
struct stclear_flags_t stclear_flags; struct stclear_flags_t stclear_flags;
bool owned; __u8 owned;
__be32 num_pcrs; __be32 num_pcrs;
struct tpm_version_t tpm_version; struct tpm_version_t tpm_version;
struct tpm_version_1_2_t tpm_version_1_2; struct tpm_version_1_2_t tpm_version_1_2;
@ -387,6 +384,11 @@ struct tpm_cmd_t {
tpm_cmd_params params; tpm_cmd_params params;
} __packed; } __packed;
struct tpm2_digest {
u16 alg_id;
u8 digest[SHA512_DIGEST_SIZE];
} __packed;
/* A string buffer type for constructing TPM commands. This is based on the /* A string buffer type for constructing TPM commands. This is based on the
* ideas of string buffer code in security/keys/trusted.h but is heap based * ideas of string buffer code in security/keys/trusted.h but is heap based
* in order to keep the stack usage minimal. * in order to keep the stack usage minimal.
@ -493,10 +495,11 @@ enum tpm_transmit_flags {
ssize_t tpm_transmit(struct tpm_chip *chip, const u8 *buf, size_t bufsiz, ssize_t tpm_transmit(struct tpm_chip *chip, const u8 *buf, size_t bufsiz,
unsigned int flags); unsigned int flags);
ssize_t tpm_transmit_cmd(struct tpm_chip *chip, const void *cmd, int len, ssize_t tpm_transmit_cmd(struct tpm_chip *chip, const void *buf, size_t bufsiz,
unsigned int flags, const char *desc); size_t min_rsp_body_len, unsigned int flags,
const char *desc);
ssize_t tpm_getcap(struct tpm_chip *chip, u32 subcap_id, cap_t *cap, ssize_t tpm_getcap(struct tpm_chip *chip, u32 subcap_id, cap_t *cap,
const char *desc); const char *desc, size_t min_cap_length);
int tpm_get_timeouts(struct tpm_chip *); int tpm_get_timeouts(struct tpm_chip *);
int tpm1_auto_startup(struct tpm_chip *chip); int tpm1_auto_startup(struct tpm_chip *chip);
int tpm_do_selftest(struct tpm_chip *chip); int tpm_do_selftest(struct tpm_chip *chip);
@ -529,8 +532,14 @@ static inline void tpm_add_ppi(struct tpm_chip *chip)
} }
#endif #endif
static inline inline u32 tpm2_rc_value(u32 rc)
{
return (rc & BIT(7)) ? rc & 0xff : rc;
}
int tpm2_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf); int tpm2_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf);
int tpm2_pcr_extend(struct tpm_chip *chip, int pcr_idx, const u8 *hash); int tpm2_pcr_extend(struct tpm_chip *chip, int pcr_idx, u32 count,
struct tpm2_digest *digests);
int tpm2_get_random(struct tpm_chip *chip, u8 *out, size_t max); int tpm2_get_random(struct tpm_chip *chip, u8 *out, size_t max);
int tpm2_seal_trusted(struct tpm_chip *chip, int tpm2_seal_trusted(struct tpm_chip *chip,
struct trusted_key_payload *payload, struct trusted_key_payload *payload,

View File

@ -390,9 +390,6 @@ int tpm_bios_log_setup(struct tpm_chip *chip)
unsigned int cnt; unsigned int cnt;
int rc = 0; int rc = 0;
if (chip->flags & TPM_CHIP_FLAG_TPM2)
return 0;
rc = tpm_read_log(chip); rc = tpm_read_log(chip);
if (rc) if (rc)
return rc; return rc;
@ -407,7 +404,13 @@ int tpm_bios_log_setup(struct tpm_chip *chip)
cnt++; cnt++;
chip->bin_log_seqops.chip = chip; chip->bin_log_seqops.chip = chip;
chip->bin_log_seqops.seqops = &tpm_binary_b_measurements_seqops; if (chip->flags & TPM_CHIP_FLAG_TPM2)
chip->bin_log_seqops.seqops =
&tpm2_binary_b_measurements_seqops;
else
chip->bin_log_seqops.seqops =
&tpm_binary_b_measurements_seqops;
chip->bios_dir[cnt] = chip->bios_dir[cnt] =
securityfs_create_file("binary_bios_measurements", securityfs_create_file("binary_bios_measurements",
@ -418,17 +421,21 @@ int tpm_bios_log_setup(struct tpm_chip *chip)
goto err; goto err;
cnt++; cnt++;
chip->ascii_log_seqops.chip = chip; if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) {
chip->ascii_log_seqops.seqops = &tpm_ascii_b_measurements_seqops;
chip->bios_dir[cnt] = chip->ascii_log_seqops.chip = chip;
securityfs_create_file("ascii_bios_measurements", chip->ascii_log_seqops.seqops =
0440, chip->bios_dir[0], &tpm_ascii_b_measurements_seqops;
(void *)&chip->ascii_log_seqops,
&tpm_bios_measurements_ops); chip->bios_dir[cnt] =
if (IS_ERR(chip->bios_dir[cnt])) securityfs_create_file("ascii_bios_measurements",
goto err; 0440, chip->bios_dir[0],
cnt++; (void *)&chip->ascii_log_seqops,
&tpm_bios_measurements_ops);
if (IS_ERR(chip->bios_dir[cnt]))
goto err;
cnt++;
}
return 0; return 0;

View File

@ -53,22 +53,6 @@ struct tpm2_pcr_read_out {
u8 digest[TPM_DIGEST_SIZE]; u8 digest[TPM_DIGEST_SIZE];
} __packed; } __packed;
struct tpm2_null_auth_area {
__be32 handle;
__be16 nonce_size;
u8 attributes;
__be16 auth_size;
} __packed;
struct tpm2_pcr_extend_in {
__be32 pcr_idx;
__be32 auth_area_size;
struct tpm2_null_auth_area auth_area;
__be32 digest_cnt;
__be16 hash_alg;
u8 digest[TPM_DIGEST_SIZE];
} __packed;
struct tpm2_get_tpm_pt_in { struct tpm2_get_tpm_pt_in {
__be32 cap_id; __be32 cap_id;
__be32 property_id; __be32 property_id;
@ -97,7 +81,6 @@ union tpm2_cmd_params {
struct tpm2_self_test_in selftest_in; struct tpm2_self_test_in selftest_in;
struct tpm2_pcr_read_in pcrread_in; struct tpm2_pcr_read_in pcrread_in;
struct tpm2_pcr_read_out pcrread_out; struct tpm2_pcr_read_out pcrread_out;
struct tpm2_pcr_extend_in pcrextend_in;
struct tpm2_get_tpm_pt_in get_tpm_pt_in; struct tpm2_get_tpm_pt_in get_tpm_pt_in;
struct tpm2_get_tpm_pt_out get_tpm_pt_out; struct tpm2_get_tpm_pt_out get_tpm_pt_out;
struct tpm2_get_random_in getrandom_in; struct tpm2_get_random_in getrandom_in;
@ -248,6 +231,9 @@ static const u8 tpm2_ordinal_duration[TPM2_CC_LAST - TPM2_CC_FIRST + 1] = {
(sizeof(struct tpm_input_header) + \ (sizeof(struct tpm_input_header) + \
sizeof(struct tpm2_pcr_read_in)) sizeof(struct tpm2_pcr_read_in))
#define TPM2_PCR_READ_RESP_BODY_SIZE \
sizeof(struct tpm2_pcr_read_out)
static const struct tpm_input_header tpm2_pcrread_header = { static const struct tpm_input_header tpm2_pcrread_header = {
.tag = cpu_to_be16(TPM2_ST_NO_SESSIONS), .tag = cpu_to_be16(TPM2_ST_NO_SESSIONS),
.length = cpu_to_be32(TPM2_PCR_READ_IN_SIZE), .length = cpu_to_be32(TPM2_PCR_READ_IN_SIZE),
@ -258,11 +244,9 @@ static const struct tpm_input_header tpm2_pcrread_header = {
* tpm2_pcr_read() - read a PCR value * tpm2_pcr_read() - read a PCR value
* @chip: TPM chip to use. * @chip: TPM chip to use.
* @pcr_idx: index of the PCR to read. * @pcr_idx: index of the PCR to read.
* @ref_buf: buffer to store the resulting hash, * @res_buf: buffer to store the resulting hash.
* *
* 0 is returned when the operation is successful. If a negative number is * Return: Same as with tpm_transmit_cmd.
* returned it remarks a POSIX error code. If a positive number is returned
* it remarks a TPM error.
*/ */
int tpm2_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf) int tpm2_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf)
{ {
@ -282,8 +266,9 @@ int tpm2_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf)
sizeof(cmd.params.pcrread_in.pcr_select)); sizeof(cmd.params.pcrread_in.pcr_select));
cmd.params.pcrread_in.pcr_select[pcr_idx >> 3] = 1 << (pcr_idx & 0x7); cmd.params.pcrread_in.pcr_select[pcr_idx >> 3] = 1 << (pcr_idx & 0x7);
rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), 0, rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd),
"attempting to read a pcr value"); TPM2_PCR_READ_RESP_BODY_SIZE,
0, "attempting to read a pcr value");
if (rc == 0) { if (rc == 0) {
buf = cmd.params.pcrread_out.digest; buf = cmd.params.pcrread_out.digest;
memcpy(res_buf, buf, TPM_DIGEST_SIZE); memcpy(res_buf, buf, TPM_DIGEST_SIZE);
@ -292,50 +277,71 @@ int tpm2_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf)
return rc; return rc;
} }
#define TPM2_GET_PCREXTEND_IN_SIZE \ struct tpm2_null_auth_area {
(sizeof(struct tpm_input_header) + \ __be32 handle;
sizeof(struct tpm2_pcr_extend_in)) __be16 nonce_size;
u8 attributes;
static const struct tpm_input_header tpm2_pcrextend_header = { __be16 auth_size;
.tag = cpu_to_be16(TPM2_ST_SESSIONS), } __packed;
.length = cpu_to_be32(TPM2_GET_PCREXTEND_IN_SIZE),
.ordinal = cpu_to_be32(TPM2_CC_PCR_EXTEND)
};
/** /**
* tpm2_pcr_extend() - extend a PCR value * tpm2_pcr_extend() - extend a PCR value
*
* @chip: TPM chip to use. * @chip: TPM chip to use.
* @pcr_idx: index of the PCR. * @pcr_idx: index of the PCR.
* @hash: hash value to use for the extend operation. * @count: number of digests passed.
* @digests: list of pcr banks and corresponding digest values to extend.
* *
* 0 is returned when the operation is successful. If a negative number is * Return: Same as with tpm_transmit_cmd.
* returned it remarks a POSIX error code. If a positive number is returned
* it remarks a TPM error.
*/ */
int tpm2_pcr_extend(struct tpm_chip *chip, int pcr_idx, const u8 *hash) int tpm2_pcr_extend(struct tpm_chip *chip, int pcr_idx, u32 count,
struct tpm2_digest *digests)
{ {
struct tpm2_cmd cmd; struct tpm_buf buf;
struct tpm2_null_auth_area auth_area;
int rc; int rc;
int i;
int j;
cmd.header.in = tpm2_pcrextend_header; if (count > ARRAY_SIZE(chip->active_banks))
cmd.params.pcrextend_in.pcr_idx = cpu_to_be32(pcr_idx); return -EINVAL;
cmd.params.pcrextend_in.auth_area_size =
cpu_to_be32(sizeof(struct tpm2_null_auth_area));
cmd.params.pcrextend_in.auth_area.handle =
cpu_to_be32(TPM2_RS_PW);
cmd.params.pcrextend_in.auth_area.nonce_size = 0;
cmd.params.pcrextend_in.auth_area.attributes = 0;
cmd.params.pcrextend_in.auth_area.auth_size = 0;
cmd.params.pcrextend_in.digest_cnt = cpu_to_be32(1);
cmd.params.pcrextend_in.hash_alg = cpu_to_be16(TPM2_ALG_SHA1);
memcpy(cmd.params.pcrextend_in.digest, hash, TPM_DIGEST_SIZE);
rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), 0, rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_PCR_EXTEND);
if (rc)
return rc;
tpm_buf_append_u32(&buf, pcr_idx);
auth_area.handle = cpu_to_be32(TPM2_RS_PW);
auth_area.nonce_size = 0;
auth_area.attributes = 0;
auth_area.auth_size = 0;
tpm_buf_append_u32(&buf, sizeof(struct tpm2_null_auth_area));
tpm_buf_append(&buf, (const unsigned char *)&auth_area,
sizeof(auth_area));
tpm_buf_append_u32(&buf, count);
for (i = 0; i < count; i++) {
for (j = 0; j < ARRAY_SIZE(tpm2_hash_map); j++) {
if (digests[i].alg_id != tpm2_hash_map[j].tpm_id)
continue;
tpm_buf_append_u16(&buf, digests[i].alg_id);
tpm_buf_append(&buf, (const unsigned char
*)&digests[i].digest,
hash_digest_size[tpm2_hash_map[j].crypto_id]);
}
}
rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, 0, 0,
"attempting extend a PCR value"); "attempting extend a PCR value");
tpm_buf_destroy(&buf);
return rc; return rc;
} }
#define TPM2_GETRANDOM_IN_SIZE \ #define TPM2_GETRANDOM_IN_SIZE \
(sizeof(struct tpm_input_header) + \ (sizeof(struct tpm_input_header) + \
sizeof(struct tpm2_get_random_in)) sizeof(struct tpm2_get_random_in))
@ -348,18 +354,18 @@ static const struct tpm_input_header tpm2_getrandom_header = {
/** /**
* tpm2_get_random() - get random bytes from the TPM RNG * tpm2_get_random() - get random bytes from the TPM RNG
*
* @chip: TPM chip to use * @chip: TPM chip to use
* @out: destination buffer for the random bytes * @out: destination buffer for the random bytes
* @max: the max number of bytes to write to @out * @max: the max number of bytes to write to @out
* *
* 0 is returned when the operation is successful. If a negative number is * Return:
* returned it remarks a POSIX error code. If a positive number is returned * Size of the output buffer, or -EIO on error.
* it remarks a TPM error.
*/ */
int tpm2_get_random(struct tpm_chip *chip, u8 *out, size_t max) int tpm2_get_random(struct tpm_chip *chip, u8 *out, size_t max)
{ {
struct tpm2_cmd cmd; struct tpm2_cmd cmd;
u32 recd; u32 recd, rlength;
u32 num_bytes; u32 num_bytes;
int err; int err;
int total = 0; int total = 0;
@ -376,13 +382,19 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *out, size_t max)
cmd.header.in = tpm2_getrandom_header; cmd.header.in = tpm2_getrandom_header;
cmd.params.getrandom_in.size = cpu_to_be16(num_bytes); cmd.params.getrandom_in.size = cpu_to_be16(num_bytes);
err = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), 0, err = tpm_transmit_cmd(chip, &cmd, sizeof(cmd),
"attempting get random"); offsetof(struct tpm2_get_random_out,
buffer),
0, "attempting get random");
if (err) if (err)
break; break;
recd = min_t(u32, be16_to_cpu(cmd.params.getrandom_out.size), recd = min_t(u32, be16_to_cpu(cmd.params.getrandom_out.size),
num_bytes); num_bytes);
rlength = be32_to_cpu(cmd.header.out.length);
if (rlength < offsetof(struct tpm2_get_random_out, buffer) +
recd)
return -EFAULT;
memcpy(dest, cmd.params.getrandom_out.buffer, recd); memcpy(dest, cmd.params.getrandom_out.buffer, recd);
dest += recd; dest += recd;
@ -397,6 +409,9 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *out, size_t max)
(sizeof(struct tpm_input_header) + \ (sizeof(struct tpm_input_header) + \
sizeof(struct tpm2_get_tpm_pt_in)) sizeof(struct tpm2_get_tpm_pt_in))
#define TPM2_GET_TPM_PT_OUT_BODY_SIZE \
sizeof(struct tpm2_get_tpm_pt_out)
static const struct tpm_input_header tpm2_get_tpm_pt_header = { static const struct tpm_input_header tpm2_get_tpm_pt_header = {
.tag = cpu_to_be16(TPM2_ST_NO_SESSIONS), .tag = cpu_to_be16(TPM2_ST_NO_SESSIONS),
.length = cpu_to_be32(TPM2_GET_TPM_PT_IN_SIZE), .length = cpu_to_be32(TPM2_GET_TPM_PT_IN_SIZE),
@ -404,15 +419,15 @@ static const struct tpm_input_header tpm2_get_tpm_pt_header = {
}; };
/** /**
* Append TPMS_AUTH_COMMAND to the buffer. The buffer must be allocated with * tpm_buf_append_auth() - append TPMS_AUTH_COMMAND to the buffer.
* tpm_buf_alloc().
* *
* @param buf: an allocated tpm_buf instance * @buf: an allocated tpm_buf instance
* @param nonce: the session nonce, may be NULL if not used * @session_handle: session handle
* @param nonce_len: the session nonce length, may be 0 if not used * @nonce: the session nonce, may be NULL if not used
* @param attributes: the session attributes * @nonce_len: the session nonce length, may be 0 if not used
* @param hmac: the session HMAC or password, may be NULL if not used * @attributes: the session attributes
* @param hmac_len: the session HMAC or password length, maybe 0 if not used * @hmac: the session HMAC or password, may be NULL if not used
* @hmac_len: the session HMAC or password length, maybe 0 if not used
*/ */
static void tpm2_buf_append_auth(struct tpm_buf *buf, u32 session_handle, static void tpm2_buf_append_auth(struct tpm_buf *buf, u32 session_handle,
const u8 *nonce, u16 nonce_len, const u8 *nonce, u16 nonce_len,
@ -435,7 +450,8 @@ static void tpm2_buf_append_auth(struct tpm_buf *buf, u32 session_handle,
/** /**
* tpm2_seal_trusted() - seal the payload of a trusted key * tpm2_seal_trusted() - seal the payload of a trusted key
* @chip_num: TPM chip to use *
* @chip: TPM chip to use
* @payload: the key data in clear and encrypted form * @payload: the key data in clear and encrypted form
* @options: authentication values and other options * @options: authentication values and other options
* *
@ -447,7 +463,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
{ {
unsigned int blob_len; unsigned int blob_len;
struct tpm_buf buf; struct tpm_buf buf;
u32 hash; u32 hash, rlength;
int i; int i;
int rc; int rc;
@ -512,7 +528,8 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
goto out; goto out;
} }
rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, 0, "sealing data"); rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, 4, 0,
"sealing data");
if (rc) if (rc)
goto out; goto out;
@ -521,6 +538,11 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
rc = -E2BIG; rc = -E2BIG;
goto out; goto out;
} }
rlength = be32_to_cpu(((struct tpm2_cmd *)&buf)->header.out.length);
if (rlength < TPM_HEADER_SIZE + 4 + blob_len) {
rc = -EFAULT;
goto out;
}
memcpy(payload->blob, &buf.data[TPM_HEADER_SIZE + 4], blob_len); memcpy(payload->blob, &buf.data[TPM_HEADER_SIZE + 4], blob_len);
payload->blob_len = blob_len; payload->blob_len = blob_len;
@ -529,7 +551,7 @@ out:
tpm_buf_destroy(&buf); tpm_buf_destroy(&buf);
if (rc > 0) { if (rc > 0) {
if ((rc & TPM2_RC_HASH) == TPM2_RC_HASH) if (tpm2_rc_value(rc) == TPM2_RC_HASH)
rc = -EINVAL; rc = -EINVAL;
else else
rc = -EPERM; rc = -EPERM;
@ -540,11 +562,17 @@ out:
/** /**
* tpm2_load_cmd() - execute a TPM2_Load command * tpm2_load_cmd() - execute a TPM2_Load command
* @chip_num: TPM chip to use *
* @chip: TPM chip to use
* @payload: the key data in clear and encrypted form * @payload: the key data in clear and encrypted form
* @options: authentication values and other options * @options: authentication values and other options
* @blob_handle: returned blob handle
* @flags: tpm transmit flags
* *
* Return: same as with tpm_transmit_cmd * Return: 0 on success.
* -E2BIG on wrong payload size.
* -EPERM on tpm error status.
* < 0 error from tpm_transmit_cmd.
*/ */
static int tpm2_load_cmd(struct tpm_chip *chip, static int tpm2_load_cmd(struct tpm_chip *chip,
struct trusted_key_payload *payload, struct trusted_key_payload *payload,
@ -584,7 +612,8 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
goto out; goto out;
} }
rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, flags, "loading blob"); rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, 4, flags,
"loading blob");
if (!rc) if (!rc)
*blob_handle = be32_to_cpup( *blob_handle = be32_to_cpup(
(__be32 *) &buf.data[TPM_HEADER_SIZE]); (__be32 *) &buf.data[TPM_HEADER_SIZE]);
@ -600,11 +629,12 @@ out:
/** /**
* tpm2_flush_context_cmd() - execute a TPM2_FlushContext command * tpm2_flush_context_cmd() - execute a TPM2_FlushContext command
* @chip_num: TPM chip to use
* @payload: the key data in clear and encrypted form
* @options: authentication values and other options
* *
* Return: same as with tpm_transmit_cmd * @chip: TPM chip to use
* @handle: the key data in clear and encrypted form
* @flags: tpm transmit flags
*
* Return: Same as with tpm_transmit_cmd.
*/ */
static void tpm2_flush_context_cmd(struct tpm_chip *chip, u32 handle, static void tpm2_flush_context_cmd(struct tpm_chip *chip, u32 handle,
unsigned int flags) unsigned int flags)
@ -621,7 +651,7 @@ static void tpm2_flush_context_cmd(struct tpm_chip *chip, u32 handle,
tpm_buf_append_u32(&buf, handle); tpm_buf_append_u32(&buf, handle);
rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, flags, rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, 0, flags,
"flushing context"); "flushing context");
if (rc) if (rc)
dev_warn(&chip->dev, "0x%08x was not flushed, rc=%d\n", handle, dev_warn(&chip->dev, "0x%08x was not flushed, rc=%d\n", handle,
@ -632,11 +662,16 @@ static void tpm2_flush_context_cmd(struct tpm_chip *chip, u32 handle,
/** /**
* tpm2_unseal_cmd() - execute a TPM2_Unload command * tpm2_unseal_cmd() - execute a TPM2_Unload command
* @chip_num: TPM chip to use *
* @chip: TPM chip to use
* @payload: the key data in clear and encrypted form * @payload: the key data in clear and encrypted form
* @options: authentication values and other options * @options: authentication values and other options
* @blob_handle: blob handle
* @flags: tpm_transmit_cmd flags
* *
* Return: same as with tpm_transmit_cmd * Return: 0 on success
* -EPERM on tpm error status
* < 0 error from tpm_transmit_cmd
*/ */
static int tpm2_unseal_cmd(struct tpm_chip *chip, static int tpm2_unseal_cmd(struct tpm_chip *chip,
struct trusted_key_payload *payload, struct trusted_key_payload *payload,
@ -647,6 +682,7 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
u16 data_len; u16 data_len;
u8 *data; u8 *data;
int rc; int rc;
u32 rlength;
rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL); rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL);
if (rc) if (rc)
@ -661,13 +697,21 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
options->blobauth /* hmac */, options->blobauth /* hmac */,
TPM_DIGEST_SIZE); TPM_DIGEST_SIZE);
rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, flags, "unsealing"); rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, 6, flags,
"unsealing");
if (rc > 0) if (rc > 0)
rc = -EPERM; rc = -EPERM;
if (!rc) { if (!rc) {
data_len = be16_to_cpup( data_len = be16_to_cpup(
(__be16 *) &buf.data[TPM_HEADER_SIZE + 4]); (__be16 *) &buf.data[TPM_HEADER_SIZE + 4]);
rlength = be32_to_cpu(((struct tpm2_cmd *)&buf)
->header.out.length);
if (rlength < TPM_HEADER_SIZE + 6 + data_len) {
rc = -EFAULT;
goto out;
}
data = &buf.data[TPM_HEADER_SIZE + 6]; data = &buf.data[TPM_HEADER_SIZE + 6];
memcpy(payload->key, data, data_len - 1); memcpy(payload->key, data, data_len - 1);
@ -675,17 +719,19 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
payload->migratable = data[data_len - 1]; payload->migratable = data[data_len - 1];
} }
out:
tpm_buf_destroy(&buf); tpm_buf_destroy(&buf);
return rc; return rc;
} }
/** /**
* tpm2_unseal_trusted() - unseal the payload of a trusted key * tpm2_unseal_trusted() - unseal the payload of a trusted key
* @chip_num: TPM chip to use *
* @chip: TPM chip to use
* @payload: the key data in clear and encrypted form * @payload: the key data in clear and encrypted form
* @options: authentication values and other options * @options: authentication values and other options
* *
* Return: < 0 on error and 0 on success. * Return: Same as with tpm_transmit_cmd.
*/ */
int tpm2_unseal_trusted(struct tpm_chip *chip, int tpm2_unseal_trusted(struct tpm_chip *chip,
struct trusted_key_payload *payload, struct trusted_key_payload *payload,
@ -715,9 +761,7 @@ out:
* @value: output variable. * @value: output variable.
* @desc: passed to tpm_transmit_cmd() * @desc: passed to tpm_transmit_cmd()
* *
* 0 is returned when the operation is successful. If a negative number is * Return: Same as with tpm_transmit_cmd.
* returned it remarks a POSIX error code. If a positive number is returned
* it remarks a TPM error.
*/ */
ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id, u32 *value, ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id, u32 *value,
const char *desc) const char *desc)
@ -730,7 +774,8 @@ ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id, u32 *value,
cmd.params.get_tpm_pt_in.property_id = cpu_to_be32(property_id); cmd.params.get_tpm_pt_in.property_id = cpu_to_be32(property_id);
cmd.params.get_tpm_pt_in.property_cnt = cpu_to_be32(1); cmd.params.get_tpm_pt_in.property_cnt = cpu_to_be32(1);
rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), 0, desc); rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd),
TPM2_GET_TPM_PT_OUT_BODY_SIZE, 0, desc);
if (!rc) if (!rc)
*value = be32_to_cpu(cmd.params.get_tpm_pt_out.value); *value = be32_to_cpu(cmd.params.get_tpm_pt_out.value);
@ -750,13 +795,12 @@ static const struct tpm_input_header tpm2_startup_header = {
/** /**
* tpm2_startup() - send startup command to the TPM chip * tpm2_startup() - send startup command to the TPM chip
*
* @chip: TPM chip to use. * @chip: TPM chip to use.
* @startup_type startup type. The value is either * @startup_type: startup type. The value is either
* TPM_SU_CLEAR or TPM_SU_STATE. * TPM_SU_CLEAR or TPM_SU_STATE.
* *
* 0 is returned when the operation is successful. If a negative number is * Return: Same as with tpm_transmit_cmd.
* returned it remarks a POSIX error code. If a positive number is returned
* it remarks a TPM error.
*/ */
static int tpm2_startup(struct tpm_chip *chip, u16 startup_type) static int tpm2_startup(struct tpm_chip *chip, u16 startup_type)
{ {
@ -765,7 +809,7 @@ static int tpm2_startup(struct tpm_chip *chip, u16 startup_type)
cmd.header.in = tpm2_startup_header; cmd.header.in = tpm2_startup_header;
cmd.params.startup_in.startup_type = cpu_to_be16(startup_type); cmd.params.startup_in.startup_type = cpu_to_be16(startup_type);
return tpm_transmit_cmd(chip, &cmd, sizeof(cmd), 0, return tpm_transmit_cmd(chip, &cmd, sizeof(cmd), 0, 0,
"attempting to start the TPM"); "attempting to start the TPM");
} }
@ -781,8 +825,9 @@ static const struct tpm_input_header tpm2_shutdown_header = {
/** /**
* tpm2_shutdown() - send shutdown command to the TPM chip * tpm2_shutdown() - send shutdown command to the TPM chip
*
* @chip: TPM chip to use. * @chip: TPM chip to use.
* @shutdown_type shutdown type. The value is either * @shutdown_type: shutdown type. The value is either
* TPM_SU_CLEAR or TPM_SU_STATE. * TPM_SU_CLEAR or TPM_SU_STATE.
*/ */
void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type) void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type)
@ -793,7 +838,8 @@ void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type)
cmd.header.in = tpm2_shutdown_header; cmd.header.in = tpm2_shutdown_header;
cmd.params.startup_in.startup_type = cpu_to_be16(shutdown_type); cmd.params.startup_in.startup_type = cpu_to_be16(shutdown_type);
rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), 0, "stopping the TPM"); rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), 0, 0,
"stopping the TPM");
/* In places where shutdown command is sent there's no much we can do /* In places where shutdown command is sent there's no much we can do
* except print the error code on a system failure. * except print the error code on a system failure.
@ -805,12 +851,11 @@ void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type)
/* /*
* tpm2_calc_ordinal_duration() - maximum duration for a command * tpm2_calc_ordinal_duration() - maximum duration for a command
*
* @chip: TPM chip to use. * @chip: TPM chip to use.
* @ordinal: command code number. * @ordinal: command code number.
* *
* 0 is returned when the operation is successful. If a negative number is * Return: maximum duration for a command
* returned it remarks a POSIX error code. If a positive number is returned
* it remarks a TPM error.
*/ */
unsigned long tpm2_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal) unsigned long tpm2_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal)
{ {
@ -842,13 +887,12 @@ static const struct tpm_input_header tpm2_selftest_header = {
/** /**
* tpm2_continue_selftest() - start a self test * tpm2_continue_selftest() - start a self test
*
* @chip: TPM chip to use * @chip: TPM chip to use
* @full: test all commands instead of testing only those that were not * @full: test all commands instead of testing only those that were not
* previously tested. * previously tested.
* *
* 0 is returned when the operation is successful. If a negative number is * Return: Same as with tpm_transmit_cmd with exception of RC_TESTING.
* returned it remarks a POSIX error code. If a positive number is returned
* it remarks a TPM error.
*/ */
static int tpm2_start_selftest(struct tpm_chip *chip, bool full) static int tpm2_start_selftest(struct tpm_chip *chip, bool full)
{ {
@ -858,7 +902,7 @@ static int tpm2_start_selftest(struct tpm_chip *chip, bool full)
cmd.header.in = tpm2_selftest_header; cmd.header.in = tpm2_selftest_header;
cmd.params.selftest_in.full_test = full; cmd.params.selftest_in.full_test = full;
rc = tpm_transmit_cmd(chip, &cmd, TPM2_SELF_TEST_IN_SIZE, 0, rc = tpm_transmit_cmd(chip, &cmd, TPM2_SELF_TEST_IN_SIZE, 0, 0,
"continue selftest"); "continue selftest");
/* At least some prototype chips seem to give RC_TESTING error /* At least some prototype chips seem to give RC_TESTING error
@ -874,14 +918,13 @@ static int tpm2_start_selftest(struct tpm_chip *chip, bool full)
/** /**
* tpm2_do_selftest() - run a full self test * tpm2_do_selftest() - run a full self test
*
* @chip: TPM chip to use * @chip: TPM chip to use
* *
* Return: Same as with tpm_transmit_cmd.
*
* During the self test TPM2 commands return with the error code RC_TESTING. * During the self test TPM2 commands return with the error code RC_TESTING.
* Waiting is done by issuing PCR read until it executes successfully. * Waiting is done by issuing PCR read until it executes successfully.
*
* 0 is returned when the operation is successful. If a negative number is
* returned it remarks a POSIX error code. If a positive number is returned
* it remarks a TPM error.
*/ */
static int tpm2_do_selftest(struct tpm_chip *chip) static int tpm2_do_selftest(struct tpm_chip *chip)
{ {
@ -910,7 +953,7 @@ static int tpm2_do_selftest(struct tpm_chip *chip)
cmd.params.pcrread_in.pcr_select[1] = 0x00; cmd.params.pcrread_in.pcr_select[1] = 0x00;
cmd.params.pcrread_in.pcr_select[2] = 0x00; cmd.params.pcrread_in.pcr_select[2] = 0x00;
rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), 0, NULL); rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), 0, 0, NULL);
if (rc < 0) if (rc < 0)
break; break;
@ -928,6 +971,8 @@ static int tpm2_do_selftest(struct tpm_chip *chip)
* tpm2_probe() - probe TPM 2.0 * tpm2_probe() - probe TPM 2.0
* @chip: TPM chip to use * @chip: TPM chip to use
* *
* Return: < 0 error and 0 on success.
*
* Send idempotent TPM 2.0 command and see whether TPM 2.0 chip replied based on * Send idempotent TPM 2.0 command and see whether TPM 2.0 chip replied based on
* the reply tag. * the reply tag.
*/ */
@ -941,7 +986,7 @@ int tpm2_probe(struct tpm_chip *chip)
cmd.params.get_tpm_pt_in.property_id = cpu_to_be32(0x100); cmd.params.get_tpm_pt_in.property_id = cpu_to_be32(0x100);
cmd.params.get_tpm_pt_in.property_cnt = cpu_to_be32(1); cmd.params.get_tpm_pt_in.property_cnt = cpu_to_be32(1);
rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), 0, NULL); rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), 0, 0, NULL);
if (rc < 0) if (rc < 0)
return rc; return rc;
@ -952,12 +997,85 @@ int tpm2_probe(struct tpm_chip *chip)
} }
EXPORT_SYMBOL_GPL(tpm2_probe); EXPORT_SYMBOL_GPL(tpm2_probe);
struct tpm2_pcr_selection {
__be16 hash_alg;
u8 size_of_select;
u8 pcr_select[3];
} __packed;
static ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip)
{
struct tpm2_pcr_selection pcr_selection;
struct tpm_buf buf;
void *marker;
void *end;
void *pcr_select_offset;
unsigned int count;
u32 sizeof_pcr_selection;
u32 rsp_len;
int rc;
int i = 0;
rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
if (rc)
return rc;
tpm_buf_append_u32(&buf, TPM2_CAP_PCRS);
tpm_buf_append_u32(&buf, 0);
tpm_buf_append_u32(&buf, 1);
rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, 9, 0,
"get tpm pcr allocation");
if (rc)
goto out;
count = be32_to_cpup(
(__be32 *)&buf.data[TPM_HEADER_SIZE + 5]);
if (count > ARRAY_SIZE(chip->active_banks)) {
rc = -ENODEV;
goto out;
}
marker = &buf.data[TPM_HEADER_SIZE + 9];
rsp_len = be32_to_cpup((__be32 *)&buf.data[2]);
end = &buf.data[rsp_len];
for (i = 0; i < count; i++) {
pcr_select_offset = marker +
offsetof(struct tpm2_pcr_selection, size_of_select);
if (pcr_select_offset >= end) {
rc = -EFAULT;
break;
}
memcpy(&pcr_selection, marker, sizeof(pcr_selection));
chip->active_banks[i] = be16_to_cpu(pcr_selection.hash_alg);
sizeof_pcr_selection = sizeof(pcr_selection.hash_alg) +
sizeof(pcr_selection.size_of_select) +
pcr_selection.size_of_select;
marker = marker + sizeof_pcr_selection;
}
out:
if (i < ARRAY_SIZE(chip->active_banks))
chip->active_banks[i] = TPM2_ALG_ERROR;
tpm_buf_destroy(&buf);
return rc;
}
/** /**
* tpm2_auto_startup - Perform the standard automatic TPM initialization * tpm2_auto_startup - Perform the standard automatic TPM initialization
* sequence * sequence
* @chip: TPM chip to use * @chip: TPM chip to use
* *
* Returns 0 on success, < 0 in case of fatal error. * Initializes timeout values for operation and command durations, conducts
* a self-test and reads the list of active PCR banks.
*
* Return: 0 on success. Otherwise, a system error code is returned.
*/ */
int tpm2_auto_startup(struct tpm_chip *chip) int tpm2_auto_startup(struct tpm_chip *chip)
{ {
@ -985,6 +1103,8 @@ int tpm2_auto_startup(struct tpm_chip *chip)
} }
} }
rc = tpm2_get_pcr_allocation(chip);
out: out:
if (rc > 0) if (rc > 0)
rc = -ENODEV; rc = -ENODEV;

View File

@ -0,0 +1,203 @@
/*
* Copyright (C) 2016 IBM Corporation
*
* Authors:
* Nayna Jain <nayna@linux.vnet.ibm.com>
*
* Access to TPM 2.0 event log as written by Firmware.
* It assumes that writer of event log has followed TCG Specification
* for Family "2.0" and written the event data in little endian.
* With that, it doesn't need any endian conversion for structure
* content.
*
* 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; either version
* 2 of the License, or (at your option) any later version.
*/
#include <linux/seq_file.h>
#include <linux/fs.h>
#include <linux/security.h>
#include <linux/module.h>
#include <linux/slab.h>
#include "tpm.h"
#include "tpm_eventlog.h"
/*
* calc_tpm2_event_size() - calculate the event size, where event
* is an entry in the TPM 2.0 event log. The event is of type Crypto
* Agile Log Entry Format as defined in TCG EFI Protocol Specification
* Family "2.0".
* @event: event whose size is to be calculated.
* @event_header: the first event in the event log.
*
* Returns size of the event. If it is an invalid event, returns 0.
*/
static int calc_tpm2_event_size(struct tcg_pcr_event2 *event,
struct tcg_pcr_event *event_header)
{
struct tcg_efi_specid_event *efispecid;
struct tcg_event_field *event_field;
void *marker;
void *marker_start;
u32 halg_size;
size_t size;
u16 halg;
int i;
int j;
marker = event;
marker_start = marker;
marker = marker + sizeof(event->pcr_idx) + sizeof(event->event_type)
+ sizeof(event->count);
efispecid = (struct tcg_efi_specid_event *)event_header->event;
for (i = 0; (i < event->count) && (i < TPM2_ACTIVE_PCR_BANKS);
i++) {
halg_size = sizeof(event->digests[i].alg_id);
memcpy(&halg, marker, halg_size);
marker = marker + halg_size;
for (j = 0; (j < efispecid->num_algs); j++) {
if (halg == efispecid->digest_sizes[j].alg_id) {
marker = marker +
efispecid->digest_sizes[j].digest_size;
break;
}
}
}
event_field = (struct tcg_event_field *)marker;
marker = marker + sizeof(event_field->event_size)
+ event_field->event_size;
size = marker - marker_start;
if ((event->event_type == 0) && (event_field->event_size == 0))
return 0;
return size;
}
static void *tpm2_bios_measurements_start(struct seq_file *m, loff_t *pos)
{
struct tpm_chip *chip = m->private;
struct tpm_bios_log *log = &chip->log;
void *addr = log->bios_event_log;
void *limit = log->bios_event_log_end;
struct tcg_pcr_event *event_header;
struct tcg_pcr_event2 *event;
size_t size;
int i;
event_header = addr;
size = sizeof(struct tcg_pcr_event) - sizeof(event_header->event)
+ event_header->event_size;
if (*pos == 0) {
if (addr + size < limit) {
if ((event_header->event_type == 0) &&
(event_header->event_size == 0))
return NULL;
return SEQ_START_TOKEN;
}
}
if (*pos > 0) {
addr += size;
event = addr;
size = calc_tpm2_event_size(event, event_header);
if ((addr + size >= limit) || (size == 0))
return NULL;
}
for (i = 0; i < (*pos - 1); i++) {
event = addr;
size = calc_tpm2_event_size(event, event_header);
if ((addr + size >= limit) || (size == 0))
return NULL;
addr += size;
}
return addr;
}
static void *tpm2_bios_measurements_next(struct seq_file *m, void *v,
loff_t *pos)
{
struct tcg_pcr_event *event_header;
struct tcg_pcr_event2 *event;
struct tpm_chip *chip = m->private;
struct tpm_bios_log *log = &chip->log;
void *limit = log->bios_event_log_end;
size_t event_size;
void *marker;
event_header = log->bios_event_log;
if (v == SEQ_START_TOKEN) {
event_size = sizeof(struct tcg_pcr_event) -
sizeof(event_header->event) + event_header->event_size;
marker = event_header;
} else {
event = v;
event_size = calc_tpm2_event_size(event, event_header);
if (event_size == 0)
return NULL;
marker = event;
}
marker = marker + event_size;
if (marker >= limit)
return NULL;
v = marker;
event = v;
event_size = calc_tpm2_event_size(event, event_header);
if (((v + event_size) >= limit) || (event_size == 0))
return NULL;
(*pos)++;
return v;
}
static void tpm2_bios_measurements_stop(struct seq_file *m, void *v)
{
}
static int tpm2_binary_bios_measurements_show(struct seq_file *m, void *v)
{
struct tpm_chip *chip = m->private;
struct tpm_bios_log *log = &chip->log;
struct tcg_pcr_event *event_header = log->bios_event_log;
struct tcg_pcr_event2 *event = v;
void *temp_ptr;
size_t size;
if (v == SEQ_START_TOKEN) {
size = sizeof(struct tcg_pcr_event) -
sizeof(event_header->event) + event_header->event_size;
temp_ptr = event_header;
if (size > 0)
seq_write(m, temp_ptr, size);
} else {
size = calc_tpm2_event_size(event, event_header);
temp_ptr = event;
if (size > 0)
seq_write(m, temp_ptr, size);
}
return 0;
}
const struct seq_operations tpm2_binary_b_measurements_seqops = {
.start = tpm2_bios_measurements_start,
.next = tpm2_bios_measurements_next,
.stop = tpm2_bios_measurements_stop,
.show = tpm2_binary_bios_measurements_show,
};

View File

@ -54,6 +54,9 @@ int tpm_read_log_acpi(struct tpm_chip *chip)
u64 len, start; u64 len, start;
struct tpm_bios_log *log; struct tpm_bios_log *log;
if (chip->flags & TPM_CHIP_FLAG_TPM2)
return -ENODEV;
log = &chip->log; log = &chip->log;
/* Unfortuntely ACPI does not associate the event log with a specific /* Unfortuntely ACPI does not associate the event log with a specific

View File

@ -96,6 +96,12 @@ enum tpm_atmel_addr {
TPM_ATMEL_BASE_ADDR_HI = 0x09 TPM_ATMEL_BASE_ADDR_HI = 0x09
}; };
static inline int tpm_read_index(int base, int index)
{
outb(index, base);
return inb(base+1) & 0xFF;
}
/* Verify this is a 1.1 Atmel TPM */ /* Verify this is a 1.1 Atmel TPM */
static int atmel_verify_tpm11(void) static int atmel_verify_tpm11(void)
{ {

View File

@ -264,10 +264,12 @@ static const struct tpm_class_ops tpm_crb = {
static int crb_check_resource(struct acpi_resource *ares, void *data) static int crb_check_resource(struct acpi_resource *ares, void *data)
{ {
struct resource *io_res = data; struct resource *io_res = data;
struct resource res; struct resource_win win;
struct resource *res = &(win.res);
if (acpi_dev_resource_memory(ares, &res)) { if (acpi_dev_resource_memory(ares, res) ||
*io_res = res; acpi_dev_resource_address_space(ares, &win)) {
*io_res = *res;
io_res->name = NULL; io_res->name = NULL;
} }

View File

@ -2,9 +2,12 @@
#ifndef __TPM_EVENTLOG_H__ #ifndef __TPM_EVENTLOG_H__
#define __TPM_EVENTLOG_H__ #define __TPM_EVENTLOG_H__
#include <crypto/hash_info.h>
#define TCG_EVENT_NAME_LEN_MAX 255 #define TCG_EVENT_NAME_LEN_MAX 255
#define MAX_TEXT_EVENT 1000 /* Max event string length */ #define MAX_TEXT_EVENT 1000 /* Max event string length */
#define ACPI_TCPA_SIG "TCPA" /* 0x41504354 /'TCPA' */ #define ACPI_TCPA_SIG "TCPA" /* 0x41504354 /'TCPA' */
#define TPM2_ACTIVE_PCR_BANKS 3
#ifdef CONFIG_PPC64 #ifdef CONFIG_PPC64
#define do_endian_conversion(x) be32_to_cpu(x) #define do_endian_conversion(x) be32_to_cpu(x)
@ -17,11 +20,6 @@ enum bios_platform_class {
BIOS_SERVER = 0x01, BIOS_SERVER = 0x01,
}; };
struct tpm_bios_log {
void *bios_event_log;
void *bios_event_log_end;
};
struct tcpa_event { struct tcpa_event {
u32 pcr_index; u32 pcr_index;
u32 event_type; u32 event_type;
@ -73,6 +71,49 @@ enum tcpa_pc_event_ids {
HOST_TABLE_OF_DEVICES, HOST_TABLE_OF_DEVICES,
}; };
/* http://www.trustedcomputinggroup.org/tcg-efi-protocol-specification/ */
struct tcg_efi_specid_event_algs {
u16 alg_id;
u16 digest_size;
} __packed;
struct tcg_efi_specid_event {
u8 signature[16];
u32 platform_class;
u8 spec_version_minor;
u8 spec_version_major;
u8 spec_errata;
u8 uintnsize;
u32 num_algs;
struct tcg_efi_specid_event_algs digest_sizes[TPM2_ACTIVE_PCR_BANKS];
u8 vendor_info_size;
u8 vendor_info[0];
} __packed;
struct tcg_pcr_event {
u32 pcr_idx;
u32 event_type;
u8 digest[20];
u32 event_size;
u8 event[0];
} __packed;
struct tcg_event_field {
u32 event_size;
u8 event[0];
} __packed;
struct tcg_pcr_event2 {
u32 pcr_idx;
u32 event_type;
u32 count;
struct tpm2_digest digests[TPM2_ACTIVE_PCR_BANKS];
struct tcg_event_field event;
} __packed;
extern const struct seq_operations tpm2_binary_b_measurements_seqops;
#if defined(CONFIG_ACPI) #if defined(CONFIG_ACPI)
int tpm_read_log_acpi(struct tpm_chip *chip); int tpm_read_log_acpi(struct tpm_chip *chip);
#else #else

View File

@ -40,11 +40,12 @@ MODULE_DEVICE_TABLE(vio, tpm_ibmvtpm_device_table);
/** /**
* ibmvtpm_send_crq - Send a CRQ request * ibmvtpm_send_crq - Send a CRQ request
*
* @vdev: vio device struct * @vdev: vio device struct
* @w1: first word * @w1: first word
* @w2: second word * @w2: second word
* *
* Return value: * Return:
* 0 -Sucess * 0 -Sucess
* Non-zero - Failure * Non-zero - Failure
*/ */
@ -55,11 +56,12 @@ static int ibmvtpm_send_crq(struct vio_dev *vdev, u64 w1, u64 w2)
/** /**
* tpm_ibmvtpm_recv - Receive data after send * tpm_ibmvtpm_recv - Receive data after send
*
* @chip: tpm chip struct * @chip: tpm chip struct
* @buf: buffer to read * @buf: buffer to read
* count: size of buffer * @count: size of buffer
* *
* Return value: * Return:
* Number of bytes read * Number of bytes read
*/ */
static int tpm_ibmvtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count) static int tpm_ibmvtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count)
@ -96,12 +98,13 @@ static int tpm_ibmvtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count)
/** /**
* tpm_ibmvtpm_send - Send tpm request * tpm_ibmvtpm_send - Send tpm request
*
* @chip: tpm chip struct * @chip: tpm chip struct
* @buf: buffer contains data to send * @buf: buffer contains data to send
* count: size of buffer * @count: size of buffer
* *
* Return value: * Return:
* Number of bytes sent * Number of bytes sent or < 0 on error.
*/ */
static int tpm_ibmvtpm_send(struct tpm_chip *chip, u8 *buf, size_t count) static int tpm_ibmvtpm_send(struct tpm_chip *chip, u8 *buf, size_t count)
{ {
@ -170,11 +173,12 @@ static u8 tpm_ibmvtpm_status(struct tpm_chip *chip)
/** /**
* ibmvtpm_crq_get_rtce_size - Send a CRQ request to get rtce size * ibmvtpm_crq_get_rtce_size - Send a CRQ request to get rtce size
*
* @ibmvtpm: vtpm device struct * @ibmvtpm: vtpm device struct
* *
* Return value: * Return:
* 0 - Success * 0 on success.
* Non-zero - Failure * Non-zero on failure.
*/ */
static int ibmvtpm_crq_get_rtce_size(struct ibmvtpm_dev *ibmvtpm) static int ibmvtpm_crq_get_rtce_size(struct ibmvtpm_dev *ibmvtpm)
{ {
@ -197,11 +201,12 @@ static int ibmvtpm_crq_get_rtce_size(struct ibmvtpm_dev *ibmvtpm)
/** /**
* ibmvtpm_crq_get_version - Send a CRQ request to get vtpm version * ibmvtpm_crq_get_version - Send a CRQ request to get vtpm version
* - Note that this is vtpm version and not tpm version * - Note that this is vtpm version and not tpm version
*
* @ibmvtpm: vtpm device struct * @ibmvtpm: vtpm device struct
* *
* Return value: * Return:
* 0 - Success * 0 on success.
* Non-zero - Failure * Non-zero on failure.
*/ */
static int ibmvtpm_crq_get_version(struct ibmvtpm_dev *ibmvtpm) static int ibmvtpm_crq_get_version(struct ibmvtpm_dev *ibmvtpm)
{ {
@ -225,9 +230,9 @@ static int ibmvtpm_crq_get_version(struct ibmvtpm_dev *ibmvtpm)
* ibmvtpm_crq_send_init_complete - Send a CRQ initialize complete message * ibmvtpm_crq_send_init_complete - Send a CRQ initialize complete message
* @ibmvtpm: vtpm device struct * @ibmvtpm: vtpm device struct
* *
* Return value: * Return:
* 0 - Success * 0 on success.
* Non-zero - Failure * Non-zero on failure.
*/ */
static int ibmvtpm_crq_send_init_complete(struct ibmvtpm_dev *ibmvtpm) static int ibmvtpm_crq_send_init_complete(struct ibmvtpm_dev *ibmvtpm)
{ {
@ -245,9 +250,9 @@ static int ibmvtpm_crq_send_init_complete(struct ibmvtpm_dev *ibmvtpm)
* ibmvtpm_crq_send_init - Send a CRQ initialize message * ibmvtpm_crq_send_init - Send a CRQ initialize message
* @ibmvtpm: vtpm device struct * @ibmvtpm: vtpm device struct
* *
* Return value: * Return:
* 0 - Success * 0 on success.
* Non-zero - Failure * Non-zero on failure.
*/ */
static int ibmvtpm_crq_send_init(struct ibmvtpm_dev *ibmvtpm) static int ibmvtpm_crq_send_init(struct ibmvtpm_dev *ibmvtpm)
{ {
@ -265,8 +270,7 @@ static int ibmvtpm_crq_send_init(struct ibmvtpm_dev *ibmvtpm)
* tpm_ibmvtpm_remove - ibm vtpm remove entry point * tpm_ibmvtpm_remove - ibm vtpm remove entry point
* @vdev: vio device struct * @vdev: vio device struct
* *
* Return value: * Return: Always 0.
* 0
*/ */
static int tpm_ibmvtpm_remove(struct vio_dev *vdev) static int tpm_ibmvtpm_remove(struct vio_dev *vdev)
{ {
@ -303,18 +307,19 @@ static int tpm_ibmvtpm_remove(struct vio_dev *vdev)
* tpm_ibmvtpm_get_desired_dma - Get DMA size needed by this driver * tpm_ibmvtpm_get_desired_dma - Get DMA size needed by this driver
* @vdev: vio device struct * @vdev: vio device struct
* *
* Return value: * Return:
* Number of bytes the driver needs to DMA map * Number of bytes the driver needs to DMA map.
*/ */
static unsigned long tpm_ibmvtpm_get_desired_dma(struct vio_dev *vdev) static unsigned long tpm_ibmvtpm_get_desired_dma(struct vio_dev *vdev)
{ {
struct tpm_chip *chip = dev_get_drvdata(&vdev->dev); struct tpm_chip *chip = dev_get_drvdata(&vdev->dev);
struct ibmvtpm_dev *ibmvtpm = dev_get_drvdata(&chip->dev); struct ibmvtpm_dev *ibmvtpm = dev_get_drvdata(&chip->dev);
/* ibmvtpm initializes at probe time, so the data we are /*
* asking for may not be set yet. Estimate that 4K required * ibmvtpm initializes at probe time, so the data we are
* for TCE-mapped buffer in addition to CRQ. * asking for may not be set yet. Estimate that 4K required
*/ * for TCE-mapped buffer in addition to CRQ.
*/
if (!ibmvtpm) if (!ibmvtpm)
return CRQ_RES_BUF_SIZE + PAGE_SIZE; return CRQ_RES_BUF_SIZE + PAGE_SIZE;
@ -325,8 +330,7 @@ static unsigned long tpm_ibmvtpm_get_desired_dma(struct vio_dev *vdev)
* tpm_ibmvtpm_suspend - Suspend * tpm_ibmvtpm_suspend - Suspend
* @dev: device struct * @dev: device struct
* *
* Return value: * Return: Always 0.
* 0
*/ */
static int tpm_ibmvtpm_suspend(struct device *dev) static int tpm_ibmvtpm_suspend(struct device *dev)
{ {
@ -350,11 +354,12 @@ static int tpm_ibmvtpm_suspend(struct device *dev)
/** /**
* ibmvtpm_reset_crq - Reset CRQ * ibmvtpm_reset_crq - Reset CRQ
*
* @ibmvtpm: ibm vtpm struct * @ibmvtpm: ibm vtpm struct
* *
* Return value: * Return:
* 0 - Success * 0 on success.
* Non-zero - Failure * Non-zero on failure.
*/ */
static int ibmvtpm_reset_crq(struct ibmvtpm_dev *ibmvtpm) static int ibmvtpm_reset_crq(struct ibmvtpm_dev *ibmvtpm)
{ {
@ -376,10 +381,10 @@ static int ibmvtpm_reset_crq(struct ibmvtpm_dev *ibmvtpm)
/** /**
* tpm_ibmvtpm_resume - Resume from suspend * tpm_ibmvtpm_resume - Resume from suspend
*
* @dev: device struct * @dev: device struct
* *
* Return value: * Return: Always 0.
* 0
*/ */
static int tpm_ibmvtpm_resume(struct device *dev) static int tpm_ibmvtpm_resume(struct device *dev)
{ {
@ -434,10 +439,10 @@ static const struct dev_pm_ops tpm_ibmvtpm_pm_ops = {
/** /**
* ibmvtpm_crq_get_next - Get next responded crq * ibmvtpm_crq_get_next - Get next responded crq
* @ibmvtpm vtpm device struct
* *
* Return value: * @ibmvtpm: vtpm device struct
* vtpm crq pointer *
* Return: vtpm crq pointer or NULL.
*/ */
static struct ibmvtpm_crq *ibmvtpm_crq_get_next(struct ibmvtpm_dev *ibmvtpm) static struct ibmvtpm_crq *ibmvtpm_crq_get_next(struct ibmvtpm_dev *ibmvtpm)
{ {
@ -455,11 +460,10 @@ static struct ibmvtpm_crq *ibmvtpm_crq_get_next(struct ibmvtpm_dev *ibmvtpm)
/** /**
* ibmvtpm_crq_process - Process responded crq * ibmvtpm_crq_process - Process responded crq
* @crq crq to be processed
* @ibmvtpm vtpm device struct
* *
* Return value: * @crq: crq to be processed
* Nothing * @ibmvtpm: vtpm device struct
*
*/ */
static void ibmvtpm_crq_process(struct ibmvtpm_crq *crq, static void ibmvtpm_crq_process(struct ibmvtpm_crq *crq,
struct ibmvtpm_dev *ibmvtpm) struct ibmvtpm_dev *ibmvtpm)
@ -528,6 +532,7 @@ static void ibmvtpm_crq_process(struct ibmvtpm_crq *crq,
/** /**
* ibmvtpm_interrupt - Interrupt handler * ibmvtpm_interrupt - Interrupt handler
*
* @irq: irq number to handle * @irq: irq number to handle
* @vtpm_instance: vtpm that received interrupt * @vtpm_instance: vtpm that received interrupt
* *
@ -554,12 +559,13 @@ static irqreturn_t ibmvtpm_interrupt(int irq, void *vtpm_instance)
/** /**
* tpm_ibmvtpm_probe - ibm vtpm initialize entry point * tpm_ibmvtpm_probe - ibm vtpm initialize entry point
*
* @vio_dev: vio device struct * @vio_dev: vio device struct
* @id: vio device id struct * @id: vio device id struct
* *
* Return value: * Return:
* 0 - Success * 0 on success.
* Non-zero - Failure * Non-zero on failure.
*/ */
static int tpm_ibmvtpm_probe(struct vio_dev *vio_dev, static int tpm_ibmvtpm_probe(struct vio_dev *vio_dev,
const struct vio_device_id *id) const struct vio_device_id *id)
@ -671,11 +677,12 @@ static struct vio_driver ibmvtpm_driver = {
}; };
/** /**
* ibmvtpm_module_init - Initialize ibm vtpm module * ibmvtpm_module_init - Initialize ibm vtpm module.
* *
* Return value: *
* 0 -Success * Return:
* Non-zero - Failure * 0 on success.
* Non-zero on failure.
*/ */
static int __init ibmvtpm_module_init(void) static int __init ibmvtpm_module_init(void)
{ {
@ -683,10 +690,7 @@ static int __init ibmvtpm_module_init(void)
} }
/** /**
* ibmvtpm_module_exit - Teardown ibm vtpm module * ibmvtpm_module_exit - Tear down ibm vtpm module.
*
* Return value:
* Nothing
*/ */
static void __exit ibmvtpm_module_exit(void) static void __exit ibmvtpm_module_exit(void)
{ {

View File

@ -278,6 +278,18 @@ static struct platform_driver nsc_drv = {
}, },
}; };
static inline int tpm_read_index(int base, int index)
{
outb(index, base);
return inb(base+1) & 0xFF;
}
static inline void tpm_write_index(int base, int index, int value)
{
outb(index, base);
outb(value & 0xFF, base+1);
}
static int __init init_nsc(void) static int __init init_nsc(void)
{ {
int rc = 0; int rc = 0;

View File

@ -27,6 +27,8 @@ int tpm_read_log_of(struct tpm_chip *chip)
const u32 *sizep; const u32 *sizep;
const u64 *basep; const u64 *basep;
struct tpm_bios_log *log; struct tpm_bios_log *log;
u32 size;
u64 base;
log = &chip->log; log = &chip->log;
if (chip->dev.parent && chip->dev.parent->of_node) if (chip->dev.parent && chip->dev.parent->of_node)
@ -41,18 +43,35 @@ int tpm_read_log_of(struct tpm_chip *chip)
if (sizep == NULL || basep == NULL) if (sizep == NULL || basep == NULL)
return -EIO; return -EIO;
if (*sizep == 0) { /*
* For both vtpm/tpm, firmware has log addr and log size in big
* endian format. But in case of vtpm, there is a method called
* sml-handover which is run during kernel init even before
* device tree is setup. This sml-handover function takes care
* of endianness and writes to sml-base and sml-size in little
* endian format. For this reason, vtpm doesn't need conversion
* but physical tpm needs the conversion.
*/
if (of_property_match_string(np, "compatible", "IBM,vtpm") < 0) {
size = be32_to_cpup(sizep);
base = be64_to_cpup(basep);
} else {
size = *sizep;
base = *basep;
}
if (size == 0) {
dev_warn(&chip->dev, "%s: Event log area empty\n", __func__); dev_warn(&chip->dev, "%s: Event log area empty\n", __func__);
return -EIO; return -EIO;
} }
log->bios_event_log = kmalloc(*sizep, GFP_KERNEL); log->bios_event_log = kmalloc(size, GFP_KERNEL);
if (!log->bios_event_log) if (!log->bios_event_log)
return -ENOMEM; return -ENOMEM;
log->bios_event_log_end = log->bios_event_log + *sizep; log->bios_event_log_end = log->bios_event_log + size;
memcpy(log->bios_event_log, __va(*basep), *sizep); memcpy(log->bios_event_log, __va(base), size);
return 0; return 0;
} }

View File

@ -159,7 +159,7 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info,
irq = tpm_info->irq; irq = tpm_info->irq;
if (itpm) if (itpm)
phy->priv.flags |= TPM_TIS_ITPM_POSSIBLE; phy->priv.flags |= TPM_TIS_ITPM_WORKAROUND;
return tpm_tis_core_init(dev, &phy->priv, irq, &tpm_tcg, return tpm_tis_core_init(dev, &phy->priv, irq, &tpm_tcg,
acpi_dev_handle); acpi_dev_handle);
@ -432,7 +432,7 @@ err_pnp:
acpi_bus_unregister_driver(&tis_acpi_driver); acpi_bus_unregister_driver(&tis_acpi_driver);
err_acpi: err_acpi:
#endif #endif
platform_device_unregister(force_pdev); platform_driver_unregister(&tis_drv);
err_platform: err_platform:
if (force_pdev) if (force_pdev)
platform_device_unregister(force_pdev); platform_device_unregister(force_pdev);

View File

@ -264,7 +264,7 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len)
struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
int rc, status, burstcnt; int rc, status, burstcnt;
size_t count = 0; size_t count = 0;
bool itpm = priv->flags & TPM_TIS_ITPM_POSSIBLE; bool itpm = priv->flags & TPM_TIS_ITPM_WORKAROUND;
if (request_locality(chip, 0) < 0) if (request_locality(chip, 0) < 0)
return -EBUSY; return -EBUSY;
@ -464,6 +464,9 @@ static int probe_itpm(struct tpm_chip *chip)
size_t len = sizeof(cmd_getticks); size_t len = sizeof(cmd_getticks);
u16 vendor; u16 vendor;
if (priv->flags & TPM_TIS_ITPM_WORKAROUND)
return 0;
rc = tpm_tis_read16(priv, TPM_DID_VID(0), &vendor); rc = tpm_tis_read16(priv, TPM_DID_VID(0), &vendor);
if (rc < 0) if (rc < 0)
return rc; return rc;
@ -479,12 +482,15 @@ static int probe_itpm(struct tpm_chip *chip)
tpm_tis_ready(chip); tpm_tis_ready(chip);
release_locality(chip, priv->locality, 0); release_locality(chip, priv->locality, 0);
priv->flags |= TPM_TIS_ITPM_WORKAROUND;
rc = tpm_tis_send_data(chip, cmd_getticks, len); rc = tpm_tis_send_data(chip, cmd_getticks, len);
if (rc == 0) { if (rc == 0)
dev_info(&chip->dev, "Detected an iTPM.\n"); dev_info(&chip->dev, "Detected an iTPM.\n");
rc = 1; else {
} else priv->flags &= ~TPM_TIS_ITPM_WORKAROUND;
rc = -EFAULT; rc = -EFAULT;
}
out: out:
tpm_tis_ready(chip); tpm_tis_ready(chip);
@ -552,7 +558,8 @@ static int tpm_tis_gen_interrupt(struct tpm_chip *chip)
if (chip->flags & TPM_CHIP_FLAG_TPM2) if (chip->flags & TPM_CHIP_FLAG_TPM2)
return tpm2_get_tpm_pt(chip, 0x100, &cap2, desc); return tpm2_get_tpm_pt(chip, 0x100, &cap2, desc);
else else
return tpm_getcap(chip, TPM_CAP_PROP_TIS_TIMEOUT, &cap, desc); return tpm_getcap(chip, TPM_CAP_PROP_TIS_TIMEOUT, &cap, desc,
0);
} }
/* Register the IRQ and issue a command that will cause an interrupt. If an /* Register the IRQ and issue a command that will cause an interrupt. If an
@ -740,15 +747,10 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq,
(chip->flags & TPM_CHIP_FLAG_TPM2) ? "2.0" : "1.2", (chip->flags & TPM_CHIP_FLAG_TPM2) ? "2.0" : "1.2",
vendor >> 16, rid); vendor >> 16, rid);
if (!(priv->flags & TPM_TIS_ITPM_POSSIBLE)) { probe = probe_itpm(chip);
probe = probe_itpm(chip); if (probe < 0) {
if (probe < 0) { rc = -ENODEV;
rc = -ENODEV; goto out_err;
goto out_err;
}
if (!!probe)
priv->flags |= TPM_TIS_ITPM_POSSIBLE;
} }
/* Figure out the capabilities */ /* Figure out the capabilities */

View File

@ -80,7 +80,7 @@ enum tis_defaults {
#define TPM_RID(l) (0x0F04 | ((l) << 12)) #define TPM_RID(l) (0x0F04 | ((l) << 12))
enum tpm_tis_flags { enum tpm_tis_flags {
TPM_TIS_ITPM_POSSIBLE = BIT(0), TPM_TIS_ITPM_WORKAROUND = BIT(0),
}; };
struct tpm_tis_data { struct tpm_tis_data {

View File

@ -33,7 +33,6 @@
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/freezer.h> #include <linux/freezer.h>
#include <linux/module.h>
#include <linux/spi/spi.h> #include <linux/spi/spi.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/of_irq.h> #include <linux/of_irq.h>

View File

@ -65,7 +65,12 @@ static void vtpm_proxy_delete_device(struct proxy_dev *proxy_dev);
/** /**
* vtpm_proxy_fops_read - Read TPM commands on 'server side' * vtpm_proxy_fops_read - Read TPM commands on 'server side'
* *
* Return value: * @filp: file pointer
* @buf: read buffer
* @count: number of bytes to read
* @off: offset
*
* Return:
* Number of bytes read or negative error code * Number of bytes read or negative error code
*/ */
static ssize_t vtpm_proxy_fops_read(struct file *filp, char __user *buf, static ssize_t vtpm_proxy_fops_read(struct file *filp, char __user *buf,
@ -115,7 +120,12 @@ static ssize_t vtpm_proxy_fops_read(struct file *filp, char __user *buf,
/** /**
* vtpm_proxy_fops_write - Write TPM responses on 'server side' * vtpm_proxy_fops_write - Write TPM responses on 'server side'
* *
* Return value: * @filp: file pointer
* @buf: write buffer
* @count: number of bytes to write
* @off: offset
*
* Return:
* Number of bytes read or negative error value * Number of bytes read or negative error value
*/ */
static ssize_t vtpm_proxy_fops_write(struct file *filp, const char __user *buf, static ssize_t vtpm_proxy_fops_write(struct file *filp, const char __user *buf,
@ -155,10 +165,12 @@ static ssize_t vtpm_proxy_fops_write(struct file *filp, const char __user *buf,
} }
/* /*
* vtpm_proxy_fops_poll: Poll status on 'server side' * vtpm_proxy_fops_poll - Poll status on 'server side'
* *
* Return value: * @filp: file pointer
* Poll flags * @wait: poll table
*
* Return: Poll flags
*/ */
static unsigned int vtpm_proxy_fops_poll(struct file *filp, poll_table *wait) static unsigned int vtpm_proxy_fops_poll(struct file *filp, poll_table *wait)
{ {
@ -185,6 +197,8 @@ static unsigned int vtpm_proxy_fops_poll(struct file *filp, poll_table *wait)
/* /*
* vtpm_proxy_fops_open - Open vTPM device on 'server side' * vtpm_proxy_fops_open - Open vTPM device on 'server side'
* *
* @filp: file pointer
*
* Called when setting up the anonymous file descriptor * Called when setting up the anonymous file descriptor
*/ */
static void vtpm_proxy_fops_open(struct file *filp) static void vtpm_proxy_fops_open(struct file *filp)
@ -196,8 +210,9 @@ static void vtpm_proxy_fops_open(struct file *filp)
/** /**
* vtpm_proxy_fops_undo_open - counter-part to vtpm_fops_open * vtpm_proxy_fops_undo_open - counter-part to vtpm_fops_open
* Call to undo vtpm_proxy_fops_open
* *
* Call to undo vtpm_proxy_fops_open *@proxy_dev: tpm proxy device
*/ */
static void vtpm_proxy_fops_undo_open(struct proxy_dev *proxy_dev) static void vtpm_proxy_fops_undo_open(struct proxy_dev *proxy_dev)
{ {
@ -212,9 +227,11 @@ static void vtpm_proxy_fops_undo_open(struct proxy_dev *proxy_dev)
} }
/* /*
* vtpm_proxy_fops_release: Close 'server side' * vtpm_proxy_fops_release - Close 'server side'
* *
* Return value: * @inode: inode
* @filp: file pointer
* Return:
* Always returns 0. * Always returns 0.
*/ */
static int vtpm_proxy_fops_release(struct inode *inode, struct file *filp) static int vtpm_proxy_fops_release(struct inode *inode, struct file *filp)
@ -245,7 +262,10 @@ static const struct file_operations vtpm_proxy_fops = {
/* /*
* Called when core TPM driver reads TPM responses from 'server side' * Called when core TPM driver reads TPM responses from 'server side'
* *
* Return value: * @chip: tpm chip to use
* @buf: receive buffer
* @count: bytes to read
* Return:
* Number of TPM response bytes read, negative error value otherwise * Number of TPM response bytes read, negative error value otherwise
*/ */
static int vtpm_proxy_tpm_op_recv(struct tpm_chip *chip, u8 *buf, size_t count) static int vtpm_proxy_tpm_op_recv(struct tpm_chip *chip, u8 *buf, size_t count)
@ -282,7 +302,11 @@ out:
/* /*
* Called when core TPM driver forwards TPM requests to 'server side'. * Called when core TPM driver forwards TPM requests to 'server side'.
* *
* Return value: * @chip: tpm chip to use
* @buf: send buffer
* @count: bytes to send
*
* Return:
* 0 in case of success, negative error value otherwise. * 0 in case of success, negative error value otherwise.
*/ */
static int vtpm_proxy_tpm_op_send(struct tpm_chip *chip, u8 *buf, size_t count) static int vtpm_proxy_tpm_op_send(struct tpm_chip *chip, u8 *buf, size_t count)
@ -442,7 +466,7 @@ static inline void vtpm_proxy_delete_proxy_dev(struct proxy_dev *proxy_dev)
/* /*
* Create a /dev/tpm%d and 'server side' file descriptor pair * Create a /dev/tpm%d and 'server side' file descriptor pair
* *
* Return value: * Return:
* Returns file pointer on success, an error value otherwise * Returns file pointer on success, an error value otherwise
*/ */
static struct file *vtpm_proxy_create_device( static struct file *vtpm_proxy_create_device(
@ -571,7 +595,7 @@ static long vtpmx_ioc_new_dev(struct file *file, unsigned int ioctl,
/* /*
* vtpmx_fops_ioctl: ioctl on /dev/vtpmx * vtpmx_fops_ioctl: ioctl on /dev/vtpmx
* *
* Return value: * Return:
* Returns 0 on success, a negative error code otherwise. * Returns 0 on success, a negative error code otherwise.
*/ */
static long vtpmx_fops_ioctl(struct file *f, unsigned int ioctl, static long vtpmx_fops_ioctl(struct file *f, unsigned int ioctl,

View File

@ -289,7 +289,6 @@ static int tpmfront_probe(struct xenbus_device *dev,
const struct xenbus_device_id *id) const struct xenbus_device_id *id)
{ {
struct tpm_private *priv; struct tpm_private *priv;
struct tpm_chip *chip;
int rv; int rv;
priv = kzalloc(sizeof(*priv), GFP_KERNEL); priv = kzalloc(sizeof(*priv), GFP_KERNEL);
@ -306,7 +305,6 @@ static int tpmfront_probe(struct xenbus_device *dev,
rv = setup_ring(dev, priv); rv = setup_ring(dev, priv);
if (rv) { if (rv) {
chip = dev_get_drvdata(&dev->dev);
ring_free(priv); ring_free(priv);
return rv; return rv;
} }

View File

@ -2488,6 +2488,12 @@ static ssize_t proc_pid_attr_write(struct file * file, const char __user * buf,
length = -ESRCH; length = -ESRCH;
if (!task) if (!task)
goto out_no_task; goto out_no_task;
/* A task may only write its own attributes. */
length = -EACCES;
if (current != task)
goto out;
if (count > PAGE_SIZE) if (count > PAGE_SIZE)
count = PAGE_SIZE; count = PAGE_SIZE;
@ -2503,14 +2509,13 @@ static ssize_t proc_pid_attr_write(struct file * file, const char __user * buf,
} }
/* Guard against adverse ptrace interaction */ /* Guard against adverse ptrace interaction */
length = mutex_lock_interruptible(&task->signal->cred_guard_mutex); length = mutex_lock_interruptible(&current->signal->cred_guard_mutex);
if (length < 0) if (length < 0)
goto out_free; goto out_free;
length = security_setprocattr(task, length = security_setprocattr(file->f_path.dentry->d_name.name,
(char*)file->f_path.dentry->d_name.name,
page, count); page, count);
mutex_unlock(&task->signal->cred_guard_mutex); mutex_unlock(&current->signal->cred_guard_mutex);
out_free: out_free:
kfree(page); kfree(page);
out: out:

View File

@ -352,8 +352,7 @@
* Return 0 if permission is granted. * Return 0 if permission is granted.
* @inode_getattr: * @inode_getattr:
* Check permission before obtaining file attributes. * Check permission before obtaining file attributes.
* @mnt is the vfsmount where the dentry was looked up * @path contains the path structure for the file.
* @dentry contains the dentry structure for the file.
* Return 0 if permission is granted. * Return 0 if permission is granted.
* @inode_setxattr: * @inode_setxattr:
* Check permission before setting the extended attributes * Check permission before setting the extended attributes
@ -666,11 +665,6 @@
* @sig contains the signal value. * @sig contains the signal value.
* @secid contains the sid of the process where the signal originated * @secid contains the sid of the process where the signal originated
* Return 0 if permission is granted. * Return 0 if permission is granted.
* @task_wait:
* Check permission before allowing a process to reap a child process @p
* and collect its status information.
* @p contains the task_struct for process.
* Return 0 if permission is granted.
* @task_prctl: * @task_prctl:
* Check permission before performing a process control operation on the * Check permission before performing a process control operation on the
* current process. * current process.
@ -1507,7 +1501,6 @@ union security_list_options {
int (*task_movememory)(struct task_struct *p); int (*task_movememory)(struct task_struct *p);
int (*task_kill)(struct task_struct *p, struct siginfo *info, int (*task_kill)(struct task_struct *p, struct siginfo *info,
int sig, u32 secid); int sig, u32 secid);
int (*task_wait)(struct task_struct *p);
int (*task_prctl)(int option, unsigned long arg2, unsigned long arg3, int (*task_prctl)(int option, unsigned long arg2, unsigned long arg3,
unsigned long arg4, unsigned long arg5); unsigned long arg4, unsigned long arg5);
void (*task_to_inode)(struct task_struct *p, struct inode *inode); void (*task_to_inode)(struct task_struct *p, struct inode *inode);
@ -1547,8 +1540,7 @@ union security_list_options {
void (*d_instantiate)(struct dentry *dentry, struct inode *inode); void (*d_instantiate)(struct dentry *dentry, struct inode *inode);
int (*getprocattr)(struct task_struct *p, char *name, char **value); int (*getprocattr)(struct task_struct *p, char *name, char **value);
int (*setprocattr)(struct task_struct *p, char *name, void *value, int (*setprocattr)(const char *name, void *value, size_t size);
size_t size);
int (*ismaclabel)(const char *name); int (*ismaclabel)(const char *name);
int (*secid_to_secctx)(u32 secid, char **secdata, u32 *seclen); int (*secid_to_secctx)(u32 secid, char **secdata, u32 *seclen);
int (*secctx_to_secid)(const char *secdata, u32 seclen, u32 *secid); int (*secctx_to_secid)(const char *secdata, u32 seclen, u32 *secid);
@ -1768,7 +1760,6 @@ struct security_hook_heads {
struct list_head task_getscheduler; struct list_head task_getscheduler;
struct list_head task_movememory; struct list_head task_movememory;
struct list_head task_kill; struct list_head task_kill;
struct list_head task_wait;
struct list_head task_prctl; struct list_head task_prctl;
struct list_head task_to_inode; struct list_head task_to_inode;
struct list_head ipc_permission; struct list_head ipc_permission;
@ -1876,6 +1867,7 @@ struct security_hook_list {
struct list_head list; struct list_head list;
struct list_head *head; struct list_head *head;
union security_list_options hook; union security_list_options hook;
char *lsm;
}; };
/* /*
@ -1888,15 +1880,10 @@ struct security_hook_list {
{ .head = &security_hook_heads.HEAD, .hook = { .HEAD = HOOK } } { .head = &security_hook_heads.HEAD, .hook = { .HEAD = HOOK } }
extern struct security_hook_heads security_hook_heads; extern struct security_hook_heads security_hook_heads;
extern char *lsm_names;
static inline void security_add_hooks(struct security_hook_list *hooks, extern void security_add_hooks(struct security_hook_list *hooks, int count,
int count) char *lsm);
{
int i;
for (i = 0; i < count; i++)
list_add_tail_rcu(&hooks[i].list, hooks[i].head);
}
#ifdef CONFIG_SECURITY_SELINUX_DISABLE #ifdef CONFIG_SECURITY_SELINUX_DISABLE
/* /*

View File

@ -332,7 +332,6 @@ int security_task_getscheduler(struct task_struct *p);
int security_task_movememory(struct task_struct *p); int security_task_movememory(struct task_struct *p);
int security_task_kill(struct task_struct *p, struct siginfo *info, int security_task_kill(struct task_struct *p, struct siginfo *info,
int sig, u32 secid); int sig, u32 secid);
int security_task_wait(struct task_struct *p);
int security_task_prctl(int option, unsigned long arg2, unsigned long arg3, int security_task_prctl(int option, unsigned long arg2, unsigned long arg3,
unsigned long arg4, unsigned long arg5); unsigned long arg4, unsigned long arg5);
void security_task_to_inode(struct task_struct *p, struct inode *inode); void security_task_to_inode(struct task_struct *p, struct inode *inode);
@ -361,7 +360,7 @@ int security_sem_semop(struct sem_array *sma, struct sembuf *sops,
unsigned nsops, int alter); unsigned nsops, int alter);
void security_d_instantiate(struct dentry *dentry, struct inode *inode); void security_d_instantiate(struct dentry *dentry, struct inode *inode);
int security_getprocattr(struct task_struct *p, char *name, char **value); int security_getprocattr(struct task_struct *p, char *name, char **value);
int security_setprocattr(struct task_struct *p, char *name, void *value, size_t size); int security_setprocattr(const char *name, void *value, size_t size);
int security_netlink_send(struct sock *sk, struct sk_buff *skb); int security_netlink_send(struct sock *sk, struct sk_buff *skb);
int security_ismaclabel(const char *name); int security_ismaclabel(const char *name);
int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen); int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen);
@ -980,11 +979,6 @@ static inline int security_task_kill(struct task_struct *p,
return 0; return 0;
} }
static inline int security_task_wait(struct task_struct *p)
{
return 0;
}
static inline int security_task_prctl(int option, unsigned long arg2, static inline int security_task_prctl(int option, unsigned long arg2,
unsigned long arg3, unsigned long arg3,
unsigned long arg4, unsigned long arg4,
@ -1106,7 +1100,7 @@ static inline int security_getprocattr(struct task_struct *p, char *name, char *
return -EINVAL; return -EINVAL;
} }
static inline int security_setprocattr(struct task_struct *p, char *name, void *value, size_t size) static inline int security_setprocattr(char *name, void *value, size_t size)
{ {
return -EINVAL; return -EINVAL;
} }

View File

@ -14,7 +14,6 @@
#include <linux/tty.h> #include <linux/tty.h>
#include <linux/iocontext.h> #include <linux/iocontext.h>
#include <linux/key.h> #include <linux/key.h>
#include <linux/security.h>
#include <linux/cpu.h> #include <linux/cpu.h>
#include <linux/acct.h> #include <linux/acct.h>
#include <linux/tsacct_kern.h> #include <linux/tsacct_kern.h>
@ -1390,7 +1389,7 @@ static int wait_task_continued(struct wait_opts *wo, struct task_struct *p)
* Returns nonzero for a final return, when we have unlocked tasklist_lock. * Returns nonzero for a final return, when we have unlocked tasklist_lock.
* Returns zero if the search for a child should continue; * Returns zero if the search for a child should continue;
* then ->notask_error is 0 if @p is an eligible child, * then ->notask_error is 0 if @p is an eligible child,
* or another error from security_task_wait(), or still -ECHILD. * or still -ECHILD.
*/ */
static int wait_consider_task(struct wait_opts *wo, int ptrace, static int wait_consider_task(struct wait_opts *wo, int ptrace,
struct task_struct *p) struct task_struct *p)
@ -1410,20 +1409,6 @@ static int wait_consider_task(struct wait_opts *wo, int ptrace,
if (!ret) if (!ret)
return ret; return ret;
ret = security_task_wait(p);
if (unlikely(ret < 0)) {
/*
* If we have not yet seen any eligible child,
* then let this error code replace -ECHILD.
* A permission error will give the user a clue
* to look for security policy problems, rather
* than for mysterious wait bugs.
*/
if (wo->notask_error)
wo->notask_error = ret;
return 0;
}
if (unlikely(exit_state == EXIT_TRACE)) { if (unlikely(exit_state == EXIT_TRACE)) {
/* /*
* ptrace == 0 means we are the natural parent. In this case * ptrace == 0 means we are the natural parent. In this case
@ -1516,7 +1501,7 @@ static int wait_consider_task(struct wait_opts *wo, int ptrace,
* Returns nonzero for a final return, when we have unlocked tasklist_lock. * Returns nonzero for a final return, when we have unlocked tasklist_lock.
* Returns zero if the search for a child should continue; then * Returns zero if the search for a child should continue; then
* ->notask_error is 0 if there were any eligible children, * ->notask_error is 0 if there were any eligible children,
* or another error from security_task_wait(), or still -ECHILD. * or still -ECHILD.
*/ */
static int do_wait_thread(struct wait_opts *wo, struct task_struct *tsk) static int do_wait_thread(struct wait_opts *wo, struct task_struct *tsk)
{ {

View File

@ -16,6 +16,7 @@
#include <linux/atomic.h> #include <linux/atomic.h>
#include <linux/audit.h> #include <linux/audit.h>
#include <linux/compat.h> #include <linux/compat.h>
#include <linux/coredump.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/seccomp.h> #include <linux/seccomp.h>
#include <linux/slab.h> #include <linux/slab.h>
@ -486,6 +487,17 @@ void put_seccomp_filter(struct task_struct *tsk)
} }
} }
static void seccomp_init_siginfo(siginfo_t *info, int syscall, int reason)
{
memset(info, 0, sizeof(*info));
info->si_signo = SIGSYS;
info->si_code = SYS_SECCOMP;
info->si_call_addr = (void __user *)KSTK_EIP(current);
info->si_errno = reason;
info->si_arch = syscall_get_arch();
info->si_syscall = syscall;
}
/** /**
* seccomp_send_sigsys - signals the task to allow in-process syscall emulation * seccomp_send_sigsys - signals the task to allow in-process syscall emulation
* @syscall: syscall number to send to userland * @syscall: syscall number to send to userland
@ -496,13 +508,7 @@ void put_seccomp_filter(struct task_struct *tsk)
static void seccomp_send_sigsys(int syscall, int reason) static void seccomp_send_sigsys(int syscall, int reason)
{ {
struct siginfo info; struct siginfo info;
memset(&info, 0, sizeof(info)); seccomp_init_siginfo(&info, syscall, reason);
info.si_signo = SIGSYS;
info.si_code = SYS_SECCOMP;
info.si_call_addr = (void __user *)KSTK_EIP(current);
info.si_errno = reason;
info.si_arch = syscall_get_arch();
info.si_syscall = syscall;
force_sig_info(SIGSYS, &info, current); force_sig_info(SIGSYS, &info, current);
} }
#endif /* CONFIG_SECCOMP_FILTER */ #endif /* CONFIG_SECCOMP_FILTER */
@ -634,10 +640,17 @@ static int __seccomp_filter(int this_syscall, const struct seccomp_data *sd,
return 0; return 0;
case SECCOMP_RET_KILL: case SECCOMP_RET_KILL:
default: default: {
siginfo_t info;
audit_seccomp(this_syscall, SIGSYS, action); audit_seccomp(this_syscall, SIGSYS, action);
/* Show the original registers in the dump. */
syscall_rollback(current, task_pt_regs(current));
/* Trigger a manual coredump since do_exit skips it. */
seccomp_init_siginfo(&info, this_syscall, data);
do_coredump(&info);
do_exit(SIGSYS); do_exit(SIGSYS);
} }
}
unreachable(); unreachable();

View File

@ -138,7 +138,7 @@ union arg64 {
#define ARG_32(idx) \ #define ARG_32(idx) \
BPF_STMT(BPF_LD+BPF_W+BPF_ABS, LO_ARG(idx)) BPF_STMT(BPF_LD+BPF_W+BPF_ABS, LO_ARG(idx))
/* Loads hi into A and lo in X */ /* Loads lo into M[0] and hi into M[1] and A */
#define ARG_64(idx) \ #define ARG_64(idx) \
BPF_STMT(BPF_LD+BPF_W+BPF_ABS, LO_ARG(idx)), \ BPF_STMT(BPF_LD+BPF_W+BPF_ABS, LO_ARG(idx)), \
BPF_STMT(BPF_ST, 0), /* lo -> M[0] */ \ BPF_STMT(BPF_ST, 0), /* lo -> M[0] */ \
@ -153,62 +153,14 @@ union arg64 {
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (value), 1, 0), \ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (value), 1, 0), \
jt jt
/* Checks the lo, then swaps to check the hi. A=lo,X=hi */
#define JEQ64(lo, hi, jt) \
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 5), \
BPF_STMT(BPF_LD+BPF_MEM, 0), /* swap in lo */ \
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (lo), 0, 2), \
BPF_STMT(BPF_LD+BPF_MEM, 1), /* passed: swap hi back in */ \
jt, \
BPF_STMT(BPF_LD+BPF_MEM, 1) /* failed: swap hi back in */
#define JNE64(lo, hi, jt) \
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 5, 0), \
BPF_STMT(BPF_LD+BPF_MEM, 0), /* swap in lo */ \
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (lo), 2, 0), \
BPF_STMT(BPF_LD+BPF_MEM, 1), /* passed: swap hi back in */ \
jt, \
BPF_STMT(BPF_LD+BPF_MEM, 1) /* failed: swap hi back in */
#define JA32(value, jt) \ #define JA32(value, jt) \
BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, (value), 0, 1), \ BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, (value), 0, 1), \
jt jt
#define JA64(lo, hi, jt) \
BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, (hi), 3, 0), \
BPF_STMT(BPF_LD+BPF_MEM, 0), /* swap in lo */ \
BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, (lo), 0, 2), \
BPF_STMT(BPF_LD+BPF_MEM, 1), /* passed: swap hi back in */ \
jt, \
BPF_STMT(BPF_LD+BPF_MEM, 1) /* failed: swap hi back in */
#define JGE32(value, jt) \ #define JGE32(value, jt) \
BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (value), 0, 1), \ BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (value), 0, 1), \
jt jt
#define JLT32(value, jt) \
BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (value), 1, 0), \
jt
/* Shortcut checking if hi > arg.hi. */
#define JGE64(lo, hi, jt) \
BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (hi), 4, 0), \
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 5), \
BPF_STMT(BPF_LD+BPF_MEM, 0), /* swap in lo */ \
BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (lo), 0, 2), \
BPF_STMT(BPF_LD+BPF_MEM, 1), /* passed: swap hi back in */ \
jt, \
BPF_STMT(BPF_LD+BPF_MEM, 1) /* failed: swap hi back in */
#define JLT64(lo, hi, jt) \
BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (hi), 0, 4), \
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 5), \
BPF_STMT(BPF_LD+BPF_MEM, 0), /* swap in lo */ \
BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (lo), 2, 0), \
BPF_STMT(BPF_LD+BPF_MEM, 1), /* passed: swap hi back in */ \
jt, \
BPF_STMT(BPF_LD+BPF_MEM, 1) /* failed: swap hi back in */
#define JGT32(value, jt) \ #define JGT32(value, jt) \
BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (value), 0, 1), \ BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (value), 0, 1), \
jt jt
@ -217,24 +169,91 @@ union arg64 {
BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (value), 1, 0), \ BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (value), 1, 0), \
jt jt
/* Check hi > args.hi first, then do the GE checking */ #define JLT32(value, jt) \
#define JGT64(lo, hi, jt) \ BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (value), 1, 0), \
BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (hi), 4, 0), \ jt
/*
* All the JXX64 checks assume lo is saved in M[0] and hi is saved in both
* A and M[1]. This invariant is kept by restoring A if necessary.
*/
#define JEQ64(lo, hi, jt) \
/* if (hi != arg.hi) goto NOMATCH; */ \
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 5), \ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 5), \
BPF_STMT(BPF_LD+BPF_MEM, 0), /* swap in lo */ \ BPF_STMT(BPF_LD+BPF_MEM, 0), /* swap in lo */ \
BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (lo), 0, 2), \ /* if (lo != arg.lo) goto NOMATCH; */ \
BPF_STMT(BPF_LD+BPF_MEM, 1), /* passed: swap hi back in */ \ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (lo), 0, 2), \
BPF_STMT(BPF_LD+BPF_MEM, 1), \
jt, \ jt, \
BPF_STMT(BPF_LD+BPF_MEM, 1) /* failed: swap hi back in */ BPF_STMT(BPF_LD+BPF_MEM, 1)
#define JNE64(lo, hi, jt) \
/* if (hi != arg.hi) goto MATCH; */ \
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 3), \
BPF_STMT(BPF_LD+BPF_MEM, 0), \
/* if (lo != arg.lo) goto MATCH; */ \
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (lo), 2, 0), \
BPF_STMT(BPF_LD+BPF_MEM, 1), \
jt, \
BPF_STMT(BPF_LD+BPF_MEM, 1)
#define JA64(lo, hi, jt) \
/* if (hi & arg.hi) goto MATCH; */ \
BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, (hi), 3, 0), \
BPF_STMT(BPF_LD+BPF_MEM, 0), \
/* if (lo & arg.lo) goto MATCH; */ \
BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, (lo), 0, 2), \
BPF_STMT(BPF_LD+BPF_MEM, 1), \
jt, \
BPF_STMT(BPF_LD+BPF_MEM, 1)
#define JGE64(lo, hi, jt) \
/* if (hi > arg.hi) goto MATCH; */ \
BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (hi), 4, 0), \
/* if (hi != arg.hi) goto NOMATCH; */ \
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 5), \
BPF_STMT(BPF_LD+BPF_MEM, 0), \
/* if (lo >= arg.lo) goto MATCH; */ \
BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (lo), 0, 2), \
BPF_STMT(BPF_LD+BPF_MEM, 1), \
jt, \
BPF_STMT(BPF_LD+BPF_MEM, 1)
#define JGT64(lo, hi, jt) \
/* if (hi > arg.hi) goto MATCH; */ \
BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (hi), 4, 0), \
/* if (hi != arg.hi) goto NOMATCH; */ \
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 5), \
BPF_STMT(BPF_LD+BPF_MEM, 0), \
/* if (lo > arg.lo) goto MATCH; */ \
BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (lo), 0, 2), \
BPF_STMT(BPF_LD+BPF_MEM, 1), \
jt, \
BPF_STMT(BPF_LD+BPF_MEM, 1)
#define JLE64(lo, hi, jt) \ #define JLE64(lo, hi, jt) \
BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (hi), 6, 0), \ /* if (hi < arg.hi) goto MATCH; */ \
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 3), \ BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (hi), 0, 4), \
BPF_STMT(BPF_LD+BPF_MEM, 0), /* swap in lo */ \ /* if (hi != arg.hi) goto NOMATCH; */ \
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 5), \
BPF_STMT(BPF_LD+BPF_MEM, 0), \
/* if (lo <= arg.lo) goto MATCH; */ \
BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (lo), 2, 0), \ BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (lo), 2, 0), \
BPF_STMT(BPF_LD+BPF_MEM, 1), /* passed: swap hi back in */ \ BPF_STMT(BPF_LD+BPF_MEM, 1), \
jt, \ jt, \
BPF_STMT(BPF_LD+BPF_MEM, 1) /* failed: swap hi back in */ BPF_STMT(BPF_LD+BPF_MEM, 1)
#define JLT64(lo, hi, jt) \
/* if (hi < arg.hi) goto MATCH; */ \
BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (hi), 0, 4), \
/* if (hi != arg.hi) goto NOMATCH; */ \
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 5), \
BPF_STMT(BPF_LD+BPF_MEM, 0), \
/* if (lo < arg.lo) goto MATCH; */ \
BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (lo), 2, 0), \
BPF_STMT(BPF_LD+BPF_MEM, 1), \
jt, \
BPF_STMT(BPF_LD+BPF_MEM, 1)
#define LOAD_SYSCALL_NR \ #define LOAD_SYSCALL_NR \
BPF_STMT(BPF_LD+BPF_W+BPF_ABS, \ BPF_STMT(BPF_LD+BPF_W+BPF_ABS, \

View File

@ -41,7 +41,9 @@
* signing with anything other than SHA1 - so we're stuck with that if such is * signing with anything other than SHA1 - so we're stuck with that if such is
* the case. * the case.
*/ */
#if OPENSSL_VERSION_NUMBER < 0x10000000L || defined(OPENSSL_NO_CMS) #if defined(LIBRESSL_VERSION_NUMBER) || \
OPENSSL_VERSION_NUMBER < 0x10000000L || \
defined(OPENSSL_NO_CMS)
#define USE_PKCS7 #define USE_PKCS7
#endif #endif
#ifndef USE_PKCS7 #ifndef USE_PKCS7

View File

@ -36,7 +36,6 @@ config SECURITY_APPARMOR_HASH
select CRYPTO select CRYPTO
select CRYPTO_SHA1 select CRYPTO_SHA1
default y default y
help help
This option selects whether introspection of loaded policy This option selects whether introspection of loaded policy
is available to userspace via the apparmor filesystem. is available to userspace via the apparmor filesystem.
@ -45,7 +44,6 @@ config SECURITY_APPARMOR_HASH_DEFAULT
bool "Enable policy hash introspection by default" bool "Enable policy hash introspection by default"
depends on SECURITY_APPARMOR_HASH depends on SECURITY_APPARMOR_HASH
default y default y
help help
This option selects whether sha1 hashing of loaded policy This option selects whether sha1 hashing of loaded policy
is enabled by default. The generation of sha1 hashes for is enabled by default. The generation of sha1 hashes for
@ -54,3 +52,32 @@ config SECURITY_APPARMOR_HASH_DEFAULT
however it can slow down policy load on some devices. In however it can slow down policy load on some devices. In
these cases policy hashing can be disabled by default and these cases policy hashing can be disabled by default and
enabled only if needed. enabled only if needed.
config SECURITY_APPARMOR_DEBUG
bool "Build AppArmor with debug code"
depends on SECURITY_APPARMOR
default n
help
Build apparmor with debugging logic in apparmor. Not all
debugging logic will necessarily be enabled. A submenu will
provide fine grained control of the debug options that are
available.
config SECURITY_APPARMOR_DEBUG_ASSERTS
bool "Build AppArmor with debugging asserts"
depends on SECURITY_APPARMOR_DEBUG
default y
help
Enable code assertions made with AA_BUG. These are primarily
function entry preconditions but also exist at other key
points. If the assert is triggered it will trigger a WARN
message.
config SECURITY_APPARMOR_DEBUG_MESSAGES
bool "Debug messages enabled by default"
depends on SECURITY_APPARMOR_DEBUG
default n
help
Set the default value of the apparmor.debug kernel parameter.
When enabled, various debug messages will be logged to
the kernel message buffer.

View File

@ -4,7 +4,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 secid.o file.o policy_ns.o
apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o
clean-files := capability_names.h rlim_names.h clean-files := capability_names.h rlim_names.h

View File

@ -18,9 +18,12 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/mount.h>
#include <linux/namei.h> #include <linux/namei.h>
#include <linux/capability.h> #include <linux/capability.h>
#include <linux/rcupdate.h> #include <linux/rcupdate.h>
#include <uapi/linux/major.h>
#include <linux/fs.h>
#include "include/apparmor.h" #include "include/apparmor.h"
#include "include/apparmorfs.h" #include "include/apparmorfs.h"
@ -28,7 +31,9 @@
#include "include/context.h" #include "include/context.h"
#include "include/crypto.h" #include "include/crypto.h"
#include "include/policy.h" #include "include/policy.h"
#include "include/policy_ns.h"
#include "include/resource.h" #include "include/resource.h"
#include "include/policy_unpack.h"
/** /**
* aa_mangle_name - mangle a profile name to std profile layout form * aa_mangle_name - mangle a profile name to std profile layout form
@ -37,7 +42,7 @@
* *
* Returns: length of mangled name * Returns: length of mangled name
*/ */
static int mangle_name(char *name, char *target) static int mangle_name(const char *name, char *target)
{ {
char *t = target; char *t = target;
@ -71,7 +76,6 @@ static int mangle_name(char *name, char *target)
/** /**
* aa_simple_write_to_buffer - common routine for getting policy from user * aa_simple_write_to_buffer - common routine for getting policy from user
* @op: operation doing the user buffer copy
* @userbuf: user buffer to copy data from (NOT NULL) * @userbuf: user buffer to copy data from (NOT NULL)
* @alloc_size: size of user buffer (REQUIRES: @alloc_size >= @copy_size) * @alloc_size: size of user buffer (REQUIRES: @alloc_size >= @copy_size)
* @copy_size: size of data to copy from user buffer * @copy_size: size of data to copy from user buffer
@ -80,31 +84,29 @@ static int mangle_name(char *name, char *target)
* Returns: kernel buffer containing copy of user buffer data or an * Returns: kernel buffer containing copy of user buffer data or an
* ERR_PTR on failure. * ERR_PTR on failure.
*/ */
static char *aa_simple_write_to_buffer(int op, const char __user *userbuf, static struct aa_loaddata *aa_simple_write_to_buffer(const char __user *userbuf,
size_t alloc_size, size_t copy_size, size_t alloc_size,
loff_t *pos) size_t copy_size,
loff_t *pos)
{ {
char *data; struct aa_loaddata *data;
BUG_ON(copy_size > alloc_size); AA_BUG(copy_size > alloc_size);
if (*pos != 0) if (*pos != 0)
/* only writes from pos 0, that is complete writes */ /* only writes from pos 0, that is complete writes */
return ERR_PTR(-ESPIPE); return ERR_PTR(-ESPIPE);
/*
* Don't allow profile load/replace/remove from profiles that don't
* have CAP_MAC_ADMIN
*/
if (!aa_may_manage_policy(op))
return ERR_PTR(-EACCES);
/* freed by caller to simple_write_to_buffer */ /* freed by caller to simple_write_to_buffer */
data = kvmalloc(alloc_size); data = kvmalloc(sizeof(*data) + alloc_size);
if (data == NULL) if (data == NULL)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
kref_init(&data->count);
data->size = copy_size;
data->hash = NULL;
data->abi = 0;
if (copy_from_user(data, userbuf, copy_size)) { if (copy_from_user(data->data, userbuf, copy_size)) {
kvfree(data); kvfree(data);
return ERR_PTR(-EFAULT); return ERR_PTR(-EFAULT);
} }
@ -112,21 +114,39 @@ static char *aa_simple_write_to_buffer(int op, const char __user *userbuf,
return data; return data;
} }
static ssize_t policy_update(int binop, const char __user *buf, size_t size,
loff_t *pos, struct aa_ns *ns)
{
ssize_t error;
struct aa_loaddata *data;
struct aa_profile *profile = aa_current_profile();
const char *op = binop == PROF_ADD ? OP_PROF_LOAD : OP_PROF_REPL;
/* high level check about policy management - fine grained in
* below after unpack
*/
error = aa_may_manage_policy(profile, ns, op);
if (error)
return error;
data = aa_simple_write_to_buffer(buf, size, size, pos);
error = PTR_ERR(data);
if (!IS_ERR(data)) {
error = aa_replace_profiles(ns ? ns : profile->ns, profile,
binop, data);
aa_put_loaddata(data);
}
return error;
}
/* .load file hook fn to load policy */ /* .load file hook fn to load policy */
static ssize_t profile_load(struct file *f, const char __user *buf, size_t size, static ssize_t profile_load(struct file *f, const char __user *buf, size_t size,
loff_t *pos) loff_t *pos)
{ {
char *data; struct aa_ns *ns = aa_get_ns(f->f_inode->i_private);
ssize_t error; int error = policy_update(PROF_ADD, buf, size, pos, ns);
data = aa_simple_write_to_buffer(OP_PROF_LOAD, buf, size, size, pos); aa_put_ns(ns);
error = PTR_ERR(data);
if (!IS_ERR(data)) {
error = aa_replace_profiles(data, size, PROF_ADD);
kvfree(data);
}
return error; return error;
} }
@ -140,15 +160,10 @@ static const struct file_operations aa_fs_profile_load = {
static ssize_t profile_replace(struct file *f, const char __user *buf, static ssize_t profile_replace(struct file *f, const char __user *buf,
size_t size, loff_t *pos) size_t size, loff_t *pos)
{ {
char *data; struct aa_ns *ns = aa_get_ns(f->f_inode->i_private);
ssize_t error; int error = policy_update(PROF_REPLACE, buf, size, pos, ns);
data = aa_simple_write_to_buffer(OP_PROF_REPL, buf, size, size, pos); aa_put_ns(ns);
error = PTR_ERR(data);
if (!IS_ERR(data)) {
error = aa_replace_profiles(data, size, PROF_REPLACE);
kvfree(data);
}
return error; return error;
} }
@ -162,22 +177,34 @@ static const struct file_operations aa_fs_profile_replace = {
static ssize_t profile_remove(struct file *f, const char __user *buf, static ssize_t profile_remove(struct file *f, const char __user *buf,
size_t size, loff_t *pos) size_t size, loff_t *pos)
{ {
char *data; struct aa_loaddata *data;
struct aa_profile *profile;
ssize_t error; ssize_t error;
struct aa_ns *ns = aa_get_ns(f->f_inode->i_private);
profile = aa_current_profile();
/* high level check about policy management - fine grained in
* below after unpack
*/
error = aa_may_manage_policy(profile, ns, OP_PROF_RM);
if (error)
goto out;
/* /*
* aa_remove_profile needs a null terminated string so 1 extra * aa_remove_profile needs a null terminated string so 1 extra
* byte is allocated and the copied data is null terminated. * byte is allocated and the copied data is null terminated.
*/ */
data = aa_simple_write_to_buffer(OP_PROF_RM, buf, size + 1, size, pos); data = aa_simple_write_to_buffer(buf, size + 1, size, pos);
error = PTR_ERR(data); error = PTR_ERR(data);
if (!IS_ERR(data)) { if (!IS_ERR(data)) {
data[size] = 0; data->data[size] = 0;
error = aa_remove_profiles(data, size); error = aa_remove_profiles(ns ? ns : profile->ns, profile,
kvfree(data); data->data, size);
aa_put_loaddata(data);
} }
out:
aa_put_ns(ns);
return error; return error;
} }
@ -186,6 +213,144 @@ static const struct file_operations aa_fs_profile_remove = {
.llseek = default_llseek, .llseek = default_llseek,
}; };
/**
* query_data - queries a policy and writes its data to buf
* @buf: the resulting data is stored here (NOT NULL)
* @buf_len: size of buf
* @query: query string used to retrieve data
* @query_len: size of query including second NUL byte
*
* The buffers pointed to by buf and query may overlap. The query buffer is
* parsed before buf is written to.
*
* The query should look like "<LABEL>\0<KEY>\0", where <LABEL> is the name of
* the security confinement context and <KEY> is the name of the data to
* retrieve. <LABEL> and <KEY> must not be NUL-terminated.
*
* Don't expect the contents of buf to be preserved on failure.
*
* Returns: number of characters written to buf or -errno on failure
*/
static ssize_t query_data(char *buf, size_t buf_len,
char *query, size_t query_len)
{
char *out;
const char *key;
struct aa_profile *profile;
struct aa_data *data;
u32 bytes, blocks;
__le32 outle32;
if (!query_len)
return -EINVAL; /* need a query */
key = query + strnlen(query, query_len) + 1;
if (key + 1 >= query + query_len)
return -EINVAL; /* not enough space for a non-empty key */
if (key + strnlen(key, query + query_len - key) >= query + query_len)
return -EINVAL; /* must end with NUL */
if (buf_len < sizeof(bytes) + sizeof(blocks))
return -EINVAL; /* not enough space */
profile = aa_current_profile();
/* We are going to leave space for two numbers. The first is the total
* number of bytes we are writing after the first number. This is so
* users can read the full output without reallocation.
*
* The second number is the number of data blocks we're writing. An
* application might be confined by multiple policies having data in
* the same key.
*/
memset(buf, 0, sizeof(bytes) + sizeof(blocks));
out = buf + sizeof(bytes) + sizeof(blocks);
blocks = 0;
if (profile->data) {
data = rhashtable_lookup_fast(profile->data, &key,
profile->data->p);
if (data) {
if (out + sizeof(outle32) + data->size > buf + buf_len)
return -EINVAL; /* not enough space */
outle32 = __cpu_to_le32(data->size);
memcpy(out, &outle32, sizeof(outle32));
out += sizeof(outle32);
memcpy(out, data->data, data->size);
out += data->size;
blocks++;
}
}
outle32 = __cpu_to_le32(out - buf - sizeof(bytes));
memcpy(buf, &outle32, sizeof(outle32));
outle32 = __cpu_to_le32(blocks);
memcpy(buf + sizeof(bytes), &outle32, sizeof(outle32));
return out - buf;
}
#define QUERY_CMD_DATA "data\0"
#define QUERY_CMD_DATA_LEN 5
/**
* aa_write_access - generic permissions and data query
* @file: pointer to open apparmorfs/access file
* @ubuf: user buffer containing the complete query string (NOT NULL)
* @count: size of ubuf
* @ppos: position in the file (MUST BE ZERO)
*
* Allows for one permissions or data query per open(), write(), and read()
* sequence. The only queries currently supported are label-based queries for
* permissions or data.
*
* For permissions queries, ubuf must begin with "label\0", followed by the
* profile query specific format described in the query_label() function
* documentation.
*
* For data queries, ubuf must have the form "data\0<LABEL>\0<KEY>\0", where
* <LABEL> is the name of the security confinement context and <KEY> is the
* name of the data to retrieve.
*
* Returns: number of bytes written or -errno on failure
*/
static ssize_t aa_write_access(struct file *file, const char __user *ubuf,
size_t count, loff_t *ppos)
{
char *buf;
ssize_t len;
if (*ppos)
return -ESPIPE;
buf = simple_transaction_get(file, ubuf, count);
if (IS_ERR(buf))
return PTR_ERR(buf);
if (count > QUERY_CMD_DATA_LEN &&
!memcmp(buf, QUERY_CMD_DATA, QUERY_CMD_DATA_LEN)) {
len = query_data(buf, SIMPLE_TRANSACTION_LIMIT,
buf + QUERY_CMD_DATA_LEN,
count - QUERY_CMD_DATA_LEN);
} else
len = -EINVAL;
if (len < 0)
return len;
simple_transaction_set(file, len);
return count;
}
static const struct file_operations aa_fs_access = {
.write = aa_write_access,
.read = simple_transaction_read,
.release = simple_transaction_release,
.llseek = generic_file_llseek,
};
static int aa_fs_seq_show(struct seq_file *seq, void *v) static int aa_fs_seq_show(struct seq_file *seq, void *v)
{ {
struct aa_fs_entry *fs_file = seq->private; struct aa_fs_entry *fs_file = seq->private;
@ -227,12 +392,12 @@ const struct file_operations aa_fs_seq_file_ops = {
static int aa_fs_seq_profile_open(struct inode *inode, struct file *file, static int aa_fs_seq_profile_open(struct inode *inode, struct file *file,
int (*show)(struct seq_file *, void *)) int (*show)(struct seq_file *, void *))
{ {
struct aa_replacedby *r = aa_get_replacedby(inode->i_private); struct aa_proxy *proxy = aa_get_proxy(inode->i_private);
int error = single_open(file, show, r); int error = single_open(file, show, proxy);
if (error) { if (error) {
file->private_data = NULL; file->private_data = NULL;
aa_put_replacedby(r); aa_put_proxy(proxy);
} }
return error; return error;
@ -242,14 +407,14 @@ static int aa_fs_seq_profile_release(struct inode *inode, struct file *file)
{ {
struct seq_file *seq = (struct seq_file *) file->private_data; struct seq_file *seq = (struct seq_file *) file->private_data;
if (seq) if (seq)
aa_put_replacedby(seq->private); aa_put_proxy(seq->private);
return single_release(inode, file); return single_release(inode, file);
} }
static int aa_fs_seq_profname_show(struct seq_file *seq, void *v) static int aa_fs_seq_profname_show(struct seq_file *seq, void *v)
{ {
struct aa_replacedby *r = seq->private; struct aa_proxy *proxy = seq->private;
struct aa_profile *profile = aa_get_profile_rcu(&r->profile); struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile);
seq_printf(seq, "%s\n", profile->base.name); seq_printf(seq, "%s\n", profile->base.name);
aa_put_profile(profile); aa_put_profile(profile);
@ -271,8 +436,8 @@ static const struct file_operations aa_fs_profname_fops = {
static int aa_fs_seq_profmode_show(struct seq_file *seq, void *v) static int aa_fs_seq_profmode_show(struct seq_file *seq, void *v)
{ {
struct aa_replacedby *r = seq->private; struct aa_proxy *proxy = seq->private;
struct aa_profile *profile = aa_get_profile_rcu(&r->profile); struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile);
seq_printf(seq, "%s\n", aa_profile_mode_names[profile->mode]); seq_printf(seq, "%s\n", aa_profile_mode_names[profile->mode]);
aa_put_profile(profile); aa_put_profile(profile);
@ -294,8 +459,8 @@ static const struct file_operations aa_fs_profmode_fops = {
static int aa_fs_seq_profattach_show(struct seq_file *seq, void *v) static int aa_fs_seq_profattach_show(struct seq_file *seq, void *v)
{ {
struct aa_replacedby *r = seq->private; struct aa_proxy *proxy = seq->private;
struct aa_profile *profile = aa_get_profile_rcu(&r->profile); struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile);
if (profile->attach) if (profile->attach)
seq_printf(seq, "%s\n", profile->attach); seq_printf(seq, "%s\n", profile->attach);
else if (profile->xmatch) else if (profile->xmatch)
@ -322,8 +487,8 @@ static const struct file_operations aa_fs_profattach_fops = {
static int aa_fs_seq_hash_show(struct seq_file *seq, void *v) static int aa_fs_seq_hash_show(struct seq_file *seq, void *v)
{ {
struct aa_replacedby *r = seq->private; struct aa_proxy *proxy = seq->private;
struct aa_profile *profile = aa_get_profile_rcu(&r->profile); struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile);
unsigned int i, size = aa_hash_size(); unsigned int i, size = aa_hash_size();
if (profile->hash) { if (profile->hash) {
@ -349,6 +514,145 @@ static const struct file_operations aa_fs_seq_hash_fops = {
.release = single_release, .release = single_release,
}; };
static int aa_fs_seq_show_ns_level(struct seq_file *seq, void *v)
{
struct aa_ns *ns = aa_current_profile()->ns;
seq_printf(seq, "%d\n", ns->level);
return 0;
}
static int aa_fs_seq_open_ns_level(struct inode *inode, struct file *file)
{
return single_open(file, aa_fs_seq_show_ns_level, inode->i_private);
}
static const struct file_operations aa_fs_ns_level = {
.owner = THIS_MODULE,
.open = aa_fs_seq_open_ns_level,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int aa_fs_seq_show_ns_name(struct seq_file *seq, void *v)
{
struct aa_ns *ns = aa_current_profile()->ns;
seq_printf(seq, "%s\n", ns->base.name);
return 0;
}
static int aa_fs_seq_open_ns_name(struct inode *inode, struct file *file)
{
return single_open(file, aa_fs_seq_show_ns_name, inode->i_private);
}
static const struct file_operations aa_fs_ns_name = {
.owner = THIS_MODULE,
.open = aa_fs_seq_open_ns_name,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int rawdata_release(struct inode *inode, struct file *file)
{
/* TODO: switch to loaddata when profile switched to symlink */
aa_put_loaddata(file->private_data);
return 0;
}
static int aa_fs_seq_raw_abi_show(struct seq_file *seq, void *v)
{
struct aa_proxy *proxy = seq->private;
struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile);
if (profile->rawdata->abi) {
seq_printf(seq, "v%d", profile->rawdata->abi);
seq_puts(seq, "\n");
}
aa_put_profile(profile);
return 0;
}
static int aa_fs_seq_raw_abi_open(struct inode *inode, struct file *file)
{
return aa_fs_seq_profile_open(inode, file, aa_fs_seq_raw_abi_show);
}
static const struct file_operations aa_fs_seq_raw_abi_fops = {
.owner = THIS_MODULE,
.open = aa_fs_seq_raw_abi_open,
.read = seq_read,
.llseek = seq_lseek,
.release = aa_fs_seq_profile_release,
};
static int aa_fs_seq_raw_hash_show(struct seq_file *seq, void *v)
{
struct aa_proxy *proxy = seq->private;
struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile);
unsigned int i, size = aa_hash_size();
if (profile->rawdata->hash) {
for (i = 0; i < size; i++)
seq_printf(seq, "%.2x", profile->rawdata->hash[i]);
seq_puts(seq, "\n");
}
aa_put_profile(profile);
return 0;
}
static int aa_fs_seq_raw_hash_open(struct inode *inode, struct file *file)
{
return aa_fs_seq_profile_open(inode, file, aa_fs_seq_raw_hash_show);
}
static const struct file_operations aa_fs_seq_raw_hash_fops = {
.owner = THIS_MODULE,
.open = aa_fs_seq_raw_hash_open,
.read = seq_read,
.llseek = seq_lseek,
.release = aa_fs_seq_profile_release,
};
static ssize_t rawdata_read(struct file *file, char __user *buf, size_t size,
loff_t *ppos)
{
struct aa_loaddata *rawdata = file->private_data;
return simple_read_from_buffer(buf, size, ppos, rawdata->data,
rawdata->size);
}
static int rawdata_open(struct inode *inode, struct file *file)
{
struct aa_proxy *proxy = inode->i_private;
struct aa_profile *profile;
if (!policy_view_capable(NULL))
return -EACCES;
profile = aa_get_profile_rcu(&proxy->profile);
file->private_data = aa_get_loaddata(profile->rawdata);
aa_put_profile(profile);
return 0;
}
static const struct file_operations aa_fs_rawdata_fops = {
.open = rawdata_open,
.read = rawdata_read,
.llseek = generic_file_llseek,
.release = rawdata_release,
};
/** fns to setup dynamic per profile/namespace files **/ /** fns to setup dynamic per profile/namespace files **/
void __aa_fs_profile_rmdir(struct aa_profile *profile) void __aa_fs_profile_rmdir(struct aa_profile *profile)
{ {
@ -362,13 +666,13 @@ void __aa_fs_profile_rmdir(struct aa_profile *profile)
__aa_fs_profile_rmdir(child); __aa_fs_profile_rmdir(child);
for (i = AAFS_PROF_SIZEOF - 1; i >= 0; --i) { for (i = AAFS_PROF_SIZEOF - 1; i >= 0; --i) {
struct aa_replacedby *r; struct aa_proxy *proxy;
if (!profile->dents[i]) if (!profile->dents[i])
continue; continue;
r = d_inode(profile->dents[i])->i_private; proxy = d_inode(profile->dents[i])->i_private;
securityfs_remove(profile->dents[i]); securityfs_remove(profile->dents[i]);
aa_put_replacedby(r); aa_put_proxy(proxy);
profile->dents[i] = NULL; profile->dents[i] = NULL;
} }
} }
@ -390,12 +694,12 @@ static struct dentry *create_profile_file(struct dentry *dir, const char *name,
struct aa_profile *profile, struct aa_profile *profile,
const struct file_operations *fops) const struct file_operations *fops)
{ {
struct aa_replacedby *r = aa_get_replacedby(profile->replacedby); struct aa_proxy *proxy = aa_get_proxy(profile->proxy);
struct dentry *dent; struct dentry *dent;
dent = securityfs_create_file(name, S_IFREG | 0444, dir, r, fops); dent = securityfs_create_file(name, S_IFREG | 0444, dir, proxy, fops);
if (IS_ERR(dent)) if (IS_ERR(dent))
aa_put_replacedby(r); aa_put_proxy(proxy);
return dent; return dent;
} }
@ -460,6 +764,29 @@ int __aa_fs_profile_mkdir(struct aa_profile *profile, struct dentry *parent)
profile->dents[AAFS_PROF_HASH] = dent; profile->dents[AAFS_PROF_HASH] = dent;
} }
if (profile->rawdata) {
dent = create_profile_file(dir, "raw_sha1", profile,
&aa_fs_seq_raw_hash_fops);
if (IS_ERR(dent))
goto fail;
profile->dents[AAFS_PROF_RAW_HASH] = dent;
dent = create_profile_file(dir, "raw_abi", profile,
&aa_fs_seq_raw_abi_fops);
if (IS_ERR(dent))
goto fail;
profile->dents[AAFS_PROF_RAW_ABI] = dent;
dent = securityfs_create_file("raw_data", S_IFREG | 0444, dir,
profile->proxy,
&aa_fs_rawdata_fops);
if (IS_ERR(dent))
goto fail;
profile->dents[AAFS_PROF_RAW_DATA] = dent;
d_inode(dent)->i_size = profile->rawdata->size;
aa_get_proxy(profile->proxy);
}
list_for_each_entry(child, &profile->base.profiles, base.list) { list_for_each_entry(child, &profile->base.profiles, base.list) {
error = __aa_fs_profile_mkdir(child, prof_child_dir(profile)); error = __aa_fs_profile_mkdir(child, prof_child_dir(profile));
if (error) if (error)
@ -477,9 +804,9 @@ fail2:
return error; return error;
} }
void __aa_fs_namespace_rmdir(struct aa_namespace *ns) void __aa_fs_ns_rmdir(struct aa_ns *ns)
{ {
struct aa_namespace *sub; struct aa_ns *sub;
struct aa_profile *child; struct aa_profile *child;
int i; int i;
@ -491,51 +818,116 @@ void __aa_fs_namespace_rmdir(struct aa_namespace *ns)
list_for_each_entry(sub, &ns->sub_ns, base.list) { list_for_each_entry(sub, &ns->sub_ns, base.list) {
mutex_lock(&sub->lock); mutex_lock(&sub->lock);
__aa_fs_namespace_rmdir(sub); __aa_fs_ns_rmdir(sub);
mutex_unlock(&sub->lock); mutex_unlock(&sub->lock);
} }
if (ns_subns_dir(ns)) {
sub = d_inode(ns_subns_dir(ns))->i_private;
aa_put_ns(sub);
}
if (ns_subload(ns)) {
sub = d_inode(ns_subload(ns))->i_private;
aa_put_ns(sub);
}
if (ns_subreplace(ns)) {
sub = d_inode(ns_subreplace(ns))->i_private;
aa_put_ns(sub);
}
if (ns_subremove(ns)) {
sub = d_inode(ns_subremove(ns))->i_private;
aa_put_ns(sub);
}
for (i = AAFS_NS_SIZEOF - 1; i >= 0; --i) { for (i = AAFS_NS_SIZEOF - 1; i >= 0; --i) {
securityfs_remove(ns->dents[i]); securityfs_remove(ns->dents[i]);
ns->dents[i] = NULL; ns->dents[i] = NULL;
} }
} }
int __aa_fs_namespace_mkdir(struct aa_namespace *ns, struct dentry *parent, /* assumes cleanup in caller */
const char *name) static int __aa_fs_ns_mkdir_entries(struct aa_ns *ns, struct dentry *dir)
{ {
struct aa_namespace *sub; struct dentry *dent;
AA_BUG(!ns);
AA_BUG(!dir);
dent = securityfs_create_dir("profiles", dir);
if (IS_ERR(dent))
return PTR_ERR(dent);
ns_subprofs_dir(ns) = dent;
dent = securityfs_create_dir("raw_data", dir);
if (IS_ERR(dent))
return PTR_ERR(dent);
ns_subdata_dir(ns) = dent;
dent = securityfs_create_file(".load", 0640, dir, ns,
&aa_fs_profile_load);
if (IS_ERR(dent))
return PTR_ERR(dent);
aa_get_ns(ns);
ns_subload(ns) = dent;
dent = securityfs_create_file(".replace", 0640, dir, ns,
&aa_fs_profile_replace);
if (IS_ERR(dent))
return PTR_ERR(dent);
aa_get_ns(ns);
ns_subreplace(ns) = dent;
dent = securityfs_create_file(".remove", 0640, dir, ns,
&aa_fs_profile_remove);
if (IS_ERR(dent))
return PTR_ERR(dent);
aa_get_ns(ns);
ns_subremove(ns) = dent;
dent = securityfs_create_dir("namespaces", dir);
if (IS_ERR(dent))
return PTR_ERR(dent);
aa_get_ns(ns);
ns_subns_dir(ns) = dent;
return 0;
}
int __aa_fs_ns_mkdir(struct aa_ns *ns, struct dentry *parent, const char *name)
{
struct aa_ns *sub;
struct aa_profile *child; struct aa_profile *child;
struct dentry *dent, *dir; struct dentry *dent, *dir;
int error; int error;
AA_BUG(!ns);
AA_BUG(!parent);
AA_BUG(!mutex_is_locked(&ns->lock));
if (!name) if (!name)
name = ns->base.name; name = ns->base.name;
/* create ns dir if it doesn't already exist */
dent = securityfs_create_dir(name, parent); dent = securityfs_create_dir(name, parent);
if (IS_ERR(dent)) if (IS_ERR(dent))
goto fail; goto fail;
ns_dir(ns) = dir = dent; ns_dir(ns) = dir = dent;
error = __aa_fs_ns_mkdir_entries(ns, dir);
if (error)
goto fail2;
dent = securityfs_create_dir("profiles", dir); /* profiles */
if (IS_ERR(dent))
goto fail;
ns_subprofs_dir(ns) = dent;
dent = securityfs_create_dir("namespaces", dir);
if (IS_ERR(dent))
goto fail;
ns_subns_dir(ns) = dent;
list_for_each_entry(child, &ns->base.profiles, base.list) { list_for_each_entry(child, &ns->base.profiles, base.list) {
error = __aa_fs_profile_mkdir(child, ns_subprofs_dir(ns)); error = __aa_fs_profile_mkdir(child, ns_subprofs_dir(ns));
if (error) if (error)
goto fail2; goto fail2;
} }
/* subnamespaces */
list_for_each_entry(sub, &ns->sub_ns, base.list) { list_for_each_entry(sub, &ns->sub_ns, base.list) {
mutex_lock(&sub->lock); mutex_lock(&sub->lock);
error = __aa_fs_namespace_mkdir(sub, ns_subns_dir(ns), NULL); error = __aa_fs_ns_mkdir(sub, ns_subns_dir(ns), NULL);
mutex_unlock(&sub->lock); mutex_unlock(&sub->lock);
if (error) if (error)
goto fail2; goto fail2;
@ -547,7 +939,7 @@ fail:
error = PTR_ERR(dent); error = PTR_ERR(dent);
fail2: fail2:
__aa_fs_namespace_rmdir(ns); __aa_fs_ns_rmdir(ns);
return error; return error;
} }
@ -556,7 +948,7 @@ fail2:
#define list_entry_is_head(pos, head, member) (&pos->member == (head)) #define list_entry_is_head(pos, head, member) (&pos->member == (head))
/** /**
* __next_namespace - find the next namespace to list * __next_ns - find the next namespace to list
* @root: root namespace to stop search at (NOT NULL) * @root: root namespace to stop search at (NOT NULL)
* @ns: current ns position (NOT NULL) * @ns: current ns position (NOT NULL)
* *
@ -567,10 +959,9 @@ fail2:
* Requires: ns->parent->lock to be held * Requires: ns->parent->lock to be held
* NOTE: will not unlock root->lock * NOTE: will not unlock root->lock
*/ */
static struct aa_namespace *__next_namespace(struct aa_namespace *root, static struct aa_ns *__next_ns(struct aa_ns *root, struct aa_ns *ns)
struct aa_namespace *ns)
{ {
struct aa_namespace *parent, *next; struct aa_ns *parent, *next;
/* is next namespace a child */ /* is next namespace a child */
if (!list_empty(&ns->sub_ns)) { if (!list_empty(&ns->sub_ns)) {
@ -603,10 +994,10 @@ static struct aa_namespace *__next_namespace(struct aa_namespace *root,
* Returns: unrefcounted profile or NULL if no profile * Returns: unrefcounted profile or NULL if no profile
* Requires: profile->ns.lock to be held * Requires: profile->ns.lock to be held
*/ */
static struct aa_profile *__first_profile(struct aa_namespace *root, static struct aa_profile *__first_profile(struct aa_ns *root,
struct aa_namespace *ns) struct aa_ns *ns)
{ {
for (; ns; ns = __next_namespace(root, ns)) { for (; ns; ns = __next_ns(root, ns)) {
if (!list_empty(&ns->base.profiles)) if (!list_empty(&ns->base.profiles))
return list_first_entry(&ns->base.profiles, return list_first_entry(&ns->base.profiles,
struct aa_profile, base.list); struct aa_profile, base.list);
@ -626,7 +1017,7 @@ static struct aa_profile *__first_profile(struct aa_namespace *root,
static struct aa_profile *__next_profile(struct aa_profile *p) static struct aa_profile *__next_profile(struct aa_profile *p)
{ {
struct aa_profile *parent; struct aa_profile *parent;
struct aa_namespace *ns = p->ns; struct aa_ns *ns = p->ns;
/* is next profile a child */ /* is next profile a child */
if (!list_empty(&p->base.profiles)) if (!list_empty(&p->base.profiles))
@ -660,7 +1051,7 @@ static struct aa_profile *__next_profile(struct aa_profile *p)
* *
* Returns: next profile or NULL if there isn't one * Returns: next profile or NULL if there isn't one
*/ */
static struct aa_profile *next_profile(struct aa_namespace *root, static struct aa_profile *next_profile(struct aa_ns *root,
struct aa_profile *profile) struct aa_profile *profile)
{ {
struct aa_profile *next = __next_profile(profile); struct aa_profile *next = __next_profile(profile);
@ -668,7 +1059,7 @@ static struct aa_profile *next_profile(struct aa_namespace *root,
return next; return next;
/* finished all profiles in namespace move to next namespace */ /* finished all profiles in namespace move to next namespace */
return __first_profile(root, __next_namespace(root, profile->ns)); return __first_profile(root, __next_ns(root, profile->ns));
} }
/** /**
@ -683,9 +1074,9 @@ static struct aa_profile *next_profile(struct aa_namespace *root,
static void *p_start(struct seq_file *f, loff_t *pos) static void *p_start(struct seq_file *f, loff_t *pos)
{ {
struct aa_profile *profile = NULL; struct aa_profile *profile = NULL;
struct aa_namespace *root = aa_current_profile()->ns; struct aa_ns *root = aa_current_profile()->ns;
loff_t l = *pos; loff_t l = *pos;
f->private = aa_get_namespace(root); f->private = aa_get_ns(root);
/* find the first profile */ /* find the first profile */
@ -712,7 +1103,7 @@ static void *p_start(struct seq_file *f, loff_t *pos)
static void *p_next(struct seq_file *f, void *p, loff_t *pos) static void *p_next(struct seq_file *f, void *p, loff_t *pos)
{ {
struct aa_profile *profile = p; struct aa_profile *profile = p;
struct aa_namespace *ns = f->private; struct aa_ns *ns = f->private;
(*pos)++; (*pos)++;
return next_profile(ns, profile); return next_profile(ns, profile);
@ -728,14 +1119,14 @@ static void *p_next(struct seq_file *f, void *p, loff_t *pos)
static void p_stop(struct seq_file *f, void *p) static void p_stop(struct seq_file *f, void *p)
{ {
struct aa_profile *profile = p; struct aa_profile *profile = p;
struct aa_namespace *root = f->private, *ns; struct aa_ns *root = f->private, *ns;
if (profile) { if (profile) {
for (ns = profile->ns; ns && ns != root; ns = ns->parent) for (ns = profile->ns; ns && ns != root; ns = ns->parent)
mutex_unlock(&ns->lock); mutex_unlock(&ns->lock);
} }
mutex_unlock(&root->lock); mutex_unlock(&root->lock);
aa_put_namespace(root); aa_put_ns(root);
} }
/** /**
@ -748,10 +1139,10 @@ static void p_stop(struct seq_file *f, void *p)
static int seq_show_profile(struct seq_file *f, void *p) static int seq_show_profile(struct seq_file *f, void *p)
{ {
struct aa_profile *profile = (struct aa_profile *)p; struct aa_profile *profile = (struct aa_profile *)p;
struct aa_namespace *root = f->private; struct aa_ns *root = f->private;
if (profile->ns != root) if (profile->ns != root)
seq_printf(f, ":%s://", aa_ns_name(root, profile->ns)); seq_printf(f, ":%s://", aa_ns_name(root, profile->ns, true));
seq_printf(f, "%s (%s)\n", profile->base.hname, seq_printf(f, "%s (%s)\n", profile->base.hname,
aa_profile_mode_names[profile->mode]); aa_profile_mode_names[profile->mode]);
@ -767,6 +1158,9 @@ static const struct seq_operations aa_fs_profiles_op = {
static int profiles_open(struct inode *inode, struct file *file) static int profiles_open(struct inode *inode, struct file *file)
{ {
if (!policy_view_capable(NULL))
return -EACCES;
return seq_open(file, &aa_fs_profiles_op); return seq_open(file, &aa_fs_profiles_op);
} }
@ -795,12 +1189,20 @@ static struct aa_fs_entry aa_fs_entry_domain[] = {
AA_FS_FILE_BOOLEAN("change_hatv", 1), AA_FS_FILE_BOOLEAN("change_hatv", 1),
AA_FS_FILE_BOOLEAN("change_onexec", 1), AA_FS_FILE_BOOLEAN("change_onexec", 1),
AA_FS_FILE_BOOLEAN("change_profile", 1), AA_FS_FILE_BOOLEAN("change_profile", 1),
AA_FS_FILE_BOOLEAN("fix_binfmt_elf_mmap", 1),
AA_FS_FILE_STRING("version", "1.2"),
{ }
};
static struct aa_fs_entry aa_fs_entry_versions[] = {
AA_FS_FILE_BOOLEAN("v5", 1),
{ } { }
}; };
static struct aa_fs_entry aa_fs_entry_policy[] = { static struct aa_fs_entry aa_fs_entry_policy[] = {
AA_FS_FILE_BOOLEAN("set_load", 1), AA_FS_DIR("versions", aa_fs_entry_versions),
{} AA_FS_FILE_BOOLEAN("set_load", 1),
{ }
}; };
static struct aa_fs_entry aa_fs_entry_features[] = { static struct aa_fs_entry aa_fs_entry_features[] = {
@ -814,10 +1216,10 @@ static struct aa_fs_entry aa_fs_entry_features[] = {
}; };
static struct aa_fs_entry aa_fs_entry_apparmor[] = { static struct aa_fs_entry aa_fs_entry_apparmor[] = {
AA_FS_FILE_FOPS(".load", 0640, &aa_fs_profile_load), AA_FS_FILE_FOPS(".access", 0640, &aa_fs_access),
AA_FS_FILE_FOPS(".replace", 0640, &aa_fs_profile_replace), AA_FS_FILE_FOPS(".ns_level", 0666, &aa_fs_ns_level),
AA_FS_FILE_FOPS(".remove", 0640, &aa_fs_profile_remove), AA_FS_FILE_FOPS(".ns_name", 0640, &aa_fs_ns_name),
AA_FS_FILE_FOPS("profiles", 0640, &aa_fs_profiles_fops), AA_FS_FILE_FOPS("profiles", 0440, &aa_fs_profiles_fops),
AA_FS_DIR("features", aa_fs_entry_features), AA_FS_DIR("features", aa_fs_entry_features),
{ } { }
}; };
@ -926,6 +1328,52 @@ void __init aa_destroy_aafs(void)
aafs_remove_dir(&aa_fs_entry); aafs_remove_dir(&aa_fs_entry);
} }
#define NULL_FILE_NAME ".null"
struct path aa_null;
static int aa_mk_null_file(struct dentry *parent)
{
struct vfsmount *mount = NULL;
struct dentry *dentry;
struct inode *inode;
int count = 0;
int error = simple_pin_fs(parent->d_sb->s_type, &mount, &count);
if (error)
return error;
inode_lock(d_inode(parent));
dentry = lookup_one_len(NULL_FILE_NAME, parent, strlen(NULL_FILE_NAME));
if (IS_ERR(dentry)) {
error = PTR_ERR(dentry);
goto out;
}
inode = new_inode(parent->d_inode->i_sb);
if (!inode) {
error = -ENOMEM;
goto out1;
}
inode->i_ino = get_next_ino();
inode->i_mode = S_IFCHR | S_IRUGO | S_IWUGO;
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
init_special_inode(inode, S_IFCHR | S_IRUGO | S_IWUGO,
MKDEV(MEM_MAJOR, 3));
d_instantiate(dentry, inode);
aa_null.dentry = dget(dentry);
aa_null.mnt = mntget(mount);
error = 0;
out1:
dput(dentry);
out:
inode_unlock(d_inode(parent));
simple_release_fs(&mount, &count);
return error;
}
/** /**
* aa_create_aafs - create the apparmor security filesystem * aa_create_aafs - create the apparmor security filesystem
* *
@ -935,6 +1383,7 @@ void __init aa_destroy_aafs(void)
*/ */
static int __init aa_create_aafs(void) static int __init aa_create_aafs(void)
{ {
struct dentry *dent;
int error; int error;
if (!apparmor_initialized) if (!apparmor_initialized)
@ -950,12 +1399,42 @@ static int __init aa_create_aafs(void)
if (error) if (error)
goto error; goto error;
error = __aa_fs_namespace_mkdir(root_ns, aa_fs_entry.dentry, dent = securityfs_create_file(".load", 0666, aa_fs_entry.dentry,
"policy"); NULL, &aa_fs_profile_load);
if (IS_ERR(dent)) {
error = PTR_ERR(dent);
goto error;
}
ns_subload(root_ns) = dent;
dent = securityfs_create_file(".replace", 0666, aa_fs_entry.dentry,
NULL, &aa_fs_profile_replace);
if (IS_ERR(dent)) {
error = PTR_ERR(dent);
goto error;
}
ns_subreplace(root_ns) = dent;
dent = securityfs_create_file(".remove", 0666, aa_fs_entry.dentry,
NULL, &aa_fs_profile_remove);
if (IS_ERR(dent)) {
error = PTR_ERR(dent);
goto error;
}
ns_subremove(root_ns) = dent;
mutex_lock(&root_ns->lock);
error = __aa_fs_ns_mkdir(root_ns, aa_fs_entry.dentry, "policy");
mutex_unlock(&root_ns->lock);
if (error) if (error)
goto error; goto error;
/* TODO: add support for apparmorfs_null and apparmorfs_mnt */ error = aa_mk_null_file(aa_fs_entry.dentry);
if (error)
goto error;
/* TODO: add default profile to apparmorfs */
/* Report that AppArmor fs is enabled */ /* Report that AppArmor fs is enabled */
aa_info_message("AppArmor Filesystem Enabled"); aa_info_message("AppArmor Filesystem Enabled");

View File

@ -18,60 +18,8 @@
#include "include/apparmor.h" #include "include/apparmor.h"
#include "include/audit.h" #include "include/audit.h"
#include "include/policy.h" #include "include/policy.h"
#include "include/policy_ns.h"
const char *const op_table[] = {
"null",
"sysctl",
"capable",
"unlink",
"mkdir",
"rmdir",
"mknod",
"truncate",
"link",
"symlink",
"rename_src",
"rename_dest",
"chmod",
"chown",
"getattr",
"open",
"file_perm",
"file_lock",
"file_mmap",
"file_mprotect",
"create",
"post_create",
"bind",
"connect",
"listen",
"accept",
"sendmsg",
"recvmsg",
"getsockname",
"getpeername",
"getsockopt",
"setsockopt",
"socket_shutdown",
"ptrace",
"exec",
"change_hat",
"change_profile",
"change_onexec",
"setprocattr",
"setrlimit",
"profile_replace",
"profile_load",
"profile_remove"
};
const char *const audit_mode_names[] = { const char *const audit_mode_names[] = {
"normal", "normal",
@ -114,23 +62,23 @@ static void audit_pre(struct audit_buffer *ab, void *ca)
if (aa_g_audit_header) { if (aa_g_audit_header) {
audit_log_format(ab, "apparmor="); audit_log_format(ab, "apparmor=");
audit_log_string(ab, aa_audit_type[sa->aad->type]); audit_log_string(ab, aa_audit_type[aad(sa)->type]);
} }
if (sa->aad->op) { if (aad(sa)->op) {
audit_log_format(ab, " operation="); audit_log_format(ab, " operation=");
audit_log_string(ab, op_table[sa->aad->op]); audit_log_string(ab, aad(sa)->op);
} }
if (sa->aad->info) { if (aad(sa)->info) {
audit_log_format(ab, " info="); audit_log_format(ab, " info=");
audit_log_string(ab, sa->aad->info); audit_log_string(ab, aad(sa)->info);
if (sa->aad->error) if (aad(sa)->error)
audit_log_format(ab, " error=%d", sa->aad->error); audit_log_format(ab, " error=%d", aad(sa)->error);
} }
if (sa->aad->profile) { if (aad(sa)->profile) {
struct aa_profile *profile = sa->aad->profile; struct aa_profile *profile = aad(sa)->profile;
if (profile->ns != root_ns) { if (profile->ns != root_ns) {
audit_log_format(ab, " namespace="); audit_log_format(ab, " namespace=");
audit_log_untrustedstring(ab, profile->ns->base.hname); audit_log_untrustedstring(ab, profile->ns->base.hname);
@ -139,9 +87,9 @@ static void audit_pre(struct audit_buffer *ab, void *ca)
audit_log_untrustedstring(ab, profile->base.hname); audit_log_untrustedstring(ab, profile->base.hname);
} }
if (sa->aad->name) { if (aad(sa)->name) {
audit_log_format(ab, " name="); audit_log_format(ab, " name=");
audit_log_untrustedstring(ab, sa->aad->name); audit_log_untrustedstring(ab, aad(sa)->name);
} }
} }
@ -153,7 +101,7 @@ static void audit_pre(struct audit_buffer *ab, void *ca)
void aa_audit_msg(int type, struct common_audit_data *sa, void aa_audit_msg(int type, struct common_audit_data *sa,
void (*cb) (struct audit_buffer *, void *)) void (*cb) (struct audit_buffer *, void *))
{ {
sa->aad->type = type; aad(sa)->type = type;
common_lsm_audit(sa, audit_pre, cb); common_lsm_audit(sa, audit_pre, cb);
} }
@ -161,7 +109,6 @@ void aa_audit_msg(int type, struct common_audit_data *sa,
* aa_audit - Log a profile based audit event to the audit subsystem * aa_audit - Log a profile based audit event to the audit subsystem
* @type: audit type for the message * @type: audit type for the message
* @profile: profile to check against (NOT NULL) * @profile: profile to check against (NOT NULL)
* @gfp: allocation flags to use
* @sa: audit event (NOT NULL) * @sa: audit event (NOT NULL)
* @cb: optional callback fn for type specific fields (MAYBE NULL) * @cb: optional callback fn for type specific fields (MAYBE NULL)
* *
@ -169,14 +116,13 @@ void aa_audit_msg(int type, struct common_audit_data *sa,
* *
* Returns: error on failure * Returns: error on failure
*/ */
int aa_audit(int type, struct aa_profile *profile, gfp_t gfp, int aa_audit(int type, struct aa_profile *profile, struct common_audit_data *sa,
struct common_audit_data *sa,
void (*cb) (struct audit_buffer *, void *)) void (*cb) (struct audit_buffer *, void *))
{ {
BUG_ON(!profile); AA_BUG(!profile);
if (type == AUDIT_APPARMOR_AUTO) { if (type == AUDIT_APPARMOR_AUTO) {
if (likely(!sa->aad->error)) { if (likely(!aad(sa)->error)) {
if (AUDIT_MODE(profile) != AUDIT_ALL) if (AUDIT_MODE(profile) != AUDIT_ALL)
return 0; return 0;
type = AUDIT_APPARMOR_AUDIT; type = AUDIT_APPARMOR_AUDIT;
@ -188,23 +134,23 @@ int aa_audit(int type, struct aa_profile *profile, gfp_t gfp,
if (AUDIT_MODE(profile) == AUDIT_QUIET || if (AUDIT_MODE(profile) == AUDIT_QUIET ||
(type == AUDIT_APPARMOR_DENIED && (type == AUDIT_APPARMOR_DENIED &&
AUDIT_MODE(profile) == AUDIT_QUIET)) AUDIT_MODE(profile) == AUDIT_QUIET))
return sa->aad->error; return aad(sa)->error;
if (KILL_MODE(profile) && type == AUDIT_APPARMOR_DENIED) if (KILL_MODE(profile) && type == AUDIT_APPARMOR_DENIED)
type = AUDIT_APPARMOR_KILL; type = AUDIT_APPARMOR_KILL;
if (!unconfined(profile)) if (!unconfined(profile))
sa->aad->profile = profile; aad(sa)->profile = profile;
aa_audit_msg(type, sa, cb); aa_audit_msg(type, sa, cb);
if (sa->aad->type == AUDIT_APPARMOR_KILL) if (aad(sa)->type == AUDIT_APPARMOR_KILL)
(void)send_sig_info(SIGKILL, NULL, (void)send_sig_info(SIGKILL, NULL,
sa->type == LSM_AUDIT_DATA_TASK && sa->u.tsk ? sa->type == LSM_AUDIT_DATA_TASK && sa->u.tsk ?
sa->u.tsk : current); sa->u.tsk : current);
if (sa->aad->type == AUDIT_APPARMOR_ALLOWED) if (aad(sa)->type == AUDIT_APPARMOR_ALLOWED)
return complain_error(sa->aad->error); return complain_error(aad(sa)->error);
return sa->aad->error; return aad(sa)->error;
} }

View File

@ -15,6 +15,7 @@
#include <linux/capability.h> #include <linux/capability.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/gfp.h> #include <linux/gfp.h>
#include <linux/security.h>
#include "include/apparmor.h" #include "include/apparmor.h"
#include "include/capability.h" #include "include/capability.h"
@ -55,6 +56,7 @@ static void audit_cb(struct audit_buffer *ab, void *va)
* audit_caps - audit a capability * audit_caps - audit a capability
* @profile: profile being tested for confinement (NOT NULL) * @profile: profile being tested for confinement (NOT NULL)
* @cap: capability tested * @cap: capability tested
@audit: whether an audit record should be generated
* @error: error code returned by test * @error: error code returned by test
* *
* Do auditing of capability and handle, audit/complain/kill modes switching * Do auditing of capability and handle, audit/complain/kill modes switching
@ -62,17 +64,16 @@ static void audit_cb(struct audit_buffer *ab, void *va)
* *
* Returns: 0 or sa->error on success, error code on failure * Returns: 0 or sa->error on success, error code on failure
*/ */
static int audit_caps(struct aa_profile *profile, int cap, int error) static int audit_caps(struct aa_profile *profile, int cap, int audit,
int error)
{ {
struct audit_cache *ent; struct audit_cache *ent;
int type = AUDIT_APPARMOR_AUTO; int type = AUDIT_APPARMOR_AUTO;
struct common_audit_data sa; DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_CAP, OP_CAPABLE);
struct apparmor_audit_data aad = {0,};
sa.type = LSM_AUDIT_DATA_CAP;
sa.aad = &aad;
sa.u.cap = cap; sa.u.cap = cap;
sa.aad->op = OP_CAPABLE; aad(&sa)->error = error;
sa.aad->error = error; if (audit == SECURITY_CAP_NOAUDIT)
aad(&sa)->info = "optional: no audit";
if (likely(!error)) { if (likely(!error)) {
/* test if auditing is being forced */ /* test if auditing is being forced */
@ -104,7 +105,7 @@ static int audit_caps(struct aa_profile *profile, int cap, int error)
} }
put_cpu_var(audit_cache); put_cpu_var(audit_cache);
return aa_audit(type, profile, GFP_ATOMIC, &sa, audit_cb); return aa_audit(type, profile, &sa, audit_cb);
} }
/** /**
@ -133,11 +134,10 @@ int aa_capable(struct aa_profile *profile, int cap, int audit)
{ {
int error = profile_capable(profile, cap); int error = profile_capable(profile, cap);
if (!audit) { if (audit == SECURITY_CAP_NOAUDIT) {
if (COMPLAIN_MODE(profile)) if (!COMPLAIN_MODE(profile))
return complain_error(error); return error;
return error;
} }
return audit_caps(profile, cap, error); return audit_caps(profile, cap, audit, error);
} }

View File

@ -13,11 +13,11 @@
* License. * License.
* *
* *
* AppArmor sets confinement on every task, via the the aa_task_cxt and * AppArmor sets confinement on every task, via the the aa_task_ctx and
* the aa_task_cxt.profile, both of which are required and are not allowed * the aa_task_ctx.profile, both of which are required and are not allowed
* to be NULL. The aa_task_cxt is not reference counted and is unique * to be NULL. The aa_task_ctx is not reference counted and is unique
* to each cred (which is reference count). The profile pointed to by * to each cred (which is reference count). The profile pointed to by
* the task_cxt is reference counted. * the task_ctx is reference counted.
* *
* TODO * TODO
* If a task uses change_hat it currently does not return to the old * If a task uses change_hat it currently does not return to the old
@ -30,28 +30,28 @@
#include "include/policy.h" #include "include/policy.h"
/** /**
* aa_alloc_task_context - allocate a new task_cxt * aa_alloc_task_context - allocate a new task_ctx
* @flags: gfp flags for allocation * @flags: gfp flags for allocation
* *
* Returns: allocated buffer or NULL on failure * Returns: allocated buffer or NULL on failure
*/ */
struct aa_task_cxt *aa_alloc_task_context(gfp_t flags) struct aa_task_ctx *aa_alloc_task_context(gfp_t flags)
{ {
return kzalloc(sizeof(struct aa_task_cxt), flags); return kzalloc(sizeof(struct aa_task_ctx), flags);
} }
/** /**
* aa_free_task_context - free a task_cxt * aa_free_task_context - free a task_ctx
* @cxt: task_cxt to free (MAYBE NULL) * @ctx: task_ctx to free (MAYBE NULL)
*/ */
void aa_free_task_context(struct aa_task_cxt *cxt) void aa_free_task_context(struct aa_task_ctx *ctx)
{ {
if (cxt) { if (ctx) {
aa_put_profile(cxt->profile); aa_put_profile(ctx->profile);
aa_put_profile(cxt->previous); aa_put_profile(ctx->previous);
aa_put_profile(cxt->onexec); aa_put_profile(ctx->onexec);
kzfree(cxt); kzfree(ctx);
} }
} }
@ -60,7 +60,7 @@ void aa_free_task_context(struct aa_task_cxt *cxt)
* @new: a blank task context (NOT NULL) * @new: a blank task context (NOT NULL)
* @old: the task context to copy (NOT NULL) * @old: the task context to copy (NOT NULL)
*/ */
void aa_dup_task_context(struct aa_task_cxt *new, const struct aa_task_cxt *old) void aa_dup_task_context(struct aa_task_ctx *new, const struct aa_task_ctx *old)
{ {
*new = *old; *new = *old;
aa_get_profile(new->profile); aa_get_profile(new->profile);
@ -93,31 +93,36 @@ struct aa_profile *aa_get_task_profile(struct task_struct *task)
*/ */
int aa_replace_current_profile(struct aa_profile *profile) int aa_replace_current_profile(struct aa_profile *profile)
{ {
struct aa_task_cxt *cxt = current_cxt(); struct aa_task_ctx *ctx = current_ctx();
struct cred *new; struct cred *new;
BUG_ON(!profile); AA_BUG(!profile);
if (cxt->profile == profile) if (ctx->profile == profile)
return 0; return 0;
if (current_cred() != current_real_cred())
return -EBUSY;
new = prepare_creds(); new = prepare_creds();
if (!new) if (!new)
return -ENOMEM; return -ENOMEM;
cxt = cred_cxt(new); ctx = cred_ctx(new);
if (unconfined(profile) || (cxt->profile->ns != profile->ns)) if (unconfined(profile) || (ctx->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_clear_task_cxt_trans(cxt); aa_clear_task_ctx_trans(ctx);
/* be careful switching cxt->profile, when racing replacement it /*
* is possible that cxt->profile->replacedby->profile is the reference * be careful switching ctx->profile, when racing replacement it
* is possible that ctx->profile->proxy->profile is the reference
* keeping @profile valid, so make sure to get its reference before * keeping @profile valid, so make sure to get its reference before
* dropping the reference on cxt->profile */ * dropping the reference on ctx->profile
*/
aa_get_profile(profile); aa_get_profile(profile);
aa_put_profile(cxt->profile); aa_put_profile(ctx->profile);
cxt->profile = profile; ctx->profile = profile;
commit_creds(new); commit_creds(new);
return 0; return 0;
@ -131,15 +136,15 @@ 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)
{ {
struct aa_task_cxt *cxt; struct aa_task_ctx *ctx;
struct cred *new = prepare_creds(); struct cred *new = prepare_creds();
if (!new) if (!new)
return -ENOMEM; return -ENOMEM;
cxt = cred_cxt(new); ctx = cred_ctx(new);
aa_get_profile(profile); aa_get_profile(profile);
aa_put_profile(cxt->onexec); aa_put_profile(ctx->onexec);
cxt->onexec = profile; ctx->onexec = profile;
commit_creds(new); commit_creds(new);
return 0; return 0;
@ -157,28 +162,28 @@ 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)
{ {
struct aa_task_cxt *cxt; struct aa_task_ctx *ctx;
struct cred *new = prepare_creds(); struct cred *new = prepare_creds();
if (!new) if (!new)
return -ENOMEM; return -ENOMEM;
BUG_ON(!profile); AA_BUG(!profile);
cxt = cred_cxt(new); ctx = cred_ctx(new);
if (!cxt->previous) { if (!ctx->previous) {
/* transfer refcount */ /* transfer refcount */
cxt->previous = cxt->profile; ctx->previous = ctx->profile;
cxt->token = token; ctx->token = token;
} else if (cxt->token == token) { } else if (ctx->token == token) {
aa_put_profile(cxt->profile); aa_put_profile(ctx->profile);
} else { } else {
/* previous_profile && cxt->token != token */ /* previous_profile && ctx->token != token */
abort_creds(new); abort_creds(new);
return -EACCES; return -EACCES;
} }
cxt->profile = aa_get_newest_profile(profile); ctx->profile = aa_get_newest_profile(profile);
/* clear exec on switching context */ /* clear exec on switching context */
aa_put_profile(cxt->onexec); aa_put_profile(ctx->onexec);
cxt->onexec = NULL; ctx->onexec = NULL;
commit_creds(new); commit_creds(new);
return 0; return 0;
@ -195,27 +200,27 @@ int aa_set_current_hat(struct aa_profile *profile, u64 token)
*/ */
int aa_restore_previous_profile(u64 token) int aa_restore_previous_profile(u64 token)
{ {
struct aa_task_cxt *cxt; struct aa_task_ctx *ctx;
struct cred *new = prepare_creds(); struct cred *new = prepare_creds();
if (!new) if (!new)
return -ENOMEM; return -ENOMEM;
cxt = cred_cxt(new); ctx = cred_ctx(new);
if (cxt->token != token) { if (ctx->token != token) {
abort_creds(new); abort_creds(new);
return -EACCES; return -EACCES;
} }
/* ignore restores when there is no saved profile */ /* ignore restores when there is no saved profile */
if (!cxt->previous) { if (!ctx->previous) {
abort_creds(new); abort_creds(new);
return 0; return 0;
} }
aa_put_profile(cxt->profile); aa_put_profile(ctx->profile);
cxt->profile = aa_get_newest_profile(cxt->previous); ctx->profile = aa_get_newest_profile(ctx->previous);
BUG_ON(!cxt->profile); AA_BUG(!ctx->profile);
/* 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_ctx_trans(ctx);
commit_creds(new); commit_creds(new);
return 0; return 0;

View File

@ -29,6 +29,43 @@ unsigned int aa_hash_size(void)
return apparmor_hash_size; return apparmor_hash_size;
} }
char *aa_calc_hash(void *data, size_t len)
{
struct {
struct shash_desc shash;
char ctx[crypto_shash_descsize(apparmor_tfm)];
} desc;
char *hash = NULL;
int error = -ENOMEM;
if (!apparmor_tfm)
return NULL;
hash = kzalloc(apparmor_hash_size, GFP_KERNEL);
if (!hash)
goto fail;
desc.shash.tfm = apparmor_tfm;
desc.shash.flags = 0;
error = crypto_shash_init(&desc.shash);
if (error)
goto fail;
error = crypto_shash_update(&desc.shash, (u8 *) data, len);
if (error)
goto fail;
error = crypto_shash_final(&desc.shash, hash);
if (error)
goto fail;
return hash;
fail:
kfree(hash);
return ERR_PTR(error);
}
int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start, int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start,
size_t len) size_t len)
{ {
@ -37,7 +74,7 @@ int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start,
char ctx[crypto_shash_descsize(apparmor_tfm)]; char ctx[crypto_shash_descsize(apparmor_tfm)];
} desc; } desc;
int error = -ENOMEM; int error = -ENOMEM;
u32 le32_version = cpu_to_le32(version); __le32 le32_version = cpu_to_le32(version);
if (!aa_g_hash_policy) if (!aa_g_hash_policy)
return 0; return 0;

View File

@ -29,6 +29,7 @@
#include "include/match.h" #include "include/match.h"
#include "include/path.h" #include "include/path.h"
#include "include/policy.h" #include "include/policy.h"
#include "include/policy_ns.h"
/** /**
* aa_free_domain_entries - free entries in a domain table * aa_free_domain_entries - free entries in a domain table
@ -93,7 +94,7 @@ out:
* Returns: permission set * Returns: permission set
*/ */
static struct file_perms change_profile_perms(struct aa_profile *profile, static struct file_perms change_profile_perms(struct aa_profile *profile,
struct aa_namespace *ns, struct aa_ns *ns,
const char *name, u32 request, const char *name, u32 request,
unsigned int start) unsigned int start)
{ {
@ -170,7 +171,7 @@ static struct aa_profile *__attach_match(const char *name,
* *
* Returns: profile or NULL if no match found * Returns: profile or NULL if no match found
*/ */
static struct aa_profile *find_attach(struct aa_namespace *ns, static struct aa_profile *find_attach(struct aa_ns *ns,
struct list_head *list, const char *name) struct list_head *list, const char *name)
{ {
struct aa_profile *profile; struct aa_profile *profile;
@ -239,7 +240,7 @@ static const char *next_name(int xtype, const char *name)
static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex)
{ {
struct aa_profile *new_profile = NULL; struct aa_profile *new_profile = NULL;
struct aa_namespace *ns = profile->ns; struct aa_ns *ns = profile->ns;
u32 xtype = xindex & AA_X_TYPE_MASK; u32 xtype = xindex & AA_X_TYPE_MASK;
int index = xindex & AA_X_INDEX_MASK; int index = xindex & AA_X_INDEX_MASK;
const char *name; const char *name;
@ -247,7 +248,7 @@ static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex)
/* index is guaranteed to be in range, validated at load time */ /* index is guaranteed to be in range, validated at load time */
for (name = profile->file.trans.table[index]; !new_profile && name; for (name = profile->file.trans.table[index]; !new_profile && name;
name = next_name(xtype, name)) { name = next_name(xtype, name)) {
struct aa_namespace *new_ns; struct aa_ns *new_ns;
const char *xname = NULL; const char *xname = NULL;
new_ns = NULL; new_ns = NULL;
@ -267,7 +268,7 @@ static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex)
; ;
} }
/* released below */ /* released below */
new_ns = aa_find_namespace(ns, ns_name); new_ns = aa_find_ns(ns, ns_name);
if (!new_ns) if (!new_ns)
continue; continue;
} else if (*name == '@') { } else if (*name == '@') {
@ -280,7 +281,7 @@ static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex)
/* released by caller */ /* released by caller */
new_profile = aa_lookup_profile(new_ns ? new_ns : ns, xname); new_profile = aa_lookup_profile(new_ns ? new_ns : ns, xname);
aa_put_namespace(new_ns); aa_put_ns(new_ns);
} }
/* released by caller */ /* released by caller */
@ -301,7 +302,7 @@ static struct aa_profile *x_to_profile(struct aa_profile *profile,
const char *name, u32 xindex) const char *name, u32 xindex)
{ {
struct aa_profile *new_profile = NULL; struct aa_profile *new_profile = NULL;
struct aa_namespace *ns = profile->ns; struct aa_ns *ns = profile->ns;
u32 xtype = xindex & AA_X_TYPE_MASK; u32 xtype = xindex & AA_X_TYPE_MASK;
switch (xtype) { switch (xtype) {
@ -336,9 +337,9 @@ static struct aa_profile *x_to_profile(struct aa_profile *profile,
*/ */
int apparmor_bprm_set_creds(struct linux_binprm *bprm) int apparmor_bprm_set_creds(struct linux_binprm *bprm)
{ {
struct aa_task_cxt *cxt; struct aa_task_ctx *ctx;
struct aa_profile *profile, *new_profile = NULL; struct aa_profile *profile, *new_profile = NULL;
struct aa_namespace *ns; struct aa_ns *ns;
char *buffer = NULL; char *buffer = NULL;
unsigned int state; unsigned int state;
struct file_perms perms = {}; struct file_perms perms = {};
@ -352,10 +353,10 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
if (bprm->cred_prepared) if (bprm->cred_prepared)
return 0; return 0;
cxt = cred_cxt(bprm->cred); ctx = cred_ctx(bprm->cred);
BUG_ON(!cxt); AA_BUG(!ctx);
profile = aa_get_newest_profile(cxt->profile); profile = aa_get_newest_profile(ctx->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
@ -379,9 +380,9 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
*/ */
if (unconfined(profile)) { if (unconfined(profile)) {
/* unconfined task */ /* unconfined task */
if (cxt->onexec) if (ctx->onexec)
/* change_profile on exec already been granted */ /* change_profile on exec already been granted */
new_profile = aa_get_profile(cxt->onexec); new_profile = aa_get_profile(ctx->onexec);
else else
new_profile = find_attach(ns, &ns->base.profiles, name); new_profile = find_attach(ns, &ns->base.profiles, name);
if (!new_profile) if (!new_profile)
@ -396,10 +397,10 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
/* find exec permissions for name */ /* find exec permissions for name */
state = aa_str_perms(profile->file.dfa, state, name, &cond, &perms); state = aa_str_perms(profile->file.dfa, state, name, &cond, &perms);
if (cxt->onexec) { if (ctx->onexec) {
struct file_perms cp; struct file_perms cp;
info = "change_profile onexec"; info = "change_profile onexec";
new_profile = aa_get_newest_profile(cxt->onexec); new_profile = aa_get_newest_profile(ctx->onexec);
if (!(perms.allow & AA_MAY_ONEXEC)) if (!(perms.allow & AA_MAY_ONEXEC))
goto audit; goto audit;
@ -408,8 +409,8 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
* exec\0change_profile * exec\0change_profile
*/ */
state = aa_dfa_null_transition(profile->file.dfa, state); state = aa_dfa_null_transition(profile->file.dfa, state);
cp = change_profile_perms(profile, cxt->onexec->ns, cp = change_profile_perms(profile, ctx->onexec->ns,
cxt->onexec->base.name, ctx->onexec->base.name,
AA_MAY_ONEXEC, state); AA_MAY_ONEXEC, state);
if (!(cp.allow & AA_MAY_ONEXEC)) if (!(cp.allow & AA_MAY_ONEXEC))
@ -441,7 +442,8 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
} }
} else if (COMPLAIN_MODE(profile)) { } else if (COMPLAIN_MODE(profile)) {
/* no exec permission - are we in learning mode */ /* no exec permission - are we in learning mode */
new_profile = aa_new_null_profile(profile, 0); new_profile = aa_new_null_profile(profile, false, name,
GFP_ATOMIC);
if (!new_profile) { if (!new_profile) {
error = -ENOMEM; error = -ENOMEM;
info = "could not create null profile"; info = "could not create null profile";
@ -497,17 +499,16 @@ apply:
bprm->per_clear |= PER_CLEAR_ON_SETID; bprm->per_clear |= PER_CLEAR_ON_SETID;
x_clear: x_clear:
aa_put_profile(cxt->profile); aa_put_profile(ctx->profile);
/* transfer new profile reference will be released when cxt is freed */ /* transfer new profile reference will be released when ctx is freed */
cxt->profile = new_profile; ctx->profile = new_profile;
new_profile = NULL; new_profile = NULL;
/* clear out all temporary/transitional state from the context */ /* clear out all temporary/transitional state from the context */
aa_clear_task_cxt_trans(cxt); aa_clear_task_ctx_trans(ctx);
audit: audit:
error = aa_audit_file(profile, &perms, GFP_KERNEL, OP_EXEC, MAY_EXEC, error = aa_audit_file(profile, &perms, OP_EXEC, MAY_EXEC, name,
name,
new_profile ? new_profile->base.hname : NULL, new_profile ? new_profile->base.hname : NULL,
cond.uid, info, error); cond.uid, info, error);
@ -543,17 +544,17 @@ 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 = cred_cxt(bprm->cred); struct aa_task_ctx *new_ctx = cred_ctx(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_ctx->profile == profile) ||
(unconfined(new_cxt->profile))) (unconfined(new_ctx->profile)))
return; return;
current->pdeath_signal = 0; current->pdeath_signal = 0;
/* reset soft limits and set hard limits for the new profile */ /* reset soft limits and set hard limits for the new profile */
__aa_transition_rlimits(profile, new_cxt->profile); __aa_transition_rlimits(profile, new_ctx->profile);
} }
/** /**
@ -602,7 +603,7 @@ static char *new_compound_name(const char *n1, const char *n2)
int aa_change_hat(const char *hats[], int count, u64 token, bool permtest) int aa_change_hat(const char *hats[], int count, u64 token, bool permtest)
{ {
const struct cred *cred; const struct cred *cred;
struct aa_task_cxt *cxt; struct aa_task_ctx *ctx;
struct aa_profile *profile, *previous_profile, *hat = NULL; struct aa_profile *profile, *previous_profile, *hat = NULL;
char *name = NULL; char *name = NULL;
int i; int i;
@ -620,9 +621,9 @@ 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_cxt(cred); ctx = cred_ctx(cred);
profile = aa_get_newest_profile(aa_cred_profile(cred)); profile = aa_get_newest_profile(aa_cred_profile(cred));
previous_profile = aa_get_newest_profile(cxt->previous); previous_profile = aa_get_newest_profile(ctx->previous);
if (unconfined(profile)) { if (unconfined(profile)) {
info = "unconfined"; info = "unconfined";
@ -666,7 +667,8 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest)
aa_put_profile(root); 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, true, hats[0],
GFP_KERNEL);
if (!hat) { if (!hat) {
info = "failed null profile create"; info = "failed null profile create";
error = -ENOMEM; error = -ENOMEM;
@ -711,9 +713,9 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest)
audit: audit:
if (!permtest) if (!permtest)
error = aa_audit_file(profile, &perms, GFP_KERNEL, error = aa_audit_file(profile, &perms, OP_CHANGE_HAT,
OP_CHANGE_HAT, AA_MAY_CHANGEHAT, NULL, AA_MAY_CHANGEHAT, NULL, target,
target, GLOBAL_ROOT_UID, info, error); GLOBAL_ROOT_UID, info, error);
out: out:
aa_put_profile(hat); aa_put_profile(hat);
@ -727,8 +729,7 @@ out:
/** /**
* aa_change_profile - perform a one-way profile transition * aa_change_profile - perform a one-way profile transition
* @ns_name: name of the profile namespace to change to (MAYBE NULL) * @fqname: name of profile may include namespace (NOT NULL)
* @hname: name of profile to change to (MAYBE NULL)
* @onexec: whether this transition is to take place immediately or at exec * @onexec: whether this transition is to take place immediately or at exec
* @permtest: true if this is just a permission test * @permtest: true if this is just a permission test
* *
@ -740,19 +741,20 @@ out:
* *
* Returns %0 on success, error otherwise. * Returns %0 on success, error otherwise.
*/ */
int aa_change_profile(const char *ns_name, const char *hname, bool onexec, int aa_change_profile(const char *fqname, bool onexec,
bool permtest) bool permtest, bool stack)
{ {
const struct cred *cred; const struct cred *cred;
struct aa_profile *profile, *target = NULL; struct aa_profile *profile, *target = NULL;
struct aa_namespace *ns = NULL;
struct file_perms perms = {}; struct file_perms perms = {};
const char *name = NULL, *info = NULL; const char *info = NULL, *op;
int op, error = 0; int error = 0;
u32 request; u32 request;
if (!hname && !ns_name) if (!fqname || !*fqname) {
AA_DEBUG("no profile name");
return -EINVAL; return -EINVAL;
}
if (onexec) { if (onexec) {
request = AA_MAY_ONEXEC; request = AA_MAY_ONEXEC;
@ -777,44 +779,15 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec,
return -EPERM; return -EPERM;
} }
if (ns_name) { target = aa_fqlookupn_profile(profile, fqname, strlen(fqname));
/* released below */
ns = aa_find_namespace(profile->ns, ns_name);
if (!ns) {
/* we don't create new namespace in complain mode */
name = ns_name;
info = "namespace not found";
error = -ENOENT;
goto audit;
}
} else
/* released below */
ns = aa_get_namespace(profile->ns);
/* if the name was not specified, use the name of the current profile */
if (!hname) {
if (unconfined(profile))
hname = ns->unconfined->base.hname;
else
hname = profile->base.hname;
}
perms = change_profile_perms(profile, ns, hname, request,
profile->file.start);
if (!(perms.allow & request)) {
error = -EACCES;
goto audit;
}
/* released below */
target = aa_lookup_profile(ns, hname);
if (!target) { if (!target) {
info = "profile not found"; info = "profile not found";
error = -ENOENT; error = -ENOENT;
if (permtest || !COMPLAIN_MODE(profile)) if (permtest || !COMPLAIN_MODE(profile))
goto audit; goto audit;
/* released below */ /* released below */
target = aa_new_null_profile(profile, 0); target = aa_new_null_profile(profile, false, fqname,
GFP_KERNEL);
if (!target) { if (!target) {
info = "failed null profile create"; info = "failed null profile create";
error = -ENOMEM; error = -ENOMEM;
@ -822,6 +795,13 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec,
} }
} }
perms = change_profile_perms(profile, target->ns, target->base.hname,
request, profile->file.start);
if (!(perms.allow & request)) {
error = -EACCES;
goto audit;
}
/* check if tracing task is allowed to trace target domain */ /* check if tracing task is allowed to trace target domain */
error = may_change_ptraced_domain(target); error = may_change_ptraced_domain(target);
if (error) { if (error) {
@ -839,10 +819,9 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec,
audit: audit:
if (!permtest) if (!permtest)
error = aa_audit_file(profile, &perms, GFP_KERNEL, op, request, error = aa_audit_file(profile, &perms, op, request, NULL,
name, hname, GLOBAL_ROOT_UID, info, error); fqname, GLOBAL_ROOT_UID, info, error);
aa_put_namespace(ns);
aa_put_profile(target); aa_put_profile(target);
put_cred(cred); put_cred(cred);

View File

@ -67,24 +67,24 @@ static void file_audit_cb(struct audit_buffer *ab, void *va)
struct common_audit_data *sa = va; struct common_audit_data *sa = va;
kuid_t fsuid = current_fsuid(); kuid_t fsuid = current_fsuid();
if (sa->aad->fs.request & AA_AUDIT_FILE_MASK) { if (aad(sa)->fs.request & AA_AUDIT_FILE_MASK) {
audit_log_format(ab, " requested_mask="); audit_log_format(ab, " requested_mask=");
audit_file_mask(ab, sa->aad->fs.request); audit_file_mask(ab, aad(sa)->fs.request);
} }
if (sa->aad->fs.denied & AA_AUDIT_FILE_MASK) { if (aad(sa)->fs.denied & AA_AUDIT_FILE_MASK) {
audit_log_format(ab, " denied_mask="); audit_log_format(ab, " denied_mask=");
audit_file_mask(ab, sa->aad->fs.denied); audit_file_mask(ab, aad(sa)->fs.denied);
} }
if (sa->aad->fs.request & AA_AUDIT_FILE_MASK) { if (aad(sa)->fs.request & AA_AUDIT_FILE_MASK) {
audit_log_format(ab, " fsuid=%d", audit_log_format(ab, " fsuid=%d",
from_kuid(&init_user_ns, fsuid)); from_kuid(&init_user_ns, fsuid));
audit_log_format(ab, " ouid=%d", audit_log_format(ab, " ouid=%d",
from_kuid(&init_user_ns, sa->aad->fs.ouid)); from_kuid(&init_user_ns, aad(sa)->fs.ouid));
} }
if (sa->aad->fs.target) { if (aad(sa)->fs.target) {
audit_log_format(ab, " target="); audit_log_format(ab, " target=");
audit_log_untrustedstring(ab, sa->aad->fs.target); audit_log_untrustedstring(ab, aad(sa)->fs.target);
} }
} }
@ -104,54 +104,53 @@ static void file_audit_cb(struct audit_buffer *ab, void *va)
* Returns: %0 or error on failure * Returns: %0 or error on failure
*/ */
int aa_audit_file(struct aa_profile *profile, struct file_perms *perms, int aa_audit_file(struct aa_profile *profile, struct file_perms *perms,
gfp_t gfp, int op, u32 request, const char *name, const char *op, u32 request, const char *name,
const char *target, kuid_t ouid, const char *info, int error) const char *target, kuid_t ouid, const char *info, int error)
{ {
int type = AUDIT_APPARMOR_AUTO; int type = AUDIT_APPARMOR_AUTO;
struct common_audit_data sa; DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_TASK, op);
struct apparmor_audit_data aad = {0,};
sa.type = LSM_AUDIT_DATA_TASK;
sa.u.tsk = NULL;
sa.aad = &aad;
aad.op = op,
aad.fs.request = request;
aad.name = name;
aad.fs.target = target;
aad.fs.ouid = ouid;
aad.info = info;
aad.error = error;
if (likely(!sa.aad->error)) { sa.u.tsk = NULL;
aad(&sa)->fs.request = request;
aad(&sa)->name = name;
aad(&sa)->fs.target = target;
aad(&sa)->fs.ouid = ouid;
aad(&sa)->info = info;
aad(&sa)->error = error;
sa.u.tsk = NULL;
if (likely(!aad(&sa)->error)) {
u32 mask = perms->audit; u32 mask = perms->audit;
if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL)) if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL))
mask = 0xffff; mask = 0xffff;
/* mask off perms that are not being force audited */ /* mask off perms that are not being force audited */
sa.aad->fs.request &= mask; aad(&sa)->fs.request &= mask;
if (likely(!sa.aad->fs.request)) if (likely(!aad(&sa)->fs.request))
return 0; return 0;
type = AUDIT_APPARMOR_AUDIT; type = AUDIT_APPARMOR_AUDIT;
} else { } else {
/* only report permissions that were denied */ /* only report permissions that were denied */
sa.aad->fs.request = sa.aad->fs.request & ~perms->allow; aad(&sa)->fs.request = aad(&sa)->fs.request & ~perms->allow;
AA_BUG(!aad(&sa)->fs.request);
if (sa.aad->fs.request & perms->kill) if (aad(&sa)->fs.request & perms->kill)
type = AUDIT_APPARMOR_KILL; type = AUDIT_APPARMOR_KILL;
/* quiet known rejects, assumes quiet and kill do not overlap */ /* quiet known rejects, assumes quiet and kill do not overlap */
if ((sa.aad->fs.request & perms->quiet) && if ((aad(&sa)->fs.request & perms->quiet) &&
AUDIT_MODE(profile) != AUDIT_NOQUIET && AUDIT_MODE(profile) != AUDIT_NOQUIET &&
AUDIT_MODE(profile) != AUDIT_ALL) AUDIT_MODE(profile) != AUDIT_ALL)
sa.aad->fs.request &= ~perms->quiet; aad(&sa)->fs.request &= ~perms->quiet;
if (!sa.aad->fs.request) if (!aad(&sa)->fs.request)
return COMPLAIN_MODE(profile) ? 0 : sa.aad->error; return COMPLAIN_MODE(profile) ? 0 : aad(&sa)->error;
} }
sa.aad->fs.denied = sa.aad->fs.request & ~perms->allow; aad(&sa)->fs.denied = aad(&sa)->fs.request & ~perms->allow;
return aa_audit(type, profile, gfp, &sa, file_audit_cb); return aa_audit(type, profile, &sa, file_audit_cb);
} }
/** /**
@ -276,8 +275,9 @@ static inline bool is_deleted(struct dentry *dentry)
* *
* Returns: %0 else error if access denied or other error * Returns: %0 else error if access denied or other error
*/ */
int aa_path_perm(int op, struct aa_profile *profile, const struct path *path, int aa_path_perm(const char *op, struct aa_profile *profile,
int flags, u32 request, struct path_cond *cond) const struct path *path, int flags, u32 request,
struct path_cond *cond)
{ {
char *buffer = NULL; char *buffer = NULL;
struct file_perms perms = {}; struct file_perms perms = {};
@ -301,8 +301,8 @@ int aa_path_perm(int op, struct aa_profile *profile, const struct path *path,
if (request & ~perms.allow) if (request & ~perms.allow)
error = -EACCES; error = -EACCES;
} }
error = aa_audit_file(profile, &perms, GFP_KERNEL, op, request, name, error = aa_audit_file(profile, &perms, op, request, name, NULL,
NULL, cond->uid, info, error); cond->uid, info, error);
kfree(buffer); kfree(buffer);
return error; return error;
@ -349,8 +349,8 @@ static inline bool xindex_is_subset(u32 link, u32 target)
int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry, int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry,
const struct path *new_dir, struct dentry *new_dentry) const struct path *new_dir, struct dentry *new_dentry)
{ {
struct path link = { new_dir->mnt, new_dentry }; struct path link = { .mnt = new_dir->mnt, .dentry = new_dentry };
struct path target = { new_dir->mnt, old_dentry }; struct path target = { .mnt = new_dir->mnt, .dentry = old_dentry };
struct path_cond cond = { struct path_cond cond = {
d_backing_inode(old_dentry)->i_uid, d_backing_inode(old_dentry)->i_uid,
d_backing_inode(old_dentry)->i_mode d_backing_inode(old_dentry)->i_mode
@ -429,7 +429,7 @@ done_tests:
error = 0; error = 0;
audit: audit:
error = aa_audit_file(profile, &lperms, GFP_KERNEL, OP_LINK, request, error = aa_audit_file(profile, &lperms, OP_LINK, request,
lname, tname, cond.uid, info, error); lname, tname, cond.uid, info, error);
kfree(buffer); kfree(buffer);
kfree(buffer2); kfree(buffer2);
@ -446,7 +446,7 @@ audit:
* *
* Returns: %0 if access allowed else error * Returns: %0 if access allowed else error
*/ */
int aa_file_perm(int op, struct aa_profile *profile, struct file *file, int aa_file_perm(const char *op, struct aa_profile *profile, struct file *file,
u32 request) u32 request)
{ {
struct path_cond cond = { struct path_cond cond = {

View File

@ -1,7 +1,7 @@
/* /*
* AppArmor security module * AppArmor security module
* *
* This file contains AppArmor basic global and lib definitions * This file contains AppArmor basic global
* *
* Copyright (C) 1998-2008 Novell/SUSE * Copyright (C) 1998-2008 Novell/SUSE
* Copyright 2009-2010 Canonical Ltd. * Copyright 2009-2010 Canonical Ltd.
@ -15,10 +15,7 @@
#ifndef __APPARMOR_H #ifndef __APPARMOR_H
#define __APPARMOR_H #define __APPARMOR_H
#include <linux/slab.h> #include <linux/types.h>
#include <linux/fs.h>
#include "match.h"
/* /*
* Class of mediation types in the AppArmor policy db * Class of mediation types in the AppArmor policy db
@ -43,73 +40,4 @@ extern bool aa_g_logsyscall;
extern bool aa_g_paranoid_load; extern bool aa_g_paranoid_load;
extern unsigned int aa_g_path_max; extern unsigned int aa_g_path_max;
/*
* DEBUG remains global (no per profile flag) since it is mostly used in sysctl
* which is not related to profile accesses.
*/
#define AA_DEBUG(fmt, args...) \
do { \
if (aa_g_debug && printk_ratelimit()) \
printk(KERN_DEBUG "AppArmor: " fmt, ##args); \
} while (0)
#define AA_ERROR(fmt, args...) \
do { \
if (printk_ratelimit()) \
printk(KERN_ERR "AppArmor: " fmt, ##args); \
} while (0)
/* Flag indicating whether initialization completed */
extern int apparmor_initialized __initdata;
/* fn's in lib */
char *aa_split_fqname(char *args, char **ns_name);
void aa_info_message(const char *str);
void *__aa_kvmalloc(size_t size, gfp_t flags);
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
* @str: a null terminated string
* @sub: a substring, not necessarily null terminated
* @len: length of @sub to compare
*
* The @str string must be full consumed for this to be considered a match
*/
static inline bool aa_strneq(const char *str, const char *sub, int len)
{
return !strncmp(str, sub, len) && !str[len];
}
/**
* aa_dfa_null_transition - step to next state after null character
* @dfa: the dfa to match against
* @start: the state of the dfa to start matching in
*
* aa_dfa_null_transition transitions to the next state after a null
* character which is not used in standard matching and is only
* used to separate pairs.
*/
static inline unsigned int aa_dfa_null_transition(struct aa_dfa *dfa,
unsigned int start)
{
/* the null transition only needs the string's null terminator byte */
return aa_dfa_next(dfa, start, 0);
}
static inline bool mediated_filesystem(struct dentry *dentry)
{
return !(dentry->d_sb->s_flags & MS_NOUSER);
}
#endif /* __APPARMOR_H */ #endif /* __APPARMOR_H */

View File

@ -15,6 +15,8 @@
#ifndef __AA_APPARMORFS_H #ifndef __AA_APPARMORFS_H
#define __AA_APPARMORFS_H #define __AA_APPARMORFS_H
extern struct path aa_null;
enum aa_fs_type { enum aa_fs_type {
AA_FS_TYPE_BOOLEAN, AA_FS_TYPE_BOOLEAN,
AA_FS_TYPE_STRING, AA_FS_TYPE_STRING,
@ -62,12 +64,16 @@ 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_profile;
struct aa_namespace; struct aa_ns;
enum aafs_ns_type { enum aafs_ns_type {
AAFS_NS_DIR, AAFS_NS_DIR,
AAFS_NS_PROFS, AAFS_NS_PROFS,
AAFS_NS_NS, AAFS_NS_NS,
AAFS_NS_RAW_DATA,
AAFS_NS_LOAD,
AAFS_NS_REPLACE,
AAFS_NS_REMOVE,
AAFS_NS_COUNT, AAFS_NS_COUNT,
AAFS_NS_MAX_COUNT, AAFS_NS_MAX_COUNT,
AAFS_NS_SIZE, AAFS_NS_SIZE,
@ -83,12 +89,19 @@ enum aafs_prof_type {
AAFS_PROF_MODE, AAFS_PROF_MODE,
AAFS_PROF_ATTACH, AAFS_PROF_ATTACH,
AAFS_PROF_HASH, AAFS_PROF_HASH,
AAFS_PROF_RAW_DATA,
AAFS_PROF_RAW_HASH,
AAFS_PROF_RAW_ABI,
AAFS_PROF_SIZEOF, AAFS_PROF_SIZEOF,
}; };
#define ns_dir(X) ((X)->dents[AAFS_NS_DIR]) #define ns_dir(X) ((X)->dents[AAFS_NS_DIR])
#define ns_subns_dir(X) ((X)->dents[AAFS_NS_NS]) #define ns_subns_dir(X) ((X)->dents[AAFS_NS_NS])
#define ns_subprofs_dir(X) ((X)->dents[AAFS_NS_PROFS]) #define ns_subprofs_dir(X) ((X)->dents[AAFS_NS_PROFS])
#define ns_subdata_dir(X) ((X)->dents[AAFS_NS_RAW_DATA])
#define ns_subload(X) ((X)->dents[AAFS_NS_LOAD])
#define ns_subreplace(X) ((X)->dents[AAFS_NS_REPLACE])
#define ns_subremove(X) ((X)->dents[AAFS_NS_REMOVE])
#define prof_dir(X) ((X)->dents[AAFS_PROF_DIR]) #define prof_dir(X) ((X)->dents[AAFS_PROF_DIR])
#define prof_child_dir(X) ((X)->dents[AAFS_PROF_PROFS]) #define prof_child_dir(X) ((X)->dents[AAFS_PROF_PROFS])
@ -97,8 +110,8 @@ void __aa_fs_profile_rmdir(struct aa_profile *profile);
void __aa_fs_profile_migrate_dents(struct aa_profile *old, void __aa_fs_profile_migrate_dents(struct aa_profile *old,
struct aa_profile *new); struct aa_profile *new);
int __aa_fs_profile_mkdir(struct aa_profile *profile, struct dentry *parent); int __aa_fs_profile_mkdir(struct aa_profile *profile, struct dentry *parent);
void __aa_fs_namespace_rmdir(struct aa_namespace *ns); void __aa_fs_ns_rmdir(struct aa_ns *ns);
int __aa_fs_namespace_mkdir(struct aa_namespace *ns, struct dentry *parent, int __aa_fs_ns_mkdir(struct aa_ns *ns, struct dentry *parent,
const char *name); const char *name);
#endif /* __AA_APPARMORFS_H */ #endif /* __AA_APPARMORFS_H */

View File

@ -46,97 +46,115 @@ enum audit_type {
AUDIT_APPARMOR_AUTO AUDIT_APPARMOR_AUTO
}; };
extern const char *const op_table[]; #define OP_NULL NULL
enum aa_ops {
OP_NULL,
OP_SYSCTL, #define OP_SYSCTL "sysctl"
OP_CAPABLE, #define OP_CAPABLE "capable"
OP_UNLINK, #define OP_UNLINK "unlink"
OP_MKDIR, #define OP_MKDIR "mkdir"
OP_RMDIR, #define OP_RMDIR "rmdir"
OP_MKNOD, #define OP_MKNOD "mknod"
OP_TRUNC, #define OP_TRUNC "truncate"
OP_LINK, #define OP_LINK "link"
OP_SYMLINK, #define OP_SYMLINK "symlink"
OP_RENAME_SRC, #define OP_RENAME_SRC "rename_src"
OP_RENAME_DEST, #define OP_RENAME_DEST "rename_dest"
OP_CHMOD, #define OP_CHMOD "chmod"
OP_CHOWN, #define OP_CHOWN "chown"
OP_GETATTR, #define OP_GETATTR "getattr"
OP_OPEN, #define OP_OPEN "open"
OP_FPERM, #define OP_FPERM "file_perm"
OP_FLOCK, #define OP_FLOCK "file_lock"
OP_FMMAP, #define OP_FMMAP "file_mmap"
OP_FMPROT, #define OP_FMPROT "file_mprotect"
OP_CREATE, #define OP_CREATE "create"
OP_POST_CREATE, #define OP_POST_CREATE "post_create"
OP_BIND, #define OP_BIND "bind"
OP_CONNECT, #define OP_CONNECT "connect"
OP_LISTEN, #define OP_LISTEN "listen"
OP_ACCEPT, #define OP_ACCEPT "accept"
OP_SENDMSG, #define OP_SENDMSG "sendmsg"
OP_RECVMSG, #define OP_RECVMSG "recvmsg"
OP_GETSOCKNAME, #define OP_GETSOCKNAME "getsockname"
OP_GETPEERNAME, #define OP_GETPEERNAME "getpeername"
OP_GETSOCKOPT, #define OP_GETSOCKOPT "getsockopt"
OP_SETSOCKOPT, #define OP_SETSOCKOPT "setsockopt"
OP_SOCK_SHUTDOWN, #define OP_SHUTDOWN "socket_shutdown"
OP_PTRACE, #define OP_PTRACE "ptrace"
OP_EXEC, #define OP_EXEC "exec"
OP_CHANGE_HAT,
OP_CHANGE_PROFILE,
OP_CHANGE_ONEXEC,
OP_SETPROCATTR, #define OP_CHANGE_HAT "change_hat"
OP_SETRLIMIT, #define OP_CHANGE_PROFILE "change_profile"
#define OP_CHANGE_ONEXEC "change_onexec"
OP_PROF_REPL, #define OP_SETPROCATTR "setprocattr"
OP_PROF_LOAD, #define OP_SETRLIMIT "setrlimit"
OP_PROF_RM,
}; #define OP_PROF_REPL "profile_replace"
#define OP_PROF_LOAD "profile_load"
#define OP_PROF_RM "profile_remove"
struct apparmor_audit_data { struct apparmor_audit_data {
int error; int error;
int op; const char *op;
int type; int type;
void *profile; void *profile;
const char *name; const char *name;
const char *info; const char *info;
union { union {
void *target; /* these entries require a custom callback fn */
struct { struct {
struct aa_profile *peer;
struct {
const char *target;
u32 request;
u32 denied;
kuid_t ouid;
} fs;
};
struct {
const char *name;
long pos; long pos;
void *target; const char *ns;
} iface; } iface;
struct { struct {
int rlim; int rlim;
unsigned long max; unsigned long max;
} rlim; } rlim;
struct {
const char *target;
u32 request;
u32 denied;
kuid_t ouid;
} fs;
}; };
}; };
/* define a short hand for apparmor_audit_data structure */ /* macros for dealing with apparmor_audit_data structure */
#define aad apparmor_audit_data #define aad(SA) ((SA)->apparmor_audit_data)
#define DEFINE_AUDIT_DATA(NAME, T, X) \
/* TODO: cleanup audit init so we don't need _aad = {0,} */ \
struct apparmor_audit_data NAME ## _aad = { .op = (X), }; \
struct common_audit_data NAME = \
{ \
.type = (T), \
.u.tsk = NULL, \
}; \
NAME.apparmor_audit_data = &(NAME ## _aad)
void aa_audit_msg(int type, struct common_audit_data *sa, void aa_audit_msg(int type, struct common_audit_data *sa,
void (*cb) (struct audit_buffer *, void *)); void (*cb) (struct audit_buffer *, void *));
int aa_audit(int type, struct aa_profile *profile, gfp_t gfp, int aa_audit(int type, struct aa_profile *profile, struct common_audit_data *sa,
struct common_audit_data *sa,
void (*cb) (struct audit_buffer *, void *)); void (*cb) (struct audit_buffer *, void *));
#define aa_audit_error(ERROR, SA, CB) \
({ \
aad((SA))->error = (ERROR); \
aa_audit_msg(AUDIT_APPARMOR_ERROR, (SA), (CB)); \
aad((SA))->error; \
})
static inline int complain_error(int error) static inline int complain_error(int error)
{ {
if (error == -EPERM || error == -EACCES) if (error == -EPERM || error == -EACCES)

View File

@ -20,44 +20,45 @@
#include <linux/sched.h> #include <linux/sched.h>
#include "policy.h" #include "policy.h"
#include "policy_ns.h"
#define cred_cxt(X) (X)->security #define cred_ctx(X) ((X)->security)
#define current_cxt() cred_cxt(current_cred()) #define current_ctx() cred_ctx(current_cred())
/* struct aa_file_cxt - the AppArmor context the file was opened in /* struct aa_file_ctx - the AppArmor context the file was opened in
* @perms: the permission the file was opened with * @perms: the permission the file was opened with
* *
* The file_cxt could currently be directly stored in file->f_security * The file_ctx could currently be directly stored in file->f_security
* as the profile reference is now stored in the f_cred. However the * as the profile reference is now stored in the f_cred. However the
* cxt struct will expand in the future so we keep the struct. * ctx struct will expand in the future so we keep the struct.
*/ */
struct aa_file_cxt { struct aa_file_ctx {
u16 allow; u16 allow;
}; };
/** /**
* aa_alloc_file_context - allocate file_cxt * aa_alloc_file_context - allocate file_ctx
* @gfp: gfp flags for allocation * @gfp: gfp flags for allocation
* *
* Returns: file_cxt or NULL on failure * Returns: file_ctx or NULL on failure
*/ */
static inline struct aa_file_cxt *aa_alloc_file_context(gfp_t gfp) static inline struct aa_file_ctx *aa_alloc_file_context(gfp_t gfp)
{ {
return kzalloc(sizeof(struct aa_file_cxt), gfp); return kzalloc(sizeof(struct aa_file_ctx), gfp);
} }
/** /**
* aa_free_file_context - free a file_cxt * aa_free_file_context - free a file_ctx
* @cxt: file_cxt to free (MAYBE_NULL) * @ctx: file_ctx to free (MAYBE_NULL)
*/ */
static inline void aa_free_file_context(struct aa_file_cxt *cxt) static inline void aa_free_file_context(struct aa_file_ctx *ctx)
{ {
if (cxt) if (ctx)
kzfree(cxt); kzfree(ctx);
} }
/** /**
* struct aa_task_cxt - primary label for confined tasks * struct aa_task_ctx - primary label for confined tasks
* @profile: the current profile (NOT NULL) * @profile: the current profile (NOT NULL)
* @exec: profile to transition to on next exec (MAYBE NULL) * @exec: profile to transition to on next exec (MAYBE NULL)
* @previous: profile the task may return to (MAYBE NULL) * @previous: profile the task may return to (MAYBE NULL)
@ -68,17 +69,17 @@ static inline void aa_free_file_context(struct aa_file_cxt *cxt)
* *
* TODO: make so a task can be confined by a stack of contexts * TODO: make so a task can be confined by a stack of contexts
*/ */
struct aa_task_cxt { struct aa_task_ctx {
struct aa_profile *profile; struct aa_profile *profile;
struct aa_profile *onexec; struct aa_profile *onexec;
struct aa_profile *previous; struct aa_profile *previous;
u64 token; u64 token;
}; };
struct aa_task_cxt *aa_alloc_task_context(gfp_t flags); struct aa_task_ctx *aa_alloc_task_context(gfp_t flags);
void aa_free_task_context(struct aa_task_cxt *cxt); void aa_free_task_context(struct aa_task_ctx *ctx);
void aa_dup_task_context(struct aa_task_cxt *new, void aa_dup_task_context(struct aa_task_ctx *new,
const struct aa_task_cxt *old); const struct aa_task_ctx *old);
int aa_replace_current_profile(struct aa_profile *profile); 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);
@ -96,9 +97,10 @@ struct aa_profile *aa_get_task_profile(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_cxt(cred); struct aa_task_ctx *ctx = cred_ctx(cred);
BUG_ON(!cxt || !cxt->profile);
return cxt->profile; AA_BUG(!ctx || !ctx->profile);
return ctx->profile;
} }
/** /**
@ -148,31 +150,37 @@ 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_cxt(); const struct aa_task_ctx *ctx = current_ctx();
struct aa_profile *profile; struct aa_profile *profile;
BUG_ON(!cxt || !cxt->profile);
if (PROFILE_INVALID(cxt->profile)) { AA_BUG(!ctx || !ctx->profile);
profile = aa_get_newest_profile(cxt->profile);
if (profile_is_stale(ctx->profile)) {
profile = aa_get_newest_profile(ctx->profile);
aa_replace_current_profile(profile); aa_replace_current_profile(profile);
aa_put_profile(profile); aa_put_profile(profile);
cxt = current_cxt(); ctx = current_ctx();
} }
return cxt->profile; return ctx->profile;
}
static inline struct aa_ns *aa_get_current_ns(void)
{
return aa_get_ns(__aa_current_profile()->ns);
} }
/** /**
* aa_clear_task_cxt_trans - clear transition tracking info from the cxt * aa_clear_task_ctx_trans - clear transition tracking info from the ctx
* @cxt: task context to clear (NOT NULL) * @ctx: task context to clear (NOT NULL)
*/ */
static inline void aa_clear_task_cxt_trans(struct aa_task_cxt *cxt) static inline void aa_clear_task_ctx_trans(struct aa_task_ctx *ctx)
{ {
aa_put_profile(cxt->previous); aa_put_profile(ctx->previous);
aa_put_profile(cxt->onexec); aa_put_profile(ctx->onexec);
cxt->previous = NULL; ctx->previous = NULL;
cxt->onexec = NULL; ctx->onexec = NULL;
cxt->token = 0; ctx->token = 0;
} }
#endif /* __AA_CONTEXT_H */ #endif /* __AA_CONTEXT_H */

View File

@ -18,9 +18,14 @@
#ifdef CONFIG_SECURITY_APPARMOR_HASH #ifdef CONFIG_SECURITY_APPARMOR_HASH
unsigned int aa_hash_size(void); unsigned int aa_hash_size(void);
char *aa_calc_hash(void *data, size_t len);
int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start, int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start,
size_t len); size_t len);
#else #else
static inline char *aa_calc_hash(void *data, size_t len)
{
return NULL;
}
static inline int aa_calc_profile_hash(struct aa_profile *profile, u32 version, static inline int aa_calc_profile_hash(struct aa_profile *profile, u32 version,
void *start, size_t len) void *start, size_t len)
{ {

View File

@ -30,7 +30,7 @@ void apparmor_bprm_committed_creds(struct linux_binprm *bprm);
void aa_free_domain_entries(struct aa_domain *domain); void aa_free_domain_entries(struct aa_domain *domain);
int aa_change_hat(const char *hats[], int count, u64 token, bool permtest); int aa_change_hat(const char *hats[], int count, u64 token, bool permtest);
int aa_change_profile(const char *ns_name, const char *name, bool onexec, int aa_change_profile(const char *fqname, bool onexec, bool permtest,
bool permtest); bool stack);
#endif /* __AA_DOMAIN_H */ #endif /* __AA_DOMAIN_H */

View File

@ -145,7 +145,7 @@ static inline u16 dfa_map_xindex(u16 mask)
dfa_map_xindex((ACCEPT_TABLE(dfa)[state] >> 14) & 0x3fff) dfa_map_xindex((ACCEPT_TABLE(dfa)[state] >> 14) & 0x3fff)
int aa_audit_file(struct aa_profile *profile, struct file_perms *perms, int aa_audit_file(struct aa_profile *profile, struct file_perms *perms,
gfp_t gfp, int op, u32 request, const char *name, const char *op, u32 request, const char *name,
const char *target, kuid_t ouid, const char *info, int error); const char *target, kuid_t ouid, const char *info, int error);
/** /**
@ -171,13 +171,14 @@ unsigned int aa_str_perms(struct aa_dfa *dfa, unsigned int start,
const char *name, struct path_cond *cond, const char *name, struct path_cond *cond,
struct file_perms *perms); struct file_perms *perms);
int aa_path_perm(int op, struct aa_profile *profile, const struct path *path, int aa_path_perm(const char *op, struct aa_profile *profile,
int flags, u32 request, struct path_cond *cond); const struct path *path, int flags, u32 request,
struct path_cond *cond);
int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry, int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry,
const struct path *new_dir, struct dentry *new_dentry); const struct path *new_dir, struct dentry *new_dentry);
int aa_file_perm(int op, struct aa_profile *profile, struct file *file, int aa_file_perm(const char *op, struct aa_profile *profile, struct file *file,
u32 request); u32 request);
static inline void aa_free_file_rules(struct aa_file_rules *rules) static inline void aa_free_file_rules(struct aa_file_rules *rules)

View File

@ -0,0 +1,194 @@
/*
* AppArmor security module
*
* This file contains AppArmor lib definitions
*
* 2017 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 __AA_LIB_H
#define __AA_LIB_H
#include <linux/slab.h>
#include <linux/fs.h>
#include "match.h"
/* Provide our own test for whether a write lock is held for asserts
* this is because on none SMP systems write_can_lock will always
* resolve to true, which is what you want for code making decisions
* based on it, but wrong for asserts checking that the lock is held
*/
#ifdef CONFIG_SMP
#define write_is_locked(X) !write_can_lock(X)
#else
#define write_is_locked(X) (1)
#endif /* CONFIG_SMP */
/*
* DEBUG remains global (no per profile flag) since it is mostly used in sysctl
* which is not related to profile accesses.
*/
#define DEBUG_ON (aa_g_debug)
#define dbg_printk(__fmt, __args...) pr_debug(__fmt, ##__args)
#define AA_DEBUG(fmt, args...) \
do { \
if (DEBUG_ON) \
pr_debug_ratelimited("AppArmor: " fmt, ##args); \
} while (0)
#define AA_WARN(X) WARN((X), "APPARMOR WARN %s: %s\n", __func__, #X)
#define AA_BUG(X, args...) AA_BUG_FMT((X), "" args)
#ifdef CONFIG_SECURITY_APPARMOR_DEBUG_ASSERTS
#define AA_BUG_FMT(X, fmt, args...) \
WARN((X), "AppArmor WARN %s: (" #X "): " fmt, __func__, ##args)
#else
#define AA_BUG_FMT(X, fmt, args...)
#endif
#define AA_ERROR(fmt, args...) \
pr_err_ratelimited("AppArmor: " fmt, ##args)
/* Flag indicating whether initialization completed */
extern int apparmor_initialized __initdata;
/* fn's in lib */
char *aa_split_fqname(char *args, char **ns_name);
const char *aa_splitn_fqname(const char *fqname, size_t n, const char **ns_name,
size_t *ns_len);
void aa_info_message(const char *str);
void *__aa_kvmalloc(size_t size, gfp_t flags);
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
* @str: a null terminated string
* @sub: a substring, not necessarily null terminated
* @len: length of @sub to compare
*
* The @str string must be full consumed for this to be considered a match
*/
static inline bool aa_strneq(const char *str, const char *sub, int len)
{
return !strncmp(str, sub, len) && !str[len];
}
/**
* aa_dfa_null_transition - step to next state after null character
* @dfa: the dfa to match against
* @start: the state of the dfa to start matching in
*
* aa_dfa_null_transition transitions to the next state after a null
* character which is not used in standard matching and is only
* used to separate pairs.
*/
static inline unsigned int aa_dfa_null_transition(struct aa_dfa *dfa,
unsigned int start)
{
/* the null transition only needs the string's null terminator byte */
return aa_dfa_next(dfa, start, 0);
}
static inline bool path_mediated_fs(struct dentry *dentry)
{
return !(dentry->d_sb->s_flags & MS_NOUSER);
}
/* struct aa_policy - common part of both namespaces and profiles
* @name: name of the object
* @hname - The hierarchical name
* @list: list policy object is on
* @profiles: head of the profiles list contained in the object
*/
struct aa_policy {
const char *name;
const char *hname;
struct list_head list;
struct list_head profiles;
};
/**
* basename - find the last component of an hname
* @name: hname to find the base profile name component of (NOT NULL)
*
* Returns: the tail (base profile name) name component of an hname
*/
static inline const char *basename(const char *hname)
{
char *split;
hname = strim((char *)hname);
for (split = strstr(hname, "//"); split; split = strstr(hname, "//"))
hname = split + 2;
return hname;
}
/**
* __policy_find - find a policy by @name on a policy list
* @head: list to search (NOT NULL)
* @name: name to search for (NOT NULL)
*
* Requires: rcu_read_lock be held
*
* Returns: unrefcounted policy that match @name or NULL if not found
*/
static inline struct aa_policy *__policy_find(struct list_head *head,
const char *name)
{
struct aa_policy *policy;
list_for_each_entry_rcu(policy, head, list) {
if (!strcmp(policy->name, name))
return policy;
}
return NULL;
}
/**
* __policy_strn_find - find a policy that's name matches @len chars of @str
* @head: list to search (NOT NULL)
* @str: string to search for (NOT NULL)
* @len: length of match required
*
* Requires: rcu_read_lock be held
*
* Returns: unrefcounted policy that match @str or NULL if not found
*
* if @len == strlen(@strlen) then this is equiv to __policy_find
* other wise it allows searching for policy by a partial match of name
*/
static inline struct aa_policy *__policy_strn_find(struct list_head *head,
const char *str, int len)
{
struct aa_policy *policy;
list_for_each_entry_rcu(policy, head, list) {
if (aa_strneq(policy->name, str, len))
return policy;
}
return NULL;
}
bool aa_policy_init(struct aa_policy *policy, const char *prefix,
const char *name, gfp_t gfp);
void aa_policy_destroy(struct aa_policy *policy);
#endif /* AA_LIB_H */

View File

@ -100,13 +100,15 @@ struct aa_dfa {
struct table_header *tables[YYTD_ID_TSIZE]; struct table_header *tables[YYTD_ID_TSIZE];
}; };
extern struct aa_dfa *nulldfa;
#define byte_to_byte(X) (X) #define byte_to_byte(X) (X)
#define UNPACK_ARRAY(TABLE, BLOB, LEN, TYPE, NTOHX) \ #define UNPACK_ARRAY(TABLE, BLOB, LEN, TTYPE, BTYPE, NTOHX) \
do { \ do { \
typeof(LEN) __i; \ typeof(LEN) __i; \
TYPE *__t = (TYPE *) TABLE; \ TTYPE *__t = (TTYPE *) TABLE; \
TYPE *__b = (TYPE *) BLOB; \ BTYPE *__b = (BTYPE *) BLOB; \
for (__i = 0; __i < LEN; __i++) { \ for (__i = 0; __i < LEN; __i++) { \
__t[__i] = NTOHX(__b[__i]); \ __t[__i] = NTOHX(__b[__i]); \
} \ } \
@ -117,6 +119,9 @@ static inline size_t table_size(size_t len, size_t el_size)
return ALIGN(sizeof(struct table_header) + len * el_size, 8); return ALIGN(sizeof(struct table_header) + len * el_size, 8);
} }
int aa_setup_dfa_engine(void);
void aa_teardown_dfa_engine(void);
struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags); struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags);
unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start, unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start,
const char *str, int len); const char *str, int len);
@ -127,6 +132,21 @@ unsigned int aa_dfa_next(struct aa_dfa *dfa, unsigned int state,
void aa_dfa_free_kref(struct kref *kref); void aa_dfa_free_kref(struct kref *kref);
/**
* aa_get_dfa - increment refcount on dfa @p
* @dfa: dfa (MAYBE NULL)
*
* Returns: pointer to @dfa if @dfa is NULL will return NULL
* Requires: @dfa must be held with valid refcount when called
*/
static inline struct aa_dfa *aa_get_dfa(struct aa_dfa *dfa)
{
if (dfa)
kref_get(&(dfa->count));
return dfa;
}
/** /**
* aa_put_dfa - put a dfa refcount * aa_put_dfa - put a dfa refcount
* @dfa: dfa to put refcount (MAYBE NULL) * @dfa: dfa to put refcount (MAYBE NULL)

View File

@ -29,4 +29,57 @@ enum path_flags {
int aa_path_name(const struct path *path, int flags, char **buffer, int aa_path_name(const struct path *path, int flags, char **buffer,
const char **name, const char **info); const char **name, const char **info);
#define MAX_PATH_BUFFERS 2
/* Per cpu buffers used during mediation */
/* preallocated buffers to use during path lookups */
struct aa_buffers {
char *buf[MAX_PATH_BUFFERS];
};
#include <linux/percpu.h>
#include <linux/preempt.h>
DECLARE_PER_CPU(struct aa_buffers, aa_buffers);
#define COUNT_ARGS(X...) COUNT_ARGS_HELPER(, ##X, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define COUNT_ARGS_HELPER(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, n, X...) n
#define CONCAT(X, Y) X ## Y
#define CONCAT_AFTER(X, Y) CONCAT(X, Y)
#define ASSIGN(FN, X, N) ((X) = FN(N))
#define EVAL1(FN, X) ASSIGN(FN, X, 0) /*X = FN(0)*/
#define EVAL2(FN, X, Y...) do { ASSIGN(FN, X, 1); EVAL1(FN, Y); } while (0)
#define EVAL(FN, X...) CONCAT_AFTER(EVAL, COUNT_ARGS(X))(FN, X)
#define for_each_cpu_buffer(I) for ((I) = 0; (I) < MAX_PATH_BUFFERS; (I)++)
#ifdef CONFIG_DEBUG_PREEMPT
#define AA_BUG_PREEMPT_ENABLED(X) AA_BUG(preempt_count() <= 0, X)
#else
#define AA_BUG_PREEMPT_ENABLED(X) /* nop */
#endif
#define __get_buffer(N) ({ \
struct aa_buffers *__cpu_var; \
AA_BUG_PREEMPT_ENABLED("__get_buffer without preempt disabled"); \
__cpu_var = this_cpu_ptr(&aa_buffers); \
__cpu_var->buf[(N)]; })
#define __get_buffers(X...) EVAL(__get_buffer, X)
#define __put_buffers(X, Y...) ((void)&(X))
#define get_buffers(X...) \
do { \
preempt_disable(); \
__get_buffers(X); \
} while (0)
#define put_buffers(X, Y...) \
do { \
__put_buffers(X, Y); \
preempt_enable(); \
} while (0)
#endif /* __AA_PATH_H */ #endif /* __AA_PATH_H */

View File

@ -18,6 +18,7 @@
#include <linux/capability.h> #include <linux/capability.h>
#include <linux/cred.h> #include <linux/cred.h>
#include <linux/kref.h> #include <linux/kref.h>
#include <linux/rhashtable.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/socket.h> #include <linux/socket.h>
@ -27,8 +28,14 @@
#include "capability.h" #include "capability.h"
#include "domain.h" #include "domain.h"
#include "file.h" #include "file.h"
#include "lib.h"
#include "resource.h" #include "resource.h"
struct aa_ns;
extern int unprivileged_userns_apparmor_policy;
extern const char *const aa_profile_mode_names[]; extern const char *const aa_profile_mode_names[];
#define APPARMOR_MODE_NAMES_MAX_INDEX 4 #define APPARMOR_MODE_NAMES_MAX_INDEX 4
@ -42,7 +49,7 @@ extern const char *const aa_profile_mode_names[];
#define PROFILE_IS_HAT(_profile) ((_profile)->flags & PFLAG_HAT) #define PROFILE_IS_HAT(_profile) ((_profile)->flags & PFLAG_HAT)
#define PROFILE_INVALID(_profile) ((_profile)->flags & PFLAG_INVALID) #define profile_is_stale(_profile) ((_profile)->flags & PFLAG_STALE)
#define on_list_rcu(X) (!list_empty(X) && (X)->prev != LIST_POISON2) #define on_list_rcu(X) (!list_empty(X) && (X)->prev != LIST_POISON2)
@ -67,7 +74,7 @@ enum profile_flags {
PFLAG_USER_DEFINED = 0x20, /* user based profile - lower privs */ PFLAG_USER_DEFINED = 0x20, /* user based profile - lower privs */
PFLAG_NO_LIST_REF = 0x40, /* list doesn't keep profile ref */ PFLAG_NO_LIST_REF = 0x40, /* list doesn't keep profile ref */
PFLAG_OLD_NULL_TRANS = 0x100, /* use // as the null transition */ PFLAG_OLD_NULL_TRANS = 0x100, /* use // as the null transition */
PFLAG_INVALID = 0x200, /* profile replaced/removed */ PFLAG_STALE = 0x200, /* profile replaced/removed */
PFLAG_NS_COUNT = 0x400, /* carries NS ref count */ PFLAG_NS_COUNT = 0x400, /* carries NS ref count */
/* These flags must correspond with PATH_flags */ /* These flags must correspond with PATH_flags */
@ -76,70 +83,6 @@ enum profile_flags {
struct aa_profile; struct aa_profile;
/* struct aa_policy - common part of both namespaces and profiles
* @name: name of the object
* @hname - The hierarchical name
* @list: list policy object is on
* @profiles: head of the profiles list contained in the object
*/
struct aa_policy {
char *name;
char *hname;
struct list_head list;
struct list_head profiles;
};
/* struct aa_ns_acct - accounting of profiles in namespace
* @max_size: maximum space allowed for all profiles in namespace
* @max_count: maximum number of profiles that can be in this namespace
* @size: current size of profiles
* @count: current count of profiles (includes null profiles)
*/
struct aa_ns_acct {
int max_size;
int max_count;
int size;
int count;
};
/* struct aa_namespace - namespace for a set of profiles
* @base: common policy
* @parent: parent of namespace
* @lock: lock for modifying the object
* @acct: accounting for the namespace
* @unconfined: special unconfined profile for the namespace
* @sub_ns: list of namespaces under the current namespace.
* @uniq_null: uniq value used for null learning profiles
* @uniq_id: a unique id count for the profiles in the namespace
* @dents: dentries for the namespaces file entries in apparmorfs
*
* 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
* aa_namespaces and profile names within a namespace are guaranteed to be
* unique. When profiles in separate namespaces have the same name they
* are NOT considered to be equivalent.
*
* Namespaces are hierarchical and only namespaces and profiles below the
* current namespace are visible.
*
* Namespace names must be unique and can not contain the characters :/\0
*
* FIXME TODO: add vserver support of namespaces (can it all be done in
* userspace?)
*/
struct aa_namespace {
struct aa_policy base;
struct aa_namespace *parent;
struct mutex lock;
struct aa_ns_acct acct;
struct aa_profile *unconfined;
struct list_head sub_ns;
atomic_t uniq_null;
long uniq_id;
struct dentry *dents[AAFS_NS_SIZEOF];
};
/* struct aa_policydb - match engine for a policy /* struct aa_policydb - match engine for a policy
* dfa: dfa pattern match * dfa: dfa pattern match
* start: set of start states for the different classes of data * start: set of start states for the different classes of data
@ -151,11 +94,24 @@ struct aa_policydb {
}; };
struct aa_replacedby { struct aa_proxy {
struct kref count; struct kref count;
struct aa_profile __rcu *profile; struct aa_profile __rcu *profile;
}; };
/* struct aa_data - generic data structure
* key: name for retrieving this data
* size: size of data in bytes
* data: binary data
* head: reserved for rhashtable
*/
struct aa_data {
char *key;
u32 size;
char *data;
struct rhash_head head;
};
/* struct aa_profile - basic confinement data /* struct aa_profile - basic confinement data
* @base - base components of the profile (name, refcount, lists, lock ...) * @base - base components of the profile (name, refcount, lists, lock ...)
@ -163,7 +119,7 @@ struct aa_replacedby {
* @rcu: rcu head used when removing from @list * @rcu: rcu head used when removing from @list
* @parent: parent of profile * @parent: parent of profile
* @ns: namespace the profile is in * @ns: namespace the profile is in
* @replacedby: is set to the profile that replaced this profile * @proxy: is set to the profile that replaced this profile
* @rename: optional profile name that this profile renamed * @rename: optional profile name that this profile renamed
* @attach: human readable attachment string * @attach: human readable attachment string
* @xmatch: optional extended matching for unconfined executables names * @xmatch: optional extended matching for unconfined executables names
@ -180,13 +136,14 @@ struct aa_replacedby {
* *
* @dents: dentries for the profiles file entries in apparmorfs * @dents: dentries for the profiles file entries in apparmorfs
* @dirname: name of the profile dir in apparmorfs * @dirname: name of the profile dir in apparmorfs
* @data: hashtable for free-form policy aa_data
* *
* The AppArmor profile contains the basic confinement data. Each profile * The AppArmor profile contains the basic confinement data. Each profile
* has a name, and exists in a namespace. The @name and @exec_match are * has a name, and exists in a namespace. The @name and @exec_match are
* used to determine profile attachment against unconfined tasks. All other * used to determine profile attachment against unconfined tasks. All other
* attachments are determined by profile X transition rules. * attachments are determined by profile X transition rules.
* *
* The @replacedby struct is write protected by the profile lock. * The @proxy struct is write protected by the profile lock.
* *
* Profiles have a hierarchy where hats and children profiles keep * Profiles have a hierarchy where hats and children profiles keep
* a reference to their parent. * a reference to their parent.
@ -201,8 +158,8 @@ struct aa_profile {
struct rcu_head rcu; struct rcu_head rcu;
struct aa_profile __rcu *parent; struct aa_profile __rcu *parent;
struct aa_namespace *ns; struct aa_ns *ns;
struct aa_replacedby *replacedby; struct aa_proxy *proxy;
const char *rename; const char *rename;
const char *attach; const char *attach;
@ -219,37 +176,39 @@ struct aa_profile {
struct aa_caps caps; struct aa_caps caps;
struct aa_rlimit rlimits; struct aa_rlimit rlimits;
struct aa_loaddata *rawdata;
unsigned char *hash; unsigned char *hash;
char *dirname; char *dirname;
struct dentry *dents[AAFS_PROF_SIZEOF]; struct dentry *dents[AAFS_PROF_SIZEOF];
struct rhashtable *data;
}; };
extern struct aa_namespace *root_ns;
extern enum profile_mode aa_g_profile_mode; extern enum profile_mode aa_g_profile_mode;
void __aa_update_proxy(struct aa_profile *orig, struct aa_profile *new);
void aa_add_profile(struct aa_policy *common, struct aa_profile *profile); void aa_add_profile(struct aa_policy *common, struct aa_profile *profile);
bool aa_ns_visible(struct aa_namespace *curr, struct aa_namespace *view);
const char *aa_ns_name(struct aa_namespace *parent, struct aa_namespace *child);
int aa_alloc_root_ns(void);
void aa_free_root_ns(void);
void aa_free_namespace_kref(struct kref *kref);
struct aa_namespace *aa_find_namespace(struct aa_namespace *root, void aa_free_proxy_kref(struct kref *kref);
const char *name); struct aa_profile *aa_alloc_profile(const char *name, gfp_t gfp);
struct aa_profile *aa_new_null_profile(struct aa_profile *parent, bool hat,
const char *base, gfp_t gfp);
void aa_free_replacedby_kref(struct kref *kref);
struct aa_profile *aa_alloc_profile(const char *name);
struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat);
void aa_free_profile(struct aa_profile *profile); void aa_free_profile(struct aa_profile *profile);
void aa_free_profile_kref(struct kref *kref); void aa_free_profile_kref(struct kref *kref);
struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name); struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name);
struct aa_profile *aa_lookup_profile(struct aa_namespace *ns, const char *name); struct aa_profile *aa_lookupn_profile(struct aa_ns *ns, const char *hname,
struct aa_profile *aa_match_profile(struct aa_namespace *ns, const char *name); size_t n);
struct aa_profile *aa_lookup_profile(struct aa_ns *ns, const char *name);
struct aa_profile *aa_fqlookupn_profile(struct aa_profile *base,
const char *fqname, size_t n);
struct aa_profile *aa_match_profile(struct aa_ns *ns, const char *name);
ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace); ssize_t aa_replace_profiles(struct aa_ns *view, struct aa_profile *profile,
ssize_t aa_remove_profiles(char *name, size_t size); bool noreplace, struct aa_loaddata *udata);
ssize_t aa_remove_profiles(struct aa_ns *view, struct aa_profile *profile,
char *name, size_t size);
void __aa_profile_list_release(struct list_head *head);
#define PROF_ADD 1 #define PROF_ADD 1
#define PROF_REPLACE 0 #define PROF_REPLACE 0
@ -257,12 +216,6 @@ ssize_t aa_remove_profiles(char *name, size_t size);
#define unconfined(X) ((X)->mode == APPARMOR_UNCONFINED) #define unconfined(X) ((X)->mode == APPARMOR_UNCONFINED)
static inline struct aa_profile *aa_deref_parent(struct aa_profile *p)
{
return rcu_dereference_protected(p->parent,
mutex_is_locked(&p->ns->lock));
}
/** /**
* aa_get_profile - increment refcount on profile @p * aa_get_profile - increment refcount on profile @p
* @p: profile (MAYBE NULL) * @p: profile (MAYBE NULL)
@ -326,8 +279,8 @@ static inline struct aa_profile *aa_get_newest_profile(struct aa_profile *p)
if (!p) if (!p)
return NULL; return NULL;
if (PROFILE_INVALID(p)) if (profile_is_stale(p))
return aa_get_profile_rcu(&p->replacedby->profile); return aa_get_profile_rcu(&p->proxy->profile);
return aa_get_profile(p); return aa_get_profile(p);
} }
@ -342,7 +295,7 @@ static inline void aa_put_profile(struct aa_profile *p)
kref_put(&p->count, aa_free_profile_kref); kref_put(&p->count, aa_free_profile_kref);
} }
static inline struct aa_replacedby *aa_get_replacedby(struct aa_replacedby *p) static inline struct aa_proxy *aa_get_proxy(struct aa_proxy *p)
{ {
if (p) if (p)
kref_get(&(p->count)); kref_get(&(p->count));
@ -350,49 +303,10 @@ static inline struct aa_replacedby *aa_get_replacedby(struct aa_replacedby *p)
return p; return p;
} }
static inline void aa_put_replacedby(struct aa_replacedby *p) static inline void aa_put_proxy(struct aa_proxy *p)
{ {
if (p) if (p)
kref_put(&p->count, aa_free_replacedby_kref); kref_put(&p->count, aa_free_proxy_kref);
}
/* requires profile list write lock held */
static inline void __aa_update_replacedby(struct aa_profile *orig,
struct aa_profile *new)
{
struct aa_profile *tmp;
tmp = rcu_dereference_protected(orig->replacedby->profile,
mutex_is_locked(&orig->ns->lock));
rcu_assign_pointer(orig->replacedby->profile, aa_get_profile(new));
orig->flags |= PFLAG_INVALID;
aa_put_profile(tmp);
}
/**
* aa_get_namespace - increment references count on @ns
* @ns: namespace to increment reference count of (MAYBE NULL)
*
* Returns: pointer to @ns, if @ns is NULL returns NULL
* Requires: @ns must be held with valid refcount when called
*/
static inline struct aa_namespace *aa_get_namespace(struct aa_namespace *ns)
{
if (ns)
aa_get_profile(ns->unconfined);
return ns;
}
/**
* aa_put_namespace - decrement refcount on @ns
* @ns: namespace to put reference of
*
* Decrement reference count of @ns and if no longer in use free it
*/
static inline void aa_put_namespace(struct aa_namespace *ns)
{
if (ns)
aa_put_profile(ns->unconfined);
} }
static inline int AUDIT_MODE(struct aa_profile *profile) static inline int AUDIT_MODE(struct aa_profile *profile)
@ -403,8 +317,9 @@ static inline int AUDIT_MODE(struct aa_profile *profile)
return profile->audit; return profile->audit;
} }
bool policy_view_capable(void); bool policy_view_capable(struct aa_ns *ns);
bool policy_admin_capable(void); bool policy_admin_capable(struct aa_ns *ns);
bool aa_may_manage_policy(int op); int aa_may_manage_policy(struct aa_profile *profile, struct aa_ns *ns,
const char *op);
#endif /* __AA_POLICY_H */ #endif /* __AA_POLICY_H */

View File

@ -0,0 +1,147 @@
/*
* AppArmor security module
*
* This file contains AppArmor policy definitions.
*
* Copyright (C) 1998-2008 Novell/SUSE
* Copyright 2009-2017 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 __AA_NAMESPACE_H
#define __AA_NAMESPACE_H
#include <linux/kref.h>
#include "apparmor.h"
#include "apparmorfs.h"
#include "policy.h"
/* struct aa_ns_acct - accounting of profiles in namespace
* @max_size: maximum space allowed for all profiles in namespace
* @max_count: maximum number of profiles that can be in this namespace
* @size: current size of profiles
* @count: current count of profiles (includes null profiles)
*/
struct aa_ns_acct {
int max_size;
int max_count;
int size;
int count;
};
/* struct aa_ns - namespace for a set of profiles
* @base: common policy
* @parent: parent of namespace
* @lock: lock for modifying the object
* @acct: accounting for the namespace
* @unconfined: special unconfined profile for the namespace
* @sub_ns: list of namespaces under the current namespace.
* @uniq_null: uniq value used for null learning profiles
* @uniq_id: a unique id count for the profiles in the namespace
* @level: level of ns within the tree hierarchy
* @dents: dentries for the namespaces file entries in apparmorfs
*
* An aa_ns defines the set profiles that are searched to determine which
* profile to attach to a task. Profiles can not be shared between aa_ns
* and profile names within a namespace are guaranteed to be unique. When
* profiles in separate namespaces have the same name they are NOT considered
* to be equivalent.
*
* Namespaces are hierarchical and only namespaces and profiles below the
* current namespace are visible.
*
* Namespace names must be unique and can not contain the characters :/\0
*/
struct aa_ns {
struct aa_policy base;
struct aa_ns *parent;
struct mutex lock;
struct aa_ns_acct acct;
struct aa_profile *unconfined;
struct list_head sub_ns;
atomic_t uniq_null;
long uniq_id;
int level;
struct dentry *dents[AAFS_NS_SIZEOF];
};
extern struct aa_ns *root_ns;
extern const char *aa_hidden_ns_name;
bool aa_ns_visible(struct aa_ns *curr, struct aa_ns *view, bool subns);
const char *aa_ns_name(struct aa_ns *parent, struct aa_ns *child, bool subns);
void aa_free_ns(struct aa_ns *ns);
int aa_alloc_root_ns(void);
void aa_free_root_ns(void);
void aa_free_ns_kref(struct kref *kref);
struct aa_ns *aa_find_ns(struct aa_ns *root, const char *name);
struct aa_ns *aa_findn_ns(struct aa_ns *root, const char *name, size_t n);
struct aa_ns *__aa_find_or_create_ns(struct aa_ns *parent, const char *name,
struct dentry *dir);
struct aa_ns *aa_prepare_ns(struct aa_ns *root, const char *name);
void __aa_remove_ns(struct aa_ns *ns);
static inline struct aa_profile *aa_deref_parent(struct aa_profile *p)
{
return rcu_dereference_protected(p->parent,
mutex_is_locked(&p->ns->lock));
}
/**
* aa_get_ns - increment references count on @ns
* @ns: namespace to increment reference count of (MAYBE NULL)
*
* Returns: pointer to @ns, if @ns is NULL returns NULL
* Requires: @ns must be held with valid refcount when called
*/
static inline struct aa_ns *aa_get_ns(struct aa_ns *ns)
{
if (ns)
aa_get_profile(ns->unconfined);
return ns;
}
/**
* aa_put_ns - decrement refcount on @ns
* @ns: namespace to put reference of
*
* Decrement reference count of @ns and if no longer in use free it
*/
static inline void aa_put_ns(struct aa_ns *ns)
{
if (ns)
aa_put_profile(ns->unconfined);
}
/**
* __aa_findn_ns - find a namespace on a list by @name
* @head: list to search for namespace on (NOT NULL)
* @name: name of namespace to look for (NOT NULL)
* @n: length of @name
* Returns: unrefcounted namespace
*
* Requires: rcu_read_lock be held
*/
static inline struct aa_ns *__aa_findn_ns(struct list_head *head,
const char *name, size_t n)
{
return (struct aa_ns *)__policy_strn_find(head, name, n);
}
static inline struct aa_ns *__aa_find_ns(struct list_head *head,
const char *name)
{
return __aa_findn_ns(head, name, strlen(name));
}
#endif /* AA_NAMESPACE_H */

View File

@ -16,12 +16,14 @@
#define __POLICY_INTERFACE_H #define __POLICY_INTERFACE_H
#include <linux/list.h> #include <linux/list.h>
#include <linux/kref.h>
struct aa_load_ent { struct aa_load_ent {
struct list_head list; struct list_head list;
struct aa_profile *new; struct aa_profile *new;
struct aa_profile *old; struct aa_profile *old;
struct aa_profile *rename; struct aa_profile *rename;
const char *ns_name;
}; };
void aa_load_ent_free(struct aa_load_ent *ent); void aa_load_ent_free(struct aa_load_ent *ent);
@ -34,6 +36,30 @@ struct aa_load_ent *aa_load_ent_alloc(void);
#define PACKED_MODE_KILL 2 #define PACKED_MODE_KILL 2
#define PACKED_MODE_UNCONFINED 3 #define PACKED_MODE_UNCONFINED 3
int aa_unpack(void *udata, size_t size, struct list_head *lh, const char **ns); /* struct aa_loaddata - buffer of policy load data set */
struct aa_loaddata {
struct kref count;
size_t size;
int abi;
unsigned char *hash;
char data[];
};
int aa_unpack(struct aa_loaddata *udata, struct list_head *lh, const char **ns);
static inline struct aa_loaddata *
aa_get_loaddata(struct aa_loaddata *data)
{
if (data)
kref_get(&(data->count));
return data;
}
void aa_loaddata_kref(struct kref *kref);
static inline void aa_put_loaddata(struct aa_loaddata *data)
{
if (data)
kref_put(&data->count, aa_loaddata_kref);
}
#endif /* __POLICY_INTERFACE_H */ #endif /* __POLICY_INTERFACE_H */

View File

@ -1,7 +1,7 @@
/* /*
* AppArmor security module * AppArmor security module
* *
* This file contains AppArmor security identifier (sid) definitions * This file contains AppArmor security identifier (secid) definitions
* *
* Copyright 2009-2010 Canonical Ltd. * Copyright 2009-2010 Canonical Ltd.
* *
@ -11,16 +11,16 @@
* License. * License.
*/ */
#ifndef __AA_SID_H #ifndef __AA_SECID_H
#define __AA_SID_H #define __AA_SECID_H
#include <linux/types.h> #include <linux/types.h>
/* sid value that will not be allocated */ /* secid value that will not be allocated */
#define AA_SID_INVALID 0 #define AA_SECID_INVALID 0
#define AA_SID_ALLOC AA_SID_INVALID #define AA_SECID_ALLOC AA_SECID_INVALID
u32 aa_alloc_sid(void); u32 aa_alloc_secid(void);
void aa_free_sid(u32 sid); void aa_free_secid(u32 secid);
#endif /* __AA_SID_H */ #endif /* __AA_SECID_H */

View File

@ -25,8 +25,8 @@
static void audit_cb(struct audit_buffer *ab, void *va) static void audit_cb(struct audit_buffer *ab, void *va)
{ {
struct common_audit_data *sa = va; struct common_audit_data *sa = va;
audit_log_format(ab, " target="); audit_log_format(ab, " peer=");
audit_log_untrustedstring(ab, sa->aad->target); audit_log_untrustedstring(ab, aad(sa)->peer->base.hname);
} }
/** /**
@ -40,16 +40,12 @@ static void audit_cb(struct audit_buffer *ab, void *va)
static int aa_audit_ptrace(struct aa_profile *profile, static int aa_audit_ptrace(struct aa_profile *profile,
struct aa_profile *target, int error) struct aa_profile *target, int error)
{ {
struct common_audit_data sa; DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_PTRACE);
struct apparmor_audit_data aad = {0,};
sa.type = LSM_AUDIT_DATA_NONE;
sa.aad = &aad;
aad.op = OP_PTRACE;
aad.target = target;
aad.error = error;
return aa_audit(AUDIT_APPARMOR_AUTO, profile, GFP_ATOMIC, &sa, aad(&sa)->peer = target;
audit_cb); aad(&sa)->error = error;
return aa_audit(AUDIT_APPARMOR_AUTO, profile, &sa, audit_cb);
} }
/** /**

View File

@ -12,6 +12,7 @@
* License. * License.
*/ */
#include <linux/ctype.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/string.h> #include <linux/string.h>
@ -19,7 +20,8 @@
#include "include/audit.h" #include "include/audit.h"
#include "include/apparmor.h" #include "include/apparmor.h"
#include "include/lib.h"
#include "include/policy.h"
/** /**
* aa_split_fqname - split a fqname into a profile and namespace name * aa_split_fqname - split a fqname into a profile and namespace name
@ -59,6 +61,58 @@ char *aa_split_fqname(char *fqname, char **ns_name)
return name; return name;
} }
/**
* skipn_spaces - Removes leading whitespace from @str.
* @str: The string to be stripped.
*
* Returns a pointer to the first non-whitespace character in @str.
* if all whitespace will return NULL
*/
static const char *skipn_spaces(const char *str, size_t n)
{
for (; n && isspace(*str); --n)
++str;
if (n)
return (char *)str;
return NULL;
}
const char *aa_splitn_fqname(const char *fqname, size_t n, const char **ns_name,
size_t *ns_len)
{
const char *end = fqname + n;
const char *name = skipn_spaces(fqname, n);
if (!name)
return NULL;
*ns_name = NULL;
*ns_len = 0;
if (name[0] == ':') {
char *split = strnchr(&name[1], end - &name[1], ':');
*ns_name = skipn_spaces(&name[1], end - &name[1]);
if (!*ns_name)
return NULL;
if (split) {
*ns_len = split - *ns_name;
if (*ns_len == 0)
*ns_name = NULL;
split++;
if (end - split > 1 && strncmp(split, "//", 2) == 0)
split += 2;
name = skipn_spaces(split, end - split);
} else {
/* a ns name without a following profile is allowed */
name = NULL;
*ns_len = end - *ns_name;
}
}
if (name && *name == 0)
name = NULL;
return name;
}
/** /**
* aa_info_message - log a none profile related status message * aa_info_message - log a none profile related status message
* @str: message to log * @str: message to log
@ -66,11 +120,9 @@ char *aa_split_fqname(char *fqname, char **ns_name)
void aa_info_message(const char *str) void aa_info_message(const char *str)
{ {
if (audit_enabled) { if (audit_enabled) {
struct common_audit_data sa; DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, NULL);
struct apparmor_audit_data aad = {0,};
sa.type = LSM_AUDIT_DATA_NONE; aad(&sa)->info = str;
sa.aad = &aad;
aad.info = str;
aa_audit_msg(AUDIT_APPARMOR_STATUS, &sa, NULL); aa_audit_msg(AUDIT_APPARMOR_STATUS, &sa, NULL);
} }
printk(KERN_INFO "AppArmor: %s\n", str); printk(KERN_INFO "AppArmor: %s\n", str);
@ -95,7 +147,8 @@ void *__aa_kvmalloc(size_t size, gfp_t flags)
/* 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, flags | GFP_NOIO | __GFP_NOWARN); buffer = kmalloc(size, flags | GFP_KERNEL | __GFP_NORETRY |
__GFP_NOWARN);
if (!buffer) { if (!buffer) {
if (flags & __GFP_ZERO) if (flags & __GFP_ZERO)
buffer = vzalloc(size); buffer = vzalloc(size);
@ -104,3 +157,47 @@ void *__aa_kvmalloc(size_t size, gfp_t flags)
} }
return buffer; return buffer;
} }
/**
* aa_policy_init - initialize a policy structure
* @policy: policy to initialize (NOT NULL)
* @prefix: prefix name if any is required. (MAYBE NULL)
* @name: name of the policy, init will make a copy of it (NOT NULL)
*
* Note: this fn creates a copy of strings passed in
*
* Returns: true if policy init successful
*/
bool aa_policy_init(struct aa_policy *policy, const char *prefix,
const char *name, gfp_t gfp)
{
/* freed by policy_free */
if (prefix) {
policy->hname = kmalloc(strlen(prefix) + strlen(name) + 3,
gfp);
if (policy->hname)
sprintf((char *)policy->hname, "%s//%s", prefix, name);
} else
policy->hname = kstrdup(name, gfp);
if (!policy->hname)
return 0;
/* base.name is a substring of fqname */
policy->name = basename(policy->hname);
INIT_LIST_HEAD(&policy->list);
INIT_LIST_HEAD(&policy->profiles);
return 1;
}
/**
* aa_policy_destroy - free the elements referenced by @policy
* @policy: policy that is to have its elements freed (NOT NULL)
*/
void aa_policy_destroy(struct aa_policy *policy)
{
AA_BUG(on_list_rcu(&policy->profiles));
AA_BUG(on_list_rcu(&policy->list));
/* don't free name as its a subset of hname */
kzfree(policy->hname);
}

View File

@ -23,6 +23,7 @@
#include <linux/sysctl.h> #include <linux/sysctl.h>
#include <linux/audit.h> #include <linux/audit.h>
#include <linux/user_namespace.h> #include <linux/user_namespace.h>
#include <linux/kmemleak.h>
#include <net/sock.h> #include <net/sock.h>
#include "include/apparmor.h" #include "include/apparmor.h"
@ -34,22 +35,26 @@
#include "include/ipc.h" #include "include/ipc.h"
#include "include/path.h" #include "include/path.h"
#include "include/policy.h" #include "include/policy.h"
#include "include/policy_ns.h"
#include "include/procattr.h" #include "include/procattr.h"
/* Flag indicating whether initialization completed */ /* Flag indicating whether initialization completed */
int apparmor_initialized __initdata; int apparmor_initialized __initdata;
DEFINE_PER_CPU(struct aa_buffers, aa_buffers);
/* /*
* LSM hook functions * LSM hook functions
*/ */
/* /*
* free the associated aa_task_cxt and put its profiles * free the associated aa_task_ctx and put its profiles
*/ */
static void apparmor_cred_free(struct cred *cred) static void apparmor_cred_free(struct cred *cred)
{ {
aa_free_task_context(cred_cxt(cred)); aa_free_task_context(cred_ctx(cred));
cred_cxt(cred) = NULL; cred_ctx(cred) = NULL;
} }
/* /*
@ -58,27 +63,29 @@ static void apparmor_cred_free(struct cred *cred)
static int apparmor_cred_alloc_blank(struct cred *cred, gfp_t gfp) static int apparmor_cred_alloc_blank(struct cred *cred, gfp_t gfp)
{ {
/* freed by apparmor_cred_free */ /* freed by apparmor_cred_free */
struct aa_task_cxt *cxt = aa_alloc_task_context(gfp); struct aa_task_ctx *ctx = aa_alloc_task_context(gfp);
if (!cxt)
if (!ctx)
return -ENOMEM; return -ENOMEM;
cred_cxt(cred) = cxt; cred_ctx(cred) = ctx;
return 0; return 0;
} }
/* /*
* prepare new aa_task_cxt for modification by prepare_cred block * prepare new aa_task_ctx for modification by prepare_cred block
*/ */
static int apparmor_cred_prepare(struct cred *new, const struct cred *old, static int apparmor_cred_prepare(struct cred *new, const struct cred *old,
gfp_t gfp) gfp_t gfp)
{ {
/* freed by apparmor_cred_free */ /* freed by apparmor_cred_free */
struct aa_task_cxt *cxt = aa_alloc_task_context(gfp); struct aa_task_ctx *ctx = aa_alloc_task_context(gfp);
if (!cxt)
if (!ctx)
return -ENOMEM; return -ENOMEM;
aa_dup_task_context(cxt, cred_cxt(old)); aa_dup_task_context(ctx, cred_ctx(old));
cred_cxt(new) = cxt; cred_ctx(new) = ctx;
return 0; return 0;
} }
@ -87,10 +94,10 @@ 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 = cred_cxt(old); const struct aa_task_ctx *old_ctx = cred_ctx(old);
struct aa_task_cxt *new_cxt = cred_cxt(new); struct aa_task_ctx *new_ctx = cred_ctx(new);
aa_dup_task_context(new_cxt, old_cxt); aa_dup_task_context(new_ctx, old_ctx);
} }
static int apparmor_ptrace_access_check(struct task_struct *child, static int apparmor_ptrace_access_check(struct task_struct *child,
@ -149,7 +156,7 @@ static int apparmor_capable(const struct cred *cred, struct user_namespace *ns,
* *
* Returns: %0 else error code if error or permission denied * Returns: %0 else error code if error or permission denied
*/ */
static int common_perm(int op, const struct path *path, u32 mask, static int common_perm(const char *op, const struct path *path, u32 mask,
struct path_cond *cond) struct path_cond *cond)
{ {
struct aa_profile *profile; struct aa_profile *profile;
@ -162,6 +169,26 @@ static int common_perm(int op, const struct path *path, u32 mask,
return error; return error;
} }
/**
* common_perm_cond - common permission wrapper around inode cond
* @op: operation being checked
* @path: location to check (NOT NULL)
* @mask: requested permissions mask
*
* Returns: %0 else error code if error or permission denied
*/
static int common_perm_cond(const char *op, const struct path *path, u32 mask)
{
struct path_cond cond = { d_backing_inode(path->dentry)->i_uid,
d_backing_inode(path->dentry)->i_mode
};
if (!path_mediated_fs(path->dentry))
return 0;
return common_perm(op, path, mask, &cond);
}
/** /**
* common_perm_dir_dentry - common permission wrapper when path is dir, dentry * common_perm_dir_dentry - common permission wrapper when path is dir, dentry
* @op: operation being checked * @op: operation being checked
@ -172,34 +199,15 @@ static int common_perm(int op, const struct path *path, u32 mask,
* *
* Returns: %0 else error code if error or permission denied * Returns: %0 else error code if error or permission denied
*/ */
static int common_perm_dir_dentry(int op, const struct path *dir, static int common_perm_dir_dentry(const char *op, const struct path *dir,
struct dentry *dentry, u32 mask, struct dentry *dentry, u32 mask,
struct path_cond *cond) struct path_cond *cond)
{ {
struct path path = { dir->mnt, dentry }; struct path path = { .mnt = dir->mnt, .dentry = dentry };
return common_perm(op, &path, mask, cond); return common_perm(op, &path, mask, cond);
} }
/**
* common_perm_path - common permission wrapper when mnt, dentry
* @op: operation being checked
* @path: location to check (NOT NULL)
* @mask: requested permissions mask
*
* Returns: %0 else error code if error or permission denied
*/
static inline int common_perm_path(int op, const struct path *path, u32 mask)
{
struct path_cond cond = { d_backing_inode(path->dentry)->i_uid,
d_backing_inode(path->dentry)->i_mode
};
if (!mediated_filesystem(path->dentry))
return 0;
return common_perm(op, path, mask, &cond);
}
/** /**
* common_perm_rm - common permission wrapper for operations doing rm * common_perm_rm - common permission wrapper for operations doing rm
* @op: operation being checked * @op: operation being checked
@ -209,13 +217,13 @@ static inline int common_perm_path(int op, const struct path *path, u32 mask)
* *
* Returns: %0 else error code if error or permission denied * Returns: %0 else error code if error or permission denied
*/ */
static int common_perm_rm(int op, const struct path *dir, static int common_perm_rm(const char *op, const struct path *dir,
struct dentry *dentry, u32 mask) struct dentry *dentry, u32 mask)
{ {
struct inode *inode = d_backing_inode(dentry); struct inode *inode = d_backing_inode(dentry);
struct path_cond cond = { }; struct path_cond cond = { };
if (!inode || !mediated_filesystem(dentry)) if (!inode || !path_mediated_fs(dentry))
return 0; return 0;
cond.uid = inode->i_uid; cond.uid = inode->i_uid;
@ -234,12 +242,12 @@ static int common_perm_rm(int op, const struct path *dir,
* *
* Returns: %0 else error code if error or permission denied * Returns: %0 else error code if error or permission denied
*/ */
static int common_perm_create(int op, const struct path *dir, static int common_perm_create(const char *op, const struct path *dir,
struct dentry *dentry, u32 mask, umode_t mode) struct dentry *dentry, u32 mask, umode_t mode)
{ {
struct path_cond cond = { current_fsuid(), mode }; struct path_cond cond = { current_fsuid(), mode };
if (!mediated_filesystem(dir->dentry)) if (!path_mediated_fs(dir->dentry))
return 0; return 0;
return common_perm_dir_dentry(op, dir, dentry, mask, &cond); return common_perm_dir_dentry(op, dir, dentry, mask, &cond);
@ -270,7 +278,7 @@ static int apparmor_path_mknod(const struct path *dir, struct dentry *dentry,
static int apparmor_path_truncate(const struct path *path) static int apparmor_path_truncate(const struct path *path)
{ {
return common_perm_path(OP_TRUNC, path, MAY_WRITE | AA_MAY_META_WRITE); return common_perm_cond(OP_TRUNC, path, MAY_WRITE | AA_MAY_META_WRITE);
} }
static int apparmor_path_symlink(const struct path *dir, struct dentry *dentry, static int apparmor_path_symlink(const struct path *dir, struct dentry *dentry,
@ -286,7 +294,7 @@ static int apparmor_path_link(struct dentry *old_dentry, const struct path *new_
struct aa_profile *profile; struct aa_profile *profile;
int error = 0; int error = 0;
if (!mediated_filesystem(old_dentry)) if (!path_mediated_fs(old_dentry))
return 0; return 0;
profile = aa_current_profile(); profile = aa_current_profile();
@ -301,13 +309,15 @@ static int apparmor_path_rename(const struct path *old_dir, struct dentry *old_d
struct aa_profile *profile; struct aa_profile *profile;
int error = 0; int error = 0;
if (!mediated_filesystem(old_dentry)) if (!path_mediated_fs(old_dentry))
return 0; return 0;
profile = aa_current_profile(); profile = aa_current_profile();
if (!unconfined(profile)) { if (!unconfined(profile)) {
struct path old_path = { old_dir->mnt, old_dentry }; struct path old_path = { .mnt = old_dir->mnt,
struct path new_path = { new_dir->mnt, new_dentry }; .dentry = old_dentry };
struct path new_path = { .mnt = new_dir->mnt,
.dentry = new_dentry };
struct path_cond cond = { d_backing_inode(old_dentry)->i_uid, struct path_cond cond = { d_backing_inode(old_dentry)->i_uid,
d_backing_inode(old_dentry)->i_mode d_backing_inode(old_dentry)->i_mode
}; };
@ -327,26 +337,26 @@ static int apparmor_path_rename(const struct path *old_dir, struct dentry *old_d
static int apparmor_path_chmod(const struct path *path, umode_t mode) static int apparmor_path_chmod(const struct path *path, umode_t mode)
{ {
return common_perm_path(OP_CHMOD, path, AA_MAY_CHMOD); return common_perm_cond(OP_CHMOD, path, AA_MAY_CHMOD);
} }
static int apparmor_path_chown(const struct path *path, kuid_t uid, kgid_t gid) static int apparmor_path_chown(const struct path *path, kuid_t uid, kgid_t gid)
{ {
return common_perm_path(OP_CHOWN, path, AA_MAY_CHOWN); return common_perm_cond(OP_CHOWN, path, AA_MAY_CHOWN);
} }
static int apparmor_inode_getattr(const struct path *path) static int apparmor_inode_getattr(const struct path *path)
{ {
return common_perm_path(OP_GETATTR, path, AA_MAY_META_READ); return common_perm_cond(OP_GETATTR, path, AA_MAY_META_READ);
} }
static int apparmor_file_open(struct file *file, const struct cred *cred) static int apparmor_file_open(struct file *file, const struct cred *cred)
{ {
struct aa_file_cxt *fcxt = file->f_security; struct aa_file_ctx *fctx = file->f_security;
struct aa_profile *profile; struct aa_profile *profile;
int error = 0; int error = 0;
if (!mediated_filesystem(file->f_path.dentry)) if (!path_mediated_fs(file->f_path.dentry))
return 0; return 0;
/* If in exec, permission is handled by bprm hooks. /* If in exec, permission is handled by bprm hooks.
@ -355,7 +365,7 @@ static int apparmor_file_open(struct file *file, const struct cred *cred)
* actually execute the image. * actually execute the image.
*/ */
if (current->in_execve) { if (current->in_execve) {
fcxt->allow = MAY_EXEC | MAY_READ | AA_EXEC_MMAP; fctx->allow = MAY_EXEC | MAY_READ | AA_EXEC_MMAP;
return 0; return 0;
} }
@ -367,7 +377,7 @@ static int apparmor_file_open(struct file *file, const struct cred *cred)
error = aa_path_perm(OP_OPEN, profile, &file->f_path, 0, error = aa_path_perm(OP_OPEN, profile, &file->f_path, 0,
aa_map_file_to_perms(file), &cond); aa_map_file_to_perms(file), &cond);
/* todo cache full allowed permissions set and state */ /* todo cache full allowed permissions set and state */
fcxt->allow = aa_map_file_to_perms(file); fctx->allow = aa_map_file_to_perms(file);
} }
return error; return error;
@ -385,21 +395,21 @@ static int apparmor_file_alloc_security(struct file *file)
static void apparmor_file_free_security(struct file *file) static void apparmor_file_free_security(struct file *file)
{ {
struct aa_file_cxt *cxt = file->f_security; struct aa_file_ctx *ctx = file->f_security;
aa_free_file_context(cxt); aa_free_file_context(ctx);
} }
static int common_file_perm(int op, struct file *file, u32 mask) static int common_file_perm(const char *op, struct file *file, u32 mask)
{ {
struct aa_file_cxt *fcxt = file->f_security; struct aa_file_ctx *fctx = file->f_security;
struct aa_profile *profile, *fprofile = aa_cred_profile(file->f_cred); struct aa_profile *profile, *fprofile = aa_cred_profile(file->f_cred);
int error = 0; int error = 0;
BUG_ON(!fprofile); AA_BUG(!fprofile);
if (!file->f_path.mnt || if (!file->f_path.mnt ||
!mediated_filesystem(file->f_path.dentry)) !path_mediated_fs(file->f_path.dentry))
return 0; return 0;
profile = __aa_current_profile(); profile = __aa_current_profile();
@ -412,7 +422,7 @@ static int common_file_perm(int op, struct file *file, u32 mask)
* delegation from unconfined tasks * delegation from unconfined tasks
*/ */
if (!unconfined(profile) && !unconfined(fprofile) && if (!unconfined(profile) && !unconfined(fprofile) &&
((fprofile != profile) || (mask & ~fcxt->allow))) ((fprofile != profile) || (mask & ~fctx->allow)))
error = aa_file_perm(op, profile, file, mask); error = aa_file_perm(op, profile, file, mask);
return error; return error;
@ -433,7 +443,7 @@ static int apparmor_file_lock(struct file *file, unsigned int cmd)
return common_file_perm(OP_FLOCK, file, mask); return common_file_perm(OP_FLOCK, file, mask);
} }
static int common_mmap(int op, struct file *file, unsigned long prot, static int common_mmap(const char *op, struct file *file, unsigned long prot,
unsigned long flags) unsigned long flags)
{ {
int mask = 0; int mask = 0;
@ -474,15 +484,15 @@ static int apparmor_getprocattr(struct task_struct *task, char *name,
int error = -ENOENT; int error = -ENOENT;
/* 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_ctx *ctx = cred_ctx(cred);
struct aa_profile *profile = NULL; struct aa_profile *profile = NULL;
if (strcmp(name, "current") == 0) if (strcmp(name, "current") == 0)
profile = aa_get_newest_profile(cxt->profile); profile = aa_get_newest_profile(ctx->profile);
else if (strcmp(name, "prev") == 0 && cxt->previous) else if (strcmp(name, "prev") == 0 && ctx->previous)
profile = aa_get_newest_profile(cxt->previous); profile = aa_get_newest_profile(ctx->previous);
else if (strcmp(name, "exec") == 0 && cxt->onexec) else if (strcmp(name, "exec") == 0 && ctx->onexec)
profile = aa_get_newest_profile(cxt->onexec); profile = aa_get_newest_profile(ctx->onexec);
else else
error = -EINVAL; error = -EINVAL;
@ -495,20 +505,16 @@ static int apparmor_getprocattr(struct task_struct *task, char *name,
return error; return error;
} }
static int apparmor_setprocattr(struct task_struct *task, char *name, static int apparmor_setprocattr(const char *name, void *value,
void *value, size_t size) size_t size)
{ {
struct common_audit_data sa;
struct apparmor_audit_data aad = {0,};
char *command, *largs = NULL, *args = value; char *command, *largs = NULL, *args = value;
size_t arg_size; size_t arg_size;
int error; int error;
DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_SETPROCATTR);
if (size == 0) if (size == 0)
return -EINVAL; return -EINVAL;
/* task can only write its own attributes */
if (current != task)
return -EACCES;
/* AppArmor requires that the buffer must be null terminated atm */ /* AppArmor requires that the buffer must be null terminated atm */
if (args[size - 1] != '\0') { if (args[size - 1] != '\0') {
@ -538,17 +544,17 @@ static int apparmor_setprocattr(struct task_struct *task, char *name,
error = aa_setprocattr_changehat(args, arg_size, error = aa_setprocattr_changehat(args, arg_size,
AA_DO_TEST); AA_DO_TEST);
} else if (strcmp(command, "changeprofile") == 0) { } else if (strcmp(command, "changeprofile") == 0) {
error = aa_setprocattr_changeprofile(args, !AA_ONEXEC, error = aa_change_profile(args, !AA_ONEXEC,
!AA_DO_TEST); !AA_DO_TEST, false);
} else if (strcmp(command, "permprofile") == 0) { } else if (strcmp(command, "permprofile") == 0) {
error = aa_setprocattr_changeprofile(args, !AA_ONEXEC, error = aa_change_profile(args, !AA_ONEXEC, AA_DO_TEST,
AA_DO_TEST); false);
} else } else
goto fail; goto fail;
} else if (strcmp(name, "exec") == 0) { } else if (strcmp(name, "exec") == 0) {
if (strcmp(command, "exec") == 0) if (strcmp(command, "exec") == 0)
error = aa_setprocattr_changeprofile(args, AA_ONEXEC, error = aa_change_profile(args, AA_ONEXEC, !AA_DO_TEST,
!AA_DO_TEST); false);
else else
goto fail; goto fail;
} else } else
@ -562,12 +568,9 @@ out:
return error; return error;
fail: fail:
sa.type = LSM_AUDIT_DATA_NONE; aad(&sa)->profile = aa_current_profile();
sa.aad = &aad; aad(&sa)->info = name;
aad.profile = aa_current_profile(); aad(&sa)->error = error = -EINVAL;
aad.op = OP_SETPROCATTR;
aad.info = name;
aad.error = error = -EINVAL;
aa_audit_msg(AUDIT_APPARMOR_DENIED, &sa, NULL); aa_audit_msg(AUDIT_APPARMOR_DENIED, &sa, NULL);
goto out; goto out;
} }
@ -671,14 +674,14 @@ enum profile_mode aa_g_profile_mode = APPARMOR_ENFORCE;
module_param_call(mode, param_set_mode, param_get_mode, module_param_call(mode, param_set_mode, param_get_mode,
&aa_g_profile_mode, S_IRUSR | S_IWUSR); &aa_g_profile_mode, S_IRUSR | S_IWUSR);
#ifdef CONFIG_SECURITY_APPARMOR_HASH
/* whether policy verification hashing is enabled */ /* whether policy verification hashing is enabled */
bool aa_g_hash_policy = IS_ENABLED(CONFIG_SECURITY_APPARMOR_HASH_DEFAULT); bool aa_g_hash_policy = IS_ENABLED(CONFIG_SECURITY_APPARMOR_HASH_DEFAULT);
#ifdef CONFIG_SECURITY_APPARMOR_HASH
module_param_named(hash_policy, aa_g_hash_policy, aabool, S_IRUSR | S_IWUSR); module_param_named(hash_policy, aa_g_hash_policy, aabool, S_IRUSR | S_IWUSR);
#endif #endif
/* Debug mode */ /* Debug mode */
bool aa_g_debug; bool aa_g_debug = IS_ENABLED(CONFIG_SECURITY_DEBUG_MESSAGES);
module_param_named(debug, aa_g_debug, aabool, S_IRUSR | S_IWUSR); module_param_named(debug, aa_g_debug, aabool, S_IRUSR | S_IWUSR);
/* Audit mode */ /* Audit mode */
@ -711,10 +714,11 @@ module_param_named(path_max, aa_g_path_max, aauint, S_IRUSR | S_IWUSR);
/* Determines how paranoid loading of policy is and how much verification /* Determines how paranoid loading of policy is and how much verification
* on the loaded policy is done. * on the loaded policy is done.
* DEPRECATED: read only as strict checking of load is always done now
* that none root users (user namespaces) can load policy.
*/ */
bool aa_g_paranoid_load = 1; bool aa_g_paranoid_load = 1;
module_param_named(paranoid_load, aa_g_paranoid_load, aabool, module_param_named(paranoid_load, aa_g_paranoid_load, aabool, S_IRUGO);
S_IRUSR | S_IWUSR);
/* 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;
@ -734,49 +738,59 @@ __setup("apparmor=", apparmor_enabled_setup);
/* set global flag turning off the ability to load policy */ /* set global flag turning off the ability to load policy */
static int param_set_aalockpolicy(const char *val, const struct kernel_param *kp) static int param_set_aalockpolicy(const char *val, const struct kernel_param *kp)
{ {
if (!policy_admin_capable()) if (!policy_admin_capable(NULL))
return -EPERM; return -EPERM;
return param_set_bool(val, kp); return param_set_bool(val, kp);
} }
static int param_get_aalockpolicy(char *buffer, const struct kernel_param *kp) static int param_get_aalockpolicy(char *buffer, const struct kernel_param *kp)
{ {
if (!policy_view_capable()) if (!policy_view_capable(NULL))
return -EPERM; return -EPERM;
if (!apparmor_enabled)
return -EINVAL;
return param_get_bool(buffer, kp); return param_get_bool(buffer, kp);
} }
static int param_set_aabool(const char *val, const struct kernel_param *kp) static int param_set_aabool(const char *val, const struct kernel_param *kp)
{ {
if (!policy_admin_capable()) if (!policy_admin_capable(NULL))
return -EPERM; return -EPERM;
if (!apparmor_enabled)
return -EINVAL;
return param_set_bool(val, kp); return param_set_bool(val, kp);
} }
static int param_get_aabool(char *buffer, const struct kernel_param *kp) static int param_get_aabool(char *buffer, const struct kernel_param *kp)
{ {
if (!policy_view_capable()) if (!policy_view_capable(NULL))
return -EPERM; return -EPERM;
if (!apparmor_enabled)
return -EINVAL;
return param_get_bool(buffer, kp); return param_get_bool(buffer, kp);
} }
static int param_set_aauint(const char *val, const struct kernel_param *kp) static int param_set_aauint(const char *val, const struct kernel_param *kp)
{ {
if (!policy_admin_capable()) if (!policy_admin_capable(NULL))
return -EPERM; return -EPERM;
if (!apparmor_enabled)
return -EINVAL;
return param_set_uint(val, kp); return param_set_uint(val, kp);
} }
static int param_get_aauint(char *buffer, const struct kernel_param *kp) static int param_get_aauint(char *buffer, const struct kernel_param *kp)
{ {
if (!policy_view_capable()) if (!policy_view_capable(NULL))
return -EPERM; return -EPERM;
if (!apparmor_enabled)
return -EINVAL;
return param_get_uint(buffer, kp); return param_get_uint(buffer, kp);
} }
static int param_get_audit(char *buffer, struct kernel_param *kp) static int param_get_audit(char *buffer, struct kernel_param *kp)
{ {
if (!policy_view_capable()) if (!policy_view_capable(NULL))
return -EPERM; return -EPERM;
if (!apparmor_enabled) if (!apparmor_enabled)
@ -788,7 +802,7 @@ static int param_get_audit(char *buffer, struct kernel_param *kp)
static int param_set_audit(const char *val, struct kernel_param *kp) static int param_set_audit(const char *val, struct kernel_param *kp)
{ {
int i; int i;
if (!policy_admin_capable()) if (!policy_admin_capable(NULL))
return -EPERM; return -EPERM;
if (!apparmor_enabled) if (!apparmor_enabled)
@ -809,7 +823,7 @@ static int param_set_audit(const char *val, struct kernel_param *kp)
static int param_get_mode(char *buffer, struct kernel_param *kp) static int param_get_mode(char *buffer, struct kernel_param *kp)
{ {
if (!policy_admin_capable()) if (!policy_view_capable(NULL))
return -EPERM; return -EPERM;
if (!apparmor_enabled) if (!apparmor_enabled)
@ -821,7 +835,7 @@ static int param_get_mode(char *buffer, struct kernel_param *kp)
static int param_set_mode(const char *val, struct kernel_param *kp) static int param_set_mode(const char *val, struct kernel_param *kp)
{ {
int i; int i;
if (!policy_admin_capable()) if (!policy_admin_capable(NULL))
return -EPERM; return -EPERM;
if (!apparmor_enabled) if (!apparmor_enabled)
@ -845,25 +859,102 @@ static int param_set_mode(const char *val, struct kernel_param *kp)
*/ */
/** /**
* set_init_cxt - set a task context and profile on the first task. * set_init_ctx - set a task context and profile on the first task.
* *
* TODO: allow setting an alternate profile than unconfined * TODO: allow setting an alternate profile than unconfined
*/ */
static int __init set_init_cxt(void) static int __init set_init_ctx(void)
{ {
struct cred *cred = (struct cred *)current->real_cred; struct cred *cred = (struct cred *)current->real_cred;
struct aa_task_cxt *cxt; struct aa_task_ctx *ctx;
cxt = aa_alloc_task_context(GFP_KERNEL); ctx = aa_alloc_task_context(GFP_KERNEL);
if (!cxt) if (!ctx)
return -ENOMEM; return -ENOMEM;
cxt->profile = aa_get_profile(root_ns->unconfined); ctx->profile = aa_get_profile(root_ns->unconfined);
cred_cxt(cred) = cxt; cred_ctx(cred) = ctx;
return 0; return 0;
} }
static void destroy_buffers(void)
{
u32 i, j;
for_each_possible_cpu(i) {
for_each_cpu_buffer(j) {
kfree(per_cpu(aa_buffers, i).buf[j]);
per_cpu(aa_buffers, i).buf[j] = NULL;
}
}
}
static int __init alloc_buffers(void)
{
u32 i, j;
for_each_possible_cpu(i) {
for_each_cpu_buffer(j) {
char *buffer;
if (cpu_to_node(i) > num_online_nodes())
/* fallback to kmalloc for offline nodes */
buffer = kmalloc(aa_g_path_max, GFP_KERNEL);
else
buffer = kmalloc_node(aa_g_path_max, GFP_KERNEL,
cpu_to_node(i));
if (!buffer) {
destroy_buffers();
return -ENOMEM;
}
per_cpu(aa_buffers, i).buf[j] = buffer;
}
}
return 0;
}
#ifdef CONFIG_SYSCTL
static int apparmor_dointvec(struct ctl_table *table, int write,
void __user *buffer, size_t *lenp, loff_t *ppos)
{
if (!policy_admin_capable(NULL))
return -EPERM;
if (!apparmor_enabled)
return -EINVAL;
return proc_dointvec(table, write, buffer, lenp, ppos);
}
static struct ctl_path apparmor_sysctl_path[] = {
{ .procname = "kernel", },
{ }
};
static struct ctl_table apparmor_sysctl_table[] = {
{
.procname = "unprivileged_userns_apparmor_policy",
.data = &unprivileged_userns_apparmor_policy,
.maxlen = sizeof(int),
.mode = 0600,
.proc_handler = apparmor_dointvec,
},
{ }
};
static int __init apparmor_init_sysctl(void)
{
return register_sysctl_paths(apparmor_sysctl_path,
apparmor_sysctl_table) ? 0 : -ENOMEM;
}
#else
static inline int apparmor_init_sysctl(void)
{
return 0;
}
#endif /* CONFIG_SYSCTL */
static int __init apparmor_init(void) static int __init apparmor_init(void)
{ {
int error; int error;
@ -874,19 +965,39 @@ static int __init apparmor_init(void)
return 0; return 0;
} }
error = aa_setup_dfa_engine();
if (error) {
AA_ERROR("Unable to setup dfa engine\n");
goto alloc_out;
}
error = aa_alloc_root_ns(); error = aa_alloc_root_ns();
if (error) { if (error) {
AA_ERROR("Unable to allocate default profile namespace\n"); AA_ERROR("Unable to allocate default profile namespace\n");
goto alloc_out; goto alloc_out;
} }
error = set_init_cxt(); error = apparmor_init_sysctl();
if (error) {
AA_ERROR("Unable to register sysctls\n");
goto alloc_out;
}
error = alloc_buffers();
if (error) {
AA_ERROR("Unable to allocate work buffers\n");
goto buffers_out;
}
error = set_init_ctx();
if (error) { if (error) {
AA_ERROR("Failed to set context on init task\n"); AA_ERROR("Failed to set context on init task\n");
aa_free_root_ns(); aa_free_root_ns();
goto alloc_out; goto buffers_out;
} }
security_add_hooks(apparmor_hooks, ARRAY_SIZE(apparmor_hooks)); security_add_hooks(apparmor_hooks, ARRAY_SIZE(apparmor_hooks),
"apparmor");
/* Report that AppArmor successfully initialized */ /* Report that AppArmor successfully initialized */
apparmor_initialized = 1; apparmor_initialized = 1;
@ -899,8 +1010,12 @@ static int __init apparmor_init(void)
return error; return error;
buffers_out:
destroy_buffers();
alloc_out: alloc_out:
aa_destroy_aafs(); aa_destroy_aafs();
aa_teardown_dfa_engine();
apparmor_enabled = 0; apparmor_enabled = 0;
return error; return error;

View File

@ -20,11 +20,38 @@
#include <linux/err.h> #include <linux/err.h>
#include <linux/kref.h> #include <linux/kref.h>
#include "include/apparmor.h" #include "include/lib.h"
#include "include/match.h" #include "include/match.h"
#define base_idx(X) ((X) & 0xffffff) #define base_idx(X) ((X) & 0xffffff)
static char nulldfa_src[] = {
#include "nulldfa.in"
};
struct aa_dfa *nulldfa;
int aa_setup_dfa_engine(void)
{
int error;
nulldfa = aa_dfa_unpack(nulldfa_src, sizeof(nulldfa_src),
TO_ACCEPT1_FLAG(YYTD_DATA32) |
TO_ACCEPT2_FLAG(YYTD_DATA32));
if (!IS_ERR(nulldfa))
return 0;
error = PTR_ERR(nulldfa);
nulldfa = NULL;
return error;
}
void aa_teardown_dfa_engine(void)
{
aa_put_dfa(nulldfa);
nulldfa = NULL;
}
/** /**
* 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)
@ -46,11 +73,11 @@ static struct table_header *unpack_table(char *blob, size_t bsize)
/* loaded td_id's start at 1, subtract 1 now to avoid doing /* loaded td_id's start at 1, subtract 1 now to avoid doing
* it every time we use td_id as an index * it every time we use td_id as an index
*/ */
th.td_id = be16_to_cpu(*(u16 *) (blob)) - 1; th.td_id = be16_to_cpu(*(__be16 *) (blob)) - 1;
if (th.td_id > YYTD_ID_MAX) if (th.td_id > YYTD_ID_MAX)
goto out; goto out;
th.td_flags = be16_to_cpu(*(u16 *) (blob + 2)); th.td_flags = be16_to_cpu(*(__be16 *) (blob + 2));
th.td_lolen = be32_to_cpu(*(u32 *) (blob + 8)); th.td_lolen = be32_to_cpu(*(__be32 *) (blob + 8));
blob += sizeof(struct table_header); blob += sizeof(struct table_header);
if (!(th.td_flags == YYTD_DATA16 || th.td_flags == YYTD_DATA32 || if (!(th.td_flags == YYTD_DATA16 || th.td_flags == YYTD_DATA32 ||
@ -68,13 +95,13 @@ static struct table_header *unpack_table(char *blob, size_t bsize)
table->td_lolen = th.td_lolen; table->td_lolen = th.td_lolen;
if (th.td_flags == YYTD_DATA8) if (th.td_flags == YYTD_DATA8)
UNPACK_ARRAY(table->td_data, blob, th.td_lolen, UNPACK_ARRAY(table->td_data, blob, th.td_lolen,
u8, byte_to_byte); u8, u8, byte_to_byte);
else if (th.td_flags == YYTD_DATA16) else if (th.td_flags == YYTD_DATA16)
UNPACK_ARRAY(table->td_data, blob, th.td_lolen, UNPACK_ARRAY(table->td_data, blob, th.td_lolen,
u16, be16_to_cpu); u16, __be16, be16_to_cpu);
else if (th.td_flags == YYTD_DATA32) else if (th.td_flags == YYTD_DATA32)
UNPACK_ARRAY(table->td_data, blob, th.td_lolen, UNPACK_ARRAY(table->td_data, blob, th.td_lolen,
u32, be32_to_cpu); u32, __be32, be32_to_cpu);
else else
goto fail; goto fail;
/* if table was vmalloced make sure the page tables are synced /* if table was vmalloced make sure the page tables are synced
@ -222,14 +249,14 @@ struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags)
if (size < sizeof(struct table_set_header)) if (size < sizeof(struct table_set_header))
goto fail; goto fail;
if (ntohl(*(u32 *) data) != YYTH_MAGIC) if (ntohl(*(__be32 *) data) != YYTH_MAGIC)
goto fail; goto fail;
hsize = ntohl(*(u32 *) (data + 4)); hsize = ntohl(*(__be32 *) (data + 4));
if (size < hsize) if (size < hsize)
goto fail; goto fail;
dfa->flags = ntohs(*(u16 *) (data + 12)); dfa->flags = ntohs(*(__be16 *) (data + 12));
data += hsize; data += hsize;
size -= hsize; size -= hsize;

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,346 @@
/*
* AppArmor security module
*
* This file contains AppArmor policy manipulation functions
*
* Copyright (C) 1998-2008 Novell/SUSE
* Copyright 2009-2017 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.
*
* AppArmor policy namespaces, allow for different sets of policies
* to be loaded for tasks within the namespace.
*/
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/string.h>
#include "include/apparmor.h"
#include "include/context.h"
#include "include/policy_ns.h"
#include "include/policy.h"
/* root profile namespace */
struct aa_ns *root_ns;
const char *aa_hidden_ns_name = "---";
/**
* aa_ns_visible - test if @view is visible from @curr
* @curr: namespace to treat as the parent (NOT NULL)
* @view: namespace to test if visible from @curr (NOT NULL)
* @subns: whether view of a subns is allowed
*
* Returns: true if @view is visible from @curr else false
*/
bool aa_ns_visible(struct aa_ns *curr, struct aa_ns *view, bool subns)
{
if (curr == view)
return true;
if (!subns)
return false;
for ( ; view; view = view->parent) {
if (view->parent == curr)
return true;
}
return false;
}
/**
* aa_na_name - Find the ns name to display for @view from @curr
* @curr - current namespace (NOT NULL)
* @view - namespace attempting to view (NOT NULL)
* @subns - are subns visible
*
* Returns: name of @view visible from @curr
*/
const char *aa_ns_name(struct aa_ns *curr, struct aa_ns *view, bool subns)
{
/* if view == curr then the namespace name isn't displayed */
if (curr == view)
return "";
if (aa_ns_visible(curr, view, subns)) {
/* at this point if a ns is visible it is in a view ns
* thus the curr ns.hname is a prefix of its name.
* Only output the virtualized portion of the name
* Add + 2 to skip over // separating curr hname prefix
* from the visible tail of the views hname
*/
return view->base.hname + strlen(curr->base.hname) + 2;
}
return aa_hidden_ns_name;
}
/**
* alloc_ns - allocate, initialize and return a new namespace
* @prefix: parent namespace name (MAYBE NULL)
* @name: a preallocated name (NOT NULL)
*
* Returns: refcounted namespace or NULL on failure.
*/
static struct aa_ns *alloc_ns(const char *prefix, const char *name)
{
struct aa_ns *ns;
ns = kzalloc(sizeof(*ns), GFP_KERNEL);
AA_DEBUG("%s(%p)\n", __func__, ns);
if (!ns)
return NULL;
if (!aa_policy_init(&ns->base, prefix, name, GFP_KERNEL))
goto fail_ns;
INIT_LIST_HEAD(&ns->sub_ns);
mutex_init(&ns->lock);
/* released by aa_free_ns() */
ns->unconfined = aa_alloc_profile("unconfined", GFP_KERNEL);
if (!ns->unconfined)
goto fail_unconfined;
ns->unconfined->flags = PFLAG_IX_ON_NAME_ERROR |
PFLAG_IMMUTABLE | PFLAG_NS_COUNT;
ns->unconfined->mode = APPARMOR_UNCONFINED;
/* ns and ns->unconfined share ns->unconfined refcount */
ns->unconfined->ns = ns;
atomic_set(&ns->uniq_null, 0);
return ns;
fail_unconfined:
kzfree(ns->base.hname);
fail_ns:
kzfree(ns);
return NULL;
}
/**
* aa_free_ns - free a profile namespace
* @ns: the namespace to free (MAYBE NULL)
*
* Requires: All references to the namespace must have been put, if the
* namespace was referenced by a profile confining a task,
*/
void aa_free_ns(struct aa_ns *ns)
{
if (!ns)
return;
aa_policy_destroy(&ns->base);
aa_put_ns(ns->parent);
ns->unconfined->ns = NULL;
aa_free_profile(ns->unconfined);
kzfree(ns);
}
/**
* aa_findn_ns - look up a profile namespace on the namespace list
* @root: namespace to search in (NOT NULL)
* @name: name of namespace to find (NOT NULL)
* @n: length of @name
*
* Returns: a refcounted namespace on the list, or NULL if no namespace
* called @name exists.
*
* refcount released by caller
*/
struct aa_ns *aa_findn_ns(struct aa_ns *root, const char *name, size_t n)
{
struct aa_ns *ns = NULL;
rcu_read_lock();
ns = aa_get_ns(__aa_findn_ns(&root->sub_ns, name, n));
rcu_read_unlock();
return ns;
}
/**
* aa_find_ns - look up a profile namespace on the namespace list
* @root: namespace to search in (NOT NULL)
* @name: name of namespace to find (NOT NULL)
*
* Returns: a refcounted namespace on the list, or NULL if no namespace
* called @name exists.
*
* refcount released by caller
*/
struct aa_ns *aa_find_ns(struct aa_ns *root, const char *name)
{
return aa_findn_ns(root, name, strlen(name));
}
static struct aa_ns *__aa_create_ns(struct aa_ns *parent, const char *name,
struct dentry *dir)
{
struct aa_ns *ns;
int error;
AA_BUG(!parent);
AA_BUG(!name);
AA_BUG(!mutex_is_locked(&parent->lock));
ns = alloc_ns(parent->base.hname, name);
if (!ns)
return NULL;
mutex_lock(&ns->lock);
error = __aa_fs_ns_mkdir(ns, ns_subns_dir(parent), name);
if (error) {
AA_ERROR("Failed to create interface for ns %s\n",
ns->base.name);
mutex_unlock(&ns->lock);
aa_free_ns(ns);
return ERR_PTR(error);
}
ns->parent = aa_get_ns(parent);
ns->level = parent->level + 1;
list_add_rcu(&ns->base.list, &parent->sub_ns);
/* add list ref */
aa_get_ns(ns);
mutex_unlock(&ns->lock);
return ns;
}
/**
* aa_create_ns - create an ns, fail if it already exists
* @parent: the parent of the namespace being created
* @name: the name of the namespace
* @dir: if not null the dir to put the ns entries in
*
* Returns: the a refcounted ns that has been add or an ERR_PTR
*/
struct aa_ns *__aa_find_or_create_ns(struct aa_ns *parent, const char *name,
struct dentry *dir)
{
struct aa_ns *ns;
AA_BUG(!mutex_is_locked(&parent->lock));
/* try and find the specified ns */
/* released by caller */
ns = aa_get_ns(__aa_find_ns(&parent->sub_ns, name));
if (!ns)
ns = __aa_create_ns(parent, name, dir);
else
ns = ERR_PTR(-EEXIST);
/* return ref */
return ns;
}
/**
* aa_prepare_ns - find an existing or create a new namespace of @name
* @parent: ns to treat as parent
* @name: the namespace to find or add (NOT NULL)
*
* Returns: refcounted namespace or PTR_ERR if failed to create one
*/
struct aa_ns *aa_prepare_ns(struct aa_ns *parent, const char *name)
{
struct aa_ns *ns;
mutex_lock(&parent->lock);
/* try and find the specified ns and if it doesn't exist create it */
/* released by caller */
ns = aa_get_ns(__aa_find_ns(&parent->sub_ns, name));
if (!ns)
ns = __aa_create_ns(parent, name, NULL);
mutex_unlock(&parent->lock);
/* return ref */
return ns;
}
static void __ns_list_release(struct list_head *head);
/**
* destroy_ns - remove everything contained by @ns
* @ns: namespace to have it contents removed (NOT NULL)
*/
static void destroy_ns(struct aa_ns *ns)
{
if (!ns)
return;
mutex_lock(&ns->lock);
/* release all profiles in this namespace */
__aa_profile_list_release(&ns->base.profiles);
/* release all sub namespaces */
__ns_list_release(&ns->sub_ns);
if (ns->parent)
__aa_update_proxy(ns->unconfined, ns->parent->unconfined);
__aa_fs_ns_rmdir(ns);
mutex_unlock(&ns->lock);
}
/**
* __aa_remove_ns - remove a namespace and all its children
* @ns: namespace to be removed (NOT NULL)
*
* Requires: ns->parent->lock be held and ns removed from parent.
*/
void __aa_remove_ns(struct aa_ns *ns)
{
/* remove ns from namespace list */
list_del_rcu(&ns->base.list);
destroy_ns(ns);
aa_put_ns(ns);
}
/**
* __ns_list_release - remove all profile namespaces on the list put refs
* @head: list of profile namespaces (NOT NULL)
*
* Requires: namespace lock be held
*/
static void __ns_list_release(struct list_head *head)
{
struct aa_ns *ns, *tmp;
list_for_each_entry_safe(ns, tmp, head, base.list)
__aa_remove_ns(ns);
}
/**
* aa_alloc_root_ns - allocate the root profile namespace
*
* Returns: %0 on success else error
*
*/
int __init aa_alloc_root_ns(void)
{
/* released by aa_free_root_ns - used as list ref*/
root_ns = alloc_ns(NULL, "root");
if (!root_ns)
return -ENOMEM;
return 0;
}
/**
* aa_free_root_ns - free the root profile namespace
*/
void __init aa_free_root_ns(void)
{
struct aa_ns *ns = root_ns;
root_ns = NULL;
destroy_ns(ns);
aa_put_ns(ns);
}

View File

@ -29,6 +29,15 @@
#include "include/policy.h" #include "include/policy.h"
#include "include/policy_unpack.h" #include "include/policy_unpack.h"
#define K_ABI_MASK 0x3ff
#define FORCE_COMPLAIN_FLAG 0x800
#define VERSION_LT(X, Y) (((X) & K_ABI_MASK) < ((Y) & K_ABI_MASK))
#define VERSION_GT(X, Y) (((X) & K_ABI_MASK) > ((Y) & K_ABI_MASK))
#define v5 5 /* base version */
#define v6 6 /* per entry policydb mediation check */
#define v7 7 /* full network masking */
/* /*
* The AppArmor interface treats data as a type byte followed by the * The AppArmor interface treats data as a type byte followed by the
* actual data. The interface has the notion of a a named entry * actual data. The interface has the notion of a a named entry
@ -70,18 +79,23 @@ struct aa_ext {
static void audit_cb(struct audit_buffer *ab, void *va) static void audit_cb(struct audit_buffer *ab, void *va)
{ {
struct common_audit_data *sa = va; struct common_audit_data *sa = va;
if (sa->aad->iface.target) {
struct aa_profile *name = sa->aad->iface.target; if (aad(sa)->iface.ns) {
audit_log_format(ab, " name="); audit_log_format(ab, " ns=");
audit_log_untrustedstring(ab, name->base.hname); audit_log_untrustedstring(ab, aad(sa)->iface.ns);
} }
if (sa->aad->iface.pos) if (aad(sa)->iface.name) {
audit_log_format(ab, " offset=%ld", sa->aad->iface.pos); audit_log_format(ab, " name=");
audit_log_untrustedstring(ab, aad(sa)->iface.name);
}
if (aad(sa)->iface.pos)
audit_log_format(ab, " offset=%ld", aad(sa)->iface.pos);
} }
/** /**
* audit_iface - do audit message for policy unpacking/load/replace/remove * audit_iface - do audit message for policy unpacking/load/replace/remove
* @new: profile if it has been allocated (MAYBE NULL) * @new: profile if it has been allocated (MAYBE NULL)
* @ns_name: name of the ns the profile is to be loaded to (MAY BE NULL)
* @name: name of the profile being manipulated (MAYBE NULL) * @name: name of the profile being manipulated (MAYBE NULL)
* @info: any extra info about the failure (MAYBE NULL) * @info: any extra info about the failure (MAYBE NULL)
* @e: buffer position info * @e: buffer position info
@ -89,23 +103,33 @@ static void audit_cb(struct audit_buffer *ab, void *va)
* *
* Returns: %0 or error * Returns: %0 or error
*/ */
static int audit_iface(struct aa_profile *new, const char *name, static int audit_iface(struct aa_profile *new, const char *ns_name,
const char *info, struct aa_ext *e, int error) const char *name, const char *info, struct aa_ext *e,
int error)
{ {
struct aa_profile *profile = __aa_current_profile(); struct aa_profile *profile = __aa_current_profile();
struct common_audit_data sa; DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, NULL);
struct apparmor_audit_data aad = {0,};
sa.type = LSM_AUDIT_DATA_NONE;
sa.aad = &aad;
if (e) if (e)
aad.iface.pos = e->pos - e->start; aad(&sa)->iface.pos = e->pos - e->start;
aad.iface.target = new; aad(&sa)->iface.ns = ns_name;
aad.name = name; if (new)
aad.info = info; aad(&sa)->iface.name = new->base.hname;
aad.error = error; else
aad(&sa)->iface.name = name;
aad(&sa)->info = info;
aad(&sa)->error = error;
return aa_audit(AUDIT_APPARMOR_STATUS, profile, GFP_KERNEL, &sa, return aa_audit(AUDIT_APPARMOR_STATUS, profile, &sa, audit_cb);
audit_cb); }
void aa_loaddata_kref(struct kref *kref)
{
struct aa_loaddata *d = container_of(kref, struct aa_loaddata, count);
if (d) {
kzfree(d->hash);
kvfree(d);
}
} }
/* test if read will be in packed data bounds */ /* test if read will be in packed data bounds */
@ -127,8 +151,8 @@ static size_t unpack_u16_chunk(struct aa_ext *e, char **chunk)
if (!inbounds(e, sizeof(u16))) if (!inbounds(e, sizeof(u16)))
return 0; return 0;
size = le16_to_cpu(get_unaligned((u16 *) e->pos)); size = le16_to_cpu(get_unaligned((__le16 *) e->pos));
e->pos += sizeof(u16); e->pos += sizeof(__le16);
if (!inbounds(e, size)) if (!inbounds(e, size))
return 0; return 0;
*chunk = e->pos; *chunk = e->pos;
@ -199,7 +223,7 @@ static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name)
if (!inbounds(e, sizeof(u32))) if (!inbounds(e, sizeof(u32)))
return 0; return 0;
if (data) if (data)
*data = le32_to_cpu(get_unaligned((u32 *) e->pos)); *data = le32_to_cpu(get_unaligned((__le32 *) e->pos));
e->pos += sizeof(u32); e->pos += sizeof(u32);
return 1; return 1;
} }
@ -212,7 +236,7 @@ static bool unpack_u64(struct aa_ext *e, u64 *data, const char *name)
if (!inbounds(e, sizeof(u64))) if (!inbounds(e, sizeof(u64)))
return 0; return 0;
if (data) if (data)
*data = le64_to_cpu(get_unaligned((u64 *) e->pos)); *data = le64_to_cpu(get_unaligned((__le64 *) e->pos));
e->pos += sizeof(u64); e->pos += sizeof(u64);
return 1; return 1;
} }
@ -225,7 +249,7 @@ static size_t unpack_array(struct aa_ext *e, const char *name)
int size; int size;
if (!inbounds(e, sizeof(u16))) if (!inbounds(e, sizeof(u16)))
return 0; return 0;
size = (int)le16_to_cpu(get_unaligned((u16 *) e->pos)); size = (int)le16_to_cpu(get_unaligned((__le16 *) e->pos));
e->pos += sizeof(u16); e->pos += sizeof(u16);
return size; return size;
} }
@ -238,7 +262,7 @@ static size_t unpack_blob(struct aa_ext *e, char **blob, const char *name)
u32 size; u32 size;
if (!inbounds(e, sizeof(u32))) if (!inbounds(e, sizeof(u32)))
return 0; return 0;
size = le32_to_cpu(get_unaligned((u32 *) e->pos)); size = le32_to_cpu(get_unaligned((__le32 *) e->pos));
e->pos += sizeof(u32); e->pos += sizeof(u32);
if (inbounds(e, (size_t) size)) { if (inbounds(e, (size_t) size)) {
*blob = e->pos; *blob = e->pos;
@ -340,12 +364,7 @@ static struct aa_dfa *unpack_dfa(struct aa_ext *e)
((e->pos - e->start) & 7); ((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) | DFA_FLAG_VERIFY_STATES;
if (aa_g_paranoid_load)
flags |= DFA_FLAG_VERIFY_STATES;
dfa = aa_dfa_unpack(blob + pad, size - pad, flags); dfa = aa_dfa_unpack(blob + pad, size - pad, flags);
if (IS_ERR(dfa)) if (IS_ERR(dfa))
@ -466,27 +485,67 @@ fail:
return 0; return 0;
} }
static void *kvmemdup(const void *src, size_t len)
{
void *p = kvmalloc(len);
if (p)
memcpy(p, src, len);
return p;
}
static u32 strhash(const void *data, u32 len, u32 seed)
{
const char * const *key = data;
return jhash(*key, strlen(*key), seed);
}
static int datacmp(struct rhashtable_compare_arg *arg, const void *obj)
{
const struct aa_data *data = obj;
const char * const *key = arg->key;
return strcmp(data->key, *key);
}
/** /**
* unpack_profile - unpack a serialized profile * unpack_profile - unpack a serialized profile
* @e: serialized data extent information (NOT NULL) * @e: serialized data extent information (NOT NULL)
* *
* NOTE: unpack profile sets audit struct if there is a failure * NOTE: unpack profile sets audit struct if there is a failure
*/ */
static struct aa_profile *unpack_profile(struct aa_ext *e) static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
{ {
struct aa_profile *profile = NULL; struct aa_profile *profile = NULL;
const char *name = NULL; const char *tmpname, *tmpns = NULL, *name = NULL;
size_t ns_len;
struct rhashtable_params params = { 0 };
char *key = NULL;
struct aa_data *data;
int i, error = -EPROTO; int i, error = -EPROTO;
kernel_cap_t tmpcap; kernel_cap_t tmpcap;
u32 tmp; u32 tmp;
*ns_name = NULL;
/* check that we have the right struct being passed */ /* check that we have the right struct being passed */
if (!unpack_nameX(e, AA_STRUCT, "profile")) if (!unpack_nameX(e, AA_STRUCT, "profile"))
goto fail; goto fail;
if (!unpack_str(e, &name, NULL)) if (!unpack_str(e, &name, NULL))
goto fail; goto fail;
if (*name == '\0')
goto fail;
profile = aa_alloc_profile(name); tmpname = aa_splitn_fqname(name, strlen(name), &tmpns, &ns_len);
if (tmpns) {
*ns_name = kstrndup(tmpns, ns_len, GFP_KERNEL);
if (!*ns_name)
goto fail;
name = tmpname;
}
profile = aa_alloc_profile(name, GFP_KERNEL);
if (!profile) if (!profile)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
@ -519,7 +578,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e)
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 == PACKED_MODE_COMPLAIN) if (tmp == PACKED_MODE_COMPLAIN || (e->version & FORCE_COMPLAIN_FLAG))
profile->mode = APPARMOR_COMPLAIN; profile->mode = APPARMOR_COMPLAIN;
else if (tmp == PACKED_MODE_KILL) else if (tmp == PACKED_MODE_KILL)
profile->mode = APPARMOR_KILL; profile->mode = APPARMOR_KILL;
@ -599,7 +658,8 @@ static struct aa_profile *unpack_profile(struct aa_ext *e)
} }
if (!unpack_nameX(e, AA_STRUCTEND, NULL)) if (!unpack_nameX(e, AA_STRUCTEND, NULL))
goto fail; goto fail;
} } else
profile->policy.dfa = aa_get_dfa(nulldfa);
/* get file rules */ /* get file rules */
profile->file.dfa = unpack_dfa(e); profile->file.dfa = unpack_dfa(e);
@ -607,15 +667,59 @@ static struct aa_profile *unpack_profile(struct aa_ext *e)
error = PTR_ERR(profile->file.dfa); error = PTR_ERR(profile->file.dfa);
profile->file.dfa = NULL; profile->file.dfa = NULL;
goto fail; goto fail;
} } else if (profile->file.dfa) {
if (!unpack_u32(e, &profile->file.start, "dfa_start"))
if (!unpack_u32(e, &profile->file.start, "dfa_start")) /* default start state */
/* default start state */ profile->file.start = DFA_START;
profile->file.start = DFA_START; } else if (profile->policy.dfa &&
profile->policy.start[AA_CLASS_FILE]) {
profile->file.dfa = aa_get_dfa(profile->policy.dfa);
profile->file.start = profile->policy.start[AA_CLASS_FILE];
} else
profile->file.dfa = aa_get_dfa(nulldfa);
if (!unpack_trans_table(e, profile)) if (!unpack_trans_table(e, profile))
goto fail; goto fail;
if (unpack_nameX(e, AA_STRUCT, "data")) {
profile->data = kzalloc(sizeof(*profile->data), GFP_KERNEL);
if (!profile->data)
goto fail;
params.nelem_hint = 3;
params.key_len = sizeof(void *);
params.key_offset = offsetof(struct aa_data, key);
params.head_offset = offsetof(struct aa_data, head);
params.hashfn = strhash;
params.obj_cmpfn = datacmp;
if (rhashtable_init(profile->data, &params))
goto fail;
while (unpack_strdup(e, &key, NULL)) {
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data) {
kzfree(key);
goto fail;
}
data->key = key;
data->size = unpack_blob(e, &data->data, NULL);
data->data = kvmemdup(data->data, data->size);
if (data->size && !data->data) {
kzfree(data->key);
kzfree(data);
goto fail;
}
rhashtable_insert_fast(profile->data, &data->head,
profile->data->p);
}
if (!unpack_nameX(e, AA_STRUCTEND, NULL))
goto fail;
}
if (!unpack_nameX(e, AA_STRUCTEND, NULL)) if (!unpack_nameX(e, AA_STRUCTEND, NULL))
goto fail; goto fail;
@ -626,7 +730,8 @@ fail:
name = NULL; name = NULL;
else if (!name) else if (!name)
name = "unknown"; name = "unknown";
audit_iface(profile, name, "failed to unpack profile", e, error); audit_iface(profile, NULL, name, "failed to unpack profile", e,
error);
aa_free_profile(profile); aa_free_profile(profile);
return ERR_PTR(error); return ERR_PTR(error);
@ -649,24 +754,32 @@ static int verify_header(struct aa_ext *e, int required, const char **ns)
/* get the interface version */ /* get the interface version */
if (!unpack_u32(e, &e->version, "version")) { if (!unpack_u32(e, &e->version, "version")) {
if (required) { if (required) {
audit_iface(NULL, NULL, "invalid profile format", e, audit_iface(NULL, NULL, NULL, "invalid profile format",
error);
return error;
}
/* check that the interface version is currently supported */
if (e->version != 5) {
audit_iface(NULL, NULL, "unsupported interface version",
e, error); e, error);
return error; return error;
} }
} }
/* Check that the interface version is currently supported.
* if not specified use previous version
* Mask off everything that is not kernel abi version
*/
if (VERSION_LT(e->version, v5) && VERSION_GT(e->version, v7)) {
audit_iface(NULL, NULL, NULL, "unsupported interface version",
e, error);
return error;
}
/* read the namespace if present */ /* read the namespace if present */
if (unpack_str(e, &name, "namespace")) { if (unpack_str(e, &name, "namespace")) {
if (*name == '\0') {
audit_iface(NULL, NULL, NULL, "invalid namespace name",
e, error);
return error;
}
if (*ns && strcmp(*ns, name)) if (*ns && strcmp(*ns, name))
audit_iface(NULL, NULL, "invalid ns change", e, error); audit_iface(NULL, NULL, NULL, "invalid ns change", e,
error);
else if (!*ns) else if (!*ns)
*ns = name; *ns = name;
} }
@ -705,14 +818,12 @@ static bool verify_dfa_xindex(struct aa_dfa *dfa, int table_size)
*/ */
static int verify_profile(struct aa_profile *profile) static int verify_profile(struct aa_profile *profile)
{ {
if (aa_g_paranoid_load) { if (profile->file.dfa &&
if (profile->file.dfa && !verify_dfa_xindex(profile->file.dfa,
!verify_dfa_xindex(profile->file.dfa, profile->file.trans.size)) {
profile->file.trans.size)) { audit_iface(profile, NULL, NULL, "Invalid named transition",
audit_iface(profile, NULL, "Invalid named transition", NULL, -EPROTO);
NULL, -EPROTO); return -EPROTO;
return -EPROTO;
}
} }
return 0; return 0;
@ -724,6 +835,7 @@ void aa_load_ent_free(struct aa_load_ent *ent)
aa_put_profile(ent->rename); aa_put_profile(ent->rename);
aa_put_profile(ent->old); aa_put_profile(ent->old);
aa_put_profile(ent->new); aa_put_profile(ent->new);
kfree(ent->ns_name);
kzfree(ent); kzfree(ent);
} }
} }
@ -739,7 +851,6 @@ struct aa_load_ent *aa_load_ent_alloc(void)
/** /**
* aa_unpack - unpack packed binary profile(s) 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
* @lh: list to place unpacked profiles in a aa_repl_ws * @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)
* *
@ -749,26 +860,28 @@ struct aa_load_ent *aa_load_ent_alloc(void)
* *
* Returns: profile(s) on @lh else error pointer if fails to unpack * Returns: profile(s) on @lh else error pointer if fails to unpack
*/ */
int aa_unpack(void *udata, size_t size, struct list_head *lh, const char **ns) int aa_unpack(struct aa_loaddata *udata, struct list_head *lh,
const char **ns)
{ {
struct aa_load_ent *tmp, *ent; 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 = {
.start = udata, .start = udata->data,
.end = udata + size, .end = udata->data + udata->size,
.pos = udata, .pos = udata->data,
}; };
*ns = NULL; *ns = NULL;
while (e.pos < e.end) { while (e.pos < e.end) {
char *ns_name = NULL;
void *start; void *start;
error = verify_header(&e, e.pos == e.start, ns); error = verify_header(&e, e.pos == e.start, ns);
if (error) if (error)
goto fail; goto fail;
start = e.pos; start = e.pos;
profile = unpack_profile(&e); profile = unpack_profile(&e, &ns_name);
if (IS_ERR(profile)) { if (IS_ERR(profile)) {
error = PTR_ERR(profile); error = PTR_ERR(profile);
goto fail; goto fail;
@ -778,7 +891,8 @@ int aa_unpack(void *udata, size_t size, struct list_head *lh, const char **ns)
if (error) if (error)
goto fail_profile; goto fail_profile;
error = aa_calc_profile_hash(profile, e.version, start, if (aa_g_hash_policy)
error = aa_calc_profile_hash(profile, e.version, start,
e.pos - start); e.pos - start);
if (error) if (error)
goto fail_profile; goto fail_profile;
@ -790,9 +904,18 @@ int aa_unpack(void *udata, size_t size, struct list_head *lh, const char **ns)
} }
ent->new = profile; ent->new = profile;
ent->ns_name = ns_name;
list_add_tail(&ent->list, lh); list_add_tail(&ent->list, lh);
} }
udata->abi = e.version & K_ABI_MASK;
if (aa_g_hash_policy) {
udata->hash = aa_calc_hash(udata->data, udata->size);
if (IS_ERR(udata->hash)) {
error = PTR_ERR(udata->hash);
udata->hash = NULL;
goto fail;
}
}
return 0; return 0;
fail_profile: fail_profile:

View File

@ -15,6 +15,7 @@
#include "include/apparmor.h" #include "include/apparmor.h"
#include "include/context.h" #include "include/context.h"
#include "include/policy.h" #include "include/policy.h"
#include "include/policy_ns.h"
#include "include/domain.h" #include "include/domain.h"
#include "include/procattr.h" #include "include/procattr.h"
@ -39,14 +40,14 @@ int aa_getprocattr(struct aa_profile *profile, char **string)
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 = aa_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_ns *ns = profile->ns;
struct aa_namespace *current_ns = __aa_current_profile()->ns; struct aa_ns *current_ns = __aa_current_profile()->ns;
char *s; char *s;
if (!aa_ns_visible(current_ns, ns)) if (!aa_ns_visible(current_ns, ns, true))
return -EACCES; return -EACCES;
ns_name = aa_ns_name(current_ns, ns); ns_name = aa_ns_name(current_ns, ns, true);
ns_len = strlen(ns_name); ns_len = strlen(ns_name);
/* if the visible ns_name is > 0 increase size for : :// seperator */ /* if the visible ns_name is > 0 increase size for : :// seperator */
@ -87,13 +88,13 @@ int aa_getprocattr(struct aa_profile *profile, char **string)
* *
* Returns: start position of name after token else NULL on failure * Returns: start position of name after token else NULL on failure
*/ */
static char *split_token_from_name(int op, char *args, u64 * token) static char *split_token_from_name(const char *op, char *args, u64 *token)
{ {
char *name; char *name;
*token = simple_strtoull(args, &name, 16); *token = simple_strtoull(args, &name, 16);
if ((name == args) || *name != '^') { if ((name == args) || *name != '^') {
AA_ERROR("%s: Invalid input '%s'", op_table[op], args); AA_ERROR("%s: Invalid input '%s'", op, args);
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
} }
@ -138,28 +139,13 @@ int aa_setprocattr_changehat(char *args, size_t size, int test)
for (count = 0; (hat < end) && count < 16; ++count) { for (count = 0; (hat < end) && count < 16; ++count) {
char *next = hat + strlen(hat) + 1; char *next = hat + strlen(hat) + 1;
hats[count] = hat; hats[count] = hat;
AA_DEBUG("%s: (pid %d) Magic 0x%llx count %d hat '%s'\n"
, __func__, current->pid, token, count, hat);
hat = next; hat = next;
} }
} } else
AA_DEBUG("%s: (pid %d) Magic 0x%llx count %d Hat '%s'\n",
AA_DEBUG("%s: Magic 0x%llx Hat '%s'\n", __func__, current->pid, token, count, "<NULL>");
__func__, token, hat ? hat : NULL);
return aa_change_hat(hats, count, token, test); return aa_change_hat(hats, count, token, test);
} }
/**
* aa_setprocattr_changeprofile - handle procattr interface to changeprofile
* @fqname: args received from writting to /proc/<pid>/attr/current (NOT NULL)
* @onexec: true if change_profile should be delayed until exec
* @test: true if this is a test of change_profile permissions
*
* Returns: %0 or error code if change_profile fails
*/
int aa_setprocattr_changeprofile(char *fqname, bool onexec, int test)
{
char *name, *ns_name;
name = aa_split_fqname(fqname, &ns_name);
return aa_change_profile(ns_name, name, onexec, test);
}

View File

@ -35,7 +35,7 @@ static void audit_cb(struct audit_buffer *ab, void *va)
struct common_audit_data *sa = va; struct common_audit_data *sa = va;
audit_log_format(ab, " rlimit=%s value=%lu", audit_log_format(ab, " rlimit=%s value=%lu",
rlim_names[sa->aad->rlim.rlim], sa->aad->rlim.max); rlim_names[aad(sa)->rlim.rlim], aad(sa)->rlim.max);
} }
/** /**
@ -50,17 +50,12 @@ static void audit_cb(struct audit_buffer *ab, void *va)
static int audit_resource(struct aa_profile *profile, unsigned int resource, static int audit_resource(struct aa_profile *profile, unsigned int resource,
unsigned long value, int error) unsigned long value, int error)
{ {
struct common_audit_data sa; DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_SETRLIMIT);
struct apparmor_audit_data aad = {0,};
sa.type = LSM_AUDIT_DATA_NONE; aad(&sa)->rlim.rlim = resource;
sa.aad = &aad; aad(&sa)->rlim.max = value;
aad.op = OP_SETRLIMIT, aad(&sa)->error = error;
aad.rlim.rlim = resource; return aa_audit(AUDIT_APPARMOR_AUTO, profile, &sa, audit_cb);
aad.rlim.max = value;
aad.error = error;
return aa_audit(AUDIT_APPARMOR_AUTO, profile, GFP_KERNEL, &sa,
audit_cb);
} }
/** /**

55
security/apparmor/secid.c Normal file
View File

@ -0,0 +1,55 @@
/*
* AppArmor security module
*
* This file contains AppArmor security identifier (secid) manipulation fns
*
* Copyright 2009-2010 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.
*
*
* AppArmor allocates a unique secid for every profile loaded. If a profile
* is replaced it receives the secid of the profile it is replacing.
*
* The secid value of 0 is invalid.
*/
#include <linux/spinlock.h>
#include <linux/errno.h>
#include <linux/err.h>
#include "include/secid.h"
/* global counter from which secids are allocated */
static u32 global_secid;
static DEFINE_SPINLOCK(secid_lock);
/* TODO FIXME: add secid to profile mapping, and secid recycling */
/**
* aa_alloc_secid - allocate a new secid for a profile
*/
u32 aa_alloc_secid(void)
{
u32 secid;
/*
* TODO FIXME: secid recycling - part of profile mapping table
*/
spin_lock(&secid_lock);
secid = (++global_secid);
spin_unlock(&secid_lock);
return secid;
}
/**
* aa_free_secid - free a secid
* @secid: secid to free
*/
void aa_free_secid(u32 secid)
{
; /* NOP ATM */
}

View File

@ -1,55 +0,0 @@
/*
* AppArmor security module
*
* This file contains AppArmor security identifier (sid) manipulation fns
*
* Copyright 2009-2010 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.
*
*
* AppArmor allocates a unique sid for every profile loaded. If a profile
* is replaced it receives the sid of the profile it is replacing.
*
* The sid value of 0 is invalid.
*/
#include <linux/spinlock.h>
#include <linux/errno.h>
#include <linux/err.h>
#include "include/sid.h"
/* global counter from which sids are allocated */
static u32 global_sid;
static DEFINE_SPINLOCK(sid_lock);
/* TODO FIXME: add sid to profile mapping, and sid recycling */
/**
* aa_alloc_sid - allocate a new sid for a profile
*/
u32 aa_alloc_sid(void)
{
u32 sid;
/*
* TODO FIXME: sid recycling - part of profile mapping table
*/
spin_lock(&sid_lock);
sid = (++global_sid);
spin_unlock(&sid_lock);
return sid;
}
/**
* aa_free_sid - free a sid
* @sid: sid to free
*/
void aa_free_sid(u32 sid)
{
; /* NOP ATM */
}

View File

@ -1093,7 +1093,8 @@ struct security_hook_list capability_hooks[] = {
void __init capability_add_hooks(void) void __init capability_add_hooks(void)
{ {
security_add_hooks(capability_hooks, ARRAY_SIZE(capability_hooks)); security_add_hooks(capability_hooks, ARRAY_SIZE(capability_hooks),
"capability");
} }
#endif /* CONFIG_SECURITY */ #endif /* CONFIG_SECURITY */

View File

@ -20,6 +20,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/namei.h> #include <linux/namei.h>
#include <linux/security.h> #include <linux/security.h>
#include <linux/lsm_hooks.h>
#include <linux/magic.h> #include <linux/magic.h>
static struct vfsmount *mount; static struct vfsmount *mount;
@ -204,6 +205,21 @@ void securityfs_remove(struct dentry *dentry)
} }
EXPORT_SYMBOL_GPL(securityfs_remove); EXPORT_SYMBOL_GPL(securityfs_remove);
#ifdef CONFIG_SECURITY
static struct dentry *lsm_dentry;
static ssize_t lsm_read(struct file *filp, char __user *buf, size_t count,
loff_t *ppos)
{
return simple_read_from_buffer(buf, count, ppos, lsm_names,
strlen(lsm_names));
}
static const struct file_operations lsm_ops = {
.read = lsm_read,
.llseek = generic_file_llseek,
};
#endif
static int __init securityfs_init(void) static int __init securityfs_init(void)
{ {
int retval; int retval;
@ -213,9 +229,15 @@ static int __init securityfs_init(void)
return retval; return retval;
retval = register_filesystem(&fs_type); retval = register_filesystem(&fs_type);
if (retval) if (retval) {
sysfs_remove_mount_point(kernel_kobj, "security"); sysfs_remove_mount_point(kernel_kobj, "security");
return retval; return retval;
}
#ifdef CONFIG_SECURITY
lsm_dentry = securityfs_create_file("lsm", 0444, NULL, NULL,
&lsm_ops);
#endif
return 0;
} }
core_initcall(securityfs_init); core_initcall(securityfs_init);

View File

@ -204,7 +204,7 @@ int ima_store_template(struct ima_template_entry *entry, int violation,
struct inode *inode, struct inode *inode,
const unsigned char *filename, int pcr); const unsigned char *filename, int pcr);
void ima_free_template_entry(struct ima_template_entry *entry); void ima_free_template_entry(struct ima_template_entry *entry);
const char *ima_d_path(const struct path *path, char **pathbuf); const char *ima_d_path(const struct path *path, char **pathbuf, char *filename);
/* IMA policy related functions */ /* IMA policy related functions */
int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask, int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,

View File

@ -157,7 +157,8 @@ err_out:
/** /**
* ima_get_action - appraise & measure decision based on policy. * ima_get_action - appraise & measure decision based on policy.
* @inode: pointer to inode to measure * @inode: pointer to inode to measure
* @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXECUTE) * @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXEC,
* MAY_APPEND)
* @func: caller identifier * @func: caller identifier
* @pcr: pointer filled in if matched measure policy sets pcr= * @pcr: pointer filled in if matched measure policy sets pcr=
* *
@ -318,7 +319,17 @@ void ima_audit_measurement(struct integrity_iint_cache *iint,
iint->flags |= IMA_AUDITED; iint->flags |= IMA_AUDITED;
} }
const char *ima_d_path(const struct path *path, char **pathbuf) /*
* ima_d_path - return a pointer to the full pathname
*
* Attempt to return a pointer to the full pathname for use in the
* IMA measurement list, IMA audit records, and auditing logs.
*
* On failure, return a pointer to a copy of the filename, not dname.
* Returning a pointer to dname, could result in using the pointer
* after the memory has been freed.
*/
const char *ima_d_path(const struct path *path, char **pathbuf, char *namebuf)
{ {
char *pathname = NULL; char *pathname = NULL;
@ -331,5 +342,11 @@ const char *ima_d_path(const struct path *path, char **pathbuf)
pathname = NULL; pathname = NULL;
} }
} }
return pathname ?: (const char *)path->dentry->d_name.name;
if (!pathname) {
strlcpy(namebuf, path->dentry->d_name.name, NAME_MAX);
pathname = namebuf;
}
return pathname;
} }

View File

@ -83,6 +83,7 @@ static void ima_rdwr_violation_check(struct file *file,
const char **pathname) const char **pathname)
{ {
struct inode *inode = file_inode(file); struct inode *inode = file_inode(file);
char filename[NAME_MAX];
fmode_t mode = file->f_mode; fmode_t mode = file->f_mode;
bool send_tomtou = false, send_writers = false; bool send_tomtou = false, send_writers = false;
@ -102,7 +103,7 @@ static void ima_rdwr_violation_check(struct file *file,
if (!send_tomtou && !send_writers) if (!send_tomtou && !send_writers)
return; return;
*pathname = ima_d_path(&file->f_path, pathbuf); *pathname = ima_d_path(&file->f_path, pathbuf, filename);
if (send_tomtou) if (send_tomtou)
ima_add_violation(file, *pathname, iint, ima_add_violation(file, *pathname, iint,
@ -161,6 +162,7 @@ static int process_measurement(struct file *file, char *buf, loff_t size,
struct integrity_iint_cache *iint = NULL; struct integrity_iint_cache *iint = NULL;
struct ima_template_desc *template_desc; struct ima_template_desc *template_desc;
char *pathbuf = NULL; char *pathbuf = NULL;
char filename[NAME_MAX];
const char *pathname = NULL; const char *pathname = NULL;
int rc = -ENOMEM, action, must_appraise; int rc = -ENOMEM, action, must_appraise;
int pcr = CONFIG_IMA_MEASURE_PCR_IDX; int pcr = CONFIG_IMA_MEASURE_PCR_IDX;
@ -239,8 +241,8 @@ static int process_measurement(struct file *file, char *buf, loff_t size,
goto out_digsig; goto out_digsig;
} }
if (!pathname) /* ima_rdwr_violation possibly pre-fetched */ if (!pathbuf) /* ima_rdwr_violation possibly pre-fetched */
pathname = ima_d_path(&file->f_path, &pathbuf); pathname = ima_d_path(&file->f_path, &pathbuf, filename);
if (action & IMA_MEASURE) if (action & IMA_MEASURE)
ima_store_measurement(iint, file, pathname, ima_store_measurement(iint, file, pathname,
@ -307,7 +309,7 @@ int ima_bprm_check(struct linux_binprm *bprm)
/** /**
* ima_path_check - based on policy, collect/store measurement. * ima_path_check - based on policy, collect/store measurement.
* @file: pointer to the file to be measured * @file: pointer to the file to be measured
* @mask: contains MAY_READ, MAY_WRITE or MAY_EXECUTE * @mask: contains MAY_READ, MAY_WRITE, MAY_EXEC or MAY_APPEND
* *
* Measure files based on the ima_must_measure() policy decision. * Measure files based on the ima_must_measure() policy decision.
* *
@ -317,8 +319,8 @@ int ima_bprm_check(struct linux_binprm *bprm)
int ima_file_check(struct file *file, int mask, int opened) int ima_file_check(struct file *file, int mask, int opened)
{ {
return process_measurement(file, NULL, 0, return process_measurement(file, NULL, 0,
mask & (MAY_READ | MAY_WRITE | MAY_EXEC), mask & (MAY_READ | MAY_WRITE | MAY_EXEC |
FILE_CHECK, opened); MAY_APPEND), FILE_CHECK, opened);
} }
EXPORT_SYMBOL_GPL(ima_file_check); EXPORT_SYMBOL_GPL(ima_file_check);

View File

@ -437,7 +437,7 @@ static struct skcipher_request *init_skcipher_req(const u8 *key,
static struct key *request_master_key(struct encrypted_key_payload *epayload, static struct key *request_master_key(struct encrypted_key_payload *epayload,
const u8 **master_key, size_t *master_keylen) const u8 **master_key, size_t *master_keylen)
{ {
struct key *mkey = NULL; struct key *mkey = ERR_PTR(-EINVAL);
if (!strncmp(epayload->master_desc, KEY_TRUSTED_PREFIX, if (!strncmp(epayload->master_desc, KEY_TRUSTED_PREFIX,
KEY_TRUSTED_PREFIX_LEN)) { KEY_TRUSTED_PREFIX_LEN)) {
@ -985,7 +985,7 @@ static void encrypted_destroy(struct key *key)
if (!epayload) if (!epayload)
return; return;
memset(epayload->decrypted_data, 0, epayload->decrypted_datalen); memzero_explicit(epayload->decrypted_data, epayload->decrypted_datalen);
kfree(key->payload.data[0]); kfree(key->payload.data[0]);
} }

View File

@ -182,7 +182,7 @@ static struct security_hook_list loadpin_hooks[] = {
void __init loadpin_add_hooks(void) void __init loadpin_add_hooks(void)
{ {
pr_info("ready to pin (currently %sabled)", enabled ? "en" : "dis"); pr_info("ready to pin (currently %sabled)", enabled ? "en" : "dis");
security_add_hooks(loadpin_hooks, ARRAY_SIZE(loadpin_hooks)); security_add_hooks(loadpin_hooks, ARRAY_SIZE(loadpin_hooks), "loadpin");
} }
/* Should not be mutable after boot, so not listed in sysfs (perm == 0). */ /* Should not be mutable after boot, so not listed in sysfs (perm == 0). */

View File

@ -32,6 +32,7 @@
/* Maximum number of letters for an LSM name string */ /* Maximum number of letters for an LSM name string */
#define SECURITY_NAME_MAX 10 #define SECURITY_NAME_MAX 10
char *lsm_names;
/* Boot-time LSM user choice */ /* Boot-time LSM user choice */
static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] = static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] =
CONFIG_DEFAULT_SECURITY; CONFIG_DEFAULT_SECURITY;
@ -78,6 +79,22 @@ static int __init choose_lsm(char *str)
} }
__setup("security=", choose_lsm); __setup("security=", choose_lsm);
static int lsm_append(char *new, char **result)
{
char *cp;
if (*result == NULL) {
*result = kstrdup(new, GFP_KERNEL);
} else {
cp = kasprintf(GFP_KERNEL, "%s,%s", *result, new);
if (cp == NULL)
return -ENOMEM;
kfree(*result);
*result = cp;
}
return 0;
}
/** /**
* security_module_enable - Load given security module on boot ? * security_module_enable - Load given security module on boot ?
* @module: the name of the module * @module: the name of the module
@ -97,6 +114,27 @@ int __init security_module_enable(const char *module)
return !strcmp(module, chosen_lsm); return !strcmp(module, chosen_lsm);
} }
/**
* security_add_hooks - Add a modules hooks to the hook lists.
* @hooks: the hooks to add
* @count: the number of hooks to add
* @lsm: the name of the security module
*
* Each LSM has to register its hooks with the infrastructure.
*/
void __init security_add_hooks(struct security_hook_list *hooks, int count,
char *lsm)
{
int i;
for (i = 0; i < count; i++) {
hooks[i].lsm = lsm;
list_add_tail_rcu(&hooks[i].list, hooks[i].head);
}
if (lsm_append(lsm, &lsm_names) < 0)
panic("%s - Cannot get early memory.\n", __func__);
}
/* /*
* Hook list operation macros. * Hook list operation macros.
* *
@ -1025,11 +1063,6 @@ int security_task_kill(struct task_struct *p, struct siginfo *info,
return call_int_hook(task_kill, 0, p, info, sig, secid); return call_int_hook(task_kill, 0, p, info, sig, secid);
} }
int security_task_wait(struct task_struct *p)
{
return call_int_hook(task_wait, 0, p);
}
int security_task_prctl(int option, unsigned long arg2, unsigned long arg3, int security_task_prctl(int option, unsigned long arg2, unsigned long arg3,
unsigned long arg4, unsigned long arg5) unsigned long arg4, unsigned long arg5)
{ {
@ -1170,9 +1203,9 @@ int security_getprocattr(struct task_struct *p, char *name, char **value)
return call_int_hook(getprocattr, -EINVAL, p, name, value); return call_int_hook(getprocattr, -EINVAL, p, name, value);
} }
int security_setprocattr(struct task_struct *p, char *name, void *value, size_t size) int security_setprocattr(const char *name, void *value, size_t size)
{ {
return call_int_hook(setprocattr, -EINVAL, p, name, value, size); return call_int_hook(setprocattr, -EINVAL, name, value, size);
} }
int security_netlink_send(struct sock *sk, struct sk_buff *skb) int security_netlink_send(struct sock *sk, struct sk_buff *skb)
@ -1769,7 +1802,6 @@ struct security_hook_heads security_hook_heads = {
.task_movememory = .task_movememory =
LIST_HEAD_INIT(security_hook_heads.task_movememory), LIST_HEAD_INIT(security_hook_heads.task_movememory),
.task_kill = LIST_HEAD_INIT(security_hook_heads.task_kill), .task_kill = LIST_HEAD_INIT(security_hook_heads.task_kill),
.task_wait = LIST_HEAD_INIT(security_hook_heads.task_wait),
.task_prctl = LIST_HEAD_INIT(security_hook_heads.task_prctl), .task_prctl = LIST_HEAD_INIT(security_hook_heads.task_prctl),
.task_to_inode = .task_to_inode =
LIST_HEAD_INIT(security_hook_heads.task_to_inode), LIST_HEAD_INIT(security_hook_heads.task_to_inode),

View File

@ -210,16 +210,6 @@ static inline u32 task_sid(const struct task_struct *task)
return sid; return sid;
} }
/*
* get the subjective security ID of the current task
*/
static inline u32 current_sid(void)
{
const struct task_security_struct *tsec = current_security();
return tsec->sid;
}
/* Allocate and free functions for each kind of security blob. */ /* Allocate and free functions for each kind of security blob. */
static int inode_alloc_security(struct inode *inode) static int inode_alloc_security(struct inode *inode)
@ -490,8 +480,11 @@ static int selinux_is_sblabel_mnt(struct super_block *sb)
sbsec->behavior == SECURITY_FS_USE_NATIVE || sbsec->behavior == SECURITY_FS_USE_NATIVE ||
/* Special handling. Genfs but also in-core setxattr handler */ /* Special handling. Genfs but also in-core setxattr handler */
!strcmp(sb->s_type->name, "sysfs") || !strcmp(sb->s_type->name, "sysfs") ||
!strcmp(sb->s_type->name, "cgroup") ||
!strcmp(sb->s_type->name, "cgroup2") ||
!strcmp(sb->s_type->name, "pstore") || !strcmp(sb->s_type->name, "pstore") ||
!strcmp(sb->s_type->name, "debugfs") || !strcmp(sb->s_type->name, "debugfs") ||
!strcmp(sb->s_type->name, "tracefs") ||
!strcmp(sb->s_type->name, "rootfs"); !strcmp(sb->s_type->name, "rootfs");
} }
@ -833,10 +826,14 @@ static int selinux_set_mnt_opts(struct super_block *sb,
} }
/* /*
* If this is a user namespace mount, no contexts are allowed * If this is a user namespace mount and the filesystem type is not
* on the command line and security labels must be ignored. * explicitly whitelisted, then no contexts are allowed on the command
* line and security labels must be ignored.
*/ */
if (sb->s_user_ns != &init_user_ns) { if (sb->s_user_ns != &init_user_ns &&
strcmp(sb->s_type->name, "tmpfs") &&
strcmp(sb->s_type->name, "ramfs") &&
strcmp(sb->s_type->name, "devpts")) {
if (context_sid || fscontext_sid || rootcontext_sid || if (context_sid || fscontext_sid || rootcontext_sid ||
defcontext_sid) { defcontext_sid) {
rc = -EACCES; rc = -EACCES;
@ -1268,6 +1265,8 @@ static inline int default_protocol_dgram(int protocol)
static inline u16 socket_type_to_security_class(int family, int type, int protocol) static inline u16 socket_type_to_security_class(int family, int type, int protocol)
{ {
int extsockclass = selinux_policycap_extsockclass;
switch (family) { switch (family) {
case PF_UNIX: case PF_UNIX:
switch (type) { switch (type) {
@ -1282,13 +1281,19 @@ static inline u16 socket_type_to_security_class(int family, int type, int protoc
case PF_INET6: case PF_INET6:
switch (type) { switch (type) {
case SOCK_STREAM: case SOCK_STREAM:
case SOCK_SEQPACKET:
if (default_protocol_stream(protocol)) if (default_protocol_stream(protocol))
return SECCLASS_TCP_SOCKET; return SECCLASS_TCP_SOCKET;
else if (extsockclass && protocol == IPPROTO_SCTP)
return SECCLASS_SCTP_SOCKET;
else else
return SECCLASS_RAWIP_SOCKET; return SECCLASS_RAWIP_SOCKET;
case SOCK_DGRAM: case SOCK_DGRAM:
if (default_protocol_dgram(protocol)) if (default_protocol_dgram(protocol))
return SECCLASS_UDP_SOCKET; return SECCLASS_UDP_SOCKET;
else if (extsockclass && (protocol == IPPROTO_ICMP ||
protocol == IPPROTO_ICMPV6))
return SECCLASS_ICMP_SOCKET;
else else
return SECCLASS_RAWIP_SOCKET; return SECCLASS_RAWIP_SOCKET;
case SOCK_DCCP: case SOCK_DCCP:
@ -1342,6 +1347,66 @@ static inline u16 socket_type_to_security_class(int family, int type, int protoc
return SECCLASS_APPLETALK_SOCKET; return SECCLASS_APPLETALK_SOCKET;
} }
if (extsockclass) {
switch (family) {
case PF_AX25:
return SECCLASS_AX25_SOCKET;
case PF_IPX:
return SECCLASS_IPX_SOCKET;
case PF_NETROM:
return SECCLASS_NETROM_SOCKET;
case PF_ATMPVC:
return SECCLASS_ATMPVC_SOCKET;
case PF_X25:
return SECCLASS_X25_SOCKET;
case PF_ROSE:
return SECCLASS_ROSE_SOCKET;
case PF_DECnet:
return SECCLASS_DECNET_SOCKET;
case PF_ATMSVC:
return SECCLASS_ATMSVC_SOCKET;
case PF_RDS:
return SECCLASS_RDS_SOCKET;
case PF_IRDA:
return SECCLASS_IRDA_SOCKET;
case PF_PPPOX:
return SECCLASS_PPPOX_SOCKET;
case PF_LLC:
return SECCLASS_LLC_SOCKET;
case PF_CAN:
return SECCLASS_CAN_SOCKET;
case PF_TIPC:
return SECCLASS_TIPC_SOCKET;
case PF_BLUETOOTH:
return SECCLASS_BLUETOOTH_SOCKET;
case PF_IUCV:
return SECCLASS_IUCV_SOCKET;
case PF_RXRPC:
return SECCLASS_RXRPC_SOCKET;
case PF_ISDN:
return SECCLASS_ISDN_SOCKET;
case PF_PHONET:
return SECCLASS_PHONET_SOCKET;
case PF_IEEE802154:
return SECCLASS_IEEE802154_SOCKET;
case PF_CAIF:
return SECCLASS_CAIF_SOCKET;
case PF_ALG:
return SECCLASS_ALG_SOCKET;
case PF_NFC:
return SECCLASS_NFC_SOCKET;
case PF_VSOCK:
return SECCLASS_VSOCK_SOCKET;
case PF_KCM:
return SECCLASS_KCM_SOCKET;
case PF_QIPCRTR:
return SECCLASS_QIPCRTR_SOCKET;
#if PF_MAX > 43
#error New address family defined, please update this function.
#endif
}
}
return SECCLASS_SOCKET; return SECCLASS_SOCKET;
} }
@ -1608,55 +1673,6 @@ static inline u32 signal_to_av(int sig)
return perm; return perm;
} }
/*
* Check permission between a pair of credentials
* fork check, ptrace check, etc.
*/
static int cred_has_perm(const struct cred *actor,
const struct cred *target,
u32 perms)
{
u32 asid = cred_sid(actor), tsid = cred_sid(target);
return avc_has_perm(asid, tsid, SECCLASS_PROCESS, perms, NULL);
}
/*
* Check permission between a pair of tasks, e.g. signal checks,
* fork check, ptrace check, etc.
* tsk1 is the actor and tsk2 is the target
* - this uses the default subjective creds of tsk1
*/
static int task_has_perm(const struct task_struct *tsk1,
const struct task_struct *tsk2,
u32 perms)
{
const struct task_security_struct *__tsec1, *__tsec2;
u32 sid1, sid2;
rcu_read_lock();
__tsec1 = __task_cred(tsk1)->security; sid1 = __tsec1->sid;
__tsec2 = __task_cred(tsk2)->security; sid2 = __tsec2->sid;
rcu_read_unlock();
return avc_has_perm(sid1, sid2, SECCLASS_PROCESS, perms, NULL);
}
/*
* Check permission between current and another task, e.g. signal checks,
* fork check, ptrace check, etc.
* current is the actor and tsk2 is the target
* - this uses current's subjective creds
*/
static int current_has_perm(const struct task_struct *tsk,
u32 perms)
{
u32 sid, tsid;
sid = current_sid();
tsid = task_sid(tsk);
return avc_has_perm(sid, tsid, SECCLASS_PROCESS, perms, NULL);
}
#if CAP_LAST_CAP > 63 #if CAP_LAST_CAP > 63
#error Fix SELinux to handle capabilities > 63. #error Fix SELinux to handle capabilities > 63.
#endif #endif
@ -1698,16 +1714,6 @@ static int cred_has_capability(const struct cred *cred,
return rc; return rc;
} }
/* Check whether a task is allowed to use a system operation. */
static int task_has_system(struct task_struct *tsk,
u32 perms)
{
u32 sid = task_sid(tsk);
return avc_has_perm(sid, SECINITSID_KERNEL,
SECCLASS_SYSTEM, perms, NULL);
}
/* Check whether a task has a particular permission to an inode. /* Check whether a task has a particular permission to an inode.
The 'adp' parameter is optional and allows other audit The 'adp' parameter is optional and allows other audit
data to be passed (e.g. the dentry). */ data to be passed (e.g. the dentry). */
@ -1879,15 +1885,6 @@ static int may_create(struct inode *dir,
FILESYSTEM__ASSOCIATE, &ad); FILESYSTEM__ASSOCIATE, &ad);
} }
/* Check whether a task can create a key. */
static int may_create_key(u32 ksid,
struct task_struct *ctx)
{
u32 sid = task_sid(ctx);
return avc_has_perm(sid, ksid, SECCLASS_KEY, KEY__CREATE, NULL);
}
#define MAY_LINK 0 #define MAY_LINK 0
#define MAY_UNLINK 1 #define MAY_UNLINK 1
#define MAY_RMDIR 2 #define MAY_RMDIR 2
@ -2143,24 +2140,26 @@ static int selinux_binder_transfer_file(struct task_struct *from,
static int selinux_ptrace_access_check(struct task_struct *child, static int selinux_ptrace_access_check(struct task_struct *child,
unsigned int mode) unsigned int mode)
{ {
if (mode & PTRACE_MODE_READ) { u32 sid = current_sid();
u32 sid = current_sid(); u32 csid = task_sid(child);
u32 csid = task_sid(child);
return avc_has_perm(sid, csid, SECCLASS_FILE, FILE__READ, NULL);
}
return current_has_perm(child, PROCESS__PTRACE); if (mode & PTRACE_MODE_READ)
return avc_has_perm(sid, csid, SECCLASS_FILE, FILE__READ, NULL);
return avc_has_perm(sid, csid, SECCLASS_PROCESS, PROCESS__PTRACE, NULL);
} }
static int selinux_ptrace_traceme(struct task_struct *parent) static int selinux_ptrace_traceme(struct task_struct *parent)
{ {
return task_has_perm(parent, current, PROCESS__PTRACE); return avc_has_perm(task_sid(parent), current_sid(), SECCLASS_PROCESS,
PROCESS__PTRACE, NULL);
} }
static int selinux_capget(struct task_struct *target, kernel_cap_t *effective, static int selinux_capget(struct task_struct *target, kernel_cap_t *effective,
kernel_cap_t *inheritable, kernel_cap_t *permitted) kernel_cap_t *inheritable, kernel_cap_t *permitted)
{ {
return current_has_perm(target, PROCESS__GETCAP); return avc_has_perm(current_sid(), task_sid(target), SECCLASS_PROCESS,
PROCESS__GETCAP, NULL);
} }
static int selinux_capset(struct cred *new, const struct cred *old, static int selinux_capset(struct cred *new, const struct cred *old,
@ -2168,7 +2167,8 @@ static int selinux_capset(struct cred *new, const struct cred *old,
const kernel_cap_t *inheritable, const kernel_cap_t *inheritable,
const kernel_cap_t *permitted) const kernel_cap_t *permitted)
{ {
return cred_has_perm(old, new, PROCESS__SETCAP); return avc_has_perm(cred_sid(old), cred_sid(new), SECCLASS_PROCESS,
PROCESS__SETCAP, NULL);
} }
/* /*
@ -2224,29 +2224,22 @@ static int selinux_quota_on(struct dentry *dentry)
static int selinux_syslog(int type) static int selinux_syslog(int type)
{ {
int rc;
switch (type) { switch (type) {
case SYSLOG_ACTION_READ_ALL: /* Read last kernel messages */ case SYSLOG_ACTION_READ_ALL: /* Read last kernel messages */
case SYSLOG_ACTION_SIZE_BUFFER: /* Return size of the log buffer */ case SYSLOG_ACTION_SIZE_BUFFER: /* Return size of the log buffer */
rc = task_has_system(current, SYSTEM__SYSLOG_READ); return avc_has_perm(current_sid(), SECINITSID_KERNEL,
break; SECCLASS_SYSTEM, SYSTEM__SYSLOG_READ, NULL);
case SYSLOG_ACTION_CONSOLE_OFF: /* Disable logging to console */ case SYSLOG_ACTION_CONSOLE_OFF: /* Disable logging to console */
case SYSLOG_ACTION_CONSOLE_ON: /* Enable logging to console */ case SYSLOG_ACTION_CONSOLE_ON: /* Enable logging to console */
/* Set level of messages printed to console */ /* Set level of messages printed to console */
case SYSLOG_ACTION_CONSOLE_LEVEL: case SYSLOG_ACTION_CONSOLE_LEVEL:
rc = task_has_system(current, SYSTEM__SYSLOG_CONSOLE); return avc_has_perm(current_sid(), SECINITSID_KERNEL,
break; SECCLASS_SYSTEM, SYSTEM__SYSLOG_CONSOLE,
case SYSLOG_ACTION_CLOSE: /* Close log */ NULL);
case SYSLOG_ACTION_OPEN: /* Open log */
case SYSLOG_ACTION_READ: /* Read from log */
case SYSLOG_ACTION_READ_CLEAR: /* Read/clear last kernel messages */
case SYSLOG_ACTION_CLEAR: /* Clear ring buffer */
default:
rc = task_has_system(current, SYSTEM__SYSLOG_MOD);
break;
} }
return rc; /* All other syslog types */
return avc_has_perm(current_sid(), SECINITSID_KERNEL,
SECCLASS_SYSTEM, SYSTEM__SYSLOG_MOD, NULL);
} }
/* /*
@ -2271,13 +2264,13 @@ static int selinux_vm_enough_memory(struct mm_struct *mm, long pages)
/* binprm security operations */ /* binprm security operations */
static u32 ptrace_parent_sid(struct task_struct *task) static u32 ptrace_parent_sid(void)
{ {
u32 sid = 0; u32 sid = 0;
struct task_struct *tracer; struct task_struct *tracer;
rcu_read_lock(); rcu_read_lock();
tracer = ptrace_parent(task); tracer = ptrace_parent(current);
if (tracer) if (tracer)
sid = task_sid(tracer); sid = task_sid(tracer);
rcu_read_unlock(); rcu_read_unlock();
@ -2406,7 +2399,7 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm)
* changes its SID has the appropriate permit */ * changes its SID has the appropriate permit */
if (bprm->unsafe & if (bprm->unsafe &
(LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) { (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) {
u32 ptsid = ptrace_parent_sid(current); u32 ptsid = ptrace_parent_sid();
if (ptsid != 0) { if (ptsid != 0) {
rc = avc_has_perm(ptsid, new_tsec->sid, rc = avc_has_perm(ptsid, new_tsec->sid,
SECCLASS_PROCESS, SECCLASS_PROCESS,
@ -3503,6 +3496,7 @@ static int default_noexec;
static int file_map_prot_check(struct file *file, unsigned long prot, int shared) static int file_map_prot_check(struct file *file, unsigned long prot, int shared)
{ {
const struct cred *cred = current_cred(); const struct cred *cred = current_cred();
u32 sid = cred_sid(cred);
int rc = 0; int rc = 0;
if (default_noexec && if (default_noexec &&
@ -3513,7 +3507,8 @@ static int file_map_prot_check(struct file *file, unsigned long prot, int shared
* private file mapping that will also be writable. * private file mapping that will also be writable.
* This has an additional check. * This has an additional check.
*/ */
rc = cred_has_perm(cred, cred, PROCESS__EXECMEM); rc = avc_has_perm(sid, sid, SECCLASS_PROCESS,
PROCESS__EXECMEM, NULL);
if (rc) if (rc)
goto error; goto error;
} }
@ -3564,6 +3559,7 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
unsigned long prot) unsigned long prot)
{ {
const struct cred *cred = current_cred(); const struct cred *cred = current_cred();
u32 sid = cred_sid(cred);
if (selinux_checkreqprot) if (selinux_checkreqprot)
prot = reqprot; prot = reqprot;
@ -3573,12 +3569,14 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
int rc = 0; int rc = 0;
if (vma->vm_start >= vma->vm_mm->start_brk && if (vma->vm_start >= vma->vm_mm->start_brk &&
vma->vm_end <= vma->vm_mm->brk) { vma->vm_end <= vma->vm_mm->brk) {
rc = cred_has_perm(cred, cred, PROCESS__EXECHEAP); rc = avc_has_perm(sid, sid, SECCLASS_PROCESS,
PROCESS__EXECHEAP, NULL);
} else if (!vma->vm_file && } else if (!vma->vm_file &&
((vma->vm_start <= vma->vm_mm->start_stack && ((vma->vm_start <= vma->vm_mm->start_stack &&
vma->vm_end >= vma->vm_mm->start_stack) || vma->vm_end >= vma->vm_mm->start_stack) ||
vma_is_stack_for_current(vma))) { vma_is_stack_for_current(vma))) {
rc = current_has_perm(current, PROCESS__EXECSTACK); rc = avc_has_perm(sid, sid, SECCLASS_PROCESS,
PROCESS__EXECSTACK, NULL);
} else if (vma->vm_file && vma->anon_vma) { } else if (vma->vm_file && vma->anon_vma) {
/* /*
* We are making executable a file mapping that has * We are making executable a file mapping that has
@ -3711,7 +3709,9 @@ static int selinux_file_open(struct file *file, const struct cred *cred)
static int selinux_task_create(unsigned long clone_flags) static int selinux_task_create(unsigned long clone_flags)
{ {
return current_has_perm(current, PROCESS__FORK); u32 sid = current_sid();
return avc_has_perm(sid, sid, SECCLASS_PROCESS, PROCESS__FORK, NULL);
} }
/* /*
@ -3821,15 +3821,12 @@ static int selinux_kernel_create_files_as(struct cred *new, struct inode *inode)
static int selinux_kernel_module_request(char *kmod_name) static int selinux_kernel_module_request(char *kmod_name)
{ {
u32 sid;
struct common_audit_data ad; struct common_audit_data ad;
sid = task_sid(current);
ad.type = LSM_AUDIT_DATA_KMOD; ad.type = LSM_AUDIT_DATA_KMOD;
ad.u.kmod_name = kmod_name; ad.u.kmod_name = kmod_name;
return avc_has_perm(sid, SECINITSID_KERNEL, SECCLASS_SYSTEM, return avc_has_perm(current_sid(), SECINITSID_KERNEL, SECCLASS_SYSTEM,
SYSTEM__MODULE_REQUEST, &ad); SYSTEM__MODULE_REQUEST, &ad);
} }
@ -3881,17 +3878,20 @@ static int selinux_kernel_read_file(struct file *file,
static int selinux_task_setpgid(struct task_struct *p, pid_t pgid) static int selinux_task_setpgid(struct task_struct *p, pid_t pgid)
{ {
return current_has_perm(p, PROCESS__SETPGID); return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS,
PROCESS__SETPGID, NULL);
} }
static int selinux_task_getpgid(struct task_struct *p) static int selinux_task_getpgid(struct task_struct *p)
{ {
return current_has_perm(p, PROCESS__GETPGID); return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS,
PROCESS__GETPGID, NULL);
} }
static int selinux_task_getsid(struct task_struct *p) static int selinux_task_getsid(struct task_struct *p)
{ {
return current_has_perm(p, PROCESS__GETSESSION); return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS,
PROCESS__GETSESSION, NULL);
} }
static void selinux_task_getsecid(struct task_struct *p, u32 *secid) static void selinux_task_getsecid(struct task_struct *p, u32 *secid)
@ -3901,17 +3901,20 @@ static void selinux_task_getsecid(struct task_struct *p, u32 *secid)
static int selinux_task_setnice(struct task_struct *p, int nice) static int selinux_task_setnice(struct task_struct *p, int nice)
{ {
return current_has_perm(p, PROCESS__SETSCHED); return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS,
PROCESS__SETSCHED, NULL);
} }
static int selinux_task_setioprio(struct task_struct *p, int ioprio) static int selinux_task_setioprio(struct task_struct *p, int ioprio)
{ {
return current_has_perm(p, PROCESS__SETSCHED); return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS,
PROCESS__SETSCHED, NULL);
} }
static int selinux_task_getioprio(struct task_struct *p) static int selinux_task_getioprio(struct task_struct *p)
{ {
return current_has_perm(p, PROCESS__GETSCHED); return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS,
PROCESS__GETSCHED, NULL);
} }
static int selinux_task_setrlimit(struct task_struct *p, unsigned int resource, static int selinux_task_setrlimit(struct task_struct *p, unsigned int resource,
@ -3924,47 +3927,42 @@ static int selinux_task_setrlimit(struct task_struct *p, unsigned int resource,
later be used as a safe reset point for the soft limit later be used as a safe reset point for the soft limit
upon context transitions. See selinux_bprm_committing_creds. */ upon context transitions. See selinux_bprm_committing_creds. */
if (old_rlim->rlim_max != new_rlim->rlim_max) if (old_rlim->rlim_max != new_rlim->rlim_max)
return current_has_perm(p, PROCESS__SETRLIMIT); return avc_has_perm(current_sid(), task_sid(p),
SECCLASS_PROCESS, PROCESS__SETRLIMIT, NULL);
return 0; return 0;
} }
static int selinux_task_setscheduler(struct task_struct *p) static int selinux_task_setscheduler(struct task_struct *p)
{ {
return current_has_perm(p, PROCESS__SETSCHED); return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS,
PROCESS__SETSCHED, NULL);
} }
static int selinux_task_getscheduler(struct task_struct *p) static int selinux_task_getscheduler(struct task_struct *p)
{ {
return current_has_perm(p, PROCESS__GETSCHED); return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS,
PROCESS__GETSCHED, NULL);
} }
static int selinux_task_movememory(struct task_struct *p) static int selinux_task_movememory(struct task_struct *p)
{ {
return current_has_perm(p, PROCESS__SETSCHED); return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS,
PROCESS__SETSCHED, NULL);
} }
static int selinux_task_kill(struct task_struct *p, struct siginfo *info, static int selinux_task_kill(struct task_struct *p, struct siginfo *info,
int sig, u32 secid) int sig, u32 secid)
{ {
u32 perm; u32 perm;
int rc;
if (!sig) if (!sig)
perm = PROCESS__SIGNULL; /* null signal; existence test */ perm = PROCESS__SIGNULL; /* null signal; existence test */
else else
perm = signal_to_av(sig); perm = signal_to_av(sig);
if (secid) if (!secid)
rc = avc_has_perm(secid, task_sid(p), secid = current_sid();
SECCLASS_PROCESS, perm, NULL); return avc_has_perm(secid, task_sid(p), SECCLASS_PROCESS, perm, NULL);
else
rc = current_has_perm(p, perm);
return rc;
}
static int selinux_task_wait(struct task_struct *p)
{
return task_has_perm(p, current, PROCESS__SIGCHLD);
} }
static void selinux_task_to_inode(struct task_struct *p, static void selinux_task_to_inode(struct task_struct *p,
@ -4254,12 +4252,11 @@ static int socket_sockcreate_sid(const struct task_security_struct *tsec,
socksid); socksid);
} }
static int sock_has_perm(struct task_struct *task, struct sock *sk, u32 perms) static int sock_has_perm(struct sock *sk, u32 perms)
{ {
struct sk_security_struct *sksec = sk->sk_security; struct sk_security_struct *sksec = sk->sk_security;
struct common_audit_data ad; struct common_audit_data ad;
struct lsm_network_audit net = {0,}; struct lsm_network_audit net = {0,};
u32 tsid = task_sid(task);
if (sksec->sid == SECINITSID_KERNEL) if (sksec->sid == SECINITSID_KERNEL)
return 0; return 0;
@ -4268,7 +4265,8 @@ static int sock_has_perm(struct task_struct *task, struct sock *sk, u32 perms)
ad.u.net = &net; ad.u.net = &net;
ad.u.net->sk = sk; ad.u.net->sk = sk;
return avc_has_perm(tsid, sksec->sid, sksec->sclass, perms, &ad); return avc_has_perm(current_sid(), sksec->sid, sksec->sclass, perms,
&ad);
} }
static int selinux_socket_create(int family, int type, static int selinux_socket_create(int family, int type,
@ -4330,7 +4328,7 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
u16 family; u16 family;
int err; int err;
err = sock_has_perm(current, sk, SOCKET__BIND); err = sock_has_perm(sk, SOCKET__BIND);
if (err) if (err)
goto out; goto out;
@ -4429,7 +4427,7 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address,
struct sk_security_struct *sksec = sk->sk_security; struct sk_security_struct *sksec = sk->sk_security;
int err; int err;
err = sock_has_perm(current, sk, SOCKET__CONNECT); err = sock_has_perm(sk, SOCKET__CONNECT);
if (err) if (err)
return err; return err;
@ -4481,7 +4479,7 @@ out:
static int selinux_socket_listen(struct socket *sock, int backlog) static int selinux_socket_listen(struct socket *sock, int backlog)
{ {
return sock_has_perm(current, sock->sk, SOCKET__LISTEN); return sock_has_perm(sock->sk, SOCKET__LISTEN);
} }
static int selinux_socket_accept(struct socket *sock, struct socket *newsock) static int selinux_socket_accept(struct socket *sock, struct socket *newsock)
@ -4492,7 +4490,7 @@ static int selinux_socket_accept(struct socket *sock, struct socket *newsock)
u16 sclass; u16 sclass;
u32 sid; u32 sid;
err = sock_has_perm(current, sock->sk, SOCKET__ACCEPT); err = sock_has_perm(sock->sk, SOCKET__ACCEPT);
if (err) if (err)
return err; return err;
@ -4513,30 +4511,30 @@ static int selinux_socket_accept(struct socket *sock, struct socket *newsock)
static int selinux_socket_sendmsg(struct socket *sock, struct msghdr *msg, static int selinux_socket_sendmsg(struct socket *sock, struct msghdr *msg,
int size) int size)
{ {
return sock_has_perm(current, sock->sk, SOCKET__WRITE); return sock_has_perm(sock->sk, SOCKET__WRITE);
} }
static int selinux_socket_recvmsg(struct socket *sock, struct msghdr *msg, static int selinux_socket_recvmsg(struct socket *sock, struct msghdr *msg,
int size, int flags) int size, int flags)
{ {
return sock_has_perm(current, sock->sk, SOCKET__READ); return sock_has_perm(sock->sk, SOCKET__READ);
} }
static int selinux_socket_getsockname(struct socket *sock) static int selinux_socket_getsockname(struct socket *sock)
{ {
return sock_has_perm(current, sock->sk, SOCKET__GETATTR); return sock_has_perm(sock->sk, SOCKET__GETATTR);
} }
static int selinux_socket_getpeername(struct socket *sock) static int selinux_socket_getpeername(struct socket *sock)
{ {
return sock_has_perm(current, sock->sk, SOCKET__GETATTR); return sock_has_perm(sock->sk, SOCKET__GETATTR);
} }
static int selinux_socket_setsockopt(struct socket *sock, int level, int optname) static int selinux_socket_setsockopt(struct socket *sock, int level, int optname)
{ {
int err; int err;
err = sock_has_perm(current, sock->sk, SOCKET__SETOPT); err = sock_has_perm(sock->sk, SOCKET__SETOPT);
if (err) if (err)
return err; return err;
@ -4546,12 +4544,12 @@ static int selinux_socket_setsockopt(struct socket *sock, int level, int optname
static int selinux_socket_getsockopt(struct socket *sock, int level, static int selinux_socket_getsockopt(struct socket *sock, int level,
int optname) int optname)
{ {
return sock_has_perm(current, sock->sk, SOCKET__GETOPT); return sock_has_perm(sock->sk, SOCKET__GETOPT);
} }
static int selinux_socket_shutdown(struct socket *sock, int how) static int selinux_socket_shutdown(struct socket *sock, int how)
{ {
return sock_has_perm(current, sock->sk, SOCKET__SHUTDOWN); return sock_has_perm(sock->sk, SOCKET__SHUTDOWN);
} }
static int selinux_socket_unix_stream_connect(struct sock *sock, static int selinux_socket_unix_stream_connect(struct sock *sock,
@ -5039,7 +5037,7 @@ static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb)
goto out; goto out;
} }
err = sock_has_perm(current, sk, perm); err = sock_has_perm(sk, perm);
out: out:
return err; return err;
} }
@ -5370,20 +5368,17 @@ static int selinux_netlink_send(struct sock *sk, struct sk_buff *skb)
return selinux_nlmsg_perm(sk, skb); return selinux_nlmsg_perm(sk, skb);
} }
static int ipc_alloc_security(struct task_struct *task, static int ipc_alloc_security(struct kern_ipc_perm *perm,
struct kern_ipc_perm *perm,
u16 sclass) u16 sclass)
{ {
struct ipc_security_struct *isec; struct ipc_security_struct *isec;
u32 sid;
isec = kzalloc(sizeof(struct ipc_security_struct), GFP_KERNEL); isec = kzalloc(sizeof(struct ipc_security_struct), GFP_KERNEL);
if (!isec) if (!isec)
return -ENOMEM; return -ENOMEM;
sid = task_sid(task);
isec->sclass = sclass; isec->sclass = sclass;
isec->sid = sid; isec->sid = current_sid();
perm->security = isec; perm->security = isec;
return 0; return 0;
@ -5451,7 +5446,7 @@ static int selinux_msg_queue_alloc_security(struct msg_queue *msq)
u32 sid = current_sid(); u32 sid = current_sid();
int rc; int rc;
rc = ipc_alloc_security(current, &msq->q_perm, SECCLASS_MSGQ); rc = ipc_alloc_security(&msq->q_perm, SECCLASS_MSGQ);
if (rc) if (rc)
return rc; return rc;
@ -5498,7 +5493,8 @@ static int selinux_msg_queue_msgctl(struct msg_queue *msq, int cmd)
case IPC_INFO: case IPC_INFO:
case MSG_INFO: case MSG_INFO:
/* No specific object, just general system-wide information. */ /* No specific object, just general system-wide information. */
return task_has_system(current, SYSTEM__IPC_INFO); return avc_has_perm(current_sid(), SECINITSID_KERNEL,
SECCLASS_SYSTEM, SYSTEM__IPC_INFO, NULL);
case IPC_STAT: case IPC_STAT:
case MSG_STAT: case MSG_STAT:
perms = MSGQ__GETATTR | MSGQ__ASSOCIATE; perms = MSGQ__GETATTR | MSGQ__ASSOCIATE;
@ -5592,7 +5588,7 @@ static int selinux_shm_alloc_security(struct shmid_kernel *shp)
u32 sid = current_sid(); u32 sid = current_sid();
int rc; int rc;
rc = ipc_alloc_security(current, &shp->shm_perm, SECCLASS_SHM); rc = ipc_alloc_security(&shp->shm_perm, SECCLASS_SHM);
if (rc) if (rc)
return rc; return rc;
@ -5640,7 +5636,8 @@ static int selinux_shm_shmctl(struct shmid_kernel *shp, int cmd)
case IPC_INFO: case IPC_INFO:
case SHM_INFO: case SHM_INFO:
/* No specific object, just general system-wide information. */ /* No specific object, just general system-wide information. */
return task_has_system(current, SYSTEM__IPC_INFO); return avc_has_perm(current_sid(), SECINITSID_KERNEL,
SECCLASS_SYSTEM, SYSTEM__IPC_INFO, NULL);
case IPC_STAT: case IPC_STAT:
case SHM_STAT: case SHM_STAT:
perms = SHM__GETATTR | SHM__ASSOCIATE; perms = SHM__GETATTR | SHM__ASSOCIATE;
@ -5684,7 +5681,7 @@ static int selinux_sem_alloc_security(struct sem_array *sma)
u32 sid = current_sid(); u32 sid = current_sid();
int rc; int rc;
rc = ipc_alloc_security(current, &sma->sem_perm, SECCLASS_SEM); rc = ipc_alloc_security(&sma->sem_perm, SECCLASS_SEM);
if (rc) if (rc)
return rc; return rc;
@ -5732,7 +5729,8 @@ static int selinux_sem_semctl(struct sem_array *sma, int cmd)
case IPC_INFO: case IPC_INFO:
case SEM_INFO: case SEM_INFO:
/* No specific object, just general system-wide information. */ /* No specific object, just general system-wide information. */
return task_has_system(current, SYSTEM__IPC_INFO); return avc_has_perm(current_sid(), SECINITSID_KERNEL,
SECCLASS_SYSTEM, SYSTEM__IPC_INFO, NULL);
case GETPID: case GETPID:
case GETNCNT: case GETNCNT:
case GETZCNT: case GETZCNT:
@ -5813,15 +5811,16 @@ static int selinux_getprocattr(struct task_struct *p,
int error; int error;
unsigned len; unsigned len;
if (current != p) {
error = current_has_perm(p, PROCESS__GETATTR);
if (error)
return error;
}
rcu_read_lock(); rcu_read_lock();
__tsec = __task_cred(p)->security; __tsec = __task_cred(p)->security;
if (current != p) {
error = avc_has_perm(current_sid(), __tsec->sid,
SECCLASS_PROCESS, PROCESS__GETATTR, NULL);
if (error)
goto bad;
}
if (!strcmp(name, "current")) if (!strcmp(name, "current"))
sid = __tsec->sid; sid = __tsec->sid;
else if (!strcmp(name, "prev")) else if (!strcmp(name, "prev"))
@ -5834,8 +5833,10 @@ static int selinux_getprocattr(struct task_struct *p,
sid = __tsec->keycreate_sid; sid = __tsec->keycreate_sid;
else if (!strcmp(name, "sockcreate")) else if (!strcmp(name, "sockcreate"))
sid = __tsec->sockcreate_sid; sid = __tsec->sockcreate_sid;
else else {
goto invalid; error = -EINVAL;
goto bad;
}
rcu_read_unlock(); rcu_read_unlock();
if (!sid) if (!sid)
@ -5846,41 +5847,37 @@ static int selinux_getprocattr(struct task_struct *p,
return error; return error;
return len; return len;
invalid: bad:
rcu_read_unlock(); rcu_read_unlock();
return -EINVAL; return error;
} }
static int selinux_setprocattr(struct task_struct *p, static int selinux_setprocattr(const char *name, void *value, size_t size)
char *name, void *value, size_t size)
{ {
struct task_security_struct *tsec; struct task_security_struct *tsec;
struct cred *new; struct cred *new;
u32 sid = 0, ptsid; u32 mysid = current_sid(), sid = 0, ptsid;
int error; int error;
char *str = value; char *str = value;
if (current != p) {
/* SELinux only allows a process to change its own
security attributes. */
return -EACCES;
}
/* /*
* Basic control over ability to set these attributes at all. * Basic control over ability to set these attributes at all.
* current == p, but we'll pass them separately in case the
* above restriction is ever removed.
*/ */
if (!strcmp(name, "exec")) if (!strcmp(name, "exec"))
error = current_has_perm(p, PROCESS__SETEXEC); error = avc_has_perm(mysid, mysid, SECCLASS_PROCESS,
PROCESS__SETEXEC, NULL);
else if (!strcmp(name, "fscreate")) else if (!strcmp(name, "fscreate"))
error = current_has_perm(p, PROCESS__SETFSCREATE); error = avc_has_perm(mysid, mysid, SECCLASS_PROCESS,
PROCESS__SETFSCREATE, NULL);
else if (!strcmp(name, "keycreate")) else if (!strcmp(name, "keycreate"))
error = current_has_perm(p, PROCESS__SETKEYCREATE); error = avc_has_perm(mysid, mysid, SECCLASS_PROCESS,
PROCESS__SETKEYCREATE, NULL);
else if (!strcmp(name, "sockcreate")) else if (!strcmp(name, "sockcreate"))
error = current_has_perm(p, PROCESS__SETSOCKCREATE); error = avc_has_perm(mysid, mysid, SECCLASS_PROCESS,
PROCESS__SETSOCKCREATE, NULL);
else if (!strcmp(name, "current")) else if (!strcmp(name, "current"))
error = current_has_perm(p, PROCESS__SETCURRENT); error = avc_has_perm(mysid, mysid, SECCLASS_PROCESS,
PROCESS__SETCURRENT, NULL);
else else
error = -EINVAL; error = -EINVAL;
if (error) if (error)
@ -5934,7 +5931,8 @@ static int selinux_setprocattr(struct task_struct *p,
} else if (!strcmp(name, "fscreate")) { } else if (!strcmp(name, "fscreate")) {
tsec->create_sid = sid; tsec->create_sid = sid;
} else if (!strcmp(name, "keycreate")) { } else if (!strcmp(name, "keycreate")) {
error = may_create_key(sid, p); error = avc_has_perm(mysid, sid, SECCLASS_KEY, KEY__CREATE,
NULL);
if (error) if (error)
goto abort_change; goto abort_change;
tsec->keycreate_sid = sid; tsec->keycreate_sid = sid;
@ -5961,7 +5959,7 @@ static int selinux_setprocattr(struct task_struct *p,
/* Check for ptracing, and update the task SID if ok. /* Check for ptracing, and update the task SID if ok.
Otherwise, leave SID unchanged and fail. */ Otherwise, leave SID unchanged and fail. */
ptsid = ptrace_parent_sid(p); ptsid = ptrace_parent_sid();
if (ptsid != 0) { if (ptsid != 0) {
error = avc_has_perm(ptsid, sid, SECCLASS_PROCESS, error = avc_has_perm(ptsid, sid, SECCLASS_PROCESS,
PROCESS__PTRACE, NULL); PROCESS__PTRACE, NULL);
@ -6209,7 +6207,6 @@ static struct security_hook_list selinux_hooks[] = {
LSM_HOOK_INIT(task_getscheduler, selinux_task_getscheduler), LSM_HOOK_INIT(task_getscheduler, selinux_task_getscheduler),
LSM_HOOK_INIT(task_movememory, selinux_task_movememory), LSM_HOOK_INIT(task_movememory, selinux_task_movememory),
LSM_HOOK_INIT(task_kill, selinux_task_kill), LSM_HOOK_INIT(task_kill, selinux_task_kill),
LSM_HOOK_INIT(task_wait, selinux_task_wait),
LSM_HOOK_INIT(task_to_inode, selinux_task_to_inode), LSM_HOOK_INIT(task_to_inode, selinux_task_to_inode),
LSM_HOOK_INIT(ipc_permission, selinux_ipc_permission), LSM_HOOK_INIT(ipc_permission, selinux_ipc_permission),
@ -6349,7 +6346,7 @@ static __init int selinux_init(void)
0, SLAB_PANIC, NULL); 0, SLAB_PANIC, NULL);
avc_init(); avc_init();
security_add_hooks(selinux_hooks, ARRAY_SIZE(selinux_hooks)); security_add_hooks(selinux_hooks, ARRAY_SIZE(selinux_hooks), "selinux");
if (avc_add_callback(selinux_netcache_avc_callback, AVC_CALLBACK_RESET)) if (avc_add_callback(selinux_netcache_avc_callback, AVC_CALLBACK_RESET))
panic("SELinux: Unable to register AVC netcache callback\n"); panic("SELinux: Unable to register AVC netcache callback\n");

View File

@ -171,5 +171,67 @@ struct security_class_mapping secclass_map[] = {
{ COMMON_CAP_PERMS, NULL } }, { COMMON_CAP_PERMS, NULL } },
{ "cap2_userns", { "cap2_userns",
{ COMMON_CAP2_PERMS, NULL } }, { COMMON_CAP2_PERMS, NULL } },
{ "sctp_socket",
{ COMMON_SOCK_PERMS,
"node_bind", NULL } },
{ "icmp_socket",
{ COMMON_SOCK_PERMS,
"node_bind", NULL } },
{ "ax25_socket",
{ COMMON_SOCK_PERMS, NULL } },
{ "ipx_socket",
{ COMMON_SOCK_PERMS, NULL } },
{ "netrom_socket",
{ COMMON_SOCK_PERMS, NULL } },
{ "atmpvc_socket",
{ COMMON_SOCK_PERMS, NULL } },
{ "x25_socket",
{ COMMON_SOCK_PERMS, NULL } },
{ "rose_socket",
{ COMMON_SOCK_PERMS, NULL } },
{ "decnet_socket",
{ COMMON_SOCK_PERMS, NULL } },
{ "atmsvc_socket",
{ COMMON_SOCK_PERMS, NULL } },
{ "rds_socket",
{ COMMON_SOCK_PERMS, NULL } },
{ "irda_socket",
{ COMMON_SOCK_PERMS, NULL } },
{ "pppox_socket",
{ COMMON_SOCK_PERMS, NULL } },
{ "llc_socket",
{ COMMON_SOCK_PERMS, NULL } },
{ "can_socket",
{ COMMON_SOCK_PERMS, NULL } },
{ "tipc_socket",
{ COMMON_SOCK_PERMS, NULL } },
{ "bluetooth_socket",
{ COMMON_SOCK_PERMS, NULL } },
{ "iucv_socket",
{ COMMON_SOCK_PERMS, NULL } },
{ "rxrpc_socket",
{ COMMON_SOCK_PERMS, NULL } },
{ "isdn_socket",
{ COMMON_SOCK_PERMS, NULL } },
{ "phonet_socket",
{ COMMON_SOCK_PERMS, NULL } },
{ "ieee802154_socket",
{ COMMON_SOCK_PERMS, NULL } },
{ "caif_socket",
{ COMMON_SOCK_PERMS, NULL } },
{ "alg_socket",
{ COMMON_SOCK_PERMS, NULL } },
{ "nfc_socket",
{ COMMON_SOCK_PERMS, NULL } },
{ "vsock_socket",
{ COMMON_SOCK_PERMS, NULL } },
{ "kcm_socket",
{ COMMON_SOCK_PERMS, NULL } },
{ "qipcrtr_socket",
{ COMMON_SOCK_PERMS, NULL } },
{ NULL } { NULL }
}; };
#if PF_MAX > 43
#error New address family defined, please update secclass_map.
#endif

View File

@ -37,6 +37,16 @@ struct task_security_struct {
u32 sockcreate_sid; /* fscreate SID */ u32 sockcreate_sid; /* fscreate SID */
}; };
/*
* get the subjective security ID of the current task
*/
static inline u32 current_sid(void)
{
const struct task_security_struct *tsec = current_security();
return tsec->sid;
}
enum label_initialized { enum label_initialized {
LABEL_INVALID, /* invalid or not initialized */ LABEL_INVALID, /* invalid or not initialized */
LABEL_INITIALIZED, /* initialized */ LABEL_INITIALIZED, /* initialized */

View File

@ -69,7 +69,7 @@ extern int selinux_enabled;
enum { enum {
POLICYDB_CAPABILITY_NETPEER, POLICYDB_CAPABILITY_NETPEER,
POLICYDB_CAPABILITY_OPENPERM, POLICYDB_CAPABILITY_OPENPERM,
POLICYDB_CAPABILITY_REDHAT1, POLICYDB_CAPABILITY_EXTSOCKCLASS,
POLICYDB_CAPABILITY_ALWAYSNETWORK, POLICYDB_CAPABILITY_ALWAYSNETWORK,
__POLICYDB_CAPABILITY_MAX __POLICYDB_CAPABILITY_MAX
}; };
@ -77,6 +77,7 @@ enum {
extern int selinux_policycap_netpeer; extern int selinux_policycap_netpeer;
extern int selinux_policycap_openperm; extern int selinux_policycap_openperm;
extern int selinux_policycap_extsockclass;
extern int selinux_policycap_alwaysnetwork; extern int selinux_policycap_alwaysnetwork;
/* /*

View File

@ -45,7 +45,7 @@
static char *policycap_names[] = { static char *policycap_names[] = {
"network_peer_controls", "network_peer_controls",
"open_perms", "open_perms",
"redhat1", "extended_socket_class",
"always_check_network" "always_check_network"
}; };
@ -77,25 +77,6 @@ static char policy_opened;
/* global data for policy capabilities */ /* global data for policy capabilities */
static struct dentry *policycap_dir; static struct dentry *policycap_dir;
/* Check whether a task is allowed to use a security operation. */
static int task_has_security(struct task_struct *tsk,
u32 perms)
{
const struct task_security_struct *tsec;
u32 sid = 0;
rcu_read_lock();
tsec = __task_cred(tsk)->security;
if (tsec)
sid = tsec->sid;
rcu_read_unlock();
if (!tsec)
return -EACCES;
return avc_has_perm(sid, SECINITSID_SECURITY,
SECCLASS_SECURITY, perms, NULL);
}
enum sel_inos { enum sel_inos {
SEL_ROOT_INO = 2, SEL_ROOT_INO = 2,
SEL_LOAD, /* load policy */ SEL_LOAD, /* load policy */
@ -166,7 +147,9 @@ static ssize_t sel_write_enforce(struct file *file, const char __user *buf,
new_value = !!new_value; new_value = !!new_value;
if (new_value != selinux_enforcing) { if (new_value != selinux_enforcing) {
length = task_has_security(current, SECURITY__SETENFORCE); length = avc_has_perm(current_sid(), SECINITSID_SECURITY,
SECCLASS_SECURITY, SECURITY__SETENFORCE,
NULL);
if (length) if (length)
goto out; goto out;
audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_STATUS, audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_STATUS,
@ -368,7 +351,8 @@ static int sel_open_policy(struct inode *inode, struct file *filp)
mutex_lock(&sel_mutex); mutex_lock(&sel_mutex);
rc = task_has_security(current, SECURITY__READ_POLICY); rc = avc_has_perm(current_sid(), SECINITSID_SECURITY,
SECCLASS_SECURITY, SECURITY__READ_POLICY, NULL);
if (rc) if (rc)
goto err; goto err;
@ -429,7 +413,8 @@ static ssize_t sel_read_policy(struct file *filp, char __user *buf,
mutex_lock(&sel_mutex); mutex_lock(&sel_mutex);
ret = task_has_security(current, SECURITY__READ_POLICY); ret = avc_has_perm(current_sid(), SECINITSID_SECURITY,
SECCLASS_SECURITY, SECURITY__READ_POLICY, NULL);
if (ret) if (ret)
goto out; goto out;
@ -499,7 +484,8 @@ static ssize_t sel_write_load(struct file *file, const char __user *buf,
mutex_lock(&sel_mutex); mutex_lock(&sel_mutex);
length = task_has_security(current, SECURITY__LOAD_POLICY); length = avc_has_perm(current_sid(), SECINITSID_SECURITY,
SECCLASS_SECURITY, SECURITY__LOAD_POLICY, NULL);
if (length) if (length)
goto out; goto out;
@ -522,20 +508,28 @@ static ssize_t sel_write_load(struct file *file, const char __user *buf,
goto out; goto out;
length = security_load_policy(data, count); length = security_load_policy(data, count);
if (length) if (length) {
pr_warn_ratelimited("SELinux: failed to load policy\n");
goto out; goto out;
}
length = sel_make_bools(); length = sel_make_bools();
if (length) if (length) {
pr_err("SELinux: failed to load policy booleans\n");
goto out1; goto out1;
}
length = sel_make_classes(); length = sel_make_classes();
if (length) if (length) {
pr_err("SELinux: failed to load policy classes\n");
goto out1; goto out1;
}
length = sel_make_policycap(); length = sel_make_policycap();
if (length) if (length) {
pr_err("SELinux: failed to load policy capabilities\n");
goto out1; goto out1;
}
length = count; length = count;
@ -561,7 +555,8 @@ static ssize_t sel_write_context(struct file *file, char *buf, size_t size)
u32 sid, len; u32 sid, len;
ssize_t length; ssize_t length;
length = task_has_security(current, SECURITY__CHECK_CONTEXT); length = avc_has_perm(current_sid(), SECINITSID_SECURITY,
SECCLASS_SECURITY, SECURITY__CHECK_CONTEXT, NULL);
if (length) if (length)
goto out; goto out;
@ -604,7 +599,9 @@ static ssize_t sel_write_checkreqprot(struct file *file, const char __user *buf,
ssize_t length; ssize_t length;
unsigned int new_value; unsigned int new_value;
length = task_has_security(current, SECURITY__SETCHECKREQPROT); length = avc_has_perm(current_sid(), SECINITSID_SECURITY,
SECCLASS_SECURITY, SECURITY__SETCHECKREQPROT,
NULL);
if (length) if (length)
return length; return length;
@ -645,7 +642,8 @@ static ssize_t sel_write_validatetrans(struct file *file,
u16 tclass; u16 tclass;
int rc; int rc;
rc = task_has_security(current, SECURITY__VALIDATE_TRANS); rc = avc_has_perm(current_sid(), SECINITSID_SECURITY,
SECCLASS_SECURITY, SECURITY__VALIDATE_TRANS, NULL);
if (rc) if (rc)
goto out; goto out;
@ -772,7 +770,8 @@ static ssize_t sel_write_access(struct file *file, char *buf, size_t size)
struct av_decision avd; struct av_decision avd;
ssize_t length; ssize_t length;
length = task_has_security(current, SECURITY__COMPUTE_AV); length = avc_has_perm(current_sid(), SECINITSID_SECURITY,
SECCLASS_SECURITY, SECURITY__COMPUTE_AV, NULL);
if (length) if (length)
goto out; goto out;
@ -822,7 +821,9 @@ static ssize_t sel_write_create(struct file *file, char *buf, size_t size)
u32 len; u32 len;
int nargs; int nargs;
length = task_has_security(current, SECURITY__COMPUTE_CREATE); length = avc_has_perm(current_sid(), SECINITSID_SECURITY,
SECCLASS_SECURITY, SECURITY__COMPUTE_CREATE,
NULL);
if (length) if (length)
goto out; goto out;
@ -919,7 +920,9 @@ static ssize_t sel_write_relabel(struct file *file, char *buf, size_t size)
char *newcon = NULL; char *newcon = NULL;
u32 len; u32 len;
length = task_has_security(current, SECURITY__COMPUTE_RELABEL); length = avc_has_perm(current_sid(), SECINITSID_SECURITY,
SECCLASS_SECURITY, SECURITY__COMPUTE_RELABEL,
NULL);
if (length) if (length)
goto out; goto out;
@ -975,7 +978,9 @@ static ssize_t sel_write_user(struct file *file, char *buf, size_t size)
int i, rc; int i, rc;
u32 len, nsids; u32 len, nsids;
length = task_has_security(current, SECURITY__COMPUTE_USER); length = avc_has_perm(current_sid(), SECINITSID_SECURITY,
SECCLASS_SECURITY, SECURITY__COMPUTE_USER,
NULL);
if (length) if (length)
goto out; goto out;
@ -1035,7 +1040,9 @@ static ssize_t sel_write_member(struct file *file, char *buf, size_t size)
char *newcon = NULL; char *newcon = NULL;
u32 len; u32 len;
length = task_has_security(current, SECURITY__COMPUTE_MEMBER); length = avc_has_perm(current_sid(), SECINITSID_SECURITY,
SECCLASS_SECURITY, SECURITY__COMPUTE_MEMBER,
NULL);
if (length) if (length)
goto out; goto out;
@ -1142,7 +1149,9 @@ static ssize_t sel_write_bool(struct file *filep, const char __user *buf,
mutex_lock(&sel_mutex); mutex_lock(&sel_mutex);
length = task_has_security(current, SECURITY__SETBOOL); length = avc_has_perm(current_sid(), SECINITSID_SECURITY,
SECCLASS_SECURITY, SECURITY__SETBOOL,
NULL);
if (length) if (length)
goto out; goto out;
@ -1198,7 +1207,9 @@ static ssize_t sel_commit_bools_write(struct file *filep,
mutex_lock(&sel_mutex); mutex_lock(&sel_mutex);
length = task_has_security(current, SECURITY__SETBOOL); length = avc_has_perm(current_sid(), SECINITSID_SECURITY,
SECCLASS_SECURITY, SECURITY__SETBOOL,
NULL);
if (length) if (length)
goto out; goto out;
@ -1299,8 +1310,11 @@ static int sel_make_bools(void)
isec = (struct inode_security_struct *)inode->i_security; isec = (struct inode_security_struct *)inode->i_security;
ret = security_genfs_sid("selinuxfs", page, SECCLASS_FILE, &sid); ret = security_genfs_sid("selinuxfs", page, SECCLASS_FILE, &sid);
if (ret) if (ret) {
goto out; pr_warn_ratelimited("SELinux: no sid found, defaulting to security isid for %s\n",
page);
sid = SECINITSID_SECURITY;
}
isec->sid = sid; isec->sid = sid;
isec->initialized = LABEL_INITIALIZED; isec->initialized = LABEL_INITIALIZED;
@ -1351,7 +1365,9 @@ static ssize_t sel_write_avc_cache_threshold(struct file *file,
ssize_t ret; ssize_t ret;
unsigned int new_value; unsigned int new_value;
ret = task_has_security(current, SECURITY__SETSECPARAM); ret = avc_has_perm(current_sid(), SECINITSID_SECURITY,
SECCLASS_SECURITY, SECURITY__SETSECPARAM,
NULL);
if (ret) if (ret)
return ret; return ret;

View File

@ -72,6 +72,7 @@
int selinux_policycap_netpeer; int selinux_policycap_netpeer;
int selinux_policycap_openperm; int selinux_policycap_openperm;
int selinux_policycap_extsockclass;
int selinux_policycap_alwaysnetwork; int selinux_policycap_alwaysnetwork;
static DEFINE_RWLOCK(policy_rwlock); static DEFINE_RWLOCK(policy_rwlock);
@ -1988,6 +1989,8 @@ static void security_load_policycaps(void)
POLICYDB_CAPABILITY_NETPEER); POLICYDB_CAPABILITY_NETPEER);
selinux_policycap_openperm = ebitmap_get_bit(&policydb.policycaps, selinux_policycap_openperm = ebitmap_get_bit(&policydb.policycaps,
POLICYDB_CAPABILITY_OPENPERM); POLICYDB_CAPABILITY_OPENPERM);
selinux_policycap_extsockclass = ebitmap_get_bit(&policydb.policycaps,
POLICYDB_CAPABILITY_EXTSOCKCLASS);
selinux_policycap_alwaysnetwork = ebitmap_get_bit(&policydb.policycaps, selinux_policycap_alwaysnetwork = ebitmap_get_bit(&policydb.policycaps,
POLICYDB_CAPABILITY_ALWAYSNETWORK); POLICYDB_CAPABILITY_ALWAYSNETWORK);
} }

View File

@ -114,6 +114,7 @@ struct inode_smack {
struct smack_known *smk_mmap; /* label of the mmap domain */ struct smack_known *smk_mmap; /* label of the mmap domain */
struct mutex smk_lock; /* initialization lock */ struct mutex smk_lock; /* initialization lock */
int smk_flags; /* smack inode flags */ int smk_flags; /* smack inode flags */
struct rcu_head smk_rcu; /* for freeing inode_smack */
}; };
struct task_smack { struct task_smack {
@ -173,6 +174,8 @@ struct smk_port_label {
unsigned short smk_port; /* the port number */ unsigned short smk_port; /* the port number */
struct smack_known *smk_in; /* inbound label */ struct smack_known *smk_in; /* inbound label */
struct smack_known *smk_out; /* outgoing label */ struct smack_known *smk_out; /* outgoing label */
short smk_sock_type; /* Socket type */
short smk_can_reuse;
}; };
#endif /* SMACK_IPV6_PORT_LABELING */ #endif /* SMACK_IPV6_PORT_LABELING */

View File

@ -52,6 +52,7 @@
#define SMK_SENDING 2 #define SMK_SENDING 2
#ifdef SMACK_IPV6_PORT_LABELING #ifdef SMACK_IPV6_PORT_LABELING
DEFINE_MUTEX(smack_ipv6_lock);
static LIST_HEAD(smk_ipv6_port_list); static LIST_HEAD(smk_ipv6_port_list);
#endif #endif
static struct kmem_cache *smack_inode_cache; static struct kmem_cache *smack_inode_cache;
@ -347,8 +348,6 @@ static int smk_copy_rules(struct list_head *nhead, struct list_head *ohead,
struct smack_rule *orp; struct smack_rule *orp;
int rc = 0; int rc = 0;
INIT_LIST_HEAD(nhead);
list_for_each_entry_rcu(orp, ohead, list) { list_for_each_entry_rcu(orp, ohead, list) {
nrp = kzalloc(sizeof(struct smack_rule), gfp); nrp = kzalloc(sizeof(struct smack_rule), gfp);
if (nrp == NULL) { if (nrp == NULL) {
@ -375,8 +374,6 @@ static int smk_copy_relabel(struct list_head *nhead, struct list_head *ohead,
struct smack_known_list_elem *nklep; struct smack_known_list_elem *nklep;
struct smack_known_list_elem *oklep; struct smack_known_list_elem *oklep;
INIT_LIST_HEAD(nhead);
list_for_each_entry(oklep, ohead, list) { list_for_each_entry(oklep, ohead, list) {
nklep = kzalloc(sizeof(struct smack_known_list_elem), gfp); nklep = kzalloc(sizeof(struct smack_known_list_elem), gfp);
if (nklep == NULL) { if (nklep == NULL) {
@ -1009,15 +1006,39 @@ static int smack_inode_alloc_security(struct inode *inode)
} }
/** /**
* smack_inode_free_security - free an inode blob * smack_inode_free_rcu - Free inode_smack blob from cache
* @head: the rcu_head for getting inode_smack pointer
*
* Call back function called from call_rcu() to free
* the i_security blob pointer in inode
*/
static void smack_inode_free_rcu(struct rcu_head *head)
{
struct inode_smack *issp;
issp = container_of(head, struct inode_smack, smk_rcu);
kmem_cache_free(smack_inode_cache, issp);
}
/**
* smack_inode_free_security - free an inode blob using call_rcu()
* @inode: the inode with a blob * @inode: the inode with a blob
* *
* Clears the blob pointer in inode * Clears the blob pointer in inode using RCU
*/ */
static void smack_inode_free_security(struct inode *inode) static void smack_inode_free_security(struct inode *inode)
{ {
kmem_cache_free(smack_inode_cache, inode->i_security); struct inode_smack *issp = inode->i_security;
inode->i_security = NULL;
/*
* The inode may still be referenced in a path walk and
* a call to smack_inode_permission() can be made
* after smack_inode_free_security() is called.
* To avoid race condition free the i_security via RCU
* and leave the current inode->i_security pointer intact.
* The inode will be freed after the RCU grace period too.
*/
call_rcu(&issp->smk_rcu, smack_inode_free_rcu);
} }
/** /**
@ -1626,6 +1647,9 @@ static int smack_file_ioctl(struct file *file, unsigned int cmd,
struct smk_audit_info ad; struct smk_audit_info ad;
struct inode *inode = file_inode(file); struct inode *inode = file_inode(file);
if (unlikely(IS_PRIVATE(inode)))
return 0;
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH);
smk_ad_setfield_u_fs_path(&ad, file->f_path); smk_ad_setfield_u_fs_path(&ad, file->f_path);
@ -1655,6 +1679,9 @@ static int smack_file_lock(struct file *file, unsigned int cmd)
int rc; int rc;
struct inode *inode = file_inode(file); struct inode *inode = file_inode(file);
if (unlikely(IS_PRIVATE(inode)))
return 0;
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH);
smk_ad_setfield_u_fs_path(&ad, file->f_path); smk_ad_setfield_u_fs_path(&ad, file->f_path);
rc = smk_curacc(smk_of_inode(inode), MAY_LOCK, &ad); rc = smk_curacc(smk_of_inode(inode), MAY_LOCK, &ad);
@ -1681,6 +1708,9 @@ static int smack_file_fcntl(struct file *file, unsigned int cmd,
int rc = 0; int rc = 0;
struct inode *inode = file_inode(file); struct inode *inode = file_inode(file);
if (unlikely(IS_PRIVATE(inode)))
return 0;
switch (cmd) { switch (cmd) {
case F_GETLK: case F_GETLK:
break; break;
@ -1734,6 +1764,9 @@ static int smack_mmap_file(struct file *file,
if (file == NULL) if (file == NULL)
return 0; return 0;
if (unlikely(IS_PRIVATE(file_inode(file))))
return 0;
isp = file_inode(file)->i_security; isp = file_inode(file)->i_security;
if (isp->smk_mmap == NULL) if (isp->smk_mmap == NULL)
return 0; return 0;
@ -1934,12 +1967,9 @@ static int smack_file_open(struct file *file, const struct cred *cred)
struct smk_audit_info ad; struct smk_audit_info ad;
int rc; int rc;
if (smack_privileged(CAP_MAC_OVERRIDE))
return 0;
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH);
smk_ad_setfield_u_fs_path(&ad, file->f_path); smk_ad_setfield_u_fs_path(&ad, file->f_path);
rc = smk_access(tsp->smk_task, smk_of_inode(inode), MAY_READ, &ad); rc = smk_tskacc(tsp, smk_of_inode(inode), MAY_READ, &ad);
rc = smk_bu_credfile(cred, file, MAY_READ, rc); rc = smk_bu_credfile(cred, file, MAY_READ, rc);
return rc; return rc;
@ -2271,25 +2301,6 @@ static int smack_task_kill(struct task_struct *p, struct siginfo *info,
return rc; return rc;
} }
/**
* smack_task_wait - Smack access check for waiting
* @p: task to wait for
*
* Returns 0
*/
static int smack_task_wait(struct task_struct *p)
{
/*
* Allow the operation to succeed.
* Zombies are bad.
* In userless environments (e.g. phones) programs
* get marked with SMACK64EXEC and even if the parent
* and child shouldn't be talking the parent still
* may expect to know when the child exits.
*/
return 0;
}
/** /**
* smack_task_to_inode - copy task smack into the inode blob * smack_task_to_inode - copy task smack into the inode blob
* @p: task to copy from * @p: task to copy from
@ -2353,6 +2364,20 @@ static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags)
*/ */
static void smack_sk_free_security(struct sock *sk) static void smack_sk_free_security(struct sock *sk)
{ {
#ifdef SMACK_IPV6_PORT_LABELING
struct smk_port_label *spp;
if (sk->sk_family == PF_INET6) {
rcu_read_lock();
list_for_each_entry_rcu(spp, &smk_ipv6_port_list, list) {
if (spp->smk_sock != sk)
continue;
spp->smk_can_reuse = 1;
break;
}
rcu_read_unlock();
}
#endif
kfree(sk->sk_security); kfree(sk->sk_security);
} }
@ -2603,17 +2628,20 @@ static void smk_ipv6_port_label(struct socket *sock, struct sockaddr *address)
* on the bound socket. Take the changes to the port * on the bound socket. Take the changes to the port
* as well. * as well.
*/ */
list_for_each_entry(spp, &smk_ipv6_port_list, list) { rcu_read_lock();
list_for_each_entry_rcu(spp, &smk_ipv6_port_list, list) {
if (sk != spp->smk_sock) if (sk != spp->smk_sock)
continue; continue;
spp->smk_in = ssp->smk_in; spp->smk_in = ssp->smk_in;
spp->smk_out = ssp->smk_out; spp->smk_out = ssp->smk_out;
rcu_read_unlock();
return; return;
} }
/* /*
* A NULL address is only used for updating existing * A NULL address is only used for updating existing
* bound entries. If there isn't one, it's OK. * bound entries. If there isn't one, it's OK.
*/ */
rcu_read_unlock();
return; return;
} }
@ -2629,16 +2657,23 @@ static void smk_ipv6_port_label(struct socket *sock, struct sockaddr *address)
* Look for an existing port list entry. * Look for an existing port list entry.
* This is an indication that a port is getting reused. * This is an indication that a port is getting reused.
*/ */
list_for_each_entry(spp, &smk_ipv6_port_list, list) { rcu_read_lock();
if (spp->smk_port != port) list_for_each_entry_rcu(spp, &smk_ipv6_port_list, list) {
if (spp->smk_port != port || spp->smk_sock_type != sock->type)
continue; continue;
if (spp->smk_can_reuse != 1) {
rcu_read_unlock();
return;
}
spp->smk_port = port; spp->smk_port = port;
spp->smk_sock = sk; spp->smk_sock = sk;
spp->smk_in = ssp->smk_in; spp->smk_in = ssp->smk_in;
spp->smk_out = ssp->smk_out; spp->smk_out = ssp->smk_out;
spp->smk_can_reuse = 0;
rcu_read_unlock();
return; return;
} }
rcu_read_unlock();
/* /*
* A new port entry is required. * A new port entry is required.
*/ */
@ -2650,8 +2685,12 @@ static void smk_ipv6_port_label(struct socket *sock, struct sockaddr *address)
spp->smk_sock = sk; spp->smk_sock = sk;
spp->smk_in = ssp->smk_in; spp->smk_in = ssp->smk_in;
spp->smk_out = ssp->smk_out; spp->smk_out = ssp->smk_out;
spp->smk_sock_type = sock->type;
spp->smk_can_reuse = 0;
list_add(&spp->list, &smk_ipv6_port_list); mutex_lock(&smack_ipv6_lock);
list_add_rcu(&spp->list, &smk_ipv6_port_list);
mutex_unlock(&smack_ipv6_lock);
return; return;
} }
@ -2702,14 +2741,16 @@ static int smk_ipv6_port_check(struct sock *sk, struct sockaddr_in6 *address,
return 0; return 0;
port = ntohs(address->sin6_port); port = ntohs(address->sin6_port);
list_for_each_entry(spp, &smk_ipv6_port_list, list) { rcu_read_lock();
if (spp->smk_port != port) list_for_each_entry_rcu(spp, &smk_ipv6_port_list, list) {
if (spp->smk_port != port || spp->smk_sock_type != sk->sk_type)
continue; continue;
object = spp->smk_in; object = spp->smk_in;
if (act == SMK_CONNECTING) if (act == SMK_CONNECTING)
ssp->smk_packet = spp->smk_out; ssp->smk_packet = spp->smk_out;
break; break;
} }
rcu_read_unlock();
return smk_ipv6_check(skp, object, address, act); return smk_ipv6_check(skp, object, address, act);
} }
@ -3438,6 +3479,13 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
case PIPEFS_MAGIC: case PIPEFS_MAGIC:
isp->smk_inode = smk_of_current(); isp->smk_inode = smk_of_current();
break; break;
case SOCKFS_MAGIC:
/*
* Socket access is controlled by the socket
* structures associated with the task involved.
*/
isp->smk_inode = &smack_known_star;
break;
default: default:
isp->smk_inode = sbsp->smk_root; isp->smk_inode = sbsp->smk_root;
break; break;
@ -3454,19 +3502,12 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
*/ */
switch (sbp->s_magic) { switch (sbp->s_magic) {
case SMACK_MAGIC: case SMACK_MAGIC:
case PIPEFS_MAGIC:
case SOCKFS_MAGIC:
case CGROUP_SUPER_MAGIC: case CGROUP_SUPER_MAGIC:
/* /*
* Casey says that it's a little embarrassing * Casey says that it's a little embarrassing
* that the smack file system doesn't do * that the smack file system doesn't do
* extended attributes. * extended attributes.
* *
* Casey says pipes are easy (?)
*
* Socket access is controlled by the socket
* structures associated with the task involved.
*
* Cgroupfs is special * Cgroupfs is special
*/ */
final = &smack_known_star; final = &smack_known_star;
@ -3620,7 +3661,6 @@ static int smack_getprocattr(struct task_struct *p, char *name, char **value)
/** /**
* smack_setprocattr - Smack process attribute setting * smack_setprocattr - Smack process attribute setting
* @p: the object task
* @name: the name of the attribute in /proc/.../attr * @name: the name of the attribute in /proc/.../attr
* @value: the value to set * @value: the value to set
* @size: the size of the value * @size: the size of the value
@ -3630,8 +3670,7 @@ static int smack_getprocattr(struct task_struct *p, char *name, char **value)
* *
* Returns the length of the smack label or an error code * Returns the length of the smack label or an error code
*/ */
static int smack_setprocattr(struct task_struct *p, char *name, static int smack_setprocattr(const char *name, void *value, size_t size)
void *value, size_t size)
{ {
struct task_smack *tsp = current_security(); struct task_smack *tsp = current_security();
struct cred *new; struct cred *new;
@ -3639,13 +3678,6 @@ static int smack_setprocattr(struct task_struct *p, char *name,
struct smack_known_list_elem *sklep; struct smack_known_list_elem *sklep;
int rc; int rc;
/*
* Changing another process' Smack value is too dangerous
* and supports no sane use case.
*/
if (p != current)
return -EPERM;
if (!smack_privileged(CAP_MAC_ADMIN) && list_empty(&tsp->smk_relabel)) if (!smack_privileged(CAP_MAC_ADMIN) && list_empty(&tsp->smk_relabel))
return -EPERM; return -EPERM;
@ -3849,7 +3881,7 @@ static struct smack_known *smack_from_secattr(struct netlbl_lsm_secattr *sap,
* ambient value. * ambient value.
*/ */
rcu_read_lock(); rcu_read_lock();
list_for_each_entry(skp, &smack_known_list, list) { list_for_each_entry_rcu(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;
/* /*
@ -4667,7 +4699,6 @@ static struct security_hook_list smack_hooks[] = {
LSM_HOOK_INIT(task_getscheduler, smack_task_getscheduler), LSM_HOOK_INIT(task_getscheduler, smack_task_getscheduler),
LSM_HOOK_INIT(task_movememory, smack_task_movememory), LSM_HOOK_INIT(task_movememory, smack_task_movememory),
LSM_HOOK_INIT(task_kill, smack_task_kill), LSM_HOOK_INIT(task_kill, smack_task_kill),
LSM_HOOK_INIT(task_wait, smack_task_wait),
LSM_HOOK_INIT(task_to_inode, smack_task_to_inode), LSM_HOOK_INIT(task_to_inode, smack_task_to_inode),
LSM_HOOK_INIT(ipc_permission, smack_ipc_permission), LSM_HOOK_INIT(ipc_permission, smack_ipc_permission),
@ -4819,7 +4850,7 @@ static __init int smack_init(void)
/* /*
* Register with LSM * Register with LSM
*/ */
security_add_hooks(smack_hooks, ARRAY_SIZE(smack_hooks)); security_add_hooks(smack_hooks, ARRAY_SIZE(smack_hooks), "smack");
return 0; return 0;
} }

View File

@ -67,6 +67,7 @@ enum smk_inos {
/* /*
* List locks * List locks
*/ */
static DEFINE_MUTEX(smack_master_list_lock);
static DEFINE_MUTEX(smack_cipso_lock); static DEFINE_MUTEX(smack_cipso_lock);
static DEFINE_MUTEX(smack_ambient_lock); static DEFINE_MUTEX(smack_ambient_lock);
static DEFINE_MUTEX(smk_net4addr_lock); static DEFINE_MUTEX(smk_net4addr_lock);
@ -262,12 +263,16 @@ static int smk_set_access(struct smack_parsed_rule *srp,
* it needs to get added for reporting. * it needs to get added for reporting.
*/ */
if (global) { if (global) {
mutex_unlock(rule_lock);
smlp = kzalloc(sizeof(*smlp), GFP_KERNEL); smlp = kzalloc(sizeof(*smlp), GFP_KERNEL);
if (smlp != NULL) { if (smlp != NULL) {
smlp->smk_rule = sp; smlp->smk_rule = sp;
mutex_lock(&smack_master_list_lock);
list_add_rcu(&smlp->list, &smack_rule_list); list_add_rcu(&smlp->list, &smack_rule_list);
mutex_unlock(&smack_master_list_lock);
} else } else
rc = -ENOMEM; rc = -ENOMEM;
return rc;
} }
} }

View File

@ -542,7 +542,7 @@ static int __init tomoyo_init(void)
if (!security_module_enable("tomoyo")) if (!security_module_enable("tomoyo"))
return 0; return 0;
/* register ourselves with the security framework */ /* register ourselves with the security framework */
security_add_hooks(tomoyo_hooks, ARRAY_SIZE(tomoyo_hooks)); security_add_hooks(tomoyo_hooks, ARRAY_SIZE(tomoyo_hooks), "tomoyo");
printk(KERN_INFO "TOMOYO Linux initialized\n"); printk(KERN_INFO "TOMOYO Linux initialized\n");
cred->security = &tomoyo_kernel_domain; cred->security = &tomoyo_kernel_domain;
tomoyo_mm_init(); tomoyo_mm_init();

View File

@ -485,6 +485,6 @@ static inline void yama_init_sysctl(void) { }
void __init yama_add_hooks(void) void __init yama_add_hooks(void)
{ {
pr_info("Yama: becoming mindful.\n"); pr_info("Yama: becoming mindful.\n");
security_add_hooks(yama_hooks, ARRAY_SIZE(yama_hooks)); security_add_hooks(yama_hooks, ARRAY_SIZE(yama_hooks), "yama");
yama_init_sysctl(); yama_init_sysctl();
} }