Received: by 2002:ac0:a5a7:0:0:0:0:0 with SMTP id m36-v6csp1491461imm; Wed, 8 Aug 2018 18:57:29 -0700 (PDT) X-Google-Smtp-Source: AA+uWPxfV/DbbsJgFNJx9Rh32VOtimlO1pHQyrD+/X8UjxzLsHHdnzbWKqc8L5pvlwaAeJBQ82Of X-Received: by 2002:a17:902:ac1:: with SMTP id 59-v6mr186773plp.18.1533779849071; Wed, 08 Aug 2018 18:57:29 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1533779849; cv=none; d=google.com; s=arc-20160816; b=dzo4t4G/It0O1WaLmJBjwrpHntb38ZGFYVPl47CziEtrYfQ8ucMLNH7JoftUnRsT4O m3tlJEgz2x986o4rq9EXYAy/DtywkvLlhKJMZhd9AeLSZQBANReCnsBdNCim3azKTzll Nmsby2wVUH10q7cs3D6fjw3p0Vl+Dhq6JfeVOMScp6PftXJ1yCAGVNP6hQA4lPSaoKfk erOsvMhdfsukUDlCiOvtSxIqI2RuabXVVvHoTHIcz7BvsgyNs5Ow5YmkcGHS5fX/fxI4 VAGLhUyHBfEUpIT8WSt9OPaRu1qfPczKC3/hCss0vh+j8eHdgYzj8dfTGd8CqIn4cXmo huTQ== 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=T5Y2NiLlVTnAqVlItfAwBzFlo2LkcRKwvO+WSSHJrAM=; b=z4Qi0xfOjLqroe1WO/aExFfCHYarTGLoUF0w/wXdAK3XUYDOO18rUT5te3TecX4o6n Qop/Mf9l9FONfpRe7kLebec1t8GGc4nOWaOxK4Y2i2IFF6idOtglMqCxOtOH5g2LrDoz 7BN60OCEByyMWORxqTd7i6zqrGWXExzozfjUXXxK4B2DX5XhkSDxqeO3i0BuZ3PCeHrB 2vjwD8j3XJMUL0gRStfvpRQxWhdBsx74y6568DLm+P6ouLs3WpYjbqZfyCDJWAnhzV2W QlOTZcJFO2yRFBuEjEFv18Ayhnq3BLjcfPKr08nAxUOtkKmEmtBGWf4ks3eo31YWcbcI Bv5w== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@paul-moore-com.20150623.gappssmtp.com header.s=20150623 header.b=XTWXV7Jq; 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 Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id w3-v6si4388675plq.61.2018.08.08.18.57.14; Wed, 08 Aug 2018 18:57:29 -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=@paul-moore-com.20150623.gappssmtp.com header.s=20150623 header.b=XTWXV7Jq; 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 Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727158AbeHIESp (ORCPT + 99 others); Thu, 9 Aug 2018 00:18:45 -0400 Received: from mail-lf1-f68.google.com ([209.85.167.68]:37588 "EHLO mail-lf1-f68.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725757AbeHIESp (ORCPT ); Thu, 9 Aug 2018 00:18:45 -0400 Received: by mail-lf1-f68.google.com with SMTP id j8-v6so2966000lfb.4 for ; Wed, 08 Aug 2018 18:56:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=paul-moore-com.20150623.gappssmtp.com; s=20150623; h=mime-version:references:in-reply-to:from:date:message-id:subject:to :cc; bh=T5Y2NiLlVTnAqVlItfAwBzFlo2LkcRKwvO+WSSHJrAM=; b=XTWXV7Jq61uJx5OLmErCoTgCHs5bmVm2QcaWuvnBeNQ8G/pqaPENBMIBlxNt9Q6gXs ZwhSQbnzaxIxlAeNyQr5c8nqncQKhPtJXA4Mcsc+Vt57qLjwtdlw6lFhfqQZnnERphgo RVJMLYCfhLkhV4S4SQfnk7Hc55N7BegPgJXyekslR6PXgzrKQp4nmPIbbJfEett1igwZ IxtApA3EaKzLZeSMcQKt+ZwqtJWfosbgsYxmuHGnBs3wi7IohO8/BP3yIdu5PWLGdyLv U5/4xrDAk5TCgzAlf3Sq/QEWv13chnx5FYc4jPQuKfRjYLqL7Z0VRXrDcYk0tRNgKPum bLVg== 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=T5Y2NiLlVTnAqVlItfAwBzFlo2LkcRKwvO+WSSHJrAM=; b=dq9JHLRTYtnk8sbWIEg3D2N+KlUr6u/9U+rYPKAfJlzNJsx7sMmnyYhIq9GMjq6un+ pzpRjuE0EbSg5a8e6wwtaxgv/Ww1+BF4GHOtZoNpfXMl6V5uYubUTHPZmM8c6gtEu1x/ f19cxteWA1nYVWUVVmUtOwFESCwUYPblK7VZdKrLvMIAzO8fN9/wWhI53eAkSyUDCgsX LTLVNWAxo0rrkbrgsOOlkW5HGYKNOsDUnIN1Ajf7Cv0qMm72767ViO8qFfop+AyijwW3 AH/SIscjXcyAma6IqS1fHIELVVgi1sZtur/M7xTJxl6+X0D86sOvv5r1A8sbYwkHpucb 6J1A== X-Gm-Message-State: AOUpUlFP0VFL+/82jXsg+moc25qR0/NuNcXLYl/TbQb95WHMlfWKtUJW yz3VGJKwv2o19rXfgpc68awNZblsxU8kpw8Lq4G4 X-Received: by 2002:a19:c403:: with SMTP id u3-v6mr66685lff.87.1533779781738; Wed, 08 Aug 2018 18:56:21 -0700 (PDT) MIME-Version: 1.0 References: <20180806211932.198488-1-jannh@google.com> In-Reply-To: <20180806211932.198488-1-jannh@google.com> From: Paul Moore Date: Wed, 8 Aug 2018 21:56:10 -0400 Message-ID: Subject: Re: [PATCH] selinux: refactor mls_context_to_sid() and make it stricter To: jannh@google.com Cc: Stephen Smalley , Eric Paris , selinux@tycho.nsa.gov, James Morris , serge@hallyn.com, linux-security-module@vger.kernel.org, linux-kernel@vger.kernel.org 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 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. > } > > /* > @@ -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