You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
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
...
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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}
|
||||
};
|
||||
|
||||
@@ -773,6 +784,20 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
|
||||
break;
|
||||
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);
|
||||
@@ -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, " ");
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user