Message ID | 20230531112927.1957093-6-juraj@jurajmarcin.com (mailing list archive) |
---|---|
State | Changes Requested |
Delegated to: | Paul Moore |
Headers | show |
Series | selinux: add prefix/suffix matching to filename type transitions | expand |
On Wed, May 31, 2023 at 7:32 AM Juraj Marcin <juraj@jurajmarcin.com> wrote: > > 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 <omosnace@redhat.com> > Signed-off-by: Juraj Marcin <juraj@jurajmarcin.com> > --- > 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/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); This is called in various contexts that cannot sleep; hence, the allocation must be GFP_ATOMIC here. With the proper config options (CONFIG_DEBUG_ATOMIC_SLEEP=y) you would have seen repeated warnings about this in the kernel messages, ala: [ 219.944942] BUG: sleeping function called from invalid context at include/linux/sched/mm.h:306 [ 219.944951] in_atomic(): 0, irqs_disabled(): 0, non_block: 0, pid: 1, name: systemd [ 219.944956] preempt_count: 0, expected: 0 [ 219.944959] RCU nest depth: 1, expected: 0 [ 219.944963] INFO: lockdep is turned off. [ 219.944967] CPU: 0 PID: 1 Comm: systemd Tainted: G B W 6.5.0-rc1+ #32 [ 219.944979] Call Trace: [ 219.944982] <TASK> [ 219.944986] dump_stack_lvl+0x75/0x90 [ 219.944997] __might_resched+0x1e1/0x310 [ 219.945008] ? security_compute_sid.part.0+0x9d8/0xe50 [ 219.945017] __kmem_cache_alloc_node+0x343/0x380 [ 219.945026] ? security_compute_sid.part.0+0x9d8/0xe50 [ 219.945033] ? rcu_is_watching+0x23/0x50 [ 219.945046] ? security_compute_sid.part.0+0x9d8/0xe50 [ 219.945054] __kmalloc_node_track_caller+0x52/0x160 [ 219.945068] kmemdup+0x22/0x50 [ 219.945078] security_compute_sid.part.0+0x9d8/0xe50 [ 219.945093] ? __pfx_security_compute_sid.part.0+0x10/0x10 [ 219.945101] ? rcu_is_watching+0x23/0x50 [ 219.945110] ? lock_release+0xa0/0x380 [ 219.945116] ? avc_has_perm_noaudit+0xb4/0x250 [ 219.945127] ? __pfx_lock_release+0x10/0x10 [ 219.945134] ? rcu_is_watching+0x23/0x50 [ 219.945143] ? lock_acquire+0xb5/0x390 [ 219.945148] ? __filename_parentat+0x282/0x350 [ 219.945160] ? avc_has_perm_noaudit+0xcc/0x250 [ 219.945178] security_transition_sid+0x63/0xa0 [ 219.945191] may_create+0x16a/0x1c0 [ 219.945201] ? __pfx_may_create+0x10/0x10 [ 219.945207] ? selinux_inode_permission+0x1c6/0x290 [ 219.945216] ? __pfx_selinux_inode_permission+0x10/0x10 [ 219.945226] ? kernfs_iop_permission+0x84/0xa0 [ 219.945238] security_inode_mkdir+0x61/0x80 [ 219.945251] vfs_mkdir+0x226/0x380 [ 219.945262] do_mkdirat+0x1a8/0x1d0 [ 219.945272] ? __pfx_do_mkdirat+0x10/0x10 [ 219.945280] ? getname_flags.part.0+0xc6/0x250 [ 219.945290] __x64_sys_mkdir+0x78/0xa0 [ 219.945300] do_syscall_64+0x3c/0x90 [ 219.945308] entry_SYSCALL_64_after_hwframe+0x6e/0xd8 [ 219.945317] RIP: 0033:0x7ff080322d6b [ 219.945337] Code: 8b 05 a1 30 0d 00 bb ff ff ff ff 64 c7 00 16 00 00 00 e9 62 ff ff ff e8 e3 1b 02 00 0f 1f 00 f3 0f 1e fa b8 53 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 05 c3 0f 1f 40 00 48 8b 15 69 30 0d 00 f7 d8 [ 219.945343] RSP: 002b:00007ffe3fbd6cd8 EFLAGS: 00000246 ORIG_RAX: 0000000000000053 [ 219.945350] RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007ff080322d6b [ 219.945355] RDX: 0000000000000000 RSI: 00000000000001ed RDI: 000055a4731c2a10 [ 219.945360] RBP: 00007ffe3fbd6d10 R08: 00000000ffffff9c R09: 00007ffe3fbd6b60 [ 219.945364] R10: 0000000000000000 R11: 0000000000000246 R12: 00007ff0806af143 [ 219.945368] R13: 000055a47311ce20 R14: 0000000000000001 R15: 00000000000003a0 [ 219.945382] </TASK> > + 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; > } > -- > 2.40.0 >
On Mon, Jul 17, 2023 at 2:33 PM Stephen Smalley <stephen.smalley.work@gmail.com> wrote: > > On Wed, May 31, 2023 at 7:32 AM Juraj Marcin <juraj@jurajmarcin.com> wrote: > > > > 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 <omosnace@redhat.com> > > Signed-off-by: Juraj Marcin <juraj@jurajmarcin.com> > > --- > > 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/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); > > This is called in various contexts that cannot sleep; hence, the > allocation must be GFP_ATOMIC here. With the proper config options > (CONFIG_DEBUG_ATOMIC_SLEEP=y) you would have seen repeated warnings > about this in the kernel messages, ala: > > [ 219.944942] BUG: sleeping function called from invalid context at > include/linux/sched/mm.h:306 > [ 219.944951] in_atomic(): 0, irqs_disabled(): 0, non_block: 0, pid: > 1, name: systemd > [ 219.944956] preempt_count: 0, expected: 0 > [ 219.944959] RCU nest depth: 1, expected: 0 > [ 219.944963] INFO: lockdep is turned off. > [ 219.944967] CPU: 0 PID: 1 Comm: systemd Tainted: G B W > 6.5.0-rc1+ #32 > [ 219.944979] Call Trace: > [ 219.944982] <TASK> > [ 219.944986] dump_stack_lvl+0x75/0x90 > [ 219.944997] __might_resched+0x1e1/0x310 > [ 219.945008] ? security_compute_sid.part.0+0x9d8/0xe50 > [ 219.945017] __kmem_cache_alloc_node+0x343/0x380 > [ 219.945026] ? security_compute_sid.part.0+0x9d8/0xe50 > [ 219.945033] ? rcu_is_watching+0x23/0x50 > [ 219.945046] ? security_compute_sid.part.0+0x9d8/0xe50 > [ 219.945054] __kmalloc_node_track_caller+0x52/0x160 > [ 219.945068] kmemdup+0x22/0x50 > [ 219.945078] security_compute_sid.part.0+0x9d8/0xe50 > [ 219.945093] ? __pfx_security_compute_sid.part.0+0x10/0x10 > [ 219.945101] ? rcu_is_watching+0x23/0x50 > [ 219.945110] ? lock_release+0xa0/0x380 > [ 219.945116] ? avc_has_perm_noaudit+0xb4/0x250 > [ 219.945127] ? __pfx_lock_release+0x10/0x10 > [ 219.945134] ? rcu_is_watching+0x23/0x50 > [ 219.945143] ? lock_acquire+0xb5/0x390 > [ 219.945148] ? __filename_parentat+0x282/0x350 > [ 219.945160] ? avc_has_perm_noaudit+0xcc/0x250 > [ 219.945178] security_transition_sid+0x63/0xa0 > [ 219.945191] may_create+0x16a/0x1c0 > [ 219.945201] ? __pfx_may_create+0x10/0x10 > [ 219.945207] ? selinux_inode_permission+0x1c6/0x290 > [ 219.945216] ? __pfx_selinux_inode_permission+0x10/0x10 > [ 219.945226] ? kernfs_iop_permission+0x84/0xa0 > [ 219.945238] security_inode_mkdir+0x61/0x80 > [ 219.945251] vfs_mkdir+0x226/0x380 > [ 219.945262] do_mkdirat+0x1a8/0x1d0 > [ 219.945272] ? __pfx_do_mkdirat+0x10/0x10 > [ 219.945280] ? getname_flags.part.0+0xc6/0x250 > [ 219.945290] __x64_sys_mkdir+0x78/0xa0 > [ 219.945300] do_syscall_64+0x3c/0x90 > [ 219.945308] entry_SYSCALL_64_after_hwframe+0x6e/0xd8 > [ 219.945317] RIP: 0033:0x7ff080322d6b > [ 219.945337] Code: 8b 05 a1 30 0d 00 bb ff ff ff ff 64 c7 00 16 00 > 00 00 e9 62 ff ff ff e8 e3 1b 02 00 0f 1f 00 f3 0f 1e fa b8 53 00 00 > 00 0f 05 <48> 3d 00 f0 ff ff 77 05 c3 0f 1f 40 00 48 8b 15 69 30 0d 00 > f7 d8 > [ 219.945343] RSP: 002b:00007ffe3fbd6cd8 EFLAGS: 00000246 ORIG_RAX: > 0000000000000053 > [ 219.945350] RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007ff080322d6b > [ 219.945355] RDX: 0000000000000000 RSI: 00000000000001ed RDI: 000055a4731c2a10 > [ 219.945360] RBP: 00007ffe3fbd6d10 R08: 00000000ffffff9c R09: 00007ffe3fbd6b60 > [ 219.945364] R10: 0000000000000000 R11: 0000000000000246 R12: 00007ff0806af143 > [ 219.945368] R13: 000055a47311ce20 R14: 0000000000000001 R15: 00000000000003a0 > [ 219.945382] </TASK> Also recommend enabling CONFIG_DEBUG_KMEMLEAK and CONFIG_DEBUG_KMEMLEAK_AUTO_SCAN - seeing kernel: kmemleak: 2634 new suspected memory leaks (see /sys/kernel/debug/kmemleak) in kernel messages and a lot of these warnings in /sys/kernel/debug/kmemleak: unreferenced object 0xffff8881b05d6ea8 (size 8): comm "systemd", pid 1, jiffies 4294785029 (age 2050.052s) hex dump (first 8 bytes): 6d 64 33 00 cc cc cc cc md3..... backtrace: [<ffffffff9f54d692>] __kmalloc_node_track_caller+0x52/0x160 [<ffffffff9f5388d2>] kmemdup+0x22/0x50 [<ffffffff9fac9702>] filenametr_tab_insert+0x332/0x650 [<ffffffff9fac5634>] hashtab_map+0x84/0xc0 [<ffffffff9fac8adb>] avtab_filename_trans_write+0x2fb/0x3a0 [<ffffffff9fad3838>] policydb_write+0x508/0x1220 [<ffffffff9fae0a10>] security_read_state_kernel+0xe0/0x1c0 [<ffffffff9fae7343>] selinux_ima_measure_state_locked+0x643/0x7b0 [<ffffffff9fadad35>] selinux_policy_commit+0x315/0x470 [<ffffffff9fac125d>] sel_write_load+0xd6d/0xfa0 [<ffffffff9f69560e>] vfs_write+0x18e/0x720 [<ffffffff9f6960e7>] ksys_write+0xb7/0x140 [<ffffffffa07dc29c>] do_syscall_64+0x3c/0x90 [<ffffffffa0a000aa>] entry_SYSCALL_64_after_hwframe+0x6e/0xd8
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; }