summaryrefslogtreecommitdiff
path: root/security
diff options
context:
space:
mode:
Diffstat (limited to 'security')
-rw-r--r--security/keys/encrypted-keys/encrypted.c20
-rw-r--r--security/keys/gc.c4
-rw-r--r--security/keys/request_key_auth.c12
-rw-r--r--security/keys/trusted-keys/trusted_tpm2.c12
-rw-r--r--security/selinux/avc.c5
-rw-r--r--security/selinux/hooks.c150
-rw-r--r--security/selinux/include/objsec.h10
7 files changed, 177 insertions, 36 deletions
diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c
index 59cb77b237b3..e07092ea301a 100644
--- a/security/keys/encrypted-keys/encrypted.c
+++ b/security/keys/encrypted-keys/encrypted.c
@@ -19,6 +19,7 @@
#include <linux/parser.h>
#include <linux/string.h>
#include <linux/err.h>
+#include <linux/overflow.h>
#include <keys/user-type.h>
#include <keys/trusted-type.h>
#include <keys/encrypted-type.h>
@@ -579,6 +580,7 @@ static struct encrypted_key_payload *encrypted_key_alloc(struct key *key,
{
struct encrypted_key_payload *epayload = NULL;
unsigned short datablob_len;
+ unsigned short payload_totallen;
unsigned short decrypted_datalen;
unsigned short payload_datalen;
unsigned int encrypted_datalen;
@@ -632,16 +634,22 @@ static struct encrypted_key_payload *encrypted_key_alloc(struct key *key,
encrypted_datalen = roundup(decrypted_datalen, blksize);
- datablob_len = format_len + 1 + strlen(master_desc) + 1
- + strlen(datalen) + 1 + ivsize + 1 + encrypted_datalen;
+ if (check_add_overflow(format_len + 1 + strlen(master_desc) + 1
+ + strlen(datalen) + 1 + ivsize + 1,
+ encrypted_datalen, &datablob_len))
+ return ERR_PTR(-EINVAL);
+
+ if (check_add_overflow(datablob_len,
+ payload_datalen + HASH_SIZE + 1,
+ &payload_totallen))
+ return ERR_PTR(-EINVAL);
- ret = key_payload_reserve(key, payload_datalen + datablob_len
- + HASH_SIZE + 1);
+ ret = key_payload_reserve(key, payload_totallen);
if (ret < 0)
return ERR_PTR(ret);
- epayload = kzalloc(sizeof(*epayload) + payload_datalen +
- datablob_len + HASH_SIZE + 1, GFP_KERNEL);
+ epayload = kzalloc_flex(*epayload, payload_data, payload_totallen,
+ GFP_KERNEL);
if (!epayload)
return ERR_PTR(-ENOMEM);
diff --git a/security/keys/gc.c b/security/keys/gc.c
index 748e83818a76..eda445f815d4 100644
--- a/security/keys/gc.c
+++ b/security/keys/gc.c
@@ -318,9 +318,7 @@ maybe_resched:
if (unlikely(gc_state & KEY_GC_REAPING_DEAD_3)) {
kdebug("dead wake");
- smp_mb();
- clear_bit(KEY_GC_REAPING_KEYTYPE, &key_gc_flags);
- wake_up_bit(&key_gc_flags, KEY_GC_REAPING_KEYTYPE);
+ clear_and_wake_up_bit(KEY_GC_REAPING_KEYTYPE, &key_gc_flags);
}
if (gc_state & KEY_GC_REAP_AGAIN)
diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c
index 282e09d8fa46..ed6f55b9cdd9 100644
--- a/security/keys/request_key_auth.c
+++ b/security/keys/request_key_auth.c
@@ -9,6 +9,8 @@
#include <linux/sched.h>
#include <linux/err.h>
+#include <linux/pid.h>
+#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
@@ -73,7 +75,10 @@ static void request_key_auth_describe(const struct key *key,
seq_puts(m, "key:");
seq_puts(m, key->description);
if (key_is_positive(key))
- seq_printf(m, " pid:%d ci:%zu", rka->pid, rka->callout_len);
+ seq_printf(m, " pid:%d ci:%zu",
+ pid_nr_ns(rka->pid,
+ proc_pid_ns(file_inode(m->file)->i_sb)),
+ rka->callout_len);
}
/*
@@ -113,6 +118,7 @@ static void free_request_key_auth(struct request_key_auth *rka)
if (rka->cred)
put_cred(rka->cred);
kfree(rka->callout_info);
+ put_pid(rka->pid);
kfree(rka);
}
@@ -226,14 +232,14 @@ struct key *request_key_auth_new(struct key *target, const char *op,
irka = cred->request_key_auth->payload.data[0];
rka->cred = get_cred(irka->cred);
- rka->pid = irka->pid;
+ rka->pid = get_pid(irka->pid);
up_read(&cred->request_key_auth->sem);
}
else {
/* it isn't - use this process as the context */
rka->cred = get_cred(cred);
- rka->pid = current->pid;
+ rka->pid = get_pid(task_pid(current));
}
rka->target_key = key_get(target);
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index 67225dd562a9..01f18bb37047 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -99,7 +99,7 @@ struct tpm2_key_context {
static int tpm2_key_decode(struct trusted_key_payload *payload,
struct trusted_key_options *options,
- u8 **buf)
+ u8 **buf, unsigned int *blob_len)
{
int ret;
struct tpm2_key_context ctx;
@@ -120,6 +120,7 @@ static int tpm2_key_decode(struct trusted_key_payload *payload,
return -ENOMEM;
*buf = blob;
+ *blob_len = ctx.priv_len + ctx.pub_len;
options->keyhandle = ctx.parent;
memcpy(blob, ctx.priv, ctx.priv_len);
@@ -384,10 +385,11 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
int rc;
u32 attrs;
- rc = tpm2_key_decode(payload, options, &blob);
+ rc = tpm2_key_decode(payload, options, &blob, &blob_len);
if (rc) {
/* old form */
blob = payload->blob;
+ blob_len = payload->blob_len;
payload->old_format = 1;
} else {
/* Bind for cleanup: */
@@ -399,17 +401,17 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
return -EINVAL;
/* must be big enough for at least the two be16 size counts */
- if (payload->blob_len < 4)
+ if (blob_len < 4)
return -EINVAL;
private_len = get_unaligned_be16(blob);
/* must be big enough for following public_len */
- if (private_len + 2 + 2 > (payload->blob_len))
+ if (private_len + 2 + 2 > blob_len)
return -E2BIG;
public_len = get_unaligned_be16(blob + 2 + private_len);
- if (private_len + 2 + public_len + 2 > payload->blob_len)
+ if (private_len + 2 + public_len + 2 > blob_len)
return -E2BIG;
pub = blob + 2 + private_len + 2;
diff --git a/security/selinux/avc.c b/security/selinux/avc.c
index a9401d6c2e5f..560a82c6d682 100644
--- a/security/selinux/avc.c
+++ b/security/selinux/avc.c
@@ -1149,8 +1149,11 @@ inline int avc_has_perm_noaudit(u32 ssid, u32 tsid,
u32 denied;
struct avc_node *node;
- if (WARN_ON(!requested))
+ if (WARN_ON(!requested)) {
+ /* Provide a deny-all, audit-all decision to the caller. */
+ *avd = (struct av_decision){ .auditdeny = 0xffffffff };
return -EACCES;
+ }
rcu_read_lock();
node = avc_lookup(ssid, tsid, tclass);
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index e5e17f100aae..3f4a6ddee322 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -1674,26 +1674,32 @@ static int cred_has_capability(const struct cred *cred,
return rc;
}
-/* Check whether a task has a particular permission to an inode.
- The 'adp' parameter is optional and allows other audit
- data to be passed (e.g. the dentry). */
-static int inode_has_perm(const struct cred *cred,
- struct inode *inode,
- u32 perms,
- struct common_audit_data *adp)
+/*
+ * Check whether a SID has a particular permission to an inode. The 'adp'
+ * parameter is optional and allows other audit data to be passed (e.g. the
+ * dentry).
+ */
+static int inode_sid_has_perm(u32 sid, struct inode *inode, u32 perms,
+ struct common_audit_data *adp)
{
struct inode_security_struct *isec;
- u32 sid;
if (unlikely(IS_PRIVATE(inode)))
return 0;
- sid = cred_sid(cred);
isec = selinux_inode(inode);
return avc_has_perm(sid, isec->sid, isec->sclass, perms, adp);
}
+static int inode_has_perm(const struct cred *cred,
+ struct inode *inode,
+ u32 perms,
+ struct common_audit_data *adp)
+{
+ return inode_sid_has_perm(cred_sid(cred), inode, perms, adp);
+}
+
/* Same as inode_has_perm, but pass explicit audit data containing
the dentry to help the auditing code to more easily generate the
pathname if needed. */
@@ -3843,17 +3849,74 @@ static int selinux_file_alloc_security(struct file *file)
return 0;
}
+static inline u32 selinux_file_user_sid(const struct file *file)
+{
+ if (unlikely(file->f_mode & FMODE_BACKING))
+ return selinux_backing_file(file)->uf_sid;
+ return selinux_file(file)->sid;
+}
+
static int selinux_backing_file_alloc(struct file *backing_file,
const struct file *user_file)
{
struct backing_file_security_struct *bfsec;
+ const struct backing_file_security_struct *ubfsec;
+ struct backing_file_security_layer *layer;
+ u32 i;
bfsec = selinux_backing_file(backing_file);
- bfsec->uf_sid = selinux_file(user_file)->sid;
+ bfsec->uf_sid = selinux_file_user_sid(user_file);
+ if (!(user_file->f_mode & FMODE_BACKING))
+ return 0;
+
+ ubfsec = selinux_backing_file(user_file);
+ /* a wrapped count would make kmalloc_array() return ZERO_SIZE_PTR */
+ if (unlikely(ubfsec->layer_count == U32_MAX))
+ return -EOVERFLOW;
+
+ /*
+ * The final VMA only retains the lowest backing file, so record the
+ * whole chain here rather than in the mmap hook, where concurrent
+ * mappings would have to be serialized. Size it dynamically: erofs
+ * inode sharing adds a backing file without bumping s_stack_depth.
+ */
+ bfsec->layers = kmalloc_array(ubfsec->layer_count + 1,
+ sizeof(*bfsec->layers), GFP_KERNEL);
+ if (!bfsec->layers)
+ return -ENOMEM;
+
+ for (i = 0; i < ubfsec->layer_count; i++) {
+ layer = &bfsec->layers[i];
+ *layer = ubfsec->layers[i];
+ path_get(&layer->path);
+ }
+
+ /* f_path, not file_user_path(): this layer, not the top-level file */
+ layer = &bfsec->layers[i];
+ layer->path = user_file->f_path;
+ layer->mounter_sid = cred_sid(user_file->f_cred);
+ layer->fd_sid = selinux_file(user_file)->sid;
+ path_get(&layer->path);
+ bfsec->layer_count = ubfsec->layer_count + 1;
return 0;
}
+static void selinux_backing_file_free(struct file *backing_file)
+{
+ struct backing_file_security_struct *bfsec;
+
+ /* security_backing_file_free() may be called twice after an error */
+ if (!backing_file_security(backing_file))
+ return;
+
+ bfsec = selinux_backing_file(backing_file);
+ while (bfsec->layer_count)
+ path_put(&bfsec->layers[--bfsec->layer_count].path);
+ kfree(bfsec->layers);
+ bfsec->layers = NULL;
+}
+
/*
* Check whether a task has the ioctl permission and cmd
* operation to an inode.
@@ -3971,6 +4034,53 @@ static int selinux_file_ioctl_compat(struct file *file, unsigned int cmd,
static int default_noexec __ro_after_init;
+static u32 file_map_prot_to_av(unsigned long prot, bool shared)
+{
+ u32 av = FILE__READ;
+
+ if (shared && (prot & PROT_WRITE))
+ av |= FILE__WRITE;
+ if (prot & PROT_EXEC)
+ av |= FILE__EXECUTE;
+
+ return av;
+}
+
+static int backing_mounters_has_perm(const struct file *file, u32 av)
+{
+ const struct backing_file_security_struct *bfsec;
+ const struct backing_file_security_layer *layer;
+ struct common_audit_data ad;
+ struct inode *inode;
+ u32 i;
+ int rc;
+
+ if (WARN_ON_ONCE(!(file->f_mode & FMODE_BACKING)))
+ return -EIO;
+
+ bfsec = selinux_backing_file(file);
+ for (i = 0; i < bfsec->layer_count; i++) {
+ layer = &bfsec->layers[i];
+ inode = d_inode(layer->path.dentry);
+
+ ad.type = LSM_AUDIT_DATA_PATH;
+ ad.u.path = layer->path;
+
+ if (layer->mounter_sid != layer->fd_sid) {
+ rc = avc_has_perm(layer->mounter_sid, layer->fd_sid,
+ SECCLASS_FD, FD__USE, &ad);
+ if (rc)
+ return rc;
+ }
+
+ rc = inode_sid_has_perm(layer->mounter_sid, inode, av, &ad);
+ if (rc)
+ return rc;
+ }
+
+ return 0;
+}
+
static int __file_map_prot_check(const struct file *file, unsigned long prot,
bool shared, bool mounter_check,
bool bf_user_file)
@@ -4004,14 +4114,10 @@ static int __file_map_prot_check(const struct file *file, unsigned long prot,
if (file) {
const struct cred *cred = mounter_check ?
file->f_cred : current_cred();
- /* "read" always possible, "write" only if shared */
- u32 av = FILE__READ;
- if (shared && prot_write)
- av |= FILE__WRITE;
- if (prot_exec)
- av |= FILE__EXECUTE;
- return __file_has_perm(cred, file, av, bf_user_file);
+ return __file_has_perm(cred, file,
+ file_map_prot_to_av(prot, shared),
+ bf_user_file);
}
return 0;
@@ -4106,6 +4212,7 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
int rc;
const struct cred *cred = current_cred();
u32 sid = cred_sid(cred);
+ u32 av;
const struct file *file = vma->vm_file;
bool backing_file;
bool shared = vma->vm_flags & VM_SHARED;
@@ -4149,6 +4256,10 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
if (rc)
return rc;
if (backing_file) {
+ rc = backing_mounters_has_perm(file,
+ FILE__EXECMOD);
+ if (rc)
+ return rc;
rc = file_has_perm(file->f_cred, file,
FILE__EXECMOD);
if (rc)
@@ -4161,6 +4272,10 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
if (rc)
return rc;
if (backing_file) {
+ av = file_map_prot_to_av(prot, shared);
+ rc = backing_mounters_has_perm(file, av);
+ if (rc)
+ return rc;
rc = file_map_prot_check(file, prot, shared, true);
if (rc)
return rc;
@@ -7619,6 +7734,7 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = {
LSM_HOOK_INIT(file_permission, selinux_file_permission),
LSM_HOOK_INIT(file_alloc_security, selinux_file_alloc_security),
LSM_HOOK_INIT(backing_file_alloc, selinux_backing_file_alloc),
+ LSM_HOOK_INIT(backing_file_free, selinux_backing_file_free),
LSM_HOOK_INIT(file_ioctl, selinux_file_ioctl),
LSM_HOOK_INIT(file_ioctl_compat, selinux_file_ioctl_compat),
LSM_HOOK_INIT(mmap_file, selinux_mmap_file),
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
index 3c0a16ec978b..2f21568251ff 100644
--- a/security/selinux/include/objsec.h
+++ b/security/selinux/include/objsec.h
@@ -86,8 +86,16 @@ struct file_security_struct {
u32 pseqno; /* Policy seqno at the time of file open */
};
+struct backing_file_security_layer {
+ struct path path; /* this layer's real path */
+ u32 mounter_sid; /* SID of the mounter that opened it */
+ u32 fd_sid; /* SID of its open file description */
+};
+
struct backing_file_security_struct {
- u32 uf_sid; /* associated user file fsec->sid */
+ u32 uf_sid; /* top-level user file fsec->sid */
+ u32 layer_count; /* number of intermediate backing files */
+ struct backing_file_security_layer *layers;
};
struct superblock_security_struct {