Commit 44c3b591 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/selinux-2.6

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/selinux-2.6:
  security: compile capabilities by default
  selinux: make selinux_set_mnt_opts() static
  SELinux: Add warning messages on network denial due to error
  SELinux: Add network ingress and egress control permission checks
  NetLabel: Add auditing to the static labeling mechanism
  NetLabel: Introduce static network labels for unlabeled connections
  SELinux: Allow NetLabel to directly cache SIDs
  SELinux: Enable dynamic enable/disable of the network access checks
  SELinux: Better integration between peer labeling subsystems
  SELinux: Add a new peer class and permissions to the Flask definitions
  SELinux: Add a capabilities bitmap to SELinux policy version 22
  SELinux: Add a network node caching mechanism similar to the sel_netif_*() functions
  SELinux: Only store the network interface's ifindex
  SELinux: Convert the netif code to use ifindex values
  NetLabel: Add IP address family information to the netlbl_skbuff_getattr() function
  NetLabel: Add secid token support to the NetLabel secattr struct
  NetLabel: Consolidate the LSM domain mapping/hashing locks
  NetLabel: Cleanup the LSM domain hash functions
  NetLabel: Remove unneeded RCU read locks
parents 3b470ac4 f71ea9dd
......@@ -115,6 +115,8 @@
#define AUDIT_MAC_IPSEC_ADDSPD 1413 /* Not used */
#define AUDIT_MAC_IPSEC_DELSPD 1414 /* Not used */
#define AUDIT_MAC_IPSEC_EVENT 1415 /* Audit an IPSec event */
#define AUDIT_MAC_UNLBL_STCADD 1416 /* NetLabel: add a static label */
#define AUDIT_MAC_UNLBL_STCDEL 1417 /* NetLabel: del a static label */
#define AUDIT_FIRST_KERN_ANOM_MSG 1700
#define AUDIT_LAST_KERN_ANOM_MSG 1799
......
......@@ -120,16 +120,35 @@ void selinux_get_task_sid(struct task_struct *tsk, u32 *sid);
int selinux_string_to_sid(char *str, u32 *sid);
/**
* selinux_relabel_packet_permission - check permission to relabel a packet
* @sid: ID value to be applied to network packet (via SECMARK, most likely)
* selinux_secmark_relabel_packet_permission - secmark permission check
* @sid: SECMARK ID value to be applied to network packet
*
* Returns 0 if the current task is allowed to label packets with the
* supplied security ID. Note that it is implicit that the packet is always
* being relabeled from the default unlabled value, and that the access
* control decision is made in the AVC.
* Returns 0 if the current task is allowed to set the SECMARK label of
* packets with the supplied security ID. Note that it is implicit that
* the packet is always being relabeled from the default unlabeled value,
* and that the access control decision is made in the AVC.
*/
int selinux_relabel_packet_permission(u32 sid);
int selinux_secmark_relabel_packet_permission(u32 sid);
/**
* selinux_secmark_refcount_inc - increments the secmark use counter
*
* SELinux keeps track of the current SECMARK targets in use so it knows
* when to apply SECMARK label access checks to network packets. This
* function incements this reference count to indicate that a new SECMARK
* target has been configured.
*/
void selinux_secmark_refcount_inc(void);
/**
* selinux_secmark_refcount_dec - decrements the secmark use counter
*
* SELinux keeps track of the current SECMARK targets in use so it knows
* when to apply SECMARK label access checks to network packets. This
* function decements this reference count to indicate that one of the
* existing SECMARK targets has been removed/flushed.
*/
void selinux_secmark_refcount_dec(void);
#else
static inline int selinux_audit_rule_init(u32 field, u32 op,
......@@ -184,11 +203,21 @@ static inline int selinux_string_to_sid(const char *str, u32 *sid)
return 0;
}
static inline int selinux_relabel_packet_permission(u32 sid)
static inline int selinux_secmark_relabel_packet_permission(u32 sid)
{
return 0;
}
static inline void selinux_secmark_refcount_inc(void)
{
return;
}
static inline void selinux_secmark_refcount_dec(void)
{
return;
}
#endif /* CONFIG_SECURITY_SELINUX */
#endif /* _LINUX_SELINUX_H */
......@@ -67,7 +67,11 @@
* NetLabel NETLINK protocol
*/
#define NETLBL_PROTO_VERSION 1
/* NetLabel NETLINK protocol version
* 1: initial version
* 2: added static labels for unlabeled connections
*/
#define NETLBL_PROTO_VERSION 2
/* NetLabel NETLINK types/families */
#define NETLBL_NLTYPE_NONE 0
......@@ -105,17 +109,49 @@ struct netlbl_dom_map;
/* Domain mapping operations */
int netlbl_domhsh_remove(const char *domain, struct netlbl_audit *audit_info);
/* LSM security attributes */
/*
* LSM security attributes
*/
/**
* struct netlbl_lsm_cache - NetLabel LSM security attribute cache
* @refcount: atomic reference counter
* @free: LSM supplied function to free the cache data
* @data: LSM supplied cache data
*
* Description:
* This structure is provided for LSMs which wish to make use of the NetLabel
* caching mechanism to store LSM specific data/attributes in the NetLabel
* cache. If the LSM has to perform a lot of translation from the NetLabel
* security attributes into it's own internal representation then the cache
* mechanism can provide a way to eliminate some or all of that translation
* overhead on a cache hit.
*
*/
struct netlbl_lsm_cache {
atomic_t refcount;
void (*free) (const void *data);
void *data;
};
/* The catmap bitmap field MUST be a power of two in length and large
/**
* struct netlbl_lsm_secattr_catmap - NetLabel LSM secattr category bitmap
* @startbit: the value of the lowest order bit in the bitmap
* @bitmap: the category bitmap
* @next: pointer to the next bitmap "node" or NULL
*
* Description:
* This structure is used to represent category bitmaps. Due to the large
* number of categories supported by most labeling protocols it is not
* practical to transfer a full bitmap internally so NetLabel adopts a sparse
* bitmap structure modeled after SELinux's ebitmap structure.
* The catmap bitmap field MUST be a power of two in length and large
* enough to hold at least 240 bits. Special care (i.e. check the code!)
* should be used when changing these values as the LSM implementation
* probably has functions which rely on the sizes of these types to speed
* processing. */
* processing.
*
*/
#define NETLBL_CATMAP_MAPTYPE u64
#define NETLBL_CATMAP_MAPCNT 4
#define NETLBL_CATMAP_MAPSIZE (sizeof(NETLBL_CATMAP_MAPTYPE) * 8)
......@@ -127,22 +163,48 @@ struct netlbl_lsm_secattr_catmap {
NETLBL_CATMAP_MAPTYPE bitmap[NETLBL_CATMAP_MAPCNT];
struct netlbl_lsm_secattr_catmap *next;
};
/**
* struct netlbl_lsm_secattr - NetLabel LSM security attributes
* @flags: indicate which attributes are contained in this structure
* @type: indicate the NLTYPE of the attributes
* @domain: the NetLabel LSM domain
* @cache: NetLabel LSM specific cache
* @attr.mls: MLS sensitivity label
* @attr.mls.cat: MLS category bitmap
* @attr.mls.lvl: MLS sensitivity level
* @attr.secid: LSM specific secid token
*
* Description:
* This structure is used to pass security attributes between NetLabel and the
* LSM modules. The flags field is used to specify which fields within the
* struct are valid and valid values can be created by bitwise OR'ing the
* NETLBL_SECATTR_* defines. The domain field is typically set by the LSM to
* specify domain specific configuration settings and is not usually used by
* NetLabel itself when returning security attributes to the LSM.
*
*/
#define NETLBL_SECATTR_NONE 0x00000000
#define NETLBL_SECATTR_DOMAIN 0x00000001
#define NETLBL_SECATTR_CACHE 0x00000002
#define NETLBL_SECATTR_MLS_LVL 0x00000004
#define NETLBL_SECATTR_MLS_CAT 0x00000008
#define NETLBL_SECATTR_SECID 0x00000010
#define NETLBL_SECATTR_CACHEABLE (NETLBL_SECATTR_MLS_LVL | \
NETLBL_SECATTR_MLS_CAT)
NETLBL_SECATTR_MLS_CAT | \
NETLBL_SECATTR_SECID)
struct netlbl_lsm_secattr {
u32 flags;
u32 type;
char *domain;
u32 mls_lvl;
struct netlbl_lsm_secattr_catmap *mls_cat;
struct netlbl_lsm_cache *cache;
union {
struct {
struct netlbl_lsm_secattr_catmap *cat;
u32 lvl;
} mls;
u32 secid;
} attr;
};
/*
......@@ -231,10 +293,7 @@ static inline void netlbl_secattr_catmap_free(
*/
static inline void netlbl_secattr_init(struct netlbl_lsm_secattr *secattr)
{
secattr->flags = 0;
secattr->domain = NULL;
secattr->mls_cat = NULL;
secattr->cache = NULL;
memset(secattr, 0, sizeof(*secattr));
}
/**
......@@ -248,11 +307,11 @@ static inline void netlbl_secattr_init(struct netlbl_lsm_secattr *secattr)
*/
static inline void netlbl_secattr_destroy(struct netlbl_lsm_secattr *secattr)
{
if (secattr->cache)
netlbl_secattr_cache_free(secattr->cache);
kfree(secattr->domain);
if (secattr->mls_cat)
netlbl_secattr_catmap_free(secattr->mls_cat);
if (secattr->flags & NETLBL_SECATTR_CACHE)
netlbl_secattr_cache_free(secattr->cache);
if (secattr->flags & NETLBL_SECATTR_MLS_CAT)
netlbl_secattr_catmap_free(secattr->attr.mls.cat);
}
/**
......@@ -300,7 +359,7 @@ int netlbl_secattr_catmap_setrng(struct netlbl_lsm_secattr_catmap *catmap,
gfp_t flags);
/*
* LSM protocol operations
* LSM protocol operations (NetLabel LSM/kernel API)
*/
int netlbl_enabled(void);
int netlbl_sock_setattr(struct sock *sk,
......@@ -308,6 +367,7 @@ int netlbl_sock_setattr(struct sock *sk,
int netlbl_sock_getattr(struct sock *sk,
struct netlbl_lsm_secattr *secattr);
int netlbl_skbuff_getattr(const struct sk_buff *skb,
u16 family,
struct netlbl_lsm_secattr *secattr);
void netlbl_skbuff_err(struct sk_buff *skb, int error);
......@@ -360,6 +420,7 @@ static inline int netlbl_sock_getattr(struct sock *sk,
return -ENOSYS;
}
static inline int netlbl_skbuff_getattr(const struct sk_buff *skb,
u16 family,
struct netlbl_lsm_secattr *secattr)
{
return -ENOSYS;
......
......@@ -348,6 +348,7 @@ static int cipso_v4_cache_check(const unsigned char *key,
atomic_inc(&entry->lsm_data->refcount);
secattr->cache = entry->lsm_data;
secattr->flags |= NETLBL_SECATTR_CACHE;
secattr->type = NETLBL_NLTYPE_CIPSOV4;
if (prev_entry == NULL) {
spin_unlock_bh(&cipso_v4_cache[bkt].lock);
return 0;
......@@ -865,7 +866,7 @@ static int cipso_v4_map_cat_rbm_hton(const struct cipso_v4_doi *doi_def,
}
for (;;) {
host_spot = netlbl_secattr_catmap_walk(secattr->mls_cat,
host_spot = netlbl_secattr_catmap_walk(secattr->attr.mls.cat,
host_spot + 1);
if (host_spot < 0)
break;
......@@ -948,7 +949,7 @@ static int cipso_v4_map_cat_rbm_ntoh(const struct cipso_v4_doi *doi_def,
return -EPERM;
break;
}
ret_val = netlbl_secattr_catmap_setbit(secattr->mls_cat,
ret_val = netlbl_secattr_catmap_setbit(secattr->attr.mls.cat,
host_spot,
GFP_ATOMIC);
if (ret_val != 0)
......@@ -1014,7 +1015,8 @@ static int cipso_v4_map_cat_enum_hton(const struct cipso_v4_doi *doi_def,
u32 cat_iter = 0;
for (;;) {
cat = netlbl_secattr_catmap_walk(secattr->mls_cat, cat + 1);
cat = netlbl_secattr_catmap_walk(secattr->attr.mls.cat,
cat + 1);
if (cat < 0)
break;
if ((cat_iter + 2) > net_cat_len)
......@@ -1049,7 +1051,7 @@ static int cipso_v4_map_cat_enum_ntoh(const struct cipso_v4_doi *doi_def,
u32 iter;
for (iter = 0; iter < net_cat_len; iter += 2) {
ret_val = netlbl_secattr_catmap_setbit(secattr->mls_cat,
ret_val = netlbl_secattr_catmap_setbit(secattr->attr.mls.cat,
ntohs(get_unaligned((__be16 *)&net_cat[iter])),
GFP_ATOMIC);
if (ret_val != 0)
......@@ -1130,7 +1132,8 @@ static int cipso_v4_map_cat_rng_hton(const struct cipso_v4_doi *doi_def,
return -ENOSPC;
for (;;) {
iter = netlbl_secattr_catmap_walk(secattr->mls_cat, iter + 1);
iter = netlbl_secattr_catmap_walk(secattr->attr.mls.cat,
iter + 1);
if (iter < 0)
break;
cat_size += (iter == 0 ? 0 : sizeof(u16));
......@@ -1138,7 +1141,8 @@ static int cipso_v4_map_cat_rng_hton(const struct cipso_v4_doi *doi_def,
return -ENOSPC;
array[array_cnt++] = iter;
iter = netlbl_secattr_catmap_walk_rng(secattr->mls_cat, iter);
iter = netlbl_secattr_catmap_walk_rng(secattr->attr.mls.cat,
iter);
if (iter < 0)
return -EFAULT;
cat_size += sizeof(u16);
......@@ -1191,7 +1195,7 @@ static int cipso_v4_map_cat_rng_ntoh(const struct cipso_v4_doi *doi_def,
else
cat_low = 0;
ret_val = netlbl_secattr_catmap_setrng(secattr->mls_cat,
ret_val = netlbl_secattr_catmap_setrng(secattr->attr.mls.cat,
cat_low,
cat_high,
GFP_ATOMIC);
......@@ -1251,7 +1255,9 @@ static int cipso_v4_gentag_rbm(const struct cipso_v4_doi *doi_def,
if ((secattr->flags & NETLBL_SECATTR_MLS_LVL) == 0)
return -EPERM;
ret_val = cipso_v4_map_lvl_hton(doi_def, secattr->mls_lvl, &level);
ret_val = cipso_v4_map_lvl_hton(doi_def,
secattr->attr.mls.lvl,
&level);
if (ret_val != 0)
return ret_val;
......@@ -1303,12 +1309,13 @@ static int cipso_v4_parsetag_rbm(const struct cipso_v4_doi *doi_def,
ret_val = cipso_v4_map_lvl_ntoh(doi_def, tag[3], &level);
if (ret_val != 0)
return ret_val;
secattr->mls_lvl = level;
secattr->attr.mls.lvl = level;
secattr->flags |= NETLBL_SECATTR_MLS_LVL;
if (tag_len > 4) {
secattr->mls_cat = netlbl_secattr_catmap_alloc(GFP_ATOMIC);
if (secattr->mls_cat == NULL)
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,
......@@ -1316,7 +1323,7 @@ static int cipso_v4_parsetag_rbm(const struct cipso_v4_doi *doi_def,
tag_len - 4,
secattr);
if (ret_val != 0) {
netlbl_secattr_catmap_free(secattr->mls_cat);
netlbl_secattr_catmap_free(secattr->attr.mls.cat);
return ret_val;
}
......@@ -1350,7 +1357,9 @@ static int cipso_v4_gentag_enum(const struct cipso_v4_doi *doi_def,
if (!(secattr->flags & NETLBL_SECATTR_MLS_LVL))
return -EPERM;
ret_val = cipso_v4_map_lvl_hton(doi_def, secattr->mls_lvl, &level);
ret_val = cipso_v4_map_lvl_hton(doi_def,
secattr->attr.mls.lvl,
&level);
if (ret_val != 0)
return ret_val;
......@@ -1396,12 +1405,13 @@ static int cipso_v4_parsetag_enum(const struct cipso_v4_doi *doi_def,
ret_val = cipso_v4_map_lvl_ntoh(doi_def, tag[3], &level);
if (ret_val != 0)
return ret_val;
secattr->mls_lvl = level;
secattr->attr.mls.lvl = level;
secattr->flags |= NETLBL_SECATTR_MLS_LVL;
if (tag_len > 4) {
secattr->mls_cat = netlbl_secattr_catmap_alloc(GFP_ATOMIC);
if (secattr->mls_cat == NULL)
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,
......@@ -1409,7 +1419,7 @@ static int cipso_v4_parsetag_enum(const struct cipso_v4_doi *doi_def,
tag_len - 4,
secattr);
if (ret_val != 0) {
netlbl_secattr_catmap_free(secattr->mls_cat);
netlbl_secattr_catmap_free(secattr->attr.mls.cat);
return ret_val;
}
......@@ -1443,7 +1453,9 @@ static int cipso_v4_gentag_rng(const struct cipso_v4_doi *doi_def,
if (!(secattr->flags & NETLBL_SECATTR_MLS_LVL))
return -EPERM;
ret_val = cipso_v4_map_lvl_hton(doi_def, secattr->mls_lvl, &level);
ret_val = cipso_v4_map_lvl_hton(doi_def,
secattr->attr.mls.lvl,
&level);
if (ret_val != 0)
return ret_val;
......@@ -1488,12 +1500,13 @@ static int cipso_v4_parsetag_rng(const struct cipso_v4_doi *doi_def,
ret_val = cipso_v4_map_lvl_ntoh(doi_def, tag[3], &level);
if (ret_val != 0)
return ret_val;
secattr->mls_lvl = level;
secattr->attr.mls.lvl = level;
secattr->flags |= NETLBL_SECATTR_MLS_LVL;
if (tag_len > 4) {
secattr->mls_cat = netlbl_secattr_catmap_alloc(GFP_ATOMIC);
if (secattr->mls_cat == NULL)
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,
......@@ -1501,7 +1514,7 @@ static int cipso_v4_parsetag_rng(const struct cipso_v4_doi *doi_def,
tag_len - 4,
secattr);
if (ret_val != 0) {
netlbl_secattr_catmap_free(secattr->mls_cat);
netlbl_secattr_catmap_free(secattr->attr.mls.cat);
return ret_val;
}
......@@ -1850,6 +1863,8 @@ static int cipso_v4_getattr(const unsigned char *cipso,
ret_val = cipso_v4_parsetag_rng(doi_def, &cipso[6], secattr);
break;
}
if (ret_val == 0)
secattr->type = NETLBL_NLTYPE_CIPSOV4;
getattr_return:
rcu_read_unlock();
......
......@@ -72,12 +72,13 @@ static bool checkentry_selinux(struct xt_secmark_target_info *info)
return false;
}
err = selinux_relabel_packet_permission(sel->selsid);
err = selinux_secmark_relabel_packet_permission(sel->selsid);
if (err) {
printk(KERN_INFO PFX "unable to obtain relabeling permission\n");
return false;
}
selinux_secmark_refcount_inc();
return true;
}
......@@ -110,11 +111,20 @@ secmark_tg_check(const char *tablename, const void *entry,
return true;
}
void secmark_tg_destroy(const struct xt_target *target, void *targinfo)
{
switch (mode) {
case SECMARK_MODE_SEL:
selinux_secmark_refcount_dec();
}
}
static struct xt_target secmark_tg_reg[] __read_mostly = {
{
.name = "SECMARK",
.family = AF_INET,
.checkentry = secmark_tg_check,
.destroy = secmark_tg_destroy,
.target = secmark_tg,
.targetsize = sizeof(struct xt_secmark_target_info),
.table = "mangle",
......@@ -124,6 +134,7 @@ static struct xt_target secmark_tg_reg[] __read_mostly = {
.name = "SECMARK",
.family = AF_INET6,
.checkentry = secmark_tg_check,
.destroy = secmark_tg_destroy,
.target = secmark_tg,
.targetsize = sizeof(struct xt_secmark_target_info),
.table = "mangle",
......
......@@ -38,6 +38,7 @@
#include <net/genetlink.h>
#include <net/netlabel.h>
#include <net/cipso_ipv4.h>
#include <asm/atomic.h>
#include "netlabel_user.h"
#include "netlabel_cipso_v4.h"
......@@ -421,7 +422,7 @@ static int netlbl_cipsov4_add(struct sk_buff *skb, struct genl_info *info)
break;
}
if (ret_val == 0)
netlbl_mgmt_protocount_inc();
atomic_inc(&netlabel_mgmt_protocount);
audit_buf = netlbl_audit_start_common(AUDIT_MAC_CIPSOV4_ADD,
&audit_info);
......@@ -698,7 +699,7 @@ static int netlbl_cipsov4_remove(struct sk_buff *skb, struct genl_info *info)
&audit_info,
netlbl_cipsov4_doi_free);
if (ret_val == 0)
netlbl_mgmt_protocount_dec();
atomic_dec(&netlabel_mgmt_protocount);
audit_buf = netlbl_audit_start_common(AUDIT_MAC_CIPSOV4_DEL,
&audit_info);
......
......@@ -54,9 +54,6 @@ struct netlbl_domhsh_tbl {
* hash table should be okay */
static DEFINE_SPINLOCK(netlbl_domhsh_lock);
static struct netlbl_domhsh_tbl *netlbl_domhsh = NULL;
/* Default domain mapping */
static DEFINE_SPINLOCK(netlbl_domhsh_def_lock);
static struct netlbl_dom_map *netlbl_domhsh_def = NULL;
/*
......@@ -109,17 +106,14 @@ static u32 netlbl_domhsh_hash(const char *key)
/**
* netlbl_domhsh_search - Search for a domain entry
* @domain: the domain
* @def: return default if no match is found
*
* Description:
* Searches the domain hash table and returns a pointer to the hash table
* entry if found, otherwise NULL is returned. If @def is non-zero and a
* match is not found in the domain hash table the default mapping is returned
* if it exists. The caller is responsibile for the rcu hash table locks
* (i.e. the caller much call rcu_read_[un]lock()).
* entry if found, otherwise NULL is returned. The caller is responsibile for
* the rcu hash table locks (i.e. the caller much call rcu_read_[un]lock()).
*
*/
static struct netlbl_dom_map *netlbl_domhsh_search(const char *domain, u32 def)
static struct netlbl_dom_map *netlbl_domhsh_search(const char *domain)
{
u32 bkt;
struct netlbl_dom_map *iter;
......@@ -133,10 +127,31 @@ static struct netlbl_dom_map *netlbl_domhsh_search(const char *domain, u32 def)
return iter;
}
if (def != 0) {
iter = rcu_dereference(netlbl_domhsh_def);
if (iter != NULL && iter->valid)
return iter;
return NULL;
}
/**
* netlbl_domhsh_search_def - Search for a domain entry
* @domain: the domain
* @def: return default if no match is found
*
* Description:
* Searches the domain hash table and returns a pointer to the hash table
* entry if an exact match is found, if an exact match is not present in the
* hash table then the default entry is returned if valid otherwise NULL is
* returned. The caller is responsibile for the rcu hash table locks
* (i.e. the caller much call rcu_read_[un]lock()).
*
*/
static struct netlbl_dom_map *netlbl_domhsh_search_def(const char *domain)
{
struct netlbl_dom_map *entry;
entry = netlbl_domhsh_search(domain);
if (entry == NULL) {
entry = rcu_dereference(netlbl_domhsh_def);
if (entry != NULL && entry->valid)
return entry;
}
return NULL;
......@@ -221,24 +236,22 @@ int netlbl_domhsh_add(struct netlbl_dom_map *entry,
INIT_RCU_HEAD(&entry->rcu);
rcu_read_lock();
spin_lock(&netlbl_domhsh_lock);
if (entry->domain != NULL) {
bkt = netlbl_domhsh_hash(entry->domain);
spin_lock(&netlbl_domhsh_lock);
if (netlbl_domhsh_search(entry->domain, 0) == NULL)
if (netlbl_domhsh_search(entry->domain) == NULL)
list_add_tail_rcu(&entry->list,
&rcu_dereference(netlbl_domhsh)->tbl[bkt]);
else
ret_val = -EEXIST;
spin_unlock(&netlbl_domhsh_lock);
} else {
INIT_LIST_HEAD(&entry->list);
spin_lock(&netlbl_domhsh_def_lock);
if (rcu_dereference(netlbl_domhsh_def) == NULL)
rcu_assign_pointer(netlbl_domhsh_def, entry);
else
ret_val = -EEXIST;
spin_unlock(&netlbl_domhsh_def_lock);
}
spin_unlock(&netlbl_domhsh_lock);
audit_buf = netlbl_audit_start_common(AUDIT_MAC_MAP_ADD, audit_info);
if (audit_buf != NULL) {
audit_log_format(audit_buf,
......@@ -307,7 +320,10 @@ int netlbl_domhsh_remove(const char *domain, struct netlbl_audit *audit_info)
struct audit_buffer *audit_buf;
rcu_read_lock();
entry = netlbl_domhsh_search(domain, (domain != NULL ? 0 : 1));
if (domain)
entry = netlbl_domhsh_search(domain);
else
entry = netlbl_domhsh_search_def(domain);
if (entry == NULL)
goto remove_return;
switch (entry->type) {
......@@ -316,23 +332,16 @@ int netlbl_domhsh_remove(const char *domain, struct netlbl_audit *audit_info)
entry->domain);
break;
}
if (entry != rcu_dereference(netlbl_domhsh_def)) {
spin_lock(&netlbl_domhsh_lock);
if (entry->valid) {
entry->valid = 0;
spin_lock(&netlbl_domhsh_lock);
if (entry->valid) {
entry->valid = 0;
if (entry != rcu_dereference(netlbl_domhsh_def))
list_del_rcu(&entry->list);
ret_val = 0;
}
spin_unlock(&netlbl_domhsh_lock);
} else {
spin_lock(&netlbl_domhsh_def_lock);
if (entry->valid) {
entry->valid = 0;
else
rcu_assign_pointer(netlbl_domhsh_def, NULL);
ret_val = 0;
}
spin_unlock(&netlbl_domhsh_def_lock);
ret_val = 0;
}
spin_unlock(&netlbl_domhsh_lock);
audit_buf = netlbl_audit_start_common(AUDIT_MAC_MAP_DEL, audit_info);
if (audit_buf != NULL) {
......@@ -377,7 +386,7 @@ int netlbl_domhsh_remove_default(struct netlbl_audit *audit_info)
*/
struct netlbl_dom_map *netlbl_domhsh_getentry(const char *domain)
{
return netlbl_domhsh_search(domain, 1);
return netlbl_domhsh_search_def(domain);
}
/**
......
......@@ -34,6 +34,7 @@
#include <net/netlabel.h>
#include <net/cipso_ipv4.h>
#include <asm/bug.h>
#include <asm/atomic.h>
#include "netlabel_domainhash.h"
#include "netlabel_unlabeled.h"
......@@ -262,7 +263,7 @@ int netlbl_enabled(void)
/* At some point we probably want to expose this mechanism to the user
* as well so that admins can toggle NetLabel regardless of the
* configuration */
return (netlbl_mgmt_protocount_value() > 0 ? 1 : 0);
return (atomic_read(&netlabel_mgmt_protocount) > 0);
}
/**
......@@ -311,7 +312,7 @@ socket_setattr_return:
* @secattr: the security attributes
*
* Description:
* Examines the given sock to see any NetLabel style labeling has been
* Examines the given sock to see if any NetLabel style labeling has been
* applied to the sock, if so it parses the socket label and returns the
* security attributes in @secattr. Returns zero on success, negative values
* on failure.
......@@ -319,18 +320,13 @@ socket_setattr_return:
*/
int netlbl_sock_getattr(struct sock *sk, struct netlbl_lsm_secattr *secattr)
{
int ret_val;
ret_val = cipso_v4_sock_getattr(sk, secattr);
if (ret_val == 0)
return 0;
return netlbl_unlabel_getattr(secattr);
return cipso_v4_sock_getattr(sk, secattr);
}
/**
* netlbl_skbuff_getattr - Determine the security attributes of a packet
* @skb: the packet
* @family: protocol family
* @secattr: the security attributes
*
* Description:
......@@ -341,13 +337,14 @@ int netlbl_sock_getattr(struct sock *sk, struct netlbl_lsm_secattr *secattr)
*
*/
int netlbl_skbuff_getattr(const struct sk_buff *skb,
u16 family,
struct netlbl_lsm_secattr *secattr)
{
if (CIPSO_V4_OPTEXIST(skb) &&
cipso_v4_skbuff_getattr(skb, secattr) == 0)
return 0;
return netlbl_unlabel_getattr(secattr);
return netlbl_unlabel_getattr(skb, family, secattr);
}
/**
......@@ -431,6 +428,10 @@ static int __init netlbl_init(void)
if (ret_val != 0)
goto init_failure;
ret_val = netlbl_unlabel_init(NETLBL_UNLHSH_BITSIZE);
if (ret_val != 0)
goto init_failure;
ret_val = netlbl_netlink_init();
if (ret_val != 0)
goto init_failure;
......
......@@ -37,14 +37,14 @@
#include <net/genetlink.h>
#include <net/netlabel.h>
#include <net/cipso_ipv4.h>
#include <asm/atomic.h>
#include "netlabel_domainhash.h"
#include "netlabel_user.h"
#include "netlabel_mgmt.h"
/* NetLabel configured protocol count */
static DEFINE_SPINLOCK(netlabel_mgmt_protocount_lock);
static u32 netlabel_mgmt_protocount = 0;
/* NetLabel configured protocol counter */
atomic_t netlabel_mgmt_protocount = ATOMIC_INIT(0);
/* Argument struct for netlbl_domhsh_walk() */
struct netlbl_domhsh_walk_arg {
......@@ -70,63 +70,6 @@ static const struct nla_policy netlbl_mgmt_genl_policy[NLBL_MGMT_A_MAX + 1] = {
[NLBL_MGMT_A_CV4DOI] = { .type = NLA_U32 },
};
/*
* NetLabel Misc Management Functions
*/
/**
* netlbl_mgmt_protocount_inc - Increment the configured labeled protocol count
*
* Description:
* Increment the number of labeled protocol configurations in the current
* NetLabel configuration. Keep track of this for use in determining if
* NetLabel label enforcement should be active/enabled or not in the LSM.
*
*/
void netlbl_mgmt_protocount_inc(void)
{
spin_lock(&netlabel_mgmt_protocount_lock);
netlabel_mgmt_protocount++;
spin_unlock(&netlabel_mgmt_protocount_lock);
}
/**
* netlbl_mgmt_protocount_dec - Decrement the configured labeled protocol count
*
* Description:
* Decrement the number of labeled protocol configurations in the current
* NetLabel configuration. Keep track of this for use in determining if
* NetLabel label enforcement should be active/enabled or not in the LSM.
*
*/
void netlbl_mgmt_protocount_dec(void)
{
spin_lock(&netlabel_mgmt_protocount_lock);
if (netlabel_mgmt_protocount > 0)
netlabel_mgmt_protocount--;
spin_unlock(&netlabel_mgmt_protocount_lock);
}
/**
* netlbl_mgmt_protocount_value - Return the number of configured protocols
*
* Description:
* Return the number of labeled protocols in the current NetLabel
* configuration. This value is useful in determining if NetLabel label
* enforcement should be active/enabled or not in the LSM.
*
*/
u32 netlbl_mgmt_protocount_value(void)
{
u32 val;
rcu_read_lock();
val = netlabel_mgmt_protocount;
rcu_read_unlock();
return val;
}
/*
* NetLabel Command Handlers
*/
......
......@@ -32,6 +32,7 @@
#define _NETLABEL_MGMT_H
#include <net/netlabel.h>
#include <asm/atomic.h>
/*
* The following NetLabel payloads are supported by the management interface.
......@@ -168,9 +169,7 @@ enum {
/* NetLabel protocol functions */
int netlbl_mgmt_genl_init(void);
/* NetLabel misc management functions */
void netlbl_mgmt_protocount_inc(void);
void netlbl_mgmt_protocount_dec(void);
u32 netlbl_mgmt_protocount_value(void);
/* NetLabel configured protocol reference counter */
extern atomic_t netlabel_mgmt_protocount;
#endif
This diff is collapsed.
......@@ -36,6 +36,116 @@
/*
* The following NetLabel payloads are supported by the Unlabeled subsystem.
*
* o STATICADD
* This message is sent from an application to add a new static label for
* incoming unlabeled connections.
*
* Required attributes:
*
* NLBL_UNLABEL_A_IFACE
* NLBL_UNLABEL_A_SECCTX
*
* If IPv4 is specified the following attributes are required:
*
* NLBL_UNLABEL_A_IPV4ADDR
* NLBL_UNLABEL_A_IPV4MASK
*
* If IPv6 is specified the following attributes are required:
*
* NLBL_UNLABEL_A_IPV6ADDR
* NLBL_UNLABEL_A_IPV6MASK
*
* o STATICREMOVE
* This message is sent from an application to remove an existing static
* label for incoming unlabeled connections.
*
* Required attributes:
*
* NLBL_UNLABEL_A_IFACE
*
* If IPv4 is specified the following attributes are required:
*
* NLBL_UNLABEL_A_IPV4ADDR
* NLBL_UNLABEL_A_IPV4MASK
*
* If IPv6 is specified the following attributes are required:
*
* NLBL_UNLABEL_A_IPV6ADDR
* NLBL_UNLABEL_A_IPV6MASK
*
* o STATICLIST
* This message can be sent either from an application or by the kernel in
* response to an application generated STATICLIST message. When sent by an
* application there is no payload and the NLM_F_DUMP flag should be set.
* The kernel should response with a series of the following messages.
*
* Required attributes:
*
* NLBL_UNLABEL_A_IFACE
* NLBL_UNLABEL_A_SECCTX
*
* If IPv4 is specified the following attributes are required:
*
* NLBL_UNLABEL_A_IPV4ADDR
* NLBL_UNLABEL_A_IPV4MASK
*
* If IPv6 is specified the following attributes are required:
*
* NLBL_UNLABEL_A_IPV6ADDR
* NLBL_UNLABEL_A_IPV6MASK
*
* o STATICADDDEF
* This message is sent from an application to set the default static
* label for incoming unlabeled connections.
*
* Required attribute:
*
* NLBL_UNLABEL_A_SECCTX
*
* If IPv4 is specified the following attributes are required:
*
* NLBL_UNLABEL_A_IPV4ADDR
* NLBL_UNLABEL_A_IPV4MASK
*
* If IPv6 is specified the following attributes are required:
*
* NLBL_UNLABEL_A_IPV6ADDR
* NLBL_UNLABEL_A_IPV6MASK
*
* o STATICREMOVEDEF
* This message is sent from an application to remove the existing default
* static label for incoming unlabeled connections.
*
* If IPv4 is specified the following attributes are required:
*
* NLBL_UNLABEL_A_IPV4ADDR
* NLBL_UNLABEL_A_IPV4MASK
*
* If IPv6 is specified the following attributes are required:
*
* NLBL_UNLABEL_A_IPV6ADDR
* NLBL_UNLABEL_A_IPV6MASK
*
* o STATICLISTDEF
* This message can be sent either from an application or by the kernel in
* response to an application generated STATICLISTDEF message. When sent by
* an application there is no payload and the NLM_F_DUMP flag should be set.
* The kernel should response with the following message.
*
* Required attribute:
*
* NLBL_UNLABEL_A_SECCTX
*
* If IPv4 is specified the following attributes are required:
*
* NLBL_UNLABEL_A_IPV4ADDR
* NLBL_UNLABEL_A_IPV4MASK
*
* If IPv6 is specified the following attributes are required:
*
* NLBL_UNLABEL_A_IPV6ADDR
* NLBL_UNLABEL_A_IPV6MASK
*
* o ACCEPT
* This message is sent from an application to specify if the kernel should
* allow unlabled packets to pass if they do not match any of the static
......@@ -62,6 +172,12 @@ enum {
NLBL_UNLABEL_C_UNSPEC,
NLBL_UNLABEL_C_ACCEPT,
NLBL_UNLABEL_C_LIST,
NLBL_UNLABEL_C_STATICADD,
NLBL_UNLABEL_C_STATICREMOVE,
NLBL_UNLABEL_C_STATICLIST,
NLBL_UNLABEL_C_STATICADDDEF,
NLBL_UNLABEL_C_STATICREMOVEDEF,
NLBL_UNLABEL_C_STATICLISTDEF,
__NLBL_UNLABEL_C_MAX,
};
#define NLBL_UNLABEL_C_MAX (__NLBL_UNLABEL_C_MAX - 1)
......@@ -73,6 +189,24 @@ enum {
/* (NLA_U8)
* if true then unlabeled packets are allowed to pass, else unlabeled
* packets are rejected */
NLBL_UNLABEL_A_IPV6ADDR,
/* (NLA_BINARY, struct in6_addr)
* an IPv6 address */
NLBL_UNLABEL_A_IPV6MASK,
/* (NLA_BINARY, struct in6_addr)
* an IPv6 address mask */
NLBL_UNLABEL_A_IPV4ADDR,
/* (NLA_BINARY, struct in_addr)
* an IPv4 address */
NLBL_UNLABEL_A_IPV4MASK,
/* (NLA_BINARY, struct in_addr)
* and IPv4 address mask */
NLBL_UNLABEL_A_IFACE,
/* (NLA_NULL_STRING)
* network interface */
NLBL_UNLABEL_A_SECCTX,
/* (NLA_BINARY)
* a LSM specific security context */
__NLBL_UNLABEL_A_MAX,
};
#define NLBL_UNLABEL_A_MAX (__NLBL_UNLABEL_A_MAX - 1)
......@@ -80,8 +214,17 @@ enum {
/* NetLabel protocol functions */
int netlbl_unlabel_genl_init(void);
/* Unlabeled connection hash table size */
/* XXX - currently this number is an uneducated guess */
#define NETLBL_UNLHSH_BITSIZE 7
/* General Unlabeled init function */
int netlbl_unlabel_init(u32 size);
/* Process Unlabeled incoming network packets */
int netlbl_unlabel_getattr(struct netlbl_lsm_secattr *secattr);
int netlbl_unlabel_getattr(const struct sk_buff *skb,
u16 family,
struct netlbl_lsm_secattr *secattr);
/* Set the default configuration to allow Unlabeled packets */
int netlbl_unlabel_defconf(void);
......
......@@ -76,6 +76,7 @@ config SECURITY_NETWORK_XFRM
config SECURITY_CAPABILITIES
bool "Default Linux Capabilities"
depends on SECURITY
default y
help
This enables the "default" Linux capabilities functionality.
If you are unsure how to answer this question, answer Y.
......
......@@ -145,7 +145,7 @@ config SECURITY_SELINUX_POLICYDB_VERSION_MAX
config SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE
int "NSA SELinux maximum supported policy format version value"
depends on SECURITY_SELINUX_POLICYDB_VERSION_MAX
range 15 21
range 15 22
default 19
help
This option sets the value for the maximum policy format version
......
......@@ -4,7 +4,14 @@
obj-$(CONFIG_SECURITY_SELINUX) := selinux.o ss/
selinux-y := avc.o hooks.o selinuxfs.o netlink.o nlmsgtab.o netif.o exports.o
selinux-y := avc.o \
hooks.o \
selinuxfs.o \
netlink.o \
nlmsgtab.o \
netif.o \
netnode.o \
exports.o
selinux-$(CONFIG_SECURITY_NETWORK_XFRM) += xfrm.o
......
......@@ -661,9 +661,18 @@ void avc_audit(u32 ssid, u32 tsid,
"daddr", "dest");
break;
}
if (a->u.net.netif)
audit_log_format(ab, " netif=%s",
a->u.net.netif);
if (a->u.net.netif > 0) {
struct net_device *dev;
/* NOTE: we always use init's namespace */
dev = dev_get_by_index(&init_net,
a->u.net.netif);
if (dev) {
audit_log_format(ab, " netif=%s",
dev->name);
dev_put(dev);
}
}
break;
}
}
......
......@@ -17,10 +17,14 @@
#include <linux/selinux.h>
#include <linux/fs.h>
#include <linux/ipc.h>
#include <asm/atomic.h>
#include "security.h"
#include "objsec.h"
/* SECMARK reference count */
extern atomic_t selinux_secmark_refcount;
int selinux_sid_to_string(u32 sid, char **ctx, u32 *ctxlen)
{
if (selinux_enabled)
......@@ -74,7 +78,7 @@ int selinux_string_to_sid(char *str, u32 *sid)
}
EXPORT_SYMBOL_GPL(selinux_string_to_sid);
int selinux_relabel_packet_permission(u32 sid)
int selinux_secmark_relabel_packet_permission(u32 sid)
{
if (selinux_enabled) {
struct task_security_struct *tsec = current->security;
......@@ -84,4 +88,16 @@ int selinux_relabel_packet_permission(u32 sid)
}
return 0;
}
EXPORT_SYMBOL_GPL(selinux_relabel_packet_permission);
EXPORT_SYMBOL_GPL(selinux_secmark_relabel_packet_permission);
void selinux_secmark_refcount_inc(void)
{
atomic_inc(&selinux_secmark_refcount);
}
EXPORT_SYMBOL_GPL(selinux_secmark_refcount_inc);
void selinux_secmark_refcount_dec(void)
{
atomic_dec(&selinux_secmark_refcount);
}
EXPORT_SYMBOL_GPL(selinux_secmark_refcount_dec);
This diff is collapsed.
......@@ -37,6 +37,8 @@
S_(SECCLASS_NODE, NODE__ENFORCE_DEST, "enforce_dest")
S_(SECCLASS_NODE, NODE__DCCP_RECV, "dccp_recv")
S_(SECCLASS_NODE, NODE__DCCP_SEND, "dccp_send")
S_(SECCLASS_NODE, NODE__RECVFROM, "recvfrom")
S_(SECCLASS_NODE, NODE__SENDTO, "sendto")
S_(SECCLASS_NETIF, NETIF__TCP_RECV, "tcp_recv")
S_(SECCLASS_NETIF, NETIF__TCP_SEND, "tcp_send")
S_(SECCLASS_NETIF, NETIF__UDP_RECV, "udp_recv")
......@@ -45,6 +47,8 @@
S_(SECCLASS_NETIF, NETIF__RAWIP_SEND, "rawip_send")
S_(SECCLASS_NETIF, NETIF__DCCP_RECV, "dccp_recv")
S_(SECCLASS_NETIF, NETIF__DCCP_SEND, "dccp_send")
S_(SECCLASS_NETIF, NETIF__INGRESS, "ingress")
S_(SECCLASS_NETIF, NETIF__EGRESS, "egress")
S_(SECCLASS_UNIX_STREAM_SOCKET, UNIX_STREAM_SOCKET__CONNECTTO, "connectto")
S_(SECCLASS_UNIX_STREAM_SOCKET, UNIX_STREAM_SOCKET__NEWCONN, "newconn")
S_(SECCLASS_UNIX_STREAM_SOCKET, UNIX_STREAM_SOCKET__ACCEPTFROM, "acceptfrom")
......@@ -149,6 +153,10 @@
S_(SECCLASS_PACKET, PACKET__SEND, "send")
S_(SECCLASS_PACKET, PACKET__RECV, "recv")
S_(SECCLASS_PACKET, PACKET__RELABELTO, "relabelto")
S_(SECCLASS_PACKET, PACKET__FLOW_IN, "flow_in")
S_(SECCLASS_PACKET, PACKET__FLOW_OUT, "flow_out")
S_(SECCLASS_PACKET, PACKET__FORWARD_IN, "forward_in")
S_(SECCLASS_PACKET, PACKET__FORWARD_OUT, "forward_out")
S_(SECCLASS_KEY, KEY__VIEW, "view")
S_(SECCLASS_KEY, KEY__READ, "read")
S_(SECCLASS_KEY, KEY__WRITE, "write")
......@@ -159,3 +167,4 @@
S_(SECCLASS_DCCP_SOCKET, DCCP_SOCKET__NODE_BIND, "node_bind")
S_(SECCLASS_DCCP_SOCKET, DCCP_SOCKET__NAME_CONNECT, "name_connect")
S_(SECCLASS_MEMPROTECT, MEMPROTECT__MMAP_ZERO, "mmap_zero")
S_(SECCLASS_PEER, PEER__RECV, "recv")
......@@ -292,6 +292,8 @@
#define NODE__ENFORCE_DEST 0x00000040UL
#define NODE__DCCP_RECV 0x00000080UL
#define NODE__DCCP_SEND 0x00000100UL
#define NODE__RECVFROM 0x00000200UL
#define NODE__SENDTO 0x00000400UL
#define NETIF__TCP_RECV 0x00000001UL
#define NETIF__TCP_SEND 0x00000002UL
#define NETIF__UDP_RECV 0x00000004UL
......@@ -300,6 +302,8 @@
#define NETIF__RAWIP_SEND 0x00000020UL
#define NETIF__DCCP_RECV 0x00000040UL
#define NETIF__DCCP_SEND 0x00000080UL
#define NETIF__INGRESS 0x00000100UL
#define NETIF__EGRESS 0x00000200UL
#define NETLINK_SOCKET__IOCTL 0x00000001UL
#define NETLINK_SOCKET__READ 0x00000002UL
#define NETLINK_SOCKET__WRITE 0x00000004UL
......@@ -792,6 +796,10 @@
#define PACKET__SEND 0x00000001UL
#define PACKET__RECV 0x00000002UL
#define PACKET__RELABELTO 0x00000004UL
#define PACKET__FLOW_IN 0x00000008UL
#define PACKET__FLOW_OUT 0x00000010UL
#define PACKET__FORWARD_IN 0x00000020UL
#define PACKET__FORWARD_OUT 0x00000040UL
#define KEY__VIEW 0x00000001UL
#define KEY__READ 0x00000002UL
#define KEY__WRITE 0x00000004UL
......@@ -824,3 +832,4 @@
#define DCCP_SOCKET__NODE_BIND 0x00400000UL
#define DCCP_SOCKET__NAME_CONNECT 0x00800000UL
#define MEMPROTECT__MMAP_ZERO 0x00000001UL
#define PEER__RECV 0x00000001UL
......@@ -51,7 +51,7 @@ struct avc_audit_data {
struct inode *inode;
} fs;
struct {
char *netif;
int netif;
struct sock *sk;
u16 family;
__be16 dport;
......
......@@ -64,3 +64,10 @@
S_(NULL)
S_("dccp_socket")
S_("memprotect")
S_(NULL)
S_(NULL)
S_(NULL)
S_(NULL)
S_(NULL)
S_(NULL)
S_("peer")
......@@ -50,6 +50,7 @@
#define SECCLASS_KEY 58
#define SECCLASS_DCCP_SOCKET 60
#define SECCLASS_MEMPROTECT 61
#define SECCLASS_PEER 68
/*
* Security identifier indices for initial entities
......
......@@ -7,6 +7,8 @@
* Author: James Morris <jmorris@redhat.com>
*
* Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com>
* Copyright (C) 2007 Hewlett-Packard Development Company, L.P.
* Paul Moore, <paul.moore@hp.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
......@@ -15,7 +17,7 @@
#ifndef _SELINUX_NETIF_H_
#define _SELINUX_NETIF_H_
int sel_netif_sids(struct net_device *dev, u32 *if_sid, u32 *msg_sid);
int sel_netif_sid(int ifindex, u32 *sid);
#endif /* _SELINUX_NETIF_H_ */
......@@ -46,13 +46,17 @@ void selinux_netlbl_sk_security_init(struct sk_security_struct *ssec,
void selinux_netlbl_sk_security_clone(struct sk_security_struct *ssec,
struct sk_security_struct *newssec);
int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, u32 base_sid, u32 *sid);
int selinux_netlbl_skbuff_getsid(struct sk_buff *skb,
u16 family,
u32 *type,
u32 *sid);
void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock);
int selinux_netlbl_socket_post_create(struct socket *sock);
int selinux_netlbl_inode_permission(struct inode *inode, int mask);
int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
struct sk_buff *skb,
u16 family,
struct avc_audit_data *ad);
int selinux_netlbl_socket_setsockopt(struct socket *sock,
int level,
......@@ -83,9 +87,11 @@ static inline void selinux_netlbl_sk_security_clone(
}
static inline int selinux_netlbl_skbuff_getsid(struct sk_buff *skb,
u32 base_sid,
u16 family,
u32 *type,
u32 *sid)
{
*type = NETLBL_NLTYPE_NONE;
*sid = SECSID_NULL;
return 0;
}
......@@ -106,6 +112,7 @@ static inline int selinux_netlbl_inode_permission(struct inode *inode,
}
static inline int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
struct sk_buff *skb,
u16 family,
struct avc_audit_data *ad)
{
return 0;
......
/*
* Network node table
*
* SELinux must keep a mapping of network nodes to labels/SIDs. This
* mapping is maintained as part of the normal policy but a fast cache is
* needed to reduce the lookup overhead since most of these queries happen on
* a per-packet basis.
*
* Author: Paul Moore <paul.moore@hp.com>
*
*/
/*
* (c) Copyright Hewlett-Packard Development Company, L.P., 2007
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef _SELINUX_NETNODE_H
#define _SELINUX_NETNODE_H
int sel_netnode_sid(void *addr, u16 family, u32 *sid);
#endif
......@@ -96,17 +96,25 @@ struct bprm_security_struct {
};
struct netif_security_struct {
struct net_device *dev; /* back pointer */
u32 if_sid; /* SID for this interface */
u32 msg_sid; /* default SID for messages received on this interface */
int ifindex; /* device index */
u32 sid; /* SID for this interface */
};
struct netnode_security_struct {
union {
__be32 ipv4; /* IPv4 node address */
struct in6_addr ipv6; /* IPv6 node address */
} addr;
u32 sid; /* SID for this node */
u16 family; /* address family */
};
struct sk_security_struct {
struct sock *sk; /* back pointer to sk object */
u32 sid; /* SID of this object */
u32 peer_sid; /* SID of peer */
#ifdef CONFIG_NETLABEL
u16 sclass; /* sock security class */
#ifdef CONFIG_NETLABEL
enum { /* NetLabel state */
NLBL_UNSET = 0,
NLBL_REQUIRE,
......
......@@ -25,13 +25,14 @@
#define POLICYDB_VERSION_MLS 19
#define POLICYDB_VERSION_AVTAB 20
#define POLICYDB_VERSION_RANGETRANS 21
#define POLICYDB_VERSION_POLCAP 22
/* Range of policy versions we understand*/
#define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE
#ifdef CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX
#define POLICYDB_VERSION_MAX CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE
#else
#define POLICYDB_VERSION_MAX POLICYDB_VERSION_RANGETRANS
#define POLICYDB_VERSION_MAX POLICYDB_VERSION_POLCAP
#endif
struct netlbl_lsm_secattr;
......@@ -39,8 +40,19 @@ struct netlbl_lsm_secattr;
extern int selinux_enabled;
extern int selinux_mls_enabled;
/* Policy capabilities */
enum {
POLICYDB_CAPABILITY_NETPEER,
__POLICYDB_CAPABILITY_MAX
};
#define POLICYDB_CAPABILITY_MAX (__POLICYDB_CAPABILITY_MAX - 1)
extern int selinux_policycap_netpeer;
int security_load_policy(void * data, size_t len);
int security_policycap_supported(unsigned int req_cap);
#define SEL_VEC_MAX 32
struct av_decision {
u32 allowed;
......@@ -77,8 +89,7 @@ int security_get_user_sids(u32 callsid, char *username,
int security_port_sid(u16 domain, u16 type, u8 protocol, u16 port,
u32 *out_sid);
int security_netif_sid(char *name, u32 *if_sid,
u32 *msg_sid);
int security_netif_sid(char *name, u32 *if_sid);
int security_node_sid(u16 domain, void *addr, u32 addrlen,
u32 *out_sid);
......@@ -88,10 +99,15 @@ int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid,
int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid);
int security_net_peersid_resolve(u32 nlbl_sid, u32 nlbl_type,
u32 xfrm_sid,
u32 *peer_sid);
int security_get_classes(char ***classes, int *nclasses);
int security_get_permissions(char *class, char ***perms, int *nperms);
int security_get_reject_unknown(void);
int security_get_allow_unknown(void);
int security_get_policycaps(int *len, int **values);
#define SECURITY_FS_USE_XATTR 1 /* use xattr */
#define SECURITY_FS_USE_TRANS 2 /* use transition SIDs, e.g. devpts/tmpfs */
......@@ -108,7 +124,6 @@ int security_genfs_sid(const char *fstype, char *name, u16 sclass,
#ifdef CONFIG_NETLABEL
int security_netlbl_secattr_to_sid(struct netlbl_lsm_secattr *secattr,
u32 base_sid,
u32 *sid);
int security_netlbl_sid_to_secattr(u32 sid,
......@@ -116,7 +131,6 @@ int security_netlbl_sid_to_secattr(u32 sid,
#else
static inline int security_netlbl_secattr_to_sid(
struct netlbl_lsm_secattr *secattr,
u32 base_sid,
u32 *sid)
{
return -EIDRM;
......
......@@ -32,6 +32,13 @@ static inline struct inode_security_struct *get_sock_isec(struct sock *sk)
}
#ifdef CONFIG_SECURITY_NETWORK_XFRM
extern atomic_t selinux_xfrm_refcount;
static inline int selinux_xfrm_enabled(void)
{
return (atomic_read(&selinux_xfrm_refcount) > 0);
}
int selinux_xfrm_sock_rcv_skb(u32 sid, struct sk_buff *skb,
struct avc_audit_data *ad);
int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb,
......@@ -43,6 +50,11 @@ static inline void selinux_xfrm_notify_policyload(void)
atomic_inc(&flow_cache_genid);
}
#else
static inline int selinux_xfrm_enabled(void)
{
return 0;
}
static inline int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb,
struct avc_audit_data *ad)
{
......
......@@ -7,6 +7,8 @@
* Author: James Morris <jmorris@redhat.com>
*
* Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com>
* Copyright (C) 2007 Hewlett-Packard Development Company, L.P.
* Paul Moore <paul.moore@hp.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
......@@ -29,14 +31,6 @@
#define SEL_NETIF_HASH_SIZE 64
#define SEL_NETIF_HASH_MAX 1024
#undef DEBUG
#ifdef DEBUG
#define DEBUGP printk
#else
#define DEBUGP(format, args...)
#endif
struct sel_netif
{
struct list_head list;
......@@ -49,174 +43,226 @@ static LIST_HEAD(sel_netif_list);
static DEFINE_SPINLOCK(sel_netif_lock);
static struct list_head sel_netif_hash[SEL_NETIF_HASH_SIZE];
static inline u32 sel_netif_hasfn(struct net_device *dev)
/**
* sel_netif_hashfn - Hashing function for the interface table
* @ifindex: the network interface
*
* Description:
* This is the hashing function for the network interface table, it returns the
* bucket number for the given interface.
*
*/
static inline u32 sel_netif_hashfn(int ifindex)
{
return (dev->ifindex & (SEL_NETIF_HASH_SIZE - 1));
return (ifindex & (SEL_NETIF_HASH_SIZE - 1));
}
/*
* All of the devices should normally fit in the hash, so we optimize
* for that case.
/**
* sel_netif_find - Search for an interface record
* @ifindex: the network interface
*
* Description:
* Search the network interface table and return the record matching @ifindex.
* If an entry can not be found in the table return NULL.
*
*/
static inline struct sel_netif *sel_netif_find(struct net_device *dev)
static inline struct sel_netif *sel_netif_find(int ifindex)
{
struct list_head *pos;
int idx = sel_netif_hasfn(dev);
int idx = sel_netif_hashfn(ifindex);
struct sel_netif *netif;
__list_for_each_rcu(pos, &sel_netif_hash[idx]) {
struct sel_netif *netif = list_entry(pos,
struct sel_netif, list);
if (likely(netif->nsec.dev == dev))
list_for_each_entry_rcu(netif, &sel_netif_hash[idx], list)
/* all of the devices should normally fit in the hash, so we
* optimize for that case */
if (likely(netif->nsec.ifindex == ifindex))
return netif;
}
return NULL;
}
/**
* sel_netif_insert - Insert a new interface into the table
* @netif: the new interface record
*
* Description:
* Add a new interface record to the network interface hash table. Returns
* zero on success, negative values on failure.
*
*/
static int sel_netif_insert(struct sel_netif *netif)
{
int idx, ret = 0;
int idx;
if (sel_netif_total >= SEL_NETIF_HASH_MAX) {
ret = -ENOSPC;
goto out;
}
if (sel_netif_total >= SEL_NETIF_HASH_MAX)
return -ENOSPC;
idx = sel_netif_hasfn(netif->nsec.dev);
idx = sel_netif_hashfn(netif->nsec.ifindex);
list_add_rcu(&netif->list, &sel_netif_hash[idx]);
sel_netif_total++;
out:
return ret;
return 0;
}
/**
* sel_netif_free - Frees an interface entry
* @p: the entry's RCU field
*
* Description:
* This function is designed to be used as a callback to the call_rcu()
* function so that memory allocated to a hash table interface entry can be
* released safely.
*
*/
static void sel_netif_free(struct rcu_head *p)
{
struct sel_netif *netif = container_of(p, struct sel_netif, rcu_head);
DEBUGP("%s: %s\n", __FUNCTION__, netif->nsec.dev->name);
kfree(netif);
}
/**
* sel_netif_destroy - Remove an interface record from the table
* @netif: the existing interface record
*
* Description:
* Remove an existing interface record from the network interface table.
*
*/
static void sel_netif_destroy(struct sel_netif *netif)
{
DEBUGP("%s: %s\n", __FUNCTION__, netif->nsec.dev->name);
list_del_rcu(&netif->list);
sel_netif_total--;
call_rcu(&netif->rcu_head, sel_netif_free);
}
static struct sel_netif *sel_netif_lookup(struct net_device *dev)
/**
* sel_netif_sid_slow - Lookup the SID of a network interface using the policy
* @ifindex: the network interface
* @sid: interface SID
*
* Description:
* This function determines the SID of a network interface by quering the
* security policy. The result is added to the network interface table to
* speedup future queries. Returns zero on success, negative values on
* failure.
*
*/
static int sel_netif_sid_slow(int ifindex, u32 *sid)
{
int ret;
struct sel_netif *netif, *new;
struct netif_security_struct *nsec;
netif = sel_netif_find(dev);
if (likely(netif != NULL))
goto out;
new = kzalloc(sizeof(*new), GFP_ATOMIC);
if (!new) {
netif = ERR_PTR(-ENOMEM);
goto out;
struct sel_netif *netif;
struct sel_netif *new = NULL;
struct net_device *dev;
/* NOTE: we always use init's network namespace since we don't
* currently support containers */
dev = dev_get_by_index(&init_net, ifindex);
if (unlikely(dev == NULL)) {
printk(KERN_WARNING
"SELinux: failure in sel_netif_sid_slow(),"
" invalid network interface (%d)\n", ifindex);
return -ENOENT;
}
nsec = &new->nsec;
ret = security_netif_sid(dev->name, &nsec->if_sid, &nsec->msg_sid);
if (ret < 0) {
kfree(new);
netif = ERR_PTR(ret);
spin_lock_bh(&sel_netif_lock);
netif = sel_netif_find(ifindex);
if (netif != NULL) {
*sid = netif->nsec.sid;
ret = 0;
goto out;
}
nsec->dev = dev;
spin_lock_bh(&sel_netif_lock);
netif = sel_netif_find(dev);
if (netif) {
spin_unlock_bh(&sel_netif_lock);
kfree(new);
new = kzalloc(sizeof(*new), GFP_ATOMIC);
if (new == NULL) {
ret = -ENOMEM;
goto out;
}
ret = security_netif_sid(dev->name, &new->nsec.sid);
if (ret != 0)
goto out;
new->nsec.ifindex = ifindex;
ret = sel_netif_insert(new);
spin_unlock_bh(&sel_netif_lock);
if (ret) {
kfree(new);
netif = ERR_PTR(ret);
if (ret != 0)
goto out;
}
*sid = new->nsec.sid;
netif = new;
DEBUGP("new: ifindex=%u name=%s if_sid=%u msg_sid=%u\n", dev->ifindex, dev->name,
nsec->if_sid, nsec->msg_sid);
out:
return netif;
}
static void sel_netif_assign_sids(u32 if_sid_in, u32 msg_sid_in, u32 *if_sid_out, u32 *msg_sid_out)
{
if (if_sid_out)
*if_sid_out = if_sid_in;
if (msg_sid_out)
*msg_sid_out = msg_sid_in;
}
static int sel_netif_sids_slow(struct net_device *dev, u32 *if_sid, u32 *msg_sid)
{
int ret = 0;
u32 tmp_if_sid, tmp_msg_sid;
ret = security_netif_sid(dev->name, &tmp_if_sid, &tmp_msg_sid);
if (!ret)
sel_netif_assign_sids(tmp_if_sid, tmp_msg_sid, if_sid, msg_sid);
spin_unlock_bh(&sel_netif_lock);
dev_put(dev);
if (unlikely(ret)) {
printk(KERN_WARNING
"SELinux: failure in sel_netif_sid_slow(),"
" unable to determine network interface label (%d)\n",
ifindex);
kfree(new);
}
return ret;
}
int sel_netif_sids(struct net_device *dev, u32 *if_sid, u32 *msg_sid)
/**
* sel_netif_sid - Lookup the SID of a network interface
* @ifindex: the network interface
* @sid: interface SID
*
* Description:
* This function determines the SID of a network interface using the fastest
* method possible. First the interface table is queried, but if an entry
* can't be found then the policy is queried and the result is added to the
* table to speedup future queries. Returns zero on success, negative values
* on failure.
*
*/
int sel_netif_sid(int ifindex, u32 *sid)
{
int ret = 0;
struct sel_netif *netif;
rcu_read_lock();
netif = sel_netif_lookup(dev);
if (IS_ERR(netif)) {
netif = sel_netif_find(ifindex);
if (likely(netif != NULL)) {
*sid = netif->nsec.sid;
rcu_read_unlock();
ret = sel_netif_sids_slow(dev, if_sid, msg_sid);
goto out;
return 0;
}
sel_netif_assign_sids(netif->nsec.if_sid, netif->nsec.msg_sid, if_sid, msg_sid);
rcu_read_unlock();
out:
return ret;
return sel_netif_sid_slow(ifindex, sid);
}
static void sel_netif_kill(struct net_device *dev)
/**
* sel_netif_kill - Remove an entry from the network interface table
* @ifindex: the network interface
*
* Description:
* This function removes the entry matching @ifindex from the network interface
* table if it exists.
*
*/
static void sel_netif_kill(int ifindex)
{
struct sel_netif *netif;
spin_lock_bh(&sel_netif_lock);
netif = sel_netif_find(dev);
netif = sel_netif_find(ifindex);
if (netif)
sel_netif_destroy(netif);
spin_unlock_bh(&sel_netif_lock);
}
/**
* sel_netif_flush - Flush the entire network interface table
*
* Description:
* Remove all entries from the network interface table.
*
*/
static void sel_netif_flush(void)
{
int idx;
struct sel_netif *netif;
spin_lock_bh(&sel_netif_lock);
for (idx = 0; idx < SEL_NETIF_HASH_SIZE; idx++) {
struct sel_netif *netif;
for (idx = 0; idx < SEL_NETIF_HASH_SIZE; idx++)
list_for_each_entry(netif, &sel_netif_hash[idx], list)
sel_netif_destroy(netif);
}
spin_unlock_bh(&sel_netif_lock);
}
......@@ -239,7 +285,7 @@ static int sel_netif_netdev_notifier_handler(struct notifier_block *this,
return NOTIFY_DONE;
if (event == NETDEV_DOWN)
sel_netif_kill(dev);
sel_netif_kill(dev->ifindex);
return NOTIFY_DONE;
}
......@@ -250,10 +296,10 @@ static struct notifier_block sel_netif_netdev_notifier = {
static __init int sel_netif_init(void)
{
int i, err = 0;
int i, err;
if (!selinux_enabled)
goto out;
return 0;
for (i = 0; i < SEL_NETIF_HASH_SIZE; i++)
INIT_LIST_HEAD(&sel_netif_hash[i]);
......@@ -265,7 +311,6 @@ static __init int sel_netif_init(void)
if (err)
panic("avc_add_callback() failed, error %d\n", err);
out:
return err;
}
......
......@@ -35,6 +35,33 @@
#include "objsec.h"
#include "security.h"
/**
* selinux_netlbl_sidlookup_cached - Cache a SID lookup
* @skb: the packet
* @secattr: the NetLabel security attributes
* @sid: the SID
*
* Description:
* Query the SELinux security server to lookup the correct SID for the given
* security attributes. If the query is successful, cache the result to speed
* up future lookups. Returns zero on success, negative values on failure.
*
*/
static int selinux_netlbl_sidlookup_cached(struct sk_buff *skb,
struct netlbl_lsm_secattr *secattr,
u32 *sid)
{
int rc;
rc = security_netlbl_secattr_to_sid(secattr, sid);
if (rc == 0 &&
(secattr->flags & NETLBL_SECATTR_CACHEABLE) &&
(secattr->flags & NETLBL_SECATTR_CACHE))
netlbl_cache_add(skb, secattr);
return rc;
}
/**
* selinux_netlbl_sock_setsid - Label a socket using the NetLabel mechanism
* @sk: the socket to label
......@@ -137,14 +164,14 @@ void selinux_netlbl_sk_security_clone(struct sk_security_struct *ssec,
* lock as other threads could have access to ssec */
rcu_read_lock();
selinux_netlbl_sk_security_reset(newssec, ssec->sk->sk_family);
newssec->sclass = ssec->sclass;
rcu_read_unlock();
}
/**
* selinux_netlbl_skbuff_getsid - Get the sid of a packet using NetLabel
* @skb: the packet
* @base_sid: the SELinux SID to use as a context for MLS only attributes
* @family: protocol family
* @type: NetLabel labeling protocol type
* @sid: the SID
*
* Description:
......@@ -153,7 +180,10 @@ void selinux_netlbl_sk_security_clone(struct sk_security_struct *ssec,
* assign to the packet. Returns zero on success, negative values on failure.
*
*/
int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, u32 base_sid, u32 *sid)
int selinux_netlbl_skbuff_getsid(struct sk_buff *skb,
u16 family,
u32 *type,
u32 *sid)
{
int rc;
struct netlbl_lsm_secattr secattr;
......@@ -164,15 +194,12 @@ int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, u32 base_sid, u32 *sid)
}
netlbl_secattr_init(&secattr);
rc = netlbl_skbuff_getattr(skb, &secattr);
if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE) {
rc = security_netlbl_secattr_to_sid(&secattr, base_sid, sid);
if (rc == 0 &&
(secattr.flags & NETLBL_SECATTR_CACHEABLE) &&
(secattr.flags & NETLBL_SECATTR_CACHE))
netlbl_cache_add(skb, &secattr);
} else
rc = netlbl_skbuff_getattr(skb, family, &secattr);
if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE)
rc = selinux_netlbl_sidlookup_cached(skb, &secattr, sid);
else
*sid = SECSID_NULL;
*type = secattr.type;
netlbl_secattr_destroy(&secattr);
return rc;
......@@ -190,13 +217,10 @@ int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, u32 base_sid, u32 *sid)
*/
void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock)
{
struct inode_security_struct *isec = SOCK_INODE(sock)->i_security;
struct sk_security_struct *sksec = sk->sk_security;
struct netlbl_lsm_secattr secattr;
u32 nlbl_peer_sid;
sksec->sclass = isec->sclass;
rcu_read_lock();
if (sksec->nlbl_state != NLBL_REQUIRE) {
......@@ -207,9 +231,7 @@ void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock)
netlbl_secattr_init(&secattr);
if (netlbl_sock_getattr(sk, &secattr) == 0 &&
secattr.flags != NETLBL_SECATTR_NONE &&
security_netlbl_secattr_to_sid(&secattr,
SECINITSID_NETMSG,
&nlbl_peer_sid) == 0)
security_netlbl_secattr_to_sid(&secattr, &nlbl_peer_sid) == 0)
sksec->peer_sid = nlbl_peer_sid;
netlbl_secattr_destroy(&secattr);
......@@ -234,11 +256,8 @@ int selinux_netlbl_socket_post_create(struct socket *sock)
{
int rc = 0;
struct sock *sk = sock->sk;
struct inode_security_struct *isec = SOCK_INODE(sock)->i_security;
struct sk_security_struct *sksec = sk->sk_security;
sksec->sclass = isec->sclass;
rcu_read_lock();
if (sksec->nlbl_state == NLBL_REQUIRE)
rc = selinux_netlbl_sock_setsid(sk, sksec->sid);
......@@ -292,6 +311,7 @@ int selinux_netlbl_inode_permission(struct inode *inode, int mask)
* selinux_netlbl_sock_rcv_skb - Do an inbound access check using NetLabel
* @sksec: the sock's sk_security_struct
* @skb: the packet
* @family: protocol family
* @ad: the audit data
*
* Description:
......@@ -302,6 +322,7 @@ int selinux_netlbl_inode_permission(struct inode *inode, int mask)
*/
int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
struct sk_buff *skb,
u16 family,
struct avc_audit_data *ad)
{
int rc;
......@@ -313,16 +334,10 @@ int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
return 0;
netlbl_secattr_init(&secattr);
rc = netlbl_skbuff_getattr(skb, &secattr);
if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE) {
rc = security_netlbl_secattr_to_sid(&secattr,
SECINITSID_NETMSG,
&nlbl_sid);
if (rc == 0 &&
(secattr.flags & NETLBL_SECATTR_CACHEABLE) &&
(secattr.flags & NETLBL_SECATTR_CACHE))
netlbl_cache_add(skb, &secattr);
} else
rc = netlbl_skbuff_getattr(skb, family, &secattr);
if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE)
rc = selinux_netlbl_sidlookup_cached(skb, &secattr, &nlbl_sid);
else
nlbl_sid = SECINITSID_UNLABELED;
netlbl_secattr_destroy(&secattr);
if (rc != 0)
......
/*
* Network node table
*
* SELinux must keep a mapping of network nodes to labels/SIDs. This
* mapping is maintained as part of the normal policy but a fast cache is
* needed to reduce the lookup overhead since most of these queries happen on
* a per-packet basis.
*
* Author: Paul Moore <paul.moore@hp.com>
*
* This code is heavily based on the "netif" concept originally developed by
* James Morris <jmorris@redhat.com>
* (see security/selinux/netif.c for more information)
*
*/
/*
* (c) Copyright Hewlett-Packard Development Company, L.P., 2007
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/types.h>
#include <linux/rcupdate.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/in.h>
#include <linux/in6.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <net/ip.h>
#include <net/ipv6.h>
#include <asm/bug.h>
#include "objsec.h"
#define SEL_NETNODE_HASH_SIZE 256
#define SEL_NETNODE_HASH_BKT_LIMIT 16
struct sel_netnode {
struct netnode_security_struct nsec;
struct list_head list;
struct rcu_head rcu;
};
/* NOTE: we are using a combined hash table for both IPv4 and IPv6, the reason
* for this is that I suspect most users will not make heavy use of both
* address families at the same time so one table will usually end up wasted,
* if this becomes a problem we can always add a hash table for each address
* family later */
static LIST_HEAD(sel_netnode_list);
static DEFINE_SPINLOCK(sel_netnode_lock);
static struct list_head sel_netnode_hash[SEL_NETNODE_HASH_SIZE];
/**
* sel_netnode_free - Frees a node entry
* @p: the entry's RCU field
*
* Description:
* This function is designed to be used as a callback to the call_rcu()
* function so that memory allocated to a hash table node entry can be
* released safely.
*
*/
static void sel_netnode_free(struct rcu_head *p)
{
struct sel_netnode *node = container_of(p, struct sel_netnode, rcu);
kfree(node);
}
/**
* sel_netnode_hashfn_ipv4 - IPv4 hashing function for the node table
* @addr: IPv4 address
*
* Description:
* This is the IPv4 hashing function for the node interface table, it returns
* the bucket number for the given IP address.
*
*/
static u32 sel_netnode_hashfn_ipv4(__be32 addr)
{
/* at some point we should determine if the mismatch in byte order
* affects the hash function dramatically */
return (addr & (SEL_NETNODE_HASH_SIZE - 1));
}
/**
* sel_netnode_hashfn_ipv6 - IPv6 hashing function for the node table
* @addr: IPv6 address
*
* Description:
* This is the IPv6 hashing function for the node interface table, it returns
* the bucket number for the given IP address.
*
*/
static u32 sel_netnode_hashfn_ipv6(const struct in6_addr *addr)
{
/* just hash the least significant 32 bits to keep things fast (they
* are the most likely to be different anyway), we can revisit this
* later if needed */
return (addr->s6_addr32[3] & (SEL_NETNODE_HASH_SIZE - 1));
}
/**
* sel_netnode_find - Search for a node record
* @addr: IP address
* @family: address family
*
* Description:
* Search the network node table and return the record matching @addr. If an
* entry can not be found in the table return NULL.
*
*/
static struct sel_netnode *sel_netnode_find(const void *addr, u16 family)
{
u32 idx;
struct sel_netnode *node;
switch (family) {
case PF_INET:
idx = sel_netnode_hashfn_ipv4(*(__be32 *)addr);
break;
case PF_INET6:
idx = sel_netnode_hashfn_ipv6(addr);
break;
default:
BUG();
}
list_for_each_entry_rcu(node, &sel_netnode_hash[idx], list)
if (node->nsec.family == family)
switch (family) {
case PF_INET:
if (node->nsec.addr.ipv4 == *(__be32 *)addr)
return node;
break;
case PF_INET6:
if (ipv6_addr_equal(&node->nsec.addr.ipv6,
addr))
return node;
break;
}
return NULL;
}
/**
* sel_netnode_insert - Insert a new node into the table
* @node: the new node record
*
* Description:
* Add a new node record to the network address hash table. Returns zero on
* success, negative values on failure.
*
*/
static int sel_netnode_insert(struct sel_netnode *node)
{
u32 idx;
u32 count = 0;
struct sel_netnode *iter;
switch (node->nsec.family) {
case PF_INET:
idx = sel_netnode_hashfn_ipv4(node->nsec.addr.ipv4);
break;
case PF_INET6:
idx = sel_netnode_hashfn_ipv6(&node->nsec.addr.ipv6);
break;
default:
BUG();
}
list_add_rcu(&node->list, &sel_netnode_hash[idx]);
/* we need to impose a limit on the growth of the hash table so check
* this bucket to make sure it is within the specified bounds */
list_for_each_entry(iter, &sel_netnode_hash[idx], list)
if (++count > SEL_NETNODE_HASH_BKT_LIMIT) {
list_del_rcu(&iter->list);
call_rcu(&iter->rcu, sel_netnode_free);
break;
}
return 0;
}
/**
* sel_netnode_destroy - Remove a node record from the table
* @node: the existing node record
*
* Description:
* Remove an existing node record from the network address table.
*
*/
static void sel_netnode_destroy(struct sel_netnode *node)
{
list_del_rcu(&node->list);
call_rcu(&node->rcu, sel_netnode_free);
}
/**
* sel_netnode_sid_slow - Lookup the SID of a network address using the policy
* @addr: the IP address
* @family: the address family
* @sid: node SID
*
* Description:
* This function determines the SID of a network address by quering the
* security policy. The result is added to the network address table to
* speedup future queries. Returns zero on success, negative values on
* failure.
*
*/
static int sel_netnode_sid_slow(void *addr, u16 family, u32 *sid)
{
int ret;
struct sel_netnode *node;
struct sel_netnode *new = NULL;
spin_lock_bh(&sel_netnode_lock);
node = sel_netnode_find(addr, family);
if (node != NULL) {
*sid = node->nsec.sid;
ret = 0;
goto out;
}
new = kzalloc(sizeof(*new), GFP_ATOMIC);
if (new == NULL) {
ret = -ENOMEM;
goto out;
}
switch (family) {
case PF_INET:
ret = security_node_sid(PF_INET,
addr, sizeof(struct in_addr),
&new->nsec.sid);
new->nsec.addr.ipv4 = *(__be32 *)addr;
break;
case PF_INET6:
ret = security_node_sid(PF_INET6,
addr, sizeof(struct in6_addr),
&new->nsec.sid);
ipv6_addr_copy(&new->nsec.addr.ipv6, addr);
break;
default:
BUG();
}
if (ret != 0)
goto out;
new->nsec.family = family;
ret = sel_netnode_insert(new);
if (ret != 0)
goto out;
*sid = new->nsec.sid;
out:
spin_unlock_bh(&sel_netnode_lock);
if (unlikely(ret)) {
printk(KERN_WARNING
"SELinux: failure in sel_netnode_sid_slow(),"
" unable to determine network node label\n");
kfree(new);
}
return ret;
}
/**
* sel_netnode_sid - Lookup the SID of a network address
* @addr: the IP address
* @family: the address family
* @sid: node SID
*
* Description:
* This function determines the SID of a network address using the fastest
* method possible. First the address table is queried, but if an entry
* can't be found then the policy is queried and the result is added to the
* table to speedup future queries. Returns zero on success, negative values
* on failure.
*
*/
int sel_netnode_sid(void *addr, u16 family, u32 *sid)
{
struct sel_netnode *node;
rcu_read_lock();
node = sel_netnode_find(addr, family);
if (node != NULL) {
*sid = node->nsec.sid;
rcu_read_unlock();
return 0;
}
rcu_read_unlock();
return sel_netnode_sid_slow(addr, family, sid);
}
/**
* sel_netnode_flush - Flush the entire network address table
*
* Description:
* Remove all entries from the network address table.
*
*/
static void sel_netnode_flush(void)
{
u32 idx;
struct sel_netnode *node;
spin_lock_bh(&sel_netnode_lock);
for (idx = 0; idx < SEL_NETNODE_HASH_SIZE; idx++)
list_for_each_entry(node, &sel_netnode_hash[idx], list)
sel_netnode_destroy(node);
spin_unlock_bh(&sel_netnode_lock);
}
static int sel_netnode_avc_callback(u32 event, u32 ssid, u32 tsid,
u16 class, u32 perms, u32 *retained)
{
if (event == AVC_CALLBACK_RESET) {
sel_netnode_flush();
synchronize_net();
}
return 0;
}
static __init int sel_netnode_init(void)
{
int iter;
int ret;
if (!selinux_enabled)
return 0;
for (iter = 0; iter < SEL_NETNODE_HASH_SIZE; iter++)
INIT_LIST_HEAD(&sel_netnode_hash[iter]);
ret = avc_add_callback(sel_netnode_avc_callback, AVC_CALLBACK_RESET,
SECSID_NULL, SECSID_NULL, SECCLASS_NULL, 0);
if (ret != 0)
panic("avc_add_callback() failed, error %d\n", ret);
return ret;
}
__initcall(sel_netnode_init);
......@@ -2,6 +2,11 @@
*
* Added conditional policy language extensions
*
* Updated: Hewlett-Packard <paul.moore@hp.com>
*
* Added support for the policy capability bitmap
*
* Copyright (C) 2007 Hewlett-Packard Development Company, L.P.
* Copyright (C) 2003 - 2004 Tresys Technology, LLC
* Copyright (C) 2004 Red Hat, Inc., James Morris <jmorris@redhat.com>
* This program is free software; you can redistribute it and/or modify
......@@ -35,6 +40,11 @@
#include "objsec.h"
#include "conditional.h"
/* Policy capability filenames */
static char *policycap_names[] = {
"network_peer_controls"
};
unsigned int selinux_checkreqprot = CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE;
#ifdef CONFIG_SECURITY_SELINUX_ENABLE_SECMARK_DEFAULT
......@@ -72,6 +82,9 @@ static int *bool_pending_values = NULL;
static struct dentry *class_dir = NULL;
static unsigned long last_class_ino;
/* global data for policy capabilities */
static struct dentry *policycap_dir = NULL;
extern void selnl_notify_setenforce(int val);
/* Check whether a task is allowed to use a security operation. */
......@@ -111,10 +124,11 @@ enum sel_inos {
static unsigned long sel_last_ino = SEL_INO_NEXT - 1;
#define SEL_INITCON_INO_OFFSET 0x01000000
#define SEL_BOOL_INO_OFFSET 0x02000000
#define SEL_CLASS_INO_OFFSET 0x04000000
#define SEL_INO_MASK 0x00ffffff
#define SEL_INITCON_INO_OFFSET 0x01000000
#define SEL_BOOL_INO_OFFSET 0x02000000
#define SEL_CLASS_INO_OFFSET 0x04000000
#define SEL_POLICYCAP_INO_OFFSET 0x08000000
#define SEL_INO_MASK 0x00ffffff
#define TMPBUFLEN 12
static ssize_t sel_read_enforce(struct file *filp, char __user *buf,
......@@ -263,6 +277,7 @@ static const struct file_operations sel_policyvers_ops = {
/* declaration for sel_write_load */
static int sel_make_bools(void);
static int sel_make_classes(void);
static int sel_make_policycap(void);
/* declaration for sel_make_class_dirs */
static int sel_make_dir(struct inode *dir, struct dentry *dentry,
......@@ -323,6 +338,12 @@ static ssize_t sel_write_load(struct file * file, const char __user * buf,
}
ret = sel_make_classes();
if (ret) {
length = ret;
goto out1;
}
ret = sel_make_policycap();
if (ret)
length = ret;
else
......@@ -1399,6 +1420,24 @@ static const struct file_operations sel_perm_ops = {
.read = sel_read_perm,
};
static ssize_t sel_read_policycap(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
int value;
char tmpbuf[TMPBUFLEN];
ssize_t length;
unsigned long i_ino = file->f_path.dentry->d_inode->i_ino;
value = security_policycap_supported(i_ino & SEL_INO_MASK);
length = scnprintf(tmpbuf, TMPBUFLEN, "%d", value);
return simple_read_from_buffer(buf, count, ppos, tmpbuf, length);
}
static const struct file_operations sel_policycap_ops = {
.read = sel_read_policycap,
};
static int sel_make_perm_files(char *objclass, int classvalue,
struct dentry *dir)
{
......@@ -1545,6 +1584,36 @@ out:
return rc;
}
static int sel_make_policycap(void)
{
unsigned int iter;
struct dentry *dentry = NULL;
struct inode *inode = NULL;
sel_remove_entries(policycap_dir);
for (iter = 0; iter <= POLICYDB_CAPABILITY_MAX; iter++) {
if (iter < ARRAY_SIZE(policycap_names))
dentry = d_alloc_name(policycap_dir,
policycap_names[iter]);
else
dentry = d_alloc_name(policycap_dir, "unknown");
if (dentry == NULL)
return -ENOMEM;
inode = sel_make_inode(policycap_dir->d_sb, S_IFREG | S_IRUGO);
if (inode == NULL)
return -ENOMEM;
inode->i_fop = &sel_policycap_ops;
inode->i_ino = iter | SEL_POLICYCAP_INO_OFFSET;
d_add(dentry, inode);
}
return 0;
}
static int sel_make_dir(struct inode *dir, struct dentry *dentry,
unsigned long *ino)
{
......@@ -1673,6 +1742,18 @@ static int sel_fill_super(struct super_block * sb, void * data, int silent)
class_dir = dentry;
dentry = d_alloc_name(sb->s_root, "policy_capabilities");
if (!dentry) {
ret = -ENOMEM;
goto err;
}
ret = sel_make_dir(root_inode, dentry, &sel_last_ino);
if (ret)
goto err;
policycap_dir = dentry;
out:
return ret;
err:
......
This diff is collapsed.
This diff is collapsed.
......@@ -241,6 +241,8 @@ struct policydb {
/* type -> attribute reverse mapping */
struct ebitmap *type_attr_map;
struct ebitmap policycaps;
unsigned int policyvers;
unsigned int reject_unknown : 1;
......
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment