[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <CAJ2a_DfhjT16U=wavqPQH67HsJTKKHfPpCA736VrCge5AWhsuQ@mail.gmail.com>
Date: Mon, 16 Dec 2024 17:02:56 +0100
From: Christian Göttsche <cgzones@...glemail.com>
To: Daniel Burgener <dburgener@...ux.microsoft.com>
Cc: selinux@...r.kernel.org, Paul Moore <paul@...l-moore.com>,
Stephen Smalley <stephen.smalley.work@...il.com>, Ondrej Mosnacek <omosnace@...hat.com>,
linux-kernel@...r.kernel.org
Subject: Re: [RFC PATCH 22/22] selinux: restrict policy strings
On Fri, 13 Dec 2024 at 23:14, Daniel Burgener
<dburgener@...ux.microsoft.com> wrote:
>
> On 11/15/2024 8:35 AM, Christian Göttsche wrote:
> > From: Christian Göttsche <cgzones@...glemail.com>
> >
> > Validate the characters and the lengths of strings parsed from binary
> > policies.
> >
> > * Disallow control characters
> > * Limit characters of identifiers to alphanumeric, underscore, dash,
> > and dot
> > * Limit identifiers in length to 128, expect types to 1024 and
> > categories to 32, characters (excluding NUL-terminator)
> >
> > Signed-off-by: Christian Göttsche <cgzones@...glemail.com>
> > ---
> > security/selinux/ss/conditional.c | 2 +-
> > security/selinux/ss/policydb.c | 60 ++++++++++++++++++++-----------
> > security/selinux/ss/policydb.h | 5 ++-
> > 3 files changed, 44 insertions(+), 23 deletions(-)
> >
> > diff --git a/security/selinux/ss/conditional.c b/security/selinux/ss/conditional.c
> > index d37b4bdf6ba9..346102417cbf 100644
> > --- a/security/selinux/ss/conditional.c
> > +++ b/security/selinux/ss/conditional.c
> > @@ -280,7 +280,7 @@ int cond_read_bool(struct policydb *p, struct symtab *s, struct policy_file *fp)
> >
> > len = le32_to_cpu(buf[2]);
> >
> > - rc = str_read(&key, GFP_KERNEL, fp, len);
> > + rc = str_read(&key, GFP_KERNEL, fp, len, STR_IDENTIFIER, 128);
> > if (rc)
> > goto err;
> >
>
> It would be nice if these limits were named constants instead of magic
> numbers. Right now it's hard to tell if all the "128"s are essentially
> the same limit referenced in different places, or if they could (in
> theory) be changed independently.
>
> > diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
> > index 917b468c5144..d98dfa6c3f30 100644
> > --- a/security/selinux/ss/policydb.c
> > +++ b/security/selinux/ss/policydb.c
> > @@ -1221,8 +1221,9 @@ static int context_read_and_validate(struct context *c, struct policydb *p,
> > * binary representation file.
> > */
> >
> > -int str_read(char **strp, gfp_t flags, struct policy_file *fp, u32 len)
> > +int str_read(char **strp, gfp_t flags, struct policy_file *fp, u32 len, int kind, u32 max_len)
> > {
> > + u32 i;
> > int rc;
> > char *str;
> >
> > @@ -1232,19 +1233,35 @@ int str_read(char **strp, gfp_t flags, struct policy_file *fp, u32 len)
> > if (oom_check(sizeof(char), len, fp))
> > return -EINVAL;
> >
> > + if (max_len != 0 && len > max_len)
> > + return -EINVAL;
> > +
> > str = kmalloc(len + 1, flags | __GFP_NOWARN);
> > if (!str)
> > return -ENOMEM;
> >
> > rc = next_entry(str, fp, len);
> > - if (rc) {
> > - kfree(str);
> > - return rc;
> > + if (rc)
> > + goto bad_str;
> > +
> > + rc = -EINVAL;
> > + for (i = 0; i < len; i++) {
> > + if (iscntrl(str[i]))
> > + goto bad_str;
> > +
> > + if (kind == STR_IDENTIFIER &&
> > + !(isalnum(str[i]) || str[i] == '_' || str[i] == '-' || str[i] == '.'))
> > + goto bad_str;
> > +
> > }
> >
> > str[len] = '\0';
> > *strp = str;
> > return 0;
> > +
> > +bad_str:
> > + kfree(str);
> > + return rc;
> > }
> >
> > static int perm_read(struct policydb *p, struct symtab *s, struct policy_file *fp)
> > @@ -1269,7 +1286,7 @@ static int perm_read(struct policydb *p, struct symtab *s, struct policy_file *f
> > if (perdatum->value < 1 || perdatum->value > 32)
> > goto bad;
> >
> > - rc = str_read(&key, GFP_KERNEL, fp, len);
> > + rc = str_read(&key, GFP_KERNEL, fp, len, STR_IDENTIFIER, 128);
> > if (rc)
> > goto bad;
> >
> > @@ -1315,7 +1332,7 @@ static int common_read(struct policydb *p, struct symtab *s, struct policy_file
> > goto bad;
> > comdatum->permissions.nprim = le32_to_cpu(buf[2]);
> >
> > - rc = str_read(&key, GFP_KERNEL, fp, len);
> > + rc = str_read(&key, GFP_KERNEL, fp, len, STR_IDENTIFIER, 128);
> > if (rc)
> > goto bad;
> >
> > @@ -1552,12 +1569,12 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
> >
> > ncons = le32_to_cpu(buf[5]);
> >
> > - rc = str_read(&key, GFP_KERNEL, fp, len);
> > + rc = str_read(&key, GFP_KERNEL, fp, len, STR_IDENTIFIER, 128);
> > if (rc)
> > goto bad;
> >
> > if (len2) {
> > - rc = str_read(&cladatum->comkey, GFP_KERNEL, fp, len2);
> > + rc = str_read(&cladatum->comkey, GFP_KERNEL, fp, len2, STR_IDENTIFIER, 128);
> > if (rc)
> > goto bad;
> >
> > @@ -1691,7 +1708,7 @@ static int role_read(struct policydb *p, struct symtab *s, struct policy_file *f
> > if (p->policyvers >= POLICYDB_VERSION_BOUNDARY)
> > role->bounds = le32_to_cpu(buf[2]);
> >
> > - rc = str_read(&key, GFP_KERNEL, fp, len);
> > + rc = str_read(&key, GFP_KERNEL, fp, len, STR_IDENTIFIER, 128);
> > if (rc)
> > goto bad;
> >
> > @@ -1758,7 +1775,7 @@ static int type_read(struct policydb *p, struct symtab *s, struct policy_file *f
> > typdatum->primary = le32_to_cpu(buf[2]);
> > }
> >
> > - rc = str_read(&key, GFP_KERNEL, fp, len);
> > + rc = str_read(&key, GFP_KERNEL, fp, len, STR_IDENTIFIER, 1024);
> > if (rc)
> > goto bad;
> >
> > @@ -1822,7 +1839,7 @@ static int user_read(struct policydb *p, struct symtab *s, struct policy_file *f
> > if (p->policyvers >= POLICYDB_VERSION_BOUNDARY)
> > usrdatum->bounds = le32_to_cpu(buf[2]);
> >
> > - rc = str_read(&key, GFP_KERNEL, fp, len);
> > + rc = str_read(&key, GFP_KERNEL, fp, len, STR_IDENTIFIER, 128);
> > if (rc)
> > goto bad;
> >
> > @@ -1871,7 +1888,7 @@ static int sens_read(struct policydb *p, struct symtab *s, struct policy_file *f
> > goto bad;
> > levdatum->isalias = val;
> >
> > - rc = str_read(&key, GFP_KERNEL, fp, len);
> > + rc = str_read(&key, GFP_KERNEL, fp, len, STR_IDENTIFIER, 128);
> > if (rc)
> > goto bad;
> >
> > @@ -1914,7 +1931,7 @@ static int cat_read(struct policydb *p, struct symtab *s, struct policy_file *fp
> > goto bad;
> > catdatum->isalias = val;
> >
> > - rc = str_read(&key, GFP_KERNEL, fp, len);
> > + rc = str_read(&key, GFP_KERNEL, fp, len, STR_IDENTIFIER, 32);
> > if (rc)
> > goto bad;
>
> The category restriction is more tight than the sensitivity one because
> a context may have many categories? I guess that makes sense, but it
> feels counterintuitive from a user perspective, because I feel like
> users tend to think of categories and sensitivities as essentially the
> same thing. Would dropping the sensitivity limit to 32 to match the
> category limit make sense?
Yes, I'll change the limit for sensitivities to 32 in v2.
> Is there a more strict limit on the number of categories a context can
> have than the U32_MAX from symtab.nprim? Because that will allow
> exceeding the page size using too many categories regardless of length
> distinctions, which is a concern if the motivation here is about
> potential future untrusted policy loaders in a namespaced environment.
It seems the limit of categories a context can have in the total
number of categories defined in the policy, which seems to be U32_MAX
(or one or two less).
But even today in the common policies on can reach this limit, e.g. via
$ for ((j=1; j<1000; j++)); do echo "limit $j";
ctx=system_u:system_r:init_t:s0:c0; for ((i=1; i<j; i++)); do
ctx+=,c$i; done; echo -n $ctx > /sys/fs/selinux/context || j=1000;
done;
For me with more than 834 categories (which have a maximum identifier
length of 4 (c833)) the context hits the page size limit.
So the maximum length of 32 is an attempt to minimize the practical likelihood.
> -Daniel
>
Powered by blists - more mailing lists