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 @@ int netlbl_sock_setattr(struct sock *sk,
* @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 @@ int netlbl_sock_setattr(struct sock *sk,
*/
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
......@@ -10,7 +10,7 @@
*/
/*
* (c) Copyright Hewlett-Packard Development Company, L.P., 2006
* (c) Copyright Hewlett-Packard Development Company, L.P., 2006 - 2007
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
......@@ -36,22 +36,92 @@
#include <linux/string.h>
#include <linux/skbuff.h>
#include <linux/audit.h>
#include <linux/in.h>
#include <linux/in6.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/notifier.h>
#include <linux/netdevice.h>
#include <linux/security.h>
#include <net/sock.h>
#include <net/netlink.h>
#include <net/genetlink.h>
#include <net/ip.h>
#include <net/ipv6.h>
#include <net/net_namespace.h>
#include <net/netlabel.h>
#include <asm/bug.h>
#include <asm/atomic.h>
#include "netlabel_user.h"
#include "netlabel_domainhash.h"
#include "netlabel_unlabeled.h"
#include "netlabel_mgmt.h"
/* NOTE: at present we always use init's network namespace since we don't
* presently support different namespaces even though the majority of
* the functions in this file are "namespace safe" */
/* The unlabeled connection hash table which we use to map network interfaces
* and addresses of unlabeled packets to a user specified secid value for the
* LSM. The hash table is used to lookup the network interface entry
* (struct netlbl_unlhsh_iface) and then the interface entry is used to
* lookup an IP address match from an ordered list. If a network interface
* match can not be found in the hash table then the default entry
* (netlbl_unlhsh_def) is used. The IP address entry list
* (struct netlbl_unlhsh_addr) is ordered such that the entries with a
* larger netmask come first.
*/
struct netlbl_unlhsh_tbl {
struct list_head *tbl;
u32 size;
};
struct netlbl_unlhsh_addr4 {
__be32 addr;
__be32 mask;
u32 secid;
u32 valid;
struct list_head list;
struct rcu_head rcu;
};
struct netlbl_unlhsh_addr6 {
struct in6_addr addr;
struct in6_addr mask;
u32 secid;
u32 valid;
struct list_head list;
struct rcu_head rcu;
};
struct netlbl_unlhsh_iface {
int ifindex;
struct list_head addr4_list;
struct list_head addr6_list;
u32 valid;
struct list_head list;
struct rcu_head rcu;
};
/* Argument struct for netlbl_unlhsh_walk() */
struct netlbl_unlhsh_walk_arg {
struct netlink_callback *nl_cb;
struct sk_buff *skb;
u32 seq;
};
/* Unlabeled connection hash table */
/* updates should be so rare that having one spinlock for the entire
* hash table should be okay */
static DEFINE_SPINLOCK(netlbl_unlhsh_lock);
static struct netlbl_unlhsh_tbl *netlbl_unlhsh = NULL;
static struct netlbl_unlhsh_iface *netlbl_unlhsh_def = NULL;
/* Accept unlabeled packets flag */
static DEFINE_SPINLOCK(netlabel_unlabel_acceptflg_lock);
static u8 netlabel_unlabel_acceptflg = 0;
/* NetLabel Generic NETLINK CIPSOv4 family */
/* NetLabel Generic NETLINK unlabeled family */
static struct genl_family netlbl_unlabel_gnl_family = {
.id = GENL_ID_GENERATE,
.hdrsize = 0,
......@@ -60,14 +130,844 @@ static struct genl_family netlbl_unlabel_gnl_family = {
.maxattr = NLBL_UNLABEL_A_MAX,
};
/* NetLabel Netlink attribute policy */
static const struct nla_policy netlbl_unlabel_genl_policy[NLBL_UNLABEL_A_MAX + 1] = {
[NLBL_UNLABEL_A_ACPTFLG] = { .type = NLA_U8 },
};
/* NetLabel Netlink attribute policy */
static const struct nla_policy netlbl_unlabel_genl_policy[NLBL_UNLABEL_A_MAX + 1] = {
[NLBL_UNLABEL_A_ACPTFLG] = { .type = NLA_U8 },
[NLBL_UNLABEL_A_IPV6ADDR] = { .type = NLA_BINARY,
.len = sizeof(struct in6_addr) },
[NLBL_UNLABEL_A_IPV6MASK] = { .type = NLA_BINARY,
.len = sizeof(struct in6_addr) },
[NLBL_UNLABEL_A_IPV4ADDR] = { .type = NLA_BINARY,
.len = sizeof(struct in_addr) },
[NLBL_UNLABEL_A_IPV4MASK] = { .type = NLA_BINARY,
.len = sizeof(struct in_addr) },
[NLBL_UNLABEL_A_IFACE] = { .type = NLA_NUL_STRING,
.len = IFNAMSIZ - 1 },
[NLBL_UNLABEL_A_SECCTX] = { .type = NLA_BINARY }
};
/*
* Audit Helper Functions
*/
/**
* netlbl_unlabel_audit_addr4 - Audit an IPv4 address
* @audit_buf: audit buffer
* @dev: network interface
* @addr: IP address
* @mask: IP address mask
*
* Description:
* Write the IPv4 address and address mask, if necessary, to @audit_buf.
*
*/
static void netlbl_unlabel_audit_addr4(struct audit_buffer *audit_buf,
const char *dev,
__be32 addr, __be32 mask)
{
u32 mask_val = ntohl(mask);
if (dev != NULL)
audit_log_format(audit_buf, " netif=%s", dev);
audit_log_format(audit_buf, " src=" NIPQUAD_FMT, NIPQUAD(addr));
if (mask_val != 0xffffffff) {
u32 mask_len = 0;
while (mask_val > 0) {
mask_val <<= 1;
mask_len++;
}
audit_log_format(audit_buf, " src_prefixlen=%d", mask_len);
}
}
/**
* netlbl_unlabel_audit_addr6 - Audit an IPv6 address
* @audit_buf: audit buffer
* @dev: network interface
* @addr: IP address
* @mask: IP address mask
*
* Description:
* Write the IPv6 address and address mask, if necessary, to @audit_buf.
*
*/
static void netlbl_unlabel_audit_addr6(struct audit_buffer *audit_buf,
const char *dev,
const struct in6_addr *addr,
const struct in6_addr *mask)
{
if (dev != NULL)
audit_log_format(audit_buf, " netif=%s", dev);
audit_log_format(audit_buf, " src=" NIP6_FMT, NIP6(*addr));
if (ntohl(mask->s6_addr32[3]) != 0xffffffff) {
u32 mask_len = 0;
u32 mask_val;
int iter = -1;
while (ntohl(mask->s6_addr32[++iter]) == 0xffffffff)
mask_len += 32;
mask_val = ntohl(mask->s6_addr32[iter]);
while (mask_val > 0) {
mask_val <<= 1;
mask_len++;
}
audit_log_format(audit_buf, " src_prefixlen=%d", mask_len);
}
}
/*
* Unlabeled Connection Hash Table Functions
*/
/**
* netlbl_unlhsh_free_addr4 - Frees an IPv4 address entry from the hash table
* @entry: 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 address entry can be
* released safely.
*
*/
static void netlbl_unlhsh_free_addr4(struct rcu_head *entry)
{
struct netlbl_unlhsh_addr4 *ptr;
ptr = container_of(entry, struct netlbl_unlhsh_addr4, rcu);
kfree(ptr);
}
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
/**
* netlbl_unlhsh_free_addr6 - Frees an IPv6 address entry from the hash table
* @entry: 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 address entry can be
* released safely.
*
*/
static void netlbl_unlhsh_free_addr6(struct rcu_head *entry)
{
struct netlbl_unlhsh_addr6 *ptr;
ptr = container_of(entry, struct netlbl_unlhsh_addr6, rcu);
kfree(ptr);
}
#endif /* IPv6 */
/**
* netlbl_unlhsh_free_iface - Frees an interface entry from the hash table
* @entry: 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. It is important to note that this function does not free
* the IPv4 and IPv6 address lists contained as part of an interface entry. It
* is up to the rest of the code to make sure an interface entry is only freed
* once it's address lists are empty.
*
*/
static void netlbl_unlhsh_free_iface(struct rcu_head *entry)
{
struct netlbl_unlhsh_iface *iface;
struct netlbl_unlhsh_addr4 *iter4;
struct netlbl_unlhsh_addr4 *tmp4;
struct netlbl_unlhsh_addr6 *iter6;
struct netlbl_unlhsh_addr6 *tmp6;
iface = container_of(entry, struct netlbl_unlhsh_iface, rcu);
/* no need for locks here since we are the only one with access to this
* structure */
list_for_each_entry_safe(iter4, tmp4, &iface->addr4_list, list)
if (iter4->valid) {
list_del_rcu(&iter4->list);
kfree(iter4);
}
list_for_each_entry_safe(iter6, tmp6, &iface->addr6_list, list)
if (iter6->valid) {
list_del_rcu(&iter6->list);
kfree(iter6);
}
kfree(iface);
}
/**
* netlbl_unlhsh_hash - Hashing function for the hash table
* @ifindex: the network interface/device to hash
*
* Description:
* This is the hashing function for the unlabeled hash table, it returns the
* bucket number for the given device/interface. The caller is responsible for
* calling the rcu_read_[un]lock() functions.
*
*/
static u32 netlbl_unlhsh_hash(int ifindex)
{
/* this is taken _almost_ directly from
* security/selinux/netif.c:sel_netif_hasfn() as they do pretty much
* the same thing */
return ifindex & (rcu_dereference(netlbl_unlhsh)->size - 1);
}
/**
* netlbl_unlhsh_search_addr4 - Search for a matching IPv4 address entry
* @addr: IPv4 address
* @iface: the network interface entry
*
* Description:
* Searches the IPv4 address list of the network interface specified by @iface.
* If a matching address entry is found it is returned, otherwise NULL is
* returned. The caller is responsible for calling the rcu_read_[un]lock()
* functions.
*
*/
static struct netlbl_unlhsh_addr4 *netlbl_unlhsh_search_addr4(
__be32 addr,
const struct netlbl_unlhsh_iface *iface)
{
struct netlbl_unlhsh_addr4 *iter;
list_for_each_entry_rcu(iter, &iface->addr4_list, list)
if (iter->valid && (addr & iter->mask) == iter->addr)
return iter;
return NULL;
}
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
/**
* netlbl_unlhsh_search_addr6 - Search for a matching IPv6 address entry
* @addr: IPv6 address
* @iface: the network interface entry
*
* Description:
* Searches the IPv6 address list of the network interface specified by @iface.
* If a matching address entry is found it is returned, otherwise NULL is
* returned. The caller is responsible for calling the rcu_read_[un]lock()
* functions.
*
*/
static struct netlbl_unlhsh_addr6 *netlbl_unlhsh_search_addr6(
const struct in6_addr *addr,
const struct netlbl_unlhsh_iface *iface)
{
struct netlbl_unlhsh_addr6 *iter;
list_for_each_entry_rcu(iter, &iface->addr6_list, list)
if (iter->valid &&
ipv6_masked_addr_cmp(&iter->addr, &iter->mask, addr) == 0)
return iter;
return NULL;
}
#endif /* IPv6 */
/**
* netlbl_unlhsh_search_iface - Search for a matching interface entry
* @ifindex: the network interface
*
* Description:
* Searches the unlabeled connection hash table and returns a pointer to the
* interface entry which matches @ifindex, otherwise NULL is returned. The
* caller is responsible for calling the rcu_read_[un]lock() functions.
*
*/
static struct netlbl_unlhsh_iface *netlbl_unlhsh_search_iface(int ifindex)
{
u32 bkt;
struct netlbl_unlhsh_iface *iter;
bkt = netlbl_unlhsh_hash(ifindex);
list_for_each_entry_rcu(iter,
&rcu_dereference(netlbl_unlhsh)->tbl[bkt],
list)
if (iter->valid && iter->ifindex == ifindex)
return iter;
return NULL;
}
/**
* netlbl_unlhsh_search_iface_def - Search for a matching interface entry
* @ifindex: the network interface
*
* Description:
* Searches the unlabeled connection hash table and returns a pointer to the
* interface entry which matches @ifindex. If an exact match can not be found
* and there is a valid default entry, the default entry is returned, otherwise
* NULL is returned. The caller is responsible for calling the
* rcu_read_[un]lock() functions.
*
*/
static struct netlbl_unlhsh_iface *netlbl_unlhsh_search_iface_def(int ifindex)
{
struct netlbl_unlhsh_iface *entry;
entry = netlbl_unlhsh_search_iface(ifindex);
if (entry != NULL)
return entry;
entry = rcu_dereference(netlbl_unlhsh_def);
if (entry != NULL && entry->valid)
return entry;
return NULL;
}
/**
* netlbl_unlhsh_add_addr4 - Add a new IPv4 address entry to the hash table
* @iface: the associated interface entry
* @addr: IPv4 address in network byte order
* @mask: IPv4 address mask in network byte order
* @secid: LSM secid value for entry
*
* Description:
* Add a new address entry into the unlabeled connection hash table using the
* interface entry specified by @iface. On success zero is returned, otherwise
* a negative value is returned. The caller is responsible for calling the
* rcu_read_[un]lock() functions.
*
*/
static int netlbl_unlhsh_add_addr4(struct netlbl_unlhsh_iface *iface,
const struct in_addr *addr,
const struct in_addr *mask,
u32 secid)
{
struct netlbl_unlhsh_addr4 *entry;
struct netlbl_unlhsh_addr4 *iter;
entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
if (entry == NULL)
return -ENOMEM;
entry->addr = addr->s_addr & mask->s_addr;
entry->mask = mask->s_addr;
entry->secid = secid;
entry->valid = 1;
INIT_RCU_HEAD(&entry->rcu);
spin_lock(&netlbl_unlhsh_lock);
iter = netlbl_unlhsh_search_addr4(entry->addr, iface);
if (iter != NULL &&
iter->addr == addr->s_addr && iter->mask == mask->s_addr) {
spin_unlock(&netlbl_unlhsh_lock);
kfree(entry);
return -EEXIST;
}
/* in order to speed up address searches through the list (the common
* case) we need to keep the list in order based on the size of the
* address mask such that the entry with the widest mask (smallest
* numerical value) appears first in the list */
list_for_each_entry_rcu(iter, &iface->addr4_list, list)
if (iter->valid &&
ntohl(entry->mask) > ntohl(iter->mask)) {
__list_add_rcu(&entry->list,
iter->list.prev,
&iter->list);
spin_unlock(&netlbl_unlhsh_lock);
return 0;
}
list_add_tail_rcu(&entry->list, &iface->addr4_list);
spin_unlock(&netlbl_unlhsh_lock);
return 0;
}
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
/**
* netlbl_unlhsh_add_addr6 - Add a new IPv6 address entry to the hash table
* @iface: the associated interface entry
* @addr: IPv6 address in network byte order
* @mask: IPv6 address mask in network byte order
* @secid: LSM secid value for entry
*
* Description:
* Add a new address entry into the unlabeled connection hash table using the
* interface entry specified by @iface. On success zero is returned, otherwise
* a negative value is returned. The caller is responsible for calling the
* rcu_read_[un]lock() functions.
*
*/
static int netlbl_unlhsh_add_addr6(struct netlbl_unlhsh_iface *iface,
const struct in6_addr *addr,
const struct in6_addr *mask,
u32 secid)
{
struct netlbl_unlhsh_addr6 *entry;
struct netlbl_unlhsh_addr6 *iter;
entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
if (entry == NULL)
return -ENOMEM;
ipv6_addr_copy(&entry->addr, addr);
entry->addr.s6_addr32[0] &= mask->s6_addr32[0];
entry->addr.s6_addr32[1] &= mask->s6_addr32[1];
entry->addr.s6_addr32[2] &= mask->s6_addr32[2];
entry->addr.s6_addr32[3] &= mask->s6_addr32[3];
ipv6_addr_copy(&entry->mask, mask);
entry->secid = secid;
entry->valid = 1;
INIT_RCU_HEAD(&entry->rcu);
spin_lock(&netlbl_unlhsh_lock);
iter = netlbl_unlhsh_search_addr6(&entry->addr, iface);
if (iter != NULL &&
(ipv6_addr_equal(&iter->addr, addr) &&
ipv6_addr_equal(&iter->mask, mask))) {
spin_unlock(&netlbl_unlhsh_lock);
kfree(entry);
return -EEXIST;
}
/* in order to speed up address searches through the list (the common
* case) we need to keep the list in order based on the size of the
* address mask such that the entry with the widest mask (smallest
* numerical value) appears first in the list */
list_for_each_entry_rcu(iter, &iface->addr6_list, list)
if (iter->valid &&
ipv6_addr_cmp(&entry->mask, &iter->mask) > 0) {
__list_add_rcu(&entry->list,
iter->list.prev,
&iter->list);
spin_unlock(&netlbl_unlhsh_lock);
return 0;
}
list_add_tail_rcu(&entry->list, &iface->addr6_list);
spin_unlock(&netlbl_unlhsh_lock);
return 0;
}
#endif /* IPv6 */
/**
* netlbl_unlhsh_add_iface - Adds a new interface entry to the hash table
* @ifindex: network interface
*
* Description:
* Add a new, empty, interface entry into the unlabeled connection hash table.
* On success a pointer to the new interface entry is returned, on failure NULL
* is returned. The caller is responsible for calling the rcu_read_[un]lock()
* functions.
*
*/
static struct netlbl_unlhsh_iface *netlbl_unlhsh_add_iface(int ifindex)
{
u32 bkt;
struct netlbl_unlhsh_iface *iface;
iface = kzalloc(sizeof(*iface), GFP_ATOMIC);
if (iface == NULL)
return NULL;
iface->ifindex = ifindex;
INIT_LIST_HEAD(&iface->addr4_list);
INIT_LIST_HEAD(&iface->addr6_list);
iface->valid = 1;
INIT_RCU_HEAD(&iface->rcu);
spin_lock(&netlbl_unlhsh_lock);
if (ifindex > 0) {
bkt = netlbl_unlhsh_hash(ifindex);
if (netlbl_unlhsh_search_iface(ifindex) != NULL)
goto add_iface_failure;
list_add_tail_rcu(&iface->list,
&rcu_dereference(netlbl_unlhsh)->tbl[bkt]);
} else {
INIT_LIST_HEAD(&iface->list);
if (rcu_dereference(netlbl_unlhsh_def) != NULL)
goto add_iface_failure;
rcu_assign_pointer(netlbl_unlhsh_def, iface);
}
spin_unlock(&netlbl_unlhsh_lock);
return iface;
add_iface_failure:
spin_unlock(&netlbl_unlhsh_lock);
kfree(iface);
return NULL;
}
/**
* netlbl_unlhsh_add - Adds a new entry to the unlabeled connection hash table
* @net: network namespace
* @dev_name: interface name
* @addr: IP address in network byte order
* @mask: address mask in network byte order
* @addr_len: length of address/mask (4 for IPv4, 16 for IPv6)
* @secid: LSM secid value for the entry
* @audit_info: NetLabel audit information
*
* Description:
* Adds a new entry to the unlabeled connection hash table. Returns zero on
* success, negative values on failure.
*
*/
static int netlbl_unlhsh_add(struct net *net,
const char *dev_name,
const void *addr,
const void *mask,
u32 addr_len,
u32 secid,
struct netlbl_audit *audit_info)
{
int ret_val;
int ifindex;
struct net_device *dev;
struct netlbl_unlhsh_iface *iface;
struct in_addr *addr4, *mask4;
struct in6_addr *addr6, *mask6;
struct audit_buffer *audit_buf = NULL;
char *secctx = NULL;
u32 secctx_len;
if (addr_len != sizeof(struct in_addr) &&
addr_len != sizeof(struct in6_addr))
return -EINVAL;
rcu_read_lock();
if (dev_name != NULL) {
dev = dev_get_by_name(net, dev_name);
if (dev == NULL) {
ret_val = -ENODEV;
goto unlhsh_add_return;
}
ifindex = dev->ifindex;
dev_put(dev);
iface = netlbl_unlhsh_search_iface(ifindex);
} else {
ifindex = 0;
iface = rcu_dereference(netlbl_unlhsh_def);
}
if (iface == NULL) {
iface = netlbl_unlhsh_add_iface(ifindex);
if (iface == NULL) {
ret_val = -ENOMEM;
goto unlhsh_add_return;
}
}
audit_buf = netlbl_audit_start_common(AUDIT_MAC_UNLBL_STCADD,
audit_info);
switch (addr_len) {
case sizeof(struct in_addr):
addr4 = (struct in_addr *)addr;
mask4 = (struct in_addr *)mask;
ret_val = netlbl_unlhsh_add_addr4(iface, addr4, mask4, secid);
if (audit_buf != NULL)
netlbl_unlabel_audit_addr4(audit_buf,
dev_name,
addr4->s_addr,
mask4->s_addr);
break;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
case sizeof(struct in6_addr):
addr6 = (struct in6_addr *)addr;
mask6 = (struct in6_addr *)mask;
ret_val = netlbl_unlhsh_add_addr6(iface, addr6, mask6, secid);
if (audit_buf != NULL)
netlbl_unlabel_audit_addr6(audit_buf,
dev_name,
addr6, mask6);
break;
#endif /* IPv6 */
default:
ret_val = -EINVAL;
}
if (ret_val == 0)
atomic_inc(&netlabel_mgmt_protocount);
unlhsh_add_return:
rcu_read_unlock();
if (audit_buf != NULL) {
if (security_secid_to_secctx(secid,
&secctx,
&secctx_len) == 0) {
audit_log_format(audit_buf, " sec_obj=%s", secctx);
security_release_secctx(secctx, secctx_len);
}
audit_log_format(audit_buf, " res=%u", ret_val == 0 ? 1 : 0);
audit_log_end(audit_buf);
}
return ret_val;
}
/**
* netlbl_unlhsh_remove_addr4 - Remove an IPv4 address entry
* @net: network namespace
* @iface: interface entry
* @addr: IP address
* @mask: IP address mask
* @audit_info: NetLabel audit information
*
* Description:
* Remove an IP address entry from the unlabeled connection hash table.
* Returns zero on success, negative values on failure. The caller is
* responsible for calling the rcu_read_[un]lock() functions.
*
*/
static int netlbl_unlhsh_remove_addr4(struct net *net,
struct netlbl_unlhsh_iface *iface,
const struct in_addr *addr,
const struct in_addr *mask,
struct netlbl_audit *audit_info)
{
int ret_val = -ENOENT;
struct netlbl_unlhsh_addr4 *entry;
struct audit_buffer *audit_buf = NULL;
struct net_device *dev;
char *secctx = NULL;
u32 secctx_len;
spin_lock(&netlbl_unlhsh_lock);
entry = netlbl_unlhsh_search_addr4(addr->s_addr, iface);
if (entry != NULL &&
entry->addr == addr->s_addr && entry->mask == mask->s_addr) {
entry->valid = 0;
list_del_rcu(&entry->list);
ret_val = 0;
}
spin_unlock(&netlbl_unlhsh_lock);
audit_buf = netlbl_audit_start_common(AUDIT_MAC_UNLBL_STCDEL,
audit_info);
if (audit_buf != NULL) {
dev = dev_get_by_index(net, iface->ifindex);
netlbl_unlabel_audit_addr4(audit_buf,
(dev != NULL ? dev->name : NULL),
entry->addr, entry->mask);
if (dev != NULL)
dev_put(dev);
if (security_secid_to_secctx(entry->secid,
&secctx,
&secctx_len) == 0) {
audit_log_format(audit_buf, " sec_obj=%s", secctx);
security_release_secctx(secctx, secctx_len);
}
audit_log_format(audit_buf, " res=%u", ret_val == 0 ? 1 : 0);
audit_log_end(audit_buf);
}
if (ret_val == 0)
call_rcu(&entry->rcu, netlbl_unlhsh_free_addr4);
return ret_val;
}
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
/**
* netlbl_unlhsh_remove_addr6 - Remove an IPv6 address entry
* @net: network namespace
* @iface: interface entry
* @addr: IP address
* @mask: IP address mask
* @audit_info: NetLabel audit information
*
* Description:
* Remove an IP address entry from the unlabeled connection hash table.
* Returns zero on success, negative values on failure. The caller is
* responsible for calling the rcu_read_[un]lock() functions.
*
*/
static int netlbl_unlhsh_remove_addr6(struct net *net,
struct netlbl_unlhsh_iface *iface,
const struct in6_addr *addr,
const struct in6_addr *mask,
struct netlbl_audit *audit_info)
{
int ret_val = -ENOENT;
struct netlbl_unlhsh_addr6 *entry;
struct audit_buffer *audit_buf = NULL;
struct net_device *dev;
char *secctx = NULL;
u32 secctx_len;
spin_lock(&netlbl_unlhsh_lock);
entry = netlbl_unlhsh_search_addr6(addr, iface);
if (entry != NULL &&
(ipv6_addr_equal(&entry->addr, addr) &&
ipv6_addr_equal(&entry->mask, mask))) {
entry->valid = 0;
list_del_rcu(&entry->list);
ret_val = 0;
}
spin_unlock(&netlbl_unlhsh_lock);
audit_buf = netlbl_audit_start_common(AUDIT_MAC_UNLBL_STCDEL,
audit_info);
if (audit_buf != NULL) {
dev = dev_get_by_index(net, iface->ifindex);
netlbl_unlabel_audit_addr6(audit_buf,
(dev != NULL ? dev->name : NULL),
addr, mask);
if (dev != NULL)
dev_put(dev);
if (security_secid_to_secctx(entry->secid,
&secctx,
&secctx_len) == 0) {
audit_log_format(audit_buf, " sec_obj=%s", secctx);
security_release_secctx(secctx, secctx_len);
}
audit_log_format(audit_buf, " res=%u", ret_val == 0 ? 1 : 0);
audit_log_end(audit_buf);
}
if (ret_val == 0)
call_rcu(&entry->rcu, netlbl_unlhsh_free_addr6);
return ret_val;
}
#endif /* IPv6 */
/**
* netlbl_unlhsh_condremove_iface - Remove an interface entry
* @iface: the interface entry
*
* Description:
* Remove an interface entry from the unlabeled connection hash table if it is
* empty. An interface entry is considered to be empty if there are no
* address entries assigned to it.
*
*/
static void netlbl_unlhsh_condremove_iface(struct netlbl_unlhsh_iface *iface)
{
struct netlbl_unlhsh_addr4 *iter4;
struct netlbl_unlhsh_addr6 *iter6;
spin_lock(&netlbl_unlhsh_lock);
list_for_each_entry_rcu(iter4, &iface->addr4_list, list)
if (iter4->valid)
goto unlhsh_condremove_failure;
list_for_each_entry_rcu(iter6, &iface->addr6_list, list)
if (iter6->valid)
goto unlhsh_condremove_failure;
iface->valid = 0;
if (iface->ifindex > 0)
list_del_rcu(&iface->list);
else
rcu_assign_pointer(netlbl_unlhsh_def, NULL);
spin_unlock(&netlbl_unlhsh_lock);
call_rcu(&iface->rcu, netlbl_unlhsh_free_iface);
return;
unlhsh_condremove_failure:
spin_unlock(&netlbl_unlhsh_lock);
return;
}
/**
* netlbl_unlhsh_remove - Remove an entry from the unlabeled hash table
* @net: network namespace
* @dev_name: interface name
* @addr: IP address in network byte order
* @mask: address mask in network byte order
* @addr_len: length of address/mask (4 for IPv4, 16 for IPv6)
* @audit_info: NetLabel audit information
*
* Description:
* Removes and existing entry from the unlabeled connection hash table.
* Returns zero on success, negative values on failure.
*
*/
static int netlbl_unlhsh_remove(struct net *net,
const char *dev_name,
const void *addr,
const void *mask,
u32 addr_len,
struct netlbl_audit *audit_info)
{
int ret_val;
struct net_device *dev;
struct netlbl_unlhsh_iface *iface;
if (addr_len != sizeof(struct in_addr) &&
addr_len != sizeof(struct in6_addr))
return -EINVAL;
rcu_read_lock();
if (dev_name != NULL) {
dev = dev_get_by_name(net, dev_name);
if (dev == NULL) {
ret_val = -ENODEV;
goto unlhsh_remove_return;
}
iface = netlbl_unlhsh_search_iface(dev->ifindex);
dev_put(dev);
} else
iface = rcu_dereference(netlbl_unlhsh_def);
if (iface == NULL) {
ret_val = -ENOENT;
goto unlhsh_remove_return;
}
switch (addr_len) {
case sizeof(struct in_addr):
ret_val = netlbl_unlhsh_remove_addr4(net,
iface, addr, mask,
audit_info);
break;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
case sizeof(struct in6_addr):
ret_val = netlbl_unlhsh_remove_addr6(net,
iface, addr, mask,
audit_info);
break;
#endif /* IPv6 */
default:
ret_val = -EINVAL;
}
if (ret_val == 0) {
netlbl_unlhsh_condremove_iface(iface);
atomic_dec(&netlabel_mgmt_protocount);
}
unlhsh_remove_return:
rcu_read_unlock();
return ret_val;
}
/*
* Helper Functions
* General Helper Functions
*/
/**
* netlbl_unlhsh_netdev_handler - Network device notification handler
* @this: notifier block
* @event: the event
* @ptr: the network device (cast to void)
*
* Description:
* Handle network device events, although at present all we care about is a
* network device going away. In the case of a device going away we clear any
* related entries from the unlabeled connection hash table.
*
*/
static int netlbl_unlhsh_netdev_handler(struct notifier_block *this,
unsigned long event,
void *ptr)
{
struct net_device *dev = ptr;
struct netlbl_unlhsh_iface *iface = NULL;
if (dev->nd_net != &init_net)
return NOTIFY_DONE;
/* XXX - should this be a check for NETDEV_DOWN or _UNREGISTER? */
if (event == NETDEV_DOWN) {
spin_lock(&netlbl_unlhsh_lock);
iface = netlbl_unlhsh_search_iface(dev->ifindex);
if (iface != NULL && iface->valid) {
iface->valid = 0;
list_del_rcu(&iface->list);
} else
iface = NULL;
spin_unlock(&netlbl_unlhsh_lock);
}
if (iface != NULL)
call_rcu(&iface->rcu, netlbl_unlhsh_free_iface);
return NOTIFY_DONE;
}
/**
* netlbl_unlabel_acceptflg_set - Set the unlabeled accept flag
......@@ -84,11 +984,8 @@ static void netlbl_unlabel_acceptflg_set(u8 value,
struct audit_buffer *audit_buf;
u8 old_val;
spin_lock(&netlabel_unlabel_acceptflg_lock);
old_val = netlabel_unlabel_acceptflg;
netlabel_unlabel_acceptflg = value;
spin_unlock(&netlabel_unlabel_acceptflg_lock);
audit_buf = netlbl_audit_start_common(AUDIT_MAC_UNLBL_ALLOW,
audit_info);
if (audit_buf != NULL) {
......@@ -98,6 +995,48 @@ static void netlbl_unlabel_acceptflg_set(u8 value,
}
}
/**
* netlbl_unlabel_addrinfo_get - Get the IPv4/6 address information
* @info: the Generic NETLINK info block
* @addr: the IP address
* @mask: the IP address mask
* @len: the address length
*
* Description:
* Examine the Generic NETLINK message and extract the IP address information.
* Returns zero on success, negative values on failure.
*
*/
static int netlbl_unlabel_addrinfo_get(struct genl_info *info,
void **addr,
void **mask,
u32 *len)
{
u32 addr_len;
if (info->attrs[NLBL_UNLABEL_A_IPV4ADDR]) {
addr_len = nla_len(info->attrs[NLBL_UNLABEL_A_IPV4ADDR]);
if (addr_len != sizeof(struct in_addr) &&
addr_len != nla_len(info->attrs[NLBL_UNLABEL_A_IPV4MASK]))
return -EINVAL;
*len = addr_len;
*addr = nla_data(info->attrs[NLBL_UNLABEL_A_IPV4ADDR]);
*mask = nla_data(info->attrs[NLBL_UNLABEL_A_IPV4MASK]);
return 0;
} else if (info->attrs[NLBL_UNLABEL_A_IPV6ADDR]) {
addr_len = nla_len(info->attrs[NLBL_UNLABEL_A_IPV6ADDR]);
if (addr_len != sizeof(struct in6_addr) &&
addr_len != nla_len(info->attrs[NLBL_UNLABEL_A_IPV6MASK]))
return -EINVAL;
*len = addr_len;
*addr = nla_data(info->attrs[NLBL_UNLABEL_A_IPV6ADDR]);
*mask = nla_data(info->attrs[NLBL_UNLABEL_A_IPV6MASK]);
return 0;
}
return -EINVAL;
}
/*
* NetLabel Command Handlers
*/
......@@ -155,11 +1094,9 @@ static int netlbl_unlabel_list(struct sk_buff *skb, struct genl_info *info)
goto list_failure;
}
rcu_read_lock();
ret_val = nla_put_u8(ans_skb,
NLBL_UNLABEL_A_ACPTFLG,
netlabel_unlabel_acceptflg);
rcu_read_unlock();
if (ret_val != 0)
goto list_failure;
......@@ -175,11 +1112,489 @@ static int netlbl_unlabel_list(struct sk_buff *skb, struct genl_info *info)
return ret_val;
}
/**
* netlbl_unlabel_staticadd - Handle a STATICADD message
* @skb: the NETLINK buffer
* @info: the Generic NETLINK info block
*
* Description:
* Process a user generated STATICADD message and add a new unlabeled
* connection entry to the hash table. Returns zero on success, negative
* values on failure.
*
*/
static int netlbl_unlabel_staticadd(struct sk_buff *skb,
struct genl_info *info)
{
int ret_val;
char *dev_name;
void *addr;
void *mask;
u32 addr_len;
u32 secid;
struct netlbl_audit audit_info;
/* Don't allow users to add both IPv4 and IPv6 addresses for a
* single entry. However, allow users to create two entries, one each
* for IPv4 and IPv4, with the same LSM security context which should
* achieve the same result. */
if (!info->attrs[NLBL_UNLABEL_A_SECCTX] ||
!info->attrs[NLBL_UNLABEL_A_IFACE] ||
!((!info->attrs[NLBL_UNLABEL_A_IPV4ADDR] ||
!info->attrs[NLBL_UNLABEL_A_IPV4MASK]) ^
(!info->attrs[NLBL_UNLABEL_A_IPV6ADDR] ||
!info->attrs[NLBL_UNLABEL_A_IPV6MASK])))
return -EINVAL;
netlbl_netlink_auditinfo(skb, &audit_info);
ret_val = netlbl_unlabel_addrinfo_get(info, &addr, &mask, &addr_len);
if (ret_val != 0)
return ret_val;
dev_name = nla_data(info->attrs[NLBL_UNLABEL_A_IFACE]);
ret_val = security_secctx_to_secid(
nla_data(info->attrs[NLBL_UNLABEL_A_SECCTX]),
nla_len(info->attrs[NLBL_UNLABEL_A_SECCTX]),
&secid);
if (ret_val != 0)
return ret_val;
return netlbl_unlhsh_add(&init_net,
dev_name, addr, mask, addr_len, secid,
&audit_info);
}
/**
* netlbl_unlabel_staticadddef - Handle a STATICADDDEF message
* @skb: the NETLINK buffer
* @info: the Generic NETLINK info block
*
* Description:
* Process a user generated STATICADDDEF message and add a new default
* unlabeled connection entry. Returns zero on success, negative values on
* failure.
*
*/
static int netlbl_unlabel_staticadddef(struct sk_buff *skb,
struct genl_info *info)
{
int ret_val;
void *addr;
void *mask;
u32 addr_len;
u32 secid;
struct netlbl_audit audit_info;
/* Don't allow users to add both IPv4 and IPv6 addresses for a
* single entry. However, allow users to create two entries, one each
* for IPv4 and IPv6, with the same LSM security context which should
* achieve the same result. */
if (!info->attrs[NLBL_UNLABEL_A_SECCTX] ||
!((!info->attrs[NLBL_UNLABEL_A_IPV4ADDR] ||
!info->attrs[NLBL_UNLABEL_A_IPV4MASK]) ^
(!info->attrs[NLBL_UNLABEL_A_IPV6ADDR] ||
!info->attrs[NLBL_UNLABEL_A_IPV6MASK])))
return -EINVAL;
netlbl_netlink_auditinfo(skb, &audit_info);
ret_val = netlbl_unlabel_addrinfo_get(info, &addr, &mask, &addr_len);
if (ret_val != 0)
return ret_val;
ret_val = security_secctx_to_secid(
nla_data(info->attrs[NLBL_UNLABEL_A_SECCTX]),
nla_len(info->attrs[NLBL_UNLABEL_A_SECCTX]),
&secid);
if (ret_val != 0)
return ret_val;
return netlbl_unlhsh_add(&init_net,
NULL, addr, mask, addr_len, secid,
&audit_info);
}
/**
* netlbl_unlabel_staticremove - Handle a STATICREMOVE message
* @skb: the NETLINK buffer
* @info: the Generic NETLINK info block
*
* Description:
* Process a user generated STATICREMOVE message and remove the specified
* unlabeled connection entry. Returns zero on success, negative values on
* failure.
*
*/
static int netlbl_unlabel_staticremove(struct sk_buff *skb,
struct genl_info *info)
{
int ret_val;
char *dev_name;
void *addr;
void *mask;
u32 addr_len;
struct netlbl_audit audit_info;
/* See the note in netlbl_unlabel_staticadd() about not allowing both
* IPv4 and IPv6 in the same entry. */
if (!info->attrs[NLBL_UNLABEL_A_IFACE] ||
!((!info->attrs[NLBL_UNLABEL_A_IPV4ADDR] ||
!info->attrs[NLBL_UNLABEL_A_IPV4MASK]) ^
(!info->attrs[NLBL_UNLABEL_A_IPV6ADDR] ||
!info->attrs[NLBL_UNLABEL_A_IPV6MASK])))
return -EINVAL;
netlbl_netlink_auditinfo(skb, &audit_info);
ret_val = netlbl_unlabel_addrinfo_get(info, &addr, &mask, &addr_len);
if (ret_val != 0)
return ret_val;
dev_name = nla_data(info->attrs[NLBL_UNLABEL_A_IFACE]);
return netlbl_unlhsh_remove(&init_net,
dev_name, addr, mask, addr_len,
&audit_info);
}
/**
* netlbl_unlabel_staticremovedef - Handle a STATICREMOVEDEF message
* @skb: the NETLINK buffer
* @info: the Generic NETLINK info block
*
* Description:
* Process a user generated STATICREMOVEDEF message and remove the default
* unlabeled connection entry. Returns zero on success, negative values on
* failure.
*
*/
static int netlbl_unlabel_staticremovedef(struct sk_buff *skb,
struct genl_info *info)
{
int ret_val;
void *addr;
void *mask;
u32 addr_len;
struct netlbl_audit audit_info;
/* See the note in netlbl_unlabel_staticadd() about not allowing both
* IPv4 and IPv6 in the same entry. */
if (!((!info->attrs[NLBL_UNLABEL_A_IPV4ADDR] ||
!info->attrs[NLBL_UNLABEL_A_IPV4MASK]) ^
(!info->attrs[NLBL_UNLABEL_A_IPV6ADDR] ||
!info->attrs[NLBL_UNLABEL_A_IPV6MASK])))
return -EINVAL;
netlbl_netlink_auditinfo(skb, &audit_info);
ret_val = netlbl_unlabel_addrinfo_get(info, &addr, &mask, &addr_len);
if (ret_val != 0)
return ret_val;
return netlbl_unlhsh_remove(&init_net,
NULL, addr, mask, addr_len,
&audit_info);
}
/**
* netlbl_unlabel_staticlist_gen - Generate messages for STATICLIST[DEF]
* @cmd: command/message
* @iface: the interface entry
* @addr4: the IPv4 address entry
* @addr6: the IPv6 address entry
* @arg: the netlbl_unlhsh_walk_arg structure
*
* Description:
* This function is designed to be used to generate a response for a
* STATICLIST or STATICLISTDEF message. When called either @addr4 or @addr6
* can be specified, not both, the other unspecified entry should be set to
* NULL by the caller. Returns the size of the message on success, negative
* values on failure.
*
*/
static int netlbl_unlabel_staticlist_gen(u32 cmd,
const struct netlbl_unlhsh_iface *iface,
const struct netlbl_unlhsh_addr4 *addr4,
const struct netlbl_unlhsh_addr6 *addr6,
void *arg)
{
int ret_val = -ENOMEM;
struct netlbl_unlhsh_walk_arg *cb_arg = arg;
struct net_device *dev;
void *data;
u32 secid;
char *secctx;
u32 secctx_len;
data = genlmsg_put(cb_arg->skb, NETLINK_CB(cb_arg->nl_cb->skb).pid,
cb_arg->seq, &netlbl_unlabel_gnl_family,
NLM_F_MULTI, cmd);
if (data == NULL)
goto list_cb_failure;
if (iface->ifindex > 0) {
dev = dev_get_by_index(&init_net, iface->ifindex);
ret_val = nla_put_string(cb_arg->skb,
NLBL_UNLABEL_A_IFACE, dev->name);
dev_put(dev);
if (ret_val != 0)
goto list_cb_failure;
}
if (addr4) {
struct in_addr addr_struct;
addr_struct.s_addr = addr4->addr;
ret_val = nla_put(cb_arg->skb,
NLBL_UNLABEL_A_IPV4ADDR,
sizeof(struct in_addr),
&addr_struct);
if (ret_val != 0)
goto list_cb_failure;
addr_struct.s_addr = addr4->mask;
ret_val = nla_put(cb_arg->skb,
NLBL_UNLABEL_A_IPV4MASK,
sizeof(struct in_addr),
&addr_struct);
if (ret_val != 0)
goto list_cb_failure;
secid = addr4->secid;
} else {
ret_val = nla_put(cb_arg->skb,
NLBL_UNLABEL_A_IPV6ADDR,
sizeof(struct in6_addr),
&addr6->addr);
if (ret_val != 0)
goto list_cb_failure;
ret_val = nla_put(cb_arg->skb,
NLBL_UNLABEL_A_IPV6MASK,
sizeof(struct in6_addr),
&addr6->mask);
if (ret_val != 0)
goto list_cb_failure;
secid = addr6->secid;
}
ret_val = security_secid_to_secctx(secid, &secctx, &secctx_len);
if (ret_val != 0)
goto list_cb_failure;
ret_val = nla_put(cb_arg->skb,
NLBL_UNLABEL_A_SECCTX,
secctx_len,
secctx);
security_release_secctx(secctx, secctx_len);
if (ret_val != 0)
goto list_cb_failure;
cb_arg->seq++;
return genlmsg_end(cb_arg->skb, data);
list_cb_failure:
genlmsg_cancel(cb_arg->skb, data);
return ret_val;
}
/**
* netlbl_unlabel_staticlist - Handle a STATICLIST message
* @skb: the NETLINK buffer
* @cb: the NETLINK callback
*
* Description:
* Process a user generated STATICLIST message and dump the unlabeled
* connection hash table in a form suitable for use in a kernel generated
* STATICLIST message. Returns the length of @skb.
*
*/
static int netlbl_unlabel_staticlist(struct sk_buff *skb,
struct netlink_callback *cb)
{
struct netlbl_unlhsh_walk_arg cb_arg;
u32 skip_bkt = cb->args[0];
u32 skip_chain = cb->args[1];
u32 skip_addr4 = cb->args[2];
u32 skip_addr6 = cb->args[3];
u32 iter_bkt;
u32 iter_chain = 0, iter_addr4 = 0, iter_addr6 = 0;
struct netlbl_unlhsh_iface *iface;
struct netlbl_unlhsh_addr4 *addr4;
struct netlbl_unlhsh_addr6 *addr6;
cb_arg.nl_cb = cb;
cb_arg.skb = skb;
cb_arg.seq = cb->nlh->nlmsg_seq;
rcu_read_lock();
for (iter_bkt = skip_bkt;
iter_bkt < rcu_dereference(netlbl_unlhsh)->size;
iter_bkt++, iter_chain = 0, iter_addr4 = 0, iter_addr6 = 0) {
list_for_each_entry_rcu(iface,
&rcu_dereference(netlbl_unlhsh)->tbl[iter_bkt],
list) {
if (!iface->valid ||
iter_chain++ < skip_chain)
continue;
list_for_each_entry_rcu(addr4,
&iface->addr4_list,
list) {
if (!addr4->valid || iter_addr4++ < skip_addr4)
continue;
if (netlbl_unlabel_staticlist_gen(
NLBL_UNLABEL_C_STATICLIST,
iface,
addr4,
NULL,
&cb_arg) < 0) {
iter_addr4--;
iter_chain--;
goto unlabel_staticlist_return;
}
}
list_for_each_entry_rcu(addr6,
&iface->addr6_list,
list) {
if (!addr6->valid || iter_addr6++ < skip_addr6)
continue;
if (netlbl_unlabel_staticlist_gen(
NLBL_UNLABEL_C_STATICLIST,
iface,
NULL,
addr6,
&cb_arg) < 0) {
iter_addr6--;
iter_chain--;
goto unlabel_staticlist_return;
}
}
}
}
unlabel_staticlist_return:
rcu_read_unlock();
cb->args[0] = skip_bkt;
cb->args[1] = skip_chain;
cb->args[2] = skip_addr4;
cb->args[3] = skip_addr6;
return skb->len;
}
/**
* netlbl_unlabel_staticlistdef - Handle a STATICLISTDEF message
* @skb: the NETLINK buffer
* @cb: the NETLINK callback
*
* Description:
* Process a user generated STATICLISTDEF message and dump the default
* unlabeled connection entry in a form suitable for use in a kernel generated
* STATICLISTDEF message. Returns the length of @skb.
*
*/
static int netlbl_unlabel_staticlistdef(struct sk_buff *skb,
struct netlink_callback *cb)
{
struct netlbl_unlhsh_walk_arg cb_arg;
struct netlbl_unlhsh_iface *iface;
u32 skip_addr4 = cb->args[0];
u32 skip_addr6 = cb->args[1];
u32 iter_addr4 = 0, iter_addr6 = 0;
struct netlbl_unlhsh_addr4 *addr4;
struct netlbl_unlhsh_addr6 *addr6;
cb_arg.nl_cb = cb;
cb_arg.skb = skb;
cb_arg.seq = cb->nlh->nlmsg_seq;
rcu_read_lock();
iface = rcu_dereference(netlbl_unlhsh_def);
if (iface == NULL || !iface->valid)
goto unlabel_staticlistdef_return;
list_for_each_entry_rcu(addr4, &iface->addr4_list, list) {
if (!addr4->valid || iter_addr4++ < skip_addr4)
continue;
if (netlbl_unlabel_staticlist_gen(NLBL_UNLABEL_C_STATICLISTDEF,
iface,
addr4,
NULL,
&cb_arg) < 0) {
iter_addr4--;
goto unlabel_staticlistdef_return;
}
}
list_for_each_entry_rcu(addr6, &iface->addr6_list, list) {
if (addr6->valid || iter_addr6++ < skip_addr6)
continue;
if (netlbl_unlabel_staticlist_gen(NLBL_UNLABEL_C_STATICLISTDEF,
iface,
NULL,
addr6,
&cb_arg) < 0) {
iter_addr6--;
goto unlabel_staticlistdef_return;
}
}
unlabel_staticlistdef_return:
rcu_read_unlock();
cb->args[0] = skip_addr4;
cb->args[1] = skip_addr6;
return skb->len;
}
/*
* NetLabel Generic NETLINK Command Definitions
*/
static struct genl_ops netlbl_unlabel_genl_c_staticadd = {
.cmd = NLBL_UNLABEL_C_STATICADD,
.flags = GENL_ADMIN_PERM,
.policy = netlbl_unlabel_genl_policy,
.doit = netlbl_unlabel_staticadd,
.dumpit = NULL,
};
static struct genl_ops netlbl_unlabel_genl_c_staticremove = {
.cmd = NLBL_UNLABEL_C_STATICREMOVE,
.flags = GENL_ADMIN_PERM,
.policy = netlbl_unlabel_genl_policy,
.doit = netlbl_unlabel_staticremove,
.dumpit = NULL,
};
static struct genl_ops netlbl_unlabel_genl_c_staticlist = {
.cmd = NLBL_UNLABEL_C_STATICLIST,
.flags = 0,
.policy = netlbl_unlabel_genl_policy,
.doit = NULL,
.dumpit = netlbl_unlabel_staticlist,
};
static struct genl_ops netlbl_unlabel_genl_c_staticadddef = {
.cmd = NLBL_UNLABEL_C_STATICADDDEF,
.flags = GENL_ADMIN_PERM,
.policy = netlbl_unlabel_genl_policy,
.doit = netlbl_unlabel_staticadddef,
.dumpit = NULL,
};
static struct genl_ops netlbl_unlabel_genl_c_staticremovedef = {
.cmd = NLBL_UNLABEL_C_STATICREMOVEDEF,
.flags = GENL_ADMIN_PERM,
.policy = netlbl_unlabel_genl_policy,
.doit = netlbl_unlabel_staticremovedef,
.dumpit = NULL,
};
static struct genl_ops netlbl_unlabel_genl_c_staticlistdef = {
.cmd = NLBL_UNLABEL_C_STATICLISTDEF,
.flags = 0,
.policy = netlbl_unlabel_genl_policy,
.doit = NULL,
.dumpit = netlbl_unlabel_staticlistdef,
};
static struct genl_ops netlbl_unlabel_genl_c_accept = {
.cmd = NLBL_UNLABEL_C_ACCEPT,
.flags = GENL_ADMIN_PERM,
......@@ -196,7 +1611,6 @@ static struct genl_ops netlbl_unlabel_genl_c_list = {
.dumpit = NULL,
};
/*
* NetLabel Generic NETLINK Protocol Functions
*/
......@@ -217,6 +1631,36 @@ int netlbl_unlabel_genl_init(void)
if (ret_val != 0)
return ret_val;
ret_val = genl_register_ops(&netlbl_unlabel_gnl_family,
&netlbl_unlabel_genl_c_staticadd);
if (ret_val != 0)
return ret_val;
ret_val = genl_register_ops(&netlbl_unlabel_gnl_family,
&netlbl_unlabel_genl_c_staticremove);
if (ret_val != 0)
return ret_val;
ret_val = genl_register_ops(&netlbl_unlabel_gnl_family,
&netlbl_unlabel_genl_c_staticlist);
if (ret_val != 0)
return ret_val;
ret_val = genl_register_ops(&netlbl_unlabel_gnl_family,
&netlbl_unlabel_genl_c_staticadddef);
if (ret_val != 0)
return ret_val;
ret_val = genl_register_ops(&netlbl_unlabel_gnl_family,
&netlbl_unlabel_genl_c_staticremovedef);
if (ret_val != 0)
return ret_val;
ret_val = genl_register_ops(&netlbl_unlabel_gnl_family,
&netlbl_unlabel_genl_c_staticlistdef);
if (ret_val != 0)
return ret_val;
ret_val = genl_register_ops(&netlbl_unlabel_gnl_family,
&netlbl_unlabel_genl_c_accept);
if (ret_val != 0)
......@@ -234,8 +1678,58 @@ int netlbl_unlabel_genl_init(void)
* NetLabel KAPI Hooks
*/
static struct notifier_block netlbl_unlhsh_netdev_notifier = {
.notifier_call = netlbl_unlhsh_netdev_handler,
};
/**
* netlbl_unlabel_init - Initialize the unlabeled connection hash table
* @size: the number of bits to use for the hash buckets
*
* Description:
* Initializes the unlabeled connection hash table and registers a network
* device notification handler. This function should only be called by the
* NetLabel subsystem itself during initialization. Returns zero on success,
* non-zero values on error.
*
*/
int netlbl_unlabel_init(u32 size)
{
u32 iter;
struct netlbl_unlhsh_tbl *hsh_tbl;
if (size == 0)
return -EINVAL;
hsh_tbl = kmalloc(sizeof(*hsh_tbl), GFP_KERNEL);
if (hsh_tbl == NULL)
return -ENOMEM;
hsh_tbl->size = 1 << size;
hsh_tbl->tbl = kcalloc(hsh_tbl->size,
sizeof(struct list_head),
GFP_KERNEL);
if (hsh_tbl->tbl == NULL) {
kfree(hsh_tbl);
return -ENOMEM;
}
for (iter = 0; iter < hsh_tbl->size; iter++)
INIT_LIST_HEAD(&hsh_tbl->tbl[iter]);
rcu_read_lock();
spin_lock(&netlbl_unlhsh_lock);
rcu_assign_pointer(netlbl_unlhsh, hsh_tbl);
spin_unlock(&netlbl_unlhsh_lock);
rcu_read_unlock();
register_netdevice_notifier(&netlbl_unlhsh_netdev_notifier);
return 0;
}
/**
* netlbl_unlabel_getattr - Get the security attributes for an unlabled packet
* @skb: the packet
* @family: protocol family
* @secattr: the security attributes
*
* Description:
......@@ -243,19 +1737,52 @@ int netlbl_unlabel_genl_init(void)
* them in @secattr. Returns zero on success and negative values on failure.
*
*/
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)
{
int ret_val;
struct iphdr *hdr4;
struct ipv6hdr *hdr6;
struct netlbl_unlhsh_addr4 *addr4;
struct netlbl_unlhsh_addr6 *addr6;
struct netlbl_unlhsh_iface *iface;
rcu_read_lock();
if (netlabel_unlabel_acceptflg == 1) {
netlbl_secattr_init(secattr);
ret_val = 0;
} else
ret_val = -ENOMSG;
iface = netlbl_unlhsh_search_iface_def(skb->iif);
if (iface == NULL)
goto unlabel_getattr_nolabel;
switch (family) {
case PF_INET:
hdr4 = ip_hdr(skb);
addr4 = netlbl_unlhsh_search_addr4(hdr4->saddr, iface);
if (addr4 == NULL)
goto unlabel_getattr_nolabel;
secattr->attr.secid = addr4->secid;
break;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
case PF_INET6:
hdr6 = ipv6_hdr(skb);
addr6 = netlbl_unlhsh_search_addr6(&hdr6->saddr, iface);
if (addr6 == NULL)
goto unlabel_getattr_nolabel;
secattr->attr.secid = addr6->secid;
break;
#endif /* IPv6 */
default:
goto unlabel_getattr_nolabel;
}
rcu_read_unlock();
return ret_val;
secattr->flags |= NETLBL_SECATTR_SECID;
secattr->type = NETLBL_NLTYPE_UNLABELED;
return 0;
unlabel_getattr_nolabel:
rcu_read_unlock();
if (netlabel_unlabel_acceptflg == 0)
return -ENOMSG;
secattr->type = NETLBL_NLTYPE_UNLABELED;
return 0;
}
/**
......
......@@ -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);
......@@ -12,8 +12,8 @@
* Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com>
* Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
* <dgoeddel@trustedcs.com>
* Copyright (C) 2006 Hewlett-Packard Development Company, L.P.
* Paul Moore, <paul.moore@hp.com>
* Copyright (C) 2006, 2007 Hewlett-Packard Development Company, L.P.
* Paul Moore <paul.moore@hp.com>
* Copyright (C) 2007 Hitachi Software Engineering Co., Ltd.
* Yuichi Nakamura <ynakam@hitachisoft.jp>
*
......@@ -50,8 +50,11 @@
#include <net/icmp.h>
#include <net/ip.h> /* for local_port_range[] */
#include <net/tcp.h> /* struct or_callable used in sock_rcv_skb */
#include <net/net_namespace.h>
#include <net/netlabel.h>
#include <asm/uaccess.h>
#include <asm/ioctls.h>
#include <asm/atomic.h>
#include <linux/bitops.h>
#include <linux/interrupt.h>
#include <linux/netdevice.h> /* for network interface checks */
......@@ -76,6 +79,7 @@
#include "avc.h"
#include "objsec.h"
#include "netif.h"
#include "netnode.h"
#include "xfrm.h"
#include "netlabel.h"
......@@ -89,6 +93,9 @@ extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm);
extern int selinux_compat_net;
extern struct security_operations *security_ops;
/* SECMARK reference count */
atomic_t selinux_secmark_refcount = ATOMIC_INIT(0);
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
int selinux_enforcing = 0;
......@@ -155,6 +162,21 @@ static int selinux_getsecurity(u32 sid, void *buffer, size_t size)
return len;
}
/**
* selinux_secmark_enabled - Check to see if SECMARK is currently enabled
*
* Description:
* This function checks the SECMARK reference counter to see if any SECMARK
* targets are currently configured, if the reference counter is greater than
* zero SECMARK is considered to be enabled. Returns true (1) if SECMARK is
* enabled, false (0) if SECMARK is disabled.
*
*/
static int selinux_secmark_enabled(void)
{
return (atomic_read(&selinux_secmark_refcount) > 0);
}
/* Allocate and free functions for each kind of security blob. */
static int task_alloc_security(struct task_struct *task)
......@@ -561,8 +583,8 @@ static int bad_option(struct superblock_security_struct *sbsec, char flag,
* Allow filesystems with binary mount data to explicitly set mount point
* labeling information.
*/
int selinux_set_mnt_opts(struct super_block *sb, char **mount_options,
int *flags, int num_opts)
static int selinux_set_mnt_opts(struct super_block *sb, char **mount_options,
int *flags, int num_opts)
{
int rc = 0, i;
struct task_security_struct *tsec = current->security;
......@@ -3395,7 +3417,7 @@ static int selinux_parse_skb_ipv6(struct sk_buff *skb,
#endif /* IPV6 */
static int selinux_parse_skb(struct sk_buff *skb, struct avc_audit_data *ad,
char **addrp, int *len, int src, u8 *proto)
char **addrp, int src, u8 *proto)
{
int ret = 0;
......@@ -3404,7 +3426,6 @@ static int selinux_parse_skb(struct sk_buff *skb, struct avc_audit_data *ad,
ret = selinux_parse_skb_ipv4(skb, ad, proto);
if (ret || !addrp)
break;
*len = 4;
*addrp = (char *)(src ? &ad->u.net.v4info.saddr :
&ad->u.net.v4info.daddr);
break;
......@@ -3414,7 +3435,6 @@ static int selinux_parse_skb(struct sk_buff *skb, struct avc_audit_data *ad,
ret = selinux_parse_skb_ipv6(skb, ad, proto);
if (ret || !addrp)
break;
*len = 16;
*addrp = (char *)(src ? &ad->u.net.v6info.saddr :
&ad->u.net.v6info.daddr);
break;
......@@ -3423,36 +3443,48 @@ static int selinux_parse_skb(struct sk_buff *skb, struct avc_audit_data *ad,
break;
}
if (unlikely(ret))
printk(KERN_WARNING
"SELinux: failure in selinux_parse_skb(),"
" unable to parse packet\n");
return ret;
}
/**
* selinux_skb_extlbl_sid - Determine the external label of a packet
* selinux_skb_peerlbl_sid - Determine the peer label of a packet
* @skb: the packet
* @sid: the packet's SID
* @family: protocol family
* @sid: the packet's peer label SID
*
* Description:
* Check the various different forms of external packet labeling and determine
* the external SID for the packet. If only one form of external labeling is
* present then it is used, if both labeled IPsec and NetLabel labels are
* present then the SELinux type information is taken from the labeled IPsec
* SA and the MLS sensitivity label information is taken from the NetLabel
* security attributes. This bit of "magic" is done in the call to
* selinux_netlbl_skbuff_getsid().
* Check the various different forms of network peer labeling and determine
* the peer label/SID for the packet; most of the magic actually occurs in
* the security server function security_net_peersid_cmp(). The function
* returns zero if the value in @sid is valid (although it may be SECSID_NULL)
* or -EACCES if @sid is invalid due to inconsistencies with the different
* peer labels.
*
*/
static void selinux_skb_extlbl_sid(struct sk_buff *skb, u32 *sid)
static int selinux_skb_peerlbl_sid(struct sk_buff *skb, u16 family, u32 *sid)
{
int err;
u32 xfrm_sid;
u32 nlbl_sid;
u32 nlbl_type;
selinux_skb_xfrm_sid(skb, &xfrm_sid);
if (selinux_netlbl_skbuff_getsid(skb,
(xfrm_sid == SECSID_NULL ?
SECINITSID_NETMSG : xfrm_sid),
&nlbl_sid) != 0)
nlbl_sid = SECSID_NULL;
*sid = (nlbl_sid == SECSID_NULL ? xfrm_sid : nlbl_sid);
selinux_netlbl_skbuff_getsid(skb, family, &nlbl_type, &nlbl_sid);
err = security_net_peersid_resolve(nlbl_sid, nlbl_type, xfrm_sid, sid);
if (unlikely(err)) {
printk(KERN_WARNING
"SELinux: failure in selinux_skb_peerlbl_sid(),"
" unable to determine packet's peer label\n");
return -EACCES;
}
return 0;
}
/* socket security operations */
......@@ -3518,6 +3550,7 @@ static int selinux_socket_post_create(struct socket *sock, int family,
if (sock->sk) {
sksec = sock->sk->sk_security;
sksec->sid = isec->sid;
sksec->sclass = isec->sclass;
err = selinux_netlbl_socket_post_create(sock);
}
......@@ -3610,7 +3643,7 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
break;
}
err = security_node_sid(family, addrp, addrlen, &sid);
err = sel_netnode_sid(addrp, family, &sid);
if (err)
goto out;
......@@ -3821,131 +3854,182 @@ static int selinux_socket_unix_may_send(struct socket *sock,
return 0;
}
static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb,
struct avc_audit_data *ad, u16 family, char *addrp, int len)
static int selinux_inet_sys_rcv_skb(int ifindex, char *addrp, u16 family,
u32 peer_sid,
struct avc_audit_data *ad)
{
int err = 0;
u32 netif_perm, node_perm, node_sid, if_sid, recv_perm = 0;
struct socket *sock;
u16 sock_class = 0;
u32 sock_sid = 0;
read_lock_bh(&sk->sk_callback_lock);
sock = sk->sk_socket;
if (sock) {
struct inode *inode;
inode = SOCK_INODE(sock);
if (inode) {
struct inode_security_struct *isec;
isec = inode->i_security;
sock_sid = isec->sid;
sock_class = isec->sclass;
}
}
read_unlock_bh(&sk->sk_callback_lock);
if (!sock_sid)
goto out;
int err;
u32 if_sid;
u32 node_sid;
if (!skb->dev)
goto out;
err = sel_netif_sid(ifindex, &if_sid);
if (err)
return err;
err = avc_has_perm(peer_sid, if_sid,
SECCLASS_NETIF, NETIF__INGRESS, ad);
if (err)
return err;
err = sel_netif_sids(skb->dev, &if_sid, NULL);
err = sel_netnode_sid(addrp, family, &node_sid);
if (err)
goto out;
return err;
return avc_has_perm(peer_sid, node_sid,
SECCLASS_NODE, NODE__RECVFROM, ad);
}
static int selinux_sock_rcv_skb_iptables_compat(struct sock *sk,
struct sk_buff *skb,
struct avc_audit_data *ad,
u16 family,
char *addrp)
{
int err;
struct sk_security_struct *sksec = sk->sk_security;
u16 sk_class;
u32 netif_perm, node_perm, recv_perm;
u32 port_sid, node_sid, if_sid, sk_sid;
switch (sock_class) {
sk_sid = sksec->sid;
sk_class = sksec->sclass;
switch (sk_class) {
case SECCLASS_UDP_SOCKET:
netif_perm = NETIF__UDP_RECV;
node_perm = NODE__UDP_RECV;
recv_perm = UDP_SOCKET__RECV_MSG;
break;
case SECCLASS_TCP_SOCKET:
netif_perm = NETIF__TCP_RECV;
node_perm = NODE__TCP_RECV;
recv_perm = TCP_SOCKET__RECV_MSG;
break;
case SECCLASS_DCCP_SOCKET:
netif_perm = NETIF__DCCP_RECV;
node_perm = NODE__DCCP_RECV;
recv_perm = DCCP_SOCKET__RECV_MSG;
break;
default:
netif_perm = NETIF__RAWIP_RECV;
node_perm = NODE__RAWIP_RECV;
recv_perm = 0;
break;
}
err = avc_has_perm(sock_sid, if_sid, SECCLASS_NETIF, netif_perm, ad);
err = sel_netif_sid(skb->iif, &if_sid);
if (err)
goto out;
err = security_node_sid(family, addrp, len, &node_sid);
return err;
err = avc_has_perm(sk_sid, if_sid, SECCLASS_NETIF, netif_perm, ad);
if (err)
goto out;
return err;
err = avc_has_perm(sock_sid, node_sid, SECCLASS_NODE, node_perm, ad);
err = sel_netnode_sid(addrp, family, &node_sid);
if (err)
goto out;
return err;
err = avc_has_perm(sk_sid, node_sid, SECCLASS_NODE, node_perm, ad);
if (err)
return err;
if (recv_perm) {
u32 port_sid;
if (!recv_perm)
return 0;
err = security_port_sid(sk->sk_family, sk->sk_type,
sk->sk_protocol, ntohs(ad->u.net.sport),
&port_sid);
if (unlikely(err)) {
printk(KERN_WARNING
"SELinux: failure in"
" selinux_sock_rcv_skb_iptables_compat(),"
" network port label not found\n");
return err;
}
return avc_has_perm(sk_sid, port_sid, sk_class, recv_perm, ad);
}
err = security_port_sid(sk->sk_family, sk->sk_type,
sk->sk_protocol, ntohs(ad->u.net.sport),
&port_sid);
if (err)
goto out;
static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb,
struct avc_audit_data *ad,
u16 family, char *addrp)
{
int err;
struct sk_security_struct *sksec = sk->sk_security;
u32 peer_sid;
u32 sk_sid = sksec->sid;
err = avc_has_perm(sock_sid, port_sid,
sock_class, recv_perm, ad);
if (selinux_compat_net)
err = selinux_sock_rcv_skb_iptables_compat(sk, skb, ad,
family, addrp);
else
err = avc_has_perm(sk_sid, skb->secmark, SECCLASS_PACKET,
PACKET__RECV, ad);
if (err)
return err;
if (selinux_policycap_netpeer) {
err = selinux_skb_peerlbl_sid(skb, family, &peer_sid);
if (err)
return err;
err = avc_has_perm(sk_sid, peer_sid,
SECCLASS_PEER, PEER__RECV, ad);
} else {
err = selinux_netlbl_sock_rcv_skb(sksec, skb, family, ad);
if (err)
return err;
err = selinux_xfrm_sock_rcv_skb(sksec->sid, skb, ad);
}
out:
return err;
}
static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
{
u16 family;
char *addrp;
int len, err = 0;
struct avc_audit_data ad;
int err;
struct sk_security_struct *sksec = sk->sk_security;
u16 family = sk->sk_family;
u32 sk_sid = sksec->sid;
struct avc_audit_data ad;
char *addrp;
family = sk->sk_family;
if (family != PF_INET && family != PF_INET6)
goto out;
return 0;
/* Handle mapped IPv4 packets arriving via IPv6 sockets */
if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP))
family = PF_INET;
AVC_AUDIT_DATA_INIT(&ad, NET);
ad.u.net.netif = skb->dev ? skb->dev->name : "[unknown]";
ad.u.net.netif = skb->iif;
ad.u.net.family = family;
err = selinux_parse_skb(skb, &ad, &addrp, &len, 1, NULL);
err = selinux_parse_skb(skb, &ad, &addrp, 1, NULL);
if (err)
goto out;
return err;
if (selinux_compat_net)
err = selinux_sock_rcv_skb_compat(sk, skb, &ad, family,
addrp, len);
else
err = avc_has_perm(sksec->sid, skb->secmark, SECCLASS_PACKET,
PACKET__RECV, &ad);
if (err)
goto out;
/* If any sort of compatibility mode is enabled then handoff processing
* to the selinux_sock_rcv_skb_compat() function to deal with the
* special handling. We do this in an attempt to keep this function
* as fast and as clean as possible. */
if (selinux_compat_net || !selinux_policycap_netpeer)
return selinux_sock_rcv_skb_compat(sk, skb, &ad,
family, addrp);
err = selinux_netlbl_sock_rcv_skb(sksec, skb, &ad);
if (err)
goto out;
if (netlbl_enabled() || selinux_xfrm_enabled()) {
u32 peer_sid;
err = selinux_skb_peerlbl_sid(skb, family, &peer_sid);
if (err)
return err;
err = selinux_inet_sys_rcv_skb(skb->iif, addrp, family,
peer_sid, &ad);
if (err)
return err;
err = avc_has_perm(sk_sid, peer_sid, SECCLASS_PEER,
PEER__RECV, &ad);
}
if (selinux_secmark_enabled()) {
err = avc_has_perm(sk_sid, skb->secmark, SECCLASS_PACKET,
PACKET__RECV, &ad);
if (err)
return err;
}
err = selinux_xfrm_sock_rcv_skb(sksec->sid, skb, &ad);
out:
return err;
}
......@@ -3996,18 +4080,25 @@ static int selinux_socket_getpeersec_stream(struct socket *sock, char __user *op
static int selinux_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *skb, u32 *secid)
{
u32 peer_secid = SECSID_NULL;
int err = 0;
u16 family;
if (sock)
family = sock->sk->sk_family;
else if (skb && skb->sk)
family = skb->sk->sk_family;
else
goto out;
if (sock && sock->sk->sk_family == PF_UNIX)
if (sock && family == PF_UNIX)
selinux_get_inode_sid(SOCK_INODE(sock), &peer_secid);
else if (skb)
selinux_skb_extlbl_sid(skb, &peer_secid);
selinux_skb_peerlbl_sid(skb, family, &peer_secid);
if (peer_secid == SECSID_NULL)
err = -EINVAL;
out:
*secid = peer_secid;
return err;
if (peer_secid == SECSID_NULL)
return -EINVAL;
return 0;
}
static int selinux_sk_alloc_security(struct sock *sk, int family, gfp_t priority)
......@@ -4027,6 +4118,7 @@ static void selinux_sk_clone_security(const struct sock *sk, struct sock *newsk)
newssec->sid = ssec->sid;
newssec->peer_sid = ssec->peer_sid;
newssec->sclass = ssec->sclass;
selinux_netlbl_sk_security_clone(ssec, newssec);
}
......@@ -4050,6 +4142,7 @@ static void selinux_sock_graft(struct sock* sk, struct socket *parent)
if (sk->sk_family == PF_INET || sk->sk_family == PF_INET6 ||
sk->sk_family == PF_UNIX)
isec->sid = sksec->sid;
sksec->sclass = isec->sclass;
selinux_netlbl_sock_graft(sk, parent);
}
......@@ -4062,7 +4155,9 @@ static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb,
u32 newsid;
u32 peersid;
selinux_skb_extlbl_sid(skb, &peersid);
err = selinux_skb_peerlbl_sid(skb, sk->sk_family, &peersid);
if (err)
return err;
if (peersid == SECSID_NULL) {
req->secid = sksec->sid;
req->peer_secid = SECSID_NULL;
......@@ -4100,7 +4195,7 @@ static void selinux_inet_conn_established(struct sock *sk,
{
struct sk_security_struct *sksec = sk->sk_security;
selinux_skb_extlbl_sid(skb, &sksec->peer_sid);
selinux_skb_peerlbl_sid(skb, sk->sk_family, &sksec->peer_sid);
}
static void selinux_req_classify_flow(const struct request_sock *req,
......@@ -4147,149 +4242,260 @@ static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb)
#ifdef CONFIG_NETFILTER
static int selinux_ip_postroute_last_compat(struct sock *sk, struct net_device *dev,
struct avc_audit_data *ad,
u16 family, char *addrp, int len)
static unsigned int selinux_ip_forward(struct sk_buff *skb, int ifindex,
u16 family)
{
int err = 0;
u32 netif_perm, node_perm, node_sid, if_sid, send_perm = 0;
struct socket *sock;
struct inode *inode;
struct inode_security_struct *isec;
char *addrp;
u32 peer_sid;
struct avc_audit_data ad;
u8 secmark_active;
u8 peerlbl_active;
sock = sk->sk_socket;
if (!sock)
goto out;
if (!selinux_policycap_netpeer)
return NF_ACCEPT;
inode = SOCK_INODE(sock);
if (!inode)
goto out;
secmark_active = selinux_secmark_enabled();
peerlbl_active = netlbl_enabled() || selinux_xfrm_enabled();
if (!secmark_active && !peerlbl_active)
return NF_ACCEPT;
isec = inode->i_security;
err = sel_netif_sids(dev, &if_sid, NULL);
if (err)
goto out;
AVC_AUDIT_DATA_INIT(&ad, NET);
ad.u.net.netif = ifindex;
ad.u.net.family = family;
if (selinux_parse_skb(skb, &ad, &addrp, 1, NULL) != 0)
return NF_DROP;
if (selinux_skb_peerlbl_sid(skb, family, &peer_sid) != 0)
return NF_DROP;
if (peerlbl_active)
if (selinux_inet_sys_rcv_skb(ifindex, addrp, family,
peer_sid, &ad) != 0)
return NF_DROP;
if (secmark_active)
if (avc_has_perm(peer_sid, skb->secmark,
SECCLASS_PACKET, PACKET__FORWARD_IN, &ad))
return NF_DROP;
return NF_ACCEPT;
}
static unsigned int selinux_ipv4_forward(unsigned int hooknum,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
return selinux_ip_forward(skb, in->ifindex, PF_INET);
}
switch (isec->sclass) {
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
static unsigned int selinux_ipv6_forward(unsigned int hooknum,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
return selinux_ip_forward(skb, in->ifindex, PF_INET6);
}
#endif /* IPV6 */
static int selinux_ip_postroute_iptables_compat(struct sock *sk,
int ifindex,
struct avc_audit_data *ad,
u16 family, char *addrp)
{
int err;
struct sk_security_struct *sksec = sk->sk_security;
u16 sk_class;
u32 netif_perm, node_perm, send_perm;
u32 port_sid, node_sid, if_sid, sk_sid;
sk_sid = sksec->sid;
sk_class = sksec->sclass;
switch (sk_class) {
case SECCLASS_UDP_SOCKET:
netif_perm = NETIF__UDP_SEND;
node_perm = NODE__UDP_SEND;
send_perm = UDP_SOCKET__SEND_MSG;
break;
case SECCLASS_TCP_SOCKET:
netif_perm = NETIF__TCP_SEND;
node_perm = NODE__TCP_SEND;
send_perm = TCP_SOCKET__SEND_MSG;
break;
case SECCLASS_DCCP_SOCKET:
netif_perm = NETIF__DCCP_SEND;
node_perm = NODE__DCCP_SEND;
send_perm = DCCP_SOCKET__SEND_MSG;
break;
default:
netif_perm = NETIF__RAWIP_SEND;
node_perm = NODE__RAWIP_SEND;
send_perm = 0;
break;
}
err = avc_has_perm(isec->sid, if_sid, SECCLASS_NETIF, netif_perm, ad);
err = sel_netif_sid(ifindex, &if_sid);
if (err)
goto out;
return err;
err = avc_has_perm(sk_sid, if_sid, SECCLASS_NETIF, netif_perm, ad);
return err;
err = security_node_sid(family, addrp, len, &node_sid);
err = sel_netnode_sid(addrp, family, &node_sid);
if (err)
goto out;
err = avc_has_perm(isec->sid, node_sid, SECCLASS_NODE, node_perm, ad);
return err;
err = avc_has_perm(sk_sid, node_sid, SECCLASS_NODE, node_perm, ad);
if (err)
goto out;
return err;
if (send_perm) {
u32 port_sid;
err = security_port_sid(sk->sk_family,
sk->sk_type,
sk->sk_protocol,
ntohs(ad->u.net.dport),
&port_sid);
if (err)
goto out;
if (send_perm != 0)
return 0;
err = avc_has_perm(isec->sid, port_sid, isec->sclass,
send_perm, ad);
err = security_port_sid(sk->sk_family, sk->sk_type,
sk->sk_protocol, ntohs(ad->u.net.dport),
&port_sid);
if (unlikely(err)) {
printk(KERN_WARNING
"SELinux: failure in"
" selinux_ip_postroute_iptables_compat(),"
" network port label not found\n");
return err;
}
out:
return err;
return avc_has_perm(sk_sid, port_sid, sk_class, send_perm, ad);
}
static unsigned int selinux_ip_postroute_last(unsigned int hooknum,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *),
u16 family)
static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb,
int ifindex,
struct avc_audit_data *ad,
u16 family,
char *addrp,
u8 proto)
{
char *addrp;
int len, err = 0;
struct sock *sk;
struct avc_audit_data ad;
struct net_device *dev = (struct net_device *)out;
struct sock *sk = skb->sk;
struct sk_security_struct *sksec;
u8 proto;
sk = skb->sk;
if (!sk)
goto out;
if (sk == NULL)
return NF_ACCEPT;
sksec = sk->sk_security;
AVC_AUDIT_DATA_INIT(&ad, NET);
ad.u.net.netif = dev->name;
ad.u.net.family = family;
if (selinux_compat_net) {
if (selinux_ip_postroute_iptables_compat(skb->sk, ifindex,
ad, family, addrp))
return NF_DROP;
} else {
if (avc_has_perm(sksec->sid, skb->secmark,
SECCLASS_PACKET, PACKET__SEND, ad))
return NF_DROP;
}
err = selinux_parse_skb(skb, &ad, &addrp, &len, 0, &proto);
if (err)
goto out;
if (selinux_policycap_netpeer)
if (selinux_xfrm_postroute_last(sksec->sid, skb, ad, proto))
return NF_DROP;
if (selinux_compat_net)
err = selinux_ip_postroute_last_compat(sk, dev, &ad,
family, addrp, len);
else
err = avc_has_perm(sksec->sid, skb->secmark, SECCLASS_PACKET,
PACKET__SEND, &ad);
return NF_ACCEPT;
}
if (err)
goto out;
static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex,
u16 family)
{
u32 secmark_perm;
u32 peer_sid;
struct sock *sk;
struct avc_audit_data ad;
char *addrp;
u8 proto;
u8 secmark_active;
u8 peerlbl_active;
err = selinux_xfrm_postroute_last(sksec->sid, skb, &ad, proto);
out:
return err ? NF_DROP : NF_ACCEPT;
AVC_AUDIT_DATA_INIT(&ad, NET);
ad.u.net.netif = ifindex;
ad.u.net.family = family;
if (selinux_parse_skb(skb, &ad, &addrp, 0, &proto))
return NF_DROP;
/* If any sort of compatibility mode is enabled then handoff processing
* to the selinux_ip_postroute_compat() function to deal with the
* special handling. We do this in an attempt to keep this function
* as fast and as clean as possible. */
if (selinux_compat_net || !selinux_policycap_netpeer)
return selinux_ip_postroute_compat(skb, ifindex, &ad,
family, addrp, proto);
/* If skb->dst->xfrm is non-NULL then the packet is undergoing an IPsec
* packet transformation so allow the packet to pass without any checks
* since we'll have another chance to perform access control checks
* when the packet is on it's final way out.
* NOTE: there appear to be some IPv6 multicast cases where skb->dst
* is NULL, in this case go ahead and apply access control. */
if (skb->dst != NULL && skb->dst->xfrm != NULL)
return NF_ACCEPT;
secmark_active = selinux_secmark_enabled();
peerlbl_active = netlbl_enabled() || selinux_xfrm_enabled();
if (!secmark_active && !peerlbl_active)
return NF_ACCEPT;
/* if the packet is locally generated (skb->sk != NULL) then use the
* socket's label as the peer label, otherwise the packet is being
* forwarded through this system and we need to fetch the peer label
* directly from the packet */
sk = skb->sk;
if (sk) {
struct sk_security_struct *sksec = sk->sk_security;
peer_sid = sksec->sid;
secmark_perm = PACKET__SEND;
} else {
if (selinux_skb_peerlbl_sid(skb, family, &peer_sid))
return NF_DROP;
secmark_perm = PACKET__FORWARD_OUT;
}
if (secmark_active)
if (avc_has_perm(peer_sid, skb->secmark,
SECCLASS_PACKET, secmark_perm, &ad))
return NF_DROP;
if (peerlbl_active) {
u32 if_sid;
u32 node_sid;
if (sel_netif_sid(ifindex, &if_sid))
return NF_DROP;
if (avc_has_perm(peer_sid, if_sid,
SECCLASS_NETIF, NETIF__EGRESS, &ad))
return NF_DROP;
if (sel_netnode_sid(addrp, family, &node_sid))
return NF_DROP;
if (avc_has_perm(peer_sid, node_sid,
SECCLASS_NODE, NODE__SENDTO, &ad))
return NF_DROP;
}
return NF_ACCEPT;
}
static unsigned int selinux_ipv4_postroute_last(unsigned int hooknum,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
static unsigned int selinux_ipv4_postroute(unsigned int hooknum,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
return selinux_ip_postroute_last(hooknum, skb, in, out, okfn, PF_INET);
return selinux_ip_postroute(skb, out->ifindex, PF_INET);
}
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
static unsigned int selinux_ipv6_postroute_last(unsigned int hooknum,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
static unsigned int selinux_ipv6_postroute(unsigned int hooknum,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
return selinux_ip_postroute_last(hooknum, skb, in, out, okfn, PF_INET6);
return selinux_ip_postroute(skb, out->ifindex, PF_INET6);
}
#endif /* IPV6 */
#endif /* CONFIG_NETFILTER */
......@@ -5277,22 +5483,40 @@ security_initcall(selinux_init);
#if defined(CONFIG_NETFILTER)
static struct nf_hook_ops selinux_ipv4_op = {
.hook = selinux_ipv4_postroute_last,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_INET_POST_ROUTING,
.priority = NF_IP_PRI_SELINUX_LAST,
static struct nf_hook_ops selinux_ipv4_ops[] = {
{
.hook = selinux_ipv4_postroute,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_INET_POST_ROUTING,
.priority = NF_IP_PRI_SELINUX_LAST,
},
{
.hook = selinux_ipv4_forward,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_INET_FORWARD,
.priority = NF_IP_PRI_SELINUX_FIRST,
}
};
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
static struct nf_hook_ops selinux_ipv6_op = {
.hook = selinux_ipv6_postroute_last,
.owner = THIS_MODULE,
.pf = PF_INET6,
.hooknum = NF_INET_POST_ROUTING,
.priority = NF_IP6_PRI_SELINUX_LAST,
static struct nf_hook_ops selinux_ipv6_ops[] = {
{
.hook = selinux_ipv6_postroute,
.owner = THIS_MODULE,
.pf = PF_INET6,
.hooknum = NF_INET_POST_ROUTING,
.priority = NF_IP6_PRI_SELINUX_LAST,
},
{
.hook = selinux_ipv6_forward,
.owner = THIS_MODULE,
.pf = PF_INET6,
.hooknum = NF_INET_FORWARD,
.priority = NF_IP6_PRI_SELINUX_FIRST,
}
};
#endif /* IPV6 */
......@@ -5300,22 +5524,27 @@ static struct nf_hook_ops selinux_ipv6_op = {
static int __init selinux_nf_ip_init(void)
{
int err = 0;
u32 iter;
if (!selinux_enabled)
goto out;
printk(KERN_DEBUG "SELinux: Registering netfilter hooks\n");
err = nf_register_hook(&selinux_ipv4_op);
if (err)
panic("SELinux: nf_register_hook for IPv4: error %d\n", err);
for (iter = 0; iter < ARRAY_SIZE(selinux_ipv4_ops); iter++) {
err = nf_register_hook(&selinux_ipv4_ops[iter]);
if (err)
panic("SELinux: nf_register_hook for IPv4: error %d\n",
err);
}
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
err = nf_register_hook(&selinux_ipv6_op);
if (err)
panic("SELinux: nf_register_hook for IPv6: error %d\n", err);
for (iter = 0; iter < ARRAY_SIZE(selinux_ipv6_ops); iter++) {
err = nf_register_hook(&selinux_ipv6_ops[iter]);
if (err)
panic("SELinux: nf_register_hook for IPv6: error %d\n",
err);
}
#endif /* IPV6 */
out:
......@@ -5327,11 +5556,15 @@ __initcall(selinux_nf_ip_init);
#ifdef CONFIG_SECURITY_SELINUX_DISABLE
static void selinux_nf_ip_exit(void)
{
u32 iter;
printk(KERN_DEBUG "SELinux: Unregistering netfilter hooks\n");
nf_unregister_hook(&selinux_ipv4_op);
for (iter = 0; iter < ARRAY_SIZE(selinux_ipv4_ops); iter++)
nf_unregister_hook(&selinux_ipv4_ops[iter]);
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
nf_unregister_hook(&selinux_ipv6_op);
for (iter = 0; iter < ARRAY_SIZE(selinux_ipv6_ops); iter++)
nf_unregister_hook(&selinux_ipv6_ops[iter]);
#endif /* IPV6 */
}
#endif
......
......@@ -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 @@ static int sel_make_classes(void)
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:
......
......@@ -562,7 +562,7 @@ void mls_export_netlbl_lvl(struct context *context,
if (!selinux_mls_enabled)
return;
secattr->mls_lvl = context->range.level[0].sens - 1;
secattr->attr.mls.lvl = context->range.level[0].sens - 1;
secattr->flags |= NETLBL_SECATTR_MLS_LVL;
}
......@@ -582,7 +582,7 @@ void mls_import_netlbl_lvl(struct context *context,
if (!selinux_mls_enabled)
return;
context->range.level[0].sens = secattr->mls_lvl + 1;
context->range.level[0].sens = secattr->attr.mls.lvl + 1;
context->range.level[1].sens = context->range.level[0].sens;
}
......@@ -605,8 +605,8 @@ int mls_export_netlbl_cat(struct context *context,
return 0;
rc = ebitmap_netlbl_export(&context->range.level[0].cat,
&secattr->mls_cat);
if (rc == 0 && secattr->mls_cat != NULL)
&secattr->attr.mls.cat);
if (rc == 0 && secattr->attr.mls.cat != NULL)
secattr->flags |= NETLBL_SECATTR_MLS_CAT;
return rc;
......@@ -633,7 +633,7 @@ int mls_import_netlbl_cat(struct context *context,
return 0;
rc = ebitmap_netlbl_import(&context->range.level[0].cat,
secattr->mls_cat);
secattr->attr.mls.cat);
if (rc != 0)
goto import_netlbl_cat_failure;
......
......@@ -13,6 +13,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) 2004-2005 Trusted Computer Solutions, Inc.
* Copyright (C) 2003 - 2004 Tresys Technology, LLC
* This program is free software; you can redistribute it and/or modify
......@@ -102,6 +107,11 @@ static struct policydb_compat_info policydb_compat[] = {
.sym_num = SYM_NUM,
.ocon_num = OCON_NUM,
},
{
.version = POLICYDB_VERSION_POLCAP,
.sym_num = SYM_NUM,
.ocon_num = OCON_NUM,
}
};
static struct policydb_compat_info *policydb_lookup_compat(int version)
......@@ -183,6 +193,8 @@ static int policydb_init(struct policydb *p)
if (rc)
goto out_free_symtab;
ebitmap_init(&p->policycaps);
out:
return rc;
......@@ -673,8 +685,8 @@ void policydb_destroy(struct policydb *p)
ebitmap_destroy(&p->type_attr_map[i]);
}
kfree(p->type_attr_map);
kfree(p->undefined_perms);
ebitmap_destroy(&p->policycaps);
return;
}
......@@ -1554,6 +1566,10 @@ int policydb_read(struct policydb *p, void *fp)
p->reject_unknown = !!(le32_to_cpu(buf[1]) & REJECT_UNKNOWN);
p->allow_unknown = !!(le32_to_cpu(buf[1]) & ALLOW_UNKNOWN);
if (p->policyvers >= POLICYDB_VERSION_POLCAP &&
ebitmap_read(&p->policycaps, fp) != 0)
goto bad;
info = policydb_lookup_compat(p->policyvers);
if (!info) {
printk(KERN_ERR "security: unable to find policy compat info "
......
......@@ -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;
......
......@@ -16,12 +16,13 @@
* Updated: Hewlett-Packard <paul.moore@hp.com>
*
* Added support for NetLabel
* Added support for the policy capability bitmap
*
* Updated: Chad Sellers <csellers@tresys.com>
*
* Added validation of kernel classes and permissions
*
* Copyright (C) 2006 Hewlett-Packard Development Company, L.P.
* Copyright (C) 2006, 2007 Hewlett-Packard Development Company, L.P.
* Copyright (C) 2004-2006 Trusted Computer Solutions, Inc.
* Copyright (C) 2003 - 2004, 2006 Tresys Technology, LLC
* Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com>
......@@ -59,6 +60,8 @@
extern void selnl_notify_policyload(u32 seqno);
unsigned int policydb_loaded_version;
int selinux_policycap_netpeer;
/*
* This is declared in avc.c
*/
......@@ -1299,6 +1302,12 @@ static int convert_context(u32 key,
goto out;
}
static void security_load_policycaps(void)
{
selinux_policycap_netpeer = ebitmap_get_bit(&policydb.policycaps,
POLICYDB_CAPABILITY_NETPEER);
}
extern void selinux_complete_init(void);
static int security_preserve_bools(struct policydb *p);
......@@ -1346,6 +1355,7 @@ int security_load_policy(void *data, size_t len)
avtab_cache_destroy();
return -EINVAL;
}
security_load_policycaps();
policydb_loaded_version = policydb.policyvers;
ss_initialized = 1;
seqno = ++latest_granting;
......@@ -1404,6 +1414,7 @@ int security_load_policy(void *data, size_t len)
POLICY_WRLOCK;
memcpy(&policydb, &newpolicydb, sizeof policydb);
sidtab_set(&sidtab, &newsidtab);
security_load_policycaps();
seqno = ++latest_granting;
policydb_loaded_version = policydb.policyvers;
POLICY_WRUNLOCK;
......@@ -1478,11 +1489,8 @@ int security_port_sid(u16 domain,
* security_netif_sid - Obtain the SID for a network interface.
* @name: interface name
* @if_sid: interface SID
* @msg_sid: default SID for received packets
*/
int security_netif_sid(char *name,
u32 *if_sid,
u32 *msg_sid)
int security_netif_sid(char *name, u32 *if_sid)
{
int rc = 0;
struct ocontext *c;
......@@ -1510,11 +1518,8 @@ int security_netif_sid(char *name,
goto out;
}
*if_sid = c->sid[0];
*msg_sid = c->sid[1];
} else {
} else
*if_sid = SECINITSID_NETIF;
*msg_sid = SECINITSID_NETMSG;
}
out:
POLICY_RDUNLOCK;
......@@ -2049,6 +2054,91 @@ int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid)
return rc;
}
/**
* security_net_peersid_resolve - Compare and resolve two network peer SIDs
* @nlbl_sid: NetLabel SID
* @nlbl_type: NetLabel labeling protocol type
* @xfrm_sid: XFRM SID
*
* Description:
* Compare the @nlbl_sid and @xfrm_sid values and if the two SIDs can be
* resolved into a single SID it is returned via @peer_sid and the function
* returns zero. Otherwise @peer_sid is set to SECSID_NULL and the function
* returns a negative value. A table summarizing the behavior is below:
*
* | function return | @sid
* ------------------------------+-----------------+-----------------
* no peer labels | 0 | SECSID_NULL
* single peer label | 0 | <peer_label>
* multiple, consistent labels | 0 | <peer_label>
* multiple, inconsistent labels | -<errno> | SECSID_NULL
*
*/
int security_net_peersid_resolve(u32 nlbl_sid, u32 nlbl_type,
u32 xfrm_sid,
u32 *peer_sid)
{
int rc;
struct context *nlbl_ctx;
struct context *xfrm_ctx;
/* handle the common (which also happens to be the set of easy) cases
* right away, these two if statements catch everything involving a
* single or absent peer SID/label */
if (xfrm_sid == SECSID_NULL) {
*peer_sid = nlbl_sid;
return 0;
}
/* NOTE: an nlbl_type == NETLBL_NLTYPE_UNLABELED is a "fallback" label
* and is treated as if nlbl_sid == SECSID_NULL when a XFRM SID/label
* is present */
if (nlbl_sid == SECSID_NULL || nlbl_type == NETLBL_NLTYPE_UNLABELED) {
*peer_sid = xfrm_sid;
return 0;
}
/* we don't need to check ss_initialized here since the only way both
* nlbl_sid and xfrm_sid are not equal to SECSID_NULL would be if the
* security server was initialized and ss_initialized was true */
if (!selinux_mls_enabled) {
*peer_sid = SECSID_NULL;
return 0;
}
POLICY_RDLOCK;
nlbl_ctx = sidtab_search(&sidtab, nlbl_sid);
if (!nlbl_ctx) {
printk(KERN_ERR
"security_sid_mls_cmp: unrecognized SID %d\n",
nlbl_sid);
rc = -EINVAL;
goto out_slowpath;
}
xfrm_ctx = sidtab_search(&sidtab, xfrm_sid);
if (!xfrm_ctx) {
printk(KERN_ERR
"security_sid_mls_cmp: unrecognized SID %d\n",
xfrm_sid);
rc = -EINVAL;
goto out_slowpath;
}
rc = (mls_context_cmp(nlbl_ctx, xfrm_ctx) ? 0 : -EACCES);
out_slowpath:
POLICY_RDUNLOCK;
if (rc == 0)
/* at present NetLabel SIDs/labels really only carry MLS
* information so if the MLS portion of the NetLabel SID
* matches the MLS portion of the labeled XFRM SID/label
* then pass along the XFRM SID as it is the most
* expressive */
*peer_sid = xfrm_sid;
else
*peer_sid = SECSID_NULL;
return rc;
}
static int get_classes_callback(void *k, void *d, void *args)
{
struct class_datum *datum = d;
......@@ -2154,6 +2244,60 @@ int security_get_allow_unknown(void)
return policydb.allow_unknown;
}
/**
* security_get_policycaps - Query the loaded policy for its capabilities
* @len: the number of capability bits
* @values: the capability bit array
*
* Description:
* Get an array of the policy capabilities in @values where each entry in
* @values is either true (1) or false (0) depending the policy's support of
* that feature. The policy capabilities are defined by the
* POLICYDB_CAPABILITY_* enums. The size of the array is stored in @len and it
* is up to the caller to free the array in @values. Returns zero on success,
* negative values on failure.
*
*/
int security_get_policycaps(int *len, int **values)
{
int rc = -ENOMEM;
unsigned int iter;
POLICY_RDLOCK;
*values = kcalloc(POLICYDB_CAPABILITY_MAX, sizeof(int), GFP_ATOMIC);
if (*values == NULL)
goto out;
for (iter = 0; iter < POLICYDB_CAPABILITY_MAX; iter++)
(*values)[iter] = ebitmap_get_bit(&policydb.policycaps, iter);
*len = POLICYDB_CAPABILITY_MAX;
out:
POLICY_RDUNLOCK;
return rc;
}
/**
* security_policycap_supported - Check for a specific policy capability
* @req_cap: capability
*
* Description:
* This function queries the currently loaded policy to see if it supports the
* capability specified by @req_cap. Returns true (1) if the capability is
* supported, false (0) if it isn't supported.
*
*/
int security_policycap_supported(unsigned int req_cap)
{
int rc;
POLICY_RDLOCK;
rc = ebitmap_get_bit(&policydb.policycaps, req_cap);
POLICY_RDUNLOCK;
return rc;
}
struct selinux_audit_rule {
u32 au_seqno;
struct context au_ctxt;
......@@ -2403,50 +2547,10 @@ void selinux_audit_set_callback(int (*callback)(void))
}
#ifdef CONFIG_NETLABEL
/*
* NetLabel cache structure
*/
#define NETLBL_CACHE(x) ((struct selinux_netlbl_cache *)(x))
#define NETLBL_CACHE_T_NONE 0
#define NETLBL_CACHE_T_SID 1
#define NETLBL_CACHE_T_MLS 2
struct selinux_netlbl_cache {
u32 type;
union {
u32 sid;
struct mls_range mls_label;
} data;
};
/**
* security_netlbl_cache_free - Free the NetLabel cached data
* @data: the data to free
*
* Description:
* This function is intended to be used as the free() callback inside the
* netlbl_lsm_cache structure.
*
*/
static void security_netlbl_cache_free(const void *data)
{
struct selinux_netlbl_cache *cache;
if (data == NULL)
return;
cache = NETLBL_CACHE(data);
switch (cache->type) {
case NETLBL_CACHE_T_MLS:
ebitmap_destroy(&cache->data.mls_label.level[0].cat);
break;
}
kfree(data);
}
/**
* security_netlbl_cache_add - Add an entry to the NetLabel cache
* @secattr: the NetLabel packet security attributes
* @ctx: the SELinux context
* @sid: the SELinux SID
*
* Description:
* Attempt to cache the context in @ctx, which was derived from the packet in
......@@ -2455,60 +2559,46 @@ static void security_netlbl_cache_free(const void *data)
*
*/
static void security_netlbl_cache_add(struct netlbl_lsm_secattr *secattr,
struct context *ctx)
u32 sid)
{
struct selinux_netlbl_cache *cache = NULL;
u32 *sid_cache;
secattr->cache = netlbl_secattr_cache_alloc(GFP_ATOMIC);
if (secattr->cache == NULL)
return;
cache = kzalloc(sizeof(*cache), GFP_ATOMIC);
if (cache == NULL)
sid_cache = kmalloc(sizeof(*sid_cache), GFP_ATOMIC);
if (sid_cache == NULL)
return;
cache->type = NETLBL_CACHE_T_MLS;
if (ebitmap_cpy(&cache->data.mls_label.level[0].cat,
&ctx->range.level[0].cat) != 0) {
kfree(cache);
secattr->cache = netlbl_secattr_cache_alloc(GFP_ATOMIC);
if (secattr->cache == NULL) {
kfree(sid_cache);
return;
}
cache->data.mls_label.level[1].cat.highbit =
cache->data.mls_label.level[0].cat.highbit;
cache->data.mls_label.level[1].cat.node =
cache->data.mls_label.level[0].cat.node;
cache->data.mls_label.level[0].sens = ctx->range.level[0].sens;
cache->data.mls_label.level[1].sens = ctx->range.level[0].sens;
secattr->cache->free = security_netlbl_cache_free;
secattr->cache->data = (void *)cache;
*sid_cache = sid;
secattr->cache->free = kfree;
secattr->cache->data = sid_cache;
secattr->flags |= NETLBL_SECATTR_CACHE;
}
/**
* security_netlbl_secattr_to_sid - Convert a NetLabel secattr to a SELinux SID
* @secattr: the NetLabel packet security attributes
* @base_sid: the SELinux SID to use as a context for MLS only attributes
* @sid: the SELinux SID
*
* Description:
* Convert the given NetLabel security attributes in @secattr into a
* SELinux SID. If the @secattr field does not contain a full SELinux
* SID/context then use the context in @base_sid as the foundation. If
* possibile the 'cache' field of @secattr is set and the CACHE flag is set;
* this is to allow the @secattr to be used by NetLabel to cache the secattr to
* SID conversion for future lookups. Returns zero on success, negative
* values on failure.
* SID/context then use SECINITSID_NETMSG as the foundation. If possibile the
* 'cache' field of @secattr is set and the CACHE flag is set; this is to
* allow the @secattr to be used by NetLabel to cache the secattr to SID
* conversion for future lookups. Returns zero on success, negative values on
* failure.
*
*/
int security_netlbl_secattr_to_sid(struct netlbl_lsm_secattr *secattr,
u32 base_sid,
u32 *sid)
{
int rc = -EIDRM;
struct context *ctx;
struct context ctx_new;
struct selinux_netlbl_cache *cache;
if (!ss_initialized) {
*sid = SECSID_NULL;
......@@ -2518,40 +2608,13 @@ int security_netlbl_secattr_to_sid(struct netlbl_lsm_secattr *secattr,
POLICY_RDLOCK;
if (secattr->flags & NETLBL_SECATTR_CACHE) {
cache = NETLBL_CACHE(secattr->cache->data);
switch (cache->type) {
case NETLBL_CACHE_T_SID:
*sid = cache->data.sid;
rc = 0;
break;
case NETLBL_CACHE_T_MLS:
ctx = sidtab_search(&sidtab, base_sid);
if (ctx == NULL)
goto netlbl_secattr_to_sid_return;
ctx_new.user = ctx->user;
ctx_new.role = ctx->role;
ctx_new.type = ctx->type;
ctx_new.range.level[0].sens =
cache->data.mls_label.level[0].sens;
ctx_new.range.level[0].cat.highbit =
cache->data.mls_label.level[0].cat.highbit;
ctx_new.range.level[0].cat.node =
cache->data.mls_label.level[0].cat.node;
ctx_new.range.level[1].sens =
cache->data.mls_label.level[1].sens;
ctx_new.range.level[1].cat.highbit =
cache->data.mls_label.level[1].cat.highbit;
ctx_new.range.level[1].cat.node =
cache->data.mls_label.level[1].cat.node;
rc = sidtab_context_to_sid(&sidtab, &ctx_new, sid);
break;
default:
goto netlbl_secattr_to_sid_return;
}
*sid = *(u32 *)secattr->cache->data;
rc = 0;
} else if (secattr->flags & NETLBL_SECATTR_SECID) {
*sid = secattr->attr.secid;
rc = 0;
} else if (secattr->flags & NETLBL_SECATTR_MLS_LVL) {
ctx = sidtab_search(&sidtab, base_sid);
ctx = sidtab_search(&sidtab, SECINITSID_NETMSG);
if (ctx == NULL)
goto netlbl_secattr_to_sid_return;
......@@ -2561,7 +2624,7 @@ int security_netlbl_secattr_to_sid(struct netlbl_lsm_secattr *secattr,
mls_import_netlbl_lvl(&ctx_new, secattr);
if (secattr->flags & NETLBL_SECATTR_MLS_CAT) {
if (ebitmap_netlbl_import(&ctx_new.range.level[0].cat,
secattr->mls_cat) != 0)
secattr->attr.mls.cat) != 0)
goto netlbl_secattr_to_sid_return;
ctx_new.range.level[1].cat.highbit =
ctx_new.range.level[0].cat.highbit;
......@@ -2578,7 +2641,7 @@ int security_netlbl_secattr_to_sid(struct netlbl_lsm_secattr *secattr,
if (rc != 0)
goto netlbl_secattr_to_sid_return_cleanup;
security_netlbl_cache_add(secattr, &ctx_new);
security_netlbl_cache_add(secattr, *sid);
ebitmap_destroy(&ctx_new.range.level[0].cat);
} else {
......
......@@ -46,11 +46,14 @@
#include <net/checksum.h>
#include <net/udp.h>
#include <asm/semaphore.h>
#include <asm/atomic.h>
#include "avc.h"
#include "objsec.h"
#include "xfrm.h"
/* Labeled XFRM instance counter */
atomic_t selinux_xfrm_refcount = ATOMIC_INIT(0);
/*
* Returns true if an LSM/SELinux context
......@@ -293,6 +296,9 @@ int selinux_xfrm_policy_alloc(struct xfrm_policy *xp,
BUG_ON(!uctx);
err = selinux_xfrm_sec_ctx_alloc(&xp->security, uctx, 0);
if (err == 0)
atomic_inc(&selinux_xfrm_refcount);
return err;
}
......@@ -340,10 +346,13 @@ int selinux_xfrm_policy_delete(struct xfrm_policy *xp)
struct xfrm_sec_ctx *ctx = xp->security;
int rc = 0;
if (ctx)
if (ctx) {
rc = avc_has_perm(tsec->sid, ctx->ctx_sid,
SECCLASS_ASSOCIATION,
ASSOCIATION__SETCONTEXT, NULL);
if (rc == 0)
atomic_dec(&selinux_xfrm_refcount);
}
return rc;
}
......@@ -360,6 +369,8 @@ int selinux_xfrm_state_alloc(struct xfrm_state *x, struct xfrm_user_sec_ctx *uct
BUG_ON(!x);
err = selinux_xfrm_sec_ctx_alloc(&x->security, uctx, secid);
if (err == 0)
atomic_inc(&selinux_xfrm_refcount);
return err;
}
......@@ -382,10 +393,13 @@ int selinux_xfrm_state_delete(struct xfrm_state *x)
struct xfrm_sec_ctx *ctx = x->security;
int rc = 0;
if (ctx)
if (ctx) {
rc = avc_has_perm(tsec->sid, ctx->ctx_sid,
SECCLASS_ASSOCIATION,
ASSOCIATION__SETCONTEXT, NULL);
if (rc == 0)
atomic_dec(&selinux_xfrm_refcount);
}
return rc;
}
......
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