Merge tag 'selinux-pr-20210409' of git://git.kernel.org/pub/scm/linux/kernel/git...
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 9 Apr 2021 18:51:06 +0000 (11:51 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 9 Apr 2021 18:51:06 +0000 (11:51 -0700)
Pull selinux fixes from Paul Moore:
 "Three SELinux fixes.

  These fix known problems relating to (re)loading SELinux policy or
  changing the policy booleans, and pass our test suite without problem"

* tag 'selinux-pr-20210409' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux:
  selinux: fix race between old and new sidtab
  selinux: fix cond_list corruption when changing booleans
  selinux: make nslot handling in avtab more robust

1  2 
security/selinux/ss/services.c

index d91e41d47777b26390b626fd38897548724c4b58,6f095c0dc658a1a6a42692a58cd72fa7a295788e..30163314504053e8a97943f1ef8a049cdf3e7e22
@@@ -65,7 -65,6 +65,7 @@@
  #include "ebitmap.h"
  #include "audit.h"
  #include "policycap_names.h"
 +#include "ima.h"
  
  struct convert_context_args {
        struct selinux_state *state;
@@@ -1552,6 -1551,7 +1552,7 @@@ static int security_context_to_sid_core
                if (!str)
                        goto out;
        }
+ retry:
        rcu_read_lock();
        policy = rcu_dereference(state->policy);
        policydb = &policy->policydb;
        } else if (rc)
                goto out_unlock;
        rc = sidtab_context_to_sid(sidtab, &context, sid);
+       if (rc == -ESTALE) {
+               rcu_read_unlock();
+               if (context.str) {
+                       str = context.str;
+                       context.str = NULL;
+               }
+               context_destroy(&context);
+               goto retry;
+       }
        context_destroy(&context);
  out_unlock:
        rcu_read_unlock();
@@@ -1714,7 -1723,7 +1724,7 @@@ static int security_compute_sid(struct 
        struct selinux_policy *policy;
        struct policydb *policydb;
        struct sidtab *sidtab;
-       struct class_datum *cladatum = NULL;
+       struct class_datum *cladatum;
        struct context *scontext, *tcontext, newcontext;
        struct sidtab_entry *sentry, *tentry;
        struct avtab_key avkey;
                goto out;
        }
  
+ retry:
+       cladatum = NULL;
        context_init(&newcontext);
  
        rcu_read_lock();
        }
        /* Obtain the sid for the context. */
        rc = sidtab_context_to_sid(sidtab, &newcontext, out_sid);
+       if (rc == -ESTALE) {
+               rcu_read_unlock();
+               context_destroy(&newcontext);
+               goto retry;
+       }
  out_unlock:
        rcu_read_unlock();
        context_destroy(&newcontext);
@@@ -2185,13 -2201,13 +2202,14 @@@ static void selinux_notify_policy_chang
        selinux_status_update_policyload(state, seqno);
        selinux_netlbl_cache_invalidate();
        selinux_xfrm_notify_policyload();
 +      selinux_ima_measure_state(state);
  }
  
  void selinux_policy_commit(struct selinux_state *state,
                           struct selinux_load_state *load_state)
  {
        struct selinux_policy *oldpolicy, *newpolicy = load_state->policy;
+       unsigned long flags;
        u32 seqno;
  
        oldpolicy = rcu_dereference_protected(state->policy,
        seqno = newpolicy->latest_granting;
  
        /* Install the new policy. */
-       rcu_assign_pointer(state->policy, newpolicy);
+       if (oldpolicy) {
+               sidtab_freeze_begin(oldpolicy->sidtab, &flags);
+               rcu_assign_pointer(state->policy, newpolicy);
+               sidtab_freeze_end(oldpolicy->sidtab, &flags);
+       } else {
+               rcu_assign_pointer(state->policy, newpolicy);
+       }
  
        /* Load the policycaps from the new policy */
        security_load_policycaps(state, newpolicy);
@@@ -2357,13 -2379,15 +2381,15 @@@ int security_port_sid(struct selinux_st
        struct policydb *policydb;
        struct sidtab *sidtab;
        struct ocontext *c;
-       int rc = 0;
+       int rc;
  
        if (!selinux_initialized(state)) {
                *out_sid = SECINITSID_PORT;
                return 0;
        }
  
+ retry:
+       rc = 0;
        rcu_read_lock();
        policy = rcu_dereference(state->policy);
        policydb = &policy->policydb;
                if (!c->sid[0]) {
                        rc = sidtab_context_to_sid(sidtab, &c->context[0],
                                                   &c->sid[0]);
+                       if (rc == -ESTALE) {
+                               rcu_read_unlock();
+                               goto retry;
+                       }
                        if (rc)
                                goto out;
                }
@@@ -2408,13 -2436,15 +2438,15 @@@ int security_ib_pkey_sid(struct selinux
        struct policydb *policydb;
        struct sidtab *sidtab;
        struct ocontext *c;
-       int rc = 0;
+       int rc;
  
        if (!selinux_initialized(state)) {
                *out_sid = SECINITSID_UNLABELED;
                return 0;
        }
  
+ retry:
+       rc = 0;
        rcu_read_lock();
        policy = rcu_dereference(state->policy);
        policydb = &policy->policydb;
                        rc = sidtab_context_to_sid(sidtab,
                                                   &c->context[0],
                                                   &c->sid[0]);
+                       if (rc == -ESTALE) {
+                               rcu_read_unlock();
+                               goto retry;
+                       }
                        if (rc)
                                goto out;
                }
@@@ -2460,13 -2494,15 +2496,15 @@@ int security_ib_endport_sid(struct seli
        struct policydb *policydb;
        struct sidtab *sidtab;
        struct ocontext *c;
-       int rc = 0;
+       int rc;
  
        if (!selinux_initialized(state)) {
                *out_sid = SECINITSID_UNLABELED;
                return 0;
        }
  
+ retry:
+       rc = 0;
        rcu_read_lock();
        policy = rcu_dereference(state->policy);
        policydb = &policy->policydb;
                if (!c->sid[0]) {
                        rc = sidtab_context_to_sid(sidtab, &c->context[0],
                                                   &c->sid[0]);
+                       if (rc == -ESTALE) {
+                               rcu_read_unlock();
+                               goto retry;
+                       }
                        if (rc)
                                goto out;
                }
@@@ -2510,7 -2550,7 +2552,7 @@@ int security_netif_sid(struct selinux_s
        struct selinux_policy *policy;
        struct policydb *policydb;
        struct sidtab *sidtab;
-       int rc = 0;
+       int rc;
        struct ocontext *c;
  
        if (!selinux_initialized(state)) {
                return 0;
        }
  
+ retry:
+       rc = 0;
        rcu_read_lock();
        policy = rcu_dereference(state->policy);
        policydb = &policy->policydb;
                if (!c->sid[0] || !c->sid[1]) {
                        rc = sidtab_context_to_sid(sidtab, &c->context[0],
                                                   &c->sid[0]);
+                       if (rc == -ESTALE) {
+                               rcu_read_unlock();
+                               goto retry;
+                       }
                        if (rc)
                                goto out;
                        rc = sidtab_context_to_sid(sidtab, &c->context[1],
                                                   &c->sid[1]);
+                       if (rc == -ESTALE) {
+                               rcu_read_unlock();
+                               goto retry;
+                       }
                        if (rc)
                                goto out;
                }
@@@ -2587,6 -2637,7 +2639,7 @@@ int security_node_sid(struct selinux_st
                return 0;
        }
  
+ retry:
        rcu_read_lock();
        policy = rcu_dereference(state->policy);
        policydb = &policy->policydb;
                        rc = sidtab_context_to_sid(sidtab,
                                                   &c->context[0],
                                                   &c->sid[0]);
+                       if (rc == -ESTALE) {
+                               rcu_read_unlock();
+                               goto retry;
+                       }
                        if (rc)
                                goto out;
                }
@@@ -2676,18 -2731,24 +2733,24 @@@ int security_get_user_sids(struct selin
        struct sidtab *sidtab;
        struct context *fromcon, usercon;
        u32 *mysids = NULL, *mysids2, sid;
-       u32 mynel = 0, maxnel = SIDS_NEL;
+       u32 i, j, mynel, maxnel = SIDS_NEL;
        struct user_datum *user;
        struct role_datum *role;
        struct ebitmap_node *rnode, *tnode;
-       int rc = 0, i, j;
+       int rc;
  
        *sids = NULL;
        *nel = 0;
  
        if (!selinux_initialized(state))
-               goto out;
+               return 0;
+       mysids = kcalloc(maxnel, sizeof(*mysids), GFP_KERNEL);
+       if (!mysids)
+               return -ENOMEM;
  
+ retry:
+       mynel = 0;
        rcu_read_lock();
        policy = rcu_dereference(state->policy);
        policydb = &policy->policydb;
  
        usercon.user = user->value;
  
-       rc = -ENOMEM;
-       mysids = kcalloc(maxnel, sizeof(*mysids), GFP_ATOMIC);
-       if (!mysids)
-               goto out_unlock;
        ebitmap_for_each_positive_bit(&user->roles, rnode, i) {
                role = policydb->role_val_to_struct[i];
                usercon.role = i + 1;
                                continue;
  
                        rc = sidtab_context_to_sid(sidtab, &usercon, &sid);
+                       if (rc == -ESTALE) {
+                               rcu_read_unlock();
+                               goto retry;
+                       }
                        if (rc)
                                goto out_unlock;
                        if (mynel < maxnel) {
@@@ -2745,14 -2805,14 +2807,14 @@@ out_unlock
        rcu_read_unlock();
        if (rc || !mynel) {
                kfree(mysids);
-               goto out;
+               return rc;
        }
  
        rc = -ENOMEM;
        mysids2 = kcalloc(mynel, sizeof(*mysids2), GFP_KERNEL);
        if (!mysids2) {
                kfree(mysids);
-               goto out;
+               return rc;
        }
        for (i = 0, j = 0; i < mynel; i++) {
                struct av_decision dummy_avd;
                        mysids2[j++] = mysids[i];
                cond_resched();
        }
-       rc = 0;
        kfree(mysids);
        *sids = mysids2;
        *nel = j;
- out:
-       return rc;
+       return 0;
  }
  
  /**
   * Obtain a SID to use for a file in a filesystem that
   * cannot support xattr or use a fixed labeling behavior like
   * transition SIDs or task SIDs.
+  *
+  * WARNING: This function may return -ESTALE, indicating that the caller
+  * must retry the operation after re-acquiring the policy pointer!
   */
  static inline int __security_genfs_sid(struct selinux_policy *policy,
                                       const char *fstype,
@@@ -2861,11 -2922,13 +2924,13 @@@ int security_genfs_sid(struct selinux_s
                return 0;
        }
  
-       rcu_read_lock();
-       policy = rcu_dereference(state->policy);
-       retval = __security_genfs_sid(policy,
-                               fstype, path, orig_sclass, sid);
-       rcu_read_unlock();
+       do {
+               rcu_read_lock();
+               policy = rcu_dereference(state->policy);
+               retval = __security_genfs_sid(policy, fstype, path,
+                                             orig_sclass, sid);
+               rcu_read_unlock();
+       } while (retval == -ESTALE);
        return retval;
  }
  
@@@ -2888,7 -2951,7 +2953,7 @@@ int security_fs_use(struct selinux_stat
        struct selinux_policy *policy;
        struct policydb *policydb;
        struct sidtab *sidtab;
-       int rc = 0;
+       int rc;
        struct ocontext *c;
        struct superblock_security_struct *sbsec = sb->s_security;
        const char *fstype = sb->s_type->name;
                return 0;
        }
  
+ retry:
+       rc = 0;
        rcu_read_lock();
        policy = rcu_dereference(state->policy);
        policydb = &policy->policydb;
                if (!c->sid[0]) {
                        rc = sidtab_context_to_sid(sidtab, &c->context[0],
                                                   &c->sid[0]);
+                       if (rc == -ESTALE) {
+                               rcu_read_unlock();
+                               goto retry;
+                       }
                        if (rc)
                                goto out;
                }
        } else {
                rc = __security_genfs_sid(policy, fstype, "/",
                                        SECCLASS_DIR, &sbsec->sid);
+               if (rc == -ESTALE) {
+                       rcu_read_unlock();
+                       goto retry;
+               }
                if (rc) {
                        sbsec->behavior = SECURITY_FS_USE_NONE;
                        rc = 0;
@@@ -3132,12 -3205,13 +3207,13 @@@ int security_sid_mls_copy(struct selinu
        u32 len;
        int rc;
  
-       rc = 0;
        if (!selinux_initialized(state)) {
                *new_sid = sid;
-               goto out;
+               return 0;
        }
  
+ retry:
+       rc = 0;
        context_init(&newcon);
  
        rcu_read_lock();
                }
        }
        rc = sidtab_context_to_sid(sidtab, &newcon, new_sid);
+       if (rc == -ESTALE) {
+               rcu_read_unlock();
+               context_destroy(&newcon);
+               goto retry;
+       }
  out_unlock:
        rcu_read_unlock();
        context_destroy(&newcon);
- out:
        return rc;
  }
  
@@@ -3792,6 -3870,8 +3872,8 @@@ int security_netlbl_secattr_to_sid(stru
                return 0;
        }
  
+ retry:
+       rc = 0;
        rcu_read_lock();
        policy = rcu_dereference(state->policy);
        policydb = &policy->policydb;
                                goto out;
                }
                rc = -EIDRM;
-               if (!mls_context_isvalid(policydb, &ctx_new))
-                       goto out_free;
+               if (!mls_context_isvalid(policydb, &ctx_new)) {
+                       ebitmap_destroy(&ctx_new.range.level[0].cat);
+                       goto out;
+               }
  
                rc = sidtab_context_to_sid(sidtab, &ctx_new, sid);
+               ebitmap_destroy(&ctx_new.range.level[0].cat);
+               if (rc == -ESTALE) {
+                       rcu_read_unlock();
+                       goto retry;
+               }
                if (rc)
-                       goto out_free;
+                       goto out;
  
                security_netlbl_cache_add(secattr, *sid);
-               ebitmap_destroy(&ctx_new.range.level[0].cat);
        } else
                *sid = SECSID_NULL;
  
-       rcu_read_unlock();
-       return 0;
- out_free:
-       ebitmap_destroy(&ctx_new.range.level[0].cat);
  out:
        rcu_read_unlock();
        return rc;
  }
  #endif /* CONFIG_NETLABEL */
  
 +/**
 + * __security_read_policy - read the policy.
 + * @policy: SELinux policy
 + * @data: binary policy data
 + * @len: length of data in bytes
 + *
 + */
 +static int __security_read_policy(struct selinux_policy *policy,
 +                                void *data, size_t *len)
 +{
 +      int rc;
 +      struct policy_file fp;
 +
 +      fp.data = data;
 +      fp.len = *len;
 +
 +      rc = policydb_write(&policy->policydb, &fp);
 +      if (rc)
 +              return rc;
 +
 +      *len = (unsigned long)fp.data - (unsigned long)data;
 +      return 0;
 +}
 +
  /**
   * security_read_policy - read the policy.
 + * @state: selinux_state
   * @data: binary policy data
   * @len: length of data in bytes
   *
@@@ -3921,6 -3977,8 +4004,6 @@@ int security_read_policy(struct selinux
                         void **data, size_t *len)
  {
        struct selinux_policy *policy;
 -      int rc;
 -      struct policy_file fp;
  
        policy = rcu_dereference_protected(
                        state->policy, lockdep_is_held(&state->policy_mutex));
        if (!*data)
                return -ENOMEM;
  
 -      fp.data = *data;
 -      fp.len = *len;
 +      return __security_read_policy(policy, *data, len);
 +}
  
 -      rc = policydb_write(&policy->policydb, &fp);
 -      if (rc)
 -              return rc;
 +/**
 + * security_read_state_kernel - read the policy.
 + * @state: selinux_state
 + * @data: binary policy data
 + * @len: length of data in bytes
 + *
 + * Allocates kernel memory for reading SELinux policy.
 + * This function is for internal use only and should not
 + * be used for returning data to user space.
 + *
 + * This function must be called with policy_mutex held.
 + */
 +int security_read_state_kernel(struct selinux_state *state,
 +                             void **data, size_t *len)
 +{
 +      struct selinux_policy *policy;
  
 -      *len = (unsigned long)fp.data - (unsigned long)*data;
 -      return 0;
 +      policy = rcu_dereference_protected(
 +                      state->policy, lockdep_is_held(&state->policy_mutex));
 +      if (!policy)
 +              return -EINVAL;
 +
 +      *len = policy->policydb.len;
 +      *data = vmalloc(*len);
 +      if (!*data)
 +              return -ENOMEM;
  
 +      return __security_read_policy(policy, *data, len);
  }