* Re: [PATCH 00/18] VFS: Filesystem information [ver #21]
From: Miklos Szeredi @ 2020-08-04 19:18 UTC (permalink / raw)
To: James Bottomley
Cc: David Howells, Al Viro, Theodore Ts'o, Andreas Dilger,
Eric Biggers, Jeff Layton, linux-ext4, Carlos Maiolino,
Darrick J. Wong, Linux API, Linus Torvalds, Ian Kent,
Miklos Szeredi, Christian Brauner, Jann Horn, Karel Zak,
Jeff Layton, linux-fsdevel, LSM, linux-kernel
In-Reply-To: <1596555579.10158.23.camel@HansenPartnership.com>
On Tue, Aug 4, 2020 at 5:40 PM James Bottomley
<James.Bottomley@hansenpartnership.com> wrote:
>
> On Mon, 2020-08-03 at 14:36 +0100, David Howells wrote:
> > Here's a set of patches that adds a system call, fsinfo(), that
> > allows information about the VFS, mount topology, superblock and
> > files to be retrieved.
> >
> > The patchset is based on top of the notifications patchset and allows
> > event counters implemented in the latter to be retrieved to allow
> > overruns to be efficiently managed.
>
> Could I repeat the question I asked about six months back that never
> got answered:
>
> https://lore.kernel.org/linux-api/1582316494.3376.45.camel@HansenPartnership.com/
>
> It sort of petered out into a long winding thread about why not use
> sysfs instead, which really doesn't look like a good idea to me.
>
> I'll repeat the information for those who want to quote it easily on
> reply without having to use a web interface:
>
> ---
> Could I make a suggestion about how this should be done in a way that
> doesn't actually require the fsinfo syscall at all: it could just be
> done with fsconfig. The idea is based on something I've wanted to do
> for configfd but couldn't because otherwise it wouldn't substitute for
> fsconfig, but Christian made me think it was actually essential to the
> ability of the seccomp and other verifier tools in the critique of
> configfd and I belive the same critique applies here.
>
> Instead of making fsconfig functionally configure ... as in you pass
> the attribute name, type and parameters down into the fs specific
> handler and the handler does a string match and then verifies the
> parameters and then acts on them, make it table configured, so what
> each fstype does is register a table of attributes which can be got and
> optionally set (with each attribute having a get and optional set
> function). We'd have multiple tables per fstype, so the generic VFS
> can register a table of attributes it understands for every fstype
> (things like name, uuid and the like) and then each fs type would
> register a table of fs specific attributes following the same pattern.
> The system would examine the fs specific table before the generic one,
> allowing overrides. fsconfig would have the ability to both get and
> set attributes, permitting retrieval as well as setting (which is how I
> get rid of the fsinfo syscall), we'd have a global parameter, which
> would retrieve the entire table by name and type so the whole thing is
> introspectable because the upper layer knows a-priori all the
> attributes which can be set for a given fs type and what type they are
> (so we can make more of the parsing generic). Any attribute which
> doesn't have a set routine would be read only and all attributes would
> have to have a get routine meaning everything is queryable.
fsconfig(2) takes an fd referring to an fs_context, that in turn
refers to a super_block.
So using fsconfig() for retrieving super_block attributes would be
fine (modulo value being const, and lack of buffer size).
But what about mount attributes?
I don't buy the argument that an API needs to be designed around the
requirements of seccomp and the like. It should be the other way
round. In that, I think your configfd idea was fine, and would answer
the above question.
Thanks,
Miklos
^ permalink raw reply
* [PATCH v2 1/2] LSM: Signal to SafeSetID when setting group IDs
From: Micah Morton @ 2020-08-04 21:13 UTC (permalink / raw)
To: linux-security-module
Cc: keescook, casey, paul, stephen.smalley.work, serge, jmorris,
Thomas Cedeno, Micah Morton
In-Reply-To: <alpine.LRH.2.21.2007280444060.18670@namei.org>
From: Thomas Cedeno <thomascedeno@google.com>
For SafeSetID to properly gate set*gid() calls, it needs to know whether
ns_capable() is being called from within a sys_set*gid() function or is
being called from elsewhere in the kernel. This allows SafeSetID to deny
CAP_SETGID to restricted groups when they are attempting to use the
capability for code paths other than updating GIDs (e.g. setting up
userns GID mappings). This is the identical approach to what is
currently done for CAP_SETUID.
NOTE: We also add signaling to SafeSetID from the setgroups() syscall,
as we have future plans to restrict a process' ability to set
supplementary groups in addition to what is added in this series for
restricting setting of the primary group.
Signed-off-by: Thomas Cedeno <thomascedeno@google.com>
Signed-off-by: Micah Morton <mortonm@chromium.org>
---
Change since last patch: signal from setgroups() syscall as well as
set*gid() syscalls.
kernel/capability.c | 2 +-
kernel/groups.c | 2 +-
kernel/sys.c | 10 +++++-----
3 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/kernel/capability.c b/kernel/capability.c
index 1444f3954d75..6cfbfba65b9b 100644
--- a/kernel/capability.c
+++ b/kernel/capability.c
@@ -418,7 +418,7 @@ EXPORT_SYMBOL(ns_capable_noaudit);
/**
* ns_capable_setid - Determine if the current task has a superior capability
* in effect, while signalling that this check is being done from within a
- * setid syscall.
+ * setid or setgroups syscall.
* @ns: The usernamespace we want the capability in
* @cap: The capability to be tested for
*
diff --git a/kernel/groups.c b/kernel/groups.c
index 6ee6691f6839..fe7e6385530e 100644
--- a/kernel/groups.c
+++ b/kernel/groups.c
@@ -178,7 +178,7 @@ bool may_setgroups(void)
{
struct user_namespace *user_ns = current_user_ns();
- return ns_capable(user_ns, CAP_SETGID) &&
+ return ns_capable_setid(user_ns, CAP_SETGID) &&
userns_may_setgroups(user_ns);
}
diff --git a/kernel/sys.c b/kernel/sys.c
index 00a96746e28a..55e0c86772ab 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -373,7 +373,7 @@ long __sys_setregid(gid_t rgid, gid_t egid)
if (rgid != (gid_t) -1) {
if (gid_eq(old->gid, krgid) ||
gid_eq(old->egid, krgid) ||
- ns_capable(old->user_ns, CAP_SETGID))
+ ns_capable_setid(old->user_ns, CAP_SETGID))
new->gid = krgid;
else
goto error;
@@ -382,7 +382,7 @@ long __sys_setregid(gid_t rgid, gid_t egid)
if (gid_eq(old->gid, kegid) ||
gid_eq(old->egid, kegid) ||
gid_eq(old->sgid, kegid) ||
- ns_capable(old->user_ns, CAP_SETGID))
+ ns_capable_setid(old->user_ns, CAP_SETGID))
new->egid = kegid;
else
goto error;
@@ -432,7 +432,7 @@ long __sys_setgid(gid_t gid)
old = current_cred();
retval = -EPERM;
- if (ns_capable(old->user_ns, CAP_SETGID))
+ if (ns_capable_setid(old->user_ns, CAP_SETGID))
new->gid = new->egid = new->sgid = new->fsgid = kgid;
else if (gid_eq(kgid, old->gid) || gid_eq(kgid, old->sgid))
new->egid = new->fsgid = kgid;
@@ -744,7 +744,7 @@ long __sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid)
old = current_cred();
retval = -EPERM;
- if (!ns_capable(old->user_ns, CAP_SETGID)) {
+ if (!ns_capable_setid(old->user_ns, CAP_SETGID)) {
if (rgid != (gid_t) -1 && !gid_eq(krgid, old->gid) &&
!gid_eq(krgid, old->egid) && !gid_eq(krgid, old->sgid))
goto error;
@@ -871,7 +871,7 @@ long __sys_setfsgid(gid_t gid)
if (gid_eq(kgid, old->gid) || gid_eq(kgid, old->egid) ||
gid_eq(kgid, old->sgid) || gid_eq(kgid, old->fsgid) ||
- ns_capable(old->user_ns, CAP_SETGID)) {
+ ns_capable_setid(old->user_ns, CAP_SETGID)) {
if (!gid_eq(kgid, old->fsgid)) {
new->fsgid = kgid;
if (security_task_fix_setgid(new,old,LSM_SETID_FS) == 0)
--
2.28.0.163.g6104cc2f0b6-goog
^ permalink raw reply related
* Re: [PATCH v5 3/3] Wire UFFD up to SELinux
From: Eric Biggers @ 2020-08-04 21:16 UTC (permalink / raw)
To: Daniel Colascione
Cc: timmurray, selinux, linux-security-module, linux-fsdevel,
linux-kernel, kvm, viro, paul, nnk, sds, lokeshgidra, jmorris
In-Reply-To: <20200401213903.182112-4-dancol@google.com>
On Wed, Apr 01, 2020 at 02:39:03PM -0700, Daniel Colascione wrote:
> This change gives userfaultfd file descriptors a real security
> context, allowing policy to act on them.
>
> Signed-off-by: Daniel Colascione <dancol@google.com>
> ---
> fs/userfaultfd.c | 30 ++++++++++++++++++++++++++----
> 1 file changed, 26 insertions(+), 4 deletions(-)
>
> diff --git a/fs/userfaultfd.c b/fs/userfaultfd.c
> index 37df7c9eedb1..78ff5d898733 100644
> --- a/fs/userfaultfd.c
> +++ b/fs/userfaultfd.c
> @@ -76,6 +76,8 @@ struct userfaultfd_ctx {
> bool mmap_changing;
> /* mm with one ore more vmas attached to this userfaultfd_ctx */
> struct mm_struct *mm;
> + /* The inode that owns this context --- not a strong reference. */
> + const struct inode *owner;
> };
Adding this field seems wrong. There's no reference held to it, so it can only
be used if the caller holds a reference to the inode anyway. The only user of
this field is via userfafultfd_read(), so why not just use the inode of the
struct file passed to userfaultfd_read()?
> SYSCALL_DEFINE1(userfaultfd, int, flags)
> {
> + struct file *file;
> struct userfaultfd_ctx *ctx;
> int fd;
>
> @@ -1974,8 +1979,25 @@ SYSCALL_DEFINE1(userfaultfd, int, flags)
> /* prevent the mm struct to be freed */
> mmgrab(ctx->mm);
>
> - fd = anon_inode_getfd("[userfaultfd]", &userfaultfd_fops, ctx,
> - O_RDWR | (flags & UFFD_SHARED_FCNTL_FLAGS));
> + file = anon_inode_getfile_secure(
> + "[userfaultfd]", &userfaultfd_fops, ctx,
> + O_RDWR | (flags & UFFD_SHARED_FCNTL_FLAGS),
> + NULL);
> + if (IS_ERR(file)) {
> + fd = PTR_ERR(file);
> + goto out;
> + }
> +
> + fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC);
> + if (fd < 0) {
> + fput(file);
> + goto out;
> + }
> +
> + ctx->owner = file_inode(file);
> + fd_install(fd, file);
> +
> +out:
> if (fd < 0) {
> mmdrop(ctx->mm);
> kmem_cache_free(userfaultfd_ctx_cachep, ctx);
What's the point of anon_inode_getfile_secure()? anon_inode_getfd_secure()
would work here too.
- Eric
^ permalink raw reply
* [PATCH v3 2/2] LSM: SafeSetID: Add GID security policy handling
From: Micah Morton @ 2020-08-04 21:18 UTC (permalink / raw)
To: linux-security-module
Cc: keescook, casey, paul, stephen.smalley.work, serge, jmorris,
Thomas Cedeno, Micah Morton
In-Reply-To: <20200727152036.153212-1-mortonm@chromium.org>
From: Thomas Cedeno <thomascedeno@google.com>
The SafeSetID LSM has functionality for restricting setuid() calls based
on its configured security policies. This patch adds the analogous
functionality for setgid() calls. This is mostly a copy-and-paste change
with some code deduplication, plus slight modifications/name changes to
the policy-rule-related structs (now contain GID rules in addition to
the UID ones) and some type generalization since SafeSetID now needs to
deal with kgid_t and kuid_t types.
Signed-off-by: Thomas Cedeno <thomascedeno@google.com>
Signed-off-by: Micah Morton <mortonm@chromium.org>
---
Change since last patch: Updated documentation and added comment in
lsm.c
Documentation/admin-guide/LSM/SafeSetID.rst | 29 ++-
security/safesetid/lsm.c | 190 +++++++++++++++-----
security/safesetid/lsm.h | 38 +++-
security/safesetid/securityfs.c | 190 ++++++++++++++------
4 files changed, 329 insertions(+), 118 deletions(-)
diff --git a/Documentation/admin-guide/LSM/SafeSetID.rst b/Documentation/admin-guide/LSM/SafeSetID.rst
index 7bff07ce4fdd..17996c9070e2 100644
--- a/Documentation/admin-guide/LSM/SafeSetID.rst
+++ b/Documentation/admin-guide/LSM/SafeSetID.rst
@@ -3,9 +3,9 @@ SafeSetID
=========
SafeSetID is an LSM module that gates the setid family of syscalls to restrict
UID/GID transitions from a given UID/GID to only those approved by a
-system-wide whitelist. These restrictions also prohibit the given UIDs/GIDs
+system-wide allowlist. These restrictions also prohibit the given UIDs/GIDs
from obtaining auxiliary privileges associated with CAP_SET{U/G}ID, such as
-allowing a user to set up user namespace UID mappings.
+allowing a user to set up user namespace UID/GID mappings.
Background
@@ -98,10 +98,21 @@ Directions for use
==================
This LSM hooks the setid syscalls to make sure transitions are allowed if an
applicable restriction policy is in place. Policies are configured through
-securityfs by writing to the safesetid/add_whitelist_policy and
-safesetid/flush_whitelist_policies files at the location where securityfs is
-mounted. The format for adding a policy is '<UID>:<UID>', using literal
-numbers, such as '123:456'. To flush the policies, any write to the file is
-sufficient. Again, configuring a policy for a UID will prevent that UID from
-obtaining auxiliary setid privileges, such as allowing a user to set up user
-namespace UID mappings.
+securityfs by writing to the safesetid/uid_allowlist_policy and
+safesetid/gid_allowlist_policy files at the location where securityfs is
+mounted. The format for adding a policy is '<UID>:<UID>' or '<GID>:<GID>',
+using literal numbers, and ending with a newline character such as '123:456\n'.
+Writing an empty string "" will flush the policy. Again, configuring a policy
+for a UID/GID will prevent that UID/GID from obtaining auxiliary setid
+privileges, such as allowing a user to set up user namespace UID/GID mappings.
+
+Note on GID policies and setgroups()
+==================
+In v5.9 we are adding support for limiting CAP_SETGID privileges as was done
+previously for CAP_SETUID. However, for compatibility with common sandboxing
+related code conventions in userspace, we currently allow arbitrary
+setgroups() calls for processes with CAP_SETGID restrictions. Until we add
+support in a future release for restricting setgroups() calls, these GID
+policies add no meaningful security. setgroups() restrictions will be enforced
+once we have the policy checking code in place, which will rely on GID policy
+configuration code added in v5.9.
diff --git a/security/safesetid/lsm.c b/security/safesetid/lsm.c
index 7760019ad35d..c08e67108a82 100644
--- a/security/safesetid/lsm.c
+++ b/security/safesetid/lsm.c
@@ -24,20 +24,36 @@
/* Flag indicating whether initialization completed */
int safesetid_initialized;
-struct setuid_ruleset __rcu *safesetid_setuid_rules;
+struct setid_ruleset __rcu *safesetid_setuid_rules;
+struct setid_ruleset __rcu *safesetid_setgid_rules;
+
/* Compute a decision for a transition from @src to @dst under @policy. */
-enum sid_policy_type _setuid_policy_lookup(struct setuid_ruleset *policy,
- kuid_t src, kuid_t dst)
+enum sid_policy_type _setid_policy_lookup(struct setid_ruleset *policy,
+ kid_t src, kid_t dst)
{
- struct setuid_rule *rule;
+ struct setid_rule *rule;
enum sid_policy_type result = SIDPOL_DEFAULT;
- hash_for_each_possible(policy->rules, rule, next, __kuid_val(src)) {
- if (!uid_eq(rule->src_uid, src))
- continue;
- if (uid_eq(rule->dst_uid, dst))
- return SIDPOL_ALLOWED;
+ if (policy->type == UID) {
+ hash_for_each_possible(policy->rules, rule, next, __kuid_val(src.uid)) {
+ if (!uid_eq(rule->src_id.uid, src.uid))
+ continue;
+ if (uid_eq(rule->dst_id.uid, dst.uid))
+ return SIDPOL_ALLOWED;
+ result = SIDPOL_CONSTRAINED;
+ }
+ } else if (policy->type == GID) {
+ hash_for_each_possible(policy->rules, rule, next, __kgid_val(src.gid)) {
+ if (!gid_eq(rule->src_id.gid, src.gid))
+ continue;
+ if (gid_eq(rule->dst_id.gid, dst.gid)){
+ return SIDPOL_ALLOWED;
+ }
+ result = SIDPOL_CONSTRAINED;
+ }
+ } else {
+ /* Should not reach here, report the ID as contrainsted */
result = SIDPOL_CONSTRAINED;
}
return result;
@@ -47,15 +63,26 @@ enum sid_policy_type _setuid_policy_lookup(struct setuid_ruleset *policy,
* Compute a decision for a transition from @src to @dst under the active
* policy.
*/
-static enum sid_policy_type setuid_policy_lookup(kuid_t src, kuid_t dst)
+static enum sid_policy_type setid_policy_lookup(kid_t src, kid_t dst, enum setid_type new_type)
{
enum sid_policy_type result = SIDPOL_DEFAULT;
- struct setuid_ruleset *pol;
+ struct setid_ruleset *pol;
rcu_read_lock();
- pol = rcu_dereference(safesetid_setuid_rules);
- if (pol)
- result = _setuid_policy_lookup(pol, src, dst);
+ if (new_type == UID)
+ pol = rcu_dereference(safesetid_setuid_rules);
+ else if (new_type == GID)
+ pol = rcu_dereference(safesetid_setgid_rules);
+ else { /* Should not reach here */
+ result = SIDPOL_CONSTRAINED;
+ rcu_read_unlock();
+ return result;
+ }
+
+ if (pol) {
+ pol->type = new_type;
+ result = _setid_policy_lookup(pol, src, dst);
+ }
rcu_read_unlock();
return result;
}
@@ -65,57 +92,101 @@ static int safesetid_security_capable(const struct cred *cred,
int cap,
unsigned int opts)
{
- /* We're only interested in CAP_SETUID. */
- if (cap != CAP_SETUID)
+ /* We're only interested in CAP_SETUID and CAP_SETGID. */
+ if (cap != CAP_SETUID && cap != CAP_SETGID)
return 0;
/*
- * If CAP_SETUID is currently used for a set*uid() syscall, we want to
+ * If CAP_SET{U/G}ID is currently used for a setid() syscall, we want to
* let it go through here; the real security check happens later, in the
- * task_fix_setuid hook.
+ * task_fix_set{u/g}id hook.
+ *
+ * NOTE:
+ * Until we add support for restricting setgroups() calls, GID security
+ * policies offer no meaningful security since we always return 0 here
+ * when called from within the setgroups() syscall and there is no
+ * additional hook later on to enforce security policies for setgroups().
*/
if ((opts & CAP_OPT_INSETID) != 0)
return 0;
- /*
- * If no policy applies to this task, allow the use of CAP_SETUID for
- * other purposes.
- */
- if (setuid_policy_lookup(cred->uid, INVALID_UID) == SIDPOL_DEFAULT)
+ switch (cap) {
+ case CAP_SETUID:
+ /*
+ * If no policy applies to this task, allow the use of CAP_SETUID for
+ * other purposes.
+ */
+ if (setid_policy_lookup((kid_t)cred->uid, INVALID_ID, UID) == SIDPOL_DEFAULT)
+ return 0;
+ /*
+ * Reject use of CAP_SETUID for functionality other than calling
+ * set*uid() (e.g. setting up userns uid mappings).
+ */
+ pr_warn("Operation requires CAP_SETUID, which is not available to UID %u for operations besides approved set*uid transitions\n",
+ __kuid_val(cred->uid));
+ return -EPERM;
+ break;
+ case CAP_SETGID:
+ /*
+ * If no policy applies to this task, allow the use of CAP_SETGID for
+ * other purposes.
+ */
+ if (setid_policy_lookup((kid_t)cred->gid, INVALID_ID, GID) == SIDPOL_DEFAULT)
+ return 0;
+ /*
+ * Reject use of CAP_SETUID for functionality other than calling
+ * set*gid() (e.g. setting up userns gid mappings).
+ */
+ pr_warn("Operation requires CAP_SETGID, which is not available to GID %u for operations besides approved set*gid transitions\n",
+ __kuid_val(cred->uid));
+ return -EPERM;
+ break;
+ default:
+ /* Error, the only capabilities were checking for is CAP_SETUID/GID */
return 0;
-
- /*
- * Reject use of CAP_SETUID for functionality other than calling
- * set*uid() (e.g. setting up userns uid mappings).
- */
- pr_warn("Operation requires CAP_SETUID, which is not available to UID %u for operations besides approved set*uid transitions\n",
- __kuid_val(cred->uid));
- return -EPERM;
+ break;
+ }
+ return 0;
}
/*
* Check whether a caller with old credentials @old is allowed to switch to
- * credentials that contain @new_uid.
+ * credentials that contain @new_id.
*/
-static bool uid_permitted_for_cred(const struct cred *old, kuid_t new_uid)
+static bool id_permitted_for_cred(const struct cred *old, kid_t new_id, enum setid_type new_type)
{
bool permitted;
- /* If our old creds already had this UID in it, it's fine. */
- if (uid_eq(new_uid, old->uid) || uid_eq(new_uid, old->euid) ||
- uid_eq(new_uid, old->suid))
- return true;
+ /* If our old creds already had this ID in it, it's fine. */
+ if (new_type == UID) {
+ if (uid_eq(new_id.uid, old->uid) || uid_eq(new_id.uid, old->euid) ||
+ uid_eq(new_id.uid, old->suid))
+ return true;
+ } else if (new_type == GID){
+ if (gid_eq(new_id.gid, old->gid) || gid_eq(new_id.gid, old->egid) ||
+ gid_eq(new_id.gid, old->sgid))
+ return true;
+ } else /* Error, new_type is an invalid type */
+ return false;
/*
* Transitions to new UIDs require a check against the policy of the old
* RUID.
*/
permitted =
- setuid_policy_lookup(old->uid, new_uid) != SIDPOL_CONSTRAINED;
+ setid_policy_lookup((kid_t)old->uid, new_id, new_type) != SIDPOL_CONSTRAINED;
+
if (!permitted) {
- pr_warn("UID transition ((%d,%d,%d) -> %d) blocked\n",
- __kuid_val(old->uid), __kuid_val(old->euid),
- __kuid_val(old->suid), __kuid_val(new_uid));
+ if (new_type == UID) {
+ pr_warn("UID transition ((%d,%d,%d) -> %d) blocked\n",
+ __kuid_val(old->uid), __kuid_val(old->euid),
+ __kuid_val(old->suid), __kuid_val(new_id.uid));
+ } else if (new_type == GID) {
+ pr_warn("GID transition ((%d,%d,%d) -> %d) blocked\n",
+ __kgid_val(old->gid), __kgid_val(old->egid),
+ __kgid_val(old->sgid), __kgid_val(new_id.gid));
+ } else /* Error, new_type is an invalid type */
+ return false;
}
return permitted;
}
@@ -131,18 +202,42 @@ static int safesetid_task_fix_setuid(struct cred *new,
{
/* Do nothing if there are no setuid restrictions for our old RUID. */
- if (setuid_policy_lookup(old->uid, INVALID_UID) == SIDPOL_DEFAULT)
+ if (setid_policy_lookup((kid_t)old->uid, INVALID_ID, UID) == SIDPOL_DEFAULT)
+ return 0;
+
+ if (id_permitted_for_cred(old, (kid_t)new->uid, UID) &&
+ id_permitted_for_cred(old, (kid_t)new->euid, UID) &&
+ id_permitted_for_cred(old, (kid_t)new->suid, UID) &&
+ id_permitted_for_cred(old, (kid_t)new->fsuid, UID))
+ return 0;
+
+ /*
+ * Kill this process to avoid potential security vulnerabilities
+ * that could arise from a missing allowlist entry preventing a
+ * privileged process from dropping to a lesser-privileged one.
+ */
+ force_sig(SIGKILL);
+ return -EACCES;
+}
+
+static int safesetid_task_fix_setgid(struct cred *new,
+ const struct cred *old,
+ int flags)
+{
+
+ /* Do nothing if there are no setgid restrictions for our old RGID. */
+ if (setid_policy_lookup((kid_t)old->gid, INVALID_ID, GID) == SIDPOL_DEFAULT)
return 0;
- if (uid_permitted_for_cred(old, new->uid) &&
- uid_permitted_for_cred(old, new->euid) &&
- uid_permitted_for_cred(old, new->suid) &&
- uid_permitted_for_cred(old, new->fsuid))
+ if (id_permitted_for_cred(old, (kid_t)new->gid, GID) &&
+ id_permitted_for_cred(old, (kid_t)new->egid, GID) &&
+ id_permitted_for_cred(old, (kid_t)new->sgid, GID) &&
+ id_permitted_for_cred(old, (kid_t)new->fsgid, GID))
return 0;
/*
* Kill this process to avoid potential security vulnerabilities
- * that could arise from a missing whitelist entry preventing a
+ * that could arise from a missing allowlist entry preventing a
* privileged process from dropping to a lesser-privileged one.
*/
force_sig(SIGKILL);
@@ -151,6 +246,7 @@ static int safesetid_task_fix_setuid(struct cred *new,
static struct security_hook_list safesetid_security_hooks[] = {
LSM_HOOK_INIT(task_fix_setuid, safesetid_task_fix_setuid),
+ LSM_HOOK_INIT(task_fix_setgid, safesetid_task_fix_setgid),
LSM_HOOK_INIT(capable, safesetid_security_capable)
};
diff --git a/security/safesetid/lsm.h b/security/safesetid/lsm.h
index db6d16e6bbc3..bde8c43a3767 100644
--- a/security/safesetid/lsm.h
+++ b/security/safesetid/lsm.h
@@ -27,27 +27,47 @@ enum sid_policy_type {
SIDPOL_ALLOWED /* target ID explicitly allowed */
};
+typedef union {
+ kuid_t uid;
+ kgid_t gid;
+} kid_t;
+
+enum setid_type {
+ UID,
+ GID
+};
+
/*
- * Hash table entry to store safesetid policy signifying that 'src_uid'
- * can setuid to 'dst_uid'.
+ * Hash table entry to store safesetid policy signifying that 'src_id'
+ * can set*id to 'dst_id'.
*/
-struct setuid_rule {
+struct setid_rule {
struct hlist_node next;
- kuid_t src_uid;
- kuid_t dst_uid;
+ kid_t src_id;
+ kid_t dst_id;
+
+ /* Flag to signal if rule is for UID's or GID's */
+ enum setid_type type;
};
#define SETID_HASH_BITS 8 /* 256 buckets in hash table */
-struct setuid_ruleset {
+/* Extension of INVALID_UID/INVALID_GID for kid_t type */
+#define INVALID_ID (kid_t){.uid = INVALID_UID}
+
+struct setid_ruleset {
DECLARE_HASHTABLE(rules, SETID_HASH_BITS);
char *policy_str;
struct rcu_head rcu;
+
+ //Flag to signal if ruleset is for UID's or GID's
+ enum setid_type type;
};
-enum sid_policy_type _setuid_policy_lookup(struct setuid_ruleset *policy,
- kuid_t src, kuid_t dst);
+enum sid_policy_type _setid_policy_lookup(struct setid_ruleset *policy,
+ kid_t src, kid_t dst);
-extern struct setuid_ruleset __rcu *safesetid_setuid_rules;
+extern struct setid_ruleset __rcu *safesetid_setuid_rules;
+extern struct setid_ruleset __rcu *safesetid_setgid_rules;
#endif /* _SAFESETID_H */
diff --git a/security/safesetid/securityfs.c b/security/safesetid/securityfs.c
index f8bc574cea9c..642139008d42 100644
--- a/security/safesetid/securityfs.c
+++ b/security/safesetid/securityfs.c
@@ -19,22 +19,23 @@
#include "lsm.h"
-static DEFINE_MUTEX(policy_update_lock);
+static DEFINE_MUTEX(uid_policy_update_lock);
+static DEFINE_MUTEX(gid_policy_update_lock);
/*
- * In the case the input buffer contains one or more invalid UIDs, the kuid_t
+ * In the case the input buffer contains one or more invalid IDs, the kid_t
* variables pointed to by @parent and @child will get updated but this
* function will return an error.
* Contents of @buf may be modified.
*/
static int parse_policy_line(struct file *file, char *buf,
- struct setuid_rule *rule)
+ struct setid_rule *rule)
{
char *child_str;
int ret;
u32 parsed_parent, parsed_child;
- /* Format of |buf| string should be <UID>:<UID>. */
+ /* Format of |buf| string should be <UID>:<UID> or <GID>:<GID> */
child_str = strchr(buf, ':');
if (child_str == NULL)
return -EINVAL;
@@ -49,20 +50,29 @@ static int parse_policy_line(struct file *file, char *buf,
if (ret)
return ret;
- rule->src_uid = make_kuid(file->f_cred->user_ns, parsed_parent);
- rule->dst_uid = make_kuid(file->f_cred->user_ns, parsed_child);
- if (!uid_valid(rule->src_uid) || !uid_valid(rule->dst_uid))
+ if (rule->type == UID){
+ rule->src_id.uid = make_kuid(file->f_cred->user_ns, parsed_parent);
+ rule->dst_id.uid = make_kuid(file->f_cred->user_ns, parsed_child);
+ if (!uid_valid(rule->src_id.uid) || !uid_valid(rule->dst_id.uid))
+ return -EINVAL;
+ } else if (rule->type == GID){
+ rule->src_id.gid = make_kgid(file->f_cred->user_ns, parsed_parent);
+ rule->dst_id.gid = make_kgid(file->f_cred->user_ns, parsed_child);
+ if (!gid_valid(rule->src_id.gid) || !gid_valid(rule->dst_id.gid))
+ return -EINVAL;
+ } else {
+ /* Error, rule->type is an invalid type */
return -EINVAL;
-
+ }
return 0;
}
static void __release_ruleset(struct rcu_head *rcu)
{
- struct setuid_ruleset *pol =
- container_of(rcu, struct setuid_ruleset, rcu);
+ struct setid_ruleset *pol =
+ container_of(rcu, struct setid_ruleset, rcu);
int bucket;
- struct setuid_rule *rule;
+ struct setid_rule *rule;
struct hlist_node *tmp;
hash_for_each_safe(pol->rules, bucket, tmp, rule, next)
@@ -71,36 +81,55 @@ static void __release_ruleset(struct rcu_head *rcu)
kfree(pol);
}
-static void release_ruleset(struct setuid_ruleset *pol)
-{
+static void release_ruleset(struct setid_ruleset *pol){
call_rcu(&pol->rcu, __release_ruleset);
}
-static void insert_rule(struct setuid_ruleset *pol, struct setuid_rule *rule)
+static void insert_rule(struct setid_ruleset *pol, struct setid_rule *rule)
{
- hash_add(pol->rules, &rule->next, __kuid_val(rule->src_uid));
+ if (pol->type == UID)
+ hash_add(pol->rules, &rule->next, __kuid_val(rule->src_id.uid));
+ else if (pol->type == GID)
+ hash_add(pol->rules, &rule->next, __kgid_val(rule->src_id.gid));
+ else /* Error, pol->type is neither UID or GID */
+ return;
}
-static int verify_ruleset(struct setuid_ruleset *pol)
+static int verify_ruleset(struct setid_ruleset *pol)
{
int bucket;
- struct setuid_rule *rule, *nrule;
+ struct setid_rule *rule, *nrule;
int res = 0;
hash_for_each(pol->rules, bucket, rule, next) {
- if (_setuid_policy_lookup(pol, rule->dst_uid, INVALID_UID) ==
- SIDPOL_DEFAULT) {
- pr_warn("insecure policy detected: uid %d is constrained but transitively unconstrained through uid %d\n",
- __kuid_val(rule->src_uid),
- __kuid_val(rule->dst_uid));
+ if (_setid_policy_lookup(pol, rule->dst_id, INVALID_ID) == SIDPOL_DEFAULT) {
+ if (pol->type == UID) {
+ pr_warn("insecure policy detected: uid %d is constrained but transitively unconstrained through uid %d\n",
+ __kuid_val(rule->src_id.uid),
+ __kuid_val(rule->dst_id.uid));
+ } else if (pol->type == GID) {
+ pr_warn("insecure policy detected: gid %d is constrained but transitively unconstrained through gid %d\n",
+ __kgid_val(rule->src_id.gid),
+ __kgid_val(rule->dst_id.gid));
+ } else { /* pol->type is an invalid type */
+ res = -EINVAL;
+ return res;
+ }
res = -EINVAL;
/* fix it up */
- nrule = kmalloc(sizeof(struct setuid_rule), GFP_KERNEL);
+ nrule = kmalloc(sizeof(struct setid_rule), GFP_KERNEL);
if (!nrule)
return -ENOMEM;
- nrule->src_uid = rule->dst_uid;
- nrule->dst_uid = rule->dst_uid;
+ if (pol->type == UID){
+ nrule->src_id.uid = rule->dst_id.uid;
+ nrule->dst_id.uid = rule->dst_id.uid;
+ nrule->type = UID;
+ } else { /* pol->type must be GID if we've made it to here */
+ nrule->src_id.gid = rule->dst_id.gid;
+ nrule->dst_id.gid = rule->dst_id.gid;
+ nrule->type = GID;
+ }
insert_rule(pol, nrule);
}
}
@@ -108,16 +137,17 @@ static int verify_ruleset(struct setuid_ruleset *pol)
}
static ssize_t handle_policy_update(struct file *file,
- const char __user *ubuf, size_t len)
+ const char __user *ubuf, size_t len, enum setid_type policy_type)
{
- struct setuid_ruleset *pol;
+ struct setid_ruleset *pol;
char *buf, *p, *end;
int err;
- pol = kmalloc(sizeof(struct setuid_ruleset), GFP_KERNEL);
+ pol = kmalloc(sizeof(struct setid_ruleset), GFP_KERNEL);
if (!pol)
return -ENOMEM;
pol->policy_str = NULL;
+ pol->type = policy_type;
hash_init(pol->rules);
p = buf = memdup_user_nul(ubuf, len);
@@ -133,7 +163,7 @@ static ssize_t handle_policy_update(struct file *file,
/* policy lines, including the last one, end with \n */
while (*p != '\0') {
- struct setuid_rule *rule;
+ struct setid_rule *rule;
end = strchr(p, '\n');
if (end == NULL) {
@@ -142,18 +172,18 @@ static ssize_t handle_policy_update(struct file *file,
}
*end = '\0';
- rule = kmalloc(sizeof(struct setuid_rule), GFP_KERNEL);
+ rule = kmalloc(sizeof(struct setid_rule), GFP_KERNEL);
if (!rule) {
err = -ENOMEM;
goto out_free_buf;
}
+ rule->type = policy_type;
err = parse_policy_line(file, p, rule);
if (err)
goto out_free_rule;
- if (_setuid_policy_lookup(pol, rule->src_uid, rule->dst_uid) ==
- SIDPOL_ALLOWED) {
+ if (_setid_policy_lookup(pol, rule->src_id, rule->dst_id) == SIDPOL_ALLOWED) {
pr_warn("bad policy: duplicate entry\n");
err = -EEXIST;
goto out_free_rule;
@@ -178,21 +208,31 @@ static ssize_t handle_policy_update(struct file *file,
* What we really want here is an xchg() wrapper for RCU, but since that
* doesn't currently exist, just use a spinlock for now.
*/
- mutex_lock(&policy_update_lock);
- pol = rcu_replace_pointer(safesetid_setuid_rules, pol,
- lockdep_is_held(&policy_update_lock));
- mutex_unlock(&policy_update_lock);
+ if (policy_type == UID) {
+ mutex_lock(&uid_policy_update_lock);
+ pol = rcu_replace_pointer(safesetid_setuid_rules, pol,
+ lockdep_is_held(&uid_policy_update_lock));
+ mutex_unlock(&uid_policy_update_lock);
+ } else if (policy_type == GID) {
+ mutex_lock(&gid_policy_update_lock);
+ pol = rcu_replace_pointer(safesetid_setgid_rules, pol,
+ lockdep_is_held(&gid_policy_update_lock));
+ mutex_unlock(&gid_policy_update_lock);
+ } else {
+ /* Error, policy type is neither UID or GID */
+ pr_warn("error: bad policy type");
+ }
err = len;
out_free_buf:
kfree(buf);
out_free_pol:
if (pol)
- release_ruleset(pol);
+ release_ruleset(pol);
return err;
}
-static ssize_t safesetid_file_write(struct file *file,
+static ssize_t safesetid_uid_file_write(struct file *file,
const char __user *buf,
size_t len,
loff_t *ppos)
@@ -203,38 +243,74 @@ static ssize_t safesetid_file_write(struct file *file,
if (*ppos != 0)
return -EINVAL;
- return handle_policy_update(file, buf, len);
+ return handle_policy_update(file, buf, len, UID);
+}
+
+static ssize_t safesetid_gid_file_write(struct file *file,
+ const char __user *buf,
+ size_t len,
+ loff_t *ppos)
+{
+ if (!file_ns_capable(file, &init_user_ns, CAP_MAC_ADMIN))
+ return -EPERM;
+
+ if (*ppos != 0)
+ return -EINVAL;
+
+ return handle_policy_update(file, buf, len, GID);
}
static ssize_t safesetid_file_read(struct file *file, char __user *buf,
- size_t len, loff_t *ppos)
+ size_t len, loff_t *ppos, struct mutex *policy_update_lock, struct setid_ruleset* ruleset)
{
ssize_t res = 0;
- struct setuid_ruleset *pol;
+ struct setid_ruleset *pol;
const char *kbuf;
- mutex_lock(&policy_update_lock);
- pol = rcu_dereference_protected(safesetid_setuid_rules,
- lockdep_is_held(&policy_update_lock));
+ mutex_lock(policy_update_lock);
+ pol = rcu_dereference_protected(ruleset, lockdep_is_held(policy_update_lock));
if (pol) {
kbuf = pol->policy_str;
res = simple_read_from_buffer(buf, len, ppos,
kbuf, strlen(kbuf));
}
- mutex_unlock(&policy_update_lock);
+ mutex_unlock(policy_update_lock);
+
return res;
}
-static const struct file_operations safesetid_file_fops = {
- .read = safesetid_file_read,
- .write = safesetid_file_write,
+static ssize_t safesetid_uid_file_read(struct file *file, char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ return safesetid_file_read(file, buf, len, ppos,
+ &uid_policy_update_lock, safesetid_setuid_rules);
+}
+
+static ssize_t safesetid_gid_file_read(struct file *file, char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ return safesetid_file_read(file, buf, len, ppos,
+ &gid_policy_update_lock, safesetid_setgid_rules);
+}
+
+
+
+static const struct file_operations safesetid_uid_file_fops = {
+ .read = safesetid_uid_file_read,
+ .write = safesetid_uid_file_write,
+};
+
+static const struct file_operations safesetid_gid_file_fops = {
+ .read = safesetid_gid_file_read,
+ .write = safesetid_gid_file_write,
};
static int __init safesetid_init_securityfs(void)
{
int ret;
struct dentry *policy_dir;
- struct dentry *policy_file;
+ struct dentry *uid_policy_file;
+ struct dentry *gid_policy_file;
if (!safesetid_initialized)
return 0;
@@ -245,13 +321,21 @@ static int __init safesetid_init_securityfs(void)
goto error;
}
- policy_file = securityfs_create_file("whitelist_policy", 0600,
- policy_dir, NULL, &safesetid_file_fops);
- if (IS_ERR(policy_file)) {
- ret = PTR_ERR(policy_file);
+ uid_policy_file = securityfs_create_file("uid_allowlist_policy", 0600,
+ policy_dir, NULL, &safesetid_uid_file_fops);
+ if (IS_ERR(uid_policy_file)) {
+ ret = PTR_ERR(uid_policy_file);
goto error;
}
+ gid_policy_file = securityfs_create_file("gid_allowlist_policy", 0600,
+ policy_dir, NULL, &safesetid_gid_file_fops);
+ if (IS_ERR(gid_policy_file)) {
+ ret = PTR_ERR(gid_policy_file);
+ goto error;
+ }
+
+
return 0;
error:
--
2.28.0.163.g6104cc2f0b6-goog
^ permalink raw reply related
* Re: [PATCH v5 1/3] Add a new LSM-supporting anonymous inode interface
From: Eric Biggers @ 2020-08-04 21:22 UTC (permalink / raw)
To: Daniel Colascione
Cc: timmurray, selinux, linux-security-module, linux-fsdevel,
linux-kernel, kvm, viro, paul, nnk, sds, lokeshgidra, jmorris
In-Reply-To: <20200401213903.182112-2-dancol@google.com>
On Wed, Apr 01, 2020 at 02:39:01PM -0700, Daniel Colascione wrote:
> This change adds two new functions, anon_inode_getfile_secure and
> anon_inode_getfd_secure, that create anonymous-node files with
> individual non-S_PRIVATE inodes to which security modules can apply
> policy. Existing callers continue using the original singleton-inode
> kind of anonymous-inode file. We can transition anonymous inode users
> to the new kind of anonymous inode in individual patches for the sake
> of bisection and review.
>
> The new functions accept an optional context_inode parameter that
> callers can use to provide additional contextual information to
> security modules, e.g., indicating that one anonymous struct file is a
> logical child of another, allowing a security model to propagate
> security information from one to the other.
>
> Signed-off-by: Daniel Colascione <dancol@google.com>
> ---
> fs/anon_inodes.c | 191 ++++++++++++++++++++++++++++--------
> include/linux/anon_inodes.h | 13 +++
> include/linux/lsm_hooks.h | 11 +++
> include/linux/security.h | 3 +
> security/security.c | 9 ++
> 5 files changed, 186 insertions(+), 41 deletions(-)
>
> diff --git a/fs/anon_inodes.c b/fs/anon_inodes.c
> index 89714308c25b..f87f221167cf 100644
> --- a/fs/anon_inodes.c
> +++ b/fs/anon_inodes.c
> @@ -55,61 +55,108 @@ static struct file_system_type anon_inode_fs_type = {
> .kill_sb = kill_anon_super,
> };
>
> -/**
> - * anon_inode_getfile - creates a new file instance by hooking it up to an
> - * anonymous inode, and a dentry that describe the "class"
> - * of the file
> - *
> - * @name: [in] name of the "class" of the new file
> - * @fops: [in] file operations for the new file
> - * @priv: [in] private data for the new file (will be file's private_data)
> - * @flags: [in] flags
> - *
> - * Creates a new file by hooking it on a single inode. This is useful for files
> - * that do not need to have a full-fledged inode in order to operate correctly.
> - * All the files created with anon_inode_getfile() will share a single inode,
> - * hence saving memory and avoiding code duplication for the file/inode/dentry
> - * setup. Returns the newly created file* or an error pointer.
> - */
> -struct file *anon_inode_getfile(const char *name,
> - const struct file_operations *fops,
> - void *priv, int flags)
> +static struct inode *anon_inode_make_secure_inode(
> + const char *name,
> + const struct inode *context_inode)
> +{
> + struct inode *inode;
> + const struct qstr qname = QSTR_INIT(name, strlen(name));
> + int error;
> +
> + inode = alloc_anon_inode(anon_inode_mnt->mnt_sb);
> + if (IS_ERR(inode))
> + return inode;
> + inode->i_flags &= ~S_PRIVATE;
> + error = security_inode_init_security_anon(
> + inode, &qname, context_inode);
> + if (error) {
> + iput(inode);
> + return ERR_PTR(error);
> + }
> + return inode;
> +}
> +
> +struct file *_anon_inode_getfile(const char *name,
> + const struct file_operations *fops,
> + void *priv, int flags,
> + const struct inode *context_inode,
> + bool secure)
Unnecessarily global function.
> {
> + struct inode *inode;
> struct file *file;
>
> - if (IS_ERR(anon_inode_inode))
> - return ERR_PTR(-ENODEV);
> + if (secure) {
> + inode = anon_inode_make_secure_inode(
> + name, context_inode);
> + if (IS_ERR(inode))
> + return ERR_PTR(PTR_ERR(inode));
Use ERR_CAST(), not ERR_PTR(PTR_ERR()).
> /**
> - * anon_inode_getfd - creates a new file instance by hooking it up to an
> - * anonymous inode, and a dentry that describe the "class"
> - * of the file
> + * anon_inode_getfile_secure - creates a new file instance by hooking
> + * it up to a new anonymous inode and a
> + * dentry that describe the "class" of the
> + * file. Make it possible to use security
> + * modules to control access to the
> + * new file.
> + *
> + * @name: [in] name of the "class" of the new file
> + * @fops: [in] file operations for the new file
> + * @priv: [in] private data for the new file (will be file's private_data)
> + * @flags: [in] flags
> + *
> + * Creates a new file by hooking it on an unspecified inode. This is
> + * useful for files that do not need to have a full-fledged filesystem
> + * to operate correctly. All the files created with
> + * anon_inode_getfile_secure() will have distinct inodes, avoiding
> + * code duplication for the file/inode/dentry setup. Returns the
> + * newly created file* or an error pointer.
> + */
> +struct file *anon_inode_getfile_secure(const char *name,
> + const struct file_operations *fops,
> + void *priv, int flags,
> + const struct inode *context_inode)
Why copy-and-paste this long comment if it's not even updated to document the
new argument?
> diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
> index 20d8cf194fb7..5434c1d285b2 100644
> --- a/include/linux/lsm_hooks.h
> +++ b/include/linux/lsm_hooks.h
> @@ -215,6 +215,13 @@
> * Returns 0 if @name and @value have been successfully set,
> * -EOPNOTSUPP if no security attribute is needed, or
> * -ENOMEM on memory allocation failure.
> + * @inode_init_security_anon:
> + * Set up a secure anonymous inode.
> + * @inode contains the inode structure
> + * @name name of the anonymous inode class
> + * @context_inode optional related inode
> + * Returns 0 on success. Returns -EPERM if the security module denies
> + * the creation of this inode.
Shouldn't it be EACCES?
^ permalink raw reply
* Re: [GIT PULL] Smack patches for v5.9
From: Linus Torvalds @ 2020-08-04 21:26 UTC (permalink / raw)
To: Casey Schaufler; +Cc: Linux Security Module list, LKML
In-Reply-To: <8ce85723-5656-0ee8-67a7-35597d9df0dd@schaufler-ca.com>
On Tue, Aug 4, 2020 at 10:49 AM Casey Schaufler <casey@schaufler-ca.com> wrote:
>
> Here are three minor fixes to Smack for the v5.9 release.
I can't pull this: that's not a signed tag, and I don't pull unsigned
stuff from open hosting sites, no matter how obvious they may seem.
You typically have a capitalized signed tag, but I'm not seeing that
this time. Hmm?
Linus
^ permalink raw reply
* Re: [GIT PULL] SELinux patches for v5.9
From: pr-tracker-bot @ 2020-08-04 21:40 UTC (permalink / raw)
To: Paul Moore; +Cc: Linus Torvalds, selinux, linux-security-module, linux-kernel
In-Reply-To: <CAHC9VhTy5xcOqx2SRjsyC-H-xvj3vvbHDt7O-S7TLYhXjANZGw@mail.gmail.com>
The pull request you sent on Mon, 3 Aug 2020 19:16:10 -0400:
> git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux.git tags/selinux-pr-20200803
has been merged into torvalds/linux.git:
https://git.kernel.org/torvalds/c/49e917deeb81e263bcdb4b20e61ca18111995ffe
Thank you!
--
Deet-doot-dot, I am a bot.
https://korg.wiki.kernel.org/userdoc/prtracker
^ permalink raw reply
* Re: [GIT PULL] Smack patches for v5.9
From: Casey Schaufler @ 2020-08-04 21:43 UTC (permalink / raw)
To: Linus Torvalds; +Cc: Linux Security Module list, LKML
In-Reply-To: <CAHk-=wjkuGCCk7DCNP6836FYeOaKZR9KLOBzr21fPOVNnOZiKA@mail.gmail.com>
On 8/4/2020 2:26 PM, Linus Torvalds wrote:
> On Tue, Aug 4, 2020 at 10:49 AM Casey Schaufler <casey@schaufler-ca.com> wrote:
>> Here are three minor fixes to Smack for the v5.9 release.
> I can't pull this: that's not a signed tag, and I don't pull unsigned
> stuff from open hosting sites, no matter how obvious they may seem.
>
> You typically have a capitalized signed tag, but I'm not seeing that
> this time. Hmm?
You're right. Tag usage failure on my part.
I will resubmit. Sorry for the extra work.
> Linus
^ permalink raw reply
* [GIT PULL] Smack patches for v5.9
From: Casey Schaufler @ 2020-08-04 21:48 UTC (permalink / raw)
To: Linus Torvalds; +Cc: Linux Security Module list, LKML, Casey Schaufler
In-Reply-To: <8ce85723-5656-0ee8-67a7-35597d9df0dd@schaufler-ca.com>
Hello Linus
I have corrected the tagging for the pull request.
Here are three minor fixes to Smack for the v5.9 release.
All were found by automated checkers and have straight forward
resolution.
--
The following changes since commit b3a9e3b9622ae10064826dccb4f7a52bd88c7407:
Linux 5.8-rc1 (2020-06-14 12:45:04 -0700)
are available in the Git repository at:
https://github.com/cschaufler/smack-next tags/Smack-for-5.9
for you to fetch changes up to 42a2df3e829f3c5562090391b33714b2e2e5ad4a:
Smack: prevent underflow in smk_set_cipso() (2020-07-27 13:35:12 -0700)
----------------------------------------------------------------
Smack fixes for 5.9
----------------------------------------------------------------
Dan Carpenter (2):
Smack: fix another vsscanf out of bounds
Smack: prevent underflow in smk_set_cipso()
Eric Biggers (1):
Smack: fix use-after-free in smk_write_relabel_self()
security/smack/smackfs.c | 19 ++++++++++++++++---
1 file changed, 16 insertions(+), 3 deletions(-)
^ permalink raw reply
* [PATCH v6 0/4] LSM: Measure security module data
From: Lakshmi Ramasubramanian @ 2020-08-05 0:43 UTC (permalink / raw)
To: zohar, stephen.smalley.work, casey
Cc: tyhicks, sashal, jmorris, linux-integrity, selinux,
linux-security-module, linux-kernel
Critical data structures of security modules are currently not measured.
Therefore an attestation service, for instance, would not be able to
attest whether the security modules are always operating with the policies
and configuration that the system administrator had setup. The policies
and configuration for the security modules could be tampered with by
malware by exploiting kernel vulnerabilities or modified through some
inadvertent actions on the system. Measuring such critical data would
enable an attestation service to better assess the state of the system.
IMA subsystem measures system files, command line arguments passed to
kexec, boot aggregate, keys, etc. It can be used to measure critical
data structures of security modules as well.
This change aims to address measuring critical data structures
of security modules when they are initialized and when they are
updated at runtime.
This series is based on commit 3db0d0c276a7 ("integrity: remove
redundant initialization of variable ret") in next-integrity
Change log:
v6:
=> Use kvmalloc for payload data for early boot data measurement
since payload size may exceed the limit supported by kmalloc.
=> Fixed IMA policy rule match error when checking for IMA hook
func LSM_STATE and LSM_POLICY.
=> Enable early boot data measurement and IMA hook func
LSM_STATE and LSM_POLICY when SELinux is enabled.
v5:
=> Append timestamp to "event name" string in the call to
the IMA hooks so that LSM data is always measured by IMA.
=> Removed workqueue patch that was handling periodic checking
of the LSM data. This change will be introduced as a separate
patch set while keeping this patch set focussed on measuring
the LSM data on initialization and on updates at runtime.
=> Handle early boot measurement of LSM data.
v4:
=> Added LSM_POLICY func and IMA hook to measure LSM policy.
=> Pass SELinux policy data, instead of the hash of the policy,
to the IMA hook to measure.
=> Include "initialized" flag in SELinux measurement.
Also, measure SELinux state even when initialization is not yet
completed. But measure SELinux policy only after initialization.
v3:
=> Loop through policy_capabilities to build the state data
to measure instead of hardcoding to current set of
policy capabilities.
=> Added error log messages for failure conditions.
v2:
=> Pass selinux_state struct as parameter to the function
that measures SELinux data.
=> Use strings from selinux_policycap_names array for SELinux
state measurement.
=> Refactored security_read_policy() to alloc kernel or user
virtual memory and then read the SELinux policy.
v1:
=> Per Stephen Smalley's suggestion added selinux_state booleans
and hash of SELinux policy in the measured data for SELinux.
=> Call IMA hook from the security module directly instead of
redirecting through the LSM.
Lakshmi Ramasubramanian (4):
IMA: Add func to measure LSM state and policy
IMA: Define IMA hooks to measure LSM state and policy
LSM: Define SELinux function to measure state and policy
IMA: Handle early boot data measurement
Documentation/ABI/testing/ima_policy | 9 +
include/linux/ima.h | 14 ++
security/integrity/ima/Kconfig | 5 +-
security/integrity/ima/Makefile | 2 +-
security/integrity/ima/ima.h | 45 +++--
security/integrity/ima/ima_api.c | 2 +-
security/integrity/ima/ima_asymmetric_keys.c | 6 +-
security/integrity/ima/ima_init.c | 2 +-
security/integrity/ima/ima_main.c | 64 ++++++-
security/integrity/ima/ima_policy.c | 36 +++-
security/integrity/ima/ima_queue_data.c | 190 +++++++++++++++++++
security/integrity/ima/ima_queue_keys.c | 174 -----------------
security/selinux/Makefile | 2 +
security/selinux/hooks.c | 1 +
security/selinux/include/security.h | 15 ++
security/selinux/measure.c | 150 +++++++++++++++
security/selinux/selinuxfs.c | 3 +
security/selinux/ss/services.c | 71 ++++++-
18 files changed, 569 insertions(+), 222 deletions(-)
create mode 100644 security/integrity/ima/ima_queue_data.c
delete mode 100644 security/integrity/ima/ima_queue_keys.c
create mode 100644 security/selinux/measure.c
--
2.27.0
^ permalink raw reply
* [PATCH v6 1/4] IMA: Add func to measure LSM state and policy
From: Lakshmi Ramasubramanian @ 2020-08-05 0:43 UTC (permalink / raw)
To: zohar, stephen.smalley.work, casey
Cc: tyhicks, sashal, jmorris, linux-integrity, selinux,
linux-security-module, linux-kernel
In-Reply-To: <20200805004331.20652-1-nramas@linux.microsoft.com>
Critical data structures of security modules need to be measured to
enable an attestation service to verify if the configuration and
policies for the security modules have been setup correctly and
that they haven't been tampered with at runtime. A new IMA policy is
required for handling this measurement.
Define two new IMA policy func namely LSM_STATE and LSM_POLICY to
measure the state and the policy provided by the security modules.
Update ima_match_rules() and ima_validate_rule() to check for
the new func and ima_parse_rule() to handle the new func.
Signed-off-by: Lakshmi Ramasubramanian <nramas@linux.microsoft.com>
---
Documentation/ABI/testing/ima_policy | 9 ++++++++
security/integrity/ima/ima.h | 2 ++
security/integrity/ima/ima_api.c | 2 +-
security/integrity/ima/ima_policy.c | 34 ++++++++++++++++++++++++----
4 files changed, 42 insertions(+), 5 deletions(-)
diff --git a/Documentation/ABI/testing/ima_policy b/Documentation/ABI/testing/ima_policy
index cd572912c593..b7c7fb548c0c 100644
--- a/Documentation/ABI/testing/ima_policy
+++ b/Documentation/ABI/testing/ima_policy
@@ -30,6 +30,7 @@ Description:
[FIRMWARE_CHECK]
[KEXEC_KERNEL_CHECK] [KEXEC_INITRAMFS_CHECK]
[KEXEC_CMDLINE] [KEY_CHECK]
+ [LSM_STATE] [LSM_POLICY]
mask:= [[^]MAY_READ] [[^]MAY_WRITE] [[^]MAY_APPEND]
[[^]MAY_EXEC]
fsmagic:= hex value
@@ -125,3 +126,11 @@ Description:
keys added to .builtin_trusted_keys or .ima keyring:
measure func=KEY_CHECK keyrings=.builtin_trusted_keys|.ima
+
+ Example of measure rule using LSM_STATE to measure LSM state:
+
+ measure func=LSM_STATE template=ima-buf
+
+ Example of measure rule using LSM_POLICY to measure LSM policy:
+
+ measure func=LSM_POLICY template=ima-ng
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 38043074ce5e..1b5f4b2f17d0 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -200,6 +200,8 @@ static inline unsigned int ima_hash_key(u8 *digest)
hook(POLICY_CHECK, policy) \
hook(KEXEC_CMDLINE, kexec_cmdline) \
hook(KEY_CHECK, key) \
+ hook(LSM_STATE, lsm_state) \
+ hook(LSM_POLICY, lsm_policy) \
hook(MAX_CHECK, none)
#define __ima_hook_enumify(ENUM, str) ENUM,
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index 4f39fb93f278..8c8b4e4a6493 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -176,7 +176,7 @@ void ima_add_violation(struct file *file, const unsigned char *filename,
* subj=, obj=, type=, func=, mask=, fsmagic=
* subj,obj, and type: are LSM specific.
* func: FILE_CHECK | BPRM_CHECK | CREDS_CHECK | MMAP_CHECK | MODULE_CHECK
- * | KEXEC_CMDLINE | KEY_CHECK
+ * | KEXEC_CMDLINE | KEY_CHECK | LSM_STATE | LSM_POLICY
* mask: contains the permission mask
* fsmagic: hex value
*
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index 07f033634b27..e4de581442d5 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -442,13 +442,21 @@ static bool ima_match_rules(struct ima_rule_entry *rule, struct inode *inode,
{
int i;
- if (func == KEY_CHECK) {
- return (rule->flags & IMA_FUNC) && (rule->func == func) &&
- ima_match_keyring(rule, keyring, cred);
- }
if ((rule->flags & IMA_FUNC) &&
(rule->func != func && func != POST_SETATTR))
return false;
+
+ switch (func) {
+ case KEY_CHECK:
+ return ((rule->func == func) &&
+ ima_match_keyring(rule, keyring, cred));
+ case LSM_STATE:
+ case LSM_POLICY:
+ return (rule->func == func);
+ default:
+ break;
+ }
+
if ((rule->flags & IMA_MASK) &&
(rule->mask != mask && func != POST_SETATTR))
return false;
@@ -1044,6 +1052,18 @@ static bool ima_validate_rule(struct ima_rule_entry *entry)
if (ima_rule_contains_lsm_cond(entry))
return false;
+ break;
+ case LSM_STATE:
+ case LSM_POLICY:
+ if (entry->action & ~(MEASURE | DONT_MEASURE))
+ return false;
+
+ if (entry->flags & ~(IMA_FUNC | IMA_PCR))
+ return false;
+
+ if (ima_rule_contains_lsm_cond(entry))
+ return false;
+
break;
default:
return false;
@@ -1176,6 +1196,12 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
entry->func = KEXEC_CMDLINE;
else if (strcmp(args[0].from, "KEY_CHECK") == 0)
entry->func = KEY_CHECK;
+ else if (IS_ENABLED(CONFIG_SECURITY_SELINUX) &&
+ strcmp(args[0].from, "LSM_STATE") == 0)
+ entry->func = LSM_STATE;
+ else if (IS_ENABLED(CONFIG_SECURITY_SELINUX) &&
+ strcmp(args[0].from, "LSM_POLICY") == 0)
+ entry->func = LSM_POLICY;
else
result = -EINVAL;
if (!result)
--
2.27.0
^ permalink raw reply related
* [PATCH v6 4/4] IMA: Handle early boot data measurement
From: Lakshmi Ramasubramanian @ 2020-08-05 0:43 UTC (permalink / raw)
To: zohar, stephen.smalley.work, casey
Cc: tyhicks, sashal, jmorris, linux-integrity, selinux,
linux-security-module, linux-kernel
In-Reply-To: <20200805004331.20652-1-nramas@linux.microsoft.com>
The current implementation of early boot measurement in
the IMA subsystem is very specific to asymmetric keys. It does not
handle measurement of other data such as Linux Security Module (LSM)
data. Since most security modules are initialized very early in
the boot cycle, data provided by those modules are not measured
by IMA. Any other subsystem that initializes early in the boot cycle
and needs IMA to measure their data would suffer from the same issue.
Update the early boot key measurement to handle any early boot data.
Change the kernel configuration CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS to
CONFIG_IMA_QUEUE_EARLY_BOOT_DATA so it can be used for enabling
early boot data measurement. Change this new configuration to support
SECURITY_SELINUX subsystem in addition to KEYS subsystem, which is
currently supported. This can be extended to include more subsystems
in the future by updating this kernel configuration.
Update LSM hooks namely ima_measure_lsm_state() and ima_measure_lsm_policy
to utilize early boot measurement support.
Signed-off-by: Lakshmi Ramasubramanian <nramas@linux.microsoft.com>
---
security/integrity/ima/Kconfig | 5 +-
security/integrity/ima/Makefile | 2 +-
security/integrity/ima/ima.h | 37 ++--
security/integrity/ima/ima_asymmetric_keys.c | 6 +-
security/integrity/ima/ima_init.c | 2 +-
security/integrity/ima/ima_main.c | 22 ++-
security/integrity/ima/ima_policy.c | 2 +-
security/integrity/ima/ima_queue_data.c | 190 +++++++++++++++++++
security/integrity/ima/ima_queue_keys.c | 174 -----------------
9 files changed, 238 insertions(+), 202 deletions(-)
create mode 100644 security/integrity/ima/ima_queue_data.c
delete mode 100644 security/integrity/ima/ima_queue_keys.c
diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig
index 080c53545ff0..e4fb1761d64a 100644
--- a/security/integrity/ima/Kconfig
+++ b/security/integrity/ima/Kconfig
@@ -322,10 +322,9 @@ config IMA_MEASURE_ASYMMETRIC_KEYS
depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y
default y
-config IMA_QUEUE_EARLY_BOOT_KEYS
+config IMA_QUEUE_EARLY_BOOT_DATA
bool
- depends on IMA_MEASURE_ASYMMETRIC_KEYS
- depends on SYSTEM_TRUSTED_KEYRING
+ depends on SECURITY_SELINUX || (IMA_MEASURE_ASYMMETRIC_KEYS && SYSTEM_TRUSTED_KEYRING)
default y
config IMA_SECURE_AND_OR_TRUSTED_BOOT
diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile
index 67dabca670e2..cbbbc9848d2f 100644
--- a/security/integrity/ima/Makefile
+++ b/security/integrity/ima/Makefile
@@ -13,4 +13,4 @@ ima-$(CONFIG_IMA_APPRAISE_MODSIG) += ima_modsig.o
ima-$(CONFIG_HAVE_IMA_KEXEC) += ima_kexec.o
ima-$(CONFIG_IMA_BLACKLIST_KEYRING) += ima_mok.o
ima-$(CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS) += ima_asymmetric_keys.o
-ima-$(CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS) += ima_queue_keys.o
+ima-$(CONFIG_IMA_QUEUE_EARLY_BOOT_DATA) += ima_queue_data.o
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 8ed9f5e1dd40..ebe4d9bb2f2b 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -229,29 +229,34 @@ extern const char *const func_tokens[];
struct modsig;
-#ifdef CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS
+#ifdef CONFIG_IMA_QUEUE_EARLY_BOOT_DATA
/*
- * To track keys that need to be measured.
+ * To track data that needs to be measured.
*/
-struct ima_key_entry {
+struct ima_data_entry {
struct list_head list;
void *payload;
size_t payload_len;
- char *keyring_name;
+ const char *event_name;
+ const char *event_data;
+ enum ima_hooks func;
};
-void ima_init_key_queue(void);
-bool ima_should_queue_key(void);
-bool ima_queue_key(struct key *keyring, const void *payload,
- size_t payload_len);
-void ima_process_queued_keys(void);
+void ima_init_data_queue(void);
+bool ima_should_queue_data(void);
+bool ima_queue_data(const char *event_name, const void *payload,
+ size_t payload_len, const char *event_data,
+ enum ima_hooks func);
+void ima_process_queued_data(void);
#else
-static inline void ima_init_key_queue(void) {}
-static inline bool ima_should_queue_key(void) { return false; }
-static inline bool ima_queue_key(struct key *keyring,
- const void *payload,
- size_t payload_len) { return false; }
-static inline void ima_process_queued_keys(void) {}
-#endif /* CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS */
+static inline void ima_init_data_queue(void) {}
+static inline bool ima_should_queue_data(void) { return false; }
+static inline bool ima_queue_data(const char *event_name,
+ const void *payload,
+ size_t payload_len,
+ const char *event_data,
+ enum ima_hooks func) { return false; }
+static inline void ima_process_queued_data(void) {}
+#endif /* CONFIG_IMA_QUEUE_EARLY_BOOT_DATA */
/* LIM API function definitions */
int ima_get_action(struct inode *inode, const struct cred *cred, u32 secid,
diff --git a/security/integrity/ima/ima_asymmetric_keys.c b/security/integrity/ima/ima_asymmetric_keys.c
index 1c68c500c26f..8f8431f8b096 100644
--- a/security/integrity/ima/ima_asymmetric_keys.c
+++ b/security/integrity/ima/ima_asymmetric_keys.c
@@ -37,8 +37,10 @@ void ima_post_key_create_or_update(struct key *keyring, struct key *key,
if (!payload || (payload_len == 0))
return;
- if (ima_should_queue_key())
- queued = ima_queue_key(keyring, payload, payload_len);
+ if (ima_should_queue_data())
+ queued = ima_queue_data(keyring->description, payload,
+ payload_len, keyring->description,
+ KEY_CHECK);
if (queued)
return;
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index 4902fe7bd570..892894bf4af3 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -145,7 +145,7 @@ int __init ima_init(void)
if (rc != 0)
return rc;
- ima_init_key_queue();
+ ima_init_data_queue();
return rc;
}
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 74d421e40c8f..1c4e140964df 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -846,6 +846,22 @@ void ima_kexec_cmdline(int kernel_fd, const void *buf, int size)
fdput(f);
}
+static int ima_measure_lsm_data(const char *event_name,
+ const void *buf, int size,
+ enum ima_hooks func)
+{
+ bool queued = false;
+
+ if (ima_should_queue_data())
+ queued = ima_queue_data(event_name, buf, size, NULL, func);
+
+ if (queued)
+ return 0;
+
+ return process_buffer_measurement(NULL, buf, size, event_name, func,
+ 0, NULL);
+}
+
/**
* ima_measure_lsm_state - measure LSM specific state
* @lsm_event_name: LSM event
@@ -860,8 +876,7 @@ int ima_measure_lsm_state(const char *lsm_event_name, const void *buf,
if (!lsm_event_name || !buf || !size)
return -EINVAL;
- return process_buffer_measurement(NULL, buf, size, lsm_event_name,
- LSM_STATE, 0, NULL);
+ return ima_measure_lsm_data(lsm_event_name, buf, size, LSM_STATE);
}
/**
@@ -878,8 +893,7 @@ int ima_measure_lsm_policy(const char *lsm_event_name, const void *buf,
if (!lsm_event_name || !buf || !size)
return -EINVAL;
- return process_buffer_measurement(NULL, buf, size, lsm_event_name,
- LSM_POLICY, 0, NULL);
+ return ima_measure_lsm_data(lsm_event_name, buf, size, LSM_POLICY);
}
static int __init init_ima(void)
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index e4de581442d5..196c427a79d1 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -837,7 +837,7 @@ void ima_update_policy(void)
ima_update_policy_flag();
/* Custom IMA policy has been loaded */
- ima_process_queued_keys();
+ ima_process_queued_data();
}
/* Keep the enumeration in sync with the policy_tokens! */
diff --git a/security/integrity/ima/ima_queue_data.c b/security/integrity/ima/ima_queue_data.c
new file mode 100644
index 000000000000..93420e7670b9
--- /dev/null
+++ b/security/integrity/ima/ima_queue_data.c
@@ -0,0 +1,190 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019 Microsoft Corporation
+ *
+ * Author: Lakshmi Ramasubramanian (nramas@linux.microsoft.com)
+ *
+ * File: ima_queue_data.c
+ * Enables deferred processing of data to be measured
+ */
+
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/workqueue.h>
+#include "ima.h"
+
+/*
+ * Flag to indicate whether data can be processed
+ * right away or should be queued for processing later.
+ */
+static bool ima_process_data;
+
+/*
+ * To synchronize access to the list of data that need to be measured
+ */
+static DEFINE_MUTEX(ima_data_lock);
+static LIST_HEAD(ima_queued_data);
+
+/*
+ * If custom IMA policy is not loaded then data queued up
+ * for measurement should be freed. This worker is used
+ * for handling this scenario.
+ */
+static long ima_data_queue_timeout = 300000; /* 5 Minutes */
+static void ima_data_handler(struct work_struct *work);
+static DECLARE_DELAYED_WORK(ima_data_delayed_work, ima_data_handler);
+static bool timer_expired;
+
+/*
+ * This worker function frees data that may still be
+ * queued up in case custom IMA policy was not loaded.
+ */
+static void ima_data_handler(struct work_struct *work)
+{
+ timer_expired = true;
+ ima_process_queued_data();
+}
+
+/*
+ * This function sets up a worker to free queued data in case
+ * custom IMA policy was never loaded.
+ */
+void ima_init_data_queue(void)
+{
+ schedule_delayed_work(&ima_data_delayed_work,
+ msecs_to_jiffies(ima_data_queue_timeout));
+}
+
+static void ima_free_data_entry(struct ima_data_entry *entry)
+{
+ if (!entry)
+ return;
+
+ kvfree(entry->payload);
+ kfree(entry->event_name);
+ kfree(entry->event_data);
+ kfree(entry);
+}
+
+static void *ima_kvmemdup(const void *src, size_t len)
+{
+ void *p = kvmalloc(len, GFP_KERNEL);
+
+ if (p)
+ memcpy(p, src, len);
+ return p;
+}
+
+static struct ima_data_entry *ima_alloc_data_entry(const char *event_name,
+ const void *payload,
+ size_t payload_len,
+ const char *event_data,
+ enum ima_hooks func)
+{
+ struct ima_data_entry *entry;
+
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ goto out;
+
+ /*
+ * Payload size may exceed the limit supported by kmalloc.
+ * So use kvmalloc instead.
+ */
+ entry->payload = ima_kvmemdup(payload, payload_len);
+ entry->event_name = kstrdup(event_name, GFP_KERNEL);
+ if (event_data)
+ entry->event_data = kstrdup(event_data, GFP_KERNEL);
+
+ entry->payload_len = payload_len;
+ entry->func = func;
+
+ if (!entry->payload || !entry->event_name ||
+ (event_data && !entry->event_data))
+ goto out;
+
+ INIT_LIST_HEAD(&entry->list);
+ return entry;
+
+out:
+ integrity_audit_message(AUDIT_INTEGRITY_PCR, NULL,
+ event_name, func_measure_str(func),
+ "ENOMEM", -ENOMEM, 0, -ENOMEM);
+ ima_free_data_entry(entry);
+ return NULL;
+}
+
+bool ima_queue_data(const char *event_name, const void *payload,
+ size_t payload_len, const char *event_data,
+ enum ima_hooks func)
+{
+ bool queued = false;
+ struct ima_data_entry *entry;
+
+ entry = ima_alloc_data_entry(event_name, payload, payload_len,
+ event_data, func);
+ if (!entry)
+ return false;
+
+ mutex_lock(&ima_data_lock);
+ if (!ima_process_data) {
+ list_add_tail(&entry->list, &ima_queued_data);
+ queued = true;
+ }
+ mutex_unlock(&ima_data_lock);
+
+ if (!queued)
+ ima_free_data_entry(entry);
+
+ return queued;
+}
+
+/*
+ * ima_process_queued_data() - process data queued for measurement
+ *
+ * This function sets ima_process_data to true and processes queued data.
+ * From here on data will be processed right away (not queued).
+ */
+void ima_process_queued_data(void)
+{
+ struct ima_data_entry *entry, *tmp;
+ bool process = false;
+
+ if (ima_process_data)
+ return;
+
+ /*
+ * Since ima_process_data is set to true, any new data will be
+ * processed immediately and not be queued to ima_queued_data list.
+ * First one setting the ima_process_data flag to true will
+ * process the queued data.
+ */
+ mutex_lock(&ima_data_lock);
+ if (!ima_process_data) {
+ ima_process_data = true;
+ process = true;
+ }
+ mutex_unlock(&ima_data_lock);
+
+ if (!process)
+ return;
+
+ if (!timer_expired)
+ cancel_delayed_work_sync(&ima_data_delayed_work);
+
+ list_for_each_entry_safe(entry, tmp, &ima_queued_data, list) {
+ if (!timer_expired)
+ process_buffer_measurement(NULL, entry->payload,
+ entry->payload_len,
+ entry->event_name,
+ entry->func, 0,
+ entry->event_data);
+ list_del(&entry->list);
+ ima_free_data_entry(entry);
+ }
+}
+
+inline bool ima_should_queue_data(void)
+{
+ return !ima_process_data;
+}
diff --git a/security/integrity/ima/ima_queue_keys.c b/security/integrity/ima/ima_queue_keys.c
deleted file mode 100644
index 69a8626a35c0..000000000000
--- a/security/integrity/ima/ima_queue_keys.c
+++ /dev/null
@@ -1,174 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Copyright (C) 2019 Microsoft Corporation
- *
- * Author: Lakshmi Ramasubramanian (nramas@linux.microsoft.com)
- *
- * File: ima_queue_keys.c
- * Enables deferred processing of keys
- */
-
-#include <linux/workqueue.h>
-#include <keys/asymmetric-type.h>
-#include "ima.h"
-
-/*
- * Flag to indicate whether a key can be processed
- * right away or should be queued for processing later.
- */
-static bool ima_process_keys;
-
-/*
- * To synchronize access to the list of keys that need to be measured
- */
-static DEFINE_MUTEX(ima_keys_lock);
-static LIST_HEAD(ima_keys);
-
-/*
- * If custom IMA policy is not loaded then keys queued up
- * for measurement should be freed. This worker is used
- * for handling this scenario.
- */
-static long ima_key_queue_timeout = 300000; /* 5 Minutes */
-static void ima_keys_handler(struct work_struct *work);
-static DECLARE_DELAYED_WORK(ima_keys_delayed_work, ima_keys_handler);
-static bool timer_expired;
-
-/*
- * This worker function frees keys that may still be
- * queued up in case custom IMA policy was not loaded.
- */
-static void ima_keys_handler(struct work_struct *work)
-{
- timer_expired = true;
- ima_process_queued_keys();
-}
-
-/*
- * This function sets up a worker to free queued keys in case
- * custom IMA policy was never loaded.
- */
-void ima_init_key_queue(void)
-{
- schedule_delayed_work(&ima_keys_delayed_work,
- msecs_to_jiffies(ima_key_queue_timeout));
-}
-
-static void ima_free_key_entry(struct ima_key_entry *entry)
-{
- if (entry) {
- kfree(entry->payload);
- kfree(entry->keyring_name);
- kfree(entry);
- }
-}
-
-static struct ima_key_entry *ima_alloc_key_entry(struct key *keyring,
- const void *payload,
- size_t payload_len)
-{
- int rc = 0;
- const char *audit_cause = "ENOMEM";
- struct ima_key_entry *entry;
-
- entry = kzalloc(sizeof(*entry), GFP_KERNEL);
- if (entry) {
- entry->payload = kmemdup(payload, payload_len, GFP_KERNEL);
- entry->keyring_name = kstrdup(keyring->description,
- GFP_KERNEL);
- entry->payload_len = payload_len;
- }
-
- if ((entry == NULL) || (entry->payload == NULL) ||
- (entry->keyring_name == NULL)) {
- rc = -ENOMEM;
- goto out;
- }
-
- INIT_LIST_HEAD(&entry->list);
-
-out:
- if (rc) {
- integrity_audit_message(AUDIT_INTEGRITY_PCR, NULL,
- keyring->description,
- func_measure_str(KEY_CHECK),
- audit_cause, rc, 0, rc);
- ima_free_key_entry(entry);
- entry = NULL;
- }
-
- return entry;
-}
-
-bool ima_queue_key(struct key *keyring, const void *payload,
- size_t payload_len)
-{
- bool queued = false;
- struct ima_key_entry *entry;
-
- entry = ima_alloc_key_entry(keyring, payload, payload_len);
- if (!entry)
- return false;
-
- mutex_lock(&ima_keys_lock);
- if (!ima_process_keys) {
- list_add_tail(&entry->list, &ima_keys);
- queued = true;
- }
- mutex_unlock(&ima_keys_lock);
-
- if (!queued)
- ima_free_key_entry(entry);
-
- return queued;
-}
-
-/*
- * ima_process_queued_keys() - process keys queued for measurement
- *
- * This function sets ima_process_keys to true and processes queued keys.
- * From here on keys will be processed right away (not queued).
- */
-void ima_process_queued_keys(void)
-{
- struct ima_key_entry *entry, *tmp;
- bool process = false;
-
- if (ima_process_keys)
- return;
-
- /*
- * Since ima_process_keys is set to true, any new key will be
- * processed immediately and not be queued to ima_keys list.
- * First one setting the ima_process_keys flag to true will
- * process the queued keys.
- */
- mutex_lock(&ima_keys_lock);
- if (!ima_process_keys) {
- ima_process_keys = true;
- process = true;
- }
- mutex_unlock(&ima_keys_lock);
-
- if (!process)
- return;
-
- if (!timer_expired)
- cancel_delayed_work_sync(&ima_keys_delayed_work);
-
- list_for_each_entry_safe(entry, tmp, &ima_keys, list) {
- if (!timer_expired)
- process_buffer_measurement(NULL, entry->payload,
- entry->payload_len,
- entry->keyring_name,
- KEY_CHECK, 0,
- entry->keyring_name);
- list_del(&entry->list);
- ima_free_key_entry(entry);
- }
-}
-
-inline bool ima_should_queue_key(void)
-{
- return !ima_process_keys;
-}
--
2.27.0
^ permalink raw reply related
* [PATCH v6 3/4] LSM: Define SELinux function to measure state and policy
From: Lakshmi Ramasubramanian @ 2020-08-05 0:43 UTC (permalink / raw)
To: zohar, stephen.smalley.work, casey
Cc: tyhicks, sashal, jmorris, linux-integrity, selinux,
linux-security-module, linux-kernel
In-Reply-To: <20200805004331.20652-1-nramas@linux.microsoft.com>
SELinux configuration and policy are some of the critical data for this
security module that needs to be measured. This measurement can be used
by an attestation service, for instance, to verify if the configuration
and policies have been setup correctly and that they haven't been tampered
with at runtime.
Measure SELinux configuration, policy capabilities settings, and the
loaded policy by calling the IMA hooks ima_measure_lsm_state() and
ima_measure_lsm_policy() respectively.
Sample measurement of SELinux state and hash of the policy:
10 e32e...5ac3 ima-buf sha256:86e8...4594 selinux-state-1595389364:287899386 696e697469616c697a65643d313b656e61626c65643d313b656e666f7263696e673d303b636865636b72657170726f743d313b6e6574776f726b5f706565725f636f6e74726f6c733d313b6f70656e5f7065726d733d313b657874656e6465645f736f636b65745f636c6173733d313b616c776179735f636865636b5f6e6574776f726b3d303b6367726f75705f7365636c6162656c3d313b6e6e705f6e6f737569645f7472616e736974696f6e3d313b67656e66735f7365636c6162656c5f73796d6c696e6b733d303
10 f4a7...9408 ima-ng sha256:8d1d...1834 selinux-policy-hash-1595389353:863934271
To verify the measurement check the following:
Execute the following command to extract the measured data
from the IMA log for SELinux configuration (selinux-state).
grep -m 1 "selinux-state" /sys/kernel/security/integrity/ima/ascii_runtime_measurements | cut -d' ' -f 6 | xxd -r -p
The output should be the list of key-value pairs. For example,
initialized=1;enabled=1;enforcing=0;checkreqprot=1;network_peer_controls=1;open_perms=1;extended_socket_class=1;always_check_network=0;cgroup_seclabel=1;nnp_nosuid_transition=1;genfs_seclabel_symlinks=0;
To verify the measured data with the current SELinux state:
=> enabled should be set to 1 if /sys/fs/selinux folder exists,
0 otherwise
For other entries, compare the integer value in the files
=> /sys/fs/selinux/enforce
=> /sys/fs/selinux/checkreqprot
And, each of the policy capabilities files under
=> /sys/fs/selinux/policy_capabilities
For selinux-policy-hash, the hash of SELinux policy is included
in the IMA log entry.
To verify the measured data with the current SELinux policy run
the following commands and verify the output hash values match.
sha256sum /sys/fs/selinux/policy | cut -d' ' -f 1
grep -m 1 "selinux-policy-hash" /sys/kernel/security/integrity/ima/ascii_runtime_measurements | cut -d' ' -f 4
Signed-off-by: Lakshmi Ramasubramanian <nramas@linux.microsoft.com>
Suggested-by: Stephen Smalley <stephen.smalley.work@gmail.com>
Reported-by: kernel test robot <lkp@intel.com> # error: implicit declaration of function 'vfree'
Reported-by: kernel test robot <lkp@intel.com> # error: implicit declaration of function 'crypto_alloc_shash'
Reported-by: kernel test robot <lkp@intel.com> # sparse: symbol 'security_read_selinux_policy' was not declared. Should it be static?
---
security/selinux/Makefile | 2 +
security/selinux/hooks.c | 1 +
security/selinux/include/security.h | 15 +++
security/selinux/measure.c | 150 ++++++++++++++++++++++++++++
security/selinux/selinuxfs.c | 3 +
security/selinux/ss/services.c | 71 +++++++++++--
6 files changed, 233 insertions(+), 9 deletions(-)
create mode 100644 security/selinux/measure.c
diff --git a/security/selinux/Makefile b/security/selinux/Makefile
index 4d8e0e8adf0b..83d512116341 100644
--- a/security/selinux/Makefile
+++ b/security/selinux/Makefile
@@ -16,6 +16,8 @@ selinux-$(CONFIG_NETLABEL) += netlabel.o
selinux-$(CONFIG_SECURITY_INFINIBAND) += ibpkey.o
+selinux-$(CONFIG_IMA) += measure.o
+
ccflags-y := -I$(srctree)/security/selinux -I$(srctree)/security/selinux/include
$(addprefix $(obj)/,$(selinux-y)): $(obj)/flask.h
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index efa6108b1ce9..5521dfc1900b 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -7394,6 +7394,7 @@ int selinux_disable(struct selinux_state *state)
}
selinux_mark_disabled(state);
+ selinux_measure_state(state);
pr_info("SELinux: Disabled at runtime.\n");
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index b0e02cfe3ce1..77f42d9b544b 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -222,16 +222,31 @@ static inline bool selinux_policycap_genfs_seclabel_symlinks(void)
return state->policycap[POLICYDB_CAPABILITY_GENFS_SECLABEL_SYMLINKS];
}
+static inline bool selinux_checkreqprot(const struct selinux_state *state)
+{
+ return READ_ONCE(state->checkreqprot);
+}
+
int security_mls_enabled(struct selinux_state *state);
int security_load_policy(struct selinux_state *state,
void *data, size_t len);
int security_read_policy(struct selinux_state *state,
void **data, size_t *len);
+int security_read_policy_kernel(struct selinux_state *state,
+ void **data, size_t *len);
size_t security_policydb_len(struct selinux_state *state);
int security_policycap_supported(struct selinux_state *state,
unsigned int req_cap);
+#ifdef CONFIG_IMA
+extern void selinux_measure_state(struct selinux_state *selinux_state);
+#else
+static inline void selinux_measure_state(struct selinux_state *selinux_state)
+{
+}
+#endif
+
#define SEL_VEC_MAX 32
struct av_decision {
u32 allowed;
diff --git a/security/selinux/measure.c b/security/selinux/measure.c
new file mode 100644
index 000000000000..1583628d09d1
--- /dev/null
+++ b/security/selinux/measure.c
@@ -0,0 +1,150 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Measure SELinux state using IMA subsystem.
+ */
+#include <linux/vmalloc.h>
+#include <linux/ktime.h>
+#include <linux/ima.h>
+#include "security.h"
+
+/*
+ * This function creates an unique name by appending the timestamp to
+ * the given string. This string is passed as "event name" to the IMA
+ * hook to measure the given SELinux data.
+ *
+ * The data provided by SELinux to the IMA subsystem for measuring may have
+ * already been measured (for instance the same state existed earlier).
+ * But for SELinux the current data represents a state change and hence
+ * needs to be measured again. To enable this, pass an unique "Event Name"
+ * to the IMA hook so that IMA subsystem will always measure the given data.
+ *
+ * For example,
+ * At time T0 SELinux data to be measured is "foo". IMA measures it.
+ * At time T1 the data is changed to "bar". IMA measures it.
+ * At time T2 the data is changed to "foo" again. IMA will not measure it
+ * (since it was already measured) unless the event name, for instance,
+ * is different in this call.
+ */
+static char *selinux_event_name(const char *name_prefix)
+{
+ char *event_name = NULL;
+ struct timespec64 curr_time;
+ int count;
+
+ ktime_get_real_ts64(&curr_time);
+ count = snprintf(NULL, 0, "%s-%lld:%09ld", name_prefix,
+ curr_time.tv_sec, curr_time.tv_nsec);
+ count++;
+ event_name = kzalloc(count, GFP_KERNEL);
+ if (!event_name) {
+ pr_warn("%s: event name not allocated.\n", __func__);
+ return NULL;
+ }
+
+ snprintf(event_name, count, "%s-%lld:%09ld", name_prefix,
+ curr_time.tv_sec, curr_time.tv_nsec);
+
+ return event_name;
+}
+
+static int read_selinux_state(char **state_str, int *state_str_len,
+ struct selinux_state *state)
+{
+ char *buf, *str_fmt = "%s=%d;";
+ int i, buf_len, curr;
+
+ buf_len = snprintf(NULL, 0, str_fmt, "initialized", 0);
+ buf_len += snprintf(NULL, 0, str_fmt, "enabled", 0);
+ buf_len += snprintf(NULL, 0, str_fmt, "enforcing", 0);
+ buf_len += snprintf(NULL, 0, str_fmt, "checkreqprot", 0);
+
+ for (i = 0; i < __POLICYDB_CAPABILITY_MAX; i++) {
+ buf_len += snprintf(NULL, 0, str_fmt,
+ selinux_policycap_names[i], 0);
+ }
+ ++buf_len;
+
+ buf = kzalloc(buf_len, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ curr = snprintf(buf, buf_len, str_fmt,
+ "initialized", selinux_initialized(state));
+ curr += snprintf((buf + curr), (buf_len - curr), str_fmt,
+ "enabled", !selinux_disabled(state));
+ curr += snprintf((buf + curr), (buf_len - curr), str_fmt,
+ "enforcing", enforcing_enabled(state));
+ curr += snprintf((buf + curr), (buf_len - curr), str_fmt,
+ "checkreqprot", selinux_checkreqprot(state));
+
+ for (i = 0; i < __POLICYDB_CAPABILITY_MAX; i++) {
+ curr += snprintf((buf + curr), (buf_len - curr), str_fmt,
+ selinux_policycap_names[i],
+ state->policycap[i]);
+ }
+
+ *state_str = buf;
+ *state_str_len = curr;
+
+ return 0;
+}
+
+void selinux_measure_state(struct selinux_state *state)
+{
+ void *policy = NULL;
+ char *event_name = NULL;
+ char *state_str = NULL;
+ size_t policy_len;
+ int state_str_len, rc = 0;
+ bool initialized = selinux_initialized(state);
+
+ rc = read_selinux_state(&state_str, &state_str_len, state);
+ if (rc) {
+ pr_warn("%s: Failed to read selinux state.\n", __func__);
+ return;
+ }
+
+ /*
+ * Get an unique string for measuring the current SELinux state.
+ */
+ event_name = selinux_event_name("selinux-state");
+ if (!event_name) {
+ pr_warn("%s: Event name for state not allocated.\n",
+ __func__);
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ rc = ima_measure_lsm_state(event_name, state_str, state_str_len);
+
+ kfree(event_name);
+ event_name = NULL;
+
+ if (rc)
+ goto out;
+
+ /*
+ * Measure SELinux policy only after initialization is completed.
+ */
+ if (!initialized)
+ goto out;
+
+ rc = security_read_policy_kernel(state, &policy, &policy_len);
+ if (rc)
+ goto out;
+
+ event_name = selinux_event_name("selinux-policy-hash");
+ if (!event_name) {
+ pr_warn("%s: Event name for policy not allocated.\n",
+ __func__);
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ rc = ima_measure_lsm_policy(event_name, policy, policy_len);
+
+out:
+ kfree(event_name);
+ kfree(state_str);
+ vfree(policy);
+}
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
index 4781314c2510..6d46eaef5c92 100644
--- a/security/selinux/selinuxfs.c
+++ b/security/selinux/selinuxfs.c
@@ -173,6 +173,7 @@ static ssize_t sel_write_enforce(struct file *file, const char __user *buf,
from_kuid(&init_user_ns, audit_get_loginuid(current)),
audit_get_sessionid(current));
enforcing_set(state, new_value);
+ selinux_measure_state(state);
if (new_value)
avc_ss_reset(state->avc, 0);
selnl_notify_setenforce(new_value);
@@ -678,6 +679,8 @@ static ssize_t sel_write_checkreqprot(struct file *file, const char __user *buf,
fsi->state->checkreqprot = new_value ? 1 : 0;
length = count;
+
+ selinux_measure_state(fsi->state);
out:
kfree(page);
return length;
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index ef0afd878bfc..3978c804c361 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -2182,6 +2182,7 @@ int security_load_policy(struct selinux_state *state, void *data, size_t len)
selinux_status_update_policyload(state, seqno);
selinux_netlbl_cache_invalidate();
selinux_xfrm_notify_policyload();
+ selinux_measure_state(state);
return 0;
}
@@ -2270,6 +2271,7 @@ int security_load_policy(struct selinux_state *state, void *data, size_t len)
selinux_status_update_policyload(state, seqno);
selinux_netlbl_cache_invalidate();
selinux_xfrm_notify_policyload();
+ selinux_measure_state(state);
rc = 0;
goto out;
@@ -2941,6 +2943,7 @@ int security_set_bools(struct selinux_state *state, u32 len, int *values)
selnl_notify_policyload(seqno);
selinux_status_update_policyload(state, seqno);
selinux_xfrm_notify_policyload();
+ selinux_measure_state(state);
}
return rc;
}
@@ -3720,14 +3723,23 @@ int security_netlbl_sid_to_secattr(struct selinux_state *state,
}
#endif /* CONFIG_NETLABEL */
+static int security_read_policy_len(struct selinux_state *state, size_t *len)
+{
+ if (!selinux_initialized(state))
+ return -EINVAL;
+
+ *len = security_policydb_len(state);
+ return 0;
+}
+
/**
- * security_read_policy - read the policy.
+ * security_read_selinux_policy - read the policy.
* @data: binary policy data
* @len: length of data in bytes
*
*/
-int security_read_policy(struct selinux_state *state,
- void **data, size_t *len)
+static int security_read_selinux_policy(struct selinux_state *state,
+ void **data, size_t *len)
{
struct policydb *policydb = &state->ss->policydb;
int rc;
@@ -3736,12 +3748,6 @@ int security_read_policy(struct selinux_state *state,
if (!selinux_initialized(state))
return -EINVAL;
- *len = security_policydb_len(state);
-
- *data = vmalloc_user(*len);
- if (!*data)
- return -ENOMEM;
-
fp.data = *data;
fp.len = *len;
@@ -3754,5 +3760,52 @@ int security_read_policy(struct selinux_state *state,
*len = (unsigned long)fp.data - (unsigned long)*data;
return 0;
+}
+
+/**
+ * security_read_policy - read the policy.
+ * @data: binary policy data
+ * @len: length of data in bytes
+ *
+ */
+int security_read_policy(struct selinux_state *state,
+ void **data, size_t *len)
+{
+ int rc;
+
+ rc = security_read_policy_len(state, len);
+ if (rc)
+ return rc;
+
+ *data = vmalloc_user(*len);
+ if (!*data)
+ return -ENOMEM;
+
+ return security_read_selinux_policy(state, data, len);
+}
+
+/**
+ * security_read_policy_kernel - read the policy.
+ * @data: binary policy data
+ * @len: length of data in bytes
+ *
+ * Allocates kernel memory for reading SELinux policy.
+ * This function is for internal use only and should not
+ * be used for returning data to user space
+ *
+ */
+int security_read_policy_kernel(struct selinux_state *state,
+ void **data, size_t *len)
+{
+ int rc;
+
+ rc = security_read_policy_len(state, len);
+ if (rc)
+ return rc;
+
+ *data = vmalloc(*len);
+ if (!*data)
+ return -ENOMEM;
+ return security_read_selinux_policy(state, data, len);
}
--
2.27.0
^ permalink raw reply related
* [PATCH v6 2/4] IMA: Define IMA hooks to measure LSM state and policy
From: Lakshmi Ramasubramanian @ 2020-08-05 0:43 UTC (permalink / raw)
To: zohar, stephen.smalley.work, casey
Cc: tyhicks, sashal, jmorris, linux-integrity, selinux,
linux-security-module, linux-kernel
In-Reply-To: <20200805004331.20652-1-nramas@linux.microsoft.com>
IMA subsystem needs to define IMA hooks that the security modules can
call to measure state and policy data.
Define two new IMA hooks, namely ima_lsm_state() and ima_lsm_policy(),
that the security modules can call to measure LSM state and LSM policy
respectively. Return the status of the measurement operation from these
two IMA hooks.
Signed-off-by: Lakshmi Ramasubramanian <nramas@linux.microsoft.com>
Reviewed-by: Tyler Hicks <tyhicks@linux.microsoft.com>
---
include/linux/ima.h | 14 +++++++++
security/integrity/ima/ima.h | 6 ++--
security/integrity/ima/ima_main.c | 50 ++++++++++++++++++++++++++-----
3 files changed, 60 insertions(+), 10 deletions(-)
diff --git a/include/linux/ima.h b/include/linux/ima.h
index d15100de6cdd..442ca0dce3c8 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -26,6 +26,10 @@ extern int ima_post_read_file(struct file *file, void *buf, loff_t size,
extern void ima_post_path_mknod(struct dentry *dentry);
extern int ima_file_hash(struct file *file, char *buf, size_t buf_size);
extern void ima_kexec_cmdline(int kernel_fd, const void *buf, int size);
+extern int ima_measure_lsm_state(const char *lsm_event_name, const void *buf,
+ int size);
+extern int ima_measure_lsm_policy(const char *lsm_event_name, const void *buf,
+ int size);
#ifdef CONFIG_IMA_KEXEC
extern void ima_add_kexec_buffer(struct kimage *image);
@@ -104,6 +108,16 @@ static inline int ima_file_hash(struct file *file, char *buf, size_t buf_size)
}
static inline void ima_kexec_cmdline(int kernel_fd, const void *buf, int size) {}
+static inline int ima_measure_lsm_state(const char *lsm_event_name,
+ const void *buf, int size)
+{
+ return -EOPNOTSUPP;
+}
+static inline int ima_measure_lsm_policy(const char *lsm_event_name,
+ const void *buf, int size)
+{
+ return -EOPNOTSUPP;
+}
#endif /* CONFIG_IMA */
#ifndef CONFIG_IMA_KEXEC
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 1b5f4b2f17d0..8ed9f5e1dd40 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -267,9 +267,9 @@ void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file,
struct evm_ima_xattr_data *xattr_value,
int xattr_len, const struct modsig *modsig, int pcr,
struct ima_template_desc *template_desc);
-void process_buffer_measurement(struct inode *inode, const void *buf, int size,
- const char *eventname, enum ima_hooks func,
- int pcr, const char *keyring);
+int process_buffer_measurement(struct inode *inode, const void *buf, int size,
+ const char *eventname, enum ima_hooks func,
+ int pcr, const char *keyring);
void ima_audit_measurement(struct integrity_iint_cache *iint,
const unsigned char *filename);
int ima_alloc_init_template(struct ima_event_data *event_data,
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 8a91711ca79b..74d421e40c8f 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -736,9 +736,9 @@ int ima_load_data(enum kernel_load_data_id id)
*
* Based on policy, the buffer is measured into the ima log.
*/
-void process_buffer_measurement(struct inode *inode, const void *buf, int size,
- const char *eventname, enum ima_hooks func,
- int pcr, const char *keyring)
+int process_buffer_measurement(struct inode *inode, const void *buf, int size,
+ const char *eventname, enum ima_hooks func,
+ int pcr, const char *keyring)
{
int ret = 0;
const char *audit_cause = "ENOMEM";
@@ -758,7 +758,7 @@ void process_buffer_measurement(struct inode *inode, const void *buf, int size,
u32 secid;
if (!ima_policy_flag)
- return;
+ return 0;
/*
* Both LSM hooks and auxilary based buffer measurements are
@@ -772,7 +772,7 @@ void process_buffer_measurement(struct inode *inode, const void *buf, int size,
action = ima_get_action(inode, current_cred(), secid, 0, func,
&pcr, &template, keyring);
if (!(action & IMA_MEASURE))
- return;
+ return 0;
}
if (!pcr)
@@ -787,7 +787,7 @@ void process_buffer_measurement(struct inode *inode, const void *buf, int size,
pr_err("template %s init failed, result: %d\n",
(strlen(template->name) ?
template->name : template->fmt), ret);
- return;
+ return ret;
}
}
@@ -819,7 +819,7 @@ void process_buffer_measurement(struct inode *inode, const void *buf, int size,
func_measure_str(func),
audit_cause, ret, 0, ret);
- return;
+ return ret;
}
/**
@@ -846,6 +846,42 @@ void ima_kexec_cmdline(int kernel_fd, const void *buf, int size)
fdput(f);
}
+/**
+ * ima_measure_lsm_state - measure LSM specific state
+ * @lsm_event_name: LSM event
+ * @buf: pointer to buffer containing LSM specific state
+ * @size: Number of bytes in buf
+ *
+ * Buffers can only be measured, not appraised.
+ */
+int ima_measure_lsm_state(const char *lsm_event_name, const void *buf,
+ int size)
+{
+ if (!lsm_event_name || !buf || !size)
+ return -EINVAL;
+
+ return process_buffer_measurement(NULL, buf, size, lsm_event_name,
+ LSM_STATE, 0, NULL);
+}
+
+/**
+ * ima_measure_lsm_policy - measure LSM specific policy
+ * @lsm_event_name: LSM event
+ * @buf: pointer to buffer containing LSM specific policy
+ * @size: Number of bytes in buf
+ *
+ * Buffers can only be measured, not appraised.
+ */
+int ima_measure_lsm_policy(const char *lsm_event_name, const void *buf,
+ int size)
+{
+ if (!lsm_event_name || !buf || !size)
+ return -EINVAL;
+
+ return process_buffer_measurement(NULL, buf, size, lsm_event_name,
+ LSM_POLICY, 0, NULL);
+}
+
static int __init init_ima(void)
{
int error;
--
2.27.0
^ permalink raw reply related
* Re: [PATCH 15/18] fsinfo: Add an attribute that lists all the visible mounts in a namespace [ver #21]
From: Ian Kent @ 2020-08-05 0:59 UTC (permalink / raw)
To: Miklos Szeredi, David Howells
Cc: viro, torvalds, mszeredi, christian, jannh, darrick.wong, kzak,
jlayton, linux-api, linux-fsdevel, linux-security-module,
linux-kernel
In-Reply-To: <20200804140558.GF32719@miu.piliscsaba.redhat.com>
On Tue, 2020-08-04 at 16:05 +0200, Miklos Szeredi wrote:
> On Mon, Aug 03, 2020 at 02:38:34PM +0100, David Howells wrote:
> > Add a filesystem attribute that exports a list of all the visible
> > mounts in
> > a namespace, given the caller's chroot setting. The returned list
> > is an
> > array of:
> >
> > struct fsinfo_mount_child {
> > __u64 mnt_unique_id;
> > __u32 mnt_id;
> > __u32 parent_id;
> > __u32 mnt_notify_sum;
> > __u32 sb_notify_sum;
> > };
> >
> > where each element contains a once-in-a-system-lifetime unique ID,
> > the
> > mount ID (which may get reused), the parent mount ID and sums of
> > the
> > notification/change counters for the mount and its superblock.
>
> The change counters are currently conditional on
> CONFIG_MOUNT_NOTIFICATIONS.
> Is this is intentional?
>
> > This works with a read lock on the namespace_sem, but ideally would
> > do it
> > under the RCU read lock only.
> >
> > Signed-off-by: David Howells <dhowells@redhat.com>
> > ---
> >
> > fs/fsinfo.c | 1 +
> > fs/internal.h | 1 +
> > fs/namespace.c | 37
> > +++++++++++++++++++++++++++++++++++++
> > include/uapi/linux/fsinfo.h | 4 ++++
> > samples/vfs/test-fsinfo.c | 22 ++++++++++++++++++++++
> > 5 files changed, 65 insertions(+)
> >
> > diff --git a/fs/fsinfo.c b/fs/fsinfo.c
> > index 0540cce89555..f230124ffdf5 100644
> > --- a/fs/fsinfo.c
> > +++ b/fs/fsinfo.c
> > @@ -296,6 +296,7 @@ static const struct fsinfo_attribute
> > fsinfo_common_attributes[] = {
> > FSINFO_STRING (FSINFO_ATTR_MOUNT_POINT, fsinfo_gene
> > ric_mount_point),
> > FSINFO_STRING (FSINFO_ATTR_MOUNT_POINT_FULL, fsinfo_gene
> > ric_mount_point_full),
> > FSINFO_LIST (FSINFO_ATTR_MOUNT_CHILDREN, fsinfo_generic_moun
> > t_children),
> > + FSINFO_LIST (FSINFO_ATTR_MOUNT_ALL, fsinfo_generic_moun
> > t_all),
> > {}
> > };
> >
> > diff --git a/fs/internal.h b/fs/internal.h
> > index cb5edcc7125a..267b4aaf0271 100644
> > --- a/fs/internal.h
> > +++ b/fs/internal.h
> > @@ -102,6 +102,7 @@ extern int fsinfo_generic_mount_topology(struct
> > path *, struct fsinfo_context *)
> > extern int fsinfo_generic_mount_point(struct path *, struct
> > fsinfo_context *);
> > extern int fsinfo_generic_mount_point_full(struct path *, struct
> > fsinfo_context *);
> > extern int fsinfo_generic_mount_children(struct path *, struct
> > fsinfo_context *);
> > +extern int fsinfo_generic_mount_all(struct path *, struct
> > fsinfo_context *);
> >
> > /*
> > * fs_struct.c
> > diff --git a/fs/namespace.c b/fs/namespace.c
> > index 122c12f9512b..1f2e06507244 100644
> > --- a/fs/namespace.c
> > +++ b/fs/namespace.c
> > @@ -4494,4 +4494,41 @@ int fsinfo_generic_mount_children(struct
> > path *path, struct fsinfo_context *ctx)
> > return ctx->usage;
> > }
> >
> > +/*
> > + * Return information about all the mounts in the namespace
> > referenced by the
> > + * path.
> > + */
> > +int fsinfo_generic_mount_all(struct path *path, struct
> > fsinfo_context *ctx)
> > +{
> > + struct mnt_namespace *ns;
> > + struct mount *m, *p;
> > + struct path chroot;
> > + bool allow;
> > +
> > + m = real_mount(path->mnt);
> > + ns = m->mnt_ns;
> > +
> > + get_fs_root(current->fs, &chroot);
> > + rcu_read_lock();
> > + allow = are_paths_connected(&chroot, path) ||
> > capable(CAP_SYS_ADMIN);
> > + rcu_read_unlock();
> > + path_put(&chroot);
> > + if (!allow)
> > + return -EPERM;
> > +
> > + down_read(&namespace_sem);
> > +
> > + list_for_each_entry(p, &ns->list, mnt_list) {
>
> This is missing locking and check added by commit 9f6c61f96f2d
> ("proc/mounts:
> add cursor").
That's a good catch Miklos.
Yes, the extra lock and the cursor check that's now needed.
>
> > + struct path mnt_root;
> > +
> > + mnt_root.mnt = &p->mnt;
> > + mnt_root.dentry = p->mnt.mnt_root;
> > + if (are_paths_connected(path, &mnt_root))
> > + fsinfo_store_mount(ctx, p, p == m);
> > + }
> > +
> > + up_read(&namespace_sem);
> > + return ctx->usage;
> > +}
> > +
> > #endif /* CONFIG_FSINFO */
> > diff --git a/include/uapi/linux/fsinfo.h
> > b/include/uapi/linux/fsinfo.h
> > index 81329de6905e..e40192d98648 100644
> > --- a/include/uapi/linux/fsinfo.h
> > +++ b/include/uapi/linux/fsinfo.h
> > @@ -37,6 +37,7 @@
> > #define FSINFO_ATTR_MOUNT_POINT_FULL 0x203 /* Absolute
> > path of mount (string) */
> > #define FSINFO_ATTR_MOUNT_TOPOLOGY 0x204 /* Mount object
> > topology */
> > #define FSINFO_ATTR_MOUNT_CHILDREN 0x205 /* Children of this
> > mount (list) */
> > +#define FSINFO_ATTR_MOUNT_ALL 0x206 /* List all
> > mounts in a namespace (list) */
> >
> > #define FSINFO_ATTR_AFS_CELL_NAME 0x300 /* AFS cell name
> > (string) */
> > #define FSINFO_ATTR_AFS_SERVER_NAME 0x301 /* Name of
> > the Nth server (string) */
> > @@ -128,6 +129,8 @@ struct fsinfo_mount_topology {
> > /*
> > * Information struct element for
> > fsinfo(FSINFO_ATTR_MOUNT_CHILDREN).
> > * - An extra element is placed on the end representing the parent
> > mount.
> > + *
> > + * Information struct element for fsinfo(FSINFO_ATTR_MOUNT_ALL).
> > */
> > struct fsinfo_mount_child {
> > __u64 mnt_unique_id; /* Kernel-lifetime unique
> > mount ID */
> > @@ -139,6 +142,7 @@ struct fsinfo_mount_child {
> > };
> >
> > #define FSINFO_ATTR_MOUNT_CHILDREN__STRUCT struct
> > fsinfo_mount_child
> > +#define FSINFO_ATTR_MOUNT_ALL__STRUCT struct fsinfo_mount_child
> >
> > /*
> > * Information struct for fsinfo(FSINFO_ATTR_STATFS).
> > diff --git a/samples/vfs/test-fsinfo.c b/samples/vfs/test-fsinfo.c
> > index 374825ab85b0..596fa5e71762 100644
> > --- a/samples/vfs/test-fsinfo.c
> > +++ b/samples/vfs/test-fsinfo.c
> > @@ -365,6 +365,27 @@ static void
> > dump_fsinfo_generic_mount_children(void *reply, unsigned int size)
> > (unsigned long long)r->mnt_notify_sum, mp);
> > }
> >
> > +static void dump_fsinfo_generic_mount_all(void *reply, unsigned
> > int size)
> > +{
> > + struct fsinfo_mount_child *r = reply;
> > + ssize_t mplen;
> > + char path[32], *mp;
> > +
> > + struct fsinfo_params params = {
> > + .flags = FSINFO_FLAGS_QUERY_MOUNT,
> > + .request = FSINFO_ATTR_MOUNT_POINT_FULL,
> > + };
> > +
> > + sprintf(path, "%u", r->mnt_id);
> > + mplen = get_fsinfo(path, "FSINFO_ATTR_MOUNT_POINT_FULL",
> > ¶ms, (void **)&mp);
> > + if (mplen < 0)
> > + mp = "-";
> > +
> > + printf("%5x %5x %12llx %10llu %s\n",
> > + r->mnt_id, r->parent_id, (unsigned long long)r-
> > >mnt_unique_id,
> > + r->mnt_notify_sum, mp);
> > +}
> > +
> > static void dump_afs_fsinfo_server_address(void *reply, unsigned
> > int size)
> > {
> > struct fsinfo_afs_server_address *f = reply;
> > @@ -492,6 +513,7 @@ static const struct fsinfo_attribute
> > fsinfo_attributes[] = {
> > FSINFO_STRING_N (FSINFO_ATTR_MOUNT_POINT, string),
> > FSINFO_STRING_N (FSINFO_ATTR_MOUNT_POINT_FULL, string),
> > FSINFO_LIST (FSINFO_ATTR_MOUNT_CHILDREN, fsinfo_generic_moun
> > t_children),
> > + FSINFO_LIST (FSINFO_ATTR_MOUNT_ALL, fsinfo_generic_moun
> > t_all),
> >
> > FSINFO_STRING (FSINFO_ATTR_AFS_CELL_NAME, string),
> > FSINFO_STRING (FSINFO_ATTR_AFS_SERVER_NAME, string),
> >
> >
^ permalink raw reply
* Re: [PATCH v6 0/4] LSM: Measure security module data
From: Casey Schaufler @ 2020-08-05 1:04 UTC (permalink / raw)
To: Lakshmi Ramasubramanian, zohar, stephen.smalley.work
Cc: tyhicks, sashal, jmorris, linux-integrity, selinux,
linux-security-module, linux-kernel, Casey Schaufler
In-Reply-To: <20200805004331.20652-1-nramas@linux.microsoft.com>
On 8/4/2020 5:43 PM, Lakshmi Ramasubramanian wrote:
> Critical data structures of security modules are currently not measured.
> Therefore an attestation service, for instance, would not be able to
> attest whether the security modules are always operating with the policies
> and configuration that the system administrator had setup. The policies
> and configuration for the security modules could be tampered with by
> malware by exploiting kernel vulnerabilities or modified through some
> inadvertent actions on the system. Measuring such critical data would
> enable an attestation service to better assess the state of the system.
I still wonder why you're calling this an LSM change/feature when
all the change is in IMA and SELinux. You're not putting anything
into the LSM infrastructure, not are you using the LSM infrastructure
to achieve your ends. Sure, you *could* support other security modules
using this scheme, but you have a configuration dependency on
SELinux, so that's at best going to be messy. If you want this to
be an LSM "feature" you need to use the LSM hooking mechanism.
I'm not objecting to the feature. It adds value. But as you've
implemented it it is either an IMA extension to SELinux, or an
SELiux extension to IMA. Could AppArmor add hooks for this without
changing the IMA code? It doesn't look like it to me.
^ permalink raw reply
* Re: [PATCH v6 0/4] LSM: Measure security module data
From: Lakshmi Ramasubramanian @ 2020-08-05 1:14 UTC (permalink / raw)
To: Casey Schaufler, zohar, stephen.smalley.work
Cc: tyhicks, sashal, jmorris, linux-integrity, selinux,
linux-security-module, linux-kernel
In-Reply-To: <f3971f35-309d-c3e5-9126-69add7ad4c11@schaufler-ca.com>
On 8/4/20 6:04 PM, Casey Schaufler wrote:
> On 8/4/2020 5:43 PM, Lakshmi Ramasubramanian wrote:
>> Critical data structures of security modules are currently not measured.
>> Therefore an attestation service, for instance, would not be able to
>> attest whether the security modules are always operating with the policies
>> and configuration that the system administrator had setup. The policies
>> and configuration for the security modules could be tampered with by
>> malware by exploiting kernel vulnerabilities or modified through some
>> inadvertent actions on the system. Measuring such critical data would
>> enable an attestation service to better assess the state of the system.
>
> I still wonder why you're calling this an LSM change/feature when
> all the change is in IMA and SELinux. You're not putting anything
> into the LSM infrastructure, not are you using the LSM infrastructure
> to achieve your ends. Sure, you *could* support other security modules
> using this scheme, but you have a configuration dependency on
> SELinux, so that's at best going to be messy. If you want this to
> be an LSM "feature" you need to use the LSM hooking mechanism.
>
> I'm not objecting to the feature. It adds value. But as you've
> implemented it it is either an IMA extension to SELinux, or an
> SELiux extension to IMA. Could AppArmor add hooks for this without
> changing the IMA code? It doesn't look like it to me.
The check in IMA to allow the new IMA hook func LSM_STATE and LSM_POLICY
when SELinux is enabled is just because SELinux is the only security
module using these hooks now.
To enable AppArmor, for instance, to use the new IMA hooks to measure
state and policy would just require adding the check for
CONFIG_SECURITY_APPARMOR. Other than that, there are no IMA changes
needed to support AppArmor or other such security modules.
Please see Patch 1/4
+ else if (IS_ENABLED(CONFIG_SECURITY_SELINUX) &&
+ strcmp(args[0].from, "LSM_STATE") == 0)
+ entry->func = LSM_STATE;
+ else if (IS_ENABLED(CONFIG_SECURITY_SELINUX) &&
+ strcmp(args[0].from, "LSM_POLICY") == 0)
+ entry->func = LSM_POLICY;
And, if early boot measurement is needed for AppArmor the following
change in IMA's Kconfig
Patch 4/4
+config IMA_QUEUE_EARLY_BOOT_DATA
bool
+ depends on SECURITY_SELINUX || (IMA_MEASURE_ASYMMETRIC_KEYS &&
SYSTEM_TRUSTED_KEYRING)
default y
If you think calling this an "LSM feature" is not appropriate, please
suggest a better phrase.
But like I said above, with minimal change in IMA other security modules
can be supported to measure STATE and POLICY data.
-lakshmi
^ permalink raw reply
* Re: [GIT PULL] Filesystem Information
From: Ian Kent @ 2020-08-05 1:33 UTC (permalink / raw)
To: Miklos Szeredi
Cc: David Howells, Linus Torvalds, Al Viro, Karel Zak, Jeff Layton,
Miklos Szeredi, Nicolas Dichtel, Christian Brauner,
Lennart Poettering, Linux API, linux-fsdevel, LSM, linux-kernel
In-Reply-To: <CAJfpegu8omNZ613tLgUY7ukLV131tt7owR+JJ346Kombt79N0A@mail.gmail.com>
On Tue, 2020-08-04 at 16:36 +0200, Miklos Szeredi wrote:
> On Tue, Aug 4, 2020 at 4:15 AM Ian Kent <raven@themaw.net> wrote:
> > On Mon, 2020-08-03 at 18:42 +0200, Miklos Szeredi wrote:
> > > On Mon, Aug 3, 2020 at 5:50 PM David Howells <dhowells@redhat.com
> > > >
> > > wrote:
> > > > Hi Linus,
> > > >
> > > > Here's a set of patches that adds a system call, fsinfo(), that
> > > > allows
> > > > information about the VFS, mount topology, superblock and files
> > > > to
> > > > be
> > > > retrieved.
> > > >
> > > > The patchset is based on top of the mount notifications
> > > > patchset so
> > > > that
> > > > the mount notification mechanism can be hooked to provide event
> > > > counters
> > > > that can be retrieved with fsinfo(), thereby making it a lot
> > > > faster
> > > > to work
> > > > out which mounts have changed.
> > > >
> > > > Note that there was a last minute change requested by Miklós:
> > > > the
> > > > event
> > > > counter bits got moved from the mount notification patchset to
> > > > this
> > > > one.
> > > > The counters got made atomic_long_t inside the kernel and __u64
> > > > in
> > > > the
> > > > UAPI. The aggregate changes can be assessed by comparing pre-
> > > > change tag,
> > > > fsinfo-core-20200724 to the requested pull tag.
> > > >
> > > > Karel Zak has created preliminary patches that add support to
> > > > libmount[*]
> > > > and Ian Kent has started working on making systemd use these
> > > > and
> > > > mount
> > > > notifications[**].
> > >
> > > So why are you asking to pull at this stage?
> > >
> > > Has anyone done a review of the patchset?
> >
> > I have been working with the patch set as it has evolved for quite
> > a
> > while now.
> >
> > I've been reading the kernel code quite a bit and forwarded
> > questions
> > and minor changes to David as they arose.
> >
> > As for a review, not specifically, but while the series implements
> > a
> > rather large change it's surprisingly straight forward to read.
> >
> > In the time I have been working with it I haven't noticed any
> > problems
> > except for those few minor things that I reported to David early on
> > (in
> > some cases accompanied by simple patches).
> >
> > And more recently (obviously) I've been working with the mount
> > notifications changes and, from a readability POV, I find it's the
> > same as the fsinfo() code.
> >
> > > I think it's obvious that this API needs more work. The
> > > integration
> > > work done by Ian is a good direction, but it's not quite the full
> > > validation and review that a complex new API needs.
> >
> > Maybe but the system call is fundamental to making notifications
> > useful
> > and, as I say, after working with it for quite a while I don't fell
> > there's missing features (that David hasn't added along the way)
> > and
> > have found it provides what's needed for what I'm doing (for mount
> > notifications at least).
>
> Apart from the various issues related to the various mount ID's and
> their sizes, my general comment is (and was always): why are we
> adding
> a multiplexer for retrieval of mostly unrelated binary structures?
>
> <linux/fsinfo.h> is 345 lines. This is not a simple and clean API.
>
> A simple and clean replacement API would be:
>
> int get_mount_attribute(int dfd, const char *path, const char
> *attr_name, char *value_buf, size_t buf_size, int flags);
>
> No header file needed with dubiously sized binary values.
>
> The only argument was performance, but apart from purely synthetic
> microbenchmarks that hasn't been proven to be an issue.
>
> And notice how similar the above interface is to getxattr(), or the
> proposed readfile(). Where has the "everything is a file"
> philosophy
> gone?
Maybe, but that philosophy (in a roundabout way) is what's resulted
in some of the problems we now have. Granted it's blind application
of that philosophy rather than the philosophy itself but that is
what happens.
I get that your comments are driven by the way that philosophy should
be applied which is more of a "if it works best doing it that way then
do it that way, and that's usually a file".
In this case there is a logical division of various types of file
system information and the underlying suggestion is maybe it's time
to move away from the "everything is a file" hard and fast rule,
and get rid of some of the problems that have resulted from it.
The notifications is an example, yes, the delivery mechanism is
a "file" but the design of the queueing mechanism makes a lot of
sense for the throughput that's going to be needed as time marches
on. Then there's different sub-systems each with unique information
that needs to be deliverable some other way because delivering "all"
the information via the notification would be just plain wrong so
a multi-faceted information delivery mechanism makes the most
sense to allow specific targeted retrieval of individual items of
information.
But that also supposes your at least open to the idea that "maybe
not everything should be a file".
>
> I think we already lost that with the xattr API, that should have
> been
> done in a way that fits this philosophy. But given that we have "/"
> as the only special purpose char in filenames, and even repetitions
> are allowed, it's hard to think of a good way to do that. Pity.
>
> Still I think it would be nice to have a general purpose attribute
> retrieval API instead of the multiplicity of binary ioctls, xattrs,
> etc.
>
> Is that totally crazy? Nobody missing the beauty in recently
> introduced APIs?
>
> Thanks,
> Miklos
^ permalink raw reply
* Re: [PATCH 13/17] watch_queue: Implement mount topology and attribute change notifications [ver #5]
From: Ian Kent @ 2020-08-05 1:53 UTC (permalink / raw)
To: Miklos Szeredi
Cc: David Howells, Linus Torvalds, Al Viro, Casey Schaufler,
Stephen Smalley, Nicolas Dichtel, Christian Brauner, andres,
Jeff Layton, dray, Karel Zak, keyrings, Linux API, linux-fsdevel,
LSM, linux-kernel
In-Reply-To: <CAJfpeguFkDDhz7+70pSUv_j=xY5L08ESpaE+jER9vE5p+ZmfFw@mail.gmail.com>
On Tue, 2020-08-04 at 15:19 +0200, Miklos Szeredi wrote:
> On Tue, Aug 4, 2020 at 1:39 PM Ian Kent <raven@themaw.net> wrote:
> > On Mon, 2020-08-03 at 11:29 +0200, Miklos Szeredi wrote:
> > > On Thu, Jul 23, 2020 at 12:48 PM David Howells <
> > > dhowells@redhat.com>
> > > wrote:
> > >
> > > > > > __u32 topology_changes;
> > > > > > __u32 attr_changes;
> > > > > > __u32 aux_topology_changes;
> > > > >
> > > > > Being 32bit this introduces wraparound effects. Is that
> > > > > really
> > > > > worth it?
> > > >
> > > > You'd have to make 2 billion changes without whoever's
> > > > monitoring
> > > > getting a
> > > > chance to update their counters. But maybe it's not worth it
> > > > putting them
> > > > here. If you'd prefer, I can make the counters all 64-bit and
> > > > just
> > > > retrieve
> > > > them with fsinfo().
> > >
> > > Yes, I think that would be preferable.
> >
> > I think this is the source of the recommendation for removing the
> > change counters from the notification message, correct?
> >
> > While it looks like I may not need those counters for systemd
> > message
> > buffer overflow handling myself I think removing them from the
> > notification message isn't a sensible thing to do.
> >
> > If you need to detect missing messages, perhaps due to message
> > buffer
> > overflow, then you need change counters that are relevant to the
> > notification message itself. That's so the next time you get a
> > message
> > for that object you can be sure that change counter comparisons you
> > you make relate to object notifications you have processed.
>
> I don't quite get it. Change notification is just that: a
> notification. You need to know what object that notification
> relates
> to, to be able to retrieve the up to date attributes of said object.
>
> What happens if you get a change counter N in the notification
> message, then get a change counter N + 1 in the attribute retrieval?
> You know that another change happened, and you haven't yet processed
> the notification yet. So when the notification with N + 1 comes in,
> you can optimize away the attribute retrieve.
>
> Nice optimization, but it's optimizing a race condition, and I don't
> think that's warranted. I don't see any other use for the change
> counter in the notification message.
>
>
> > Yes, I know it isn't quite that simple, but tallying up what you
> > have
> > processed in the current batch of messages (or in multiple batches
> > of
> > messages if more than one read has been possible) to perform the
> > check
> > is a user space responsibility. And it simply can't be done if the
> > counters consistency is in question which it would be if you need
> > to
> > perform another system call to get it.
> >
> > It's way more useful to have these in the notification than
> > obtainable
> > via fsinfo() IMHO.
>
> What is it useful for?
Only to verify that you have seen all the notifications.
If you have to grab that info with a separate call then the count
isn't necessarily consistent because other notifications can occur
while you grab it.
My per-object rant isn't quite right, what's needed is a consistent
way to verify you have seen everything you were supposed to.
I think your point is that if you grab the info in another call and
it doesn't match you need to refresh and that's fine but I think it's
better to be able to verify you have got everything that was sent as
you go and avoid the need for the refresh more often.
>
> If the notification itself would contain the list of updated
> attributes and their new values, then yes, this would make sense. If
> the notification just tells us that the object was modified, but not
> the modifications themselves, then I don't see how the change counter
> in itself could add any information (other than optimizing the race
> condition above).
>
> Thanks,
> Miklos
>
> Thanks,
>
>
>
> > > > > > n->watch.info & NOTIFY_MOUNT_IS_RECURSIVE if true
> > > > > > indicates that
> > > > > > the notifcation was generated by an event (eg.
> > > > > > SETATTR)
> > > > > > that was
> > > > > > applied recursively. The notification is only
> > > > > > generated for the
> > > > > > object that initially triggered it.
> > > > >
> > > > > Unused in this patchset. Please don't add things to the API
> > > > > which are not
> > > > > used.
> > > >
> > > > Christian Brauner has patches for mount_setattr() that will
> > > > need to
> > > > use this.
> > >
> > > Fine, then that patch can add the flag.
> > >
> > > Thanks,
> > > Miklos
^ permalink raw reply
* Re: [PATCH 10/18] fsinfo: Provide notification overrun handling support [ver #21]
From: Ian Kent @ 2020-08-05 2:05 UTC (permalink / raw)
To: Miklos Szeredi, David Howells
Cc: viro, torvalds, mszeredi, christian, jannh, darrick.wong, kzak,
jlayton, linux-api, linux-fsdevel, linux-security-module,
linux-kernel
In-Reply-To: <20200804135641.GE32719@miu.piliscsaba.redhat.com>
On Tue, 2020-08-04 at 15:56 +0200, Miklos Szeredi wrote:
> On Mon, Aug 03, 2020 at 02:37:50PM +0100, David Howells wrote:
> > Provide support for the handling of an overrun in a watch
> > queue. In the
> > event that an overrun occurs, the watcher needs to be able to find
> > out what
> > it was that they missed. To this end, previous patches added event
> > counters to struct mount.
>
> So this is optimizing the buffer overrun case?
>
> Shoun't we just make sure that the likelyhood of overruns is low and
> if it
> happens, just reinitialize everthing from scratch (shouldn't be
> *that*
> expensive).
But maybe not possible if you are using notifications for tracking
state in user space, you need to know when the thing you have needs
to be synced because you missed something and it's during the
notification processing you actually have the object that may need
to be refreshed.
>
> Trying to find out what was missed seems like just adding complexity
> for no good
> reason.
>
^ permalink raw reply
* Re: [PATCH 10/18] fsinfo: Provide notification overrun handling support [ver #21]
From: Ian Kent @ 2020-08-05 2:46 UTC (permalink / raw)
To: Miklos Szeredi, David Howells
Cc: viro, torvalds, mszeredi, christian, jannh, darrick.wong, kzak,
jlayton, linux-api, linux-fsdevel, linux-security-module,
linux-kernel
In-Reply-To: <94bba6f200bb2bbf83f4945faa2ccb83fd947540.camel@themaw.net>
On Wed, 2020-08-05 at 10:05 +0800, Ian Kent wrote:
> On Tue, 2020-08-04 at 15:56 +0200, Miklos Szeredi wrote:
> > On Mon, Aug 03, 2020 at 02:37:50PM +0100, David Howells wrote:
> > > Provide support for the handling of an overrun in a watch
> > > queue. In the
> > > event that an overrun occurs, the watcher needs to be able to
> > > find
> > > out what
> > > it was that they missed. To this end, previous patches added
> > > event
> > > counters to struct mount.
> >
> > So this is optimizing the buffer overrun case?
> >
> > Shoun't we just make sure that the likelyhood of overruns is low
> > and
> > if it
> > happens, just reinitialize everthing from scratch (shouldn't be
> > *that*
> > expensive).
>
> But maybe not possible if you are using notifications for tracking
> state in user space, you need to know when the thing you have needs
> to be synced because you missed something and it's during the
> notification processing you actually have the object that may need
> to be refreshed.
>
> > Trying to find out what was missed seems like just adding
> > complexity
> > for no good
> > reason.
Coming back to an actual use case.
What I said above is one aspect but, since I'm looking at this right
now with systemd, and I do have the legacy code to fall back to, the
"just reset everything" suggestion does make sense.
But I'm struggling to see how I can identify notification buffer
overrun in libmount, and overrun is just one possibility for lost
notifications, so I like the idea that, as a library user, I can
work out that I need to take action based on what I have in the
notifications themselves.
Ian
^ permalink raw reply
* Re: [PATCH v6 1/4] IMA: Add func to measure LSM state and policy
From: Mimi Zohar @ 2020-08-05 3:25 UTC (permalink / raw)
To: Lakshmi Ramasubramanian, stephen.smalley.work, casey
Cc: tyhicks, sashal, jmorris, linux-integrity, selinux,
linux-security-module, linux-kernel
In-Reply-To: <20200805004331.20652-2-nramas@linux.microsoft.com>
Hi Lakshmi,
There's still a number of other patch sets needing to be reviewed
before my getting to this one. The comment below is from a high level.
On Tue, 2020-08-04 at 17:43 -0700, Lakshmi Ramasubramanian wrote:
> Critical data structures of security modules need to be measured to
> enable an attestation service to verify if the configuration and
> policies for the security modules have been setup correctly and
> that they haven't been tampered with at runtime. A new IMA policy is
> required for handling this measurement.
>
> Define two new IMA policy func namely LSM_STATE and LSM_POLICY to
> measure the state and the policy provided by the security modules.
> Update ima_match_rules() and ima_validate_rule() to check for
> the new func and ima_parse_rule() to handle the new func.
I can understand wanting to measure the in kernel LSM memory state to
make sure it hasn't changed, but policies are stored as files. Buffer
measurements should be limited to those things that are not files.
Changing how data is passed to the kernel has been happening for a
while. For example, instead of passing the kernel module or kernel
image in a buffer, the new syscalls - finit_module, kexec_file_load -
pass an open file descriptor. Similarly, instead of loading the IMA
policy data, a pathname may be provided.
Pre and post security hooks already exist for reading files. Instead
of adding IMA support for measuring the policy file data, update the
mechanism for loading the LSM policy. Then not only will you be able
to measure the policy, you'll also be able to require the policy be
signed.
Mimi
^ permalink raw reply
* Re: [PATCH 13/17] watch_queue: Implement mount topology and attribute change notifications [ver #5]
From: Miklos Szeredi @ 2020-08-05 7:43 UTC (permalink / raw)
To: Ian Kent
Cc: David Howells, Linus Torvalds, Al Viro, Casey Schaufler,
Stephen Smalley, Nicolas Dichtel, Christian Brauner, andres,
Jeff Layton, dray, Karel Zak, keyrings, Linux API, linux-fsdevel,
LSM, linux-kernel
In-Reply-To: <c558fc4af785f62a2751be3b297d1ccbbfcfa969.camel@themaw.net>
On Wed, Aug 5, 2020 at 3:54 AM Ian Kent <raven@themaw.net> wrote:
>
> > > It's way more useful to have these in the notification than
> > > obtainable
> > > via fsinfo() IMHO.
> >
> > What is it useful for?
>
> Only to verify that you have seen all the notifications.
>
> If you have to grab that info with a separate call then the count
> isn't necessarily consistent because other notifications can occur
> while you grab it.
No, no no. The watch queue will signal an overflow, without any
additional overhead for the normal case. If you think of this as a
protocol stack, then the overflow detection happens on the transport
layer, instead of the application layer. The application layer is
responsible for restoring state in case of a transport layer error,
but detection of that error is not the responsibility of the
application layer.
Thanks,
Miklos
^ permalink raw reply
* Re: [PATCH 10/18] fsinfo: Provide notification overrun handling support [ver #21]
From: Miklos Szeredi @ 2020-08-05 7:45 UTC (permalink / raw)
To: Ian Kent
Cc: David Howells, Al Viro, Linus Torvalds, Miklos Szeredi,
Christian Brauner, Jann Horn, Darrick J. Wong, Karel Zak,
Jeff Layton, Linux API, linux-fsdevel, LSM, linux-kernel
In-Reply-To: <5078554c6028e29c91d815c63e2af1ffac2ecbbb.camel@themaw.net>
On Wed, Aug 5, 2020 at 4:46 AM Ian Kent <raven@themaw.net> wrote:
>
> Coming back to an actual use case.
>
> What I said above is one aspect but, since I'm looking at this right
> now with systemd, and I do have the legacy code to fall back to, the
> "just reset everything" suggestion does make sense.
>
> But I'm struggling to see how I can identify notification buffer
> overrun in libmount, and overrun is just one possibility for lost
> notifications, so I like the idea that, as a library user, I can
> work out that I need to take action based on what I have in the
> notifications themselves.
Hmm, what's the other possibility for lost notifications?
Thanks,
Miklos
^ permalink raw reply
* Re: [GIT PULL] Filesystem Information
From: Miklos Szeredi @ 2020-08-05 8:00 UTC (permalink / raw)
To: Ian Kent
Cc: David Howells, Linus Torvalds, Al Viro, Karel Zak, Jeff Layton,
Miklos Szeredi, Nicolas Dichtel, Christian Brauner,
Lennart Poettering, Linux API, linux-fsdevel, LSM, linux-kernel
In-Reply-To: <dd1a41a129cd6e8d13525a14807e6dc65b52e0bf.camel@themaw.net>
On Wed, Aug 5, 2020 at 3:33 AM Ian Kent <raven@themaw.net> wrote:
>
> On Tue, 2020-08-04 at 16:36 +0200, Miklos Szeredi wrote:
> > And notice how similar the above interface is to getxattr(), or the
> > proposed readfile(). Where has the "everything is a file"
> > philosophy
> > gone?
>
> Maybe, but that philosophy (in a roundabout way) is what's resulted
> in some of the problems we now have. Granted it's blind application
> of that philosophy rather than the philosophy itself but that is
> what happens.
Agree. What people don't seem to realize, even though there are
blindingly obvious examples, that binary interfaces like the proposed
fsinfo(2) syscall can also result in a multitude of problems at the
same time as solving some others.
There's no magic solution in API design, it's not balck and white.
We just need to strive for a good enough solution. The problem seems
to be that trying to discuss the merits of other approaches seems to
hit a brick wall. We just see repeated pull requests from David,
without any real discussion of the proposed alternatives.
>
> I get that your comments are driven by the way that philosophy should
> be applied which is more of a "if it works best doing it that way then
> do it that way, and that's usually a file".
>
> In this case there is a logical division of various types of file
> system information and the underlying suggestion is maybe it's time
> to move away from the "everything is a file" hard and fast rule,
> and get rid of some of the problems that have resulted from it.
>
> The notifications is an example, yes, the delivery mechanism is
> a "file" but the design of the queueing mechanism makes a lot of
> sense for the throughput that's going to be needed as time marches
> on. Then there's different sub-systems each with unique information
> that needs to be deliverable some other way because delivering "all"
> the information via the notification would be just plain wrong so
> a multi-faceted information delivery mechanism makes the most
> sense to allow specific targeted retrieval of individual items of
> information.
>
> But that also supposes your at least open to the idea that "maybe
> not everything should be a file".
Sure. I've learned pragmatism, although idealist at heart. And I'm
not saying all API's from David are shit. statx(2) is doing fine.
It's a simple binary interface that does its job well. Compare the
header files for statx and fsinfo, though, and maybe you'll see what
I'm getting at...
Thanks,
Miklos
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox