On Wed, 2005-12-07 at 11:40 -0600, Dustin Kirkland wrote:
Ok, this is good info, Stephen. I'm thinking now that we should
probably design this with flexibility in mind so that any of those
components (user,role,type,sensitivity) could be filtered upon.
Yes, and you also want to allow this for not only the current task
context but for any inode context (or other object context) collected
during the syscall processing. Which gets more complicated, as in the
object context case, you have already done the work to get the string
representation (at least presently) during the syscall processing.
Right? Whereas for the task, you only generate a context string if you
are logging an audit message (i.e. if audit filters evaluated to true),
so you are just using the SID to get to the index without needing to
deal with a string.
Now, these indices of individual components that are not exported...
Can SELinux easily and quickly translate the strings into their indices?
Is it hashed or something? If not, we're right back to the string
matching, just pushing the work deeper into SELinux.
It doesn't matter for mapping the role string in the filter to an index,
because you only do that when the audit filter is inserted, not upon
each filter evaluation. For the current task SID, where you have to
look up the index on filter evaluation or precache it in some manner and
invalidate it upon task SID changes, it is a hash lookup of the SID to
obtain a context structure (not a string), and then just extract the
index value directly from that structure (no translation required).
Let's say that SELinux exposes the following function to the
audit
subsystem:
extern int security_match_element(const char *haystack_ctx, const char *needle,
const int element) {
if (
security_element_atoi(needle) == security_ctx_element_atoi(haystack_ctx, element)
)
return 1;
return 0;
}
Where:
haystack_ctx is a string that might look something like:
"system_u:object_r:tmp_t:s0"
needle is a string that might look something like: "object_r"
element is an int [0-3] (or a #define constant like SECURITY_ROLE_ELEMENT) that specifies
which component to match: 1
Internal to SELinux, helper functions would exist such that the
appropriate element can be extracted from the string and translated to
an int:
#define SECURITY_USER_ELEMENT 0
#define SECURITY_ROLE_ELEMENT 1
#define SECURITY_TYPE_ELEMENT 2
#define SECURITY_SENS_ELEMENT 3
u32 security_ctx_atoi(const char *ctx, const int i) {
char *a;
switch(i) {
case (SECURITY_USER_ELEMENT):
/* get the user element out of ctx string */
case (SECURITY_ROLE_ELEMENT):
/* get the role element out of ctx string */
case (SECURITY_TYPE_ELEMENT):
/* get the role element out of ctx string */
case (SECURITY_SENS_ELEMENT):
/* get the sensitivity element out of ctx string */
}
return security_element_atoi(a);
}
And a function that can translate the string representation to the int:
u32 security_element_atoi(const char *a) {
/* code here that translates a user, role, type, or sensitivity string to an int */
}
Thoughts?
In the task case, you don't need this approach; you compute the one
index at audit filter insertion time and save it in the filter rule
structure, and you perform a hash table lookup of the task SID at audit
filter evaluation time to obtain the corresponding index for comparison
(or you could precache it upon task creation and revalidate it upon task
SID changes, which would require some kind of callback from SELinux upon
such changes).
For the object case, you have already harvested the object context along
the way, so you do need to split the string and might as well just do a
strcmp at that point, as you won't save anything by looking up the
string to obtain the indices then.
--
Stephen Smalley
National Security Agency