aboutsummaryrefslogtreecommitdiff
path: root/security
diff options
context:
space:
mode:
Diffstat (limited to 'security')
-rw-r--r--security/Kconfig.hardening23
-rw-r--r--security/apparmor/apparmorfs.c12
-rw-r--r--security/apparmor/lsm.c6
-rw-r--r--security/apparmor/policy_unpack.c11
-rw-r--r--security/commoncap.c6
-rw-r--r--security/inode.c2
-rw-r--r--security/integrity/Kconfig46
-rw-r--r--security/integrity/digsig.c6
-rw-r--r--security/integrity/evm/Kconfig3
-rw-r--r--security/integrity/evm/evm.h4
-rw-r--r--security/integrity/evm/evm_crypto.c11
-rw-r--r--security/integrity/evm/evm_main.c42
-rw-r--r--security/integrity/iint.c50
-rw-r--r--security/integrity/ima/Kconfig37
-rw-r--r--security/integrity/ima/ima_api.c5
-rw-r--r--security/integrity/ima/ima_appraise.c12
-rw-r--r--security/integrity/ima/ima_efi.c3
-rw-r--r--security/integrity/ima/ima_kexec.c2
-rw-r--r--security/integrity/ima/ima_main.c16
-rw-r--r--security/integrity/ima/ima_modsig.c6
-rw-r--r--security/integrity/ima/ima_policy.c21
-rw-r--r--security/integrity/integrity.h7
-rw-r--r--security/integrity/platform_certs/keyring_handler.c19
-rw-r--r--security/integrity/platform_certs/keyring_handler.h10
-rw-r--r--security/integrity/platform_certs/load_ipl_s390.c4
-rw-r--r--security/integrity/platform_certs/load_powerpc.c34
-rw-r--r--security/integrity/platform_certs/machine_keyring.c22
-rw-r--r--security/keys/encrypted-keys/encrypted.c2
-rw-r--r--security/keys/internal.h7
-rw-r--r--security/keys/key.c1
-rw-r--r--security/keys/keyctl.c11
-rw-r--r--security/keys/request_key.c35
-rw-r--r--security/keys/request_key_auth.c2
-rw-r--r--security/keys/sysctl.c2
-rw-r--r--security/keys/trusted-keys/trusted_core.c31
-rw-r--r--security/keys/trusted-keys/trusted_tee.c64
-rw-r--r--security/keys/trusted-keys/trusted_tpm2.c2
-rw-r--r--security/landlock/Kconfig1
-rw-r--r--security/landlock/Makefile2
-rw-r--r--security/landlock/fs.c232
-rw-r--r--security/landlock/limits.h6
-rw-r--r--security/landlock/net.c200
-rw-r--r--security/landlock/net.h33
-rw-r--r--security/landlock/ruleset.c405
-rw-r--r--security/landlock/ruleset.h187
-rw-r--r--security/landlock/setup.c2
-rw-r--r--security/landlock/syscalls.c158
-rw-r--r--security/loadpin/loadpin.c3
-rw-r--r--security/security.c118
-rw-r--r--security/selinux/Kconfig35
-rw-r--r--security/selinux/Makefile2
-rw-r--r--security/selinux/avc.c17
-rw-r--r--security/selinux/hooks.c198
-rw-r--r--security/selinux/include/avc.h2
-rw-r--r--security/selinux/include/avc_ss.h2
-rw-r--r--security/selinux/include/objsec.h4
-rw-r--r--security/selinux/include/policycap_names.h2
-rw-r--r--security/selinux/include/security.h45
-rw-r--r--security/selinux/netif.c4
-rw-r--r--security/selinux/netlabel.c1
-rw-r--r--security/selinux/nlmsgtab.c3
-rw-r--r--security/selinux/selinuxfs.c30
-rw-r--r--security/selinux/ss/avtab.c101
-rw-r--r--security/selinux/ss/avtab.h12
-rw-r--r--security/selinux/ss/conditional.c4
-rw-r--r--security/selinux/ss/constraint.h2
-rw-r--r--security/selinux/ss/context.h2
-rw-r--r--security/selinux/ss/ebitmap.c2
-rw-r--r--security/selinux/ss/ebitmap.h2
-rw-r--r--security/selinux/ss/hashtab.c13
-rw-r--r--security/selinux/ss/hashtab.h9
-rw-r--r--security/selinux/ss/mls.c12
-rw-r--r--security/selinux/ss/mls.h2
-rw-r--r--security/selinux/ss/mls_types.h2
-rw-r--r--security/selinux/ss/policydb.c106
-rw-r--r--security/selinux/ss/policydb.h9
-rw-r--r--security/selinux/ss/services.c54
-rw-r--r--security/selinux/ss/services.h4
-rw-r--r--security/selinux/ss/sidtab.c4
-rw-r--r--security/selinux/ss/sidtab.h2
-rw-r--r--security/selinux/ss/symtab.c4
-rw-r--r--security/selinux/ss/symtab.h4
-rw-r--r--security/selinux/status.c6
-rw-r--r--security/selinux/xfrm.c2
-rw-r--r--security/smack/smack.h2
-rw-r--r--security/smack/smack_lsm.c119
-rw-r--r--security/smack/smackfs.c4
-rw-r--r--security/tomoyo/common.c1
-rw-r--r--security/tomoyo/common.h6
-rw-r--r--security/tomoyo/domain.c5
-rw-r--r--security/tomoyo/tomoyo.c2
91 files changed, 1833 insertions, 933 deletions
diff --git a/security/Kconfig.hardening b/security/Kconfig.hardening
index 0f295961e773..2cff851ebfd7 100644
--- a/security/Kconfig.hardening
+++ b/security/Kconfig.hardening
@@ -279,6 +279,29 @@ config ZERO_CALL_USED_REGS
endmenu
+menu "Hardening of kernel data structures"
+
+config LIST_HARDENED
+ bool "Check integrity of linked list manipulation"
+ help
+ Minimal integrity checking in the linked-list manipulation routines
+ to catch memory corruptions that are not guaranteed to result in an
+ immediate access fault.
+
+ If unsure, say N.
+
+config BUG_ON_DATA_CORRUPTION
+ bool "Trigger a BUG when data corruption is detected"
+ select LIST_HARDENED
+ help
+ Select this option if the kernel should BUG when it encounters
+ data corruption in kernel memory structures when they get checked
+ for validity.
+
+ If unsure, say N.
+
+endmenu
+
config CC_HAS_RANDSTRUCT
def_bool $(cc-option,-frandomize-layout-seed-file=/dev/null)
# Randstruct was first added in Clang 15, but it isn't safe to use until
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
index a608a6bd76c5..38650e52ef57 100644
--- a/security/apparmor/apparmorfs.c
+++ b/security/apparmor/apparmorfs.c
@@ -226,7 +226,7 @@ static int __aafs_setup_d_inode(struct inode *dir, struct dentry *dentry,
inode->i_ino = get_next_ino();
inode->i_mode = mode;
- inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode);
+ simple_inode_init_ts(inode);
inode->i_private = data;
if (S_ISDIR(mode)) {
inode->i_op = iops ? iops : &simple_dir_inode_operations;
@@ -1553,8 +1553,12 @@ void __aafs_profile_migrate_dents(struct aa_profile *old,
for (i = 0; i < AAFS_PROF_SIZEOF; i++) {
new->dents[i] = old->dents[i];
- if (new->dents[i])
- new->dents[i]->d_inode->i_mtime = current_time(new->dents[i]->d_inode);
+ if (new->dents[i]) {
+ struct inode *inode = d_inode(new->dents[i]);
+
+ inode_set_mtime_to_ts(inode,
+ inode_set_ctime_current(inode));
+ }
old->dents[i] = NULL;
}
}
@@ -2559,7 +2563,7 @@ static int aa_mk_null_file(struct dentry *parent)
inode->i_ino = get_next_ino();
inode->i_mode = S_IFCHR | S_IRUGO | S_IWUGO;
- inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode);
+ simple_inode_init_ts(inode);
init_special_inode(inode, S_IFCHR | S_IRUGO | S_IWUGO,
MKDEV(MEM_MAJOR, 3));
d_instantiate(dentry, inode);
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index 4d34180e9799..4981bdf02993 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -156,7 +156,7 @@ static int apparmor_ptrace_traceme(struct task_struct *parent)
}
/* Derived from security/commoncap.c:cap_capget */
-static int apparmor_capget(struct task_struct *target, kernel_cap_t *effective,
+static int apparmor_capget(const struct task_struct *target, kernel_cap_t *effective,
kernel_cap_t *inheritable, kernel_cap_t *permitted)
{
struct aa_label *label;
@@ -880,7 +880,7 @@ fail:
* apparmor_bprm_committing_creds - do task cleanup on committing new creds
* @bprm: binprm for the exec (NOT NULL)
*/
-static void apparmor_bprm_committing_creds(struct linux_binprm *bprm)
+static void apparmor_bprm_committing_creds(const struct linux_binprm *bprm)
{
struct aa_label *label = aa_current_raw_label();
struct aa_label *new_label = cred_label(bprm->cred);
@@ -902,7 +902,7 @@ static void apparmor_bprm_committing_creds(struct linux_binprm *bprm)
* apparmor_bprm_committed_creds() - do cleanup after new creds committed
* @bprm: binprm for the exec (NOT NULL)
*/
-static void apparmor_bprm_committed_creds(struct linux_binprm *bprm)
+static void apparmor_bprm_committed_creds(const struct linux_binprm *bprm)
{
/* clear out temporary/transitional state from the context */
aa_clear_task_ctx_trans(task_ctx(current));
diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c
index 3fad34b68fdc..47ec097d6741 100644
--- a/security/apparmor/policy_unpack.c
+++ b/security/apparmor/policy_unpack.c
@@ -87,10 +87,13 @@ void __aa_loaddata_update(struct aa_loaddata *data, long revision)
data->revision = revision;
if ((data->dents[AAFS_LOADDATA_REVISION])) {
- d_inode(data->dents[AAFS_LOADDATA_DIR])->i_mtime =
- current_time(d_inode(data->dents[AAFS_LOADDATA_DIR]));
- d_inode(data->dents[AAFS_LOADDATA_REVISION])->i_mtime =
- current_time(d_inode(data->dents[AAFS_LOADDATA_REVISION]));
+ struct inode *inode;
+
+ inode = d_inode(data->dents[AAFS_LOADDATA_DIR]);
+ inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
+
+ inode = d_inode(data->dents[AAFS_LOADDATA_REVISION]);
+ inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
}
}
diff --git a/security/commoncap.c b/security/commoncap.c
index ab5742ab4362..8e8c630ce204 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -197,7 +197,7 @@ out:
* This function retrieves the capabilities of the nominated task and returns
* them to the caller.
*/
-int cap_capget(struct task_struct *target, kernel_cap_t *effective,
+int cap_capget(const struct task_struct *target, kernel_cap_t *effective,
kernel_cap_t *inheritable, kernel_cap_t *permitted)
{
const struct cred *cred;
@@ -720,7 +720,7 @@ int get_vfs_caps_from_disk(struct mnt_idmap *idmap,
* its xattrs and, if present, apply them to the proposed credentials being
* constructed by execve().
*/
-static int get_file_caps(struct linux_binprm *bprm, struct file *file,
+static int get_file_caps(struct linux_binprm *bprm, const struct file *file,
bool *effective, bool *has_fcap)
{
int rc = 0;
@@ -882,7 +882,7 @@ static inline bool nonroot_raised_pE(struct cred *new, const struct cred *old,
*
* Return: 0 if successful, -ve on error.
*/
-int cap_bprm_creds_from_file(struct linux_binprm *bprm, struct file *file)
+int cap_bprm_creds_from_file(struct linux_binprm *bprm, const struct file *file)
{
/* Process setpcap binaries and capabilities for uid 0 */
const struct cred *old = current_cred();
diff --git a/security/inode.c b/security/inode.c
index 6c326939750d..9e7cde913667 100644
--- a/security/inode.c
+++ b/security/inode.c
@@ -145,7 +145,7 @@ static struct dentry *securityfs_create_dentry(const char *name, umode_t mode,
inode->i_ino = get_next_ino();
inode->i_mode = mode;
- inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode);
+ simple_inode_init_ts(inode);
inode->i_private = data;
if (S_ISDIR(mode)) {
inode->i_op = &simple_dir_inode_operations;
diff --git a/security/integrity/Kconfig b/security/integrity/Kconfig
index ec6e0d789da1..3c45f4f3455f 100644
--- a/security/integrity/Kconfig
+++ b/security/integrity/Kconfig
@@ -34,10 +34,10 @@ config INTEGRITY_ASYMMETRIC_KEYS
bool "Enable asymmetric keys support"
depends on INTEGRITY_SIGNATURE
default n
- select ASYMMETRIC_KEY_TYPE
- select ASYMMETRIC_PUBLIC_KEY_SUBTYPE
- select CRYPTO_RSA
- select X509_CERTIFICATE_PARSER
+ select ASYMMETRIC_KEY_TYPE
+ select ASYMMETRIC_PUBLIC_KEY_SUBTYPE
+ select CRYPTO_RSA
+ select X509_CERTIFICATE_PARSER
help
This option enables digital signature verification using
asymmetric keys.
@@ -53,26 +53,26 @@ config INTEGRITY_TRUSTED_KEYRING
keyring.
config INTEGRITY_PLATFORM_KEYRING
- bool "Provide keyring for platform/firmware trusted keys"
- depends on INTEGRITY_ASYMMETRIC_KEYS
- depends on SYSTEM_BLACKLIST_KEYRING
- help
- Provide a separate, distinct keyring for platform trusted keys, which
- the kernel automatically populates during initialization from values
- provided by the platform for verifying the kexec'ed kerned image
- and, possibly, the initramfs signature.
+ bool "Provide keyring for platform/firmware trusted keys"
+ depends on INTEGRITY_ASYMMETRIC_KEYS
+ depends on SYSTEM_BLACKLIST_KEYRING
+ help
+ Provide a separate, distinct keyring for platform trusted keys, which
+ the kernel automatically populates during initialization from values
+ provided by the platform for verifying the kexec'ed kerned image
+ and, possibly, the initramfs signature.
config INTEGRITY_MACHINE_KEYRING
bool "Provide a keyring to which Machine Owner Keys may be added"
depends on SECONDARY_TRUSTED_KEYRING
depends on INTEGRITY_ASYMMETRIC_KEYS
depends on SYSTEM_BLACKLIST_KEYRING
- depends on LOAD_UEFI_KEYS
+ depends on LOAD_UEFI_KEYS || LOAD_PPC_KEYS
help
- If set, provide a keyring to which Machine Owner Keys (MOK) may
- be added. This keyring shall contain just MOK keys. Unlike keys
- in the platform keyring, keys contained in the .machine keyring will
- be trusted within the kernel.
+ If set, provide a keyring to which Machine Owner Keys (MOK) may
+ be added. This keyring shall contain just MOK keys. Unlike keys
+ in the platform keyring, keys contained in the .machine keyring will
+ be trusted within the kernel.
config INTEGRITY_CA_MACHINE_KEYRING
bool "Enforce Machine Keyring CA Restrictions"
@@ -97,14 +97,14 @@ config INTEGRITY_CA_MACHINE_KEYRING_MAX
.platform keyring.
config LOAD_UEFI_KEYS
- depends on INTEGRITY_PLATFORM_KEYRING
- depends on EFI
- def_bool y
+ depends on INTEGRITY_PLATFORM_KEYRING
+ depends on EFI
+ def_bool y
config LOAD_IPL_KEYS
- depends on INTEGRITY_PLATFORM_KEYRING
- depends on S390
- def_bool y
+ depends on INTEGRITY_PLATFORM_KEYRING
+ depends on S390
+ def_bool y
config LOAD_PPC_KEYS
bool "Enable loading of platform and blacklisted keys for POWER"
diff --git a/security/integrity/digsig.c b/security/integrity/digsig.c
index 6f31ffe23c48..df387de29bfa 100644
--- a/security/integrity/digsig.c
+++ b/security/integrity/digsig.c
@@ -34,9 +34,9 @@ static const char * const keyring_name[INTEGRITY_KEYRING_MAX] = {
};
#ifdef CONFIG_IMA_KEYRINGS_PERMIT_SIGNED_BY_BUILTIN_OR_SECONDARY
-#define restrict_link_to_ima restrict_link_by_builtin_and_secondary_trusted
+#define restrict_link_to_ima restrict_link_by_digsig_builtin_and_secondary
#else
-#define restrict_link_to_ima restrict_link_by_builtin_trusted
+#define restrict_link_to_ima restrict_link_by_digsig_builtin
#endif
static struct key *integrity_keyring_from_id(const unsigned int id)
@@ -113,7 +113,7 @@ static int __init __integrity_init_keyring(const unsigned int id,
} else {
if (id == INTEGRITY_KEYRING_PLATFORM)
set_platform_trusted_keys(keyring[id]);
- if (id == INTEGRITY_KEYRING_MACHINE && trust_moklist())
+ if (id == INTEGRITY_KEYRING_MACHINE && imputed_trust_enabled())
set_machine_trusted_keys(keyring[id]);
if (id == INTEGRITY_KEYRING_IMA)
load_module_cert(keyring[id]);
diff --git a/security/integrity/evm/Kconfig b/security/integrity/evm/Kconfig
index a6e19d23e700..fba9ee359bc9 100644
--- a/security/integrity/evm/Kconfig
+++ b/security/integrity/evm/Kconfig
@@ -64,7 +64,8 @@ config EVM_LOAD_X509
This option enables X509 certificate loading from the kernel
onto the '.evm' trusted keyring. A public key can be used to
- verify EVM integrity starting from the 'init' process.
+ verify EVM integrity starting from the 'init' process. The
+ key must have digitalSignature usage set.
config EVM_X509_PATH
string "EVM X509 certificate path"
diff --git a/security/integrity/evm/evm.h b/security/integrity/evm/evm.h
index f8b8c5004fc7..53bd7fec93fa 100644
--- a/security/integrity/evm/evm.h
+++ b/security/integrity/evm/evm.h
@@ -46,6 +46,8 @@ struct evm_digest {
char digest[IMA_MAX_DIGEST_SIZE];
} __packed;
+int evm_protected_xattr(const char *req_xattr_name);
+
int evm_init_key(void);
int evm_update_evmxattr(struct dentry *dentry,
const char *req_xattr_name,
@@ -58,7 +60,7 @@ int evm_calc_hash(struct dentry *dentry, const char *req_xattr_name,
const char *req_xattr_value,
size_t req_xattr_value_len, char type,
struct evm_digest *data);
-int evm_init_hmac(struct inode *inode, const struct xattr *xattr,
+int evm_init_hmac(struct inode *inode, const struct xattr *xattrs,
char *hmac_val);
int evm_init_secfs(void);
diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c
index 0dae649f3740..b1ffd4cc0b44 100644
--- a/security/integrity/evm/evm_crypto.c
+++ b/security/integrity/evm/evm_crypto.c
@@ -385,10 +385,11 @@ int evm_update_evmxattr(struct dentry *dentry, const char *xattr_name,
return rc;
}
-int evm_init_hmac(struct inode *inode, const struct xattr *lsm_xattr,
+int evm_init_hmac(struct inode *inode, const struct xattr *xattrs,
char *hmac_val)
{
struct shash_desc *desc;
+ const struct xattr *xattr;
desc = init_desc(EVM_XATTR_HMAC, HASH_ALGO_SHA1);
if (IS_ERR(desc)) {
@@ -396,7 +397,13 @@ int evm_init_hmac(struct inode *inode, const struct xattr *lsm_xattr,
return PTR_ERR(desc);
}
- crypto_shash_update(desc, lsm_xattr->value, lsm_xattr->value_len);
+ for (xattr = xattrs; xattr->name; xattr++) {
+ if (!evm_protected_xattr(xattr->name))
+ continue;
+
+ crypto_shash_update(desc, xattr->value, xattr->value_len);
+ }
+
hmac_add_misc(desc, inode, EVM_XATTR_HMAC, hmac_val);
kfree(desc);
return 0;
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
index c9b6e2a43478..894570fe39bc 100644
--- a/security/integrity/evm/evm_main.c
+++ b/security/integrity/evm/evm_main.c
@@ -14,17 +14,17 @@
#define pr_fmt(fmt) "EVM: "fmt
#include <linux/init.h>
-#include <linux/crypto.h>
#include <linux/audit.h>
#include <linux/xattr.h>
#include <linux/integrity.h>
#include <linux/evm.h>
#include <linux/magic.h>
#include <linux/posix_acl_xattr.h>
+#include <linux/lsm_hooks.h>
#include <crypto/hash.h>
#include <crypto/hash_info.h>
-#include <crypto/algapi.h>
+#include <crypto/utils.h>
#include "evm.h"
int evm_initialized;
@@ -305,7 +305,7 @@ static int evm_protected_xattr_common(const char *req_xattr_name,
return found;
}
-static int evm_protected_xattr(const char *req_xattr_name)
+int evm_protected_xattr(const char *req_xattr_name)
{
return evm_protected_xattr_common(req_xattr_name, false);
}
@@ -866,23 +866,47 @@ void evm_inode_post_setattr(struct dentry *dentry, int ia_valid)
/*
* evm_inode_init_security - initializes security.evm HMAC value
*/
-int evm_inode_init_security(struct inode *inode,
- const struct xattr *lsm_xattr,
- struct xattr *evm_xattr)
+int evm_inode_init_security(struct inode *inode, struct inode *dir,
+ const struct qstr *qstr, struct xattr *xattrs,
+ int *xattr_count)
{
struct evm_xattr *xattr_data;
+ struct xattr *xattr, *evm_xattr;
+ bool evm_protected_xattrs = false;
int rc;
- if (!(evm_initialized & EVM_INIT_HMAC) ||
- !evm_protected_xattr(lsm_xattr->name))
+ if (!(evm_initialized & EVM_INIT_HMAC) || !xattrs)
return 0;
+ /*
+ * security_inode_init_security() makes sure that the xattrs array is
+ * contiguous, there is enough space for security.evm, and that there is
+ * a terminator at the end of the array.
+ */
+ for (xattr = xattrs; xattr->name; xattr++) {
+ if (evm_protected_xattr(xattr->name))
+ evm_protected_xattrs = true;
+ }
+
+ /* EVM xattr not needed. */
+ if (!evm_protected_xattrs)
+ return 0;
+
+ evm_xattr = lsm_get_xattr_slot(xattrs, xattr_count);
+ /*
+ * Array terminator (xattr name = NULL) must be the first non-filled
+ * xattr slot.
+ */
+ WARN_ONCE(evm_xattr != xattr,
+ "%s: xattrs terminator is not the first non-filled slot\n",
+ __func__);
+
xattr_data = kzalloc(sizeof(*xattr_data), GFP_NOFS);
if (!xattr_data)
return -ENOMEM;
xattr_data->data.type = EVM_XATTR_HMAC;
- rc = evm_init_hmac(inode, lsm_xattr, xattr_data->digest);
+ rc = evm_init_hmac(inode, xattrs, xattr_data->digest);
if (rc < 0)
goto out;
diff --git a/security/integrity/iint.c b/security/integrity/iint.c
index a462df827de2..d4419a2a1e24 100644
--- a/security/integrity/iint.c
+++ b/security/integrity/iint.c
@@ -23,7 +23,7 @@
static struct rb_root integrity_iint_tree = RB_ROOT;
static DEFINE_RWLOCK(integrity_iint_lock);
-static struct kmem_cache *iint_cache __read_mostly;
+static struct kmem_cache *iint_cache __ro_after_init;
struct dentry *integrity_dir;
@@ -66,9 +66,32 @@ struct integrity_iint_cache *integrity_iint_find(struct inode *inode)
return iint;
}
-static void iint_free(struct integrity_iint_cache *iint)
+#define IMA_MAX_NESTING (FILESYSTEM_MAX_STACK_DEPTH+1)
+
+/*
+ * It is not clear that IMA should be nested at all, but as long is it measures
+ * files both on overlayfs and on underlying fs, we need to annotate the iint
+ * mutex to avoid lockdep false positives related to IMA + overlayfs.
+ * See ovl_lockdep_annotate_inode_mutex_key() for more details.
+ */
+static inline void iint_lockdep_annotate(struct integrity_iint_cache *iint,
+ struct inode *inode)
+{
+#ifdef CONFIG_LOCKDEP
+ static struct lock_class_key iint_mutex_key[IMA_MAX_NESTING];
+
+ int depth = inode->i_sb->s_stack_depth;
+
+ if (WARN_ON_ONCE(depth < 0 || depth >= IMA_MAX_NESTING))
+ depth = 0;
+
+ lockdep_set_class(&iint->mutex, &iint_mutex_key[depth]);
+#endif
+}
+
+static void iint_init_always(struct integrity_iint_cache *iint,
+ struct inode *inode)
{
- kfree(iint->ima_hash);
iint->ima_hash = NULL;
iint->version = 0;
iint->flags = 0UL;
@@ -80,6 +103,14 @@ static void iint_free(struct integrity_iint_cache *iint)
iint->ima_creds_status = INTEGRITY_UNKNOWN;
iint->evm_status = INTEGRITY_UNKNOWN;
iint->measured_pcrs = 0;
+ mutex_init(&iint->mutex);
+ iint_lockdep_annotate(iint, inode);
+}
+
+static void iint_free(struct integrity_iint_cache *iint)
+{
+ kfree(iint->ima_hash);
+ mutex_destroy(&iint->mutex);
kmem_cache_free(iint_cache, iint);
}
@@ -104,6 +135,8 @@ struct integrity_iint_cache *integrity_inode_get(struct inode *inode)
if (!iint)
return NULL;
+ iint_init_always(iint, inode);
+
write_lock(&integrity_iint_lock);
p = &integrity_iint_tree.rb_node;
@@ -153,25 +186,18 @@ void integrity_inode_free(struct inode *inode)
iint_free(iint);
}
-static void init_once(void *foo)
+static void iint_init_once(void *foo)
{
struct integrity_iint_cache *iint = (struct integrity_iint_cache *) foo;
memset(iint, 0, sizeof(*iint));
- iint->ima_file_status = INTEGRITY_UNKNOWN;
- iint->ima_mmap_status = INTEGRITY_UNKNOWN;
- iint->ima_bprm_status = INTEGRITY_UNKNOWN;
- iint->ima_read_status = INTEGRITY_UNKNOWN;
- iint->ima_creds_status = INTEGRITY_UNKNOWN;
- iint->evm_status = INTEGRITY_UNKNOWN;
- mutex_init(&iint->mutex);
}
static int __init integrity_iintcache_init(void)
{
iint_cache =
kmem_cache_create("iint_cache", sizeof(struct integrity_iint_cache),
- 0, SLAB_PANIC, init_once);
+ 0, SLAB_PANIC, iint_init_once);
return 0;
}
DEFINE_LSM(integrity) = {
diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig
index 60a511c6b583..a6bd817efc1a 100644
--- a/security/integrity/ima/Kconfig
+++ b/security/integrity/ima/Kconfig
@@ -29,9 +29,11 @@ config IMA
to learn more about IMA.
If unsure, say N.
+if IMA
+
config IMA_KEXEC
bool "Enable carrying the IMA measurement list across a soft boot"
- depends on IMA && TCG_TPM && HAVE_IMA_KEXEC
+ depends on TCG_TPM && HAVE_IMA_KEXEC
default n
help
TPM PCRs are only reset on a hard reboot. In order to validate
@@ -43,7 +45,6 @@ config IMA_KEXEC
config IMA_MEASURE_PCR_IDX
int
- depends on IMA
range 8 14
default 10
help
@@ -53,7 +54,7 @@ config IMA_MEASURE_PCR_IDX
config IMA_LSM_RULES
bool
- depends on IMA && AUDIT && (SECURITY_SELINUX || SECURITY_SMACK || SECURITY_APPARMOR)
+ depends on AUDIT && (SECURITY_SELINUX || SECURITY_SMACK || SECURITY_APPARMOR)
default y
help
Disabling this option will disregard LSM based policy rules.
@@ -61,7 +62,6 @@ config IMA_LSM_RULES
choice
prompt "Default template"
default IMA_NG_TEMPLATE
- depends on IMA
help
Select the default IMA measurement template.
@@ -80,14 +80,12 @@ endchoice
config IMA_DEFAULT_TEMPLATE
string
- depends on IMA
default "ima-ng" if IMA_NG_TEMPLATE
default "ima-sig" if IMA_SIG_TEMPLATE
choice
prompt "Default integrity hash algorithm"
default IMA_DEFAULT_HASH_SHA1
- depends on IMA
help
Select the default hash algorithm used for the measurement
list, integrity appraisal and audit log. The compiled default
@@ -117,7 +115,6 @@ endchoice
config IMA_DEFAULT_HASH
string
- depends on IMA
default "sha1" if IMA_DEFAULT_HASH_SHA1
default "sha256" if IMA_DEFAULT_HASH_SHA256
default "sha512" if IMA_DEFAULT_HASH_SHA512
@@ -126,7 +123,6 @@ config IMA_DEFAULT_HASH
config IMA_WRITE_POLICY
bool "Enable multiple writes to the IMA policy"
- depends on IMA
default n
help
IMA policy can now be updated multiple times. The new rules get
@@ -137,7 +133,6 @@ config IMA_WRITE_POLICY
config IMA_READ_POLICY
bool "Enable reading back the current IMA policy"
- depends on IMA
default y if IMA_WRITE_POLICY
default n if !IMA_WRITE_POLICY
help
@@ -147,7 +142,6 @@ config IMA_READ_POLICY
config IMA_APPRAISE
bool "Appraise integrity measurements"
- depends on IMA
default n
help
This option enables local measurement integrity appraisal.
@@ -248,18 +242,6 @@ config IMA_APPRAISE_MODSIG
The modsig keyword can be used in the IMA policy to allow a hook
to accept such signatures.
-config IMA_TRUSTED_KEYRING
- bool "Require all keys on the .ima keyring be signed (deprecated)"
- depends on IMA_APPRAISE && SYSTEM_TRUSTED_KEYRING
- depends on INTEGRITY_ASYMMETRIC_KEYS
- select INTEGRITY_TRUSTED_KEYRING
- default y
- help
- This option requires that all keys added to the .ima
- keyring be signed by a key on the system trusted keyring.
-
- This option is deprecated in favor of INTEGRITY_TRUSTED_KEYRING
-
config IMA_KEYRINGS_PERMIT_SIGNED_BY_BUILTIN_OR_SECONDARY
bool "Permit keys validly signed by a built-in or secondary CA cert (EXPERIMENTAL)"
depends on SYSTEM_TRUSTED_KEYRING
@@ -270,7 +252,8 @@ config IMA_KEYRINGS_PERMIT_SIGNED_BY_BUILTIN_OR_SECONDARY
help
Keys may be added to the IMA or IMA blacklist keyrings, if the
key is validly signed by a CA cert in the system built-in or
- secondary trusted keyrings.
+ secondary trusted keyrings. The key must also have the
+ digitalSignature usage set.
Intermediate keys between those the kernel has compiled in and the
IMA keys to be added may be added to the system secondary keyring,
@@ -280,7 +263,7 @@ config IMA_KEYRINGS_PERMIT_SIGNED_BY_BUILTIN_OR_SECONDARY
config IMA_BLACKLIST_KEYRING
bool "Create IMA machine owner blacklist keyrings (EXPERIMENTAL)"
depends on SYSTEM_TRUSTED_KEYRING
- depends on IMA_TRUSTED_KEYRING
+ depends on INTEGRITY_TRUSTED_KEYRING
default n
help
This option creates an IMA blacklist keyring, which contains all
@@ -290,7 +273,7 @@ config IMA_BLACKLIST_KEYRING
config IMA_LOAD_X509
bool "Load X509 certificate onto the '.ima' trusted keyring"
- depends on IMA_TRUSTED_KEYRING
+ depends on INTEGRITY_TRUSTED_KEYRING
default n
help
File signature verification is based on the public keys
@@ -315,7 +298,6 @@ config IMA_APPRAISE_SIGNED_INIT
config IMA_MEASURE_ASYMMETRIC_KEYS
bool
- depends on IMA
depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y
default y
@@ -334,7 +316,8 @@ config IMA_SECURE_AND_OR_TRUSTED_BOOT
config IMA_DISABLE_HTABLE
bool "Disable htable to allow measurement of duplicate records"
- depends on IMA
default n
help
This option disables htable to allow measurement of duplicate records.
+
+endif
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index 452e80b541e5..597ea0c4d72f 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -243,6 +243,7 @@ int ima_collect_measurement(struct integrity_iint_cache *iint,
{
const char *audit_cause = "failed";
struct inode *inode = file_inode(file);
+ struct inode *real_inode = d_real_inode(file_dentry(file));
const char *filename = file->f_path.dentry->d_name.name;
struct ima_max_digest_data hash;
struct kstat stat;
@@ -302,6 +303,10 @@ int ima_collect_measurement(struct integrity_iint_cache *iint,
iint->ima_hash = tmpbuf;
memcpy(iint->ima_hash, &hash, length);
iint->version = i_version;
+ if (real_inode != inode) {
+ iint->real_ino = real_inode->i_ino;
+ iint->real_dev = real_inode->i_sb->s_dev;
+ }
/* Possibly temporary failure due to type of read (eg. O_DIRECT) */
if (!result)
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index 491c1aca0b1c..870dde67707b 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -458,11 +458,13 @@ int ima_check_blacklist(struct integrity_iint_cache *iint,
ima_get_modsig_digest(modsig, &hash_algo, &digest, &digestsize);
rc = is_binary_blacklisted(digest, digestsize);
- if ((rc == -EPERM) && (iint->flags & IMA_MEASURE))
- process_buffer_measurement(&nop_mnt_idmap, NULL, digest, digestsize,
- "blacklisted-hash", NONE,
- pcr, NULL, false, NULL, 0);
- }
+ } else if (iint->flags & IMA_DIGSIG_REQUIRED && iint->ima_hash)
+ rc = is_binary_blacklisted(iint->ima_hash->digest, iint->ima_hash->length);
+
+ if ((rc == -EPERM) && (iint->flags & IMA_MEASURE))
+ process_buffer_measurement(&nop_mnt_idmap, NULL, digest, digestsize,
+ "blacklisted-hash", NONE,
+ pcr, NULL, false, NULL, 0);
return rc;
}
diff --git a/security/integrity/ima/ima_efi.c b/security/integrity/ima/ima_efi.c
index 9db66fe310d4..138029bfcce1 100644
--- a/security/integrity/ima/ima_efi.c
+++ b/security/integrity/ima/ima_efi.c
@@ -58,6 +58,9 @@ static const char * const sb_arch_rules[] = {
#if !IS_ENABLED(CONFIG_MODULE_SIG)
"appraise func=MODULE_CHECK appraise_type=imasig",
#endif
+#if IS_ENABLED(CONFIG_INTEGRITY_MACHINE_KEYRING) && IS_ENABLED(CONFIG_IMA_KEYRINGS_PERMIT_SIGNED_BY_BUILTIN_OR_SECONDARY)
+ "appraise func=POLICY_CHECK appraise_type=imasig",
+#endif
"measure func=MODULE_CHECK",
NULL
};
diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c
index 419dc405c831..ad133fe120db 100644
--- a/security/integrity/ima/ima_kexec.c
+++ b/security/integrity/ima/ima_kexec.c
@@ -77,7 +77,7 @@ out:
* Called during kexec_file_load so that IMA can add a segment to the kexec
* image for the measurement list for the next kernel.
*
- * This function assumes that kexec_mutex is held.
+ * This function assumes that kexec_lock is held.
*/
void ima_add_kexec_buffer(struct kimage *image)
{
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 365db0e43d7c..cc1217ac2c6f 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -25,6 +25,7 @@
#include <linux/xattr.h>
#include <linux/ima.h>
#include <linux/fs.h>
+#include <linux/iversion.h>
#include "ima.h"
@@ -207,7 +208,7 @@ static int process_measurement(struct file *file, const struct cred *cred,
u32 secid, char *buf, loff_t size, int mask,
enum ima_hooks func)
{
- struct inode *inode = file_inode(file);
+ struct inode *backing_inode, *inode = file_inode(file);
struct integrity_iint_cache *iint = NULL;
struct ima_template_desc *template_desc = NULL;
char *pathbuf = NULL;
@@ -284,6 +285,19 @@ static int process_measurement(struct file *file, const struct cred *cred,
iint->measured_pcrs = 0;
}
+ /* Detect and re-evaluate changes made to the backing file. */
+ backing_inode = d_real_inode(file_dentry(file));
+ if (backing_inode != inode &&
+ (action & IMA_DO_MASK) && (iint->flags & IMA_DONE_MASK)) {
+ if (!IS_I_VERSION(backing_inode) ||
+ backing_inode->i_sb->s_dev != iint->real_dev ||
+ backing_inode->i_ino != iint->real_ino ||
+ !inode_eq_iversion(backing_inode, iint->version)) {
+ iint->flags &= ~IMA_DONE_MASK;
+ iint->measured_pcrs = 0;
+ }
+ }
+
/* Determine if already appraised/measured based on bitmask
* (IMA_MEASURE, IMA_MEASURED, IMA_XXXX_APPRAISE, IMA_XXXX_APPRAISED,
* IMA_AUDIT, IMA_AUDITED)
diff --git a/security/integrity/ima/ima_modsig.c b/security/integrity/ima/ima_modsig.c
index 3e7bee30080f..3265d744d5ce 100644
--- a/security/integrity/ima/ima_modsig.c
+++ b/security/integrity/ima/ima_modsig.c
@@ -29,7 +29,7 @@ struct modsig {
* storing the signature.
*/
int raw_pkcs7_len;
- u8 raw_pkcs7[];
+ u8 raw_pkcs7[] __counted_by(raw_pkcs7_len);
};
/*
@@ -65,10 +65,11 @@ int ima_read_modsig(enum ima_hooks func, const void *buf, loff_t buf_len,
buf_len -= sig_len + sizeof(*sig);
/* Allocate sig_len additional bytes to hold the raw PKCS#7 data. */
- hdr = kzalloc(sizeof(*hdr) + sig_len, GFP_KERNEL);
+ hdr = kzalloc(struct_size(hdr, raw_pkcs7, sig_len), GFP_KERNEL);
if (!hdr)
return -ENOMEM;
+ hdr->raw_pkcs7_len = sig_len;
hdr->pkcs7_msg = pkcs7_parse_message(buf + buf_len, sig_len);
if (IS_ERR(hdr->pkcs7_msg)) {
rc = PTR_ERR(hdr->pkcs7_msg);
@@ -77,7 +78,6 @@ int ima_read_modsig(enum ima_hooks func, const void *buf, loff_t buf_len,
}
memcpy(hdr->raw_pkcs7, buf + buf_len, sig_len);
- hdr->raw_pkcs7_len = sig_len;
/* We don't know the hash algorithm yet. */
hdr->hash_algo = HASH_ALGO__LAST;
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index c9b3bd8f1bb9..f69062617754 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -68,7 +68,7 @@ enum policy_rule_list { IMA_DEFAULT_POLICY = 1, IMA_CUSTOM_POLICY };
struct ima_rule_opt_list {
size_t count;
- char *items[];
+ char *items[] __counted_by(count);
};
/*
@@ -342,6 +342,7 @@ static struct ima_rule_opt_list *ima_alloc_rule_opt_list(const substring_t *src)
kfree(src_copy);
return ERR_PTR(-ENOMEM);
}
+ opt_list->count = count;
/*
* strsep() has already replaced all instances of '|' with '\0',
@@ -357,7 +358,6 @@ static struct ima_rule_opt_list *ima_alloc_rule_opt_list(const substring_t *src)
opt_list->items[i] = cur;
cur = strchr(cur, '\0') + 1;
}
- opt_list->count = count;
return opt_list;
}
@@ -1280,7 +1280,7 @@ static bool ima_validate_rule(struct ima_rule_entry *entry)
IMA_FSNAME | IMA_GID | IMA_EGID |
IMA_FGROUP | IMA_DIGSIG_REQUIRED |
IMA_PERMIT_DIRECTIO | IMA_VALIDATE_ALGOS |
- IMA_VERITY_REQUIRED))
+ IMA_CHECK_BLACKLIST | IMA_VERITY_REQUIRED))
return false;
break;
@@ -1355,7 +1355,7 @@ static bool ima_validate_rule(struct ima_rule_entry *entry)
/* Ensure that combinations of flags are compatible with each other */
if (entry->flags & IMA_CHECK_BLACKLIST &&
- !(entry->flags & IMA_MODSIG_ALLOWED))
+ !(entry->flags & IMA_DIGSIG_REQUIRED))
return false;
/*
@@ -1803,11 +1803,11 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
if (entry->flags & IMA_VERITY_REQUIRED)
result = -EINVAL;
else
- entry->flags |= IMA_DIGSIG_REQUIRED;
+ entry->flags |= IMA_DIGSIG_REQUIRED | IMA_CHECK_BLACKLIST;
} else if (strcmp(args[0].from, "sigv3") == 0) {
/* Only fsverity supports sigv3 for now */
if (entry->flags & IMA_VERITY_REQUIRED)
- entry->flags |= IMA_DIGSIG_REQUIRED;
+ entry->flags |= IMA_DIGSIG_REQUIRED | IMA_CHECK_BLACKLIST;
else
result = -EINVAL;
} else if (IS_ENABLED(CONFIG_IMA_APPRAISE_MODSIG) &&
@@ -1816,18 +1816,13 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
result = -EINVAL;
else
entry->flags |= IMA_DIGSIG_REQUIRED |
- IMA_MODSIG_ALLOWED;
+ IMA_MODSIG_ALLOWED | IMA_CHECK_BLACKLIST;
} else {
result = -EINVAL;
}
break;
case Opt_appraise_flag:
ima_log_string(ab, "appraise_flag", args[0].from);
- if (IS_ENABLED(CONFIG_IMA_APPRAISE_MODSIG) &&
- strstr(args[0].from, "blacklist"))
- entry->flags |= IMA_CHECK_BLACKLIST;
- else
- result = -EINVAL;
break;
case Opt_appraise_algos:
ima_log_string(ab, "appraise_algos", args[0].from);
@@ -2271,8 +2266,6 @@ int ima_policy_show(struct seq_file *m, void *v)
}
if (entry->flags & IMA_VERITY_REQUIRED)
seq_puts(m, "digest_type=verity ");
- if (entry->flags & IMA_CHECK_BLACKLIST)
- seq_puts(m, "appraise_flag=check_blacklist ");
if (entry->flags & IMA_PERMIT_DIRECTIO)
seq_puts(m, "permit_directio ");
rcu_read_unlock();
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
index 7167a6e99bdc..9561db7cf6b4 100644
--- a/security/integrity/integrity.h
+++ b/security/integrity/integrity.h
@@ -164,6 +164,8 @@ struct integrity_iint_cache {
unsigned long flags;
unsigned long measured_pcrs;
unsigned long atomic_flags;
+ unsigned long real_ino;
+ dev_t real_dev;
enum integrity_status ima_file_status:4;
enum integrity_status ima_mmap_status:4;
enum integrity_status ima_bprm_status:4;
@@ -320,13 +322,14 @@ static inline void __init add_to_platform_keyring(const char *source,
#ifdef CONFIG_INTEGRITY_MACHINE_KEYRING
void __init add_to_machine_keyring(const char *source, const void *data, size_t len);
-bool __init trust_moklist(void);
+bool __init imputed_trust_enabled(void);
#else
static inline void __init add_to_machine_keyring(const char *source,
const void *data, size_t len)
{
}
-static inline bool __init trust_moklist(void)
+
+static inline bool __init imputed_trust_enabled(void)
{
return false;
}
diff --git a/security/integrity/platform_certs/keyring_handler.c b/security/integrity/platform_certs/keyring_handler.c
index 8a1124e4d769..13ea17207902 100644
--- a/security/integrity/platform_certs/keyring_handler.c
+++ b/security/integrity/platform_certs/keyring_handler.c
@@ -61,7 +61,8 @@ __init efi_element_handler_t get_handler_for_db(const efi_guid_t *sig_type)
__init efi_element_handler_t get_handler_for_mok(const efi_guid_t *sig_type)
{
if (efi_guidcmp(*sig_type, efi_cert_x509_guid) == 0) {
- if (IS_ENABLED(CONFIG_INTEGRITY_MACHINE_KEYRING) && trust_moklist())
+ if (IS_ENABLED(CONFIG_INTEGRITY_MACHINE_KEYRING) &&
+ imputed_trust_enabled())
return add_to_machine_keyring;
else
return add_to_platform_keyring;
@@ -69,6 +70,22 @@ __init efi_element_handler_t get_handler_for_mok(const efi_guid_t *sig_type)
return NULL;
}
+__init efi_element_handler_t get_handler_for_ca_keys(const efi_guid_t *sig_type)
+{
+ if (efi_guidcmp(*sig_type, efi_cert_x509_guid) == 0)
+ return add_to_machine_keyring;
+
+ return NULL;
+}
+
+__init efi_element_handler_t get_handler_for_code_signing_keys(const efi_guid_t *sig_type)
+{
+ if (efi_guidcmp(*sig_type, efi_cert_x509_guid) == 0)
+ return add_to_secondary_keyring;
+
+ return NULL;
+}
+
/*
* Return the appropriate handler for particular signature list types found in
* the UEFI dbx and MokListXRT tables.
diff --git a/security/integrity/platform_certs/keyring_handler.h b/security/integrity/platform_certs/keyring_handler.h
index 212d894a8c0c..f92895cc50f6 100644
--- a/security/integrity/platform_certs/keyring_handler.h
+++ b/security/integrity/platform_certs/keyring_handler.h
@@ -30,6 +30,16 @@ efi_element_handler_t get_handler_for_db(const efi_guid_t *sig_type);
efi_element_handler_t get_handler_for_mok(const efi_guid_t *sig_type);
/*
+ * Return the handler for particular signature list types for CA keys.
+ */
+efi_element_handler_t get_handler_for_ca_keys(const efi_guid_t *sig_type);
+
+/*
+ * Return the handler for particular signature list types for code signing keys.
+ */
+efi_element_handler_t get_handler_for_code_signing_keys(const efi_guid_t *sig_type);
+
+/*
* Return the handler for particular signature list types found in the dbx.
*/
efi_element_handler_t get_handler_for_dbx(const efi_guid_t *sig_type);
diff --git a/security/integrity/platform_certs/load_ipl_s390.c b/security/integrity/platform_certs/load_ipl_s390.c
index e769dcb7ea94..c7c381a9ddaa 100644
--- a/security/integrity/platform_certs/load_ipl_s390.c
+++ b/security/integrity/platform_certs/load_ipl_s390.c
@@ -22,8 +22,8 @@ static int __init load_ipl_certs(void)
if (!ipl_cert_list_addr)
return 0;
- /* Copy the certificates to the system keyring */
- ptr = (void *) ipl_cert_list_addr;
+ /* Copy the certificates to the platform keyring */
+ ptr = __va(ipl_cert_list_addr);
end = ptr + ipl_cert_list_size;
while ((void *) ptr < end) {
len = *(unsigned int *) ptr;
diff --git a/security/integrity/platform_certs/load_powerpc.c b/security/integrity/platform_certs/load_powerpc.c
index 170789dc63d2..c85febca3343 100644
--- a/security/integrity/platform_certs/load_powerpc.c
+++ b/security/integrity/platform_certs/load_powerpc.c
@@ -59,6 +59,8 @@ static __init void *get_cert_list(u8 *key, unsigned long keylen, u64 *size)
static int __init load_powerpc_certs(void)
{
void *db = NULL, *dbx = NULL, *data = NULL;
+ void *trustedca;
+ void *moduledb;
u64 dsize = 0;
u64 offset = 0;
int rc = 0;
@@ -120,6 +122,38 @@ static int __init load_powerpc_certs(void)
kfree(data);
}
+ data = get_cert_list("trustedcadb", 12, &dsize);
+ if (!data) {
+ pr_info("Couldn't get trustedcadb list from firmware\n");
+ } else if (IS_ERR(data)) {
+ rc = PTR_ERR(data);
+ pr_err("Error reading trustedcadb from firmware: %d\n", rc);
+ } else {
+ extract_esl(trustedca, data, dsize, offset);
+
+ rc = parse_efi_signature_list("powerpc:trustedca", trustedca, dsize,
+ get_handler_for_ca_keys);
+ if (rc)
+ pr_err("Couldn't parse trustedcadb signatures: %d\n", rc);
+ kfree(data);
+ }
+
+ data = get_cert_list("moduledb", 9, &dsize);
+ if (!data) {
+ pr_info("Couldn't get moduledb list from firmware\n");
+ } else if (IS_ERR(data)) {
+ rc = PTR_ERR(data);
+ pr_err("Error reading moduledb from firmware: %d\n", rc);
+ } else {
+ extract_esl(moduledb, data, dsize, offset);
+
+ rc = parse_efi_signature_list("powerpc:moduledb", moduledb, dsize,
+ get_handler_for_code_signing_keys);
+ if (rc)
+ pr_err("Couldn't parse moduledb signatures: %d\n", rc);
+ kfree(data);
+ }
+
return rc;
}
late_initcall(load_powerpc_certs);
diff --git a/security/integrity/platform_certs/machine_keyring.c b/security/integrity/platform_certs/machine_keyring.c
index 7aaed7950b6e..a401640a63cd 100644
--- a/security/integrity/platform_certs/machine_keyring.c
+++ b/security/integrity/platform_certs/machine_keyring.c
@@ -8,8 +8,6 @@
#include <linux/efi.h>
#include "../integrity.h"
-static bool trust_mok;
-
static __init int machine_keyring_init(void)
{
int rc;
@@ -36,7 +34,8 @@ void __init add_to_machine_keyring(const char *source, const void *data, size_t
* If the restriction check does not pass and the platform keyring
* is configured, try to add it into that keyring instead.
*/
- if (rc && IS_ENABLED(CONFIG_INTEGRITY_PLATFORM_KEYRING))
+ if (rc && efi_enabled(EFI_BOOT) &&
+ IS_ENABLED(CONFIG_INTEGRITY_PLATFORM_KEYRING))
rc = integrity_load_cert(INTEGRITY_KEYRING_PLATFORM, source,
data, len, perm);
@@ -62,12 +61,14 @@ static __init bool uefi_check_trust_mok_keys(void)
return false;
}
-bool __init trust_moklist(void)
+static bool __init trust_moklist(void)
{
static bool initialized;
+ static bool trust_mok;
if (!initialized) {
initialized = true;
+ trust_mok = false;
if (uefi_check_trust_mok_keys())
trust_mok = true;
@@ -75,3 +76,16 @@ bool __init trust_moklist(void)
return trust_mok;
}
+
+/*
+ * Provides platform specific check for trusting imputed keys before loading
+ * on .machine keyring. UEFI systems enable this trust based on a variable,
+ * and for other platforms, it is always enabled.
+ */
+bool __init imputed_trust_enabled(void)
+{
+ if (efi_enabled(EFI_BOOT))
+ return trust_moklist();
+
+ return true;
+}
diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c
index 1e313982af02..8af2136069d2 100644
--- a/security/keys/encrypted-keys/encrypted.c
+++ b/security/keys/encrypted-keys/encrypted.c
@@ -27,10 +27,10 @@
#include <linux/scatterlist.h>
#include <linux/ctype.h>
#include <crypto/aes.h>
-#include <crypto/algapi.h>
#include <crypto/hash.h>
#include <crypto/sha2.h>
#include <crypto/skcipher.h>
+#include <crypto/utils.h>
#include "encrypted.h"
#include "ecryptfs_format.h"
diff --git a/security/keys/internal.h b/security/keys/internal.h
index 3c1e7122076b..471cf36dedc0 100644
--- a/security/keys/internal.h
+++ b/security/keys/internal.h
@@ -109,13 +109,6 @@ extern void __key_link_end(struct key *keyring,
extern key_ref_t find_key_to_update(key_ref_t keyring_ref,
const struct keyring_index_key *index_key);
-extern struct key *keyring_search_instkey(struct key *keyring,
- key_serial_t target_id);
-
-extern int iterate_over_keyring(const struct key *keyring,
- int (*func)(const struct key *key, void *data),
- void *data);
-
struct keyring_search_context {
struct keyring_index_key index_key;
const struct cred *cred;
diff --git a/security/keys/key.c b/security/keys/key.c
index 5c0c7df833f8..0260a1902922 100644
--- a/security/keys/key.c
+++ b/security/keys/key.c
@@ -693,6 +693,7 @@ error:
spin_unlock(&key_serial_lock);
return key;
}
+EXPORT_SYMBOL(key_lookup);
/*
* Find and lock the specified key type against removal.
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
index d54f73c558f7..19be69fa4d05 100644
--- a/security/keys/keyctl.c
+++ b/security/keys/keyctl.c
@@ -980,14 +980,19 @@ long keyctl_chown_key(key_serial_t id, uid_t user, gid_t group)
ret = -EACCES;
down_write(&key->sem);
- if (!capable(CAP_SYS_ADMIN)) {
+ {
+ bool is_privileged_op = false;
+
/* only the sysadmin can chown a key to some other UID */
if (user != (uid_t) -1 && !uid_eq(key->uid, uid))
- goto error_put;
+ is_privileged_op = true;
/* only the sysadmin can set the key's GID to a group other
* than one of those that the current process subscribes to */
if (group != (gid_t) -1 && !gid_eq(gid, key->gid) && !in_group_p(gid))
+ is_privileged_op = true;
+
+ if (is_privileged_op && !capable(CAP_SYS_ADMIN))
goto error_put;
}
@@ -1088,7 +1093,7 @@ long keyctl_setperm_key(key_serial_t id, key_perm_t perm)
down_write(&key->sem);
/* if we're not the sysadmin, we can only change a key that we own */
- if (capable(CAP_SYS_ADMIN) || uid_eq(key->uid, current_fsuid())) {
+ if (uid_eq(key->uid, current_fsuid()) || capable(CAP_SYS_ADMIN)) {
key->perm = perm;
notify_key(key, NOTIFY_KEY_SETATTR, 0);
ret = 0;
diff --git a/security/keys/request_key.c b/security/keys/request_key.c
index 07a0ef2baacd..a7673ad86d18 100644
--- a/security/keys/request_key.c
+++ b/security/keys/request_key.c
@@ -401,17 +401,21 @@ static int construct_alloc_key(struct keyring_search_context *ctx,
set_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags);
if (dest_keyring) {
- ret = __key_link_lock(dest_keyring, &ctx->index_key);
+ ret = __key_link_lock(dest_keyring, &key->index_key);
if (ret < 0)
goto link_lock_failed;
- ret = __key_link_begin(dest_keyring, &ctx->index_key, &edit);
- if (ret < 0)
- goto link_prealloc_failed;
}
- /* attach the key to the destination keyring under lock, but we do need
+ /*
+ * Attach the key to the destination keyring under lock, but we do need
* to do another check just in case someone beat us to it whilst we
- * waited for locks */
+ * waited for locks.
+ *
+ * The caller might specify a comparison function which looks for keys
+ * that do not exactly match but are still equivalent from the caller's
+ * perspective. The __key_link_begin() operation must be done only after
+ * an actual key is determined.
+ */
mutex_lock(&key_construction_mutex);
rcu_read_lock();
@@ -420,12 +424,16 @@ static int construct_alloc_key(struct keyring_search_context *ctx,
if (!IS_ERR(key_ref))
goto key_already_present;
- if (dest_keyring)
+ if (dest_keyring) {
+ ret = __key_link_begin(dest_keyring, &key->index_key, &edit);
+ if (ret < 0)
+ goto link_alloc_failed;
__key_link(dest_keyring, key, &edit);
+ }
mutex_unlock(&key_construction_mutex);
if (dest_keyring)
- __key_link_end(dest_keyring, &ctx->index_key, edit);
+ __key_link_end(dest_keyring, &key->index_key, edit);
mutex_unlock(&user->cons_lock);
*_key = key;
kleave(" = 0 [%d]", key_serial(key));
@@ -438,10 +446,13 @@ key_already_present:
mutex_unlock(&key_construction_mutex);
key = key_ref_to_ptr(key_ref);
if (dest_keyring) {
+ ret = __key_link_begin(dest_keyring, &key->index_key, &edit);
+ if (ret < 0)
+ goto link_alloc_failed_unlocked;
ret = __key_link_check_live_key(dest_keyring, key);
if (ret == 0)
__key_link(dest_keyring, key, &edit);
- __key_link_end(dest_keyring, &ctx->index_key, edit);
+ __key_link_end(dest_keyring, &key->index_key, edit);
if (ret < 0)
goto link_check_failed;
}
@@ -456,8 +467,10 @@ link_check_failed:
kleave(" = %d [linkcheck]", ret);
return ret;
-link_prealloc_failed:
- __key_link_end(dest_keyring, &ctx->index_key, edit);
+link_alloc_failed:
+ mutex_unlock(&key_construction_mutex);
+link_alloc_failed_unlocked:
+ __key_link_end(dest_keyring, &key->index_key, edit);
link_lock_failed:
mutex_unlock(&user->cons_lock);
key_put(key);
diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c
index 41e9735006d0..8f33cd170e42 100644
--- a/security/keys/request_key_auth.c
+++ b/security/keys/request_key_auth.c
@@ -178,7 +178,7 @@ struct key *request_key_auth_new(struct key *target, const char *op,
if (!rka->callout_info)
goto error_free_rka;
rka->callout_len = callout_len;
- strlcpy(rka->op, op, sizeof(rka->op));
+ strscpy(rka->op, op, sizeof(rka->op));
/* see if the calling process is already servicing the key request of
* another process */
diff --git a/security/keys/sysctl.c b/security/keys/sysctl.c
index b72b82bb20c6..b348e1679d5d 100644
--- a/security/keys/sysctl.c
+++ b/security/keys/sysctl.c
@@ -9,7 +9,7 @@
#include <linux/sysctl.h>
#include "internal.h"
-struct ctl_table key_sysctls[] = {
+static struct ctl_table key_sysctls[] = {
{
.procname = "maxkeys",
.data = &key_quota_maxkeys,
diff --git a/security/keys/trusted-keys/trusted_core.c b/security/keys/trusted-keys/trusted_core.c
index c6fc50d67214..fee1ab2c734d 100644
--- a/security/keys/trusted-keys/trusted_core.c
+++ b/security/keys/trusted-keys/trusted_core.c
@@ -44,13 +44,12 @@ static const struct trusted_key_source trusted_key_sources[] = {
#endif
};
-DEFINE_STATIC_CALL_NULL(trusted_key_init, *trusted_key_sources[0].ops->init);
DEFINE_STATIC_CALL_NULL(trusted_key_seal, *trusted_key_sources[0].ops->seal);
DEFINE_STATIC_CALL_NULL(trusted_key_unseal,
*trusted_key_sources[0].ops->unseal);
DEFINE_STATIC_CALL_NULL(trusted_key_get_random,
*trusted_key_sources[0].ops->get_random);
-DEFINE_STATIC_CALL_NULL(trusted_key_exit, *trusted_key_sources[0].ops->exit);
+static void (*trusted_key_exit)(void);
static unsigned char migratable;
enum {
@@ -359,20 +358,17 @@ static int __init init_trusted(void)
if (!get_random)
get_random = kernel_get_random;
- static_call_update(trusted_key_init,
- trusted_key_sources[i].ops->init);
- static_call_update(trusted_key_seal,
- trusted_key_sources[i].ops->seal);
- static_call_update(trusted_key_unseal,
- trusted_key_sources[i].ops->unseal);
- static_call_update(trusted_key_get_random,
- get_random);
- static_call_update(trusted_key_exit,
- trusted_key_sources[i].ops->exit);
- migratable = trusted_key_sources[i].ops->migratable;
-
- ret = static_call(trusted_key_init)();
- if (!ret)
+ ret = trusted_key_sources[i].ops->init();
+ if (!ret) {
+ static_call_update(trusted_key_seal, trusted_key_sources[i].ops->seal);
+ static_call_update(trusted_key_unseal, trusted_key_sources[i].ops->unseal);
+ static_call_update(trusted_key_get_random, get_random);
+
+ trusted_key_exit = trusted_key_sources[i].ops->exit;
+ migratable = trusted_key_sources[i].ops->migratable;
+ }
+
+ if (!ret || ret != -ENODEV)
break;
}
@@ -388,7 +384,8 @@ static int __init init_trusted(void)
static void __exit cleanup_trusted(void)
{
- static_call_cond(trusted_key_exit)();
+ if (trusted_key_exit)
+ (*trusted_key_exit)();
}
late_initcall(init_trusted);
diff --git a/security/keys/trusted-keys/trusted_tee.c b/security/keys/trusted-keys/trusted_tee.c
index ac3e270ade69..aa3d477de6db 100644
--- a/security/keys/trusted-keys/trusted_tee.c
+++ b/security/keys/trusted-keys/trusted_tee.c
@@ -65,24 +65,16 @@ static int trusted_tee_seal(struct trusted_key_payload *p, char *datablob)
int ret;
struct tee_ioctl_invoke_arg inv_arg;
struct tee_param param[4];
- struct tee_shm *reg_shm_in = NULL, *reg_shm_out = NULL;
+ struct tee_shm *reg_shm = NULL;
memset(&inv_arg, 0, sizeof(inv_arg));
memset(&param, 0, sizeof(param));
- reg_shm_in = tee_shm_register_kernel_buf(pvt_data.ctx, p->key,
- p->key_len);
- if (IS_ERR(reg_shm_in)) {
- dev_err(pvt_data.dev, "key shm register failed\n");
- return PTR_ERR(reg_shm_in);
- }
-
- reg_shm_out = tee_shm_register_kernel_buf(pvt_data.ctx, p->blob,
- sizeof(p->blob));
- if (IS_ERR(reg_shm_out)) {
- dev_err(pvt_data.dev, "blob shm register failed\n");
- ret = PTR_ERR(reg_shm_out);
- goto out;
+ reg_shm = tee_shm_register_kernel_buf(pvt_data.ctx, p->key,
+ sizeof(p->key) + sizeof(p->blob));
+ if (IS_ERR(reg_shm)) {
+ dev_err(pvt_data.dev, "shm register failed\n");
+ return PTR_ERR(reg_shm);
}
inv_arg.func = TA_CMD_SEAL;
@@ -90,13 +82,13 @@ static int trusted_tee_seal(struct trusted_key_payload *p, char *datablob)
inv_arg.num_params = 4;
param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
- param[0].u.memref.shm = reg_shm_in;
+ param[0].u.memref.shm = reg_shm;
param[0].u.memref.size = p->key_len;
param[0].u.memref.shm_offs = 0;
param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT;
- param[1].u.memref.shm = reg_shm_out;
+ param[1].u.memref.shm = reg_shm;
param[1].u.memref.size = sizeof(p->blob);
- param[1].u.memref.shm_offs = 0;
+ param[1].u.memref.shm_offs = sizeof(p->key);
ret = tee_client_invoke_func(pvt_data.ctx, &inv_arg, param);
if ((ret < 0) || (inv_arg.ret != 0)) {
@@ -107,11 +99,7 @@ static int trusted_tee_seal(struct trusted_key_payload *p, char *datablob)
p->blob_len = param[1].u.memref.size;
}
-out:
- if (reg_shm_out)
- tee_shm_free(reg_shm_out);
- if (reg_shm_in)
- tee_shm_free(reg_shm_in);
+ tee_shm_free(reg_shm);
return ret;
}
@@ -124,24 +112,16 @@ static int trusted_tee_unseal(struct trusted_key_payload *p, char *datablob)
int ret;
struct tee_ioctl_invoke_arg inv_arg;
struct tee_param param[4];
- struct tee_shm *reg_shm_in = NULL, *reg_shm_out = NULL;
+ struct tee_shm *reg_shm = NULL;
memset(&inv_arg, 0, sizeof(inv_arg));
memset(&param, 0, sizeof(param));
- reg_shm_in = tee_shm_register_kernel_buf(pvt_data.ctx, p->blob,
- p->blob_len);
- if (IS_ERR(reg_shm_in)) {
- dev_err(pvt_data.dev, "blob shm register failed\n");
- return PTR_ERR(reg_shm_in);
- }
-
- reg_shm_out = tee_shm_register_kernel_buf(pvt_data.ctx, p->key,
- sizeof(p->key));
- if (IS_ERR(reg_shm_out)) {
- dev_err(pvt_data.dev, "key shm register failed\n");
- ret = PTR_ERR(reg_shm_out);
- goto out;
+ reg_shm = tee_shm_register_kernel_buf(pvt_data.ctx, p->key,
+ sizeof(p->key) + sizeof(p->blob));
+ if (IS_ERR(reg_shm)) {
+ dev_err(pvt_data.dev, "shm register failed\n");
+ return PTR_ERR(reg_shm);
}
inv_arg.func = TA_CMD_UNSEAL;
@@ -149,11 +129,11 @@ static int trusted_tee_unseal(struct trusted_key_payload *p, char *datablob)
inv_arg.num_params = 4;
param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
- param[0].u.memref.shm = reg_shm_in;
+ param[0].u.memref.shm = reg_shm;
param[0].u.memref.size = p->blob_len;
- param[0].u.memref.shm_offs = 0;
+ param[0].u.memref.shm_offs = sizeof(p->key);
param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT;
- param[1].u.memref.shm = reg_shm_out;
+ param[1].u.memref.shm = reg_shm;
param[1].u.memref.size = sizeof(p->key);
param[1].u.memref.shm_offs = 0;
@@ -166,11 +146,7 @@ static int trusted_tee_unseal(struct trusted_key_payload *p, char *datablob)
p->key_len = param[1].u.memref.size;
}
-out:
- if (reg_shm_out)
- tee_shm_free(reg_shm_out);
- if (reg_shm_in)
- tee_shm_free(reg_shm_in);
+ tee_shm_free(reg_shm);
return ret;
}
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index 2b2c8eb258d5..bc700f85f80b 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -186,7 +186,7 @@ int tpm2_key_priv(void *context, size_t hdrlen,
}
/**
- * tpm_buf_append_auth() - append TPMS_AUTH_COMMAND to the buffer.
+ * tpm2_buf_append_auth() - append TPMS_AUTH_COMMAND to the buffer.
*
* @buf: an allocated tpm_buf instance
* @session_handle: session handle
diff --git a/security/landlock/Kconfig b/security/landlock/Kconfig
index c1e862a38410..c4bf0d5eff39 100644
--- a/security/landlock/Kconfig
+++ b/security/landlock/Kconfig
@@ -3,6 +3,7 @@
config SECURITY_LANDLOCK
bool "Landlock support"
depends on SECURITY
+ select SECURITY_NETWORK
select SECURITY_PATH
help
Landlock is a sandboxing mechanism that enables processes to restrict
diff --git a/security/landlock/Makefile b/security/landlock/Makefile
index 7bbd2f413b3e..c2e116f2a299 100644
--- a/security/landlock/Makefile
+++ b/security/landlock/Makefile
@@ -2,3 +2,5 @@ obj-$(CONFIG_SECURITY_LANDLOCK) := landlock.o
landlock-y := setup.o syscalls.o object.o ruleset.o \
cred.o ptrace.o fs.o
+
+landlock-$(CONFIG_INET) += net.o
diff --git a/security/landlock/fs.c b/security/landlock/fs.c
index 1c0c198f6fdb..bc7c126deea2 100644
--- a/security/landlock/fs.c
+++ b/security/landlock/fs.c
@@ -151,16 +151,6 @@ retry:
/* clang-format on */
/*
- * All access rights that are denied by default whether they are handled or not
- * by a ruleset/layer. This must be ORed with all ruleset->fs_access_masks[]
- * entries when we need to get the absolute handled access masks.
- */
-/* clang-format off */
-#define ACCESS_INITIALLY_DENIED ( \
- LANDLOCK_ACCESS_FS_REFER)
-/* clang-format on */
-
-/*
* @path: Should have been checked by get_path_from_fd().
*/
int landlock_append_fs_rule(struct landlock_ruleset *const ruleset,
@@ -168,7 +158,9 @@ int landlock_append_fs_rule(struct landlock_ruleset *const ruleset,
access_mask_t access_rights)
{
int err;
- struct landlock_object *object;
+ struct landlock_id id = {
+ .type = LANDLOCK_KEY_INODE,
+ };
/* Files only get access rights that make sense. */
if (!d_is_dir(path->dentry) &&
@@ -178,20 +170,19 @@ int landlock_append_fs_rule(struct landlock_ruleset *const ruleset,
return -EINVAL;
/* Transforms relative access rights to absolute ones. */
- access_rights |=
- LANDLOCK_MASK_ACCESS_FS &
- ~(ruleset->fs_access_masks[0] | ACCESS_INITIALLY_DENIED);
- object = get_inode_object(d_backing_inode(path->dentry));
- if (IS_ERR(object))
- return PTR_ERR(object);
+ access_rights |= LANDLOCK_MASK_ACCESS_FS &
+ ~landlock_get_fs_access_mask(ruleset, 0);
+ id.key.object = get_inode_object(d_backing_inode(path->dentry));
+ if (IS_ERR(id.key.object))
+ return PTR_ERR(id.key.object);
mutex_lock(&ruleset->lock);
- err = landlock_insert_rule(ruleset, object, access_rights);
+ err = landlock_insert_rule(ruleset, id, access_rights);
mutex_unlock(&ruleset->lock);
/*
* No need to check for an error because landlock_insert_rule()
* increments the refcount for the new object if needed.
*/
- landlock_put_object(object);
+ landlock_put_object(id.key.object);
return err;
}
@@ -208,6 +199,9 @@ find_rule(const struct landlock_ruleset *const domain,
{
const struct landlock_rule *rule;
const struct inode *inode;
+ struct landlock_id id = {
+ .type = LANDLOCK_KEY_INODE,
+ };
/* Ignores nonexistent leafs. */
if (d_is_negative(dentry))
@@ -215,67 +209,13 @@ find_rule(const struct landlock_ruleset *const domain,
inode = d_backing_inode(dentry);
rcu_read_lock();
- rule = landlock_find_rule(
- domain, rcu_dereference(landlock_inode(inode)->object));
+ id.key.object = rcu_dereference(landlock_inode(inode)->object);
+ rule = landlock_find_rule(domain, id);
rcu_read_unlock();
return rule;
}
/*
- * @layer_masks is read and may be updated according to the access request and
- * the matching rule.
- *
- * Returns true if the request is allowed (i.e. relevant layer masks for the
- * request are empty).
- */
-static inline bool
-unmask_layers(const struct landlock_rule *const rule,
- const access_mask_t access_request,
- layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS])
-{
- size_t layer_level;
-
- if (!access_request || !layer_masks)
- return true;
- if (!rule)
- return false;
-
- /*
- * An access is granted if, for each policy layer, at least one rule
- * encountered on the pathwalk grants the requested access,
- * regardless of its position in the layer stack. We must then check
- * the remaining layers for each inode, from the first added layer to
- * the last one. When there is multiple requested accesses, for each
- * policy layer, the full set of requested accesses may not be granted
- * by only one rule, but by the union (binary OR) of multiple rules.
- * E.g. /a/b <execute> + /a <read> => /a/b <execute + read>
- */
- for (layer_level = 0; layer_level < rule->num_layers; layer_level++) {
- const struct landlock_layer *const layer =
- &rule->layers[layer_level];
- const layer_mask_t layer_bit = BIT_ULL(layer->level - 1);
- const unsigned long access_req = access_request;
- unsigned long access_bit;
- bool is_empty;
-
- /*
- * Records in @layer_masks which layer grants access to each
- * requested access.
- */
- is_empty = true;
- for_each_set_bit(access_bit, &access_req,
- ARRAY_SIZE(*layer_masks)) {
- if (layer->access & BIT_ULL(access_bit))
- (*layer_masks)[access_bit] &= ~layer_bit;
- is_empty = is_empty && !(*layer_masks)[access_bit];
- }
- if (is_empty)
- return true;
- }
- return false;
-}
-
-/*
* Allows access to pseudo filesystems that will never be mountable (e.g.
* sockfs, pipefs), but can still be reachable through
* /proc/<pid>/fd/<file-descriptor>
@@ -287,64 +227,35 @@ static inline bool is_nouser_or_private(const struct dentry *dentry)
unlikely(IS_PRIVATE(d_backing_inode(dentry))));
}
-static inline access_mask_t
-get_handled_accesses(const struct landlock_ruleset *const domain)
+static access_mask_t
+get_raw_handled_fs_accesses(const struct landlock_ruleset *const domain)
{
- access_mask_t access_dom = ACCESS_INITIALLY_DENIED;
+ access_mask_t access_dom = 0;
size_t layer_level;
for (layer_level = 0; layer_level < domain->num_layers; layer_level++)
- access_dom |= domain->fs_access_masks[layer_level];
- return access_dom & LANDLOCK_MASK_ACCESS_FS;
+ access_dom |=
+ landlock_get_raw_fs_access_mask(domain, layer_level);
+ return access_dom;
}
-/**
- * init_layer_masks - Initialize layer masks from an access request
- *
- * Populates @layer_masks such that for each access right in @access_request,
- * the bits for all the layers are set where this access right is handled.
- *
- * @domain: The domain that defines the current restrictions.
- * @access_request: The requested access rights to check.
- * @layer_masks: The layer masks to populate.
- *
- * Returns: An access mask where each access right bit is set which is handled
- * in any of the active layers in @domain.
- */
-static inline access_mask_t
-init_layer_masks(const struct landlock_ruleset *const domain,
- const access_mask_t access_request,
- layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS])
+static access_mask_t
+get_handled_fs_accesses(const struct landlock_ruleset *const domain)
{
- access_mask_t handled_accesses = 0;
- size_t layer_level;
+ /* Handles all initially denied by default access rights. */
+ return get_raw_handled_fs_accesses(domain) |
+ LANDLOCK_ACCESS_FS_INITIALLY_DENIED;
+}
- memset(layer_masks, 0, sizeof(*layer_masks));
- /* An empty access request can happen because of O_WRONLY | O_RDWR. */
- if (!access_request)
- return 0;
+static const struct landlock_ruleset *get_current_fs_domain(void)
+{
+ const struct landlock_ruleset *const dom =
+ landlock_get_current_domain();
- /* Saves all handled accesses per layer. */
- for (layer_level = 0; layer_level < domain->num_layers; layer_level++) {
- const unsigned long access_req = access_request;
- unsigned long access_bit;
+ if (!dom || !get_raw_handled_fs_accesses(dom))
+ return NULL;
- for_each_set_bit(access_bit, &access_req,
- ARRAY_SIZE(*layer_masks)) {
- /*
- * Artificially handles all initially denied by default
- * access rights.
- */
- if (BIT_ULL(access_bit) &
- (domain->fs_access_masks[layer_level] |
- ACCESS_INITIALLY_DENIED)) {
- (*layer_masks)[access_bit] |=
- BIT_ULL(layer_level);
- handled_accesses |= BIT_ULL(access_bit);
- }
- }
- }
- return handled_accesses;
+ return dom;
}
/*
@@ -519,7 +430,7 @@ static bool is_access_to_paths_allowed(
* a superset of the meaningful requested accesses).
*/
access_masked_parent1 = access_masked_parent2 =
- get_handled_accesses(domain);
+ get_handled_fs_accesses(domain);
is_dom_check = true;
} else {
if (WARN_ON_ONCE(dentry_child1 || dentry_child2))
@@ -531,18 +442,22 @@ static bool is_access_to_paths_allowed(
}
if (unlikely(dentry_child1)) {
- unmask_layers(find_rule(domain, dentry_child1),
- init_layer_masks(domain, LANDLOCK_MASK_ACCESS_FS,
- &_layer_masks_child1),
- &_layer_masks_child1);
+ landlock_unmask_layers(
+ find_rule(domain, dentry_child1),
+ landlock_init_layer_masks(
+ domain, LANDLOCK_MASK_ACCESS_FS,
+ &_layer_masks_child1, LANDLOCK_KEY_INODE),
+ &_layer_masks_child1, ARRAY_SIZE(_layer_masks_child1));
layer_masks_child1 = &_layer_masks_child1;
child1_is_directory = d_is_dir(dentry_child1);
}
if (unlikely(dentry_child2)) {
- unmask_layers(find_rule(domain, dentry_child2),
- init_layer_masks(domain, LANDLOCK_MASK_ACCESS_FS,
- &_layer_masks_child2),
- &_layer_masks_child2);
+ landlock_unmask_layers(
+ find_rule(domain, dentry_child2),
+ landlock_init_layer_masks(
+ domain, LANDLOCK_MASK_ACCESS_FS,
+ &_layer_masks_child2, LANDLOCK_KEY_INODE),
+ &_layer_masks_child2, ARRAY_SIZE(_layer_masks_child2));
layer_masks_child2 = &_layer_masks_child2;
child2_is_directory = d_is_dir(dentry_child2);
}
@@ -594,15 +509,16 @@ static bool is_access_to_paths_allowed(
}
rule = find_rule(domain, walker_path.dentry);
- allowed_parent1 = unmask_layers(rule, access_masked_parent1,
- layer_masks_parent1);
- allowed_parent2 = unmask_layers(rule, access_masked_parent2,
- layer_masks_parent2);
+ allowed_parent1 = landlock_unmask_layers(
+ rule, access_masked_parent1, layer_masks_parent1,
+ ARRAY_SIZE(*layer_masks_parent1));
+ allowed_parent2 = landlock_unmask_layers(
+ rule, access_masked_parent2, layer_masks_parent2,
+ ARRAY_SIZE(*layer_masks_parent2));
/* Stops when a rule from each layer grants access. */
if (allowed_parent1 && allowed_parent2)
break;
-
jump_up:
if (walker_path.dentry == walker_path.mnt->mnt_root) {
if (follow_up(&walker_path)) {
@@ -641,7 +557,8 @@ static inline int check_access_path(const struct landlock_ruleset *const domain,
{
layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {};
- access_request = init_layer_masks(domain, access_request, &layer_masks);
+ access_request = landlock_init_layer_masks(
+ domain, access_request, &layer_masks, LANDLOCK_KEY_INODE);
if (is_access_to_paths_allowed(domain, path, access_request,
&layer_masks, NULL, 0, NULL, NULL))
return 0;
@@ -651,8 +568,7 @@ static inline int check_access_path(const struct landlock_ruleset *const domain,
static inline int current_check_access_path(const struct path *const path,
const access_mask_t access_request)
{
- const struct landlock_ruleset *const dom =
- landlock_get_current_domain();
+ const struct landlock_ruleset *const dom = get_current_fs_domain();
if (!dom)
return 0;
@@ -727,16 +643,18 @@ static bool collect_domain_accesses(
if (is_nouser_or_private(dir))
return true;
- access_dom = init_layer_masks(domain, LANDLOCK_MASK_ACCESS_FS,
- layer_masks_dom);
+ access_dom = landlock_init_layer_masks(domain, LANDLOCK_MASK_ACCESS_FS,
+ layer_masks_dom,
+ LANDLOCK_KEY_INODE);
dget(dir);
while (true) {
struct dentry *parent_dentry;
/* Gets all layers allowing all domain accesses. */
- if (unmask_layers(find_rule(domain, dir), access_dom,
- layer_masks_dom)) {
+ if (landlock_unmask_layers(find_rule(domain, dir), access_dom,
+ layer_masks_dom,
+ ARRAY_SIZE(*layer_masks_dom))) {
/*
* Stops when all handled accesses are allowed by at
* least one rule in each layer.
@@ -815,8 +733,7 @@ static int current_check_refer_path(struct dentry *const old_dentry,
struct dentry *const new_dentry,
const bool removable, const bool exchange)
{
- const struct landlock_ruleset *const dom =
- landlock_get_current_domain();
+ const struct landlock_ruleset *const dom = get_current_fs_domain();
bool allow_parent1, allow_parent2;
access_mask_t access_request_parent1, access_request_parent2;
struct path mnt_dir;
@@ -850,9 +767,9 @@ static int current_check_refer_path(struct dentry *const old_dentry,
* The LANDLOCK_ACCESS_FS_REFER access right is not required
* for same-directory referer (i.e. no reparenting).
*/
- access_request_parent1 = init_layer_masks(
+ access_request_parent1 = landlock_init_layer_masks(
dom, access_request_parent1 | access_request_parent2,
- &layer_masks_parent1);
+ &layer_masks_parent1, LANDLOCK_KEY_INODE);
if (is_access_to_paths_allowed(
dom, new_dir, access_request_parent1,
&layer_masks_parent1, NULL, 0, NULL, NULL))
@@ -1050,7 +967,7 @@ static int hook_sb_mount(const char *const dev_name,
const struct path *const path, const char *const type,
const unsigned long flags, void *const data)
{
- if (!landlock_get_current_domain())
+ if (!get_current_fs_domain())
return 0;
return -EPERM;
}
@@ -1058,7 +975,7 @@ static int hook_sb_mount(const char *const dev_name,
static int hook_move_mount(const struct path *const from_path,
const struct path *const to_path)
{
- if (!landlock_get_current_domain())
+ if (!get_current_fs_domain())
return 0;
return -EPERM;
}
@@ -1069,14 +986,14 @@ static int hook_move_mount(const struct path *const from_path,
*/
static int hook_sb_umount(struct vfsmount *const mnt, const int flags)
{
- if (!landlock_get_current_domain())
+ if (!get_current_fs_domain())
return 0;
return -EPERM;
}
static int hook_sb_remount(struct super_block *const sb, void *const mnt_opts)
{
- if (!landlock_get_current_domain())
+ if (!get_current_fs_domain())
return 0;
return -EPERM;
}
@@ -1092,7 +1009,7 @@ static int hook_sb_remount(struct super_block *const sb, void *const mnt_opts)
static int hook_sb_pivotroot(const struct path *const old_path,
const struct path *const new_path)
{
- if (!landlock_get_current_domain())
+ if (!get_current_fs_domain())
return 0;
return -EPERM;
}
@@ -1128,8 +1045,7 @@ static int hook_path_mknod(const struct path *const dir,
struct dentry *const dentry, const umode_t mode,
const unsigned int dev)
{
- const struct landlock_ruleset *const dom =
- landlock_get_current_domain();
+ const struct landlock_ruleset *const dom = get_current_fs_domain();
if (!dom)
return 0;
@@ -1208,8 +1124,7 @@ static int hook_file_open(struct file *const file)
layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {};
access_mask_t open_access_request, full_access_request, allowed_access;
const access_mask_t optional_access = LANDLOCK_ACCESS_FS_TRUNCATE;
- const struct landlock_ruleset *const dom =
- landlock_get_current_domain();
+ const struct landlock_ruleset *const dom = get_current_fs_domain();
if (!dom)
return 0;
@@ -1229,7 +1144,8 @@ static int hook_file_open(struct file *const file)
if (is_access_to_paths_allowed(
dom, &file->f_path,
- init_layer_masks(dom, full_access_request, &layer_masks),
+ landlock_init_layer_masks(dom, full_access_request,
+ &layer_masks, LANDLOCK_KEY_INODE),
&layer_masks, NULL, 0, NULL, NULL)) {
allowed_access = full_access_request;
} else {
diff --git a/security/landlock/limits.h b/security/landlock/limits.h
index 82288f0e9e5e..93c9c6f91556 100644
--- a/security/landlock/limits.h
+++ b/security/landlock/limits.h
@@ -21,6 +21,12 @@
#define LANDLOCK_LAST_ACCESS_FS LANDLOCK_ACCESS_FS_TRUNCATE
#define LANDLOCK_MASK_ACCESS_FS ((LANDLOCK_LAST_ACCESS_FS << 1) - 1)
#define LANDLOCK_NUM_ACCESS_FS __const_hweight64(LANDLOCK_MASK_ACCESS_FS)
+#define LANDLOCK_SHIFT_ACCESS_FS 0
+
+#define LANDLOCK_LAST_ACCESS_NET LANDLOCK_ACCESS_NET_CONNECT_TCP
+#define LANDLOCK_MASK_ACCESS_NET ((LANDLOCK_LAST_ACCESS_NET << 1) - 1)
+#define LANDLOCK_NUM_ACCESS_NET __const_hweight64(LANDLOCK_MASK_ACCESS_NET)
+#define LANDLOCK_SHIFT_ACCESS_NET LANDLOCK_NUM_ACCESS_FS
/* clang-format on */
diff --git a/security/landlock/net.c b/security/landlock/net.c
new file mode 100644
index 000000000000..aaa92c2b1f08
--- /dev/null
+++ b/security/landlock/net.c
@@ -0,0 +1,200 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Landlock LSM - Network management and hooks
+ *
+ * Copyright © 2022-2023 Huawei Tech. Co., Ltd.
+ * Copyright © 2022-2023 Microsoft Corporation
+ */
+
+#include <linux/in.h>
+#include <linux/net.h>
+#include <linux/socket.h>
+#include <net/ipv6.h>
+
+#include "common.h"
+#include "cred.h"
+#include "limits.h"
+#include "net.h"
+#include "ruleset.h"
+
+int landlock_append_net_rule(struct landlock_ruleset *const ruleset,
+ const u16 port, access_mask_t access_rights)
+{
+ int err;
+ const struct landlock_id id = {
+ .key.data = (__force uintptr_t)htons(port),
+ .type = LANDLOCK_KEY_NET_PORT,
+ };
+
+ BUILD_BUG_ON(sizeof(port) > sizeof(id.key.data));
+
+ /* Transforms relative access rights to absolute ones. */
+ access_rights |= LANDLOCK_MASK_ACCESS_NET &
+ ~landlock_get_net_access_mask(ruleset, 0);
+
+ mutex_lock(&ruleset->lock);
+ err = landlock_insert_rule(ruleset, id, access_rights);
+ mutex_unlock(&ruleset->lock);
+
+ return err;
+}
+
+static access_mask_t
+get_raw_handled_net_accesses(const struct landlock_ruleset *const domain)
+{
+ access_mask_t access_dom = 0;
+ size_t layer_level;
+
+ for (layer_level = 0; layer_level < domain->num_layers; layer_level++)
+ access_dom |= landlock_get_net_access_mask(domain, layer_level);
+ return access_dom;
+}
+
+static const struct landlock_ruleset *get_current_net_domain(void)
+{
+ const struct landlock_ruleset *const dom =
+ landlock_get_current_domain();
+
+ if (!dom || !get_raw_handled_net_accesses(dom))
+ return NULL;
+
+ return dom;
+}
+
+static int current_check_access_socket(struct socket *const sock,
+ struct sockaddr *const address,
+ const int addrlen,
+ const access_mask_t access_request)
+{
+ __be16 port;
+ layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_NET] = {};
+ const struct landlock_rule *rule;
+ access_mask_t handled_access;
+ struct landlock_id id = {
+ .type = LANDLOCK_KEY_NET_PORT,
+ };
+ const struct landlock_ruleset *const dom = get_current_net_domain();
+
+ if (!dom)
+ return 0;
+ if (WARN_ON_ONCE(dom->num_layers < 1))
+ return -EACCES;
+
+ /* Checks if it's a (potential) TCP socket. */
+ if (sock->type != SOCK_STREAM)
+ return 0;
+
+ /* Checks for minimal header length to safely read sa_family. */
+ if (addrlen < offsetofend(typeof(*address), sa_family))
+ return -EINVAL;
+
+ switch (address->sa_family) {
+ case AF_UNSPEC:
+ case AF_INET:
+ if (addrlen < sizeof(struct sockaddr_in))
+ return -EINVAL;
+ port = ((struct sockaddr_in *)address)->sin_port;
+ break;
+
+#if IS_ENABLED(CONFIG_IPV6)
+ case AF_INET6:
+ if (addrlen < SIN6_LEN_RFC2133)
+ return -EINVAL;
+ port = ((struct sockaddr_in6 *)address)->sin6_port;
+ break;
+#endif /* IS_ENABLED(CONFIG_IPV6) */
+
+ default:
+ return 0;
+ }
+
+ /* Specific AF_UNSPEC handling. */
+ if (address->sa_family == AF_UNSPEC) {
+ /*
+ * Connecting to an address with AF_UNSPEC dissolves the TCP
+ * association, which have the same effect as closing the
+ * connection while retaining the socket object (i.e., the file
+ * descriptor). As for dropping privileges, closing
+ * connections is always allowed.
+ *
+ * For a TCP access control system, this request is legitimate.
+ * Let the network stack handle potential inconsistencies and
+ * return -EINVAL if needed.
+ */
+ if (access_request == LANDLOCK_ACCESS_NET_CONNECT_TCP)
+ return 0;
+
+ /*
+ * For compatibility reason, accept AF_UNSPEC for bind
+ * accesses (mapped to AF_INET) only if the address is
+ * INADDR_ANY (cf. __inet_bind). Checking the address is
+ * required to not wrongfully return -EACCES instead of
+ * -EAFNOSUPPORT.
+ *
+ * We could return 0 and let the network stack handle these
+ * checks, but it is safer to return a proper error and test
+ * consistency thanks to kselftest.
+ */
+ if (access_request == LANDLOCK_ACCESS_NET_BIND_TCP) {
+ /* addrlen has already been checked for AF_UNSPEC. */
+ const struct sockaddr_in *const sockaddr =
+ (struct sockaddr_in *)address;
+
+ if (sock->sk->__sk_common.skc_family != AF_INET)
+ return -EINVAL;
+
+ if (sockaddr->sin_addr.s_addr != htonl(INADDR_ANY))
+ return -EAFNOSUPPORT;
+ }
+ } else {
+ /*
+ * Checks sa_family consistency to not wrongfully return
+ * -EACCES instead of -EINVAL. Valid sa_family changes are
+ * only (from AF_INET or AF_INET6) to AF_UNSPEC.
+ *
+ * We could return 0 and let the network stack handle this
+ * check, but it is safer to return a proper error and test
+ * consistency thanks to kselftest.
+ */
+ if (address->sa_family != sock->sk->__sk_common.skc_family)
+ return -EINVAL;
+ }
+
+ id.key.data = (__force uintptr_t)port;
+ BUILD_BUG_ON(sizeof(port) > sizeof(id.key.data));
+
+ rule = landlock_find_rule(dom, id);
+ handled_access = landlock_init_layer_masks(
+ dom, access_request, &layer_masks, LANDLOCK_KEY_NET_PORT);
+ if (landlock_unmask_layers(rule, handled_access, &layer_masks,
+ ARRAY_SIZE(layer_masks)))
+ return 0;
+
+ return -EACCES;
+}
+
+static int hook_socket_bind(struct socket *const sock,
+ struct sockaddr *const address, const int addrlen)
+{
+ return current_check_access_socket(sock, address, addrlen,
+ LANDLOCK_ACCESS_NET_BIND_TCP);
+}
+
+static int hook_socket_connect(struct socket *const sock,
+ struct sockaddr *const address,
+ const int addrlen)
+{
+ return current_check_access_socket(sock, address, addrlen,
+ LANDLOCK_ACCESS_NET_CONNECT_TCP);
+}
+
+static struct security_hook_list landlock_hooks[] __ro_after_init = {
+ LSM_HOOK_INIT(socket_bind, hook_socket_bind),
+ LSM_HOOK_INIT(socket_connect, hook_socket_connect),
+};
+
+__init void landlock_add_net_hooks(void)
+{
+ security_add_hooks(landlock_hooks, ARRAY_SIZE(landlock_hooks),
+ LANDLOCK_NAME);
+}
diff --git a/security/landlock/net.h b/security/landlock/net.h
new file mode 100644
index 000000000000..09960c237a13
--- /dev/null
+++ b/security/landlock/net.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Landlock LSM - Network management and hooks
+ *
+ * Copyright © 2022-2023 Huawei Tech. Co., Ltd.
+ */
+
+#ifndef _SECURITY_LANDLOCK_NET_H
+#define _SECURITY_LANDLOCK_NET_H
+
+#include "common.h"
+#include "ruleset.h"
+#include "setup.h"
+
+#if IS_ENABLED(CONFIG_INET)
+__init void landlock_add_net_hooks(void);
+
+int landlock_append_net_rule(struct landlock_ruleset *const ruleset,
+ const u16 port, access_mask_t access_rights);
+#else /* IS_ENABLED(CONFIG_INET) */
+static inline void landlock_add_net_hooks(void)
+{
+}
+
+static inline int
+landlock_append_net_rule(struct landlock_ruleset *const ruleset, const u16 port,
+ access_mask_t access_rights)
+{
+ return -EAFNOSUPPORT;
+}
+#endif /* IS_ENABLED(CONFIG_INET) */
+
+#endif /* _SECURITY_LANDLOCK_NET_H */
diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
index 996484f98bfd..ffedc99f2b68 100644
--- a/security/landlock/ruleset.c
+++ b/security/landlock/ruleset.c
@@ -29,33 +29,43 @@ static struct landlock_ruleset *create_ruleset(const u32 num_layers)
struct landlock_ruleset *new_ruleset;
new_ruleset =
- kzalloc(struct_size(new_ruleset, fs_access_masks, num_layers),
+ kzalloc(struct_size(new_ruleset, access_masks, num_layers),
GFP_KERNEL_ACCOUNT);
if (!new_ruleset)
return ERR_PTR(-ENOMEM);
refcount_set(&new_ruleset->usage, 1);
mutex_init(&new_ruleset->lock);
- new_ruleset->root = RB_ROOT;
+ new_ruleset->root_inode = RB_ROOT;
+
+#if IS_ENABLED(CONFIG_INET)
+ new_ruleset->root_net_port = RB_ROOT;
+#endif /* IS_ENABLED(CONFIG_INET) */
+
new_ruleset->num_layers = num_layers;
/*
* hierarchy = NULL
* num_rules = 0
- * fs_access_masks[] = 0
+ * access_masks[] = 0
*/
return new_ruleset;
}
struct landlock_ruleset *
-landlock_create_ruleset(const access_mask_t fs_access_mask)
+landlock_create_ruleset(const access_mask_t fs_access_mask,
+ const access_mask_t net_access_mask)
{
struct landlock_ruleset *new_ruleset;
/* Informs about useless ruleset. */
- if (!fs_access_mask)
+ if (!fs_access_mask && !net_access_mask)
return ERR_PTR(-ENOMSG);
new_ruleset = create_ruleset(1);
- if (!IS_ERR(new_ruleset))
- new_ruleset->fs_access_masks[0] = fs_access_mask;
+ if (IS_ERR(new_ruleset))
+ return new_ruleset;
+ if (fs_access_mask)
+ landlock_add_fs_access_mask(new_ruleset, fs_access_mask, 0);
+ if (net_access_mask)
+ landlock_add_net_access_mask(new_ruleset, net_access_mask, 0);
return new_ruleset;
}
@@ -68,8 +78,25 @@ static void build_check_rule(void)
BUILD_BUG_ON(rule.num_layers < LANDLOCK_MAX_NUM_LAYERS);
}
+static bool is_object_pointer(const enum landlock_key_type key_type)
+{
+ switch (key_type) {
+ case LANDLOCK_KEY_INODE:
+ return true;
+
+#if IS_ENABLED(CONFIG_INET)
+ case LANDLOCK_KEY_NET_PORT:
+ return false;
+#endif /* IS_ENABLED(CONFIG_INET) */
+
+ default:
+ WARN_ON_ONCE(1);
+ return false;
+ }
+}
+
static struct landlock_rule *
-create_rule(struct landlock_object *const object,
+create_rule(const struct landlock_id id,
const struct landlock_layer (*const layers)[], const u32 num_layers,
const struct landlock_layer *const new_layer)
{
@@ -90,8 +117,13 @@ create_rule(struct landlock_object *const object,
if (!new_rule)
return ERR_PTR(-ENOMEM);
RB_CLEAR_NODE(&new_rule->node);
- landlock_get_object(object);
- new_rule->object = object;
+ if (is_object_pointer(id.type)) {
+ /* This should be catched by insert_rule(). */
+ WARN_ON_ONCE(!id.key.object);
+ landlock_get_object(id.key.object);
+ }
+
+ new_rule->key = id.key;
new_rule->num_layers = new_num_layers;
/* Copies the original layer stack. */
memcpy(new_rule->layers, layers,
@@ -102,12 +134,32 @@ create_rule(struct landlock_object *const object,
return new_rule;
}
-static void free_rule(struct landlock_rule *const rule)
+static struct rb_root *get_root(struct landlock_ruleset *const ruleset,
+ const enum landlock_key_type key_type)
+{
+ switch (key_type) {
+ case LANDLOCK_KEY_INODE:
+ return &ruleset->root_inode;
+
+#if IS_ENABLED(CONFIG_INET)
+ case LANDLOCK_KEY_NET_PORT:
+ return &ruleset->root_net_port;
+#endif /* IS_ENABLED(CONFIG_INET) */
+
+ default:
+ WARN_ON_ONCE(1);
+ return ERR_PTR(-EINVAL);
+ }
+}
+
+static void free_rule(struct landlock_rule *const rule,
+ const enum landlock_key_type key_type)
{
might_sleep();
if (!rule)
return;
- landlock_put_object(rule->object);
+ if (is_object_pointer(key_type))
+ landlock_put_object(rule->key.object);
kfree(rule);
}
@@ -117,19 +169,21 @@ static void build_check_ruleset(void)
.num_rules = ~0,
.num_layers = ~0,
};
- typeof(ruleset.fs_access_masks[0]) fs_access_mask = ~0;
+ typeof(ruleset.access_masks[0]) access_masks = ~0;
BUILD_BUG_ON(ruleset.num_rules < LANDLOCK_MAX_NUM_RULES);
BUILD_BUG_ON(ruleset.num_layers < LANDLOCK_MAX_NUM_LAYERS);
- BUILD_BUG_ON(fs_access_mask < LANDLOCK_MASK_ACCESS_FS);
+ BUILD_BUG_ON(access_masks <
+ ((LANDLOCK_MASK_ACCESS_FS << LANDLOCK_SHIFT_ACCESS_FS) |
+ (LANDLOCK_MASK_ACCESS_NET << LANDLOCK_SHIFT_ACCESS_NET)));
}
/**
* insert_rule - Create and insert a rule in a ruleset
*
* @ruleset: The ruleset to be updated.
- * @object: The object to build the new rule with. The underlying kernel
- * object must be held by the caller.
+ * @id: The ID to build the new rule with. The underlying kernel object, if
+ * any, must be held by the caller.
* @layers: One or multiple layers to be copied into the new rule.
* @num_layers: The number of @layers entries.
*
@@ -143,26 +197,35 @@ static void build_check_ruleset(void)
* access rights.
*/
static int insert_rule(struct landlock_ruleset *const ruleset,
- struct landlock_object *const object,
+ const struct landlock_id id,
const struct landlock_layer (*const layers)[],
- size_t num_layers)
+ const size_t num_layers)
{
struct rb_node **walker_node;
struct rb_node *parent_node = NULL;
struct landlock_rule *new_rule;
+ struct rb_root *root;
might_sleep();
lockdep_assert_held(&ruleset->lock);
- if (WARN_ON_ONCE(!object || !layers))
+ if (WARN_ON_ONCE(!layers))
return -ENOENT;
- walker_node = &(ruleset->root.rb_node);
+
+ if (is_object_pointer(id.type) && WARN_ON_ONCE(!id.key.object))
+ return -ENOENT;
+
+ root = get_root(ruleset, id.type);
+ if (IS_ERR(root))
+ return PTR_ERR(root);
+
+ walker_node = &root->rb_node;
while (*walker_node) {
struct landlock_rule *const this =
rb_entry(*walker_node, struct landlock_rule, node);
- if (this->object != object) {
+ if (this->key.data != id.key.data) {
parent_node = *walker_node;
- if (this->object < object)
+ if (this->key.data < id.key.data)
walker_node = &((*walker_node)->rb_right);
else
walker_node = &((*walker_node)->rb_left);
@@ -194,24 +257,24 @@ static int insert_rule(struct landlock_ruleset *const ruleset,
* Intersects access rights when it is a merge between a
* ruleset and a domain.
*/
- new_rule = create_rule(object, &this->layers, this->num_layers,
+ new_rule = create_rule(id, &this->layers, this->num_layers,
&(*layers)[0]);
if (IS_ERR(new_rule))
return PTR_ERR(new_rule);
- rb_replace_node(&this->node, &new_rule->node, &ruleset->root);
- free_rule(this);
+ rb_replace_node(&this->node, &new_rule->node, root);
+ free_rule(this, id.type);
return 0;
}
- /* There is no match for @object. */
+ /* There is no match for @id. */
build_check_ruleset();
if (ruleset->num_rules >= LANDLOCK_MAX_NUM_RULES)
return -E2BIG;
- new_rule = create_rule(object, layers, num_layers, NULL);
+ new_rule = create_rule(id, layers, num_layers, NULL);
if (IS_ERR(new_rule))
return PTR_ERR(new_rule);
rb_link_node(&new_rule->node, parent_node, walker_node);
- rb_insert_color(&new_rule->node, &ruleset->root);
+ rb_insert_color(&new_rule->node, root);
ruleset->num_rules++;
return 0;
}
@@ -229,7 +292,7 @@ static void build_check_layer(void)
/* @ruleset must be locked by the caller. */
int landlock_insert_rule(struct landlock_ruleset *const ruleset,
- struct landlock_object *const object,
+ const struct landlock_id id,
const access_mask_t access)
{
struct landlock_layer layers[] = { {
@@ -239,7 +302,7 @@ int landlock_insert_rule(struct landlock_ruleset *const ruleset,
} };
build_check_layer();
- return insert_rule(ruleset, object, &layers, ARRAY_SIZE(layers));
+ return insert_rule(ruleset, id, &layers, ARRAY_SIZE(layers));
}
static inline void get_hierarchy(struct landlock_hierarchy *const hierarchy)
@@ -258,10 +321,51 @@ static void put_hierarchy(struct landlock_hierarchy *hierarchy)
}
}
+static int merge_tree(struct landlock_ruleset *const dst,
+ struct landlock_ruleset *const src,
+ const enum landlock_key_type key_type)
+{
+ struct landlock_rule *walker_rule, *next_rule;
+ struct rb_root *src_root;
+ int err = 0;
+
+ might_sleep();
+ lockdep_assert_held(&dst->lock);
+ lockdep_assert_held(&src->lock);
+
+ src_root = get_root(src, key_type);
+ if (IS_ERR(src_root))
+ return PTR_ERR(src_root);
+
+ /* Merges the @src tree. */
+ rbtree_postorder_for_each_entry_safe(walker_rule, next_rule, src_root,
+ node) {
+ struct landlock_layer layers[] = { {
+ .level = dst->num_layers,
+ } };
+ const struct landlock_id id = {
+ .key = walker_rule->key,
+ .type = key_type,
+ };
+
+ if (WARN_ON_ONCE(walker_rule->num_layers != 1))
+ return -EINVAL;
+
+ if (WARN_ON_ONCE(walker_rule->layers[0].level != 0))
+ return -EINVAL;
+
+ layers[0].access = walker_rule->layers[0].access;
+
+ err = insert_rule(dst, id, &layers, ARRAY_SIZE(layers));
+ if (err)
+ return err;
+ }
+ return err;
+}
+
static int merge_ruleset(struct landlock_ruleset *const dst,
struct landlock_ruleset *const src)
{
- struct landlock_rule *walker_rule, *next_rule;
int err = 0;
might_sleep();
@@ -281,29 +385,19 @@ static int merge_ruleset(struct landlock_ruleset *const dst,
err = -EINVAL;
goto out_unlock;
}
- dst->fs_access_masks[dst->num_layers - 1] = src->fs_access_masks[0];
+ dst->access_masks[dst->num_layers - 1] = src->access_masks[0];
- /* Merges the @src tree. */
- rbtree_postorder_for_each_entry_safe(walker_rule, next_rule, &src->root,
- node) {
- struct landlock_layer layers[] = { {
- .level = dst->num_layers,
- } };
+ /* Merges the @src inode tree. */
+ err = merge_tree(dst, src, LANDLOCK_KEY_INODE);
+ if (err)
+ goto out_unlock;
- if (WARN_ON_ONCE(walker_rule->num_layers != 1)) {
- err = -EINVAL;
- goto out_unlock;
- }
- if (WARN_ON_ONCE(walker_rule->layers[0].level != 0)) {
- err = -EINVAL;
- goto out_unlock;
- }
- layers[0].access = walker_rule->layers[0].access;
- err = insert_rule(dst, walker_rule->object, &layers,
- ARRAY_SIZE(layers));
- if (err)
- goto out_unlock;
- }
+#if IS_ENABLED(CONFIG_INET)
+ /* Merges the @src network port tree. */
+ err = merge_tree(dst, src, LANDLOCK_KEY_NET_PORT);
+ if (err)
+ goto out_unlock;
+#endif /* IS_ENABLED(CONFIG_INET) */
out_unlock:
mutex_unlock(&src->lock);
@@ -311,10 +405,41 @@ out_unlock:
return err;
}
+static int inherit_tree(struct landlock_ruleset *const parent,
+ struct landlock_ruleset *const child,
+ const enum landlock_key_type key_type)
+{
+ struct landlock_rule *walker_rule, *next_rule;
+ struct rb_root *parent_root;
+ int err = 0;
+
+ might_sleep();
+ lockdep_assert_held(&parent->lock);
+ lockdep_assert_held(&child->lock);
+
+ parent_root = get_root(parent, key_type);
+ if (IS_ERR(parent_root))
+ return PTR_ERR(parent_root);
+
+ /* Copies the @parent inode or network tree. */
+ rbtree_postorder_for_each_entry_safe(walker_rule, next_rule,
+ parent_root, node) {
+ const struct landlock_id id = {
+ .key = walker_rule->key,
+ .type = key_type,
+ };
+
+ err = insert_rule(child, id, &walker_rule->layers,
+ walker_rule->num_layers);
+ if (err)
+ return err;
+ }
+ return err;
+}
+
static int inherit_ruleset(struct landlock_ruleset *const parent,
struct landlock_ruleset *const child)
{
- struct landlock_rule *walker_rule, *next_rule;
int err = 0;
might_sleep();
@@ -325,23 +450,25 @@ static int inherit_ruleset(struct landlock_ruleset *const parent,
mutex_lock(&child->lock);
mutex_lock_nested(&parent->lock, SINGLE_DEPTH_NESTING);
- /* Copies the @parent tree. */
- rbtree_postorder_for_each_entry_safe(walker_rule, next_rule,
- &parent->root, node) {
- err = insert_rule(child, walker_rule->object,
- &walker_rule->layers,
- walker_rule->num_layers);
- if (err)
- goto out_unlock;
- }
+ /* Copies the @parent inode tree. */
+ err = inherit_tree(parent, child, LANDLOCK_KEY_INODE);
+ if (err)
+ goto out_unlock;
+
+#if IS_ENABLED(CONFIG_INET)
+ /* Copies the @parent network port tree. */
+ err = inherit_tree(parent, child, LANDLOCK_KEY_NET_PORT);
+ if (err)
+ goto out_unlock;
+#endif /* IS_ENABLED(CONFIG_INET) */
if (WARN_ON_ONCE(child->num_layers <= parent->num_layers)) {
err = -EINVAL;
goto out_unlock;
}
/* Copies the parent layer stack and leaves a space for the new layer. */
- memcpy(child->fs_access_masks, parent->fs_access_masks,
- flex_array_size(parent, fs_access_masks, parent->num_layers));
+ memcpy(child->access_masks, parent->access_masks,
+ flex_array_size(parent, access_masks, parent->num_layers));
if (WARN_ON_ONCE(!parent->hierarchy)) {
err = -EINVAL;
@@ -361,8 +488,16 @@ static void free_ruleset(struct landlock_ruleset *const ruleset)
struct landlock_rule *freeme, *next;
might_sleep();
- rbtree_postorder_for_each_entry_safe(freeme, next, &ruleset->root, node)
- free_rule(freeme);
+ rbtree_postorder_for_each_entry_safe(freeme, next, &ruleset->root_inode,
+ node)
+ free_rule(freeme, LANDLOCK_KEY_INODE);
+
+#if IS_ENABLED(CONFIG_INET)
+ rbtree_postorder_for_each_entry_safe(freeme, next,
+ &ruleset->root_net_port, node)
+ free_rule(freeme, LANDLOCK_KEY_NET_PORT);
+#endif /* IS_ENABLED(CONFIG_INET) */
+
put_hierarchy(ruleset->hierarchy);
kfree(ruleset);
}
@@ -453,23 +588,151 @@ out_put_dom:
*/
const struct landlock_rule *
landlock_find_rule(const struct landlock_ruleset *const ruleset,
- const struct landlock_object *const object)
+ const struct landlock_id id)
{
+ const struct rb_root *root;
const struct rb_node *node;
- if (!object)
+ root = get_root((struct landlock_ruleset *)ruleset, id.type);
+ if (IS_ERR(root))
return NULL;
- node = ruleset->root.rb_node;
+ node = root->rb_node;
+
while (node) {
struct landlock_rule *this =
rb_entry(node, struct landlock_rule, node);
- if (this->object == object)
+ if (this->key.data == id.key.data)
return this;
- if (this->object < object)
+ if (this->key.data < id.key.data)
node = node->rb_right;
else
node = node->rb_left;
}
return NULL;
}
+
+/*
+ * @layer_masks is read and may be updated according to the access request and
+ * the matching rule.
+ * @masks_array_size must be equal to ARRAY_SIZE(*layer_masks).
+ *
+ * Returns true if the request is allowed (i.e. relevant layer masks for the
+ * request are empty).
+ */
+bool landlock_unmask_layers(const struct landlock_rule *const rule,
+ const access_mask_t access_request,
+ layer_mask_t (*const layer_masks)[],
+ const size_t masks_array_size)
+{
+ size_t layer_level;
+
+ if (!access_request || !layer_masks)
+ return true;
+ if (!rule)
+ return false;
+
+ /*
+ * An access is granted if, for each policy layer, at least one rule
+ * encountered on the pathwalk grants the requested access,
+ * regardless of its position in the layer stack. We must then check
+ * the remaining layers for each inode, from the first added layer to
+ * the last one. When there is multiple requested accesses, for each
+ * policy layer, the full set of requested accesses may not be granted
+ * by only one rule, but by the union (binary OR) of multiple rules.
+ * E.g. /a/b <execute> + /a <read> => /a/b <execute + read>
+ */
+ for (layer_level = 0; layer_level < rule->num_layers; layer_level++) {
+ const struct landlock_layer *const layer =
+ &rule->layers[layer_level];
+ const layer_mask_t layer_bit = BIT_ULL(layer->level - 1);
+ const unsigned long access_req = access_request;
+ unsigned long access_bit;
+ bool is_empty;
+
+ /*
+ * Records in @layer_masks which layer grants access to each
+ * requested access.
+ */
+ is_empty = true;
+ for_each_set_bit(access_bit, &access_req, masks_array_size) {
+ if (layer->access & BIT_ULL(access_bit))
+ (*layer_masks)[access_bit] &= ~layer_bit;
+ is_empty = is_empty && !(*layer_masks)[access_bit];
+ }
+ if (is_empty)
+ return true;
+ }
+ return false;
+}
+
+typedef access_mask_t
+get_access_mask_t(const struct landlock_ruleset *const ruleset,
+ const u16 layer_level);
+
+/**
+ * landlock_init_layer_masks - Initialize layer masks from an access request
+ *
+ * Populates @layer_masks such that for each access right in @access_request,
+ * the bits for all the layers are set where this access right is handled.
+ *
+ * @domain: The domain that defines the current restrictions.
+ * @access_request: The requested access rights to check.
+ * @layer_masks: It must contain %LANDLOCK_NUM_ACCESS_FS or
+ * %LANDLOCK_NUM_ACCESS_NET elements according to @key_type.
+ * @key_type: The key type to switch between access masks of different types.
+ *
+ * Returns: An access mask where each access right bit is set which is handled
+ * in any of the active layers in @domain.
+ */
+access_mask_t
+landlock_init_layer_masks(const struct landlock_ruleset *const domain,
+ const access_mask_t access_request,
+ layer_mask_t (*const layer_masks)[],
+ const enum landlock_key_type key_type)
+{
+ access_mask_t handled_accesses = 0;
+ size_t layer_level, num_access;
+ get_access_mask_t *get_access_mask;
+
+ switch (key_type) {
+ case LANDLOCK_KEY_INODE:
+ get_access_mask = landlock_get_fs_access_mask;
+ num_access = LANDLOCK_NUM_ACCESS_FS;
+ break;
+
+#if IS_ENABLED(CONFIG_INET)
+ case LANDLOCK_KEY_NET_PORT:
+ get_access_mask = landlock_get_net_access_mask;
+ num_access = LANDLOCK_NUM_ACCESS_NET;
+ break;
+#endif /* IS_ENABLED(CONFIG_INET) */
+
+ default:
+ WARN_ON_ONCE(1);
+ return 0;
+ }
+
+ memset(layer_masks, 0,
+ array_size(sizeof((*layer_masks)[0]), num_access));
+
+ /* An empty access request can happen because of O_WRONLY | O_RDWR. */
+ if (!access_request)
+ return 0;
+
+ /* Saves all handled accesses per layer. */
+ for (layer_level = 0; layer_level < domain->num_layers; layer_level++) {
+ const unsigned long access_req = access_request;
+ unsigned long access_bit;
+
+ for_each_set_bit(access_bit, &access_req, num_access) {
+ if (BIT_ULL(access_bit) &
+ get_access_mask(domain, layer_level)) {
+ (*layer_masks)[access_bit] |=
+ BIT_ULL(layer_level);
+ handled_accesses |= BIT_ULL(access_bit);
+ }
+ }
+ }
+ return handled_accesses;
+}
diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
index d43231b783e4..c7f1526784fd 100644
--- a/security/landlock/ruleset.h
+++ b/security/landlock/ruleset.h
@@ -15,16 +15,35 @@
#include <linux/rbtree.h>
#include <linux/refcount.h>
#include <linux/workqueue.h>
+#include <uapi/linux/landlock.h>
#include "limits.h"
#include "object.h"
+/*
+ * All access rights that are denied by default whether they are handled or not
+ * by a ruleset/layer. This must be ORed with all ruleset->access_masks[]
+ * entries when we need to get the absolute handled access masks.
+ */
+/* clang-format off */
+#define LANDLOCK_ACCESS_FS_INITIALLY_DENIED ( \
+ LANDLOCK_ACCESS_FS_REFER)
+/* clang-format on */
+
typedef u16 access_mask_t;
/* Makes sure all filesystem access rights can be stored. */
static_assert(BITS_PER_TYPE(access_mask_t) >= LANDLOCK_NUM_ACCESS_FS);
+/* Makes sure all network access rights can be stored. */
+static_assert(BITS_PER_TYPE(access_mask_t) >= LANDLOCK_NUM_ACCESS_NET);
/* Makes sure for_each_set_bit() and for_each_clear_bit() calls are OK. */
static_assert(sizeof(unsigned long) >= sizeof(access_mask_t));
+/* Ruleset access masks. */
+typedef u32 access_masks_t;
+/* Makes sure all ruleset access rights can be stored. */
+static_assert(BITS_PER_TYPE(access_masks_t) >=
+ LANDLOCK_NUM_ACCESS_FS + LANDLOCK_NUM_ACCESS_NET);
+
typedef u16 layer_mask_t;
/* Makes sure all layers can be checked. */
static_assert(BITS_PER_TYPE(layer_mask_t) >= LANDLOCK_MAX_NUM_LAYERS);
@@ -45,6 +64,52 @@ struct landlock_layer {
};
/**
+ * union landlock_key - Key of a ruleset's red-black tree
+ */
+union landlock_key {
+ /**
+ * @object: Pointer to identify a kernel object (e.g. an inode).
+ */
+ struct landlock_object *object;
+ /**
+ * @data: Raw data to identify an arbitrary 32-bit value
+ * (e.g. a TCP port).
+ */
+ uintptr_t data;
+};
+
+/**
+ * enum landlock_key_type - Type of &union landlock_key
+ */
+enum landlock_key_type {
+ /**
+ * @LANDLOCK_KEY_INODE: Type of &landlock_ruleset.root_inode's node
+ * keys.
+ */
+ LANDLOCK_KEY_INODE = 1,
+ /**
+ * @LANDLOCK_KEY_NET_PORT: Type of &landlock_ruleset.root_net_port's
+ * node keys.
+ */
+ LANDLOCK_KEY_NET_PORT,
+};
+
+/**
+ * struct landlock_id - Unique rule identifier for a ruleset
+ */
+struct landlock_id {
+ /**
+ * @key: Identifies either a kernel object (e.g. an inode) or
+ * a raw value (e.g. a TCP port).
+ */
+ union landlock_key key;
+ /**
+ * @type: Type of a landlock_ruleset's root tree.
+ */
+ const enum landlock_key_type type;
+};
+
+/**
* struct landlock_rule - Access rights tied to an object
*/
struct landlock_rule {
@@ -53,12 +118,13 @@ struct landlock_rule {
*/
struct rb_node node;
/**
- * @object: Pointer to identify a kernel object (e.g. an inode). This
- * is used as a key for this ruleset element. This pointer is set once
- * and never modified. It always points to an allocated object because
- * each rule increments the refcount of its object.
+ * @key: A union to identify either a kernel object (e.g. an inode) or
+ * a raw data value (e.g. a network socket port). This is used as a key
+ * for this ruleset element. The pointer is set once and never
+ * modified. It always points to an allocated object because each rule
+ * increments the refcount of its object.
*/
- struct landlock_object *object;
+ union landlock_key key;
/**
* @num_layers: Number of entries in @layers.
*/
@@ -67,7 +133,7 @@ struct landlock_rule {
* @layers: Stack of layers, from the latest to the newest, implemented
* as a flexible array member (FAM).
*/
- struct landlock_layer layers[];
+ struct landlock_layer layers[] __counted_by(num_layers);
};
/**
@@ -94,11 +160,23 @@ struct landlock_hierarchy {
*/
struct landlock_ruleset {
/**
- * @root: Root of a red-black tree containing &struct landlock_rule
- * nodes. Once a ruleset is tied to a process (i.e. as a domain), this
- * tree is immutable until @usage reaches zero.
+ * @root_inode: Root of a red-black tree containing &struct
+ * landlock_rule nodes with inode object. Once a ruleset is tied to a
+ * process (i.e. as a domain), this tree is immutable until @usage
+ * reaches zero.
*/
- struct rb_root root;
+ struct rb_root root_inode;
+
+#if IS_ENABLED(CONFIG_INET)
+ /**
+ * @root_net_port: Root of a red-black tree containing &struct
+ * landlock_rule nodes with network port. Once a ruleset is tied to a
+ * process (i.e. as a domain), this tree is immutable until @usage
+ * reaches zero.
+ */
+ struct rb_root root_net_port;
+#endif /* IS_ENABLED(CONFIG_INET) */
+
/**
* @hierarchy: Enables hierarchy identification even when a parent
* domain vanishes. This is needed for the ptrace protection.
@@ -110,7 +188,7 @@ struct landlock_ruleset {
* section. This is only used by
* landlock_put_ruleset_deferred() when @usage reaches zero.
* The fields @lock, @usage, @num_rules, @num_layers and
- * @fs_access_masks are then unused.
+ * @access_masks are then unused.
*/
struct work_struct work_free;
struct {
@@ -137,30 +215,31 @@ struct landlock_ruleset {
*/
u32 num_layers;
/**
- * @fs_access_masks: Contains the subset of filesystem
- * actions that are restricted by a ruleset. A domain
- * saves all layers of merged rulesets in a stack
- * (FAM), starting from the first layer to the last
- * one. These layers are used when merging rulesets,
- * for user space backward compatibility (i.e.
- * future-proof), and to properly handle merged
+ * @access_masks: Contains the subset of filesystem and
+ * network actions that are restricted by a ruleset.
+ * A domain saves all layers of merged rulesets in a
+ * stack (FAM), starting from the first layer to the
+ * last one. These layers are used when merging
+ * rulesets, for user space backward compatibility
+ * (i.e. future-proof), and to properly handle merged
* rulesets without overlapping access rights. These
* layers are set once and never changed for the
* lifetime of the ruleset.
*/
- access_mask_t fs_access_masks[];
+ access_masks_t access_masks[];
};
};
};
struct landlock_ruleset *
-landlock_create_ruleset(const access_mask_t fs_access_mask);
+landlock_create_ruleset(const access_mask_t access_mask_fs,
+ const access_mask_t access_mask_net);
void landlock_put_ruleset(struct landlock_ruleset *const ruleset);
void landlock_put_ruleset_deferred(struct landlock_ruleset *const ruleset);
int landlock_insert_rule(struct landlock_ruleset *const ruleset,
- struct landlock_object *const object,
+ const struct landlock_id id,
const access_mask_t access);
struct landlock_ruleset *
@@ -169,7 +248,7 @@ landlock_merge_ruleset(struct landlock_ruleset *const parent,
const struct landlock_rule *
landlock_find_rule(const struct landlock_ruleset *const ruleset,
- const struct landlock_object *const object);
+ const struct landlock_id id);
static inline void landlock_get_ruleset(struct landlock_ruleset *const ruleset)
{
@@ -177,4 +256,68 @@ static inline void landlock_get_ruleset(struct landlock_ruleset *const ruleset)
refcount_inc(&ruleset->usage);
}
+static inline void
+landlock_add_fs_access_mask(struct landlock_ruleset *const ruleset,
+ const access_mask_t fs_access_mask,
+ const u16 layer_level)
+{
+ access_mask_t fs_mask = fs_access_mask & LANDLOCK_MASK_ACCESS_FS;
+
+ /* Should already be checked in sys_landlock_create_ruleset(). */
+ WARN_ON_ONCE(fs_access_mask != fs_mask);
+ ruleset->access_masks[layer_level] |=
+ (fs_mask << LANDLOCK_SHIFT_ACCESS_FS);
+}
+
+static inline void
+landlock_add_net_access_mask(struct landlock_ruleset *const ruleset,
+ const access_mask_t net_access_mask,
+ const u16 layer_level)
+{
+ access_mask_t net_mask = net_access_mask & LANDLOCK_MASK_ACCESS_NET;
+
+ /* Should already be checked in sys_landlock_create_ruleset(). */
+ WARN_ON_ONCE(net_access_mask != net_mask);
+ ruleset->access_masks[layer_level] |=
+ (net_mask << LANDLOCK_SHIFT_ACCESS_NET);
+}
+
+static inline access_mask_t
+landlock_get_raw_fs_access_mask(const struct landlock_ruleset *const ruleset,
+ const u16 layer_level)
+{
+ return (ruleset->access_masks[layer_level] >>
+ LANDLOCK_SHIFT_ACCESS_FS) &
+ LANDLOCK_MASK_ACCESS_FS;
+}
+
+static inline access_mask_t
+landlock_get_fs_access_mask(const struct landlock_ruleset *const ruleset,
+ const u16 layer_level)
+{
+ /* Handles all initially denied by default access rights. */
+ return landlock_get_raw_fs_access_mask(ruleset, layer_level) |
+ LANDLOCK_ACCESS_FS_INITIALLY_DENIED;
+}
+
+static inline access_mask_t
+landlock_get_net_access_mask(const struct landlock_ruleset *const ruleset,
+ const u16 layer_level)
+{
+ return (ruleset->access_masks[layer_level] >>
+ LANDLOCK_SHIFT_ACCESS_NET) &
+ LANDLOCK_MASK_ACCESS_NET;
+}
+
+bool landlock_unmask_layers(const struct landlock_rule *const rule,
+ const access_mask_t access_request,
+ layer_mask_t (*const layer_masks)[],
+ const size_t masks_array_size);
+
+access_mask_t
+landlock_init_layer_masks(const struct landlock_ruleset *const domain,
+ const access_mask_t access_request,
+ layer_mask_t (*const layer_masks)[],
+ const enum landlock_key_type key_type);
+
#endif /* _SECURITY_LANDLOCK_RULESET_H */
diff --git a/security/landlock/setup.c b/security/landlock/setup.c
index 0f6113528fa4..3e11d303542f 100644
--- a/security/landlock/setup.c
+++ b/security/landlock/setup.c
@@ -12,6 +12,7 @@
#include "common.h"
#include "cred.h"
#include "fs.h"
+#include "net.h"
#include "ptrace.h"
#include "setup.h"
@@ -29,6 +30,7 @@ static int __init landlock_init(void)
landlock_add_cred_hooks();
landlock_add_ptrace_hooks();
landlock_add_fs_hooks();
+ landlock_add_net_hooks();
landlock_initialized = true;
pr_info("Up and running.\n");
return 0;
diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
index 245cc650a4dc..898358f57fa0 100644
--- a/security/landlock/syscalls.c
+++ b/security/landlock/syscalls.c
@@ -29,6 +29,7 @@
#include "cred.h"
#include "fs.h"
#include "limits.h"
+#include "net.h"
#include "ruleset.h"
#include "setup.h"
@@ -74,7 +75,8 @@ static void build_check_abi(void)
{
struct landlock_ruleset_attr ruleset_attr;
struct landlock_path_beneath_attr path_beneath_attr;
- size_t ruleset_size, path_beneath_size;
+ struct landlock_net_port_attr net_port_attr;
+ size_t ruleset_size, path_beneath_size, net_port_size;
/*
* For each user space ABI structures, first checks that there is no
@@ -82,13 +84,19 @@ static void build_check_abi(void)
* struct size.
*/
ruleset_size = sizeof(ruleset_attr.handled_access_fs);
+ ruleset_size += sizeof(ruleset_attr.handled_access_net);
BUILD_BUG_ON(sizeof(ruleset_attr) != ruleset_size);
- BUILD_BUG_ON(sizeof(ruleset_attr) != 8);
+ BUILD_BUG_ON(sizeof(ruleset_attr) != 16);
path_beneath_size = sizeof(path_beneath_attr.allowed_access);
path_beneath_size += sizeof(path_beneath_attr.parent_fd);
BUILD_BUG_ON(sizeof(path_beneath_attr) != path_beneath_size);
BUILD_BUG_ON(sizeof(path_beneath_attr) != 12);
+
+ net_port_size = sizeof(net_port_attr.allowed_access);
+ net_port_size += sizeof(net_port_attr.port);
+ BUILD_BUG_ON(sizeof(net_port_attr) != net_port_size);
+ BUILD_BUG_ON(sizeof(net_port_attr) != 16);
}
/* Ruleset handling */
@@ -129,7 +137,7 @@ static const struct file_operations ruleset_fops = {
.write = fop_dummy_write,
};
-#define LANDLOCK_ABI_VERSION 3
+#define LANDLOCK_ABI_VERSION 4
/**
* sys_landlock_create_ruleset - Create a new ruleset
@@ -188,8 +196,14 @@ SYSCALL_DEFINE3(landlock_create_ruleset,
LANDLOCK_MASK_ACCESS_FS)
return -EINVAL;
+ /* Checks network content (and 32-bits cast). */
+ if ((ruleset_attr.handled_access_net | LANDLOCK_MASK_ACCESS_NET) !=
+ LANDLOCK_MASK_ACCESS_NET)
+ return -EINVAL;
+
/* Checks arguments and transforms to kernel struct. */
- ruleset = landlock_create_ruleset(ruleset_attr.handled_access_fs);
+ ruleset = landlock_create_ruleset(ruleset_attr.handled_access_fs,
+ ruleset_attr.handled_access_net);
if (IS_ERR(ruleset))
return PTR_ERR(ruleset);
@@ -274,13 +288,84 @@ out_fdput:
return err;
}
+static int add_rule_path_beneath(struct landlock_ruleset *const ruleset,
+ const void __user *const rule_attr)
+{
+ struct landlock_path_beneath_attr path_beneath_attr;
+ struct path path;
+ int res, err;
+ access_mask_t mask;
+
+ /* Copies raw user space buffer. */
+ res = copy_from_user(&path_beneath_attr, rule_attr,
+ sizeof(path_beneath_attr));
+ if (res)
+ return -EFAULT;
+
+ /*
+ * Informs about useless rule: empty allowed_access (i.e. deny rules)
+ * are ignored in path walks.
+ */
+ if (!path_beneath_attr.allowed_access)
+ return -ENOMSG;
+
+ /* Checks that allowed_access matches the @ruleset constraints. */
+ mask = landlock_get_raw_fs_access_mask(ruleset, 0);
+ if ((path_beneath_attr.allowed_access | mask) != mask)
+ return -EINVAL;
+
+ /* Gets and checks the new rule. */
+ err = get_path_from_fd(path_beneath_attr.parent_fd, &path);
+ if (err)
+ return err;
+
+ /* Imports the new rule. */
+ err = landlock_append_fs_rule(ruleset, &path,
+ path_beneath_attr.allowed_access);
+ path_put(&path);
+ return err;
+}
+
+static int add_rule_net_port(struct landlock_ruleset *ruleset,
+ const void __user *const rule_attr)
+{
+ struct landlock_net_port_attr net_port_attr;
+ int res;
+ access_mask_t mask;
+
+ /* Copies raw user space buffer. */
+ res = copy_from_user(&net_port_attr, rule_attr, sizeof(net_port_attr));
+ if (res)
+ return -EFAULT;
+
+ /*
+ * Informs about useless rule: empty allowed_access (i.e. deny rules)
+ * are ignored by network actions.
+ */
+ if (!net_port_attr.allowed_access)
+ return -ENOMSG;
+
+ /* Checks that allowed_access matches the @ruleset constraints. */
+ mask = landlock_get_net_access_mask(ruleset, 0);
+ if ((net_port_attr.allowed_access | mask) != mask)
+ return -EINVAL;
+
+ /* Denies inserting a rule with port greater than 65535. */
+ if (net_port_attr.port > U16_MAX)
+ return -EINVAL;
+
+ /* Imports the new rule. */
+ return landlock_append_net_rule(ruleset, net_port_attr.port,
+ net_port_attr.allowed_access);
+}
+
/**
* sys_landlock_add_rule - Add a new rule to a ruleset
*
* @ruleset_fd: File descriptor tied to the ruleset that should be extended
* with the new rule.
- * @rule_type: Identify the structure type pointed to by @rule_attr (only
- * %LANDLOCK_RULE_PATH_BENEATH for now).
+ * @rule_type: Identify the structure type pointed to by @rule_attr:
+ * %LANDLOCK_RULE_PATH_BENEATH or %LANDLOCK_RULE_NET_PORT.
* @rule_attr: Pointer to a rule (only of type &struct
* landlock_path_beneath_attr for now).
* @flags: Must be 0.
@@ -291,9 +376,13 @@ out_fdput:
* Possible returned errors are:
*
* - %EOPNOTSUPP: Landlock is supported by the kernel but disabled at boot time;
+ * - %EAFNOSUPPORT: @rule_type is %LANDLOCK_RULE_NET_PORT but TCP/IP is not
+ * supported by the running kernel;
* - %EINVAL: @flags is not 0, or inconsistent access in the rule (i.e.
- * &landlock_path_beneath_attr.allowed_access is not a subset of the
- * ruleset handled accesses);
+ * &landlock_path_beneath_attr.allowed_access or
+ * &landlock_net_port_attr.allowed_access is not a subset of the
+ * ruleset handled accesses), or &landlock_net_port_attr.port is
+ * greater than 65535;
* - %ENOMSG: Empty accesses (e.g. &landlock_path_beneath_attr.allowed_access);
* - %EBADF: @ruleset_fd is not a file descriptor for the current thread, or a
* member of @rule_attr is not a file descriptor as expected;
@@ -306,10 +395,8 @@ SYSCALL_DEFINE4(landlock_add_rule, const int, ruleset_fd,
const enum landlock_rule_type, rule_type,
const void __user *const, rule_attr, const __u32, flags)
{
- struct landlock_path_beneath_attr path_beneath_attr;
- struct path path;
struct landlock_ruleset *ruleset;
- int res, err;
+ int err;
if (!landlock_initialized)
return -EOPNOTSUPP;
@@ -323,48 +410,17 @@ SYSCALL_DEFINE4(landlock_add_rule, const int, ruleset_fd,
if (IS_ERR(ruleset))
return PTR_ERR(ruleset);
- if (rule_type != LANDLOCK_RULE_PATH_BENEATH) {
+ switch (rule_type) {
+ case LANDLOCK_RULE_PATH_BENEATH:
+ err = add_rule_path_beneath(ruleset, rule_attr);
+ break;
+ case LANDLOCK_RULE_NET_PORT:
+ err = add_rule_net_port(ruleset, rule_attr);
+ break;
+ default:
err = -EINVAL;
- goto out_put_ruleset;
- }
-
- /* Copies raw user space buffer, only one type for now. */
- res = copy_from_user(&path_beneath_attr, rule_attr,
- sizeof(path_beneath_attr));
- if (res) {
- err = -EFAULT;
- goto out_put_ruleset;
+ break;
}
-
- /*
- * Informs about useless rule: empty allowed_access (i.e. deny rules)
- * are ignored in path walks.
- */
- if (!path_beneath_attr.allowed_access) {
- err = -ENOMSG;
- goto out_put_ruleset;
- }
- /*
- * Checks that allowed_access matches the @ruleset constraints
- * (ruleset->fs_access_masks[0] is automatically upgraded to 64-bits).
- */
- if ((path_beneath_attr.allowed_access | ruleset->fs_access_masks[0]) !=
- ruleset->fs_access_masks[0]) {
- err = -EINVAL;
- goto out_put_ruleset;
- }
-
- /* Gets and checks the new rule. */
- err = get_path_from_fd(path_beneath_attr.parent_fd, &path);
- if (err)
- goto out_put_ruleset;
-
- /* Imports the new rule. */
- err = landlock_append_fs_rule(ruleset, &path,
- path_beneath_attr.allowed_access);
- path_put(&path);
-
-out_put_ruleset:
landlock_put_ruleset(ruleset);
return err;
}
diff --git a/security/loadpin/loadpin.c b/security/loadpin/loadpin.c
index ebae964f7cc9..a9d40456a064 100644
--- a/security/loadpin/loadpin.c
+++ b/security/loadpin/loadpin.c
@@ -336,6 +336,7 @@ static int read_trusted_verity_root_digests(unsigned int fd)
rc = -ENOMEM;
goto err;
}
+ trd->len = len;
if (hex2bin(trd->data, d, len)) {
kfree(trd);
@@ -343,8 +344,6 @@ static int read_trusted_verity_root_digests(unsigned int fd)
goto err;
}
- trd->len = len;
-
list_add_tail(&trd->node, &dm_verity_loadpin_trusted_root_digests);
}
diff --git a/security/security.c b/security/security.c
index b720424ca37d..dcb3e7014f9b 100644
--- a/security/security.c
+++ b/security/security.c
@@ -31,8 +31,6 @@
#include <linux/msg.h>
#include <net/flow.h>
-#define MAX_LSM_EVM_XATTR 2
-
/* How many LSMs were built into the kernel? */
#define LSM_COUNT (__end_lsm_info - __start_lsm_info)
@@ -212,6 +210,8 @@ static void __init lsm_set_blob_sizes(struct lsm_blob_sizes *needed)
lsm_set_blob_size(&needed->lbs_msg_msg, &blob_sizes.lbs_msg_msg);
lsm_set_blob_size(&needed->lbs_superblock, &blob_sizes.lbs_superblock);
lsm_set_blob_size(&needed->lbs_task, &blob_sizes.lbs_task);
+ lsm_set_blob_size(&needed->lbs_xattr_count,
+ &blob_sizes.lbs_xattr_count);
}
/* Prepare LSM for initialization. */
@@ -378,6 +378,7 @@ static void __init ordered_lsm_init(void)
init_debug("msg_msg blob size = %d\n", blob_sizes.lbs_msg_msg);
init_debug("superblock blob size = %d\n", blob_sizes.lbs_superblock);
init_debug("task blob size = %d\n", blob_sizes.lbs_task);
+ init_debug("xattr slots = %d\n", blob_sizes.lbs_xattr_count);
/*
* Create any kmem_caches needed for blobs
@@ -840,7 +841,7 @@ int security_binder_transfer_binder(const struct cred *from,
* Return: Returns 0 if permission is granted.
*/
int security_binder_transfer_file(const struct cred *from,
- const struct cred *to, struct file *file)
+ const struct cred *to, const struct file *file)
{
return call_int_hook(binder_transfer_file, 0, from, to, file);
}
@@ -893,7 +894,7 @@ int security_ptrace_traceme(struct task_struct *parent)
*
* Return: Returns 0 if the capability sets were successfully obtained.
*/
-int security_capget(struct task_struct *target,
+int security_capget(const struct task_struct *target,
kernel_cap_t *effective,
kernel_cap_t *inheritable,
kernel_cap_t *permitted)
@@ -956,7 +957,7 @@ int security_capable(const struct cred *cred,
*
* Return: Returns 0 if permission is granted.
*/
-int security_quotactl(int cmds, int type, int id, struct super_block *sb)
+int security_quotactl(int cmds, int type, int id, const struct super_block *sb)
{
return call_int_hook(quotactl, 0, cmds, type, id, sb);
}
@@ -1078,7 +1079,7 @@ int security_bprm_creds_for_exec(struct linux_binprm *bprm)
*
* Return: Returns 0 if the hook is successful and permission is granted.
*/
-int security_bprm_creds_from_file(struct linux_binprm *bprm, struct file *file)
+int security_bprm_creds_from_file(struct linux_binprm *bprm, const struct file *file)
{
return call_int_hook(bprm_creds_from_file, 0, bprm, file);
}
@@ -1117,7 +1118,7 @@ int security_bprm_check(struct linux_binprm *bprm)
* open file descriptors to which access will no longer be granted when the
* attributes are changed. This is called immediately before commit_creds().
*/
-void security_bprm_committing_creds(struct linux_binprm *bprm)
+void security_bprm_committing_creds(const struct linux_binprm *bprm)
{
call_void_hook(bprm_committing_creds, bprm);
}
@@ -1133,12 +1134,26 @@ void security_bprm_committing_creds(struct linux_binprm *bprm)
* process such as clearing out non-inheritable signal state. This is called
* immediately after commit_creds().
*/
-void security_bprm_committed_creds(struct linux_binprm *bprm)
+void security_bprm_committed_creds(const struct linux_binprm *bprm)
{
call_void_hook(bprm_committed_creds, bprm);
}
/**
+ * security_fs_context_submount() - Initialise fc->security
+ * @fc: new filesystem context
+ * @reference: dentry reference for submount/remount
+ *
+ * Fill out the ->security field for a new fs_context.
+ *
+ * Return: Returns 0 on success or negative error code on failure.
+ */
+int security_fs_context_submount(struct fs_context *fc, struct super_block *reference)
+{
+ return call_int_hook(fs_context_submount, 0, fc, reference);
+}
+
+/**
* security_fs_context_dup() - Duplicate a fs_context LSM blob
* @fc: destination filesystem context
* @src_fc: source filesystem context
@@ -1304,7 +1319,7 @@ EXPORT_SYMBOL(security_sb_remount);
*
* Return: Returns 0 if permission is granted.
*/
-int security_sb_kern_mount(struct super_block *sb)
+int security_sb_kern_mount(const struct super_block *sb)
{
return call_int_hook(sb_kern_mount, 0, sb);
}
@@ -1591,46 +1606,70 @@ EXPORT_SYMBOL(security_dentry_create_files_as);
* created inode and set up the incore security field for the new inode. This
* hook is called by the fs code as part of the inode creation transaction and
* provides for atomic labeling of the inode, unlike the post_create/mkdir/...
- * hooks called by the VFS. The hook function is expected to allocate the name
- * and value via kmalloc, with the caller being responsible for calling kfree
- * after using them. If the security module does not use security attributes
- * or does not wish to put a security attribute on this particular inode, then
- * it should return -EOPNOTSUPP to skip this processing.
+ * hooks called by the VFS.
+ *
+ * The hook function is expected to populate the xattrs array, by calling
+ * lsm_get_xattr_slot() to retrieve the slots reserved by the security module
+ * with the lbs_xattr_count field of the lsm_blob_sizes structure. For each
+ * slot, the hook function should set ->name to the attribute name suffix
+ * (e.g. selinux), to allocate ->value (will be freed by the caller) and set it
+ * to the attribute value, to set ->value_len to the length of the value. If
+ * the security module does not use security attributes or does not wish to put
+ * a security attribute on this particular inode, then it should return
+ * -EOPNOTSUPP to skip this processing.
*
- * Return: Returns 0 on success, -EOPNOTSUPP if no security attribute is
- * needed, or -ENOMEM on memory allocation failure.
+ * Return: Returns 0 if the LSM successfully initialized all of the inode
+ * security attributes that are required, negative values otherwise.
*/
int security_inode_init_security(struct inode *inode, struct inode *dir,
const struct qstr *qstr,
const initxattrs initxattrs, void *fs_data)
{
- struct xattr new_xattrs[MAX_LSM_EVM_XATTR + 1];
- struct xattr *lsm_xattr, *evm_xattr, *xattr;
- int ret;
+ struct security_hook_list *hp;
+ struct xattr *new_xattrs = NULL;
+ int ret = -EOPNOTSUPP, xattr_count = 0;
if (unlikely(IS_PRIVATE(inode)))
return 0;
- if (!initxattrs)
- return call_int_hook(inode_init_security, -EOPNOTSUPP, inode,
- dir, qstr, NULL, NULL, NULL);
- memset(new_xattrs, 0, sizeof(new_xattrs));
- lsm_xattr = new_xattrs;
- ret = call_int_hook(inode_init_security, -EOPNOTSUPP, inode, dir, qstr,
- &lsm_xattr->name,
- &lsm_xattr->value,
- &lsm_xattr->value_len);
- if (ret)
+ if (!blob_sizes.lbs_xattr_count)
+ return 0;
+
+ if (initxattrs) {
+ /* Allocate +1 for EVM and +1 as terminator. */
+ new_xattrs = kcalloc(blob_sizes.lbs_xattr_count + 2,
+ sizeof(*new_xattrs), GFP_NOFS);
+ if (!new_xattrs)
+ return -ENOMEM;
+ }
+
+ hlist_for_each_entry(hp, &security_hook_heads.inode_init_security,
+ list) {
+ ret = hp->hook.inode_init_security(inode, dir, qstr, new_xattrs,
+ &xattr_count);
+ if (ret && ret != -EOPNOTSUPP)
+ goto out;
+ /*
+ * As documented in lsm_hooks.h, -EOPNOTSUPP in this context
+ * means that the LSM is not willing to provide an xattr, not
+ * that it wants to signal an error. Thus, continue to invoke
+ * the remaining LSMs.
+ */
+ }
+
+ /* If initxattrs() is NULL, xattr_count is zero, skip the call. */
+ if (!xattr_count)
goto out;
- evm_xattr = lsm_xattr + 1;
- ret = evm_inode_init_security(inode, lsm_xattr, evm_xattr);
+ ret = evm_inode_init_security(inode, dir, qstr, new_xattrs,
+ &xattr_count);
if (ret)
goto out;
ret = initxattrs(inode, new_xattrs, fs_data);
out:
- for (xattr = new_xattrs; xattr->value != NULL; xattr++)
- kfree(xattr->value);
+ for (; xattr_count > 0; xattr_count--)
+ kfree(new_xattrs[xattr_count - 1].value);
+ kfree(new_xattrs);
return (ret == -EOPNOTSUPP) ? 0 : ret;
}
EXPORT_SYMBOL(security_inode_init_security);
@@ -2717,7 +2756,7 @@ int security_file_lock(struct file *file, unsigned int cmd)
/**
* security_file_fcntl() - Check if fcntl() op is allowed
* @file: file
- * @cmd: fnctl command
+ * @cmd: fcntl command
* @arg: command argument
*
* Check permission before allowing the file operation specified by @cmd from
@@ -3918,7 +3957,7 @@ void security_inode_invalidate_secctx(struct inode *inode)
EXPORT_SYMBOL(security_inode_invalidate_secctx);
/**
- * security_inode_notifysecctx() - Nofify the LSM of an inode's security label
+ * security_inode_notifysecctx() - Notify the LSM of an inode's security label
* @inode: inode
* @ctx: secctx
* @ctxlen: length of secctx
@@ -4396,7 +4435,14 @@ void security_sk_clone(const struct sock *sk, struct sock *newsk)
}
EXPORT_SYMBOL(security_sk_clone);
-void security_sk_classify_flow(struct sock *sk, struct flowi_common *flic)
+/**
+ * security_sk_classify_flow() - Set a flow's secid based on socket
+ * @sk: original socket
+ * @flic: target flow
+ *
+ * Set the target flow's secid to socket's secid.
+ */
+void security_sk_classify_flow(const struct sock *sk, struct flowi_common *flic)
{
call_void_hook(sk_getsecid, sk, &flic->flowic_secid);
}
diff --git a/security/selinux/Kconfig b/security/selinux/Kconfig
index 95a186ec0fcb..61abc1e094a8 100644
--- a/security/selinux/Kconfig
+++ b/security/selinux/Kconfig
@@ -1,16 +1,16 @@
# SPDX-License-Identifier: GPL-2.0-only
config SECURITY_SELINUX
- bool "NSA SELinux Support"
+ bool "SELinux Support"
depends on SECURITY_NETWORK && AUDIT && NET && INET
select NETWORK_SECMARK
default n
help
- This selects NSA Security-Enhanced Linux (SELinux).
+ This selects Security-Enhanced Linux (SELinux).
You will also need a policy configuration and a labeled filesystem.
If you are unsure how to answer this question, answer N.
config SECURITY_SELINUX_BOOTPARAM
- bool "NSA SELinux boot parameter"
+ bool "SELinux boot parameter"
depends on SECURITY_SELINUX
default n
help
@@ -24,11 +24,11 @@ config SECURITY_SELINUX_BOOTPARAM
If you are unsure how to answer this question, answer N.
config SECURITY_SELINUX_DEVELOP
- bool "NSA SELinux Development Support"
+ bool "SELinux Development Support"
depends on SECURITY_SELINUX
default y
help
- This enables the development support option of NSA SELinux,
+ This enables the development support option of SELinux,
which is useful for experimenting with SELinux and developing
policies. If unsure, say Y. With this option enabled, the
kernel will start in permissive mode (log everything, deny nothing)
@@ -38,7 +38,7 @@ config SECURITY_SELINUX_DEVELOP
/sys/fs/selinux/enforce.
config SECURITY_SELINUX_AVC_STATS
- bool "NSA SELinux AVC Statistics"
+ bool "SELinux AVC Statistics"
depends on SECURITY_SELINUX
default y
help
@@ -47,7 +47,7 @@ config SECURITY_SELINUX_AVC_STATS
tools such as avcstat.
config SECURITY_SELINUX_SIDTAB_HASH_BITS
- int "NSA SELinux sidtab hashtable size"
+ int "SELinux sidtab hashtable size"
depends on SECURITY_SELINUX
range 8 13
default 9
@@ -59,7 +59,7 @@ config SECURITY_SELINUX_SIDTAB_HASH_BITS
will ensure that lookups times are short and stable.
config SECURITY_SELINUX_SID2STR_CACHE_SIZE
- int "NSA SELinux SID to context string translation cache size"
+ int "SELinux SID to context string translation cache size"
depends on SECURITY_SELINUX
default 256
help
@@ -68,3 +68,22 @@ config SECURITY_SELINUX_SID2STR_CACHE_SIZE
conversion. Setting this option to 0 disables the cache completely.
If unsure, keep the default value.
+
+config SECURITY_SELINUX_DEBUG
+ bool "SELinux kernel debugging support"
+ depends on SECURITY_SELINUX
+ default n
+ help
+ This enables debugging code designed to help SELinux kernel
+ developers, unless you know what this does in the kernel code you
+ should leave this disabled.
+
+ To fine control the messages to be printed enable
+ CONFIG_DYNAMIC_DEBUG and see
+ Documentation/admin-guide/dynamic-debug-howto.rst for additional
+ information.
+
+ Example usage:
+
+ echo -n 'file "security/selinux/*" +p' > \
+ /proc/dynamic_debug/control
diff --git a/security/selinux/Makefile b/security/selinux/Makefile
index 836379639058..c47519ed8156 100644
--- a/security/selinux/Makefile
+++ b/security/selinux/Makefile
@@ -12,6 +12,8 @@ obj-$(CONFIG_SECURITY_SELINUX) := selinux.o
ccflags-y := -I$(srctree)/security/selinux -I$(srctree)/security/selinux/include
+ccflags-$(CONFIG_SECURITY_SELINUX_DEBUG) += -DDEBUG
+
selinux-y := avc.o hooks.o selinuxfs.o netlink.o nlmsgtab.o netif.o \
netnode.o netport.o status.o \
ss/ebitmap.o ss/hashtab.o ss/symtab.o ss/sidtab.o ss/avtab.o \
diff --git a/security/selinux/avc.c b/security/selinux/avc.c
index 1074db66e5ff..32eb67fb3e42 100644
--- a/security/selinux/avc.c
+++ b/security/selinux/avc.c
@@ -2,7 +2,7 @@
/*
* Implementation of the kernel access vector cache (AVC).
*
- * Authors: Stephen Smalley, <[email protected]>
+ * Authors: Stephen Smalley, <[email protected]>
* James Morris <[email protected]>
*
* Update: KaiGai, Kohei <[email protected]>
@@ -122,7 +122,7 @@ static struct kmem_cache *avc_xperms_data_cachep __ro_after_init;
static struct kmem_cache *avc_xperms_decision_cachep __ro_after_init;
static struct kmem_cache *avc_xperms_cachep __ro_after_init;
-static inline int avc_hash(u32 ssid, u32 tsid, u16 tclass)
+static inline u32 avc_hash(u32 ssid, u32 tsid, u16 tclass)
{
return (ssid ^ (tsid<<2) ^ (tclass<<4)) & (AVC_CACHE_SLOTS - 1);
}
@@ -523,7 +523,7 @@ static void avc_node_populate(struct avc_node *node, u32 ssid, u32 tsid, u16 tcl
static inline struct avc_node *avc_search_node(u32 ssid, u32 tsid, u16 tclass)
{
struct avc_node *node, *ret = NULL;
- int hvalue;
+ u32 hvalue;
struct hlist_head *head;
hvalue = avc_hash(ssid, tsid, tclass);
@@ -566,7 +566,7 @@ static struct avc_node *avc_lookup(u32 ssid, u32 tsid, u16 tclass)
return NULL;
}
-static int avc_latest_notif_update(int seqno, int is_insert)
+static int avc_latest_notif_update(u32 seqno, int is_insert)
{
int ret = 0;
static DEFINE_SPINLOCK(notif_lock);
@@ -609,7 +609,7 @@ static void avc_insert(u32 ssid, u32 tsid, u16 tclass,
struct av_decision *avd, struct avc_xperms_node *xp_node)
{
struct avc_node *pos, *node = NULL;
- int hvalue;
+ u32 hvalue;
unsigned long flag;
spinlock_t *lock;
struct hlist_head *head;
@@ -654,9 +654,9 @@ static void avc_audit_pre_callback(struct audit_buffer *ab, void *a)
{
struct common_audit_data *ad = a;
struct selinux_audit_data *sad = ad->selinux_audit_data;
- u32 av = sad->audited;
+ u32 av = sad->audited, perm;
const char *const *perms;
- int i, perm;
+ u32 i;
audit_log_format(ab, "avc: %s ", sad->denied ? "denied" : "granted");
@@ -833,7 +833,8 @@ static int avc_update_node(u32 event, u32 perms, u8 driver, u8 xperm, u32 ssid,
struct extended_perms_decision *xpd,
u32 flags)
{
- int hvalue, rc = 0;
+ u32 hvalue;
+ int rc = 0;
unsigned long flag;
struct avc_node *pos, *node, *orig = NULL;
struct hlist_head *head;
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index d06e350fedee..feda711c6b7b 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -1,10 +1,10 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * NSA Security-Enhanced Linux (SELinux) security module
+ * Security-Enhanced Linux (SELinux) security module
*
* This file contains the SELinux hook function implementations.
*
- * Authors: Stephen Smalley, <[email protected]>
+ * Authors: Stephen Smalley, <[email protected]>
* Chris Vance, <[email protected]>
* Wayne Salamon, <[email protected]>
* James Morris <[email protected]>
@@ -104,6 +104,8 @@
#include "audit.h"
#include "avc_ss.h"
+#define SELINUX_INODE_INIT_XATTRS 1
+
struct selinux_state selinux_state;
/* SECMARK reference count */
@@ -224,6 +226,31 @@ static inline u32 cred_sid(const struct cred *cred)
return tsec->sid;
}
+static void __ad_net_init(struct common_audit_data *ad,
+ struct lsm_network_audit *net,
+ int ifindex, struct sock *sk, u16 family)
+{
+ ad->type = LSM_AUDIT_DATA_NET;
+ ad->u.net = net;
+ net->netif = ifindex;
+ net->sk = sk;
+ net->family = family;
+}
+
+static void ad_net_init_from_sk(struct common_audit_data *ad,
+ struct lsm_network_audit *net,
+ struct sock *sk)
+{
+ __ad_net_init(ad, net, 0, sk, 0);
+}
+
+static void ad_net_init_from_iif(struct common_audit_data *ad,
+ struct lsm_network_audit *net,
+ int ifindex, u16 family)
+{
+ __ad_net_init(ad, net, ifindex, NULL, family);
+}
+
/*
* get the objective security ID of a task
*/
@@ -1125,7 +1152,7 @@ static inline int default_protocol_dgram(int protocol)
static inline u16 socket_type_to_security_class(int family, int type, int protocol)
{
- int extsockclass = selinux_policycap_extsockclass();
+ bool extsockclass = selinux_policycap_extsockclass();
switch (family) {
case PF_UNIX:
@@ -1689,7 +1716,7 @@ static inline int file_path_has_perm(const struct cred *cred,
}
#ifdef CONFIG_BPF_SYSCALL
-static int bpf_fd_pass(struct file *file, u32 sid);
+static int bpf_fd_pass(const struct file *file, u32 sid);
#endif
/* Check whether a task can use an open file descriptor to
@@ -1910,7 +1937,7 @@ static inline int may_rename(struct inode *old_dir,
/* Check whether a task can perform a filesystem operation. */
static int superblock_has_perm(const struct cred *cred,
- struct super_block *sb,
+ const struct super_block *sb,
u32 perms,
struct common_audit_data *ad)
{
@@ -1950,7 +1977,7 @@ static inline u32 file_mask_to_av(int mode, int mask)
}
/* Convert a Linux file to an access vector. */
-static inline u32 file_to_av(struct file *file)
+static inline u32 file_to_av(const struct file *file)
{
u32 av = 0;
@@ -2025,7 +2052,7 @@ static int selinux_binder_transfer_binder(const struct cred *from,
static int selinux_binder_transfer_file(const struct cred *from,
const struct cred *to,
- struct file *file)
+ const struct file *file)
{
u32 sid = cred_sid(to);
struct file_security_struct *fsec = selinux_file(file);
@@ -2080,7 +2107,7 @@ static int selinux_ptrace_traceme(struct task_struct *parent)
SECCLASS_PROCESS, PROCESS__PTRACE, NULL);
}
-static int selinux_capget(struct task_struct *target, kernel_cap_t *effective,
+static int selinux_capget(const struct task_struct *target, kernel_cap_t *effective,
kernel_cap_t *inheritable, kernel_cap_t *permitted)
{
return avc_has_perm(current_sid(), task_sid_obj(target),
@@ -2112,7 +2139,7 @@ static int selinux_capable(const struct cred *cred, struct user_namespace *ns,
return cred_has_capability(cred, cap, opts, ns == &init_user_ns);
}
-static int selinux_quotactl(int cmds, int type, int id, struct super_block *sb)
+static int selinux_quotactl(int cmds, int type, int id, const struct super_block *sb)
{
const struct cred *cred = current_cred();
int rc = 0;
@@ -2428,7 +2455,7 @@ static inline void flush_unauthorized_files(const struct cred *cred,
/*
* Prepare a process for imminent new credential changes due to exec
*/
-static void selinux_bprm_committing_creds(struct linux_binprm *bprm)
+static void selinux_bprm_committing_creds(const struct linux_binprm *bprm)
{
struct task_security_struct *new_tsec;
struct rlimit *rlim, *initrlim;
@@ -2474,7 +2501,7 @@ static void selinux_bprm_committing_creds(struct linux_binprm *bprm)
* Clean up the process immediately after the installation of new credentials
* due to exec
*/
-static void selinux_bprm_committed_creds(struct linux_binprm *bprm)
+static void selinux_bprm_committed_creds(const struct linux_binprm *bprm)
{
const struct task_security_struct *tsec = selinux_cred(current_cred());
u32 osid, sid;
@@ -2694,7 +2721,7 @@ out_bad_option:
return -EINVAL;
}
-static int selinux_sb_kern_mount(struct super_block *sb)
+static int selinux_sb_kern_mount(const struct super_block *sb)
{
const struct cred *cred = current_cred();
struct common_audit_data ad;
@@ -2745,6 +2772,33 @@ static int selinux_umount(struct vfsmount *mnt, int flags)
FILESYSTEM__UNMOUNT, NULL);
}
+static int selinux_fs_context_submount(struct fs_context *fc,
+ struct super_block *reference)
+{
+ const struct superblock_security_struct *sbsec = selinux_superblock(reference);
+ struct selinux_mnt_opts *opts;
+
+ /*
+ * Ensure that fc->security remains NULL when no options are set
+ * as expected by selinux_set_mnt_opts().
+ */
+ if (!(sbsec->flags & (FSCONTEXT_MNT|CONTEXT_MNT|DEFCONTEXT_MNT)))
+ return 0;
+
+ opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+ if (!opts)
+ return -ENOMEM;
+
+ if (sbsec->flags & FSCONTEXT_MNT)
+ opts->fscontext_sid = sbsec->sid;
+ if (sbsec->flags & CONTEXT_MNT)
+ opts->context_sid = sbsec->mntpoint_sid;
+ if (sbsec->flags & DEFCONTEXT_MNT)
+ opts->defcontext_sid = sbsec->def_sid;
+ fc->security = opts;
+ return 0;
+}
+
static int selinux_fs_context_dup(struct fs_context *fc,
struct fs_context *src_fc)
{
@@ -2847,11 +2901,11 @@ static int selinux_dentry_create_files_as(struct dentry *dentry, int mode,
static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
const struct qstr *qstr,
- const char **name,
- void **value, size_t *len)
+ struct xattr *xattrs, int *xattr_count)
{
const struct task_security_struct *tsec = selinux_cred(current_cred());
struct superblock_security_struct *sbsec;
+ struct xattr *xattr = lsm_get_xattr_slot(xattrs, xattr_count);
u32 newsid, clen;
int rc;
char *context;
@@ -2878,16 +2932,14 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
!(sbsec->flags & SBLABEL_MNT))
return -EOPNOTSUPP;
- if (name)
- *name = XATTR_SELINUX_SUFFIX;
-
- if (value && len) {
+ if (xattr) {
rc = security_sid_to_context_force(newsid,
&context, &clen);
if (rc)
return rc;
- *value = context;
- *len = clen;
+ xattr->value = context;
+ xattr->value_len = clen;
+ xattr->name = XATTR_SELINUX_SUFFIX;
}
return 0;
@@ -2917,7 +2969,7 @@ static int selinux_inode_init_security_anon(struct inode *inode,
struct inode_security_struct *context_isec =
selinux_inode(context_inode);
if (context_isec->initialized != LABEL_INITIALIZED) {
- pr_err("SELinux: context_inode is not initialized");
+ pr_err("SELinux: context_inode is not initialized\n");
return -EACCES;
}
@@ -3762,13 +3814,10 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
if (default_noexec &&
(prot & PROT_EXEC) && !(vma->vm_flags & VM_EXEC)) {
int rc = 0;
- if (vma->vm_start >= vma->vm_mm->start_brk &&
- vma->vm_end <= vma->vm_mm->brk) {
+ if (vma_is_initial_heap(vma)) {
rc = avc_has_perm(sid, sid, SECCLASS_PROCESS,
PROCESS__EXECHEAP, NULL);
- } else if (!vma->vm_file &&
- ((vma->vm_start <= vma->vm_mm->start_stack &&
- vma->vm_end >= vma->vm_mm->start_stack) ||
+ } else if (!vma->vm_file && (vma_is_initial_stack(vma) ||
vma_is_stack_for_current(vma))) {
rc = avc_has_perm(sid, sid, SECCLASS_PROCESS,
PROCESS__EXECSTACK, NULL);
@@ -4499,14 +4548,12 @@ static int sock_has_perm(struct sock *sk, u32 perms)
{
struct sk_security_struct *sksec = sk->sk_security;
struct common_audit_data ad;
- struct lsm_network_audit net = {0,};
+ struct lsm_network_audit net;
if (sksec->sid == SECINITSID_KERNEL)
return 0;
- ad.type = LSM_AUDIT_DATA_NET;
- ad.u.net = &net;
- ad.u.net->sk = sk;
+ ad_net_init_from_sk(&ad, &net, sk);
return avc_has_perm(current_sid(), sksec->sid, sksec->sclass, perms,
&ad);
@@ -4899,12 +4946,10 @@ static int selinux_socket_unix_stream_connect(struct sock *sock,
struct sk_security_struct *sksec_other = other->sk_security;
struct sk_security_struct *sksec_new = newsk->sk_security;
struct common_audit_data ad;
- struct lsm_network_audit net = {0,};
+ struct lsm_network_audit net;
int err;
- ad.type = LSM_AUDIT_DATA_NET;
- ad.u.net = &net;
- ad.u.net->sk = other;
+ ad_net_init_from_sk(&ad, &net, other);
err = avc_has_perm(sksec_sock->sid, sksec_other->sid,
sksec_other->sclass,
@@ -4931,11 +4976,9 @@ static int selinux_socket_unix_may_send(struct socket *sock,
struct sk_security_struct *ssec = sock->sk->sk_security;
struct sk_security_struct *osec = other->sk->sk_security;
struct common_audit_data ad;
- struct lsm_network_audit net = {0,};
+ struct lsm_network_audit net;
- ad.type = LSM_AUDIT_DATA_NET;
- ad.u.net = &net;
- ad.u.net->sk = other->sk;
+ ad_net_init_from_sk(&ad, &net, other->sk);
return avc_has_perm(ssec->sid, osec->sid, osec->sclass, SOCKET__SENDTO,
&ad);
@@ -4971,13 +5014,10 @@ static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb,
struct sk_security_struct *sksec = sk->sk_security;
u32 sk_sid = sksec->sid;
struct common_audit_data ad;
- struct lsm_network_audit net = {0,};
+ struct lsm_network_audit net;
char *addrp;
- ad.type = LSM_AUDIT_DATA_NET;
- ad.u.net = &net;
- ad.u.net->netif = skb->skb_iif;
- ad.u.net->family = family;
+ ad_net_init_from_iif(&ad, &net, skb->skb_iif, family);
err = selinux_parse_skb(skb, &ad, &addrp, 1, NULL);
if (err)
return err;
@@ -4999,15 +5039,13 @@ static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb,
static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
{
- int err;
+ int err, peerlbl_active, secmark_active;
struct sk_security_struct *sksec = sk->sk_security;
u16 family = sk->sk_family;
u32 sk_sid = sksec->sid;
struct common_audit_data ad;
- struct lsm_network_audit net = {0,};
+ struct lsm_network_audit net;
char *addrp;
- u8 secmark_active;
- u8 peerlbl_active;
if (family != PF_INET && family != PF_INET6)
return 0;
@@ -5028,10 +5066,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
if (!secmark_active && !peerlbl_active)
return 0;
- ad.type = LSM_AUDIT_DATA_NET;
- ad.u.net = &net;
- ad.u.net->netif = skb->skb_iif;
- ad.u.net->family = family;
+ ad_net_init_from_iif(&ad, &net, skb->skb_iif, family);
err = selinux_parse_skb(skb, &ad, &addrp, 1, NULL);
if (err)
return err;
@@ -5167,12 +5202,12 @@ static void selinux_sk_clone_security(const struct sock *sk, struct sock *newsk)
selinux_netlbl_sk_security_reset(newsksec);
}
-static void selinux_sk_getsecid(struct sock *sk, u32 *secid)
+static void selinux_sk_getsecid(const struct sock *sk, u32 *secid)
{
if (!sk)
*secid = SECINITSID_ANY_SOCKET;
else {
- struct sk_security_struct *sksec = sk->sk_security;
+ const struct sk_security_struct *sksec = sk->sk_security;
*secid = sksec->sid;
}
@@ -5201,7 +5236,7 @@ static int selinux_sctp_process_new_assoc(struct sctp_association *asoc,
u16 family = sk->sk_family;
struct sk_security_struct *sksec = sk->sk_security;
struct common_audit_data ad;
- struct lsm_network_audit net = {0,};
+ struct lsm_network_audit net;
int err;
/* handle mapped IPv4 packets arriving via IPv6 sockets */
@@ -5237,9 +5272,7 @@ static int selinux_sctp_process_new_assoc(struct sctp_association *asoc,
/* Other association peer SIDs are checked to enforce
* consistency among the peer SIDs.
*/
- ad.type = LSM_AUDIT_DATA_NET;
- ad.u.net = &net;
- ad.u.net->sk = asoc->base.sk;
+ ad_net_init_from_sk(&ad, &net, asoc->base.sk);
err = avc_has_perm(sksec->peer_sid, asoc->peer_secid,
sksec->sclass, SCTP_SOCKET__ASSOCIATION,
&ad);
@@ -5470,11 +5503,11 @@ static void selinux_inet_conn_established(struct sock *sk, struct sk_buff *skb)
static int selinux_secmark_relabel_packet(u32 sid)
{
- const struct task_security_struct *__tsec;
+ const struct task_security_struct *tsec;
u32 tsid;
- __tsec = selinux_cred(current_cred());
- tsid = __tsec->sid;
+ tsec = selinux_cred(current_cred());
+ tsid = tsec->sid;
return avc_has_perm(tsid, sid, SECCLASS_PACKET, PACKET__RELABELTO,
NULL);
@@ -5584,7 +5617,7 @@ static unsigned int selinux_ip_forward(void *priv, struct sk_buff *skb,
char *addrp;
u32 peer_sid;
struct common_audit_data ad;
- struct lsm_network_audit net = {0,};
+ struct lsm_network_audit net;
int secmark_active, peerlbl_active;
if (!selinux_policycap_netpeer())
@@ -5600,10 +5633,7 @@ static unsigned int selinux_ip_forward(void *priv, struct sk_buff *skb,
return NF_DROP;
ifindex = state->in->ifindex;
- ad.type = LSM_AUDIT_DATA_NET;
- ad.u.net = &net;
- ad.u.net->netif = ifindex;
- ad.u.net->family = family;
+ ad_net_init_from_iif(&ad, &net, ifindex, family);
if (selinux_parse_skb(skb, &ad, &addrp, 1, NULL) != 0)
return NF_DROP;
@@ -5683,7 +5713,7 @@ static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb,
struct sock *sk;
struct sk_security_struct *sksec;
struct common_audit_data ad;
- struct lsm_network_audit net = {0,};
+ struct lsm_network_audit net;
u8 proto = 0;
sk = skb_to_full_sk(skb);
@@ -5691,10 +5721,7 @@ static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb,
return NF_ACCEPT;
sksec = sk->sk_security;
- ad.type = LSM_AUDIT_DATA_NET;
- ad.u.net = &net;
- ad.u.net->netif = state->out->ifindex;
- ad.u.net->family = state->pf;
+ ad_net_init_from_iif(&ad, &net, state->out->ifindex, state->pf);
if (selinux_parse_skb(skb, &ad, NULL, 0, &proto))
return NF_DROP;
@@ -5719,7 +5746,7 @@ static unsigned int selinux_ip_postroute(void *priv,
int ifindex;
struct sock *sk;
struct common_audit_data ad;
- struct lsm_network_audit net = {0,};
+ struct lsm_network_audit net;
char *addrp;
int secmark_active, peerlbl_active;
@@ -5816,10 +5843,7 @@ static unsigned int selinux_ip_postroute(void *priv,
}
ifindex = state->out->ifindex;
- ad.type = LSM_AUDIT_DATA_NET;
- ad.u.net = &net;
- ad.u.net->netif = ifindex;
- ad.u.net->family = family;
+ ad_net_init_from_iif(&ad, &net, ifindex, family);
if (selinux_parse_skb(skb, &ad, &addrp, 0, NULL))
return NF_DROP;
@@ -5972,8 +5996,7 @@ static int selinux_msg_queue_associate(struct kern_ipc_perm *msq, int msqflg)
static int selinux_msg_queue_msgctl(struct kern_ipc_perm *msq, int cmd)
{
- int err;
- int perms;
+ u32 perms;
switch (cmd) {
case IPC_INFO:
@@ -5996,8 +6019,7 @@ static int selinux_msg_queue_msgctl(struct kern_ipc_perm *msq, int cmd)
return 0;
}
- err = ipc_has_perm(msq, perms);
- return err;
+ return ipc_has_perm(msq, perms);
}
static int selinux_msg_queue_msgsnd(struct kern_ipc_perm *msq, struct msg_msg *msg, int msqflg)
@@ -6102,8 +6124,7 @@ static int selinux_shm_associate(struct kern_ipc_perm *shp, int shmflg)
/* Note, at this point, shp is locked down */
static int selinux_shm_shmctl(struct kern_ipc_perm *shp, int cmd)
{
- int perms;
- int err;
+ u32 perms;
switch (cmd) {
case IPC_INFO:
@@ -6130,8 +6151,7 @@ static int selinux_shm_shmctl(struct kern_ipc_perm *shp, int cmd)
return 0;
}
- err = ipc_has_perm(shp, perms);
- return err;
+ return ipc_has_perm(shp, perms);
}
static int selinux_shm_shmat(struct kern_ipc_perm *shp,
@@ -6718,7 +6738,7 @@ static u32 bpf_map_fmode_to_av(fmode_t fmode)
* access the bpf object and that's why we have to add this additional check in
* selinux_file_receive and selinux_binder_transfer_files.
*/
-static int bpf_fd_pass(struct file *file, u32 sid)
+static int bpf_fd_pass(const struct file *file, u32 sid)
{
struct bpf_security_struct *bpfsec;
struct bpf_prog *prog;
@@ -6815,6 +6835,7 @@ struct lsm_blob_sizes selinux_blob_sizes __ro_after_init = {
.lbs_ipc = sizeof(struct ipc_security_struct),
.lbs_msg_msg = sizeof(struct msg_security_struct),
.lbs_superblock = sizeof(struct superblock_security_struct),
+ .lbs_xattr_count = SELINUX_INODE_INIT_XATTRS,
};
#ifdef CONFIG_PERF_EVENTS
@@ -6900,7 +6921,7 @@ static int selinux_uring_override_creds(const struct cred *new)
*/
static int selinux_uring_sqpoll(void)
{
- int sid = current_sid();
+ u32 sid = current_sid();
return avc_has_perm(sid, sid,
SECCLASS_IO_URING, IO_URING__SQPOLL, NULL);
@@ -6939,10 +6960,6 @@ static int selinux_uring_cmd(struct io_uring_cmd *ioucmd)
* hooks ("allocating" hooks).
*
* Please follow block comment delimiters in the list to keep this order.
- *
- * This ordering is needed for SELinux runtime disable to work at least somewhat
- * safely. Breaking the ordering rules above might lead to NULL pointer derefs
- * when disabling SELinux at runtime.
*/
static struct security_hook_list selinux_hooks[] __ro_after_init = {
LSM_HOOK_INIT(binder_set_context_mgr, selinux_binder_set_context_mgr),
@@ -7182,6 +7199,7 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = {
/*
* PUT "CLONING" (ACCESSING + ALLOCATING) HOOKS HERE
*/
+ LSM_HOOK_INIT(fs_context_submount, selinux_fs_context_submount),
LSM_HOOK_INIT(fs_context_dup, selinux_fs_context_dup),
LSM_HOOK_INIT(fs_context_parse_param, selinux_fs_context_parse_param),
LSM_HOOK_INIT(sb_eat_lsm_opts, selinux_sb_eat_lsm_opts),
@@ -7241,6 +7259,8 @@ static __init int selinux_init(void)
cred_init_security();
default_noexec = !(VM_DATA_DEFAULT_FLAGS & VM_EXEC);
+ if (!default_noexec)
+ pr_notice("SELinux: virtual memory is executable by default\n");
avc_init();
diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h
index 9e055f74daf6..8f0aa66ccb13 100644
--- a/security/selinux/include/avc.h
+++ b/security/selinux/include/avc.h
@@ -2,7 +2,7 @@
/*
* Access vector cache interface for object managers.
*
- * Author : Stephen Smalley, <[email protected]>
+ * Author : Stephen Smalley, <[email protected]>
*/
#ifndef _SELINUX_AVC_H_
#define _SELINUX_AVC_H_
diff --git a/security/selinux/include/avc_ss.h b/security/selinux/include/avc_ss.h
index b9668be7b443..88b139e086c4 100644
--- a/security/selinux/include/avc_ss.h
+++ b/security/selinux/include/avc_ss.h
@@ -2,7 +2,7 @@
/*
* Access vector cache interface for the security server.
*
- * Author : Stephen Smalley, <[email protected]>
+ * Author : Stephen Smalley, <[email protected]>
*/
#ifndef _SELINUX_AVC_SS_H_
#define _SELINUX_AVC_SS_H_
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
index 2953132408bf..8159fd53c3de 100644
--- a/security/selinux/include/objsec.h
+++ b/security/selinux/include/objsec.h
@@ -1,10 +1,10 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * NSA Security-Enhanced Linux (SELinux) security module
+ * Security-Enhanced Linux (SELinux) security module
*
* This file contains the SELinux security data structures for kernel objects.
*
- * Author(s): Stephen Smalley, <[email protected]>
+ * Author(s): Stephen Smalley, <[email protected]>
* Chris Vance, <[email protected]>
* Wayne Salamon, <[email protected]>
* James Morris <[email protected]>
diff --git a/security/selinux/include/policycap_names.h b/security/selinux/include/policycap_names.h
index 2a87fc3702b8..49bbe120d173 100644
--- a/security/selinux/include/policycap_names.h
+++ b/security/selinux/include/policycap_names.h
@@ -13,7 +13,7 @@ const char *const selinux_policycap_names[__POLICYDB_CAP_MAX] = {
"cgroup_seclabel",
"nnp_nosuid_transition",
"genfs_seclabel_symlinks",
- "ioctl_skip_cloexec"
+ "ioctl_skip_cloexec",
};
#endif /* _SELINUX_POLICYCAP_NAMES_H_ */
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index 3b605f39e040..a9de89af8fdc 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -2,7 +2,7 @@
/*
* Security server interface.
*
- * Author : Stephen Smalley, <[email protected]>
+ * Author : Stephen Smalley, <[email protected]>
*
*/
@@ -148,58 +148,45 @@ static inline bool checkreqprot_get(void)
static inline bool selinux_policycap_netpeer(void)
{
- struct selinux_state *state = &selinux_state;
-
- return READ_ONCE(state->policycap[POLICYDB_CAP_NETPEER]);
+ return READ_ONCE(selinux_state.policycap[POLICYDB_CAP_NETPEER]);
}
static inline bool selinux_policycap_openperm(void)
{
- struct selinux_state *state = &selinux_state;
-
- return READ_ONCE(state->policycap[POLICYDB_CAP_OPENPERM]);
+ return READ_ONCE(selinux_state.policycap[POLICYDB_CAP_OPENPERM]);
}
static inline bool selinux_policycap_extsockclass(void)
{
- struct selinux_state *state = &selinux_state;
-
- return READ_ONCE(state->policycap[POLICYDB_CAP_EXTSOCKCLASS]);
+ return READ_ONCE(selinux_state.policycap[POLICYDB_CAP_EXTSOCKCLASS]);
}
static inline bool selinux_policycap_alwaysnetwork(void)
{
- struct selinux_state *state = &selinux_state;
-
- return READ_ONCE(state->policycap[POLICYDB_CAP_ALWAYSNETWORK]);
+ return READ_ONCE(selinux_state.policycap[POLICYDB_CAP_ALWAYSNETWORK]);
}
static inline bool selinux_policycap_cgroupseclabel(void)
{
- struct selinux_state *state = &selinux_state;
-
- return READ_ONCE(state->policycap[POLICYDB_CAP_CGROUPSECLABEL]);
+ return READ_ONCE(selinux_state.policycap[POLICYDB_CAP_CGROUPSECLABEL]);
}
static inline bool selinux_policycap_nnp_nosuid_transition(void)
{
- struct selinux_state *state = &selinux_state;
-
- return READ_ONCE(state->policycap[POLICYDB_CAP_NNP_NOSUID_TRANSITION]);
+ return READ_ONCE(
+ selinux_state.policycap[POLICYDB_CAP_NNP_NOSUID_TRANSITION]);
}
static inline bool selinux_policycap_genfs_seclabel_symlinks(void)
{
- struct selinux_state *state = &selinux_state;
-
- return READ_ONCE(state->policycap[POLICYDB_CAP_GENFS_SECLABEL_SYMLINKS]);
+ return READ_ONCE(
+ selinux_state.policycap[POLICYDB_CAP_GENFS_SECLABEL_SYMLINKS]);
}
static inline bool selinux_policycap_ioctl_skip_cloexec(void)
{
- struct selinux_state *state = &selinux_state;
-
- return READ_ONCE(state->policycap[POLICYDB_CAP_IOCTL_SKIP_CLOEXEC]);
+ return READ_ONCE(
+ selinux_state.policycap[POLICYDB_CAP_IOCTL_SKIP_CLOEXEC]);
}
struct selinux_policy_convert_data;
@@ -319,9 +306,9 @@ int security_net_peersid_resolve(u32 nlbl_sid, u32 nlbl_type,
u32 *peer_sid);
int security_get_classes(struct selinux_policy *policy,
- char ***classes, int *nclasses);
+ char ***classes, u32 *nclasses);
int security_get_permissions(struct selinux_policy *policy,
- char *class, char ***perms, int *nperms);
+ const char *class, char ***perms, u32 *nperms);
int security_get_reject_unknown(void);
int security_get_allow_unknown(void);
@@ -382,8 +369,8 @@ struct selinux_kernel_status {
*/
} __packed;
-extern void selinux_status_update_setenforce(int enforcing);
-extern void selinux_status_update_policyload(int seqno);
+extern void selinux_status_update_setenforce(bool enforcing);
+extern void selinux_status_update_policyload(u32 seqno);
extern void selinux_complete_init(void);
extern struct path selinux_null;
extern void selnl_notify_setenforce(int val);
diff --git a/security/selinux/netif.c b/security/selinux/netif.c
index adbe9bea2d26..43a0d3594b72 100644
--- a/security/selinux/netif.c
+++ b/security/selinux/netif.c
@@ -67,7 +67,7 @@ static inline u32 sel_netif_hashfn(const struct net *ns, int ifindex)
static inline struct sel_netif *sel_netif_find(const struct net *ns,
int ifindex)
{
- int idx = sel_netif_hashfn(ns, ifindex);
+ u32 idx = sel_netif_hashfn(ns, ifindex);
struct sel_netif *netif;
list_for_each_entry_rcu(netif, &sel_netif_hash[idx], list)
@@ -89,7 +89,7 @@ static inline struct sel_netif *sel_netif_find(const struct net *ns,
*/
static int sel_netif_insert(struct sel_netif *netif)
{
- int idx;
+ u32 idx;
if (sel_netif_total >= SEL_NETIF_HASH_MAX)
return -ENOSPC;
diff --git a/security/selinux/netlabel.c b/security/selinux/netlabel.c
index 528f5186e912..8f182800e412 100644
--- a/security/selinux/netlabel.c
+++ b/security/selinux/netlabel.c
@@ -198,6 +198,7 @@ int selinux_netlbl_skbuff_getsid(struct sk_buff *skb,
struct netlbl_lsm_secattr secattr;
if (!netlbl_enabled()) {
+ *type = NETLBL_NLTYPE_NONE;
*sid = SECSID_NULL;
return 0;
}
diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c
index 2ee7b4ed43ef..8ff670cf1ee5 100644
--- a/security/selinux/nlmsgtab.c
+++ b/security/selinux/nlmsgtab.c
@@ -153,7 +153,8 @@ static const struct nlmsg_perm nlmsg_audit_perms[] = {
static int nlmsg_perm(u16 nlmsg_type, u32 *perm, const struct nlmsg_perm *tab, size_t tabsize)
{
- int i, err = -EINVAL;
+ unsigned int i;
+ int err = -EINVAL;
for (i = 0; i < tabsize/sizeof(struct nlmsg_perm); i++)
if (nlmsg_type == tab[i].nlmsg_type) {
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
index bad1f6b685fd..6c596ae7fef9 100644
--- a/security/selinux/selinuxfs.c
+++ b/security/selinux/selinuxfs.c
@@ -97,7 +97,7 @@ static int selinux_fs_info_create(struct super_block *sb)
static void selinux_fs_info_free(struct super_block *sb)
{
struct selinux_fs_info *fsi = sb->s_fs_info;
- int i;
+ unsigned int i;
if (fsi) {
for (i = 0; i < fsi->bool_num; i++)
@@ -138,7 +138,8 @@ static ssize_t sel_write_enforce(struct file *file, const char __user *buf,
{
char *page = NULL;
ssize_t length;
- int old_value, new_value;
+ int scan_value;
+ bool old_value, new_value;
if (count >= PAGE_SIZE)
return -ENOMEM;
@@ -152,10 +153,10 @@ static ssize_t sel_write_enforce(struct file *file, const char __user *buf,
return PTR_ERR(page);
length = -EINVAL;
- if (sscanf(page, "%d", &new_value) != 1)
+ if (sscanf(page, "%d", &scan_value) != 1)
goto out;
- new_value = !!new_value;
+ new_value = !!scan_value;
old_value = enforcing_enabled();
if (new_value != old_value) {
@@ -1074,8 +1075,8 @@ static ssize_t sel_write_user(struct file *file, char *buf, size_t size)
u32 sid, *sids = NULL;
ssize_t length;
char *newcon;
- int i, rc;
- u32 len, nsids;
+ int rc;
+ u32 i, len, nsids;
length = avc_has_perm(current_sid(), SECINITSID_SECURITY,
SECCLASS_SECURITY, SECURITY__COMPUTE_USER,
@@ -1191,13 +1192,13 @@ out:
return length;
}
-static struct inode *sel_make_inode(struct super_block *sb, int mode)
+static struct inode *sel_make_inode(struct super_block *sb, umode_t mode)
{
struct inode *ret = new_inode(sb);
if (ret) {
ret->i_mode = mode;
- ret->i_atime = ret->i_mtime = ret->i_ctime = current_time(ret);
+ simple_inode_init_ts(ret);
}
return ret;
}
@@ -1612,7 +1613,7 @@ static int sel_make_avc_files(struct dentry *dir)
{
struct super_block *sb = dir->d_sb;
struct selinux_fs_info *fsi = sb->s_fs_info;
- int i;
+ unsigned int i;
static const struct tree_descr files[] = {
{ "cache_threshold",
&sel_avc_cache_threshold_ops, S_IRUGO|S_IWUSR },
@@ -1648,7 +1649,7 @@ static int sel_make_ss_files(struct dentry *dir)
{
struct super_block *sb = dir->d_sb;
struct selinux_fs_info *fsi = sb->s_fs_info;
- int i;
+ unsigned int i;
static const struct tree_descr files[] = {
{ "sidtab_hash_stats", &sel_sidtab_hash_stats_ops, S_IRUGO },
};
@@ -1699,7 +1700,7 @@ static const struct file_operations sel_initcon_ops = {
static int sel_make_initcon_files(struct dentry *dir)
{
- int i;
+ unsigned int i;
for (i = 1; i <= SECINITSID_NUM; i++) {
struct inode *inode;
@@ -1797,7 +1798,8 @@ static int sel_make_perm_files(struct selinux_policy *newpolicy,
char *objclass, int classvalue,
struct dentry *dir)
{
- int i, rc, nperms;
+ u32 i, nperms;
+ int rc;
char **perms;
rc = security_get_permissions(newpolicy, objclass, &perms, &nperms);
@@ -1867,8 +1869,8 @@ static int sel_make_classes(struct selinux_policy *newpolicy,
struct dentry *class_dir,
unsigned long *last_class_ino)
{
-
- int rc, nclasses, i;
+ u32 i, nclasses;
+ int rc;
char **classes;
rc = security_get_classes(newpolicy, &classes, &nclasses);
diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c
index 6766edc0fe68..8751a602ead2 100644
--- a/security/selinux/ss/avtab.c
+++ b/security/selinux/ss/avtab.c
@@ -1,7 +1,7 @@
/*
* Implementation of the access vector table type.
*
- * Author : Stephen Smalley, <[email protected]>
+ * Author : Stephen Smalley, <[email protected]>
*/
/* Updated: Frank Mayer <[email protected]> and Karl MacMillan <[email protected]>
@@ -17,6 +17,7 @@
* Tuned number of hash slots for avtab to reduce memory usage
*/
+#include <linux/bitops.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/errno.h>
@@ -29,7 +30,7 @@ static struct kmem_cache *avtab_xperms_cachep __ro_after_init;
/* Based on MurmurHash3, written by Austin Appleby and placed in the
* public domain.
*/
-static inline int avtab_hash(const struct avtab_key *keyp, u32 mask)
+static inline u32 avtab_hash(const struct avtab_key *keyp, u32 mask)
{
static const u32 c1 = 0xcc9e2d51;
static const u32 c2 = 0x1b873593;
@@ -66,8 +67,7 @@ static inline int avtab_hash(const struct avtab_key *keyp, u32 mask)
}
static struct avtab_node*
-avtab_insert_node(struct avtab *h, int hvalue,
- struct avtab_node *prev,
+avtab_insert_node(struct avtab *h, struct avtab_node **dst,
const struct avtab_key *key, const struct avtab_datum *datum)
{
struct avtab_node *newnode;
@@ -89,15 +89,8 @@ avtab_insert_node(struct avtab *h, int hvalue,
newnode->datum.u.data = datum->u.data;
}
- if (prev) {
- newnode->next = prev->next;
- prev->next = newnode;
- } else {
- struct avtab_node **n = &h->htable[hvalue];
-
- newnode->next = *n;
- *n = newnode;
- }
+ newnode->next = *dst;
+ *dst = newnode;
h->nel++;
return newnode;
@@ -106,11 +99,11 @@ avtab_insert_node(struct avtab *h, int hvalue,
static int avtab_insert(struct avtab *h, const struct avtab_key *key,
const struct avtab_datum *datum)
{
- int hvalue;
+ u32 hvalue;
struct avtab_node *prev, *cur, *newnode;
u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD);
- if (!h || !h->nslot)
+ if (!h || !h->nslot || h->nel == U32_MAX)
return -EINVAL;
hvalue = avtab_hash(key, h->mask);
@@ -137,7 +130,8 @@ static int avtab_insert(struct avtab *h, const struct avtab_key *key,
break;
}
- newnode = avtab_insert_node(h, hvalue, prev, key, datum);
+ newnode = avtab_insert_node(h, prev ? &prev->next : &h->htable[hvalue],
+ key, datum);
if (!newnode)
return -ENOMEM;
@@ -152,11 +146,11 @@ struct avtab_node *avtab_insert_nonunique(struct avtab *h,
const struct avtab_key *key,
const struct avtab_datum *datum)
{
- int hvalue;
+ u32 hvalue;
struct avtab_node *prev, *cur;
u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD);
- if (!h || !h->nslot)
+ if (!h || !h->nslot || h->nel == U32_MAX)
return NULL;
hvalue = avtab_hash(key, h->mask);
for (prev = NULL, cur = h->htable[hvalue];
@@ -177,39 +171,8 @@ struct avtab_node *avtab_insert_nonunique(struct avtab *h,
key->target_class < cur->key.target_class)
break;
}
- return avtab_insert_node(h, hvalue, prev, key, datum);
-}
-
-struct avtab_datum *avtab_search(struct avtab *h, const struct avtab_key *key)
-{
- int hvalue;
- struct avtab_node *cur;
- u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD);
-
- if (!h || !h->nslot)
- return NULL;
-
- hvalue = avtab_hash(key, h->mask);
- for (cur = h->htable[hvalue]; cur;
- cur = cur->next) {
- if (key->source_type == cur->key.source_type &&
- key->target_type == cur->key.target_type &&
- key->target_class == cur->key.target_class &&
- (specified & cur->key.specified))
- return &cur->datum;
-
- if (key->source_type < cur->key.source_type)
- break;
- if (key->source_type == cur->key.source_type &&
- key->target_type < cur->key.target_type)
- break;
- if (key->source_type == cur->key.source_type &&
- key->target_type == cur->key.target_type &&
- key->target_class < cur->key.target_class)
- break;
- }
-
- return NULL;
+ return avtab_insert_node(h, prev ? &prev->next : &h->htable[hvalue],
+ key, datum);
}
/* This search function returns a node pointer, and can be used in
@@ -218,7 +181,7 @@ struct avtab_datum *avtab_search(struct avtab *h, const struct avtab_key *key)
struct avtab_node *avtab_search_node(struct avtab *h,
const struct avtab_key *key)
{
- int hvalue;
+ u32 hvalue;
struct avtab_node *cur;
u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD);
@@ -248,7 +211,7 @@ struct avtab_node *avtab_search_node(struct avtab *h,
}
struct avtab_node*
-avtab_search_node_next(struct avtab_node *node, int specified)
+avtab_search_node_next(struct avtab_node *node, u16 specified)
{
struct avtab_node *cur;
@@ -278,7 +241,7 @@ avtab_search_node_next(struct avtab_node *node, int specified)
void avtab_destroy(struct avtab *h)
{
- int i;
+ u32 i;
struct avtab_node *cur, *temp;
if (!h)
@@ -330,13 +293,7 @@ int avtab_alloc(struct avtab *h, u32 nrules)
u32 nslot = 0;
if (nrules != 0) {
- u32 shift = 1;
- u32 work = nrules >> 3;
- while (work) {
- work >>= 1;
- shift++;
- }
- nslot = 1 << shift;
+ nslot = nrules > 3 ? rounddown_pow_of_two(nrules / 2) : 2;
if (nslot > MAX_AVTAB_HASH_BUCKETS)
nslot = MAX_AVTAB_HASH_BUCKETS;
@@ -354,9 +311,10 @@ int avtab_alloc_dup(struct avtab *new, const struct avtab *orig)
return avtab_alloc_common(new, orig->nslot);
}
+#ifdef CONFIG_SECURITY_SELINUX_DEBUG
void avtab_hash_eval(struct avtab *h, const char *tag)
{
- int i, chain_len, slots_used, max_chain_len;
+ u32 i, chain_len, slots_used, max_chain_len;
unsigned long long chain2_len_sum;
struct avtab_node *cur;
@@ -375,15 +333,16 @@ void avtab_hash_eval(struct avtab *h, const char *tag)
if (chain_len > max_chain_len)
max_chain_len = chain_len;
- chain2_len_sum += chain_len * chain_len;
+ chain2_len_sum += (unsigned long long)chain_len * chain_len;
}
}
pr_debug("SELinux: %s: %d entries and %d/%d buckets used, "
- "longest chain length %d sum of chain length^2 %llu\n",
+ "longest chain length %d, sum of chain length^2 %llu\n",
tag, h->nel, slots_used, h->nslot, max_chain_len,
chain2_len_sum);
}
+#endif /* CONFIG_SECURITY_SELINUX_DEBUG */
static const uint16_t spec_order[] = {
AVTAB_ALLOWED,
@@ -404,13 +363,13 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol,
{
__le16 buf16[4];
u16 enabled;
- u32 items, items2, val, vers = pol->policyvers;
+ u32 items, items2, val, i;
struct avtab_key key;
struct avtab_datum datum;
struct avtab_extended_perms xperms;
__le32 buf32[ARRAY_SIZE(xperms.perms.p)];
- int i, rc;
- unsigned set;
+ int rc;
+ unsigned int set, vers = pol->policyvers;
memset(&key, 0, sizeof(struct avtab_key));
memset(&datum, 0, sizeof(struct avtab_datum));
@@ -507,11 +466,7 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol,
return -EINVAL;
}
- set = 0;
- for (i = 0; i < ARRAY_SIZE(spec_order); i++) {
- if (key.specified & spec_order[i])
- set++;
- }
+ set = hweight16(key.specified & (AVTAB_XPERMS | AVTAB_TYPE | AVTAB_AV));
if (!set || set > 1) {
pr_err("SELinux: avtab: more than one specifier\n");
return -EINVAL;
@@ -646,7 +601,7 @@ int avtab_write_item(struct policydb *p, const struct avtab_node *cur, void *fp)
int avtab_write(struct policydb *p, struct avtab *a, void *fp)
{
- unsigned int i;
+ u32 i;
int rc = 0;
struct avtab_node *cur;
__le32 buf[1];
diff --git a/security/selinux/ss/avtab.h b/security/selinux/ss/avtab.h
index d6742fd9c560..3c3904bf02b0 100644
--- a/security/selinux/ss/avtab.h
+++ b/security/selinux/ss/avtab.h
@@ -6,7 +6,7 @@
* table is used to represent the type enforcement
* tables.
*
- * Author : Stephen Smalley, <[email protected]>
+ * Author : Stephen Smalley, <[email protected]>
*/
/* Updated: Frank Mayer <[email protected]> and Karl MacMillan <[email protected]>
@@ -90,9 +90,15 @@ struct avtab {
void avtab_init(struct avtab *h);
int avtab_alloc(struct avtab *, u32);
int avtab_alloc_dup(struct avtab *new, const struct avtab *orig);
-struct avtab_datum *avtab_search(struct avtab *h, const struct avtab_key *k);
void avtab_destroy(struct avtab *h);
+
+#ifdef CONFIG_SECURITY_SELINUX_DEBUG
void avtab_hash_eval(struct avtab *h, const char *tag);
+#else
+static inline void avtab_hash_eval(struct avtab *h, const char *tag)
+{
+}
+#endif
struct policydb;
int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol,
@@ -111,7 +117,7 @@ struct avtab_node *avtab_insert_nonunique(struct avtab *h,
struct avtab_node *avtab_search_node(struct avtab *h,
const struct avtab_key *key);
-struct avtab_node *avtab_search_node_next(struct avtab_node *node, int specified);
+struct avtab_node *avtab_search_node_next(struct avtab_node *node, u16 specified);
#define MAX_AVTAB_HASH_BITS 16
#define MAX_AVTAB_HASH_BUCKETS (1 << MAX_AVTAB_HASH_BITS)
diff --git a/security/selinux/ss/conditional.c b/security/selinux/ss/conditional.c
index b156c181c3c1..81ff676f209a 100644
--- a/security/selinux/ss/conditional.c
+++ b/security/selinux/ss/conditional.c
@@ -272,7 +272,7 @@ static int cond_insertf(struct avtab *a, const struct avtab_key *k,
* cond_te_avtab.
*/
if (k->specified & AVTAB_TYPE) {
- if (avtab_search(&p->te_avtab, k)) {
+ if (avtab_search_node(&p->te_avtab, k)) {
pr_err("SELinux: type rule already exists outside of a conditional.\n");
return -EINVAL;
}
@@ -304,7 +304,7 @@ static int cond_insertf(struct avtab *a, const struct avtab_key *k,
}
}
} else {
- if (avtab_search(&p->te_cond_avtab, k)) {
+ if (avtab_search_node(&p->te_cond_avtab, k)) {
pr_err("SELinux: conflicting type rules when adding type rule for true.\n");
return -EINVAL;
}
diff --git a/security/selinux/ss/constraint.h b/security/selinux/ss/constraint.h
index 4e563be9ef5f..f76eb3128ad5 100644
--- a/security/selinux/ss/constraint.h
+++ b/security/selinux/ss/constraint.h
@@ -11,7 +11,7 @@
* process from labeling an object with a different user
* identity.
*
- * Author : Stephen Smalley, <[email protected]>
+ * Author : Stephen Smalley, <[email protected]>
*/
#ifndef _SS_CONSTRAINT_H_
#define _SS_CONSTRAINT_H_
diff --git a/security/selinux/ss/context.h b/security/selinux/ss/context.h
index aed704b8c642..1f59468c0759 100644
--- a/security/selinux/ss/context.h
+++ b/security/selinux/ss/context.h
@@ -11,7 +11,7 @@
* security server and can be changed without affecting
* clients of the security server.
*
- * Author : Stephen Smalley, <[email protected]>
+ * Author : Stephen Smalley, <[email protected]>
*/
#ifndef _SS_CONTEXT_H_
#define _SS_CONTEXT_H_
diff --git a/security/selinux/ss/ebitmap.c b/security/selinux/ss/ebitmap.c
index d31b87be9a1e..77875ad355f7 100644
--- a/security/selinux/ss/ebitmap.c
+++ b/security/selinux/ss/ebitmap.c
@@ -2,7 +2,7 @@
/*
* Implementation of the extensible bitmap type.
*
- * Author : Stephen Smalley, <[email protected]>
+ * Author : Stephen Smalley, <[email protected]>
*/
/*
* Updated: Hewlett-Packard <[email protected]>
diff --git a/security/selinux/ss/ebitmap.h b/security/selinux/ss/ebitmap.h
index e5b57dc3fc53..e3c807cfad90 100644
--- a/security/selinux/ss/ebitmap.h
+++ b/security/selinux/ss/ebitmap.h
@@ -10,7 +10,7 @@
* an explicitly specified starting bit position within
* the total bitmap.
*
- * Author : Stephen Smalley, <[email protected]>
+ * Author : Stephen Smalley, <[email protected]>
*/
#ifndef _SS_EBITMAP_H_
#define _SS_EBITMAP_H_
diff --git a/security/selinux/ss/hashtab.c b/security/selinux/ss/hashtab.c
index 3fb8f9026e9b..c05d8346a94a 100644
--- a/security/selinux/ss/hashtab.c
+++ b/security/selinux/ss/hashtab.c
@@ -2,7 +2,7 @@
/*
* Implementation of the hash table type.
*
- * Author : Stephen Smalley, <[email protected]>
+ * Author : Stephen Smalley, <[email protected]>
*/
#include <linux/kernel.h>
#include <linux/slab.h>
@@ -103,14 +103,16 @@ int hashtab_map(struct hashtab *h,
return 0;
}
-
+#ifdef CONFIG_SECURITY_SELINUX_DEBUG
void hashtab_stat(struct hashtab *h, struct hashtab_info *info)
{
u32 i, chain_len, slots_used, max_chain_len;
+ u64 chain2_len_sum;
struct hashtab_node *cur;
slots_used = 0;
max_chain_len = 0;
+ chain2_len_sum = 0;
for (i = 0; i < h->size; i++) {
cur = h->htable[i];
if (cur) {
@@ -123,12 +125,16 @@ void hashtab_stat(struct hashtab *h, struct hashtab_info *info)
if (chain_len > max_chain_len)
max_chain_len = chain_len;
+
+ chain2_len_sum += (u64)chain_len * chain_len;
}
}
info->slots_used = slots_used;
info->max_chain_len = max_chain_len;
+ info->chain2_len_sum = chain2_len_sum;
}
+#endif /* CONFIG_SECURITY_SELINUX_DEBUG */
int hashtab_duplicate(struct hashtab *new, struct hashtab *orig,
int (*copy)(struct hashtab_node *new,
@@ -137,7 +143,8 @@ int hashtab_duplicate(struct hashtab *new, struct hashtab *orig,
void *args)
{
struct hashtab_node *cur, *tmp, *tail;
- int i, rc;
+ u32 i;
+ int rc;
memset(new, 0, sizeof(*new));
diff --git a/security/selinux/ss/hashtab.h b/security/selinux/ss/hashtab.h
index 043a773bf0b7..09b0a3744937 100644
--- a/security/selinux/ss/hashtab.h
+++ b/security/selinux/ss/hashtab.h
@@ -6,7 +6,7 @@
* functions for hash computation and key comparison are
* provided by the creator of the table.
*
- * Author : Stephen Smalley, <[email protected]>
+ * Author : Stephen Smalley, <[email protected]>
*/
#ifndef _SS_HASHTAB_H_
#define _SS_HASHTAB_H_
@@ -38,6 +38,7 @@ struct hashtab {
struct hashtab_info {
u32 slots_used;
u32 max_chain_len;
+ u64 chain2_len_sum;
};
/*
@@ -142,7 +143,13 @@ int hashtab_duplicate(struct hashtab *new, struct hashtab *orig,
int (*destroy)(void *k, void *d, void *args),
void *args);
+#ifdef CONFIG_SECURITY_SELINUX_DEBUG
/* Fill info with some hash table statistics */
void hashtab_stat(struct hashtab *h, struct hashtab_info *info);
+#else
+static inline void hashtab_stat(struct hashtab *h, struct hashtab_info *info)
+{
+}
+#endif
#endif /* _SS_HASHTAB_H */
diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c
index 99571b19d4a9..cd38f5913b63 100644
--- a/security/selinux/ss/mls.c
+++ b/security/selinux/ss/mls.c
@@ -2,7 +2,7 @@
/*
* Implementation of the multi-level security (MLS) policy.
*
- * Author : Stephen Smalley, <[email protected]>
+ * Author : Stephen Smalley, <[email protected]>
*/
/*
* Updated: Trusted Computer Solutions, Inc. <[email protected]>
@@ -45,7 +45,7 @@ int mls_compute_context_len(struct policydb *p, struct context *context)
len = 1; /* for the beginning ":" */
for (l = 0; l < 2; l++) {
- int index_sens = context->range.level[l].sens;
+ u32 index_sens = context->range.level[l].sens;
len += strlen(sym_name(p, SYM_LEVELS, index_sens - 1));
/* categories */
@@ -240,7 +240,8 @@ int mls_context_to_sid(struct policydb *pol,
char *sensitivity, *cur_cat, *next_cat, *rngptr;
struct level_datum *levdatum;
struct cat_datum *catdatum, *rngdatum;
- int l, rc, i;
+ u32 i;
+ int l, rc;
char *rangep[2];
if (!pol->mls_enabled) {
@@ -451,7 +452,8 @@ int mls_convert_context(struct policydb *oldp,
struct level_datum *levdatum;
struct cat_datum *catdatum;
struct ebitmap_node *node;
- int l, i;
+ u32 i;
+ int l;
if (!oldp->mls_enabled || !newp->mls_enabled)
return 0;
@@ -495,7 +497,7 @@ int mls_compute_sid(struct policydb *p,
struct range_trans rtr;
struct mls_range *r;
struct class_datum *cladatum;
- int default_range = 0;
+ char default_range = 0;
if (!p->mls_enabled)
return 0;
diff --git a/security/selinux/ss/mls.h b/security/selinux/ss/mls.h
index 15cacde0ff61..107681dd1824 100644
--- a/security/selinux/ss/mls.h
+++ b/security/selinux/ss/mls.h
@@ -2,7 +2,7 @@
/*
* Multi-level security (MLS) policy operations.
*
- * Author : Stephen Smalley, <[email protected]>
+ * Author : Stephen Smalley, <[email protected]>
*/
/*
* Updated: Trusted Computer Solutions, Inc. <[email protected]>
diff --git a/security/selinux/ss/mls_types.h b/security/selinux/ss/mls_types.h
index 7d48d5e52233..f492cf148891 100644
--- a/security/selinux/ss/mls_types.h
+++ b/security/selinux/ss/mls_types.h
@@ -2,7 +2,7 @@
/*
* Type definitions for the multi-level security (MLS) policy.
*
- * Author : Stephen Smalley, <[email protected]>
+ * Author : Stephen Smalley, <[email protected]>
*/
/*
* Updated: Trusted Computer Solutions, Inc. <[email protected]>
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index 31b08b34c722..595a435ea9c8 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -2,7 +2,7 @@
/*
* Implementation of the policy database.
*
- * Author : Stephen Smalley, <[email protected]>
+ * Author : Stephen Smalley, <[email protected]>
*/
/*
@@ -41,7 +41,7 @@
#include "mls.h"
#include "services.h"
-#ifdef DEBUG_HASHES
+#ifdef CONFIG_SECURITY_SELINUX_DEBUG
static const char *const symtab_name[SYM_NUM] = {
"common prefixes",
"classes",
@@ -55,9 +55,9 @@ static const char *const symtab_name[SYM_NUM] = {
#endif
struct policydb_compat_info {
- int version;
- int sym_num;
- int ocon_num;
+ unsigned int version;
+ unsigned int sym_num;
+ unsigned int ocon_num;
};
/* These need to be updated if SYM_NUM or OCON_NUM changes */
@@ -159,9 +159,9 @@ static const struct policydb_compat_info policydb_compat[] = {
},
};
-static const struct policydb_compat_info *policydb_lookup_compat(int version)
+static const struct policydb_compat_info *policydb_lookup_compat(unsigned int version)
{
- int i;
+ unsigned int i;
for (i = 0; i < ARRAY_SIZE(policydb_compat); i++) {
if (policydb_compat[i].version == version)
@@ -359,7 +359,7 @@ static int role_tr_destroy(void *key, void *datum, void *p)
return 0;
}
-static void ocontext_destroy(struct ocontext *c, int i)
+static void ocontext_destroy(struct ocontext *c, unsigned int i)
{
if (!c)
return;
@@ -491,7 +491,7 @@ static u32 role_trans_hash(const void *k)
{
const struct role_trans_key *key = k;
- return key->role + (key->type << 3) + (key->tclass << 5);
+ return jhash_3words(key->role, key->type, (u32)key->tclass << 16 | key->tclass, 0);
}
static int role_trans_cmp(const void *k1, const void *k2)
@@ -678,15 +678,15 @@ static int (*const index_f[SYM_NUM]) (void *key, void *datum, void *datap) = {
cat_index,
};
-#ifdef DEBUG_HASHES
+#ifdef CONFIG_SECURITY_SELINUX_DEBUG
static void hash_eval(struct hashtab *h, const char *hash_name)
{
struct hashtab_info info;
hashtab_stat(h, &info);
- pr_debug("SELinux: %s: %d entries and %d/%d buckets used, longest chain length %d\n",
+ pr_debug("SELinux: %s: %d entries and %d/%d buckets used, longest chain length %d, sum of chain length^2 %llu\n",
hash_name, h->nel, info.slots_used, h->size,
- info.max_chain_len);
+ info.max_chain_len, info.chain2_len_sum);
}
static void symtab_hash_eval(struct symtab *s)
@@ -701,7 +701,10 @@ static void symtab_hash_eval(struct symtab *s)
static inline void hash_eval(struct hashtab *h, const char *hash_name)
{
}
-#endif
+static inline void symtab_hash_eval(struct symtab *s)
+{
+}
+#endif /* CONFIG_SECURITY_SELINUX_DEBUG */
/*
* Define the other val_to_name and val_to_struct arrays
@@ -725,10 +728,8 @@ static int policydb_index(struct policydb *p)
pr_debug("SELinux: %d classes, %d rules\n",
p->p_classes.nprim, p->te_avtab.nel);
-#ifdef DEBUG_HASHES
avtab_hash_eval(&p->te_avtab, "rules");
symtab_hash_eval(p->symtab);
-#endif
p->class_val_to_struct = kcalloc(p->p_classes.nprim,
sizeof(*p->class_val_to_struct),
@@ -781,7 +782,7 @@ void policydb_destroy(struct policydb *p)
{
struct ocontext *c, *ctmp;
struct genfs *g, *gtmp;
- int i;
+ u32 i;
struct role_allow *ra, *lra = NULL;
for (i = 0; i < SYM_NUM; i++) {
@@ -1127,8 +1128,8 @@ static int common_read(struct policydb *p, struct symtab *s, void *fp)
char *key = NULL;
struct common_datum *comdatum;
__le32 buf[4];
- u32 len, nel;
- int i, rc;
+ u32 i, len, nel;
+ int rc;
comdatum = kzalloc(sizeof(*comdatum), GFP_KERNEL);
if (!comdatum)
@@ -1193,13 +1194,13 @@ static int type_set_read(struct type_set *t, void *fp)
static int read_cons_helper(struct policydb *p,
struct constraint_node **nodep,
- int ncons, int allowxtarget, void *fp)
+ u32 ncons, int allowxtarget, void *fp)
{
struct constraint_node *c, *lc;
struct constraint_expr *e, *le;
__le32 buf[3];
- u32 nexpr;
- int rc, i, j, depth;
+ u32 i, j, nexpr;
+ int rc, depth;
lc = NULL;
for (i = 0; i < ncons; i++) {
@@ -1291,8 +1292,8 @@ static int class_read(struct policydb *p, struct symtab *s, void *fp)
char *key = NULL;
struct class_datum *cladatum;
__le32 buf[6];
- u32 len, len2, ncons, nel;
- int i, rc;
+ u32 i, len, len2, ncons, nel;
+ int rc;
cladatum = kzalloc(sizeof(*cladatum), GFP_KERNEL);
if (!cladatum)
@@ -1385,7 +1386,8 @@ static int role_read(struct policydb *p, struct symtab *s, void *fp)
{
char *key = NULL;
struct role_datum *role;
- int rc, to_read = 2;
+ int rc;
+ unsigned int to_read = 2;
__le32 buf[3];
u32 len;
@@ -1441,7 +1443,8 @@ static int type_read(struct policydb *p, struct symtab *s, void *fp)
{
char *key = NULL;
struct type_datum *typdatum;
- int rc, to_read = 3;
+ int rc;
+ unsigned int to_read = 3;
__le32 buf[4];
u32 len;
@@ -1515,7 +1518,8 @@ static int user_read(struct policydb *p, struct symtab *s, void *fp)
{
char *key = NULL;
struct user_datum *usrdatum;
- int rc, to_read = 2;
+ int rc;
+ unsigned int to_read = 2;
__le32 buf[3];
u32 len;
@@ -1569,7 +1573,7 @@ static int sens_read(struct policydb *p, struct symtab *s, void *fp)
__le32 buf[2];
u32 len;
- levdatum = kzalloc(sizeof(*levdatum), GFP_ATOMIC);
+ levdatum = kzalloc(sizeof(*levdatum), GFP_KERNEL);
if (!levdatum)
return -ENOMEM;
@@ -1580,12 +1584,12 @@ static int sens_read(struct policydb *p, struct symtab *s, void *fp)
len = le32_to_cpu(buf[0]);
levdatum->isalias = le32_to_cpu(buf[1]);
- rc = str_read(&key, GFP_ATOMIC, fp, len);
+ rc = str_read(&key, GFP_KERNEL, fp, len);
if (rc)
goto bad;
rc = -ENOMEM;
- levdatum->level = kmalloc(sizeof(*levdatum->level), GFP_ATOMIC);
+ levdatum->level = kmalloc(sizeof(*levdatum->level), GFP_KERNEL);
if (!levdatum->level)
goto bad;
@@ -1610,7 +1614,7 @@ static int cat_read(struct policydb *p, struct symtab *s, void *fp)
__le32 buf[3];
u32 len;
- catdatum = kzalloc(sizeof(*catdatum), GFP_ATOMIC);
+ catdatum = kzalloc(sizeof(*catdatum), GFP_KERNEL);
if (!catdatum)
return -ENOMEM;
@@ -1622,7 +1626,7 @@ static int cat_read(struct policydb *p, struct symtab *s, void *fp)
catdatum->value = le32_to_cpu(buf[1]);
catdatum->isalias = le32_to_cpu(buf[2]);
- rc = str_read(&key, GFP_ATOMIC, fp, len);
+ rc = str_read(&key, GFP_KERNEL, fp, len);
if (rc)
goto bad;
@@ -1656,11 +1660,11 @@ static int user_bounds_sanity_check(void *key, void *datum, void *datap)
upper = user = datum;
while (upper->bounds) {
struct ebitmap_node *node;
- unsigned long bit;
+ u32 bit;
if (++depth == POLICYDB_BOUNDS_MAXDEPTH) {
pr_err("SELinux: user %s: "
- "too deep or looped boundary",
+ "too deep or looped boundary\n",
(char *) key);
return -EINVAL;
}
@@ -1692,7 +1696,7 @@ static int role_bounds_sanity_check(void *key, void *datum, void *datap)
upper = role = datum;
while (upper->bounds) {
struct ebitmap_node *node;
- unsigned long bit;
+ u32 bit;
if (++depth == POLICYDB_BOUNDS_MAXDEPTH) {
pr_err("SELinux: role %s: "
@@ -1739,7 +1743,7 @@ static int type_bounds_sanity_check(void *key, void *datum, void *datap)
if (upper->attribute) {
pr_err("SELinux: type %s: "
- "bounded by attribute %s",
+ "bounded by attribute %s\n",
(char *) key,
sym_name(p, SYM_TYPES, upper->value - 1));
return -EINVAL;
@@ -1807,9 +1811,9 @@ static int range_read(struct policydb *p, void *fp)
{
struct range_trans *rt = NULL;
struct mls_range *r = NULL;
- int i, rc;
+ int rc;
__le32 buf[2];
- u32 nel;
+ u32 i, nel;
if (p->policyvers < POLICYDB_VERSION_MLS)
return 0;
@@ -2005,6 +2009,7 @@ static int filename_trans_read_helper(struct policydb *p, void *fp)
if (!datum)
goto out;
+ datum->next = NULL;
*dst = datum;
/* ebitmap_read() will at least init the bitmap */
@@ -2017,7 +2022,6 @@ static int filename_trans_read_helper(struct policydb *p, void *fp)
goto out;
datum->otype = le32_to_cpu(buf[0]);
- datum->next = NULL;
dst = &datum->next;
}
@@ -2055,9 +2059,9 @@ out:
static int filename_trans_read(struct policydb *p, void *fp)
{
- u32 nel;
+ u32 nel, i;
__le32 buf[1];
- int rc, i;
+ int rc;
if (p->policyvers < POLICYDB_VERSION_FILENAME_TRANS)
return 0;
@@ -2096,8 +2100,8 @@ static int filename_trans_read(struct policydb *p, void *fp)
static int genfs_read(struct policydb *p, void *fp)
{
- int i, j, rc;
- u32 nel, nel2, len, len2;
+ int rc;
+ u32 i, j, nel, nel2, len, len2;
__le32 buf[1];
struct ocontext *l, *c;
struct ocontext *newc = NULL;
@@ -2210,8 +2214,9 @@ out:
static int ocontext_read(struct policydb *p, const struct policydb_compat_info *info,
void *fp)
{
- int i, j, rc;
- u32 nel, len;
+ int rc;
+ unsigned int i;
+ u32 j, nel, len;
__be64 prefixbuf[1];
__le32 buf[3];
struct ocontext *l, *c;
@@ -2402,9 +2407,9 @@ int policydb_read(struct policydb *p, void *fp)
struct role_allow *ra, *lra;
struct role_trans_key *rtk = NULL;
struct role_trans_datum *rtd = NULL;
- int i, j, rc;
+ int rc;
__le32 buf[4];
- u32 len, nprim, nel, perm;
+ u32 i, j, len, nprim, nel, perm;
char *policydb_str;
const struct policydb_compat_info *info;
@@ -3255,7 +3260,8 @@ static int (*const write_f[SYM_NUM]) (void *key, void *datum, void *datap) = {
static int ocontext_write(struct policydb *p, const struct policydb_compat_info *info,
void *fp)
{
- unsigned int i, j, rc;
+ unsigned int i, j;
+ int rc;
size_t nel, len;
__be64 prefixbuf[1];
__le32 buf[3];
@@ -3604,10 +3610,10 @@ static int filename_trans_write(struct policydb *p, void *fp)
*/
int policydb_write(struct policydb *p, void *fp)
{
- unsigned int i, num_syms;
+ unsigned int num_syms;
int rc;
__le32 buf[4];
- u32 config;
+ u32 config, i;
size_t len;
const struct policydb_compat_info *info;
@@ -3648,7 +3654,7 @@ int policydb_write(struct policydb *p, void *fp)
info = policydb_lookup_compat(p->policyvers);
if (!info) {
pr_err("SELinux: compatibility lookup failed for policy "
- "version %d", p->policyvers);
+ "version %d\n", p->policyvers);
return -EINVAL;
}
diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h
index 74b63ed1173f..b97cda489753 100644
--- a/security/selinux/ss/policydb.h
+++ b/security/selinux/ss/policydb.h
@@ -3,7 +3,7 @@
* A policy database (policydb) specifies the
* configuration data for the security policy.
*
- * Author : Stephen Smalley, <[email protected]>
+ * Author : Stephen Smalley, <[email protected]>
*/
/*
@@ -366,9 +366,12 @@ static inline int next_entry(void *buf, struct policy_file *fp, size_t bytes)
return 0;
}
-static inline int put_entry(const void *buf, size_t bytes, int num, struct policy_file *fp)
+static inline int put_entry(const void *buf, size_t bytes, size_t num, struct policy_file *fp)
{
- size_t len = bytes * num;
+ size_t len;
+
+ if (unlikely(check_mul_overflow(bytes, num, &len)))
+ return -EINVAL;
if (len > fp->len)
return -EINVAL;
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index 78946b71c1c1..1eeffc66ea7d 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -2,7 +2,7 @@
/*
* Implementation of the security services.
*
- * Authors : Stephen Smalley, <[email protected]>
+ * Authors : Stephen Smalley, <[email protected]>
* James Morris <[email protected]>
*
* Updated: Trusted Computer Solutions, Inc. <[email protected]>
@@ -97,7 +97,6 @@ static int selinux_set_mapping(struct policydb *pol,
struct selinux_map *out_map)
{
u16 i, j;
- unsigned k;
bool print_unknown_handle = false;
/* Find number of classes in the input mapping */
@@ -117,6 +116,7 @@ static int selinux_set_mapping(struct policydb *pol,
while (map[j].name) {
const struct security_class_mapping *p_in = map + (j++);
struct selinux_mapping *p_out = out_map->mapping + j;
+ u16 k;
/* An empty class string skips ahead */
if (!strcmp(p_in->name, "")) {
@@ -207,22 +207,22 @@ static void map_decision(struct selinux_map *map,
for (i = 0, result = 0; i < n; i++) {
if (avd->allowed & mapping->perms[i])
- result |= 1<<i;
+ result |= (u32)1<<i;
if (allow_unknown && !mapping->perms[i])
- result |= 1<<i;
+ result |= (u32)1<<i;
}
avd->allowed = result;
for (i = 0, result = 0; i < n; i++)
if (avd->auditallow & mapping->perms[i])
- result |= 1<<i;
+ result |= (u32)1<<i;
avd->auditallow = result;
for (i = 0, result = 0; i < n; i++) {
if (avd->auditdeny & mapping->perms[i])
- result |= 1<<i;
+ result |= (u32)1<<i;
if (!allow_unknown && !mapping->perms[i])
- result |= 1<<i;
+ result |= (u32)1<<i;
}
/*
* In case the kernel has a bug and requests a permission
@@ -230,7 +230,7 @@ static void map_decision(struct selinux_map *map,
* should audit that denial
*/
for (; i < (sizeof(u32)*8); i++)
- result |= 1<<i;
+ result |= (u32)1<<i;
avd->auditdeny = result;
}
}
@@ -856,7 +856,7 @@ int security_bounded_transition(u32 old_sid, u32 new_sid)
struct sidtab *sidtab;
struct sidtab_entry *old_entry, *new_entry;
struct type_datum *type;
- int index;
+ u32 index;
int rc;
if (!selinux_initialized())
@@ -1511,7 +1511,7 @@ static int security_context_to_sid_core(const char *scontext, u32 scontext_len,
return -ENOMEM;
if (!selinux_initialized()) {
- int i;
+ u32 i;
for (i = 1; i < SECINITSID_NUM; i++) {
const char *s = initial_sid_to_string[i];
@@ -1694,7 +1694,7 @@ static void filename_compute_type(struct policydb *policydb,
static int security_compute_sid(u32 ssid,
u32 tsid,
u16 orig_tclass,
- u32 specified,
+ u16 specified,
const char *objname,
u32 *out_sid,
bool kern)
@@ -1706,8 +1706,7 @@ static int security_compute_sid(u32 ssid,
struct context *scontext, *tcontext, newcontext;
struct sidtab_entry *sentry, *tentry;
struct avtab_key avkey;
- struct avtab_datum *avdatum;
- struct avtab_node *node;
+ struct avtab_node *avnode, *node;
u16 tclass;
int rc = 0;
bool sock;
@@ -1815,22 +1814,22 @@ retry:
avkey.target_type = tcontext->type;
avkey.target_class = tclass;
avkey.specified = specified;
- avdatum = avtab_search(&policydb->te_avtab, &avkey);
+ avnode = avtab_search_node(&policydb->te_avtab, &avkey);
/* If no permanent rule, also check for enabled conditional rules */
- if (!avdatum) {
+ if (!avnode) {
node = avtab_search_node(&policydb->te_cond_avtab, &avkey);
for (; node; node = avtab_search_node_next(node, specified)) {
if (node->key.specified & AVTAB_ENABLED) {
- avdatum = &node->datum;
+ avnode = node;
break;
}
}
}
- if (avdatum) {
+ if (avnode) {
/* Use the type from the type transition/member/change rule. */
- newcontext.type = avdatum->u.data;
+ newcontext.type = avnode->datum.u.data;
}
/* if we have a objname this is a file trans check so check those rules */
@@ -2822,7 +2821,6 @@ static inline int __security_genfs_sid(struct selinux_policy *policy,
{
struct policydb *policydb = &policy->policydb;
struct sidtab *sidtab = policy->sidtab;
- int len;
u16 sclass;
struct genfs *genfs;
struct ocontext *c;
@@ -2844,7 +2842,7 @@ static inline int __security_genfs_sid(struct selinux_policy *policy,
return -ENOENT;
for (c = genfs->head; c; c = c->next) {
- len = strlen(c->u.name);
+ size_t len = strlen(c->u.name);
if ((!c->v.sclass || sclass == c->v.sclass) &&
(strncmp(c->u.name, path, len) == 0))
break;
@@ -3332,7 +3330,7 @@ static int get_classes_callback(void *k, void *d, void *args)
{
struct class_datum *datum = d;
char *name = k, **classes = args;
- int value = datum->value - 1;
+ u32 value = datum->value - 1;
classes[value] = kstrdup(name, GFP_ATOMIC);
if (!classes[value])
@@ -3342,7 +3340,7 @@ static int get_classes_callback(void *k, void *d, void *args)
}
int security_get_classes(struct selinux_policy *policy,
- char ***classes, int *nclasses)
+ char ***classes, u32 *nclasses)
{
struct policydb *policydb;
int rc;
@@ -3358,7 +3356,8 @@ int security_get_classes(struct selinux_policy *policy,
rc = hashtab_map(&policydb->p_classes.table, get_classes_callback,
*classes);
if (rc) {
- int i;
+ u32 i;
+
for (i = 0; i < *nclasses; i++)
kfree((*classes)[i]);
kfree(*classes);
@@ -3372,7 +3371,7 @@ static int get_permissions_callback(void *k, void *d, void *args)
{
struct perm_datum *datum = d;
char *name = k, **perms = args;
- int value = datum->value - 1;
+ u32 value = datum->value - 1;
perms[value] = kstrdup(name, GFP_ATOMIC);
if (!perms[value])
@@ -3382,10 +3381,11 @@ static int get_permissions_callback(void *k, void *d, void *args)
}
int security_get_permissions(struct selinux_policy *policy,
- char *class, char ***perms, int *nperms)
+ const char *class, char ***perms, u32 *nperms)
{
struct policydb *policydb;
- int rc, i;
+ u32 i;
+ int rc;
struct class_datum *match;
policydb = &policy->policydb;
@@ -3600,7 +3600,7 @@ err:
/* Check to see if the rule contains any selinux fields */
int selinux_audit_rule_known(struct audit_krule *rule)
{
- int i;
+ u32 i;
for (i = 0; i < rule->field_count; i++) {
struct audit_field *f = &rule->fields[i];
diff --git a/security/selinux/ss/services.h b/security/selinux/ss/services.h
index 8a9b85f44b66..d24b0a3d198e 100644
--- a/security/selinux/ss/services.h
+++ b/security/selinux/ss/services.h
@@ -2,7 +2,7 @@
/*
* Implementation of the security services.
*
- * Author : Stephen Smalley, <[email protected]>
+ * Author : Stephen Smalley, <[email protected]>
*/
#ifndef _SS_SERVICES_H_
#define _SS_SERVICES_H_
@@ -12,7 +12,7 @@
/* Mapping for a single class */
struct selinux_mapping {
u16 value; /* policy value for class */
- unsigned int num_perms; /* number of permissions in class */
+ u16 num_perms; /* number of permissions in class */
u32 perms[sizeof(u32) * 8]; /* policy values for permissions */
};
diff --git a/security/selinux/ss/sidtab.c b/security/selinux/ss/sidtab.c
index 38d25173aebd..732fd8e22a12 100644
--- a/security/selinux/ss/sidtab.c
+++ b/security/selinux/ss/sidtab.c
@@ -2,7 +2,7 @@
/*
* Implementation of the SID table type.
*
- * Original author: Stephen Smalley, <[email protected]>
+ * Original author: Stephen Smalley, <[email protected]>
* Author: Ondrej Mosnacek, <[email protected]>
*
* Copyright (C) 2018 Red Hat, Inc.
@@ -25,7 +25,7 @@ struct sidtab_str_cache {
struct list_head lru_member;
struct sidtab_entry *parent;
u32 len;
- char str[];
+ char str[] __counted_by(len);
};
#define index_to_sid(index) ((index) + SECINITSID_NUM + 1)
diff --git a/security/selinux/ss/sidtab.h b/security/selinux/ss/sidtab.h
index 72810a080e77..22258201cd14 100644
--- a/security/selinux/ss/sidtab.h
+++ b/security/selinux/ss/sidtab.h
@@ -3,7 +3,7 @@
* A security identifier table (sidtab) is a lookup table
* of security context structures indexed by SID value.
*
- * Original author: Stephen Smalley, <[email protected]>
+ * Original author: Stephen Smalley, <[email protected]>
* Author: Ondrej Mosnacek, <[email protected]>
*
* Copyright (C) 2018 Red Hat, Inc.
diff --git a/security/selinux/ss/symtab.c b/security/selinux/ss/symtab.c
index c42a6648a07d..43d7f0319ccd 100644
--- a/security/selinux/ss/symtab.c
+++ b/security/selinux/ss/symtab.c
@@ -2,7 +2,7 @@
/*
* Implementation of the symbol table type.
*
- * Author : Stephen Smalley, <[email protected]>
+ * Author : Stephen Smalley, <[email protected]>
*/
#include <linux/kernel.h>
#include <linux/string.h>
@@ -37,7 +37,7 @@ static const struct hashtab_key_params symtab_key_params = {
.cmp = symcmp,
};
-int symtab_init(struct symtab *s, unsigned int size)
+int symtab_init(struct symtab *s, u32 size)
{
s->nprim = 0;
return hashtab_init(&s->table, size);
diff --git a/security/selinux/ss/symtab.h b/security/selinux/ss/symtab.h
index f2614138d0cd..0a3b5de79a0f 100644
--- a/security/selinux/ss/symtab.h
+++ b/security/selinux/ss/symtab.h
@@ -5,7 +5,7 @@
* is arbitrary. The symbol table type is implemented
* using the hash table type (hashtab).
*
- * Author : Stephen Smalley, <[email protected]>
+ * Author : Stephen Smalley, <[email protected]>
*/
#ifndef _SS_SYMTAB_H_
#define _SS_SYMTAB_H_
@@ -17,7 +17,7 @@ struct symtab {
u32 nprim; /* number of primary names in table */
};
-int symtab_init(struct symtab *s, unsigned int size);
+int symtab_init(struct symtab *s, u32 size);
int symtab_insert(struct symtab *s, char *name, void *datum);
void *symtab_search(struct symtab *s, const char *name);
diff --git a/security/selinux/status.c b/security/selinux/status.c
index 19ef929a075c..dffca22ce6f7 100644
--- a/security/selinux/status.c
+++ b/security/selinux/status.c
@@ -76,7 +76,7 @@ struct page *selinux_kernel_status_page(void)
*
* It updates status of the current enforcing/permissive mode.
*/
-void selinux_status_update_setenforce(int enforcing)
+void selinux_status_update_setenforce(bool enforcing)
{
struct selinux_kernel_status *status;
@@ -87,7 +87,7 @@ void selinux_status_update_setenforce(int enforcing)
status->sequence++;
smp_wmb();
- status->enforcing = enforcing;
+ status->enforcing = enforcing ? 1 : 0;
smp_wmb();
status->sequence++;
@@ -101,7 +101,7 @@ void selinux_status_update_setenforce(int enforcing)
* It updates status of the times of policy reloaded, and current
* setting of deny_unknown.
*/
-void selinux_status_update_policyload(int seqno)
+void selinux_status_update_policyload(u32 seqno)
{
struct selinux_kernel_status *status;
diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c
index 1fca42c4d0ae..95fcd2d3433e 100644
--- a/security/selinux/xfrm.c
+++ b/security/selinux/xfrm.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * NSA Security-Enhanced Linux (SELinux) security module
+ * Security-Enhanced Linux (SELinux) security module
*
* This file contains the SELinux XFRM hook function implementations.
*
diff --git a/security/smack/smack.h b/security/smack/smack.h
index aa15ff56ed6e..041688e5a77a 100644
--- a/security/smack/smack.h
+++ b/security/smack/smack.h
@@ -128,7 +128,7 @@ struct task_smack {
#define SMK_INODE_INSTANT 0x01 /* inode is instantiated */
#define SMK_INODE_TRANSMUTE 0x02 /* directory is transmuting */
-#define SMK_INODE_CHANGED 0x04 /* smack was transmuted */
+#define SMK_INODE_CHANGED 0x04 /* smack was transmuted (unused) */
#define SMK_INODE_IMPURE 0x08 /* involved in an impure transaction */
/*
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 6e270cf3fd30..65130a791f57 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -52,6 +52,15 @@
#define SMK_RECEIVING 1
#define SMK_SENDING 2
+/*
+ * Smack uses multiple xattrs.
+ * SMACK64 - for access control,
+ * SMACK64TRANSMUTE - label initialization,
+ * Not saved on files - SMACK64IPIN and SMACK64IPOUT,
+ * Must be set explicitly - SMACK64EXEC and SMACK64MMAP
+ */
+#define SMACK_INODE_INIT_XATTRS 2
+
#ifdef SMACK_IPV6_PORT_LABELING
static DEFINE_MUTEX(smack_ipv6_lock);
static LIST_HEAD(smk_ipv6_port_list);
@@ -615,6 +624,56 @@ out_opt_err:
}
/**
+ * smack_fs_context_submount - Initialise security data for a filesystem context
+ * @fc: The filesystem context.
+ * @reference: reference superblock
+ *
+ * Returns 0 on success or -ENOMEM on error.
+ */
+static int smack_fs_context_submount(struct fs_context *fc,
+ struct super_block *reference)
+{
+ struct superblock_smack *sbsp;
+ struct smack_mnt_opts *ctx;
+ struct inode_smack *isp;
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+ fc->security = ctx;
+
+ sbsp = smack_superblock(reference);
+ isp = smack_inode(reference->s_root->d_inode);
+
+ if (sbsp->smk_default) {
+ ctx->fsdefault = kstrdup(sbsp->smk_default->smk_known, GFP_KERNEL);
+ if (!ctx->fsdefault)
+ return -ENOMEM;
+ }
+
+ if (sbsp->smk_floor) {
+ ctx->fsfloor = kstrdup(sbsp->smk_floor->smk_known, GFP_KERNEL);
+ if (!ctx->fsfloor)
+ return -ENOMEM;
+ }
+
+ if (sbsp->smk_hat) {
+ ctx->fshat = kstrdup(sbsp->smk_hat->smk_known, GFP_KERNEL);
+ if (!ctx->fshat)
+ return -ENOMEM;
+ }
+
+ if (isp->smk_flags & SMK_INODE_TRANSMUTE) {
+ if (sbsp->smk_root) {
+ ctx->fstransmute = kstrdup(sbsp->smk_root->smk_known, GFP_KERNEL);
+ if (!ctx->fstransmute)
+ return -ENOMEM;
+ }
+ }
+ return 0;
+}
+
+/**
* smack_fs_context_dup - Duplicate the security data on fs_context duplication
* @fc: The new filesystem context.
* @src_fc: The source filesystem context being duplicated.
@@ -923,27 +982,23 @@ static int smack_inode_alloc_security(struct inode *inode)
* @inode: the newly created inode
* @dir: containing directory object
* @qstr: unused
- * @name: where to put the attribute name
- * @value: where to put the attribute value
- * @len: where to put the length of the attribute
+ * @xattrs: where to put the attributes
+ * @xattr_count: current number of LSM-provided xattrs (updated)
*
* Returns 0 if it all works out, -ENOMEM if there's no memory
*/
static int smack_inode_init_security(struct inode *inode, struct inode *dir,
- const struct qstr *qstr, const char **name,
- void **value, size_t *len)
+ const struct qstr *qstr,
+ 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 smack_known *dsp = smk_of_inode(dir);
+ struct xattr *xattr = lsm_get_xattr_slot(xattrs, xattr_count);
int may;
- if (name)
- *name = XATTR_SMACK_SUFFIX;
-
- if (value && len) {
+ if (xattr) {
/*
* If equal, transmuting already occurred in
* smack_dentry_create_files_as(). No need to check again.
@@ -964,6 +1019,8 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir,
if ((tsp->smk_task == tsp->smk_transmuted) ||
(may > 0 && ((may & MAY_TRANSMUTE) != 0) &&
smk_inode_transmutable(dir))) {
+ struct xattr *xattr_transmute;
+
/*
* The caller of smack_dentry_create_files_as()
* should have overridden the current cred, so the
@@ -972,14 +1029,26 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir,
*/
if (tsp->smk_task != tsp->smk_transmuted)
isp = dsp;
- issp->smk_flags |= SMK_INODE_CHANGED;
+ 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;
+
+ xattr_transmute->value_len = TRANS_TRUE_SIZE;
+ xattr_transmute->name = XATTR_SMACK_TRANSMUTE;
+ }
}
- *value = kstrdup(isp->smk_known, GFP_NOFS);
- if (*value == NULL)
+ xattr->value = kstrdup(isp->smk_known, GFP_NOFS);
+ if (!xattr->value)
return -ENOMEM;
- *len = strlen(isp->smk_known);
+ xattr->value_len = strlen(isp->smk_known);
+ xattr->name = XATTR_SMACK_SUFFIX;
}
return 0;
@@ -3518,20 +3587,12 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
* If there is a transmute attribute on the
* directory mark the inode.
*/
- if (isp->smk_flags & SMK_INODE_CHANGED) {
- isp->smk_flags &= ~SMK_INODE_CHANGED;
- rc = __vfs_setxattr(&nop_mnt_idmap, dp, inode,
- XATTR_NAME_SMACKTRANSMUTE,
- TRANS_TRUE, TRANS_TRUE_SIZE,
- 0);
- } else {
- rc = __vfs_getxattr(dp, inode,
- XATTR_NAME_SMACKTRANSMUTE, trattr,
- TRANS_TRUE_SIZE);
- if (rc >= 0 && strncmp(trattr, TRANS_TRUE,
- TRANS_TRUE_SIZE) != 0)
- rc = -EINVAL;
- }
+ rc = __vfs_getxattr(dp, inode,
+ XATTR_NAME_SMACKTRANSMUTE, trattr,
+ TRANS_TRUE_SIZE);
+ if (rc >= 0 && strncmp(trattr, TRANS_TRUE,
+ TRANS_TRUE_SIZE) != 0)
+ rc = -EINVAL;
if (rc >= 0)
transflag = SMK_INODE_TRANSMUTE;
}
@@ -4869,6 +4930,7 @@ struct lsm_blob_sizes smack_blob_sizes __ro_after_init = {
.lbs_ipc = sizeof(struct smack_known *),
.lbs_msg_msg = sizeof(struct smack_known *),
.lbs_superblock = sizeof(struct superblock_smack),
+ .lbs_xattr_count = SMACK_INODE_INIT_XATTRS,
};
static struct security_hook_list smack_hooks[] __ro_after_init = {
@@ -4876,6 +4938,7 @@ static struct security_hook_list smack_hooks[] __ro_after_init = {
LSM_HOOK_INIT(ptrace_traceme, smack_ptrace_traceme),
LSM_HOOK_INIT(syslog, smack_syslog),
+ LSM_HOOK_INIT(fs_context_submount, smack_fs_context_submount),
LSM_HOOK_INIT(fs_context_dup, smack_fs_context_dup),
LSM_HOOK_INIT(fs_context_parse_param, smack_fs_context_parse_param),
diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c
index 5590eaad241b..e22aad7604e8 100644
--- a/security/smack/smackfs.c
+++ b/security/smack/smackfs.c
@@ -114,7 +114,7 @@ struct smack_known *smack_syslog_label;
* SMACK_PTRACE_DEFAULT regular smack ptrace rules (/proc based)
* SMACK_PTRACE_EXACT labels must match, but can be overriden with
* CAP_SYS_PTRACE
- * SMACK_PTRACE_DRACONIAN lables must match, CAP_SYS_PTRACE has no effect
+ * SMACK_PTRACE_DRACONIAN labels must match, CAP_SYS_PTRACE has no effect
*/
int smack_ptrace_rule = SMACK_PTRACE_DEFAULT;
@@ -896,7 +896,7 @@ static ssize_t smk_set_cipso(struct file *file, const char __user *buf,
}
ret = sscanf(rule, "%d", &catlen);
- if (ret != 1 || catlen > SMACK_CIPSO_MAXCATNUM)
+ if (ret != 1 || catlen < 0 || catlen > SMACK_CIPSO_MAXCATNUM)
goto out;
if (format == SMK_FIXED24_FMT &&
diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c
index 969d4aa6fd55..57ee70ae50f2 100644
--- a/security/tomoyo/common.c
+++ b/security/tomoyo/common.c
@@ -184,6 +184,7 @@ static bool tomoyo_manage_by_non_root;
*
* Returns nothing.
*/
+__printf(3, 4)
static void tomoyo_addprintf(char *buffer, int len, const char *fmt, ...)
{
va_list args;
diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h
index a539b2cbb5c4..0e8e2e959aef 100644
--- a/security/tomoyo/common.h
+++ b/security/tomoyo/common.h
@@ -954,7 +954,7 @@ bool tomoyo_str_starts(char **src, const char *find);
char *tomoyo_encode(const char *str);
char *tomoyo_encode2(const char *str, int str_len);
char *tomoyo_init_log(struct tomoyo_request_info *r, int len, const char *fmt,
- va_list args);
+ va_list args) __printf(3, 0);
char *tomoyo_read_token(struct tomoyo_acl_param *param);
char *tomoyo_realpath_from_path(const struct path *path);
char *tomoyo_realpath_nofollow(const char *pathname);
@@ -1037,8 +1037,6 @@ struct tomoyo_policy_namespace *tomoyo_assign_namespace
(const char *domainname);
struct tomoyo_profile *tomoyo_profile(const struct tomoyo_policy_namespace *ns,
const u8 profile);
-unsigned int tomoyo_check_flags(const struct tomoyo_domain_info *domain,
- const u8 index);
u8 tomoyo_parse_ulong(unsigned long *result, char **str);
void *tomoyo_commit_ok(void *data, const unsigned int size);
void __init tomoyo_load_builtin_policy(void);
@@ -1067,7 +1065,7 @@ void tomoyo_warn_oom(const char *function);
void tomoyo_write_log(struct tomoyo_request_info *r, const char *fmt, ...)
__printf(2, 3);
void tomoyo_write_log2(struct tomoyo_request_info *r, int len, const char *fmt,
- va_list args);
+ va_list args) __printf(3, 0);
/********** External variable definitions. **********/
diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c
index ac20c0bdff9d..90b53500a236 100644
--- a/security/tomoyo/domain.c
+++ b/security/tomoyo/domain.c
@@ -784,13 +784,12 @@ retry:
if (!strcmp(domainname, "parent")) {
char *cp;
- strncpy(ee->tmp, old_domain->domainname->name,
- TOMOYO_EXEC_TMPSIZE - 1);
+ strscpy(ee->tmp, old_domain->domainname->name, TOMOYO_EXEC_TMPSIZE);
cp = strrchr(ee->tmp, ' ');
if (cp)
*cp = '\0';
} else if (*domainname == '<')
- strncpy(ee->tmp, domainname, TOMOYO_EXEC_TMPSIZE - 1);
+ strscpy(ee->tmp, domainname, TOMOYO_EXEC_TMPSIZE);
else
snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s",
old_domain->domainname->name, domainname);
diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c
index 25006fddc964..255f1b470295 100644
--- a/security/tomoyo/tomoyo.c
+++ b/security/tomoyo/tomoyo.c
@@ -52,7 +52,7 @@ static int tomoyo_cred_prepare(struct cred *new, const struct cred *old,
*
* @bprm: Pointer to "struct linux_binprm".
*/
-static void tomoyo_bprm_committed_creds(struct linux_binprm *bprm)
+static void tomoyo_bprm_committed_creds(const struct linux_binprm *bprm)
{
/* Clear old_domain_info saved by execve() request. */
struct tomoyo_task *s = tomoyo_task(current);