Commit d906c10d authored by Matthew Garrett's avatar Matthew Garrett Committed by Mimi Zohar

IMA: Support using new creds in appraisal policy

The existing BPRM_CHECK functionality in IMA validates against the
credentials of the existing process, not any new credentials that the
child process may transition to. Add an additional CREDS_CHECK target
and refactor IMA to pass the appropriate creds structure. In
ima_bprm_check(), check with both the existing process credentials and
the credentials that will be committed when the new process is started.
This will not change behaviour unless the system policy is extended to
include CREDS_CHECK targets - BPRM_CHECK will continue to check the same
credentials that it did previously.

After this patch, an IMA policy rule along the lines of:

measure func=CREDS_CHECK subj_type=unconfined_t

will trigger if a process is executed and runs as unconfined_t, ignoring
the context of the parent process. This is in contrast to:

measure func=BPRM_CHECK subj_type=unconfined_t

which will trigger if the process that calls exec() is already executing
in unconfined_t, ignoring the context that the child process executes
into.
Signed-off-by: default avatarMatthew Garrett <mjg59@google.com>
Signed-off-by: default avatarMimi Zohar <zohar@linux.vnet.ibm.com>

Changelog:
- initialize ima_creds_status
parent 3ec30113
......@@ -26,7 +26,7 @@ Description:
[obj_user=] [obj_role=] [obj_type=]]
option: [[appraise_type=]] [permit_directio]
base: func:= [BPRM_CHECK][MMAP_CHECK][FILE_CHECK][MODULE_CHECK]
base: func:= [BPRM_CHECK][MMAP_CHECK][CREDS_CHECK][FILE_CHECK][MODULE_CHECK]
[FIRMWARE_CHECK]
[KEXEC_KERNEL_CHECK] [KEXEC_INITRAMFS_CHECK]
mask:= [[^]MAY_READ] [[^]MAY_WRITE] [[^]MAY_APPEND]
......
......@@ -79,6 +79,7 @@ static void iint_free(struct integrity_iint_cache *iint)
iint->ima_mmap_status = INTEGRITY_UNKNOWN;
iint->ima_bprm_status = INTEGRITY_UNKNOWN;
iint->ima_read_status = INTEGRITY_UNKNOWN;
iint->ima_creds_status = INTEGRITY_UNKNOWN;
iint->evm_status = INTEGRITY_UNKNOWN;
iint->measured_pcrs = 0;
kmem_cache_free(iint_cache, iint);
......@@ -158,6 +159,7 @@ static void init_once(void *foo)
iint->ima_mmap_status = INTEGRITY_UNKNOWN;
iint->ima_bprm_status = INTEGRITY_UNKNOWN;
iint->ima_read_status = INTEGRITY_UNKNOWN;
iint->ima_creds_status = INTEGRITY_UNKNOWN;
iint->evm_status = INTEGRITY_UNKNOWN;
mutex_init(&iint->mutex);
}
......
......@@ -177,6 +177,7 @@ static inline unsigned long ima_hash_key(u8 *digest)
hook(FILE_CHECK) \
hook(MMAP_CHECK) \
hook(BPRM_CHECK) \
hook(CREDS_CHECK) \
hook(POST_SETATTR) \
hook(MODULE_CHECK) \
hook(FIRMWARE_CHECK) \
......@@ -191,8 +192,8 @@ enum ima_hooks {
};
/* LIM API function definitions */
int ima_get_action(struct inode *inode, int mask,
enum ima_hooks func, int *pcr);
int ima_get_action(struct inode *inode, const struct cred *cred, u32 secid,
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,
......@@ -212,8 +213,8 @@ void ima_free_template_entry(struct ima_template_entry *entry);
const char *ima_d_path(const struct path *path, char **pathbuf, char *filename);
/* IMA policy related functions */
int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
int flags, int *pcr);
int ima_match_policy(struct inode *inode, const struct cred *cred, u32 secid,
enum ima_hooks func, int mask, int flags, int *pcr);
void ima_init_policy(void);
void ima_update_policy(void);
void ima_update_policy_flag(void);
......
......@@ -158,6 +158,8 @@ void ima_add_violation(struct file *file, const unsigned char *filename,
/**
* ima_get_action - appraise & measure decision based on policy.
* @inode: pointer to inode to measure
* @cred: pointer to credentials structure to validate
* @secid: secid of the task being validated
* @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXEC,
* MAY_APPEND)
* @func: caller identifier
......@@ -166,20 +168,21 @@ void ima_add_violation(struct file *file, const unsigned char *filename,
* The policy is defined in terms of keypairs:
* subj=, obj=, type=, func=, mask=, fsmagic=
* subj,obj, and type: are LSM specific.
* func: FILE_CHECK | BPRM_CHECK | MMAP_CHECK | MODULE_CHECK
* func: FILE_CHECK | BPRM_CHECK | CREDS_CHECK | MMAP_CHECK | MODULE_CHECK
* mask: contains the permission mask
* fsmagic: hex value
*
* Returns IMA_MEASURE, IMA_APPRAISE mask.
*
*/
int ima_get_action(struct inode *inode, int mask, enum ima_hooks func, int *pcr)
int ima_get_action(struct inode *inode, const struct cred *cred, u32 secid,
int mask, enum ima_hooks func, int *pcr)
{
int flags = IMA_MEASURE | IMA_AUDIT | IMA_APPRAISE | IMA_HASH;
flags &= ima_policy_flag;
return ima_match_policy(inode, func, mask, flags, pcr);
return ima_match_policy(inode, cred, secid, func, mask, flags, pcr);
}
/*
......
......@@ -50,11 +50,14 @@ bool is_ima_appraise_enabled(void)
*/
int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func)
{
u32 secid;
if (!ima_appraise)
return 0;
return ima_match_policy(inode, func, mask, IMA_APPRAISE | IMA_HASH,
NULL);
security_task_getsecid(current, &secid);
return ima_match_policy(inode, current_cred(), secid, func, mask,
IMA_APPRAISE | IMA_HASH, NULL);
}
static int ima_fix_xattr(struct dentry *dentry,
......@@ -87,6 +90,8 @@ enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint,
return iint->ima_mmap_status;
case BPRM_CHECK:
return iint->ima_bprm_status;
case CREDS_CHECK:
return iint->ima_creds_status;
case FILE_CHECK:
case POST_SETATTR:
return iint->ima_file_status;
......@@ -107,6 +112,8 @@ static void ima_set_cache_status(struct integrity_iint_cache *iint,
case BPRM_CHECK:
iint->ima_bprm_status = status;
break;
case CREDS_CHECK:
iint->ima_creds_status = status;
case FILE_CHECK:
case POST_SETATTR:
iint->ima_file_status = status;
......@@ -128,6 +135,9 @@ static void ima_cache_flags(struct integrity_iint_cache *iint,
case BPRM_CHECK:
iint->flags |= (IMA_BPRM_APPRAISED | IMA_APPRAISED);
break;
case CREDS_CHECK:
iint->flags |= (IMA_CREDS_APPRAISED | IMA_APPRAISED);
break;
case FILE_CHECK:
case POST_SETATTR:
iint->flags |= (IMA_FILE_APPRAISED | IMA_APPRAISED);
......
......@@ -167,8 +167,9 @@ void ima_file_free(struct file *file)
ima_check_last_writer(iint, inode, file);
}
static int process_measurement(struct file *file, char *buf, loff_t size,
int mask, enum ima_hooks func, int opened)
static int process_measurement(struct file *file, const struct cred *cred,
u32 secid, char *buf, loff_t size, int mask,
enum ima_hooks func, int opened)
{
struct inode *inode = file_inode(file);
struct integrity_iint_cache *iint = NULL;
......@@ -190,7 +191,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, &pcr);
action = ima_get_action(inode, cred, secid, mask, func, &pcr);
violation_check = ((func == FILE_CHECK || func == MMAP_CHECK) &&
(ima_policy_flag & IMA_MEASURE));
if (!action && !violation_check)
......@@ -324,9 +325,14 @@ static int process_measurement(struct file *file, char *buf, loff_t size,
*/
int ima_file_mmap(struct file *file, unsigned long prot)
{
if (file && (prot & PROT_EXEC))
return process_measurement(file, NULL, 0, MAY_EXEC,
MMAP_CHECK, 0);
u32 secid;
if (file && (prot & PROT_EXEC)) {
security_task_getsecid(current, &secid);
return process_measurement(file, current_cred(), secid, NULL,
0, MAY_EXEC, MMAP_CHECK, 0);
}
return 0;
}
......@@ -345,8 +351,18 @@ int ima_file_mmap(struct file *file, unsigned long prot)
*/
int ima_bprm_check(struct linux_binprm *bprm)
{
return process_measurement(bprm->file, NULL, 0, MAY_EXEC,
BPRM_CHECK, 0);
int ret;
u32 secid;
security_task_getsecid(current, &secid);
ret = process_measurement(bprm->file, current_cred(), secid, NULL, 0,
MAY_EXEC, BPRM_CHECK, 0);
if (ret)
return ret;
security_cred_getsecid(bprm->cred, &secid);
return process_measurement(bprm->file, bprm->cred, secid, NULL, 0,
MAY_EXEC, CREDS_CHECK, 0);
}
/**
......@@ -361,7 +377,10 @@ int ima_bprm_check(struct linux_binprm *bprm)
*/
int ima_file_check(struct file *file, int mask, int opened)
{
return process_measurement(file, NULL, 0,
u32 secid;
security_task_getsecid(current, &secid);
return process_measurement(file, current_cred(), secid, NULL, 0,
mask & (MAY_READ | MAY_WRITE | MAY_EXEC |
MAY_APPEND), FILE_CHECK, opened);
}
......@@ -440,6 +459,7 @@ int ima_post_read_file(struct file *file, void *buf, loff_t size,
enum kernel_read_file_id read_id)
{
enum ima_hooks func;
u32 secid;
if (!file && read_id == READING_FIRMWARE) {
if ((ima_appraise & IMA_APPRAISE_FIRMWARE) &&
......@@ -462,7 +482,9 @@ int ima_post_read_file(struct file *file, void *buf, loff_t size,
}
func = read_idmap[read_id] ?: FILE_CHECK;
return process_measurement(file, buf, size, MAY_READ, func, 0);
security_task_getsecid(current, &secid);
return process_measurement(file, current_cred(), secid, buf, size,
MAY_READ, func, 0);
}
static int __init init_ima(void)
......
......@@ -243,16 +243,17 @@ static void ima_lsm_update_rules(void)
* ima_match_rules - determine whether an inode matches the measure rule.
* @rule: a pointer to a rule
* @inode: a pointer to an inode
* @cred: a pointer to a credentials structure for user validation
* @secid: the secid of the task to be validated
* @func: LIM hook identifier
* @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC)
*
* Returns true on rule match, false on failure.
*/
static bool ima_match_rules(struct ima_rule_entry *rule, struct inode *inode,
const struct cred *cred, u32 secid,
enum ima_hooks func, int mask)
{
struct task_struct *tsk = current;
const struct cred *cred = current_cred();
int i;
if ((rule->flags & IMA_FUNC) &&
......@@ -287,7 +288,7 @@ static bool ima_match_rules(struct ima_rule_entry *rule, struct inode *inode,
return false;
for (i = 0; i < MAX_LSM_RULES; i++) {
int rc = 0;
u32 osid, sid;
u32 osid;
int retried = 0;
if (!rule->lsm[i].rule)
......@@ -307,8 +308,7 @@ static bool ima_match_rules(struct ima_rule_entry *rule, struct inode *inode,
case LSM_SUBJ_USER:
case LSM_SUBJ_ROLE:
case LSM_SUBJ_TYPE:
security_task_getsecid(tsk, &sid);
rc = security_filter_rule_match(sid,
rc = security_filter_rule_match(secid,
rule->lsm[i].type,
Audit_equal,
rule->lsm[i].rule,
......@@ -341,6 +341,8 @@ static int get_subaction(struct ima_rule_entry *rule, enum ima_hooks func)
return IMA_MMAP_APPRAISE;
case BPRM_CHECK:
return IMA_BPRM_APPRAISE;
case CREDS_CHECK:
return IMA_CREDS_APPRAISE;
case FILE_CHECK:
case POST_SETATTR:
return IMA_FILE_APPRAISE;
......@@ -353,6 +355,9 @@ static int get_subaction(struct ima_rule_entry *rule, enum ima_hooks func)
/**
* ima_match_policy - decision based on LSM and other conditions
* @inode: pointer to an inode for which the policy decision is being made
* @cred: pointer to a credentials structure for which the policy decision is
* being made
* @secid: LSM secid of the task to be validated
* @func: IMA hook identifier
* @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC)
* @pcr: set the pcr to extend
......@@ -364,8 +369,8 @@ static int get_subaction(struct ima_rule_entry *rule, enum ima_hooks func)
* list when walking it. Reads are many orders of magnitude more numerous
* 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 *pcr)
int ima_match_policy(struct inode *inode, const struct cred *cred, u32 secid,
enum ima_hooks func, int mask, int flags, int *pcr)
{
struct ima_rule_entry *entry;
int action = 0, actmask = flags | (flags << 1);
......@@ -376,7 +381,7 @@ int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
if (!(entry->action & actmask))
continue;
if (!ima_match_rules(entry, inode, func, mask))
if (!ima_match_rules(entry, inode, cred, secid, func, mask))
continue;
action |= entry->flags & IMA_ACTION_FLAGS;
......@@ -713,6 +718,8 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
entry->func = MMAP_CHECK;
else if (strcmp(args[0].from, "BPRM_CHECK") == 0)
entry->func = BPRM_CHECK;
else if (strcmp(args[0].from, "CREDS_CHECK") == 0)
entry->func = CREDS_CHECK;
else if (strcmp(args[0].from, "KEXEC_KERNEL_CHECK") ==
0)
entry->func = KEXEC_KERNEL_CHECK;
......
......@@ -51,10 +51,14 @@
#define IMA_BPRM_APPRAISED 0x00020000
#define IMA_READ_APPRAISE 0x00040000
#define IMA_READ_APPRAISED 0x00080000
#define IMA_CREDS_APPRAISE 0x00100000
#define IMA_CREDS_APPRAISED 0x00200000
#define IMA_APPRAISE_SUBMASK (IMA_FILE_APPRAISE | IMA_MMAP_APPRAISE | \
IMA_BPRM_APPRAISE | IMA_READ_APPRAISE)
IMA_BPRM_APPRAISE | IMA_READ_APPRAISE | \
IMA_CREDS_APPRAISE)
#define IMA_APPRAISED_SUBMASK (IMA_FILE_APPRAISED | IMA_MMAP_APPRAISED | \
IMA_BPRM_APPRAISED | IMA_READ_APPRAISED)
IMA_BPRM_APPRAISED | IMA_READ_APPRAISED | \
IMA_CREDS_APPRAISED)
/* iint cache atomic_flags */
#define IMA_CHANGE_XATTR 0
......@@ -121,6 +125,7 @@ struct integrity_iint_cache {
enum integrity_status ima_mmap_status:4;
enum integrity_status ima_bprm_status:4;
enum integrity_status ima_read_status:4;
enum integrity_status ima_creds_status:4;
enum integrity_status evm_status:4;
struct ima_digest_data *ima_hash;
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment