summaryrefslogtreecommitdiff
path: root/security/integrity
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2016-07-29 17:38:46 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2016-07-29 17:38:46 -0700
commit7a1e8b80fb1e8ead4cec15d1fc494ed290e4d2e9 (patch)
tree55a36d4256f1ae793b5c8e88c0f158737447193f /security/integrity
parenta867d7349e94b6409b08629886a819f802377e91 (diff)
parent7616ac70d1bb4f2e9d25c1a82d283f3368a7b632 (diff)
downloadlinux-7a1e8b80fb1e8ead4cec15d1fc494ed290e4d2e9.tar.gz
Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security
Pull security subsystem updates from James Morris: "Highlights: - TPM core and driver updates/fixes - IPv6 security labeling (CALIPSO) - Lots of Apparmor fixes - Seccomp: remove 2-phase API, close hole where ptrace can change syscall #" * 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security: (156 commits) apparmor: fix SECURITY_APPARMOR_HASH_DEFAULT parameter handling tpm: Add TPM 2.0 support to the Nuvoton i2c driver (NPCT6xx family) tpm: Factor out common startup code tpm: use devm_add_action_or_reset tpm2_i2c_nuvoton: add irq validity check tpm: read burstcount from TPM_STS in one 32-bit transaction tpm: fix byte-order for the value read by tpm2_get_tpm_pt tpm_tis_core: convert max timeouts from msec to jiffies apparmor: fix arg_size computation for when setprocattr is null terminated apparmor: fix oops, validate buffer size in apparmor_setprocattr() apparmor: do not expose kernel stack apparmor: fix module parameters can be changed after policy is locked apparmor: fix oops in profile_unpack() when policy_db is not present apparmor: don't check for vmalloc_addr if kvzalloc() failed apparmor: add missing id bounds check on dfa verification apparmor: allow SYS_CAP_RESOURCE to be sufficient to prlimit another task apparmor: use list_next_entry instead of list_entry_next apparmor: fix refcount race when finding a child profile apparmor: fix ref count leak when profile sha1 hash is read apparmor: check that xindex is in trans_table bounds ...
Diffstat (limited to 'security/integrity')
-rw-r--r--security/integrity/iint.c2
-rw-r--r--security/integrity/ima/ima.h11
-rw-r--r--security/integrity/ima/ima_api.c21
-rw-r--r--security/integrity/ima/ima_appraise.c3
-rw-r--r--security/integrity/ima/ima_fs.c9
-rw-r--r--security/integrity/ima/ima_init.c3
-rw-r--r--security/integrity/ima/ima_main.c12
-rw-r--r--security/integrity/ima/ima_policy.c35
-rw-r--r--security/integrity/ima/ima_queue.c13
-rw-r--r--security/integrity/integrity.h1
10 files changed, 80 insertions, 30 deletions
diff --git a/security/integrity/iint.c b/security/integrity/iint.c
index 345b75997e4c..c710d22042f9 100644
--- a/security/integrity/iint.c
+++ b/security/integrity/iint.c
@@ -79,6 +79,7 @@ static void iint_free(struct integrity_iint_cache *iint)
iint->ima_bprm_status = INTEGRITY_UNKNOWN;
iint->ima_read_status = INTEGRITY_UNKNOWN;
iint->evm_status = INTEGRITY_UNKNOWN;
+ iint->measured_pcrs = 0;
kmem_cache_free(iint_cache, iint);
}
@@ -159,6 +160,7 @@ static void init_once(void *foo)
iint->ima_bprm_status = INTEGRITY_UNKNOWN;
iint->ima_read_status = INTEGRITY_UNKNOWN;
iint->evm_status = INTEGRITY_UNKNOWN;
+ iint->measured_pcrs = 0;
}
static int __init integrity_iintcache_init(void)
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index d3a939bf2781..db25f54a04fe 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -88,6 +88,7 @@ struct ima_template_desc {
};
struct ima_template_entry {
+ int pcr;
u8 digest[TPM_DIGEST_SIZE]; /* sha1 or md5 measurement hash */
struct ima_template_desc *template_desc; /* template descriptor */
u32 template_data_len;
@@ -154,7 +155,8 @@ enum ima_hooks {
};
/* LIM API function definitions */
-int ima_get_action(struct inode *inode, int mask, enum ima_hooks func);
+int ima_get_action(struct inode *inode, int mask,
+ enum ima_hooks func, int *pcr);
int ima_must_measure(struct inode *inode, int mask, enum ima_hooks func);
int ima_collect_measurement(struct integrity_iint_cache *iint,
struct file *file, void *buf, loff_t size,
@@ -162,19 +164,20 @@ int ima_collect_measurement(struct integrity_iint_cache *iint,
void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file,
const unsigned char *filename,
struct evm_ima_xattr_data *xattr_value,
- int xattr_len);
+ int xattr_len, int pcr);
void ima_audit_measurement(struct integrity_iint_cache *iint,
const unsigned char *filename);
int ima_alloc_init_template(struct ima_event_data *event_data,
struct ima_template_entry **entry);
int ima_store_template(struct ima_template_entry *entry, int violation,
- struct inode *inode, const unsigned char *filename);
+ struct inode *inode,
+ const unsigned char *filename, int pcr);
void ima_free_template_entry(struct ima_template_entry *entry);
const char *ima_d_path(const struct path *path, char **pathbuf);
/* IMA policy related functions */
int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
- int flags);
+ int flags, int *pcr);
void ima_init_policy(void);
void ima_update_policy(void);
void ima_update_policy_flag(void);
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index 5a2218fe877a..9df26a2b75ba 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -87,7 +87,7 @@ out:
*/
int ima_store_template(struct ima_template_entry *entry,
int violation, struct inode *inode,
- const unsigned char *filename)
+ const unsigned char *filename, int pcr)
{
static const char op[] = "add_template_measure";
static const char audit_cause[] = "hashing_error";
@@ -114,6 +114,7 @@ int ima_store_template(struct ima_template_entry *entry,
}
memcpy(entry->digest, hash.hdr.digest, hash.hdr.length);
}
+ entry->pcr = pcr;
result = ima_add_template_entry(entry, violation, op, inode, filename);
return result;
}
@@ -144,7 +145,8 @@ void ima_add_violation(struct file *file, const unsigned char *filename,
result = -ENOMEM;
goto err_out;
}
- result = ima_store_template(entry, violation, inode, filename);
+ result = ima_store_template(entry, violation, inode,
+ filename, CONFIG_IMA_MEASURE_PCR_IDX);
if (result < 0)
ima_free_template_entry(entry);
err_out:
@@ -157,6 +159,7 @@ err_out:
* @inode: pointer to inode to measure
* @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXECUTE)
* @func: caller identifier
+ * @pcr: pointer filled in if matched measure policy sets pcr=
*
* The policy is defined in terms of keypairs:
* subj=, obj=, type=, func=, mask=, fsmagic=
@@ -168,13 +171,13 @@ err_out:
* Returns IMA_MEASURE, IMA_APPRAISE mask.
*
*/
-int ima_get_action(struct inode *inode, int mask, enum ima_hooks func)
+int ima_get_action(struct inode *inode, int mask, enum ima_hooks func, int *pcr)
{
int flags = IMA_MEASURE | IMA_AUDIT | IMA_APPRAISE;
flags &= ima_policy_flag;
- return ima_match_policy(inode, func, mask, flags);
+ return ima_match_policy(inode, func, mask, flags, pcr);
}
/*
@@ -252,7 +255,7 @@ out:
void ima_store_measurement(struct integrity_iint_cache *iint,
struct file *file, const unsigned char *filename,
struct evm_ima_xattr_data *xattr_value,
- int xattr_len)
+ int xattr_len, int pcr)
{
static const char op[] = "add_template_measure";
static const char audit_cause[] = "ENOMEM";
@@ -263,7 +266,7 @@ void ima_store_measurement(struct integrity_iint_cache *iint,
xattr_len, NULL};
int violation = 0;
- if (iint->flags & IMA_MEASURED)
+ if (iint->measured_pcrs & (0x1 << pcr))
return;
result = ima_alloc_init_template(&event_data, &entry);
@@ -273,9 +276,11 @@ void ima_store_measurement(struct integrity_iint_cache *iint,
return;
}
- result = ima_store_template(entry, violation, inode, filename);
- if (!result || result == -EEXIST)
+ result = ima_store_template(entry, violation, inode, filename, pcr);
+ if (!result || result == -EEXIST) {
iint->flags |= IMA_MEASURED;
+ iint->measured_pcrs |= (0x1 << pcr);
+ }
if (result < 0)
ima_free_template_entry(entry);
}
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index 1bcbc12e03d9..4b9b4a4e1b89 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -41,7 +41,7 @@ int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func)
if (!ima_appraise)
return 0;
- return ima_match_policy(inode, func, mask, IMA_APPRAISE);
+ return ima_match_policy(inode, func, mask, IMA_APPRAISE, NULL);
}
static int ima_fix_xattr(struct dentry *dentry,
@@ -370,6 +370,7 @@ static void ima_reset_appraise_flags(struct inode *inode, int digsig)
return;
iint->flags &= ~IMA_DONE_MASK;
+ iint->measured_pcrs = 0;
if (digsig)
iint->flags |= IMA_DIGSIG;
return;
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 60d011aaec38..c07a3844ea0a 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -123,7 +123,6 @@ static int ima_measurements_show(struct seq_file *m, void *v)
struct ima_template_entry *e;
char *template_name;
int namelen;
- u32 pcr = CONFIG_IMA_MEASURE_PCR_IDX;
bool is_ima_template = false;
int i;
@@ -137,10 +136,10 @@ static int ima_measurements_show(struct seq_file *m, void *v)
/*
* 1st: PCRIndex
- * PCR used is always the same (config option) in
- * little-endian format
+ * PCR used defaults to the same (config option) in
+ * little-endian format, unless set in policy
*/
- ima_putc(m, &pcr, sizeof(pcr));
+ ima_putc(m, &e->pcr, sizeof(e->pcr));
/* 2nd: template digest */
ima_putc(m, e->digest, TPM_DIGEST_SIZE);
@@ -219,7 +218,7 @@ static int ima_ascii_measurements_show(struct seq_file *m, void *v)
e->template_desc->name : e->template_desc->fmt;
/* 1st: PCR used (config option) */
- seq_printf(m, "%2d ", CONFIG_IMA_MEASURE_PCR_IDX);
+ seq_printf(m, "%2d ", e->pcr);
/* 2nd: SHA1 template hash */
ima_print_digest(m, e->digest, TPM_DIGEST_SIZE);
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index 5d679a685616..32912bd54ead 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -79,7 +79,8 @@ static int __init ima_add_boot_aggregate(void)
}
result = ima_store_template(entry, violation, NULL,
- boot_aggregate_name);
+ boot_aggregate_name,
+ CONFIG_IMA_MEASURE_PCR_IDX);
if (result < 0) {
ima_free_template_entry(entry);
audit_cause = "store_entry";
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 68b26c340acd..596ef616ac21 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -125,6 +125,7 @@ static void ima_check_last_writer(struct integrity_iint_cache *iint,
if ((iint->version != inode->i_version) ||
(iint->flags & IMA_NEW_FILE)) {
iint->flags &= ~(IMA_DONE_MASK | IMA_NEW_FILE);
+ iint->measured_pcrs = 0;
if (iint->flags & IMA_APPRAISE)
ima_update_xattr(iint, file);
}
@@ -162,6 +163,7 @@ static int process_measurement(struct file *file, char *buf, loff_t size,
char *pathbuf = NULL;
const char *pathname = NULL;
int rc = -ENOMEM, action, must_appraise;
+ int pcr = CONFIG_IMA_MEASURE_PCR_IDX;
struct evm_ima_xattr_data *xattr_value = NULL;
int xattr_len = 0;
bool violation_check;
@@ -174,7 +176,7 @@ static int process_measurement(struct file *file, char *buf, loff_t size,
* bitmask based on the appraise/audit/measurement policy.
* Included is the appraise submask.
*/
- action = ima_get_action(inode, mask, func);
+ action = ima_get_action(inode, mask, func, &pcr);
violation_check = ((func == FILE_CHECK || func == MMAP_CHECK) &&
(ima_policy_flag & IMA_MEASURE));
if (!action && !violation_check)
@@ -209,7 +211,11 @@ static int process_measurement(struct file *file, char *buf, loff_t size,
*/
iint->flags |= action;
action &= IMA_DO_MASK;
- action &= ~((iint->flags & IMA_DONE_MASK) >> 1);
+ action &= ~((iint->flags & (IMA_DONE_MASK ^ IMA_MEASURED)) >> 1);
+
+ /* If target pcr is already measured, unset IMA_MEASURE action */
+ if ((action & IMA_MEASURE) && (iint->measured_pcrs & (0x1 << pcr)))
+ action ^= IMA_MEASURE;
/* Nothing to do, just return existing appraised status */
if (!action) {
@@ -238,7 +244,7 @@ static int process_measurement(struct file *file, char *buf, loff_t size,
if (action & IMA_MEASURE)
ima_store_measurement(iint, file, pathname,
- xattr_value, xattr_len);
+ xattr_value, xattr_len, pcr);
if (action & IMA_APPRAISE_SUBMASK)
rc = ima_appraise_measurement(func, iint, file, pathname,
xattr_value, xattr_len, opened);
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index 0f887a564a29..aed47b777a57 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -32,6 +32,7 @@
#define IMA_FSUUID 0x0020
#define IMA_INMASK 0x0040
#define IMA_EUID 0x0080
+#define IMA_PCR 0x0100
#define UNKNOWN 0
#define MEASURE 0x0001 /* same as IMA_MEASURE */
@@ -40,6 +41,9 @@
#define DONT_APPRAISE 0x0008
#define AUDIT 0x0040
+#define INVALID_PCR(a) (((a) < 0) || \
+ (a) >= (FIELD_SIZEOF(struct integrity_iint_cache, measured_pcrs) * 8))
+
int ima_policy_flag;
static int temp_ima_appraise;
@@ -60,6 +64,7 @@ struct ima_rule_entry {
u8 fsuuid[16];
kuid_t uid;
kuid_t fowner;
+ int pcr;
struct {
void *rule; /* LSM file metadata specific */
void *args_p; /* audit value */
@@ -319,6 +324,7 @@ static int get_subaction(struct ima_rule_entry *rule, enum ima_hooks func)
* @inode: pointer to an inode for which the policy decision is being made
* @func: IMA hook identifier
* @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC)
+ * @pcr: set the pcr to extend
*
* Measure decision based on func/mask/fsmagic and LSM(subj/obj/type)
* conditions.
@@ -328,7 +334,7 @@ static int get_subaction(struct ima_rule_entry *rule, enum ima_hooks func)
* than writes so ima_match_policy() is classical RCU candidate.
*/
int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
- int flags)
+ int flags, int *pcr)
{
struct ima_rule_entry *entry;
int action = 0, actmask = flags | (flags << 1);
@@ -353,6 +359,9 @@ int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
else
actmask &= ~(entry->action | entry->action >> 1);
+ if ((pcr) && (entry->flags & IMA_PCR))
+ *pcr = entry->pcr;
+
if (!actmask)
break;
}
@@ -478,7 +487,8 @@ enum {
Opt_subj_user, Opt_subj_role, Opt_subj_type,
Opt_func, Opt_mask, Opt_fsmagic,
Opt_fsuuid, Opt_uid, Opt_euid, Opt_fowner,
- Opt_appraise_type, Opt_permit_directio
+ Opt_appraise_type, Opt_permit_directio,
+ Opt_pcr
};
static match_table_t policy_tokens = {
@@ -502,6 +512,7 @@ static match_table_t policy_tokens = {
{Opt_fowner, "fowner=%s"},
{Opt_appraise_type, "appraise_type=%s"},
{Opt_permit_directio, "permit_directio"},
+ {Opt_pcr, "pcr=%s"},
{Opt_err, NULL}
};
@@ -774,6 +785,20 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
case Opt_permit_directio:
entry->flags |= IMA_PERMIT_DIRECTIO;
break;
+ case Opt_pcr:
+ if (entry->action != MEASURE) {
+ result = -EINVAL;
+ break;
+ }
+ ima_log_string(ab, "pcr", args[0].from);
+
+ result = kstrtoint(args[0].from, 10, &entry->pcr);
+ if (result || INVALID_PCR(entry->pcr))
+ result = -EINVAL;
+ else
+ entry->flags |= IMA_PCR;
+
+ break;
case Opt_err:
ima_log_string(ab, "UNKNOWN", p);
result = -EINVAL;
@@ -1011,6 +1036,12 @@ int ima_policy_show(struct seq_file *m, void *v)
seq_puts(m, " ");
}
+ if (entry->flags & IMA_PCR) {
+ snprintf(tbuf, sizeof(tbuf), "%d", entry->pcr);
+ seq_printf(m, pt(Opt_pcr), tbuf);
+ seq_puts(m, " ");
+ }
+
if (entry->flags & IMA_FSUUID) {
seq_printf(m, "fsuuid=%pU", entry->fsuuid);
seq_puts(m, " ");
diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c
index 552705d5a78d..32f6ac0f96df 100644
--- a/security/integrity/ima/ima_queue.c
+++ b/security/integrity/ima/ima_queue.c
@@ -44,7 +44,8 @@ struct ima_h_table ima_htable = {
static DEFINE_MUTEX(ima_extend_list_mutex);
/* lookup up the digest value in the hash table, and return the entry */
-static struct ima_queue_entry *ima_lookup_digest_entry(u8 *digest_value)
+static struct ima_queue_entry *ima_lookup_digest_entry(u8 *digest_value,
+ int pcr)
{
struct ima_queue_entry *qe, *ret = NULL;
unsigned int key;
@@ -54,7 +55,7 @@ static struct ima_queue_entry *ima_lookup_digest_entry(u8 *digest_value)
rcu_read_lock();
hlist_for_each_entry_rcu(qe, &ima_htable.queue[key], hnext) {
rc = memcmp(qe->entry->digest, digest_value, TPM_DIGEST_SIZE);
- if (rc == 0) {
+ if ((rc == 0) && (qe->entry->pcr == pcr)) {
ret = qe;
break;
}
@@ -89,14 +90,14 @@ static int ima_add_digest_entry(struct ima_template_entry *entry)
return 0;
}
-static int ima_pcr_extend(const u8 *hash)
+static int ima_pcr_extend(const u8 *hash, int pcr)
{
int result = 0;
if (!ima_used_chip)
return result;
- result = tpm_pcr_extend(TPM_ANY_NUM, CONFIG_IMA_MEASURE_PCR_IDX, hash);
+ result = tpm_pcr_extend(TPM_ANY_NUM, pcr, hash);
if (result != 0)
pr_err("Error Communicating to TPM chip, result: %d\n", result);
return result;
@@ -118,7 +119,7 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation,
mutex_lock(&ima_extend_list_mutex);
if (!violation) {
memcpy(digest, entry->digest, sizeof(digest));
- if (ima_lookup_digest_entry(digest)) {
+ if (ima_lookup_digest_entry(digest, entry->pcr)) {
audit_cause = "hash_exists";
result = -EEXIST;
goto out;
@@ -135,7 +136,7 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation,
if (violation) /* invalidate pcr */
memset(digest, 0xff, sizeof(digest));
- tpmresult = ima_pcr_extend(digest);
+ tpmresult = ima_pcr_extend(digest, entry->pcr);
if (tpmresult != 0) {
snprintf(tpm_audit_cause, AUDIT_CAUSE_LEN_MAX, "TPM_error(%d)",
tpmresult);
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
index 90bc57d796ec..24520b4ef3b0 100644
--- a/security/integrity/integrity.h
+++ b/security/integrity/integrity.h
@@ -103,6 +103,7 @@ struct integrity_iint_cache {
struct inode *inode; /* back pointer to inode in question */
u64 version; /* track inode changes */
unsigned long flags;
+ unsigned long measured_pcrs;
enum integrity_status ima_file_status:4;
enum integrity_status ima_mmap_status:4;
enum integrity_status ima_bprm_status:4;