On 2/8/20 2:36 AM, Petr Lautrbach wrote:
get_ordered_context_list() code used to ask the kernel to compute the complete
set of reachable contexts using /sys/fs/selinux/user aka
security_compute_user(). This set can be so huge so that it doesn't fit into a
kernel page and security_compute_user() fails. Even if it doesn't fail,
get_ordered_context_list() throws away the vast majority of the returned
contexts because they don't match anything in
/etc/selinux/targeted/contexts/default_contexts or
/etc/selinux/targeted/contexts/users/
get_ordered_context_list() is rewritten to compute set of contexts based on
/etc/selinux/targeted/contexts/users/ and
/etc/selinux/targeted/contexts/default_contexts files and to return only valid
contexts, using security_check_context(), from this set.
Fixes: https://github.com/SELinuxProject/selinux/issues/28
Signed-off-by: Petr Lautrbach <plautrba@xxxxxxxxxx>
---
diff --git a/libselinux/src/get_context_list.c b/libselinux/src/get_context_list.c
index 689e46589f30..cc39f8c6a96c 100644
--- a/libselinux/src/get_context_list.c
+++ b/libselinux/src/get_context_list.c
@@ -243,23 +222,89 @@ static int get_context_order(FILE * fp,
if (*end)
*end++ = 0;
- /* Check for a match in the reachable list. */
- rc = find_partialcon(reachable, nreach, start);
- if (rc < 0) {
- /* No match, skip it. */
+ /* Check whether a new context is valid */
+ if (SIZE_MAX - user_len < strlen(start) + 1) {
+ fprintf(stderr, "%s: one of partial contexts is too big\n", __FUNCTION__);
+ errno = EINVAL;
+ rc = -1;
+ goto out;
+ }
+ usercon_len = user_len + strlen(start) + 1;
+ usercon_str = malloc(usercon_len);
+ if (!usercon_str) {
+ rc = -1;
+ goto out;
+ }
+
+ /* set range from fromcon in the new usercon */
+ snprintf(usercon_str, usercon_len - 1, "%s:%s", user, start);
+ usercon = context_new(usercon_str);
+ if (!usercon) {
+ if (errno != EINVAL) {
+ free(usercon_str);
+ rc = -1;
+ goto out;
+ }
+ fprintf(stderr,
+ "%s: can't create a context from %s, skipping\n",
+ __FUNCTION__, usercon_str);
+ free(usercon_str);
+ start = end;
+ continue;
+ }
+ if (context_range_set(usercon, fromlevel) != 0) {
+ if (errno != EINVAL) {
+ free(usercon_str);
+ rc = -1;
+ goto out;
+ }
+ fprintf(stderr,
+ "%s: can't set a new range %s for %s, skipping\n",
+ __FUNCTION__, fromlevel, usercon_str);
+ free(usercon_str);
I'd think we could always treat this as a fatal error but up to you.
Regardless, we need to also do a context_free(usercon); here or we will
leak the memory.
start = end;
continue;
}
+ free(usercon_str);
+ usercon_str = context_str(usercon);
+ if (!usercon_str) {
+ rc = -1;
context_free(usercon); needed here as well.
+ goto out;
+ }
- /* If a match is found and the entry is not already ordered
- (e.g. due to prior match in prior config file), then set
- the ordering for it. */
- i = rc;
- if (ordering[i] == nreach)
- ordering[i] = (*nordered)++;
+ /* check whether usercon is already in reachable */
+ if (is_in_reachable(*reachable, usercon_str)) {
+ start = end;
And again.
+ continue;
+ }
+ if (security_check_context(usercon_str) == 0) {
+ if (*nreachable == 0) {
+ new_reachable = malloc(2 * sizeof(char *));
+ if (!new_reachable) {
+ context_free(usercon);
+ rc = -1;
+ goto out;
+ }
+ } else {
+ new_reachable = realloc(*reachable, (*nreachable + 2) * sizeof(char *));
+ if (!new_reachable) {
+ context_free(usercon);
+ rc = -1;
+ goto out;
+ }
+ }
+ new_reachable[*nreachable] = strdup(usercon_str);
+ if (new_reachable[*nreachable] == NULL) {
+ rc = -1;
+ goto out;
+ }
+ new_reachable[*nreachable + 1] = 0;
+ *reachable = new_reachable;
+ *nreachable += 1;
+ }
+ context_free(usercon);
start = end;
}
-
rc = 0;
out: