|
@@ -42,135 +42,27 @@ static const struct cifs_sid sid_authusers = {
|
|
|
/* group users */
|
|
|
static const struct cifs_sid sid_user = {1, 2 , {0, 0, 0, 0, 0, 5}, {} };
|
|
|
|
|
|
-const struct cred *root_cred;
|
|
|
-
|
|
|
-static void
|
|
|
-shrink_idmap_tree(struct rb_root *root, int nr_to_scan, int *nr_rem,
|
|
|
- int *nr_del)
|
|
|
-{
|
|
|
- struct rb_node *node;
|
|
|
- struct rb_node *tmp;
|
|
|
- struct cifs_sid_id *psidid;
|
|
|
-
|
|
|
- node = rb_first(root);
|
|
|
- while (node) {
|
|
|
- tmp = node;
|
|
|
- node = rb_next(tmp);
|
|
|
- psidid = rb_entry(tmp, struct cifs_sid_id, rbnode);
|
|
|
- if (nr_to_scan == 0 || *nr_del == nr_to_scan)
|
|
|
- ++(*nr_rem);
|
|
|
- else {
|
|
|
- if (time_after(jiffies, psidid->time + SID_MAP_EXPIRE)
|
|
|
- && psidid->refcount == 0) {
|
|
|
- rb_erase(tmp, root);
|
|
|
- ++(*nr_del);
|
|
|
- } else
|
|
|
- ++(*nr_rem);
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
- * Run idmap cache shrinker.
|
|
|
- */
|
|
|
-static int
|
|
|
-cifs_idmap_shrinker(struct shrinker *shrink, struct shrink_control *sc)
|
|
|
-{
|
|
|
- int nr_to_scan = sc->nr_to_scan;
|
|
|
- int nr_del = 0;
|
|
|
- int nr_rem = 0;
|
|
|
- struct rb_root *root;
|
|
|
-
|
|
|
- root = &uidtree;
|
|
|
- spin_lock(&siduidlock);
|
|
|
- shrink_idmap_tree(root, nr_to_scan, &nr_rem, &nr_del);
|
|
|
- spin_unlock(&siduidlock);
|
|
|
-
|
|
|
- root = &gidtree;
|
|
|
- spin_lock(&sidgidlock);
|
|
|
- shrink_idmap_tree(root, nr_to_scan, &nr_rem, &nr_del);
|
|
|
- spin_unlock(&sidgidlock);
|
|
|
-
|
|
|
- root = &siduidtree;
|
|
|
- spin_lock(&uidsidlock);
|
|
|
- shrink_idmap_tree(root, nr_to_scan, &nr_rem, &nr_del);
|
|
|
- spin_unlock(&uidsidlock);
|
|
|
-
|
|
|
- root = &sidgidtree;
|
|
|
- spin_lock(&gidsidlock);
|
|
|
- shrink_idmap_tree(root, nr_to_scan, &nr_rem, &nr_del);
|
|
|
- spin_unlock(&gidsidlock);
|
|
|
-
|
|
|
- return nr_rem;
|
|
|
-}
|
|
|
-
|
|
|
-static void
|
|
|
-sid_rb_insert(struct rb_root *root, unsigned long cid,
|
|
|
- struct cifs_sid_id **psidid, char *typestr)
|
|
|
-{
|
|
|
- char *strptr;
|
|
|
- struct rb_node *node = root->rb_node;
|
|
|
- struct rb_node *parent = NULL;
|
|
|
- struct rb_node **linkto = &(root->rb_node);
|
|
|
- struct cifs_sid_id *lsidid;
|
|
|
-
|
|
|
- while (node) {
|
|
|
- lsidid = rb_entry(node, struct cifs_sid_id, rbnode);
|
|
|
- parent = node;
|
|
|
- if (cid > lsidid->id) {
|
|
|
- linkto = &(node->rb_left);
|
|
|
- node = node->rb_left;
|
|
|
- }
|
|
|
- if (cid < lsidid->id) {
|
|
|
- linkto = &(node->rb_right);
|
|
|
- node = node->rb_right;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- (*psidid)->id = cid;
|
|
|
- (*psidid)->time = jiffies - (SID_MAP_RETRY + 1);
|
|
|
- (*psidid)->refcount = 0;
|
|
|
-
|
|
|
- sprintf((*psidid)->sidstr, "%s", typestr);
|
|
|
- strptr = (*psidid)->sidstr + strlen((*psidid)->sidstr);
|
|
|
- sprintf(strptr, "%ld", cid);
|
|
|
-
|
|
|
- clear_bit(SID_ID_PENDING, &(*psidid)->state);
|
|
|
- clear_bit(SID_ID_MAPPED, &(*psidid)->state);
|
|
|
-
|
|
|
- rb_link_node(&(*psidid)->rbnode, parent, linkto);
|
|
|
- rb_insert_color(&(*psidid)->rbnode, root);
|
|
|
-}
|
|
|
-
|
|
|
-static struct cifs_sid_id *
|
|
|
-sid_rb_search(struct rb_root *root, unsigned long cid)
|
|
|
-{
|
|
|
- struct rb_node *node = root->rb_node;
|
|
|
- struct cifs_sid_id *lsidid;
|
|
|
-
|
|
|
- while (node) {
|
|
|
- lsidid = rb_entry(node, struct cifs_sid_id, rbnode);
|
|
|
- if (cid > lsidid->id)
|
|
|
- node = node->rb_left;
|
|
|
- else if (cid < lsidid->id)
|
|
|
- node = node->rb_right;
|
|
|
- else /* node found */
|
|
|
- return lsidid;
|
|
|
- }
|
|
|
-
|
|
|
- return NULL;
|
|
|
-}
|
|
|
-
|
|
|
-static struct shrinker cifs_shrinker = {
|
|
|
- .shrink = cifs_idmap_shrinker,
|
|
|
- .seeks = DEFAULT_SEEKS,
|
|
|
-};
|
|
|
+static const struct cred *root_cred;
|
|
|
|
|
|
static int
|
|
|
cifs_idmap_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
|
|
|
{
|
|
|
char *payload;
|
|
|
|
|
|
+ /*
|
|
|
+ * If the payload is less than or equal to the size of a pointer, then
|
|
|
+ * an allocation here is wasteful. Just copy the data directly to the
|
|
|
+ * payload.value union member instead.
|
|
|
+ *
|
|
|
+ * With this however, you must check the datalen before trying to
|
|
|
+ * dereference payload.data!
|
|
|
+ */
|
|
|
+ if (prep->datalen <= sizeof(key->payload)) {
|
|
|
+ key->payload.value = 0;
|
|
|
+ memcpy(&key->payload.value, prep->data, prep->datalen);
|
|
|
+ key->datalen = prep->datalen;
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
payload = kmalloc(prep->datalen, GFP_KERNEL);
|
|
|
if (!payload)
|
|
|
return -ENOMEM;
|
|
@@ -184,10 +76,11 @@ cifs_idmap_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
|
|
|
static inline void
|
|
|
cifs_idmap_key_destroy(struct key *key)
|
|
|
{
|
|
|
- kfree(key->payload.data);
|
|
|
+ if (key->datalen > sizeof(key->payload))
|
|
|
+ kfree(key->payload.data);
|
|
|
}
|
|
|
|
|
|
-struct key_type cifs_idmap_key_type = {
|
|
|
+static struct key_type cifs_idmap_key_type = {
|
|
|
.name = "cifs.idmap",
|
|
|
.instantiate = cifs_idmap_key_instantiate,
|
|
|
.destroy = cifs_idmap_key_destroy,
|
|
@@ -195,221 +88,174 @@ struct key_type cifs_idmap_key_type = {
|
|
|
.match = user_match,
|
|
|
};
|
|
|
|
|
|
-static void
|
|
|
-sid_to_str(struct cifs_sid *sidptr, char *sidstr)
|
|
|
+static char *
|
|
|
+sid_to_key_str(struct cifs_sid *sidptr, unsigned int type)
|
|
|
{
|
|
|
- int i;
|
|
|
- unsigned long saval;
|
|
|
- char *strptr;
|
|
|
+ int i, len;
|
|
|
+ unsigned int saval;
|
|
|
+ char *sidstr, *strptr;
|
|
|
+ unsigned long long id_auth_val;
|
|
|
+
|
|
|
+ /* 3 bytes for prefix */
|
|
|
+ sidstr = kmalloc(3 + SID_STRING_BASE_SIZE +
|
|
|
+ (SID_STRING_SUBAUTH_SIZE * sidptr->num_subauth),
|
|
|
+ GFP_KERNEL);
|
|
|
+ if (!sidstr)
|
|
|
+ return sidstr;
|
|
|
|
|
|
strptr = sidstr;
|
|
|
+ len = sprintf(strptr, "%cs:S-%hhu", type == SIDOWNER ? 'o' : 'g',
|
|
|
+ sidptr->revision);
|
|
|
+ strptr += len;
|
|
|
+
|
|
|
+ /* The authority field is a single 48-bit number */
|
|
|
+ id_auth_val = (unsigned long long)sidptr->authority[5];
|
|
|
+ id_auth_val |= (unsigned long long)sidptr->authority[4] << 8;
|
|
|
+ id_auth_val |= (unsigned long long)sidptr->authority[3] << 16;
|
|
|
+ id_auth_val |= (unsigned long long)sidptr->authority[2] << 24;
|
|
|
+ id_auth_val |= (unsigned long long)sidptr->authority[1] << 32;
|
|
|
+ id_auth_val |= (unsigned long long)sidptr->authority[0] << 48;
|
|
|
|
|
|
- sprintf(strptr, "%s", "S");
|
|
|
- strptr = sidstr + strlen(sidstr);
|
|
|
-
|
|
|
- sprintf(strptr, "-%d", sidptr->revision);
|
|
|
- strptr = sidstr + strlen(sidstr);
|
|
|
+ /*
|
|
|
+ * MS-DTYP states that if the authority is >= 2^32, then it should be
|
|
|
+ * expressed as a hex value.
|
|
|
+ */
|
|
|
+ if (id_auth_val <= UINT_MAX)
|
|
|
+ len = sprintf(strptr, "-%llu", id_auth_val);
|
|
|
+ else
|
|
|
+ len = sprintf(strptr, "-0x%llx", id_auth_val);
|
|
|
|
|
|
- for (i = 0; i < 6; ++i) {
|
|
|
- if (sidptr->authority[i]) {
|
|
|
- sprintf(strptr, "-%d", sidptr->authority[i]);
|
|
|
- strptr = sidstr + strlen(sidstr);
|
|
|
- }
|
|
|
- }
|
|
|
+ strptr += len;
|
|
|
|
|
|
for (i = 0; i < sidptr->num_subauth; ++i) {
|
|
|
saval = le32_to_cpu(sidptr->sub_auth[i]);
|
|
|
- sprintf(strptr, "-%ld", saval);
|
|
|
- strptr = sidstr + strlen(sidstr);
|
|
|
+ len = sprintf(strptr, "-%u", saval);
|
|
|
+ strptr += len;
|
|
|
}
|
|
|
-}
|
|
|
|
|
|
-static void
|
|
|
-cifs_copy_sid(struct cifs_sid *dst, const struct cifs_sid *src)
|
|
|
-{
|
|
|
- memcpy(dst, src, sizeof(*dst));
|
|
|
- dst->num_subauth = min_t(u8, src->num_subauth, NUM_SUBAUTHS);
|
|
|
+ return sidstr;
|
|
|
}
|
|
|
|
|
|
-static void
|
|
|
-id_rb_insert(struct rb_root *root, struct cifs_sid *sidptr,
|
|
|
- struct cifs_sid_id **psidid, char *typestr)
|
|
|
+/*
|
|
|
+ * if the two SIDs (roughly equivalent to a UUID for a user or group) are
|
|
|
+ * the same returns zero, if they do not match returns non-zero.
|
|
|
+ */
|
|
|
+static int
|
|
|
+compare_sids(const struct cifs_sid *ctsid, const struct cifs_sid *cwsid)
|
|
|
{
|
|
|
- int rc;
|
|
|
- char *strptr;
|
|
|
- struct rb_node *node = root->rb_node;
|
|
|
- struct rb_node *parent = NULL;
|
|
|
- struct rb_node **linkto = &(root->rb_node);
|
|
|
- struct cifs_sid_id *lsidid;
|
|
|
-
|
|
|
- while (node) {
|
|
|
- lsidid = rb_entry(node, struct cifs_sid_id, rbnode);
|
|
|
- parent = node;
|
|
|
- rc = compare_sids(sidptr, &((lsidid)->sid));
|
|
|
- if (rc > 0) {
|
|
|
- linkto = &(node->rb_left);
|
|
|
- node = node->rb_left;
|
|
|
- } else if (rc < 0) {
|
|
|
- linkto = &(node->rb_right);
|
|
|
- node = node->rb_right;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- cifs_copy_sid(&(*psidid)->sid, sidptr);
|
|
|
- (*psidid)->time = jiffies - (SID_MAP_RETRY + 1);
|
|
|
- (*psidid)->refcount = 0;
|
|
|
+ int i;
|
|
|
+ int num_subauth, num_sat, num_saw;
|
|
|
|
|
|
- sprintf((*psidid)->sidstr, "%s", typestr);
|
|
|
- strptr = (*psidid)->sidstr + strlen((*psidid)->sidstr);
|
|
|
- sid_to_str(&(*psidid)->sid, strptr);
|
|
|
+ if ((!ctsid) || (!cwsid))
|
|
|
+ return 1;
|
|
|
|
|
|
- clear_bit(SID_ID_PENDING, &(*psidid)->state);
|
|
|
- clear_bit(SID_ID_MAPPED, &(*psidid)->state);
|
|
|
+ /* compare the revision */
|
|
|
+ if (ctsid->revision != cwsid->revision) {
|
|
|
+ if (ctsid->revision > cwsid->revision)
|
|
|
+ return 1;
|
|
|
+ else
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
|
|
|
- rb_link_node(&(*psidid)->rbnode, parent, linkto);
|
|
|
- rb_insert_color(&(*psidid)->rbnode, root);
|
|
|
-}
|
|
|
+ /* compare all of the six auth values */
|
|
|
+ for (i = 0; i < NUM_AUTHS; ++i) {
|
|
|
+ if (ctsid->authority[i] != cwsid->authority[i]) {
|
|
|
+ if (ctsid->authority[i] > cwsid->authority[i])
|
|
|
+ return 1;
|
|
|
+ else
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
-static struct cifs_sid_id *
|
|
|
-id_rb_search(struct rb_root *root, struct cifs_sid *sidptr)
|
|
|
-{
|
|
|
- int rc;
|
|
|
- struct rb_node *node = root->rb_node;
|
|
|
- struct cifs_sid_id *lsidid;
|
|
|
-
|
|
|
- while (node) {
|
|
|
- lsidid = rb_entry(node, struct cifs_sid_id, rbnode);
|
|
|
- rc = compare_sids(sidptr, &((lsidid)->sid));
|
|
|
- if (rc > 0) {
|
|
|
- node = node->rb_left;
|
|
|
- } else if (rc < 0) {
|
|
|
- node = node->rb_right;
|
|
|
- } else /* node found */
|
|
|
- return lsidid;
|
|
|
+ /* compare all of the subauth values if any */
|
|
|
+ num_sat = ctsid->num_subauth;
|
|
|
+ num_saw = cwsid->num_subauth;
|
|
|
+ num_subauth = num_sat < num_saw ? num_sat : num_saw;
|
|
|
+ if (num_subauth) {
|
|
|
+ for (i = 0; i < num_subauth; ++i) {
|
|
|
+ if (ctsid->sub_auth[i] != cwsid->sub_auth[i]) {
|
|
|
+ if (le32_to_cpu(ctsid->sub_auth[i]) >
|
|
|
+ le32_to_cpu(cwsid->sub_auth[i]))
|
|
|
+ return 1;
|
|
|
+ else
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- return NULL;
|
|
|
+ return 0; /* sids compare/match */
|
|
|
}
|
|
|
|
|
|
-static int
|
|
|
-sidid_pending_wait(void *unused)
|
|
|
+static void
|
|
|
+cifs_copy_sid(struct cifs_sid *dst, const struct cifs_sid *src)
|
|
|
{
|
|
|
- schedule();
|
|
|
- return signal_pending(current) ? -ERESTARTSYS : 0;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ dst->revision = src->revision;
|
|
|
+ dst->num_subauth = min_t(u8, src->num_subauth, SID_MAX_SUB_AUTHORITIES);
|
|
|
+ for (i = 0; i < NUM_AUTHS; ++i)
|
|
|
+ dst->authority[i] = src->authority[i];
|
|
|
+ for (i = 0; i < dst->num_subauth; ++i)
|
|
|
+ dst->sub_auth[i] = src->sub_auth[i];
|
|
|
}
|
|
|
|
|
|
static int
|
|
|
-id_to_sid(unsigned long cid, uint sidtype, struct cifs_sid *ssid)
|
|
|
+id_to_sid(unsigned int cid, uint sidtype, struct cifs_sid *ssid)
|
|
|
{
|
|
|
- int rc = 0;
|
|
|
+ int rc;
|
|
|
struct key *sidkey;
|
|
|
+ struct cifs_sid *ksid;
|
|
|
+ unsigned int ksid_size;
|
|
|
+ char desc[3 + 10 + 1]; /* 3 byte prefix + 10 bytes for value + NULL */
|
|
|
const struct cred *saved_cred;
|
|
|
- struct cifs_sid *lsid;
|
|
|
- struct cifs_sid_id *psidid, *npsidid;
|
|
|
- struct rb_root *cidtree;
|
|
|
- spinlock_t *cidlock;
|
|
|
-
|
|
|
- if (sidtype == SIDOWNER) {
|
|
|
- cidlock = &siduidlock;
|
|
|
- cidtree = &uidtree;
|
|
|
- } else if (sidtype == SIDGROUP) {
|
|
|
- cidlock = &sidgidlock;
|
|
|
- cidtree = &gidtree;
|
|
|
- } else
|
|
|
- return -EINVAL;
|
|
|
-
|
|
|
- spin_lock(cidlock);
|
|
|
- psidid = sid_rb_search(cidtree, cid);
|
|
|
|
|
|
- if (!psidid) { /* node does not exist, allocate one & attempt adding */
|
|
|
- spin_unlock(cidlock);
|
|
|
- npsidid = kzalloc(sizeof(struct cifs_sid_id), GFP_KERNEL);
|
|
|
- if (!npsidid)
|
|
|
- return -ENOMEM;
|
|
|
-
|
|
|
- npsidid->sidstr = kmalloc(SIDLEN, GFP_KERNEL);
|
|
|
- if (!npsidid->sidstr) {
|
|
|
- kfree(npsidid);
|
|
|
- return -ENOMEM;
|
|
|
- }
|
|
|
+ rc = snprintf(desc, sizeof(desc), "%ci:%u",
|
|
|
+ sidtype == SIDOWNER ? 'o' : 'g', cid);
|
|
|
+ if (rc >= sizeof(desc))
|
|
|
+ return -EINVAL;
|
|
|
|
|
|
- spin_lock(cidlock);
|
|
|
- psidid = sid_rb_search(cidtree, cid);
|
|
|
- if (psidid) { /* node happened to get inserted meanwhile */
|
|
|
- ++psidid->refcount;
|
|
|
- spin_unlock(cidlock);
|
|
|
- kfree(npsidid->sidstr);
|
|
|
- kfree(npsidid);
|
|
|
- } else {
|
|
|
- psidid = npsidid;
|
|
|
- sid_rb_insert(cidtree, cid, &psidid,
|
|
|
- sidtype == SIDOWNER ? "oi:" : "gi:");
|
|
|
- ++psidid->refcount;
|
|
|
- spin_unlock(cidlock);
|
|
|
- }
|
|
|
- } else {
|
|
|
- ++psidid->refcount;
|
|
|
- spin_unlock(cidlock);
|
|
|
+ rc = 0;
|
|
|
+ saved_cred = override_creds(root_cred);
|
|
|
+ sidkey = request_key(&cifs_idmap_key_type, desc, "");
|
|
|
+ if (IS_ERR(sidkey)) {
|
|
|
+ rc = -EINVAL;
|
|
|
+ cFYI(1, "%s: Can't map %cid %u to a SID", __func__,
|
|
|
+ sidtype == SIDOWNER ? 'u' : 'g', cid);
|
|
|
+ goto out_revert_creds;
|
|
|
+ } else if (sidkey->datalen < CIFS_SID_BASE_SIZE) {
|
|
|
+ rc = -EIO;
|
|
|
+ cFYI(1, "%s: Downcall contained malformed key "
|
|
|
+ "(datalen=%hu)", __func__, sidkey->datalen);
|
|
|
+ goto invalidate_key;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
- * If we are here, it is safe to access psidid and its fields
|
|
|
- * since a reference was taken earlier while holding the spinlock.
|
|
|
- * A reference on the node is put without holding the spinlock
|
|
|
- * and it is OK to do so in this case, shrinker will not erase
|
|
|
- * this node until all references are put and we do not access
|
|
|
- * any fields of the node after a reference is put .
|
|
|
+ * A sid is usually too large to be embedded in payload.value, but if
|
|
|
+ * there are no subauthorities and the host has 8-byte pointers, then
|
|
|
+ * it could be.
|
|
|
*/
|
|
|
- if (test_bit(SID_ID_MAPPED, &psidid->state)) {
|
|
|
- cifs_copy_sid(ssid, &psidid->sid);
|
|
|
- psidid->time = jiffies; /* update ts for accessing */
|
|
|
- goto id_sid_out;
|
|
|
+ ksid = sidkey->datalen <= sizeof(sidkey->payload) ?
|
|
|
+ (struct cifs_sid *)&sidkey->payload.value :
|
|
|
+ (struct cifs_sid *)sidkey->payload.data;
|
|
|
+
|
|
|
+ ksid_size = CIFS_SID_BASE_SIZE + (ksid->num_subauth * sizeof(__le32));
|
|
|
+ if (ksid_size > sidkey->datalen) {
|
|
|
+ rc = -EIO;
|
|
|
+ cFYI(1, "%s: Downcall contained malformed key (datalen=%hu, "
|
|
|
+ "ksid_size=%u)", __func__, sidkey->datalen, ksid_size);
|
|
|
+ goto invalidate_key;
|
|
|
}
|
|
|
|
|
|
- if (time_after(psidid->time + SID_MAP_RETRY, jiffies)) {
|
|
|
- rc = -EINVAL;
|
|
|
- goto id_sid_out;
|
|
|
- }
|
|
|
-
|
|
|
- if (!test_and_set_bit(SID_ID_PENDING, &psidid->state)) {
|
|
|
- saved_cred = override_creds(root_cred);
|
|
|
- sidkey = request_key(&cifs_idmap_key_type, psidid->sidstr, "");
|
|
|
- if (IS_ERR(sidkey)) {
|
|
|
- rc = -EINVAL;
|
|
|
- cFYI(1, "%s: Can't map and id to a SID", __func__);
|
|
|
- } else if (sidkey->datalen < sizeof(struct cifs_sid)) {
|
|
|
- rc = -EIO;
|
|
|
- cFYI(1, "%s: Downcall contained malformed key "
|
|
|
- "(datalen=%hu)", __func__, sidkey->datalen);
|
|
|
- } else {
|
|
|
- lsid = (struct cifs_sid *)sidkey->payload.data;
|
|
|
- cifs_copy_sid(&psidid->sid, lsid);
|
|
|
- cifs_copy_sid(ssid, &psidid->sid);
|
|
|
- set_bit(SID_ID_MAPPED, &psidid->state);
|
|
|
- key_put(sidkey);
|
|
|
- kfree(psidid->sidstr);
|
|
|
- }
|
|
|
- psidid->time = jiffies; /* update ts for accessing */
|
|
|
- revert_creds(saved_cred);
|
|
|
- clear_bit(SID_ID_PENDING, &psidid->state);
|
|
|
- wake_up_bit(&psidid->state, SID_ID_PENDING);
|
|
|
- } else {
|
|
|
- rc = wait_on_bit(&psidid->state, SID_ID_PENDING,
|
|
|
- sidid_pending_wait, TASK_INTERRUPTIBLE);
|
|
|
- if (rc) {
|
|
|
- cFYI(1, "%s: sidid_pending_wait interrupted %d",
|
|
|
- __func__, rc);
|
|
|
- --psidid->refcount;
|
|
|
- return rc;
|
|
|
- }
|
|
|
- if (test_bit(SID_ID_MAPPED, &psidid->state))
|
|
|
- cifs_copy_sid(ssid, &psidid->sid);
|
|
|
- else
|
|
|
- rc = -EINVAL;
|
|
|
- }
|
|
|
-id_sid_out:
|
|
|
- --psidid->refcount;
|
|
|
+ cifs_copy_sid(ssid, ksid);
|
|
|
+out_key_put:
|
|
|
+ key_put(sidkey);
|
|
|
+out_revert_creds:
|
|
|
+ revert_creds(saved_cred);
|
|
|
return rc;
|
|
|
+
|
|
|
+invalidate_key:
|
|
|
+ key_invalidate(sidkey);
|
|
|
+ goto out_key_put;
|
|
|
}
|
|
|
|
|
|
static int
|
|
@@ -417,111 +263,67 @@ sid_to_id(struct cifs_sb_info *cifs_sb, struct cifs_sid *psid,
|
|
|
struct cifs_fattr *fattr, uint sidtype)
|
|
|
{
|
|
|
int rc;
|
|
|
- unsigned long cid;
|
|
|
- struct key *idkey;
|
|
|
+ struct key *sidkey;
|
|
|
+ char *sidstr;
|
|
|
const struct cred *saved_cred;
|
|
|
- struct cifs_sid_id *psidid, *npsidid;
|
|
|
- struct rb_root *cidtree;
|
|
|
- spinlock_t *cidlock;
|
|
|
-
|
|
|
- if (sidtype == SIDOWNER) {
|
|
|
- cid = cifs_sb->mnt_uid; /* default uid, in case upcall fails */
|
|
|
- cidlock = &siduidlock;
|
|
|
- cidtree = &uidtree;
|
|
|
- } else if (sidtype == SIDGROUP) {
|
|
|
- cid = cifs_sb->mnt_gid; /* default gid, in case upcall fails */
|
|
|
- cidlock = &sidgidlock;
|
|
|
- cidtree = &gidtree;
|
|
|
- } else
|
|
|
- return -ENOENT;
|
|
|
-
|
|
|
- spin_lock(cidlock);
|
|
|
- psidid = id_rb_search(cidtree, psid);
|
|
|
-
|
|
|
- if (!psidid) { /* node does not exist, allocate one & attempt adding */
|
|
|
- spin_unlock(cidlock);
|
|
|
- npsidid = kzalloc(sizeof(struct cifs_sid_id), GFP_KERNEL);
|
|
|
- if (!npsidid)
|
|
|
- return -ENOMEM;
|
|
|
-
|
|
|
- npsidid->sidstr = kmalloc(SIDLEN, GFP_KERNEL);
|
|
|
- if (!npsidid->sidstr) {
|
|
|
- kfree(npsidid);
|
|
|
- return -ENOMEM;
|
|
|
- }
|
|
|
-
|
|
|
- spin_lock(cidlock);
|
|
|
- psidid = id_rb_search(cidtree, psid);
|
|
|
- if (psidid) { /* node happened to get inserted meanwhile */
|
|
|
- ++psidid->refcount;
|
|
|
- spin_unlock(cidlock);
|
|
|
- kfree(npsidid->sidstr);
|
|
|
- kfree(npsidid);
|
|
|
- } else {
|
|
|
- psidid = npsidid;
|
|
|
- id_rb_insert(cidtree, psid, &psidid,
|
|
|
- sidtype == SIDOWNER ? "os:" : "gs:");
|
|
|
- ++psidid->refcount;
|
|
|
- spin_unlock(cidlock);
|
|
|
- }
|
|
|
- } else {
|
|
|
- ++psidid->refcount;
|
|
|
- spin_unlock(cidlock);
|
|
|
- }
|
|
|
+ uid_t fuid = cifs_sb->mnt_uid;
|
|
|
+ gid_t fgid = cifs_sb->mnt_gid;
|
|
|
|
|
|
/*
|
|
|
- * If we are here, it is safe to access psidid and its fields
|
|
|
- * since a reference was taken earlier while holding the spinlock.
|
|
|
- * A reference on the node is put without holding the spinlock
|
|
|
- * and it is OK to do so in this case, shrinker will not erase
|
|
|
- * this node until all references are put and we do not access
|
|
|
- * any fields of the node after a reference is put .
|
|
|
+ * If we have too many subauthorities, then something is really wrong.
|
|
|
+ * Just return an error.
|
|
|
*/
|
|
|
- if (test_bit(SID_ID_MAPPED, &psidid->state)) {
|
|
|
- cid = psidid->id;
|
|
|
- psidid->time = jiffies; /* update ts for accessing */
|
|
|
- goto sid_to_id_out;
|
|
|
+ if (unlikely(psid->num_subauth > SID_MAX_SUB_AUTHORITIES)) {
|
|
|
+ cFYI(1, "%s: %u subauthorities is too many!", __func__,
|
|
|
+ psid->num_subauth);
|
|
|
+ return -EIO;
|
|
|
}
|
|
|
|
|
|
- if (time_after(psidid->time + SID_MAP_RETRY, jiffies))
|
|
|
- goto sid_to_id_out;
|
|
|
-
|
|
|
- if (!test_and_set_bit(SID_ID_PENDING, &psidid->state)) {
|
|
|
- saved_cred = override_creds(root_cred);
|
|
|
- idkey = request_key(&cifs_idmap_key_type, psidid->sidstr, "");
|
|
|
- if (IS_ERR(idkey))
|
|
|
- cFYI(1, "%s: Can't map SID to an id", __func__);
|
|
|
- else {
|
|
|
- cid = *(unsigned long *)idkey->payload.value;
|
|
|
- psidid->id = cid;
|
|
|
- set_bit(SID_ID_MAPPED, &psidid->state);
|
|
|
- key_put(idkey);
|
|
|
- kfree(psidid->sidstr);
|
|
|
- }
|
|
|
- revert_creds(saved_cred);
|
|
|
- psidid->time = jiffies; /* update ts for accessing */
|
|
|
- clear_bit(SID_ID_PENDING, &psidid->state);
|
|
|
- wake_up_bit(&psidid->state, SID_ID_PENDING);
|
|
|
- } else {
|
|
|
- rc = wait_on_bit(&psidid->state, SID_ID_PENDING,
|
|
|
- sidid_pending_wait, TASK_INTERRUPTIBLE);
|
|
|
- if (rc) {
|
|
|
- cFYI(1, "%s: sidid_pending_wait interrupted %d",
|
|
|
- __func__, rc);
|
|
|
- --psidid->refcount; /* decremented without spinlock */
|
|
|
- return rc;
|
|
|
- }
|
|
|
- if (test_bit(SID_ID_MAPPED, &psidid->state))
|
|
|
- cid = psidid->id;
|
|
|
+ sidstr = sid_to_key_str(psid, sidtype);
|
|
|
+ if (!sidstr)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ saved_cred = override_creds(root_cred);
|
|
|
+ sidkey = request_key(&cifs_idmap_key_type, sidstr, "");
|
|
|
+ if (IS_ERR(sidkey)) {
|
|
|
+ rc = -EINVAL;
|
|
|
+ cFYI(1, "%s: Can't map SID %s to a %cid", __func__, sidstr,
|
|
|
+ sidtype == SIDOWNER ? 'u' : 'g');
|
|
|
+ goto out_revert_creds;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * FIXME: Here we assume that uid_t and gid_t are same size. It's
|
|
|
+ * probably a safe assumption but might be better to check based on
|
|
|
+ * sidtype.
|
|
|
+ */
|
|
|
+ if (sidkey->datalen != sizeof(uid_t)) {
|
|
|
+ rc = -EIO;
|
|
|
+ cFYI(1, "%s: Downcall contained malformed key "
|
|
|
+ "(datalen=%hu)", __func__, sidkey->datalen);
|
|
|
+ key_invalidate(sidkey);
|
|
|
+ goto out_key_put;
|
|
|
}
|
|
|
|
|
|
-sid_to_id_out:
|
|
|
- --psidid->refcount; /* decremented without spinlock */
|
|
|
if (sidtype == SIDOWNER)
|
|
|
- fattr->cf_uid = cid;
|
|
|
+ memcpy(&fuid, &sidkey->payload.value, sizeof(uid_t));
|
|
|
else
|
|
|
- fattr->cf_gid = cid;
|
|
|
+ memcpy(&fgid, &sidkey->payload.value, sizeof(gid_t));
|
|
|
+
|
|
|
+out_key_put:
|
|
|
+ key_put(sidkey);
|
|
|
+out_revert_creds:
|
|
|
+ revert_creds(saved_cred);
|
|
|
+ kfree(sidstr);
|
|
|
|
|
|
+ /*
|
|
|
+ * Note that we return 0 here unconditionally. If the mapping
|
|
|
+ * fails then we just fall back to using the mnt_uid/mnt_gid.
|
|
|
+ */
|
|
|
+ if (sidtype == SIDOWNER)
|
|
|
+ fattr->cf_uid = fuid;
|
|
|
+ else
|
|
|
+ fattr->cf_gid = fgid;
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -568,17 +370,6 @@ init_cifs_idmap(void)
|
|
|
cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING;
|
|
|
root_cred = cred;
|
|
|
|
|
|
- spin_lock_init(&siduidlock);
|
|
|
- uidtree = RB_ROOT;
|
|
|
- spin_lock_init(&sidgidlock);
|
|
|
- gidtree = RB_ROOT;
|
|
|
-
|
|
|
- spin_lock_init(&uidsidlock);
|
|
|
- siduidtree = RB_ROOT;
|
|
|
- spin_lock_init(&gidsidlock);
|
|
|
- sidgidtree = RB_ROOT;
|
|
|
- register_shrinker(&cifs_shrinker);
|
|
|
-
|
|
|
cFYI(1, "cifs idmap keyring: %d", key_serial(keyring));
|
|
|
return 0;
|
|
|
|
|
@@ -595,89 +386,9 @@ exit_cifs_idmap(void)
|
|
|
key_revoke(root_cred->thread_keyring);
|
|
|
unregister_key_type(&cifs_idmap_key_type);
|
|
|
put_cred(root_cred);
|
|
|
- unregister_shrinker(&cifs_shrinker);
|
|
|
cFYI(1, "Unregistered %s key type", cifs_idmap_key_type.name);
|
|
|
}
|
|
|
|
|
|
-void
|
|
|
-cifs_destroy_idmaptrees(void)
|
|
|
-{
|
|
|
- struct rb_root *root;
|
|
|
- struct rb_node *node;
|
|
|
-
|
|
|
- root = &uidtree;
|
|
|
- spin_lock(&siduidlock);
|
|
|
- while ((node = rb_first(root)))
|
|
|
- rb_erase(node, root);
|
|
|
- spin_unlock(&siduidlock);
|
|
|
-
|
|
|
- root = &gidtree;
|
|
|
- spin_lock(&sidgidlock);
|
|
|
- while ((node = rb_first(root)))
|
|
|
- rb_erase(node, root);
|
|
|
- spin_unlock(&sidgidlock);
|
|
|
-
|
|
|
- root = &siduidtree;
|
|
|
- spin_lock(&uidsidlock);
|
|
|
- while ((node = rb_first(root)))
|
|
|
- rb_erase(node, root);
|
|
|
- spin_unlock(&uidsidlock);
|
|
|
-
|
|
|
- root = &sidgidtree;
|
|
|
- spin_lock(&gidsidlock);
|
|
|
- while ((node = rb_first(root)))
|
|
|
- rb_erase(node, root);
|
|
|
- spin_unlock(&gidsidlock);
|
|
|
-}
|
|
|
-
|
|
|
-/* if the two SIDs (roughly equivalent to a UUID for a user or group) are
|
|
|
- the same returns 1, if they do not match returns 0 */
|
|
|
-int compare_sids(const struct cifs_sid *ctsid, const struct cifs_sid *cwsid)
|
|
|
-{
|
|
|
- int i;
|
|
|
- int num_subauth, num_sat, num_saw;
|
|
|
-
|
|
|
- if ((!ctsid) || (!cwsid))
|
|
|
- return 1;
|
|
|
-
|
|
|
- /* compare the revision */
|
|
|
- if (ctsid->revision != cwsid->revision) {
|
|
|
- if (ctsid->revision > cwsid->revision)
|
|
|
- return 1;
|
|
|
- else
|
|
|
- return -1;
|
|
|
- }
|
|
|
-
|
|
|
- /* compare all of the six auth values */
|
|
|
- for (i = 0; i < 6; ++i) {
|
|
|
- if (ctsid->authority[i] != cwsid->authority[i]) {
|
|
|
- if (ctsid->authority[i] > cwsid->authority[i])
|
|
|
- return 1;
|
|
|
- else
|
|
|
- return -1;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /* compare all of the subauth values if any */
|
|
|
- num_sat = ctsid->num_subauth;
|
|
|
- num_saw = cwsid->num_subauth;
|
|
|
- num_subauth = num_sat < num_saw ? num_sat : num_saw;
|
|
|
- if (num_subauth) {
|
|
|
- for (i = 0; i < num_subauth; ++i) {
|
|
|
- if (ctsid->sub_auth[i] != cwsid->sub_auth[i]) {
|
|
|
- if (le32_to_cpu(ctsid->sub_auth[i]) >
|
|
|
- le32_to_cpu(cwsid->sub_auth[i]))
|
|
|
- return 1;
|
|
|
- else
|
|
|
- return -1;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return 0; /* sids compare/match */
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
/* copy ntsd, owner sid, and group sid from a security descriptor to another */
|
|
|
static void copy_sec_desc(const struct cifs_ntsd *pntsd,
|
|
|
struct cifs_ntsd *pnntsd, __u32 sidsoffset)
|
|
@@ -811,7 +522,7 @@ static __u16 fill_ace_for_sid(struct cifs_ace *pntace,
|
|
|
|
|
|
pntace->sid.revision = psid->revision;
|
|
|
pntace->sid.num_subauth = psid->num_subauth;
|
|
|
- for (i = 0; i < 6; i++)
|
|
|
+ for (i = 0; i < NUM_AUTHS; i++)
|
|
|
pntace->sid.authority[i] = psid->authority[i];
|
|
|
for (i = 0; i < psid->num_subauth; i++)
|
|
|
pntace->sid.sub_auth[i] = psid->sub_auth[i];
|
|
@@ -987,8 +698,8 @@ static int parse_sid(struct cifs_sid *psid, char *end_of_acl)
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
|
|
|
- if (psid->num_subauth) {
|
|
|
#ifdef CONFIG_CIFS_DEBUG2
|
|
|
+ if (psid->num_subauth) {
|
|
|
int i;
|
|
|
cFYI(1, "SID revision %d num_auth %d",
|
|
|
psid->revision, psid->num_subauth);
|
|
@@ -1002,8 +713,8 @@ static int parse_sid(struct cifs_sid *psid, char *end_of_acl)
|
|
|
num auths and therefore go off the end */
|
|
|
cFYI(1, "RID 0x%x",
|
|
|
le32_to_cpu(psid->sub_auth[psid->num_subauth-1]));
|
|
|
-#endif
|
|
|
}
|
|
|
+#endif
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -1307,42 +1018,39 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 nmode,
|
|
|
|
|
|
/* Get the security descriptor */
|
|
|
pntsd = get_cifs_acl(CIFS_SB(inode->i_sb), inode, path, &secdesclen);
|
|
|
-
|
|
|
- /* Add three ACEs for owner, group, everyone getting rid of
|
|
|
- other ACEs as chmod disables ACEs and set the security descriptor */
|
|
|
-
|
|
|
if (IS_ERR(pntsd)) {
|
|
|
rc = PTR_ERR(pntsd);
|
|
|
cERROR(1, "%s: error %d getting sec desc", __func__, rc);
|
|
|
- } else {
|
|
|
- /* allocate memory for the smb header,
|
|
|
- set security descriptor request security descriptor
|
|
|
- parameters, and secuirty descriptor itself */
|
|
|
-
|
|
|
- secdesclen = secdesclen < DEFSECDESCLEN ?
|
|
|
- DEFSECDESCLEN : secdesclen;
|
|
|
- pnntsd = kmalloc(secdesclen, GFP_KERNEL);
|
|
|
- if (!pnntsd) {
|
|
|
- cERROR(1, "Unable to allocate security descriptor");
|
|
|
- kfree(pntsd);
|
|
|
- return -ENOMEM;
|
|
|
- }
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
|
|
|
- rc = build_sec_desc(pntsd, pnntsd, secdesclen, nmode, uid, gid,
|
|
|
- &aclflag);
|
|
|
+ /*
|
|
|
+ * Add three ACEs for owner, group, everyone getting rid of other ACEs
|
|
|
+ * as chmod disables ACEs and set the security descriptor. Allocate
|
|
|
+ * memory for the smb header, set security descriptor request security
|
|
|
+ * descriptor parameters, and secuirty descriptor itself
|
|
|
+ */
|
|
|
+ secdesclen = max_t(u32, secdesclen, DEFAULT_SEC_DESC_LEN);
|
|
|
+ pnntsd = kmalloc(secdesclen, GFP_KERNEL);
|
|
|
+ if (!pnntsd) {
|
|
|
+ cERROR(1, "Unable to allocate security descriptor");
|
|
|
+ kfree(pntsd);
|
|
|
+ return -ENOMEM;
|
|
|
+ }
|
|
|
|
|
|
- cFYI(DBG2, "build_sec_desc rc: %d", rc);
|
|
|
+ rc = build_sec_desc(pntsd, pnntsd, secdesclen, nmode, uid, gid,
|
|
|
+ &aclflag);
|
|
|
|
|
|
- if (!rc) {
|
|
|
- /* Set the security descriptor */
|
|
|
- rc = set_cifs_acl(pnntsd, secdesclen, inode,
|
|
|
- path, aclflag);
|
|
|
- cFYI(DBG2, "set_cifs_acl rc: %d", rc);
|
|
|
- }
|
|
|
+ cFYI(DBG2, "build_sec_desc rc: %d", rc);
|
|
|
|
|
|
- kfree(pnntsd);
|
|
|
- kfree(pntsd);
|
|
|
+ if (!rc) {
|
|
|
+ /* Set the security descriptor */
|
|
|
+ rc = set_cifs_acl(pnntsd, secdesclen, inode, path, aclflag);
|
|
|
+ cFYI(DBG2, "set_cifs_acl rc: %d", rc);
|
|
|
}
|
|
|
|
|
|
+ kfree(pnntsd);
|
|
|
+ kfree(pntsd);
|
|
|
+out:
|
|
|
return rc;
|
|
|
}
|