Merge c832183148 ("Merge tag 'ipe-pr-20251202' of git://git.kernel.org/pub/scm/linux/kernel/git/wufan/ipe") into android-mainline

Steps on the way to v6.19-rc1

Change-Id: Ibd6a520e58bb15284e112b545a20ae07c9be4726
Signed-off-by: Carlos Llamas <cmllamas@google.com>
This commit is contained in:
Carlos Llamas committed 2025-12-29 12:25:41 -08:00
commit 16a601569e
18 files changed
+466 -178

No files matched your search

+2 -1
View File
@@ -20,9 +20,10 @@ Description:
rule format: action [condition ...]
action: measure | dont_measure | appraise | dont_appraise |
audit | hash | dont_hash
audit | dont_audit | hash | dont_hash
condition:= base | lsm [option]
base: [[func=] [mask=] [fsmagic=] [fsuuid=] [fsname=]
[fs_subtype=]
[uid=] [euid=] [gid=] [egid=]
[fowner=] [fgroup=]]
lsm: [[subj_user=] [subj_role=] [subj_type=]
+13 -3
View File
@@ -601,10 +601,15 @@ specification.
Task Attribute
~~~~~~~~~~~~~~
The Smack label of a process can be read from /proc/<pid>/attr/current. A
process can read its own Smack label from /proc/self/attr/current. A
The Smack label of a process can be read from ``/proc/<pid>/attr/current``. A
process can read its own Smack label from ``/proc/self/attr/current``. A
privileged process can change its own Smack label by writing to
/proc/self/attr/current but not the label of another process.
``/proc/self/attr/current`` but not the label of another process.
Format of writing is : only the label or the label followed by one of the
3 trailers: ``\n`` (by common agreement for ``/proc/...`` interfaces),
``\0`` (because some applications incorrectly include it),
``\n\0`` (because we think some applications may incorrectly include it).
File Attribute
~~~~~~~~~~~~~~
@@ -696,6 +701,11 @@ sockets.
A privileged program may set this to match the label of another
task with which it hopes to communicate.
UNIX domain socket (UDS) with a BSD address functions both as a file in a
filesystem and as a socket. As a file, it carries the SMACK64 attribute. This
attribute is not involved in Smack security enforcement and is immutably
assigned the label "*".
Smack Netlabel Exceptions
~~~~~~~~~~~~~~~~~~~~~~~~~
+14 -3
View File
@@ -95,7 +95,20 @@ languages when these scripts are invoked by passing these program files
to the interpreter. This is because the way interpreters execute these
files; the scripts themselves are not evaluated as executable code
through one of IPE's hooks, but they are merely text files that are read
(as opposed to compiled executables) [#interpreters]_.
(as opposed to compiled executables). However, with the introduction of the
``AT_EXECVE_CHECK`` flag (:doc:`AT_EXECVE_CHECK </userspace-api/check_exec>`),
interpreters can use it to signal the kernel that a script file will be executed,
and request the kernel to perform LSM security checks on it.
IPE's EXECUTE operation enforcement differs between compiled executables and
interpreted scripts: For compiled executables, enforcement is triggered
automatically by the kernel during ``execve()``, ``execveat()``, ``mmap()``
and ``mprotect()`` syscalls when loading executable content. For interpreted
scripts, enforcement requires explicit interpreter integration using
``execveat()`` with ``AT_EXECVE_CHECK`` flag. Unlike exec syscalls that IPE
intercepts during the execution process, this mechanism needs the interpreter
to take the initiative, and existing interpreters won't be automatically
supported unless the signal call is added.
Threat Model
------------
@@ -806,8 +819,6 @@ A:
.. [#digest_cache_lsm] https://lore.kernel.org/lkml/20240415142436.2545003-1-roberto.sassu@huaweicloud.com/
.. [#interpreters] There is `some interest in solving this issue <https://lore.kernel.org/lkml/20220321161557.495388-1-mic@digikod.net/>`_.
.. [#devdoc] Please see :doc:`the design docs </security/ipe>` for more on
this topic.
+1
View File
@@ -14,6 +14,7 @@
id(KEXEC_INITRAMFS, kexec-initramfs) \
id(POLICY, security-policy) \
id(X509_CERTIFICATE, x509-certificate) \
id(MODULE_COMPRESSED, kernel-module-compressed) \
id(MAX_ID, )
#define __fid_enumify(ENUM, dummy) READING_ ## ENUM,
+1 -1
View File
@@ -138,7 +138,7 @@ struct audit_context {
struct audit_aux_data *aux_pids;
struct sockaddr_storage *sockaddr;
size_t sockaddr_len;
/* Save things to print about task_struct */
/* Save things to print about task_struct */
pid_t ppid;
kuid_t uid, euid, suid, fsuid;
kgid_t gid, egid, sgid, fsgid;
+1 -2
View File
@@ -638,10 +638,9 @@ static struct audit_rule_data *audit_krule_to_data(struct audit_krule *krule)
void *bufp;
int i;
data = kmalloc(struct_size(data, buf, krule->buflen), GFP_KERNEL);
data = kzalloc(struct_size(data, buf, krule->buflen), GFP_KERNEL);
if (unlikely(!data))
return NULL;
memset(data, 0, sizeof(*data));
data->flags = krule->flags | krule->listnr;
data->action = krule->action;
+21 -26
View File
@@ -2416,41 +2416,36 @@ void __audit_inode_child(struct inode *parent,
if (inode)
handle_one(inode);
/* look for a parent entry first */
list_for_each_entry(n, &context->names_list, list) {
if (!n->name ||
(n->type != AUDIT_TYPE_PARENT &&
n->type != AUDIT_TYPE_UNKNOWN))
continue;
if (n->ino == parent->i_ino && n->dev == parent->i_sb->s_dev &&
!audit_compare_dname_path(dname,
n->name->name, n->name_len)) {
if (n->type == AUDIT_TYPE_UNKNOWN)
n->type = AUDIT_TYPE_PARENT;
found_parent = n;
break;
}
}
cond_resched();
/* is there a matching child entry? */
list_for_each_entry(n, &context->names_list, list) {
/* can only match entries that have a name */
if (!n->name ||
(n->type != type && n->type != AUDIT_TYPE_UNKNOWN))
if (!n->name)
continue;
if (!strcmp(dname->name, n->name->name) ||
!audit_compare_dname_path(dname, n->name->name,
/* look for a parent entry first */
if (!found_parent &&
(n->type == AUDIT_TYPE_PARENT || n->type == AUDIT_TYPE_UNKNOWN) &&
(n->ino == parent->i_ino && n->dev == parent->i_sb->s_dev &&
!audit_compare_dname_path(dname, n->name->name, n->name_len))) {
n->type = AUDIT_TYPE_PARENT;
found_parent = n;
if (found_child)
break;
continue;
}
/* is there a matching child entry? */
if (!found_child &&
(n->type == type || n->type == AUDIT_TYPE_UNKNOWN) &&
(!strcmp(dname->name, n->name->name) ||
!audit_compare_dname_path(dname, n->name->name,
found_parent ?
found_parent->name_len :
AUDIT_NAME_FULL)) {
AUDIT_NAME_FULL))) {
if (n->type == AUDIT_TYPE_UNKNOWN)
n->type = type;
found_child = n;
break;
if (found_parent)
break;
}
}
+14 -3
View File
@@ -3715,24 +3715,35 @@ static int idempotent_wait_for_completion(struct idempotent *u)
static int init_module_from_file(struct file *f, const char __user * uargs, int flags)
{
bool compressed = !!(flags & MODULE_INIT_COMPRESSED_FILE);
struct load_info info = { };
void *buf = NULL;
int len;
int err;
len = kernel_read_file(f, 0, &buf, INT_MAX, NULL, READING_MODULE);
len = kernel_read_file(f, 0, &buf, INT_MAX, NULL,
compressed ? READING_MODULE_COMPRESSED :
READING_MODULE);
if (len < 0) {
mod_stat_inc(&failed_kreads);
return len;
}
if (flags & MODULE_INIT_COMPRESSED_FILE) {
int err = module_decompress(&info, buf, len);
if (compressed) {
err = module_decompress(&info, buf, len);
vfree(buf); /* compressed data is no longer needed */
if (err) {
mod_stat_inc(&failed_decompress);
mod_stat_add_long(len, &invalid_decompress_bytes);
return err;
}
err = security_kernel_post_read_file(f, (char *)info.hdr, info.len,
READING_MODULE);
if (err) {
mod_stat_inc(&failed_kreads);
free_copy(&info, flags);
return err;
}
} else {
info.hdr = buf;
info.len = len;
+46 -14
View File
@@ -235,7 +235,8 @@ static void ima_file_free(struct file *file)
static int process_measurement(struct file *file, const struct cred *cred,
struct lsm_prop *prop, char *buf, loff_t size,
int mask, enum ima_hooks func)
int mask, enum ima_hooks func,
enum kernel_read_file_id read_id)
{
struct inode *real_inode, *inode = file_inode(file);
struct ima_iint_cache *iint = NULL;
@@ -406,6 +407,12 @@ static int process_measurement(struct file *file, const struct cred *cred,
if (rc != 0 && rc != -EBADF && rc != -EINVAL)
goto out_locked;
/* Defer measuring/appraising kernel modules to READING_MODULE */
if (read_id == READING_MODULE_COMPRESSED) {
must_appraise = 0;
goto out_locked;
}
if (!pathbuf) /* ima_rdwr_violation possibly pre-fetched */
pathname = ima_d_path(&file->f_path, &pathbuf, filename);
@@ -486,14 +493,14 @@ static int ima_file_mmap(struct file *file, unsigned long reqprot,
if (reqprot & PROT_EXEC) {
ret = process_measurement(file, current_cred(), &prop, NULL,
0, MAY_EXEC, MMAP_CHECK_REQPROT);
0, MAY_EXEC, MMAP_CHECK_REQPROT, 0);
if (ret)
return ret;
}
if (prot & PROT_EXEC)
return process_measurement(file, current_cred(), &prop, NULL,
0, MAY_EXEC, MMAP_CHECK);
0, MAY_EXEC, MMAP_CHECK, 0);
return 0;
}
@@ -573,18 +580,41 @@ static int ima_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
*/
static int ima_bprm_check(struct linux_binprm *bprm)
{
int ret;
struct lsm_prop prop;
security_current_getlsmprop_subj(&prop);
ret = process_measurement(bprm->file, current_cred(),
&prop, NULL, 0, MAY_EXEC, BPRM_CHECK);
if (ret)
return ret;
return process_measurement(bprm->file, current_cred(),
&prop, NULL, 0, MAY_EXEC, BPRM_CHECK, 0);
}
security_cred_getlsmprop(bprm->cred, &prop);
return process_measurement(bprm->file, bprm->cred, &prop, NULL, 0,
MAY_EXEC, CREDS_CHECK);
/**
* ima_creds_check - based on policy, collect/store measurement.
* @bprm: contains the linux_binprm structure
* @file: contains the file descriptor of the binary being executed
*
* The OS protects against an executable file, already open for write,
* from being executed in deny_write_access() and an executable file,
* already open for execute, from being modified in get_write_access().
* So we can be certain that what we verify and measure here is actually
* what is being executed.
*
* The difference from ima_bprm_check() is that ima_creds_check() is invoked
* only after determining the final binary to be executed without interpreter,
* and not when searching for intermediate binaries. The reason is that since
* commit 56305aa9b6fab ("exec: Compute file based creds only once"), the
* credentials to be applied to the process are calculated only at that stage
* (bprm_creds_from_file security hook instead of bprm_check_security).
*
* On success return 0. On integrity appraisal error, assuming the file
* is in policy and IMA-appraisal is in enforcing mode, return -EACCES.
*/
static int ima_creds_check(struct linux_binprm *bprm, const struct file *file)
{
struct lsm_prop prop;
security_current_getlsmprop_subj(&prop);
return process_measurement((struct file *)file, bprm->cred, &prop, NULL,
0, MAY_EXEC, CREDS_CHECK, 0);
}
/**
@@ -632,7 +662,7 @@ static int ima_file_check(struct file *file, int mask)
security_current_getlsmprop_subj(&prop);
return process_measurement(file, current_cred(), &prop, NULL, 0,
mask & (MAY_READ | MAY_WRITE | MAY_EXEC |
MAY_APPEND), FILE_CHECK);
MAY_APPEND), FILE_CHECK, 0);
}
static int __ima_inode_hash(struct inode *inode, struct file *file, char *buf,
@@ -851,12 +881,13 @@ static int ima_read_file(struct file *file, enum kernel_read_file_id read_id,
func = read_idmap[read_id] ?: FILE_CHECK;
security_current_getlsmprop_subj(&prop);
return process_measurement(file, current_cred(), &prop, NULL, 0,
MAY_READ, func);
MAY_READ, func, 0);
}
const int read_idmap[READING_MAX_ID] = {
[READING_FIRMWARE] = FIRMWARE_CHECK,
[READING_MODULE] = MODULE_CHECK,
[READING_MODULE_COMPRESSED] = MODULE_CHECK,
[READING_KEXEC_IMAGE] = KEXEC_KERNEL_CHECK,
[READING_KEXEC_INITRAMFS] = KEXEC_INITRAMFS_CHECK,
[READING_POLICY] = POLICY_CHECK
@@ -894,7 +925,7 @@ static int ima_post_read_file(struct file *file, char *buf, loff_t size,
func = read_idmap[read_id] ?: FILE_CHECK;
security_current_getlsmprop_subj(&prop);
return process_measurement(file, current_cred(), &prop, buf, size,
MAY_READ, func);
MAY_READ, func, read_id);
}
/**
@@ -1242,6 +1273,7 @@ static int __init init_ima(void)
static struct security_hook_list ima_hooks[] __ro_after_init = {
LSM_HOOK_INIT(bprm_check_security, ima_bprm_check),
LSM_HOOK_INIT(bprm_creds_for_exec, ima_bprm_creds_for_exec),
LSM_HOOK_INIT(bprm_creds_from_file, ima_creds_check),
LSM_HOOK_INIT(file_post_open, ima_file_check),
LSM_HOOK_INIT(inode_post_create_tmpfile, ima_post_create_tmpfile),
LSM_HOOK_INIT(file_release, ima_file_free),
+55 -7
View File
@@ -38,6 +38,7 @@
#define IMA_GID 0x2000
#define IMA_EGID 0x4000
#define IMA_FGROUP 0x8000
#define IMA_FS_SUBTYPE 0x10000
#define UNKNOWN 0
#define MEASURE 0x0001 /* same as IMA_MEASURE */
@@ -45,6 +46,7 @@
#define APPRAISE 0x0004 /* same as IMA_APPRAISE */
#define DONT_APPRAISE 0x0008
#define AUDIT 0x0040
#define DONT_AUDIT 0x0080
#define HASH 0x0100
#define DONT_HASH 0x0200
@@ -119,6 +121,7 @@ struct ima_rule_entry {
int type; /* audit type */
} lsm[MAX_LSM_RULES];
char *fsname;
char *fs_subtype;
struct ima_rule_opt_list *keyrings; /* Measure keys added to these keyrings */
struct ima_rule_opt_list *label; /* Measure data grouped under this label */
struct ima_template_desc *template;
@@ -241,7 +244,8 @@ static struct ima_rule_entry build_appraise_rules[] __ro_after_init = {
static struct ima_rule_entry secure_boot_rules[] __ro_after_init = {
{.action = APPRAISE, .func = MODULE_CHECK,
.flags = IMA_FUNC | IMA_DIGSIG_REQUIRED},
.flags = IMA_FUNC | IMA_DIGSIG_REQUIRED | IMA_MODSIG_ALLOWED |
IMA_CHECK_BLACKLIST},
{.action = APPRAISE, .func = FIRMWARE_CHECK,
.flags = IMA_FUNC | IMA_DIGSIG_REQUIRED},
{.action = APPRAISE, .func = KEXEC_KERNEL_CHECK,
@@ -397,6 +401,7 @@ static void ima_free_rule(struct ima_rule_entry *entry)
* the defined_templates list and cannot be freed here
*/
kfree(entry->fsname);
kfree(entry->fs_subtype);
ima_free_rule_opt_list(entry->keyrings);
ima_lsm_free_rule(entry);
kfree(entry);
@@ -601,6 +606,12 @@ static bool ima_match_rules(struct ima_rule_entry *rule,
if ((rule->flags & IMA_FSNAME)
&& strcmp(rule->fsname, inode->i_sb->s_type->name))
return false;
if (rule->flags & IMA_FS_SUBTYPE) {
if (!inode->i_sb->s_subtype)
return false;
if (strcmp(rule->fs_subtype, inode->i_sb->s_subtype))
return false;
}
if ((rule->flags & IMA_FSUUID) &&
!uuid_equal(&rule->fsuuid, &inode->i_sb->s_uuid))
return false;
@@ -674,7 +685,7 @@ retry:
goto retry;
}
}
if (!rc) {
if (rc <= 0) {
result = false;
goto out;
}
@@ -1064,10 +1075,10 @@ void ima_update_policy(void)
enum policy_opt {
Opt_measure, Opt_dont_measure,
Opt_appraise, Opt_dont_appraise,
Opt_audit, Opt_hash, Opt_dont_hash,
Opt_audit, Opt_dont_audit, Opt_hash, Opt_dont_hash,
Opt_obj_user, Opt_obj_role, Opt_obj_type,
Opt_subj_user, Opt_subj_role, Opt_subj_type,
Opt_func, Opt_mask, Opt_fsmagic, Opt_fsname, Opt_fsuuid,
Opt_func, Opt_mask, Opt_fsmagic, Opt_fsname, Opt_fs_subtype, Opt_fsuuid,
Opt_uid_eq, Opt_euid_eq, Opt_gid_eq, Opt_egid_eq,
Opt_fowner_eq, Opt_fgroup_eq,
Opt_uid_gt, Opt_euid_gt, Opt_gid_gt, Opt_egid_gt,
@@ -1086,6 +1097,7 @@ static const match_table_t policy_tokens = {
{Opt_appraise, "appraise"},
{Opt_dont_appraise, "dont_appraise"},
{Opt_audit, "audit"},
{Opt_dont_audit, "dont_audit"},
{Opt_hash, "hash"},
{Opt_dont_hash, "dont_hash"},
{Opt_obj_user, "obj_user=%s"},
@@ -1098,6 +1110,7 @@ static const match_table_t policy_tokens = {
{Opt_mask, "mask=%s"},
{Opt_fsmagic, "fsmagic=%s"},
{Opt_fsname, "fsname=%s"},
{Opt_fs_subtype, "fs_subtype=%s"},
{Opt_fsuuid, "fsuuid=%s"},
{Opt_uid_eq, "uid=%s"},
{Opt_euid_eq, "euid=%s"},
@@ -1282,7 +1295,8 @@ static bool ima_validate_rule(struct ima_rule_entry *entry)
if (entry->flags & ~(IMA_FUNC | IMA_MASK | IMA_FSMAGIC |
IMA_UID | IMA_FOWNER | IMA_FSUUID |
IMA_INMASK | IMA_EUID | IMA_PCR |
IMA_FSNAME | IMA_GID | IMA_EGID |
IMA_FSNAME | IMA_FS_SUBTYPE |
IMA_GID | IMA_EGID |
IMA_FGROUP | IMA_DIGSIG_REQUIRED |
IMA_PERMIT_DIRECTIO | IMA_VALIDATE_ALGOS |
IMA_CHECK_BLACKLIST | IMA_VERITY_REQUIRED))
@@ -1295,7 +1309,8 @@ static bool ima_validate_rule(struct ima_rule_entry *entry)
if (entry->flags & ~(IMA_FUNC | IMA_MASK | IMA_FSMAGIC |
IMA_UID | IMA_FOWNER | IMA_FSUUID |
IMA_INMASK | IMA_EUID | IMA_PCR |
IMA_FSNAME | IMA_GID | IMA_EGID |
IMA_FSNAME | IMA_FS_SUBTYPE |
IMA_GID | IMA_EGID |
IMA_FGROUP | IMA_DIGSIG_REQUIRED |
IMA_PERMIT_DIRECTIO | IMA_MODSIG_ALLOWED |
IMA_CHECK_BLACKLIST | IMA_VALIDATE_ALGOS))
@@ -1308,7 +1323,8 @@ static bool ima_validate_rule(struct ima_rule_entry *entry)
if (entry->flags & ~(IMA_FUNC | IMA_FSMAGIC | IMA_UID |
IMA_FOWNER | IMA_FSUUID | IMA_EUID |
IMA_PCR | IMA_FSNAME | IMA_GID | IMA_EGID |
IMA_PCR | IMA_FSNAME | IMA_FS_SUBTYPE |
IMA_GID | IMA_EGID |
IMA_FGROUP))
return false;
@@ -1478,6 +1494,14 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
entry->action = AUDIT;
break;
case Opt_dont_audit:
ima_log_string(ab, "action", "dont_audit");
if (entry->action != UNKNOWN)
result = -EINVAL;
entry->action = DONT_AUDIT;
break;
case Opt_hash:
ima_log_string(ab, "action", "hash");
@@ -1587,6 +1611,22 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
result = 0;
entry->flags |= IMA_FSNAME;
break;
case Opt_fs_subtype:
ima_log_string(ab, "fs_subtype", args[0].from);
if (entry->fs_subtype) {
result = -EINVAL;
break;
}
entry->fs_subtype = kstrdup(args[0].from, GFP_KERNEL);
if (!entry->fs_subtype) {
result = -ENOMEM;
break;
}
result = 0;
entry->flags |= IMA_FS_SUBTYPE;
break;
case Opt_keyrings:
ima_log_string(ab, "keyrings", args[0].from);
@@ -2097,6 +2137,8 @@ int ima_policy_show(struct seq_file *m, void *v)
seq_puts(m, pt(Opt_dont_appraise));
if (entry->action & AUDIT)
seq_puts(m, pt(Opt_audit));
if (entry->action & DONT_AUDIT)
seq_puts(m, pt(Opt_dont_audit));
if (entry->action & HASH)
seq_puts(m, pt(Opt_hash));
if (entry->action & DONT_HASH)
@@ -2133,6 +2175,12 @@ int ima_policy_show(struct seq_file *m, void *v)
seq_puts(m, " ");
}
if (entry->flags & IMA_FS_SUBTYPE) {
snprintf(tbuf, sizeof(tbuf), "%s", entry->fs_subtype);
seq_printf(m, pt(Opt_fs_subtype), tbuf);
seq_puts(m, " ");
}
if (entry->flags & IMA_KEYRINGS) {
seq_puts(m, "keyrings=");
ima_show_rule_opt_list(m, entry->keyrings);
+1
View File
@@ -46,6 +46,7 @@ static const char *const audit_op_names[__IPE_OP_MAX + 1] = {
static const char *const audit_hook_names[__IPE_HOOK_MAX] = {
"BPRM_CHECK",
"BPRM_CREDS_FOR_EXEC",
"MMAP",
"MPROTECT",
"KERNEL_READ",
+29 -1
View File
@@ -35,6 +35,33 @@ int ipe_bprm_check_security(struct linux_binprm *bprm)
return ipe_evaluate_event(&ctx);
}
/**
* ipe_bprm_creds_for_exec() - ipe security hook function for bprm creds check.
* @bprm: Supplies a pointer to a linux_binprm structure to source the file
* being evaluated.
*
* This LSM hook is called when userspace signals the kernel to check a file
* for execution through the execveat syscall with the AT_EXECVE_CHECK flag.
* The hook triggers IPE policy evaluation on the script file and returns
* the policy decision to userspace. The userspace program receives the
* return code and can decide whether to proceed with script execution.
*
* Return:
* * %0 - Success
* * %-EACCES - Did not pass IPE policy
*/
int ipe_bprm_creds_for_exec(struct linux_binprm *bprm)
{
struct ipe_eval_ctx ctx = IPE_EVAL_CTX_INIT;
if (!bprm->is_check)
return 0;
ipe_build_eval_ctx(&ctx, bprm->file, IPE_OP_EXEC,
IPE_HOOK_BPRM_CREDS_FOR_EXEC);
return ipe_evaluate_event(&ctx);
}
/**
* ipe_mmap_file() - ipe security hook function for mmap check.
* @f: File being mmap'd. Can be NULL in the case of anonymous memory.
@@ -118,6 +145,7 @@ int ipe_kernel_read_file(struct file *file, enum kernel_read_file_id id,
op = IPE_OP_FIRMWARE;
break;
case READING_MODULE:
case READING_MODULE_COMPRESSED:
op = IPE_OP_KERNEL_MODULE;
break;
case READING_KEXEC_INITRAMFS:
@@ -311,4 +339,4 @@ int ipe_inode_setintegrity(const struct inode *inode,
return -EINVAL;
}
#endif /* CONFIG_CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG */
#endif /* CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG */
+3
View File
@@ -13,6 +13,7 @@
enum ipe_hook_type {
IPE_HOOK_BPRM_CHECK = 0,
IPE_HOOK_BPRM_CREDS_FOR_EXEC,
IPE_HOOK_MMAP,
IPE_HOOK_MPROTECT,
IPE_HOOK_KERNEL_READ,
@@ -24,6 +25,8 @@ enum ipe_hook_type {
int ipe_bprm_check_security(struct linux_binprm *bprm);
int ipe_bprm_creds_for_exec(struct linux_binprm *bprm);
int ipe_mmap_file(struct file *f, unsigned long reqprot, unsigned long prot,
unsigned long flags);
+1
View File
@@ -47,6 +47,7 @@ struct ipe_inode *ipe_inode(const struct inode *inode)
static struct security_hook_list ipe_hooks[] __ro_after_init = {
LSM_HOOK_INIT(bprm_check_security, ipe_bprm_check_security),
LSM_HOOK_INIT(bprm_creds_for_exec, ipe_bprm_creds_for_exec),
LSM_HOOK_INIT(mmap_file, ipe_mmap_file),
LSM_HOOK_INIT(file_mprotect, ipe_file_mprotect),
LSM_HOOK_INIT(kernel_read_file, ipe_kernel_read_file),
+3 -2
View File
@@ -4296,7 +4296,7 @@ static int selinux_kernel_read_file(struct file *file,
{
int rc = 0;
BUILD_BUG_ON_MSG(READING_MAX_ID > 7,
BUILD_BUG_ON_MSG(READING_MAX_ID > 8,
"New kernel_read_file_id introduced; update SELinux!");
switch (id) {
@@ -4304,6 +4304,7 @@ static int selinux_kernel_read_file(struct file *file,
rc = selinux_kernel_load_from_file(file, SYSTEM__FIRMWARE_LOAD);
break;
case READING_MODULE:
case READING_MODULE_COMPRESSED:
rc = selinux_kernel_load_from_file(file, SYSTEM__MODULE_LOAD);
break;
case READING_KEXEC_IMAGE:
@@ -4332,7 +4333,7 @@ static int selinux_kernel_load_data(enum kernel_load_data_id id, bool contents)
{
int rc = 0;
BUILD_BUG_ON_MSG(LOADING_MAX_ID > 7,
BUILD_BUG_ON_MSG(LOADING_MAX_ID > 8,
"New kernel_load_data_id introduced; update SELinux!");
switch (id) {
+3
View File
@@ -300,9 +300,12 @@ int smk_tskacc(struct task_smack *, struct smack_known *,
int smk_curacc(struct smack_known *, u32, struct smk_audit_info *);
int smack_str_from_perm(char *string, int access);
struct smack_known *smack_from_secid(const u32);
int smk_parse_label_len(const char *string, int len);
char *smk_parse_smack(const char *string, int len);
int smk_netlbl_mls(int, char *, struct netlbl_lsm_secattr *, int);
struct smack_known *smk_import_entry(const char *, int);
struct smack_known *smk_import_valid_label(const char *label, int label_len,
gfp_t gfp);
void smk_insert_entry(struct smack_known *skp);
struct smack_known *smk_find_entry(const char *);
bool smack_privileged(int cap);
+74 -22
View File
@@ -443,19 +443,19 @@ struct smack_known *smk_find_entry(const char *string)
}
/**
* smk_parse_smack - parse smack label from a text string
* @string: a text string that might contain a Smack label
* @len: the maximum size, or zero if it is NULL terminated.
* smk_parse_label_len - calculate the length of the starting segment
* in the string that constitutes a valid smack label
* @string: a text string that might contain a Smack label at the beginning
* @len: the maximum size to look into, may be zero if string is null-terminated
*
* Returns a pointer to the clean label or an error code.
* Returns the length of the segment (0 < L < SMK_LONGLABEL) or an error code.
*/
char *smk_parse_smack(const char *string, int len)
int smk_parse_label_len(const char *string, int len)
{
char *smack;
int i;
if (len <= 0)
len = strlen(string) + 1;
if (len <= 0 || len > SMK_LONGLABEL)
len = SMK_LONGLABEL;
/*
* Reserve a leading '-' as an indicator that
@@ -463,7 +463,7 @@ char *smk_parse_smack(const char *string, int len)
* including /smack/cipso and /smack/cipso2
*/
if (string[0] == '-')
return ERR_PTR(-EINVAL);
return -EINVAL;
for (i = 0; i < len; i++)
if (string[i] > '~' || string[i] <= ' ' || string[i] == '/' ||
@@ -471,6 +471,25 @@ char *smk_parse_smack(const char *string, int len)
break;
if (i == 0 || i >= SMK_LONGLABEL)
return -EINVAL;
return i;
}
/**
* smk_parse_smack - copy the starting segment in the string
* that constitutes a valid smack label
* @string: a text string that might contain a Smack label at the beginning
* @len: the maximum size to look into, may be zero if string is null-terminated
*
* Returns a pointer to the copy of the label or an error code.
*/
char *smk_parse_smack(const char *string, int len)
{
char *smack;
int i = smk_parse_label_len(string, len);
if (i < 0)
return ERR_PTR(-EINVAL);
smack = kstrndup(string, i, GFP_NOFS);
@@ -554,31 +573,26 @@ int smack_populate_secattr(struct smack_known *skp)
}
/**
* smk_import_entry - import a label, return the list entry
* @string: a text string that might be a Smack label
* @len: the maximum size, or zero if it is NULL terminated.
* smk_import_valid_allocated_label - import a label, return the list entry
* @smack: a text string that is a valid Smack label and may be kfree()ed.
* It is consumed: either becomes a part of the entry or kfree'ed.
* @gfp: Allocation type
*
* Returns a pointer to the entry in the label list that
* matches the passed string, adding it if necessary,
* or an error code.
* Returns: see description of smk_import_entry()
*/
struct smack_known *smk_import_entry(const char *string, int len)
static struct smack_known *
smk_import_allocated_label(char *smack, gfp_t gfp)
{
struct smack_known *skp;
char *smack;
int rc;
smack = smk_parse_smack(string, len);
if (IS_ERR(smack))
return ERR_CAST(smack);
mutex_lock(&smack_known_lock);
skp = smk_find_entry(smack);
if (skp != NULL)
goto freeout;
skp = kzalloc(sizeof(*skp), GFP_NOFS);
skp = kzalloc(sizeof(*skp), gfp);
if (skp == NULL) {
skp = ERR_PTR(-ENOMEM);
goto freeout;
@@ -608,6 +622,44 @@ unlockout:
return skp;
}
/**
* smk_import_entry - import a label, return the list entry
* @string: a text string that might contain a Smack label at the beginning
* @len: the maximum size to look into, may be zero if string is null-terminated
*
* Returns a pointer to the entry in the label list that
* matches the passed string, adding it if necessary,
* or an error code.
*/
struct smack_known *smk_import_entry(const char *string, int len)
{
char *smack = smk_parse_smack(string, len);
if (IS_ERR(smack))
return ERR_CAST(smack);
return smk_import_allocated_label(smack, GFP_NOFS);
}
/**
* smk_import_valid_label - import a label, return the list entry
* @label: a text string that is a valid Smack label, not null-terminated
* @label_len: the length of the text string in the @label
* @gfp: the GFP mask used for allocating memory for the @label text string copy
*
* Return: see description of smk_import_entry()
*/
struct smack_known *
smk_import_valid_label(const char *label, int label_len, gfp_t gfp)
{
char *smack = kstrndup(label, label_len, gfp);
if (!smack)
return ERR_PTR(-ENOMEM);
return smk_import_allocated_label(smack, gfp);
}
/**
* smack_from_secid - find the Smack label associated with a secid
* @secid: an integer that might be associated with a Smack label
+184 -93
View File
@@ -962,6 +962,42 @@ static int smack_inode_alloc_security(struct inode *inode)
return 0;
}
/**
* smk_rule_transmutes - does access rule for (subject,object) contain 't'?
* @subject: a pointer to the subject's Smack label entry
* @object: a pointer to the object's Smack label entry
*/
static bool
smk_rule_transmutes(struct smack_known *subject,
const struct smack_known *object)
{
int may;
rcu_read_lock();
may = smk_access_entry(subject->smk_known, object->smk_known,
&subject->smk_rules);
rcu_read_unlock();
return (may > 0) && (may & MAY_TRANSMUTE);
}
static int
xattr_dupval(struct xattr *xattrs, int *xattr_count,
const char *name, const void *value, unsigned int vallen)
{
struct xattr * const xattr = lsm_get_xattr_slot(xattrs, xattr_count);
if (!xattr)
return 0;
xattr->value = kmemdup(value, vallen, GFP_NOFS);
if (!xattr->value)
return -ENOMEM;
xattr->value_len = vallen;
xattr->name = name;
return 0;
}
/**
* smack_inode_init_security - copy out the smack from an inode
* @inode: the newly created inode
@@ -977,23 +1013,30 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir,
struct xattr *xattrs, int *xattr_count)
{
struct task_smack *tsp = smack_cred(current_cred());
struct inode_smack *issp = smack_inode(inode);
struct smack_known *skp = smk_of_task(tsp);
struct smack_known *isp = smk_of_inode(inode);
struct inode_smack * const issp = smack_inode(inode);
struct smack_known *dsp = smk_of_inode(dir);
struct xattr *xattr = lsm_get_xattr_slot(xattrs, xattr_count);
int may;
int rc = 0;
int transflag = 0;
bool trans_cred;
bool trans_rule;
/*
* UNIX domain sockets use lower level socket data. Let
* UDS inode have fixed * label to keep smack_inode_permission() calm
* when called from unix_find_bsd()
*/
if (S_ISSOCK(inode->i_mode)) {
/* forced label, no need to save to xattrs */
issp->smk_inode = &smack_known_star;
goto instant_inode;
}
/*
* If equal, transmuting already occurred in
* smack_dentry_create_files_as(). No need to check again.
*/
if (tsp->smk_task != tsp->smk_transmuted) {
rcu_read_lock();
may = smk_access_entry(skp->smk_known, dsp->smk_known,
&skp->smk_rules);
rcu_read_unlock();
}
trans_cred = (tsp->smk_task == tsp->smk_transmuted);
if (!trans_cred)
trans_rule = smk_rule_transmutes(smk_of_task(tsp), dsp);
/*
* In addition to having smk_task equal to smk_transmuted,
@@ -1001,47 +1044,38 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir,
* requests transmutation then by all means transmute.
* Mark the inode as changed.
*/
if ((tsp->smk_task == tsp->smk_transmuted) ||
(may > 0 && ((may & MAY_TRANSMUTE) != 0) &&
smk_inode_transmutable(dir))) {
struct xattr *xattr_transmute;
if (trans_cred || (trans_rule && smk_inode_transmutable(dir))) {
/*
* The caller of smack_dentry_create_files_as()
* should have overridden the current cred, so the
* inode label was already set correctly in
* smack_inode_alloc_security().
*/
if (tsp->smk_task != tsp->smk_transmuted)
isp = issp->smk_inode = dsp;
if (!trans_cred)
issp->smk_inode = dsp;
issp->smk_flags |= SMK_INODE_TRANSMUTE;
xattr_transmute = lsm_get_xattr_slot(xattrs,
xattr_count);
if (xattr_transmute) {
xattr_transmute->value = kmemdup(TRANS_TRUE,
TRANS_TRUE_SIZE,
GFP_NOFS);
if (!xattr_transmute->value)
return -ENOMEM;
if (S_ISDIR(inode->i_mode)) {
transflag = SMK_INODE_TRANSMUTE;
xattr_transmute->value_len = TRANS_TRUE_SIZE;
xattr_transmute->name = XATTR_SMACK_TRANSMUTE;
if (xattr_dupval(xattrs, xattr_count,
XATTR_SMACK_TRANSMUTE,
TRANS_TRUE,
TRANS_TRUE_SIZE
))
rc = -ENOMEM;
}
}
issp->smk_flags |= SMK_INODE_INSTANT;
if (xattr) {
xattr->value = kstrdup(isp->smk_known, GFP_NOFS);
if (!xattr->value)
return -ENOMEM;
xattr->value_len = strlen(isp->smk_known);
xattr->name = XATTR_SMACK_SUFFIX;
}
return 0;
if (rc == 0)
if (xattr_dupval(xattrs, xattr_count,
XATTR_SMACK_SUFFIX,
issp->smk_inode->smk_known,
strlen(issp->smk_inode->smk_known)
))
rc = -ENOMEM;
instant_inode:
issp->smk_flags |= (SMK_INODE_INSTANT | transflag);
return rc;
}
/**
@@ -1315,13 +1349,23 @@ static int smack_inode_setxattr(struct mnt_idmap *idmap,
int check_import = 0;
int check_star = 0;
int rc = 0;
umode_t const i_mode = d_backing_inode(dentry)->i_mode;
/*
* Check label validity here so import won't fail in post_setxattr
*/
if (strcmp(name, XATTR_NAME_SMACK) == 0 ||
strcmp(name, XATTR_NAME_SMACKIPIN) == 0 ||
strcmp(name, XATTR_NAME_SMACKIPOUT) == 0) {
if (strcmp(name, XATTR_NAME_SMACK) == 0) {
/*
* UDS inode has fixed label
*/
if (S_ISSOCK(i_mode)) {
rc = -EINVAL;
} else {
check_priv = 1;
check_import = 1;
}
} else if (strcmp(name, XATTR_NAME_SMACKIPIN) == 0 ||
strcmp(name, XATTR_NAME_SMACKIPOUT) == 0) {
check_priv = 1;
check_import = 1;
} else if (strcmp(name, XATTR_NAME_SMACKEXEC) == 0 ||
@@ -1331,7 +1375,7 @@ static int smack_inode_setxattr(struct mnt_idmap *idmap,
check_star = 1;
} else if (strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0) {
check_priv = 1;
if (!S_ISDIR(d_backing_inode(dentry)->i_mode) ||
if (!S_ISDIR(i_mode) ||
size != TRANS_TRUE_SIZE ||
strncmp(value, TRANS_TRUE, TRANS_TRUE_SIZE) != 0)
rc = -EINVAL;
@@ -1462,12 +1506,15 @@ static int smack_inode_removexattr(struct mnt_idmap *idmap,
* Don't do anything special for these.
* XATTR_NAME_SMACKIPIN
* XATTR_NAME_SMACKIPOUT
* XATTR_NAME_SMACK if S_ISSOCK (UDS inode has fixed label)
*/
if (strcmp(name, XATTR_NAME_SMACK) == 0) {
struct super_block *sbp = dentry->d_sb;
struct superblock_smack *sbsp = smack_superblock(sbp);
if (!S_ISSOCK(d_backing_inode(dentry)->i_mode)) {
struct super_block *sbp = dentry->d_sb;
struct superblock_smack *sbsp = smack_superblock(sbp);
isp->smk_inode = sbsp->smk_default;
isp->smk_inode = sbsp->smk_default;
}
} else if (strcmp(name, XATTR_NAME_SMACKEXEC) == 0)
isp->smk_task = NULL;
else if (strcmp(name, XATTR_NAME_SMACKMMAP) == 0)
@@ -3585,7 +3632,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
*/
/*
* UNIX domain sockets use lower level socket data.
* UDS inode has fixed label (*)
*/
if (S_ISSOCK(inode->i_mode)) {
final = &smack_known_star;
@@ -3663,7 +3710,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
* @attr: which attribute to fetch
* @ctx: buffer to receive the result
* @size: available size in, actual size out
* @flags: unused
* @flags: reserved, currently zero
*
* Fill the passed user space @ctx with the details of the requested
* attribute.
@@ -3724,47 +3771,55 @@ static int smack_getprocattr(struct task_struct *p, const char *name, char **val
* Sets the Smack value of the task. Only setting self
* is permitted and only with privilege
*
* Returns the length of the smack label or an error code
* Returns zero on success or an error code
*/
static int do_setattr(u64 attr, void *value, size_t size)
static int do_setattr(unsigned int attr, void *value, size_t size)
{
struct task_smack *tsp = smack_cred(current_cred());
struct cred *new;
struct smack_known *skp;
struct smack_known_list_elem *sklep;
int rc;
if (!smack_privileged(CAP_MAC_ADMIN) && list_empty(&tsp->smk_relabel))
return -EPERM;
int label_len;
/*
* let unprivileged user validate input, check permissions later
*/
if (value == NULL || size == 0 || size >= SMK_LONGLABEL)
return -EINVAL;
if (attr != LSM_ATTR_CURRENT)
return -EOPNOTSUPP;
skp = smk_import_entry(value, size);
if (IS_ERR(skp))
return PTR_ERR(skp);
label_len = smk_parse_label_len(value, size);
if (label_len < 0 || label_len != size)
return -EINVAL;
/*
* No process is ever allowed the web ("@") label
* and the star ("*") label.
*/
if (skp == &smack_known_web || skp == &smack_known_star)
return -EINVAL;
if (label_len == 1 /* '@', '*' */) {
const char c = *(const char *)value;
if (c == *smack_known_web.smk_known ||
c == *smack_known_star.smk_known)
return -EPERM;
}
if (!smack_privileged(CAP_MAC_ADMIN)) {
rc = -EPERM;
list_for_each_entry(sklep, &tsp->smk_relabel, list)
if (sklep->smk_label == skp) {
rc = 0;
break;
}
if (rc)
return rc;
const struct smack_known_list_elem *sklep;
list_for_each_entry(sklep, &tsp->smk_relabel, list) {
const char *cp = sklep->smk_label->smk_known;
if (strlen(cp) == label_len &&
strncmp(cp, value, label_len) == 0)
goto in_relabel;
}
return -EPERM;
in_relabel:
;
}
skp = smk_import_valid_label(value, label_len, GFP_KERNEL);
if (IS_ERR(skp))
return PTR_ERR(skp);
new = prepare_creds();
if (new == NULL)
return -ENOMEM;
@@ -3777,7 +3832,7 @@ static int do_setattr(u64 attr, void *value, size_t size)
smk_destroy_label_list(&tsp->smk_relabel);
commit_creds(new);
return size;
return 0;
}
/**
@@ -3785,7 +3840,7 @@ static int do_setattr(u64 attr, void *value, size_t size)
* @attr: which attribute to set
* @ctx: buffer containing the data
* @size: size of @ctx
* @flags: unused
* @flags: reserved, must be zero
*
* Fill the passed user space @ctx with the details of the requested
* attribute.
@@ -3795,12 +3850,26 @@ static int do_setattr(u64 attr, void *value, size_t size)
static int smack_setselfattr(unsigned int attr, struct lsm_ctx *ctx,
u32 size, u32 flags)
{
int rc;
if (attr != LSM_ATTR_CURRENT)
return -EOPNOTSUPP;
rc = do_setattr(attr, ctx->ctx, ctx->ctx_len);
if (rc > 0)
return 0;
return rc;
if (ctx->flags)
return -EINVAL;
/*
* string must have \0 terminator, included in ctx->ctx
* (see description of struct lsm_ctx)
*/
if (ctx->ctx_len == 0)
return -EINVAL;
if (ctx->ctx[ctx->ctx_len - 1] != '\0')
return -EINVAL;
/*
* other do_setattr() caller, smack_setprocattr(),
* does not count \0 into size, so
* decreasing length by 1 to accommodate the divergence.
*/
return do_setattr(attr, ctx->ctx, ctx->ctx_len - 1);
}
/**
@@ -3812,15 +3881,39 @@ static int smack_setselfattr(unsigned int attr, struct lsm_ctx *ctx,
* Sets the Smack value of the task. Only setting self
* is permitted and only with privilege
*
* Returns the length of the smack label or an error code
* Returns the size of the input value or an error code
*/
static int smack_setprocattr(const char *name, void *value, size_t size)
{
int attr = lsm_name_to_attr(name);
size_t realsize = size;
unsigned int attr = lsm_name_to_attr(name);
if (attr != LSM_ATTR_UNDEF)
return do_setattr(attr, value, size);
return -EINVAL;
switch (attr) {
case LSM_ATTR_UNDEF: return -EINVAL;
default: return -EOPNOTSUPP;
case LSM_ATTR_CURRENT:
;
}
/*
* The value for the "current" attribute is the label
* followed by one of the 4 trailers: none, \0, \n, \n\0
*
* I.e. following inputs are accepted as 3-characters long label "foo":
*
* "foo" (3 characters)
* "foo\0" (4 characters)
* "foo\n" (4 characters)
* "foo\n\0" (5 characters)
*/
if (realsize && (((const char *)value)[realsize - 1] == '\0'))
--realsize;
if (realsize && (((const char *)value)[realsize - 1] == '\n'))
--realsize;
return do_setattr(attr, value, realsize) ? : size;
}
/**
@@ -4850,6 +4943,11 @@ static int smack_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid)
static int smack_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen)
{
/*
* UDS inode has fixed label. Ignore nfs label.
*/
if (S_ISSOCK(inode->i_mode))
return 0;
return smack_inode_setsecurity(inode, XATTR_SMACK_SUFFIX, ctx,
ctxlen, 0);
}
@@ -4915,7 +5013,6 @@ static int smack_dentry_create_files_as(struct dentry *dentry, int mode,
struct task_smack *otsp = smack_cred(old);
struct task_smack *ntsp = smack_cred(new);
struct inode_smack *isp;
int may;
/*
* Use the process credential unless all of
@@ -4929,18 +5026,12 @@ static int smack_dentry_create_files_as(struct dentry *dentry, int mode,
isp = smack_inode(d_inode(dentry->d_parent));
if (isp->smk_flags & SMK_INODE_TRANSMUTE) {
rcu_read_lock();
may = smk_access_entry(otsp->smk_task->smk_known,
isp->smk_inode->smk_known,
&otsp->smk_task->smk_rules);
rcu_read_unlock();
/*
* If the directory is transmuting and the rule
* providing access is transmuting use the containing
* directory label instead of the process label.
*/
if (may > 0 && (may & MAY_TRANSMUTE)) {
if (smk_rule_transmutes(otsp->smk_task, isp->smk_inode)) {
ntsp->smk_task = isp->smk_inode;
ntsp->smk_transmuted = ntsp->smk_task;
}