Received: by 2002:ac0:a5a7:0:0:0:0:0 with SMTP id m36-v6csp516481imm; Fri, 10 Aug 2018 16:02:46 -0700 (PDT) X-Google-Smtp-Source: AA+uWPwD1SCSOym6yaEUxlos5HYsSNPItJbzk21M/XQasXfOZCinK/MyB7saUTcfrGbPIlXV0IBT X-Received: by 2002:a17:902:7e06:: with SMTP id b6-v6mr7792592plm.230.1533942166404; Fri, 10 Aug 2018 16:02:46 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1533942166; cv=none; d=google.com; s=arc-20160816; b=xKK+MLf9ysGRO0Ltk9DIOpKeKSYcqL1vT7kZMuMdgB83k7em394k730KTiVqyZoS+d MlZVqAE9vvhmulaJNUcPlZFkjEbKI9pHJK95HjFNI1j8kmnCTEbOwlI7Rk8o62UZDRG0 iKQnHYZLgw+rfu10uME/5Bo7h2MCb78iaFijllzfOLDzKewQkTvF7jktikTPyB1FxntH ls2hE0jgOf1cTfwcvcB8C4oqnj2SeXmVld0ocHLT6V+odBGDGcbz0fplnp9O/qWLMKxF 9/VqG019Id9dpsS/ThCmWDDwjZ32JE9wGjQY2CT0g3QjGzw+TdvNDTWlRKcttam8NG0g NMoQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:dkim-signature :arc-authentication-results; bh=ty6YhRY2fNMR0qCfXsybxBJ+8pKYaa+LZmETNWj0MKQ=; b=jtSQ2ONG2Pd617561MX/APJJtDGXC3/90y01k5+Bau+h8cBaG0qyBHkKpitnfkg6z/ dad/axr+QmiQfY3X4mdb60voJw7KevhvXe5b/t9/alAujMyxwd2aFkPgXdGlwrFK+Ytw pthuqI8x9ENwKVgyf6TsXMkxmGwZ0OCOAF5Om8npLuRT4Fo9pZlx0fAcUm+DZL4859rT ssYKyJXiHAHN5e4YnkHrhgTeqYtYoRfk6q3crHd1A3cMlavl0meDzpL9Lo5q1WqdqkxR TmgroPnMwFsn9gIGiDb7ah9cWdhBuumuxbfN4JLDxlNb39dwFjXUcEycbcDZHympb/fd BYBw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@google.com header.s=20161025 header.b=HefIuFJc; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id v127-v6si10763984pgv.89.2018.08.10.16.02.28; Fri, 10 Aug 2018 16:02:46 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@google.com header.s=20161025 header.b=HefIuFJc; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727129AbeHKBd0 (ORCPT + 99 others); Fri, 10 Aug 2018 21:33:26 -0400 Received: from mail-oi0-f68.google.com ([209.85.218.68]:42443 "EHLO mail-oi0-f68.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727026AbeHKBd0 (ORCPT ); Fri, 10 Aug 2018 21:33:26 -0400 Received: by mail-oi0-f68.google.com with SMTP id b16-v6so18443590oic.9 for ; Fri, 10 Aug 2018 16:01:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=mime-version:references:in-reply-to:from:date:message-id:subject:to :cc; bh=ty6YhRY2fNMR0qCfXsybxBJ+8pKYaa+LZmETNWj0MKQ=; b=HefIuFJcEQOADqc/fbq3itU0MWml6deY2bP+J+Jwo226WD0H9ovHCugeent+0rcKPl anLjOr3RLjubkm9wR399pxC9E1ky4qc7SzY6vo55igEscvuj9UyWKr+uELy0v0vPUPY0 d9o5vXoismzfW4yoDNeJ7ZzyLBkEqZwsRE07T05XSjqKpcrasC6hzHzg8K62GI3N7o5L xv+Kprkj6g792aYJLFfL3yttjqxd5epuuy2GkVqxPWhotratbVTZEbVBji3XKmriD6AZ f62NPs+onbxA1vrdSrIKBYM1RC8nuZhDLVNS7QU+tRfRbFeUNqVP9+lIDLsk6hmDzxRr j/9g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc; bh=ty6YhRY2fNMR0qCfXsybxBJ+8pKYaa+LZmETNWj0MKQ=; b=DOUIUxM/NNA/5KQgqH6eA7i+m8lMfVktLYY3WVzib6+J770PJjaO98XjsxwrAuCdxG 28li7qVSIPWHcLpZWRo96/uV5c0yBGRW7v45agNVAJBeWsVYkRHJbbvAYhcSp+MC9iI1 WAu0lUUMupOEIr8h5lrJ5+jxVYBQHtADJfVCxmbwaIFZkiWf9kLVwNnror2DRfRj5+dq RYM4yfNINhKPk88H2JLfnZ6UsHTO7xJM2R6poCBHz3E5Etrada+HhEDKFiS5isdS9Oyt knLfCyDmlXIUerDhkDo/d0HOhQEAvqRanDvI5lYNuAvJ7enoXhfX9621y6sTy29eslnf GKJw== X-Gm-Message-State: AOUpUlFW4YQT5dMBGz8v0qWpXo29GrD6/m9WCL30ZepylXf5gbUmZs5X 0FQGgcTHlxrHekM8nLOAoIlUmZuRdDsVQs6Eod68tw== X-Received: by 2002:aca:6044:: with SMTP id u65-v6mr9260719oib.323.1533942090444; Fri, 10 Aug 2018 16:01:30 -0700 (PDT) MIME-Version: 1.0 References: <20180806211932.198488-1-jannh@google.com> In-Reply-To: From: Jann Horn Date: Sat, 11 Aug 2018 01:01:03 +0200 Message-ID: Subject: Re: [PATCH] selinux: refactor mls_context_to_sid() and make it stricter To: pmoore@redhat.com Cc: Paul Moore , Stephen Smalley , Eric Paris , selinux@tycho.nsa.gov, James Morris , "Serge E. Hallyn" , linux-security-module , kernel list Content-Type: text/plain; charset="UTF-8" Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Thu, Aug 9, 2018 at 4:07 AM Paul Moore wrote: > > On Wed, Aug 8, 2018 at 9:56 PM Paul Moore wrote: > > > > On Mon, Aug 6, 2018 at 5:19 PM Jann Horn wrote: > > > > > > The intended behavior change for this patch is to reject any MLS strings > > > that contain (trailing) garbage if p->mls_enabled is true. > > > > > > As suggested by Paul Moore, change mls_context_to_sid() so that the two > > > parts of the range are extracted before the rest of the parsing. Because > > > now we don't have to scan for two different separators simultaneously > > > everywhere, we can actually switch to strchr() everywhere instead of the > > > open-coded loops that scan for two separators at once. > > > > > > mls_context_to_sid() used to signal how much of the input string was parsed > > > by updating `*scontext`. However, there is actually no case in which > > > mls_context_to_sid() only parses a subset of the input and still returns > > > a success (other than the buggy case with a second '-' in which it > > > incorrectly claims to have consumed the entire string). Turn `scontext` > > > into a simple pointer argument and stop redundantly checking whether the > > > entire input was consumed in string_to_context_struct(). This also lets us > > > remove the `scontext_len` argument from `string_to_context_struct()`. > > > > > > Signed-off-by: Jann Horn > > > --- > > > Refactored version of > > > "[PATCH] selinux: stricter parsing in mls_context_to_sid()" based on > > > Paul's comments. WDYT? > > > I've thrown some inputs at it, and it seems to work. > > > > > > security/selinux/ss/mls.c | 178 ++++++++++++++------------------- > > > security/selinux/ss/mls.h | 2 +- > > > security/selinux/ss/services.c | 12 +-- > > > 3 files changed, 82 insertions(+), 110 deletions(-) > > > > Thanks for the rework, this looks much better than what we currently > > have. I have some comments/questions below ... > > > > > diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c > > > index 39475fb455bc..2fe459df3c85 100644 > > > --- a/security/selinux/ss/mls.c > > > +++ b/security/selinux/ss/mls.c > > > @@ -218,9 +218,7 @@ int mls_context_isvalid(struct policydb *p, struct context *c) > > > /* > > > * Set the MLS fields in the security context structure > > > * `context' based on the string representation in > > > - * the string `*scontext'. Update `*scontext' to > > > - * point to the end of the string representation of > > > - * the MLS fields. > > > + * the string `scontext'. > > > * > > > * This function modifies the string in place, inserting > > > * NULL characters to terminate the MLS fields. > > > @@ -235,22 +233,21 @@ int mls_context_isvalid(struct policydb *p, struct context *c) > > > */ > > > int mls_context_to_sid(struct policydb *pol, > > > char oldc, > > > - char **scontext, > > > + char *scontext, > > > struct context *context, > > > struct sidtab *s, > > > u32 def_sid) > > > { > > > - > > > - char delim; > > > - char *scontextp, *p, *rngptr; > > > + char *sensitivity, *cur_cat, *next_cat, *rngptr; > > > struct level_datum *levdatum; > > > struct cat_datum *catdatum, *rngdatum; > > > - int l, rc = -EINVAL; > > > + int l, rc, i; > > > + char *rangep[2]; > > > > > > if (!pol->mls_enabled) { > > > - if (def_sid != SECSID_NULL && oldc) > > > - *scontext += strlen(*scontext) + 1; > > > - return 0; > > > + if ((def_sid != SECSID_NULL && oldc) || (*scontext) == '\0') > > > + return 0; > > > + return -EINVAL; > > > > Why are we simply not always return 0 in the case where MLS is not > > enabled in the policy? The mls_context_to_sid() is pretty much a > > no-op in this case (even more so with your pat regardless of input and > > I worry that returning EINVAL here is a deviation from current > > behavior which could cause problems. > > Sorry, I was rephrasing the text above when I accidentally hit send. > While my emails are generally a good source of typos, the above is > pretty bad, so let me try again ... > > Why are we simply not always returning 0 in the case where MLS is not > enabled in the policy? The mls_context_to_sid() function is pretty > much a no-op in this case regardless of input and I worry that > returning EINVAL here is a deviation from current behavior which could > cause problems. Reverse call graph of mls_context_to_sid(): mls_context_to_sid() mls_from_string() selinux_audit_rule_init() [...] string_to_context_struct() security_context_to_sid_core() [...] convert_context() [...] string_to_context_struct() currently has the following code: rc = mls_context_to_sid(pol, oldc, &p, ctx, sidtabp, def_sid); if (rc) goto out; rc = -EINVAL; if ((p - scontext) < scontext_len) goto out; In my patch, I tried to preserve the behavior of string_to_context_struct(), at the expense of slightly changing the behavior of selinux_audit_rule_init(). But if you think that's a bad idea or unnecessary, say so and I'll change it. > > > } > > > > > > /* > > > @@ -261,113 +258,94 @@ int mls_context_to_sid(struct policydb *pol, > > > struct context *defcon; > > > > > > if (def_sid == SECSID_NULL) > > > - goto out; > > > + return -EINVAL; > > > > > > defcon = sidtab_search(s, def_sid); > > > if (!defcon) > > > - goto out; > > > + return -EINVAL; > > > > > > - rc = mls_context_cpy(context, defcon); > > > - goto out; > > > + return mls_context_cpy(context, defcon); > > > } > > > > > > - /* Extract low sensitivity. */ > > > - scontextp = p = *scontext; > > > - while (*p && *p != ':' && *p != '-') > > > - p++; > > > - > > > - delim = *p; > > > - if (delim != '\0') > > > - *p++ = '\0'; > > > + /* > > > + * If we're dealing with a range, figure out where the two parts > > > + * of the range begin. > > > + */ > > > + rangep[0] = scontext; > > > + rangep[1] = strchr(scontext, '-'); > > > + if (rangep[1]) { > > > + rangep[1][0] = '\0'; > > > + rangep[1]++; > > > + } > > > > > > + /* For each part of the range: */ > > > for (l = 0; l < 2; l++) { > > > - levdatum = hashtab_search(pol->p_levels.table, scontextp); > > > - if (!levdatum) { > > > - rc = -EINVAL; > > > - goto out; > > > - } > > > + /* Split sensitivity and category set. */ > > > + sensitivity = rangep[l]; > > > + if (sensitivity == NULL) > > > + break; > > > + next_cat = strchr(sensitivity, ':'); > > > + if (next_cat) > > > + *(next_cat++) = '\0'; > > > > > > + /* Parse sensitivity. */ > > > + levdatum = hashtab_search(pol->p_levels.table, sensitivity); > > > + if (!levdatum) > > > + return -EINVAL; > > > context->range.level[l].sens = levdatum->level->sens; > > > > > > - if (delim == ':') { > > > - /* Extract category set. */ > > > - while (1) { > > > - scontextp = p; > > > - while (*p && *p != ',' && *p != '-') > > > - p++; > > > - delim = *p; > > > - if (delim != '\0') > > > - *p++ = '\0'; > > > - > > > - /* Separate into range if exists */ > > > - rngptr = strchr(scontextp, '.'); > > > - if (rngptr != NULL) { > > > - /* Remove '.' */ > > > - *rngptr++ = '\0'; > > > - } > > > + /* Extract category set. */ > > > + while (next_cat != NULL) { > > > + cur_cat = next_cat; > > > + next_cat = strchr(next_cat, ','); > > > + if (next_cat != NULL) > > > + *(next_cat++) = '\0'; > > > + > > > + /* Separate into range if exists */ > > > + rngptr = strchr(cur_cat, '.'); > > > + if (rngptr != NULL) { > > > + /* Remove '.' */ > > > > On the chance you need to respin this patch, you can probably get rid > > of the above comment and the if-body braces; we don't have "Remove X" > > comments in other similar places in this function. > > > > > + *rngptr++ = '\0'; > > > + } > > > > > > - catdatum = hashtab_search(pol->p_cats.table, > > > - scontextp); > > > - if (!catdatum) { > > > - rc = -EINVAL; > > > - goto out; > > > - } > > > + catdatum = hashtab_search(pol->p_cats.table, cur_cat); > > > + if (!catdatum) > > > + return -EINVAL; > > > > > > - rc = ebitmap_set_bit(&context->range.level[l].cat, > > > - catdatum->value - 1, 1); > > > - if (rc) > > > - goto out; > > > - > > > - /* If range, set all categories in range */ > > > - if (rngptr) { > > > - int i; > > > - > > > - rngdatum = hashtab_search(pol->p_cats.table, rngptr); > > > - if (!rngdatum) { > > > - rc = -EINVAL; > > > - goto out; > > > - } > > > - > > > - if (catdatum->value >= rngdatum->value) { > > > - rc = -EINVAL; > > > - goto out; > > > - } > > > - > > > - for (i = catdatum->value; i < rngdatum->value; i++) { > > > - rc = ebitmap_set_bit(&context->range.level[l].cat, i, 1); > > > - if (rc) > > > - goto out; > > > - } > > > - } > > > + rc = ebitmap_set_bit(&context->range.level[l].cat, > > > + catdatum->value - 1, 1); > > > + if (rc) > > > + return rc; > > > + > > > + /* If range, set all categories in range */ > > > + if (rngptr == NULL) > > > + continue; > > > + > > > + rngdatum = hashtab_search(pol->p_cats.table, rngptr); > > > + if (!rngdatum) > > > + return -EINVAL; > > > + > > > + if (catdatum->value >= rngdatum->value) > > > + return -EINVAL; > > > > > > - if (delim != ',') > > > - break; > > > + for (i = catdatum->value; i < rngdatum->value; i++) { > > > + rc = ebitmap_set_bit(&context->range.level[l].cat, i, 1); > > > + if (rc) > > > + return rc; > > > } > > > } > > > - if (delim == '-') { > > > - /* Extract high sensitivity. */ > > > - scontextp = p; > > > - while (*p && *p != ':') > > > - p++; > > > - > > > - delim = *p; > > > - if (delim != '\0') > > > - *p++ = '\0'; > > > - } else > > > - break; > > > } > > > > > > - if (l == 0) { > > > + /* If we didn't see a '-', the range start is also the range end. */ > > > + if (rangep[1] == NULL) { > > > context->range.level[1].sens = context->range.level[0].sens; > > > rc = ebitmap_cpy(&context->range.level[1].cat, > > > &context->range.level[0].cat); > > > if (rc) > > > - goto out; > > > + return rc; > > > } > > > - *scontext = ++p; > > > - rc = 0; > > > -out: > > > - return rc; > > > + > > > + return 0; > > > > In the case where we have a MLS policy loaded (pol->mls_enabled != 0) > > and scontext is empty (scontext[0] = '\0'), we could end up returning > > 0 couldn't we? It seems like we might want a quick check for this > > before we parse the low/high portions of the field into the rangep > > array. > > > > > } > > > > > > /* > > > @@ -379,21 +357,19 @@ int mls_context_to_sid(struct policydb *pol, > > > int mls_from_string(struct policydb *p, char *str, struct context *context, > > > gfp_t gfp_mask) > > > { > > > - char *tmpstr, *freestr; > > > + char *tmpstr; > > > int rc; > > > > > > if (!p->mls_enabled) > > > return -EINVAL; > > > > > > - /* we need freestr because mls_context_to_sid will change > > > - the value of tmpstr */ > > > - tmpstr = freestr = kstrdup(str, gfp_mask); > > > + tmpstr = kstrdup(str, gfp_mask); > > > if (!tmpstr) { > > > rc = -ENOMEM; > > > } else { > > > - rc = mls_context_to_sid(p, ':', &tmpstr, context, > > > + rc = mls_context_to_sid(p, ':', tmpstr, context, > > > NULL, SECSID_NULL); > > > - kfree(freestr); > > > + kfree(tmpstr); > > > } > > > > > > return rc; > > > diff --git a/security/selinux/ss/mls.h b/security/selinux/ss/mls.h > > > index 9a3ff7af70ad..67093647576d 100644 > > > --- a/security/selinux/ss/mls.h > > > +++ b/security/selinux/ss/mls.h > > > @@ -34,7 +34,7 @@ int mls_level_isvalid(struct policydb *p, struct mls_level *l); > > > > > > int mls_context_to_sid(struct policydb *p, > > > char oldc, > > > - char **scontext, > > > + char *scontext, > > > struct context *context, > > > struct sidtab *s, > > > u32 def_sid); > > > diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c > > > index dd2ceec06fef..9212d4dd817a 100644 > > > --- a/security/selinux/ss/services.c > > > +++ b/security/selinux/ss/services.c > > > @@ -1367,7 +1367,6 @@ int security_sid_to_context_force(struct selinux_state *state, u32 sid, > > > static int string_to_context_struct(struct policydb *pol, > > > struct sidtab *sidtabp, > > > char *scontext, > > > - u32 scontext_len, > > > struct context *ctx, > > > u32 def_sid) > > > { > > > @@ -1428,15 +1427,12 @@ static int string_to_context_struct(struct policydb *pol, > > > > > > ctx->type = typdatum->value; > > > > > > - rc = mls_context_to_sid(pol, oldc, &p, ctx, sidtabp, def_sid); > > > + rc = mls_context_to_sid(pol, oldc, p, ctx, sidtabp, def_sid); > > > if (rc) > > > goto out; > > > > > > - rc = -EINVAL; > > > - if ((p - scontext) < scontext_len) > > > - goto out; > > > - > > > /* Check the validity of the new context. */ > > > + rc = -EINVAL; > > > if (!policydb_context_isvalid(pol, ctx)) > > > goto out; > > > rc = 0; > > > @@ -1491,7 +1487,7 @@ static int security_context_to_sid_core(struct selinux_state *state, > > > policydb = &state->ss->policydb; > > > sidtab = &state->ss->sidtab; > > > rc = string_to_context_struct(policydb, sidtab, scontext2, > > > - scontext_len, &context, def_sid); > > > + &context, def_sid); > > > if (rc == -EINVAL && force) { > > > context.str = str; > > > context.len = strlen(str) + 1; > > > @@ -1959,7 +1955,7 @@ static int convert_context(u32 key, > > > goto out; > > > > > > rc = string_to_context_struct(args->newp, NULL, s, > > > - c->len, &ctx, SECSID_NULL); > > > + &ctx, SECSID_NULL); > > > kfree(s); > > > if (!rc) { > > > printk(KERN_INFO "SELinux: Context %s became valid (mapped).\n", > > > -- > > > 2.18.0.597.ga71716f1ad-goog > > > > -- > > paul moore > > www.paul-moore.com > > -- > > To unsubscribe from this list: send the line "unsubscribe linux-security-module" in > > the body of a message to majordomo@vger.kernel.org > > More majordomo info at http://vger.kernel.org/majordomo-info.html > > > > -- > paul moore > security @ redhat