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:
commit
16a601569e
18 files changed
+466
-178
No files matched your search
@@ -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=]
|
||||
|
||||
@@ -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
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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 */
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user