From: Paul Moore <paul@paul-moore.com>
To: "Christian Göttsche" <cgoettsche@seltendoof.de>, selinux@vger.kernel.org
Cc: "Christian Göttsche" <cgzones@googlemail.com>,
"Stephen Smalley" <stephen.smalley.work@gmail.com>,
"Ondrej Mosnacek" <omosnace@redhat.com>,
"Nathan Chancellor" <nathan@kernel.org>,
"Nick Desaulniers" <ndesaulniers@google.com>,
"Bill Wendling" <morbo@google.com>,
"Justin Stitt" <justinstitt@google.com>,
"Thiébaud Weksteen" <tweek@google.com>,
"Bram Bonné" <brambonne@google.com>,
"Masahiro Yamada" <masahiroy@kernel.org>,
linux-kernel@vger.kernel.org, llvm@lists.linux.dev,
"Eric Suen" <ericsu@linux.microsoft.com>,
"Casey Schaufler" <casey@schaufler-ca.com>,
"Mimi Zohar" <zohar@linux.ibm.com>,
"Canfeng Guo" <guocanfeng@uniontech.com>,
"GUO Zihua" <guozihua@huawei.com>
Subject: Re: [PATCH RFC v2 11/22] selinux: more strict policy parsing
Date: Tue, 07 Jan 2025 22:00:06 -0500 [thread overview]
Message-ID: <ef5f7c20f5a3a485cdf2603ea4a4cde9@paul-moore.com> (raw)
In-Reply-To: <20241216164055.96267-11-cgoettsche@seltendoof.de>
On Dec 16, 2024 =?UTF-8?q?Christian=20G=C3=B6ttsche?= <cgoettsche@seltendoof.de> wrote:
>
> Be more strict during parsing of policies and reject invalid values.
>
> Add some error messages in the case of policy parse failures, to
> enhance debugging, either on a malformed policy or a too strict check.
>
> Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
> ---
> v2:
> accept unknown xperm specifiers to support backwards compatibility for
> future ones, suggested by Thiébaud
> ---
> security/selinux/ss/avtab.c | 37 +++++--
> security/selinux/ss/conditional.c | 18 ++--
> security/selinux/ss/constraint.h | 2 +-
> security/selinux/ss/policydb.c | 157 ++++++++++++++++++++++++------
> security/selinux/ss/policydb.h | 19 +++-
> security/selinux/ss/services.c | 2 -
> 6 files changed, 182 insertions(+), 53 deletions(-)
>
> diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c
> index c2c31521cace..3bd949a200ef 100644
> --- a/security/selinux/ss/avtab.c
> +++ b/security/selinux/ss/avtab.c
> @@ -349,7 +349,7 @@ int avtab_read_item(struct avtab *a, struct policy_file *fp, struct policydb *po
> struct avtab_extended_perms xperms;
> __le32 buf32[ARRAY_SIZE(xperms.perms.p)];
> int rc;
> - unsigned int set, vers = pol->policyvers;
> + unsigned int vers = pol->policyvers;
>
> memset(&key, 0, sizeof(struct avtab_key));
> memset(&datum, 0, sizeof(struct avtab_datum));
> @@ -361,8 +361,8 @@ int avtab_read_item(struct avtab *a, struct policy_file *fp, struct policydb *po
> return rc;
> }
> items2 = le32_to_cpu(buf32[0]);
> - if (items2 > ARRAY_SIZE(buf32)) {
> - pr_err("SELinux: avtab: entry overflow\n");
> + if (items2 < 5 || items2 > ARRAY_SIZE(buf32)) {
> + pr_err("SELinux: avtab: invalid item count\n");
> return -EINVAL;
> }
A reminder that magic numbers are a bad thing, if we can't make it clear
what the '5' in the conditional above represents by using a computed
value, let's either use a #define with a helpful name or a comment to
make this a bit more understandable.
> @@ -444,9 +456,13 @@ int avtab_read_item(struct avtab *a, struct policy_file *fp, struct policydb *po
> return -EINVAL;
> }
>
> - set = hweight16(key.specified & (AVTAB_XPERMS | AVTAB_TYPE | AVTAB_AV));
> - if (!set || set > 1) {
> - pr_err("SELinux: avtab: more than one specifier\n");
> + if (hweight16(key.specified & ~AVTAB_ENABLED) != 1) {
> + pr_err("SELinux: avtab: not exactly one specifier\n");
> + return -EINVAL;
> + }
> +
> + if (key.specified & ~(AVTAB_AV | AVTAB_TYPE | AVTAB_XPERMS | AVTAB_ENABLED)) {
> + pr_err("SELinux: avtab: invalid specifier\n");
> return -EINVAL;
> }
Let's define a macro in avtab.h with all of the allowed avtab key
values, otherwise I think people are going to forget about this check
when adding a new flag and they are going to get frustrated :)
> @@ -471,6 +487,15 @@ int avtab_read_item(struct avtab *a, struct policy_file *fp, struct policydb *po
> pr_err("SELinux: avtab: truncated entry\n");
> return rc;
> }
> + switch (xperms.specified) {
> + case AVTAB_XPERMS_IOCTLFUNCTION:
> + case AVTAB_XPERMS_IOCTLDRIVER:
> + case AVTAB_XPERMS_NLMSG:
> + break;
> + default:
> + pr_warn_once_policyload(pol, "SELinux: avtab: unsupported xperm specifier %#x\n",
> + xperms.specified);
> + }
Similar to the avtab flags discussion above, can we create a small
inline function in avtab.h that checks to see if an xperm is valid?
/* feel free to come up with a better name */
static inline bool avtab_xpermspec_valid(u8 specified)
{
if (specified == AVTAB_XPERMS_IOCTLFUNCTION)
return true;
elif (...)
return true;
return false;
}
> diff --git a/security/selinux/ss/constraint.h b/security/selinux/ss/constraint.h
> index 203033cfad67..26ffdb8c1c29 100644
> --- a/security/selinux/ss/constraint.h
> +++ b/security/selinux/ss/constraint.h
> @@ -50,7 +50,7 @@ struct constraint_expr {
> u32 op; /* operator */
>
> struct ebitmap names; /* names */
> - struct type_set *type_names;
> + struct type_set *type_names; /* (unused) */
If we're not using this field, let's remove it. If for some odd reason
we need to keep it here for size reasons, or something similar, let's
turn it into a 'void *unused;' field.
> diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
> index eeca470cc90c..1275fd7d9148 100644
> --- a/security/selinux/ss/policydb.c
> +++ b/security/selinux/ss/policydb.c
> @@ -634,13 +634,11 @@ static int sens_index(void *key, void *datum, void *datap)
> levdatum = datum;
> p = datap;
>
> - if (!levdatum->isalias) {
> - if (!levdatum->level.sens ||
> - levdatum->level.sens > p->p_levels.nprim)
> - return -EINVAL;
> + if (!levdatum->level.sens || levdatum->level.sens > p->p_levels.nprim)
> + return -EINVAL;
>
> + if (!levdatum->isalias)
> p->sym_val_to_name[SYM_LEVELS][levdatum->level.sens - 1] = key;
> - }
>
> return 0;
> }
Hmm, I don't think the code above does the error checking in the same
way, how about something like this:
int sens_index(...)
{
if (isalias)
return 0;
if (!level->sens || level->send > levels.nprim)
return -EINVAL;
p = ...;
return 0;
}
> @@ -653,12 +651,11 @@ static int cat_index(void *key, void *datum, void *datap)
> catdatum = datum;
> p = datap;
>
> - if (!catdatum->isalias) {
> - if (!catdatum->value || catdatum->value > p->p_cats.nprim)
> - return -EINVAL;
> + if (!catdatum->value || catdatum->value > p->p_cats.nprim)
> + return -EINVAL;
>
> + if (!catdatum->isalias)
> p->sym_val_to_name[SYM_CATS][catdatum->value - 1] = key;
> - }
>
> return 0;
> }
Similar to the sensitivity level comment above.
> @@ -1136,6 +1133,9 @@ static int perm_read(struct policydb *p, struct symtab *s, struct policy_file *f
>
> len = le32_to_cpu(buf[0]);
> perdatum->value = le32_to_cpu(buf[1]);
> + rc = -EINVAL;
> + if (perdatum->value < 1 || perdatum->value > 32)
> + goto bad;
More magic number problems.
> rc = str_read(&key, GFP_KERNEL, fp, len);
> if (rc)
> @@ -1170,6 +1170,9 @@ static int common_read(struct policydb *p, struct symtab *s, struct policy_file
> len = le32_to_cpu(buf[0]);
> comdatum->value = le32_to_cpu(buf[1]);
> nel = le32_to_cpu(buf[3]);
> + rc = -EINVAL;
> + if (nel > 32)
> + goto bad;
Magic number.
> rc = symtab_init(&comdatum->permissions, nel);
> if (rc)
> @@ -1335,6 +1338,9 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
> len = le32_to_cpu(buf[0]);
> len2 = le32_to_cpu(buf[1]);
> nel = le32_to_cpu(buf[4]);
> + rc = -EINVAL;
> + if (nel > 32)
> + goto bad;
Again.
> @@ -1527,7 +1578,7 @@ static int type_read(struct policydb *p, struct symtab *s, struct policy_file *f
> * Read a MLS level structure from a policydb binary
> * representation file.
> */
> -static int mls_read_level(struct mls_level *lp, struct policy_file *fp)
> +static int mls_read_level(const struct policydb *p, struct mls_level *lp, struct policy_file *fp)
> {
> __le32 buf[1];
> int rc;
Why is this here? You don't use the @p parameter anywhere in this
patch and it add some code churn in all of the callers.
> @@ -1606,7 +1657,7 @@ static int sens_read(struct policydb *p, struct symtab *s, struct policy_file *f
> struct level_datum *levdatum;
> int rc;
> __le32 buf[2];
> - u32 len;
> + u32 len, val;
>
> levdatum = kzalloc(sizeof(*levdatum), GFP_KERNEL);
> if (!levdatum)
> @@ -1617,13 +1668,17 @@ static int sens_read(struct policydb *p, struct symtab *s, struct policy_file *f
> goto bad;
>
> len = le32_to_cpu(buf[0]);
> - levdatum->isalias = le32_to_cpu(buf[1]);
> + val = le32_to_cpu(buf[1]);
> + rc = -EINVAL;
> + if (val != 0 && val != 1)
> + goto bad;
> + levdatum->isalias = val;
Should we have a simple inline function to do the integer boolean check?
Considering all the places we check for 0 and 1, it seems like it might
be a bit cleaner, and would help with self-documenting.
> @@ -2221,7 +2303,7 @@ static int genfs_read(struct policydb *p, struct policy_file *fp)
>
> rc = -EINVAL;
> val = le32_to_cpu(buf[0]);
> - if (val >= U16_MAX)
> + if (val >= U16_MAX || (val != 0 && !policydb_class_isvalid(p, val)))
> goto out;
> newc->v.sclass = val;
> rc = context_read_and_validate(&newc->context[0], p,
This should probably be in patch 10/22, yes?
> @@ -110,15 +110,15 @@ struct role_allow {
> /* Type attributes */
> struct type_datum {
> u32 value; /* internal type value */
> - u32 bounds; /* boundary of type */
> - unsigned char primary; /* primary name? */
> + u32 bounds; /* boundary of type, 0 for none */
> + unsigned char primary; /* primary name? (unused) */
See my previous comment about unused fields.
> #endif /* _SS_POLICYDB_H_ */
> diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
> index 28c0bdf9fc9d..d5725c768d59 100644
> --- a/security/selinux/ss/services.c
> +++ b/security/selinux/ss/services.c
> @@ -445,8 +445,6 @@ static int dump_masked_av_helper(void *k, void *d, void *args)
> struct perm_datum *pdatum = d;
> char **permission_names = args;
>
> - BUG_ON(pdatum->value < 1 || pdatum->value > 32);
Do we need to convert this to a if-then check that does the proper error
handling, or is it already handled in the other changes in this patch?
--
paul-moore.com
next prev parent reply other threads:[~2025-01-08 3:00 UTC|newest]
Thread overview: 46+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-12-16 16:39 [RFC PATCH v2 01/22] selinux: supply missing field initializers Christian Göttsche
2024-12-16 16:40 ` [RFC PATCH v2 02/22] selinux: avoid using types indicating user space interaction Christian Göttsche
2025-01-08 2:59 ` [PATCH RFC v2 2/22] " Paul Moore
2024-12-16 16:40 ` [RFC PATCH v2 03/22] selinux: align and constify functions Christian Göttsche
2025-01-08 2:59 ` [PATCH RFC v2 3/22] " Paul Moore
2024-12-16 16:40 ` [RFC PATCH v2 04/22] selinux: rework match_ipv6_addrmask() Christian Göttsche
2025-01-08 2:59 ` [PATCH RFC v2 4/22] " Paul Moore
2024-12-16 16:40 ` [RFC PATCH v2 05/22] selinux: avoid nontransitive comparison Christian Göttsche
2025-01-08 2:59 ` [PATCH RFC v2 5/22] " Paul Moore
2025-01-08 13:17 ` Christian Göttsche
2025-01-08 15:06 ` Christian Göttsche
2024-12-16 16:40 ` [RFC PATCH v2 06/22] selinux: rename comparison functions for clarity Christian Göttsche
2025-01-08 3:00 ` [PATCH RFC v2 6/22] " Paul Moore
2024-12-16 16:40 ` [RFC PATCH v2 07/22] selinux: use known type instead of void pointer Christian Göttsche
2025-01-08 3:00 ` [PATCH RFC v2 7/22] " Paul Moore
2024-12-16 16:40 ` [RFC PATCH v2 08/22] selinux: avoid unnecessary indirection in struct level_datum Christian Göttsche
2025-01-08 3:00 ` [PATCH RFC v2 8/22] " Paul Moore
2024-12-16 16:40 ` [RFC PATCH v2 09/22] selinux: make use of str_read() Christian Göttsche
2025-01-08 3:00 ` [PATCH RFC v2 9/22] " Paul Moore
2024-12-16 16:40 ` [RFC PATCH v2 10/22] selinux: use u16 for security classes Christian Göttsche
2025-01-08 3:00 ` [PATCH RFC " Paul Moore
2024-12-16 16:40 ` [RFC PATCH v2 11/22] selinux: more strict policy parsing Christian Göttsche
2025-01-08 3:00 ` Paul Moore [this message]
2025-01-08 15:49 ` [PATCH RFC " Christian Göttsche
2024-12-16 16:40 ` [RFC PATCH v2 12/22] selinux: check length fields in policies Christian Göttsche
2025-01-08 3:00 ` [PATCH RFC " Paul Moore
2024-12-16 16:40 ` [RFC PATCH v2 13/22] selinux: validate constraints Christian Göttsche
2024-12-16 16:40 ` [RFC PATCH v2 14/22] selinux: pre-validate conditional expressions Christian Göttsche
2024-12-16 16:40 ` [RFC PATCH v2 15/22] selinux: introduce ebitmap_highest_set_bit() Christian Göttsche
2025-01-08 3:00 ` [PATCH RFC " Paul Moore
2024-12-16 16:40 ` [RFC PATCH v2 16/22] selinux: check type attr map overflows Christian Göttsche
2024-12-16 16:40 ` [RFC PATCH v2 17/22] selinux: reorder policydb_index() Christian Göttsche
2024-12-16 16:40 ` [RFC PATCH v2 18/22] selinux: beef up isvalid checks Christian Göttsche
2024-12-16 16:40 ` [RFC PATCH v2 19/22] selinux: validate symbols Christian Göttsche
2025-01-08 3:00 ` [PATCH RFC " Paul Moore
2025-01-08 17:02 ` Christian Göttsche
2024-12-16 16:40 ` [RFC PATCH v2 20/22] selinux: more strict bounds check Christian Göttsche
2024-12-16 16:40 ` [RFC PATCH v2 21/22] selinux: check for simple types Christian Göttsche
2024-12-16 16:40 ` [RFC PATCH v2 22/22] selinux: restrict policy strings Christian Göttsche
2025-01-03 20:12 ` Stephen Smalley
2025-01-05 23:26 ` Joe Nall
2025-01-07 14:04 ` Christian Göttsche
2025-01-07 16:09 ` Daniel Burgener
2025-01-07 16:32 ` James Carter
2024-12-16 16:40 ` [RFC PATCH v2 00/22] selinux: harden against malformed policies Christian Göttsche
2025-01-08 2:59 ` [PATCH RFC v2 1/22] selinux: supply missing field initializers Paul Moore
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=ef5f7c20f5a3a485cdf2603ea4a4cde9@paul-moore.com \
--to=paul@paul-moore.com \
--cc=brambonne@google.com \
--cc=casey@schaufler-ca.com \
--cc=cgoettsche@seltendoof.de \
--cc=cgzones@googlemail.com \
--cc=ericsu@linux.microsoft.com \
--cc=guocanfeng@uniontech.com \
--cc=guozihua@huawei.com \
--cc=justinstitt@google.com \
--cc=linux-kernel@vger.kernel.org \
--cc=llvm@lists.linux.dev \
--cc=masahiroy@kernel.org \
--cc=morbo@google.com \
--cc=nathan@kernel.org \
--cc=ndesaulniers@google.com \
--cc=omosnace@redhat.com \
--cc=selinux@vger.kernel.org \
--cc=stephen.smalley.work@gmail.com \
--cc=tweek@google.com \
--cc=zohar@linux.ibm.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox