Skip to content

Commit

Permalink
netlabel: fix the horribly broken catmap functions
Browse files Browse the repository at this point in the history
The NetLabel secattr catmap functions, and the SELinux import/export
glue routines, were broken in many horrible ways and the SELinux glue
code fiddled with the NetLabel catmap structures in ways that we
probably shouldn't allow.  At some point this "worked", but that was
likely due to a bit of dumb luck and sub-par testing (both inflicted
by yours truly).  This patch corrects these problems by basically
gutting the code in favor of something less obtuse and restoring the
NetLabel abstractions in the SELinux catmap glue code.

Everything is working now, and if it decides to break itself in the
future this code will be much easier to debug than the code it
replaces.

One noteworthy side effect of the changes is that it is no longer
necessary to allocate a NetLabel catmap before calling one of the
NetLabel APIs to set a bit in the catmap.  NetLabel will automatically
allocate the catmap nodes when needed, resulting in less allocations
when the lowest bit is greater than 255 and less code in the LSMs.

Cc: [email protected]
Reported-by: Christian Evans <[email protected]>
Signed-off-by: Paul Moore <[email protected]>
Tested-by: Casey Schaufler <[email protected]>
  • Loading branch information
pcmoore committed Aug 1, 2014
1 parent 41c3bd2 commit 4b8feff
Show file tree
Hide file tree
Showing 5 changed files with 240 additions and 146 deletions.
26 changes: 24 additions & 2 deletions include/net/netlabel.h
Original file line number Diff line number Diff line change
Expand Up @@ -285,11 +285,11 @@ static inline void netlbl_secattr_catmap_free(
{
struct netlbl_lsm_secattr_catmap *iter;

do {
while (catmap) {
iter = catmap;
catmap = catmap->next;
kfree(iter);
} while (catmap);
}
}

/**
Expand Down Expand Up @@ -394,13 +394,20 @@ int netlbl_secattr_catmap_walk(struct netlbl_lsm_secattr_catmap *catmap,
u32 offset);
int netlbl_secattr_catmap_walk_rng(struct netlbl_lsm_secattr_catmap *catmap,
u32 offset);
int netlbl_secattr_catmap_getlong(struct netlbl_lsm_secattr_catmap *catmap,
u32 *offset,
unsigned long *bitmap);
int netlbl_secattr_catmap_setbit(struct netlbl_lsm_secattr_catmap **catmap,
u32 bit,
gfp_t flags);
int netlbl_secattr_catmap_setrng(struct netlbl_lsm_secattr_catmap **catmap,
u32 start,
u32 end,
gfp_t flags);
int netlbl_secattr_catmap_setlong(struct netlbl_lsm_secattr_catmap **catmap,
u32 offset,
unsigned long bitmap,
gfp_t flags);

/*
* LSM protocol operations (NetLabel LSM/kernel API)
Expand Down Expand Up @@ -504,6 +511,13 @@ static inline int netlbl_secattr_catmap_walk_rng(
{
return -ENOENT;
}
static inline int netlbl_secattr_catmap_getlong(
struct netlbl_lsm_secattr_catmap *catmap,
u32 *offset,
unsigned long *bitmap)
{
return 0;
}
static inline int netlbl_secattr_catmap_setbit(
struct netlbl_lsm_secattr_catmap **catmap,
u32 bit,
Expand All @@ -519,6 +533,14 @@ static inline int netlbl_secattr_catmap_setrng(
{
return 0;
}
static int netlbl_secattr_catmap_setlong(
struct netlbl_lsm_secattr_catmap **catmap,
u32 offset,
unsigned long bitmap,
gfp_t flags)
{
return 0;
}
static inline int netlbl_enabled(void)
{
return 0;
Expand Down
12 changes: 0 additions & 12 deletions net/ipv4/cipso_ipv4.c
Original file line number Diff line number Diff line change
Expand Up @@ -1335,10 +1335,6 @@ static int cipso_v4_parsetag_rbm(const struct cipso_v4_doi *doi_def,
secattr->flags |= NETLBL_SECATTR_MLS_LVL;

if (tag_len > 4) {
secattr->attr.mls.cat = netlbl_secattr_catmap_alloc(GFP_ATOMIC);
if (secattr->attr.mls.cat == NULL)
return -ENOMEM;

ret_val = cipso_v4_map_cat_rbm_ntoh(doi_def,
&tag[4],
tag_len - 4,
Expand Down Expand Up @@ -1430,10 +1426,6 @@ static int cipso_v4_parsetag_enum(const struct cipso_v4_doi *doi_def,
secattr->flags |= NETLBL_SECATTR_MLS_LVL;

if (tag_len > 4) {
secattr->attr.mls.cat = netlbl_secattr_catmap_alloc(GFP_ATOMIC);
if (secattr->attr.mls.cat == NULL)
return -ENOMEM;

ret_val = cipso_v4_map_cat_enum_ntoh(doi_def,
&tag[4],
tag_len - 4,
Expand Down Expand Up @@ -1524,10 +1516,6 @@ static int cipso_v4_parsetag_rng(const struct cipso_v4_doi *doi_def,
secattr->flags |= NETLBL_SECATTR_MLS_LVL;

if (tag_len > 4) {
secattr->attr.mls.cat = netlbl_secattr_catmap_alloc(GFP_ATOMIC);
if (secattr->attr.mls.cat == NULL)
return -ENOMEM;

ret_val = cipso_v4_map_cat_rng_ntoh(doi_def,
&tag[4],
tag_len - 4,
Expand Down
216 changes: 166 additions & 50 deletions net/netlabel/netlabel_kapi.c
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,63 @@ int netlbl_cfg_cipsov4_map_add(u32 doi,
* Security Attribute Functions
*/

#define _CM_F_NONE 0x00000000
#define _CM_F_ALLOC 0x00000001

/**
* _netlbl_secattr_catmap_getnode - Get a individual node from a catmap
* @catmap: pointer to the category bitmap
* @offset: the requested offset
* @cm_flags: catmap flags, see _CM_F_*
* @gfp_flags: memory allocation flags
*
* Description:
* Iterate through the catmap looking for the node associated with @offset; if
* the _CM_F_ALLOC flag is set in @cm_flags and there is no associated node,
* one will be created and inserted into the catmap. Returns a pointer to the
* node on success, NULL on failure.
*
*/
static struct netlbl_lsm_secattr_catmap *_netlbl_secattr_catmap_getnode(
struct netlbl_lsm_secattr_catmap **catmap,
u32 offset,
unsigned int cm_flags,
gfp_t gfp_flags)
{
struct netlbl_lsm_secattr_catmap *iter = *catmap;
struct netlbl_lsm_secattr_catmap *prev = NULL;

if (iter == NULL || offset < iter->startbit)
goto secattr_catmap_getnode_alloc;
while (iter && offset >= (iter->startbit + NETLBL_CATMAP_SIZE)) {
prev = iter;
iter = iter->next;
}
if (iter == NULL || offset < iter->startbit)
goto secattr_catmap_getnode_alloc;

return iter;

secattr_catmap_getnode_alloc:
if (!(cm_flags & _CM_F_ALLOC))
return NULL;

iter = netlbl_secattr_catmap_alloc(gfp_flags);
if (iter == NULL)
return NULL;
iter->startbit = offset & ~(NETLBL_CATMAP_SIZE - 1);

if (prev == NULL) {
iter->next = *catmap;
*catmap = iter;
} else {
iter->next = prev->next;
prev->next = iter;
}

return iter;
}

/**
* netlbl_secattr_catmap_walk - Walk a LSM secattr catmap looking for a bit
* @catmap: the category bitmap
Expand Down Expand Up @@ -520,6 +577,54 @@ int netlbl_secattr_catmap_walk_rng(struct netlbl_lsm_secattr_catmap *catmap,
return -ENOENT;
}

/**
* netlbl_secattr_catmap_getlong - Export an unsigned long bitmap
* @catmap: pointer to the category bitmap
* @offset: pointer to the requested offset
* @bitmap: the exported bitmap
*
* Description:
* Export a bitmap with an offset greater than or equal to @offset and return
* it in @bitmap. The @offset must be aligned to an unsigned long and will be
* updated on return if different from what was requested; if the catmap is
* empty at the requested offset and beyond, the @offset is set to (u32)-1.
* Returns zero on sucess, negative values on failure.
*
*/
int netlbl_secattr_catmap_getlong(struct netlbl_lsm_secattr_catmap *catmap,
u32 *offset,
unsigned long *bitmap)
{
struct netlbl_lsm_secattr_catmap *iter;
u32 off = *offset;
u32 idx;

/* only allow aligned offsets */
if ((off & (BITS_PER_LONG - 1)) != 0)
return -EINVAL;

if (off < catmap->startbit) {
off = catmap->startbit;
*offset = off;
}
iter = _netlbl_secattr_catmap_getnode(&catmap, off, _CM_F_NONE, 0);
if (iter == NULL) {
*offset = (u32)-1;
return 0;
}

if (off < iter->startbit) {
off = iter->startbit;
*offset = off;
} else
off -= iter->startbit;

idx = off / NETLBL_CATMAP_MAPSIZE;
*bitmap = iter->bitmap[idx] >> (off % NETLBL_CATMAP_SIZE);

return 0;
}

/**
* netlbl_secattr_catmap_setbit - Set a bit in a LSM secattr catmap
* @catmap: pointer to the category bitmap
Expand All @@ -535,32 +640,16 @@ int netlbl_secattr_catmap_setbit(struct netlbl_lsm_secattr_catmap **catmap,
u32 bit,
gfp_t flags)
{
struct netlbl_lsm_secattr_catmap *iter = *catmap;
u32 node_bit;
u32 node_idx;
struct netlbl_lsm_secattr_catmap *iter;
u32 idx;

while (iter->next != NULL &&
bit >= (iter->startbit + NETLBL_CATMAP_SIZE))
iter = iter->next;
if (bit < iter->startbit) {
iter = netlbl_secattr_catmap_alloc(flags);
if (iter == NULL)
return -ENOMEM;
iter->next = *catmap;
iter->startbit = bit & ~(NETLBL_CATMAP_SIZE - 1);
*catmap = iter;
} else if (bit >= (iter->startbit + NETLBL_CATMAP_SIZE)) {
iter->next = netlbl_secattr_catmap_alloc(flags);
if (iter->next == NULL)
return -ENOMEM;
iter = iter->next;
iter->startbit = bit & ~(NETLBL_CATMAP_SIZE - 1);
}
iter = _netlbl_secattr_catmap_getnode(catmap, bit, _CM_F_ALLOC, flags);
if (iter == NULL)
return -ENOMEM;

/* gcc always rounds to zero when doing integer division */
node_idx = (bit - iter->startbit) / NETLBL_CATMAP_MAPSIZE;
node_bit = bit - iter->startbit - (NETLBL_CATMAP_MAPSIZE * node_idx);
iter->bitmap[node_idx] |= NETLBL_CATMAP_BIT << node_bit;
bit -= iter->startbit;
idx = bit / NETLBL_CATMAP_MAPSIZE;
iter->bitmap[idx] |= NETLBL_CATMAP_BIT << (bit % NETLBL_CATMAP_MAPSIZE);

return 0;
}
Expand All @@ -582,34 +671,61 @@ int netlbl_secattr_catmap_setrng(struct netlbl_lsm_secattr_catmap **catmap,
u32 end,
gfp_t flags)
{
int ret_val = 0;
struct netlbl_lsm_secattr_catmap *iter = *catmap;
u32 iter_max_spot;
u32 spot;
u32 orig_spot = iter->startbit;

/* XXX - This could probably be made a bit faster by combining writes
* to the catmap instead of setting a single bit each time, but for
* right now skipping to the start of the range in the catmap should
* be a nice improvement over calling the individual setbit function
* repeatedly from a loop. */

while (iter->next != NULL &&
start >= (iter->startbit + NETLBL_CATMAP_SIZE))
iter = iter->next;
iter_max_spot = iter->startbit + NETLBL_CATMAP_SIZE;

for (spot = start; spot <= end && ret_val == 0; spot++) {
if (spot >= iter_max_spot && iter->next != NULL) {
iter = iter->next;
iter_max_spot = iter->startbit + NETLBL_CATMAP_SIZE;
}
ret_val = netlbl_secattr_catmap_setbit(&iter, spot, flags);
if (iter->startbit < orig_spot)
*catmap = iter;
int rc = 0;
u32 spot = start;

while (rc == 0 && spot <= end) {
if (((spot & (BITS_PER_LONG - 1)) != 0) &&
((end - spot) > BITS_PER_LONG)) {
rc = netlbl_secattr_catmap_setlong(catmap,
spot,
(unsigned long)-1,
flags);
spot += BITS_PER_LONG;
} else
rc = netlbl_secattr_catmap_setbit(catmap,
spot++,
flags);
}

return ret_val;
return rc;
}

/**
* netlbl_secattr_catmap_setlong - Import an unsigned long bitmap
* @catmap: pointer to the category bitmap
* @offset: offset to the start of the imported bitmap
* @bitmap: the bitmap to import
* @flags: memory allocation flags
*
* Description:
* Import the bitmap specified in @bitmap into @catmap, using the offset
* in @offset. The offset must be aligned to an unsigned long. Returns zero
* on success, negative values on failure.
*
*/
int netlbl_secattr_catmap_setlong(struct netlbl_lsm_secattr_catmap **catmap,
u32 offset,
unsigned long bitmap,
gfp_t flags)
{
struct netlbl_lsm_secattr_catmap *iter;
u32 idx;

/* only allow aligned offsets */
if ((offset & (BITS_PER_LONG - 1)) != 0)
return -EINVAL;

iter = _netlbl_secattr_catmap_getnode(catmap,
offset, _CM_F_ALLOC, flags);
if (iter == NULL)
return -ENOMEM;

offset -= iter->startbit;
idx = offset / NETLBL_CATMAP_MAPSIZE;
iter->bitmap[idx] |= bitmap << (offset % NETLBL_CATMAP_MAPSIZE);

return 0;
}

/*
Expand Down
Loading

0 comments on commit 4b8feff

Please sign in to comment.