提交 93db6286 编写于 作者: J James Morris

Merge branch 'next' into for-linus

......@@ -954,72 +954,63 @@ EXPORT_SYMBOL_GPL(tpm_store_cancel);
/*
* Device file system interface to the TPM
*
* It's assured that the chip will be opened just once,
* by the check of is_open variable, which is protected
* by driver_lock.
*/
int tpm_open(struct inode *inode, struct file *file)
{
int rc = 0, minor = iminor(inode);
int minor = iminor(inode);
struct tpm_chip *chip = NULL, *pos;
lock_kernel();
spin_lock(&driver_lock);
list_for_each_entry(pos, &tpm_chip_list, list) {
rcu_read_lock();
list_for_each_entry_rcu(pos, &tpm_chip_list, list) {
if (pos->vendor.miscdev.minor == minor) {
chip = pos;
get_device(chip->dev);
break;
}
}
rcu_read_unlock();
if (chip == NULL) {
rc = -ENODEV;
goto err_out;
}
if (!chip)
return -ENODEV;
if (chip->num_opens) {
if (test_and_set_bit(0, &chip->is_open)) {
dev_dbg(chip->dev, "Another process owns this TPM\n");
rc = -EBUSY;
goto err_out;
put_device(chip->dev);
return -EBUSY;
}
chip->num_opens++;
get_device(chip->dev);
spin_unlock(&driver_lock);
chip->data_buffer = kmalloc(TPM_BUFSIZE * sizeof(u8), GFP_KERNEL);
if (chip->data_buffer == NULL) {
chip->num_opens--;
clear_bit(0, &chip->is_open);
put_device(chip->dev);
unlock_kernel();
return -ENOMEM;
}
atomic_set(&chip->data_pending, 0);
file->private_data = chip;
unlock_kernel();
return 0;
err_out:
spin_unlock(&driver_lock);
unlock_kernel();
return rc;
}
EXPORT_SYMBOL_GPL(tpm_open);
/*
* Called on file close
*/
int tpm_release(struct inode *inode, struct file *file)
{
struct tpm_chip *chip = file->private_data;
del_singleshot_timer_sync(&chip->user_read_timer);
flush_scheduled_work();
spin_lock(&driver_lock);
file->private_data = NULL;
del_singleshot_timer_sync(&chip->user_read_timer);
atomic_set(&chip->data_pending, 0);
chip->num_opens--;
put_device(chip->dev);
kfree(chip->data_buffer);
spin_unlock(&driver_lock);
clear_bit(0, &chip->is_open);
put_device(chip->dev);
return 0;
}
EXPORT_SYMBOL_GPL(tpm_release);
......@@ -1093,13 +1084,11 @@ void tpm_remove_hardware(struct device *dev)
}
spin_lock(&driver_lock);
list_del(&chip->list);
list_del_rcu(&chip->list);
spin_unlock(&driver_lock);
synchronize_rcu();
misc_deregister(&chip->vendor.miscdev);
sysfs_remove_group(&dev->kobj, chip->vendor.attr_group);
tpm_bios_log_teardown(chip->bios_dir);
......@@ -1144,25 +1133,33 @@ int tpm_pm_resume(struct device *dev)
}
EXPORT_SYMBOL_GPL(tpm_pm_resume);
/* In case vendor provided release function, call it too.*/
void tpm_dev_vendor_release(struct tpm_chip *chip)
{
if (chip->vendor.release)
chip->vendor.release(chip->dev);
clear_bit(chip->dev_num, dev_mask);
kfree(chip->vendor.miscdev.name);
}
EXPORT_SYMBOL_GPL(tpm_dev_vendor_release);
/*
* Once all references to platform device are down to 0,
* release all allocated structures.
* In case vendor provided release function,
* call it too.
*/
static void tpm_dev_release(struct device *dev)
{
struct tpm_chip *chip = dev_get_drvdata(dev);
if (chip->vendor.release)
chip->vendor.release(dev);
tpm_dev_vendor_release(chip);
chip->release(dev);
clear_bit(chip->dev_num, dev_mask);
kfree(chip->vendor.miscdev.name);
kfree(chip);
}
EXPORT_SYMBOL_GPL(tpm_dev_release);
/*
* Called from tpm_<specific>.c probe function only for devices
......@@ -1171,8 +1168,8 @@ static void tpm_dev_release(struct device *dev)
* upon errant exit from this function specific probe function should call
* pci_disable_device
*/
struct tpm_chip *tpm_register_hardware(struct device *dev, const struct tpm_vendor_specific
*entry)
struct tpm_chip *tpm_register_hardware(struct device *dev,
const struct tpm_vendor_specific *entry)
{
#define DEVNAME_SIZE 7
......@@ -1231,21 +1228,20 @@ struct tpm_chip *tpm_register_hardware(struct device *dev, const struct tpm_vend
return NULL;
}
spin_lock(&driver_lock);
list_add(&chip->list, &tpm_chip_list);
spin_unlock(&driver_lock);
if (sysfs_create_group(&dev->kobj, chip->vendor.attr_group)) {
list_del(&chip->list);
misc_deregister(&chip->vendor.miscdev);
put_device(chip->dev);
return NULL;
}
chip->bios_dir = tpm_bios_log_setup(devname);
/* Make chip available */
spin_lock(&driver_lock);
list_add_rcu(&chip->list, &tpm_chip_list);
spin_unlock(&driver_lock);
return chip;
}
EXPORT_SYMBOL_GPL(tpm_register_hardware);
......
......@@ -90,7 +90,7 @@ struct tpm_chip {
struct device *dev; /* Device stuff */
int dev_num; /* /dev/tpm# */
int num_opens; /* only one allowed */
unsigned long is_open; /* only one allowed */
int time_expired;
/* Data passed to and from the tpm via the read/write calls */
......@@ -132,6 +132,7 @@ extern struct tpm_chip* tpm_register_hardware(struct device *,
const struct tpm_vendor_specific *);
extern int tpm_open(struct inode *, struct file *);
extern int tpm_release(struct inode *, struct file *);
extern void tpm_dev_vendor_release(struct tpm_chip *);
extern ssize_t tpm_write(struct file *, const char __user *, size_t,
loff_t *);
extern ssize_t tpm_read(struct file *, char __user *, size_t, loff_t *);
......
......@@ -630,12 +630,23 @@ static struct pnp_device_id tpm_pnp_tbl[] __devinitdata = {
{"", 0} /* Terminator */
};
static __devexit void tpm_tis_pnp_remove(struct pnp_dev *dev)
{
struct tpm_chip *chip = pnp_get_drvdata(dev);
tpm_dev_vendor_release(chip);
kfree(chip);
}
static struct pnp_driver tis_pnp_driver = {
.name = "tpm_tis",
.id_table = tpm_pnp_tbl,
.probe = tpm_tis_pnp_init,
.suspend = tpm_tis_pnp_suspend,
.resume = tpm_tis_pnp_resume,
.remove = tpm_tis_pnp_remove,
};
#define TIS_HID_USR_IDX sizeof(tpm_pnp_tbl)/sizeof(struct pnp_device_id) -2
......@@ -683,6 +694,7 @@ static void __exit cleanup_tis(void)
spin_lock(&tis_lock);
list_for_each_entry_safe(i, j, &tis_chips, list) {
chip = to_tpm_chip(i);
tpm_remove_hardware(chip->dev);
iowrite32(~TPM_GLOBAL_INT_ENABLE &
ioread32(chip->vendor.iobase +
TPM_INT_ENABLE(chip->vendor.
......@@ -694,9 +706,9 @@ static void __exit cleanup_tis(void)
free_irq(chip->vendor.irq, chip);
iounmap(i->iobase);
list_del(&i->list);
tpm_remove_hardware(chip->dev);
}
spin_unlock(&tis_lock);
if (force) {
platform_device_unregister(pdev);
driver_unregister(&tis_drv);
......
......@@ -40,11 +40,12 @@
#include <linux/net.h>
#include <linux/skbuff.h>
#include <net/netlabel.h>
#include <asm/atomic.h>
/* known doi values */
#define CIPSO_V4_DOI_UNKNOWN 0x00000000
/* tag types */
/* standard tag types */
#define CIPSO_V4_TAG_INVALID 0
#define CIPSO_V4_TAG_RBITMAP 1
#define CIPSO_V4_TAG_ENUM 2
......@@ -52,10 +53,14 @@
#define CIPSO_V4_TAG_PBITMAP 6
#define CIPSO_V4_TAG_FREEFORM 7
/* non-standard tag types (tags > 127) */
#define CIPSO_V4_TAG_LOCAL 128
/* doi mapping types */
#define CIPSO_V4_MAP_UNKNOWN 0
#define CIPSO_V4_MAP_STD 1
#define CIPSO_V4_MAP_TRANS 1
#define CIPSO_V4_MAP_PASS 2
#define CIPSO_V4_MAP_LOCAL 3
/* limits */
#define CIPSO_V4_MAX_REM_LVLS 255
......@@ -79,10 +84,9 @@ struct cipso_v4_doi {
} map;
u8 tags[CIPSO_V4_TAG_MAXCNT];
u32 valid;
atomic_t refcount;
struct list_head list;
struct rcu_head rcu;
struct list_head dom_list;
};
/* Standard CIPSO mapping table */
......@@ -128,25 +132,26 @@ extern int cipso_v4_rbm_strictvalid;
#ifdef CONFIG_NETLABEL
int cipso_v4_doi_add(struct cipso_v4_doi *doi_def);
int cipso_v4_doi_remove(u32 doi,
struct netlbl_audit *audit_info,
void (*callback) (struct rcu_head * head));
void cipso_v4_doi_free(struct cipso_v4_doi *doi_def);
int cipso_v4_doi_remove(u32 doi, struct netlbl_audit *audit_info);
struct cipso_v4_doi *cipso_v4_doi_getdef(u32 doi);
void cipso_v4_doi_putdef(struct cipso_v4_doi *doi_def);
int cipso_v4_doi_walk(u32 *skip_cnt,
int (*callback) (struct cipso_v4_doi *doi_def, void *arg),
void *cb_arg);
int cipso_v4_doi_domhsh_add(struct cipso_v4_doi *doi_def, const char *domain);
int cipso_v4_doi_domhsh_remove(struct cipso_v4_doi *doi_def,
const char *domain);
#else
static inline int cipso_v4_doi_add(struct cipso_v4_doi *doi_def)
{
return -ENOSYS;
}
static inline void cipso_v4_doi_free(struct cipso_v4_doi *doi_def)
{
return;
}
static inline int cipso_v4_doi_remove(u32 doi,
struct netlbl_audit *audit_info,
void (*callback) (struct rcu_head * head))
struct netlbl_audit *audit_info)
{
return 0;
}
......@@ -206,10 +211,15 @@ void cipso_v4_error(struct sk_buff *skb, int error, u32 gateway);
int cipso_v4_sock_setattr(struct sock *sk,
const struct cipso_v4_doi *doi_def,
const struct netlbl_lsm_secattr *secattr);
void cipso_v4_sock_delattr(struct sock *sk);
int cipso_v4_sock_getattr(struct sock *sk, struct netlbl_lsm_secattr *secattr);
int cipso_v4_skbuff_setattr(struct sk_buff *skb,
const struct cipso_v4_doi *doi_def,
const struct netlbl_lsm_secattr *secattr);
int cipso_v4_skbuff_delattr(struct sk_buff *skb);
int cipso_v4_skbuff_getattr(const struct sk_buff *skb,
struct netlbl_lsm_secattr *secattr);
int cipso_v4_validate(unsigned char **option);
int cipso_v4_validate(const struct sk_buff *skb, unsigned char **option);
#else
static inline void cipso_v4_error(struct sk_buff *skb,
int error,
......@@ -225,19 +235,36 @@ static inline int cipso_v4_sock_setattr(struct sock *sk,
return -ENOSYS;
}
static inline void cipso_v4_sock_delattr(struct sock *sk)
{
}
static inline int cipso_v4_sock_getattr(struct sock *sk,
struct netlbl_lsm_secattr *secattr)
{
return -ENOSYS;
}
static inline int cipso_v4_skbuff_setattr(struct sk_buff *skb,
const struct cipso_v4_doi *doi_def,
const struct netlbl_lsm_secattr *secattr)
{
return -ENOSYS;
}
static inline int cipso_v4_skbuff_delattr(struct sk_buff *skb)
{
return -ENOSYS;
}
static inline int cipso_v4_skbuff_getattr(const struct sk_buff *skb,
struct netlbl_lsm_secattr *secattr)
{
return -ENOSYS;
}
static inline int cipso_v4_validate(unsigned char **option)
static inline int cipso_v4_validate(const struct sk_buff *skb,
unsigned char **option)
{
return -ENOSYS;
}
......
......@@ -9,7 +9,7 @@
*/
/*
* (c) Copyright Hewlett-Packard Development Company, L.P., 2006
* (c) Copyright Hewlett-Packard Development Company, L.P., 2006, 2008
*
* 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
......@@ -72,8 +72,10 @@ struct cipso_v4_doi;
/* NetLabel NETLINK protocol version
* 1: initial version
* 2: added static labels for unlabeled connections
* 3: network selectors added to the NetLabel/LSM domain mapping and the
* CIPSO_V4_MAP_LOCAL CIPSO mapping was added
*/
#define NETLBL_PROTO_VERSION 2
#define NETLBL_PROTO_VERSION 3
/* NetLabel NETLINK types/families */
#define NETLBL_NLTYPE_NONE 0
......@@ -87,6 +89,8 @@ struct cipso_v4_doi;
#define NETLBL_NLTYPE_CIPSOV6_NAME "NLBL_CIPSOv6"
#define NETLBL_NLTYPE_UNLABELED 5
#define NETLBL_NLTYPE_UNLABELED_NAME "NLBL_UNLBL"
#define NETLBL_NLTYPE_ADDRSELECT 6
#define NETLBL_NLTYPE_ADDRSELECT_NAME "NLBL_ADRSEL"
/*
* NetLabel - Kernel API for accessing the network packet label mappings.
......@@ -200,7 +204,7 @@ struct netlbl_lsm_secattr {
u32 type;
char *domain;
struct netlbl_lsm_cache *cache;
union {
struct {
struct {
struct netlbl_lsm_secattr_catmap *cat;
u32 lvl;
......@@ -352,12 +356,9 @@ static inline void netlbl_secattr_free(struct netlbl_lsm_secattr *secattr)
int netlbl_cfg_map_del(const char *domain, struct netlbl_audit *audit_info);
int netlbl_cfg_unlbl_add_map(const char *domain,
struct netlbl_audit *audit_info);
int netlbl_cfg_cipsov4_add(struct cipso_v4_doi *doi_def,
struct netlbl_audit *audit_info);
int netlbl_cfg_cipsov4_add_map(struct cipso_v4_doi *doi_def,
const char *domain,
struct netlbl_audit *audit_info);
int netlbl_cfg_cipsov4_del(u32 doi, struct netlbl_audit *audit_info);
/*
* LSM security attribute operations
......@@ -380,12 +381,19 @@ int netlbl_secattr_catmap_setrng(struct netlbl_lsm_secattr_catmap *catmap,
int netlbl_enabled(void);
int netlbl_sock_setattr(struct sock *sk,
const struct netlbl_lsm_secattr *secattr);
void netlbl_sock_delattr(struct sock *sk);
int netlbl_sock_getattr(struct sock *sk,
struct netlbl_lsm_secattr *secattr);
int netlbl_conn_setattr(struct sock *sk,
struct sockaddr *addr,
const struct netlbl_lsm_secattr *secattr);
int netlbl_skbuff_setattr(struct sk_buff *skb,
u16 family,
const 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);
void netlbl_skbuff_err(struct sk_buff *skb, int error, int gateway);
/*
* LSM label mapping cache operations
......@@ -404,22 +412,12 @@ static inline int netlbl_cfg_unlbl_add_map(const char *domain,
{
return -ENOSYS;
}
static inline int netlbl_cfg_cipsov4_add(struct cipso_v4_doi *doi_def,
struct netlbl_audit *audit_info)
{
return -ENOSYS;
}
static inline int netlbl_cfg_cipsov4_add_map(struct cipso_v4_doi *doi_def,
const char *domain,
struct netlbl_audit *audit_info)
{
return -ENOSYS;
}
static inline int netlbl_cfg_cipsov4_del(u32 doi,
struct netlbl_audit *audit_info)
{
return -ENOSYS;
}
static inline int netlbl_secattr_catmap_walk(
struct netlbl_lsm_secattr_catmap *catmap,
u32 offset)
......@@ -456,18 +454,35 @@ static inline int netlbl_sock_setattr(struct sock *sk,
{
return -ENOSYS;
}
static inline void netlbl_sock_delattr(struct sock *sk)
{
}
static inline int netlbl_sock_getattr(struct sock *sk,
struct netlbl_lsm_secattr *secattr)
{
return -ENOSYS;
}
static inline int netlbl_conn_setattr(struct sock *sk,
struct sockaddr *addr,
const struct netlbl_lsm_secattr *secattr)
{
return -ENOSYS;
}
static inline int netlbl_skbuff_setattr(struct sk_buff *skb,
u16 family,
const struct netlbl_lsm_secattr *secattr)
{
return -ENOSYS;
}
static inline int netlbl_skbuff_getattr(const struct sk_buff *skb,
u16 family,
struct netlbl_lsm_secattr *secattr)
{
return -ENOSYS;
}
static inline void netlbl_skbuff_err(struct sk_buff *skb, int error)
static inline void netlbl_skbuff_err(struct sk_buff *skb,
int error,
int gateway)
{
return;
}
......
此差异已折叠。
......@@ -438,7 +438,7 @@ int ip_options_compile(struct net *net,
goto error;
}
opt->cipso = optptr - iph;
if (cipso_v4_validate(&optptr)) {
if (cipso_v4_validate(skb, &optptr)) {
pp_ptr = optptr;
goto error;
}
......
......@@ -5,7 +5,8 @@
#
# base objects
obj-y := netlabel_user.o netlabel_kapi.o netlabel_domainhash.o
obj-y := netlabel_user.o netlabel_kapi.o
obj-y += netlabel_domainhash.o netlabel_addrlist.o
# management objects
obj-y += netlabel_mgmt.o
......
/*
* NetLabel Network Address Lists
*
* This file contains network address list functions used to manage ordered
* lists of network addresses for use by the NetLabel subsystem. The NetLabel
* system manages static and dynamic label mappings for network protocols such
* as CIPSO and RIPSO.
*
* Author: Paul Moore <paul.moore@hp.com>
*
*/
/*
* (c) Copyright Hewlett-Packard Development Company, L.P., 2008
*
* 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#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 <linux/audit.h>
#include "netlabel_addrlist.h"
/*
* Address List Functions
*/
/**
* netlbl_af4list_search - Search for a matching IPv4 address entry
* @addr: IPv4 address
* @head: the list head
*
* Description:
* Searches the IPv4 address list given by @head. 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.
*
*/
struct netlbl_af4list *netlbl_af4list_search(__be32 addr,
struct list_head *head)
{
struct netlbl_af4list *iter;
list_for_each_entry_rcu(iter, head, list)
if (iter->valid && (addr & iter->mask) == iter->addr)
return iter;
return NULL;
}
/**
* netlbl_af4list_search_exact - Search for an exact IPv4 address entry
* @addr: IPv4 address
* @mask: IPv4 address mask
* @head: the list head
*
* Description:
* Searches the IPv4 address list given by @head. If an exact match if found
* it is returned, otherwise NULL is returned. The caller is responsible for
* calling the rcu_read_[un]lock() functions.
*
*/
struct netlbl_af4list *netlbl_af4list_search_exact(__be32 addr,
__be32 mask,
struct list_head *head)
{
struct netlbl_af4list *iter;
list_for_each_entry_rcu(iter, head, list)
if (iter->valid && iter->addr == addr && iter->mask == mask)
return iter;
return NULL;
}
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
/**
* netlbl_af6list_search - Search for a matching IPv6 address entry
* @addr: IPv6 address
* @head: the list head
*
* Description:
* Searches the IPv6 address list given by @head. 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.
*
*/
struct netlbl_af6list *netlbl_af6list_search(const struct in6_addr *addr,
struct list_head *head)
{
struct netlbl_af6list *iter;
list_for_each_entry_rcu(iter, head, list)
if (iter->valid &&
ipv6_masked_addr_cmp(&iter->addr, &iter->mask, addr) == 0)
return iter;
return NULL;
}
/**
* netlbl_af6list_search_exact - Search for an exact IPv6 address entry
* @addr: IPv6 address
* @mask: IPv6 address mask
* @head: the list head
*
* Description:
* Searches the IPv6 address list given by @head. If an exact match if found
* it is returned, otherwise NULL is returned. The caller is responsible for
* calling the rcu_read_[un]lock() functions.
*
*/
struct netlbl_af6list *netlbl_af6list_search_exact(const struct in6_addr *addr,
const struct in6_addr *mask,
struct list_head *head)
{
struct netlbl_af6list *iter;
list_for_each_entry_rcu(iter, head, list)
if (iter->valid &&
ipv6_addr_equal(&iter->addr, addr) &&
ipv6_addr_equal(&iter->mask, mask))
return iter;
return NULL;
}
#endif /* IPv6 */
/**
* netlbl_af4list_add - Add a new IPv4 address entry to a list
* @entry: address entry
* @head: the list head
*
* Description:
* Add a new address entry to the list pointed to by @head. On success zero is
* returned, otherwise a negative value is returned. The caller is responsible
* for calling the necessary locking functions.
*
*/
int netlbl_af4list_add(struct netlbl_af4list *entry, struct list_head *head)
{
struct netlbl_af4list *iter;
iter = netlbl_af4list_search(entry->addr, head);
if (iter != NULL &&
iter->addr == entry->addr && iter->mask == entry->mask)
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, head, list)
if (iter->valid &&
ntohl(entry->mask) > ntohl(iter->mask)) {
__list_add_rcu(&entry->list,
iter->list.prev,
&iter->list);
return 0;
}
list_add_tail_rcu(&entry->list, head);
return 0;
}
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
/**
* netlbl_af6list_add - Add a new IPv6 address entry to a list
* @entry: address entry
* @head: the list head
*
* Description:
* Add a new address entry to the list pointed to by @head. On success zero is
* returned, otherwise a negative value is returned. The caller is responsible
* for calling the necessary locking functions.
*
*/
int netlbl_af6list_add(struct netlbl_af6list *entry, struct list_head *head)
{
struct netlbl_af6list *iter;
iter = netlbl_af6list_search(&entry->addr, head);
if (iter != NULL &&
ipv6_addr_equal(&iter->addr, &entry->addr) &&
ipv6_addr_equal(&iter->mask, &entry->mask))
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, head, list)
if (iter->valid &&
ipv6_addr_cmp(&entry->mask, &iter->mask) > 0) {
__list_add_rcu(&entry->list,
iter->list.prev,
&iter->list);
return 0;
}
list_add_tail_rcu(&entry->list, head);
return 0;
}
#endif /* IPv6 */
/**
* netlbl_af4list_remove_entry - Remove an IPv4 address entry
* @entry: address entry
*
* Description:
* Remove the specified IP address entry. The caller is responsible for
* calling the necessary locking functions.
*
*/
void netlbl_af4list_remove_entry(struct netlbl_af4list *entry)
{
entry->valid = 0;
list_del_rcu(&entry->list);
}
/**
* netlbl_af4list_remove - Remove an IPv4 address entry
* @addr: IP address
* @mask: IP address mask
* @head: the list head
*
* Description:
* Remove an IP address entry from the list pointed to by @head. Returns the
* entry on success, NULL on failure. The caller is responsible for calling
* the necessary locking functions.
*
*/
struct netlbl_af4list *netlbl_af4list_remove(__be32 addr, __be32 mask,
struct list_head *head)
{
struct netlbl_af4list *entry;
entry = netlbl_af4list_search(addr, head);
if (entry != NULL && entry->addr == addr && entry->mask == mask) {
netlbl_af4list_remove_entry(entry);
return entry;
}
return NULL;
}
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
/**
* netlbl_af6list_remove_entry - Remove an IPv6 address entry
* @entry: address entry
*
* Description:
* Remove the specified IP address entry. The caller is responsible for
* calling the necessary locking functions.
*
*/
void netlbl_af6list_remove_entry(struct netlbl_af6list *entry)
{
entry->valid = 0;
list_del_rcu(&entry->list);
}
/**
* netlbl_af6list_remove - Remove an IPv6 address entry
* @addr: IP address
* @mask: IP address mask
* @head: the list head
*
* Description:
* Remove an IP address entry from the list pointed to by @head. Returns the
* entry on success, NULL on failure. The caller is responsible for calling
* the necessary locking functions.
*
*/
struct netlbl_af6list *netlbl_af6list_remove(const struct in6_addr *addr,
const struct in6_addr *mask,
struct list_head *head)
{
struct netlbl_af6list *entry;
entry = netlbl_af6list_search(addr, head);
if (entry != NULL &&
ipv6_addr_equal(&entry->addr, addr) &&
ipv6_addr_equal(&entry->mask, mask)) {
netlbl_af6list_remove_entry(entry);
return entry;
}
return NULL;
}
#endif /* IPv6 */
/*
* Audit Helper Functions
*/
/**
* netlbl_af4list_audit_addr - Audit an IPv4 address
* @audit_buf: audit buffer
* @src: true if source address, false if destination
* @dev: network interface
* @addr: IP address
* @mask: IP address mask
*
* Description:
* Write the IPv4 address and address mask, if necessary, to @audit_buf.
*
*/
void netlbl_af4list_audit_addr(struct audit_buffer *audit_buf,
int src, const char *dev,
__be32 addr, __be32 mask)
{
u32 mask_val = ntohl(mask);
char *dir = (src ? "src" : "dst");
if (dev != NULL)
audit_log_format(audit_buf, " netif=%s", dev);
audit_log_format(audit_buf, " %s=" NIPQUAD_FMT, dir, NIPQUAD(addr));
if (mask_val != 0xffffffff) {
u32 mask_len = 0;
while (mask_val > 0) {
mask_val <<= 1;
mask_len++;
}
audit_log_format(audit_buf, " %s_prefixlen=%d", dir, mask_len);
}
}
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
/**
* netlbl_af6list_audit_addr - Audit an IPv6 address
* @audit_buf: audit buffer
* @src: true if source address, false if destination
* @dev: network interface
* @addr: IP address
* @mask: IP address mask
*
* Description:
* Write the IPv6 address and address mask, if necessary, to @audit_buf.
*
*/
void netlbl_af6list_audit_addr(struct audit_buffer *audit_buf,
int src,
const char *dev,
const struct in6_addr *addr,
const struct in6_addr *mask)
{
char *dir = (src ? "src" : "dst");
if (dev != NULL)
audit_log_format(audit_buf, " netif=%s", dev);
audit_log_format(audit_buf, " %s=" NIP6_FMT, dir, 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, " %s_prefixlen=%d", dir, mask_len);
}
}
#endif /* IPv6 */
/*
* NetLabel Network Address Lists
*
* This file contains network address list functions used to manage ordered
* lists of network addresses for use by the NetLabel subsystem. The NetLabel
* system manages static and dynamic label mappings for network protocols such
* as CIPSO and RIPSO.
*
* Author: Paul Moore <paul.moore@hp.com>
*
*/
/*
* (c) Copyright Hewlett-Packard Development Company, L.P., 2008
*
* 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef _NETLABEL_ADDRLIST_H
#define _NETLABEL_ADDRLIST_H
#include <linux/types.h>
#include <linux/rcupdate.h>
#include <linux/list.h>
#include <linux/in6.h>
#include <linux/audit.h>
/**
* struct netlbl_af4list - NetLabel IPv4 address list
* @addr: IPv4 address
* @mask: IPv4 address mask
* @valid: valid flag
* @list: list structure, used internally
*/
struct netlbl_af4list {
__be32 addr;
__be32 mask;
u32 valid;
struct list_head list;
};
/**
* struct netlbl_af6list - NetLabel IPv6 address list
* @addr: IPv6 address
* @mask: IPv6 address mask
* @valid: valid flag
* @list: list structure, used internally
*/
struct netlbl_af6list {
struct in6_addr addr;
struct in6_addr mask;
u32 valid;
struct list_head list;
};
#define __af4list_entry(ptr) container_of(ptr, struct netlbl_af4list, list)
static inline struct netlbl_af4list *__af4list_valid(struct list_head *s,
struct list_head *h)
{
struct list_head *i = s;
struct netlbl_af4list *n = __af4list_entry(s);
while (i != h && !n->valid) {
i = i->next;
n = __af4list_entry(i);
}
return n;
}
static inline struct netlbl_af4list *__af4list_valid_rcu(struct list_head *s,
struct list_head *h)
{
struct list_head *i = s;
struct netlbl_af4list *n = __af4list_entry(s);
while (i != h && !n->valid) {
i = rcu_dereference(i->next);
n = __af4list_entry(i);
}
return n;
}
#define netlbl_af4list_foreach(iter, head) \
for (iter = __af4list_valid((head)->next, head); \
prefetch(iter->list.next), &iter->list != (head); \
iter = __af4list_valid(iter->list.next, head))
#define netlbl_af4list_foreach_rcu(iter, head) \
for (iter = __af4list_valid_rcu((head)->next, head); \
prefetch(iter->list.next), &iter->list != (head); \
iter = __af4list_valid_rcu(iter->list.next, head))
#define netlbl_af4list_foreach_safe(iter, tmp, head) \
for (iter = __af4list_valid((head)->next, head), \
tmp = __af4list_valid(iter->list.next, head); \
&iter->list != (head); \
iter = tmp, tmp = __af4list_valid(iter->list.next, head))
int netlbl_af4list_add(struct netlbl_af4list *entry,
struct list_head *head);
struct netlbl_af4list *netlbl_af4list_remove(__be32 addr, __be32 mask,
struct list_head *head);
void netlbl_af4list_remove_entry(struct netlbl_af4list *entry);
struct netlbl_af4list *netlbl_af4list_search(__be32 addr,
struct list_head *head);
struct netlbl_af4list *netlbl_af4list_search_exact(__be32 addr,
__be32 mask,
struct list_head *head);
void netlbl_af4list_audit_addr(struct audit_buffer *audit_buf,
int src, const char *dev,
__be32 addr, __be32 mask);
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
#define __af6list_entry(ptr) container_of(ptr, struct netlbl_af6list, list)
static inline struct netlbl_af6list *__af6list_valid(struct list_head *s,
struct list_head *h)
{
struct list_head *i = s;
struct netlbl_af6list *n = __af6list_entry(s);
while (i != h && !n->valid) {
i = i->next;
n = __af6list_entry(i);
}
return n;
}
static inline struct netlbl_af6list *__af6list_valid_rcu(struct list_head *s,
struct list_head *h)
{
struct list_head *i = s;
struct netlbl_af6list *n = __af6list_entry(s);
while (i != h && !n->valid) {
i = rcu_dereference(i->next);
n = __af6list_entry(i);
}
return n;
}
#define netlbl_af6list_foreach(iter, head) \
for (iter = __af6list_valid((head)->next, head); \
prefetch(iter->list.next), &iter->list != (head); \
iter = __af6list_valid(iter->list.next, head))
#define netlbl_af6list_foreach_rcu(iter, head) \
for (iter = __af6list_valid_rcu((head)->next, head); \
prefetch(iter->list.next), &iter->list != (head); \
iter = __af6list_valid_rcu(iter->list.next, head))
#define netlbl_af6list_foreach_safe(iter, tmp, head) \
for (iter = __af6list_valid((head)->next, head), \
tmp = __af6list_valid(iter->list.next, head); \
&iter->list != (head); \
iter = tmp, tmp = __af6list_valid(iter->list.next, head))
int netlbl_af6list_add(struct netlbl_af6list *entry,
struct list_head *head);
struct netlbl_af6list *netlbl_af6list_remove(const struct in6_addr *addr,
const struct in6_addr *mask,
struct list_head *head);
void netlbl_af6list_remove_entry(struct netlbl_af6list *entry);
struct netlbl_af6list *netlbl_af6list_search(const struct in6_addr *addr,
struct list_head *head);
struct netlbl_af6list *netlbl_af6list_search_exact(const struct in6_addr *addr,
const struct in6_addr *mask,
struct list_head *head);
void netlbl_af6list_audit_addr(struct audit_buffer *audit_buf,
int src,
const char *dev,
const struct in6_addr *addr,
const struct in6_addr *mask);
#endif /* IPV6 */
#endif
......@@ -43,6 +43,7 @@
#include "netlabel_user.h"
#include "netlabel_cipso_v4.h"
#include "netlabel_mgmt.h"
#include "netlabel_domainhash.h"
/* Argument struct for cipso_v4_doi_walk() */
struct netlbl_cipsov4_doiwalk_arg {
......@@ -51,6 +52,12 @@ struct netlbl_cipsov4_doiwalk_arg {
u32 seq;
};
/* Argument struct for netlbl_domhsh_walk() */
struct netlbl_domhsh_walk_arg {
struct netlbl_audit *audit_info;
u32 doi;
};
/* NetLabel Generic NETLINK CIPSOv4 family */
static struct genl_family netlbl_cipsov4_gnl_family = {
.id = GENL_ID_GENERATE,
......@@ -80,32 +87,6 @@ static const struct nla_policy netlbl_cipsov4_genl_policy[NLBL_CIPSOV4_A_MAX + 1
* Helper Functions
*/
/**
* netlbl_cipsov4_doi_free - Frees a CIPSO V4 DOI definition
* @entry: the entry's RCU field
*
* Description:
* This function is designed to be used as a callback to the call_rcu()
* function so that the memory allocated to the DOI definition can be released
* safely.
*
*/
void netlbl_cipsov4_doi_free(struct rcu_head *entry)
{
struct cipso_v4_doi *ptr;
ptr = container_of(entry, struct cipso_v4_doi, rcu);
switch (ptr->type) {
case CIPSO_V4_MAP_STD:
kfree(ptr->map.std->lvl.cipso);
kfree(ptr->map.std->lvl.local);
kfree(ptr->map.std->cat.cipso);
kfree(ptr->map.std->cat.local);
break;
}
kfree(ptr);
}
/**
* netlbl_cipsov4_add_common - Parse the common sections of a ADD message
* @info: the Generic NETLINK info block
......@@ -151,9 +132,9 @@ static int netlbl_cipsov4_add_common(struct genl_info *info,
* @info: the Generic NETLINK info block
*
* Description:
* Create a new CIPSO_V4_MAP_STD DOI definition based on the given ADD message
* and add it to the CIPSO V4 engine. Return zero on success and non-zero on
* error.
* Create a new CIPSO_V4_MAP_TRANS DOI definition based on the given ADD
* message and add it to the CIPSO V4 engine. Return zero on success and
* non-zero on error.
*
*/
static int netlbl_cipsov4_add_std(struct genl_info *info)
......@@ -183,7 +164,7 @@ static int netlbl_cipsov4_add_std(struct genl_info *info)
ret_val = -ENOMEM;
goto add_std_failure;
}
doi_def->type = CIPSO_V4_MAP_STD;
doi_def->type = CIPSO_V4_MAP_TRANS;
ret_val = netlbl_cipsov4_add_common(info, doi_def);
if (ret_val != 0)
......@@ -342,7 +323,7 @@ static int netlbl_cipsov4_add_std(struct genl_info *info)
add_std_failure:
if (doi_def)
netlbl_cipsov4_doi_free(&doi_def->rcu);
cipso_v4_doi_free(doi_def);
return ret_val;
}
......@@ -379,7 +360,44 @@ static int netlbl_cipsov4_add_pass(struct genl_info *info)
return 0;
add_pass_failure:
netlbl_cipsov4_doi_free(&doi_def->rcu);
cipso_v4_doi_free(doi_def);
return ret_val;
}
/**
* netlbl_cipsov4_add_local - Adds a CIPSO V4 DOI definition
* @info: the Generic NETLINK info block
*
* Description:
* Create a new CIPSO_V4_MAP_LOCAL DOI definition based on the given ADD
* message and add it to the CIPSO V4 engine. Return zero on success and
* non-zero on error.
*
*/
static int netlbl_cipsov4_add_local(struct genl_info *info)
{
int ret_val;
struct cipso_v4_doi *doi_def = NULL;
if (!info->attrs[NLBL_CIPSOV4_A_TAGLST])
return -EINVAL;
doi_def = kmalloc(sizeof(*doi_def), GFP_KERNEL);
if (doi_def == NULL)
return -ENOMEM;
doi_def->type = CIPSO_V4_MAP_LOCAL;
ret_val = netlbl_cipsov4_add_common(info, doi_def);
if (ret_val != 0)
goto add_local_failure;
ret_val = cipso_v4_doi_add(doi_def);
if (ret_val != 0)
goto add_local_failure;
return 0;
add_local_failure:
cipso_v4_doi_free(doi_def);
return ret_val;
}
......@@ -412,14 +430,18 @@ static int netlbl_cipsov4_add(struct sk_buff *skb, struct genl_info *info)
type = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_MTYPE]);
switch (type) {
case CIPSO_V4_MAP_STD:
type_str = "std";
case CIPSO_V4_MAP_TRANS:
type_str = "trans";
ret_val = netlbl_cipsov4_add_std(info);
break;
case CIPSO_V4_MAP_PASS:
type_str = "pass";
ret_val = netlbl_cipsov4_add_pass(info);
break;
case CIPSO_V4_MAP_LOCAL:
type_str = "local";
ret_val = netlbl_cipsov4_add_local(info);
break;
}
if (ret_val == 0)
atomic_inc(&netlabel_mgmt_protocount);
......@@ -491,7 +513,7 @@ static int netlbl_cipsov4_list(struct sk_buff *skb, struct genl_info *info)
doi_def = cipso_v4_doi_getdef(doi);
if (doi_def == NULL) {
ret_val = -EINVAL;
goto list_failure;
goto list_failure_lock;
}
ret_val = nla_put_u32(ans_skb, NLBL_CIPSOV4_A_MTYPE, doi_def->type);
......@@ -516,7 +538,7 @@ static int netlbl_cipsov4_list(struct sk_buff *skb, struct genl_info *info)
nla_nest_end(ans_skb, nla_a);
switch (doi_def->type) {
case CIPSO_V4_MAP_STD:
case CIPSO_V4_MAP_TRANS:
nla_a = nla_nest_start(ans_skb, NLBL_CIPSOV4_A_MLSLVLLST);
if (nla_a == NULL) {
ret_val = -ENOMEM;
......@@ -655,7 +677,7 @@ static int netlbl_cipsov4_listall(struct sk_buff *skb,
struct netlink_callback *cb)
{
struct netlbl_cipsov4_doiwalk_arg cb_arg;
int doi_skip = cb->args[0];
u32 doi_skip = cb->args[0];
cb_arg.nl_cb = cb;
cb_arg.skb = skb;
......@@ -667,6 +689,29 @@ static int netlbl_cipsov4_listall(struct sk_buff *skb,
return skb->len;
}
/**
* netlbl_cipsov4_remove_cb - netlbl_cipsov4_remove() callback for REMOVE
* @entry: LSM domain mapping entry
* @arg: the netlbl_domhsh_walk_arg structure
*
* Description:
* This function is intended for use by netlbl_cipsov4_remove() as the callback
* for the netlbl_domhsh_walk() function; it removes LSM domain map entries
* which are associated with the CIPSO DOI specified in @arg. Returns zero on
* success, negative values on failure.
*
*/
static int netlbl_cipsov4_remove_cb(struct netlbl_dom_map *entry, void *arg)
{
struct netlbl_domhsh_walk_arg *cb_arg = arg;
if (entry->type == NETLBL_NLTYPE_CIPSOV4 &&
entry->type_def.cipsov4->doi == cb_arg->doi)
return netlbl_domhsh_remove_entry(entry, cb_arg->audit_info);
return 0;
}
/**
* netlbl_cipsov4_remove - Handle a REMOVE message
* @skb: the NETLINK buffer
......@@ -681,8 +726,11 @@ static int netlbl_cipsov4_remove(struct sk_buff *skb, struct genl_info *info)
{
int ret_val = -EINVAL;
u32 doi = 0;
struct netlbl_domhsh_walk_arg cb_arg;
struct audit_buffer *audit_buf;
struct netlbl_audit audit_info;
u32 skip_bkt = 0;
u32 skip_chain = 0;
if (!info->attrs[NLBL_CIPSOV4_A_DOI])
return -EINVAL;
......@@ -690,11 +738,15 @@ static int netlbl_cipsov4_remove(struct sk_buff *skb, struct genl_info *info)
doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]);
netlbl_netlink_auditinfo(skb, &audit_info);
ret_val = cipso_v4_doi_remove(doi,
&audit_info,
netlbl_cipsov4_doi_free);
if (ret_val == 0)
atomic_dec(&netlabel_mgmt_protocount);
cb_arg.doi = doi;
cb_arg.audit_info = &audit_info;
ret_val = netlbl_domhsh_walk(&skip_bkt, &skip_chain,
netlbl_cipsov4_remove_cb, &cb_arg);
if (ret_val == 0 || ret_val == -ENOENT) {
ret_val = cipso_v4_doi_remove(doi, &audit_info);
if (ret_val == 0)
atomic_dec(&netlabel_mgmt_protocount);
}
audit_buf = netlbl_audit_start_common(AUDIT_MAC_CIPSOV4_DEL,
&audit_info);
......
......@@ -45,12 +45,13 @@
* NLBL_CIPSOV4_A_MTYPE
* NLBL_CIPSOV4_A_TAGLST
*
* If using CIPSO_V4_MAP_STD the following attributes are required:
* If using CIPSO_V4_MAP_TRANS the following attributes are required:
*
* NLBL_CIPSOV4_A_MLSLVLLST
* NLBL_CIPSOV4_A_MLSCATLST
*
* If using CIPSO_V4_MAP_PASS no additional attributes are required.
* If using CIPSO_V4_MAP_PASS or CIPSO_V4_MAP_LOCAL no additional attributes
* are required.
*
* o REMOVE:
* Sent by an application to remove a specific DOI mapping table from the
......@@ -76,12 +77,13 @@
* NLBL_CIPSOV4_A_MTYPE
* NLBL_CIPSOV4_A_TAGLST
*
* If using CIPSO_V4_MAP_STD the following attributes are required:
* If using CIPSO_V4_MAP_TRANS the following attributes are required:
*
* NLBL_CIPSOV4_A_MLSLVLLST
* NLBL_CIPSOV4_A_MLSCATLST
*
* If using CIPSO_V4_MAP_PASS no additional attributes are required.
* If using CIPSO_V4_MAP_PASS or CIPSO_V4_MAP_LOCAL no additional attributes
* are required.
*
* o LISTALL:
* This message is sent by an application to list the valid DOIs on the
......
......@@ -11,7 +11,7 @@
*/
/*
* (c) Copyright Hewlett-Packard Development Company, L.P., 2006
* (c) Copyright Hewlett-Packard Development Company, L.P., 2006, 2008
*
* 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
......@@ -40,6 +40,7 @@
#include <asm/bug.h>
#include "netlabel_mgmt.h"
#include "netlabel_addrlist.h"
#include "netlabel_domainhash.h"
#include "netlabel_user.h"
......@@ -72,8 +73,28 @@ static struct netlbl_dom_map *netlbl_domhsh_def = NULL;
static void netlbl_domhsh_free_entry(struct rcu_head *entry)
{
struct netlbl_dom_map *ptr;
struct netlbl_af4list *iter4;
struct netlbl_af4list *tmp4;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
struct netlbl_af6list *iter6;
struct netlbl_af6list *tmp6;
#endif /* IPv6 */
ptr = container_of(entry, struct netlbl_dom_map, rcu);
if (ptr->type == NETLBL_NLTYPE_ADDRSELECT) {
netlbl_af4list_foreach_safe(iter4, tmp4,
&ptr->type_def.addrsel->list4) {
netlbl_af4list_remove_entry(iter4);
kfree(netlbl_domhsh_addr4_entry(iter4));
}
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
netlbl_af6list_foreach_safe(iter6, tmp6,
&ptr->type_def.addrsel->list6) {
netlbl_af6list_remove_entry(iter6);
kfree(netlbl_domhsh_addr6_entry(iter6));
}
#endif /* IPv6 */
}
kfree(ptr->domain);
kfree(ptr);
}
......@@ -115,13 +136,13 @@ static u32 netlbl_domhsh_hash(const char *key)
static struct netlbl_dom_map *netlbl_domhsh_search(const char *domain)
{
u32 bkt;
struct list_head *bkt_list;
struct netlbl_dom_map *iter;
if (domain != NULL) {
bkt = netlbl_domhsh_hash(domain);
list_for_each_entry_rcu(iter,
&rcu_dereference(netlbl_domhsh)->tbl[bkt],
list)
bkt_list = &rcu_dereference(netlbl_domhsh)->tbl[bkt];
list_for_each_entry_rcu(iter, bkt_list, list)
if (iter->valid && strcmp(iter->domain, domain) == 0)
return iter;
}
......@@ -156,6 +177,69 @@ static struct netlbl_dom_map *netlbl_domhsh_search_def(const char *domain)
return entry;
}
/**
* netlbl_domhsh_audit_add - Generate an audit entry for an add event
* @entry: the entry being added
* @addr4: the IPv4 address information
* @addr6: the IPv6 address information
* @result: the result code
* @audit_info: NetLabel audit information
*
* Description:
* Generate an audit record for adding a new NetLabel/LSM mapping entry with
* the given information. Caller is responsibile for holding the necessary
* locks.
*
*/
static void netlbl_domhsh_audit_add(struct netlbl_dom_map *entry,
struct netlbl_af4list *addr4,
struct netlbl_af6list *addr6,
int result,
struct netlbl_audit *audit_info)
{
struct audit_buffer *audit_buf;
struct cipso_v4_doi *cipsov4 = NULL;
u32 type;
audit_buf = netlbl_audit_start_common(AUDIT_MAC_MAP_ADD, audit_info);
if (audit_buf != NULL) {
audit_log_format(audit_buf, " nlbl_domain=%s",
entry->domain ? entry->domain : "(default)");
if (addr4 != NULL) {
struct netlbl_domaddr4_map *map4;
map4 = netlbl_domhsh_addr4_entry(addr4);
type = map4->type;
cipsov4 = map4->type_def.cipsov4;
netlbl_af4list_audit_addr(audit_buf, 0, NULL,
addr4->addr, addr4->mask);
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
} else if (addr6 != NULL) {
struct netlbl_domaddr6_map *map6;
map6 = netlbl_domhsh_addr6_entry(addr6);
type = map6->type;
netlbl_af6list_audit_addr(audit_buf, 0, NULL,
&addr6->addr, &addr6->mask);
#endif /* IPv6 */
} else {
type = entry->type;
cipsov4 = entry->type_def.cipsov4;
}
switch (type) {
case NETLBL_NLTYPE_UNLABELED:
audit_log_format(audit_buf, " nlbl_protocol=unlbl");
break;
case NETLBL_NLTYPE_CIPSOV4:
BUG_ON(cipsov4 == NULL);
audit_log_format(audit_buf,
" nlbl_protocol=cipsov4 cipso_doi=%u",
cipsov4->doi);
break;
}
audit_log_format(audit_buf, " res=%u", result == 0 ? 1 : 0);
audit_log_end(audit_buf);
}
}
/*
* Domain Hash Table Functions
*/
......@@ -213,74 +297,106 @@ int __init netlbl_domhsh_init(u32 size)
int netlbl_domhsh_add(struct netlbl_dom_map *entry,
struct netlbl_audit *audit_info)
{
int ret_val;
u32 bkt;
struct audit_buffer *audit_buf;
switch (entry->type) {
case NETLBL_NLTYPE_UNLABELED:
ret_val = 0;
break;
case NETLBL_NLTYPE_CIPSOV4:
ret_val = cipso_v4_doi_domhsh_add(entry->type_def.cipsov4,
entry->domain);
break;
default:
return -EINVAL;
}
if (ret_val != 0)
return ret_val;
entry->valid = 1;
INIT_RCU_HEAD(&entry->rcu);
int ret_val = 0;
struct netlbl_dom_map *entry_old;
struct netlbl_af4list *iter4;
struct netlbl_af4list *tmp4;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
struct netlbl_af6list *iter6;
struct netlbl_af6list *tmp6;
#endif /* IPv6 */
rcu_read_lock();
spin_lock(&netlbl_domhsh_lock);
if (entry->domain != NULL) {
bkt = netlbl_domhsh_hash(entry->domain);
if (netlbl_domhsh_search(entry->domain) == NULL)
if (entry->domain != NULL)
entry_old = netlbl_domhsh_search(entry->domain);
else
entry_old = netlbl_domhsh_search_def(entry->domain);
if (entry_old == NULL) {
entry->valid = 1;
INIT_RCU_HEAD(&entry->rcu);
if (entry->domain != NULL) {
u32 bkt = netlbl_domhsh_hash(entry->domain);
list_add_tail_rcu(&entry->list,
&rcu_dereference(netlbl_domhsh)->tbl[bkt]);
else
ret_val = -EEXIST;
} else {
INIT_LIST_HEAD(&entry->list);
if (rcu_dereference(netlbl_domhsh_def) == NULL)
} else {
INIT_LIST_HEAD(&entry->list);
rcu_assign_pointer(netlbl_domhsh_def, entry);
else
ret_val = -EEXIST;
}
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,
" nlbl_domain=%s",
entry->domain ? entry->domain : "(default)");
switch (entry->type) {
case NETLBL_NLTYPE_UNLABELED:
audit_log_format(audit_buf, " nlbl_protocol=unlbl");
break;
case NETLBL_NLTYPE_CIPSOV4:
audit_log_format(audit_buf,
" nlbl_protocol=cipsov4 cipso_doi=%u",
entry->type_def.cipsov4->doi);
break;
}
audit_log_format(audit_buf, " res=%u", ret_val == 0 ? 1 : 0);
audit_log_end(audit_buf);
}
rcu_read_unlock();
if (ret_val != 0) {
switch (entry->type) {
case NETLBL_NLTYPE_CIPSOV4:
if (cipso_v4_doi_domhsh_remove(entry->type_def.cipsov4,
entry->domain) != 0)
BUG();
break;
if (entry->type == NETLBL_NLTYPE_ADDRSELECT) {
netlbl_af4list_foreach_rcu(iter4,
&entry->type_def.addrsel->list4)
netlbl_domhsh_audit_add(entry, iter4, NULL,
ret_val, audit_info);
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
netlbl_af6list_foreach_rcu(iter6,
&entry->type_def.addrsel->list6)
netlbl_domhsh_audit_add(entry, NULL, iter6,
ret_val, audit_info);
#endif /* IPv6 */
} else
netlbl_domhsh_audit_add(entry, NULL, NULL,
ret_val, audit_info);
} else if (entry_old->type == NETLBL_NLTYPE_ADDRSELECT &&
entry->type == NETLBL_NLTYPE_ADDRSELECT) {
struct list_head *old_list4;
struct list_head *old_list6;
old_list4 = &entry_old->type_def.addrsel->list4;
old_list6 = &entry_old->type_def.addrsel->list6;
/* we only allow the addition of address selectors if all of
* the selectors do not exist in the existing domain map */
netlbl_af4list_foreach_rcu(iter4,
&entry->type_def.addrsel->list4)
if (netlbl_af4list_search_exact(iter4->addr,
iter4->mask,
old_list4)) {
ret_val = -EEXIST;
goto add_return;
}
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
netlbl_af6list_foreach_rcu(iter6,
&entry->type_def.addrsel->list6)
if (netlbl_af6list_search_exact(&iter6->addr,
&iter6->mask,
old_list6)) {
ret_val = -EEXIST;
goto add_return;
}
#endif /* IPv6 */
netlbl_af4list_foreach_safe(iter4, tmp4,
&entry->type_def.addrsel->list4) {
netlbl_af4list_remove_entry(iter4);
iter4->valid = 1;
ret_val = netlbl_af4list_add(iter4, old_list4);
netlbl_domhsh_audit_add(entry_old, iter4, NULL,
ret_val, audit_info);
if (ret_val != 0)
goto add_return;
}
}
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
netlbl_af6list_foreach_safe(iter6, tmp6,
&entry->type_def.addrsel->list6) {
netlbl_af6list_remove_entry(iter6);
iter6->valid = 1;
ret_val = netlbl_af6list_add(iter6, old_list6);
netlbl_domhsh_audit_add(entry_old, NULL, iter6,
ret_val, audit_info);
if (ret_val != 0)
goto add_return;
}
#endif /* IPv6 */
} else
ret_val = -EINVAL;
add_return:
spin_unlock(&netlbl_domhsh_lock);
rcu_read_unlock();
return ret_val;
}
......@@ -302,35 +418,26 @@ int netlbl_domhsh_add_default(struct netlbl_dom_map *entry,
}
/**
* netlbl_domhsh_remove - Removes an entry from the domain hash table
* @domain: the domain to remove
* netlbl_domhsh_remove_entry - Removes a given entry from the domain table
* @entry: the entry to remove
* @audit_info: NetLabel audit information
*
* Description:
* Removes an entry from the domain hash table and handles any updates to the
* lower level protocol handler (i.e. CIPSO). Returns zero on success,
* negative on failure.
* lower level protocol handler (i.e. CIPSO). Caller is responsible for
* ensuring that the RCU read lock is held. Returns zero on success, negative
* on failure.
*
*/
int netlbl_domhsh_remove(const char *domain, struct netlbl_audit *audit_info)
int netlbl_domhsh_remove_entry(struct netlbl_dom_map *entry,
struct netlbl_audit *audit_info)
{
int ret_val = -ENOENT;
struct netlbl_dom_map *entry;
int ret_val = 0;
struct audit_buffer *audit_buf;
rcu_read_lock();
if (domain)
entry = netlbl_domhsh_search(domain);
else
entry = netlbl_domhsh_search_def(domain);
if (entry == NULL)
goto remove_return;
switch (entry->type) {
case NETLBL_NLTYPE_CIPSOV4:
cipso_v4_doi_domhsh_remove(entry->type_def.cipsov4,
entry->domain);
break;
}
return -ENOENT;
spin_lock(&netlbl_domhsh_lock);
if (entry->valid) {
entry->valid = 0;
......@@ -338,8 +445,8 @@ int netlbl_domhsh_remove(const char *domain, struct netlbl_audit *audit_info)
list_del_rcu(&entry->list);
else
rcu_assign_pointer(netlbl_domhsh_def, NULL);
ret_val = 0;
}
} else
ret_val = -ENOENT;
spin_unlock(&netlbl_domhsh_lock);
audit_buf = netlbl_audit_start_common(AUDIT_MAC_MAP_DEL, audit_info);
......@@ -351,10 +458,54 @@ int netlbl_domhsh_remove(const char *domain, struct netlbl_audit *audit_info)
audit_log_end(audit_buf);
}
remove_return:
rcu_read_unlock();
if (ret_val == 0)
if (ret_val == 0) {
struct netlbl_af4list *iter4;
struct netlbl_domaddr4_map *map4;
switch (entry->type) {
case NETLBL_NLTYPE_ADDRSELECT:
netlbl_af4list_foreach_rcu(iter4,
&entry->type_def.addrsel->list4) {
map4 = netlbl_domhsh_addr4_entry(iter4);
cipso_v4_doi_putdef(map4->type_def.cipsov4);
}
/* no need to check the IPv6 list since we currently
* support only unlabeled protocols for IPv6 */
break;
case NETLBL_NLTYPE_CIPSOV4:
cipso_v4_doi_putdef(entry->type_def.cipsov4);
break;
}
call_rcu(&entry->rcu, netlbl_domhsh_free_entry);
}
return ret_val;
}
/**
* netlbl_domhsh_remove - Removes an entry from the domain hash table
* @domain: the domain to remove
* @audit_info: NetLabel audit information
*
* Description:
* Removes an entry from the domain hash table and handles any updates to the
* lower level protocol handler (i.e. CIPSO). Returns zero on success,
* negative on failure.
*
*/
int netlbl_domhsh_remove(const char *domain, struct netlbl_audit *audit_info)
{
int ret_val;
struct netlbl_dom_map *entry;
rcu_read_lock();
if (domain)
entry = netlbl_domhsh_search(domain);
else
entry = netlbl_domhsh_search_def(domain);
ret_val = netlbl_domhsh_remove_entry(entry, audit_info);
rcu_read_unlock();
return ret_val;
}
......@@ -388,6 +539,70 @@ struct netlbl_dom_map *netlbl_domhsh_getentry(const char *domain)
return netlbl_domhsh_search_def(domain);
}
/**
* netlbl_domhsh_getentry_af4 - Get an entry from the domain hash table
* @domain: the domain name to search for
* @addr: the IP address to search for
*
* Description:
* Look through the domain hash table searching for an entry to match @domain
* and @addr, return a pointer to a copy of the entry or NULL. The caller is
* responsible for ensuring that rcu_read_[un]lock() is called.
*
*/
struct netlbl_domaddr4_map *netlbl_domhsh_getentry_af4(const char *domain,
__be32 addr)
{
struct netlbl_dom_map *dom_iter;
struct netlbl_af4list *addr_iter;
dom_iter = netlbl_domhsh_search_def(domain);
if (dom_iter == NULL)
return NULL;
if (dom_iter->type != NETLBL_NLTYPE_ADDRSELECT)
return NULL;
addr_iter = netlbl_af4list_search(addr,
&dom_iter->type_def.addrsel->list4);
if (addr_iter == NULL)
return NULL;
return netlbl_domhsh_addr4_entry(addr_iter);
}
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
/**
* netlbl_domhsh_getentry_af6 - Get an entry from the domain hash table
* @domain: the domain name to search for
* @addr: the IP address to search for
*
* Description:
* Look through the domain hash table searching for an entry to match @domain
* and @addr, return a pointer to a copy of the entry or NULL. The caller is
* responsible for ensuring that rcu_read_[un]lock() is called.
*
*/
struct netlbl_domaddr6_map *netlbl_domhsh_getentry_af6(const char *domain,
const struct in6_addr *addr)
{
struct netlbl_dom_map *dom_iter;
struct netlbl_af6list *addr_iter;
dom_iter = netlbl_domhsh_search_def(domain);
if (dom_iter == NULL)
return NULL;
if (dom_iter->type != NETLBL_NLTYPE_ADDRSELECT)
return NULL;
addr_iter = netlbl_af6list_search(addr,
&dom_iter->type_def.addrsel->list6);
if (addr_iter == NULL)
return NULL;
return netlbl_domhsh_addr6_entry(addr_iter);
}
#endif /* IPv6 */
/**
* netlbl_domhsh_walk - Iterate through the domain mapping hash table
* @skip_bkt: the number of buckets to skip at the start
......@@ -410,6 +625,7 @@ int netlbl_domhsh_walk(u32 *skip_bkt,
{
int ret_val = -ENOENT;
u32 iter_bkt;
struct list_head *iter_list;
struct netlbl_dom_map *iter_entry;
u32 chain_cnt = 0;
......@@ -417,9 +633,8 @@ int netlbl_domhsh_walk(u32 *skip_bkt,
for (iter_bkt = *skip_bkt;
iter_bkt < rcu_dereference(netlbl_domhsh)->size;
iter_bkt++, chain_cnt = 0) {
list_for_each_entry_rcu(iter_entry,
&rcu_dereference(netlbl_domhsh)->tbl[iter_bkt],
list)
iter_list = &rcu_dereference(netlbl_domhsh)->tbl[iter_bkt];
list_for_each_entry_rcu(iter_entry, iter_list, list)
if (iter_entry->valid) {
if (chain_cnt++ < *skip_chain)
continue;
......
......@@ -11,7 +11,7 @@
*/
/*
* (c) Copyright Hewlett-Packard Development Company, L.P., 2006
* (c) Copyright Hewlett-Packard Development Company, L.P., 2006, 2008
*
* 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,16 +36,43 @@
#include <linux/rcupdate.h>
#include <linux/list.h>
#include "netlabel_addrlist.h"
/* Domain hash table size */
/* XXX - currently this number is an uneducated guess */
#define NETLBL_DOMHSH_BITSIZE 7
/* Domain mapping definition struct */
/* Domain mapping definition structures */
#define netlbl_domhsh_addr4_entry(iter) \
container_of(iter, struct netlbl_domaddr4_map, list)
struct netlbl_domaddr4_map {
u32 type;
union {
struct cipso_v4_doi *cipsov4;
} type_def;
struct netlbl_af4list list;
};
#define netlbl_domhsh_addr6_entry(iter) \
container_of(iter, struct netlbl_domaddr6_map, list)
struct netlbl_domaddr6_map {
u32 type;
/* NOTE: no 'type_def' union needed at present since we don't currently
* support any IPv6 labeling protocols */
struct netlbl_af6list list;
};
struct netlbl_domaddr_map {
struct list_head list4;
struct list_head list6;
};
struct netlbl_dom_map {
char *domain;
u32 type;
union {
struct cipso_v4_doi *cipsov4;
struct netlbl_domaddr_map *addrsel;
} type_def;
u32 valid;
......@@ -61,12 +88,21 @@ int netlbl_domhsh_add(struct netlbl_dom_map *entry,
struct netlbl_audit *audit_info);
int netlbl_domhsh_add_default(struct netlbl_dom_map *entry,
struct netlbl_audit *audit_info);
int netlbl_domhsh_remove_entry(struct netlbl_dom_map *entry,
struct netlbl_audit *audit_info);
int netlbl_domhsh_remove(const char *domain, struct netlbl_audit *audit_info);
int netlbl_domhsh_remove_default(struct netlbl_audit *audit_info);
struct netlbl_dom_map *netlbl_domhsh_getentry(const char *domain);
struct netlbl_domaddr4_map *netlbl_domhsh_getentry_af4(const char *domain,
__be32 addr);
int netlbl_domhsh_walk(u32 *skip_bkt,
u32 *skip_chain,
int (*callback) (struct netlbl_dom_map *entry, void *arg),
void *cb_arg);
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
struct netlbl_domaddr6_map *netlbl_domhsh_getentry_af6(const char *domain,
const struct in6_addr *addr);
#endif /* IPv6 */
#endif
......@@ -10,7 +10,7 @@
*/
/*
* (c) Copyright Hewlett-Packard Development Company, L.P., 2006
* (c) Copyright Hewlett-Packard Development Company, L.P., 2006, 2008
*
* 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
......@@ -82,7 +82,7 @@ int netlbl_cfg_unlbl_add_map(const char *domain,
entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
if (entry == NULL)
goto cfg_unlbl_add_map_failure;
return -ENOMEM;
if (domain != NULL) {
entry->domain = kstrdup(domain, GFP_ATOMIC);
if (entry->domain == NULL)
......@@ -103,49 +103,6 @@ int netlbl_cfg_unlbl_add_map(const char *domain,
return ret_val;
}
/**
* netlbl_cfg_cipsov4_add - Add a new CIPSOv4 DOI definition
* @doi_def: the DOI definition
* @audit_info: NetLabel audit information
*
* Description:
* Add a new CIPSOv4 DOI definition to the NetLabel subsystem. Returns zero on
* success, negative values on failure.
*
*/
int netlbl_cfg_cipsov4_add(struct cipso_v4_doi *doi_def,
struct netlbl_audit *audit_info)
{
int ret_val;
const char *type_str;
struct audit_buffer *audit_buf;
ret_val = cipso_v4_doi_add(doi_def);
audit_buf = netlbl_audit_start_common(AUDIT_MAC_CIPSOV4_ADD,
audit_info);
if (audit_buf != NULL) {
switch (doi_def->type) {
case CIPSO_V4_MAP_STD:
type_str = "std";
break;
case CIPSO_V4_MAP_PASS:
type_str = "pass";
break;
default:
type_str = "(unknown)";
}
audit_log_format(audit_buf,
" cipso_doi=%u cipso_type=%s res=%u",
doi_def->doi,
type_str,
ret_val == 0 ? 1 : 0);
audit_log_end(audit_buf);
}
return ret_val;
}
/**
* netlbl_cfg_cipsov4_add_map - Add a new CIPSOv4 DOI definition and mapping
* @doi_def: the DOI definition
......@@ -164,58 +121,71 @@ int netlbl_cfg_cipsov4_add_map(struct cipso_v4_doi *doi_def,
struct netlbl_audit *audit_info)
{
int ret_val = -ENOMEM;
u32 doi;
u32 doi_type;
struct netlbl_dom_map *entry;
const char *type_str;
struct audit_buffer *audit_buf;
doi = doi_def->doi;
doi_type = doi_def->type;
entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
if (entry == NULL)
goto cfg_cipsov4_add_map_failure;
return -ENOMEM;
if (domain != NULL) {
entry->domain = kstrdup(domain, GFP_ATOMIC);
if (entry->domain == NULL)
goto cfg_cipsov4_add_map_failure;
}
entry->type = NETLBL_NLTYPE_CIPSOV4;
entry->type_def.cipsov4 = doi_def;
/* Grab a RCU read lock here so nothing happens to the doi_def variable
* between adding it to the CIPSOv4 protocol engine and adding a
* domain mapping for it. */
rcu_read_lock();
ret_val = netlbl_cfg_cipsov4_add(doi_def, audit_info);
ret_val = cipso_v4_doi_add(doi_def);
if (ret_val != 0)
goto cfg_cipsov4_add_map_failure_unlock;
goto cfg_cipsov4_add_map_failure_remove_doi;
entry->type = NETLBL_NLTYPE_CIPSOV4;
entry->type_def.cipsov4 = cipso_v4_doi_getdef(doi);
if (entry->type_def.cipsov4 == NULL) {
ret_val = -ENOENT;
goto cfg_cipsov4_add_map_failure_remove_doi;
}
ret_val = netlbl_domhsh_add(entry, audit_info);
if (ret_val != 0)
goto cfg_cipsov4_add_map_failure_remove_doi;
rcu_read_unlock();
goto cfg_cipsov4_add_map_failure_release_doi;
return 0;
cfg_cipsov4_add_map_return:
audit_buf = netlbl_audit_start_common(AUDIT_MAC_CIPSOV4_ADD,
audit_info);
if (audit_buf != NULL) {
switch (doi_type) {
case CIPSO_V4_MAP_TRANS:
type_str = "trans";
break;
case CIPSO_V4_MAP_PASS:
type_str = "pass";
break;
case CIPSO_V4_MAP_LOCAL:
type_str = "local";
break;
default:
type_str = "(unknown)";
}
audit_log_format(audit_buf,
" cipso_doi=%u cipso_type=%s res=%u",
doi, type_str, ret_val == 0 ? 1 : 0);
audit_log_end(audit_buf);
}
return ret_val;
cfg_cipsov4_add_map_failure_release_doi:
cipso_v4_doi_putdef(doi_def);
cfg_cipsov4_add_map_failure_remove_doi:
cipso_v4_doi_remove(doi_def->doi, audit_info, netlbl_cipsov4_doi_free);
cfg_cipsov4_add_map_failure_unlock:
rcu_read_unlock();
cipso_v4_doi_remove(doi, audit_info);
cfg_cipsov4_add_map_failure:
if (entry != NULL)
kfree(entry->domain);
kfree(entry);
return ret_val;
}
/**
* netlbl_cfg_cipsov4_del - Removean existing CIPSOv4 DOI definition
* @doi: the CIPSO DOI value
* @audit_info: NetLabel audit information
*
* Description:
* Removes an existing CIPSOv4 DOI definition from the NetLabel subsystem.
* Returns zero on success, negative values on failure.
*
*/
int netlbl_cfg_cipsov4_del(u32 doi, struct netlbl_audit *audit_info)
{
return cipso_v4_doi_remove(doi, audit_info, netlbl_cipsov4_doi_free);
goto cfg_cipsov4_add_map_return;
}
/*
......@@ -452,7 +422,9 @@ int netlbl_enabled(void)
* Attach the correct label to the given socket using the security attributes
* specified in @secattr. This function requires exclusive access to @sk,
* which means it either needs to be in the process of being created or locked.
* Returns zero on success, negative values on failure.
* Returns zero on success, -EDESTADDRREQ if the domain is configured to use
* network address selectors (can't blindly label the socket), and negative
* values on all other failures.
*
*/
int netlbl_sock_setattr(struct sock *sk,
......@@ -466,6 +438,9 @@ int netlbl_sock_setattr(struct sock *sk,
if (dom_entry == NULL)
goto socket_setattr_return;
switch (dom_entry->type) {
case NETLBL_NLTYPE_ADDRSELECT:
ret_val = -EDESTADDRREQ;
break;
case NETLBL_NLTYPE_CIPSOV4:
ret_val = cipso_v4_sock_setattr(sk,
dom_entry->type_def.cipsov4,
......@@ -483,6 +458,20 @@ int netlbl_sock_setattr(struct sock *sk,
return ret_val;
}
/**
* netlbl_sock_delattr - Delete all the NetLabel labels on a socket
* @sk: the socket
*
* Description:
* Remove all the NetLabel labeling from @sk. The caller is responsible for
* ensuring that @sk is locked.
*
*/
void netlbl_sock_delattr(struct sock *sk)
{
cipso_v4_sock_delattr(sk);
}
/**
* netlbl_sock_getattr - Determine the security attributes of a sock
* @sk: the sock
......@@ -500,6 +489,128 @@ int netlbl_sock_getattr(struct sock *sk, struct netlbl_lsm_secattr *secattr)
return cipso_v4_sock_getattr(sk, secattr);
}
/**
* netlbl_conn_setattr - Label a connected socket using the correct protocol
* @sk: the socket to label
* @addr: the destination address
* @secattr: the security attributes
*
* Description:
* Attach the correct label to the given connected socket using the security
* attributes specified in @secattr. The caller is responsible for ensuring
* that @sk is locked. Returns zero on success, negative values on failure.
*
*/
int netlbl_conn_setattr(struct sock *sk,
struct sockaddr *addr,
const struct netlbl_lsm_secattr *secattr)
{
int ret_val;
struct sockaddr_in *addr4;
struct netlbl_domaddr4_map *af4_entry;
rcu_read_lock();
switch (addr->sa_family) {
case AF_INET:
addr4 = (struct sockaddr_in *)addr;
af4_entry = netlbl_domhsh_getentry_af4(secattr->domain,
addr4->sin_addr.s_addr);
if (af4_entry == NULL) {
ret_val = -ENOENT;
goto conn_setattr_return;
}
switch (af4_entry->type) {
case NETLBL_NLTYPE_CIPSOV4:
ret_val = cipso_v4_sock_setattr(sk,
af4_entry->type_def.cipsov4,
secattr);
break;
case NETLBL_NLTYPE_UNLABELED:
/* just delete the protocols we support for right now
* but we could remove other protocols if needed */
cipso_v4_sock_delattr(sk);
ret_val = 0;
break;
default:
ret_val = -ENOENT;
}
break;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
case AF_INET6:
/* since we don't support any IPv6 labeling protocols right
* now we can optimize everything away until we do */
ret_val = 0;
break;
#endif /* IPv6 */
default:
ret_val = 0;
}
conn_setattr_return:
rcu_read_unlock();
return ret_val;
}
/**
* netlbl_skbuff_setattr - Label a packet using the correct protocol
* @skb: the packet
* @family: protocol family
* @secattr: the security attributes
*
* Description:
* Attach the correct label to the given packet using the security attributes
* specified in @secattr. Returns zero on success, negative values on failure.
*
*/
int netlbl_skbuff_setattr(struct sk_buff *skb,
u16 family,
const struct netlbl_lsm_secattr *secattr)
{
int ret_val;
struct iphdr *hdr4;
struct netlbl_domaddr4_map *af4_entry;
rcu_read_lock();
switch (family) {
case AF_INET:
hdr4 = ip_hdr(skb);
af4_entry = netlbl_domhsh_getentry_af4(secattr->domain,
hdr4->daddr);
if (af4_entry == NULL) {
ret_val = -ENOENT;
goto skbuff_setattr_return;
}
switch (af4_entry->type) {
case NETLBL_NLTYPE_CIPSOV4:
ret_val = cipso_v4_skbuff_setattr(skb,
af4_entry->type_def.cipsov4,
secattr);
break;
case NETLBL_NLTYPE_UNLABELED:
/* just delete the protocols we support for right now
* but we could remove other protocols if needed */
ret_val = cipso_v4_skbuff_delattr(skb);
break;
default:
ret_val = -ENOENT;
}
break;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
case AF_INET6:
/* since we don't support any IPv6 labeling protocols right
* now we can optimize everything away until we do */
ret_val = 0;
break;
#endif /* IPv6 */
default:
ret_val = 0;
}
skbuff_setattr_return:
rcu_read_unlock();
return ret_val;
}
/**
* netlbl_skbuff_getattr - Determine the security attributes of a packet
* @skb: the packet
......@@ -528,6 +639,7 @@ int netlbl_skbuff_getattr(const struct sk_buff *skb,
* netlbl_skbuff_err - Handle a LSM error on a sk_buff
* @skb: the packet
* @error: the error code
* @gateway: true if host is acting as a gateway, false otherwise
*
* Description:
* Deal with a LSM problem when handling the packet in @skb, typically this is
......@@ -535,10 +647,10 @@ int netlbl_skbuff_getattr(const struct sk_buff *skb,
* according to the packet's labeling protocol.
*
*/
void netlbl_skbuff_err(struct sk_buff *skb, int error)
void netlbl_skbuff_err(struct sk_buff *skb, int error, int gateway)
{
if (CIPSO_V4_OPTEXIST(skb))
cipso_v4_error(skb, error, 0);
cipso_v4_error(skb, error, gateway);
}
/**
......
......@@ -10,7 +10,7 @@
*/
/*
* (c) Copyright Hewlett-Packard Development Company, L.P., 2006
* (c) Copyright Hewlett-Packard Development Company, L.P., 2006, 2008
*
* 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
......@@ -32,9 +32,13 @@
#include <linux/socket.h>
#include <linux/string.h>
#include <linux/skbuff.h>
#include <linux/in.h>
#include <linux/in6.h>
#include <net/sock.h>
#include <net/netlink.h>
#include <net/genetlink.h>
#include <net/ip.h>
#include <net/ipv6.h>
#include <net/netlabel.h>
#include <net/cipso_ipv4.h>
#include <asm/atomic.h>
......@@ -71,85 +75,336 @@ static const struct nla_policy netlbl_mgmt_genl_policy[NLBL_MGMT_A_MAX + 1] = {
};
/*
* NetLabel Command Handlers
* Helper Functions
*/
/**
* netlbl_mgmt_add - Handle an ADD message
* @skb: the NETLINK buffer
* @info: the Generic NETLINK info block
* @audit_info: NetLabel audit information
*
* Description:
* Process a user generated ADD message and add the domains from the message
* to the hash table. See netlabel.h for a description of the message format.
* Returns zero on success, negative values on failure.
* Helper function for the ADD and ADDDEF messages to add the domain mappings
* from the message to the hash table. See netlabel.h for a description of the
* message format. Returns zero on success, negative values on failure.
*
*/
static int netlbl_mgmt_add(struct sk_buff *skb, struct genl_info *info)
static int netlbl_mgmt_add_common(struct genl_info *info,
struct netlbl_audit *audit_info)
{
int ret_val = -EINVAL;
struct netlbl_dom_map *entry = NULL;
size_t tmp_size;
struct netlbl_domaddr_map *addrmap = NULL;
struct cipso_v4_doi *cipsov4 = NULL;
u32 tmp_val;
struct netlbl_audit audit_info;
if (!info->attrs[NLBL_MGMT_A_DOMAIN] ||
!info->attrs[NLBL_MGMT_A_PROTOCOL])
goto add_failure;
netlbl_netlink_auditinfo(skb, &audit_info);
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
if (entry == NULL) {
ret_val = -ENOMEM;
goto add_failure;
}
tmp_size = nla_len(info->attrs[NLBL_MGMT_A_DOMAIN]);
entry->domain = kmalloc(tmp_size, GFP_KERNEL);
if (entry->domain == NULL) {
ret_val = -ENOMEM;
goto add_failure;
}
entry->type = nla_get_u32(info->attrs[NLBL_MGMT_A_PROTOCOL]);
nla_strlcpy(entry->domain, info->attrs[NLBL_MGMT_A_DOMAIN], tmp_size);
if (info->attrs[NLBL_MGMT_A_DOMAIN]) {
size_t tmp_size = nla_len(info->attrs[NLBL_MGMT_A_DOMAIN]);
entry->domain = kmalloc(tmp_size, GFP_KERNEL);
if (entry->domain == NULL) {
ret_val = -ENOMEM;
goto add_failure;
}
nla_strlcpy(entry->domain,
info->attrs[NLBL_MGMT_A_DOMAIN], tmp_size);
}
/* NOTE: internally we allow/use a entry->type value of
* NETLBL_NLTYPE_ADDRSELECT but we don't currently allow users
* to pass that as a protocol value because we need to know the
* "real" protocol */
switch (entry->type) {
case NETLBL_NLTYPE_UNLABELED:
ret_val = netlbl_domhsh_add(entry, &audit_info);
break;
case NETLBL_NLTYPE_CIPSOV4:
if (!info->attrs[NLBL_MGMT_A_CV4DOI])
goto add_failure;
tmp_val = nla_get_u32(info->attrs[NLBL_MGMT_A_CV4DOI]);
/* We should be holding a rcu_read_lock() here while we hold
* the result but since the entry will always be deleted when
* the CIPSO DOI is deleted we aren't going to keep the
* lock. */
rcu_read_lock();
entry->type_def.cipsov4 = cipso_v4_doi_getdef(tmp_val);
if (entry->type_def.cipsov4 == NULL) {
rcu_read_unlock();
cipsov4 = cipso_v4_doi_getdef(tmp_val);
if (cipsov4 == NULL)
goto add_failure;
}
ret_val = netlbl_domhsh_add(entry, &audit_info);
rcu_read_unlock();
entry->type_def.cipsov4 = cipsov4;
break;
default:
goto add_failure;
}
if (info->attrs[NLBL_MGMT_A_IPV4ADDR]) {
struct in_addr *addr;
struct in_addr *mask;
struct netlbl_domaddr4_map *map;
addrmap = kzalloc(sizeof(*addrmap), GFP_KERNEL);
if (addrmap == NULL) {
ret_val = -ENOMEM;
goto add_failure;
}
INIT_LIST_HEAD(&addrmap->list4);
INIT_LIST_HEAD(&addrmap->list6);
if (nla_len(info->attrs[NLBL_MGMT_A_IPV4ADDR]) !=
sizeof(struct in_addr)) {
ret_val = -EINVAL;
goto add_failure;
}
if (nla_len(info->attrs[NLBL_MGMT_A_IPV4MASK]) !=
sizeof(struct in_addr)) {
ret_val = -EINVAL;
goto add_failure;
}
addr = nla_data(info->attrs[NLBL_MGMT_A_IPV4ADDR]);
mask = nla_data(info->attrs[NLBL_MGMT_A_IPV4MASK]);
map = kzalloc(sizeof(*map), GFP_KERNEL);
if (map == NULL) {
ret_val = -ENOMEM;
goto add_failure;
}
map->list.addr = addr->s_addr & mask->s_addr;
map->list.mask = mask->s_addr;
map->list.valid = 1;
map->type = entry->type;
if (cipsov4)
map->type_def.cipsov4 = cipsov4;
ret_val = netlbl_af4list_add(&map->list, &addrmap->list4);
if (ret_val != 0) {
kfree(map);
goto add_failure;
}
entry->type = NETLBL_NLTYPE_ADDRSELECT;
entry->type_def.addrsel = addrmap;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
} else if (info->attrs[NLBL_MGMT_A_IPV6ADDR]) {
struct in6_addr *addr;
struct in6_addr *mask;
struct netlbl_domaddr6_map *map;
addrmap = kzalloc(sizeof(*addrmap), GFP_KERNEL);
if (addrmap == NULL) {
ret_val = -ENOMEM;
goto add_failure;
}
INIT_LIST_HEAD(&addrmap->list4);
INIT_LIST_HEAD(&addrmap->list6);
if (nla_len(info->attrs[NLBL_MGMT_A_IPV6ADDR]) !=
sizeof(struct in6_addr)) {
ret_val = -EINVAL;
goto add_failure;
}
if (nla_len(info->attrs[NLBL_MGMT_A_IPV6MASK]) !=
sizeof(struct in6_addr)) {
ret_val = -EINVAL;
goto add_failure;
}
addr = nla_data(info->attrs[NLBL_MGMT_A_IPV6ADDR]);
mask = nla_data(info->attrs[NLBL_MGMT_A_IPV6MASK]);
map = kzalloc(sizeof(*map), GFP_KERNEL);
if (map == NULL) {
ret_val = -ENOMEM;
goto add_failure;
}
ipv6_addr_copy(&map->list.addr, addr);
map->list.addr.s6_addr32[0] &= mask->s6_addr32[0];
map->list.addr.s6_addr32[1] &= mask->s6_addr32[1];
map->list.addr.s6_addr32[2] &= mask->s6_addr32[2];
map->list.addr.s6_addr32[3] &= mask->s6_addr32[3];
ipv6_addr_copy(&map->list.mask, mask);
map->list.valid = 1;
map->type = entry->type;
ret_val = netlbl_af6list_add(&map->list, &addrmap->list6);
if (ret_val != 0) {
kfree(map);
goto add_failure;
}
entry->type = NETLBL_NLTYPE_ADDRSELECT;
entry->type_def.addrsel = addrmap;
#endif /* IPv6 */
}
ret_val = netlbl_domhsh_add(entry, audit_info);
if (ret_val != 0)
goto add_failure;
return 0;
add_failure:
if (cipsov4)
cipso_v4_doi_putdef(cipsov4);
if (entry)
kfree(entry->domain);
kfree(addrmap);
kfree(entry);
return ret_val;
}
/**
* netlbl_mgmt_listentry - List a NetLabel/LSM domain map entry
* @skb: the NETLINK buffer
* @entry: the map entry
*
* Description:
* This function is a helper function used by the LISTALL and LISTDEF command
* handlers. The caller is responsibile for ensuring that the RCU read lock
* is held. Returns zero on success, negative values on failure.
*
*/
static int netlbl_mgmt_listentry(struct sk_buff *skb,
struct netlbl_dom_map *entry)
{
int ret_val;
struct nlattr *nla_a;
struct nlattr *nla_b;
struct netlbl_af4list *iter4;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
struct netlbl_af6list *iter6;
#endif
if (entry->domain != NULL) {
ret_val = nla_put_string(skb,
NLBL_MGMT_A_DOMAIN, entry->domain);
if (ret_val != 0)
return ret_val;
}
switch (entry->type) {
case NETLBL_NLTYPE_ADDRSELECT:
nla_a = nla_nest_start(skb, NLBL_MGMT_A_SELECTORLIST);
if (nla_a == NULL)
return -ENOMEM;
netlbl_af4list_foreach_rcu(iter4,
&entry->type_def.addrsel->list4) {
struct netlbl_domaddr4_map *map4;
struct in_addr addr_struct;
nla_b = nla_nest_start(skb, NLBL_MGMT_A_ADDRSELECTOR);
if (nla_b == NULL)
return -ENOMEM;
addr_struct.s_addr = iter4->addr;
ret_val = nla_put(skb, NLBL_MGMT_A_IPV4ADDR,
sizeof(struct in_addr),
&addr_struct);
if (ret_val != 0)
return ret_val;
addr_struct.s_addr = iter4->mask;
ret_val = nla_put(skb, NLBL_MGMT_A_IPV4MASK,
sizeof(struct in_addr),
&addr_struct);
if (ret_val != 0)
return ret_val;
map4 = netlbl_domhsh_addr4_entry(iter4);
ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL,
map4->type);
if (ret_val != 0)
return ret_val;
switch (map4->type) {
case NETLBL_NLTYPE_CIPSOV4:
ret_val = nla_put_u32(skb, NLBL_MGMT_A_CV4DOI,
map4->type_def.cipsov4->doi);
if (ret_val != 0)
return ret_val;
break;
}
nla_nest_end(skb, nla_b);
}
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
netlbl_af6list_foreach_rcu(iter6,
&entry->type_def.addrsel->list6) {
struct netlbl_domaddr6_map *map6;
nla_b = nla_nest_start(skb, NLBL_MGMT_A_ADDRSELECTOR);
if (nla_b == NULL)
return -ENOMEM;
ret_val = nla_put(skb, NLBL_MGMT_A_IPV6ADDR,
sizeof(struct in6_addr),
&iter6->addr);
if (ret_val != 0)
return ret_val;
ret_val = nla_put(skb, NLBL_MGMT_A_IPV6MASK,
sizeof(struct in6_addr),
&iter6->mask);
if (ret_val != 0)
return ret_val;
map6 = netlbl_domhsh_addr6_entry(iter6);
ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL,
map6->type);
if (ret_val != 0)
return ret_val;
nla_nest_end(skb, nla_b);
}
#endif /* IPv6 */
nla_nest_end(skb, nla_a);
break;
case NETLBL_NLTYPE_UNLABELED:
ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL, entry->type);
break;
case NETLBL_NLTYPE_CIPSOV4:
ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL, entry->type);
if (ret_val != 0)
return ret_val;
ret_val = nla_put_u32(skb, NLBL_MGMT_A_CV4DOI,
entry->type_def.cipsov4->doi);
break;
}
return ret_val;
}
/*
* NetLabel Command Handlers
*/
/**
* netlbl_mgmt_add - Handle an ADD message
* @skb: the NETLINK buffer
* @info: the Generic NETLINK info block
*
* Description:
* Process a user generated ADD message and add the domains from the message
* to the hash table. See netlabel.h for a description of the message format.
* Returns zero on success, negative values on failure.
*
*/
static int netlbl_mgmt_add(struct sk_buff *skb, struct genl_info *info)
{
struct netlbl_audit audit_info;
if ((!info->attrs[NLBL_MGMT_A_DOMAIN]) ||
(!info->attrs[NLBL_MGMT_A_PROTOCOL]) ||
(info->attrs[NLBL_MGMT_A_IPV4ADDR] &&
info->attrs[NLBL_MGMT_A_IPV6ADDR]) ||
(info->attrs[NLBL_MGMT_A_IPV4MASK] &&
info->attrs[NLBL_MGMT_A_IPV6MASK]) ||
((info->attrs[NLBL_MGMT_A_IPV4ADDR] != NULL) ^
(info->attrs[NLBL_MGMT_A_IPV4MASK] != NULL)) ||
((info->attrs[NLBL_MGMT_A_IPV6ADDR] != NULL) ^
(info->attrs[NLBL_MGMT_A_IPV6MASK] != NULL)))
return -EINVAL;
netlbl_netlink_auditinfo(skb, &audit_info);
return netlbl_mgmt_add_common(info, &audit_info);
}
/**
* netlbl_mgmt_remove - Handle a REMOVE message
* @skb: the NETLINK buffer
......@@ -198,23 +453,9 @@ static int netlbl_mgmt_listall_cb(struct netlbl_dom_map *entry, void *arg)
if (data == NULL)
goto listall_cb_failure;
ret_val = nla_put_string(cb_arg->skb,
NLBL_MGMT_A_DOMAIN,
entry->domain);
ret_val = netlbl_mgmt_listentry(cb_arg->skb, entry);
if (ret_val != 0)
goto listall_cb_failure;
ret_val = nla_put_u32(cb_arg->skb, NLBL_MGMT_A_PROTOCOL, entry->type);
if (ret_val != 0)
goto listall_cb_failure;
switch (entry->type) {
case NETLBL_NLTYPE_CIPSOV4:
ret_val = nla_put_u32(cb_arg->skb,
NLBL_MGMT_A_CV4DOI,
entry->type_def.cipsov4->doi);
if (ret_val != 0)
goto listall_cb_failure;
break;
}
cb_arg->seq++;
return genlmsg_end(cb_arg->skb, data);
......@@ -268,56 +509,22 @@ static int netlbl_mgmt_listall(struct sk_buff *skb,
*/
static int netlbl_mgmt_adddef(struct sk_buff *skb, struct genl_info *info)
{
int ret_val = -EINVAL;
struct netlbl_dom_map *entry = NULL;
u32 tmp_val;
struct netlbl_audit audit_info;
if (!info->attrs[NLBL_MGMT_A_PROTOCOL])
goto adddef_failure;
if ((!info->attrs[NLBL_MGMT_A_PROTOCOL]) ||
(info->attrs[NLBL_MGMT_A_IPV4ADDR] &&
info->attrs[NLBL_MGMT_A_IPV6ADDR]) ||
(info->attrs[NLBL_MGMT_A_IPV4MASK] &&
info->attrs[NLBL_MGMT_A_IPV6MASK]) ||
((info->attrs[NLBL_MGMT_A_IPV4ADDR] != NULL) ^
(info->attrs[NLBL_MGMT_A_IPV4MASK] != NULL)) ||
((info->attrs[NLBL_MGMT_A_IPV6ADDR] != NULL) ^
(info->attrs[NLBL_MGMT_A_IPV6MASK] != NULL)))
return -EINVAL;
netlbl_netlink_auditinfo(skb, &audit_info);
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
if (entry == NULL) {
ret_val = -ENOMEM;
goto adddef_failure;
}
entry->type = nla_get_u32(info->attrs[NLBL_MGMT_A_PROTOCOL]);
switch (entry->type) {
case NETLBL_NLTYPE_UNLABELED:
ret_val = netlbl_domhsh_add_default(entry, &audit_info);
break;
case NETLBL_NLTYPE_CIPSOV4:
if (!info->attrs[NLBL_MGMT_A_CV4DOI])
goto adddef_failure;
tmp_val = nla_get_u32(info->attrs[NLBL_MGMT_A_CV4DOI]);
/* We should be holding a rcu_read_lock() here while we hold
* the result but since the entry will always be deleted when
* the CIPSO DOI is deleted we aren't going to keep the
* lock. */
rcu_read_lock();
entry->type_def.cipsov4 = cipso_v4_doi_getdef(tmp_val);
if (entry->type_def.cipsov4 == NULL) {
rcu_read_unlock();
goto adddef_failure;
}
ret_val = netlbl_domhsh_add_default(entry, &audit_info);
rcu_read_unlock();
break;
default:
goto adddef_failure;
}
if (ret_val != 0)
goto adddef_failure;
return 0;
adddef_failure:
kfree(entry);
return ret_val;
return netlbl_mgmt_add_common(info, &audit_info);
}
/**
......@@ -371,19 +578,10 @@ static int netlbl_mgmt_listdef(struct sk_buff *skb, struct genl_info *info)
ret_val = -ENOENT;
goto listdef_failure_lock;
}
ret_val = nla_put_u32(ans_skb, NLBL_MGMT_A_PROTOCOL, entry->type);
if (ret_val != 0)
goto listdef_failure_lock;
switch (entry->type) {
case NETLBL_NLTYPE_CIPSOV4:
ret_val = nla_put_u32(ans_skb,
NLBL_MGMT_A_CV4DOI,
entry->type_def.cipsov4->doi);
if (ret_val != 0)
goto listdef_failure_lock;
break;
}
ret_val = netlbl_mgmt_listentry(ans_skb, entry);
rcu_read_unlock();
if (ret_val != 0)
goto listdef_failure;
genlmsg_end(ans_skb, data);
return genlmsg_reply(ans_skb, info);
......
......@@ -45,6 +45,16 @@
* NLBL_MGMT_A_DOMAIN
* NLBL_MGMT_A_PROTOCOL
*
* If IPv4 is specified the following attributes are required:
*
* NLBL_MGMT_A_IPV4ADDR
* NLBL_MGMT_A_IPV4MASK
*
* If IPv6 is specified the following attributes are required:
*
* NLBL_MGMT_A_IPV6ADDR
* NLBL_MGMT_A_IPV6MASK
*
* If using NETLBL_NLTYPE_CIPSOV4 the following attributes are required:
*
* NLBL_MGMT_A_CV4DOI
......@@ -68,13 +78,24 @@
* Required attributes:
*
* NLBL_MGMT_A_DOMAIN
*
* If the IP address selectors are not used the following attribute is
* required:
*
* NLBL_MGMT_A_PROTOCOL
*
* If using NETLBL_NLTYPE_CIPSOV4 the following attributes are required:
* If the IP address selectors are used then the following attritbute is
* required:
*
* NLBL_MGMT_A_SELECTORLIST
*
* If the mapping is using the NETLBL_NLTYPE_CIPSOV4 type then the following
* attributes are required:
*
* NLBL_MGMT_A_CV4DOI
*
* If using NETLBL_NLTYPE_UNLABELED no other attributes are required.
* If the mapping is using the NETLBL_NLTYPE_UNLABELED type no other
* attributes are required.
*
* o ADDDEF:
* Sent by an application to set the default domain mapping for the NetLabel
......@@ -100,15 +121,23 @@
* application there is no payload. On success the kernel should send a
* response using the following format.
*
* Required attributes:
* If the IP address selectors are not used the following attribute is
* required:
*
* NLBL_MGMT_A_PROTOCOL
*
* If using NETLBL_NLTYPE_CIPSOV4 the following attributes are required:
* If the IP address selectors are used then the following attritbute is
* required:
*
* NLBL_MGMT_A_SELECTORLIST
*
* If the mapping is using the NETLBL_NLTYPE_CIPSOV4 type then the following
* attributes are required:
*
* NLBL_MGMT_A_CV4DOI
*
* If using NETLBL_NLTYPE_UNLABELED no other attributes are required.
* If the mapping is using the NETLBL_NLTYPE_UNLABELED type no other
* attributes are required.
*
* o PROTOCOLS:
* Sent by an application to request a list of configured NetLabel protocols
......@@ -162,6 +191,26 @@ enum {
NLBL_MGMT_A_CV4DOI,
/* (NLA_U32)
* the CIPSOv4 DOI value */
NLBL_MGMT_A_IPV6ADDR,
/* (NLA_BINARY, struct in6_addr)
* an IPv6 address */
NLBL_MGMT_A_IPV6MASK,
/* (NLA_BINARY, struct in6_addr)
* an IPv6 address mask */
NLBL_MGMT_A_IPV4ADDR,
/* (NLA_BINARY, struct in_addr)
* an IPv4 address */
NLBL_MGMT_A_IPV4MASK,
/* (NLA_BINARY, struct in_addr)
* and IPv4 address mask */
NLBL_MGMT_A_ADDRSELECTOR,
/* (NLA_NESTED)
* an IP address selector, must contain an address, mask, and protocol
* attribute plus any protocol specific attributes */
NLBL_MGMT_A_SELECTORLIST,
/* (NLA_NESTED)
* the selector list, there must be at least one
* NLBL_MGMT_A_ADDRSELECTOR attribute */
__NLBL_MGMT_A_MAX,
};
#define NLBL_MGMT_A_MAX (__NLBL_MGMT_A_MAX - 1)
......
此差异已折叠。
......@@ -291,6 +291,7 @@ static void sk_free_security(struct sock *sk)
struct sk_security_struct *ssec = sk->sk_security;
sk->sk_security = NULL;
selinux_netlbl_sk_security_free(ssec);
kfree(ssec);
}
......@@ -3801,6 +3802,7 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen)
{
struct sock *sk = sock->sk;
struct inode_security_struct *isec;
int err;
......@@ -3814,7 +3816,6 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address,
isec = SOCK_INODE(sock)->i_security;
if (isec->sclass == SECCLASS_TCP_SOCKET ||
isec->sclass == SECCLASS_DCCP_SOCKET) {
struct sock *sk = sock->sk;
struct avc_audit_data ad;
struct sockaddr_in *addr4 = NULL;
struct sockaddr_in6 *addr6 = NULL;
......@@ -3848,6 +3849,8 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address,
goto out;
}
err = selinux_netlbl_socket_connect(sk, address);
out:
return err;
}
......@@ -4077,20 +4080,28 @@ static int selinux_sock_rcv_skb_iptables_compat(struct sock *sk,
}
static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb,
struct avc_audit_data *ad,
u16 family, char *addrp)
u16 family)
{
int err;
struct sk_security_struct *sksec = sk->sk_security;
u32 peer_sid;
u32 sk_sid = sksec->sid;
struct avc_audit_data ad;
char *addrp;
AVC_AUDIT_DATA_INIT(&ad, NET);
ad.u.net.netif = skb->iif;
ad.u.net.family = family;
err = selinux_parse_skb(skb, &ad, &addrp, 1, NULL);
if (err)
return err;
if (selinux_compat_net)
err = selinux_sock_rcv_skb_iptables_compat(sk, skb, ad,
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);
PACKET__RECV, &ad);
if (err)
return err;
......@@ -4099,12 +4110,14 @@ static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb,
if (err)
return err;
err = avc_has_perm(sk_sid, peer_sid,
SECCLASS_PEER, PEER__RECV, ad);
SECCLASS_PEER, PEER__RECV, &ad);
if (err)
selinux_netlbl_err(skb, err, 0);
} else {
err = selinux_netlbl_sock_rcv_skb(sksec, skb, family, ad);
err = selinux_netlbl_sock_rcv_skb(sksec, skb, family, &ad);
if (err)
return err;
err = selinux_xfrm_sock_rcv_skb(sksec->sid, skb, ad);
err = selinux_xfrm_sock_rcv_skb(sksec->sid, skb, &ad);
}
return err;
......@@ -4118,6 +4131,8 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
u32 sk_sid = sksec->sid;
struct avc_audit_data ad;
char *addrp;
u8 secmark_active;
u8 peerlbl_active;
if (family != PF_INET && family != PF_INET6)
return 0;
......@@ -4126,6 +4141,18 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP))
family = PF_INET;
/* 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, family);
secmark_active = selinux_secmark_enabled();
peerlbl_active = netlbl_enabled() || selinux_xfrm_enabled();
if (!secmark_active && !peerlbl_active)
return 0;
AVC_AUDIT_DATA_INIT(&ad, NET);
ad.u.net.netif = skb->iif;
ad.u.net.family = family;
......@@ -4133,15 +4160,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
if (err)
return err;
/* 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);
if (netlbl_enabled() || selinux_xfrm_enabled()) {
if (peerlbl_active) {
u32 peer_sid;
err = selinux_skb_peerlbl_sid(skb, family, &peer_sid);
......@@ -4149,13 +4168,17 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
return err;
err = selinux_inet_sys_rcv_skb(skb->iif, addrp, family,
peer_sid, &ad);
if (err)
if (err) {
selinux_netlbl_err(skb, err, 0);
return err;
}
err = avc_has_perm(sk_sid, peer_sid, SECCLASS_PEER,
PEER__RECV, &ad);
if (err)
selinux_netlbl_err(skb, err, 0);
}
if (selinux_secmark_enabled()) {
if (secmark_active) {
err = avc_has_perm(sk_sid, skb->secmark, SECCLASS_PACKET,
PACKET__RECV, &ad);
if (err)
......@@ -4214,10 +4237,12 @@ static int selinux_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *
u32 peer_secid = SECSID_NULL;
u16 family;
if (sock)
if (skb && skb->protocol == htons(ETH_P_IP))
family = PF_INET;
else if (skb && skb->protocol == htons(ETH_P_IPV6))
family = PF_INET6;
else if (sock)
family = sock->sk->sk_family;
else if (skb && skb->sk)
family = skb->sk->sk_family;
else
goto out;
......@@ -4275,8 +4300,6 @@ static void selinux_sock_graft(struct sock *sk, struct socket *parent)
sk->sk_family == PF_UNIX)
isec->sid = sksec->sid;
sksec->sclass = isec->sclass;
selinux_netlbl_sock_graft(sk, parent);
}
static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb,
......@@ -4284,10 +4307,15 @@ static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb,
{
struct sk_security_struct *sksec = sk->sk_security;
int err;
u16 family = sk->sk_family;
u32 newsid;
u32 peersid;
err = selinux_skb_peerlbl_sid(skb, sk->sk_family, &peersid);
/* handle mapped IPv4 packets arriving via IPv6 sockets */
if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP))
family = PF_INET;
err = selinux_skb_peerlbl_sid(skb, family, &peersid);
if (err)
return err;
if (peersid == SECSID_NULL) {
......@@ -4322,12 +4350,18 @@ static void selinux_inet_csk_clone(struct sock *newsk,
selinux_netlbl_sk_security_reset(newsksec, req->rsk_ops->family);
}
static void selinux_inet_conn_established(struct sock *sk,
struct sk_buff *skb)
static void selinux_inet_conn_established(struct sock *sk, struct sk_buff *skb)
{
u16 family = sk->sk_family;
struct sk_security_struct *sksec = sk->sk_security;
selinux_skb_peerlbl_sid(skb, sk->sk_family, &sksec->peer_sid);
/* handle mapped IPv4 packets arriving via IPv6 sockets */
if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP))
family = PF_INET;
selinux_skb_peerlbl_sid(skb, family, &sksec->peer_sid);
selinux_netlbl_inet_conn_established(sk, family);
}
static void selinux_req_classify_flow(const struct request_sock *req,
......@@ -4377,39 +4411,54 @@ static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb)
static unsigned int selinux_ip_forward(struct sk_buff *skb, int ifindex,
u16 family)
{
int err;
char *addrp;
u32 peer_sid;
struct avc_audit_data ad;
u8 secmark_active;
u8 netlbl_active;
u8 peerlbl_active;
if (!selinux_policycap_netpeer)
return NF_ACCEPT;
secmark_active = selinux_secmark_enabled();
peerlbl_active = netlbl_enabled() || selinux_xfrm_enabled();
netlbl_active = netlbl_enabled();
peerlbl_active = netlbl_active || selinux_xfrm_enabled();
if (!secmark_active && !peerlbl_active)
return NF_ACCEPT;
if (selinux_skb_peerlbl_sid(skb, family, &peer_sid) != 0)
return NF_DROP;
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)
if (peerlbl_active) {
err = selinux_inet_sys_rcv_skb(ifindex, addrp, family,
peer_sid, &ad);
if (err) {
selinux_netlbl_err(skb, err, 1);
return NF_DROP;
}
}
if (secmark_active)
if (avc_has_perm(peer_sid, skb->secmark,
SECCLASS_PACKET, PACKET__FORWARD_IN, &ad))
return NF_DROP;
if (netlbl_active)
/* we do this in the FORWARD path and not the POST_ROUTING
* path because we want to make sure we apply the necessary
* labeling before IPsec is applied so we can leverage AH
* protection */
if (selinux_netlbl_skbuff_setsid(skb, family, peer_sid) != 0)
return NF_DROP;
return NF_ACCEPT;
}
......@@ -4433,6 +4482,37 @@ static unsigned int selinux_ipv6_forward(unsigned int hooknum,
}
#endif /* IPV6 */
static unsigned int selinux_ip_output(struct sk_buff *skb,
u16 family)
{
u32 sid;
if (!netlbl_enabled())
return NF_ACCEPT;
/* we do this in the LOCAL_OUT path and not the POST_ROUTING path
* because we want to make sure we apply the necessary labeling
* before IPsec is applied so we can leverage AH protection */
if (skb->sk) {
struct sk_security_struct *sksec = skb->sk->sk_security;
sid = sksec->sid;
} else
sid = SECINITSID_KERNEL;
if (selinux_netlbl_skbuff_setsid(skb, family, sid) != 0)
return NF_DROP;
return NF_ACCEPT;
}
static unsigned int selinux_ipv4_output(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_output(skb, PF_INET);
}
static int selinux_ip_postroute_iptables_compat(struct sock *sk,
int ifindex,
struct avc_audit_data *ad,
......@@ -4500,30 +4580,36 @@ static int selinux_ip_postroute_iptables_compat(struct sock *sk,
static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb,
int ifindex,
struct avc_audit_data *ad,
u16 family,
char *addrp,
u8 proto)
u16 family)
{
struct sock *sk = skb->sk;
struct sk_security_struct *sksec;
struct avc_audit_data ad;
char *addrp;
u8 proto;
if (sk == NULL)
return NF_ACCEPT;
sksec = sk->sk_security;
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 (selinux_compat_net) {
if (selinux_ip_postroute_iptables_compat(skb->sk, ifindex,
ad, family, addrp))
&ad, family, addrp))
return NF_DROP;
} else {
if (avc_has_perm(sksec->sid, skb->secmark,
SECCLASS_PACKET, PACKET__SEND, ad))
SECCLASS_PACKET, PACKET__SEND, &ad))
return NF_DROP;
}
if (selinux_policycap_netpeer)
if (selinux_xfrm_postroute_last(sksec->sid, skb, ad, proto))
if (selinux_xfrm_postroute_last(sksec->sid, skb, &ad, proto))
return NF_DROP;
return NF_ACCEPT;
......@@ -4537,23 +4623,15 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex,
struct sock *sk;
struct avc_audit_data ad;
char *addrp;
u8 proto;
u8 secmark_active;
u8 peerlbl_active;
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);
return selinux_ip_postroute_compat(skb, ifindex, family);
/* 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
......@@ -4569,21 +4647,45 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex,
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 */
/* if the packet is being forwarded then get the peer label from the
* packet itself; otherwise check to see if it is from a local
* application or the kernel, if from an application get the peer label
* from the sending socket, otherwise use the kernel's sid */
sk = skb->sk;
if (sk) {
if (sk == NULL) {
switch (family) {
case PF_INET:
if (IPCB(skb)->flags & IPSKB_FORWARDED)
secmark_perm = PACKET__FORWARD_OUT;
else
secmark_perm = PACKET__SEND;
break;
case PF_INET6:
if (IP6CB(skb)->flags & IP6SKB_FORWARDED)
secmark_perm = PACKET__FORWARD_OUT;
else
secmark_perm = PACKET__SEND;
break;
default:
return NF_DROP;
}
if (secmark_perm == PACKET__FORWARD_OUT) {
if (selinux_skb_peerlbl_sid(skb, family, &peer_sid))
return NF_DROP;
} else
peer_sid = SECINITSID_KERNEL;
} else {
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;
}
AVC_AUDIT_DATA_INIT(&ad, NET);
ad.u.net.netif = ifindex;
ad.u.net.family = family;
if (selinux_parse_skb(skb, &ad, &addrp, 0, NULL))
return NF_DROP;
if (secmark_active)
if (avc_has_perm(peer_sid, skb->secmark,
SECCLASS_PACKET, secmark_perm, &ad))
......@@ -5657,6 +5759,13 @@ static struct nf_hook_ops selinux_ipv4_ops[] = {
.pf = PF_INET,
.hooknum = NF_INET_FORWARD,
.priority = NF_IP_PRI_SELINUX_FIRST,
},
{
.hook = selinux_ipv4_output,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_INET_LOCAL_OUT,
.priority = NF_IP_PRI_SELINUX_FIRST,
}
};
......
......@@ -39,6 +39,9 @@
#ifdef CONFIG_NETLABEL
void selinux_netlbl_cache_invalidate(void);
void selinux_netlbl_err(struct sk_buff *skb, int error, int gateway);
void selinux_netlbl_sk_security_free(struct sk_security_struct *ssec);
void selinux_netlbl_sk_security_reset(struct sk_security_struct *ssec,
int family);
......@@ -46,8 +49,11 @@ int selinux_netlbl_skbuff_getsid(struct sk_buff *skb,
u16 family,
u32 *type,
u32 *sid);
int selinux_netlbl_skbuff_setsid(struct sk_buff *skb,
u16 family,
u32 sid);
void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock);
void selinux_netlbl_inet_conn_established(struct sock *sk, u16 family);
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,
......@@ -57,12 +63,27 @@ int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
int selinux_netlbl_socket_setsockopt(struct socket *sock,
int level,
int optname);
int selinux_netlbl_socket_connect(struct sock *sk, struct sockaddr *addr);
#else
static inline void selinux_netlbl_cache_invalidate(void)
{
return;
}
static inline void selinux_netlbl_err(struct sk_buff *skb,
int error,
int gateway)
{
return;
}
static inline void selinux_netlbl_sk_security_free(
struct sk_security_struct *ssec)
{
return;
}
static inline void selinux_netlbl_sk_security_reset(
struct sk_security_struct *ssec,
int family)
......@@ -79,9 +100,21 @@ static inline int selinux_netlbl_skbuff_getsid(struct sk_buff *skb,
*sid = SECSID_NULL;
return 0;
}
static inline int selinux_netlbl_skbuff_setsid(struct sk_buff *skb,
u16 family,
u32 sid)
{
return 0;
}
static inline void selinux_netlbl_sock_graft(struct sock *sk,
struct socket *sock)
static inline int selinux_netlbl_conn_setsid(struct sock *sk,
struct sockaddr *addr)
{
return 0;
}
static inline void selinux_netlbl_inet_conn_established(struct sock *sk,
u16 family)
{
return;
}
......@@ -107,6 +140,11 @@ static inline int selinux_netlbl_socket_setsockopt(struct socket *sock,
{
return 0;
}
static inline int selinux_netlbl_socket_connect(struct sock *sk,
struct sockaddr *addr)
{
return 0;
}
#endif /* CONFIG_NETLABEL */
#endif
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册