From patchwork Wed May 31 11:29:24 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Juraj Marcin X-Patchwork-Id: 13262005 X-Patchwork-Delegate: paul@paul-moore.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 48EBBC77B73 for ; Wed, 31 May 2023 11:32:41 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230341AbjEaLcj (ORCPT ); Wed, 31 May 2023 07:32:39 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:41600 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232156AbjEaLch (ORCPT ); Wed, 31 May 2023 07:32:37 -0400 Received: from sender11-of-o52.zoho.eu (sender11-of-o52.zoho.eu [31.186.226.238]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 24B0F18F for ; Wed, 31 May 2023 04:32:25 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1685532740; cv=none; d=zohomail.eu; s=zohoarc; b=jIcP8HBvh+hRGcxqttkN/RqbneRYuQo/aR0E11WWQ3FSsc4rQet9hFVDdUAV4x15FLRW6PvnfQeboSgjhOKQ6oPdtgFVArjYshTNOdl3WfdtbXAksZN5pM7gBJjqziqEwjSIQclJ0L12EGDskLQW/7Q6B037eq5DKFIj/9SRnro= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.eu; s=zohoarc; t=1685532740; h=Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:To; bh=ItTSBFytDvSRgOXEUTTZS36SFbs/HssU43x9M9D8vzM=; b=TgXYHXGRWwziDSL+R1P9ETx4r+5JFgbap5rZwT5g9ia1DSkxNOsCKW6c5tv4elzjzN19ZS0c0sksZxF6/XdJk+DzuCXXFi3vE8bv9uVzjkArzI8n/7SC7W6de1fTeQhMufIVdowEkK46JSK6SSidNRLW98qvuXjKfb9iMGNekZM= ARC-Authentication-Results: i=1; mx.zohomail.eu; dkim=pass header.i=jurajmarcin.com; spf=pass smtp.mailfrom=juraj@jurajmarcin.com; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1685532740; s=zoho; d=jurajmarcin.com; i=juraj@jurajmarcin.com; h=From:From:To:To:Cc:Cc:Subject:Subject:Date:Date:Message-Id:Message-Id:In-Reply-To:References:MIME-Version:Content-Transfer-Encoding:Reply-To; bh=ItTSBFytDvSRgOXEUTTZS36SFbs/HssU43x9M9D8vzM=; b=PZUG0fDY08P5Q0AilSHhLNaHfE+mYnmylXmityH67Uw0UnDkygEZcCbaQ2MxKA7Y SaHShPcJfOkRnWmlHOLg2sPnD0zYj1hN0Q062KkTUq7M5G7CrHw0w/JxkRTZ0g51XIC MYMAKPh4u6R8jMFgoOT8ZtCkd1WDInaz/dNgtFFw= Received: from morty01.jurajmarcin.com (129.159.244.31 [129.159.244.31]) by mx.zoho.eu with SMTPS id 1685532739281307.85207014981677; Wed, 31 May 2023 13:32:19 +0200 (CEST) Received: from jmarcin-t14s-01.redhat.com (unknown [147.251.183.113]) by morty01.jurajmarcin.com (Postfix) with ESMTPSA id AAD762081F76; Wed, 31 May 2023 11:32:18 +0000 (UTC) From: Juraj Marcin To: Paul Moore Cc: Stephen Smalley , selinux@vger.kernel.org, Ondrej Mosnacek Subject: [PATCH 1/5] selinux: move transition to separate structure in avtab_datum Date: Wed, 31 May 2023 13:29:24 +0200 Message-Id: <20230531112927.1957093-2-juraj@jurajmarcin.com> In-Reply-To: <20230531112927.1957093-1-juraj@jurajmarcin.com> References: <20230531112927.1957093-1-juraj@jurajmarcin.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: selinux@vger.kernel.org This is a preparation to move filename transitions to be part of the avtab data structure. To do that, we first need to create space for it in the avtab_datum structure which holds the rule for certain combination of stype, ttype and tclass. As only type transitions have a special variant that uses a filename, it would be suboptimal to add a (mostly empty) pointer to some structure to all avtab rules. Therefore, this patch adds a new structure to the union in avtab_datum and moves the otype of the transition to this structure. In the next patch, this structure will also hold filename transitions for the combination of stype, ttype and tclass. As the union already contains a pointer, the size of avtab_datum does not increase. The only trade-off is that each transition requires at least 4 more bytes and structure allocation. Reviewed-by: Ondrej Mosnacek Signed-off-by: Juraj Marcin --- security/selinux/ss/avtab.c | 60 ++++++++++++++++++++++++++++++---- security/selinux/ss/avtab.h | 7 +++- security/selinux/ss/services.c | 5 ++- 3 files changed, 63 insertions(+), 9 deletions(-) diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c index 6766edc0fe68..a7f348e4509d 100644 --- a/security/selinux/ss/avtab.c +++ b/security/selinux/ss/avtab.c @@ -24,6 +24,7 @@ #include "policydb.h" static struct kmem_cache *avtab_node_cachep __ro_after_init; +static struct kmem_cache *avtab_trans_cachep __ro_after_init; static struct kmem_cache *avtab_xperms_cachep __ro_after_init; /* Based on MurmurHash3, written by Austin Appleby and placed in the @@ -71,6 +72,7 @@ avtab_insert_node(struct avtab *h, int hvalue, const struct avtab_key *key, const struct avtab_datum *datum) { struct avtab_node *newnode; + struct avtab_trans *trans; struct avtab_extended_perms *xperms; newnode = kmem_cache_zalloc(avtab_node_cachep, GFP_KERNEL); if (newnode == NULL) @@ -85,6 +87,14 @@ avtab_insert_node(struct avtab *h, int hvalue, } *xperms = *(datum->u.xperms); newnode->datum.u.xperms = xperms; + } else if (key->specified & AVTAB_TRANSITION) { + trans = kmem_cache_zalloc(avtab_trans_cachep, GFP_KERNEL); + if (!trans) { + kmem_cache_free(avtab_node_cachep, newnode); + return NULL; + } + *trans = *datum->u.trans; + newnode->datum.u.trans = trans; } else { newnode->datum.u.data = datum->u.data; } @@ -289,9 +299,13 @@ void avtab_destroy(struct avtab *h) while (cur) { temp = cur; cur = cur->next; - if (temp->key.specified & AVTAB_XPERMS) + if (temp->key.specified & AVTAB_XPERMS) { kmem_cache_free(avtab_xperms_cachep, temp->datum.u.xperms); + } else if (temp->key.specified & AVTAB_TRANSITION) { + kmem_cache_free(avtab_trans_cachep, + temp->datum.u.trans); + } kmem_cache_free(avtab_node_cachep, temp); } } @@ -407,6 +421,7 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, u32 items, items2, val, vers = pol->policyvers; struct avtab_key key; struct avtab_datum datum; + struct avtab_trans trans; struct avtab_extended_perms xperms; __le32 buf32[ARRAY_SIZE(xperms.perms.p)]; int i, rc; @@ -473,7 +488,16 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, for (i = 0; i < ARRAY_SIZE(spec_order); i++) { if (val & spec_order[i]) { key.specified = spec_order[i] | enabled; - datum.u.data = le32_to_cpu(buf32[items++]); + if (key.specified & AVTAB_TRANSITION) { + memset(&trans, 0, + sizeof(struct avtab_trans)); + trans.otype = + le32_to_cpu(buf32[items++]); + datum.u.trans = &trans; + } else { + datum.u.data = + le32_to_cpu(buf32[items++]); + } rc = insertf(a, &key, &datum, p); if (rc) return rc; @@ -543,6 +567,15 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, for (i = 0; i < ARRAY_SIZE(xperms.perms.p); i++) xperms.perms.p[i] = le32_to_cpu(buf32[i]); datum.u.xperms = &xperms; + } else if (key.specified & AVTAB_TRANSITION) { + memset(&trans, 0, sizeof(struct avtab_trans)); + rc = next_entry(buf32, fp, sizeof(u32)); + if (rc) { + pr_err("SELinux: avtab: truncated entry\n"); + return rc; + } + trans.otype = le32_to_cpu(*buf32); + datum.u.trans = &trans; } else { rc = next_entry(buf32, fp, sizeof(u32)); if (rc) { @@ -551,12 +584,19 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, } datum.u.data = le32_to_cpu(*buf32); } - if ((key.specified & AVTAB_TYPE) && - !policydb_type_isvalid(pol, datum.u.data)) { - pr_err("SELinux: avtab: invalid type\n"); - return -EINVAL; + if (key.specified & AVTAB_TRANSITION) { + if (!policydb_type_isvalid(pol, datum.u.trans->otype)) { + pr_err("SELinux: avtab: invalid transition type\n"); + return -EINVAL; + } + } else if (key.specified & AVTAB_TYPE) { + if (!policydb_type_isvalid(pol, datum.u.data)) { + pr_err("SELinux: avtab: invalid type\n"); + return -EINVAL; + } } - return insertf(a, &key, &datum, p); + rc = insertf(a, &key, &datum, p); + return rc; } static int avtab_insertf(struct avtab *a, const struct avtab_key *k, @@ -635,6 +675,9 @@ int avtab_write_item(struct policydb *p, const struct avtab_node *cur, void *fp) buf32[i] = cpu_to_le32(cur->datum.u.xperms->perms.p[i]); rc = put_entry(buf32, sizeof(u32), ARRAY_SIZE(cur->datum.u.xperms->perms.p), fp); + } else if (cur->key.specified & AVTAB_TRANSITION) { + buf32[0] = cpu_to_le32(cur->datum.u.trans->otype); + rc = put_entry(buf32, sizeof(u32), 1, fp); } else { buf32[0] = cpu_to_le32(cur->datum.u.data); rc = put_entry(buf32, sizeof(u32), 1, fp); @@ -673,6 +716,9 @@ void __init avtab_cache_init(void) avtab_node_cachep = kmem_cache_create("avtab_node", sizeof(struct avtab_node), 0, SLAB_PANIC, NULL); + avtab_trans_cachep = kmem_cache_create("avtab_trans", + sizeof(struct avtab_trans), + 0, SLAB_PANIC, NULL); avtab_xperms_cachep = kmem_cache_create("avtab_extended_perms", sizeof(struct avtab_extended_perms), 0, SLAB_PANIC, NULL); diff --git a/security/selinux/ss/avtab.h b/security/selinux/ss/avtab.h index d6742fd9c560..6c8eb7c379cf 100644 --- a/security/selinux/ss/avtab.h +++ b/security/selinux/ss/avtab.h @@ -47,6 +47,10 @@ struct avtab_key { u16 specified; /* what field is specified */ }; +struct avtab_trans { + u32 otype; /* default resulting type of the new object */ +}; + /* * For operations that require more than the 32 permissions provided by the avc * extended permissions may be used to provide 256 bits of permissions. @@ -69,7 +73,8 @@ struct avtab_extended_perms { struct avtab_datum { union { - u32 data; /* access vector or type value */ + u32 data; /* access vector, member or change value */ + struct avtab_trans *trans; /* transition value */ struct avtab_extended_perms *xperms; } u; }; diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 78946b71c1c1..8ed12406acba 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -1830,7 +1830,10 @@ static int security_compute_sid(u32 ssid, if (avdatum) { /* Use the type from the type transition/member/change rule. */ - newcontext.type = avdatum->u.data; + if (avkey.specified & AVTAB_TRANSITION) + newcontext.type = avdatum->u.trans->otype; + else + newcontext.type = avdatum->u.data; } /* if we have a objname this is a file trans check so check those rules */ From patchwork Wed May 31 11:29:25 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Juraj Marcin X-Patchwork-Id: 13262006 X-Patchwork-Delegate: paul@paul-moore.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id B4E0DC77B7C for ; Wed, 31 May 2023 11:32:47 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232156AbjEaLcq (ORCPT ); Wed, 31 May 2023 07:32:46 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:41682 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232888AbjEaLcp (ORCPT ); Wed, 31 May 2023 07:32:45 -0400 Received: from sender11-of-o52.zoho.eu (sender11-of-o52.zoho.eu [31.186.226.238]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 44ACC126 for ; Wed, 31 May 2023 04:32:34 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1685532748; cv=none; d=zohomail.eu; s=zohoarc; b=IP0vryHo9PaH7PkXneMiEe7SIsTOJZEGT59ef5DDbOyHJaipEX3aObTGmw1U8QJPyx0qUn6/0PVJfMlhttFGzJLaL3156IIkATxTHp3CYB249Dt1mSj/aTLH1UyICIqo7fZv0nhEnyIGj5DU4EWfGn/lffau7LTpEprFR6EBOmo= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.eu; s=zohoarc; t=1685532748; h=Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:To; bh=xIyLZyT2jksBJDRbhfNmmc5LCHTOBgUAKz7/tqnxopM=; b=kKR3HMRqcfx95cFTbHkpQkscH9ni9oZ+ii3JH6Y/m1HYXDOvUzvdV3YUXTA1kKr6YvbDn4eE2JwnvgnBbZ895wBdQBzoxykEyjAynxl75S10ow1GTjiSr04OAwdvWwBcivmRbg7sCiRbZanRr3eXZ81H8j3nqLAxQO6+fKhzHAo= ARC-Authentication-Results: i=1; mx.zohomail.eu; dkim=pass header.i=jurajmarcin.com; spf=pass smtp.mailfrom=juraj@jurajmarcin.com; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1685532748; s=zoho; d=jurajmarcin.com; i=juraj@jurajmarcin.com; h=From:From:To:To:Cc:Cc:Subject:Subject:Date:Date:Message-Id:Message-Id:In-Reply-To:References:MIME-Version:Content-Transfer-Encoding:Reply-To; bh=xIyLZyT2jksBJDRbhfNmmc5LCHTOBgUAKz7/tqnxopM=; b=efL1ls0MTcl6/fnmgN2K4yoxbjyxj0r98qbhGMLhZ2MVDsU2y7+aEh7mucaTSOcF 3rDg7gvSJ8E+HYDdEv8BNucWi9Si2xnZP+7KppK9GEuPsXNbpUhGcKswZK9lwQOn23U +jWYe2G3en4vvpWe/9rLcobt1wgWPqs919JHAC9A= Received: from morty01.jurajmarcin.com (129.159.244.31 [129.159.244.31]) by mx.zoho.eu with SMTPS id 1685532747100817.8019150565543; Wed, 31 May 2023 13:32:27 +0200 (CEST) Received: from jmarcin-t14s-01.redhat.com (unknown [147.251.183.113]) by morty01.jurajmarcin.com (Postfix) with ESMTPSA id 6D9482081F76; Wed, 31 May 2023 11:32:26 +0000 (UTC) From: Juraj Marcin To: Paul Moore Cc: Stephen Smalley , selinux@vger.kernel.org, Ondrej Mosnacek Subject: [PATCH 2/5] selinux: move filename transitions to avtab Date: Wed, 31 May 2023 13:29:25 +0200 Message-Id: <20230531112927.1957093-3-juraj@jurajmarcin.com> In-Reply-To: <20230531112927.1957093-1-juraj@jurajmarcin.com> References: <20230531112927.1957093-1-juraj@jurajmarcin.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: selinux@vger.kernel.org Currently, filename transitions are stored separately from other type enforcement rules. This leads to possibly sub-optimal performance and makes further improvements cumbersome. This patch adds a symbol table of filename transitions to the transition structure added to avtab in the previous patch. It also implements functions required for reading and writing filename transitions in kernel policy format and updates computation of new type to use filename transitions embedded in avtab. Last but not least, it updates the conflict check in the conditional avtab to account for empty transitions in the non-conditional avtab. These changes are expected to cause higher memory usage, as now there needs to be a filename transition structure for every stype. This patch effectively undoes most of the commit c3a276111ea2 ("selinux: optimize storage of filename transitions"), but this will be mitigated by providing support for matching prefix/suffix of the filename for filename transitions in future patches which will reduce to need to have so many of them. On the other hand, the changes do not significantly slow down the creation of new files. Kernel | Mem | Create test_tty | Create test_tty | osbench [1] | usage | (real time) | (kernel time) | create_files -----------+-------+-----------------+-----------------+-------------- reference | 155MB | 1.3440 ms/file | 1.0071 ms/file | 10.6507 us/file this patch | 198MB | 1.3912 ms/file | 1.0172 ms/file | 10.5567 us/file Create test_tty benchmark: mknod /dev/test_tty c 4 1 time for i in `seq 1 10000`; do mknod /dev/test_tty$i c 4 1 done This benchmark should simulate the worst case scenario as many filename transitions affect files created in the /dev directory. [1] https://github.com/mbitsnbites/osbench Reviewed-by: Ondrej Mosnacek Signed-off-by: Juraj Marcin --- security/selinux/ss/avtab.c | 516 ++++++++++++++++++++++++++++++ security/selinux/ss/avtab.h | 7 + security/selinux/ss/conditional.c | 6 +- security/selinux/ss/hashtab.h | 6 + security/selinux/ss/policydb.c | 399 +---------------------- security/selinux/ss/policydb.h | 25 +- security/selinux/ss/services.c | 54 +--- 7 files changed, 555 insertions(+), 458 deletions(-) diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c index a7f348e4509d..fe921e1586e5 100644 --- a/security/selinux/ss/avtab.c +++ b/security/selinux/ss/avtab.c @@ -22,6 +22,7 @@ #include #include "avtab.h" #include "policydb.h" +#include "hashtab.h" static struct kmem_cache *avtab_node_cachep __ro_after_init; static struct kmem_cache *avtab_trans_cachep __ro_after_init; @@ -286,6 +287,19 @@ avtab_search_node_next(struct avtab_node *node, int specified) return NULL; } +static int avtab_trans_destroy_helper(void *k, void *d, void *args) +{ + kfree(k); + kfree(d); + return 0; +} + +static void avtab_trans_destroy(struct avtab_trans *trans) +{ + hashtab_map(&trans->name_trans.table, avtab_trans_destroy_helper, NULL); + hashtab_destroy(&trans->name_trans.table); +} + void avtab_destroy(struct avtab *h) { int i; @@ -303,6 +317,7 @@ void avtab_destroy(struct avtab *h) kmem_cache_free(avtab_xperms_cachep, temp->datum.u.xperms); } else if (temp->key.specified & AVTAB_TRANSITION) { + avtab_trans_destroy(temp->datum.u.trans); kmem_cache_free(avtab_trans_cachep, temp->datum.u.trans); } @@ -587,6 +602,7 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, if (key.specified & AVTAB_TRANSITION) { if (!policydb_type_isvalid(pol, datum.u.trans->otype)) { pr_err("SELinux: avtab: invalid transition type\n"); + avtab_trans_destroy(&trans); return -EINVAL; } } else if (key.specified & AVTAB_TYPE) { @@ -596,6 +612,8 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, } } rc = insertf(a, &key, &datum, p); + if (rc && key.specified & AVTAB_TRANSITION) + avtab_trans_destroy(&trans); return rc; } @@ -656,6 +674,10 @@ int avtab_write_item(struct policydb *p, const struct avtab_node *cur, void *fp) int rc; unsigned int i; + if (cur->key.specified & AVTAB_TRANSITION && + !cur->datum.u.trans->otype) + return 0; + buf16[0] = cpu_to_le16(cur->key.source_type); buf16[1] = cpu_to_le16(cur->key.target_type); buf16[2] = cpu_to_le16(cur->key.target_class); @@ -723,3 +745,497 @@ void __init avtab_cache_init(void) sizeof(struct avtab_extended_perms), 0, SLAB_PANIC, NULL); } + +/* policydb filename transitions compatibility */ + +static int avtab_insert_filename_trans(struct avtab *a, + const struct avtab_key *key, + char *name, u32 otype) +{ + int rc; + struct avtab_node *node; + struct avtab_trans new_trans = {0}; + struct avtab_datum new_datum = {.u.trans = &new_trans}; + struct avtab_datum *datum; + u32 *otype_datum = NULL; + + datum = avtab_search(a, key); + if (!datum) { + /* + * insert is acctually unique, but with this function we can get + * the inserted node and therefore the datum + */ + node = avtab_insert_nonunique(a, key, &new_datum); + if (!node) + return -ENOMEM; + datum = &node->datum; + } + + if (hashtab_is_empty(&datum->u.trans->name_trans.table)) { + rc = symtab_init(&datum->u.trans->name_trans, 1 << 8); + if (rc) + return rc; + } + + otype_datum = kmalloc(sizeof(u32), GFP_KERNEL); + if (!otype_datum) + return -ENOMEM; + *otype_datum = otype; + + rc = symtab_insert(&datum->u.trans->name_trans, name, otype_datum); + if (rc) + kfree(otype_datum); + + return rc; +} + +static int filename_trans_read_item(struct avtab *a, void *fp) +{ + int rc; + __le32 buf32[4]; + u32 len, otype; + char *name = NULL; + struct avtab_key key; + + /* read length of the name */ + rc = next_entry(buf32, fp, sizeof(u32)); + if (rc) + return rc; + len = le32_to_cpu(buf32[0]); + + /* read the name */ + rc = str_read(&name, GFP_KERNEL, fp, len); + if (rc) + return rc; + + /* read stype, ttype, tclass and otype */ + rc = next_entry(buf32, fp, sizeof(u32) * 4); + if (rc) + goto bad; + + key.source_type = le32_to_cpu(buf32[0]); + key.target_type = le32_to_cpu(buf32[1]); + key.target_class = le32_to_cpu(buf32[2]); + key.specified = AVTAB_TRANSITION; + + otype = le32_to_cpu(buf32[3]); + + rc = avtab_insert_filename_trans(a, &key, name, otype); + if (rc) + goto bad; + + return rc; + +bad: + kfree(name); + return rc; +} + +static int filename_trans_comp_read_item(struct avtab *a, void *fp) +{ + int rc; + __le32 buf32[3]; + u32 len, ndatum, i, bit, otype; + char *name = NULL, *name_copy = NULL; + struct avtab_key key; + struct ebitmap stypes; + struct ebitmap_node *node; + + /* read length of the name */ + rc = next_entry(buf32, fp, sizeof(u32)); + if (rc) + return rc; + len = le32_to_cpu(*buf32); + + /* read the name */ + rc = str_read(&name, GFP_KERNEL, fp, len); + if (rc) + goto out; + + /* read target type, target class and number of elements for key */ + rc = next_entry(buf32, fp, sizeof(u32) * 3); + if (rc) + goto out; + + key.specified = AVTAB_TRANSITION; + key.target_type = le32_to_cpu(buf32[0]); + key.target_class = le32_to_cpu(buf32[1]); + + ndatum = le32_to_cpu(buf32[2]); + if (ndatum == 0) { + pr_err("SELinux: Filename transition key with no datum\n"); + rc = -ENOENT; + goto out; + } + + for (i = 0; i < ndatum; i++) { + rc = ebitmap_read(&stypes, fp); + if (rc) + goto out; + + rc = next_entry(buf32, fp, sizeof(u32)); + if (rc) { + ebitmap_destroy(&stypes); + goto out; + } + otype = le32_to_cpu(*buf32); + + ebitmap_for_each_positive_bit(&stypes, node, bit) { + key.source_type = bit + 1; + + name_copy = kmemdup(name, len + 1, GFP_KERNEL); + if (!name_copy) { + ebitmap_destroy(&stypes); + goto out; + } + + rc = avtab_insert_filename_trans(a, &key, name_copy, + otype); + if (rc) { + ebitmap_destroy(&stypes); + kfree(name_copy); + goto out; + } + } + + ebitmap_destroy(&stypes); + } + rc = 0; + +out: + kfree(name); + return rc; +} + +int avtab_filename_trans_read(struct avtab *a, void *fp, struct policydb *p) +{ + int rc; + __le32 buf[1]; + u32 nel, i; + + if (p->policyvers < POLICYDB_VERSION_FILENAME_TRANS) + return 0; + + rc = next_entry(buf, fp, sizeof(u32)); + if (rc) + return rc; + nel = le32_to_cpu(buf[0]); + + if (p->policyvers < POLICYDB_VERSION_COMP_FTRANS) { + for (i = 0; i < nel; i++) { + rc = filename_trans_read_item(a, fp); + if (rc) + return rc; + } + } else { + for (i = 0; i < nel; i++) { + rc = filename_trans_comp_read_item(a, fp); + if (rc) + return rc; + } + } + + return 0; +} + + +struct filenametr_write_args { + void *fp; + struct avtab_key *key; +}; + +static int filenametr_write_helper(void *k, void *d, void *a) +{ + char *name = k; + u32 *otype = d; + struct filenametr_write_args *args = a; + int rc; + u32 len; + __le32 buf32[4]; + + len = strlen(name); + buf32[0] = cpu_to_le32(len); + rc = put_entry(buf32, sizeof(u32), 1, args->fp); + if (rc) + return rc; + + rc = put_entry(name, sizeof(char), len, args->fp); + if (rc) + return rc; + + buf32[0] = cpu_to_le32(args->key->source_type); + buf32[1] = cpu_to_le32(args->key->target_type); + buf32[2] = cpu_to_le32(args->key->target_class); + buf32[3] = cpu_to_le32(*otype); + + rc = put_entry(buf32, sizeof(u32), 4, args->fp); + if (rc) + return rc; + + return 0; +} + +struct filenametr_key { + u32 ttype; /* parent dir context */ + u16 tclass; /* class of new object */ + const char *name; /* last path component */ +}; + +struct filenametr_datum { + struct ebitmap stypes; /* bitmap of source types for this otype */ + u32 otype; /* resulting type of new object */ + struct filenametr_datum *next; /* record for next otype*/ +}; + +static int filenametr_comp_write_helper(void *k, void *d, void *fp) +{ + struct filenametr_key *key = k; + struct filenametr_datum *datum = d; + __le32 buf[3]; + int rc; + u32 ndatum, len = strlen(key->name); + struct filenametr_datum *cur; + + buf[0] = cpu_to_le32(len); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return rc; + + rc = put_entry(key->name, sizeof(char), len, fp); + if (rc) + return rc; + + ndatum = 0; + cur = datum; + do { + ndatum++; + cur = cur->next; + } while (unlikely(cur)); + + buf[0] = cpu_to_le32(key->ttype); + buf[1] = cpu_to_le32(key->tclass); + buf[2] = cpu_to_le32(ndatum); + rc = put_entry(buf, sizeof(u32), 3, fp); + if (rc) + return rc; + + cur = datum; + do { + rc = ebitmap_write(&cur->stypes, fp); + if (rc) + return rc; + + buf[0] = cpu_to_le32(cur->otype); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return rc; + + cur = cur->next; + } while (unlikely(cur)); + + return 0; +} + +static int filenametr_destroy(void *k, void *d, void *args) +{ + struct filenametr_key *key = k; + struct filenametr_datum *datum = d; + struct filenametr_datum *next; + + kfree(key); + do { + ebitmap_destroy(&datum->stypes); + next = datum->next; + kfree(datum); + datum = next; + } while (unlikely(datum)); + cond_resched(); + return 0; +} + +static u32 filenametr_hash(const void *k) +{ + const struct filenametr_key *ft = k; + unsigned long hash; + unsigned int byte_num; + unsigned char focus; + + hash = ft->ttype ^ ft->tclass; + + byte_num = 0; + while ((focus = ft->name[byte_num++])) + hash = partial_name_hash(focus, hash); + return hash; +} + +static int filenametr_cmp(const void *k1, const void *k2) +{ + const struct filenametr_key *ft1 = k1; + const struct filenametr_key *ft2 = k2; + int v; + + v = ft1->ttype - ft2->ttype; + if (v) + return v; + + v = ft1->tclass - ft2->tclass; + if (v) + return v; + + return strcmp(ft1->name, ft2->name); +} + +static const struct hashtab_key_params filenametr_key_params = { + .hash = filenametr_hash, + .cmp = filenametr_cmp, +}; + +struct filenametr_tab_insert_args { + struct avtab_key *key; + struct hashtab *tab; +}; + +static int filenametr_tab_insert(void *k, void *d, void *a) +{ + char *name = k; + u32 *otype = d; + struct filenametr_tab_insert_args *args = a; + struct filenametr_key key, *ft = NULL; + struct filenametr_datum *last, *datum = NULL; + int rc; + + key.ttype = args->key->target_type; + key.tclass = args->key->target_class; + key.name = name; + + last = NULL; + datum = hashtab_search(args->tab, &key, filenametr_key_params); + while (datum) { + if (unlikely(ebitmap_get_bit(&datum->stypes, + args->key->source_type - 1))) { + /* conflicting/duplicate rules are ignored */ + datum = NULL; + goto bad; + } + if (likely(datum->otype == *otype)) + break; + last = datum; + datum = datum->next; + } + if (!datum) { + rc = -ENOMEM; + datum = kmalloc(sizeof(*datum), GFP_KERNEL); + if (!datum) + goto bad; + + ebitmap_init(&datum->stypes); + datum->otype = *otype; + datum->next = NULL; + + if (unlikely(last)) { + last->next = datum; + } else { + rc = -ENOMEM; + ft = kmemdup(&key, sizeof(key), GFP_KERNEL); + if (!ft) + goto bad; + + ft->name = kmemdup(key.name, strlen(key.name) + 1, + GFP_KERNEL); + if (!ft->name) + goto bad; + + rc = hashtab_insert(args->tab, ft, datum, + filenametr_key_params); + if (rc) + goto bad; + } + } + + return ebitmap_set_bit(&datum->stypes, args->key->source_type - 1, 1); + +bad: + if (ft) + kfree(ft->name); + kfree(ft); + kfree(datum); + return rc; +} + +int avtab_filename_trans_write(struct policydb *p, struct avtab *a, void *fp) +{ + int rc; + __le32 buf32[1]; + u32 i, nel = 0; + struct avtab_node *cur; + struct hashtab fnts_tab; + struct filenametr_tab_insert_args tab_insert_args = {.tab = &fnts_tab}; + struct filenametr_write_args write_args = {.fp = fp}; + + if (p->policyvers < POLICYDB_VERSION_FILENAME_TRANS) + return 0; + + /* count number of filename transitions */ + for (i = 0; i < a->nslot; i++) { + for (cur = a->htable[i]; cur; cur = cur->next) { + if (cur->key.specified & AVTAB_TRANSITION) + nel += cur->datum.u.trans->name_trans.table.nel; + } + } + + if (p->policyvers < POLICYDB_VERSION_COMP_FTRANS) { + buf32[0] = cpu_to_le32(nel); + rc = put_entry(buf32, sizeof(u32), 1, fp); + if (rc) + return rc; + + /* write filename transitions */ + for (i = 0; i < a->nslot; i++) { + for (cur = a->htable[i]; cur; cur = cur->next) { + if (cur->key.specified & AVTAB_TRANSITION) { + write_args.key = &cur->key; + rc = hashtab_map(&cur->datum.u.trans->name_trans.table, + filenametr_write_helper, + &write_args); + if (rc) + return rc; + } + } + } + + return 0; + } + + /* init temp filename transition table */ + rc = hashtab_init(&fnts_tab, nel); + if (rc) + return rc; + + for (i = 0; i < a->nslot; i++) { + for (cur = a->htable[i]; cur; cur = cur->next) { + if (cur->key.specified & AVTAB_TRANSITION) { + tab_insert_args.key = &cur->key; + rc = hashtab_map(&cur->datum.u.trans->name_trans.table, + filenametr_tab_insert, + &tab_insert_args); + if (rc) + goto out; + } + } + } + + /* write compressed filename transitions */ + buf32[0] = cpu_to_le32(fnts_tab.nel); + rc = put_entry(buf32, sizeof(u32), 1, fp); + if (rc) + goto out; + + rc = hashtab_map(&fnts_tab, filenametr_comp_write_helper, fp); + +out: + /* destroy temp filename transitions table */ + hashtab_map(&fnts_tab, filenametr_destroy, NULL); + hashtab_destroy(&fnts_tab); + + return rc; +} diff --git a/security/selinux/ss/avtab.h b/security/selinux/ss/avtab.h index 6c8eb7c379cf..162ef1be85e7 100644 --- a/security/selinux/ss/avtab.h +++ b/security/selinux/ss/avtab.h @@ -22,6 +22,7 @@ #define _SS_AVTAB_H_ #include "security.h" +#include "symtab.h" struct avtab_key { u16 source_type; /* source type */ @@ -49,6 +50,7 @@ struct avtab_key { struct avtab_trans { u32 otype; /* default resulting type of the new object */ + struct symtab name_trans; /* filename transitions */ }; /* @@ -121,5 +123,10 @@ struct avtab_node *avtab_search_node_next(struct avtab_node *node, int specified #define MAX_AVTAB_HASH_BITS 16 #define MAX_AVTAB_HASH_BUCKETS (1 << MAX_AVTAB_HASH_BITS) +/* policydb filename transitions compatibility */ + +int avtab_filename_trans_read(struct avtab *a, void *fp, struct policydb *p); +int avtab_filename_trans_write(struct policydb *p, struct avtab *a, void *fp); + #endif /* _SS_AVTAB_H_ */ diff --git a/security/selinux/ss/conditional.c b/security/selinux/ss/conditional.c index e11219fdf9f7..91392d65563e 100644 --- a/security/selinux/ss/conditional.c +++ b/security/selinux/ss/conditional.c @@ -263,6 +263,7 @@ static int cond_insertf(struct avtab *a, const struct avtab_key *k, struct policydb *p = data->p; struct cond_av_list *other = data->other; struct avtab_node *node_ptr; + struct avtab_datum *existing; u32 i; bool found; @@ -272,7 +273,10 @@ static int cond_insertf(struct avtab *a, const struct avtab_key *k, * cond_te_avtab. */ if (k->specified & AVTAB_TYPE) { - if (avtab_search(&p->te_avtab, k)) { + existing = avtab_search(&p->te_avtab, k); + /* empty transition rule is not a conflict */ + if (existing && !(k->specified & AVTAB_TRANSITION && + !existing->u.trans->otype)) { pr_err("SELinux: type rule already exists outside of a conditional.\n"); return -EINVAL; } diff --git a/security/selinux/ss/hashtab.h b/security/selinux/ss/hashtab.h index 043a773bf0b7..4d04acf4d5af 100644 --- a/security/selinux/ss/hashtab.h +++ b/security/selinux/ss/hashtab.h @@ -145,4 +145,10 @@ int hashtab_duplicate(struct hashtab *new, struct hashtab *orig, /* Fill info with some hash table statistics */ void hashtab_stat(struct hashtab *h, struct hashtab_info *info); +/* Checks if the hashtab is empty (its size is zero) */ +static inline int hashtab_is_empty(struct hashtab *h) +{ + return !h->size; +} + #endif /* _SS_HASHTAB_H */ diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 97c0074f9312..928a08835db8 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -323,23 +323,6 @@ static int (*const destroy_f[SYM_NUM]) (void *key, void *datum, void *datap) = { cat_destroy, }; -static int filenametr_destroy(void *key, void *datum, void *p) -{ - struct filename_trans_key *ft = key; - struct filename_trans_datum *next, *d = datum; - - kfree(ft->name); - kfree(key); - do { - ebitmap_destroy(&d->stypes); - next = d->next; - kfree(d); - d = next; - } while (unlikely(d)); - cond_resched(); - return 0; -} - static int range_tr_destroy(void *key, void *datum, void *p) { struct mls_range *rt = datum; @@ -406,50 +389,6 @@ static int roles_init(struct policydb *p) return rc; } -static u32 filenametr_hash(const void *k) -{ - const struct filename_trans_key *ft = k; - unsigned long hash; - unsigned int byte_num; - unsigned char focus; - - hash = ft->ttype ^ ft->tclass; - - byte_num = 0; - while ((focus = ft->name[byte_num++])) - hash = partial_name_hash(focus, hash); - return hash; -} - -static int filenametr_cmp(const void *k1, const void *k2) -{ - const struct filename_trans_key *ft1 = k1; - const struct filename_trans_key *ft2 = k2; - int v; - - v = ft1->ttype - ft2->ttype; - if (v) - return v; - - v = ft1->tclass - ft2->tclass; - if (v) - return v; - - return strcmp(ft1->name, ft2->name); - -} - -static const struct hashtab_key_params filenametr_key_params = { - .hash = filenametr_hash, - .cmp = filenametr_cmp, -}; - -struct filename_trans_datum *policydb_filenametr_search( - struct policydb *p, struct filename_trans_key *key) -{ - return hashtab_search(&p->filename_trans, key, filenametr_key_params); -} - static u32 rangetr_hash(const void *k) { const struct range_trans *key = k; @@ -531,7 +470,6 @@ static void policydb_init(struct policydb *p) avtab_init(&p->te_avtab); cond_policydb_init(p); - ebitmap_init(&p->filename_trans_ttypes); ebitmap_init(&p->policycaps); ebitmap_init(&p->permissive_map); } @@ -839,9 +777,6 @@ void policydb_destroy(struct policydb *p) } kfree(lra); - hashtab_map(&p->filename_trans, filenametr_destroy, NULL); - hashtab_destroy(&p->filename_trans); - hashtab_map(&p->range_tr, range_tr_destroy, NULL); hashtab_destroy(&p->range_tr); @@ -851,7 +786,6 @@ void policydb_destroy(struct policydb *p) kvfree(p->type_attr_map_array); } - ebitmap_destroy(&p->filename_trans_ttypes); ebitmap_destroy(&p->policycaps); ebitmap_destroy(&p->permissive_map); } @@ -1066,7 +1000,7 @@ static int context_read_and_validate(struct context *c, * binary representation file. */ -static int str_read(char **strp, gfp_t flags, void *fp, u32 len) +int str_read(char **strp, gfp_t flags, void *fp, u32 len) { int rc; char *str; @@ -1880,220 +1814,6 @@ static int range_read(struct policydb *p, void *fp) return rc; } -static int filename_trans_read_helper_compat(struct policydb *p, void *fp) -{ - struct filename_trans_key key, *ft = NULL; - struct filename_trans_datum *last, *datum = NULL; - char *name = NULL; - u32 len, stype, otype; - __le32 buf[4]; - int rc; - - /* length of the path component string */ - rc = next_entry(buf, fp, sizeof(u32)); - if (rc) - return rc; - len = le32_to_cpu(buf[0]); - - /* path component string */ - rc = str_read(&name, GFP_KERNEL, fp, len); - if (rc) - return rc; - - rc = next_entry(buf, fp, sizeof(u32) * 4); - if (rc) - goto out; - - stype = le32_to_cpu(buf[0]); - key.ttype = le32_to_cpu(buf[1]); - key.tclass = le32_to_cpu(buf[2]); - key.name = name; - - otype = le32_to_cpu(buf[3]); - - last = NULL; - datum = policydb_filenametr_search(p, &key); - while (datum) { - if (unlikely(ebitmap_get_bit(&datum->stypes, stype - 1))) { - /* conflicting/duplicate rules are ignored */ - datum = NULL; - goto out; - } - if (likely(datum->otype == otype)) - break; - last = datum; - datum = datum->next; - } - if (!datum) { - rc = -ENOMEM; - datum = kmalloc(sizeof(*datum), GFP_KERNEL); - if (!datum) - goto out; - - ebitmap_init(&datum->stypes); - datum->otype = otype; - datum->next = NULL; - - if (unlikely(last)) { - last->next = datum; - } else { - rc = -ENOMEM; - ft = kmemdup(&key, sizeof(key), GFP_KERNEL); - if (!ft) - goto out; - - rc = hashtab_insert(&p->filename_trans, ft, datum, - filenametr_key_params); - if (rc) - goto out; - name = NULL; - - rc = ebitmap_set_bit(&p->filename_trans_ttypes, - key.ttype, 1); - if (rc) - return rc; - } - } - kfree(name); - return ebitmap_set_bit(&datum->stypes, stype - 1, 1); - -out: - kfree(ft); - kfree(name); - kfree(datum); - return rc; -} - -static int filename_trans_read_helper(struct policydb *p, void *fp) -{ - struct filename_trans_key *ft = NULL; - struct filename_trans_datum **dst, *datum, *first = NULL; - char *name = NULL; - u32 len, ttype, tclass, ndatum, i; - __le32 buf[3]; - int rc; - - /* length of the path component string */ - rc = next_entry(buf, fp, sizeof(u32)); - if (rc) - return rc; - len = le32_to_cpu(buf[0]); - - /* path component string */ - rc = str_read(&name, GFP_KERNEL, fp, len); - if (rc) - return rc; - - rc = next_entry(buf, fp, sizeof(u32) * 3); - if (rc) - goto out; - - ttype = le32_to_cpu(buf[0]); - tclass = le32_to_cpu(buf[1]); - - ndatum = le32_to_cpu(buf[2]); - if (ndatum == 0) { - pr_err("SELinux: Filename transition key with no datum\n"); - rc = -ENOENT; - goto out; - } - - dst = &first; - for (i = 0; i < ndatum; i++) { - rc = -ENOMEM; - datum = kmalloc(sizeof(*datum), GFP_KERNEL); - if (!datum) - goto out; - - *dst = datum; - - /* ebitmap_read() will at least init the bitmap */ - rc = ebitmap_read(&datum->stypes, fp); - if (rc) - goto out; - - rc = next_entry(buf, fp, sizeof(u32)); - if (rc) - goto out; - - datum->otype = le32_to_cpu(buf[0]); - datum->next = NULL; - - dst = &datum->next; - } - - rc = -ENOMEM; - ft = kmalloc(sizeof(*ft), GFP_KERNEL); - if (!ft) - goto out; - - ft->ttype = ttype; - ft->tclass = tclass; - ft->name = name; - - rc = hashtab_insert(&p->filename_trans, ft, first, - filenametr_key_params); - if (rc == -EEXIST) - pr_err("SELinux: Duplicate filename transition key\n"); - if (rc) - goto out; - - return ebitmap_set_bit(&p->filename_trans_ttypes, ttype, 1); - -out: - kfree(ft); - kfree(name); - while (first) { - datum = first; - first = first->next; - - ebitmap_destroy(&datum->stypes); - kfree(datum); - } - return rc; -} - -static int filename_trans_read(struct policydb *p, void *fp) -{ - u32 nel; - __le32 buf[1]; - int rc, i; - - if (p->policyvers < POLICYDB_VERSION_FILENAME_TRANS) - return 0; - - rc = next_entry(buf, fp, sizeof(u32)); - if (rc) - return rc; - nel = le32_to_cpu(buf[0]); - - if (p->policyvers < POLICYDB_VERSION_COMP_FTRANS) { - p->compat_filename_trans_count = nel; - - rc = hashtab_init(&p->filename_trans, (1 << 11)); - if (rc) - return rc; - - for (i = 0; i < nel; i++) { - rc = filename_trans_read_helper_compat(p, fp); - if (rc) - return rc; - } - } else { - rc = hashtab_init(&p->filename_trans, nel); - if (rc) - return rc; - - for (i = 0; i < nel; i++) { - rc = filename_trans_read_helper(p, fp); - if (rc) - return rc; - } - } - hash_eval(&p->filename_trans, "filenametr"); - return 0; -} - static int genfs_read(struct policydb *p, void *fp) { int i, j, rc; @@ -2634,7 +2354,7 @@ int policydb_read(struct policydb *p, void *fp) lra = ra; } - rc = filename_trans_read(p, fp); + rc = avtab_filename_trans_read(&p->te_avtab, fp, p); if (rc) goto bad; @@ -3480,119 +3200,6 @@ static int range_write(struct policydb *p, void *fp) return 0; } -static int filename_write_helper_compat(void *key, void *data, void *ptr) -{ - struct filename_trans_key *ft = key; - struct filename_trans_datum *datum = data; - struct ebitmap_node *node; - void *fp = ptr; - __le32 buf[4]; - int rc; - u32 bit, len = strlen(ft->name); - - do { - ebitmap_for_each_positive_bit(&datum->stypes, node, bit) { - buf[0] = cpu_to_le32(len); - rc = put_entry(buf, sizeof(u32), 1, fp); - if (rc) - return rc; - - rc = put_entry(ft->name, sizeof(char), len, fp); - if (rc) - return rc; - - buf[0] = cpu_to_le32(bit + 1); - buf[1] = cpu_to_le32(ft->ttype); - buf[2] = cpu_to_le32(ft->tclass); - buf[3] = cpu_to_le32(datum->otype); - - rc = put_entry(buf, sizeof(u32), 4, fp); - if (rc) - return rc; - } - - datum = datum->next; - } while (unlikely(datum)); - - return 0; -} - -static int filename_write_helper(void *key, void *data, void *ptr) -{ - struct filename_trans_key *ft = key; - struct filename_trans_datum *datum; - void *fp = ptr; - __le32 buf[3]; - int rc; - u32 ndatum, len = strlen(ft->name); - - buf[0] = cpu_to_le32(len); - rc = put_entry(buf, sizeof(u32), 1, fp); - if (rc) - return rc; - - rc = put_entry(ft->name, sizeof(char), len, fp); - if (rc) - return rc; - - ndatum = 0; - datum = data; - do { - ndatum++; - datum = datum->next; - } while (unlikely(datum)); - - buf[0] = cpu_to_le32(ft->ttype); - buf[1] = cpu_to_le32(ft->tclass); - buf[2] = cpu_to_le32(ndatum); - rc = put_entry(buf, sizeof(u32), 3, fp); - if (rc) - return rc; - - datum = data; - do { - rc = ebitmap_write(&datum->stypes, fp); - if (rc) - return rc; - - buf[0] = cpu_to_le32(datum->otype); - rc = put_entry(buf, sizeof(u32), 1, fp); - if (rc) - return rc; - - datum = datum->next; - } while (unlikely(datum)); - - return 0; -} - -static int filename_trans_write(struct policydb *p, void *fp) -{ - __le32 buf[1]; - int rc; - - if (p->policyvers < POLICYDB_VERSION_FILENAME_TRANS) - return 0; - - if (p->policyvers < POLICYDB_VERSION_COMP_FTRANS) { - buf[0] = cpu_to_le32(p->compat_filename_trans_count); - rc = put_entry(buf, sizeof(u32), 1, fp); - if (rc) - return rc; - - rc = hashtab_map(&p->filename_trans, - filename_write_helper_compat, fp); - } else { - buf[0] = cpu_to_le32(p->filename_trans.nel); - rc = put_entry(buf, sizeof(u32), 1, fp); - if (rc) - return rc; - - rc = hashtab_map(&p->filename_trans, filename_write_helper, fp); - } - return rc; -} - /* * Write the configuration data in a policy database * structure to a policy database binary representation @@ -3703,7 +3310,7 @@ int policydb_write(struct policydb *p, void *fp) if (rc) return rc; - rc = filename_trans_write(p, fp); + rc = avtab_filename_trans_write(p, &p->te_avtab, fp); if (rc) return rc; diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h index ffc4e7bad205..2ecb24a1611a 100644 --- a/security/selinux/ss/policydb.h +++ b/security/selinux/ss/policydb.h @@ -91,18 +91,6 @@ struct role_trans_datum { u32 new_role; /* new role */ }; -struct filename_trans_key { - u32 ttype; /* parent dir context */ - u16 tclass; /* class of new object */ - const char *name; /* last path component */ -}; - -struct filename_trans_datum { - struct ebitmap stypes; /* bitmap of source types for this otype */ - u32 otype; /* resulting type of new object */ - struct filename_trans_datum *next; /* record for next otype*/ -}; - struct role_allow { u32 role; /* current role */ u32 new_role; /* new role */ @@ -265,14 +253,6 @@ struct policydb { /* role transitions */ struct hashtab role_tr; - /* file transitions with the last path component */ - /* quickly exclude lookups when parent ttype has no rules */ - struct ebitmap filename_trans_ttypes; - /* actual set of filename_trans rules */ - struct hashtab filename_trans; - /* only used if policyvers < POLICYDB_VERSION_COMP_FTRANS */ - u32 compat_filename_trans_count; - /* bools indexed by (value - 1) */ struct cond_bool_datum **bool_val_to_struct; /* type enforcement conditional access vectors and transitions */ @@ -324,9 +304,6 @@ extern int policydb_role_isvalid(struct policydb *p, unsigned int role); extern int policydb_read(struct policydb *p, void *fp); extern int policydb_write(struct policydb *p, void *fp); -extern struct filename_trans_datum *policydb_filenametr_search( - struct policydb *p, struct filename_trans_key *key); - extern struct mls_range *policydb_rangetr_search( struct policydb *p, struct range_trans *key); @@ -379,6 +356,8 @@ static inline int put_entry(const void *buf, size_t bytes, int num, struct polic return 0; } +extern int str_read(char **strp, gfp_t flags, void *fp, u32 len); + static inline char *sym_name(struct policydb *p, unsigned int sym_num, unsigned int element_nr) { return p->sym_val_to_name[sym_num][element_nr]; diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 8ed12406acba..131647e7ec68 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -1661,36 +1661,6 @@ static int compute_sid_handle_invalid_context( return -EACCES; } -static void filename_compute_type(struct policydb *policydb, - struct context *newcontext, - u32 stype, u32 ttype, u16 tclass, - const char *objname) -{ - struct filename_trans_key ft; - struct filename_trans_datum *datum; - - /* - * Most filename trans rules are going to live in specific directories - * like /dev or /var/run. This bitmap will quickly skip rule searches - * if the ttype does not contain any rules. - */ - if (!ebitmap_get_bit(&policydb->filename_trans_ttypes, ttype)) - return; - - ft.ttype = ttype; - ft.tclass = tclass; - ft.name = objname; - - datum = policydb_filenametr_search(policydb, &ft); - while (datum) { - if (ebitmap_get_bit(&datum->stypes, stype - 1)) { - newcontext->type = datum->otype; - return; - } - datum = datum->next; - } -} - static int security_compute_sid(u32 ssid, u32 tsid, u16 orig_tclass, @@ -1711,6 +1681,7 @@ static int security_compute_sid(u32 ssid, u16 tclass; int rc = 0; bool sock; + u32 *otype; if (!selinux_initialized()) { switch (orig_tclass) { @@ -1830,17 +1801,24 @@ static int security_compute_sid(u32 ssid, if (avdatum) { /* Use the type from the type transition/member/change rule. */ - if (avkey.specified & AVTAB_TRANSITION) - newcontext.type = avdatum->u.trans->otype; - else + if (avkey.specified & AVTAB_TRANSITION) { + /* + * use default otype if not empty and then to try to + * find more specific rule using objname + */ + if (avdatum->u.trans->otype) + newcontext.type = avdatum->u.trans->otype; + if (objname) { + otype = symtab_search(&avdatum->u.trans->name_trans, + objname); + if (otype) + newcontext.type = *otype; + } + } else { newcontext.type = avdatum->u.data; + } } - /* if we have a objname this is a file trans check so check those rules */ - if (objname) - filename_compute_type(policydb, &newcontext, scontext->type, - tcontext->type, tclass, objname); - /* Check for class-specific changes. */ if (specified & AVTAB_TRANSITION) { /* Look for a role transition rule. */ From patchwork Wed May 31 11:29:26 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Juraj Marcin X-Patchwork-Id: 13262007 X-Patchwork-Delegate: paul@paul-moore.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id EDC6EC77B73 for ; Wed, 31 May 2023 11:32:48 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232888AbjEaLcr (ORCPT ); Wed, 31 May 2023 07:32:47 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:41684 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233024AbjEaLcp (ORCPT ); Wed, 31 May 2023 07:32:45 -0400 Received: from sender11-of-o52.zoho.eu (sender11-of-o52.zoho.eu [31.186.226.238]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id EB0F9132 for ; Wed, 31 May 2023 04:32:39 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1685532754; cv=none; d=zohomail.eu; s=zohoarc; b=fBA6Z5ZRK6rItSq7G6O9pBMwZAa2tcImWbCp5KKj75E9U8IcMwNP6mna+X6yUAfjZcvfrELa2LZIKD2dJy8vcInML07lK8F0P9hUHrHrsMoJuXkPek/qOEbak/GQN29kF0HsCQZZFomw1m0uVzVaN5dKQeHIMhYcJNBhR8bgoCo= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.eu; s=zohoarc; t=1685532754; h=Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:To; bh=UUfCud7Was1fdoE2+mBB0S0D6ta6gyloHW/eQTFbHi8=; b=jyklAiUKGThgsCAkJz1LeiUEcTeDbpMFCS5f85lJajLF8hWtXhEVhvKPZ0NpWBZQEyihDJ8R4wlQjW37z+eYL2yAs3gRcmsLMohvc9qUMBTmSdMUZ28NTsuQmlcSz05J9gqIl3R8kJZi4t/uFb8W5GznrbU52lTSnkk8sfCwlrE= ARC-Authentication-Results: i=1; mx.zohomail.eu; dkim=pass header.i=jurajmarcin.com; spf=pass smtp.mailfrom=juraj@jurajmarcin.com; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1685532754; s=zoho; d=jurajmarcin.com; i=juraj@jurajmarcin.com; h=From:From:To:To:Cc:Cc:Subject:Subject:Date:Date:Message-Id:Message-Id:In-Reply-To:References:MIME-Version:Content-Transfer-Encoding:Reply-To; bh=UUfCud7Was1fdoE2+mBB0S0D6ta6gyloHW/eQTFbHi8=; b=eSEzdR3RU63LmCZcLY5s4J5YzcMrqwkiWoWqUmWTUhdwXA+3jowMSFWo0zYgZI3y 9nAlzplNK1z0VIBwS/OfKjRzc3CBLJicmdKVXPFCPbjXUHgTmV1+7/gwnURtEjJBcyw gtb3cdfSgEcS/aclEm64TE+Hbj4++Lq9yM+1qDhg= Received: from morty01.jurajmarcin.com (129.159.244.31 [129.159.244.31]) by mx.zoho.eu with SMTPS id 1685532752529749.0499511845534; Wed, 31 May 2023 13:32:32 +0200 (CEST) Received: from jmarcin-t14s-01.redhat.com (unknown [147.251.183.113]) by morty01.jurajmarcin.com (Postfix) with ESMTPSA id E8A552081F76; Wed, 31 May 2023 11:32:31 +0000 (UTC) From: Juraj Marcin To: Paul Moore Cc: Stephen Smalley , selinux@vger.kernel.org, Ondrej Mosnacek Subject: [PATCH 3/5] selinux: implement new binary format for filename transitions in avtab Date: Wed, 31 May 2023 13:29:26 +0200 Message-Id: <20230531112927.1957093-4-juraj@jurajmarcin.com> In-Reply-To: <20230531112927.1957093-1-juraj@jurajmarcin.com> References: <20230531112927.1957093-1-juraj@jurajmarcin.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: selinux@vger.kernel.org Implement a new binary policy format that closely matches the new internal representation introduced in the previous patch. This patch bumps the maximum kernel policy version and implements reading/writing functions such that kernel binary policy structure matches internal representation. These changes cause the binary policy to grow in size due to effectively undoing the benefits of the commit 430059024389 ("selinux: implement new format of filename transitions"), as well as increase its loading time, but this will be mitigated by adding the prefix/suffix support as described in the previous patch. Kernel | Policy file | Policy size | Policy load | version | | time -----------+--------------+--------------+------------- reference | 33 | 3.5 MB | 78 ms prev patch | 33 | 3.5 MB | 230 ms this patch | 34 | 5.5 MB | 113 ms Reviewed-by: Ondrej Mosnacek Signed-off-by: Juraj Marcin --- security/selinux/include/security.h | 3 +- security/selinux/ss/avtab.c | 189 ++++++++++++++++++++++++++-- security/selinux/ss/policydb.c | 21 +++- 3 files changed, 195 insertions(+), 18 deletions(-) diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index 8746fafeb778..c483680fe22e 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -46,10 +46,11 @@ #define POLICYDB_VERSION_INFINIBAND 31 #define POLICYDB_VERSION_GLBLUB 32 #define POLICYDB_VERSION_COMP_FTRANS 33 /* compressed filename transitions */ +#define POLICYDB_VERSION_AVTAB_FTRANS 34 /* filename transitions moved to avtab */ /* Range of policy versions we understand*/ #define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE -#define POLICYDB_VERSION_MAX POLICYDB_VERSION_COMP_FTRANS +#define POLICYDB_VERSION_MAX POLICYDB_VERSION_AVTAB_FTRANS /* Mask for just the mount related flags */ #define SE_MNTMASK 0x0f diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c index fe921e1586e5..2cd9eabf796c 100644 --- a/security/selinux/ss/avtab.c +++ b/security/selinux/ss/avtab.c @@ -426,6 +426,95 @@ static const uint16_t spec_order[] = { AVTAB_XPERMS_DONTAUDIT }; +static int avtab_trans_read_name_trans(struct policydb *pol, + struct symtab *target, void *fp) +{ + int rc; + __le32 buf32[2]; + u32 nfnts, i, len, *fnt_otype = NULL; + char *name = NULL; + + /* read number of name transitions */ + rc = next_entry(buf32, fp, sizeof(u32)); + if (rc) + return rc; + nfnts = le32_to_cpu(buf32[0]); + + rc = symtab_init(target, nfnts); + if (rc) + return rc; + + /* read name transitions */ + for (i = 0; i < nfnts; i++) { + rc = -ENOMEM; + fnt_otype = kmalloc(sizeof(u32), GFP_KERNEL); + if (!fnt_otype) + goto exit; + + /* read name transition otype and name length */ + rc = next_entry(buf32, fp, sizeof(u32) * 2); + if (rc) + goto exit; + *fnt_otype = le32_to_cpu(buf32[0]); + len = le32_to_cpu(buf32[1]); + if (!policydb_type_isvalid(pol, *fnt_otype)) { + pr_err("SELinux: avtab: invalid filename transition type\n"); + rc = -EINVAL; + goto exit; + } + + /* read the name */ + rc = str_read(&name, GFP_KERNEL, fp, len); + if (rc) + goto exit; + + /* insert to the table */ + rc = symtab_insert(target, name, fnt_otype); + if (rc) + goto exit; + name = NULL; + fnt_otype = NULL; + } + +exit: + kfree(fnt_otype); + kfree(name); + return rc; +} + +static int avtab_trans_read(void *fp, struct policydb *pol, + struct avtab_trans *trans) +{ + int rc; + __le32 buf32[1]; + + if (pol->policyvers < POLICYDB_VERSION_AVTAB_FTRANS) { + rc = next_entry(buf32, fp, sizeof(u32)); + if (rc) { + pr_err("SELinux: avtab: truncated entry\n"); + return rc; + } + trans->otype = le32_to_cpu(*buf32); + return 0; + } + + /* read default otype */ + rc = next_entry(buf32, fp, sizeof(u32)); + if (rc) + return rc; + trans->otype = le32_to_cpu(buf32[0]); + + rc = avtab_trans_read_name_trans(pol, &trans->name_trans, fp); + if (rc) + goto bad; + + return 0; + +bad: + avtab_trans_destroy(trans); + return rc; +} + int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, int (*insertf)(struct avtab *a, const struct avtab_key *k, const struct avtab_datum *d, void *p), @@ -433,7 +522,7 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, { __le16 buf16[4]; u16 enabled; - u32 items, items2, val, vers = pol->policyvers; + u32 otype, items, items2, val, vers = pol->policyvers; struct avtab_key key; struct avtab_datum datum; struct avtab_trans trans; @@ -584,12 +673,9 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, datum.u.xperms = &xperms; } else if (key.specified & AVTAB_TRANSITION) { memset(&trans, 0, sizeof(struct avtab_trans)); - rc = next_entry(buf32, fp, sizeof(u32)); - if (rc) { - pr_err("SELinux: avtab: truncated entry\n"); + rc = avtab_trans_read(fp, pol, &trans); + if (rc) return rc; - } - trans.otype = le32_to_cpu(*buf32); datum.u.trans = &trans; } else { rc = next_entry(buf32, fp, sizeof(u32)); @@ -600,11 +686,24 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, datum.u.data = le32_to_cpu(*buf32); } if (key.specified & AVTAB_TRANSITION) { - if (!policydb_type_isvalid(pol, datum.u.trans->otype)) { + /* if otype is set (non-zero), it must by a valid type */ + otype = datum.u.trans->otype; + if (otype && !policydb_type_isvalid(pol, otype)) { pr_err("SELinux: avtab: invalid transition type\n"); avtab_trans_destroy(&trans); return -EINVAL; } + /* + * also each transition entry must meet at least one condition + * to be considered non-empty: + * - set (non-zero) otype + * - non-empty filename transitions table + */ + if (!otype && !datum.u.trans->name_trans.table.nel) { + pr_err("SELinux: avtab: empty transition\n"); + avtab_trans_destroy(&trans); + return -EINVAL; + } } else if (key.specified & AVTAB_TYPE) { if (!policydb_type_isvalid(pol, datum.u.data)) { pr_err("SELinux: avtab: invalid type\n"); @@ -667,6 +766,59 @@ int avtab_read(struct avtab *a, void *fp, struct policydb *pol) goto out; } +static int avtab_trans_write_helper(void *k, void *d, void *fp) +{ + char *name = k; + u32 *otype = d; + int rc; + __le32 buf32[2]; + u32 len; + + /* write filename transition otype and name length */ + len = strlen(name); + buf32[0] = cpu_to_le32(*otype); + buf32[1] = cpu_to_le32(len); + rc = put_entry(buf32, sizeof(u32), 2, fp); + if (rc) + return rc; + + /* write filename transition name */ + rc = put_entry(name, sizeof(char), len, fp); + if (rc) + return rc; + + return 0; +} + +static int avtab_trans_write(struct policydb *p, struct avtab_trans *cur, + void *fp) +{ + int rc; + __le32 buf32[2]; + + if (p->policyvers >= POLICYDB_VERSION_AVTAB_FTRANS) { + /* write otype and number of filename transitions */ + buf32[0] = cpu_to_le32(cur->otype); + buf32[1] = cpu_to_le32(cur->name_trans.table.nel); + rc = put_entry(buf32, sizeof(u32), 2, fp); + if (rc) + return rc; + + /* write filename transitions */ + rc = hashtab_map(&cur->name_trans.table, + avtab_trans_write_helper, fp); + if (rc) + return rc; + } else if (cur->otype) { + buf32[0] = cpu_to_le32(cur->otype); + rc = put_entry(buf32, sizeof(u32), 1, fp); + if (rc) + return rc; + } + + return 0; +} + int avtab_write_item(struct policydb *p, const struct avtab_node *cur, void *fp) { __le16 buf16[4]; @@ -674,7 +826,8 @@ int avtab_write_item(struct policydb *p, const struct avtab_node *cur, void *fp) int rc; unsigned int i; - if (cur->key.specified & AVTAB_TRANSITION && + if (p->policyvers < POLICYDB_VERSION_AVTAB_FTRANS && + cur->key.specified & AVTAB_TRANSITION && !cur->datum.u.trans->otype) return 0; @@ -698,8 +851,7 @@ int avtab_write_item(struct policydb *p, const struct avtab_node *cur, void *fp) rc = put_entry(buf32, sizeof(u32), ARRAY_SIZE(cur->datum.u.xperms->perms.p), fp); } else if (cur->key.specified & AVTAB_TRANSITION) { - buf32[0] = cpu_to_le32(cur->datum.u.trans->otype); - rc = put_entry(buf32, sizeof(u32), 1, fp); + rc = avtab_trans_write(p, cur->datum.u.trans, fp); } else { buf32[0] = cpu_to_le32(cur->datum.u.data); rc = put_entry(buf32, sizeof(u32), 1, fp); @@ -715,8 +867,23 @@ int avtab_write(struct policydb *p, struct avtab *a, void *fp) int rc = 0; struct avtab_node *cur; __le32 buf[1]; + u32 nel; - buf[0] = cpu_to_le32(a->nel); + nel = a->nel; + if (p->policyvers < POLICYDB_VERSION_AVTAB_FTRANS) { + /* + * in older version, skip entries with only filename transition, + * as these are written out separately + */ + for (i = 0; i < a->nslot; i++) { + for (cur = a->htable[i]; cur; cur = cur->next) { + if (cur->key.specified & AVTAB_TRANSITION && + !cur->datum.u.trans->otype) + nel--; + } + } + } + buf[0] = cpu_to_le32(nel); rc = put_entry(buf, sizeof(u32), 1, fp); if (rc) return rc; diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 928a08835db8..96b8d5bd8e4e 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -157,6 +157,11 @@ static const struct policydb_compat_info policydb_compat[] = { .sym_num = SYM_NUM, .ocon_num = OCON_NUM, }, + { + .version = POLICYDB_VERSION_AVTAB_FTRANS, + .sym_num = SYM_NUM, + .ocon_num = OCON_NUM, + }, }; static const struct policydb_compat_info *policydb_lookup_compat(int version) @@ -2354,9 +2359,11 @@ int policydb_read(struct policydb *p, void *fp) lra = ra; } - rc = avtab_filename_trans_read(&p->te_avtab, fp, p); - if (rc) - goto bad; + if (p->policyvers < POLICYDB_VERSION_AVTAB_FTRANS) { + rc = avtab_filename_trans_read(&p->te_avtab, fp, p); + if (rc) + goto bad; + } rc = policydb_index(p); if (rc) @@ -3310,9 +3317,11 @@ int policydb_write(struct policydb *p, void *fp) if (rc) return rc; - rc = avtab_filename_trans_write(p, &p->te_avtab, fp); - if (rc) - return rc; + if (p->policyvers < POLICYDB_VERSION_AVTAB_FTRANS) { + rc = avtab_filename_trans_write(p, &p->te_avtab, fp); + if (rc) + return rc; + } rc = ocontext_write(p, info, fp); if (rc) From patchwork Wed May 31 11:29:27 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Juraj Marcin X-Patchwork-Id: 13262009 X-Patchwork-Delegate: paul@paul-moore.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9ABC0C7EE23 for ; Wed, 31 May 2023 11:32:49 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232241AbjEaLcs (ORCPT ); Wed, 31 May 2023 07:32:48 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:41720 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S234672AbjEaLcr (ORCPT ); Wed, 31 May 2023 07:32:47 -0400 Received: from sender11-of-o52.zoho.eu (sender11-of-o52.zoho.eu [31.186.226.238]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C411AD9 for ; Wed, 31 May 2023 04:32:43 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1685532759; cv=none; d=zohomail.eu; s=zohoarc; b=FtbCLt2HpMqnTdXWJdcf8uM01mIXEglozwUj7t2mLF/+Q3nwi3AzBJZTell/9QIpHBKGPPS170qDNtFT+N1uwjJ2z/vgyHzlze47Rm2EMIGuWFv+FQ5b0H+oIAjX+w5x7BPOO/inO3RLiE1HQG1uz7epCGZOoBEWIM76eiO7Uu0= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.eu; s=zohoarc; t=1685532759; h=Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:To; bh=S0Rw2G6uUE+JDBv41Op8AYbpCmnktaHD6YPPMxxPOTQ=; b=T46fP1qw/9XphDXxmjdfuIWv2SLV/B26OY9J/1ofRzOTlPPR9NKsOdnF6a7AJoxIbaueteigkZ93eZJqsbS9BU4dFzp/mlEkgj9RvHcBXp8npUPzCCv1wEGjv/TdQEGkfn5B0QNZdem6bh45ajDHgKisBS6+sKIA+fzXz437H9k= ARC-Authentication-Results: i=1; mx.zohomail.eu; dkim=pass header.i=jurajmarcin.com; spf=pass smtp.mailfrom=juraj@jurajmarcin.com; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1685532759; s=zoho; d=jurajmarcin.com; i=juraj@jurajmarcin.com; h=From:From:To:To:Cc:Cc:Subject:Subject:Date:Date:Message-Id:Message-Id:In-Reply-To:References:MIME-Version:Content-Transfer-Encoding:Reply-To; bh=S0Rw2G6uUE+JDBv41Op8AYbpCmnktaHD6YPPMxxPOTQ=; b=P7rT6kwofFjql0pXoWZrhNQew0/lFv2aqijJiUuOdqJH/MGI+JWrky6FTRLhBoic 3gj8sBp36ShYAsHnFIBK1bvrp5tpBpOd+UCJs6vwxj7pIrOH5adyXPYdenmn4zhcWm8 ISBBnK59m8uQZcFo+cbCVyBHT3LXLM29jO1WVfE8= Received: from morty01.jurajmarcin.com (129.159.244.31 [129.159.244.31]) by mx.zoho.eu with SMTPS id 1685532756354542.3961958356815; Wed, 31 May 2023 13:32:36 +0200 (CEST) Received: from jmarcin-t14s-01.redhat.com (unknown [147.251.183.113]) by morty01.jurajmarcin.com (Postfix) with ESMTPSA id B50C62081F76; Wed, 31 May 2023 11:32:35 +0000 (UTC) From: Juraj Marcin To: Paul Moore Cc: Stephen Smalley , selinux@vger.kernel.org, Ondrej Mosnacek Subject: [PATCH 4/5] selinux: filename transitions move tests Date: Wed, 31 May 2023 13:29:27 +0200 Message-Id: <20230531112927.1957093-5-juraj@jurajmarcin.com> In-Reply-To: <20230531112927.1957093-1-juraj@jurajmarcin.com> References: <20230531112927.1957093-1-juraj@jurajmarcin.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: selinux@vger.kernel.org This patch adds a set of simple tests that test if the functions for reading and writing filename transitions in avtab work properly. Reviewed-by: Ondrej Mosnacek Signed-off-by: Juraj Marcin --- security/selinux/Kconfig | 17 + security/selinux/Makefile | 2 + security/selinux/ss/avtab.c | 20 + security/selinux/ss/avtab_test.c | 729 +++++++++++++++++++++++++++++++ security/selinux/ss/symtab.c | 3 + 5 files changed, 771 insertions(+) create mode 100644 security/selinux/ss/avtab_test.c diff --git a/security/selinux/Kconfig b/security/selinux/Kconfig index 95a186ec0fcb..78cdc8d4232d 100644 --- a/security/selinux/Kconfig +++ b/security/selinux/Kconfig @@ -68,3 +68,20 @@ config SECURITY_SELINUX_SID2STR_CACHE_SIZE conversion. Setting this option to 0 disables the cache completely. If unsure, keep the default value. + +config SECURITY_SELINUX_KUNIT_TEST + tristate "Tests for SELinux" if !KUNIT_ALL_TESTS + depends on KUNIT && SECURITY_SELINUX + default KUNIT_ALL_TESTS + help + This builds the SELinux KUnit tests. + + KUnit tests run during boot and output the results to the debug log + in TAP format (https://testanything.org/). Only useful for kernel devs + running KUnit test harness and are not for inclusion into a + production build. + + For more information on KUnit and unit tests in general please refer + to the KUnit documentation in Documentation/dev-tools/kunit/. + + If unsure, say N. diff --git a/security/selinux/Makefile b/security/selinux/Makefile index ab8c3093d5fd..d3d63b28edcf 100644 --- a/security/selinux/Makefile +++ b/security/selinux/Makefile @@ -27,3 +27,5 @@ quiet_cmd_genhdrs = GEN $(addprefix $(obj)/,$(genhdrs)) targets += $(genhdrs) $(addprefix $(obj)/,$(genhdrs)) &: scripts/selinux/genheaders/genheaders FORCE $(call if_changed,genhdrs) + +obj-$(CONFIG_SECURITY_SELINUX_KUNIT_TEST) += ss/avtab_test.o diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c index 2cd9eabf796c..db7123670ef8 100644 --- a/security/selinux/ss/avtab.c +++ b/security/selinux/ss/avtab.c @@ -223,6 +223,9 @@ struct avtab_datum *avtab_search(struct avtab *h, const struct avtab_key *key) return NULL; } +/* Export for avtab KUnit tests */ +EXPORT_SYMBOL_GPL(avtab_search); + /* This search function returns a node pointer, and can be used in * conjunction with avtab_search_next_node() */ @@ -331,6 +334,9 @@ void avtab_destroy(struct avtab *h) h->mask = 0; } +/* Export for avtab KUnit tests */ +EXPORT_SYMBOL_GPL(avtab_destroy); + void avtab_init(struct avtab *h) { h->htable = NULL; @@ -378,6 +384,9 @@ int avtab_alloc(struct avtab *h, u32 nrules) return 0; } +/* Export for avtab KUnit tests */ +EXPORT_SYMBOL_GPL(avtab_alloc); + int avtab_alloc_dup(struct avtab *new, const struct avtab *orig) { return avtab_alloc_common(new, orig->nslot); @@ -766,6 +775,9 @@ int avtab_read(struct avtab *a, void *fp, struct policydb *pol) goto out; } +/* Export for avtab KUnit tests */ +EXPORT_SYMBOL_GPL(avtab_read); + static int avtab_trans_write_helper(void *k, void *d, void *fp) { char *name = k; @@ -900,6 +912,9 @@ int avtab_write(struct policydb *p, struct avtab *a, void *fp) return rc; } +/* Export for avtab KUnit tests */ +EXPORT_SYMBOL_GPL(avtab_write); + void __init avtab_cache_init(void) { avtab_node_cachep = kmem_cache_create("avtab_node", @@ -1105,6 +1120,8 @@ int avtab_filename_trans_read(struct avtab *a, void *fp, struct policydb *p) return 0; } +/* Export for avtab KUnit tests */ +EXPORT_SYMBOL_GPL(avtab_filename_trans_read); struct filenametr_write_args { void *fp; @@ -1406,3 +1423,6 @@ int avtab_filename_trans_write(struct policydb *p, struct avtab *a, void *fp) return rc; } + +/* Export for avtab KUnit tests */ +EXPORT_SYMBOL_GPL(avtab_filename_trans_write); diff --git a/security/selinux/ss/avtab_test.c b/security/selinux/ss/avtab_test.c new file mode 100644 index 000000000000..daa8e4cfaeb2 --- /dev/null +++ b/security/selinux/ss/avtab_test.c @@ -0,0 +1,729 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * KUnit tests for access vector table type Implementation + * + * Author: Juraj Marcin + */ + +#include +#include "policydb.h" +#include "security.h" +#include "avtab.h" + +static void filename_trans_read__pre_filename_trans(struct kunit *test) +{ + struct policydb p = { + .te_avtab = {0}, + .policyvers = POLICYDB_VERSION_FILENAME_TRANS - 1, + }; + struct policy_file fp = { + .data = NULL, + .len = 0, + }; + + p.p_types.nprim = 54; + p.p_classes.nprim = 49; + + KUNIT_EXPECT_EQ(test, 0, + avtab_filename_trans_read(&p.te_avtab, &fp, &p)); + KUNIT_EXPECT_EQ(test, 0, p.te_avtab.nel); + KUNIT_EXPECT_EQ(test, 0, fp.len); +} + +static void filename_trans_read__empty(struct kunit *test) +{ + char data[] = {0, 0, 0, 0}; + struct policydb p = { + .te_avtab = {0}, + .policyvers = POLICYDB_VERSION_FILENAME_TRANS, + }; + struct policy_file fp = { + .data = data, + .len = sizeof(data), + }; + + p.p_types.nprim = 54; + p.p_classes.nprim = 49; + + KUNIT_EXPECT_EQ(test, 0, + avtab_filename_trans_read(&p.te_avtab, &fp, &p)); + KUNIT_EXPECT_EQ(test, 0, p.te_avtab.nel); + KUNIT_EXPECT_EQ(test, 0, fp.len); +} + +static void filename_trans_read__comp_empty(struct kunit *test) +{ + char data[] = {0, 0, 0, 0}; + struct policydb p = { + .te_avtab = {0}, + .policyvers = POLICYDB_VERSION_COMP_FTRANS, + }; + struct policy_file fp = { + .data = data, + .len = sizeof(data), + }; + + p.p_types.nprim = 54; + p.p_classes.nprim = 49; + + KUNIT_EXPECT_EQ(test, 0, + avtab_filename_trans_read(&p.te_avtab, &fp, &p)); + KUNIT_EXPECT_EQ(test, 0, p.te_avtab.nel); + KUNIT_EXPECT_EQ(test, 0, fp.len); +} + +static void filename_trans_read__simple(struct kunit *test) +{ + char data[] = { + 3, 0, 0, 0, /* count */ + + 5, 0, 0, 0, /* entry 1 name len */ + 'f', 'i', 'l', 'e', '1', /* entry 1 name */ + 42, 0, 0, 0, /* entry 1 stype */ + 43, 0, 0, 0, /* entry 1 ttype */ + 44, 0, 0, 0, /* entry 1 tclass */ + 45, 0, 0, 0, /* entry 1 otype */ + + 5, 0, 0, 0, /* entry 2 name len */ + 'f', 'i', 'l', 'e', '2', /* entry 2 name */ + 46, 0, 0, 0, /* entry 2 stype */ + 47, 0, 0, 0, /* entry 2 ttype */ + 48, 0, 0, 0, /* entry 2 tclass */ + 49, 0, 0, 0, /* entry 2 otype */ + + 5, 0, 0, 0, /* entry 3 name len */ + 'f', 'i', 'l', 'e', '3', /* entry 3 name */ + 46, 0, 0, 0, /* entry 3 stype */ + 47, 0, 0, 0, /* entry 3 ttype */ + 48, 0, 0, 0, /* entry 3 tclass */ + 53, 0, 0, 0, /* entry 3 otype */ + }; + struct policydb p = { + .te_avtab = {0}, + .policyvers = POLICYDB_VERSION_FILENAME_TRANS, + }; + struct policy_file fp = { + .data = data, + .len = sizeof(data), + }; + struct avtab_key key; + struct avtab_datum *node; + u32 *otype; + + p.p_types.nprim = 54; + p.p_classes.nprim = 49; + KUNIT_ASSERT_EQ(test, 0, avtab_alloc(&p.te_avtab, 3)); + + KUNIT_ASSERT_EQ(test, 0, + avtab_filename_trans_read(&p.te_avtab, &fp, &p)); + KUNIT_EXPECT_EQ(test, 2, p.te_avtab.nel); + KUNIT_EXPECT_EQ(test, 0, fp.len); + + key = (struct avtab_key){42, 43, 44, AVTAB_TRANSITION}; + node = avtab_search(&p.te_avtab, &key); + KUNIT_ASSERT_NOT_NULL(test, node); + KUNIT_EXPECT_EQ(test, 0, node->u.trans->otype); + KUNIT_EXPECT_EQ(test, 1, node->u.trans->name_trans.table.nel); + + otype = symtab_search(&node->u.trans->name_trans, "file1"); + KUNIT_ASSERT_NOT_NULL(test, otype); + KUNIT_EXPECT_EQ(test, 45, *otype); + + key = (struct avtab_key){46, 47, 48, AVTAB_TRANSITION}; + node = avtab_search(&p.te_avtab, &key); + KUNIT_ASSERT_NOT_NULL(test, node); + KUNIT_EXPECT_EQ(test, 0, node->u.trans->otype); + KUNIT_EXPECT_EQ(test, 2, node->u.trans->name_trans.table.nel); + + otype = symtab_search(&node->u.trans->name_trans, "file2"); + KUNIT_ASSERT_NOT_NULL(test, otype); + KUNIT_EXPECT_EQ(test, 49, *otype); + + otype = symtab_search(&node->u.trans->name_trans, "file3"); + KUNIT_ASSERT_NOT_NULL(test, otype); + KUNIT_EXPECT_EQ(test, 53, *otype); + + avtab_destroy(&p.te_avtab); +} + +static void filename_trans_read__comp_simple(struct kunit *test) +{ + char data[] = { + 3, 0, 0, 0, /* count */ + + 5, 0, 0, 0, /* entry 1 name len */ + 'f', 'i', 'l', 'e', '1', /* entry 1 name */ + 43, 0, 0, 0, /* entry 1 ttype */ + 44, 0, 0, 0, /* entry 1 tclass */ + 1, 0, 0, 0, /* entry 1 ndatum */ + 64, 0, 0, 0, /* entry 1 datum 1 stypes */ + 64, 0, 0, 0, + 1, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, 0, 2, 0, 0, + 45, 0, 0, 0, /* entry 1 otype */ + + 5, 0, 0, 0, /* entry 2 name len */ + 'f', 'i', 'l', 'e', '2', /* entry 2 name */ + 47, 0, 0, 0, /* entry 2 ttype */ + 48, 0, 0, 0, /* entry 2 tclass */ + 1, 0, 0, 0, /* entry 2 ndatum */ + 64, 0, 0, 0, /* entry 2 datum 1 stypes */ + 64, 0, 0, 0, + 1, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, 0, 32, 0, 0, + 49, 0, 0, 0, /* entry 2 otype */ + + 5, 0, 0, 0, /* entry 3 name len */ + 'f', 'i', 'l', 'e', '3', /* entry 3 name */ + 47, 0, 0, 0, /* entry 3 ttype */ + 48, 0, 0, 0, /* entry 3 tclass */ + 1, 0, 0, 0, /* entry 2 ndatum */ + 64, 0, 0, 0, /* entry 2 datum 1 stypes */ + 64, 0, 0, 0, + 1, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, 0, 32, 0, 0, + 53, 0, 0, 0, /* entry 3 otype */ + }; + struct policydb p = { + .te_avtab = {0}, + .policyvers = POLICYDB_VERSION_COMP_FTRANS, + }; + struct policy_file fp = { + .data = data, + .len = sizeof(data), + }; + struct avtab_key key; + struct avtab_datum *node; + u32 *otype; + + p.p_types.nprim = 54; + p.p_classes.nprim = 49; + KUNIT_ASSERT_EQ(test, 0, avtab_alloc(&p.te_avtab, 3)); + + KUNIT_ASSERT_EQ(test, 0, + avtab_filename_trans_read(&p.te_avtab, &fp, &p)); + KUNIT_EXPECT_EQ(test, 2, p.te_avtab.nel); + KUNIT_EXPECT_EQ(test, 0, fp.len); + + key = (struct avtab_key){42, 43, 44, AVTAB_TRANSITION}; + node = avtab_search(&p.te_avtab, &key); + KUNIT_ASSERT_NOT_NULL(test, node); + KUNIT_EXPECT_EQ(test, 0, node->u.trans->otype); + KUNIT_EXPECT_EQ(test, 1, node->u.trans->name_trans.table.nel); + + otype = symtab_search(&node->u.trans->name_trans, "file1"); + KUNIT_ASSERT_NOT_NULL(test, otype); + KUNIT_EXPECT_EQ(test, 45, *otype); + + key = (struct avtab_key){46, 47, 48, AVTAB_TRANSITION}; + node = avtab_search(&p.te_avtab, &key); + KUNIT_ASSERT_NOT_NULL(test, node); + KUNIT_EXPECT_EQ(test, 0, node->u.trans->otype); + KUNIT_EXPECT_EQ(test, 2, node->u.trans->name_trans.table.nel); + + otype = symtab_search(&node->u.trans->name_trans, "file2"); + KUNIT_ASSERT_NOT_NULL(test, otype); + KUNIT_EXPECT_EQ(test, 49, *otype); + + otype = symtab_search(&node->u.trans->name_trans, "file3"); + KUNIT_ASSERT_NOT_NULL(test, otype); + KUNIT_EXPECT_EQ(test, 53, *otype); + + avtab_destroy(&p.te_avtab); +} + +static void filename_trans_write__pre_filename_trans(struct kunit *test) +{ + struct policydb p = { + .te_avtab = {0}, + .policyvers = POLICYDB_VERSION_FILENAME_TRANS - 1, + }; + struct policy_file fp = { + .data = NULL, + .len = 0, + }; + + p.p_types.nprim = 54; + p.p_classes.nprim = 49; + + KUNIT_EXPECT_EQ(test, 0, + avtab_filename_trans_write(&p, &p.te_avtab, &fp)); + KUNIT_EXPECT_EQ(test, 0, fp.len); +} + +static void filename_trans_write__empty(struct kunit *test) +{ + char expected_data[] = {0, 0, 0, 0}; + char data[sizeof(expected_data)] = {0}; + struct policydb p = { + .te_avtab = {0}, + .policyvers = POLICYDB_VERSION_FILENAME_TRANS, + }; + struct policy_file fp = { + .data = data, + .len = sizeof(data), + }; + + p.p_types.nprim = 54; + p.p_classes.nprim = 49; + + KUNIT_ASSERT_EQ(test, 0, + avtab_filename_trans_write(&p, &p.te_avtab, &fp)); + + KUNIT_EXPECT_EQ(test, 0, fp.len); + KUNIT_EXPECT_TRUE(test, + !memcmp(expected_data, data, sizeof(expected_data))); +} + +static void filename_trans_write__comp_empty(struct kunit *test) +{ + char expected_data[] = {0, 0, 0, 0}; + char data[sizeof(expected_data)] = {0}; + struct policydb p = { + .te_avtab = {0}, + .policyvers = POLICYDB_VERSION_COMP_FTRANS, + }; + struct policy_file fp = { + .data = data, + .len = sizeof(data), + }; + + p.p_types.nprim = 54; + p.p_classes.nprim = 49; + + KUNIT_ASSERT_EQ(test, 0, + avtab_filename_trans_write(&p, &p.te_avtab, &fp)); + + KUNIT_EXPECT_EQ(test, 0, fp.len); + KUNIT_EXPECT_TRUE(test, + !memcmp(expected_data, data, sizeof(expected_data))); +} + +static void filename_trans_write__simple(struct kunit *test) +{ + char expected_data[] = { + 3, 0, 0, 0, /* count */ + + 5, 0, 0, 0, /* entry 1 name len */ + 'f', 'i', 'l', 'e', '1', /* entry 1 name */ + 42, 0, 0, 0, /* entry 1 stype */ + 43, 0, 0, 0, /* entry 1 ttype */ + 44, 0, 0, 0, /* entry 1 tclass */ + 45, 0, 0, 0, /* entry 1 otype */ + + 5, 0, 0, 0, /* entry 2 name len */ + 'f', 'i', 'l', 'e', '2', /* entry 2 name */ + 46, 0, 0, 0, /* entry 2 stype */ + 47, 0, 0, 0, /* entry 2 ttype */ + 48, 0, 0, 0, /* entry 2 tclass */ + 49, 0, 0, 0, /* entry 2 otype */ + + 5, 0, 0, 0, /* entry 3 name len */ + 'f', 'i', 'l', 'e', '3', /* entry 3 name */ + 46, 0, 0, 0, /* entry 3 stype */ + 47, 0, 0, 0, /* entry 3 ttype */ + 48, 0, 0, 0, /* entry 3 tclass */ + 53, 0, 0, 0, /* entry 3 otype */ + }; + char data[sizeof(expected_data)] = {0}; + struct policydb p = { + .te_avtab = {0}, + .policyvers = POLICYDB_VERSION_FILENAME_TRANS, + }; + struct policy_file fp = { + .data = data, + .len = sizeof(data), + }; + u32 otypes[] = {45, 49, 53}; + struct hashtab_node nhnodes[] = { + {"file1", &otypes[0], NULL}, + {"file2", &otypes[1], &nhnodes[2]}, + {"file3", &otypes[2], NULL}, + }; + struct hashtab_node *nhtable[] = {&nhnodes[0], &nhnodes[1]}; + struct avtab_trans transs[] = { + {0, {{&nhtable[0], 1, 1}, 0}}, + {0, {{&nhtable[1], 1, 2}, 0}}, + }; + struct avtab_node nodes[] = { + {{42, 43, 44, AVTAB_TRANSITION}, + {.u.trans = &transs[0]}, NULL}, + {{46, 47, 48, AVTAB_TRANSITION}, + {.u.trans = &transs[1]}, NULL}, + }; + struct avtab_node *htable[] = {&nodes[0], &nodes[1]}; + + p.p_types.nprim = 54; + p.p_classes.nprim = 49; + p.te_avtab.htable = htable; + p.te_avtab.nslot = 2; + p.te_avtab.nel = 2; + + KUNIT_ASSERT_EQ(test, 0, + avtab_filename_trans_write(&p, &p.te_avtab, &fp)); + + KUNIT_ASSERT_EQ(test, 0, fp.len); + KUNIT_EXPECT_TRUE(test, + !memcmp(expected_data, data, sizeof(expected_data))); +} + +static void filename_trans_write__comp_simple(struct kunit *test) +{ + char expected_data[] = { + 3, 0, 0, 0, /* count */ + + 5, 0, 0, 0, /* entry 1 name len */ + 'f', 'i', 'l', 'e', '1', /* entry 1 name */ + 43, 0, 0, 0, /* entry 1 ttype */ + 44, 0, 0, 0, /* entry 1 tclass */ + 1, 0, 0, 0, /* entry 1 ndatum */ + 64, 0, 0, 0, /* entry 1 datum 1 stypes */ + 64, 0, 0, 0, + 1, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, 0, 2, 0, 0, + 45, 0, 0, 0, /* entry 1 otype */ + + 5, 0, 0, 0, /* entry 2 name len */ + 'f', 'i', 'l', 'e', '2', /* entry 2 name */ + 47, 0, 0, 0, /* entry 2 ttype */ + 48, 0, 0, 0, /* entry 2 tclass */ + 1, 0, 0, 0, /* entry 2 ndatum */ + 64, 0, 0, 0, /* entry 2 datum 1 stypes */ + 64, 0, 0, 0, + 1, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, 0, 32, 0, 0, + 49, 0, 0, 0, /* entry 2 otype */ + + 5, 0, 0, 0, /* entry 3 name len */ + 'f', 'i', 'l', 'e', '3', /* entry 3 name */ + 47, 0, 0, 0, /* entry 3 ttype */ + 48, 0, 0, 0, /* entry 3 tclass */ + 1, 0, 0, 0, /* entry 2 ndatum */ + 64, 0, 0, 0, /* entry 2 datum 1 stypes */ + 64, 0, 0, 0, + 1, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, 0, 32, 0, 0, + 53, 0, 0, 0, /* entry 3 otype */ + }; + char data[sizeof(expected_data)] = {0}; + struct policydb p = { + .te_avtab = {0}, + .policyvers = POLICYDB_VERSION_COMP_FTRANS, + }; + struct policy_file fp = { + .data = data, + .len = sizeof(data), + }; + u32 otypes[] = {45, 49, 53}; + struct hashtab_node nhnodes[] = { + {"file1", &otypes[0], NULL}, + {"file2", &otypes[1], &nhnodes[2]}, + {"file3", &otypes[2], NULL}, + }; + struct hashtab_node *nhtable[] = {&nhnodes[0], &nhnodes[1]}; + struct avtab_trans transs[] = { + {0, {{&nhtable[0], 1, 1}, 0}}, + {0, {{&nhtable[1], 1, 2}, 0}}, + }; + struct avtab_node nodes[] = { + {{42, 43, 44, AVTAB_TRANSITION}, + {.u.trans = &transs[0]}, NULL}, + {{46, 47, 48, AVTAB_TRANSITION}, + {.u.trans = &transs[1]}, NULL}, + }; + struct avtab_node *htable[] = {&nodes[0], &nodes[1]}; + + p.p_types.nprim = 54; + p.p_classes.nprim = 49; + p.te_avtab.htable = htable; + p.te_avtab.nslot = 2; + p.te_avtab.nel = 2; + + KUNIT_ASSERT_EQ(test, 0, + avtab_filename_trans_write(&p, &p.te_avtab, &fp)); + + KUNIT_ASSERT_EQ(test, 0, fp.len); + KUNIT_EXPECT_TRUE(test, + !memcmp(expected_data, data, sizeof(expected_data))); +} + +static void read__pre_avtab_ftrans(struct kunit *test) +{ + char data[] = { + 2, 0, 0, 0, /* nel */ + + 42, 0, /* entry 1 source type */ + 43, 0, /* entry 1 target type */ + 44, 0, /* entry 1 target class */ + AVTAB_TRANSITION, 0, /* entry 1 specified */ + 45, 0, 0, 0, /* entry 1 otype */ + + 46, 0, /* entry 2 source type */ + 47, 0, /* entry 2 target type */ + 48, 0, /* entry 2 target class */ + AVTAB_TRANSITION, 0, /* entry 2 specified */ + 49, 0, 0, 0, /* entry 2 otype */ + }; + struct policydb p = { + .te_avtab = {0}, + .policyvers = POLICYDB_VERSION_COMP_FTRANS, + }; + struct policy_file fp = { + .data = data, + .len = sizeof(data), + }; + struct avtab_key key; + struct avtab_datum *node; + + p.p_types.nprim = 54; + p.p_classes.nprim = 49; + KUNIT_ASSERT_EQ(test, 0, + avtab_read(&p.te_avtab, &fp, &p)); + KUNIT_EXPECT_EQ(test, 2, p.te_avtab.nel); + KUNIT_EXPECT_EQ(test, 0, fp.len); + + key = (struct avtab_key){42, 43, 44, AVTAB_TRANSITION}; + node = avtab_search(&p.te_avtab, &key); + KUNIT_ASSERT_NOT_NULL(test, node); + KUNIT_EXPECT_EQ(test, 45, node->u.trans->otype); + KUNIT_EXPECT_EQ(test, 0, node->u.trans->name_trans.table.nel); + + key = (struct avtab_key){46, 47, 48, AVTAB_TRANSITION}; + node = avtab_search(&p.te_avtab, &key); + KUNIT_ASSERT_NOT_NULL(test, node); + KUNIT_EXPECT_EQ(test, 49, node->u.trans->otype); + KUNIT_EXPECT_EQ(test, 0, node->u.trans->name_trans.table.nel); + + avtab_destroy(&p.te_avtab); +} + +static void read__simple(struct kunit *test) +{ + char data[] = { + 2, 0, 0, 0, /* nel */ + + 42, 0, /* entry 1 source type */ + 43, 0, /* entry 1 target type */ + 44, 0, /* entry 1 target class */ + AVTAB_TRANSITION, 0, /* entry 1 specified */ + 41, 0, 0, 0, /* entry 1 otype */ + 1, 0, 0, 0, /* entry 1 nfnts */ + 45, 0, 0, 0, /* entry 1 fnt 1 otype */ + 5, 0, 0, 0, /* entry 1 fnt 1 name len */ + 'f', 'i', 'l', 'e', '1', /* entry 1 fnt 1 name */ + + 46, 0, /* entry 2 source type */ + 47, 0, /* entry 2 target type */ + 48, 0, /* entry 2 target class */ + AVTAB_TRANSITION, 0, /* entry 2 specified */ + 40, 0, 0, 0, /* entry 2 otype */ + 2, 0, 0, 0, /* entry 2 nfnts */ + 49, 0, 0, 0, /* entry 2 fnt 1 otype */ + 5, 0, 0, 0, /* entry 2 fnt 1 name len */ + 'f', 'i', 'l', 'e', '2', /* entry 2 fnt 1 name */ + 50, 0, 0, 0, /* entry 2 fnt 2 otype */ + 5, 0, 0, 0, /* entry 2 fnt 2 name len */ + 'f', 'i', 'l', 'e', '3', /* entry 2 fnt 2 name */ + }; + struct policydb p = { + .te_avtab = {0}, + .policyvers = POLICYDB_VERSION_AVTAB_FTRANS, + }; + struct policy_file fp = { + .data = data, + .len = sizeof(data), + }; + struct avtab_key key; + struct avtab_datum *node; + u32 *otype; + + p.p_types.nprim = 54; + p.p_classes.nprim = 49; + KUNIT_ASSERT_EQ(test, 0, + avtab_read(&p.te_avtab, &fp, &p)); + KUNIT_EXPECT_EQ(test, 2, p.te_avtab.nel); + KUNIT_EXPECT_EQ(test, 0, fp.len); + + key = (struct avtab_key){42, 43, 44, AVTAB_TRANSITION}; + node = avtab_search(&p.te_avtab, &key); + KUNIT_ASSERT_NOT_NULL(test, node); + KUNIT_EXPECT_EQ(test, 41, node->u.trans->otype); + KUNIT_EXPECT_EQ(test, 1, node->u.trans->name_trans.table.nel); + + otype = symtab_search(&node->u.trans->name_trans, "file1"); + KUNIT_ASSERT_NOT_NULL(test, otype); + KUNIT_EXPECT_EQ(test, 45, *otype); + + key = (struct avtab_key){46, 47, 48, AVTAB_TRANSITION}; + node = avtab_search(&p.te_avtab, &key); + KUNIT_ASSERT_NOT_NULL(test, node); + KUNIT_EXPECT_EQ(test, 40, node->u.trans->otype); + KUNIT_EXPECT_EQ(test, 2, node->u.trans->name_trans.table.nel); + + otype = symtab_search(&node->u.trans->name_trans, "file2"); + KUNIT_ASSERT_NOT_NULL(test, otype); + KUNIT_EXPECT_EQ(test, 49, *otype); + + otype = symtab_search(&node->u.trans->name_trans, "file3"); + KUNIT_ASSERT_NOT_NULL(test, otype); + KUNIT_EXPECT_EQ(test, 50, *otype); + + avtab_destroy(&p.te_avtab); +} + +static void write__pre_avtab_ftrans(struct kunit *test) +{ + char expected_data[] = { + 1, 0, 0, 0, /* nel */ + + 46, 0, /* entry 2 source type */ + 47, 0, /* entry 2 target type */ + 48, 0, /* entry 2 target class */ + AVTAB_TRANSITION, 0, /* entry 2 specified */ + 40, 0, 0, 0, /* entry 2 otype */ + }; + char data[sizeof(expected_data)] = {0}; + struct policydb p = { + .te_avtab = {0}, + .policyvers = POLICYDB_VERSION_COMP_FTRANS, + }; + struct policy_file fp = { + .data = data, + .len = sizeof(data), + }; + u32 otypes[] = {45, 49, 53}; + struct hashtab_node nhnodes[] = { + {"file1", &otypes[0], NULL}, + {"file2", &otypes[1], &nhnodes[2]}, + {"file3", &otypes[2], NULL}, + }; + struct hashtab_node *nhtable[] = {&nhnodes[0], &nhnodes[1]}; + struct avtab_trans transs[] = { + {0, {{&nhtable[0], 1, 1}, 0}}, + {40, {{&nhtable[1], 1, 2}, 0}}, + }; + struct avtab_node nodes[] = { + {{42, 43, 44, AVTAB_TRANSITION}, + {.u.trans = &transs[0]}, NULL}, + {{46, 47, 48, AVTAB_TRANSITION}, + {.u.trans = &transs[1]}, NULL}, + }; + struct avtab_node *htable[] = {&nodes[0], &nodes[1]}; + + p.p_types.nprim = 54; + p.p_classes.nprim = 49; + p.te_avtab.htable = htable; + p.te_avtab.nslot = 2; + p.te_avtab.nel = 2; + + KUNIT_ASSERT_EQ(test, 0, avtab_write(&p, &p.te_avtab, &fp)); + + KUNIT_ASSERT_EQ(test, 0, fp.len); + KUNIT_EXPECT_TRUE(test, + !memcmp(expected_data, data, sizeof(expected_data))); +} + +static void write__simple(struct kunit *test) +{ + char expected_data[] = { + 2, 0, 0, 0, /* nel */ + + 42, 0, /* entry 1 source type */ + 43, 0, /* entry 1 target type */ + 44, 0, /* entry 1 target class */ + AVTAB_TRANSITION, 0, /* entry 1 specified */ + 41, 0, 0, 0, /* entry 1 otype */ + 1, 0, 0, 0, /* entry 1 nfnts */ + 45, 0, 0, 0, /* entry 1 fnt 1 otype */ + 5, 0, 0, 0, /* entry 1 fnt 1 name len */ + 'f', 'i', 'l', 'e', '1', /* entry 1 fnt 1 name */ + + 46, 0, /* entry 2 source type */ + 47, 0, /* entry 2 target type */ + 48, 0, /* entry 2 target class */ + AVTAB_TRANSITION, 0, /* entry 2 specified */ + 40, 0, 0, 0, /* entry 2 otype */ + 2, 0, 0, 0, /* entry 2 nfnts */ + 49, 0, 0, 0, /* entry 1 fnt 1 otype */ + 5, 0, 0, 0, /* entry 1 fnt 1 name len */ + 'f', 'i', 'l', 'e', '2', /* entry 1 fnt 1 name */ + 50, 0, 0, 0, /* entry 1 fnt 1 otype */ + 5, 0, 0, 0, /* entry 1 fnt 1 name len */ + 'f', 'i', 'l', 'e', '3', /* entry 1 fnt 1 name */ + }; + char data[sizeof(expected_data)] = {0}; + struct policydb p = { + .te_avtab = {0}, + .policyvers = POLICYDB_VERSION_AVTAB_FTRANS, + }; + struct policy_file fp = { + .data = data, + .len = sizeof(data), + }; + u32 otypes[] = {45, 49, 50}; + struct hashtab_node nhnodes[] = { + {"file1", &otypes[0], NULL}, + {"file2", &otypes[1], &nhnodes[2]}, + {"file3", &otypes[2], NULL}, + }; + struct hashtab_node *nhtable[] = {&nhnodes[0], &nhnodes[1]}; + struct avtab_trans transs[] = { + {41, {{&nhtable[0], 1, 1}, 0}}, + {40, {{&nhtable[1], 1, 2}, 0}}, + }; + struct avtab_node nodes[] = { + {{42, 43, 44, AVTAB_TRANSITION}, + {.u.trans = &transs[0]}, NULL}, + {{46, 47, 48, AVTAB_TRANSITION}, + {.u.trans = &transs[1]}, NULL}, + }; + struct avtab_node *htable[] = {&nodes[0], &nodes[1]}; + + p.p_types.nprim = 54; + p.p_classes.nprim = 49; + p.te_avtab.htable = htable; + p.te_avtab.nslot = 2; + p.te_avtab.nel = 2; + + KUNIT_ASSERT_EQ(test, 0, avtab_write(&p, &p.te_avtab, &fp)); + + KUNIT_ASSERT_EQ(test, 0, fp.len); + KUNIT_EXPECT_TRUE(test, + !memcmp(expected_data, data, sizeof(expected_data))); +} + +static struct kunit_case avtab_test_cases[] = { + KUNIT_CASE(filename_trans_read__pre_filename_trans), + KUNIT_CASE(filename_trans_read__empty), + KUNIT_CASE(filename_trans_read__comp_empty), + KUNIT_CASE(filename_trans_read__simple), + KUNIT_CASE(filename_trans_read__comp_simple), + + KUNIT_CASE(filename_trans_write__pre_filename_trans), + KUNIT_CASE(filename_trans_write__empty), + KUNIT_CASE(filename_trans_write__comp_empty), + KUNIT_CASE(filename_trans_write__simple), + KUNIT_CASE(filename_trans_write__comp_simple), + + KUNIT_CASE(read__pre_avtab_ftrans), + KUNIT_CASE(read__simple), + + KUNIT_CASE(write__pre_avtab_ftrans), + KUNIT_CASE(write__simple), + {0}, +}; + +static struct kunit_suite avtab_test_suite = { + .name = "security-selinux-avtab", + .test_cases = avtab_test_cases, +}; + +kunit_test_suite(avtab_test_suite); + +MODULE_LICENSE("GPL"); diff --git a/security/selinux/ss/symtab.c b/security/selinux/ss/symtab.c index c42a6648a07d..c53863d5ed40 100644 --- a/security/selinux/ss/symtab.c +++ b/security/selinux/ss/symtab.c @@ -52,3 +52,6 @@ void *symtab_search(struct symtab *s, const char *name) { return hashtab_search(&s->table, name, symtab_key_params); } + +/* Export for avtab KUnit tests */ +EXPORT_SYMBOL_GPL(symtab_search); From patchwork Wed May 31 11:29:28 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Juraj Marcin X-Patchwork-Id: 13262008 X-Patchwork-Delegate: paul@paul-moore.com Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 79361C7EE31 for ; Wed, 31 May 2023 11:32:51 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233024AbjEaLcu (ORCPT ); Wed, 31 May 2023 07:32:50 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:41746 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232803AbjEaLct (ORCPT ); Wed, 31 May 2023 07:32:49 -0400 Received: from sender11-of-o52.zoho.eu (sender11-of-o52.zoho.eu [31.186.226.238]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 9D4D4B2 for ; Wed, 31 May 2023 04:32:47 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1685532763; cv=none; d=zohomail.eu; s=zohoarc; b=HKDOCR7cScIUMnyjHsu2s+0K8M0ke2fIW8HeX0T7OREP0EtRwoqg1nhigIJzJ7KHTP+U3l2bTfS7wTud/CRSyP7Dl6K0CWiraHDnulgHVeJKmEA0TaUBchEEPaS2DX0m3Qm4r4ll4jKliJOoJViPQR8os9mgr/RR3Ec5GQsEoZU= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.eu; s=zohoarc; t=1685532763; h=Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:To; bh=I+52IdUDvPBaUKjSOxvO/U3gSWccVwotlotI9R/0pOw=; b=W/ItSZQXeamStiOYSFr77QObatEnd3j807eMdIzabi1F2Pumwr1y6McLBIE3fXu3poaeMYyjlZhLzY0sYCkFhBzel1VYoU2VWeceZlyfnTlJqZH3Xnuvg4TH5zPGlHk48uf+Rzn/qyCkxJGJxd9ndHMFgeduAlQpAY0FiqFDYlY= ARC-Authentication-Results: i=1; mx.zohomail.eu; dkim=pass header.i=jurajmarcin.com; spf=pass smtp.mailfrom=juraj@jurajmarcin.com; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1685532763; s=zoho; d=jurajmarcin.com; i=juraj@jurajmarcin.com; h=From:From:To:To:Cc:Cc:Subject:Subject:Date:Date:Message-Id:Message-Id:In-Reply-To:References:MIME-Version:Content-Transfer-Encoding:Reply-To; bh=I+52IdUDvPBaUKjSOxvO/U3gSWccVwotlotI9R/0pOw=; b=SWqVUmbbt9ykPYIvbcvGbLQvwQgtKsD7V1qjpkWA0Dlna0uGOAu09rqVG4w6ORPi wwX74nf/eauPLap2Zl4lWuBKFZVpwT0DM6/ahBja1etx628B76mI5bc6KInjDGTm8Av GL1MgEwQpQnXU8Qt1DxBMYOz9uLdwUPhKMk+eKMY= Received: from morty01.jurajmarcin.com (129.159.244.31 [129.159.244.31]) by mx.zoho.eu with SMTPS id 1685532762045924.6464679151087; Wed, 31 May 2023 13:32:42 +0200 (CEST) Received: from jmarcin-t14s-01.redhat.com (unknown [147.251.183.113]) by morty01.jurajmarcin.com (Postfix) with ESMTPSA id 6D5C92081F76; Wed, 31 May 2023 11:32:41 +0000 (UTC) From: Juraj Marcin To: Paul Moore Cc: Stephen Smalley , selinux@vger.kernel.org, Ondrej Mosnacek Subject: [PATCH 5/5] selinux: add prefix/suffix matching support to filename type transitions Date: Wed, 31 May 2023 13:29:28 +0200 Message-Id: <20230531112927.1957093-6-juraj@jurajmarcin.com> In-Reply-To: <20230531112927.1957093-1-juraj@jurajmarcin.com> References: <20230531112927.1957093-1-juraj@jurajmarcin.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: selinux@vger.kernel.org Currently, filename type transitions support only exact name matching. However, in practice, the names contain variable parts. This leads to many duplicated rules in the policy that differ only in the part of the name, or it is even impossible to cover all possible combinations. This patch extends the filename type transitions structures to include new types of filename transitions - prefix and suffix filename transitions. It also implements the reading and writing of those rules in the kernel binary policy format together with increasing its version. Last, it updates the function responsible for determining the new context to also include the prefix and suffix filename transitions in the process. It does that by first checking for the exact match, then the longest matching prefix and then the longest matching suffix, in that order. That way the exact match rules have precedence before new rules, to ensure compatibility with older policies. Reviewed-by: Ondrej Mosnacek Signed-off-by: Juraj Marcin --- security/selinux/include/security.h | 3 +- security/selinux/ss/avtab.c | 53 +++++++++++++++++++-- security/selinux/ss/avtab.h | 2 + security/selinux/ss/avtab_test.c | 16 +++++++ security/selinux/ss/policydb.c | 5 ++ security/selinux/ss/services.c | 71 ++++++++++++++++++++++++----- 6 files changed, 133 insertions(+), 17 deletions(-) diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index c483680fe22e..0cf0767d061b 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -47,10 +47,11 @@ #define POLICYDB_VERSION_GLBLUB 32 #define POLICYDB_VERSION_COMP_FTRANS 33 /* compressed filename transitions */ #define POLICYDB_VERSION_AVTAB_FTRANS 34 /* filename transitions moved to avtab */ +#define POLICYDB_VERSION_PREFIX_SUFFIX 35 /* prefix/suffix support for filename transitions */ /* Range of policy versions we understand*/ #define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE -#define POLICYDB_VERSION_MAX POLICYDB_VERSION_AVTAB_FTRANS +#define POLICYDB_VERSION_MAX POLICYDB_VERSION_PREFIX_SUFFIX /* Mask for just the mount related flags */ #define SE_MNTMASK 0x0f diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c index db7123670ef8..f58707930189 100644 --- a/security/selinux/ss/avtab.c +++ b/security/selinux/ss/avtab.c @@ -301,6 +301,12 @@ static void avtab_trans_destroy(struct avtab_trans *trans) { hashtab_map(&trans->name_trans.table, avtab_trans_destroy_helper, NULL); hashtab_destroy(&trans->name_trans.table); + hashtab_map(&trans->prefix_trans.table, avtab_trans_destroy_helper, + NULL); + hashtab_destroy(&trans->prefix_trans.table); + hashtab_map(&trans->suffix_trans.table, avtab_trans_destroy_helper, + NULL); + hashtab_destroy(&trans->suffix_trans.table); } void avtab_destroy(struct avtab *h) @@ -517,6 +523,14 @@ static int avtab_trans_read(void *fp, struct policydb *pol, if (rc) goto bad; + if (pol->policyvers >= POLICYDB_VERSION_PREFIX_SUFFIX) { + rc = avtab_trans_read_name_trans(pol, &trans->prefix_trans, fp); + if (rc) + goto bad; + rc = avtab_trans_read_name_trans(pol, &trans->suffix_trans, fp); + if (rc) + goto bad; + } return 0; bad: @@ -706,9 +720,14 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, * also each transition entry must meet at least one condition * to be considered non-empty: * - set (non-zero) otype - * - non-empty filename transitions table + * - non-empty full name transitions table + * - non-empty prefix name transitions table + * - non-empty suffix name transitions table */ - if (!otype && !datum.u.trans->name_trans.table.nel) { + if (!otype && + !datum.u.trans->name_trans.table.nel && + !datum.u.trans->prefix_trans.table.nel && + !datum.u.trans->suffix_trans.table.nel) { pr_err("SELinux: avtab: empty transition\n"); avtab_trans_destroy(&trans); return -EINVAL; @@ -809,18 +828,44 @@ static int avtab_trans_write(struct policydb *p, struct avtab_trans *cur, __le32 buf32[2]; if (p->policyvers >= POLICYDB_VERSION_AVTAB_FTRANS) { - /* write otype and number of filename transitions */ + /* write otype and number of name transitions */ buf32[0] = cpu_to_le32(cur->otype); buf32[1] = cpu_to_le32(cur->name_trans.table.nel); rc = put_entry(buf32, sizeof(u32), 2, fp); if (rc) return rc; - /* write filename transitions */ + /* write name transitions */ rc = hashtab_map(&cur->name_trans.table, avtab_trans_write_helper, fp); if (rc) return rc; + + if (p->policyvers >= POLICYDB_VERSION_PREFIX_SUFFIX) { + /* write number of prefix transitions */ + buf32[0] = cpu_to_le32(cur->prefix_trans.table.nel); + rc = put_entry(buf32, sizeof(u32), 1, fp); + if (rc) + return rc; + + /* write prefix transitions */ + rc = hashtab_map(&cur->prefix_trans.table, + avtab_trans_write_helper, fp); + if (rc) + return rc; + + /* write number of suffix transitions */ + buf32[0] = cpu_to_le32(cur->suffix_trans.table.nel); + rc = put_entry(buf32, sizeof(u32), 1, fp); + if (rc) + return rc; + + /* write suffix transitions */ + rc = hashtab_map(&cur->suffix_trans.table, + avtab_trans_write_helper, fp); + if (rc) + return rc; + } } else if (cur->otype) { buf32[0] = cpu_to_le32(cur->otype); rc = put_entry(buf32, sizeof(u32), 1, fp); diff --git a/security/selinux/ss/avtab.h b/security/selinux/ss/avtab.h index 162ef1be85e7..929e322715d1 100644 --- a/security/selinux/ss/avtab.h +++ b/security/selinux/ss/avtab.h @@ -51,6 +51,8 @@ struct avtab_key { struct avtab_trans { u32 otype; /* default resulting type of the new object */ struct symtab name_trans; /* filename transitions */ + struct symtab prefix_trans; /* prefix filename transitions */ + struct symtab suffix_trans; /* prefix filename transitions */ }; /* diff --git a/security/selinux/ss/avtab_test.c b/security/selinux/ss/avtab_test.c index daa8e4cfaeb2..7e63204629fd 100644 --- a/security/selinux/ss/avtab_test.c +++ b/security/selinux/ss/avtab_test.c @@ -124,6 +124,8 @@ static void filename_trans_read__simple(struct kunit *test) KUNIT_ASSERT_NOT_NULL(test, node); KUNIT_EXPECT_EQ(test, 0, node->u.trans->otype); KUNIT_EXPECT_EQ(test, 1, node->u.trans->name_trans.table.nel); + KUNIT_EXPECT_EQ(test, 0, node->u.trans->prefix_trans.table.nel); + KUNIT_EXPECT_EQ(test, 0, node->u.trans->suffix_trans.table.nel); otype = symtab_search(&node->u.trans->name_trans, "file1"); KUNIT_ASSERT_NOT_NULL(test, otype); @@ -134,6 +136,8 @@ static void filename_trans_read__simple(struct kunit *test) KUNIT_ASSERT_NOT_NULL(test, node); KUNIT_EXPECT_EQ(test, 0, node->u.trans->otype); KUNIT_EXPECT_EQ(test, 2, node->u.trans->name_trans.table.nel); + KUNIT_EXPECT_EQ(test, 0, node->u.trans->prefix_trans.table.nel); + KUNIT_EXPECT_EQ(test, 0, node->u.trans->suffix_trans.table.nel); otype = symtab_search(&node->u.trans->name_trans, "file2"); KUNIT_ASSERT_NOT_NULL(test, otype); @@ -213,6 +217,8 @@ static void filename_trans_read__comp_simple(struct kunit *test) KUNIT_ASSERT_NOT_NULL(test, node); KUNIT_EXPECT_EQ(test, 0, node->u.trans->otype); KUNIT_EXPECT_EQ(test, 1, node->u.trans->name_trans.table.nel); + KUNIT_EXPECT_EQ(test, 0, node->u.trans->prefix_trans.table.nel); + KUNIT_EXPECT_EQ(test, 0, node->u.trans->suffix_trans.table.nel); otype = symtab_search(&node->u.trans->name_trans, "file1"); KUNIT_ASSERT_NOT_NULL(test, otype); @@ -223,6 +229,8 @@ static void filename_trans_read__comp_simple(struct kunit *test) KUNIT_ASSERT_NOT_NULL(test, node); KUNIT_EXPECT_EQ(test, 0, node->u.trans->otype); KUNIT_EXPECT_EQ(test, 2, node->u.trans->name_trans.table.nel); + KUNIT_EXPECT_EQ(test, 0, node->u.trans->prefix_trans.table.nel); + KUNIT_EXPECT_EQ(test, 0, node->u.trans->suffix_trans.table.nel); otype = symtab_search(&node->u.trans->name_trans, "file2"); KUNIT_ASSERT_NOT_NULL(test, otype); @@ -493,12 +501,16 @@ static void read__pre_avtab_ftrans(struct kunit *test) KUNIT_ASSERT_NOT_NULL(test, node); KUNIT_EXPECT_EQ(test, 45, node->u.trans->otype); KUNIT_EXPECT_EQ(test, 0, node->u.trans->name_trans.table.nel); + KUNIT_EXPECT_EQ(test, 0, node->u.trans->prefix_trans.table.nel); + KUNIT_EXPECT_EQ(test, 0, node->u.trans->suffix_trans.table.nel); key = (struct avtab_key){46, 47, 48, AVTAB_TRANSITION}; node = avtab_search(&p.te_avtab, &key); KUNIT_ASSERT_NOT_NULL(test, node); KUNIT_EXPECT_EQ(test, 49, node->u.trans->otype); KUNIT_EXPECT_EQ(test, 0, node->u.trans->name_trans.table.nel); + KUNIT_EXPECT_EQ(test, 0, node->u.trans->prefix_trans.table.nel); + KUNIT_EXPECT_EQ(test, 0, node->u.trans->suffix_trans.table.nel); avtab_destroy(&p.te_avtab); } @@ -555,6 +567,8 @@ static void read__simple(struct kunit *test) KUNIT_ASSERT_NOT_NULL(test, node); KUNIT_EXPECT_EQ(test, 41, node->u.trans->otype); KUNIT_EXPECT_EQ(test, 1, node->u.trans->name_trans.table.nel); + KUNIT_EXPECT_EQ(test, 0, node->u.trans->prefix_trans.table.nel); + KUNIT_EXPECT_EQ(test, 0, node->u.trans->suffix_trans.table.nel); otype = symtab_search(&node->u.trans->name_trans, "file1"); KUNIT_ASSERT_NOT_NULL(test, otype); @@ -565,6 +579,8 @@ static void read__simple(struct kunit *test) KUNIT_ASSERT_NOT_NULL(test, node); KUNIT_EXPECT_EQ(test, 40, node->u.trans->otype); KUNIT_EXPECT_EQ(test, 2, node->u.trans->name_trans.table.nel); + KUNIT_EXPECT_EQ(test, 0, node->u.trans->prefix_trans.table.nel); + KUNIT_EXPECT_EQ(test, 0, node->u.trans->suffix_trans.table.nel); otype = symtab_search(&node->u.trans->name_trans, "file2"); KUNIT_ASSERT_NOT_NULL(test, otype); diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 96b8d5bd8e4e..db759ec3e1ce 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -162,6 +162,11 @@ static const struct policydb_compat_info policydb_compat[] = { .sym_num = SYM_NUM, .ocon_num = OCON_NUM, }, + { + .version = POLICYDB_VERSION_PREFIX_SUFFIX, + .sym_num = SYM_NUM, + .ocon_num = OCON_NUM, + }, }; static const struct policydb_compat_info *policydb_lookup_compat(int version) diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 131647e7ec68..2faf92bf12da 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -1661,6 +1661,60 @@ static int compute_sid_handle_invalid_context( return -EACCES; } +static int security_compute_type_trans_otype(struct avtab_trans *trans, + const char *name, u32 *res_type) +{ + u32 *otype; + size_t len; + char *namedup = NULL; + size_t i; + + /* + * use default otype if not empty and then try to find more specific + * rule using name + */ + if (trans->otype) + *res_type = trans->otype; + if (!name) + return 0; + + /* try to find full name */ + otype = symtab_search(&trans->name_trans, name); + if (otype) { + *res_type = *otype; + return 0; + } + + /* copy name for shortening */ + len = strlen(name); + namedup = kmemdup(name, len + 1, GFP_KERNEL); + if (!namedup) + return -ENOMEM; + + /* try to find possible prefixes of name starting from the longest */ + for (i = len; i > 0; i--) { + namedup[i] = '\0'; + otype = symtab_search(&trans->prefix_trans, namedup); + if (otype) { + kfree(namedup); + *res_type = *otype; + return 0; + } + } + kfree(namedup); + + /*try to find possible suffixes of name starting from the longest */ + for (i = 0; i < len; i++) { + otype = symtab_search(&trans->suffix_trans, &name[i]); + if (otype) { + *res_type = *otype; + return 0; + } + } + + return 0; +} + static int security_compute_sid(u32 ssid, u32 tsid, u16 orig_tclass, @@ -1802,18 +1856,11 @@ static int security_compute_sid(u32 ssid, if (avdatum) { /* Use the type from the type transition/member/change rule. */ if (avkey.specified & AVTAB_TRANSITION) { - /* - * use default otype if not empty and then to try to - * find more specific rule using objname - */ - if (avdatum->u.trans->otype) - newcontext.type = avdatum->u.trans->otype; - if (objname) { - otype = symtab_search(&avdatum->u.trans->name_trans, - objname); - if (otype) - newcontext.type = *otype; - } + rc = security_compute_type_trans_otype(avdatum->u.trans, + objname, + &newcontext.type); + if (rc) + goto out_unlock; } else { newcontext.type = avdatum->u.data; }